1 /* 2 * Copyright (C) 2019 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.res.Resources; 21 import android.content.res.TypedArray; 22 import android.graphics.drawable.Drawable; 23 import android.graphics.drawable.StateListDrawable; 24 import android.text.TextUtils; 25 import android.view.View; 26 import android.widget.ImageButton; 27 import android.widget.ImageView; 28 29 import androidx.annotation.DrawableRes; 30 import androidx.annotation.NonNull; 31 import androidx.annotation.VisibleForTesting; 32 import androidx.preference.Preference; 33 import androidx.preference.PreferenceViewHolder; 34 35 import com.android.settingslib.R; 36 import com.android.settingslib.Utils; 37 import com.android.wifitrackerlib.WifiEntry; 38 39 /** 40 * Preference to display a WifiEntry in a wifi picker. 41 */ 42 public class WifiEntryPreference extends Preference implements WifiEntry.WifiEntryCallback, 43 View.OnClickListener { 44 45 private static final int[] STATE_SECURED = { 46 R.attr.state_encrypted 47 }; 48 49 private static final int[] FRICTION_ATTRS = { 50 R.attr.wifi_friction 51 }; 52 53 // These values must be kept within [WifiEntry.WIFI_LEVEL_MIN, WifiEntry.WIFI_LEVEL_MAX] 54 private static final int[] WIFI_CONNECTION_STRENGTH = { 55 R.string.accessibility_no_wifi, 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 // StateListDrawable to display secured lock / metered "$" icon 63 @Nullable private final StateListDrawable mFrictionSld; 64 private final IconInjector mIconInjector; 65 private WifiEntry mWifiEntry; 66 private int mLevel = -1; 67 private boolean mShowX; // Shows the Wi-Fi signl icon of Pie+x when it's true. 68 private CharSequence mContentDescription; 69 private OnButtonClickListener mOnButtonClickListener; 70 WifiEntryPreference(@onNull Context context, @NonNull WifiEntry wifiEntry)71 public WifiEntryPreference(@NonNull Context context, @NonNull WifiEntry wifiEntry) { 72 this(context, wifiEntry, new IconInjector(context)); 73 } 74 75 @VisibleForTesting WifiEntryPreference(@onNull Context context, @NonNull WifiEntry wifiEntry, @NonNull IconInjector iconInjector)76 WifiEntryPreference(@NonNull Context context, @NonNull WifiEntry wifiEntry, 77 @NonNull IconInjector iconInjector) { 78 super(context); 79 80 setLayoutResource(R.layout.preference_access_point); 81 setWidgetLayoutResource(R.layout.access_point_friction_widget); 82 mFrictionSld = getFrictionStateListDrawable(); 83 mWifiEntry = wifiEntry; 84 mWifiEntry.setListener(this); 85 mIconInjector = iconInjector; 86 refresh(); 87 } 88 getWifiEntry()89 public WifiEntry getWifiEntry() { 90 return mWifiEntry; 91 } 92 93 @Override onBindViewHolder(final PreferenceViewHolder view)94 public void onBindViewHolder(final PreferenceViewHolder view) { 95 super.onBindViewHolder(view); 96 final Drawable drawable = getIcon(); 97 if (drawable != null) { 98 drawable.setLevel(mLevel); 99 } 100 101 view.itemView.setContentDescription(mContentDescription); 102 103 // Turn off divider 104 view.findViewById(R.id.two_target_divider).setVisibility(View.INVISIBLE); 105 106 // Enable the icon button when the help string in this WifiEntry is not null. 107 final ImageButton imageButton = (ImageButton) view.findViewById(R.id.icon_button); 108 final ImageView frictionImageView = (ImageView) view.findViewById( 109 R.id.friction_icon); 110 if (mWifiEntry.getHelpUriString() != null 111 && mWifiEntry.getConnectedState() == WifiEntry.CONNECTED_STATE_DISCONNECTED) { 112 final Drawable drawablehelp = getDrawable(R.drawable.ic_help); 113 drawablehelp.setTintList( 114 Utils.getColorAttr(getContext(), android.R.attr.colorControlNormal)); 115 ((ImageView) imageButton).setImageDrawable(drawablehelp); 116 imageButton.setVisibility(View.VISIBLE); 117 imageButton.setOnClickListener(this); 118 imageButton.setContentDescription( 119 getContext().getText(R.string.help_label)); 120 121 if (frictionImageView != null) { 122 frictionImageView.setVisibility(View.GONE); 123 } 124 } else { 125 imageButton.setVisibility(View.GONE); 126 127 if (frictionImageView != null) { 128 frictionImageView.setVisibility(View.VISIBLE); 129 bindFrictionImage(frictionImageView); 130 } 131 } 132 } 133 134 /** 135 * Updates the title and summary; may indirectly call notifyChanged(). 136 */ refresh()137 public void refresh() { 138 setTitle(mWifiEntry.getTitle()); 139 final int level = mWifiEntry.getLevel(); 140 final boolean showX = mWifiEntry.shouldShowXLevelIcon(); 141 if (level != mLevel || showX != mShowX) { 142 mLevel = level; 143 mShowX = showX; 144 updateIcon(mShowX, mLevel); 145 notifyChanged(); 146 } 147 148 setSummary(mWifiEntry.getSummary(false /* concise */)); 149 mContentDescription = buildContentDescription(); 150 } 151 152 /** 153 * Indicates the state of the WifiEntry has changed and clients may retrieve updates through 154 * the WifiEntry getter methods. 155 */ onUpdated()156 public void onUpdated() { 157 // TODO(b/70983952): Fill this method in 158 refresh(); 159 } 160 161 /** 162 * Result of the connect request indicated by the WifiEntry.CONNECT_STATUS constants. 163 */ onConnectResult(int status)164 public void onConnectResult(int status) { 165 // TODO(b/70983952): Fill this method in 166 } 167 168 /** 169 * Result of the disconnect request indicated by the WifiEntry.DISCONNECT_STATUS constants. 170 */ onDisconnectResult(int status)171 public void onDisconnectResult(int status) { 172 // TODO(b/70983952): Fill this method in 173 } 174 175 /** 176 * Result of the forget request indicated by the WifiEntry.FORGET_STATUS constants. 177 */ onForgetResult(int status)178 public void onForgetResult(int status) { 179 // TODO(b/70983952): Fill this method in 180 } 181 182 /** 183 * Result of the sign-in request indecated by the WifiEntry.SIGNIN_STATUS constants 184 */ onSignInResult(int status)185 public void onSignInResult(int status) { 186 // TODO(b/70983952): Fill this method in 187 } 188 getIconColorAttr()189 protected int getIconColorAttr() { 190 final boolean accent = (mWifiEntry.hasInternetAccess() 191 && mWifiEntry.getConnectedState() == WifiEntry.CONNECTED_STATE_CONNECTED); 192 return accent ? android.R.attr.colorAccent : android.R.attr.colorControlNormal; 193 } 194 updateIcon(boolean showX, int level)195 private void updateIcon(boolean showX, int level) { 196 if (level == -1) { 197 setIcon(null); 198 return; 199 } 200 201 final Drawable drawable = mIconInjector.getIcon(showX, level); 202 if (drawable != null) { 203 drawable.setTint(Utils.getColorAttrDefaultColor(getContext(), getIconColorAttr())); 204 setIcon(drawable); 205 } else { 206 setIcon(null); 207 } 208 } 209 210 @Nullable getFrictionStateListDrawable()211 private StateListDrawable getFrictionStateListDrawable() { 212 TypedArray frictionSld; 213 try { 214 frictionSld = getContext().getTheme().obtainStyledAttributes(FRICTION_ATTRS); 215 } catch (Resources.NotFoundException e) { 216 // Fallback for platforms that do not need friction icon resources. 217 frictionSld = null; 218 } 219 return frictionSld != null ? (StateListDrawable) frictionSld.getDrawable(0) : null; 220 } 221 222 /** 223 * Binds the friction icon drawable using a StateListDrawable. 224 * 225 * <p>Friction icons will be rebound when notifyChange() is called, and therefore 226 * do not need to be managed in refresh()</p>. 227 */ bindFrictionImage(ImageView frictionImageView)228 private void bindFrictionImage(ImageView frictionImageView) { 229 if (frictionImageView == null || mFrictionSld == null) { 230 return; 231 } 232 if ((mWifiEntry.getSecurity() != WifiEntry.SECURITY_NONE) 233 && (mWifiEntry.getSecurity() != WifiEntry.SECURITY_OWE)) { 234 mFrictionSld.setState(STATE_SECURED); 235 } 236 frictionImageView.setImageDrawable(mFrictionSld.getCurrent()); 237 } 238 239 /** 240 * Helper method to generate content description string. 241 */ 242 @VisibleForTesting buildContentDescription()243 CharSequence buildContentDescription() { 244 final Context context = getContext(); 245 246 CharSequence contentDescription = getTitle(); 247 final CharSequence summary = getSummary(); 248 if (!TextUtils.isEmpty(summary)) { 249 contentDescription = TextUtils.concat(contentDescription, ",", summary); 250 } 251 int level = mWifiEntry.getLevel(); 252 if (level >= 0 && level < WIFI_CONNECTION_STRENGTH.length) { 253 contentDescription = TextUtils.concat(contentDescription, ",", 254 context.getString(WIFI_CONNECTION_STRENGTH[level])); 255 } 256 return TextUtils.concat(contentDescription, ",", 257 mWifiEntry.getSecurity() == WifiEntry.SECURITY_NONE 258 ? context.getString(R.string.accessibility_wifi_security_type_none) 259 : context.getString(R.string.accessibility_wifi_security_type_secured)); 260 } 261 262 263 static class IconInjector { 264 private final Context mContext; 265 IconInjector(Context context)266 IconInjector(Context context) { 267 mContext = context; 268 } 269 getIcon(boolean showX, int level)270 public Drawable getIcon(boolean showX, int level) { 271 return mContext.getDrawable(WifiUtils.getInternetIconResource(level, showX)); 272 } 273 } 274 275 /** 276 * Set listeners, who want to listen the button client event. 277 */ setOnButtonClickListener(OnButtonClickListener listener)278 public void setOnButtonClickListener(OnButtonClickListener listener) { 279 mOnButtonClickListener = listener; 280 notifyChanged(); 281 } 282 283 @Override onClick(View view)284 public void onClick(View view) { 285 if (view.getId() == R.id.icon_button) { 286 if (mOnButtonClickListener != null) { 287 mOnButtonClickListener.onButtonClick(this); 288 } 289 } 290 } 291 292 /** 293 * Callback to inform the caller that the icon button is clicked. 294 */ 295 public interface OnButtonClickListener { 296 297 /** 298 * Register to listen the button click event. 299 */ onButtonClick(WifiEntryPreference preference)300 void onButtonClick(WifiEntryPreference preference); 301 } 302 getDrawable(@rawableRes int iconResId)303 private Drawable getDrawable(@DrawableRes int iconResId) { 304 Drawable buttonIcon = null; 305 306 try { 307 buttonIcon = getContext().getDrawable(iconResId); 308 } catch (Resources.NotFoundException exception) { 309 // Do nothing 310 } 311 return buttonIcon; 312 } 313 314 } 315