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 17 package com.android.tv.settings.connectivity; 18 19 import android.content.Context; 20 import android.content.Intent; 21 import android.content.res.Resources; 22 import android.net.wifi.ScanResult; 23 import android.net.wifi.WifiInfo; 24 import android.net.wifi.WifiManager; 25 import android.os.Handler; 26 import android.os.Message; 27 import android.support.v17.leanback.widget.ArrayObjectAdapter; 28 import android.support.v17.leanback.widget.HeaderItem; 29 import android.support.v17.leanback.widget.ObjectAdapter; 30 import android.support.v17.leanback.widget.ListRow; 31 import android.text.TextUtils; 32 import android.util.Log; 33 import android.util.Pair; 34 import android.util.SparseArray; 35 36 import com.android.tv.settings.BrowseInfoBase; 37 import com.android.tv.settings.MenuItem; 38 import com.android.tv.settings.R; 39 40 import java.util.ArrayList; 41 import java.util.Collections; 42 import java.util.HashMap; 43 import java.util.List; 44 45 /** 46 * Gets the list of browse headers and browse items. 47 */ 48 public class WifiNetworksBrowseInfo extends BrowseInfoBase { 49 50 private static final String TAG = "WifiNetworksBrowseInfo"; 51 private static final boolean DEBUG = false; 52 53 private static final int FIRST_HEADER_ID = 1; 54 private static final int SECOND_HEADER_ID = 2; 55 private static final int OTHER_OPTIONS_WPS = 0; 56 private static final int OTHER_OPTIONS_ADD_NETWORK = 1; 57 private static final int MSG_NETWORK_REFRESH = 1; 58 private static final int NETWORK_REFRESH_TIMEOUT = 15000; 59 private static final int NUMBER_SIGNAL_LEVELS = 4; 60 61 private final Context mContext; 62 private final Handler mHandler; 63 private final WifiManager mWifiManager; 64 private int mNextItemId; 65 private final HashMap<Pair<String, WifiSecurity>, Integer> mIdToSsidMap; 66 67 WifiNetworksBrowseInfo(Context context) { 68 mContext = context; 69 mWifiManager = (WifiManager) context.getSystemService(Context.WIFI_SERVICE); 70 mHandler = new Handler() { 71 @Override 72 public void handleMessage(Message msg) { 73 refreshAvailableNetworksBrowseItems(); 74 mHandler.sendEmptyMessageDelayed(MSG_NETWORK_REFRESH, NETWORK_REFRESH_TIMEOUT); 75 } 76 }; 77 mIdToSsidMap = new HashMap<Pair<String, WifiSecurity>, Integer>(); 78 mNextItemId = 0; 79 mRows.put(FIRST_HEADER_ID, new ArrayObjectAdapter()); 80 mRows.put(SECOND_HEADER_ID, new ArrayObjectAdapter()); 81 } 82 83 @Override 84 public void refreshContent() { 85 init(); 86 } 87 88 public void onShutdown() { 89 stopScanning(); 90 mIdToSsidMap.clear(); 91 } 92 93 void init() { 94 mHeaderItems.clear(); 95 String header1 = mContext.getString(R.string.wifi_setting_header_available_networks); 96 String header2 = mContext.getString(R.string.wifi_setting_header_other_options); 97 addBrowseHeader(FIRST_HEADER_ID, header1); 98 addBrowseHeader(SECOND_HEADER_ID, header2); 99 100 ArrayObjectAdapter row = mRows.get(FIRST_HEADER_ID); 101 initAvailableNetworksBrowseItems(row); 102 initOtherOptionsBrowseItems(); 103 } 104 105 protected void startScanning() { 106 mHandler.sendEmptyMessage(MSG_NETWORK_REFRESH); 107 } 108 109 protected void stopScanning() { 110 mHandler.removeMessages(MSG_NETWORK_REFRESH); 111 } 112 113 private void addBrowseHeader(int id, String name) { 114 mHeaderItems.add(new HeaderItem(id, name, null)); 115 } 116 117 private void initAvailableNetworksBrowseItems(ArrayObjectAdapter row) { 118 WifiInfo currentConnection = mWifiManager.getConnectionInfo(); 119 List<ScanResult> networks = getAvailableNetworks(currentConnection); 120 121 if (networks.size() > 0) { 122 for (ScanResult network : networks) { 123 if (network != null) { 124 WifiSecurity security = WifiSecurity.getSecurity(network); 125 126 String networkDescription = security.isOpen() ? "" : security.getName(mContext); 127 Intent intent = 128 WifiConnectionActivity.createIntent(mContext, network, security); 129 int signalLevel = WifiManager.calculateSignalLevel( 130 network.level, NUMBER_SIGNAL_LEVELS); 131 int imageResourceId = getNetworkIconResourceId( 132 network, signalLevel); 133 if (WifiConfigHelper.areSameNetwork(mWifiManager, network, currentConnection)) { 134 networkDescription = mContext.getString(R.string.connected); 135 intent = getConnectedNetworkIntent(); 136 signalLevel = WifiManager.calculateSignalLevel( 137 currentConnection.getRssi(), NUMBER_SIGNAL_LEVELS); 138 imageResourceId = getCurrentNetworkIconResourceId(network, signalLevel); 139 } 140 141 Integer itemId = getItemId(network, security); 142 143 if (DEBUG) { 144 Log.d(TAG, "Network " + itemId + " has SSID=" + network.SSID + 145 ", BSSID=" + network.BSSID + 146 ", current connected SSID=" + currentConnection.getSSID() + 147 ", current connected BSSID=" + currentConnection.getBSSID()); 148 } 149 150 row.add(new MenuItem.Builder().id(itemId).title(network.SSID) 151 .description(networkDescription) 152 .imageResourceId(mContext, imageResourceId).intent(intent) 153 .build()); 154 } 155 } 156 } else { 157 row.add(new MenuItem.Builder().id(generateNextItemId()) 158 .title(mContext.getString(R.string.title_wifi_no_networks_available)) 159 .imageResourceId(mContext, R.drawable.ic_settings_wifi_scan).build()); 160 } 161 } 162 163 private Integer getItemId(ScanResult network, WifiSecurity security) { 164 Pair<String, WifiSecurity> key = Pair.create(network.SSID, security); 165 Integer itemId = mIdToSsidMap.get(key); 166 if (itemId == null) { 167 itemId = generateNextItemId(); 168 mIdToSsidMap.put(key, itemId); 169 } 170 return itemId; 171 } 172 173 private int generateNextItemId() { 174 int id = mNextItemId; 175 mNextItemId++; 176 return id; 177 } 178 179 private Intent getConnectedNetworkIntent() { 180 return new Intent(mContext, WifiConfigurationActivity.class); 181 } 182 183 private void initOtherOptionsBrowseItems() { 184 String wpsName = mContext.getString(R.string.wifi_setting_other_options_wps); 185 String addNetworkName = mContext.getString(R.string.wifi_setting_other_options_add_network); 186 ArrayObjectAdapter row = mRows.get(SECOND_HEADER_ID); 187 188 addOtherOptionBrowseItem(row, OTHER_OPTIONS_WPS, wpsName); 189 addOtherOptionBrowseItem(row, OTHER_OPTIONS_ADD_NETWORK, addNetworkName); 190 } 191 192 private void addOtherOptionBrowseItem(ArrayObjectAdapter row, int id, String name) { 193 row.add(new MenuItem.Builder().id(id).title(name) 194 .imageResourceId(mContext, getOtherOptionsIconResourceId(id)) 195 .intent(getOtherOptionsIntent(id)).build()); 196 } 197 198 private Intent getOtherOptionsIntent(int otherOptionIndex) { 199 Intent intent = null; 200 switch (otherOptionIndex) { 201 case OTHER_OPTIONS_WPS: 202 intent = new Intent(mContext, WpsConnectionActivity.class); 203 break; 204 case OTHER_OPTIONS_ADD_NETWORK: 205 intent = new Intent(mContext, AddWifiNetworkActivity.class); 206 break; 207 default: 208 Log.d(TAG, "Unknown otherOptionIndex: " + otherOptionIndex); 209 break; 210 } 211 return intent; 212 } 213 214 private int getCurrentNetworkIconResourceId( 215 ScanResult scanResult, int signalLevel) { 216 int resourceId = 0; 217 if (scanResult != null) { 218 WifiSecurity security = WifiSecurity.getSecurity(scanResult); 219 if (security.isOpen()) { 220 switch (signalLevel) 221 { 222 case 0: 223 resourceId = R.drawable.ic_settings_wifi_active_1; 224 break; 225 case 1: 226 resourceId = R.drawable.ic_settings_wifi_active_2; 227 break; 228 case 2: 229 resourceId = R.drawable.ic_settings_wifi_active_3; 230 break; 231 case 3: 232 resourceId = R.drawable.ic_settings_wifi_active_4; 233 break; 234 } 235 } else { 236 switch (signalLevel) 237 { 238 case 0: 239 resourceId = R.drawable.ic_settings_wifi_secure_active_1; 240 break; 241 case 1: 242 resourceId = R.drawable.ic_settings_wifi_secure_active_2; 243 break; 244 case 2: 245 resourceId = R.drawable.ic_settings_wifi_secure_active_3; 246 break; 247 case 3: 248 resourceId = R.drawable.ic_settings_wifi_secure_active_4; 249 break; 250 } 251 } 252 } 253 return resourceId; 254 } 255 256 private int getNetworkIconResourceId(ScanResult scanResult, int signalLevel) { 257 int resourceId = 0; 258 if (scanResult != null) { 259 WifiSecurity security = WifiSecurity.getSecurity(scanResult); 260 if (security.isOpen()) { 261 switch (signalLevel) 262 { 263 case 0: 264 resourceId = R.drawable.ic_settings_wifi_1; 265 break; 266 case 1: 267 resourceId = R.drawable.ic_settings_wifi_2; 268 break; 269 case 2: 270 resourceId = R.drawable.ic_settings_wifi_3; 271 break; 272 case 3: 273 resourceId = R.drawable.ic_settings_wifi_4; 274 break; 275 } 276 } else { 277 switch (signalLevel) 278 { 279 case 0: 280 resourceId = R.drawable.ic_settings_wifi_secure_1; 281 break; 282 case 1: 283 resourceId = R.drawable.ic_settings_wifi_secure_2; 284 break; 285 case 2: 286 resourceId = R.drawable.ic_settings_wifi_secure_3; 287 break; 288 case 3: 289 resourceId = R.drawable.ic_settings_wifi_secure_4; 290 break; 291 } 292 } 293 } 294 return resourceId; 295 } 296 297 private List<ScanResult> getAvailableNetworks(WifiInfo connectedWifiInfo) { 298 String currentConnectedSSID = connectedWifiInfo == null ? "" : connectedWifiInfo.getSSID(); 299 currentConnectedSSID = WifiInfo.removeDoubleQuotes(currentConnectedSSID); 300 WifiSecurity currentConnectedSecurity = WifiConfigHelper.getCurrentConnectionSecurity( 301 mWifiManager, connectedWifiInfo); 302 303 // TODO : Refactor with similar code in SelectFromListWizard 304 final List<ScanResult> results = mWifiManager.getScanResults(); 305 306 if (results.size() == 0) { 307 Log.w(TAG, "No results found! Initiate scan..."); 308 mWifiManager.startScan(); 309 } 310 311 final HashMap<Pair<String, WifiSecurity>, ScanResult> consolidatedScanResults = 312 new HashMap<Pair<String, WifiSecurity>, ScanResult>(); 313 HashMap<Pair<String, WifiSecurity>, Boolean> specialNetworks = new HashMap< 314 Pair<String, WifiSecurity>, Boolean>(); 315 for (ScanResult result : results) { 316 if (TextUtils.isEmpty(result.SSID)) { 317 continue; 318 } 319 320 Pair<String, WifiSecurity> key = Pair.create( 321 result.SSID, WifiSecurity.getSecurity(result)); 322 ScanResult existing = consolidatedScanResults.get(key); 323 324 if (WifiConfigHelper.areSameNetwork(mWifiManager, result, connectedWifiInfo)) { 325 // The currently connected network should always be included. 326 consolidatedScanResults.put(key, result); 327 specialNetworks.put(key, true); 328 } else { 329 if (existing == null || 330 (!specialNetworks.containsKey(key) && existing.level < result.level)) { 331 consolidatedScanResults.put(key, result); 332 } 333 } 334 } 335 336 ArrayList<ScanResult> networkList = new ArrayList<ScanResult>(); 337 networkList.addAll(consolidatedScanResults.values()); 338 ScanResultComparator comparator = connectedWifiInfo == null ? new ScanResultComparator() : 339 new ScanResultComparator(currentConnectedSSID, currentConnectedSecurity); 340 Collections.sort(networkList, comparator); 341 return networkList; 342 } 343 344 private int getOtherOptionsIconResourceId(int otherOptionIndex) { 345 int resourceId = 0; 346 switch (otherOptionIndex) { 347 case OTHER_OPTIONS_WPS: 348 resourceId = R.drawable.ic_settings_wifi_wps; 349 break; 350 case OTHER_OPTIONS_ADD_NETWORK: 351 resourceId = R.drawable.ic_settings_add; 352 break; 353 } 354 return resourceId; 355 } 356 357 private void refreshAvailableNetworksBrowseItems() { 358 ArrayObjectAdapter row = mRows.get(FIRST_HEADER_ID); 359 row.clear(); 360 initAvailableNetworksBrowseItems(row); 361 } 362 } 363