blob: 00586bd4e711693450862e2a3e16c71e78a0d3fe [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;
Winson Chung69dffdb2014-04-04 16:53:17 -070024import android.content.ComponentName;
Winson Chunga6945242014-01-08 14:04:34 -080025import android.content.Context;
26import android.content.SharedPreferences;
Winson Chung69dffdb2014-04-04 16:53:17 -070027import android.content.pm.ApplicationInfo;
28import android.content.pm.PackageManager;
Winson Chung205cd772014-01-15 14:31:59 -080029import android.graphics.Rect;
Winson Chunga6945242014-01-08 14:04:34 -080030import android.os.Bundle;
31import android.os.UserManager;
Amith Yamasani5ba21b62014-06-25 16:14:02 +053032import android.provider.Settings;
Winson Chunga6945242014-01-08 14:04:34 -080033import android.view.LayoutInflater;
34import android.view.View;
35import android.view.ViewGroup;
36import android.view.accessibility.AccessibilityManager;
37import android.widget.TextView;
38
39class LauncherClings {
40 private static final String FIRST_RUN_CLING_DISMISSED_KEY = "cling_gel.first_run.dismissed";
41 private static final String MIGRATION_CLING_DISMISSED_KEY = "cling_gel.migration.dismissed";
Winson Chung205cd772014-01-15 14:31:59 -080042 private static final String MIGRATION_WORKSPACE_CLING_DISMISSED_KEY =
43 "cling_gel.migration_workspace.dismissed";
Winson Chunga6945242014-01-08 14:04:34 -080044 private static final String WORKSPACE_CLING_DISMISSED_KEY = "cling_gel.workspace.dismissed";
45 private static final String FOLDER_CLING_DISMISSED_KEY = "cling_gel.folder.dismissed";
46
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;
57 private HideFromAccessibilityHelper mHideFromAccessibilityHelper
58 = new HideFromAccessibilityHelper();
59
60 /** Ctor */
61 public LauncherClings(Launcher launcher) {
62 mLauncher = launcher;
63 mInflater = mLauncher.getLayoutInflater();
64 }
65
66 /** Initializes a cling */
67 private Cling initCling(int clingId, int scrimId, boolean animate,
68 boolean dimNavBarVisibilty) {
69 Cling cling = (Cling) mLauncher.findViewById(clingId);
70 View scrim = null;
71 if (scrimId > 0) {
Winson Chung205cd772014-01-15 14:31:59 -080072 scrim = mLauncher.findViewById(scrimId);
Winson Chunga6945242014-01-08 14:04:34 -080073 }
74 if (cling != null) {
75 cling.init(mLauncher, scrim);
76 cling.show(animate, SHOW_CLING_DURATION);
77
78 if (dimNavBarVisibilty) {
79 cling.setSystemUiVisibility(cling.getSystemUiVisibility() |
80 View.SYSTEM_UI_FLAG_LOW_PROFILE);
81 }
82 }
83 return cling;
84 }
85
86 /** Returns whether the clings are enabled or should be shown */
Winson Chung205cd772014-01-15 14:31:59 -080087 private boolean areClingsEnabled() {
Winson Chunga6945242014-01-08 14:04:34 -080088 if (DISABLE_CLINGS) {
89 return false;
90 }
91
Winson Chunga6945242014-01-08 14:04:34 -080092 // disable clings when running in a test harness
93 if(ActivityManager.isRunningInTestHarness()) return false;
94
95 // Disable clings for accessibility when explore by touch is enabled
96 final AccessibilityManager a11yManager = (AccessibilityManager) mLauncher.getSystemService(
97 Launcher.ACCESSIBILITY_SERVICE);
98 if (a11yManager.isTouchExplorationEnabled()) {
99 return false;
100 }
101
102 // Restricted secondary users (child mode) will potentially have very few apps
103 // seeded when they start up for the first time. Clings won't work well with that
104 boolean supportsLimitedUsers =
105 android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.JELLY_BEAN_MR2;
106 Account[] accounts = AccountManager.get(mLauncher).getAccounts();
107 if (supportsLimitedUsers && accounts.length == 0) {
108 UserManager um = (UserManager) mLauncher.getSystemService(Context.USER_SERVICE);
109 Bundle restrictions = um.getUserRestrictions();
110 if (restrictions.getBoolean(UserManager.DISALLOW_MODIFY_ACCOUNTS, false)) {
111 return false;
112 }
113 }
Amith Yamasani5ba21b62014-06-25 16:14:02 +0530114 if (Settings.Secure.getInt(mLauncher.getContentResolver(), SKIP_FIRST_USE_HINTS, 0)
115 == 1) {
116 return false;
117 }
Winson Chunga6945242014-01-08 14:04:34 -0800118 return true;
119 }
120
121 /** Returns whether the folder cling is visible. */
122 public boolean isFolderClingVisible() {
123 Cling cling = (Cling) mLauncher.findViewById(R.id.folder_cling);
124 if (cling != null) {
125 return cling.getVisibility() == View.VISIBLE;
126 }
127 return false;
128 }
129
130 private boolean skipCustomClingIfNoAccounts() {
131 Cling cling = (Cling) mLauncher.findViewById(R.id.workspace_cling);
132 boolean customCling = cling.getDrawIdentifier().equals("workspace_custom");
133 if (customCling) {
134 AccountManager am = AccountManager.get(mLauncher);
135 if (am == null) return false;
136 Account[] accounts = am.getAccountsByType("com.google");
137 return accounts.length == 0;
138 }
139 return false;
140 }
141
142 /** Updates the first run cling custom content hint */
143 private void setCustomContentHintVisibility(Cling cling, String ccHintStr, boolean visible,
144 boolean animate) {
145 final TextView ccHint = (TextView) cling.findViewById(R.id.custom_content_hint);
146 if (ccHint != null) {
147 if (visible && !ccHintStr.isEmpty()) {
148 ccHint.setText(ccHintStr);
149 ccHint.setVisibility(View.VISIBLE);
150 if (animate) {
151 ccHint.setAlpha(0f);
152 ccHint.animate().alpha(1f)
153 .setDuration(SHOW_CLING_DURATION)
154 .start();
155 } else {
156 ccHint.setAlpha(1f);
157 }
158 } else {
159 if (animate) {
160 ccHint.animate().alpha(0f)
161 .setDuration(SHOW_CLING_DURATION)
162 .setListener(new AnimatorListenerAdapter() {
163 @Override
164 public void onAnimationEnd(Animator animation) {
165 ccHint.setVisibility(View.GONE);
166 }
167 })
168 .start();
169 } else {
170 ccHint.setAlpha(0f);
171 ccHint.setVisibility(View.GONE);
172 }
173 }
174 }
175 }
176
177 /** Updates the first run cling custom content hint */
178 public void updateCustomContentHintVisibility() {
179 Cling cling = (Cling) mLauncher.findViewById(R.id.first_run_cling);
180 String ccHintStr = mLauncher.getFirstRunCustomContentHint();
181
182 if (mLauncher.getWorkspace().hasCustomContent()) {
183 // Show the custom content hint if ccHintStr is not empty
184 if (cling != null) {
185 setCustomContentHintVisibility(cling, ccHintStr, true, true);
186 }
187 } else {
188 // Hide the custom content hint
189 if (cling != null) {
190 setCustomContentHintVisibility(cling, ccHintStr, false, true);
191 }
192 }
193 }
194
195 /** Updates the first run cling search bar hint. */
196 public void updateSearchBarHint(String hint) {
197 Cling cling = (Cling) mLauncher.findViewById(R.id.first_run_cling);
198 if (cling != null && cling.getVisibility() == View.VISIBLE && !hint.isEmpty()) {
199 TextView sbHint = (TextView) cling.findViewById(R.id.search_bar_hint);
200 sbHint.setText(hint);
201 sbHint.setVisibility(View.VISIBLE);
202 }
203 }
204
Winson Chunge43a1e72014-01-15 10:33:02 -0800205 public boolean shouldShowFirstRunOrMigrationClings() {
Winson Chunga6945242014-01-08 14:04:34 -0800206 SharedPreferences sharedPrefs = mLauncher.getSharedPrefs();
Winson Chung205cd772014-01-15 14:31:59 -0800207 return areClingsEnabled() &&
Winson Chunge43a1e72014-01-15 10:33:02 -0800208 !sharedPrefs.getBoolean(FIRST_RUN_CLING_DISMISSED_KEY, false) &&
Adam Cohen71e03b92014-02-21 14:09:53 -0800209 !sharedPrefs.getBoolean(MIGRATION_CLING_DISMISSED_KEY, false);
Winson Chunge43a1e72014-01-15 10:33:02 -0800210 }
Winson Chunga6945242014-01-08 14:04:34 -0800211
Winson Chunge43a1e72014-01-15 10:33:02 -0800212 public void removeFirstRunAndMigrationClings() {
213 removeCling(R.id.first_run_cling);
214 removeCling(R.id.migration_cling);
215 }
Winson Chunga6945242014-01-08 14:04:34 -0800216
Winson Chunge43a1e72014-01-15 10:33:02 -0800217 /**
218 * Shows the first run cling.
219 *
220 * This flow is mutually exclusive with showMigrationCling, and only runs if this Launcher
221 * package was preinstalled or there is no db to migrate from.
222 */
223 public void showFirstRunCling() {
224 if (!skipCustomClingIfNoAccounts()) {
Winson Chunga6945242014-01-08 14:04:34 -0800225 Cling cling = (Cling) mLauncher.findViewById(R.id.first_run_cling);
226 if (cling != null) {
227 String sbHintStr = mLauncher.getFirstRunClingSearchBarHint();
228 String ccHintStr = mLauncher.getFirstRunCustomContentHint();
229 if (!sbHintStr.isEmpty()) {
230 TextView sbHint = (TextView) cling.findViewById(R.id.search_bar_hint);
231 sbHint.setText(sbHintStr);
232 sbHint.setVisibility(View.VISIBLE);
233 }
234 setCustomContentHintVisibility(cling, ccHintStr, true, false);
235 }
236 initCling(R.id.first_run_cling, 0, false, true);
237 } else {
Winson Chunge43a1e72014-01-15 10:33:02 -0800238 removeFirstRunAndMigrationClings();
Winson Chunga6945242014-01-08 14:04:34 -0800239 }
240 }
241
Winson Chunge43a1e72014-01-15 10:33:02 -0800242 /**
243 * Shows the migration cling.
244 *
245 * This flow is mutually exclusive with showFirstRunCling, and only runs if this Launcher
246 * package was not preinstalled and there exists a db to migrate from.
247 */
Winson Chunga6945242014-01-08 14:04:34 -0800248 public void showMigrationCling() {
Winson Chunge43a1e72014-01-15 10:33:02 -0800249 mLauncher.hideWorkspaceSearchAndHotseat();
Winson Chunga6945242014-01-08 14:04:34 -0800250
Winson Chunge43a1e72014-01-15 10:33:02 -0800251 Cling c = initCling(R.id.migration_cling, 0, false, true);
252 c.bringScrimToFront();
253 c.bringToFront();
Winson Chunga6945242014-01-08 14:04:34 -0800254 }
255
256 public void showMigrationWorkspaceCling() {
257 // Enable the clings only if they have not been dismissed before
Winson Chung205cd772014-01-15 14:31:59 -0800258 if (areClingsEnabled() && !mLauncher.getSharedPrefs().getBoolean(
Winson Chunga6945242014-01-08 14:04:34 -0800259 MIGRATION_WORKSPACE_CLING_DISMISSED_KEY, false)) {
260 Cling c = initCling(R.id.migration_workspace_cling, 0, false, true);
261 c.updateMigrationWorkspaceBubblePosition();
262 c.bringScrimToFront();
263 c.bringToFront();
264 } else {
265 removeCling(R.id.migration_workspace_cling);
266 }
267 }
268
269 public void showWorkspaceCling() {
270 // Enable the clings only if they have not been dismissed before
Winson Chung205cd772014-01-15 14:31:59 -0800271 if (areClingsEnabled() && !mLauncher.getSharedPrefs().getBoolean(
Winson Chunga6945242014-01-08 14:04:34 -0800272 WORKSPACE_CLING_DISMISSED_KEY, false)) {
273 Cling c = initCling(R.id.workspace_cling, 0, false, true);
Winson Chung205cd772014-01-15 14:31:59 -0800274 c.updateWorkspaceBubblePosition();
Adam Cohen5eed5d82014-05-19 13:12:13 -0700275 if (mLauncher.shouldClingFocusHotseatApp()) {
276 // Set the focused hotseat app
277 c.setFocusedHotseatApp(mLauncher.getFirstRunFocusedHotseatAppDrawableId(),
278 mLauncher.getFirstRunFocusedHotseatAppRank(),
279 mLauncher.getFirstRunFocusedHotseatAppComponentName(),
280 mLauncher.getFirstRunFocusedHotseatAppBubbleTitle(),
281 mLauncher.getFirstRunFocusedHotseatAppBubbleDescription());
Winson Chung69dffdb2014-04-04 16:53:17 -0700282 }
Winson Chunga6945242014-01-08 14:04:34 -0800283 } else {
284 removeCling(R.id.workspace_cling);
285 }
286 }
Adam Cohen71e03b92014-02-21 14:09:53 -0800287
Winson Chunga6945242014-01-08 14:04:34 -0800288 public Cling showFoldersCling() {
289 SharedPreferences sharedPrefs = mLauncher.getSharedPrefs();
290 // Enable the clings only if they have not been dismissed before
Winson Chung205cd772014-01-15 14:31:59 -0800291 if (areClingsEnabled() &&
Winson Chunga6945242014-01-08 14:04:34 -0800292 !sharedPrefs.getBoolean(FOLDER_CLING_DISMISSED_KEY, false) &&
293 !sharedPrefs.getBoolean(Launcher.USER_HAS_MIGRATED, false)) {
294 Cling cling = initCling(R.id.folder_cling, R.id.cling_scrim,
295 true, true);
Winson Chung205cd772014-01-15 14:31:59 -0800296 Folder openFolder = mLauncher.getWorkspace().getOpenFolder();
297 if (openFolder != null) {
298 Rect openFolderRect = new Rect();
299 openFolder.getHitRect(openFolderRect);
300 cling.setOpenFolderRect(openFolderRect);
301 openFolder.bringToFront();
302 }
Winson Chunga6945242014-01-08 14:04:34 -0800303 return cling;
304 } else {
305 removeCling(R.id.folder_cling);
306 return null;
307 }
308 }
309
Adam Cohen71e03b92014-02-21 14:09:53 -0800310 public static void synchonouslyMarkFirstRunClingDismissed(Context ctx) {
311 SharedPreferences prefs = ctx.getSharedPreferences(
Winson Chungc6c03672014-03-06 10:12:02 -0800312 LauncherAppState.getSharedPreferencesKey(), Context.MODE_PRIVATE);
Adam Cohen71e03b92014-02-21 14:09:53 -0800313 SharedPreferences.Editor editor = prefs.edit();
314 editor.putBoolean(LauncherClings.FIRST_RUN_CLING_DISMISSED_KEY, true);
315 editor.commit();
316 }
317
Winson Chunga6945242014-01-08 14:04:34 -0800318 /** Removes the cling outright from the DragLayer */
319 private void removeCling(int id) {
320 final View cling = mLauncher.findViewById(id);
321 if (cling != null) {
322 final ViewGroup parent = (ViewGroup) cling.getParent();
323 parent.post(new Runnable() {
324 @Override
325 public void run() {
326 parent.removeView(cling);
327 }
328 });
329 mHideFromAccessibilityHelper.restoreImportantForAccessibility(mLauncher.getDragLayer());
330 }
331 }
332
333 /** Hides the specified Cling */
334 private void dismissCling(final Cling cling, final Runnable postAnimationCb,
335 final String flag, int duration, boolean restoreNavBarVisibilty) {
336 // To catch cases where siblings of top-level views are made invisible, just check whether
337 // the cling is directly set to GONE before dismissing it.
338 if (cling != null && cling.getVisibility() != View.GONE) {
339 final Runnable cleanUpClingCb = new Runnable() {
340 public void run() {
341 cling.cleanup();
342 SharedPreferences.Editor editor = mLauncher.getSharedPrefs().edit();
343 editor.putBoolean(flag, true);
344 editor.apply();
345 if (postAnimationCb != null) {
346 postAnimationCb.run();
347 }
348 }
349 };
350 if (duration <= 0) {
351 cleanUpClingCb.run();
352 } else {
353 cling.hide(duration, cleanUpClingCb);
354 }
355 mHideFromAccessibilityHelper.restoreImportantForAccessibility(mLauncher.getDragLayer());
356
357 if (restoreNavBarVisibilty) {
358 cling.setSystemUiVisibility(cling.getSystemUiVisibility() &
359 ~View.SYSTEM_UI_FLAG_LOW_PROFILE);
360 }
361 }
362 }
363
364 public void dismissFirstRunCling(View v) {
365 Cling cling = (Cling) mLauncher.findViewById(R.id.first_run_cling);
366 Runnable cb = new Runnable() {
367 public void run() {
368 // Show the workspace cling next
369 showWorkspaceCling();
370 }
371 };
372 dismissCling(cling, cb, FIRST_RUN_CLING_DISMISSED_KEY,
373 DISMISS_CLING_DURATION, false);
374
375 // Fade out the search bar for the workspace cling coming up
376 mLauncher.getSearchBar().hideSearchBar(true);
377 }
378
379 private void dismissMigrationCling() {
380 mLauncher.showWorkspaceSearchAndHotseat();
381 Runnable dismissCb = new Runnable() {
382 public void run() {
383 Cling cling = (Cling) mLauncher.findViewById(R.id.migration_cling);
384 Runnable cb = new Runnable() {
385 public void run() {
386 // Show the migration workspace cling next
387 showMigrationWorkspaceCling();
388 }
389 };
Winson Chung285b6e12014-01-14 10:30:27 -0800390 dismissCling(cling, cb, MIGRATION_CLING_DISMISSED_KEY,
Winson Chunga6945242014-01-08 14:04:34 -0800391 DISMISS_CLING_DURATION, true);
392 }
393 };
394 mLauncher.getWorkspace().post(dismissCb);
395 }
396
Winson Chung285b6e12014-01-14 10:30:27 -0800397 private void dismissAnyWorkspaceCling(Cling cling, String key, View v) {
Winson Chunga6945242014-01-08 14:04:34 -0800398 Runnable cb = null;
399 if (v == null) {
400 cb = new Runnable() {
401 public void run() {
402 mLauncher.getWorkspace().enterOverviewMode();
403 }
404 };
405 }
Winson Chung285b6e12014-01-14 10:30:27 -0800406 dismissCling(cling, cb, key, DISMISS_CLING_DURATION, true);
Winson Chunga6945242014-01-08 14:04:34 -0800407
408 // Fade in the search bar
409 mLauncher.getSearchBar().showSearchBar(true);
410 }
411
Adam Cohen6268f2d2014-05-16 16:42:35 -0700412 public void markFolderClingDismissedIfNecessary() {
413 SharedPreferences prefs = mLauncher.getSharedPrefs();
414 if (!prefs.getBoolean(FOLDER_CLING_DISMISSED_KEY, false)) {
415 SharedPreferences.Editor editor = prefs.edit();
416 editor.putBoolean(FOLDER_CLING_DISMISSED_KEY, true);
417 editor.apply();
418 }
419 }
420
Winson Chunga6945242014-01-08 14:04:34 -0800421 public void dismissMigrationClingCopyApps(View v) {
422 // Copy the shortcuts from the old database
423 LauncherModel model = mLauncher.getModel();
Winson Chung5317c2b2014-01-10 16:46:15 -0800424 model.resetLoadedState(false, true);
425 model.startLoader(false, PagedView.INVALID_RESTORE_PAGE,
426 LauncherModel.LOADER_FLAG_CLEAR_WORKSPACE
427 | LauncherModel.LOADER_FLAG_MIGRATE_SHORTCUTS);
Winson Chunga6945242014-01-08 14:04:34 -0800428
429 // Set the flag to skip the folder cling
430 String spKey = LauncherAppState.getSharedPreferencesKey();
431 SharedPreferences sp = mLauncher.getSharedPreferences(spKey, Context.MODE_PRIVATE);
432 SharedPreferences.Editor editor = sp.edit();
433 editor.putBoolean(Launcher.USER_HAS_MIGRATED, true);
434 editor.apply();
435
436 // Disable the migration cling
437 dismissMigrationCling();
438 }
439
440 public void dismissMigrationClingUseDefault(View v) {
Adam Cohen8457d492014-05-16 17:05:49 -0700441 // Don't need to do anything special here. We've already loaded the default workspace,
442 // (which is the default loader behavior triggered from Launcher#onCreate.).
Winson Chunga6945242014-01-08 14:04:34 -0800443
444 // Disable the migration cling
445 dismissMigrationCling();
446 }
447
448 public void dismissMigrationWorkspaceCling(View v) {
449 Cling cling = (Cling) mLauncher.findViewById(R.id.migration_workspace_cling);
Winson Chung285b6e12014-01-14 10:30:27 -0800450 dismissAnyWorkspaceCling(cling, MIGRATION_WORKSPACE_CLING_DISMISSED_KEY, v);
Winson Chunga6945242014-01-08 14:04:34 -0800451 }
452
453 public void dismissWorkspaceCling(View v) {
454 Cling cling = (Cling) mLauncher.findViewById(R.id.workspace_cling);
Winson Chung285b6e12014-01-14 10:30:27 -0800455 dismissAnyWorkspaceCling(cling, WORKSPACE_CLING_DISMISSED_KEY, v);
Winson Chunga6945242014-01-08 14:04:34 -0800456 }
457
458 public void dismissFolderCling(View v) {
459 Cling cling = (Cling) mLauncher.findViewById(R.id.folder_cling);
460 dismissCling(cling, null, FOLDER_CLING_DISMISSED_KEY,
461 DISMISS_CLING_DURATION, true);
462 }
Adam Cohen71e03b92014-02-21 14:09:53 -0800463}