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

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

Merge "Defines SettingsEntry and use it in ArgumentPage"

parents 5dcb8aad df0103f8
Loading
Loading
Loading
Loading
+85 −30
Original line number Original line Diff line number Diff line
@@ -21,6 +21,10 @@ import androidx.compose.runtime.Composable
import androidx.compose.ui.tooling.preview.Preview
import androidx.compose.ui.tooling.preview.Preview
import androidx.navigation.NavType
import androidx.navigation.NavType
import androidx.navigation.navArgument
import androidx.navigation.navArgument
import com.android.settingslib.spa.framework.common.PageArguments
import com.android.settingslib.spa.framework.common.SettingsEntry
import com.android.settingslib.spa.framework.common.SettingsEntryBuilder
import com.android.settingslib.spa.framework.common.SettingsPageBuilder
import com.android.settingslib.spa.framework.common.SettingsPageProvider
import com.android.settingslib.spa.framework.common.SettingsPageProvider
import com.android.settingslib.spa.framework.compose.navigator
import com.android.settingslib.spa.framework.compose.navigator
import com.android.settingslib.spa.framework.compose.toState
import com.android.settingslib.spa.framework.compose.toState
@@ -41,41 +45,72 @@ object ArgumentPageProvider : SettingsPageProvider {
        navArgument(INT_PARAM_NAME) { type = NavType.IntType },
        navArgument(INT_PARAM_NAME) { type = NavType.IntType },
    )
    )


    @Composable
    override fun buildEntry(arguments: Bundle?): List<SettingsEntry> {
    override fun Page(arguments: Bundle?) {
        val pageArgs = PageArguments(ArgumentPageProvider.arguments, arguments)
        ArgumentPage(
        if (!pageArgs.isValid()) return emptyList()
            stringParam = arguments!!.getString(STRING_PARAM_NAME, "default"),
            intParam = arguments.getInt(INT_PARAM_NAME),
        )
    }


    @Composable
        val owner = SettingsPageBuilder.create(name, pageArgs.normalize()).build()
    fun EntryItem(stringParam: String, intParam: Int) {
        val entryList = mutableListOf<SettingsEntry>()
        val stringParamEntry = SettingsEntryBuilder.create("string_param", owner)
        stringParamEntry.setUiLayoutFn {
            Preference(object : PreferenceModel {
            Preference(object : PreferenceModel {
            override val title = TITLE
                override val title = "String param value"
            override val summary =
                override val summary = pageArgs.getStringArg(STRING_PARAM_NAME)!!.toState()
                "$STRING_PARAM_NAME=$stringParam, $INT_PARAM_NAME=$intParam".toState()
            override val onClick = navigator("$name/$stringParam/$intParam")
            })
            })
        }
        }
}
        entryList.add(stringParamEntry.build())


@Composable
        val intParamEntry = SettingsEntryBuilder.create("int_param", owner)
fun ArgumentPage(stringParam: String, intParam: Int) {
        intParamEntry.setUiLayoutFn {
    RegularScaffold(title = TITLE) {
            Preference(object : PreferenceModel {
            Preference(object : PreferenceModel {
            override val title = "String param value"
                override val title = "Int param value"
            override val summary = stringParam.toState()
                override val summary = pageArgs.getIntArg(INT_PARAM_NAME)!!.toString().toState()
            })
            })
        }
        entryList.add(intParamEntry.build())

        val entryFoo = buildInjectEntry(pageArgs.buildNextArg("foo"))
        val entryBar = buildInjectEntry(pageArgs.buildNextArg("bar"))
        if (entryFoo != null) entryList.add(entryFoo.setLink(fromPage = owner).build())
        if (entryBar != null) entryList.add(entryBar.setLink(fromPage = owner).build())


        return entryList
    }

    private fun buildInjectEntry(arguments: Bundle?): SettingsEntryBuilder? {
        val pageArgs = PageArguments(ArgumentPageProvider.arguments, arguments)
        if (!pageArgs.isValid()) return null

        val seBuilder = SettingsEntryBuilder.createLinkTo("injection", name, pageArgs.normalize())
        seBuilder.setIsAllowSearch(false)

        seBuilder.setUiLayoutFn {
            val summaryArray = listOf(
                "$STRING_PARAM_NAME=" + pageArgs.getStringArg(STRING_PARAM_NAME)!!,
                "$INT_PARAM_NAME=" + pageArgs.getIntArg(INT_PARAM_NAME)!!
            )
            Preference(object : PreferenceModel {
            Preference(object : PreferenceModel {
            override val title = "Int param value"
                override val title = TITLE
            override val summary = intParam.toString().toState()
                override val summary = summaryArray.joinToString(", ").toState()
                override val onClick = navigator(name + pageArgs.navLink())
            })
            })
        }


        ArgumentPageProvider.EntryItem(stringParam = "foo", intParam = intParam + 1)
        return seBuilder
    }


        ArgumentPageProvider.EntryItem(stringParam = "bar", intParam = intParam + 1)
    @Composable
    override fun Page(arguments: Bundle?) {
        RegularScaffold(title = TITLE) {
            for (entry in buildEntry(arguments)) {
                entry.uiLayout()
            }
        }
    }

    @Composable
    fun EntryItem(stringParam: String, intParam: Int) {
        buildInjectEntry(buildArgument(stringParam, intParam))?.build()?.uiLayout?.let { it() }
    }
    }
}
}


