Adding migration Clings. (Bug 11973614)

- Refactoring Launcher cling code out to LauncherClings.

Change-Id: Iff4f84f5b8bfeb69b1be0b4802022c3eb20b6f2c
diff --git a/proguard.flags b/proguard.flags
index 9b59b21..a922e91 100644
--- a/proguard.flags
+++ b/proguard.flags
@@ -8,6 +8,9 @@
   public void onClickAllAppsButton(android.view.View);
   public void onClickAppMarketButton(android.view.View);
   public void dismissFirstRunCling(android.view.View);
+  public void dismissMigrationClingCopyApps(android.view.View);
+  public void dismissMigrationClingUseDefault(android.view.View);
+  public void dismissMigrationWorkspaceCling(android.view.View);
   public void dismissWorkspaceCling(android.view.View);
   public void dismissAllAppsCling(android.view.View);
 }
diff --git a/res/layout-port/launcher.xml b/res/layout-port/launcher.xml
index 2b3cf81..7400534 100644
--- a/res/layout-port/launcher.xml
+++ b/res/layout-port/launcher.xml
@@ -73,6 +73,16 @@
             android:layout_width="match_parent"
             android:layout_height="match_parent"
             android:visibility="gone" />
+        <include layout="@layout/migration_cling"
+            android:id="@+id/migration_cling"
+            android:layout_width="match_parent"
+            android:layout_height="match_parent"
+            android:visibility="gone" />
+        <include layout="@layout/migration_workspace_cling"
+            android:id="@+id/migration_workspace_cling"
+            android:layout_width="match_parent"
+            android:layout_height="match_parent"
+            android:visibility="gone" />
         <include layout="@layout/workspace_cling"
             android:id="@+id/workspace_cling"
             android:layout_width="match_parent"
diff --git a/res/layout-port/migration_cling.xml b/res/layout-port/migration_cling.xml
new file mode 100644
index 0000000..36bbab8
--- /dev/null
+++ b/res/layout-port/migration_cling.xml
@@ -0,0 +1,93 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2011 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.
+-->
+<com.android.launcher3.Cling
+    xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:launcher="http://schemas.android.com/apk/res-auto/com.android.launcher3"
+    android:layout_width="match_parent"
+    android:layout_height="match_parent"
+    launcher:drawIdentifier="migration_portrait">
+    <FrameLayout
+        android:id="@+id/content"
+        android:layout_width="match_parent"
+        android:layout_height="match_parent"
+        android:orientation="vertical">
+
+        <LinearLayout
+            android:layout_width="match_parent"
+            android:layout_height="wrap_content"
+            android:layout_gravity="top"
+            android:layout_marginStart="25dp"
+            android:layout_marginEnd="25dp"
+            android:orientation="vertical">
+            <TextView
+                style="@style/ClingAltTitleText"
+                android:layout_width="match_parent"
+                android:layout_height="wrap_content"
+                android:layout_gravity="top"
+                android:layout_marginBottom="10dp"
+                android:gravity="center"
+                android:text="@string/first_run_cling_title" />
+
+            <ImageView
+                android:layout_width="wrap_content"
+                android:layout_height="wrap_content"
+                android:layout_gravity="center_horizontal"
+                android:src="@drawable/cling_arrow_up" />
+            <LinearLayout
+                android:paddingLeft="20dp"
+                android:paddingRight="20dp"
+                android:paddingTop="20dp"
+                android:paddingBottom="20dp"
+                android:layout_width="match_parent"
+                android:layout_height="wrap_content"
+                android:orientation="vertical"
+                android:background="@drawable/cling">
+                <TextView
+                    style="@style/ClingTitleText"
+                    android:layout_width="wrap_content"
+                    android:layout_height="wrap_content"
+                    android:text="@string/migration_cling_title" />
+                <TextView
+                    style="@style/ClingText"
+                    android:layout_width="match_parent"
+                    android:layout_height="wrap_content"
+                    android:text="@string/migration_cling_description" />
+            </LinearLayout>
+        </LinearLayout>
+
+        <LinearLayout
+            android:layout_width="match_parent"
+            android:layout_height="wrap_content"
+            android:layout_gravity="bottom"
+            android:layout_marginStart="25dp"
+            android:layout_marginEnd="25dp"
+            android:layout_marginBottom="25dp"
+            android:orientation="vertical">
+            <Button
+                style="@style/ClingButton"
+                android:layout_width="match_parent"
+                android:layout_height="wrap_content"
+                android:text="@string/migration_cling_copy_apps"
+                android:onClick="dismissMigrationClingCopyApps" />
+            <Button
+                style="@style/ClingButton"
+                android:layout_width="match_parent"
+                android:layout_height="wrap_content"
+                android:text="@string/migration_cling_use_default"
+                android:onClick="dismissMigrationClingUseDefault" />
+        </LinearLayout>
+    </FrameLayout>
+</com.android.launcher3.Cling>
diff --git a/res/layout-port/migration_workspace_cling.xml b/res/layout-port/migration_workspace_cling.xml
new file mode 100644
index 0000000..bc5e22f
--- /dev/null
+++ b/res/layout-port/migration_workspace_cling.xml
@@ -0,0 +1,70 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2011 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.
+-->
+<com.android.launcher3.Cling
+    xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:launcher="http://schemas.android.com/apk/res-auto/com.android.launcher3"
+    android:layout_width="match_parent"
+    android:layout_height="match_parent"
+    launcher:drawIdentifier="migration_workspace_portrait">
+    <FrameLayout
+        android:id="@+id/content"
+        android:layout_width="match_parent"
+        android:layout_height="match_parent">
+        <LinearLayout
+            android:id="@+id/migration_workspace_cling_bubble"
+            android:layout_width="match_parent"
+            android:layout_height="wrap_content"
+            android:layout_gravity="bottom"
+            android:layout_marginStart="25dp"
+            android:layout_marginEnd="25dp"
+            android:orientation="vertical">
+            <LinearLayout
+                android:paddingLeft="20dp"
+                android:paddingRight="20dp"
+                android:paddingTop="20dp"
+                android:paddingBottom="20dp"
+                android:layout_width="match_parent"
+                android:layout_height="wrap_content"
+                android:orientation="vertical"
+                android:background="@drawable/cling">
+                <TextView
+                    style="@style/ClingTitleText"
+                    android:layout_width="wrap_content"
+                    android:layout_height="wrap_content"
+                    android:text="@string/workspace_cling_title" />
+                <TextView
+                    style="@style/ClingText"
+                    android:layout_width="match_parent"
+                    android:layout_height="wrap_content"
+                    android:text="@string/workspace_cling_move_item" />
+            </LinearLayout>
+            <ImageView
+                android:layout_width="wrap_content"
+                android:layout_height="wrap_content"
+                android:layout_gravity="center_horizontal"
+                android:src="@drawable/cling_arrow_down" />
+        </LinearLayout>
+
+        <Button
+            style="@style/ClingButton"
+            android:layout_width="wrap_content"
+            android:layout_height="wrap_content"
+            android:layout_marginBottom="15dp"
+            android:layout_marginRight="20dp"
+            android:layout_gravity="bottom|right"
+            android:onClick="dismissMigrationWorkspaceCling" />
+    </FrameLayout>
+</com.android.launcher3.Cling>
diff --git a/res/values/strings.xml b/res/values/strings.xml
index 361be7b..09120d0 100644
--- a/res/values/strings.xml
+++ b/res/values/strings.xml
@@ -216,6 +216,14 @@
     <string name="first_run_cling_search_bar_hint"></string>
     <!-- The description of how to use the workspace [CHAR_LIMIT=60] -->
     <string name="first_run_cling_create_screens_hint">Create more screens for apps and folders</string>
