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