diff options
author | Jorim Jaggi <jjaggi@google.com> | 2015-04-02 16:32:29 -0700 |
---|---|---|
committer | Jorim Jaggi <jjaggi@google.com> | 2015-04-07 11:24:05 -0700 |
commit | a86790bf23a98ede5fc0c29b996a5229e08181cd (patch) | |
tree | 356a816c89f4dd08c1437221eb57aa168fe0a015 | |
parent | 6e9fa1a4b74dc4e35470739cfd64881cb9bcc7b1 (diff) | |
download | frameworks_base-a86790bf23a98ede5fc0c29b996a5229e08181cd.zip frameworks_base-a86790bf23a98ede5fc0c29b996a5229e08181cd.tar.gz frameworks_base-a86790bf23a98ede5fc0c29b996a5229e08181cd.tar.bz2 |
Add Camera prewarm intent.
Also adds a test app for testing this intent. In addition, the secure
camera gets launched in the background to fix jank while sending the
intent.
Bug: 20016619
Change-Id: I7bb7e22ddaf5dc67fc09b9e63e5f3d10fe8e3ee4
14 files changed, 332 insertions, 19 deletions
diff --git a/api/current.txt b/api/current.txt index c0176c9..977478e 100644 --- a/api/current.txt +++ b/api/current.txt @@ -25738,6 +25738,7 @@ package android.provider { method public static java.lang.String getVersion(android.content.Context); field public static final java.lang.String ACTION_IMAGE_CAPTURE = "android.media.action.IMAGE_CAPTURE"; field public static final java.lang.String ACTION_IMAGE_CAPTURE_SECURE = "android.media.action.IMAGE_CAPTURE_SECURE"; + field public static final java.lang.String ACTION_STILL_IMAGE_CAMERA_PREWARM = "android.media.action.STILL_IMAGE_CAMERA_PREWARM"; field public static final java.lang.String ACTION_VIDEO_CAPTURE = "android.media.action.VIDEO_CAPTURE"; field public static final java.lang.String AUTHORITY = "media"; field public static final java.lang.String EXTRA_DURATION_LIMIT = "android.intent.extra.durationLimit"; diff --git a/api/system-current.txt b/api/system-current.txt index 4a2e402..a4e3793 100644 --- a/api/system-current.txt +++ b/api/system-current.txt @@ -27617,6 +27617,7 @@ package android.provider { method public static java.lang.String getVersion(android.content.Context); field public static final java.lang.String ACTION_IMAGE_CAPTURE = "android.media.action.IMAGE_CAPTURE"; field public static final java.lang.String ACTION_IMAGE_CAPTURE_SECURE = "android.media.action.IMAGE_CAPTURE_SECURE"; + field public static final java.lang.String ACTION_STILL_IMAGE_CAMERA_PREWARM = "android.media.action.STILL_IMAGE_CAMERA_PREWARM"; field public static final java.lang.String ACTION_VIDEO_CAPTURE = "android.media.action.VIDEO_CAPTURE"; field public static final java.lang.String AUTHORITY = "media"; field public static final java.lang.String EXTRA_DURATION_LIMIT = "android.intent.extra.durationLimit"; diff --git a/core/java/android/provider/MediaStore.java b/core/java/android/provider/MediaStore.java index 5afbd6d..7565654b 100644 --- a/core/java/android/provider/MediaStore.java +++ b/core/java/android/provider/MediaStore.java @@ -226,6 +226,35 @@ public final class MediaStore { public static final String INTENT_ACTION_STILL_IMAGE_CAMERA = "android.media.action.STILL_IMAGE_CAMERA"; /** + * The name of the Intent action used to indicate that a camera launch might be imminent. This + * broadcast should be targeted to the package that is receiving + * {@link #INTENT_ACTION_STILL_IMAGE_CAMERA} or + * {@link #INTENT_ACTION_STILL_IMAGE_CAMERA_SECURE}, depending on the context. If such + * intent would launch the resolver activity, this broadcast should not be sent at all. + * <p> + * A receiver of this broadcast should do the absolute minimum amount of work to initialize the + * camera in order to reduce startup time in likely case that shortly after an actual camera + * launch intent would be sent. + * <p> + * In case the actual intent will not be fired, the target package will receive + * {@link #ACTION_STILL_IMAGE_CAMERA_COOLDOWN}. However, it is recommended that the receiver + * also implements a timeout to close the camera after receiving this intent, as there is no + * guarantee that {@link #ACTION_STILL_IMAGE_CAMERA_COOLDOWN} will be delivered. + */ + @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION) + public static final String ACTION_STILL_IMAGE_CAMERA_PREWARM = "android.media.action.STILL_IMAGE_CAMERA_PREWARM"; + + /** + * The name of the Intent action used to indicate that an imminent camera launch has been + * cancelled by the user. This broadcast should be targeted to the package that has received + * {@link #ACTION_STILL_IMAGE_CAMERA_PREWARM}. + * <p> + * A receiver of this broadcast should close the camera immediately. + */ + @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION) + public static final String ACTION_STILL_IMAGE_CAMERA_COOLDOWN = "android.media.action.STILL_IMAGE_CAMERA_COOLDOWN"; + + /** * The name of the Intent action used to launch a camera in still image mode * for use when the device is secured (e.g. with a pin, password, pattern, * or face unlock). Applications responding to this intent must not expose diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardAffordanceHelper.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardAffordanceHelper.java index 3b8fccc..cab152f 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardAffordanceHelper.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardAffordanceHelper.java @@ -69,7 +69,7 @@ public class KeyguardAffordanceHelper { @Override public void onAnimationEnd(Animator animation) { mSwipeAnimator = null; - setSwipingInProgress(false); + mSwipingInProgress = false; } }; private Runnable mAnimationEndRunnable = new Runnable() { @@ -117,14 +117,17 @@ public class KeyguardAffordanceHelper { } public boolean onTouchEvent(MotionEvent event) { - if (mMotionCancelled && event.getActionMasked() != MotionEvent.ACTION_DOWN) { + int action = event.getActionMasked(); + if (mMotionCancelled && action != MotionEvent.ACTION_DOWN + && action != MotionEvent.ACTION_UP + && action != MotionEvent.ACTION_CANCEL) { return false; } final float y = event.getY(); final float x = event.getX(); boolean isUp = false; - switch (event.getActionMasked()) { + switch (action) { case MotionEvent.ACTION_DOWN: if (mSwipingInProgress) { cancelAnimation(); @@ -152,7 +155,8 @@ public class KeyguardAffordanceHelper { mInitialTouchY = y; mInitialTouchX = x; mTranslationOnDown = mTranslation; - setSwipingInProgress(true); + mSwipingInProgress = true; + mCallback.onSwipingStarted(w < -mTouchSlop); } if (mSwipingInProgress) { setTranslation(mTranslationOnDown + x - mInitialTouchX, false, false); @@ -179,13 +183,6 @@ public class KeyguardAffordanceHelper { } } - private void setSwipingInProgress(boolean inProgress) { - mSwipingInProgress = inProgress; - if (inProgress) { - mCallback.onSwipingStarted(); - } - } - private boolean rightSwipePossible() { return mRightIcon.getVisibility() == View.VISIBLE; } @@ -323,6 +320,9 @@ public class KeyguardAffordanceHelper { } animator.start(); mSwipeAnimator = animator; + if (snapBack) { + mCallback.onSwipingAborted(); + } } private void startFinishingCircleAnimation(float velocity, Runnable mAnimationEndRunnable) { @@ -451,7 +451,11 @@ public class KeyguardAffordanceHelper { mSwipeAnimator.cancel(); } setTranslation(0.0f, true, animate); - setSwipingInProgress(false); + mMotionCancelled = true; + if (mSwipingInProgress) { + mCallback.onSwipingAborted(); + } + mSwipingInProgress = false; } public interface Callback { @@ -470,7 +474,9 @@ public class KeyguardAffordanceHelper { float getPageWidth(); - void onSwipingStarted(); + void onSwipingStarted(boolean isRightwardMotion); + + void onSwipingAborted(); KeyguardAffordanceView getLeftIcon(); diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardBottomAreaView.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardBottomAreaView.java index a247c8e..628ae84 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardBottomAreaView.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardBottomAreaView.java @@ -100,6 +100,7 @@ public class KeyguardBottomAreaView extends FrameLayout implements View.OnClickL private final TrustDrawable mTrustDrawable; private final Interpolator mLinearOutSlowInInterpolator; private int mLastUnlockIconRes = 0; + private boolean mPrewarmSent; public KeyguardBottomAreaView(Context context) { this(context, null); @@ -335,12 +336,47 @@ public class KeyguardBottomAreaView extends FrameLayout implements View.OnClickL mLockPatternUtils.requireCredentialEntry(mLockPatternUtils.getCurrentUser()); } - public void launchCamera() { + public void prewarmCamera() { + Intent intent = getCameraIntent(); + String targetPackage = PreviewInflater.getTargetPackage(mContext, intent, + mLockPatternUtils.getCurrentUser()); + if (targetPackage != null) { + Intent prewarm = new Intent(MediaStore.ACTION_STILL_IMAGE_CAMERA_PREWARM); + prewarm.setPackage(targetPackage); + mPrewarmSent = true; + mContext.sendBroadcast(prewarm); + } + } + + public void maybeCooldownCamera() { + if (!mPrewarmSent) { + return; + } + mPrewarmSent = false; Intent intent = getCameraIntent(); + String targetPackage = PreviewInflater.getTargetPackage(mContext, intent, + mLockPatternUtils.getCurrentUser()); + if (targetPackage != null) { + Intent prewarm = new Intent(MediaStore.ACTION_STILL_IMAGE_CAMERA_COOLDOWN); + prewarm.setPackage(targetPackage); + mContext.sendBroadcast(prewarm); + } + } + + public void launchCamera() { + + // Reset prewarm state. + mPrewarmSent = false; + final Intent intent = getCameraIntent(); boolean wouldLaunchResolverActivity = PreviewInflater.wouldLaunchResolverActivity( mContext, intent, mLockPatternUtils.getCurrentUser()); if (intent == SECURE_CAMERA_INTENT && !wouldLaunchResolverActivity) { - mContext.startActivityAsUser(intent, UserHandle.CURRENT); + AsyncTask.execute(new Runnable() { + @Override + public void run() { + mContext.startActivityAsUser(intent, UserHandle.CURRENT); + } + }); } else { // We need to delay starting the activity because ResolverActivity finishes itself if diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelView.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelView.java index 195da46..216730b 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelView.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelView.java @@ -1809,14 +1809,24 @@ public class NotificationPanelView extends PanelView implements } @Override - public void onSwipingStarted() { - mSecureCameraLaunchManager.onSwipingStarted(); + public void onSwipingStarted(boolean isRightwardMotion) { + boolean start = getLayoutDirection() == LAYOUT_DIRECTION_RTL ? isRightwardMotion + : !isRightwardMotion; + if (!start) { + mSecureCameraLaunchManager.onSwipingStarted(); + mKeyguardBottomArea.prewarmCamera(); + } requestDisallowInterceptTouchEvent(true); mOnlyAffordanceInThisMotion = true; mQsTracking = false; } @Override + public void onSwipingAborted() { + mKeyguardBottomArea.maybeCooldownCamera(); + } + + @Override public KeyguardAffordanceView getLeftIcon() { return getLayoutDirection() == LAYOUT_DIRECTION_RTL ? mKeyguardBottomArea.getCameraView() diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/PreviewInflater.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/PreviewInflater.java index 34068fd..0dce82f 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/PreviewInflater.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/PreviewInflater.java @@ -16,6 +16,7 @@ package com.android.systemui.statusbar.policy; +import android.content.ComponentName; import android.content.Context; import android.content.Intent; import android.content.pm.PackageManager; @@ -106,15 +107,28 @@ public class PreviewInflater { public static boolean wouldLaunchResolverActivity(Context ctx, Intent intent, int currentUserId) { + return getTargetPackage(ctx, intent, currentUserId) == null; + } + + /** + * @return the target package of the intent it resolves to a specific package or {@code null} if + * it resolved to the resolver activity + */ + public static String getTargetPackage(Context ctx, Intent intent, + int currentUserId) { PackageManager packageManager = ctx.getPackageManager(); final List<ResolveInfo> appList = packageManager.queryIntentActivitiesAsUser( intent, PackageManager.MATCH_DEFAULT_ONLY, currentUserId); if (appList.size() == 0) { - return false; + return null; } ResolveInfo resolved = packageManager.resolveActivityAsUser(intent, PackageManager.MATCH_DEFAULT_ONLY | PackageManager.GET_META_DATA, currentUserId); - return wouldLaunchResolverActivity(resolved, appList); + if (resolved == null || wouldLaunchResolverActivity(resolved, appList)) { + return null; + } else { + return resolved.activityInfo.packageName; + } } private static boolean wouldLaunchResolverActivity( diff --git a/tests/CameraPrewarmTest/Android.mk b/tests/CameraPrewarmTest/Android.mk new file mode 100644 index 0000000..b6316f0 --- /dev/null +++ b/tests/CameraPrewarmTest/Android.mk @@ -0,0 +1,11 @@ +LOCAL_PATH:= $(call my-dir) +include $(CLEAR_VARS) + +LOCAL_SRC_FILES := $(call all-subdir-java-files) + +LOCAL_PACKAGE_NAME := CameraPrewarmTest + +LOCAL_MODULE_TAGS := tests +LOCAL_CERTIFICATE := platform + +include $(BUILD_PACKAGE) diff --git a/tests/CameraPrewarmTest/AndroidManifest.xml b/tests/CameraPrewarmTest/AndroidManifest.xml new file mode 100644 index 0000000..eb40200 --- /dev/null +++ b/tests/CameraPrewarmTest/AndroidManifest.xml @@ -0,0 +1,52 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- + ~ Copyright (C) 2015 The Android Open Source Project + ~ + ~ Licensed under the Apache License, Version 2.0 (the "License"); + ~ you may not use this file except in compliance with the License. + ~ You may obtain a copy of the License at + ~ + ~ http://www.apache.org/licenses/LICENSE-2.0 + ~ + ~ Unless required by applicable law or agreed to in writing, software + ~ distributed under the License is distributed on an "AS IS" BASIS, + ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + ~ See the License for the specific language governing permissions and + ~ limitations under the License + --> + +<manifest xmlns:android="http://schemas.android.com/apk/res/android" + package="com.google.android.test.cameraprewarm"> + <application android:label="@string/activity_title"> + + <activity android:name=".CameraActivity" + android:theme="@android:style/Theme.NoTitleBar"> + <intent-filter> + <action android:name="android.media.action.STILL_IMAGE_CAMERA_SECURE" /> + <category android:name="android.intent.category.DEFAULT" /> + </intent-filter> + <intent-filter> + <action android:name="android.media.action.STILL_IMAGE_CAMERA" /> + <category android:name="android.intent.category.DEFAULT" /> + </intent-filter> + </activity> + + <activity android:name=".SecureCameraActivity" + android:theme="@android:style/Theme.NoTitleBar"> + <intent-filter> + <action android:name="android.media.action.STILL_IMAGE_CAMERA_SECURE" /> + <category android:name="android.intent.category.DEFAULT" /> + </intent-filter> + </activity> + + <receiver android:name=".PrewarmReceiver" > + <intent-filter> + <action android:name="android.media.action.STILL_IMAGE_CAMERA_PREWARM" /> + </intent-filter> + <intent-filter> + <action android:name="android.media.action.STILL_IMAGE_CAMERA_COOLDOWN" /> + </intent-filter> + </receiver> + + </application> +</manifest> diff --git a/tests/CameraPrewarmTest/res/layout/camera_activity.xml b/tests/CameraPrewarmTest/res/layout/camera_activity.xml new file mode 100644 index 0000000..64437bc --- /dev/null +++ b/tests/CameraPrewarmTest/res/layout/camera_activity.xml @@ -0,0 +1,25 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- + ~ Copyright (C) 2015 The Android Open Source Project + ~ + ~ Licensed under the Apache License, Version 2.0 (the "License"); + ~ you may not use this file except in compliance with the License. + ~ You may obtain a copy of the License at + ~ + ~ http://www.apache.org/licenses/LICENSE-2.0 + ~ + ~ Unless required by applicable law or agreed to in writing, software + ~ distributed under the License is distributed on an "AS IS" BASIS, + ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + ~ See the License for the specific language governing permissions and + ~ limitations under the License + --> + +<LinearLayout + xmlns:android="http://schemas.android.com/apk/res/android" + android:orientation="vertical" + android:layout_width="match_parent" + android:layout_height="match_parent" + android:gravity="center"> +</LinearLayout> + diff --git a/tests/CameraPrewarmTest/res/values/strings.xml b/tests/CameraPrewarmTest/res/values/strings.xml new file mode 100644 index 0000000..11f7ac7 --- /dev/null +++ b/tests/CameraPrewarmTest/res/values/strings.xml @@ -0,0 +1,21 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- + ~ Copyright (C) 2015 The Android Open Source Project + ~ + ~ Licensed under the Apache License, Version 2.0 (the "License"); + ~ you may not use this file except in compliance with the License. + ~ You may obtain a copy of the License at + ~ + ~ http://www.apache.org/licenses/LICENSE-2.0 + ~ + ~ Unless required by applicable law or agreed to in writing, software + ~ distributed under the License is distributed on an "AS IS" BASIS, + ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + ~ See the License for the specific language governing permissions and + ~ limitations under the License + --> + +<resources> + <string name="activity_title">Assistant</string> + <string name="search_label">Orilla Search Engine</string> +</resources> diff --git a/tests/CameraPrewarmTest/src/com/google/android/test/cameraprewarm/CameraActivity.java b/tests/CameraPrewarmTest/src/com/google/android/test/cameraprewarm/CameraActivity.java new file mode 100644 index 0000000..4d22234 --- /dev/null +++ b/tests/CameraPrewarmTest/src/com/google/android/test/cameraprewarm/CameraActivity.java @@ -0,0 +1,37 @@ +/* + * Copyright (C) 2015 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License + */ + +package com.google.android.test.cameraprewarm; + +import android.app.Activity; +import android.os.Bundle; +import android.util.Log; +import android.view.WindowManager; + +import com.google.android.test.cameraprewarm.R; + +public class CameraActivity extends Activity { + + public final static String TAG = "PrewarmTest"; + + @Override + protected void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + setContentView(R.layout.camera_activity); + getWindow().addFlags(WindowManager.LayoutParams.FLAG_DISMISS_KEYGUARD); + Log.i(TAG, "Activity created"); + } +} diff --git a/tests/CameraPrewarmTest/src/com/google/android/test/cameraprewarm/PrewarmReceiver.java b/tests/CameraPrewarmTest/src/com/google/android/test/cameraprewarm/PrewarmReceiver.java new file mode 100644 index 0000000..d49f96d --- /dev/null +++ b/tests/CameraPrewarmTest/src/com/google/android/test/cameraprewarm/PrewarmReceiver.java @@ -0,0 +1,35 @@ +/* + * Copyright (C) 2015 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License + */ + +package com.google.android.test.cameraprewarm; + +import android.content.BroadcastReceiver; +import android.content.Context; +import android.content.Intent; +import android.provider.MediaStore; +import android.util.Log; + +public class PrewarmReceiver extends BroadcastReceiver { + + @Override + public void onReceive(Context context, Intent intent) { + if (intent.getAction().equals(MediaStore.ACTION_STILL_IMAGE_CAMERA_PREWARM)) { + Log.i(CameraActivity.TAG, "Prewarm received"); + } else if (intent.getAction().equals(MediaStore.ACTION_STILL_IMAGE_CAMERA_COOLDOWN)){ + Log.i(CameraActivity.TAG, "Cooldown received"); + } + } +} diff --git a/tests/CameraPrewarmTest/src/com/google/android/test/cameraprewarm/SecureCameraActivity.java b/tests/CameraPrewarmTest/src/com/google/android/test/cameraprewarm/SecureCameraActivity.java new file mode 100644 index 0000000..530fe00 --- /dev/null +++ b/tests/CameraPrewarmTest/src/com/google/android/test/cameraprewarm/SecureCameraActivity.java @@ -0,0 +1,35 @@ +/* + * Copyright (C) 2015 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License + */ + +package com.google.android.test.cameraprewarm; + +import android.app.Activity; +import android.os.Bundle; +import android.util.Log; +import android.view.WindowManager; + +import com.google.android.test.cameraprewarm.R; + +public class SecureCameraActivity extends Activity { + + @Override + protected void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + setContentView(R.layout.camera_activity); + getWindow().addFlags(WindowManager.LayoutParams.FLAG_SHOW_WHEN_LOCKED); + Log.i(CameraActivity.TAG, "Activity created"); + } +} |