1 /* 2 * Copyright (C) 2019 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.systemui.controls.management 18 19 import android.text.TextUtils 20 import android.util.ArrayMap 21 import com.android.systemui.controls.ControlStatus 22 import com.android.systemui.controls.controller.ControlInfo 23 24 /** 25 * This model is used to show controls separated by zones. 26 * 27 * The model will sort the controls and zones in the following manner: 28 * * The zones will be sorted in a first seen basis 29 * * The controls in each zone will be sorted in a first seen basis. 30 * 31 * The controls passed should belong to the same structure, as an instance of this model will be 32 * created for each structure. 33 * 34 * The list of favorite ids can contain ids for controls not passed to this model. Those will be 35 * filtered out. 36 * 37 * @property controls List of controls as returned by loading 38 * @property initialFavoriteIds sorted ids of favorite controls. 39 * @property noZoneString text to use as header for all controls that have blank or `null` zone. 40 * @property controlsModelCallback callback to notify that favorites have changed for the first time 41 */ 42 class AllModel( 43 private val controls: List<ControlStatus>, 44 initialFavoriteIds: List<String>, 45 private val emptyZoneString: CharSequence, 46 private val controlsModelCallback: ControlsModel.ControlsModelCallback 47 ) : ControlsModel { 48 49 private var modified = false 50 51 override val moveHelper = null 52 53 override val favorites: List<ControlInfo> 54 get() = favoriteIds.mapNotNull { id -> 55 val control = controls.firstOrNull { it.control.controlId == id }?.control 56 control?.let { 57 ControlInfo.fromControl(it) 58 } 59 } 60 61 private val favoriteIds = run { 62 val ids = controls.mapTo(HashSet()) { it.control.controlId } 63 initialFavoriteIds.filter { it in ids }.toMutableList() 64 } 65 66 override val elements: List<ElementWrapper> = createWrappers(controls) 67 68 override fun changeFavoriteStatus(controlId: String, favorite: Boolean) { 69 val toChange = elements.firstOrNull { 70 it is ControlStatusWrapper && it.controlStatus.control.controlId == controlId 71 } as ControlStatusWrapper? 72 if (favorite == toChange?.controlStatus?.favorite) return 73 val changed: Boolean = if (favorite) { 74 favoriteIds.add(controlId) 75 } else { 76 favoriteIds.remove(controlId) 77 } 78 if (changed) { 79 if (!modified) { 80 modified = true 81 controlsModelCallback.onFirstChange() 82 } 83 controlsModelCallback.onChange() 84 } 85 toChange?.let { 86 it.controlStatus.favorite = favorite 87 } 88 } 89 90 private fun createWrappers(list: List<ControlStatus>): List<ElementWrapper> { 91 val map = list.groupByTo(OrderedMap(ArrayMap<CharSequence, MutableList<ControlStatus>>())) { 92 it.control.zone ?: "" 93 } 94 val output = mutableListOf<ElementWrapper>() 95 var emptyZoneValues: Sequence<ControlStatusWrapper>? = null 96 for (zoneName in map.orderedKeys) { 97 val values = map.getValue(zoneName).asSequence().map { ControlStatusWrapper(it) } 98 if (TextUtils.isEmpty(zoneName)) { 99 emptyZoneValues = values 100 } else { 101 output.add(ZoneNameWrapper(zoneName)) 102 output.addAll(values) 103 } 104 } 105 // Add controls with empty zone at the end 106 if (emptyZoneValues != null) { 107 if (map.size != 1) { 108 output.add(ZoneNameWrapper(emptyZoneString)) 109 } 110 output.addAll(emptyZoneValues) 111 } 112 return output 113 } 114 115 private class OrderedMap<K, V>(private val map: MutableMap<K, V>) : MutableMap<K, V> by map { 116 117 val orderedKeys = mutableListOf<K>() 118 119 override fun put(key: K, value: V): V? { 120 if (key !in map) { 121 orderedKeys.add(key) 122 } 123 return map.put(key, value) 124 } 125 126 override fun clear() { 127 orderedKeys.clear() 128 map.clear() 129 } 130 131 override fun remove(key: K): V? { 132 val removed = map.remove(key) 133 if (removed != null) { 134 orderedKeys.remove(key) 135 } 136 return removed 137 } 138 } 139 } 140