Integrate remote display routes into screen cast settings UI.

Merged remote display route selection into the existing wifi
display preference UI.  Moved the on/off toggle over to a menu item.

The preference page is now mainly implemented in terms of the
media router.  It only interacts with the display manager for the purpose
of pairing with new devices or making wifi display certification
controls available.

This means that the observed state is now completely consistent across
the system ui, settings, and applications that use the media router.

Bug: 11257292
Change-Id: I3705570812384fef4bfffeccaaccf7895d370d12
diff --git a/AndroidManifest.xml b/AndroidManifest.xml
index b730ba7..7c4be12 100644
--- a/AndroidManifest.xml
+++ b/AndroidManifest.xml
@@ -1343,7 +1343,6 @@
         </activity>
 
         <activity android:name="Settings$WifiDisplaySettingsActivity"
-                android:uiOptions="splitActionBarWhenNarrow"
                 android:label="@string/wifi_display_settings_title"
                 android:taskAffinity=""
                 android:excludeFromRecents="true">
diff --git a/res/values/strings.xml b/res/values/strings.xml
index bb537bf..e32ac43 100644
--- a/res/values/strings.xml
+++ b/res/values/strings.xml
@@ -1226,37 +1226,22 @@
     <string name="bluetooth_dock_settings_remember">Remember settings</string>
 
     <!-- Wifi Display settings. The title of the screen. [CHAR LIMIT=40] -->
-    <string name="wifi_display_settings_title">Wireless display</string>
-    <!-- Wifi Display settings. Text displayed when Wifi display is off and device list is empty [CHAR LIMIT=80]-->
-    <string name="wifi_display_settings_empty_list_wifi_display_off">To see devices, turn wireless display on.</string>
-    <!-- Wifi Display settings. Text displayed when Wifi Display is off and device list is empty [CHAR LIMIT=80]-->
-    <string name="wifi_display_settings_empty_list_wifi_display_disabled">Wireless display is disabled because Wi\u2011Fi is off.</string>
-    <!-- Wifi Display settings. The title of the action button that initiates a search for nearby devices [CHAR LIMIT=20] -->
-    <string name="wifi_display_search_for_devices">Search for displays</string>
-    <!-- Wifi Display settings. The title of the action button while a search for nearby devices is in progress [CHAR LIMIT=20] -->
-    <string name="wifi_display_searching_for_devices">Searching\u2026</string>
+    <string name="wifi_display_settings_title">Cast screen</string>
+    <!-- Wifi Display settings. The title of a menu item to enable wireless display [CHAR LIMIT=40] -->
+    <string name="wifi_display_enable_menu_item">Enable wireless display</string>
     <!-- Wifi Display settings. Text that appears when scanning for devices is finished and no nearby device was found [CHAR LIMIT=40]-->
-    <string name="wifi_display_no_devices_found">No nearby wireless displays were found.</string>
-    <!-- Wifi Display settings. The sub heading for devices which have already been paired with this device. [CHAR LIMIT=40] -->
-    <string name="wifi_display_paired_devices">Paired displays</string>
-    <!-- Wifi Display settings. The sub heading for available devices during and after scanning. [CHAR LIMIT=40] -->
-    <string name="wifi_display_available_devices">Available devices</string>
+    <string name="wifi_display_no_devices_found">No nearby devices were found.</string>
     <!-- Wifi Display settings. The status summary for connecting devices. [CHAR LIMIT=40] -->
     <string name="wifi_display_status_connecting">Connecting</string>
     <!-- Wifi Display settings. The status summary for connected devices. [CHAR LIMIT=40] -->
     <string name="wifi_display_status_connected">Connected</string>
-    <!-- Wifi Display settings. The status summary for available devices. [CHAR LIMIT=40] -->
-    <string name="wifi_display_status_available">Available</string>
     <!-- Wifi Display settings. The status summary for devices that's already in use. [CHAR LIMIT=40] -->
     <string name="wifi_display_status_in_use">In use</string>
+    <!-- Wifi Display settings. The status summary for devices that's not available. [CHAR LIMIT=40] -->
+    <string name="wifi_display_status_not_available">Unavailable</string>
     <!-- Wifi Display settings. Image description for device details button. This opens the screen to rename, unpair, etc. a single device. -->
     <string name="wifi_display_details">Display settings</string>
 
-    <!-- Wifi Display settings. Disconnect dialog.  The title of the dialog. [CHAR LIMIT=40] -->
-    <string name="wifi_display_disconnect_title">Disconnect?</string>
-    <!-- Wifi Display settings. Disconnect dialog.  Message for disconnecting from the display. [CHAR LIMIT=NONE] -->
-    <string name="wifi_display_disconnect_text">This will end your connection with:&lt;br>&lt;b><xliff:g id="device_name">%1$s</xliff:g>&lt;/b></string>
-
     <!-- Wifi Display settings. Options dialog.  The title of the dialog. [CHAR LIMIT=40] -->
     <string name="wifi_display_options_title">Wireless display options</string>
     <!-- Wifi Display settings. Options dialog.  The forget button text. [CHAR LIMIT=20] -->
