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

Commit 364796eb authored by Yuchen Sun's avatar Yuchen Sun Committed by Android (Google) Code Review
Browse files

Merge "[expressive design] Create IntroPreference layout." into main

parents 65e59d9a 0832f75f
Loading
Loading
Loading
Loading
+2 −0
Original line number Diff line number Diff line
@@ -42,6 +42,7 @@ import com.android.settingslib.spa.gallery.page.LoadingBarPageProvider
import com.android.settingslib.spa.gallery.page.ProgressBarPageProvider
import com.android.settingslib.spa.gallery.scaffold.NonScrollablePagerPageProvider
import com.android.settingslib.spa.gallery.page.SliderPageProvider
import com.android.settingslib.spa.gallery.preference.IntroPreferencePageProvider
import com.android.settingslib.spa.gallery.preference.ListPreferencePageProvider
import com.android.settingslib.spa.gallery.preference.MainSwitchPreferencePageProvider
import com.android.settingslib.spa.gallery.preference.PreferenceMainPageProvider
@@ -109,6 +110,7 @@ class GallerySpaEnvironment(context: Context) : SpaEnvironment(context) {
                SuwScaffoldPageProvider,
                BannerPageProvider,
                CopyablePageProvider,
                IntroPreferencePageProvider,
            ),
            rootPages = listOf(
                HomePageProvider.createSettingsPage(),
+78 −0
Original line number Diff line number Diff line
/*
 * Copyright (C) 2024 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.preference

import android.os.Bundle
import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.outlined.AirplanemodeActive
import androidx.compose.runtime.Composable
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import com.android.settingslib.spa.framework.common.SettingsEntry
import com.android.settingslib.spa.framework.common.SettingsEntryBuilder
import com.android.settingslib.spa.framework.common.SettingsPageProvider
import com.android.settingslib.spa.framework.common.createSettingsPage
import com.android.settingslib.spa.framework.compose.navigator
import com.android.settingslib.spa.widget.preference.IntroPreference
import com.android.settingslib.spa.widget.preference.Preference
import com.android.settingslib.spa.widget.preference.PreferenceModel

private const val TITLE = "Sample IntroPreference"

object IntroPreferencePageProvider : SettingsPageProvider {
    override val name = "IntroPreference"
    private val owner = createSettingsPage()

    override fun buildEntry(arguments: Bundle?): List<SettingsEntry> {
        val entryList = mutableListOf<SettingsEntry>()
        entryList.add(
            SettingsEntryBuilder.create("IntroPreference", owner)
                .setUiLayoutFn { SampleIntroPreference() }
                .build()
        )

        return entryList
    }

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

    override fun getTitle(arguments: Bundle?): String {
        return TITLE
    }
}

@Composable
private fun SampleIntroPreference() {
    Box(modifier = Modifier.fillMaxSize(), contentAlignment = Alignment.Center) {
        IntroPreference(
            title = "Preferred network type",
            descriptions = listOf("Description"),
            imageVector = Icons.Outlined.AirplanemodeActive,
        )
    }
}
+1 −0
Original line number Diff line number Diff line
@@ -39,6 +39,7 @@ object PreferenceMainPageProvider : SettingsPageProvider {
            ListPreferencePageProvider.buildInjectEntry().setLink(fromPage = owner).build(),
            TwoTargetSwitchPreferencePageProvider.buildInjectEntry()
                .setLink(fromPage = owner).build(),
            IntroPreferencePageProvider.buildInjectEntry().setLink(fromPage = owner).build(),
        )
    }

+2 −0
Original line number Diff line number Diff line
@@ -21,6 +21,7 @@ import androidx.compose.ui.unit.dp

object SettingsDimension {
    val paddingTiny = 2.dp
    val paddingExtraSmall = 4.dp
    val paddingSmall = 4.dp
    val paddingLarge = 16.dp
    val paddingExtraLarge = 24.dp
@@ -56,6 +57,7 @@ object SettingsDimension {
    val itemDividerHeight = 32.dp

    val iconLarge = 48.dp
    val introIconSize = 40.dp

    /** The size when app icon is displayed in list. */
    val appIconItemSize = 32.dp
+145 −0
Original line number Diff line number Diff line
/*
 * Copyright (C) 2024 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 androidx.compose.foundation.background
import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.layout.size
import androidx.compose.foundation.shape.CircleShape
import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.outlined.AirplanemodeActive
import androidx.compose.material3.Icon
import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.Text
import androidx.compose.runtime.Composable
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.draw.clip
import androidx.compose.ui.graphics.vector.ImageVector
import androidx.compose.ui.text.style.TextAlign
import androidx.compose.ui.tooling.preview.Preview
import com.android.settingslib.spa.framework.theme.SettingsDimension

@Composable
fun IntroPreference(
    title: String,
    descriptions: List<String>? = null,
    imageVector: ImageVector? = null,
) {
    IntroPreference(title = title, descriptions = descriptions, icon = { IntroIcon(imageVector) })
}

@Composable
fun IntroAppPreference(
    title: String,
    descriptions: List<String>? = null,
    appIcon: @Composable (() -> Unit),
) {
    IntroPreference(title = title, descriptions = descriptions, icon = { IntroAppIcon(appIcon) })
}

@Composable
internal fun IntroPreference(
    title: String,
    descriptions: List<String>?,
    icon: @Composable (() -> Unit),
) {
    Column(
        modifier =
            Modifier.fillMaxWidth()
                .padding(
                    horizontal = SettingsDimension.paddingExtraLarge,
                    vertical = SettingsDimension.paddingLarge,
                ),
        horizontalAlignment = Alignment.CenterHorizontally,
    ) {
        icon()
        IntroTitle(title)
        IntroDescription(descriptions)
    }
}

@Composable
private fun IntroIcon(imageVector: ImageVector?) {
    if (imageVector != null) {
        Box(
            modifier =
                Modifier.size(SettingsDimension.itemIconContainerSize)
                    .clip(CircleShape)
                    .background(MaterialTheme.colorScheme.secondaryContainer),
            contentAlignment = Alignment.Center,
        ) {
            Icon(
                imageVector = imageVector,
                contentDescription = null,
                modifier = Modifier.size(SettingsDimension.introIconSize),
                tint = MaterialTheme.colorScheme.onSecondary,
            )
        }
    }
}

@Composable
private fun IntroAppIcon(appIcon: @Composable () -> Unit) {
    Box(
        modifier = Modifier.size(SettingsDimension.itemIconContainerSize).clip(CircleShape),
        contentAlignment = Alignment.Center,
    ) {
        appIcon()
    }
}

@Composable
private fun IntroTitle(title: String) {
    Box(modifier = Modifier.padding(top = SettingsDimension.paddingLarge)) {
        Text(
            text = title,
            textAlign = TextAlign.Center,
            style = MaterialTheme.typography.titleLarge,
            color = MaterialTheme.colorScheme.onSurface,
        )
    }
}

@Composable
private fun IntroDescription(descriptions: List<String>?) {
    if (descriptions != null) {
        for (description in descriptions) {
            if (description.isEmpty()) continue
            Text(
                text = description,
                textAlign = TextAlign.Center,
                style = MaterialTheme.typography.titleMedium,
                color = MaterialTheme.colorScheme.onSurfaceVariant,
                modifier = Modifier.padding(top = SettingsDimension.paddingExtraSmall),
            )
        }
    }
}

@Preview
@Composable
private fun IntroPreferencePreview() {
    IntroPreference(
        title = "Preferred network type",
        descriptions = listOf("Description", "Version"),
        imageVector = Icons.Outlined.AirplanemodeActive,
    )
}
Loading