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.camera 18 19 import android.app.ActivityManager 20 import android.app.ActivityOptions 21 import android.app.IActivityTaskManager 22 import android.content.ContentResolver 23 import android.content.Context 24 import android.content.Intent 25 import android.content.pm.PackageManager 26 import android.content.pm.ResolveInfo 27 import android.os.RemoteException 28 import android.util.Log 29 import android.view.WindowManager 30 import com.android.systemui.ActivityIntentHelper 31 import com.android.systemui.dagger.SysUISingleton 32 import com.android.systemui.dagger.qualifiers.Main 33 import com.android.systemui.plugins.ActivityStarter 34 import com.android.systemui.shared.system.ActivityManagerKt.isInForeground 35 import com.android.systemui.statusbar.StatusBarState 36 import com.android.systemui.statusbar.phone.CentralSurfaces 37 import com.android.systemui.statusbar.phone.StatusBarKeyguardViewManager 38 import com.android.systemui.statusbar.policy.KeyguardStateController 39 import com.android.systemui.user.domain.interactor.SelectedUserInteractor 40 import java.util.concurrent.Executor 41 import javax.inject.Inject 42 43 /** 44 * Helps with handling camera-related gestures (for example, double-tap the power button to launch 45 * the camera). 46 */ 47 @SysUISingleton 48 class CameraGestureHelper @Inject constructor( 49 private val context: Context, 50 private val centralSurfaces: CentralSurfaces, 51 private val keyguardStateController: KeyguardStateController, 52 private val statusBarKeyguardViewManager: StatusBarKeyguardViewManager, 53 private val packageManager: PackageManager, 54 private val activityManager: ActivityManager, 55 private val activityStarter: ActivityStarter, 56 private val activityIntentHelper: ActivityIntentHelper, 57 private val activityTaskManager: IActivityTaskManager, 58 private val cameraIntents: CameraIntentsWrapper, 59 private val contentResolver: ContentResolver, 60 @Main private val uiExecutor: Executor, 61 private val selectedUserInteractor: SelectedUserInteractor, 62 ) { 63 /** 64 * Whether the camera application can be launched for the camera launch gesture. 65 */ canCameraGestureBeLaunchednull66 fun canCameraGestureBeLaunched(statusBarState: Int): Boolean { 67 if (!centralSurfaces.isCameraAllowedByAdmin) { 68 return false 69 } 70 71 val resolveInfo: ResolveInfo? = packageManager.resolveActivityAsUser( 72 getStartCameraIntent(selectedUserInteractor.getSelectedUserId()), 73 PackageManager.MATCH_DEFAULT_ONLY, 74 selectedUserInteractor.getSelectedUserId() 75 ) 76 val resolvedPackage = resolveInfo?.activityInfo?.packageName 77 return (resolvedPackage != null && 78 (statusBarState != StatusBarState.SHADE || 79 !activityManager.isInForeground(resolvedPackage))) 80 } 81 82 /** 83 * Launches the camera. 84 * 85 * @param source The source of the camera launch, to be passed to the camera app via [Intent] 86 */ launchCameranull87 fun launchCamera(source: Int) { 88 val intent: Intent = getStartCameraIntent(selectedUserInteractor.getSelectedUserId()) 89 intent.putExtra(CameraIntents.EXTRA_LAUNCH_SOURCE, source) 90 val wouldLaunchResolverActivity = activityIntentHelper.wouldLaunchResolverActivity( 91 intent, selectedUserInteractor.getSelectedUserId() 92 ) 93 if (CameraIntents.isSecureCameraIntent(intent) && !wouldLaunchResolverActivity) { 94 uiExecutor.execute { 95 // Normally an activity will set its requested rotation animation on its window. 96 // However when launching an activity causes the orientation to change this is too 97 // late. In these cases, the default animation is used. This doesn't look good for 98 // the camera (as it rotates the camera contents out of sync with physical reality). 99 // Therefore, we ask the WindowManager to force the cross-fade animation if an 100 // orientation change happens to occur during the launch. 101 val activityOptions = ActivityOptions.makeBasic() 102 activityOptions.setDisallowEnterPictureInPictureWhileLaunching(true) 103 activityOptions.rotationAnimationHint = 104 WindowManager.LayoutParams.ROTATION_ANIMATION_SEAMLESS 105 try { 106 activityTaskManager.startActivityAsUser( 107 null, 108 context.basePackageName, 109 context.attributionTag, 110 intent, 111 intent.resolveTypeIfNeeded(contentResolver), 112 null, 113 null, 114 0, 115 Intent.FLAG_ACTIVITY_NEW_TASK, 116 null, 117 activityOptions.toBundle(), 118 selectedUserInteractor.getSelectedUserId(true), 119 ) 120 } catch (e: RemoteException) { 121 Log.w( 122 "CameraGestureHelper", 123 "Unable to start camera activity", 124 e 125 ) 126 } 127 } 128 } else { 129 // We need to delay starting the activity because ResolverActivity finishes itself if 130 // launched from behind the lock-screen. 131 activityStarter.startActivity(intent, false /* dismissShade */) 132 } 133 134 // Call this to make sure that the keyguard returns if the app that is being launched 135 // crashes after a timeout. 136 centralSurfaces.startLaunchTransitionTimeout() 137 // Call this to make sure the keyguard is ready to be dismissed once the next intent is 138 // handled by the OS (in our case it is the activity we started right above) 139 statusBarKeyguardViewManager.readyForKeyguardDone() 140 } 141 142 /** 143 * Returns an [Intent] that can be used to start the camera app such that it occludes the 144 * lock-screen, if needed. 145 */ getStartCameraIntentnull146 private fun getStartCameraIntent(userId: Int): Intent { 147 val isLockScreenDismissible = keyguardStateController.canDismissLockScreen() 148 val isSecure = keyguardStateController.isMethodSecure 149 return if (isSecure && !isLockScreenDismissible) { 150 cameraIntents.getSecureCameraIntent(userId) 151 } else { 152 cameraIntents.getInsecureCameraIntent(userId) 153 } 154 } 155 } 156