+    <!-- The title text for the migration cling [CHAR_LIMIT=30] -->
+    <string name="migration_cling_title">Copy your apps?</string>
+    <!-- The description of what migration does [CHAR_LIMIT=60] -->
+    <string name="migration_cling_description">Import apps and folders from your old Home screens?</string>
+    <!-- The description of the button to migrate apps from another launcher [CHAR_LIMIT=30] -->
+    <string name="migration_cling_copy_apps">COPY APPS</string>
+    <!-- The description of the button to use the default launcher layout [CHAR_LIMIT=30] -->
+    <string name="migration_cling_use_default">USE DEFAULT</string>
     <!-- The title text for the workspace cling [CHAR_LIMIT=30] -->
     <string name="workspace_cling_title">Organize your space</string>
     <!-- The description of how to use the workspace [CHAR_LIMIT=70] -->
diff --git a/src/com/android/launcher3/AppsCustomizeTabHost.java b/src/com/android/launcher3/AppsCustomizeTabHost.java
index 697bd7e..bb7f045 100644
--- a/src/com/android/launcher3/AppsCustomizeTabHost.java
+++ b/src/com/android/launcher3/AppsCustomizeTabHost.java
@@ -407,7 +407,7 @@
         }
 
         // Dismiss the workspace cling
-        l.dismissWorkspaceCling(null);
+        l.getLauncherClings().dismissWorkspaceCling(null);
     }
 
     @Override
diff --git a/src/com/android/launcher3/Cling.java b/src/com/android/launcher3/Cling.java
index 3af4271..185b49b 100644
--- a/src/com/android/launcher3/Cling.java
+++ b/src/com/android/launcher3/Cling.java
@@ -37,10 +37,6 @@
 public class Cling extends FrameLayout implements Insettable, View.OnClickListener,
         View.OnLongClickListener, View.OnTouchListener {
 
-    static final String FIRST_RUN_CLING_DISMISSED_KEY = "cling_gel.first_run.dismissed";
-    static final String WORKSPACE_CLING_DISMISSED_KEY = "cling_gel.workspace.dismissed";
-    static final String FOLDER_CLING_DISMISSED_KEY = "cling_gel.folder.dismissed";
-
     private static String FIRST_RUN_PORTRAIT = "first_run_portrait";
     private static String FIRST_RUN_LANDSCAPE = "first_run_landscape";
 
@@ -49,6 +45,12 @@
     private static String WORKSPACE_LARGE = "workspace_large";
     private static String WORKSPACE_CUSTOM = "workspace_custom";
 
+    private static String MIGRATION_PORTRAIT = "migration_portrait";
+    private static String MIGRATION_LANDSCAPE = "migration_landscape";
+
+    private static String MIGRATION_WORKSPACE_PORTRAIT = "migration_workspace_portrait";
+    private static String MIGRATION_WORKSPACE_LANDSCAPE = "migration_workspace_landscape";
+
     private static String FOLDER_PORTRAIT = "folder_portrait";
     private static String FOLDER_LANDSCAPE = "folder_landscape";
     private static String FOLDER_LARGE = "folder_large";
@@ -57,6 +59,8 @@
     private static float WORKSPACE_INNER_CIRCLE_RADIUS_DPS = 50;
     private static float WORKSPACE_OUTER_CIRCLE_RADIUS_DPS = 60;
     private static float WORKSPACE_CIRCLE_Y_OFFSET_DPS = 30;
+    private static float MIGRATION_WORKSPACE_INNER_CIRCLE_RADIUS_DPS = 42;
+    private static float MIGRATION_WORKSPACE_OUTER_CIRCLE_RADIUS_DPS = 46;
 
     private Launcher mLauncher;
     private boolean mIsInitialized;
@@ -70,6 +74,7 @@
     private Rect mFocusedHotseatAppBounds;
 
     private Paint mErasePaint;
+    private Paint mBorderPaint;
     private Paint mBubblePaint;
     private Paint mDotPaint;
 
@@ -112,6 +117,10 @@
             mErasePaint.setAlpha(0);
             mErasePaint.setAntiAlias(true);
 
+            mBorderPaint = new Paint();
+            mBorderPaint.setColor(0xFFFFFFFF);
+            mBorderPaint.setAntiAlias(true);
+
             int circleColor = getResources().getColor(
                     R.color.first_run_cling_circle_background_color);
             mBubblePaint = new Paint();
@@ -166,13 +175,30 @@
         }
     }
 
