1/* 2 * Copyright (C) 2017 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 17package androidx.leanback.media; 18 19import static org.junit.Assert.assertEquals; 20import static org.junit.Assert.assertSame; 21import static org.junit.Assert.assertTrue; 22import static org.mockito.Matchers.anyInt; 23import static org.mockito.Matchers.anyString; 24import static org.mockito.Mockito.never; 25import static org.mockito.Mockito.times; 26import static org.mockito.Mockito.when; 27 28import android.content.Context; 29import android.support.test.InstrumentationRegistry; 30import android.support.test.filters.SmallTest; 31import android.view.ContextThemeWrapper; 32import android.view.KeyEvent; 33import android.view.ViewGroup; 34import android.widget.FrameLayout; 35import android.widget.LinearLayout; 36 37import androidx.leanback.widget.PlaybackControlsRow; 38import androidx.leanback.widget.PlaybackRowPresenter; 39import androidx.leanback.widget.PlaybackTransportRowPresenter; 40import androidx.leanback.widget.RowPresenter; 41 42import org.junit.Test; 43import org.mockito.Mockito; 44 45@SmallTest 46public class PlaybackTransportControlGlueTest { 47 48 public static class PlayerAdapterSample extends PlayerAdapter { 49 @Override 50 public void play() { 51 } 52 53 @Override 54 public void pause() { 55 } 56 } 57 58 public static class PlaybackTransportControlGlueImpl 59 extends PlaybackTransportControlGlue { 60 public PlaybackTransportControlGlueImpl(Context context) { 61 super(context, new PlayerAdapterSample()); 62 } 63 64 public PlaybackTransportControlGlueImpl(Context context, PlayerAdapter impl) { 65 super(context, impl); 66 } 67 } 68 69 Context mContext; 70 PlaybackTransportControlGlueImpl mGlue; 71 PlaybackTransportRowPresenter.ViewHolder mViewHolder; 72 PlayerAdapter mAdapter; 73 74 void setupWithMockAdapterAndViewHolder() { 75 mContext = new ContextThemeWrapper( 76 InstrumentationRegistry.getInstrumentation().getTargetContext(), 77 androidx.leanback.test.R.style.Theme_Leanback); 78 79 mAdapter = Mockito.mock(PlayerAdapter.class); 80 when(mAdapter.isPrepared()).thenReturn(true); 81 when(mAdapter.getCurrentPosition()).thenReturn(123L); 82 when(mAdapter.getDuration()).thenReturn(20000L); 83 when(mAdapter.getBufferedPosition()).thenReturn(321L); 84 InstrumentationRegistry.getInstrumentation().runOnMainSync(new Runnable() { 85 @Override 86 public void run() { 87 mGlue = new PlaybackTransportControlGlueImpl(mContext, mAdapter); 88 PlaybackGlueHostImpl host = new PlaybackGlueHostImpl(); 89 mGlue.setHost(host); 90 91 PlaybackTransportRowPresenter presenter = (PlaybackTransportRowPresenter) 92 mGlue.getPlaybackRowPresenter(); 93 FrameLayout parent = new FrameLayout(mContext); 94 mViewHolder = (PlaybackTransportRowPresenter.ViewHolder) 95 presenter.onCreateViewHolder(parent); 96 presenter.onBindViewHolder(mViewHolder, mGlue.getControlsRow()); 97 } 98 }); 99 } 100 101 void playMockAdapter() { 102 mGlue.play(); 103 Mockito.verify(mAdapter, times(1)).play(); 104 when(mAdapter.isPlaying()).thenReturn(true); 105 mAdapter.getCallback().onPlayStateChanged(mAdapter); 106 } 107 108 @Test 109 public void usingDefaultRowAndPresenter() { 110 mContext = InstrumentationRegistry.getInstrumentation().getTargetContext(); 111 InstrumentationRegistry.getInstrumentation().runOnMainSync(new Runnable() { 112 @Override 113 public void run() { 114 mGlue = new PlaybackTransportControlGlueImpl(mContext); 115 } 116 }); 117 PlaybackGlueHostImpl host = new PlaybackGlueHostImpl(); 118 119 mGlue.setHost(host); 120 assertSame(mGlue, host.mGlue); 121 assertSame(host, mGlue.getHost()); 122 assertTrue(host.mPlaybackRowPresenter instanceof PlaybackTransportRowPresenter); 123 assertTrue(host.mRow instanceof PlaybackControlsRow); 124 125 } 126 @Test 127 public void customRowPresenter() { 128 mContext = InstrumentationRegistry.getInstrumentation().getTargetContext(); 129 InstrumentationRegistry.getInstrumentation().runOnMainSync(new Runnable() { 130 @Override 131 public void run() { 132 mGlue = new PlaybackTransportControlGlueImpl(mContext); 133 } 134 }); 135 PlaybackRowPresenter presenter = new PlaybackRowPresenter() { 136 @Override 137 protected RowPresenter.ViewHolder createRowViewHolder(ViewGroup parent) { 138 return new RowPresenter.ViewHolder(new LinearLayout(parent.getContext())); 139 } 140 }; 141 mGlue.setPlaybackRowPresenter(presenter); 142 PlaybackGlueHostImpl host = new PlaybackGlueHostImpl(); 143 144 mGlue.setHost(host); 145 assertSame(mGlue, host.mGlue); 146 assertSame(host, mGlue.getHost()); 147 assertSame(host.mPlaybackRowPresenter, presenter); 148 assertTrue(host.mRow instanceof PlaybackControlsRow); 149 150 } 151 152 @Test 153 public void customControlsRow() { 154 mContext = InstrumentationRegistry.getInstrumentation().getTargetContext(); 155 InstrumentationRegistry.getInstrumentation().runOnMainSync(new Runnable() { 156 @Override 157 public void run() { 158 mGlue = new PlaybackTransportControlGlueImpl(mContext); 159 } 160 }); 161 PlaybackControlsRow row = new PlaybackControlsRow(mContext); 162 mGlue.setControlsRow(row); 163 PlaybackGlueHostImpl host = new PlaybackGlueHostImpl(); 164 165 mGlue.setHost(host); 166 assertSame(mGlue, host.mGlue); 167 assertSame(host, mGlue.getHost()); 168 assertTrue(host.mPlaybackRowPresenter instanceof PlaybackTransportRowPresenter); 169 assertSame(host.mRow, row); 170 171 } 172 173 @Test 174 public void customRowAndPresenter() { 175 mContext = InstrumentationRegistry.getInstrumentation().getTargetContext(); 176 InstrumentationRegistry.getInstrumentation().runOnMainSync(new Runnable() { 177 @Override 178 public void run() { 179 mGlue = new PlaybackTransportControlGlueImpl(mContext); 180 } 181 }); 182 PlaybackControlsRow row = new PlaybackControlsRow(mContext); 183 mGlue.setControlsRow(row); 184 PlaybackRowPresenter presenter = new PlaybackRowPresenter() { 185 @Override 186 protected RowPresenter.ViewHolder createRowViewHolder(ViewGroup parent) { 187 return new RowPresenter.ViewHolder(new LinearLayout(parent.getContext())); 188 } 189 }; 190 mGlue.setPlaybackRowPresenter(presenter); 191 PlaybackGlueHostImpl host = new PlaybackGlueHostImpl(); 192 193 mGlue.setHost(host); 194 assertSame(mGlue, host.mGlue); 195 assertSame(host, mGlue.getHost()); 196 assertSame(host.mPlaybackRowPresenter, presenter); 197 assertSame(host.mRow, row); 198 199 } 200 201 @Test 202 public void playerAdapterTest() { 203 setupWithMockAdapterAndViewHolder(); 204 205 mGlue.play(); 206 Mockito.verify(mAdapter, times(1)).play(); 207 mGlue.pause(); 208 Mockito.verify(mAdapter, times(1)).pause(); 209 mGlue.seekTo(123L); 210 Mockito.verify(mAdapter, times(1)).seekTo(123L); 211 assertEquals(123L, mGlue.getCurrentPosition()); 212 assertEquals(20000L, mGlue.getDuration()); 213 assertEquals(321L, mGlue.getBufferedPosition()); 214 215 assertSame(mGlue.mAdapterCallback, mAdapter.getCallback()); 216 217 when(mAdapter.getCurrentPosition()).thenReturn(124L); 218 mAdapter.getCallback().onCurrentPositionChanged(mAdapter); 219 assertEquals(124L, mGlue.getControlsRow().getCurrentPosition()); 220 221 when(mAdapter.getBufferedPosition()).thenReturn(333L); 222 mAdapter.getCallback().onBufferedPositionChanged(mAdapter); 223 assertEquals(333L, mGlue.getControlsRow().getBufferedPosition()); 224 225 when(mAdapter.getDuration()).thenReturn((long) (Integer.MAX_VALUE) * 2); 226 mAdapter.getCallback().onDurationChanged(mAdapter); 227 assertEquals((long) (Integer.MAX_VALUE) * 2, mGlue.getControlsRow().getDuration()); 228 229 } 230 231 @Test 232 public void savePlayerAdapterEventBeforeAttachToHost() { 233 mContext = new ContextThemeWrapper( 234 InstrumentationRegistry.getInstrumentation().getTargetContext(), 235 androidx.leanback.test.R.style.Theme_Leanback); 236 237 final PlayerAdapter impl = Mockito.mock(PlayerAdapter.class); 238 when(impl.isPrepared()).thenReturn(true); 239 when(impl.getCurrentPosition()).thenReturn(123L); 240 when(impl.getDuration()).thenReturn(20000L); 241 when(impl.getBufferedPosition()).thenReturn(321L); 242 final PlaybackGlueHost.PlayerCallback hostCallback = Mockito.mock( 243 PlaybackGlueHost.PlayerCallback.class); 244 InstrumentationRegistry.getInstrumentation().runOnMainSync(new Runnable() { 245 @Override 246 public void run() { 247 mGlue = new PlaybackTransportControlGlueImpl(mContext, impl); 248 // fire events before attach to host. 249 impl.getCallback().onBufferingStateChanged(impl, true); 250 impl.getCallback().onVideoSizeChanged(impl, 200, 150); 251 impl.getCallback().onError(impl, 12, "abc"); 252 PlaybackGlueHostImpl host = new PlaybackGlueHostImpl(); 253 host.setPlayerCallback(hostCallback); 254 mGlue.setHost(host); 255 } 256 }); 257 258 // when attach to host, should pass the buffering state, video size and last error message 259 // to the host. 260 Mockito.verify(hostCallback, times(1)).onBufferingStateChanged(true); 261 Mockito.verify(hostCallback, times(1)).onVideoSizeChanged(200, 150); 262 Mockito.verify(hostCallback, times(1)).onError(12, "abc"); 263 Mockito.reset(hostCallback); 264 265 final PlaybackGlueHost.PlayerCallback hostCallback2 = Mockito.mock( 266 PlaybackGlueHost.PlayerCallback.class); 267 InstrumentationRegistry.getInstrumentation().runOnMainSync(new Runnable() { 268 @Override 269 public void run() { 270 PlaybackGlueHostImpl host = new PlaybackGlueHostImpl(); 271 host.setPlayerCallback(hostCallback2); 272 mGlue.setHost(host); 273 } 274 }); 275 276 // when detach from host, should have host stop buffering. 277 Mockito.verify(hostCallback, times(1)).onBufferingStateChanged(false); 278 Mockito.verify(hostCallback, times(0)).onVideoSizeChanged(anyInt(), anyInt()); 279 Mockito.verify(hostCallback, times(0)).onError(anyInt(), anyString()); 280 281 // attach to a different host, buffering state and video size should be saved, one time 282 // error state is not saved. 283 Mockito.verify(hostCallback2, times(1)).onBufferingStateChanged(true); 284 Mockito.verify(hostCallback2, times(1)).onVideoSizeChanged(200, 150); 285 Mockito.verify(hostCallback2, times(0)).onError(anyInt(), anyString()); 286 } 287 288 @Test 289 public void playStateReceivePlayPause() { 290 setupWithMockAdapterAndViewHolder(); 291 playMockAdapter(); 292 293 mGlue.onKey(null, KeyEvent.KEYCODE_MEDIA_PLAY_PAUSE, 294 new KeyEvent(KeyEvent.ACTION_DOWN, KeyEvent.KEYCODE_MEDIA_PLAY_PAUSE)); 295 Mockito.verify(mAdapter, times(1)).pause(); 296 } 297 298 @Test 299 public void playStateReceivePause() { 300 setupWithMockAdapterAndViewHolder(); 301 playMockAdapter(); 302 303 mGlue.onKey(null, KeyEvent.KEYCODE_MEDIA_PAUSE, 304 new KeyEvent(KeyEvent.ACTION_DOWN, KeyEvent.KEYCODE_MEDIA_PAUSE)); 305 Mockito.verify(mAdapter, times(1)).pause(); 306 } 307 308 @Test 309 public void playStateReceivePlay() { 310 setupWithMockAdapterAndViewHolder(); 311 playMockAdapter(); 312 313 mGlue.onKey(null, KeyEvent.KEYCODE_MEDIA_PLAY, 314 new KeyEvent(KeyEvent.ACTION_DOWN, KeyEvent.KEYCODE_MEDIA_PLAY)); 315 Mockito.verify(mAdapter, never()).pause(); 316 } 317 318 @Test 319 public void pauseStateReceivePlayPause() { 320 setupWithMockAdapterAndViewHolder(); 321 322 mGlue.onKey(null, KeyEvent.KEYCODE_MEDIA_PLAY_PAUSE, 323 new KeyEvent(KeyEvent.ACTION_DOWN, KeyEvent.KEYCODE_MEDIA_PLAY_PAUSE)); 324 Mockito.verify(mAdapter, times(1)).play(); 325 } 326 327 @Test 328 public void pauseStateReceivePause() { 329 setupWithMockAdapterAndViewHolder(); 330 331 mGlue.onKey(null, KeyEvent.KEYCODE_MEDIA_PAUSE, 332 new KeyEvent(KeyEvent.ACTION_DOWN, KeyEvent.KEYCODE_MEDIA_PAUSE)); 333 Mockito.verify(mAdapter, never()).play(); 334 } 335 336 @Test 337 public void pauseStateReceivePlay() { 338 setupWithMockAdapterAndViewHolder(); 339 340 mGlue.onKey(null, KeyEvent.KEYCODE_MEDIA_PLAY, 341 new KeyEvent(KeyEvent.ACTION_DOWN, KeyEvent.KEYCODE_MEDIA_PLAY)); 342 Mockito.verify(mAdapter, times(1)).play(); 343 } 344} 345