Merge changes from topic "ExoPlayer_in_MP" into sc-mainline-prod

* changes:
  Change Espresso tests to migrate to ExoPlayer
  Add ExoPlayer support for video playback
diff --git a/Android.bp b/Android.bp
index 62c38d5..e6bee09 100644
--- a/Android.bp
+++ b/Android.bp
@@ -37,6 +37,7 @@
         "androidx.fragment_fragment",
         "androidx.vectordrawable_vectordrawable-animated",
         "androidx.exifinterface_exifinterface",
+        "exoplayer2.15.1",
     ],
 
     libs: [
diff --git a/res/layout/item_video_preview.xml b/res/layout/item_video_preview.xml
index f1b5274..b888ac0 100644
--- a/res/layout/item_video_preview.xml
+++ b/res/layout/item_video_preview.xml
@@ -16,13 +16,23 @@
 
 <FrameLayout
     xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:app="http://schemas.android.com/apk/res-auto"
     android:layout_width="match_parent"
     android:layout_height="match_parent">
 
-    <VideoView
-        android:id="@+id/preview_videoView"
+
+    <com.google.android.exoplayer2.ui.StyledPlayerView
+        android:id="@+id/preview_player_view"
+        android:layout_width="match_parent"
+        android:layout_height="match_parent"
+        android:visibility="gone"
+        app:use_controller="false" />
+
+    <ImageView
+        android:id="@+id/preview_video_image"
         android:layout_width="match_parent"
         android:layout_height="match_parent"
         android:layout_gravity="center"
-        android:contentDescription="@null"/>
+        android:scaleType="fitCenter"
+        android:contentDescription="@null" />
 </FrameLayout>
diff --git a/src/com/android/providers/media/photopicker/ui/BaseViewHolder.java b/src/com/android/providers/media/photopicker/ui/BaseViewHolder.java
index e5f0af0..db8c0c1 100644
--- a/src/com/android/providers/media/photopicker/ui/BaseViewHolder.java
+++ b/src/com/android/providers/media/photopicker/ui/BaseViewHolder.java
@@ -44,28 +44,4 @@
     }
 
     public abstract void bind();
-
-    /**
-     * Called when view in this {@code RecyclerView.ViewHolder} has been recycled.
-     * <p>
-     * Optional method for BaseViewHolder subclasses to implement if they have additional actions to
-     * take on {@link RecyclerView.Adapter#onViewRecycled}
-     */
-    public void onViewRecycled() {};
-
-    /**
-     * Called when a view in this {@code RecyclerView.ViewHolder} has been attached to a window.
-     * <p>
-     * Optional method for BaseViewHolder subclasses to implement if they have additional actions to
-     * take on {@link RecyclerView.Adapter#onViewAttachedToWindow}
-     */
-    public void onViewAttachedToWindow() {};
-
-    /**
-     * Called when a view in this {@code RecyclerView.ViewHolder} has been detached from its window.
-     * <p>
-     * Optional method for BaseViewHolder subclasses to implement if they have additional actions to
-     * take on {@link RecyclerView.Adapter#onViewDetachedFromWindow}
-     */
-    public void onViewDetachedFromWindow() {};
 }
