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

Commit f2b3938a authored by Chaohui Wang's avatar Chaohui Wang
Browse files

Create AppList for SpaPrivilegedLib

Bug: 235727273
Test: Manual with Test App
Change-Id: Ia5b179d311fe9172c705ad036588e172a5e2a5ca
parent ea7e31ce
Loading
Loading
Loading
Loading
+2 −8
Original line number Diff line number Diff line
@@ -17,11 +17,7 @@
package com.android.settingslib.spa.gallery.page

import android.os.Bundle
import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.runtime.Composable
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.tooling.preview.Preview
import com.android.settingslib.spa.framework.api.SettingsPageProvider
import com.android.settingslib.spa.framework.compose.navigator
@@ -29,7 +25,7 @@ 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.SettingsPager
import com.android.settingslib.spa.widget.ui.SettingsTitle
import com.android.settingslib.spa.widget.ui.PlaceholderTitle

object SettingsPagerPageProvider : SettingsPageProvider {
    override val name = "SettingsPager"
@@ -51,9 +47,7 @@ object SettingsPagerPageProvider : SettingsPageProvider {
@Composable
private fun SettingsPagerPage() {
    SettingsPager(listOf("Personal", "Work")) {
        Box(Modifier.fillMaxSize(), contentAlignment = Alignment.Center) {
            SettingsTitle(title = "Page $it")
        }
        PlaceholderTitle("Page $it")
    }
}

+39 −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.framework.compose

import android.util.Log
import androidx.compose.runtime.Composable
import androidx.compose.runtime.SideEffect
import androidx.compose.runtime.remember

const val ENABLE_LOG_COMPOSITIONS = false

data class LogCompositionsRef(var count: Int)

// Note the inline function below which ensures that this function is essentially
// copied at the call site to ensure that its logging only recompositions from the
// original call site.
@Suppress("NOTHING_TO_INLINE")
@Composable
inline fun LogCompositions(tag: String, msg: String) {
    if (ENABLE_LOG_COMPOSITIONS) {
        val ref = remember { LogCompositionsRef(0) }
        SideEffect { ref.count++ }
        Log.d(tag, "Compositions $msg: ${ref.count}")
    }
}
+6 −0
Original line number Diff line number Diff line
@@ -32,4 +32,10 @@ object SettingsDimension {
        bottom = itemPaddingVertical,
    )
    val itemPaddingAround = 8.dp

    /** The size when app icon is displayed in list. */
    val appIconItemSize = 32.dp

    /** The size when app icon is displayed in App info page. */
    val appIconInfoSize = 48.dp
}
+33 −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.framework.util

import kotlinx.coroutines.async
import kotlinx.coroutines.awaitAll
import kotlinx.coroutines.coroutineScope

suspend inline fun <R, T> Iterable<T>.asyncMap(crossinline transform: (T) -> R): List<R> =
    coroutineScope {
        map { item ->
            async { transform(item) }
        }.awaitAll()
    }

suspend inline fun <T> Iterable<T>.asyncFilter(crossinline predicate: (T) -> Boolean): List<T> =
    asyncMap { item -> item to predicate(item) }
        .filter { it.second }
        .map { it.first }
+58 −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.framework.util

import androidx.compose.runtime.Composable
import androidx.compose.runtime.LaunchedEffect
import androidx.compose.runtime.State
import androidx.compose.runtime.snapshotFlow
import kotlinx.coroutines.ExperimentalCoroutinesApi
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.combine
import kotlinx.coroutines.flow.distinctUntilChangedBy
import kotlinx.coroutines.flow.filterNotNull
import kotlinx.coroutines.flow.flatMapLatest
import kotlinx.coroutines.flow.map

inline fun <T, R> Flow<List<T>>.asyncMapItem(crossinline transform: (T) -> R): Flow<List<R>> =
    map { list -> list.asyncMap(transform) }

@OptIn(ExperimentalCoroutinesApi::class)
inline fun <T, R> Flow<T>.mapState(crossinline block: (T) -> State<R>): Flow<R> =
    flatMapLatest { snapshotFlow { block(it).value } }

fun <T1, T2> Flow<T1>.waitFirst(flow: Flow<T2>): Flow<T1> =
    combine(flow.distinctUntilChangedBy {}) { value, _ -> value }

class StateFlowBridge<T> {
    private val stateFlow = MutableStateFlow<T?>(null)
    val flow = stateFlow.filterNotNull()

    fun setIfAbsent(value: T) {
        if (stateFlow.value == null) {
            stateFlow.value = value
        }
    }

    @Composable
    fun Sync(state: State<T>) {
        LaunchedEffect(state.value) {
            stateFlow.value = state.value
        }
    }
}
Loading