Files
app_Settings/src/com/android/settings/applications/ManageApplications.java
Dianne Hackborn 3db54cb976 Add app details menu item to uninstall an app for all users.
Also some fixes: uninstalled apps are now marked as such in the
all apps lists, and sorted to the bottom like disabled apps.  We
don't immediately close the installed app details screen when
launching the uninstall UI.

Change-Id: Id22b749c20fc2441733c604d1de8889269217c5c
2012-09-16 18:27:14 -07:00

1240 lines
49 KiB
Java

/*
* Copyright (C) 2006 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 static android.net.NetworkPolicyManager.POLICY_NONE;
import static android.net.NetworkPolicyManager.POLICY_REJECT_METERED_BACKGROUND;
import android.app.Activity;
import android.app.ActivityManager;
import android.app.AlertDialog;
import android.app.Fragment;
import android.app.INotificationManager;
import android.content.ComponentName;
import android.content.Context;
import android.content.DialogInterface;
import android.content.Intent;
import android.content.IntentFilter;
import android.content.ServiceConnection;
import android.content.pm.ApplicationInfo;
import android.content.pm.IPackageManager;
import android.content.pm.PackageInfo;
import android.content.pm.PackageManager;
import android.net.NetworkPolicyManager;
import android.os.AsyncTask;
import android.os.Bundle;
import android.os.Environment;
import android.os.Handler;
import android.os.IBinder;
import android.os.RemoteException;
import android.os.ServiceManager;
import android.os.UserHandle;
import android.preference.PreferenceActivity;
import android.preference.PreferenceFrameLayout;
import android.provider.Settings;
import android.support.v4.view.PagerAdapter;
import android.support.v4.view.PagerTabStrip;
import android.support.v4.view.ViewPager;
import android.text.format.Formatter;
import android.util.Log;
import android.view.LayoutInflater;
import android.view.Menu;
import android.view.MenuInflater;
import android.view.MenuItem;
import android.view.View;
import android.view.ViewGroup;
import android.view.animation.AnimationUtils;
import android.widget.AbsListView;
import android.widget.AdapterView;
import android.widget.AdapterView.OnItemClickListener;
import android.widget.BaseAdapter;
import android.widget.Filter;
import android.widget.Filterable;
import android.widget.ListView;
import android.widget.TextView;
import com.android.internal.app.IMediaContainerService;
import com.android.internal.content.PackageHelper;
import com.android.settings.R;
import com.android.settings.Settings.RunningServicesActivity;
import com.android.settings.Settings.StorageUseActivity;
import com.android.settings.applications.ApplicationsState.AppEntry;
import com.android.settings.deviceinfo.StorageMeasurement;
import com.android.settings.Utils;
import java.util.ArrayList;
import java.util.Comparator;
import java.util.List;
final class CanBeOnSdCardChecker {
final IPackageManager mPm;
int mInstallLocation;
CanBeOnSdCardChecker() {
mPm = IPackageManager.Stub.asInterface(
ServiceManager.getService("package"));
}
void init() {
try {
mInstallLocation = mPm.getInstallLocation();
} catch (RemoteException e) {
Log.e("CanBeOnSdCardChecker", "Is Package Manager running?");
return;
}
}
boolean check(ApplicationInfo info) {
boolean canBe = false;
if ((info.flags & ApplicationInfo.FLAG_EXTERNAL_STORAGE) != 0) {
canBe = true;
} else {
if ((info.flags & ApplicationInfo.FLAG_SYSTEM) == 0) {
if (info.installLocation == PackageInfo.INSTALL_LOCATION_PREFER_EXTERNAL ||
info.installLocation == PackageInfo.INSTALL_LOCATION_AUTO) {
canBe = true;
} else if (info.installLocation
== PackageInfo.INSTALL_LOCATION_UNSPECIFIED) {
if (mInstallLocation == PackageHelper.APP_INSTALL_EXTERNAL) {
// For apps with no preference and the default value set
// to install on sdcard.
canBe = true;
}
}
}
}
return canBe;
}
}
interface AppClickListener {
void onItemClick(ManageApplications.TabInfo tab, AdapterView<?> parent,
View view, int position, long id);
}
/**
* Activity to pick an application that will be used to display installation information and
* options to uninstall/delete user data for system applications. This activity
* can be launched through Settings or via the ACTION_MANAGE_PACKAGE_STORAGE
* intent.
*/
public class ManageApplications extends Fragment implements
AppClickListener, DialogInterface.OnClickListener,
DialogInterface.OnDismissListener {
static final String TAG = "ManageApplications";
static final boolean DEBUG = false;
private static final String EXTRA_SORT_ORDER = "sortOrder";
private static final String EXTRA_SHOW_BACKGROUND = "showBackground";
private static final String EXTRA_DEFAULT_LIST_TYPE = "defaultListType";
private static final String EXTRA_RESET_DIALOG = "resetDialog";
// attributes used as keys when passing values to InstalledAppDetails activity
public static final String APP_CHG = "chg";
// constant value that can be used to check return code from sub activity.
private static final int INSTALLED_APP_DETAILS = 1;
public static final int SIZE_TOTAL = 0;
public static final int SIZE_INTERNAL = 1;
public static final int SIZE_EXTERNAL = 2;
// sort order that can be changed through the menu can be sorted alphabetically
// or size(descending)
private static final int MENU_OPTIONS_BASE = 0;
// Filter options used for displayed list of applications
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 SORT_ORDER_ALPHA = MENU_OPTIONS_BASE + 4;
public static final int SORT_ORDER_SIZE = MENU_OPTIONS_BASE + 5;
public static final int SHOW_RUNNING_SERVICES = MENU_OPTIONS_BASE + 6;
public static final int SHOW_BACKGROUND_PROCESSES = MENU_OPTIONS_BASE + 7;
public static final int RESET_APP_PREFERENCES = MENU_OPTIONS_BASE + 8;
// sort order
private int mSortOrder = SORT_ORDER_ALPHA;
private ApplicationsState mApplicationsState;
public static class TabInfo implements OnItemClickListener {
public final ManageApplications mOwner;
public final ApplicationsState mApplicationsState;
public final CharSequence mLabel;
public final int mListType;
public final int mFilter;
public final AppClickListener mClickListener;
public final CharSequence mInvalidSizeStr;
public final CharSequence mComputingSizeStr;
private final Bundle mSavedInstanceState;
public ApplicationsAdapter mApplications;
public LayoutInflater mInflater;
public View mRootView;
private IMediaContainerService mContainerService;
private View mLoadingContainer;
private View mListContainer;
// ListView used to display list
private ListView mListView;
// Custom view used to display running processes
private RunningProcessesView mRunningProcessesView;
private LinearColorBar mColorBar;
private TextView mStorageChartLabel;
private TextView mUsedStorageText;
private TextView mFreeStorageText;
private long mFreeStorage = 0, mAppStorage = 0, mTotalStorage = 0;
private long mLastUsedStorage, mLastAppStorage, mLastFreeStorage;
final Runnable mRunningProcessesAvail = new Runnable() {
public void run() {
handleRunningProcessesAvail();
}
};
public TabInfo(ManageApplications owner, ApplicationsState apps,
CharSequence label, int listType, AppClickListener clickListener,
Bundle savedInstanceState) {
mOwner = owner;
mApplicationsState = apps;
mLabel = label;
mListType = listType;
switch (listType) {
case LIST_TYPE_DOWNLOADED: mFilter = FILTER_APPS_THIRD_PARTY; break;
case LIST_TYPE_SDCARD: mFilter = FILTER_APPS_SDCARD; break;
default: mFilter = FILTER_APPS_ALL; break;
}
mClickListener = clickListener;
mInvalidSizeStr = owner.getActivity().getText(R.string.invalid_size_value);
mComputingSizeStr = owner.getActivity().getText(R.string.computing_size);
mSavedInstanceState = savedInstanceState;
}
public void setContainerService(IMediaContainerService containerService) {
mContainerService = containerService;
updateStorageUsage();
}
public View build(LayoutInflater inflater, ViewGroup contentParent, View contentChild) {
if (mRootView != null) {
return mRootView;
}
mInflater = inflater;
mRootView = inflater.inflate(mListType == LIST_TYPE_RUNNING
? R.layout.manage_applications_running
: R.layout.manage_applications_apps, null);
mLoadingContainer = mRootView.findViewById(R.id.loading_container);
mLoadingContainer.setVisibility(View.VISIBLE);
mListContainer = mRootView.findViewById(R.id.list_container);
if (mListContainer != null) {
// Create adapter and list view here
View emptyView = mListContainer.findViewById(com.android.internal.R.id.empty);
ListView lv = (ListView) mListContainer.findViewById(android.R.id.list);
if (emptyView != null) {
lv.setEmptyView(emptyView);
}
lv.setOnItemClickListener(this);
lv.setSaveEnabled(true);
lv.setItemsCanFocus(true);
lv.setTextFilterEnabled(true);
mListView = lv;
mApplications = new ApplicationsAdapter(mApplicationsState, this, mFilter);
mListView.setAdapter(mApplications);
mListView.setRecyclerListener(mApplications);
mColorBar = (LinearColorBar)mListContainer.findViewById(R.id.storage_color_bar);
mStorageChartLabel = (TextView)mListContainer.findViewById(R.id.storageChartLabel);
mUsedStorageText = (TextView)mListContainer.findViewById(R.id.usedStorageText);
mFreeStorageText = (TextView)mListContainer.findViewById(R.id.freeStorageText);
Utils.prepareCustomPreferencesList(contentParent, contentChild, mListView, false);
if (mFilter == FILTER_APPS_SDCARD) {
mStorageChartLabel.setText(mOwner.getActivity().getText(
R.string.sd_card_storage));
} else {
mStorageChartLabel.setText(mOwner.getActivity().getText(
R.string.internal_storage));
}
applyCurrentStorage();
}
mRunningProcessesView = (RunningProcessesView)mRootView.findViewById(
R.id.running_processes);
if (mRunningProcessesView != null) {
mRunningProcessesView.doCreate(mSavedInstanceState);
}
return mRootView;
}
public void detachView() {
if (mRootView != null) {
ViewGroup group = (ViewGroup)mRootView.getParent();
if (group != null) {
group.removeView(mRootView);
}
}
}
public void resume(int sortOrder) {
if (mApplications != null) {
mApplications.resume(sortOrder);
}
if (mRunningProcessesView != null) {
boolean haveData = mRunningProcessesView.doResume(mOwner, mRunningProcessesAvail);
if (haveData) {
mRunningProcessesView.setVisibility(View.VISIBLE);
mLoadingContainer.setVisibility(View.INVISIBLE);
} else {
mLoadingContainer.setVisibility(View.VISIBLE);
}
}
}
public void pause() {
if (mApplications != null) {
mApplications.pause();
}
if (mRunningProcessesView != null) {
mRunningProcessesView.doPause();
}
}
void updateStorageUsage() {
// Make sure a callback didn't come at an inopportune time.
if (mOwner.getActivity() == null) return;
// Doesn't make sense for stuff that is not an app list.
if (mApplications == null) return;
mFreeStorage = 0;
mAppStorage = 0;
mTotalStorage = 0;
if (mFilter == FILTER_APPS_SDCARD) {
if (mContainerService != null) {
try {
final long[] stats = mContainerService.getFileSystemStats(
Environment.getExternalStorageDirectory().getPath());
mTotalStorage = stats[0];
mFreeStorage = stats[1];
} catch (RemoteException e) {
Log.w(TAG, "Problem in container service", e);
}
}
if (mApplications != null) {
final int N = mApplications.getCount();
for (int i=0; i<N; i++) {
ApplicationsState.AppEntry ae = mApplications.getAppEntry(i);
mAppStorage += ae.externalCodeSize + ae.externalDataSize
+ ae.externalCacheSize;
}
}
} else {
if (mContainerService != null) {
try {
final long[] stats = mContainerService.getFileSystemStats(
Environment.getDataDirectory().getPath());
mTotalStorage = stats[0];
mFreeStorage = stats[1];
} catch (RemoteException e) {
Log.w(TAG, "Problem in container service", e);
}
}
final boolean emulatedStorage = Environment.isExternalStorageEmulated();
if (mApplications != null) {
final int N = mApplications.getCount();
for (int i=0; i<N; i++) {
ApplicationsState.AppEntry ae = mApplications.getAppEntry(i);
mAppStorage += ae.codeSize + ae.dataSize;
if (emulatedStorage) {
mAppStorage += ae.externalCodeSize + ae.externalDataSize;
}
}
}
mFreeStorage += mApplicationsState.sumCacheSizes();
}
applyCurrentStorage();
}
void applyCurrentStorage() {
// If view hierarchy is not yet created, no views to update.
if (mRootView == null) {
return;
}
if (mTotalStorage > 0) {
mColorBar.setRatios((mTotalStorage-mFreeStorage-mAppStorage)/(float)mTotalStorage,
mAppStorage/(float)mTotalStorage, mFreeStorage/(float)mTotalStorage);
long usedStorage = mTotalStorage - mFreeStorage;
if (mLastUsedStorage != usedStorage) {
mLastUsedStorage = usedStorage;
String sizeStr = Formatter.formatShortFileSize(
mOwner.getActivity(), usedStorage);
mUsedStorageText.setText(mOwner.getActivity().getResources().getString(
R.string.service_foreground_processes, sizeStr));
}
if (mLastFreeStorage != mFreeStorage) {
mLastFreeStorage = mFreeStorage;
String sizeStr = Formatter.formatShortFileSize(
mOwner.getActivity(), mFreeStorage);
mFreeStorageText.setText(mOwner.getActivity().getResources().getString(
R.string.service_background_processes, sizeStr));
}
} else {
mColorBar.setRatios(0, 0, 0);
if (mLastUsedStorage != -1) {
mLastUsedStorage = -1;
mUsedStorageText.setText("");
}
if (mLastFreeStorage != -1) {
mLastFreeStorage = -1;
mFreeStorageText.setText("");
}
}
}
@Override
public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
mClickListener.onItemClick(this, parent, view, position, id);
}
void handleRunningProcessesAvail() {
mLoadingContainer.startAnimation(AnimationUtils.loadAnimation(
mOwner.getActivity(), android.R.anim.fade_out));
mRunningProcessesView.startAnimation(AnimationUtils.loadAnimation(
mOwner.getActivity(), android.R.anim.fade_in));
mRunningProcessesView.setVisibility(View.VISIBLE);
mLoadingContainer.setVisibility(View.GONE);
}
}
private final ArrayList<TabInfo> mTabs = new ArrayList<TabInfo>();
TabInfo mCurTab = null;
// Size resource used for packages whose size computation failed for some reason
CharSequence mInvalidSizeStr;
private CharSequence mComputingSizeStr;
// layout inflater object used to inflate views
private LayoutInflater mInflater;
private String mCurrentPkgName;
private Menu mOptionsMenu;
// These are for keeping track of activity and spinner switch state.
private boolean mActivityResumed;
static final int LIST_TYPE_DOWNLOADED = 0;
static final int LIST_TYPE_RUNNING = 1;
static final int LIST_TYPE_SDCARD = 2;
static final int LIST_TYPE_ALL = 3;
private boolean mShowBackground = false;
private int mDefaultListType = -1;
private ViewGroup mContentContainer;
private View mRootView;
private ViewPager mViewPager;
AlertDialog mResetDialog;
class MyPagerAdapter extends PagerAdapter
implements ViewPager.OnPageChangeListener {
int mCurPos = 0;
@Override
public int getCount() {
return mTabs.size();
}
@Override
public Object instantiateItem(ViewGroup container, int position) {
TabInfo tab = mTabs.get(position);
View root = tab.build(mInflater, mContentContainer, mRootView);
container.addView(root);
return root;
}
@Override
public void destroyItem(ViewGroup container, int position, Object object) {
container.removeView((View)object);
}
@Override
public boolean isViewFromObject(View view, Object object) {
return view == object;
}
@Override
public CharSequence getPageTitle(int position) {
return mTabs.get(position).mLabel;
}
@Override
public void onPageScrolled(int position, float positionOffset, int positionOffsetPixels) {
}
@Override
public void onPageSelected(int position) {
mCurPos = position;
}
@Override
public void onPageScrollStateChanged(int state) {
if (state == ViewPager.SCROLL_STATE_IDLE) {
updateCurrentTab(mCurPos);
}
}
}
/*
* Custom adapter implementation for the ListView
* This adapter maintains a map for each displayed application and its properties
* An index value on each AppInfo object indicates the correct position or index
* in the list. If the list gets updated dynamically when the user is viewing the list of
* applications, we need to return the correct index of position. This is done by mapping
* the getId methods via the package name into the internal maps and indices.
* The order of applications in the list is mirrored in mAppLocalList
*/
static class ApplicationsAdapter extends BaseAdapter implements Filterable,
ApplicationsState.Callbacks, AbsListView.RecyclerListener {
private final ApplicationsState mState;
private final ApplicationsState.Session mSession;
private final TabInfo mTab;
private final Context mContext;
private final ArrayList<View> mActive = new ArrayList<View>();
private final int mFilterMode;
private ArrayList<ApplicationsState.AppEntry> mBaseEntries;
private ArrayList<ApplicationsState.AppEntry> mEntries;
private boolean mResumed;
private int mLastSortMode=-1;
private boolean mWaitingForData;
private int mWhichSize = SIZE_TOTAL;
CharSequence mCurFilterPrefix;
private Filter mFilter = new Filter() {
@Override
protected FilterResults performFiltering(CharSequence constraint) {
ArrayList<ApplicationsState.AppEntry> entries
= applyPrefixFilter(constraint, mBaseEntries);
FilterResults fr = new FilterResults();
fr.values = entries;
fr.count = entries.size();
return fr;
}
@Override
protected void publishResults(CharSequence constraint, FilterResults results) {
mCurFilterPrefix = constraint;
mEntries = (ArrayList<ApplicationsState.AppEntry>)results.values;
notifyDataSetChanged();
mTab.updateStorageUsage();
}
};
public ApplicationsAdapter(ApplicationsState state, TabInfo tab, int filterMode) {
mState = state;
mSession = state.newSession(this);
mTab = tab;
mContext = tab.mOwner.getActivity();
mFilterMode = filterMode;
}
public void resume(int sort) {
if (DEBUG) Log.i(TAG, "Resume! mResumed=" + mResumed);
if (!mResumed) {
mResumed = true;
mSession.resume();
mLastSortMode = sort;
rebuild(true);
} else {
rebuild(sort);
}
}
public void pause() {
if (mResumed) {
mResumed = false;
mSession.pause();
}
}
public void rebuild(int sort) {
if (sort == mLastSortMode) {
return;
}
mLastSortMode = sort;
rebuild(true);
}
public void rebuild(boolean eraseold) {
if (DEBUG) Log.i(TAG, "Rebuilding app list...");
ApplicationsState.AppFilter filterObj;
Comparator<AppEntry> comparatorObj;
boolean emulated = Environment.isExternalStorageEmulated();
if (emulated) {
mWhichSize = SIZE_TOTAL;
} else {
mWhichSize = SIZE_INTERNAL;
}
switch (mFilterMode) {
case FILTER_APPS_THIRD_PARTY:
filterObj = ApplicationsState.THIRD_PARTY_FILTER;
break;
case FILTER_APPS_SDCARD:
filterObj = ApplicationsState.ON_SD_CARD_FILTER;
if (!emulated) {
mWhichSize = SIZE_EXTERNAL;
}
break;
default:
filterObj = null;
break;
}
switch (mLastSortMode) {
case SORT_ORDER_SIZE:
switch (mWhichSize) {
case SIZE_INTERNAL:
comparatorObj = ApplicationsState.INTERNAL_SIZE_COMPARATOR;
break;
case SIZE_EXTERNAL:
comparatorObj = ApplicationsState.EXTERNAL_SIZE_COMPARATOR;
break;
default:
comparatorObj = ApplicationsState.SIZE_COMPARATOR;
break;
}
break;
default:
comparatorObj = ApplicationsState.ALPHA_COMPARATOR;
break;
}
ArrayList<ApplicationsState.AppEntry> entries
= mSession.rebuild(filterObj, comparatorObj);
if (entries == null && !eraseold) {
// Don't have new list yet, but can continue using the old one.
return;
}
mBaseEntries = entries;
if (mBaseEntries != null) {
mEntries = applyPrefixFilter(mCurFilterPrefix, mBaseEntries);
} else {
mEntries = null;
}
notifyDataSetChanged();
mTab.updateStorageUsage();
if (entries == null) {
mWaitingForData = true;
mTab.mListContainer.setVisibility(View.INVISIBLE);
mTab.mLoadingContainer.setVisibility(View.VISIBLE);
} else {
mTab.mListContainer.setVisibility(View.VISIBLE);
mTab.mLoadingContainer.setVisibility(View.GONE);
}
}
ArrayList<ApplicationsState.AppEntry> applyPrefixFilter(CharSequence prefix,
ArrayList<ApplicationsState.AppEntry> origEntries) {
if (prefix == null || prefix.length() == 0) {
return origEntries;
} else {
String prefixStr = ApplicationsState.normalize(prefix.toString());
final String spacePrefixStr = " " + prefixStr;
ArrayList<ApplicationsState.AppEntry> newEntries
= new ArrayList<ApplicationsState.AppEntry>();
for (int i=0; i<origEntries.size(); i++) {
ApplicationsState.AppEntry entry = origEntries.get(i);
String nlabel = entry.getNormalizedLabel();
if (nlabel.startsWith(prefixStr) || nlabel.indexOf(spacePrefixStr) != -1) {
newEntries.add(entry);
}
}
return newEntries;
}
}
@Override
public void onRunningStateChanged(boolean running) {
mTab.mOwner.getActivity().setProgressBarIndeterminateVisibility(running);
}
@Override
public void onRebuildComplete(ArrayList<AppEntry> apps) {
if (mTab.mLoadingContainer.getVisibility() == View.VISIBLE) {
mTab.mLoadingContainer.startAnimation(AnimationUtils.loadAnimation(
mContext, android.R.anim.fade_out));
mTab.mListContainer.startAnimation(AnimationUtils.loadAnimation(
mContext, android.R.anim.fade_in));
}
mTab.mListContainer.setVisibility(View.VISIBLE);
mTab.mLoadingContainer.setVisibility(View.GONE);
mWaitingForData = false;
mBaseEntries = apps;
mEntries = applyPrefixFilter(mCurFilterPrefix, mBaseEntries);
notifyDataSetChanged();
mTab.updateStorageUsage();
}
@Override
public void onPackageListChanged() {
rebuild(false);
}
@Override
public void onPackageIconChanged() {
// We ensure icons are loaded when their item is displayed, so
// don't care about icons loaded in the background.
}
@Override
public void onPackageSizeChanged(String packageName) {
for (int i=0; i<mActive.size(); i++) {
AppViewHolder holder = (AppViewHolder)mActive.get(i).getTag();
if (holder.entry.info.packageName.equals(packageName)) {
synchronized (holder.entry) {
holder.updateSizeText(mTab.mInvalidSizeStr, mWhichSize);
}
if (holder.entry.info.packageName.equals(mTab.mOwner.mCurrentPkgName)
&& mLastSortMode == SORT_ORDER_SIZE) {
// We got the size information for the last app the
// user viewed, and are sorting by size... they may
// have cleared data, so we immediately want to resort
// the list with the new size to reflect it to the user.
rebuild(false);
}
mTab.updateStorageUsage();
return;
}
}
}
@Override
public void onAllSizesComputed() {
if (mLastSortMode == SORT_ORDER_SIZE) {
rebuild(false);
}
mTab.updateStorageUsage();
}
public int getCount() {
return mEntries != null ? mEntries.size() : 0;
}
public Object getItem(int position) {
return mEntries.get(position);
}
public ApplicationsState.AppEntry getAppEntry(int position) {
return mEntries.get(position);
}
public long getItemId(int position) {
return mEntries.get(position).id;
}
public View getView(int position, View convertView, ViewGroup parent) {
// A ViewHolder keeps references to children views to avoid unnecessary calls
// to findViewById() on each row.
AppViewHolder holder = AppViewHolder.createOrRecycle(mTab.mInflater, convertView);
convertView = holder.rootView;
// Bind the data efficiently with the holder
ApplicationsState.AppEntry entry = mEntries.get(position);
synchronized (entry) {
holder.entry = entry;
if (entry.label != null) {
holder.appName.setText(entry.label);
holder.appName.setTextColor(mContext.getResources().getColorStateList(
entry.info.enabled ? android.R.color.primary_text_dark
: android.R.color.secondary_text_dark));
}
mState.ensureIcon(entry);
if (entry.icon != null) {
holder.appIcon.setImageDrawable(entry.icon);
}
holder.updateSizeText(mTab.mInvalidSizeStr, mWhichSize);
if ((entry.info.flags&ApplicationInfo.FLAG_INSTALLED) == 0) {
holder.disabled.setVisibility(View.VISIBLE);
holder.disabled.setText(R.string.not_installed);
} else if (!entry.info.enabled) {
holder.disabled.setVisibility(View.VISIBLE);
holder.disabled.setText(R.string.disabled);
} else {
holder.disabled.setVisibility(View.GONE);
}
if (mFilterMode == FILTER_APPS_SDCARD) {
holder.checkBox.setVisibility(View.VISIBLE);
holder.checkBox.setChecked((entry.info.flags
& ApplicationInfo.FLAG_EXTERNAL_STORAGE) != 0);
} else {
holder.checkBox.setVisibility(View.GONE);
}
}
mActive.remove(convertView);
mActive.add(convertView);
return convertView;
}
@Override
public Filter getFilter() {
return mFilter;
}
@Override
public void onMovedToScrapHeap(View view) {
mActive.remove(view);
}
}
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setHasOptionsMenu(true);
mApplicationsState = ApplicationsState.getInstance(getActivity().getApplication());
Intent intent = getActivity().getIntent();
String action = intent.getAction();
int defaultListType = LIST_TYPE_DOWNLOADED;
String className = getArguments() != null
? getArguments().getString("classname") : null;
if (className == null) {
className = intent.getComponent().getClassName();
}
if (className.equals(RunningServicesActivity.class.getName())
|| className.endsWith(".RunningServices")) {
defaultListType = LIST_TYPE_RUNNING;
} else if (className.equals(StorageUseActivity.class.getName())
|| Intent.ACTION_MANAGE_PACKAGE_STORAGE.equals(action)
|| className.endsWith(".StorageUse")) {
mSortOrder = SORT_ORDER_SIZE;
defaultListType = LIST_TYPE_ALL;
} else if (Settings.ACTION_MANAGE_ALL_APPLICATIONS_SETTINGS.equals(action)) {
// Select the all-apps list, with the default sorting
defaultListType = LIST_TYPE_ALL;
}
if (savedInstanceState != null) {
mSortOrder = savedInstanceState.getInt(EXTRA_SORT_ORDER, mSortOrder);
int tmp = savedInstanceState.getInt(EXTRA_DEFAULT_LIST_TYPE, -1);
if (tmp != -1) defaultListType = tmp;
mShowBackground = savedInstanceState.getBoolean(EXTRA_SHOW_BACKGROUND, false);
}
mDefaultListType = defaultListType;
final Intent containerIntent = new Intent().setComponent(
StorageMeasurement.DEFAULT_CONTAINER_COMPONENT);
getActivity().bindService(containerIntent, mContainerConnection, Context.BIND_AUTO_CREATE);
mInvalidSizeStr = getActivity().getText(R.string.invalid_size_value);
mComputingSizeStr = getActivity().getText(R.string.computing_size);
TabInfo tab = new TabInfo(this, mApplicationsState,
getActivity().getString(R.string.filter_apps_third_party),
LIST_TYPE_DOWNLOADED, this, savedInstanceState);
mTabs.add(tab);
if (!Environment.isExternalStorageEmulated()) {
tab = new TabInfo(this, mApplicationsState,
getActivity().getString(R.string.filter_apps_onsdcard),
LIST_TYPE_SDCARD, this, savedInstanceState);
mTabs.add(tab);
}
tab = new TabInfo(this, mApplicationsState,
getActivity().getString(R.string.filter_apps_running),
LIST_TYPE_RUNNING, this, savedInstanceState);
mTabs.add(tab);
tab = new TabInfo(this, mApplicationsState,
getActivity().getString(R.string.filter_apps_all),
LIST_TYPE_ALL, this, savedInstanceState);
mTabs.add(tab);
}
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
// initialize the inflater
mInflater = inflater;
View rootView = mInflater.inflate(R.layout.manage_applications_content,
container, false);
mContentContainer = container;
mRootView = rootView;
mViewPager = (ViewPager) rootView.findViewById(R.id.pager);
MyPagerAdapter adapter = new MyPagerAdapter();
mViewPager.setAdapter(adapter);
mViewPager.setOnPageChangeListener(adapter);
PagerTabStrip tabs = (PagerTabStrip) rootView.findViewById(R.id.tabs);
tabs.setTabIndicatorColorResource(android.R.color.holo_blue_light);
// 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;
}
if (savedInstanceState != null && savedInstanceState.getBoolean(EXTRA_RESET_DIALOG)) {
buildResetDialog();
}
if (savedInstanceState == null) {
// First time init: make sure view pager is showing the correct tab.
for (int i = 0; i < mTabs.size(); i++) {
TabInfo tab = mTabs.get(i);
if (tab.mListType == mDefaultListType) {
mViewPager.setCurrentItem(i);
break;
}
}
}
return rootView;
}
@Override
public void onStart() {
super.onStart();
}
@Override
public void onResume() {
super.onResume();
mActivityResumed = true;
updateCurrentTab(mViewPager.getCurrentItem());
updateOptionsMenu();
}
@Override
public void onSaveInstanceState(Bundle outState) {
super.onSaveInstanceState(outState);
outState.putInt(EXTRA_SORT_ORDER, mSortOrder);
if (mDefaultListType != -1) {
outState.putInt(EXTRA_DEFAULT_LIST_TYPE, mDefaultListType);
}
outState.putBoolean(EXTRA_SHOW_BACKGROUND, mShowBackground);
if (mResetDialog != null) {
outState.putBoolean(EXTRA_RESET_DIALOG, true);
}
}
@Override
public void onPause() {
super.onPause();
mActivityResumed = false;
for (int i=0; i<mTabs.size(); i++) {
mTabs.get(i).pause();
}
}
@Override
public void onStop() {
super.onStop();
if (mResetDialog != null) {
mResetDialog.dismiss();
mResetDialog = null;
}
}
@Override
public void onDestroyView() {
super.onDestroyView();
// We are going to keep the tab data structures around, but they
// are no longer attached to their view hierarchy.
for (int i=0; i<mTabs.size(); i++) {
mTabs.get(i).detachView();
}
}
@Override
public void onActivityResult(int requestCode, int resultCode, Intent data) {
if (requestCode == INSTALLED_APP_DETAILS && mCurrentPkgName != null) {
mApplicationsState.requestSize(mCurrentPkgName);
}
}
TabInfo tabForType(int type) {
for (int i = 0; i < mTabs.size(); i++) {
TabInfo tab = mTabs.get(i);
if (tab.mListType == type) {
return tab;
}
}
return null;
}
// utility method used to start sub activity
private void startApplicationDetailsActivity() {
// start new fragment to display extended information
Bundle args = new Bundle();
args.putString(InstalledAppDetails.ARG_PACKAGE_NAME, mCurrentPkgName);
PreferenceActivity pa = (PreferenceActivity)getActivity();
pa.startPreferencePanel(InstalledAppDetails.class.getName(), args,
R.string.application_info_label, null, this, INSTALLED_APP_DETAILS);
}
@Override
public void onCreateOptionsMenu(Menu menu, MenuInflater inflater) {
mOptionsMenu = menu;
// note: icons removed for now because the cause the new action
// bar UI to be very confusing.
menu.add(0, SORT_ORDER_ALPHA, 1, R.string.sort_order_alpha)
//.setIcon(android.R.drawable.ic_menu_sort_alphabetically)
.setShowAsAction(MenuItem.SHOW_AS_ACTION_NEVER);
menu.add(0, SORT_ORDER_SIZE, 2, R.string.sort_order_size)
//.setIcon(android.R.drawable.ic_menu_sort_by_size)
.setShowAsAction(MenuItem.SHOW_AS_ACTION_NEVER);
menu.add(0, SHOW_RUNNING_SERVICES, 3, R.string.show_running_services)
.setShowAsAction(MenuItem.SHOW_AS_ACTION_IF_ROOM);
menu.add(0, SHOW_BACKGROUND_PROCESSES, 3, R.string.show_background_processes)
.setShowAsAction(MenuItem.SHOW_AS_ACTION_IF_ROOM);
menu.add(0, RESET_APP_PREFERENCES, 4, R.string.reset_app_preferences)
.setShowAsAction(MenuItem.SHOW_AS_ACTION_NEVER);
updateOptionsMenu();
}
@Override
public void onPrepareOptionsMenu(Menu menu) {
updateOptionsMenu();
}
@Override
public void onDestroyOptionsMenu() {
mOptionsMenu = null;
}
@Override
public void onDestroy() {
getActivity().unbindService(mContainerConnection);
super.onDestroy();
}
void updateOptionsMenu() {
if (mOptionsMenu == null) {
return;
}
/*
* The running processes screen doesn't use the mApplicationsAdapter
* so bringing up this menu in that case doesn't make any sense.
*/
if (mCurTab != null && mCurTab.mListType == LIST_TYPE_RUNNING) {
TabInfo tab = tabForType(LIST_TYPE_RUNNING);
boolean showingBackground = tab != null && tab.mRunningProcessesView != null
? tab.mRunningProcessesView.mAdapter.getShowBackground() : false;
mOptionsMenu.findItem(SORT_ORDER_ALPHA).setVisible(false);
mOptionsMenu.findItem(SORT_ORDER_SIZE).setVisible(false);
mOptionsMenu.findItem(SHOW_RUNNING_SERVICES).setVisible(showingBackground);
mOptionsMenu.findItem(SHOW_BACKGROUND_PROCESSES).setVisible(!showingBackground);
mOptionsMenu.findItem(RESET_APP_PREFERENCES).setVisible(false);
} else {
mOptionsMenu.findItem(SORT_ORDER_ALPHA).setVisible(mSortOrder != SORT_ORDER_ALPHA);
mOptionsMenu.findItem(SORT_ORDER_SIZE).setVisible(mSortOrder != SORT_ORDER_SIZE);
mOptionsMenu.findItem(SHOW_RUNNING_SERVICES).setVisible(false);
mOptionsMenu.findItem(SHOW_BACKGROUND_PROCESSES).setVisible(false);
mOptionsMenu.findItem(RESET_APP_PREFERENCES).setVisible(true);
}
}
void buildResetDialog() {
if (mResetDialog == null) {
AlertDialog.Builder builder = new AlertDialog.Builder(getActivity());
builder.setTitle(R.string.reset_app_preferences_title);
builder.setMessage(R.string.reset_app_preferences_desc);
builder.setPositiveButton(R.string.reset_app_preferences_button, this);
builder.setNegativeButton(R.string.cancel, null);
mResetDialog = builder.show();
mResetDialog.setOnDismissListener(this);
}
}
@Override
public void onDismiss(DialogInterface dialog) {
if (mResetDialog == dialog) {
mResetDialog = null;
}
}
@Override
public void onClick(DialogInterface dialog, int which) {
if (mResetDialog == dialog) {
final PackageManager pm = getActivity().getPackageManager();
final INotificationManager nm = INotificationManager.Stub.asInterface(
ServiceManager.getService(Context.NOTIFICATION_SERVICE));
final NetworkPolicyManager npm = NetworkPolicyManager.from(getActivity());
final Handler handler = new Handler(getActivity().getMainLooper());
(new AsyncTask<Void, Void, Void>() {
@Override protected Void doInBackground(Void... params) {
List<ApplicationInfo> apps = pm.getInstalledApplications(
PackageManager.GET_DISABLED_COMPONENTS);
for (int i=0; i<apps.size(); i++) {
ApplicationInfo app = apps.get(i);
try {
if (DEBUG) Log.v(TAG, "Enabling notifications: " + app.packageName);
nm.setNotificationsEnabledForPackage(app.packageName, true);
} catch (android.os.RemoteException ex) {
}
if (DEBUG) Log.v(TAG, "Clearing preferred: " + app.packageName);
pm.clearPackagePreferredActivities(app.packageName);
if (!app.enabled) {
if (DEBUG) Log.v(TAG, "Enabling app: " + app.packageName);
if (pm.getApplicationEnabledSetting(app.packageName)
== PackageManager.COMPONENT_ENABLED_STATE_DISABLED_USER) {
pm.setApplicationEnabledSetting(app.packageName,
PackageManager.COMPONENT_ENABLED_STATE_DEFAULT,
PackageManager.DONT_KILL_APP);
}
}
}
// We should have cleared all of the preferred apps above;
// just in case some may be lingering, retrieve whatever is
// still set and remove it.
ArrayList<IntentFilter> filters = new ArrayList<IntentFilter>();
ArrayList<ComponentName> prefActivities = new ArrayList<ComponentName>();
pm.getPreferredActivities(filters, prefActivities, null);
for (int i=0; i<prefActivities.size(); i++) {
if (DEBUG) Log.v(TAG, "Clearing preferred: "
+ prefActivities.get(i).getPackageName());
pm.clearPackagePreferredActivities(prefActivities.get(i).getPackageName());
}
final int[] restrictedUids = npm.getUidsWithPolicy(
POLICY_REJECT_METERED_BACKGROUND);
final int currentUserId = ActivityManager.getCurrentUser();
for (int uid : restrictedUids) {
// Only reset for current user
if (UserHandle.getUserId(uid) == currentUserId) {
if (DEBUG) Log.v(TAG, "Clearing data policy: " + uid);
npm.setUidPolicy(uid, POLICY_NONE);
}
}
handler.post(new Runnable() {
@Override public void run() {
if (DEBUG) Log.v(TAG, "Done clearing");
if (getActivity() != null && mActivityResumed) {
if (DEBUG) Log.v(TAG, "Updating UI!");
for (int i=0; i<mTabs.size(); i++) {
TabInfo tab = mTabs.get(i);
if (tab.mApplications != null) {
tab.mApplications.pause();
}
}
if (mCurTab != null) {
mCurTab.resume(mSortOrder);
}
}
}
});
return null;
}
}).execute();
}
}
@Override
public boolean onOptionsItemSelected(MenuItem item) {
int menuId = item.getItemId();
if ((menuId == SORT_ORDER_ALPHA) || (menuId == SORT_ORDER_SIZE)) {
mSortOrder = menuId;
if (mCurTab != null && mCurTab.mApplications != null) {
mCurTab.mApplications.rebuild(mSortOrder);
}
} else if (menuId == SHOW_RUNNING_SERVICES) {
mShowBackground = false;
if (mCurTab != null && mCurTab.mRunningProcessesView != null) {
mCurTab.mRunningProcessesView.mAdapter.setShowBackground(false);
}
} else if (menuId == SHOW_BACKGROUND_PROCESSES) {
mShowBackground = true;
if (mCurTab != null && mCurTab.mRunningProcessesView != null) {
mCurTab.mRunningProcessesView.mAdapter.setShowBackground(true);
}
} else if (menuId == RESET_APP_PREFERENCES) {
buildResetDialog();
} else {
// Handle the home button
return false;
}
updateOptionsMenu();
return true;
}
public void onItemClick(TabInfo tab, AdapterView<?> parent, View view, int position,
long id) {
if (tab.mApplications != null && tab.mApplications.getCount() > position) {
ApplicationsState.AppEntry entry = tab.mApplications.getAppEntry(position);
mCurrentPkgName = entry.info.packageName;
startApplicationDetailsActivity();
}
}
public void updateCurrentTab(int position) {
TabInfo tab = mTabs.get(position);
mCurTab = tab;
// Put things in the correct paused/resumed state.
if (mActivityResumed) {
mCurTab.build(mInflater, mContentContainer, mRootView);
mCurTab.resume(mSortOrder);
} else {
mCurTab.pause();
}
for (int i=0; i<mTabs.size(); i++) {
TabInfo t = mTabs.get(i);
if (t != mCurTab) {
t.pause();
}
}
mCurTab.updateStorageUsage();
updateOptionsMenu();
final Activity host = getActivity();
if (host != null) {
host.invalidateOptionsMenu();
}
}
private volatile IMediaContainerService mContainerService;
private final ServiceConnection mContainerConnection = new ServiceConnection() {
@Override
public void onServiceConnected(ComponentName name, IBinder service) {
mContainerService = IMediaContainerService.Stub.asInterface(service);
for (int i=0; i<mTabs.size(); i++) {
mTabs.get(i).setContainerService(mContainerService);
}
}
@Override
public void onServiceDisconnected(ComponentName name) {
mContainerService = null;
}
};
}