diff --git a/src/com/android/providers/media/photopicker/ui/ExoPlayerWrapper.java b/src/com/android/providers/media/photopicker/ui/ExoPlayerWrapper.java
new file mode 100644
index 0000000..9963cff
--- /dev/null
+++ b/src/com/android/providers/media/photopicker/ui/ExoPlayerWrapper.java
@@ -0,0 +1,142 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.providers.media.photopicker.ui;
+
+import android.content.Context;
+import android.net.Uri;
+import android.view.View;
+import android.widget.ImageView;
+
+import com.android.providers.media.R;
+
+import com.google.android.exoplayer2.DefaultLoadControl;
+import com.google.android.exoplayer2.DefaultRenderersFactory;
+import com.google.android.exoplayer2.ExoPlayer;
+import com.google.android.exoplayer2.LoadControl;
+import com.google.android.exoplayer2.MediaItem;
+import com.google.android.exoplayer2.Player;
+import com.google.android.exoplayer2.analytics.AnalyticsCollector;
+import com.google.android.exoplayer2.source.MediaParserExtractorAdapter;
+import com.google.android.exoplayer2.source.ProgressiveMediaSource;
+import com.google.android.exoplayer2.trackselection.DefaultTrackSelector;
+import com.google.android.exoplayer2.ui.StyledPlayerView;
+import com.google.android.exoplayer2.upstream.ContentDataSource;
+import com.google.android.exoplayer2.upstream.DefaultBandwidthMeter;
+import com.google.android.exoplayer2.util.Clock;
+
+/**
+ * A helper class that assists in initialize/prepare/play/release of ExoPlayer. The class assumes
+ * that all its public methods are called from main thread only.
+ */
+class ExoPlayerWrapper {
+    // The minimum duration of media that the player will attempt to ensure is buffered at all
+    // times.
+    private static final int MIN_BUFFER_MS = 1000;
+    // The maximum duration of media that the player will attempt to buffer.
+    private static final int MAX_BUFFER_MS = 2000;
+    // The duration of media that must be buffered for playback to start or resume following a user
+    // action such as a seek.
+    private static final int BUFFER_FOR_PLAYBACK_MS = 1000;
+    // The default duration of media that must be buffered for playback to resume after a rebuffer.
+    private static final int BUFFER_FOR_PLAYBACK_AFTER_REBUFFER_MS = 1000;
+    private static final LoadControl sLoadControl = new DefaultLoadControl.Builder()
+            .setBufferDurationsMs(
+                    MIN_BUFFER_MS,
+                    MAX_BUFFER_MS,
+                    BUFFER_FOR_PLAYBACK_MS,
+                    BUFFER_FOR_PLAYBACK_AFTER_REBUFFER_MS).build();
+
+    private final Context mContext;
+    private ExoPlayer mExoPlayer;
+    private boolean mIsPlayerReleased = true;
+
+    public ExoPlayerWrapper(Context context) {
+        mContext = context;
+    }
+
+    /**
+     * Prepares the {@link ExoPlayer} and attaches it to given {@code styledPlayerView} and starts
+     * playing.
+     * Note: The method tries to release the {@link ExoPlayer} before preparing the new one. As we
+     * don't have previous page's {@link StyledPlayerView}, we can't switch the player from previous
+     * {@link StyledPlayerView} to new one. Hence, we try to create a new {@link ExoPlayer} instead.
+     */
+    public void prepareAndPlay(StyledPlayerView styledPlayerView, ImageView imageView, Uri uri) {
+        // TODO(b/197083539): Explore options for not re-creating ExoPlayer everytime.
+        initializeExoPlayer(uri);
+
+        // TODO(b/197083539): Remove this if it drains battery.
+        styledPlayerView.setKeepScreenOn(true);
+        styledPlayerView.setPlayer(mExoPlayer);
+        styledPlayerView.setVisibility(View.VISIBLE);
+        // Hide ImageView when the player is ready.
+        mExoPlayer.addListener(new Player.Listener() {
+            @Override
+            public void onPlaybackStateChanged(int playbackState) {
+                if (playbackState == Player.STATE_READY ) {
+                    imageView.setVisibility(View.GONE);
+                }
+            }
+        });
+
+        mExoPlayer.prepare();
+        mIsPlayerReleased = false;
+
+        mExoPlayer.setPlayWhenReady(true);
+    }
+
+    public void releaseIfNecessary() {
+        releaseIfNecessaryInternal();
+    }
+
+    private ExoPlayer createExoPlayer() {
+        // ProgressiveMediaFactory will be enough for video playback of videos on the device.
+        // This also reduces apk size.
+        ProgressiveMediaSource.Factory mediaSourceFactory = new ProgressiveMediaSource.Factory(
+                () -> new ContentDataSource(mContext), MediaParserExtractorAdapter.FACTORY);
+
+        return new ExoPlayer.Builder(mContext,
+                new DefaultRenderersFactory(mContext),
+                new DefaultTrackSelector(mContext),
+                mediaSourceFactory,
+                sLoadControl,
+                DefaultBandwidthMeter.getSingletonInstance(mContext),
+                new AnalyticsCollector(Clock.DEFAULT)).buildExoPlayer();
+    }
+
+    private void initializeExoPlayer(Uri uri) {
+        // Try releasing the ExoPlayer first.
+        releaseIfNecessaryInternal();
+
+        mExoPlayer = createExoPlayer();
+        // We always start from the beginning of the video, and we always repeat the video in a loop
+        mExoPlayer.setRepeatMode(Player.REPEAT_MODE_ONE);
+        // We only play one video in the player, hence we should always use setMediaItem instead of
+        // ExoPlayer#addMediaItem
+        mExoPlayer.setMediaItem(MediaItem.fromUri(uri));
+    }
+
+    private void releaseIfNecessaryInternal() {
+        // Release the player only when it's not already released. ExoPlayer doesn't crash if we try
+        // to release already released player, but ExoPlayer#release() may not be a no-op, hence we
+        // call release() only when it's not already released.
+        if (!mIsPlayerReleased) {
+            mExoPlayer.release();
+            mIsPlayerReleased = true;
+        }
+    }
+}
diff --git a/src/com/android/providers/media/photopicker/ui/ImageLoader.java b/src/com/android/providers/media/photopicker/ui/ImageLoader.java
index 3bdcd68..bfcfe34 100644
--- a/src/com/android/providers/media/photopicker/ui/ImageLoader.java
+++ b/src/com/android/providers/media/photopicker/ui/ImageLoader.java
@@ -23,6 +23,7 @@
 import androidx.annotation.NonNull;
 
 import com.bumptech.glide.Glide;
