Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit ca825872 authored by Chaohui Wang's avatar Chaohui Wang Committed by Android (Google) Code Review
Browse files

Merge "Create Category & CategoryTitle for SpaLib"

parents 5f931ece 54038d8a
Loading
Loading
Loading
Loading
+2 −0
Original line number Diff line number Diff line
@@ -30,6 +30,7 @@ import com.android.settingslib.spa.gallery.preference.PreferenceMainPageProvider
import com.android.settingslib.spa.gallery.preference.PreferencePageProvider
import com.android.settingslib.spa.gallery.preference.SwitchPreferencePageProvider
import com.android.settingslib.spa.gallery.preference.TwoTargetSwitchPreferencePageProvider
import com.android.settingslib.spa.gallery.ui.CategoryPageProvider
import com.android.settingslib.spa.gallery.ui.SpinnerPageProvider

object SpaEnvironment {
@@ -49,6 +50,7 @@ object SpaEnvironment {
                SettingsPagerPageProvider,
                FooterPageProvider,
                IllustrationPageProvider,
                CategoryPageProvider,
            ),
            rootPages = listOf(
                SettingsPage.create(HomePageProvider.name)
+2 −0
Original line number Diff line number Diff line
@@ -32,6 +32,7 @@ import com.android.settingslib.spa.gallery.page.IllustrationPageProvider
import com.android.settingslib.spa.gallery.page.SettingsPagerPageProvider
import com.android.settingslib.spa.gallery.page.SliderPageProvider
import com.android.settingslib.spa.gallery.preference.PreferenceMainPageProvider
import com.android.settingslib.spa.gallery.ui.CategoryPageProvider
import com.android.settingslib.spa.gallery.ui.SpinnerPageProvider
import com.android.settingslib.spa.widget.scaffold.HomeScaffold

@@ -48,6 +49,7 @@ object HomePageProvider : SettingsPageProvider {
            SettingsPagerPageProvider.buildInjectEntry().setLink(fromPage = owner).build(),
            FooterPageProvider.buildInjectEntry().setLink(fromPage = owner).build(),
            IllustrationPageProvider.buildInjectEntry().setLink(fromPage = owner).build(),
            CategoryPageProvider.buildInjectEntry().setLink(fromPage = owner).build(),
        )
    }

+96 −0
Original line number Diff line number Diff line
/*
 * 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.gallery.ui

import android.os.Bundle
import androidx.compose.runtime.Composable
import androidx.compose.runtime.remember
import androidx.compose.ui.tooling.preview.Preview
import com.android.settingslib.spa.framework.common.SettingsEntryBuilder
import com.android.settingslib.spa.framework.common.SettingsPage
import com.android.settingslib.spa.framework.common.SettingsPageProvider
import com.android.settingslib.spa.framework.compose.navigator
import com.android.settingslib.spa.framework.compose.stateOf
import com.android.settingslib.spa.framework.theme.SettingsTheme
import com.android.settingslib.spa.widget.preference.Preference
import com.android.settingslib.spa.widget.preference.PreferenceModel
import com.android.settingslib.spa.widget.scaffold.RegularScaffold
import com.android.settingslib.spa.widget.ui.Category
import com.android.settingslib.spa.widget.ui.CategoryTitle

private const val TITLE = "Sample Category"

object CategoryPageProvider : SettingsPageProvider {
    override val name = "Spinner"

    fun buildInjectEntry(): SettingsEntryBuilder {
        return SettingsEntryBuilder.createInject(owner = SettingsPage.create(name))
            .setIsAllowSearch(true)
            .setUiLayoutFn {
                Preference(object : PreferenceModel {
                    override val title = TITLE
                    override val onClick = navigator(name)
                })
            }
    }

    @Composable
    override fun Page(arguments: Bundle?) {
        CategoryPage()
    }
}

@Composable
private fun CategoryPage() {
    RegularScaffold(title = TITLE) {
        CategoryTitle("Category A")
        Preference(remember {
            object : PreferenceModel {
                override val title = "Preference 1"
                override val summary = stateOf("Summary 1")
            }
        })
        Preference(remember {
            object : PreferenceModel {
                override val title = "Preference 2"
                override val summary = stateOf("Summary 2")
            }
        })
        Category("Category B") {
            Preference(remember {
                object : PreferenceModel {
                    override val title = "Preference 3"
                    override val summary = stateOf("Summary 3")
                }
            })
            Preference(remember {
                object : PreferenceModel {
                    override val title = "Preference 4"
                    override val summary = stateOf("Summary 4")
                }
            })
        }
    }
}

@Preview(showBackground = true)
@Composable
private fun SpinnerPagePreview() {
    SettingsTheme {
        SpinnerPageProvider.Page(null)
    }
}
+78 −0
Original line number Diff line number Diff line
/*
 * 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.ui

import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.ColumnScope
import androidx.compose.foundation.layout.padding
import androidx.compose.material3.MaterialTheme
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.setValue
import androidx.compose.ui.Modifier
import androidx.compose.ui.layout.onGloballyPositioned
import androidx.compose.ui.tooling.preview.Preview
import androidx.compose.ui.unit.dp
import com.android.settingslib.spa.framework.theme.SettingsDimension
import com.android.settingslib.spa.framework.theme.SettingsTheme

/**
 * A category title that is placed before a group of similar items.
 */
@Composable
fun CategoryTitle(title: String) {
    Text(
        text = title,
        modifier = Modifier.padding(
            start = SettingsDimension.itemPaddingStart,
            top = 20.dp,
            end = SettingsDimension.itemPaddingEnd,
            bottom = 8.dp,
        ),
        color = SettingsTheme.colorScheme.categoryTitle,
        style = MaterialTheme.typography.labelMedium,
    )
}

/**
 * A container that is used to group similar items. A [Category] displays a [CategoryTitle] and
 * visually separates groups of items.
 */
@Composable
fun Category(title: String, content: @Composable ColumnScope.() -> Unit) {
    Column {
        var displayTitle by remember { mutableStateOf(false) }
        if (displayTitle) CategoryTitle(title = title)
        Column(
            modifier = Modifier.onGloballyPositioned { coordinates ->
                displayTitle = coordinates.size.height > 0
            },
            content = content,
        )
    }
}

@Preview
@Composable
private fun CategoryPreview() {
    SettingsTheme {
        CategoryTitle("Appearance")
    }
}
+70 −0
Original line number Diff line number Diff line
/*
 * 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.ui

import androidx.compose.runtime.remember
import androidx.compose.ui.test.assertIsDisplayed
import androidx.compose.ui.test.junit4.createComposeRule
import androidx.compose.ui.test.onNodeWithText
import androidx.test.ext.junit.runners.AndroidJUnit4
import com.android.settingslib.spa.framework.compose.stateOf
import com.android.settingslib.spa.widget.preference.Preference
import com.android.settingslib.spa.widget.preference.PreferenceModel
import org.junit.Rule
import org.junit.Test
import org.junit.runner.RunWith

@RunWith(AndroidJUnit4::class)
class CategoryTest {

    @get:Rule
    val composeTestRule = createComposeRule()

    @Test
    fun categoryTitle() {
        composeTestRule.setContent {
            CategoryTitle(title = "CategoryTitle")
        }

        composeTestRule.onNodeWithText("CategoryTitle").assertIsDisplayed()
    }

    @Test
    fun category_hasContent_titleDisplayed() {
        composeTestRule.setContent {
            Category(title = "CategoryTitle") {
                Preference(remember {
                    object : PreferenceModel {
                        override val title = "Some Preference"
                        override val summary = stateOf("Some summary")
                    }
                })
            }
        }

        composeTestRule.onNodeWithText("CategoryTitle").assertIsDisplayed()
    }

    @Test
    fun category_noContent_titleNotDisplayed() {
        composeTestRule.setContent {
            Category(title = "CategoryTitle") {}
        }

        composeTestRule.onNodeWithText("CategoryTitle").assertDoesNotExist()
    }
}