/* * 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.Collections; 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); mRootView.setLayoutDirection(mRootView.getResources().getConfiguration().getLayoutDirection()); 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); lv.setFastScrollEnabled(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); } 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) { container.setLayoutDirection(container.getResources().getConfiguration().getLayoutDirection()); // 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) { //Reverse the tab list once if the language is RTL. if(container.isLayoutRtl()){ Collections.reverse(mTabs); } // 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); mShowBackground = showingBackground; } 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; } }; }