blob: 9d2778cfac87a777fd22266266b418caee97a376 [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;
44 private static final boolean DISABLE_CUSTOM_CLINGS = true;
45
46 private static final int SHOW_CLING_DURATION = 250;
47 private static final int DISMISS_CLING_DURATION = 200;
48
49 private Launcher mLauncher;
50 private LayoutInflater mInflater;
51 private HideFromAccessibilityHelper mHideFromAccessibilityHelper
52 = new HideFromAccessibilityHelper();
53
54 /** Ctor */
55 public LauncherClings(Launcher launcher) {
56 mLauncher = launcher;
57 mInflater = mLauncher.getLayoutInflater();
58 }
59
60 /** Initializes a cling */
61 private Cling initCling(int clingId, int scrimId, boolean animate,
62 boolean dimNavBarVisibilty) {
63 Cling cling = (Cling) mLauncher.findViewById(clingId);
64 View scrim = null;
65 if (scrimId > 0) {
Winson Chung205cd772014-01-15 14:31:59 -080066 scrim = mLauncher.findViewById(scrimId);
Winson Chunga6945242014-01-08 14:04:34 -080067 }
68 if (cling != null) {
69 cling.init(mLauncher, scrim);
70 cling.show(animate, SHOW_CLING_DURATION);
71
72 if (dimNavBarVisibilty) {
73 cling.setSystemUiVisibility(cling.getSystemUiVisibility() |
74 View.SYSTEM_UI_FLAG_LOW_PROFILE);
75 }
76 }
77 return cling;
78 }
79
80 /** Returns whether the clings are enabled or should be shown */
Winson Chung205cd772014-01-15 14:31:59 -080081 private boolean areClingsEnabled() {
Winson Chunga6945242014-01-08 14:04:34 -080082 if (DISABLE_CLINGS) {
83 return false;
84 }
85
Winson Chunga6945242014-01-08 14:04:34 -080086 // disable clings when running in a test harness
87 if(ActivityManager.isRunningInTestHarness()) return false;
88
89 // Disable clings for accessibility when explore by touch is enabled
90 final AccessibilityManager a11yManager = (AccessibilityManager) mLauncher.getSystemService(
91 Launcher.ACCESSIBILITY_SERVICE);
92 if (a11yManager.isTouchExplorationEnabled()) {
93 return false;
94 }
95
96 // Restricted secondary users (child mode) will potentially have very few apps
97 // seeded when they start up for the first time. Clings won't work well with that
98 boolean supportsLimitedUsers =
99 android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.JELLY_BEAN_MR2;
100 Account[] accounts = AccountManager.get(mLauncher).getAccounts();
101 if (supportsLimitedUsers && accounts.length == 0) {
102 UserManager um = (UserManager) mLauncher.getSystemService(Context.USER_SERVICE);
103 Bundle restrictions = um.getUserRestrictions();
104 if (restrictions.getBoolean(UserManager.DISALLOW_MODIFY_ACCOUNTS, false)) {
105 return false;
106 }
107 }
108 return true;
109 }
110
111 /** Returns whether the folder cling is visible. */
112 public boolean isFolderClingVisible() {
113 Cling cling = (Cling) mLauncher.findViewById(R.id.folder_cling);
114 if (cling != null) {
115 return cling.getVisibility() == View.VISIBLE;
116 }
117 return false;
118 }
119
120 private boolean skipCustomClingIfNoAccounts() {
121 Cling cling = (Cling) mLauncher.findViewById(R.id.workspace_cling);
122 boolean customCling = cling.getDrawIdentifier().equals("workspace_custom");
123 if (customCling) {
124 AccountManager am = AccountManager.get(mLauncher);
125 if (am == null) return false;
126 Account[] accounts = am.getAccountsByType("com.google");
127 return accounts.length == 0;
128 }
129 return false;
130 }
131
132 /** Updates the first run cling custom content hint */
133 private void setCustomContentHintVisibility(Cling cling, String ccHintStr, boolean visible,
134 boolean animate) {
135 final TextView ccHint = (TextView) cling.findViewById(R.id.custom_content_hint);
136 if (ccHint != null) {
137 if (visible && !ccHintStr.isEmpty()) {
138 ccHint.setText(ccHintStr);
139 ccHint.setVisibility(View.VISIBLE);
140 if (animate) {
141 ccHint.setAlpha(0f);
142 ccHint.animate().alpha(1f)
143 .setDuration(SHOW_CLING_DURATION)
144 .start();
145 } else {
146 ccHint.setAlpha(1f);
147 }
148 } else {
149 if (animate) {
150 ccHint.animate().alpha(0f)
151 .setDuration(SHOW_CLING_DURATION)
152 .setListener(new AnimatorListenerAdapter() {
153 @Override
154 public void onAnimationEnd(Animator animation) {
155 ccHint.setVisibility(View.GONE);
156 }
157 })
158 .start();
159 } else {
160 ccHint.setAlpha(0f);
161 ccHint.setVisibility(View.GONE);
162 }
163 }
164 }
165 }
166
167 /** Updates the first run cling custom content hint */
168 public void updateCustomContentHintVisibility() {
169 Cling cling = (Cling) mLauncher.findViewById(R.id.first_run_cling);
170 String ccHintStr = mLauncher.getFirstRunCustomContentHint();
171
172 if (mLauncher.getWorkspace().hasCustomContent()) {
173 // Show the custom content hint if ccHintStr is not empty
174 if (cling != null) {
175 setCustomContentHintVisibility(cling, ccHintStr, true, true);
176 }
177 } else {
178 // Hide the custom content hint
179 if (cling != null) {
180 setCustomContentHintVisibility(cling, ccHintStr, false, true);
181 }
182 }
183 }
184
185 /** Updates the first run cling search bar hint. */
186 public void updateSearchBarHint(String hint) {
187 Cling cling = (Cling) mLauncher.findViewById(R.id.first_run_cling);
188 if (cling != null && cling.getVisibility() == View.VISIBLE && !hint.isEmpty()) {
189 TextView sbHint = (TextView) cling.findViewById(R.id.search_bar_hint);
190 sbHint.setText(hint);
191 sbHint.setVisibility(View.VISIBLE);
192 }
193 }
194
Winson Chunge43a1e72014-01-15 10:33:02 -0800195 public boolean shouldShowFirstRunOrMigrationClings() {
Winson Chunga6945242014-01-08 14:04:34 -0800196 SharedPreferences sharedPrefs = mLauncher.getSharedPrefs();
Winson Chung205cd772014-01-15 14:31:59 -0800197 return areClingsEnabled() &&
Winson Chunge43a1e72014-01-15 10:33:02 -0800198 !sharedPrefs.getBoolean(FIRST_RUN_CLING_DISMISSED_KEY, false) &&
Winson Chung0b560dd2014-01-21 13:00:26 -0800199 !sharedPrefs.getBoolean(MIGRATION_CLING_DISMISSED_KEY, false) &&
200 LauncherAppState.getLauncherProvider().wasNewDbCreated();
Winson Chunge43a1e72014-01-15 10:33:02 -0800201 }
Winson Chunga6945242014-01-08 14:04:34 -0800202
Winson Chunge43a1e72014-01-15 10:33:02 -0800203 public void removeFirstRunAndMigrationClings() {
204 removeCling(R.id.first_run_cling);
205 removeCling(R.id.migration_cling);
206 }
Winson Chunga6945242014-01-08 14:04:34 -0800207
Winson Chunge43a1e72014-01-15 10:33:02 -0800208 /**
209 * Shows the first run cling.
210 *
211 * This flow is mutually exclusive with showMigrationCling, and only runs if this Launcher
212 * package was preinstalled or there is no db to migrate from.
213 */
214 public void showFirstRunCling() {
215 if (!skipCustomClingIfNoAccounts()) {
216 SharedPreferences sharedPrefs = mLauncher.getSharedPrefs();
Winson Chunga6945242014-01-08 14:04:34 -0800217 // If we're not using the default workspace layout, replace workspace cling
218 // with a custom workspace cling (usually specified in an overlay)
219 // For now, only do this on tablets
220 if (!DISABLE_CUSTOM_CLINGS) {
221 if (sharedPrefs.getInt(LauncherProvider.DEFAULT_WORKSPACE_RESOURCE_ID, 0) != 0 &&
222 mLauncher.getResources().getBoolean(R.bool.config_useCustomClings)) {
223 // Use a custom cling
224 View cling = mLauncher.findViewById(R.id.workspace_cling);
225 ViewGroup clingParent = (ViewGroup) cling.getParent();
226 int clingIndex = clingParent.indexOfChild(cling);
227 clingParent.removeViewAt(clingIndex);
228 View customCling = mInflater.inflate(R.layout.custom_workspace_cling,
229 clingParent, false);
230 clingParent.addView(customCling, clingIndex);
231 customCling.setId(R.id.workspace_cling);
232 }
233 }
234 Cling cling = (Cling) mLauncher.findViewById(R.id.first_run_cling);
235 if (cling != null) {
236 String sbHintStr = mLauncher.getFirstRunClingSearchBarHint();
237 String ccHintStr = mLauncher.getFirstRunCustomContentHint();
238 if (!sbHintStr.isEmpty()) {
239 TextView sbHint = (TextView) cling.findViewById(R.id.search_bar_hint);
240 sbHint.setText(sbHintStr);
241 sbHint.setVisibility(View.VISIBLE);
242 }
243 setCustomContentHintVisibility(cling, ccHintStr, true, false);
244 }
245 initCling(R.id.first_run_cling, 0, false, true);
246 } else {
Winson Chunge43a1e72014-01-15 10:33:02 -0800247 removeFirstRunAndMigrationClings();
Winson Chunga6945242014-01-08 14:04:34 -0800248 }
249 }
250
Winson Chunge43a1e72014-01-15 10:33:02 -0800251 /**
252 * Shows the migration cling.
253 *
254 * This flow is mutually exclusive with showFirstRunCling, and only runs if this Launcher
255 * package was not preinstalled and there exists a db to migrate from.
256 */
Winson Chunga6945242014-01-08 14:04:34 -0800257 public void showMigrationCling() {
Winson Chunge43a1e72014-01-15 10:33:02 -0800258 mLauncher.hideWorkspaceSearchAndHotseat();
Winson Chunga6945242014-01-08 14:04:34 -0800259
Winson Chunge43a1e72014-01-15 10:33:02 -0800260 Cling c = initCling(R.id.migration_cling, 0, false, true);
261 c.bringScrimToFront();
262 c.bringToFront();
Winson Chunga6945242014-01-08 14:04:34 -0800263 }
264
265 public void showMigrationWorkspaceCling() {
266 // Enable the clings only if they have not been dismissed before
Winson Chung205cd772014-01-15 14:31:59 -0800267 if (areClingsEnabled() && !mLauncher.getSharedPrefs().getBoolean(
Winson Chunga6945242014-01-08 14:04:34 -0800268 MIGRATION_WORKSPACE_CLING_DISMISSED_KEY, false)) {
269 Cling c = initCling(R.id.migration_workspace_cling, 0, false, true);
270 c.updateMigrationWorkspaceBubblePosition();
271 c.bringScrimToFront();
272 c.bringToFront();
273 } else {
274 removeCling(R.id.migration_workspace_cling);
275 }
276 }
277
278 public void showWorkspaceCling() {
279 // Enable the clings only if they have not been dismissed before
Winson Chung205cd772014-01-15 14:31:59 -0800280 if (areClingsEnabled() && !mLauncher.getSharedPrefs().getBoolean(
Winson Chunga6945242014-01-08 14:04:34 -0800281 WORKSPACE_CLING_DISMISSED_KEY, false)) {
282 Cling c = initCling(R.id.workspace_cling, 0, false, true);
Winson Chung205cd772014-01-15 14:31:59 -0800283 c.updateWorkspaceBubblePosition();
Winson Chunga6945242014-01-08 14:04:34 -0800284
285 // Set the focused hotseat app if there is one
286 c.setFocusedHotseatApp(mLauncher.getFirstRunFocusedHotseatAppDrawableId(),
287 mLauncher.getFirstRunFocusedHotseatAppRank(),
288 mLauncher.getFirstRunFocusedHotseatAppComponentName(),
289 mLauncher.getFirstRunFocusedHotseatAppBubbleTitle(),
290 mLauncher.getFirstRunFocusedHotseatAppBubbleDescription());
291 } else {
292 removeCling(R.id.workspace_cling);
293 }
294 }
295 public Cling showFoldersCling() {
296 SharedPreferences sharedPrefs = mLauncher.getSharedPrefs();
297 // Enable the clings only if they have not been dismissed before
Winson Chung205cd772014-01-15 14:31:59 -0800298 if (areClingsEnabled() &&
Winson Chunga6945242014-01-08 14:04:34 -0800299 !sharedPrefs.getBoolean(FOLDER_CLING_DISMISSED_KEY, false) &&
300 !sharedPrefs.getBoolean(Launcher.USER_HAS_MIGRATED, false)) {
301 Cling cling = initCling(R.id.folder_cling, R.id.cling_scrim,
302 true, true);
Winson Chung205cd772014-01-15 14:31:59 -0800303 Folder openFolder = mLauncher.getWorkspace().getOpenFolder();
304 if (openFolder != null) {
305 Rect openFolderRect = new Rect();
306 openFolder.getHitRect(openFolderRect);
307 cling.setOpenFolderRect(openFolderRect);
308 openFolder.bringToFront();
309 }
Winson Chunga6945242014-01-08 14:04:34 -0800310 return cling;
311 } else {
312 removeCling(R.id.folder_cling);
313 return null;
314 }
315 }
316
Winson Chunga6945242014-01-08 14:04:34 -0800317 /** Removes the cling outright from the DragLayer */
318 private void removeCling(int id) {
319 final View cling = mLauncher.findViewById(id);
320 if (cling != null) {
321 final ViewGroup parent = (ViewGroup) cling.getParent();
322 parent.post(new Runnable() {
323 @Override
324 public void run() {
325 parent.removeView(cling);
326 }
327 });
328 mHideFromAccessibilityHelper.restoreImportantForAccessibility(mLauncher.getDragLayer());
329 }
330 }
331
332 /** Hides the specified Cling */
333 private void dismissCling(final Cling cling, final Runnable postAnimationCb,
334 final String flag, int duration, boolean restoreNavBarVisibilty) {
335 // To catch cases where siblings of top-level views are made invisible, just check whether
336 // the cling is directly set to GONE before dismissing it.
337 if (cling != null && cling.getVisibility() != View.GONE) {
338 final Runnable cleanUpClingCb = new Runnable() {
339 public void run() {
340 cling.cleanup();
341 SharedPreferences.Editor editor = mLauncher.getSharedPrefs().edit();
342 editor.putBoolean(flag, true);
343 editor.apply();
344 if (postAnimationCb != null) {
345 postAnimationCb.run();
346 }
347 }
348 };
349 if (duration <= 0) {
350 cleanUpClingCb.run();
351 } else {
352 cling.hide(duration, cleanUpClingCb);
353 }
354 mHideFromAccessibilityHelper.restoreImportantForAccessibility(mLauncher.getDragLayer());
355
356 if (restoreNavBarVisibilty) {
357 cling.setSystemUiVisibility(cling.getSystemUiVisibility() &
358 ~View.SYSTEM_UI_FLAG_LOW_PROFILE);
359 }
360 }
361 }
362
363 public void dismissFirstRunCling(View v) {
364 Cling cling = (Cling) mLauncher.findViewById(R.id.first_run_cling);
365 Runnable cb = new Runnable() {
366 public void run() {
367 // Show the workspace cling next
368 showWorkspaceCling();
369 }
370 };
371 dismissCling(cling, cb, FIRST_RUN_CLING_DISMISSED_KEY,
372 DISMISS_CLING_DURATION, false);
373
374 // Fade out the search bar for the workspace cling coming up
375 mLauncher.getSearchBar().hideSearchBar(true);
376 }
377
378 private void dismissMigrationCling() {
379 mLauncher.showWorkspaceSearchAndHotseat();
380 Runnable dismissCb = new Runnable() {
381 public void run() {
382 Cling cling = (Cling) mLauncher.findViewById(R.id.migration_cling);
383 Runnable cb = new Runnable() {
384 public void run() {
385 // Show the migration workspace cling next
386 showMigrationWorkspaceCling();
387 }
388 };
Winson Chung285b6e12014-01-14 10:30:27 -0800389 dismissCling(cling, cb, MIGRATION_CLING_DISMISSED_KEY,
Winson Chunga6945242014-01-08 14:04:34 -0800390 DISMISS_CLING_DURATION, true);
391 }
392 };
393 mLauncher.getWorkspace().post(dismissCb);
394 }
395
Winson Chung285b6e12014-01-14 10:30:27 -0800396 private void dismissAnyWorkspaceCling(Cling cling, String key, View v) {
Winson Chunga6945242014-01-08 14:04:34 -0800397 Runnable cb = null;
398 if (v == null) {
399 cb = new Runnable() {
400 public void run() {
401 mLauncher.getWorkspace().enterOverviewMode();
402 }
403 };
404 }
Winson Chung285b6e12014-01-14 10:30:27 -0800405 dismissCling(cling, cb, key, DISMISS_CLING_DURATION, true);
Winson Chunga6945242014-01-08 14:04:34 -0800406
407 // Fade in the search bar
408 mLauncher.getSearchBar().showSearchBar(true);
409 }
410
411 public void dismissMigrationClingCopyApps(View v) {
412 // Copy the shortcuts from the old database
413 LauncherModel model = mLauncher.getModel();
Winson Chung5317c2b2014-01-10 16:46:15 -0800414 model.resetLoadedState(false, true);
415 model.startLoader(false, PagedView.INVALID_RESTORE_PAGE,
416 LauncherModel.LOADER_FLAG_CLEAR_WORKSPACE
417 | LauncherModel.LOADER_FLAG_MIGRATE_SHORTCUTS);
Winson Chunga6945242014-01-08 14:04:34 -0800418
419 // Set the flag to skip the folder cling
420 String spKey = LauncherAppState.getSharedPreferencesKey();
421 SharedPreferences sp = mLauncher.getSharedPreferences(spKey, Context.MODE_PRIVATE);
422 SharedPreferences.Editor editor = sp.edit();
423 editor.putBoolean(Launcher.USER_HAS_MIGRATED, true);
424 editor.apply();
425
426 // Disable the migration cling
427 dismissMigrationCling();
428 }
429
430 public void dismissMigrationClingUseDefault(View v) {
431 // Clear the workspace
432 LauncherModel model = mLauncher.getModel();
Winson Chung5317c2b2014-01-10 16:46:15 -0800433 model.resetLoadedState(false, true);
434 model.startLoader(false, PagedView.INVALID_RESTORE_PAGE,
435 LauncherModel.LOADER_FLAG_CLEAR_WORKSPACE);
Winson Chunga6945242014-01-08 14:04:34 -0800436
437 // Disable the migration cling
438 dismissMigrationCling();
439 }
440
441 public void dismissMigrationWorkspaceCling(View v) {
442 Cling cling = (Cling) mLauncher.findViewById(R.id.migration_workspace_cling);
Winson Chung285b6e12014-01-14 10:30:27 -0800443 dismissAnyWorkspaceCling(cling, MIGRATION_WORKSPACE_CLING_DISMISSED_KEY, v);
Winson Chunga6945242014-01-08 14:04:34 -0800444 }
445
446 public void dismissWorkspaceCling(View v) {
447 Cling cling = (Cling) mLauncher.findViewById(R.id.workspace_cling);
Winson Chung285b6e12014-01-14 10:30:27 -0800448 dismissAnyWorkspaceCling(cling, WORKSPACE_CLING_DISMISSED_KEY, v);
Winson Chunga6945242014-01-08 14:04:34 -0800449 }
450
451 public void dismissFolderCling(View v) {
452 Cling cling = (Cling) mLauncher.findViewById(R.id.folder_cling);
453 dismissCling(cling, null, FOLDER_CLING_DISMISSED_KEY,
454 DISMISS_CLING_DURATION, true);
455 }
456}