+import com.bumptech.glide.request.RequestOptions;
 import com.bumptech.glide.signature.ObjectKey;
 
 import com.android.providers.media.photopicker.data.model.Category;
@@ -86,9 +87,12 @@
         if (item.isGif()) {
             Glide.with(mContext)
                     .load(item.getContentUri())
+                    .signature(new ObjectKey(
+                            item.getContentUri().toString() + item.getGenerationModified()))
                     .into(imageView);
             return;
         }
+
         // Preview as bitmap image for all other image types
         Glide.with(mContext)
                 .asBitmap()
@@ -97,4 +101,17 @@
                         item.getContentUri().toString() + item.getGenerationModified()))
                 .into(imageView);
     }
+
+    /**
+     * Loads the image from first frame of the given video item
+     */
+    public void loadImageFromVideoForPreview(@NonNull Item item, @NonNull ImageView imageView) {
+        Glide.with(mContext)
+                .asBitmap()
+                .load(item.getContentUri())
+                .apply(new RequestOptions().frame(1000))
+                .signature(new ObjectKey("Preview"
+                        + item.getContentUri().toString() + item.getGenerationModified()))
+                .into(imageView);
+    }
 }
diff --git a/src/com/android/providers/media/photopicker/ui/PlaybackHandler.java b/src/com/android/providers/media/photopicker/ui/PlaybackHandler.java
new file mode 100644
index 0000000..18a77ee
--- /dev/null
+++ b/src/com/android/providers/media/photopicker/ui/PlaybackHandler.java
@@ -0,0 +1,134 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.providers.media.photopicker.ui;
+
+import android.content.Context;
+import android.net.Uri;
+import android.os.Looper;
+import android.util.Log;
+import android.view.View;
+import android.widget.ImageView;
+
+import com.android.providers.media.R;
+import com.android.providers.media.photopicker.data.model.Item;
+
+import com.google.android.exoplayer2.ui.StyledPlayerView;
+
+/**
+ * A class to handle page selected state to initiate video playback or release video player
+ * resources. All the public methods of this class must be called from main thread as ExoPlayer
+ * should be prepared/released from main thread.
+ */
+class PlaybackHandler {
+    private static final String TAG = "PlaybackHandler";
+    // Only main thread can call the methods in this class, hence we don't need to guard mVideoUri
+    // with lock while reading or writing to it.
+    private Uri mVideoUri = null;
+    private final ExoPlayerWrapper mExoPlayerWrapper;
+    private final ImageLoader mImageLoader;
+
+    PlaybackHandler(Context context, ImageLoader imageLoader) {
+        mExoPlayerWrapper = new ExoPlayerWrapper(context);
+        mImageLoader = imageLoader;
+    }
+
+    /**
+     * Handles video playback for the {@link ViewPager2} page when it's selected i.e., completely
+     * visible.
+     * <ul>
+     * <li> If the selected page is a video page, prepare and play the video associated with
+     * selected page
+     * <li> If the selected page is a video page and the same video is already playing, then no
+     * action will be taken.
+     * <li> If the selected page is non-video page, try releasing the ExoPlayer associated with
+     * previous page that was selected.
+     * </ul>
+     * @param view {@link RecyclerView.ViewHolder#itemView} of the selected page.
+     */
+    public void handleVideoPlayback(View view) {
+        assertMainThread();
+
+        final Object tag = view.getTag();
+        if (!(tag instanceof Item)) {
+            throw new IllegalStateException("Expected Item tag to be set to " + view);
+        }
+
+        final Item item = (Item) tag;
+        if (!item.isVideo()) {
+            // We only need to handle video playback. For everything else, try releasing ExoPlayer
+            // if there is a prepared ExoPlayer of the previous page.
+            mExoPlayerWrapper.releaseIfNecessary();
+            mVideoUri = null;
+            return;
+        }
+
+        final Uri videoUri = item.getContentUri();
+        if (mVideoUri != null && mVideoUri.equals(videoUri)) {
+            // Selected video is already handled. This must be a slight drag and drop, and we don't
+            // have to change state of the player.
+            Log.d(TAG, "Ignoring handlePageSelected of already selected page, with uri "
+                    + videoUri);
+            return;
+        }
+
+        final StyledPlayerView styledPlayerView = view.findViewById(R.id.preview_player_view);
+        if (styledPlayerView == null) {
+            throw new IllegalStateException("Expected to find StyledPlayerView in " + view);
+        }
+        final ImageView imageView = view.findViewById(R.id.preview_video_image);
+
+        mVideoUri = videoUri;
+        mExoPlayerWrapper.prepareAndPlay(styledPlayerView, imageView, mVideoUri);
+    }
+
+    public void onBind(View itemView) {
+        final Item item = (Item) itemView.getTag();
+        // We set the ImageView with image from the video. ImageView is needed to improve the user
+        // experience while video player is not yet initialized or being prepared.
+        final ImageView imageView = itemView.findViewById(R.id.preview_video_image);
+        mImageLoader.loadImageFromVideoForPreview(item, imageView);
+
+        // Video playback needs granular page state events and hence video playback is initiated by
+        // ViewPagerWrapper and handled by PlaybackHandler#handleVideoPlayback
+    }
+
+    public void onViewAttachedToWindow(View itemView) {
+        final ImageView imageView = itemView.findViewById(R.id.preview_video_image);
+        final StyledPlayerView styledPlayerView = itemView.findViewById(R.id.preview_player_view);
+
+        imageView.setVisibility(View.VISIBLE);
+        styledPlayerView.setVisibility(View.GONE);
+    }
+
+    /**
+     * Releases ExoPlayer if there is any. Also resets the saved video uri.
+     */
+    public void releaseResources() {
+        assertMainThread();
+
+        mVideoUri = null;
+        mExoPlayerWrapper.releaseIfNecessary();
+    }
+
+    private void assertMainThread() {
+        if (Looper.getMainLooper().isCurrentThread()) return;
+
+        throw new IllegalStateException("PlaybackHandler methods are expected to be called from"
+                + " main thread. Current thread " + Looper.myLooper().getThread()
+                + ", Main thread" + Looper.getMainLooper().getThread());
+    }
+}
diff --git a/src/com/android/providers/media/photopicker/ui/PreviewAdapter.java b/src/com/android/providers/media/photopicker/ui/PreviewAdapter.java
index 7bdacf0..a39932b 100644
--- a/src/com/android/providers/media/photopicker/ui/PreviewAdapter.java
+++ b/src/com/android/providers/media/photopicker/ui/PreviewAdapter.java
@@ -16,6 +16,8 @@
 
 package com.android.providers.media.photopicker.ui;
 
