1 /*
2  * Copyright (C) 2016 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 androidx.core.app
17 
18 import android.app.Activity
19 import android.os.Build
20 import android.os.Bundle
21 import android.view.KeyEvent
22 import androidx.annotation.CallSuper
23 import androidx.annotation.RestrictTo
24 import androidx.collection.SimpleArrayMap
25 import androidx.core.view.KeyEventDispatcher
26 import androidx.lifecycle.Lifecycle
27 import androidx.lifecycle.LifecycleOwner
28 import androidx.lifecycle.LifecycleRegistry
29 import androidx.lifecycle.ReportFragment
30 
31 /**
32  * Base class for activities to allow intercepting [KeyEvent] methods in a composable way in core.
33  *
34  * You most certainly **don't** want to extend this class, but instead extend
35  * `androidx.activity.ComponentActivity`.
36  */
37 @RestrictTo(RestrictTo.Scope.LIBRARY_GROUP_PREFIX)
38 public open class ComponentActivity : Activity(), LifecycleOwner, KeyEventDispatcher.Component {
39     /**
40      * Storage for [ExtraData] instances.
41      *
42      * Note that these objects are not retained across configuration changes
43      */
44     @Suppress("DEPRECATION")
45     private val extraDataMap = SimpleArrayMap<Class<out ExtraData>, ExtraData>()
46 
47     /**
48      * This is only used for apps that have not switched to Fragments 1.1.0, where this behavior is
49      * provided by `androidx.activity.ComponentActivity`.
50      */
51     @Suppress("LeakingThis") private val lifecycleRegistry = LifecycleRegistry(this)
52 
53     /**
54      * Store an instance of [ExtraData] for later retrieval by class name via [getExtraData].
55      *
56      * Note that these objects are not retained across configuration changes
57      *
58      * @see getExtraData
59      */
60     @RestrictTo(RestrictTo.Scope.LIBRARY_GROUP_PREFIX)
61     @Suppress("DEPRECATION")
62     @Deprecated("Use {@link View#setTag(int, Object)} with the window's decor view.")
putExtraDatanull63     public open fun putExtraData(extraData: ExtraData) {
64         extraDataMap.put(extraData.javaClass, extraData)
65     }
66 
onCreatenull67     override fun onCreate(savedInstanceState: Bundle?) {
68         super.onCreate(savedInstanceState)
69         ReportFragment.injectIfNeededIn(this)
70     }
71 
72     @CallSuper
onSaveInstanceStatenull73     override fun onSaveInstanceState(outState: Bundle) {
74         lifecycleRegistry.currentState = Lifecycle.State.CREATED
75         super.onSaveInstanceState(outState)
76     }
77 
78     /**
79      * Retrieves a previously set [ExtraData] by class name.
80      *
81      * @see putExtraData
82      */
83     @RestrictTo(RestrictTo.Scope.LIBRARY_GROUP_PREFIX)
84     @Suppress("DEPRECATION", "UNCHECKED_CAST")
85     @Deprecated("Use {@link View#getTag(int)} with the window's decor view.")
getExtraDatanull86     public open fun <T : ExtraData> getExtraData(extraDataClass: Class<T>): T? {
87         return extraDataMap[extraDataClass] as T?
88     }
89 
90     override val lifecycle: Lifecycle
91         get() = lifecycleRegistry
92 
93     /** @param event */
94     @RestrictTo(RestrictTo.Scope.LIBRARY_GROUP_PREFIX)
superDispatchKeyEventnull95     override fun superDispatchKeyEvent(event: KeyEvent): Boolean {
96         return super.dispatchKeyEvent(event)
97     }
98 
dispatchKeyShortcutEventnull99     override fun dispatchKeyShortcutEvent(event: KeyEvent): Boolean {
100         val decor = window.decorView
101         return if (KeyEventDispatcher.dispatchBeforeHierarchy(decor, event)) {
102             true
103         } else super.dispatchKeyShortcutEvent(event)
104     }
105 
dispatchKeyEventnull106     override fun dispatchKeyEvent(event: KeyEvent): Boolean {
107         val decor = window.decorView
108         return if (KeyEventDispatcher.dispatchBeforeHierarchy(decor, event)) {
109             true
110         } else KeyEventDispatcher.dispatchKeyEvent(this, decor, this, event)
111     }
112 
113     /**
114      * Checks if the internal state should be dump, as some special args are handled by [Activity]
115      * itself.
116      *
117      * Subclasses implementing [Activity.dump] should typically start with:
118      * ```
119      * override fun dump(
120      *   prefix: String,
121      *   fd: FileDescriptor?,
122      *   writer: PrintWriter,
123      *   args: Array<out String>?
124      * ) {
125      *   super.dump(prefix, fd, writer, args)
126      *
127      *   if (!shouldDumpInternalState(args)) {
128      *     return
129      *   }
130      *   // dump internal state
131      * }
132      * ```
133      */
shouldDumpInternalStatenull134     protected fun shouldDumpInternalState(args: Array<String>?): Boolean {
135         return !shouldSkipDump(args)
136     }
137 
shouldSkipDumpnull138     private fun shouldSkipDump(args: Array<String>?): Boolean {
139         if (!args.isNullOrEmpty()) {
140             // NOTE: values below arke hardcoded on framework's Activity (like dumpInner())
141             when (args[0]) {
142                 "--autofill" -> return Build.VERSION.SDK_INT >= Build.VERSION_CODES.O
143                 "--contentcapture" -> return Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q
144                 "--translation" -> return Build.VERSION.SDK_INT >= Build.VERSION_CODES.S
145                 "--list-dumpables",
146                 "--dump-dumpable" -> return Build.VERSION.SDK_INT >= 33
147             }
148         }
149         return false
150     }
151 
152     @RestrictTo(RestrictTo.Scope.LIBRARY_GROUP_PREFIX)
153     @Deprecated(
154         """Store the object you want to save directly by using
155       {@link View#setTag(int, Object)} with the window's decor view."""
156     )
157     public open class ExtraData
158 }
159