Lazily instantiate LauncherAppState.

The application context for LauncherAppState is supplied by
the application whenever it starts; don't ask for an
instance before that.

Change-Id: I1ca8ea04238a357a682f79250f08813ead7ae532
diff --git a/src/com/android/launcher3/AppsCustomizePagedView.java b/src/com/android/launcher3/AppsCustomizePagedView.java
index 0d088ac..9bc1e74 100644
--- a/src/com/android/launcher3/AppsCustomizePagedView.java
+++ b/src/com/android/launcher3/AppsCustomizePagedView.java
@@ -23,7 +23,6 @@
 import android.appwidget.AppWidgetProviderInfo;
 import android.content.ComponentName;
 import android.content.Context;
-import android.content.Intent;
 import android.content.pm.PackageManager;
 import android.content.pm.ResolveInfo;
 import android.content.res.Configuration;
@@ -51,7 +50,6 @@
 import android.widget.ImageView;
 import android.widget.Toast;
 
-import com.android.launcher3.R;
 import com.android.launcher3.DropTarget.DragObject;
 
 import java.util.ArrayList;
@@ -362,7 +360,7 @@
             Configuration.ORIENTATION_LANDSCAPE;
         int maxCellCountX = Integer.MAX_VALUE;
         int maxCellCountY = Integer.MAX_VALUE;