@@ -83,6 +118,26 @@ fun ArgumentPage(stringParam: String, intParam: Int) {
@Composable
@Composable
private fun ArgumentPagePreview() {
private fun ArgumentPagePreview() {
    SettingsTheme {
    SettingsTheme {
        ArgumentPage(stringParam = "foo", intParam = 0)
        ArgumentPageProvider.Page(buildArgument(stringParam = "foo", intParam = 0))
    }
    }
}
}

private fun PageArguments.isValid(): Boolean {
    val stringParam = getStringArg(STRING_PARAM_NAME)
    return (stringParam != null && listOf("foo", "bar").contains(stringParam))
}

private fun PageArguments.buildNextArg(stringParam: String): Bundle {
    val intParam = getIntArg(INT_PARAM_NAME)
    return if (intParam == null)
        buildArgument(stringParam)
    else
        buildArgument(stringParam, intParam + 1)
}

private fun buildArgument(stringParam: String, intParam: Int? = null): Bundle {
    val args = Bundle()
    args.putString(STRING_PARAM_NAME, stringParam)
    if (intParam != null) args.putInt(INT_PARAM_NAME, intParam)
    return args
}
+74 −0
Original line number Original line 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.framework.common

import android.os.Bundle
import androidx.navigation.NamedNavArgument
import androidx.navigation.NavType

class PageArguments(
    private val navArgsDef: List<NamedNavArgument>,
    private val rawArgs: Bundle?
) {
    fun normalize(): Bundle {
        if (rawArgs == null) return Bundle.EMPTY
        val normArgs = Bundle()
        for (navArg in navArgsDef) {
            when (navArg.argument.type) {
                NavType.StringType -> {
                    val value = rawArgs.getString(navArg.name)
                    if (value != null) normArgs.putString(navArg.name, value)
                }
                NavType.IntType -> {
                    if (rawArgs.containsKey(navArg.name))
                        normArgs.putInt(navArg.name, rawArgs.getInt(navArg.name))
                }
            }
        }
        return normArgs
    }

    fun navLink(): String{
        if (rawArgs == null) return ""
        val argsArray = mutableListOf<String>()
        for (navArg in navArgsDef) {
            when (navArg.argument.type) {
                NavType.StringType -> {
                    argsArray.add(rawArgs.getString(navArg.name, ""))
                }
                NavType.IntType -> {
                    argsArray.add(rawArgs.getInt(navArg.name).toString())
                }
            }
        }
        return argsArray.joinToString("") {arg -> "/$arg" }
    }

    fun getStringArg(key: String): String? {
        if (rawArgs != null && rawArgs.containsKey(key)) {
            return rawArgs.getString(key)
        }
        return null
    }

    fun getIntArg(key: String): Int? {
        if (rawArgs != null && rawArgs.containsKey(key)) {
            return rawArgs.getInt(key)
        }
        return null
    }
}
+205 −0
Original line number Original line 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.framework.common

import android.os.Bundle
import androidx.compose.runtime.Composable

/**
 * Defines data of one Settings entry for Settings search.
 */
data class SearchData(val keyword: String = "")

/**
 * Defines data of one Settings entry for UI rendering.
 */
data class UiData(val title: String = "")

/**
 * Defines data to identify a Settings page.
 */
data class SettingsPage(val name: String = "", val args: Bundle = Bundle.EMPTY)

/**
 * Defines data of a Settings entry.
 */
data class SettingsEntry(
    val name: String,
    val owner: SettingsPage,

    // Generates the unique id of this entry
    val uniqueId: String,

    // Defines linking of Settings entries
    val fromPage: SettingsPage? = null,
    val toPage: SettingsPage? = null,

    /**
     * ========================================
     * Defines entry attributes here.
     * ========================================
     */
    val isAllowSearch: Boolean,

    /**
     * ========================================
     * Defines entry APIs to get data here.
     * ========================================
     */

    /**
     * API to get Search related data for this entry.
     * Returns null if this entry is not available for the search at the moment.
     */
    val searchData: () -> SearchData? = { null },

    /**
     * API to get UI related data for this entry.
     * Returns null if the entry is not render-able.
     */
    val uiData: () -> UiData? = { null },

    /**
     * API to Render UI of this entry directly. For now, we use it in the internal injection, to
     * support the case that the injection page owner wants to maintain both data and UI of the
     * injected entry. In the long term, we may deprecate the @Composable Page() API in SPP, and
     * use each entries' UI rendering function in the page instead.
     */
    val uiLayout: (@Composable () -> Unit) = {},
)

/**
 * The helper to build a Settings Page instance.
 */
class SettingsPageBuilder(private val name: String) {
    private val arguments = Bundle()

    fun pushArgs(args: Bundle?): SettingsPageBuilder {
        if (args != null) arguments.putAll(args)
        return this
    }

    fun pushArg(argName: String, argValue: String?): SettingsPageBuilder {
        if (argValue != null) this.arguments.putString(argName, argValue)
        return this
    }

    fun pushArg(argName: String, argValue: Int?): SettingsPageBuilder {
        if (argValue != null) this.arguments.putInt(argName, argValue)
        return this
    }

    fun build(): SettingsPage {
        return SettingsPage(name, arguments)
    }

    companion object {
        fun create(name: String, args: Bundle? = null): SettingsPageBuilder {
            return SettingsPageBuilder(name).apply {
                pushArgs(args)
            }
        }
    }
}

/**
 * The helper to build a Settings Entry instance.
 */
class SettingsEntryBuilder(private val name: String, private val owner: SettingsPage) {
    private var uniqueId: String? = null
    private var fromPage: SettingsPage? = null
    private var toPage: SettingsPage? = null
    private var isAllowSearch: Boolean? = null

    private var searchDataFn: () -> SearchData? = { null }
    private var uiLayoutFn: (@Composable () -> Unit) = {}

    fun build(): SettingsEntry {
        return SettingsEntry(
            name = name,
            owner = owner,
            uniqueId = computeUniqueId(),

            // linking data
            fromPage = fromPage,
            toPage = toPage,

            // attributes
            isAllowSearch = computeSearchable(),

            // functions
            searchData = searchDataFn,
            uiLayout = uiLayoutFn,
        )
    }

    fun setLink(
        fromPage: SettingsPage? = null,
        toPage: SettingsPage? = null
    ): SettingsEntryBuilder {
        if (fromPage != null) this.fromPage = fromPage
        if (toPage != null) this.toPage = toPage
        return this
    }

    fun setIsAllowSearch(isAllowSearch: Boolean): SettingsEntryBuilder {
        this.isAllowSearch = isAllowSearch
        return this
    }

    fun setSearchDataFn(fn: () -> SearchData?): SettingsEntryBuilder {
        this.searchDataFn = fn
        return this
    }

    fun setUiLayoutFn(fn: @Composable () -> Unit): SettingsEntryBuilder {
        this.uiLayoutFn = fn
        return this
    }

    private fun computeUniqueId(): String = uniqueId ?: (name + owner.toString())

    private fun computeSearchable(): Boolean = isAllowSearch ?: false

    companion object {
        fun create(
            entryName: String,
            ownerPageName: String,
            ownerPageArgs: Bundle? = null
        ): SettingsEntryBuilder {
            val owner = SettingsPageBuilder.create(ownerPageName, ownerPageArgs)
            return SettingsEntryBuilder(entryName, owner.build())
        }

        fun create(entryName: String, owner: SettingsPage): SettingsEntryBuilder {
            return SettingsEntryBuilder(entryName, owner)
        }

        fun createLinkTo(
            entryName: String,
            ownerPageName: String,
            ownerPageArgs: Bundle? = null
        ): SettingsEntryBuilder {
            val owner = SettingsPageBuilder.create(ownerPageName, ownerPageArgs)
            return SettingsEntryBuilder(entryName, owner.build()).setLink(toPage = owner.build())
        }

        fun createLinkTo(entryName: String, owner: SettingsPage): SettingsEntryBuilder {
            return SettingsEntryBuilder(entryName, owner).setLink(toPage = owner)
        }
    }
}
+1 −1
Original line number Original line Diff line number Diff line
@@ -36,5 +36,5 @@ interface SettingsPageProvider {
    @Composable
    @Composable
    fun Page(arguments: Bundle?)
    fun Page(arguments: Bundle?)


    // fun buildEntry( arguments: Bundle?) : List<entry>
    fun buildEntry(arguments: Bundle?): List<SettingsEntry> = emptyList()
}
}