blob: c78f28c8050cfa66bcca46230dcd3bd5844a1bf6 [file] [log] [blame]
/*
* Copyright (C) 2021 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.android.settings.wifi;
import static com.android.settingslib.wifi.WifiUtils.getHotspotIconResource;
import android.annotation.Nullable;
import android.content.Context;
import android.content.res.Resources;
import android.content.res.TypedArray;
import android.graphics.drawable.Drawable;
import android.graphics.drawable.StateListDrawable;
import android.text.TextUtils;
import android.view.View;
import android.widget.ImageButton;
import android.widget.ImageView;
import android.widget.TextView;
import androidx.annotation.DrawableRes;
import androidx.annotation.NonNull;
import androidx.annotation.VisibleForTesting;
import androidx.preference.PreferenceViewHolder;
import com.android.settingslib.R;
import com.android.settingslib.RestrictedPreference;
import com.android.settingslib.Utils;
import com.android.settingslib.wifi.WifiUtils;
import com.android.wifitrackerlib.BaseWifiTracker;
import com.android.wifitrackerlib.HotspotNetworkEntry;
import com.android.wifitrackerlib.WifiEntry;
/**
* Preference to display a WifiEntry in a wifi picker.
*/
public class WifiEntryPreference extends RestrictedPreference implements
WifiEntry.WifiEntryCallback,
View.OnClickListener {
private static final int[] STATE_SECURED = {
R.attr.state_encrypted
};
private static final int[] FRICTION_ATTRS = {
R.attr.wifi_friction
};
// These values must be kept within [WifiEntry.WIFI_LEVEL_MIN, WifiEntry.WIFI_LEVEL_MAX]
private static final int[] WIFI_CONNECTION_STRENGTH = {
R.string.accessibility_no_wifi,
R.string.accessibility_wifi_one_bar,
R.string.accessibility_wifi_two_bars,
R.string.accessibility_wifi_three_bars,
R.string.accessibility_wifi_signal_full
};
// StateListDrawable to display secured lock / metered "$" icon
@Nullable private final StateListDrawable mFrictionSld;
private final WifiUtils.InternetIconInjector mIconInjector;
private WifiEntry mWifiEntry;
private int mLevel = -1;
private boolean mShowX; // Shows the Wi-Fi signl icon of Pie+x when it's true.
private CharSequence mContentDescription;
private OnButtonClickListener mOnButtonClickListener;
public WifiEntryPreference(@NonNull Context context, @NonNull WifiEntry wifiEntry) {
this(context, wifiEntry, new WifiUtils.InternetIconInjector(context));
}
@VisibleForTesting
WifiEntryPreference(@NonNull Context context, @NonNull WifiEntry wifiEntry,
@NonNull WifiUtils.InternetIconInjector iconInjector) {
super(context);
setLayoutResource(R.layout.preference_access_point);
mFrictionSld = getFrictionStateListDrawable();
mIconInjector = iconInjector;
setWifiEntry(wifiEntry);
}
/**
* Set updated {@link WifiEntry} to refresh the preference
*
* @param wifiEntry An instance of {@link WifiEntry}
*/
public void setWifiEntry(@NonNull WifiEntry wifiEntry) {
mWifiEntry = wifiEntry;
mWifiEntry.setListener(this);
refresh();
}
public WifiEntry getWifiEntry() {
return mWifiEntry;
}
@Override
public void onBindViewHolder(final PreferenceViewHolder view) {
super.onBindViewHolder(view);
if (BaseWifiTracker.isVerboseLoggingEnabled()) {
TextView summary = (TextView) view.findViewById(android.R.id.summary);
if (summary != null) {
summary.setMaxLines(100);
}
}
final Drawable drawable = getIcon();
if (drawable != null) {
drawable.setLevel(mLevel);
}
view.itemView.setContentDescription(mContentDescription);
// Turn off divider
view.findViewById(com.android.settingslib.widget.preference.twotarget.R.id.two_target_divider)
.setVisibility(View.INVISIBLE);
// Enable the icon button when the help string in this WifiEntry is not null.
final ImageButton imageButton = (ImageButton) view.findViewById(R.id.icon_button);
final ImageView frictionImageView = (ImageView) view.findViewById(
R.id.friction_icon);
if (mWifiEntry.getHelpUriString() != null
&& mWifiEntry.getConnectedState() == WifiEntry.CONNECTED_STATE_DISCONNECTED) {
final Drawable drawablehelp = getDrawable(R.drawable.ic_help);
drawablehelp.setTintList(
Utils.getColorAttr(getContext(), android.R.attr.colorControlNormal));
((ImageView) imageButton).setImageDrawable(drawablehelp);
imageButton.setVisibility(View.VISIBLE);
imageButton.setOnClickListener(this);
imageButton.setContentDescription(
getContext().getText(R.string.help_label));
if (frictionImageView != null) {
frictionImageView.setVisibility(View.GONE);
}
} else {
imageButton.setVisibility(View.GONE);
if (frictionImageView != null) {
frictionImageView.setVisibility(View.VISIBLE);
bindFrictionImage(frictionImageView);
}
}
}
/**
* Updates the title and summary; may indirectly call notifyChanged().
*/
public void refresh() {
setTitle(mWifiEntry.getTitle());
if (mWifiEntry instanceof HotspotNetworkEntry) {
updateHotspotIcon(((HotspotNetworkEntry) mWifiEntry).getDeviceType());
} else {
mLevel = mWifiEntry.getLevel();
mShowX = mWifiEntry.shouldShowXLevelIcon();
updateIcon(mShowX, mLevel);
}
setSummary(mWifiEntry.getSummary(false /* concise */));
mContentDescription = buildContentDescription();
}
/**
* Indicates the state of the WifiEntry has changed and clients may retrieve updates through
* the WifiEntry getter methods.
*/
public void onUpdated() {
// TODO(b/70983952): Fill this method in
refresh();
}
/**
* Result of the connect request indicated by the WifiEntry.CONNECT_STATUS constants.
*/
public void onConnectResult(int status) {
// TODO(b/70983952): Fill this method in
}
/**
* Result of the disconnect request indicated by the WifiEntry.DISCONNECT_STATUS constants.
*/
public void onDisconnectResult(int status) {
// TODO(b/70983952): Fill this method in
}
/**
* Result of the forget request indicated by the WifiEntry.FORGET_STATUS constants.
*/
public void onForgetResult(int status) {
// TODO(b/70983952): Fill this method in
}
/**
* Result of the sign-in request indecated by the WifiEntry.SIGNIN_STATUS constants
*/
public void onSignInResult(int status) {
// TODO(b/70983952): Fill this method in
}
protected int getIconColorAttr() {
final boolean accent = (mWifiEntry.hasInternetAccess()
&& mWifiEntry.getConnectedState() == WifiEntry.CONNECTED_STATE_CONNECTED);
return accent ? android.R.attr.colorAccent : android.R.attr.colorControlNormal;
}
private void setIconWithTint(Drawable drawable) {
if (drawable != null) {
// Must use Drawable#setTintList() instead of Drawable#setTint() to show the grey
// icon when the preference is disabled.
drawable.setTintList(Utils.getColorAttr(getContext(), getIconColorAttr()));
setIcon(drawable);
} else {
setIcon(null);
}
}
@VisibleForTesting
void updateIcon(boolean showX, int level) {
if (level == -1) {
setIcon(null);
return;
}
setIconWithTint(mIconInjector.getIcon(showX, level));
}
@VisibleForTesting
void updateHotspotIcon(int deviceType) {
setIconWithTint(getContext().getDrawable(getHotspotIconResource(deviceType)));
}
@Nullable
private StateListDrawable getFrictionStateListDrawable() {
TypedArray frictionSld;
try {
frictionSld = getContext().getTheme().obtainStyledAttributes(FRICTION_ATTRS);
} catch (Resources.NotFoundException e) {
// Fallback for platforms that do not need friction icon resources.
frictionSld = null;
}
return frictionSld != null ? (StateListDrawable) frictionSld.getDrawable(0) : null;
}
/**
* Binds the friction icon drawable using a StateListDrawable.
*
* <p>Friction icons will be rebound when notifyChange() is called, and therefore
* do not need to be managed in refresh()</p>.
*/
private void bindFrictionImage(ImageView frictionImageView) {
if (frictionImageView == null || mFrictionSld == null) {
return;
}
if ((mWifiEntry.getSecurity() != WifiEntry.SECURITY_NONE)
&& (mWifiEntry.getSecurity() != WifiEntry.SECURITY_OWE)) {
mFrictionSld.setState(STATE_SECURED);
}
frictionImageView.setImageDrawable(mFrictionSld.getCurrent());
}
/**
* Helper method to generate content description string.
*/
@VisibleForTesting
CharSequence buildContentDescription() {
final Context context = getContext();
CharSequence contentDescription = getTitle();
final CharSequence summary = getSummary();
if (!TextUtils.isEmpty(summary)) {
contentDescription = TextUtils.concat(contentDescription, ",", summary);
}
int level = mWifiEntry.getLevel();
if (level >= 0 && level < WIFI_CONNECTION_STRENGTH.length) {
contentDescription = TextUtils.concat(contentDescription, ",",
context.getString(WIFI_CONNECTION_STRENGTH[level]));
}
return TextUtils.concat(contentDescription, ",",
mWifiEntry.getSecurity() == WifiEntry.SECURITY_NONE
? context.getString(R.string.accessibility_wifi_security_type_none)
: context.getString(R.string.accessibility_wifi_security_type_secured));
}
/**
* Set listeners, who want to listen the button client event.
*/
public void setOnButtonClickListener(OnButtonClickListener listener) {
mOnButtonClickListener = listener;
notifyChanged();
}
@Override
protected int getSecondTargetResId() {
return R.layout.access_point_friction_widget;
}
@Override
public void onClick(View view) {
if (view.getId() == R.id.icon_button) {
if (mOnButtonClickListener != null) {
mOnButtonClickListener.onButtonClick(this);
}
}
}
/**
* Callback to inform the caller that the icon button is clicked.
*/
public interface OnButtonClickListener {
/**
* Register to listen the button click event.
*/
void onButtonClick(WifiEntryPreference preference);
}
private Drawable getDrawable(@DrawableRes int iconResId) {
Drawable buttonIcon = null;
try {
buttonIcon = getContext().getDrawable(iconResId);
} catch (Resources.NotFoundException exception) {
// Do nothing
}
return buttonIcon;
}
}