xref: /aosp_15_r20/frameworks/base/packages/SystemUI/src/com/android/systemui/scene/data/model/SceneStack.kt (revision d57664e9bc4670b3ecf6748a746a57c557b6bc9e)
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.systemui.scene.data.model
18  
19  import com.android.compose.animation.scene.SceneKey
20  
21  /** An immutable stack of [SceneKey]s backed by a singly-linked list. */
22  sealed interface SceneStack
23  
24  /** Returns the scene at the head of the stack, or `null` if empty. O(1) */
SceneStacknull25  fun SceneStack.peek(): SceneKey? =
26      when (this) {
27          EmptyStack -> null
28          is StackedNodes -> head
29      }
30  
31  /** Returns a stack with the head removed, or `null` if empty. O(1) */
SceneStacknull32  fun SceneStack.pop(): SceneStack? =
33      when (this) {
34          EmptyStack -> null
35          is StackedNodes -> tail
36      }
37  
38  /** Returns a stack with [sceneKey] as the head on top of [this]. O(1) */
SceneStacknull39  fun SceneStack.push(sceneKey: SceneKey): SceneStack = StackedNodes(sceneKey, this)
40  
41  /** Returns an iterable that produces all elements in the stack, from head to tail. */
42  fun SceneStack.asIterable(): Iterable<SceneKey> = Iterable {
43      iterator {
44          when (this@asIterable) {
45              EmptyStack -> {}
46              is StackedNodes -> {
47                  yield(head)
48                  yieldAll(tail.asIterable())
49              }
50          }
51      }
52  }
53  
54  /** Does this stack contain the given [sceneKey]? O(N) */
SceneStacknull55  fun SceneStack.contains(sceneKey: SceneKey): Boolean = asIterable().any { it == sceneKey }
56  
57  /**
58   * Returns a new [SceneStack] containing the given [scenes], ordered such that the first argument is
59   * the head returned from [peek], then the second, and so forth.
60   */
sceneStackOfnull61  fun sceneStackOf(vararg scenes: SceneKey): SceneStack {
62      var result: SceneStack = EmptyStack
63      for (sceneKey in scenes.reversed()) {
64          result = result.push(sceneKey)
65      }
66      return result
67  }
68  
69  private data object EmptyStack : SceneStack {
toStringnull70      override fun toString() = sceneStackToString()
71  }
72  
73  private data class StackedNodes(val head: SceneKey, val tail: SceneStack) : SceneStack {
74      override fun toString() = sceneStackToString()
75  }
76  
SceneStacknull77  private fun SceneStack.sceneStackToString(): String =
78      asIterable().joinToString { it.testTag }.let { "SceneStack([$it])" }
79