• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
<lambda>null2  * 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.charging
18 
19 import android.animation.Animator
20 import android.animation.AnimatorListenerAdapter
21 import android.animation.ValueAnimator
22 import android.content.Context
23 import android.content.res.Configuration
24 import android.graphics.Canvas
25 import android.graphics.Paint
26 import android.graphics.PointF
27 import android.util.AttributeSet
28 import android.view.View
29 
30 private const val RIPPLE_SPARKLE_STRENGTH: Float = 0.3f
31 
32 /**
33  * Expanding ripple effect that shows when charging begins.
34  */
35 class ChargingRippleView(context: Context?, attrs: AttributeSet?) : View(context, attrs) {
36     private val rippleShader = RippleShader()
37     private val defaultColor: Int = 0xffffffff.toInt()
38     private val ripplePaint = Paint()
39 
40     var rippleInProgress: Boolean = false
41     var radius: Float = 0.0f
42         set(value) {
43             rippleShader.radius = value
44             field = value
45         }
46     var origin: PointF = PointF()
47         set(value) {
48             rippleShader.origin = value
49             field = value
50         }
51     var duration: Long = 1750
52 
53     init {
54         rippleShader.color = defaultColor
55         rippleShader.progress = 0f
56         rippleShader.sparkleStrength = RIPPLE_SPARKLE_STRENGTH
57         ripplePaint.shader = rippleShader
58         visibility = View.GONE
59     }
60 
61     override fun onConfigurationChanged(newConfig: Configuration?) {
62         rippleShader.pixelDensity = resources.displayMetrics.density
63         super.onConfigurationChanged(newConfig)
64     }
65 
66     override fun onAttachedToWindow() {
67         rippleShader.pixelDensity = resources.displayMetrics.density
68         super.onAttachedToWindow()
69     }
70 
71     @JvmOverloads
72     fun startRipple(onAnimationEnd: Runnable? = null) {
73         if (rippleInProgress) {
74             return // Ignore if ripple effect is already playing
75         }
76         val animator = ValueAnimator.ofFloat(0f, 1f)
77         animator.duration = duration
78         animator.addUpdateListener { animator ->
79             val now = animator.currentPlayTime
80             val progress = animator.animatedValue as Float
81             rippleShader.progress = progress
82             rippleShader.distortionStrength = 1 - progress
83             rippleShader.time = now.toFloat()
84             invalidate()
85         }
86         animator.addListener(object : AnimatorListenerAdapter() {
87             override fun onAnimationEnd(animation: Animator?) {
88                 rippleInProgress = false
89                 visibility = View.GONE
90                 onAnimationEnd?.run()
91             }
92         })
93         animator.start()
94         visibility = View.VISIBLE
95         rippleInProgress = true
96     }
97 
98     fun setColor(color: Int) {
99         rippleShader.color = color
100     }
101 
102     override fun onDraw(canvas: Canvas?) {
103         // To reduce overdraw, we mask the effect to a circle whose radius is big enough to cover
104         // the active effect area. Values here should be kept in sync with the
105         // animation implementation in the ripple shader.
106         val maskRadius = (1 - (1 - rippleShader.progress) * (1 - rippleShader.progress) *
107                 (1 - rippleShader.progress)) * radius * 1.5f
108         canvas?.drawCircle(origin.x, origin.y, maskRadius, ripplePaint)
109     }
110 }
111