@@ -1266,13 +1251,6 @@
     <!-- Wifi Display settings. Options dialog.  The name label used when prompting the user to rename the display. [CHAR LIMIT=20] -->
     <string name="wifi_display_options_name">Name</string>
 
-    <!-- Wifi Display settings. Summary shown in Display settings. Text used for 'On' state. [CHAR LIMIT=40] -->
-    <string name="wifi_display_summary_on">On</string>
-    <!-- Wifi Display settings. Summary shown in Display settings. Text used for 'Off' state. [CHAR LIMIT=40] -->
-    <string name="wifi_display_summary_off">Off</string>
-    <!-- Wifi Display settings. Summary shown in Display settings. Text used for 'Disabled' state. [CHAR LIMIT=40] -->
-    <string name="wifi_display_summary_disabled">Disabled</string>
-
     <!-- Wifi Display settings. The sub heading for wireless display certification options. [CHAR LIMIT=40] -->
     <string name="wifi_display_certification_heading" translatable="false">Certification</string>
     <!-- Wifi Display settings. The section title for wireless display session info. [CHAR LIMIT=40] -->
diff --git a/src/com/android/settings/DisplaySettings.java b/src/com/android/settings/DisplaySettings.java
index c379ec3..7772b7c 100644
--- a/src/com/android/settings/DisplaySettings.java
+++ b/src/com/android/settings/DisplaySettings.java
@@ -21,16 +21,10 @@
 import android.app.ActivityManagerNative;
 import android.app.Dialog;
 import android.app.admin.DevicePolicyManager;
-import android.content.BroadcastReceiver;
 import android.content.ContentResolver;
 import android.content.Context;
-import android.content.Intent;
-import android.content.IntentFilter;
 import android.content.res.Configuration;
 import android.content.res.Resources;
-import android.hardware.display.DisplayManager;
-import android.hardware.display.WifiDisplay;
-import android.hardware.display.WifiDisplayStatus;
 import android.os.Bundle;
 import android.os.RemoteException;
 import android.preference.CheckBoxPreference;
@@ -40,7 +34,6 @@
 import android.preference.PreferenceScreen;
 import android.provider.Settings;
 import android.provider.Settings.SettingNotFoundException;
-import android.util.AttributeSet;
 import android.util.Log;
 
 import com.android.internal.view.RotationPolicy;
@@ -60,12 +53,9 @@
     private static final String KEY_FONT_SIZE = "font_size";
     private static final String KEY_NOTIFICATION_PULSE = "notification_pulse";
     private static final String KEY_SCREEN_SAVER = "screensaver";
-    private static final String KEY_WIFI_DISPLAY = "wifi_display";
 
     private static final int DLG_GLOBAL_CHANGE_WARNING = 1;
 
-    private DisplayManager mDisplayManager;
-
     private CheckBoxPreference mAccelerometer;
     private WarnedListPreference mFontSizePref;
     private CheckBoxPreference mNotificationPulse;
@@ -75,9 +65,6 @@
     private ListPreference mScreenTimeoutPreference;
     private Preference mScreenSaverPreference;
 
-    private WifiDisplayStatus mWifiDisplayStatus;
-    private Preference mWifiDisplayPreference;
-
     private final RotationPolicy.RotationPolicyListener mRotationPolicyListener =
             new RotationPolicy.RotationPolicyListener() {
         @Override
@@ -135,16 +122,6 @@
                 Log.e(TAG, Settings.System.NOTIFICATION_LIGHT_PULSE + " not found");
             }
         }
-
-        mDisplayManager = (DisplayManager)getActivity().getSystemService(
-                Context.DISPLAY_SERVICE);
-        mWifiDisplayStatus = mDisplayManager.getWifiDisplayStatus();
-        mWifiDisplayPreference = (Preference)findPreference(KEY_WIFI_DISPLAY);
-        if (mWifiDisplayStatus.getFeatureState()
-                == WifiDisplayStatus.FEATURE_STATE_UNAVAILABLE) {
-            getPreferenceScreen().removePreference(mWifiDisplayPreference);
-            mWifiDisplayPreference = null;
-        }
     }
 
     private void updateTimeoutPreferenceDescription(long currentTimeout) {
@@ -252,12 +229,6 @@
         RotationPolicy.registerRotationPolicyListener(getActivity(),
                 mRotationPolicyListener);
 
-        if (mWifiDisplayPreference != null) {
-            getActivity().registerReceiver(mReceiver, new IntentFilter(
-                    DisplayManager.ACTION_WIFI_DISPLAY_STATUS_CHANGED));
-            mWifiDisplayStatus = mDisplayManager.getWifiDisplayStatus();
-        }
-
         updateState();
     }
 
@@ -267,10 +238,6 @@
 
         RotationPolicy.unregisterRotationPolicyListener(getActivity(),
                 mRotationPolicyListener);
