blob: 0ecb8be627f6cf058fdc2cedefcb311d2fada49a [file] [log] [blame]
Tony Mantler98608e42016-01-22 14:58:48 -08001/*
2 * Copyright (C) 2016 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 */
Tony Mantler98608e42016-01-22 14:58:48 -080016package com.android.settingslib.wifi;
17
Eric Schwarzenbach6efd09a2017-08-22 10:33:00 -070018import android.annotation.Nullable;
Tony Mantler98608e42016-01-22 14:58:48 -080019import android.content.Context;
20import android.content.pm.PackageManager;
Stephen Chen2c096592017-01-18 15:25:31 -080021import android.content.res.Resources;
22import android.content.res.TypedArray;
Tony Mantler98608e42016-01-22 14:58:48 -080023import android.graphics.drawable.Drawable;
24import android.graphics.drawable.StateListDrawable;
25import android.net.wifi.WifiConfiguration;
26import android.os.Looper;
27import android.os.UserHandle;
Fan Zhang60b850d2017-07-18 15:59:06 -070028import android.support.annotation.VisibleForTesting;
Tony Mantler98608e42016-01-22 14:58:48 -080029import android.support.v7.preference.Preference;
30import android.support.v7.preference.PreferenceViewHolder;
31import android.text.TextUtils;
32import android.util.AttributeSet;
33import android.util.SparseArray;
Stephen Chen2c096592017-01-18 15:25:31 -080034import android.widget.ImageView;
Tony Mantler98608e42016-01-22 14:58:48 -080035import android.widget.TextView;
Sundeep Ghumanbf339f52017-01-23 15:41:43 -080036
Tony Mantler98608e42016-01-22 14:58:48 -080037import com.android.settingslib.R;
Sundeep Ghumanbf339f52017-01-23 15:41:43 -080038import com.android.settingslib.TronUtils;
Sundeep Ghumanf84e0572017-01-10 13:43:03 -080039import com.android.settingslib.Utils;
Eric Schwarzenbach44268e32017-08-04 16:38:07 -070040import com.android.settingslib.wifi.AccessPoint.Speed;
Tony Mantler98608e42016-01-22 14:58:48 -080041
42public class AccessPointPreference extends Preference {
43
44 private static final int[] STATE_SECURED = {
45 R.attr.state_encrypted
46 };
Tony Mantler98608e42016-01-22 14:58:48 -080047
Stephen Chenaed318e2017-03-31 16:58:38 -070048 private static final int[] STATE_METERED = {
49 R.attr.state_metered
50 };
51
Fan Zhang60b850d2017-07-18 15:59:06 -070052 private static final int[] FRICTION_ATTRS = {
53 R.attr.wifi_friction
54 };
55
56 private static final int[] WIFI_CONNECTION_STRENGTH = {
57 R.string.accessibility_no_wifi,
58 R.string.accessibility_wifi_one_bar,
59 R.string.accessibility_wifi_two_bars,
60 R.string.accessibility_wifi_three_bars,
61 R.string.accessibility_wifi_signal_full
62 };
Tony Mantler98608e42016-01-22 14:58:48 -080063
Eric Schwarzenbach6efd09a2017-08-22 10:33:00 -070064 @Nullable private final StateListDrawable mFrictionSld;
Tony Mantler98608e42016-01-22 14:58:48 -080065 private final int mBadgePadding;
66 private final UserBadgeCache mBadgeCache;
Eric Schwarzenbach6efd09a2017-08-22 10:33:00 -070067 private final IconInjector mIconInjector;
Tony Mantler98608e42016-01-22 14:58:48 -080068 private TextView mTitleView;
Salvador Martinez45a15942016-08-11 17:14:11 -070069
Tony Mantler98608e42016-01-22 14:58:48 -080070 private boolean mForSavedNetworks = false;
71 private AccessPoint mAccessPoint;
72 private Drawable mBadge;
73 private int mLevel;
74 private CharSequence mContentDescription;
Salvador Martinez45a15942016-08-11 17:14:11 -070075 private int mDefaultIconResId;
Eric Schwarzenbach44268e32017-08-04 16:38:07 -070076 private int mWifiSpeed = Speed.NONE;
Tony Mantler98608e42016-01-22 14:58:48 -080077
Sundeep Ghuman7c32aa32017-06-26 17:13:00 -070078 public static String generatePreferenceKey(AccessPoint accessPoint) {
79 StringBuilder builder = new StringBuilder();
80
81 if (TextUtils.isEmpty(accessPoint.getBssid())) {
82 builder.append(accessPoint.getSsidStr());
83 } else {
84 builder.append(accessPoint.getBssid());
85 }
86
87 builder.append(',').append(accessPoint.getSecurity());
88 return builder.toString();
89 }
90
Eric Schwarzenbach6efd09a2017-08-22 10:33:00 -070091 @Nullable
92 private static StateListDrawable getFrictionStateListDrawable(Context context) {
93 TypedArray frictionSld;
94 try {
95 frictionSld = context.getTheme().obtainStyledAttributes(FRICTION_ATTRS);
96 } catch (Resources.NotFoundException e) {
97 // Fallback for platforms that do not need friction icon resources.
98 frictionSld = null;
99 }
100 return frictionSld != null ? (StateListDrawable) frictionSld.getDrawable(0) : null;
101 }
102
Tony Mantler98608e42016-01-22 14:58:48 -0800103 // Used for dummy pref.
104 public AccessPointPreference(Context context, AttributeSet attrs) {
105 super(context, attrs);
Stephen Chen2c096592017-01-18 15:25:31 -0800106 mFrictionSld = null;
Tony Mantler98608e42016-01-22 14:58:48 -0800107 mBadgePadding = 0;
108 mBadgeCache = null;
Eric Schwarzenbach6efd09a2017-08-22 10:33:00 -0700109 mIconInjector = new IconInjector(context);
Tony Mantler98608e42016-01-22 14:58:48 -0800110 }
111
112 public AccessPointPreference(AccessPoint accessPoint, Context context, UserBadgeCache cache,
113 boolean forSavedNetworks) {
Eric Schwarzenbach6efd09a2017-08-22 10:33:00 -0700114 this(accessPoint, context, cache, 0 /* iconResId */, forSavedNetworks);
Tony Mantler98608e42016-01-22 14:58:48 -0800115 refresh();
116 }
117
Salvador Martinez45a15942016-08-11 17:14:11 -0700118 public AccessPointPreference(AccessPoint accessPoint, Context context, UserBadgeCache cache,
119 int iconResId, boolean forSavedNetworks) {
Eric Schwarzenbach6efd09a2017-08-22 10:33:00 -0700120 this(accessPoint, context, cache, iconResId, forSavedNetworks,
121 getFrictionStateListDrawable(context), -1 /* level */, new IconInjector(context));
122 }
123
124 @VisibleForTesting
125 AccessPointPreference(AccessPoint accessPoint, Context context, UserBadgeCache cache,
126 int iconResId, boolean forSavedNetworks, StateListDrawable frictionSld,
127 int level, IconInjector iconInjector) {
Salvador Martinez45a15942016-08-11 17:14:11 -0700128 super(context);
Stephen Chen2c096592017-01-18 15:25:31 -0800129 setWidgetLayoutResource(R.layout.access_point_friction_widget);
Salvador Martinez45a15942016-08-11 17:14:11 -0700130 mBadgeCache = cache;
131 mAccessPoint = accessPoint;
132 mForSavedNetworks = forSavedNetworks;
133 mAccessPoint.setTag(this);
Eric Schwarzenbach6efd09a2017-08-22 10:33:00 -0700134 mLevel = level;
Salvador Martinez45a15942016-08-11 17:14:11 -0700135 mDefaultIconResId = iconResId;
Eric Schwarzenbach6efd09a2017-08-22 10:33:00 -0700136 mFrictionSld = frictionSld;
137 mIconInjector = iconInjector;
Salvador Martinez45a15942016-08-11 17:14:11 -0700138 mBadgePadding = context.getResources()
139 .getDimensionPixelSize(R.dimen.wifi_preference_badge_padding);
140 }
141
Tony Mantler98608e42016-01-22 14:58:48 -0800142 public AccessPoint getAccessPoint() {
143 return mAccessPoint;
144 }
145
146 @Override
147 public void onBindViewHolder(final PreferenceViewHolder view) {
148 super.onBindViewHolder(view);
149 if (mAccessPoint == null) {
150 // Used for dummy pref.
151 return;
152 }
153 Drawable drawable = getIcon();
154 if (drawable != null) {
155 drawable.setLevel(mLevel);
156 }
157
158 mTitleView = (TextView) view.findViewById(com.android.internal.R.id.title);
159 if (mTitleView != null) {
160 // Attach to the end of the title view
161 mTitleView.setCompoundDrawablesRelativeWithIntrinsicBounds(null, null, mBadge, null);
162 mTitleView.setCompoundDrawablePadding(mBadgePadding);
163 }
164 view.itemView.setContentDescription(mContentDescription);
Stephen Chen2c096592017-01-18 15:25:31 -0800165
Sundeep Ghuman4d8781e2017-03-15 16:03:55 -0700166 ImageView frictionImageView = (ImageView) view.findViewById(R.id.friction_icon);
167 bindFrictionImage(frictionImageView);
Tony Mantler98608e42016-01-22 14:58:48 -0800168 }
169
170 protected void updateIcon(int level, Context context) {
171 if (level == -1) {
Salvador Martinez45a15942016-08-11 17:14:11 -0700172 safeSetDefaultIcon();
Stephen Chened8f5292017-01-30 16:31:23 -0800173 return;
174 }
Sundeep Ghuman53200ed2017-06-21 16:54:36 -0700175 TronUtils.logWifiSettingsSpeed(context, mWifiSpeed);
Sundeep Ghuman271e5de2017-05-30 14:11:39 -0700176
Eric Schwarzenbach6efd09a2017-08-22 10:33:00 -0700177 Drawable drawable = mIconInjector.getIcon(level);
Stephen Chened8f5292017-01-30 16:31:23 -0800178 if (!mForSavedNetworks && drawable != null) {
Sundeep Ghuman6962c0c2017-05-08 14:27:06 -0700179 drawable.setTint(Utils.getColorAttr(context, android.R.attr.colorControlNormal));
Stephen Chened8f5292017-01-30 16:31:23 -0800180 setIcon(drawable);
Tony Mantler98608e42016-01-22 14:58:48 -0800181 } else {
Stephen Chened8f5292017-01-30 16:31:23 -0800182 safeSetDefaultIcon();
Tony Mantler98608e42016-01-22 14:58:48 -0800183 }
184 }
185
Stephen Chen2c096592017-01-18 15:25:31 -0800186 /**
187 * Binds the friction icon drawable using a StateListDrawable.
188 *
189 * <p>Friction icons will be rebound when notifyChange() is called, and therefore
190 * do not need to be managed in refresh()</p>.
191 */
192 private void bindFrictionImage(ImageView frictionImageView) {
193 if (frictionImageView == null || mFrictionSld == null) {
194 return;
195 }
196 if (mAccessPoint.getSecurity() != AccessPoint.SECURITY_NONE) {
Sundeep Ghuman4d8781e2017-03-15 16:03:55 -0700197 mFrictionSld.setState(STATE_SECURED);
Stephen Chen21f68682017-04-04 13:23:31 -0700198 } else if (mAccessPoint.isMetered()) {
Stephen Chenaed318e2017-03-31 16:58:38 -0700199 mFrictionSld.setState(STATE_METERED);
Stephen Chen2c096592017-01-18 15:25:31 -0800200 }
201 Drawable drawable = mFrictionSld.getCurrent();
202 frictionImageView.setImageDrawable(drawable);
203 }
204
Salvador Martinez45a15942016-08-11 17:14:11 -0700205 private void safeSetDefaultIcon() {
206 if (mDefaultIconResId != 0) {
207 setIcon(mDefaultIconResId);
208 } else {
209 setIcon(null);
210 }
211 }
212
Tony Mantler98608e42016-01-22 14:58:48 -0800213 protected void updateBadge(Context context) {
214 WifiConfiguration config = mAccessPoint.getConfig();
215 if (config != null) {
216 // Fetch badge (may be null)
217 // Get the badge using a cache since the PM will ask the UserManager for the list
218 // of profiles every time otherwise.
219 mBadge = mBadgeCache.getUserBadge(config.creatorUid);
220 }
221 }
222
223 /**
224 * Updates the title and summary; may indirectly call notifyChanged().
225 */
226 public void refresh() {
227 if (mForSavedNetworks) {
228 setTitle(mAccessPoint.getConfigName());
229 } else {
230 setTitle(mAccessPoint.getSsid());
231 }
232
233 final Context context = getContext();
Sundeep Ghumanaaa8a1b2017-03-13 14:40:56 -0700234 int level = mAccessPoint.getLevel();
Sundeep Ghuman55adc6b2017-06-05 16:46:59 -0700235 int wifiSpeed = mAccessPoint.getSpeed();
236 if (level != mLevel || wifiSpeed != mWifiSpeed) {
Tony Mantler98608e42016-01-22 14:58:48 -0800237 mLevel = level;
Sundeep Ghuman55adc6b2017-06-05 16:46:59 -0700238 mWifiSpeed = wifiSpeed;
Tony Mantler98608e42016-01-22 14:58:48 -0800239 updateIcon(mLevel, context);
Julia Reynolds8897a502016-06-14 17:28:47 +0000240 notifyChanged();
Tony Mantler98608e42016-01-22 14:58:48 -0800241 }
Sundeep Ghumanf84e0572017-01-10 13:43:03 -0800242
Tony Mantler98608e42016-01-22 14:58:48 -0800243 updateBadge(context);
244
245 setSummary(mForSavedNetworks ? mAccessPoint.getSavedNetworkSummary()
246 : mAccessPoint.getSettingsSummary());
247
Fan Zhang60b850d2017-07-18 15:59:06 -0700248 mContentDescription = buildContentDescription(getContext(), this /* pref */, mAccessPoint);
Tony Mantler98608e42016-01-22 14:58:48 -0800249 }
250
251 @Override
252 protected void notifyChanged() {
253 if (Looper.getMainLooper() != Looper.myLooper()) {
254 // Let our BG thread callbacks call setTitle/setSummary.
255 postNotifyChanged();
256 } else {
257 super.notifyChanged();
258 }
259 }
260
Fan Zhang60b850d2017-07-18 15:59:06 -0700261 /**
262 * Helper method to generate content description string.
263 */
264 @VisibleForTesting
265 static CharSequence buildContentDescription(Context context, Preference pref, AccessPoint ap) {
266 CharSequence contentDescription = pref.getTitle();
267 final CharSequence summary = pref.getSummary();
268 if (!TextUtils.isEmpty(summary)) {
269 contentDescription = TextUtils.concat(contentDescription, ",", summary);
270 }
271 int level = ap.getLevel();
272 if (level >= 0 && level < WIFI_CONNECTION_STRENGTH.length) {
273 contentDescription = TextUtils.concat(contentDescription, ",",
274 context.getString(WIFI_CONNECTION_STRENGTH[level]));
275 }
276 return TextUtils.concat(contentDescription, ",",
277 ap.getSecurity() == AccessPoint.SECURITY_NONE
278 ? context.getString(R.string.accessibility_wifi_security_type_none)
279 : context.getString(R.string.accessibility_wifi_security_type_secured));
280 }
281
Tony Mantler98608e42016-01-22 14:58:48 -0800282 public void onLevelChanged() {
283 postNotifyChanged();
284 }
285
286 private void postNotifyChanged() {
287 if (mTitleView != null) {
288 mTitleView.post(mNotifyChanged);
289 } // Otherwise we haven't been bound yet, and don't need to update.
290 }
291
292 private final Runnable mNotifyChanged = new Runnable() {
293 @Override
294 public void run() {
295 notifyChanged();
296 }
297 };
298
299 public static class UserBadgeCache {
300 private final SparseArray<Drawable> mBadges = new SparseArray<>();
301 private final PackageManager mPm;
302
303 public UserBadgeCache(PackageManager pm) {
304 mPm = pm;
305 }
306
307 private Drawable getUserBadge(int userId) {
308 int index = mBadges.indexOfKey(userId);
309 if (index < 0) {
310 Drawable badge = mPm.getUserBadgeForDensity(new UserHandle(userId), 0 /* dpi */);
311 mBadges.put(userId, badge);
312 return badge;
313 }
314 return mBadges.valueAt(index);
315 }
316 }
Eric Schwarzenbach6efd09a2017-08-22 10:33:00 -0700317
318 static class IconInjector {
319 private final Context mContext;
320
321 public IconInjector(Context context) {
322 mContext = context;
323 }
324
325 public Drawable getIcon(int level) {
326 return mContext.getDrawable(Utils.getWifiIconResource(level));
327 }
328 }
Tony Mantler98608e42016-01-22 14:58:48 -0800329}