| /* |
| * Copyright (C) 2014 The Android Open Source Project |
| * |
| * Licensed under the Apache License, Version 2.0 (the "License"); |
| * you may not use this file except in compliance with the License. |
| * You may obtain a copy of the License at |
| * |
| * http://www.apache.org/licenses/LICENSE-2.0 |
| * |
| * Unless required by applicable law or agreed to in writing, software |
| * distributed under the License is distributed on an "AS IS" BASIS, |
| * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| * See the License for the specific language governing permissions and |
| * limitations under the License. |
| */ |
| |
| package com.android.tv.settings.connectivity; |
| |
| import android.app.Activity; |
| import android.content.Context; |
| import android.content.Intent; |
| import android.content.res.Resources; |
| import android.net.IpConfiguration; |
| import android.net.IpConfiguration.IpAssignment; |
| import android.net.IpConfiguration.ProxySettings; |
| import android.net.wifi.WifiConfiguration; |
| import android.net.wifi.ScanResult; |
| import android.net.wifi.WifiInfo; |
| import android.net.wifi.WifiManager; |
| import android.os.Bundle; |
| import android.os.Handler; |
| import android.os.SystemClock; |
| import android.text.TextUtils; |
| import android.util.Log; |
| import android.util.Pair; |
| |
| import com.android.tv.settings.dialog.SettingsLayoutActivity; |
| import com.android.tv.settings.dialog.Layout; |
| import com.android.tv.settings.dialog.Layout.Header; |
| import com.android.tv.settings.dialog.Layout.Action; |
| import com.android.tv.settings.dialog.Layout.Status; |
| import com.android.tv.settings.dialog.Layout.Static; |
| import com.android.tv.settings.dialog.Layout.StringGetter; |
| import com.android.tv.settings.dialog.Layout.LayoutGetter; |
| import com.android.tv.settings.R; |
| |
| import java.util.ArrayList; |
| import java.util.Collections; |
| import java.util.HashMap; |
| import java.util.List; |
| |
| /** |
| * Activity to manage network settings. |
| */ |
| public class NetworkActivity extends SettingsLayoutActivity implements |
| ConnectivityListener.Listener { |
| |
| private static final boolean DEBUG = false; |
| private static final String TAG = "NetworkActivity"; |
| private static final int REQUEST_CODE_ADVANCED_OPTIONS = 1; |
| private static final int WIFI_REFRESH_INTERVAL_CAP_MILLIS = 15 * 1000; |
| |
| private ConnectivityListener mConnectivityListener; |
| private Resources mRes; |
| private Handler mHandler = new Handler(); |
| |
| @Override |
| protected void onCreate(Bundle savedInstanceState) { |
| mRes = getResources(); |
| mConnectivityListener = new ConnectivityListener(this, this); |
| mConnectivityListener.start(); |
| super.onCreate(savedInstanceState); |
| } |
| |
| @Override |
| protected void onDestroy(){ |
| super.onDestroy(); |
| mConnectivityListener.stop(); |
| } |
| |
| @Override |
| public void onConnectivityChange(Intent intent) { |
| mEthernetConnectedDescription.refreshView(); |
| mWifiConnectedDescription.refreshView(); |
| } |
| |
| @Override |
| public void onResume() { |
| if (mWifiShortListLayout != null) { |
| mWifiShortListLayout.onWifiListInvalidated(); |
| } |
| if (mWifiAllListLayout != null) { |
| mWifiAllListLayout.onWifiListInvalidated(); |
| } |
| super.onResume(); |
| } |
| |
| StringGetter mEthernetConnectedDescription = new StringGetter() { |
| private boolean lastIsEthernetConnected; |
| @Override |
| public String get() { |
| lastIsEthernetConnected = |
| mConnectivityListener.getConnectivityStatus().isEthernetConnected(); |
| int resId = lastIsEthernetConnected ? R.string.connected : R.string.not_connected; |
| return mRes.getString(resId); |
| } |
| @Override |
| public void refreshView() { |
| if (mConnectivityListener.getConnectivityStatus().isEthernetConnected() != |
| lastIsEthernetConnected) { |
| super.refreshView(); |
| } |
| } |
| }; |
| |
| StringGetter mWifiConnectedDescription = new StringGetter() { |
| private boolean lastIsWifiConnected; |
| @Override |
| public String get() { |
| lastIsWifiConnected = mConnectivityListener.getConnectivityStatus().isWifiConnected(); |
| int resId = lastIsWifiConnected ? R.string.connected : R.string.not_connected; |
| return mRes.getString(resId); |
| } |
| @Override |
| public void refreshView() { |
| if (mConnectivityListener.getConnectivityStatus().isWifiConnected() != |
| lastIsWifiConnected) { |
| super.refreshView(); |
| } |
| } |
| }; |
| |
| StringGetter mEthernetIPAddress = new StringGetter() { |
| public String get() { |
| ConnectivityListener.ConnectivityStatus status = |
| mConnectivityListener.getConnectivityStatus(); |
| if (status.isEthernetConnected()) { |
| return mConnectivityListener.getEthernetIpAddress(); |
| } else { |
| return ""; |
| } |
| } |
| }; |
| |
| StringGetter mEthernetMacAddress = new StringGetter() { |
| public String get() { |
| return mConnectivityListener.getEthernetMacAddress(); |
| } |
| }; |
| |
| LayoutGetter mEthernetAdvancedLayout = new LayoutGetter() { |
| public Layout get() { |
| Layout layout = new Layout(); |
| // Do not check Ethernet's availability here |
| // because it might not be active due to the invalid configuration. |
| IpConfiguration ipConfiguration = mConnectivityListener.getIpConfiguration(); |
| if (ipConfiguration != null) { |
| int proxySettingsResourceId = |
| (ipConfiguration.getProxySettings() == ProxySettings.STATIC) ? |
| R.string.wifi_action_proxy_manual : |
| R.string.wifi_action_proxy_none; |
| int ipSettingsResourceId = |
| (ipConfiguration.getIpAssignment() == IpAssignment.STATIC) ? |
| R.string.wifi_action_static : |
| R.string.wifi_action_dhcp; |
| layout |
| .add(new Action.Builder(mRes, ACTION_ETHERNET_PROXY_SETTINGS) |
| .title(R.string.title_wifi_proxy_settings) |
| .description(proxySettingsResourceId).build()) |
| .add(new Action.Builder(mRes, ACTION_ETHERNET_IP_SETTINGS) |
| .title(R.string.title_wifi_ip_settings) |
| .description(ipSettingsResourceId).build()); |
| } else { |
| layout |
| .add(new Status.Builder(mRes) |
| .title(R.string.title_internet_connection) |
| .description(R.string.not_connected).build()); |
| } |
| return layout; |
| } |
| }; |
| |
| LayoutGetter mEthernetLayout = new LayoutGetter() { |
| public Layout get() { |
| boolean ethernetConnected = |
| mConnectivityListener.getConnectivityStatus().isEthernetConnected(); |
| if (ethernetConnected) { |
| return new Layout() |
| .add(new Status.Builder(mRes).title(R.string.title_internet_connection) |
| .description(R.string.connected).build()) |
| .add(new Status.Builder(mRes) |
| .title(R.string.title_ip_address) |
| .description(mEthernetIPAddress) |
| .build()) |
| .add(new Status.Builder(mRes) |
| .title(R.string.title_mac_address) |
| .description(mEthernetMacAddress) |
| .build()) |
| .add(new Header.Builder(mRes) |
| .title(R.string.wifi_action_advanced_options_title).build() |
| .add(mEthernetAdvancedLayout) |
| ); |
| |
| } else { |
| return new Layout() |
| .add(new Status.Builder(mRes) |
| .title(R.string.title_internet_connection) |
| .description(R.string.not_connected) |
| .build()) |
| .add(new Header.Builder(mRes) |
| .title(R.string.wifi_action_advanced_options_title).build() |
| .add(mEthernetAdvancedLayout) |
| ); |
| } |
| } |
| }; |
| |
| private static final int NUMBER_SIGNAL_LEVELS = 4; |
| private static final int ACTION_WIFI_FORGET_NETWORK = 1; |
| private static final int ACTION_WIFI_PROXY_SETTINGS = 4; |
| private static final int ACTION_WIFI_IP_SETTINGS = 5; |
| private static final int ACTION_ETHERNET_PROXY_SETTINGS = 6; |
| private static final int ACTION_ETHERNET_IP_SETTINGS = 7; |
| |
| private final Context mContext = this; |
| |
| private int getCurrentNetworkIconResourceId( |
| ScanResult scanResult, int signalLevel) { |
| int resourceId = 0; |
| if (scanResult != null) { |
| WifiSecurity security = WifiSecurity.getSecurity(scanResult); |
| if (security.isOpen()) { |
| switch (signalLevel) { |
| case 0: |
| resourceId = R.drawable.ic_settings_wifi_active_1; |
| break; |
| case 1: |
| resourceId = R.drawable.ic_settings_wifi_active_2; |
| break; |
| case 2: |
| resourceId = R.drawable.ic_settings_wifi_active_3; |
| break; |
| case 3: |
| resourceId = R.drawable.ic_settings_wifi_active_4; |
| break; |
| } |
| } else { |
| switch (signalLevel) { |
| case 0: |
| resourceId = R.drawable.ic_settings_wifi_secure_active_1; |
| break; |
| case 1: |
| resourceId = R.drawable.ic_settings_wifi_secure_active_2; |
| break; |
| case 2: |
| resourceId = R.drawable.ic_settings_wifi_secure_active_3; |
| break; |
| case 3: |
| resourceId = R.drawable.ic_settings_wifi_secure_active_4; |
| break; |
| } |
| } |
| } |
| return resourceId; |
| } |
| |
| private String getSignalStrength() { |
| String[] signalLevels = mRes.getStringArray(R.array.wifi_signal_strength); |
| int strength = mConnectivityListener.getWifiSignalStrength(signalLevels.length); |
| return signalLevels[strength]; |
| } |
| |
| LayoutGetter mWifiInfoLayout = new LayoutGetter() { |
| public Layout get() { |
| Layout layout = new Layout(); |
| ConnectivityListener.ConnectivityStatus status = |
| mConnectivityListener.getConnectivityStatus(); |
| boolean isConnected = status.isWifiConnected(); |
| if (isConnected) { |
| layout |
| .add(new Status.Builder(mRes) |
| .title(R.string.title_internet_connection) |
| .description(R.string.connected).build()) |
| .add(new Status.Builder(mRes) |
| .title(R.string.title_ip_address) |
| .description(mConnectivityListener.getWifiIpAddress()).build()) |
| .add(new Status.Builder(mRes) |
| .title(R.string.title_mac_address) |
| .description(mConnectivityListener.getWifiMacAddress()).build()) |
| .add(new Status.Builder(mRes) |
| .title(R.string.title_signal_strength) |
| .description(getSignalStrength()).build()); |
| } else { |
| layout |
| .add(new Status.Builder(mRes) |
| .title(R.string.title_internet_connection) |
| .description(R.string.not_connected).build()); |
| } |
| return layout; |
| } |
| }; |
| |
| LayoutGetter mWifiAdvancedLayout = new LayoutGetter() { |
| public Layout get() { |
| Layout layout = new Layout(); |
| WifiConfiguration wifiConfiguration = mConnectivityListener.getWifiConfiguration(); |
| if (wifiConfiguration != null) { |
| int proxySettingsResourceId = |
| (wifiConfiguration.getProxySettings() == ProxySettings.NONE) ? |
| R.string.wifi_action_proxy_none : |
| R.string.wifi_action_proxy_manual; |
| int ipSettingsResourceId = |
| (wifiConfiguration.getIpAssignment() == IpAssignment.STATIC) ? |
| R.string.wifi_action_static : |
| R.string.wifi_action_dhcp; |
| layout |
| .add(new Action.Builder(mRes, ACTION_WIFI_PROXY_SETTINGS) |
| .title(R.string.title_wifi_proxy_settings) |
| .description(proxySettingsResourceId).build()) |
| .add(new Action.Builder(mRes, ACTION_WIFI_IP_SETTINGS) |
| .title(R.string.title_wifi_ip_settings) |
| .description(ipSettingsResourceId).build()); |
| } else { |
| layout |
| .add(new Status.Builder(mRes) |
| .title(R.string.title_internet_connection) |
| .description(R.string.not_connected).build()); |
| } |
| return layout; |
| } |
| }; |
| |
| private void addWifiConnectedHeader(Layout layout, String SSID) { |
| layout |
| .add(new Header.Builder(mRes) |
| .title(SSID) |
| .description(R.string.connected).build() |
| .add(new Header.Builder(mRes) |
| .title(R.string.wifi_action_status_info).build() |
| .add(mWifiInfoLayout) |
| ) |
| .add(new Header.Builder(mRes) |
| .title(R.string.wifi_action_advanced_options_title).build() |
| .add(mWifiAdvancedLayout) |
| ) |
| .add(new Header.Builder(mRes) |
| .title(R.string.wifi_forget_network).build() |
| .add(new Action.Builder(mRes, ACTION_WIFI_FORGET_NETWORK) |
| .title(R.string.title_ok).build()) |
| .add(new Action.Builder(mRes, Action.ACTION_BACK) |
| .title(R.string.title_cancel).build()) |
| ) |
| ); |
| } |
| |
| private class WifiListLayout extends LayoutGetter implements |
| ConnectivityListener.WifiNetworkListener { |
| private final boolean mTop3EntriesOnly; |
| private String mSelectedTitle; |
| private long mLastWifiRefresh = 0; |
| |
| private final Runnable mRefreshViewRunnable = new Runnable() { |
| @Override |
| public void run() { |
| Layout.Node selected = getSelectedNode(); |
| if (selected != null) { |
| mSelectedTitle = selected.getTitle(); |
| } |
| refreshView(); |
| } |
| }; |
| |
| WifiListLayout(boolean top3EntriesOnly) { |
| mTop3EntriesOnly = top3EntriesOnly; |
| } |
| |
| @Override |
| public Layout get() { |
| mConnectivityListener.startScanningWifi(this); |
| mLastWifiRefresh = SystemClock.elapsedRealtime(); |
| mHandler.removeCallbacks(mRefreshViewRunnable); |
| return initAvailableWifiNetworks(mTop3EntriesOnly, mSelectedTitle). |
| setSelectedByTitle(mSelectedTitle); |
| } |
| |
| @Override |
| public void onMovedOffScreen() { |
| mHandler.removeCallbacks(mRefreshViewRunnable); |
| mConnectivityListener.stopScanningWifi(this); |
| } |
| |
| @Override |
| public void onWifiListChanged() { |
| long now = SystemClock.elapsedRealtime(); |
| long millisToNextRefreshView = |
| WIFI_REFRESH_INTERVAL_CAP_MILLIS - now + mLastWifiRefresh; |
| mHandler.removeCallbacks(mRefreshViewRunnable); |
| mHandler.postDelayed(mRefreshViewRunnable, millisToNextRefreshView); |
| } |
| |
| /** |
| * Wifi network configuration has changed and an immediate refresh of the list of Wifi |
| * networks is required. |
| */ |
| public void onWifiListInvalidated() { |
| mLastWifiRefresh = 0; |
| onWifiListChanged(); |
| } |
| |
| /** |
| * Create a list of available Wifi networks sorted by connection status (a connected Wifi |
| * network is shown at the first position on the list) and signal strength, with the |
| * provisio that the wireless network with SSID "mustHave" should be included in the list |
| * even if it would be otherwise excluded. |
| * |
| * @param top3EntriesOnly Show only 3 entries in the list. |
| * @param mustHave Include this wifi network in the list even if it would otherwise |
| * be excluded by virtue of inadequate signal strength. |
| */ |
| private Layout initAvailableWifiNetworks(boolean top3EntriesOnly, String mustHave) { |
| List<ScanResult> networks = mConnectivityListener.getAvailableNetworks(); |
| Layout layout = new Layout(); |
| if (networks.size() > 0) { |
| int maxItems = top3EntriesOnly ? 3 : Integer.MAX_VALUE; |
| // "networks" is already sorted by the signal strength and connection status. |
| // Generate a new list with size less than "maxItems" that ensures "mustHave" is |
| // included. |
| boolean haveMustHave = false; |
| List<ScanResult> displayList = new ArrayList<ScanResult>(); |
| for (ScanResult scanResult : networks) { |
| if (!haveMustHave && TextUtils.equals(scanResult.SSID, mustHave)) { |
| haveMustHave = true; |
| if (displayList.size() == maxItems) { |
| displayList.remove(maxItems-1); |
| } |
| displayList.add(scanResult); |
| } else if (displayList.size() < maxItems) { |
| displayList.add(scanResult); |
| } |
| if (haveMustHave && displayList.size() == maxItems) { |
| break; |
| } |
| } |
| |
| // If a network is connected, it will be the first on the list. |
| boolean isConnected = |
| mConnectivityListener.getConnectivityStatus().isWifiConnected(); |
| for (ScanResult network : displayList) { |
| if (network != null) { |
| WifiSecurity security = WifiSecurity.getSecurity(network); |
| |
| String networkDescription = |
| security.isOpen() ? "" : security.getName(mContext); |
| Intent intent = |
| WifiConnectionActivity.createIntent(mContext, network, security); |
| int signalLevel = WifiManager.calculateSignalLevel( |
| network.level, NUMBER_SIGNAL_LEVELS); |
| //TODO implement signal dependent list icon. |
| /* |
| int imageResourceId = getNetworkIconResourceId(network, signalLevel); |
| if (WifiConfigHelper.areSameNetwork(mWifiManager, network, |
| currentConnection)) { |
| networkDescription = getString(R.string.connected); |
| signalLevel = WifiManager.calculateSignalLevel( |
| currentConnection.getRssi(), NUMBER_SIGNAL_LEVELS); |
| imageResourceId = getCurrentNetworkIconResourceId(network, signalLevel); |
| } */ |
| |
| if (isConnected) { |
| addWifiConnectedHeader(layout, network.SSID); |
| } else { |
| layout.add(new Action.Builder(mRes, intent) |
| .title(network.SSID) |
| .description(networkDescription).build()); |
| } |
| } |
| isConnected = false; |
| } |
| } else { |
| layout.add(new Action.Builder(mRes, 0) |
| .title(R.string.title_wifi_no_networks_available).build()); |
| } |
| return layout; |
| } |
| }; |
| |
| private final WifiListLayout mWifiShortListLayout = new WifiListLayout(true); |
| |
| private final WifiListLayout mWifiAllListLayout = new WifiListLayout(false); |
| |
| @Override |
| public Layout createLayout() { |
| return |
| new Layout() |
| .breadcrumb(getString(R.string.header_category_device)) |
| .add(new Header.Builder(mRes) |
| .icon(R.drawable.ic_settings_wifi_4) |
| .title(R.string.connectivity_network) |
| .description(mWifiConnectedDescription) |
| .build() |
| .add(new Header.Builder(mRes).title(R.string.connectivity_wifi) |
| .contentIconRes(R.drawable.ic_settings_wifi_4) |
| .description(mWifiConnectedDescription).build() |
| .add(new Static.Builder(mRes) |
| .title(R.string.wifi_setting_available_networks) |
| .build()) |
| .add(mWifiShortListLayout) |
| .add(new Header.Builder(mRes) |
| .title(R.string.wifi_setting_see_all) |
| .build() |
| .add(mWifiAllListLayout) |
| ) |
| .add(new Static.Builder(mRes) |
| .title(R.string.wifi_setting_header_other_options) |
| .build()) |
| .add(new Action.Builder(mRes, |
| new Intent(this, WpsConnectionActivity.class)) |
| .title(R.string.wifi_setting_other_options_wps) |
| .build()) |
| .add(new Action.Builder(mRes, |
| new Intent(this, AddWifiNetworkActivity.class)) |
| .title(R.string.wifi_setting_other_options_add_network) |
| .build()) |
| ) |
| .add(new Header.Builder(mRes) |
| .title(R.string.connectivity_ethernet) |
| .contentIconRes(R.drawable.ic_settings_ethernet) |
| .description(mEthernetConnectedDescription) |
| .build() |
| .add(mEthernetLayout) |
| ) |
| ); |
| } |
| |
| @Override |
| protected void onActivityResult(int requestCode, int resultCode, Intent data) { |
| if (requestCode == REQUEST_CODE_ADVANCED_OPTIONS && resultCode == RESULT_OK) { |
| //TODO make sure view reflects model deltas |
| } else { |
| super.onActivityResult(requestCode, resultCode, data); |
| } |
| } |
| |
| @Override |
| public void onActionFocused(Layout.LayoutRow item) { |
| int resId = item.getContentIconRes(); |
| if (resId != 0) { |
| setIcon(resId); |
| } |
| } |
| |
| @Override |
| public void onActionClicked(Action action) { |
| switch (action.getId()) { |
| case Action.ACTION_INTENT: |
| startActivityForResult(action.getIntent(), REQUEST_CODE_ADVANCED_OPTIONS); |
| break; |
| case ACTION_WIFI_FORGET_NETWORK: |
| mConnectivityListener.forgetWifiNetwork(); |
| goBackToTitle(mRes.getString(R.string.connectivity_wifi)); |
| mWifiShortListLayout.onWifiListInvalidated(); |
| mWifiAllListLayout.onWifiListInvalidated(); |
| break; |
| case ACTION_WIFI_PROXY_SETTINGS: { |
| int networkId = mConnectivityListener.getWifiNetworkId(); |
| if (networkId != -1) { |
| startActivityForResult(EditProxySettingsActivity.createIntent(this, networkId), |
| REQUEST_CODE_ADVANCED_OPTIONS); |
| } |
| break; |
| } |
| case ACTION_WIFI_IP_SETTINGS: { |
| int networkId = mConnectivityListener.getWifiNetworkId(); |
| if (networkId != -1) { |
| startActivityForResult(EditIpSettingsActivity.createIntent(this, networkId), |
| REQUEST_CODE_ADVANCED_OPTIONS); |
| } |
| break; |
| } |
| case ACTION_ETHERNET_PROXY_SETTINGS: { |
| int networkId = WifiConfiguration.INVALID_NETWORK_ID; |
| startActivityForResult(EditProxySettingsActivity.createIntent(this, networkId), |
| REQUEST_CODE_ADVANCED_OPTIONS); |
| break; |
| } |
| case ACTION_ETHERNET_IP_SETTINGS: { |
| int networkId = WifiConfiguration.INVALID_NETWORK_ID; |
| startActivityForResult(EditIpSettingsActivity.createIntent(this, networkId), |
| REQUEST_CODE_ADVANCED_OPTIONS); |
| break; |
| } |
| } |
| } |
| } |