blob: 9a1aa5b79564986aeb357fcf07b95d1a9a08f280 [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
56 abstract KeyguardSecurityCallback getCallback();
57
58 abstract LockPatternUtils getLockPatternUtils();
59
60 public static class CameraWidgetInfo {
61 public String contextPackage;
62 public int layoutId;
63 }
64
65 public CameraWidgetInfo getCameraWidgetInfo() {
66 CameraWidgetInfo info = new CameraWidgetInfo();
67 Intent intent = getCameraIntent();
68 PackageManager packageManager = getContext().getPackageManager();
69 final List<ResolveInfo> appList = packageManager.queryIntentActivitiesAsUser(
70 intent, PackageManager.MATCH_DEFAULT_ONLY, getLockPatternUtils().getCurrentUser());
71 if (appList.size() == 0) {
72 if (DEBUG) Log.d(TAG, "getCameraWidgetInfo(): Nothing found");
73 return null;
74 }
75 ResolveInfo resolved = packageManager.resolveActivityAsUser(intent,
76 PackageManager.MATCH_DEFAULT_ONLY | PackageManager.GET_META_DATA,
77 getLockPatternUtils().getCurrentUser());
78 if (DEBUG) Log.d(TAG, "getCameraWidgetInfo(): resolved: " + resolved);
79 if (wouldLaunchResolverActivity(resolved, appList)) {
80 if (DEBUG) Log.d(TAG, "getCameraWidgetInfo(): Would launch resolver");
81 return info;
82 }
83 if (resolved == null || resolved.activityInfo == null) {
84 return null;
85 }
86 if (resolved.activityInfo.metaData == null || resolved.activityInfo.metaData.isEmpty()) {
87 if (DEBUG) Log.d(TAG, "getCameraWidgetInfo(): no metadata found");
88 return info;
89 }
90 int layoutId = resolved.activityInfo.metaData.getInt(META_DATA_KEYGUARD_LAYOUT);
91 if (layoutId == 0) {
92 if (DEBUG) Log.d(TAG, "getCameraWidgetInfo(): no layout specified");
93 return info;
94 }
95 info.contextPackage = resolved.activityInfo.packageName;
96 info.layoutId = layoutId;
97 return info;
98 }
99
John Spurlock57f928f2012-11-02 13:09:25 -0400100 public void launchCamera(Handler worker, Runnable onSecureCameraStarted) {
John Spurlockdcc96812012-10-25 14:35:19 -0400101 LockPatternUtils lockPatternUtils = getLockPatternUtils();
102 if (lockPatternUtils.isSecure()) {
103 // Launch the secure version of the camera
104 if (wouldLaunchResolverActivity(SECURE_CAMERA_INTENT)) {
105 // TODO: Show disambiguation dialog instead.
106 // For now, we'll treat this like launching any other app from secure keyguard.
107 // When they do, user sees the system's ResolverActivity which lets them choose
108 // which secure camera to use.
John Spurlock57f928f2012-11-02 13:09:25 -0400109 launchActivity(SECURE_CAMERA_INTENT, false, false, null, null);
John Spurlockdcc96812012-10-25 14:35:19 -0400110 } else {
John Spurlock57f928f2012-11-02 13:09:25 -0400111 launchActivity(SECURE_CAMERA_INTENT, true, false, worker, onSecureCameraStarted);
John Spurlockdcc96812012-10-25 14:35:19 -0400112 }
113 } else {
John Spurlockdcc96812012-10-25 14:35:19 -0400114 // Launch the normal camera
John Spurlock57f928f2012-11-02 13:09:25 -0400115 launchActivity(INSECURE_CAMERA_INTENT, false, false, null, null);
John Spurlockdcc96812012-10-25 14:35:19 -0400116 }
117 }
118
John Spurlock65cbcbe2012-11-06 11:39:34 -0500119 public void launchWidgetPicker(int appWidgetId) {
120 Intent pickIntent = new Intent(AppWidgetManager.ACTION_KEYGUARD_APPWIDGET_PICK);
121
122 pickIntent.putExtra(AppWidgetManager.EXTRA_APPWIDGET_ID, appWidgetId);
123 pickIntent.putExtra(AppWidgetManager.EXTRA_CUSTOM_SORT, false);
124 pickIntent.putExtra(AppWidgetManager.EXTRA_CATEGORY_FILTER,
125 AppWidgetProviderInfo.WIDGET_CATEGORY_KEYGUARD);
126
127 Bundle options = new Bundle();
128 options.putInt(AppWidgetManager.OPTION_APPWIDGET_HOST_CATEGORY,
129 AppWidgetProviderInfo.WIDGET_CATEGORY_KEYGUARD);
130 pickIntent.putExtra(AppWidgetManager.EXTRA_APPWIDGET_OPTIONS, options);
131 pickIntent.addFlags(
132 Intent.FLAG_ACTIVITY_NEW_TASK
133 | Intent.FLAG_ACTIVITY_SINGLE_TOP
134 | Intent.FLAG_ACTIVITY_CLEAR_TOP
135 | Intent.FLAG_ACTIVITY_EXCLUDE_FROM_RECENTS);
136
137 launchActivity(pickIntent, false, false, null, null);
138 }
139
John Spurlockdcc96812012-10-25 14:35:19 -0400140 /**
141 * Launches the said intent for the current foreground user.
John Spurlock57f928f2012-11-02 13:09:25 -0400142 *
John Spurlockdcc96812012-10-25 14:35:19 -0400143 * @param intent
144 * @param showsWhileLocked true if the activity can be run on top of keyguard.
John Spurlock57f928f2012-11-02 13:09:25 -0400145 * See {@link WindowManager#FLAG_SHOW_WHEN_LOCKED}
146 * @param useDefaultAnimations true if default transitions should be used, else suppressed.
147 * @param worker if supplied along with onStarted, used to launch the blocking activity call.
148 * @param onStarted if supplied along with worker, called after activity is started.
John Spurlockdcc96812012-10-25 14:35:19 -0400149 */
John Spurlock57f928f2012-11-02 13:09:25 -0400150 public void launchActivity(final Intent intent,
151 boolean showsWhileLocked,
152 boolean useDefaultAnimations,
153 final Handler worker,
154 final Runnable onStarted) {
Daniel Sandler6ed3fca2012-11-08 09:58:27 -0500155
John Spurlockdcc96812012-10-25 14:35:19 -0400156 final Context context = getContext();
John Spurlock65cbcbe2012-11-06 11:39:34 -0500157 final Bundle animation = useDefaultAnimations ? null
158 : ActivityOptions.makeCustomAnimation(context, 0, 0).toBundle();
Daniel Sandler6ed3fca2012-11-08 09:58:27 -0500159 launchActivityWithAnimation(intent, showsWhileLocked, animation, worker, onStarted);
160 }
161
162 public void launchActivityWithAnimation(final Intent intent,
163 boolean showsWhileLocked,
164 final Bundle animation,
165 final Handler worker,
166 final Runnable onStarted) {
167
John Spurlockdcc96812012-10-25 14:35:19 -0400168 LockPatternUtils lockPatternUtils = getLockPatternUtils();
169 intent.addFlags(
170 Intent.FLAG_ACTIVITY_NEW_TASK
171 | Intent.FLAG_ACTIVITY_SINGLE_TOP
172 | Intent.FLAG_ACTIVITY_CLEAR_TOP);
173 boolean isSecure = lockPatternUtils.isSecure();
174 if (!isSecure || showsWhileLocked) {
John Spurlock34c4fe52012-11-07 10:12:29 -0500175 if (!isSecure) {
176 dismissKeyguardOnNextActivity();
John Spurlockdcc96812012-10-25 14:35:19 -0400177 }
178 try {
John Spurlockdbe24b72012-11-01 13:01:05 -0400179 if (DEBUG) Log.d(TAG, String.format("Starting activity for intent %s at %s",
180 intent, SystemClock.uptimeMillis()));
John Spurlock57f928f2012-11-02 13:09:25 -0400181 startActivityForCurrentUser(intent, animation, worker, onStarted);
John Spurlockdcc96812012-10-25 14:35:19 -0400182 } catch (ActivityNotFoundException e) {
183 Log.w(TAG, "Activity not found for intent + " + intent.getAction());
184 }
185 } else {
186 // Create a runnable to start the activity and ask the user to enter their
187 // credentials.
188 KeyguardSecurityCallback callback = getCallback();
John Spurlock34c4fe52012-11-07 10:12:29 -0500189 callback.setOnDismissAction(new OnDismissAction() {
John Spurlockdcc96812012-10-25 14:35:19 -0400190 @Override
John Spurlock34c4fe52012-11-07 10:12:29 -0500191 public boolean onDismiss() {
192 dismissKeyguardOnNextActivity();
John Spurlock57f928f2012-11-02 13:09:25 -0400193 startActivityForCurrentUser(intent, animation, worker, onStarted);
John Spurlock34c4fe52012-11-07 10:12:29 -0500194 return true;
John Spurlockdcc96812012-10-25 14:35:19 -0400195 }
196 });
197 callback.dismiss(false);
198 }
199 }
200
John Spurlock34c4fe52012-11-07 10:12:29 -0500201 private void dismissKeyguardOnNextActivity() {
202 try {
203 ActivityManagerNative.getDefault().dismissKeyguardOnNextActivity();
204 } catch (RemoteException e) {
205 Log.w(TAG, "can't dismiss keyguard on launch");
206 }
207 }
208
John Spurlock57f928f2012-11-02 13:09:25 -0400209 private void startActivityForCurrentUser(final Intent intent, final Bundle options,
210 Handler worker, final Runnable onStarted) {
211 final UserHandle user = new UserHandle(UserHandle.USER_CURRENT);
212 if (worker == null || onStarted == null) {
213 getContext().startActivityAsUser(intent, options, user);
214 return;
215 }
216 // if worker + onStarted are supplied, run blocking activity launch call in the background
217 worker.post(new Runnable(){
218 @Override
219 public void run() {
220 try {
221 WaitResult result = ActivityManagerNative.getDefault().startActivityAndWait(
222 null /*caller*/,
Dianne Hackbornf265ea92013-01-31 15:00:51 -0800223 null /*caller pkg*/,
John Spurlock57f928f2012-11-02 13:09:25 -0400224 intent,
225 intent.resolveTypeIfNeeded(getContext().getContentResolver()),
226 null /*resultTo*/,
227 null /*resultWho*/,
228 0 /*requestCode*/,
229 Intent.FLAG_ACTIVITY_NEW_TASK,
230 null /*profileFile*/,
231 null /*profileFd*/,
232 options,
233 user.getIdentifier());
234 if (DEBUG) Log.d(TAG, String.format("waitResult[%s,%s,%s,%s] at %s",
235 result.result, result.thisTime, result.totalTime, result.who,
236 SystemClock.uptimeMillis()));
237 } catch (RemoteException e) {
238 Log.w(TAG, "Error starting activity", e);
239 return;
240 }
241 try {
242 onStarted.run();
243 } catch (Throwable t) {
244 Log.w(TAG, "Error running onStarted callback", t);
245 }
246 }});
247 }
248
John Spurlockdcc96812012-10-25 14:35:19 -0400249 private Intent getCameraIntent() {
250 return getLockPatternUtils().isSecure() ? SECURE_CAMERA_INTENT : INSECURE_CAMERA_INTENT;
251 }
252
253 private boolean wouldLaunchResolverActivity(Intent intent) {
254 PackageManager packageManager = getContext().getPackageManager();
255 ResolveInfo resolved = packageManager.resolveActivityAsUser(intent,
256 PackageManager.MATCH_DEFAULT_ONLY, getLockPatternUtils().getCurrentUser());
257 List<ResolveInfo> appList = packageManager.queryIntentActivitiesAsUser(
258 intent, PackageManager.MATCH_DEFAULT_ONLY, getLockPatternUtils().getCurrentUser());
259 return wouldLaunchResolverActivity(resolved, appList);
260 }
261
262 private boolean wouldLaunchResolverActivity(ResolveInfo resolved, List<ResolveInfo> appList) {
263 // If the list contains the above resolved activity, then it can't be
264 // ResolverActivity itself.
265 for (int i = 0; i < appList.size(); i++) {
266 ResolveInfo tmp = appList.get(i);
267 if (tmp.activityInfo.name.equals(resolved.activityInfo.name)
268 && tmp.activityInfo.packageName.equals(resolved.activityInfo.packageName)) {
269 return false;
270 }
271 }
272 return true;
273 }
274}