1 /*
2  * Copyright (C) 2022 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.settingslib.spaprivileged.template.app
18 
19 import android.content.pm.ApplicationInfo
20 import android.content.pm.PackageInfo
21 import android.text.BidiFormatter
22 import androidx.compose.foundation.Image
23 import androidx.compose.foundation.layout.Box
24 import androidx.compose.foundation.layout.Column
25 import androidx.compose.foundation.layout.Spacer
26 import androidx.compose.foundation.layout.fillMaxWidth
27 import androidx.compose.foundation.layout.height
28 import androidx.compose.foundation.layout.padding
29 import androidx.compose.foundation.layout.size
30 import androidx.compose.material3.HorizontalDivider
31 import androidx.compose.runtime.Composable
32 import androidx.compose.runtime.remember
33 import androidx.compose.ui.Alignment
34 import androidx.compose.ui.Modifier
35 import androidx.compose.ui.platform.LocalContext
36 import androidx.compose.ui.res.stringResource
37 import androidx.compose.ui.semantics.semantics
38 import androidx.compose.ui.unit.Dp
39 import com.android.settingslib.development.DevelopmentSettingsEnabler
40 import com.android.settingslib.spa.framework.compose.rememberDrawablePainter
41 import com.android.settingslib.spa.framework.theme.SettingsDimension
42 import com.android.settingslib.spa.widget.ui.CopyableBody
43 import com.android.settingslib.spa.widget.ui.SettingsBody
44 import com.android.settingslib.spa.widget.ui.SettingsTitle
45 import com.android.settingslib.spaprivileged.R
46 import com.android.settingslib.spaprivileged.model.app.rememberAppRepository
47 
48 class AppInfoProvider(private val packageInfo: PackageInfo) {
49     @Composable
AppInfonull50     fun AppInfo(displayVersion: Boolean = false, isClonedAppPage: Boolean = false) {
51         Column(
52             modifier = Modifier
53                 .fillMaxWidth()
54                 .padding(
55                     horizontal = SettingsDimension.itemPaddingStart,
56                     vertical = SettingsDimension.itemPaddingVertical,
57                 )
58                 .semantics(mergeDescendants = true) {},
59             horizontalAlignment = Alignment.CenterHorizontally,
60         ) {
61             val app = checkNotNull(packageInfo.applicationInfo)
62             Box(modifier = Modifier.padding(SettingsDimension.itemPaddingAround)) {
63                 AppIcon(app = app, size = SettingsDimension.appIconInfoSize)
64             }
65             AppLabel(app, isClonedAppPage)
66             InstallType(app)
67             if (displayVersion) AppVersion()
68         }
69     }
70 
71     @Composable
InstallTypenull72     private fun InstallType(app: ApplicationInfo) {
73         if (!app.isInstantApp) return
74         Spacer(modifier = Modifier.height(SettingsDimension.paddingSmall))
75         SettingsBody(
76             stringResource(
77                 com.android.settingslib.widget.preference.app.R.string.install_type_instant
78             )
79         )
80     }
81 
82     @Composable
AppVersionnull83     private fun AppVersion() {
84         val versionName = packageInfo.versionNameBidiWrapped ?: return
85         Spacer(modifier = Modifier.height(SettingsDimension.paddingSmall))
86         SettingsBody(versionName)
87     }
88 
89     @Composable
FooterAppVersionnull90     fun FooterAppVersion(showPackageName: Boolean = rememberIsDevelopmentSettingsEnabled()) {
91         val context = LocalContext.current
92         val footer = remember(packageInfo, showPackageName) {
93             val list = mutableListOf<String>()
94             packageInfo.versionNameBidiWrapped?.let {
95                 list += context.getString(R.string.version_text, it)
96             }
97             if (showPackageName) {
98                 list += packageInfo.packageName
99             }
100             list.joinToString(separator = System.lineSeparator())
101         }
102         if (footer.isBlank()) return
103         HorizontalDivider()
104         Column(modifier = Modifier.padding(SettingsDimension.itemPadding)) {
105             CopyableBody(footer)
106         }
107     }
108 
109     @Composable
rememberIsDevelopmentSettingsEnablednull110     private fun rememberIsDevelopmentSettingsEnabled(): Boolean {
111         val context = LocalContext.current
112         return remember {
113             DevelopmentSettingsEnabler.isDevelopmentSettingsEnabled(context)
114         }
115     }
116 
117     private companion object {
118         /** Wrapped the version name, so its directionality still keep same when RTL. */
119         val PackageInfo.versionNameBidiWrapped: String?
120             get() = BidiFormatter.getInstance().unicodeWrap(versionName)
121     }
122 }
123 
124 @Composable
AppIconnull125 internal fun AppIcon(app: ApplicationInfo, size: Dp) {
126     val appRepository = rememberAppRepository()
127     Image(
128         painter = rememberDrawablePainter(appRepository.produceIcon(app).value),
129         contentDescription = appRepository.produceIconContentDescription(app).value,
130         modifier = Modifier.size(size),
131     )
132 }
133 
134 @Composable
AppLabelnull135 internal fun AppLabel(app: ApplicationInfo, isClonedAppPage: Boolean = false) {
136     val appRepository = rememberAppRepository()
137     SettingsTitle(appRepository.produceLabel(app, isClonedAppPage).value, useMediumWeight = true)
138 }
139