• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2010 The Android Open Source Project
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *      http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 
17 package com.android.videoeditor.widgets;
18 
19 import java.util.List;
20 
21 import android.app.Activity;
22 import android.app.Dialog;
23 import android.content.Context;
24 import android.content.DialogInterface;
25 import android.os.Bundle;
26 import android.util.AttributeSet;
27 import android.util.Log;
28 import android.view.ActionMode;
29 import android.view.Display;
30 import android.view.Menu;
31 import android.view.MenuItem;
32 import android.view.MotionEvent;
33 import android.view.View;
34 import android.widget.LinearLayout;
35 import android.widget.RelativeLayout;
36 import android.widget.SeekBar;
37 import android.widget.TextView;
38 
39 import com.android.videoeditor.AlertDialogs;
40 import com.android.videoeditor.VideoEditorActivity;
41 import com.android.videoeditor.service.ApiService;
42 import com.android.videoeditor.service.MovieAudioTrack;
43 import com.android.videoeditor.service.VideoEditorProject;
44 import com.android.videoeditor.util.FileUtils;
45 import com.android.videoeditor.R;
46 
47 /**
48  * The LinearLayout which displays audio tracks
49  */
50 public class AudioTrackLinearLayout extends LinearLayout {
51     // Logging
52     private static final String TAG = "AudioTrackLinearLayout";
53 
54     // Dialog parameter ids
55     private static final String PARAM_DIALOG_AUDIO_TRACK_ID = "audio_track_id";
56 
57     // Instance variables
58     private final ItemSimpleGestureListener mAudioTrackGestureListener;
59     private final int mAudioTrackHeight;
60     private final int mHalfParentWidth;
61     private final View mAddAudioTrackButtonView;
62     private final int mAddAudioTrackButtonWidth;
63     private AudioTracksLayoutListener mListener;
64     private ActionMode mAudioTrackActionMode;
65     private VideoEditorProject mProject;
66     private boolean mPlaybackInProgress;
67     private long mTimelineDurationMs;
68 
69     /**
70      * Activity listener
71      */
72     public interface AudioTracksLayoutListener {
73         /**
74          * Add an audio track
75          */
onAddAudioTrack()76         public void onAddAudioTrack();
77     }
78 
79     /**
80      * The audio track action mode handler
81      */
82     private class AudioTrackActionModeCallback implements ActionMode.Callback,
83             SeekBar.OnSeekBarChangeListener {
84         // Instance variables
85         private final MovieAudioTrack mAudioTrack;
86 
87         /**
88          * Constructor
89          *
90          * @param audioTrack The audio track
91          */
AudioTrackActionModeCallback(MovieAudioTrack audioTrack)92         public AudioTrackActionModeCallback(MovieAudioTrack audioTrack) {
93             mAudioTrack = audioTrack;
94         }
95 
96         @Override
onCreateActionMode(ActionMode mode, Menu menu)97         public boolean onCreateActionMode(ActionMode mode, Menu menu) {
98             mAudioTrackActionMode = mode;
99 
100             mode.getMenuInflater().inflate(R.menu.audio_mode_menu, menu);
101 
102             final View titleBarView = inflate(getContext(), R.layout.audio_track_action_bar, null);
103 
104             mode.setCustomView(titleBarView);
105 
106             final TextView titleView = (TextView)titleBarView.findViewById(R.id.action_bar_title);
107             titleView.setText(FileUtils.getSimpleName(mAudioTrack.getFilename()));
108 
109             final SeekBar seekBar =
110                 ((SeekBar)titleBarView.findViewById(R.id.action_volume));
111             seekBar.setOnSeekBarChangeListener(this);
112             seekBar.setProgress(mAudioTrack.getAppVolume());
113 
114             return true;
115         }
116 
117         @Override
onPrepareActionMode(ActionMode mode, Menu menu)118         public boolean onPrepareActionMode(ActionMode mode, Menu menu) {
119             MenuItem duckItem = menu.findItem(R.id.action_duck);
120             duckItem.setChecked(mAudioTrack.isAppDuckingEnabled());
121             return true;
122         }
123 
124         @Override
onActionItemClicked(ActionMode mode, MenuItem item)125         public boolean onActionItemClicked(ActionMode mode, MenuItem item) {
126             switch (item.getItemId()) {
127                 case R.id.action_duck: {
128                     final boolean duck = !mAudioTrack.isAppDuckingEnabled();
129                     mAudioTrack.enableAppDucking(duck);
130                     ApiService.setAudioTrackDuck(getContext(), mProject.getPath(),
131                             mAudioTrack.getId(), duck);
132                     item.setChecked(duck);
133                     break;
134                 }
135 
136                 case R.id.action_remove_audio_track: {
137                     final Bundle bundle = new Bundle();
138                     bundle.putString(PARAM_DIALOG_AUDIO_TRACK_ID, mAudioTrack.getId());
139                     ((Activity)getContext()).showDialog(
140                             VideoEditorActivity.DIALOG_REMOVE_AUDIO_TRACK_ID, bundle);
141                     break;
142                 }
143             }
144             return true;
145         }
146 
147         @Override
onProgressChanged(SeekBar seekBar, int progress, boolean fromUser)148         public void onProgressChanged(SeekBar seekBar, int progress, boolean fromUser) {
149             if (fromUser) {
150                 mAudioTrack.setAppVolume(progress);
151                 ApiService.setAudioTrackVolume(getContext(), mProject.getPath(),
152                         mAudioTrack.getId(), progress);
153             }
154         }
155 
156         @Override
onStartTrackingTouch(SeekBar seekBar)157         public void onStartTrackingTouch(SeekBar seekBar) {
158         }
159 
160         @Override
onStopTrackingTouch(SeekBar seekBar)161         public void onStopTrackingTouch(SeekBar seekBar) {
162         }
163 
164         @Override
onDestroyActionMode(ActionMode mode)165         public void onDestroyActionMode(ActionMode mode) {
166             final View audioTrackView = getAudioTrackView(mAudioTrack.getId());
167             if (audioTrackView != null) {
168                 selectView(audioTrackView, false);
169             }
170 
171             mAudioTrackActionMode = null;
172         }
173     }
174 
AudioTrackLinearLayout(Context context, AttributeSet attrs, int defStyle)175     public AudioTrackLinearLayout(Context context, AttributeSet attrs, int defStyle) {
176         super(context, attrs, defStyle);
177 
178         mAudioTrackGestureListener = new ItemSimpleGestureListener() {
179             @Override
180             public boolean onSingleTapConfirmed(View view, int area, MotionEvent e) {
181                 if (mPlaybackInProgress) {
182                     return false;
183                 }
184 
185                 if (!view.isSelected()) {
186                     selectView(view, true);
187                 }
188 
189                 return true;
190             }
191 
192             @Override
193             public void onLongPress(View view, MotionEvent e) {
194                 if (mPlaybackInProgress) {
195                     return;
196                 }
197 
198                 if (!view.isSelected()) {
199                     selectView(view, true);
200                 }
201 
202                 if (mAudioTrackActionMode == null) {
203                     startActionMode(new AudioTrackActionModeCallback(
204                             (MovieAudioTrack)view.getTag()));
205                 }
206             }
207         };
208 
209         // Add the beginning timeline item
210         final View beginView = inflate(getContext(), R.layout.empty_timeline_item, null);
211         beginView.setOnClickListener(new View.OnClickListener() {
212             @Override
213             public void onClick(View view) {
214                 unselectAllViews();
215             }
216         });
217         addView(beginView);
218 
219         // Add the end timeline item
220         final View endView = inflate(context, R.layout.empty_timeline_item, null);
221         endView.setOnClickListener(new View.OnClickListener() {
222             @Override
223             public void onClick(View view) {
224                 unselectAllViews();
225             }
226         });
227         addView(endView);
228 
229         // Add the audio track button
230         mAddAudioTrackButtonView = inflate(getContext(), R.layout.add_audio_track_button, null);
231         addView(mAddAudioTrackButtonView, 1);
232         mAddAudioTrackButtonView.setOnClickListener(new View.OnClickListener() {
233             @Override
234             public void onClick(View view) {
235                 if (mListener != null) {
236                     mListener.onAddAudioTrack();
237                 }
238             }
239         });
240         mAddAudioTrackButtonWidth = (int)context.getResources().getDimension(
241                 R.dimen.add_audio_track_button_width);
242 
243         // Compute half the width of the screen (and therefore the parent view)
244         final Display display = ((Activity)context).getWindowManager().getDefaultDisplay();
245         mHalfParentWidth = display.getWidth() / 2;
246 
247         // Get the layout height
248         mAudioTrackHeight = (int)context.getResources().getDimension(R.dimen.audio_layout_height);
249 
250         setMotionEventSplittingEnabled(false);
251     }
252 
AudioTrackLinearLayout(Context context, AttributeSet attrs)253     public AudioTrackLinearLayout(Context context, AttributeSet attrs) {
254         this(context, attrs, 0);
255     }
256 
AudioTrackLinearLayout(Context context)257     public AudioTrackLinearLayout(Context context) {
258         this(context, null, 0);
259     }
260 
261     /**
262      * The activity was resumed
263      */
onResume()264     public void onResume() {
265         final int childrenCount = getChildCount();
266         for (int i = 0; i < childrenCount; i++) {
267             final View childView = getChildAt(i);
268             final Object tag = childView.getTag();
269             if (tag != null) { // This view represents an audio track
270                 final AudioTrackView audioTrackView = (AudioTrackView)childView;
271                 if (audioTrackView.getWaveformData() == null) {
272                     final MovieAudioTrack audioTrack = (MovieAudioTrack)tag;
273                     if (audioTrack.getWaveformData() != null) {
274                         audioTrackView.setWaveformData(audioTrack.getWaveformData());
275                         audioTrackView.invalidate();
276                     }
277                 }
278             }
279         }
280     }
281 
282     /**
283      * @param listener The listener
284      */
setListener(AudioTracksLayoutListener listener)285     public void setListener(AudioTracksLayoutListener listener) {
286         mListener = listener;
287     }
288 
289     /**
290      * @param project The project
291      */
setProject(VideoEditorProject project)292     public void setProject(VideoEditorProject project) {
293         // Close the contextual action bar
294         if (mAudioTrackActionMode != null) {
295             mAudioTrackActionMode.finish();
296             mAudioTrackActionMode = null;
297         }
298 
299         mProject = project;
300 
301         updateAddAudioTrackButton();
302 
303         removeAudioTrackViews();
304     }
305 
306     /**
307      * @param inProgress true if playback is in progress
308      */
setPlaybackInProgress(boolean inProgress)309     public void setPlaybackInProgress(boolean inProgress) {
310         mPlaybackInProgress = inProgress;
311 
312         // Don't allow the user to interact with the audio tracks while playback
313         // is in progress
314         if (inProgress && mAudioTrackActionMode != null) {
315             mAudioTrackActionMode.finish();
316             mAudioTrackActionMode = null;
317         }
318     }
319 
320     /**
321      * Add the audio tracks
322      *
323      * @param audioTracks The audio tracks
324      */
addAudioTracks(List<MovieAudioTrack> audioTracks)325     public void addAudioTracks(List<MovieAudioTrack> audioTracks) {
326         if (mAudioTrackActionMode != null) {
327             mAudioTrackActionMode.finish();
328             mAudioTrackActionMode = null;
329         }
330 
331         updateAddAudioTrackButton();
332 
333         removeAudioTrackViews();
334 
335         mTimelineDurationMs = mProject.computeDuration();
336 
337         for (MovieAudioTrack audioTrack : audioTracks) {
338             addAudioTrack(audioTrack);
339         }
340     }
341 
342     /**
343      * Add a new audio track
344      *
345      * @param audioTrack The audio track
346      *
347      * @return The view that was added
348      */
addAudioTrack(MovieAudioTrack audioTrack)349     public View addAudioTrack(MovieAudioTrack audioTrack) {
350         updateAddAudioTrackButton();
351 
352         final AudioTrackView audioTrackView = (AudioTrackView)inflate(getContext(),
353                 R.layout.audio_track_item, null);
354 
355         audioTrackView.setTag(audioTrack);
356 
357         audioTrackView.setGestureListener(mAudioTrackGestureListener);
358 
359         audioTrackView.updateTimelineDuration(mTimelineDurationMs);
360 
361         if (audioTrack.getWaveformData() != null) {
362             audioTrackView.setWaveformData(audioTrack.getWaveformData());
363         } else {
364             ApiService.extractAudioTrackAudioWaveform(getContext(), mProject.getPath(),
365                     audioTrack.getId());
366         }
367 
368         final LinearLayout.LayoutParams lp = new LinearLayout.LayoutParams(
369                 LinearLayout.LayoutParams.WRAP_CONTENT, LinearLayout.LayoutParams.FILL_PARENT);
370         addView(audioTrackView, getChildCount() - 1, lp);
371 
372         if (mAudioTrackActionMode != null) {
373             mAudioTrackActionMode.invalidate();
374         }
375 
376         requestLayout();
377         return audioTrackView;
378     }
379 
380     /**
381      * Remove an audio track
382      *
383      * @param audioTrackId The audio track id
384      * @return The view which was removed
385      */
removeAudioTrack(String audioTrackId)386     public View removeAudioTrack(String audioTrackId) {
387         final int childrenCount = getChildCount();
388         for (int i = 0; i < childrenCount; i++) {
389             final View childView = getChildAt(i);
390             final MovieAudioTrack audioTrack = (MovieAudioTrack)childView.getTag();
391             if (audioTrack != null && audioTrack.getId().equals(audioTrackId)) {
392                 removeViewAt(i);
393 
394                 updateAddAudioTrackButton();
395 
396                 requestLayout();
397                 return childView;
398             }
399         }
400 
401         return null;
402     }
403 
404     /**
405      * Update the audio track item
406      *
407      * @param audioTrackId The audio track id
408      */
updateAudioTrack(String audioTrackId)409     public void updateAudioTrack(String audioTrackId) {
410         final AudioTrackView audioTrackView = (AudioTrackView)getAudioTrackView(audioTrackId);
411         if (audioTrackView == null) {
412             Log.e(TAG, "updateAudioTrack: audio track view not found: " + audioTrackId);
413             return;
414         }
415 
416         if (mAudioTrackActionMode != null) {
417             mAudioTrackActionMode.invalidate();
418         }
419 
420         requestLayout();
421         invalidate();
422     }
423 
424     /**
425      * An audio track is being decoded
426      *
427      * @param audioTrackId The audio track id
428      * @param action The action
429      * @param progress The progress
430      */
onGeneratePreviewProgress(String audioTrackId, int action, int progress)431     public void onGeneratePreviewProgress(String audioTrackId, int action, int progress) {
432         final AudioTrackView audioTrackView = (AudioTrackView)getAudioTrackView(audioTrackId);
433         if (audioTrackView == null) {
434             Log.e(TAG, "onGeneratePreviewProgress: audio track view not found: " + audioTrackId);
435             return;
436         }
437 
438         audioTrackView.setProgress(progress);
439     }
440 
441     /**
442      * Set the waveform progress
443      *
444      * @param audioTrackId The audio track id
445      * @param progress The progress
446      */
setWaveformExtractionProgress(String audioTrackId, int progress)447     public void setWaveformExtractionProgress(String audioTrackId, int progress) {
448         final AudioTrackView audioTrackView = (AudioTrackView)getAudioTrackView(audioTrackId);
449         if (audioTrackView == null) {
450             Log.e(TAG, "setWaveformExtractionProgress: audio track view not found: "
451                     + audioTrackId);
452             return;
453         }
454 
455         audioTrackView.setProgress(progress);
456     }
457 
458     /**
459      * The waveform extraction is complete
460      *
461      * @param audioTrackId The audio track id
462      */
setWaveformExtractionComplete(String audioTrackId)463     public void setWaveformExtractionComplete(String audioTrackId) {
464         final AudioTrackView audioTrackView = (AudioTrackView)getAudioTrackView(audioTrackId);
465         if (audioTrackView == null) {
466             Log.e(TAG, "setWaveformExtractionComplete: audio track view not found: "
467                     + audioTrackId);
468             return;
469         }
470 
471         audioTrackView.setProgress(-1);
472 
473         final MovieAudioTrack audioTrack = (MovieAudioTrack)audioTrackView.getTag();
474         if (audioTrack.getWaveformData() != null) {
475             audioTrackView.setWaveformData(audioTrack.getWaveformData());
476         }
477 
478         requestLayout();
479         invalidate();
480     }
481 
482     /**
483      * The timeline duration has changed. Refresh the view.
484      */
updateTimelineDuration()485     public void updateTimelineDuration() {
486         mTimelineDurationMs = mProject.computeDuration();
487 
488         // Media items may had been added or removed
489         updateAddAudioTrackButton();
490 
491         // Update the project duration for all views
492         final int childrenCount = getChildCount();
493         for (int i = 0; i < childrenCount; i++) {
494             final View childView = getChildAt(i);
495             final MovieAudioTrack audioTrack = (MovieAudioTrack)childView.getTag();
496             if (audioTrack != null) {
497                 ((AudioTrackView)childView).updateTimelineDuration(mTimelineDurationMs);
498             }
499         }
500 
501         requestLayout();
502         invalidate();
503     }
504 
505     @Override
onLayout(boolean changed, int l, int t, int r, int b)506     protected void onLayout(boolean changed, int l, int t, int r, int b) {
507         final int childrenCount = getChildCount();
508         if (mTimelineDurationMs == 0) {
509             int left = 0;
510             for (int i = 0; i < childrenCount; i++) {
511                 final View childView = getChildAt(i);
512                 final MovieAudioTrack audioTrack = (MovieAudioTrack)childView.getTag();
513                 if (audioTrack != null) {
514                     // Audio tracks are not visible
515                     childView.layout(left, 0, left, mAudioTrackHeight);
516                 } else { // Beginning and end views
517                     childView.layout(left, 0, left + mHalfParentWidth, mAudioTrackHeight);
518                     left += mHalfParentWidth;
519                 }
520             }
521         } else {
522             final int viewWidth = getWidth() - (2 * mHalfParentWidth);
523             int left = 0;
524 
525             final int leftViewWidth = (Integer)((View)getParent().getParent()).getTag(
526                     R.id.left_view_width);
527 
528             for (int i = 0; i < childrenCount; i++) {
529                 final View childView = getChildAt(i);
530                 final int id = childView.getId();
531                 final MovieAudioTrack audioTrack = (MovieAudioTrack)childView.getTag();
532                 if (audioTrack != null) { // Audio track views
533                     final int width;
534                     if (audioTrack.isAppLooping()) {
535                         width = (int)((mTimelineDurationMs -
536                                 audioTrack.getAppStartTime()) * viewWidth / mTimelineDurationMs);
537                     } else {
538                         if (audioTrack.getAppStartTime() + audioTrack.getTimelineDuration() >
539                                 mTimelineDurationMs) {
540                             width = (int)((mTimelineDurationMs -
541                                 audioTrack.getAppStartTime()) * viewWidth / mTimelineDurationMs);
542                         } else {
543                             width = (int)(audioTrack.getTimelineDuration() * viewWidth /
544                                     mTimelineDurationMs);
545                         }
546                     }
547 
548                     final int trackLeft =
549                         (int)((audioTrack.getAppStartTime() * viewWidth) / mTimelineDurationMs) +
550                             leftViewWidth;
551                     childView.layout(trackLeft, 0, trackLeft + width, mAudioTrackHeight);
552                     left = trackLeft + width;
553                 } else if (id == R.id.add_audio_track_button) {
554                     if (childView.getVisibility() == View.VISIBLE) {
555                         childView.layout(left, 0, left + mAddAudioTrackButtonWidth,
556                                 mAudioTrackHeight);
557                         left += mAddAudioTrackButtonWidth;
558                     }
559                 } else if (i == 0) { // Begin view
560                     childView.layout(left, 0, left + leftViewWidth, mAudioTrackHeight);
561                     left += leftViewWidth;
562                 } else { // End view
563                     childView.layout(left, 0, getWidth(), mAudioTrackHeight);
564                 }
565             }
566         }
567     }
568 
569     /**
570      * Create a new dialog
571      *
572      * @param id The dialog id
573      * @param bundle The dialog bundle
574      *
575      * @return The dialog
576      */
onCreateDialog(int id, final Bundle bundle)577     public Dialog onCreateDialog(int id, final Bundle bundle) {
578         // If the project is not yet loaded do nothing.
579         if (mProject == null) {
580             return null;
581         }
582 
583         switch (id) {
584             case VideoEditorActivity.DIALOG_REMOVE_AUDIO_TRACK_ID: {
585                 final MovieAudioTrack audioTrack = mProject.getAudioTrack(
586                         bundle.getString(PARAM_DIALOG_AUDIO_TRACK_ID));
587                 if (audioTrack == null) {
588                     return null;
589                 }
590 
591                 final Activity activity = (Activity)getContext();
592                 return AlertDialogs.createAlert(activity,
593                         FileUtils.getSimpleName(audioTrack.getFilename()), 0,
594                         activity.getString(R.string.editor_remove_audio_track_question),
595                         activity.getString(R.string.yes),
596                         new DialogInterface.OnClickListener() {
597                     @Override
598                     public void onClick(DialogInterface dialog, int which) {
599                         if (mAudioTrackActionMode != null) {
600                             mAudioTrackActionMode.finish();
601                             mAudioTrackActionMode = null;
602                         }
603                         activity.removeDialog(VideoEditorActivity.DIALOG_REMOVE_AUDIO_TRACK_ID);
604 
605                         ApiService.removeAudioTrack(activity, mProject.getPath(),
606                                 audioTrack.getId());
607                     }
608                 }, activity.getString(R.string.no), new DialogInterface.OnClickListener() {
609                     @Override
610                     public void onClick(DialogInterface dialog, int which) {
611                         activity.removeDialog(VideoEditorActivity.DIALOG_REMOVE_AUDIO_TRACK_ID);
612                     }
613                 }, new DialogInterface.OnCancelListener() {
614                     @Override
615                     public void onCancel(DialogInterface dialog) {
616                         activity.removeDialog(VideoEditorActivity.DIALOG_REMOVE_AUDIO_TRACK_ID);
617                     }
618                 }, true);
619             }
620 
621             default: {
622                 return null;
623             }
624         }
625     }
626 
627     /**
628      * Find the audio track view with the specified id
629      *
630      * @param audioTrackId The audio track id
631      * @return The audio track view
632      */
633     private View getAudioTrackView(String audioTrackId) {
634         final int childrenCount = getChildCount();
635         for (int i = 0; i < childrenCount; i++) {
636             final View childView = getChildAt(i);
637             final MovieAudioTrack audioTrack = (MovieAudioTrack)childView.getTag();
638             if (audioTrack != null && audioTrackId.equals(audioTrack.getId())) {
639                 return childView;
640             }
641         }
642 
643         return null;
644     }
645 
646     /**
647      * Remove all audio track views (leave the beginning and end views)
648      */
649     private void removeAudioTrackViews() {
650         int index = 0;
651         while (index < getChildCount()) {
652             final Object tag = getChildAt(index).getTag();
653             if (tag != null) {
654                 removeViewAt(index);
655             } else {
656                 index++;
657             }
658         }
659 
660         requestLayout();
661     }
662 
663     /**
664      * Set the background of the begin view
665      */
666     private void updateAddAudioTrackButton() {
667         if (mProject == null) { // No project
668             mAddAudioTrackButtonView.setVisibility(View.GONE);
669         } else if (mProject.getMediaItemCount() > 0) {
670             if (mProject.getAudioTracks().size() > 0) {
671                 mAddAudioTrackButtonView.setVisibility(View.GONE);
672             } else {
673                 mAddAudioTrackButtonView.setVisibility(View.VISIBLE);
674             }
675         } else { // No media items
676             mAddAudioTrackButtonView.setVisibility(View.GONE);
677         }
678     }
679 
680     @Override
681     public void setSelected(boolean selected) {
682         if (selected == false) {
683             // Close the contextual action bar
684             if (mAudioTrackActionMode != null) {
685                 mAudioTrackActionMode.finish();
686                 mAudioTrackActionMode = null;
687             }
688         }
689 
690         final int childrenCount = getChildCount();
691         for (int i = 0; i < childrenCount; i++) {
692             final View childView = getChildAt(i);
693             childView.setSelected(false);
694         }
695     }
696 
697     /**
698      * Select a view and unselect any view that is selected.
699      *
700      * @param selectedView The view to select
701      * @param selected true if selected
702      */
703     private void selectView(View selectedView, boolean selected) {
704         // Check if the selection has changed
705         if (selectedView.isSelected() == selected) {
706             return;
707         }
708 
709         if (selected) {
710             unselectAllViews();
711         }
712 
713         if (selected && mAudioTrackActionMode == null) {
714             startActionMode(new AudioTrackActionModeCallback(
715                     (MovieAudioTrack)selectedView.getTag()));
716         }
717 
718         // Select the new view
719         selectedView.setSelected(selected);
720     }
721 
722     /**
723      * Unselect all views
724      */
725     private void unselectAllViews() {
726         ((RelativeLayout)getParent()).setSelected(false);
727     }
728 }
729