-        if (LauncherAppState.isScreenLarge()) {
+        if (LauncherAppState.getInstance().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 6a2f130..51d2fba 100644
--- a/src/com/android/launcher3/AppsCustomizeTabHost.java
+++ b/src/com/android/launcher3/AppsCustomizeTabHost.java
@@ -33,8 +33,6 @@
 import android.widget.TabWidget;
 import android.widget.TextView;
 
-import com.android.launcher3.R;
-
 import java.util.ArrayList;
 
 public class AppsCustomizeTabHost extends TabHost implements LauncherTransitionable,
@@ -184,7 +182,7 @@
     }
 
     private void reloadCurrentPage() {
-        if (!LauncherAppState.isScreenLarge()) {
+        if (!LauncherAppState.getInstance().isScreenLarge()) {
             mAppsCustomizePane.flashScrollingIndicator(true);
         }
         mAppsCustomizePane.loadAssociatedPages(mAppsCustomizePane.getCurrentPage());
@@ -383,7 +381,7 @@
             // transition to prevent slowing down the animation)
             mAppsCustomizePane.loadAssociatedPages(mAppsCustomizePane.getCurrentPage(), true);
 
-            if (!LauncherAppState.isScreenLarge()) {
+            if (!LauncherAppState.getInstance().isScreenLarge()) {
                 mAppsCustomizePane.showScrollingIndicator(true);
             }
         }
@@ -423,7 +421,7 @@
             // prevent slowing down the animation)
             mAppsCustomizePane.loadAssociatedPages(mAppsCustomizePane.getCurrentPage());
 
-            if (!LauncherAppState.isScreenLarge()) {
+            if (!LauncherAppState.getInstance().isScreenLarge()) {
                 mAppsCustomizePane.hideScrollingIndicator(false);
             }
 
diff --git a/src/com/android/launcher3/BubbleTextView.java b/src/com/android/launcher3/BubbleTextView.java
index 7c7d93e..868e3ac 100644
--- a/src/com/android/launcher3/BubbleTextView.java
+++ b/src/com/android/launcher3/BubbleTextView.java
@@ -45,7 +45,7 @@
 
     private int mPrevAlpha = -1;
 
-    private final HolographicOutlineHelper mOutlineHelper = HolographicOutlineHelper.obtain();
+    private HolographicOutlineHelper mOutlineHelper;
     private final Canvas mTempCanvas = new Canvas();
     private final Rect mTempRect = new Rect();
     private boolean mDidInvalidateForPressedState;
@@ -80,6 +80,8 @@
         mLongPressHelper = new CheckLongPressHelper(this);
         mBackground = getBackground();
 
+        mOutlineHelper = HolographicOutlineHelper.obtain(getContext());
+
         final Resources res = getContext().getResources();
         mFocusedOutlineColor = mFocusedGlowColor = mPressedOutlineColor = mPressedGlowColor =
             res.getColor(android.R.color.holo_blue_light);
@@ -189,7 +191,7 @@
      * Responsibility for the bitmap is transferred to the caller.
      */
     private Bitmap createGlowingOutline(Canvas canvas, int outlineColor, int glowColor) {
-        final int padding = HolographicOutlineHelper.MAX_OUTER_BLUR_RADIUS;
+        final int padding = mOutlineHelper.mMaxOuterBlurRadius;
         final Bitmap b = Bitmap.createBitmap(
                 getWidth() + padding, getHeight() + padding, Bitmap.Config.ARGB_8888);
 
@@ -269,7 +271,7 @@
     }
 
     int getPressedOrFocusedBackgroundPadding() {
-        return HolographicOutlineHelper.MAX_OUTER_BLUR_RADIUS / 2;
+        return mOutlineHelper.mMaxOuterBlurRadius / 2;
     }
 
     @Override
diff --git a/src/com/android/launcher3/CheckLongPressHelper.java b/src/com/android/launcher3/CheckLongPressHelper.java
index 968dc79..8114979 100644
--- a/src/com/android/launcher3/CheckLongPressHelper.java
+++ b/src/com/android/launcher3/CheckLongPressHelper.java
@@ -45,7 +45,8 @@
         if (mPendingCheckForLongPress == null) {
             mPendingCheckForLongPress = new CheckForLongPress();
         }
-        mView.postDelayed(mPendingCheckForLongPress, LauncherAppState.getLongPressTimeout());
+        mView.postDelayed(mPendingCheckForLongPress,
+                LauncherAppState.getInstance().getLongPressTimeout());
     }
 
     public void cancelLongPress() {
diff --git a/src/com/android/launcher3/Cling.java b/src/com/android/launcher3/Cling.java
index 9326e38..77c41b7 100644
--- a/src/com/android/launcher3/Cling.java
+++ b/src/com/android/launcher3/Cling.java
@@ -33,8 +33,6 @@
 import android.view.View;
 import android.widget.FrameLayout;
 
-import com.android.launcher3.R;
-
 public class Cling extends FrameLayout {
 
     static final String WORKSPACE_CLING_DISMISSED_KEY = "cling.workspace.dismissed";
@@ -126,7 +124,7 @@
         } else if (mDrawIdentifier.equals(WORKSPACE_LANDSCAPE)) {
             return new int[]{getMeasuredWidth() - (mButtonBarHeight / 2), getMeasuredHeight() / 2};
         } else if (mDrawIdentifier.equals(WORKSPACE_LARGE)) {
-            final float scale = LauncherAppState.getScreenDensity();
+            final float scale = LauncherAppState.getInstance().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 0ccdae1..4be9b81 100644
--- a/src/com/android/launcher3/DeleteDropTarget.java
+++ b/src/com/android/launcher3/DeleteDropTarget.java
@@ -34,8 +34,6 @@
 import android.view.animation.DecelerateInterpolator;
 import android.view.animation.LinearInterpolator;
 
-import com.android.launcher3.R;
-
 public class DeleteDropTarget extends ButtonDropTarget {
     private static int DELETE_ANIMATION_DURATION = 285;
     private static int FLING_DELETE_ANIMATION_DURATION = 350;
@@ -82,7 +80,7 @@
         // Remove the text in the Phone UI in landscape
         int orientation = getResources().getConfiguration().orientation;
         if (orientation == Configuration.ORIENTATION_LANDSCAPE) {
-            if (!LauncherAppState.isScreenLarge()) {
+            if (!LauncherAppState.getInstance().isScreenLarge()) {
                 setText("");
             }
         }
diff --git a/src/com/android/launcher3/DragLayer.java b/src/com/android/launcher3/DragLayer.java
index 3bf54e8..b02f803 100644
--- a/src/com/android/launcher3/DragLayer.java
+++ b/src/com/android/launcher3/DragLayer.java
@@ -34,14 +34,11 @@
 import android.view.ViewParent;
 import android.view.accessibility.AccessibilityEvent;
 import android.view.accessibility.AccessibilityManager;
-import android.view.accessibility.AccessibilityNodeInfo;
 import android.view.animation.DecelerateInterpolator;
 import android.view.animation.Interpolator;
 import android.widget.FrameLayout;
 import android.widget.TextView;
 
-import com.android.launcher3.R;
-
 import java.util.ArrayList;
 
 /**
@@ -779,7 +776,7 @@
     protected void dispatchDraw(Canvas canvas) {
         super.dispatchDraw(canvas);
 
-        if (mInScrollArea && !LauncherAppState.isScreenLarge()) {
+        if (mInScrollArea && !LauncherAppState.getInstance().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 6796633..a591433 100644
--- a/src/com/android/launcher3/FocusHelper.java
+++ b/src/com/android/launcher3/FocusHelper.java
@@ -24,8 +24,6 @@
 import android.widget.TabHost;
 import android.widget.TabWidget;
 
-import com.android.launcher3.R;
-
 import java.util.ArrayList;
 import java.util.Collections;
 import java.util.Comparator;
@@ -437,7 +435,7 @@
      * Handles key events in the tab widget.
      */
     static boolean handleTabKeyEvent(AccessibleTabView v, int keyCode, KeyEvent e) {
-        if (!LauncherAppState.isScreenLarge()) return false;
+        if (!LauncherAppState.getInstance().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 be511fb..36b6bf4 100644
--- a/src/com/android/launcher3/Folder.java
+++ b/src/com/android/launcher3/Folder.java
@@ -20,7 +20,6 @@
 import android.animation.AnimatorListenerAdapter;
 import android.animation.ObjectAnimator;
 import android.animation.PropertyValuesHolder;
-import android.content.ComponentName;
 import android.content.Context;
 import android.content.res.Resources;
 import android.graphics.PointF;
@@ -48,7 +47,6 @@
 import android.widget.ScrollView;
 import android.widget.TextView;
 
-import com.android.launcher3.R;
 import com.android.launcher3.FolderInfo.FolderListener;
 
 import java.util.ArrayList;
diff --git a/src/com/android/launcher3/HolographicOutlineHelper.java b/src/com/android/launcher3/HolographicOutlineHelper.java
index 47c40b6..d7b960a 100644
--- a/src/com/android/launcher3/HolographicOutlineHelper.java
+++ b/src/com/android/launcher3/HolographicOutlineHelper.java
@@ -16,6 +16,7 @@
 
 package com.android.launcher3;
 
+import android.content.Context;
 import android.graphics.Bitmap;
 import android.graphics.BlurMaskFilter;
 import android.graphics.Canvas;
@@ -28,16 +29,16 @@
     private final Paint mBlurPaint = new Paint();
     private final Paint mErasePaint = new Paint();
 
-    public static final int MAX_OUTER_BLUR_RADIUS;
-    public static final int MIN_OUTER_BLUR_RADIUS;
+    public int mMaxOuterBlurRadius;
+    public int mMinOuterBlurRadius;
 
-    private static final BlurMaskFilter sExtraThickOuterBlurMaskFilter;
-    private static final BlurMaskFilter sThickOuterBlurMaskFilter;
-    private static final BlurMaskFilter sMediumOuterBlurMaskFilter;
-    private static final BlurMaskFilter sThinOuterBlurMaskFilter;
-    private static final BlurMaskFilter sThickInnerBlurMaskFilter;
-    private static final BlurMaskFilter sExtraThickInnerBlurMaskFilter;
-    private static final BlurMaskFilter sMediumInnerBlurMaskFilter;
+    private BlurMaskFilter mExtraThickOuterBlurMaskFilter;
+    private BlurMaskFilter mThickOuterBlurMaskFilter;
+    private BlurMaskFilter mMediumOuterBlurMaskFilter;
+    private BlurMaskFilter mThinOuterBlurMaskFilter;
+    private BlurMaskFilter mThickInnerBlurMaskFilter;
+    private BlurMaskFilter mExtraThickInnerBlurMaskFilter;
+    private BlurMaskFilter mMediumInnerBlurMaskFilter;
 
     private static final int THICK = 0;
     private static final int MEDIUM = 1;
@@ -45,24 +46,20 @@
 
     static HolographicOutlineHelper INSTANCE;
 
-    static {
-        final float scale = LauncherAppState.getScreenDensity();
+    private HolographicOutlineHelper(Context context) {
+        final float scale = LauncherAppState.getInstance().getScreenDensity();
 
-        MIN_OUTER_BLUR_RADIUS = (int) (scale * 1.0f);
-        MAX_OUTER_BLUR_RADIUS = (int) (scale * 12.0f);
+        mMinOuterBlurRadius = (int) (scale * 1.0f);
+        mMaxOuterBlurRadius = (int) (scale * 12.0f);
 
-        sExtraThickOuterBlurMaskFilter = new BlurMaskFilter(scale * 12.0f, BlurMaskFilter.Blur.OUTER);
-        sThickOuterBlurMaskFilter = new BlurMaskFilter(scale * 6.0f, BlurMaskFilter.Blur.OUTER);
-        sMediumOuterBlurMaskFilter = new BlurMaskFilter(scale * 2.0f, BlurMaskFilter.Blur.OUTER);
-        sThinOuterBlurMaskFilter = new BlurMaskFilter(scale * 1.0f, BlurMaskFilter.Blur.OUTER);
-        sExtraThickInnerBlurMaskFilter = new BlurMaskFilter(scale * 6.0f, BlurMaskFilter.Blur.NORMAL);
-        sThickInnerBlurMaskFilter = new BlurMaskFilter(scale * 4.0f, BlurMaskFilter.Blur.NORMAL);
-        sMediumInnerBlurMaskFilter = new BlurMaskFilter(scale * 2.0f, BlurMaskFilter.Blur.NORMAL);
+        mExtraThickOuterBlurMaskFilter = new BlurMaskFilter(scale * 12.0f, BlurMaskFilter.Blur.OUTER);
+        mThickOuterBlurMaskFilter = new BlurMaskFilter(scale * 6.0f, BlurMaskFilter.Blur.OUTER);
+        mMediumOuterBlurMaskFilter = new BlurMaskFilter(scale * 2.0f, BlurMaskFilter.Blur.OUTER);
+        mThinOuterBlurMaskFilter = new BlurMaskFilter(scale * 1.0f, BlurMaskFilter.Blur.OUTER);
+        mExtraThickInnerBlurMaskFilter = new BlurMaskFilter(scale * 6.0f, BlurMaskFilter.Blur.NORMAL);
+        mThickInnerBlurMaskFilter = new BlurMaskFilter(scale * 4.0f, BlurMaskFilter.Blur.NORMAL);
+        mMediumInnerBlurMaskFilter = new BlurMaskFilter(scale * 2.0f, BlurMaskFilter.Blur.NORMAL);
 
-        INSTANCE = new HolographicOutlineHelper();
-    }
-
-    private HolographicOutlineHelper() {
         mHolographicPaint.setFilterBitmap(true);
         mHolographicPaint.setAntiAlias(true);
         mBlurPaint.setFilterBitmap(true);
@@ -72,7 +69,10 @@
         mErasePaint.setAntiAlias(true);
     }
 
-    public static HolographicOutlineHelper obtain() {
+    public static HolographicOutlineHelper obtain(Context context) {
+        if (INSTANCE == null) {
+            INSTANCE = new HolographicOutlineHelper(context);
+        }
         return INSTANCE;
     }
 
@@ -130,13 +130,13 @@
         BlurMaskFilter outerBlurMaskFilter;
         switch (thickness) {
             case EXTRA_THICK:
-                outerBlurMaskFilter = sExtraThickOuterBlurMaskFilter;
+                outerBlurMaskFilter = mExtraThickOuterBlurMaskFilter;
                 break;
             case THICK:
-                outerBlurMaskFilter = sThickOuterBlurMaskFilter;
+                outerBlurMaskFilter = mThickOuterBlurMaskFilter;
                 break;
             case MEDIUM:
-                outerBlurMaskFilter = sMediumOuterBlurMaskFilter;
+                outerBlurMaskFilter = mMediumOuterBlurMaskFilter;
                 break;
             default:
                 throw new RuntimeException("Invalid blur thickness");
@@ -145,9 +145,9 @@
         int[] outerBlurOffset = new int[2];
         Bitmap thickOuterBlur = glowShape.extractAlpha(mBlurPaint, outerBlurOffset);
         if (thickness == EXTRA_THICK) {
-            mBlurPaint.setMaskFilter(sMediumOuterBlurMaskFilter);
+            mBlurPaint.setMaskFilter(mMediumOuterBlurMaskFilter);
         } else {
-            mBlurPaint.setMaskFilter(sThinOuterBlurMaskFilter);
+            mBlurPaint.setMaskFilter(mThinOuterBlurMaskFilter);
         }
 
         int[] brightOutlineOffset = new int[2];
@@ -159,13 +159,13 @@
         BlurMaskFilter innerBlurMaskFilter;
         switch (thickness) {
             case EXTRA_THICK:
-                innerBlurMaskFilter = sExtraThickInnerBlurMaskFilter;
+                innerBlurMaskFilter = mExtraThickInnerBlurMaskFilter;
                 break;
             case THICK:
-                innerBlurMaskFilter = sThickInnerBlurMaskFilter;
+                innerBlurMaskFilter = mThickInnerBlurMaskFilter;
                 break;
             case MEDIUM:
-                innerBlurMaskFilter = sMediumInnerBlurMaskFilter;
+                innerBlurMaskFilter = mMediumInnerBlurMaskFilter;
                 break;
             default:
                 throw new RuntimeException("Invalid blur thickness");
diff --git a/src/com/android/launcher3/InfoDropTarget.java b/src/com/android/launcher3/InfoDropTarget.java
index 4db8318..a643a0d 100644
--- a/src/com/android/launcher3/InfoDropTarget.java
+++ b/src/com/android/launcher3/InfoDropTarget.java
@@ -26,8 +26,6 @@
 import android.view.View;
 import android.view.ViewGroup;
 
-import com.android.launcher3.R;
-
 public class InfoDropTarget extends ButtonDropTarget {
 
     private ColorStateList mOriginalTextColor;
@@ -58,7 +56,7 @@
         // Remove the text in the Phone UI in landscape
         int orientation = getResources().getConfiguration().orientation;
         if (orientation == Configuration.ORIENTATION_LANDSCAPE) {
-            if (!LauncherAppState.isScreenLarge()) {
+            if (!LauncherAppState.getInstance().isScreenLarge()) {
                 setText("");
             }
         }
diff --git a/src/com/android/launcher3/InstallShortcutReceiver.java b/src/com/android/launcher3/InstallShortcutReceiver.java
index 3bc7be4..f97ed53 100644
--- a/src/com/android/launcher3/InstallShortcutReceiver.java
+++ b/src/com/android/launcher3/InstallShortcutReceiver.java
@@ -28,8 +28,6 @@
 import android.util.Log;
 import android.widget.Toast;
 
-import com.android.launcher3.R;
-
 import java.util.ArrayList;
 import java.util.HashSet;
 import java.util.Iterator;
diff --git a/src/com/android/launcher3/Launcher.java b/src/com/android/launcher3/Launcher.java
index c216572..40e44d1 100644
--- a/src/com/android/launcher3/Launcher.java
+++ b/src/com/android/launcher3/Launcher.java
@@ -44,17 +44,14 @@
 import android.content.SharedPreferences;
 import android.content.pm.ActivityInfo;
 import android.content.pm.PackageManager;
-import android.content.pm.ResolveInfo;
 import android.content.pm.PackageManager.NameNotFoundException;
 import android.content.res.Configuration;
 import android.content.res.Resources;
 import android.database.ContentObserver;
 import android.graphics.Bitmap;
 import android.graphics.Canvas;
-import android.graphics.Color;
 import android.graphics.PorterDuff;
 import android.graphics.Rect;
-import android.graphics.drawable.ColorDrawable;
 import android.graphics.drawable.Drawable;
 import android.net.Uri;
 import android.os.AsyncTask;
@@ -82,7 +79,6 @@
 import android.view.inputmethod.InputMethodManager;
 import android.widget.*;
 
-import com.android.launcher3.R;
 import com.android.launcher3.DropTarget.DragObject;
 
 import java.io.DataInputStream;
@@ -2671,7 +2667,9 @@
                     dispatchOnLauncherTransitionEnd(fromView, animated, false);
                     dispatchOnLauncherTransitionEnd(toView, animated, false);
 
-                    if (mWorkspace != null && !springLoaded && !LauncherAppState.isScreenLarge()) {
+                    if (mWorkspace != null
+                            && !springLoaded
+                            && !LauncherAppState.getInstance().isScreenLarge()) {
                         // Hide the workspace scrollbar
                         mWorkspace.hideScrollingIndicator(true);
                         hideDockDivider();
@@ -2741,7 +2739,7 @@
             toView.setVisibility(View.VISIBLE);
             toView.bringToFront();
 
-            if (!springLoaded && !LauncherAppState.isScreenLarge()) {
+            if (!springLoaded && !LauncherAppState.getInstance().isScreenLarge()) {
                 // Hide the workspace scrollbar
                 mWorkspace.hideScrollingIndicator(true);
                 hideDockDivider();
@@ -3032,7 +3030,7 @@
      * Shows the hotseat area.
      */
     void showHotseat(boolean animated) {
-        if (!LauncherAppState.isScreenLarge()) {
+        if (!LauncherAppState.getInstance().isScreenLarge()) {
             if (animated) {
                 if (mHotseat.getAlpha() != 1f) {
                     int duration = 0;
@@ -3051,7 +3049,7 @@
      * Hides the hotseat area.
      */
     void hideHotseat(boolean animated) {
-        if (!LauncherAppState.isScreenLarge()) {
+        if (!LauncherAppState.getInstance().isScreenLarge()) {
             if (animated) {
                 if (mHotseat.getAlpha() != 0f) {
                     int duration = 0;
@@ -3818,7 +3816,8 @@
                         // If we can't find a valid position, then just add a new screen.
                         // This takes time so we need to re-queue the add until the new
                         // page is added.
-                        long screenId = LauncherAppState.getInstance().getLauncherProvider().generateNewScreenId();
+                        long screenId = LauncherAppState.getInstance().getLauncherProvider()
+                                .generateNewScreenId();
                         mWorkspace.insertNewWorkspaceScreen(screenId, false);
                         model.updateWorkspaceScreenOrder(launcher, mWorkspace.getScreenOrder(),
                             new Runnable() {
diff --git a/src/com/android/launcher3/LauncherAppState.java b/src/com/android/launcher3/LauncherAppState.java
index aeafcf0..ea8904f 100644
--- a/src/com/android/launcher3/LauncherAppState.java
+++ b/src/com/android/launcher3/LauncherAppState.java
@@ -20,92 +20,85 @@
 import android.content.*;
 import android.content.res.Configuration;
 import android.database.ContentObserver;
-import android.net.Uri;
-import android.os.Debug;
-import android.os.Environment;
 import android.os.Handler;
-import android.os.IBinder;
 import android.util.Log;
 
-import java.io.File;
-import java.io.IOException;
 import java.lang.ref.WeakReference;
-import java.util.ArrayList;
 
 public class LauncherAppState {
-    private Context mContext;
+    private static final String SHARED_PREFERENCES_KEY = "com.android.launcher3.prefs";
+
     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";
-    private long mStarttime;
+    private boolean mIsScreenLarge;
+    private float mScreenDensity;
+    private int mLongPressTimeout = 300;
 
-    WeakReference<LauncherProvider> mLauncherProvider;
+    private static WeakReference<LauncherProvider> sLauncherProvider;
+    private static Context sContext;
 
-    private static final LauncherAppState INSTANCE = new LauncherAppState();
+    private static Object mLock = new Object();
+    private static LauncherAppState INSTANCE;
 
     public static LauncherAppState getInstance() {
+        if (INSTANCE == null) {
+            INSTANCE = new LauncherAppState();
+        }
         return INSTANCE;
     }
 
-    public static void init(Context context) {
-        INSTANCE.initialize(context);
-    }
-
     public Context getContext() {
-        return mContext;
+        return sContext;
     }
 
-    private LauncherAppState() { }
+    public static void setApplicationContext(Context context) {
+        if (sContext != null) {
+            throw new IllegalStateException("setApplicationContext called twice! old=" + sContext + " new=" + context);
+        }
+        sContext = context.getApplicationContext();
+    }
 
-    private void initialize(Context context) {
-        Log.v(Launcher.TAG, "LauncherAppState initialize() called in process " + android.os.Process.myPid());
-
-        mContext = context;
-
-        mStarttime = System.currentTimeMillis();
-
-        if (context.getResources().getBoolean(R.bool.debug_memory_enabled)) {
-            context.startService(new Intent(context, MemoryTracker.class)
-                    .setAction(MemoryTracker.ACTION_START_TRACKING)
-                    .putExtra("pid", android.os.Process.myPid())
-                    .putExtra("name", "L")
-                    );
+    private LauncherAppState() {
+        if (sContext == null) {
+            throw new IllegalStateException("LauncherAppState inited before app context set");
         }
 
+        Log.v(Launcher.TAG, "LauncherAppState inited");
 
-        // set sIsScreenXLarge and sScreenDensity *before* creating icon cache
-        sIsScreenLarge = context.getResources().getBoolean(R.bool.is_large_screen);
-        sScreenDensity = context.getResources().getDisplayMetrics().density;
+        if (sContext.getResources().getBoolean(R.bool.debug_memory_enabled)) {
+            MemoryTracker.startTrackingMe(sContext, "L");
+        }
 
-        mWidgetPreviewCacheDb = new WidgetPreviewLoader.CacheDb(context);
-        mIconCache = new IconCache(context);
-        mModel = new LauncherModel(context, mIconCache);
+        // set sIsScreenXLarge and mScreenDensity *before* creating icon cache
+        mIsScreenLarge = sContext.getResources().getBoolean(R.bool.is_large_screen);
+        mScreenDensity = sContext.getResources().getDisplayMetrics().density;
+
+        mWidgetPreviewCacheDb = new WidgetPreviewLoader.CacheDb(sContext);
+        mIconCache = new IconCache(sContext);
+        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");
-        context.registerReceiver(mModel, filter);
+        sContext.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);
+        sContext.registerReceiver(mModel, filter);
         filter = new IntentFilter();
         filter.addAction(SearchManager.INTENT_GLOBAL_SEARCH_ACTIVITY_CHANGED);
-        context.registerReceiver(mModel, filter);
+        sContext.registerReceiver(mModel, filter);
         filter = new IntentFilter();
         filter.addAction(SearchManager.INTENT_ACTION_SEARCHABLES_CHANGED);
-        context.registerReceiver(mModel, filter);
+        sContext.registerReceiver(mModel, filter);
 
         // Register for changes to the favorites
-        ContentResolver resolver = context.getContentResolver();
+        ContentResolver resolver = sContext.getContentResolver();
         resolver.registerContentObserver(LauncherSettings.Favorites.CONTENT_URI, true,
                 mFavoritesObserver);
     }
@@ -114,9 +107,9 @@
      * Call from Application.onTerminate(), which is not guaranteed to ever be called.
      */
     public void onTerminate() {
-        mContext.unregisterReceiver(mModel);
+        sContext.unregisterReceiver(mModel);
 
-        ContentResolver resolver = mContext.getContentResolver();
+        ContentResolver resolver = sContext.getContentResolver();
         resolver.unregisterContentObserver(mFavoritesObserver);
     }
 
@@ -134,6 +127,9 @@
     };
 
     LauncherModel setLauncher(Launcher launcher) {
+        if (mModel == null) {
+            throw new IllegalStateException("setLauncher() called before init()");
+        }
         mModel.initialize(launcher);
         return mModel;
     }
@@ -150,27 +146,20 @@
         return mWidgetPreviewCacheDb;
     }
 
-    void setLauncherProvider(LauncherProvider provider) {
-        mLauncherProvider = new WeakReference<LauncherProvider>(provider);
+    static void setLauncherProvider(LauncherProvider provider) {
+        sLauncherProvider = new WeakReference<LauncherProvider>(provider);
     }
 
-    LauncherProvider getLauncherProvider() {
-        return mLauncherProvider.get();
-    }
-
-    /**
-     * @return Milliseconds since the application state was created.
-     */
-    public long getUptime() {
-        return System.currentTimeMillis() - mStarttime;
+    static LauncherProvider getLauncherProvider() {
+        return sLauncherProvider.get();
     }
 
     public static String getSharedPreferencesKey() {
-        return sSharedPreferencesKey;
+        return SHARED_PREFERENCES_KEY;
     }
 
-    public static boolean isScreenLarge() {
-        return sIsScreenLarge;
+    public boolean isScreenLarge() {
+        return mIsScreenLarge;
     }
 
     public static boolean isScreenLandscape(Context context) {
@@ -178,11 +167,11 @@
             Configuration.ORIENTATION_LANDSCAPE;
     }
 
-    public static float getScreenDensity() {
-        return sScreenDensity;
+    public float getScreenDensity() {
+        return mScreenDensity;
     }
 
-    public static int getLongPressTimeout() {
-        return sLongPressTimeout;
+    public int getLongPressTimeout() {
+        return mLongPressTimeout;
     }
 }
diff --git a/src/com/android/launcher3/LauncherApplication.java b/src/com/android/launcher3/LauncherApplication.java
index 647bf79..8b179f1 100644
--- a/src/com/android/launcher3/LauncherApplication.java
+++ b/src/com/android/launcher3/LauncherApplication.java
@@ -17,22 +17,13 @@
 package com.android.launcher3;
 
 import android.app.Application;
-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 LauncherApplication extends Application {
     @Override
     public void onCreate() {
         super.onCreate();
-        LauncherAppState.getInstance().init(getApplicationContext());
+        LauncherAppState.setApplicationContext(this);
+        LauncherAppState.getInstance();
     }
 
     @Override
diff --git a/src/com/android/launcher3/LauncherModel.java b/src/com/android/launcher3/LauncherModel.java
index 5253591..d098805 100644
--- a/src/com/android/launcher3/LauncherModel.java
+++ b/src/com/android/launcher3/LauncherModel.java
@@ -167,9 +167,11 @@
         public void onPageBoundSynchronously(int page);
     }
 
-    LauncherModel(Context context, IconCache iconCache) {
+    LauncherModel(LauncherAppState app, IconCache iconCache) {
+        final Context context = app.getContext();
+
         mAppsCanBeOnExternalStorage = !Environment.isExternalStorageEmulated();
-        mApp = LauncherAppState.getInstance();
+        mApp = app;
         mBgAllAppsList = new AllAppsList(iconCache);
         mIconCache = iconCache;
 
@@ -2323,10 +2325,8 @@
                     for (int i=0; i<N; i++) {
                         if (DEBUG_LOADERS) Log.d(TAG, "mAllAppsList.updatePackage " + packages[i]);
                         mBgAllAppsList.updatePackage(context, packages[i]);
-                        LauncherAppState app =
-                                LauncherAppState.getInstance();
                         WidgetPreviewLoader.removeFromDb(
-                                app.getWidgetPreviewCacheDb(), packages[i]);
+                                mApp.getWidgetPreviewCacheDb(), packages[i]);
                     }
                     break;
                 case OP_REMOVE:
@@ -2334,10 +2334,8 @@
                     for (int i=0; i<N; i++) {
                         if (DEBUG_LOADERS) Log.d(TAG, "mAllAppsList.removePackage " + packages[i]);
                         mBgAllAppsList.removePackage(packages[i]);
-                        LauncherAppState app =
-                                LauncherAppState.getInstance();
                         WidgetPreviewLoader.removeFromDb(
-                                app.getWidgetPreviewCacheDb(), packages[i]);
+                                mApp.getWidgetPreviewCacheDb(), packages[i]);
                     }
                     break;
             }
diff --git a/src/com/android/launcher3/LauncherProvider.java b/src/com/android/launcher3/LauncherProvider.java
index 91e58e2..ae227a9 100644
--- a/src/com/android/launcher3/LauncherProvider.java
+++ b/src/com/android/launcher3/LauncherProvider.java
@@ -49,7 +49,6 @@
 import android.util.Log;
 import android.util.Xml;
 
-import com.android.launcher3.R;
 import com.android.launcher3.LauncherSettings.Favorites;
 
 import org.xmlpull.v1.XmlPullParser;
@@ -95,8 +94,9 @@
 
     @Override
     public boolean onCreate() {
-        mOpenHelper = new DatabaseHelper(getContext());
-        LauncherAppState.getInstance().setLauncherProvider(this);
+        final Context context = getContext();
+        mOpenHelper = new DatabaseHelper(context);
+        LauncherAppState.setLauncherProvider(this);
         return true;
     }
 
diff --git a/src/com/android/launcher3/PagedViewCellLayout.java b/src/com/android/launcher3/PagedViewCellLayout.java
index 423b1cf..9aa2467 100644
--- a/src/com/android/launcher3/PagedViewCellLayout.java
+++ b/src/com/android/launcher3/PagedViewCellLayout.java
@@ -24,8 +24,6 @@
 import android.view.ViewDebug;
 import android.view.ViewGroup;
 
-import com.android.launcher3.R;
-
 /**
  * An abstraction of the original CellLayout which supports laying out items
  * which span multiple cells into a grid-like layout.  Also supports dimming
@@ -459,8 +457,9 @@
             this.cellVSpan = cellVSpan;
         }
 
-        public void setup(int cellWidth, int cellHeight, int widthGap, int heightGap,
-                int hStartPadding, int vStartPadding) {
+        public void setup(Context context,
+                          int cellWidth, int cellHeight, int widthGap, int heightGap,
+                          int hStartPadding, int vStartPadding) {
 
             final int myCellHSpan = cellHSpan;
             final int myCellVSpan = cellVSpan;
@@ -472,7 +471,7 @@
             height = myCellVSpan * cellHeight + ((myCellVSpan - 1) * heightGap) -
                     topMargin - bottomMargin;
 
-            if (LauncherAppState.isScreenLarge()) {
+            if (LauncherAppState.getInstance().isScreenLarge()) {
                 x = hStartPadding + myCellX * (cellWidth + widthGap) + leftMargin;
                 y = vStartPadding + myCellY * (cellHeight + heightGap) + topMargin;
             } else {
diff --git a/src/com/android/launcher3/PagedViewCellLayoutChildren.java b/src/com/android/launcher3/PagedViewCellLayoutChildren.java
index c9e108d..84d2b1d 100644
--- a/src/com/android/launcher3/PagedViewCellLayoutChildren.java
+++ b/src/com/android/launcher3/PagedViewCellLayoutChildren.java
@@ -91,7 +91,8 @@
             View child = getChildAt(i);
             PagedViewCellLayout.LayoutParams lp =
                 (PagedViewCellLayout.LayoutParams) child.getLayoutParams();
-            lp.setup(mCellWidth, mCellHeight, mWidthGap, mHeightGap,
+            lp.setup(getContext(),
+                    mCellWidth, mCellHeight, mWidthGap, mHeightGap,
                     getPaddingLeft(),
                     getPaddingTop());
 
diff --git a/src/com/android/launcher3/UninstallShortcutReceiver.java b/src/com/android/launcher3/UninstallShortcutReceiver.java
index eb43fc9..8e968cd 100644
--- a/src/com/android/launcher3/UninstallShortcutReceiver.java
+++ b/src/com/android/launcher3/UninstallShortcutReceiver.java
@@ -25,8 +25,6 @@
 import android.net.Uri;
 import android.widget.Toast;
 
-import com.android.launcher3.R;
-
 import java.net.URISyntaxException;
 import java.util.ArrayList;
 import java.util.HashSet;
@@ -87,7 +85,7 @@
         final Intent data = pendingInfo.data;
 
         LauncherAppState app = LauncherAppState.getInstance();
-        synchronized (app) {
+        synchronized (app) { // TODO: make removeShortcut internally threadsafe
             removeShortcut(context, data, sharedPrefs);
         }
     }
diff --git a/src/com/android/launcher3/WeightWatcher.java b/src/com/android/launcher3/WeightWatcher.java
index 410856a..5a682dd 100644
--- a/src/com/android/launcher3/WeightWatcher.java
+++ b/src/com/android/launcher3/WeightWatcher.java
@@ -130,35 +130,6 @@
         mHandler.sendEmptyMessage(MSG_STOP);
     }
 
-    public String getUptimeString() {
-        long sec = LauncherAppState.getInstance().getUptime() / 1000;
-        StringBuilder sb = new StringBuilder();
-        long days = sec / 86400;
-        if (days > 0) {
-            sec -= days * 86400;
-            sb.append(days);
-            sb.append("d");
-        }
-
-        long hours = sec / 3600;
-        if (hours > 0) {
-            sec -= hours * 3600;
-            sb.append(hours);
-            sb.append("h");
-        }
-
-        long mins = sec / 60;
-        if (mins > 0) {
-            sec -= mins * 60;
-            sb.append(mins);
-            sb.append("m");
-        }
-
-        sb.append(sec);
-        sb.append("s");
-        return sb.toString();
-    }
-
     public class ProcessWatcher extends LinearLayout {
         GraphView mRamGraph;
         TextView mText;
@@ -210,6 +181,35 @@
             return mPid;
         }
 
+        public String getUptimeString() {
+            long sec = mMemInfo.getUptime() / 1000;
+            StringBuilder sb = new StringBuilder();
+            long days = sec / 86400;
+            if (days > 0) {
+                sec -= days * 86400;
+                sb.append(days);
+                sb.append("d");
+            }
+
+            long hours = sec / 3600;
+            if (hours > 0) {
+                sec -= hours * 3600;
+                sb.append(hours);
+                sb.append("h");
+            }
+
+            long mins = sec / 60;
+            if (mins > 0) {
+                sec -= mins * 60;
+                sb.append(mins);
+                sb.append("m");
+            }
+
+            sb.append(sec);
+            sb.append("s");
+            return sb.toString();
+        }
+
         public void update() {
             //Log.v("WeightWatcher.ProcessWatcher",
             //        "MSG_UPDATE pss=" + mMemInfo.currentPss);
diff --git a/src/com/android/launcher3/WidgetPreviewLoader.java b/src/com/android/launcher3/WidgetPreviewLoader.java
index a487f8a..e96981a 100644
--- a/src/com/android/launcher3/WidgetPreviewLoader.java
+++ b/src/com/android/launcher3/WidgetPreviewLoader.java
@@ -25,8 +25,6 @@
 import android.os.AsyncTask;
 import android.util.Log;
 
-import com.android.launcher3.R;
-
 import java.io.ByteArrayOutputStream;
 import java.io.File;
 import java.lang.ref.SoftReference;
diff --git a/src/com/android/launcher3/Workspace.java b/src/com/android/launcher3/Workspace.java
index 88c8e2a..af94a1a 100644
--- a/src/com/android/launcher3/Workspace.java
+++ b/src/com/android/launcher3/Workspace.java
@@ -29,7 +29,6 @@
 import android.content.Context;
 import android.content.Intent;
 import android.content.SharedPreferences;
-import android.content.pm.ResolveInfo;
 import android.content.res.Resources;
 import android.content.res.TypedArray;
 import android.graphics.Bitmap;
@@ -40,7 +39,6 @@
 import android.graphics.Rect;
 import android.graphics.Region.Op;
 import android.graphics.drawable.Drawable;
-import android.net.Uri;
 import android.os.IBinder;
 import android.os.Parcelable;
 import android.util.AttributeSet;
@@ -54,7 +52,6 @@
 import android.widget.ImageView;
 import android.widget.TextView;
 
-import com.android.launcher3.R;
 import com.android.launcher3.FolderIcon.FolderRingAnimator;
 import com.android.launcher3.LauncherSettings.Favorites;
 
@@ -63,7 +60,6 @@
 import java.util.HashMap;
 import java.util.HashSet;
 import java.util.Iterator;
-import java.util.List;
 import java.util.Set;
 
 /**
@@ -178,7 +174,7 @@
     /** Is the user is dragging an item near the edge of a page? */
     private boolean mInScrollArea = false;
 
-    private final HolographicOutlineHelper mOutlineHelper = HolographicOutlineHelper.obtain();
+    private HolographicOutlineHelper mOutlineHelper;
     private Bitmap mDragOutline = null;
     private final Rect mTempRect = new Rect();
     private final int[] mTempXY = new int[2];
@@ -293,6 +289,8 @@
         mContentIsRefreshable = false;
         mOriginalPageSpacing = mPageSpacing;
 
+        mOutlineHelper = HolographicOutlineHelper.obtain(context);
+
         mDragEnforcer = new DropTarget.DragEnforcer(context);
         // With workspace, data is available straight from the get-go
         setDataIsReady();
@@ -309,7 +307,7 @@
         TypedArray a = context.obtainStyledAttributes(attrs,
                 R.styleable.Workspace, defStyle, 0);
 
-        if (LauncherAppState.isScreenLarge()) {
+        if (LauncherAppState.getInstance().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
@@ -730,7 +728,7 @@
 
         // Only allow tap to next page on large devices, where there's significant margin outside
         // the active workspace
-        return LauncherAppState.isScreenLarge() && hitsPage(current - 1, x, y);
+        return LauncherAppState.getInstance().isScreenLarge() && hitsPage(current - 1, x, y);
     }
 
     @Override
@@ -741,7 +739,7 @@
 
         // Only allow tap to next page on large devices, where there's significant margin outside
         // the active workspace
-        return LauncherAppState.isScreenLarge() && hitsPage(current + 1, x, y);
+        return LauncherAppState.getInstance().isScreenLarge() && hitsPage(current + 1, x, y);
     }
 
     /**
@@ -876,7 +874,7 @@
         }
 
         // Only show page outlines as we pan if we are on large screen
-        if (LauncherAppState.isScreenLarge()) {
+        if (LauncherAppState.getInstance().isScreenLarge()) {
             showOutlines();
             mIsStaticWallpaper = mWallpaperManager.getWallpaperInfo() == null;
         }
@@ -911,7 +909,7 @@
             }
         } else {
             // If we are not mid-dragging, hide the page outlines if we are on a large screen
-            if (LauncherAppState.isScreenLarge()) {
+            if (LauncherAppState.getInstance().isScreenLarge()) {
                 hideOutlines();
             }
 
@@ -980,7 +978,7 @@
 
         // We need to ensure that there is enough extra space in the wallpaper for the intended
         // parallax effects
-        if (LauncherAppState.isScreenLarge()) {
+        if (LauncherAppState.getInstance().isScreenLarge()) {
             mWallpaperWidth = (int) (maxDim * wallpaperTravelToScreenWidthRatio(maxDim, minDim));
             mWallpaperHeight = maxDim;
         } else {
@@ -1013,7 +1011,7 @@
         float scrollProgress =
             adjustedScrollX / (float) scrollRange;
 
-        if (LauncherAppState.isScreenLarge() && mIsStaticWallpaper) {
+        if (LauncherAppState.getInstance().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.
@@ -1173,7 +1171,7 @@
                 Math.abs(vOffsetDelta) < UPDATE_THRESHOLD;
 
             // Don't have any lag between workspace and wallpaper on non-large devices
-            if (!LauncherAppState.isScreenLarge() || jumpToFinalValue) {
+            if (!LauncherAppState.getInstance().isScreenLarge() || jumpToFinalValue) {
                 mHorizontalWallpaperOffset = mFinalHorizontalWallpaperOffset;
                 mVerticalWallpaperOffset = mFinalVerticalWallpaperOffset;
             } else {
@@ -2536,7 +2534,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 (LauncherAppState.isScreenLarge()) {
+        if (LauncherAppState.getInstance().isScreenLarge()) {
             showOutlines();
         }
     }