1 /* 2 * 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.app.Activity 20 import android.content.BroadcastReceiver 21 import android.content.Context 22 import android.content.Intent 23 import android.content.IntentFilter 24 import android.content.pm.ActivityInfo 25 import android.content.res.Configuration 26 import android.os.Bundle 27 import android.os.RemoteException 28 import android.service.dreams.IDreamManager 29 import android.view.ViewGroup 30 import android.view.WindowManager 31 import androidx.activity.ComponentActivity 32 import com.android.systemui.broadcast.BroadcastDispatcher 33 import com.android.systemui.controls.management.ControlsAnimations 34 import com.android.systemui.controls.management.ControlsManagementActivity 35 import com.android.systemui.controls.management.applyInsets 36 import com.android.systemui.controls.settings.ControlsSettingsDialogManager 37 import com.android.systemui.flags.FeatureFlags 38 import com.android.systemui.res.R 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 destroyed on 46 * SCREEN_OFF events, due to issues with occluded activities over lockscreen as well as user 47 * expectations for the activity to not continue running. 48 */ 49 // Open for testing 50 open class ControlsActivity 51 @Inject 52 constructor( 53 private val uiController: ControlsUiController, 54 private val broadcastDispatcher: BroadcastDispatcher, 55 private val dreamManager: IDreamManager, 56 private val featureFlags: FeatureFlags, 57 private val controlsSettingsDialogManager: ControlsSettingsDialogManager, 58 private val keyguardStateController: KeyguardStateController, 59 ) : ComponentActivity(), ControlsManagementActivity { 60 61 override val activity: Activity 62 get() = this 63 64 private val lastConfiguration = Configuration() 65 66 private lateinit var parent: ViewGroup 67 private lateinit var broadcastReceiver: BroadcastReceiver 68 private var mExitToDream: Boolean = false 69 onCreatenull70 override fun onCreate(savedInstanceState: Bundle?) { 71 super.onCreate(savedInstanceState) 72 lastConfiguration.setTo(resources.configuration) 73 window.addPrivateFlags(WindowManager.LayoutParams.PRIVATE_FLAG_TRUSTED_OVERLAY) 74 75 setContentView(R.layout.controls_fullscreen) 76 77 applyInsets(R.id.control_detail_root) 78 79 lifecycle.addObserver( 80 ControlsAnimations.observerForAnimations( 81 requireViewById(R.id.control_detail_root), 82 window, 83 intent, 84 false, 85 ) 86 ) 87 88 initBroadcastReceiver() 89 } 90 onConfigurationChangednull91 override fun onConfigurationChanged(newConfig: Configuration) { 92 super.onConfigurationChanged(newConfig) 93 val interestingFlags = 94 ActivityInfo.CONFIG_ORIENTATION or 95 ActivityInfo.CONFIG_SCREEN_SIZE or 96 ActivityInfo.CONFIG_SMALLEST_SCREEN_SIZE 97 if (lastConfiguration.diff(newConfig) and interestingFlags != 0) { 98 uiController.onSizeChange() 99 } 100 lastConfiguration.setTo(newConfig) 101 } 102 onStartnull103 override fun onStart() { 104 super.onStart() 105 106 parent = requireViewById(R.id.control_detail_root) 107 parent.alpha = 0f 108 if (!keyguardStateController.isUnlocked) { 109 controlsSettingsDialogManager.maybeShowDialog(this) { 110 uiController.show(parent, { finishOrReturnToDream() }, this) 111 } 112 } else { 113 uiController.show(parent, { finishOrReturnToDream() }, this) 114 } 115 116 ControlsAnimations.enterAnimation(parent).start() 117 } 118 onResumenull119 override fun onResume() { 120 super.onResume() 121 mExitToDream = intent.getBooleanExtra(ControlsUiController.EXIT_TO_DREAM, false) 122 } 123 finishOrReturnToDreamnull124 fun finishOrReturnToDream() { 125 if (mExitToDream) { 126 try { 127 mExitToDream = false 128 dreamManager.dream() 129 return 130 } catch (e: RemoteException) { 131 // Fall through 132 } 133 } 134 finish() 135 } 136 onBackPressednull137 override fun onBackPressed() { 138 finishOrReturnToDream() 139 } 140 onStopnull141 override fun onStop() { 142 super.onStop() 143 mExitToDream = false 144 145 // parent is set in onStart, so the field is initialized when we get here 146 uiController.hide(parent) 147 controlsSettingsDialogManager.closeDialog() 148 } 149 onDestroynull150 override fun onDestroy() { 151 super.onDestroy() 152 153 unregisterReceiver() 154 } 155 unregisterReceivernull156 protected open fun unregisterReceiver() { 157 broadcastDispatcher.unregisterReceiver(broadcastReceiver) 158 } 159 initBroadcastReceivernull160 private fun initBroadcastReceiver() { 161 broadcastReceiver = 162 object : BroadcastReceiver() { 163 override fun onReceive(context: Context, intent: Intent) { 164 val action = intent.getAction() 165 if ( 166 action == Intent.ACTION_SCREEN_OFF || 167 action == Intent.ACTION_DREAMING_STARTED 168 ) { 169 finish() 170 } 171 } 172 } 173 174 val filter = IntentFilter() 175 filter.addAction(Intent.ACTION_SCREEN_OFF) 176 filter.addAction(Intent.ACTION_DREAMING_STARTED) 177 broadcastDispatcher.registerReceiver(broadcastReceiver, filter) 178 } 179 } 180