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.res.ColorStateList 19 import android.graphics.BlendMode 20 import android.graphics.Canvas 21 import android.graphics.Color 22 import android.graphics.ColorFilter 23 import android.graphics.PixelFormat 24 import android.graphics.PorterDuff 25 import android.graphics.PorterDuffColorFilter 26 import android.graphics.RenderEffect 27 import android.graphics.RenderNode 28 import android.graphics.Shader 29 import android.graphics.drawable.Drawable 30 import android.graphics.drawable.InsetDrawable 31 import com.android.systemui.shared.shadow.DoubleShadowTextHelper.ShadowInfo 32 33 /** A component to draw an icon with two layers of shadows. */ 34 class DoubleShadowIconDrawable( 35 keyShadowInfo: ShadowInfo, 36 ambientShadowInfo: ShadowInfo, 37 iconDrawable: Drawable, 38 iconSize: Int, 39 val iconInsetSize: Int 40 ) : Drawable() { 41 private val mAmbientShadowInfo: ShadowInfo 42 private val mCanvasSize: Int 43 private val mKeyShadowInfo: ShadowInfo 44 private val mIconDrawable: InsetDrawable 45 private val mDoubleShadowNode: RenderNode 46 47 init { 48 mCanvasSize = iconSize + iconInsetSize * 2 49 mKeyShadowInfo = keyShadowInfo 50 mAmbientShadowInfo = ambientShadowInfo 51 setBounds(0, 0, mCanvasSize, mCanvasSize) 52 mIconDrawable = InsetDrawable(iconDrawable, iconInsetSize) 53 mIconDrawable.setBounds(0, 0, mCanvasSize, mCanvasSize) 54 mDoubleShadowNode = createShadowRenderNode() 55 } 56 57 private fun createShadowRenderNode(): RenderNode { 58 val renderNode = RenderNode("DoubleShadowNode") 59 renderNode.setPosition(0, 0, mCanvasSize, mCanvasSize) 60 // Create render effects 61 val ambientShadow = 62 createShadowRenderEffect( 63 mAmbientShadowInfo.blur, 64 mAmbientShadowInfo.offsetX, 65 mAmbientShadowInfo.offsetY, 66 mAmbientShadowInfo.alpha 67 ) 68 val keyShadow = 69 createShadowRenderEffect( 70 mKeyShadowInfo.blur, 71 mKeyShadowInfo.offsetX, 72 mKeyShadowInfo.offsetY, 73 mKeyShadowInfo.alpha 74 ) 75 val blend = RenderEffect.createBlendModeEffect(ambientShadow, keyShadow, BlendMode.DST_ATOP) 76 renderNode.setRenderEffect(blend) 77 return renderNode 78 } 79 80 private fun createShadowRenderEffect( 81 radius: Float, 82 offsetX: Float, 83 offsetY: Float, 84 alpha: Float 85 ): RenderEffect { 86 return RenderEffect.createColorFilterEffect( 87 PorterDuffColorFilter(Color.argb(alpha, 0f, 0f, 0f), PorterDuff.Mode.MULTIPLY), 88 RenderEffect.createOffsetEffect( 89 offsetX, 90 offsetY, 91 RenderEffect.createBlurEffect(radius, radius, Shader.TileMode.CLAMP) 92 ) 93 ) 94 } 95 96 override fun draw(canvas: Canvas) { 97 if (canvas.isHardwareAccelerated) { 98 if (!mDoubleShadowNode.hasDisplayList()) { 99 // Record render node if its display list is not recorded or discarded 100 // (which happens when it's no longer drawn by anything). 101 val recordingCanvas = mDoubleShadowNode.beginRecording() 102 mIconDrawable.draw(recordingCanvas) 103 mDoubleShadowNode.endRecording() 104 } 105 canvas.drawRenderNode(mDoubleShadowNode) 106 } 107 mIconDrawable.draw(canvas) 108 } 109 110 override fun getIntrinsicHeight(): Int { 111 return mCanvasSize 112 } 113 114 override fun getIntrinsicWidth(): Int { 115 return mCanvasSize 116 } 117 118 override fun getOpacity(): Int { 119 return PixelFormat.TRANSPARENT 120 } 121 122 override fun setAlpha(alpha: Int) { 123 mIconDrawable.alpha = alpha 124 } 125 126 override fun setColorFilter(colorFilter: ColorFilter?) { 127 mIconDrawable.colorFilter = colorFilter 128 } 129 130 override fun setTint(color: Int) { 131 mIconDrawable.setTint(color) 132 } 133 134 override fun setTintList(tint: ColorStateList?) { 135 mIconDrawable.setTintList(tint) 136 } 137 } 138