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.systemui.communal.ui.compose 18 19 import android.content.ComponentName 20 import android.os.UserHandle 21 import androidx.compose.runtime.Composable 22 import androidx.compose.runtime.remember 23 import androidx.compose.runtime.toMutableStateList 24 import com.android.systemui.communal.domain.model.CommunalContentModel 25 import com.android.systemui.communal.ui.viewmodel.BaseCommunalViewModel 26 import com.android.systemui.communal.widgets.WidgetConfigurator 27 28 @Composable 29 fun rememberContentListState( 30 widgetConfigurator: WidgetConfigurator?, 31 communalContent: List<CommunalContentModel>, 32 viewModel: BaseCommunalViewModel, 33 ): ContentListState { 34 return remember(communalContent) { 35 ContentListState( 36 communalContent, 37 { componentName, user, priority -> 38 viewModel.onAddWidget( 39 componentName, 40 user, 41 priority, 42 widgetConfigurator, 43 ) 44 }, 45 viewModel::onDeleteWidget, 46 viewModel::onReorderWidgets, 47 ) 48 } 49 } 50 51 /** 52 * Keeps the current state of the [CommunalContentModel] list being edited. [GridDragDropState] 53 * interacts with this class to update the order in the list. [onSaveList] should be called on 54 * dragging ends to persist the state in db for better performance. 55 */ 56 class ContentListState 57 internal constructor( 58 communalContent: List<CommunalContentModel>, 59 private val onAddWidget: 60 (componentName: ComponentName, user: UserHandle, priority: Int) -> Unit, 61 private val onDeleteWidget: (id: Int) -> Unit, 62 private val onReorderWidgets: (widgetIdToPriorityMap: Map<Int, Int>) -> Unit, 63 ) { 64 var list = communalContent.toMutableStateList() 65 private set 66 67 /** Move item to a new position in the list. */ onMovenull68 fun onMove(fromIndex: Int, toIndex: Int) { 69 list.apply { add(toIndex, removeAt(fromIndex)) } 70 } 71 72 /** Remove widget from the list and the database. */ onRemovenull73 fun onRemove(indexToRemove: Int) { 74 if (list[indexToRemove].isWidgetContent()) { 75 val widget = list[indexToRemove] as CommunalContentModel.WidgetContent 76 list.apply { removeAt(indexToRemove) } 77 onDeleteWidget(widget.appWidgetId) 78 } 79 } 80 81 /** 82 * Persists the new order with all the movements happened during drag operations & the new 83 * widget drop (if applicable). 84 * 85 * @param newItemComponentName name of the new widget that was dropped into the list; null if no 86 * new widget was added. 87 * @param newItemUser user profile associated with the new widget that was dropped into the 88 * list; null if no new widget was added. 89 * @param newItemIndex index at which the a new widget was dropped into the list; null if no new 90 * widget was dropped. 91 */ onSaveListnull92 fun onSaveList( 93 newItemComponentName: ComponentName? = null, 94 newItemUser: UserHandle? = null, 95 newItemIndex: Int? = null 96 ) { 97 // filters placeholder, but, maintains the indices of the widgets as if the placeholder was 98 // in the list. When persisted in DB, this leaves space for the new item (to be added) at 99 // the correct priority. 100 val widgetIdToPriorityMap: Map<Int, Int> = 101 list 102 .mapIndexedNotNull { index, item -> 103 if (item is CommunalContentModel.WidgetContent) { 104 item.appWidgetId to list.size - index 105 } else { 106 null 107 } 108 } 109 .toMap() 110 // reorder and then add the new widget 111 onReorderWidgets(widgetIdToPriorityMap) 112 if (newItemComponentName != null && newItemUser != null && newItemIndex != null) { 113 onAddWidget(newItemComponentName, newItemUser, /*priority=*/ list.size - newItemIndex) 114 } 115 } 116 117 /** Returns true if the item at given index is editable. */ isItemEditablenull118 fun isItemEditable(index: Int) = list[index].isWidgetContent() 119 } 120