• 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.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