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