blob: c931dfbe2f4ba758a235156d7d48b12040c81c06 [file] [log] [blame]
/*
* Copyright (C) 2014 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.android.camera.widget;
import android.animation.Animator;
import android.animation.ObjectAnimator;
import android.animation.ValueAnimator;
import android.content.Context;
import android.graphics.Canvas;
import android.graphics.drawable.Drawable;
import android.util.AttributeSet;
import android.view.OrientationEventListener;
import android.view.View;
import com.android.camera.util.CameraUtil;
import com.android.camera2.R;
/**
* This class is designed to show the video recording hint when device is held in
* portrait before video recording. The rotation device indicator will start rotating
* after a time-out and will fade out if the device is rotated to landscape. A tap
* on screen will dismiss the indicator.
*/
public class VideoRecordingHints extends View {
private static final int PORTRAIT_ROTATE_DELAY_MS = 1000;
private static final int ROTATION_DURATION_MS = 1000;
private static final int FADE_OUT_DURATION_MS = 600;
private static final float ROTATION_DEGREES = 180f;
private static final float INITIAL_ROTATION = 0f;
private static final int UNSET = -1;
private final int mRotateArrowsHalfSize;
private final int mPhoneGraphicHalfWidth;
private final Drawable mRotateArrows;
private final Drawable mPhoneGraphic;
private final int mPhoneGraphicHalfHeight;
private final boolean mIsDefaultToPortrait;
private float mRotation = INITIAL_ROTATION;
private final ValueAnimator mRotationAnimation;
private final ObjectAnimator mAlphaAnimator;
private boolean mIsInLandscape = false;
private int mCenterX = UNSET;
private int mCenterY = UNSET;
private int mLastOrientation = OrientationEventListener.ORIENTATION_UNKNOWN;
public VideoRecordingHints(Context context, AttributeSet attrs) {
super(context, attrs);
mRotateArrows = getResources().getDrawable(R.drawable.rotate_arrows);
mPhoneGraphic = getResources().getDrawable(R.drawable.ic_phone_graphic);
mRotateArrowsHalfSize = getResources().getDimensionPixelSize(
R.dimen.video_hint_arrow_size) / 2;
mPhoneGraphicHalfWidth = getResources()
.getDimensionPixelSize(R.dimen.video_hint_phone_graphic_width) / 2;
mPhoneGraphicHalfHeight = getResources()
.getDimensionPixelSize(R.dimen.video_hint_phone_graphic_height) / 2;
mRotationAnimation = ValueAnimator.ofFloat(mRotation, mRotation + ROTATION_DEGREES);
mRotationAnimation.setDuration(ROTATION_DURATION_MS);
mRotationAnimation.setStartDelay(PORTRAIT_ROTATE_DELAY_MS);
mRotationAnimation.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
@Override
public void onAnimationUpdate(ValueAnimator animation) {
mRotation = (Float) animation.getAnimatedValue();
invalidate();
}
});
mRotationAnimation.addListener(new Animator.AnimatorListener() {
private boolean mCanceled = false;
@Override
public void onAnimationStart(Animator animation) {
mCanceled = false;
}
@Override
public void onAnimationEnd(Animator animation) {
mRotation = ((int) mRotation) % 360;
// If animation is canceled, do not restart it.
if (mCanceled) {
return;
}
post(new Runnable() {
@Override
public void run() {
continueRotationAnimation();
}
});
}
@Override
public void onAnimationCancel(Animator animation) {
mCanceled = true;
}
@Override
public void onAnimationRepeat(Animator animation) {
// Do nothing.
}
});
mAlphaAnimator = ObjectAnimator.ofFloat(this, "alpha", 1f, 0f);
mAlphaAnimator.setDuration(FADE_OUT_DURATION_MS);
mAlphaAnimator.addListener(new Animator.AnimatorListener() {
@Override
public void onAnimationStart(Animator animation) {
// Do nothing.
}
@Override
public void onAnimationEnd(Animator animation) {
invalidate();
setAlpha(1f);
mRotation = 0;
}
@Override
public void onAnimationCancel(Animator animation) {
// Do nothing.
}
@Override
public void onAnimationRepeat(Animator animation) {
// Do nothing.
}
});
mIsDefaultToPortrait = CameraUtil.isDefaultToPortrait(context);
}
/**
* Restart the rotation animation using the current rotation as the starting
* rotation, and then rotate a pre-defined amount. If the rotation animation
* is currently running, do nothing.
*/
private void continueRotationAnimation() {
if (mRotationAnimation.isRunning()) {
return;
}
mRotationAnimation.setFloatValues(mRotation, mRotation + ROTATION_DEGREES);
mRotationAnimation.start();
}
@Override
public void onVisibilityChanged(View v, int visibility) {
super.onVisibilityChanged(v, visibility);
if (getVisibility() == VISIBLE && !isInLandscape()) {
continueRotationAnimation();
} else if (getVisibility() != VISIBLE) {
mRotationAnimation.cancel();
mRotation = 0;
}
}
@Override
public void onLayout(boolean changed, int left, int top, int right, int bottom) {
super.onLayout(changed, left, top, right, bottom);
// Center drawables in the layout
mCenterX = (right - left) / 2;
mCenterY = (bottom - top) / 2;
mRotateArrows.setBounds(mCenterX - mRotateArrowsHalfSize, mCenterY - mRotateArrowsHalfSize,
mCenterX + mRotateArrowsHalfSize, mCenterY + mRotateArrowsHalfSize);
mPhoneGraphic.setBounds(mCenterX - mPhoneGraphicHalfWidth, mCenterY - mPhoneGraphicHalfHeight,
mCenterX + mPhoneGraphicHalfWidth, mCenterY + mPhoneGraphicHalfHeight);
invalidate();
}
@Override
public void draw(Canvas canvas) {
super.draw(canvas);
// Don't draw anything after the fade-out animation in landscape.
if (mIsInLandscape && !mAlphaAnimator.isRunning()) {
return;
}
canvas.save();
canvas.rotate(-mRotation, mCenterX, mCenterY);
mRotateArrows.draw(canvas);
canvas.restore();
if (mIsInLandscape) {
canvas.save();
canvas.rotate(90, mCenterX, mCenterY);
mPhoneGraphic.draw(canvas);
canvas.restore();
} else {
mPhoneGraphic.draw(canvas);
}
}
/**
* Handles orientation change by starting/stopping the video hint based on the
* new orientation.
*/
public void onOrientationChanged(int orientation) {
if (mLastOrientation == orientation) {
return;
}
mLastOrientation = orientation;
if (mLastOrientation == OrientationEventListener.ORIENTATION_UNKNOWN) {
return;
}
mIsInLandscape = isInLandscape();
if (getVisibility() == VISIBLE) {
if (mIsInLandscape) {
// Landscape.
mRotationAnimation.cancel();
// Start fading out.
if (mAlphaAnimator.isRunning()) {
return;
}
mAlphaAnimator.start();
} else {
// Portrait.
continueRotationAnimation();
}
}
}
/**
* Returns whether the device is in landscape based on the natural orientation
* and rotation from natural orientation.
*/
private boolean isInLandscape() {
return (mLastOrientation % 180 == 90 && mIsDefaultToPortrait)
|| (mLastOrientation % 180 == 0 && !mIsDefaultToPortrait);
}
}