From 07db40434b7ba496e473826e21e2e959fea365e5 Mon Sep 17 00:00:00 2001 From: Sundeep Ghuman Date: Mon, 27 Feb 2017 18:21:53 -0800 Subject: [PATCH] Create the Wifi Network Details Page. This page should not be searchable, as it corresponds to a specific access point, thus click on a search result would not have an access point in which to open. Fix WifiPicker bugs b/35883232 and b/35951638 that deal with UI jank. Bug: 34713316 Test: Tracked in b/35963536 and to be implemented in an immediate follow up CL. Change-Id: Iad7f26c7f04c3fc7d07a8e9188843eeb4d44cd36 --- res/drawable/ic_frequency_antenna.xml | 28 ++ res/drawable/ic_security_lock_24dp.xml | 26 ++ .../ic_settings_widget_background.xml | 24 ++ res/layout/connection_detail_pref.xml | 50 ++++ res/values/colors.xml | 3 + res/values/strings.xml | 8 + res/xml/wifi_network_details_fragment.xml | 73 +++++ .../settings/wifi/WifiDetailPreference.java | 51 ++++ .../android/settings/wifi/WifiSettings.java | 30 ++- .../WifiDetailPreferenceController.java | 250 ++++++++++++++++++ .../details/WifiNetworkDetailsFragment.java | 113 ++++++++ ...randfather_not_implementing_index_provider | 1 + 12 files changed, 647 insertions(+), 10 deletions(-) create mode 100644 res/drawable/ic_frequency_antenna.xml create mode 100644 res/drawable/ic_security_lock_24dp.xml create mode 100644 res/drawable/ic_settings_widget_background.xml create mode 100644 res/layout/connection_detail_pref.xml create mode 100644 res/xml/wifi_network_details_fragment.xml create mode 100644 src/com/android/settings/wifi/WifiDetailPreference.java create mode 100644 src/com/android/settings/wifi/details/WifiDetailPreferenceController.java create mode 100644 src/com/android/settings/wifi/details/WifiNetworkDetailsFragment.java diff --git a/res/drawable/ic_frequency_antenna.xml b/res/drawable/ic_frequency_antenna.xml new file mode 100644 index 00000000000..581b83e6077 --- /dev/null +++ b/res/drawable/ic_frequency_antenna.xml @@ -0,0 +1,28 @@ + + + + + diff --git a/res/drawable/ic_security_lock_24dp.xml b/res/drawable/ic_security_lock_24dp.xml new file mode 100644 index 00000000000..fd49b23fcf1 --- /dev/null +++ b/res/drawable/ic_security_lock_24dp.xml @@ -0,0 +1,26 @@ + + + + + diff --git a/res/drawable/ic_settings_widget_background.xml b/res/drawable/ic_settings_widget_background.xml new file mode 100644 index 00000000000..6d81d47c710 --- /dev/null +++ b/res/drawable/ic_settings_widget_background.xml @@ -0,0 +1,24 @@ + + + + diff --git a/res/layout/connection_detail_pref.xml b/res/layout/connection_detail_pref.xml new file mode 100644 index 00000000000..15f1c77f1c7 --- /dev/null +++ b/res/layout/connection_detail_pref.xml @@ -0,0 +1,50 @@ + + + + + + + + + + + + + + diff --git a/res/values/colors.xml b/res/values/colors.xml index 9e41a080b3d..ec8fca96d96 100644 --- a/res/values/colors.xml +++ b/res/values/colors.xml @@ -108,4 +108,7 @@ #fff5f5f5 + + #8A000000 + diff --git a/res/values/strings.xml b/res/values/strings.xml index f7b171fc038..22fe7f7d295 100644 --- a/res/values/strings.xml +++ b/res/values/strings.xml @@ -1836,6 +1836,14 @@ IP address + + + Subnet Mask + + DNS + + IPv6 Addresses + Saved networks diff --git a/res/xml/wifi_network_details_fragment.xml b/res/xml/wifi_network_details_fragment.xml new file mode 100644 index 00000000000..dbbf0c37708 --- /dev/null +++ b/res/xml/wifi_network_details_fragment.xml @@ -0,0 +1,73 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/com/android/settings/wifi/WifiDetailPreference.java b/src/com/android/settings/wifi/WifiDetailPreference.java new file mode 100644 index 00000000000..6d34ad1c68f --- /dev/null +++ b/src/com/android/settings/wifi/WifiDetailPreference.java @@ -0,0 +1,51 @@ +/* + * Copyright (C) 2009 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 android.content.Context; +import android.support.v7.preference.Preference; +import android.support.v7.preference.PreferenceViewHolder; +import android.util.AttributeSet; +import android.widget.TextView; + +import com.android.settings.R; + +/** + * A Preference to be used with the Wifi Network Detail Fragment that allows a summary text to be + * set inside the widget resource + */ +public class WifiDetailPreference extends Preference { + private String mDetailText; + + public WifiDetailPreference(Context context, AttributeSet attrs) { + super(context, attrs); + setWidgetLayoutResource(R.layout.preference_widget_summary); + } + + public void setDetailText(String text) { + mDetailText = text; + notifyChanged(); + } + + @Override + public void onBindViewHolder(PreferenceViewHolder view) { + super.onBindViewHolder(view); + TextView textView = ((TextView) view.findViewById(R.id.widget_summary)); + textView.setText(mDetailText); + textView.setPadding(0, 0, 10, 0); + } +} diff --git a/src/com/android/settings/wifi/WifiSettings.java b/src/com/android/settings/wifi/WifiSettings.java index 8a3c8ac9bc3..91676a13ecc 100644 --- a/src/com/android/settings/wifi/WifiSettings.java +++ b/src/com/android/settings/wifi/WifiSettings.java @@ -42,7 +42,6 @@ import android.support.annotation.VisibleForTesting; import android.support.v7.preference.Preference; import android.support.v7.preference.PreferenceCategory; import android.support.v7.preference.PreferenceManager; -import android.support.v7.preference.PreferenceViewHolder; import android.text.TextUtils; import android.util.Log; import android.view.ContextMenu; @@ -65,6 +64,7 @@ import com.android.settings.search.Indexable; import com.android.settings.search.SearchIndexableRaw; import com.android.settings.widget.SummaryUpdater.OnSummaryChangeListener; import com.android.settings.widget.SwitchBarController; +import com.android.settings.wifi.details.WifiNetworkDetailsFragment; import com.android.settingslib.RestrictedLockUtils; import com.android.settingslib.wifi.AccessPoint; import com.android.settingslib.wifi.AccessPoint.AccessPointListener; @@ -187,6 +187,7 @@ public class WifiSettings extends RestrictedSettingsFragment @Override public void onCreate(Bundle icicle) { super.onCreate(icicle); + getPreferenceManager().setPreferenceComparisonCallback( new PreferenceManager.SimplePreferenceComparisonCallback()); addPreferencesFromResource(R.xml.wifi_settings); @@ -496,6 +497,12 @@ public class WifiSettings extends RestrictedSettingsFragment @Override public boolean onPreferenceTreeClick(Preference preference) { + // If the preference has a fragment set, open that + if (preference.getFragment() != null) { + preference.setOnPreferenceClickListener(null); + return super.onPreferenceTreeClick(preference); + } + if (preference instanceof LongPressAccessPointPreference) { mSelectedAccessPoint = ((LongPressAccessPointPreference) preference).getAccessPoint(); if (mSelectedAccessPoint == null) { @@ -665,14 +672,7 @@ public class WifiSettings extends RestrictedSettingsFragment removeCachedPrefs(mAccessPointsPreferenceCategory); if (!hasAvailableAccessPoints) { setProgressBarVisible(true); - Preference pref = new Preference(getContext()) { - @Override - public void onBindViewHolder(PreferenceViewHolder holder) { - super.onBindViewHolder(holder); - // Show a line on each side of add network. - holder.setDividerAllowedBelow(true); - } - }; + Preference pref = new Preference(getPrefContext()); pref.setSelectable(false); pref.setSummary(R.string.wifi_empty_list_wifi_on); pref.setOrder(index++); @@ -752,7 +752,10 @@ public class WifiSettings extends RestrictedSettingsFragment return true; } - // Else same AP is connected, nothing to do + // Else same AP is connected, simply refresh the connected access point preference + // (first and only access point in this category). + ((LongPressAccessPointPreference) mConnectedAccessPointPreferenceCategory.getPreference(0)) + .refresh(); return true; } @@ -767,6 +770,12 @@ public class WifiSettings extends RestrictedSettingsFragment if (pref == null) { pref = createLongPressActionPointPreference(connectedAp); } + + // Save the state of the current access point in the bundle so that we can restore it + // in the Wifi Network Details Fragment + pref.getAccessPoint().saveWifiState(pref.getExtras()); + pref.setFragment(WifiNetworkDetailsFragment.class.getName()); + pref.refresh(); mConnectedAccessPointPreferenceCategory.addPreference(pref); mConnectedAccessPointPreferenceCategory.setVisible(true); @@ -862,6 +871,7 @@ public class WifiSettings extends RestrictedSettingsFragment @Override public void onConnectedChanged() { + onAccessPointsChanged(); changeNextButtonState(mWifiTracker.isConnected()); } diff --git a/src/com/android/settings/wifi/details/WifiDetailPreferenceController.java b/src/com/android/settings/wifi/details/WifiDetailPreferenceController.java new file mode 100644 index 00000000000..5f489f2029e --- /dev/null +++ b/src/com/android/settings/wifi/details/WifiDetailPreferenceController.java @@ -0,0 +1,250 @@ +/* + * Copyright (C) 2017 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.details; + +import android.content.Context; +import android.graphics.drawable.Drawable; +import android.net.ConnectivityManager; +import android.net.DhcpInfo; +import android.net.LinkProperties; +import android.net.Network; +import android.net.NetworkBadging; +import android.net.NetworkInfo; +import android.net.NetworkUtils; +import android.net.wifi.WifiConfiguration; +import android.net.wifi.WifiInfo; +import android.net.wifi.WifiManager; +import android.support.v7.preference.Preference; +import android.support.v7.preference.PreferenceCategory; +import android.support.v7.preference.PreferenceScreen; +import android.util.Log; + +import com.android.settings.R; +import com.android.settings.core.PreferenceController; +import com.android.settings.core.lifecycle.Lifecycle; +import com.android.settings.core.lifecycle.LifecycleObserver; +import com.android.settings.core.lifecycle.events.OnResume; +import com.android.settings.wifi.WifiDetailPreference; +import com.android.settingslib.wifi.AccessPoint; + +import java.net.Inet4Address; +import java.net.Inet6Address; +import java.net.InetAddress; +import java.util.List; + +/** + * Controller for logic pertaining to displaying Wifi information for the + * {@link WifiNetworkDetailsFragment}. + */ +public class WifiDetailPreferenceController extends PreferenceController implements + LifecycleObserver, OnResume { + private static final String TAG = "WifiDetailsPrefCtrl"; + + private static final String KEY_CONNECTION_DETAIL_PREF = "connection_detail"; + private static final String KEY_SIGNAL_STRENGTH_PREF = "signal_strength"; + private static final String KEY_FREQUENCY_PREF = "frequency"; + private static final String KEY_SECURITY_PREF = "security"; + private static final String KEY_IP_ADDRESS_PREF = "ip_address"; + private static final String KEY_ROUTER_PREF = "router"; + private static final String KEY_SUBNET_MASK_PREF = "subnet_mask"; + private static final String KEY_DNS_PREF = "dns"; + private static final String KEY_IPV6_ADDRESS_CATEGORY = "ipv6_details_category"; + + private AccessPoint mAccessPoint; + private NetworkInfo mNetworkInfo; + private Context mPrefContext; + private int mRssi; + private String[] mSignalStr; + private WifiConfiguration mWifiConfig; + private WifiInfo mWifiInfo; + private final WifiManager mWifiManager; + + // Preferences - in order of appearance + private Preference mConnectionDetailPref; + private WifiDetailPreference mSignalStrengthPref; + private WifiDetailPreference mFrequencyPref; + private WifiDetailPreference mSecurityPref; + private WifiDetailPreference mIpAddressPref; + private WifiDetailPreference mRouterPref; + private WifiDetailPreference mSubnetPref; + private WifiDetailPreference mDnsPref; + private PreferenceCategory mIpv6AddressCategory; + + public WifiDetailPreferenceController(AccessPoint accessPoint, Context context, + Lifecycle lifecycle, WifiManager wifiManager) { + super(context); + + mAccessPoint = accessPoint; + mNetworkInfo = accessPoint.getNetworkInfo(); + mRssi = accessPoint.getRssi(); + mSignalStr = context.getResources().getStringArray(R.array.wifi_signal); + mWifiConfig = accessPoint.getConfig(); + mWifiManager = wifiManager; + + lifecycle.addObserver(this); + } + + @Override + public boolean isAvailable() { + return true; + } + + @Override + public String getPreferenceKey() { + // Returns null since this controller contains more than one Preference + return null; + } + + @Override + public void displayPreference(PreferenceScreen screen) { + super.displayPreference(screen); + + mPrefContext = screen.getPreferenceManager().getContext(); + + mConnectionDetailPref = screen.findPreference(KEY_CONNECTION_DETAIL_PREF); + + mSignalStrengthPref = + (WifiDetailPreference) screen.findPreference(KEY_SIGNAL_STRENGTH_PREF); + mFrequencyPref = (WifiDetailPreference) screen.findPreference(KEY_FREQUENCY_PREF); + mSecurityPref = (WifiDetailPreference) screen.findPreference(KEY_SECURITY_PREF); + + mIpAddressPref = (WifiDetailPreference) screen.findPreference(KEY_IP_ADDRESS_PREF); + mRouterPref = (WifiDetailPreference) screen.findPreference(KEY_ROUTER_PREF); + mSubnetPref = (WifiDetailPreference) screen.findPreference(KEY_SUBNET_MASK_PREF); + mDnsPref = (WifiDetailPreference) screen.findPreference(KEY_DNS_PREF); + + mIpv6AddressCategory = + (PreferenceCategory) screen.findPreference(KEY_IPV6_ADDRESS_CATEGORY); + + mSecurityPref.setDetailText(mAccessPoint.getSecurityString(false /* concise */)); + } + + public WifiInfo getWifiInfo() { + return mWifiInfo; + } + + @Override + public void onResume() { + mWifiInfo = mWifiManager.getConnectionInfo(); + mWifiConfig = mWifiManager.getWifiApConfiguration(); + + refreshFromWifiInfo(); + setIpText(); + } + + private void refreshFromWifiInfo() { + if (mWifiInfo == null) { + return; + } + mAccessPoint.update(mWifiConfig, mWifiInfo, mNetworkInfo); + + int iconSignalLevel = WifiManager.calculateSignalLevel( + mWifiInfo.getRssi(), WifiManager.RSSI_LEVELS); + Drawable wifiIcon = NetworkBadging.getWifiIcon( + iconSignalLevel, NetworkBadging.BADGING_NONE, mContext.getTheme()).mutate(); + + // Connected Header Pref + mConnectionDetailPref.setIcon(wifiIcon); + mConnectionDetailPref.setTitle(mAccessPoint.getSettingsSummary()); + + // Signal Strength Pref + Drawable wifiIconDark = wifiIcon.getConstantState().newDrawable().mutate(); + wifiIconDark.setTint(mContext.getResources().getColor( + R.color.wifi_details_icon_color, mContext.getTheme())); + mSignalStrengthPref.setIcon(wifiIconDark); + + int summarySignalLevel = WifiManager.calculateSignalLevel(mRssi, mSignalStr.length); + mSignalStrengthPref.setDetailText(mSignalStr[summarySignalLevel]); + + // Frequency Pref + final int frequency = mWifiInfo.getFrequency(); + String band = null; + if (frequency >= AccessPoint.LOWER_FREQ_24GHZ + && frequency < AccessPoint.HIGHER_FREQ_24GHZ) { + band = mContext.getResources().getString(R.string.wifi_band_24ghz); + } else if (frequency >= AccessPoint.LOWER_FREQ_5GHZ + && frequency < AccessPoint.HIGHER_FREQ_5GHZ) { + band = mContext.getResources().getString(R.string.wifi_band_5ghz); + } else { + Log.e(TAG, "Unexpected frequency " + frequency); + } + mFrequencyPref.setDetailText(band); + } + + private void setIpText() { + mIpv6AddressCategory.removeAll(); + mIpv6AddressCategory.setVisible(false); + + Network currentNetwork = mWifiManager.getCurrentNetwork(); + if (currentNetwork == null) { + return; + } + + ConnectivityManager cm = mContext.getSystemService(ConnectivityManager.class); + LinkProperties prop = cm.getLinkProperties(currentNetwork); + List addresses = prop.getAllAddresses(); + + // Set ip addresses + for (int i = 0; i < addresses.size(); i++) { + InetAddress addr = addresses.get(i); + if (addr instanceof Inet4Address) { + mIpAddressPref.setDetailText(addr.getHostAddress()); + } else if (addr instanceof Inet6Address) { + String ip = addr.getHostAddress(); + Preference pref = new Preference(mPrefContext); + pref.setKey(ip); + pref.setTitle(ip); + mIpv6AddressCategory.addPreference(pref); + mIpv6AddressCategory.setVisible(true); // TODO(sghuman): Make sure to + } + } + + String subnetMask = null; + String router; + DhcpInfo dhcp = mWifiManager.getDhcpInfo(); + if (dhcp != null) { + if (dhcp.netmask == 0) { + Log.e(TAG, "invalid netmask value of 0 for DhcpInfo: " + dhcp); + mSubnetPref.setVisible(false); + } else { + subnetMask = NetworkUtils.intToInetAddress(dhcp.netmask).getHostAddress(); + mSubnetPref.setVisible(true); + } + + router = NetworkUtils.intToInetAddress(dhcp.gateway).getHostAddress(); + } else { // Statically configured IP + + // TODO(sghuman): How do we get subnet mask for static ips? + mSubnetPref.setVisible(false); + + router = mWifiManager.getWifiApConfiguration().getStaticIpConfiguration().gateway + .getHostAddress(); + } + mRouterPref.setDetailText(router); + mSubnetPref.setDetailText(subnetMask); + + // Set DNS + addresses = prop.getDnsServers(); + StringBuilder builder = new StringBuilder(); + + // addresses is backed by an ArrayList, so use a hand-written iterator for performance gains + for (int i = 0; i < addresses.size(); i++) { + if (i > 0) builder.append(", "); + builder.append(addresses.get(i).getHostAddress()); + } + mDnsPref.setDetailText(builder.toString()); + } +} diff --git a/src/com/android/settings/wifi/details/WifiNetworkDetailsFragment.java b/src/com/android/settings/wifi/details/WifiNetworkDetailsFragment.java new file mode 100644 index 00000000000..0a80f218682 --- /dev/null +++ b/src/com/android/settings/wifi/details/WifiNetworkDetailsFragment.java @@ -0,0 +1,113 @@ +/* + * Copyright (C) 2017 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.details; + +import android.content.Context; +import android.net.wifi.WifiInfo; +import android.net.wifi.WifiManager; +import android.os.Bundle; +import android.widget.Button; + +import com.android.internal.logging.nano.MetricsProto; +import com.android.settings.R; +import com.android.settings.SettingsActivity; +import com.android.settings.applications.LayoutPreference; +import com.android.settings.core.PreferenceController; +import com.android.settings.dashboard.DashboardFragment; +import com.android.settingslib.wifi.AccessPoint; + +import java.util.ArrayList; +import java.util.List; + +/** + * Detail page for the currently connected wifi network. + * + *

