blob: 840fb52c32509c6d69b7302132f14a9ce867c9bb [file] [log] [blame]
Winson Chunga6945242014-01-08 14:04:34 -08001/*
2 * Copyright (C) 2008 The Android Open Source Project
3 *
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
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
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.
15 */
16
17package com.android.launcher3;
18
19import android.accounts.Account;
20import android.accounts.AccountManager;
Sunny Goyal424418b2014-08-22 16:09:37 -070021import android.animation.AnimatorSet;
22import android.animation.ObjectAnimator;
23import android.animation.PropertyValuesHolder;
24import android.animation.ValueAnimator;
Winson Chunga6945242014-01-08 14:04:34 -080025import android.app.ActivityManager;
26import android.content.Context;
27import android.content.SharedPreferences;
28import android.os.Bundle;
29import android.os.UserManager;
Amith Yamasani5ba21b62014-06-25 16:14:02 +053030import android.provider.Settings;
Sunny Goyal424418b2014-08-22 16:09:37 -070031import android.view.ContextThemeWrapper;
Winson Chunga6945242014-01-08 14:04:34 -080032import android.view.LayoutInflater;
33import android.view.View;
Sunny Goyal424418b2014-08-22 16:09:37 -070034import android.view.View.OnClickListener;
35import android.view.View.OnLongClickListener;
Winson Chunga6945242014-01-08 14:04:34 -080036import android.view.ViewGroup;
Sunny Goyal424418b2014-08-22 16:09:37 -070037import android.view.ViewTreeObserver.OnGlobalLayoutListener;
Winson Chunga6945242014-01-08 14:04:34 -080038import android.view.accessibility.AccessibilityManager;
Winson Chunga6945242014-01-08 14:04:34 -080039
Sunny Goyal424418b2014-08-22 16:09:37 -070040class LauncherClings implements OnClickListener {
Winson Chunga6945242014-01-08 14:04:34 -080041 private static final String MIGRATION_CLING_DISMISSED_KEY = "cling_gel.migration.dismissed";
Winson Chunga6945242014-01-08 14:04:34 -080042 private static final String WORKSPACE_CLING_DISMISSED_KEY = "cling_gel.workspace.dismissed";
Sunny Goyal424418b2014-08-22 16:09:37 -070043
44 private static final String ANIM_SLIDE_FROM_BOTTOM = "slide_from_bottom";
45 private static final String ANIM_SLIDE_FROM_TOP = "slide_from_top";
Winson Chunga6945242014-01-08 14:04:34 -080046
47 private static final boolean DISABLE_CLINGS = false;
Winson Chunga6945242014-01-08 14:04:34 -080048
49 private static final int SHOW_CLING_DURATION = 250;
50 private static final int DISMISS_CLING_DURATION = 200;
51
Amith Yamasani5ba21b62014-06-25 16:14:02 +053052 // New Secure Setting in L
53 private static final String SKIP_FIRST_USE_HINTS = "skip_first_use_hints";
54
Winson Chunga6945242014-01-08 14:04:34 -080055 private Launcher mLauncher;
56 private LayoutInflater mInflater;
Winson Chunga6945242014-01-08 14:04:34 -080057
58 /** Ctor */
59 public LauncherClings(Launcher launcher) {
60 mLauncher = launcher;
Sunny Goyal424418b2014-08-22 16:09:37 -070061 mInflater = LayoutInflater.from(new
62 ContextThemeWrapper(mLauncher, android.R.style.Theme_DeviceDefault));
Winson Chunga6945242014-01-08 14:04:34 -080063 }
64
Sunny Goyal424418b2014-08-22 16:09:37 -070065 @Override
66 public void onClick(View v) {
67 int id = v.getId();
68 if (id == R.id.cling_dismiss_migration_use_default) {
69 // Disable the migration cling
70 dismissMigrationCling();
71 } else if (id == R.id.cling_dismiss_migration_copy_apps) {
72 // Copy the shortcuts from the old database
73 LauncherModel model = mLauncher.getModel();
74 model.resetLoadedState(false, true);
75 model.startLoader(false, PagedView.INVALID_RESTORE_PAGE,
76 LauncherModel.LOADER_FLAG_CLEAR_WORKSPACE
77 | LauncherModel.LOADER_FLAG_MIGRATE_SHORTCUTS);
78 // Set the flag to skip the folder cling
79 String spKey = LauncherAppState.getSharedPreferencesKey();
80 SharedPreferences sp = mLauncher.getSharedPreferences(spKey, Context.MODE_PRIVATE);
81 SharedPreferences.Editor editor = sp.edit();
82 editor.putBoolean(Launcher.USER_HAS_MIGRATED, true);
83 editor.apply();
84 // Disable the migration cling
85 dismissMigrationCling();
86 } else if (id == R.id.cling_dismiss_longpress_info) {
87 dismissLongPressCling();
Winson Chunga6945242014-01-08 14:04:34 -080088 }
Sunny Goyal424418b2014-08-22 16:09:37 -070089 }
Winson Chunga6945242014-01-08 14:04:34 -080090
Sunny Goyal424418b2014-08-22 16:09:37 -070091 /**
92 * Shows the migration cling.
93 *
94 * This flow is mutually exclusive with showFirstRunCling, and only runs if this Launcher
95 * package was not preinstalled and there exists a db to migrate from.
96 */
97 public void showMigrationCling() {
98 mLauncher.hideWorkspaceSearchAndHotseat();
99
100 ViewGroup root = (ViewGroup) mLauncher.findViewById(R.id.launcher);
101 View inflated = mInflater.inflate(R.layout.migration_cling, root);
102 inflated.findViewById(R.id.cling_dismiss_migration_copy_apps).setOnClickListener(this);
103 inflated.findViewById(R.id.cling_dismiss_migration_use_default).setOnClickListener(this);
104 }
105
106 private void dismissMigrationCling() {
107 mLauncher.showWorkspaceSearchAndHotseat();
108 Runnable dismissCb = new Runnable() {
109 public void run() {
110 Runnable cb = new Runnable() {
111 public void run() {
112 // Show the longpress cling next
113 showLongPressCling(false);
114 }
115 };
116 dismissCling(mLauncher.findViewById(R.id.migration_cling), cb,
117 MIGRATION_CLING_DISMISSED_KEY, DISMISS_CLING_DURATION);
118 }
119 };
120 mLauncher.getWorkspace().post(dismissCb);
121 }
122
123 public void showLongPressCling(boolean showWelcome) {
124 ViewGroup root = (ViewGroup) mLauncher.findViewById(R.id.launcher);
125 View cling = mInflater.inflate(R.layout.longpress_cling, root, false);
126
127 final ClearCircleLayout hole = (ClearCircleLayout) cling.findViewById(R.id.cling_longpress_hole);
128 hole.initHole(mLauncher);
129 hole.setClickable(true);
130 hole.setOnLongClickListener(new OnLongClickListener() {
131
132 @Override
133 public boolean onLongClick(View v) {
134 mLauncher.getWorkspace().enterOverviewMode();
135 dismissLongPressCling();
136 return true;
137 }
138 });
139
140 final ViewGroup content = (ViewGroup) cling.findViewById(R.id.cling_content);
141 mInflater.inflate(showWelcome ? R.layout.longpress_cling_welcome_content
142 : R.layout.longpress_cling_content, content);
143 content.findViewById(R.id.cling_dismiss_longpress_info).setOnClickListener(this);
144
145 root.addView(cling);
146
147 if (showWelcome) {
148 // This is the first cling being shown. No need to animate.
149 return;
150 }
151
152 // Animate
153 content.getViewTreeObserver().addOnGlobalLayoutListener(new OnGlobalLayoutListener() {
154
155 @Override
156 public void onGlobalLayout() {
157 content.getViewTreeObserver().removeOnGlobalLayoutListener(this);
158
159 hole.setAlpha(0);
160 ValueAnimator anim1 = LauncherAnimUtils.ofFloat(hole, "alpha", 1);
161
162 ObjectAnimator anim2;
163
164 if (ANIM_SLIDE_FROM_TOP.equals(content.getTag())) {
165 content.setTranslationY(-content.getMeasuredHeight());
166 anim2 = LauncherAnimUtils.ofFloat(content, "translationY", 0);
167 } else if (ANIM_SLIDE_FROM_BOTTOM.equals(content.getTag())) {
168 content.setTranslationY(content.getMeasuredHeight());
169 anim2 = LauncherAnimUtils.ofFloat(content, "translationY", 0);
170 } else {
171 content.setScaleX(0);
172 content.setScaleY(0);
173 PropertyValuesHolder scaleX = PropertyValuesHolder.ofFloat("scaleX", 1);
174 PropertyValuesHolder scaleY = PropertyValuesHolder.ofFloat("scaleY", 1);
175 anim2 = LauncherAnimUtils.ofPropertyValuesHolder(content, scaleX, scaleY);
176 }
177
178 AnimatorSet anim = LauncherAnimUtils.createAnimatorSet();
179 anim.setDuration(SHOW_CLING_DURATION);
180 anim.setInterpolator(new LogDecelerateInterpolator(100, 0));
181 anim.playTogether(anim1, anim2);
182 anim.start();
183 }
184 });
185 }
186
187 private void dismissLongPressCling() {
188 Runnable dismissCb = new Runnable() {
189 public void run() {
190 dismissCling(mLauncher.findViewById(R.id.longpress_cling), null,
191 WORKSPACE_CLING_DISMISSED_KEY, DISMISS_CLING_DURATION);
192 }
193 };
194 mLauncher.getWorkspace().post(dismissCb);
195 }
196
197 /** Hides the specified Cling */
198 private void dismissCling(final View cling, final Runnable postAnimationCb,
199 final String flag, int duration) {
200 // To catch cases where siblings of top-level views are made invisible, just check whether
201 // the cling is directly set to GONE before dismissing it.
202 if (cling != null && cling.getVisibility() != View.GONE) {
203 final Runnable cleanUpClingCb = new Runnable() {
204 public void run() {
205 cling.setVisibility(View.GONE);
206 mLauncher.getSharedPrefs().edit()
207 .putBoolean(flag, true)
208 .apply();
209 if (postAnimationCb != null) {
210 postAnimationCb.run();
211 }
212 }
213 };
214 if (duration <= 0) {
215 cleanUpClingCb.run();
216 } else {
217 cling.animate().alpha(0).setDuration(duration).withEndAction(cleanUpClingCb);
Winson Chunga6945242014-01-08 14:04:34 -0800218 }
219 }
Winson Chunga6945242014-01-08 14:04:34 -0800220 }
221
222 /** Returns whether the clings are enabled or should be shown */
Winson Chung205cd772014-01-15 14:31:59 -0800223 private boolean areClingsEnabled() {
Winson Chunga6945242014-01-08 14:04:34 -0800224 if (DISABLE_CLINGS) {
225 return false;
226 }
227
Winson Chunga6945242014-01-08 14:04:34 -0800228 // disable clings when running in a test harness
229 if(ActivityManager.isRunningInTestHarness()) return false;
230
231 // Disable clings for accessibility when explore by touch is enabled
232 final AccessibilityManager a11yManager = (AccessibilityManager) mLauncher.getSystemService(
233 Launcher.ACCESSIBILITY_SERVICE);
234 if (a11yManager.isTouchExplorationEnabled()) {
235 return false;
236 }
237
238 // Restricted secondary users (child mode) will potentially have very few apps
239 // seeded when they start up for the first time. Clings won't work well with that
240 boolean supportsLimitedUsers =
241 android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.JELLY_BEAN_MR2;
242 Account[] accounts = AccountManager.get(mLauncher).getAccounts();
243 if (supportsLimitedUsers && accounts.length == 0) {
244 UserManager um = (UserManager) mLauncher.getSystemService(Context.USER_SERVICE);
245 Bundle restrictions = um.getUserRestrictions();
246 if (restrictions.getBoolean(UserManager.DISALLOW_MODIFY_ACCOUNTS, false)) {
247 return false;
248 }
249 }
Amith Yamasani5ba21b62014-06-25 16:14:02 +0530250 if (Settings.Secure.getInt(mLauncher.getContentResolver(), SKIP_FIRST_USE_HINTS, 0)
251 == 1) {
252 return false;
253 }
Winson Chunga6945242014-01-08 14:04:34 -0800254 return true;
255 }
256
Winson Chunge43a1e72014-01-15 10:33:02 -0800257 public boolean shouldShowFirstRunOrMigrationClings() {
Winson Chunga6945242014-01-08 14:04:34 -0800258 SharedPreferences sharedPrefs = mLauncher.getSharedPrefs();
Winson Chung205cd772014-01-15 14:31:59 -0800259 return areClingsEnabled() &&
Sunny Goyal424418b2014-08-22 16:09:37 -0700260 !sharedPrefs.getBoolean(WORKSPACE_CLING_DISMISSED_KEY, false) &&
Adam Cohen71e03b92014-02-21 14:09:53 -0800261 !sharedPrefs.getBoolean(MIGRATION_CLING_DISMISSED_KEY, false);
Winson Chunge43a1e72014-01-15 10:33:02 -0800262 }
Winson Chunga6945242014-01-08 14:04:34 -0800263
Adam Cohen71e03b92014-02-21 14:09:53 -0800264 public static void synchonouslyMarkFirstRunClingDismissed(Context ctx) {
265 SharedPreferences prefs = ctx.getSharedPreferences(
Winson Chungc6c03672014-03-06 10:12:02 -0800266 LauncherAppState.getSharedPreferencesKey(), Context.MODE_PRIVATE);
Adam Cohen71e03b92014-02-21 14:09:53 -0800267 SharedPreferences.Editor editor = prefs.edit();
Sunny Goyal424418b2014-08-22 16:09:37 -0700268 editor.putBoolean(WORKSPACE_CLING_DISMISSED_KEY, true);
Adam Cohen71e03b92014-02-21 14:09:53 -0800269 editor.commit();
270 }
Adam Cohen71e03b92014-02-21 14:09:53 -0800271}