diff options
author | Dianne Hackborn <hackbod@google.com> | 2013-01-18 10:52:38 -0800 |
---|---|---|
committer | Dianne Hackborn <hackbod@google.com> | 2013-01-18 18:45:47 -0800 |
commit | 18b64f446cd7b9043909b0cd42d1ab364392da24 (patch) | |
tree | 7e72e12ce1e4d663a6d37282fd26fd8863f8ae7f /src | |
parent | e533c3191deeacfdc62f24527b236535807edf89 (diff) | |
download | packages_apps_Settings-18b64f446cd7b9043909b0cd42d1ab364392da24.zip packages_apps_Settings-18b64f446cd7b9043909b0cd42d1ab364392da24.tar.gz packages_apps_Settings-18b64f446cd7b9043909b0cd42d1ab364392da24.tar.bz2 |
Improve app ops UI.
Merge app entries together some.
Also a little weekend present for swetland.
Change-Id: Ie2b6654d9de0b9eeaafbf4b070845353c130b9c1
Diffstat (limited to 'src')
6 files changed, 573 insertions, 261 deletions
diff --git a/src/com/android/settings/applications/AppOpsCategory.java b/src/com/android/settings/applications/AppOpsCategory.java index 940ba0b..1eafbbf 100644 --- a/src/com/android/settings/applications/AppOpsCategory.java +++ b/src/com/android/settings/applications/AppOpsCategory.java @@ -1,9 +1,23 @@ +/** + * Copyright (C) 2013 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; -import android.app.AppOpsManager; import android.app.ListFragment; import android.app.LoaderManager; -import android.app.AppOpsManager.OpEntry; import android.content.AsyncTaskLoader; import android.content.BroadcastReceiver; import android.content.Context; @@ -11,14 +25,9 @@ import android.content.Intent; import android.content.IntentFilter; import android.content.Loader; import android.content.pm.ActivityInfo; -import android.content.pm.ApplicationInfo; -import android.content.pm.PackageInfo; -import android.content.pm.PackageManager; import android.content.res.Configuration; import android.content.res.Resources; -import android.graphics.drawable.Drawable; import android.os.Bundle; -import android.text.format.DateUtils; import android.util.Log; import android.view.LayoutInflater; import android.view.View; @@ -28,155 +37,27 @@ import android.widget.ImageView; import android.widget.ListView; import android.widget.TextView; -import java.io.File; -import java.text.Collator; -import java.util.ArrayList; -import java.util.Collections; -import java.util.Comparator; -import java.util.HashMap; import java.util.List; import com.android.settings.R; +import com.android.settings.applications.AppOpsState.AppOpEntry; public class AppOpsCategory extends ListFragment implements - LoaderManager.LoaderCallbacks<List<AppOpsCategory.AppOpEntry>> { + LoaderManager.LoaderCallbacks<List<AppOpEntry>> { // This is the Adapter being used to display the list's data. AppListAdapter mAdapter; - /** - * This class holds the per-item data in our Loader. - */ - public static class AppEntry { - private final AppListLoader mLoader; - private final ApplicationInfo mInfo; - private final File mApkFile; - private String mLabel; - private Drawable mIcon; - private boolean mMounted; - - public AppEntry(AppListLoader loader, ApplicationInfo info) { - mLoader = loader; - mInfo = info; - mApkFile = new File(info.sourceDir); - } - - public ApplicationInfo getApplicationInfo() { - return mInfo; - } - - public String getLabel() { - return mLabel; - } - - public Drawable getIcon() { - if (mIcon == null) { - if (mApkFile.exists()) { - mIcon = mInfo.loadIcon(mLoader.mPm); - return mIcon; - } else { - mMounted = false; - } - } else if (!mMounted) { - // If the app wasn't mounted but is now mounted, reload - // its icon. - if (mApkFile.exists()) { - mMounted = true; - mIcon = mInfo.loadIcon(mLoader.mPm); - return mIcon; - } - } else { - return mIcon; - } - - return mLoader.getContext().getResources().getDrawable( - android.R.drawable.sym_def_app_icon); - } - - @Override public String toString() { - return mLabel; - } - - void loadLabel(Context context) { - if (mLabel == null || !mMounted) { - if (!mApkFile.exists()) { - mMounted = false; - mLabel = mInfo.packageName; - } else { - mMounted = true; - CharSequence label = mInfo.loadLabel(context.getPackageManager()); - mLabel = label != null ? label.toString() : mInfo.packageName; - } - } - } - } - public AppOpsCategory() { } - public AppOpsCategory(int[] ops, String[] perms) { + public AppOpsCategory(AppOpsState.OpsTemplate template) { Bundle args = new Bundle(); - args.putIntArray("ops", ops); - args.putStringArray("perms", perms); + args.putParcelable("template", template); setArguments(args); } /** - * This class holds the per-item data in our Loader. - */ - public static class AppOpEntry { - private final AppOpsManager.PackageOps mPkgOps; - private final AppOpsManager.OpEntry mOp; - private final AppEntry mApp; - - public AppOpEntry(AppOpsManager.PackageOps pkg, AppOpsManager.OpEntry op, AppEntry app) { - mPkgOps = pkg; - mOp = op; - mApp = app; - } - - public AppEntry getAppEntry() { - return mApp; - } - - public AppOpsManager.PackageOps getPackageOps() { - return mPkgOps; - } - - public AppOpsManager.OpEntry getOpEntry() { - return mOp; - } - - public long getTime() { - return mOp.getTime(); - } - - @Override public String toString() { - return mApp.getLabel(); - } - } - - /** - * Perform alphabetical comparison of application entry objects. - */ - public static final Comparator<AppOpEntry> APP_OP_COMPARATOR = new Comparator<AppOpEntry>() { - private final Collator sCollator = Collator.getInstance(); - @Override - public int compare(AppOpEntry object1, AppOpEntry object2) { - if (object1.getOpEntry().isRunning() != object2.getOpEntry().isRunning()) { - // Currently running ops go first. - return object1.getOpEntry().isRunning() ? -1 : 1; - } - if (object1.getTime() != object2.getTime()) { - // More recent times go first. - return object1.getTime() > object2.getTime() ? -1 : 1; - } - return sCollator.compare(object1.getAppEntry().getLabel(), - object2.getAppEntry().getLabel()); - } - }; - - /** * Helper for determining if the configuration has changed in an interesting * way so we need to rebuild the app list. */ @@ -228,76 +109,20 @@ public class AppOpsCategory extends ListFragment implements */ public static class AppListLoader extends AsyncTaskLoader<List<AppOpEntry>> { final InterestingConfigChanges mLastConfig = new InterestingConfigChanges(); - final AppOpsManager mAppOps; - final PackageManager mPm; - final int[] mOps; - final String[] mPerms; - - final HashMap<String, AppEntry> mAppEntries = new HashMap<String, AppEntry>(); + final AppOpsState mState; + final AppOpsState.OpsTemplate mTemplate; List<AppOpEntry> mApps; PackageIntentReceiver mPackageObserver; - public AppListLoader(Context context, int[] ops, String[] perms) { + public AppListLoader(Context context, AppOpsState.OpsTemplate template) { super(context); - mAppOps = (AppOpsManager)context.getSystemService(Context.APP_OPS_SERVICE); - mPm = context.getPackageManager(); - mOps = ops; - mPerms = perms; + mState = new AppOpsState(context); + mTemplate = template; } @Override public List<AppOpEntry> loadInBackground() { - final Context context = getContext(); - - List<AppOpsManager.PackageOps> pkgs = mAppOps.getPackagesForOps(mOps); - List<AppOpEntry> entries = new ArrayList<AppOpEntry>(pkgs.size()); - for (int i=0; i<pkgs.size(); i++) { - AppOpsManager.PackageOps pkgOps = pkgs.get(i); - AppEntry appEntry = mAppEntries.get(pkgOps.getPackageName()); - if (appEntry == null) { - ApplicationInfo appInfo = null; - try { - appInfo = mPm.getApplicationInfo(pkgOps.getPackageName(), - PackageManager.GET_DISABLED_COMPONENTS - | PackageManager.GET_UNINSTALLED_PACKAGES); - } catch (PackageManager.NameNotFoundException e) { - } - appEntry = new AppEntry(this, appInfo); - appEntry.loadLabel(context); - mAppEntries.put(pkgOps.getPackageName(), appEntry); - } - for (int j=0; j<pkgOps.getOps().size(); j++) { - AppOpsManager.OpEntry opEntry = pkgOps.getOps().get(j); - AppOpEntry entry = new AppOpEntry(pkgOps, opEntry, appEntry); - entries.add(entry); - } - } - - if (mPerms != null) { - List<PackageInfo> apps = mPm.getPackagesHoldingPermissions(mPerms, 0); - for (int i=0; i<apps.size(); i++) { - PackageInfo appInfo = apps.get(i); - AppEntry appEntry = mAppEntries.get(appInfo.packageName); - if (appEntry == null) { - appEntry = new AppEntry(this, appInfo.applicationInfo); - appEntry.loadLabel(context); - mAppEntries.put(appInfo.packageName, appEntry); - List<AppOpsManager.OpEntry> dummyOps = new ArrayList<AppOpsManager.OpEntry>(); - AppOpsManager.OpEntry opEntry = new AppOpsManager.OpEntry(0, 0, 0); - dummyOps.add(opEntry); - AppOpsManager.PackageOps pkgOps = new AppOpsManager.PackageOps( - appInfo.packageName, appInfo.applicationInfo.uid, dummyOps); - AppOpEntry entry = new AppOpEntry(pkgOps, opEntry, appEntry); - entries.add(entry); - } - } - } - - // Sort the list. - Collections.sort(entries, APP_OP_COMPARATOR); - - // Done! - return entries; + return mState.buildState(mTemplate); } /** @@ -409,15 +234,15 @@ public class AppOpsCategory extends ListFragment implements } public static class AppListAdapter extends ArrayAdapter<AppOpEntry> { + private final Resources mResources; private final LayoutInflater mInflater; private final CharSequence[] mOpNames; - private final CharSequence mRunningStr; public AppListAdapter(Context context) { super(context, android.R.layout.simple_list_item_2); + mResources = context.getResources(); mInflater = (LayoutInflater)context.getSystemService(Context.LAYOUT_INFLATER_SERVICE); - mOpNames = context.getResources().getTextArray(R.array.app_ops_names); - mRunningStr = context.getResources().getText(R.string.app_ops_running); + mOpNames = mResources.getTextArray(R.array.app_ops_names); } public void setData(List<AppOpEntry> data) { @@ -427,16 +252,6 @@ public class AppOpsCategory extends ListFragment implements } } - CharSequence opTimeToString(AppOpsManager.OpEntry op) { - if (op.isRunning()) { - return mRunningStr; - } - return DateUtils.getRelativeTimeSpanString(op.getTime(), - System.currentTimeMillis(), - DateUtils.MINUTE_IN_MILLIS, - DateUtils.FORMAT_ABBREV_RELATIVE); - } - /** * Populate new items in the list. */ @@ -453,14 +268,8 @@ public class AppOpsCategory extends ListFragment implements ((ImageView)view.findViewById(R.id.app_icon)).setImageDrawable( item.getAppEntry().getIcon()); ((TextView)view.findViewById(R.id.app_name)).setText(item.getAppEntry().getLabel()); - if (item.getOpEntry().getTime() != 0) { - ((TextView)view.findViewById(R.id.op_name)).setText( - mOpNames[item.getOpEntry().getOp()]); - ((TextView)view.findViewById(R.id.op_time)).setText(opTimeToString(item.getOpEntry())); - } else { - ((TextView)view.findViewById(R.id.op_name)).setText(""); - ((TextView)view.findViewById(R.id.op_time)).setText(""); - } + ((TextView)view.findViewById(R.id.op_name)).setText(item.getLabelText(mOpNames)); + ((TextView)view.findViewById(R.id.op_time)).setText(item.getTimeText(mResources)); return view; } @@ -495,13 +304,11 @@ public class AppOpsCategory extends ListFragment implements @Override public Loader<List<AppOpEntry>> onCreateLoader(int id, Bundle args) { Bundle fargs = getArguments(); - int[] ops = null; - String[] perms = null; + AppOpsState.OpsTemplate template = null; if (fargs != null) { - ops = fargs.getIntArray("ops"); - perms = fargs.getStringArray("perms"); + template = (AppOpsState.OpsTemplate)fargs.getParcelable("template"); } - return new AppListLoader(getActivity(), ops, perms); + return new AppListLoader(getActivity(), template); } @Override public void onLoadFinished(Loader<List<AppOpEntry>> loader, List<AppOpEntry> data) { diff --git a/src/com/android/settings/applications/AppOpsDetails.java b/src/com/android/settings/applications/AppOpsDetails.java new file mode 100644 index 0000000..2a071f2 --- /dev/null +++ b/src/com/android/settings/applications/AppOpsDetails.java @@ -0,0 +1,23 @@ +/** + * Copyright (C) 2013 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; + +import android.app.Fragment; + +public class AppOpsDetails extends Fragment { + +} diff --git a/src/com/android/settings/applications/AppOpsState.java b/src/com/android/settings/applications/AppOpsState.java new file mode 100644 index 0000000..2bd724b --- /dev/null +++ b/src/com/android/settings/applications/AppOpsState.java @@ -0,0 +1,404 @@ +/** + * Copyright (C) 2013 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; + +import android.app.AppOpsManager; +import android.content.Context; +import android.content.pm.ApplicationInfo; +import android.content.pm.PackageInfo; +import android.content.pm.PackageManager; +import android.content.pm.PackageManager.NameNotFoundException; +import android.content.res.Resources; +import android.graphics.drawable.Drawable; +import android.os.Parcel; +import android.os.Parcelable; +import android.text.format.DateUtils; + +import com.android.settings.R; + +import java.io.File; +import java.text.Collator; +import java.util.ArrayList; +import java.util.Collections; +import java.util.Comparator; +import java.util.HashMap; +import java.util.List; + +public class AppOpsState { + final Context mContext; + final AppOpsManager mAppOps; + final PackageManager mPm; + + List<AppOpEntry> mApps; + + public AppOpsState(Context context) { + mContext = context; + mAppOps = (AppOpsManager)context.getSystemService(Context.APP_OPS_SERVICE); + mPm = context.getPackageManager(); + } + + public static class OpsTemplate implements Parcelable { + public final int[] ops; + public final String[] perms; + public final int[] permOps; + + public OpsTemplate(int[] _ops, String[] _perms, int[] _permOps) { + ops = _ops; + perms = _perms; + permOps = _permOps; + } + + OpsTemplate(Parcel src) { + ops = src.createIntArray(); + perms = src.createStringArray(); + permOps = src.createIntArray(); + } + + @Override + public int describeContents() { + return 0; + } + + @Override + public void writeToParcel(Parcel dest, int flags) { + dest.writeIntArray(ops); + dest.writeStringArray(perms); + dest.writeIntArray(permOps); + } + + public static final Creator<OpsTemplate> CREATOR = new Creator<OpsTemplate>() { + @Override public OpsTemplate createFromParcel(Parcel source) { + return new OpsTemplate(source); + } + + @Override public OpsTemplate[] newArray(int size) { + return new OpsTemplate[size]; + } + }; + } + + public static final OpsTemplate LOCATION_TEMPLATE = new OpsTemplate( + new int[] { AppOpsManager.OP_COARSE_LOCATION, + AppOpsManager.OP_FINE_LOCATION, + AppOpsManager.OP_GPS }, + new String[] { android.Manifest.permission.ACCESS_COARSE_LOCATION, + android.Manifest.permission.ACCESS_FINE_LOCATION }, + new int[] { AppOpsManager.OP_COARSE_LOCATION, + AppOpsManager.OP_FINE_LOCATION } + ); + + public static final OpsTemplate PERSONAL_TEMPLATE = new OpsTemplate( + new int[] { AppOpsManager.OP_READ_CONTACTS, + AppOpsManager.OP_WRITE_CONTACTS, + AppOpsManager.OP_READ_CALL_LOG, + AppOpsManager.OP_WRITE_CALL_LOG }, + new String[] { android.Manifest.permission.READ_CONTACTS, + android.Manifest.permission.WRITE_CONTACTS, + android.Manifest.permission.READ_CALL_LOG, + android.Manifest.permission.WRITE_CALL_LOG }, + new int[] { AppOpsManager.OP_READ_CONTACTS, + AppOpsManager.OP_WRITE_CONTACTS, + AppOpsManager.OP_READ_CALL_LOG, + AppOpsManager.OP_WRITE_CALL_LOG } + ); + + public static final OpsTemplate DEVICE_TEMPLATE = new OpsTemplate( + new int[] { AppOpsManager.OP_VIBRATE }, + new String[] { android.Manifest.permission.VIBRATE }, + new int[] { AppOpsManager.OP_VIBRATE } + ); + + /** + * This class holds the per-item data in our Loader. + */ + public static class AppEntry { + private final AppOpsState mState; + private final ApplicationInfo mInfo; + private final File mApkFile; + private String mLabel; + private Drawable mIcon; + private boolean mMounted; + + public AppEntry(AppOpsState state, ApplicationInfo info) { + mState = state; + mInfo = info; + mApkFile = new File(info.sourceDir); + } + + public ApplicationInfo getApplicationInfo() { + return mInfo; + } + + public String getLabel() { + return mLabel; + } + + public Drawable getIcon() { + if (mIcon == null) { + if (mApkFile.exists()) { + mIcon = mInfo.loadIcon(mState.mPm); + return mIcon; + } else { + mMounted = false; + } + } else if (!mMounted) { + // If the app wasn't mounted but is now mounted, reload + // its icon. + if (mApkFile.exists()) { + mMounted = true; + mIcon = mInfo.loadIcon(mState.mPm); + return mIcon; + } + } else { + return mIcon; + } + + return mState.mContext.getResources().getDrawable( + android.R.drawable.sym_def_app_icon); + } + + @Override public String toString() { + return mLabel; + } + + void loadLabel(Context context) { + if (mLabel == null || !mMounted) { + if (!mApkFile.exists()) { + mMounted = false; + mLabel = mInfo.packageName; + } else { + mMounted = true; + CharSequence label = mInfo.loadLabel(context.getPackageManager()); + mLabel = label != null ? label.toString() : mInfo.packageName; + } + } + } + } + + /** + * This class holds the per-item data in our Loader. + */ + public static class AppOpEntry { + private final AppOpsManager.PackageOps mPkgOps; + private final ArrayList<AppOpsManager.OpEntry> mOps + = new ArrayList<AppOpsManager.OpEntry>(); + private final AppEntry mApp; + + public AppOpEntry(AppOpsManager.PackageOps pkg, AppOpsManager.OpEntry op, AppEntry app) { + mPkgOps = pkg; + mOps.add(op); + mApp = app; + } + + public void addOp(AppOpsManager.OpEntry op) { + for (int i=0; i<mOps.size(); i++) { + AppOpsManager.OpEntry pos = mOps.get(i); + if (pos.isRunning() != op.isRunning()) { + if (op.isRunning()) { + mOps.add(i, op); + return; + } + } + if (pos.getTime() > op.getTime()) { + mOps.add(i, op); + return; + } + } + mOps.add(op); + } + + public AppEntry getAppEntry() { + return mApp; + } + + public AppOpsManager.PackageOps getPackageOps() { + return mPkgOps; + } + + public int getNumOpEntry() { + return mOps.size(); + } + + public AppOpsManager.OpEntry getOpEntry(int pos) { + return mOps.get(pos); + } + + public CharSequence getLabelText(CharSequence opNames[]) { + if (getNumOpEntry() == 1) { + return opNames[getOpEntry(0).getOp()]; + } else { + StringBuilder builder = new StringBuilder(); + for (int i=0; i<getNumOpEntry(); i++) { + if (i > 0) { + builder.append(", "); + } + builder.append(opNames[getOpEntry(i).getOp()]); + } + return builder.toString(); + } + } + + public CharSequence getTimeText(Resources res) { + if (isRunning()) { + return res.getText(R.string.app_ops_running); + } + if (getTime() > 0) { + return DateUtils.getRelativeTimeSpanString(getTime(), + System.currentTimeMillis(), + DateUtils.MINUTE_IN_MILLIS, + DateUtils.FORMAT_ABBREV_RELATIVE); + } + return ""; + } + + public boolean isRunning() { + return mOps.get(0).isRunning(); + } + + public long getTime() { + return mOps.get(0).getTime(); + } + + @Override public String toString() { + return mApp.getLabel(); + } + } + + /** + * Perform alphabetical comparison of application entry objects. + */ + public static final Comparator<AppOpEntry> APP_OP_COMPARATOR = new Comparator<AppOpEntry>() { + private final Collator sCollator = Collator.getInstance(); + @Override + public int compare(AppOpEntry object1, AppOpEntry object2) { + if (object1.isRunning() != object2.isRunning()) { + // Currently running ops go first. + return object1.isRunning() ? -1 : 1; + } + if (object1.getTime() != object2.getTime()) { + // More recent times go first. + return object1.getTime() > object2.getTime() ? -1 : 1; + } + return sCollator.compare(object1.getAppEntry().getLabel(), + object2.getAppEntry().getLabel()); + } + }; + + private void addOp(List<AppOpEntry> entries, AppOpsManager.PackageOps pkgOps, + AppEntry appEntry, AppOpsManager.OpEntry opEntry) { + if (entries.size() > 0) { + AppOpEntry last = entries.get(entries.size()-1); + if (last.getAppEntry() == appEntry) { + boolean lastExe = last.getTime() != 0; + boolean entryExe = opEntry.getTime() != 0; + if (lastExe == entryExe) { + last.addOp(opEntry); + return; + } + } + } + AppOpEntry entry = new AppOpEntry(pkgOps, opEntry, appEntry); + entries.add(entry); + } + + public List<AppOpEntry> buildState(OpsTemplate tpl) { + return buildState(tpl, 0, null); + } + + public List<AppOpEntry> buildState(OpsTemplate tpl, int uid, String packageName) { + final Context context = mContext; + + final HashMap<String, AppEntry> appEntries = new HashMap<String, AppEntry>(); + + List<AppOpsManager.PackageOps> pkgs; + if (packageName != null) { + pkgs = mAppOps.getOpsForPackage(uid, packageName, tpl.ops); + } else { + pkgs = mAppOps.getPackagesForOps(tpl.ops); + } + List<AppOpEntry> entries = new ArrayList<AppOpEntry>(pkgs.size()); + for (int i=0; i<pkgs.size(); i++) { + AppOpsManager.PackageOps pkgOps = pkgs.get(i); + AppEntry appEntry = appEntries.get(pkgOps.getPackageName()); + if (appEntry == null) { + ApplicationInfo appInfo = null; + try { + appInfo = mPm.getApplicationInfo(pkgOps.getPackageName(), + PackageManager.GET_DISABLED_COMPONENTS + | PackageManager.GET_UNINSTALLED_PACKAGES); + } catch (PackageManager.NameNotFoundException e) { + } + appEntry = new AppEntry(this, appInfo); + appEntry.loadLabel(context); + appEntries.put(pkgOps.getPackageName(), appEntry); + } + for (int j=0; j<pkgOps.getOps().size(); j++) { + AppOpsManager.OpEntry opEntry = pkgOps.getOps().get(j); + addOp(entries, pkgOps, appEntry, opEntry); + } + } + + if (tpl.perms != null) { + List<PackageInfo> apps; + if (packageName != null) { + apps = new ArrayList<PackageInfo>(); + try { + PackageInfo pi = mPm.getPackageInfo(packageName, PackageManager.GET_PERMISSIONS); + apps.add(pi); + } catch (NameNotFoundException e) { + } + } else { + apps = mPm.getPackagesHoldingPermissions(tpl.perms, 0); + } + for (int i=0; i<apps.size(); i++) { + PackageInfo appInfo = apps.get(i); + AppEntry appEntry = appEntries.get(appInfo.packageName); + if (appEntry == null) { + appEntry = new AppEntry(this, appInfo.applicationInfo); + appEntry.loadLabel(context); + appEntries.put(appInfo.packageName, appEntry); + List<AppOpsManager.OpEntry> dummyOps + = new ArrayList<AppOpsManager.OpEntry>(); + AppOpsManager.PackageOps pkgOps = new AppOpsManager.PackageOps( + appInfo.packageName, appInfo.applicationInfo.uid, dummyOps); + for (int j=0; j<appInfo.requestedPermissions.length; j++) { + if (appInfo.requestedPermissionsFlags != null) { + if ((appInfo.requestedPermissionsFlags[j] + & PackageInfo.REQUESTED_PERMISSION_GRANTED) == 0) { + break; + } + } + for (int k=0; k<tpl.perms.length; k++) { + if (tpl.perms[k].equals(appInfo.requestedPermissions[j])) { + AppOpsManager.OpEntry opEntry = new AppOpsManager.OpEntry( + tpl.permOps[k], 0, 0); + dummyOps.add(opEntry); + addOp(entries, pkgOps, appEntry, opEntry); + } + } + } + } + } + } + + // Sort the list. + Collections.sort(entries, APP_OP_COMPARATOR); + + // Done! + return entries; + } +} diff --git a/src/com/android/settings/applications/AppOpsSummary.java b/src/com/android/settings/applications/AppOpsSummary.java index 4872fa5..1e0cd41 100644 --- a/src/com/android/settings/applications/AppOpsSummary.java +++ b/src/com/android/settings/applications/AppOpsSummary.java @@ -1,3 +1,19 @@ +/** + * Copyright (C) 2013 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; import android.app.AppOpsManager; @@ -23,31 +39,10 @@ public class AppOpsSummary extends Fragment { private ViewPager mViewPager; CharSequence[] mPageNames; - static int[][] sPageOps = new int[][] { - // "Location" page. - new int[] { AppOpsManager.OP_COARSE_LOCATION, AppOpsManager.OP_FINE_LOCATION, - AppOpsManager.OP_GPS }, - - // "Personal" page. - new int[] { AppOpsManager.OP_READ_CONTACTS, AppOpsManager.OP_WRITE_CONTACTS, - AppOpsManager.OP_READ_CALL_LOG, AppOpsManager.OP_WRITE_CALL_LOG }, - - // "Device" page. - new int[] { AppOpsManager.OP_VIBRATE }, - }; - static String[][] sPagePerms = new String[][] { - // "Location" page. - new String[] { android.Manifest.permission.ACCESS_COARSE_LOCATION, - android.Manifest.permission.ACCESS_FINE_LOCATION }, - - // "Personal" page. - new String[] { android.Manifest.permission.READ_CONTACTS, - android.Manifest.permission.WRITE_CONTACTS, - android.Manifest.permission.READ_CALL_LOG, - android.Manifest.permission.WRITE_CALL_LOG }, - - // "Device" page. - new String[] { android.Manifest.permission.VIBRATE }, + static AppOpsState.OpsTemplate[] sPageTemplates = new AppOpsState.OpsTemplate[] { + AppOpsState.LOCATION_TEMPLATE, + AppOpsState.PERSONAL_TEMPLATE, + AppOpsState.DEVICE_TEMPLATE }; int mCurPos; @@ -60,12 +55,12 @@ public class AppOpsSummary extends Fragment { @Override public Fragment getItem(int position) { - return new AppOpsCategory(sPageOps[position], sPagePerms[position]); + return new AppOpsCategory(sPageTemplates[position]); } @Override public int getCount() { - return sPageOps.length; + return sPageTemplates.length; } @Override diff --git a/src/com/android/settings/applications/ApplicationsState.java b/src/com/android/settings/applications/ApplicationsState.java index e87d7cf..94a0aa3 100644 --- a/src/com/android/settings/applications/ApplicationsState.java +++ b/src/com/android/settings/applications/ApplicationsState.java @@ -237,12 +237,39 @@ public class ApplicationsState { } }; + public static final AppFilter DISABLED_FILTER = new AppFilter() { + public void init() { + } + + @Override + public boolean filterApp(ApplicationInfo info) { + if (!info.enabled) { + return true; + } + return false; + } + }; + + public static final AppFilter ALL_ENABLED_FILTER = new AppFilter() { + public void init() { + } + + @Override + public boolean filterApp(ApplicationInfo info) { + if (info.enabled) { + return true; + } + return false; + } + }; + final Context mContext; final PackageManager mPm; final int mRetrieveFlags; PackageIntentReceiver mPackageIntentReceiver; boolean mResumed; + boolean mHaveDisabledApps; // Information about all applications. Synchronize on mEntriesMap // to protect access to these. @@ -617,15 +644,18 @@ public class ApplicationsState { } } + mHaveDisabledApps = false; for (int i=0; i<mApplications.size(); i++) { final ApplicationInfo info = mApplications.get(i); // Need to trim out any applications that are disabled by // something different than the user. - if (!info.enabled && info.enabledSetting - != PackageManager.COMPONENT_ENABLED_STATE_DISABLED_USER) { - mApplications.remove(i); - i--; - continue; + if (!info.enabled) { + if (info.enabledSetting != PackageManager.COMPONENT_ENABLED_STATE_DISABLED_USER) { + mApplications.remove(i); + i--; + continue; + } + mHaveDisabledApps = true; } final AppEntry entry = mEntriesMap.get(info.packageName); if (entry != null) { @@ -638,6 +668,10 @@ public class ApplicationsState { } } + public boolean haveDisabledApps() { + return mHaveDisabledApps; + } + void doPauseIfNeededLocked() { if (!mResumed) { return; @@ -732,6 +766,13 @@ public class ApplicationsState { return; } ApplicationInfo info = mPm.getApplicationInfo(pkgName, mRetrieveFlags); + if (!info.enabled) { + if (info.enabledSetting + != PackageManager.COMPONENT_ENABLED_STATE_DISABLED_USER) { + return; + } + mHaveDisabledApps = true; + } mApplications.add(info); if (!mBackgroundHandler.hasMessages(BackgroundHandler.MSG_LOAD_ENTRIES)) { mBackgroundHandler.sendEmptyMessage(BackgroundHandler.MSG_LOAD_ENTRIES); @@ -757,7 +798,17 @@ public class ApplicationsState { mEntriesMap.remove(pkgName); mAppEntries.remove(entry); } + ApplicationInfo info = mApplications.get(idx); mApplications.remove(idx); + if (!info.enabled) { + mHaveDisabledApps = false; + for (int i=0; i<mApplications.size(); i++) { + if (!mApplications.get(i).enabled) { + mHaveDisabledApps = true; + break; + } + } + } if (!mMainHandler.hasMessages(MainHandler.MSG_PACKAGE_LIST_CHANGED)) { mMainHandler.sendEmptyMessage(MainHandler.MSG_PACKAGE_LIST_CHANGED); } diff --git a/src/com/android/settings/applications/ManageApplications.java b/src/com/android/settings/applications/ManageApplications.java index d4df397..c6a9792 100644 --- a/src/com/android/settings/applications/ManageApplications.java +++ b/src/com/android/settings/applications/ManageApplications.java @@ -161,6 +161,7 @@ public class ManageApplications extends Fragment implements public static final int FILTER_APPS_ALL = MENU_OPTIONS_BASE + 0; public static final int FILTER_APPS_THIRD_PARTY = MENU_OPTIONS_BASE + 1; public static final int FILTER_APPS_SDCARD = MENU_OPTIONS_BASE + 2; + public static final int FILTER_APPS_DISABLED = MENU_OPTIONS_BASE + 3; public static final int SORT_ORDER_ALPHA = MENU_OPTIONS_BASE + 4; public static final int SORT_ORDER_SIZE = MENU_OPTIONS_BASE + 5; @@ -221,6 +222,7 @@ public class ManageApplications extends Fragment implements switch (listType) { case LIST_TYPE_DOWNLOADED: mFilter = FILTER_APPS_THIRD_PARTY; break; case LIST_TYPE_SDCARD: mFilter = FILTER_APPS_SDCARD; break; + case LIST_TYPE_DISABLED: mFilter = FILTER_APPS_DISABLED; break; default: mFilter = FILTER_APPS_ALL; break; } mClickListener = clickListener; @@ -427,6 +429,7 @@ public class ManageApplications extends Fragment implements } } private final ArrayList<TabInfo> mTabs = new ArrayList<TabInfo>(); + private int mNumTabs; TabInfo mCurTab = null; // Size resource used for packages whose size computation failed for some reason @@ -447,6 +450,7 @@ public class ManageApplications extends Fragment implements static final int LIST_TYPE_RUNNING = 1; static final int LIST_TYPE_SDCARD = 2; static final int LIST_TYPE_ALL = 3; + static final int LIST_TYPE_DISABLED = 4; private boolean mShowBackground = false; @@ -464,7 +468,7 @@ public class ManageApplications extends Fragment implements @Override public int getCount() { - return mTabs.size(); + return mNumTabs; } @Override @@ -472,6 +476,7 @@ public class ManageApplications extends Fragment implements TabInfo tab = mTabs.get(position); View root = tab.build(mInflater, mContentContainer, mRootView); container.addView(root); + root.setTag(R.id.name, tab); return root; } @@ -486,6 +491,12 @@ public class ManageApplications extends Fragment implements } @Override + public int getItemPosition(Object object) { + return super.getItemPosition(object); + //return ((TabInfo)((View)object).getTag(R.id.name)).mListType; + } + + @Override public CharSequence getPageTitle(int position) { return mTabs.get(position).mLabel; } @@ -607,8 +618,11 @@ public class ManageApplications extends Fragment implements mWhichSize = SIZE_EXTERNAL; } break; + case FILTER_APPS_DISABLED: + filterObj = ApplicationsState.DISABLED_FILTER; + break; default: - filterObj = null; + filterObj = ApplicationsState.ALL_ENABLED_FILTER; break; } switch (mLastSortMode) { @@ -869,6 +883,13 @@ public class ManageApplications extends Fragment implements getActivity().getString(R.string.filter_apps_all), LIST_TYPE_ALL, this, savedInstanceState); mTabs.add(tab); + + tab = new TabInfo(this, mApplicationsState, + getActivity().getString(R.string.filter_apps_disabled), + LIST_TYPE_DISABLED, this, savedInstanceState); + mTabs.add(tab); + + mNumTabs = mTabs.size(); } @@ -922,6 +943,7 @@ public class ManageApplications extends Fragment implements public void onResume() { super.onResume(); mActivityResumed = true; + updateNumTabs(); updateCurrentTab(mViewPager.getCurrentItem()); updateOptionsMenu(); } @@ -975,6 +997,16 @@ public class ManageApplications extends Fragment implements } } + private void updateNumTabs() { + int newNum = mApplicationsState.haveDisabledApps() ? mTabs.size() : (mTabs.size()-1); + if (newNum != mNumTabs) { + mNumTabs = newNum; + if (mViewPager != null) { + mViewPager.getAdapter().notifyDataSetChanged(); + } + } + } + TabInfo tabForType(int type) { for (int i = 0; i < mTabs.size(); i++) { TabInfo tab = mTabs.get(i); |