-
-        if (mWifiDisplayPreference != null) {
-            getActivity().unregisterReceiver(mReceiver);
-        }
     }
 
     @Override
@@ -291,7 +258,6 @@
         updateAccelerometerRotationCheckbox();
         readFontSizePreference(mFontSizePref);
         updateScreenSaverSummary();
-        updateWifiDisplaySummary();
     }
 
     private void updateScreenSaverSummary() {
@@ -301,23 +267,6 @@
         }
     }
 
-    private void updateWifiDisplaySummary() {
-        if (mWifiDisplayPreference != null) {
-            switch (mWifiDisplayStatus.getFeatureState()) {
-                case WifiDisplayStatus.FEATURE_STATE_OFF:
-                    mWifiDisplayPreference.setSummary(R.string.wifi_display_summary_off);
-                    break;
-                case WifiDisplayStatus.FEATURE_STATE_ON:
-                    mWifiDisplayPreference.setSummary(R.string.wifi_display_summary_on);
-                    break;
-                case WifiDisplayStatus.FEATURE_STATE_DISABLED:
-                default:
-                    mWifiDisplayPreference.setSummary(R.string.wifi_display_summary_disabled);
-                    break;
-            }
-        }
-    }
-
     private void updateAccelerometerRotationCheckbox() {
         if (getActivity() == null) return;
 
@@ -347,6 +296,7 @@
         return super.onPreferenceTreeClick(preferenceScreen, preference);
     }
 
