Merge "MediaSession2: Complete transport control commands"
diff --git a/packages/MediaComponents/res/layout/media_controller.xml b/packages/MediaComponents/res/layout/media_controller.xml
index 4d05546..74a8306 100644
--- a/packages/MediaComponents/res/layout/media_controller.xml
+++ b/packages/MediaComponents/res/layout/media_controller.xml
@@ -136,7 +136,13 @@
             android:layout_height="wrap_content"
             android:layout_centerVertical="true"
             android:visibility="gone"
-            android:orientation="horizontal" >
+            android:orientation="horizontal"
+            android:gravity="center">
+
+            <LinearLayout
+                android:id="@+id/custom_buttons"
+                android:layout_width="wrap_content"
+                android:layout_height="wrap_content" />
 
             <ImageButton
                 android:id="@+id/mute"
diff --git a/packages/MediaComponents/src/com/android/widget/MediaControlView2Impl.java b/packages/MediaComponents/src/com/android/widget/MediaControlView2Impl.java
index bc370d8..f96ff70 100644
--- a/packages/MediaComponents/src/com/android/widget/MediaControlView2Impl.java
+++ b/packages/MediaComponents/src/com/android/widget/MediaControlView2Impl.java
@@ -24,11 +24,11 @@
 import android.media.update.ViewProvider;
 import android.os.Bundle;
 import android.util.Log;
-import android.view.KeyEvent;
 import android.view.MotionEvent;
 import android.view.View;
 import android.view.ViewGroup;
 import android.view.accessibility.AccessibilityManager;
+import android.widget.Button;
 import android.widget.ImageButton;
 import android.widget.MediaControlView2;
 import android.widget.ProgressBar;
@@ -40,6 +40,7 @@
 import com.android.media.update.R;
 
 import java.util.Formatter;
+import java.util.List;
 import java.util.Locale;
 
 public class MediaControlView2Impl implements MediaControlView2Provider {
@@ -97,6 +98,7 @@
     private ImageButton mOverflowButtonRight;
 
     private ViewGroup mExtraControls;
+    private ViewGroup mCustomButtons;
     private ImageButton mOverflowButtonLeft;
     private ImageButton mMuteButton;
     private ImageButton mAspectRationButton;
@@ -305,62 +307,11 @@
     }
 
     @Override
-    public boolean onKeyDown_impl(int keyCode, KeyEvent event) {
-        return mSuperProvider.onKeyDown_impl(keyCode, event);
-    }
-
-    @Override
     public void onFinishInflate_impl() {
         mSuperProvider.onFinishInflate_impl();
     }
 
     @Override
