• 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 dummy 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 dummy 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                 && (mAccessPoint.getSecurity() != AccessPoint.SECURITY_OWE_TRANSITION)) {
206             mFrictionSld.setState(STATE_SECURED);
207         } else if (mAccessPoint.isMetered()) {
208             mFrictionSld.setState(STATE_METERED);
209         }
210         Drawable drawable = mFrictionSld.getCurrent();
211         frictionImageView.setImageDrawable(drawable);
212     }
213 
safeSetDefaultIcon()214     private void safeSetDefaultIcon() {
215         if (mDefaultIconResId != 0) {
216             setIcon(mDefaultIconResId);
217         } else {
218             setIcon(null);
219         }
220     }
221 
updateBadge(Context context)222     protected void updateBadge(Context context) {
223         WifiConfiguration config = mAccessPoint.getConfig();
224         if (config != null) {
225             // Fetch badge (may be null)
226             // Get the badge using a cache since the PM will ask the UserManager for the list
227             // of profiles every time otherwise.
228             mBadge = mBadgeCache.getUserBadge(config.creatorUid);
229         }
230     }
231 
232     /**
233      * Updates the title and summary; may indirectly call notifyChanged().
234      */
refresh()235     public void refresh() {
236         setTitle(this, mAccessPoint);
237         final Context context = getContext();
238         int level = mAccessPoint.getLevel();
239         int wifiSpeed = mAccessPoint.getSpeed();
240         if (level != mLevel || wifiSpeed != mWifiSpeed) {
241             mLevel = level;
242             mWifiSpeed = wifiSpeed;
243             updateIcon(mLevel, context);
244             notifyChanged();
245         }
246 
247         updateBadge(context);
248 
249         setSummary(mForSavedNetworks ? mAccessPoint.getSavedNetworkSummary()
250                 : mAccessPoint.getSettingsSummary());
251 
252         mContentDescription = buildContentDescription(getContext(), this /* pref */, mAccessPoint);
253     }
254 
255     @Override
notifyChanged()256     protected void notifyChanged() {
257         if (Looper.getMainLooper() != Looper.myLooper()) {
258             // Let our BG thread callbacks call setTitle/setSummary.
259             postNotifyChanged();
260         } else {
261             super.notifyChanged();
262         }
263     }
264 
265     @VisibleForTesting
setTitle(AccessPointPreference preference, AccessPoint ap)266     static void setTitle(AccessPointPreference preference, AccessPoint ap) {
267         preference.setTitle(ap.getTitle());
268     }
269 
270     /**
271      * Helper method to generate content description string.
272      */
273     @VisibleForTesting
buildContentDescription(Context context, Preference pref, AccessPoint ap)274     static CharSequence buildContentDescription(Context context, Preference pref, AccessPoint ap) {
275         CharSequence contentDescription = pref.getTitle();
276         final CharSequence summary = pref.getSummary();
277         if (!TextUtils.isEmpty(summary)) {
278             contentDescription = TextUtils.concat(contentDescription, ",", summary);
279         }
280         int level = ap.getLevel();
281         if (level >= 0 && level < WIFI_CONNECTION_STRENGTH.length) {
282             contentDescription = TextUtils.concat(contentDescription, ",",
283                     context.getString(WIFI_CONNECTION_STRENGTH[level]));
284         }
285         return TextUtils.concat(contentDescription, ",",
286                 ap.getSecurity() == AccessPoint.SECURITY_NONE
287                         ? context.getString(R.string.accessibility_wifi_security_type_none)
288                         : context.getString(R.string.accessibility_wifi_security_type_secured));
289     }
290 
onLevelChanged()291     public void onLevelChanged() {
292         postNotifyChanged();
293     }
294 
postNotifyChanged()295     private void postNotifyChanged() {
296         if (mTitleView != null) {
297             mTitleView.post(mNotifyChanged);
298         } // Otherwise we haven't been bound yet, and don't need to update.
299     }
300 
301     private final Runnable mNotifyChanged = new Runnable() {
302         @Override
303         public void run() {
304             notifyChanged();
305         }
306     };
307 
308     public static class UserBadgeCache {
309         private final SparseArray<Drawable> mBadges = new SparseArray<>();
310         private final PackageManager mPm;
311 
UserBadgeCache(PackageManager pm)312         public UserBadgeCache(PackageManager pm) {
313             mPm = pm;
314         }
315 
getUserBadge(int userId)316         private Drawable getUserBadge(int userId) {
317             int index = mBadges.indexOfKey(userId);
318             if (index < 0) {
319                 Drawable badge = mPm.getUserBadgeForDensity(new UserHandle(userId), 0 /* dpi */);
320                 mBadges.put(userId, badge);
321                 return badge;
322             }
323             return mBadges.valueAt(index);
324         }
325     }
326 
327     static class IconInjector {
328         private final Context mContext;
329 
IconInjector(Context context)330         public IconInjector(Context context) {
331             mContext = context;
332         }
333 
getIcon(int level)334         public Drawable getIcon(int level) {
335             return mContext.getDrawable(Utils.getWifiIconResource(level));
336         }
337     }
338 }
339