/*
* Tint Browser for Android
*
* Copyright (C) 2012 - to infinity and beyond J. Devauchelle and contributors.
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* version 3 as published by the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*/
package org.tint.ui.views;
import org.tint.R;
import android.animation.Animator;
import android.animation.AnimatorListenerAdapter;
import android.animation.AnimatorSet;
import android.animation.ObjectAnimator;
import android.animation.Animator.AnimatorListener;
import android.content.Context;
import android.util.AttributeSet;
import android.util.TypedValue;
import android.view.LayoutInflater;
import android.view.MotionEvent;
import android.view.View;
import android.view.animation.AccelerateDecelerateInterpolator;
import android.widget.RelativeLayout;
public class PanelLayout extends RelativeLayout {
public interface PanelEventsListener {
void onPanelShown();
void onPanelHidden();
}
private static final int ANIMATION_DURATION = 150;
private static final int BEZEL_SIZE_REDUCED = 5;
private static final int BEZEL_SIZE_STANDARD = 10;
private static final int BEZEL_SIZE_OPENED = 100;
private Animator mAnimator;
private boolean mPanelShown;
private AnimatorListener mShowListener;
private AnimatorListener mHideListener;
private RelativeLayout mContent;
private RelativeLayout mPanel;
private TabsScroller mTabsScroller;
private boolean mInSlide;
private float mBezelTopDelta;
private float mBezelSizeReduced;
private float mBezelSizeStandard;
private float mBezelSizeOpened;
private float mLastX;
private float mTranslation;
private float mAlpha;
private boolean mLastMoveOpen;
private PanelEventsListener mListener;
public PanelLayout(Context context) {
this(context, null);
}
public PanelLayout(Context context, AttributeSet attrs) {
this(context, attrs, 0);
}
public PanelLayout(Context context, AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle);
mListener = null;
mInSlide = false;
mLastMoveOpen = false;
mTranslation = 0;
mAlpha = 0;
mPanelShown = false;
mAnimator = null;
if (!isInEditMode()) {
TypedValue tv = new TypedValue();
context.getTheme().resolveAttribute(android.R.attr.actionBarSize, tv, true);
mBezelTopDelta = getResources().getDimension(tv.resourceId);
float density = context.getResources().getDisplayMetrics().density;
mBezelSizeReduced = BEZEL_SIZE_REDUCED * density + 0.5f;
mBezelSizeStandard = BEZEL_SIZE_STANDARD * density + 0.5f;
mBezelSizeOpened = BEZEL_SIZE_OPENED * density + 0.5f;
LayoutInflater layoutInflater = (LayoutInflater) context.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
View v = layoutInflater.inflate(R.layout.panel_layout, this);
mContent = (RelativeLayout) v.findViewById(R.id.main_content);
mPanel = (RelativeLayout) v.findViewById(R.id.panel);
mTabsScroller = (TabsScroller) v.findViewById(R.id.tabs_scroller);
mShowListener = new AnimatorListenerAdapter() {
@Override
public void onAnimationEnd(Animator animation) {
mAnimator = null;
mPanel.requestLayout();
mPanelShown = true;
mTranslation = mPanel.getWidth();
mAlpha = 1;
if (mListener != null) {
mListener.onPanelShown();
}
}
};
mHideListener = new AnimatorListenerAdapter() {
@Override
public void onAnimationEnd(Animator animation) {
mAnimator = null;
mPanelShown = false;
mTranslation = 0;
mAlpha = 0;
if (mListener != null) {
mListener.onPanelHidden();
}
}
};
}
}
public void setPanelEventsListener(PanelEventsListener listener) {
mListener = listener;
}
@Override
public boolean onInterceptTouchEvent(MotionEvent ev) {
switch (ev.getAction()) {
case MotionEvent.ACTION_DOWN:
float y = ev.getY();
if (y > mBezelTopDelta) {
float x = ev.getX();
float bezelSize;
if (mPanelShown) {
bezelSize = mBezelSizeOpened;
} else {
float height = mPanel.getHeight() - mBezelTopDelta;
if ((y - mBezelTopDelta <= 0.1 * height) ||
(y - mBezelTopDelta >= 0.9 * height)) {
bezelSize = mBezelSizeReduced;
} else {
bezelSize = mBezelSizeStandard;
}
}
if ((x >= mTranslation) &&
(x <= mTranslation + bezelSize)) {
return true;
}
}
break;
case MotionEvent.ACTION_UP:
case MotionEvent.ACTION_MOVE:
if (mInSlide) {
return true;
}
break;
}
return super.onInterceptTouchEvent(ev);
}
@Override
public boolean onTouchEvent(MotionEvent event) {
switch (event.getAction()) {
case MotionEvent.ACTION_DOWN:
float y = event.getY();
if (y > mBezelTopDelta) {
float x = event.getX();
float bezelSize;
if (mPanelShown) {
bezelSize = mBezelSizeOpened;
} else {
float height = mPanel.getHeight() - mBezelTopDelta;
if ((y - mBezelTopDelta <= 0.1 * height) ||
(y - mBezelTopDelta >= 0.9 * height)) {
bezelSize = mBezelSizeReduced;
} else {
bezelSize = mBezelSizeStandard;
}
}
if ((x >= mTranslation) &&
(x <= mTranslation + bezelSize)) {
mInSlide = true;
mLastX = event.getX();
return true;
}
}
break;
case MotionEvent.ACTION_UP:
if (mInSlide) {
mInSlide = false;
if (mLastMoveOpen) {
if (mTranslation >= 0.2 * mPanel.getWidth()) {
showPanel();
} else {
hidePanel();
}
} else {
if (mTranslation <= 0.9 * mPanel.getWidth()) {
hidePanel();
} else {
showPanel();
}
}
return true;
}
break;
case MotionEvent.ACTION_MOVE:
if (mInSlide) {
float translation = event.getX() - mLastX;
mLastMoveOpen = translation >= 0;
mTranslation += translation;
mAlpha = mTranslation / mPanel.getWidth();
if (mTranslation > mPanel.getWidth()) {
mTranslation = mPanel.getWidth();
mAlpha = 1;
mPanelShown = true;
}
if (mTranslation < 0) {
mTranslation = 0;
mAlpha = 0;
mPanelShown = false;
}
mLastX = event.getX();
mContent.setTranslationX(mTranslation);
mPanel.setAlpha(mAlpha);
return true;
}
break;
}
return super.onTouchEvent(event);
}
public TabsScroller getTabsScroller() {
return mTabsScroller;
}
public void togglePanel() {
if (mPanelShown) {
hidePanel();
} else {
showPanel();
}
}
public void showPanel() {
if (mAnimator != null) {
mAnimator.end();
}
mPanel.setAlpha(mAlpha);
AnimatorSet animator = new AnimatorSet();
AnimatorSet.Builder b = animator.play(ObjectAnimator.ofFloat(mPanel, "alpha", 1));
b.with(ObjectAnimator.ofFloat(mContent, "translationX", mPanel.getWidth()));
animator.addListener(mShowListener);
mAnimator = animator;
mAnimator.setDuration((long) (ANIMATION_DURATION * ((mPanel.getWidth() - mTranslation) / mPanel.getWidth())));
mAnimator.setInterpolator(new AccelerateDecelerateInterpolator());
mAnimator.start();
}
public void hidePanel() {
if (mAnimator != null) {
mAnimator.end();
}
mPanel.setAlpha(mAlpha);
AnimatorSet animator = new AnimatorSet();
AnimatorSet.Builder b = animator.play(ObjectAnimator.ofFloat(mPanel, "alpha", 0));
b.with(ObjectAnimator.ofFloat(mContent, "translationX", 0));
animator.addListener(mHideListener);
mAnimator = animator;
mAnimator.setDuration((long) (ANIMATION_DURATION * (mTranslation / mPanel.getWidth())));
mAnimator.setInterpolator(new AccelerateDecelerateInterpolator());
mAnimator.start();
}
public boolean isPanelShown() {
return mPanelShown;
}
}