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