1 /* 2 * Copyright (C) 2025 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.wallpapers 18 19 import android.app.Flags 20 import android.content.res.Configuration.UI_MODE_NIGHT_MASK 21 import android.content.res.Configuration.UI_MODE_NIGHT_YES 22 import android.graphics.Canvas 23 import android.graphics.Color 24 import android.graphics.Paint 25 import android.graphics.PorterDuff 26 import android.graphics.PorterDuffXfermode 27 import android.graphics.RadialGradient 28 import android.graphics.Shader 29 import android.service.wallpaper.WallpaperService 30 import android.util.Log 31 import android.view.SurfaceHolder 32 import androidx.core.graphics.ColorUtils 33 import androidx.core.graphics.toRectF 34 import com.android.systemui.res.R 35 36 /** A wallpaper that shows a static gradient color image wallpaper. */ 37 class GradientColorWallpaper : WallpaperService() { 38 onCreateEnginenull39 override fun onCreateEngine(): Engine = 40 if (Flags.enableConnectedDisplaysWallpaper()) { 41 GradientColorWallpaperEngine() 42 } else { 43 EmptyWallpaperEngine() 44 } 45 46 /** Empty engine used when the feature flag is disabled. */ 47 inner class EmptyWallpaperEngine : Engine() 48 49 inner class GradientColorWallpaperEngine : Engine() { 50 init { 51 setShowForAllUsers(true) 52 } 53 onSurfaceRedrawNeedednull54 override fun onSurfaceRedrawNeeded(surfaceHolder: SurfaceHolder) { 55 drawFrameInternal(surfaceHolder) 56 } 57 drawFrameInternalnull58 private fun drawFrameInternal(surfaceHolder: SurfaceHolder) { 59 val context = displayContext ?: return 60 val surface = surfaceHolder.surface 61 var canvas: Canvas? = null 62 try { 63 canvas = surface.lockHardwareCanvas() 64 val destRectF = surfaceHolder.surfaceFrame.toRectF() 65 val toColor = context.getColor(com.android.internal.R.color.materialColorPrimary) 66 val fromColor = 67 ColorUtils.setAlphaComponent( 68 context.getColor( 69 com.android.internal.R.color.materialColorPrimaryContainer 70 ), 71 /* alpha= */ 153, // 0.6f * 255 72 ) 73 74 canvas.drawRect(destRectF, Paint().apply { color = toColor }) 75 76 val offsetPx: Float = 77 context.resources 78 .getDimensionPixelSize(R.dimen.gradient_color_wallpaper_center_offset) 79 .toFloat() 80 val totalHeight = destRectF.height() + (offsetPx * 2) 81 val leftCenterX = -offsetPx 82 val leftCenterY = totalHeight - offsetPx 83 val rightCenterX = offsetPx + destRectF.width() 84 val rightCenterY = -offsetPx 85 val radius = (destRectF.width() / 2) + offsetPx 86 87 canvas.drawCircle( 88 leftCenterX, 89 leftCenterY, 90 radius, 91 Paint().apply { 92 shader = 93 RadialGradient( 94 /* centerX= */ leftCenterX, 95 /* centerY= */ leftCenterY, 96 /* radius= */ radius, 97 /* centerColor= */ fromColor, 98 /* edgeColor= */ toColor, 99 /* tileMode= */ Shader.TileMode.CLAMP, 100 ) 101 }, 102 ) 103 104 canvas.drawCircle( 105 rightCenterX, 106 rightCenterY, 107 radius, 108 Paint().apply { 109 shader = 110 RadialGradient( 111 /* centerX= */ rightCenterX, 112 /* centerY= */ rightCenterY, 113 /* radius= */ radius, 114 /* centerColor= */ fromColor, 115 /* edgeColor= */ toColor, 116 /* tileMode= */ Shader.TileMode.CLAMP, 117 ) 118 }, 119 ) 120 121 val isDarkMode = 122 context.resources.configuration.uiMode and UI_MODE_NIGHT_MASK == 123 UI_MODE_NIGHT_YES 124 val maskColor = 125 ColorUtils.setAlphaComponent( 126 if (isDarkMode) Color.BLACK else Color.WHITE, 127 /* alpha= */ 87, // 0.34f * 255 128 ) 129 val maskPaint = 130 Paint().apply { 131 xfermode = 132 PorterDuffXfermode( 133 if (isDarkMode) { 134 PorterDuff.Mode.DARKEN 135 } else { 136 PorterDuff.Mode.LIGHTEN 137 } 138 ) 139 color = maskColor 140 } 141 canvas.drawRect(destRectF, maskPaint) 142 } catch (exception: IllegalStateException) { 143 Log.d(TAG, "Fail to draw in the canvas", exception) 144 } finally { 145 canvas?.let { surface.unlockCanvasAndPost(it) } 146 } 147 } 148 } 149 150 private companion object { 151 const val TAG = "GradientColorWallpaper" 152 } 153 } 154