From 25773378b7deb3b027f5f421350bc892a16a69d6 Mon Sep 17 00:00:00 2001 From: George Chan Date: Thu, 1 Dec 2022 18:29:49 +0000 Subject: [PATCH] Updated AppList to allow custom message and display summary for empty list, added a button appItem as part of requirements for background install control feature. Change-Id: I3f2129e4787d3143c61377679d6b4f0c02d5133e Test: Manually tested with Settings app Bug: 238451991 --- .../preference/TwoTargetButtonPreference.kt | 47 ++++++++++++ .../TwoTargetButtonPreferenceTest.kt | 73 +++++++++++++++++++ .../spaprivileged/template/app/AppList.kt | 9 ++- .../template/app/AppListButtonItem.kt | 41 +++++++++++ .../spaprivileged/template/app/AppListPage.kt | 2 + 5 files changed, 169 insertions(+), 3 deletions(-) create mode 100644 packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/widget/preference/TwoTargetButtonPreference.kt create mode 100644 packages/SettingsLib/Spa/tests/src/com/android/settingslib/spa/widget/preference/TwoTargetButtonPreferenceTest.kt create mode 100644 packages/SettingsLib/SpaPrivileged/src/com/android/settingslib/spaprivileged/template/app/AppListButtonItem.kt diff --git a/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/widget/preference/TwoTargetButtonPreference.kt b/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/widget/preference/TwoTargetButtonPreference.kt new file mode 100644 index 0000000000000..b8db63c9658e6 --- /dev/null +++ b/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/widget/preference/TwoTargetButtonPreference.kt @@ -0,0 +1,47 @@ +/* + * 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.spa.widget.preference + +import com.android.settingslib.spa.framework.util.EntryHighlight +import androidx.compose.material3.IconButton +import androidx.compose.runtime.State +import androidx.compose.runtime.Composable +import androidx.compose.ui.graphics.vector.ImageVector +import androidx.compose.material3.Icon + +@Composable +fun TwoTargetButtonPreference( + title: String, + summary: State, + icon: @Composable (() -> Unit)? = null, + onClick: () -> Unit, + buttonIcon: ImageVector, + buttonIconDescription: String, + onButtonClick: () -> Unit +) { + EntryHighlight { + TwoTargetPreference( + title = title, + summary = summary, + onClick = onClick, + icon = icon) { + IconButton(onClick = onButtonClick) { + Icon(imageVector = buttonIcon, contentDescription = buttonIconDescription) + } + } + } +} \ No newline at end of file diff --git a/packages/SettingsLib/Spa/tests/src/com/android/settingslib/spa/widget/preference/TwoTargetButtonPreferenceTest.kt b/packages/SettingsLib/Spa/tests/src/com/android/settingslib/spa/widget/preference/TwoTargetButtonPreferenceTest.kt new file mode 100644 index 0000000000000..3a2b445e21487 --- /dev/null +++ b/packages/SettingsLib/Spa/tests/src/com/android/settingslib/spa/widget/preference/TwoTargetButtonPreferenceTest.kt @@ -0,0 +1,73 @@ +package com.android.settingslib.spa.widget.preference + +import androidx.compose.material.icons.Icons +import androidx.compose.material.icons.outlined.Delete +import androidx.compose.runtime.Composable +import androidx.compose.ui.test.assertIsDisplayed +import androidx.compose.ui.test.junit4.createComposeRule +import androidx.compose.ui.test.onNodeWithContentDescription +import androidx.compose.ui.test.onNodeWithText +import androidx.compose.ui.test.performClick +import androidx.test.ext.junit.runners.AndroidJUnit4 +import com.android.settingslib.spa.framework.compose.toState +import com.google.common.truth.Truth +import org.junit.Rule +import org.junit.Test +import org.junit.runner.RunWith + +private const val TEST_MODEL_TITLE = "TwoTargetButtonPreference" +private const val TEST_MODEL_SUMMARY = "TestSummary" +private const val TEST_BUTTON_ICON_DESCRIPTION = "TestButtonIconDescription" +private val TEST_BUTTON_ICON = Icons.Outlined.Delete + +@RunWith(AndroidJUnit4::class) +class TwoTargetButtonPreferenceTest { + @get:Rule + val composeTestRule = createComposeRule() + + @Test + fun title_displayed() { + composeTestRule.setContent { + testTwoTargetButtonPreference() + } + + composeTestRule.onNodeWithText(TEST_MODEL_TITLE).assertIsDisplayed() + } + + @Test + fun clickable_label_canBeClicked() { + var clicked = false + composeTestRule.setContent { + testTwoTargetButtonPreference(onClick = { clicked = true }) + } + + composeTestRule.onNodeWithText(TEST_MODEL_TITLE).performClick() + Truth.assertThat(clicked).isTrue() + } + + @Test + fun clickable_button_label_canBeClicked() { + var clicked = false + composeTestRule.setContent { + testTwoTargetButtonPreference(onButtonClick = { clicked = true }) + } + + composeTestRule.onNodeWithContentDescription(TEST_BUTTON_ICON_DESCRIPTION).performClick() + Truth.assertThat(clicked).isTrue() + } +} + +@Composable +private fun testTwoTargetButtonPreference( + onClick: () -> Unit = {}, + onButtonClick: () -> Unit = {}, +) { + TwoTargetButtonPreference( + title = TEST_MODEL_TITLE, + summary = TEST_MODEL_SUMMARY.toState(), + onClick = onClick, + buttonIcon = TEST_BUTTON_ICON, + buttonIconDescription = TEST_BUTTON_ICON_DESCRIPTION, + onButtonClick = onButtonClick + ) +} \ No newline at end of file diff --git a/packages/SettingsLib/SpaPrivileged/src/com/android/settingslib/spaprivileged/template/app/AppList.kt b/packages/SettingsLib/SpaPrivileged/src/com/android/settingslib/spaprivileged/template/app/AppList.kt index deec267454d63..3ff1d897ad6fa 100644 --- a/packages/SettingsLib/SpaPrivileged/src/com/android/settingslib/spaprivileged/template/app/AppList.kt +++ b/packages/SettingsLib/SpaPrivileged/src/com/android/settingslib/spaprivileged/template/app/AppList.kt @@ -60,6 +60,7 @@ data class AppListInput( val listModel: AppListModel, val state: AppListState, val header: @Composable () -> Unit, + val noItemMessage: String? = null, val bottomPadding: Dp, ) @@ -79,7 +80,7 @@ internal fun AppListInput.AppListImpl( ) { LogCompositions(TAG, config.userId.toString()) val appListData = appListDataSupplier() - listModel.AppListWidget(appListData, header, bottomPadding) + listModel.AppListWidget(appListData, header, bottomPadding, noItemMessage) } @Composable @@ -87,12 +88,14 @@ private fun AppListModel.AppListWidget( appListData: State?>, header: @Composable () -> Unit, bottomPadding: Dp, + noItemMessage: String? ) { val timeMeasurer = rememberTimeMeasurer(TAG) appListData.value?.let { (list, option) -> timeMeasurer.logFirst("app list first loaded") if (list.isEmpty()) { - PlaceholderTitle(stringResource(R.string.no_applications)) + header() + PlaceholderTitle(noItemMessage ?: stringResource(R.string.no_applications)) return } LazyColumn( @@ -151,4 +154,4 @@ private fun loadAppListData( ) { viewModel.reloadApps() } return viewModel.appListDataFlow.collectAsState(null, Dispatchers.IO) -} +} \ No newline at end of file diff --git a/packages/SettingsLib/SpaPrivileged/src/com/android/settingslib/spaprivileged/template/app/AppListButtonItem.kt b/packages/SettingsLib/SpaPrivileged/src/com/android/settingslib/spaprivileged/template/app/AppListButtonItem.kt new file mode 100644 index 0000000000000..919793a0abfa0 --- /dev/null +++ b/packages/SettingsLib/SpaPrivileged/src/com/android/settingslib/spaprivileged/template/app/AppListButtonItem.kt @@ -0,0 +1,41 @@ +/* + * 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 androidx.compose.runtime.Composable +import androidx.compose.ui.graphics.vector.ImageVector +import com.android.settingslib.spa.framework.theme.SettingsDimension +import com.android.settingslib.spaprivileged.model.app.AppRecord +import com.android.settingslib.spa.widget.preference.TwoTargetButtonPreference + +@Composable +fun AppListItemModel.AppListButtonItem ( + onClick: () -> Unit, + onButtonClick: () -> Unit, + buttonIcon: ImageVector, + buttonIconDescription: String, +) { + TwoTargetButtonPreference( + title = label, + summary = this@AppListButtonItem.summary, + icon = { AppIcon(record.app, SettingsDimension.appIconItemSize) }, + onClick = onClick, + buttonIcon = buttonIcon, + buttonIconDescription = buttonIconDescription, + onButtonClick = onButtonClick + ) +} diff --git a/packages/SettingsLib/SpaPrivileged/src/com/android/settingslib/spaprivileged/template/app/AppListPage.kt b/packages/SettingsLib/SpaPrivileged/src/com/android/settingslib/spaprivileged/template/app/AppListPage.kt index 318bcd93d39d5..7d21d98820d35 100644 --- a/packages/SettingsLib/SpaPrivileged/src/com/android/settingslib/spaprivileged/template/app/AppListPage.kt +++ b/packages/SettingsLib/SpaPrivileged/src/com/android/settingslib/spaprivileged/template/app/AppListPage.kt @@ -45,6 +45,7 @@ fun AppListPage( listModel: AppListModel, showInstantApps: Boolean = false, primaryUserOnly: Boolean = false, + noItemMessage: String? = null, moreOptions: @Composable MoreOptionsScope.() -> Unit = {}, header: @Composable () -> Unit = {}, appList: @Composable AppListInput.() -> Unit = { AppList() }, @@ -77,6 +78,7 @@ fun AppListPage( ), header = header, bottomPadding = bottomPadding, + noItemMessage = noItemMessage, ) appList(appListInput) }