blob: 9db371e487c7e4aa16a546f9df93739b0f66e51f [file] [log] [blame]
Joshua Tsuji4accf5982019-04-22 17:36:11 -04001/*
2 * Copyright (C) 2019 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
17package com.android.systemui.bubbles;
18
Joshua Tsuji4accf5982019-04-22 17:36:11 -040019import android.content.Context;
Joshua Tsuji4accf5982019-04-22 17:36:11 -040020import android.view.LayoutInflater;
21import android.view.View;
22import android.view.animation.AccelerateDecelerateInterpolator;
23import android.widget.FrameLayout;
24import android.widget.ImageView;
25import android.widget.LinearLayout;
Joshua Tsuji4accf5982019-04-22 17:36:11 -040026
27import androidx.dynamicanimation.animation.DynamicAnimation;
28import androidx.dynamicanimation.animation.SpringAnimation;
29import androidx.dynamicanimation.animation.SpringForce;
30
31import com.android.systemui.R;
32
33/** Dismiss view that contains a scrim gradient, as well as a dismiss icon, text, and circle. */
34public class BubbleDismissView extends FrameLayout {
Lyn Han634483c2019-06-28 16:52:47 -070035 /** Duration for animations involving the dismiss target text/icon. */
Joshua Tsuji4accf5982019-04-22 17:36:11 -040036 private static final int DISMISS_TARGET_ANIMATION_BASE_DURATION = 150;
Lyn Han634483c2019-06-28 16:52:47 -070037 private static final float SCALE_FOR_POP = 1.2f;
38 private static final float SCALE_FOR_DISMISS = 0.9f;
Joshua Tsuji4accf5982019-04-22 17:36:11 -040039
40 private LinearLayout mDismissTarget;
41 private ImageView mDismissIcon;
Joshua Tsuji4accf5982019-04-22 17:36:11 -040042 private View mDismissCircle;
43
44 private SpringAnimation mDismissTargetAlphaSpring;
45 private SpringAnimation mDismissTargetVerticalSpring;
46
47 public BubbleDismissView(Context context) {
48 super(context);
49 setVisibility(GONE);
50
Joshua Tsuji4accf5982019-04-22 17:36:11 -040051 LayoutInflater.from(context).inflate(R.layout.bubble_dismiss_target, this, true);
52 mDismissTarget = findViewById(R.id.bubble_dismiss_icon_container);
53 mDismissIcon = findViewById(R.id.bubble_dismiss_close_icon);
Joshua Tsuji4accf5982019-04-22 17:36:11 -040054 mDismissCircle = findViewById(R.id.bubble_dismiss_circle);
55
56 // Set up the basic target area animations. These are very simple animations that don't need
57 // fancy interpolators.
58 final AccelerateDecelerateInterpolator interpolator =
59 new AccelerateDecelerateInterpolator();
Joshua Tsuji4accf5982019-04-22 17:36:11 -040060 mDismissIcon.animate()
61 .setDuration(DISMISS_TARGET_ANIMATION_BASE_DURATION)
62 .setInterpolator(interpolator);
63 mDismissCircle.animate()
64 .setDuration(DISMISS_TARGET_ANIMATION_BASE_DURATION / 2)
65 .setInterpolator(interpolator);
66
67 mDismissTargetAlphaSpring =
68 new SpringAnimation(mDismissTarget, DynamicAnimation.ALPHA)
69 .setSpring(new SpringForce()
70 .setStiffness(SpringForce.STIFFNESS_LOW)
71 .setDampingRatio(SpringForce.DAMPING_RATIO_LOW_BOUNCY));
72 mDismissTargetVerticalSpring =
73 new SpringAnimation(mDismissTarget, DynamicAnimation.TRANSLATION_Y)
74 .setSpring(new SpringForce()
75 .setStiffness(SpringForce.STIFFNESS_MEDIUM)
76 .setDampingRatio(SpringForce.DAMPING_RATIO_LOW_BOUNCY));
77
78 mDismissTargetAlphaSpring.addEndListener((anim, canceled, alpha, velocity) -> {
79 // Since DynamicAnimations end when they're 'nearly' done, we can't rely on alpha being
80 // exactly zero when this listener is triggered. However, if it's less than 50% we can
81 // safely assume it was animating out rather than in.
82 if (alpha < 0.5f) {
83 // If the alpha spring was animating the view out, set it to GONE when it's done.
Mady Mellor44977c02019-07-26 15:11:03 -070084 setVisibility(INVISIBLE);
Joshua Tsuji4accf5982019-04-22 17:36:11 -040085 }
86 });
87 }
88
Lyn Han634483c2019-06-28 16:52:47 -070089 /** Springs in the dismiss target. */
Joshua Tsuji4accf5982019-04-22 17:36:11 -040090 void springIn() {
91 setVisibility(View.VISIBLE);
92
Lyn Han634483c2019-06-28 16:52:47 -070093 // Fade in the dismiss target icon.
94 mDismissIcon.animate()
95 .setDuration(50)
96 .scaleX(1f)
97 .scaleY(1f)
98 .alpha(1f);
Joshua Tsuji4accf5982019-04-22 17:36:11 -040099 mDismissTarget.setAlpha(0f);
100 mDismissTargetAlphaSpring.animateToFinalPosition(1f);
101
Lyn Han634483c2019-06-28 16:52:47 -0700102 // Spring up the dismiss target.
Joshua Tsuji4accf5982019-04-22 17:36:11 -0400103 mDismissTarget.setTranslationY(mDismissTarget.getHeight() / 2f);
104 mDismissTargetVerticalSpring.animateToFinalPosition(0);
105
Lyn Han634483c2019-06-28 16:52:47 -0700106 mDismissCircle.setAlpha(0f);
107 mDismissCircle.setScaleX(SCALE_FOR_POP);
108 mDismissCircle.setScaleY(SCALE_FOR_POP);
Joshua Tsuji4accf5982019-04-22 17:36:11 -0400109
Lyn Han634483c2019-06-28 16:52:47 -0700110 // Fade in circle and reduce size.
111 mDismissCircle.animate()
112 .alpha(1f)
113 .scaleX(1f)
114 .scaleY(1f);
Joshua Tsuji4accf5982019-04-22 17:36:11 -0400115 }
116
Lyn Han634483c2019-06-28 16:52:47 -0700117 /** Springs out the dismiss target. */
Joshua Tsuji4accf5982019-04-22 17:36:11 -0400118 void springOut() {
Lyn Han634483c2019-06-28 16:52:47 -0700119 // Fade out the target icon.
120 mDismissIcon.animate()
121 .setDuration(50)
122 .scaleX(SCALE_FOR_DISMISS)
123 .scaleY(SCALE_FOR_DISMISS)
124 .alpha(0f);
125
Joshua Tsuji4accf5982019-04-22 17:36:11 -0400126 // Fade out the target.
127 mDismissTargetAlphaSpring.animateToFinalPosition(0f);
128
129 // Spring the target down a bit.
130 mDismissTargetVerticalSpring.animateToFinalPosition(mDismissTarget.getHeight() / 2f);
131
Lyn Han634483c2019-06-28 16:52:47 -0700132 // Pop out the circle.
Joshua Tsuji4accf5982019-04-22 17:36:11 -0400133 mDismissCircle.animate()
Lyn Han634483c2019-06-28 16:52:47 -0700134 .scaleX(SCALE_FOR_DISMISS)
135 .scaleY(SCALE_FOR_DISMISS)
Joshua Tsuji4accf5982019-04-22 17:36:11 -0400136 .alpha(0f);
137 }
138
139 /** Returns the Y value of the center of the dismiss target. */
140 float getDismissTargetCenterY() {
141 return getTop() + mDismissTarget.getTop() + mDismissTarget.getHeight() / 2f;
142 }
143
144 /** Returns the dismiss target, which contains the text/icon and any added padding. */
145 View getDismissTarget() {
146 return mDismissTarget;
147 }
148}