OSDN Git Service

Clean up DeviceAdminSetting: make it into DashboardFragment
authorFan Zhang <zhfan@google.com>
Thu, 14 Jun 2018 20:17:51 +0000 (13:17 -0700)
committerFan Zhang <zhfan@google.com>
Mon, 18 Jun 2018 18:09:01 +0000 (18:09 +0000)
Change-Id: Ib5634f782daef15ab317175084c6813d6b8a8bb7
Fixes: 110207366
Test: robo

20 files changed:
res/layout/date_time_custom_list_item_2.xml [deleted file]
res/layout/device_admin_item.xml [deleted file]
res/layout/device_admin_settings.xml [deleted file]
res/values/bools.xml
res/xml/device_admin_settings.xml [new file with mode: 0644]
res/xml/security_dashboard_settings.xml
res/xml/special_access.xml
src/com/android/settings/applications/specialaccess/deviceadmin/DeviceAdminListItem.java [new file with mode: 0644]
src/com/android/settings/applications/specialaccess/deviceadmin/DeviceAdminListPreferenceController.java [new file with mode: 0644]
src/com/android/settings/applications/specialaccess/deviceadmin/DeviceAdminSettings.java
src/com/android/settings/applications/specialaccess/deviceadmin/DeviceAdminUtils.java [new file with mode: 0644]
src/com/android/settings/applications/specialaccess/deviceadmin/DeviceAdministratorsController.java [deleted file]
src/com/android/settings/datetime/DatePreferenceController.java
src/com/android/settings/enterprise/ManageDeviceAdminPreferenceController.java
src/com/android/settings/security/SecuritySettings.java
tests/robotests/res/values-mcc999/config.xml
tests/robotests/src/com/android/settings/applications/specialaccess/deviceadmin/DeviceAdminListItemTest.java [new file with mode: 0644]
tests/robotests/src/com/android/settings/applications/specialaccess/deviceadmin/DeviceAdminListPreferenceControllerTest.java [new file with mode: 0644]
tests/robotests/src/com/android/settings/applications/specialaccess/deviceadmin/DeviceAdministratorsControllerTest.java [deleted file]
tests/robotests/src/com/android/settings/enterprise/ManageDeviceAdminPreferenceControllerTest.java

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 (file)
index 4902758..0000000
+++ /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 (file)
index 875c734..0000000
+++ /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 (file)
index 1766138..0000000
+++ /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>
index fa57b29..b406605 100644 (file)
@@ -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/xml/device_admin_settings.xml b/res/xml/device_admin_settings.xml
new file mode 100644 (file)
index 0000000..999d790
--- /dev/null
@@ -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
index af15c08..8a3d108 100644 (file)
@@ -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"
             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>
 
             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"
index b7a8e5c..574686a 100644 (file)
@@ -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/specialaccess/deviceadmin/DeviceAdminListItem.java b/src/com/android/settings/applications/specialaccess/deviceadmin/DeviceAdminListItem.java
new file mode 100644 (file)
index 0000000..305281c
--- /dev/null
@@ -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 (file)
index 0000000..52d186b
--- /dev/null
@@ -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));
+            }
+        }
+    }
+}
index 6e0f60c..4cd4c04 100644 (file)
 
 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());
-    }
-
-    @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);
+    public void onAttach(Context context) {
+        super.onAttach(context);
+        use(DeviceAdminListPreferenceController.class).setFooterPreferenceMixin(
+                mFooterPreferenceMixin);
     }
 
     @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 int getPreferenceScreenResId() {
+        return R.xml.device_admin_settings;
     }
 
     @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);
-
-        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();
+    protected String getLogTag() {
+        return TAG;
     }
 
-    @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;
-        }
+    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<>();
 
-        @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 (file)
index 0000000..13d9d20
--- /dev/null
@@ -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 (file)
index 5b05b86..0000000
+++ /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
index 856722c..ceb65ca 100644 (file)
@@ -20,8 +20,6 @@ import android.app.Activity;
 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 com.android.settingslib.core.AbstractPreferenceController;
 
 import java.util.Calendar;
 
+import androidx.annotation.VisibleForTesting;
+import androidx.preference.Preference;
+
 public class DatePreferenceController extends AbstractPreferenceController
         implements PreferenceControllerMixin, DatePickerDialog.OnDateSetListener {
 
index e4f2e76..bbb217b 100644 (file)
 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;
+
+public class ManageDeviceAdminPreferenceController extends BasePreferenceController {
 
-    private static final String KEY_MANAGE_DEVICE_ADMIN = "manage_device_admin";
     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;
-    }
 }
index 7ce6560..b590618 100644 (file)
@@ -29,7 +29,6 @@ import com.android.settings.Utils;
 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 @@ public class SecuritySettings extends DashboardFragment {
             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));
index 72f5134..2d95b65 100644 (file)
@@ -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/specialaccess/deviceadmin/DeviceAdminListItemTest.java b/tests/robotests/src/com/android/settings/applications/specialaccess/deviceadmin/DeviceAdminListItemTest.java
new file mode 100644 (file)
index 0000000..e19d9e6
--- /dev/null
@@ -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 (file)
index 0000000..c99df01
--- /dev/null
@@ -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 (file)
index 6e3ff2c..0000000
+++ /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();
-    }
-}
index 885e890..e9066aa 100644 (file)
@@ -22,7 +22,6 @@ import static org.mockito.Mockito.when;
 
 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.mockito.MockitoAnnotations;
 import org.robolectric.RuntimeEnvironment;
 import org.robolectric.annotation.Config;
 
+import androidx.preference.Preference;
+
 @RunWith(SettingsRobolectricTestRunner.class)
 public class ManageDeviceAdminPreferenceControllerTest {
 
@@ -51,7 +52,7 @@ public class ManageDeviceAdminPreferenceControllerTest {
         MockitoAnnotations.initMocks(this);
         mContext = spy(RuntimeEnvironment.application);
         mFeatureFactory = FakeFeatureFactory.setupForTest();
-        mController = new ManageDeviceAdminPreferenceController(mContext);
+        mController = new ManageDeviceAdminPreferenceController(mContext, "testkey");
     }
 
     @Test
@@ -60,7 +61,7 @@ public class ManageDeviceAdminPreferenceControllerTest {
 
         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 class ManageDeviceAdminPreferenceControllerTest {
     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");
-    }
 }