blob: 97138eeaa21fc86ef1557cdfd5fe68d04fc96add [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;
32import android.view.LayoutInflater;
33import android.view.View;
34import android.view.ViewGroup;
35import android.view.accessibility.AccessibilityManager;
36import android.widget.TextView;
37
38class LauncherClings {
39 private static final String FIRST_RUN_CLING_DISMISSED_KEY = "cling_gel.first_run.dismissed";
40 private static final String MIGRATION_CLING_DISMISSED_KEY = "cling_gel.migration.dismissed";
Winson Chung205cd772014-01-15 14:31:59 -080041 private static final String MIGRATION_WORKSPACE_CLING_DISMISSED_KEY =
42 "cling_gel.migration_workspace.dismissed";
Winson Chunga6945242014-01-08 14:04:34 -080043 private static final String WORKSPACE_CLING_DISMISSED_KEY = "cling_gel.workspace.dismissed";
44 private static final String FOLDER_CLING_DISMISSED_KEY = "cling_gel.folder.dismissed";
45
46 private static final boolean DISABLE_CLINGS = false;
Winson Chunga6945242014-01-08 14:04:34 -080047
48 private static final int SHOW_CLING_DURATION = 250;
49 private static final int DISMISS_CLING_DURATION = 200;
50
51 private Launcher mLauncher;
52 private LayoutInflater mInflater;
53 private HideFromAccessibilityHelper mHideFromAccessibilityHelper
54 = new HideFromAccessibilityHelper();
55
56 /** Ctor */
57 public LauncherClings(Launcher launcher) {
58 mLauncher = launcher;
59 mInflater = mLauncher.getLayoutInflater();
60 }
61
62 /** Initializes a cling */
63 private Cling initCling(int clingId, int scrimId, boolean animate,
64 boolean dimNavBarVisibilty) {
65 Cling cling = (Cling) mLauncher.findViewById(clingId);
66 View scrim = null;
67 if (scrimId > 0) {
Winson Chung205cd772014-01-15 14:31:59 -080068 scrim = mLauncher.findViewById(scrimId);
Winson Chunga6945242014-01-08 14:04:34 -080069 }
70 if (cling != null) {
71 cling.init(mLauncher, scrim);
72 cling.show(animate, SHOW_CLING_DURATION);
73
74 if (dimNavBarVisibilty) {
75 cling.setSystemUiVisibility(cling.getSystemUiVisibility() |
76 View.SYSTEM_UI_FLAG_LOW_PROFILE);
77 }
78 }
79 return cling;
80 }
81
82 /** Returns whether the clings are enabled or should be shown */
Winson Chung205cd772014-01-15 14:31:59 -080083 private boolean areClingsEnabled() {
Winson Chunga6945242014-01-08 14:04:34 -080084 if (DISABLE_CLINGS) {
85 return false;
86 }
87
Winson Chunga6945242014-01-08 14:04:34 -080088 // disable clings when running in a test harness
89 if(ActivityManager.isRunningInTestHarness()) return false;
90
91 // Disable clings for accessibility when explore by touch is enabled
92 final AccessibilityManager a11yManager = (AccessibilityManager) mLauncher.getSystemService(
93 Launcher.ACCESSIBILITY_SERVICE);
94 if (a11yManager.isTouchExplorationEnabled()) {
95 return false;
96 }
97
98 // Restricted secondary users (child mode) will potentially have very few apps
99 // seeded when they start up for the first time. Clings won't work well with that
100 boolean supportsLimitedUsers =
101 android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.JELLY_BEAN_MR2;
102 Account[] accounts = AccountManager.get(mLauncher).getAccounts();
103 if (supportsLimitedUsers && accounts.length == 0) {
104 UserManager um = (UserManager) mLauncher.getSystemService(Context.USER_SERVICE);
105 Bundle restrictions = um.getUserRestrictions();
106 if (restrictions.getBoolean(UserManager.DISALLOW_MODIFY_ACCOUNTS, false)) {
107 return false;
108 }
109 }
110 return true;
111 }
112
113 /** Returns whether the folder cling is visible. */
114 public boolean isFolderClingVisible() {
115 Cling cling = (Cling) mLauncher.findViewById(R.id.folder_cling);
116 if (cling != null) {
117 return cling.getVisibility() == View.VISIBLE;
118 }
119 return false;
120 }
121
122 private boolean skipCustomClingIfNoAccounts() {
123 Cling cling = (Cling) mLauncher.findViewById(R.id.workspace_cling);
124 boolean customCling = cling.getDrawIdentifier().equals("workspace_custom");
125 if (customCling) {
126 AccountManager am = AccountManager.get(mLauncher);
127 if (am == null) return false;
128 Account[] accounts = am.getAccountsByType("com.google");
129 return accounts.length == 0;
130 }
131 return false;
132 }
133
134 /** Updates the first run cling custom content hint */
135 private void setCustomContentHintVisibility(Cling cling, String ccHintStr, boolean visible,
136 boolean animate) {
137 final TextView ccHint = (TextView) cling.findViewById(R.id.custom_content_hint);
138 if (ccHint != null) {
139 if (visible && !ccHintStr.isEmpty()) {
140 ccHint.setText(ccHintStr);
141 ccHint.setVisibility(View.VISIBLE);
142 if (animate) {
143 ccHint.setAlpha(0f);
144 ccHint.animate().alpha(1f)
145 .setDuration(SHOW_CLING_DURATION)
146 .start();
147 } else {
148 ccHint.setAlpha(1f);
149 }
150 } else {
151 if (animate) {
152 ccHint.animate().alpha(0f)
153 .setDuration(SHOW_CLING_DURATION)
154 .setListener(new AnimatorListenerAdapter() {
155 @Override
156 public void onAnimationEnd(Animator animation) {
157 ccHint.setVisibility(View.GONE);
158 }
159 })
160 .start();
161 } else {
162 ccHint.setAlpha(0f);
163 ccHint.setVisibility(View.GONE);
164 }
165 }
166 }
167 }
168
169 /** Updates the first run cling custom content hint */
170 public void updateCustomContentHintVisibility() {
171 Cling cling = (Cling) mLauncher.findViewById(R.id.first_run_cling);
172 String ccHintStr = mLauncher.getFirstRunCustomContentHint();
173
174 if (mLauncher.getWorkspace().hasCustomContent()) {
175 // Show the custom content hint if ccHintStr is not empty
176 if (cling != null) {
177 setCustomContentHintVisibility(cling, ccHintStr, true, true);
178 }
179 } else {
180 // Hide the custom content hint
181 if (cling != null) {
182 setCustomContentHintVisibility(cling, ccHintStr, false, true);
183 }
184 }
185 }
186
187 /** Updates the first run cling search bar hint. */
188 public void updateSearchBarHint(String hint) {
189 Cling cling = (Cling) mLauncher.findViewById(R.id.first_run_cling);
190 if (cling != null && cling.getVisibility() == View.VISIBLE && !hint.isEmpty()) {
191 TextView sbHint = (TextView) cling.findViewById(R.id.search_bar_hint);
192 sbHint.setText(hint);
193 sbHint.setVisibility(View.VISIBLE);
194 }
195 }
196
Winson Chunge43a1e72014-01-15 10:33:02 -0800197 public boolean shouldShowFirstRunOrMigrationClings() {
Winson Chunga6945242014-01-08 14:04:34 -0800198 SharedPreferences sharedPrefs = mLauncher.getSharedPrefs();
Winson Chung205cd772014-01-15 14:31:59 -0800199 return areClingsEnabled() &&
Winson Chunge43a1e72014-01-15 10:33:02 -0800200 !sharedPrefs.getBoolean(FIRST_RUN_CLING_DISMISSED_KEY, false) &&
Adam Cohen71e03b92014-02-21 14:09:53 -0800201 !sharedPrefs.getBoolean(MIGRATION_CLING_DISMISSED_KEY, false);
Winson Chunge43a1e72014-01-15 10:33:02 -0800202 }
Winson Chunga6945242014-01-08 14:04:34 -0800203
Winson Chunge43a1e72014-01-15 10:33:02 -0800204 public void removeFirstRunAndMigrationClings() {
205 removeCling(R.id.first_run_cling);
206 removeCling(R.id.migration_cling);
207 }
Winson Chunga6945242014-01-08 14:04:34 -0800208
Winson Chunge43a1e72014-01-15 10:33:02 -0800209 /**
210 * Shows the first run cling.
211 *
212 * This flow is mutually exclusive with showMigrationCling, and only runs if this Launcher
213 * package was preinstalled or there is no db to migrate from.
214 */
215 public void showFirstRunCling() {
216 if (!skipCustomClingIfNoAccounts()) {
Winson Chunga6945242014-01-08 14:04:34 -0800217 Cling cling = (Cling) mLauncher.findViewById(R.id.first_run_cling);
218 if (cling != null) {
219 String sbHintStr = mLauncher.getFirstRunClingSearchBarHint();
220 String ccHintStr = mLauncher.getFirstRunCustomContentHint();
221 if (!sbHintStr.isEmpty()) {
222 TextView sbHint = (TextView) cling.findViewById(R.id.search_bar_hint);
223 sbHint.setText(sbHintStr);
224 sbHint.setVisibility(View.VISIBLE);
225 }
226 setCustomContentHintVisibility(cling, ccHintStr, true, false);
227 }
228 initCling(R.id.first_run_cling, 0, false, true);
229 } else {
Winson Chunge43a1e72014-01-15 10:33:02 -0800230 removeFirstRunAndMigrationClings();
Winson Chunga6945242014-01-08 14:04:34 -0800231 }
232 }
233
Winson Chunge43a1e72014-01-15 10:33:02 -0800234 /**
235 * Shows the migration cling.
236 *
237 * This flow is mutually exclusive with showFirstRunCling, and only runs if this Launcher
238 * package was not preinstalled and there exists a db to migrate from.
239 */
Winson Chunga6945242014-01-08 14:04:34 -0800240 public void showMigrationCling() {
Winson Chunge43a1e72014-01-15 10:33:02 -0800241 mLauncher.hideWorkspaceSearchAndHotseat();
Winson Chunga6945242014-01-08 14:04:34 -0800242
Winson Chunge43a1e72014-01-15 10:33:02 -0800243 Cling c = initCling(R.id.migration_cling, 0, false, true);
244 c.bringScrimToFront();
245 c.bringToFront();
Winson Chunga6945242014-01-08 14:04:34 -0800246 }
247
248 public void showMigrationWorkspaceCling() {
249 // Enable the clings only if they have not been dismissed before
Winson Chung205cd772014-01-15 14:31:59 -0800250 if (areClingsEnabled() && !mLauncher.getSharedPrefs().getBoolean(
Winson Chunga6945242014-01-08 14:04:34 -0800251 MIGRATION_WORKSPACE_CLING_DISMISSED_KEY, false)) {
252 Cling c = initCling(R.id.migration_workspace_cling, 0, false, true);
253 c.updateMigrationWorkspaceBubblePosition();
254 c.bringScrimToFront();
255 c.bringToFront();
256 } else {
257 removeCling(R.id.migration_workspace_cling);
258 }
259 }
260
261 public void showWorkspaceCling() {
262 // Enable the clings only if they have not been dismissed before
Winson Chung205cd772014-01-15 14:31:59 -0800263 if (areClingsEnabled() && !mLauncher.getSharedPrefs().getBoolean(
Winson Chunga6945242014-01-08 14:04:34 -0800264 WORKSPACE_CLING_DISMISSED_KEY, false)) {
265 Cling c = initCling(R.id.workspace_cling, 0, false, true);
Winson Chung205cd772014-01-15 14:31:59 -0800266 c.updateWorkspaceBubblePosition();
Winson Chunga6945242014-01-08 14:04:34 -0800267
Winson Chung69dffdb2014-04-04 16:53:17 -0700268 try {
269 // We only enable the focused hotseat app if we are preinstalled
270 PackageManager pm = mLauncher.getPackageManager();
271 ApplicationInfo ai = pm.getApplicationInfo(mLauncher.getPackageName(), 0);
272 if ((ai.flags & ApplicationInfo.FLAG_SYSTEM) != 0) {
273 // Set the focused hotseat app
274 c.setFocusedHotseatApp(mLauncher.getFirstRunFocusedHotseatAppDrawableId(),
275 mLauncher.getFirstRunFocusedHotseatAppRank(),
276 mLauncher.getFirstRunFocusedHotseatAppComponentName(),
277 mLauncher.getFirstRunFocusedHotseatAppBubbleTitle(),
278 mLauncher.getFirstRunFocusedHotseatAppBubbleDescription());
279 }
280 } catch (PackageManager.NameNotFoundException e) {
281 e.printStackTrace();
282 }
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 Chungc6c03672014-03-06 10:12:02 -0800318 public void markFolderClingDismissed() {
319 SharedPreferences.Editor editor = mLauncher.getSharedPrefs().edit();
320 editor.putBoolean(LauncherClings.FOLDER_CLING_DISMISSED_KEY, true);
321 editor.apply();
322 }
323
Winson Chunga6945242014-01-08 14:04:34 -0800324 /** Removes the cling outright from the DragLayer */
325 private void removeCling(int id) {
326 final View cling = mLauncher.findViewById(id);
327 if (cling != null) {
328 final ViewGroup parent = (ViewGroup) cling.getParent();
329 parent.post(new Runnable() {
330 @Override
331 public void run() {
332 parent.removeView(cling);
333 }
334 });
335 mHideFromAccessibilityHelper.restoreImportantForAccessibility(mLauncher.getDragLayer());
336 }
337 }
338
339 /** Hides the specified Cling */
340 private void dismissCling(final Cling cling, final Runnable postAnimationCb,
341 final String flag, int duration, boolean restoreNavBarVisibilty) {
342 // To catch cases where siblings of top-level views are made invisible, just check whether
343 // the cling is directly set to GONE before dismissing it.
344 if (cling != null && cling.getVisibility() != View.GONE) {
345 final Runnable cleanUpClingCb = new Runnable() {
346 public void run() {
347 cling.cleanup();
348 SharedPreferences.Editor editor = mLauncher.getSharedPrefs().edit();
349 editor.putBoolean(flag, true);
350 editor.apply();
351 if (postAnimationCb != null) {
352 postAnimationCb.run();
353 }
354 }
355 };
356 if (duration <= 0) {
357 cleanUpClingCb.run();
358 } else {
359 cling.hide(duration, cleanUpClingCb);
360 }
361 mHideFromAccessibilityHelper.restoreImportantForAccessibility(mLauncher.getDragLayer());
362
363 if (restoreNavBarVisibilty) {
364 cling.setSystemUiVisibility(cling.getSystemUiVisibility() &
365 ~View.SYSTEM_UI_FLAG_LOW_PROFILE);
366 }
367 }
368 }
369
370 public void dismissFirstRunCling(View v) {
371 Cling cling = (Cling) mLauncher.findViewById(R.id.first_run_cling);
372 Runnable cb = new Runnable() {
373 public void run() {
374 // Show the workspace cling next
375 showWorkspaceCling();
376 }
377 };
378 dismissCling(cling, cb, FIRST_RUN_CLING_DISMISSED_KEY,
379 DISMISS_CLING_DURATION, false);
380
381 // Fade out the search bar for the workspace cling coming up
382 mLauncher.getSearchBar().hideSearchBar(true);
383 }
384
385 private void dismissMigrationCling() {
386 mLauncher.showWorkspaceSearchAndHotseat();
387 Runnable dismissCb = new Runnable() {
388 public void run() {
389 Cling cling = (Cling) mLauncher.findViewById(R.id.migration_cling);
390 Runnable cb = new Runnable() {
391 public void run() {
392 // Show the migration workspace cling next
393 showMigrationWorkspaceCling();
394 }
395 };
Winson Chung285b6e12014-01-14 10:30:27 -0800396 dismissCling(cling, cb, MIGRATION_CLING_DISMISSED_KEY,
Winson Chunga6945242014-01-08 14:04:34 -0800397 DISMISS_CLING_DURATION, true);
398 }
399 };
400 mLauncher.getWorkspace().post(dismissCb);
401 }
402
Winson Chung285b6e12014-01-14 10:30:27 -0800403 private void dismissAnyWorkspaceCling(Cling cling, String key, View v) {
Winson Chunga6945242014-01-08 14:04:34 -0800404 Runnable cb = null;
405 if (v == null) {
406 cb = new Runnable() {
407 public void run() {
408 mLauncher.getWorkspace().enterOverviewMode();
409 }
410 };
411 }
Winson Chung285b6e12014-01-14 10:30:27 -0800412 dismissCling(cling, cb, key, DISMISS_CLING_DURATION, true);
Winson Chunga6945242014-01-08 14:04:34 -0800413
414 // Fade in the search bar
415 mLauncher.getSearchBar().showSearchBar(true);
416 }
417
418 public void dismissMigrationClingCopyApps(View v) {
419 // Copy the shortcuts from the old database
420 LauncherModel model = mLauncher.getModel();
Winson Chung5317c2b2014-01-10 16:46:15 -0800421 model.resetLoadedState(false, true);
422 model.startLoader(false, PagedView.INVALID_RESTORE_PAGE,
423 LauncherModel.LOADER_FLAG_CLEAR_WORKSPACE
424 | LauncherModel.LOADER_FLAG_MIGRATE_SHORTCUTS);
Winson Chunga6945242014-01-08 14:04:34 -0800425
426 // Set the flag to skip the folder cling
427 String spKey = LauncherAppState.getSharedPreferencesKey();
428 SharedPreferences sp = mLauncher.getSharedPreferences(spKey, Context.MODE_PRIVATE);
429 SharedPreferences.Editor editor = sp.edit();
430 editor.putBoolean(Launcher.USER_HAS_MIGRATED, true);
431 editor.apply();
432
433 // Disable the migration cling
434 dismissMigrationCling();
435 }
436
437 public void dismissMigrationClingUseDefault(View v) {
438 // Clear the workspace
439 LauncherModel model = mLauncher.getModel();
Winson Chung5317c2b2014-01-10 16:46:15 -0800440 model.resetLoadedState(false, true);
441 model.startLoader(false, PagedView.INVALID_RESTORE_PAGE,
442 LauncherModel.LOADER_FLAG_CLEAR_WORKSPACE);
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}