Rework manage applications to be page-able.

Turn everything around so that we can have multiple list views
with their own adapters.  Switch to using a ViewPager for managing
the different lists.  Smile!

Change-Id: I9c102abb06cf67f313a8696507aa4597b38c7ab9
diff --git a/Android.mk b/Android.mk
index c08be7f..38732bb 100644
--- a/Android.mk
+++ b/Android.mk
@@ -2,7 +2,7 @@
 include $(CLEAR_VARS)
 
 LOCAL_JAVA_LIBRARIES := bouncycastle
-LOCAL_STATIC_JAVA_LIBRARIES := guava
+LOCAL_STATIC_JAVA_LIBRARIES := guava android-support-v4
 
 LOCAL_MODULE_TAGS := optional
 
diff --git a/res/layout/manage_applications.xml b/res/layout/manage_applications_apps.xml
old mode 100755
new mode 100644
similarity index 93%
rename from res/layout/manage_applications.xml
rename to res/layout/manage_applications_apps.xml
index da56c99..51ab412
--- a/res/layout/manage_applications.xml
+++ b/res/layout/manage_applications_apps.xml
@@ -1,5 +1,5 @@
 <?xml version="1.0" encoding="utf-8"?>
-<!-- Copyright (C) 2008 The Android Open Source Project
+<!-- Copyright (C) 2012 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.
@@ -83,12 +83,6 @@
         </view>
     </LinearLayout>
 
-    <view class="com.android.settings.applications.RunningProcessesView"
-            android:id="@+id/running_processes"
-            android:layout_width="match_parent"
-            android:layout_height="match_parent"
-            android:visibility="gone" />
-
     <LinearLayout android:id="@+id/loading_container"
             android:orientation="vertical"
             android:layout_width="match_parent"
@@ -110,4 +104,4 @@
 
     </LinearLayout>
 
-</FrameLayout>
\ No newline at end of file
+</FrameLayout>
diff --git a/res/layout/manage_applications_content.xml b/res/layout/manage_applications_content.xml
new file mode 100644
index 0000000..f72aa03
--- /dev/null
+++ b/res/layout/manage_applications_content.xml
@@ -0,0 +1,41 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+/*
+**
+** Copyright 2012, 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.
+*/
+-->
+
+
+<LinearLayout
+    xmlns:android="http://schemas.android.com/apk/res/android"
+    android:layout_width="match_parent"
+    android:layout_height="match_parent"
+    android:orientation="vertical">
+
+    <android.support.v4.view.ViewPager
+            android:id="@+id/pager"
+            android:layout_width="match_parent"
+            android:layout_height="match_parent"
+            android:layout_weight="1">
+        <android.support.v4.view.PagerTabStrip
+                android:id="@+id/tabs"
+                android:layout_width="match_parent"
+                android:layout_height="wrap_content"
+                android:layout_gravity="top">
+        </android.support.v4.view.PagerTabStrip>
+    </android.support.v4.view.ViewPager>
+
+</LinearLayout>
diff --git a/res/layout/manage_applications_running.xml b/res/layout/manage_applications_running.xml
new file mode 100644
index 0000000..634de1c
--- /dev/null
+++ b/res/layout/manage_applications_running.xml
@@ -0,0 +1,47 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2012 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.
+-->
+
+<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
+    android:layout_width="match_parent"
+    android:layout_height="match_parent" >
+    <view class="com.android.settings.applications.RunningProcessesView"
+            android:id="@+id/running_processes"
+            android:layout_width="match_parent"
+            android:layout_height="match_parent"
+            android:visibility="gone" />
+
+    <LinearLayout android:id="@+id/loading_container"
+            android:orientation="vertical"
+            android:layout_width="match_parent"
+            android:layout_height="match_parent"
+            android:layout_marginLeft="@*android:dimen/preference_fragment_padding_side"
+            android:layout_marginRight="@*android:dimen/preference_fragment_padding_side"
+            android:visibility="gone"
+            android:gravity="center">
+
+        <ProgressBar style="?android:attr/progressBarStyleLarge"
+                android:layout_width="wrap_content"
+                android:layout_height="wrap_content" />
+        <TextView android:layout_width="wrap_content"
+                android:layout_height="wrap_content"
+                android:textAppearance="?android:attr/textAppearanceSmall"
+                android:text="@string/settings_safetylegal_activity_loading"
+                android:paddingTop="4dip"
+                android:singleLine="true" />
+
+    </LinearLayout>
+
+</FrameLayout>
diff --git a/res/layout/manage_apps_spinner_content.xml b/res/layout/manage_apps_spinner_content.xml
deleted file mode 100644
index cbf0a40..0000000
--- a/res/layout/manage_apps_spinner_content.xml
+++ /dev/null
@@ -1,51 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
-/*
-**
-** Copyright 2012, 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.
-*/
--->
-
-
-<LinearLayout
-    xmlns:android="http://schemas.android.com/apk/res/android"
-    android:layout_width="match_parent"
-    android:layout_height="match_parent"
-    android:orientation="vertical">
-
-    <Spinner
-        android:id="@+id/spinner"
-        android:layout_width="wrap_content"
-        android:layout_height="48sp"
-        android:minWidth="180dp"
-        android:layout_marginLeft="@*android:dimen/preference_fragment_padding_side"
-        android:layout_marginRight="@*android:dimen/preference_fragment_padding_side"
-        />
-
-    <ImageView
-        android:layout_width="match_parent"
-        android:layout_height="wrap_content"
-        android:layout_marginLeft="@*android:dimen/preference_fragment_padding_side"
-        android:layout_marginRight="@*android:dimen/preference_fragment_padding_side"
-        android:scaleType="fitXY"
-        android:src="?android:attr/listDivider" />
-
-    <FrameLayout
-        android:id="@+id/spinner_content"
-        android:layout_width="match_parent"
-        android:layout_height="0dip"
-        android:layout_weight="1" />
-
-</LinearLayout>
diff --git a/src/com/android/settings/applications/AppViewHolder.java b/src/com/android/settings/applications/AppViewHolder.java
index 2a12f9b..d23f187 100644
--- a/src/com/android/settings/applications/AppViewHolder.java
+++ b/src/com/android/settings/applications/AppViewHolder.java
@@ -42,7 +42,7 @@
         }
     }
 
-    void updateSizeText(ManageApplications ma, int whichSize) {
+    void updateSizeText(CharSequence invalidSizeStr, int whichSize) {
         if (ManageApplications.DEBUG) Log.i(ManageApplications.TAG, "updateSizeText of " + entry.label + " " + entry
                 + ": " + entry.sizeStr);
         if (entry.sizeStr != null) {
@@ -58,7 +58,7 @@
                     break;
             }
         } else if (entry.size == ApplicationsState.SIZE_INVALID) {
-            appSize.setText(ma.mInvalidSizeStr);
+            appSize.setText(invalidSizeStr);
         }
     }
 }
\ No newline at end of file
diff --git a/src/com/android/settings/applications/ApplicationsState.java b/src/com/android/settings/applications/ApplicationsState.java
index 799b34a..0a5c26e 100644
--- a/src/com/android/settings/applications/ApplicationsState.java
+++ b/src/com/android/settings/applications/ApplicationsState.java
@@ -227,24 +227,21 @@
     PackageIntentReceiver mPackageIntentReceiver;
 
     boolean mResumed;
