Merge "Codec2: fix API style for some helpers"
diff --git a/packages/MediaComponents/res/layout/media_controller.xml b/packages/MediaComponents/res/layout/media_controller.xml
index f9ebd44..73c1b38 100644
--- a/packages/MediaComponents/res/layout/media_controller.xml
+++ b/packages/MediaComponents/res/layout/media_controller.xml
@@ -71,7 +71,7 @@
             android:orientation="horizontal">
 
             <LinearLayout
-                android:id="@+id/ad"
+                android:id="@+id/ad_external_link"
                 android:clickable="true"
                 android:gravity="center"
                 android:layout_width="wrap_content"
@@ -80,7 +80,8 @@
                 android:layout_centerVertical="true"
                 android:paddingLeft="5dip"
                 android:paddingRight="10dip"
-                android:orientation="horizontal">
+                android:orientation="horizontal"
+                android:visibility="gone">
 
                 <TextView
                     android:id="@+id/ad_text"
@@ -88,7 +89,7 @@
                     android:layout_height="wrap_content"
                     android:layout_centerVertical="true"
                     android:paddingRight="5dip"
-                    android:text="Visit Advertiser"
+                    android:text="@string/MediaControlView2_ad_text"
                     android:textSize="10sp"
                     android:textColor="#FFFFFFFF" />
 
@@ -163,14 +164,35 @@
             android:textStyle="bold"
             android:textColor="#BBBBBB" />
 
+        <TextView
+            android:id="@+id/ad_skip_time"
+            android:gravity="center"
+            android:layout_width="wrap_content"
+            android:layout_height="wrap_content"
+            android:layout_centerVertical="true"
+            android:textSize="12sp"
+            android:textColor="#FFFFFF"
+            android:visibility="gone" />
+
         <LinearLayout
             android:id="@+id/basic_controls"
+            android:gravity="center"
             android:layout_alignParentRight="true"
             android:layout_width="wrap_content"
             android:layout_height="wrap_content"
             android:layout_centerVertical="true"
             android:orientation="horizontal" >
 
+            <TextView
+                android:id="@+id/ad_remaining"
+                android:gravity="center"
+                android:layout_width="wrap_content"
+                android:layout_height="wrap_content"
+                android:layout_centerVertical="true"
+                android:textSize="12sp"
+                android:textColor="#FFFFFF"
+                android:visibility="gone" />
+
             <ImageButton
                 android:id="@+id/subtitle"
                 android:scaleType="fitCenter"
diff --git a/packages/MediaComponents/res/values/strings.xml b/packages/MediaComponents/res/values/strings.xml
index 35db0e5..333d400 100644
--- a/packages/MediaComponents/res/values/strings.xml
+++ b/packages/MediaComponents/res/values/strings.xml
@@ -14,7 +14,9 @@
      limitations under the License.
 -->
 
-<resources>
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+
     <!-- Name for the default system route prior to Jellybean. [CHAR LIMIT=30] -->
     <string name="mr_system_route_name">System</string>
 
@@ -92,4 +94,15 @@
     <string name="VideoView2_error_text_unknown">Can\'t play this video.</string>
     <!-- Button to close error alert when a video cannot be played. -->
     <string name="VideoView2_error_button">OK</string>
+
+    <!-- Text for displaying ad skip wait time. -->
+    <string name="MediaControlView2_ad_skip_wait_time">
+        You can skip Ad in <xliff:g id="wait_time" example="5">%1$d</xliff:g>s
+    </string>
+    <!-- Text for displaying ad total remaining time. -->
+    <string name="MediaControlView2_ad_remaining_time">
+        Ad · <xliff:g id="remaining_time" example="1:15">%1$s</xliff:g> remaining
+    </string>
+    <!-- Placeholder text indicating that the user can press the button to go to an external website. -->
+    <string name="MediaControlView2_ad_text">Visit Advertiser</string>
 </resources>
