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