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 
     87         /**
     88          * Constructor
     89          *
     90          * @param audioTrack The audio track
     91          */
     92         public AudioTrackActionModeCallback(MovieAudioTrack audioTrack) {
     93             mAudioTrack = audioTrack;
     94         }
     95 
     96         @Override
     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
    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
    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
    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
    157         public void onStartTrackingTouch(SeekBar seekBar) {
    158         }
    159 
    160         @Override
    161         public void onStopTrackingTouch(SeekBar seekBar) {
    162         }
    163 
    164         @Override
    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 
    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 
    253     public AudioTrackLinearLayout(Context context, AttributeSet attrs) {
    254         this(context, attrs, 0);
    255     }
    256 
    257     public AudioTrackLinearLayout(Context context) {
    258         this(context, null, 0);
    259     }
    260 
    261     /**
    262      * The activity was resumed
    263      */
    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      */
    285     public void setListener(AudioTracksLayoutListener listener) {
    286         mListener = listener;
    287     }
    288 
    289     /**
    290      * @param project The project
    291      */
    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      */
    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      */
    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      */
    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      */
    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      */
    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      */
    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      */
    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      */
    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      */
    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
    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      */
    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