Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit a86790bf authored by Jorim Jaggi's avatar Jorim Jaggi
Browse files

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
parent 6e9fa1a4
Loading
Loading
Loading
Loading
+1 −0
Original line number Diff line number Diff line
@@ -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";
+1 −0
Original line number Diff line number Diff line
@@ -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";
+29 −0
Original line number Diff line number Diff line
@@ -225,6 +225,35 @@ public final class MediaStore {
    @SdkConstant(SdkConstantType.ACTIVITY_INTENT_ACTION)
    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,
+19 −13
Original line number Diff line number Diff line
@@ -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();

+38 −2
Original line number Diff line number Diff line
@@ -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) {
            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
Loading