1 /*
2  * Copyright (C) 2024 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.intentresolver.contentpreview.payloadtoggle.domain.model
18 
19 /** A window of data loaded from a cursor. */
20 data class LoadedWindow<K, V>(
21     /** First cursor page index loaded within this window. */
22     val firstLoadedPageNum: Int,
23     /** Last cursor page index loaded within this window. */
24     val lastLoadedPageNum: Int,
25     /** Keys of cursor data within this window, grouped by loaded page. */
26     val pages: List<Set<K>>,
27     /** Merged set of all cursor data within this window. */
28     val merged: Map<K, V>,
29     /** Is there more data to the left of this window? */
30     val hasMoreLeft: Boolean,
31     /** Is there more data to the right of this window? */
32     val hasMoreRight: Boolean,
33 )
34 
35 /** Number of loaded pages stored within this [LoadedWindow]. */
36 val LoadedWindow<*, *>.numLoadedPages: Int
37     get() = (lastLoadedPageNum - firstLoadedPageNum) + 1
38 
39 /** Inserts [newPage] to the right, and removes the leftmost page from the window. */
shiftWindowRightnull40 fun <K, V> LoadedWindow<K, V>.shiftWindowRight(
41     newPage: Map<K, V>,
42     hasMore: Boolean,
43 ): LoadedWindow<K, V> =
44     LoadedWindow(
45         firstLoadedPageNum = firstLoadedPageNum + 1,
46         lastLoadedPageNum = lastLoadedPageNum + 1,
47         pages = pages.drop(1) + listOf(newPage.keys),
48         merged =
49             buildMap {
50                 putAll(merged)
51                 pages.first().forEach(::remove)
52                 putAll(newPage)
53             },
54         hasMoreLeft = true,
55         hasMoreRight = hasMore,
56     )
57 
58 /** Inserts [newPage] to the right, increasing the size of the window to accommodate it. */
expandWindowRightnull59 fun <K, V> LoadedWindow<K, V>.expandWindowRight(
60     newPage: Map<K, V>,
61     hasMore: Boolean,
62 ): LoadedWindow<K, V> =
63     LoadedWindow(
64         firstLoadedPageNum = firstLoadedPageNum,
65         lastLoadedPageNum = lastLoadedPageNum + 1,
66         pages = pages + listOf(newPage.keys),
67         merged = merged + newPage,
68         hasMoreLeft = hasMoreLeft,
69         hasMoreRight = hasMore,
70     )
71 
72 /** Inserts [newPage] to the left, and removes the rightmost page from the window. */
73 fun <K, V> LoadedWindow<K, V>.shiftWindowLeft(
74     newPage: Map<K, V>,
75     hasMore: Boolean,
76 ): LoadedWindow<K, V> =
77     LoadedWindow(
78         firstLoadedPageNum = firstLoadedPageNum - 1,
79         lastLoadedPageNum = lastLoadedPageNum - 1,
80         pages = listOf(newPage.keys) + pages.dropLast(1),
81         merged =
82             buildMap {
83                 putAll(newPage)
84                 putAll(merged - pages.last())
85             },
86         hasMoreLeft = hasMore,
87         hasMoreRight = true,
88     )
89 
90 /** Inserts [newPage] to the left, increasing the size olf the window to accommodate it. */
expandWindowLeftnull91 fun <K, V> LoadedWindow<K, V>.expandWindowLeft(
92     newPage: Map<K, V>,
93     hasMore: Boolean,
94 ): LoadedWindow<K, V> =
95     LoadedWindow(
96         firstLoadedPageNum = firstLoadedPageNum - 1,
97         lastLoadedPageNum = lastLoadedPageNum,
98         pages = listOf(newPage.keys) + pages,
99         merged = newPage + merged,
100         hasMoreLeft = hasMore,
101         hasMoreRight = hasMoreRight,
102     )
103