Home | History | Annotate | Download | only in wifi
      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.content.Context;
     19 import android.content.pm.PackageManager;
     20 import android.content.res.Resources;
     21 import android.content.res.TypedArray;
     22 import android.graphics.drawable.Drawable;
     23 import android.graphics.drawable.StateListDrawable;
     24 import android.net.NetworkBadging;
     25 import android.net.wifi.WifiConfiguration;
     26 import android.os.Looper;
     27 import android.os.UserHandle;
     28 import android.support.v7.preference.Preference;
     29 import android.support.v7.preference.PreferenceViewHolder;
     30 import android.text.TextUtils;
     31 import android.util.AttributeSet;
     32 import android.util.SparseArray;
     33 import android.widget.ImageView;
     34 import android.widget.TextView;
     35 
     36 import com.android.settingslib.R;
     37 import com.android.settingslib.TronUtils;
     38 import com.android.settingslib.Utils;
     39 
     40 public class AccessPointPreference extends Preference {
     41 
     42     private static final int[] STATE_SECURED = {
     43             R.attr.state_encrypted
     44     };
     45 
     46     private static final int[] STATE_METERED = {
     47             R.attr.state_metered
     48     };
     49 
     50     private static final int[] wifi_friction_attributes = { R.attr.wifi_friction };
     51 
     52     private final StateListDrawable mFrictionSld;
     53     private final int mBadgePadding;
     54     private final UserBadgeCache mBadgeCache;
     55     private TextView mTitleView;
     56 
     57     private boolean mForSavedNetworks = false;
     58     private AccessPoint mAccessPoint;
     59     private Drawable mBadge;
     60     private int mLevel;
     61     private CharSequence mContentDescription;
     62     private int mDefaultIconResId;
     63     private int mWifiBadge = NetworkBadging.BADGING_NONE;
     64 
     65     static final int[] WIFI_CONNECTION_STRENGTH = {
     66             R.string.accessibility_wifi_one_bar,
     67             R.string.accessibility_wifi_two_bars,
     68             R.string.accessibility_wifi_three_bars,
     69             R.string.accessibility_wifi_signal_full
     70     };
     71 
     72     // Used for dummy pref.
     73     public AccessPointPreference(Context context, AttributeSet attrs) {
     74         super(context, attrs);
     75         mFrictionSld = null;
     76         mBadgePadding = 0;
     77         mBadgeCache = null;
     78     }
     79 
     80     public AccessPointPreference(AccessPoint accessPoint, Context context, UserBadgeCache cache,
     81             boolean forSavedNetworks) {
     82         super(context);
     83         setWidgetLayoutResource(R.layout.access_point_friction_widget);
     84         mBadgeCache = cache;
     85         mAccessPoint = accessPoint;
     86         mForSavedNetworks = forSavedNetworks;
     87         mAccessPoint.setTag(this);
     88         mLevel = -1;
     89 
     90         TypedArray frictionSld;
     91         try {
     92             frictionSld = context.getTheme().obtainStyledAttributes(wifi_friction_attributes);
     93         } catch (Resources.NotFoundException e) {
     94             // Fallback for platforms that do not need friction icon resources.
     95             frictionSld = null;
     96         }
     97         mFrictionSld = frictionSld != null ? (StateListDrawable) frictionSld.getDrawable(0) : null;
     98 
     99         // Distance from the end of the title at which this AP's user badge should sit.
    100         mBadgePadding = context.getResources()
    101                 .getDimensionPixelSize(R.dimen.wifi_preference_badge_padding);
    102         refresh();
    103     }
    104 
    105     public AccessPointPreference(AccessPoint accessPoint, Context context, UserBadgeCache cache,
    106             int iconResId, boolean forSavedNetworks) {
    107         super(context);
    108         setWidgetLayoutResource(R.layout.access_point_friction_widget);
    109         mBadgeCache = cache;
    110         mAccessPoint = accessPoint;
    111         mForSavedNetworks = forSavedNetworks;
    112         mAccessPoint.setTag(this);
    113         mLevel = -1;
    114         mDefaultIconResId = iconResId;
    115 
    116         TypedArray frictionSld;
    117         try {
    118             frictionSld = context.getTheme().obtainStyledAttributes(wifi_friction_attributes);
    119         } catch (Resources.NotFoundException e) {
    120             // Fallback for platforms that do not need friction icon resources.
    121             frictionSld = null;
    122         }
    123         mFrictionSld = frictionSld != null ? (StateListDrawable) frictionSld.getDrawable(0) : null;
    124 
    125         // Distance from the end of the title at which this AP's user badge should sit.
    126         mBadgePadding = context.getResources()
    127                 .getDimensionPixelSize(R.dimen.wifi_preference_badge_padding);
    128     }
    129 
    130     public AccessPoint getAccessPoint() {
    131         return mAccessPoint;
    132     }
    133 
    134     @Override
    135     public void onBindViewHolder(final PreferenceViewHolder view) {
    136         super.onBindViewHolder(view);
    137         if (mAccessPoint == null) {
    138             // Used for dummy pref.
    139             return;
    140         }
    141         Drawable drawable = getIcon();
    142         if (drawable != null) {
    143             drawable.setLevel(mLevel);
    144         }
    145 
    146         mTitleView = (TextView) view.findViewById(com.android.internal.R.id.title);
    147         if (mTitleView != null) {
    148             // Attach to the end of the title view
    149             mTitleView.setCompoundDrawablesRelativeWithIntrinsicBounds(null, null, mBadge, null);
    150             mTitleView.setCompoundDrawablePadding(mBadgePadding);
    151         }
    152         view.itemView.setContentDescription(mContentDescription);
    153 
    154         ImageView frictionImageView = (ImageView) view.findViewById(R.id.friction_icon);
    155         bindFrictionImage(frictionImageView);
    156     }
    157 
    158     protected void updateIcon(int level, Context context) {
    159         if (level == -1) {
    160             safeSetDefaultIcon();
    161             return;
    162         }
    163         TronUtils.logWifiSettingsBadge(context, mWifiBadge);
    164         Drawable drawable = NetworkBadging.getWifiIcon(level, mWifiBadge, getContext().getTheme());
    165         if (!mForSavedNetworks && drawable != null) {
    166             drawable.setTint(Utils.getColorAttr(context, android.R.attr.colorControlNormal));
    167             setIcon(drawable);
    168         } else {
    169             safeSetDefaultIcon();
    170         }
    171     }
    172 
    173     /**
    174      * Binds the friction icon drawable using a StateListDrawable.
    175      *
    176      * <p>Friction icons will be rebound when notifyChange() is called, and therefore
    177      * do not need to be managed in refresh()</p>.
    178      */
    179     private void bindFrictionImage(ImageView frictionImageView) {
    180         if (frictionImageView == null || mFrictionSld == null) {
    181             return;
    182         }
    183         if (mAccessPoint.getSecurity() != AccessPoint.SECURITY_NONE) {
    184             mFrictionSld.setState(STATE_SECURED);
    185         } else if (mAccessPoint.isMetered()) {
    186             mFrictionSld.setState(STATE_METERED);
    187         }
    188         Drawable drawable = mFrictionSld.getCurrent();
    189         frictionImageView.setImageDrawable(drawable);
    190     }
    191 
    192     private void safeSetDefaultIcon() {
    193         if (mDefaultIconResId != 0) {
    194             setIcon(mDefaultIconResId);
    195         } else {
    196             setIcon(null);
    197         }
    198     }
    199 
    200     protected void updateBadge(Context context) {
    201         WifiConfiguration config = mAccessPoint.getConfig();
    202         if (config != null) {
    203             // Fetch badge (may be null)
    204             // Get the badge using a cache since the PM will ask the UserManager for the list
    205             // of profiles every time otherwise.
    206             mBadge = mBadgeCache.getUserBadge(config.creatorUid);
    207         }
    208     }
    209 
    210     /**
    211      * Updates the title and summary; may indirectly call notifyChanged().
    212      */
    213     public void refresh() {
    214         if (mForSavedNetworks) {
    215             setTitle(mAccessPoint.getConfigName());
    216         } else {
    217             setTitle(mAccessPoint.getSsid());
    218         }
    219 
    220         final Context context = getContext();
    221         int level = mAccessPoint.getLevel();
    222         int wifiBadge = mAccessPoint.getBadge();
    223         if (level != mLevel || wifiBadge != mWifiBadge) {
    224             mLevel = level;
    225             mWifiBadge = wifiBadge;
    226             updateIcon(mLevel, context);
    227             notifyChanged();
    228         }
    229 
    230         updateBadge(context);
    231 
    232         setSummary(mForSavedNetworks ? mAccessPoint.getSavedNetworkSummary()
    233                 : mAccessPoint.getSettingsSummary());
    234 
    235         mContentDescription = getTitle();
    236         if (getSummary() != null) {
    237             mContentDescription = TextUtils.concat(mContentDescription, ",", getSummary());
    238         }
    239         if (level >= 0 && level < WIFI_CONNECTION_STRENGTH.length) {
    240             mContentDescription = TextUtils.concat(mContentDescription, ",",
    241                     getContext().getString(WIFI_CONNECTION_STRENGTH[level]));
    242         }
    243     }
    244 
    245     @Override
    246     protected void notifyChanged() {
    247         if (Looper.getMainLooper() != Looper.myLooper()) {
    248             // Let our BG thread callbacks call setTitle/setSummary.
    249             postNotifyChanged();
    250         } else {
    251             super.notifyChanged();
    252         }
    253     }
    254 
    255     public void onLevelChanged() {
    256         postNotifyChanged();
    257     }
    258 
    259     private void postNotifyChanged() {
    260         if (mTitleView != null) {
    261             mTitleView.post(mNotifyChanged);
    262         } // Otherwise we haven't been bound yet, and don't need to update.
    263     }
    264 
    265     private final Runnable mNotifyChanged = new Runnable() {
    266         @Override
    267         public void run() {
    268             notifyChanged();
    269         }
    270     };
    271 
    272     public static class UserBadgeCache {
    273         private final SparseArray<Drawable> mBadges = new SparseArray<>();
    274         private final PackageManager mPm;
    275 
    276         public UserBadgeCache(PackageManager pm) {
    277             mPm = pm;
    278         }
    279 
    280         private Drawable getUserBadge(int userId) {
    281             int index = mBadges.indexOfKey(userId);
    282             if (index < 0) {
    283                 Drawable badge = mPm.getUserBadgeForDensity(new UserHandle(userId), 0 /* dpi */);
    284                 mBadges.put(userId, badge);
    285                 return badge;
    286             }
    287             return mBadges.valueAt(index);
    288         }
    289     }
    290 }
    291