Home | History | Annotate | Download | only in widgets
      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          */
     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          */
     93         public AudioTrackActionModeCallback(MovieAudioTrack audioTrack) {
     94             mAudioTrack = audioTrack;
     95         }
     96 
     97         @Override
     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
    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
    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
    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
    158         public void onStartTrackingTouch(SeekBar seekBar) {
    159         }
    160 
    161         @Override
    162         public void onStopTrackingTouch(SeekBar seekBar) {
    163             ApiService.setAudioTrackVolume(getContext(), mProject.getPath(),
    164                     mAudioTrack.getId(), mProgress);
    165         }
    166 
    167         @Override
    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 
    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 
    256     public AudioTrackLinearLayout(Context context, AttributeSet attrs) {
    257         this(context, attrs, 0);
    258     }
    259 
    260     public AudioTrackLinearLayout(Context context) {
    261         this(context, null, 0);
    262     }
    263 
    264     /**
    265      * The activity was resumed
    266      */
    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      */
    288     public void setListener(AudioTracksLayoutListener listener) {
    289         mListener = listener;
    290     }
    291 
    292     /**
    293      * @param project The project
    294      */
    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      */
    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      */
    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      */
    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      */
    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      */
    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      */
    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      */
    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      */
    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      */
    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
    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      */
    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