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