blob: ec0a4238fbcfc201846ec646bf655763501ba8fd [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;
26import android.os.Bundle;
27import android.os.UserManager;
28import android.view.LayoutInflater;
29import android.view.View;
30import android.view.ViewGroup;
31import android.view.accessibility.AccessibilityManager;
32import android.widget.TextView;
33
34class LauncherClings {
35 private static final String FIRST_RUN_CLING_DISMISSED_KEY = "cling_gel.first_run.dismissed";
36 private static final String MIGRATION_CLING_DISMISSED_KEY = "cling_gel.migration.dismissed";
37 private static final String MIGRATION_WORKSPACE_CLING_DISMISSED_KEY = "cling_gel.migration.dismissed";
38 private static final String WORKSPACE_CLING_DISMISSED_KEY = "cling_gel.workspace.dismissed";
39 private static final String FOLDER_CLING_DISMISSED_KEY = "cling_gel.folder.dismissed";
40
41 private static final boolean DISABLE_CLINGS = false;
42 private static final boolean DISABLE_CUSTOM_CLINGS = true;
43
44 private static final int SHOW_CLING_DURATION = 250;
45 private static final int DISMISS_CLING_DURATION = 200;
46
47 private Launcher mLauncher;
48 private LayoutInflater mInflater;
49 private HideFromAccessibilityHelper mHideFromAccessibilityHelper
50 = new HideFromAccessibilityHelper();
51
52 /** Ctor */
53 public LauncherClings(Launcher launcher) {
54 mLauncher = launcher;
55 mInflater = mLauncher.getLayoutInflater();
56 }
57
58 /** Initializes a cling */
59 private Cling initCling(int clingId, int scrimId, boolean animate,
60 boolean dimNavBarVisibilty) {
61 Cling cling = (Cling) mLauncher.findViewById(clingId);
62 View scrim = null;
63 if (scrimId > 0) {
64 scrim = mLauncher.findViewById(R.id.cling_scrim);
65 }
66 if (cling != null) {
67 cling.init(mLauncher, scrim);
68 cling.show(animate, SHOW_CLING_DURATION);
69
70 if (dimNavBarVisibilty) {
71 cling.setSystemUiVisibility(cling.getSystemUiVisibility() |
72 View.SYSTEM_UI_FLAG_LOW_PROFILE);
73 }
74 }
75 return cling;
76 }
77
78 /** Returns whether the clings are enabled or should be shown */
79 private boolean isClingsEnabled() {
80 if (DISABLE_CLINGS) {
81 return false;
82 }
83
84 // For now, limit only to phones
85 LauncherAppState app = LauncherAppState.getInstance();
86 DeviceProfile grid = app.getDynamicGrid().getDeviceProfile();
87 if (grid.isTablet()) {
88 return false;
89 }
90 if (grid.isLandscape) {
91 return false;
92 }
93
94 // disable clings when running in a test harness
95 if(ActivityManager.isRunningInTestHarness()) return false;
96
97 // Disable clings for accessibility when explore by touch is enabled
98 final AccessibilityManager a11yManager = (AccessibilityManager) mLauncher.getSystemService(
99 Launcher.ACCESSIBILITY_SERVICE);
100 if (a11yManager.isTouchExplorationEnabled()) {
101 return false;
102 }
103
104 // Restricted secondary users (child mode) will potentially have very few apps
105 // seeded when they start up for the first time. Clings won't work well with that
106 boolean supportsLimitedUsers =
107 android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.JELLY_BEAN_MR2;
108 Account[] accounts = AccountManager.get(mLauncher).getAccounts();
109 if (supportsLimitedUsers && accounts.length == 0) {
110 UserManager um = (UserManager) mLauncher.getSystemService(Context.USER_SERVICE);
111 Bundle restrictions = um.getUserRestrictions();
112 if (restrictions.getBoolean(UserManager.DISALLOW_MODIFY_ACCOUNTS, false)) {
113 return false;
114 }
115 }
116 return true;
117 }
118
119 /** Returns whether the folder cling is visible. */
120 public boolean isFolderClingVisible() {
121 Cling cling = (Cling) mLauncher.findViewById(R.id.folder_cling);
122 if (cling != null) {
123 return cling.getVisibility() == View.VISIBLE;
124 }
125 return false;
126 }
127
128 private boolean skipCustomClingIfNoAccounts() {
129 Cling cling = (Cling) mLauncher.findViewById(R.id.workspace_cling);
130 boolean customCling = cling.getDrawIdentifier().equals("workspace_custom");
131 if (customCling) {
132 AccountManager am = AccountManager.get(mLauncher);
133 if (am == null) return false;
134 Account[] accounts = am.getAccountsByType("com.google");
135 return accounts.length == 0;
136 }
137 return false;
138 }
139
140 /** Updates the first run cling custom content hint */
141 private void setCustomContentHintVisibility(Cling cling, String ccHintStr, boolean visible,
142 boolean animate) {
143 final TextView ccHint = (TextView) cling.findViewById(R.id.custom_content_hint);
144 if (ccHint != null) {
145 if (visible && !ccHintStr.isEmpty()) {
146 ccHint.setText(ccHintStr);
147 ccHint.setVisibility(View.VISIBLE);
148 if (animate) {
149 ccHint.setAlpha(0f);
150 ccHint.animate().alpha(1f)
151 .setDuration(SHOW_CLING_DURATION)
152 .start();
153 } else {
154 ccHint.setAlpha(1f);
155 }
156 } else {
157 if (animate) {
158 ccHint.animate().alpha(0f)
159 .setDuration(SHOW_CLING_DURATION)
160 .setListener(new AnimatorListenerAdapter() {
161 @Override
162 public void onAnimationEnd(Animator animation) {
163 ccHint.setVisibility(View.GONE);
164 }
165 })
166 .start();
167 } else {
168 ccHint.setAlpha(0f);
169 ccHint.setVisibility(View.GONE);
170 }
171 }
172 }
173 }
174
175 /** Updates the first run cling custom content hint */
176 public void updateCustomContentHintVisibility() {
177 Cling cling = (Cling) mLauncher.findViewById(R.id.first_run_cling);
178 String ccHintStr = mLauncher.getFirstRunCustomContentHint();
179
180 if (mLauncher.getWorkspace().hasCustomContent()) {
181 // Show the custom content hint if ccHintStr is not empty
182 if (cling != null) {
183 setCustomContentHintVisibility(cling, ccHintStr, true, true);
184 }
185 } else {
186 // Hide the custom content hint
187 if (cling != null) {
188 setCustomContentHintVisibility(cling, ccHintStr, false, true);
189 }
190 }
191 }
192
193 /** Updates the first run cling search bar hint. */
194 public void updateSearchBarHint(String hint) {
195 Cling cling = (Cling) mLauncher.findViewById(R.id.first_run_cling);
196 if (cling != null && cling.getVisibility() == View.VISIBLE && !hint.isEmpty()) {
197 TextView sbHint = (TextView) cling.findViewById(R.id.search_bar_hint);
198 sbHint.setText(hint);
199 sbHint.setVisibility(View.VISIBLE);
200 }
201 }
202
203 /** Shows the first run cling */
204 public void showFirstRunCling() {
205 SharedPreferences sharedPrefs = mLauncher.getSharedPrefs();
206 if (isClingsEnabled() &&
207 !sharedPrefs.getBoolean(FIRST_RUN_CLING_DISMISSED_KEY, false) &&
208 !skipCustomClingIfNoAccounts() ) {
209
210
211 // If we're not using the default workspace layout, replace workspace cling
212 // with a custom workspace cling (usually specified in an overlay)
213 // For now, only do this on tablets
214 if (!DISABLE_CUSTOM_CLINGS) {
215 if (sharedPrefs.getInt(LauncherProvider.DEFAULT_WORKSPACE_RESOURCE_ID, 0) != 0 &&
216 mLauncher.getResources().getBoolean(R.bool.config_useCustomClings)) {
217 // Use a custom cling
218 View cling = mLauncher.findViewById(R.id.workspace_cling);
219 ViewGroup clingParent = (ViewGroup) cling.getParent();
220 int clingIndex = clingParent.indexOfChild(cling);
221 clingParent.removeViewAt(clingIndex);
222 View customCling = mInflater.inflate(R.layout.custom_workspace_cling,
223 clingParent, false);
224 clingParent.addView(customCling, clingIndex);
225 customCling.setId(R.id.workspace_cling);
226 }
227 }
228 Cling cling = (Cling) mLauncher.findViewById(R.id.first_run_cling);
229 if (cling != null) {
230 String sbHintStr = mLauncher.getFirstRunClingSearchBarHint();
231 String ccHintStr = mLauncher.getFirstRunCustomContentHint();
232 if (!sbHintStr.isEmpty()) {
233 TextView sbHint = (TextView) cling.findViewById(R.id.search_bar_hint);
234 sbHint.setText(sbHintStr);
235 sbHint.setVisibility(View.VISIBLE);
236 }
237 setCustomContentHintVisibility(cling, ccHintStr, true, false);
238 }
239 initCling(R.id.first_run_cling, 0, false, true);
240 } else {
241 removeCling(R.id.first_run_cling);
242 }
243 }
244
245 public void showMigrationCling() {
246 // Enable the clings only if they have not been dismissed before
247 if (isClingsEnabled() && !mLauncher.getSharedPrefs().getBoolean(
248 MIGRATION_CLING_DISMISSED_KEY, false)) {
249 mLauncher.hideWorkspaceSearchAndHotseat();
250
251 Cling c = initCling(R.id.migration_cling, 0, false, true);
252 c.bringScrimToFront();
253 c.bringToFront();
254 } else {
255 removeCling(R.id.migration_cling);
256 }
257 }
258
259 public void showMigrationWorkspaceCling() {
260 // Enable the clings only if they have not been dismissed before
261 if (isClingsEnabled() && !mLauncher.getSharedPrefs().getBoolean(
262 MIGRATION_WORKSPACE_CLING_DISMISSED_KEY, false)) {
263 Cling c = initCling(R.id.migration_workspace_cling, 0, false, true);
264 c.updateMigrationWorkspaceBubblePosition();
265 c.bringScrimToFront();
266 c.bringToFront();
267 } else {
268 removeCling(R.id.migration_workspace_cling);
269 }
270 }
271
272 public void showWorkspaceCling() {
273 // Enable the clings only if they have not been dismissed before
274 if (isClingsEnabled() && !mLauncher.getSharedPrefs().getBoolean(
275 WORKSPACE_CLING_DISMISSED_KEY, false)) {
276 Cling c = initCling(R.id.workspace_cling, 0, false, true);
277
278 // Set the focused hotseat app if there is one
279 c.setFocusedHotseatApp(mLauncher.getFirstRunFocusedHotseatAppDrawableId(),
280 mLauncher.getFirstRunFocusedHotseatAppRank(),
281 mLauncher.getFirstRunFocusedHotseatAppComponentName(),
282 mLauncher.getFirstRunFocusedHotseatAppBubbleTitle(),
283 mLauncher.getFirstRunFocusedHotseatAppBubbleDescription());
284 } else {
285 removeCling(R.id.workspace_cling);
286 }
287 }
288 public Cling showFoldersCling() {
289 SharedPreferences sharedPrefs = mLauncher.getSharedPrefs();
290 // Enable the clings only if they have not been dismissed before
291 if (isClingsEnabled() &&
292 !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);
296 return cling;
297 } else {
298 removeCling(R.id.folder_cling);
299 return null;
300 }
301 }
302
303
304 /** Removes the cling outright from the DragLayer */
305 private void removeCling(int id) {
306 final View cling = mLauncher.findViewById(id);
307 if (cling != null) {
308 final ViewGroup parent = (ViewGroup) cling.getParent();
309 parent.post(new Runnable() {
310 @Override
311 public void run() {
312 parent.removeView(cling);
313 }
314 });
315 mHideFromAccessibilityHelper.restoreImportantForAccessibility(mLauncher.getDragLayer());
316 }
317 }
318
319 /** Hides the specified Cling */
320 private void dismissCling(final Cling cling, final Runnable postAnimationCb,
321 final String flag, int duration, boolean restoreNavBarVisibilty) {
322 // To catch cases where siblings of top-level views are made invisible, just check whether
323 // the cling is directly set to GONE before dismissing it.
324 if (cling != null && cling.getVisibility() != View.GONE) {
325 final Runnable cleanUpClingCb = new Runnable() {
326 public void run() {
327 cling.cleanup();
328 SharedPreferences.Editor editor = mLauncher.getSharedPrefs().edit();
329 editor.putBoolean(flag, true);
330 editor.apply();
331 if (postAnimationCb != null) {
332 postAnimationCb.run();
333 }
334 }
335 };
336 if (duration <= 0) {
337 cleanUpClingCb.run();
338 } else {
339 cling.hide(duration, cleanUpClingCb);
340 }
341 mHideFromAccessibilityHelper.restoreImportantForAccessibility(mLauncher.getDragLayer());
342
343 if (restoreNavBarVisibilty) {
344 cling.setSystemUiVisibility(cling.getSystemUiVisibility() &
345 ~View.SYSTEM_UI_FLAG_LOW_PROFILE);
346 }
347 }
348 }
349
350 public void dismissFirstRunCling(View v) {
351 Cling cling = (Cling) mLauncher.findViewById(R.id.first_run_cling);
352 Runnable cb = new Runnable() {
353 public void run() {
354 // Show the workspace cling next
355 showWorkspaceCling();
356 }
357 };
358 dismissCling(cling, cb, FIRST_RUN_CLING_DISMISSED_KEY,
359 DISMISS_CLING_DURATION, false);
360
361 // Fade out the search bar for the workspace cling coming up
362 mLauncher.getSearchBar().hideSearchBar(true);
363 }
364
365 private void dismissMigrationCling() {
366 mLauncher.showWorkspaceSearchAndHotseat();
367 Runnable dismissCb = new Runnable() {
368 public void run() {
369 Cling cling = (Cling) mLauncher.findViewById(R.id.migration_cling);
370 Runnable cb = new Runnable() {
371 public void run() {
372 // Show the migration workspace cling next
373 showMigrationWorkspaceCling();
374 }
375 };
376 dismissCling(cling, cb, WORKSPACE_CLING_DISMISSED_KEY,
377 DISMISS_CLING_DURATION, true);
378 }
379 };
380 mLauncher.getWorkspace().post(dismissCb);
381 }
382
383 private void dismissAnyWorkspaceCling(Cling cling, View v) {
384 Runnable cb = null;
385 if (v == null) {
386 cb = new Runnable() {
387 public void run() {
388 mLauncher.getWorkspace().enterOverviewMode();
389 }
390 };
391 }
392 dismissCling(cling, cb, WORKSPACE_CLING_DISMISSED_KEY,
393 DISMISS_CLING_DURATION, true);
394
395 // Fade in the search bar
396 mLauncher.getSearchBar().showSearchBar(true);
397 }
398
399 public void dismissMigrationClingCopyApps(View v) {
400 // Copy the shortcuts from the old database
401 LauncherModel model = mLauncher.getModel();
402// model.resetLoadedState(false, true);
403// model.startLoader(false, PagedView.INVALID_RESTORE_PAGE,
404// LauncherModel.LOADER_FLAG_CLEAR_WORKSPACE
405// | LauncherModel.LOADER_FLAG_MIGRATE_SHORTCUTS);
406
407 // Set the flag to skip the folder cling
408 String spKey = LauncherAppState.getSharedPreferencesKey();
409 SharedPreferences sp = mLauncher.getSharedPreferences(spKey, Context.MODE_PRIVATE);
410 SharedPreferences.Editor editor = sp.edit();
411 editor.putBoolean(Launcher.USER_HAS_MIGRATED, true);
412 editor.apply();
413
414 // Disable the migration cling
415 dismissMigrationCling();
416 }
417
418 public void dismissMigrationClingUseDefault(View v) {
419 // Clear the workspace
420 LauncherModel model = mLauncher.getModel();
421// model.resetLoadedState(false, true);
422// model.startLoader(false, PagedView.INVALID_RESTORE_PAGE,
423// LauncherModel.LOADER_FLAG_CLEAR_WORKSPACE);
424
425 // Disable the migration cling
426 dismissMigrationCling();
427 }
428
429 public void dismissMigrationWorkspaceCling(View v) {
430 Cling cling = (Cling) mLauncher.findViewById(R.id.migration_workspace_cling);
431 dismissAnyWorkspaceCling(cling, v);
432 }
433
434 public void dismissWorkspaceCling(View v) {
435 Cling cling = (Cling) mLauncher.findViewById(R.id.workspace_cling);
436 dismissAnyWorkspaceCling(cling, v);
437 }
438
439 public void dismissFolderCling(View v) {
440 Cling cling = (Cling) mLauncher.findViewById(R.id.folder_cling);
441 dismissCling(cling, null, FOLDER_CLING_DISMISSED_KEY,
442 DISMISS_CLING_DURATION, true);
443 }
444}