/* * 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); } } }