-    Callbacks mCurCallbacks;
 
-    // Information about all applications.  Synchronize on mAppEntries
+    // Information about all applications.  Synchronize on mEntriesMap
     // to protect access to these.
+    final ArrayList<Session> mSessions = new ArrayList<Session>();
+    final ArrayList<Session> mRebuildingSessions = new ArrayList<Session>();
     final InterestingConfigChanges mInterestingConfigChanges = new InterestingConfigChanges();
     final HashMap<String, AppEntry> mEntriesMap = new HashMap<String, AppEntry>();
     final ArrayList<AppEntry> mAppEntries = new ArrayList<AppEntry>();
     List<ApplicationInfo> mApplications = new ArrayList<ApplicationInfo>();
     long mCurId = 1;
     String mCurComputingSizePkg;
+    boolean mSessionsChanged;
 
-    // Rebuilding of app list.  Synchronized on mRebuildSync.
-    final Object mRebuildSync = new Object();
-    boolean mRebuildRequested;
-    boolean mRebuildAsync;
-    AppFilter mRebuildFilter;
-    Comparator<AppEntry> mRebuildComparator;
-    ArrayList<AppEntry> mRebuildResult;
+    // Temporary for dispatching session callbacks.  Only touched by main thread.
+    final ArrayList<Session> mActiveSessions = new ArrayList<Session>();
 
     /**
      * Receives notifications when applications are added/removed.
@@ -262,6 +259,9 @@
              sdFilter.addAction(Intent.ACTION_EXTERNAL_APPLICATIONS_UNAVAILABLE);
              mContext.registerReceiver(this, sdFilter);
          }
+         void unregisterReceiver() {
+             mContext.unregisterReceiver(this);
+         }
          @Override
          public void onReceive(Context context, Intent intent) {
              String actionStr = intent.getAction();
@@ -300,6 +300,21 @@
          }
     }
 
+    void rebuildActiveSessions() {
+        synchronized (mEntriesMap) {
+            if (!mSessionsChanged) {
+                return;
+            }
+            mActiveSessions.clear();
+            for (int i=0; i<mSessions.size(); i++) {
+                Session s = mSessions.get(i);
+                if (s.mResumed) {
+                    mActiveSessions.add(s);
+                }
+            }
+        }
+    }
+
     class MainHandler extends Handler {
         static final int MSG_REBUILD_COMPLETE = 1;
         static final int MSG_PACKAGE_LIST_CHANGED = 2;
@@ -310,35 +325,39 @@
 
         @Override
         public void handleMessage(Message msg) {
+            rebuildActiveSessions();
             switch (msg.what) {
                 case MSG_REBUILD_COMPLETE: {
-                    if (mCurCallbacks != null) {
-                        mCurCallbacks.onRebuildComplete((ArrayList<AppEntry>)msg.obj);
+                    Session s = (Session)msg.obj;
+                    if (mActiveSessions.contains(s)) {
+                        s.mCallbacks.onRebuildComplete(s.mLastAppList);
                     }
                 } break;
                 case MSG_PACKAGE_LIST_CHANGED: {
-                    if (mCurCallbacks != null) {
-                        mCurCallbacks.onPackageListChanged();
+                    for (int i=0; i<mActiveSessions.size(); i++) {
+                        mActiveSessions.get(i).mCallbacks.onPackageListChanged();
                     }
                 } break;
                 case MSG_PACKAGE_ICON_CHANGED: {
-                    if (mCurCallbacks != null) {
-                        mCurCallbacks.onPackageIconChanged();
+                    for (int i=0; i<mActiveSessions.size(); i++) {
+                        mActiveSessions.get(i).mCallbacks.onPackageIconChanged();
                     }
                 } break;
                 case MSG_PACKAGE_SIZE_CHANGED: {
-                    if (mCurCallbacks != null) {
-                        mCurCallbacks.onPackageSizeChanged((String)msg.obj);
+                    for (int i=0; i<mActiveSessions.size(); i++) {
+                        mActiveSessions.get(i).mCallbacks.onPackageSizeChanged(
+                                (String)msg.obj);
                     }
                 } break;
                 case MSG_ALL_SIZES_COMPUTED: {
-                    if (mCurCallbacks != null) {
-                        mCurCallbacks.onAllSizesComputed();
+                    for (int i=0; i<mActiveSessions.size(); i++) {
+                        mActiveSessions.get(i).mCallbacks.onAllSizesComputed();
                     }
                 } break;
                 case MSG_RUNNING_STATE_CHANGED: {
-                    if (mCurCallbacks != null) {
-                        mCurCallbacks.onRunningStateChanged(msg.arg1 != 0);
+                    for (int i=0; i<mActiveSessions.size(); i++) {
+                        mActiveSessions.get(i).mCallbacks.onRunningStateChanged(
+                                msg.arg1 != 0);
                     }
                 } break;
             }
@@ -391,157 +410,226 @@
         }
     }
 
-    void resume(Callbacks callbacks) {
-        if (DEBUG_LOCKING) Log.v(TAG, "resume about to acquire lock...");
-        synchronized (mEntriesMap) {
-            mCurCallbacks = callbacks;
-            mResumed = true;
-            if (mPackageIntentReceiver == null) {
-                mPackageIntentReceiver = new PackageIntentReceiver();
-                mPackageIntentReceiver.registerReceiver();
-            }
-            mApplications = mPm.getInstalledApplications(
-                    PackageManager.GET_UNINSTALLED_PACKAGES |
-                    PackageManager.GET_DISABLED_COMPONENTS);
-            if (mApplications == null) {
-                mApplications = new ArrayList<ApplicationInfo>();
-            }
+    public class Session {
+        final Callbacks mCallbacks;
+        boolean mResumed;
 
-            if (mInterestingConfigChanges.applyNewConfig(mContext.getResources())) {
-                // If an interesting part of the configuration has changed, we
-                // should completely reload the app entries.
-                mEntriesMap.clear();
-                mAppEntries.clear();
-            } else {
-                for (int i=0; i<mAppEntries.size(); i++) {
-                    mAppEntries.get(i).sizeStale = true;
-                }
-            }
+        // Rebuilding of app list.  Synchronized on mRebuildSync.
+        final Object mRebuildSync = new Object();
+        boolean mRebuildRequested;
+        boolean mRebuildAsync;
+        AppFilter mRebuildFilter;
+        Comparator<AppEntry> mRebuildComparator;
+        ArrayList<AppEntry> mRebuildResult;
+        ArrayList<AppEntry> mLastAppList;
 
-            for (int i=0; i<mApplications.size(); i++) {
-                final ApplicationInfo info = mApplications.get(i);
-                // Need to trim out any applications that are disabled by
-                // something different than the user.
-                if (!info.enabled && info.enabledSetting
-                        != PackageManager.COMPONENT_ENABLED_STATE_DISABLED_USER) {
-                    mApplications.remove(i);
-                    i--;
-                    continue;
+        Session(Callbacks callbacks) {
+            mCallbacks = callbacks;
+        }
+
+        public void resume() {
+            if (DEBUG_LOCKING) Log.v(TAG, "resume about to acquire lock...");
+            synchronized (mEntriesMap) {
+                if (!mResumed) {
+                    mResumed = true;
+                    mSessionsChanged = true;
+                    doResumeIfNeededLocked();
                 }
-                final AppEntry entry = mEntriesMap.get(info.packageName);
-                if (entry != null) {
-                    entry.info = info;
-                }
-            }
-            mCurComputingSizePkg = null;
-            if (!mBackgroundHandler.hasMessages(BackgroundHandler.MSG_LOAD_ENTRIES)) {
-                mBackgroundHandler.sendEmptyMessage(BackgroundHandler.MSG_LOAD_ENTRIES);
             }
             if (DEBUG_LOCKING) Log.v(TAG, "...resume releasing lock");
         }
-    }
 
-    void pause() {
-        if (DEBUG_LOCKING) Log.v(TAG, "pause about to acquire lock...");
-        synchronized (mEntriesMap) {
-            mCurCallbacks = null;
-            mResumed = false;
-            if (DEBUG_LOCKING) Log.v(TAG, "...pause releasing lock");
-        }
-    }
-
-    // Creates a new list of app entries with the given filter and comparator.
-    ArrayList<AppEntry> rebuild(AppFilter filter, Comparator<AppEntry> comparator) {
-        synchronized (mRebuildSync) {
-            mRebuildRequested = true;
-            mRebuildAsync = false;
-            mRebuildFilter = filter;
-            mRebuildComparator = comparator;
-            mRebuildResult = null;
-            if (!mBackgroundHandler.hasMessages(BackgroundHandler.MSG_REBUILD_LIST)) {
-                mBackgroundHandler.sendEmptyMessage(BackgroundHandler.MSG_REBUILD_LIST);
-            }
-
-            // We will wait for .25s for the list to be built.
-            long waitend = SystemClock.uptimeMillis()+250;
-
-            while (mRebuildResult == null) {
-                long now = SystemClock.uptimeMillis();
-                if (now >= waitend) {
-                    break;
+        public void pause() {
+            if (DEBUG_LOCKING) Log.v(TAG, "pause about to acquire lock...");
+            synchronized (mEntriesMap) {
+                if (mResumed) {
+                    mResumed = false;
+                    mSessionsChanged = true;
+                    mBackgroundHandler.removeMessages(BackgroundHandler.MSG_REBUILD_LIST, this);
+                    doPauseIfNeededLocked();
                 }
-                try {
-                    mRebuildSync.wait(waitend - now);
-                } catch (InterruptedException e) {
-                }
+                if (DEBUG_LOCKING) Log.v(TAG, "...pause releasing lock");
             }
-
-            mRebuildAsync = true;
-
-            return mRebuildResult;
-        }
-    }
-
-    void handleRebuildList() {
-        AppFilter filter;
-        Comparator<AppEntry> comparator;
-        synchronized (mRebuildSync) {
-            if (!mRebuildRequested) {
-                return;
-            }
-
-            filter = mRebuildFilter;
-            comparator = mRebuildComparator;
-            mRebuildRequested = false;
-            mRebuildFilter = null;
-            mRebuildComparator = null;
         }
 
-        Process.setThreadPriority(Process.THREAD_PRIORITY_FOREGROUND);
-
-        if (filter != null) {
-            filter.init();
-        }
-        
-        List<ApplicationInfo> apps;
-        synchronized (mEntriesMap) {
-            apps = new ArrayList<ApplicationInfo>(mApplications);
-        }
-
-        ArrayList<AppEntry> filteredApps = new ArrayList<AppEntry>();
-        if (DEBUG) Log.i(TAG, "Rebuilding...");
-        for (int i=0; i<apps.size(); i++) {
-            ApplicationInfo info = apps.get(i);
-            if (filter == null || filter.filterApp(info)) {
+        // Creates a new list of app entries with the given filter and comparator.
+        ArrayList<AppEntry> rebuild(AppFilter filter, Comparator<AppEntry> comparator) {
+            synchronized (mRebuildSync) {
                 synchronized (mEntriesMap) {
-                    if (DEBUG_LOCKING) Log.v(TAG, "rebuild acquired lock");
-                    AppEntry entry = getEntryLocked(info);
-                    entry.ensureLabel(mContext);
-                    if (DEBUG) Log.i(TAG, "Using " + info.packageName + ": " + entry);
-                    filteredApps.add(entry);
-                    if (DEBUG_LOCKING) Log.v(TAG, "rebuild releasing lock");
+                    mRebuildingSessions.add(this);
+                    mRebuildRequested = true;
+                    mRebuildAsync = false;
+                    mRebuildFilter = filter;
+                    mRebuildComparator = comparator;
+                    mRebuildResult = null;
+                    if (!mBackgroundHandler.hasMessages(BackgroundHandler.MSG_REBUILD_LIST)) {
+                        Message msg = mBackgroundHandler.obtainMessage(
+                                BackgroundHandler.MSG_REBUILD_LIST);
+                        mBackgroundHandler.sendMessage(msg);
+                    }
                 }
+
+                // We will wait for .25s for the list to be built.
+                long waitend = SystemClock.uptimeMillis()+250;
+
+                while (mRebuildResult == null) {
+                    long now = SystemClock.uptimeMillis();
+                    if (now >= waitend) {
+                        break;
+                    }
+                    try {
+                        mRebuildSync.wait(waitend - now);
+                    } catch (InterruptedException e) {
+                    }
+                }
+
+                mRebuildAsync = true;
+
+                return mRebuildResult;
             }
         }
 
-        Collections.sort(filteredApps, comparator);
+        void handleRebuildList() {
+            AppFilter filter;
+            Comparator<AppEntry> comparator;
+            synchronized (mRebuildSync) {
+                if (!mRebuildRequested) {
+                    return;
+                }
 
-        synchronized (mRebuildSync) {
-            if (!mRebuildRequested) {
-                if (!mRebuildAsync) {
-                    mRebuildResult = filteredApps;
-                    mRebuildSync.notifyAll();
-                } else {
-                    if (!mMainHandler.hasMessages(MainHandler.MSG_REBUILD_COMPLETE)) {
-                        Message msg = mMainHandler.obtainMessage(
-                                MainHandler.MSG_REBUILD_COMPLETE, filteredApps);
-                        mMainHandler.sendMessage(msg);
+                filter = mRebuildFilter;
+                comparator = mRebuildComparator;
+                mRebuildRequested = false;
+                mRebuildFilter = null;
+                mRebuildComparator = null;
+            }
+
+            Process.setThreadPriority(Process.THREAD_PRIORITY_FOREGROUND);
+
+            if (filter != null) {
+                filter.init();
+            }
+            
+            List<ApplicationInfo> apps;
+            synchronized (mEntriesMap) {
+                apps = new ArrayList<ApplicationInfo>(mApplications);
+            }
+
+            ArrayList<AppEntry> filteredApps = new ArrayList<AppEntry>();
+            if (DEBUG) Log.i(TAG, "Rebuilding...");
+            for (int i=0; i<apps.size(); i++) {
+                ApplicationInfo info = apps.get(i);
+                if (filter == null || filter.filterApp(info)) {
+                    synchronized (mEntriesMap) {
+                        if (DEBUG_LOCKING) Log.v(TAG, "rebuild acquired lock");
+                        AppEntry entry = getEntryLocked(info);
+                        entry.ensureLabel(mContext);
+                        if (DEBUG) Log.i(TAG, "Using " + info.packageName + ": " + entry);
+                        filteredApps.add(entry);
+                        if (DEBUG_LOCKING) Log.v(TAG, "rebuild releasing lock");
                     }
                 }
             }
+
+            Collections.sort(filteredApps, comparator);
+
+            synchronized (mRebuildSync) {
+                if (!mRebuildRequested) {
+                    mLastAppList = filteredApps;
+                    if (!mRebuildAsync) {
+                        mRebuildResult = filteredApps;
+                        mRebuildSync.notifyAll();
+                    } else {
+                        if (!mMainHandler.hasMessages(MainHandler.MSG_REBUILD_COMPLETE, this)) {
+                            Message msg = mMainHandler.obtainMessage(
+                                    MainHandler.MSG_REBUILD_COMPLETE, this);
+                            mMainHandler.sendMessage(msg);
+                        }
+                    }
+                }
+            }
+
+            Process.setThreadPriority(Process.THREAD_PRIORITY_BACKGROUND);
         }
 
-        Process.setThreadPriority(Process.THREAD_PRIORITY_BACKGROUND);
+        public void release() {
+            pause();
+            synchronized (mEntriesMap) {
+                mSessions.remove(this);
+            }
+        }
+    }
+
+    public Session newSession(Callbacks callbacks) {
+        Session s = new Session(callbacks);
+        synchronized (mEntriesMap) {
+            mSessions.add(s);
+        }
+        return s;
+    }
+
+    void doResumeIfNeededLocked() {
+        if (mResumed) {
+            return;
+        }
+        mResumed = true;
+        if (mPackageIntentReceiver == null) {
+            mPackageIntentReceiver = new PackageIntentReceiver();
+            mPackageIntentReceiver.registerReceiver();
+        }
+        mApplications = mPm.getInstalledApplications(
+                PackageManager.GET_UNINSTALLED_PACKAGES |
+                PackageManager.GET_DISABLED_COMPONENTS);
+        if (mApplications == null) {
+            mApplications = new ArrayList<ApplicationInfo>();
+        }
+
+        if (mInterestingConfigChanges.applyNewConfig(mContext.getResources())) {
+            // If an interesting part of the configuration has changed, we
+            // should completely reload the app entries.
+            mEntriesMap.clear();
+            mAppEntries.clear();
+        } else {
+            for (int i=0; i<mAppEntries.size(); i++) {
+                mAppEntries.get(i).sizeStale = true;
+            }
+        }
+
+        for (int i=0; i<mApplications.size(); i++) {
+            final ApplicationInfo info = mApplications.get(i);
+            // Need to trim out any applications that are disabled by
+            // something different than the user.
+            if (!info.enabled && info.enabledSetting
+                    != PackageManager.COMPONENT_ENABLED_STATE_DISABLED_USER) {
+                mApplications.remove(i);
+                i--;
+                continue;
+            }
+            final AppEntry entry = mEntriesMap.get(info.packageName);
+            if (entry != null) {
+                entry.info = info;
+            }
+        }
+        mCurComputingSizePkg = null;
+        if (!mBackgroundHandler.hasMessages(BackgroundHandler.MSG_LOAD_ENTRIES)) {
+            mBackgroundHandler.sendEmptyMessage(BackgroundHandler.MSG_LOAD_ENTRIES);
+        }
+    }
+
+    void doPauseIfNeededLocked() {
+        if (!mResumed) {
+            return;
+        }
+        for (int i=0; i<mSessions.size(); i++) {
+            if (mSessions.get(i).mResumed) {
+                return;
+            }
+        }
+        mResumed = false;
+        if (mPackageIntentReceiver != null) {
+            mPackageIntentReceiver.unregisterReceiver();
+            mPackageIntentReceiver = null;
+        }
     }
 
     AppEntry getEntry(String packageName) {
@@ -772,7 +860,18 @@
         @Override
         public void handleMessage(Message msg) {
             // Always try rebuilding list first thing, if needed.
-            handleRebuildList();
+            ArrayList<Session> rebuildingSessions = null;
+            synchronized (mEntriesMap) {
+                if (mRebuildingSessions.size() > 0) {
+                    rebuildingSessions = new ArrayList<Session>(mRebuildingSessions);
+                    mRebuildingSessions.clear();
+                }
+            }
+            if (rebuildingSessions != null) {
+                for (int i=0; i<rebuildingSessions.size(); i++) {
+                    rebuildingSessions.get(i).handleRebuildList();
+                }
+            }
 
             switch (msg.what) {
                 case MSG_REBUILD_LIST: {
diff --git a/src/com/android/settings/applications/InstalledAppDetails.java b/src/com/android/settings/applications/InstalledAppDetails.java
index 6fa20ce..bd77118 100644
--- a/src/com/android/settings/applications/InstalledAppDetails.java
+++ b/src/com/android/settings/applications/InstalledAppDetails.java
@@ -98,6 +98,7 @@
     private AppWidgetManager mAppWidgetManager;
     private DevicePolicyManager mDpm;
     private ApplicationsState mState;
+    private ApplicationsState.Session mSession;
     private ApplicationsState.AppEntry mAppEntry;
     private PackageInfo mPackageInfo;
     private CanBeOnSdCardChecker mCanBeOnSdCardChecker;
@@ -348,6 +349,7 @@
         super.onCreate(icicle);
         
         mState = ApplicationsState.getInstance(getActivity().getApplication());
+        mSession = mState.newSession(this);
         mPm = getActivity().getPackageManager();
         IBinder b = ServiceManager.getService(Context.USB_SERVICE);
         mUsbManager = IUsbManager.Stub.asInterface(b);
@@ -423,7 +425,7 @@
     public void onResume() {
         super.onResume();
         
-        mState.resume(this);
+        mSession.resume();
         if (!refreshUi()) {
             setIntentAndFinish(true, true);
         }
@@ -432,7 +434,7 @@
     @Override
     public void onPause() {
         super.onPause();
-        mState.pause();
+        mSession.pause();
     }
 
     @Override
diff --git a/src/com/android/settings/applications/ManageApplications.java b/src/com/android/settings/applications/ManageApplications.java
index 37967d1..1448dfd 100644
--- a/src/com/android/settings/applications/ManageApplications.java
+++ b/src/com/android/settings/applications/ManageApplications.java
@@ -18,11 +18,9 @@
 
 import static android.net.NetworkPolicyManager.POLICY_NONE;
 import static android.net.NetworkPolicyManager.POLICY_REJECT_METERED_BACKGROUND;
-import static com.android.settings.Utils.prepareCustomPreferencesList;
 
 import android.app.Activity;
 import android.app.AlertDialog;
-import android.app.DialogFragment;
 import android.app.Fragment;
 import android.app.INotificationManager;
 import android.content.ComponentName;
@@ -45,9 +43,11 @@
 import android.os.ServiceManager;
 import android.preference.PreferenceActivity;
 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.util.SparseIntArray;
 import android.view.LayoutInflater;
 import android.view.Menu;
 import android.view.MenuInflater;
@@ -75,6 +75,7 @@
 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;
@@ -169,8 +170,242 @@
     private int mFilterApps = FILTER_APPS_THIRD_PARTY;
     
     private ApplicationsState mApplicationsState;
-    private ApplicationsAdapter mApplicationsAdapter;
-    
+
+    public static class TabInfo implements OnItemClickListener {
+        public final ManageApplications mOwner;
+        public final ApplicationsState mApplicationsState;
+        public final IMediaContainerService mContainerService;
+        public final CharSequence mLabel;
+        public final int mListType;
+        public final int mFilter;
+        public final OnItemClickListener mClickListener;
+        public final CharSequence mInvalidSizeStr;
+        public final CharSequence mComputingSizeStr;
+        private final Bundle mSavedInstanceState;
+
+        public ApplicationsAdapter mApplications;
+        public LayoutInflater mInflater;
+        public View mRootView;
+
+        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 mLastUsedStorage, mLastAppStorage, mLastFreeStorage;
+
+        final Runnable mRunningProcessesAvail = new Runnable() {
+            public void run() {
+                handleRunningProcessesAvail();
+            }
+        };
+
+        public TabInfo(ManageApplications owner, ApplicationsState apps,
+                IMediaContainerService containerService,
+                CharSequence label, int listType, OnItemClickListener clickListener,
+                Bundle savedInstanceState) {
+            mOwner = owner;
+            mApplicationsState = apps;
+            mContainerService = containerService;
+            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 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);
+            }
+            mRunningProcessesView = (RunningProcessesView)mRootView.findViewById(
+                    R.id.running_processes);
+            if (mRunningProcessesView != null) {
+                mRunningProcessesView.doCreate(mSavedInstanceState);
+            }
+
+            return 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() {
+            // Fragment view not yet created?
+            if (mRootView == null) return;
+            // 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;
+
+            long freeStorage = 0;
+            long appStorage = 0;
+            long totalStorage = 0;
+            CharSequence newLabel = null;
+
+            if (mFilter == FILTER_APPS_SDCARD) {
+                newLabel = mOwner.getActivity().getText(R.string.sd_card_storage);
+
+                if (mContainerService != null) {
+                    try {
+                        final long[] stats = mContainerService.getFileSystemStats(
+                                Environment.getExternalStorageDirectory().getPath());
+                        totalStorage = stats[0];
+                        freeStorage = 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);
+                        appStorage += ae.externalCodeSize + ae.externalDataSize;
+                    }
+                }
+            } else {
+                newLabel = mOwner.getActivity().getText(R.string.internal_storage);
+
+                if (mContainerService != null) {
+                    try {
+                        final long[] stats = mContainerService.getFileSystemStats(
+                                Environment.getDataDirectory().getPath());
+                        totalStorage = stats[0];
+                        freeStorage = 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);
+                        appStorage += ae.codeSize + ae.dataSize;
+                        if (emulatedStorage) {
+                            appStorage += ae.externalCodeSize + ae.externalDataSize;
+                        }
+                    }
+                }
+                freeStorage += mApplicationsState.sumCacheSizes();
+            }
+            if (newLabel != null) {
+                mStorageChartLabel.setText(newLabel);
+            }
+            if (totalStorage > 0) {
+                mColorBar.setRatios((totalStorage-freeStorage-appStorage)/(float)totalStorage,
+                        appStorage/(float)totalStorage, freeStorage/(float)totalStorage);
+                long usedStorage = totalStorage - freeStorage;
+                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 != freeStorage) {
+                    mLastFreeStorage = freeStorage;
+                    String sizeStr = Formatter.formatShortFileSize(
+                            mOwner.getActivity(), freeStorage);
+                    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(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;
@@ -180,59 +415,72 @@
     
     private String mCurrentPkgName;
     
-    private View mLoadingContainer;
-
-    private View mListContainer;
-
-    // ListView used to display list
-    private ListView mListView;
-    // Custom view used to display running processes
-    private RunningProcessesView mRunningProcessesView;
-    
-    LinearColorBar mColorBar;
-    TextView mStorageChartLabel;
-    TextView mUsedStorageText;
-    TextView mFreeStorageText;
-
     private Menu mOptionsMenu;
 
     // These are for keeping track of activity and spinner switch state.
-    private int mCurView;
-    private boolean mCreatedRunning;
-
-    private boolean mResumedRunning;
     private boolean mActivityResumed;
     
-    private boolean mLastShowedInternalStorage = true;
-    private long mLastUsedStorage, mLastAppStorage, mLastFreeStorage;
-
     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 View mRootView;
 
     private boolean mShowBackground = false;
     
     private int mDefaultListType = -1;
-    private SparseIntArray mIndexToType = new SparseIntArray(4);
 
-    private Spinner mSpinner;
-    private FrameLayout mSpinnerContent;
+    private ViewGroup mContentContainer;
+    private View mRootView;
+    private ViewPager mViewPager;
 
     AlertDialog mResetDialog;
 
-    final Runnable mRunningProcessesAvail = new Runnable() {
-        public void run() {
-            handleRunningProcessesAvail();
+    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;
+        }
 
-    static class AppFilterAdapter extends ArrayAdapter<String> {
+        @Override
+        public void destroyItem(ViewGroup container, int position, Object object) {
+            container.removeView((View)object);
+        }
 
-        public AppFilterAdapter(Context context) {
-            super(context, R.layout.apps_spinner_item);
-            setDropDownViewResource(R.layout.apps_spinner_dropdown_item);
+        @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);
+            }
         }
     }
 
@@ -245,14 +493,18 @@
      * the getId methods via the package name into the internal maps and indices.
      * The order of applications in the list is mirrored in mAppLocalList
      */
