1 /*
2  * 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.util
18 
19 import android.content.Context
20 import android.content.res.XmlResourceParser
21 import android.util.Log
22 import android.util.Xml
23 import com.android.wallpaper.model.LiveWallpaperInfo
24 import com.android.wallpaper.model.PartnerWallpaperInfo
25 import com.android.wallpaper.model.SystemStaticWallpaperInfo
26 import com.android.wallpaper.model.WallpaperCategory
27 import com.android.wallpaper.model.WallpaperInfo
28 import com.android.wallpaper.module.PartnerProvider
29 import dagger.hilt.android.qualifiers.ApplicationContext
30 import java.io.IOException
31 import javax.inject.Inject
32 import javax.inject.Singleton
33 import org.xmlpull.v1.XmlPullParser
34 import org.xmlpull.v1.XmlPullParserException
35 
36 /**
37  * Utility class for parsing an XML file containing information about a list of wallpapers. The
38  * logic in this class has been extracted into a separate class which uses dependency injection and
39  * was earlier present in a single method.
40  */
41 @Singleton
42 class WallpaperParserImpl
43 @Inject
44 constructor(
45     @ApplicationContext private val context: Context,
46     private val partnerProvider: PartnerProvider
47 ) : WallpaperParser {
48 
49     /** This method is responsible for parsing the XML file for system categories. */
parseSystemCategoriesnull50     override fun parseSystemCategories(parser: XmlResourceParser): List<WallpaperCategory> {
51         val categories = mutableListOf<WallpaperCategory>()
52         try {
53             var priorityTracker = 0
54             val depth = parser.depth
55             var type: Int
56             while (
57                 (parser.next().also { type = it } != XmlPullParser.END_TAG ||
58                     parser.depth > depth) && type != XmlPullParser.END_DOCUMENT
59             ) {
60                 if (type == XmlPullParser.START_TAG && WallpaperCategory.TAG_NAME == parser.name) {
61                     val categoryBuilder =
62                         WallpaperCategory.Builder(
63                             partnerProvider.resources,
64                             Xml.asAttributeSet(parser)
65                         )
66                     categoryBuilder.setPriorityIfEmpty(PRIORITY_SYSTEM + priorityTracker++)
67                     var publishedPlaceholder = false
68                     val categoryDepth = parser.depth
69                     while (
70                         (parser.next().also { type = it } != XmlPullParser.END_TAG ||
71                             parser.depth > categoryDepth) && type != XmlPullParser.END_DOCUMENT
72                     ) {
73                         if (type == XmlPullParser.START_TAG) {
74                             val wallpaper =
75                                 if (SystemStaticWallpaperInfo.TAG_NAME == parser.name) {
76                                     SystemStaticWallpaperInfo.fromAttributeSet(
77                                         partnerProvider.packageName,
78                                         categoryBuilder.id,
79                                         Xml.asAttributeSet(parser)
80                                     )
81                                 } else if (LiveWallpaperInfo.TAG_NAME == parser.name) {
82                                     LiveWallpaperInfo.fromAttributeSet(
83                                         context,
84                                         categoryBuilder.id,
85                                         Xml.asAttributeSet(parser)
86                                     )
87                                 } else {
88                                     null
89                                 }
90                             if (wallpaper != null) {
91                                 categoryBuilder.addWallpaper(wallpaper)
92                                 if (!publishedPlaceholder) {
93                                     publishedPlaceholder = true
94                                 }
95                             }
96                         }
97                     }
98                     val category = categoryBuilder.build()
99                     if (!category.getUnmodifiableWallpapers().isEmpty()) {
100                         categories.add(category)
101                     }
102                 }
103             }
104         } catch (e: Exception) {
105             when (e) {
106                 is IOException,
107                 is XmlPullParserException -> {
108                     Log.w(TAG, "Failed to parse the XML file of system wallpapers", e)
109                     return emptyList()
110                 }
111                 else -> throw e
112             }
113         }
114         return categories
115     }
116 
117     /**
118      * This method is responsible for parsing resources for PartnerWallpaperInfo wallpapers and
119      * returning a list of such wallpapers.
120      */
parsePartnerWallpaperInfoResourcesnull121     override fun parsePartnerWallpaperInfoResources(): List<WallpaperInfo> {
122         val wallpaperInfos: MutableList<WallpaperInfo> = ArrayList()
123 
124         val partnerRes = partnerProvider.getResources()
125         val packageName = partnerProvider.getPackageName()
126         if (partnerRes == null) {
127             return wallpaperInfos
128         }
129 
130         val resId =
131             partnerRes.getIdentifier(PartnerProvider.LEGACY_WALLPAPER_RES_ID, "array", packageName)
132         // Certain partner configurations don't have wallpapers provided, so need to check; return
133         // early if they are missing.
134         if (resId == 0) {
135             return wallpaperInfos
136         }
137 
138         val extras = partnerRes.getStringArray(resId)
139         for (extra in extras) {
140             val wpResId = partnerRes.getIdentifier(extra, "drawable", packageName)
141             if (wpResId != 0) {
142                 val thumbRes = partnerRes.getIdentifier(extra + "_small", "drawable", packageName)
143                 if (thumbRes != 0) {
144                     val wallpaperInfo: WallpaperInfo = PartnerWallpaperInfo(thumbRes, wpResId)
145                     wallpaperInfos.add(wallpaperInfo)
146                 }
147             } else {
148                 Log.e(TAG, "Couldn't find wallpaper $extra")
149             }
150         }
151 
152         return wallpaperInfos
153     }
154 
155     companion object {
156         const val PRIORITY_SYSTEM = 100
157         private const val TAG = "WallpaperXMLParser"
158     }
159 }
160