1 /* 2 * Copyright 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 package com.android.photopicker.core.embedded 17 18 import android.os.Bundle 19 import android.view.View 20 import androidx.activity.OnBackPressedDispatcher 21 import androidx.activity.OnBackPressedDispatcherOwner 22 import androidx.annotation.MainThread 23 import androidx.lifecycle.HasDefaultViewModelProviderFactory 24 import androidx.lifecycle.Lifecycle 25 import androidx.lifecycle.LifecycleOwner 26 import androidx.lifecycle.LifecycleRegistry 27 import androidx.lifecycle.ViewModelProvider 28 import androidx.lifecycle.ViewModelStore 29 import androidx.lifecycle.ViewModelStoreOwner 30 import androidx.lifecycle.setViewTreeLifecycleOwner 31 import androidx.lifecycle.setViewTreeViewModelStoreOwner 32 import androidx.lifecycle.viewmodel.CreationExtras 33 import androidx.savedstate.SavedStateRegistry 34 import androidx.savedstate.SavedStateRegistryController 35 import androidx.savedstate.SavedStateRegistryOwner 36 import androidx.savedstate.setViewTreeSavedStateRegistryOwner 37 38 /** 39 * A custom [LifecycleOwner], [ViewModelStoreOwner], [OnBackPressedDispatcherOwner] and 40 * [SavedStateRegistryOwner] for use with the embedded runtime of Photopicker. 41 * 42 * This class is only used for Embedded Photopicker, it is not invoked during the regular, 43 * activity-based photopicker experience. 44 * 45 * IMPORTANT: It can only be created on the MainThread. 46 * 47 * The lifecycle is controlled by the [Session] class, and expects that all Lifecycle state changes 48 * are always called from the MainThread. 49 * 50 * The embedded photopicker is not run inside of the activity framework, however the compose UI 51 * requires certain activity provided conventions to run correctly, and for embedded photopicker 52 * sessions, this class provides all of that functionality to the embedded runtime. 53 * 54 * @property viewModelFactory A [ViewModelProvider.Factory] implementation that can build all of the 55 * Photopicker [ViewModel] that are required during an embedded photopicker session. 56 * @see [EmbeddedViewModelFactory] for implementation of how view models are created during the 57 * embedded runtime. 58 */ 59 class EmbeddedLifecycle(viewModelFactory: ViewModelProvider.Factory) : 60 LifecycleOwner, 61 ViewModelStoreOwner, 62 SavedStateRegistryOwner, 63 OnBackPressedDispatcherOwner, 64 HasDefaultViewModelProviderFactory { 65 66 companion object { 67 val TAG: String = "EmbeddedViewLifecycle" 68 } 69 70 private val stateBundle: Bundle = Bundle() 71 72 private val lifecycleRegistry: LifecycleRegistry = LifecycleRegistry(/* provider= */ this) 73 private val savedStateRegistryController = SavedStateRegistryController.create(this) 74 75 override val lifecycle: Lifecycle 76 get() = lifecycleRegistry 77 78 override val savedStateRegistry: SavedStateRegistry = 79 savedStateRegistryController.savedStateRegistry 80 81 init { 82 83 // Initialize and attach the savedStateRegistryController. 84 lifecycleRegistry.currentState = Lifecycle.State.INITIALIZED 85 savedStateRegistryController.performAttach() 86 savedStateRegistryController.performRestore(stateBundle) 87 } 88 89 /** 90 * This [OnBackPressedDispatcher] should be used to provide dispatcher for any 91 * [OnBackPressedCallback] in embedded session to [PhotopickerNavGraph] 92 */ 93 override val onBackPressedDispatcher: OnBackPressedDispatcher = OnBackPressedDispatcher() 94 95 /** 96 * This [ViewModelStore] holds all of the Photopicker view models for an individual embedded 97 * photopicker session. 98 */ 99 override val viewModelStore: ViewModelStore = ViewModelStore() 100 101 /** 102 * Returns the default [ViewModelProvider.Factory] that should be used when no custom `Factory` 103 * is provided to the [ViewModelProvider] constructors. 104 */ 105 override val defaultViewModelProviderFactory: ViewModelProvider.Factory = viewModelFactory 106 107 /** 108 * Returns the default [CreationExtras] that should be passed into 109 * [ViewModelProvider.Factory.create] when no overriding [CreationExtras] were passed to the 110 * [ViewModelProvider] constructors. 111 */ 112 override val defaultViewModelCreationExtras: CreationExtras 113 get() = CreationExtras.Empty 114 115 @MainThread onCreatenull116 fun onCreate() { 117 lifecycleRegistry.handleLifecycleEvent(Lifecycle.Event.ON_CREATE) 118 } 119 120 @MainThread onStartnull121 fun onStart() { 122 lifecycleRegistry.handleLifecycleEvent(Lifecycle.Event.ON_START) 123 } 124 125 @MainThread onResumenull126 fun onResume() { 127 lifecycleRegistry.handleLifecycleEvent(Lifecycle.Event.ON_RESUME) 128 } 129 130 @MainThread onPausenull131 fun onPause() { 132 lifecycleRegistry.handleLifecycleEvent(Lifecycle.Event.ON_PAUSE) 133 } 134 135 @MainThread onStopnull136 fun onStop() { 137 lifecycleRegistry.handleLifecycleEvent(Lifecycle.Event.ON_STOP) 138 } 139 140 @MainThread onDestroynull141 fun onDestroy() { 142 savedStateRegistryController.performSave(stateBundle) 143 lifecycleRegistry.handleLifecycleEvent(Lifecycle.Event.ON_DESTROY) 144 } 145 146 /** 147 * Sets the view tree owners for the given view. 148 * 149 * This should be done before setting the content view so that the inflation process and attach 150 * listeners will see them already present. They will be reused by any ComposeView inside the 151 * view's hierarchy. 152 * 153 * @param view The view to attach to this lifecycle. 154 */ attachViewnull155 fun attachView(view: View) { 156 view.setViewTreeLifecycleOwner(this) 157 view.setViewTreeViewModelStoreOwner(this) 158 view.setViewTreeSavedStateRegistryOwner(this) 159 } 160 } 161