-    class ApplicationsAdapter extends BaseAdapter implements Filterable,
+    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 mLastFilterMode=-1, mLastSortMode=-1;
+        private int mLastSortMode=-1;
         private boolean mWaitingForData;
         private int mWhichSize = SIZE_TOTAL;
         CharSequence mCurFilterPrefix;
@@ -273,39 +525,41 @@
                 mCurFilterPrefix = constraint;
                 mEntries = (ArrayList<ApplicationsState.AppEntry>)results.values;
                 notifyDataSetChanged();
-                updateStorageUsage();
+                mTab.updateStorageUsage();
             }
         };
 
-        public ApplicationsAdapter(ApplicationsState state) {
+        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 filter, int sort) {
+        public void resume(int sort) {
             if (DEBUG) Log.i(TAG, "Resume!  mResumed=" + mResumed);
             if (!mResumed) {
                 mResumed = true;
-                mState.resume(this);
-                mLastFilterMode = filter;
+                mSession.resume();
                 mLastSortMode = sort;
                 rebuild(true);
             } else {
-                rebuild(filter, sort);
+                rebuild(sort);
             }
         }
 
         public void pause() {
             if (mResumed) {
                 mResumed = false;
-                mState.pause();
+                mSession.pause();
             }
         }
 
-        public void rebuild(int filter, int sort) {
-            if (filter == mLastFilterMode && sort == mLastSortMode) {
+        public void rebuild(int sort) {
+            if (sort == mLastSortMode) {
                 return;
             }
-            mLastFilterMode = filter;
             mLastSortMode = sort;
             rebuild(true);
         }
@@ -320,7 +574,7 @@
             } else {
                 mWhichSize = SIZE_INTERNAL;
             }
-            switch (mLastFilterMode) {
+            switch (mFilterMode) {
                 case FILTER_APPS_THIRD_PARTY:
                     filterObj = ApplicationsState.THIRD_PARTY_FILTER;
                     break;
@@ -353,7 +607,7 @@
                     break;
             }
             ArrayList<ApplicationsState.AppEntry> entries
-                    = mState.rebuild(filterObj, comparatorObj);
+                    = mSession.rebuild(filterObj, comparatorObj);
             if (entries == null && !eraseold) {
                 // Don't have new list yet, but can continue using the old one.
                 return;
@@ -365,15 +619,15 @@
                 mEntries = null;
             }
             notifyDataSetChanged();
-            updateStorageUsage();
+            mTab.updateStorageUsage();
 
             if (entries == null) {
                 mWaitingForData = true;
-                mListContainer.setVisibility(View.INVISIBLE);
-                mLoadingContainer.setVisibility(View.VISIBLE);
+                mTab.mListContainer.setVisibility(View.INVISIBLE);
+                mTab.mLoadingContainer.setVisibility(View.VISIBLE);
             } else {
-                mListContainer.setVisibility(View.VISIBLE);
-                mLoadingContainer.setVisibility(View.GONE);
+                mTab.mListContainer.setVisibility(View.VISIBLE);
+                mTab.mLoadingContainer.setVisibility(View.GONE);
             }
         }
 
@@ -399,24 +653,24 @@
 
         @Override
         public void onRunningStateChanged(boolean running) {
-            getActivity().setProgressBarIndeterminateVisibility(running);
+            mTab.mOwner.getActivity().setProgressBarIndeterminateVisibility(running);
         }
 
         @Override
         public void onRebuildComplete(ArrayList<AppEntry> apps) {
-            if (mLoadingContainer.getVisibility() == View.VISIBLE) {
-                mLoadingContainer.startAnimation(AnimationUtils.loadAnimation(
-                        getActivity(), android.R.anim.fade_out));
-                mListContainer.startAnimation(AnimationUtils.loadAnimation(
-                        getActivity(), android.R.anim.fade_in));
+            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));
             }
-            mListContainer.setVisibility(View.VISIBLE);
-            mLoadingContainer.setVisibility(View.GONE);
+            mTab.mListContainer.setVisibility(View.VISIBLE);
+            mTab.mLoadingContainer.setVisibility(View.GONE);
             mWaitingForData = false;
             mBaseEntries = apps;
             mEntries = applyPrefixFilter(mCurFilterPrefix, mBaseEntries);
             notifyDataSetChanged();
-            updateStorageUsage();
+            mTab.updateStorageUsage();
         }
 
         @Override
