content: /*
   * Copyright (C) 2013 Priboi Tiberiu
   * Copyright (C) 2013 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.ptr.folding;
  
  import android.content.Context;
  import android.util.AttributeSet;
  import android.view.GestureDetector;
  import android.view.MotionEvent;
  import android.view.View;
  import android.view.ViewConfiguration;
  
  /**
   * The folding layout where the number of folds, the anchor point and the
   * orientation of the fold can be specified. Each of these parameters can be
   * modified individually and updates and resets the fold to a default (unfolded)
   * state. The fold factor varies between 0 (completely unfolded flat image) to
   * 1.0 (completely folded, non-visible image).
   * 
   * This layout throws an exception if there is more than one child added to the
   * view. For more complicated view hierarchy's inside the folding layout, the
   * views should all be nested inside 1 parent layout.
   * 
   * This layout folds the contents of its child in real time. By applying matrix
   * transformations when drawing to canvas, the contents of the child may change
   * as the fold takes place. It is important to note that there are jagged edges
   * about the perimeter of the layout as a result of applying transformations to
   * a rectangle. This can be avoided by having the child of this layout wrap its
   * content inside a 1 pixel transparent border. This will cause an anti-aliasing
   * like effect and smoothen out the edges.
   * 
   */
  public class FoldingLayout extends BaseFoldingLayout {
  
  	private final String FOLDING_VIEW_EXCEPTION_MESSAGE = "Folding Layout can only 1 child at "
  			+ "most";
  
  	private GestureDetector mScrollGestureDetector;
  
  	FoldingLayout that = null;
  
  	private int mTranslation = 0;
  	private int mParentPositionY = -1;
  	private int mTouchSlop = -1;
  	private boolean mDidNotStartScroll = true;
  
  	public FoldingLayout(Context context) {
  		super(context);
  		init(context, null);
  		that = this;
  	}
  
  	public FoldingLayout(Context context, AttributeSet attrs) {
  		super(context, attrs);
  		init(context, attrs);
  		that = this;
  	}
  
  	public FoldingLayout(Context context, AttributeSet attrs, int defStyle) {
  		super(context, attrs, defStyle);
  		init(context, attrs);
  		that = this;
  	}
  
  	public void init(Context context, AttributeSet attrs) {
  		mScrollGestureDetector = new GestureDetector(context,
  				new ScrollGestureDetector());
  		mTouchSlop = ViewConfiguration.get(context).getScaledTouchSlop();
  		setAnchorFactor(0);
  		super.init(context, attrs);
  	}
  
  	@Override
  	protected boolean addViewInLayout(View child, int index,
  			LayoutParams params, boolean preventRequestLayout) {
  		throwCustomException(getChildCount());
  		boolean returnValue = super.addViewInLayout(child, index, params,
  				preventRequestLayout);
  		return returnValue;
  	}
  
  	/**
  	 * The custom exception to be thrown so as to limit the number of views in
  	 * this layout to at most one.
  	 */
  	private class NumberOfFoldingLayoutChildrenException extends
  			RuntimeException {
  		/**
  		 * 
  		 */
  		private static final long serialVersionUID = 1L;
  
  		public NumberOfFoldingLayoutChildrenException(String message) {
  			super(message);
  		}
  	}
  
  	/**
  	 * Throws an exception if the number of views added to this layout exceeds
  	 * one.
  	 */
  	private void throwCustomException(int numOfChildViews) {
  		if (numOfChildViews == 1) {
  			throw new NumberOfFoldingLayoutChildrenException(
  					FOLDING_VIEW_EXCEPTION_MESSAGE);
  		}
  	}
  
  	/** This class uses user touch events to fold and unfold the folding view. */
  	private class ScrollGestureDetector extends
  			GestureDetector.SimpleOnGestureListener {
  		@Override
  		public boolean onDown(MotionEvent e) {
  			mDidNotStartScroll = true;
  			return true;
  		}
  
  		/**
  		 * All the logic here is used to determine by what factor the paper view
  		 * should be folded in response to the user's touch events. The logic
  		 * here uses vertical scrolling to fold a vertically oriented view and
  		 * horizontal scrolling to fold a horizontally oriented fold. Depending
  		 * on where the anchor point of the fold is, movements towards or away
  		 * from the anchor point will either fold or unfold the paper
  		 * respectively.
  		 * 
  		 * The translation logic here also accounts for the touch slop when a
  		 * new user touch begins, but before a scroll event is first invoked.
  		 */
  		@Override
  		public boolean onScroll(MotionEvent e1, MotionEvent e2,
  				float distanceX, float distanceY) {
  			int touchSlop = 0;
  			float factor;
  			if (mOrientation == Orientation.VERTICAL) {
  				factor = Math.abs((float) (mTranslation)
  						/ (float) (that.getHeight()));
  
  				if (e2.getY() - mParentPositionY <= that.getHeight()
  						&& e2.getY() - mParentPositionY >= 0) {
  					if ((e2.getY() - mParentPositionY) > that.getHeight()
  							* getAnchorFactor()) {
  						mTranslation -= (int) distanceY;
  						touchSlop = distanceY < 0 ? -mTouchSlop : mTouchSlop;
  					} else {
  						mTranslation += (int) distanceY;
  						touchSlop = distanceY < 0 ? mTouchSlop : -mTouchSlop;
  					}
  					mTranslation = mDidNotStartScroll ? mTranslation
  							+ touchSlop : mTranslation;
  
  					if (mTranslation < -that.getHeight()) {
  						mTranslation = -that.getHeight();
  					}
  				}
  			} else {
  				factor = Math.abs(((float) mTranslation)
  						/ ((float) that.getWidth()));
  
  				if (e2.getRawX() > that.getWidth() * getAnchorFactor()) {
  					mTranslation -= (int) distanceX;
  					touchSlop = distanceX < 0 ? -mTouchSlop : mTouchSlop;
  				} else {
  					mTranslation += (int) distanceX;
  					touchSlop = distanceX < 0 ? mTouchSlop : -mTouchSlop;
  				}
  				mTranslation = mDidNotStartScroll ? mTranslation + touchSlop
  						: mTranslation;
  
  				if (mTranslation < -that.getWidth()) {
  					mTranslation = -that.getWidth();
  				}
  			}
  
  			mDidNotStartScroll = false;
  
  			if (mTranslation > 0) {
  				mTranslation = 0;
  			}
  
  			that.setFoldFactor(factor);
  
  			return true;
  		}
  	}
  
  	@Override
  	public boolean onTouchEvent(MotionEvent me) {
  		return mScrollGestureDetector.onTouchEvent(me);
  	}
  
  }