1 /* 2 * Copyright (C) 2021 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.util 18 19 import android.content.pm.ActivityInfo 20 import android.content.res.Resources 21 import android.graphics.Rect 22 import android.graphics.drawable.Drawable 23 import android.graphics.drawable.DrawableWrapper 24 import android.graphics.drawable.InsetDrawable 25 26 /** 27 * [DrawableWrapper] to use in the progress of brightness slider. 28 * 29 * This drawable is used to change the bounds of the enclosed drawable depending on the level to 30 * simulate a sliding progress, instead of using clipping or scaling. That way, the shape of the 31 * edges is maintained. 32 * 33 * Meant to be used with a rounded ends background, it will also prevent deformation when the slider 34 * is meant to be smaller than the rounded corner. The background should have rounded corners that 35 * are half of the height. 36 * 37 * This class also assumes that a "thumb" icon exists within the end's edge of the progress 38 * drawable, and the slider's width, when interacted on, if offset by half the size of the thumb 39 * icon which puts the icon directly underneath the user's finger. 40 */ 41 class BrightnessProgressDrawable @JvmOverloads constructor(drawable: Drawable? = null) : 42 InsetDrawable(drawable, 0) { 43 44 companion object { 45 private const val MAX_LEVEL = 10000 // Taken from Drawable 46 } 47 onLayoutDirectionChangednull48 override fun onLayoutDirectionChanged(layoutDirection: Int): Boolean { 49 onLevelChange(level) 50 return super.onLayoutDirectionChanged(layoutDirection) 51 } 52 onBoundsChangenull53 override fun onBoundsChange(bounds: Rect) { 54 super.onBoundsChange(bounds) 55 onLevelChange(level) 56 } 57 onLevelChangenull58 override fun onLevelChange(level: Int): Boolean { 59 val db = drawable?.bounds!! 60 61 // The thumb offset shifts the sun icon directly under the user's thumb 62 val thumbOffset = bounds.height() / 2 63 val width = bounds.width() * level / MAX_LEVEL + thumbOffset 64 65 // On 0, the width is bounds.height (a circle), and on MAX_LEVEL, the width is bounds.width 66 drawable?.setBounds( 67 bounds.left, 68 db.top, 69 width.coerceAtMost(bounds.width()).coerceAtLeast(bounds.height()), 70 db.bottom 71 ) 72 return super.onLevelChange(level) 73 } 74 getConstantStatenull75 override fun getConstantState(): ConstantState { 76 // This should not be null as it was created with a state in the constructor. 77 return RoundedCornerState(super.getConstantState()!!) 78 } 79 getChangingConfigurationsnull80 override fun getChangingConfigurations(): Int { 81 return super.getChangingConfigurations() or ActivityInfo.CONFIG_DENSITY 82 } 83 canApplyThemenull84 override fun canApplyTheme(): Boolean { 85 return (drawable?.canApplyTheme() ?: false) || super.canApplyTheme() 86 } 87 88 private class RoundedCornerState(private val wrappedState: ConstantState) : ConstantState() { newDrawablenull89 override fun newDrawable(): Drawable { 90 return newDrawable(null, null) 91 } 92 newDrawablenull93 override fun newDrawable(res: Resources?, theme: Resources.Theme?): Drawable { 94 val wrapper = wrappedState.newDrawable(res, theme) as DrawableWrapper 95 return BrightnessProgressDrawable(wrapper.drawable) 96 } 97 getChangingConfigurationsnull98 override fun getChangingConfigurations(): Int { 99 return wrappedState.changingConfigurations 100 } 101 canApplyThemenull102 override fun canApplyTheme(): Boolean { 103 return true 104 } 105 } 106 } 107