• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2023 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 package com.android.quickstep.views;
17 
18 import static com.android.app.animation.Interpolators.EMPHASIZED_DECELERATE;
19 import static com.android.app.animation.Interpolators.LINEAR;
20 
21 import android.animation.Animator;
22 import android.animation.AnimatorListenerAdapter;
23 import android.animation.AnimatorSet;
24 import android.animation.ObjectAnimator;
25 import android.animation.ValueAnimator;
26 import android.content.Context;
27 import android.util.AttributeSet;
28 import android.view.View;
29 import android.widget.LinearLayout;
30 
31 import androidx.annotation.Nullable;
32 
33 import com.android.launcher3.Launcher;
34 import com.android.launcher3.R;
35 import com.android.launcher3.Utilities;
36 
37 /**
38  * Floating view show on launcher home screen that notifies the user that an app will be launched to
39  * the desktop.
40  */
41 public class DesktopAppSelectView extends LinearLayout {
42 
43     private static final int SHOW_INITIAL_HEIGHT_DP = 7;
44     private static final int SHOW_CONTAINER_SCALE_DURATION = 333;
45     private static final int SHOW_CONTAINER_ALPHA_DURATION = 83;
46     private static final int SHOW_CONTENT_ALPHA_DELAY = 67;
47     private static final int SHOW_CONTENT_ALPHA_DURATION = 83;
48     private static final int HIDE_DURATION = 83;
49 
50     private final Launcher mLauncher;
51 
52     private View mText;
53     private View mCloseButton;
54     @Nullable
55     private Runnable mOnCloseCallback;
56     private AnimatorSet mShowAnimation;
57     private Animator mHideAnimation;
58 
DesktopAppSelectView(Context context)59     public DesktopAppSelectView(Context context) {
60         this(context, null);
61     }
62 
DesktopAppSelectView(Context context, AttributeSet attrs)63     public DesktopAppSelectView(Context context, AttributeSet attrs) {
64         this(context, attrs, 0);
65     }
66 
DesktopAppSelectView(Context context, AttributeSet attrs, int defStyleAttr)67     public DesktopAppSelectView(Context context, AttributeSet attrs, int defStyleAttr) {
68         this(context, attrs, defStyleAttr, 0);
69     }
70 
DesktopAppSelectView(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes)71     public DesktopAppSelectView(Context context, AttributeSet attrs, int defStyleAttr,
72             int defStyleRes) {
73         super(context, attrs, defStyleAttr, defStyleRes);
74         mLauncher = Launcher.getLauncher(context);
75     }
76 
77     /**
78      * Show the popup on launcher home screen
79      *
80      * @param onCloseCallback optional callback that is called when user clicks the close button
81      * @return the created view
82      */
show(Launcher launcher, @Nullable Runnable onCloseCallback)83     public static DesktopAppSelectView show(Launcher launcher, @Nullable Runnable onCloseCallback) {
84         DesktopAppSelectView view = (DesktopAppSelectView) launcher.getLayoutInflater().inflate(
85                 R.layout.floating_desktop_app_select, launcher.getDragLayer(), false);
86         view.setOnCloseClickCallback(onCloseCallback);
87         view.show();
88         return view;
89     }
90 
91     @Override
onFinishInflate()92     protected void onFinishInflate() {
93         super.onFinishInflate();
94         mText = findViewById(R.id.desktop_app_select_text);
95         mCloseButton = findViewById(R.id.close_button);
96         mCloseButton.setOnClickListener(v -> {
97             if (mHideAnimation == null) {
98                 hide();
99                 if (mOnCloseCallback != null) {
100                     mOnCloseCallback.run();
101                 }
102             }
103         });
104     }
105 
show()106     private void show() {
107         mLauncher.getDragLayer().addView(this);
108 
109         // Set up initial values
110         getBackground().setAlpha(0);
111         mText.setAlpha(0);
112         mCloseButton.setAlpha(0);
113         int initialHeightPx = Utilities.dpToPx(SHOW_INITIAL_HEIGHT_DP);
114         int finalHeight = getResources().getDimensionPixelSize(
115                 R.dimen.desktop_mode_floating_app_select_height);
116         float initialScale = initialHeightPx / (float) finalHeight;
117         setScaleY(initialScale);
118         setPivotY(0);
119 
120         // Animate the container
121         ValueAnimator containerBackground = ValueAnimator.ofInt(0, 255);
122         containerBackground.addUpdateListener(
123                 animation -> getBackground().setAlpha((Integer) animation.getAnimatedValue()));
124         containerBackground.setDuration(SHOW_CONTAINER_ALPHA_DURATION);
125         containerBackground.setInterpolator(LINEAR);
126 
127         ObjectAnimator containerSize = ObjectAnimator.ofFloat(this, SCALE_Y, 1f);
128         containerSize.setDuration(SHOW_CONTAINER_SCALE_DURATION);
129         containerSize.setInterpolator(EMPHASIZED_DECELERATE);
130 
131         // Animate the contents
132         ObjectAnimator textAlpha = ObjectAnimator.ofFloat(mText, ALPHA, 1);
133         ObjectAnimator buttonAlpha = ObjectAnimator.ofFloat(mCloseButton, ALPHA, 1);
134         AnimatorSet contentAlpha = new AnimatorSet();
135         contentAlpha.playTogether(textAlpha, buttonAlpha);
136         contentAlpha.setStartDelay(SHOW_CONTENT_ALPHA_DELAY);
137         contentAlpha.setDuration(SHOW_CONTENT_ALPHA_DURATION);
138         contentAlpha.setInterpolator(LINEAR);
139 
140         // Start the animation
141         mShowAnimation = new AnimatorSet();
142         mShowAnimation.addListener(new AnimatorListenerAdapter() {
143             @Override
144             public void onAnimationEnd(Animator animation) {
145                 super.onAnimationEnd(animation);
146                 mShowAnimation = null;
147             }
148         });
149         mShowAnimation.playTogether(containerBackground, containerSize, contentAlpha);
150         mShowAnimation.start();
151     }
152 
153     /**
154      * Hide the floating view
155      */
hide()156     public void hide() {
157         if (mShowAnimation != null) {
158             mShowAnimation.cancel();
159         }
160         mHideAnimation = ObjectAnimator.ofFloat(this, ALPHA, 0);
161         mHideAnimation.setDuration(HIDE_DURATION).setInterpolator(LINEAR);
162         mHideAnimation.addListener(new AnimatorListenerAdapter() {
163             @Override
164             public void onAnimationEnd(Animator animation) {
165                 super.onAnimationEnd(animation);
166                 mLauncher.getDragLayer().removeView(DesktopAppSelectView.this);
167                 mHideAnimation = null;
168             }
169         });
170         mHideAnimation.start();
171     }
172 
173     /**
174      * Add a callback that is called when close button is clicked
175      */
setOnCloseClickCallback(@ullable Runnable callback)176     public void setOnCloseClickCallback(@Nullable Runnable callback) {
177         mOnCloseCallback = callback;
178     }
179 }
180