/* * Copyright (C) 2025 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package com.android.launcher3.util import android.content.Context import android.view.ContextThemeWrapper import android.view.View import android.view.View.OnAttachStateChangeListener import android.view.ViewTreeObserver.OnWindowFocusChangeListener import android.view.ViewTreeObserver.OnWindowVisibilityChangeListener import androidx.lifecycle.Lifecycle import androidx.lifecycle.Lifecycle.State.CREATED import androidx.lifecycle.Lifecycle.State.DESTROYED import androidx.lifecycle.Lifecycle.State.RESUMED import androidx.lifecycle.Lifecycle.State.STARTED import androidx.lifecycle.LifecycleRegistry import androidx.lifecycle.setViewTreeLifecycleOwner import androidx.savedstate.SavedStateRegistry import androidx.savedstate.SavedStateRegistryController import androidx.savedstate.setViewTreeSavedStateRegistryOwner import com.android.launcher3.DeviceProfile.OnDeviceProfileChangeListener import com.android.launcher3.Utilities import com.android.launcher3.views.ActivityContext /** * A context wrapper with lifecycle tracking based on the window events on the rootView of the * [ActivityContext] */ abstract class BaseContext @JvmOverloads constructor(base: Context, themeResId: Int, private val destroyOnDetach: Boolean = true) : ContextThemeWrapper(base, themeResId), ActivityContext { private val listeners = mutableListOf() private val savedStateRegistryController = SavedStateRegistryController.create(this) private val lifecycleRegistry = LifecycleRegistry(this) private val cleanupSet = WeakCleanupSet(this) override val savedStateRegistry: SavedStateRegistry get() = savedStateRegistryController.savedStateRegistry override val lifecycle: Lifecycle get() = lifecycleRegistry private val viewCache = ViewCache() init { Executors.MAIN_EXECUTOR.execute { savedStateRegistryController.performAttach() savedStateRegistryController.performRestore(null) } } override fun getOnDeviceProfileChangeListeners() = listeners private val finishActions = RunnableList() /** Called when the root view is created for this context */ fun onViewCreated() { val view = rootView val attachListener = object : OnAttachStateChangeListener { override fun onViewAttachedToWindow(view: View) { view.rootView.setViewTreeLifecycleOwner(this@BaseContext) view.rootView.setViewTreeSavedStateRegistryOwner(this@BaseContext) lifecycleRegistry.handleLifecycleEvent(Lifecycle.Event.ON_CREATE) val treeObserver = view.viewTreeObserver val focusListener = OnWindowFocusChangeListener { updateState() } treeObserver.addOnWindowFocusChangeListener(focusListener) finishActions.add { treeObserver.removeOnWindowFocusChangeListener(focusListener) } if (Utilities.ATLEAST_V) { val visibilityListener = OnWindowVisibilityChangeListener { updateState() } treeObserver.addOnWindowVisibilityChangeListener(visibilityListener) finishActions.add { treeObserver.removeOnWindowVisibilityChangeListener(visibilityListener) } } } override fun onViewDetachedFromWindow(view: View) { if (destroyOnDetach) onViewDestroyed() } } view.addOnAttachStateChangeListener(attachListener) finishActions.add { view.removeOnAttachStateChangeListener(attachListener) } if (view.isAttachedToWindow) attachListener.onViewAttachedToWindow(view) updateState() } override fun getViewCache() = viewCache override fun getOwnerCleanupSet() = cleanupSet private fun updateState() { if (lifecycleRegistry.currentState.isAtLeast(CREATED)) { lifecycleRegistry.currentState = if (rootView.windowVisibility != View.VISIBLE) CREATED else (if (!rootView.hasWindowFocus()) STARTED else RESUMED) } } fun onViewDestroyed() { if ( !lifecycleRegistry.currentState.isAtLeast(CREATED) && lifecycleRegistry.currentState != DESTROYED ) { lifecycleRegistry.currentState = CREATED } lifecycleRegistry.handleLifecycleEvent(Lifecycle.Event.ON_DESTROY) finishActions.executeAllAndDestroy() } }