• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
<lambda>null2  * 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.permissioncontroller.wear.permission.components
17 
18 import android.graphics.drawable.Animatable
19 import android.graphics.drawable.ColorDrawable
20 import android.graphics.drawable.Drawable
21 import android.os.Build
22 import android.os.Handler
23 import android.os.Looper
24 import android.view.View
25 import androidx.compose.runtime.Composable
26 import androidx.compose.runtime.RememberObserver
27 import androidx.compose.runtime.getValue
28 import androidx.compose.runtime.mutableStateOf
29 import androidx.compose.runtime.remember
30 import androidx.compose.runtime.setValue
31 import androidx.compose.ui.geometry.Size
32 import androidx.compose.ui.graphics.Color
33 import androidx.compose.ui.graphics.ColorFilter
34 import androidx.compose.ui.graphics.asAndroidColorFilter
35 import androidx.compose.ui.graphics.drawscope.DrawScope
36 import androidx.compose.ui.graphics.drawscope.drawIntoCanvas
37 import androidx.compose.ui.graphics.nativeCanvas
38 import androidx.compose.ui.graphics.painter.ColorPainter
39 import androidx.compose.ui.graphics.painter.Painter
40 import androidx.compose.ui.graphics.withSave
41 import androidx.compose.ui.unit.LayoutDirection
42 import kotlin.math.roundToInt
43 
44 private val MAIN_HANDLER by lazy(LazyThreadSafetyMode.NONE) { Handler(Looper.getMainLooper()) }
45 
46 /**
47  * A [Painter] which draws an Android [Drawable] and supports [Animatable] drawables. Instances
48  * should be remembered to be able to start and stop [Animatable] animations.
49  *
50  * Instances are usually retrieved from [rememberDrawablePainter].
51  */
52 class DrawablePainter(val drawable: Drawable) : Painter(), RememberObserver {
53     private var drawInvalidateTick by mutableStateOf(0)
54     private var drawableIntrinsicSize by mutableStateOf(drawable.intrinsicSize)
55 
<lambda>null56     private val callback: Drawable.Callback by lazy {
57         object : Drawable.Callback {
58             override fun invalidateDrawable(d: Drawable) {
59                 // Update the tick so that we get re-drawn
60                 drawInvalidateTick++
61                 // Update our intrinsic size too
62                 drawableIntrinsicSize = drawable.intrinsicSize
63             }
64 
65             override fun scheduleDrawable(d: Drawable, what: Runnable, time: Long) {
66                 MAIN_HANDLER.postAtTime(what, time)
67             }
68 
69             override fun unscheduleDrawable(d: Drawable, what: Runnable) {
70                 MAIN_HANDLER.removeCallbacks(what)
71             }
72         }
73     }
74 
75     init {
76         if (drawable.intrinsicWidth >= 0 && drawable.intrinsicHeight >= 0) {
77             // Update the drawable's bounds to match the intrinsic size
78             drawable.setBounds(0, 0, drawable.intrinsicWidth, drawable.intrinsicHeight)
79         }
80     }
81 
onRememberednull82     override fun onRemembered() {
83         drawable.callback = callback
84         drawable.setVisible(true, true)
85         if (drawable is Animatable) drawable.start()
86     }
87 
onAbandonednull88     override fun onAbandoned() = onForgotten()
89 
90     override fun onForgotten() {
91         if (drawable is Animatable) drawable.stop()
92         drawable.setVisible(false, false)
93         drawable.callback = null
94     }
95 
applyAlphanull96     override fun applyAlpha(alpha: Float): Boolean {
97         drawable.alpha = (alpha * 255).roundToInt().coerceIn(0, 255)
98         return true
99     }
100 
applyColorFilternull101     override fun applyColorFilter(colorFilter: ColorFilter?): Boolean {
102         drawable.colorFilter = colorFilter?.asAndroidColorFilter()
103         return true
104     }
105 
applyLayoutDirectionnull106     override fun applyLayoutDirection(layoutDirection: LayoutDirection): Boolean {
107         if (Build.VERSION.SDK_INT >= 23) {
108             return drawable.setLayoutDirection(
109                 when (layoutDirection) {
110                     LayoutDirection.Ltr -> View.LAYOUT_DIRECTION_LTR
111                     LayoutDirection.Rtl -> View.LAYOUT_DIRECTION_RTL
112                 }
113             )
114         }
115         return false
116     }
117 
118     override val intrinsicSize: Size
119         get() = drawableIntrinsicSize
120 
onDrawnull121     override fun DrawScope.onDraw() {
122         drawIntoCanvas { canvas ->
123             // Reading this ensures that we invalidate when invalidateDrawable() is called
124             drawInvalidateTick
125 
126             // Update the Drawable's bounds
127             drawable.setBounds(0, 0, size.width.roundToInt(), size.height.roundToInt())
128 
129             canvas.withSave { drawable.draw(canvas.nativeCanvas) }
130         }
131     }
132 }
133 
134 /**
135  * Remembers [Drawable] wrapped up as a [Painter]. This function attempts to un-wrap the drawable
136  * contents and use Compose primitives where possible.
137  *
138  * If the provided [drawable] is `null`, an empty no-op painter is returned.
139  *
140  * This function tries to dispatch lifecycle events to [drawable] as much as possible from within
141  * Compose.
142  */
143 @Composable
rememberDrawablePainternull144 fun rememberDrawablePainter(drawable: Drawable?): Painter =
145     remember(drawable) {
146         when (drawable) {
147             null -> EmptyPainter
148             is ColorDrawable -> ColorPainter(Color(drawable.color))
149             // Since the DrawablePainter will be remembered and it implements RememberObserver, it
150             // will receive the necessary events
151             else -> DrawablePainter(drawable.mutate())
152         }
153     }
154 
155 private val Drawable.intrinsicSize: Size
156     get() =
157         when {
158             // Only return a finite size if the drawable has an intrinsic size
159             intrinsicWidth >= 0 && intrinsicHeight >= 0 -> {
160                 Size(width = intrinsicWidth.toFloat(), height = intrinsicHeight.toFloat())
161             }
162             else -> Size.Unspecified
163         }
164 
165 internal object EmptyPainter : Painter() {
166     override val intrinsicSize: Size
167         get() = Size.Unspecified
168 
onDrawnull169     override fun DrawScope.onDraw() {}
170 }
171