1 /*
2  * Copyright (C) 2019 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 com.android.systemui.statusbar.notification.row.wrapper;
18 
19 import static org.junit.Assert.assertTrue;
20 import static org.mockito.ArgumentMatchers.argThat;
21 import static org.mockito.Mockito.times;
22 import static org.mockito.Mockito.verify;
23 
24 import android.app.Notification;
25 import android.media.MediaMetadata;
26 import android.media.session.MediaSession;
27 import android.media.session.PlaybackState;
28 import android.testing.AndroidTestingRunner;
29 import android.testing.TestableLooper;
30 import android.testing.TestableLooper.RunWithLooper;
31 import android.view.View;
32 import android.widget.RemoteViews;
33 import android.widget.SeekBar;
34 
35 import androidx.test.filters.SmallTest;
36 
37 import com.android.internal.logging.MetricsLogger;
38 import com.android.internal.logging.nano.MetricsProto.MetricsEvent;
39 import com.android.systemui.R;
40 import com.android.systemui.SysuiTestCase;
41 import com.android.systemui.statusbar.NotificationTestHelper;
42 import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow;
43 
44 import org.junit.Before;
45 import org.junit.Test;
46 import org.junit.runner.RunWith;
47 import org.mockito.Mock;
48 import org.mockito.MockitoAnnotations;
49 
50 
51 @SmallTest
52 @RunWith(AndroidTestingRunner.class)
53 @RunWithLooper
54 public class NotificationMediaTemplateViewWrapperTest extends SysuiTestCase {
55 
56     private ExpandableNotificationRow mRow;
57     private Notification mNotif;
58     private View mView;
59     private NotificationMediaTemplateViewWrapper mWrapper;
60 
61     @Mock
62     private MetricsLogger mMetricsLogger;
63 
64     @Before
setUp()65     public void setUp() {
66         MockitoAnnotations.initMocks(this);
67         com.android.systemui.util.Assert.sMainLooper = TestableLooper.get(this).getLooper();
68 
69         mDependency.injectTestDependency(MetricsLogger.class, mMetricsLogger);
70     }
71 
makeTestNotification(long duration, boolean allowSeeking)72     private void makeTestNotification(long duration, boolean allowSeeking) throws Exception {
73         Notification.Builder builder = new Notification.Builder(mContext)
74                 .setSmallIcon(R.drawable.ic_person)
75                 .setContentTitle("Title")
76                 .setContentText("Text");
77 
78         MediaMetadata metadata = new MediaMetadata.Builder()
79                 .putLong(MediaMetadata.METADATA_KEY_DURATION, duration)
80                 .build();
81         MediaSession session = new MediaSession(mContext, "TEST_CHANNEL");
82         session.setMetadata(metadata);
83 
84         PlaybackState playbackState = new PlaybackState.Builder()
85                 .setActions(allowSeeking ? PlaybackState.ACTION_SEEK_TO : 0)
86                 .build();
87 
88         session.setPlaybackState(playbackState);
89 
90         builder.setStyle(new Notification.MediaStyle()
91                 .setMediaSession(session.getSessionToken())
92         );
93 
94         mNotif = builder.build();
95         assertTrue(mNotif.hasMediaSession());
96 
97         mRow = new NotificationTestHelper(mContext).createRow(mNotif);
98 
99         RemoteViews views = new RemoteViews(mContext.getPackageName(),
100                 com.android.internal.R.layout.notification_template_material_big_media);
101         mView = views.apply(mContext, null);
102         mWrapper = new NotificationMediaTemplateViewWrapper(mContext,
103                 mView, mRow);
104         mWrapper.onContentUpdated(mRow);
105     }
106 
107     @Test
testLogging_NoSeekbar()108     public void testLogging_NoSeekbar() throws Exception {
109         // Media sessions with duration <= 0 should not include a seekbar
110         makeTestNotification(0, false);
111 
112         verify(mMetricsLogger).write(argThat(logMaker ->
113                 logMaker.getCategory() == MetricsEvent.MEDIA_NOTIFICATION_SEEKBAR
114                         && logMaker.getType() == MetricsEvent.TYPE_CLOSE
115         ));
116 
117         verify(mMetricsLogger, times(0)).write(argThat(logMaker ->
118                 logMaker.getCategory() == MetricsEvent.MEDIA_NOTIFICATION_SEEKBAR
119                         && logMaker.getType() == MetricsEvent.TYPE_OPEN
120         ));
121     }
122 
123     @Test
testLogging_HasSeekbarNoScrubber()124     public void testLogging_HasSeekbarNoScrubber() throws Exception {
125         // Media sessions that do not support seeking should have a seekbar, but no scrubber
126         makeTestNotification(1000, false);
127 
128         verify(mMetricsLogger).write(argThat(logMaker ->
129                 logMaker.getCategory() == MetricsEvent.MEDIA_NOTIFICATION_SEEKBAR
130                         && logMaker.getType() == MetricsEvent.TYPE_OPEN
131         ));
132 
133         // Ensure the callback runs at least once
134         mWrapper.mOnUpdateTimerTick.run();
135 
136         verify(mMetricsLogger).write(argThat(logMaker ->
137                 logMaker.getCategory() == MetricsEvent.MEDIA_NOTIFICATION_SEEKBAR
138                 && logMaker.getType() == MetricsEvent.TYPE_DETAIL
139                 && logMaker.getSubtype() == 0
140         ));
141     }
142 
143     @Test
testLogging_HasSeekbarAndScrubber()144     public void testLogging_HasSeekbarAndScrubber() throws Exception {
145         makeTestNotification(1000, true);
146 
147         verify(mMetricsLogger).write(argThat(logMaker ->
148                 logMaker.getCategory() == MetricsEvent.MEDIA_NOTIFICATION_SEEKBAR
149                         && logMaker.getType() == MetricsEvent.TYPE_OPEN
150         ));
151 
152         verify(mMetricsLogger).write(argThat(logMaker ->
153                 logMaker.getCategory() == MetricsEvent.MEDIA_NOTIFICATION_SEEKBAR
154                         && logMaker.getType() == MetricsEvent.TYPE_DETAIL
155                         && logMaker.getSubtype() == 1
156         ));
157     }
158 
159     @Test
testLogging_UpdateSeekbar()160     public void testLogging_UpdateSeekbar() throws Exception {
161         makeTestNotification(1000, true);
162 
163         SeekBar seekbar = mView.findViewById(
164                 com.android.internal.R.id.notification_media_progress_bar);
165         assertTrue(seekbar != null);
166 
167         mWrapper.mSeekListener.onStopTrackingTouch(seekbar);
168 
169         verify(mMetricsLogger).write(argThat(logMaker ->
170                 logMaker.getCategory() == MetricsEvent.MEDIA_NOTIFICATION_SEEKBAR
171                         && logMaker.getType() == MetricsEvent.TYPE_UPDATE));
172     }
173 }
174