Adding migration Clings. (Bug 11973614)

- Refactoring Launcher cling code out to LauncherClings.

Change-Id: Iff4f84f5b8bfeb69b1be0b4802022c3eb20b6f2c
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