Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit c9969aff authored by Jin Seok Park's avatar Jin Seok Park
Browse files

MediaControlView2: Add Support for Ad

This CL adds additional UX for showing the remaining time and the
unskippable time. Also disable NEXT button for the first 5 seconds
and disable SEEK throughout the whole duration of the advertisement.

Also, this CL adds a listener to TitleBar so that when the layout of
VideoView2 is changed the layouts of the TitleBar is also changed to
avoid overlapping text.

Bug: 73136129
Test: run VideoViewTest.apk
Change-Id: Ic91b3f285343f4b6bbca2900ce499fb1f0d54bf6
parent 467af1dd
Loading
Loading
Loading
Loading
+25 −3
Original line number Diff line number Diff line
@@ -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"
+14 −1
Original line number Diff line number Diff line
@@ -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>
+119 −3
Original line number Diff line number Diff line
@@ -33,6 +33,7 @@ import android.widget.Button;
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 @@ public class MediaControlView2Impl extends BaseLayout implements MediaControlVie
    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 @@ public class MediaControlView2Impl extends BaseLayout implements MediaControlVie
    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 @@ public class MediaControlView2Impl extends BaseLayout implements MediaControlVie
    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 @@ public class MediaControlView2Impl extends BaseLayout implements MediaControlVie
    @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 @@ public class MediaControlView2Impl extends BaseLayout implements MediaControlVie

    @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 @@ public class MediaControlView2Impl extends BaseLayout implements MediaControlVie
        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 @@ public class MediaControlView2Impl extends BaseLayout implements MediaControlVie
            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 @@ public class MediaControlView2Impl extends BaseLayout implements MediaControlVie
        }
    };

    // 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 @@ public class MediaControlView2Impl extends BaseLayout implements MediaControlVie
        }
    }

    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 @@ public class MediaControlView2Impl extends BaseLayout implements MediaControlVie
                    }
                    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();
                }
            }
        }
    }
+37 −0
Original line number Diff line number Diff line
@@ -27,6 +27,7 @@ import android.media.MediaPlayer;
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 @@ public class VideoView2Impl extends BaseLayout
    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;
@@ -234,6 +238,32 @@ public class VideoView2Impl extends BaseLayout
        return mMediaControlView;
    }

    @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) {
@@ -866,6 +896,13 @@ public class VideoView2Impl extends BaseLayout

            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;
                }
            }
        }
    };