• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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