• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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.launcher3.allapps;
18 
19 import static com.android.launcher3.LauncherState.NORMAL;
20 import static com.android.launcher3.util.OnboardingPrefs.HOME_BOUNCE_SEEN;
21 
22 import android.animation.Animator;
23 import android.animation.AnimatorInflater;
24 import android.os.Handler;
25 import android.os.UserManager;
26 import android.view.MotionEvent;
27 import android.view.View;
28 
29 import com.android.launcher3.AbstractFloatingView;
30 import com.android.launcher3.Launcher;
31 import com.android.launcher3.LauncherState;
32 import com.android.launcher3.R;
33 import com.android.launcher3.Utilities;
34 import com.android.launcher3.anim.AnimatorListeners;
35 import com.android.launcher3.statemanager.StateManager.StateListener;
36 import com.android.launcher3.util.OnboardingPrefs;
37 
38 /**
39  * Abstract base class of floating view responsible for showing discovery bounce animation
40  */
41 public class DiscoveryBounce extends AbstractFloatingView {
42 
43     private static final long DELAY_MS = 450;
44 
45     private final Launcher mLauncher;
46     private final Animator mDiscoBounceAnimation;
47 
48     private final StateListener<LauncherState> mStateListener = new StateListener<LauncherState>() {
49         @Override
50         public void onStateTransitionStart(LauncherState toState) {
51             handleClose(false);
52         }
53 
54         @Override
55         public void onStateTransitionComplete(LauncherState finalState) {}
56     };
57 
DiscoveryBounce(Launcher launcher)58     public DiscoveryBounce(Launcher launcher) {
59         super(launcher, null);
60         mLauncher = launcher;
61 
62         mDiscoBounceAnimation =
63                 AnimatorInflater.loadAnimator(launcher, R.animator.discovery_bounce);
64         mDiscoBounceAnimation.setTarget(new VerticalProgressWrapper(
65                 launcher.getHotseat(), mLauncher.getDragLayer().getHeight()));
66         mDiscoBounceAnimation.addListener(AnimatorListeners.forEndCallback(this::handleClose));
67         launcher.getStateManager().addStateListener(mStateListener);
68     }
69 
70     @Override
onAttachedToWindow()71     protected void onAttachedToWindow() {
72         super.onAttachedToWindow();
73         mDiscoBounceAnimation.start();
74     }
75 
76     @Override
onDetachedFromWindow()77     protected void onDetachedFromWindow() {
78         super.onDetachedFromWindow();
79         if (mDiscoBounceAnimation.isRunning()) {
80             mDiscoBounceAnimation.end();
81         }
82     }
83 
84     @Override
canHandleBack()85     public boolean canHandleBack() {
86         // Since DiscoveryBounce doesn't handle back, onBackInvoked() won't be called and we should
87         // close it without animation.
88         close(false);
89         return false;
90     }
91 
92     @Override
onControllerInterceptTouchEvent(MotionEvent ev)93     public boolean onControllerInterceptTouchEvent(MotionEvent ev) {
94         handleClose(false);
95         return false;
96     }
97 
98     @Override
handleClose(boolean animate)99     protected void handleClose(boolean animate) {
100         if (mIsOpen) {
101             mIsOpen = false;
102             mLauncher.getDragLayer().removeView(this);
103             // Reset the translation to what ever it was previously.
104             mLauncher.getHotseat().setTranslationY(mLauncher.getStateManager().getState()
105                     .getHotseatScaleAndTranslation(mLauncher).translationY);
106             mLauncher.getStateManager().removeStateListener(mStateListener);
107         }
108     }
109 
110     @Override
isOfType(int type)111     protected boolean isOfType(int type) {
112         return (type & TYPE_DISCOVERY_BOUNCE) != 0;
113     }
114 
show()115     private void show() {
116         mIsOpen = true;
117         mLauncher.getDragLayer().addView(this);
118         // TODO: add WW log for discovery bounce tip show event.
119     }
120 
showForHomeIfNeeded(Launcher launcher)121     public static void showForHomeIfNeeded(Launcher launcher) {
122         showForHomeIfNeeded(launcher, true);
123     }
124 
showForHomeIfNeeded(Launcher launcher, boolean withDelay)125     private static void showForHomeIfNeeded(Launcher launcher, boolean withDelay) {
126         if (!launcher.isInState(NORMAL)
127                 || HOME_BOUNCE_SEEN.get(launcher)
128                 || AbstractFloatingView.getTopOpenView(launcher) != null
129                 || launcher.getSystemService(UserManager.class).isDemoUser()
130                 || Utilities.isRunningInTestHarness()) {
131             return;
132         }
133 
134         if (withDelay) {
135             new Handler().postDelayed(() -> showForHomeIfNeeded(launcher, false), DELAY_MS);
136             return;
137         }
138         OnboardingPrefs.HOME_BOUNCE_COUNT.increment(launcher);
139         new DiscoveryBounce(launcher).show();
140     }
141 
142     /**
143      * A wrapper around hotseat animator allowing a fixed shift in the value.
144      */
145     public static class VerticalProgressWrapper {
146 
147         private final View mView;
148         private final float mLimit;
149 
VerticalProgressWrapper(View view, float limit)150         private VerticalProgressWrapper(View view, float limit) {
151             mView = view;
152             mLimit = limit;
153         }
154 
getProgress()155         public float getProgress() {
156             return 1 + mView.getTranslationY() / mLimit;
157         }
158 
setProgress(float progress)159         public void setProgress(float progress) {
160             mView.setTranslationY(mLimit * (progress - 1));
161         }
162     }
163 }
164