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