@@ -436,9 +690,9 @@
                 AppViewHolder holder = (AppViewHolder)mActive.get(i).getTag();
                 if (holder.entry.info.packageName.equals(packageName)) {
                     synchronized (holder.entry) {
-                        holder.updateSizeText(ManageApplications.this, mWhichSize);
+                        holder.updateSizeText(mTab.mInvalidSizeStr, mWhichSize);
                     }
-                    if (holder.entry.info.packageName.equals(mCurrentPkgName)
+                    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
@@ -446,7 +700,7 @@
                         // the list with the new size to reflect it to the user.
                         rebuild(false);
                     }
-                    updateStorageUsage();
+                    mTab.updateStorageUsage();
                     return;
                 }
             }
@@ -478,7 +732,7 @@
         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(mInflater, convertView);
+            AppViewHolder holder = AppViewHolder.createOrRecycle(mTab.mInflater, convertView);
             convertView = holder.rootView;
 
             // Bind the data efficiently with the holder
@@ -487,7 +741,7 @@
                 holder.entry = entry;
                 if (entry.label != null) {
                     holder.appName.setText(entry.label);
-                    holder.appName.setTextColor(getActivity().getResources().getColorStateList(
+                    holder.appName.setTextColor(mContext.getResources().getColorStateList(
                             entry.info.enabled ? android.R.color.primary_text_dark
                                     : android.R.color.secondary_text_dark));
                 }
@@ -495,13 +749,13 @@
                 if (entry.icon != null) {
                     holder.appIcon.setImageDrawable(entry.icon);
                 }
-                holder.updateSizeText(ManageApplications.this, mWhichSize);
+                holder.updateSizeText(mTab.mInvalidSizeStr, mWhichSize);
                 if (InstalledAppDetails.SUPPORT_DISABLE_APPS) {
                     holder.disabled.setVisibility(entry.info.enabled ? View.GONE : View.VISIBLE);
                 } else {
                     holder.disabled.setVisibility(View.GONE);
                 }
-                if (mLastFilterMode == FILTER_APPS_SDCARD) {
+                if (mFilterMode == FILTER_APPS_SDCARD) {
                     holder.checkBox.setVisibility(View.VISIBLE);
                     holder.checkBox.setChecked((entry.info.flags
                             & ApplicationInfo.FLAG_EXTERNAL_STORAGE) != 0);
@@ -532,7 +786,6 @@
         setHasOptionsMenu(true);
 
         mApplicationsState = ApplicationsState.getInstance(getActivity().getApplication());
-        mApplicationsAdapter = new ApplicationsAdapter(mApplicationsState);
         Intent intent = getActivity().getIntent();
         String action = intent.getAction();
         int defaultListType = LIST_TYPE_DOWNLOADED;
@@ -571,6 +824,28 @@
 
         mInvalidSizeStr = getActivity().getText(R.string.invalid_size_value);
         mComputingSizeStr = getActivity().getText(R.string.computing_size);
+
+        TabInfo tab = new TabInfo(this, mApplicationsState, mContainerService,
+                getActivity().getString(R.string.filter_apps_third_party),
+                LIST_TYPE_DOWNLOADED, this, savedInstanceState);
+        mTabs.add(tab);
+
+        if (!Environment.isExternalStorageEmulated()) {
+            tab = new TabInfo(this, mApplicationsState, mContainerService,
+                    getActivity().getString(R.string.filter_apps_onsdcard),
+                    LIST_TYPE_SDCARD, this, savedInstanceState);
+            mTabs.add(tab);
+        }
+
+        tab = new TabInfo(this, mApplicationsState, mContainerService,
+                getActivity().getString(R.string.filter_apps_running),
+                LIST_TYPE_RUNNING, this, savedInstanceState);
+        mTabs.add(tab);
+
+        tab = new TabInfo(this, mApplicationsState, mContainerService,
+                getActivity().getString(R.string.filter_apps_all),
+                LIST_TYPE_ALL, this, savedInstanceState);
+        mTabs.add(tab);
     }
 
 
@@ -578,75 +853,24 @@
     public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
         // initialize the inflater
         mInflater = inflater;
-        mRootView = inflater.inflate(R.layout.manage_applications, null);
-        mLoadingContainer = mRootView.findViewById(R.id.loading_container);
-        mListContainer = mRootView.findViewById(R.id.list_container);
-        // Create adapter and list view here
-        ListView lv = (ListView) mListContainer.findViewById(android.R.id.list);
-        View emptyView = mListContainer.findViewById(com.android.internal.R.id.empty);
-        if (emptyView != null) {
-            lv.setEmptyView(emptyView);
-        }
-        lv.setOnItemClickListener(this);
-        lv.setSaveEnabled(true);
-        lv.setItemsCanFocus(true);
-        lv.setOnItemClickListener(this);
-        lv.setTextFilterEnabled(true);
-        mListView = lv;
-        lv.setRecyclerListener(mApplicationsAdapter);
-        mListView.setAdapter(mApplicationsAdapter);
-        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);
-        mRunningProcessesView = (RunningProcessesView)mRootView.findViewById(
-                R.id.running_processes);
 
-        mCreatedRunning = mResumedRunning = false;
-        mCurView = VIEW_NOTHING;
-
-        View spinnerHost = mInflater.inflate(R.layout.manage_apps_spinner_content,
+        View rootView = mInflater.inflate(R.layout.manage_applications_content,
                 container, false);
+        mContentContainer = container;
+        mRootView = rootView;
 
-        mSpinner = (Spinner) spinnerHost.findViewById(R.id.spinner);
-        mSpinnerContent = (FrameLayout) spinnerHost.findViewById(R.id.spinner_content);
-        mSpinnerContent.addView(mRootView);
-
-        AppFilterAdapter sa = new AppFilterAdapter(getActivity());
-        mIndexToType.append(sa.getCount(), LIST_TYPE_DOWNLOADED);
-        sa.add(getActivity().getString(R.string.filter_apps_third_party));
-        if (!Environment.isExternalStorageEmulated()) {
-            mIndexToType.append(sa.getCount(), LIST_TYPE_SDCARD);
-            sa.add(getActivity().getString(R.string.filter_apps_onsdcard));
-        }
-        mIndexToType.append(sa.getCount(), LIST_TYPE_RUNNING);
-        sa.add(getActivity().getString(R.string.filter_apps_running));
-        mIndexToType.append(sa.getCount(), LIST_TYPE_ALL);
-        sa.add(getActivity().getString(R.string.filter_apps_all));
-
-        mSpinner.setOnItemSelectedListener(new AdapterView.OnItemSelectedListener() {
-            @Override
-            public void onItemSelected(AdapterView<?> parent,
-                    View view, int position, long id) {
-                showCurrentList();
-            }
-            @Override
-            public void onNothingSelected(AdapterView<?> parent) {
-                // Nothing
-            }
-
-        });
-
-        mSpinner.setSelection(getIndex(mDefaultListType));
-        mSpinner.setAdapter(sa);
-
-        prepareCustomPreferencesList(container, spinnerHost, mListView, false);
+        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);
 
         if (savedInstanceState != null && savedInstanceState.getBoolean(EXTRA_RESET_DIALOG)) {
             buildResetDialog();
         }
 
-        return spinnerHost;
+        return rootView;
     }
 
     @Override
@@ -658,10 +882,8 @@
     public void onResume() {
         super.onResume();
         mActivityResumed = true;
-        showCurrentList();
+        updateCurrentTab(mViewPager.getCurrentItem());
         updateOptionsMenu();
-        mSpinner.setEnabled(true);
-        mSpinnerContent.setEnabled(true);
     }
 
     @Override
@@ -682,13 +904,9 @@
     public void onPause() {
         super.onPause();
         mActivityResumed = false;
-        mApplicationsAdapter.pause();
-        if (mResumedRunning) {
-            mRunningProcessesView.doPause();
-            mResumedRunning = false;
+        for (int i=0; i<mTabs.size(); i++) {
+            mTabs.get(i).pause();
         }
-        mSpinner.setEnabled(false);
-        mSpinnerContent.setEnabled(false);
     }
 
     @Override
@@ -707,11 +925,14 @@
         }
     }
 