The AccessPoint should be saved to the intent Extras when launching this class via + * {@link AccessPoint#saveWifiState(Bundle)} in order to properly render this page. + */ +public class WifiNetworkDetailsFragment extends DashboardFragment { + private static final String TAG = "WifiNetworkDetailsFrg"; + + // XML KEYS + private static final String KEY_FORGET_BUTTON = "forget_button"; + + private AccessPoint mAccessPoint; + private Button mForgetButton; + private WifiDetailPreferenceController mWifiDetailPreferenceController; + private WifiManager mWifiManager; + + @Override + public void onAttach(Context context) { + mAccessPoint = new AccessPoint(context, getArguments()); + mWifiManager = context.getSystemService(WifiManager.class); + + super.onAttach(context); + } + + @Override + public void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + + // Header Title set automatically from launching Preference + + mForgetButton = (Button) ((LayoutPreference) findPreference(KEY_FORGET_BUTTON)) + .findViewById(R.id.button); + mForgetButton.setText(R.string.forget); + mForgetButton.setOnClickListener(view -> forgetNetwork()); + } + + private void forgetNetwork() { + WifiInfo info = mWifiDetailPreferenceController.getWifiInfo(); + mMetricsFeatureProvider.action(getActivity(), MetricsProto.MetricsEvent.ACTION_WIFI_FORGET); + if (!info.isEphemeral()) { + // Network is active but has no network ID - must be ephemeral. + mWifiManager.disableEphemeralNetwork( + AccessPoint.convertToQuotedString(info.getSSID())); + } else if (mAccessPoint.getConfig().isPasspoint()) { + mWifiManager.removePasspointConfiguration(mAccessPoint.getConfig().FQDN); + } else { + mWifiManager.forget(info.getNetworkId(), null /* action listener */); + } + } + + @Override + public int getMetricsCategory() { + return MetricsProto.MetricsEvent.WIFI_NETWORK_DETAILS; + } + + @Override + protected String getLogTag() { + return TAG; + } + + @Override + protected int getPreferenceScreenResId() { + return R.xml.wifi_network_details_fragment; + } + + @Override + protected List getPreferenceControllers(Context context) { + mWifiDetailPreferenceController = new WifiDetailPreferenceController( + mAccessPoint, + context, + getLifecycle(), + mWifiManager); + + ArrayList controllers = new ArrayList(1); + controllers.add(mWifiDetailPreferenceController); + return controllers; + } +} diff --git a/tests/robotests/assets/grandfather_not_implementing_index_provider b/tests/robotests/assets/grandfather_not_implementing_index_provider index a63bd7f4a34..778440d4990 100644 --- a/tests/robotests/assets/grandfather_not_implementing_index_provider +++ b/tests/robotests/assets/grandfather_not_implementing_index_provider @@ -5,3 +5,4 @@ com.android.settings.inputmethod.InputAndGestureSettings com.android.settings.accounts.AccountDetailDashboardFragment com.android.settings.fuelgauge.PowerUsageDetail com.android.settings.deviceinfo.StorageProfileFragment +com.android.settings.wifi.details.WifiNetworkDetailsFragment