WifiDisplaySettings.java revision 6c607384c478958260952b82932b9084a4536fc6
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
17package com.android.settings.wfd;
18
19import android.app.ActionBar;
20import android.app.Activity;
21import android.app.AlertDialog;
22import android.app.Service;
23import android.content.BroadcastReceiver;
24import android.content.Context;
25import android.content.DialogInterface;
26import android.content.Intent;
27import android.content.IntentFilter;
28import android.database.ContentObserver;
29import android.hardware.display.DisplayManager;
30import android.hardware.display.WifiDisplay;
31import android.hardware.display.WifiDisplayStatus;
32import android.net.Uri;
33import android.net.wifi.p2p.WifiP2pManager;
34import android.net.wifi.p2p.WifiP2pManager.ActionListener;
35import android.net.wifi.p2p.WifiP2pManager.Channel;
36import android.os.Bundle;
37import android.os.Handler;
38import android.os.Looper;
39import android.preference.CheckBoxPreference;
40import android.preference.ListPreference;
41import android.preference.Preference;
42import android.preference.PreferenceActivity;
43import android.preference.PreferenceCategory;
44import android.preference.PreferenceGroup;
45import android.preference.PreferenceScreen;
46import android.provider.Settings;
47import android.text.Html;
48import android.util.Slog;
49import android.util.TypedValue;
50import android.view.Gravity;
51import android.view.LayoutInflater;
52import android.view.Menu;
53import android.view.MenuInflater;
54import android.view.MenuItem;
55import android.view.View;
56import android.view.View.OnClickListener;
57import android.view.ViewGroup;
58import android.widget.Button;
59import android.widget.CompoundButton;
60import android.widget.EditText;
61import android.widget.ImageView;
62import android.widget.Switch;
63import android.widget.TextView;
64
65import com.android.settings.ProgressCategory;
66import com.android.settings.R;
67import com.android.settings.SettingsPreferenceFragment;
68
69/**
70 * The Settings screen for WifiDisplay configuration and connection management.
71 */
72public final class WifiDisplaySettings extends SettingsPreferenceFragment {
73    private static final String TAG = "WifiDisplaySettings";
74    private static final boolean DEBUG = false;
75
76    private static final int MENU_ID_SCAN = Menu.FIRST;
77
78    private DisplayManager mDisplayManager;
79
80    private boolean mWifiDisplayOnSetting;
81    private WifiDisplayStatus mWifiDisplayStatus;
82
83    private PreferenceGroup mPairedDevicesCategory;
84    private ProgressCategory mAvailableDevicesCategory;
85
86    private TextView mEmptyView;
87
88    private Switch mActionBarSwitch;
89
90    /* certification */
91    private boolean mWifiDisplayCertificationOn;
92    private WifiP2pManager mWifiP2pManager;
93    private Channel mWifiP2pChannel;
94    private PreferenceGroup mCertCategory;
95    private boolean mListen;
96    private boolean mAutoGO;
97    private int mListenChannel;
98    private int mOperatingChannel;
99
100    public WifiDisplaySettings() {
101    }
102
103    @Override
104    public void onCreate(Bundle icicle) {
105        super.onCreate(icicle);
106
107        mDisplayManager = (DisplayManager)getActivity().getSystemService(Context.DISPLAY_SERVICE);
108        mWifiP2pManager = (WifiP2pManager)getActivity().getSystemService(Context.WIFI_P2P_SERVICE);
109        mWifiP2pChannel = mWifiP2pManager.initialize(getActivity(), Looper.getMainLooper(), null);
110
111        addPreferencesFromResource(R.xml.wifi_display_settings);
112        setHasOptionsMenu(true);
113    }
114
115    @Override
116    public void onActivityCreated(Bundle savedInstanceState) {
117        super.onActivityCreated(savedInstanceState);
118
119        Activity activity = getActivity();
120        mActionBarSwitch = new Switch(activity);
121        mActionBarSwitch.setOnCheckedChangeListener(mSwitchOnCheckedChangedListener);
122
123        final int padding = activity.getResources().getDimensionPixelSize(
124                R.dimen.action_bar_switch_padding);
125        mActionBarSwitch.setPaddingRelative(0, 0, padding, 0);
126        activity.getActionBar().setDisplayOptions(ActionBar.DISPLAY_SHOW_CUSTOM,
127                ActionBar.DISPLAY_SHOW_CUSTOM);
128        activity.getActionBar().setCustomView(mActionBarSwitch,
129                new ActionBar.LayoutParams(
130                        ActionBar.LayoutParams.WRAP_CONTENT,
131                        ActionBar.LayoutParams.WRAP_CONTENT,
132                        Gravity.CENTER_VERTICAL | Gravity.END));
133
134        mEmptyView = (TextView) getView().findViewById(android.R.id.empty);
135        getListView().setEmptyView(mEmptyView);
136
137        update();
138
139        if (mWifiDisplayStatus.getFeatureState() == WifiDisplayStatus.FEATURE_STATE_UNAVAILABLE) {
140            activity.finish();
141        }
142    }
143
144    @Override
145    public void onDestroyView() {
146        getActivity().getActionBar().setCustomView(null);
147        super.onDestroyView();
148    }
149
150    @Override
151    public void onResume() {
152        super.onResume();
153
154        Context context = getActivity();
155        IntentFilter filter = new IntentFilter();
156        filter.addAction(DisplayManager.ACTION_WIFI_DISPLAY_STATUS_CHANGED);
157        context.registerReceiver(mReceiver, filter);
158
159        getContentResolver().registerContentObserver(Settings.Global.getUriFor(
160                Settings.Global.WIFI_DISPLAY_ON), false, mSettingsObserver);
161        getContentResolver().registerContentObserver(Settings.Global.getUriFor(
162                Settings.Global.WIFI_DISPLAY_CERTIFICATION_ON), false, mSettingsObserver);
163
164        mDisplayManager.scanWifiDisplays();
165
166        update();
167    }
168
169    @Override
170    public void onPause() {
171        super.onPause();
172
173        Context context = getActivity();
174        context.unregisterReceiver(mReceiver);
175
176        getContentResolver().unregisterContentObserver(mSettingsObserver);
177    }
178
179    @Override
180    public void onCreateOptionsMenu(Menu menu, MenuInflater inflater) {
181        MenuItem item = menu.add(Menu.NONE, MENU_ID_SCAN, 0,
182                mWifiDisplayStatus.getScanState() == WifiDisplayStatus.SCAN_STATE_SCANNING ?
183                        R.string.wifi_display_searching_for_devices :
184                                R.string.wifi_display_search_for_devices);
185        item.setEnabled(mWifiDisplayStatus.getFeatureState() == WifiDisplayStatus.FEATURE_STATE_ON
186                && mWifiDisplayStatus.getScanState() == WifiDisplayStatus.SCAN_STATE_NOT_SCANNING);
187        item.setShowAsAction(MenuItem.SHOW_AS_ACTION_IF_ROOM);
188        super.onCreateOptionsMenu(menu, inflater);
189    }
190
191    @Override
192    public boolean onOptionsItemSelected(MenuItem item) {
193        switch (item.getItemId()) {
194            case MENU_ID_SCAN:
195                if (mWifiDisplayStatus.getFeatureState() == WifiDisplayStatus.FEATURE_STATE_ON) {
196                    mDisplayManager.scanWifiDisplays();
197                }
198                return true;
199        }
200        return super.onOptionsItemSelected(item);
201    }
202
203    @Override
204    public boolean onPreferenceTreeClick(PreferenceScreen preferenceScreen,
205            Preference preference) {
206        if (preference instanceof WifiDisplayPreference) {
207            WifiDisplayPreference p = (WifiDisplayPreference)preference;
208            WifiDisplay display = p.getDisplay();
209
210            if (display.equals(mWifiDisplayStatus.getActiveDisplay())) {
211                showDisconnectDialog(display);
212            } else if (display.canConnect()){
213                mDisplayManager.connectWifiDisplay(display.getDeviceAddress());
214            }
215        }
216
217        return super.onPreferenceTreeClick(preferenceScreen, preference);
218    }
219
220    private void update() {
221        mWifiDisplayOnSetting = Settings.Global.getInt(getContentResolver(),
222                Settings.Global.WIFI_DISPLAY_ON, 0) != 0;
223        mWifiDisplayCertificationOn = Settings.Global.getInt(getContentResolver(),
224                Settings.Global.WIFI_DISPLAY_CERTIFICATION_ON, 0) != 0;
225        mWifiDisplayStatus = mDisplayManager.getWifiDisplayStatus();
226
227        applyState();
228    }
229
230    private void applyState() {
231        final int featureState = mWifiDisplayStatus.getFeatureState();
232        mActionBarSwitch.setEnabled(featureState != WifiDisplayStatus.FEATURE_STATE_DISABLED);
233        mActionBarSwitch.setChecked(mWifiDisplayOnSetting);
234
235        final PreferenceScreen preferenceScreen = getPreferenceScreen();
236        preferenceScreen.removeAll();
237
238        if (featureState == WifiDisplayStatus.FEATURE_STATE_ON) {
239            final WifiDisplay[] displays = mWifiDisplayStatus.getDisplays();
240
241            if (mWifiDisplayCertificationOn) {
242                buildCertificationMenu(preferenceScreen);
243            }
244
245            if (mPairedDevicesCategory == null) {
246                mPairedDevicesCategory = new PreferenceCategory(getActivity());
247                mPairedDevicesCategory.setTitle(R.string.wifi_display_paired_devices);
248            } else {
249                mPairedDevicesCategory.removeAll();
250            }
251            preferenceScreen.addPreference(mPairedDevicesCategory);
252
253            if (mAvailableDevicesCategory == null) {
254                mAvailableDevicesCategory = new ProgressCategory(getActivity(), null,
255                        R.string.wifi_display_no_devices_found);
256                mAvailableDevicesCategory.setTitle(R.string.wifi_display_available_devices);
257            } else {
258                mAvailableDevicesCategory.removeAll();
259            }
260            preferenceScreen.addPreference(mAvailableDevicesCategory);
261
262            for (WifiDisplay d : displays) {
263                if (d.isRemembered()) {
264                    mPairedDevicesCategory.addPreference(createWifiDisplayPreference(d));
265                } else if (d.isAvailable()){
266                    mAvailableDevicesCategory.addPreference(createWifiDisplayPreference(d));
267                }
268            }
269            if (mPairedDevicesCategory.getPreferenceCount() == 0) {
270                preferenceScreen.removePreference(mPairedDevicesCategory);
271            }
272            if (mWifiDisplayStatus.getScanState() == WifiDisplayStatus.SCAN_STATE_SCANNING) {
273                mAvailableDevicesCategory.setProgress(true);
274            } else {
275                mAvailableDevicesCategory.setProgress(false);
276            }
277        } else {
278            mEmptyView.setText(featureState == WifiDisplayStatus.FEATURE_STATE_OFF ?
279                    R.string.wifi_display_settings_empty_list_wifi_display_off :
280                            R.string.wifi_display_settings_empty_list_wifi_display_disabled);
281        }
282
283        getActivity().invalidateOptionsMenu();
284    }
285
286    private void buildCertificationMenu(final PreferenceScreen preferenceScreen) {
287        if (mCertCategory == null) {
288            mCertCategory = new PreferenceCategory(getActivity());
289            mCertCategory.setTitle(R.string.wifi_display_certification_heading);
290        } else {
291            mCertCategory.removeAll();
292        }
293        preferenceScreen.addPreference(mCertCategory);
294
295        // display session info if there is an active p2p session
296        if (!mWifiDisplayStatus.getSessionInfo().getGroupId().isEmpty()) {
297            Preference p = new Preference(getActivity());
298            p.setTitle(R.string.wifi_display_session_info);
299            p.setSummary(mWifiDisplayStatus.getSessionInfo().toString());
300            mCertCategory.addPreference(p);
301
302            // show buttons for Pause/Resume when a WFD session is established
303            if (mWifiDisplayStatus.getSessionInfo().getSessionId() != 0) {
304                mCertCategory.addPreference(new Preference(getActivity()) {
305                    @Override
306                    public View getView(View convertView, ViewGroup parent) {
307                        final View v;
308                        if (convertView == null) {
309                            LayoutInflater li = (LayoutInflater) getActivity().
310                                    getSystemService(Service.LAYOUT_INFLATER_SERVICE);
311                            v = li.inflate(R.layout.two_buttons_panel, null);
312                        } else {
313                            v = convertView;
314                        }
315
316                        Button b = (Button)v.findViewById(R.id.left_button);
317                        b.setText(R.string.wifi_display_pause);
318                        b.setOnClickListener(new OnClickListener() {
319                            @Override
320                            public void onClick(View v) {
321                                mDisplayManager.pauseWifiDisplay();
322                            }
323                        });
324
325                        b = (Button)v.findViewById(R.id.right_button);
326                        b.setText(R.string.wifi_display_resume);
327                        b.setOnClickListener(new OnClickListener() {
328                            @Override
329                            public void onClick(View v) {
330                                mDisplayManager.resumeWifiDisplay();
331                            }
332                        });
333
334                        return v;
335                    }
336                });
337            }
338        }
339
340        // switch for Listen Mode
341        CheckBoxPreference cbp = new CheckBoxPreference(getActivity()) {
342            @Override
343            protected void onClick() {
344                mListen = !mListen;
345                setListenMode(mListen);
346                setChecked(mListen);
347            }
348        };
349        cbp.setTitle(R.string.wifi_display_listen_mode);
350        cbp.setChecked(mListen);
351        mCertCategory.addPreference(cbp);
352
353        // switch for Autonomous GO
354        cbp = new CheckBoxPreference(getActivity()) {
355            @Override
356            protected void onClick() {
357                mAutoGO = !mAutoGO;
358                if (mAutoGO) {
359                    startAutoGO();
360                } else {
361                    stopAutoGO();
362                }
363                setChecked(mAutoGO);
364            }
365        };
366        cbp.setTitle(R.string.wifi_display_autonomous_go);
367        cbp.setChecked(mAutoGO);
368        mCertCategory.addPreference(cbp);
369
370        // Drop down list for choosing listen channel
371        ListPreference lp = new ListPreference(getActivity()) {
372            @Override
373            protected void onDialogClosed(boolean positiveResult) {
374                super.onDialogClosed(positiveResult);
375                if (positiveResult) {
376                    mListenChannel = Integer.parseInt(getValue());
377                    setSummary("%1$s");
378                    getActivity().invalidateOptionsMenu();
379                    setWifiP2pChannels(mListenChannel, mOperatingChannel);
380                }
381            }
382        };
383        String[] lcEntries = { "Auto", "1", "6", "11" };
384        String[] lcValues = { "0", "1", "6", "11" };
385        lp.setTitle(R.string.wifi_display_listen_channel);
386        lp.setEntries(lcEntries);
387        lp.setEntryValues(lcValues);
388        lp.setValue("" + mListenChannel);
389        lp.setSummary("%1$s");
390        mCertCategory.addPreference(lp);
391
392        // Drop down list for choosing operating channel
393        lp = new ListPreference(getActivity()) {
394            @Override
395            protected void onDialogClosed(boolean positiveResult) {
396                super.onDialogClosed(positiveResult);
397                if (positiveResult) {
398                    mOperatingChannel = Integer.parseInt(getValue());
399                    setSummary("%1$s");
400                    getActivity().invalidateOptionsMenu();
401                    setWifiP2pChannels(mListenChannel, mOperatingChannel);
402                }
403            }
404        };
405        String[] ocEntries = { "Auto", "1", "6", "11", "36" };
406        String[] ocValues = { "0", "1", "6", "11", "36" };
407        lp.setTitle(R.string.wifi_display_operating_channel);
408        lp.setEntries(ocEntries);
409        lp.setEntryValues(ocValues);
410        lp.setValue("" + mOperatingChannel);
411        lp.setSummary("%1$s");
412        mCertCategory.addPreference(lp);
413    }
414
415    private void startAutoGO() {
416        if (DEBUG) {
417            Slog.d(TAG, "Starting Autonomous GO...");
418        }
419        mWifiP2pManager.createGroup(mWifiP2pChannel, new ActionListener() {
420            @Override
421            public void onSuccess() {
422                if (DEBUG) {
423                    Slog.d(TAG, "Successfully started AutoGO.");
424                }
425            }
426
427            @Override
428            public void onFailure(int reason) {
429                Slog.e(TAG, "Failed to start AutoGO with reason " + reason + ".");
430            }
431        });
432    }
433
434    private void stopAutoGO() {
435        if (DEBUG) {
436            Slog.d(TAG, "Stopping Autonomous GO...");
437        }
438        mWifiP2pManager.removeGroup(mWifiP2pChannel, new ActionListener() {
439            @Override
440            public void onSuccess() {
441                if (DEBUG) {
442                    Slog.d(TAG, "Successfully stopped AutoGO.");
443                }
444            }
445
446            @Override
447            public void onFailure(int reason) {
448                Slog.e(TAG, "Failed to stop AutoGO with reason " + reason + ".");
449            }
450        });
451    }
452
453    private void setListenMode(final boolean enable) {
454        if (DEBUG) {
455            Slog.d(TAG, "Setting listen mode to: " + enable);
456        }
457        mWifiP2pManager.listen(mWifiP2pChannel, enable, new ActionListener() {
458            @Override
459            public void onSuccess() {
460                if (DEBUG) {
461                    Slog.d(TAG, "Successfully " + (enable ? "entered" : "exited")
462                            +" listen mode.");
463                }
464            }
465
466            @Override
467            public void onFailure(int reason) {
468                Slog.e(TAG, "Failed to " + (enable ? "entered" : "exited")
469                        +" listen mode with reason " + reason + ".");
470            }
471        });
472    }
473
474    private void setWifiP2pChannels(final int lc, final int oc) {
475        if (DEBUG) {
476            Slog.d(TAG, "Setting wifi p2p channel: lc=" + lc + ", oc=" + oc);
477        }
478        mWifiP2pManager.setWifiP2pChannels(mWifiP2pChannel,
479                lc, oc, new ActionListener() {
480            @Override
481            public void onSuccess() {
482                if (DEBUG) {
483                    Slog.d(TAG, "Successfully set wifi p2p channels.");
484                }
485            }
486
487            @Override
488            public void onFailure(int reason) {
489                Slog.e(TAG, "Failed to set wifi p2p channels with reason " + reason + ".");
490            }
491        });
492    }
493
494    private Preference createWifiDisplayPreference(final WifiDisplay d) {
495        WifiDisplayPreference p = new WifiDisplayPreference(getActivity(), d);
496        if (d.equals(mWifiDisplayStatus.getActiveDisplay())) {
497            switch (mWifiDisplayStatus.getActiveDisplayState()) {
498                case WifiDisplayStatus.DISPLAY_STATE_CONNECTED:
499                    p.setSummary(R.string.wifi_display_status_connected);
500                    break;
501                case WifiDisplayStatus.DISPLAY_STATE_CONNECTING:
502                    p.setSummary(R.string.wifi_display_status_connecting);
503                    break;
504            }
505        } else if (d.isAvailable()) {
506            if (!d.canConnect()) {
507                p.setSummary(R.string.wifi_display_status_in_use);
508                p.setEnabled(false);
509            } else if (d.isRemembered()) {
510                p.setSummary(R.string.wifi_display_status_available);
511            }
512        }
513        if (d.isRemembered()) {
514            p.setWidgetLayoutResource(R.layout.wifi_display_preference);
515        }
516        return p;
517    }
518
519    private void showDisconnectDialog(final WifiDisplay display) {
520        DialogInterface.OnClickListener ok = new DialogInterface.OnClickListener() {
521            @Override
522            public void onClick(DialogInterface dialog, int which) {
523                if (display.equals(mWifiDisplayStatus.getActiveDisplay())) {
524                    mDisplayManager.disconnectWifiDisplay();
525                }
526            }
527        };
528
529        AlertDialog dialog = new AlertDialog.Builder(getActivity())
530                .setCancelable(true)
531                .setTitle(R.string.wifi_display_disconnect_title)
532                .setMessage(Html.fromHtml(getResources().getString(
533                        R.string.wifi_display_disconnect_text, display.getFriendlyDisplayName())))
534                .setPositiveButton(android.R.string.ok, ok)
535                .setNegativeButton(android.R.string.cancel, null)
536                .create();
537        dialog.show();
538    }
539
540    private void showOptionsDialog(final WifiDisplay display) {
541        View view = getActivity().getLayoutInflater().inflate(R.layout.wifi_display_options, null);
542        final EditText nameEditText = (EditText)view.findViewById(R.id.name);
543        nameEditText.setText(display.getFriendlyDisplayName());
544
545        DialogInterface.OnClickListener done = new DialogInterface.OnClickListener() {
546            @Override
547            public void onClick(DialogInterface dialog, int which) {
548                String name = nameEditText.getText().toString().trim();
549                if (name.isEmpty() || name.equals(display.getDeviceName())) {
550                    name = null;
551                }
552                mDisplayManager.renameWifiDisplay(display.getDeviceAddress(), name);
553            }
554        };
555        DialogInterface.OnClickListener forget = new DialogInterface.OnClickListener() {
556            @Override
557            public void onClick(DialogInterface dialog, int which) {
558                mDisplayManager.forgetWifiDisplay(display.getDeviceAddress());
559            }
560        };
561
562        AlertDialog dialog = new AlertDialog.Builder(getActivity())
563                .setCancelable(true)
564                .setTitle(R.string.wifi_display_options_title)
565                .setView(view)
566                .setPositiveButton(R.string.wifi_display_options_done, done)
567                .setNegativeButton(R.string.wifi_display_options_forget, forget)
568                .create();
569        dialog.show();
570    }
571
572    private static boolean contains(WifiDisplay[] displays, String address) {
573        for (WifiDisplay d : displays) {
574            if (d.getDeviceAddress().equals(address)) {
575                return true;
576            }
577        }
578        return false;
579    }
580
581    private final CompoundButton.OnCheckedChangeListener mSwitchOnCheckedChangedListener =
582            new CompoundButton.OnCheckedChangeListener() {
583        @Override
584        public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) {
585            mWifiDisplayOnSetting = isChecked;
586            Settings.Global.putInt(getContentResolver(),
587                    Settings.Global.WIFI_DISPLAY_ON, isChecked ? 1 : 0);
588        }
589    };
590
591    private final BroadcastReceiver mReceiver = new BroadcastReceiver() {
592        @Override
593        public void onReceive(Context context, Intent intent) {
594            String action = intent.getAction();
595            if (action.equals(DisplayManager.ACTION_WIFI_DISPLAY_STATUS_CHANGED)) {
596                WifiDisplayStatus status = (WifiDisplayStatus)intent.getParcelableExtra(
597                        DisplayManager.EXTRA_WIFI_DISPLAY_STATUS);
598                mWifiDisplayStatus = status;
599                applyState();
600            }
601        }
602    };
603
604    private final ContentObserver mSettingsObserver = new ContentObserver(new Handler()) {
605        @Override
606        public void onChange(boolean selfChange, Uri uri) {
607            update();
608        }
609    };
610
611    private final class WifiDisplayPreference extends Preference
612            implements View.OnClickListener {
613        private final WifiDisplay mDisplay;
614
615        public WifiDisplayPreference(Context context, WifiDisplay display) {
616            super(context);
617
618            mDisplay = display;
619            setTitle(display.getFriendlyDisplayName());
620        }
621
622        public WifiDisplay getDisplay() {
623            return mDisplay;
624        }
625
626        @Override
627        protected void onBindView(View view) {
628            super.onBindView(view);
629
630            ImageView deviceDetails = (ImageView) view.findViewById(R.id.deviceDetails);
631            if (deviceDetails != null) {
632                deviceDetails.setOnClickListener(this);
633
634                if (!isEnabled()) {
635                    TypedValue value = new TypedValue();
636                    getContext().getTheme().resolveAttribute(android.R.attr.disabledAlpha,
637                            value, true);
638                    deviceDetails.setImageAlpha((int)(value.getFloat() * 255));
639                }
640            }
641        }
642
643        @Override
644        public void onClick(View v) {
645            showOptionsDialog(mDisplay);
646        }
647    }
648}
649