• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2021 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.systemui.statusbar.events
18 
19 import android.animation.ValueAnimator
20 import android.content.Context
21 import android.view.Gravity
22 import android.view.LayoutInflater
23 import android.view.View
24 import android.view.ViewGroup.LayoutParams.MATCH_PARENT
25 import android.view.ViewGroup.LayoutParams.WRAP_CONTENT
26 import android.widget.FrameLayout
27 
28 import com.android.systemui.R
29 import com.android.systemui.statusbar.SuperStatusBarViewFactory
30 import com.android.systemui.statusbar.phone.StatusBarLocationPublisher
31 import com.android.systemui.statusbar.phone.StatusBarWindowController
32 import com.android.systemui.statusbar.phone.StatusBarWindowView
33 
34 import javax.inject.Inject
35 
36 /**
37  * Controls the view for system event animations.
38  */
39 class SystemEventChipAnimationController @Inject constructor(
40     private val context: Context,
41     private val statusBarViewFactory: SuperStatusBarViewFactory,
42     private val statusBarWindowController: StatusBarWindowController,
43     private val locationPublisher: StatusBarLocationPublisher
44 ) : SystemStatusChipAnimationCallback {
45     var showPersistentDot = false
46         set(value) {
47             field = value
48             statusBarWindowController.setForceStatusBarVisible(value)
49             maybeUpdateShowDot()
50         }
51 
52     private lateinit var animationWindowView: FrameLayout
53     private lateinit var animationDotView: View
54     private lateinit var statusBarWindowView: StatusBarWindowView
55     private var currentAnimatedView: View? = null
56 
57     // TODO: move to dagger
58     private var initialized = false
59 
onChipAnimationStartnull60     override fun onChipAnimationStart(
61         viewCreator: (context: Context) -> View,
62         @SystemAnimationState state: Int
63     ) {
64         if (!initialized) init()
65 
66         if (state == ANIMATING_IN) {
67             currentAnimatedView = viewCreator(context)
68             animationWindowView.addView(currentAnimatedView, layoutParamsDefault())
69 
70             // We are animating IN; chip comes in from View.END
71             currentAnimatedView?.apply {
72                 val translation = width.toFloat()
73                 translationX = if (isLayoutRtl) -translation else translation
74                 alpha = 0f
75                 visibility = View.VISIBLE
76                 setPadding(locationPublisher.marginLeft, 0, locationPublisher.marginRight, 0)
77             }
78         } else {
79             // We are animating away
80             currentAnimatedView?.apply {
81                 translationX = 0f
82                 alpha = 1f
83             }
84         }
85     }
86 
onChipAnimationEndnull87     override fun onChipAnimationEnd(@SystemAnimationState state: Int) {
88         if (state == ANIMATING_IN) {
89             // Finished animating in
90             currentAnimatedView?.apply {
91                 translationX = 0f
92                 alpha = 1f
93             }
94         } else {
95             // Finished animating away
96             currentAnimatedView?.apply {
97                 visibility = View.INVISIBLE
98             }
99             animationWindowView.removeView(currentAnimatedView)
100         }
101     }
102 
onChipAnimationUpdatenull103     override fun onChipAnimationUpdate(
104         animator: ValueAnimator,
105         @SystemAnimationState state: Int
106     ) {
107         // Alpha is parameterized 0,1, and translation from (width, 0)
108         currentAnimatedView?.apply {
109             val amt = animator.animatedValue as Float
110 
111             alpha = amt
112 
113             val w = width
114             val translation = (1 - amt) * w
115             translationX = if (isLayoutRtl) -translation else translation
116         }
117     }
118 
maybeUpdateShowDotnull119     private fun maybeUpdateShowDot() {
120         if (!initialized) return
121         if (!showPersistentDot && currentAnimatedView == null) {
122             animationDotView.visibility = View.INVISIBLE
123         }
124     }
125 
initnull126     private fun init() {
127         initialized = true
128         statusBarWindowView = statusBarViewFactory.statusBarWindowView
129         animationWindowView = LayoutInflater.from(context)
130                 .inflate(R.layout.system_event_animation_window, null) as FrameLayout
131         animationDotView = animationWindowView.findViewById(R.id.dot_view)
132         val lp = FrameLayout.LayoutParams(MATCH_PARENT, MATCH_PARENT)
133         lp.gravity = Gravity.END or Gravity.CENTER_VERTICAL
134         statusBarWindowView.addView(animationWindowView, lp)
135     }
136 
startnull137     private fun start() = if (animationWindowView.isLayoutRtl) right() else left()
138     private fun right() = locationPublisher.marginRight
139     private fun left() = locationPublisher.marginLeft
140 
141     private fun layoutParamsDefault(): FrameLayout.LayoutParams =
142         FrameLayout.LayoutParams(WRAP_CONTENT, WRAP_CONTENT).also {
143             it.gravity = Gravity.END or Gravity.CENTER_VERTICAL
144             it.marginStart = start()
145     }
146 }
147