Merge "Remove wake from Settings gestures.xml"
diff --git a/res/color/circle_outline_color.xml b/res/color/circle_outline_color.xml
new file mode 100644
index 0000000..eb4e83c
--- /dev/null
+++ b/res/color/circle_outline_color.xml
@@ -0,0 +1,20 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ 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.
+ -->
+<selector xmlns:android="http://schemas.android.com/apk/res/android">
+ <item android:alpha="0.14"
+ android:color="?android:attr/colorForeground"/>
+</selector>
\ No newline at end of file
diff --git a/res/drawable/circle_outline.xml b/res/drawable/circle_outline.xml
new file mode 100644
index 0000000..1b2631d
--- /dev/null
+++ b/res/drawable/circle_outline.xml
@@ -0,0 +1,22 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ 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.
+ -->
+<shape xmlns:android="http://schemas.android.com/apk/res/android"
+ android:shape="oval">
+ <stroke
+ android:width="1dp"
+ android:color="@color/circle_outline_color"/>
+</shape>
diff --git a/res/layout/advanced_bt_entity_sub.xml b/res/layout/advanced_bt_entity_sub.xml
index 07ea814..0f30583 100644
--- a/res/layout/advanced_bt_entity_sub.xml
+++ b/res/layout/advanced_bt_entity_sub.xml
@@ -26,9 +26,11 @@
android:id="@+id/header_icon"
android:layout_width="72dp"
android:layout_height="72dp"
- android:scaleType="fitCenter"
android:layout_gravity="center_horizontal"
- android:antialias="true"/>
+ android:antialias="true"
+ android:background="@drawable/circle_outline"
+ android:padding="8dp"
+ android:scaleType="fitCenter"/>
<TextView
android:id="@+id/header_title"
diff --git a/res/layout/time_zone_search_header.xml b/res/layout/time_zone_search_header.xml
new file mode 100644
index 0000000..5c4e0ee
--- /dev/null
+++ b/res/layout/time_zone_search_header.xml
@@ -0,0 +1,28 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ 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.
+ -->
+<!-- similar to preference_material.xml but textview for emoji country flag
+instead of an ImageView -->
+
+<TextView xmlns:android="http://schemas.android.com/apk/res/android"
+ android:id="@+android:id/title"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:layout_marginBottom="16dp"
+ android:layout_marginTop="16dp"
+ android:textAppearance="@style/TextAppearance.CategoryTitle"
+ android:textColor="?android:attr/colorAccent"
+ android:paddingStart="@dimen/preference_no_icon_padding_start"/>
diff --git a/res/values/strings.xml b/res/values/strings.xml
index e4d0d11..61b3366 100644
--- a/res/values/strings.xml
+++ b/res/values/strings.xml
@@ -7939,6 +7939,9 @@
<!-- Sound & notification > Advanced section: Title for managing Do Not Disturb access option. [CHAR LIMIT=40] -->
<string name="manage_zen_access_title">Do Not Disturb access</string>
+ <!-- Button title that grants 'Do Not Disturb' permission to an app [CHAR_LIMIT=60]-->
+ <string name="zen_access_detail_switch">Allow Do Not Disturb</string>
+
<!-- Sound & notification > Do Not Disturb access > Text to display when the list is empty. [CHAR LIMIT=NONE] -->
<string name="zen_access_empty_text">No installed apps have requested Do Not Disturb access</string>
@@ -10759,7 +10762,7 @@
</plurals>
<!-- Title for notification channel slice. [CHAR LIMIT=NONE] -->
- <string name="manage_app_notification">Manage <xliff:g id="app_name" example="Settings">%1$s</xliff:g> Notifications</string>
+ <string name="manage_app_notification">Manage <xliff:g id="app_name" example="Settings">%1$s</xliff:g> notifications</string>
<!-- Title for no suggested app in notification channel slice. [CHAR LIMIT=NONE] -->
<string name="no_suggested_app">No suggested application</string>
<!-- Summary for the channels count is equal or less than 3 in notification channel slice. [CHAR LIMIT=NONE] -->
@@ -10769,6 +10772,8 @@
</plurals>
<!-- Summary for the channels count is more than 3 in notification channel slice. [CHAR LIMIT=NONE] -->
<string name="notification_many_channel_count_summary"><xliff:g id="notification_channel_count" example="4">%1$d</xliff:g> notification channels. Tap to manage all.</string>
+ <!-- Summary for recently installed app in contextual notification channel slice. [CHAR LIMIT=NONE] -->
+ <string name="recently_installed_app">You recently installed this app.</string>
<!-- Title for the Switch output dialog (settings panel) with media related devices [CHAR LIMIT=50] -->
<string name="media_output_panel_title">Switch output</string>
diff --git a/res/xml/zen_access_permission_details.xml b/res/xml/zen_access_permission_details.xml
new file mode 100644
index 0000000..afa8d80
--- /dev/null
+++ b/res/xml/zen_access_permission_details.xml
@@ -0,0 +1,27 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ 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.
+ -->
+
+<PreferenceScreen
+ xmlns:android="http://schemas.android.com/apk/res/android"
+ android:key="zen_access_permission_detail_settings"
+ android:title="@string/manage_zen_access_title">
+
+ <SwitchPreference
+ android:key="zen_access_switch"
+ android:title="@string/zen_access_detail_switch"/>
+
+</PreferenceScreen>
\ No newline at end of file
diff --git a/src/com/android/settings/applications/specialaccess/zenaccess/FriendlyWarningDialogFragment.java b/src/com/android/settings/applications/specialaccess/zenaccess/FriendlyWarningDialogFragment.java
new file mode 100644
index 0000000..fc85f7d
--- /dev/null
+++ b/src/com/android/settings/applications/specialaccess/zenaccess/FriendlyWarningDialogFragment.java
@@ -0,0 +1,76 @@
+/*
+ * 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.settings.applications.specialaccess.zenaccess;
+
+import android.app.Dialog;
+import android.app.settings.SettingsEnums;
+import android.os.Bundle;
+import android.text.TextUtils;
+
+import androidx.appcompat.app.AlertDialog;
+
+import com.android.settings.R;
+import com.android.settings.core.instrumentation.InstrumentedDialogFragment;
+
+/**
+ * Warning dialog when revoking zen access warning that zen rule instances will be deleted.
+ */
+public class FriendlyWarningDialogFragment extends InstrumentedDialogFragment {
+ static final String KEY_PKG = "p";
+ static final String KEY_LABEL = "l";
+
+
+ @Override
+ public int getMetricsCategory() {
+ return SettingsEnums.DIALOG_ZEN_ACCESS_REVOKE;
+ }
+
+ public FriendlyWarningDialogFragment setPkgInfo(String pkg, CharSequence label) {
+ Bundle args = new Bundle();
+ args.putString(KEY_PKG, pkg);
+ args.putString(KEY_LABEL, TextUtils.isEmpty(label) ? pkg : label.toString());
+ setArguments(args);
+ return this;
+ }
+
+ @Override
+ public Dialog onCreateDialog(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+ final Bundle args = getArguments();
+ final String pkg = args.getString(KEY_PKG);
+ final String label = args.getString(KEY_LABEL);
+
+ final String title = getResources().getString(
+ R.string.zen_access_revoke_warning_dialog_title, label);
+ final String summary = getResources()
+ .getString(R.string.zen_access_revoke_warning_dialog_summary);
+ return new AlertDialog.Builder(getContext())
+ .setMessage(summary)
+ .setTitle(title)
+ .setCancelable(true)
+ .setPositiveButton(R.string.okay,
+ (dialog, id) -> {
+ ZenAccessController.deleteRules(getContext(), pkg);
+ ZenAccessController.setAccess(getContext(), pkg, false);
+ })
+ .setNegativeButton(R.string.cancel,
+ (dialog, id) -> {
+ // pass
+ })
+ .create();
+ }
+}
diff --git a/src/com/android/settings/applications/specialaccess/zenaccess/ScaryWarningDialogFragment.java b/src/com/android/settings/applications/specialaccess/zenaccess/ScaryWarningDialogFragment.java
new file mode 100644
index 0000000..69318f8
--- /dev/null
+++ b/src/com/android/settings/applications/specialaccess/zenaccess/ScaryWarningDialogFragment.java
@@ -0,0 +1,73 @@
+/*
+ * 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.settings.applications.specialaccess.zenaccess;
+
+import android.app.Dialog;
+import android.app.settings.SettingsEnums;
+import android.os.Bundle;
+import android.text.TextUtils;
+
+import androidx.appcompat.app.AlertDialog;
+
+import com.android.settings.R;
+import com.android.settings.core.instrumentation.InstrumentedDialogFragment;
+import com.android.settings.notification.ZenAccessSettings;
+
+/**
+ * Warning dialog when allowing zen access warning about the privileges being granted.
+ */
+public class ScaryWarningDialogFragment extends InstrumentedDialogFragment {
+ static final String KEY_PKG = "p";
+ static final String KEY_LABEL = "l";
+
+ @Override
+ public int getMetricsCategory() {
+ return SettingsEnums.DIALOG_ZEN_ACCESS_GRANT;
+ }
+
+ public ScaryWarningDialogFragment setPkgInfo(String pkg, CharSequence label) {
+ Bundle args = new Bundle();
+ args.putString(KEY_PKG, pkg);
+ args.putString(KEY_LABEL, TextUtils.isEmpty(label) ? pkg : label.toString());
+ setArguments(args);
+ return this;
+ }
+
+ @Override
+ public Dialog onCreateDialog(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+ final Bundle args = getArguments();
+ final String pkg = args.getString(KEY_PKG);
+ final String label = args.getString(KEY_LABEL);
+
+ final String title = getResources().getString(R.string.zen_access_warning_dialog_title,
+ label);
+ final String summary = getResources()
+ .getString(R.string.zen_access_warning_dialog_summary);
+ return new AlertDialog.Builder(getContext())
+ .setMessage(summary)
+ .setTitle(title)
+ .setCancelable(true)
+ .setPositiveButton(R.string.allow,
+ (dialog, id) -> ZenAccessController.setAccess(getContext(), pkg, true))
+ .setNegativeButton(R.string.deny,
+ (dialog, id) -> {
+ // pass
+ })
+ .create();
+ }
+}
diff --git a/src/com/android/settings/applications/specialaccess/zenaccess/ZenAccessController.java b/src/com/android/settings/applications/specialaccess/zenaccess/ZenAccessController.java
index 88d444d..946599b 100644
--- a/src/com/android/settings/applications/specialaccess/zenaccess/ZenAccessController.java
+++ b/src/com/android/settings/applications/specialaccess/zenaccess/ZenAccessController.java
@@ -17,12 +17,29 @@
package com.android.settings.applications.specialaccess.zenaccess;
import android.app.ActivityManager;
+import android.app.AppGlobals;
+import android.app.NotificationManager;
+import android.app.settings.SettingsEnums;
import android.content.Context;
+import android.content.pm.PackageInfo;
+import android.content.pm.ParceledListSlice;
+import android.os.AsyncTask;
+import android.os.RemoteException;
+import android.util.ArraySet;
+import android.util.Log;
+
+import androidx.annotation.VisibleForTesting;
import com.android.settings.core.BasePreferenceController;
+import com.android.settings.overlay.FeatureFactory;
+
+import java.util.List;
+import java.util.Set;
public class ZenAccessController extends BasePreferenceController {
+ private static final String TAG = "ZenAccessController";
+
private final ActivityManager mActivityManager;
public ZenAccessController(Context context, String preferenceKey) {
@@ -32,8 +49,68 @@
@Override
public int getAvailabilityStatus() {
- return !mActivityManager.isLowRamDevice()
+ return isSupported(mActivityManager)
? AVAILABLE_UNSEARCHABLE
: UNSUPPORTED_ON_DEVICE;
}
+
+ public static boolean isSupported(ActivityManager activityManager) {
+ return !activityManager.isLowRamDevice();
+ }
+
+ public static Set<String> getPackagesRequestingNotificationPolicyAccess() {
+ final ArraySet<String> requestingPackages = new ArraySet<>();
+ try {
+ final String[] PERM = {
+ android.Manifest.permission.ACCESS_NOTIFICATION_POLICY
+ };
+ final ParceledListSlice list = AppGlobals.getPackageManager()
+ .getPackagesHoldingPermissions(PERM, 0 /*flags*/,
+ ActivityManager.getCurrentUser());
+ final List<PackageInfo> pkgs = list.getList();
+ if (pkgs != null) {
+ for (PackageInfo info : pkgs) {
+ requestingPackages.add(info.packageName);
+ }
+ }
+ } catch (RemoteException e) {
+ Log.e(TAG, "Cannot reach packagemanager", e);
+ }
+ return requestingPackages;
+ }
+
+ public static Set<String> getAutoApprovedPackages(Context context) {
+ final Set<String> autoApproved = new ArraySet<>();
+ autoApproved.addAll(context.getSystemService(NotificationManager.class)
+ .getEnabledNotificationListenerPackages());
+ return autoApproved;
+ }
+
+ public static boolean hasAccess(Context context, String pkg) {
+ return context.getSystemService(
+ NotificationManager.class).isNotificationPolicyAccessGrantedForPackage(pkg);
+ }
+
+ public static void setAccess(final Context context, final String pkg, final boolean access) {
+ logSpecialPermissionChange(access, pkg, context);
+ AsyncTask.execute(() -> {
+ final NotificationManager mgr = context.getSystemService(NotificationManager.class);
+ mgr.setNotificationPolicyAccessGranted(pkg, access);
+ });
+ }
+
+ public static void deleteRules(final Context context, final String pkg) {
+ AsyncTask.execute(() -> {
+ final NotificationManager mgr = context.getSystemService(NotificationManager.class);
+ mgr.removeAutomaticZenRules(pkg);
+ });
+ }
+
+ @VisibleForTesting
+ static void logSpecialPermissionChange(boolean enable, String packageName, Context context) {
+ int logCategory = enable ? SettingsEnums.APP_SPECIAL_PERMISSION_DND_ALLOW
+ : SettingsEnums.APP_SPECIAL_PERMISSION_DND_DENY;
+ FeatureFactory.getFactory(context).getMetricsFeatureProvider().action(context,
+ logCategory, packageName);
+ }
}
diff --git a/src/com/android/settings/applications/specialaccess/zenaccess/ZenAccessDetails.java b/src/com/android/settings/applications/specialaccess/zenaccess/ZenAccessDetails.java
new file mode 100644
index 0000000..a18e7d6
--- /dev/null
+++ b/src/com/android/settings/applications/specialaccess/zenaccess/ZenAccessDetails.java
@@ -0,0 +1,100 @@
+/*
+ * 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.settings.applications.specialaccess.zenaccess;
+
+import android.app.ActivityManager;
+import android.app.settings.SettingsEnums;
+import android.content.Context;
+import android.os.Bundle;
+
+import androidx.appcompat.app.AlertDialog;
+import androidx.preference.SwitchPreference;
+
+import com.android.settings.R;
+import com.android.settings.applications.AppInfoWithHeader;
+
+import java.util.Set;
+
+public class ZenAccessDetails extends AppInfoWithHeader implements
+ ZenAccessSettingObserverMixin.Listener {
+
+ private static final String SWITCH_PREF_KEY = "zen_access_switch";
+
+ @Override
+ public int getMetricsCategory() {
+ return SettingsEnums.ZEN_ACCESS_DETAIL;
+ }
+
+ @Override
+ public void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+ addPreferencesFromResource(R.xml.zen_access_permission_details);
+ getSettingsLifecycle().addObserver(
+ new ZenAccessSettingObserverMixin(getContext(), this /* listener */));
+ }
+
+ @Override
+ protected boolean refreshUi() {
+ final Context context = getContext();
+ if (!ZenAccessController.isSupported(context.getSystemService(ActivityManager.class))) {
+ return false;
+ }
+ // If this app didn't declare this permission in their manifest, don't bother showing UI.
+ final Set<String> needAccessApps =
+ ZenAccessController.getPackagesRequestingNotificationPolicyAccess();
+ if (!needAccessApps.contains(mPackageName)) {
+ return false;
+ }
+ updatePreference(context, findPreference(SWITCH_PREF_KEY));
+ return true;
+ }
+
+ @Override
+ protected AlertDialog createDialog(int id, int errorCode) {
+ return null;
+ }
+
+ public void updatePreference(Context context, SwitchPreference preference) {
+ final CharSequence label = mPackageInfo.applicationInfo.loadLabel(mPm);
+ final Set<String> autoApproved = ZenAccessController.getAutoApprovedPackages(context);
+ if (autoApproved.contains(mPackageName)) {
+ //Auto approved, user cannot do anything. Hard code summary and disable preference.
+ preference.setEnabled(false);
+ preference.setSummary(getString(R.string.zen_access_disabled_package_warning));
+ return;
+ }
+ preference.setChecked(ZenAccessController.hasAccess(context, mPackageName));
+ preference.setOnPreferenceChangeListener((p, newValue) -> {
+ final boolean access = (Boolean) newValue;
+ if (access) {
+ new ScaryWarningDialogFragment()
+ .setPkgInfo(mPackageName, label)
+ .show(getFragmentManager(), "dialog");
+ } else {
+ new FriendlyWarningDialogFragment()
+ .setPkgInfo(mPackageName, label)
+ .show(getFragmentManager(), "dialog");
+ }
+ return false;
+ });
+ }
+
+ @Override
+ public void onZenAccessPolicyChanged() {
+ refreshUi();
+ }
+}
diff --git a/src/com/android/settings/applications/specialaccess/zenaccess/ZenAccessSettingObserverMixin.java b/src/com/android/settings/applications/specialaccess/zenaccess/ZenAccessSettingObserverMixin.java
new file mode 100644
index 0000000..30507ef
--- /dev/null
+++ b/src/com/android/settings/applications/specialaccess/zenaccess/ZenAccessSettingObserverMixin.java
@@ -0,0 +1,77 @@
+/*
+ * 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.settings.applications.specialaccess.zenaccess;
+
+import android.app.ActivityManager;
+import android.content.Context;
+import android.database.ContentObserver;
+import android.net.Uri;
+import android.os.Handler;
+import android.os.Looper;
+import android.provider.Settings;
+
+import com.android.settingslib.core.lifecycle.LifecycleObserver;
+import com.android.settingslib.core.lifecycle.events.OnStart;
+import com.android.settingslib.core.lifecycle.events.OnStop;
+
+public class ZenAccessSettingObserverMixin extends ContentObserver implements LifecycleObserver,
+ OnStart, OnStop {
+
+ public interface Listener {
+ void onZenAccessPolicyChanged();
+ }
+
+ private final Context mContext;
+ private final Listener mListener;
+
+ public ZenAccessSettingObserverMixin(Context context, Listener listener) {
+ super(new Handler(Looper.getMainLooper()));
+ mContext = context;
+ mListener = listener;
+ }
+
+ @Override
+ public void onChange(boolean selfChange, Uri uri) {
+ if (mListener != null) {
+ mListener.onZenAccessPolicyChanged();
+ }
+ }
+
+ @Override
+ public void onStart() {
+ if (!ZenAccessController.isSupported(mContext.getSystemService(ActivityManager.class))) {
+ return;
+ }
+ mContext.getContentResolver().registerContentObserver(
+ Settings.Secure.getUriFor(
+ Settings.Secure.ENABLED_NOTIFICATION_POLICY_ACCESS_PACKAGES),
+ false /* notifyForDescendants */,
+ this /* observer */);
+ mContext.getContentResolver().registerContentObserver(
+ Settings.Secure.getUriFor(Settings.Secure.ENABLED_NOTIFICATION_LISTENERS),
+ false /* notifyForDescendants */,
+ this /* observer */);
+ }
+
+ @Override
+ public void onStop() {
+ if (!ZenAccessController.isSupported(mContext.getSystemService(ActivityManager.class))) {
+ return;
+ }
+ mContext.getContentResolver().unregisterContentObserver(this /* observer */);
+ }
+}
diff --git a/src/com/android/settings/datetime/timezone/BaseTimeZoneAdapter.java b/src/com/android/settings/datetime/timezone/BaseTimeZoneAdapter.java
index ff980b2..66735c8 100644
--- a/src/com/android/settings/datetime/timezone/BaseTimeZoneAdapter.java
+++ b/src/com/android/settings/datetime/timezone/BaseTimeZoneAdapter.java
@@ -77,9 +77,10 @@
@Override
public RecyclerView.ViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {
LayoutInflater inflater = LayoutInflater.from(parent.getContext());
- switch(viewType) {
+ switch (viewType) {
case TYPE_HEADER: {
- final View view = inflater.inflate(R.layout.preference_category_material,
+ final View view = inflater.inflate(
+ R.layout.time_zone_search_header,
parent, false);
return new HeaderViewHolder(view);
}
@@ -136,7 +137,8 @@
return mShowHeader && position == 0;
}
- public @NonNull ArrayFilter getFilter() {
+ @NonNull
+ public ArrayFilter getFilter() {
if (mFilter == null) {
mFilter = new ArrayFilter();
}
@@ -153,14 +155,18 @@
public interface AdapterItem {
CharSequence getTitle();
+
CharSequence getSummary();
+
String getIconText();
+
String getCurrentTime();
/**
* @return unique non-negative number
*/
long getItemId();
+
String[] getSearchKeys();
}
diff --git a/src/com/android/settings/notification/ZenAccessSettings.java b/src/com/android/settings/notification/ZenAccessSettings.java
index d057c75..fca8255 100644
--- a/src/com/android/settings/notification/ZenAccessSettings.java
+++ b/src/com/android/settings/notification/ZenAccessSettings.java
@@ -18,56 +18,40 @@
import android.annotation.Nullable;
import android.app.ActivityManager;
-import android.app.AppGlobals;
-import android.app.Dialog;
import android.app.NotificationManager;
import android.app.settings.SettingsEnums;
import android.content.Context;
-import android.content.DialogInterface;
import android.content.pm.ApplicationInfo;
-import android.content.pm.PackageInfo;
import android.content.pm.PackageItemInfo;
import android.content.pm.PackageManager;
-import android.content.pm.ParceledListSlice;
-import android.database.ContentObserver;
-import android.net.Uri;
-import android.os.AsyncTask;
import android.os.Bundle;
-import android.os.Handler;
-import android.os.Looper;
-import android.os.RemoteException;
import android.provider.SearchIndexableResource;
-import android.provider.Settings.Secure;
-import android.text.TextUtils;
import android.util.ArraySet;
-import android.util.Log;
import android.view.View;
-import androidx.annotation.VisibleForTesting;
-import androidx.appcompat.app.AlertDialog;
-import androidx.preference.Preference;
-import androidx.preference.Preference.OnPreferenceChangeListener;
import androidx.preference.PreferenceScreen;
-import androidx.preference.SwitchPreference;
import com.android.settings.R;
-import com.android.settings.core.instrumentation.InstrumentedDialogFragment;
-import com.android.settings.overlay.FeatureFactory;
+import com.android.settings.applications.AppInfoBase;
+import com.android.settings.applications.specialaccess.zenaccess.ZenAccessController;
+import com.android.settings.applications.specialaccess.zenaccess.ZenAccessDetails;
+import com.android.settings.applications.specialaccess.zenaccess.ZenAccessSettingObserverMixin;
import com.android.settings.search.BaseSearchIndexProvider;
import com.android.settings.search.Indexable;
-import com.android.settings.widget.AppSwitchPreference;
import com.android.settings.widget.EmptyTextSettings;
import com.android.settingslib.search.SearchIndexable;
+import com.android.settingslib.widget.apppreference.AppPreference;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
+import java.util.Set;
@SearchIndexable
-public class ZenAccessSettings extends EmptyTextSettings {
+public class ZenAccessSettings extends EmptyTextSettings implements
+ ZenAccessSettingObserverMixin.Listener {
private final String TAG = "ZenAccessSettings";
- private final SettingObserver mObserver = new SettingObserver();
private Context mContext;
private PackageManager mPkgMan;
private NotificationManager mNoMan;
@@ -84,6 +68,8 @@
mContext = getActivity();
mPkgMan = mContext.getPackageManager();
mNoMan = mContext.getSystemService(NotificationManager.class);
+ getSettingsLifecycle().addObserver(
+ new ZenAccessSettingObserverMixin(getContext(), this /* listener */));
}
@Override
@@ -102,30 +88,22 @@
super.onResume();
if (!ActivityManager.isLowRamDeviceStatic()) {
reloadList();
- getContentResolver().registerContentObserver(
- Secure.getUriFor(Secure.ENABLED_NOTIFICATION_POLICY_ACCESS_PACKAGES), false,
- mObserver);
- getContentResolver().registerContentObserver(
- Secure.getUriFor(Secure.ENABLED_NOTIFICATION_LISTENERS), false,
- mObserver);
} else {
setEmptyText(R.string.disabled_low_ram_device);
}
}
@Override
- public void onPause() {
- super.onPause();
- if (!ActivityManager.isLowRamDeviceStatic()) {
- getContentResolver().unregisterContentObserver(mObserver);
- }
+ public void onZenAccessPolicyChanged() {
+ reloadList();
}
private void reloadList() {
final PreferenceScreen screen = getPreferenceScreen();
screen.removeAll();
final ArrayList<ApplicationInfo> apps = new ArrayList<>();
- final ArraySet<String> requesting = getPackagesRequestingNotificationPolicyAccess();
+ final Set<String> requesting =
+ ZenAccessController.getPackagesRequestingNotificationPolicyAccess();
if (!requesting.isEmpty()) {
final List<ApplicationInfo> installed = mPkgMan.getInstalledApplications(0);
if (installed != null) {
@@ -143,204 +121,42 @@
for (ApplicationInfo app : apps) {
final String pkg = app.packageName;
final CharSequence label = app.loadLabel(mPkgMan);
- final SwitchPreference pref = new AppSwitchPreference(getPrefContext());
+ final AppPreference pref = new AppPreference(getPrefContext());
pref.setKey(pkg);
- pref.setPersistent(false);
pref.setIcon(app.loadIcon(mPkgMan));
pref.setTitle(label);
- pref.setChecked(hasAccess(pkg));
if (autoApproved.contains(pkg)) {
+ //Auto approved, user cannot do anything. Hard code summary and disable preference.
pref.setEnabled(false);
pref.setSummary(getString(R.string.zen_access_disabled_package_warning));
+ } else {
+ // Not auto approved, update summary according to notification backend.
+ pref.setSummary(getPreferenceSummary(pkg));
}
- pref.setOnPreferenceChangeListener(new OnPreferenceChangeListener() {
- @Override
- public boolean onPreferenceChange(Preference preference, Object newValue) {
- final boolean access = (Boolean) newValue;
- if (access) {
- new ScaryWarningDialogFragment()
- .setPkgInfo(pkg, label)
- .show(getFragmentManager(), "dialog");
- } else {
- new FriendlyWarningDialogFragment()
- .setPkgInfo(pkg, label)
- .show(getFragmentManager(), "dialog");
- }
- return false;
- }
+ pref.setOnPreferenceClickListener(preference -> {
+ AppInfoBase.startAppInfoFragment(
+ ZenAccessDetails.class /* fragment */,
+ R.string.manage_zen_access_title /* titleRes */,
+ pkg,
+ app.uid,
+ this /* source */,
+ -1 /* requestCode */,
+ getMetricsCategory() /* sourceMetricsCategory */);
+ return true;
});
+
screen.addPreference(pref);
}
}
- private ArraySet<String> getPackagesRequestingNotificationPolicyAccess() {
- ArraySet<String> requestingPackages = new ArraySet<>();
- try {
- final String[] PERM = {
- android.Manifest.permission.ACCESS_NOTIFICATION_POLICY
- };
- final ParceledListSlice list = AppGlobals.getPackageManager()
- .getPackagesHoldingPermissions(PERM, 0 /*flags*/,
- ActivityManager.getCurrentUser());
- final List<PackageInfo> pkgs = list.getList();
- if (pkgs != null) {
- for (PackageInfo info : pkgs) {
- requestingPackages.add(info.packageName);
- }
- }
- } catch(RemoteException e) {
- Log.e(TAG, "Cannot reach packagemanager", e);
- }
- return requestingPackages;
- }
-
- private boolean hasAccess(String pkg) {
- return mNoMan.isNotificationPolicyAccessGrantedForPackage(pkg);
- }
-
- private static void setAccess(final Context context, final String pkg, final boolean access) {
- logSpecialPermissionChange(access, pkg, context);
- AsyncTask.execute(new Runnable() {
- @Override
- public void run() {
- final NotificationManager mgr = context.getSystemService(NotificationManager.class);
- mgr.setNotificationPolicyAccessGranted(pkg, access);
- }
- });
- }
-
- @VisibleForTesting
- static void logSpecialPermissionChange(boolean enable, String packageName, Context context) {
- int logCategory = enable ? SettingsEnums.APP_SPECIAL_PERMISSION_DND_ALLOW
- : SettingsEnums.APP_SPECIAL_PERMISSION_DND_DENY;
- FeatureFactory.getFactory(context).getMetricsFeatureProvider().action(context,
- logCategory, packageName);
- }
-
-
- private static void deleteRules(final Context context, final String pkg) {
- AsyncTask.execute(new Runnable() {
- @Override
- public void run() {
- final NotificationManager mgr = context.getSystemService(NotificationManager.class);
- mgr.removeAutomaticZenRules(pkg);
- }
- });
- }
-
- private final class SettingObserver extends ContentObserver {
- public SettingObserver() {
- super(new Handler(Looper.getMainLooper()));
- }
-
- @Override
- public void onChange(boolean selfChange, Uri uri) {
- reloadList();
- }
- }
-
/**
- * Warning dialog when allowing zen access warning about the privileges being granted.
+ * @return the summary for the current state of whether the app associated with the given
+ * {@param packageName} is allowed to enter picture-in-picture.
*/
- public static class ScaryWarningDialogFragment extends InstrumentedDialogFragment {
- static final String KEY_PKG = "p";
- static final String KEY_LABEL = "l";
-
- @Override
- public int getMetricsCategory() {
- return SettingsEnums.DIALOG_ZEN_ACCESS_GRANT;
- }
-
- public ScaryWarningDialogFragment setPkgInfo(String pkg, CharSequence label) {
- Bundle args = new Bundle();
- args.putString(KEY_PKG, pkg);
- args.putString(KEY_LABEL, TextUtils.isEmpty(label) ? pkg : label.toString());
- setArguments(args);
- return this;
- }
-
- @Override
- public Dialog onCreateDialog(Bundle savedInstanceState) {
- super.onCreate(savedInstanceState);
- final Bundle args = getArguments();
- final String pkg = args.getString(KEY_PKG);
- final String label = args.getString(KEY_LABEL);
-
- final String title = getResources().getString(R.string.zen_access_warning_dialog_title,
- label);
- final String summary = getResources()
- .getString(R.string.zen_access_warning_dialog_summary);
- return new AlertDialog.Builder(getContext())
- .setMessage(summary)
- .setTitle(title)
- .setCancelable(true)
- .setPositiveButton(R.string.allow,
- new DialogInterface.OnClickListener() {
- public void onClick(DialogInterface dialog, int id) {
- setAccess(getContext(), pkg, true);
- }
- })
- .setNegativeButton(R.string.deny,
- new DialogInterface.OnClickListener() {
- public void onClick(DialogInterface dialog, int id) {
- // pass
- }
- })
- .create();
- }
- }
-
- /**
- * Warning dialog when revoking zen access warning that zen rule instances will be deleted.
- */
- public static class FriendlyWarningDialogFragment extends InstrumentedDialogFragment {
- static final String KEY_PKG = "p";
- static final String KEY_LABEL = "l";
-
-
- @Override
- public int getMetricsCategory() {
- return SettingsEnums.DIALOG_ZEN_ACCESS_REVOKE;
- }
-
- public FriendlyWarningDialogFragment setPkgInfo(String pkg, CharSequence label) {
- Bundle args = new Bundle();
- args.putString(KEY_PKG, pkg);
- args.putString(KEY_LABEL, TextUtils.isEmpty(label) ? pkg : label.toString());
- setArguments(args);
- return this;
- }
-
- @Override
- public Dialog onCreateDialog(Bundle savedInstanceState) {
- super.onCreate(savedInstanceState);
- final Bundle args = getArguments();
- final String pkg = args.getString(KEY_PKG);
- final String label = args.getString(KEY_LABEL);
-
- final String title = getResources().getString(
- R.string.zen_access_revoke_warning_dialog_title, label);
- final String summary = getResources()
- .getString(R.string.zen_access_revoke_warning_dialog_summary);
- return new AlertDialog.Builder(getContext())
- .setMessage(summary)
- .setTitle(title)
- .setCancelable(true)
- .setPositiveButton(R.string.okay,
- new DialogInterface.OnClickListener() {
- public void onClick(DialogInterface dialog, int id) {
- deleteRules(getContext(), pkg);
- setAccess(getContext(), pkg, false);
- }
- })
- .setNegativeButton(R.string.cancel,
- new DialogInterface.OnClickListener() {
- public void onClick(DialogInterface dialog, int id) {
- // pass
- }
- })
- .create();
- }
+ private int getPreferenceSummary(String packageName) {
+ final boolean enabled = ZenAccessController.hasAccess(getContext(), packageName);
+ return enabled ? R.string.app_permission_summary_allowed
+ : R.string.app_permission_summary_not_allowed;
}
public static final Indexable.SearchIndexProvider SEARCH_INDEX_DATA_PROVIDER =
diff --git a/src/com/android/settings/wifi/details/WifiDetailPreferenceController.java b/src/com/android/settings/wifi/details/WifiDetailPreferenceController.java
index 89565df..5588977 100644
--- a/src/com/android/settings/wifi/details/WifiDetailPreferenceController.java
+++ b/src/com/android/settings/wifi/details/WifiDetailPreferenceController.java
@@ -66,6 +66,7 @@
import com.android.settings.wifi.WifiDialog.WifiDialogListener;
import com.android.settings.wifi.WifiUtils;
import com.android.settings.wifi.dpp.WifiDppUtils;
+import com.android.settings.wifi.savedaccesspoints.SavedAccessPointsWifiSettings;
import com.android.settingslib.core.AbstractPreferenceController;
import com.android.settingslib.core.instrumentation.MetricsFeatureProvider;
import com.android.settingslib.core.lifecycle.Lifecycle;
@@ -75,6 +76,8 @@
import com.android.settingslib.widget.ActionButtonsPreference;
import com.android.settingslib.widget.LayoutPreference;
import com.android.settingslib.wifi.AccessPoint;
+import com.android.settingslib.wifi.WifiTracker;
+import com.android.settingslib.wifi.WifiTrackerFactory;
import java.net.Inet4Address;
import java.net.Inet6Address;
@@ -136,7 +139,9 @@
private WifiConfiguration mWifiConfig;
private WifiInfo mWifiInfo;
private final WifiManager mWifiManager;
+ private final WifiTracker mWifiTracker;
private final MetricsFeatureProvider mMetricsFeatureProvider;
+ private boolean mIsOutOfRange;
// UI elements - in order of appearance
private ActionButtonsPreference mButtonsPref;
@@ -176,7 +181,7 @@
// fall through
case WifiManager.NETWORK_STATE_CHANGED_ACTION:
case WifiManager.RSSI_CHANGED_ACTION:
- updateLiveNetworkInfo();
+ updateNetworkInfo();
break;
}
}
@@ -206,14 +211,16 @@
@Override
public void onCapabilitiesChanged(Network network, NetworkCapabilities nc) {
// If the network just validated or lost Internet access, refresh network state.
- // Don't do this on every NetworkCapabilities change because refreshNetworkState
- // sends IPCs to the system server from the UI thread, which can cause jank.
+ // Don't do this on every NetworkCapabilities change because update accesspoint notify
+ // changed for accesspoint changed on the main thread, which can cause jank.
if (network.equals(mNetwork) && !nc.equals(mNetworkCapabilities)) {
if (hasCapabilityChanged(nc, NET_CAPABILITY_VALIDATED) ||
hasCapabilityChanged(nc, NET_CAPABILITY_CAPTIVE_PORTAL)) {
- refreshNetworkState();
+ mAccessPoint.update(mWifiConfig, mWifiInfo, mNetworkInfo);
+ refreshEntityHeader();
}
mNetworkCapabilities = nc;
+ refreshButtons();
updateIpLayerInfo();
}
}
@@ -226,6 +233,29 @@
}
};
+ private final WifiTracker.WifiListener mWifiListener = new WifiTracker.WifiListener() {
+ /** Called when the state of Wifi has changed. */
+ public void onWifiStateChanged(int state) {
+ Log.d(TAG, "onWifiStateChanged(" + state + ")");
+ // Do nothing.
+ }
+
+ /** Called when the connection state of wifi has changed. */
+ public void onConnectedChanged() {
+ Log.d(TAG, "onConnectedChanged");
+ // Do nothing.
+ }
+
+ /**
+ * Called to indicate the list of AccessPoints has been updated and
+ * {@link WifiTracker#getAccessPoints()} should be called to get the updated list.
+ */
+ public void onAccessPointsChanged() {
+ Log.d(TAG, "onAccessPointsChanged");
+ updateNetworkInfo();
+ }
+ };
+
public static WifiDetailPreferenceController newInstance(
AccessPoint accessPoint,
ConnectivityManager connectivityManager,
@@ -270,6 +300,17 @@
mLifecycle = lifecycle;
lifecycle.addObserver(this);
+
+ if (SavedAccessPointsWifiSettings.usingDetailsFragment(mContext)) {
+ mWifiTracker = WifiTrackerFactory.create(
+ mFragment.getActivity(),
+ mWifiListener,
+ mLifecycle,
+ true /*includeSaved*/,
+ true /*includeScans*/);
+ } else {
+ mWifiTracker = null;
+ }
}
@Override
@@ -360,7 +401,7 @@
mNetwork = mWifiManager.getCurrentNetwork();
mLinkProperties = mConnectivityManager.getLinkProperties(mNetwork);
mNetworkCapabilities = mConnectivityManager.getNetworkCapabilities(mNetwork);
- updateLiveNetworkInfo();
+ updateNetworkInfo();
mContext.registerReceiver(mReceiver, mFilter);
mConnectivityManager.registerNetworkCallback(mNetworkRequest, mNetworkCallback,
mHandler);
@@ -377,72 +418,73 @@
mConnectivityManager.unregisterNetworkCallback(mNetworkCallback);
}
- // TODO(b/124707751): Refactoring the code later, keeping it currently for stability.
- protected void updateSavedNetworkInfo() {
- mSignalStrengthPref.setVisible(false);
- mFrequencyPref.setVisible(false);
- mTxLinkSpeedPref.setVisible(false);
- mRxLinkSpeedPref.setVisible(false);
-
- // MAC Address Pref
- mMacAddressPref.setSummary(mWifiConfig.getRandomizedMacAddress().toString());
-
- refreshEntityHeader();
-
- updateIpLayerInfo();
-
- // Update whether the forget button should be displayed.
- mButtonsPref.setButton1Visible(canForgetNetwork());
- }
-
- private void updateLiveNetworkInfo() {
- // No need to fetch LinkProperties and NetworkCapabilities, they are updated by the
- // callbacks. mNetwork doesn't change except in onResume.
- mNetworkInfo = mConnectivityManager.getNetworkInfo(mNetwork);
- mWifiInfo = mWifiManager.getConnectionInfo();
- if (mNetwork == null || mNetworkInfo == null || mWifiInfo == null) {
- exitActivity();
+ private void updateNetworkInfo() {
+ if(!updateAccessPoint()) {
return;
}
- // Update whether the forget button should be displayed.
- mButtonsPref.setButton1Visible(canForgetNetwork());
+ // refresh header
+ refreshEntityHeader();
- refreshNetworkState();
+ // refresh Buttons
+ refreshButtons();
// Update Connection Header icon and Signal Strength Preference
refreshRssiViews();
-
- // MAC Address Pref
- mMacAddressPref.setSummary(mWifiInfo.getMacAddress());
-
- // Transmit Link Speed Pref
- int txLinkSpeedMbps = mWifiInfo.getTxLinkSpeedMbps();
- mTxLinkSpeedPref.setVisible(txLinkSpeedMbps >= 0);
- mTxLinkSpeedPref.setSummary(mContext.getString(
- R.string.tx_link_speed, mWifiInfo.getTxLinkSpeedMbps()));
-
- // Receive Link Speed Pref
- int rxLinkSpeedMbps = mWifiInfo.getRxLinkSpeedMbps();
- mRxLinkSpeedPref.setVisible(rxLinkSpeedMbps >= 0);
- mRxLinkSpeedPref.setSummary(mContext.getString(
- R.string.rx_link_speed, mWifiInfo.getRxLinkSpeedMbps()));
-
// 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.setSummary(band);
-
+ refreshFrequency();
+ // Transmit Link Speed Pref
+ refreshTxSpeed();
+ // Receive Link Speed Pref
+ refreshRxSpeed();
+ // IP related information
updateIpLayerInfo();
+ // MAC Address Pref
+ refreshMacAddress();
+
+ }
+
+ private boolean updateAccessPoint() {
+ boolean changed = false;
+ if (mWifiTracker != null) {
+ updateAccessPointFromScannedList();
+ // refresh UI if signal level changed for disconnect network.
+ changed = mRssiSignalLevel != mAccessPoint.getLevel();
+ }
+
+ if (mAccessPoint.isActive()) {
+ // No need to fetch LinkProperties and NetworkCapabilities, they are updated by the
+ // callbacks. mNetwork doesn't change except in onResume.
+ mNetworkInfo = mConnectivityManager.getNetworkInfo(mNetwork);
+ mWifiInfo = mWifiManager.getConnectionInfo();
+ if (mNetwork == null || mNetworkInfo == null || mWifiInfo == null) {
+ exitActivity();
+ return false;
+ }
+
+ changed |= mAccessPoint.update(mWifiConfig, mWifiInfo, mNetworkInfo);
+ // If feature for saved network not enabled, always return true.
+ return mWifiTracker == null || changed;
+ }
+
+ return changed;
+ }
+
+ private void updateAccessPointFromScannedList() {
+ mIsOutOfRange = true;
+
+ if (mAccessPoint.getConfig() == null) {
+ return;
+ }
+
+ for (AccessPoint ap : mWifiTracker.getAccessPoints()) {
+ if (ap.getConfig() != null
+ && mAccessPoint.matches(ap.getConfig())) {
+ mAccessPoint = ap;
+ mIsOutOfRange = false;
+ return;
+ }
+ }
}
private void exitActivity() {
@@ -452,14 +494,16 @@
mFragment.getActivity().finish();
}
- private void refreshNetworkState() {
- mAccessPoint.update(mWifiConfig, mWifiInfo, mNetworkInfo);
- refreshEntityHeader();
- }
-
private void refreshRssiViews() {
int signalLevel = mAccessPoint.getLevel();
+ // Disappears signal view if not in range. e.g. for saved networks.
+ if (mIsOutOfRange) {
+ mSignalStrengthPref.setVisible(false);
+ mRssiSignalLevel = -1;
+ return;
+ }
+
if (mRssiSignalLevel == signalLevel) {
return;
}
@@ -477,6 +521,84 @@
mSignalStrengthPref.setIcon(wifiIconDark);
mSignalStrengthPref.setSummary(mSignalStr[mRssiSignalLevel]);
+ mSignalStrengthPref.setVisible(true);
+ }
+
+ private void refreshFrequency() {
+ if (mWifiInfo == null) {
+ mFrequencyPref.setVisible(false);
+ return;
+ }
+
+ 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.setSummary(band);
+ mFrequencyPref.setVisible(true);
+ }
+
+ private void refreshTxSpeed() {
+ if (mWifiInfo == null) {
+ mTxLinkSpeedPref.setVisible(false);
+ return;
+ }
+
+ int txLinkSpeedMbps = mWifiInfo.getTxLinkSpeedMbps();
+ mTxLinkSpeedPref.setVisible(txLinkSpeedMbps >= 0);
+ mTxLinkSpeedPref.setSummary(mContext.getString(
+ R.string.tx_link_speed, mWifiInfo.getTxLinkSpeedMbps()));
+ }
+
+ private void refreshRxSpeed() {
+ if (mWifiInfo == null) {
+ mRxLinkSpeedPref.setVisible(false);
+ return;
+ }
+
+ int rxLinkSpeedMbps = mWifiInfo.getRxLinkSpeedMbps();
+ mRxLinkSpeedPref.setVisible(rxLinkSpeedMbps >= 0);
+ mRxLinkSpeedPref.setSummary(mContext.getString(
+ R.string.rx_link_speed, mWifiInfo.getRxLinkSpeedMbps()));
+ }
+
+ private void refreshMacAddress() {
+ String macAddress = getMacAddress();
+ if (macAddress == null) {
+ mMacAddressPref.setVisible(false);
+ return;
+ }
+
+ mMacAddressPref.setVisible(true);
+ mMacAddressPref.setSummary(macAddress);
+ }
+
+ private String getMacAddress() {
+ if (mWifiInfo != null) {
+ // get MAC address from connected network information
+ return mWifiInfo.getMacAddress();
+ }
+
+ // return randomized MAC address
+ if (mWifiConfig.macRandomizationSetting == WifiConfiguration.RANDOMIZATION_PERSISTENT) {
+ return mWifiConfig.getRandomizedMacAddress().toString();
+ }
+
+ // return device MAC address
+ final String[] macAddresses = mWifiManager.getFactoryMacAddresses();
+ if (macAddresses != null && macAddresses.length > 0) {
+ return macAddresses[0];
+ }
+
+ Log.e(TAG, "Can't get device MAC address!");
+ return null;
}
private void updatePreference(Preference pref, String detailText) {
@@ -488,13 +610,17 @@
}
}
- private void updateIpLayerInfo() {
+ private void refreshButtons() {
+ mButtonsPref.setButton1Visible(canForgetNetwork());
mButtonsPref.setButton2Visible(canSignIntoNetwork());
mButtonsPref.setButton3Visible(canShareNetwork());
mButtonsPref.setVisible(
canSignIntoNetwork() || canForgetNetwork() || canShareNetwork());
+ }
- if (mNetwork == null || mLinkProperties == null) {
+ private void updateIpLayerInfo() {
+ // Hide IP layer info if not a connected network.
+ if (!mAccessPoint.isActive() || mNetwork == null || mLinkProperties == null) {
mIpAddressPref.setVisible(false);
mSubnetPref.setVisible(false);
mGatewayPref.setVisible(false);
diff --git a/src/com/android/settings/wifi/details/WifiDetailSavedNetworkPreferenceController.java b/src/com/android/settings/wifi/details/WifiDetailSavedNetworkPreferenceController.java
deleted file mode 100644
index 3407890..0000000
--- a/src/com/android/settings/wifi/details/WifiDetailSavedNetworkPreferenceController.java
+++ /dev/null
@@ -1,66 +0,0 @@
-/*
- * 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.settings.wifi.details;
-
-import android.content.Context;
-import android.net.ConnectivityManager;
-import android.net.wifi.WifiManager;
-import android.os.Handler;
-
-import androidx.fragment.app.Fragment;
-
-import com.android.settingslib.core.instrumentation.MetricsFeatureProvider;
-import com.android.settingslib.core.lifecycle.Lifecycle;
-import com.android.settingslib.wifi.AccessPoint;
-
-public class WifiDetailSavedNetworkPreferenceController extends WifiDetailPreferenceController {
-
- WifiDetailSavedNetworkPreferenceController(AccessPoint accessPoint,
- ConnectivityManager connectivityManager, Context context,
- Fragment fragment, Handler handler,
- Lifecycle lifecycle,
- WifiManager wifiManager,
- MetricsFeatureProvider metricsFeatureProvider,
- IconInjector injector) {
- super(accessPoint, connectivityManager, context, fragment, handler, lifecycle, wifiManager,
- metricsFeatureProvider, injector);
- }
-
- public static WifiDetailSavedNetworkPreferenceController newInstance(
- AccessPoint accessPoint,
- ConnectivityManager connectivityManager,
- Context context,
- Fragment fragment,
- Handler handler,
- Lifecycle lifecycle,
- WifiManager wifiManager,
- MetricsFeatureProvider metricsFeatureProvider) {
- return new WifiDetailSavedNetworkPreferenceController(
- accessPoint, connectivityManager, context, fragment, handler, lifecycle,
- wifiManager, metricsFeatureProvider, new IconInjector(context));
- }
-
- @Override
- public void onPause() {
- // Do nothing
- }
-
- @Override
- public void onResume() {
- updateSavedNetworkInfo();
- }
-}
diff --git a/src/com/android/settings/wifi/details/WifiNetworkDetailsFragment.java b/src/com/android/settings/wifi/details/WifiNetworkDetailsFragment.java
index 7edd227..66587ed 100644
--- a/src/com/android/settings/wifi/details/WifiNetworkDetailsFragment.java
+++ b/src/com/android/settings/wifi/details/WifiNetworkDetailsFragment.java
@@ -51,9 +51,6 @@
private static final String TAG = "WifiNetworkDetailsFrg";
- // Extra for if current fragment shows saved network status or not.
- public static final String EXTRA_IS_SAVED_NETWORK = "SavedNetwork";
-
private AccessPoint mAccessPoint;
private WifiDetailPreferenceController mWifiDetailPreferenceController;
@@ -126,30 +123,15 @@
final List<AbstractPreferenceController> controllers = new ArrayList<>();
final ConnectivityManager cm = context.getSystemService(ConnectivityManager.class);
- final boolean isDisplaySavedNetworkDetails =
- getArguments().getBoolean(EXTRA_IS_SAVED_NETWORK, false /* defaultValue */);
- if (isDisplaySavedNetworkDetails) {
- mWifiDetailPreferenceController =
- WifiDetailSavedNetworkPreferenceController.newInstance(
- mAccessPoint,
- cm,
- context,
- this,
- new Handler(Looper.getMainLooper()), // UI thread.
- getSettingsLifecycle(),
- context.getSystemService(WifiManager.class),
- mMetricsFeatureProvider);
- } else {
- mWifiDetailPreferenceController = WifiDetailPreferenceController.newInstance(
- mAccessPoint,
- cm,
- context,
- this,
- new Handler(Looper.getMainLooper()), // UI thread.
- getSettingsLifecycle(),
- context.getSystemService(WifiManager.class),
- mMetricsFeatureProvider);
- }
+ mWifiDetailPreferenceController = WifiDetailPreferenceController.newInstance(
+ mAccessPoint,
+ cm,
+ context,
+ this,
+ new Handler(Looper.getMainLooper()), // UI thread.
+ getSettingsLifecycle(),
+ context.getSystemService(WifiManager.class),
+ mMetricsFeatureProvider);
controllers.add(mWifiDetailPreferenceController);
controllers.add(new WifiMeteredPreferenceController(context, mAccessPoint.getConfig()));
diff --git a/src/com/android/settings/wifi/savedaccesspoints/SavedAccessPointsWifiSettings.java b/src/com/android/settings/wifi/savedaccesspoints/SavedAccessPointsWifiSettings.java
index ea858f3..3f600e6 100644
--- a/src/com/android/settings/wifi/savedaccesspoints/SavedAccessPointsWifiSettings.java
+++ b/src/com/android/settings/wifi/savedaccesspoints/SavedAccessPointsWifiSettings.java
@@ -108,7 +108,6 @@
}
final Bundle savedState = new Bundle();
mSelectedAccessPoint.saveWifiState(savedState);
- savedState.putBoolean(WifiNetworkDetailsFragment.EXTRA_IS_SAVED_NETWORK, true);
new SubSettingLauncher(getContext())
.setTitleText(mSelectedAccessPoint.getTitle())
diff --git a/tests/robotests/src/com/android/settings/applications/specialaccess/zenaccess/ZenAccessControllerTest.java b/tests/robotests/src/com/android/settings/applications/specialaccess/zenaccess/ZenAccessControllerTest.java
index bcb4bb3..6041e9d 100644
--- a/tests/robotests/src/com/android/settings/applications/specialaccess/zenaccess/ZenAccessControllerTest.java
+++ b/tests/robotests/src/com/android/settings/applications/specialaccess/zenaccess/ZenAccessControllerTest.java
@@ -18,26 +18,41 @@
import static com.google.common.truth.Truth.assertThat;
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.ArgumentMatchers.eq;
+import static org.mockito.Mockito.verify;
+
+import android.app.NotificationManager;
import android.content.Context;
+import com.android.internal.logging.nano.MetricsProto;
+import com.android.settings.testutils.FakeFeatureFactory;
+import com.android.settings.testutils.shadow.ShadowNotificationManager;
+
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.robolectric.RobolectricTestRunner;
import org.robolectric.RuntimeEnvironment;
+import org.robolectric.annotation.Config;
import org.robolectric.shadow.api.Shadow;
import org.robolectric.shadows.ShadowActivityManager;
@RunWith(RobolectricTestRunner.class)
public class ZenAccessControllerTest {
+ private static final String TEST_PKG = "com.test.package";
+
+ private FakeFeatureFactory mFeatureFactory;
private Context mContext;
private ZenAccessController mController;
private ShadowActivityManager mActivityManager;
+
@Before
public void setUp() {
mContext = RuntimeEnvironment.application;
+ mFeatureFactory = FakeFeatureFactory.setupForTest();
mController = new ZenAccessController(mContext, "key");
mActivityManager = Shadow.extract(mContext.getSystemService(Context.ACTIVITY_SERVICE));
}
@@ -52,4 +67,32 @@
mActivityManager.setIsLowRamDevice(true);
assertThat(mController.isAvailable()).isFalse();
}
+
+ @Test
+ public void logSpecialPermissionChange() {
+ ZenAccessController.logSpecialPermissionChange(true, "app", mContext);
+ verify(mFeatureFactory.metricsFeatureProvider).action(any(Context.class),
+ eq(MetricsProto.MetricsEvent.APP_SPECIAL_PERMISSION_DND_ALLOW),
+ eq("app"));
+
+ ZenAccessController.logSpecialPermissionChange(false, "app", mContext);
+ verify(mFeatureFactory.metricsFeatureProvider).action(any(Context.class),
+ eq(MetricsProto.MetricsEvent.APP_SPECIAL_PERMISSION_DND_DENY),
+ eq("app"));
+ }
+
+ @Test
+ @Config(shadows = ShadowNotificationManager.class)
+ public void hasAccess_granted_yes() {
+ final ShadowNotificationManager snm = Shadow.extract(mContext.getSystemService(
+ NotificationManager.class));
+ snm.setNotificationPolicyAccessGrantedForPackage(TEST_PKG);
+ assertThat(ZenAccessController.hasAccess(mContext, TEST_PKG)).isTrue();
+ }
+
+ @Test
+ @Config(shadows = ShadowNotificationManager.class)
+ public void hasAccess_notGranted_no() {
+ assertThat(ZenAccessController.hasAccess(mContext, TEST_PKG)).isFalse();
+ }
}
diff --git a/tests/robotests/src/com/android/settings/applications/specialaccess/zenaccess/ZenAccessSettingObserverMixinTest.java b/tests/robotests/src/com/android/settings/applications/specialaccess/zenaccess/ZenAccessSettingObserverMixinTest.java
new file mode 100644
index 0000000..cba1a51
--- /dev/null
+++ b/tests/robotests/src/com/android/settings/applications/specialaccess/zenaccess/ZenAccessSettingObserverMixinTest.java
@@ -0,0 +1,109 @@
+/*
+ * 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.settings.applications.specialaccess.zenaccess;
+
+import static androidx.lifecycle.Lifecycle.Event.ON_START;
+import static androidx.lifecycle.Lifecycle.Event.ON_STOP;
+
+import static org.mockito.Mockito.never;
+import static org.mockito.Mockito.spy;
+import static org.mockito.Mockito.verify;
+
+import android.app.ActivityManager;
+import android.content.Context;
+import android.provider.Settings;
+
+import androidx.lifecycle.LifecycleOwner;
+
+import com.android.settingslib.core.lifecycle.Lifecycle;
+
+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 org.robolectric.shadow.api.Shadow;
+import org.robolectric.shadows.ShadowActivityManager;
+
+@RunWith(RobolectricTestRunner.class)
+public class ZenAccessSettingObserverMixinTest {
+
+ @Mock
+ private ZenAccessSettingObserverMixin.Listener mListener;
+
+ private Context mContext;
+ private LifecycleOwner mLifecycleOwner;
+ private Lifecycle mLifecycle;
+ private ZenAccessSettingObserverMixin mMixin;
+
+ @Before
+ public void setUp() {
+ MockitoAnnotations.initMocks(this);
+ mContext = spy(RuntimeEnvironment.application);
+ mLifecycleOwner = () -> mLifecycle;
+ mLifecycle = new Lifecycle(mLifecycleOwner);
+
+ mMixin = new ZenAccessSettingObserverMixin(mContext, mListener);
+
+ mLifecycle.addObserver(mMixin);
+ }
+
+ @Test
+ public void onStart_lowMemory_shouldNotRegisterListener() {
+ final ShadowActivityManager sam = Shadow.extract(
+ mContext.getSystemService(ActivityManager.class));
+ sam.setIsLowRamDevice(true);
+
+ mLifecycle.handleLifecycleEvent(ON_START);
+
+ mContext.getContentResolver().notifyChange(Settings.Secure.getUriFor(
+ Settings.Secure.ENABLED_NOTIFICATION_POLICY_ACCESS_PACKAGES), null);
+
+ verify(mListener, never()).onZenAccessPolicyChanged();
+ }
+
+ @Test
+ public void onStart_highMemory_shouldRegisterListener() {
+ final ShadowActivityManager sam = Shadow.extract(
+ mContext.getSystemService(ActivityManager.class));
+ sam.setIsLowRamDevice(false);
+
+ mLifecycle.handleLifecycleEvent(ON_START);
+
+ mContext.getContentResolver().notifyChange(Settings.Secure.getUriFor(
+ Settings.Secure.ENABLED_NOTIFICATION_POLICY_ACCESS_PACKAGES), null);
+
+ verify(mListener).onZenAccessPolicyChanged();
+ }
+
+ @Test
+ public void onStop_shouldUnregisterListener() {
+ final ShadowActivityManager sam = Shadow.extract(
+ mContext.getSystemService(ActivityManager.class));
+ sam.setIsLowRamDevice(false);
+
+ mLifecycle.handleLifecycleEvent(ON_START);
+ mLifecycle.handleLifecycleEvent(ON_STOP);
+
+ mContext.getContentResolver().notifyChange(Settings.Secure.getUriFor(
+ Settings.Secure.ENABLED_NOTIFICATION_POLICY_ACCESS_PACKAGES), null);
+
+ verify(mListener, never()).onZenAccessPolicyChanged();
+ }
+}
diff --git a/tests/robotests/src/com/android/settings/notification/ZenAccessSettingsTest.java b/tests/robotests/src/com/android/settings/notification/ZenAccessSettingsTest.java
deleted file mode 100644
index c2a6f4f..0000000
--- a/tests/robotests/src/com/android/settings/notification/ZenAccessSettingsTest.java
+++ /dev/null
@@ -1,63 +0,0 @@
-/*
- * 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.notification;
-
-import static org.mockito.ArgumentMatchers.any;
-import static org.mockito.ArgumentMatchers.eq;
-import static org.mockito.Mockito.verify;
-
-import android.content.Context;
-
-import com.android.internal.logging.nano.MetricsProto;
-import com.android.settings.testutils.FakeFeatureFactory;
-
-import org.junit.Before;
-import org.junit.Test;
-import org.junit.runner.RunWith;
-import org.mockito.Answers;
-import org.mockito.Mock;
-import org.mockito.MockitoAnnotations;
-import org.robolectric.RobolectricTestRunner;
-
-@RunWith(RobolectricTestRunner.class)
-public class ZenAccessSettingsTest {
-
- @Mock(answer = Answers.RETURNS_DEEP_STUBS)
- private Context mContext;
-
- private FakeFeatureFactory mFeatureFactory;
-
- @Before
- public void setUp() {
- MockitoAnnotations.initMocks(this);
-
- mFeatureFactory = FakeFeatureFactory.setupForTest();
- }
-
- @Test
- public void logSpecialPermissionChange() {
- ZenAccessSettings.logSpecialPermissionChange(true, "app", mContext);
- verify(mFeatureFactory.metricsFeatureProvider).action(any(Context.class),
- eq(MetricsProto.MetricsEvent.APP_SPECIAL_PERMISSION_DND_ALLOW),
- eq("app"));
-
- ZenAccessSettings.logSpecialPermissionChange(false, "app", mContext);
- verify(mFeatureFactory.metricsFeatureProvider).action(any(Context.class),
- eq(MetricsProto.MetricsEvent.APP_SPECIAL_PERMISSION_DND_DENY),
- eq("app"));
- }
-}
diff --git a/tests/robotests/src/com/android/settings/testutils/shadow/ShadowNotificationManager.java b/tests/robotests/src/com/android/settings/testutils/shadow/ShadowNotificationManager.java
index 8325777..78fb23f 100644
--- a/tests/robotests/src/com/android/settings/testutils/shadow/ShadowNotificationManager.java
+++ b/tests/robotests/src/com/android/settings/testutils/shadow/ShadowNotificationManager.java
@@ -19,15 +19,19 @@
import android.app.NotificationManager;
import android.net.Uri;
import android.service.notification.ZenModeConfig;
+import android.util.ArraySet;
import org.robolectric.annotation.Implementation;
import org.robolectric.annotation.Implements;
+import java.util.Set;
+
@Implements(NotificationManager.class)
public class ShadowNotificationManager {
private int mZenMode;
private ZenModeConfig mZenModeConfig;
+ private Set<String> mNotificationPolicyGrantedPackages = new ArraySet<>();
@Implementation
protected void setZenMode(int mode, Uri conditionId, String reason) {
@@ -40,6 +44,11 @@
}
@Implementation
+ protected boolean isNotificationPolicyAccessGrantedForPackage(String pkg) {
+ return mNotificationPolicyGrantedPackages.contains(pkg);
+ }
+
+ @Implementation
public ZenModeConfig getZenModeConfig() {
return mZenModeConfig;
}
@@ -47,4 +56,8 @@
public void setZenModeConfig(ZenModeConfig config) {
mZenModeConfig = config;
}
+
+ public void setNotificationPolicyAccessGrantedForPackage(String pkg) {
+ mNotificationPolicyGrantedPackages.add(pkg);
+ }
}