blob: b3d1414eaeaa5d6f3e1b350902814e92fcbf2c39 [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 java.util.ArrayList;
import java.util.List;
import com.android.videoeditor.R;
import android.app.Activity;
import android.content.Context;
import android.content.res.Resources;
import android.graphics.Canvas;
import android.graphics.drawable.Drawable;
import android.os.Handler;
import android.os.SystemClock;
import android.util.AttributeSet;
import android.view.Display;
import android.view.MotionEvent;
import android.view.ScaleGestureDetector;
/**
* The timeline scroll view
*/
public class TimelineHorizontalScrollView extends HorizontalScrollView {
public final static int PLAYHEAD_NORMAL = 1;
public final static int PLAYHEAD_MOVE_OK = 2;
public final static int PLAYHEAD_MOVE_NOT_OK = 3;
// Instance variables
private final List<ScrollViewListener> mScrollListenerList;
private final Handler mHandler;
private final int mPlayheadMarginTop;
private final int mPlayheadMarginTopOk;
private final int mPlayheadMarginTopNotOk;
private final int mPlayheadMarginBottom;
private final Drawable mNormalPlayheadDrawable;
private final Drawable mMoveOkPlayheadDrawable;
private final Drawable mMoveNotOkPlayheadDrawable;
private final int mHalfParentWidth;
private ScaleGestureDetector mScaleDetector;
private int mLastScrollX;
private boolean mIsScrolling;
private boolean mAppScroll;
private boolean mEnableUserScrolling;
// The runnable which executes when the scrolling ends
private Runnable mScrollEndedRunnable = new Runnable() {
@Override
public void run() {
mIsScrolling = false;
for (ScrollViewListener listener : mScrollListenerList) {
listener.onScrollEnd(TimelineHorizontalScrollView.this, getScrollX(),
getScrollY(), mAppScroll);
}
mAppScroll = false;
}
};
public TimelineHorizontalScrollView(Context context, AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle);
mEnableUserScrolling = true;
mScrollListenerList = new ArrayList<ScrollViewListener>();
mHandler = new Handler();
// Compute half the width of the screen (and therefore the parent view)
final Display display = ((Activity)context).getWindowManager().getDefaultDisplay();
mHalfParentWidth = display.getWidth() / 2;
// This value is shared by all children. It represents the width of
// the left empty view.
setTag(R.id.left_view_width, mHalfParentWidth);
setTag(R.id.playhead_offset, -1);
setTag(R.id.playhead_type, PLAYHEAD_NORMAL);
final Resources resources = context.getResources();
// Get the playhead margins
mPlayheadMarginTop = (int)resources.getDimension(R.dimen.playhead_margin_top);
mPlayheadMarginBottom = (int)resources.getDimension(R.dimen.playhead_margin_bottom);
mPlayheadMarginTopOk = (int)resources.getDimension(R.dimen.playhead_margin_top_ok);
mPlayheadMarginTopNotOk = (int)resources.getDimension(R.dimen.playhead_margin_top_not_ok);
// Prepare the playhead drawable
mNormalPlayheadDrawable = resources.getDrawable(R.drawable.ic_playhead);
mMoveOkPlayheadDrawable = resources.getDrawable(R.drawable.playhead_move_ok);
mMoveNotOkPlayheadDrawable = resources.getDrawable(R.drawable.playhead_move_not_ok);
}
public TimelineHorizontalScrollView(Context context, AttributeSet attrs) {
this(context, attrs, 0);
}
public TimelineHorizontalScrollView(Context context) {
this(context, null, 0);
}
/**
* Invoked to enable/disable user scrolling (as opposed to programmatic scrolling)
* @param enable true to enable user scrolling
*/
public void enableUserScrolling(boolean enable) {
mEnableUserScrolling = enable;
}
@Override
public boolean onInterceptTouchEvent(MotionEvent ev) {
mScaleDetector.onTouchEvent(ev);
return mScaleDetector.isInProgress() || super.onInterceptTouchEvent(ev);
}
@Override
public boolean onTouchEvent(MotionEvent ev) {
if (mEnableUserScrolling) {
mScaleDetector.onTouchEvent(ev);
if (!mScaleDetector.isInProgress()) {
return super.onTouchEvent(ev);
} else {
return true;
}
} else {
if (mScaleDetector.isInProgress()) {
final MotionEvent cancelEvent = MotionEvent.obtain(SystemClock.uptimeMillis(),
SystemClock.uptimeMillis(), MotionEvent.ACTION_CANCEL, 0, 0, 0);
mScaleDetector.onTouchEvent(cancelEvent);
cancelEvent.recycle();
}
return true;
}
}
/**
* @param listener The scale listener
*/
public void setScaleListener(ScaleGestureDetector.SimpleOnScaleGestureListener listener) {
mScaleDetector = new ScaleGestureDetector(getContext(), listener);
}
/**
* @param listener The listener
*/
public void addScrollListener(ScrollViewListener listener) {
mScrollListenerList.add(listener);
}
/**
* @param listener The listener
*/
public void removeScrollListener(ScrollViewListener listener) {
mScrollListenerList.remove(listener);
}
/**
* @return true if scrolling is in progress
*/
public boolean isScrolling() {
return mIsScrolling;
}
/**
* The app wants to scroll (as opposed to the user)
*
* @param scrollX Horizontal scroll position
* @param smooth true to scroll smoothly
*/
public void appScrollTo(int scrollX, boolean smooth) {
if (getScrollX() == scrollX) {
return;
}
mAppScroll = true;
if (smooth) {
smoothScrollTo(scrollX, 0);
} else {
scrollTo(scrollX, 0);
}
}
/**
* The app wants to scroll (as opposed to the user)
*
* @param scrollX Horizontal scroll offset
* @param smooth true to scroll smoothly
*/
public void appScrollBy(int scrollX, boolean smooth) {
mAppScroll = true;
if (smooth) {
smoothScrollBy(scrollX, 0);
} else {
scrollBy(scrollX, 0);
}
}
@Override
public void computeScroll() {
super.computeScroll();
final int scrollX = getScrollX();
if (mLastScrollX != scrollX) {
mLastScrollX = scrollX;
// Cancel the previous event
mHandler.removeCallbacks(mScrollEndedRunnable);
// Post a new event
mHandler.postDelayed(mScrollEndedRunnable, 300);
final int scrollY = getScrollY();
if (mIsScrolling) {
for (ScrollViewListener listener : mScrollListenerList) {
listener.onScrollProgress(this, scrollX, scrollY, mAppScroll);
}
} else {
mIsScrolling = true;
for (ScrollViewListener listener : mScrollListenerList) {
listener.onScrollBegin(this, scrollX, scrollY, mAppScroll);
}
}
}
}
@Override
protected void dispatchDraw(Canvas canvas) {
super.dispatchDraw(canvas);
final int playheadOffset = (Integer)getTag(R.id.playhead_offset);
final int startX;
if (playheadOffset < 0) {
// Draw the playhead in the middle of the screen
startX = mHalfParentWidth + getScrollX();
} else {
// Draw the playhead at the specified position (during trimming)
startX = playheadOffset;
}
final int playheadType = (Integer)getTag(R.id.playhead_type);
final int halfPlayheadWidth = mNormalPlayheadDrawable.getIntrinsicWidth() / 2;
switch (playheadType) {
case PLAYHEAD_NORMAL: {
// Draw the normal playhead
mNormalPlayheadDrawable.setBounds(
startX - halfPlayheadWidth,
mPlayheadMarginTop,
startX + halfPlayheadWidth,
getHeight() - mPlayheadMarginBottom);
mNormalPlayheadDrawable.draw(canvas);
break;
}
case PLAYHEAD_MOVE_OK: {
// Draw the move playhead
mMoveOkPlayheadDrawable.setBounds(
startX - halfPlayheadWidth,
mPlayheadMarginTopOk,
startX + halfPlayheadWidth,
mPlayheadMarginTopOk + mMoveOkPlayheadDrawable.getIntrinsicHeight());
mMoveOkPlayheadDrawable.draw(canvas);
break;
}
case PLAYHEAD_MOVE_NOT_OK: {
// Draw the move playhead
mMoveNotOkPlayheadDrawable.setBounds(
startX - halfPlayheadWidth,
mPlayheadMarginTopNotOk,
startX + halfPlayheadWidth,
mPlayheadMarginTopNotOk + mMoveNotOkPlayheadDrawable.getIntrinsicHeight());
mMoveNotOkPlayheadDrawable.draw(canvas);
break;
}
default: {
break;
}
}
}
}