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