Home | History | Annotate | Download | only in wfd
      1 /*
      2  * Copyright (C) 2012 The Android Open Source Project
      3  *
      4  * Licensed under the Apache License, Version 2.0 (the "License");
      5  * you may not use this file except in compliance with the License.
      6  * You may obtain a copy of the License at
      7  *
      8  *      http://www.apache.org/licenses/LICENSE-2.0
      9  *
     10  * Unless required by applicable law or agreed to in writing, software
     11  * distributed under the License is distributed on an "AS IS" BASIS,
     12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
     13  * See the License for the specific language governing permissions and
     14  * limitations under the License.
     15  */
     16 
     17 package com.android.settings.wfd;
     18 
     19 import android.app.AlertDialog;
     20 import android.app.Service;
     21 import android.content.BroadcastReceiver;
     22 import android.content.Context;
     23 import android.content.DialogInterface;
     24 import android.content.Intent;
     25 import android.content.IntentFilter;
     26 import android.database.ContentObserver;
     27 import android.hardware.display.DisplayManager;
     28 import android.hardware.display.WifiDisplay;
     29 import android.hardware.display.WifiDisplayStatus;
     30 import android.media.MediaRouter;
     31 import android.media.MediaRouter.RouteInfo;
     32 import android.net.Uri;
     33 import android.net.wifi.p2p.WifiP2pManager;
     34 import android.net.wifi.p2p.WifiP2pManager.ActionListener;
     35 import android.net.wifi.p2p.WifiP2pManager.Channel;
     36 import android.net.wifi.WpsInfo;
     37 import android.os.Bundle;
     38 import android.os.Handler;
     39 import android.os.Looper;
     40 import android.preference.ListPreference;
     41 import android.preference.Preference;
     42 import android.preference.PreferenceCategory;
     43 import android.preference.PreferenceGroup;
     44 import android.preference.PreferenceScreen;
     45 import android.preference.SwitchPreference;
     46 import android.provider.Settings;
     47 import android.util.Slog;
     48 import android.util.TypedValue;
     49 import android.view.LayoutInflater;
     50 import android.view.Menu;
     51 import android.view.MenuInflater;
     52 import android.view.MenuItem;
     53 import android.view.View;
     54 import android.view.View.OnClickListener;
     55 import android.view.ViewGroup;
     56 import android.widget.Button;
     57 import android.widget.EditText;
     58 import android.widget.ImageView;
     59 import android.widget.TextView;
     60 
     61 import com.android.internal.app.MediaRouteDialogPresenter;
     62 import com.android.internal.logging.MetricsLogger;
     63 import com.android.settings.R;
     64 import com.android.settings.SettingsPreferenceFragment;
     65 
     66 /**
     67  * The Settings screen for WifiDisplay configuration and connection management.
     68  *
     69  * The wifi display routes are integrated together with other remote display routes
     70  * from the media router.  It may happen that wifi display isn't actually available
     71  * on the system.  In that case, the enable option will not be shown but other
     72  * remote display routes will continue to be made available.
     73  */
     74 public final class WifiDisplaySettings extends SettingsPreferenceFragment {
     75     private static final String TAG = "WifiDisplaySettings";
     76     private static final boolean DEBUG = false;
     77 
     78     private static final int MENU_ID_ENABLE_WIFI_DISPLAY = Menu.FIRST;
     79 
     80     private static final int CHANGE_SETTINGS = 1 << 0;
     81     private static final int CHANGE_ROUTES = 1 << 1;
     82     private static final int CHANGE_WIFI_DISPLAY_STATUS = 1 << 2;
     83     private static final int CHANGE_ALL = -1;
     84 
     85     private static final int ORDER_CERTIFICATION = 1;
     86     private static final int ORDER_CONNECTED = 2;
     87     private static final int ORDER_AVAILABLE = 3;
     88     private static final int ORDER_UNAVAILABLE = 4;
     89 
     90     private final Handler mHandler;
     91 
     92     private MediaRouter mRouter;
     93     private DisplayManager mDisplayManager;
     94 
     95     private boolean mStarted;
     96     private int mPendingChanges;
     97 
     98     private boolean mWifiDisplayOnSetting;
     99     private WifiDisplayStatus mWifiDisplayStatus;
    100 
    101     private TextView mEmptyView;
    102 
    103     /* certification */
    104     private boolean mWifiDisplayCertificationOn;
    105     private WifiP2pManager mWifiP2pManager;
    106     private Channel mWifiP2pChannel;
    107     private PreferenceGroup mCertCategory;
    108     private boolean mListen;
    109     private boolean mAutoGO;
    110     private int mWpsConfig = WpsInfo.INVALID;
    111     private int mListenChannel;
    112     private int mOperatingChannel;
    113 
    114     public WifiDisplaySettings() {
    115         mHandler = new Handler();
    116     }
    117 
    118     @Override
    119     protected int getMetricsCategory() {
    120         return MetricsLogger.WFD_WIFI_DISPLAY;
    121     }
    122 
    123     @Override
    124     public void onCreate(Bundle icicle) {
    125         super.onCreate(icicle);
    126 
    127         final Context context = getActivity();
    128         mRouter = (MediaRouter)context.getSystemService(Context.MEDIA_ROUTER_SERVICE);
    129         mDisplayManager = (DisplayManager)context.getSystemService(Context.DISPLAY_SERVICE);
    130         mWifiP2pManager = (WifiP2pManager)context.getSystemService(Context.WIFI_P2P_SERVICE);
    131         mWifiP2pChannel = mWifiP2pManager.initialize(context, Looper.getMainLooper(), null);
    132 
    133         addPreferencesFromResource(R.xml.wifi_display_settings);
    134         setHasOptionsMenu(true);
    135     }
    136 
    137     @Override
    138     protected int getHelpResource() {
    139         return R.string.help_url_remote_display;
    140     }
    141 
    142     @Override
    143     public void onActivityCreated(Bundle savedInstanceState) {
    144         super.onActivityCreated(savedInstanceState);
    145 
    146         mEmptyView = (TextView) getView().findViewById(android.R.id.empty);
    147         mEmptyView.setText(R.string.wifi_display_no_devices_found);
    148         getListView().setEmptyView(mEmptyView);
    149     }
    150 
    151     @Override
    152     public void onStart() {
    153         super.onStart();
    154         mStarted = true;
    155 
    156         final Context context = getActivity();
    157         IntentFilter filter = new IntentFilter();
    158         filter.addAction(DisplayManager.ACTION_WIFI_DISPLAY_STATUS_CHANGED);
    159         context.registerReceiver(mReceiver, filter);
    160 
    161         getContentResolver().registerContentObserver(Settings.Global.getUriFor(
    162                 Settings.Global.WIFI_DISPLAY_ON), false, mSettingsObserver);
    163         getContentResolver().registerContentObserver(Settings.Global.getUriFor(
    164                 Settings.Global.WIFI_DISPLAY_CERTIFICATION_ON), false, mSettingsObserver);
    165         getContentResolver().registerContentObserver(Settings.Global.getUriFor(
    166                 Settings.Global.WIFI_DISPLAY_WPS_CONFIG), false, mSettingsObserver);
    167 
    168         mRouter.addCallback(MediaRouter.ROUTE_TYPE_REMOTE_DISPLAY, mRouterCallback,
    169                 MediaRouter.CALLBACK_FLAG_PERFORM_ACTIVE_SCAN);
    170 
    171         update(CHANGE_ALL);
    172     }
    173 
    174     @Override
    175     public void onStop() {
    176         super.onStop();
    177         mStarted = false;
    178 
    179         final Context context = getActivity();
    180         context.unregisterReceiver(mReceiver);
    181 
    182         getContentResolver().unregisterContentObserver(mSettingsObserver);
    183 
    184         mRouter.removeCallback(mRouterCallback);
    185 
    186         unscheduleUpdate();
    187     }
    188 
    189     @Override
    190     public void onCreateOptionsMenu(Menu menu, MenuInflater inflater) {
    191         if (mWifiDisplayStatus != null && mWifiDisplayStatus.getFeatureState()
    192                 != WifiDisplayStatus.FEATURE_STATE_UNAVAILABLE) {
    193             MenuItem item = menu.add(Menu.NONE, MENU_ID_ENABLE_WIFI_DISPLAY, 0,
    194                     R.string.wifi_display_enable_menu_item);
    195             item.setCheckable(true);
    196             item.setChecked(mWifiDisplayOnSetting);
    197         }
    198         super.onCreateOptionsMenu(menu, inflater);
    199     }
    200 
    201     @Override
    202     public boolean onOptionsItemSelected(MenuItem item) {
    203         switch (item.getItemId()) {
    204             case MENU_ID_ENABLE_WIFI_DISPLAY:
    205                 mWifiDisplayOnSetting = !item.isChecked();
    206                 item.setChecked(mWifiDisplayOnSetting);
    207                 Settings.Global.putInt(getContentResolver(),
    208                         Settings.Global.WIFI_DISPLAY_ON, mWifiDisplayOnSetting ? 1 : 0);
    209                 return true;
    210         }
    211         return super.onOptionsItemSelected(item);
    212     }
    213 
    214     private void scheduleUpdate(int changes) {
    215         if (mStarted) {
    216             if (mPendingChanges == 0) {
    217                 mHandler.post(mUpdateRunnable);
    218             }
    219             mPendingChanges |= changes;
    220         }
    221     }
    222 
    223     private void unscheduleUpdate() {
    224         if (mPendingChanges != 0) {
    225             mPendingChanges = 0;
    226             mHandler.removeCallbacks(mUpdateRunnable);
    227         }
    228     }
    229 
    230     private void update(int changes) {
    231         boolean invalidateOptions = false;
    232 
    233         // Update settings.
    234         if ((changes & CHANGE_SETTINGS) != 0) {
    235             mWifiDisplayOnSetting = Settings.Global.getInt(getContentResolver(),
    236                     Settings.Global.WIFI_DISPLAY_ON, 0) != 0;
    237             mWifiDisplayCertificationOn = Settings.Global.getInt(getContentResolver(),
    238                     Settings.Global.WIFI_DISPLAY_CERTIFICATION_ON, 0) != 0;
    239             mWpsConfig = Settings.Global.getInt(getContentResolver(),
    240                 Settings.Global.WIFI_DISPLAY_WPS_CONFIG, WpsInfo.INVALID);
    241 
    242             // The wifi display enabled setting may have changed.
    243             invalidateOptions = true;
    244         }
    245 
    246         // Update wifi display state.
    247         if ((changes & CHANGE_WIFI_DISPLAY_STATUS) != 0) {
    248             mWifiDisplayStatus = mDisplayManager.getWifiDisplayStatus();
    249 
    250             // The wifi display feature state may have changed.
    251             invalidateOptions = true;
    252         }
    253 
    254         // Rebuild the routes.
    255         final PreferenceScreen preferenceScreen = getPreferenceScreen();
    256         preferenceScreen.removeAll();
    257 
    258         // Add all known remote display routes.
    259         final int routeCount = mRouter.getRouteCount();
    260         for (int i = 0; i < routeCount; i++) {
    261             MediaRouter.RouteInfo route = mRouter.getRouteAt(i);
    262             if (route.matchesTypes(MediaRouter.ROUTE_TYPE_REMOTE_DISPLAY)) {
    263                 preferenceScreen.addPreference(createRoutePreference(route));
    264             }
    265         }
    266 
    267         // Additional features for wifi display routes.
    268         if (mWifiDisplayStatus != null
    269                 && mWifiDisplayStatus.getFeatureState() == WifiDisplayStatus.FEATURE_STATE_ON) {
    270             // Add all unpaired wifi displays.
    271             for (WifiDisplay display : mWifiDisplayStatus.getDisplays()) {
    272                 if (!display.isRemembered() && display.isAvailable()
    273                         && !display.equals(mWifiDisplayStatus.getActiveDisplay())) {
    274                     preferenceScreen.addPreference(new UnpairedWifiDisplayPreference(
    275                             getActivity(), display));
    276                 }
    277             }
    278 
    279             // Add the certification menu if enabled in developer options.
    280             if (mWifiDisplayCertificationOn) {
    281                 buildCertificationMenu(preferenceScreen);
    282             }
    283         }
    284 
    285         // Invalidate menu options if needed.
    286         if (invalidateOptions) {
    287             getActivity().invalidateOptionsMenu();
    288         }
    289     }
    290 
    291     private RoutePreference createRoutePreference(MediaRouter.RouteInfo route) {
    292         WifiDisplay display = findWifiDisplay(route.getDeviceAddress());
    293         if (display != null) {
    294             return new WifiDisplayRoutePreference(getActivity(), route, display);
    295         } else {
    296             return new RoutePreference(getActivity(), route);
    297         }
    298     }
    299 
    300     private WifiDisplay findWifiDisplay(String deviceAddress) {
    301         if (mWifiDisplayStatus != null && deviceAddress != null) {
    302             for (WifiDisplay display : mWifiDisplayStatus.getDisplays()) {
    303                 if (display.getDeviceAddress().equals(deviceAddress)) {
    304                     return display;
    305                 }
    306             }
    307         }
    308         return null;
    309     }
    310 
    311     private void buildCertificationMenu(final PreferenceScreen preferenceScreen) {
    312         if (mCertCategory == null) {
    313             mCertCategory = new PreferenceCategory(getActivity());
    314             mCertCategory.setTitle(R.string.wifi_display_certification_heading);
    315             mCertCategory.setOrder(ORDER_CERTIFICATION);
    316         } else {
    317             mCertCategory.removeAll();
    318         }
    319         preferenceScreen.addPreference(mCertCategory);
    320 
    321         // display session info if there is an active p2p session
    322         if (!mWifiDisplayStatus.getSessionInfo().getGroupId().isEmpty()) {
    323             Preference p = new Preference(getActivity());
    324             p.setTitle(R.string.wifi_display_session_info);
    325             p.setSummary(mWifiDisplayStatus.getSessionInfo().toString());
    326             mCertCategory.addPreference(p);
    327 
    328             // show buttons for Pause/Resume when a WFD session is established
    329             if (mWifiDisplayStatus.getSessionInfo().getSessionId() != 0) {
    330                 mCertCategory.addPreference(new Preference(getActivity()) {
    331                     @Override
    332                     public View getView(View convertView, ViewGroup parent) {
    333                         final View v;
    334                         if (convertView == null) {
    335                             LayoutInflater li = (LayoutInflater) getActivity().
    336                                     getSystemService(Service.LAYOUT_INFLATER_SERVICE);
    337                             v = li.inflate(R.layout.two_buttons_panel, null);
    338                         } else {
    339                             v = convertView;
    340                         }
    341 
    342                         Button b = (Button)v.findViewById(R.id.left_button);
    343                         b.setText(R.string.wifi_display_pause);
    344                         b.setOnClickListener(new OnClickListener() {
    345                             @Override
    346                             public void onClick(View v) {
    347                                 mDisplayManager.pauseWifiDisplay();
    348                             }
    349                         });
    350 
    351                         b = (Button)v.findViewById(R.id.right_button);
    352                         b.setText(R.string.wifi_display_resume);
    353                         b.setOnClickListener(new OnClickListener() {
    354                             @Override
    355                             public void onClick(View v) {
    356                                 mDisplayManager.resumeWifiDisplay();
    357                             }
    358                         });
    359 
    360                         return v;
    361                     }
    362                 });
    363             }
    364         }
    365 
    366         // switch for Listen Mode
    367         SwitchPreference pref = new SwitchPreference(getActivity()) {
    368             @Override
    369             protected void onClick() {
    370                 mListen = !mListen;
    371                 setListenMode(mListen);
    372                 setChecked(mListen);
    373             }
    374         };
    375         pref.setTitle(R.string.wifi_display_listen_mode);
    376         pref.setChecked(mListen);
    377         mCertCategory.addPreference(pref);
    378 
    379         // switch for Autonomous GO
    380         pref = new SwitchPreference(getActivity()) {
    381             @Override
    382             protected void onClick() {
    383                 mAutoGO = !mAutoGO;
    384                 if (mAutoGO) {
    385                     startAutoGO();
    386                 } else {
    387                     stopAutoGO();
    388                 }
    389                 setChecked(mAutoGO);
    390             }
    391         };
    392         pref.setTitle(R.string.wifi_display_autonomous_go);
    393         pref.setChecked(mAutoGO);
    394         mCertCategory.addPreference(pref);
    395 
    396         // Drop down list for choosing WPS method (PBC/KEYPAD/DISPLAY)
    397         ListPreference lp = new ListPreference(getActivity()) {
    398             @Override
    399             protected void onDialogClosed(boolean positiveResult) {
    400                 super.onDialogClosed(positiveResult);
    401                 if (positiveResult) {
    402                     mWpsConfig = Integer.parseInt(getValue());
    403                     setSummary("%1$s");
    404                     getActivity().invalidateOptionsMenu();
    405                     Settings.Global.putInt(getActivity().getContentResolver(),
    406                             Settings.Global.WIFI_DISPLAY_WPS_CONFIG, mWpsConfig);
    407                 }
    408             }
    409         };
    410         mWpsConfig = Settings.Global.getInt(getActivity().getContentResolver(),
    411                 Settings.Global.WIFI_DISPLAY_WPS_CONFIG, WpsInfo.INVALID);
    412         String[] wpsEntries = { "Default", "PBC", "KEYPAD", "DISPLAY" };
    413         String[] wpsValues = {
    414             "" + WpsInfo.INVALID,
    415             "" + WpsInfo.PBC,
    416             "" + WpsInfo.KEYPAD,
    417             "" + WpsInfo.DISPLAY };
    418         lp.setTitle(R.string.wifi_display_wps_config);
    419         lp.setEntries(wpsEntries);
    420         lp.setEntryValues(wpsValues);
    421         lp.setValue("" + mWpsConfig);
    422         lp.setSummary("%1$s");
    423         mCertCategory.addPreference(lp);
    424 
    425         // Drop down list for choosing listen channel
    426         lp = new ListPreference(getActivity()) {
    427             @Override
    428             protected void onDialogClosed(boolean positiveResult) {
    429                 super.onDialogClosed(positiveResult);
    430                 if (positiveResult) {
    431                     mListenChannel = Integer.parseInt(getValue());
    432                     setSummary("%1$s");
    433                     getActivity().invalidateOptionsMenu();
    434                     setWifiP2pChannels(mListenChannel, mOperatingChannel);
    435                 }
    436             }
    437         };
    438         String[] lcEntries = { "Auto", "1", "6", "11" };
    439         String[] lcValues = { "0", "1", "6", "11" };
    440         lp.setTitle(R.string.wifi_display_listen_channel);
    441         lp.setEntries(lcEntries);
    442         lp.setEntryValues(lcValues);
    443         lp.setValue("" + mListenChannel);
    444         lp.setSummary("%1$s");
    445         mCertCategory.addPreference(lp);
    446 
    447         // Drop down list for choosing operating channel
    448         lp = new ListPreference(getActivity()) {
    449             @Override
    450             protected void onDialogClosed(boolean positiveResult) {
    451                 super.onDialogClosed(positiveResult);
    452                 if (positiveResult) {
    453                     mOperatingChannel = Integer.parseInt(getValue());
    454                     setSummary("%1$s");
    455                     getActivity().invalidateOptionsMenu();
    456                     setWifiP2pChannels(mListenChannel, mOperatingChannel);
    457                 }
    458             }
    459         };
    460         String[] ocEntries = { "Auto", "1", "6", "11", "36" };
    461         String[] ocValues = { "0", "1", "6", "11", "36" };
    462         lp.setTitle(R.string.wifi_display_operating_channel);
    463         lp.setEntries(ocEntries);
    464         lp.setEntryValues(ocValues);
    465         lp.setValue("" + mOperatingChannel);
    466         lp.setSummary("%1$s");
    467         mCertCategory.addPreference(lp);
    468     }
    469 
    470     private void startAutoGO() {
    471         if (DEBUG) {
    472             Slog.d(TAG, "Starting Autonomous GO...");
    473         }
    474         mWifiP2pManager.createGroup(mWifiP2pChannel, new ActionListener() {
    475             @Override
    476             public void onSuccess() {
    477                 if (DEBUG) {
    478                     Slog.d(TAG, "Successfully started AutoGO.");
    479                 }
    480             }
    481 
    482             @Override
    483             public void onFailure(int reason) {
    484                 Slog.e(TAG, "Failed to start AutoGO with reason " + reason + ".");
    485             }
    486         });
    487     }
    488 
    489     private void stopAutoGO() {
    490         if (DEBUG) {
    491             Slog.d(TAG, "Stopping Autonomous GO...");
    492         }
    493         mWifiP2pManager.removeGroup(mWifiP2pChannel, new ActionListener() {
    494             @Override
    495             public void onSuccess() {
    496                 if (DEBUG) {
    497                     Slog.d(TAG, "Successfully stopped AutoGO.");
    498                 }
    499             }
    500 
    501             @Override
    502             public void onFailure(int reason) {
    503                 Slog.e(TAG, "Failed to stop AutoGO with reason " + reason + ".");
    504             }
    505         });
    506     }
    507 
    508     private void setListenMode(final boolean enable) {
    509         if (DEBUG) {
    510             Slog.d(TAG, "Setting listen mode to: " + enable);
    511         }
    512         mWifiP2pManager.listen(mWifiP2pChannel, enable, new ActionListener() {
    513             @Override
    514             public void onSuccess() {
    515                 if (DEBUG) {
    516                     Slog.d(TAG, "Successfully " + (enable ? "entered" : "exited")
    517                             +" listen mode.");
    518                 }
    519             }
    520 
    521             @Override
    522             public void onFailure(int reason) {
    523                 Slog.e(TAG, "Failed to " + (enable ? "entered" : "exited")
    524                         +" listen mode with reason " + reason + ".");
    525             }
    526         });
    527     }
    528 
    529     private void setWifiP2pChannels(final int lc, final int oc) {
    530         if (DEBUG) {
    531             Slog.d(TAG, "Setting wifi p2p channel: lc=" + lc + ", oc=" + oc);
    532         }
    533         mWifiP2pManager.setWifiP2pChannels(mWifiP2pChannel,
    534                 lc, oc, new ActionListener() {
    535             @Override
    536             public void onSuccess() {
    537                 if (DEBUG) {
    538                     Slog.d(TAG, "Successfully set wifi p2p channels.");
    539                 }
    540             }
    541 
    542             @Override
    543             public void onFailure(int reason) {
    544                 Slog.e(TAG, "Failed to set wifi p2p channels with reason " + reason + ".");
    545             }
    546         });
    547     }
    548 
    549     private void toggleRoute(MediaRouter.RouteInfo route) {
    550         if (route.isSelected()) {
    551             MediaRouteDialogPresenter.showDialogFragment(getActivity(),
    552                     MediaRouter.ROUTE_TYPE_REMOTE_DISPLAY, null);
    553         } else {
    554             route.select();
    555         }
    556     }
    557 
    558     private void pairWifiDisplay(WifiDisplay display) {
    559         if (display.canConnect()) {
    560             mDisplayManager.connectWifiDisplay(display.getDeviceAddress());
    561         }
    562     }
    563 
    564     private void showWifiDisplayOptionsDialog(final WifiDisplay display) {
    565         View view = getActivity().getLayoutInflater().inflate(R.layout.wifi_display_options, null);
    566         final EditText nameEditText = (EditText)view.findViewById(R.id.name);
    567         nameEditText.setText(display.getFriendlyDisplayName());
    568 
    569         DialogInterface.OnClickListener done = new DialogInterface.OnClickListener() {
    570             @Override
    571             public void onClick(DialogInterface dialog, int which) {
    572                 String name = nameEditText.getText().toString().trim();
    573                 if (name.isEmpty() || name.equals(display.getDeviceName())) {
    574                     name = null;
    575                 }
    576                 mDisplayManager.renameWifiDisplay(display.getDeviceAddress(), name);
    577             }
    578         };
    579         DialogInterface.OnClickListener forget = new DialogInterface.OnClickListener() {
    580             @Override
    581             public void onClick(DialogInterface dialog, int which) {
    582                 mDisplayManager.forgetWifiDisplay(display.getDeviceAddress());
    583             }
    584         };
    585 
    586         AlertDialog dialog = new AlertDialog.Builder(getActivity())
    587                 .setCancelable(true)
    588                 .setTitle(R.string.wifi_display_options_title)
    589                 .setView(view)
    590                 .setPositiveButton(R.string.wifi_display_options_done, done)
    591                 .setNegativeButton(R.string.wifi_display_options_forget, forget)
    592                 .create();
    593         dialog.show();
    594     }
    595 
    596     private final Runnable mUpdateRunnable = new Runnable() {
    597         @Override
    598         public void run() {
    599             final int changes = mPendingChanges;
    600             mPendingChanges = 0;
    601             update(changes);
    602         }
    603     };
    604 
    605     private final BroadcastReceiver mReceiver = new BroadcastReceiver() {
    606         @Override
    607         public void onReceive(Context context, Intent intent) {
    608             String action = intent.getAction();
    609             if (action.equals(DisplayManager.ACTION_WIFI_DISPLAY_STATUS_CHANGED)) {
    610                 scheduleUpdate(CHANGE_WIFI_DISPLAY_STATUS);
    611             }
    612         }
    613     };
    614 
    615     private final ContentObserver mSettingsObserver = new ContentObserver(new Handler()) {
    616         @Override
    617         public void onChange(boolean selfChange, Uri uri) {
    618             scheduleUpdate(CHANGE_SETTINGS);
    619         }
    620     };
    621 
    622     private final MediaRouter.Callback mRouterCallback = new MediaRouter.SimpleCallback() {
    623         @Override
    624         public void onRouteAdded(MediaRouter router, RouteInfo info) {
    625             scheduleUpdate(CHANGE_ROUTES);
    626         }
    627 
    628         @Override
    629         public void onRouteChanged(MediaRouter router, RouteInfo info) {
    630             scheduleUpdate(CHANGE_ROUTES);
    631         }
    632 
    633         @Override
    634         public void onRouteRemoved(MediaRouter router, RouteInfo info) {
    635             scheduleUpdate(CHANGE_ROUTES);
    636         }
    637 
    638         @Override
    639         public void onRouteSelected(MediaRouter router, int type, RouteInfo info) {
    640             scheduleUpdate(CHANGE_ROUTES);
    641         }
    642 
    643         @Override
    644         public void onRouteUnselected(MediaRouter router, int type, RouteInfo info) {
    645             scheduleUpdate(CHANGE_ROUTES);
    646         }
    647     };
    648 
    649     private class RoutePreference extends Preference
    650             implements Preference.OnPreferenceClickListener {
    651         private final MediaRouter.RouteInfo mRoute;
    652 
    653         public RoutePreference(Context context, MediaRouter.RouteInfo route) {
    654             super(context);
    655 
    656             mRoute = route;
    657             setTitle(route.getName());
    658             setSummary(route.getDescription());
    659             setEnabled(route.isEnabled());
    660             if (route.isSelected()) {
    661                 setOrder(ORDER_CONNECTED);
    662                 if (route.isConnecting()) {
    663                     setSummary(R.string.wifi_display_status_connecting);
    664                 } else {
    665                     setSummary(R.string.wifi_display_status_connected);
    666                 }
    667             } else {
    668                 if (isEnabled()) {
    669                     setOrder(ORDER_AVAILABLE);
    670                 } else {
    671                     setOrder(ORDER_UNAVAILABLE);
    672                     if (route.getStatusCode() == MediaRouter.RouteInfo.STATUS_IN_USE) {
    673                         setSummary(R.string.wifi_display_status_in_use);
    674                     } else {
    675                         setSummary(R.string.wifi_display_status_not_available);
    676                     }
    677                 }
    678             }
    679             setOnPreferenceClickListener(this);
    680         }
    681 
    682         @Override
    683         public boolean onPreferenceClick(Preference preference) {
    684             toggleRoute(mRoute);
    685             return true;
    686         }
    687     }
    688 
    689     private class WifiDisplayRoutePreference extends RoutePreference
    690             implements View.OnClickListener {
    691         private final WifiDisplay mDisplay;
    692 
    693         public WifiDisplayRoutePreference(Context context, MediaRouter.RouteInfo route,
    694                 WifiDisplay display) {
    695             super(context, route);
    696 
    697             mDisplay = display;
    698             setWidgetLayoutResource(R.layout.wifi_display_preference);
    699         }
    700 
    701         @Override
    702         protected void onBindView(View view) {
    703             super.onBindView(view);
    704 
    705             ImageView deviceDetails = (ImageView) view.findViewById(R.id.deviceDetails);
    706             if (deviceDetails != null) {
    707                 deviceDetails.setOnClickListener(this);
    708                 if (!isEnabled()) {
    709                     TypedValue value = new TypedValue();
    710                     getContext().getTheme().resolveAttribute(android.R.attr.disabledAlpha,
    711                             value, true);
    712                     deviceDetails.setImageAlpha((int)(value.getFloat() * 255));
    713                     deviceDetails.setEnabled(true); // always allow button to be pressed
    714                 }
    715             }
    716         }
    717 
    718         @Override
    719         public void onClick(View v) {
    720             showWifiDisplayOptionsDialog(mDisplay);
    721         }
    722     }
    723 
    724     private class UnpairedWifiDisplayPreference extends Preference
    725             implements Preference.OnPreferenceClickListener {
    726         private final WifiDisplay mDisplay;
    727 
    728         public UnpairedWifiDisplayPreference(Context context, WifiDisplay display) {
    729             super(context);
    730 
    731             mDisplay = display;
    732             setTitle(display.getFriendlyDisplayName());
    733             setSummary(com.android.internal.R.string.wireless_display_route_description);
    734             setEnabled(display.canConnect());
    735             if (isEnabled()) {
    736                 setOrder(ORDER_AVAILABLE);
    737             } else {
    738                 setOrder(ORDER_UNAVAILABLE);
    739                 setSummary(R.string.wifi_display_status_in_use);
    740             }
    741             setOnPreferenceClickListener(this);
    742         }
    743 
    744         @Override
    745         public boolean onPreferenceClick(Preference preference) {
    746             pairWifiDisplay(mDisplay);
    747             return true;
    748         }
    749     }
    750 }
    751