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.times;
25import static org.mockito.Mockito.when;
26
27import android.content.Context;
28import android.support.test.InstrumentationRegistry;
29import android.support.test.filters.SmallTest;
30import android.view.ContextThemeWrapper;
31import android.view.ViewGroup;
32import android.widget.FrameLayout;
33import android.widget.LinearLayout;
34
35import androidx.leanback.widget.PlaybackControlsRow;
36import androidx.leanback.widget.PlaybackControlsRowPresenter;
37import androidx.leanback.widget.PlaybackRowPresenter;
38import androidx.leanback.widget.RowPresenter;
39
40import org.junit.Test;
41import org.mockito.Mockito;
42
43@SmallTest
44public class PlaybackBannerControlGlueTest {
45
46    public static class PlayerAdapterSample extends PlayerAdapter {
47        @Override
48        public void play() {
49        }
50
51        @Override
52        public void pause() {
53        }
54    }
55
56    public static class PlaybackBannerControlGlueImpl
57            extends PlaybackBannerControlGlue {
58        public PlaybackBannerControlGlueImpl(Context context) {
59            super(context, new int[] {1, 2 , 3, 4, 5}, new PlayerAdapterSample());
60        }
61
62        public PlaybackBannerControlGlueImpl(Context context, PlayerAdapter impl) {
63            super(context, new int[] {1, 2 , 3, 4, 5}, impl);
64        }
65    }
66
67    Context mContext;
68    PlaybackBannerControlGlueImpl mGlue;
69    PlaybackControlsRowPresenter.ViewHolder mViewHolder;
70
71    @Test
72    public void usingDefaultRowAndPresenter() {
73        mContext = InstrumentationRegistry.getInstrumentation().getTargetContext();
74        InstrumentationRegistry.getInstrumentation().runOnMainSync(new Runnable() {
75            @Override
76            public void run() {
77                mGlue = new PlaybackBannerControlGlueImpl(mContext);
78            }
79        });
80        PlaybackGlueHostImpl host = new PlaybackGlueHostImpl();
81
82        mGlue.setHost(host);
83        assertSame(mGlue, host.mGlue);
84        assertSame(host, mGlue.getHost());
85        assertTrue(host.mPlaybackRowPresenter instanceof PlaybackControlsRowPresenter);
86        assertTrue(host.mRow instanceof PlaybackControlsRow);
87
88    }
89    @Test
90    public void customRowPresenter() {
91        mContext = InstrumentationRegistry.getInstrumentation().getTargetContext();
92        InstrumentationRegistry.getInstrumentation().runOnMainSync(new Runnable() {
93            @Override
94            public void run() {
95                mGlue = new PlaybackBannerControlGlueImpl(mContext);
96            }
97        });
98        PlaybackRowPresenter presenter = new PlaybackRowPresenter() {
99            @Override
100            protected RowPresenter.ViewHolder createRowViewHolder(ViewGroup parent) {
101                return new RowPresenter.ViewHolder(new LinearLayout(parent.getContext()));
102            }
103        };
104        mGlue.setPlaybackRowPresenter(presenter);
105        PlaybackGlueHostImpl host = new PlaybackGlueHostImpl();
106
107        mGlue.setHost(host);
108        assertSame(mGlue, host.mGlue);
109        assertSame(host, mGlue.getHost());
110        assertSame(host.mPlaybackRowPresenter, presenter);
111        assertTrue(host.mRow instanceof PlaybackControlsRow);
112
113    }
114
115    @Test
116    public void customControlsRow() {
117        mContext = InstrumentationRegistry.getInstrumentation().getTargetContext();
118        InstrumentationRegistry.getInstrumentation().runOnMainSync(new Runnable() {
119            @Override
120            public void run() {
121                mGlue = new PlaybackBannerControlGlueImpl(mContext);
122            }
123        });
124        PlaybackControlsRow row = new PlaybackControlsRow(mContext);
125        mGlue.setControlsRow(row);
126        PlaybackGlueHostImpl host = new PlaybackGlueHostImpl();
127
128        mGlue.setHost(host);
129        assertSame(mGlue, host.mGlue);
130        assertSame(host, mGlue.getHost());
131        assertTrue(host.mPlaybackRowPresenter instanceof PlaybackControlsRowPresenter);
132        assertSame(host.mRow, row);
133
134    }
135
136    @Test
137    public void customRowAndPresenter() {
138        mContext = InstrumentationRegistry.getInstrumentation().getTargetContext();
139        InstrumentationRegistry.getInstrumentation().runOnMainSync(new Runnable() {
140            @Override
141            public void run() {
142                mGlue = new PlaybackBannerControlGlueImpl(mContext);
143            }
144        });
145        PlaybackControlsRow row = new PlaybackControlsRow(mContext);
146        mGlue.setControlsRow(row);
147        PlaybackRowPresenter presenter = new PlaybackRowPresenter() {
148            @Override
149            protected RowPresenter.ViewHolder createRowViewHolder(ViewGroup parent) {
150                return new RowPresenter.ViewHolder(new LinearLayout(parent.getContext()));
151            }
152        };
153        mGlue.setPlaybackRowPresenter(presenter);
154        PlaybackGlueHostImpl host = new PlaybackGlueHostImpl();
155
156        mGlue.setHost(host);
157        assertSame(mGlue, host.mGlue);
158        assertSame(host, mGlue.getHost());
159        assertSame(host.mPlaybackRowPresenter, presenter);
160        assertSame(host.mRow, row);
161
162    }
163
164    @Test
165    public void playerAdapterTest() {
166        mContext = new ContextThemeWrapper(
167                InstrumentationRegistry.getInstrumentation().getTargetContext(),
168                androidx.leanback.test.R.style.Theme_Leanback);
169
170        final PlayerAdapter impl = Mockito.mock(PlayerAdapter.class);
171        when(impl.isPrepared()).thenReturn(true);
172        when(impl.getCurrentPosition()).thenReturn(123L);
173        when(impl.getDuration()).thenReturn(20000L);
174        when(impl.getBufferedPosition()).thenReturn(321L);
175        InstrumentationRegistry.getInstrumentation().runOnMainSync(new Runnable() {
176            @Override
177            public void run() {
178                mGlue = new PlaybackBannerControlGlueImpl(mContext, impl);
179                PlaybackGlueHostImpl host = new PlaybackGlueHostImpl();
180                mGlue.setHost(host);
181
182                PlaybackControlsRowPresenter presenter = (PlaybackControlsRowPresenter)
183                        mGlue.getPlaybackRowPresenter();
184                FrameLayout parent = new FrameLayout(mContext);
185                mViewHolder = (PlaybackControlsRowPresenter.ViewHolder)
186                        presenter.onCreateViewHolder(parent);
187                presenter.onBindViewHolder(mViewHolder, mGlue.getControlsRow());
188            }
189        });
190
191
192        mGlue.play();
193        Mockito.verify(impl, times(1)).play();
194        mGlue.pause();
195        Mockito.verify(impl, times(1)).pause();
196        mGlue.seekTo(123L);
197        // one call for play() and one call for seekTo()
198        Mockito.verify(impl, times(2)).seekTo(123L);
199        assertEquals(123L, mGlue.getCurrentPosition());
200        assertEquals(20000L, mGlue.getDuration());
201        assertEquals(321L, mGlue.getBufferedPosition());
202
203        assertSame(mGlue.mAdapterCallback, impl.getCallback());
204
205        when(impl.getCurrentPosition()).thenReturn(124L);
206        impl.getCallback().onCurrentPositionChanged(impl);
207        assertEquals(124L, mGlue.getControlsRow().getCurrentPosition());
208
209        when(impl.getBufferedPosition()).thenReturn(333L);
210        impl.getCallback().onBufferedPositionChanged(impl);
211        assertEquals(333L, mGlue.getControlsRow().getBufferedPosition());
212
213        when(impl.getDuration()).thenReturn((long) (Integer.MAX_VALUE) * 2);
214        impl.getCallback().onDurationChanged(impl);
215        assertEquals((long) (Integer.MAX_VALUE) * 2, mGlue.getControlsRow().getDuration());
216
217    }
218
219    @Test
220    public void savePlayerAdapterEventBeforeAttachToHost() {
221        mContext = new ContextThemeWrapper(
222                InstrumentationRegistry.getInstrumentation().getTargetContext(),
223                androidx.leanback.test.R.style.Theme_Leanback);
224
225        final PlayerAdapter impl = Mockito.mock(PlayerAdapter.class);
226        when(impl.isPrepared()).thenReturn(true);
227        when(impl.getCurrentPosition()).thenReturn(123L);
228        when(impl.getDuration()).thenReturn(20000L);
229        when(impl.getBufferedPosition()).thenReturn(321L);
230        final PlaybackGlueHost.PlayerCallback hostCallback = Mockito.mock(
231                PlaybackGlueHost.PlayerCallback.class);
232        InstrumentationRegistry.getInstrumentation().runOnMainSync(new Runnable() {
233            @Override
234            public void run() {
235                mGlue = new PlaybackBannerControlGlueImpl(mContext, impl);
236                // fire events before attach to host.
237                impl.getCallback().onBufferingStateChanged(impl, true);
238                impl.getCallback().onVideoSizeChanged(impl, 200, 150);
239                impl.getCallback().onError(impl, 12, "abc");
240                PlaybackGlueHostImpl host = new PlaybackGlueHostImpl();
241                host.setPlayerCallback(hostCallback);
242                mGlue.setHost(host);
243            }
244        });
245
246        // when attach to host, should pass the buffering state, video size and last error message
247        // to the host.
248        Mockito.verify(hostCallback, times(1)).onBufferingStateChanged(true);
249        Mockito.verify(hostCallback, times(1)).onVideoSizeChanged(200, 150);
250        Mockito.verify(hostCallback, times(1)).onError(12, "abc");
251        Mockito.reset(hostCallback);
252
253        final PlaybackGlueHost.PlayerCallback hostCallback2 = Mockito.mock(
254                PlaybackGlueHost.PlayerCallback.class);
255        InstrumentationRegistry.getInstrumentation().runOnMainSync(new Runnable() {
256            @Override
257            public void run() {
258                PlaybackGlueHostImpl host = new PlaybackGlueHostImpl();
259                host.setPlayerCallback(hostCallback2);
260                mGlue.setHost(host);
261            }
262        });
263
264        // when detach from host, should have host stop buffering.
265        Mockito.verify(hostCallback, times(1)).onBufferingStateChanged(false);
266        Mockito.verify(hostCallback, times(0)).onVideoSizeChanged(anyInt(), anyInt());
267        Mockito.verify(hostCallback, times(0)).onError(anyInt(), anyString());
268
269        // attach to a different host, buffering state and video size should be saved, one time
270        // error state is not saved.
271        Mockito.verify(hostCallback2, times(1)).onBufferingStateChanged(true);
272        Mockito.verify(hostCallback2, times(1)).onVideoSizeChanged(200, 150);
273        Mockito.verify(hostCallback2, times(0)).onError(anyInt(), anyString());
274    }
275
276}
277