Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Skip to content
...@@ -359,7 +359,8 @@ public class AppOpsCategory extends ListFragment implements ...@@ -359,7 +359,8 @@ public class AppOpsCategory extends ListFragment implements
SettingsActivity sa = (SettingsActivity) getActivity(); SettingsActivity sa = (SettingsActivity) getActivity();
sa.startPreferencePanel(AppOpsDetails.class.getName(), args, sa.startPreferencePanel(AppOpsDetails.class.getName(), args,
R.string.app_ops_settings, null, this, RESULT_APP_DETAILS); org.cyanogenmod.platform.internal.R.string.privacy_guard_manager_title,
null, this, RESULT_APP_DETAILS);
} }
@Override public void onListItemClick(ListView l, View v, int position, long id) { @Override public void onListItemClick(ListView l, View v, int position, long id) {
......
/** /**
* Copyright (C) 2013 The Android Open Source Project * Copyright (C) 2013 The Android Open Source Project
* Copyright (C) 2017-2018 The LineageOS Project
* *
* Licensed under the Apache License, Version 2.0 (the "License"); you may not * 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 * use this file except in compliance with the License. You may obtain a copy
...@@ -20,6 +21,7 @@ import android.app.Activity; ...@@ -20,6 +21,7 @@ import android.app.Activity;
import android.app.AppOpsManager; import android.app.AppOpsManager;
import android.content.Context; import android.content.Context;
import android.content.Intent; import android.content.Intent;
import android.content.pm.ApplicationInfo;
import android.content.pm.PackageInfo; import android.content.pm.PackageInfo;
import android.content.pm.PackageManager; import android.content.pm.PackageManager;
import android.content.pm.PackageManager.NameNotFoundException; import android.content.pm.PackageManager.NameNotFoundException;
...@@ -28,25 +30,25 @@ import android.content.pm.PermissionInfo; ...@@ -28,25 +30,25 @@ import android.content.pm.PermissionInfo;
import android.content.res.Resources; import android.content.res.Resources;
import android.graphics.drawable.Drawable; import android.graphics.drawable.Drawable;
import android.os.Bundle; import android.os.Bundle;
import android.support.v14.preference.SwitchPreference;
import android.support.v7.preference.ListPreference;
import android.support.v7.preference.Preference;
import android.support.v7.preference.Preference.OnPreferenceChangeListener;
import android.support.v7.preference.PreferenceScreen;
import android.text.TextUtils;
import android.util.Log; import android.util.Log;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.CompoundButton;
import android.widget.ImageView;
import android.widget.LinearLayout;
import android.widget.Switch;
import android.widget.TextView;
import com.android.internal.logging.MetricsProto.MetricsEvent; import com.android.internal.logging.MetricsProto.MetricsEvent;
import com.android.settings.InstrumentedFragment; import com.android.settings.AppHeader;
import com.android.settings.R; import com.android.settings.R;
import com.android.settings.SettingsActivity; import com.android.settings.SettingsActivity;
import com.android.settings.Utils; import com.android.settings.SettingsPreferenceFragment;
import java.util.HashMap;
import java.util.List; import java.util.List;
import java.util.StringJoiner;
public class AppOpsDetails extends InstrumentedFragment { public class AppOpsDetails extends SettingsPreferenceFragment {
static final String TAG = "AppOpsDetails"; static final String TAG = "AppOpsDetails";
public static final String ARG_PACKAGE_NAME = "package"; public static final String ARG_PACKAGE_NAME = "package";
...@@ -55,17 +57,94 @@ public class AppOpsDetails extends InstrumentedFragment { ...@@ -55,17 +57,94 @@ public class AppOpsDetails extends InstrumentedFragment {
private PackageManager mPm; private PackageManager mPm;
private AppOpsManager mAppOps; private AppOpsManager mAppOps;
private PackageInfo mPackageInfo; private PackageInfo mPackageInfo;
private LayoutInflater mInflater; private PreferenceScreen mPreferenceScreen;
private View mRootView;
private LinearLayout mOperationsSection; private final int MODE_ALLOWED = 0;
private final int MODE_IGNORED = 1;
private final int MODE_ASK = 2;
private final String[] MODE_ENTRIES = {
String.valueOf(MODE_ALLOWED),
String.valueOf(MODE_IGNORED),
String.valueOf(MODE_ASK)
};
private int modeToPosition (int mode) {
switch(mode) {
case AppOpsManager.MODE_ALLOWED:
return MODE_ALLOWED;
case AppOpsManager.MODE_IGNORED:
return MODE_IGNORED;
case AppOpsManager.MODE_ASK:
return MODE_ASK;
};
return MODE_IGNORED;
}
private int positionToMode (int position) {
switch(position) {
case MODE_ALLOWED:
return AppOpsManager.MODE_ALLOWED;
case MODE_IGNORED:
return AppOpsManager.MODE_IGNORED;
case MODE_ASK:
return AppOpsManager.MODE_ASK;
};
return AppOpsManager.MODE_IGNORED;
}
private static HashMap<Integer, Integer> OP_ICONS = new HashMap<>();
static {
OP_ICONS.put(AppOpsManager.OP_ACTIVATE_VPN, R.drawable.ic_perm_vpn);
OP_ICONS.put(AppOpsManager.OP_AUDIO_ALARM_VOLUME, R.drawable.ic_perm_alarm);
OP_ICONS.put(AppOpsManager.OP_AUDIO_MEDIA_VOLUME, R.drawable.ic_perm_audio);
OP_ICONS.put(AppOpsManager.OP_BLUETOOTH_CHANGE, R.drawable.ic_perm_bluetooth);
OP_ICONS.put(AppOpsManager.OP_BOOT_COMPLETED, R.drawable.ic_perm_boot);
OP_ICONS.put(AppOpsManager.OP_CHANGE_WIFI_STATE, R.drawable.ic_perm_wifi);
OP_ICONS.put(AppOpsManager.OP_DATA_CONNECT_CHANGE, R.drawable.ic_perm_data);
OP_ICONS.put(AppOpsManager.OP_GET_USAGE_STATS, R.drawable.ic_perm_data);
OP_ICONS.put(AppOpsManager.OP_GPS, R.drawable.ic_perm_location);
OP_ICONS.put(AppOpsManager.OP_MUTE_MICROPHONE, R.drawable.ic_perm_microphone);
OP_ICONS.put(AppOpsManager.OP_NFC_CHANGE, R.drawable.ic_perm_nfc);
OP_ICONS.put(AppOpsManager.OP_POST_NOTIFICATION, R.drawable.ic_perm_notifications);
OP_ICONS.put(AppOpsManager.OP_READ_CLIPBOARD, R.drawable.ic_perm_clipboard);
OP_ICONS.put(AppOpsManager.OP_RUN_IN_BACKGROUND, R.drawable.ic_perm_background);
OP_ICONS.put(AppOpsManager.OP_SU, R.drawable.ic_perm_su);
OP_ICONS.put(AppOpsManager.OP_SYSTEM_ALERT_WINDOW, R.drawable.ic_perm_drawontop);
OP_ICONS.put(AppOpsManager.OP_TAKE_AUDIO_FOCUS, R.drawable.ic_perm_audio);
OP_ICONS.put(AppOpsManager.OP_TOAST_WINDOW, R.drawable.ic_perm_notifications);
OP_ICONS.put(AppOpsManager.OP_TURN_SCREEN_ON, R.drawable.ic_perm_turnscreenon);
OP_ICONS.put(AppOpsManager.OP_VIBRATE, R.drawable.ic_perm_vibrate);
OP_ICONS.put(AppOpsManager.OP_WAKE_LOCK, R.drawable.ic_perm_nosleep);
OP_ICONS.put(AppOpsManager.OP_WIFI_SCAN, R.drawable.ic_perm_wifi);
OP_ICONS.put(AppOpsManager.OP_WRITE_CLIPBOARD, R.drawable.ic_perm_clipboard);
OP_ICONS.put(AppOpsManager.OP_WRITE_SETTINGS, R.drawable.ic_perm_settings);
OP_ICONS.put(AppOpsManager.OP_WRITE_SMS , R.drawable.ic_perm_sms);
}
private boolean isPlatformSigned() {
final int match = mPm.checkSignatures("android", mPackageInfo.packageName);
return match >= PackageManager.SIGNATURE_MATCH;
}
// Utility method to set application label and icon. // Utility method to set application label and icon.
private void setAppLabelAndIcon(PackageInfo pkgInfo) { private void setAppHeader(PackageInfo pkgInfo) {
final View appSnippet = mRootView.findViewById(R.id.app_snippet); ApplicationInfo appInfo = pkgInfo.applicationInfo;
CharSequence label = mPm.getApplicationLabel(pkgInfo.applicationInfo); String appLabel = mPm.getApplicationLabel(appInfo).toString();
Drawable icon = mPm.getApplicationIcon(pkgInfo.applicationInfo); Drawable icon = mPm.getApplicationIcon(appInfo);
InstalledAppDetails.setupAppSnippet(appSnippet, label, icon, int uid = appInfo.uid;
pkgInfo != null ? pkgInfo.versionName : null); String label;
try {
label = appInfo.loadLabel(mPm).toString();
} catch (Throwable t) {
Log.e(TAG, "Error loading application label for " + appLabel, t);
label = appLabel;
}
AppHeader.createAppHeader(this, icon, label, appInfo.packageName, uid);
} }
private String retrieveAppEntry() { private String retrieveAppEntry() {
...@@ -95,57 +174,171 @@ public class AppOpsDetails extends InstrumentedFragment { ...@@ -95,57 +174,171 @@ public class AppOpsDetails extends InstrumentedFragment {
return false; return false;
} }
setAppLabelAndIcon(mPackageInfo); mPreferenceScreen.removeAll();
setAppHeader(mPackageInfo);
Resources res = getActivity().getResources(); AppOpsState.OpsTemplate[] allTemplates = getTemplates();
for (AppOpsState.OpsTemplate tpl : allTemplates) {
mOperationsSection.removeAllViews();
String lastPermGroup = "";
for (AppOpsState.OpsTemplate tpl : AppOpsState.ALL_TEMPLATES) {
List<AppOpsState.AppOpEntry> entries = mState.buildState(tpl, List<AppOpsState.AppOpEntry> entries = mState.buildState(tpl,
mPackageInfo.applicationInfo.uid, mPackageInfo.packageName); mPackageInfo.applicationInfo.uid, mPackageInfo.packageName, true);
for (final AppOpsState.AppOpEntry entry : entries) { for (final AppOpsState.AppOpEntry entry : entries) {
final AppOpsManager.OpEntry firstOp = entry.getOpEntry(0); String perm = null;
final View view = mInflater.inflate(R.layout.app_ops_details_item, int op = -1;
mOperationsSection, false); // Find the first permission with a known name
mOperationsSection.addView(view); for (int i = 0; i < entry.getNumOpEntry() && perm == null; i++) {
String perm = AppOpsManager.opToPermission(firstOp.getOp()); op = entry.getOpEntry(i).getOp();
if (perm != null) { perm = AppOpsManager.opToPermission(op);
try { }
PermissionInfo pi = mPm.getPermissionInfo(perm, 0); Drawable icon = getIconByPermission(perm);
if (pi.group != null && !lastPermGroup.equals(pi.group)) { if (icon == null && op != -1 && OP_ICONS.containsKey(op)) {
lastPermGroup = pi.group; icon = getActivity().getDrawable(OP_ICONS.get(op));
PermissionGroupInfo pgi = mPm.getPermissionGroupInfo(pi.group, 0);
if (pgi.icon != 0) {
((ImageView)view.findViewById(R.id.op_icon)).setImageDrawable(
pgi.loadIcon(mPm));
}
}
} catch (NameNotFoundException e) {
}
} }
((TextView)view.findViewById(R.id.op_name)).setText(
entry.getSwitchText(mState)); final AppOpsManager.OpEntry firstOp = entry.getOpEntry(0);
((TextView)view.findViewById(R.id.op_time)).setText(
entry.getTimeText(res, true));
Switch sw = (Switch)view.findViewById(R.id.switchWidget);
final int switchOp = AppOpsManager.opToSwitch(firstOp.getOp()); final int switchOp = AppOpsManager.opToSwitch(firstOp.getOp());
sw.setChecked(mAppOps.checkOp(switchOp, entry.getPackageOps().getUid(),
entry.getPackageOps().getPackageName()) == AppOpsManager.MODE_ALLOWED); // ListPreference for 3 states: ask, allow, deny
sw.setOnCheckedChangeListener(new Switch.OnCheckedChangeListener() { if (AppOpsManager.isStrictOp(switchOp)) {
@Override ListPreference listPref = getListPrefForEntry(entry, icon);
public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) { mPreferenceScreen.addPreference(listPref);
mAppOps.setMode(switchOp, entry.getPackageOps().getUid(), } else {
entry.getPackageOps().getPackageName(), isChecked SwitchPreference switchPref = getSwitchPrefForEntry(entry, icon);
? AppOpsManager.MODE_ALLOWED : AppOpsManager.MODE_IGNORED); mPreferenceScreen.addPreference(switchPref);
} }
});
} }
} }
if (mPreferenceScreen.getPreferenceCount() == 0) {
Preference noBlockablePermissionsPref = getNoBlockablePermissionsPref();
mPreferenceScreen.addPreference(noBlockablePermissionsPref);
}
return true; return true;
} }
private AppOpsState.OpsTemplate[] getTemplates() {
/* If we are platform signed, only show the root switch, this
* one is safe to toggle while other permission-based ones could
* certainly cause system-wide problems
*/
if (isPlatformSigned()) {
return new AppOpsState.OpsTemplate[]{ AppOpsState.SU_TEMPLATE };
}
int length = AppOpsState.ALL_PERMS_TEMPLATES.length;
AppOpsState.OpsTemplate[] allTemplates = new AppOpsState.OpsTemplate[length];
// Loop all existing templates and set the visibility of each perm to true
for (int i = 0; i < length; i++) {
AppOpsState.OpsTemplate tpl = AppOpsState.ALL_PERMS_TEMPLATES[i];
for (int j = 0; j < tpl.ops.length; j++) {
// we only want to use the template's orderings, not the visibility
tpl.showPerms[j] = true;
}
allTemplates[i] = tpl;
}
return allTemplates;
}
private Drawable getIconByPermission(String perm) {
Drawable icon = null;
if (perm != null) {
try {
PermissionInfo pi = mPm.getPermissionInfo(perm, 0);
if (pi.group != null) {
PermissionGroupInfo pgi = mPm.getPermissionGroupInfo(pi.group, 0);
if (pgi.icon != 0) {
icon = pgi.loadIcon(mPm);
}
}
} catch (NameNotFoundException e) {
}
}
return icon;
}
private ListPreference getListPrefForEntry(final AppOpsState.AppOpEntry entry, Drawable icon) {
final Resources res = getActivity().getResources();
final AppOpsManager.OpEntry firstOp = entry.getOpEntry(0);
final AppOpsManager.PackageOps pkgOps = entry.getPackageOps();
final int uid = pkgOps.getUid();
final String pkgName = pkgOps.getPackageName();
final int switchOp = AppOpsManager.opToSwitch(firstOp.getOp());
final int mode = mAppOps.checkOpNoThrow(switchOp, uid, pkgName);
final CharSequence opName = entry.getSwitchText(mState);
ListPreference listPref = new ListPreference(getActivity());
listPref.setLayoutResource(R.layout.preference_appops);
listPref.setIcon(icon);
listPref.setTitle(opName);
listPref.setDialogTitle(opName);
listPref.setEntries(R.array.app_ops_permissions);
listPref.setEntryValues(MODE_ENTRIES);
listPref.setValueIndex(modeToPosition(mode));
String summary = getSummary(listPref.getEntry(), entry.getCountsText(res),
entry.getTimeText(res, true));
listPref.setSummary(summary);
listPref.setOnPreferenceChangeListener(new OnPreferenceChangeListener() {
@Override
public boolean onPreferenceChange(Preference preference, Object newValue) {
ListPreference listPref = (ListPreference) preference;
String value = newValue.toString();
int selectedIndex = listPref.findIndexOfValue(value);
mAppOps.setMode(switchOp, uid, pkgName, positionToMode(selectedIndex));
String summary = getSummary(listPref.getEntries()[selectedIndex],
entry.getCountsText(res), entry.getTimeText(res, true));
listPref.setSummary(summary);
return true;
}
});
return listPref;
}
private SwitchPreference getSwitchPrefForEntry(final AppOpsState.AppOpEntry entry,
Drawable icon) {
final Resources res = getActivity().getResources();
final AppOpsManager.OpEntry firstOp = entry.getOpEntry(0);
final AppOpsManager.PackageOps pkgOps = entry.getPackageOps();
final int uid = pkgOps.getUid();
final String pkgName = pkgOps.getPackageName();
final int switchOp = AppOpsManager.opToSwitch(firstOp.getOp());
final int mode = mAppOps.checkOpNoThrow(switchOp, uid, pkgName);
final CharSequence opName = entry.getSwitchText(mState);
SwitchPreference switchPref = new SwitchPreference(getActivity());
switchPref.setLayoutResource(R.layout.preference_appops);
switchPref.setIcon(icon);
switchPref.setTitle(opName);
String summary = getSummary(entry.getCountsText(res), entry.getTimeText(res, true));
switchPref.setSummary(summary);
switchPref.setChecked(mode == AppOpsManager.MODE_ALLOWED);
switchPref.setOnPreferenceChangeListener(new OnPreferenceChangeListener() {
@Override
public boolean onPreferenceChange(Preference preference,
Object newValue) {
Boolean isChecked = (Boolean) newValue;
mAppOps.setMode(switchOp, uid, pkgName,
isChecked ? AppOpsManager.MODE_ALLOWED
: AppOpsManager.MODE_IGNORED);
return true;
}
});
return switchPref;
}
private Preference getNoBlockablePermissionsPref() {
Preference emptyPref = new Preference(getActivity());
emptyPref.setTitle(R.string.app_ops_no_blockable_permissions);
emptyPref.setSelectable(false);
emptyPref.setEnabled(false);
return emptyPref;
}
private void setIntentAndFinish(boolean finish, boolean appChanged) { private void setIntentAndFinish(boolean finish, boolean appChanged) {
Intent intent = new Intent(); Intent intent = new Intent();
intent.putExtra(ManageApplications.APP_CHG, appChanged); intent.putExtra(ManageApplications.APP_CHG, appChanged);
...@@ -160,25 +353,14 @@ public class AppOpsDetails extends InstrumentedFragment { ...@@ -160,25 +353,14 @@ public class AppOpsDetails extends InstrumentedFragment {
mState = new AppOpsState(getActivity()); mState = new AppOpsState(getActivity());
mPm = getActivity().getPackageManager(); mPm = getActivity().getPackageManager();
mInflater = (LayoutInflater)getActivity().getSystemService(Context.LAYOUT_INFLATER_SERVICE);
mAppOps = (AppOpsManager)getActivity().getSystemService(Context.APP_OPS_SERVICE); mAppOps = (AppOpsManager)getActivity().getSystemService(Context.APP_OPS_SERVICE);
mPreferenceScreen = getPreferenceManager().createPreferenceScreen(getActivity());
retrieveAppEntry(); retrieveAppEntry();
setPreferenceScreen(mPreferenceScreen);
setHasOptionsMenu(true); setHasOptionsMenu(true);
} }
@Override
public View onCreateView(
LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
final View view = inflater.inflate(R.layout.app_ops_details, container, false);
Utils.prepareCustomPreferencesList(container, view, view, false);
mRootView = view;
mOperationsSection = (LinearLayout)view.findViewById(R.id.operations_section);
return view;
}
@Override @Override
protected int getMetricsCategory() { protected int getMetricsCategory() {
return MetricsEvent.APP_OPS_DETAILS; return MetricsEvent.APP_OPS_DETAILS;
...@@ -191,4 +373,14 @@ public class AppOpsDetails extends InstrumentedFragment { ...@@ -191,4 +373,14 @@ public class AppOpsDetails extends InstrumentedFragment {
setIntentAndFinish(true, true); setIntentAndFinish(true, true);
} }
} }
private String getSummary(CharSequence... lines) {
StringJoiner sj = new StringJoiner("\n");
for (CharSequence line : lines) {
if (!TextUtils.isEmpty(line)) {
sj.add(line);
}
}
return sj.toString();
}
} }
/**
* Copyright (C) 2015 The CyanogenMod 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;
import android.content.Intent;
import android.preference.PreferenceActivity;
public class AppOpsDetailsTop extends PreferenceActivity {
@Override
public Intent getIntent() {
Intent modIntent = new Intent(super.getIntent());
modIntent.putExtra(EXTRA_SHOW_FRAGMENT, AppOpsDetails.class.getName());
modIntent.putExtra(EXTRA_NO_HEADERS, true);
return modIntent;
}
@Override
protected boolean isValidFragment(String fragmentName) {
if (AppOpsDetails.class.getName().equals(fragmentName)) return true;
return false;
}
}
/** /**
* Copyright (C) 2013 The Android Open Source Project * Copyright (C) 2013 The Android Open Source Project
* Copyright (C) 2018 The LineageOS Project
* *
* Licensed under the Apache License, Version 2.0 (the "License"); you may not * 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 * use this file except in compliance with the License. You may obtain a copy
...@@ -16,6 +17,7 @@ ...@@ -16,6 +17,7 @@
package com.android.settings.applications; package com.android.settings.applications;
import android.app.Activity;
import android.app.AppOpsManager; import android.app.AppOpsManager;
import android.content.Context; import android.content.Context;
import android.content.pm.ApplicationInfo; import android.content.pm.ApplicationInfo;
...@@ -23,6 +25,7 @@ import android.content.pm.PackageInfo; ...@@ -23,6 +25,7 @@ import android.content.pm.PackageInfo;
import android.content.pm.PackageManager; import android.content.pm.PackageManager;
import android.content.pm.PackageManager.NameNotFoundException; import android.content.pm.PackageManager.NameNotFoundException;
import android.content.res.Resources; import android.content.res.Resources;
import android.content.SharedPreferences;
import android.graphics.drawable.Drawable; import android.graphics.drawable.Drawable;
import android.os.Parcel; import android.os.Parcel;
import android.os.Parcelable; import android.os.Parcelable;
...@@ -38,7 +41,9 @@ import java.util.ArrayList; ...@@ -38,7 +41,9 @@ import java.util.ArrayList;
import java.util.Collections; import java.util.Collections;
import java.util.Comparator; import java.util.Comparator;
import java.util.HashMap; import java.util.HashMap;
import java.util.HashSet;
import java.util.List; import java.util.List;
import java.util.Set;
public class AppOpsState { public class AppOpsState {
static final String TAG = "AppOpsState"; static final String TAG = "AppOpsState";
...@@ -52,12 +57,15 @@ public class AppOpsState { ...@@ -52,12 +57,15 @@ public class AppOpsState {
List<AppOpEntry> mApps; List<AppOpEntry> mApps;
private SharedPreferences mPreferences;
public AppOpsState(Context context) { public AppOpsState(Context context) {
mContext = context; mContext = context;
mAppOps = (AppOpsManager)context.getSystemService(Context.APP_OPS_SERVICE); mAppOps = (AppOpsManager)context.getSystemService(Context.APP_OPS_SERVICE);
mPm = context.getPackageManager(); mPm = context.getPackageManager();
mOpSummaries = context.getResources().getTextArray(R.array.app_ops_summaries); mOpSummaries = context.getResources().getTextArray(R.array.app_ops_summaries_cm);
mOpLabels = context.getResources().getTextArray(R.array.app_ops_labels); mOpLabels = context.getResources().getTextArray(R.array.app_ops_labels_cm);
mPreferences = context.getSharedPreferences("appops_manager", Activity.MODE_PRIVATE);
} }
public static class OpsTemplate implements Parcelable { public static class OpsTemplate implements Parcelable {
...@@ -167,7 +175,7 @@ public class AppOpsState { ...@@ -167,7 +175,7 @@ public class AppOpsState {
AppOpsManager.OP_AUDIO_ALARM_VOLUME, AppOpsManager.OP_AUDIO_ALARM_VOLUME,
AppOpsManager.OP_AUDIO_NOTIFICATION_VOLUME, AppOpsManager.OP_AUDIO_NOTIFICATION_VOLUME,
AppOpsManager.OP_AUDIO_BLUETOOTH_VOLUME, AppOpsManager.OP_AUDIO_BLUETOOTH_VOLUME,
AppOpsManager.OP_MUTE_MICROPHONE}, AppOpsManager.OP_MUTE_MICROPHONE },
new boolean[] { false, new boolean[] { false,
true, true,
true, true,
...@@ -194,7 +202,11 @@ public class AppOpsState { ...@@ -194,7 +202,11 @@ public class AppOpsState {
AppOpsManager.OP_PROJECT_MEDIA, AppOpsManager.OP_PROJECT_MEDIA,
AppOpsManager.OP_ACTIVATE_VPN, AppOpsManager.OP_ACTIVATE_VPN,
AppOpsManager.OP_ASSIST_STRUCTURE, AppOpsManager.OP_ASSIST_STRUCTURE,
AppOpsManager.OP_ASSIST_SCREENSHOT}, AppOpsManager.OP_ASSIST_SCREENSHOT,
AppOpsManager.OP_CHANGE_WIFI_STATE,
AppOpsManager.OP_BLUETOOTH_CHANGE,
AppOpsManager.OP_NFC_CHANGE,
AppOpsManager.OP_DATA_CONNECT_CHANGE },
new boolean[] { false, new boolean[] { false,
true, true,
true, true,
...@@ -204,7 +216,11 @@ public class AppOpsState { ...@@ -204,7 +216,11 @@ public class AppOpsState {
false, false,
false, false,
false, false,
false } false,
true,
true,
true,
true }
); );
public static final OpsTemplate RUN_IN_BACKGROUND_TEMPLATE = new OpsTemplate( public static final OpsTemplate RUN_IN_BACKGROUND_TEMPLATE = new OpsTemplate(
...@@ -212,9 +228,62 @@ public class AppOpsState { ...@@ -212,9 +228,62 @@ public class AppOpsState {
new boolean[] { false } new boolean[] { false }
); );
public static final OpsTemplate BOOTUP_TEMPLATE = new OpsTemplate(
new int[] { AppOpsManager.OP_BOOT_COMPLETED },
new boolean[] { true }
);
public static final OpsTemplate SU_TEMPLATE = new OpsTemplate(
new int[] { AppOpsManager.OP_SU },
new boolean[] { false }
);
// this template should contain all ops which are not part of any other template in
// ALL_TEMPLATES
public static final OpsTemplate REMAINING_TEMPLATE = new OpsTemplate(
new int[] { AppOpsManager.OP_GET_USAGE_STATS,
AppOpsManager.OP_TOAST_WINDOW,
AppOpsManager.OP_WRITE_WALLPAPER,
AppOpsManager.OP_READ_PHONE_STATE,
AppOpsManager.OP_ADD_VOICEMAIL,
AppOpsManager.OP_USE_SIP,
AppOpsManager.OP_PROCESS_OUTGOING_CALLS,
AppOpsManager.OP_USE_FINGERPRINT,
AppOpsManager.OP_BODY_SENSORS,
AppOpsManager.OP_READ_CELL_BROADCASTS,
AppOpsManager.OP_MOCK_LOCATION,
AppOpsManager.OP_READ_EXTERNAL_STORAGE,
AppOpsManager.OP_WRITE_EXTERNAL_STORAGE,
AppOpsManager.OP_TURN_SCREEN_ON,
AppOpsManager.OP_GET_ACCOUNTS },
new boolean[] { true,
true,
true,
true,
true,
true,
true,
true,
true,
true,
true,
true,
true,
true,
true }
);
public static final OpsTemplate[] ALL_TEMPLATES = new OpsTemplate[] { public static final OpsTemplate[] ALL_TEMPLATES = new OpsTemplate[] {
LOCATION_TEMPLATE, PERSONAL_TEMPLATE, MESSAGING_TEMPLATE, LOCATION_TEMPLATE, PERSONAL_TEMPLATE, MESSAGING_TEMPLATE,
MEDIA_TEMPLATE, DEVICE_TEMPLATE, RUN_IN_BACKGROUND_TEMPLATE MEDIA_TEMPLATE, DEVICE_TEMPLATE, RUN_IN_BACKGROUND_TEMPLATE,
BOOTUP_TEMPLATE, SU_TEMPLATE
};
// this template contains all permissions grouped by templates
public static final OpsTemplate[] ALL_PERMS_TEMPLATES = new OpsTemplate[] {
LOCATION_TEMPLATE, PERSONAL_TEMPLATE, MESSAGING_TEMPLATE,
MEDIA_TEMPLATE, DEVICE_TEMPLATE, RUN_IN_BACKGROUND_TEMPLATE,
BOOTUP_TEMPLATE, SU_TEMPLATE, REMAINING_TEMPLATE
}; };
/** /**
...@@ -379,30 +448,59 @@ public class AppOpsState { ...@@ -379,30 +448,59 @@ public class AppOpsState {
} }
private CharSequence getCombinedText(ArrayList<AppOpsManager.OpEntry> ops, private CharSequence getCombinedText(ArrayList<AppOpsManager.OpEntry> ops,
CharSequence[] items) { CharSequence[] items, Resources res, boolean withTerseCounts) {
if (ops.size() == 1) { StringBuilder builder = new StringBuilder();
return items[ops.get(0).getOp()]; for (int i=0; i<ops.size(); i++) {
} else { if (i > 0) {
StringBuilder builder = new StringBuilder(); builder.append(", ");
for (int i=0; i<ops.size(); i++) { }
if (i > 0) { AppOpsManager.OpEntry op = ops.get(i);
builder.append(", "); int count = op.getAllowedCount() + op.getIgnoredCount();
}
builder.append(items[ops.get(i).getOp()]); if (withTerseCounts && count > 0) {
String quantity = res.getQuantityString(R.plurals.app_ops_count,
count, count);
builder.append(res.getString(R.string.app_ops_entry_summary,
items[op.getOp()], quantity));
} else {
builder.append(items[op.getOp()]);
} }
return builder.toString();
} }
return builder.toString();
}
public CharSequence getCountsText(Resources res) {
AppOpsManager.OpEntry op = mOps.get(0);
int allowed = op.getAllowedCount();
int denied = op.getIgnoredCount();
if (allowed == 0 && denied == 0) {
return null;
}
CharSequence allowedQuantity = res.getQuantityString(R.plurals.app_ops_count,
allowed, allowed);
CharSequence deniedQuantity = res.getQuantityString(R.plurals.app_ops_count,
denied, denied);
if (denied == 0) {
return res.getString(R.string.app_ops_allowed_count, allowedQuantity);
} else if (allowed == 0) {
return res.getString(R.string.app_ops_ignored_count, deniedQuantity);
}
return res.getString(R.string.app_ops_both_count, allowedQuantity, deniedQuantity);
} }
public CharSequence getSummaryText(AppOpsState state) { public CharSequence getSummaryText(AppOpsState state) {
return getCombinedText(mOps, state.mOpSummaries); return getCombinedText(mOps, state.mOpSummaries, state.mContext.getResources(), true);
} }
public CharSequence getSwitchText(AppOpsState state) { public CharSequence getSwitchText(AppOpsState state) {
final Resources res = state.mContext.getResources();
if (mSwitchOps.size() > 0) { if (mSwitchOps.size() > 0) {
return getCombinedText(mSwitchOps, state.mOpLabels); return getCombinedText(mSwitchOps, state.mOpLabels, res, false);
} else { } else {
return getCombinedText(mOps, state.mOpLabels); return getCombinedText(mOps, state.mOpLabels, res, false);
} }
} }
...@@ -502,19 +600,34 @@ public class AppOpsState { ...@@ -502,19 +600,34 @@ public class AppOpsState {
} }
private AppEntry getAppEntry(final Context context, final HashMap<String, AppEntry> appEntries, private AppEntry getAppEntry(final Context context, final HashMap<String, AppEntry> appEntries,
final String packageName, ApplicationInfo appInfo) { final String packageName, ApplicationInfo appInfo, boolean applyFilters) {
if (appInfo == null) {
try {
appInfo = mPm.getApplicationInfo(packageName,
PackageManager.GET_DISABLED_COMPONENTS
| PackageManager.GET_UNINSTALLED_PACKAGES);
} catch (PackageManager.NameNotFoundException e) {
Log.w(TAG, "Unable to find info for package " + packageName);
return null;
}
}
if (applyFilters) {
// Hide user apps if needed
if (!shouldShowUserApps() &&
(appInfo.flags & ApplicationInfo.FLAG_SYSTEM) == 0) {
return null;
}
// Hide system apps if needed
if (!shouldShowSystemApps() &&
(appInfo.flags & ApplicationInfo.FLAG_SYSTEM) != 0) {
return null;
}
}
AppEntry appEntry = appEntries.get(packageName); AppEntry appEntry = appEntries.get(packageName);
if (appEntry == null) { if (appEntry == null) {
if (appInfo == null) {
try {
appInfo = mPm.getApplicationInfo(packageName,
PackageManager.GET_DISABLED_COMPONENTS
| PackageManager.GET_UNINSTALLED_PACKAGES);
} catch (PackageManager.NameNotFoundException e) {
Log.w(TAG, "Unable to find info for package " + packageName);
return null;
}
}
appEntry = new AppEntry(this, appInfo); appEntry = new AppEntry(this, appInfo);
appEntry.loadLabel(context); appEntry.loadLabel(context);
appEntries.put(packageName, appEntry); appEntries.put(packageName, appEntry);
...@@ -522,12 +635,35 @@ public class AppOpsState { ...@@ -522,12 +635,35 @@ public class AppOpsState {
return appEntry; return appEntry;
} }
public List<AppOpEntry> buildState(OpsTemplate tpl, int uid, String packageName) { private boolean shouldShowUserApps() {
return buildState(tpl, uid, packageName, RECENCY_COMPARATOR); return mPreferences.getBoolean("show_user_apps", true);
}
private boolean shouldShowSystemApps() {
return mPreferences.getBoolean("show_system_apps", true);
}
public List<AppOpEntry> buildState(OpsTemplate tpl, int uid, String packageName,
boolean privacyGuard) {
return buildState(tpl, uid, packageName, RECENCY_COMPARATOR, privacyGuard);
} }
public List<AppOpEntry> buildState(OpsTemplate tpl, int uid, String packageName, public List<AppOpEntry> buildState(OpsTemplate tpl, int uid, String packageName,
Comparator<AppOpEntry> comparator) { Comparator<AppOpEntry> comparator) {
return buildState(tpl, uid, packageName, comparator, false);
}
private boolean isPrivacyGuardOp(int op) {
for (int privacyGuardOp : AppOpsManager.PRIVACY_GUARD_OP_STATES) {
if (privacyGuardOp == op) {
return true;
}
}
return false;
}
public List<AppOpEntry> buildState(OpsTemplate tpl, int uid, String packageName,
Comparator<AppOpEntry> comparator, boolean privacyGuard) {
final Context context = mContext; final Context context = mContext;
final HashMap<String, AppEntry> appEntries = new HashMap<String, AppEntry>(); final HashMap<String, AppEntry> appEntries = new HashMap<String, AppEntry>();
...@@ -536,7 +672,25 @@ public class AppOpsState { ...@@ -536,7 +672,25 @@ public class AppOpsState {
final ArrayList<String> perms = new ArrayList<String>(); final ArrayList<String> perms = new ArrayList<String>();
final ArrayList<Integer> permOps = new ArrayList<Integer>(); final ArrayList<Integer> permOps = new ArrayList<Integer>();
final int[] opToOrder = new int[AppOpsManager._NUM_OP]; final int[] opToOrder = new int[AppOpsManager._NUM_OP];
final Set<Integer> privacyGuardOps = new HashSet<>();
for (int i=0; i<tpl.ops.length; i++) { for (int i=0; i<tpl.ops.length; i++) {
if (privacyGuard && isPrivacyGuardOp(tpl.ops[i])) {
// If there's a permission for this Privacy Guard OP, then
// we don't have to treat it in a special way. The application
// should have the permission declared if it uses it, so we
// will add this later when we query PackageManager
String perm = AppOpsManager.opToPermission(tpl.ops[i]);
if (perm != null) {
if (DEBUG) Log.d(TAG, "Adding " + AppOpsManager.opToName(tpl.ops[i])
+ " (" + tpl.ops[i] + ") to privacyGuardOps");
privacyGuardOps.add(tpl.ops[i]);
} else {
if (DEBUG) Log.d(TAG, "Not adding " + AppOpsManager.opToName(tpl.ops[i])
+ " (" + tpl.ops[i] + ") with perm " + perm + " to privacyGuardOps");
}
}
if (tpl.showPerms[i]) { if (tpl.showPerms[i]) {
String perm = AppOpsManager.opToPermission(tpl.ops[i]); String perm = AppOpsManager.opToPermission(tpl.ops[i]);
if (perm != null && !perms.contains(perm)) { if (perm != null && !perms.contains(perm)) {
...@@ -547,6 +701,9 @@ public class AppOpsState { ...@@ -547,6 +701,9 @@ public class AppOpsState {
} }
} }
// Whether to apply hide user / system app filters
final boolean applyFilters = (packageName == null);
List<AppOpsManager.PackageOps> pkgs; List<AppOpsManager.PackageOps> pkgs;
if (packageName != null) { if (packageName != null) {
pkgs = mAppOps.getOpsForPackage(uid, packageName, tpl.ops); pkgs = mAppOps.getOpsForPackage(uid, packageName, tpl.ops);
...@@ -557,12 +714,21 @@ public class AppOpsState { ...@@ -557,12 +714,21 @@ public class AppOpsState {
if (pkgs != null) { if (pkgs != null) {
for (int i=0; i<pkgs.size(); i++) { for (int i=0; i<pkgs.size(); i++) {
AppOpsManager.PackageOps pkgOps = pkgs.get(i); AppOpsManager.PackageOps pkgOps = pkgs.get(i);
AppEntry appEntry = getAppEntry(context, appEntries, pkgOps.getPackageName(), null); AppEntry appEntry = getAppEntry(context, appEntries, pkgOps.getPackageName(), null,
applyFilters);
if (appEntry == null) { if (appEntry == null) {
continue; continue;
} }
for (int j=0; j<pkgOps.getOps().size(); j++) { for (int j=0; j<pkgOps.getOps().size(); j++) {
AppOpsManager.OpEntry opEntry = pkgOps.getOps().get(j); AppOpsManager.OpEntry opEntry = pkgOps.getOps().get(j);
if (privacyGuard && privacyGuardOps.contains(opEntry.getOp())) {
// This OP is here because the user enabled Privacy Guard
// for this application.
if (DEBUG) Log.d(TAG, "Not adding "
+ AppOpsManager.opToName(opEntry.getOp())
+ " (" + opEntry.getOp() + ")");
continue;
}
addOp(entries, pkgOps, appEntry, opEntry, packageName == null, addOp(entries, pkgOps, appEntry, opEntry, packageName == null,
packageName == null ? 0 : opToOrder[opEntry.getOp()]); packageName == null ? 0 : opToOrder[opEntry.getOp()]);
} }
...@@ -585,7 +751,7 @@ public class AppOpsState { ...@@ -585,7 +751,7 @@ public class AppOpsState {
for (int i=0; i<apps.size(); i++) { for (int i=0; i<apps.size(); i++) {
PackageInfo appInfo = apps.get(i); PackageInfo appInfo = apps.get(i);
AppEntry appEntry = getAppEntry(context, appEntries, appInfo.packageName, AppEntry appEntry = getAppEntry(context, appEntries, appInfo.packageName,
appInfo.applicationInfo); appInfo.applicationInfo, applyFilters);
if (appEntry == null) { if (appEntry == null) {
continue; continue;
} }
...@@ -594,7 +760,7 @@ public class AppOpsState { ...@@ -594,7 +760,7 @@ public class AppOpsState {
if (appInfo.requestedPermissions != null) { if (appInfo.requestedPermissions != null) {
for (int j=0; j<appInfo.requestedPermissions.length; j++) { for (int j=0; j<appInfo.requestedPermissions.length; j++) {
if (appInfo.requestedPermissionsFlags != null) { if (appInfo.requestedPermissionsFlags != null) {
if ((appInfo.requestedPermissionsFlags[j] if (!privacyGuard && (appInfo.requestedPermissionsFlags[j]
& PackageInfo.REQUESTED_PERMISSION_GRANTED) == 0) { & PackageInfo.REQUESTED_PERMISSION_GRANTED) == 0) {
if (DEBUG) Log.d(TAG, "Pkg " + appInfo.packageName + " perm " if (DEBUG) Log.d(TAG, "Pkg " + appInfo.packageName + " perm "
+ appInfo.requestedPermissions[j] + " not granted; skipping"); + appInfo.requestedPermissions[j] + " not granted; skipping");
...@@ -619,7 +785,7 @@ public class AppOpsState { ...@@ -619,7 +785,7 @@ public class AppOpsState {
} }
AppOpsManager.OpEntry opEntry = new AppOpsManager.OpEntry( AppOpsManager.OpEntry opEntry = new AppOpsManager.OpEntry(
permOps.get(k), AppOpsManager.MODE_ALLOWED, 0, 0, 0, -1, null); permOps.get(k), AppOpsManager.MODE_ALLOWED, 0, 0, 0, -1, null, 0, 0);
dummyOps.add(opEntry); dummyOps.add(opEntry);
addOp(entries, pkgOps, appEntry, opEntry, packageName == null, addOp(entries, pkgOps, appEntry, opEntry, packageName == null,
packageName == null ? 0 : opToOrder[opEntry.getOp()]); packageName == null ? 0 : opToOrder[opEntry.getOp()]);
......
/** /**
* Copyright (C) 2013 The Android Open Source Project * Copyright (C) 2013 The Android Open Source Project
* Copyright (C) 2016 The CyanogenMod Project
* *
* Licensed under the Apache License, Version 2.0 (the "License"); you may not * 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 * use this file except in compliance with the License. You may obtain a copy
...@@ -16,65 +17,81 @@ ...@@ -16,65 +17,81 @@
package com.android.settings.applications; package com.android.settings.applications;
import android.app.Activity;
import android.app.AlertDialog;
import android.app.AppOpsManager;
import android.app.Fragment; import android.app.Fragment;
import android.app.FragmentManager; import android.app.FragmentManager;
import android.content.res.TypedArray; import android.content.Context;
import android.content.DialogInterface;
import android.content.res.Resources;
import android.content.SharedPreferences;
import android.os.Bundle; import android.os.Bundle;
import android.preference.PreferenceFrameLayout; import android.preference.PreferenceFrameLayout;
import android.support.v13.app.FragmentPagerAdapter; import android.support.v13.app.FragmentPagerAdapter;
import android.support.v4.view.PagerTabStrip; import android.support.v4.view.PagerTabStrip;
import android.support.v4.view.ViewPager; import android.support.v4.view.ViewPager;
import android.util.Pair;
import android.util.TypedValue;
import android.view.LayoutInflater; import android.view.LayoutInflater;
import android.view.Menu;
import android.view.MenuInflater;
import android.view.MenuItem;
import android.view.View; import android.view.View;
import android.view.ViewGroup; import android.view.ViewGroup;
import com.android.internal.logging.MetricsProto.MetricsEvent; import com.android.internal.logging.MetricsProto.MetricsEvent;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import com.android.settings.DevelopmentSettings;
import com.android.settings.InstrumentedFragment; import com.android.settings.InstrumentedFragment;
import com.android.settings.R; import com.android.settings.R;
public class AppOpsSummary extends InstrumentedFragment { public class AppOpsSummary extends InstrumentedFragment {
// layout inflater object used to inflate views // layout inflater object used to inflate views
private LayoutInflater mInflater; private LayoutInflater mInflater;
private ViewGroup mContentContainer; private ViewGroup mContentContainer;
private View mRootView; private View mRootView;
private ViewPager mViewPager; private ViewPager mViewPager;
CharSequence[] mPageNames; private MyPagerAdapter mAdapter;
static AppOpsState.OpsTemplate[] sPageTemplates = new AppOpsState.OpsTemplate[] {
AppOpsState.LOCATION_TEMPLATE,
AppOpsState.PERSONAL_TEMPLATE,
AppOpsState.MESSAGING_TEMPLATE,
AppOpsState.MEDIA_TEMPLATE,
AppOpsState.DEVICE_TEMPLATE
};
int mCurPos; private Activity mActivity;
private SharedPreferences mPreferences;
@Override @Override
protected int getMetricsCategory() { protected int getMetricsCategory() {
return MetricsEvent.APP_OPS_SUMMARY; return MetricsEvent.APP_OPS_SUMMARY;
} }
class MyPagerAdapter extends FragmentPagerAdapter implements ViewPager.OnPageChangeListener { static class MyPagerAdapter extends FragmentPagerAdapter
implements ViewPager.OnPageChangeListener {
private List<Pair<CharSequence, AppOpsState.OpsTemplate>> mPageData;
private int mCurPos;
public MyPagerAdapter(FragmentManager fm) { public MyPagerAdapter(FragmentManager fm,
List<Pair<CharSequence, AppOpsState.OpsTemplate>> data) {
super(fm); super(fm);
mPageData = data;
} }
@Override @Override
public Fragment getItem(int position) { public Fragment getItem(int position) {
return new AppOpsCategory(sPageTemplates[position]); return new AppOpsCategory(mPageData.get(position).second);
} }
@Override @Override
public int getCount() { public int getCount() {
return sPageTemplates.length; return mPageData.size();
} }
@Override @Override
public CharSequence getPageTitle(int position) { public CharSequence getPageTitle(int position) {
return mPageNames[position]; return mPageData.get(position).first;
} }
@Override @Override
...@@ -86,6 +103,10 @@ public class AppOpsSummary extends InstrumentedFragment { ...@@ -86,6 +103,10 @@ public class AppOpsSummary extends InstrumentedFragment {
mCurPos = position; mCurPos = position;
} }
public int getCurrentPage() {
return mCurPos;
}
@Override @Override
public void onPageScrollStateChanged(int state) { public void onPageScrollStateChanged(int state) {
if (state == ViewPager.SCROLL_STATE_IDLE) { if (state == ViewPager.SCROLL_STATE_IDLE) {
...@@ -94,6 +115,14 @@ public class AppOpsSummary extends InstrumentedFragment { ...@@ -94,6 +115,14 @@ public class AppOpsSummary extends InstrumentedFragment {
} }
} }
private void resetAdapter() {
// trigger adapter load, preserving the selected page
int curPos = mAdapter.getCurrentPage();
mViewPager.setAdapter(mAdapter);
mViewPager.setOnPageChangeListener(mAdapter);
mViewPager.setCurrentItem(curPos);
}
@Override @Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
// initialize the inflater // initialize the inflater
...@@ -104,22 +133,36 @@ public class AppOpsSummary extends InstrumentedFragment { ...@@ -104,22 +133,36 @@ public class AppOpsSummary extends InstrumentedFragment {
mContentContainer = container; mContentContainer = container;
mRootView = rootView; mRootView = rootView;
mPageNames = getResources().getTextArray(R.array.app_ops_categories); CharSequence[] pageNames = getResources().getTextArray(R.array.app_ops_categories_cm);
AppOpsState.OpsTemplate[] templates = AppOpsState.ALL_PERMS_TEMPLATES;
assert(pageNames.length == templates.length);
int specificTab = -1;
Bundle bundle = getArguments();
if (bundle != null) {
specificTab = Arrays.asList(pageNames).indexOf(bundle.getString("appops_tab", ""));
}
List<Pair<CharSequence, AppOpsState.OpsTemplate>> pageData = new ArrayList<>();
for (int i = 0; i < pageNames.length; i++) {
pageData.add(Pair.create(pageNames[i], templates[i]));
}
filterPageData(pageData, specificTab);
mViewPager = (ViewPager) rootView.findViewById(R.id.pager); mViewPager = (ViewPager) rootView.findViewById(R.id.pager);
MyPagerAdapter adapter = new MyPagerAdapter(getChildFragmentManager()); mAdapter = new MyPagerAdapter(getChildFragmentManager(), pageData);
mViewPager.setAdapter(adapter); mViewPager.setAdapter(mAdapter);
mViewPager.setOnPageChangeListener(adapter); mViewPager.setOnPageChangeListener(mAdapter);
PagerTabStrip tabs = (PagerTabStrip) rootView.findViewById(R.id.tabs); PagerTabStrip tabs = (PagerTabStrip) rootView.findViewById(R.id.tabs);
// This should be set in the XML layout, but PagerTabStrip lives in // HACK - https://code.google.com/p/android/issues/detail?id=213359
// support-v4 and doesn't have styleable attributes. ((ViewPager.LayoutParams)tabs.getLayoutParams()).isDecor = true;
final TypedArray ta = tabs.getContext().obtainStyledAttributes(
new int[] { android.R.attr.colorAccent });
final int colorAccent = ta.getColor(0, 0);
ta.recycle();
tabs.setTabIndicatorColorResource(colorAccent); Resources.Theme theme = tabs.getContext().getTheme();
TypedValue typedValue = new TypedValue();
theme.resolveAttribute(android.R.attr.colorAccent, typedValue, true);
final int colorAccent = getContext().getColor(typedValue.resourceId);
tabs.setTabIndicatorColor(colorAccent);
// We have to do this now because PreferenceFrameLayout looks at it // We have to do this now because PreferenceFrameLayout looks at it
// only when the view is added. // only when the view is added.
...@@ -127,6 +170,103 @@ public class AppOpsSummary extends InstrumentedFragment { ...@@ -127,6 +170,103 @@ public class AppOpsSummary extends InstrumentedFragment {
((PreferenceFrameLayout.LayoutParams) rootView.getLayoutParams()).removeBorders = true; ((PreferenceFrameLayout.LayoutParams) rootView.getLayoutParams()).removeBorders = true;
} }
mActivity = getActivity();
return rootView; return rootView;
} }
private void filterPageData(List<Pair<CharSequence, AppOpsState.OpsTemplate>> data, int tab) {
if (tab >= 0 && tab < data.size()) {
Pair<CharSequence, AppOpsState.OpsTemplate> item = data.get(tab);
data.clear();
data.add(item);
} else if (!DevelopmentSettings.isRootForAppsEnabled()) {
for (Pair<CharSequence, AppOpsState.OpsTemplate> item : data) {
if (item.second == AppOpsState.SU_TEMPLATE) {
data.remove(item);
return;
}
}
}
}
private boolean shouldShowUserApps() {
return mPreferences.getBoolean("show_user_apps", true);
}
private boolean shouldShowSystemApps() {
return mPreferences.getBoolean("show_system_apps", true);
}
@Override
public void onActivityCreated(Bundle savedInstanceState) {
super.onActivityCreated(savedInstanceState);
// get shared preferences
mPreferences = mActivity.getSharedPreferences("appops_manager", Activity.MODE_PRIVATE);
setHasOptionsMenu(true);
}
@Override
public void onCreateOptionsMenu(Menu menu, MenuInflater inflater) {
super.onCreateOptionsMenu(menu, inflater);
inflater.inflate(R.menu.appops_manager, menu);
menu.findItem(R.id.show_user_apps).setChecked(shouldShowUserApps());
menu.findItem(R.id.show_system_apps).setChecked(shouldShowSystemApps());
}
private void resetCounters() {
final AppOpsManager appOps =
(AppOpsManager) mActivity.getSystemService(Context.APP_OPS_SERVICE);
if (appOps == null) {
return;
}
appOps.resetCounters();
// reload content
resetAdapter();
}
private void resetCountersConfirm() {
new AlertDialog.Builder(getActivity())
.setIcon(android.R.drawable.ic_dialog_alert)
.setTitle(R.string.app_ops_reset_confirm_title)
.setMessage(R.string.app_ops_reset_confirm_mesg)
.setPositiveButton(android.R.string.ok, new DialogInterface.OnClickListener()
{
@Override
public void onClick(DialogInterface dialog, int which) {
resetCounters();
}
})
.setNegativeButton(android.R.string.cancel, null)
.show();
}
@Override
public boolean onOptionsItemSelected(MenuItem item) {
switch (item.getItemId()) {
case R.id.show_user_apps:
final String prefNameUserApps = "show_user_apps";
// set the menu checkbox and save it in shared preference
item.setChecked(!item.isChecked());
mPreferences.edit().putBoolean(prefNameUserApps, item.isChecked()).commit();
// reload content
resetAdapter();
return true;
case R.id.show_system_apps:
final String prefNameSysApps = "show_system_apps";
// set the menu checkbox and save it in shared preference
item.setChecked(!item.isChecked());
mPreferences.edit().putBoolean(prefNameSysApps, item.isChecked()).commit();
// reload view content
resetAdapter();
return true;
case R.id.reset_counters:
resetCountersConfirm();
return true;
default:
return super.onContextItemSelected(item);
}
}
} }
...@@ -187,7 +187,11 @@ public abstract class AppStateAppOpsBridge extends AppStateBaseBridge { ...@@ -187,7 +187,11 @@ public abstract class AppStateAppOpsBridge extends AppStateBaseBridge {
* PermissionState, which describes a particular package. * PermissionState, which describes a particular package.
*/ */
private void loadPermissionsStates(SparseArray<ArrayMap<String, PermissionState>> entries) { private void loadPermissionsStates(SparseArray<ArrayMap<String, PermissionState>> entries) {
// Load the packages that have been granted the permission specified in mPermission. // Load the packages that have been granted the permission specified in mPermission.
if (entries == null) {
return;
}
try { try {
for (final UserHandle profile : mProfiles) { for (final UserHandle profile : mProfiles) {
final int profileId = profile.getIdentifier(); final int profileId = profile.getIdentifier();
......
...@@ -570,7 +570,9 @@ public class AppStorageSettings extends AppInfoWithHeader ...@@ -570,7 +570,9 @@ public class AppStorageSettings extends AppInfoWithHeader
@Override @Override
public void onPackageSizeChanged(String packageName) { public void onPackageSizeChanged(String packageName) {
if (packageName.equals(mAppEntry.info.packageName)) { if (mAppEntry == null || mAppEntry.info == null) {
return;
} else if (packageName.equals(mAppEntry.info.packageName)) {
refreshSizeInfo(); refreshSizeInfo();
} }
} }
......