diff --git a/AndroidManifest.xml b/AndroidManifest.xml index 37edca7b9ca..43fe7964b96 100644 --- a/AndroidManifest.xml +++ b/AndroidManifest.xml @@ -1161,6 +1161,18 @@ --> + + + + + + + + + android:columnCount="4"> + + + + + + diff --git a/res/values/arrays.xml b/res/values/arrays.xml index f04892ef828..12a0172cb9b 100644 --- a/res/values/arrays.xml +++ b/res/values/arrays.xml @@ -620,11 +620,25 @@ monitor high power location get usage stats mute/unmute microphone + show toast project media activate VPN write wallpaper assist structure assist screenshot + read phone state + add voicemail + use sip + process outgoing call + fingerprint + body sensors + read cell broadcasts + mock location + read storage + write storage + turn on screen + get accounts + run in background @@ -674,11 +688,25 @@ Location Get usage stats Mute/unmute microphone + Show toast Project media Activate VPN Write wallpaper Assist structure Assist screenshot + Read phone state + Add voicemail + Use sip + Process outgoing call + Fingerprint + Body sensors + Read cell broadcasts + Mock location + Read storage + Write storage + Turn on screen + Get accounts + Run in background diff --git a/res/values/strings.xml b/res/values/strings.xml index cd7a7103893..c0436076441 100644 --- a/res/values/strings.xml +++ b/res/values/strings.xml @@ -6425,6 +6425,12 @@ usb_use_file_transfer, use_use_photo_transfer, and usb_use_MIDI --> Use USB for + + Background check + + + Full background access + Use text from screen diff --git a/res/xml/development_prefs.xml b/res/xml/development_prefs.xml index 84c3cbfaaae..d3c88529068 100644 --- a/res/xml/development_prefs.xml +++ b/res/xml/development_prefs.xml @@ -340,6 +340,10 @@ android:entries="@array/app_process_limit_entries" android:entryValues="@array/app_process_limit_values" /> + + mApps; PackageIntentReceiver mPackageObserver; - public AppListLoader(Context context, AppOpsState state, AppOpsState.OpsTemplate template) { + public AppListLoader(Context context, AppOpsState state, AppOpsState.OpsTemplate template, + boolean userControlled) { super(context); mState = state; mTemplate = template; + mUserControlled = userControlled; } @Override public List loadInBackground() { - return mState.buildState(mTemplate); + return mState.buildState(mTemplate, 0, null, + mUserControlled ? AppOpsState.LABEL_COMPARATOR : AppOpsState.RECENCY_COMPARATOR); } /** @@ -247,13 +259,15 @@ public class AppOpsCategory extends ListFragment implements private final Resources mResources; private final LayoutInflater mInflater; private final AppOpsState mState; + private final boolean mUserControlled; List mList; - public AppListAdapter(Context context, AppOpsState state) { + public AppListAdapter(Context context, AppOpsState state, boolean userControlled) { mResources = context.getResources(); mInflater = (LayoutInflater)context.getSystemService(Context.LAYOUT_INFLATER_SERVICE); mState = state; + mUserControlled = userControlled; } public void setData(List data) { @@ -292,9 +306,18 @@ 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()); - ((TextView)view.findViewById(R.id.op_name)).setText(item.getSummaryText(mState)); - ((TextView)view.findViewById(R.id.op_time)).setText( - item.getTimeText(mResources, false)); + if (mUserControlled) { + ((TextView) view.findViewById(R.id.op_name)).setText( + item.getTimeText(mResources, false)); + view.findViewById(R.id.op_time).setVisibility(View.GONE); + ((Switch) view.findViewById(R.id.op_switch)).setChecked( + item.getPrimaryOpMode() == AppOpsManager.MODE_ALLOWED); + } else { + ((TextView) view.findViewById(R.id.op_name)).setText(item.getSummaryText(mState)); + ((TextView) view.findViewById(R.id.op_time)).setText( + item.getTimeText(mResources, false)); + view.findViewById(R.id.op_switch).setVisibility(View.GONE); + } return view; } @@ -304,6 +327,7 @@ public class AppOpsCategory extends ListFragment implements public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); mState = new AppOpsState(getActivity()); + mUserControlled = getArguments().getBoolean("userControlled"); } @Override public void onActivityCreated(Bundle savedInstanceState) { @@ -317,7 +341,7 @@ public class AppOpsCategory extends ListFragment implements setHasOptionsMenu(true); // Create an empty adapter we will use to display the loaded data. - mAdapter = new AppListAdapter(getActivity(), mState); + mAdapter = new AppListAdapter(getActivity(), mState, mUserControlled); setListAdapter(mAdapter); // Start out with a progress indicator. @@ -341,8 +365,22 @@ public class AppOpsCategory extends ListFragment implements @Override public void onListItemClick(ListView l, View v, int position, long id) { AppOpEntry entry = mAdapter.getItem(position); if (entry != null) { - mCurrentPkgName = entry.getAppEntry().getApplicationInfo().packageName; - startApplicationDetailsActivity(); + if (mUserControlled) { + // We treat this as tapping on the check box, toggling the app op state. + Switch sw = ((Switch) v.findViewById(R.id.op_switch)); + boolean checked = !sw.isChecked(); + sw.setChecked(checked); + AppOpsManager.OpEntry op = entry.getOpEntry(0); + int mode = checked ? AppOpsManager.MODE_ALLOWED : AppOpsManager.MODE_IGNORED; + mState.getAppOpsManager().setMode(op.getOp(), + entry.getAppEntry().getApplicationInfo().uid, + entry.getAppEntry().getApplicationInfo().packageName, + mode); + entry.overridePrimaryOpMode(mode); + } else { + mCurrentPkgName = entry.getAppEntry().getApplicationInfo().packageName; + startApplicationDetailsActivity(); + } } } @@ -352,7 +390,7 @@ public class AppOpsCategory extends ListFragment implements if (fargs != null) { template = (AppOpsState.OpsTemplate)fargs.getParcelable("template"); } - return new AppListLoader(getActivity(), mState, template); + return new AppListLoader(getActivity(), mState, template, mUserControlled); } @Override public void onLoadFinished(Loader> loader, List data) { diff --git a/src/com/android/settings/applications/AppOpsState.java b/src/com/android/settings/applications/AppOpsState.java index c3189d62fc6..237eac6f975 100644 --- a/src/com/android/settings/applications/AppOpsState.java +++ b/src/com/android/settings/applications/AppOpsState.java @@ -180,6 +180,7 @@ public class AppOpsState { false, false, false, + false, false } ); @@ -206,9 +207,14 @@ public class AppOpsState { false } ); + public static final OpsTemplate RUN_IN_BACKGROUND_TEMPLATE = new OpsTemplate( + new int[] { AppOpsManager.OP_RUN_IN_BACKGROUND }, + new boolean[] { false } + ); + public static final OpsTemplate[] ALL_TEMPLATES = new OpsTemplate[] { LOCATION_TEMPLATE, PERSONAL_TEMPLATE, MESSAGING_TEMPLATE, - MEDIA_TEMPLATE, DEVICE_TEMPLATE + MEDIA_TEMPLATE, DEVICE_TEMPLATE, RUN_IN_BACKGROUND_TEMPLATE }; /** @@ -306,6 +312,7 @@ public class AppOpsState { = new ArrayList(); private final AppEntry mApp; private final int mSwitchOrder; + private int mOverriddenPrimaryMode = -1; public AppOpEntry(AppOpsManager.PackageOps pkg, AppOpsManager.OpEntry op, AppEntry app, int switchOrder) { @@ -363,6 +370,14 @@ public class AppOpsState { return mOps.get(pos); } + public int getPrimaryOpMode() { + return mOverriddenPrimaryMode >= 0 ? mOverriddenPrimaryMode : mOps.get(0).getMode(); + } + + public void overridePrimaryOpMode(int mode) { + mOverriddenPrimaryMode = mode; + } + private CharSequence getCombinedText(ArrayList ops, CharSequence[] items) { if (ops.size() == 1) { @@ -418,9 +433,9 @@ public class AppOpsState { } /** - * Perform alphabetical comparison of application entry objects. + * Perform app op state comparison of application entry objects. */ - public static final Comparator APP_OP_COMPARATOR = new Comparator() { + public static final Comparator RECENCY_COMPARATOR = new Comparator() { private final Collator sCollator = Collator.getInstance(); @Override public int compare(AppOpEntry object1, AppOpEntry object2) { @@ -440,6 +455,18 @@ public class AppOpsState { } }; + /** + * Perform alphabetical comparison of application entry objects. + */ + public static final Comparator LABEL_COMPARATOR = new Comparator() { + private final Collator sCollator = Collator.getInstance(); + @Override + public int compare(AppOpEntry object1, AppOpEntry object2) { + return sCollator.compare(object1.getAppEntry().getLabel(), + object2.getAppEntry().getLabel()); + } + }; + private void addOp(List entries, AppOpsManager.PackageOps pkgOps, AppEntry appEntry, AppOpsManager.OpEntry opEntry, boolean allowMerge, int switchOrder) { if (allowMerge && entries.size() > 0) { @@ -466,8 +493,12 @@ public class AppOpsState { entries.add(entry); } + public AppOpsManager getAppOpsManager() { + return mAppOps; + } + public List buildState(OpsTemplate tpl) { - return buildState(tpl, 0, null); + return buildState(tpl, 0, null, RECENCY_COMPARATOR); } private AppEntry getAppEntry(final Context context, final HashMap appEntries, @@ -492,6 +523,11 @@ public class AppOpsState { } public List buildState(OpsTemplate tpl, int uid, String packageName) { + return buildState(tpl, uid, packageName, RECENCY_COMPARATOR); + } + + public List buildState(OpsTemplate tpl, int uid, String packageName, + Comparator comparator) { final Context context = mContext; final HashMap appEntries = new HashMap(); @@ -593,7 +629,7 @@ public class AppOpsState { } // Sort the list. - Collections.sort(entries, APP_OP_COMPARATOR); + Collections.sort(entries, comparator); // Done! return entries; diff --git a/src/com/android/settings/applications/BackgroundCheckSummary.java b/src/com/android/settings/applications/BackgroundCheckSummary.java new file mode 100644 index 00000000000..dfd4c496371 --- /dev/null +++ b/src/com/android/settings/applications/BackgroundCheckSummary.java @@ -0,0 +1,66 @@ +/** + * Copyright (C) 2015 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; +import android.app.FragmentManager; +import android.app.FragmentTransaction; +import android.content.res.TypedArray; +import android.os.Bundle; +import android.preference.PreferenceFrameLayout; +import android.support.v13.app.FragmentPagerAdapter; +import android.support.v4.view.PagerTabStrip; +import android.support.v4.view.ViewPager; +import android.view.LayoutInflater; +import android.view.View; +import android.view.ViewGroup; + +import com.android.internal.logging.MetricsLogger; +import com.android.settings.InstrumentedFragment; +import com.android.settings.R; + +public class BackgroundCheckSummary extends InstrumentedFragment { + // layout inflater object used to inflate views + private LayoutInflater mInflater; + + @Override + protected int getMetricsCategory() { + return MetricsLogger.BACKGROUND_CHECK_SUMMARY; + } + + @Override + public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { + // initialize the inflater + mInflater = inflater; + + View rootView = mInflater.inflate(R.layout.background_check_summary, + container, false); + + // We have to do this now because PreferenceFrameLayout looks at it + // only when the view is added. + if (container instanceof PreferenceFrameLayout) { + ((PreferenceFrameLayout.LayoutParams) rootView.getLayoutParams()).removeBorders = true; + } + + FragmentTransaction ft = getChildFragmentManager().beginTransaction(); + ft.add(R.id.appops_content, new AppOpsCategory(AppOpsState.RUN_IN_BACKGROUND_TEMPLATE, + true), "appops"); + ft.commitAllowingStateLoss(); + + return rootView; + } +}