| /* |
| * 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.ComponentName; |
| import android.content.Context; |
| import android.content.SharedPreferences; |
| import android.content.pm.ApplicationInfo; |
| import android.content.pm.PackageManager; |
| import android.graphics.Rect; |
| import android.os.Bundle; |
| import android.os.UserManager; |
| import android.provider.Settings; |
| 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_workspace.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 int SHOW_CLING_DURATION = 250; |
| private static final int DISMISS_CLING_DURATION = 200; |
| |
| // New Secure Setting in L |
| private static final String SKIP_FIRST_USE_HINTS = "skip_first_use_hints"; |
| |
| 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(scrimId); |
| } |
| 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 areClingsEnabled() { |
| if (DISABLE_CLINGS) { |
| 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; |
| } |
| } |
| if (Settings.Secure.getInt(mLauncher.getContentResolver(), SKIP_FIRST_USE_HINTS, 0) |
| == 1) { |
| 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); |
| } |
| } |
| |
| public boolean shouldShowFirstRunOrMigrationClings() { |
| SharedPreferences sharedPrefs = mLauncher.getSharedPrefs(); |
| return areClingsEnabled() && |
| !sharedPrefs.getBoolean(FIRST_RUN_CLING_DISMISSED_KEY, false) && |
| !sharedPrefs.getBoolean(MIGRATION_CLING_DISMISSED_KEY, false); |
| } |
| |
| public void removeFirstRunAndMigrationClings() { |
| removeCling(R.id.first_run_cling); |
| removeCling(R.id.migration_cling); |
| } |
| |
| /** |
| * Shows the first run cling. |
| * |
| * This flow is mutually exclusive with showMigrationCling, and only runs if this Launcher |
| * package was preinstalled or there is no db to migrate from. |
| */ |
| public void showFirstRunCling() { |
| if (!skipCustomClingIfNoAccounts()) { |
| 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 { |
| removeFirstRunAndMigrationClings(); |
| } |
| } |
| |
| /** |
| * Shows the migration cling. |
| * |
| * This flow is mutually exclusive with showFirstRunCling, and only runs if this Launcher |
| * package was not preinstalled and there exists a db to migrate from. |
| */ |
| public void showMigrationCling() { |
| mLauncher.hideWorkspaceSearchAndHotseat(); |
| |
| Cling c = initCling(R.id.migration_cling, 0, false, true); |
| c.bringScrimToFront(); |
| c.bringToFront(); |
| } |
| |
| public void showMigrationWorkspaceCling() { |
| // Enable the clings only if they have not been dismissed before |
| if (areClingsEnabled() && !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 (areClingsEnabled() && !mLauncher.getSharedPrefs().getBoolean( |
| WORKSPACE_CLING_DISMISSED_KEY, false)) { |
| Cling c = initCling(R.id.workspace_cling, 0, false, true); |
| c.updateWorkspaceBubblePosition(); |
| if (mLauncher.shouldClingFocusHotseatApp()) { |
| // Set the focused hotseat app |
| 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 (areClingsEnabled() && |
| !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); |
| Folder openFolder = mLauncher.getWorkspace().getOpenFolder(); |
| if (openFolder != null) { |
| Rect openFolderRect = new Rect(); |
| openFolder.getHitRect(openFolderRect); |
| cling.setOpenFolderRect(openFolderRect); |
| openFolder.bringToFront(); |
| } |
| return cling; |
| } else { |
| removeCling(R.id.folder_cling); |
| return null; |
| } |
| } |
| |
| public static void synchonouslyMarkFirstRunClingDismissed(Context ctx) { |
| SharedPreferences prefs = ctx.getSharedPreferences( |
| LauncherAppState.getSharedPreferencesKey(), Context.MODE_PRIVATE); |
| SharedPreferences.Editor editor = prefs.edit(); |
| editor.putBoolean(LauncherClings.FIRST_RUN_CLING_DISMISSED_KEY, true); |
| editor.commit(); |
| } |
| |
| /** 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, MIGRATION_CLING_DISMISSED_KEY, |
| DISMISS_CLING_DURATION, true); |
| } |
| }; |
| mLauncher.getWorkspace().post(dismissCb); |
| } |
| |
| private void dismissAnyWorkspaceCling(Cling cling, String key, View v) { |
| Runnable cb = null; |
| if (v == null) { |
| cb = new Runnable() { |
| public void run() { |
| mLauncher.getWorkspace().enterOverviewMode(); |
| } |
| }; |
| } |
| dismissCling(cling, cb, key, DISMISS_CLING_DURATION, true); |
| |
| // Fade in the search bar |
| mLauncher.getSearchBar().showSearchBar(true); |
| } |
| |
| public void markFolderClingDismissedIfNecessary() { |
| SharedPreferences prefs = mLauncher.getSharedPrefs(); |
| if (!prefs.getBoolean(FOLDER_CLING_DISMISSED_KEY, false)) { |
| SharedPreferences.Editor editor = prefs.edit(); |
| editor.putBoolean(FOLDER_CLING_DISMISSED_KEY, true); |
| editor.apply(); |
| } |
| } |
| |
| 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) { |
| // Don't need to do anything special here. We've already loaded the default workspace, |
| // (which is the default loader behavior triggered from Launcher#onCreate.). |
| |
| // Disable the migration cling |
| dismissMigrationCling(); |
| } |
| |
| public void dismissMigrationWorkspaceCling(View v) { |
| Cling cling = (Cling) mLauncher.findViewById(R.id.migration_workspace_cling); |
| dismissAnyWorkspaceCling(cling, MIGRATION_WORKSPACE_CLING_DISMISSED_KEY, v); |
| } |
| |
| public void dismissWorkspaceCling(View v) { |
| Cling cling = (Cling) mLauncher.findViewById(R.id.workspace_cling); |
| dismissAnyWorkspaceCling(cling, WORKSPACE_CLING_DISMISSED_KEY, 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); |
| } |
| } |