blob: 0ee71ab9e62451ba1445be444e9f630325b81305 [file] [log] [blame]
/*
* Copyright (C) 2010 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.videoeditor.widgets;
import com.android.videoeditor.R;
import android.content.Context;
import android.graphics.Canvas;
import android.graphics.drawable.Drawable;
import android.util.AttributeSet;
import android.view.MotionEvent;
import android.view.View;
/**
* The zoom control
*/
public class ZoomControl extends View {
private static final double MAX_ANGLE = Math.PI / 3;
private static final double THUMB_RADIUS_CONTAINER_SIZE_RATIO = 0.432;
private static final double THUMB_INTERNAL_RADIUS_CONTAINER_SIZE_RATIO = 0.24;
// Instance variables
private final Drawable mThumb;
private double mRadius;
private double mInternalRadius;
private int mMaxProgress, mProgress;
private OnZoomChangeListener mListener;
private int mThumbX, mThumbY;
private double mInterval;
/**
* The zoom change listener
*/
public interface OnZoomChangeListener {
/**
* The progress value has changed
*
* @param progress The progress value
* @param fromUser true if the user is changing the zoom
*/
public void onProgressChanged(int progress, boolean fromUser);
}
public ZoomControl(Context context, AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle);
// Set the default maximum progress
mMaxProgress = 100;
computeInterval();
// Load the thumb selector
mThumb = context.getResources().getDrawable(R.drawable.zoom_thumb_selector);
}
@Override
protected void onLayout(boolean changed, int left, int top, int right, int bottom) {
super.onLayout(changed, left, top, right, bottom);
double width = right - left;
mRadius = width * THUMB_RADIUS_CONTAINER_SIZE_RATIO;
mInternalRadius = width * THUMB_INTERNAL_RADIUS_CONTAINER_SIZE_RATIO;
}
public ZoomControl(Context context, AttributeSet attrs) {
this(context, attrs, 0);
}
public ZoomControl(Context context) {
this(context, null, 0);
}
@Override
public void refreshDrawableState() {
mThumb.setState(isPressed() ? PRESSED_WINDOW_FOCUSED_STATE_SET : ENABLED_STATE_SET);
invalidate();
}
/**
* @param max The maximum value
*/
public void setMax(int max) {
mMaxProgress = max;
computeInterval();
}
/**
* @param progress The progress
*/
public void setProgress(int progress) {
mProgress = progress;
progressToPosition();
invalidate();
}
/**
* @param listener The listener
*/
public void setOnZoomChangeListener(OnZoomChangeListener listener) {
mListener = listener;
}
@Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
if (mThumbX == 0 && mThumbY == 0) {
progressToPosition();
}
final int halfWidth = mThumb.getIntrinsicWidth() / 2;
final int halfHeight = mThumb.getIntrinsicHeight() / 2;
mThumb.setBounds(mThumbX - halfWidth, mThumbY - halfHeight, mThumbX + halfWidth,
mThumbY + halfHeight);
mThumb.setAlpha(isEnabled() ? 255 : 100);
mThumb.draw(canvas);
}
@Override
public boolean onTouchEvent(MotionEvent ev) {
super.onTouchEvent(ev);
switch (ev.getAction()) {
case MotionEvent.ACTION_DOWN: {
if (isEnabled()) {
getParent().requestDisallowInterceptTouchEvent(true);
}
break;
}
case MotionEvent.ACTION_MOVE: {
if (isEnabled()) {
final float x = ev.getX() - (getWidth() / 2);
final float y = -(ev.getY() - (getHeight() / 2));
final double alpha = Math.atan((double)y / (double)x);
if (!checkHit(x, y, alpha)) {
return true;
}
final int progress;
if (x >= 0 && y >= 0) {
mThumbX = (int)((mRadius * Math.cos(alpha)) + (getWidth() / 2));
mThumbY = (int)((getHeight() / 2) - (mRadius * Math.sin(alpha)));
progress = (int)((mMaxProgress / 2) - (alpha / mInterval));
} else if (x >= 0 && y <= 0) {
mThumbX = (int)((mRadius * Math.cos(alpha)) + (getWidth() / 2));
mThumbY = (int)((getHeight() / 2) - (mRadius * Math.sin(alpha)));
progress = (int)((mMaxProgress / 2) - (alpha / mInterval));
} else if (x <= 0 && y >= 0) {
mThumbX = (int)((getWidth() / 2) - (mRadius * Math.cos(alpha)));
mThumbY = (int)((getHeight() / 2) + (mRadius * Math.sin(alpha)));
progress = -(int)(((alpha + MAX_ANGLE) / mInterval));
} else {
mThumbX = (int)((getWidth() / 2) - (mRadius * Math.cos(alpha)));
mThumbY = (int)((getHeight() / 2) + (mRadius * Math.sin(alpha)));
progress = (int)(mMaxProgress - ((alpha - MAX_ANGLE) / mInterval));
}
invalidate();
if (mListener != null) {
if (progress != mProgress) {
mProgress = progress;
mListener.onProgressChanged(mProgress, true);
}
}
}
break;
}
case MotionEvent.ACTION_CANCEL:
case MotionEvent.ACTION_UP: {
break;
}
default: {
break;
}
}
return true;
}
/**
* Check if the user is touching the correct area
*
* @param x The horizontal coordinate
* @param y The vertical coordinate
* @param alpha The angle
* @return true if there is a hit in the allowed area
*/
private boolean checkHit(float x, float y, double alpha) {
final double radius = Math.sqrt((x * x) + (y * y));
if (radius < mInternalRadius) {
return false;
}
if (x >= 0) {
return true;
} else if (y >= 0) {
if ((alpha >= -(Math.PI / 2)) && (alpha <= -MAX_ANGLE)) {
return true;
}
} else {
if ((alpha >= MAX_ANGLE) && (alpha <= (Math.PI / 2))) {
return true;
}
}
return false;
}
/**
* Compute the position of the thumb based on the progress values
*/
private void progressToPosition() {
if (getWidth() == 0) { // Layout is not yet complete
return;
}
final double beta;
if (mProgress <= mMaxProgress / 2) {
beta = ((mMaxProgress / 2) - mProgress) * mInterval;
} else {
beta = ((mMaxProgress - mProgress) * mInterval) + Math.PI + MAX_ANGLE;
}
final double alpha;
if (beta >= 0 && beta <= Math.PI / 2) {
alpha = beta;
mThumbX = (int)((mRadius * Math.cos(alpha)) + (getWidth() / 2));
mThumbY = (int)((getHeight() / 2) - (mRadius * Math.sin(alpha)));
} else if (beta > Math.PI / 2 && beta < (Math.PI / 2) + MAX_ANGLE) {
alpha = beta - Math.PI;
mThumbX = (int)((getWidth() / 2) - (mRadius * Math.cos(alpha)));
mThumbY = (int)((getHeight() / 2) + (mRadius * Math.sin(alpha)));
} else if (beta <= 2 * Math.PI && beta > (3 * Math.PI) / 2) {
alpha = beta - (2 * Math.PI);
mThumbX = (int)((mRadius * Math.cos(alpha)) + (getWidth() / 2));
mThumbY = (int)((getHeight() / 2) - (mRadius * Math.sin(alpha)));
} else {
alpha = beta - Math.PI;
mThumbX = (int)((getWidth() / 2) - (mRadius * Math.cos(alpha)));
mThumbY = (int)((getHeight() / 2) + (mRadius * Math.sin(alpha)));
}
}
/**
* Compute the radians interval between progress values
*/
private void computeInterval() {
mInterval = (Math.PI - MAX_ANGLE) / (mMaxProgress / 2);
}
}