1/*
2 * Copyright (C) 2006 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 *      http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17package com.android.settings.applications;
18
19import static android.net.NetworkPolicyManager.POLICY_NONE;
20import static android.net.NetworkPolicyManager.POLICY_REJECT_METERED_BACKGROUND;
21
22import android.app.Activity;
23import android.app.ActivityManager;
24import android.app.AlertDialog;
25import android.app.Fragment;
26import android.app.INotificationManager;
27import android.content.ComponentName;
28import android.content.Context;
29import android.content.DialogInterface;
30import android.content.Intent;
31import android.content.IntentFilter;
32import android.content.ServiceConnection;
33import android.content.pm.ApplicationInfo;
34import android.content.pm.IPackageManager;
35import android.content.pm.PackageInfo;
36import android.content.pm.PackageManager;
37import android.net.NetworkPolicyManager;
38import android.os.AsyncTask;
39import android.os.Bundle;
40import android.os.Environment;
41import android.os.Handler;
42import android.os.IBinder;
43import android.os.RemoteException;
44import android.os.ServiceManager;
45import android.os.UserHandle;
46import android.preference.PreferenceActivity;
47import android.preference.PreferenceFrameLayout;
48import android.provider.Settings;
49import android.support.v4.view.PagerAdapter;
50import android.support.v4.view.PagerTabStrip;
51import android.support.v4.view.ViewPager;
52import android.text.format.Formatter;
53import android.util.Log;
54import android.view.LayoutInflater;
55import android.view.Menu;
56import android.view.MenuInflater;
57import android.view.MenuItem;
58import android.view.View;
59import android.view.ViewGroup;
60import android.view.animation.AnimationUtils;
61import android.widget.AbsListView;
62import android.widget.AdapterView;
63import android.widget.AdapterView.OnItemClickListener;
64import android.widget.BaseAdapter;
65import android.widget.Filter;
66import android.widget.Filterable;
67import android.widget.ListView;
68import android.widget.TextView;
69
70import com.android.internal.app.IMediaContainerService;
71import com.android.internal.content.PackageHelper;
72import com.android.settings.R;
73import com.android.settings.Settings.RunningServicesActivity;
74import com.android.settings.Settings.StorageUseActivity;
75import com.android.settings.applications.ApplicationsState.AppEntry;
76import com.android.settings.deviceinfo.StorageMeasurement;
77import com.android.settings.Utils;
78
79import java.util.ArrayList;
80import java.util.Comparator;
81import java.util.List;
82
83final class CanBeOnSdCardChecker {
84    final IPackageManager mPm;
85    int mInstallLocation;
86
87    CanBeOnSdCardChecker() {
88        mPm = IPackageManager.Stub.asInterface(
89                ServiceManager.getService("package"));
90    }
91
92    void init() {
93        try {
94            mInstallLocation = mPm.getInstallLocation();
95        } catch (RemoteException e) {
96            Log.e("CanBeOnSdCardChecker", "Is Package Manager running?");
97            return;
98        }
99    }
100
101    boolean check(ApplicationInfo info) {
102        boolean canBe = false;
103        if ((info.flags & ApplicationInfo.FLAG_EXTERNAL_STORAGE) != 0) {
104            canBe = true;
105        } else {
106            if ((info.flags & ApplicationInfo.FLAG_SYSTEM) == 0) {
107                if (info.installLocation == PackageInfo.INSTALL_LOCATION_PREFER_EXTERNAL ||
108                        info.installLocation == PackageInfo.INSTALL_LOCATION_AUTO) {
109                    canBe = true;
110                } else if (info.installLocation
111                        == PackageInfo.INSTALL_LOCATION_UNSPECIFIED) {
112                    if (mInstallLocation == PackageHelper.APP_INSTALL_EXTERNAL) {
113                        // For apps with no preference and the default value set
114                        // to install on sdcard.
115                        canBe = true;
116                    }
117                }
118            }
119        }
120        return canBe;
121    }
122}
123
124interface AppClickListener {
125    void onItemClick(ManageApplications.TabInfo tab, AdapterView<?> parent,
126            View view, int position, long id);
127}
128
129/**
130 * Activity to pick an application that will be used to display installation information and
131 * options to uninstall/delete user data for system applications. This activity
132 * can be launched through Settings or via the ACTION_MANAGE_PACKAGE_STORAGE
133 * intent.
134 */
135public class ManageApplications extends Fragment implements
136        AppClickListener, DialogInterface.OnClickListener,
137        DialogInterface.OnDismissListener {
138
139    static final String TAG = "ManageApplications";
140    static final boolean DEBUG = false;
141
142    private static final String EXTRA_SORT_ORDER = "sortOrder";
143    private static final String EXTRA_SHOW_BACKGROUND = "showBackground";
144    private static final String EXTRA_DEFAULT_LIST_TYPE = "defaultListType";
145    private static final String EXTRA_RESET_DIALOG = "resetDialog";
146
147    // attributes used as keys when passing values to InstalledAppDetails activity
148    public static final String APP_CHG = "chg";
149
150    // constant value that can be used to check return code from sub activity.
151    private static final int INSTALLED_APP_DETAILS = 1;
152
153    public static final int SIZE_TOTAL = 0;
154    public static final int SIZE_INTERNAL = 1;
155    public static final int SIZE_EXTERNAL = 2;
156
157    // sort order that can be changed through the menu can be sorted alphabetically
158    // or size(descending)
159    private static final int MENU_OPTIONS_BASE = 0;
160    // Filter options used for displayed list of applications
161    public static final int FILTER_APPS_ALL = MENU_OPTIONS_BASE + 0;
162    public static final int FILTER_APPS_THIRD_PARTY = MENU_OPTIONS_BASE + 1;
163    public static final int FILTER_APPS_SDCARD = MENU_OPTIONS_BASE + 2;
164
165    public static final int SORT_ORDER_ALPHA = MENU_OPTIONS_BASE + 4;
166    public static final int SORT_ORDER_SIZE = MENU_OPTIONS_BASE + 5;
167    public static final int SHOW_RUNNING_SERVICES = MENU_OPTIONS_BASE + 6;
168    public static final int SHOW_BACKGROUND_PROCESSES = MENU_OPTIONS_BASE + 7;
169    public static final int RESET_APP_PREFERENCES = MENU_OPTIONS_BASE + 8;
170    // sort order
171    private int mSortOrder = SORT_ORDER_ALPHA;
172
173    private ApplicationsState mApplicationsState;
174
175    public static class TabInfo implements OnItemClickListener {
176        public final ManageApplications mOwner;
177        public final ApplicationsState mApplicationsState;
178        public final CharSequence mLabel;
179        public final int mListType;
180        public final int mFilter;
181        public final AppClickListener mClickListener;
182        public final CharSequence mInvalidSizeStr;
183        public final CharSequence mComputingSizeStr;
184        private final Bundle mSavedInstanceState;
185
186        public ApplicationsAdapter mApplications;
187        public LayoutInflater mInflater;
188        public View mRootView;
189
190        private IMediaContainerService mContainerService;
191
192        private View mLoadingContainer;
193
194        private View mListContainer;
195
196        // ListView used to display list
197        private ListView mListView;
198        // Custom view used to display running processes
199        private RunningProcessesView mRunningProcessesView;
200
201        private LinearColorBar mColorBar;
202        private TextView mStorageChartLabel;
203        private TextView mUsedStorageText;
204        private TextView mFreeStorageText;
205        private long mFreeStorage = 0, mAppStorage = 0, mTotalStorage = 0;
206        private long mLastUsedStorage, mLastAppStorage, mLastFreeStorage;
207
208        final Runnable mRunningProcessesAvail = new Runnable() {
209            public void run() {
210                handleRunningProcessesAvail();
211            }
212        };
213
214        public TabInfo(ManageApplications owner, ApplicationsState apps,
215                CharSequence label, int listType, AppClickListener clickListener,
216                Bundle savedInstanceState) {
217            mOwner = owner;
218            mApplicationsState = apps;
219            mLabel = label;
220            mListType = listType;
221            switch (listType) {
222                case LIST_TYPE_DOWNLOADED: mFilter = FILTER_APPS_THIRD_PARTY; break;
223                case LIST_TYPE_SDCARD: mFilter = FILTER_APPS_SDCARD; break;
224                default: mFilter = FILTER_APPS_ALL; break;
225            }
226            mClickListener = clickListener;
227            mInvalidSizeStr = owner.getActivity().getText(R.string.invalid_size_value);
228            mComputingSizeStr = owner.getActivity().getText(R.string.computing_size);
229            mSavedInstanceState = savedInstanceState;
230        }
231
232        public void setContainerService(IMediaContainerService containerService) {
233            mContainerService = containerService;
234            updateStorageUsage();
235        }
236
237        public View build(LayoutInflater inflater, ViewGroup contentParent, View contentChild) {
238            if (mRootView != null) {
239                return mRootView;
240            }
241
242            mInflater = inflater;
243            mRootView = inflater.inflate(mListType == LIST_TYPE_RUNNING
244                    ? R.layout.manage_applications_running
245                    : R.layout.manage_applications_apps, null);
246            mLoadingContainer = mRootView.findViewById(R.id.loading_container);
247            mLoadingContainer.setVisibility(View.VISIBLE);
248            mListContainer = mRootView.findViewById(R.id.list_container);
249            if (mListContainer != null) {
250                // Create adapter and list view here
251                View emptyView = mListContainer.findViewById(com.android.internal.R.id.empty);
252                ListView lv = (ListView) mListContainer.findViewById(android.R.id.list);
253                if (emptyView != null) {
254                    lv.setEmptyView(emptyView);
255                }
256                lv.setOnItemClickListener(this);
257                lv.setSaveEnabled(true);
258                lv.setItemsCanFocus(true);
259                lv.setTextFilterEnabled(true);
260                mListView = lv;
261                mApplications = new ApplicationsAdapter(mApplicationsState, this, mFilter);
262                mListView.setAdapter(mApplications);
263                mListView.setRecyclerListener(mApplications);
264                mColorBar = (LinearColorBar)mListContainer.findViewById(R.id.storage_color_bar);
265                mStorageChartLabel = (TextView)mListContainer.findViewById(R.id.storageChartLabel);
266                mUsedStorageText = (TextView)mListContainer.findViewById(R.id.usedStorageText);
267                mFreeStorageText = (TextView)mListContainer.findViewById(R.id.freeStorageText);
268                Utils.prepareCustomPreferencesList(contentParent, contentChild, mListView, false);
269                if (mFilter == FILTER_APPS_SDCARD) {
270                    mStorageChartLabel.setText(mOwner.getActivity().getText(
271                            R.string.sd_card_storage));
272                } else {
273                    mStorageChartLabel.setText(mOwner.getActivity().getText(
274                            R.string.internal_storage));
275                }
276                applyCurrentStorage();
277            }
278            mRunningProcessesView = (RunningProcessesView)mRootView.findViewById(
279                    R.id.running_processes);
280            if (mRunningProcessesView != null) {
281                mRunningProcessesView.doCreate(mSavedInstanceState);
282            }
283
284            return mRootView;
285        }
286
287        public void detachView() {
288            if (mRootView != null) {
289                ViewGroup group = (ViewGroup)mRootView.getParent();
290                if (group != null) {
291                    group.removeView(mRootView);
292                }
293            }
294        }
295
296        public void resume(int sortOrder) {
297            if (mApplications != null) {
298                mApplications.resume(sortOrder);
299            }
300            if (mRunningProcessesView != null) {
301                boolean haveData = mRunningProcessesView.doResume(mOwner, mRunningProcessesAvail);
302                if (haveData) {
303                    mRunningProcessesView.setVisibility(View.VISIBLE);
304                    mLoadingContainer.setVisibility(View.INVISIBLE);
305                } else {
306                    mLoadingContainer.setVisibility(View.VISIBLE);
307                }
308            }
309        }
310
311        public void pause() {
312            if (mApplications != null) {
313                mApplications.pause();
314            }
315            if (mRunningProcessesView != null) {
316                mRunningProcessesView.doPause();
317            }
318        }
319
320        void updateStorageUsage() {
321            // Make sure a callback didn't come at an inopportune time.
322            if (mOwner.getActivity() == null) return;
323            // Doesn't make sense for stuff that is not an app list.
324            if (mApplications == null) return;
325
326            mFreeStorage = 0;
327            mAppStorage = 0;
328            mTotalStorage = 0;
329
330            if (mFilter == FILTER_APPS_SDCARD) {
331                if (mContainerService != null) {
332                    try {
333                        final long[] stats = mContainerService.getFileSystemStats(
334                                Environment.getExternalStorageDirectory().getPath());
335                        mTotalStorage = stats[0];
336                        mFreeStorage = stats[1];
337                    } catch (RemoteException e) {
338                        Log.w(TAG, "Problem in container service", e);
339                    }
340                }
341
342                if (mApplications != null) {
343                    final int N = mApplications.getCount();
344                    for (int i=0; i<N; i++) {
345                        ApplicationsState.AppEntry ae = mApplications.getAppEntry(i);
346                        mAppStorage += ae.externalCodeSize + ae.externalDataSize
347                                + ae.externalCacheSize;
348                    }
349                }
350            } else {
351                if (mContainerService != null) {
352                    try {
353                        final long[] stats = mContainerService.getFileSystemStats(
354                                Environment.getDataDirectory().getPath());
355                        mTotalStorage = stats[0];
356                        mFreeStorage = stats[1];
357                    } catch (RemoteException e) {
358                        Log.w(TAG, "Problem in container service", e);
359                    }
360                }
361
362                final boolean emulatedStorage = Environment.isExternalStorageEmulated();
363                if (mApplications != null) {
364                    final int N = mApplications.getCount();
365                    for (int i=0; i<N; i++) {
366                        ApplicationsState.AppEntry ae = mApplications.getAppEntry(i);
367                        mAppStorage += ae.codeSize + ae.dataSize;
368                        if (emulatedStorage) {
369                            mAppStorage += ae.externalCodeSize + ae.externalDataSize;
370                        }
371                    }
372                }
373                mFreeStorage += mApplicationsState.sumCacheSizes();
374            }
375
376            applyCurrentStorage();
377        }
378
379        void applyCurrentStorage() {
380            // If view hierarchy is not yet created, no views to update.
381            if (mRootView == null) {
382                return;
383            }
384            if (mTotalStorage > 0) {
385                mColorBar.setRatios((mTotalStorage-mFreeStorage-mAppStorage)/(float)mTotalStorage,
386                        mAppStorage/(float)mTotalStorage, mFreeStorage/(float)mTotalStorage);
387                long usedStorage = mTotalStorage - mFreeStorage;
388                if (mLastUsedStorage != usedStorage) {
389                    mLastUsedStorage = usedStorage;
390                    String sizeStr = Formatter.formatShortFileSize(
391                            mOwner.getActivity(), usedStorage);
392                    mUsedStorageText.setText(mOwner.getActivity().getResources().getString(
393                            R.string.service_foreground_processes, sizeStr));
394                }
395                if (mLastFreeStorage != mFreeStorage) {
396                    mLastFreeStorage = mFreeStorage;
397                    String sizeStr = Formatter.formatShortFileSize(
398                            mOwner.getActivity(), mFreeStorage);
399                    mFreeStorageText.setText(mOwner.getActivity().getResources().getString(
400                            R.string.service_background_processes, sizeStr));
401                }
402            } else {
403                mColorBar.setRatios(0, 0, 0);
404                if (mLastUsedStorage != -1) {
405                    mLastUsedStorage = -1;
406                    mUsedStorageText.setText("");
407                }
408                if (mLastFreeStorage != -1) {
409                    mLastFreeStorage = -1;
410                    mFreeStorageText.setText("");
411                }
412            }
413        }
414
415        @Override
416        public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
417            mClickListener.onItemClick(this, parent, view, position, id);
418        }
419
420        void handleRunningProcessesAvail() {
421            mLoadingContainer.startAnimation(AnimationUtils.loadAnimation(
422                    mOwner.getActivity(), android.R.anim.fade_out));
423            mRunningProcessesView.startAnimation(AnimationUtils.loadAnimation(
424                    mOwner.getActivity(), android.R.anim.fade_in));
425            mRunningProcessesView.setVisibility(View.VISIBLE);
426            mLoadingContainer.setVisibility(View.GONE);
427        }
428    }
429    private final ArrayList<TabInfo> mTabs = new ArrayList<TabInfo>();
430    TabInfo mCurTab = null;
431
432    // Size resource used for packages whose size computation failed for some reason
433    CharSequence mInvalidSizeStr;
434    private CharSequence mComputingSizeStr;
435
436    // layout inflater object used to inflate views
437    private LayoutInflater mInflater;
438
439    private String mCurrentPkgName;
440
441    private Menu mOptionsMenu;
442
443    // These are for keeping track of activity and spinner switch state.
444    private boolean mActivityResumed;
445
446    static final int LIST_TYPE_DOWNLOADED = 0;
447    static final int LIST_TYPE_RUNNING = 1;
448    static final int LIST_TYPE_SDCARD = 2;
449    static final int LIST_TYPE_ALL = 3;
450
451    private boolean mShowBackground = false;
452
453    private int mDefaultListType = -1;
454
455    private ViewGroup mContentContainer;
456    private View mRootView;
457    private ViewPager mViewPager;
458
459    AlertDialog mResetDialog;
460
461    class MyPagerAdapter extends PagerAdapter
462            implements ViewPager.OnPageChangeListener {
463        int mCurPos = 0;
464
465        @Override
466        public int getCount() {
467            return mTabs.size();
468        }
469
470        @Override
471        public Object instantiateItem(ViewGroup container, int position) {
472            TabInfo tab = mTabs.get(position);
473            View root = tab.build(mInflater, mContentContainer, mRootView);
474            container.addView(root);
475            return root;
476        }
477
478        @Override
479        public void destroyItem(ViewGroup container, int position, Object object) {
480            container.removeView((View)object);
481        }
482
483        @Override
484        public boolean isViewFromObject(View view, Object object) {
485            return view == object;
486        }
487
488        @Override
489        public CharSequence getPageTitle(int position) {
490            return mTabs.get(position).mLabel;
491        }
492
493        @Override
494        public void onPageScrolled(int position, float positionOffset, int positionOffsetPixels) {
495        }
496
497        @Override
498        public void onPageSelected(int position) {
499            mCurPos = position;
500        }
501
502        @Override
503        public void onPageScrollStateChanged(int state) {
504            if (state == ViewPager.SCROLL_STATE_IDLE) {
505                updateCurrentTab(mCurPos);
506            }
507        }
508    }
509
510    /*
511     * Custom adapter implementation for the ListView
512     * This adapter maintains a map for each displayed application and its properties
513     * An index value on each AppInfo object indicates the correct position or index
514     * in the list. If the list gets updated dynamically when the user is viewing the list of
515     * applications, we need to return the correct index of position. This is done by mapping
516     * the getId methods via the package name into the internal maps and indices.
517     * The order of applications in the list is mirrored in mAppLocalList
518     */
519    static class ApplicationsAdapter extends BaseAdapter implements Filterable,
520            ApplicationsState.Callbacks, AbsListView.RecyclerListener {
521        private final ApplicationsState mState;
522        private final ApplicationsState.Session mSession;
523        private final TabInfo mTab;
524        private final Context mContext;
525        private final ArrayList<View> mActive = new ArrayList<View>();
526        private final int mFilterMode;
527        private ArrayList<ApplicationsState.AppEntry> mBaseEntries;
528        private ArrayList<ApplicationsState.AppEntry> mEntries;
529        private boolean mResumed;
530        private int mLastSortMode=-1;
531        private boolean mWaitingForData;
532        private int mWhichSize = SIZE_TOTAL;
533        CharSequence mCurFilterPrefix;
534
535        private Filter mFilter = new Filter() {
536            @Override
537            protected FilterResults performFiltering(CharSequence constraint) {
538                ArrayList<ApplicationsState.AppEntry> entries
539                        = applyPrefixFilter(constraint, mBaseEntries);
540                FilterResults fr = new FilterResults();
541                fr.values = entries;
542                fr.count = entries.size();
543                return fr;
544            }
545
546            @Override
547            protected void publishResults(CharSequence constraint, FilterResults results) {
548                mCurFilterPrefix = constraint;
549                mEntries = (ArrayList<ApplicationsState.AppEntry>)results.values;
550                notifyDataSetChanged();
551                mTab.updateStorageUsage();
552            }
553        };
554
555        public ApplicationsAdapter(ApplicationsState state, TabInfo tab, int filterMode) {
556            mState = state;
557            mSession = state.newSession(this);
558            mTab = tab;
559            mContext = tab.mOwner.getActivity();
560            mFilterMode = filterMode;
561        }
562
563        public void resume(int sort) {
564            if (DEBUG) Log.i(TAG, "Resume!  mResumed=" + mResumed);
565            if (!mResumed) {
566                mResumed = true;
567                mSession.resume();
568                mLastSortMode = sort;
569                rebuild(true);
570            } else {
571                rebuild(sort);
572            }
573        }
574
575        public void pause() {
576            if (mResumed) {
577                mResumed = false;
578                mSession.pause();
579            }
580        }
581
582        public void rebuild(int sort) {
583            if (sort == mLastSortMode) {
584                return;
585            }
586            mLastSortMode = sort;
587            rebuild(true);
588        }
589
590        public void rebuild(boolean eraseold) {
591            if (DEBUG) Log.i(TAG, "Rebuilding app list...");
592            ApplicationsState.AppFilter filterObj;
593            Comparator<AppEntry> comparatorObj;
594            boolean emulated = Environment.isExternalStorageEmulated();
595            if (emulated) {
596                mWhichSize = SIZE_TOTAL;
597            } else {
598                mWhichSize = SIZE_INTERNAL;
599            }
600            switch (mFilterMode) {
601                case FILTER_APPS_THIRD_PARTY:
602                    filterObj = ApplicationsState.THIRD_PARTY_FILTER;
603                    break;
604                case FILTER_APPS_SDCARD:
605                    filterObj = ApplicationsState.ON_SD_CARD_FILTER;
606                    if (!emulated) {
607                        mWhichSize = SIZE_EXTERNAL;
608                    }
609                    break;
610                default:
611                    filterObj = null;
612                    break;
613            }
614            switch (mLastSortMode) {
615                case SORT_ORDER_SIZE:
616                    switch (mWhichSize) {
617                        case SIZE_INTERNAL:
618                            comparatorObj = ApplicationsState.INTERNAL_SIZE_COMPARATOR;
619                            break;
620                        case SIZE_EXTERNAL:
621                            comparatorObj = ApplicationsState.EXTERNAL_SIZE_COMPARATOR;
622                            break;
623                        default:
624                            comparatorObj = ApplicationsState.SIZE_COMPARATOR;
625                            break;
626                    }
627                    break;
628                default:
629                    comparatorObj = ApplicationsState.ALPHA_COMPARATOR;
630                    break;
631            }
632            ArrayList<ApplicationsState.AppEntry> entries
633                    = mSession.rebuild(filterObj, comparatorObj);
634            if (entries == null && !eraseold) {
635                // Don't have new list yet, but can continue using the old one.
636                return;
637            }
638            mBaseEntries = entries;
639            if (mBaseEntries != null) {
640                mEntries = applyPrefixFilter(mCurFilterPrefix, mBaseEntries);
641            } else {
642                mEntries = null;
643            }
644            notifyDataSetChanged();
645            mTab.updateStorageUsage();
646
647            if (entries == null) {
648                mWaitingForData = true;
649                mTab.mListContainer.setVisibility(View.INVISIBLE);
650                mTab.mLoadingContainer.setVisibility(View.VISIBLE);
651            } else {
652                mTab.mListContainer.setVisibility(View.VISIBLE);
653                mTab.mLoadingContainer.setVisibility(View.GONE);
654            }
655        }
656
657        ArrayList<ApplicationsState.AppEntry> applyPrefixFilter(CharSequence prefix,
658                ArrayList<ApplicationsState.AppEntry> origEntries) {
659            if (prefix == null || prefix.length() == 0) {
660                return origEntries;
661            } else {
662                String prefixStr = ApplicationsState.normalize(prefix.toString());
663                final String spacePrefixStr = " " + prefixStr;
664                ArrayList<ApplicationsState.AppEntry> newEntries
665                        = new ArrayList<ApplicationsState.AppEntry>();
666                for (int i=0; i<origEntries.size(); i++) {
667                    ApplicationsState.AppEntry entry = origEntries.get(i);
668                    String nlabel = entry.getNormalizedLabel();
669                    if (nlabel.startsWith(prefixStr) || nlabel.indexOf(spacePrefixStr) != -1) {
670                        newEntries.add(entry);
671                    }
672                }
673                return newEntries;
674            }
675        }
676
677        @Override
678        public void onRunningStateChanged(boolean running) {
679            mTab.mOwner.getActivity().setProgressBarIndeterminateVisibility(running);
680        }
681
682        @Override
683        public void onRebuildComplete(ArrayList<AppEntry> apps) {
684            if (mTab.mLoadingContainer.getVisibility() == View.VISIBLE) {
685                mTab.mLoadingContainer.startAnimation(AnimationUtils.loadAnimation(
686                        mContext, android.R.anim.fade_out));
687                mTab.mListContainer.startAnimation(AnimationUtils.loadAnimation(
688                        mContext, android.R.anim.fade_in));
689            }
690            mTab.mListContainer.setVisibility(View.VISIBLE);
691            mTab.mLoadingContainer.setVisibility(View.GONE);
692            mWaitingForData = false;
693            mBaseEntries = apps;
694            mEntries = applyPrefixFilter(mCurFilterPrefix, mBaseEntries);
695            notifyDataSetChanged();
696            mTab.updateStorageUsage();
697        }
698
699        @Override
700        public void onPackageListChanged() {
701            rebuild(false);
702        }
703
704        @Override
705        public void onPackageIconChanged() {
706            // We ensure icons are loaded when their item is displayed, so
707            // don't care about icons loaded in the background.
708        }
709
710        @Override
711        public void onPackageSizeChanged(String packageName) {
712            for (int i=0; i<mActive.size(); i++) {
713                AppViewHolder holder = (AppViewHolder)mActive.get(i).getTag();
714                if (holder.entry.info.packageName.equals(packageName)) {
715                    synchronized (holder.entry) {
716                        holder.updateSizeText(mTab.mInvalidSizeStr, mWhichSize);
717                    }
718                    if (holder.entry.info.packageName.equals(mTab.mOwner.mCurrentPkgName)
719                            && mLastSortMode == SORT_ORDER_SIZE) {
720                        // We got the size information for the last app the
721                        // user viewed, and are sorting by size...  they may
722                        // have cleared data, so we immediately want to resort
723                        // the list with the new size to reflect it to the user.
724                        rebuild(false);
725                    }
726                    mTab.updateStorageUsage();
727                    return;
728                }
729            }
730        }
731
732        @Override
733        public void onAllSizesComputed() {
734            if (mLastSortMode == SORT_ORDER_SIZE) {
735                rebuild(false);
736            }
737            mTab.updateStorageUsage();
738        }
739
740        public int getCount() {
741            return mEntries != null ? mEntries.size() : 0;
742        }
743
744        public Object getItem(int position) {
745            return mEntries.get(position);
746        }
747
748        public ApplicationsState.AppEntry getAppEntry(int position) {
749            return mEntries.get(position);
750        }
751
752        public long getItemId(int position) {
753            return mEntries.get(position).id;
754        }
755
756        public View getView(int position, View convertView, ViewGroup parent) {
757            // A ViewHolder keeps references to children views to avoid unnecessary calls
758            // to findViewById() on each row.
759            AppViewHolder holder = AppViewHolder.createOrRecycle(mTab.mInflater, convertView);
760            convertView = holder.rootView;
761
762            // Bind the data efficiently with the holder
763            ApplicationsState.AppEntry entry = mEntries.get(position);
764            synchronized (entry) {
765                holder.entry = entry;
766                if (entry.label != null) {
767                    holder.appName.setText(entry.label);
768                    holder.appName.setTextColor(mContext.getResources().getColorStateList(
769                            entry.info.enabled ? android.R.color.primary_text_dark
770                                    : android.R.color.secondary_text_dark));
771                }
772                mState.ensureIcon(entry);
773                if (entry.icon != null) {
774                    holder.appIcon.setImageDrawable(entry.icon);
775                }
776                holder.updateSizeText(mTab.mInvalidSizeStr, mWhichSize);
777                if ((entry.info.flags&ApplicationInfo.FLAG_INSTALLED) == 0) {
778                    holder.disabled.setVisibility(View.VISIBLE);
779                    holder.disabled.setText(R.string.not_installed);
780                } else if (!entry.info.enabled) {
781                    holder.disabled.setVisibility(View.VISIBLE);
782                    holder.disabled.setText(R.string.disabled);
783                } else {
784                    holder.disabled.setVisibility(View.GONE);
785                }
786                if (mFilterMode == FILTER_APPS_SDCARD) {
787                    holder.checkBox.setVisibility(View.VISIBLE);
788                    holder.checkBox.setChecked((entry.info.flags
789                            & ApplicationInfo.FLAG_EXTERNAL_STORAGE) != 0);
790                } else {
791                    holder.checkBox.setVisibility(View.GONE);
792                }
793            }
794            mActive.remove(convertView);
795            mActive.add(convertView);
796            return convertView;
797        }
798
799        @Override
800        public Filter getFilter() {
801            return mFilter;
802        }
803
804        @Override
805        public void onMovedToScrapHeap(View view) {
806            mActive.remove(view);
807        }
808    }
809
810    @Override
811    public void onCreate(Bundle savedInstanceState) {
812        super.onCreate(savedInstanceState);
813
814        setHasOptionsMenu(true);
815
816        mApplicationsState = ApplicationsState.getInstance(getActivity().getApplication());
817        Intent intent = getActivity().getIntent();
818        String action = intent.getAction();
819        int defaultListType = LIST_TYPE_DOWNLOADED;
820        String className = getArguments() != null
821                ? getArguments().getString("classname") : null;
822        if (className == null) {
823            className = intent.getComponent().getClassName();
824        }
825        if (className.equals(RunningServicesActivity.class.getName())
826                || className.endsWith(".RunningServices")) {
827            defaultListType = LIST_TYPE_RUNNING;
828        } else if (className.equals(StorageUseActivity.class.getName())
829                || Intent.ACTION_MANAGE_PACKAGE_STORAGE.equals(action)
830                || className.endsWith(".StorageUse")) {
831            mSortOrder = SORT_ORDER_SIZE;
832            defaultListType = LIST_TYPE_ALL;
833        } else if (Settings.ACTION_MANAGE_ALL_APPLICATIONS_SETTINGS.equals(action)) {
834            // Select the all-apps list, with the default sorting
835            defaultListType = LIST_TYPE_ALL;
836        }
837
838        if (savedInstanceState != null) {
839            mSortOrder = savedInstanceState.getInt(EXTRA_SORT_ORDER, mSortOrder);
840            int tmp = savedInstanceState.getInt(EXTRA_DEFAULT_LIST_TYPE, -1);
841            if (tmp != -1) defaultListType = tmp;
842            mShowBackground = savedInstanceState.getBoolean(EXTRA_SHOW_BACKGROUND, false);
843        }
844
845        mDefaultListType = defaultListType;
846
847        final Intent containerIntent = new Intent().setComponent(
848                StorageMeasurement.DEFAULT_CONTAINER_COMPONENT);
849        getActivity().bindService(containerIntent, mContainerConnection, Context.BIND_AUTO_CREATE);
850
851        mInvalidSizeStr = getActivity().getText(R.string.invalid_size_value);
852        mComputingSizeStr = getActivity().getText(R.string.computing_size);
853
854        TabInfo tab = new TabInfo(this, mApplicationsState,
855                getActivity().getString(R.string.filter_apps_third_party),
856                LIST_TYPE_DOWNLOADED, this, savedInstanceState);
857        mTabs.add(tab);
858
859        if (!Environment.isExternalStorageEmulated()) {
860            tab = new TabInfo(this, mApplicationsState,
861                    getActivity().getString(R.string.filter_apps_onsdcard),
862                    LIST_TYPE_SDCARD, this, savedInstanceState);
863            mTabs.add(tab);
864        }
865
866        tab = new TabInfo(this, mApplicationsState,
867                getActivity().getString(R.string.filter_apps_running),
868                LIST_TYPE_RUNNING, this, savedInstanceState);
869        mTabs.add(tab);
870
871        tab = new TabInfo(this, mApplicationsState,
872                getActivity().getString(R.string.filter_apps_all),
873                LIST_TYPE_ALL, this, savedInstanceState);
874        mTabs.add(tab);
875    }
876
877
878    @Override
879    public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
880        // initialize the inflater
881        mInflater = inflater;
882
883        View rootView = mInflater.inflate(R.layout.manage_applications_content,
884                container, false);
885        mContentContainer = container;
886        mRootView = rootView;
887
888        mViewPager = (ViewPager) rootView.findViewById(R.id.pager);
889        MyPagerAdapter adapter = new MyPagerAdapter();
890        mViewPager.setAdapter(adapter);
891        mViewPager.setOnPageChangeListener(adapter);
892        PagerTabStrip tabs = (PagerTabStrip) rootView.findViewById(R.id.tabs);
893        tabs.setTabIndicatorColorResource(android.R.color.holo_blue_light);
894
895        // We have to do this now because PreferenceFrameLayout looks at it
896        // only when the view is added.
897        if (container instanceof PreferenceFrameLayout) {
898            ((PreferenceFrameLayout.LayoutParams) rootView.getLayoutParams()).removeBorders = true;
899        }
900
901        if (savedInstanceState != null && savedInstanceState.getBoolean(EXTRA_RESET_DIALOG)) {
902            buildResetDialog();
903        }
904
905        if (savedInstanceState == null) {
906            // First time init: make sure view pager is showing the correct tab.
907            for (int i = 0; i < mTabs.size(); i++) {
908                TabInfo tab = mTabs.get(i);
909                if (tab.mListType == mDefaultListType) {
910                    mViewPager.setCurrentItem(i);
911                    break;
912                }
913            }
914        }
915
916        return rootView;
917    }
918
919    @Override
920    public void onStart() {
921        super.onStart();
922    }
923
924    @Override
925    public void onResume() {
926        super.onResume();
927        mActivityResumed = true;
928        updateCurrentTab(mViewPager.getCurrentItem());
929        updateOptionsMenu();
930    }
931
932    @Override
933    public void onSaveInstanceState(Bundle outState) {
934        super.onSaveInstanceState(outState);
935        outState.putInt(EXTRA_SORT_ORDER, mSortOrder);
936        if (mDefaultListType != -1) {
937            outState.putInt(EXTRA_DEFAULT_LIST_TYPE, mDefaultListType);
938        }
939        outState.putBoolean(EXTRA_SHOW_BACKGROUND, mShowBackground);
940        if (mResetDialog != null) {
941            outState.putBoolean(EXTRA_RESET_DIALOG, true);
942        }
943    }
944
945    @Override
946    public void onPause() {
947        super.onPause();
948        mActivityResumed = false;
949        for (int i=0; i<mTabs.size(); i++) {
950            mTabs.get(i).pause();
951        }
952    }
953
954    @Override
955    public void onStop() {
956        super.onStop();
957        if (mResetDialog != null) {
958            mResetDialog.dismiss();
959            mResetDialog = null;
960        }
961    }
962
963    @Override
964    public void onDestroyView() {
965        super.onDestroyView();
966
967        // We are going to keep the tab data structures around, but they
968        // are no longer attached to their view hierarchy.
969        for (int i=0; i<mTabs.size(); i++) {
970            mTabs.get(i).detachView();
971        }
972    }
973
974    @Override
975    public void onActivityResult(int requestCode, int resultCode, Intent data) {
976        if (requestCode == INSTALLED_APP_DETAILS && mCurrentPkgName != null) {
977            mApplicationsState.requestSize(mCurrentPkgName);
978        }
979    }
980
981    TabInfo tabForType(int type) {
982        for (int i = 0; i < mTabs.size(); i++) {
983            TabInfo tab = mTabs.get(i);
984            if (tab.mListType == type) {
985                return tab;
986            }
987        }
988        return null;
989    }
990
991    // utility method used to start sub activity
992    private void startApplicationDetailsActivity() {
993        // start new fragment to display extended information
994        Bundle args = new Bundle();
995        args.putString(InstalledAppDetails.ARG_PACKAGE_NAME, mCurrentPkgName);
996
997        PreferenceActivity pa = (PreferenceActivity)getActivity();
998        pa.startPreferencePanel(InstalledAppDetails.class.getName(), args,
999                R.string.application_info_label, null, this, INSTALLED_APP_DETAILS);
1000    }
1001
1002    @Override
1003    public void onCreateOptionsMenu(Menu menu, MenuInflater inflater) {
1004        mOptionsMenu = menu;
1005        // note: icons removed for now because the cause the new action
1006        // bar UI to be very confusing.
1007        menu.add(0, SORT_ORDER_ALPHA, 1, R.string.sort_order_alpha)
1008                //.setIcon(android.R.drawable.ic_menu_sort_alphabetically)
1009                .setShowAsAction(MenuItem.SHOW_AS_ACTION_NEVER);
1010        menu.add(0, SORT_ORDER_SIZE, 2, R.string.sort_order_size)
1011                //.setIcon(android.R.drawable.ic_menu_sort_by_size)
1012                .setShowAsAction(MenuItem.SHOW_AS_ACTION_NEVER);
1013        menu.add(0, SHOW_RUNNING_SERVICES, 3, R.string.show_running_services)
1014                .setShowAsAction(MenuItem.SHOW_AS_ACTION_IF_ROOM);
1015        menu.add(0, SHOW_BACKGROUND_PROCESSES, 3, R.string.show_background_processes)
1016                .setShowAsAction(MenuItem.SHOW_AS_ACTION_IF_ROOM);
1017        menu.add(0, RESET_APP_PREFERENCES, 4, R.string.reset_app_preferences)
1018                .setShowAsAction(MenuItem.SHOW_AS_ACTION_NEVER);
1019        updateOptionsMenu();
1020    }
1021
1022    @Override
1023    public void onPrepareOptionsMenu(Menu menu) {
1024        updateOptionsMenu();
1025    }
1026
1027    @Override
1028    public void onDestroyOptionsMenu() {
1029        mOptionsMenu = null;
1030    }
1031
1032    @Override
1033    public void onDestroy() {
1034        getActivity().unbindService(mContainerConnection);
1035        super.onDestroy();
1036    }
1037
1038    void updateOptionsMenu() {
1039        if (mOptionsMenu == null) {
1040            return;
1041        }
1042
1043        /*
1044         * The running processes screen doesn't use the mApplicationsAdapter
1045         * so bringing up this menu in that case doesn't make any sense.
1046         */
1047        if (mCurTab != null && mCurTab.mListType == LIST_TYPE_RUNNING) {
1048            TabInfo tab = tabForType(LIST_TYPE_RUNNING);
1049            boolean showingBackground = tab != null && tab.mRunningProcessesView != null
1050                    ? tab.mRunningProcessesView.mAdapter.getShowBackground() : false;
1051            mOptionsMenu.findItem(SORT_ORDER_ALPHA).setVisible(false);
1052            mOptionsMenu.findItem(SORT_ORDER_SIZE).setVisible(false);
1053            mOptionsMenu.findItem(SHOW_RUNNING_SERVICES).setVisible(showingBackground);
1054            mOptionsMenu.findItem(SHOW_BACKGROUND_PROCESSES).setVisible(!showingBackground);
1055            mOptionsMenu.findItem(RESET_APP_PREFERENCES).setVisible(false);
1056        } else {
1057            mOptionsMenu.findItem(SORT_ORDER_ALPHA).setVisible(mSortOrder != SORT_ORDER_ALPHA);
1058            mOptionsMenu.findItem(SORT_ORDER_SIZE).setVisible(mSortOrder != SORT_ORDER_SIZE);
1059            mOptionsMenu.findItem(SHOW_RUNNING_SERVICES).setVisible(false);
1060            mOptionsMenu.findItem(SHOW_BACKGROUND_PROCESSES).setVisible(false);
1061            mOptionsMenu.findItem(RESET_APP_PREFERENCES).setVisible(true);
1062        }
1063    }
1064
1065    void buildResetDialog() {
1066        if (mResetDialog == null) {
1067            AlertDialog.Builder builder = new AlertDialog.Builder(getActivity());
1068            builder.setTitle(R.string.reset_app_preferences_title);
1069            builder.setMessage(R.string.reset_app_preferences_desc);
1070            builder.setPositiveButton(R.string.reset_app_preferences_button, this);
1071            builder.setNegativeButton(R.string.cancel, null);
1072            mResetDialog = builder.show();
1073            mResetDialog.setOnDismissListener(this);
1074        }
1075    }
1076
1077    @Override
1078    public void onDismiss(DialogInterface dialog) {
1079        if (mResetDialog == dialog) {
1080            mResetDialog = null;
1081        }
1082    }
1083
1084
1085    @Override
1086    public void onClick(DialogInterface dialog, int which) {
1087        if (mResetDialog == dialog) {
1088            final PackageManager pm = getActivity().getPackageManager();
1089            final INotificationManager nm = INotificationManager.Stub.asInterface(
1090                    ServiceManager.getService(Context.NOTIFICATION_SERVICE));
1091            final NetworkPolicyManager npm = NetworkPolicyManager.from(getActivity());
1092            final Handler handler = new Handler(getActivity().getMainLooper());
1093            (new AsyncTask<Void, Void, Void>() {
1094                @Override protected Void doInBackground(Void... params) {
1095                    List<ApplicationInfo> apps = pm.getInstalledApplications(
1096                            PackageManager.GET_DISABLED_COMPONENTS);
1097                    for (int i=0; i<apps.size(); i++) {
1098                        ApplicationInfo app = apps.get(i);
1099                        try {
1100                            if (DEBUG) Log.v(TAG, "Enabling notifications: " + app.packageName);
1101                            nm.setNotificationsEnabledForPackage(app.packageName, true);
1102                        } catch (android.os.RemoteException ex) {
1103                        }
1104                        if (DEBUG) Log.v(TAG, "Clearing preferred: " + app.packageName);
1105                        pm.clearPackagePreferredActivities(app.packageName);
1106                        if (!app.enabled) {
1107                            if (DEBUG) Log.v(TAG, "Enabling app: " + app.packageName);
1108                            if (pm.getApplicationEnabledSetting(app.packageName)
1109                                    == PackageManager.COMPONENT_ENABLED_STATE_DISABLED_USER) {
1110                                pm.setApplicationEnabledSetting(app.packageName,
1111                                        PackageManager.COMPONENT_ENABLED_STATE_DEFAULT,
1112                                        PackageManager.DONT_KILL_APP);
1113                            }
1114                        }
1115                    }
1116                    // We should have cleared all of the preferred apps above;
1117                    // just in case some may be lingering, retrieve whatever is
1118                    // still set and remove it.
1119                    ArrayList<IntentFilter> filters = new ArrayList<IntentFilter>();
1120                    ArrayList<ComponentName> prefActivities = new ArrayList<ComponentName>();
1121                    pm.getPreferredActivities(filters, prefActivities, null);
1122                    for (int i=0; i<prefActivities.size(); i++) {
1123                        if (DEBUG) Log.v(TAG, "Clearing preferred: "
1124                                + prefActivities.get(i).getPackageName());
1125                        pm.clearPackagePreferredActivities(prefActivities.get(i).getPackageName());
1126                    }
1127                    final int[] restrictedUids = npm.getUidsWithPolicy(
1128                            POLICY_REJECT_METERED_BACKGROUND);
1129                    final int currentUserId = ActivityManager.getCurrentUser();
1130                    for (int uid : restrictedUids) {
1131                        // Only reset for current user
1132                        if (UserHandle.getUserId(uid) == currentUserId) {
1133                            if (DEBUG) Log.v(TAG, "Clearing data policy: " + uid);
1134                            npm.setUidPolicy(uid, POLICY_NONE);
1135                        }
1136                    }
1137                    handler.post(new Runnable() {
1138                        @Override public void run() {
1139                            if (DEBUG) Log.v(TAG, "Done clearing");
1140                            if (getActivity() != null && mActivityResumed) {
1141                                if (DEBUG) Log.v(TAG, "Updating UI!");
1142                                for (int i=0; i<mTabs.size(); i++) {
1143                                    TabInfo tab = mTabs.get(i);
1144                                    if (tab.mApplications != null) {
1145                                        tab.mApplications.pause();
1146                                    }
1147                                }
1148                                if (mCurTab != null) {
1149                                    mCurTab.resume(mSortOrder);
1150                                }
1151                            }
1152                        }
1153                    });
1154                    return null;
1155                }
1156            }).execute();
1157        }
1158    }
1159
1160    @Override
1161    public boolean onOptionsItemSelected(MenuItem item) {
1162        int menuId = item.getItemId();
1163        if ((menuId == SORT_ORDER_ALPHA) || (menuId == SORT_ORDER_SIZE)) {
1164            mSortOrder = menuId;
1165            if (mCurTab != null && mCurTab.mApplications != null) {
1166                mCurTab.mApplications.rebuild(mSortOrder);
1167            }
1168        } else if (menuId == SHOW_RUNNING_SERVICES) {
1169            mShowBackground = false;
1170            if (mCurTab != null && mCurTab.mRunningProcessesView != null) {
1171                mCurTab.mRunningProcessesView.mAdapter.setShowBackground(false);
1172            }
1173        } else if (menuId == SHOW_BACKGROUND_PROCESSES) {
1174            mShowBackground = true;
1175            if (mCurTab != null && mCurTab.mRunningProcessesView != null) {
1176                mCurTab.mRunningProcessesView.mAdapter.setShowBackground(true);
1177            }
1178        } else if (menuId == RESET_APP_PREFERENCES) {
1179            buildResetDialog();
1180        } else {
1181            // Handle the home button
1182            return false;
1183        }
1184        updateOptionsMenu();
1185        return true;
1186    }
1187
1188    public void onItemClick(TabInfo tab, AdapterView<?> parent, View view, int position,
1189            long id) {
1190        if (tab.mApplications != null && tab.mApplications.getCount() > position) {
1191            ApplicationsState.AppEntry entry = tab.mApplications.getAppEntry(position);
1192            mCurrentPkgName = entry.info.packageName;
1193            startApplicationDetailsActivity();
1194        }
1195    }
1196
1197    public void updateCurrentTab(int position) {
1198        TabInfo tab = mTabs.get(position);
1199        mCurTab = tab;
1200
1201        // Put things in the correct paused/resumed state.
1202        if (mActivityResumed) {
1203            mCurTab.build(mInflater, mContentContainer, mRootView);
1204            mCurTab.resume(mSortOrder);
1205        } else {
1206            mCurTab.pause();
1207        }
1208        for (int i=0; i<mTabs.size(); i++) {
1209            TabInfo t = mTabs.get(i);
1210            if (t != mCurTab) {
1211                t.pause();
1212            }
1213        }
1214
1215        mCurTab.updateStorageUsage();
1216        updateOptionsMenu();
1217        final Activity host = getActivity();
1218        if (host != null) {
1219            host.invalidateOptionsMenu();
1220        }
1221    }
1222
1223    private volatile IMediaContainerService mContainerService;
1224
1225    private final ServiceConnection mContainerConnection = new ServiceConnection() {
1226        @Override
1227        public void onServiceConnected(ComponentName name, IBinder service) {
1228            mContainerService = IMediaContainerService.Stub.asInterface(service);
1229            for (int i=0; i<mTabs.size(); i++) {
1230                mTabs.get(i).setContainerService(mContainerService);
1231            }
1232        }
1233
1234        @Override
1235        public void onServiceDisconnected(ComponentName name) {
1236            mContainerService = null;
1237        }
1238    };
1239}
1240