Home | History | Annotate | Download | only in allapps
      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 
     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
     68     protected void onAttachedToWindow() {
     69         super.onAttachedToWindow();
     70         mDiscoBounceAnimation.start();
     71     }
     72 
     73     @Override
     74     protected void onDetachedFromWindow() {
     75         super.onDetachedFromWindow();
     76         if (mDiscoBounceAnimation.isRunning()) {
     77             mDiscoBounceAnimation.end();
     78         }
     79     }
     80 
     81     @Override
     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
     90     public boolean onControllerInterceptTouchEvent(MotionEvent ev) {
     91         handleClose(false);
     92         return false;
     93     }
     94 
     95     @Override
     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
    107     public void logActionCommand(int command) {
    108         // Since this is on-boarding popup, it is not a user controlled action.
    109     }
    110 
    111     @Override
    112     protected boolean isOfType(int type) {
    113         return (type & TYPE_DISCOVERY_BOUNCE) != 0;
    114     }
    115 
    116     private void show(int containerType) {
    117         mIsOpen = true;
    118         mLauncher.getDragLayer().addView(this);
    119         mLauncher.getUserEventDispatcher().logActionBounceTip(containerType);
    120     }
    121 
    122     public static void showForHomeIfNeeded(Launcher launcher) {
    123         showForHomeIfNeeded(launcher, true);
    124     }
    125 
    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 
    143     public static void showForOverviewIfNeeded(Launcher launcher) {
    144         showForOverviewIfNeeded(launcher, true);
    145     }
    146 
    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 
    179         private VerticalProgressWrapper(AllAppsTransitionController controller, float delta) {
    180             mController = controller;
    181             mDelta = delta;
    182         }
    183 
    184         public float getProgress() {
    185             return mController.getProgress() + mDelta;
    186         }
    187 
    188         public void setProgress(float progress) {
    189             mController.setProgress(progress - mDelta);
    190         }
    191     }
    192 }
    193