Create SpaPrivileged Lib

If a functionality cannot be put into Spa Lib because one of:
- Using private API
- Depends on SettingsLib

Then the functionality can be put into the SpaPrivileged Lib.

Add AppInfo widget as example.

Bug: 235727273
Test: Manual with Test App
Change-Id: I5e711e0a9067314819c7f4ba86764f25d0060239
diff --git a/packages/SettingsLib/SpaPrivileged/Android.bp b/packages/SettingsLib/SpaPrivileged/Android.bp
new file mode 100644
index 0000000..48f7ff2
--- /dev/null
+++ b/packages/SettingsLib/SpaPrivileged/Android.bp
@@ -0,0 +1,33 @@
+//
+// Copyright (C) 2022 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//      http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+//
+
+package {
+    default_applicable_licenses: ["frameworks_base_license"],
+}
+
+android_library {
+    name: "SpaPrivilegedLib",
+
+    srcs: ["src/**/*.kt"],
+
+    static_libs: [
+        "SpaLib",
+        "SettingsLib",
+        "androidx.compose.runtime_runtime",
+    ],
+    kotlincflags: ["-Xjvm-default=all"],
+    min_sdk_version: "31",
+}
diff --git a/packages/SettingsLib/SpaPrivileged/AndroidManifest.xml b/packages/SettingsLib/SpaPrivileged/AndroidManifest.xml
new file mode 100644
index 0000000..2efa107
--- /dev/null
+++ b/packages/SettingsLib/SpaPrivileged/AndroidManifest.xml
@@ -0,0 +1,18 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+  Copyright (C) 2022 The Android Open Source Project
+
+  Licensed under the Apache License, Version 2.0 (the "License");
+  you may not use this file except in compliance with the License.
+  You may obtain a copy of the License at
+
+       http://www.apache.org/licenses/LICENSE-2.0
+
+  Unless required by applicable law or agreed to in writing, software
+  distributed under the License is distributed on an "AS IS" BASIS,
+  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+  See the License for the specific language governing permissions and
+  limitations under the License.
+-->
+
+<manifest package="com.android.settingslib.spaprivileged" />
diff --git a/packages/SettingsLib/SpaPrivileged/OWNERS b/packages/SettingsLib/SpaPrivileged/OWNERS
new file mode 100644
index 0000000..9256ca5
--- /dev/null
+++ b/packages/SettingsLib/SpaPrivileged/OWNERS
@@ -0,0 +1 @@
+include platform/frameworks/base:/packages/SettingsLib/Spa/OWNERS
diff --git a/packages/SettingsLib/SpaPrivileged/src/com/android/settingslib/spaprivileged/framework/app/AppRepository.kt b/packages/SettingsLib/SpaPrivileged/src/com/android/settingslib/spaprivileged/framework/app/AppRepository.kt
new file mode 100644
index 0000000..a6378ef
--- /dev/null
+++ b/packages/SettingsLib/SpaPrivileged/src/com/android/settingslib/spaprivileged/framework/app/AppRepository.kt
@@ -0,0 +1,58 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.settingslib.spaprivileged.framework.app
+
+import android.content.Context
+import android.content.pm.ApplicationInfo
+import android.graphics.drawable.Drawable
+import androidx.compose.runtime.Composable
+import androidx.compose.runtime.State
+import androidx.compose.runtime.produceState
+import com.android.settingslib.Utils
+import com.android.settingslib.spa.framework.compose.rememberContext
+import kotlinx.coroutines.Dispatchers
+import kotlinx.coroutines.withContext
+
+@Composable
+fun rememberAppRepository(): AppRepository = rememberContext(::AppRepositoryImpl)
+
+interface AppRepository {
+    @Composable
+    fun produceLabel(app: ApplicationInfo): State<String>
+
+    @Composable
+    fun produceIcon(app: ApplicationInfo): State<Drawable?>
+}
+
+private class AppRepositoryImpl(private val context: Context) : AppRepository {
+    private val packageManager = context.packageManager
+
+    @Composable
+    override fun produceLabel(app: ApplicationInfo) = produceState(initialValue = "", app) {
+        withContext(Dispatchers.Default) {
+            value = app.loadLabel(packageManager).toString()
+        }
+    }
+
+    @Composable
+    override fun produceIcon(app: ApplicationInfo) =
+        produceState<Drawable?>(initialValue = null, app) {
+            withContext(Dispatchers.Default) {
+                value = Utils.getBadgedIcon(context, app)
+            }
+        }
+}
diff --git a/packages/SettingsLib/SpaPrivileged/src/com/android/settingslib/spaprivileged/framework/app/PackageManagers.kt b/packages/SettingsLib/SpaPrivileged/src/com/android/settingslib/spaprivileged/framework/app/PackageManagers.kt
new file mode 100644
index 0000000..5a3e666
--- /dev/null
+++ b/packages/SettingsLib/SpaPrivileged/src/com/android/settingslib/spaprivileged/framework/app/PackageManagers.kt
@@ -0,0 +1,25 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.settingslib.spaprivileged.framework.app
+
+import android.content.pm.PackageInfo
+import android.content.pm.PackageManager
+
+object PackageManagers {
+    fun getPackageInfoAsUser(packageName: String, userId: Int): PackageInfo =
+        PackageManager.getPackageInfoAsUserCached(packageName, 0, userId)
+}
diff --git a/packages/SettingsLib/SpaPrivileged/src/com/android/settingslib/spaprivileged/template/app/AppInfo.kt b/packages/SettingsLib/SpaPrivileged/src/com/android/settingslib/spaprivileged/template/app/AppInfo.kt
new file mode 100644
index 0000000..5ae514c
--- /dev/null
+++ b/packages/SettingsLib/SpaPrivileged/src/com/android/settingslib/spaprivileged/template/app/AppInfo.kt
@@ -0,0 +1,70 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.settingslib.spaprivileged.template.app
+
+import android.content.pm.ApplicationInfo
+import androidx.compose.foundation.Image
+import androidx.compose.foundation.layout.Box
+import androidx.compose.foundation.layout.Column
+import androidx.compose.foundation.layout.Spacer
+import androidx.compose.foundation.layout.fillMaxWidth
+import androidx.compose.foundation.layout.height
+import androidx.compose.foundation.layout.padding
+import androidx.compose.foundation.layout.size
+import androidx.compose.runtime.Composable
+import androidx.compose.runtime.remember
+import androidx.compose.ui.Alignment
+import androidx.compose.ui.Modifier
+import androidx.compose.ui.unit.dp
+import com.android.settingslib.spa.framework.compose.rememberDrawablePainter
+import com.android.settingslib.spa.widget.ui.SettingsBody
+import com.android.settingslib.spa.widget.ui.SettingsTitle
+import com.android.settingslib.spaprivileged.framework.app.PackageManagers
+import com.android.settingslib.spaprivileged.framework.app.rememberAppRepository
+
+@Composable
+fun AppInfo(packageName: String, userId: Int) {
+    Column(
+        modifier = Modifier
+            .fillMaxWidth()
+            .padding(16.dp),
+        horizontalAlignment = Alignment.CenterHorizontally) {
+        val packageInfo = remember { PackageManagers.getPackageInfoAsUser(packageName, userId) }
+        Box(modifier = Modifier.padding(8.dp)) {
+            AppIcon(app = packageInfo.applicationInfo, size = 48)
+        }
+        AppLabel(packageInfo.applicationInfo)
+        Spacer(modifier = Modifier.height(4.dp))
+        SettingsBody(packageInfo.versionName)
+    }
+}
+
+@Composable
+fun AppIcon(app: ApplicationInfo, size: Int) {
+    val appRepository = rememberAppRepository()
+    Image(
+        painter = rememberDrawablePainter(appRepository.produceIcon(app).value),
+        contentDescription = null,
+        modifier = Modifier.size(size.dp)
+    )
+}
+
+@Composable
+fun AppLabel(app: ApplicationInfo) {
+    val appRepository = rememberAppRepository()
+    SettingsTitle(appRepository.produceLabel(app))
+}