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