+    @Override
     public boolean onPreferenceChange(Preference preference, Object objValue) {
         final String key = preference.getKey();
         if (KEY_SCREEN_TIMEOUT.equals(key)) {
@@ -365,17 +315,6 @@
         return true;
     }
 
-    private final BroadcastReceiver mReceiver = new BroadcastReceiver() {
-        @Override
-        public void onReceive(Context context, Intent intent) {
-            if (intent.getAction().equals(DisplayManager.ACTION_WIFI_DISPLAY_STATUS_CHANGED)) {
-                mWifiDisplayStatus = (WifiDisplayStatus)intent.getParcelableExtra(
-                        DisplayManager.EXTRA_WIFI_DISPLAY_STATUS);
-                updateWifiDisplaySummary();
-            }
-        }
-    };
-
     @Override
     public boolean onPreferenceClick(Preference preference) {
         if (preference == mFontSizePref) {
diff --git a/src/com/android/settings/Settings.java b/src/com/android/settings/Settings.java
index e93fa18..db7eb2b 100644
--- a/src/com/android/settings/Settings.java
+++ b/src/com/android/settings/Settings.java
@@ -506,7 +506,6 @@
         // uiOptions for fragments also defined as activities in manifest.
         if (WifiSettings.class.getName().equals(fragmentName) ||
                 WifiP2pSettings.class.getName().equals(fragmentName) ||
-                WifiDisplaySettings.class.getName().equals(fragmentName) ||
                 BluetoothSettings.class.getName().equals(fragmentName) ||
                 DreamSettings.class.getName().equals(fragmentName) ||
                 LocationSettings.class.getName().equals(fragmentName) ||
diff --git a/src/com/android/settings/wfd/WifiDisplaySettings.java b/src/com/android/settings/wfd/WifiDisplaySettings.java
index 6ec79f3..d04f6f2 100755
--- a/src/com/android/settings/wfd/WifiDisplaySettings.java
+++ b/src/com/android/settings/wfd/WifiDisplaySettings.java
@@ -29,6 +29,8 @@
 import android.hardware.display.DisplayManager;
 import android.hardware.display.WifiDisplay;
 import android.hardware.display.WifiDisplayStatus;
+import android.media.MediaRouter;
+import android.media.MediaRouter.RouteInfo;
 import android.net.Uri;
 import android.net.wifi.p2p.WifiP2pManager;
 import android.net.wifi.p2p.WifiP2pManager.ActionListener;
@@ -63,31 +65,48 @@
 import android.widget.Switch;
 import android.widget.TextView;
 
+import com.android.internal.app.MediaRouteDialogPresenter;
 import com.android.settings.ProgressCategory;
 import com.android.settings.R;
 import com.android.settings.SettingsPreferenceFragment;
 
 /**
  * The Settings screen for WifiDisplay configuration and connection management.
+ *
+ * The wifi display routes are integrated together with other remote display routes
+ * from the media router.  It may happen that wifi display isn't actually available
+ * on the system.  In that case, the enable option will not be shown but other
+ * remote display routes will continue to be made available.
  */
 public final class WifiDisplaySettings extends SettingsPreferenceFragment {
     private static final String TAG = "WifiDisplaySettings";
     private static final boolean DEBUG = false;
 
-    private static final int MENU_ID_SCAN = Menu.FIRST;
+    private static final int MENU_ID_ENABLE_WIFI_DISPLAY = Menu.FIRST;
 
+    private static final int CHANGE_SETTINGS = 1 << 0;
+    private static final int CHANGE_ROUTES = 1 << 1;
+    private static final int CHANGE_WIFI_DISPLAY_STATUS = 1 << 2;
+    private static final int CHANGE_ALL = -1;
+
+    private static final int ORDER_CERTIFICATION = 1;
+    private static final int ORDER_CONNECTED = 2;
+    private static final int ORDER_AVAILABLE = 3;
+    private static final int ORDER_UNAVAILABLE = 4;
+
+    private final Handler mHandler;
+
+    private MediaRouter mRouter;
     private DisplayManager mDisplayManager;
 
+    private boolean mStarted;
+    private int mPendingChanges;
+
     private boolean mWifiDisplayOnSetting;
     private WifiDisplayStatus mWifiDisplayStatus;
 
-    private PreferenceGroup mPairedDevicesCategory;
-    private ProgressCategory mAvailableDevicesCategory;
-
     private TextView mEmptyView;
 
-    private Switch mActionBarSwitch;
-
     /* certification */
     private boolean mWifiDisplayCertificationOn;
     private WifiP2pManager mWifiP2pManager;
@@ -100,15 +119,18 @@
     private int mOperatingChannel;
 
     public WifiDisplaySettings() {
+        mHandler = new Handler();
     }
 
     @Override
     public void onCreate(Bundle icicle) {
         super.onCreate(icicle);
 
-        mDisplayManager = (DisplayManager)getActivity().getSystemService(Context.DISPLAY_SERVICE);
-        mWifiP2pManager = (WifiP2pManager)getActivity().getSystemService(Context.WIFI_P2P_SERVICE);
-        mWifiP2pChannel = mWifiP2pManager.initialize(getActivity(), Looper.getMainLooper(), null);
+        final Context context = getActivity();
+        mRouter = (MediaRouter)context.getSystemService(Context.MEDIA_ROUTER_SERVICE);
+        mDisplayManager = (DisplayManager)context.getSystemService(Context.DISPLAY_SERVICE);
+        mWifiP2pManager = (WifiP2pManager)context.getSystemService(Context.WIFI_P2P_SERVICE);
+        mWifiP2pChannel = mWifiP2pManager.initialize(context, Looper.getMainLooper(), null);
 
         addPreferencesFromResource(R.xml.wifi_display_settings);
         setHasOptionsMenu(true);
@@ -118,42 +140,17 @@
     public void onActivityCreated(Bundle savedInstanceState) {
         super.onActivityCreated(savedInstanceState);
 
-        Activity activity = getActivity();
-        mActionBarSwitch = new Switch(activity);
-        mActionBarSwitch.setOnCheckedChangeListener(mSwitchOnCheckedChangedListener);
-
-        final int padding = activity.getResources().getDimensionPixelSize(
-                R.dimen.action_bar_switch_padding);
-        mActionBarSwitch.setPaddingRelative(0, 0, padding, 0);
-        activity.getActionBar().setDisplayOptions(ActionBar.DISPLAY_SHOW_CUSTOM,
-                ActionBar.DISPLAY_SHOW_CUSTOM);
-        activity.getActionBar().setCustomView(mActionBarSwitch,
-                new ActionBar.LayoutParams(
-                        ActionBar.LayoutParams.WRAP_CONTENT,
-                        ActionBar.LayoutParams.WRAP_CONTENT,
-                        Gravity.CENTER_VERTICAL | Gravity.END));
-
         mEmptyView = (TextView) getView().findViewById(android.R.id.empty);
+        mEmptyView.setText(R.string.wifi_display_no_devices_found);
         getListView().setEmptyView(mEmptyView);
-
-        update();
-
-        if (mWifiDisplayStatus.getFeatureState() == WifiDisplayStatus.FEATURE_STATE_UNAVAILABLE) {
-            activity.finish();
-        }
     }
 
     @Override
-    public void onDestroyView() {
-        getActivity().getActionBar().setCustomView(null);
-        super.onDestroyView();
-    }
+    public void onStart() {
+        super.onStart();
+        mStarted = true;
 
-    @Override
-    public void onResume() {
-        super.onResume();
-
-        Context context = getActivity();
+        final Context context = getActivity();
         IntentFilter filter = new IntentFilter();
         filter.addAction(DisplayManager.ACTION_WIFI_DISPLAY_STATUS_CHANGED);
         context.registerReceiver(mReceiver, filter);
@@ -163,136 +160,156 @@
         getContentResolver().registerContentObserver(Settings.Global.getUriFor(
                 Settings.Global.WIFI_DISPLAY_CERTIFICATION_ON), false, mSettingsObserver);
         getContentResolver().registerContentObserver(Settings.Global.getUriFor(
-            Settings.Global.WIFI_DISPLAY_WPS_CONFIG), false, mSettingsObserver);
+                Settings.Global.WIFI_DISPLAY_WPS_CONFIG), false, mSettingsObserver);
 
-        mDisplayManager.scanWifiDisplays();
+        mRouter.addCallback(MediaRouter.ROUTE_TYPE_REMOTE_DISPLAY, mRouterCallback,
+                MediaRouter.CALLBACK_FLAG_PERFORM_ACTIVE_SCAN);
 
-        update();
+        update(CHANGE_ALL);
     }
 
     @Override
-    public void onPause() {
-        super.onPause();
+    public void onStop() {
+        super.onStop();
+        mStarted = false;
 
-        Context context = getActivity();
+        final Context context = getActivity();
         context.unregisterReceiver(mReceiver);
 
         getContentResolver().unregisterContentObserver(mSettingsObserver);
+
+        mRouter.removeCallback(mRouterCallback);
+
+        unscheduleUpdate();
     }
 
     @Override
     public void onCreateOptionsMenu(Menu menu, MenuInflater inflater) {
-        MenuItem item = menu.add(Menu.NONE, MENU_ID_SCAN, 0,
-                mWifiDisplayStatus.getScanState() == WifiDisplayStatus.SCAN_STATE_SCANNING ?
-                        R.string.wifi_display_searching_for_devices :
-                                R.string.wifi_display_search_for_devices);
-        item.setEnabled(mWifiDisplayStatus.getFeatureState() == WifiDisplayStatus.FEATURE_STATE_ON
-                && mWifiDisplayStatus.getScanState() == WifiDisplayStatus.SCAN_STATE_NOT_SCANNING);
-        item.setShowAsAction(MenuItem.SHOW_AS_ACTION_IF_ROOM);
+        if (mWifiDisplayStatus != null && mWifiDisplayStatus.getFeatureState()
+                != WifiDisplayStatus.FEATURE_STATE_UNAVAILABLE) {
+            MenuItem item = menu.add(Menu.NONE, MENU_ID_ENABLE_WIFI_DISPLAY, 0,
+                    R.string.wifi_display_enable_menu_item);
+            item.setCheckable(true);
+            item.setChecked(mWifiDisplayOnSetting);
+        }
         super.onCreateOptionsMenu(menu, inflater);
     }
 
     @Override
     public boolean onOptionsItemSelected(MenuItem item) {
         switch (item.getItemId()) {
-            case MENU_ID_SCAN:
-                if (mWifiDisplayStatus.getFeatureState() == WifiDisplayStatus.FEATURE_STATE_ON) {
-                    mDisplayManager.scanWifiDisplays();
-                }
+            case MENU_ID_ENABLE_WIFI_DISPLAY:
+                mWifiDisplayOnSetting = !item.isChecked();
+                item.setChecked(mWifiDisplayOnSetting);
+                Settings.Global.putInt(getContentResolver(),
+                        Settings.Global.WIFI_DISPLAY_ON, mWifiDisplayOnSetting ? 1 : 0);
                 return true;
         }
         return super.onOptionsItemSelected(item);
     }
 
-    @Override
-    public boolean onPreferenceTreeClick(PreferenceScreen preferenceScreen,
-            Preference preference) {
-        if (preference instanceof WifiDisplayPreference) {
-            WifiDisplayPreference p = (WifiDisplayPreference)preference;
-            WifiDisplay display = p.getDisplay();
-
-            if (display.equals(mWifiDisplayStatus.getActiveDisplay())) {
-                showDisconnectDialog(display);
-            } else if (display.canConnect()){
-                mDisplayManager.connectWifiDisplay(display.getDeviceAddress());
+    private void scheduleUpdate(int changes) {
+        if (mStarted) {
+            if (mPendingChanges == 0) {
+                mHandler.post(mUpdateRunnable);
             }
+            mPendingChanges |= changes;
+        }
+    }
+
+    private void unscheduleUpdate() {
+        if (mPendingChanges != 0) {
+            mPendingChanges = 0;
+            mHandler.removeCallbacks(mUpdateRunnable);
+        }
+    }
+
+    private void update(int changes) {
+        boolean invalidateOptions = false;
+
+        // Update settings.
+        if ((changes & CHANGE_SETTINGS) != 0) {
+            mWifiDisplayOnSetting = Settings.Global.getInt(getContentResolver(),
+                    Settings.Global.WIFI_DISPLAY_ON, 0) != 0;
+            mWifiDisplayCertificationOn = Settings.Global.getInt(getContentResolver(),
+                    Settings.Global.WIFI_DISPLAY_CERTIFICATION_ON, 0) != 0;
+            mWpsConfig = Settings.Global.getInt(getContentResolver(),
+                Settings.Global.WIFI_DISPLAY_WPS_CONFIG, WpsInfo.INVALID);
+
+            // The wifi display enabled setting may have changed.
+            invalidateOptions = true;
         }
 
-        return super.onPreferenceTreeClick(preferenceScreen, preference);
-    }
+        // Update wifi display state.
+        if ((changes & CHANGE_WIFI_DISPLAY_STATUS) != 0) {
+            mWifiDisplayStatus = mDisplayManager.getWifiDisplayStatus();
 
-    private void update() {
-        mWifiDisplayOnSetting = Settings.Global.getInt(getContentResolver(),
-                Settings.Global.WIFI_DISPLAY_ON, 0) != 0;
-        mWifiDisplayCertificationOn = Settings.Global.getInt(getContentResolver(),
-                Settings.Global.WIFI_DISPLAY_CERTIFICATION_ON, 0) != 0;
-        mWpsConfig = Settings.Global.getInt(getContentResolver(),
-            Settings.Global.WIFI_DISPLAY_WPS_CONFIG, WpsInfo.INVALID);
-        mWifiDisplayStatus = mDisplayManager.getWifiDisplayStatus();
+            // The wifi display feature state may have changed.
+            invalidateOptions = true;
+        }
 
-        applyState();
-    }
-
-    private void applyState() {
-        final int featureState = mWifiDisplayStatus.getFeatureState();
-        mActionBarSwitch.setEnabled(featureState != WifiDisplayStatus.FEATURE_STATE_DISABLED);
-        mActionBarSwitch.setChecked(mWifiDisplayOnSetting);
-
+        // Rebuild the routes.
         final PreferenceScreen preferenceScreen = getPreferenceScreen();
         preferenceScreen.removeAll();
 
-        if (featureState == WifiDisplayStatus.FEATURE_STATE_ON) {
-            final WifiDisplay[] displays = mWifiDisplayStatus.getDisplays();
+        // Add all known remote display routes.
+        final int routeCount = mRouter.getRouteCount();
+        for (int i = 0; i < routeCount; i++) {
+            MediaRouter.RouteInfo route = mRouter.getRouteAt(i);
+            if (route.matchesTypes(MediaRouter.ROUTE_TYPE_REMOTE_DISPLAY)) {
+                preferenceScreen.addPreference(createRoutePreference(route));
+            }
+        }
 
+        // Additional features for wifi display routes.
+        if (mWifiDisplayStatus != null
+                && mWifiDisplayStatus.getFeatureState() == WifiDisplayStatus.FEATURE_STATE_ON) {
+            // Add all unpaired wifi displays.
+            for (WifiDisplay display : mWifiDisplayStatus.getDisplays()) {
+                if (!display.isRemembered() && display.isAvailable()
+                        && !display.equals(mWifiDisplayStatus.getActiveDisplay())) {
+                    preferenceScreen.addPreference(new UnpairedWifiDisplayPreference(
+                            getActivity(), display));
+                }
+            }
+
+            // Add the certification menu if enabled in developer options.
             if (mWifiDisplayCertificationOn) {
                 buildCertificationMenu(preferenceScreen);
             }
-
-            if (mPairedDevicesCategory == null) {
-                mPairedDevicesCategory = new PreferenceCategory(getActivity());
-                mPairedDevicesCategory.setTitle(R.string.wifi_display_paired_devices);
-            } else {
-                mPairedDevicesCategory.removeAll();
-            }
-            preferenceScreen.addPreference(mPairedDevicesCategory);
-
-            if (mAvailableDevicesCategory == null) {
-                mAvailableDevicesCategory = new ProgressCategory(getActivity(), null,
-                        R.string.wifi_display_no_devices_found);
-                mAvailableDevicesCategory.setTitle(R.string.wifi_display_available_devices);
-            } else {
-                mAvailableDevicesCategory.removeAll();
-            }
-            preferenceScreen.addPreference(mAvailableDevicesCategory);
-
-            for (WifiDisplay d : displays) {
-                if (d.isRemembered()) {
-                    mPairedDevicesCategory.addPreference(createWifiDisplayPreference(d));
-                } else if (d.isAvailable()){
-                    mAvailableDevicesCategory.addPreference(createWifiDisplayPreference(d));
-                }
-            }
-            if (mPairedDevicesCategory.getPreferenceCount() == 0) {
-                preferenceScreen.removePreference(mPairedDevicesCategory);
-            }
-            if (mWifiDisplayStatus.getScanState() == WifiDisplayStatus.SCAN_STATE_SCANNING) {
-                mAvailableDevicesCategory.setProgress(true);
-            } else {
-                mAvailableDevicesCategory.setProgress(false);
-            }
-        } else {
-            mEmptyView.setText(featureState == WifiDisplayStatus.FEATURE_STATE_OFF ?
-                    R.string.wifi_display_settings_empty_list_wifi_display_off :
-                            R.string.wifi_display_settings_empty_list_wifi_display_disabled);
         }
 
-        getActivity().invalidateOptionsMenu();
+        // Invalidate menu options if needed.
+        if (invalidateOptions) {
+            getActivity().invalidateOptionsMenu();
+        }
+    }
+
+    private RoutePreference createRoutePreference(MediaRouter.RouteInfo route) {
+        WifiDisplay display = findWifiDisplay(route.getDeviceAddress());
+        if (display != null) {
+            return new WifiDisplayRoutePreference(getActivity(), route, display);
+        } else {
+            return new RoutePreference(getActivity(), route);
+        }
+    }
+
+    private WifiDisplay findWifiDisplay(String deviceAddress) {
+        if (mWifiDisplayStatus != null && deviceAddress != null) {
+            for (WifiDisplay display : mWifiDisplayStatus.getDisplays()) {
+                if (display.getDeviceAddress().equals(deviceAddress)) {
+                    return display;
+                }
+            }
+        }
+        return null;
     }
 
     private void buildCertificationMenu(final PreferenceScreen preferenceScreen) {
         if (mCertCategory == null) {
             mCertCategory = new PreferenceCategory(getActivity());
             mCertCategory.setTitle(R.string.wifi_display_certification_heading);
+            mCertCategory.setOrder(ORDER_CERTIFICATION);
         } else {
             mCertCategory.removeAll();
         }
@@ -526,53 +543,22 @@
         });
     }
 
-    private Preference createWifiDisplayPreference(final WifiDisplay d) {
-        WifiDisplayPreference p = new WifiDisplayPreference(getActivity(), d);
-        if (d.equals(mWifiDisplayStatus.getActiveDisplay())) {
-            switch (mWifiDisplayStatus.getActiveDisplayState()) {
-                case WifiDisplayStatus.DISPLAY_STATE_CONNECTED:
-                    p.setSummary(R.string.wifi_display_status_connected);
-                    break;
-                case WifiDisplayStatus.DISPLAY_STATE_CONNECTING:
-                    p.setSummary(R.string.wifi_display_status_connecting);
-                    break;
-            }
-        } else if (d.isAvailable()) {
-            if (!d.canConnect()) {
-                p.setSummary(R.string.wifi_display_status_in_use);
-                p.setEnabled(false);
-            } else if (d.isRemembered()) {
-                p.setSummary(R.string.wifi_display_status_available);
-            }
+    private void toggleRoute(MediaRouter.RouteInfo route) {
+        if (route.isSelected()) {
+            MediaRouteDialogPresenter.showDialogFragment(getActivity(),
+                    MediaRouter.ROUTE_TYPE_REMOTE_DISPLAY, null);
+        } else {
+            route.select();
         }
-        if (d.isRemembered()) {
-            p.setWidgetLayoutResource(R.layout.wifi_display_preference);
-        }
-        return p;
     }
 
-    private void showDisconnectDialog(final WifiDisplay display) {
-        DialogInterface.OnClickListener ok = new DialogInterface.OnClickListener() {
-            @Override
-            public void onClick(DialogInterface dialog, int which) {
-                if (display.equals(mWifiDisplayStatus.getActiveDisplay())) {
-                    mDisplayManager.disconnectWifiDisplay();
-                }
-            }
-        };
-
-        AlertDialog dialog = new AlertDialog.Builder(getActivity())
-                .setCancelable(true)
-                .setTitle(R.string.wifi_display_disconnect_title)
-                .setMessage(Html.fromHtml(getResources().getString(
-                        R.string.wifi_display_disconnect_text, display.getFriendlyDisplayName())))
-                .setPositiveButton(android.R.string.ok, ok)
-                .setNegativeButton(android.R.string.cancel, null)
-                .create();
-        dialog.show();
+    private void pairWifiDisplay(WifiDisplay display) {
+        if (display.canConnect()) {
+            mDisplayManager.connectWifiDisplay(display.getDeviceAddress());
+        }
     }
 
-    private void showOptionsDialog(final WifiDisplay display) {
+    private void showWifiDisplayOptionsDialog(final WifiDisplay display) {
         View view = getActivity().getLayoutInflater().inflate(R.layout.wifi_display_options, null);
         final EditText nameEditText = (EditText)view.findViewById(R.id.name);
         nameEditText.setText(display.getFriendlyDisplayName());
@@ -604,22 +590,12 @@
         dialog.show();
     }
 
-    private static boolean contains(WifiDisplay[] displays, String address) {
-        for (WifiDisplay d : displays) {
-            if (d.getDeviceAddress().equals(address)) {
-                return true;
-            }
-        }
-        return false;
-    }
-
-    private final CompoundButton.OnCheckedChangeListener mSwitchOnCheckedChangedListener =
-            new CompoundButton.OnCheckedChangeListener() {
+    private final Runnable mUpdateRunnable = new Runnable() {
         @Override
-        public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) {
-            mWifiDisplayOnSetting = isChecked;
-            Settings.Global.putInt(getContentResolver(),
-                    Settings.Global.WIFI_DISPLAY_ON, isChecked ? 1 : 0);
+        public void run() {
+            final int changes = mPendingChanges;
+            mPendingChanges = 0;
+            update(changes);
         }
     };
 
@@ -628,10 +604,7 @@
         public void onReceive(Context context, Intent intent) {
             String action = intent.getAction();
             if (action.equals(DisplayManager.ACTION_WIFI_DISPLAY_STATUS_CHANGED)) {
-                WifiDisplayStatus status = (WifiDisplayStatus)intent.getParcelableExtra(
-                        DisplayManager.EXTRA_WIFI_DISPLAY_STATUS);
-                mWifiDisplayStatus = status;
-                applyState();
+                scheduleUpdate(CHANGE_WIFI_DISPLAY_STATUS);
             }
         }
     };
@@ -639,23 +612,87 @@
     private final ContentObserver mSettingsObserver = new ContentObserver(new Handler()) {
         @Override
         public void onChange(boolean selfChange, Uri uri) {
-            update();
+            scheduleUpdate(CHANGE_SETTINGS);
         }
     };
 
-    private final class WifiDisplayPreference extends Preference
+    private final MediaRouter.Callback mRouterCallback = new MediaRouter.SimpleCallback() {
+        @Override
+        public void onRouteAdded(MediaRouter router, RouteInfo info) {
+            scheduleUpdate(CHANGE_ROUTES);
+        }
+
+        @Override
+        public void onRouteChanged(MediaRouter router, RouteInfo info) {
+            scheduleUpdate(CHANGE_ROUTES);
+        }
+
+        @Override
+        public void onRouteRemoved(MediaRouter router, RouteInfo info) {
+            scheduleUpdate(CHANGE_ROUTES);
+        }
+
+        @Override
+        public void onRouteSelected(MediaRouter router, int type, RouteInfo info) {
+            scheduleUpdate(CHANGE_ROUTES);
+        }
+
+        @Override
+        public void onRouteUnselected(MediaRouter router, int type, RouteInfo info) {
+            scheduleUpdate(CHANGE_ROUTES);
+        }
+    };
+
+    private class RoutePreference extends Preference
+            implements Preference.OnPreferenceClickListener {
+        private final MediaRouter.RouteInfo mRoute;
+
+        public RoutePreference(Context context, MediaRouter.RouteInfo route) {
+            super(context);
+
+            mRoute = route;
+            setTitle(route.getName());
+            setSummary(route.getDescription());
+            setEnabled(route.isEnabled());
+            if (route.isSelected()) {
+                setOrder(ORDER_CONNECTED);
+                if (route.isConnecting()) {
+                    setSummary(R.string.wifi_display_status_connecting);
+                } else {
+                    setSummary(R.string.wifi_display_status_connected);
+                }
+            } else {
+                if (isEnabled()) {
+                    setOrder(ORDER_AVAILABLE);
+                } else {
+                    setOrder(ORDER_UNAVAILABLE);
+                    if (route.getStatusCode() == MediaRouter.RouteInfo.STATUS_IN_USE) {
+                        setSummary(R.string.wifi_display_status_in_use);
+                    } else {
+                        setSummary(R.string.wifi_display_status_not_available);
+                    }
+                }
+            }
+            setOnPreferenceClickListener(this);
+        }
+
+        @Override
+        public boolean onPreferenceClick(Preference preference) {
+            toggleRoute(mRoute);
+            return true;
+        }
+    }
+
+    private class WifiDisplayRoutePreference extends RoutePreference
             implements View.OnClickListener {
         private final WifiDisplay mDisplay;
 
-        public WifiDisplayPreference(Context context, WifiDisplay display) {
-            super(context);
+        public WifiDisplayRoutePreference(Context context, MediaRouter.RouteInfo route,
+                WifiDisplay display) {
+            super(context, route);
 
             mDisplay = display;
-            setTitle(display.getFriendlyDisplayName());
-        }
-
-        public WifiDisplay getDisplay() {
-            return mDisplay;
+            setWidgetLayoutResource(R.layout.wifi_display_preference);
         }
 
         @Override
@@ -665,19 +702,46 @@
             ImageView deviceDetails = (ImageView) view.findViewById(R.id.deviceDetails);
             if (deviceDetails != null) {
                 deviceDetails.setOnClickListener(this);
-
                 if (!isEnabled()) {
                     TypedValue value = new TypedValue();
                     getContext().getTheme().resolveAttribute(android.R.attr.disabledAlpha,
                             value, true);
                     deviceDetails.setImageAlpha((int)(value.getFloat() * 255));
+                    deviceDetails.setEnabled(true); // always allow button to be pressed
                 }
             }
         }
 
         @Override
         public void onClick(View v) {
-            showOptionsDialog(mDisplay);
+            showWifiDisplayOptionsDialog(mDisplay);
+        }
+    }
+
+    private class UnpairedWifiDisplayPreference extends Preference
+            implements Preference.OnPreferenceClickListener {
+        private final WifiDisplay mDisplay;
+
+        public UnpairedWifiDisplayPreference(Context context, WifiDisplay display) {
+            super(context);
+
+            mDisplay = display;
+            setTitle(display.getFriendlyDisplayName());
+            setSummary(com.android.internal.R.string.wireless_display_route_description);
+            setEnabled(display.canConnect());
+            if (isEnabled()) {
+                setOrder(ORDER_AVAILABLE);
+            } else {
+                setOrder(ORDER_UNAVAILABLE);
+                setSummary(R.string.wifi_display_status_in_use);
+            }
+            setOnPreferenceClickListener(this);
+        }
+
+        @Override
+        public boolean onPreferenceClick(Preference preference) {
+            pairWifiDisplay(mDisplay);
+            return true;
         }
     }
 }