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