Added WifiEntryPreference as WifiEntry version of AccessPointPreference
Added WifiEntryPreference and LongPressWifiEntryPreference to replace
AccessPointPreference using WifiEntry instead of AccessPoint.
Test: atest WifiEntryPreferenceTest && manual - visual verification with
dummy entries.
Bug: 70983952
Change-Id: I7234a9371737c32ee8d585c2802e4ce03a36a441
diff --git a/packages/SettingsLib/Android.bp b/packages/SettingsLib/Android.bp
index 7760e0e..fea1831 100644
--- a/packages/SettingsLib/Android.bp
+++ b/packages/SettingsLib/Android.bp
@@ -24,6 +24,7 @@
"SettingsLibProgressBar",
"SettingsLibAdaptiveIcon",
"SettingsLibRadioButtonPreference",
+ "WifiTrackerLib",
],
// ANDROIDMK TRANSLATION ERROR: unsupported assignment to LOCAL_SHARED_JAVA_LIBRARIES
diff --git a/packages/SettingsLib/src/com/android/settingslib/wifi/LongPressWifiEntryPreference.java b/packages/SettingsLib/src/com/android/settingslib/wifi/LongPressWifiEntryPreference.java
new file mode 100644
index 0000000..503d60c
--- /dev/null
+++ b/packages/SettingsLib/src/com/android/settingslib/wifi/LongPressWifiEntryPreference.java
@@ -0,0 +1,46 @@
+/*
+ * Copyright (C) 2019 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.settingslib.wifi;
+
+import android.content.Context;
+
+import androidx.fragment.app.Fragment;
+import androidx.preference.PreferenceViewHolder;
+
+import com.android.wifitrackerlib.WifiEntry;
+
+/**
+ * WifiEntryPreference that can be long pressed.
+ */
+public class LongPressWifiEntryPreference extends WifiEntryPreference {
+
+ private final Fragment mFragment;
+
+ public LongPressWifiEntryPreference(Context context, WifiEntry wifiEntry, Fragment fragment) {
+ super(context, wifiEntry);
+ mFragment = fragment;
+ }
+
+ @Override
+ public void onBindViewHolder(final PreferenceViewHolder view) {
+ super.onBindViewHolder(view);
+ if (mFragment != null) {
+ view.itemView.setOnCreateContextMenuListener(mFragment);
+ view.itemView.setTag(this);
+ view.itemView.setLongClickable(true);
+ }
+ }
+}
diff --git a/packages/SettingsLib/src/com/android/settingslib/wifi/WifiEntryPreference.java b/packages/SettingsLib/src/com/android/settingslib/wifi/WifiEntryPreference.java
new file mode 100644
index 0000000..22f47f1
--- /dev/null
+++ b/packages/SettingsLib/src/com/android/settingslib/wifi/WifiEntryPreference.java
@@ -0,0 +1,238 @@
+/*
+ * Copyright (C) 2019 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.settingslib.wifi;
+
+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.ImageView;
+
+import androidx.annotation.NonNull;
+import androidx.annotation.VisibleForTesting;
+import androidx.preference.Preference;
+import androidx.preference.PreferenceViewHolder;
+
+import com.android.settingslib.R;
+import com.android.settingslib.Utils;
+import com.android.wifitrackerlib.WifiEntry;
+
+/**
+ * Preference to display a WifiEntry in a wifi picker.
+ */
+public class WifiEntryPreference extends Preference implements WifiEntry.WifiEntryCallback {
+
+ private static final int[] STATE_SECURED = {
+ R.attr.state_encrypted
+ };
+
+ private static final int[] STATE_METERED = {
+ R.attr.state_metered
+ };
+
+ 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 IconInjector mIconInjector;
+ private WifiEntry mWifiEntry;
+ private int mLevel = -1;
+ private CharSequence mContentDescription;
+
+ public WifiEntryPreference(@NonNull Context context, @NonNull WifiEntry wifiEntry) {
+ this(context, wifiEntry, new IconInjector(context));
+ }
+
+ @VisibleForTesting
+ WifiEntryPreference(@NonNull Context context, @NonNull WifiEntry wifiEntry,
+ @NonNull IconInjector iconInjector) {
+ super(context);
+
+ setLayoutResource(R.layout.preference_access_point);
+ setWidgetLayoutResource(R.layout.access_point_friction_widget);
+ mFrictionSld = getFrictionStateListDrawable();
+ mWifiEntry = wifiEntry;
+ mWifiEntry.setListener(this);
+ mIconInjector = iconInjector;
+ refresh();
+ }
+
+ public WifiEntry getWifiEntry() {
+ return mWifiEntry;
+ }
+
+ @Override
+ public void onBindViewHolder(final PreferenceViewHolder view) {
+ super.onBindViewHolder(view);
+ final Drawable drawable = getIcon();
+ if (drawable != null) {
+ drawable.setLevel(mLevel);
+ }
+
+ view.itemView.setContentDescription(mContentDescription);
+
+ final ImageView frictionImageView = (ImageView) view.findViewById(R.id.friction_icon);
+ bindFrictionImage(frictionImageView);
+
+ // Turn off divider
+ view.findViewById(R.id.two_target_divider).setVisibility(View.INVISIBLE);
+ }
+
+ /**
+ * Updates the title and summary; may indirectly call notifyChanged().
+ */
+ public void refresh() {
+ setTitle(mWifiEntry.getTitle());
+ final int level = mWifiEntry.getLevel();
+ if (level != mLevel) {
+ mLevel = level;
+ updateIcon(mLevel);
+ notifyChanged();
+ }
+
+ setSummary(mWifiEntry.getSummary());
+ 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
+ }
+
+ private void updateIcon(int level) {
+ if (level == -1) {
+ setIcon(null);
+ return;
+ }
+
+ final Drawable drawable = mIconInjector.getIcon(level);
+ if (drawable != null) {
+ drawable.setTintList(Utils.getColorAttr(getContext(),
+ android.R.attr.colorControlNormal));
+ setIcon(drawable);
+ } else {
+ setIcon(null);
+ }
+ }
+
+ @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)
+ && (mWifiEntry.getSecurity() != WifiEntry.SECURITY_OWE_TRANSITION)) {
+ mFrictionSld.setState(STATE_SECURED);
+ } else if (mWifiEntry.isMetered()) {
+ mFrictionSld.setState(STATE_METERED);
+ }
+ 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));
+ }
+
+
+ static class IconInjector {
+ private final Context mContext;
+
+ IconInjector(Context context) {
+ mContext = context;
+ }
+
+ public Drawable getIcon(int level) {
+ return mContext.getDrawable(Utils.getWifiIconResource(level));
+ }
+ }
+}
diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/wifi/WifiEntryPreferenceTest.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/wifi/WifiEntryPreferenceTest.java
new file mode 100644
index 0000000..752a549
--- /dev/null
+++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/wifi/WifiEntryPreferenceTest.java
@@ -0,0 +1,150 @@
+/*
+ * Copyright (C) 2019 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.settingslib.wifi;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import static org.mockito.Mockito.when;
+
+import android.content.Context;
+import android.graphics.drawable.Drawable;
+
+import com.android.wifitrackerlib.WifiEntry;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
+import org.robolectric.RobolectricTestRunner;
+import org.robolectric.RuntimeEnvironment;
+
+import java.util.ArrayList;
+import java.util.List;
+
+@RunWith(RobolectricTestRunner.class)
+public class WifiEntryPreferenceTest {
+
+ private Context mContext;
+
+ @Mock
+ private WifiEntry mMockWifiEntry;
+ @Mock
+ private WifiEntryPreference.IconInjector mMockIconInjector;
+
+ @Mock
+ private Drawable mMockDrawable0;
+ @Mock
+ private Drawable mMockDrawable1;
+ @Mock
+ private Drawable mMockDrawable2;
+ @Mock
+ private Drawable mMockDrawable3;
+ @Mock
+ private Drawable mMockDrawable4;
+
+ private static final String MOCK_TITLE = "title";
+ private static final String MOCK_SUMMARY = "summary";
+
+
+ @Before
+ public void setUp() {
+ mContext = RuntimeEnvironment.application;
+
+ MockitoAnnotations.initMocks(this);
+
+ when(mMockWifiEntry.getTitle()).thenReturn(MOCK_TITLE);
+ when(mMockWifiEntry.getSummary()).thenReturn(MOCK_SUMMARY);
+
+ when(mMockIconInjector.getIcon(0)).thenReturn(mMockDrawable0);
+ when(mMockIconInjector.getIcon(1)).thenReturn(mMockDrawable1);
+ when(mMockIconInjector.getIcon(2)).thenReturn(mMockDrawable2);
+ when(mMockIconInjector.getIcon(3)).thenReturn(mMockDrawable3);
+ when(mMockIconInjector.getIcon(4)).thenReturn(mMockDrawable4);
+ }
+
+ @Test
+ public void constructor_shouldSetWifiEntryTitleAndSummary() {
+ final WifiEntryPreference pref =
+ new WifiEntryPreference(mContext, mMockWifiEntry, mMockIconInjector);
+
+ assertThat(pref.getTitle()).isEqualTo(MOCK_TITLE);
+ assertThat(pref.getSummary()).isEqualTo(MOCK_SUMMARY);
+ }
+
+ @Test
+ public void constructor_shouldSetIcon() {
+ when(mMockWifiEntry.getLevel()).thenReturn(0);
+
+ final WifiEntryPreference pref =
+ new WifiEntryPreference(mContext, mMockWifiEntry, mMockIconInjector);
+
+ assertThat(pref.getIcon()).isEqualTo(mMockDrawable0);
+ }
+
+ @Test
+ public void titleChanged_refresh_shouldUpdateTitle() {
+ final WifiEntryPreference pref =
+ new WifiEntryPreference(mContext, mMockWifiEntry, mMockIconInjector);
+ final String updatedTitle = "updated title";
+ when(mMockWifiEntry.getTitle()).thenReturn(updatedTitle);
+
+ pref.refresh();
+
+ assertThat(pref.getTitle()).isEqualTo(updatedTitle);
+ }
+
+ @Test
+ public void summaryChanged_refresh_shouldUpdateSummary() {
+ final WifiEntryPreference pref =
+ new WifiEntryPreference(mContext, mMockWifiEntry, mMockIconInjector);
+ final String updatedSummary = "updated summary";
+ when(mMockWifiEntry.getSummary()).thenReturn(updatedSummary);
+
+ pref.refresh();
+
+ assertThat(pref.getSummary()).isEqualTo(updatedSummary);
+ }
+
+ @Test
+ public void levelChanged_refresh_shouldUpdateLevelIcon() {
+ final List<Drawable> iconList = new ArrayList<>();
+ final WifiEntryPreference pref =
+ new WifiEntryPreference(mContext, mMockWifiEntry, mMockIconInjector);
+
+ when(mMockWifiEntry.getLevel()).thenReturn(0);
+ pref.refresh();
+ iconList.add(pref.getIcon());
+ when(mMockWifiEntry.getLevel()).thenReturn(1);
+ pref.refresh();
+ iconList.add(pref.getIcon());
+ when(mMockWifiEntry.getLevel()).thenReturn(2);
+ pref.refresh();
+ iconList.add(pref.getIcon());
+ when(mMockWifiEntry.getLevel()).thenReturn(3);
+ pref.refresh();
+ iconList.add(pref.getIcon());
+ when(mMockWifiEntry.getLevel()).thenReturn(4);
+ pref.refresh();
+ iconList.add(pref.getIcon());
+ when(mMockWifiEntry.getLevel()).thenReturn(-1);
+ pref.refresh();
+ iconList.add(pref.getIcon());
+
+ assertThat(iconList).containsExactly(mMockDrawable0, mMockDrawable1,
+ mMockDrawable2, mMockDrawable3, mMockDrawable4, null);
+ }
+}