diff --git a/packages/MediaComponents/src/com/android/widget/MediaControlView2Impl.java b/packages/MediaComponents/src/com/android/widget/MediaControlView2Impl.java
index 318cbf9..e937659 100644
--- a/packages/MediaComponents/src/com/android/widget/MediaControlView2Impl.java
+++ b/packages/MediaComponents/src/com/android/widget/MediaControlView2Impl.java
@@ -33,6 +33,7 @@
 import android.widget.ImageButton;
 import android.widget.MediaControlView2;
 import android.widget.ProgressBar;
+import android.widget.RelativeLayout;
 import android.widget.SeekBar;
 import android.widget.SeekBar.OnSeekBarChangeListener;
 import android.widget.TextView;
@@ -58,11 +59,15 @@
     static final String KEY_STATE_CONTAINS_SUBTITLE = "StateContainsSubtitle";
     static final String EVENT_UPDATE_SUBTITLE_STATUS = "UpdateSubtitleStatus";
 
+    // TODO: Remove this once integrating with MediaSession2 & MediaMetadata2
+    static final String KEY_STATE_IS_ADVERTISEMENT = "MediaTypeAdvertisement";
+    static final String EVENT_UPDATE_MEDIA_TYPE_STATUS = "UpdateMediaTypeStatus";
+
     private static final int MAX_PROGRESS = 1000;
     private static final int DEFAULT_PROGRESS_UPDATE_TIME_MS = 1000;
-
     private static final int REWIND_TIME_MS = 10000;
     private static final int FORWARD_TIME_MS = 30000;
+    private static final int AD_SKIP_WAIT_TIME_MS = 5000;
 
     private MediaController mController;
     private MediaController.TransportControls mControls;
@@ -71,8 +76,12 @@
     private ProgressBar mProgress;
     private TextView mEndTime, mCurrentTime;
     private TextView mTitleView;
+    private TextView mAdSkipView, mAdRemainingView;
+    private View mAdExternalLink;
+    private View mRoot;
     private int mDuration;
     private int mPrevState;
+    private int mPrevLeftBarWidth;
     private long mPlaybackActions;
     private boolean mDragging;
     private boolean mIsFullScreen;
@@ -81,6 +90,7 @@
     private boolean mSubtitleIsEnabled;
     private boolean mContainsSubtitle;
     private boolean mSeekAvailable;
+    private boolean mIsAdvertisement;
     private ImageButton mPlayPauseButton;
     private ImageButton mFfwdButton;
     private ImageButton mRewButton;
@@ -118,8 +128,9 @@
     @Override
     public void initialize(@Nullable AttributeSet attrs, int defStyleAttr, int defStyleRes) {
         // Inflate MediaControlView2 from XML
-        View root = makeControllerView();
-        mInstance.addView(root);
+        mRoot = makeControllerView();
+        mRoot.addOnLayoutChangeListener(mTitleBarLayoutChangeListener);
+        mInstance.addView(mRoot);
     }
 
     @Override
