1 /* <lambda>null2 * Copyright (C) 2022 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17 package com.android.settingslib.spa.framework.common 18 19 import android.net.Uri 20 import android.os.Bundle 21 import androidx.compose.runtime.Composable 22 import androidx.compose.runtime.CompositionLocalProvider 23 import androidx.compose.runtime.ProvidedValue 24 import androidx.compose.runtime.compositionLocalOf 25 import androidx.compose.runtime.remember 26 import com.android.settingslib.spa.framework.compose.LocalNavController 27 28 interface EntryData { 29 val pageId: String? 30 get() = null 31 val entryId: String? 32 get() = null 33 val isHighlighted: Boolean 34 get() = false 35 val arguments: Bundle? 36 get() = null 37 } 38 39 val LocalEntryDataProvider = <lambda>null40 compositionLocalOf<EntryData> { object : EntryData {} } 41 42 typealias UiLayerRenderer = @Composable (arguments: Bundle?) -> Unit 43 typealias StatusDataGetter = (arguments: Bundle?) -> EntryStatusData? 44 typealias SearchDataGetter = (arguments: Bundle?) -> EntrySearchData? 45 typealias SliceDataGetter = (sliceUri: Uri, arguments: Bundle?) -> EntrySliceData? 46 47 /** 48 * Defines data of a Settings entry. 49 */ 50 data class SettingsEntry( 51 // The unique id of this entry, which is computed by name + owner + fromPage + toPage. 52 val id: String, 53 54 // The name of the entry, which is used to compute the unique id, and need to be stable. 55 private val name: String, 56 57 // The label of the entry, for better readability. 58 // For migration mapping, this should match the android:key field in the old architecture 59 // if applicable. 60 val label: String, 61 62 // The owner page of this entry. 63 val owner: SettingsPage, 64 65 // Defines linking of Settings entries 66 val fromPage: SettingsPage? = null, 67 val toPage: SettingsPage? = null, 68 69 /** 70 * ======================================== 71 * Defines entry attributes here. 72 * ======================================== 73 */ 74 val isAllowSearch: Boolean = false, 75 76 // Indicate whether the search indexing data of entry is dynamic. 77 val isSearchDataDynamic: Boolean = false, 78 79 // Indicate whether the status of entry is mutable. 80 // If so, for instance, we'll reindex its status for search. 81 val hasMutableStatus: Boolean = false, 82 83 // Indicate whether the entry has SliceProvider support. 84 val hasSliceSupport: Boolean = false, 85 86 /** 87 * ======================================== 88 * Defines entry APIs to get data here. 89 * ======================================== 90 */ 91 92 /** 93 * API to get the status data of the entry, such as isDisabled / isSwitchOff. 94 * Returns null if this entry do NOT have any status. 95 */ <lambda>null96 private val statusDataImpl: StatusDataGetter = { null }, 97 98 /** 99 * API to get Search indexing data for this entry, such as title / keyword. 100 * Returns null if this entry do NOT support search. 101 */ <lambda>null102 private val searchDataImpl: SearchDataGetter = { null }, 103 104 /** 105 * API to get Slice data of this entry. The Slice data is implemented as a LiveData, 106 * and is associated with the Slice's lifecycle (pin / unpin) by the framework. 107 */ _null108 private val sliceDataImpl: SliceDataGetter = { _: Uri, _: Bundle? -> null }, 109 110 /** 111 * API to Render UI of this entry directly. For now, we use it in the internal injection, to 112 * support the case that the injection page owner wants to maintain both data and UI of the 113 * injected entry. In the long term, we may deprecate the @Composable Page() API in SPP, and 114 * use each entries' UI rendering function in the page instead. 115 */ <lambda>null116 private val uiLayoutImpl: UiLayerRenderer = {}, 117 ) { containerPagenull118 fun containerPage(): SettingsPage { 119 // The Container page of the entry, which is the from-page or 120 // the owner-page if from-page is unset. 121 return fromPage ?: owner 122 } 123 fullArgumentnull124 private fun fullArgument(runtimeArguments: Bundle? = null): Bundle { 125 return Bundle().apply { 126 if (owner.arguments != null) putAll(owner.arguments) 127 // Put runtime args later, which can override page args. 128 if (runtimeArguments != null) putAll(runtimeArguments) 129 } 130 } 131 getStatusDatanull132 fun getStatusData(runtimeArguments: Bundle? = null): EntryStatusData? { 133 return statusDataImpl(fullArgument(runtimeArguments)) 134 } 135 getSearchDatanull136 fun getSearchData(runtimeArguments: Bundle? = null): EntrySearchData? { 137 return searchDataImpl(fullArgument(runtimeArguments)) 138 } 139 getSliceDatanull140 fun getSliceData(sliceUri: Uri, runtimeArguments: Bundle? = null): EntrySliceData? { 141 return sliceDataImpl(sliceUri, fullArgument(runtimeArguments)) 142 } 143 144 @Composable UiLayoutnull145 fun UiLayout(runtimeArguments: Bundle? = null) { 146 val arguments = remember { fullArgument(runtimeArguments) } 147 CompositionLocalProvider(provideLocalEntryData(arguments)) { 148 uiLayoutImpl(arguments) 149 } 150 } 151 152 @Composable provideLocalEntryDatanull153 private fun provideLocalEntryData(arguments: Bundle): ProvidedValue<EntryData> { 154 val controller = LocalNavController.current 155 return LocalEntryDataProvider provides remember { 156 object : EntryData { 157 override val pageId = containerPage().id 158 override val entryId = id 159 override val isHighlighted = controller.highlightEntryId == id 160 override val arguments = arguments 161 } 162 } 163 } 164 } 165