-    public boolean dispatchKeyEvent_impl(KeyEvent event) {
-        int keyCode = event.getKeyCode();
-        final boolean uniqueDown = event.getRepeatCount() == 0
-                && event.getAction() == KeyEvent.ACTION_DOWN;
-        if (keyCode == KeyEvent.KEYCODE_HEADSETHOOK
-                || keyCode == KeyEvent.KEYCODE_MEDIA_PLAY_PAUSE
-                || keyCode == KeyEvent.KEYCODE_SPACE) {
-            if (uniqueDown) {
-                togglePausePlayState();
-                mInstance.show(DEFAULT_TIMEOUT_MS);
-                if (mPlayPauseButton != null) {
-                    mPlayPauseButton.requestFocus();
-                }
-            }
-            return true;
-        } else if (keyCode == KeyEvent.KEYCODE_MEDIA_PLAY) {
-            if (uniqueDown && !isPlaying()) {
-                togglePausePlayState();
-                mInstance.show(DEFAULT_TIMEOUT_MS);
-            }
-            return true;
-        } else if (keyCode == KeyEvent.KEYCODE_MEDIA_STOP
-                || keyCode == KeyEvent.KEYCODE_MEDIA_PAUSE) {
-            if (uniqueDown && isPlaying()) {
-                togglePausePlayState();
-                mInstance.show(DEFAULT_TIMEOUT_MS);
-            }
-            return true;
-        } else if (keyCode == KeyEvent.KEYCODE_VOLUME_DOWN
-                || keyCode == KeyEvent.KEYCODE_VOLUME_UP
-                || keyCode == KeyEvent.KEYCODE_VOLUME_MUTE
-                || keyCode == KeyEvent.KEYCODE_CAMERA) {
-            // don't show the controls for volume adjustment
-            return mSuperProvider.dispatchKeyEvent_impl(event);
-        } else if (keyCode == KeyEvent.KEYCODE_BACK || keyCode == KeyEvent.KEYCODE_MENU) {
-            if (uniqueDown) {
-                mInstance.hide();
-            }
-            return true;
-        }
-
-        mInstance.show(DEFAULT_TIMEOUT_MS);
-        return mSuperProvider.dispatchKeyEvent_impl(event);
-    }
-
-    @Override
     public void setEnabled_impl(boolean enabled) {
         if (mPlayPauseButton != null) {
             mPlayPauseButton.setEnabled(enabled);
@@ -501,6 +452,7 @@
 
         // TODO: should these buttons be shown as default?
         mExtraControls = v.findViewById(R.id.extra_controls);
+        mCustomButtons = v.findViewById(R.id.custom_buttons);
         mOverflowButtonLeft = v.findViewById(R.id.overflow_left);
         if (mOverflowButtonLeft != null) {
             mOverflowButtonLeft.setOnClickListener(mOverflowLeftListener);
@@ -875,6 +827,31 @@
                 }
                 mPlaybackActions = newActions;
             }
+
+            // Add buttons if custom actions are present.
+            List<PlaybackState.CustomAction> customActions = mPlaybackState.getCustomActions();
+            mCustomButtons.removeAllViews();
+            if (customActions.size() > 0) {
+                for (PlaybackState.CustomAction action : customActions) {
+                    ImageButton button = new ImageButton(mInstance.getContext(),
+                            null /* AttributeSet */, 0 /* Style */);
+                    // TODO: Apply R.style.BottomBarButton to this button using library context.
+                    // Refer Constructor with argument (int defStyleRes) of View.java
+                    button.setImageResource(action.getIcon());
+                    button.setTooltipText(action.getName());
+                    final String actionString = action.getAction().toString();
+                    button.setOnClickListener(new View.OnClickListener() {
+                        @Override
+                        public void onClick(View v) {
+                            // TODO: Currently, we are just sending extras that came from session.
+                            // Is it the right behavior?
+                            mControls.sendCustomAction(actionString, action.getExtras());
+                            mInstance.show(DEFAULT_TIMEOUT_MS);
+                        }
+                    });
+                    mCustomButtons.addView(button);
+                }
+            }
         }
 
         @Override
diff --git a/packages/MediaComponents/src/com/android/widget/VideoView2Impl.java b/packages/MediaComponents/src/com/android/widget/VideoView2Impl.java
index 6a75c1a..8cd932d 100644
--- a/packages/MediaComponents/src/com/android/widget/VideoView2Impl.java
+++ b/packages/MediaComponents/src/com/android/widget/VideoView2Impl.java
@@ -45,7 +45,6 @@
 import android.util.AttributeSet;
 import android.util.Log;
 import android.view.Gravity;
-import android.view.KeyEvent;
 import android.view.MotionEvent;
 import android.view.View;
 import android.widget.FrameLayout.LayoutParams;
@@ -78,8 +77,8 @@
     private final AudioManager mAudioManager;
     private AudioAttributes mAudioAttributes;
     private int mAudioFocusType = AudioManager.AUDIOFOCUS_GAIN; // legacy focus gain
-    private int mAudioSession;
 
+    private VideoView2.OnCustomActionListener mOnCustomActionListener;
     private VideoView2.OnPreparedListener mOnPreparedListener;
     private VideoView2.OnCompletionListener mOnCompletionListener;
     private VideoView2.OnErrorListener mOnErrorListener;
@@ -99,6 +98,7 @@
     private String mTitle;
 
     private PlaybackState.Builder mStateBuilder;
+    private List<PlaybackState.CustomAction> mCustomActionList;
     private int mTargetState = STATE_IDLE;
     private int mCurrentState = STATE_IDLE;
     private int mCurrentBufferPercentage;
@@ -192,16 +192,6 @@
     }
 
     @Override
