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