• 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.support.annotation.VisibleForTesting;
29 import android.support.v7.preference.Preference;
30 import android.support.v7.preference.PreferenceViewHolder;
31 import android.text.TextUtils;
32 import android.util.AttributeSet;
33 import android.util.SparseArray;
34 import android.view.View;
35 import android.widget.ImageView;
36 import android.widget.TextView;
37 
38 import com.android.settingslib.R;
39 import com.android.settingslib.TronUtils;
40 import com.android.settingslib.TwoTargetPreference;
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.setTint(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             mFrictionSld.setState(STATE_SECURED);
205         } else if (mAccessPoint.isMetered()) {
206             mFrictionSld.setState(STATE_METERED);
207         }
208         Drawable drawable = mFrictionSld.getCurrent();
209         frictionImageView.setImageDrawable(drawable);
210     }
211 
safeSetDefaultIcon()212     private void safeSetDefaultIcon() {
213         if (mDefaultIconResId != 0) {
214             setIcon(mDefaultIconResId);
215         } else {
216             setIcon(null);
217         }
218     }
219 
updateBadge(Context context)220     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      */
refresh()233     public void refresh() {
234         setTitle(this, mAccessPoint, mForSavedNetworks);
235         final Context context = getContext();
236         int level = mAccessPoint.getLevel();
237         int wifiSpeed = mAccessPoint.getSpeed();
238         if (level != mLevel || wifiSpeed != mWifiSpeed) {
239             mLevel = level;
240             mWifiSpeed = wifiSpeed;
241             updateIcon(mLevel, context);
242             notifyChanged();
243         }
244 
245         updateBadge(context);
246 
247         setSummary(mForSavedNetworks ? mAccessPoint.getSavedNetworkSummary()
248                 : mAccessPoint.getSettingsSummary());
249 
250         mContentDescription = buildContentDescription(getContext(), this /* pref */, mAccessPoint);
251     }
252 
253     @Override
notifyChanged()254     protected void notifyChanged() {
255         if (Looper.getMainLooper() != Looper.myLooper()) {
256             // Let our BG thread callbacks call setTitle/setSummary.
257             postNotifyChanged();
258         } else {
259             super.notifyChanged();
260         }
261     }
262 
263     @VisibleForTesting
setTitle(AccessPointPreference preference, AccessPoint ap, boolean savedNetworks)264     static void setTitle(AccessPointPreference preference, AccessPoint ap, boolean savedNetworks) {
265         if (savedNetworks) {
266             preference.setTitle(ap.getConfigName());
267         } else {
268             preference.setTitle(ap.getSsidStr());
269         }
270     }
271 
272     /**
273      * Helper method to generate content description string.
274      */
275     @VisibleForTesting
buildContentDescription(Context context, Preference pref, AccessPoint ap)276     static CharSequence buildContentDescription(Context context, Preference pref, AccessPoint ap) {
277         CharSequence contentDescription = pref.getTitle();
278         final CharSequence summary = pref.getSummary();
279         if (!TextUtils.isEmpty(summary)) {
280             contentDescription = TextUtils.concat(contentDescription, ",", summary);
281         }
282         int level = ap.getLevel();
283         if (level >= 0 && level < WIFI_CONNECTION_STRENGTH.length) {
284             contentDescription = TextUtils.concat(contentDescription, ",",
285                     context.getString(WIFI_CONNECTION_STRENGTH[level]));
286         }
287         return TextUtils.concat(contentDescription, ",",
288                 ap.getSecurity() == AccessPoint.SECURITY_NONE
289                         ? context.getString(R.string.accessibility_wifi_security_type_none)
290                         : context.getString(R.string.accessibility_wifi_security_type_secured));
291     }
292 
onLevelChanged()293     public void onLevelChanged() {
294         postNotifyChanged();
295     }
296 
postNotifyChanged()297     private void postNotifyChanged() {
298         if (mTitleView != null) {
299             mTitleView.post(mNotifyChanged);
300         } // Otherwise we haven't been bound yet, and don't need to update.
301     }
302 
303     private final Runnable mNotifyChanged = new Runnable() {
304         @Override
305         public void run() {
306             notifyChanged();
307         }
308     };
309 
310     public static class UserBadgeCache {
311         private final SparseArray<Drawable> mBadges = new SparseArray<>();
312         private final PackageManager mPm;
313 
UserBadgeCache(PackageManager pm)314         public UserBadgeCache(PackageManager pm) {
315             mPm = pm;
316         }
317 
getUserBadge(int userId)318         private Drawable getUserBadge(int userId) {
319             int index = mBadges.indexOfKey(userId);
320             if (index < 0) {
321                 Drawable badge = mPm.getUserBadgeForDensity(new UserHandle(userId), 0 /* dpi */);
322                 mBadges.put(userId, badge);
323                 return badge;
324             }
325             return mBadges.valueAt(index);
326         }
327     }
328 
329     static class IconInjector {
330         private final Context mContext;
331 
IconInjector(Context context)332         public IconInjector(Context context) {
333             mContext = context;
334         }
335 
getIcon(int level)336         public Drawable getIcon(int level) {
337             return mContext.getDrawable(Utils.getWifiIconResource(level));
338         }
339     }
340 }
341