blob: 2c4f9c4321946962de838e6b5fee6369b3cee92d [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
18import android.content.Context;
19import android.content.pm.PackageManager;
Stephen Chen2c096592017-01-18 15:25:31 -080020import android.content.res.Resources;
21import android.content.res.TypedArray;
Tony Mantler98608e42016-01-22 14:58:48 -080022import android.graphics.drawable.Drawable;
23import android.graphics.drawable.StateListDrawable;
Stephen Chened8f5292017-01-30 16:31:23 -080024import android.net.NetworkBadging;
Tony Mantler98608e42016-01-22 14:58:48 -080025import 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;
Tony Mantler98608e42016-01-22 14:58:48 -080040
41public class AccessPointPreference extends Preference {
42
43 private static final int[] STATE_SECURED = {
44 R.attr.state_encrypted
45 };
Tony Mantler98608e42016-01-22 14:58:48 -080046
Stephen Chenaed318e2017-03-31 16:58:38 -070047 private static final int[] STATE_METERED = {
48 R.attr.state_metered
49 };
50
Fan Zhang60b850d2017-07-18 15:59:06 -070051 private static final int[] FRICTION_ATTRS = {
52 R.attr.wifi_friction
53 };
54
55 private static final int[] WIFI_CONNECTION_STRENGTH = {
56 R.string.accessibility_no_wifi,
57 R.string.accessibility_wifi_one_bar,
58 R.string.accessibility_wifi_two_bars,
59 R.string.accessibility_wifi_three_bars,
60 R.string.accessibility_wifi_signal_full
61 };
Tony Mantler98608e42016-01-22 14:58:48 -080062
Stephen Chen2c096592017-01-18 15:25:31 -080063 private final StateListDrawable mFrictionSld;
Tony Mantler98608e42016-01-22 14:58:48 -080064 private final int mBadgePadding;
65 private final UserBadgeCache mBadgeCache;
Tony Mantler98608e42016-01-22 14:58:48 -080066 private TextView mTitleView;
Salvador Martinez45a15942016-08-11 17:14:11 -070067
Tony Mantler98608e42016-01-22 14:58:48 -080068 private boolean mForSavedNetworks = false;
69 private AccessPoint mAccessPoint;
70 private Drawable mBadge;
71 private int mLevel;
72 private CharSequence mContentDescription;
Salvador Martinez45a15942016-08-11 17:14:11 -070073 private int mDefaultIconResId;
Sundeep Ghuman55adc6b2017-06-05 16:46:59 -070074 private int mWifiSpeed = NetworkBadging.BADGING_NONE;
Tony Mantler98608e42016-01-22 14:58:48 -080075
Sundeep Ghuman7c32aa32017-06-26 17:13:00 -070076 public static String generatePreferenceKey(AccessPoint accessPoint) {
77 StringBuilder builder = new StringBuilder();
78
79 if (TextUtils.isEmpty(accessPoint.getBssid())) {
80 builder.append(accessPoint.getSsidStr());
81 } else {
82 builder.append(accessPoint.getBssid());
83 }
84
85 builder.append(',').append(accessPoint.getSecurity());
86 return builder.toString();
87 }
88
Tony Mantler98608e42016-01-22 14:58:48 -080089 // Used for dummy pref.
90 public AccessPointPreference(Context context, AttributeSet attrs) {
91 super(context, attrs);
Stephen Chen2c096592017-01-18 15:25:31 -080092 mFrictionSld = null;
Tony Mantler98608e42016-01-22 14:58:48 -080093 mBadgePadding = 0;
94 mBadgeCache = null;
95 }
96
97 public AccessPointPreference(AccessPoint accessPoint, Context context, UserBadgeCache cache,
98 boolean forSavedNetworks) {
99 super(context);
Stephen Chen2c096592017-01-18 15:25:31 -0800100 setWidgetLayoutResource(R.layout.access_point_friction_widget);
Tony Mantler98608e42016-01-22 14:58:48 -0800101 mBadgeCache = cache;
102 mAccessPoint = accessPoint;
103 mForSavedNetworks = forSavedNetworks;
104 mAccessPoint.setTag(this);
105 mLevel = -1;
106
Stephen Chen2c096592017-01-18 15:25:31 -0800107 TypedArray frictionSld;
108 try {
Fan Zhang60b850d2017-07-18 15:59:06 -0700109 frictionSld = context.getTheme().obtainStyledAttributes(FRICTION_ATTRS);
Stephen Chen2c096592017-01-18 15:25:31 -0800110 } catch (Resources.NotFoundException e) {
111 // Fallback for platforms that do not need friction icon resources.
112 frictionSld = null;
113 }
114 mFrictionSld = frictionSld != null ? (StateListDrawable) frictionSld.getDrawable(0) : null;
115
Tony Mantler98608e42016-01-22 14:58:48 -0800116 // Distance from the end of the title at which this AP's user badge should sit.
117 mBadgePadding = context.getResources()
118 .getDimensionPixelSize(R.dimen.wifi_preference_badge_padding);
119 refresh();
120 }
121
Salvador Martinez45a15942016-08-11 17:14:11 -0700122 public AccessPointPreference(AccessPoint accessPoint, Context context, UserBadgeCache cache,
123 int iconResId, boolean forSavedNetworks) {
124 super(context);
Stephen Chen2c096592017-01-18 15:25:31 -0800125 setWidgetLayoutResource(R.layout.access_point_friction_widget);
Salvador Martinez45a15942016-08-11 17:14:11 -0700126 mBadgeCache = cache;
127 mAccessPoint = accessPoint;
128 mForSavedNetworks = forSavedNetworks;
129 mAccessPoint.setTag(this);
130 mLevel = -1;
131 mDefaultIconResId = iconResId;
132
Stephen Chen2c096592017-01-18 15:25:31 -0800133 TypedArray frictionSld;
134 try {
Fan Zhang60b850d2017-07-18 15:59:06 -0700135 frictionSld = context.getTheme().obtainStyledAttributes(FRICTION_ATTRS);
Stephen Chen2c096592017-01-18 15:25:31 -0800136 } catch (Resources.NotFoundException e) {
137 // Fallback for platforms that do not need friction icon resources.
138 frictionSld = null;
139 }
140 mFrictionSld = frictionSld != null ? (StateListDrawable) frictionSld.getDrawable(0) : null;
141
Salvador Martinez45a15942016-08-11 17:14:11 -0700142 // Distance from the end of the title at which this AP's user badge should sit.
143 mBadgePadding = context.getResources()
144 .getDimensionPixelSize(R.dimen.wifi_preference_badge_padding);
145 }
146
Tony Mantler98608e42016-01-22 14:58:48 -0800147 public AccessPoint getAccessPoint() {
148 return mAccessPoint;
149 }
150
151 @Override
152 public void onBindViewHolder(final PreferenceViewHolder view) {
153 super.onBindViewHolder(view);
154 if (mAccessPoint == null) {
155 // Used for dummy pref.
156 return;
157 }
158 Drawable drawable = getIcon();
159 if (drawable != null) {
160 drawable.setLevel(mLevel);
161 }
162
163 mTitleView = (TextView) view.findViewById(com.android.internal.R.id.title);
164 if (mTitleView != null) {
165 // Attach to the end of the title view
166 mTitleView.setCompoundDrawablesRelativeWithIntrinsicBounds(null, null, mBadge, null);
167 mTitleView.setCompoundDrawablePadding(mBadgePadding);
168 }
169 view.itemView.setContentDescription(mContentDescription);
Stephen Chen2c096592017-01-18 15:25:31 -0800170
Sundeep Ghuman4d8781e2017-03-15 16:03:55 -0700171 ImageView frictionImageView = (ImageView) view.findViewById(R.id.friction_icon);
172 bindFrictionImage(frictionImageView);
Tony Mantler98608e42016-01-22 14:58:48 -0800173 }
174
175 protected void updateIcon(int level, Context context) {
176 if (level == -1) {
Salvador Martinez45a15942016-08-11 17:14:11 -0700177 safeSetDefaultIcon();
Stephen Chened8f5292017-01-30 16:31:23 -0800178 return;
179 }
Sundeep Ghuman53200ed2017-06-21 16:54:36 -0700180 TronUtils.logWifiSettingsSpeed(context, mWifiSpeed);
Sundeep Ghuman271e5de2017-05-30 14:11:39 -0700181
182 // TODO(b/62355275): Revert this to N code after deleting NetworkBadging API
183 Drawable drawable = NetworkBadging.getWifiIcon(
184 level, NetworkBadging.BADGING_NONE, getContext().getTheme());
Stephen Chened8f5292017-01-30 16:31:23 -0800185 if (!mForSavedNetworks && drawable != null) {
Sundeep Ghuman6962c0c2017-05-08 14:27:06 -0700186 drawable.setTint(Utils.getColorAttr(context, android.R.attr.colorControlNormal));
Stephen Chened8f5292017-01-30 16:31:23 -0800187 setIcon(drawable);
Tony Mantler98608e42016-01-22 14:58:48 -0800188 } else {
Stephen Chened8f5292017-01-30 16:31:23 -0800189 safeSetDefaultIcon();
Tony Mantler98608e42016-01-22 14:58:48 -0800190 }
191 }
192
Stephen Chen2c096592017-01-18 15:25:31 -0800193 /**
194 * Binds the friction icon drawable using a StateListDrawable.
195 *
196 * <p>Friction icons will be rebound when notifyChange() is called, and therefore
197 * do not need to be managed in refresh()</p>.
198 */
199 private void bindFrictionImage(ImageView frictionImageView) {
200 if (frictionImageView == null || mFrictionSld == null) {
201 return;
202 }
203 if (mAccessPoint.getSecurity() != AccessPoint.SECURITY_NONE) {
Sundeep Ghuman4d8781e2017-03-15 16:03:55 -0700204 mFrictionSld.setState(STATE_SECURED);
Stephen Chen21f68682017-04-04 13:23:31 -0700205 } else if (mAccessPoint.isMetered()) {
Stephen Chenaed318e2017-03-31 16:58:38 -0700206 mFrictionSld.setState(STATE_METERED);
Stephen Chen2c096592017-01-18 15:25:31 -0800207 }
208 Drawable drawable = mFrictionSld.getCurrent();
209 frictionImageView.setImageDrawable(drawable);
210 }
211
Salvador Martinez45a15942016-08-11 17:14:11 -0700212 private void safeSetDefaultIcon() {
213 if (mDefaultIconResId != 0) {
214 setIcon(mDefaultIconResId);
215 } else {
216 setIcon(null);
217 }
218 }
219
Tony Mantler98608e42016-01-22 14:58:48 -0800220 protected void updateBadge(Context context) {
221 WifiConfiguration config = mAccessPoint.getConfig();
222 if (config != null) {
223 // Fetch badge (may be null)
224 // Get the badge using a cache since the PM will ask the UserManager for the list
225 // of profiles every time otherwise.
226 mBadge = mBadgeCache.getUserBadge(config.creatorUid);
227 }
228 }
229
230 /**
231 * Updates the title and summary; may indirectly call notifyChanged().
232 */
233 public void refresh() {
234 if (mForSavedNetworks) {
235 setTitle(mAccessPoint.getConfigName());
236 } else {
237 setTitle(mAccessPoint.getSsid());
238 }
239
240 final Context context = getContext();
Sundeep Ghumanaaa8a1b2017-03-13 14:40:56 -0700241 int level = mAccessPoint.getLevel();
Sundeep Ghuman55adc6b2017-06-05 16:46:59 -0700242 int wifiSpeed = mAccessPoint.getSpeed();
243 if (level != mLevel || wifiSpeed != mWifiSpeed) {
Tony Mantler98608e42016-01-22 14:58:48 -0800244 mLevel = level;
Sundeep Ghuman55adc6b2017-06-05 16:46:59 -0700245 mWifiSpeed = wifiSpeed;
Tony Mantler98608e42016-01-22 14:58:48 -0800246 updateIcon(mLevel, context);
Julia Reynolds8897a502016-06-14 17:28:47 +0000247 notifyChanged();
Tony Mantler98608e42016-01-22 14:58:48 -0800248 }
Sundeep Ghumanf84e0572017-01-10 13:43:03 -0800249
Tony Mantler98608e42016-01-22 14:58:48 -0800250 updateBadge(context);
251
252 setSummary(mForSavedNetworks ? mAccessPoint.getSavedNetworkSummary()
253 : mAccessPoint.getSettingsSummary());
254
Fan Zhang60b850d2017-07-18 15:59:06 -0700255 mContentDescription = buildContentDescription(getContext(), this /* pref */, mAccessPoint);
Tony Mantler98608e42016-01-22 14:58:48 -0800256 }
257
258 @Override
259 protected void notifyChanged() {
260 if (Looper.getMainLooper() != Looper.myLooper()) {
261 // Let our BG thread callbacks call setTitle/setSummary.
262 postNotifyChanged();
263 } else {
264 super.notifyChanged();
265 }
266 }
267
Fan Zhang60b850d2017-07-18 15:59:06 -0700268 /**
269 * Helper method to generate content description string.
270 */
271 @VisibleForTesting
272 static CharSequence buildContentDescription(Context context, Preference pref, AccessPoint ap) {
273 CharSequence contentDescription = pref.getTitle();
274 final CharSequence summary = pref.getSummary();
275 if (!TextUtils.isEmpty(summary)) {
276 contentDescription = TextUtils.concat(contentDescription, ",", summary);
277 }
278 int level = ap.getLevel();
279 if (level >= 0 && level < WIFI_CONNECTION_STRENGTH.length) {
280 contentDescription = TextUtils.concat(contentDescription, ",",
281 context.getString(WIFI_CONNECTION_STRENGTH[level]));
282 }
283 return TextUtils.concat(contentDescription, ",",
284 ap.getSecurity() == AccessPoint.SECURITY_NONE
285 ? context.getString(R.string.accessibility_wifi_security_type_none)
286 : context.getString(R.string.accessibility_wifi_security_type_secured));
287 }
288
Tony Mantler98608e42016-01-22 14:58:48 -0800289 public void onLevelChanged() {
290 postNotifyChanged();
291 }
292
293 private void postNotifyChanged() {
294 if (mTitleView != null) {
295 mTitleView.post(mNotifyChanged);
296 } // Otherwise we haven't been bound yet, and don't need to update.
297 }
298
299 private final Runnable mNotifyChanged = new Runnable() {
300 @Override
301 public void run() {
302 notifyChanged();
303 }
304 };
305
306 public static class UserBadgeCache {
307 private final SparseArray<Drawable> mBadges = new SparseArray<>();
308 private final PackageManager mPm;
309
310 public UserBadgeCache(PackageManager pm) {
311 mPm = pm;
312 }
313
314 private Drawable getUserBadge(int userId) {
315 int index = mBadges.indexOfKey(userId);
316 if (index < 0) {
317 Drawable badge = mPm.getUserBadgeForDensity(new UserHandle(userId), 0 /* dpi */);
318 mBadges.put(userId, badge);
319 return badge;
320 }
321 return mBadges.valueAt(index);
322 }
323 }
324}