Move LauncherApplication's state and code to LauncherAppState.

This removes Launcher's static data and other state out of
the Application object. Now LauncherApplication (extends
Application) exists only to instantiate LauncherAppState.

Change-Id: I4e323bd78b77536b92054105536a55c0c2c19ba8
diff --git a/src/com/android/launcher3/AppsCustomizePagedView.java b/src/com/android/launcher3/AppsCustomizePagedView.java
index 7faa706..be0dd8a 100644
--- a/src/com/android/launcher3/AppsCustomizePagedView.java
+++ b/src/com/android/launcher3/AppsCustomizePagedView.java
@@ -251,7 +251,7 @@
         mPackageManager = context.getPackageManager();
         mApps = new ArrayList<ApplicationInfo>();
         mWidgets = new ArrayList<Object>();
-        mIconCache = ((LauncherApplication) context.getApplicationContext()).getIconCache();
+        mIconCache = (LauncherAppState.getInstance()).getIconCache();
         mCanvas = new Canvas();
         mRunningTasks = new ArrayList<AppsCustomizeAsyncTask>();
 
@@ -362,7 +362,7 @@
             Configuration.ORIENTATION_LANDSCAPE;
         int maxCellCountX = Integer.MAX_VALUE;
         int maxCellCountY = Integer.MAX_VALUE;
