• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * 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 package com.android.systemui.compose
18 
19 import android.view.View
20 import androidx.lifecycle.Lifecycle
21 import androidx.lifecycle.findViewTreeLifecycleOwner
22 import androidx.lifecycle.setViewTreeLifecycleOwner
23 import androidx.savedstate.SavedStateRegistryController
24 import androidx.savedstate.SavedStateRegistryOwner
25 import com.android.compose.animation.ViewTreeSavedStateRegistryOwner
26 import com.android.systemui.lifecycle.ViewLifecycleOwner
27 
28 /**
29  * An initializer to use Compose outside of an Activity, e.g. inside a window added directly using
30  * [android.view.WindowManager.addView] (like the shade or status bar) or inside a dialog.
31  *
32  * Example:
33  * ```
34  *    windowManager.addView(MyWindowRootView(context), /* layoutParams */)
35  *
36  *    class MyWindowRootView(context: Context) : FrameLayout(context) {
37  *        override fun onAttachedToWindow() {
38  *            super.onAttachedToWindow()
39  *            ComposeInitializer.onAttachedToWindow(this)
40  *        }
41  *
42  *        override fun onDetachedFromWindow() {
43  *            super.onDetachedFromWindow()
44  *            ComposeInitializer.onDetachedFromWindow(this)
45  *        }
46  *    }
47  * ```
48  */
49 object ComposeInitializer {
50     /** Function to be called on your window root view's [View.onAttachedToWindow] function. */
onAttachedToWindownull51     fun onAttachedToWindow(root: View) {
52         if (root.findViewTreeLifecycleOwner() != null) {
53             error("root $root already has a LifecycleOwner")
54         }
55 
56         val parent = root.parent
57         if (parent is View && parent.id != android.R.id.content) {
58             error(
59                 "ComposeInitializer.onAttachedToWindow(View) must be called on the content child." +
60                     "Outside of activities and dialogs, this is usually the top-most View of a " +
61                     "window."
62             )
63         }
64 
65         // The lifecycle owner, which is STARTED when [root] is visible and RESUMED when [root] is
66         // both visible and focused.
67         val lifecycleOwner = ViewLifecycleOwner(root)
68 
69         // We create a trivial implementation of [SavedStateRegistryOwner] that does not do any save
70         // or restore because SystemUI process is always running and top-level windows using this
71         // initializer are created once, when the process is started.
72         val savedStateRegistryOwner =
73             object : SavedStateRegistryOwner {
74                 private val savedStateRegistryController =
75                     SavedStateRegistryController.create(this).apply { performRestore(null) }
76 
77                 override val savedStateRegistry = savedStateRegistryController.savedStateRegistry
78 
79                 override val lifecycle: Lifecycle
80                     get() = lifecycleOwner.lifecycle
81             }
82 
83         // We must call [ViewLifecycleOwner.onCreate] after creating the [SavedStateRegistryOwner]
84         // because `onCreate` might move the lifecycle state to STARTED which will make
85         // [SavedStateRegistryController.performRestore] throw.
86         lifecycleOwner.onCreate()
87 
88         // Set the owners on the root. They will be reused by any ComposeView inside the root
89         // hierarchy.
90         root.setViewTreeLifecycleOwner(lifecycleOwner)
91         ViewTreeSavedStateRegistryOwner.set(root, savedStateRegistryOwner)
92     }
93 
94     /** Function to be called on your window root view's [View.onDetachedFromWindow] function. */
onDetachedFromWindownull95     fun onDetachedFromWindow(root: View) {
96         (root.findViewTreeLifecycleOwner() as ViewLifecycleOwner).onDestroy()
97         root.setViewTreeLifecycleOwner(null)
98         ViewTreeSavedStateRegistryOwner.set(root, null)
99     }
100 }
101