blob: 6a7f16dd9a32c9d750d46df26d935cc67230f02b [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 com.android.videoeditor.service.ApiService;
import com.android.videoeditor.service.MovieTransition;
import com.android.videoeditor.R;
import android.content.Context;
import android.content.res.Resources;
import android.graphics.Bitmap;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Paint;
import android.graphics.Rect;
import android.util.AttributeSet;
import android.util.DisplayMetrics;
import android.util.Log;
import android.view.Display;
import android.view.GestureDetector;
import android.view.MotionEvent;
import android.view.View;
import android.view.WindowManager;
import android.widget.ImageView;
/**
* Transition view. This class assumes transition is always put on a MediaLinearLayout and is
* wrapped with a timeline scroll view.
*/
public class TransitionView extends ImageView {
// Logging
private static final String TAG = "TransitionView";
// Instance variables
private final GestureDetector mSimpleGestureDetector;
private final ScrollViewListener mScrollListener;
private final Rect mGeneratingTransitionProgressDestRect;
private final Paint mSeparatorPaint;
private boolean mIsScrolling;
// Convenient handle to the parent timeline scroll view.
private TimelineHorizontalScrollView mScrollView;
// Convenient handle to the parent timeline linear layout.
private MediaLinearLayout mTimeline;
private int mScrollX;
private int mScreenWidth;
private String mProjectPath;
private Bitmap[] mBitmaps;
private ItemSimpleGestureListener mGestureListener;
private int mGeneratingTransitionProgress;
private boolean mIsPlaying;
public TransitionView(Context context, AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle);
// Setup the gesture listener
mSimpleGestureDetector = new GestureDetector(context,
new GestureDetector.SimpleOnGestureListener() {
@Override
public boolean onSingleTapConfirmed(MotionEvent e) {
if (mGestureListener != null) {
return mGestureListener.onSingleTapConfirmed(TransitionView.this, -1,
e);
} else {
return false;
}
}
@Override
public void onLongPress(MotionEvent e) {
if (mGestureListener != null) {
mGestureListener.onLongPress(TransitionView.this, e);
}
}
});
mScrollListener = new ScrollViewListener() {
@Override
public void onScrollBegin(View view, int scrollX, int scrollY, boolean appScroll) {
mIsScrolling = true;
}
@Override
public void onScrollProgress(View view, int scrollX, int scrollY, boolean appScroll) {
invalidate();
}
@Override
public void onScrollEnd(View view, int scrollX, int scrollY, boolean appScroll) {
mIsScrolling = false;
mScrollX = scrollX;
if (requestThumbnails()) {
invalidate();
}
}
};
final Resources resources = getResources();
// Prepare the bitmap rectangles
final ProgressBar progressBar = ProgressBar.getProgressBar(context);
final int layoutHeight = (int)(resources.getDimension(R.dimen.media_layout_height) -
resources.getDimension(R.dimen.media_layout_padding) -
(2 * resources.getDimension(R.dimen.timelime_transition_vertical_inset)));
mGeneratingTransitionProgressDestRect = new Rect(getPaddingLeft(),
layoutHeight - progressBar.getHeight() - getPaddingBottom(), 0,
layoutHeight - getPaddingBottom());
// Initialize the progress value
mGeneratingTransitionProgress = -1;
// Get the screen width
final Display display = ((WindowManager) getContext().getSystemService(
Context.WINDOW_SERVICE)).getDefaultDisplay();
final DisplayMetrics metrics = new DisplayMetrics();
display.getMetrics(metrics);
mScreenWidth = metrics.widthPixels;
// Prepare the separator paint
mSeparatorPaint = new Paint();
mSeparatorPaint.setColor(Color.BLACK);
mSeparatorPaint.setStrokeWidth(2);
}
public TransitionView(Context context, AttributeSet attrs) {
this(context, attrs, 0);
}
public TransitionView(Context context) {
this(context, null, 0);
}
@Override
protected void onAttachedToWindow() {
// Add the horizontal scroll view listener
mScrollView = (TimelineHorizontalScrollView) getRootView().findViewById(
R.id.timeline_scroller);
mScrollView.addScrollListener(mScrollListener);
mScrollX = mScrollView.getScrollX();
mTimeline = (MediaLinearLayout) getRootView().findViewById(R.id.timeline_media);
}
@Override
protected void onDetachedFromWindow() {
// Remove the horizontal scroll listener
mScrollView.removeScrollListener(mScrollListener);
// Release the current set of bitmaps
if (mBitmaps != null) {
for (int i = 0; i < mBitmaps.length; i++) {
if (mBitmaps[i] != null) {
mBitmaps[i].recycle();
}
}
mBitmaps = null;
}
}
/**
* @param projectPath The project path
*/
public void setProjectPath(String projectPath) {
mProjectPath = projectPath;
}
/**
* @param listener The gesture listener
*/
public void setGestureListener(ItemSimpleGestureListener listener) {
mGestureListener = listener;
}
/**
* Resets the transition generation progress.
*/
public void resetGeneratingTransitionProgress() {
setGeneratingTransitionProgress(-1);
}
/**
* Sets the transition generation progress.
*/
public void setGeneratingTransitionProgress(int progress) {
if (progress == 100) {
// Request the preview bitmaps
requestThumbnails();
mGeneratingTransitionProgress = -1;
} else {
mGeneratingTransitionProgress = progress;
}
invalidate();
}
/**
* @return true if generation is in progress
*/
public boolean isGeneratingTransition() {
return (mGeneratingTransitionProgress >= 0);
}
/**
* A view enters or exits the playback mode
*
* @param playback true if playback is in progress
*/
public void setPlaybackMode(boolean playback) {
mIsPlaying = playback;
}
/**
* @param bitmaps The bitmaps array
*
* @return true if the bitmaps were used
*/
public boolean setBitmaps(Bitmap[] bitmaps) {
if (mGeneratingTransitionProgress >= 0) {
return false;
}
// Release the current set of bitmaps
if (mBitmaps != null) {
for (int i = 0; i < mBitmaps.length; i++) {
if (mBitmaps[i] != null) {
mBitmaps[i].recycle();
}
}
}
mBitmaps = bitmaps;
invalidate();
return true;
}
@Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
// If the view is too small, don't draw anything.
if (getWidth() <= getPaddingLeft() + getPaddingRight()) {
return;
}
if (mGeneratingTransitionProgress >= 0) {
ProgressBar.getProgressBar(getContext()).draw(canvas, mGeneratingTransitionProgress,
mGeneratingTransitionProgressDestRect, getPaddingLeft(),
getWidth() - getPaddingRight());
} else if (mBitmaps != null) {
final int halfWidth = getWidth() / 2;
// Draw the bitmaps
// Draw the left side of the transition
canvas.save();
canvas.clipRect(getPaddingLeft(), getPaddingTop(), halfWidth,
getHeight() - getPaddingBottom());
if (mBitmaps[0] != null) {
canvas.drawBitmap(mBitmaps[0], getPaddingLeft(), getPaddingTop(), null);
} else {
canvas.drawColor(Color.BLACK);
}
canvas.restore();
// Draw the right side of the transition
canvas.save();
canvas.clipRect(halfWidth, getPaddingTop(),
getWidth() - getPaddingRight(), getHeight() - getPaddingBottom());
if (mBitmaps[1] != null) {
canvas.drawBitmap(mBitmaps[1],
getWidth() - getPaddingRight() - mBitmaps[1].getWidth(), getPaddingTop(),
null);
} else {
canvas.drawColor(Color.BLACK);
}
canvas.restore();
canvas.drawLine(halfWidth, getPaddingTop(), halfWidth,
getHeight() - getPaddingBottom(), mSeparatorPaint);
// Dim myself if some view on the timeline is selected but not me
// by drawing a transparent black overlay.
if (!isSelected() && mTimeline.hasItemSelected()) {
final Paint paint = new Paint();
paint.setColor(Color.BLACK);
paint.setAlpha(192);
canvas.drawPaint(paint);
}
} else if (mIsPlaying) { // Playing
} else if (mIsScrolling) { // Scrolling
} else { // Not scrolling and not playing
requestThumbnails();
}
}
@Override
public boolean onTouchEvent(MotionEvent ev) {
// Let the gesture detector inspect all events.
mSimpleGestureDetector.onTouchEvent(ev);
return super.onTouchEvent(ev);
}
/**
* Request thumbnails if necessary
*
* @return true if the bitmaps already exist
*/
private boolean requestThumbnails() {
// Check if we already have the bitmaps
if (mBitmaps != null) {
return true;
}
// Do not request thumbnails during playback
if (mIsScrolling) {
return false;
}
final MovieTransition transition = (MovieTransition)getTag();
// Check if we already requested the thumbnails
if (ApiService.isTransitionThumbnailsPending(mProjectPath, transition.getId())) {
return false;
}
final int start = getLeft() + getPaddingLeft() - mScrollX;
final int end = getRight() - getPaddingRight() - mScrollX;
if (start >= mScreenWidth || end < 0 || start == end) {
if (Log.isLoggable(TAG, Log.VERBOSE)) {
Log.v(TAG, "Transition view is off screen: " + transition.getId() +
", from: " + start + " to " + end);
}
// Release the current set of bitmaps
if (mBitmaps != null) {
for (int i = 0; i < mBitmaps.length; i++) {
if (mBitmaps[i] != null) {
mBitmaps[i].recycle();
}
}
mBitmaps = null;
}
return false;
}
// Compute the thumbnail width
final int thumbnailHeight = getHeight() - getPaddingTop() - getPaddingBottom();
// Request the thumbnails
ApiService.getTransitionThumbnails(getContext(), mProjectPath, transition.getId(),
thumbnailHeight);
return false;
}
}