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.Context
20 import android.content.Intent
21 import android.content.pm.ApplicationInfo
22 import android.content.pm.PackageManager
23 import android.content.pm.ResolveInfo
24 import android.service.wallpaper.WallpaperService
25 import android.util.Log
26 import com.android.wallpaper.model.WallpaperInfo
27 import com.android.wallpaper.module.InjectorProvider
28 import dagger.hilt.android.qualifiers.ApplicationContext
29 import org.xmlpull.v1.XmlPullParserException
30 import java.io.IOException
31 import java.text.Collator
32 import javax.inject.Inject
33 import javax.inject.Singleton
34 
35 /**
36  * Defines methods related to handling of live wallpapers.
37  */
38 @Singleton
39 class LiveWallpapersClientImpl @Inject constructor(@ApplicationContext val context: Context):
40     LiveWallpapersClient {
41 
42     override fun getAll(
43         excludedPackageNames: Set<String?>?
44     ): List<WallpaperInfo> {
45         val resolveInfos = getAllOnDevice()
46         val wallpaperInfos: MutableList<WallpaperInfo> = mutableListOf()
47         val factory =
48             InjectorProvider.getInjector().getLiveWallpaperInfoFactory(context)
49 
50         resolveInfos.forEach { resolveInfo ->
51             val wallpaperInfo: android.app.WallpaperInfo
52             try {
53                 wallpaperInfo = android.app.WallpaperInfo(context, resolveInfo)
54             } catch (e: XmlPullParserException) {
55                 Log.w(TAG, "Skipping wallpaper " + resolveInfo.serviceInfo, e)
56                 return@forEach
57             } catch (e: IOException) {
58                 Log.w(TAG, "Skipping wallpaper " + resolveInfo.serviceInfo, e)
59                 return@forEach
60             }
61             if (excludedPackageNames != null
62                 && excludedPackageNames.contains(wallpaperInfo.packageName)) {
63                 return@forEach
64             }
65             wallpaperInfos.add(factory.getLiveWallpaperInfo(wallpaperInfo))
66         }
67 
68         return wallpaperInfos
69     }
70 
71     /**
72      * Returns ResolveInfo objects for all live wallpaper services installed on the device. System
73      * wallpapers are listed first, unsorted, with other installed wallpapers following sorted
74      * in alphabetical order.
75      */
76     fun getAllOnDevice(): List<ResolveInfo> {
77         val pm = context.packageManager
78         val packageName = context.packageName
79 
80         val resolveInfos = pm.queryIntentServices(
81             Intent(WallpaperService.SERVICE_INTERFACE),
82             PackageManager.GET_META_DATA
83         )
84 
85         val wallpaperInfos: MutableList<ResolveInfo> = mutableListOf()
86 
87         // Remove the "Rotating Image Wallpaper" live wallpaper, which is owned by this package,
88         // and separate system wallpapers to sort only non-system ones.
89         val iter = resolveInfos.iterator()
90         while (iter.hasNext()) {
91             val resolveInfo = iter.next()
92             if (packageName == resolveInfo.serviceInfo.packageName) {
93                 iter.remove()
94             } else if (isSystemApp(resolveInfo.serviceInfo.applicationInfo)) {
95                 wallpaperInfos.add(resolveInfo)
96                 iter.remove()
97             }
98         }
99 
100         if (resolveInfos.isEmpty()) {
101             return wallpaperInfos
102         }
103 
104         // Sort non-system wallpapers alphabetically and append them to system ones
105         val collator = Collator.getInstance()
106         resolveInfos.sortWith(compareBy(collator) { it.loadLabel(pm).toString() })
107 
108         wallpaperInfos.addAll(resolveInfos)
109 
110         return wallpaperInfos
111     }
112 
113     private fun isSystemApp(appInfo: ApplicationInfo): Boolean {
114         return (appInfo.flags and (ApplicationInfo.FLAG_SYSTEM
115                 or ApplicationInfo.FLAG_UPDATED_SYSTEM_APP)) != 0 }
116 
117     companion object {
118         private const val TAG = "LiveWallpapersClient"
119     }
120 }