1 /*
<lambda>null2  * Copyright (C) 2024 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.picker.category.client
18 
19 import android.content.ComponentName
20 import android.content.Context
21 import android.content.Intent
22 import android.content.pm.PackageManager
23 import androidx.annotation.XmlRes
24 import com.android.wallpaper.R
25 import com.android.wallpaper.model.Category
26 import com.android.wallpaper.model.DefaultWallpaperInfo
27 import com.android.wallpaper.model.ImageCategory
28 import com.android.wallpaper.model.LegacyPartnerWallpaperInfo
29 import com.android.wallpaper.model.LiveWallpaperInfo
30 import com.android.wallpaper.model.ThirdPartyAppCategory
31 import com.android.wallpaper.model.ThirdPartyLiveWallpaperCategory
32 import com.android.wallpaper.model.WallpaperCategory
33 import com.android.wallpaper.model.WallpaperInfo
34 import com.android.wallpaper.module.DefaultCategoryProvider
35 import com.android.wallpaper.module.PartnerProvider
36 import com.android.wallpaper.util.WallpaperParser
37 import dagger.hilt.android.qualifiers.ApplicationContext
38 import java.util.Locale
39 import javax.inject.Inject
40 import javax.inject.Singleton
41 
42 /**
43  * This class is responsible for fetching wallpaper categories, listed as follows:
44  * 1. MyPhotos category that allows users to select custom photos
45  * 2. OnDevice category that are pre-loaded wallpapers on device (legacy way of pre-loading
46  *    wallpapers, modern way is described below)
47  * 3. System categories on device (modern way of pre-loading wallpapers on device)
48  * 4. Third party app categories
49  */
50 @Singleton
51 class DefaultWallpaperCategoryClientImpl
52 @Inject
53 constructor(
54     @ApplicationContext val context: Context,
55     private val partnerProvider: PartnerProvider,
56     private val wallpaperXMLParser: WallpaperParser,
57     private val liveWallpapersClient: LiveWallpapersClient
58 ) : DefaultWallpaperCategoryClient {
59 
60     private var systemCategories: List<Category>? = null
61 
62     /** This method is used for fetching and creating the MyPhotos category tile. */
63     override suspend fun getMyPhotosCategory(): Category {
64         val imageCategory = ImageCategory(
65                     context.getString(R.string.my_photos_category_title),
66                     context.getString(R.string.image_wallpaper_collection_id),
67                     PRIORITY_MY_PHOTOS_WHEN_CREATIVE_WALLPAPERS_ENABLED,
68                     R.drawable.wallpaperpicker_emptystate, /* overlayIconResId */
69             )
70         return imageCategory
71     }
72 
73     /**
74      * This method is used for fetching the on-device categories. This returns a category which
75      * incorporates both GEL and bundled wallpapers.
76      */
77     override suspend fun getOnDeviceCategory(): Category? {
78         val onDeviceWallpapers = mutableListOf<WallpaperInfo?>()
79 
80         if (!partnerProvider.shouldHideDefaultWallpaper()) {
81             val defaultWallpaperInfo = DefaultWallpaperInfo()
82             onDeviceWallpapers.add(defaultWallpaperInfo)
83         }
84 
85         val partnerWallpaperInfos = wallpaperXMLParser.parsePartnerWallpaperInfoResources()
86         onDeviceWallpapers.addAll(partnerWallpaperInfos)
87 
88         val legacyPartnerWallpaperInfos = LegacyPartnerWallpaperInfo.getAll(context)
89         onDeviceWallpapers.addAll(legacyPartnerWallpaperInfos)
90 
91         val privateWallpapers = getPrivateDeviceWallpapers()
92         privateWallpapers?.let { onDeviceWallpapers.addAll(it) }
93 
94         return onDeviceWallpapers
95             .takeIf { it.isNotEmpty() }
96             ?.let {
97                 val wallpaperCategory =
98                     WallpaperCategory(
99                         context.getString(R.string.on_device_wallpapers_category_title),
100                         context.getString(R.string.on_device_wallpaper_collection_id),
101                         it,
102                         PRIORITY_ON_DEVICE
103                     )
104                 wallpaperCategory
105             }
106     }
107 
108     override suspend fun getThirdPartyCategory
109                 (excludedPackageNames: List<String>): List<Category> {
110         val pickWallpaperIntent = Intent(Intent.ACTION_SET_WALLPAPER)
111         val apps = context.packageManager.queryIntentActivities(pickWallpaperIntent, 0)
112 
113         // Get list of image picker intents.
114         val pickImageIntent = Intent(Intent.ACTION_GET_CONTENT)
115         pickImageIntent.setType("image/*")
116         val imagePickerActivities = context.packageManager.queryIntentActivities(pickImageIntent, 0)
117 
118         val thirdPartyApps = apps.mapNotNull { info ->
119             val itemComponentName = ComponentName(info.activityInfo.packageName, info.activityInfo.name)
120             val itemPackageName = itemComponentName.packageName
121 
122             if (excludedPackageNames.contains(itemPackageName) ||
123                     itemPackageName == context.packageName ||
124                     imagePickerActivities.any { it.activityInfo.packageName == itemPackageName }) {
125                 null
126             } else {
127                 ThirdPartyAppCategory(
128                         context,
129                         info, context.getString(R.string.third_party_app_wallpaper_collection_id) + "_" + itemPackageName,
130                         PRIORITY_THIRD_PARTY
131                 )
132             }
133         }
134 
135         return thirdPartyApps
136     }
137 
138     override suspend fun getThirdPartyLiveWallpaperCategory
139                 (excludedPackageNames: Set<String>): List<Category> {
140         if (context.packageManager.hasSystemFeature(PackageManager.FEATURE_LIVE_WALLPAPER)) {
141             val liveWallpapers = liveWallpapersClient.getAll(excludedPackageNames)
142             if (liveWallpapers.isNotEmpty()) {
143                 val thirdPartyLiveWallpaperCategory = ThirdPartyLiveWallpaperCategory(
144                     context.getString(R.string.live_wallpapers_category_title),
145                     context.getString(R.string.live_wallpaper_collection_id), liveWallpapers,
146                     PRIORITY_LIVE,
147                     getExcludedLiveWallpaperPackageNames() + excludedPackageNames)
148                 return listOf(thirdPartyLiveWallpaperCategory)
149             }
150         }
151         return listOf()
152     }
153 
154     override fun getExcludedLiveWallpaperPackageNames(): Set<String> {
155         val excluded = mutableSetOf<String>()
156         systemCategories?.forEach { category ->
157             if (category is WallpaperCategory) {
158                 category.wallpapers.forEach { wallpaperInfo ->
159                     if (wallpaperInfo is LiveWallpaperInfo) {
160                         excluded.add(wallpaperInfo.wallpaperComponent.packageName)
161                     }
162                 }
163             }
164         }
165         return excluded
166     }
167 
168     override fun getExcludedThirdPartyPackageNames(): List<String> {
169         return listOf(
170                 LAUNCHER_PACKAGE,  // Legacy launcher
171                 LIVE_WALLPAPER_PICKER) // Live wallpaper picker
172     }
173 
174     /** This method is used for fetching the system categories. */
175     override suspend fun getSystemCategories(): List<Category> {
176         systemCategories?.let { return it }
177         val partnerRes = partnerProvider.resources
178         val packageName = partnerProvider.packageName
179         if (partnerRes == null || packageName == null) {
180             return listOf()
181         }
182 
183         @XmlRes val wallpapersResId =
184             partnerRes.getIdentifier(PartnerProvider.WALLPAPER_RES_ID, "xml", packageName)
185         // Certain partner configurations don't have wallpapers provided, so need to check;
186         // return early if they are missing.
187         if (wallpapersResId == 0) {
188             return listOf()
189         }
190 
191         systemCategories =
192             wallpaperXMLParser.parseSystemCategories(partnerRes.getXml(wallpapersResId))
193         return systemCategories as List<Category>
194     }
195 
196     private fun getLocale(): Locale {
197         return context.resources.configuration.locales.get(0)
198     }
199 
200     private fun getPrivateDeviceWallpapers(): Collection<WallpaperInfo?>? {
201         return null
202     }
203 
204     companion object {
205         private const val TAG = "DefaultWallpaperCategoryClientImpl"
206         private const val LAUNCHER_PACKAGE = "com.android.launcher"
207         private const val LIVE_WALLPAPER_PICKER = "com.android.wallpaper.livepicker"
208 
209         /**
210          * Relative category priorities. Lower numbers correspond to higher priorities (i.e., should
211          * appear higher in the categories list).
212          */
213         private const val PRIORITY_MY_PHOTOS_WHEN_CREATIVE_WALLPAPERS_ENABLED = 51
214         private const val PRIORITY_ON_DEVICE = 200
215         private const val PRIORITY_LIVE = 300
216         private const val PRIORITY_THIRD_PARTY = 400
217     }
218 }
219