-    private int getIndex(int listType) {
-        for (int i = 0; i < mIndexToType.size(); i++) {
-            if (listType == mIndexToType.get(i)) return i;
+    TabInfo tabForType(int type) {
+        for (int i = 0; i < mTabs.size(); i++) {
+            TabInfo tab = mTabs.get(i);
+            if (tab.mListType == type) {
+                return tab;
+            }
         }
-        return 0;
+        return null;
     }
 
     // utility method used to start sub activity
@@ -771,9 +992,10 @@
          * The running processes screen doesn't use the mApplicationsAdapter
          * so bringing up this menu in that case doesn't make any sense.
          */
-        if (mCurView == VIEW_RUNNING) {
-            boolean showingBackground = mRunningProcessesView != null
-                    ? mRunningProcessesView.mAdapter.getShowBackground() : false;
+        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);
@@ -861,8 +1083,15 @@
                             if (DEBUG) Log.v(TAG, "Done clearing");
                             if (getActivity() != null && mActivityResumed) {
                                 if (DEBUG) Log.v(TAG, "Updating UI!");
-                                mApplicationsAdapter.pause();
-                                showCurrentList();
+                                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);
+                                }
                             }
                         }
                     });
@@ -877,15 +1106,19 @@
         int menuId = item.getItemId();
         if ((menuId == SORT_ORDER_ALPHA) || (menuId == SORT_ORDER_SIZE)) {
             mSortOrder = menuId;
-            if (mCurView != VIEW_RUNNING) {
-                mApplicationsAdapter.rebuild(mFilterApps, mSortOrder);
+            if (mCurTab != null && mCurTab.mApplications != null) {
+                mCurTab.mApplications.rebuild(mSortOrder);
             }
         } else if (menuId == SHOW_RUNNING_SERVICES) {
             mShowBackground = false;
-            mRunningProcessesView.mAdapter.setShowBackground(false);
+            if (mCurTab != null && mCurTab.mRunningProcessesView != null) {
+                mCurTab.mRunningProcessesView.mAdapter.setShowBackground(false);
+            }
         } else if (menuId == SHOW_BACKGROUND_PROCESSES) {
             mShowBackground = true;
-            mRunningProcessesView.mAdapter.setShowBackground(true);
+            if (mCurTab != null && mCurTab.mRunningProcessesView != null) {
+                mCurTab.mRunningProcessesView.mAdapter.setShowBackground(true);
+            }
         } else if (menuId == RESET_APP_PREFERENCES) {
             buildResetDialog();
         } else {
@@ -898,199 +1131,48 @@
     
     public void onItemClick(AdapterView<?> parent, View view, int position,
             long id) {
-        ApplicationsState.AppEntry entry = mApplicationsAdapter.getAppEntry(position);
-        mCurrentPkgName = entry.info.packageName;
-        startApplicationDetailsActivity();
-    }
-
-    static final int VIEW_NOTHING = 0;
-    static final int VIEW_LIST = 1;
-    static final int VIEW_RUNNING = 2;
-
-    void updateStorageUsage() {
-        // Fragment view not yet created?
-        if (mRootView == null) return;
-        // Make sure a callback didn't come at an inopportune time.
-        if (getActivity() == null) return;
-
-        if (mCurView == VIEW_RUNNING) {
-            return;
-        }
-
-        long freeStorage = 0;
-        long appStorage = 0;
-        long totalStorage = 0;
-        CharSequence newLabel = null;
-
-        if (mFilterApps == FILTER_APPS_SDCARD) {
-            if (mLastShowedInternalStorage) {
-                mLastShowedInternalStorage = false;
-            }
-            newLabel = getActivity().getText(R.string.sd_card_storage);
-
-            if (mContainerService != null) {
-                try {
-                    final long[] stats = mContainerService.getFileSystemStats(
-                            Environment.getExternalStorageDirectory().getPath());
-                    totalStorage = stats[0];
-                    freeStorage = stats[1];
-                } catch (RemoteException e) {
-                    Log.w(TAG, "Problem in container service", e);
-                }
-            }
-
-            final int N = mApplicationsAdapter.getCount();
-            for (int i=0; i<N; i++) {
-                ApplicationsState.AppEntry ae = mApplicationsAdapter.getAppEntry(i);
-                appStorage += ae.externalCodeSize + ae.externalDataSize;
-            }
-        } else {
-            if (!mLastShowedInternalStorage) {
-                mLastShowedInternalStorage = true;
-            }
-            newLabel = getActivity().getText(R.string.internal_storage);
-
-            if (mContainerService != null) {
-                try {
-                    final long[] stats = mContainerService.getFileSystemStats(
-                            Environment.getDataDirectory().getPath());
-                    totalStorage = stats[0];
-                    freeStorage = stats[1];
-                } catch (RemoteException e) {
-                    Log.w(TAG, "Problem in container service", e);
-                }
-            }
-
-            final boolean emulatedStorage = Environment.isExternalStorageEmulated();
-            final int N = mApplicationsAdapter.getCount();
-            for (int i=0; i<N; i++) {
-                ApplicationsState.AppEntry ae = mApplicationsAdapter.getAppEntry(i);
-                appStorage += ae.codeSize + ae.dataSize;
-                if (emulatedStorage) {
-                    appStorage += ae.externalCodeSize + ae.externalDataSize;
-                }
-            }
-            freeStorage += mApplicationsState.sumCacheSizes();
-        }
-        if (newLabel != null) {
-            mStorageChartLabel.setText(newLabel);
-        }
-        if (totalStorage > 0) {
-            mColorBar.setRatios((totalStorage-freeStorage-appStorage)/(float)totalStorage,
-                    appStorage/(float)totalStorage, freeStorage/(float)totalStorage);
-            long usedStorage = totalStorage - freeStorage;
-            if (mLastUsedStorage != usedStorage) {
-                mLastUsedStorage = usedStorage;
-                String sizeStr = Formatter.formatShortFileSize(getActivity(), usedStorage);
-                mUsedStorageText.setText(getActivity().getResources().getString(
-                        R.string.service_foreground_processes, sizeStr));
-            }
-            if (mLastFreeStorage != freeStorage) {
-                mLastFreeStorage = freeStorage;
-                String sizeStr = Formatter.formatShortFileSize(getActivity(), freeStorage);
-                mFreeStorageText.setText(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("");
-            }
+        if (mCurTab != null && mCurTab.mApplications != null) {
+            ApplicationsState.AppEntry entry = mCurTab.mApplications.getAppEntry(position);
+            mCurrentPkgName = entry.info.packageName;
+            startApplicationDetailsActivity();
         }
     }
 
-    private void selectView(int which) {
-        if (which == VIEW_LIST) {
-            if (mResumedRunning) {
-                mRunningProcessesView.doPause();
-                mResumedRunning = false;
-            }
-            if (mCurView != which) {
-                mRunningProcessesView.setVisibility(View.GONE);
-                mListContainer.setVisibility(View.VISIBLE);
-                mLoadingContainer.setVisibility(View.GONE);
-            }
-            if (mActivityResumed) {
-                mApplicationsAdapter.resume(mFilterApps, mSortOrder);
-            }
-        } else if (which == VIEW_RUNNING) {
-            if (!mCreatedRunning) {
-                mRunningProcessesView.doCreate(null);
-                mRunningProcessesView.mAdapter.setShowBackground(mShowBackground);
-                mCreatedRunning = true;
-            }
-            boolean haveData = true;
-            if (mActivityResumed && !mResumedRunning) {
-                haveData = mRunningProcessesView.doResume(this, mRunningProcessesAvail);
-                mResumedRunning = true;
-            }
-            mApplicationsAdapter.pause();
-            if (mCurView != which) {
-                if (haveData) {
-                    mRunningProcessesView.setVisibility(View.VISIBLE);
-                } else {
-                    mLoadingContainer.setVisibility(View.VISIBLE);
-                }
-                mListContainer.setVisibility(View.GONE);
+    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();
             }
         }
-        mCurView = which;
+
+        mCurTab.updateStorageUsage();
+        updateOptionsMenu();
         final Activity host = getActivity();
         if (host != null) {
             host.invalidateOptionsMenu();
         }
     }
 
-    void handleRunningProcessesAvail() {
-        if (mCurView == VIEW_RUNNING) {
-            mLoadingContainer.startAnimation(AnimationUtils.loadAnimation(
-                    getActivity(), android.R.anim.fade_out));
-            mRunningProcessesView.startAnimation(AnimationUtils.loadAnimation(
-                    getActivity(), android.R.anim.fade_in));
-            mRunningProcessesView.setVisibility(View.VISIBLE);
-            mLoadingContainer.setVisibility(View.GONE);
-        }
-    }
-
-    public void showCurrentList() {
-        int listType = mIndexToType.get(mSpinner.getSelectedItemPosition());
-
-        int newOption;
-        if (LIST_TYPE_DOWNLOADED == listType) {
-            newOption = FILTER_APPS_THIRD_PARTY;
-        } else if (LIST_TYPE_ALL == listType) {
-            newOption = FILTER_APPS_ALL;
-        } else if (LIST_TYPE_SDCARD == listType) {
-            newOption = FILTER_APPS_SDCARD;
-        } else if (LIST_TYPE_RUNNING == listType) {
-            ((InputMethodManager)getActivity().getSystemService(Context.INPUT_METHOD_SERVICE))
-                    .hideSoftInputFromWindow(
-                            getActivity().getWindow().getDecorView().getWindowToken(), 0);
-            selectView(VIEW_RUNNING);
-            return;
-        } else {
-            // Invalid option. Do nothing
-            return;
-        }
-
-        mFilterApps = newOption;
-        selectView(VIEW_LIST);
-        updateStorageUsage();
-        updateOptionsMenu();
-    }
-
     private volatile IMediaContainerService mContainerService;
 
     private final ServiceConnection mContainerConnection = new ServiceConnection() {
         @Override
         public void onServiceConnected(ComponentName name, IBinder service) {
             mContainerService = IMediaContainerService.Stub.asInterface(service);
-            updateStorageUsage();
+            if (mCurTab != null) {
+                mCurTab.updateStorageUsage();
+            }
         }
 
         @Override