• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
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  */
16 package com.android.settingslib.wifi;
17 
18 import android.annotation.Nullable;
19 import android.content.Context;
20 import android.content.pm.PackageManager;
21 import android.content.res.Resources;
22 import android.content.res.TypedArray;
23 import android.graphics.drawable.Drawable;
24 import android.graphics.drawable.StateListDrawable;
25 import android.net.wifi.WifiConfiguration;
26 import android.os.Looper;
27 import android.os.UserHandle;
28 import android.text.TextUtils;
29 import android.util.AttributeSet;
30 import android.util.SparseArray;
31 import android.view.View;
32 import android.widget.ImageView;
33 import android.widget.TextView;
34 
35 import androidx.annotation.VisibleForTesting;
36 import androidx.preference.Preference;
37 import androidx.preference.PreferenceViewHolder;
38 
39 import com.android.settingslib.R;
40 import com.android.settingslib.TronUtils;
41 import com.android.settingslib.Utils;
42 import com.android.settingslib.wifi.AccessPoint.Speed;
43 
44 public class AccessPointPreference extends Preference {
45 
46     private static final int[] STATE_SECURED = {
47             R.attr.state_encrypted
48     };
49 
50     private static final int[] STATE_METERED = {
51             R.attr.state_metered
52     };
53 
54     private static final int[] FRICTION_ATTRS = {
55             R.attr.wifi_friction
56     };
57 
58     private static final int[] WIFI_CONNECTION_STRENGTH = {
59             R.string.accessibility_no_wifi,
60             R.string.accessibility_wifi_one_bar,
61             R.string.accessibility_wifi_two_bars,
62             R.string.accessibility_wifi_three_bars,
63             R.string.accessibility_wifi_signal_full
64     };
65 
66     @Nullable private final StateListDrawable mFrictionSld;
67     private final int mBadgePadding;
68     private final UserBadgeCache mBadgeCache;
69     private final IconInjector mIconInjector;
70     private TextView mTitleView;
71     private boolean mShowDivider;
72 
73     private boolean mForSavedNetworks = false;
74     private AccessPoint mAccessPoint;
75     private Drawable mBadge;
76     private int mLevel;
77     private CharSequence mContentDescription;
78     private int mDefaultIconResId;
79     private int mWifiSpeed = Speed.NONE;
80 
81     @Nullable
getFrictionStateListDrawable(Context context)82     private static StateListDrawable getFrictionStateListDrawable(Context context) {
83         TypedArray frictionSld;
84         try {
85             frictionSld = context.getTheme().obtainStyledAttributes(FRICTION_ATTRS);
86         } catch (Resources.NotFoundException e) {
87             // Fallback for platforms that do not need friction icon resources.
88             frictionSld = null;
89         }
90         return frictionSld != null ? (StateListDrawable) frictionSld.getDrawable(0) : null;
91     }
92 
93     // Used for fake pref.
AccessPointPreference(Context context, AttributeSet attrs)94     public AccessPointPreference(Context context, AttributeSet attrs) {
95         super(context, attrs);
96         mFrictionSld = null;
97         mBadgePadding = 0;
98         mBadgeCache = null;
99         mIconInjector = new IconInjector(context);
100     }
101 
AccessPointPreference(AccessPoint accessPoint, Context context, UserBadgeCache cache, boolean forSavedNetworks)102     public AccessPointPreference(AccessPoint accessPoint, Context context, UserBadgeCache cache,
103             boolean forSavedNetworks) {
104         this(accessPoint, context, cache, 0 /* iconResId */, forSavedNetworks);
105         refresh();
106     }
107 
AccessPointPreference(AccessPoint accessPoint, Context context, UserBadgeCache cache, int iconResId, boolean forSavedNetworks)108     public AccessPointPreference(AccessPoint accessPoint, Context context, UserBadgeCache cache,
109             int iconResId, boolean forSavedNetworks) {
110         this(accessPoint, context, cache, iconResId, forSavedNetworks,
111                 getFrictionStateListDrawable(context), -1 /* level */, new IconInjector(context));
112     }
113 
114     @VisibleForTesting
AccessPointPreference(AccessPoint accessPoint, Context context, UserBadgeCache cache, int iconResId, boolean forSavedNetworks, StateListDrawable frictionSld, int level, IconInjector iconInjector)115     AccessPointPreference(AccessPoint accessPoint, Context context, UserBadgeCache cache,
116                           int iconResId, boolean forSavedNetworks, StateListDrawable frictionSld,
117                           int level, IconInjector iconInjector) {
118         super(context);
119         setLayoutResource(R.layout.preference_access_point);
120         setWidgetLayoutResource(getWidgetLayoutResourceId());
121         mBadgeCache = cache;
122         mAccessPoint = accessPoint;
123         mForSavedNetworks = forSavedNetworks;
124         mAccessPoint.setTag(this);
125         mLevel = level;
126         mDefaultIconResId = iconResId;
127         mFrictionSld = frictionSld;
128         mIconInjector = iconInjector;
129         mBadgePadding = context.getResources()
130                 .getDimensionPixelSize(R.dimen.wifi_preference_badge_padding);
131     }
132 
getWidgetLayoutResourceId()133     protected int getWidgetLayoutResourceId() {
134         return R.layout.access_point_friction_widget;
135     }
136 
getAccessPoint()137     public AccessPoint getAccessPoint() {
138         return mAccessPoint;
139     }
140 
141     @Override
onBindViewHolder(final PreferenceViewHolder view)142     public void onBindViewHolder(final PreferenceViewHolder view) {
143         super.onBindViewHolder(view);
144         if (mAccessPoint == null) {
145             // Used for fake pref.
146             return;
147         }
148         Drawable drawable = getIcon();
149         if (drawable != null) {
150             drawable.setLevel(mLevel);
151         }
152 
153         mTitleView = (TextView) view.findViewById(android.R.id.title);
154         if (mTitleView != null) {
155             // Attach to the end of the title view
156             mTitleView.setCompoundDrawablesRelativeWithIntrinsicBounds(null, null, mBadge, null);
157             mTitleView.setCompoundDrawablePadding(mBadgePadding);
158         }
159         view.itemView.setContentDescription(mContentDescription);
160 
161         ImageView frictionImageView = (ImageView) view.findViewById(R.id.friction_icon);
162         bindFrictionImage(frictionImageView);
163 
164         final View divider = view.findViewById(R.id.two_target_divider);
165         divider.setVisibility(shouldShowDivider() ? View.VISIBLE : View.INVISIBLE);
166     }
167 
shouldShowDivider()168     public boolean shouldShowDivider() {
169         return mShowDivider;
170     }
171 
setShowDivider(boolean showDivider)172     public void setShowDivider(boolean showDivider) {
173         mShowDivider = showDivider;
174         notifyChanged();
175     }
176 
updateIcon(int level, Context context)177     protected void updateIcon(int level, Context context) {
178         if (level == -1) {
179             safeSetDefaultIcon();
180             return;
181         }
182         TronUtils.logWifiSettingsSpeed(context, mWifiSpeed);
183 
184         Drawable drawable = mIconInjector.getIcon(level);
185         if (!mForSavedNetworks && drawable != null) {
186             drawable.setTintList(Utils.getColorAttr(context, android.R.attr.colorControlNormal));
187             setIcon(drawable);
188         } else {
189             safeSetDefaultIcon();
190         }
191     }
192 
193     /**
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      */
bindFrictionImage(ImageView frictionImageView)199     private void bindFrictionImage(ImageView frictionImageView) {
200         if (frictionImageView == null || mFrictionSld == null) {
201             return;
202         }
203         if ((mAccessPoint.getSecurity() != AccessPoint.SECURITY_NONE)
204                 && (mAccessPoint.getSecurity() != AccessPoint.SECURITY_OWE)) {
205             mFrictionSld.setState(STATE_SECURED);
206         } else if (mAccessPoint.isMetered()) {
207             mFrictionSld.setState(STATE_METERED);
208         }
209         Drawable drawable = mFrictionSld.getCurrent();
210         frictionImageView.setImageDrawable(drawable);
211     }
212 
safeSetDefaultIcon()213     private void safeSetDefaultIcon() {
214         if (mDefaultIconResId != 0) {
215             setIcon(mDefaultIconResId);
216         } else {
217             setIcon(null);
218         }
219     }
220 
updateBadge(Context context)221     protected void updateBadge(Context context) {
222         WifiConfiguration config = mAccessPoint.getConfig();
223         if (config != null) {
224             // Fetch badge (may be null)
225             // Get the badge using a cache since the PM will ask the UserManager for the list
226             // of profiles every time otherwise.
227             mBadge = mBadgeCache.getUserBadge(config.creatorUid);
228         }
229     }
230 
231     /**
232      * Updates the title and summary; may indirectly call notifyChanged().
233      */
refresh()234     public void refresh() {
235         setTitle(this, mAccessPoint);
236         final Context context = getContext();
237         int level = mAccessPoint.getLevel();
238         int wifiSpeed = mAccessPoint.getSpeed();
239         if (level != mLevel || wifiSpeed != mWifiSpeed) {
240             mLevel = level;
241             mWifiSpeed = wifiSpeed;
242             updateIcon(mLevel, context);
243             notifyChanged();
244         }
245 
246         updateBadge(context);
247 
248         setSummary(mForSavedNetworks ? mAccessPoint.getSavedNetworkSummary()
249                 : mAccessPoint.getSettingsSummary());
250 
251         mContentDescription = buildContentDescription(getContext(), this /* pref */, mAccessPoint);
252     }
253 
254     @Override
notifyChanged()255     protected void notifyChanged() {
256         if (Looper.getMainLooper() != Looper.myLooper()) {
257             // Let our BG thread callbacks call setTitle/setSummary.
258             postNotifyChanged();
259         } else {
260             super.notifyChanged();
261         }
262     }
263 
264     @VisibleForTesting
setTitle(AccessPointPreference preference, AccessPoint ap)265     static void setTitle(AccessPointPreference preference, AccessPoint ap) {
266         preference.setTitle(ap.getTitle());
267     }
268 
269     /**
270      * Helper method to generate content description string.
271      */
272     @VisibleForTesting
buildContentDescription(Context context, Preference pref, AccessPoint ap)273     static CharSequence buildContentDescription(Context context, Preference pref, AccessPoint ap) {
274         CharSequence contentDescription = pref.getTitle();
275         final CharSequence summary = pref.getSummary();
276         if (!TextUtils.isEmpty(summary)) {
277             contentDescription = TextUtils.concat(contentDescription, ",", summary);
278         }
279         int level = ap.getLevel();
280         if (level >= 0 && level < WIFI_CONNECTION_STRENGTH.length) {
281             contentDescription = TextUtils.concat(contentDescription, ",",
282                     context.getString(WIFI_CONNECTION_STRENGTH[level]));
283         }
284         return TextUtils.concat(contentDescription, ",",
285                 ap.getSecurity() == AccessPoint.SECURITY_NONE
286                         ? context.getString(R.string.accessibility_wifi_security_type_none)
287                         : context.getString(R.string.accessibility_wifi_security_type_secured));
288     }
289 
onLevelChanged()290     public void onLevelChanged() {
291         postNotifyChanged();
292     }
293 
postNotifyChanged()294     private void postNotifyChanged() {
295         if (mTitleView != null) {
296             mTitleView.post(mNotifyChanged);
297         } // Otherwise we haven't been bound yet, and don't need to update.
298     }
299 
300     private final Runnable mNotifyChanged = new Runnable() {
301         @Override
302         public void run() {
303             notifyChanged();
304         }
305     };
306 
307     public static class UserBadgeCache {
308         private final SparseArray<Drawable> mBadges = new SparseArray<>();
309         private final PackageManager mPm;
310 
UserBadgeCache(PackageManager pm)311         public UserBadgeCache(PackageManager pm) {
312             mPm = pm;
313         }
314 
getUserBadge(int userId)315         private Drawable getUserBadge(int userId) {
316             int index = mBadges.indexOfKey(userId);
317             if (index < 0) {
318                 Drawable badge = mPm.getUserBadgeForDensity(new UserHandle(userId), 0 /* dpi */);
319                 mBadges.put(userId, badge);
320                 return badge;
321             }
322             return mBadges.valueAt(index);
323         }
324     }
325 
326     static class IconInjector {
327         private final Context mContext;
328 
IconInjector(Context context)329         public IconInjector(Context context) {
330             mContext = context;
331         }
332 
getIcon(int level)333         public Drawable getIcon(int level) {
334             return mContext.getDrawable(Utils.getWifiIconResource(level));
335         }
336     }
337 }
338