• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2020 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.wm.shell.bubbles
18 
19 import android.animation.ObjectAnimator
20 import android.content.Context
21 import android.graphics.Color
22 import android.graphics.drawable.GradientDrawable
23 import android.util.IntProperty
24 import android.view.Gravity
25 import android.view.View
26 import android.view.ViewGroup
27 import android.view.WindowInsets
28 import android.view.WindowManager
29 import android.widget.FrameLayout
30 import androidx.dynamicanimation.animation.DynamicAnimation
31 import androidx.dynamicanimation.animation.SpringForce.DAMPING_RATIO_LOW_BOUNCY
32 import androidx.dynamicanimation.animation.SpringForce.STIFFNESS_LOW
33 import com.android.wm.shell.R
34 import com.android.wm.shell.animation.PhysicsAnimator
35 import com.android.wm.shell.common.DismissCircleView
36 
37 /*
38  * View that handles interactions between DismissCircleView and BubbleStackView.
39  */
40 class DismissView(context: Context) : FrameLayout(context) {
41 
42     var circle = DismissCircleView(context)
43     var isShowing = false
44     var targetSizeResId: Int
45 
46     private val animator = PhysicsAnimator.getInstance(circle)
47     private val spring = PhysicsAnimator.SpringConfig(STIFFNESS_LOW, DAMPING_RATIO_LOW_BOUNCY)
48     private val DISMISS_SCRIM_FADE_MS = 200L
49     private var wm: WindowManager =
50             context.getSystemService(Context.WINDOW_SERVICE) as WindowManager
51     private var gradientDrawable = createGradient()
52 
53     private val GRADIENT_ALPHA: IntProperty<GradientDrawable> =
54             object : IntProperty<GradientDrawable>("alpha") {
setValuenull55         override fun setValue(d: GradientDrawable, percent: Int) {
56             d.alpha = percent
57         }
getnull58         override fun get(d: GradientDrawable): Int {
59             return d.alpha
60         }
61     }
62 
63     init {
64         setLayoutParams(LayoutParams(
65             ViewGroup.LayoutParams.MATCH_PARENT,
66             resources.getDimensionPixelSize(R.dimen.floating_dismiss_gradient_height),
67             Gravity.BOTTOM))
68         updatePadding()
69         setClipToPadding(false)
70         setClipChildren(false)
71         setVisibility(View.INVISIBLE)
72         setBackgroundDrawable(gradientDrawable)
73 
74         targetSizeResId = R.dimen.dismiss_circle_size
75         val targetSize: Int = resources.getDimensionPixelSize(targetSizeResId)
76         addView(circle, LayoutParams(targetSize, targetSize,
77                 Gravity.BOTTOM or Gravity.CENTER_HORIZONTAL))
78         // start with circle offscreen so it's animated up
79         circle.setTranslationY(resources.getDimensionPixelSize(
80                 R.dimen.floating_dismiss_gradient_height).toFloat())
81     }
82 
83     /**
84      * Animates this view in.
85      */
shownull86     fun show() {
87         if (isShowing) return
88         isShowing = true
89         setVisibility(View.VISIBLE)
90         val alphaAnim = ObjectAnimator.ofInt(gradientDrawable, GRADIENT_ALPHA,
91                 gradientDrawable.alpha, 255)
92         alphaAnim.setDuration(DISMISS_SCRIM_FADE_MS)
93         alphaAnim.start()
94 
95         animator.cancel()
96         animator
97             .spring(DynamicAnimation.TRANSLATION_Y, 0f, spring)
98             .start()
99     }
100 
101     /**
102      * Animates this view out, as well as the circle that encircles the bubbles, if they
103      * were dragged into the target and encircled.
104      */
hidenull105     fun hide() {
106         if (!isShowing) return
107         isShowing = false
108         val alphaAnim = ObjectAnimator.ofInt(gradientDrawable, GRADIENT_ALPHA,
109                 gradientDrawable.alpha, 0)
110         alphaAnim.setDuration(DISMISS_SCRIM_FADE_MS)
111         alphaAnim.start()
112         animator
113             .spring(DynamicAnimation.TRANSLATION_Y, height.toFloat(),
114                 spring)
115             .withEndActions({ setVisibility(View.INVISIBLE) })
116             .start()
117     }
118 
119     /**
120      * Cancels the animator for the dismiss target.
121      */
cancelAnimatorsnull122     fun cancelAnimators() {
123         animator.cancel()
124     }
125 
updateResourcesnull126     fun updateResources() {
127         updatePadding()
128         layoutParams.height = resources.getDimensionPixelSize(
129                 R.dimen.floating_dismiss_gradient_height)
130 
131         val targetSize = resources.getDimensionPixelSize(targetSizeResId)
132         circle.layoutParams.width = targetSize
133         circle.layoutParams.height = targetSize
134         circle.requestLayout()
135     }
136 
createGradientnull137     private fun createGradient(): GradientDrawable {
138         val gradientColor = context.resources.getColor(android.R.color.system_neutral1_900)
139         val alpha = 0.7f * 255
140         val gradientColorWithAlpha = Color.argb(alpha.toInt(),
141                 Color.red(gradientColor),
142                 Color.green(gradientColor),
143                 Color.blue(gradientColor))
144         val gd = GradientDrawable(
145                 GradientDrawable.Orientation.BOTTOM_TOP,
146                 intArrayOf(gradientColorWithAlpha, Color.TRANSPARENT))
147         gd.setAlpha(0)
148         return gd
149     }
150 
updatePaddingnull151     private fun updatePadding() {
152         val insets: WindowInsets = wm.getCurrentWindowMetrics().getWindowInsets()
153         val navInset = insets.getInsetsIgnoringVisibility(
154                 WindowInsets.Type.navigationBars())
155         setPadding(0, 0, 0, navInset.bottom +
156                 resources.getDimensionPixelSize(R.dimen.floating_dismiss_bottom_margin))
157     }
158 }
159