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.launcher3.util
18 
19 import android.content.Context
20 import android.content.Intent
21 import android.content.pm.ApplicationInfo
22 import android.content.pm.ApplicationInfo.FLAG_EXTERNAL_STORAGE
23 import android.content.pm.ApplicationInfo.FLAG_INSTALLED
24 import android.content.pm.ApplicationInfo.FLAG_SUSPENDED
25 import android.content.pm.ApplicationInfo.FLAG_SYSTEM
26 import android.content.pm.LauncherApps
27 import android.content.pm.PackageManager
28 import android.content.pm.PackageManager.NameNotFoundException
29 import android.os.UserHandle
30 import com.android.launcher3.Flags.enableSupportForArchiving
31 import com.android.launcher3.Utilities.ATLEAST_V
32 import kotlin.LazyThreadSafetyMode.NONE
33 
34 /**
35  * A set of utility methods around ApplicationInfo with support for fetching the actual info lazily
36  */
37 class ApplicationInfoWrapper private constructor(provider: () -> ApplicationInfo?) {
38 
39     constructor(appInfo: ApplicationInfo?) : this({ appInfo })
40 
41     constructor(
42         ctx: Context,
43         pkg: String,
44         user: UserHandle,
45     ) : this({
46         try {
47             ctx.getSystemService(LauncherApps::class.java)
48                 ?.getApplicationInfo(pkg, PackageManager.MATCH_UNINSTALLED_PACKAGES, user)
49                 ?.let { ai ->
50                     // its enabled and (either installed or archived)
51                     if (
52                         ai.enabled &&
53                             (ai.flags.and(FLAG_INSTALLED) != 0 ||
54                                 (ATLEAST_V && enableSupportForArchiving() && ai.isArchived))
55                     ) {
56                         ai
57                     } else {
58                         null
59                     }
60                 }
61         } catch (e: NameNotFoundException) {
62             null
63         }
64     })
65 
66     constructor(
67         ctx: Context,
68         intent: Intent,
69     ) : this(
70         provider@{
71             try {
72                 val pm = ctx.packageManager
73                 val packageName: String =
74                     intent.component?.packageName
75                         ?: intent.getPackage()
76                         ?: return@provider pm.resolveActivity(
77                                 intent,
78                                 PackageManager.MATCH_DEFAULT_ONLY,
79                             )
80                             ?.activityInfo
81                             ?.applicationInfo
82                 pm.getApplicationInfo(packageName, 0)
83             } catch (e: NameNotFoundException) {
84                 null
85             }
86         }
87     )
88 
89     private val appInfo: ApplicationInfo? by lazy(NONE, provider)
90 
91     private fun hasFlag(flag: Int) = appInfo?.let { it.flags.and(flag) != 0 } ?: false
92 
93     /**
94      * Returns true if the app can possibly be on the SDCard. This is just a workaround and doesn't
95      * guarantee that the app is on SD card.
96      */
97     fun isOnSdCard() = hasFlag(FLAG_EXTERNAL_STORAGE)
98 
99     /** Returns whether the target app is installed for a given user */
100     fun isInstalled() = hasFlag(FLAG_INSTALLED)
101 
102     /**
103      * Returns whether the target app is suspended for a given user as per
104      * [android.app.admin.DevicePolicyManager.isPackageSuspended].
105      */
106     fun isSuspended() = hasFlag(FLAG_INSTALLED) && hasFlag(FLAG_SUSPENDED)
107 
108     /** Returns whether the target app is archived for a given user */
109     fun isArchived() = ATLEAST_V && enableSupportForArchiving() && appInfo?.isArchived ?: false
110 
111     /** Returns whether the target app is a system app */
112     fun isSystem() = hasFlag(FLAG_SYSTEM)
113 
114     fun getInfo(): ApplicationInfo? = appInfo
115 }
116