• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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