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