From 79ab3418000dca3dc7632ec1e2c187d7427a6623 Mon Sep 17 00:00:00 2001 From: Chaohui Wang Date: Thu, 17 Nov 2022 12:56:50 +0800 Subject: [PATCH] Refactor MoreOptionsAction Also add unit test. Bug: 259492166 Test: Unit test Change-Id: Ib79d14da794d2830a12922f47c04ddd28d448246 --- .../spa/widget/scaffold/Actions.kt | 31 ------- .../spa/widget/scaffold/MoreOptions.kt | 82 +++++++++++++++++ .../spa/widget/scaffold/MoreOptionsTest.kt | 87 +++++++++++++++++++ .../spaprivileged/template/app/AppListPage.kt | 26 +++--- 4 files changed, 182 insertions(+), 44 deletions(-) create mode 100644 packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/widget/scaffold/MoreOptions.kt create mode 100644 packages/SettingsLib/Spa/tests/src/com/android/settingslib/spa/widget/scaffold/MoreOptionsTest.kt diff --git a/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/widget/scaffold/Actions.kt b/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/widget/scaffold/Actions.kt index 764973f2a8f51..32b283e9a8a9b 100644 --- a/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/widget/scaffold/Actions.kt +++ b/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/widget/scaffold/Actions.kt @@ -17,20 +17,13 @@ package com.android.settingslib.spa.widget.scaffold import androidx.appcompat.R -import androidx.compose.foundation.layout.ColumnScope import androidx.compose.material.icons.Icons import androidx.compose.material.icons.outlined.ArrowBack import androidx.compose.material.icons.outlined.Clear import androidx.compose.material.icons.outlined.FindInPage -import androidx.compose.material.icons.outlined.MoreVert -import androidx.compose.material3.DropdownMenu import androidx.compose.material3.Icon import androidx.compose.material3.IconButton import androidx.compose.runtime.Composable -import androidx.compose.runtime.getValue -import androidx.compose.runtime.mutableStateOf -import androidx.compose.runtime.saveable.rememberSaveable -import androidx.compose.runtime.setValue import androidx.compose.ui.res.stringResource import com.android.settingslib.spa.framework.compose.LocalNavController @@ -82,27 +75,3 @@ internal fun ClearAction(onClick: () -> Unit) { ) } } - -@Composable -fun MoreOptionsAction( - content: @Composable ColumnScope.(onDismissRequest: () -> Unit) -> Unit, -) { - var expanded by rememberSaveable { mutableStateOf(false) } - MoreOptionsActionButton { expanded = true } - val onDismissRequest = { expanded = false } - DropdownMenu( - expanded = expanded, - onDismissRequest = onDismissRequest, - content = { content(onDismissRequest) }, - ) -} - -@Composable -private fun MoreOptionsActionButton(onClick: () -> Unit) { - IconButton(onClick) { - Icon( - imageVector = Icons.Outlined.MoreVert, - contentDescription = stringResource(R.string.abc_action_menu_overflow_description), - ) - } -} diff --git a/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/widget/scaffold/MoreOptions.kt b/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/widget/scaffold/MoreOptions.kt new file mode 100644 index 0000000000000..5e201dfccbd53 --- /dev/null +++ b/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/widget/scaffold/MoreOptions.kt @@ -0,0 +1,82 @@ +/* + * 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.scaffold + +import androidx.appcompat.R +import androidx.compose.foundation.layout.ColumnScope +import androidx.compose.material.icons.Icons +import androidx.compose.material.icons.outlined.MoreVert +import androidx.compose.material3.DropdownMenu +import androidx.compose.material3.DropdownMenuItem +import androidx.compose.material3.Icon +import androidx.compose.material3.IconButton +import androidx.compose.material3.Text +import androidx.compose.runtime.Composable +import androidx.compose.runtime.getValue +import androidx.compose.runtime.mutableStateOf +import androidx.compose.runtime.remember +import androidx.compose.runtime.saveable.rememberSaveable +import androidx.compose.runtime.setValue +import androidx.compose.ui.res.stringResource + +/** + * Scope for the children of [MoreOptionsAction]. + */ +interface MoreOptionsScope : ColumnScope { + fun dismiss() + + @Composable + fun MenuItem(text: String, enabled: Boolean = true, onClick: () -> Unit) { + DropdownMenuItem( + text = { Text(text) }, + onClick = { + dismiss() + onClick() + }, + enabled = enabled, + ) + } +} + +@Composable +fun MoreOptionsAction( + content: @Composable MoreOptionsScope.() -> Unit, +) { + var expanded by rememberSaveable { mutableStateOf(false) } + MoreOptionsActionButton { expanded = true } + val onDismiss = { expanded = false } + DropdownMenu(expanded = expanded, onDismissRequest = onDismiss) { + val moreOptionsScope = remember(this) { + object : MoreOptionsScope, ColumnScope by this { + override fun dismiss() { + onDismiss() + } + } + } + moreOptionsScope.content() + } +} + +@Composable +private fun MoreOptionsActionButton(onClick: () -> Unit) { + IconButton(onClick) { + Icon( + imageVector = Icons.Outlined.MoreVert, + contentDescription = stringResource(R.string.abc_action_menu_overflow_description), + ) + } +} diff --git a/packages/SettingsLib/Spa/tests/src/com/android/settingslib/spa/widget/scaffold/MoreOptionsTest.kt b/packages/SettingsLib/Spa/tests/src/com/android/settingslib/spa/widget/scaffold/MoreOptionsTest.kt new file mode 100644 index 0000000000000..019a22e38f931 --- /dev/null +++ b/packages/SettingsLib/Spa/tests/src/com/android/settingslib/spa/widget/scaffold/MoreOptionsTest.kt @@ -0,0 +1,87 @@ +/* + * 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.scaffold + +import android.content.Context +import androidx.appcompat.R +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.core.app.ApplicationProvider +import androidx.test.ext.junit.runners.AndroidJUnit4 +import com.google.common.truth.Truth.assertThat +import org.junit.Rule +import org.junit.Test +import org.junit.runner.RunWith + +@RunWith(AndroidJUnit4::class) +class MoreOptionsTest { + @get:Rule + val composeTestRule = createComposeRule() + + private val context: Context = ApplicationProvider.getApplicationContext() + + @Test + fun moreOptionsAction_collapseAtBegin() { + composeTestRule.setContent { + MoreOptionsAction { + MenuItem(text = ITEM_TEXT) {} + } + } + + composeTestRule.onNodeWithText(ITEM_TEXT).assertDoesNotExist() + } + + @Test + fun moreOptionsAction_canExpand() { + composeTestRule.setContent { + MoreOptionsAction { + MenuItem(text = ITEM_TEXT) {} + } + } + composeTestRule.onNodeWithContentDescription( + context.getString(R.string.abc_action_menu_overflow_description) + ).performClick() + + composeTestRule.onNodeWithText(ITEM_TEXT).assertIsDisplayed() + } + + @Test + fun moreOptionsAction_itemClicked() { + var menuItemClicked = false + + composeTestRule.setContent { + MoreOptionsAction { + MenuItem(text = ITEM_TEXT) { + menuItemClicked = true + } + } + } + composeTestRule.onNodeWithContentDescription( + context.getString(R.string.abc_action_menu_overflow_description) + ).performClick() + composeTestRule.onNodeWithText(ITEM_TEXT).performClick() + + assertThat(menuItemClicked).isTrue() + } + + private companion object { + const val ITEM_TEXT = "item text" + } +} 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 388a7d87a1bdf..f371ce97b16ea 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 @@ -18,8 +18,6 @@ package com.android.settingslib.spaprivileged.template.app import androidx.compose.foundation.layout.Column import androidx.compose.foundation.layout.fillMaxSize -import androidx.compose.material3.DropdownMenuItem -import androidx.compose.material3.Text import androidx.compose.runtime.Composable import androidx.compose.runtime.mutableStateOf import androidx.compose.runtime.remember @@ -27,6 +25,7 @@ import androidx.compose.runtime.saveable.rememberSaveable import androidx.compose.ui.Modifier import androidx.compose.ui.res.stringResource import com.android.settingslib.spa.widget.scaffold.MoreOptionsAction +import com.android.settingslib.spa.widget.scaffold.MoreOptionsScope import com.android.settingslib.spa.widget.scaffold.SearchScaffold import com.android.settingslib.spa.widget.ui.Spinner import com.android.settingslib.spaprivileged.R @@ -46,6 +45,7 @@ fun AppListPage( listModel: AppListModel, showInstantApps: Boolean = false, primaryUserOnly: Boolean = false, + moreOptions: @Composable MoreOptionsScope.() -> Unit = {}, header: @Composable () -> Unit = {}, appItem: @Composable (itemState: AppListItemModel) -> Unit, ) { @@ -53,7 +53,10 @@ fun AppListPage( SearchScaffold( title = title, actions = { - ShowSystemAction(showSystem.value) { showSystem.value = it } + MoreOptionsAction { + ShowSystemAction(showSystem.value) { showSystem.value = it } + moreOptions() + } }, ) { bottomPadding, searchQuery -> WorkProfilePager(primaryUserOnly) { userInfo -> @@ -82,15 +85,12 @@ fun AppListPage( } @Composable -private fun ShowSystemAction(showSystem: Boolean, setShowSystem: (showSystem: Boolean) -> Unit) { - MoreOptionsAction { onDismissRequest -> - val menuText = if (showSystem) R.string.menu_hide_system else R.string.menu_show_system - DropdownMenuItem( - text = { Text(stringResource(menuText)) }, - onClick = { - onDismissRequest() - setShowSystem(!showSystem) - }, - ) +private fun MoreOptionsScope.ShowSystemAction( + showSystem: Boolean, + setShowSystem: (showSystem: Boolean) -> Unit, +) { + val menuText = if (showSystem) R.string.menu_hide_system else R.string.menu_show_system + MenuItem(text = stringResource(menuText)) { + setShowSystem(!showSystem) } }