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