blob: 368a97a251e59c8c523fe8be438874a176b60ba0 [file] [log] [blame]
John Spurlockdcc96812012-10-25 14:35:19 -04001/*
2 * Copyright (C) 2012 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
Jim Miller5ecd8112013-01-09 18:50:26 -080017package com.android.keyguard;
18
19import com.android.internal.widget.LockPatternUtils;
John Spurlockdcc96812012-10-25 14:35:19 -040020
21import android.app.ActivityManagerNative;
Adam Powellcfc30862012-10-29 18:21:31 -070022import android.app.ActivityOptions;
John Spurlock57f928f2012-11-02 13:09:25 -040023import android.app.IActivityManager.WaitResult;
John Spurlock65cbcbe2012-11-06 11:39:34 -050024import android.appwidget.AppWidgetManager;
25import android.appwidget.AppWidgetProviderInfo;
John Spurlockdcc96812012-10-25 14:35:19 -040026import android.content.ActivityNotFoundException;
27import android.content.Context;
28import android.content.Intent;
29import android.content.pm.PackageManager;
30import android.content.pm.ResolveInfo;
Adam Powellcfc30862012-10-29 18:21:31 -070031import android.os.Bundle;
John Spurlock57f928f2012-11-02 13:09:25 -040032import android.os.Handler;
John Spurlockdcc96812012-10-25 14:35:19 -040033import android.os.RemoteException;
John Spurlockdbe24b72012-11-01 13:01:05 -040034import android.os.SystemClock;
John Spurlockdcc96812012-10-25 14:35:19 -040035import android.os.UserHandle;
36import android.provider.MediaStore;
37import android.util.Log;
38import android.view.WindowManager;
39
Jim Miller5ecd8112013-01-09 18:50:26 -080040import com.android.keyguard.KeyguardHostView.OnDismissAction;
John Spurlockdcc96812012-10-25 14:35:19 -040041
42import java.util.List;
43
44public abstract class KeyguardActivityLauncher {
45 private static final String TAG = KeyguardActivityLauncher.class.getSimpleName();
46 private static final boolean DEBUG = KeyguardHostView.DEBUG;
47 private static final String META_DATA_KEYGUARD_LAYOUT = "com.android.keyguard.layout";
48 private static final Intent SECURE_CAMERA_INTENT =
49 new Intent(MediaStore.INTENT_ACTION_STILL_IMAGE_CAMERA_SECURE)
50 .addFlags(Intent.FLAG_ACTIVITY_EXCLUDE_FROM_RECENTS);
51 private static final Intent INSECURE_CAMERA_INTENT =
52 new Intent(MediaStore.INTENT_ACTION_STILL_IMAGE_CAMERA);
53
54 abstract Context getContext();
55
John Spurlockdcc96812012-10-25 14:35:19 -040056 abstract LockPatternUtils getLockPatternUtils();
57
Jim Miller7751ff62014-01-14 18:57:03 -080058 abstract void setOnDismissAction(OnDismissAction action);
59
60 abstract void requestDismissKeyguard();
61
John Spurlockdcc96812012-10-25 14:35:19 -040062 public static class CameraWidgetInfo {
63 public String contextPackage;
64 public int layoutId;
65 }
66
67 public CameraWidgetInfo getCameraWidgetInfo() {
68 CameraWidgetInfo info = new CameraWidgetInfo();
69 Intent intent = getCameraIntent();
70 PackageManager packageManager = getContext().getPackageManager();
71 final List<ResolveInfo> appList = packageManager.queryIntentActivitiesAsUser(
72 intent, PackageManager.MATCH_DEFAULT_ONLY, getLockPatternUtils().getCurrentUser());
73 if (appList.size() == 0) {
74 if (DEBUG) Log.d(TAG, "getCameraWidgetInfo(): Nothing found");
75 return null;
76 }
77 ResolveInfo resolved = packageManager.resolveActivityAsUser(intent,
78 PackageManager.MATCH_DEFAULT_ONLY | PackageManager.GET_META_DATA,
79 getLockPatternUtils().getCurrentUser());
80 if (DEBUG) Log.d(TAG, "getCameraWidgetInfo(): resolved: " + resolved);
81 if (wouldLaunchResolverActivity(resolved, appList)) {
82 if (DEBUG) Log.d(TAG, "getCameraWidgetInfo(): Would launch resolver");
83 return info;
84 }
85 if (resolved == null || resolved.activityInfo == null) {
86 return null;
87 }
88 if (resolved.activityInfo.metaData == null || resolved.activityInfo.metaData.isEmpty()) {
89 if (DEBUG) Log.d(TAG, "getCameraWidgetInfo(): no metadata found");
90 return info;
91 }
92 int layoutId = resolved.activityInfo.metaData.getInt(META_DATA_KEYGUARD_LAYOUT);
93 if (layoutId == 0) {
94 if (DEBUG) Log.d(TAG, "getCameraWidgetInfo(): no layout specified");
95 return info;
96 }
97 info.contextPackage = resolved.activityInfo.packageName;
98 info.layoutId = layoutId;
99 return info;
100 }
101
John Spurlock57f928f2012-11-02 13:09:25 -0400102 public void launchCamera(Handler worker, Runnable onSecureCameraStarted) {
John Spurlockdcc96812012-10-25 14:35:19 -0400103 LockPatternUtils lockPatternUtils = getLockPatternUtils();
Ruben Brunk21a99dd2013-11-05 18:29:06 -0800104
105 // Workaround to avoid camera release/acquisition race when resuming face unlock
106 // after showing lockscreen camera (bug 11063890).
107 KeyguardUpdateMonitor.getInstance(getContext()).setAlternateUnlockEnabled(false);
108
John Spurlockdcc96812012-10-25 14:35:19 -0400109 if (lockPatternUtils.isSecure()) {
110 // Launch the secure version of the camera
111 if (wouldLaunchResolverActivity(SECURE_CAMERA_INTENT)) {
112 // TODO: Show disambiguation dialog instead.
113 // For now, we'll treat this like launching any other app from secure keyguard.
114 // When they do, user sees the system's ResolverActivity which lets them choose
115 // which secure camera to use.
John Spurlock57f928f2012-11-02 13:09:25 -0400116 launchActivity(SECURE_CAMERA_INTENT, false, false, null, null);
John Spurlockdcc96812012-10-25 14:35:19 -0400117 } else {
John Spurlock57f928f2012-11-02 13:09:25 -0400118 launchActivity(SECURE_CAMERA_INTENT, true, false, worker, onSecureCameraStarted);
John Spurlockdcc96812012-10-25 14:35:19 -0400119 }
120 } else {
John Spurlockdcc96812012-10-25 14:35:19 -0400121 // Launch the normal camera
John Spurlock57f928f2012-11-02 13:09:25 -0400122 launchActivity(INSECURE_CAMERA_INTENT, false, false, null, null);
John Spurlockdcc96812012-10-25 14:35:19 -0400123 }
124 }
125
John Spurlock65cbcbe2012-11-06 11:39:34 -0500126 public void launchWidgetPicker(int appWidgetId) {
127 Intent pickIntent = new Intent(AppWidgetManager.ACTION_KEYGUARD_APPWIDGET_PICK);
128
129 pickIntent.putExtra(AppWidgetManager.EXTRA_APPWIDGET_ID, appWidgetId);
130 pickIntent.putExtra(AppWidgetManager.EXTRA_CUSTOM_SORT, false);
131 pickIntent.putExtra(AppWidgetManager.EXTRA_CATEGORY_FILTER,
132 AppWidgetProviderInfo.WIDGET_CATEGORY_KEYGUARD);
133
134 Bundle options = new Bundle();
135 options.putInt(AppWidgetManager.OPTION_APPWIDGET_HOST_CATEGORY,
136 AppWidgetProviderInfo.WIDGET_CATEGORY_KEYGUARD);
137 pickIntent.putExtra(AppWidgetManager.EXTRA_APPWIDGET_OPTIONS, options);
138 pickIntent.addFlags(
139 Intent.FLAG_ACTIVITY_NEW_TASK
140 | Intent.FLAG_ACTIVITY_SINGLE_TOP
141 | Intent.FLAG_ACTIVITY_CLEAR_TOP
142 | Intent.FLAG_ACTIVITY_EXCLUDE_FROM_RECENTS);
143
144 launchActivity(pickIntent, false, false, null, null);
145 }
146
John Spurlockdcc96812012-10-25 14:35:19 -0400147 /**
148 * Launches the said intent for the current foreground user.
John Spurlock57f928f2012-11-02 13:09:25 -0400149 *
John Spurlockdcc96812012-10-25 14:35:19 -0400150 * @param intent
151 * @param showsWhileLocked true if the activity can be run on top of keyguard.
John Spurlock57f928f2012-11-02 13:09:25 -0400152 * See {@link WindowManager#FLAG_SHOW_WHEN_LOCKED}
153 * @param useDefaultAnimations true if default transitions should be used, else suppressed.
154 * @param worker if supplied along with onStarted, used to launch the blocking activity call.
155 * @param onStarted if supplied along with worker, called after activity is started.
John Spurlockdcc96812012-10-25 14:35:19 -0400156 */
John Spurlock57f928f2012-11-02 13:09:25 -0400157 public void launchActivity(final Intent intent,
158 boolean showsWhileLocked,
159 boolean useDefaultAnimations,
160 final Handler worker,
161 final Runnable onStarted) {
Daniel Sandler6ed3fca2012-11-08 09:58:27 -0500162
John Spurlockdcc96812012-10-25 14:35:19 -0400163 final Context context = getContext();
John Spurlock65cbcbe2012-11-06 11:39:34 -0500164 final Bundle animation = useDefaultAnimations ? null
165 : ActivityOptions.makeCustomAnimation(context, 0, 0).toBundle();
Daniel Sandler6ed3fca2012-11-08 09:58:27 -0500166 launchActivityWithAnimation(intent, showsWhileLocked, animation, worker, onStarted);
167 }
168
169 public void launchActivityWithAnimation(final Intent intent,
170 boolean showsWhileLocked,
171 final Bundle animation,
172 final Handler worker,
173 final Runnable onStarted) {
174
John Spurlockdcc96812012-10-25 14:35:19 -0400175 LockPatternUtils lockPatternUtils = getLockPatternUtils();
176 intent.addFlags(
177 Intent.FLAG_ACTIVITY_NEW_TASK
178 | Intent.FLAG_ACTIVITY_SINGLE_TOP
179 | Intent.FLAG_ACTIVITY_CLEAR_TOP);
180 boolean isSecure = lockPatternUtils.isSecure();
181 if (!isSecure || showsWhileLocked) {
John Spurlock34c4fe52012-11-07 10:12:29 -0500182 if (!isSecure) {
183 dismissKeyguardOnNextActivity();
John Spurlockdcc96812012-10-25 14:35:19 -0400184 }
185 try {
John Spurlockdbe24b72012-11-01 13:01:05 -0400186 if (DEBUG) Log.d(TAG, String.format("Starting activity for intent %s at %s",
187 intent, SystemClock.uptimeMillis()));
John Spurlock57f928f2012-11-02 13:09:25 -0400188 startActivityForCurrentUser(intent, animation, worker, onStarted);
John Spurlockdcc96812012-10-25 14:35:19 -0400189 } catch (ActivityNotFoundException e) {
190 Log.w(TAG, "Activity not found for intent + " + intent.getAction());
191 }
192 } else {
193 // Create a runnable to start the activity and ask the user to enter their
194 // credentials.
Jim Miller7751ff62014-01-14 18:57:03 -0800195 setOnDismissAction(new OnDismissAction() {
John Spurlockdcc96812012-10-25 14:35:19 -0400196 @Override
John Spurlock34c4fe52012-11-07 10:12:29 -0500197 public boolean onDismiss() {
198 dismissKeyguardOnNextActivity();
John Spurlock57f928f2012-11-02 13:09:25 -0400199 startActivityForCurrentUser(intent, animation, worker, onStarted);
John Spurlock34c4fe52012-11-07 10:12:29 -0500200 return true;
John Spurlockdcc96812012-10-25 14:35:19 -0400201 }
202 });
Jim Miller7751ff62014-01-14 18:57:03 -0800203 requestDismissKeyguard();
John Spurlockdcc96812012-10-25 14:35:19 -0400204 }
205 }
206
John Spurlock34c4fe52012-11-07 10:12:29 -0500207 private void dismissKeyguardOnNextActivity() {
208 try {
209 ActivityManagerNative.getDefault().dismissKeyguardOnNextActivity();
210 } catch (RemoteException e) {
211 Log.w(TAG, "can't dismiss keyguard on launch");
212 }
213 }
214
John Spurlock57f928f2012-11-02 13:09:25 -0400215 private void startActivityForCurrentUser(final Intent intent, final Bundle options,
216 Handler worker, final Runnable onStarted) {
217 final UserHandle user = new UserHandle(UserHandle.USER_CURRENT);
218 if (worker == null || onStarted == null) {
219 getContext().startActivityAsUser(intent, options, user);
220 return;
221 }
222 // if worker + onStarted are supplied, run blocking activity launch call in the background
223 worker.post(new Runnable(){
224 @Override
225 public void run() {
226 try {
227 WaitResult result = ActivityManagerNative.getDefault().startActivityAndWait(
228 null /*caller*/,
Dianne Hackbornf265ea92013-01-31 15:00:51 -0800229 null /*caller pkg*/,
John Spurlock57f928f2012-11-02 13:09:25 -0400230 intent,
231 intent.resolveTypeIfNeeded(getContext().getContentResolver()),
232 null /*resultTo*/,
233 null /*resultWho*/,
234 0 /*requestCode*/,
235 Intent.FLAG_ACTIVITY_NEW_TASK,
236 null /*profileFile*/,
237 null /*profileFd*/,
238 options,
239 user.getIdentifier());
240 if (DEBUG) Log.d(TAG, String.format("waitResult[%s,%s,%s,%s] at %s",
241 result.result, result.thisTime, result.totalTime, result.who,
242 SystemClock.uptimeMillis()));
243 } catch (RemoteException e) {
244 Log.w(TAG, "Error starting activity", e);
245 return;
246 }
247 try {
248 onStarted.run();
249 } catch (Throwable t) {
250 Log.w(TAG, "Error running onStarted callback", t);
251 }
252 }});
253 }
254
John Spurlockdcc96812012-10-25 14:35:19 -0400255 private Intent getCameraIntent() {
256 return getLockPatternUtils().isSecure() ? SECURE_CAMERA_INTENT : INSECURE_CAMERA_INTENT;
257 }
258
259 private boolean wouldLaunchResolverActivity(Intent intent) {
260 PackageManager packageManager = getContext().getPackageManager();
261 ResolveInfo resolved = packageManager.resolveActivityAsUser(intent,
262 PackageManager.MATCH_DEFAULT_ONLY, getLockPatternUtils().getCurrentUser());
263 List<ResolveInfo> appList = packageManager.queryIntentActivitiesAsUser(
264 intent, PackageManager.MATCH_DEFAULT_ONLY, getLockPatternUtils().getCurrentUser());
265 return wouldLaunchResolverActivity(resolved, appList);
266 }
267
268 private boolean wouldLaunchResolverActivity(ResolveInfo resolved, List<ResolveInfo> appList) {
269 // If the list contains the above resolved activity, then it can't be
270 // ResolverActivity itself.
271 for (int i = 0; i < appList.size(); i++) {
272 ResolveInfo tmp = appList.get(i);
273 if (tmp.activityInfo.name.equals(resolved.activityInfo.name)
274 && tmp.activityInfo.packageName.equals(resolved.activityInfo.packageName)) {
275 return false;
276 }
277 }
278 return true;
279 }
280}