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.systemui.qs.tiles; 18 19 import android.content.Context; 20 import android.content.Intent; 21 import android.content.pm.PackageManager; 22 import android.content.res.Resources; 23 import android.provider.Settings; 24 import android.util.Log; 25 import android.view.View; 26 import android.view.ViewGroup; 27 import android.widget.Button; 28 import android.widget.Switch; 29 30 import com.android.internal.logging.MetricsLogger; 31 import com.android.internal.logging.MetricsProto.MetricsEvent; 32 import com.android.settingslib.wifi.AccessPoint; 33 import com.android.systemui.R; 34 import com.android.systemui.qs.QSDetailItems; 35 import com.android.systemui.qs.QSDetailItems.Item; 36 import com.android.systemui.qs.QSIconView; 37 import com.android.systemui.qs.QSTile; 38 import com.android.systemui.qs.SignalTileView; 39 import com.android.systemui.statusbar.policy.NetworkController; 40 import com.android.systemui.statusbar.policy.NetworkController.AccessPointController; 41 import com.android.systemui.statusbar.policy.NetworkController.IconState; 42 import com.android.systemui.statusbar.policy.SignalCallbackAdapter; 43 44 import java.util.List; 45 46 /** Quick settings tile: Wifi **/ 47 public class WifiTile extends QSTile<QSTile.SignalState> { 48 private static final Intent WIFI_SETTINGS = new Intent(Settings.ACTION_WIFI_SETTINGS); 49 50 private final NetworkController mController; 51 private final AccessPointController mWifiController; 52 private final WifiDetailAdapter mDetailAdapter; 53 private final QSTile.SignalState mStateBeforeClick = newTileState(); 54 55 protected final WifiSignalCallback mSignalCallback = new WifiSignalCallback(); 56 57 public WifiTile(Host host) { 58 super(host); 59 mController = host.getNetworkController(); 60 mWifiController = mController.getAccessPointController(); 61 mDetailAdapter = new WifiDetailAdapter(); 62 } 63 64 @Override 65 public SignalState newTileState() { 66 return new SignalState(); 67 } 68 69 @Override 70 public void setListening(boolean listening) { 71 if (listening) { 72 mController.addSignalCallback(mSignalCallback); 73 } else { 74 mController.removeSignalCallback(mSignalCallback); 75 } 76 } 77 78 @Override 79 public void setDetailListening(boolean listening) { 80 if (listening) { 81 mWifiController.addAccessPointCallback(mDetailAdapter); 82 } else { 83 mWifiController.removeAccessPointCallback(mDetailAdapter); 84 } 85 } 86 87 @Override 88 public DetailAdapter getDetailAdapter() { 89 return mDetailAdapter; 90 } 91 92 @Override 93 public QSIconView createTileView(Context context) { 94 return new SignalTileView(context); 95 } 96 97 @Override 98 public Intent getLongClickIntent() { 99 return WIFI_SETTINGS; 100 } 101 102 @Override 103 protected void handleSecondaryClick() { 104 // Secondary clicks are header clicks, just toggle. 105 mState.copyTo(mStateBeforeClick); 106 MetricsLogger.action(mContext, getMetricsCategory(), !mState.value); 107 mController.setWifiEnabled(!mState.value); 108 } 109 110 @Override 111 protected void handleClick() { 112 if (!mWifiController.canConfigWifi()) { 113 mHost.startActivityDismissingKeyguard(new Intent(Settings.ACTION_WIFI_SETTINGS)); 114 return; 115 } 116 showDetail(true); 117 if (!mState.value) { 118 mController.setWifiEnabled(true); 119 mState.value = true; 120 } 121 } 122 123 @Override 124 public CharSequence getTileLabel() { 125 return mContext.getString(R.string.quick_settings_wifi_label); 126 } 127 128 @Override 129 protected void handleUpdateState(SignalState state, Object arg) { 130 if (DEBUG) Log.d(TAG, "handleUpdateState arg=" + arg); 131 CallbackInfo cb = (CallbackInfo) arg; 132 if (cb == null) { 133 cb = mSignalCallback.mInfo; 134 } 135 136 boolean wifiConnected = cb.enabled && (cb.wifiSignalIconId > 0) && (cb.enabledDesc != null); 137 boolean wifiNotConnected = (cb.wifiSignalIconId > 0) && (cb.enabledDesc == null); 138 boolean enabledChanging = state.value != cb.enabled; 139 if (enabledChanging) { 140 mDetailAdapter.setItemsVisible(cb.enabled); 141 fireToggleStateChanged(cb.enabled); 142 } 143 state.value = cb.enabled; 144 state.connected = wifiConnected; 145 state.activityIn = cb.enabled && cb.activityIn; 146 state.activityOut = cb.enabled && cb.activityOut; 147 state.filter = true; 148 final StringBuffer minimalContentDescription = new StringBuffer(); 149 final StringBuffer expandedContentDescription = new StringBuffer(); 150 final Resources r = mContext.getResources(); 151 if (!state.value) { 152 state.icon = ResourceIcon.get(R.drawable.ic_qs_wifi_disabled); 153 state.label = r.getString(R.string.quick_settings_wifi_label); 154 } else if (wifiConnected) { 155 state.icon = ResourceIcon.get(cb.wifiSignalIconId); 156 state.label = removeDoubleQuotes(cb.enabledDesc); 157 } else if (wifiNotConnected) { 158 state.icon = ResourceIcon.get(R.drawable.ic_qs_wifi_disconnected); 159 state.label = r.getString(R.string.quick_settings_wifi_label); 160 } else { 161 state.icon = ResourceIcon.get(R.drawable.ic_qs_wifi_no_network); 162 state.label = r.getString(R.string.quick_settings_wifi_label); 163 } 164 minimalContentDescription.append( 165 mContext.getString(R.string.quick_settings_wifi_label)).append(","); 166 if (state.value) { 167 expandedContentDescription.append( 168 r.getString(R.string.quick_settings_wifi_on_label)).append(","); 169 if (wifiConnected) { 170 minimalContentDescription.append(cb.wifiSignalContentDescription).append(","); 171 minimalContentDescription.append(removeDoubleQuotes(cb.enabledDesc)); 172 expandedContentDescription.append(cb.wifiSignalContentDescription).append(","); 173 expandedContentDescription.append(removeDoubleQuotes(cb.enabledDesc)); 174 } 175 } else { 176 expandedContentDescription.append( 177 r.getString(R.string.quick_settings_wifi_off_label)); 178 } 179 state.minimalContentDescription = minimalContentDescription; 180 expandedContentDescription.append(",").append( 181 r.getString(R.string.accessibility_quick_settings_open_settings, getTileLabel())); 182 state.contentDescription = expandedContentDescription; 183 CharSequence wifiName = state.label; 184 if (state.connected) { 185 wifiName = r.getString(R.string.accessibility_wifi_name, state.label); 186 } 187 state.dualLabelContentDescription = wifiName; 188 state.expandedAccessibilityClassName = Button.class.getName(); 189 state.minimalAccessibilityClassName = Switch.class.getName(); 190 } 191 192 @Override 193 public int getMetricsCategory() { 194 return MetricsEvent.QS_WIFI; 195 } 196 197 @Override 198 protected boolean shouldAnnouncementBeDelayed() { 199 return mStateBeforeClick.value == mState.value; 200 } 201 202 @Override 203 protected String composeChangeAnnouncement() { 204 if (mState.value) { 205 return mContext.getString(R.string.accessibility_quick_settings_wifi_changed_on); 206 } else { 207 return mContext.getString(R.string.accessibility_quick_settings_wifi_changed_off); 208 } 209 } 210 211 @Override 212 public boolean isAvailable() { 213 return mContext.getPackageManager().hasSystemFeature(PackageManager.FEATURE_WIFI); 214 } 215 216 private static String removeDoubleQuotes(String string) { 217 if (string == null) return null; 218 final int length = string.length(); 219 if ((length > 1) && (string.charAt(0) == '"') && (string.charAt(length - 1) == '"')) { 220 return string.substring(1, length - 1); 221 } 222 return string; 223 } 224 225 protected static final class CallbackInfo { 226 boolean enabled; 227 boolean connected; 228 int wifiSignalIconId; 229 String enabledDesc; 230 boolean activityIn; 231 boolean activityOut; 232 String wifiSignalContentDescription; 233 234 @Override 235 public String toString() { 236 return new StringBuilder("CallbackInfo[") 237 .append("enabled=").append(enabled) 238 .append(",connected=").append(connected) 239 .append(",wifiSignalIconId=").append(wifiSignalIconId) 240 .append(",enabledDesc=").append(enabledDesc) 241 .append(",activityIn=").append(activityIn) 242 .append(",activityOut=").append(activityOut) 243 .append(",wifiSignalContentDescription=").append(wifiSignalContentDescription) 244 .append(']').toString(); 245 } 246 } 247 248 protected final class WifiSignalCallback extends SignalCallbackAdapter { 249 final CallbackInfo mInfo = new CallbackInfo(); 250 251 @Override 252 public void setWifiIndicators(boolean enabled, IconState statusIcon, IconState qsIcon, 253 boolean activityIn, boolean activityOut, String description) { 254 if (DEBUG) Log.d(TAG, "onWifiSignalChanged enabled=" + enabled); 255 mInfo.enabled = enabled; 256 mInfo.connected = qsIcon.visible; 257 mInfo.wifiSignalIconId = qsIcon.icon; 258 mInfo.enabledDesc = description; 259 mInfo.activityIn = activityIn; 260 mInfo.activityOut = activityOut; 261 mInfo.wifiSignalContentDescription = qsIcon.contentDescription; 262 refreshState(mInfo); 263 } 264 }; 265 266 private final class WifiDetailAdapter implements DetailAdapter, 267 NetworkController.AccessPointController.AccessPointCallback, QSDetailItems.Callback { 268 269 private QSDetailItems mItems; 270 private AccessPoint[] mAccessPoints; 271 272 @Override 273 public CharSequence getTitle() { 274 return mContext.getString(R.string.quick_settings_wifi_label); 275 } 276 277 public Intent getSettingsIntent() { 278 return WIFI_SETTINGS; 279 } 280 281 @Override 282 public Boolean getToggleState() { 283 return mState.value; 284 } 285 286 @Override 287 public void setToggleState(boolean state) { 288 if (DEBUG) Log.d(TAG, "setToggleState " + state); 289 MetricsLogger.action(mContext, MetricsEvent.QS_WIFI_TOGGLE, state); 290 mController.setWifiEnabled(state); 291 showDetail(false); 292 } 293 294 @Override 295 public int getMetricsCategory() { 296 return MetricsEvent.QS_WIFI_DETAILS; 297 } 298 299 @Override 300 public View createDetailView(Context context, View convertView, ViewGroup parent) { 301 if (DEBUG) Log.d(TAG, "createDetailView convertView=" + (convertView != null)); 302 mAccessPoints = null; 303 mWifiController.scanForAccessPoints(); 304 fireScanStateChanged(true); 305 mItems = QSDetailItems.convertOrInflate(context, convertView, parent); 306 mItems.setTagSuffix("Wifi"); 307 mItems.setCallback(this); 308 mItems.setEmptyState(R.drawable.ic_qs_wifi_detail_empty, 309 R.string.quick_settings_wifi_detail_empty_text); 310 updateItems(); 311 setItemsVisible(mState.value); 312 return mItems; 313 } 314 315 @Override 316 public void onAccessPointsChanged(final List<AccessPoint> accessPoints) { 317 mAccessPoints = accessPoints.toArray(new AccessPoint[accessPoints.size()]); 318 updateItems(); 319 if (accessPoints != null && accessPoints.size() > 0) { 320 fireScanStateChanged(false); 321 } 322 } 323 324 @Override 325 public void onSettingsActivityTriggered(Intent settingsIntent) { 326 mHost.startActivityDismissingKeyguard(settingsIntent); 327 } 328 329 @Override 330 public void onDetailItemClick(Item item) { 331 if (item == null || item.tag == null) return; 332 final AccessPoint ap = (AccessPoint) item.tag; 333 if (!ap.isActive()) { 334 if (mWifiController.connect(ap)) { 335 mHost.collapsePanels(); 336 } 337 } 338 showDetail(false); 339 } 340 341 @Override 342 public void onDetailItemDisconnect(Item item) { 343 // noop 344 } 345 346 public void setItemsVisible(boolean visible) { 347 if (mItems == null) return; 348 mItems.setItemsVisible(visible); 349 } 350 351 private void updateItems() { 352 if (mItems == null) return; 353 Item[] items = null; 354 if (mAccessPoints != null) { 355 items = new Item[mAccessPoints.length]; 356 for (int i = 0; i < mAccessPoints.length; i++) { 357 final AccessPoint ap = mAccessPoints[i]; 358 final Item item = new Item(); 359 item.tag = ap; 360 item.icon = mWifiController.getIcon(ap); 361 item.line1 = ap.getSsid(); 362 item.line2 = ap.isActive() ? ap.getSummary() : null; 363 item.overlay = ap.getSecurity() != AccessPoint.SECURITY_NONE 364 ? mContext.getDrawable(R.drawable.qs_ic_wifi_lock) 365 : null; 366 items[i] = item; 367 } 368 } 369 mItems.setItems(items); 370 } 371 } 372 } 373