WifiP2pSettings.java revision 9f86b5df783ed063d1142f0050edbddf4fe3c7c2
1/*
2 * Copyright (C) 2011 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.wifi.p2p;
18
19import android.app.ActionBar;
20import android.app.Activity;
21import android.app.AlertDialog;
22import android.app.Dialog;
23import android.content.BroadcastReceiver;
24import android.content.Context;
25import android.content.DialogInterface;
26import android.content.DialogInterface.OnClickListener;
27import android.content.Intent;
28import android.content.IntentFilter;
29import android.net.NetworkInfo;
30import android.net.wifi.p2p.WifiP2pConfig;
31import android.net.wifi.p2p.WifiP2pDevice;
32import android.net.wifi.p2p.WifiP2pDeviceList;
33import android.net.wifi.p2p.WifiP2pGroup;
34import android.net.wifi.p2p.WifiP2pGroupList;
35import android.net.wifi.p2p.WifiP2pManager;
36import android.net.wifi.p2p.WifiP2pManager.GroupInfoListener;
37import android.net.wifi.p2p.WifiP2pManager.PersistentGroupInfoListener;
38import android.net.wifi.WpsInfo;
39import android.os.Bundle;
40import android.os.Handler;
41import android.os.SystemProperties;
42import android.preference.Preference;
43import android.preference.PreferenceActivity;
44import android.preference.PreferenceCategory;
45import android.preference.PreferenceGroup;
46import android.preference.PreferenceScreen;
47import android.text.TextUtils;
48import android.util.Log;
49import android.view.Gravity;
50import android.view.Menu;
51import android.view.MenuInflater;
52import android.view.MenuItem;
53import android.widget.EditText;
54import android.widget.Switch;
55import android.widget.Toast;
56
57import com.android.settings.R;
58import com.android.settings.SettingsPreferenceFragment;
59
60import java.util.Arrays;
61import java.util.List;
62import java.util.Collection;
63
64/*
65 * Displays Wi-fi p2p settings UI
66 */
67public class WifiP2pSettings extends SettingsPreferenceFragment
68        implements PersistentGroupInfoListener, GroupInfoListener {
69
70    private static final String TAG = "WifiP2pSettings";
71    private static final boolean DBG = false;
72    private static final int MENU_ID_SEARCH = Menu.FIRST;
73    private static final int MENU_ID_RENAME = Menu.FIRST + 1;
74
75    private final IntentFilter mIntentFilter = new IntentFilter();
76    private WifiP2pManager mWifiP2pManager;
77    private WifiP2pManager.Channel mChannel;
78    private OnClickListener mRenameListener;
79    private OnClickListener mDisconnectListener;
80    private OnClickListener mCancelConnectListener;
81    private OnClickListener mDeleteGroupListener;
82    private WifiP2pPeer mSelectedWifiPeer;
83    private WifiP2pPersistentGroup mSelectedGroup;
84    private EditText mDeviceNameText;
85
86    private boolean mWifiP2pEnabled;
87    private boolean mWifiP2pSearching;
88    private int mConnectedDevices;
89    private WifiP2pGroup mConnectedGroup;
90
91    private PreferenceGroup mPeersGroup;
92    private PreferenceGroup mPersistentGroup;
93    private Preference mThisDevicePref;
94
95    private static final int DIALOG_DISCONNECT  = 1;
96    private static final int DIALOG_CANCEL_CONNECT = 2;
97    private static final int DIALOG_RENAME = 3;
98    private static final int DIALOG_DELETE_GROUP = 4;
99
100    private static final String SAVE_DIALOG_PEER = "PEER_STATE";
101    private static final String SAVE_DEVICE_NAME = "DEV_NAME";
102
103    private WifiP2pDevice mThisDevice;
104    private WifiP2pDeviceList mPeers = new WifiP2pDeviceList();
105
106    private String mSavedDeviceName;
107
108    private final BroadcastReceiver mReceiver = new BroadcastReceiver() {
109        @Override
110        public void onReceive(Context context, Intent intent) {
111            String action = intent.getAction();
112
113            if (WifiP2pManager.WIFI_P2P_STATE_CHANGED_ACTION.equals(action)) {
114                mWifiP2pEnabled = intent.getIntExtra(WifiP2pManager.EXTRA_WIFI_STATE,
115                    WifiP2pManager.WIFI_P2P_STATE_DISABLED) == WifiP2pManager.WIFI_P2P_STATE_ENABLED;
116                handleP2pStateChanged();
117            } else if (WifiP2pManager.WIFI_P2P_PEERS_CHANGED_ACTION.equals(action)) {
118                mPeers = (WifiP2pDeviceList) intent.getParcelableExtra(
119                        WifiP2pManager.EXTRA_P2P_DEVICE_LIST);
120                handlePeersChanged();
121            } else if (WifiP2pManager.WIFI_P2P_CONNECTION_CHANGED_ACTION.equals(action)) {
122                if (mWifiP2pManager == null) return;
123                NetworkInfo networkInfo = (NetworkInfo) intent.getParcelableExtra(
124                        WifiP2pManager.EXTRA_NETWORK_INFO);
125                if (mWifiP2pManager != null) {
126                    mWifiP2pManager.requestGroupInfo(mChannel, WifiP2pSettings.this);
127                }
128                if (networkInfo.isConnected()) {
129                    if (DBG) Log.d(TAG, "Connected");
130                } else {
131                    //start a search when we are disconnected
132                    startSearch();
133                }
134            } else if (WifiP2pManager.WIFI_P2P_THIS_DEVICE_CHANGED_ACTION.equals(action)) {
135                mThisDevice = (WifiP2pDevice) intent.getParcelableExtra(
136                        WifiP2pManager.EXTRA_WIFI_P2P_DEVICE);
137                if (DBG) Log.d(TAG, "Update device info: " + mThisDevice);
138                updateDevicePref();
139            } else if (WifiP2pManager.WIFI_P2P_DISCOVERY_CHANGED_ACTION.equals(action)) {
140                int discoveryState = intent.getIntExtra(WifiP2pManager.EXTRA_DISCOVERY_STATE,
141                    WifiP2pManager.WIFI_P2P_DISCOVERY_STOPPED);
142                if (DBG) Log.d(TAG, "Discovery state changed: " + discoveryState);
143                if (discoveryState == WifiP2pManager.WIFI_P2P_DISCOVERY_STARTED) {
144                    updateSearchMenu(true);
145                } else {
146                    updateSearchMenu(false);
147                }
148            } else if (WifiP2pManager.WIFI_P2P_PERSISTENT_GROUPS_CHANGED_ACTION.equals(action)) {
149                if (mWifiP2pManager != null) {
150                    mWifiP2pManager.requestPersistentGroupInfo(mChannel, WifiP2pSettings.this);
151                }
152            }
153        }
154    };
155
156    @Override
157    public void onActivityCreated(Bundle savedInstanceState) {
158        addPreferencesFromResource(R.xml.wifi_p2p_settings);
159
160        mIntentFilter.addAction(WifiP2pManager.WIFI_P2P_STATE_CHANGED_ACTION);
161        mIntentFilter.addAction(WifiP2pManager.WIFI_P2P_PEERS_CHANGED_ACTION);
162        mIntentFilter.addAction(WifiP2pManager.WIFI_P2P_CONNECTION_CHANGED_ACTION);
163        mIntentFilter.addAction(WifiP2pManager.WIFI_P2P_THIS_DEVICE_CHANGED_ACTION);
164        mIntentFilter.addAction(WifiP2pManager.WIFI_P2P_DISCOVERY_CHANGED_ACTION);
165        mIntentFilter.addAction(WifiP2pManager.WIFI_P2P_PERSISTENT_GROUPS_CHANGED_ACTION);
166
167        final Activity activity = getActivity();
168        mWifiP2pManager = (WifiP2pManager) getSystemService(Context.WIFI_P2P_SERVICE);
169        if (mWifiP2pManager != null) {
170            mChannel = mWifiP2pManager.initialize(activity, getActivity().getMainLooper(), null);
171            if (mChannel == null) {
172                //Failure to set up connection
173                Log.e(TAG, "Failed to set up connection with wifi p2p service");
174                mWifiP2pManager = null;
175            }
176        } else {
177            Log.e(TAG, "mWifiP2pManager is null !");
178        }
179
180        if (savedInstanceState != null && savedInstanceState.containsKey(SAVE_DIALOG_PEER)) {
181            WifiP2pDevice device = savedInstanceState.getParcelable(SAVE_DIALOG_PEER);
182            mSelectedWifiPeer = new WifiP2pPeer(getActivity(), device);
183        }
184        if (savedInstanceState != null && savedInstanceState.containsKey(SAVE_DEVICE_NAME)) {
185            mSavedDeviceName = savedInstanceState.getString(SAVE_DEVICE_NAME);
186        }
187
188        mRenameListener = new OnClickListener() {
189            @Override
190            public void onClick(DialogInterface dialog, int which) {
191                if (which == DialogInterface.BUTTON_POSITIVE) {
192                    if (mWifiP2pManager != null) {
193                        mWifiP2pManager.setDeviceName(mChannel,
194                                mDeviceNameText.getText().toString(),
195                                new WifiP2pManager.ActionListener() {
196                            public void onSuccess() {
197                                if (DBG) Log.d(TAG, " device rename success");
198                            }
199                            public void onFailure(int reason) {
200                                Toast.makeText(getActivity(),
201                                        R.string.wifi_p2p_failed_rename_message,
202                                        Toast.LENGTH_LONG).show();
203                            }
204                        });
205                    }
206                }
207            }
208        };
209
210        //disconnect dialog listener
211        mDisconnectListener = new OnClickListener() {
212            @Override
213            public void onClick(DialogInterface dialog, int which) {
214                if (which == DialogInterface.BUTTON_POSITIVE) {
215                    if (mWifiP2pManager != null) {
216                        mWifiP2pManager.removeGroup(mChannel, new WifiP2pManager.ActionListener() {
217                            public void onSuccess() {
218                                if (DBG) Log.d(TAG, " remove group success");
219                            }
220                            public void onFailure(int reason) {
221                                if (DBG) Log.d(TAG, " remove group fail " + reason);
222                            }
223                        });
224                    }
225                }
226            }
227        };
228
229        //cancel connect dialog listener
230        mCancelConnectListener = new OnClickListener() {
231            @Override
232            public void onClick(DialogInterface dialog, int which) {
233                if (which == DialogInterface.BUTTON_POSITIVE) {
234                    if (mWifiP2pManager != null) {
235                        mWifiP2pManager.cancelConnect(mChannel,
236                                new WifiP2pManager.ActionListener() {
237                            public void onSuccess() {
238                                if (DBG) Log.d(TAG, " cancel connect success");
239                            }
240                            public void onFailure(int reason) {
241                                if (DBG) Log.d(TAG, " cancel connect fail " + reason);
242                            }
243                        });
244                    }
245                }
246            }
247        };
248
249        //delete persistent group dialog listener
250        mDeleteGroupListener = new OnClickListener() {
251            @Override
252            public void onClick(DialogInterface dialog, int which) {
253                if (which == DialogInterface.BUTTON_POSITIVE) {
254                    if (mWifiP2pManager != null) {
255                        mWifiP2pManager.deletePersistentGroup(mChannel,
256                                mSelectedGroup.getNetworkId(),
257                                new WifiP2pManager.ActionListener() {
258                            public void onSuccess() {
259                                if (DBG) Log.d(TAG, " delete group success");
260                            }
261                            public void onFailure(int reason) {
262                                if (DBG) Log.d(TAG, " delete group fail " + reason);
263                            }
264                        });
265                    }
266                }
267            }
268        };
269
270        setHasOptionsMenu(true);
271
272        final PreferenceScreen preferenceScreen = getPreferenceScreen();
273        preferenceScreen.removeAll();
274
275        preferenceScreen.setOrderingAsAdded(true);
276        mThisDevicePref = new Preference(getActivity());
277        preferenceScreen.addPreference(mThisDevicePref);
278
279        mPeersGroup = new PreferenceCategory(getActivity());
280        mPeersGroup.setTitle(R.string.wifi_p2p_peer_devices);
281
282        mPersistentGroup = new PreferenceCategory(getActivity());
283        mPersistentGroup.setTitle(R.string.wifi_p2p_remembered_groups);
284
285        super.onActivityCreated(savedInstanceState);
286    }
287
288    @Override
289    public void onResume() {
290        super.onResume();
291        getActivity().registerReceiver(mReceiver, mIntentFilter);
292    }
293
294    @Override
295    public void onPause() {
296        super.onPause();
297        mWifiP2pManager.stopPeerDiscovery(mChannel, null);
298        getActivity().unregisterReceiver(mReceiver);
299    }
300
301    @Override
302    public void onCreateOptionsMenu(Menu menu, MenuInflater inflater) {
303        int textId = mWifiP2pSearching ? R.string.wifi_p2p_menu_searching :
304                R.string.wifi_p2p_menu_search;
305        menu.add(Menu.NONE, MENU_ID_SEARCH, 0, textId)
306            .setEnabled(mWifiP2pEnabled)
307            .setShowAsAction(MenuItem.SHOW_AS_ACTION_IF_ROOM);
308        menu.add(Menu.NONE, MENU_ID_RENAME, 0, R.string.wifi_p2p_menu_rename)
309            .setEnabled(mWifiP2pEnabled)
310            .setShowAsAction(MenuItem.SHOW_AS_ACTION_IF_ROOM);
311        super.onCreateOptionsMenu(menu, inflater);
312    }
313
314    @Override
315    public void onPrepareOptionsMenu(Menu menu) {
316        MenuItem searchMenu = menu.findItem(MENU_ID_SEARCH);
317        MenuItem renameMenu = menu.findItem(MENU_ID_RENAME);
318        if (mWifiP2pEnabled) {
319            searchMenu.setEnabled(true);
320            renameMenu.setEnabled(true);
321        } else {
322            searchMenu.setEnabled(false);
323            renameMenu.setEnabled(false);
324        }
325
326        if (mWifiP2pSearching) {
327            searchMenu.setTitle(R.string.wifi_p2p_menu_searching);
328        } else {
329            searchMenu.setTitle(R.string.wifi_p2p_menu_search);
330        }
331    }
332
333    @Override
334    public boolean onOptionsItemSelected(MenuItem item) {
335        switch (item.getItemId()) {
336            case MENU_ID_SEARCH:
337                startSearch();
338                return true;
339            case MENU_ID_RENAME:
340                showDialog(DIALOG_RENAME);
341                return true;
342        }
343        return super.onOptionsItemSelected(item);
344    }
345
346    @Override
347    public boolean onPreferenceTreeClick(PreferenceScreen screen, Preference preference) {
348        if (preference instanceof WifiP2pPeer) {
349            mSelectedWifiPeer = (WifiP2pPeer) preference;
350            if (mSelectedWifiPeer.device.status == WifiP2pDevice.CONNECTED) {
351                showDialog(DIALOG_DISCONNECT);
352            } else if (mSelectedWifiPeer.device.status == WifiP2pDevice.INVITED) {
353                showDialog(DIALOG_CANCEL_CONNECT);
354            } else {
355                WifiP2pConfig config = new WifiP2pConfig();
356                config.deviceAddress = mSelectedWifiPeer.device.deviceAddress;
357
358                int forceWps = SystemProperties.getInt("wifidirect.wps", -1);
359
360                if (forceWps != -1) {
361                    config.wps.setup = forceWps;
362                } else {
363                    if (mSelectedWifiPeer.device.wpsPbcSupported()) {
364                        config.wps.setup = WpsInfo.PBC;
365                    } else if (mSelectedWifiPeer.device.wpsKeypadSupported()) {
366                        config.wps.setup = WpsInfo.KEYPAD;
367                    } else {
368                        config.wps.setup = WpsInfo.DISPLAY;
369                    }
370                }
371
372                mWifiP2pManager.connect(mChannel, config,
373                        new WifiP2pManager.ActionListener() {
374                            public void onSuccess() {
375                                if (DBG) Log.d(TAG, " connect success");
376                            }
377                            public void onFailure(int reason) {
378                                Log.e(TAG, " connect fail " + reason);
379                                Toast.makeText(getActivity(),
380                                        R.string.wifi_p2p_failed_connect_message,
381                                        Toast.LENGTH_SHORT).show();
382                            }
383                    });
384            }
385        } else if (preference instanceof WifiP2pPersistentGroup) {
386            mSelectedGroup = (WifiP2pPersistentGroup) preference;
387            showDialog(DIALOG_DELETE_GROUP);
388        }
389        return super.onPreferenceTreeClick(screen, preference);
390    }
391
392    @Override
393    public Dialog onCreateDialog(int id) {
394        if (id == DIALOG_DISCONNECT) {
395            String deviceName = TextUtils.isEmpty(mSelectedWifiPeer.device.deviceName) ?
396                    mSelectedWifiPeer.device.deviceAddress :
397                    mSelectedWifiPeer.device.deviceName;
398            String msg;
399            if (mConnectedDevices > 1) {
400                msg = getActivity().getString(R.string.wifi_p2p_disconnect_multiple_message,
401                        deviceName, mConnectedDevices - 1);
402            } else {
403                msg = getActivity().getString(R.string.wifi_p2p_disconnect_message, deviceName);
404            }
405            AlertDialog dialog = new AlertDialog.Builder(getActivity())
406                .setTitle(R.string.wifi_p2p_disconnect_title)
407                .setMessage(msg)
408                .setPositiveButton(getActivity().getString(R.string.dlg_ok), mDisconnectListener)
409                .setNegativeButton(getActivity().getString(R.string.dlg_cancel), null)
410                .create();
411            return dialog;
412        } else if (id == DIALOG_CANCEL_CONNECT) {
413            int stringId = R.string.wifi_p2p_cancel_connect_message;
414            String deviceName = TextUtils.isEmpty(mSelectedWifiPeer.device.deviceName) ?
415                    mSelectedWifiPeer.device.deviceAddress :
416                    mSelectedWifiPeer.device.deviceName;
417
418            AlertDialog dialog = new AlertDialog.Builder(getActivity())
419                .setTitle(R.string.wifi_p2p_cancel_connect_title)
420                .setMessage(getActivity().getString(stringId, deviceName))
421                .setPositiveButton(getActivity().getString(R.string.dlg_ok), mCancelConnectListener)
422                .setNegativeButton(getActivity().getString(R.string.dlg_cancel), null)
423                .create();
424            return dialog;
425        } else if (id == DIALOG_RENAME) {
426            mDeviceNameText = new EditText(getActivity());
427            if (mSavedDeviceName != null) {
428                mDeviceNameText.setText(mSavedDeviceName);
429                mDeviceNameText.setSelection(mSavedDeviceName.length());
430            } else if (mThisDevice != null && !TextUtils.isEmpty(mThisDevice.deviceName)) {
431                mDeviceNameText.setText(mThisDevice.deviceName);
432                mDeviceNameText.setSelection(0, mThisDevice.deviceName.length());
433            }
434            mSavedDeviceName = null;
435            AlertDialog dialog = new AlertDialog.Builder(getActivity())
436                .setTitle(R.string.wifi_p2p_menu_rename)
437                .setView(mDeviceNameText)
438                .setPositiveButton(getActivity().getString(R.string.dlg_ok), mRenameListener)
439                .setNegativeButton(getActivity().getString(R.string.dlg_cancel), null)
440                .create();
441            return dialog;
442        } else if (id == DIALOG_DELETE_GROUP) {
443            int stringId = R.string.wifi_p2p_delete_group_message;
444
445            AlertDialog dialog = new AlertDialog.Builder(getActivity())
446                .setMessage(getActivity().getString(stringId))
447                .setPositiveButton(getActivity().getString(R.string.dlg_ok), mDeleteGroupListener)
448                .setNegativeButton(getActivity().getString(R.string.dlg_cancel), null)
449                .create();
450            return dialog;
451        }
452        return null;
453    }
454
455    @Override
456    public void onSaveInstanceState(Bundle outState) {
457        if (mSelectedWifiPeer != null) {
458            outState.putParcelable(SAVE_DIALOG_PEER, mSelectedWifiPeer.device);
459        }
460        if (mDeviceNameText != null) {
461            outState.putString(SAVE_DEVICE_NAME, mDeviceNameText.getText().toString());
462        }
463    }
464
465    private void handlePeersChanged() {
466        mPeersGroup.removeAll();
467
468        mConnectedDevices = 0;
469        if (DBG) Log.d(TAG, "List of available peers");
470        for (WifiP2pDevice peer: mPeers.getDeviceList()) {
471            if (DBG) Log.d(TAG, "-> " + peer);
472            mPeersGroup.addPreference(new WifiP2pPeer(getActivity(), peer));
473            if (peer.status == WifiP2pDevice.CONNECTED) mConnectedDevices++;
474        }
475        if (DBG) Log.d(TAG, " mConnectedDevices " + mConnectedDevices);
476    }
477
478    public void onPersistentGroupInfoAvailable(WifiP2pGroupList groups) {
479        mPersistentGroup.removeAll();
480
481        for (WifiP2pGroup group: groups.getGroupList()) {
482            if (DBG) Log.d(TAG, " group " + group);
483            mPersistentGroup.addPreference(new WifiP2pPersistentGroup(getActivity(), group));
484        }
485    }
486
487    public void onGroupInfoAvailable(WifiP2pGroup group) {
488        if (DBG) Log.d(TAG, " group " + group);
489        mConnectedGroup = group;
490        updateDevicePref();
491    }
492
493    private void handleP2pStateChanged() {
494        updateSearchMenu(false);
495        if (mWifiP2pEnabled) {
496            final PreferenceScreen preferenceScreen = getPreferenceScreen();
497            preferenceScreen.removeAll();
498
499            preferenceScreen.setOrderingAsAdded(true);
500            preferenceScreen.addPreference(mThisDevicePref);
501
502            mPeersGroup.setEnabled(true);
503            preferenceScreen.addPreference(mPeersGroup);
504
505            mPersistentGroup.setEnabled(true);
506            preferenceScreen.addPreference(mPersistentGroup);
507        }
508    }
509
510    private void updateSearchMenu(boolean searching) {
511       mWifiP2pSearching = searching;
512       Activity activity = getActivity();
513       if (activity != null) activity.invalidateOptionsMenu();
514    }
515
516    private void startSearch() {
517        if (mWifiP2pManager != null && !mWifiP2pSearching) {
518            mWifiP2pManager.discoverPeers(mChannel, new WifiP2pManager.ActionListener() {
519                public void onSuccess() {
520                }
521                public void onFailure(int reason) {
522                    if (DBG) Log.d(TAG, " discover fail " + reason);
523                }
524            });
525        }
526    }
527
528    private void updateDevicePref() {
529        if (mThisDevice != null) {
530            if (TextUtils.isEmpty(mThisDevice.deviceName)) {
531                mThisDevicePref.setTitle(mThisDevice.deviceAddress);
532            } else {
533                mThisDevicePref.setTitle(mThisDevice.deviceName);
534            }
535
536            mThisDevicePref.setPersistent(false);
537            mThisDevicePref.setEnabled(true);
538            mThisDevicePref.setSelectable(false);
539        }
540    }
541}
542