Separate MediaRouter Androidx demo to a new app.

- Remove some extra un-related work like light/dark themes, ...etc.

Bug: 257999945
Test: Manually
Change-Id: I2d3f11d39402ce9a22d452f07dc3d72cbdc7349f
diff --git a/samples/AndroidXDemos/src/main/AndroidManifest.xml b/samples/AndroidXDemos/src/main/AndroidManifest.xml
index 394fac1..549d7fc 100644
--- a/samples/AndroidXDemos/src/main/AndroidManifest.xml
+++ b/samples/AndroidXDemos/src/main/AndroidManifest.xml
@@ -39,88 +39,6 @@
                 <category android:name="android.intent.category.LAUNCHER" />
             </intent-filter>
         </activity>
-        <activity
-            android:name=".media.SampleMediaRouterActivity"
-            android:configChanges="orientation|screenSize"
-            android:exported="true"
-            android:label="@string/sample_media_router_activity_dark"
-            android:theme="@style/Theme.SampleMediaRouter">
-            <intent-filter>
-                <action android:name="android.intent.action.MAIN" />
-                <category android:name="com.example.androidx.SAMPLE_CODE" />
-            </intent-filter>
-        </activity>
-        <!-- MediaRouter Support Samples -->
-
-        <activity
-            android:name=".media.SampleMediaRouterActivity$Light"
-            android:configChanges="orientation|screenSize"
-            android:exported="true"
-            android:label="@string/sample_media_router_activity_light"
-            android:theme="@style/Theme.SampleMediaRouter.Light">
-            <intent-filter>
-                <action android:name="android.intent.action.MAIN" />
-                <category android:name="com.example.androidx.SAMPLE_CODE" />
-            </intent-filter>
-        </activity>
-
-        <activity
-            android:name=".media.SampleMediaRouterActivity$LightWithDarkActionBar"
-            android:configChanges="orientation|screenSize"
-            android:exported="true"
-            android:label="@string/sample_media_router_activity_light_with_dark_action_bar"
-            android:theme="@style/Theme.SampleMediaRouter.Light.DarkActionBar">
-            <intent-filter>
-                <action android:name="android.intent.action.MAIN" />
-                <category android:name="com.example.androidx.SAMPLE_CODE" />
-            </intent-filter>
-        </activity>
-
-        <activity
-            android:name=".media.SampleMediaRouterActivity$DynamicGroupActivity"
-            android:configChanges="orientation|screenSize"
-            android:exported="true"
-            android:label="@string/sample_media_router_activity_dynamic_group"
-            android:theme="@style/Theme.SampleMediaRouter">
-            <intent-filter>
-                <action android:name="android.intent.action.MAIN" />
-                <category android:name="com.example.androidx.SAMPLE_CODE" />
-            </intent-filter>
-        </activity>
-
-        <activity
-            android:name=".media.SampleMediaRouterActivity$OutputSwitcherActivity"
-            android:configChanges="orientation|screenSize"
-            android:exported="true"
-            android:label="@string/sample_media_router_activity_output_switcher"
-            android:theme="@style/Theme.SampleMediaRouter">
-            <intent-filter>
-                <action android:name="android.intent.action.MAIN" />
-                <category android:name="com.example.androidx.SAMPLE_CODE" />
-            </intent-filter>
-        </activity>
-
-        <activity
-            android:name=".media.SampleMediaRouterActivity$LegacyMediaRouterActivity"
-            android:configChanges="orientation|screenSize"
-            android:exported="true"
-            android:label="@string/sample_media_router_activity_legacy"
-            android:theme="@style/Theme.SampleMediaRouter">
-            <intent-filter>
-                <action android:name="android.intent.action.MAIN" />
-                <category android:name="com.example.androidx.SAMPLE_CODE" />
-            </intent-filter>
-        </activity>
-
-        <activity
-            android:name=".media.SampleMediaRouteSettingsActivity"
-            android:exported="true"
-            android:label="@string/sample_media_route_settings_activity"
-            android:theme="@style/Theme.AppCompat.Light">
-            <intent-filter>
-                <action android:name="android.intent.action.MAIN" />
-            </intent-filter>
-        </activity>
 
         <activity
             android:name=".view.GridLayout1"
@@ -925,48 +843,12 @@
             android:name=".app.RecentSuggestionsProvider"
             android:authorities="com.example.androidx.RecentSuggestionsProvider" />
 
-        <!-- Selection helper single selection demo activity -->
-        <receiver android:name=".media.SampleMediaButtonReceiver"
-            android:exported="true">
-            <intent-filter>
-                <action android:name="android.intent.action.MEDIA_BUTTON" />
-            </intent-filter>
-        </receiver>
-
-        <!-- Selection helper rudimentary demo activity -->
-        <receiver android:name="androidx.mediarouter.media.MediaTransferReceiver"
-            android:exported="true" />
-
-        <!-- Selection helper "the whole kit and caboodle" demo activity -->
-        <service
-            android:name=".media.SampleMediaRouteProviderService"
-            android:exported="true"
-            android:label="@string/sample_media_route_provider_service"
-            android:process=":mrp">
-            <intent-filter>
-                <action android:name="android.media.MediaRouteProviderService" />
-                <action android:name="android.media.MediaRoute2ProviderService" />
-            </intent-filter>
-        </service>
-
-        <!-- Custom drawable activity -->
-        <service
-            android:name=".media.SampleDynamicGroupMrpService"
-            android:label="@string/sample_media_route_provider_service"
-            android:exported="true"
-            android:process=":dynamic_mrp">
-            <intent-filter>
-                <action android:name="android.media.MediaRouteProviderService" />
-                <action android:name="android.media.MediaRoute2ProviderService" />
-            </intent-filter>
-        </service>
-
     </application>
-    <!-- Permission for SYSTEM_ALERT_WINDOW is only required for emulating
-         remote display using system alert window. -->
+
     <supports-screens
         android:compatibleWidthLimitDp="480"
         android:requiresSmallestWidthDp="320" />
+
     <!-- Permission for READ_EXTERNAL_STORAGE is explicitly required for
          reading images from the media store from API v19+. -->
     <uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
@@ -980,6 +862,4 @@
          this is just convenient for testing). -->
     <uses-permission android:name="android.permission.INTERNET" />
 
-    <uses-permission android:name="android.permission.SYSTEM_ALERT_WINDOW" />
-
 </manifest>
diff --git a/samples/AndroidXDemos/src/main/res/values/arrays.xml b/samples/AndroidXDemos/src/main/res/values/arrays.xml
index 5c0b352..cc37fb7 100644
--- a/samples/AndroidXDemos/src/main/res/values/arrays.xml
+++ b/samples/AndroidXDemos/src/main/res/values/arrays.xml
@@ -15,30 +15,6 @@
 -->
 
 <resources>
-    <string-array name="media_names">
-        <item>Big Buck Bunny</item>
-        <item>Elephants Dream</item>
-        <item>Sintel</item>
-        <item>Tears of Steel</item>
-        <item>(Music) Let\'s Dance</item>
-    </string-array>
-
-    <string-array name="media_uris">
-        <item>https://archive.org/download/BigBuckBunny_328/BigBuckBunny_512kb.mp4</item>
-        <item>https://archive.org/download/ElephantsDream_277/elephant_dreams_640_512kb.mp4</item>
-        <item>https://archive.org/download/Sintel/sintel-2048-stereo_512kb.mp4</item>
-        <item>https://archive.org/download/Tears-of-Steel/tears_of_steel_720p.mp4</item>
-        <item>https://archive.org/download/78_lets-dance-theme-song_baldridge-stone-bonime-benny-goodman-and-his-orchestra_gbia0000390b/Let%27s%20Dance%20%28Theme%20Song%29%20-%20Baldridge%20-%20Stone.mp3</item>
-    </string-array>
-
-    <string-array name="media_mimes">
-        <item>video/mp4</item>
-        <item>video/mp4</item>
-        <item>video/mp4</item>
-        <item>video/mp4</item>
-        <item>audio/mp3</item>
-    </string-array>
-
     <string-array name="dialog_types">
         <item>Simple</item>
         <item>Button bar</item>
diff --git a/samples/AndroidXDemos/src/main/res/values/strings.xml b/samples/AndroidXDemos/src/main/res/values/strings.xml
index 5844c3d..9953f125 100644
--- a/samples/AndroidXDemos/src/main/res/values/strings.xml
+++ b/samples/AndroidXDemos/src/main/res/values/strings.xml
@@ -17,44 +17,6 @@
 <resources>
     <string name="activity_sample_code">AndroidX Demos</string>
 
-    <!-- MediaRouter -->
-
-    <string name="sample_media_router_activity_dark">MediaRouter/Dark Theme</string>
-    <string name="sample_media_router_activity_light">MediaRouter/Light Theme</string>
-    <string name="sample_media_router_activity_light_with_dark_action_bar">MediaRouter/Light Theme, Dark Action Bar</string>
-    <string name="sample_media_router_activity_dynamic_group">MediaRouter/Dynamic Group
-        Dialog</string>
-    <string name="sample_media_router_activity_output_switcher">MediaRouter/Output Switcher</string>
-    <string name="sample_media_router_activity_legacy">MediaRouter/Disable MediaRouter2</string>
-    <string name="sample_media_router_text">This activity demonstrates how to
-            use MediaRouter from the support library.  Select a route from the action bar.</string>
-    <string name="media_route_menu_title">Play on...</string>
-    <string name="sample_media_route_settings_activity">Sample route settings</string>
-
-    <string name="use_default_media_control">Use default media control</string>
-    <string name="my_media_control_text">My Media Control</string>
-
-    <string name="library_tab_text">Library</string>
-    <string name="playlist_tab_text">Playlist</string>
-    <string name="info_tab_text">Route Info</string>
-
-    <string name="sample_media_route_provider_service">Media Route Provider Service Support Library Sample</string>
-    <string name="fixed_volume_route_name">Fixed Volume Remote Playback Route</string>
-    <string name="variable_volume_basic_route_name">Variable Volume (Basic) Remote Playback Route</string>
-    <string name="variable_volume_queuing_route_name">Variable Volume (Queuing) Remote Playback Route</string>
-    <string name="variable_volume_session_route_name">Variable Volume (Session) Remote Playback Route</string>
-    <string name="variable_volume_route_group_name">Variable Volume Route Group</string>
-    <string name="mixed_volume_route_group_name">Mixed Volume Route Group</string>
-    <string name="sample_route_description">Sample route from AndroidXDemos</string>
-
-    <string name="sample_dynamic_group_mrp_service">Media Route Provider Service Support Library Sample (supporting dynamic group)</string>
-    <string name="dg_tv_route_name1">Dynamic Route 1 - TV</string>
-    <string name="dg_tv_route_name2">Dynamic Route 2 - TV</string>
-    <string name="dg_speaker_route_name3">Dynamic Route 3 - Speaker</string>
-    <string name="dg_speaker_route_name4">Dynamic Route 4 - Speaker</string>
-    <string name="dg_not_unselectable_route_name5"> Dynamic Route 5 - Not unselectable</string>
-    <string name="dg_static_group_route_name6"> Dynamic Route 6 - Static Group</string>
-
     <!-- GridLayout -->
 
     <string name="grid_layout_1">GridLayout/1. Simple Form</string>
diff --git a/samples/MediaRoutingDemo/OWNERS b/samples/MediaRoutingDemo/OWNERS
new file mode 100644
index 0000000..834477b
--- /dev/null
+++ b/samples/MediaRoutingDemo/OWNERS
@@ -0,0 +1 @@
+file: ../../mediarouter/OWNERS
diff --git a/samples/MediaRoutingDemo/build.gradle b/samples/MediaRoutingDemo/build.gradle
new file mode 100644
index 0000000..5aff7c8
--- /dev/null
+++ b/samples/MediaRoutingDemo/build.gradle
@@ -0,0 +1,23 @@
+plugins {
+    id("AndroidXPlugin")
+    id("com.android.application")
+}
+
+dependencies {
+    implementation(project(":appcompat:appcompat"))
+    implementation(project(":mediarouter:mediarouter"))
+    implementation("androidx.concurrent:concurrent-futures:1.1.0")
+}
+
+android {
+    buildTypes {
+        release {
+            minifyEnabled = true
+            proguardFiles getDefaultProguardFile("proguard-android-optimize.txt")
+        }
+    }
+    defaultConfig {
+        vectorDrawables.useSupportLibrary = true
+    }
+    namespace "com.example.androidx.mediarouting"
+}
diff --git a/samples/MediaRoutingDemo/lint-baseline.xml b/samples/MediaRoutingDemo/lint-baseline.xml
new file mode 100644
index 0000000..a9df457
--- /dev/null
+++ b/samples/MediaRoutingDemo/lint-baseline.xml
@@ -0,0 +1,4 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<issues format="6" by="lint 7.4.0-alpha08" type="baseline" client="gradle" dependencies="false" name="AGP (7.4.0-alpha08)" variant="all" version="7.4.0-alpha08">
+
+</issues>
diff --git a/samples/MediaRoutingDemo/src/main/AndroidManifest.xml b/samples/MediaRoutingDemo/src/main/AndroidManifest.xml
new file mode 100644
index 0000000..d3910e3
--- /dev/null
+++ b/samples/MediaRoutingDemo/src/main/AndroidManifest.xml
@@ -0,0 +1,163 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2013 The Android Open Source Project
+
+     Licensed under the Apache License, Version 2.0 (the "License");
+     you may not use this file except in compliance with the License.
+     You may obtain a copy of the License at
+
+          http://www.apache.org/licenses/LICENSE-2.0
+
+     Unless required by applicable law or agreed to in writing, software
+     distributed under the License is distributed on an "AS IS" BASIS,
+     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+     See the License for the specific language governing permissions and
+     limitations under the License.
+-->
+
+<!-- Declare the contents of this Android application.  The namespace
+     attribute brings in the Android platform namespace, and the package
+     supplies a unique name for the application.  When writing your
+     own application, the package name must be changed from "com.example.*"
+     to come from a domain that you own or have control over. -->
+<manifest xmlns:android="http://schemas.android.com/apk/res/android">
+    <uses-permission android:name="android.permission.POST_NOTIFICATIONS" />
+
+    <application
+        android:hardwareAccelerated="true"
+        android:icon="@drawable/app_sample_code"
+        android:label="@string/activity_sample_code"
+        android:supportsRtl="true">
+
+        <activity
+            android:name=".SampleMediaRouterActivity"
+            android:configChanges="orientation|screenSize"
+            android:exported="true"
+            android:label="@string/sample_media_router_activity_dark"
+            android:theme="@style/Theme.SampleMediaRouter">
+            <intent-filter>
+                <action android:name="android.intent.action.MAIN" />
+                <category android:name="com.example.androidx.SAMPLE_CODE" />
+                <category android:name="android.intent.category.DEFAULT" />
+                <category android:name="android.intent.category.LAUNCHER" />
+            </intent-filter>
+        </activity>
+
+        <!-- MediaRouter Support Samples -->
+        <activity
+            android:name=".SampleMediaRouterActivity$Light"
+            android:configChanges="orientation|screenSize"
+            android:exported="true"
+            android:label="@string/sample_media_router_activity_light"
+            android:theme="@style/Theme.SampleMediaRouter.Light">
+            <intent-filter>
+                <action android:name="android.intent.action.MAIN" />
+                <category android:name="com.example.androidx.SAMPLE_CODE" />
+            </intent-filter>
+        </activity>
+
+        <activity
+            android:name=".SampleMediaRouterActivity$LightWithDarkActionBar"
+            android:configChanges="orientation|screenSize"
+            android:exported="true"
+            android:label="@string/sample_media_router_activity_light_with_dark_action_bar"
+            android:theme="@style/Theme.SampleMediaRouter.Light.DarkActionBar">
+            <intent-filter>
+                <action android:name="android.intent.action.MAIN" />
+                <category android:name="com.example.androidx.SAMPLE_CODE" />
+            </intent-filter>
+        </activity>
+
+        <activity
+            android:name=".SampleMediaRouterActivity$DynamicGroupActivity"
+            android:configChanges="orientation|screenSize"
+            android:exported="true"
+            android:label="@string/sample_media_router_activity_dynamic_group"
+            android:theme="@style/Theme.SampleMediaRouter">
+            <intent-filter>
+                <action android:name="android.intent.action.MAIN" />
+                <category android:name="com.example.androidx.SAMPLE_CODE" />
+            </intent-filter>
+        </activity>
+
+        <activity
+            android:name=".SampleMediaRouterActivity$OutputSwitcherActivity"
+            android:configChanges="orientation|screenSize"
+            android:exported="true"
+            android:label="@string/sample_media_router_activity_output_switcher"
+            android:theme="@style/Theme.SampleMediaRouter">
+            <intent-filter>
+                <action android:name="android.intent.action.MAIN" />
+                <category android:name="com.example.androidx.SAMPLE_CODE" />
+            </intent-filter>
+        </activity>
+
+        <activity
+            android:name=".SampleMediaRouterActivity$LegacyMediaRouterActivity"
+            android:configChanges="orientation|screenSize"
+            android:exported="true"
+            android:label="@string/sample_media_router_activity_legacy"
+            android:theme="@style/Theme.SampleMediaRouter">
+            <intent-filter>
+                <action android:name="android.intent.action.MAIN" />
+                <category android:name="com.example.androidx.SAMPLE_CODE" />
+            </intent-filter>
+        </activity>
+
+        <activity
+            android:name=".SampleMediaRouteSettingsActivity"
+            android:exported="true"
+            android:label="@string/sample_media_route_settings_activity"
+            android:theme="@style/Theme.AppCompat.Light">
+            <intent-filter>
+                <action android:name="android.intent.action.MAIN" />
+            </intent-filter>
+        </activity>
+
+        <receiver android:name="androidx.mediarouter.media.MediaTransferReceiver"
+            android:exported="true" />
+
+        <service
+            android:name=".SampleMediaRouteProviderService"
+            android:exported="true"
+            android:label="@string/sample_media_route_provider_service"
+            android:process=":mrp">
+            <intent-filter>
+                <action android:name="android.media.MediaRouteProviderService" />
+                <action android:name="android.media.MediaRoute2ProviderService" />
+            </intent-filter>
+        </service>
+
+        <service
+            android:name=".SampleDynamicGroupMrpService"
+            android:label="@string/sample_media_route_provider_service"
+            android:exported="true"
+            android:process=":dynamic_mrp">
+            <intent-filter>
+                <action android:name="android.media.MediaRouteProviderService" />
+                <action android:name="android.media.MediaRoute2ProviderService" />
+            </intent-filter>
+        </service>
+
+    </application>
+
+    <!-- The smallest screen this app works on is a phone.  The app will
+         scale its UI to larger screens but doesn't make good use of them
+         so allow the compatibility mode button to be shown (mostly because
+         this is just convenient for testing). -->
+    <supports-screens
+        android:compatibleWidthLimitDp="480"
+        android:requiresSmallestWidthDp="320" />
+
+    <!-- Permission for SYSTEM_ALERT_WINDOW is only required for emulating
+         remote display using system alert window. -->
+    <uses-permission android:name="android.permission.SYSTEM_ALERT_WINDOW" />
+
+    <!-- Permission for READ_EXTERNAL_STORAGE is explicitly required for
+         reading images from the media store from API v19+. -->
+    <uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
+
+    <!-- Permission for INTERNET is required for streaming video content
+         from the web, it's not required otherwise. -->
+    <uses-permission android:name="android.permission.INTERNET" />
+
+</manifest>
diff --git a/samples/AndroidXDemos/src/main/java/com/example/androidx/media/LocalPlayer.java b/samples/MediaRoutingDemo/src/main/java/com/example/androidx/mediarouting/LocalPlayer.java
similarity index 86%
rename from samples/AndroidXDemos/src/main/java/com/example/androidx/media/LocalPlayer.java
rename to samples/MediaRoutingDemo/src/main/java/com/example/androidx/mediarouting/LocalPlayer.java
index ec022f6..36a7b77 100644
--- a/samples/AndroidXDemos/src/main/java/com/example/androidx/media/LocalPlayer.java
+++ b/samples/MediaRoutingDemo/src/main/java/com/example/androidx/mediarouting/LocalPlayer.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2013 The Android Open Source Project
+ * Copyright 2022 The Android Open Source Project
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
@@ -14,7 +14,7 @@
  * limitations under the License.
  */
 