-        if (LauncherApplication.isScreenLarge()) {
+        if (LauncherAppState.isScreenLarge()) {
             maxCellCountX = (isLandscape ? LauncherModel.getCellCountX() :
                 LauncherModel.getCellCountY());
             maxCellCountY = (isLandscape ? LauncherModel.getCellCountY() :
diff --git a/src/com/android/launcher3/AppsCustomizeTabHost.java b/src/com/android/launcher3/AppsCustomizeTabHost.java
index c1f6733..6a2f130 100644
--- a/src/com/android/launcher3/AppsCustomizeTabHost.java
+++ b/src/com/android/launcher3/AppsCustomizeTabHost.java
@@ -184,7 +184,7 @@
     }
 
     private void reloadCurrentPage() {
-        if (!LauncherApplication.isScreenLarge()) {
+        if (!LauncherAppState.isScreenLarge()) {
             mAppsCustomizePane.flashScrollingIndicator(true);
         }
         mAppsCustomizePane.loadAssociatedPages(mAppsCustomizePane.getCurrentPage());
@@ -383,7 +383,7 @@
             // transition to prevent slowing down the animation)
             mAppsCustomizePane.loadAssociatedPages(mAppsCustomizePane.getCurrentPage(), true);
 
-            if (!LauncherApplication.isScreenLarge()) {
+            if (!LauncherAppState.isScreenLarge()) {
                 mAppsCustomizePane.showScrollingIndicator(true);
             }
         }
@@ -423,7 +423,7 @@
             // prevent slowing down the animation)
             mAppsCustomizePane.loadAssociatedPages(mAppsCustomizePane.getCurrentPage());
 
-            if (!LauncherApplication.isScreenLarge()) {
+            if (!LauncherAppState.isScreenLarge()) {
                 mAppsCustomizePane.hideScrollingIndicator(false);
             }
 
diff --git a/src/com/android/launcher3/CheckLongPressHelper.java b/src/com/android/launcher3/CheckLongPressHelper.java
index 7760f4e..968dc79 100644
--- a/src/com/android/launcher3/CheckLongPressHelper.java
+++ b/src/com/android/launcher3/CheckLongPressHelper.java
@@ -45,7 +45,7 @@
         if (mPendingCheckForLongPress == null) {
             mPendingCheckForLongPress = new CheckForLongPress();
         }
-        mView.postDelayed(mPendingCheckForLongPress, LauncherApplication.getLongPressTimeout());
+        mView.postDelayed(mPendingCheckForLongPress, LauncherAppState.getLongPressTimeout());
     }
 
     public void cancelLongPress() {
diff --git a/src/com/android/launcher3/Cling.java b/src/com/android/launcher3/Cling.java
index 6bb183c..9326e38 100644
--- a/src/com/android/launcher3/Cling.java
+++ b/src/com/android/launcher3/Cling.java
@@ -126,7 +126,7 @@
         } else if (mDrawIdentifier.equals(WORKSPACE_LANDSCAPE)) {
             return new int[]{getMeasuredWidth() - (mButtonBarHeight / 2), getMeasuredHeight() / 2};
         } else if (mDrawIdentifier.equals(WORKSPACE_LARGE)) {
-            final float scale = LauncherApplication.getScreenDensity();
+            final float scale = LauncherAppState.getScreenDensity();
             final int cornerXOffset = (int) (scale * 15);
             final int cornerYOffset = (int) (scale * 10);
             return new int[]{getMeasuredWidth() - cornerXOffset, cornerYOffset};
diff --git a/src/com/android/launcher3/DeleteDropTarget.java b/src/com/android/launcher3/DeleteDropTarget.java
index d4e17b4..a1090dc 100644
--- a/src/com/android/launcher3/DeleteDropTarget.java
+++ b/src/com/android/launcher3/DeleteDropTarget.java
@@ -82,7 +82,7 @@
         // Remove the text in the Phone UI in landscape
         int orientation = getResources().getConfiguration().orientation;
         if (orientation == Configuration.ORIENTATION_LANDSCAPE) {
-            if (!LauncherApplication.isScreenLarge()) {
+            if (!LauncherAppState.isScreenLarge()) {
                 setText("");
             }
         }
diff --git a/src/com/android/launcher3/DragLayer.java b/src/com/android/launcher3/DragLayer.java
index 5a1b4cc..3bf54e8 100644
--- a/src/com/android/launcher3/DragLayer.java
+++ b/src/com/android/launcher3/DragLayer.java
@@ -736,7 +736,7 @@
     protected int getChildDrawingOrder(int childCount, int i) {
         // TODO: We have turned off this custom drawing order because it now effects touch
         // dispatch order. We need to sort that issue out and then decide how to go about this.
-        if (true || LauncherApplication.isScreenLandscape(getContext()) ||
+        if (true || LauncherAppState.isScreenLandscape(getContext()) ||
                 mWorkspaceIndex == -1 || mQsbIndex == -1 ||
                 mLauncher.getWorkspace().isDrawingBackgroundGradient()) {
             return i;
@@ -779,7 +779,7 @@
     protected void dispatchDraw(Canvas canvas) {
         super.dispatchDraw(canvas);
 
-        if (mInScrollArea && !LauncherApplication.isScreenLarge()) {
+        if (mInScrollArea && !LauncherAppState.isScreenLarge()) {
             Workspace workspace = mLauncher.getWorkspace();
             int width = workspace.getWidth();
             Rect childRect = new Rect();
diff --git a/src/com/android/launcher3/FocusHelper.java b/src/com/android/launcher3/FocusHelper.java
index 94c5820..6796633 100644
--- a/src/com/android/launcher3/FocusHelper.java
+++ b/src/com/android/launcher3/FocusHelper.java
@@ -437,7 +437,7 @@
      * Handles key events in the tab widget.
      */
     static boolean handleTabKeyEvent(AccessibleTabView v, int keyCode, KeyEvent e) {
-        if (!LauncherApplication.isScreenLarge()) return false;
+        if (!LauncherAppState.isScreenLarge()) return false;
 
         final FocusOnlyTabWidget parent = (FocusOnlyTabWidget) v.getParent();
         final TabHost tabHost = findTabHostParent(parent);
diff --git a/src/com/android/launcher3/Folder.java b/src/com/android/launcher3/Folder.java
index 07c9400..7b15e9e 100644
--- a/src/com/android/launcher3/Folder.java
+++ b/src/com/android/launcher3/Folder.java
@@ -135,7 +135,7 @@
         super(context, attrs);
         setAlwaysDrawnWithCacheEnabled(false);
         mInflater = LayoutInflater.from(context);
-        mIconCache = ((LauncherApplication)context.getApplicationContext()).getIconCache();
+        mIconCache = (LauncherAppState.getInstance()).getIconCache();
 
         Resources res = getResources();
         mMaxCountX = mMaxVisibleX = res.getInteger(R.integer.folder_max_count_x);
diff --git a/src/com/android/launcher3/HolographicOutlineHelper.java b/src/com/android/launcher3/HolographicOutlineHelper.java
index 2decc3d..0344cd5 100644
--- a/src/com/android/launcher3/HolographicOutlineHelper.java
+++ b/src/com/android/launcher3/HolographicOutlineHelper.java
@@ -44,7 +44,7 @@
     private static final int EXTRA_THICK = 2;
 
     static {
-        final float scale = LauncherApplication.getScreenDensity();
+        final float scale = LauncherAppState.getScreenDensity();
 
         MIN_OUTER_BLUR_RADIUS = (int) (scale * 1.0f);
         MAX_OUTER_BLUR_RADIUS = (int) (scale * 12.0f);
diff --git a/src/com/android/launcher3/IconCache.java b/src/com/android/launcher3/IconCache.java
index 774f27e..6fc40e3 100644
--- a/src/com/android/launcher3/IconCache.java
+++ b/src/com/android/launcher3/IconCache.java
@@ -45,13 +45,13 @@
     }
 
     private final Bitmap mDefaultIcon;
-    private final LauncherApplication mContext;
+    private final Context mContext;
     private final PackageManager mPackageManager;
     private final HashMap<ComponentName, CacheEntry> mCache =
             new HashMap<ComponentName, CacheEntry>(INITIAL_ICON_CACHE_CAPACITY);
     private int mIconDpi;
 
-    public IconCache(LauncherApplication context) {
+    public IconCache(Context context) {
         ActivityManager activityManager =
                 (ActivityManager) context.getSystemService(Context.ACTIVITY_SERVICE);
 
diff --git a/src/com/android/launcher3/InfoDropTarget.java b/src/com/android/launcher3/InfoDropTarget.java
index 9f1b016..4db8318 100644
--- a/src/com/android/launcher3/InfoDropTarget.java
+++ b/src/com/android/launcher3/InfoDropTarget.java
@@ -58,7 +58,7 @@
         // Remove the text in the Phone UI in landscape
         int orientation = getResources().getConfiguration().orientation;
         if (orientation == Configuration.ORIENTATION_LANDSCAPE) {
-            if (!LauncherApplication.isScreenLarge()) {
+            if (!LauncherAppState.isScreenLarge()) {
                 setText("");
             }
         }
diff --git a/src/com/android/launcher3/InstallShortcutReceiver.java b/src/com/android/launcher3/InstallShortcutReceiver.java
index d61cec0..07d68da 100644
--- a/src/com/android/launcher3/InstallShortcutReceiver.java
+++ b/src/com/android/launcher3/InstallShortcutReceiver.java
@@ -204,7 +204,7 @@
         info.icon = icon;
         info.iconResource = iconResource;
         if (mUseInstallQueue || launcherNotLoaded) {
-            String spKey = LauncherApplication.getSharedPreferencesKey();
+            String spKey = LauncherAppState.getSharedPreferencesKey();
             SharedPreferences sp = context.getSharedPreferences(spKey, Context.MODE_PRIVATE);
             addToInstallQueue(sp, info);
         } else {
@@ -220,7 +220,7 @@
         flushInstallQueue(context);
     }
     static void flushInstallQueue(Context context) {
-        String spKey = LauncherApplication.getSharedPreferencesKey();
+        String spKey = LauncherAppState.getSharedPreferencesKey();
         SharedPreferences sp = context.getSharedPreferences(spKey, Context.MODE_PRIVATE);
         ArrayList<PendingInstallShortcutInfo> installQueue = getAndClearInstallQueue(sp);
         Iterator<PendingInstallShortcutInfo> iter = installQueue.iterator();
@@ -231,7 +231,7 @@
 
     private static void processInstallShortcut(Context context,
             PendingInstallShortcutInfo pendingInfo) {
-        String spKey = LauncherApplication.getSharedPreferencesKey();
+        String spKey = LauncherAppState.getSharedPreferencesKey();
         SharedPreferences sp = context.getSharedPreferences(spKey, Context.MODE_PRIVATE);
 
         final Intent data = pendingInfo.data;
@@ -239,7 +239,7 @@
         final String name = pendingInfo.name;
 
         // Lock on the app so that we don't try and get the items while apps are being added
-        LauncherApplication app = (LauncherApplication) context.getApplicationContext();
+        LauncherAppState app = LauncherAppState.getInstance();
         final int[] result = {INSTALL_SHORTCUT_SUCCESSFUL};
         boolean found = false;
         synchronized (app) {
@@ -313,7 +313,7 @@
                     }.start();
 
                     // Update the Launcher db
-                    LauncherApplication app = (LauncherApplication) context.getApplicationContext();
+                    LauncherAppState app = LauncherAppState.getInstance();
                     ShortcutInfo info = app.getModel().addShortcut(context, data,
                             LauncherSettings.Favorites.CONTAINER_DESKTOP, screen,
                             tmpCoordinates[0], tmpCoordinates[1], true);
diff --git a/src/com/android/launcher3/Launcher.java b/src/com/android/launcher3/Launcher.java
index bb264d6..512ed96 100644
--- a/src/com/android/launcher3/Launcher.java
+++ b/src/com/android/launcher3/Launcher.java
@@ -357,8 +357,8 @@
         }
 
         super.onCreate(savedInstanceState);
-        LauncherApplication app = ((LauncherApplication)getApplication());
-        mSharedPrefs = getSharedPreferences(LauncherApplication.getSharedPreferencesKey(),
+        LauncherAppState app = LauncherAppState.getInstance();
+        mSharedPrefs = getSharedPreferences(LauncherAppState.getSharedPreferencesKey(),
                 Context.MODE_PRIVATE);
         mModel = app.setLauncher(this);
         mIconCache = app.getIconCache();
@@ -1567,7 +1567,7 @@
         mWorkspace.removeCallbacks(mBuildLayersRunnable);
 
         // Stop callbacks from LauncherModel
-        LauncherApplication app = ((LauncherApplication) getApplication());
+        LauncherAppState app = (LauncherAppState.getInstance());
         mModel.stopLoader();
         app.setLauncher(null);
 
@@ -2645,7 +2645,7 @@
                     dispatchOnLauncherTransitionEnd(fromView, animated, false);
                     dispatchOnLauncherTransitionEnd(toView, animated, false);
 
-                    if (mWorkspace != null && !springLoaded && !LauncherApplication.isScreenLarge()) {
+                    if (mWorkspace != null && !springLoaded && !LauncherAppState.isScreenLarge()) {
                         // Hide the workspace scrollbar
                         mWorkspace.hideScrollingIndicator(true);
                         hideDockDivider();
@@ -2715,7 +2715,7 @@
             toView.setVisibility(View.VISIBLE);
             toView.bringToFront();
 
-            if (!springLoaded && !LauncherApplication.isScreenLarge()) {
+            if (!springLoaded && !LauncherAppState.isScreenLarge()) {
                 // Hide the workspace scrollbar
                 mWorkspace.hideScrollingIndicator(true);
                 hideDockDivider();
@@ -3006,7 +3006,7 @@
      * Shows the hotseat area.
      */
     void showHotseat(boolean animated) {
-        if (!LauncherApplication.isScreenLarge()) {
+        if (!LauncherAppState.isScreenLarge()) {
             if (animated) {
                 if (mHotseat.getAlpha() != 1f) {
                     int duration = 0;
@@ -3025,7 +3025,7 @@
      * Hides the hotseat area.
      */
     void hideHotseat(boolean animated) {
-        if (!LauncherApplication.isScreenLarge()) {
+        if (!LauncherAppState.isScreenLarge()) {
             if (animated) {
                 if (mHotseat.getAlpha() != 0f) {
                     int duration = 0;
diff --git a/src/com/android/launcher3/LauncherAppState.java b/src/com/android/launcher3/LauncherAppState.java
new file mode 100644
index 0000000..fb49d93
--- /dev/null
+++ b/src/com/android/launcher3/LauncherAppState.java
@@ -0,0 +1,163 @@
+/*
+ * Copyright (C) 2013 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.launcher3;
+
+import android.app.SearchManager;
+import android.content.ContentResolver;
+import android.content.Context;
+import android.content.Intent;
+import android.content.IntentFilter;
+import android.content.res.Configuration;
+import android.database.ContentObserver;
+import android.os.Handler;
+
+import java.lang.ref.WeakReference;
+
+public class LauncherAppState {
+    private Context mContext;
+    private LauncherModel mModel;
+    private IconCache mIconCache;
+    private WidgetPreviewLoader.CacheDb mWidgetPreviewCacheDb;
+    private static boolean sIsScreenLarge;
+    private static float sScreenDensity;
+    private static int sLongPressTimeout = 300;
+    private static final String sSharedPreferencesKey = "com.android.launcher3.prefs";
+    WeakReference<LauncherProvider> mLauncherProvider;
+
+    private static LauncherAppState INSTANCE;
+
+    private static final LauncherAppState INSTANCE = new LauncherAppState();
+
+    public static LauncherAppState getInstance() {
+        return INSTANCE;
+    }
+
+    public static void init(Context context) {
+        INSTANCE.initialize(context);
+    }
+
+    public Context getContext() {
+        return mContext;
+    }
+
+    private LauncherAppState() { }
+
+    private void initialize(Context context) {
+        mContext = context;
+
+        // set sIsScreenXLarge and sScreenDensity *before* creating icon cache
+        sIsScreenLarge = context.getResources().getBoolean(R.bool.is_large_screen);
+        sScreenDensity = context.getResources().getDisplayMetrics().density;
+
+        mWidgetPreviewCacheDb = new WidgetPreviewLoader.CacheDb(context);
+        mIconCache = new IconCache(context);
+        mModel = new LauncherModel(context, mIconCache);
+
+        // Register intent receivers
+        IntentFilter filter = new IntentFilter(Intent.ACTION_PACKAGE_ADDED);
+        filter.addAction(Intent.ACTION_PACKAGE_REMOVED);
+        filter.addAction(Intent.ACTION_PACKAGE_CHANGED);
+        filter.addDataScheme("package");
+        context.registerReceiver(mModel, filter);
+        filter = new IntentFilter();
+        filter.addAction(Intent.ACTION_EXTERNAL_APPLICATIONS_AVAILABLE);
+        filter.addAction(Intent.ACTION_EXTERNAL_APPLICATIONS_UNAVAILABLE);
+        filter.addAction(Intent.ACTION_LOCALE_CHANGED);
+        filter.addAction(Intent.ACTION_CONFIGURATION_CHANGED);
+        context.registerReceiver(mModel, filter);
+        filter = new IntentFilter();
+        filter.addAction(SearchManager.INTENT_GLOBAL_SEARCH_ACTIVITY_CHANGED);
+        context.registerReceiver(mModel, filter);
+        filter = new IntentFilter();
+        filter.addAction(SearchManager.INTENT_ACTION_SEARCHABLES_CHANGED);
+        context.registerReceiver(mModel, filter);
+
+        // Register for changes to the favorites
+        ContentResolver resolver = context.getContentResolver();
+        resolver.registerContentObserver(LauncherSettings.Favorites.CONTENT_URI, true,
+                mFavoritesObserver);
+    }
+
+    /**
+     * Call from Application.onTerminate(), which is not guaranteed to ever be called.
+     */
+    public void onTerminate() {
+        mContext.unregisterReceiver(mModel);
+
+        ContentResolver resolver = mContext.getContentResolver();
+        resolver.unregisterContentObserver(mFavoritesObserver);
+    }
+
+    /**
+     * Receives notifications whenever the user favorites have changed.
+     */
+    private final ContentObserver mFavoritesObserver = new ContentObserver(new Handler()) {
+        @Override
+        public void onChange(boolean selfChange) {
+            // If the database has ever changed, then we really need to force a reload of the
+            // workspace on the next load
+            mModel.resetLoadedState(false, true);
+            mModel.startLoaderFromBackground();
+        }
+    };
+
+    LauncherModel setLauncher(Launcher launcher) {
+        mModel.initialize(launcher);
+        return mModel;
+    }
+
+    IconCache getIconCache() {
+        return mIconCache;
+    }
+
+    LauncherModel getModel() {
+        return mModel;
+    }
+
+    WidgetPreviewLoader.CacheDb getWidgetPreviewCacheDb() {
+        return mWidgetPreviewCacheDb;
+    }
+
+   void setLauncherProvider(LauncherProvider provider) {
+        mLauncherProvider = new WeakReference<LauncherProvider>(provider);
+    }
+
+    LauncherProvider getLauncherProvider() {
+        return mLauncherProvider.get();
+    }
+
+    public static String getSharedPreferencesKey() {
+        return sSharedPreferencesKey;
+    }
+
+    public static boolean isScreenLarge() {
+        return sIsScreenLarge;
+    }
+
+    public static boolean isScreenLandscape(Context context) {
+        return context.getResources().getConfiguration().orientation ==
+            Configuration.ORIENTATION_LANDSCAPE;
+    }
+
+    public static float getScreenDensity() {
+        return sScreenDensity;
+    }
+
+    public static int getLongPressTimeout() {
+        return sLongPressTimeout;
+    }
+}
diff --git a/src/com/android/launcher3/LauncherApplication.java b/src/com/android/launcher3/LauncherApplication.java
index 45e2425..647bf79 100644
--- a/src/com/android/launcher3/LauncherApplication.java
+++ b/src/com/android/launcher3/LauncherApplication.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2008 The Android Open Source Project
+ * Copyright (C) 2013 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.
@@ -26,126 +26,18 @@
 import android.database.ContentObserver;
 import android.os.Handler;
 
-import com.android.launcher3.R;
-
 import java.lang.ref.WeakReference;
 
 public class LauncherApplication extends Application {
-    private LauncherModel mModel;
-    private IconCache mIconCache;
-    private WidgetPreviewLoader.CacheDb mWidgetPreviewCacheDb;
-    private static boolean sIsScreenLarge;
-    private static float sScreenDensity;
-    private static int sLongPressTimeout = 300;
-    private static final String sSharedPreferencesKey = "com.android.launcher3.prefs";
-    WeakReference<LauncherProvider> mLauncherProvider;
-
     @Override
     public void onCreate() {
         super.onCreate();
-
-        // set sIsScreenXLarge and sScreenDensity *before* creating icon cache
-        sIsScreenLarge = getResources().getBoolean(R.bool.is_large_screen);
-        sScreenDensity = getResources().getDisplayMetrics().density;
-
-        mWidgetPreviewCacheDb = new WidgetPreviewLoader.CacheDb(this);
-        mIconCache = new IconCache(this);
-        mModel = new LauncherModel(this, mIconCache);
-
-        // Register intent receivers
-        IntentFilter filter = new IntentFilter(Intent.ACTION_PACKAGE_ADDED);
-        filter.addAction(Intent.ACTION_PACKAGE_REMOVED);
-        filter.addAction(Intent.ACTION_PACKAGE_CHANGED);
-        filter.addDataScheme("package");
-        registerReceiver(mModel, filter);
-        filter = new IntentFilter();
-        filter.addAction(Intent.ACTION_EXTERNAL_APPLICATIONS_AVAILABLE);
-        filter.addAction(Intent.ACTION_EXTERNAL_APPLICATIONS_UNAVAILABLE);
-        filter.addAction(Intent.ACTION_LOCALE_CHANGED);
-        filter.addAction(Intent.ACTION_CONFIGURATION_CHANGED);
-        registerReceiver(mModel, filter);
-        filter = new IntentFilter();
-        filter.addAction(SearchManager.INTENT_GLOBAL_SEARCH_ACTIVITY_CHANGED);
-        registerReceiver(mModel, filter);
-        filter = new IntentFilter();
-        filter.addAction(SearchManager.INTENT_ACTION_SEARCHABLES_CHANGED);
-        registerReceiver(mModel, filter);
-
-        // Register for changes to the favorites
-        ContentResolver resolver = getContentResolver();
-        resolver.registerContentObserver(LauncherSettings.Favorites.CONTENT_URI, true,
-                mFavoritesObserver);
+        LauncherAppState.getInstance().init(getApplicationContext());
     }
 
-    /**
-     * There's no guarantee that this function is ever called.
-     */
     @Override
     public void onTerminate() {
         super.onTerminate();
-
-        unregisterReceiver(mModel);
-
-        ContentResolver resolver = getContentResolver();
-        resolver.unregisterContentObserver(mFavoritesObserver);
+        LauncherAppState.getInstance().onTerminate();
     }
-
-    /**
-     * Receives notifications whenever the user favorites have changed.
-     */
-    private final ContentObserver mFavoritesObserver = new ContentObserver(new Handler()) {
-        @Override
-        public void onChange(boolean selfChange) {
-            // If the database has ever changed, then we really need to force a reload of the
-            // workspace on the next load
-            mModel.resetLoadedState(false, true);
-            mModel.startLoaderFromBackground();
-        }
-    };
-
-    LauncherModel setLauncher(Launcher launcher) {
-        mModel.initialize(launcher);
-        return mModel;
-    }
-
-    IconCache getIconCache() {
-        return mIconCache;
-    }
-
-    LauncherModel getModel() {
-        return mModel;
-    }
-
-    WidgetPreviewLoader.CacheDb getWidgetPreviewCacheDb() {
-        return mWidgetPreviewCacheDb;
-    }
-
-   void setLauncherProvider(LauncherProvider provider) {
-        mLauncherProvider = new WeakReference<LauncherProvider>(provider);
-    }
-
-    LauncherProvider getLauncherProvider() {
-        return mLauncherProvider.get();
-    }
-
-    public static String getSharedPreferencesKey() {
-        return sSharedPreferencesKey;
-    }
-
-    public static boolean isScreenLarge() {
-        return sIsScreenLarge;
-    }
-
-    public static boolean isScreenLandscape(Context context) {
-        return context.getResources().getConfiguration().orientation ==
-            Configuration.ORIENTATION_LANDSCAPE;
-    }
-
-    public static float getScreenDensity() {
-        return sScreenDensity;
-    }
-
-    public static int getLongPressTimeout() {
-        return sLongPressTimeout;
-    }
-}
+}
\ No newline at end of file
diff --git a/src/com/android/launcher3/LauncherModel.java b/src/com/android/launcher3/LauncherModel.java
index bde4f7c..2e76a65 100644
--- a/src/com/android/launcher3/LauncherModel.java
+++ b/src/com/android/launcher3/LauncherModel.java
@@ -78,7 +78,7 @@
     private int mBatchSize; // 0 is all apps at once
     private int mAllAppsLoadDelay; // milliseconds between batches
 
-    private final LauncherApplication mApp;
+    private final LauncherAppState mApp;
     private final Object mLock = new Object();
     private DeferredHandler mHandler = new DeferredHandler();
     private LoaderTask mLoaderTask;
@@ -169,16 +169,16 @@
         public void onPageBoundSynchronously(int page);
     }
 
-    LauncherModel(LauncherApplication app, IconCache iconCache) {
+    LauncherModel(Context context, IconCache iconCache) {
         mAppsCanBeOnExternalStorage = !Environment.isExternalStorageEmulated();
-        mApp = app;
+        mApp = LauncherAppState.getInstance();
         mBgAllAppsList = new AllAppsList(iconCache);
         mIconCache = iconCache;
 
         mDefaultIcon = Utilities.createIconBitmap(
-                mIconCache.getFullResDefaultActivityIcon(), app);
+                mIconCache.getFullResDefaultActivityIcon(), context);
 
-        final Resources res = app.getResources();
+        final Resources res = context.getResources();
         mAllAppsLoadDelay = res.getInteger(R.integer.config_allAppsBatchLoadDelay);
         mBatchSize = res.getInteger(R.integer.config_allAppsBatchSize);
         Configuration config = res.getConfiguration();
@@ -681,7 +681,7 @@
         final ContentResolver cr = context.getContentResolver();
         item.onAddToDatabase(values);
 
-        LauncherApplication app = (LauncherApplication) context.getApplicationContext();
+        LauncherAppState app = LauncherAppState.getInstance();
         item.id = app.getLauncherProvider().generateNewId();
         values.put(LauncherSettings.Favorites._ID, item.id);
         item.updateValuesWithCoordinates(values, item.cellX, item.cellY);
@@ -997,7 +997,7 @@
                 // If there is already one running, tell it to stop.
                 // also, don't downgrade isLaunching if we're already running
                 isLaunching = isLaunching || stopLoaderLocked();
-                mLoaderTask = new LoaderTask(mApp, isLaunching);
+                mLoaderTask = new LoaderTask(mApp.getContext(), isLaunching);
                 if (synchronousBindPage > -1 && mAllAppsLoaded && mWorkspaceLoaded) {
                     mLoaderTask.runBindSynchronousPage(synchronousBindPage);
                 } else {
@@ -1150,7 +1150,7 @@
             //      data structures, we can't allow any other thread to touch that data, but because
             //      this call is synchronous, we can get away with not locking).
 
-            // The LauncherModel is static in the LauncherApplication and mHandler may have queued
+            // The LauncherModel is static in the LauncherAppState and mHandler may have queued
             // operations from the previous activity.  We need to ensure that all queued operations
             // are executed before any synchronous binding work is done.
             mHandler.flush();
@@ -2108,7 +2108,7 @@
         }
 
         public void run() {
-            final Context context = mApp;
+            final Context context = mApp.getContext();
 
             final String[] packages = mPackages;
             final int N = packages.length;
@@ -2123,8 +2123,8 @@
                     for (int i=0; i<N; i++) {
                         if (DEBUG_LOADERS) Log.d(TAG, "mAllAppsList.updatePackage " + packages[i]);
                         mBgAllAppsList.updatePackage(context, packages[i]);
-                        LauncherApplication app =
-                                (LauncherApplication) context.getApplicationContext();
+                        LauncherAppState app =
+                                LauncherAppState.getInstance();
                         WidgetPreviewLoader.removeFromDb(
                                 app.getWidgetPreviewCacheDb(), packages[i]);
                     }
@@ -2134,8 +2134,8 @@
                     for (int i=0; i<N; i++) {
                         if (DEBUG_LOADERS) Log.d(TAG, "mAllAppsList.removePackage " + packages[i]);
                         mBgAllAppsList.removePackage(packages[i]);
-                        LauncherApplication app =
-                                (LauncherApplication) context.getApplicationContext();
+                        LauncherAppState app =
+                                LauncherAppState.getInstance();
                         WidgetPreviewLoader.removeFromDb(
                                 app.getWidgetPreviewCacheDb(), packages[i]);
                     }
diff --git a/src/com/android/launcher3/LauncherProvider.java b/src/com/android/launcher3/LauncherProvider.java
index 4c80b6b..f971a37 100644
--- a/src/com/android/launcher3/LauncherProvider.java
+++ b/src/com/android/launcher3/LauncherProvider.java
@@ -94,7 +94,7 @@
     @Override
     public boolean onCreate() {
         mOpenHelper = new DatabaseHelper(getContext());
-        ((LauncherApplication) getContext()).setLauncherProvider(this);
+        LauncherAppState.getInstance().setLauncherProvider(this);
         return true;
     }
 
@@ -210,7 +210,7 @@
      * @param Should we load the old db for upgrade? first run only.
      */
     synchronized public boolean shouldLoadOldDb() {
-        String spKey = LauncherApplication.getSharedPreferencesKey();
+        String spKey = LauncherAppState.getSharedPreferencesKey();
         SharedPreferences sp = getContext().getSharedPreferences(spKey, Context.MODE_PRIVATE);
 
         boolean loadOldDb = false;
@@ -228,7 +228,7 @@
      * @param workspaceResId that can be 0 to use default or non-zero for specific resource
      */
     synchronized public void loadDefaultFavoritesIfNecessary(int origWorkspaceResId) {
-        String spKey = LauncherApplication.getSharedPreferencesKey();
+        String spKey = LauncherAppState.getSharedPreferencesKey();
         SharedPreferences sp = getContext().getSharedPreferences(spKey, Context.MODE_PRIVATE);
 
         if (sp.getBoolean(DB_CREATED_BUT_DEFAULT_WORKSPACE_NOT_LOADED, false)) {
@@ -328,7 +328,7 @@
         }
 
         private void setFlagToLoadDefaultWorkspaceLater() {
-            String spKey = LauncherApplication.getSharedPreferencesKey();
+            String spKey = LauncherAppState.getSharedPreferencesKey();
             SharedPreferences sp = mContext.getSharedPreferences(spKey, Context.MODE_PRIVATE);
             SharedPreferences.Editor editor = sp.edit();
             editor.putBoolean(DB_CREATED_BUT_DEFAULT_WORKSPACE_NOT_LOADED, true);
diff --git a/src/com/android/launcher3/PackageChangedReceiver.java b/src/com/android/launcher3/PackageChangedReceiver.java
index ded01a6..1a8ec7f 100644
--- a/src/com/android/launcher3/PackageChangedReceiver.java
+++ b/src/com/android/launcher3/PackageChangedReceiver.java
@@ -13,7 +13,7 @@
             // they sent us a bad intent
             return;
         }
-        LauncherApplication app = (LauncherApplication) context.getApplicationContext();
+        LauncherAppState app = LauncherAppState.getInstance();
         WidgetPreviewLoader.removeFromDb(app.getWidgetPreviewCacheDb(), packageName);
     }
 }
diff --git a/src/com/android/launcher3/PagedViewCellLayout.java b/src/com/android/launcher3/PagedViewCellLayout.java
index 177425a..423b1cf 100644
--- a/src/com/android/launcher3/PagedViewCellLayout.java
+++ b/src/com/android/launcher3/PagedViewCellLayout.java
@@ -472,7 +472,7 @@
             height = myCellVSpan * cellHeight + ((myCellVSpan - 1) * heightGap) -
                     topMargin - bottomMargin;
 
-            if (LauncherApplication.isScreenLarge()) {
+            if (LauncherAppState.isScreenLarge()) {
                 x = hStartPadding + myCellX * (cellWidth + widthGap) + leftMargin;
                 y = vStartPadding + myCellY * (cellHeight + heightGap) + topMargin;
             } else {
diff --git a/src/com/android/launcher3/PreloadReceiver.java b/src/com/android/launcher3/PreloadReceiver.java
index ee34348..4c9032f 100644
--- a/src/com/android/launcher3/PreloadReceiver.java
+++ b/src/com/android/launcher3/PreloadReceiver.java
@@ -31,7 +31,7 @@
 
     @Override
     public void onReceive(Context context, Intent intent) {
-        final LauncherApplication app = (LauncherApplication) context.getApplicationContext();
+        final LauncherAppState app = LauncherAppState.getInstance();
         final LauncherProvider provider = app.getLauncherProvider();
         if (provider != null) {
             String name = intent.getStringExtra(EXTRA_WORKSPACE_NAME);
diff --git a/src/com/android/launcher3/UninstallShortcutReceiver.java b/src/com/android/launcher3/UninstallShortcutReceiver.java
index 6bc289a..eb43fc9 100644
--- a/src/com/android/launcher3/UninstallShortcutReceiver.java
+++ b/src/com/android/launcher3/UninstallShortcutReceiver.java
@@ -81,12 +81,12 @@
 
     private static void processUninstallShortcut(Context context,
             PendingUninstallShortcutInfo pendingInfo) {
-        String spKey = LauncherApplication.getSharedPreferencesKey();
+        String spKey = LauncherAppState.getSharedPreferencesKey();
         SharedPreferences sharedPrefs = context.getSharedPreferences(spKey, Context.MODE_PRIVATE);
 
         final Intent data = pendingInfo.data;
 
-        LauncherApplication app = (LauncherApplication) context.getApplicationContext();
+        LauncherAppState app = LauncherAppState.getInstance();
         synchronized (app) {
             removeShortcut(context, data, sharedPrefs);
         }
diff --git a/src/com/android/launcher3/WidgetPreviewLoader.java b/src/com/android/launcher3/WidgetPreviewLoader.java
index ddc478a..a487f8a 100644
--- a/src/com/android/launcher3/WidgetPreviewLoader.java
+++ b/src/com/android/launcher3/WidgetPreviewLoader.java
@@ -143,7 +143,7 @@
         mContext = mLauncher = launcher;
         mPackageManager = mContext.getPackageManager();
         mAppIconSize = mContext.getResources().getDimensionPixelSize(R.dimen.app_icon_size);
-        LauncherApplication app = (LauncherApplication) launcher.getApplicationContext();
+        LauncherAppState app = LauncherAppState.getInstance();
         mIconCache = app.getIconCache();
         mDb = app.getWidgetPreviewCacheDb();
         mLoadedPreviews = new HashMap<String, WeakReference<Bitmap>>();
diff --git a/src/com/android/launcher3/Workspace.java b/src/com/android/launcher3/Workspace.java
index d600e9e..a44ee92 100644
--- a/src/com/android/launcher3/Workspace.java
+++ b/src/com/android/launcher3/Workspace.java
@@ -297,7 +297,7 @@
         TypedArray a = context.obtainStyledAttributes(attrs,
                 R.styleable.Workspace, defStyle, 0);
 
-        if (LauncherApplication.isScreenLarge()) {
+        if (LauncherAppState.isScreenLarge()) {
             // Determine number of rows/columns dynamically
             // TODO: This code currently fails on tablets with an aspect ratio < 1.3.
             // Around that ratio we should make cells the same size in portrait and
@@ -405,7 +405,7 @@
         Context context = getContext();
         mCurrentPage = mDefaultPage;
         Launcher.setScreen(mCurrentPage);
-        LauncherApplication app = (LauncherApplication)context.getApplicationContext();
+        LauncherAppState app = LauncherAppState.getInstance();
         mIconCache = app.getIconCache();
         setWillNotDraw(false);
         setClipChildren(false);
@@ -600,7 +600,7 @@
 
         // Only allow tap to next page on large devices, where there's significant margin outside
         // the active workspace
-        return LauncherApplication.isScreenLarge() && hitsPage(current - 1, x, y);
+        return LauncherAppState.isScreenLarge() && hitsPage(current - 1, x, y);
     }
 
     @Override
@@ -611,7 +611,7 @@
 
         // Only allow tap to next page on large devices, where there's significant margin outside
         // the active workspace
-        return LauncherApplication.isScreenLarge() && hitsPage(current + 1, x, y);
+        return LauncherAppState.isScreenLarge() && hitsPage(current + 1, x, y);
     }
 
     /**
@@ -746,7 +746,7 @@
         }
 
         // Only show page outlines as we pan if we are on large screen
-        if (LauncherApplication.isScreenLarge()) {
+        if (LauncherAppState.isScreenLarge()) {
             showOutlines();
             mIsStaticWallpaper = mWallpaperManager.getWallpaperInfo() == null;
         }
@@ -781,7 +781,7 @@
             }
         } else {
             // If we are not mid-dragging, hide the page outlines if we are on a large screen
-            if (LauncherApplication.isScreenLarge()) {
+            if (LauncherAppState.isScreenLarge()) {
                 hideOutlines();
             }
 
@@ -850,7 +850,7 @@
 
         // We need to ensure that there is enough extra space in the wallpaper for the intended
         // parallax effects
-        if (LauncherApplication.isScreenLarge()) {
+        if (LauncherAppState.isScreenLarge()) {
             mWallpaperWidth = (int) (maxDim * wallpaperTravelToScreenWidthRatio(maxDim, minDim));
             mWallpaperHeight = maxDim;
         } else {
@@ -883,7 +883,7 @@
         float scrollProgress =
             adjustedScrollX / (float) scrollRange;
 
-        if (LauncherApplication.isScreenLarge() && mIsStaticWallpaper) {
+        if (LauncherAppState.isScreenLarge() && mIsStaticWallpaper) {
             // The wallpaper travel width is how far, from left to right, the wallpaper will move
             // at this orientation. On tablets in portrait mode we don't move all the way to the
             // edges of the wallpaper, or otherwise the parallax effect would be too strong.
@@ -1038,7 +1038,7 @@
                 Math.abs(vOffsetDelta) < UPDATE_THRESHOLD;
 
             // Don't have any lag between workspace and wallpaper on non-large devices
-            if (!LauncherApplication.isScreenLarge() || jumpToFinalValue) {
+            if (!LauncherAppState.isScreenLarge() || jumpToFinalValue) {
                 mHorizontalWallpaperOffset = mFinalHorizontalWallpaperOffset;
                 mVerticalWallpaperOffset = mFinalVerticalWallpaperOffset;
             } else {
@@ -2400,7 +2400,7 @@
 
         // Because we don't have space in the Phone UI (the CellLayouts run to the edge) we
         // don't need to show the outlines
-        if (LauncherApplication.isScreenLarge()) {
+        if (LauncherAppState.isScreenLarge()) {
             showOutlines();
         }
     }
@@ -3587,7 +3587,7 @@
     @Override
     public boolean onEnterScrollArea(int x, int y, int direction) {
         // Ignore the scroll area if we are dragging over the hot seat
-        boolean isPortrait = !LauncherApplication.isScreenLandscape(getContext());
+        boolean isPortrait = !LauncherAppState.isScreenLandscape(getContext());
         if (mLauncher.getHotseat() != null && isPortrait) {
             Rect r = new Rect();
             mLauncher.getHotseat().getHitRect(r);
@@ -3876,7 +3876,7 @@
         post(new Runnable() {
             @Override
             public void run() {
-                String spKey = LauncherApplication.getSharedPreferencesKey();
+                String spKey = LauncherAppState.getSharedPreferencesKey();
                 SharedPreferences sp = context.getSharedPreferences(spKey,
                         Context.MODE_PRIVATE);
                 Set<String> newApps = sp.getStringSet(InstallShortcutReceiver.NEW_APPS_LIST_KEY,