1 /* <lambda>null2 * Copyright (C) 2022 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 18 package com.android.wallpaper.picker.undo.domain.interactor 19 20 import com.android.wallpaper.picker.undo.data.repository.UndoRepository 21 import com.android.wallpaper.picker.undo.shared.model.RestorableSnapshot 22 import kotlinx.coroutines.CoroutineScope 23 import kotlinx.coroutines.flow.Flow 24 import kotlinx.coroutines.launch 25 26 /** 27 * Encapsulates the "undo" business logic. 28 * 29 * ## Usage 30 * 1. Instantiate, injecting the supported [SnapshotRestorer] into it, one for each feature that 31 * 32 * ``` 33 * should support undo functionality. 34 * ``` 35 * 2. Call [startSession] which will bootstrap all passed-in [SnapshotRestorer] instances and 36 * 37 * ``` 38 * hydrate our model with the latest snapshots from each one. 39 * ``` 40 * 3. Observe [isUndoable] to know whether the UI for triggering an "undo" action should be made 41 * 42 * ``` 43 * visible to the user. 44 * ``` 45 * 4. Call [revertAll] when the user wishes to revert everything. 46 */ 47 class UndoInteractor( 48 private val scope: CoroutineScope, 49 private val repository: UndoRepository, 50 private val restorerByOwnerId: Map<Int, SnapshotRestorer>, 51 ) { 52 53 /** Whether the current state is undoable. */ 54 val isUndoable: Flow<Boolean> = repository.isAnythingDirty 55 56 /** Bootstraps the undo system, querying each undo-supporting area for the initial snapshot. */ 57 fun startSession() { 58 repository.clearAllDirty() 59 restorerByOwnerId.forEach { (ownerId, restorer) -> 60 scope.launch { 61 val initialSnapshot = 62 restorer.setUpSnapshotRestorer( 63 object : SnapshotStore { 64 override fun retrieve(): RestorableSnapshot { 65 return repository.getSnapshot(ownerId) 66 ?: error( 67 "No snapshot for this owner ID! Did you call this before" + 68 " storing a snapshot?" 69 ) 70 } 71 72 override fun store(snapshot: RestorableSnapshot) { 73 val initialSnapshot = repository.getSnapshot(ownerId) 74 repository.putDirty( 75 ownerId = ownerId, 76 isDirty = initialSnapshot != snapshot 77 ) 78 } 79 } 80 ) 81 82 repository.putSnapshot( 83 ownerId = ownerId, 84 snapshot = initialSnapshot, 85 ) 86 } 87 } 88 } 89 90 /** Triggers a revert for all areas. */ 91 fun revertAll() { 92 repository.getAllDirty().forEach { ownerId -> 93 val restorer = restorerByOwnerId[ownerId] 94 val snapshot = repository.getSnapshot(ownerId) 95 if (restorer != null && snapshot != null) { 96 scope.launch { restorer.restoreToSnapshot(snapshot) } 97 } 98 } 99 100 repository.clearAllDirty() 101 } 102 } 103