<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