1 /*
<lambda>null2 * Copyright (C) 2022 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.decor
18
19 import android.content.Context
20 import android.content.res.ColorStateList
21 import android.view.DisplayCutout
22 import android.view.Gravity
23 import android.view.Surface
24 import android.view.View
25 import android.view.ViewGroup
26 import android.widget.FrameLayout
27 import android.widget.ImageView
28 import com.android.systemui.R
29
30 class RoundedCornerDecorProviderImpl(
31 override val viewId: Int,
32 @DisplayCutout.BoundsPosition override val alignedBound1: Int,
33 @DisplayCutout.BoundsPosition override val alignedBound2: Int,
34 private val roundedCornerResDelegate: RoundedCornerResDelegate
35 ) : CornerDecorProvider() {
36
37 private val isTop = alignedBounds.contains(DisplayCutout.BOUNDS_POSITION_TOP)
38
39 override fun inflateView(
40 context: Context,
41 parent: ViewGroup,
42 @Surface.Rotation rotation: Int,
43 tintColor: Int
44 ): View {
45 return ImageView(context).also { view ->
46 // View
47 view.id = viewId
48 initView(view, rotation, tintColor)
49
50 // LayoutParams
51 val layoutSize = if (isTop) {
52 roundedCornerResDelegate.topRoundedSize
53 } else {
54 roundedCornerResDelegate.bottomRoundedSize
55 }
56 val params = FrameLayout.LayoutParams(
57 layoutSize.width,
58 layoutSize.height,
59 alignedBound1.toLayoutGravity(rotation) or alignedBound2.toLayoutGravity(rotation)
60 )
61
62 // AddView
63 parent.addView(view, params)
64 }
65 }
66
67 private fun initView(
68 view: ImageView,
69 @Surface.Rotation rotation: Int,
70 tintColor: Int
71 ) {
72 view.setRoundedCornerImage(roundedCornerResDelegate, isTop)
73 view.adjustRotation(alignedBounds, rotation)
74 view.imageTintList = ColorStateList.valueOf(tintColor)
75 }
76
77 override fun onReloadResAndMeasure(
78 view: View,
79 reloadToken: Int,
80 @Surface.Rotation rotation: Int,
81 tintColor: Int,
82 displayUniqueId: String?
83 ) {
84 roundedCornerResDelegate.updateDisplayUniqueId(displayUniqueId, reloadToken)
85
86 initView((view as ImageView), rotation, tintColor)
87
88 val layoutSize = if (isTop) {
89 roundedCornerResDelegate.topRoundedSize
90 } else {
91 roundedCornerResDelegate.bottomRoundedSize
92 }
93 (view.layoutParams as FrameLayout.LayoutParams).let {
94 it.width = layoutSize.width
95 it.height = layoutSize.height
96 it.gravity = alignedBound1.toLayoutGravity(rotation) or
97 alignedBound2.toLayoutGravity(rotation)
98 view.setLayoutParams(it)
99 }
100 }
101 }
102
103 @DisplayCutout.BoundsPosition
toLayoutGravitynull104 private fun Int.toLayoutGravity(@Surface.Rotation rotation: Int): Int = when (rotation) {
105 Surface.ROTATION_0 -> when (this) {
106 DisplayCutout.BOUNDS_POSITION_LEFT -> Gravity.LEFT
107 DisplayCutout.BOUNDS_POSITION_TOP -> Gravity.TOP
108 DisplayCutout.BOUNDS_POSITION_RIGHT -> Gravity.RIGHT
109 else /* DisplayCutout.BOUNDS_POSITION_BOTTOM */ -> Gravity.BOTTOM
110 }
111 Surface.ROTATION_90 -> when (this) {
112 DisplayCutout.BOUNDS_POSITION_LEFT -> Gravity.BOTTOM
113 DisplayCutout.BOUNDS_POSITION_TOP -> Gravity.LEFT
114 DisplayCutout.BOUNDS_POSITION_RIGHT -> Gravity.TOP
115 else /* DisplayCutout.BOUNDS_POSITION_BOTTOM */ -> Gravity.RIGHT
116 }
117 Surface.ROTATION_270 -> when (this) {
118 DisplayCutout.BOUNDS_POSITION_LEFT -> Gravity.TOP
119 DisplayCutout.BOUNDS_POSITION_TOP -> Gravity.RIGHT
120 DisplayCutout.BOUNDS_POSITION_RIGHT -> Gravity.BOTTOM
121 else /* DisplayCutout.BOUNDS_POSITION_BOTTOM */ -> Gravity.LEFT
122 }
123 else /* Surface.ROTATION_180 */ -> when (this) {
124 DisplayCutout.BOUNDS_POSITION_LEFT -> Gravity.RIGHT
125 DisplayCutout.BOUNDS_POSITION_TOP -> Gravity.BOTTOM
126 DisplayCutout.BOUNDS_POSITION_RIGHT -> Gravity.LEFT
127 else /* DisplayCutout.BOUNDS_POSITION_BOTTOM */ -> Gravity.TOP
128 }
129 }
130
setRoundedCornerImagenull131 private fun ImageView.setRoundedCornerImage(
132 resDelegate: RoundedCornerResDelegate,
133 isTop: Boolean
134 ) {
135 val drawable = if (isTop)
136 resDelegate.topRoundedDrawable
137 else
138 resDelegate.bottomRoundedDrawable
139
140 if (drawable != null) {
141 setImageDrawable(drawable)
142 } else {
143 setImageResource(
144 if (isTop)
145 R.drawable.rounded_corner_top
146 else
147 R.drawable.rounded_corner_bottom
148 )
149 }
150 }
151
152 /**
153 * Configures the rounded corner drawable's view matrix based on the gravity.
154 *
155 * The gravity describes which corner to configure for, and the drawable we are rotating is assumed
156 * to be oriented for the top-left corner of the device regardless of the target corner.
157 * Therefore we need to rotate 180 degrees to get a bottom-left corner, and mirror in the x- or
158 * y-axis for the top-right and bottom-left corners.
159 */
adjustRotationnull160 private fun ImageView.adjustRotation(alignedBounds: List<Int>, @Surface.Rotation rotation: Int) {
161 var newRotation = 0F
162 var newScaleX = 1F
163 var newScaleY = 1F
164
165 val isTop = alignedBounds.contains(DisplayCutout.BOUNDS_POSITION_TOP)
166 val isLeft = alignedBounds.contains(DisplayCutout.BOUNDS_POSITION_LEFT)
167 when (rotation) {
168 Surface.ROTATION_0 -> when {
169 isTop && isLeft -> {}
170 isTop && !isLeft -> { newScaleX = -1F }
171 !isTop && isLeft -> { newScaleY = -1F }
172 else /* !isTop && !isLeft */ -> { newRotation = 180F }
173 }
174 Surface.ROTATION_90 -> when {
175 isTop && isLeft -> { newScaleY = -1F }
176 isTop && !isLeft -> {}
177 !isTop && isLeft -> { newRotation = 180F }
178 else /* !isTop && !isLeft */ -> { newScaleX = -1F }
179 }
180 Surface.ROTATION_270 -> when {
181 isTop && isLeft -> { newScaleX = -1F }
182 isTop && !isLeft -> { newRotation = 180F }
183 !isTop && isLeft -> {}
184 else /* !isTop && !isLeft */ -> { newScaleY = -1F }
185 }
186 else /* Surface.ROTATION_180 */ -> when {
187 isTop && isLeft -> { newRotation = 180F }
188 isTop && !isLeft -> { newScaleY = -1F }
189 !isTop && isLeft -> { newScaleX = -1F }
190 else /* !isTop && !isLeft */ -> {}
191 }
192 }
193
194 this.rotation = newRotation
195 this.scaleX = newScaleX
196 this.scaleY = newScaleY
197 }
198