/* * 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.example.android.videoeditor.widgets; import java.util.List; import android.app.Activity; import android.app.Dialog; import android.content.Context; import android.content.DialogInterface; import android.content.Intent; import android.os.Bundle; import android.os.Handler; import android.util.AttributeSet; import android.util.Log; import android.view.ActionMode; import android.view.Display; import android.view.Menu; import android.view.MenuItem; import android.view.MotionEvent; import android.view.View; import android.widget.LinearLayout; import android.widget.RelativeLayout; import com.example.android.videoeditor.AlertDialogs; import com.example.android.videoeditor.OverlayTitleEditor; import com.example.android.videoeditor.VideoEditorActivity; import com.example.android.videoeditor.service.ApiService; import com.example.android.videoeditor.service.MovieMediaItem; import com.example.android.videoeditor.service.MovieOverlay; import com.example.android.videoeditor.service.VideoEditorProject; import com.example.android.videoeditor.util.FileUtils; import com.example.android.videoeditor.util.MediaItemUtils; import com.example.android.videoeditor.R; /** * LinearLayout which displays overlays. */ public class OverlayLinearLayout extends LinearLayout { // Logging private static final String TAG = "OverlayLinearLayout"; // Dialog parameter ids private static final String PARAM_DIALOG_MEDIA_ITEM_ID = "media_item_id"; // Default overlay duration public static final long DEFAULT_TITLE_DURATION = 3000; // Instance variables private final ItemMoveGestureListener mOverlayGestureListener; private final int mHalfParentWidth; private final Handler mHandler; private final int mHandleWidth; private ActionMode mOverlayActionMode; private boolean mPlaybackInProgress; private VideoEditorProject mProject; private HandleView mLeftHandle, mRightHandle; private boolean mMoveLayoutPending; private View mResizingView; /** * The overlay listener */ public interface OverlayLayoutListener { /** * Add a new overlay */ public void onAddOverlay(); } /** * The overlay action mode handler. */ private class OverlayActionModeCallback implements ActionMode.Callback { // Instance variables private final MovieMediaItem mMediaItem; public OverlayActionModeCallback(MovieMediaItem mediaItem) { mMediaItem = mediaItem; } @Override public boolean onCreateActionMode(ActionMode mode, Menu menu) { mOverlayActionMode = mode; final Activity activity = (Activity) getContext(); activity.getMenuInflater().inflate(R.menu.overlay_mode_menu, menu); return true; } @Override public boolean onPrepareActionMode(ActionMode mode, Menu menu) { final boolean enable = !ApiService.isProjectBeingEdited(mProject.getPath()) && !mPlaybackInProgress; final MenuItem eomi = menu.findItem(R.id.action_edit_overlay); eomi.setEnabled(enable); final MenuItem romi = menu.findItem(R.id.action_remove_overlay); romi.setEnabled(enable); return true; } @Override public boolean onActionItemClicked(ActionMode mode, MenuItem item) { switch (item.getItemId()) { case R.id.action_edit_overlay: { final Activity activity = (Activity) getContext(); final Intent intent = new Intent(activity, OverlayTitleEditor.class); intent.putExtra(OverlayTitleEditor.PARAM_MEDIA_ITEM_ID, mMediaItem.getId()); final MovieOverlay overlay = mMediaItem.getOverlay(); intent.putExtra(OverlayTitleEditor.PARAM_OVERLAY_ID, overlay.getId()); intent.putExtra(OverlayTitleEditor.PARAM_OVERLAY_ATTRIBUTES, overlay.buildUserAttributes()); activity.startActivityForResult(intent, VideoEditorActivity.REQUEST_CODE_PICK_OVERLAY); break; } case R.id.action_remove_overlay: { final Bundle bundle = new Bundle(); bundle.putString(PARAM_DIALOG_MEDIA_ITEM_ID, mMediaItem.getId()); ((Activity) getContext()).showDialog( VideoEditorActivity.DIALOG_REMOVE_OVERLAY_ID, bundle); break; } default: { break; } } return true; } @Override public void onDestroyActionMode(ActionMode mode) { final View overlayView = getOverlayView(mMediaItem.getId()); if (overlayView != null) { selectView(overlayView, false); } mOverlayActionMode = null; } } public OverlayLinearLayout(Context context, AttributeSet attrs, int defStyle) { super(context, attrs, defStyle); mOverlayGestureListener = new ItemMoveGestureListener() { private MovieMediaItem mScrollMediaItem; private MovieOverlay mScrollOverlay; private long mScrollTotalDurationMs, mScrollMediaItemStartTime; private boolean mScrolled; @Override public boolean onSingleTapConfirmed(View view, int area, MotionEvent e) { if (mPlaybackInProgress) { return false; } switch (((OverlayView)view).getState()) { case OverlayView.STATE_STUB: { unselectAllViews(); ((OverlayView)view).setState(OverlayView.STATE_ADD_BUTTON); break; } case OverlayView.STATE_ADD_BUTTON: { final MovieMediaItem mediaItem = (MovieMediaItem)view.getTag(); final Intent intent = new Intent(getContext(), OverlayTitleEditor.class); intent.putExtra(OverlayTitleEditor.PARAM_MEDIA_ITEM_ID, mediaItem.getId()); ((Activity) getContext()).startActivityForResult(intent, VideoEditorActivity.REQUEST_CODE_PICK_OVERLAY); break; } case OverlayView.STATE_OVERLAY: { if (!view.isSelected()) { selectView(view, true); } break; } default: { break; } } return true; } @Override public void onLongPress(View view, MotionEvent e) { if (mPlaybackInProgress) { return; } switch (((OverlayView)view).getState()) { case OverlayView.STATE_STUB: { break; } case OverlayView.STATE_ADD_BUTTON: { break; } case OverlayView.STATE_OVERLAY: { if (!view.isSelected()) { selectView(view, true); } if (mOverlayActionMode == null) { startActionMode(new OverlayActionModeCallback( (MovieMediaItem)view.getTag())); } break; } default: { break; } } } @Override public boolean onMoveBegin(View view, MotionEvent e) { if (mPlaybackInProgress) { return false; } mScrollMediaItem = (MovieMediaItem)view.getTag(); mScrollMediaItemStartTime = mProject.getMediaItemBeginTime(mScrollMediaItem.getId()); mScrollOverlay = mScrollMediaItem.getOverlay(); // The duration of the timeline does not change while moving the Overlay mScrollTotalDurationMs = mProject.computeDuration(); mRightHandle.setVisibility(View.GONE); mScrolled = false; return true; } @Override public boolean onMove(View view, MotionEvent e1, MotionEvent e2) { final int beginPos = (int)(view.getLeft() - mHalfParentWidth - e1.getX() + e2.getX()); long startTimeMs = ((beginPos * mScrollTotalDurationMs) / (getWidth() - (2 * mHalfParentWidth))); if (startTimeMs <= mScrollMediaItemStartTime) { startTimeMs = mScrollMediaItemStartTime; } else if (startTimeMs + mScrollOverlay.getAppDuration() > mScrollMediaItemStartTime + mScrollMediaItem.getAppTimelineDuration()) { startTimeMs = mScrollMediaItemStartTime + mScrollMediaItem.getAppTimelineDuration() - mScrollOverlay.getAppDuration(); } mScrolled = true; mScrollOverlay.setAppStartTime(startTimeMs - mScrollMediaItemStartTime + mScrollMediaItem.getAppBoundaryBeginTime()); requestLayout(); return true; } @Override public void onMoveEnd(View view) { mRightHandle.setVisibility(View.VISIBLE); if (mScrolled) { mScrolled = false; // Update the limits of the right handle mRightHandle.setLimitReached(mScrollOverlay.getAppDuration() <= MediaItemUtils.getMinimumMediaItemDuration(mScrollMediaItem), mScrollOverlay.getAppStartTime() + mScrollOverlay.getAppDuration() >= mScrollMediaItem.getAppBoundaryEndTime()); ApiService.setOverlayStartTime(getContext(), mProject.getPath(), mScrollMediaItem.getId(), mScrollOverlay.getId(), mScrollOverlay.getAppStartTime()); } } }; // Add the beginning timeline item final View beginView = inflate(getContext(), R.layout.empty_timeline_item, null); beginView.setOnClickListener(new OnClickListener() { @Override public void onClick(View view) { unselectAllViews(); } }); addView(beginView); // Add the end timeline item final View endView = inflate(getContext(), R.layout.empty_timeline_item, null); endView.setOnClickListener(new OnClickListener() { @Override public void onClick(View view) { unselectAllViews(); } }); addView(endView); mLeftHandle = (HandleView)inflate(getContext(), R.layout.left_handle_view, null); addView(mLeftHandle); mRightHandle = (HandleView)inflate(getContext(), R.layout.right_handle_view, null); addView(mRightHandle); mHandleWidth = (int)context.getResources().getDimension(R.dimen.handle_width); // Compute half the width of the screen (and therefore the parent view) final Display display = ((Activity)context).getWindowManager().getDefaultDisplay(); mHalfParentWidth = display.getWidth() / 2; mHandler = new Handler(); setMotionEventSplittingEnabled(false); } public OverlayLinearLayout(Context context, AttributeSet attrs) { this(context, attrs, 0); } public OverlayLinearLayout(Context context) { this(context, null, 0); } /** * @param project The project */ public void setProject(VideoEditorProject project) { // Close the contextual action bar if (mOverlayActionMode != null) { mOverlayActionMode.finish(); mOverlayActionMode = null; } mLeftHandle.setVisibility(View.GONE); mLeftHandle.setListener(null); mRightHandle.setVisibility(View.GONE); mRightHandle.setListener(null); removeViews(); mProject = project; } /** * @param inProgress true if playback is in progress */ public void setPlaybackInProgress(boolean inProgress) { mPlaybackInProgress = inProgress; // Don't allow the user to interact with the overlays while playback // is in progress if (inProgress && mOverlayActionMode != null) { mOverlayActionMode.finish(); mOverlayActionMode = null; } } /** * Add all the media items * * @param mediaItems The list of media items */ public void addMediaItems(List<MovieMediaItem> mediaItems) { if (mOverlayActionMode != null) { mOverlayActionMode.finish(); mOverlayActionMode = null; } removeViews(); for (MovieMediaItem mediaItem : mediaItems) { addMediaItem(mediaItem); } } /** * Add a new media item at the end of the timeline * * @param mediaItem The media item * @return The view that was added */ private View addMediaItem(MovieMediaItem mediaItem) { final OverlayView overlayView = (OverlayView)inflate(getContext(), R.layout.overlay_item, null); if (mediaItem.getOverlay() != null) { overlayView.setState(OverlayView.STATE_OVERLAY); } else { overlayView.setState(OverlayView.STATE_STUB); } overlayView.setTag(mediaItem); overlayView.setGestureListener(mOverlayGestureListener); final LayoutParams lp = new LayoutParams( LayoutParams.WRAP_CONTENT, LayoutParams.FILL_PARENT); addView(overlayView, getChildCount() - 1, lp); if (mOverlayActionMode != null) { mOverlayActionMode.invalidate(); } requestLayout(); return overlayView; } /** * Insert a new media item after the specified media item id * * @param mediaItem The media item * @param afterMediaItemId The id of the media item preceding the media item */ public void insertMediaItem(MovieMediaItem mediaItem, String afterMediaItemId) { final OverlayView overlayView = (OverlayView)inflate(getContext(), R.layout.overlay_item, null); if (mediaItem.getOverlay() != null) { overlayView.setState(OverlayView.STATE_OVERLAY); } else { overlayView.setState(OverlayView.STATE_STUB); } overlayView.setTag(mediaItem); overlayView.setGestureListener(mOverlayGestureListener); int insertViewIndex; if (afterMediaItemId != null) { if ((insertViewIndex = getMediaItemViewIndex(afterMediaItemId)) == -1) { Log.e(TAG, "Media item not found: " + afterMediaItemId); return; } insertViewIndex++; } else { // Insert at the beginning insertViewIndex = 1; } final LayoutParams lp = new LayoutParams( LayoutParams.WRAP_CONTENT, LayoutParams.FILL_PARENT); addView(overlayView, insertViewIndex, lp); if (mOverlayActionMode != null) { mOverlayActionMode.invalidate(); } requestLayout(); } /** * Update media item * * @param mediaItem The media item */ public void updateMediaItem(MovieMediaItem mediaItem) { final String mediaItemId = mediaItem.getId(); final int childrenCount = getChildCount(); for (int i = 0; i < childrenCount; i++) { final View childView = getChildAt(i); final MovieMediaItem mi = (MovieMediaItem)childView.getTag(); if (mi != null && mediaItemId.equals(mi.getId())) { if (mediaItem != mi) { // The media item is a new instance childView.setTag(mediaItem); } break; } } requestLayout(); invalidate(); } /** * Remove a media item * * @param mediaItemId The media item id * @return The view which was removed */ public View removeMediaItem(String mediaItemId) { final int childrenCount = getChildCount(); for (int i = 0; i < childrenCount; i++) { final View childView = getChildAt(i); final MovieMediaItem mediaItem = (MovieMediaItem)childView.getTag(); if (mediaItem != null && mediaItem.getId().equals(mediaItemId)) { removeViewAt(i); requestLayout(); return childView; } } return null; } /** * A new overlay was added * * @param mediaItemId The media item which owns the overlay * @param overlay The overlay which was added */ public void addOverlay(String mediaItemId, MovieOverlay overlay) { final OverlayView view = (OverlayView)getOverlayView(mediaItemId); if (view == null) { Log.e(TAG, "addOverlay: Media item not found: " + mediaItemId); return; } view.setState(OverlayView.STATE_OVERLAY); requestLayout(); invalidate(); } /** * An overlay was removed * * @param mediaItemId The media item which owns the overlay * @param overlayId The overlay id */ public void removeOverlay(String mediaItemId, String overlayId) { final OverlayView view = (OverlayView)getOverlayView(mediaItemId); if (view == null) { Log.e(TAG, "removeOverlay: Media item not found: " + mediaItemId); return; } view.setState(OverlayView.STATE_STUB); requestLayout(); invalidate(); if (mOverlayActionMode != null) { mOverlayActionMode.finish(); mOverlayActionMode = null; } } /** * Update the overlay attributes * * @param mediaItemId The media item id * @param overlayId The overlay id * @param userAttributes The overlay attributes */ public void updateOverlayAttributes(String mediaItemId, String overlayId, Bundle userAttributes) { final MovieMediaItem mediaItem = mProject.getMediaItem(mediaItemId); if (mediaItem == null) { Log.e(TAG, "updateOverlayAttributes: Media item not found: " + mediaItemId); return; } final View overlayView = getOverlayView(mediaItemId); if (overlayView == null) { Log.e(TAG, "updateOverlayAttributes: Overlay not found: " + overlayId); return; } overlayView.invalidate(); } /** * Refresh the view */ public void refresh() { requestLayout(); invalidate(); } /** * Invalidate the CAB */ public void invalidateCAB() { if (mOverlayActionMode != null) { mOverlayActionMode.invalidate(); } } @Override protected void onLayout(boolean changed, int l, int t, int r, int b) { final long totalDurationMs = mProject.computeDuration(); final int viewWidth = getWidth() - (2 * mHalfParentWidth); final int leftViewWidth = (Integer)((View)getParent().getParent()).getTag( R.id.left_view_width); long mediaItemStartTimeMs = 0; int left = 0; final int childrenCount = getChildCount(); for (int i = 0; i < childrenCount; i++) { final View view = getChildAt(i); final MovieMediaItem mediaItem = (MovieMediaItem)view.getTag(); if (mediaItem != null) { // Media item final MovieOverlay overlay = mediaItem.getOverlay(); final int right; if (overlay != null) { // Note that this logic matches the one used in ApiService // when handling the OP_MEDIA_ITEM_SET_BOUNDARIES command if (overlay.getAppStartTime() <= mediaItem.getAppBoundaryBeginTime()) { left = leftViewWidth + (int)((mediaItemStartTimeMs * viewWidth) / totalDurationMs); final long durationMs = Math.min(overlay.getAppDuration(), mediaItem.getAppTimelineDuration()); right = leftViewWidth + (int)(((mediaItemStartTimeMs + durationMs) * viewWidth) / totalDurationMs); } else if (overlay.getAppStartTime() + overlay.getAppDuration() > mediaItem.getAppBoundaryEndTime()) { final long startTimeMs = Math.max(mediaItem.getAppBoundaryBeginTime(), mediaItem.getAppBoundaryEndTime() - overlay.getAppDuration()); left = leftViewWidth + (int)(((mediaItemStartTimeMs + startTimeMs - mediaItem.getAppBoundaryBeginTime()) * viewWidth) / totalDurationMs); final long durationMs = mediaItem.getAppBoundaryEndTime() - startTimeMs; right = leftViewWidth + (int)(((mediaItemStartTimeMs + startTimeMs - mediaItem.getAppBoundaryBeginTime() + durationMs) * viewWidth) / totalDurationMs); } else { left = leftViewWidth + (int)(((mediaItemStartTimeMs + overlay.getAppStartTime() - mediaItem.getAppBoundaryBeginTime()) * viewWidth) / totalDurationMs); right = leftViewWidth + (int)(((mediaItemStartTimeMs + overlay.getAppStartTime() - mediaItem.getAppBoundaryBeginTime() + overlay.getAppDuration()) * viewWidth) / totalDurationMs); } } else { left = leftViewWidth + (int)((mediaItemStartTimeMs * viewWidth) / totalDurationMs); right = leftViewWidth + (int)(((mediaItemStartTimeMs + mediaItem.getAppTimelineDuration()) * viewWidth) / totalDurationMs); } view.layout(left, 0, right, b - t); mediaItemStartTimeMs += mediaItem.getAppTimelineDuration(); if (mediaItem.getEndTransition() != null) { mediaItemStartTimeMs -= mediaItem.getEndTransition().getAppDuration(); } left = right; } else if (view == mLeftHandle) { if (mResizingView != null) { view.layout(mResizingView.getLeft() - mHandleWidth, mResizingView.getPaddingTop(), mResizingView.getLeft(), b - t - mResizingView.getPaddingBottom()); } } else if (view == mRightHandle) { if (mResizingView != null) { view.layout(mResizingView.getRight(), mResizingView.getPaddingTop(), mResizingView.getRight() + mHandleWidth, b - t - mResizingView.getPaddingBottom()); } } else if (i == 0) { // Begin view view.layout(left, 0, left + leftViewWidth, b - t); left += leftViewWidth; } else { // End view view.layout(getWidth() - mHalfParentWidth - (mHalfParentWidth - leftViewWidth), 0, getWidth(), b - t); } } mMoveLayoutPending = false; } /** * Create a new dialog * * @param id The dialog id * @param bundle The dialog bundle * @return The dialog */ public Dialog onCreateDialog(int id, final Bundle bundle) { // If the project is not yet loaded do nothing. if (mProject == null) { return null; } switch (id) { case VideoEditorActivity.DIALOG_REMOVE_OVERLAY_ID: { final MovieMediaItem mediaItem = mProject.getMediaItem( bundle.getString(PARAM_DIALOG_MEDIA_ITEM_ID)); if (mediaItem == null) { return null; } final Activity activity = (Activity) getContext(); return AlertDialogs.createAlert(activity, FileUtils.getSimpleName( mediaItem.getFilename()), 0, activity.getString(R.string.editor_remove_overlay_question), activity.getString(R.string.yes), new DialogInterface.OnClickListener() { @Override public void onClick(DialogInterface dialog, int which) { if (mOverlayActionMode != null) { mOverlayActionMode.finish(); mOverlayActionMode = null; } activity.removeDialog(VideoEditorActivity.DIALOG_REMOVE_OVERLAY_ID); ApiService.removeOverlay(activity, mProject.getPath(), mediaItem.getId(), mediaItem.getOverlay().getId()); } }, activity.getString(R.string.no), new DialogInterface.OnClickListener() { @Override public void onClick(DialogInterface dialog, int which) { activity.removeDialog(VideoEditorActivity.DIALOG_REMOVE_OVERLAY_ID); } }, new DialogInterface.OnCancelListener() { @Override public void onCancel(DialogInterface dialog) { activity.removeDialog(VideoEditorActivity.DIALOG_REMOVE_OVERLAY_ID); } }, true); } default: { return null; } } } /** * Find the overlay view with the specified id * * @param mediaItemId The media item id * @return The overlay view */ private View getOverlayView(String mediaItemId) { final int childrenCount = getChildCount(); for (int i = 0; i < childrenCount; i++) { final View childView = getChildAt(i); final MovieMediaItem mediaItem = (MovieMediaItem)childView.getTag(); if (mediaItem != null && mediaItemId.equals(mediaItem.getId())) { return childView; } } return null; } /** * Find the media item view index with the specified id * * @param mediaItemId The media item id * @return The media item view index */ private int getMediaItemViewIndex(String mediaItemId) { final int childrenCount = getChildCount(); for (int i = 0; i < childrenCount; i++) { final View childView = getChildAt(i); final Object tag = childView.getTag(); if (tag != null && tag instanceof MovieMediaItem) { final MovieMediaItem mediaItem = (MovieMediaItem)tag; if (mediaItemId.equals(mediaItem.getId())) { return i; } } } return -1; } /** * Remove all overlay views (leave the beginning and end views) */ private void removeViews() { int index = 0; while (index < getChildCount()) { final MovieMediaItem mediaItem = (MovieMediaItem)getChildAt(index).getTag(); if (mediaItem != null) { removeViewAt(index); } else { index++; } } requestLayout(); } @Override public void setSelected(boolean selected) { if (selected == false) { // Close the contextual action bar if (mOverlayActionMode != null) { mOverlayActionMode.finish(); mOverlayActionMode = null; } mLeftHandle.setVisibility(View.GONE); mLeftHandle.setListener(null); mRightHandle.setVisibility(View.GONE); mRightHandle.setListener(null); mResizingView = null; } final int childrenCount = getChildCount(); for (int i = 0; i < childrenCount; i++) { final View childView = getChildAt(i); childView.setSelected(false); } } /** * Select a view and unselect any view that is selected. * * @param view The view to select * @param selected true if selected */ private void selectView(final View selectedView, boolean selected) { // Check if the selection has changed if (selectedView.isSelected() == selected) { return; } if (selected == false) { // Select the new view selectedView.setSelected(selected); mResizingView = null; mLeftHandle.setVisibility(View.GONE); mLeftHandle.setListener(null); mRightHandle.setVisibility(View.GONE); mRightHandle.setListener(null); return; } // Unselect all other views unselectAllViews(); // Select the new view selectedView.setSelected(selected); final Object tag = selectedView.getTag(); final MovieMediaItem mediaItem = (MovieMediaItem)tag; if (mOverlayActionMode == null) { startActionMode(new OverlayActionModeCallback(mediaItem)); } final MovieOverlay overlay = mediaItem.getOverlay(); final View overlayView = getOverlayView(mediaItem.getId()); mResizingView = overlayView; mRightHandle.setVisibility(View.VISIBLE); mRightHandle.bringToFront(); mRightHandle.setLimitReached(overlay.getAppDuration() <= MediaItemUtils.getMinimumMediaItemDuration(mediaItem), overlay.getAppStartTime() + overlay.getAppDuration() >= mediaItem.getAppBoundaryEndTime()); mRightHandle.setListener(new HandleView.MoveListener() { private MovieMediaItem mMediaItem; private int mMovePosition; private long mMinimumDurationMs; @Override public void onMoveBegin(HandleView view) { mMediaItem = mediaItem; mMinimumDurationMs = MediaItemUtils.getMinimumMediaItemDuration(mediaItem); } @Override public boolean onMove(HandleView view, int left, int delta) { if (mMoveLayoutPending) { return false; } final int position = left + delta; // Compute what will become the width of the view final int newWidth = position - overlayView.getLeft(); // Compute the new duration long newDurationMs = (newWidth * mProject.computeDuration()) / (getWidth() - (2 * mHalfParentWidth)); if (newDurationMs < mMinimumDurationMs) { newDurationMs = mMinimumDurationMs; } else if (overlay.getAppStartTime() + newDurationMs > mMediaItem.getAppBoundaryEndTime()) { newDurationMs = mMediaItem.getAppBoundaryEndTime() - overlay.getAppStartTime(); } mRightHandle.setLimitReached(overlay.getAppDuration() <= mMinimumDurationMs, overlay.getAppStartTime() + overlay.getAppDuration() >= mMediaItem.getAppBoundaryEndTime()); overlay.setAppDuration(newDurationMs); mMovePosition = position; mMoveLayoutPending = true; requestLayout(); return true; } @Override public void onMoveEnd(final HandleView view, final int left, final int delta) { final int position = left + delta; if (mMoveLayoutPending || (position != mMovePosition)) { mHandler.post(new Runnable() { @Override public void run() { if (mMoveLayoutPending) { mHandler.post(this); } else if (position != mMovePosition) { if (onMove(view, left, delta)) { mHandler.post(this); } else { scaleDone(); } } else { scaleDone(); } } }); } else { scaleDone(); } } /** * Scale is done */ public void scaleDone() { ApiService.setOverlayDuration(getContext(), mProject.getPath(), mMediaItem.getId(), overlay.getId(), overlay.getAppDuration()); } }); } /** * Unselect all views */ private void unselectAllViews() { ((RelativeLayout)getParent()).setSelected(false); } }