2 * Copyright (C) 2008 The Android Open Source Project
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
8 * http://www.apache.org/licenses/LICENSE-2.0
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
17 package com.android.launcher3;
19 import android.animation.ObjectAnimator;
20 import android.animation.PropertyValuesHolder;
21 import android.annotation.TargetApi;
22 import android.app.ActivityManager;
23 import android.content.Context;
24 import android.content.SharedPreferences;
25 import android.graphics.drawable.Drawable;
26 import android.os.Build;
27 import android.os.Bundle;
28 import android.os.UserManager;
29 import android.provider.Settings;
30 import android.view.LayoutInflater;
31 import android.view.View;
32 import android.view.View.OnClickListener;
33 import android.view.View.OnLongClickListener;
34 import android.view.ViewGroup;
35 import android.view.ViewTreeObserver.OnGlobalLayoutListener;
36 import android.view.accessibility.AccessibilityManager;
38 import com.android.launcher3.util.Thunk;
40 class LauncherClings implements OnClickListener {
41 private static final String MIGRATION_CLING_DISMISSED_KEY = "cling_gel.migration.dismissed";
42 private static final String WORKSPACE_CLING_DISMISSED_KEY = "cling_gel.workspace.dismissed";
44 private static final String TAG_CROP_TOP_AND_SIDES = "crop_bg_top_and_sides";
46 private static final int SHOW_CLING_DURATION = 250;
47 private static final int DISMISS_CLING_DURATION = 200;
49 // New Secure Setting in L
50 private static final String SKIP_FIRST_USE_HINTS = "skip_first_use_hints";
52 @Thunk Launcher mLauncher;
53 private LayoutInflater mInflater;
54 @Thunk boolean mIsVisible;
57 public LauncherClings(Launcher launcher) {
59 mInflater = LayoutInflater.from(mLauncher);
63 public void onClick(View v) {
65 if (id == R.id.cling_dismiss_migration_use_default) {
66 // Disable the migration cling
67 dismissMigrationCling();
68 } else if (id == R.id.cling_dismiss_migration_copy_apps) {
69 // Copy the shortcuts from the old database
70 LauncherModel model = mLauncher.getModel();
71 model.resetLoadedState(false, true);
72 model.startLoader(PagedView.INVALID_RESTORE_PAGE,
73 LauncherModel.LOADER_FLAG_CLEAR_WORKSPACE
74 | LauncherModel.LOADER_FLAG_MIGRATE_SHORTCUTS);
75 // Set the flag to skip the folder cling
76 String spKey = LauncherAppState.getSharedPreferencesKey();
77 SharedPreferences sp = mLauncher.getSharedPreferences(spKey, Context.MODE_PRIVATE);
78 SharedPreferences.Editor editor = sp.edit();
79 editor.putBoolean(Launcher.USER_HAS_MIGRATED, true);
81 // Disable the migration cling
82 dismissMigrationCling();
83 } else if (id == R.id.cling_dismiss_longpress_info) {
84 dismissLongPressCling();
89 * Shows the migration cling.
91 * This flow is mutually exclusive with showFirstRunCling, and only runs if this Launcher
92 * package was not preinstalled and there exists a db to migrate from.
94 public void showMigrationCling() {
96 mLauncher.hideWorkspaceSearchAndHotseat();
98 ViewGroup root = (ViewGroup) mLauncher.findViewById(R.id.launcher);
99 View inflated = mInflater.inflate(R.layout.migration_cling, root);
100 inflated.findViewById(R.id.cling_dismiss_migration_copy_apps).setOnClickListener(this);
101 inflated.findViewById(R.id.cling_dismiss_migration_use_default).setOnClickListener(this);
104 private void dismissMigrationCling() {
105 mLauncher.showWorkspaceSearchAndHotseat();
106 Runnable dismissCb = new Runnable() {
108 Runnable cb = new Runnable() {
110 // Show the longpress cling next
111 showLongPressCling(false);
114 dismissCling(mLauncher.findViewById(R.id.migration_cling), cb,
115 MIGRATION_CLING_DISMISSED_KEY, DISMISS_CLING_DURATION);
118 mLauncher.getWorkspace().post(dismissCb);
121 public void showLongPressCling(boolean showWelcome) {
123 ViewGroup root = (ViewGroup) mLauncher.findViewById(R.id.launcher);
124 View cling = mInflater.inflate(R.layout.longpress_cling, root, false);
126 cling.setOnLongClickListener(new OnLongClickListener() {
129 public boolean onLongClick(View v) {
130 mLauncher.showOverviewMode(true);
131 dismissLongPressCling();
136 final ViewGroup content = (ViewGroup) cling.findViewById(R.id.cling_content);
137 mInflater.inflate(showWelcome ? R.layout.longpress_cling_welcome_content
138 : R.layout.longpress_cling_content, content);
139 content.findViewById(R.id.cling_dismiss_longpress_info).setOnClickListener(this);
141 if (TAG_CROP_TOP_AND_SIDES.equals(content.getTag())) {
142 Drawable bg = new BorderCropDrawable(mLauncher.getResources().getDrawable(R.drawable.cling_bg),
143 true, true, true, false);
144 content.setBackground(bg);
150 // This is the first cling being shown. No need to animate.
155 content.getViewTreeObserver().addOnGlobalLayoutListener(new OnGlobalLayoutListener() {
158 public void onGlobalLayout() {
159 content.getViewTreeObserver().removeOnGlobalLayoutListener(this);
162 if (TAG_CROP_TOP_AND_SIDES.equals(content.getTag())) {
163 content.setTranslationY(-content.getMeasuredHeight());
164 anim = LauncherAnimUtils.ofFloat(content, "translationY", 0);
166 content.setScaleX(0);
167 content.setScaleY(0);
168 PropertyValuesHolder scaleX = PropertyValuesHolder.ofFloat("scaleX", 1);
169 PropertyValuesHolder scaleY = PropertyValuesHolder.ofFloat("scaleY", 1);
170 anim = LauncherAnimUtils.ofPropertyValuesHolder(content, scaleX, scaleY);
173 anim.setDuration(SHOW_CLING_DURATION);
174 anim.setInterpolator(new LogDecelerateInterpolator(100, 0));
180 @Thunk void dismissLongPressCling() {
181 Runnable dismissCb = new Runnable() {
183 dismissCling(mLauncher.findViewById(R.id.longpress_cling), null,
184 WORKSPACE_CLING_DISMISSED_KEY, DISMISS_CLING_DURATION);
187 mLauncher.getWorkspace().post(dismissCb);
190 /** Hides the specified Cling */
191 @Thunk void dismissCling(final View cling, final Runnable postAnimationCb,
192 final String flag, int duration) {
193 // To catch cases where siblings of top-level views are made invisible, just check whether
194 // the cling is directly set to GONE before dismissing it.
195 if (cling != null && cling.getVisibility() != View.GONE) {
196 final Runnable cleanUpClingCb = new Runnable() {
198 cling.setVisibility(View.GONE);
199 mLauncher.getSharedPrefs().edit()
200 .putBoolean(flag, true)
203 if (postAnimationCb != null) {
204 postAnimationCb.run();
209 cleanUpClingCb.run();
211 cling.animate().alpha(0).setDuration(duration).withEndAction(cleanUpClingCb);
216 public boolean isVisible() {
220 /** Returns whether the clings are enabled or should be shown */
221 @TargetApi(Build.VERSION_CODES.JELLY_BEAN_MR2)
222 private boolean areClingsEnabled() {
223 // disable clings when running in a test harness
224 if(ActivityManager.isRunningInTestHarness()) return false;
226 // Disable clings for accessibility when explore by touch is enabled
227 final AccessibilityManager a11yManager = (AccessibilityManager) mLauncher.getSystemService(
228 Launcher.ACCESSIBILITY_SERVICE);
229 if (a11yManager.isTouchExplorationEnabled()) {
233 // Restricted secondary users (child mode) will potentially have very few apps
234 // seeded when they start up for the first time. Clings won't work well with that
235 if (Utilities.ATLEAST_JB_MR2) {
236 UserManager um = (UserManager) mLauncher.getSystemService(Context.USER_SERVICE);
237 Bundle restrictions = um.getUserRestrictions();
238 if (restrictions.getBoolean(UserManager.DISALLOW_MODIFY_ACCOUNTS, false)) {
242 if (Settings.Secure.getInt(mLauncher.getContentResolver(), SKIP_FIRST_USE_HINTS, 0)
249 public boolean shouldShowFirstRunOrMigrationClings() {
250 SharedPreferences sharedPrefs = mLauncher.getSharedPrefs();
251 return areClingsEnabled() &&
252 !sharedPrefs.getBoolean(WORKSPACE_CLING_DISMISSED_KEY, false) &&
253 !sharedPrefs.getBoolean(MIGRATION_CLING_DISMISSED_KEY, false);
256 public static void synchonouslyMarkFirstRunClingDismissed(Context ctx) {
257 SharedPreferences prefs = ctx.getSharedPreferences(
258 LauncherAppState.getSharedPreferencesKey(), Context.MODE_PRIVATE);
259 SharedPreferences.Editor editor = prefs.edit();
260 editor.putBoolean(WORKSPACE_CLING_DISMISSED_KEY, true);