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
This commit is contained in:
@@ -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<String>,
|
||||
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)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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
|
||||
)
|
||||
}
|
||||
@@ -60,6 +60,7 @@ data class AppListInput<T : AppRecord>(
|
||||
val listModel: AppListModel<T>,
|
||||
val state: AppListState,
|
||||
val header: @Composable () -> Unit,
|
||||
val noItemMessage: String? = null,
|
||||
val bottomPadding: Dp,
|
||||
)
|
||||
|
||||
@@ -79,7 +80,7 @@ internal fun <T : AppRecord> AppListInput<T>.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 <T : AppRecord> AppListModel<T>.AppListWidget(
|
||||
appListData: State<AppListData<T>?>,
|
||||
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 <T : AppRecord> loadAppListData(
|
||||
) { viewModel.reloadApps() }
|
||||
|
||||
return viewModel.appListDataFlow.collectAsState(null, Dispatchers.IO)
|
||||
}
|
||||
}
|
||||
@@ -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 <T : AppRecord> AppListItemModel<T>.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
|
||||
)
|
||||
}
|
||||
@@ -45,6 +45,7 @@ fun <T : AppRecord> AppListPage(
|
||||
listModel: AppListModel<T>,
|
||||
showInstantApps: Boolean = false,
|
||||
primaryUserOnly: Boolean = false,
|
||||
noItemMessage: String? = null,
|
||||
moreOptions: @Composable MoreOptionsScope.() -> Unit = {},
|
||||
header: @Composable () -> Unit = {},
|
||||
appList: @Composable AppListInput<T>.() -> Unit = { AppList() },
|
||||
@@ -77,6 +78,7 @@ fun <T : AppRecord> AppListPage(
|
||||
),
|
||||
header = header,
|
||||
bottomPadding = bottomPadding,
|
||||
noItemMessage = noItemMessage,
|
||||
)
|
||||
appList(appListInput)
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user