1 /*
2 * Copyright (C) 2023 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.settings.spa.app.storage
18
19 import android.content.Context
20 import android.content.pm.ApplicationInfo
21 import android.os.Bundle
22 import androidx.annotation.StringRes
23 import androidx.compose.runtime.Composable
24 import androidx.compose.runtime.State
25 import androidx.compose.runtime.getValue
26 import androidx.compose.runtime.remember
27 import androidx.compose.ui.platform.LocalContext
28 import androidx.compose.ui.res.stringResource
29 import com.android.settings.R
30 import com.android.settings.spa.app.appinfo.AppInfoSettingsProvider
31 import com.android.settingslib.spa.framework.common.SettingsPageProvider
32 import com.android.settingslib.spa.framework.util.filterItem
33 import com.android.settingslib.spa.framework.util.mapItem
34 import com.android.settingslib.spaprivileged.model.app.AppEntry
35 import com.android.settingslib.spaprivileged.model.app.AppListModel
36 import com.android.settingslib.spaprivileged.model.app.AppRecord
37 import com.android.settingslib.spaprivileged.template.app.AppList
38 import com.android.settingslib.spaprivileged.template.app.AppListInput
39 import com.android.settingslib.spaprivileged.template.app.AppListItem
40 import com.android.settingslib.spaprivileged.template.app.AppListItemModel
41 import com.android.settingslib.spaprivileged.template.app.AppListPage
42 import com.android.settingslib.spaprivileged.template.app.calculateSizeBytes
43 import com.android.settingslib.spaprivileged.template.app.getStorageSize
44 import kotlinx.coroutines.flow.Flow
45
46 sealed class StorageAppListPageProvider(private val type: StorageType) : SettingsPageProvider {
47 @Composable
Pagenull48 override fun Page(arguments: Bundle?) {
49 StorageAppListPage(type)
50 }
51
52 object Apps : StorageAppListPageProvider(StorageType.Apps) {
53 override val name = "StorageAppList"
54 }
55
56 object Games : StorageAppListPageProvider(StorageType.Games) {
57 override val name = "GameStorageAppList"
58 }
59 }
60
61 sealed class StorageType(
62 @StringRes val titleResource: Int,
63 val filter: (AppRecordWithSize) -> Boolean
64 ) {
65 object Apps : StorageType(
66 titleResource = R.string.apps_storage,
<lambda>null67 filter = {
68 (it.app.flags and ApplicationInfo.FLAG_IS_GAME) == 0 &&
69 it.app.category != ApplicationInfo.CATEGORY_GAME
70 }
71 )
72 object Games : StorageType(
73 titleResource = R.string.game_storage_settings,
<lambda>null74 filter = {
75 (it.app.flags and ApplicationInfo.FLAG_IS_GAME) != 0 ||
76 it.app.category == ApplicationInfo.CATEGORY_GAME
77 }
78 )
79 }
80
81 @Composable
StorageAppListPagenull82 fun StorageAppListPage(
83 type: StorageType,
84 appList: @Composable AppListInput<AppRecordWithSize>.() -> Unit = { AppList() }
85 ) {
86 val context = LocalContext.current
87 AppListPage(
88 title = stringResource(type.titleResource),
89 listModel = when (type) {
<lambda>null90 StorageType.Apps -> remember(context) { StorageAppListModel(context, type) }
<lambda>null91 StorageType.Games -> remember(context) { StorageAppListModel(context, type) }
92 },
93 showInstantApps = true,
94 matchAnyUserForAdmin = true,
95 appList = appList,
<lambda>null96 moreOptions = { }, // TODO(b/292165031) Sorting in Options not yet supported
97 )
98 }
99
100 data class AppRecordWithSize(
101 override val app: ApplicationInfo,
102 val size: Long
103 ) : AppRecord
104
105 class StorageAppListModel(
106 private val context: Context,
107 private val type: StorageType,
<lambda>null108 private val getStorageSummary: @Composable ApplicationInfo.() -> State<String> = {
109 getStorageSize()
110 }
111 ) : AppListModel<AppRecordWithSize> {
transformnull112 override fun transform(userIdFlow: Flow<Int>, appListFlow: Flow<List<ApplicationInfo>>) =
113 appListFlow.mapItem {
114 AppRecordWithSize(it, it.calculateSizeBytes(context) ?: 0L)
115 }
116
filternull117 override fun filter(
118 userIdFlow: Flow<Int>,
119 option: Int,
120 recordListFlow: Flow<List<AppRecordWithSize>>
121 ): Flow<List<AppRecordWithSize>> = recordListFlow.filterItem { type.filter(it) }
122
123 @Composable
getSummarynull124 override fun getSummary(option: Int, record: AppRecordWithSize): () -> String {
125 val storageSummary by record.app.getStorageSummary()
126 return { storageSummary }
127 }
128
129 @Composable
AppItemnull130 override fun AppListItemModel<AppRecordWithSize>.AppItem() {
131 AppListItem(onClick = AppInfoSettingsProvider.navigator(app = record.app))
132 }
133
<lambda>null134 override fun getComparator(option: Int) = compareByDescending<AppEntry<AppRecordWithSize>> {
135 it.record.size
136 }.then(super.getComparator(option))
137 }
138