1 /* <lambda>null2 * Copyright (C) 2021 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.controls.ui 18 19 import android.content.BroadcastReceiver 20 import android.content.Context 21 import android.content.Intent 22 import android.content.IntentFilter 23 import android.content.pm.ActivityInfo 24 import android.content.res.Configuration 25 import android.os.Bundle 26 import android.os.RemoteException 27 import android.service.dreams.IDreamManager 28 import android.view.View 29 import android.view.ViewGroup 30 import android.view.WindowInsets 31 import android.view.WindowInsets.Type 32 import android.view.WindowManager 33 import androidx.activity.ComponentActivity 34 import com.android.systemui.res.R 35 import com.android.systemui.broadcast.BroadcastDispatcher 36 import com.android.systemui.controls.management.ControlsAnimations 37 import com.android.systemui.controls.settings.ControlsSettingsDialogManager 38 import com.android.systemui.flags.FeatureFlags 39 import com.android.systemui.statusbar.policy.KeyguardStateController 40 import javax.inject.Inject 41 42 /** 43 * Displays Device Controls inside an activity. This activity is available to be displayed over the 44 * lockscreen if the user has allowed it via 45 * [android.provider.Settings.Secure.LOCKSCREEN_SHOW_CONTROLS]. This activity will be 46 * destroyed on SCREEN_OFF events, due to issues with occluded activities over lockscreen as well as 47 * user expectations for the activity to not continue running. 48 */ 49 // Open for testing 50 open class ControlsActivity @Inject constructor( 51 private val uiController: ControlsUiController, 52 private val broadcastDispatcher: BroadcastDispatcher, 53 private val dreamManager: IDreamManager, 54 private val featureFlags: FeatureFlags, 55 private val controlsSettingsDialogManager: ControlsSettingsDialogManager, 56 private val keyguardStateController: KeyguardStateController 57 ) : ComponentActivity() { 58 59 private val lastConfiguration = Configuration() 60 61 private lateinit var parent: ViewGroup 62 private lateinit var broadcastReceiver: BroadcastReceiver 63 private var mExitToDream: Boolean = false 64 65 override fun onCreate(savedInstanceState: Bundle?) { 66 super.onCreate(savedInstanceState) 67 lastConfiguration.setTo(resources.configuration) 68 window.addPrivateFlags(WindowManager.LayoutParams.PRIVATE_FLAG_TRUSTED_OVERLAY) 69 70 setContentView(R.layout.controls_fullscreen) 71 72 lifecycle.addObserver( 73 ControlsAnimations.observerForAnimations( 74 requireViewById(R.id.control_detail_root), 75 window, 76 intent, 77 false 78 ) 79 ) 80 81 requireViewById<ViewGroup>(R.id.control_detail_root).apply { 82 setOnApplyWindowInsetsListener { 83 v: View, insets: WindowInsets -> 84 v.apply { 85 val l = getPaddingLeft() 86 val t = getPaddingTop() 87 val r = getPaddingRight() 88 setPadding(l, t, r, insets.getInsets(Type.systemBars()).bottom) 89 } 90 91 WindowInsets.CONSUMED 92 } 93 } 94 95 initBroadcastReceiver() 96 } 97 98 override fun onConfigurationChanged(newConfig: Configuration) { 99 super.onConfigurationChanged(newConfig) 100 val interestingFlags = ActivityInfo.CONFIG_ORIENTATION or 101 ActivityInfo.CONFIG_SCREEN_SIZE or 102 ActivityInfo.CONFIG_SMALLEST_SCREEN_SIZE 103 if (lastConfiguration.diff(newConfig) and interestingFlags != 0 ) { 104 uiController.onSizeChange() 105 } 106 lastConfiguration.setTo(newConfig) 107 } 108 109 override fun onStart() { 110 super.onStart() 111 112 parent = requireViewById(R.id.control_detail_root) 113 parent.alpha = 0f 114 if (!keyguardStateController.isUnlocked) { 115 controlsSettingsDialogManager.maybeShowDialog(this) { 116 uiController.show(parent, { finishOrReturnToDream() }, this) 117 } 118 } else { 119 uiController.show(parent, { finishOrReturnToDream() }, this) 120 } 121 122 ControlsAnimations.enterAnimation(parent).start() 123 } 124 125 override fun onResume() { 126 super.onResume() 127 mExitToDream = intent.getBooleanExtra(ControlsUiController.EXIT_TO_DREAM, false) 128 } 129 130 fun finishOrReturnToDream() { 131 if (mExitToDream) { 132 try { 133 mExitToDream = false 134 dreamManager.dream() 135 return 136 } catch (e: RemoteException) { 137 // Fall through 138 } 139 } 140 finish() 141 } 142 143 override fun onBackPressed() { 144 finishOrReturnToDream() 145 } 146 147 override fun onStop() { 148 super.onStop() 149 mExitToDream = false 150 151 // parent is set in onStart, so the field is initialized when we get here 152 uiController.hide(parent) 153 controlsSettingsDialogManager.closeDialog() 154 } 155 156 override fun onDestroy() { 157 super.onDestroy() 158 159 unregisterReceiver() 160 } 161 162 protected open fun unregisterReceiver() { 163 broadcastDispatcher.unregisterReceiver(broadcastReceiver) 164 } 165 166 private fun initBroadcastReceiver() { 167 broadcastReceiver = object : BroadcastReceiver() { 168 override fun onReceive(context: Context, intent: Intent) { 169 val action = intent.getAction() 170 if (action == Intent.ACTION_SCREEN_OFF || 171 action == Intent.ACTION_DREAMING_STARTED) { 172 finish() 173 } 174 } 175 } 176 177 val filter = IntentFilter() 178 filter.addAction(Intent.ACTION_SCREEN_OFF) 179 filter.addAction(Intent.ACTION_DREAMING_STARTED) 180 broadcastDispatcher.registerReceiver(broadcastReceiver, filter) 181 } 182 } 183