1 /*
<lambda>null2  * Copyright (C) 2023 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.remember
22 import com.android.settingslib.spa.framework.util.genEntryId
23 
24 private const val INJECT_ENTRY_LABEL = "INJECT"
25 private const val ROOT_ENTRY_LABEL = "ROOT"
26 
27 /**
28  * The helper to build a Settings Entry instance.
29  */
30 class SettingsEntryBuilder(private val name: String, private val owner: SettingsPage) {
31     private var label = name
32     private var fromPage: SettingsPage? = null
33     private var toPage: SettingsPage? = null
34 
35     // Attributes
36     private var isAllowSearch: Boolean = false
37     private var isSearchDataDynamic: Boolean = false
38     private var hasMutableStatus: Boolean = false
39     private var hasSliceSupport: Boolean = false
40 
41     // Functions
42     private var uiLayoutFn: UiLayerRenderer = { }
43     private var statusDataFn: StatusDataGetter = { null }
44     private var searchDataFn: SearchDataGetter = { null }
45     private var sliceDataFn: SliceDataGetter = { _: Uri, _: Bundle? -> null }
46 
47     fun build(): SettingsEntry {
48         val page = fromPage ?: owner
49         val isEnabled = page.isEnabled()
50         return SettingsEntry(
51             id = genEntryId(name, owner, fromPage, toPage),
52             name = name,
53             owner = owner,
54             label = label,
55 
56             // linking data
57             fromPage = fromPage,
58             toPage = toPage,
59 
60             // attributes
61             // TODO: set isEnabled & (isAllowSearch, hasSliceSupport) separately
62             isAllowSearch = isEnabled && isAllowSearch,
63             isSearchDataDynamic = isSearchDataDynamic,
64             hasMutableStatus = hasMutableStatus,
65             hasSliceSupport = isEnabled && hasSliceSupport,
66 
67             // functions
68             statusDataImpl = statusDataFn,
69             searchDataImpl = searchDataFn,
70             sliceDataImpl = sliceDataFn,
71             uiLayoutImpl = uiLayoutFn,
72         )
73     }
74 
75     fun setLabel(label: String): SettingsEntryBuilder {
76         this.label = label
77         return this
78     }
79 
80     fun setLink(
81         fromPage: SettingsPage? = null,
82         toPage: SettingsPage? = null
83     ): SettingsEntryBuilder {
84         if (fromPage != null) this.fromPage = fromPage
85         if (toPage != null) this.toPage = toPage
86         return this
87     }
88 
89     fun setIsSearchDataDynamic(isDynamic: Boolean): SettingsEntryBuilder {
90         this.isSearchDataDynamic = isDynamic
91         return this
92     }
93 
94     fun setHasMutableStatus(hasMutableStatus: Boolean): SettingsEntryBuilder {
95         this.hasMutableStatus = hasMutableStatus
96         return this
97     }
98 
99     fun setMacro(fn: (arguments: Bundle?) -> EntryMacro): SettingsEntryBuilder {
100         setStatusDataFn { fn(it).getStatusData() }
101         setSearchDataFn { fn(it).getSearchData() }
102         setUiLayoutFn {
103             val macro = remember { fn(it) }
104             macro.UiLayout()
105         }
106         return this
107     }
108 
109     fun setStatusDataFn(fn: StatusDataGetter): SettingsEntryBuilder {
110         this.statusDataFn = fn
111         return this
112     }
113 
114     fun setSearchDataFn(fn: SearchDataGetter): SettingsEntryBuilder {
115         this.searchDataFn = fn
116         this.isAllowSearch = true
117         return this
118     }
119 
120     fun clearSearchDataFn(): SettingsEntryBuilder {
121         this.searchDataFn = { null }
122         this.isAllowSearch = false
123         return this
124     }
125 
126     fun setSliceDataFn(fn: SliceDataGetter): SettingsEntryBuilder {
127         this.sliceDataFn = fn
128         this.hasSliceSupport = true
129         return this
130     }
131 
132     fun setUiLayoutFn(fn: UiLayerRenderer): SettingsEntryBuilder {
133         this.uiLayoutFn = fn
134         return this
135     }
136 
137     companion object {
138         fun create(entryName: String, owner: SettingsPage): SettingsEntryBuilder {
139             return SettingsEntryBuilder(entryName, owner)
140         }
141 
142         fun createLinkFrom(entryName: String, owner: SettingsPage): SettingsEntryBuilder {
143             return create(entryName, owner).setLink(fromPage = owner)
144         }
145 
146         fun createLinkTo(entryName: String, owner: SettingsPage): SettingsEntryBuilder {
147             return create(entryName, owner).setLink(toPage = owner)
148         }
149 
150         fun create(
151             owner: SettingsPage,
152             entryName: String,
153             label: String = entryName,
154         ): SettingsEntryBuilder = SettingsEntryBuilder(entryName, owner).setLabel(label)
155 
156         fun createInject(
157             owner: SettingsPage,
158             label: String = "${INJECT_ENTRY_LABEL}_${owner.displayName}",
159         ): SettingsEntryBuilder = createLinkTo(INJECT_ENTRY_LABEL, owner).setLabel(label)
160 
161         fun createRoot(
162             owner: SettingsPage,
163             label: String = "${ROOT_ENTRY_LABEL}_${owner.displayName}",
164         ): SettingsEntryBuilder = createLinkTo(ROOT_ENTRY_LABEL, owner).setLabel(label)
165     }
166 }
167