1 /* <lambda>null2 * Copyright (C) 2024 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.launcher3.icons.mono 18 19 import android.annotation.TargetApi 20 import android.content.Context 21 import android.graphics.Bitmap 22 import android.graphics.Bitmap.Config.ALPHA_8 23 import android.graphics.Bitmap.Config.HARDWARE 24 import android.graphics.BlendMode.SRC_IN 25 import android.graphics.BlendModeColorFilter 26 import android.graphics.Canvas 27 import android.graphics.Color 28 import android.graphics.drawable.AdaptiveIconDrawable 29 import android.graphics.drawable.BitmapDrawable 30 import android.graphics.drawable.ColorDrawable 31 import android.graphics.drawable.Drawable 32 import android.graphics.drawable.InsetDrawable 33 import android.os.Build 34 import com.android.launcher3.Flags 35 import com.android.launcher3.icons.BaseIconFactory 36 import com.android.launcher3.icons.BitmapInfo 37 import com.android.launcher3.icons.IconThemeController 38 import com.android.launcher3.icons.MonochromeIconFactory 39 import com.android.launcher3.icons.ThemedBitmap 40 import com.android.launcher3.icons.mono.ThemedIconDrawable.Companion.getColors 41 import java.nio.ByteBuffer 42 43 @TargetApi(Build.VERSION_CODES.TIRAMISU) 44 class MonoIconThemeController : IconThemeController { 45 46 override fun createThemedBitmap( 47 icon: AdaptiveIconDrawable, 48 info: BitmapInfo, 49 factory: BaseIconFactory, 50 ): ThemedBitmap? { 51 val mono = getMonochromeDrawable(icon, info) 52 if (mono != null) { 53 val scale = 54 factory.normalizer.getScale( 55 AdaptiveIconDrawable(ColorDrawable(Color.BLACK), null), 56 null, 57 null, 58 null, 59 ) 60 return MonoThemedBitmap( 61 factory.createIconBitmap(mono, scale, BaseIconFactory.MODE_ALPHA), 62 factory.whiteShadowLayer, 63 ) 64 } 65 return null 66 } 67 68 /** 69 * Returns a monochromatic version of the given drawable or null, if it is not supported 70 * 71 * @param base the original icon 72 */ 73 private fun getMonochromeDrawable(base: AdaptiveIconDrawable, info: BitmapInfo): Drawable? { 74 val mono = base.monochrome 75 if (mono != null) { 76 return ClippedMonoDrawable(mono) 77 } 78 if (Flags.forceMonochromeAppIcons()) { 79 return MonochromeIconFactory(info.icon.width).wrap(base) 80 } 81 return null 82 } 83 84 override fun decode( 85 data: ByteArray, 86 info: BitmapInfo, 87 factory: BaseIconFactory, 88 ): ThemedBitmap? { 89 val icon = info.icon 90 if (data.size != icon.height * icon.width) return null 91 92 var monoBitmap = Bitmap.createBitmap(icon.width, icon.height, ALPHA_8) 93 monoBitmap.copyPixelsFromBuffer(ByteBuffer.wrap(data)) 94 95 val hwMonoBitmap = monoBitmap.copy(HARDWARE, false /*isMutable*/) 96 if (hwMonoBitmap != null) { 97 monoBitmap.recycle() 98 monoBitmap = hwMonoBitmap 99 } 100 return MonoThemedBitmap(monoBitmap, factory.whiteShadowLayer) 101 } 102 103 override fun createThemedAdaptiveIcon( 104 context: Context, 105 originalIcon: AdaptiveIconDrawable, 106 info: BitmapInfo?, 107 ): AdaptiveIconDrawable? { 108 val colors = getColors(context) 109 originalIcon.mutate() 110 var monoDrawable = originalIcon.monochrome?.apply { setTint(colors[1]) } 111 112 if (monoDrawable == null) { 113 info?.themedBitmap?.let { themedBitmap -> 114 if (themedBitmap is MonoThemedBitmap) { 115 // Inject a previously generated monochrome icon 116 // Use BitmapDrawable instead of FastBitmapDrawable so that the colorState is 117 // preserved in constantState 118 // Inset the drawable according to the AdaptiveIconDrawable layers 119 monoDrawable = 120 InsetDrawable( 121 BitmapDrawable(themedBitmap.mono).apply { 122 colorFilter = BlendModeColorFilter(colors[1], SRC_IN) 123 }, 124 AdaptiveIconDrawable.getExtraInsetFraction() / 2, 125 ) 126 } 127 } 128 } 129 130 return monoDrawable?.let { AdaptiveIconDrawable(ColorDrawable(colors[0]), it) } 131 } 132 133 class ClippedMonoDrawable(base: Drawable?) : 134 InsetDrawable(base, -AdaptiveIconDrawable.getExtraInsetFraction()) { 135 private val mCrop = AdaptiveIconDrawable(ColorDrawable(Color.BLACK), null) 136 137 override fun draw(canvas: Canvas) { 138 mCrop.bounds = bounds 139 val saveCount = canvas.save() 140 canvas.clipPath(mCrop.iconMask) 141 super.draw(canvas) 142 canvas.restoreToCount(saveCount) 143 } 144 } 145 } 146