1 /* 2 * 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.util.Log 20 import java.util.LinkedList 21 22 private const val TAG = "EntryRepository" 23 private const val MAX_ENTRY_SIZE = 5000 24 private const val MAX_ENTRY_DEPTH = 10 25 26 data class SettingsPageWithEntry( 27 val page: SettingsPage, 28 val entries: List<SettingsEntry>, 29 // The inject entry, which to-page is current page. 30 val injectEntry: SettingsEntry, 31 ) 32 33 /** 34 * The repository to maintain all Settings entries 35 */ 36 class SettingsEntryRepository(sppRepository: SettingsPageProviderRepository) { 37 // Map of entry unique Id to entry 38 private val entryMap: Map<String, SettingsEntry> 39 40 // Map of Settings page to its contained entries. 41 private val pageWithEntryMap: Map<String, SettingsPageWithEntry> 42 43 init { 44 Log.d(TAG, "Initialize") 45 entryMap = mutableMapOf() 46 pageWithEntryMap = mutableMapOf() 47 48 val nullPage = NullPageProvider.createSettingsPage() 49 val entryQueue = LinkedList<SettingsEntry>() 50 for (page in sppRepository.getAllRootPages()) { 51 val rootEntry = 52 SettingsEntryBuilder.createRoot(owner = page).setLink(fromPage = nullPage).build() 53 if (!entryMap.containsKey(rootEntry.id)) { 54 entryQueue.push(rootEntry) 55 entryMap.put(rootEntry.id, rootEntry) 56 } 57 } 58 59 while (entryQueue.isNotEmpty() && entryMap.size < MAX_ENTRY_SIZE) { 60 val entry = entryQueue.pop() 61 val page = entry.toPage 62 if (page == null || pageWithEntryMap.containsKey(page.id)) continue 63 val spp = sppRepository.getProviderOrNull(page.sppName) ?: continue 64 val newEntries = spp.buildEntry(page.arguments) 65 // The page id could be existed already, if there are 2+ pages go to the same one. 66 // For now, override the previous ones, which means only the last from-page is kept. 67 // TODO: support multiple from-pages if necessary. 68 pageWithEntryMap[page.id] = SettingsPageWithEntry( 69 page = page, 70 entries = newEntries, 71 injectEntry = entry 72 ) 73 for (newEntry in newEntries) { 74 if (!entryMap.containsKey(newEntry.id)) { 75 entryQueue.push(newEntry) 76 entryMap.put(newEntry.id, newEntry) 77 } 78 } 79 } 80 81 Log.d( 82 TAG, 83 "Initialize Completed: ${entryMap.size} entries in ${pageWithEntryMap.size} pages" 84 ) 85 } 86 getAllPageWithEntrynull87 fun getAllPageWithEntry(): Collection<SettingsPageWithEntry> { 88 return pageWithEntryMap.values 89 } 90 getPageWithEntrynull91 fun getPageWithEntry(pageId: String): SettingsPageWithEntry? { 92 return pageWithEntryMap[pageId] 93 } 94 getAllEntriesnull95 fun getAllEntries(): Collection<SettingsEntry> { 96 return entryMap.values 97 } 98 getEntrynull99 fun getEntry(entryId: String): SettingsEntry? { 100 return entryMap[entryId] 101 } 102 getEntryPathnull103 private fun getEntryPath(entryId: String): List<SettingsEntry> { 104 val entryPath = ArrayList<SettingsEntry>() 105 var currentEntry = entryMap[entryId] 106 while (currentEntry != null && entryPath.size < MAX_ENTRY_DEPTH) { 107 entryPath.add(currentEntry) 108 val currentPage = currentEntry.containerPage() 109 currentEntry = pageWithEntryMap[currentPage.id]?.injectEntry 110 } 111 return entryPath 112 } 113 getEntryPathWithLabelnull114 fun getEntryPathWithLabel(entryId: String): List<String> { 115 val entryPath = getEntryPath(entryId) 116 return entryPath.map { it.label } 117 } 118 getEntryPathWithTitlenull119 fun getEntryPathWithTitle(entryId: String, defaultTitle: String): List<String> { 120 val entryPath = getEntryPath(entryId) 121 return entryPath.map { 122 if (it.toPage == null) 123 defaultTitle 124 else { 125 it.toPage.getTitle() 126 } 127 } 128 } 129 } 130