blob: 17c4343ad4f808f3e492342dfb09035aec138628 [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.gallery3d.photoeditor.actions;
import android.content.Context;
import android.util.AttributeSet;
import android.view.MotionEvent;
/**
* View that handles touch-events to track flipping directions and angles.
*/
class FlipView extends FullscreenToolView {
/**
* Listens to flip changes.
*/
public interface OnFlipChangeListener {
void onAngleChanged(float horizontalDegrees, float verticalDegrees, boolean fromUser);
void onStartTrackingTouch();
void onStopTrackingTouch();
}
private static final float FIXED_DIRECTION_THRESHOLD = 20;
private OnFlipChangeListener listener;
private float maxFlipSpan;
private float touchStartX;
private float touchStartY;
private float currentHorizontalDegrees;
private float currentVerticalDegrees;
private float lastHorizontalDegrees;
private float lastVerticalDegrees;
private boolean fixedDirection;
private boolean fixedDirectionHorizontal;
public FlipView(Context context, AttributeSet attrs) {
super(context, attrs);
}
public void setOnFlipChangeListener(OnFlipChangeListener listener) {
this.listener = listener;
}
public void setFlippedAngles(float horizontalDegrees, float verticalDegrees) {
refreshAngle(horizontalDegrees, verticalDegrees, false);
}
/**
* Sets allowed degrees for every flip before flipping the view.
*/
public void setFlipSpan(float degrees) {
// Flip-span limits allowed flipping degrees of every flip for usability purpose; the max.
// flipped angles could be accumulated and larger than allowed flip-span.
maxFlipSpan = degrees;
}
private float calculateAngle(boolean flipHorizontal, float x, float y) {
// Use partial length along the moving direction to calculate the flip angle.
float maxDistance = (flipHorizontal ? getWidth() : getHeight()) * 0.35f;
float moveDistance = flipHorizontal ? (x - touchStartX) : (touchStartY - y);
if (Math.abs(moveDistance) > maxDistance) {
moveDistance = (moveDistance > 0) ? maxDistance : -maxDistance;
if (flipHorizontal) {
touchStartX = x - moveDistance;
} else {
touchStartY = moveDistance + y;
}
}
return (moveDistance / maxDistance) * maxFlipSpan;
}
@Override
public boolean onTouchEvent(MotionEvent ev) {
super.onTouchEvent(ev);
if (isEnabled()) {
switch (ev.getAction()) {
case MotionEvent.ACTION_DOWN:
fixedDirection = false;
lastHorizontalDegrees = currentHorizontalDegrees;
lastVerticalDegrees = currentVerticalDegrees;
touchStartX = ev.getX();
touchStartY = ev.getY();
if (listener != null) {
listener.onStartTrackingTouch();
}
break;
case MotionEvent.ACTION_MOVE:
// Allow only one direction for flipping during movements, and make the
// direction fixed once it exceeds threshold.
float x = ev.getX();
float y = ev.getY();
boolean flipHorizontal = fixedDirection ? fixedDirectionHorizontal
: (Math.abs(x - touchStartX) >= Math.abs(y - touchStartY));
float degrees = calculateAngle(flipHorizontal, x, y);
if (!fixedDirection && (Math.abs(degrees) > FIXED_DIRECTION_THRESHOLD)) {
fixedDirection = true;
fixedDirectionHorizontal = flipHorizontal;
}
if (flipHorizontal) {
refreshAngle(lastHorizontalDegrees + degrees, lastVerticalDegrees, true);
} else {
refreshAngle(lastHorizontalDegrees, lastVerticalDegrees + degrees, true);
}
break;
case MotionEvent.ACTION_CANCEL:
case MotionEvent.ACTION_UP:
if (listener != null) {
listener.onStopTrackingTouch();
}
break;
}
}
return true;
}
private void refreshAngle(float horizontalDegrees, float verticalDegrees, boolean fromUser) {
currentHorizontalDegrees = horizontalDegrees;
currentVerticalDegrees = verticalDegrees;
if (listener != null) {
listener.onAngleChanged(horizontalDegrees, verticalDegrees, fromUser);
}
}
}