1 /*
2  * Copyright (C) 2018 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 
17 package com.android.systemui.recents;
18 
19 import static android.app.WindowConfiguration.ACTIVITY_TYPE_STANDARD;
20 import static android.view.WindowManagerPolicyConstants.NAV_BAR_MODE_3BUTTON;
21 
22 import static com.android.systemui.Prefs.Key.DISMISSED_RECENTS_SWIPE_UP_ONBOARDING_COUNT;
23 import static com.android.systemui.Prefs.Key.HAS_DISMISSED_RECENTS_QUICK_SCRUB_ONBOARDING_ONCE;
24 import static com.android.systemui.Prefs.Key.HAS_SEEN_RECENTS_QUICK_SCRUB_ONBOARDING;
25 import static com.android.systemui.Prefs.Key.HAS_SEEN_RECENTS_SWIPE_UP_ONBOARDING;
26 import static com.android.systemui.Prefs.Key.OVERVIEW_OPENED_COUNT;
27 import static com.android.systemui.Prefs.Key.OVERVIEW_OPENED_FROM_HOME_COUNT;
28 import static com.android.systemui.shared.system.LauncherEventUtil.DISMISS;
29 import static com.android.systemui.shared.system.LauncherEventUtil.RECENTS_QUICK_SCRUB_ONBOARDING_TIP;
30 import static com.android.systemui.shared.system.LauncherEventUtil.RECENTS_SWIPE_UP_ONBOARDING_TIP;
31 import static com.android.systemui.shared.system.LauncherEventUtil.VISIBLE;
32 
33 import android.annotation.StringRes;
34 import android.annotation.TargetApi;
35 import android.app.ActivityManager;
36 import android.content.BroadcastReceiver;
37 import android.content.ComponentName;
38 import android.content.Context;
39 import android.content.Intent;
40 import android.content.IntentFilter;
41 import android.content.res.Configuration;
42 import android.content.res.Resources;
43 import android.graphics.CornerPathEffect;
44 import android.graphics.Paint;
45 import android.graphics.PixelFormat;
46 import android.graphics.drawable.ShapeDrawable;
47 import android.os.Build;
48 import android.os.RemoteException;
49 import android.os.SystemProperties;
50 import android.os.UserManager;
51 import android.util.TypedValue;
52 import android.view.Gravity;
53 import android.view.LayoutInflater;
54 import android.view.View;
55 import android.view.ViewGroup;
56 import android.view.WindowManager;
57 import android.view.animation.AccelerateInterpolator;
58 import android.view.animation.DecelerateInterpolator;
59 import android.widget.ImageView;
60 import android.widget.TextView;
61 
62 import com.android.systemui.Dependency;
63 import com.android.systemui.Prefs;
64 import com.android.systemui.R;
65 import com.android.systemui.broadcast.BroadcastDispatcher;
66 import com.android.systemui.shared.recents.IOverviewProxy;
67 import com.android.systemui.shared.system.ActivityManagerWrapper;
68 import com.android.systemui.shared.system.QuickStepContract;
69 import com.android.systemui.shared.system.TaskStackChangeListener;
70 
71 import java.io.PrintWriter;
72 import java.util.Collections;
73 import java.util.HashSet;
74 import java.util.Set;
75 
76 /**
77  * Shows onboarding for the new recents interaction in P (codenamed quickstep).
78  */
79 @TargetApi(Build.VERSION_CODES.P)
80 public class RecentsOnboarding {
81 
82     private static final String TAG = "RecentsOnboarding";
83     private static final boolean RESET_PREFS_FOR_DEBUG = false;
84     private static final boolean ONBOARDING_ENABLED = true;
85     private static final long SHOW_DELAY_MS = 500;
86     private static final long SHOW_DURATION_MS = 300;
87     private static final long HIDE_DURATION_MS = 100;
88     // Show swipe-up tips after opening overview from home this number of times.
89     private static final int SWIPE_UP_SHOW_ON_OVERVIEW_OPENED_FROM_HOME_COUNT = 3;
90     // Show quick scrub tips after opening overview this number of times.
91     private static final int QUICK_SCRUB_SHOW_ON_OVERVIEW_OPENED_COUNT = 10;
92     // Maximum number of dismissals while still showing swipe-up tips.
93     private static final int MAX_DISMISSAL_ON_SWIPE_UP_SHOW = 2;
94     // Number of dismissals for swipe-up tips when exponential backoff starts.
95     private static final int BACKOFF_DISMISSAL_COUNT_ON_SWIPE_UP_SHOW = 1;
96     // After explicitly dismissing for <= BACKOFF_DISMISSAL_COUNT_ON_SWIPE_UP_SHOW times, show again
97     // after launching this number of apps for swipe-up tips.
98     private static final int SWIPE_UP_SHOW_ON_APP_LAUNCH_AFTER_DISMISS = 5;
99     // After explicitly dismissing for > BACKOFF_DISMISSAL_COUNT_ON_SWIPE_UP_SHOW but
100     // <= MAX_DISMISSAL_ON_SWIPE_UP_SHOW times, show again after launching this number of apps for
101     // swipe-up tips.
102     private static final int SWIPE_UP_SHOW_ON_APP_LAUNCH_AFTER_DISMISS_BACK_OFF = 40;
103 
104     private final Context mContext;
105     private final WindowManager mWindowManager;
106     private final OverviewProxyService mOverviewProxyService;
107     private Set<String> mBlacklistedPackages;
108     private final View mLayout;
109     private final TextView mTextView;
110     private final ImageView mDismissView;
111     private final View mArrowView;
112     private final int mOnboardingToastColor;
113     private final int mOnboardingToastArrowRadius;
114     private int mNavBarHeight;
115     private int mNavBarMode = NAV_BAR_MODE_3BUTTON;
116 
117     private boolean mOverviewProxyListenerRegistered;
118     private boolean mTaskListenerRegistered;
119     private boolean mLayoutAttachedToWindow;
120     private boolean mHasDismissedSwipeUpTip;
121     private boolean mHasDismissedQuickScrubTip;
122     private int mNumAppsLaunchedSinceSwipeUpTipDismiss;
123     private int mOverviewOpenedCountSinceQuickScrubTipDismiss;
124 
125     private final TaskStackChangeListener mTaskListener = new TaskStackChangeListener() {
126         private String mLastPackageName;
127 
128         @Override
129         public void onTaskCreated(int taskId, ComponentName componentName) {
130             onAppLaunch();
131         }
132 
133         @Override
134         public void onTaskMovedToFront(int taskId) {
135             onAppLaunch();
136         }
137 
138         private void onAppLaunch() {
139             ActivityManager.RunningTaskInfo info = ActivityManagerWrapper.getInstance()
140                     .getRunningTask();
141             if (info == null) {
142                 return;
143             }
144             if (mBlacklistedPackages.contains(info.baseActivity.getPackageName())) {
145                 hide(true);
146                 return;
147             }
148             if (info.baseActivity.getPackageName().equals(mLastPackageName)) {
149                 return;
150             }
151             mLastPackageName = info.baseActivity.getPackageName();
152             int activityType = info.configuration.windowConfiguration.getActivityType();
153             if (activityType == ACTIVITY_TYPE_STANDARD) {
154                 boolean alreadySeenSwipeUpOnboarding = hasSeenSwipeUpOnboarding();
155                 boolean alreadySeenQuickScrubsOnboarding = hasSeenQuickScrubOnboarding();
156                 if (alreadySeenSwipeUpOnboarding && alreadySeenQuickScrubsOnboarding) {
157                     onDisconnectedFromLauncher();
158                     return;
159                 }
160 
161                 boolean shouldLog = false;
162                 if (!alreadySeenSwipeUpOnboarding) {
163                     if (getOpenedOverviewFromHomeCount()
164                             >= SWIPE_UP_SHOW_ON_OVERVIEW_OPENED_FROM_HOME_COUNT) {
165                         if (mHasDismissedSwipeUpTip) {
166                             int hasDimissedSwipeUpOnboardingCount =
167                                     getDismissedSwipeUpOnboardingCount();
168                             if (hasDimissedSwipeUpOnboardingCount
169                                     > MAX_DISMISSAL_ON_SWIPE_UP_SHOW) {
170                                 return;
171                             }
172                             final int swipeUpShowOnAppLauncherAfterDismiss =
173                                     hasDimissedSwipeUpOnboardingCount
174                                             <= BACKOFF_DISMISSAL_COUNT_ON_SWIPE_UP_SHOW
175                                             ? SWIPE_UP_SHOW_ON_APP_LAUNCH_AFTER_DISMISS
176                                             : SWIPE_UP_SHOW_ON_APP_LAUNCH_AFTER_DISMISS_BACK_OFF;
177                             mNumAppsLaunchedSinceSwipeUpTipDismiss++;
178                             if (mNumAppsLaunchedSinceSwipeUpTipDismiss
179                                     >= swipeUpShowOnAppLauncherAfterDismiss) {
180                                 mNumAppsLaunchedSinceSwipeUpTipDismiss = 0;
181                                 shouldLog = show(R.string.recents_swipe_up_onboarding);
182                             }
183                         } else {
184                             shouldLog = show(R.string.recents_swipe_up_onboarding);
185                         }
186                         if (shouldLog) {
187                             notifyOnTip(VISIBLE, RECENTS_SWIPE_UP_ONBOARDING_TIP);
188                         }
189                     }
190                 } else {
191                     if (getOpenedOverviewCount() >= QUICK_SCRUB_SHOW_ON_OVERVIEW_OPENED_COUNT) {
192                         if (mHasDismissedQuickScrubTip) {
193                             if (mOverviewOpenedCountSinceQuickScrubTipDismiss
194                                     >= QUICK_SCRUB_SHOW_ON_OVERVIEW_OPENED_COUNT) {
195                                 mOverviewOpenedCountSinceQuickScrubTipDismiss = 0;
196                                 shouldLog = show(R.string.recents_quick_scrub_onboarding);
197                             }
198                         } else {
199                             shouldLog = show(R.string.recents_quick_scrub_onboarding);
200                         }
201                         if (shouldLog) {
202                             notifyOnTip(VISIBLE, RECENTS_QUICK_SCRUB_ONBOARDING_TIP);
203                         }
204                     }
205 
206                 }
207             } else {
208                 hide(false);
209             }
210         }
211     };
212 
213     private OverviewProxyService.OverviewProxyListener mOverviewProxyListener =
214             new OverviewProxyService.OverviewProxyListener() {
215                 @Override
216                 public void onOverviewShown(boolean fromHome) {
217                     if (!hasSeenSwipeUpOnboarding() && !fromHome) {
218                         setHasSeenSwipeUpOnboarding(true);
219                     }
220                     if (fromHome) {
221                         incrementOpenedOverviewFromHomeCount();
222                     }
223                     incrementOpenedOverviewCount();
224 
225                     if (getOpenedOverviewCount() >= QUICK_SCRUB_SHOW_ON_OVERVIEW_OPENED_COUNT) {
226                         if (mHasDismissedQuickScrubTip) {
227                             mOverviewOpenedCountSinceQuickScrubTipDismiss++;
228                         }
229                     }
230                 }
231 
232                 @Override
233                 public void onQuickStepStarted() {
234                     hide(true);
235                 }
236 
237                 @Override
238                 public void onQuickScrubStarted() {
239                     boolean alreadySeenQuickScrubsOnboarding = hasSeenQuickScrubOnboarding();
240                     if (!alreadySeenQuickScrubsOnboarding) {
241                         setHasSeenQuickScrubOnboarding(true);
242                     }
243                 }
244             };
245 
246     private final View.OnAttachStateChangeListener mOnAttachStateChangeListener
247             = new View.OnAttachStateChangeListener() {
248 
249         private final BroadcastDispatcher mBroadcastDispatcher = Dependency.get(
250                 BroadcastDispatcher.class);
251 
252         @Override
253         public void onViewAttachedToWindow(View view) {
254             if (view == mLayout) {
255                 mBroadcastDispatcher.registerReceiver(mReceiver,
256                         new IntentFilter(Intent.ACTION_SCREEN_OFF));
257                 mLayoutAttachedToWindow = true;
258                 if (view.getTag().equals(R.string.recents_swipe_up_onboarding)) {
259                     mHasDismissedSwipeUpTip = false;
260                 } else {
261                     mHasDismissedQuickScrubTip = false;
262                 }
263             }
264         }
265 
266         @Override
267         public void onViewDetachedFromWindow(View view) {
268             if (view == mLayout) {
269                 mLayoutAttachedToWindow = false;
270                 if (view.getTag().equals(R.string.recents_quick_scrub_onboarding)) {
271                     mHasDismissedQuickScrubTip = true;
272                     if (hasDismissedQuickScrubOnboardingOnce()) {
273                         // If user dismisses the quick scrub tip twice, we consider user has seen it
274                         // and do not show it again.
275                         setHasSeenQuickScrubOnboarding(true);
276                     } else {
277                         setHasDismissedQuickScrubOnboardingOnce(true);
278                     }
279                     mOverviewOpenedCountSinceQuickScrubTipDismiss = 0;
280                 }
281                 mBroadcastDispatcher.unregisterReceiver(mReceiver);
282             }
283         }
284     };
285 
RecentsOnboarding(Context context, OverviewProxyService overviewProxyService)286     public RecentsOnboarding(Context context, OverviewProxyService overviewProxyService) {
287         mContext = context;
288         mOverviewProxyService = overviewProxyService;
289         final Resources res = context.getResources();
290         mWindowManager = (WindowManager) mContext.getSystemService(Context.WINDOW_SERVICE);
291         mBlacklistedPackages = new HashSet<>();
292         Collections.addAll(mBlacklistedPackages, res.getStringArray(
293                 R.array.recents_onboarding_blacklisted_packages));
294         mLayout = LayoutInflater.from(mContext).inflate(R.layout.recents_onboarding, null);
295         mTextView = mLayout.findViewById(R.id.onboarding_text);
296         mDismissView = mLayout.findViewById(R.id.dismiss);
297         mArrowView = mLayout.findViewById(R.id.arrow);
298 
299         TypedValue typedValue = new TypedValue();
300         context.getTheme().resolveAttribute(android.R.attr.colorAccent, typedValue, true);
301         mOnboardingToastColor = res.getColor(typedValue.resourceId);
302         mOnboardingToastArrowRadius = res.getDimensionPixelSize(
303                 R.dimen.recents_onboarding_toast_arrow_corner_radius);
304 
305         mLayout.addOnAttachStateChangeListener(mOnAttachStateChangeListener);
306         mDismissView.setOnClickListener(v -> {
307             hide(true);
308             if (v.getTag().equals(R.string.recents_swipe_up_onboarding)) {
309                 mHasDismissedSwipeUpTip = true;
310                 mNumAppsLaunchedSinceSwipeUpTipDismiss = 0;
311                 setDismissedSwipeUpOnboardingCount(getDismissedSwipeUpOnboardingCount() + 1);
312                 if (getDismissedSwipeUpOnboardingCount() > MAX_DISMISSAL_ON_SWIPE_UP_SHOW) {
313                     setHasSeenSwipeUpOnboarding(true);
314                 }
315                 notifyOnTip(DISMISS, RECENTS_SWIPE_UP_ONBOARDING_TIP);
316             } else {
317                 notifyOnTip(DISMISS, RECENTS_QUICK_SCRUB_ONBOARDING_TIP);
318             }
319         });
320 
321         ViewGroup.LayoutParams arrowLp = mArrowView.getLayoutParams();
322         ShapeDrawable arrowDrawable = new ShapeDrawable(TriangleShape.create(
323                 arrowLp.width, arrowLp.height, false));
324         Paint arrowPaint = arrowDrawable.getPaint();
325         arrowPaint.setColor(mOnboardingToastColor);
326         // The corner path effect won't be reflected in the shadow, but shouldn't be noticeable.
327         arrowPaint.setPathEffect(new CornerPathEffect(mOnboardingToastArrowRadius));
328         mArrowView.setBackground(arrowDrawable);
329 
330         if (RESET_PREFS_FOR_DEBUG) {
331             setHasSeenSwipeUpOnboarding(false);
332             setHasSeenQuickScrubOnboarding(false);
333             setDismissedSwipeUpOnboardingCount(0);
334             setHasDismissedQuickScrubOnboardingOnce(false);
335             setOpenedOverviewCount(0);
336             setOpenedOverviewFromHomeCount(0);
337         }
338     }
339 
notifyOnTip(int action, int target)340     private void notifyOnTip(int action, int target) {
341         try {
342             IOverviewProxy overviewProxy = mOverviewProxyService.getProxy();
343             if (overviewProxy != null) {
344                 overviewProxy.onTip(action, target);
345             }
346         } catch (RemoteException e) {
347         }
348     }
349 
onNavigationModeChanged(int mode)350     public void onNavigationModeChanged(int mode) {
351         mNavBarMode = mode;
352     }
353 
onConnectedToLauncher()354     public void onConnectedToLauncher() {
355         if (!ONBOARDING_ENABLED || QuickStepContract.isGesturalMode(mNavBarMode)) {
356             return;
357         }
358 
359         if (hasSeenSwipeUpOnboarding() && hasSeenQuickScrubOnboarding()) {
360             return;
361         }
362 
363         if (!mOverviewProxyListenerRegistered) {
364             mOverviewProxyService.addCallback(mOverviewProxyListener);
365             mOverviewProxyListenerRegistered = true;
366         }
367         if (!mTaskListenerRegistered) {
368             ActivityManagerWrapper.getInstance().registerTaskStackListener(mTaskListener);
369             mTaskListenerRegistered = true;
370         }
371     }
372 
onDisconnectedFromLauncher()373     public void onDisconnectedFromLauncher() {
374 
375         if (mOverviewProxyListenerRegistered) {
376             mOverviewProxyService.removeCallback(mOverviewProxyListener);
377             mOverviewProxyListenerRegistered = false;
378         }
379         if (mTaskListenerRegistered) {
380             ActivityManagerWrapper.getInstance().unregisterTaskStackListener(mTaskListener);
381             mTaskListenerRegistered = false;
382         }
383 
384         mHasDismissedSwipeUpTip = false;
385         mHasDismissedQuickScrubTip = false;
386         mNumAppsLaunchedSinceSwipeUpTipDismiss = 0;
387         mOverviewOpenedCountSinceQuickScrubTipDismiss = 0;
388         hide(true);
389     }
390 
onConfigurationChanged(Configuration newConfiguration)391     public void onConfigurationChanged(Configuration newConfiguration) {
392         if (newConfiguration.orientation != Configuration.ORIENTATION_PORTRAIT) {
393             hide(false);
394         }
395     }
396 
show(@tringRes int stringRes)397     public boolean show(@StringRes int stringRes) {
398         if (!shouldShow()) {
399             return false;
400         }
401         mDismissView.setTag(stringRes);
402         mLayout.setTag(stringRes);
403         mTextView.setText(stringRes);
404         // Only show in portrait.
405         int orientation = mContext.getResources().getConfiguration().orientation;
406         if (!mLayoutAttachedToWindow && orientation == Configuration.ORIENTATION_PORTRAIT) {
407             mLayout.setSystemUiVisibility(View.SYSTEM_UI_FLAG_LAYOUT_STABLE);
408 
409             final int gravity;
410             final int x;
411             if (stringRes == R.string.recents_swipe_up_onboarding) {
412                 gravity = Gravity.BOTTOM | Gravity.CENTER_HORIZONTAL;
413                 x = 0;
414             } else {
415                 int layoutDirection =
416                         mContext.getResources().getConfiguration().getLayoutDirection();
417                 gravity = Gravity.BOTTOM | (layoutDirection == View.LAYOUT_DIRECTION_LTR
418                         ? Gravity.LEFT : Gravity.RIGHT);
419                 x = mContext.getResources().getDimensionPixelSize(
420                         R.dimen.recents_quick_scrub_onboarding_margin_start);
421             }
422             mWindowManager.addView(mLayout, getWindowLayoutParams(gravity, x));
423             mLayout.setAlpha(0);
424             mLayout.animate()
425                     .alpha(1f)
426                     .withLayer()
427                     .setStartDelay(SHOW_DELAY_MS)
428                     .setDuration(SHOW_DURATION_MS)
429                     .setInterpolator(new DecelerateInterpolator())
430                     .start();
431             return true;
432         }
433         return false;
434     }
435 
436     /**
437      * @return True unless setprop has been set to false, we're in demo mode, or running tests in
438      * automation.
439      */
shouldShow()440     private boolean shouldShow() {
441         return SystemProperties.getBoolean("persist.quickstep.onboarding.enabled",
442                 !(mContext.getSystemService(UserManager.class)).isDemoUser() &&
443                         !ActivityManager.isRunningInTestHarness());
444     }
445 
hide(boolean animate)446     public void hide(boolean animate) {
447         if (mLayoutAttachedToWindow) {
448             if (animate) {
449                 mLayout.animate()
450                         .alpha(0f)
451                         .withLayer()
452                         .setStartDelay(0)
453                         .setDuration(HIDE_DURATION_MS)
454                         .setInterpolator(new AccelerateInterpolator())
455                         .withEndAction(() -> mWindowManager.removeViewImmediate(mLayout))
456                         .start();
457             } else {
458                 mLayout.animate().cancel();
459                 mWindowManager.removeViewImmediate(mLayout);
460             }
461         }
462     }
463 
setNavBarHeight(int navBarHeight)464     public void setNavBarHeight(int navBarHeight) {
465         mNavBarHeight = navBarHeight;
466     }
467 
dump(PrintWriter pw)468     public void dump(PrintWriter pw) {
469         pw.println("RecentsOnboarding {");
470         pw.println("      mTaskListenerRegistered: " + mTaskListenerRegistered);
471         pw.println("      mOverviewProxyListenerRegistered: " + mOverviewProxyListenerRegistered);
472         pw.println("      mLayoutAttachedToWindow: " + mLayoutAttachedToWindow);
473         pw.println("      mHasDismissedSwipeUpTip: " + mHasDismissedSwipeUpTip);
474         pw.println("      mHasDismissedQuickScrubTip: " + mHasDismissedQuickScrubTip);
475         pw.println("      mNumAppsLaunchedSinceSwipeUpTipDismiss: "
476                 + mNumAppsLaunchedSinceSwipeUpTipDismiss);
477         pw.println("      hasSeenSwipeUpOnboarding: " + hasSeenSwipeUpOnboarding());
478         pw.println("      hasSeenQuickScrubOnboarding: " + hasSeenQuickScrubOnboarding());
479         pw.println("      getDismissedSwipeUpOnboardingCount: "
480                 + getDismissedSwipeUpOnboardingCount());
481         pw.println("      hasDismissedQuickScrubOnboardingOnce: "
482                 + hasDismissedQuickScrubOnboardingOnce());
483         pw.println("      getOpenedOverviewCount: " + getOpenedOverviewCount());
484         pw.println("      getOpenedOverviewFromHomeCount: " + getOpenedOverviewFromHomeCount());
485         pw.println("    }");
486     }
487 
getWindowLayoutParams(int gravity, int x)488     private WindowManager.LayoutParams getWindowLayoutParams(int gravity, int x) {
489         int flags = WindowManager.LayoutParams.FLAG_LAYOUT_NO_LIMITS
490                 | WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE;
491         final WindowManager.LayoutParams lp = new WindowManager.LayoutParams(
492                 ViewGroup.LayoutParams.WRAP_CONTENT,
493                 ViewGroup.LayoutParams.WRAP_CONTENT,
494                 x, -mNavBarHeight / 2,
495                 WindowManager.LayoutParams.TYPE_APPLICATION_OVERLAY,
496                 flags,
497                 PixelFormat.TRANSLUCENT);
498         lp.privateFlags |= WindowManager.LayoutParams.SYSTEM_FLAG_SHOW_FOR_ALL_USERS;
499         lp.setTitle("RecentsOnboarding");
500         lp.gravity = gravity;
501         return lp;
502     }
503 
hasSeenSwipeUpOnboarding()504     private boolean hasSeenSwipeUpOnboarding() {
505         return Prefs.getBoolean(mContext, HAS_SEEN_RECENTS_SWIPE_UP_ONBOARDING, false);
506     }
507 
setHasSeenSwipeUpOnboarding(boolean hasSeenSwipeUpOnboarding)508     private void setHasSeenSwipeUpOnboarding(boolean hasSeenSwipeUpOnboarding) {
509         Prefs.putBoolean(mContext, HAS_SEEN_RECENTS_SWIPE_UP_ONBOARDING, hasSeenSwipeUpOnboarding);
510         if (hasSeenSwipeUpOnboarding && hasSeenQuickScrubOnboarding()) {
511             onDisconnectedFromLauncher();
512         }
513     }
514 
hasSeenQuickScrubOnboarding()515     private boolean hasSeenQuickScrubOnboarding() {
516         return Prefs.getBoolean(mContext, HAS_SEEN_RECENTS_QUICK_SCRUB_ONBOARDING, false);
517     }
518 
setHasSeenQuickScrubOnboarding(boolean hasSeenQuickScrubOnboarding)519     private void setHasSeenQuickScrubOnboarding(boolean hasSeenQuickScrubOnboarding) {
520         Prefs.putBoolean(mContext, HAS_SEEN_RECENTS_QUICK_SCRUB_ONBOARDING,
521                 hasSeenQuickScrubOnboarding);
522         if (hasSeenQuickScrubOnboarding && hasSeenSwipeUpOnboarding()) {
523             onDisconnectedFromLauncher();
524         }
525     }
526 
getDismissedSwipeUpOnboardingCount()527     private int getDismissedSwipeUpOnboardingCount() {
528         return Prefs.getInt(mContext, DISMISSED_RECENTS_SWIPE_UP_ONBOARDING_COUNT, 0);
529     }
530 
setDismissedSwipeUpOnboardingCount(int dismissedSwipeUpOnboardingCount)531     private void setDismissedSwipeUpOnboardingCount(int dismissedSwipeUpOnboardingCount) {
532         Prefs.putInt(mContext, DISMISSED_RECENTS_SWIPE_UP_ONBOARDING_COUNT,
533                 dismissedSwipeUpOnboardingCount);
534     }
535 
hasDismissedQuickScrubOnboardingOnce()536     private boolean hasDismissedQuickScrubOnboardingOnce() {
537         return Prefs.getBoolean(mContext, HAS_DISMISSED_RECENTS_QUICK_SCRUB_ONBOARDING_ONCE, false);
538     }
539 
setHasDismissedQuickScrubOnboardingOnce( boolean hasDismissedQuickScrubOnboardingOnce)540     private void setHasDismissedQuickScrubOnboardingOnce(
541             boolean hasDismissedQuickScrubOnboardingOnce) {
542         Prefs.putBoolean(mContext, HAS_DISMISSED_RECENTS_QUICK_SCRUB_ONBOARDING_ONCE,
543                 hasDismissedQuickScrubOnboardingOnce);
544     }
545 
getOpenedOverviewFromHomeCount()546     private int getOpenedOverviewFromHomeCount() {
547         return Prefs.getInt(mContext, OVERVIEW_OPENED_FROM_HOME_COUNT, 0);
548     }
549 
incrementOpenedOverviewFromHomeCount()550     private void incrementOpenedOverviewFromHomeCount() {
551         int openedOverviewFromHomeCount = getOpenedOverviewFromHomeCount();
552         if (openedOverviewFromHomeCount >= SWIPE_UP_SHOW_ON_OVERVIEW_OPENED_FROM_HOME_COUNT) {
553             return;
554         }
555         setOpenedOverviewFromHomeCount(openedOverviewFromHomeCount + 1);
556     }
557 
setOpenedOverviewFromHomeCount(int openedOverviewFromHomeCount)558     private void setOpenedOverviewFromHomeCount(int openedOverviewFromHomeCount) {
559         Prefs.putInt(mContext, OVERVIEW_OPENED_FROM_HOME_COUNT, openedOverviewFromHomeCount);
560     }
561 
getOpenedOverviewCount()562     private int getOpenedOverviewCount() {
563         return Prefs.getInt(mContext, OVERVIEW_OPENED_COUNT, 0);
564     }
565 
incrementOpenedOverviewCount()566     private void incrementOpenedOverviewCount() {
567         int openedOverviewCount = getOpenedOverviewCount();
568         if (openedOverviewCount >= QUICK_SCRUB_SHOW_ON_OVERVIEW_OPENED_COUNT) {
569             return;
570         }
571         setOpenedOverviewCount(openedOverviewCount + 1);
572     }
573 
setOpenedOverviewCount(int openedOverviewCount)574     private void setOpenedOverviewCount(int openedOverviewCount) {
575         Prefs.putInt(mContext, OVERVIEW_OPENED_COUNT, openedOverviewCount);
576     }
577 
578     private final BroadcastReceiver mReceiver = new BroadcastReceiver() {
579         @Override
580         public void onReceive(Context context, Intent intent) {
581             if (intent.getAction().equals(Intent.ACTION_SCREEN_OFF)) {
582                 hide(false);
583             }
584         }
585     };
586 }
587