Loading aconfig/catalyst/app_list.aconfig 0 → 100644 +9 −0 Original line number Diff line number Diff line package: "com.android.settings.flags" container: "system_ext" flag { name: "catalyst_app_list" namespace: "android_settings" description: "Basic support for App List pages" bug: "404280548" } No newline at end of file src/com/android/settings/spa/app/catalyst/AppInfoStorageScreen.kt 0 → 100644 +148 −0 Original line number Diff line number Diff line /* * Copyright (C) 2025 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.settings.spa.app.catalyst import android.content.Context import android.content.pm.ApplicationInfo import android.os.Bundle import com.android.settings.R import com.android.settings.flags.Flags import com.android.settings.spa.app.storage.StorageType import com.android.settingslib.datastore.KeyValueStore import com.android.settingslib.datastore.NoOpKeyedObservable import com.android.settingslib.metadata.PersistentPreference import com.android.settingslib.metadata.PreferenceSummaryProvider import com.android.settingslib.metadata.PreferenceTitleProvider import com.android.settingslib.metadata.ProvidePreferenceScreen import com.android.settingslib.metadata.ReadWritePermit import com.android.settingslib.metadata.SensitivityLevel import com.android.settingslib.metadata.preferenceHierarchy import com.android.settingslib.preference.PreferenceFragment import com.android.settingslib.preference.PreferenceScreenCreator import com.android.settingslib.spaprivileged.model.app.AppListRepositoryImpl import com.android.settingslib.spaprivileged.model.app.AppStorageRepositoryImpl import kotlinx.coroutines.flow.Flow import kotlinx.coroutines.flow.flow import kotlinx.coroutines.flow.map @ProvidePreferenceScreen(AppInfoStorageScreen.KEY, parameterized = true) class AppInfoStorageScreen( private val context: Context, override val arguments: Bundle ) : PreferenceScreenCreator, PreferenceSummaryProvider, PreferenceTitleProvider, PersistentPreference<Long> { private val appInfo by lazy { context.packageManager.getApplicationInfo(arguments.getString("app")!!, 0) } override val key: String get() = KEY override val valueType: Class<Long> get() = Long::class.javaObjectType override val sensitivityLevel: @SensitivityLevel Int get() = SensitivityLevel.NO_SENSITIVITY override val screenTitle: Int get() = R.string.storage_label override fun getTitle(context: Context): CharSequence? { return appInfo.loadLabel(context.packageManager).toString() } override fun getSummary(context: Context): CharSequence? { return AppStorageRepositoryImpl(context).formatSize(appInfo) } override fun isFlagEnabled(context: Context) = Flags.catalystAppList() override fun extras(context: Context): Bundle? { return Bundle(1).apply { putString(KEY_EXTRA_PACKAGE_NAME, arguments.getString("app")) } } override fun hasCompleteHierarchy() = false override fun fragmentClass() = PreferenceFragment::class.java override fun getPreferenceHierarchy(context: Context) = preferenceHierarchy(context, this) { // TODO(b/404280477): app info screen contents } override fun getReadPermit(context: Context, callingPid: Int, callingUid: Int) = ReadWritePermit.ALLOW override fun getWritePermit(context: Context, callingPid: Int, callingUid: Int) = ReadWritePermit.DISALLOW override fun storage(context: Context): KeyValueStore { return AppStorageStore(context, appInfo) } companion object { const val KEY = "app_info_storage_screen" const val KEY_EXTRA_PACKAGE_NAME = "package_name" @JvmStatic fun parameters(context: Context): Flow<Bundle> { val repo = AppListRepositoryImpl(context) val apps = flow { repo.loadAndFilterApps(context.userId, true) .filter { app -> StorageType.Apps.filter(app) }.forEach { emit(it) } } return apps.map { app -> Bundle(1).apply { putString("app", app.packageName) } } } } } private class AppStorageStore( private val context: Context, private val appInfo: ApplicationInfo ) : NoOpKeyedObservable<String>(), KeyValueStore { override fun contains(key: String): Boolean { return true } override fun <T : Any> getValue( key: String, valueType: Class<T> ): T? { return (AppStorageRepositoryImpl(context).calculateSizeBytes(appInfo) ?: 0L) as T } override fun <T : Any> setValue( key: String, valueType: Class<T>, value: T? ) { } } src/com/android/settings/spa/app/catalyst/AppsStorageScreen.kt 0 → 100644 +82 −0 Original line number Diff line number Diff line /* * Copyright (C) 2025 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.settings.spa.app.catalyst import android.content.Context import android.content.Intent import android.os.Bundle import com.android.settingslib.metadata.ProvidePreferenceScreen import com.android.settingslib.metadata.preferenceHierarchy import com.android.settingslib.preference.PreferenceFragment import com.android.settingslib.preference.PreferenceScreenCreator import com.android.settingslib.spaprivileged.model.app.AppListRepositoryImpl import com.android.settings.flags.Flags import com.android.settings.spa.app.storage.StorageType import com.android.settingslib.metadata.PreferenceMetadata import kotlinx.coroutines.flow.Flow import kotlinx.coroutines.flow.flowOf import kotlinx.coroutines.runBlocking // future improvement = parameterize this to support apps (default) and games @ProvidePreferenceScreen(AppStorageAppListScreen.KEY, parameterized = true) class AppStorageAppListScreen( override val arguments: Bundle ) : PreferenceScreenCreator { override val key: String get() = KEY override val title: Int get() = StorageType.Apps.titleResource override fun isFlagEnabled(context: Context) = Flags.catalystAppList() override fun hasCompleteHierarchy() = false override fun fragmentClass() = PreferenceFragment::class.java override fun getLaunchIntent(context: Context, metadata: PreferenceMetadata?): Intent? { return Intent("com.android.settings.APP_STORAGE_SETTINGS") .setPackage(context.packageName) } override fun getPreferenceHierarchy(context: Context) = preferenceHierarchy(context, this) { val repo = AppListRepositoryImpl(context) val apps = runBlocking { repo.loadAndFilterApps( context.userId, arguments.getBoolean(KEY_INCLUDE_SYSTEM_APPS) ) }.filter { app -> StorageType.Apps.filter(app) } for (app in apps) { addParameterizedScreen( AppInfoStorageScreen.KEY, Bundle(1).apply { putString("app", app.packageName) } ) } } companion object { const val KEY = "app_storage_app_list" const val KEY_INCLUDE_SYSTEM_APPS = "include_system" @JvmStatic fun parameters(context: Context): Flow<Bundle> { return flowOf(Bundle(1).apply { putBoolean(KEY_INCLUDE_SYSTEM_APPS, true) }) } } } src/com/android/settings/spa/app/storage/StorageAppList.kt +6 −6 Original line number Diff line number Diff line Loading @@ -60,20 +60,20 @@ sealed class StorageAppListPageProvider(private val type: StorageType) : Setting sealed class StorageType( @StringRes val titleResource: Int, val filter: (AppRecordWithSize) -> Boolean val filter: (ApplicationInfo) -> Boolean ) { object Apps : StorageType( titleResource = R.string.apps_storage, filter = { (it.app.flags and ApplicationInfo.FLAG_IS_GAME) == 0 && it.app.category != ApplicationInfo.CATEGORY_GAME (it.flags and ApplicationInfo.FLAG_IS_GAME) == 0 && it.category != ApplicationInfo.CATEGORY_GAME } ) object Games : StorageType( titleResource = R.string.game_storage_settings, filter = { (it.app.flags and ApplicationInfo.FLAG_IS_GAME) != 0 || it.app.category == ApplicationInfo.CATEGORY_GAME (it.flags and ApplicationInfo.FLAG_IS_GAME) != 0 || it.category == ApplicationInfo.CATEGORY_GAME } ) } Loading Loading @@ -120,7 +120,7 @@ class StorageAppListModel( userIdFlow: Flow<Int>, option: Int, recordListFlow: Flow<List<AppRecordWithSize>> ): Flow<List<AppRecordWithSize>> = recordListFlow.filterItem { type.filter(it) } ): Flow<List<AppRecordWithSize>> = recordListFlow.filterItem { type.filter(it.app) } @Composable override fun getSummary(option: Int, record: AppRecordWithSize): () -> String { Loading Loading
aconfig/catalyst/app_list.aconfig 0 → 100644 +9 −0 Original line number Diff line number Diff line package: "com.android.settings.flags" container: "system_ext" flag { name: "catalyst_app_list" namespace: "android_settings" description: "Basic support for App List pages" bug: "404280548" } No newline at end of file
src/com/android/settings/spa/app/catalyst/AppInfoStorageScreen.kt 0 → 100644 +148 −0 Original line number Diff line number Diff line /* * Copyright (C) 2025 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.settings.spa.app.catalyst import android.content.Context import android.content.pm.ApplicationInfo import android.os.Bundle import com.android.settings.R import com.android.settings.flags.Flags import com.android.settings.spa.app.storage.StorageType import com.android.settingslib.datastore.KeyValueStore import com.android.settingslib.datastore.NoOpKeyedObservable import com.android.settingslib.metadata.PersistentPreference import com.android.settingslib.metadata.PreferenceSummaryProvider import com.android.settingslib.metadata.PreferenceTitleProvider import com.android.settingslib.metadata.ProvidePreferenceScreen import com.android.settingslib.metadata.ReadWritePermit import com.android.settingslib.metadata.SensitivityLevel import com.android.settingslib.metadata.preferenceHierarchy import com.android.settingslib.preference.PreferenceFragment import com.android.settingslib.preference.PreferenceScreenCreator import com.android.settingslib.spaprivileged.model.app.AppListRepositoryImpl import com.android.settingslib.spaprivileged.model.app.AppStorageRepositoryImpl import kotlinx.coroutines.flow.Flow import kotlinx.coroutines.flow.flow import kotlinx.coroutines.flow.map @ProvidePreferenceScreen(AppInfoStorageScreen.KEY, parameterized = true) class AppInfoStorageScreen( private val context: Context, override val arguments: Bundle ) : PreferenceScreenCreator, PreferenceSummaryProvider, PreferenceTitleProvider, PersistentPreference<Long> { private val appInfo by lazy { context.packageManager.getApplicationInfo(arguments.getString("app")!!, 0) } override val key: String get() = KEY override val valueType: Class<Long> get() = Long::class.javaObjectType override val sensitivityLevel: @SensitivityLevel Int get() = SensitivityLevel.NO_SENSITIVITY override val screenTitle: Int get() = R.string.storage_label override fun getTitle(context: Context): CharSequence? { return appInfo.loadLabel(context.packageManager).toString() } override fun getSummary(context: Context): CharSequence? { return AppStorageRepositoryImpl(context).formatSize(appInfo) } override fun isFlagEnabled(context: Context) = Flags.catalystAppList() override fun extras(context: Context): Bundle? { return Bundle(1).apply { putString(KEY_EXTRA_PACKAGE_NAME, arguments.getString("app")) } } override fun hasCompleteHierarchy() = false override fun fragmentClass() = PreferenceFragment::class.java override fun getPreferenceHierarchy(context: Context) = preferenceHierarchy(context, this) { // TODO(b/404280477): app info screen contents } override fun getReadPermit(context: Context, callingPid: Int, callingUid: Int) = ReadWritePermit.ALLOW override fun getWritePermit(context: Context, callingPid: Int, callingUid: Int) = ReadWritePermit.DISALLOW override fun storage(context: Context): KeyValueStore { return AppStorageStore(context, appInfo) } companion object { const val KEY = "app_info_storage_screen" const val KEY_EXTRA_PACKAGE_NAME = "package_name" @JvmStatic fun parameters(context: Context): Flow<Bundle> { val repo = AppListRepositoryImpl(context) val apps = flow { repo.loadAndFilterApps(context.userId, true) .filter { app -> StorageType.Apps.filter(app) }.forEach { emit(it) } } return apps.map { app -> Bundle(1).apply { putString("app", app.packageName) } } } } } private class AppStorageStore( private val context: Context, private val appInfo: ApplicationInfo ) : NoOpKeyedObservable<String>(), KeyValueStore { override fun contains(key: String): Boolean { return true } override fun <T : Any> getValue( key: String, valueType: Class<T> ): T? { return (AppStorageRepositoryImpl(context).calculateSizeBytes(appInfo) ?: 0L) as T } override fun <T : Any> setValue( key: String, valueType: Class<T>, value: T? ) { } }
src/com/android/settings/spa/app/catalyst/AppsStorageScreen.kt 0 → 100644 +82 −0 Original line number Diff line number Diff line /* * Copyright (C) 2025 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.settings.spa.app.catalyst import android.content.Context import android.content.Intent import android.os.Bundle import com.android.settingslib.metadata.ProvidePreferenceScreen import com.android.settingslib.metadata.preferenceHierarchy import com.android.settingslib.preference.PreferenceFragment import com.android.settingslib.preference.PreferenceScreenCreator import com.android.settingslib.spaprivileged.model.app.AppListRepositoryImpl import com.android.settings.flags.Flags import com.android.settings.spa.app.storage.StorageType import com.android.settingslib.metadata.PreferenceMetadata import kotlinx.coroutines.flow.Flow import kotlinx.coroutines.flow.flowOf import kotlinx.coroutines.runBlocking // future improvement = parameterize this to support apps (default) and games @ProvidePreferenceScreen(AppStorageAppListScreen.KEY, parameterized = true) class AppStorageAppListScreen( override val arguments: Bundle ) : PreferenceScreenCreator { override val key: String get() = KEY override val title: Int get() = StorageType.Apps.titleResource override fun isFlagEnabled(context: Context) = Flags.catalystAppList() override fun hasCompleteHierarchy() = false override fun fragmentClass() = PreferenceFragment::class.java override fun getLaunchIntent(context: Context, metadata: PreferenceMetadata?): Intent? { return Intent("com.android.settings.APP_STORAGE_SETTINGS") .setPackage(context.packageName) } override fun getPreferenceHierarchy(context: Context) = preferenceHierarchy(context, this) { val repo = AppListRepositoryImpl(context) val apps = runBlocking { repo.loadAndFilterApps( context.userId, arguments.getBoolean(KEY_INCLUDE_SYSTEM_APPS) ) }.filter { app -> StorageType.Apps.filter(app) } for (app in apps) { addParameterizedScreen( AppInfoStorageScreen.KEY, Bundle(1).apply { putString("app", app.packageName) } ) } } companion object { const val KEY = "app_storage_app_list" const val KEY_INCLUDE_SYSTEM_APPS = "include_system" @JvmStatic fun parameters(context: Context): Flow<Bundle> { return flowOf(Bundle(1).apply { putBoolean(KEY_INCLUDE_SYSTEM_APPS, true) }) } } }
src/com/android/settings/spa/app/storage/StorageAppList.kt +6 −6 Original line number Diff line number Diff line Loading @@ -60,20 +60,20 @@ sealed class StorageAppListPageProvider(private val type: StorageType) : Setting sealed class StorageType( @StringRes val titleResource: Int, val filter: (AppRecordWithSize) -> Boolean val filter: (ApplicationInfo) -> Boolean ) { object Apps : StorageType( titleResource = R.string.apps_storage, filter = { (it.app.flags and ApplicationInfo.FLAG_IS_GAME) == 0 && it.app.category != ApplicationInfo.CATEGORY_GAME (it.flags and ApplicationInfo.FLAG_IS_GAME) == 0 && it.category != ApplicationInfo.CATEGORY_GAME } ) object Games : StorageType( titleResource = R.string.game_storage_settings, filter = { (it.app.flags and ApplicationInfo.FLAG_IS_GAME) != 0 || it.app.category == ApplicationInfo.CATEGORY_GAME (it.flags and ApplicationInfo.FLAG_IS_GAME) != 0 || it.category == ApplicationInfo.CATEGORY_GAME } ) } Loading Loading @@ -120,7 +120,7 @@ class StorageAppListModel( userIdFlow: Flow<Int>, option: Int, recordListFlow: Flow<List<AppRecordWithSize>> ): Flow<List<AppRecordWithSize>> = recordListFlow.filterItem { type.filter(it) } ): Flow<List<AppRecordWithSize>> = recordListFlow.filterItem { type.filter(it.app) } @Composable override fun getSummary(option: Int, record: AppRecordWithSize): () -> String { Loading