-    public int getAudioSessionId_impl() {
-        if (mAudioSession == 0) {
-            MediaPlayer foo = new MediaPlayer();
-            mAudioSession = foo.getAudioSessionId();
-            foo.release();
-        }
-        return mAudioSession;
-    }
-
-    @Override
     public void showSubtitle_impl() {
         // Retrieve all tracks that belong to the current video.
         MediaPlayer.TrackInfo[] trackInfos = mMediaPlayer.getTrackInfo();
@@ -232,7 +222,7 @@
     @Override
     public void setFullScreen_impl(boolean fullScreen) {
         if (mOnFullScreenChangedListener != null) {
-            mOnFullScreenChangedListener.onFullScreenChanged(fullScreen);
+            mOnFullScreenChangedListener.onFullScreenChanged(mInstance, fullScreen);
         }
     }
 
@@ -277,16 +267,16 @@
 
     @Override
     public void setVideoPath_impl(String path) {
-        mInstance.setVideoURI(Uri.parse(path));
+        mInstance.setVideoUri(Uri.parse(path));
     }
 
     @Override
-    public void setVideoURI_impl(Uri uri) {
-        mInstance.setVideoURI(uri, null);
+    public void setVideoUri_impl(Uri uri) {
+        mInstance.setVideoUri(uri, null);
     }
 
     @Override
-    public void setVideoURI_impl(Uri uri, Map<String, String> headers) {
+    public void setVideoUri_impl(Uri uri, Map<String, String> headers) {
         mSeekWhenPrepared = 0;
         openVideo(uri, headers);
     }
@@ -317,6 +307,17 @@
     }
 
     @Override
+    public void setCustomActions_impl(List<PlaybackState.CustomAction> actionList,
+            VideoView2.OnCustomActionListener listener) {
+        mCustomActionList = actionList;
+        mOnCustomActionListener = listener;
+
+        // Create a new playback builder in order to clear existing the custom actions.
+        mStateBuilder = null;
+        updatePlaybackState();
+    }
+
+    @Override
     public void setOnPreparedListener_impl(VideoView2.OnPreparedListener l) {
         mOnPreparedListener = l;
     }
@@ -363,6 +364,7 @@
         mSuperProvider.onDetachedFromWindow_impl();
         mMediaSession.release();
         mMediaSession = null;
+        mMediaController = null;
     }
 
     @Override
@@ -393,58 +395,11 @@
     }
 
     @Override
-    public boolean onKeyDown_impl(int keyCode, KeyEvent event) {
-        Log.v(TAG, "onKeyDown_impl: " + keyCode);
-        boolean isKeyCodeSupported = keyCode != KeyEvent.KEYCODE_BACK
-                && keyCode != KeyEvent.KEYCODE_VOLUME_UP
-                && keyCode != KeyEvent.KEYCODE_VOLUME_DOWN
-                && keyCode != KeyEvent.KEYCODE_VOLUME_MUTE
-                && keyCode != KeyEvent.KEYCODE_MENU
-                && keyCode != KeyEvent.KEYCODE_CALL
-                && keyCode != KeyEvent.KEYCODE_ENDCALL;
-        if (isInPlaybackState() && isKeyCodeSupported && mMediaControlView != null) {
-            if (keyCode == KeyEvent.KEYCODE_HEADSETHOOK
-                    || keyCode == KeyEvent.KEYCODE_MEDIA_PLAY_PAUSE) {
-                if (mMediaPlayer.isPlaying()) {
-                    mMediaController.getTransportControls().pause();
-                    mMediaControlView.show();
-                } else {
-                    mMediaController.getTransportControls().play();
-                    mMediaControlView.hide();
-                }
-                return true;
-            } else if (keyCode == KeyEvent.KEYCODE_MEDIA_PLAY) {
-                if (!mMediaPlayer.isPlaying()) {
-                    mMediaController.getTransportControls().play();
-                    mMediaControlView.hide();
-                }
-                return true;
-            } else if (keyCode == KeyEvent.KEYCODE_MEDIA_STOP
-                    || keyCode == KeyEvent.KEYCODE_MEDIA_PAUSE) {
-                if (mMediaPlayer.isPlaying()) {
-                    mMediaController.getTransportControls().pause();
-                    mMediaControlView.show();
-                }
-                return true;
-            } else {
-                toggleMediaControlViewVisibility();
-            }
-        }
-
-        return mSuperProvider.onKeyDown_impl(keyCode, event);
-    }
-
-    @Override
     public void onFinishInflate_impl() {
         mSuperProvider.onFinishInflate_impl();
     }
 
     @Override
