blob: 952edfd060ccc5a6c453ad4e3ae409c8b1b91bd9 [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;
21import android.animation.Animator;
22import android.animation.AnimatorListenerAdapter;
23import android.app.ActivityManager;
24import android.content.Context;
25import android.content.SharedPreferences;
Winson Chung205cd772014-01-15 14:31:59 -080026import android.graphics.Rect;
Winson Chunga6945242014-01-08 14:04:34 -080027import android.os.Bundle;
28import android.os.UserManager;
29import android.view.LayoutInflater;
30import android.view.View;
31import android.view.ViewGroup;
32import android.view.accessibility.AccessibilityManager;
33import android.widget.TextView;
34
35class LauncherClings {
36 private static final String FIRST_RUN_CLING_DISMISSED_KEY = "cling_gel.first_run.dismissed";
37 private static final String MIGRATION_CLING_DISMISSED_KEY = "cling_gel.migration.dismissed";
Winson Chung205cd772014-01-15 14:31:59 -080038 private static final String MIGRATION_WORKSPACE_CLING_DISMISSED_KEY =
39 "cling_gel.migration_workspace.dismissed";
Winson Chunga6945242014-01-08 14:04:34 -080040 private static final String WORKSPACE_CLING_DISMISSED_KEY = "cling_gel.workspace.dismissed";
41 private static final String FOLDER_CLING_DISMISSED_KEY = "cling_gel.folder.dismissed";
42
43 private static final boolean DISABLE_CLINGS = false;
Winson Chunga6945242014-01-08 14:04:34 -080044
45 private static final int SHOW_CLING_DURATION = 250;
46 private static final int DISMISS_CLING_DURATION = 200;
47
48 private Launcher mLauncher;
49 private LayoutInflater mInflater;
50 private HideFromAccessibilityHelper mHideFromAccessibilityHelper
51 = new HideFromAccessibilityHelper();
52
53 /** Ctor */
54 public LauncherClings(Launcher launcher) {
55 mLauncher = launcher;
56 mInflater = mLauncher.getLayoutInflater();
57 }
58
59 /** Initializes a cling */
60 private Cling initCling(int clingId, int scrimId, boolean animate,
61 boolean dimNavBarVisibilty) {
62 Cling cling = (Cling) mLauncher.findViewById(clingId);
63 View scrim = null;
64 if (scrimId > 0) {
Winson Chung205cd772014-01-15 14:31:59 -080065 scrim = mLauncher.findViewById(scrimId);
Winson Chunga6945242014-01-08 14:04:34 -080066 }
67 if (cling != null) {
68 cling.init(mLauncher, scrim);
69 cling.show(animate, SHOW_CLING_DURATION);
70
71 if (dimNavBarVisibilty) {
72 cling.setSystemUiVisibility(cling.getSystemUiVisibility() |
73 View.SYSTEM_UI_FLAG_LOW_PROFILE);
74 }
75 }
76 return cling;
77 }
78
79 /** Returns whether the clings are enabled or should be shown */
Winson Chung205cd772014-01-15 14:31:59 -080080 private boolean areClingsEnabled() {
Winson Chunga6945242014-01-08 14:04:34 -080081 if (DISABLE_CLINGS) {
82 return false;
83 }
84
Winson Chunga6945242014-01-08 14:04:34 -080085 // disable clings when running in a test harness
86 if(ActivityManager.isRunningInTestHarness()) return false;
87
88 // Disable clings for accessibility when explore by touch is enabled
89 final AccessibilityManager a11yManager = (AccessibilityManager) mLauncher.getSystemService(
90 Launcher.ACCESSIBILITY_SERVICE);
91 if (a11yManager.isTouchExplorationEnabled()) {
92 return false;
93 }
94
95 // Restricted secondary users (child mode) will potentially have very few apps
96 // seeded when they start up for the first time. Clings won't work well with that
97 boolean supportsLimitedUsers =
98 android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.JELLY_BEAN_MR2;
99 Account[] accounts = AccountManager.get(mLauncher).getAccounts();
100 if (supportsLimitedUsers && accounts.length == 0) {
101 UserManager um = (UserManager) mLauncher.getSystemService(Context.USER_SERVICE);
102 Bundle restrictions = um.getUserRestrictions();
103 if (restrictions.getBoolean(UserManager.DISALLOW_MODIFY_ACCOUNTS, false)) {
104 return false;
105 }
106 }
107 return true;
108 }
109
110 /** Returns whether the folder cling is visible. */
111 public boolean isFolderClingVisible() {
112 Cling cling = (Cling) mLauncher.findViewById(R.id.folder_cling);
113 if (cling != null) {
114 return cling.getVisibility() == View.VISIBLE;
115 }
116 return false;
117 }
118
119 private boolean skipCustomClingIfNoAccounts() {
120 Cling cling = (Cling) mLauncher.findViewById(R.id.workspace_cling);
121 boolean customCling = cling.getDrawIdentifier().equals("workspace_custom");
122 if (customCling) {
123 AccountManager am = AccountManager.get(mLauncher);
124 if (am == null) return false;
125 Account[] accounts = am.getAccountsByType("com.google");
126 return accounts.length == 0;
127 }
128 return false;
129 }
130
131 /** Updates the first run cling custom content hint */
132 private void setCustomContentHintVisibility(Cling cling, String ccHintStr, boolean visible,
133 boolean animate) {
134 final TextView ccHint = (TextView) cling.findViewById(R.id.custom_content_hint);
135 if (ccHint != null) {
136 if (visible && !ccHintStr.isEmpty()) {
137 ccHint.setText(ccHintStr);
138 ccHint.setVisibility(View.VISIBLE);
139 if (animate) {
140 ccHint.setAlpha(0f);
141 ccHint.animate().alpha(1f)
142 .setDuration(SHOW_CLING_DURATION)
143 .start();
144 } else {
145 ccHint.setAlpha(1f);
146 }
147 } else {
148 if (animate) {
149 ccHint.animate().alpha(0f)
150 .setDuration(SHOW_CLING_DURATION)
151 .setListener(new AnimatorListenerAdapter() {
152 @Override
153 public void onAnimationEnd(Animator animation) {
154 ccHint.setVisibility(View.GONE);
155 }
156 })
157 .start();
158 } else {
159 ccHint.setAlpha(0f);
160 ccHint.setVisibility(View.GONE);
161 }
162 }
163 }
164 }
165
166 /** Updates the first run cling custom content hint */
167 public void updateCustomContentHintVisibility() {
168 Cling cling = (Cling) mLauncher.findViewById(R.id.first_run_cling);
169 String ccHintStr = mLauncher.getFirstRunCustomContentHint();
170
171 if (mLauncher.getWorkspace().hasCustomContent()) {
172 // Show the custom content hint if ccHintStr is not empty
173 if (cling != null) {
174 setCustomContentHintVisibility(cling, ccHintStr, true, true);
175 }
176 } else {
177 // Hide the custom content hint
178 if (cling != null) {
179 setCustomContentHintVisibility(cling, ccHintStr, false, true);
180 }
181 }
182 }
183
184 /** Updates the first run cling search bar hint. */
185 public void updateSearchBarHint(String hint) {
186 Cling cling = (Cling) mLauncher.findViewById(R.id.first_run_cling);
187 if (cling != null && cling.getVisibility() == View.VISIBLE && !hint.isEmpty()) {
188 TextView sbHint = (TextView) cling.findViewById(R.id.search_bar_hint);
189 sbHint.setText(hint);
190 sbHint.setVisibility(View.VISIBLE);
191 }
192 }
193
Winson Chunge43a1e72014-01-15 10:33:02 -0800194 public boolean shouldShowFirstRunOrMigrationClings() {
Winson Chunga6945242014-01-08 14:04:34 -0800195 SharedPreferences sharedPrefs = mLauncher.getSharedPrefs();
Winson Chung205cd772014-01-15 14:31:59 -0800196 return areClingsEnabled() &&
Winson Chunge43a1e72014-01-15 10:33:02 -0800197 !sharedPrefs.getBoolean(FIRST_RUN_CLING_DISMISSED_KEY, false) &&
Adam Cohen71e03b92014-02-21 14:09:53 -0800198 !sharedPrefs.getBoolean(MIGRATION_CLING_DISMISSED_KEY, false);
Winson Chunge43a1e72014-01-15 10:33:02 -0800199 }
Winson Chunga6945242014-01-08 14:04:34 -0800200
Winson Chunge43a1e72014-01-15 10:33:02 -0800201 public void removeFirstRunAndMigrationClings() {
202 removeCling(R.id.first_run_cling);
203 removeCling(R.id.migration_cling);
204 }
Winson Chunga6945242014-01-08 14:04:34 -0800205
Winson Chunge43a1e72014-01-15 10:33:02 -0800206 /**
207 * Shows the first run cling.
208 *
209 * This flow is mutually exclusive with showMigrationCling, and only runs if this Launcher
210 * package was preinstalled or there is no db to migrate from.
211 */
212 public void showFirstRunCling() {
213 if (!skipCustomClingIfNoAccounts()) {
Winson Chunga6945242014-01-08 14:04:34 -0800214 Cling cling = (Cling) mLauncher.findViewById(R.id.first_run_cling);
215 if (cling != null) {
216 String sbHintStr = mLauncher.getFirstRunClingSearchBarHint();
217 String ccHintStr = mLauncher.getFirstRunCustomContentHint();
218 if (!sbHintStr.isEmpty()) {
219 TextView sbHint = (TextView) cling.findViewById(R.id.search_bar_hint);
220 sbHint.setText(sbHintStr);
221 sbHint.setVisibility(View.VISIBLE);
222 }
223 setCustomContentHintVisibility(cling, ccHintStr, true, false);
224 }
225 initCling(R.id.first_run_cling, 0, false, true);
226 } else {
Winson Chunge43a1e72014-01-15 10:33:02 -0800227 removeFirstRunAndMigrationClings();
Winson Chunga6945242014-01-08 14:04:34 -0800228 }
229 }
230
Winson Chunge43a1e72014-01-15 10:33:02 -0800231 /**
232 * Shows the migration cling.
233 *
234 * This flow is mutually exclusive with showFirstRunCling, and only runs if this Launcher
235 * package was not preinstalled and there exists a db to migrate from.
236 */
Winson Chunga6945242014-01-08 14:04:34 -0800237 public void showMigrationCling() {
Winson Chunge43a1e72014-01-15 10:33:02 -0800238 mLauncher.hideWorkspaceSearchAndHotseat();
Winson Chunga6945242014-01-08 14:04:34 -0800239
Winson Chunge43a1e72014-01-15 10:33:02 -0800240 Cling c = initCling(R.id.migration_cling, 0, false, true);
241 c.bringScrimToFront();
242 c.bringToFront();
Winson Chunga6945242014-01-08 14:04:34 -0800243 }
244
245 public void showMigrationWorkspaceCling() {
246 // Enable the clings only if they have not been dismissed before
Winson Chung205cd772014-01-15 14:31:59 -0800247 if (areClingsEnabled() && !mLauncher.getSharedPrefs().getBoolean(
Winson Chunga6945242014-01-08 14:04:34 -0800248 MIGRATION_WORKSPACE_CLING_DISMISSED_KEY, false)) {
249 Cling c = initCling(R.id.migration_workspace_cling, 0, false, true);
250 c.updateMigrationWorkspaceBubblePosition();
251 c.bringScrimToFront();
252 c.bringToFront();
253 } else {
254 removeCling(R.id.migration_workspace_cling);
255 }
256 }
257
258 public void showWorkspaceCling() {
259 // Enable the clings only if they have not been dismissed before
Winson Chung205cd772014-01-15 14:31:59 -0800260 if (areClingsEnabled() && !mLauncher.getSharedPrefs().getBoolean(
Winson Chunga6945242014-01-08 14:04:34 -0800261 WORKSPACE_CLING_DISMISSED_KEY, false)) {
262 Cling c = initCling(R.id.workspace_cling, 0, false, true);
Winson Chung205cd772014-01-15 14:31:59 -0800263 c.updateWorkspaceBubblePosition();
Winson Chunga6945242014-01-08 14:04:34 -0800264
265 // Set the focused hotseat app if there is one
266 c.setFocusedHotseatApp(mLauncher.getFirstRunFocusedHotseatAppDrawableId(),
267 mLauncher.getFirstRunFocusedHotseatAppRank(),
268 mLauncher.getFirstRunFocusedHotseatAppComponentName(),
269 mLauncher.getFirstRunFocusedHotseatAppBubbleTitle(),
270 mLauncher.getFirstRunFocusedHotseatAppBubbleDescription());
271 } else {
272 removeCling(R.id.workspace_cling);
273 }
274 }
Adam Cohen71e03b92014-02-21 14:09:53 -0800275
Winson Chunga6945242014-01-08 14:04:34 -0800276 public Cling showFoldersCling() {
277 SharedPreferences sharedPrefs = mLauncher.getSharedPrefs();
278 // Enable the clings only if they have not been dismissed before
Winson Chung205cd772014-01-15 14:31:59 -0800279 if (areClingsEnabled() &&
Winson Chunga6945242014-01-08 14:04:34 -0800280 !sharedPrefs.getBoolean(FOLDER_CLING_DISMISSED_KEY, false) &&
281 !sharedPrefs.getBoolean(Launcher.USER_HAS_MIGRATED, false)) {
282 Cling cling = initCling(R.id.folder_cling, R.id.cling_scrim,
283 true, true);
Winson Chung205cd772014-01-15 14:31:59 -0800284 Folder openFolder = mLauncher.getWorkspace().getOpenFolder();
285 if (openFolder != null) {
286 Rect openFolderRect = new Rect();
287 openFolder.getHitRect(openFolderRect);
288 cling.setOpenFolderRect(openFolderRect);
289 openFolder.bringToFront();
290 }
Winson Chunga6945242014-01-08 14:04:34 -0800291 return cling;
292 } else {
293 removeCling(R.id.folder_cling);
294 return null;
295 }
296 }
297
Adam Cohen71e03b92014-02-21 14:09:53 -0800298 public static void synchonouslyMarkFirstRunClingDismissed(Context ctx) {
299 SharedPreferences prefs = ctx.getSharedPreferences(
Winson Chungc6c03672014-03-06 10:12:02 -0800300 LauncherAppState.getSharedPreferencesKey(), Context.MODE_PRIVATE);
Adam Cohen71e03b92014-02-21 14:09:53 -0800301 SharedPreferences.Editor editor = prefs.edit();
302 editor.putBoolean(LauncherClings.FIRST_RUN_CLING_DISMISSED_KEY, true);
303 editor.commit();
304 }
305
Winson Chungc6c03672014-03-06 10:12:02 -0800306 public void markFolderClingDismissed() {
307 SharedPreferences.Editor editor = mLauncher.getSharedPrefs().edit();
308 editor.putBoolean(LauncherClings.FOLDER_CLING_DISMISSED_KEY, true);
309 editor.apply();
310 }
311
Winson Chunga6945242014-01-08 14:04:34 -0800312 /** Removes the cling outright from the DragLayer */
313 private void removeCling(int id) {
314 final View cling = mLauncher.findViewById(id);
315 if (cling != null) {
316 final ViewGroup parent = (ViewGroup) cling.getParent();
317 parent.post(new Runnable() {
318 @Override
319 public void run() {
320 parent.removeView(cling);
321 }
322 });
323 mHideFromAccessibilityHelper.restoreImportantForAccessibility(mLauncher.getDragLayer());
324 }
325 }
326
327 /** Hides the specified Cling */
328 private void dismissCling(final Cling cling, final Runnable postAnimationCb,
329 final String flag, int duration, boolean restoreNavBarVisibilty) {
330 // To catch cases where siblings of top-level views are made invisible, just check whether
331 // the cling is directly set to GONE before dismissing it.
332 if (cling != null && cling.getVisibility() != View.GONE) {
333 final Runnable cleanUpClingCb = new Runnable() {
334 public void run() {
335 cling.cleanup();
336 SharedPreferences.Editor editor = mLauncher.getSharedPrefs().edit();
337 editor.putBoolean(flag, true);
338 editor.apply();
339 if (postAnimationCb != null) {
340 postAnimationCb.run();
341 }
342 }
343 };
344 if (duration <= 0) {
345 cleanUpClingCb.run();
346 } else {
347 cling.hide(duration, cleanUpClingCb);
348 }
349 mHideFromAccessibilityHelper.restoreImportantForAccessibility(mLauncher.getDragLayer());
350
351 if (restoreNavBarVisibilty) {
352 cling.setSystemUiVisibility(cling.getSystemUiVisibility() &
353 ~View.SYSTEM_UI_FLAG_LOW_PROFILE);
354 }
355 }
356 }
357
358 public void dismissFirstRunCling(View v) {
359 Cling cling = (Cling) mLauncher.findViewById(R.id.first_run_cling);
360 Runnable cb = new Runnable() {
361 public void run() {
362 // Show the workspace cling next
363 showWorkspaceCling();
364 }
365 };
366 dismissCling(cling, cb, FIRST_RUN_CLING_DISMISSED_KEY,
367 DISMISS_CLING_DURATION, false);
368
369 // Fade out the search bar for the workspace cling coming up
370 mLauncher.getSearchBar().hideSearchBar(true);
371 }
372
373 private void dismissMigrationCling() {
374 mLauncher.showWorkspaceSearchAndHotseat();
375 Runnable dismissCb = new Runnable() {
376 public void run() {
377 Cling cling = (Cling) mLauncher.findViewById(R.id.migration_cling);
378 Runnable cb = new Runnable() {
379 public void run() {
380 // Show the migration workspace cling next
381 showMigrationWorkspaceCling();
382 }
383 };
Winson Chung285b6e12014-01-14 10:30:27 -0800384 dismissCling(cling, cb, MIGRATION_CLING_DISMISSED_KEY,
Winson Chunga6945242014-01-08 14:04:34 -0800385 DISMISS_CLING_DURATION, true);
386 }
387 };
388 mLauncher.getWorkspace().post(dismissCb);
389 }
390
Winson Chung285b6e12014-01-14 10:30:27 -0800391 private void dismissAnyWorkspaceCling(Cling cling, String key, View v) {
Winson Chunga6945242014-01-08 14:04:34 -0800392 Runnable cb = null;
393 if (v == null) {
394 cb = new Runnable() {
395 public void run() {
396 mLauncher.getWorkspace().enterOverviewMode();
397 }
398 };
399 }
Winson Chung285b6e12014-01-14 10:30:27 -0800400 dismissCling(cling, cb, key, DISMISS_CLING_DURATION, true);
Winson Chunga6945242014-01-08 14:04:34 -0800401
402 // Fade in the search bar
403 mLauncher.getSearchBar().showSearchBar(true);
404 }
405
406 public void dismissMigrationClingCopyApps(View v) {
407 // Copy the shortcuts from the old database
408 LauncherModel model = mLauncher.getModel();
Winson Chung5317c2b2014-01-10 16:46:15 -0800409 model.resetLoadedState(false, true);
410 model.startLoader(false, PagedView.INVALID_RESTORE_PAGE,
411 LauncherModel.LOADER_FLAG_CLEAR_WORKSPACE
412 | LauncherModel.LOADER_FLAG_MIGRATE_SHORTCUTS);
Winson Chunga6945242014-01-08 14:04:34 -0800413
414 // Set the flag to skip the folder cling
415 String spKey = LauncherAppState.getSharedPreferencesKey();
416 SharedPreferences sp = mLauncher.getSharedPreferences(spKey, Context.MODE_PRIVATE);
417 SharedPreferences.Editor editor = sp.edit();
418 editor.putBoolean(Launcher.USER_HAS_MIGRATED, true);
419 editor.apply();
420
421 // Disable the migration cling
422 dismissMigrationCling();
423 }
424
425 public void dismissMigrationClingUseDefault(View v) {
426 // Clear the workspace
427 LauncherModel model = mLauncher.getModel();
Winson Chung5317c2b2014-01-10 16:46:15 -0800428 model.resetLoadedState(false, true);
429 model.startLoader(false, PagedView.INVALID_RESTORE_PAGE,
430 LauncherModel.LOADER_FLAG_CLEAR_WORKSPACE);
Winson Chunga6945242014-01-08 14:04:34 -0800431
432 // Disable the migration cling
433 dismissMigrationCling();
434 }
435
436 public void dismissMigrationWorkspaceCling(View v) {
437 Cling cling = (Cling) mLauncher.findViewById(R.id.migration_workspace_cling);
Winson Chung285b6e12014-01-14 10:30:27 -0800438 dismissAnyWorkspaceCling(cling, MIGRATION_WORKSPACE_CLING_DISMISSED_KEY, v);
Winson Chunga6945242014-01-08 14:04:34 -0800439 }
440
441 public void dismissWorkspaceCling(View v) {
442 Cling cling = (Cling) mLauncher.findViewById(R.id.workspace_cling);
Winson Chung285b6e12014-01-14 10:30:27 -0800443 dismissAnyWorkspaceCling(cling, WORKSPACE_CLING_DISMISSED_KEY, v);
Winson Chunga6945242014-01-08 14:04:34 -0800444 }
445
446 public void dismissFolderCling(View v) {
447 Cling cling = (Cling) mLauncher.findViewById(R.id.folder_cling);
448 dismissCling(cling, null, FOLDER_CLING_DISMISSED_KEY,
449 DISMISS_CLING_DURATION, true);
450 }
Adam Cohen71e03b92014-02-21 14:09:53 -0800451}