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