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 17package com.android.settingslib.wifi; 18 19import android.content.Context; 20import android.content.pm.PackageManager; 21import android.graphics.drawable.Drawable; 22import android.graphics.drawable.StateListDrawable; 23import android.net.wifi.WifiConfiguration; 24import android.os.Looper; 25import android.os.UserHandle; 26import android.support.v7.preference.Preference; 27import android.support.v7.preference.PreferenceViewHolder; 28import android.text.TextUtils; 29import android.util.AttributeSet; 30import android.util.SparseArray; 31import android.widget.TextView; 32import com.android.settingslib.R; 33 34public class AccessPointPreference extends Preference { 35 36 private static final int[] STATE_SECURED = { 37 R.attr.state_encrypted 38 }; 39 private static final int[] STATE_NONE = {}; 40 41 private static int[] wifi_signal_attributes = { R.attr.wifi_signal }; 42 43 private final StateListDrawable mWifiSld; 44 private final int mBadgePadding; 45 private final UserBadgeCache mBadgeCache; 46 private TextView mTitleView; 47 48 private boolean mForSavedNetworks = false; 49 private AccessPoint mAccessPoint; 50 private Drawable mBadge; 51 private int mLevel; 52 private CharSequence mContentDescription; 53 private int mDefaultIconResId; 54 55 static final int[] WIFI_CONNECTION_STRENGTH = { 56 R.string.accessibility_wifi_one_bar, 57 R.string.accessibility_wifi_two_bars, 58 R.string.accessibility_wifi_three_bars, 59 R.string.accessibility_wifi_signal_full 60 }; 61 62 // Used for dummy pref. 63 public AccessPointPreference(Context context, AttributeSet attrs) { 64 super(context, attrs); 65 mWifiSld = null; 66 mBadgePadding = 0; 67 mBadgeCache = null; 68 } 69 70 public AccessPointPreference(AccessPoint accessPoint, Context context, UserBadgeCache cache, 71 boolean forSavedNetworks) { 72 super(context); 73 mBadgeCache = cache; 74 mAccessPoint = accessPoint; 75 mForSavedNetworks = forSavedNetworks; 76 mAccessPoint.setTag(this); 77 mLevel = -1; 78 79 mWifiSld = (StateListDrawable) context.getTheme() 80 .obtainStyledAttributes(wifi_signal_attributes).getDrawable(0); 81 82 // Distance from the end of the title at which this AP's user badge should sit. 83 mBadgePadding = context.getResources() 84 .getDimensionPixelSize(R.dimen.wifi_preference_badge_padding); 85 refresh(); 86 } 87 88 public AccessPointPreference(AccessPoint accessPoint, Context context, UserBadgeCache cache, 89 int iconResId, boolean forSavedNetworks) { 90 super(context); 91 mBadgeCache = cache; 92 mAccessPoint = accessPoint; 93 mForSavedNetworks = forSavedNetworks; 94 mAccessPoint.setTag(this); 95 mLevel = -1; 96 mDefaultIconResId = iconResId; 97 98 mWifiSld = (StateListDrawable) context.getTheme() 99 .obtainStyledAttributes(wifi_signal_attributes).getDrawable(0); 100 101 // Distance from the end of the title at which this AP's user badge should sit. 102 mBadgePadding = context.getResources() 103 .getDimensionPixelSize(R.dimen.wifi_preference_badge_padding); 104 } 105 106 public AccessPoint getAccessPoint() { 107 return mAccessPoint; 108 } 109 110 @Override 111 public void onBindViewHolder(final PreferenceViewHolder view) { 112 super.onBindViewHolder(view); 113 if (mAccessPoint == null) { 114 // Used for dummy pref. 115 return; 116 } 117 Drawable drawable = getIcon(); 118 if (drawable != null) { 119 drawable.setLevel(mLevel); 120 } 121 122 mTitleView = (TextView) view.findViewById(com.android.internal.R.id.title); 123 if (mTitleView != null) { 124 // Attach to the end of the title view 125 mTitleView.setCompoundDrawablesRelativeWithIntrinsicBounds(null, null, mBadge, null); 126 mTitleView.setCompoundDrawablePadding(mBadgePadding); 127 } 128 view.itemView.setContentDescription(mContentDescription); 129 } 130 131 protected void updateIcon(int level, Context context) { 132 if (level == -1) { 133 safeSetDefaultIcon(); 134 } else { 135 if (getIcon() == null) { 136 // To avoid a drawing race condition, we first set the state (SECURE/NONE) and then 137 // set the icon (drawable) to that state's drawable. 138 // If sld is null then we are indexing and therefore do not have access to 139 // (nor need to display) the drawable. 140 if (mWifiSld != null) { 141 mWifiSld.setState((mAccessPoint.getSecurity() != AccessPoint.SECURITY_NONE) 142 ? STATE_SECURED 143 : STATE_NONE); 144 Drawable drawable = mWifiSld.getCurrent(); 145 if (!mForSavedNetworks && drawable != null) { 146 setIcon(drawable); 147 return; 148 } 149 } 150 safeSetDefaultIcon(); 151 } 152 } 153 } 154 155 private void safeSetDefaultIcon() { 156 if (mDefaultIconResId != 0) { 157 setIcon(mDefaultIconResId); 158 } else { 159 setIcon(null); 160 } 161 } 162 163 protected void updateBadge(Context context) { 164 WifiConfiguration config = mAccessPoint.getConfig(); 165 if (config != null) { 166 // Fetch badge (may be null) 167 // Get the badge using a cache since the PM will ask the UserManager for the list 168 // of profiles every time otherwise. 169 mBadge = mBadgeCache.getUserBadge(config.creatorUid); 170 } 171 } 172 173 /** 174 * Updates the title and summary; may indirectly call notifyChanged(). 175 */ 176 public void refresh() { 177 if (mForSavedNetworks) { 178 setTitle(mAccessPoint.getConfigName()); 179 } else { 180 setTitle(mAccessPoint.getSsid()); 181 } 182 183 final Context context = getContext(); 184 int level = mAccessPoint.getLevel(); 185 if (level != mLevel) { 186 mLevel = level; 187 updateIcon(mLevel, context); 188 notifyChanged(); 189 } 190 updateBadge(context); 191 192 setSummary(mForSavedNetworks ? mAccessPoint.getSavedNetworkSummary() 193 : mAccessPoint.getSettingsSummary()); 194 195 mContentDescription = getTitle(); 196 if (getSummary() != null) { 197 mContentDescription = TextUtils.concat(mContentDescription, ",", getSummary()); 198 } 199 if (level >= 0 && level < WIFI_CONNECTION_STRENGTH.length) { 200 mContentDescription = TextUtils.concat(mContentDescription, ",", 201 getContext().getString(WIFI_CONNECTION_STRENGTH[level])); 202 } 203 } 204 205 @Override 206 protected void notifyChanged() { 207 if (Looper.getMainLooper() != Looper.myLooper()) { 208 // Let our BG thread callbacks call setTitle/setSummary. 209 postNotifyChanged(); 210 } else { 211 super.notifyChanged(); 212 } 213 } 214 215 public void onLevelChanged() { 216 postNotifyChanged(); 217 } 218 219 private void postNotifyChanged() { 220 if (mTitleView != null) { 221 mTitleView.post(mNotifyChanged); 222 } // Otherwise we haven't been bound yet, and don't need to update. 223 } 224 225 private final Runnable mNotifyChanged = new Runnable() { 226 @Override 227 public void run() { 228 notifyChanged(); 229 } 230 }; 231 232 public static class UserBadgeCache { 233 private final SparseArray<Drawable> mBadges = new SparseArray<>(); 234 private final PackageManager mPm; 235 236 public UserBadgeCache(PackageManager pm) { 237 mPm = pm; 238 } 239 240 private Drawable getUserBadge(int userId) { 241 int index = mBadges.indexOfKey(userId); 242 if (index < 0) { 243 Drawable badge = mPm.getUserBadgeForDensity(new UserHandle(userId), 0 /* dpi */); 244 mBadges.put(userId, badge); 245 return badge; 246 } 247 return mBadges.valueAt(index); 248 } 249 } 250} 251