diff --git a/res/values/strings.xml b/res/values/strings.xml index 8b576381d7e..077f571abbd 100644 --- a/res/values/strings.xml +++ b/res/values/strings.xml @@ -4333,9 +4333,15 @@ You need to set a lock screen PIN or password before you can use credential storage. - Usage access - - Apps that have access to your device\'s usage history. + Apps with usage access + + Apps requesting access + + Only allow apps you trust to have access to usage data. Apps you allow will have access to your app usage history, such as the last time an app was used, or the total time you spent using an app. + + Allow access? + + If you allow access, this app can view general info about your apps, such as how often you use them." Emergency tone diff --git a/res/xml/security_settings_misc.xml b/res/xml/security_settings_misc.xml index 1f280a9fb1b..71f9ffac328 100644 --- a/res/xml/security_settings_misc.xml +++ b/res/xml/security_settings_misc.xml @@ -117,7 +117,6 @@ diff --git a/res/xml/usage_access_settings.xml b/res/xml/usage_access_settings.xml index 9cea7252135..944126eb501 100644 --- a/res/xml/usage_access_settings.xml +++ b/res/xml/usage_access_settings.xml @@ -16,4 +16,16 @@ + android:title="@string/usage_access_title"> + + + + + + diff --git a/src/com/android/settings/UsageAccessSettings.java b/src/com/android/settings/UsageAccessSettings.java index 8ae277d228d..1816c504dca 100644 --- a/src/com/android/settings/UsageAccessSettings.java +++ b/src/com/android/settings/UsageAccessSettings.java @@ -20,8 +20,12 @@ import com.android.internal.content.PackageMonitor; import android.Manifest; import android.app.ActivityThread; +import android.app.AlertDialog; import android.app.AppOpsManager; +import android.app.Dialog; +import android.app.DialogFragment; import android.content.Context; +import android.content.DialogInterface; import android.content.pm.IPackageManager; import android.content.pm.PackageInfo; import android.content.pm.PackageManager; @@ -30,6 +34,7 @@ import android.os.Bundle; import android.os.Looper; import android.os.RemoteException; import android.preference.Preference; +import android.preference.PreferenceCategory; import android.preference.SwitchPreference; import android.util.ArrayMap; import android.util.Log; @@ -176,7 +181,7 @@ public class UsageAccessSettings extends SettingsPreferenceFragment implements if (newEntries == null) { mPackageEntryMap.clear(); - getPreferenceScreen().removeAll(); + mAppsCategory.removeAll(); return; } @@ -187,7 +192,7 @@ public class UsageAccessSettings extends SettingsPreferenceFragment implements final PackageEntry newPackageEntry = newEntries.get(oldPackageEntry.packageName); if (newPackageEntry == null) { // This package has been removed. - getPreferenceScreen().removePreference(oldPackageEntry.preference); + mAppsCategory.removePreference(oldPackageEntry.preference); } else { // This package already exists in the preference hierarchy, so reuse that // Preference. @@ -203,7 +208,7 @@ public class UsageAccessSettings extends SettingsPreferenceFragment implements packageEntry.preference = new SwitchPreference(mContext); packageEntry.preference.setPersistent(false); packageEntry.preference.setOnPreferenceChangeListener(UsageAccessSettings.this); - getPreferenceScreen().addPreference(packageEntry.preference); + mAppsCategory.addPreference(packageEntry.preference); } updatePreference(packageEntry); } @@ -232,20 +237,22 @@ public class UsageAccessSettings extends SettingsPreferenceFragment implements } } - private static boolean shouldIgnorePackage(String packageName) { + static boolean shouldIgnorePackage(String packageName) { return packageName.equals("android") || packageName.equals("com.android.settings"); } - private ArrayMap mPackageEntryMap = new ArrayMap<>(); - private AppOpsManager mAppOpsManager; private AppsRequestingAccessFetcher mLastFetcherTask; + ArrayMap mPackageEntryMap = new ArrayMap<>(); + AppOpsManager mAppOpsManager; + PreferenceCategory mAppsCategory; @Override public void onCreate(Bundle icicle) { super.onCreate(icicle); addPreferencesFromResource(R.xml.usage_access_settings); - getPreferenceScreen().setOrderingAsAdded(false); + mAppsCategory = (PreferenceCategory) getPreferenceScreen().findPreference("apps"); + mAppsCategory.setOrderingAsAdded(false); mAppOpsManager = (AppOpsManager) getSystemService(Context.APP_OPS_SERVICE); } @@ -302,13 +309,27 @@ public class UsageAccessSettings extends SettingsPreferenceFragment implements // Check if we need to do any work. if (pe.appOpMode != newMode) { - mAppOpsManager.setMode(AppOpsManager.OP_GET_USAGE_STATS, - pe.packageInfo.applicationInfo.uid, packageName, newMode); - pe.appOpMode = newMode; + if (newMode != AppOpsManager.MODE_ALLOWED) { + // Turning off the setting has no warning. + setNewMode(pe, newMode); + return true; + } + + // Turning on the setting has a Warning. + getFragmentManager().beginTransaction() + .add(new WarningDialog(pe), "warning") + .commit(); + return false; } return true; } + void setNewMode(PackageEntry pe, int newMode) { + mAppOpsManager.setMode(AppOpsManager.OP_GET_USAGE_STATS, + pe.packageInfo.applicationInfo.uid, pe.packageName, newMode); + pe.appOpMode = newMode; + } + private final PackageMonitor mPackageMonitor = new PackageMonitor() { @Override public void onPackageAdded(String packageName, int uid) { @@ -320,4 +341,34 @@ public class UsageAccessSettings extends SettingsPreferenceFragment implements updateInterestedApps(); } }; + + private class WarningDialog extends DialogFragment + implements DialogInterface.OnClickListener { + private final PackageEntry mEntry; + + public WarningDialog(PackageEntry pe) { + mEntry = pe; + } + + @Override + public Dialog onCreateDialog(Bundle savedInstanceState) { + return new AlertDialog.Builder(getActivity()) + .setTitle(R.string.allow_usage_access_title) + .setMessage(R.string.allow_usage_access_message) + .setIconAttribute(android.R.attr.alertDialogIcon) + .setNegativeButton(R.string.cancel, this) + .setPositiveButton(R.string.allow, this) + .create(); + } + + @Override + public void onClick(DialogInterface dialog, int which) { + if (which == DialogInterface.BUTTON_POSITIVE) { + setNewMode(mEntry, AppOpsManager.MODE_ALLOWED); + mEntry.preference.setChecked(true); + } else { + dialog.cancel(); + } + } + } }