<lambda>null1 package com.android.wallpaper.util.wallpaperconnection
2 
3 import android.app.WallpaperColors
4 import android.graphics.Point
5 import android.graphics.Rect
6 import android.graphics.RectF
7 import android.os.Bundle
8 import android.os.IBinder
9 import android.os.ParcelFileDescriptor
10 import android.os.RemoteException
11 import android.service.wallpaper.IWallpaperConnection
12 import android.service.wallpaper.IWallpaperEngine
13 import android.service.wallpaper.IWallpaperService
14 import android.util.Log
15 import android.view.SurfaceView
16 import android.view.WindowManager
17 import com.android.wallpaper.util.WallpaperConnection
18 import com.android.wallpaper.util.WallpaperConnection.WhichPreview
19 import java.lang.reflect.InvocationTargetException
20 import java.lang.reflect.Method
21 import kotlinx.coroutines.CancellableContinuation
22 import kotlinx.coroutines.suspendCancellableCoroutine
23 
24 class WallpaperEngineConnection(
25     private val displayMetrics: Point,
26     private val whichPreview: WhichPreview,
27 ) : IWallpaperConnection.Stub() {
28 
29     var engine: IWallpaperEngine? = null
30     private var engineContinuation: CancellableContinuation<IWallpaperEngine>? = null
31     private var listener: WallpaperEngineConnectionListener? = null
32 
33     suspend fun getEngine(
34         wallpaperService: IWallpaperService,
35         destinationFlag: Int,
36         surfaceView: SurfaceView,
37     ): IWallpaperEngine {
38         return engine
39             ?: suspendCancellableCoroutine { k: CancellableContinuation<IWallpaperEngine> ->
40                 engineContinuation = k
41                 attachEngineConnection(
42                     wallpaperEngineConnection = this,
43                     wallpaperService = wallpaperService,
44                     destinationFlag = destinationFlag,
45                     surfaceView = surfaceView,
46                 )
47             }
48     }
49 
50     override fun attachEngine(engine: IWallpaperEngine?, displayId: Int) {
51         // Note that the engine in attachEngine callback is different from the one in engineShown
52         // callback, which is called after this attachEngine callback. We use the engine reference
53         // passed in attachEngine callback for WallpaperEngineConnection.
54         this.engine = engine
55         engine?.apply {
56             setVisibility(true)
57             resizePreview(Rect(0, 0, displayMetrics.x, displayMetrics.y))
58             requestWallpaperColors()
59         }
60     }
61 
62     override fun engineShown(engine: IWallpaperEngine?) {
63         if (engine != null) {
64             dispatchWallpaperCommand(engine)
65             // Note that the engines from the attachEngine and engineShown callbacks are different
66             // and we only use the reference of engine from the attachEngine callback.
67             this.engine?.let { engineContinuation?.resumeWith(Result.success(it)) }
68         }
69     }
70 
71     private fun dispatchWallpaperCommand(engine: IWallpaperEngine) {
72         val bundle = Bundle()
73         bundle.putInt(WHICH_PREVIEW, whichPreview.value)
74         try {
75             engine.dispatchWallpaperCommand(COMMAND_PREVIEW_INFO, 0, 0, 0, bundle)
76         } catch (e: RemoteException) {
77             Log.e(TAG, "Error dispatching wallpaper command: $whichPreview")
78         }
79     }
80 
81     override fun onLocalWallpaperColorsChanged(
82         area: RectF?,
83         colors: WallpaperColors?,
84         displayId: Int
85     ) {
86         // Do nothing intended.
87     }
88 
89     override fun onWallpaperColorsChanged(colors: WallpaperColors?, displayId: Int) {
90         listener?.onWallpaperColorsChanged(colors, displayId)
91     }
92 
93     override fun setWallpaper(name: String?): ParcelFileDescriptor {
94         TODO("Not yet implemented")
95     }
96 
97     fun setListener(listener: WallpaperEngineConnectionListener) {
98         this.listener = listener
99     }
100 
101     fun removeListener() {
102         this.listener = null
103     }
104 
105     companion object {
106         const val TAG = "WallpaperEngineConnection"
107 
108         const val COMMAND_PREVIEW_INFO = "android.wallpaper.previewinfo"
109         const val WHICH_PREVIEW = "which_preview"
110 
111         /**
112          * Before Android U, [IWallpaperService.attach] has no input of destinationFlag. We do
113          * method reflection to probe if the service from the external app is using a pre-U API;
114          * otherwise, we use the new one.
115          */
116         private fun attachEngineConnection(
117             wallpaperEngineConnection: WallpaperEngineConnection,
118             wallpaperService: IWallpaperService,
119             destinationFlag: Int,
120             surfaceView: SurfaceView,
121         ) {
122             try {
123                 val preUMethod: Method =
124                     wallpaperService.javaClass.getMethod(
125                         "attach",
126                         IWallpaperConnection::class.java,
127                         IBinder::class.java,
128                         Int::class.javaPrimitiveType,
129                         Boolean::class.javaPrimitiveType,
130                         Int::class.javaPrimitiveType,
131                         Int::class.javaPrimitiveType,
132                         Rect::class.java,
133                         Int::class.javaPrimitiveType
134                     )
135                 preUMethod.invoke(
136                     wallpaperService,
137                     wallpaperEngineConnection,
138                     surfaceView.windowToken,
139                     WindowManager.LayoutParams.TYPE_APPLICATION_MEDIA,
140                     true,
141                     surfaceView.width,
142                     surfaceView.height,
143                     Rect(0, 0, 0, 0),
144                     surfaceView.display.displayId
145                 )
146             } catch (e: Exception) {
147                 when (e) {
148                     is NoSuchMethodException,
149                     is InvocationTargetException,
150                     is IllegalAccessException ->
151                         wallpaperService.attach(
152                             wallpaperEngineConnection,
153                             surfaceView.windowToken,
154                             WindowManager.LayoutParams.TYPE_APPLICATION_MEDIA,
155                             true,
156                             surfaceView.width,
157                             surfaceView.height,
158                             Rect(0, 0, 0, 0),
159                             surfaceView.display.displayId,
160                             destinationFlag,
161                             null,
162                         )
163                     else -> throw e
164                 }
165             }
166         }
167     }
168 
169     /** Interface to be notified of connect/disconnect events from [WallpaperConnection] */
170     interface WallpaperEngineConnectionListener {
171         /** Called after the wallpaper color is available or updated. */
172         fun onWallpaperColorsChanged(colors: WallpaperColors?, displayId: Int) {}
173     }
174 }
175