1 /*
2  * Copyright 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 androidx.compose.ui.graphics
18 
19 import android.annotation.SuppressLint
20 import android.graphics.Canvas
21 import android.os.Build
22 import androidx.annotation.RequiresApi
23 import java.lang.reflect.InvocationTargetException
24 import java.lang.reflect.Method
25 
26 internal object CanvasUtils {
27     private var reorderBarrierMethod: Method? = null
28     private var inorderBarrierMethod: Method? = null
29     private var orderMethodsFetched = false
30 
31     /**
32      * Enables Z support for the Canvas.
33      *
34      * This is only supported on Lollipop and later.
35      */
36     @SuppressLint("SoonBlockedPrivateApi")
enableZnull37     fun enableZ(canvas: Canvas, enable: Boolean) {
38         if (Build.VERSION.SDK_INT >= 29) {
39             CanvasZHelper.enableZ(canvas, enable)
40         } else {
41             if (!orderMethodsFetched) {
42                 try {
43                     if (Build.VERSION.SDK_INT == Build.VERSION_CODES.P) {
44                         // use double reflection to avoid grey list on P
45                         val getDeclaredMethod =
46                             Class::class
47                                 .java
48                                 .getDeclaredMethod(
49                                     "getDeclaredMethod",
50                                     String::class.java,
51                                     arrayOf<Class<*>>()::class.java
52                                 )
53                         reorderBarrierMethod =
54                             getDeclaredMethod.invoke(
55                                 Canvas::class.java,
56                                 "insertReorderBarrier",
57                                 emptyArray<Class<*>>()
58                             ) as Method?
59                         inorderBarrierMethod =
60                             getDeclaredMethod.invoke(
61                                 Canvas::class.java,
62                                 "insertInorderBarrier",
63                                 emptyArray<Class<*>>()
64                             ) as Method?
65                     } else {
66                         reorderBarrierMethod =
67                             Canvas::class.java.getDeclaredMethod("insertReorderBarrier")
68                         inorderBarrierMethod =
69                             Canvas::class.java.getDeclaredMethod("insertInorderBarrier")
70                     }
71                     reorderBarrierMethod?.isAccessible = true
72                     inorderBarrierMethod?.isAccessible = true
73                 } catch (ignore: IllegalAccessException) { // Do nothing
74                 } catch (ignore: InvocationTargetException) { // Do nothing
75                 } catch (ignore: NoSuchMethodException) { // Do nothing
76                 }
77                 orderMethodsFetched = true
78             }
79             try {
80                 if (enable && reorderBarrierMethod != null) {
81                     reorderBarrierMethod!!.invoke(canvas)
82                 }
83                 if (!enable && inorderBarrierMethod != null) {
84                     inorderBarrierMethod!!.invoke(canvas)
85                 }
86             } catch (ignore: IllegalAccessException) { // Do nothing
87             } catch (ignore: InvocationTargetException) { // Do nothing
88             }
89         }
90     }
91 }
92 
93 @RequiresApi(29)
94 private object CanvasZHelper {
enableZnull95     fun enableZ(canvas: Canvas, enable: Boolean) {
96         if (enable) {
97             canvas.enableZ()
98         } else {
99             canvas.disableZ()
100         }
101     }
102 }
103