blob: 7ec01aa60e0621cad42d15d5cf3c4b49f48c7c05 [file] [log] [blame]
/*
* 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 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;
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;
case LIST_TYPE_DISABLED: mFilter = FILTER_APPS_DISABLED; 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>();
private int mNumTabs;
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;
static final int LIST_TYPE_DISABLED = 4;
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 mNumTabs;
}
@Override
public Object instantiateItem(ViewGroup container, int position) {
TabInfo tab = mTabs.get(position);
View root = tab.build(mInflater, mContentContainer, mRootView);
container.addView(root);
root.setTag(R.id.name, tab);
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 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;
}
@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;
case FILTER_APPS_DISABLED:
filterObj = ApplicationsState.DISABLED_FILTER;
break;
default:
filterObj = ApplicationsState.ALL_ENABLED_FILTER;
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);
}
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);
tab = new TabInfo(this, mApplicationsState,
getActivity().getString(R.string.filter_apps_disabled),
LIST_TYPE_DISABLED, this, savedInstanceState);
mTabs.add(tab);
mNumTabs = mTabs.size();
}
@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());
updateNumTabs();
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);
}
}
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);
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 IPackageManager mIPm = IPackageManager.Stub.asInterface(
ServiceManager.getService("package"));
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, app.uid, true);
} catch (android.os.RemoteException ex) {
}
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);
}
}
}
try {
mIPm.resetPreferredActivities(UserHandle.myUserId());
} catch (RemoteException e) {
}
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;
}
};
}