+    void updateMigrationWorkspaceBubblePosition() {
+        DisplayMetrics metrics = new DisplayMetrics();
+        mLauncher.getWindowManager().getDefaultDisplay().getMetrics(metrics);
+
+        // Get the page indicator bounds
+        LauncherAppState app = LauncherAppState.getInstance();
+        DeviceProfile grid = app.getDynamicGrid().getDeviceProfile();
+        Rect pageIndicatorBounds = grid.getWorkspacePageIndicatorBounds(mInsets);
+
+        View bubble = findViewById(R.id.migration_workspace_cling_bubble);
+        FrameLayout.LayoutParams lp = (FrameLayout.LayoutParams) bubble.getLayoutParams();
+        lp.bottomMargin = grid.heightPx - pageIndicatorBounds.top;
+        bubble.requestLayout();
+    }
+
     void show(boolean animate, int duration) {
         setVisibility(View.VISIBLE);
         setLayerType(View.LAYER_TYPE_HARDWARE, null);
         if (mDrawIdentifier.equals(WORKSPACE_PORTRAIT) ||
                 mDrawIdentifier.equals(WORKSPACE_LANDSCAPE) ||
                 mDrawIdentifier.equals(WORKSPACE_LARGE) ||
-                mDrawIdentifier.equals(WORKSPACE_CUSTOM)) {
+                mDrawIdentifier.equals(WORKSPACE_CUSTOM) ||
+                mDrawIdentifier.equals(MIGRATION_WORKSPACE_PORTRAIT) ||
+                mDrawIdentifier.equals(MIGRATION_WORKSPACE_LANDSCAPE)) {
             View content = getContent();
             content.setAlpha(0f);
             content.animate()
@@ -218,7 +244,9 @@
 
     void hide(final int duration, final Runnable postCb) {
         if (mDrawIdentifier.equals(FIRST_RUN_PORTRAIT) ||
-                mDrawIdentifier.equals(FIRST_RUN_LANDSCAPE)) {
+                mDrawIdentifier.equals(FIRST_RUN_LANDSCAPE) ||
+                mDrawIdentifier.equals(MIGRATION_PORTRAIT) ||
+                mDrawIdentifier.equals(MIGRATION_LANDSCAPE)) {
             View content = getContent();
             content.animate()
                 .alpha(0f)
@@ -340,7 +368,7 @@
                 intent.setComponent(mFocusedHotseatAppComponent);
                 intent.addCategory(Intent.CATEGORY_LAUNCHER);
                 mLauncher.startActivity(intent, null);
-                mLauncher.dismissWorkspaceCling(this);
+                mLauncher.getLauncherClings().dismissWorkspaceCling(this);
             }
         }
     }
@@ -350,7 +378,11 @@
         if (mDrawIdentifier.equals(WORKSPACE_PORTRAIT) ||
                 mDrawIdentifier.equals(WORKSPACE_LANDSCAPE) ||
                 mDrawIdentifier.equals(WORKSPACE_LARGE)) {
-            mLauncher.dismissWorkspaceCling(null);
+            mLauncher.getLauncherClings().dismissWorkspaceCling(null);
+            return true;
+        } else if (mDrawIdentifier.equals(MIGRATION_WORKSPACE_PORTRAIT) ||
+                mDrawIdentifier.equals(MIGRATION_WORKSPACE_LANDSCAPE)) {
+            mLauncher.getLauncherClings().dismissMigrationWorkspaceCling(null);
             return true;
         }
         return false;
@@ -361,6 +393,11 @@
         if (mIsInitialized) {
             canvas.save();
 
+            // Get the page indicator bounds
+            LauncherAppState app = LauncherAppState.getInstance();
+            DeviceProfile grid = app.getDynamicGrid().getDeviceProfile();
+            Rect pageIndicatorBounds = grid.getWorkspacePageIndicatorBounds(mInsets);
+
             // Get the background override if there is one
             if (mBackground == null) {
                 if (mDrawIdentifier.equals(WORKSPACE_CUSTOM)) {
@@ -378,7 +415,9 @@
                 mBackground.draw(canvas);
             } else if (mDrawIdentifier.equals(WORKSPACE_PORTRAIT) ||
                     mDrawIdentifier.equals(WORKSPACE_LANDSCAPE) ||
-                    mDrawIdentifier.equals(WORKSPACE_LARGE)) {
+                    mDrawIdentifier.equals(WORKSPACE_LARGE) ||
+                    mDrawIdentifier.equals(MIGRATION_WORKSPACE_PORTRAIT) ||
+                    mDrawIdentifier.equals(MIGRATION_WORKSPACE_LANDSCAPE)) {
                 // Initialize the draw buffer (to allow punching through)
                 eraseBg = Bitmap.createBitmap(getMeasuredWidth(), getMeasuredHeight(),
                         Bitmap.Config.ARGB_8888);
@@ -412,11 +451,13 @@
                     mDrawIdentifier.equals(WORKSPACE_LANDSCAPE) ||
                     mDrawIdentifier.equals(WORKSPACE_LARGE)) {
                 int offset = DynamicGrid.pxFromDp(WORKSPACE_CIRCLE_Y_OFFSET_DPS, metrics);
-                mErasePaint.setAlpha((int) (128));
+                // Draw the outer circle
+                mErasePaint.setAlpha(128);
                 eraseCanvas.drawCircle(metrics.widthPixels / 2,
                         metrics.heightPixels / 2 - offset,
                         DynamicGrid.pxFromDp(WORKSPACE_OUTER_CIRCLE_RADIUS_DPS, metrics),
                         mErasePaint);
+                // Draw the inner circle
                 mErasePaint.setAlpha(0);
                 eraseCanvas.drawCircle(metrics.widthPixels / 2,
                         metrics.heightPixels / 2 - offset,
@@ -434,8 +475,24 @@
                     mFocusedHotseatApp.setAlpha((int) (255 * alpha));
                     mFocusedHotseatApp.draw(canvas);
                 }
+            } else if (mDrawIdentifier.equals(MIGRATION_WORKSPACE_PORTRAIT) ||
+                    mDrawIdentifier.equals(MIGRATION_WORKSPACE_LANDSCAPE)) {
+                int offset = DynamicGrid.pxFromDp(WORKSPACE_CIRCLE_Y_OFFSET_DPS, metrics);
+                // Draw the outer circle
+                eraseCanvas.drawCircle(pageIndicatorBounds.centerX(),
+                        pageIndicatorBounds.centerY(),
+                        DynamicGrid.pxFromDp(MIGRATION_WORKSPACE_OUTER_CIRCLE_RADIUS_DPS, metrics),
+                        mBorderPaint);
+                // Draw the inner circle
+                mErasePaint.setAlpha(0);
+                eraseCanvas.drawCircle(pageIndicatorBounds.centerX(),
+                        pageIndicatorBounds.centerY(),
+                        DynamicGrid.pxFromDp(MIGRATION_WORKSPACE_INNER_CIRCLE_RADIUS_DPS, metrics),
+                        mErasePaint);
+                canvas.drawBitmap(eraseBg, 0, 0, null);
+                eraseCanvas.setBitmap(null);
+                eraseBg = null;
             }
-
             canvas.restore();
         }
 
diff --git a/src/com/android/launcher3/DeviceProfile.java b/src/com/android/launcher3/DeviceProfile.java
index 67b0933..b6c394c 100644
--- a/src/com/android/launcher3/DeviceProfile.java
+++ b/src/com/android/launcher3/DeviceProfile.java
@@ -475,6 +475,14 @@
         return bounds;
     }
 
+    /** Returns the bounds of the workspace page indicators. */
+    Rect getWorkspacePageIndicatorBounds(Rect insets) {
+        Rect workspacePadding = getWorkspacePadding();
+        int pageIndicatorTop = heightPx - insets.bottom - workspacePadding.bottom;
+        return new Rect(workspacePadding.left, pageIndicatorTop,
+                widthPx - workspacePadding.right, pageIndicatorTop + pageIndicatorHeightPx);
+    }
+
     /** Returns the workspace padding in the specified orientation */
     Rect getWorkspacePadding() {
         return getWorkspacePadding(isLandscape ? CellLayout.LANDSCAPE : CellLayout.PORTRAIT);
diff --git a/src/com/android/launcher3/DragLayer.java b/src/com/android/launcher3/DragLayer.java
index 8726f30..ab0469d 100644
--- a/src/com/android/launcher3/DragLayer.java
+++ b/src/com/android/launcher3/DragLayer.java
@@ -168,7 +168,8 @@
         }
 
         Folder currentFolder = mLauncher.getWorkspace().getOpenFolder();
-        if (currentFolder != null && !mLauncher.isFolderClingVisible() && intercept) {
+        if (currentFolder != null && !mLauncher.getLauncherClings().isFolderClingVisible() &&
+                intercept) {
             if (currentFolder.isEditingName()) {
                 if (!isEventOverFolderTextRegion(currentFolder, ev)) {
                     currentFolder.dismissEditingName();
diff --git a/src/com/android/launcher3/Folder.java b/src/com/android/launcher3/Folder.java
index 758ee5b..41815b6 100644
--- a/src/com/android/launcher3/Folder.java
+++ b/src/com/android/launcher3/Folder.java
@@ -238,7 +238,7 @@
                 return false;
             }
 
-            mLauncher.dismissFolderCling(null);
+            mLauncher.getLauncherClings().dismissFolderCling(null);
 
             mLauncher.getWorkspace().onDragStartedWithItem(v);
             mLauncher.getWorkspace().beginDragShared(v, this);
@@ -466,7 +466,7 @@
             public void onAnimationEnd(Animator animation) {
                 mState = STATE_OPEN;
                 setLayerType(LAYER_TYPE_NONE, null);
-                Cling cling = mLauncher.showFirstRunFoldersCling();
+                Cling cling = mLauncher.getLauncherClings().showFoldersCling();
                 if (cling != null) {
                     cling.bringScrimToFront();
                     bringToFront();
diff --git a/src/com/android/launcher3/Launcher.java b/src/com/android/launcher3/Launcher.java
index 269f53b..34e8b78 100644
--- a/src/com/android/launcher3/Launcher.java
+++ b/src/com/android/launcher3/Launcher.java
@@ -17,8 +17,6 @@
 
 package com.android.launcher3;
 
-import android.accounts.Account;
-import android.accounts.AccountManager;
 import android.animation.Animator;
 import android.animation.AnimatorListenerAdapter;
 import android.animation.AnimatorSet;
@@ -87,7 +85,6 @@
 import android.view.ViewTreeObserver.OnGlobalLayoutListener;
 import android.view.WindowManager;
 import android.view.accessibility.AccessibilityEvent;
-import android.view.accessibility.AccessibilityManager;
 import android.view.animation.AccelerateDecelerateInterpolator;
 import android.view.animation.DecelerateInterpolator;
 import android.view.inputmethod.InputMethodManager;
@@ -115,6 +112,7 @@
 import java.util.List;
 import java.util.concurrent.atomic.AtomicInteger;
 
+
 /**
  * Default launcher application.
  */
@@ -199,6 +197,8 @@
     public static final String SHOW_WEIGHT_WATCHER = "debug.show_mem";
     public static final boolean SHOW_WEIGHT_WATCHER_DEFAULT = false;
 
+    public static final String USER_HAS_MIGRATED = "launcher.user_migrated_from_old_data";
+
     /** The different states that Launcher can be in. */
     private enum State { NONE, WORKSPACE, APPS_CUSTOMIZE, APPS_CUSTOMIZE_SPRING_LOADED };
     private State mState = State.WORKSPACE;
@@ -207,8 +207,6 @@
     static final int APPWIDGET_HOST_ID = 1024;
     public static final int EXIT_SPRINGLOADED_MODE_SHORT_TIMEOUT = 300;
     private static final int ON_ACTIVITY_RESULT_ANIMATION_DELAY = 500;
-    private static final int SHOW_CLING_DURATION = 250;
-    private static final int DISMISS_CLING_DURATION = 200;
 
     private static final Object sLock = new Object();
     private static int sScreen = DEFAULT_SCREEN;
@@ -229,9 +227,11 @@
 
     private Workspace mWorkspace;
     private View mLauncherView;
+    private View mPageIndicators;
     private DragLayer mDragLayer;
     private DragController mDragController;
     private View mWeightWatcher;
+    private LauncherClings mLauncherClings;
 
     private AppWidgetManager mAppWidgetManager;
     private LauncherAppWidgetHost mAppWidgetHost;
@@ -284,8 +284,6 @@
     private boolean mVisible = false;
     private boolean mHasFocus = false;
     private boolean mAttached = false;
-    private static final boolean DISABLE_CLINGS = false;
-    private static final boolean DISABLE_CUSTOM_CLINGS = true;
 
     private static LocaleConfiguration sLocaleConfiguration = null;
 
@@ -341,9 +339,6 @@
 
     private BubbleTextView mWaitingForResume;
 
-    private HideFromAccessibilityHelper mHideFromAccessibilityHelper
-        = new HideFromAccessibilityHelper();
-
     private Runnable mBuildLayersRunnable = new Runnable() {
         public void run() {
             if (mWorkspace != null) {
@@ -418,6 +413,7 @@
         mIconCache = app.getIconCache();
         mIconCache.flushInvalidIcons(grid);
         mDragController = new DragController(this);
+        mLauncherClings = new LauncherClings(this);
         mInflater = getLayoutInflater();
 
         mStats = new Stats(this);
@@ -480,7 +476,11 @@
         unlockScreenOrientation(true);
 
         showFirstRunActivity();
-        showFirstRunCling();
+        if (mModel.canMigrateFromOldLauncherDb()) {
+            mLauncherClings.showMigrationCling();
+        } else {
+            mLauncherClings.showFirstRunCling();
+        }
     }
 
     protected void onUserLeaveHint() {
@@ -673,10 +673,6 @@
         return mInflater;
     }
 
-    public DragLayer getDragLayer() {
-        return mDragLayer;
-    }
-
     boolean isDraggingEnabled() {
         // We prevent dragging when we are loading the workspace as it is possible to pick up a view
         // that is subsequently removed from the workspace in startBinding().
@@ -1227,6 +1223,7 @@
         mLauncherView = findViewById(R.id.launcher);
         mDragLayer = (DragLayer) findViewById(R.id.drag_layer);
         mWorkspace = (Workspace) mDragLayer.findViewById(R.id.workspace);
+        mPageIndicators = mDragLayer.findViewById(R.id.page_indicator);
 
         mLauncherView.setSystemUiVisibility(
                 View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN | View.SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION);
@@ -1721,6 +1718,26 @@
         Toast.makeText(this, getString(strId), Toast.LENGTH_SHORT).show();
     }
 
+    public DragLayer getDragLayer() {
+        return mDragLayer;
+    }
+
+    public Workspace getWorkspace() {
+        return mWorkspace;
+    }
+
+    public Hotseat getHotseat() {
+        return mHotseat;
+    }
+
+    public View getOverviewPanel() {
+        return mOverviewPanel;
+    }
+
+    public SearchDropTargetBar getSearchBar() {
+        return mSearchDropTargetBar;
+    }
+
     public LauncherAppWidgetHost getAppWidgetHost() {
         return mAppWidgetHost;
     }
@@ -1729,6 +1746,14 @@
         return mModel;
     }
 
+    public LauncherClings getLauncherClings() {
+        return mLauncherClings;
+    }
+
+    protected SharedPreferences getSharedPrefs() {
+        return mSharedPrefs;
+    }
+
     public void closeSystemDialogs() {
         getWindow().closeAllPanels();
 
@@ -2690,7 +2715,7 @@
             closeFolder(folder);
 
             // Dismiss the folder cling
-            dismissFolderCling(null);
+            mLauncherClings.dismissFolderCling(null);
         }
     }
 
@@ -2766,15 +2791,6 @@
         return mHotseat != null && layout != null &&
                 (layout instanceof CellLayout) && (layout == mHotseat.getLayout());
     }
-    Hotseat getHotseat() {
-        return mHotseat;
-    }
-    View getOverviewPanel() {
-        return mOverviewPanel;
-    }
-    SearchDropTargetBar getSearchBar() {
-        return mSearchDropTargetBar;
-    }
 
     /**
      * Returns the CellLayout of the specified container at the specified screen.
@@ -2791,10 +2807,6 @@
         }
     }
 
-    Workspace getWorkspace() {
-        return mWorkspace;
-    }
-
     public boolean isAllAppsVisible() {
         return (mState == State.APPS_CUSTOMIZE) || (mOnResumeState == State.APPS_CUSTOMIZE);
     }
@@ -4225,245 +4237,13 @@
         }
     }
 
-    private boolean shouldRunFirstRunActivity() {
-        return !ActivityManager.isRunningInTestHarness();
-    }
-
-    /* Cling related */
-    private boolean isClingsEnabled() {
-        if (DISABLE_CLINGS) {
-            return false;
-        }
-
-        // For now, limit only to phones
-        LauncherAppState app = LauncherAppState.getInstance();
-        DeviceProfile grid = app.getDynamicGrid().getDeviceProfile();
-        if (grid.isTablet()) {
-            return false;
-        }
-        if (grid.isLandscape) {
-            return false;
-        }
-
-        // disable clings when running in a test harness
-        if(ActivityManager.isRunningInTestHarness()) return false;
-
-        // Disable clings for accessibility when explore by touch is enabled
-        final AccessibilityManager a11yManager = (AccessibilityManager) getSystemService(
-                ACCESSIBILITY_SERVICE);
-        if (a11yManager.isTouchExplorationEnabled()) {
-            return false;
-        }
-
-        // Restricted secondary users (child mode) will potentially have very few apps
-        // seeded when they start up for the first time. Clings won't work well with that
-//        boolean supportsLimitedUsers =
-//                android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.JELLY_BEAN_MR2;
-//        Account[] accounts = AccountManager.get(this).getAccounts();
-//        if (supportsLimitedUsers && accounts.length == 0) {
-//            UserManager um = (UserManager) getSystemService(Context.USER_SERVICE);
-//            Bundle restrictions = um.getUserRestrictions();
-//            if (restrictions.getBoolean(UserManager.DISALLOW_MODIFY_ACCOUNTS, false)) {
-//               return false;
-//            }
-//        }
-        return true;
-    }
-
-    private Cling initCling(int clingId, int scrimId, boolean animate,
-                            boolean dimNavBarVisibilty) {
-        Cling cling = (Cling) findViewById(clingId);
-        View scrim = null;
-        if (scrimId > 0) {
-            scrim = findViewById(R.id.cling_scrim);
-        }
-        if (cling != null) {
-            cling.init(this, scrim);
-            cling.show(animate, SHOW_CLING_DURATION);
-
-            if (dimNavBarVisibilty) {
-                cling.setSystemUiVisibility(cling.getSystemUiVisibility() |
-                        View.SYSTEM_UI_FLAG_LOW_PROFILE);
-            }
-        }
-        return cling;
-    }
-
-    private void dismissCling(final Cling cling, final Runnable postAnimationCb,
-                              final String flag, int duration, boolean restoreNavBarVisibilty) {
-        // To catch cases where siblings of top-level views are made invisible, just check whether
-        // the cling is directly set to GONE before dismissing it.
-        if (cling != null && cling.getVisibility() != View.GONE) {
-            final Runnable cleanUpClingCb = new Runnable() {
-                public void run() {
-                    cling.cleanup();
-                    SharedPreferences.Editor editor = mSharedPrefs.edit();
-                    editor.putBoolean(flag, true);
-                    editor.apply();
-                    if (postAnimationCb != null) {
-                        postAnimationCb.run();
-                    }
-                }
-            };
-            if (duration <= 0) {
-                cleanUpClingCb.run();
-            } else {
-                cling.hide(duration, cleanUpClingCb);
-            }
-            mHideFromAccessibilityHelper.restoreImportantForAccessibility(mDragLayer);
-
-            if (restoreNavBarVisibilty) {
-                cling.setSystemUiVisibility(cling.getSystemUiVisibility() &
-                        ~View.SYSTEM_UI_FLAG_LOW_PROFILE);
-            }
-        }
-    }
-
-    private void removeCling(int id) {
-        final View cling = findViewById(id);
-        if (cling != null) {
-            final ViewGroup parent = (ViewGroup) cling.getParent();
-            parent.post(new Runnable() {
-                @Override
-                public void run() {
-                    parent.removeView(cling);
-                }
-            });
-            mHideFromAccessibilityHelper.restoreImportantForAccessibility(mDragLayer);
-        }
-    }
-
-    private boolean skipCustomClingIfNoAccounts() {
-        Cling cling = (Cling) findViewById(R.id.workspace_cling);
-        boolean customCling = cling.getDrawIdentifier().equals("workspace_custom");
-        if (customCling) {
-            AccountManager am = AccountManager.get(this);
-            if (am == null) return false;
-            Account[] accounts = am.getAccountsByType("com.google");
-            return accounts.length == 0;
-        }
-        return false;
-    }
-
-    public void updateCustomContentHintVisibility() {
-        Cling cling = (Cling) findViewById(R.id.first_run_cling);
-        String ccHintStr = getFirstRunCustomContentHint();
-
-        if (mWorkspace.hasCustomContent()) {
-            // Show the custom content hint if ccHintStr is not empty
-            if (cling != null) {
-                setCustomContentHintVisibility(cling, ccHintStr, true, true);
-            }
-        } else {
-            // Hide the custom content hint
-            if (cling != null) {
-                setCustomContentHintVisibility(cling, ccHintStr, false, true);
-            }
-        }
-    }
-
-    private void setCustomContentHintVisibility(Cling cling, String ccHintStr, boolean visible,
-                                                boolean animate) {
-        final TextView ccHint = (TextView) cling.findViewById(R.id.custom_content_hint);
-        if (ccHint != null) {
-            if (visible && !ccHintStr.isEmpty()) {
-                ccHint.setText(ccHintStr);
-                ccHint.setVisibility(View.VISIBLE);
-                if (animate) {
-                    ccHint.setAlpha(0f);
-                    ccHint.animate().alpha(1f)
-                                    .setDuration(SHOW_CLING_DURATION)
-                                    .start();
-                } else {
-                    ccHint.setAlpha(1f);
-                }
-            } else {
-                if (animate) {
-                    ccHint.animate().alpha(0f)
-                                    .setDuration(SHOW_CLING_DURATION)
-                                    .setListener(new AnimatorListenerAdapter() {
-                                        @Override
-                                        public void onAnimationEnd(Animator animation) {
-                                            ccHint.setVisibility(View.GONE);
-                                        }
-                                    })
-                                    .start();
-                } else {
-                    ccHint.setAlpha(0f);
-                    ccHint.setVisibility(View.GONE);
-                }
-            }
-        }
-    }
-
-    public void showFirstRunActivity() {
-        if (shouldRunFirstRunActivity() && hasFirstRunActivity()
-                && !mSharedPrefs.getBoolean(FIRST_RUN_ACTIVITY_DISPLAYED, false)) {
-            Intent firstRunIntent = getFirstRunActivity();
-            if (firstRunIntent != null) {
-                startActivity(firstRunIntent);
-                markFirstRunActivityShown();
-            }
-        }
-    }
-
-    private void markFirstRunActivityShown() {
-        SharedPreferences.Editor editor = mSharedPrefs.edit();
-        editor.putBoolean(FIRST_RUN_ACTIVITY_DISPLAYED, true);
-        editor.apply();
-    }
-
-    public void showFirstRunCling() {
-        if (isClingsEnabled() &&
-                !mSharedPrefs.getBoolean(Cling.FIRST_RUN_CLING_DISMISSED_KEY, false) &&
-                !skipCustomClingIfNoAccounts() ) {
-
-
-            // If we're not using the default workspace layout, replace workspace cling
-            // with a custom workspace cling (usually specified in an overlay)
-            // For now, only do this on tablets
-            if (!DISABLE_CUSTOM_CLINGS) {
-                if (mSharedPrefs.getInt(LauncherProvider.DEFAULT_WORKSPACE_RESOURCE_ID, 0) != 0 &&
-                        getResources().getBoolean(R.bool.config_useCustomClings)) {
-                    // Use a custom cling
-                    View cling = findViewById(R.id.workspace_cling);
-                    ViewGroup clingParent = (ViewGroup) cling.getParent();
-                    int clingIndex = clingParent.indexOfChild(cling);
-                    clingParent.removeViewAt(clingIndex);
-                    View customCling = mInflater.inflate(R.layout.custom_workspace_cling, clingParent, false);
-                    clingParent.addView(customCling, clingIndex);
-                    customCling.setId(R.id.workspace_cling);
-                }
-            }
-            Cling cling = (Cling) findViewById(R.id.first_run_cling);
-            if (cling != null) {
-                String sbHintStr = getFirstRunClingSearchBarHint();
-                String ccHintStr = getFirstRunCustomContentHint();
-                if (!sbHintStr.isEmpty()) {
-                    TextView sbHint = (TextView) cling.findViewById(R.id.search_bar_hint);
-                    sbHint.setText(sbHintStr);
-                    sbHint.setVisibility(View.VISIBLE);
-                }
-                setCustomContentHintVisibility(cling, ccHintStr, true, false);
-            }
-            initCling(R.id.first_run_cling, 0, false, true);
-        } else {
-            removeCling(R.id.first_run_cling);
-        }
-    }
-
     /**
      * Called when the SearchBar hint should be changed.
      *
      * @param hint the hint to be displayed in the search bar.
      */
     protected void onSearchBarHintChanged(String hint) {
-        Cling cling = (Cling) findViewById(R.id.first_run_cling);
-        if (cling != null && cling.getVisibility() == View.VISIBLE && !hint.isEmpty()) {
-            TextView sbHint = (TextView) cling.findViewById(R.id.search_bar_hint);
-            sbHint.setText(hint);
-            sbHint.setVisibility(View.VISIBLE);
-        }
+        mLauncherClings.updateSearchBarHint(hint);
     }
 
     protected String getFirstRunClingSearchBarHint() {
@@ -4488,80 +4268,61 @@
         return "";
     }
 
-    public void showFirstRunWorkspaceCling() {
-        // Enable the clings only if they have not been dismissed before
-        if (isClingsEnabled() &&
-                !mSharedPrefs.getBoolean(Cling.WORKSPACE_CLING_DISMISSED_KEY, false)) {
-            Cling c = initCling(R.id.workspace_cling, 0, false, true);
-
-            // Set the focused hotseat app if there is one
-            c.setFocusedHotseatApp(getFirstRunFocusedHotseatAppDrawableId(),
-                    getFirstRunFocusedHotseatAppRank(),
-                    getFirstRunFocusedHotseatAppComponentName(),
-                    getFirstRunFocusedHotseatAppBubbleTitle(),
-                    getFirstRunFocusedHotseatAppBubbleDescription());
-        } else {
-            removeCling(R.id.workspace_cling);
-        }
-    }
-    public Cling showFirstRunFoldersCling() {
-        // Enable the clings only if they have not been dismissed before
-        if (isClingsEnabled() &&
-                !mSharedPrefs.getBoolean(Cling.FOLDER_CLING_DISMISSED_KEY, false)) {
-            Cling cling = initCling(R.id.folder_cling, R.id.cling_scrim,
-                    true, true);
-            return cling;
-        } else {
-            removeCling(R.id.folder_cling);
-            return null;
-        }
-    }
-    protected SharedPreferences getSharedPrefs() {
-        return mSharedPrefs;
-    }
-    public boolean isFolderClingVisible() {
-        Cling cling = (Cling) findViewById(R.id.folder_cling);
-        if (cling != null) {
-            return cling.getVisibility() == View.VISIBLE;
-        }
-        return false;
-    }
     public void dismissFirstRunCling(View v) {
-        Cling cling = (Cling) findViewById(R.id.first_run_cling);
-        Runnable cb = new Runnable() {
-            public void run() {
-                // Show the workspace cling next
-                showFirstRunWorkspaceCling();
-            }
-        };
-        dismissCling(cling, cb, Cling.FIRST_RUN_CLING_DISMISSED_KEY,
-                DISMISS_CLING_DURATION, false);
-
-        // Fade out the search bar for the workspace cling coming up
-        mSearchDropTargetBar.hideSearchBar(true);
+        mLauncherClings.dismissFirstRunCling(v);
+    }
+    public void dismissMigrationClingCopyApps(View v) {
+        mLauncherClings.dismissMigrationClingCopyApps(v);
+    }
+    public void dismissMigrationClingUseDefault(View v) {
+        mLauncherClings.dismissMigrationClingUseDefault(v);
+    }
+    public void dismissMigrationWorkspaceCling(View v) {
+        mLauncherClings.dismissMigrationWorkspaceCling(v);
     }
     public void dismissWorkspaceCling(View v) {
-        Cling cling = (Cling) findViewById(R.id.workspace_cling);
-        Runnable cb = null;
-        if (v == null) {
-            cb = new Runnable() {
-                public void run() {
-                    mWorkspace.enterOverviewMode();
-                }
-            };
-        }
-        dismissCling(cling, cb, Cling.WORKSPACE_CLING_DISMISSED_KEY,
-                DISMISS_CLING_DURATION, true);
-
-        // Fade in the search bar
-        mSearchDropTargetBar.showSearchBar(true);
+        mLauncherClings.dismissWorkspaceCling(v);
     }
     public void dismissFolderCling(View v) {
-        Cling cling = (Cling) findViewById(R.id.folder_cling);
-        dismissCling(cling, null, Cling.FOLDER_CLING_DISMISSED_KEY,
-                DISMISS_CLING_DURATION, true);
+        mLauncherClings.dismissFolderCling(v);
     }
 
+    private boolean shouldRunFirstRunActivity() {
+        return !ActivityManager.isRunningInTestHarness();
+    }
+
+    public void showFirstRunActivity() {
+        if (shouldRunFirstRunActivity() && hasFirstRunActivity()
+                && !mSharedPrefs.getBoolean(FIRST_RUN_ACTIVITY_DISPLAYED, false)) {
+            Intent firstRunIntent = getFirstRunActivity();
+            if (firstRunIntent != null) {
+                startActivity(firstRunIntent);
+                markFirstRunActivityShown();
+            }
+        }
+    }
+
+    private void markFirstRunActivityShown() {
+        SharedPreferences.Editor editor = mSharedPrefs.edit();
+        editor.putBoolean(FIRST_RUN_ACTIVITY_DISPLAYED, true);
+        editor.apply();
+    }
+
+    void showWorkspaceSearchAndHotseat() {
+        mWorkspace.setAlpha(1f);
+        mHotseat.setAlpha(1f);
+        mPageIndicators.setAlpha(1f);
+        mSearchDropTargetBar.showSearchBar(false);
+    }
+
+    void hideWorkspaceSearchAndHotseat() {
+        mWorkspace.setAlpha(0f);
+        mHotseat.setAlpha(0f);
+        mPageIndicators.setAlpha(0f);
+        mSearchDropTargetBar.hideSearchBar(false);
+    }
+
+
     public ItemInfo createAppDragInfo(Intent appLaunchIntent) {
         ResolveInfo ri = getPackageManager().resolveActivity(appLaunchIntent, 0);
         if (ri == null) {
diff --git a/src/com/android/launcher3/LauncherClings.java b/src/com/android/launcher3/LauncherClings.java
new file mode 100644
index 0000000..ec0a423
--- /dev/null
+++ b/src/com/android/launcher3/LauncherClings.java
@@ -0,0 +1,444 @@
+/*
+ * Copyright (C) 2008 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.accounts.Account;
+import android.accounts.AccountManager;
+import android.animation.Animator;
+import android.animation.AnimatorListenerAdapter;
+import android.app.ActivityManager;
+import android.content.Context;
+import android.content.SharedPreferences;
+import android.os.Bundle;
+import android.os.UserManager;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.view.ViewGroup;
+import android.view.accessibility.AccessibilityManager;
+import android.widget.TextView;
+
+class LauncherClings {
+    private static final String FIRST_RUN_CLING_DISMISSED_KEY = "cling_gel.first_run.dismissed";
+    private static final String MIGRATION_CLING_DISMISSED_KEY = "cling_gel.migration.dismissed";
+    private static final String MIGRATION_WORKSPACE_CLING_DISMISSED_KEY = "cling_gel.migration.dismissed";
+    private static final String WORKSPACE_CLING_DISMISSED_KEY = "cling_gel.workspace.dismissed";
+    private static final String FOLDER_CLING_DISMISSED_KEY = "cling_gel.folder.dismissed";
+
+    private static final boolean DISABLE_CLINGS = false;
+    private static final boolean DISABLE_CUSTOM_CLINGS = true;
+
+    private static final int SHOW_CLING_DURATION = 250;
+    private static final int DISMISS_CLING_DURATION = 200;
+
+    private Launcher mLauncher;
+    private LayoutInflater mInflater;
+    private HideFromAccessibilityHelper mHideFromAccessibilityHelper
+            = new HideFromAccessibilityHelper();
+
+    /** Ctor */
+    public LauncherClings(Launcher launcher) {
+        mLauncher = launcher;
+        mInflater = mLauncher.getLayoutInflater();
+    }
+
+    /** Initializes a cling */
+    private Cling initCling(int clingId, int scrimId, boolean animate,
+                            boolean dimNavBarVisibilty) {
+        Cling cling = (Cling) mLauncher.findViewById(clingId);
+        View scrim = null;
+        if (scrimId > 0) {
+            scrim = mLauncher.findViewById(R.id.cling_scrim);
+        }
+        if (cling != null) {
+            cling.init(mLauncher, scrim);
+            cling.show(animate, SHOW_CLING_DURATION);
+
+            if (dimNavBarVisibilty) {
+                cling.setSystemUiVisibility(cling.getSystemUiVisibility() |
+                        View.SYSTEM_UI_FLAG_LOW_PROFILE);
+            }
+        }
+        return cling;
+    }
+
+    /** Returns whether the clings are enabled or should be shown */
+    private boolean isClingsEnabled() {
+        if (DISABLE_CLINGS) {
+            return false;
+        }
+
+        // For now, limit only to phones
+        LauncherAppState app = LauncherAppState.getInstance();
+        DeviceProfile grid = app.getDynamicGrid().getDeviceProfile();
+        if (grid.isTablet()) {
+            return false;
+        }
+        if (grid.isLandscape) {
+            return false;
+        }
+
+        // disable clings when running in a test harness
+        if(ActivityManager.isRunningInTestHarness()) return false;
+
+        // Disable clings for accessibility when explore by touch is enabled
+        final AccessibilityManager a11yManager = (AccessibilityManager) mLauncher.getSystemService(
+                Launcher.ACCESSIBILITY_SERVICE);
+        if (a11yManager.isTouchExplorationEnabled()) {
+            return false;
+        }
+
+        // Restricted secondary users (child mode) will potentially have very few apps
+        // seeded when they start up for the first time. Clings won't work well with that
+        boolean supportsLimitedUsers =
+                android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.JELLY_BEAN_MR2;
+        Account[] accounts = AccountManager.get(mLauncher).getAccounts();
+        if (supportsLimitedUsers && accounts.length == 0) {
+            UserManager um = (UserManager) mLauncher.getSystemService(Context.USER_SERVICE);
+            Bundle restrictions = um.getUserRestrictions();
+            if (restrictions.getBoolean(UserManager.DISALLOW_MODIFY_ACCOUNTS, false)) {
+                return false;
+            }
+        }
+        return true;
+    }
+
+    /** Returns whether the folder cling is visible. */
+    public boolean isFolderClingVisible() {
+        Cling cling = (Cling) mLauncher.findViewById(R.id.folder_cling);
+        if (cling != null) {
+            return cling.getVisibility() == View.VISIBLE;
+        }
+        return false;
+    }
+
+    private boolean skipCustomClingIfNoAccounts() {
+        Cling cling = (Cling) mLauncher.findViewById(R.id.workspace_cling);
+        boolean customCling = cling.getDrawIdentifier().equals("workspace_custom");
+        if (customCling) {
+            AccountManager am = AccountManager.get(mLauncher);
+            if (am == null) return false;
+            Account[] accounts = am.getAccountsByType("com.google");
+            return accounts.length == 0;
+        }
+        return false;
+    }
+
+    /** Updates the first run cling custom content hint */
+    private void setCustomContentHintVisibility(Cling cling, String ccHintStr, boolean visible,
+                                                boolean animate) {
+        final TextView ccHint = (TextView) cling.findViewById(R.id.custom_content_hint);
+        if (ccHint != null) {
+            if (visible && !ccHintStr.isEmpty()) {
+                ccHint.setText(ccHintStr);
+                ccHint.setVisibility(View.VISIBLE);
+                if (animate) {
+                    ccHint.setAlpha(0f);
+                    ccHint.animate().alpha(1f)
+                            .setDuration(SHOW_CLING_DURATION)
+                            .start();
+                } else {
+                    ccHint.setAlpha(1f);
+                }
+            } else {
+                if (animate) {
+                    ccHint.animate().alpha(0f)
+                            .setDuration(SHOW_CLING_DURATION)
+                            .setListener(new AnimatorListenerAdapter() {
+                                @Override
+                                public void onAnimationEnd(Animator animation) {
+                                    ccHint.setVisibility(View.GONE);
+                                }
+                            })
+                            .start();
+                } else {
+                    ccHint.setAlpha(0f);
+                    ccHint.setVisibility(View.GONE);
+                }
+            }
+        }
+    }
+
+    /** Updates the first run cling custom content hint */
+    public void updateCustomContentHintVisibility() {
+        Cling cling = (Cling) mLauncher.findViewById(R.id.first_run_cling);
+        String ccHintStr = mLauncher.getFirstRunCustomContentHint();
+
+        if (mLauncher.getWorkspace().hasCustomContent()) {
+            // Show the custom content hint if ccHintStr is not empty
+            if (cling != null) {
+                setCustomContentHintVisibility(cling, ccHintStr, true, true);
+            }
+        } else {
+            // Hide the custom content hint
+            if (cling != null) {
+                setCustomContentHintVisibility(cling, ccHintStr, false, true);
+            }
+        }
+    }
+
+    /** Updates the first run cling search bar hint. */
+    public void updateSearchBarHint(String hint) {
+        Cling cling = (Cling) mLauncher.findViewById(R.id.first_run_cling);
+        if (cling != null && cling.getVisibility() == View.VISIBLE && !hint.isEmpty()) {
+            TextView sbHint = (TextView) cling.findViewById(R.id.search_bar_hint);
+            sbHint.setText(hint);
+            sbHint.setVisibility(View.VISIBLE);
+        }
+    }
+
+    /** Shows the first run cling */
+    public void showFirstRunCling() {
+        SharedPreferences sharedPrefs = mLauncher.getSharedPrefs();
+        if (isClingsEnabled() &&
+                !sharedPrefs.getBoolean(FIRST_RUN_CLING_DISMISSED_KEY, false) &&
+                !skipCustomClingIfNoAccounts() ) {
+
+
+            // If we're not using the default workspace layout, replace workspace cling
+            // with a custom workspace cling (usually specified in an overlay)
+            // For now, only do this on tablets
+            if (!DISABLE_CUSTOM_CLINGS) {
+                if (sharedPrefs.getInt(LauncherProvider.DEFAULT_WORKSPACE_RESOURCE_ID, 0) != 0 &&
+                        mLauncher.getResources().getBoolean(R.bool.config_useCustomClings)) {
+                    // Use a custom cling
+                    View cling = mLauncher.findViewById(R.id.workspace_cling);
+                    ViewGroup clingParent = (ViewGroup) cling.getParent();
+                    int clingIndex = clingParent.indexOfChild(cling);
+                    clingParent.removeViewAt(clingIndex);
+                    View customCling = mInflater.inflate(R.layout.custom_workspace_cling,
+                            clingParent, false);
+                    clingParent.addView(customCling, clingIndex);
+                    customCling.setId(R.id.workspace_cling);
+                }
+            }
+            Cling cling = (Cling) mLauncher.findViewById(R.id.first_run_cling);
+            if (cling != null) {
+                String sbHintStr = mLauncher.getFirstRunClingSearchBarHint();
+                String ccHintStr = mLauncher.getFirstRunCustomContentHint();
+                if (!sbHintStr.isEmpty()) {
+                    TextView sbHint = (TextView) cling.findViewById(R.id.search_bar_hint);
+                    sbHint.setText(sbHintStr);
+                    sbHint.setVisibility(View.VISIBLE);
+                }
+                setCustomContentHintVisibility(cling, ccHintStr, true, false);
+            }
+            initCling(R.id.first_run_cling, 0, false, true);
+        } else {
+            removeCling(R.id.first_run_cling);
+        }
+    }
+
+    public void showMigrationCling() {
+        // Enable the clings only if they have not been dismissed before
+        if (isClingsEnabled() && !mLauncher.getSharedPrefs().getBoolean(
+                MIGRATION_CLING_DISMISSED_KEY, false)) {
+            mLauncher.hideWorkspaceSearchAndHotseat();
+
+            Cling c = initCling(R.id.migration_cling, 0, false, true);
+            c.bringScrimToFront();
+            c.bringToFront();
+        } else {
+            removeCling(R.id.migration_cling);
+        }
+    }
+
+    public void showMigrationWorkspaceCling() {
+        // Enable the clings only if they have not been dismissed before
+        if (isClingsEnabled() && !mLauncher.getSharedPrefs().getBoolean(
+                MIGRATION_WORKSPACE_CLING_DISMISSED_KEY, false)) {
+            Cling c = initCling(R.id.migration_workspace_cling, 0, false, true);
+            c.updateMigrationWorkspaceBubblePosition();
+            c.bringScrimToFront();
+            c.bringToFront();
+        } else {
+            removeCling(R.id.migration_workspace_cling);
+        }
+    }
+
+    public void showWorkspaceCling() {
+        // Enable the clings only if they have not been dismissed before
+        if (isClingsEnabled() && !mLauncher.getSharedPrefs().getBoolean(
+                WORKSPACE_CLING_DISMISSED_KEY, false)) {
+            Cling c = initCling(R.id.workspace_cling, 0, false, true);
+
+            // Set the focused hotseat app if there is one
+            c.setFocusedHotseatApp(mLauncher.getFirstRunFocusedHotseatAppDrawableId(),
+                    mLauncher.getFirstRunFocusedHotseatAppRank(),
+                    mLauncher.getFirstRunFocusedHotseatAppComponentName(),
+                    mLauncher.getFirstRunFocusedHotseatAppBubbleTitle(),
+                    mLauncher.getFirstRunFocusedHotseatAppBubbleDescription());
+        } else {
+            removeCling(R.id.workspace_cling);
+        }
+    }
+    public Cling showFoldersCling() {
+        SharedPreferences sharedPrefs = mLauncher.getSharedPrefs();
+        // Enable the clings only if they have not been dismissed before
+        if (isClingsEnabled() &&
+                !sharedPrefs.getBoolean(FOLDER_CLING_DISMISSED_KEY, false) &&
+                !sharedPrefs.getBoolean(Launcher.USER_HAS_MIGRATED, false)) {
+            Cling cling = initCling(R.id.folder_cling, R.id.cling_scrim,
+                    true, true);
+            return cling;
+        } else {
+            removeCling(R.id.folder_cling);
+            return null;
+        }
+    }
+
+
+    /** Removes the cling outright from the DragLayer */
+    private void removeCling(int id) {
+        final View cling = mLauncher.findViewById(id);
+        if (cling != null) {
+            final ViewGroup parent = (ViewGroup) cling.getParent();
+            parent.post(new Runnable() {
+                @Override
+                public void run() {
+                    parent.removeView(cling);
+                }
+            });
+            mHideFromAccessibilityHelper.restoreImportantForAccessibility(mLauncher.getDragLayer());
+        }
+    }
+
+    /** Hides the specified Cling */
+    private void dismissCling(final Cling cling, final Runnable postAnimationCb,
+                              final String flag, int duration, boolean restoreNavBarVisibilty) {
+        // To catch cases where siblings of top-level views are made invisible, just check whether
+        // the cling is directly set to GONE before dismissing it.
+        if (cling != null && cling.getVisibility() != View.GONE) {
+            final Runnable cleanUpClingCb = new Runnable() {
+                public void run() {
+                    cling.cleanup();
+                    SharedPreferences.Editor editor = mLauncher.getSharedPrefs().edit();
+                    editor.putBoolean(flag, true);
+                    editor.apply();
+                    if (postAnimationCb != null) {
+                        postAnimationCb.run();
+                    }
+                }
+            };
+            if (duration <= 0) {
+                cleanUpClingCb.run();
+            } else {
+                cling.hide(duration, cleanUpClingCb);
+            }
+            mHideFromAccessibilityHelper.restoreImportantForAccessibility(mLauncher.getDragLayer());
+
+            if (restoreNavBarVisibilty) {
+                cling.setSystemUiVisibility(cling.getSystemUiVisibility() &
+                        ~View.SYSTEM_UI_FLAG_LOW_PROFILE);
+            }
+        }
+    }
+
+    public void dismissFirstRunCling(View v) {
+        Cling cling = (Cling) mLauncher.findViewById(R.id.first_run_cling);
+        Runnable cb = new Runnable() {
+            public void run() {
+                // Show the workspace cling next
+                showWorkspaceCling();
+            }
+        };
+        dismissCling(cling, cb, FIRST_RUN_CLING_DISMISSED_KEY,
+                DISMISS_CLING_DURATION, false);
+
+        // Fade out the search bar for the workspace cling coming up
+        mLauncher.getSearchBar().hideSearchBar(true);
+    }
+
+    private void dismissMigrationCling() {
+        mLauncher.showWorkspaceSearchAndHotseat();
+        Runnable dismissCb = new Runnable() {
+            public void run() {
+                Cling cling = (Cling) mLauncher.findViewById(R.id.migration_cling);
+                Runnable cb = new Runnable() {
+                    public void run() {
+                        // Show the migration workspace cling next
+                        showMigrationWorkspaceCling();
+                    }
+                };
+                dismissCling(cling, cb, WORKSPACE_CLING_DISMISSED_KEY,
+                        DISMISS_CLING_DURATION, true);
+            }
+        };
+        mLauncher.getWorkspace().post(dismissCb);
+    }
+
+    private void dismissAnyWorkspaceCling(Cling cling, View v) {
+        Runnable cb = null;
+        if (v == null) {
+            cb = new Runnable() {
+                public void run() {
+                    mLauncher.getWorkspace().enterOverviewMode();
+                }
+            };
+        }
+        dismissCling(cling, cb, WORKSPACE_CLING_DISMISSED_KEY,
+                DISMISS_CLING_DURATION, true);
+
+        // Fade in the search bar
+        mLauncher.getSearchBar().showSearchBar(true);
+    }
+
+    public void dismissMigrationClingCopyApps(View v) {
+        // Copy the shortcuts from the old database
+        LauncherModel model = mLauncher.getModel();
+//        model.resetLoadedState(false, true);
+//        model.startLoader(false, PagedView.INVALID_RESTORE_PAGE,
+//                LauncherModel.LOADER_FLAG_CLEAR_WORKSPACE
+//                        | LauncherModel.LOADER_FLAG_MIGRATE_SHORTCUTS);
+
+        // Set the flag to skip the folder cling
+        String spKey = LauncherAppState.getSharedPreferencesKey();
+        SharedPreferences sp = mLauncher.getSharedPreferences(spKey, Context.MODE_PRIVATE);
+        SharedPreferences.Editor editor = sp.edit();
+        editor.putBoolean(Launcher.USER_HAS_MIGRATED, true);
+        editor.apply();
+
+        // Disable the migration cling
+        dismissMigrationCling();
+    }
+
+    public void dismissMigrationClingUseDefault(View v) {
+        // Clear the workspace
+        LauncherModel model = mLauncher.getModel();
+//        model.resetLoadedState(false, true);
+//        model.startLoader(false, PagedView.INVALID_RESTORE_PAGE,
+//                LauncherModel.LOADER_FLAG_CLEAR_WORKSPACE);
+
+        // Disable the migration cling
+        dismissMigrationCling();
+    }
+
+    public void dismissMigrationWorkspaceCling(View v) {
+        Cling cling = (Cling) mLauncher.findViewById(R.id.migration_workspace_cling);
+        dismissAnyWorkspaceCling(cling, v);
+    }
+
+    public void dismissWorkspaceCling(View v) {
+        Cling cling = (Cling) mLauncher.findViewById(R.id.workspace_cling);
+        dismissAnyWorkspaceCling(cling, v);
+    }
+
+    public void dismissFolderCling(View v) {
+        Cling cling = (Cling) mLauncher.findViewById(R.id.folder_cling);
+        dismissCling(cling, null, FOLDER_CLING_DISMISSED_KEY,
+                DISMISS_CLING_DURATION, true);
+    }
+}
\ No newline at end of file
diff --git a/src/com/android/launcher3/LauncherModel.java b/src/com/android/launcher3/LauncherModel.java
index b2cfb24..498d91a 100644
--- a/src/com/android/launcher3/LauncherModel.java
+++ b/src/com/android/launcher3/LauncherModel.java
@@ -77,7 +77,9 @@
 
     private static final int ITEMS_CHUNK = 6; // batch size for the workspace icons
     private static final long INVALID_SCREEN_ID = -1L;
+
     private final boolean mAppsCanBeOnRemoveableStorage;
+    private final boolean mOldContentProviderExists;
 
     private final LauncherAppState mApp;
     private final Object mLock = new Object();
@@ -181,9 +183,12 @@
     }
 
     LauncherModel(LauncherAppState app, IconCache iconCache, AppFilter appFilter) {
-        final Context context = app.getContext();
+        Context context = app.getContext();
+        ContentResolver contentResolver = context.getContentResolver();
 
         mAppsCanBeOnRemoveableStorage = Environment.isExternalStorageRemovable();
+        mOldContentProviderExists = (contentResolver.acquireContentProviderClient(
+                LauncherSettings.Favorites.OLD_CONTENT_URI) != null);
         mApp = app;
         mBgAllAppsList = new AllAppsList(iconCache, appFilter);
         mIconCache = iconCache;
@@ -218,6 +223,10 @@
         }
     }
 
+    boolean canMigrateFromOldLauncherDb() {
+        return mOldContentProviderExists;
+    }
+
     static boolean findNextAvailableIconSpaceInScreen(ArrayList<ItemInfo> items, int[] xy,
                                  long screen) {
         LauncherAppState app = LauncherAppState.getInstance();
@@ -1651,7 +1660,7 @@
             }
         }
 
-        /** Returns whether this is an upgradge path */
+        /** Returns whether this is an upgrade path */
         private boolean loadWorkspace() {
             // Log to disk
             Launcher.addDumpLog(TAG, "11683562 - loadWorkspace()", true);
diff --git a/src/com/android/launcher3/Workspace.java b/src/com/android/launcher3/Workspace.java
index b6276c0..2ce9eb3 100644
--- a/src/com/android/launcher3/Workspace.java
+++ b/src/com/android/launcher3/Workspace.java
@@ -579,7 +579,7 @@
         mDefaultPage = mOriginalDefaultPage + 1;
 
         // Update the custom content hint
-        mLauncher.updateCustomContentHintVisibility();
+        mLauncher.getLauncherClings().updateCustomContentHintVisibility();
         if (mRestorePage != INVALID_RESTORE_PAGE) {
             mRestorePage = mRestorePage + 1;
         } else {
@@ -608,7 +608,7 @@
         mDefaultPage = mOriginalDefaultPage - 1;
 
         // Update the custom content hint
-        mLauncher.updateCustomContentHintVisibility();
+        mLauncher.getLauncherClings().updateCustomContentHintVisibility();
         if (mRestorePage != INVALID_RESTORE_PAGE) {
             mRestorePage = mRestorePage - 1;
         } else {