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