+++ /dev/null
-<?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>
+++ /dev/null
-<?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>
+++ /dev/null
-<?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>
<!-- 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>
--- /dev/null
+<?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
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>
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"
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"
--- /dev/null
+/*
+ * 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());
+ }
+}
--- /dev/null
+/*
+ * 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));
+ }
+ }
+ }
+}
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);
- }
+ };
}
--- /dev/null
+/*
+ * 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);
+ }
+}
+++ /dev/null
-/*
- * 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
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;
import java.util.Calendar;
+import androidx.annotation.VisibleForTesting;
+import androidx.preference.Preference;
+
public class DatePreferenceController extends AbstractPreferenceController
implements PreferenceControllerMixin, DatePickerDialog.OnDateSetListener {
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;
- }
}
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;
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));
<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>
--- /dev/null
+/*
+ * 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);
+ }
+
+}
--- /dev/null
+/*
+ * 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));
+ }
+}
+++ /dev/null
-/*
- * 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();
- }
-}
import android.content.Context;
import android.content.res.Resources;
-import androidx.preference.Preference;
import com.android.settings.R;
import com.android.settings.testutils.FakeFeatureFactory;
import org.robolectric.RuntimeEnvironment;
import org.robolectric.annotation.Config;
+import androidx.preference.Preference;
+
@RunWith(SettingsRobolectricTestRunner.class)
public class ManageDeviceAdminPreferenceControllerTest {
MockitoAnnotations.initMocks(this);
mContext = spy(RuntimeEnvironment.application);
mFeatureFactory = FakeFeatureFactory.setupForTest();
- mController = new ManageDeviceAdminPreferenceController(mContext);
+ mController = new ManageDeviceAdminPreferenceController(mContext, "testkey");
}
@Test
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);
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");
- }
}