-package com.example.androidx.media;
+package com.example.androidx.mediarouting;
 
 import android.app.Activity;
 import android.app.Presentation;
@@ -37,21 +37,19 @@
 import android.view.WindowManager;
 import android.widget.FrameLayout;
 
+import androidx.annotation.NonNull;
+import androidx.annotation.Nullable;
 import androidx.annotation.RequiresApi;
 import androidx.mediarouter.media.MediaItemStatus;
 import androidx.mediarouter.media.MediaRouter.RouteInfo;
 
-import com.example.androidx.R;
-
 import java.io.IOException;
 
 /**
  * Handles playback of a single media item using MediaPlayer.
  */
-public abstract class LocalPlayer extends Player implements
-        MediaPlayer.OnPreparedListener,
-        MediaPlayer.OnCompletionListener,
-        MediaPlayer.OnErrorListener,
+public abstract class LocalPlayer extends Player implements MediaPlayer.OnPreparedListener,
+        MediaPlayer.OnCompletionListener, MediaPlayer.OnErrorListener,
         MediaPlayer.OnSeekCompleteListener {
     private static final String TAG = "LocalPlayer";
     private static final boolean DEBUG = Log.isLoggable(TAG, Log.DEBUG);
@@ -67,7 +65,7 @@
     private Surface mSurface;
     private SurfaceHolder mSurfaceHolder;
 
-    public LocalPlayer(Context context) {
+    public LocalPlayer(@NonNull Context context) {
         mContext = context;
 
         // reset media player
@@ -85,7 +83,7 @@
     }
 
     @Override
-    public void connect(RouteInfo route) {
+    public void connect(@NonNull RouteInfo route) {
         if (DEBUG) {
             Log.d(TAG, "connecting to: " + route);
         }
@@ -106,12 +104,12 @@
 
     // Player
     @Override
-    public void play(final PlaylistItem item) {
+    public void play(@NonNull final PlaylistItem item) {
         if (DEBUG) {
             Log.d(TAG, "play: item=" + item);
         }
         reset();
-        mSeekToPos = (int)item.getPosition();
+        mSeekToPos = (int) item.getPosition();
         try {
             mMediaPlayer.setDataSource(mContext, item.getUri());
             mMediaPlayer.prepareAsync();
@@ -132,11 +130,11 @@
     }
 
     @Override
-    public void seek(final PlaylistItem item) {
+    public void seek(@NonNull final PlaylistItem item) {
         if (DEBUG) {
             Log.d(TAG, "seek: item=" + item);
         }
-        int pos = (int)item.getPosition();
+        int pos = (int) item.getPosition();
         if (mState == STATE_PLAYING || mState == STATE_PAUSED) {
             mMediaPlayer.seekTo(pos);
             mSeekToPos = pos;
@@ -149,13 +147,12 @@
     }
 
     @Override
-    public void getStatus(final PlaylistItem item, final boolean update) {
+    public void getStatus(@NonNull final PlaylistItem item, final boolean update) {
         if (mState == STATE_PLAYING || mState == STATE_PAUSED) {
             // use mSeekToPos if we're currently seeking (mSeekToPos is reset
             // when seeking is completed)
             item.setDuration(mMediaPlayer.getDuration());
-            item.setPosition(mSeekToPos > 0 ?
-                    mSeekToPos : mMediaPlayer.getCurrentPosition());
+            item.setPosition(mSeekToPos > 0 ? mSeekToPos : mMediaPlayer.getCurrentPosition());
             item.setTimestamp(SystemClock.elapsedRealtime());
         }
         if (update && mCallback != null) {
@@ -201,12 +198,13 @@
     }
 
     @Override
-    public void enqueue(final PlaylistItem item) {
+    public void enqueue(@NonNull final PlaylistItem item) {
         throw new UnsupportedOperationException("LocalPlayer doesn't support enqueue!");
     }
 
+    @NonNull
     @Override
-    public PlaylistItem remove(String iid) {
+    public PlaylistItem remove(@NonNull String iid) {
         throw new UnsupportedOperationException("LocalPlayer doesn't support remove!");
     }
 
@@ -292,26 +290,43 @@
         });
     }
 
-    protected Context getContext() { return mContext; }
-    protected MediaPlayer getMediaPlayer() { return mMediaPlayer; }
-    protected int getVideoWidth() { return mVideoWidth; }
-    protected int getVideoHeight() { return mVideoHeight; }
-    protected int getState() { return mState; }
-    protected void setSurface(Surface surface) {
+    @NonNull
+    protected Context getContext() {
+        return mContext;
+    }
+
+    @NonNull
+    protected MediaPlayer getMediaPlayer() {
+        return mMediaPlayer;
+    }
+
+    protected int getVideoWidth() {
+        return mVideoWidth;
+    }
+
+    protected int getVideoHeight() {
+        return mVideoHeight;
+    }
+
+    protected int getState() {
+        return mState;
+    }
+
+    protected void setSurface(@NonNull Surface surface) {
         mSurface = surface;
         mSurfaceHolder = null;
         updateSurface();
     }
 
-    protected void setSurface(SurfaceHolder surfaceHolder) {
+    protected void setSurface(@Nullable SurfaceHolder surfaceHolder) {
         mSurface = null;
         mSurfaceHolder = surfaceHolder;
         updateSurface();
     }
 
-    protected void removeSurface(SurfaceHolder surfaceHolder) {
+    protected void removeSurface(@NonNull SurfaceHolder surfaceHolder) {
         if (surfaceHolder == mSurfaceHolder) {
-            setSurface((SurfaceHolder)null);
+            setSurface((SurfaceHolder) null);
         }
     }
 
@@ -378,19 +393,19 @@
     /**
      * Handles playback of a single media item using MediaPlayer in SurfaceView
      */
-    public static class SurfaceViewPlayer extends LocalPlayer implements
-            SurfaceHolder.Callback {
+    @RequiresApi(api = Build.VERSION_CODES.JELLY_BEAN_MR1)
+    public static class SurfaceViewPlayer extends LocalPlayer implements SurfaceHolder.Callback {
         private static final String TAG = "SurfaceViewPlayer";
         private RouteInfo mRoute;
         private final SurfaceView mSurfaceView;
         private final FrameLayout mLayout;
         private DemoPresentation mPresentation;
 
-        public SurfaceViewPlayer(Context context) {
+        public SurfaceViewPlayer(@NonNull Context context) {
             super(context);
 
-            mLayout = (FrameLayout)((Activity)context).findViewById(R.id.player);
-            mSurfaceView = (SurfaceView)((Activity)context).findViewById(R.id.surface_view);
+            mLayout = (FrameLayout) ((Activity) context).findViewById(R.id.player);
+            mSurfaceView = (SurfaceView) ((Activity) context).findViewById(R.id.surface_view);
 
             // add surface holder callback
             SurfaceHolder holder = mSurfaceView.getHolder();
@@ -398,7 +413,7 @@
         }
 
         @Override
-        public void connect(RouteInfo route) {
+        public void connect(@NonNull RouteInfo route) {
             super.connect(route);
             mRoute = route;
         }
@@ -443,7 +458,7 @@
                     mPresentation.show();
                 } catch (WindowManager.InvalidDisplayException ex) {
                     Log.w(TAG, "Couldn't show presentation!  Display was removed in "
-                              + "the meantime.", ex);
+                            + "the meantime.", ex);
                     mPresentation = null;
                 }
             }
@@ -453,8 +468,8 @@
 
         // SurfaceHolder.Callback
         @Override
-        public void surfaceChanged(SurfaceHolder holder, int format,
-                int width, int height) {
+        public void surfaceChanged(@NonNull SurfaceHolder holder, int format, int width,
+                int height) {
             if (DEBUG) {
                 Log.d(TAG, "surfaceChanged: " + width + "x" + height);
             }
@@ -462,7 +477,7 @@
         }
 
         @Override
-        public void surfaceCreated(SurfaceHolder holder) {
+        public void surfaceCreated(@NonNull SurfaceHolder holder) {
             if (DEBUG) {
                 Log.d(TAG, "surfaceCreated");
             }
@@ -471,7 +486,7 @@
         }
 
         @Override
-        public void surfaceDestroyed(SurfaceHolder holder) {
+        public void surfaceDestroyed(@NonNull SurfaceHolder holder) {
             if (DEBUG) {
                 Log.d(TAG, "surfaceDestroyed");
             }
@@ -524,15 +539,15 @@
         // Listens for when presentations are dismissed.
         private final DialogInterface.OnDismissListener mOnDismissListener =
                 new DialogInterface.OnDismissListener() {
-            @Override
-            public void onDismiss(DialogInterface dialog) {
-                if (dialog == mPresentation) {
-                    Log.i(TAG, "Presentation dismissed.");
-                    mPresentation = null;
-                    updateContents();
-                }
-            }
-        };
+                    @Override
+                    public void onDismiss(DialogInterface dialog) {
+                        if (dialog == mPresentation) {
+                            Log.i(TAG, "Presentation dismissed.");
+                            mPresentation = null;
+                            updateContents();
+                        }
+                    }
+                };
 
         @RequiresApi(Build.VERSION_CODES.JELLY_BEAN_MR1)
         private void releasePresentation() {
@@ -549,7 +564,7 @@
         private final class DemoPresentation extends Presentation {
             private SurfaceView mPresentationSurfaceView;
 
-            public DemoPresentation(Context context, Display display) {
+            DemoPresentation(Context context, Display display) {
                 super(context, display);
             }
 
@@ -594,19 +609,19 @@
         private static final String TAG = "OverlayPlayer";
         private final OverlayDisplayWindow mOverlay;
 
-        public OverlayPlayer(Context context) {
+        public OverlayPlayer(@NonNull Context context) {
             super(context);
 
             mOverlay = OverlayDisplayWindow.create(getContext(),
                     getContext().getResources().getString(
-                            R.string.sample_media_route_provider_remote),
-                    1024, 768, Gravity.CENTER);
+                            R.string.sample_media_route_provider_remote), 1024, 768,
+                    Gravity.CENTER);
 
             mOverlay.setOverlayWindowListener(this);
         }
 
         @Override
-        public void connect(RouteInfo route) {
+        public void connect(@NonNull RouteInfo route) {
             super.connect(route);
             mOverlay.show();
         }
@@ -628,20 +643,21 @@
 
         // OverlayDisplayWindow.OverlayWindowListener
         @Override
-        public void onWindowCreated(Surface surface) {
+        public void onWindowCreated(@NonNull Surface surface) {
             setSurface(surface);
         }
 
         @Override
-        public void onWindowCreated(SurfaceHolder surfaceHolder) {
+        public void onWindowCreated(@NonNull SurfaceHolder surfaceHolder) {
             setSurface(surfaceHolder);
         }
 
         @Override
         public void onWindowDestroyed() {
-            setSurface((SurfaceHolder)null);
+            setSurface((SurfaceHolder) null);
         }
 
+        @Nullable
         @Override
         public Bitmap getSnapshot() {
             if (getState() == STATE_PLAYING || getState() == STATE_PAUSED) {
diff --git a/samples/AndroidXDemos/src/main/java/com/example/androidx/media/MyMediaRouteControllerDialog.java b/samples/MediaRoutingDemo/src/main/java/com/example/androidx/mediarouting/MyMediaRouteControllerDialog.java
similarity index 80%
rename from samples/AndroidXDemos/src/main/java/com/example/androidx/media/MyMediaRouteControllerDialog.java
rename to samples/MediaRoutingDemo/src/main/java/com/example/androidx/mediarouting/MyMediaRouteControllerDialog.java
index efdf6fd..8051d4a7 100644
--- a/samples/AndroidXDemos/src/main/java/com/example/androidx/media/MyMediaRouteControllerDialog.java
+++ b/samples/MediaRoutingDemo/src/main/java/com/example/androidx/mediarouting/MyMediaRouteControllerDialog.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2015 The Android Open Source Project
+ * Copyright 2022 The Android Open Source Project
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
@@ -14,7 +14,7 @@
  * limitations under the License.
  */
 
-package com.example.androidx.media;
+package com.example.androidx.mediarouting;
 
 import android.content.Context;
 import android.graphics.Color;
@@ -22,21 +22,21 @@
 import android.view.View;
 import android.widget.TextView;
 
+import androidx.annotation.NonNull;
 import androidx.mediarouter.app.MediaRouteControllerDialog;
 
-import com.example.androidx.R;
-
 /**
  * An example MediaRouteControllerDialog for demonstrating
  * {@link androidx.mediarouter.app.MediaRouteControllerDialog#onCreateMediaControlView}.
  */
 public class MyMediaRouteControllerDialog extends MediaRouteControllerDialog {
-    public MyMediaRouteControllerDialog(Context context) {
+    public MyMediaRouteControllerDialog(@NonNull Context context) {
         super(context);
     }
 
+    @NonNull
     @Override
-    public View onCreateMediaControlView(Bundle savedInstanceState) {
+    public View onCreateMediaControlView(@NonNull Bundle savedInstanceState) {
         TextView view = new TextView(getContext());
         view.setText(R.string.my_media_control_text);
         view.setBackgroundColor(Color.GRAY);
diff --git a/samples/AndroidXDemos/src/main/java/com/example/androidx/media/OverlayDisplayWindow.java b/samples/MediaRoutingDemo/src/main/java/com/example/androidx/mediarouting/OverlayDisplayWindow.java
similarity index 70%
rename from samples/AndroidXDemos/src/main/java/com/example/androidx/media/OverlayDisplayWindow.java
rename to samples/MediaRoutingDemo/src/main/java/com/example/androidx/mediarouting/OverlayDisplayWindow.java
index 199046f..acee983 100644
--- a/samples/AndroidXDemos/src/main/java/com/example/androidx/media/OverlayDisplayWindow.java
+++ b/samples/MediaRoutingDemo/src/main/java/com/example/androidx/mediarouting/OverlayDisplayWindow.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2012 The Android Open Source Project
+ * Copyright 2022 The Android Open Source Project
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
@@ -14,7 +14,7 @@
  * limitations under the License.
  */
 
-package com.example.androidx.media;
+package com.example.androidx.mediarouting;
 
 import android.content.Context;
 import android.graphics.Bitmap;
@@ -38,10 +38,10 @@
 import android.view.WindowManager;
 import android.widget.TextView;
 
+import androidx.annotation.NonNull;
+import androidx.annotation.Nullable;
 import androidx.annotation.RequiresApi;
 
-import com.example.androidx.R;
-
 /**
  * Manages an overlay display window, used for simulating remote playback.
  */
@@ -59,10 +59,11 @@
     protected final int mWidth;
     protected final int mHeight;
     protected final int mGravity;
+    @Nullable
     protected OverlayWindowListener mListener;
 
-    protected OverlayDisplayWindow(Context context, String name,
-            int width, int height, int gravity) {
+    protected OverlayDisplayWindow(@NonNull Context context, @NonNull String name, int width,
+            int height, int gravity) {
         mContext = context;
         mName = name;
         mWidth = width;
@@ -70,7 +71,13 @@
         mGravity = gravity;
     }
 
-    public static OverlayDisplayWindow create(Context context, String name,
+    /**
+     * Factory methd to create the overlay window.
+     *
+     * @return the created overlay window.
+     */
+    @NonNull
+    public static OverlayDisplayWindow create(@NonNull Context context, @NonNull String name,
             int width, int height, int gravity) {
         if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR1) {
             return new JellybeanMr1Impl(context, name, width, height, gravity);
@@ -79,26 +86,55 @@
         }
     }
 
-    public void setOverlayWindowListener(OverlayWindowListener listener) {
+    public void setOverlayWindowListener(@NonNull OverlayWindowListener listener) {
         mListener = listener;
     }
 
+    @NonNull
     public Context getContext() {
         return mContext;
     }
 
+    /**
+     * Shows the overlay window.
+     */
     public abstract void show();
 
+    /**
+     * Dismisses the overlay window.
+     */
     public abstract void dismiss();
 
+    /**
+     * Change the view aspect ration to a new ratio.
+     */
     public abstract void updateAspectRatio(int width, int height);
 
+    /**
+     * Gets a bitmap representing the snapshot of the window.
+     *
+     * @return a bitmap representing the snapshot of the window.
+     */
+    @Nullable
     public abstract Bitmap getSnapshot();
 
-    // Watches for significant changes in the overlay display window lifecycle.
+    /**
+     * Watches for significant changes in the overlay display window lifecycle.
+     */
     public interface OverlayWindowListener {
-        void onWindowCreated(Surface surface);
-        void onWindowCreated(SurfaceHolder surfaceHolder);
+        /**
+         * Called when the window is created.
+         */
+        void onWindowCreated(@NonNull Surface surface);
+
+        /**
+         * Called when the window is created.
+         */
+        void onWindowCreated(@NonNull SurfaceHolder surfaceHolder);
+
+        /**
+         * Called when the window is destroyed.
+         */
         void onWindowDestroyed();
     }
 
@@ -112,17 +148,14 @@
         private boolean mWindowVisible;
         private SurfaceView mSurfaceView;
 
-        public LegacyImpl(Context context, String name,
-                int width, int height, int gravity) {
+        LegacyImpl(Context context, String name, int width, int height, int gravity) {
             super(context, name, width, height, gravity);
 
-            mWindowManager = (WindowManager)context.getSystemService(
-                    Context.WINDOW_SERVICE);
+            mWindowManager = (WindowManager) context.getSystemService(Context.WINDOW_SERVICE);
         }
 
         @Override
-        @SuppressWarnings("deprecation") /* getDefaultDisplay */
-        public void show() {
+        @SuppressWarnings("deprecation") /* getDefaultDisplay */ public void show() {
             if (!mWindowVisible) {
                 mSurfaceView = new SurfaceView(mContext);
 
@@ -146,8 +179,8 @@
                 params.gravity = Gravity.LEFT | Gravity.BOTTOM;
                 params.setTitle(mName);
 
-                int width = (int)(display.getWidth() * INITIAL_SCALE);
-                int height = (int)(display.getHeight() * INITIAL_SCALE);
+                int width = (int) (display.getWidth() * INITIAL_SCALE);
+                int height = (int) (display.getHeight() * INITIAL_SCALE);
                 if (mWidth > mHeight) {
                     height = mHeight * width / mWidth;
                 } else {
@@ -179,6 +212,7 @@
         public void updateAspectRatio(int width, int height) {
         }
 
+        @Nullable
         @Override
         public Bitmap getSnapshot() {
             return null;
@@ -189,8 +223,9 @@
      * Implementation for API version 17+.
      */
     @RequiresApi(Build.VERSION_CODES.JELLY_BEAN_MR1)
-    @SuppressWarnings("deprecation") /* getDefaultDisplay */
-    private static final class JellybeanMr1Impl extends OverlayDisplayWindow {
+    @SuppressWarnings("deprecation")
+    /* getDefaultDisplay */ private static final class JellybeanMr1Impl extends
+            OverlayDisplayWindow {
         // When true, disables support for moving and resizing the overlay.
         // The window is made non-touchable, which makes it possible to
         // directly interact with the content underneath.
@@ -219,15 +254,12 @@
         private float mLiveTranslationY;
         private float mLiveScale = 1.0f;
 
-        @SuppressWarnings("deprecation") /* defaultDisplay */
-        public JellybeanMr1Impl(Context context, String name,
-                int width, int height, int gravity) {
+        @SuppressWarnings("deprecation") /* defaultDisplay */ JellybeanMr1Impl(
+                Context context, String name, int width, int height, int gravity) {
             super(context, name, width, height, gravity);
 
-            mDisplayManager = (DisplayManager)context.getSystemService(
-                    Context.DISPLAY_SERVICE);
-            mWindowManager = (WindowManager)context.getSystemService(
-                    Context.WINDOW_SERVICE);
+            mDisplayManager = (DisplayManager) context.getSystemService(Context.DISPLAY_SERVICE);
+            mWindowManager = (WindowManager) context.getSystemService(Context.WINDOW_SERVICE);
 
             mDefaultDisplay = mWindowManager.getDefaultDisplay();
             updateDefaultDisplayInfo();
@@ -272,6 +304,7 @@
             relayout();
         }
 
+        @NonNull
         @Override
         public Bitmap getSnapshot() {
             return mTextureView.getBitmap();
@@ -292,11 +325,10 @@
         private void createWindow() {
             LayoutInflater inflater = LayoutInflater.from(mContext);
 
-            mWindowContent = inflater.inflate(
-                    R.layout.overlay_display_window, null);
+            mWindowContent = inflater.inflate(R.layout.overlay_display_window, null);
             mWindowContent.setOnTouchListener(mOnTouchListener);
 
-            mTextureView = (TextureView)mWindowContent.findViewById(
+            mTextureView = (TextureView) mWindowContent.findViewById(
                     R.id.overlay_display_window_texture);
             mTextureView.setPivotX(0);
             mTextureView.setPivotY(0);
@@ -305,7 +337,7 @@
             mTextureView.setOpaque(false);
             mTextureView.setSurfaceTextureListener(mSurfaceTextureListener);
 
-            mNameTextView = (TextView)mWindowContent.findViewById(
+            mNameTextView = (TextView) mWindowContent.findViewById(
                     R.id.overlay_display_window_title);
             mNameTextView.setText(mName);
 
@@ -334,10 +366,10 @@
 
             // Set the initial position and scale.
             // The position and scale will be clamped when the display is first shown.
-            mWindowX = (mGravity & Gravity.LEFT) == Gravity.LEFT ?
-                    0 : mDefaultDisplayMetrics.widthPixels;
-            mWindowY = (mGravity & Gravity.TOP) == Gravity.TOP ?
-                    0 : mDefaultDisplayMetrics.heightPixels;
+            mWindowX = (mGravity & Gravity.LEFT) == Gravity.LEFT
+                    ? 0 : mDefaultDisplayMetrics.widthPixels;
+            mWindowY = (mGravity & Gravity.TOP) == Gravity.TOP
+                    ? 0 : mDefaultDisplayMetrics.heightPixels;
             Log.d(TAG, mDefaultDisplayMetrics.toString());
             mWindowScale = INITIAL_SCALE;
 
@@ -348,23 +380,21 @@
 
         private void updateWindowParams() {
             float scale = mWindowScale * mLiveScale;
-            scale = Math.min(scale, (float)mDefaultDisplayMetrics.widthPixels / mWidth);
-            scale = Math.min(scale, (float)mDefaultDisplayMetrics.heightPixels / mHeight);
+            scale = Math.min(scale, (float) mDefaultDisplayMetrics.widthPixels / mWidth);
+            scale = Math.min(scale, (float) mDefaultDisplayMetrics.heightPixels / mHeight);
             scale = Math.max(MIN_SCALE, Math.min(MAX_SCALE, scale));
 
             float offsetScale = (scale / mWindowScale - 1.0f) * 0.5f;
-            int width = (int)(mWidth * scale);
-            int height = (int)(mHeight * scale);
-            int x = (int)(mWindowX + mLiveTranslationX - width * offsetScale);
-            int y = (int)(mWindowY + mLiveTranslationY - height * offsetScale);
+            int width = (int) (mWidth * scale);
+            int height = (int) (mHeight * scale);
+            int x = (int) (mWindowX + mLiveTranslationX - width * offsetScale);
+            int y = (int) (mWindowY + mLiveTranslationY - height * offsetScale);
             x = Math.max(0, Math.min(x, mDefaultDisplayMetrics.widthPixels - width));
             y = Math.max(0, Math.min(y, mDefaultDisplayMetrics.heightPixels - height));
 
             if (DEBUG) {
-                Log.d(TAG, "updateWindowParams: scale=" + scale
-                        + ", offsetScale=" + offsetScale
-                        + ", x=" + x + ", y=" + y
-                        + ", width=" + width + ", height=" + height);
+                Log.d(TAG, "updateWindowParams: scale=" + scale + ", offsetScale=" + offsetScale
+                        + ", x=" + x + ", y=" + y + ", width=" + width + ", height=" + height);
             }
 
             mTextureView.setScaleX(scale);
@@ -396,56 +426,56 @@
 
         private final DisplayManager.DisplayListener mDisplayListener =
                 new DisplayManager.DisplayListener() {
-            @Override
-            public void onDisplayAdded(int displayId) {
-            }
-
-            @Override
-            public void onDisplayChanged(int displayId) {
-                if (displayId == mDefaultDisplay.getDisplayId()) {
-                    if (updateDefaultDisplayInfo()) {
-                        relayout();
-                    } else {
-                        dismiss();
+                    @Override
+                    public void onDisplayAdded(int displayId) {
                     }
-                }
-            }
 
-            @Override
-            public void onDisplayRemoved(int displayId) {
-                if (displayId == mDefaultDisplay.getDisplayId()) {
-                    dismiss();
-                }
-            }
-        };
+                    @Override
+                    public void onDisplayChanged(int displayId) {
+                        if (displayId == mDefaultDisplay.getDisplayId()) {
+                            if (updateDefaultDisplayInfo()) {
+                                relayout();
+                            } else {
+                                dismiss();
+                            }
+                        }
+                    }
+
+                    @Override
+                    public void onDisplayRemoved(int displayId) {
+                        if (displayId == mDefaultDisplay.getDisplayId()) {
+                            dismiss();
+                        }
+                    }
+                };
 
         private final SurfaceTextureListener mSurfaceTextureListener =
                 new SurfaceTextureListener() {
-            @Override
-            public void onSurfaceTextureAvailable(SurfaceTexture surfaceTexture,
-                    int width, int height) {
-                if (mListener != null) {
-                    mListener.onWindowCreated(new Surface(surfaceTexture));
-                }
-            }
+                    @Override
+                    public void onSurfaceTextureAvailable(SurfaceTexture surfaceTexture, int width,
+                            int height) {
+                        if (mListener != null) {
+                            mListener.onWindowCreated(new Surface(surfaceTexture));
+                        }
+                    }
 
-            @Override
-            public boolean onSurfaceTextureDestroyed(SurfaceTexture surfaceTexture) {
-                if (mListener != null) {
-                    mListener.onWindowDestroyed();
-                }
-                return true;
-            }
+                    @Override
+                    public boolean onSurfaceTextureDestroyed(SurfaceTexture surfaceTexture) {
+                        if (mListener != null) {
+                            mListener.onWindowDestroyed();
+                        }
+                        return true;
+                    }
 
-            @Override
-            public void onSurfaceTextureSizeChanged(SurfaceTexture surfaceTexture,
-                    int width, int height) {
-            }
+                    @Override
+                    public void onSurfaceTextureSizeChanged(SurfaceTexture surfaceTexture,
+                            int width, int height) {
+                    }
 
-            @Override
-            public void onSurfaceTextureUpdated(SurfaceTexture surfaceTexture) {
-            }
-        };
+                    @Override
+                    public void onSurfaceTextureUpdated(SurfaceTexture surfaceTexture) {
+                    }
+                };
 
         private final View.OnTouchListener mOnTouchListener = new View.OnTouchListener() {
             @Override
@@ -473,24 +503,24 @@
 
         private final GestureDetector.OnGestureListener mOnGestureListener =
                 new GestureDetector.SimpleOnGestureListener() {
-            @Override
-            public boolean onScroll(MotionEvent e1, MotionEvent e2,
-                    float distanceX, float distanceY) {
-                mLiveTranslationX -= distanceX;
-                mLiveTranslationY -= distanceY;
-                relayout();
-                return true;
-            }
-        };
+                    @Override
+                    public boolean onScroll(MotionEvent e1, MotionEvent e2, float distanceX,
+                            float distanceY) {
+                        mLiveTranslationX -= distanceX;
+                        mLiveTranslationY -= distanceY;
+                        relayout();
+                        return true;
+                    }
+                };
 
         private final ScaleGestureDetector.OnScaleGestureListener mOnScaleGestureListener =
                 new ScaleGestureDetector.SimpleOnScaleGestureListener() {
-            @Override
-            public boolean onScale(ScaleGestureDetector detector) {
-                mLiveScale *= detector.getScaleFactor();
-                relayout();
-                return true;
-            }
-        };
+                    @Override
+                    public boolean onScale(ScaleGestureDetector detector) {
+                        mLiveScale *= detector.getScaleFactor();
+                        relayout();
+                        return true;
+                    }
+                };
     }
 }
diff --git a/samples/AndroidXDemos/src/main/java/com/example/androidx/media/Player.java b/samples/MediaRoutingDemo/src/main/java/com/example/androidx/mediarouting/Player.java
similarity index 63%
rename from samples/AndroidXDemos/src/main/java/com/example/androidx/media/Player.java
rename to samples/MediaRoutingDemo/src/main/java/com/example/androidx/mediarouting/Player.java
index 3ccd49d..d98da84 100644
--- a/samples/AndroidXDemos/src/main/java/com/example/androidx/media/Player.java
+++ b/samples/MediaRoutingDemo/src/main/java/com/example/androidx/mediarouting/Player.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2013 The Android Open Source Project
+ * Copyright 2022 The Android Open Source Project
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
@@ -14,11 +14,12 @@
  * limitations under the License.
  */
 
-package com.example.androidx.media;
+package com.example.androidx.mediarouting;
 
 import static android.support.v4.media.session.PlaybackStateCompat.ACTION_PAUSE;
 import static android.support.v4.media.session.PlaybackStateCompat.ACTION_PLAY;
 
+import android.Manifest;
 import android.app.Notification;
 import android.app.NotificationChannel;
 import android.app.NotificationManager;
@@ -26,6 +27,7 @@
 import android.content.ComponentName;
 import android.content.Context;
 import android.content.Intent;
+import android.content.pm.PackageManager;
 import android.graphics.Bitmap;
 import android.os.Build;
 import android.support.v4.media.MediaMetadataCompat;
@@ -34,13 +36,17 @@
 import android.util.Log;
 import android.view.KeyEvent;
 
+import androidx.annotation.DoNotInline;
+import androidx.annotation.NonNull;
+import androidx.annotation.Nullable;
 import androidx.annotation.RequiresApi;
+import androidx.core.app.ActivityCompat;
 import androidx.core.app.NotificationCompat;
 import androidx.core.app.NotificationManagerCompat;
+import androidx.media.session.MediaButtonReceiver;
 import androidx.mediarouter.media.MediaControlIntent;
 import androidx.mediarouter.media.MediaRouter.RouteInfo;
 
-import com.example.androidx.R;
 /**
  * Abstraction of common playback operations of media items, such as play,
  * seek, etc. Used by PlaybackManager as a backend to handle actual playback
@@ -51,68 +57,147 @@
 public abstract class Player {
     private static final String TAG = "SampleMediaRoutePlayer";
     private static final boolean DEBUG = Log.isLoggable(TAG, Log.DEBUG);
-    protected static final int STATE_IDLE = 0;
-    protected static final int STATE_PREPARING_FOR_PLAY = 1;
-    protected static final int STATE_PREPARING_FOR_PAUSE = 2;
-    protected static final int STATE_READY = 3;
-    protected static final int STATE_PLAYING = 4;
-    protected static final int STATE_PAUSED = 5;
+    public static final int STATE_IDLE = 0;
+    public static final int STATE_PREPARING_FOR_PLAY = 1;
+    public static final int STATE_PREPARING_FOR_PAUSE = 2;
+    public static final int STATE_READY = 3;
+    public static final int STATE_PLAYING = 4;
+    public static final int STATE_PAUSED = 5;
 
     protected static final String NOTIFICATION_CHANNEL_ID =
             "com.example.androidx.media.channel";
     protected static final int NOTIFICATION_ID = 1;
 
-    private static final long PLAYBACK_ACTIONS = PlaybackStateCompat.ACTION_PAUSE
-            | PlaybackStateCompat.ACTION_PLAY;
+    private static final long PLAYBACK_ACTIONS = ACTION_PAUSE
+            | ACTION_PLAY;
     private static final PlaybackStateCompat INIT_PLAYBACK_STATE = new PlaybackStateCompat.Builder()
             .setState(PlaybackStateCompat.STATE_NONE, 0, .0f).build();
 
+    @NonNull
     protected Context mContext;
+    @NonNull
     protected Callback mCallback;
+    @NonNull
     protected MediaSessionCompat mMediaSession;
 
+    @NonNull
     protected String mNotificationChannelId;
     private NotificationCompat.Action mPlayAction;
     private NotificationCompat.Action mPauseAction;
 
+    /**
+     * Check if player is playing a remote playback.
+     * @return
+     */
     public abstract boolean isRemotePlayback();
+
+    /**
+     * Returns whether the queuing is supported.
+     * @return
+     */
     public abstract boolean isQueuingSupported();
 
-    public abstract void connect(RouteInfo route);
+    /**
+     * Connects the player with a route info.
+     * @param route
+     */
+    public abstract void connect(@NonNull RouteInfo route);
+
+    /**
+     * Release the player resources.
+     */
     public abstract void release();
 
     // basic operations that are always supported
-    public abstract void play(final PlaylistItem item);
-    public abstract void seek(final PlaylistItem item);
-    public abstract void getStatus(final PlaylistItem item, final boolean update);
+
+    /**
+     * Player play operation
+     * @param item
+     */
+    public abstract void play(@NonNull PlaylistItem item);
+
+    /**
+     * Player seek operation.
+     * @param item
+     */
+    public abstract void seek(@NonNull PlaylistItem item);
+
+    /**
+     * Get player status of an item.
+     * @param item
+     * @param update
+     */
+    public abstract void getStatus(@NonNull PlaylistItem item, boolean update);
+
+    /**
+     * Player pause operation.
+     */
     public abstract void pause();
+
+    /**
+     * Player resume operation.
+     */
     public abstract void resume();
+
+    /**
+     * Player step operation.
+     */
     public abstract void stop();
 
     // advanced queuing (enqueue & remove) are only supported
     // if isQueuingSupported() returns true
-    public abstract void enqueue(final PlaylistItem item);
-    public abstract PlaylistItem remove(String iid);
 
-    public void takeSnapshot() {}
-    public Bitmap getSnapshot() { return null; }
+    /**
+     * Enqueue an item in the playlist.
+     * @param item
+     */
+    public abstract void enqueue(@NonNull PlaylistItem item);
+
+    /**
+     * Remove an item for the playlist.
+     * @param iid
+     * @return
+     */
+    @NonNull
+    public abstract PlaylistItem remove(@NonNull String iid);
+
+    /**
+     * Takes player snapshot.
+     */
+    public void takeSnapshot() {
+    }
+
+    @Nullable
+    public Bitmap getSnapshot() {
+        return null;
+    }
 
     /**
      * presentation display
      */
     @RequiresApi(Build.VERSION_CODES.JELLY_BEAN_MR1)
-    public void updatePresentation() {}
+    public void updatePresentation() {
+    }
 
-    public void setCallback(Callback callback) {
+    public void setCallback(@NonNull Callback callback) {
         mCallback = callback;
     }
 
-    public static Player create(Context context, RouteInfo route, MediaSessionCompat session) {
+    /**
+     * Factory method for creating the suitable player.
+     * @param context
+     * @param route
+     * @param session
+     * @return
+     */
+    @NonNull
+    public static Player create(@NonNull Context context, @NonNull RouteInfo route,
+            @NonNull MediaSessionCompat session) {
         Player player;
         if (route != null && route.supportsControlCategory(
                 MediaControlIntent.CATEGORY_REMOTE_PLAYBACK)) {
             player = new RemotePlayer(context);
-        } else if (route != null) {
+        } else if (route != null && Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR1) {
             player = new LocalPlayer.SurfaceViewPlayer(context);
         } else {
             player = new LocalPlayer.OverlayPlayer(context);
@@ -124,7 +209,10 @@
         return player;
     }
 
-    protected void initMediaSession() {
+    /**
+     * Initialize the media session.
+     */
+    public void initMediaSession() {
         if (mMediaSession == null) {
             return;
         }
@@ -132,7 +220,11 @@
         mMediaSession.setPlaybackState(INIT_PLAYBACK_STATE);
     }
 
-    protected void updateMetadata(PlaylistItem currentItem) {
+    /**
+     * Update the player metadata.
+     * @param currentItem
+     */
+    public void updateMetadata(@NonNull PlaylistItem currentItem) {
         if (mMediaSession == null) {
             return;
         }
@@ -167,6 +259,11 @@
                 .build();
 
         NotificationManagerCompat notificationManager = NotificationManagerCompat.from(mContext);
+        if (ActivityCompat.checkSelfPermission(mContext,
+                Manifest.permission.POST_NOTIFICATIONS)
+                != PackageManager.PERMISSION_GRANTED) {
+            return;
+        }
         notificationManager.notify(NOTIFICATION_ID, notification);
     }
 
@@ -181,14 +278,15 @@
             CharSequence name = "Channel";
             String description = "Description";
             int importance = NotificationManager.IMPORTANCE_LOW;
-            NotificationChannel channel = new NotificationChannel(
-                    mNotificationChannelId, name, importance);
-            channel.setDescription(description);
+            NotificationChannel channel = Api26Impl.createNotificationChannel(
+                    mNotificationChannelId, name.toString(), importance);
+
+            Api26Impl.setDescription(channel, description);
             // Register the channel with the system; you can't change the importance
             // or other notification behaviors after this
             NotificationManager notificationManager =
-                    mContext.getSystemService(NotificationManager.class);
-            notificationManager.createNotificationChannel(channel);
+                    Api26Impl.getSystemServiceReturnsNotificationManager(mContext);
+            Api26Impl.createNotificationChannel(notificationManager, channel);
         }
     }
 
@@ -201,13 +299,18 @@
         int keyCode = PlaybackStateCompat.toKeyCode(action);
         Intent intent = new Intent(Intent.ACTION_MEDIA_BUTTON);
         intent.setComponent(new ComponentName(mContext.getPackageName(),
-                SampleMediaButtonReceiver.class.getName()));
+                MediaButtonReceiver.class.getName()));
         intent.putExtra(Intent.EXTRA_KEY_EVENT, new KeyEvent(KeyEvent.ACTION_DOWN, keyCode));
 
         return PendingIntent.getBroadcast(mContext, keyCode, intent,
                 PendingIntent.FLAG_UPDATE_CURRENT | PendingIntent.FLAG_IMMUTABLE);
     }
-    protected void publishState(int state) {
+
+    /**
+     * Publish the player state.
+     * @param state
+     */
+    public void publishState(int state) {
         if (mMediaSession == null) {
             return;
         }
@@ -239,7 +342,7 @@
         }
     }
 
-    private void setMediaSession(MediaSessionCompat session) {
+    private void setMediaSession(@NonNull MediaSessionCompat session) {
         mMediaSession = session;
     }
 
@@ -250,10 +353,56 @@
                 R.drawable.ic_media_pause, "pause", ACTION_PAUSE);
     }
 
+    /**
+     * The player callback
+     */
     public interface Callback {
+        /**
+         * On player error.
+         */
         void onError();
+
+        /**
+         * On Playlist play completed.
+         */
         void onCompletion();
+
+        /**
+         * On Playlist changed.
+         */
         void onPlaylistChanged();
+
+        /**
+         * On playlist ready.
+         */
         void onPlaylistReady();
     }
+    @RequiresApi(26)
+    static class Api26Impl {
+        private Api26Impl() {
+            // This class is not instantiable.
+        }
+
+        @DoNotInline
+        static NotificationChannel createNotificationChannel(String notificationChannelId,
+                String name, int importance) {
+            return new NotificationChannel(notificationChannelId, name, importance);
+        }
+
+        @DoNotInline
+        static void createNotificationChannel(NotificationManager notificationManager,
+                NotificationChannel channel) {
+            notificationManager.createNotificationChannel(channel);
+        }
+
+        @DoNotInline
+        static void setDescription(NotificationChannel notificationChannel, String description) {
+            notificationChannel.setDescription(description);
+        }
+
+        @DoNotInline
+        static NotificationManager getSystemServiceReturnsNotificationManager(Context context) {
+            return context.getSystemService(NotificationManager.class);
+        }
+    }
 }
diff --git a/samples/AndroidXDemos/src/main/java/com/example/androidx/media/PlaylistItem.java b/samples/MediaRoutingDemo/src/main/java/com/example/androidx/mediarouting/PlaylistItem.java
similarity index 73%
rename from samples/AndroidXDemos/src/main/java/com/example/androidx/media/PlaylistItem.java
rename to samples/MediaRoutingDemo/src/main/java/com/example/androidx/mediarouting/PlaylistItem.java
index b5c50b1d..29f68eb 100644
--- a/samples/AndroidXDemos/src/main/java/com/example/androidx/media/PlaylistItem.java
+++ b/samples/MediaRoutingDemo/src/main/java/com/example/androidx/mediarouting/PlaylistItem.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2013 The Android Open Source Project
+ * Copyright 2022 The Android Open Source Project
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
@@ -14,18 +14,19 @@
  * limitations under the License.
  */
 
-package com.example.androidx.media;
+package com.example.androidx.mediarouting;
 
 import android.app.PendingIntent;
 import android.net.Uri;
 import android.os.SystemClock;
 
+import androidx.annotation.NonNull;
 import androidx.mediarouter.media.MediaItemStatus;
 
 /**
  * PlaylistItem helps keep track of the current status of an media item.
  */
-final class PlaylistItem {
+public final class PlaylistItem {
     // immutables
     private final String mSessionId;
     private final String mItemId;
@@ -40,7 +41,7 @@
     private long mTimestamp;
     private String mRemoteItemId;
 
-    PlaylistItem(PlaylistItem item) {
+    public PlaylistItem(@NonNull PlaylistItem item) {
         mSessionId = item.mSessionId;
         mItemId = item.mItemId;
         mTitle = item.mTitle;
@@ -55,8 +56,8 @@
         mRemoteItemId = item.mRemoteItemId;
     }
 
-    public PlaylistItem(String qid, String iid, String title, Uri uri, String mime,
-            PendingIntent pi) {
+    public PlaylistItem(@NonNull String qid, @NonNull String iid, @NonNull String title,
+            @NonNull Uri uri, @NonNull String mime, @NonNull PendingIntent pi) {
         mSessionId = qid;
         mItemId = iid;
         mTitle = title;
@@ -66,7 +67,7 @@
         setTimestamp(SystemClock.elapsedRealtime());
     }
 
-    public void setRemoteItemId(String riid) {
+    public void setRemoteItemId(@NonNull String riid) {
         mRemoteItemId = riid;
     }
 
@@ -86,30 +87,37 @@
         mContentDuration = duration;
     }
 
+    @NonNull
     public String getSessionId() {
         return mSessionId;
     }
 
+    @NonNull
     public String getItemId() {
         return mItemId;
     }
 
+    @NonNull
     public String getRemoteItemId() {
         return mRemoteItemId;
     }
 
+    @NonNull
     public String getTitle() {
         return mTitle;
     }
 
+    @NonNull
     public Uri getUri() {
         return mUri;
     }
 
+    @NonNull
     public String getMime() {
         return mMime;
     }
 
+    @NonNull
     public PendingIntent getUpdateReceiver() {
         return mUpdateReceiver;
     }
@@ -130,28 +138,30 @@
         return mTimestamp;
     }
 
+    @NonNull
     public MediaItemStatus getStatus() {
         return new MediaItemStatus.Builder(mPlaybackState)
-            .setContentPosition(mContentPosition)
-            .setContentDuration(mContentDuration)
-            .setTimestamp(mTimestamp)
-            .build();
+                .setContentPosition(mContentPosition)
+                .setContentDuration(mContentDuration)
+                .setTimestamp(mTimestamp)
+                .build();
     }
 
+    @NonNull
     @Override
     public String toString() {
-        String state[] = {
-            "PENDING",
-            "PLAYING",
-            "PAUSED",
-            "BUFFERING",
-            "FINISHED",
-            "CANCELED",
-            "INVALIDATED",
-            "ERROR"
+        String[] state = {
+                "PENDING",
+                "PLAYING",
+                "PAUSED",
+                "BUFFERING",
+                "FINISHED",
+                "CANCELED",
+                "INVALIDATED",
+                "ERROR"
         };
         return "[" + mSessionId + "|" + mItemId + "|"
-            + (mRemoteItemId != null ? mRemoteItemId : "-") + "|"
-            + state[mPlaybackState] + "] " + mTitle + ": " + mUri.toString();
+                + (mRemoteItemId != null ? mRemoteItemId : "-") + "|"
+                + state[mPlaybackState] + "] " + mTitle + ": " + mUri.toString();
     }
 }
diff --git a/samples/AndroidXDemos/src/main/java/com/example/androidx/media/RemotePlayer.java b/samples/MediaRoutingDemo/src/main/java/com/example/androidx/mediarouting/RemotePlayer.java
similarity index 79%
rename from samples/AndroidXDemos/src/main/java/com/example/androidx/media/RemotePlayer.java
rename to samples/MediaRoutingDemo/src/main/java/com/example/androidx/mediarouting/RemotePlayer.java
index db07e51..2990d44 100644
--- a/samples/AndroidXDemos/src/main/java/com/example/androidx/media/RemotePlayer.java
+++ b/samples/MediaRoutingDemo/src/main/java/com/example/androidx/mediarouting/RemotePlayer.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2013 The Android Open Source Project
+ * Copyright 2022 The Android Open Source Project
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
@@ -14,7 +14,7 @@
  * limitations under the License.
  */
 
-package com.example.androidx.media;
+package com.example.androidx.mediarouting;
 
 import android.content.Context;
 import android.content.Intent;
@@ -22,6 +22,7 @@
 import android.os.Bundle;
 import android.util.Log;
 
+import androidx.annotation.NonNull;
 import androidx.mediarouter.media.MediaItemStatus;
 import androidx.mediarouter.media.MediaRouter.ControlRequestCallback;
 import androidx.mediarouter.media.MediaRouter.RouteInfo;
@@ -53,8 +54,8 @@
     private StatusCallback mStatusCallback = new StatusCallback() {
         @Override
         public void onItemStatusChanged(Bundle data,
-                String sessionId, MediaSessionStatus sessionStatus,
-                String itemId, MediaItemStatus itemStatus) {
+                @NonNull String sessionId, MediaSessionStatus sessionStatus,
+                @NonNull String itemId, @NonNull MediaItemStatus itemStatus) {
             logStatus("onItemStatusChanged", sessionId, sessionStatus, itemId, itemStatus);
             if (mCallback != null) {
                 mCallback.onPlaylistChanged();
@@ -69,7 +70,7 @@
 
         @Override
         public void onSessionStatusChanged(Bundle data,
-                String sessionId, MediaSessionStatus sessionStatus) {
+                @NonNull String sessionId, MediaSessionStatus sessionStatus) {
             logStatus("onSessionStatusChanged", sessionId, sessionStatus, null, null);
             if (mCallback != null) {
                 mCallback.onPlaylistChanged();
@@ -84,7 +85,7 @@
         }
     };
 
-    public RemotePlayer(Context context) {
+    public RemotePlayer(@NonNull Context context) {
         mContext = context;
     }
 
@@ -99,7 +100,7 @@
     }
 
     @Override
-    public void connect(RouteInfo route) {
+    public void connect(@NonNull RouteInfo route) {
         mRoute = route;
         mClient = new RemotePlaybackClient(mContext, route);
         mClient.setStatusCallback(mStatusCallback);
@@ -107,7 +108,7 @@
         if (DEBUG) {
             Log.d(TAG, "connected to: " + route
                     + ", isRemotePlaybackSupported: " + mClient.isRemotePlaybackSupported()
-                    + ", isQueuingSupported: "+ mClient.isQueuingSupported());
+                    + ", isQueuingSupported: " + mClient.isQueuingSupported());
         }
     }
 
@@ -122,41 +123,42 @@
 
     // basic playback operations that are always supported
     @Override
-    public void play(final PlaylistItem item) {
+    public void play(final @NonNull PlaylistItem item) {
         if (DEBUG) {
             Log.d(TAG, "play: item=" + item);
         }
         mClient.play(item.getUri(), item.getMime(), null, item.getPosition(), null,
                 new ItemActionCallback() {
-            @Override
-            public void onResult(Bundle data, String sessionId, MediaSessionStatus sessionStatus,
-                    String itemId, MediaItemStatus itemStatus) {
-                logStatus("play: succeeded", sessionId, sessionStatus, itemId, itemStatus);
-                item.setRemoteItemId(itemId);
-                if (item.getState() == MediaItemStatus.PLAYBACK_STATE_PAUSED) {
-                    pause();
-                } else {
-                    publishState(STATE_PLAYING);
-                }
-                if (mCallback != null) {
-                    mCallback.onPlaylistChanged();
-                }
-            }
+                    @Override
+                    public void onResult(@NonNull Bundle data, @NonNull String sessionId,
+                            MediaSessionStatus sessionStatus,
+                            @NonNull String itemId, @NonNull MediaItemStatus itemStatus) {
+                        logStatus("play: succeeded", sessionId, sessionStatus, itemId, itemStatus);
+                        item.setRemoteItemId(itemId);
+                        if (item.getState() == MediaItemStatus.PLAYBACK_STATE_PAUSED) {
+                            pause();
+                        } else {
+                            publishState(STATE_PLAYING);
+                        }
+                        if (mCallback != null) {
+                            mCallback.onPlaylistChanged();
+                        }
+                    }
 
-            @Override
-            public void onError(String error, int code, Bundle data) {
-                logError("play: failed", error, code);
-            }
-        });
+                    @Override
+                    public void onError(String error, int code, Bundle data) {
+                        logError("play: failed", error, code);
+                    }
+                });
     }
 
     @Override
-    public void seek(final PlaylistItem item) {
+    public void seek(final @NonNull PlaylistItem item) {
         seekInternal(item);
     }
 
     @Override
-    public void getStatus(final PlaylistItem item, final boolean update) {
+    public void getStatus(final @NonNull PlaylistItem item, final boolean update) {
         if (!mClient.hasSession() || item.getRemoteItemId() == null) {
             // if session is not valid or item id not assigend yet.
             // just return, it's not fatal
@@ -168,8 +170,9 @@
         }
         mClient.getStatus(item.getRemoteItemId(), null, new ItemActionCallback() {
             @Override
-            public void onResult(Bundle data, String sessionId, MediaSessionStatus sessionStatus,
-                    String itemId, MediaItemStatus itemStatus) {
+            public void onResult(@NonNull Bundle data, @NonNull String sessionId,
+                    MediaSessionStatus sessionStatus, @NonNull String itemId,
+                    @NonNull MediaItemStatus itemStatus) {
                 logStatus("getStatus: succeeded", sessionId, sessionStatus, itemId, itemStatus);
                 int state = itemStatus.getPlaybackState();
                 if (state == MediaItemStatus.PLAYBACK_STATE_PLAYING
@@ -206,7 +209,8 @@
         }
         mClient.pause(null, new SessionActionCallback() {
             @Override
-            public void onResult(Bundle data, String sessionId, MediaSessionStatus sessionStatus) {
+            public void onResult(@NonNull Bundle data, @NonNull String sessionId,
+                    MediaSessionStatus sessionStatus) {
                 logStatus("pause: succeeded", sessionId, sessionStatus, null, null);
                 if (mCallback != null) {
                     mCallback.onPlaylistChanged();
@@ -232,7 +236,8 @@
         }
         mClient.resume(null, new SessionActionCallback() {
             @Override
-            public void onResult(Bundle data, String sessionId, MediaSessionStatus sessionStatus) {
+            public void onResult(@NonNull Bundle data, @NonNull String sessionId,
+                    MediaSessionStatus sessionStatus) {
                 logStatus("resume: succeeded", sessionId, sessionStatus, null, null);
                 if (mCallback != null) {
                     mCallback.onPlaylistChanged();
@@ -259,7 +264,8 @@
         }
         mClient.stop(null, new SessionActionCallback() {
             @Override
-            public void onResult(Bundle data, String sessionId, MediaSessionStatus sessionStatus) {
+            public void onResult(@NonNull Bundle data, @NonNull String sessionId,
+                    MediaSessionStatus sessionStatus) {
                 logStatus("stop: succeeded", sessionId, sessionStatus, null, null);
                 if (mClient.isSessionManagementSupported()) {
                     endSession();
@@ -278,7 +284,7 @@
 
     // enqueue & remove are only supported if isQueuingSupported() returns true
     @Override
-    public void enqueue(final PlaylistItem item) {
+    public void enqueue(final @NonNull PlaylistItem item) {
         throwIfQueuingUnsupported();
 
         if (!mClient.hasSession() && !mEnqueuePending) {
@@ -288,15 +294,16 @@
             } else {
                 enqueueInternal(item);
             }
-        } else if (mEnqueuePending){
+        } else if (mEnqueuePending) {
             mTempQueue.add(item);
         } else {
             enqueueInternal(item);
         }
     }
 
+    @NonNull
     @Override
-    public PlaylistItem remove(String itemId) {
+    public PlaylistItem remove(@NonNull String itemId) {
         throwIfNoSession();
         throwIfQueuingUnsupported();
 
@@ -305,8 +312,9 @@
         }
         mClient.remove(itemId, null, new ItemActionCallback() {
             @Override
-            public void onResult(Bundle data, String sessionId, MediaSessionStatus sessionStatus,
-                    String itemId, MediaItemStatus itemStatus) {
+            public void onResult(@NonNull Bundle data, @NonNull String sessionId,
+                    MediaSessionStatus sessionStatus, @NonNull String itemId,
+                    @NonNull MediaItemStatus itemStatus) {
                 logStatus("remove: succeeded", sessionId, sessionStatus, itemId, itemStatus);
             }
 
@@ -349,6 +357,7 @@
         }
     }
 
+    @NonNull
     @Override
     public Bitmap getSnapshot() {
         return mSnapshot;
@@ -362,8 +371,9 @@
         }
         mClient.enqueue(item.getUri(), item.getMime(), null, 0, null, new ItemActionCallback() {
             @Override
-            public void onResult(Bundle data, String sessionId, MediaSessionStatus sessionStatus,
-                    String itemId, MediaItemStatus itemStatus) {
+            public void onResult(@NonNull Bundle data, @NonNull String sessionId,
+                    MediaSessionStatus sessionStatus, @NonNull String itemId,
+                    @NonNull MediaItemStatus itemStatus) {
                 logStatus("enqueue: succeeded", sessionId, sessionStatus, itemId, itemStatus);
                 item.setRemoteItemId(itemId);
                 if (item.getPosition() > 0) {
@@ -403,26 +413,28 @@
             Log.d(TAG, "seek: item=" + item);
         }
         mClient.seek(item.getRemoteItemId(), item.getPosition(), null, new ItemActionCallback() {
-           @Override
-           public void onResult(Bundle data, String sessionId, MediaSessionStatus sessionStatus,
-                   String itemId, MediaItemStatus itemStatus) {
-               logStatus("seek: succeeded", sessionId, sessionStatus, itemId, itemStatus);
-               if (mCallback != null) {
-                   mCallback.onPlaylistChanged();
-               }
-           }
+            @Override
+            public void onResult(@NonNull Bundle data, @NonNull String sessionId,
+                    MediaSessionStatus sessionStatus, @NonNull String itemId,
+                    @NonNull MediaItemStatus itemStatus) {
+                logStatus("seek: succeeded", sessionId, sessionStatus, itemId, itemStatus);
+                if (mCallback != null) {
+                    mCallback.onPlaylistChanged();
+                }
+            }
 
-           @Override
-           public void onError(String error, int code, Bundle data) {
-               logError("seek: failed", error, code);
-           }
+            @Override
+            public void onError(String error, int code, Bundle data) {
+                logError("seek: failed", error, code);
+            }
         });
     }
 
     private void startSession(final PlaylistItem item) {
         mClient.startSession(null, new SessionActionCallback() {
             @Override
-            public void onResult(Bundle data, String sessionId, MediaSessionStatus sessionStatus) {
+            public void onResult(@NonNull Bundle data, @NonNull String sessionId,
+                    MediaSessionStatus sessionStatus) {
                 logStatus("startSession: succeeded", sessionId, sessionStatus, null, null);
                 enqueueInternal(item);
             }
@@ -437,7 +449,8 @@
     private void endSession() {
         mClient.endSession(null, new SessionActionCallback() {
             @Override
-            public void onResult(Bundle data, String sessionId, MediaSessionStatus sessionStatus) {
+            public void onResult(@NonNull Bundle data, @NonNull String sessionId,
+                    MediaSessionStatus sessionStatus) {
                 logStatus("endSession: succeeded", sessionId, sessionStatus, null, null);
             }
 
diff --git a/samples/AndroidXDemos/src/main/java/com/example/androidx/media/SampleDynamicGroupMrp.java b/samples/MediaRoutingDemo/src/main/java/com/example/androidx/mediarouting/SampleDynamicGroupMrp.java
similarity index 98%
rename from samples/AndroidXDemos/src/main/java/com/example/androidx/media/SampleDynamicGroupMrp.java
rename to samples/MediaRoutingDemo/src/main/java/com/example/androidx/mediarouting/SampleDynamicGroupMrp.java
index 81ab7a8..28c53cd7 100644
--- a/samples/AndroidXDemos/src/main/java/com/example/androidx/media/SampleDynamicGroupMrp.java
+++ b/samples/MediaRoutingDemo/src/main/java/com/example/androidx/mediarouting/SampleDynamicGroupMrp.java
@@ -1,5 +1,5 @@
 /*
- * Copyright 2018 The Android Open Source Project
+ * Copyright 2022 The Android Open Source Project
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
@@ -14,7 +14,7 @@
  * limitations under the License.
  */
 
-package com.example.androidx.media;
+package com.example.androidx.mediarouting;
 
 import android.app.PendingIntent;
 import android.content.Context;
@@ -33,8 +33,6 @@
 import androidx.mediarouter.media.MediaRouter.ControlRequestCallback;
 import androidx.mediarouter.media.MediaRouter.RouteInfo;
 
-import com.example.androidx.R;
-
 import java.lang.ref.WeakReference;
 import java.util.ArrayList;
 import java.util.Iterator;
@@ -63,7 +61,7 @@
     }
 
     @Override
-    public RouteController onCreateRouteController(String routeId) {
+    public RouteController onCreateRouteController(@NonNull String routeId) {
         if (!checkDrawOverlay()) return null;
 
         MediaRouteDescriptor routeDescriptor = mRouteDescriptors.get(routeId);
@@ -484,7 +482,7 @@
         }
 
         @Override
-        public boolean onControlRequest(Intent intent, ControlRequestCallback callback) {
+        public boolean onControlRequest(@NonNull Intent intent, ControlRequestCallback callback) {
             Log.d(TAG, mRouteId + ": Received control request " + intent);
             for (RouteController controller : getValidMemberControllers()) {
                 controller.onControlRequest(intent, callback);
diff --git a/samples/AndroidXDemos/src/main/java/com/example/androidx/media/SampleDynamicGroupMrpService.java b/samples/MediaRoutingDemo/src/main/java/com/example/androidx/mediarouting/SampleDynamicGroupMrpService.java
similarity index 87%
rename from samples/AndroidXDemos/src/main/java/com/example/androidx/media/SampleDynamicGroupMrpService.java
rename to samples/MediaRoutingDemo/src/main/java/com/example/androidx/mediarouting/SampleDynamicGroupMrpService.java
index 89698ea..0cb3ce8 100644
--- a/samples/AndroidXDemos/src/main/java/com/example/androidx/media/SampleDynamicGroupMrpService.java
+++ b/samples/MediaRoutingDemo/src/main/java/com/example/androidx/mediarouting/SampleDynamicGroupMrpService.java
@@ -1,5 +1,5 @@
 /*
- * Copyright 2018 The Android Open Source Project
+ * Copyright 2022 The Android Open Source Project
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
@@ -14,8 +14,9 @@
  * limitations under the License.
  */
 
-package com.example.androidx.media;
+package com.example.androidx.mediarouting;
 
+import androidx.annotation.NonNull;
 import androidx.mediarouter.media.MediaRouteProvider;
 import androidx.mediarouter.media.MediaRouteProviderService;
 
@@ -26,6 +27,7 @@
  * @see SampleDynamicGroupMrp
  */
 public class SampleDynamicGroupMrpService extends MediaRouteProviderService {
+    @NonNull
     @Override
     public MediaRouteProvider onCreateMediaRouteProvider() {
         return new SampleDynamicGroupMrp(this);
diff --git a/samples/AndroidXDemos/src/main/java/com/example/androidx/media/SampleMediaButtonReceiver.java b/samples/MediaRoutingDemo/src/main/java/com/example/androidx/mediarouting/SampleMediaButtonReceiver.java
similarity index 72%
rename from samples/AndroidXDemos/src/main/java/com/example/androidx/media/SampleMediaButtonReceiver.java
rename to samples/MediaRoutingDemo/src/main/java/com/example/androidx/mediarouting/SampleMediaButtonReceiver.java
index b8f236a..c019bf8 100644
--- a/samples/AndroidXDemos/src/main/java/com/example/androidx/media/SampleMediaButtonReceiver.java
+++ b/samples/MediaRoutingDemo/src/main/java/com/example/androidx/mediarouting/SampleMediaButtonReceiver.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2013 The Android Open Source Project
+ * Copyright 2022 The Android Open Source Project
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
@@ -14,12 +14,13 @@
  * limitations under the License.
  */
 
-package com.example.androidx.media;
+package com.example.androidx.mediarouting;
 
 import android.content.BroadcastReceiver;
 import android.content.Context;
 import android.content.Intent;
-import android.view.KeyEvent;
+
+import androidx.annotation.NonNull;
 
 /**
  * Broadcast receiver for handling ACTION_MEDIA_BUTTON.
@@ -31,17 +32,16 @@
 @SuppressWarnings("deprecation")
 public class SampleMediaButtonReceiver extends BroadcastReceiver {
     private static final String TAG = "SampleMediaButtonReceiver";
-    private static SampleMediaRouterActivity mActivity;
+    private static SampleMediaRouterActivity sActivity;
 
-    public static void setActivity(SampleMediaRouterActivity activity) {
-        mActivity = activity;
+    public static void setActivity(@NonNull SampleMediaRouterActivity activity) {
+        sActivity = activity;
     }
 
     @Override
     public void onReceive(Context context, Intent intent) {
-        if (mActivity != null && Intent.ACTION_MEDIA_BUTTON.equals(intent.getAction())) {
-            mActivity.handleMediaKey(
-                    (KeyEvent)intent.getParcelableExtra(Intent.EXTRA_KEY_EVENT));
+        if (sActivity != null && Intent.ACTION_MEDIA_BUTTON.equals(intent.getAction())) {
+            sActivity.handleMediaKey(intent.getParcelableExtra(Intent.EXTRA_KEY_EVENT));
         }
     }
 }
diff --git a/samples/AndroidXDemos/src/main/java/com/example/androidx/media/SampleMediaRouteProvider.java b/samples/MediaRoutingDemo/src/main/java/com/example/androidx/mediarouting/SampleMediaRouteProvider.java
similarity index 96%
rename from samples/AndroidXDemos/src/main/java/com/example/androidx/media/SampleMediaRouteProvider.java
rename to samples/MediaRoutingDemo/src/main/java/com/example/androidx/mediarouting/SampleMediaRouteProvider.java
index 98abca4e..69a14bd 100644
--- a/samples/AndroidXDemos/src/main/java/com/example/androidx/media/SampleMediaRouteProvider.java
+++ b/samples/MediaRoutingDemo/src/main/java/com/example/androidx/mediarouting/SampleMediaRouteProvider.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2013 The Android Open Source Project
+ * Copyright 2022 The Android Open Source Project
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
@@ -14,7 +14,7 @@
  * limitations under the License.
  */
 
-package com.example.androidx.media;
+package com.example.androidx.mediarouting;
 
 import android.app.PendingIntent;
 import android.content.Context;
@@ -31,6 +31,9 @@
 import android.provider.Settings;
 import android.util.Log;
 
+import androidx.annotation.DoNotInline;
+import androidx.annotation.NonNull;
+import androidx.annotation.RequiresApi;
 import androidx.collection.ArrayMap;
 import androidx.mediarouter.media.MediaControlIntent;
 import androidx.mediarouter.media.MediaRouteDescriptor;
@@ -40,8 +43,6 @@
 import androidx.mediarouter.media.MediaRouter.RouteInfo;
 import androidx.mediarouter.media.MediaSessionStatus;
 
-import com.example.androidx.R;
-
 import java.util.ArrayList;
 import java.util.HashMap;
 import java.util.Map;
@@ -155,14 +156,14 @@
     protected Map<String, Integer> mVolumes = new ArrayMap<>();
     protected Map<String, MediaRouteDescriptor> mRouteDescriptors = new HashMap<>();
 
-    public SampleMediaRouteProvider(Context context) {
+    SampleMediaRouteProvider(Context context) {
         super(context);
         initializeRoutes();
         publishRoutes();
     }
 
     @Override
-    public RouteController onCreateRouteController(String routeId) {
+    public RouteController onCreateRouteController(@NonNull String routeId) {
         if (!checkDrawOverlay()) return null;
         return new SampleRouteController(routeId);
     }
@@ -250,7 +251,7 @@
     }
 
     boolean checkDrawOverlay() {
-        if (Build.VERSION.SDK_INT >= 23 && !Settings.canDrawOverlays(getContext())) {
+        if (Build.VERSION.SDK_INT >= 23 && !Api23Impl.canDrawOverlays(getContext())) {
             Intent intent = new Intent(Settings.ACTION_MANAGE_OVERLAY_PERMISSION,
                     Uri.parse("package:" + getContext().getPackageName()));
             intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
@@ -268,7 +269,7 @@
         return volume;
     }
 
-    final class SampleRouteController extends MediaRouteProvider.RouteController {
+    final class SampleRouteController extends RouteController {
         private final String mRouteId;
         private RouteControlHelper mHelper;
 
@@ -329,7 +330,7 @@
         }
 
         @Override
-        public boolean onControlRequest(Intent intent, ControlRequestCallback callback) {
+        public boolean onControlRequest(@NonNull Intent intent, ControlRequestCallback callback) {
             Log.d(TAG, mRouteId + ": Received control request " + intent);
             return mHelper.onControlRequest(intent, callback);
         }
@@ -356,7 +357,7 @@
                 }
 
                 @Override
-                public void onItemChanged(PlaylistItem item) {
+                public void onItemChanged(@NonNull PlaylistItem item) {
                     handleStatusChange(item);
                 }
             });
@@ -716,4 +717,15 @@
             }
         }
     }
+    @RequiresApi(23)
+    static class Api23Impl {
+        private Api23Impl() {
+            // This class is not instantiable.
+        }
+
+        @DoNotInline
+        static boolean canDrawOverlays(Context context) {
+            return Settings.canDrawOverlays(context);
+        }
+    }
 }
diff --git a/samples/AndroidXDemos/src/main/java/com/example/androidx/media/SampleMediaRouteProviderService.java b/samples/MediaRoutingDemo/src/main/java/com/example/androidx/mediarouting/SampleMediaRouteProviderService.java
similarity index 87%
rename from samples/AndroidXDemos/src/main/java/com/example/androidx/media/SampleMediaRouteProviderService.java
rename to samples/MediaRoutingDemo/src/main/java/com/example/androidx/mediarouting/SampleMediaRouteProviderService.java
index 0f17e65..4b42632 100644
--- a/samples/AndroidXDemos/src/main/java/com/example/androidx/media/SampleMediaRouteProviderService.java
+++ b/samples/MediaRoutingDemo/src/main/java/com/example/androidx/mediarouting/SampleMediaRouteProviderService.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2013 The Android Open Source Project
+ * Copyright 2022 The Android Open Source Project
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
@@ -14,8 +14,9 @@
  * limitations under the License.
  */
 
-package com.example.androidx.media;
+package com.example.androidx.mediarouting;
 
+import androidx.annotation.NonNull;
 import androidx.mediarouter.media.MediaRouteProvider;
 import androidx.mediarouter.media.MediaRouteProviderService;
 
@@ -26,6 +27,7 @@
  * @see SampleMediaRouteProvider
  */
 public class SampleMediaRouteProviderService extends MediaRouteProviderService {
+    @NonNull
     @Override
     public MediaRouteProvider onCreateMediaRouteProvider() {
         return new SampleMediaRouteProvider(this);
diff --git a/samples/AndroidXDemos/src/main/java/com/example/androidx/media/SampleMediaRouteSettingsActivity.java b/samples/MediaRoutingDemo/src/main/java/com/example/androidx/mediarouting/SampleMediaRouteSettingsActivity.java
similarity index 89%
rename from samples/AndroidXDemos/src/main/java/com/example/androidx/media/SampleMediaRouteSettingsActivity.java
rename to samples/MediaRoutingDemo/src/main/java/com/example/androidx/mediarouting/SampleMediaRouteSettingsActivity.java
index 4e66493..554555d 100644
--- a/samples/AndroidXDemos/src/main/java/com/example/androidx/media/SampleMediaRouteSettingsActivity.java
+++ b/samples/MediaRoutingDemo/src/main/java/com/example/androidx/mediarouting/SampleMediaRouteSettingsActivity.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2014 The Android Open Source Project
+ * Copyright 2022 The Android Open Source Project
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
@@ -14,7 +14,7 @@
  * limitations under the License.
  */
 
-package com.example.androidx.media;
+package com.example.androidx.mediarouting;
 
 import androidx.appcompat.app.AppCompatActivity;
 
diff --git a/samples/AndroidXDemos/src/main/java/com/example/androidx/media/SampleMediaRouterActivity.java b/samples/MediaRoutingDemo/src/main/java/com/example/androidx/mediarouting/SampleMediaRouterActivity.java
similarity index 93%
rename from samples/AndroidXDemos/src/main/java/com/example/androidx/media/SampleMediaRouterActivity.java
rename to samples/MediaRoutingDemo/src/main/java/com/example/androidx/mediarouting/SampleMediaRouterActivity.java
index e51f9b7..1177d9f9 100644
--- a/samples/AndroidXDemos/src/main/java/com/example/androidx/media/SampleMediaRouterActivity.java
+++ b/samples/MediaRoutingDemo/src/main/java/com/example/androidx/mediarouting/SampleMediaRouterActivity.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2013 The Android Open Source Project
+ * Copyright 2022 The Android Open Source Project
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
@@ -14,7 +14,7 @@
  * limitations under the License.
  */
 
-package com.example.androidx.media;
+package com.example.androidx.mediarouting;
 
 import android.app.PendingIntent;
 import android.content.ComponentName;
@@ -65,8 +65,6 @@
 import androidx.mediarouter.media.MediaRouter.RouteInfo;
 import androidx.mediarouter.media.MediaRouterParams;
 
-import com.example.androidx.R;
-
 import java.io.File;
 
 /**
@@ -78,7 +76,6 @@
  * targets.
  * </p>
  */
-@SuppressWarnings("deprecation")
 public class SampleMediaRouterActivity extends AppCompatActivity {
     private static final String TAG = "SampleMediaRouter";
     private static final String DISCOVERY_FRAGMENT_TAG = "DiscoveryFragment";
@@ -145,16 +142,16 @@
             updateUi();
         }
 
-        @SuppressWarnings("deprecation")
         @Override
-        public void onRouteUnselected(@NonNull MediaRouter router, @NonNull RouteInfo route) {
+        public void onRouteUnselected(@NonNull MediaRouter router, @NonNull RouteInfo route,
+                int reason) {
             Log.d(TAG, "onRouteUnselected: route=" + route);
             mMediaSession.setActive(false);
 
             PlaylistItem item = getCheckedPlaylistItem();
             if (item != null) {
-                long pos = item.getPosition() + (mSessionManager.isPaused() ?
-                        0 : (SystemClock.elapsedRealtime() - item.getTimestamp()));
+                long pos = item.getPosition() + (mSessionManager.isPaused()
+                        ? 0 : (SystemClock.elapsedRealtime() - item.getTimestamp()));
                 mSessionManager.suspend(pos);
             }
             if (isPresentationApiSupported()) {
@@ -201,7 +198,6 @@
     private ComponentName mEventReceiver;
     private PendingIntent mMediaPendingIntent;
 
-    @SuppressWarnings("deprecation")
     @Override
     protected void onCreate(@Nullable Bundle savedInstanceState) {
         // Be sure to call the super class.
@@ -283,17 +279,17 @@
         TabHost tabHost = findViewById(R.id.tabHost);
         tabHost.setup();
         String tabName = getResources().getString(R.string.library_tab_text);
-        TabSpec spec1=tabHost.newTabSpec(tabName);
+        TabSpec spec1 = tabHost.newTabSpec(tabName);
         spec1.setContent(R.id.tab1);
         spec1.setIndicator(tabName);
 
         tabName = getResources().getString(R.string.playlist_tab_text);
-        TabSpec spec2=tabHost.newTabSpec(tabName);
+        TabSpec spec2 = tabHost.newTabSpec(tabName);
         spec2.setIndicator(tabName);
         spec2.setContent(R.id.tab2);
 
         tabName = getResources().getString(R.string.info_tab_text);
-        TabSpec spec3=tabHost.newTabSpec(tabName);
+        TabSpec spec3 = tabHost.newTabSpec(tabName);
         spec3.setIndicator(tabName);
         spec3.setContent(R.id.tab3);
 
@@ -344,7 +340,8 @@
             }
 
             @Override
-            public void onStartTrackingTouch(SeekBar seekBar) { }
+            public void onStartTrackingTouch(SeekBar seekBar) {
+            }
 
             @Override
             public void onStopTrackingTouch(SeekBar seekBar) {
@@ -361,7 +358,7 @@
         Intent mediaButtonIntent = new Intent(Intent.ACTION_MEDIA_BUTTON);
         mediaButtonIntent.setComponent(mEventReceiver);
         mMediaPendingIntent = PendingIntent.getBroadcast(this, /* requestCode = */0,
-            mediaButtonIntent, PendingIntent.FLAG_IMMUTABLE);
+                mediaButtonIntent, PendingIntent.FLAG_IMMUTABLE);
 
         // Create and register the remote control client
         createMediaSession();
@@ -379,7 +376,8 @@
             }
 
             @Override
-            public void onItemChanged(PlaylistItem item) { }
+            public void onItemChanged(@NonNull PlaylistItem item) {
+            }
         });
 
         updateUi();
@@ -421,8 +419,7 @@
                 && event.getRepeatCount() == 0) {
             switch (event.getKeyCode()) {
                 case KeyEvent.KEYCODE_MEDIA_PLAY_PAUSE:
-                case KeyEvent.KEYCODE_HEADSETHOOK:
-                {
+                case KeyEvent.KEYCODE_HEADSETHOOK: {
                     Log.d(TAG, "Received Play/Pause event from RemoteControlClient");
                     if (mSessionManager.isPaused()) {
                         mSessionManager.resume();
@@ -431,24 +428,21 @@
                     }
                     return true;
                 }
-                case KeyEvent.KEYCODE_MEDIA_PLAY:
-                {
+                case KeyEvent.KEYCODE_MEDIA_PLAY: {
                     Log.d(TAG, "Received Play event from RemoteControlClient");
                     if (mSessionManager.isPaused()) {
                         mSessionManager.resume();
                     }
                     return true;
                 }
-                case KeyEvent.KEYCODE_MEDIA_PAUSE:
-                {
+                case KeyEvent.KEYCODE_MEDIA_PAUSE: {
                     Log.d(TAG, "Received Pause event from RemoteControlClient");
                     if (!mSessionManager.isPaused()) {
                         mSessionManager.pause();
                     }
                     return true;
                 }
-                case KeyEvent.KEYCODE_MEDIA_STOP:
-                {
+                case KeyEvent.KEYCODE_MEDIA_STOP: {
                     Log.d(TAG, "Received Stop event from RemoteControlClient");
                     mSessionManager.stop();
                     return true;
@@ -503,7 +497,7 @@
                 @Override
                 public MediaRouteControllerDialogFragment onCreateControllerDialogFragment() {
                     return new ControllerDialogFragment(SampleMediaRouterActivity.this,
-                        mUseDefaultControlCheckBox);
+                            mUseDefaultControlCheckBox);
                 }
             });
         }
@@ -534,9 +528,9 @@
                 }
             } else {
                 long position = item.getPosition();
-                long timeDelta = mSessionManager.isPaused() ? 0 :
-                        (SystemClock.elapsedRealtime() - item.getTimestamp());
-                progress = (int)(100.0 * (position + timeDelta) / duration);
+                long timeDelta = mSessionManager.isPaused()
+                        ? 0 : (SystemClock.elapsedRealtime() - item.getTimestamp());
+                progress = (int) (100.0 * (position + timeDelta) / duration);
             }
         }
         mSeekBar.setProgress(progress);
@@ -551,7 +545,7 @@
             if (currentItem != null) {
                 mPlayer.updateMetadata(currentItem);
                 int currentItemState = Player.STATE_IDLE;
-                switch(currentItem.getState()) {
+                switch (currentItem.getState()) {
                     case MediaItemStatus.PLAYBACK_STATE_PLAYING:
                         currentItemState = Player.STATE_PLAYING;
                         break;
@@ -585,8 +579,8 @@
 
     private void updateButtons() {
         // show pause or resume icon depending on current state
-        mPauseResumeButton.setImageResource(mSessionManager.isPaused() ?
-                R.drawable.ic_media_play : R.drawable.ic_media_pause);
+        mPauseResumeButton.setImageResource(mSessionManager.isPaused()
+                ? R.drawable.ic_media_play : R.drawable.ic_media_pause);
         // only enable seek bar when duration is known
         PlaylistItem item = getCheckedPlaylistItem();
         mSeekBar.setEnabled(item != null && item.getDuration() > 0);
@@ -640,7 +634,7 @@
         public final Uri mUri;
         public final String mMime;
 
-        public MediaItem(String name, Uri uri, String mime) {
+        MediaItem(String name, Uri uri, String mime) {
             mName = name;
             mUri = uri;
             mMime = mime;
@@ -654,7 +648,7 @@
     }
 
     private final class LibraryAdapter extends ArrayAdapter<MediaItem> {
-        public LibraryAdapter() {
+        LibraryAdapter() {
             super(SampleMediaRouterActivity.this, R.layout.media_item);
         }
 
@@ -682,7 +676,7 @@
     }
 
     private final class PlaylistAdapter extends ArrayAdapter<PlaylistItem> {
-        public PlaylistAdapter() {
+        PlaylistAdapter() {
             super(SampleMediaRouterActivity.this, R.layout.media_item);
         }
 
@@ -724,7 +718,7 @@
     }
 
     /**
-     * This will show dynamic group dialog when ther user clicks the media route button.
+     * This will show dynamic group dialog when the user clicks the media route button.
      */
     public static class DynamicGroupActivity extends SampleMediaRouterActivity {
         @NonNull
@@ -762,6 +756,9 @@
         }
     }
 
+    /**
+     * Controller Dialog Fragment for the media router dialog.
+     */
     public static class ControllerDialogFragment extends MediaRouteControllerDialogFragment {
         private SampleMediaRouterActivity mSampleMediaRouterActivity;
         private MediaRouteControllerDialog mControllerDialog;
@@ -784,8 +781,8 @@
             mSampleMediaRouterActivity.updateStatusFromSessionManager();
             mControllerDialog =
                     mUseDefaultControlCheckBox != null && mUseDefaultControlCheckBox.isChecked()
-                    ? super.onCreateControllerDialog(context, savedInstanceState)
-                    : new MyMediaRouteControllerDialog(context);
+                            ? super.onCreateControllerDialog(context, savedInstanceState)
+                            : new MyMediaRouteControllerDialog(context);
             mControllerDialog.setOnDismissListener(dialog -> mControllerDialog = null);
             return mControllerDialog;
         }
diff --git a/samples/AndroidXDemos/src/main/java/com/example/androidx/media/SessionManager.java b/samples/MediaRoutingDemo/src/main/java/com/example/androidx/mediarouting/SessionManager.java
similarity index 80%
rename from samples/AndroidXDemos/src/main/java/com/example/androidx/media/SessionManager.java
rename to samples/MediaRoutingDemo/src/main/java/com/example/androidx/mediarouting/SessionManager.java
index e7d706f..cfa9cd6 100644
--- a/samples/AndroidXDemos/src/main/java/com/example/androidx/media/SessionManager.java
+++ b/samples/MediaRoutingDemo/src/main/java/com/example/androidx/mediarouting/SessionManager.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2013 The Android Open Source Project
+ * Copyright 2022 The Android Open Source Project
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
@@ -14,12 +14,14 @@
  * limitations under the License.
  */
 
-package com.example.androidx.media;
+package com.example.androidx.mediarouting;
 
 import android.app.PendingIntent;
 import android.net.Uri;
 import android.util.Log;
 
+import androidx.annotation.NonNull;
+import androidx.annotation.Nullable;
 import androidx.mediarouter.media.MediaItemStatus;
 import androidx.mediarouter.media.MediaSessionStatus;
 
@@ -47,7 +49,7 @@
     private Callback mCallback;
     private List<PlaylistItem> mPlaylist = new ArrayList<PlaylistItem>();
 
-    public SessionManager(String name) {
+    public SessionManager(@NonNull String name) {
         mName = name;
     }
 
@@ -55,24 +57,34 @@
         return hasSession() && mPaused;
     }
 
+    /**
+     * Returns if the session manager has a session or not.
+     */
     public boolean hasSession() {
         return mSessionValid;
     }
 
+    @Nullable
     public String getSessionId() {
         return mSessionValid ? Integer.toString(mSessionId) : null;
     }
 
+    @Nullable
     public PlaylistItem getCurrentItem() {
         return mPlaylist.isEmpty() ? null : mPlaylist.get(0);
     }
 
     // Returns the cached playlist (note this is not responsible for updating it)
+    @Nullable
     public List<PlaylistItem> getPlaylist() {
         return mPlaylist;
     }
 
     // Updates the playlist asynchronously, calls onPlaylistReady() when finished.
+
+    /**
+     * Updates the session status.
+     */
     public void updateStatus() {
         if (DEBUG) {
             log("updateStatus");
@@ -100,12 +112,20 @@
         }
     }
 
-    public PlaylistItem add(String title, Uri uri, String mime) {
+    /**
+     * Adds an item to the playlist.
+     */
+    @NonNull
+    public PlaylistItem add(@NonNull String title, @NonNull Uri uri, @NonNull String mime) {
         return add(title, uri, mime, 0, null);
     }
 
-    public PlaylistItem add(String title, Uri uri, String mime, long startPosition,
-            PendingIntent receiver) {
+    /**
+     * Adds an item to the playlist.
+     */
+    @NonNull
+    public PlaylistItem add(@NonNull String title, @NonNull Uri uri, @NonNull String mime,
+            long startPosition, @Nullable PendingIntent receiver) {
         if (DEBUG) {
             log("add: title=" + title + ", uri=" + uri + ", receiver=" + receiver);
         }
@@ -128,7 +148,11 @@
         return item;
     }
 
-    public PlaylistItem remove(String iid) {
+    /**
+     * Removes an item from the playlist.
+     */
+    @NonNull
+    public PlaylistItem remove(@NonNull String iid) {
         if (DEBUG) {
             log("remove: iid=" + iid);
         }
@@ -136,9 +160,13 @@
         return removeItem(iid, MediaItemStatus.PLAYBACK_STATE_CANCELED);
     }
 
-    public PlaylistItem seek(String iid, long pos) {
+    /**
+     * Seeks a specific position for the current item.
+     */
+    @NonNull
+    public PlaylistItem seek(@NonNull String iid, long pos) {
         if (DEBUG) {
-            log("seek: iid=" + iid +", pos=" + pos);
+            log("seek: iid=" + iid + ", pos=" + pos);
         }
         checkPlayerAndSession();
         // seeking on pending items are not yet supported
@@ -155,7 +183,11 @@
         return item;
     }
 
-    public PlaylistItem getStatus(String iid) {
+    /**
+     * Returns the status for the current item.
+     */
+    @NonNull
+    public PlaylistItem getStatus(@NonNull String iid) {
         checkPlayerAndSession();
 
         // This should only be called for local player. Remote player is
@@ -176,6 +208,9 @@
         return null;
     }
 
+    /**
+     * Pauses the current session.
+     */
     public void pause() {
         if (DEBUG) {
             log("pause");
@@ -188,6 +223,9 @@
         updatePlaybackState();
     }
 
+    /**
+     * Resumes the current session.
+     */
     public void resume() {
         if (DEBUG) {
             log("resume");
@@ -200,6 +238,9 @@
         updatePlaybackState();
     }
 
+    /**
+     * Stops the current session.
+     */
     public void stop() {
         if (DEBUG) {
             log("stop");
@@ -214,6 +255,10 @@
         updateStatus();
     }
 
+    /**
+     * Starts the current session.
+     */
+    @Nullable
     public String startSession() {
         if (!mSessionValid) {
             mSessionId++;
@@ -225,6 +270,9 @@
         return null;
     }
 
+    /**
+     * Ends the current session.
+     */
     public boolean endSession() {
         if (mSessionValid) {
             mSessionValid = false;
@@ -233,7 +281,10 @@
         return false;
     }
 
-    void syncWithManager(SessionManager manager) {
+    /**
+     * Syncs the current session with manager.
+     */
+    public void syncWithManager(@NonNull SessionManager manager) {
         manager.updateStatus();
         mSessionId = manager.mSessionId;
         mItemId = manager.mItemId;
@@ -250,19 +301,25 @@
         updatePlaybackState();
     }
 
-    MediaSessionStatus getSessionStatus(String sid) {
-        int sessionState = (sid != null && sid.equals(Integer.toString(mSessionId))) ?
-                MediaSessionStatus.SESSION_STATE_ACTIVE :
-                    MediaSessionStatus.SESSION_STATE_INVALIDATED;
+    /**
+     * Returns the current session status.
+     */
+    @NonNull
+    public MediaSessionStatus getSessionStatus(@NonNull String sid) {
+        int sessionState = (sid != null && sid.equals(Integer.toString(mSessionId)))
+                ? MediaSessionStatus.SESSION_STATE_ACTIVE
+                : MediaSessionStatus.SESSION_STATE_INVALIDATED;
 
         return new MediaSessionStatus.Builder(sessionState)
                 .setQueuePaused(mPaused)
                 .build();
     }
 
-    // Suspend the playback manager. Put the current item back into PENDING
-    // state, and remember the current playback position. Called when switching
-    // to a different player (route).
+    /**
+     * Suspends the playback manager. Puts the current item back into PENDING
+     * state, and remembers the current playback position. Called when switching
+     * to a different player (route).
+     */
     public void suspend(long pos) {
         for (PlaylistItem item : mPlaylist) {
             item.setRemoteItemId(null);
@@ -281,9 +338,11 @@
         }
     }
 
-    // Unsuspend the playback manager. Restart playback on new player (route).
-    // This will resume playback of current item. Furthermore, if the new player
-    // supports queuing, playlist will be re-established on the remote player.
+    /**
+     * Unsuspends the playback manager. Restarts playback on new player (route).
+     * This will resumes playback of current item. Furthermore, if the new player
+     * supports queuing, playlist will be re-established on the remote player.
+     */
     public void unsuspend() {
         if (DEBUG) {
             log("unsuspend");
@@ -385,7 +444,7 @@
                 if (mPlayer.isQueuingSupported()) {
                     mPlayer.remove(item.getRemoteItemId());
                 } else if (item.getState() == MediaItemStatus.PLAYBACK_STATE_PLAYING
-                        || item.getState() == MediaItemStatus.PLAYBACK_STATE_PAUSED){
+                        || item.getState() == MediaItemStatus.PLAYBACK_STATE_PAUSED) {
                     mPlayer.stop();
                 }
                 item.setState(state);
@@ -410,25 +469,28 @@
     private void finishItem(boolean error) {
         PlaylistItem item = getCurrentItem();
         if (item != null) {
-            removeItem(item.getItemId(), error ?
-                    MediaItemStatus.PLAYBACK_STATE_ERROR :
-                        MediaItemStatus.PLAYBACK_STATE_FINISHED);
+            removeItem(item.getItemId(), error
+                    ? MediaItemStatus.PLAYBACK_STATE_ERROR
+                    : MediaItemStatus.PLAYBACK_STATE_FINISHED);
             updateStatus();
         }
     }
 
-    // set the Player that this playback manager will interact with
-    public void setPlayer(Player player) {
+    /**
+     * Set the Player that this playback manager will interact with.
+     */
+    public void setPlayer(@NonNull Player player) {
         mPlayer = player;
         checkPlayer();
         mPlayer.setCallback(this);
     }
 
     // provide a callback interface to tell the UI when significant state changes occur
-    public void setCallback(Callback callback) {
+    public void setCallback(@NonNull Callback callback) {
         mCallback = callback;
     }
 
+    @NonNull
     @Override
     public String toString() {
         String result = "Media Queue: ";
@@ -442,8 +504,18 @@
         return result;
     }
 
+    /**
+     * Session Manager callback.
+     */
     public interface Callback {
+        /**
+         * Called when the status of the manager is changed.
+         */
         void onStatusChanged();
-        void onItemChanged(PlaylistItem item);
+
+        /**
+         * Called when the current active item is changed.
+         */
+        void onItemChanged(@NonNull PlaylistItem item);
     }
 }
diff --git a/samples/AndroidXDemos/src/main/java/com/example/androidx/media/_index.html b/samples/MediaRoutingDemo/src/main/java/com/example/androidx/mediarouting/_index.html
similarity index 100%
rename from samples/AndroidXDemos/src/main/java/com/example/androidx/media/_index.html
rename to samples/MediaRoutingDemo/src/main/java/com/example/androidx/mediarouting/_index.html
diff --git a/samples/MediaRoutingDemo/src/main/res/drawable-hdpi/app_sample_code.png b/samples/MediaRoutingDemo/src/main/res/drawable-hdpi/app_sample_code.png
new file mode 100755
index 0000000..66a1984
--- /dev/null
+++ b/samples/MediaRoutingDemo/src/main/res/drawable-hdpi/app_sample_code.png
Binary files differ
diff --git a/samples/MediaRoutingDemo/src/main/res/drawable-hdpi/ic_android.png b/samples/MediaRoutingDemo/src/main/res/drawable-hdpi/ic_android.png
new file mode 100755
index 0000000..94b8fb1
--- /dev/null
+++ b/samples/MediaRoutingDemo/src/main/res/drawable-hdpi/ic_android.png
Binary files differ
diff --git a/samples/MediaRoutingDemo/src/main/res/drawable-hdpi/ic_media_pause.png b/samples/MediaRoutingDemo/src/main/res/drawable-hdpi/ic_media_pause.png
new file mode 100644
index 0000000..1d465a4
--- /dev/null
+++ b/samples/MediaRoutingDemo/src/main/res/drawable-hdpi/ic_media_pause.png
Binary files differ
diff --git a/samples/MediaRoutingDemo/src/main/res/drawable-hdpi/ic_media_play.png b/samples/MediaRoutingDemo/src/main/res/drawable-hdpi/ic_media_play.png
new file mode 100644
index 0000000..2746d17
--- /dev/null
+++ b/samples/MediaRoutingDemo/src/main/res/drawable-hdpi/ic_media_play.png
Binary files differ
diff --git a/samples/MediaRoutingDemo/src/main/res/drawable-hdpi/ic_media_stop.png b/samples/MediaRoutingDemo/src/main/res/drawable-hdpi/ic_media_stop.png
new file mode 100644
index 0000000..a0ff136
--- /dev/null
+++ b/samples/MediaRoutingDemo/src/main/res/drawable-hdpi/ic_media_stop.png
Binary files differ
diff --git a/samples/MediaRoutingDemo/src/main/res/drawable-hdpi/ic_menu_add.png b/samples/MediaRoutingDemo/src/main/res/drawable-hdpi/ic_menu_add.png
new file mode 100644
index 0000000..444e8a5
--- /dev/null
+++ b/samples/MediaRoutingDemo/src/main/res/drawable-hdpi/ic_menu_add.png
Binary files differ
diff --git a/samples/MediaRoutingDemo/src/main/res/drawable-hdpi/ic_menu_delete.png b/samples/MediaRoutingDemo/src/main/res/drawable-hdpi/ic_menu_delete.png
new file mode 100644
index 0000000..24d8f6a
--- /dev/null
+++ b/samples/MediaRoutingDemo/src/main/res/drawable-hdpi/ic_menu_delete.png
Binary files differ
diff --git a/samples/MediaRoutingDemo/src/main/res/drawable-mdpi/app_sample_code.png b/samples/MediaRoutingDemo/src/main/res/drawable-mdpi/app_sample_code.png
new file mode 100644
index 0000000..5ae7701
--- /dev/null
+++ b/samples/MediaRoutingDemo/src/main/res/drawable-mdpi/app_sample_code.png
Binary files differ
diff --git a/samples/MediaRoutingDemo/src/main/res/drawable-mdpi/ic_android.png b/samples/MediaRoutingDemo/src/main/res/drawable-mdpi/ic_android.png
new file mode 100755
index 0000000..afc43db
--- /dev/null
+++ b/samples/MediaRoutingDemo/src/main/res/drawable-mdpi/ic_android.png
Binary files differ
diff --git a/samples/MediaRoutingDemo/src/main/res/drawable-mdpi/ic_media_pause.png b/samples/MediaRoutingDemo/src/main/res/drawable-mdpi/ic_media_pause.png
new file mode 100644
index 0000000..3e6b2a1
--- /dev/null
+++ b/samples/MediaRoutingDemo/src/main/res/drawable-mdpi/ic_media_pause.png
Binary files differ
diff --git a/samples/MediaRoutingDemo/src/main/res/drawable-mdpi/ic_media_play.png b/samples/MediaRoutingDemo/src/main/res/drawable-mdpi/ic_media_play.png
new file mode 100644
index 0000000..7966bbc
--- /dev/null
+++ b/samples/MediaRoutingDemo/src/main/res/drawable-mdpi/ic_media_play.png
Binary files differ
diff --git a/samples/MediaRoutingDemo/src/main/res/drawable-mdpi/ic_media_stop.png b/samples/MediaRoutingDemo/src/main/res/drawable-mdpi/ic_media_stop.png
new file mode 100644
index 0000000..8ea7efe
--- /dev/null
+++ b/samples/MediaRoutingDemo/src/main/res/drawable-mdpi/ic_media_stop.png
Binary files differ
diff --git a/samples/MediaRoutingDemo/src/main/res/drawable-mdpi/ic_menu_add.png b/samples/MediaRoutingDemo/src/main/res/drawable-mdpi/ic_menu_add.png
new file mode 100644
index 0000000..361c7c4
--- /dev/null
+++ b/samples/MediaRoutingDemo/src/main/res/drawable-mdpi/ic_menu_add.png
Binary files differ
diff --git a/samples/MediaRoutingDemo/src/main/res/drawable-mdpi/ic_menu_delete.png b/samples/MediaRoutingDemo/src/main/res/drawable-mdpi/ic_menu_delete.png
new file mode 100644
index 0000000..e2c8700
--- /dev/null
+++ b/samples/MediaRoutingDemo/src/main/res/drawable-mdpi/ic_menu_delete.png
Binary files differ
diff --git a/samples/AndroidXDemos/src/main/res/layout/media_item.xml b/samples/MediaRoutingDemo/src/main/res/layout/media_item.xml
similarity index 100%
rename from samples/AndroidXDemos/src/main/res/layout/media_item.xml
rename to samples/MediaRoutingDemo/src/main/res/layout/media_item.xml
diff --git a/samples/MediaRoutingDemo/src/main/res/layout/overlay_display_window.xml b/samples/MediaRoutingDemo/src/main/res/layout/overlay_display_window.xml
new file mode 100644
index 0000000..1f026fe
--- /dev/null
+++ b/samples/MediaRoutingDemo/src/main/res/layout/overlay_display_window.xml
@@ -0,0 +1,30 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2012 The Android Open Source Project
+
+     Licensed under the Apache License, Version 2.0 (the "License");
+     you may not use this file except in compliance with the License.
+     You may obtain a copy of the License at
+
+          http://www.apache.org/licenses/LICENSE-2.0
+
+     Unless required by applicable law or agreed to in writing, software
+     distributed under the License is distributed on an "AS IS" BASIS,
+     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+     See the License for the specific language governing permissions and
+     limitations under the License.
+-->
+
+<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
+      xmlns:tools="http://schemas.android.com/tools"
+      android:layout_width="match_parent"
+      android:layout_height="match_parent"
+      android:background="#000000">
+    <TextureView tools:ignore="NewApi"
+               android:id="@+id/overlay_display_window_texture"
+               android:layout_width="0px"
+               android:layout_height="0px" />
+    <TextView android:id="@+id/overlay_display_window_title"
+               android:layout_width="wrap_content"
+               android:layout_height="wrap_content"
+               android:layout_gravity="top|center_horizontal" />
+</FrameLayout>
diff --git a/samples/AndroidXDemos/src/main/res/layout/sample_media_controller.xml b/samples/MediaRoutingDemo/src/main/res/layout/sample_media_controller.xml
similarity index 100%
rename from samples/AndroidXDemos/src/main/res/layout/sample_media_controller.xml
rename to samples/MediaRoutingDemo/src/main/res/layout/sample_media_controller.xml
diff --git a/samples/AndroidXDemos/src/main/res/layout/sample_media_router.xml b/samples/MediaRoutingDemo/src/main/res/layout/sample_media_router.xml
similarity index 100%
rename from samples/AndroidXDemos/src/main/res/layout/sample_media_router.xml
rename to samples/MediaRoutingDemo/src/main/res/layout/sample_media_router.xml
diff --git a/samples/AndroidXDemos/src/main/res/layout/sample_media_router_presentation.xml b/samples/MediaRoutingDemo/src/main/res/layout/sample_media_router_presentation.xml
similarity index 100%
rename from samples/AndroidXDemos/src/main/res/layout/sample_media_router_presentation.xml
rename to samples/MediaRoutingDemo/src/main/res/layout/sample_media_router_presentation.xml
diff --git a/samples/AndroidXDemos/src/main/res/menu/sample_media_router_menu.xml b/samples/MediaRoutingDemo/src/main/res/menu/sample_media_router_menu.xml
similarity index 100%
rename from samples/AndroidXDemos/src/main/res/menu/sample_media_router_menu.xml
rename to samples/MediaRoutingDemo/src/main/res/menu/sample_media_router_menu.xml
diff --git a/samples/MediaRoutingDemo/src/main/res/values-v18/bools.xml b/samples/MediaRoutingDemo/src/main/res/values-v18/bools.xml
new file mode 100644
index 0000000..438733c
--- /dev/null
+++ b/samples/MediaRoutingDemo/src/main/res/values-v18/bools.xml
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+  Copyright 2019 The Android Open Source Project
+
+  Licensed under the Apache License, Version 2.0 (the "License");
+  you may not use this file except in compliance with the License.
+  You may obtain a copy of the License at
+
+       http://www.apache.org/licenses/LICENSE-2.0
+
+  Unless required by applicable law or agreed to in writing, software
+  distributed under the License is distributed on an "AS IS" BASIS,
+  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+  See the License for the specific language governing permissions and
+  limitations under the License.
+  -->
+
+<resources>
+    <!-- True if running under JellyBean MR2 or later. -->
+    <bool name="atLeastJellyBeanMR2">true</bool>
+</resources>
\ No newline at end of file
diff --git a/samples/MediaRoutingDemo/src/main/res/values/arrays.xml b/samples/MediaRoutingDemo/src/main/res/values/arrays.xml
new file mode 100644
index 0000000..3e87f3a
--- /dev/null
+++ b/samples/MediaRoutingDemo/src/main/res/values/arrays.xml
@@ -0,0 +1,41 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2013 The Android Open Source Project
+
+     Licensed under the Apache License, Version 2.0 (the "License");
+     you may not use this file except in compliance with the License.
+     You may obtain a copy of the License at
+
+          http://www.apache.org/licenses/LICENSE-2.0
+
+     Unless required by applicable law or agreed to in writing, software
+     distributed under the License is distributed on an "AS IS" BASIS,
+     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+     See the License for the specific language governing permissions and
+     limitations under the License.
+-->
+
+<resources>
+    <string-array name="media_names">
+        <item>Big Buck Bunny</item>
+        <item>Elephants Dream</item>
+        <item>Sintel</item>
+        <item>Tears of Steel</item>
+        <item>(Music) Let\'s Dance</item>
+    </string-array>
+
+    <string-array name="media_uris">
+        <item>https://archive.org/download/BigBuckBunny_328/BigBuckBunny_512kb.mp4</item>
+        <item>https://archive.org/download/ElephantsDream_277/elephant_dreams_640_512kb.mp4</item>
+        <item>https://archive.org/download/Sintel/sintel-2048-stereo_512kb.mp4</item>
+        <item>https://archive.org/download/Tears-of-Steel/tears_of_steel_720p.mp4</item>
+        <item>https://archive.org/download/78_lets-dance-theme-song_baldridge-stone-bonime-benny-goodman-and-his-orchestra_gbia0000390b/Let%27s%20Dance%20%28Theme%20Song%29%20-%20Baldridge%20-%20Stone.mp3</item>
+    </string-array>
+
+    <string-array name="media_mimes">
+        <item>video/mp4</item>
+        <item>video/mp4</item>
+        <item>video/mp4</item>
+        <item>video/mp4</item>
+        <item>audio/mp3</item>
+    </string-array>
+</resources>
diff --git a/samples/MediaRoutingDemo/src/main/res/values/bools.xml b/samples/MediaRoutingDemo/src/main/res/values/bools.xml
new file mode 100644
index 0000000..c10c13e
--- /dev/null
+++ b/samples/MediaRoutingDemo/src/main/res/values/bools.xml
@@ -0,0 +1,23 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+  Copyright 2019 The Android Open Source Project
+
+  Licensed under the Apache License, Version 2.0 (the "License");
+  you may not use this file except in compliance with the License.
+  You may obtain a copy of the License at
+
+       http://www.apache.org/licenses/LICENSE-2.0
+
+  Unless required by applicable law or agreed to in writing, software
+  distributed under the License is distributed on an "AS IS" BASIS,
+  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+  See the License for the specific language governing permissions and
+  limitations under the License.
+  -->
+
+<resources>
+    <!-- This resource is true if running under at least JellyBean MR2
+         API level.  The default value is false; an alternative value
+         for JellyBean MR 2 is true. -->
+    <bool name="atLeastJellyBeanMR2">false</bool>
+</resources>
diff --git a/samples/MediaRoutingDemo/src/main/res/values/strings.xml b/samples/MediaRoutingDemo/src/main/res/values/strings.xml
new file mode 100644
index 0000000..664c28a
--- /dev/null
+++ b/samples/MediaRoutingDemo/src/main/res/values/strings.xml
@@ -0,0 +1,63 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2013 The Android Open Source Project
+
+     Licensed under the Apache License, Version 2.0 (the "License");
+     you may not use this file except in compliance with the License.
+     You may obtain a copy of the License at
+
+          http://www.apache.org/licenses/LICENSE-2.0
+
+     Unless required by applicable law or agreed to in writing, software
+     distributed under the License is distributed on an "AS IS" BASIS,
+     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+     See the License for the specific language governing permissions and
+     limitations under the License.
+-->
+
+<resources>
+    <string name="activity_sample_code">Media Routing Demo</string>
+
+    <!-- MediaRouter -->
+
+    <string name="sample_media_router_activity_dark">MediaRouter/Dark Theme</string>
+    <string name="sample_media_router_activity_light">MediaRouter/Light Theme</string>
+    <string name="sample_media_router_activity_light_with_dark_action_bar">MediaRouter/Light Theme, Dark Action Bar</string>
+    <string name="sample_media_router_activity_dynamic_group">MediaRouter/Dynamic Group
+        Dialog</string>
+    <string name="sample_media_router_activity_output_switcher">MediaRouter/Output Switcher</string>
+    <string name="sample_media_router_activity_legacy">MediaRouter/Disable MediaRouter2</string>
+    <string name="sample_media_router_text">This activity demonstrates how to
+            use MediaRouter from the support library.  Select a route from the action bar.</string>
+    <string name="media_route_menu_title">Play on...</string>
+    <string name="sample_media_route_settings_activity">Sample route settings</string>
+
+    <string name="use_default_media_control">Use default media control</string>
+    <string name="my_media_control_text">My Media Control</string>
+
+    <string name="library_tab_text">Library</string>
+    <string name="playlist_tab_text">Playlist</string>
+    <string name="info_tab_text">Route Info</string>
+
+    <string name="sample_media_route_provider_service">Media Route Provider Service Support Library Sample</string>
+    <string name="fixed_volume_route_name">Fixed Volume Remote Playback Route</string>
+    <string name="variable_volume_basic_route_name">Variable Volume (Basic) Remote Playback Route</string>
+    <string name="variable_volume_queuing_route_name">Variable Volume (Queuing) Remote Playback Route</string>
+    <string name="variable_volume_session_route_name">Variable Volume (Session) Remote Playback Route</string>
+    <string name="variable_volume_route_group_name">Variable Volume Route Group</string>
+    <string name="mixed_volume_route_group_name">Mixed Volume Route Group</string>
+    <string name="sample_route_description">Sample route from AndroidXDemos</string>
+
+    <string name="sample_dynamic_group_mrp_service">Media Route Provider Service Support Library Sample (supporting dynamic group)</string>
+    <string name="dg_tv_route_name1">Dynamic Route 1 - TV</string>
+    <string name="dg_tv_route_name2">Dynamic Route 2 - TV</string>
+    <string name="dg_speaker_route_name3">Dynamic Route 3 - Speaker</string>
+    <string name="dg_speaker_route_name4">Dynamic Route 4 - Speaker</string>
+    <string name="dg_not_unselectable_route_name5"> Dynamic Route 5 - Not unselectable</string>
+    <string name="dg_static_group_route_name6"> Dynamic Route 6 - Static Group</string>
+
+    <string name="sample_media_route_provider_remote">Remote Playback (Simulated)</string>
+    <string name="sample_media_route_activity_local">Local Playback</string>
+    <string name="sample_media_route_activity_presentation">Local Playback on Presentation Display</string>
+
+    <string name="action_mode_done_content_description">Cancel</string>
+</resources>
diff --git a/samples/MediaRoutingDemo/src/main/res/values/styles.xml b/samples/MediaRoutingDemo/src/main/res/values/styles.xml
new file mode 100644
index 0000000..c914ac3
--- /dev/null
+++ b/samples/MediaRoutingDemo/src/main/res/values/styles.xml
@@ -0,0 +1,33 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+     Copyright (C) 2014 The Android Open Source Project
+
+     Licensed under the Apache License, Version 2.0 (the "License");
+     you may not use this file except in compliance with the License.
+     You may obtain a copy of the License at
+
+          http://www.apache.org/licenses/LICENSE-2.0
+
+     Unless required by applicable law or agreed to in writing, software
+     distributed under the License is distributed on an "AS IS" BASIS,
+     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+     See the License for the specific language governing permissions and
+     limitations under the License.
+-->
+
+<resources>
+    <style name="Theme.SampleMediaRouter" parent="Theme.AppCompat">
+        <item name="colorPrimary">#fff44336</item>
+        <item name="colorPrimaryDark">#d32f2f</item>
+    </style>
+
+    <style name="Theme.SampleMediaRouter.Light" parent="Theme.AppCompat.Light">
+        <item name="colorPrimary">#ffff9800</item>
+        <item name="colorPrimaryDark">#f57c00</item>
+    </style>
+
+    <style name="Theme.SampleMediaRouter.Light.DarkActionBar" parent="Theme.AppCompat.Light.DarkActionBar">
+        <item name="colorPrimary">#ff2196f3</item>
+        <item name="colorPrimaryDark">#1976d2</item>
+    </style>
+</resources>
diff --git a/settings.gradle b/settings.gradle
index afcd43e..4e3b1e2 100644
--- a/settings.gradle
+++ b/settings.gradle
@@ -982,6 +982,7 @@
 // Note: don't add new samples/ apps. Instead, Create
 // <module>/integration-tests/testapp in the "Libraries" section above.
 includeProject(":androidx-demos", new File(samplesRoot, "AndroidXDemos"), [BuildType.MAIN])
+includeProject(":media-routing-demo", new File(samplesRoot, "MediaRoutingDemo"), [BuildType.MAIN])
 includeProject(":support-animation-demos", new File(samplesRoot, "SupportAnimationDemos"), [BuildType.MAIN])
 includeProject(":support-content-demos", new File(samplesRoot, "SupportContentDemos"), [BuildType.MAIN])
 includeProject(":support-emoji-demos", new File(samplesRoot, "SupportEmojiDemos"), [BuildType.MAIN])