Merge "Suppress some gesture search when there is no hardware"
diff --git a/res/layout/date_time_custom_list_item_2.xml b/res/layout/date_time_custom_list_item_2.xml
deleted file mode 100644
index 4902758..0000000
--- a/res/layout/date_time_custom_list_item_2.xml
+++ /dev/null
@@ -1,42 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!-- Copyright (C) 2011 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.
--->
-
-<!-- Based on simple_list_item_2.xml in framework -->
-<LinearLayout
- xmlns:android="http://schemas.android.com/apk/res/android"
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:background="?android:attr/selectableItemBackground"
- android:gravity="center_vertical"
- android:minHeight="?android:attr/listPreferredItemHeight"
- android:orientation="vertical"
- android:paddingStart="@dimen/preference_no_icon_padding_start"
- android:paddingEnd="?android:attr/listPreferredItemPaddingEnd">
-
- <TextView
- android:id="@android:id/text1"
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:textAppearance="?android:attr/textAppearanceListItem" />
-
- <TextView
- android:id="@android:id/text2"
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:textAppearance="?android:attr/textAppearanceListItemSecondary"
- android:textColor="?android:attr/textColorSecondary" />
-
-</LinearLayout>
diff --git a/res/layout/device_admin_item.xml b/res/layout/device_admin_item.xml
deleted file mode 100644
index 875c734..0000000
--- a/res/layout/device_admin_item.xml
+++ /dev/null
@@ -1,86 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
-/*
-** Copyright 2010, 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.
-*/
--->
-
-<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:paddingStart="?android:attr/listPreferredItemPaddingStart"
- android:paddingEnd="?android:attr/listPreferredItemPaddingEnd"
- android:minHeight="?android:attr/listPreferredItemHeight"
- android:orientation="horizontal"
- android:gravity="center_vertical" >
-
- <LinearLayout
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:gravity="start|center_vertical"
- android:minWidth="60dp"
- android:orientation="horizontal"
- android:paddingEnd="12dp"
- android:paddingTop="4dp"
- android:paddingBottom="4dp">
-
- <ImageView
- android:id="@+id/icon"
- android:layout_width="@dimen/secondary_app_icon_size"
- android:layout_height="@dimen/secondary_app_icon_size"
- android:layout_gravity="center_vertical"
- android:layout_marginEnd="8dip"
- android:contentDescription="@null" />
-
- </LinearLayout>
-
- <RelativeLayout
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:layout_marginTop="8dip"
- android:layout_marginBottom="8dip"
- android:layout_weight="1">
-
- <TextView android:id="@+id/name"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:singleLine="true"
- android:textAppearance="?android:attr/textAppearanceMedium"
- android:ellipsize="marquee"
- android:layout_alignParentTop="true"
- android:fadingEdge="horizontal" />
-
- <TextView android:id="@+id/description"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:layout_marginTop="4dip"
- android:layout_below="@id/name"
- android:layout_alignStart="@id/name"
- android:textAppearance="?android:attr/textAppearanceSmall"
- android:textColor="?android:attr/textColorSecondary"
- android:maxLines="4" />
-
- </RelativeLayout>
-
- <Switch
- android:id="@+id/checkbox"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:layout_gravity="center_vertical"
- android:layout_marginStart="16dip"
- android:focusable="false"
- android:clickable="false" />
-
-</LinearLayout>
diff --git a/res/layout/device_admin_settings.xml b/res/layout/device_admin_settings.xml
deleted file mode 100644
index 1766138..0000000
--- a/res/layout/device_admin_settings.xml
+++ /dev/null
@@ -1,43 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!-- Copyright (C) 2010 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.
--->
-
-<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
- android:layout_width="match_parent"
- android:layout_height="match_parent"
- android:orientation="vertical">
-
- <FrameLayout
- android:layout_width="match_parent"
- android:layout_height="0px"
- android:layout_weight="1">
-
- <ListView android:id="@android:id/list"
- android:layout_width="match_parent"
- android:layout_height="match_parent"
- android:scrollbarStyle="outsideOverlay"
- android:drawSelectorOnTop="false"
- android:fastScrollEnabled="true" />
-
- <TextView android:id="@android:id/empty"
- android:layout_width="match_parent"
- android:layout_height="match_parent"
- android:gravity="center"
- android:text="@string/no_device_admins"
- android:textAppearance="?android:attr/textAppearanceMedium" />
-
- </FrameLayout>
-
-</LinearLayout>
diff --git a/res/values/bools.xml b/res/values/bools.xml
index fa57b29..b406605 100644
--- a/res/values/bools.xml
+++ b/res/values/bools.xml
@@ -96,9 +96,6 @@
<!-- Whether touch_sounds should be shown or not. -->
<bool name="config_show_touch_sounds">true</bool>
- <!-- Whether device_administrators should be shown or not. -->
- <bool name="config_show_device_administrators">true</bool>
-
<!-- Whether encryption_and_credentials_encryption_status should be shown or not. -->
<bool name="config_show_encryption_and_credentials_encryption_status">true</bool>
diff --git a/res/values/strings.xml b/res/values/strings.xml
index f0394c9..d0c076e 100644
--- a/res/values/strings.xml
+++ b/res/values/strings.xml
@@ -8270,7 +8270,7 @@
<!-- App notification summary with notifications enabled [CHAR LIMIT=40] -->
<string name="notifications_enabled">On</string>
<!-- App notification summary with notifications enabled [CHAR LIMIT=40] -->
- <string name="notifications_enabled_with_info">On / <xliff:g id="notifications_categories_off" example="3 categories turned off">%1$s</xliff:g> </string>
+ <string name="notifications_enabled_with_info"><xliff:g id="notifications_sent" example="~6 per week">%1$s</xliff:g> / <xliff:g id="notifications_categories_off" example="3 categories turned off">%2$s</xliff:g> </string>
<!-- Label for showing apps with blocked notifications in list [CHAR LIMIT=30] -->
<string name="notifications_disabled">Off</string>
<!-- Label for showing apps with some blocked notifications in list [CHAR LIMIT=30] -->
diff --git a/res/xml/device_admin_settings.xml b/res/xml/device_admin_settings.xml
new file mode 100644
index 0000000..999d790
--- /dev/null
+++ b/res/xml/device_admin_settings.xml
@@ -0,0 +1,29 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ Copyright (C) 2018 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"
+ xmlns:settings="http://schemas.android.com/apk/res-auto"
+ android:title="@string/manage_device_admin"
+ android:key="device_admin_settings">
+
+ <PreferenceCategory
+ android:key="device_admin_list"
+ android:title="@string/summary_placeholder"
+ settings:controller="com.android.settings.applications.specialaccess.deviceadmin.DeviceAdminListPreferenceController" />
+
+</PreferenceScreen>
\ No newline at end of file
diff --git a/res/xml/security_dashboard_settings.xml b/res/xml/security_dashboard_settings.xml
index af15c08..8a3d108 100644
--- a/res/xml/security_dashboard_settings.xml
+++ b/res/xml/security_dashboard_settings.xml
@@ -48,7 +48,7 @@
android:key="fingerprint_settings"
android:title="@string/security_settings_fingerprint_preference_title"
android:summary="@string/summary_placeholder"
- settings:keywords="@string/keywords_fingerprint_settings"/>
+ settings:keywords="@string/keywords_fingerprint_settings" />
</PreferenceCategory>
@@ -75,7 +75,7 @@
android:key="visiblepattern_profile"
android:summary="@string/summary_placeholder"
android:title="@string/lockpattern_settings_enable_visible_pattern_title_profile"
- settings:controller="com.android.settings.security.VisiblePatternProfilePreferenceController"/>
+ settings:controller="com.android.settings.security.VisiblePatternProfilePreferenceController" />
<Preference
android:key="fingerprint_settings_profile"
@@ -100,7 +100,7 @@
android:key="show_password"
android:title="@string/show_password"
android:summary="@string/show_password_summary"
- settings:controller="com.android.settings.security.ShowPasswordPreferenceController"/>
+ settings:controller="com.android.settings.security.ShowPasswordPreferenceController" />
</PreferenceCategory>
@@ -112,7 +112,8 @@
android:key="manage_device_admin"
android:title="@string/manage_device_admin"
android:summary="@string/summary_placeholder"
- android:fragment="com.android.settings.applications.specialaccess.deviceadmin.DeviceAdminSettings" />
+ android:fragment="com.android.settings.applications.specialaccess.deviceadmin.DeviceAdminSettings"
+ settings:controller="com.android.settings.enterprise.ManageDeviceAdminPreferenceController" />
<Preference
android:key="enterprise_privacy"
diff --git a/res/xml/special_access.xml b/res/xml/special_access.xml
index b7a8e5c..574686a 100644
--- a/res/xml/special_access.xml
+++ b/res/xml/special_access.xml
@@ -35,7 +35,8 @@
android:key="device_administrators"
android:title="@string/manage_device_admin"
android:fragment="com.android.settings.applications.specialaccess.deviceadmin.DeviceAdminSettings"
- settings:controller="com.android.settings.applications.specialaccess.deviceadmin.DeviceAdministratorsController" />
+ android:summary="@string/summary_placeholder"
+ settings:controller="com.android.settings.enterprise.ManageDeviceAdminPreferenceController" />
<Preference
android:key="system_alert_window"
diff --git a/src/com/android/settings/applications/appinfo/AppNotificationPreferenceController.java b/src/com/android/settings/applications/appinfo/AppNotificationPreferenceController.java
index d4e7e60..e4aaa54 100644
--- a/src/com/android/settings/applications/appinfo/AppNotificationPreferenceController.java
+++ b/src/com/android/settings/applications/appinfo/AppNotificationPreferenceController.java
@@ -84,14 +84,15 @@
if (appRow.banned) {
return context.getText(R.string.notifications_disabled);
} else if (appRow.channelCount == 0) {
- return context.getText(R.string.notifications_enabled);
+ return NotificationBackend.getSentSummary(context, appRow.sentByApp, false);
} else if (appRow.channelCount == appRow.blockedChannelCount) {
return context.getText(R.string.notifications_disabled);
} else {
if (appRow.blockedChannelCount == 0) {
- return context.getText(R.string.notifications_enabled);
+ return NotificationBackend.getSentSummary(context, appRow.sentByApp, false);
}
return context.getString(R.string.notifications_enabled_with_info,
+ NotificationBackend.getSentSummary(context, appRow.sentByApp, false),
context.getResources().getQuantityString(R.plurals.notifications_categories_off,
appRow.blockedChannelCount, appRow.blockedChannelCount));
}
diff --git a/src/com/android/settings/applications/specialaccess/deviceadmin/DeviceAdminListItem.java b/src/com/android/settings/applications/specialaccess/deviceadmin/DeviceAdminListItem.java
new file mode 100644
index 0000000..305281c
--- /dev/null
+++ b/src/com/android/settings/applications/specialaccess/deviceadmin/DeviceAdminListItem.java
@@ -0,0 +1,94 @@
+/*
+ * Copyright (C) 2018 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.deviceadmin;
+
+import android.app.admin.DeviceAdminInfo;
+import android.app.admin.DevicePolicyManager;
+import android.content.Context;
+import android.content.Intent;
+import android.content.pm.PackageManager;
+import android.content.res.Resources;
+import android.graphics.drawable.Drawable;
+import android.os.UserHandle;
+import android.util.Log;
+
+class DeviceAdminListItem implements Comparable<DeviceAdminListItem> {
+
+ private static final String TAG = "DeviceAdminListItem";
+
+ private final String mKey;
+ private final DeviceAdminInfo mInfo;
+ private final CharSequence mName;
+ private final Drawable mIcon;
+ private final DevicePolicyManager mDPM;
+ private CharSequence mDescription;
+
+ public DeviceAdminListItem(Context context, DeviceAdminInfo info) {
+ mInfo = info;
+ mKey = mInfo.getComponent().flattenToString();
+ mDPM = (DevicePolicyManager) context.getSystemService(Context.DEVICE_POLICY_SERVICE);
+ final PackageManager pm = context.getPackageManager();
+ mName = mInfo.loadLabel(pm);
+ try {
+ mDescription = mInfo.loadDescription(pm);
+ } catch (Resources.NotFoundException exception) {
+ Log.w(TAG, "Setting description to null because can't find resource: " + mKey);
+ }
+ mIcon = pm.getUserBadgedIcon(mInfo.loadIcon(pm),
+ new UserHandle(DeviceAdminUtils.getUserIdFromDeviceAdminInfo(mInfo)));
+ }
+
+ @Override
+ public int compareTo(DeviceAdminListItem other) {
+ return this.mName.toString().compareTo(other.mName.toString());
+ }
+
+ public String getKey() {
+ return mKey;
+ }
+
+ public CharSequence getName() {
+ return mName;
+ }
+
+ public CharSequence getDescription() {
+ return mDescription;
+ }
+
+ public boolean isActive() {
+ return mDPM.isAdminActiveAsUser(mInfo.getComponent(),
+ DeviceAdminUtils.getUserIdFromDeviceAdminInfo(mInfo));
+ }
+
+ public Drawable getIcon() {
+ return mIcon;
+ }
+
+ public boolean isEnabled() {
+ return !mDPM.isRemovingAdmin(mInfo.getComponent(),
+ DeviceAdminUtils.getUserIdFromDeviceAdminInfo(mInfo));
+ }
+
+ public UserHandle getUser() {
+ return new UserHandle(DeviceAdminUtils.getUserIdFromDeviceAdminInfo(mInfo));
+ }
+
+ public Intent getLaunchIntent(Context context) {
+ return new Intent(context, DeviceAdminAdd.class)
+ .putExtra(DevicePolicyManager.EXTRA_DEVICE_ADMIN, mInfo.getComponent());
+ }
+}
diff --git a/src/com/android/settings/applications/specialaccess/deviceadmin/DeviceAdminListPreferenceController.java b/src/com/android/settings/applications/specialaccess/deviceadmin/DeviceAdminListPreferenceController.java
new file mode 100644
index 0000000..52d186b
--- /dev/null
+++ b/src/com/android/settings/applications/specialaccess/deviceadmin/DeviceAdminListPreferenceController.java
@@ -0,0 +1,296 @@
+/*
+ * Copyright (C) 2018 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.deviceadmin;
+
+import static android.app.admin.DevicePolicyManager.ACTION_DEVICE_POLICY_MANAGER_STATE_CHANGED;
+
+import android.app.AppGlobals;
+import android.app.admin.DeviceAdminInfo;
+import android.app.admin.DeviceAdminReceiver;
+import android.app.admin.DevicePolicyManager;
+import android.content.BroadcastReceiver;
+import android.content.ComponentName;
+import android.content.Context;
+import android.content.Intent;
+import android.content.IntentFilter;
+import android.content.pm.ActivityInfo;
+import android.content.pm.IPackageManager;
+import android.content.pm.PackageManager;
+import android.content.pm.ResolveInfo;
+import android.os.RemoteException;
+import android.os.UserHandle;
+import android.os.UserManager;
+import android.text.TextUtils;
+import android.util.ArrayMap;
+import android.util.Log;
+import android.util.SparseArray;
+
+import com.android.settings.R;
+import com.android.settings.core.BasePreferenceController;
+import com.android.settingslib.core.lifecycle.LifecycleObserver;
+import com.android.settingslib.core.lifecycle.events.OnStart;
+import com.android.settingslib.core.lifecycle.events.OnStop;
+import com.android.settingslib.widget.FooterPreference;
+import com.android.settingslib.widget.FooterPreferenceMixin;
+
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.List;
+import java.util.Map;
+
+import androidx.annotation.VisibleForTesting;
+import androidx.preference.Preference;
+import androidx.preference.PreferenceGroup;
+import androidx.preference.PreferenceScreen;
+import androidx.preference.SwitchPreference;
+
+public class DeviceAdminListPreferenceController extends BasePreferenceController
+ implements LifecycleObserver, OnStart, OnStop {
+
+ private static final IntentFilter FILTER = new IntentFilter();
+ private static final String TAG = "DeviceAdminListPrefCtrl";
+
+ private final DevicePolicyManager mDPM;
+ private final UserManager mUm;
+ private final PackageManager mPackageManager;
+ private final IPackageManager mIPackageManager;
+ /**
+ * Internal collection of device admin info objects for all profiles associated with the current
+ * user.
+ */
+ private final ArrayList<DeviceAdminListItem> mAdmins = new ArrayList<>();
+ private final SparseArray<ComponentName> mProfileOwnerComponents = new SparseArray<>();
+
+ private final BroadcastReceiver mBroadcastReceiver = new BroadcastReceiver() {
+ @Override
+ public void onReceive(Context context, Intent intent) {
+ // Refresh the list, if state change has been received. It could be that checkboxes
+ // need to be updated
+ if (TextUtils.equals(ACTION_DEVICE_POLICY_MANAGER_STATE_CHANGED, intent.getAction())) {
+ updateList();
+ }
+ }
+ };
+
+ private PreferenceGroup mPreferenceGroup;
+ private FooterPreferenceMixin mFooterPreferenceMixin;
+
+ static {
+ FILTER.addAction(ACTION_DEVICE_POLICY_MANAGER_STATE_CHANGED);
+ }
+
+ public DeviceAdminListPreferenceController(Context context, String preferenceKey) {
+ super(context, preferenceKey);
+ mDPM = (DevicePolicyManager) context.getSystemService(Context.DEVICE_POLICY_SERVICE);
+ mUm = (UserManager) context.getSystemService(Context.USER_SERVICE);
+ mPackageManager = mContext.getPackageManager();
+ mIPackageManager = AppGlobals.getPackageManager();
+ }
+
+ public DeviceAdminListPreferenceController setFooterPreferenceMixin(
+ FooterPreferenceMixin mixin) {
+ mFooterPreferenceMixin = mixin;
+ return this;
+ }
+
+ @Override
+ public int getAvailabilityStatus() {
+ return AVAILABLE;
+ }
+
+ @Override
+ public void displayPreference(PreferenceScreen screen) {
+ super.displayPreference(screen);
+ mPreferenceGroup = (PreferenceGroup) screen.findPreference(getPreferenceKey());
+ }
+
+ @Override
+ public void onStart() {
+ mContext.registerReceiverAsUser(
+ mBroadcastReceiver, UserHandle.ALL, FILTER,
+ null /* broadcastPermission */, null /* scheduler */);
+ }
+
+ @Override
+ public void updateState(Preference preference) {
+ super.updateState(preference);
+ mProfileOwnerComponents.clear();
+ final List<UserHandle> profiles = mUm.getUserProfiles();
+ final int profilesSize = profiles.size();
+ for (int i = 0; i < profilesSize; ++i) {
+ final int profileId = profiles.get(i).getIdentifier();
+ mProfileOwnerComponents.put(profileId, mDPM.getProfileOwnerAsUser(profileId));
+ }
+ updateList();
+ }
+
+ @Override
+ public void onStop() {
+ mContext.unregisterReceiver(mBroadcastReceiver);
+ }
+
+ @VisibleForTesting
+ void updateList() {
+ refreshData();
+ refreshUI();
+ }
+
+ private void refreshData() {
+ mAdmins.clear();
+ final List<UserHandle> profiles = mUm.getUserProfiles();
+ for (UserHandle profile : profiles) {
+ final int profileId = profile.getIdentifier();
+ updateAvailableAdminsForProfile(profileId);
+ }
+ Collections.sort(mAdmins);
+ }
+
+ private void refreshUI() {
+ if (mPreferenceGroup == null) {
+ return;
+ }
+ if (mFooterPreferenceMixin != null) {
+ final FooterPreference footer = mFooterPreferenceMixin.createFooterPreference();
+ footer.setTitle(R.string.no_device_admins);
+ footer.setVisible(mAdmins.isEmpty());
+ }
+ final Map<String, SwitchPreference> preferenceCache = new ArrayMap<>();
+ final Context prefContext = mPreferenceGroup.getContext();
+ final int childrenCount = mPreferenceGroup.getPreferenceCount();
+ for (int i = 0; i < childrenCount; i++) {
+ SwitchPreference pref = (SwitchPreference) mPreferenceGroup.getPreference(i);
+ preferenceCache.put(pref.getKey(), pref);
+ }
+ for (DeviceAdminListItem item : mAdmins) {
+ final String key = item.getKey();
+ SwitchPreference pref = preferenceCache.remove(key);
+ if (pref == null) {
+ pref = new SwitchPreference(prefContext);
+ mPreferenceGroup.addPreference(pref);
+ }
+ bindPreference(item, pref);
+ }
+ for (SwitchPreference unusedCacheItem : preferenceCache.values()) {
+ mPreferenceGroup.removePreference(unusedCacheItem);
+ }
+ }
+
+ private void bindPreference(DeviceAdminListItem item, SwitchPreference pref) {
+ pref.setKey(item.getKey());
+ pref.setTitle(item.getName());
+ pref.setIcon(item.getIcon());
+ pref.setChecked(item.isActive());
+ pref.setSummary(item.getDescription());
+ pref.setEnabled(item.isEnabled());
+ pref.setOnPreferenceClickListener(preference -> {
+ final UserHandle user = item.getUser();
+ mContext.startActivityAsUser(item.getLaunchIntent(mContext), user);
+ return true;
+ });
+ pref.setOnPreferenceChangeListener((preference, newValue) -> false);
+ }
+
+ /**
+ * Add device admins to the internal collection that belong to a profile.
+ *
+ * @param profileId the profile identifier.
+ */
+ private void updateAvailableAdminsForProfile(final int profileId) {
+ // We are adding the union of two sets 'A' and 'B' of device admins to mAvailableAdmins.
+ // - Set 'A' is the set of active admins for the profile
+ // - set 'B' is the set of listeners to DeviceAdminReceiver.ACTION_DEVICE_ADMIN_ENABLED for
+ // the profile.
+
+ // Add all of set 'A' to mAvailableAdmins.
+ final List<ComponentName> activeAdminsForProfile = mDPM.getActiveAdminsAsUser(profileId);
+ addActiveAdminsForProfile(activeAdminsForProfile, profileId);
+
+ // Collect set 'B' and add B-A to mAvailableAdmins.
+ addDeviceAdminBroadcastReceiversForProfile(activeAdminsForProfile, profileId);
+ }
+
+ /**
+ * Add a {@link DeviceAdminInfo} object to the internal collection of available admins for all
+ * active admin components associated with a profile.
+ */
+ private void addActiveAdminsForProfile(List<ComponentName> activeAdmins, int profileId) {
+ if (activeAdmins == null) {
+ return;
+ }
+
+ for (ComponentName activeAdmin : activeAdmins) {
+ final ActivityInfo ai;
+ try {
+ ai = mIPackageManager.getReceiverInfo(activeAdmin,
+ PackageManager.GET_META_DATA |
+ PackageManager.MATCH_DISABLED_UNTIL_USED_COMPONENTS |
+ PackageManager.MATCH_DIRECT_BOOT_UNAWARE |
+ PackageManager.MATCH_DIRECT_BOOT_AWARE, profileId);
+ } catch (RemoteException e) {
+ Log.w(TAG, "Unable to load component: " + activeAdmin);
+ continue;
+ }
+ final DeviceAdminInfo deviceAdminInfo = DeviceAdminUtils.createDeviceAdminInfo(
+ mContext, ai);
+ if (deviceAdminInfo == null) {
+ continue;
+ }
+ mAdmins.add(new DeviceAdminListItem(mContext, deviceAdminInfo));
+ }
+ }
+
+ /**
+ * Add a profile's device admins that are receivers of
+ * {@code DeviceAdminReceiver.ACTION_DEVICE_ADMIN_ENABLED} to the internal collection if they
+ * haven't been added yet.
+ *
+ * @param alreadyAddedComponents the set of active admin component names. Receivers of
+ * {@code DeviceAdminReceiver.ACTION_DEVICE_ADMIN_ENABLED}
+ * whose component is in this
+ * set are not added to the internal collection again.
+ * @param profileId the identifier of the profile
+ */
+ private void addDeviceAdminBroadcastReceiversForProfile(
+ Collection<ComponentName> alreadyAddedComponents, int profileId) {
+ final List<ResolveInfo> enabledForProfile = mPackageManager.queryBroadcastReceiversAsUser(
+ new Intent(DeviceAdminReceiver.ACTION_DEVICE_ADMIN_ENABLED),
+ PackageManager.GET_META_DATA | PackageManager.MATCH_DISABLED_UNTIL_USED_COMPONENTS,
+ profileId);
+ if (enabledForProfile == null) {
+ return;
+ }
+ for (ResolveInfo resolveInfo : enabledForProfile) {
+ final ComponentName riComponentName =
+ new ComponentName(resolveInfo.activityInfo.packageName,
+ resolveInfo.activityInfo.name);
+ if (alreadyAddedComponents != null
+ && alreadyAddedComponents.contains(riComponentName)) {
+ continue;
+ }
+ DeviceAdminInfo deviceAdminInfo = DeviceAdminUtils.createDeviceAdminInfo(
+ mContext, resolveInfo.activityInfo);
+ // add only visible ones (note: active admins are added regardless of visibility)
+ if (deviceAdminInfo != null && deviceAdminInfo.isVisible()) {
+ if (!deviceAdminInfo.getActivityInfo().applicationInfo.isInternal()) {
+ continue;
+ }
+ mAdmins.add(new DeviceAdminListItem(mContext, deviceAdminInfo));
+ }
+ }
+ }
+}
diff --git a/src/com/android/settings/applications/specialaccess/deviceadmin/DeviceAdminSettings.java b/src/com/android/settings/applications/specialaccess/deviceadmin/DeviceAdminSettings.java
index 6e0f60c..4cd4c04 100644
--- a/src/com/android/settings/applications/specialaccess/deviceadmin/DeviceAdminSettings.java
+++ b/src/com/android/settings/applications/specialaccess/deviceadmin/DeviceAdminSettings.java
@@ -16,448 +16,55 @@
package com.android.settings.applications.specialaccess.deviceadmin;
-import android.app.Activity;
-import android.app.AppGlobals;
-import android.app.ListFragment;
-import android.app.admin.DeviceAdminInfo;
-import android.app.admin.DeviceAdminReceiver;
-import android.app.admin.DevicePolicyManager;
-import android.content.BroadcastReceiver;
-import android.content.ComponentName;
import android.content.Context;
-import android.content.Intent;
-import android.content.IntentFilter;
-import android.content.pm.ActivityInfo;
-import android.content.pm.IPackageManager;
-import android.content.pm.PackageManager;
-import android.content.pm.ResolveInfo;
-import android.content.res.Resources;
-import android.graphics.drawable.Drawable;
-import android.os.Bundle;
-import android.os.RemoteException;
-import android.os.UserHandle;
-import android.os.UserManager;
-import android.util.Log;
-import android.util.SparseArray;
-import android.view.LayoutInflater;
-import android.view.View;
-import android.view.ViewGroup;
-import android.widget.BaseAdapter;
-import android.widget.ImageView;
-import android.widget.ListView;
-import android.widget.Switch;
-import android.widget.TextView;
+import android.provider.SearchIndexableResource;
import com.android.internal.logging.nano.MetricsProto;
import com.android.settings.R;
-import com.android.settings.Utils;
-import com.android.settings.overlay.FeatureFactory;
-import com.android.settingslib.core.instrumentation.Instrumentable;
-import com.android.settingslib.core.instrumentation.VisibilityLoggerMixin;
+import com.android.settings.dashboard.DashboardFragment;
+import com.android.settings.search.BaseSearchIndexProvider;
+import com.android.settings.search.Indexable;
+import com.android.settingslib.search.SearchIndexable;
-import org.xmlpull.v1.XmlPullParserException;
-
-import java.io.IOException;
import java.util.ArrayList;
-import java.util.Collection;
-import java.util.Collections;
import java.util.List;
-public class DeviceAdminSettings extends ListFragment implements Instrumentable {
+@SearchIndexable
+public class DeviceAdminSettings extends DashboardFragment {
static final String TAG = "DeviceAdminSettings";
- private VisibilityLoggerMixin mVisibilityLoggerMixin;
- private DevicePolicyManager mDPM;
- private UserManager mUm;
-
- private static class DeviceAdminListItem implements Comparable<DeviceAdminListItem> {
- public DeviceAdminInfo info;
-
- // These aren't updated so they keep a stable sort order if user activates / de-activates
- // an admin.
- public String name;
- public boolean active;
-
- public int compareTo(DeviceAdminListItem other) {
- // Sort active admins first, then by name.
- if (this.active != other.active) {
- return this.active ? -1 : 1;
- }
- return this.name.compareTo(other.name);
- }
- }
-
- /**
- * Internal collection of device admin info objects for all profiles associated with the current
- * user.
- */
- private final ArrayList<DeviceAdminListItem>
- mAdmins = new ArrayList<DeviceAdminListItem>();
-
- private String mDeviceOwnerPkg;
- private SparseArray<ComponentName> mProfileOwnerComponents = new SparseArray<ComponentName>();
-
- private final BroadcastReceiver mBroadcastReceiver = new BroadcastReceiver() {
- @Override
- public void onReceive(Context context, Intent intent) {
- // Refresh the list, if state change has been received. It could be that checkboxes
- // need to be updated
- if (DevicePolicyManager.ACTION_DEVICE_POLICY_MANAGER_STATE_CHANGED.equals(
- intent.getAction())) {
- updateList();
- }
- }
- };
-
- @Override
public int getMetricsCategory() {
return MetricsProto.MetricsEvent.DEVICE_ADMIN_SETTINGS;
}
@Override
- public void onCreate(Bundle icicle) {
- super.onCreate(icicle);
- mVisibilityLoggerMixin = new VisibilityLoggerMixin(getMetricsCategory(),
- FeatureFactory.getFactory(getContext()).getMetricsFeatureProvider());
+ public void onAttach(Context context) {
+ super.onAttach(context);
+ use(DeviceAdminListPreferenceController.class).setFooterPreferenceMixin(
+ mFooterPreferenceMixin);
}
@Override
- public View onCreateView(LayoutInflater inflater, ViewGroup container,
- Bundle savedInstanceState) {
- mDPM = (DevicePolicyManager) getActivity().getSystemService(Context.DEVICE_POLICY_SERVICE);
- mUm = (UserManager) getActivity().getSystemService(Context.USER_SERVICE);
- return inflater.inflate(R.layout.device_admin_settings, container, false);
+ protected int getPreferenceScreenResId() {
+ return R.xml.device_admin_settings;
}
@Override
- public void onActivityCreated(Bundle savedInstanceState) {
- super.onActivityCreated(savedInstanceState);
- setHasOptionsMenu(true);
- Utils.forceCustomPadding(getListView(), true /* additive padding */);
- getActivity().setTitle(R.string.manage_device_admin);
+ protected String getLogTag() {
+ return TAG;
}
- @Override
- public void onResume() {
- super.onResume();
- final Activity activity = getActivity();
- mVisibilityLoggerMixin.onResume();
- IntentFilter filter = new IntentFilter();
- filter.addAction(DevicePolicyManager.ACTION_DEVICE_POLICY_MANAGER_STATE_CHANGED);
- activity.registerReceiverAsUser(
- mBroadcastReceiver, UserHandle.ALL, filter, null, null);
+ public static final Indexable.SearchIndexProvider SEARCH_INDEX_DATA_PROVIDER =
+ new BaseSearchIndexProvider() {
+ @Override
+ public List<SearchIndexableResource> getXmlResourcesToIndex(Context context,
+ boolean enabled) {
+ final ArrayList<SearchIndexableResource> result = new ArrayList<>();
- final ComponentName deviceOwnerComponent = mDPM.getDeviceOwnerComponentOnAnyUser();
- mDeviceOwnerPkg =
- deviceOwnerComponent != null ? deviceOwnerComponent.getPackageName() : null;
- mProfileOwnerComponents.clear();
- final List<UserHandle> profiles = mUm.getUserProfiles();
- final int profilesSize = profiles.size();
- for (int i = 0; i < profilesSize; ++i) {
- final int profileId = profiles.get(i).getIdentifier();
- mProfileOwnerComponents.put(profileId, mDPM.getProfileOwnerAsUser(profileId));
- }
- updateList();
- }
-
- @Override
- public void onPause() {
- final Activity activity = getActivity();
- activity.unregisterReceiver(mBroadcastReceiver);
- mVisibilityLoggerMixin.onPause();
- super.onPause();
- }
-
- /**
- * Update the internal collection of available admins for all profiles associated with the
- * current user.
- */
- void updateList() {
- mAdmins.clear();
-
- final List<UserHandle> profiles = mUm.getUserProfiles();
- final int profilesSize = profiles.size();
- for (int i = 0; i < profilesSize; ++i) {
- final int profileId = profiles.get(i).getIdentifier();
- updateAvailableAdminsForProfile(profileId);
- }
- Collections.sort(mAdmins);
-
- getListView().setAdapter(new PolicyListAdapter());
- }
-
- @Override
- public void onListItemClick(ListView l, View v, int position, long id) {
- Object o = l.getAdapter().getItem(position);
- DeviceAdminInfo dpi = (DeviceAdminInfo) o;
- final UserHandle user = new UserHandle(getUserId(dpi));
- final Activity activity = getActivity();
- Intent intent = new Intent(activity, DeviceAdminAdd.class);
- intent.putExtra(DevicePolicyManager.EXTRA_DEVICE_ADMIN, dpi.getComponent());
- activity.startActivityAsUser(intent, user);
- }
-
- static class ViewHolder {
- ImageView icon;
- TextView name;
- Switch checkbox;
- TextView description;
- }
-
- class PolicyListAdapter extends BaseAdapter {
- final LayoutInflater mInflater;
-
- PolicyListAdapter() {
- mInflater = (LayoutInflater)
- getActivity().getSystemService(Context.LAYOUT_INFLATER_SERVICE);
- }
-
- @Override
- public boolean hasStableIds() {
- return false;
- }
-
- @Override
- public int getCount() {
- return mAdmins.size();
- }
-
- /**
- * The item for the given position in the list.
- *
- * @return DeviceAdminInfo object for actual device admins.
- */
- @Override
- public Object getItem(int position) {
- return ((DeviceAdminListItem) (mAdmins.get(position))).info;
- }
-
- @Override
- public long getItemId(int position) {
- return position;
- }
-
- @Override
- public boolean areAllItemsEnabled() {
- return false;
- }
-
- /**
- * See {@link #getItemViewType} for the view types.
- */
- @Override
- public int getViewTypeCount() {
- return 1;
- }
-
- /**
- * Returns 0 for all types.
- */
- @Override
- public int getItemViewType(int position) {
- return 0;
- }
-
- @Override
- public boolean isEnabled(int position) {
- Object o = getItem(position);
- return isEnabled(o);
- }
-
- private boolean isEnabled(Object o) {
- DeviceAdminInfo info = (DeviceAdminInfo) o;
- // Disable item if admin is being removed
- if (isRemovingAdmin(info)) {
- return false;
- }
- return true;
- }
-
- @Override
- public View getView(int position, View convertView, ViewGroup parent) {
- Object o = getItem(position);
- if (convertView == null) {
- convertView = newDeviceAdminView(parent);
- }
- bindView(convertView, (DeviceAdminInfo) o);
- return convertView;
- }
-
- private View newDeviceAdminView(ViewGroup parent) {
- View v = mInflater.inflate(R.layout.device_admin_item, parent, false);
- ViewHolder h = new ViewHolder();
- h.icon = v.findViewById(R.id.icon);
- h.name = v.findViewById(R.id.name);
- h.checkbox = v.findViewById(R.id.checkbox);
- h.description = v.findViewById(R.id.description);
- v.setTag(h);
- return v;
- }
-
- private void bindView(View view, DeviceAdminInfo item) {
- final Activity activity = getActivity();
- ViewHolder vh = (ViewHolder) view.getTag();
- Drawable activityIcon = item.loadIcon(activity.getPackageManager());
- Drawable badgedIcon = activity.getPackageManager().getUserBadgedIcon(
- activityIcon, new UserHandle(getUserId(item)));
- vh.icon.setImageDrawable(badgedIcon);
- vh.name.setText(item.loadLabel(activity.getPackageManager()));
- vh.checkbox.setChecked(isActiveAdmin(item));
- final boolean enabled = isEnabled(item);
- try {
- vh.description.setText(item.loadDescription(activity.getPackageManager()));
- } catch (Resources.NotFoundException e) {
- }
- vh.checkbox.setEnabled(enabled);
- vh.name.setEnabled(enabled);
- vh.description.setEnabled(enabled);
- vh.icon.setEnabled(enabled);
- }
- }
-
- private boolean isDeviceOwner(DeviceAdminInfo item) {
- return getUserId(item) == UserHandle.myUserId()
- && item.getPackageName().equals(mDeviceOwnerPkg);
- }
-
- private boolean isProfileOwner(DeviceAdminInfo item) {
- ComponentName profileOwner = mProfileOwnerComponents.get(getUserId(item));
- return item.getComponent().equals(profileOwner);
- }
-
- private boolean isActiveAdmin(DeviceAdminInfo item) {
- return mDPM.isAdminActiveAsUser(item.getComponent(), getUserId(item));
- }
-
- private boolean isRemovingAdmin(DeviceAdminInfo item) {
- return mDPM.isRemovingAdmin(item.getComponent(), getUserId(item));
- }
-
- /**
- * Add device admins to the internal collection that belong to a profile.
- *
- * @param profileId the profile identifier.
- */
- private void updateAvailableAdminsForProfile(final int profileId) {
- // We are adding the union of two sets 'A' and 'B' of device admins to mAvailableAdmins.
- // Set 'A' is the set of active admins for the profile whereas set 'B' is the set of
- // listeners to DeviceAdminReceiver.ACTION_DEVICE_ADMIN_ENABLED for the profile.
-
- // Add all of set 'A' to mAvailableAdmins.
- List<ComponentName> activeAdminsListForProfile = mDPM.getActiveAdminsAsUser(profileId);
- addActiveAdminsForProfile(activeAdminsListForProfile, profileId);
-
- // Collect set 'B' and add B-A to mAvailableAdmins.
- addDeviceAdminBroadcastReceiversForProfile(activeAdminsListForProfile, profileId);
- }
-
- /**
- * Add a profile's device admins that are receivers of
- * {@code DeviceAdminReceiver.ACTION_DEVICE_ADMIN_ENABLED} to the internal collection if they
- * haven't been added yet.
- *
- * @param alreadyAddedComponents the set of active admin component names. Receivers of
- * {@code DeviceAdminReceiver.ACTION_DEVICE_ADMIN_ENABLED} whose component is in this
- * set are not added to the internal collection again.
- * @param profileId the identifier of the profile
- */
- private void addDeviceAdminBroadcastReceiversForProfile(
- Collection<ComponentName> alreadyAddedComponents, final int profileId) {
- final PackageManager pm = getActivity().getPackageManager();
- List<ResolveInfo> enabledForProfile = pm.queryBroadcastReceiversAsUser(
- new Intent(DeviceAdminReceiver.ACTION_DEVICE_ADMIN_ENABLED),
- PackageManager.GET_META_DATA | PackageManager.GET_DISABLED_UNTIL_USED_COMPONENTS,
- profileId);
- if (enabledForProfile == null) {
- enabledForProfile = Collections.emptyList();
- }
- final int n = enabledForProfile.size();
- for (int i = 0; i < n; ++i) {
- ResolveInfo resolveInfo = enabledForProfile.get(i);
- ComponentName riComponentName =
- new ComponentName(resolveInfo.activityInfo.packageName,
- resolveInfo.activityInfo.name);
- if (alreadyAddedComponents == null
- || !alreadyAddedComponents.contains(riComponentName)) {
- DeviceAdminInfo deviceAdminInfo = createDeviceAdminInfo(resolveInfo.activityInfo);
- // add only visible ones (note: active admins are added regardless of visibility)
- if (deviceAdminInfo != null && deviceAdminInfo.isVisible()) {
- if (!deviceAdminInfo.getActivityInfo().applicationInfo.isInternal()) {
- continue;
- }
- DeviceAdminListItem item = new DeviceAdminListItem();
- item.info = deviceAdminInfo;
- item.name = deviceAdminInfo.loadLabel(pm).toString();
- // Active ones already added.
- item.active = false;
- mAdmins.add(item);
+ final SearchIndexableResource sir = new SearchIndexableResource(context);
+ sir.xmlResId = R.xml.device_admin_settings;
+ result.add(sir);
+ return result;
}
- }
- }
- }
-
- /**
- * Add a {@link DeviceAdminInfo} object to the internal collection of available admins for all
- * active admin components associated with a profile.
- *
- * @param profileId a profile identifier.
- */
- private void addActiveAdminsForProfile(final List<ComponentName> activeAdmins,
- final int profileId) {
- if (activeAdmins != null) {
- final PackageManager packageManager = getActivity().getPackageManager();
- final IPackageManager iPackageManager = AppGlobals.getPackageManager();
- final int n = activeAdmins.size();
- for (int i = 0; i < n; ++i) {
- final ComponentName activeAdmin = activeAdmins.get(i);
- final ActivityInfo ai;
- try {
- ai = iPackageManager.getReceiverInfo(activeAdmin,
- PackageManager.GET_META_DATA |
- PackageManager.GET_DISABLED_UNTIL_USED_COMPONENTS |
- PackageManager.MATCH_DIRECT_BOOT_UNAWARE |
- PackageManager.MATCH_DIRECT_BOOT_AWARE, profileId);
- } catch (RemoteException e) {
- Log.w(TAG, "Unable to load component: " + activeAdmin);
- continue;
- }
- final DeviceAdminInfo deviceAdminInfo = createDeviceAdminInfo(ai);
- if (deviceAdminInfo == null) {
- continue;
- }
- // Don't do the applicationInfo.isInternal() check here; if an active
- // admin is already on SD card, just show it.
- final DeviceAdminListItem item = new DeviceAdminListItem();
- item.info = deviceAdminInfo;
- item.name = deviceAdminInfo.loadLabel(packageManager).toString();
- item.active = true;
- mAdmins.add(item);
- }
- }
- }
-
- /**
- * Creates a device admin info object for the resolved intent that points to the component of
- * the device admin.
- *
- * @param ai ActivityInfo for the admin component.
- * @return new {@link DeviceAdminInfo} object or null if there was an error.
- */
- private DeviceAdminInfo createDeviceAdminInfo(ActivityInfo ai) {
- try {
- return new DeviceAdminInfo(getActivity(), ai);
- } catch (XmlPullParserException|IOException e) {
- Log.w(TAG, "Skipping " + ai, e);
- }
- return null;
- }
-
- /**
- * Extracts the user id from a device admin info object.
- * @param adminInfo the device administrator info.
- * @return identifier of the user associated with the device admin.
- */
- private int getUserId(DeviceAdminInfo adminInfo) {
- return UserHandle.getUserId(adminInfo.getActivityInfo().applicationInfo.uid);
- }
+ };
}
diff --git a/src/com/android/settings/applications/specialaccess/deviceadmin/DeviceAdminUtils.java b/src/com/android/settings/applications/specialaccess/deviceadmin/DeviceAdminUtils.java
new file mode 100644
index 0000000..13d9d20
--- /dev/null
+++ b/src/com/android/settings/applications/specialaccess/deviceadmin/DeviceAdminUtils.java
@@ -0,0 +1,58 @@
+/*
+ * Copyright (C) 2018 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.deviceadmin;
+
+import android.app.admin.DeviceAdminInfo;
+import android.content.Context;
+import android.content.pm.ActivityInfo;
+import android.os.UserHandle;
+import android.util.Log;
+
+import org.xmlpull.v1.XmlPullParserException;
+
+import java.io.IOException;
+
+public class DeviceAdminUtils {
+
+ private static final String TAG = "DeviceAdminUtils";
+
+ /**
+ * Creates a device admin info object for the resolved intent that points to the component of
+ * the device admin.
+ *
+ * @param ai ActivityInfo for the admin component.
+ * @return new {@link DeviceAdminInfo} object or null if there was an error.
+ */
+ public static DeviceAdminInfo createDeviceAdminInfo(Context context, ActivityInfo ai) {
+ try {
+ return new DeviceAdminInfo(context, ai);
+ } catch (XmlPullParserException | IOException e) {
+ Log.w(TAG, "Skipping " + ai, e);
+ }
+ return null;
+ }
+
+ /**
+ * Extracts the user id from a device admin info object.
+ *
+ * @param adminInfo the device administrator info.
+ * @return identifier of the user associated with the device admin.
+ */
+ public static int getUserIdFromDeviceAdminInfo(DeviceAdminInfo adminInfo) {
+ return UserHandle.getUserId(adminInfo.getActivityInfo().applicationInfo.uid);
+ }
+}
diff --git a/src/com/android/settings/applications/specialaccess/deviceadmin/DeviceAdministratorsController.java b/src/com/android/settings/applications/specialaccess/deviceadmin/DeviceAdministratorsController.java
deleted file mode 100644
index 5b05b86..0000000
--- a/src/com/android/settings/applications/specialaccess/deviceadmin/DeviceAdministratorsController.java
+++ /dev/null
@@ -1,36 +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.applications.specialaccess.deviceadmin;
-
-import android.content.Context;
-
-import com.android.settings.R;
-import com.android.settings.core.BasePreferenceController;
-
-public class DeviceAdministratorsController extends BasePreferenceController {
-
- public DeviceAdministratorsController(Context context, String key) {
- super(context, key);
- }
-
- @AvailabilityStatus
- public int getAvailabilityStatus() {
- return mContext.getResources().getBoolean(R.bool.config_show_device_administrators)
- ? AVAILABLE
- : UNSUPPORTED_ON_DEVICE;
- }
-}
\ No newline at end of file
diff --git a/src/com/android/settings/datetime/DatePreferenceController.java b/src/com/android/settings/datetime/DatePreferenceController.java
index 856722c..ceb65ca 100644
--- a/src/com/android/settings/datetime/DatePreferenceController.java
+++ b/src/com/android/settings/datetime/DatePreferenceController.java
@@ -20,8 +20,6 @@
import android.app.AlarmManager;
import android.app.DatePickerDialog;
import android.content.Context;
-import androidx.annotation.VisibleForTesting;
-import androidx.preference.Preference;
import android.text.TextUtils;
import android.text.format.DateFormat;
import android.widget.DatePicker;
@@ -32,6 +30,9 @@
import java.util.Calendar;
+import androidx.annotation.VisibleForTesting;
+import androidx.preference.Preference;
+
public class DatePreferenceController extends AbstractPreferenceController
implements PreferenceControllerMixin, DatePickerDialog.OnDateSetListener {
diff --git a/src/com/android/settings/enterprise/ManageDeviceAdminPreferenceController.java b/src/com/android/settings/enterprise/ManageDeviceAdminPreferenceController.java
index e4f2e76..bbb217b 100644
--- a/src/com/android/settings/enterprise/ManageDeviceAdminPreferenceController.java
+++ b/src/com/android/settings/enterprise/ManageDeviceAdminPreferenceController.java
@@ -14,42 +14,38 @@
package com.android.settings.enterprise;
import android.content.Context;
-import androidx.preference.Preference;
import com.android.settings.R;
-import com.android.settings.core.PreferenceControllerMixin;
+import com.android.settings.core.BasePreferenceController;
import com.android.settings.overlay.FeatureFactory;
-import com.android.settingslib.core.AbstractPreferenceController;
-public class ManageDeviceAdminPreferenceController extends AbstractPreferenceController implements
- PreferenceControllerMixin {
+import androidx.preference.Preference;
- private static final String KEY_MANAGE_DEVICE_ADMIN = "manage_device_admin";
+public class ManageDeviceAdminPreferenceController extends BasePreferenceController {
+
private final EnterprisePrivacyFeatureProvider mFeatureProvider;
- public ManageDeviceAdminPreferenceController(Context context) {
- super(context);
+ public ManageDeviceAdminPreferenceController(Context context, String key) {
+ super(context, key);
mFeatureProvider = FeatureFactory.getFactory(context)
.getEnterprisePrivacyFeatureProvider(context);
}
@Override
- public void updateState(Preference preference) {
+ public CharSequence getSummary() {
final int activeAdmins
= mFeatureProvider.getNumberOfActiveDeviceAdminsForCurrentUserAndManagedProfile();
- preference.setSummary(activeAdmins == 0
+ return activeAdmins == 0
? mContext.getResources().getString(R.string.number_of_device_admins_none)
: mContext.getResources().getQuantityString(R.plurals.number_of_device_admins,
- activeAdmins, activeAdmins));
+ activeAdmins, activeAdmins);
}
@Override
- public boolean isAvailable() {
- return mContext.getResources().getBoolean(R.bool.config_show_manage_device_admin);
+ public int getAvailabilityStatus() {
+ return mContext.getResources().getBoolean(R.bool.config_show_manage_device_admin)
+ ? AVAILABLE_UNSEARCHABLE
+ : UNSUPPORTED_ON_DEVICE;
}
- @Override
- public String getPreferenceKey() {
- return KEY_MANAGE_DEVICE_ADMIN;
- }
}
diff --git a/src/com/android/settings/notification/NotificationBackend.java b/src/com/android/settings/notification/NotificationBackend.java
index 5e8ef19..a2474fc 100644
--- a/src/com/android/settings/notification/NotificationBackend.java
+++ b/src/com/android/settings/notification/NotificationBackend.java
@@ -21,6 +21,8 @@
import android.app.INotificationManager;
import android.app.NotificationChannel;
import android.app.NotificationChannelGroup;
+import android.app.usage.IUsageStatsManager;
+import android.app.usage.UsageEvents;
import android.content.Context;
import android.content.Intent;
import android.content.pm.ApplicationInfo;
@@ -28,21 +30,30 @@
import android.content.pm.PackageManager;
import android.content.pm.ParceledListSlice;
import android.graphics.drawable.Drawable;
+import android.os.RemoteException;
import android.os.ServiceManager;
import android.os.UserHandle;
import android.service.notification.NotifyingApp;
+import android.text.format.DateUtils;
import android.util.IconDrawableFactory;
import android.util.Log;
import com.android.internal.annotations.VisibleForTesting;
+import com.android.settingslib.R;
import com.android.settingslib.Utils;
+import com.android.settingslib.utils.StringUtil;
import java.util.ArrayList;
+import java.util.HashMap;
import java.util.List;
+import java.util.Map;
public class NotificationBackend {
private static final String TAG = "NotificationBackend";
+ static IUsageStatsManager sUsageStatsManager = IUsageStatsManager.Stub.asInterface(
+ ServiceManager.getService(Context.USAGE_STATS_SERVICE));
+ private static final int DAYS_TO_CHECK = 7;
static INotificationManager sINM = INotificationManager.Stub.asInterface(
ServiceManager.getService(Context.NOTIFICATION_SERVICE));
@@ -62,6 +73,7 @@
row.userId = UserHandle.getUserId(row.uid);
row.blockedChannelCount = getBlockedChannelCount(row.pkg, row.uid);
row.channelCount = getChannelCount(row.pkg, row.uid);
+ recordAggregatedUsageEvents(context, row);
return row;
}
@@ -259,6 +271,89 @@
}
}
+ protected void recordAggregatedUsageEvents(Context context, AppRow appRow) {
+ long now = System.currentTimeMillis();
+ long startTime = now - (DateUtils.DAY_IN_MILLIS * DAYS_TO_CHECK);
+ UsageEvents events = null;
+ try {
+ events = sUsageStatsManager.queryEventsForPackageForUser(
+ startTime, now, appRow.userId, appRow.pkg, context.getPackageName());
+ } catch (RemoteException e) {
+ e.printStackTrace();
+ }
+ recordAggregatedUsageEvents(events, appRow);
+ }
+
+ protected void recordAggregatedUsageEvents(UsageEvents events, AppRow appRow) {
+ appRow.sentByChannel = new HashMap<>();
+ appRow.sentByApp = new NotificationsSentState();
+ if (events != null) {
+ UsageEvents.Event event = new UsageEvents.Event();
+ while (events.hasNextEvent()) {
+ events.getNextEvent(event);
+
+ if (event.getEventType() == UsageEvents.Event.NOTIFICATION_INTERRUPTION) {
+ String channelId = event.mNotificationChannelId;
+ if (channelId != null) {
+ NotificationsSentState stats = appRow.sentByChannel.get(channelId);
+ if (stats == null) {
+ stats = new NotificationsSentState();
+ appRow.sentByChannel.put(channelId, stats);
+ }
+ if (event.getTimeStamp() > stats.lastSent) {
+ stats.lastSent = event.getTimeStamp();
+ appRow.sentByApp.lastSent = event.getTimeStamp();
+ }
+ stats.sentCount++;
+ appRow.sentByApp.sentCount++;
+ calculateAvgSentCounts(stats);
+ }
+ }
+
+ }
+ calculateAvgSentCounts(appRow.sentByApp);
+ }
+ }
+
+ public static CharSequence getSentSummary(Context context, NotificationsSentState state,
+ boolean sortByRecency) {
+ if (state == null) {
+ return null;
+ }
+ if (sortByRecency) {
+ if (state.lastSent == 0) {
+ return context.getString(R.string.notifications_sent_never);
+ }
+ return StringUtil.formatRelativeTime(
+ context, System.currentTimeMillis() - state.lastSent, true);
+ } else {
+ if (state.avgSentWeekly > 0) {
+ return context.getString(R.string.notifications_sent_weekly, state.avgSentWeekly);
+ }
+ return context.getString(R.string.notifications_sent_daily, state.avgSentDaily);
+ }
+ }
+
+ private void calculateAvgSentCounts(NotificationsSentState stats) {
+ if (stats != null) {
+ stats.avgSentDaily = Math.round((float) stats.sentCount / DAYS_TO_CHECK);
+ if (stats.sentCount < DAYS_TO_CHECK) {
+ stats.avgSentWeekly = stats.sentCount;
+ }
+ }
+ }
+
+ /**
+ * NotificationsSentState contains how often an app sends notifications and how recently it sent
+ * one.
+ */
+ public static class NotificationsSentState {
+ public int avgSentDaily = 0;
+ public int avgSentWeekly = 0;
+ public long lastSent = 0;
+ public int sentCount = 0;
+ }
+
static class Row {
public String section;
}
@@ -278,5 +373,7 @@
public int userId;
public int blockedChannelCount;
public int channelCount;
+ public Map<String, NotificationsSentState> sentByChannel;
+ public NotificationsSentState sentByApp;
}
}
diff --git a/src/com/android/settings/notification/NotificationSettingsBase.java b/src/com/android/settings/notification/NotificationSettingsBase.java
index 9f53334..e1c5876 100644
--- a/src/com/android/settings/notification/NotificationSettingsBase.java
+++ b/src/com/android/settings/notification/NotificationSettingsBase.java
@@ -276,6 +276,8 @@
&& !groupBlocked);
channelPref.setKey(channel.getId());
channelPref.setTitle(channel.getName());
+ channelPref.setSummary(NotificationBackend.getSentSummary(
+ mContext, mAppRow.sentByChannel.get(channel.getId()), false));
channelPref.setChecked(channel.getImportance() != IMPORTANCE_NONE);
Bundle channelArgs = new Bundle();
channelArgs.putInt(AppInfoBase.ARG_PACKAGE_UID, mUid);
diff --git a/src/com/android/settings/security/SecuritySettings.java b/src/com/android/settings/security/SecuritySettings.java
index 7ce6560..b590618 100644
--- a/src/com/android/settings/security/SecuritySettings.java
+++ b/src/com/android/settings/security/SecuritySettings.java
@@ -29,7 +29,6 @@
import com.android.settings.dashboard.DashboardFragment;
import com.android.settings.dashboard.SummaryLoader;
import com.android.settings.enterprise.EnterprisePrivacyPreferenceController;
-import com.android.settings.enterprise.ManageDeviceAdminPreferenceController;
import com.android.settings.fingerprint.FingerprintProfileStatusPreferenceController;
import com.android.settings.fingerprint.FingerprintStatusPreferenceController;
import com.android.settings.location.LocationPreferenceController;
@@ -114,7 +113,6 @@
Lifecycle lifecycle, SecuritySettings host) {
final List<AbstractPreferenceController> controllers = new ArrayList<>();
controllers.add(new LocationPreferenceController(context, lifecycle));
- controllers.add(new ManageDeviceAdminPreferenceController(context));
controllers.add(new EnterprisePrivacyPreferenceController(context));
controllers.add(new ManageTrustAgentsPreferenceController(context));
controllers.add(new ScreenPinningPreferenceController(context));
diff --git a/tests/robotests/res/values-mcc999/config.xml b/tests/robotests/res/values-mcc999/config.xml
index 72f5134..2d95b65 100644
--- a/tests/robotests/res/values-mcc999/config.xml
+++ b/tests/robotests/res/values-mcc999/config.xml
@@ -33,7 +33,6 @@
<bool name="config_show_notification_volume">false</bool>
<bool name="config_show_screen_locking_sounds">false</bool>
<bool name="config_show_touch_sounds">false</bool>
- <bool name="config_show_device_administrators">false</bool>
<bool name="config_show_encryption_and_credentials_encryption_status">false</bool>
<bool name="config_show_premium_sms">false</bool>
<bool name="config_show_data_saver">false</bool>
diff --git a/tests/robotests/src/com/android/settings/applications/appinfo/AppNotificationPreferenceControllerTest.java b/tests/robotests/src/com/android/settings/applications/appinfo/AppNotificationPreferenceControllerTest.java
index 9678ecb..a70c3c4 100644
--- a/tests/robotests/src/com/android/settings/applications/appinfo/AppNotificationPreferenceControllerTest.java
+++ b/tests/robotests/src/com/android/settings/applications/appinfo/AppNotificationPreferenceControllerTest.java
@@ -25,6 +25,7 @@
import static org.mockito.Mockito.when;
import android.app.Activity;
+import android.app.usage.IUsageStatsManager;
import android.content.Context;
import android.content.Intent;
import android.content.pm.ApplicationInfo;
@@ -78,6 +79,8 @@
final ApplicationsState.AppEntry appEntry = mock(ApplicationsState.AppEntry.class);
appEntry.info = new ApplicationInfo();
when(mFragment.getAppEntry()).thenReturn(appEntry);
+ NotificationBackend backend = new NotificationBackend();
+ ReflectionHelpers.setField(backend, "sUsageStatsManager", mock(IUsageStatsManager.class));
ReflectionHelpers.setField(mController, "mBackend", new NotificationBackend());
mController.displayPreference(mScreen);
@@ -136,10 +139,12 @@
appRow.banned = false;
appRow.blockedChannelCount = 30;
appRow.channelCount = 60;
+ appRow.sentByApp = new NotificationBackend.NotificationsSentState();
+ appRow.sentByApp.avgSentWeekly = 4;
assertThat(mController.getNotificationSummary(
appRow, mContext).toString().contains("30")).isTrue();
- assertThat(mController.getNotificationSummary(
- appRow, mContext).toString().contains("On")).isTrue();
+ assertThat(mController.getNotificationSummary(appRow, mContext).toString().contains(
+ NotificationBackend.getSentSummary(mContext, appRow.sentByApp, false))).isTrue();
}
@Test
@@ -148,7 +153,10 @@
appRow.banned = false;
appRow.blockedChannelCount = 0;
appRow.channelCount = 10;
- assertThat(mController.getNotificationSummary(appRow, mContext).toString()).isEqualTo("On");
+ appRow.sentByApp = new NotificationBackend.NotificationsSentState();
+ appRow.sentByApp.avgSentDaily = 4;
+ assertThat(mController.getNotificationSummary(appRow, mContext).toString()).isEqualTo(
+ NotificationBackend.getSentSummary(mContext, appRow.sentByApp, false));
}
@Test
@@ -157,6 +165,9 @@
appRow.banned = false;
appRow.blockedChannelCount = 0;
appRow.channelCount = 0;
- assertThat(mController.getNotificationSummary(appRow, mContext).toString()).isEqualTo("On");
+ appRow.sentByApp = new NotificationBackend.NotificationsSentState();
+ appRow.sentByApp.avgSentDaily = 7;
+ assertThat(mController.getNotificationSummary(appRow, mContext).toString()).isEqualTo(
+ NotificationBackend.getSentSummary(mContext, appRow.sentByApp, false));
}
}
diff --git a/tests/robotests/src/com/android/settings/applications/specialaccess/deviceadmin/DeviceAdminListItemTest.java b/tests/robotests/src/com/android/settings/applications/specialaccess/deviceadmin/DeviceAdminListItemTest.java
new file mode 100644
index 0000000..e19d9e6
--- /dev/null
+++ b/tests/robotests/src/com/android/settings/applications/specialaccess/deviceadmin/DeviceAdminListItemTest.java
@@ -0,0 +1,76 @@
+/*
+ * Copyright (C) 2018 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.deviceadmin;
+
+
+import static com.google.common.truth.Truth.assertThat;
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.Mockito.when;
+
+import android.app.admin.DeviceAdminInfo;
+import android.content.ComponentName;
+import android.content.Context;
+import android.content.pm.ActivityInfo;
+import android.content.pm.ApplicationInfo;
+import android.content.pm.PackageManager;
+import android.graphics.Color;
+import android.graphics.drawable.ColorDrawable;
+
+import com.android.settings.testutils.SettingsRobolectricTestRunner;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
+import org.robolectric.RuntimeEnvironment;
+
+@RunWith(SettingsRobolectricTestRunner.class)
+public class DeviceAdminListItemTest {
+
+ @Mock
+ private DeviceAdminInfo mDeviceAdminInfo;
+ private Context mContext;
+
+
+ @Before
+ public void setUp() {
+ MockitoAnnotations.initMocks(this);
+ mContext = RuntimeEnvironment.application;
+ }
+
+ @Test
+ public void newInstance_shouldLoadInfoFromDeviceAdminInfo() {
+ final String label = "testlabel";
+ final String description = "testdesc";
+ final ComponentName cn = new ComponentName(mContext.getPackageName(), "test");
+ when(mDeviceAdminInfo.getActivityInfo()).thenReturn(new ActivityInfo());
+ mDeviceAdminInfo.getActivityInfo().applicationInfo = new ApplicationInfo();
+ when(mDeviceAdminInfo.loadLabel(any(PackageManager.class))).thenReturn(label);
+ when(mDeviceAdminInfo.loadDescription(any(PackageManager.class))).thenReturn(description);
+ when(mDeviceAdminInfo.loadIcon(any(PackageManager.class)))
+ .thenReturn(new ColorDrawable(Color.BLUE));
+ when(mDeviceAdminInfo.getComponent()).thenReturn(cn);
+
+ DeviceAdminListItem item = new DeviceAdminListItem(mContext, mDeviceAdminInfo);
+
+ assertThat(item.getKey()).isEqualTo(cn.flattenToShortString());
+ assertThat(item.getName()).isEqualTo(label);
+ assertThat(item.getDescription()).isEqualTo(description);
+ }
+
+}
diff --git a/tests/robotests/src/com/android/settings/applications/specialaccess/deviceadmin/DeviceAdminListPreferenceControllerTest.java b/tests/robotests/src/com/android/settings/applications/specialaccess/deviceadmin/DeviceAdminListPreferenceControllerTest.java
new file mode 100644
index 0000000..c99df01
--- /dev/null
+++ b/tests/robotests/src/com/android/settings/applications/specialaccess/deviceadmin/DeviceAdminListPreferenceControllerTest.java
@@ -0,0 +1,83 @@
+/*
+ * Copyright (C) 2018 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.deviceadmin;
+
+import static androidx.lifecycle.Lifecycle.Event.ON_START;
+import static androidx.lifecycle.Lifecycle.Event.ON_STOP;
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.ArgumentMatchers.eq;
+import static org.mockito.ArgumentMatchers.isNull;
+import static org.mockito.Mockito.spy;
+import static org.mockito.Mockito.verify;
+
+import android.content.BroadcastReceiver;
+import android.content.Context;
+import android.content.IntentFilter;
+import android.os.UserHandle;
+
+import com.android.settings.testutils.SettingsRobolectricTestRunner;
+import com.android.settingslib.core.lifecycle.Lifecycle;
+import com.android.settingslib.widget.FooterPreferenceMixin;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
+import org.robolectric.RuntimeEnvironment;
+
+import androidx.lifecycle.LifecycleOwner;
+
+@RunWith(SettingsRobolectricTestRunner.class)
+public class DeviceAdminListPreferenceControllerTest {
+
+ @Mock
+ private FooterPreferenceMixin mFooterPreferenceMixin;
+ private Context mContext;
+ private DeviceAdminListPreferenceController mController;
+ private LifecycleOwner mLifecycleOwner;
+ private Lifecycle mLifecycle;
+
+ @Before
+ public void setUp() {
+ MockitoAnnotations.initMocks(this);
+ mContext = spy(RuntimeEnvironment.application);
+
+ mLifecycleOwner = () -> mLifecycle;
+ mLifecycle = new Lifecycle(mLifecycleOwner);
+
+ mController = spy(new DeviceAdminListPreferenceController(mContext, "test_key")
+ .setFooterPreferenceMixin(mFooterPreferenceMixin));
+ mLifecycle.addObserver(mController);
+ }
+
+ @Test
+ public void onStart_registerReceiver() {
+ mLifecycle.handleLifecycleEvent(ON_START);
+
+ verify(mContext).registerReceiverAsUser(any(BroadcastReceiver.class), eq(UserHandle.ALL),
+ any(IntentFilter.class), isNull(), isNull());
+ }
+
+ @Test
+ public void onStop_unregisterReceiver() {
+ mLifecycle.handleLifecycleEvent(ON_START);
+ mLifecycle.handleLifecycleEvent(ON_STOP);
+
+ verify(mContext).unregisterReceiver(any(BroadcastReceiver.class));
+ }
+}
diff --git a/tests/robotests/src/com/android/settings/applications/specialaccess/deviceadmin/DeviceAdministratorsControllerTest.java b/tests/robotests/src/com/android/settings/applications/specialaccess/deviceadmin/DeviceAdministratorsControllerTest.java
deleted file mode 100644
index 6e3ff2c..0000000
--- a/tests/robotests/src/com/android/settings/applications/specialaccess/deviceadmin/DeviceAdministratorsControllerTest.java
+++ /dev/null
@@ -1,56 +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.applications.specialaccess.deviceadmin;
-
-import static com.google.common.truth.Truth.assertThat;
-import static org.mockito.Mockito.spy;
-
-import android.content.Context;
-
-import com.android.settings.testutils.SettingsRobolectricTestRunner;
-
-import org.junit.Before;
-import org.junit.Test;
-import org.junit.runner.RunWith;
-import org.mockito.MockitoAnnotations;
-import org.robolectric.RuntimeEnvironment;
-import org.robolectric.annotation.Config;
-
-@RunWith(SettingsRobolectricTestRunner.class)
-public class DeviceAdministratorsControllerTest {
-
- private Context mContext;
- private DeviceAdministratorsController mController;
-
- @Before
- public void setUp() {
- MockitoAnnotations.initMocks(this);
- mContext = spy(RuntimeEnvironment.application.getApplicationContext());
- mController = new DeviceAdministratorsController(mContext, "key");
- }
-
- @Test
- public void testDeviceAdministrators_byDefault_shouldBeShown() {
- assertThat(mController.isAvailable()).isTrue();
- }
-
- @Test
- @Config(qualifiers = "mcc999")
- public void testDeviceAdministrators_ifDisabled_shouldNotBeShown() {
- assertThat(mController.isAvailable()).isFalse();
- }
-}
diff --git a/tests/robotests/src/com/android/settings/development/MockLocationAppPreferenceControllerTest.java b/tests/robotests/src/com/android/settings/development/MockLocationAppPreferenceControllerTest.java
index e247485..a8f5341 100644
--- a/tests/robotests/src/com/android/settings/development/MockLocationAppPreferenceControllerTest.java
+++ b/tests/robotests/src/com/android/settings/development/MockLocationAppPreferenceControllerTest.java
@@ -1,6 +1,7 @@
package com.android.settings.development;
-import static com.android.settings.development.DevelopmentOptionsActivityRequestCodes.REQUEST_MOCK_LOCATION_APP;
+import static com.android.settings.development.DevelopmentOptionsActivityRequestCodes
+ .REQUEST_MOCK_LOCATION_APP;
import static com.google.common.truth.Truth.assertThat;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.anyInt;
@@ -16,14 +17,11 @@
import android.content.Intent;
import android.content.pm.ApplicationInfo;
import android.content.pm.PackageManager;
-import androidx.preference.Preference;
-import androidx.preference.PreferenceScreen;
import com.android.settings.R;
import com.android.settings.testutils.SettingsRobolectricTestRunner;
import org.junit.Before;
-import org.junit.Ignore;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.Mock;
@@ -33,8 +31,10 @@
import java.util.Collections;
+import androidx.preference.Preference;
+import androidx.preference.PreferenceScreen;
+
@RunWith(SettingsRobolectricTestRunner.class)
-@Ignore
public class MockLocationAppPreferenceControllerTest {
@Mock
@@ -47,10 +47,7 @@
private Preference mPreference;
@Mock
private PreferenceScreen mScreen;
- @Mock
- private AppOpsManager.PackageOps mPackageOps;
- @Mock
- private AppOpsManager.OpEntry mOpEntry;
+
@Mock
private ApplicationInfo mApplicationInfo;
@@ -71,11 +68,12 @@
@Test
public void updateState_foobarAppSelected_shouldSetSummaryToFoobar() {
final String appName = "foobar";
+
+ final AppOpsManager.PackageOps packageOps =
+ new AppOpsManager.PackageOps(appName, 0,
+ Collections.singletonList(createOpEntry(AppOpsManager.MODE_ALLOWED)));
when(mAppOpsManager.getPackagesForOps(any())).thenReturn(
- Collections.singletonList(mPackageOps));
- when(mPackageOps.getOps()).thenReturn(Collections.singletonList(mOpEntry));
- when(mOpEntry.getMode()).thenReturn(AppOpsManager.MODE_ALLOWED);
- when(mPackageOps.getPackageName()).thenReturn(appName);
+ Collections.singletonList(packageOps));
mController.updateState(mPreference);
@@ -98,16 +96,16 @@
final String newAppName = "bar";
final Intent intent = new Intent();
intent.setAction(newAppName);
+ final AppOpsManager.PackageOps packageOps = new AppOpsManager.PackageOps(prevAppName, 0,
+ Collections.singletonList(createOpEntry(AppOpsManager.MODE_ALLOWED)));
+
when(mAppOpsManager.getPackagesForOps(any()))
- .thenReturn(Collections.singletonList(mPackageOps));
- when(mPackageOps.getOps()).thenReturn(Collections.singletonList(mOpEntry));
- when(mOpEntry.getMode()).thenReturn(AppOpsManager.MODE_ALLOWED);
- when(mPackageOps.getPackageName()).thenReturn(prevAppName);
+ .thenReturn(Collections.singletonList(packageOps));
when(mPackageManager.getApplicationInfo(anyString(),
eq(PackageManager.MATCH_DISABLED_COMPONENTS))).thenReturn(mApplicationInfo);
final boolean handled =
- mController.onActivityResult(REQUEST_MOCK_LOCATION_APP, Activity.RESULT_OK, intent);
+ mController.onActivityResult(REQUEST_MOCK_LOCATION_APP, Activity.RESULT_OK, intent);
assertThat(handled).isTrue();
verify(mAppOpsManager).setMode(anyInt(), anyInt(), eq(newAppName),
@@ -141,4 +139,9 @@
assertThat(mController.handlePreferenceTreeClick(mPreference)).isFalse();
}
+
+ private AppOpsManager.OpEntry createOpEntry(int opMode) {
+ return new AppOpsManager.OpEntry(0, opMode, 0l /* time */, 0 /* rejectTime */,
+ 0 /* duration */, 0 /* proxyUid */, null /* proxyPackage */);
+ }
}
diff --git a/tests/robotests/src/com/android/settings/enterprise/ManageDeviceAdminPreferenceControllerTest.java b/tests/robotests/src/com/android/settings/enterprise/ManageDeviceAdminPreferenceControllerTest.java
index 885e890..e9066aa 100644
--- a/tests/robotests/src/com/android/settings/enterprise/ManageDeviceAdminPreferenceControllerTest.java
+++ b/tests/robotests/src/com/android/settings/enterprise/ManageDeviceAdminPreferenceControllerTest.java
@@ -22,7 +22,6 @@
import android.content.Context;
import android.content.res.Resources;
-import androidx.preference.Preference;
import com.android.settings.R;
import com.android.settings.testutils.FakeFeatureFactory;
@@ -36,6 +35,8 @@
import org.robolectric.RuntimeEnvironment;
import org.robolectric.annotation.Config;
+import androidx.preference.Preference;
+
@RunWith(SettingsRobolectricTestRunner.class)
public class ManageDeviceAdminPreferenceControllerTest {
@@ -51,7 +52,7 @@
MockitoAnnotations.initMocks(this);
mContext = spy(RuntimeEnvironment.application);
mFeatureFactory = FakeFeatureFactory.setupForTest();
- mController = new ManageDeviceAdminPreferenceController(mContext);
+ mController = new ManageDeviceAdminPreferenceController(mContext, "testkey");
}
@Test
@@ -60,7 +61,7 @@
when(mFeatureFactory.enterprisePrivacyFeatureProvider
.getNumberOfActiveDeviceAdminsForCurrentUserAndManagedProfile()).thenReturn(0);
- when (mContext.getResources()).thenReturn(mResources);
+ when(mContext.getResources()).thenReturn(mResources);
when(mResources.getString(R.string.number_of_device_admins_none))
.thenReturn("no apps");
mController.updateState(preference);
@@ -84,15 +85,4 @@
public void isAvailable_whenNotVisible_isFalse() {
assertThat(mController.isAvailable()).isFalse();
}
-
- @Test
- public void testHandlePreferenceTreeClick() {
- assertThat(mController.handlePreferenceTreeClick(new Preference(mContext, null, 0, 0)))
- .isFalse();
- }
-
- @Test
- public void testGetPreferenceKey() {
- assertThat(mController.getPreferenceKey()).isEqualTo("manage_device_admin");
- }
}
diff --git a/tests/robotests/src/com/android/settings/fuelgauge/RestrictAppPreferenceControllerTest.java b/tests/robotests/src/com/android/settings/fuelgauge/RestrictAppPreferenceControllerTest.java
index a377c16..81cf652 100644
--- a/tests/robotests/src/com/android/settings/fuelgauge/RestrictAppPreferenceControllerTest.java
+++ b/tests/robotests/src/com/android/settings/fuelgauge/RestrictAppPreferenceControllerTest.java
@@ -18,9 +18,7 @@
import static com.android.settings.SettingsActivity.EXTRA_SHOW_FRAGMENT;
import static com.android.settings.SettingsActivity.EXTRA_SHOW_FRAGMENT_TITLE_RESID;
-
import static com.google.common.truth.Truth.assertThat;
-
import static org.mockito.Matchers.any;
import static org.mockito.Mockito.doReturn;
import static org.mockito.Mockito.spy;
@@ -31,15 +29,12 @@
import android.content.Intent;
import android.os.UserHandle;
import android.os.UserManager;
-import androidx.preference.Preference;
import com.android.settings.R;
-import com.android.settings.SettingsActivity;
import com.android.settings.core.InstrumentedPreferenceFragment;
import com.android.settings.fuelgauge.batterytip.AppInfo;
import org.junit.Before;
-import org.junit.Ignore;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.ArgumentCaptor;
@@ -51,8 +46,9 @@
import java.util.ArrayList;
import java.util.List;
+import androidx.preference.Preference;
+
@RunWith(RobolectricTestRunner.class)
-@Ignore
public class RestrictAppPreferenceControllerTest {
private static final int ALLOWED_UID = 111;
private static final String ALLOWED_PACKAGE_NAME = "com.android.allowed.package";
@@ -63,15 +59,13 @@
@Mock
private AppOpsManager mAppOpsManager;
@Mock
- private AppOpsManager.PackageOps mRestrictedPackageOps;
- @Mock
- private AppOpsManager.PackageOps mAllowedPackageOps;
- @Mock
- private AppOpsManager.PackageOps mOtherUserPackageOps;
- @Mock
private InstrumentedPreferenceFragment mFragment;
@Mock
private UserManager mUserManager;
+
+ private AppOpsManager.PackageOps mRestrictedPackageOps;
+ private AppOpsManager.PackageOps mAllowedPackageOps;
+ private AppOpsManager.PackageOps mOtherUserPackageOps;
private List<AppOpsManager.PackageOps> mPackageOpsList;
private RestrictAppPreferenceController mRestrictAppPreferenceController;
private Preference mPreference;
@@ -89,15 +83,12 @@
AppOpsManager.OP_RUN_ANY_IN_BACKGROUND, AppOpsManager.MODE_IGNORED, 0, 0, 0, 0, "");
final List<AppOpsManager.OpEntry> restrictedOps = new ArrayList<>();
restrictedOps.add(restrictedOpEntry);
- doReturn(ALLOWED_UID).when(mAllowedPackageOps).getUid();
- doReturn(ALLOWED_PACKAGE_NAME).when(mAllowedPackageOps).getPackageName();
- doReturn(allowOps).when(mAllowedPackageOps).getOps();
- doReturn(RESTRICTED_UID).when(mRestrictedPackageOps).getUid();
- doReturn(RESTRICTED_PACKAGE_NAME).when(mRestrictedPackageOps).getPackageName();
- doReturn(restrictedOps).when(mRestrictedPackageOps).getOps();
- doReturn(OTHER_USER_UID).when(mOtherUserPackageOps).getUid();
- doReturn(RESTRICTED_PACKAGE_NAME).when(mOtherUserPackageOps).getPackageName();
- doReturn(restrictedOps).when(mOtherUserPackageOps).getOps();
+ mAllowedPackageOps = new AppOpsManager.PackageOps(
+ ALLOWED_PACKAGE_NAME, ALLOWED_UID, allowOps);
+ mRestrictedPackageOps = new AppOpsManager.PackageOps(
+ RESTRICTED_PACKAGE_NAME, RESTRICTED_UID, restrictedOps);
+ mOtherUserPackageOps = new AppOpsManager.PackageOps(
+ RESTRICTED_PACKAGE_NAME, OTHER_USER_UID, restrictedOps);
mContext = spy(RuntimeEnvironment.application);
doReturn(mAppOpsManager).when(mContext).getSystemService(Context.APP_OPS_SERVICE);
diff --git a/tests/robotests/src/com/android/settings/notification/NotificationBackendTest.java b/tests/robotests/src/com/android/settings/notification/NotificationBackendTest.java
index 4d7b07c..0f5f44d 100644
--- a/tests/robotests/src/com/android/settings/notification/NotificationBackendTest.java
+++ b/tests/robotests/src/com/android/settings/notification/NotificationBackendTest.java
@@ -16,17 +16,31 @@
package com.android.settings.notification;
+import static com.google.common.truth.Truth.assertThat;
+
import static junit.framework.Assert.assertEquals;
import static junit.framework.Assert.assertFalse;
import static junit.framework.Assert.assertNull;
import static junit.framework.Assert.assertTrue;
+import static org.mockito.ArgumentMatchers.anyInt;
+import static org.mockito.ArgumentMatchers.anyLong;
+import static org.mockito.ArgumentMatchers.anyString;
+import static org.mockito.Mockito.when;
+
+import android.app.usage.UsageEvents;
+import android.os.Parcel;
+
import com.android.settings.notification.NotificationBackend.AppRow;
import com.android.settings.testutils.SettingsRobolectricTestRunner;
import org.junit.Test;
import org.junit.runner.RunWith;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Map;
+
@RunWith(SettingsRobolectricTestRunner.class)
public class NotificationBackendTest {
@@ -118,4 +132,51 @@
assertTrue(appRow.lockedImportance);
assertEquals("SpecificChannel", appRow.lockedChannelId);
}
+
+ @Test
+ public void testGetAggregatedUsageEvents_multipleEventsAgg() {
+ List<UsageEvents.Event> events = new ArrayList<>();
+ UsageEvents.Event good = new UsageEvents.Event();
+ good.mEventType = UsageEvents.Event.NOTIFICATION_INTERRUPTION;
+ good.mPackage = "pkg";
+ good.mNotificationChannelId = "channel1";
+ good.mTimeStamp = 2;
+ events.add(good);
+ UsageEvents.Event good2 = new UsageEvents.Event();
+ good2.mEventType = UsageEvents.Event.NOTIFICATION_INTERRUPTION;
+ good2.mPackage = "pkg";
+ good2.mNotificationChannelId = "channel2";
+ good2.mTimeStamp = 3;
+ events.add(good2);
+ UsageEvents.Event good1 = new UsageEvents.Event();
+ good1.mEventType = UsageEvents.Event.NOTIFICATION_INTERRUPTION;
+ good1.mPackage = "pkg";
+ good1.mNotificationChannelId = "channel1";
+ good1.mTimeStamp = 6;
+ events.add(good1);
+ NotificationBackend backend = new NotificationBackend();
+
+ AppRow appRow = new AppRow();
+ appRow.pkg = "pkg";
+ backend.recordAggregatedUsageEvents(getUsageEvents(events), appRow);
+
+ assertThat(appRow.sentByChannel.get("channel1").sentCount).isEqualTo(2);
+ assertThat(appRow.sentByChannel.get("channel1").lastSent).isEqualTo(6);
+ assertThat(appRow.sentByChannel.get("channel1").avgSentWeekly).isEqualTo(2);
+ assertThat(appRow.sentByChannel.get("channel2").sentCount).isEqualTo(1);
+ assertThat(appRow.sentByChannel.get("channel2").lastSent).isEqualTo(3);
+ assertThat(appRow.sentByChannel.get("channel2").avgSentWeekly).isEqualTo(1);
+ assertThat(appRow.sentByApp.sentCount).isEqualTo(3);
+ assertThat(appRow.sentByApp.lastSent).isEqualTo(6);
+ assertThat(appRow.sentByApp.avgSentWeekly).isEqualTo(3);
+ }
+
+ private UsageEvents getUsageEvents(List<UsageEvents.Event> events) {
+ UsageEvents usageEvents = new UsageEvents(events, new String[] {"pkg"});
+ Parcel parcel = Parcel.obtain();
+ parcel.setDataPosition(0);
+ usageEvents.writeToParcel(parcel, 0);
+ parcel.setDataPosition(0);
+ return UsageEvents.CREATOR.createFromParcel(parcel);
+ }
}