-    public boolean dispatchKeyEvent_impl(KeyEvent event) {
-        return mSuperProvider.dispatchKeyEvent_impl(event);
-    }
-
-    @Override
     public void setEnabled_impl(boolean enabled) {
         mSuperProvider.setEnabled_impl(enabled);
     }
@@ -492,7 +447,7 @@
         }
         mCurrentView = view;
         if (mOnViewTypeChangedListener != null) {
-            mOnViewTypeChangedListener.onViewTypeChanged(view.getViewType());
+            mOnViewTypeChangedListener.onViewTypeChanged(mInstance, view.getViewType());
         }
         if (needToStart()) {
             mMediaController.getTransportControls().play();
@@ -554,12 +509,6 @@
             controller.registerRenderer(new Cea708CaptionRenderer(context));
             controller.registerRenderer(new ClosedCaptionRenderer(context));
             mMediaPlayer.setSubtitleAnchor(controller, (SubtitleController.Anchor) mSubtitleView);
-
-            if (mAudioSession != 0) {
-                mMediaPlayer.setAudioSessionId(mAudioSession);
-            } else {
-                mAudioSession = mMediaPlayer.getAudioSessionId();
-            }
             mMediaPlayer.setOnPreparedListener(mPreparedListener);
             mMediaPlayer.setOnVideoSizeChangedListener(mSizeChangedListener);
             mMediaPlayer.setOnCompletionListener(mCompletionListener);
@@ -658,8 +607,12 @@
             }
             mStateBuilder = new PlaybackState.Builder();
             mStateBuilder.setActions(playbackActions);
-            mStateBuilder.addCustomAction(MediaControlView2Impl.COMMAND_SHOW_SUBTITLE, null, -1);
-            mStateBuilder.addCustomAction(MediaControlView2Impl.COMMAND_HIDE_SUBTITLE, null, -1);
+
+            if (mCustomActionList != null) {
+                for (PlaybackState.CustomAction action : mCustomActionList) {
+                    mStateBuilder.addCustomAction(action);
+                }
+            }
         }
         mStateBuilder.setState(getCorrespondingPlaybackState(),
                 mMediaPlayer.getCurrentPosition(), mSpeed);
@@ -752,7 +705,7 @@
             }
             mCurrentState = STATE_PREPARED;
             if (mOnPreparedListener != null) {
-                mOnPreparedListener.onPrepared();
+                mOnPreparedListener.onPrepared(mInstance);
             }
             if (mMediaControlView != null) {
                 mMediaControlView.setEnabled(true);
@@ -827,7 +780,7 @@
                     updatePlaybackState();
 
                     if (mOnCompletionListener != null) {
-                        mOnCompletionListener.onCompletion();
+                        mOnCompletionListener.onCompletion(mInstance);
                     }
                     if (mAudioFocusType != AudioManager.AUDIOFOCUS_NONE) {
                         mAudioManager.abandonAudioFocus(null);
@@ -839,7 +792,7 @@
             new MediaPlayer.OnInfoListener() {
                 public boolean onInfo(MediaPlayer mp, int what, int extra) {
                     if (mOnInfoListener != null) {
-                        mOnInfoListener.onInfo(what, extra);
+                        mOnInfoListener.onInfo(mInstance, what, extra);
                     }
                     return true;
                 }
@@ -861,7 +814,7 @@
 
                     /* If an error handler has been supplied, use it and finish. */
                     if (mOnErrorListener != null) {
-                        if (mOnErrorListener.onError(frameworkErr, implErr)) {
+                        if (mOnErrorListener.onError(mInstance, frameworkErr, implErr)) {
                             return true;
                         }
                     }
@@ -892,7 +845,7 @@
                                                 * at least inform them that the video is over.
                                                 */
                                                 if (mOnCompletionListener != null) {
-                                                    mOnCompletionListener.onCompletion();
+                                                    mOnCompletionListener.onCompletion(mInstance);
                                                 }
                                             }
                                         })
@@ -929,6 +882,11 @@
         }
 
         @Override
+        public void onCustomAction(String action, Bundle extras) {
+            mOnCustomActionListener.onCustomAction(action, extras);
+        }
+
+        @Override
         public void onPlay() {
             if (isInPlaybackState() && mCurrentView.hasAvailableSurface()) {
                 applySpeed();