NetworkActivity.java revision 134de29dacd645eddf672d7d2d544f059e75aff6
1/* 2 * Copyright (C) 2014 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.tv.settings.connectivity; 18 19import android.app.Activity; 20import android.content.Context; 21import android.content.Intent; 22import android.content.res.Resources; 23import android.net.IpConfiguration; 24import android.net.IpConfiguration.IpAssignment; 25import android.net.IpConfiguration.ProxySettings; 26import android.net.wifi.WifiConfiguration; 27import android.net.wifi.ScanResult; 28import android.net.wifi.WifiInfo; 29import android.net.wifi.WifiManager; 30import android.os.Bundle; 31import android.os.Handler; 32import android.os.SystemClock; 33import android.text.TextUtils; 34import android.util.Log; 35import android.util.Pair; 36 37import com.android.tv.settings.dialog.SettingsLayoutActivity; 38import com.android.tv.settings.dialog.Layout; 39import com.android.tv.settings.dialog.Layout.Header; 40import com.android.tv.settings.dialog.Layout.Action; 41import com.android.tv.settings.dialog.Layout.Status; 42import com.android.tv.settings.dialog.Layout.Static; 43import com.android.tv.settings.dialog.Layout.StringGetter; 44import com.android.tv.settings.dialog.Layout.LayoutGetter; 45import com.android.tv.settings.R; 46 47import java.util.ArrayList; 48import java.util.Collections; 49import java.util.HashMap; 50import java.util.List; 51 52/** 53 * Activity to manage network settings. 54 */ 55public class NetworkActivity extends SettingsLayoutActivity implements 56 ConnectivityListener.Listener { 57 58 private static final boolean DEBUG = false; 59 private static final String TAG = "NetworkActivity"; 60 private static final int REQUEST_CODE_ADVANCED_OPTIONS = 1; 61 private static final int WIFI_REFRESH_INTERVAL_CAP_MILLIS = 15 * 1000; 62 63 private ConnectivityListener mConnectivityListener; 64 private Resources mRes; 65 private Handler mHandler = new Handler(); 66 67 @Override 68 protected void onCreate(Bundle savedInstanceState) { 69 mRes = getResources(); 70 mConnectivityListener = new ConnectivityListener(this, this); 71 mConnectivityListener.start(); 72 super.onCreate(savedInstanceState); 73 } 74 75 @Override 76 protected void onDestroy(){ 77 super.onDestroy(); 78 mConnectivityListener.stop(); 79 } 80 81 @Override 82 public void onConnectivityChange(Intent intent) { 83 mEthernetConnectedDescription.refreshView(); 84 mWifiConnectedDescription.refreshView(); 85 } 86 87 @Override 88 public void onResume() { 89 if (mWifiShortListLayout != null) { 90 mWifiShortListLayout.onWifiListInvalidated(); 91 } 92 if (mWifiAllListLayout != null) { 93 mWifiAllListLayout.onWifiListInvalidated(); 94 } 95 super.onResume(); 96 } 97 98 StringGetter mEthernetConnectedDescription = new StringGetter() { 99 private boolean lastIsEthernetConnected; 100 @Override 101 public String get() { 102 lastIsEthernetConnected = 103 mConnectivityListener.getConnectivityStatus().isEthernetConnected(); 104 int resId = lastIsEthernetConnected ? R.string.connected : R.string.not_connected; 105 return mRes.getString(resId); 106 } 107 @Override 108 public void refreshView() { 109 if (mConnectivityListener.getConnectivityStatus().isEthernetConnected() != 110 lastIsEthernetConnected) { 111 super.refreshView(); 112 } 113 } 114 }; 115 116 StringGetter mWifiConnectedDescription = new StringGetter() { 117 private boolean lastIsWifiConnected; 118 @Override 119 public String get() { 120 lastIsWifiConnected = mConnectivityListener.getConnectivityStatus().isWifiConnected(); 121 int resId = lastIsWifiConnected ? R.string.connected : R.string.not_connected; 122 return mRes.getString(resId); 123 } 124 @Override 125 public void refreshView() { 126 if (mConnectivityListener.getConnectivityStatus().isWifiConnected() != 127 lastIsWifiConnected) { 128 super.refreshView(); 129 } 130 } 131 }; 132 133 StringGetter mEthernetIPAddress = new StringGetter() { 134 public String get() { 135 ConnectivityListener.ConnectivityStatus status = 136 mConnectivityListener.getConnectivityStatus(); 137 if (status.isEthernetConnected()) { 138 return mConnectivityListener.getEthernetIpAddress(); 139 } else { 140 return ""; 141 } 142 } 143 }; 144 145 StringGetter mEthernetMacAddress = new StringGetter() { 146 public String get() { 147 return mConnectivityListener.getEthernetMacAddress(); 148 } 149 }; 150 151 LayoutGetter mEthernetAdvancedLayout = new LayoutGetter() { 152 public Layout get() { 153 Layout layout = new Layout(); 154 // Do not check Ethernet's availability here 155 // because it might not be active due to the invalid configuration. 156 IpConfiguration ipConfiguration = mConnectivityListener.getIpConfiguration(); 157 if (ipConfiguration != null) { 158 int proxySettingsResourceId = 159 (ipConfiguration.getProxySettings() == ProxySettings.STATIC) ? 160 R.string.wifi_action_proxy_manual : 161 R.string.wifi_action_proxy_none; 162 int ipSettingsResourceId = 163 (ipConfiguration.getIpAssignment() == IpAssignment.STATIC) ? 164 R.string.wifi_action_static : 165 R.string.wifi_action_dhcp; 166 layout 167 .add(new Action.Builder(mRes, ACTION_ETHERNET_PROXY_SETTINGS) 168 .title(R.string.title_wifi_proxy_settings) 169 .description(proxySettingsResourceId).build()) 170 .add(new Action.Builder(mRes, ACTION_ETHERNET_IP_SETTINGS) 171 .title(R.string.title_wifi_ip_settings) 172 .description(ipSettingsResourceId).build()); 173 } else { 174 layout 175 .add(new Status.Builder(mRes) 176 .title(R.string.title_internet_connection) 177 .description(R.string.not_connected).build()); 178 } 179 return layout; 180 } 181 }; 182 183 LayoutGetter mEthernetLayout = new LayoutGetter() { 184 public Layout get() { 185 boolean ethernetConnected = 186 mConnectivityListener.getConnectivityStatus().isEthernetConnected(); 187 if (ethernetConnected) { 188 return new Layout() 189 .add(new Status.Builder(mRes).title(R.string.title_internet_connection) 190 .description(R.string.connected).build()) 191 .add(new Status.Builder(mRes) 192 .title(R.string.title_ip_address) 193 .description(mEthernetIPAddress) 194 .build()) 195 .add(new Status.Builder(mRes) 196 .title(R.string.title_mac_address) 197 .description(mEthernetMacAddress) 198 .build()) 199 .add(new Header.Builder(mRes) 200 .title(R.string.wifi_action_advanced_options_title).build() 201 .add(mEthernetAdvancedLayout) 202 ); 203 204 } else { 205 return new Layout() 206 .add(new Status.Builder(mRes) 207 .title(R.string.title_internet_connection) 208 .description(R.string.not_connected) 209 .build()) 210 .add(new Header.Builder(mRes) 211 .title(R.string.wifi_action_advanced_options_title).build() 212 .add(mEthernetAdvancedLayout) 213 ); 214 } 215 } 216 }; 217 218 private static final int NUMBER_SIGNAL_LEVELS = 4; 219 private static final int ACTION_WIFI_FORGET_NETWORK = 1; 220 private static final int ACTION_WIFI_PROXY_SETTINGS = 4; 221 private static final int ACTION_WIFI_IP_SETTINGS = 5; 222 private static final int ACTION_ETHERNET_PROXY_SETTINGS = 6; 223 private static final int ACTION_ETHERNET_IP_SETTINGS = 7; 224 225 private final Context mContext = this; 226 227 private int getCurrentNetworkIconResourceId( 228 ScanResult scanResult, int signalLevel) { 229 int resourceId = 0; 230 if (scanResult != null) { 231 WifiSecurity security = WifiSecurity.getSecurity(scanResult); 232 if (security.isOpen()) { 233 switch (signalLevel) { 234 case 0: 235 resourceId = R.drawable.ic_settings_wifi_active_1; 236 break; 237 case 1: 238 resourceId = R.drawable.ic_settings_wifi_active_2; 239 break; 240 case 2: 241 resourceId = R.drawable.ic_settings_wifi_active_3; 242 break; 243 case 3: 244 resourceId = R.drawable.ic_settings_wifi_active_4; 245 break; 246 } 247 } else { 248 switch (signalLevel) { 249 case 0: 250 resourceId = R.drawable.ic_settings_wifi_secure_active_1; 251 break; 252 case 1: 253 resourceId = R.drawable.ic_settings_wifi_secure_active_2; 254 break; 255 case 2: 256 resourceId = R.drawable.ic_settings_wifi_secure_active_3; 257 break; 258 case 3: 259 resourceId = R.drawable.ic_settings_wifi_secure_active_4; 260 break; 261 } 262 } 263 } 264 return resourceId; 265 } 266 267 private String getSignalStrength() { 268 String[] signalLevels = mRes.getStringArray(R.array.wifi_signal_strength); 269 int strength = mConnectivityListener.getWifiSignalStrength(signalLevels.length); 270 return signalLevels[strength]; 271 } 272 273 LayoutGetter mWifiInfoLayout = new LayoutGetter() { 274 public Layout get() { 275 Layout layout = new Layout(); 276 ConnectivityListener.ConnectivityStatus status = 277 mConnectivityListener.getConnectivityStatus(); 278 boolean isConnected = status.isWifiConnected(); 279 if (isConnected) { 280 layout 281 .add(new Status.Builder(mRes) 282 .title(R.string.title_internet_connection) 283 .description(R.string.connected).build()) 284 .add(new Status.Builder(mRes) 285 .title(R.string.title_ip_address) 286 .description(mConnectivityListener.getWifiIpAddress()).build()) 287 .add(new Status.Builder(mRes) 288 .title(R.string.title_mac_address) 289 .description(mConnectivityListener.getWifiMacAddress()).build()) 290 .add(new Status.Builder(mRes) 291 .title(R.string.title_signal_strength) 292 .description(getSignalStrength()).build()); 293 } else { 294 layout 295 .add(new Status.Builder(mRes) 296 .title(R.string.title_internet_connection) 297 .description(R.string.not_connected).build()); 298 } 299 return layout; 300 } 301 }; 302 303 LayoutGetter mWifiAdvancedLayout = new LayoutGetter() { 304 public Layout get() { 305 Layout layout = new Layout(); 306 WifiConfiguration wifiConfiguration = mConnectivityListener.getWifiConfiguration(); 307 if (wifiConfiguration != null) { 308 int proxySettingsResourceId = 309 (wifiConfiguration.getProxySettings() == ProxySettings.NONE) ? 310 R.string.wifi_action_proxy_none : 311 R.string.wifi_action_proxy_manual; 312 int ipSettingsResourceId = 313 (wifiConfiguration.getIpAssignment() == IpAssignment.STATIC) ? 314 R.string.wifi_action_static : 315 R.string.wifi_action_dhcp; 316 layout 317 .add(new Action.Builder(mRes, ACTION_WIFI_PROXY_SETTINGS) 318 .title(R.string.title_wifi_proxy_settings) 319 .description(proxySettingsResourceId).build()) 320 .add(new Action.Builder(mRes, ACTION_WIFI_IP_SETTINGS) 321 .title(R.string.title_wifi_ip_settings) 322 .description(ipSettingsResourceId).build()); 323 } else { 324 layout 325 .add(new Status.Builder(mRes) 326 .title(R.string.title_internet_connection) 327 .description(R.string.not_connected).build()); 328 } 329 return layout; 330 } 331 }; 332 333 private void addWifiConnectedHeader(Layout layout, String SSID) { 334 layout 335 .add(new Header.Builder(mRes) 336 .title(SSID) 337 .description(R.string.connected).build() 338 .add(new Header.Builder(mRes) 339 .title(R.string.wifi_action_status_info).build() 340 .add(mWifiInfoLayout) 341 ) 342 .add(new Header.Builder(mRes) 343 .title(R.string.wifi_action_advanced_options_title).build() 344 .add(mWifiAdvancedLayout) 345 ) 346 .add(new Header.Builder(mRes) 347 .title(R.string.wifi_forget_network).build() 348 .add(new Action.Builder(mRes, ACTION_WIFI_FORGET_NETWORK) 349 .title(R.string.title_ok).build()) 350 .add(new Action.Builder(mRes, Action.ACTION_BACK) 351 .title(R.string.title_cancel).build()) 352 ) 353 ); 354 } 355 356 private class WifiListLayout extends LayoutGetter implements 357 ConnectivityListener.WifiNetworkListener { 358 private final boolean mTop3EntriesOnly; 359 private String mSelectedTitle; 360 private long mLastWifiRefresh = 0; 361 362 private final Runnable mRefreshViewRunnable = new Runnable() { 363 @Override 364 public void run() { 365 Layout.Node selected = getSelectedNode(); 366 if (selected != null) { 367 mSelectedTitle = selected.getTitle(); 368 } 369 refreshView(); 370 } 371 }; 372 373 WifiListLayout(boolean top3EntriesOnly) { 374 mTop3EntriesOnly = top3EntriesOnly; 375 } 376 377 @Override 378 public Layout get() { 379 mConnectivityListener.startScanningWifi(this); 380 mLastWifiRefresh = SystemClock.elapsedRealtime(); 381 mHandler.removeCallbacks(mRefreshViewRunnable); 382 return initAvailableWifiNetworks(mTop3EntriesOnly, mSelectedTitle). 383 setSelectedByTitle(mSelectedTitle); 384 } 385 386 @Override 387 public void onMovedOffScreen() { 388 mHandler.removeCallbacks(mRefreshViewRunnable); 389 mConnectivityListener.stopScanningWifi(this); 390 } 391 392 @Override 393 public void onWifiListChanged() { 394 long now = SystemClock.elapsedRealtime(); 395 long millisToNextRefreshView = 396 WIFI_REFRESH_INTERVAL_CAP_MILLIS - now + mLastWifiRefresh; 397 mHandler.removeCallbacks(mRefreshViewRunnable); 398 mHandler.postDelayed(mRefreshViewRunnable, millisToNextRefreshView); 399 } 400 401 /** 402 * Wifi network configuration has changed and an immediate refresh of the list of Wifi 403 * networks is required. 404 */ 405 public void onWifiListInvalidated() { 406 mLastWifiRefresh = 0; 407 onWifiListChanged(); 408 } 409 410 /** 411 * Create a list of available Wifi networks sorted by connection status (a connected Wifi 412 * network is shown at the first position on the list) and signal strength, with the 413 * provisio that the wireless network with SSID "mustHave" should be included in the list 414 * even if it would be otherwise excluded. 415 * 416 * @param top3EntriesOnly Show only 3 entries in the list. 417 * @param mustHave Include this wifi network in the list even if it would otherwise 418 * be excluded by virtue of inadequate signal strength. 419 */ 420 private Layout initAvailableWifiNetworks(boolean top3EntriesOnly, String mustHave) { 421 List<ScanResult> networks = mConnectivityListener.getAvailableNetworks(); 422 Layout layout = new Layout(); 423 if (networks.size() > 0) { 424 int maxItems = top3EntriesOnly ? 3 : Integer.MAX_VALUE; 425 // "networks" is already sorted by the signal strength and connection status. 426 // Generate a new list with size less than "maxItems" that ensures "mustHave" is 427 // included. 428 boolean haveMustHave = false; 429 List<ScanResult> displayList = new ArrayList<ScanResult>(); 430 for (ScanResult scanResult : networks) { 431 if (!haveMustHave && TextUtils.equals(scanResult.SSID, mustHave)) { 432 haveMustHave = true; 433 if (displayList.size() == maxItems) { 434 displayList.remove(maxItems-1); 435 } 436 displayList.add(scanResult); 437 } else if (displayList.size() < maxItems) { 438 displayList.add(scanResult); 439 } 440 if (haveMustHave && displayList.size() == maxItems) { 441 break; 442 } 443 } 444 445 // If a network is connected, it will be the first on the list. 446 boolean isConnected = 447 mConnectivityListener.getConnectivityStatus().isWifiConnected(); 448 for (ScanResult network : displayList) { 449 if (network != null) { 450 WifiSecurity security = WifiSecurity.getSecurity(network); 451 452 String networkDescription = 453 security.isOpen() ? "" : security.getName(mContext); 454 Intent intent = 455 WifiConnectionActivity.createIntent(mContext, network, security); 456 int signalLevel = WifiManager.calculateSignalLevel( 457 network.level, NUMBER_SIGNAL_LEVELS); 458 //TODO implement signal dependent list icon. 459 /* 460 int imageResourceId = getNetworkIconResourceId(network, signalLevel); 461 if (WifiConfigHelper.areSameNetwork(mWifiManager, network, 462 currentConnection)) { 463 networkDescription = getString(R.string.connected); 464 signalLevel = WifiManager.calculateSignalLevel( 465 currentConnection.getRssi(), NUMBER_SIGNAL_LEVELS); 466 imageResourceId = getCurrentNetworkIconResourceId(network, signalLevel); 467 } */ 468 469 if (isConnected) { 470 addWifiConnectedHeader(layout, network.SSID); 471 } else { 472 layout.add(new Action.Builder(mRes, intent) 473 .title(network.SSID) 474 .description(networkDescription).build()); 475 } 476 } 477 isConnected = false; 478 } 479 } else { 480 layout.add(new Action.Builder(mRes, 0) 481 .title(R.string.title_wifi_no_networks_available).build()); 482 } 483 return layout; 484 } 485 }; 486 487 private final WifiListLayout mWifiShortListLayout = new WifiListLayout(true); 488 489 private final WifiListLayout mWifiAllListLayout = new WifiListLayout(false); 490 491 @Override 492 public Layout createLayout() { 493 return 494 new Layout() 495 .breadcrumb(getString(R.string.header_category_device)) 496 .add(new Header.Builder(mRes) 497 .icon(R.drawable.ic_settings_wifi_4) 498 .title(R.string.connectivity_network) 499 .description(mWifiConnectedDescription) 500 .build() 501 .add(new Header.Builder(mRes).title(R.string.connectivity_wifi) 502 .contentIconRes(R.drawable.ic_settings_wifi_4) 503 .description(mWifiConnectedDescription).build() 504 .add(new Static.Builder(mRes) 505 .title(R.string.wifi_setting_available_networks) 506 .build()) 507 .add(mWifiShortListLayout) 508 .add(new Header.Builder(mRes) 509 .title(R.string.wifi_setting_see_all) 510 .build() 511 .add(mWifiAllListLayout) 512 ) 513 .add(new Static.Builder(mRes) 514 .title(R.string.wifi_setting_header_other_options) 515 .build()) 516 .add(new Action.Builder(mRes, 517 new Intent(this, WpsConnectionActivity.class)) 518 .title(R.string.wifi_setting_other_options_wps) 519 .build()) 520 .add(new Action.Builder(mRes, 521 new Intent(this, AddWifiNetworkActivity.class)) 522 .title(R.string.wifi_setting_other_options_add_network) 523 .build()) 524 ) 525 .add(new Header.Builder(mRes) 526 .title(R.string.connectivity_ethernet) 527 .contentIconRes(R.drawable.ic_settings_ethernet) 528 .description(mEthernetConnectedDescription) 529 .build() 530 .add(mEthernetLayout) 531 ) 532 ); 533 } 534 535 @Override 536 protected void onActivityResult(int requestCode, int resultCode, Intent data) { 537 if (requestCode == REQUEST_CODE_ADVANCED_OPTIONS && resultCode == RESULT_OK) { 538 //TODO make sure view reflects model deltas 539 } else { 540 super.onActivityResult(requestCode, resultCode, data); 541 } 542 } 543 544 @Override 545 public void onActionFocused(Layout.LayoutRow item) { 546 int resId = item.getContentIconRes(); 547 if (resId != 0) { 548 setIcon(resId); 549 } 550 } 551 552 @Override 553 public void onActionClicked(Action action) { 554 switch (action.getId()) { 555 case Action.ACTION_INTENT: 556 startActivityForResult(action.getIntent(), REQUEST_CODE_ADVANCED_OPTIONS); 557 break; 558 case ACTION_WIFI_FORGET_NETWORK: 559 mConnectivityListener.forgetWifiNetwork(); 560 goBackToTitle(mRes.getString(R.string.connectivity_wifi)); 561 mWifiShortListLayout.onWifiListInvalidated(); 562 mWifiAllListLayout.onWifiListInvalidated(); 563 break; 564 case ACTION_WIFI_PROXY_SETTINGS: { 565 int networkId = mConnectivityListener.getWifiNetworkId(); 566 if (networkId != -1) { 567 startActivityForResult(EditProxySettingsActivity.createIntent(this, networkId), 568 REQUEST_CODE_ADVANCED_OPTIONS); 569 } 570 break; 571 } 572 case ACTION_WIFI_IP_SETTINGS: { 573 int networkId = mConnectivityListener.getWifiNetworkId(); 574 if (networkId != -1) { 575 startActivityForResult(EditIpSettingsActivity.createIntent(this, networkId), 576 REQUEST_CODE_ADVANCED_OPTIONS); 577 } 578 break; 579 } 580 case ACTION_ETHERNET_PROXY_SETTINGS: { 581 int networkId = WifiConfiguration.INVALID_NETWORK_ID; 582 startActivityForResult(EditProxySettingsActivity.createIntent(this, networkId), 583 REQUEST_CODE_ADVANCED_OPTIONS); 584 break; 585 } 586 case ACTION_ETHERNET_IP_SETTINGS: { 587 int networkId = WifiConfiguration.INVALID_NETWORK_ID; 588 startActivityForResult(EditIpSettingsActivity.createIntent(this, networkId), 589 REQUEST_CODE_ADVANCED_OPTIONS); 590 break; 591 } 592 } 593 } 594} 595