1 /* 2 * Copyright (C) 2025 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.launcher3.util 18 19 import android.content.Context 20 import android.view.ContextThemeWrapper 21 import android.view.View 22 import android.view.View.OnAttachStateChangeListener 23 import android.view.ViewTreeObserver.OnWindowFocusChangeListener 24 import android.view.ViewTreeObserver.OnWindowVisibilityChangeListener 25 import androidx.lifecycle.Lifecycle 26 import androidx.lifecycle.Lifecycle.State.CREATED 27 import androidx.lifecycle.Lifecycle.State.DESTROYED 28 import androidx.lifecycle.Lifecycle.State.RESUMED 29 import androidx.lifecycle.Lifecycle.State.STARTED 30 import androidx.lifecycle.LifecycleRegistry 31 import androidx.lifecycle.setViewTreeLifecycleOwner 32 import androidx.savedstate.SavedStateRegistry 33 import androidx.savedstate.SavedStateRegistryController 34 import androidx.savedstate.setViewTreeSavedStateRegistryOwner 35 import com.android.launcher3.DeviceProfile.OnDeviceProfileChangeListener 36 import com.android.launcher3.Utilities 37 import com.android.launcher3.views.ActivityContext 38 39 /** 40 * A context wrapper with lifecycle tracking based on the window events on the rootView of the 41 * [ActivityContext] 42 */ 43 abstract class BaseContext 44 @JvmOverloads 45 constructor(base: Context, themeResId: Int, private val destroyOnDetach: Boolean = true) : 46 ContextThemeWrapper(base, themeResId), ActivityContext { 47 48 private val listeners = mutableListOf<OnDeviceProfileChangeListener>() 49 50 private val savedStateRegistryController = SavedStateRegistryController.create(this) 51 private val lifecycleRegistry = LifecycleRegistry(this) 52 private val cleanupSet = WeakCleanupSet(this) 53 54 override val savedStateRegistry: SavedStateRegistry 55 get() = savedStateRegistryController.savedStateRegistry 56 57 override val lifecycle: Lifecycle 58 get() = lifecycleRegistry 59 60 private val viewCache = ViewCache() 61 62 init { <lambda>null63 Executors.MAIN_EXECUTOR.execute { 64 savedStateRegistryController.performAttach() 65 savedStateRegistryController.performRestore(null) 66 } 67 } 68 getOnDeviceProfileChangeListenersnull69 override fun getOnDeviceProfileChangeListeners() = listeners 70 71 private val finishActions = RunnableList() 72 73 /** Called when the root view is created for this context */ 74 fun onViewCreated() { 75 val view = rootView 76 val attachListener = 77 object : OnAttachStateChangeListener { 78 79 override fun onViewAttachedToWindow(view: View) { 80 view.rootView.setViewTreeLifecycleOwner(this@BaseContext) 81 view.rootView.setViewTreeSavedStateRegistryOwner(this@BaseContext) 82 lifecycleRegistry.handleLifecycleEvent(Lifecycle.Event.ON_CREATE) 83 84 val treeObserver = view.viewTreeObserver 85 86 val focusListener = OnWindowFocusChangeListener { updateState() } 87 treeObserver.addOnWindowFocusChangeListener(focusListener) 88 finishActions.add { 89 treeObserver.removeOnWindowFocusChangeListener(focusListener) 90 } 91 92 if (Utilities.ATLEAST_V) { 93 val visibilityListener = OnWindowVisibilityChangeListener { updateState() } 94 treeObserver.addOnWindowVisibilityChangeListener(visibilityListener) 95 finishActions.add { 96 treeObserver.removeOnWindowVisibilityChangeListener(visibilityListener) 97 } 98 } 99 } 100 101 override fun onViewDetachedFromWindow(view: View) { 102 if (destroyOnDetach) onViewDestroyed() 103 } 104 } 105 view.addOnAttachStateChangeListener(attachListener) 106 finishActions.add { view.removeOnAttachStateChangeListener(attachListener) } 107 108 if (view.isAttachedToWindow) attachListener.onViewAttachedToWindow(view) 109 updateState() 110 } 111 getViewCachenull112 override fun getViewCache() = viewCache 113 114 override fun getOwnerCleanupSet() = cleanupSet 115 116 private fun updateState() { 117 if (lifecycleRegistry.currentState.isAtLeast(CREATED)) { 118 lifecycleRegistry.currentState = 119 if (rootView.windowVisibility != View.VISIBLE) CREATED 120 else (if (!rootView.hasWindowFocus()) STARTED else RESUMED) 121 } 122 } 123 onViewDestroyednull124 fun onViewDestroyed() { 125 if ( 126 !lifecycleRegistry.currentState.isAtLeast(CREATED) && 127 lifecycleRegistry.currentState != DESTROYED 128 ) { 129 lifecycleRegistry.currentState = CREATED 130 } 131 lifecycleRegistry.handleLifecycleEvent(Lifecycle.Event.ON_DESTROY) 132 finishActions.executeAllAndDestroy() 133 } 134 } 135