@@ -140,6 +151,8 @@
 
     @Override
     public void setButtonVisibility_impl(int button, int visibility) {
+        // TODO: add member variables for Fast-Forward/Prvious/Rewind buttons to save visibility in
+        // order to prevent being overriden inside updateLayout().
         switch (button) {
             case MediaControlView2.BUTTON_PLAY_PAUSE:
                 if (mPlayPauseButton != null && canPause()) {
@@ -421,6 +434,10 @@
         mCurrentTime = v.findViewById(R.id.time_current);
         mFormatBuilder = new StringBuilder();
         mFormatter = new Formatter(mFormatBuilder, Locale.getDefault());
+
+        mAdSkipView = v.findViewById(R.id.ad_skip_time);
+        mAdRemainingView = v.findViewById(R.id.ad_remaining);
+        mAdExternalLink = v.findViewById(R.id.ad_external_link);
     }
 
     /**
@@ -505,6 +522,36 @@
             mCurrentTime.setText(stringForTime(currentPosition));
         }
 
+        if (mIsAdvertisement) {
+            // Update the remaining number of seconds until the first 5 seconds of the
+            // advertisement.
+            if (mAdSkipView != null) {
+                if (currentPosition <= AD_SKIP_WAIT_TIME_MS) {
+                    if (mAdSkipView.getVisibility() == View.GONE) {
+                        mAdSkipView.setVisibility(View.VISIBLE);
+                    }
+                    String skipTimeText = ApiHelper.getLibResources().getString(
+                            R.string.MediaControlView2_ad_skip_wait_time,
+                            ((AD_SKIP_WAIT_TIME_MS - currentPosition) / 1000 + 1));
+                    mAdSkipView.setText(skipTimeText);
+                } else {
+                    if (mAdSkipView.getVisibility() == View.VISIBLE) {
+                        mAdSkipView.setVisibility(View.GONE);
+                        mNextButton.setEnabled(true);
+                        mNextButton.clearColorFilter();
+                    }
+                }
+            }
+            // Update the remaining number of seconds of the advertisement.
+            if (mAdRemainingView != null) {
+                int remainingTime =
+                        (mDuration - currentPosition < 0) ? 0 : (mDuration - currentPosition);
+                String remainingTimeText = ApiHelper.getLibResources().getString(
+                        R.string.MediaControlView2_ad_remaining_time,
+                        stringForTime(remainingTime));
+                mAdRemainingView.setText(remainingTimeText);
+            }
+        }
         return currentPosition;
     }
 
@@ -693,6 +740,36 @@
         }
     };
 
+    // The title bar is made up of two separate LinearLayouts. If the sum of the two bars are
+    // greater than the length of the title bar, reduce the size of the left bar (which makes the
+    // TextView that contains the title of the media file shrink).
+    private final View.OnLayoutChangeListener mTitleBarLayoutChangeListener
+            = new View.OnLayoutChangeListener() {
+        @Override
+        public void onLayoutChange(View v, int left, int top, int right, int bottom, int oldLeft,
+                int oldTop, int oldRight, int oldBottom) {
+            if (mRoot != null) {
+                int titleBarWidth = mRoot.findViewById(R.id.title_bar).getWidth();
+
+                View leftBar = mRoot.findViewById(R.id.title_bar_left);
+                View rightBar = mRoot.findViewById(R.id.title_bar_right);
+                int leftBarWidth = leftBar.getWidth();
+                int rightBarWidth = rightBar.getWidth();
+
+                RelativeLayout.LayoutParams params =
+                        (RelativeLayout.LayoutParams) leftBar.getLayoutParams();
+                if (leftBarWidth + rightBarWidth > titleBarWidth) {
+                    params.width = titleBarWidth - rightBarWidth;
+                    mPrevLeftBarWidth = leftBarWidth;
+                } else if (leftBarWidth + rightBarWidth < titleBarWidth && mPrevLeftBarWidth != 0) {
+                    params.width = mPrevLeftBarWidth;
+                    mPrevLeftBarWidth = 0;
+                }
+                leftBar.setLayoutParams(params);
+            }
+        }
+    };
+
     private void updateDuration() {
         if (mMetadata != null) {
             if (mMetadata.containsKey(MediaMetadata.METADATA_KEY_DURATION)) {
@@ -711,6 +788,39 @@
         }
     }
 
+    private void updateLayout() {
+        if (mIsAdvertisement) {
+            mRewButton.setVisibility(View.GONE);
+            mFfwdButton.setVisibility(View.GONE);
+            mPrevButton.setVisibility(View.GONE);
+            mCurrentTime.setVisibility(View.GONE);
+            mEndTime.setVisibility(View.GONE);
+
+            mAdSkipView.setVisibility(View.VISIBLE);
+            mAdRemainingView.setVisibility(View.VISIBLE);
+            mAdExternalLink.setVisibility(View.VISIBLE);
+
+            mProgress.setEnabled(false);
+            mNextButton.setEnabled(false);
+            mNextButton.setColorFilter(R.integer.gray);
+        } else {
+            mRewButton.setVisibility(View.VISIBLE);
+            mFfwdButton.setVisibility(View.VISIBLE);
+            mPrevButton.setVisibility(View.VISIBLE);
+            mCurrentTime.setVisibility(View.VISIBLE);
+            mEndTime.setVisibility(View.VISIBLE);
+
+            mAdSkipView.setVisibility(View.GONE);
+            mAdRemainingView.setVisibility(View.GONE);
+            mAdExternalLink.setVisibility(View.GONE);
+
+            mProgress.setEnabled(true);
+            mNextButton.setEnabled(true);
+            mNextButton.clearColorFilter();
+            disableUnsupportedButtons();
+        }
+    }
+
     private class MediaControllerCallback extends MediaController.Callback {
         @Override
         public void onPlaybackStateChanged(PlaybackState state) {
@@ -818,6 +928,12 @@
                     }
                     mContainsSubtitle = newSubtitleStatus;
                 }
+            } else if (event.equals(EVENT_UPDATE_MEDIA_TYPE_STATUS)) {
+                boolean newStatus = extras.getBoolean(KEY_STATE_IS_ADVERTISEMENT);
+                if (newStatus != mIsAdvertisement) {
+                    mIsAdvertisement = newStatus;
+                    updateLayout();
+                }
             }
         }
     }
diff --git a/packages/MediaComponents/src/com/android/widget/VideoView2Impl.java b/packages/MediaComponents/src/com/android/widget/VideoView2Impl.java
index a8ce18b..805c262 100644
--- a/packages/MediaComponents/src/com/android/widget/VideoView2Impl.java
+++ b/packages/MediaComponents/src/com/android/widget/VideoView2Impl.java
@@ -27,6 +27,7 @@
 import android.media.MediaPlayerInterface;
 import android.media.Cea708CaptionRenderer;
 import android.media.ClosedCaptionRenderer;
+import android.media.MediaMetadata2;
 import android.media.Metadata;
 import android.media.PlaybackParams;
 import android.media.SRTRenderer;
@@ -107,6 +108,9 @@
     private MediaRouter mMediaRouter;
     private MediaRouteSelector mRouteSelector;
     private Metadata mMetadata;
+    private MediaMetadata2 mMediaMetadata;
+    private boolean mNeedUpdateMediaType;
+    private Bundle mMediaTypeData;
     private String mTitle;
 
     private PlaybackState.Builder mStateBuilder;
@@ -235,6 +239,32 @@
     }
 
     @Override
+    public MediaMetadata2 getMediaMetadata_impl() {
+        return mMediaMetadata;
+    }
+
+    @Override
+    public void setMediaMetadata_impl(MediaMetadata2 metadata) {
+        // TODO: integrate this with MediaSession2#MediaItem2
+        mMediaMetadata = metadata;
+
+        // TODO: add support for handling website link
+        mMediaTypeData = new Bundle();
+        boolean isAd = metadata == null ?
+                false : metadata.getLong(MediaMetadata2.METADATA_KEY_ADVERTISEMENT) != 0;
+        mMediaTypeData.putBoolean(
+                MediaControlView2Impl.KEY_STATE_IS_ADVERTISEMENT, isAd);
+
+        if (mMediaSession != null) {
+            mMediaSession.sendSessionEvent(
+                    MediaControlView2Impl.EVENT_UPDATE_MEDIA_TYPE_STATUS, mMediaTypeData);
+        } else {
+            // Update later inside OnPreparedListener after MediaSession is initialized.
+            mNeedUpdateMediaType = true;
+        }
+    }
+
+    @Override
     public void setSubtitleEnabled_impl(boolean enable) {
         if (enable != mSubtitleEnabled) {
             selectOrDeselectSubtitle(enable);
@@ -866,6 +896,13 @@
 
             if (mMediaSession != null) {
                 mMediaSession.setMetadata(builder.build());
+
+                // TODO: merge this code with the above code when integrating with MediaSession2.
+                if (mNeedUpdateMediaType) {
+                    mMediaSession.sendSessionEvent(
+                            MediaControlView2Impl.EVENT_UPDATE_MEDIA_TYPE_STATUS, mMediaTypeData);
+                    mNeedUpdateMediaType = false;
+                }
             }
         }
     };