• 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.LauncherState.OVERVIEW;
21 import static com.android.launcher3.userevent.nano.LauncherLogProto.ContainerType.HOTSEAT;
22 import static com.android.launcher3.userevent.nano.LauncherLogProto.ContainerType.PREDICTION;
23 
24 import android.animation.Animator;
25 import android.animation.AnimatorInflater;
26 import android.animation.AnimatorListenerAdapter;
27 import android.app.ActivityManager;
28 import android.os.Handler;
29 import android.view.MotionEvent;
30 
31 import com.android.launcher3.AbstractFloatingView;
32 import com.android.launcher3.Launcher;
33 import com.android.launcher3.R;
34 import com.android.launcher3.compat.UserManagerCompat;
35 import com.android.launcher3.states.InternalStateHandler;
36 
37 /**
38  * Abstract base class of floating view responsible for showing discovery bounce animation
39  */
40 public class DiscoveryBounce extends AbstractFloatingView {
41 
42     private static final long DELAY_MS = 450;
43 
44     public static final String HOME_BOUNCE_SEEN = "launcher.apps_view_shown";
45     public static final String SHELF_BOUNCE_SEEN = "launcher.shelf_bounce_seen";
46 
47     private final Launcher mLauncher;
48     private final Animator mDiscoBounceAnimation;
49 
DiscoveryBounce(Launcher launcher, float delta)50     public DiscoveryBounce(Launcher launcher, float delta) {
51         super(launcher, null);
52         mLauncher = launcher;
53         AllAppsTransitionController controller = mLauncher.getAllAppsController();
54 
55         mDiscoBounceAnimation =
56                 AnimatorInflater.loadAnimator(launcher, R.animator.discovery_bounce);
57         mDiscoBounceAnimation.setTarget(new VerticalProgressWrapper(controller, delta));
58         mDiscoBounceAnimation.addListener(new AnimatorListenerAdapter() {
59             @Override
60             public void onAnimationEnd(Animator animation) {
61                 handleClose(false);
62             }
63         });
64         mDiscoBounceAnimation.addListener(controller.getProgressAnimatorListener());
65     }
66 
67     @Override
onAttachedToWindow()68     protected void onAttachedToWindow() {
69         super.onAttachedToWindow();
70         mDiscoBounceAnimation.start();
71     }
72 
73     @Override
onDetachedFromWindow()74     protected void onDetachedFromWindow() {
75         super.onDetachedFromWindow();
76         if (mDiscoBounceAnimation.isRunning()) {
77             mDiscoBounceAnimation.end();
78         }
79     }
80 
81     @Override
onBackPressed()82     public boolean onBackPressed() {
83         super.onBackPressed();
84         // Go back to the previous state (from a user's perspective this floating view isn't
85         // something to go back from).
86         return false;
87     }
88 
89     @Override
onControllerInterceptTouchEvent(MotionEvent ev)90     public boolean onControllerInterceptTouchEvent(MotionEvent ev) {
91         handleClose(false);
92         return false;
93     }
94 
95     @Override
handleClose(boolean animate)96     protected void handleClose(boolean animate) {
97         if (mIsOpen) {
98             mIsOpen = false;
99             mLauncher.getDragLayer().removeView(this);
100             // Reset the all-apps progress to what ever it was previously.
101             mLauncher.getAllAppsController().setProgress(mLauncher.getStateManager()
102                     .getState().getVerticalProgress(mLauncher));
103         }
104     }
105 
106     @Override
logActionCommand(int command)107     public void logActionCommand(int command) {
108         // Since this is on-boarding popup, it is not a user controlled action.
109     }
110 
111     @Override
isOfType(int type)112     protected boolean isOfType(int type) {
113         return (type & TYPE_DISCOVERY_BOUNCE) != 0;
114     }
115 
show(int containerType)116     private void show(int containerType) {
117         mIsOpen = true;
118         mLauncher.getDragLayer().addView(this);
119         mLauncher.getUserEventDispatcher().logActionBounceTip(containerType);
120     }
121 
showForHomeIfNeeded(Launcher launcher)122     public static void showForHomeIfNeeded(Launcher launcher) {
123         showForHomeIfNeeded(launcher, true);
124     }
125 
showForHomeIfNeeded(Launcher launcher, boolean withDelay)126     private static void showForHomeIfNeeded(Launcher launcher, boolean withDelay) {
127         if (!launcher.isInState(NORMAL)
128                 || launcher.getSharedPrefs().getBoolean(HOME_BOUNCE_SEEN, false)
129                 || AbstractFloatingView.getTopOpenView(launcher) != null
130                 || UserManagerCompat.getInstance(launcher).isDemoUser()
131                 || ActivityManager.isRunningInTestHarness()) {
132             return;
133         }
134 
135         if (withDelay) {
136             new Handler().postDelayed(() -> showForHomeIfNeeded(launcher, false), DELAY_MS);
137             return;
138         }
139 
140         new DiscoveryBounce(launcher, 0).show(HOTSEAT);
141     }
142 
showForOverviewIfNeeded(Launcher launcher)143     public static void showForOverviewIfNeeded(Launcher launcher) {
144         showForOverviewIfNeeded(launcher, true);
145     }
146 
showForOverviewIfNeeded(Launcher launcher, boolean withDelay)147     private static void showForOverviewIfNeeded(Launcher launcher, boolean withDelay) {
148         if (!launcher.isInState(OVERVIEW)
149                 || !launcher.hasBeenResumed()
150                 || launcher.isForceInvisible()
151                 || launcher.getDeviceProfile().isVerticalBarLayout()
152                 || launcher.getSharedPrefs().getBoolean(SHELF_BOUNCE_SEEN, false)
153                 || UserManagerCompat.getInstance(launcher).isDemoUser()
154                 || ActivityManager.isRunningInTestHarness()) {
155             return;
156         }
157 
158         if (withDelay) {
159             new Handler().postDelayed(() -> showForOverviewIfNeeded(launcher, false), DELAY_MS);
160             return;
161         } else if (InternalStateHandler.hasPending()
162                 || AbstractFloatingView.getTopOpenView(launcher) != null) {
163             // TODO: Move these checks to the top and call this method after invalidate handler.
164             return;
165         }
166 
167         new DiscoveryBounce(launcher, (1 - OVERVIEW.getVerticalProgress(launcher)))
168                 .show(PREDICTION);
169     }
170 
171     /**
172      * A wrapper around {@link AllAppsTransitionController} allowing a fixed shift in the value.
173      */
174     public static class VerticalProgressWrapper {
175 
176         private final float mDelta;
177         private final AllAppsTransitionController mController;
178 
VerticalProgressWrapper(AllAppsTransitionController controller, float delta)179         private VerticalProgressWrapper(AllAppsTransitionController controller, float delta) {
180             mController = controller;
181             mDelta = delta;
182         }
183 
getProgress()184         public float getProgress() {
185             return mController.getProgress() + mDelta;
186         }
187 
setProgress(float progress)188         public void setProgress(float progress) {
189             mController.setProgress(progress - mDelta);
190         }
191     }
192 }
193