1 /*
2  * Copyright 2023 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 android.media.cujcommon.cts;
18 
19 import static org.junit.Assert.assertEquals;
20 
21 import android.net.Uri;
22 import android.os.Bundle;
23 import android.view.SurfaceView;
24 import android.view.WindowManager;
25 import android.widget.ScrollView;
26 
27 import androidx.appcompat.app.AppCompatActivity;
28 import androidx.media3.common.MediaItem;
29 import androidx.media3.common.Player;
30 import androidx.media3.exoplayer.ExoPlayer;
31 
32 import java.util.ArrayList;
33 import java.util.Collections;
34 import java.util.List;
35 
36 public class ScrollTestActivity extends AppCompatActivity {
37 
38   protected static final int SURFACE_HEIGHT = 600; /* Surface layout_height is 600dp*/
39   protected SurfaceView mFirstSurfaceView;
40   protected SurfaceView mSecondSurfaceView;
41   protected ExoPlayer mFirstPlayer;
42   protected ExoPlayer mSecondPlayer;
43   protected static List<String> sVideoUrls = new ArrayList<>();
44   protected Player.Listener mPlayerListener;
45   protected ScrollView mScrollView;
46   protected boolean mIsFirstSurfaceActive;
47 
48   @Override
onCreate(Bundle savedInstanceState)49   protected void onCreate(Bundle savedInstanceState) {
50     super.onCreate(savedInstanceState);
51     setContentView(R.layout.activity_main_scroll);
52     getWindow().addFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON);
53     buildPlayer();
54   }
55 
56   /**
57    * Build the two players with two surface view and changes the surface and player according to
58    * the top position of the view.
59    */
buildPlayer()60   protected void buildPlayer() {
61     mScrollView = this.findViewById(R.id.scroll_view);
62     mFirstPlayer = new ExoPlayer.Builder(getApplicationContext()).build();
63     mFirstSurfaceView = findViewById(R.id.firstSurface);
64     mFirstPlayer.setVideoSurfaceView(mFirstSurfaceView);
65     mSecondPlayer = new ExoPlayer.Builder(getApplicationContext()).build();
66     mSecondSurfaceView = findViewById(R.id.secondSurface);
67     mSecondPlayer.setVideoSurfaceView(mSecondSurfaceView);
68 
69     mScrollView.getViewTreeObserver().addOnScrollChangedListener(() -> {
70       if (mIsFirstSurfaceActive && mScrollView.getScrollY() >= SURFACE_HEIGHT) {
71         mFirstPlayer.pause();
72         mIsFirstSurfaceActive = false;
73         mSecondPlayer.play();
74       } else if (!mIsFirstSurfaceActive && mScrollView.getScrollY() < SURFACE_HEIGHT) {
75         mSecondPlayer.pause();
76         mIsFirstSurfaceActive = true;
77         mFirstPlayer.play();
78       }
79     });
80   }
81 
82   /**
83    * Prepare input list and add alternate urls to first and second player's playlist.
84    */
prepareMediaItems(List<String> urls)85   public void prepareMediaItems(List<String> urls) {
86     sVideoUrls = urls != null ? Collections.unmodifiableList(urls) : null;
87     if (sVideoUrls == null) {
88       return;
89     }
90     assertEquals(0, (sVideoUrls.size() % 2));
91     for (int i = 0; i < sVideoUrls.size(); ) {
92       mFirstPlayer.addMediaItem(MediaItem.fromUri(Uri.parse(sVideoUrls.get(i++))));
93       mSecondPlayer.addMediaItem(MediaItem.fromUri(Uri.parse(sVideoUrls.get(i++))));
94     }
95   }
96 
97   /**
98    * Prepare both players, and start the first player.
99    */
100   @Override
onStart()101   protected void onStart() {
102     super.onStart();
103     mFirstPlayer.prepare();
104     mSecondPlayer.prepare();
105     mIsFirstSurfaceActive = true;
106     mFirstPlayer.play();
107   }
108 
109   /**
110    * Stop both players.
111    */
112   @Override
onStop()113   protected void onStop() {
114     mFirstPlayer.stop();
115     mSecondPlayer.stop();
116     super.onStop();
117   }
118 
119   /**
120    * Release the players and destroy the activity.
121    */
122   @Override
onDestroy()123   protected void onDestroy() {
124     super.onDestroy();
125     mFirstPlayer.release();
126     mSecondPlayer.release();
127     getWindow().clearFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON);
128   }
129 
130   /**
131    * Register listener to receive events from the player.
132    *
133    * <p>This method can be called from any thread.
134    *
135    * @param listener The listener to register.
136    */
addPlayerListener(Player.Listener listener)137   public void addPlayerListener(Player.Listener listener) {
138     mSecondPlayer.addListener(listener);
139     mFirstPlayer.addListener(listener);
140     this.mPlayerListener = listener;
141   }
142 
143   /**
144    * Unregister a listener registered through addPlayerListener(Listener). The listener will no
145    * longer receive events.
146    */
removePlayerListener()147   public void removePlayerListener() {
148     mSecondPlayer.removeListener(this.mPlayerListener);
149     mFirstPlayer.removeListener(this.mPlayerListener);
150   }
151 }
152