+import android.content.Context;
+import android.view.View;
 import android.view.ViewGroup;
 
 import androidx.annotation.NonNull;
@@ -36,9 +38,11 @@
 
     private List<Item> mItemList = new ArrayList<>();
     private ImageLoader mImageLoader;
+    private final PlaybackHandler mPlaybackHandler;
 
-    public PreviewAdapter(ImageLoader imageLoader) {
-        mImageLoader = imageLoader;
+    public PreviewAdapter(Context context) {
+        mImageLoader = new ImageLoader(context);
+        mPlaybackHandler = new PlaybackHandler(context, mImageLoader);
     }
 
     @NonNull
@@ -52,28 +56,32 @@
     }
 
     @Override
-    public void onBindViewHolder(@NonNull BaseViewHolder photoHolder, int position) {
+    public void onBindViewHolder(@NonNull BaseViewHolder holder, int position) {
         final Item item = getItem(position);
-        photoHolder.itemView.setTag(item);
-        photoHolder.bind();
+        holder.itemView.setTag(item);
+        holder.bind();
+
+        if (item.isVideo()) {
+            mPlaybackHandler.onBind(holder.itemView);
+        }
     }
 
     @Override
     public void onViewAttachedToWindow(BaseViewHolder holder) {
         super.onViewAttachedToWindow(holder);
-        holder.onViewAttachedToWindow();
+
+        final Item item = (Item) holder.itemView.getTag();
+        if (item.isVideo()) {
+            mPlaybackHandler.onViewAttachedToWindow(holder.itemView);
+        }
     }
 
-    @Override
-    public void onViewDetachedFromWindow(BaseViewHolder holder) {
-        super.onViewDetachedFromWindow(holder);
-        holder.onViewDetachedFromWindow();
+    public void handlePageSelected(View itemView) {
+        mPlaybackHandler.handleVideoPlayback(itemView);
     }
 
-    @Override
-    public void onViewRecycled(BaseViewHolder holder) {
-        super.onViewRecycled(holder);
-        holder.onViewRecycled();
+    public void onStop() {
+        mPlaybackHandler.releaseResources();
     }
 
     @Override
diff --git a/src/com/android/providers/media/photopicker/ui/PreviewFragment.java b/src/com/android/providers/media/photopicker/ui/PreviewFragment.java
index 2e8dd6a..6f1801f 100644
--- a/src/com/android/providers/media/photopicker/ui/PreviewFragment.java
+++ b/src/com/android/providers/media/photopicker/ui/PreviewFragment.java
@@ -241,9 +241,23 @@
 
         ((PhotoPickerActivity) getActivity()).updateCommonLayouts(LayoutModeUtils.MODE_PREVIEW,
                 /* title */"");
+    }
+
+    @Override
+    public void onStop() {
+        super.onStop();
 
         if (mViewPager2Wrapper != null) {
-            mViewPager2Wrapper.onResume();
+            mViewPager2Wrapper.onStop();
+        }
+    }
+
+    @Override
+    public void onStart() {
+        super.onStart();
+
+        if (mViewPager2Wrapper != null) {
+            mViewPager2Wrapper.onStart();
         }
     }
 
diff --git a/src/com/android/providers/media/photopicker/ui/PreviewVideoHolder.java b/src/com/android/providers/media/photopicker/ui/PreviewVideoHolder.java
index 366fdb5..8a9ca2e 100644
--- a/src/com/android/providers/media/photopicker/ui/PreviewVideoHolder.java
+++ b/src/com/android/providers/media/photopicker/ui/PreviewVideoHolder.java
@@ -17,11 +17,14 @@
 package com.android.providers.media.photopicker.ui;
 
 import android.content.Context;
+import android.view.View;
 import android.view.ViewGroup;
-import android.widget.VideoView;
+import android.widget.ImageView;
 
 import androidx.viewpager2.widget.ViewPager2;
 
+import com.google.android.exoplayer2.ui.StyledPlayerView;
+
 import com.android.providers.media.R;
 import com.android.providers.media.photopicker.data.model.Item;
 
@@ -29,40 +32,13 @@
  * ViewHolder of a video item within the {@link ViewPager2}
  */
 public class PreviewVideoHolder extends BaseViewHolder {
-    private final VideoView mVideoView;
-
     public PreviewVideoHolder(Context context, ViewGroup parent) {
         super(context, parent, R.layout.item_video_preview);
-        mVideoView = itemView.findViewById(R.id.preview_videoView);
     }
 
     @Override
     public void bind() {
-        final Item item = (Item) itemView.getTag();
-        mVideoView.setVideoURI(item.getContentUri());
-    }
-
-    @Override
-    public void onViewAttachedToWindow() {
-        super.onViewAttachedToWindow();
-        mVideoView.setOnPreparedListener(mp -> {
-            mp.setLooping(true);
-            // For simplicity, we will always start the video from the beginning.
-            mp.seekTo(0);
-            mp.start();
-        });
-    }
-
-    @Override
-    public void onViewDetachedFromWindow() {
-        super.onViewDetachedFromWindow();
-        mVideoView.pause();
-    }
-
-    @Override
-    public void onViewRecycled() {
-        super.onViewRecycled();
-        // This will deallocate any MediaPlayer resources it has been holding
-        mVideoView.stopPlayback();
+        // Video playback needs granular page state events and hence video playback is initiated by
+        // ViewPagerWrapper and handled by PlaybackHandler#handleVideoPlayback
     }
 }
diff --git a/src/com/android/providers/media/photopicker/ui/ViewPager2Wrapper.java b/src/com/android/providers/media/photopicker/ui/ViewPager2Wrapper.java
index 6e5a2b5..1678bbf 100644
--- a/src/com/android/providers/media/photopicker/ui/ViewPager2Wrapper.java
+++ b/src/com/android/providers/media/photopicker/ui/ViewPager2Wrapper.java
@@ -19,6 +19,7 @@
 import android.content.Context;
 import android.view.View;
 
+import androidx.viewpager2.widget.CompositePageTransformer;
 import androidx.viewpager2.widget.MarginPageTransformer;
 import androidx.viewpager2.widget.ViewPager2;
 
@@ -45,12 +46,15 @@
 
         final Context context = mViewPager.getContext();
 
-        mAdapter = new PreviewAdapter(new ImageLoader(context));
+        mAdapter = new PreviewAdapter(context);
         mAdapter.updateItemList(selectedItems);
         mViewPager.setAdapter(mAdapter);
 
-        mViewPager.setPageTransformer(new MarginPageTransformer(
+        CompositePageTransformer compositePageTransformer = new CompositePageTransformer();
+        compositePageTransformer.addTransformer(new MarginPageTransformer(
                 context.getResources().getDimensionPixelSize(R.dimen.preview_viewpager_margin)));
+        compositePageTransformer.addTransformer(new PlayerPageTransformer());
+        mViewPager.setPageTransformer(compositePageTransformer);
     }
 
     /**
@@ -74,14 +78,31 @@
         return mAdapter.getItem(position);
     }
 
-    public void onResume() {
-        // This is necessary to ensure we call ViewHolder#bind() onResume()
-        mAdapter.notifyDataSetChanged();
+    public void onStop() {
+        mAdapter.onStop();
+    }
+
+    public void onStart() {
+        // TODO(b/197083539): Restore the playback state here.
+        // This forces PageTransformer#transformPage call and assists in ExoPlayer initialization.
+        mViewPager.requestTransform();
     }
 
     public void onDestroy() {
         for (ViewPager2.OnPageChangeCallback callback : mOnPageChangeCallbacks) {
             mViewPager.unregisterOnPageChangeCallback(callback);
         }
+        mOnPageChangeCallbacks.clear();
+    }
+
+    private class PlayerPageTransformer implements ViewPager2.PageTransformer {
+        @Override
+        public void transformPage(View view, float position) {
+            // We are only interested in position == 0.0. Only position=0.0 indicates that the page
+            // is selected.
+            if (position != 0) return;
+
+            mAdapter.handlePageSelected(view);
+        }
     }
 }
diff --git a/tests/Android.bp b/tests/Android.bp
index 87a353c..6277a2e 100644
--- a/tests/Android.bp
+++ b/tests/Android.bp
@@ -139,6 +139,7 @@
         "androidx.fragment_fragment",
         "androidx.vectordrawable_vectordrawable-animated",
         "androidx.exifinterface_exifinterface",
+        "exoplayer2.15.1"
     ],
 
     certificate: "media",
diff --git a/tests/src/com/android/providers/media/photopicker/espresso/PreviewMultiSelectLongPressTest.java b/tests/src/com/android/providers/media/photopicker/espresso/PreviewMultiSelectLongPressTest.java
index a59d02b..7ad186d 100644
--- a/tests/src/com/android/providers/media/photopicker/espresso/PreviewMultiSelectLongPressTest.java
+++ b/tests/src/com/android/providers/media/photopicker/espresso/PreviewMultiSelectLongPressTest.java
@@ -101,12 +101,9 @@
 
         registerIdlingResourceAndWaitForIdle();
 
-        // Since there is no video in the video file, we get an error.
-        onView(withText(android.R.string.ok)).perform(click());
-
-        // Verify videoView is displayed
+        // Verify video player is displayed
         assertMultiSelectLongPressCommonLayoutMatches();
-        onView(withId(R.id.preview_videoView)).check(matches(isDisplayed()));
+        onView(withId(R.id.preview_player_view)).check(matches(isDisplayed()));
         // Verify no special format icon is previewed
         onView(withId(PREVIEW_MOTION_PHOTO_ID)).check(doesNotExist());
         onView(withId(PREVIEW_GIF_ID)).check(doesNotExist());
diff --git a/tests/src/com/android/providers/media/photopicker/espresso/PreviewMultiSelectTest.java b/tests/src/com/android/providers/media/photopicker/espresso/PreviewMultiSelectTest.java
index 84306a3..d45290d 100644
--- a/tests/src/com/android/providers/media/photopicker/espresso/PreviewMultiSelectTest.java
+++ b/tests/src/com/android/providers/media/photopicker/espresso/PreviewMultiSelectTest.java
@@ -69,7 +69,7 @@
 
 @RunWith(AndroidJUnit4ClassRunner.class)
 public class PreviewMultiSelectTest extends PhotoPickerBaseTest {
-    private static final int VIDEO_VIEW_ID = R.id.preview_videoView;
+    private static final int PLAYER_VIEW_ID = R.id.preview_player_view;
 
     @Rule
     public ActivityScenarioRule<PhotoPickerTestActivity> mRule
@@ -211,11 +211,12 @@
         assertSpecialFormatBadgeDoesNotExist();
 
         swipeLeftAndWait();
-        // Since there is no video in the video file, we get an error.
-        onView(withText(android.R.string.ok)).perform(click());
         // 3. Video item
         assertMultiSelectPreviewCommonLayoutDisplayed();
-        onView(ViewPagerMatcher(PREVIEW_VIEW_PAGER_ID, VIDEO_VIEW_ID))
+        // TODO(b/197083539): We don't check the video image to be visible or not because its
+        // visibility is time sensitive. Try waiting till player is ready and assert that video
+        // image is no more visible.
+        onView(ViewPagerMatcher(PREVIEW_VIEW_PAGER_ID, PLAYER_VIEW_ID))
                 .check(matches(isDisplayed()));
         // Verify no special format icon is previewed
         assertSpecialFormatBadgeDoesNotExist();
@@ -313,21 +314,19 @@
         onView(withId(VIEW_SELECTED_BUTTON_ID)).perform(click());
         registerIdlingResourceAndWaitForIdle();
 
-        // Since there is no video in the video file, we get an error.
-        onView(withText(android.R.string.ok)).perform(click());
         assertMultiSelectPreviewCommonLayoutDisplayed();
 
         // Verify that "View Selected" shows the video item, not the image item that was previewed
         // earlier with preview on long press
-        onView(ViewPagerMatcher(PREVIEW_VIEW_PAGER_ID, VIDEO_VIEW_ID))
+        onView(ViewPagerMatcher(PREVIEW_VIEW_PAGER_ID, PLAYER_VIEW_ID))
                 .check(matches(isDisplayed()));
 
         // Swipe and verify we don't preview the image item
         swipeLeftAndWait();
-        onView(ViewPagerMatcher(PREVIEW_VIEW_PAGER_ID, VIDEO_VIEW_ID))
+        onView(ViewPagerMatcher(PREVIEW_VIEW_PAGER_ID, PLAYER_VIEW_ID))
                 .check(matches(isDisplayed()));
         swipeRightAndWait();
-        onView(ViewPagerMatcher(PREVIEW_VIEW_PAGER_ID, VIDEO_VIEW_ID))
+        onView(ViewPagerMatcher(PREVIEW_VIEW_PAGER_ID, PLAYER_VIEW_ID))
                 .check(matches(isDisplayed()));
     }
 
diff --git a/tests/src/com/android/providers/media/photopicker/espresso/PreviewSingleSelectTest.java b/tests/src/com/android/providers/media/photopicker/espresso/PreviewSingleSelectTest.java
index 8d847ef..8073cd2 100644
--- a/tests/src/com/android/providers/media/photopicker/espresso/PreviewSingleSelectTest.java
+++ b/tests/src/com/android/providers/media/photopicker/espresso/PreviewSingleSelectTest.java
@@ -115,12 +115,9 @@
 
         registerIdlingResourceAndWaitForIdle();
 
-        // Since there is no video in the video file, we get an error.
-        onView(withText(android.R.string.ok)).perform(click());
-
-        // Verify videoView is displayed
+        // Verify video player is displayed
         assertSingleSelectCommonLayoutMatches();
-        onView(withId(R.id.preview_videoView)).check(matches(isDisplayed()));
+        onView(withId(R.id.preview_player_view)).check(matches(isDisplayed()));
         // Verify no special format icon is previewed
         onView(withId(PREVIEW_MOTION_PHOTO_ID)).check(doesNotExist());
         onView(withId(PREVIEW_GIF_ID)).check(doesNotExist());