• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
<lambda>null2  * 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.wallpaper.module
18 
19 import android.app.WallpaperInfo
20 import android.app.wallpaper.WallpaperDescription
21 import android.content.Context
22 import android.net.Uri
23 import android.os.Parcel
24 import android.os.RemoteException
25 import android.util.Log
26 import android.util.Pair
27 import androidx.annotation.VisibleForTesting
28 import androidx.core.database.getBlobOrNull
29 import com.android.wallpaper.model.CreativeCategory
30 import com.android.wallpaper.model.WallpaperInfoContract
31 import com.android.wallpaper.picker.customization.shared.model.WallpaperDestination
32 import javax.inject.Inject
33 
34 class DefaultCreativeHelper @Inject constructor() : CreativeHelper {
35     override fun getCreativePreviewUri(
36         context: Context,
37         info: WallpaperInfo,
38         destination: WallpaperDestination,
39     ): Uri? {
40         return getCurrentCreativeData(context, info, destination).first
41     }
42 
43     override fun getCreativeDescription(
44         context: Context,
45         info: WallpaperInfo,
46         destination: WallpaperDestination,
47     ): WallpaperDescription? {
48         return getCurrentCreativeData(context, info, destination).second
49     }
50 
51     companion object {
52         private const val TAG = "DefaultCreativeHelper"
53 
54         // Queries a live wallpaper for its preview Uri and description, and returns them if they
55         // exist.
56         @VisibleForTesting
57         private fun getCurrentCreativeData(
58             context: Context,
59             info: WallpaperInfo,
60             destination: WallpaperDestination,
61         ): Pair<Uri?, WallpaperDescription?> {
62             val metaData = info.serviceInfo.metaData
63             val defaultValue: Pair<Uri?, WallpaperDescription?> = Pair(null, null)
64             val uri =
65                 metaData.getString(CreativeCategory.KEY_WALLPAPER_SAVE_CREATIVE_WALLPAPER_CURRENT)
66                     ?: return defaultValue
67 
68             val currentAssetsUri = Uri.parse(uri)
69             if (currentAssetsUri.authority == null) return defaultValue
70             context.contentResolver
71                 .acquireContentProviderClient(currentAssetsUri.authority!!)
72                 ?.use { client ->
73                     try {
74                         client.query(currentAssetsUri, null, null, null, null)?.use { cursor ->
75                             if (!cursor.moveToFirst()) return defaultValue
76                             do {
77                                 val dest =
78                                     cursor.getString(
79                                         cursor.getColumnIndex(
80                                             WallpaperInfoContract.CURRENT_DESTINATION
81                                         )
82                                     )
83                                 val previewUri =
84                                     Uri.parse(
85                                         cursor.getString(
86                                             cursor.getColumnIndex(
87                                                 WallpaperInfoContract.CURRENT_CONFIG_PREVIEW_URI
88                                             )
89                                         )
90                                     )
91                                 val descriptionIndex =
92                                     cursor.getColumnIndex(
93                                         WallpaperInfoContract.CURRENT_DESCRIPITION
94                                     )
95                                 val description =
96                                     if (descriptionIndex >= 0) {
97                                         cursor.getBlobOrNull(descriptionIndex)?.let {
98                                             descriptionFromBytes(it).let { desc ->
99                                                 if (desc.component == null) {
100                                                     desc
101                                                         .toBuilder()
102                                                         .setComponent(info.component)
103                                                         .build()
104                                                 } else desc
105                                             }
106                                         }
107                                     } else null
108                                 if (
109                                     (dest == "home" && destination == WallpaperDestination.HOME) ||
110                                         (dest == "lock" && destination == WallpaperDestination.LOCK)
111                                 ) {
112                                     return Pair(previewUri, description)
113                                 }
114                             } while (cursor.moveToNext())
115                         }
116                     } catch (e: RemoteException) {
117                         Log.w(TAG, "Error retrieving current creative asset id: ", e)
118                     }
119                 }
120 
121             return defaultValue
122         }
123 
124         private fun descriptionFromBytes(bytes: ByteArray): WallpaperDescription {
125             val parcel = Parcel.obtain()
126             parcel.unmarshall(bytes, 0, bytes.size)
127             parcel.setDataPosition(0)
128             val desc = WallpaperDescription.CREATOR.createFromParcel(parcel)
129             parcel.recycle()
130             return desc
131         }
132     }
133 }
134