1 /* 2 * Copyright (C) 2023 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.customization.picker.clock.ui.view 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 SaturationProgressDrawable @JvmOverloads constructor(drawable: Drawable? = null) : 42 InsetDrawable(drawable, 0) { 43 44 companion object { 45 private const val MAX_LEVEL = 10000 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 drawableBounds = 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 // TODO (b/268541542) Test if RTL devices also works for the slider 67 drawable?.setBounds( 68 bounds.left, 69 drawableBounds.top, 70 width.coerceAtMost(bounds.width()).coerceAtLeast(bounds.height()), 71 drawableBounds.bottom 72 ) 73 return super.onLevelChange(level) 74 } 75 getConstantStatenull76 override fun getConstantState(): ConstantState { 77 // This should not be null as it was created with a state in the constructor. 78 return RoundedCornerState(super.getConstantState()!!) 79 } 80 getChangingConfigurationsnull81 override fun getChangingConfigurations(): Int { 82 return super.getChangingConfigurations() or ActivityInfo.CONFIG_DENSITY 83 } 84 canApplyThemenull85 override fun canApplyTheme(): Boolean { 86 return (drawable?.canApplyTheme() ?: false) || super.canApplyTheme() 87 } 88 89 private class RoundedCornerState(private val wrappedState: ConstantState) : ConstantState() { newDrawablenull90 override fun newDrawable(): Drawable { 91 return newDrawable(null, null) 92 } 93 newDrawablenull94 override fun newDrawable(res: Resources?, theme: Resources.Theme?): Drawable { 95 val wrapper = wrappedState.newDrawable(res, theme) as DrawableWrapper 96 return SaturationProgressDrawable(wrapper.drawable) 97 } 98 getChangingConfigurationsnull99 override fun getChangingConfigurations(): Int { 100 return wrappedState.changingConfigurations 101 } 102 canApplyThemenull103 override fun canApplyTheme(): Boolean { 104 return true 105 } 106 } 107 } 108