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