OSDN Git Service

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