1 /*
2  * 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 package com.android.systemui.shared.shadow
17 
18 import android.content.Context
19 import android.content.res.Resources
20 import android.content.res.TypedArray
21 import android.graphics.Canvas
22 import android.util.AttributeSet
23 import android.widget.TextClock
24 import com.android.systemui.shared.R
25 import com.android.systemui.shared.shadow.DoubleShadowTextHelper.ShadowInfo
26 import com.android.systemui.shared.shadow.DoubleShadowTextHelper.applyShadows
27 import kotlin.math.floor
28 
29 /** Extension of [TextClock] which draws two shadows on the text (ambient and key shadows) */
30 class DoubleShadowTextClock
31 @JvmOverloads
32 constructor(
33     context: Context,
34     attrs: AttributeSet? = null,
35     defStyleAttr: Int = 0,
36     defStyleRes: Int = 0,
37 ) : TextClock(context, attrs, defStyleAttr, defStyleRes) {
38     private lateinit var mAmbientShadowInfo: ShadowInfo
39     private lateinit var mKeyShadowInfo: ShadowInfo
40     private var attributesInput: TypedArray? = null
41     private var resources: Resources? = null
42 
43     constructor(
44         resources: Resources,
45         context: Context,
46         attrs: AttributeSet? = null,
47         defStyleAttr: Int = 0,
48         defStyleRes: Int = 0,
49         attributesInput: TypedArray? = null
50     ) : this(context, attrs, defStyleAttr, defStyleRes) {
51         this.attributesInput = attributesInput
52         this.resources = resources
53         this.initializeAttributes(attrs, defStyleAttr, defStyleRes)
54     }
55     init {
56         initializeAttributes(attrs, defStyleAttr, defStyleRes)
57     }
58 
59     private fun initializeAttributes(attrs: AttributeSet?, defStyleAttr: Int, defStyleRes: Int) {
60         var attributes: TypedArray =
61             this.attributesInput
62                 ?: context.obtainStyledAttributes(
63                     attrs,
64                     R.styleable.DoubleShadowTextClock,
65                     defStyleAttr,
66                     defStyleRes
67                 )
68 
69         var resource: Resources = this.resources ?: context.resources
70         try {
71             val keyShadowBlur =
72                 attributes.getDimensionPixelSize(R.styleable.DoubleShadowTextClock_keyShadowBlur, 0)
73             val keyShadowOffsetX =
74                 attributes.getDimensionPixelSize(
75                     R.styleable.DoubleShadowTextClock_keyShadowOffsetX,
76                     0
77                 )
78             val keyShadowOffsetY =
79                 attributes.getDimensionPixelSize(
80                     R.styleable.DoubleShadowTextClock_keyShadowOffsetY,
81                     0
82                 )
83             val keyShadowAlpha =
84                 attributes.getFloat(R.styleable.DoubleShadowTextClock_keyShadowAlpha, 0f)
85             mKeyShadowInfo =
86                 ShadowInfo(
87                     keyShadowBlur.toFloat(),
88                     keyShadowOffsetX.toFloat(),
89                     keyShadowOffsetY.toFloat(),
90                     keyShadowAlpha
91                 )
92             val ambientShadowBlur =
93                 attributes.getDimensionPixelSize(
94                     R.styleable.DoubleShadowTextClock_ambientShadowBlur,
95                     0
96                 )
97             val ambientShadowOffsetX =
98                 attributes.getDimensionPixelSize(
99                     R.styleable.DoubleShadowTextClock_ambientShadowOffsetX,
100                     0
101                 )
102             val ambientShadowOffsetY =
103                 attributes.getDimensionPixelSize(
104                     R.styleable.DoubleShadowTextClock_ambientShadowOffsetY,
105                     0
106                 )
107             val ambientShadowAlpha =
108                 attributes.getFloat(R.styleable.DoubleShadowTextClock_ambientShadowAlpha, 0f)
109             mAmbientShadowInfo =
110                 ShadowInfo(
111                     ambientShadowBlur.toFloat(),
112                     ambientShadowOffsetX.toFloat(),
113                     ambientShadowOffsetY.toFloat(),
114                     ambientShadowAlpha
115                 )
116             val removeTextDescent =
117                 attributes.getBoolean(R.styleable.DoubleShadowTextClock_removeTextDescent, false)
118             val textDescentExtraPadding =
119                 attributes.getDimensionPixelSize(
120                     R.styleable.DoubleShadowTextClock_textDescentExtraPadding,
121                     0
122                 )
123             if (removeTextDescent) {
124                 val addBottomPaddingToClock =
125                     resource.getBoolean(R.bool.dream_overlay_complication_clock_bottom_padding)
126                 val metrics = paint.fontMetrics
127                 val padding =
128                     if (addBottomPaddingToClock) {
129                         textDescentExtraPadding +
130                             floor(metrics.descent.toDouble()).toInt() / paddingDividedOffset
131                     } else {
132                         textDescentExtraPadding - floor(metrics.descent.toDouble()).toInt()
133                     }
134                 setPaddingRelative(0, 0, 0, padding)
135             }
136         } finally {
137             attributes.recycle()
138         }
139     }
140 
141     companion object {
142         private val paddingDividedOffset = 2
143     }
144 
145     public override fun onDraw(canvas: Canvas) {
146         applyShadows(mKeyShadowInfo, mAmbientShadowInfo, this, canvas) { super.onDraw(canvas) }
147     }
148 }
149