blob: 54a96cd767150a9c9386c7a429d9619f03a0b759 [file] [log] [blame]
Selim Cinek2630dc72017-04-20 15:16:10 -07001/*
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 com.android.systemui.statusbar.notification;
18
19import static com.android.systemui.statusbar.notification.NotificationInflater.FLAG_REINFLATE_ALL;
20
21import static org.mockito.Mockito.spy;
22import static org.mockito.Mockito.times;
23import static org.mockito.Mockito.verify;
24
25import android.app.Notification;
26import android.content.Context;
Selim Cinekd246bed2017-06-19 16:58:35 -070027import android.os.CancellationSignal;
28import android.os.Handler;
29import android.os.Looper;
Selim Cinek2630dc72017-04-20 15:16:10 -070030import android.service.notification.StatusBarNotification;
Selim Cinek01d3da62017-04-28 15:03:48 -070031import android.support.test.annotation.UiThreadTest;
Geoffrey Pitsch70950de2017-05-23 17:13:38 -040032import android.support.test.filters.FlakyTest;
Selim Cinek2630dc72017-04-20 15:16:10 -070033import android.support.test.filters.SmallTest;
34import android.support.test.runner.AndroidJUnit4;
Selim Cinekd246bed2017-06-19 16:58:35 -070035import android.view.View;
36import android.view.ViewGroup;
Selim Cinek2630dc72017-04-20 15:16:10 -070037import android.widget.RemoteViews;
38
39import com.android.systemui.R;
Jason Monkfba8faf2017-05-23 10:42:59 -040040import com.android.systemui.SysuiTestCase;
Selim Cinek2630dc72017-04-20 15:16:10 -070041import com.android.systemui.statusbar.ExpandableNotificationRow;
Jason Monkb05395f2017-07-11 10:05:03 -040042import com.android.systemui.statusbar.InflationTask;
Selim Cinek2630dc72017-04-20 15:16:10 -070043import com.android.systemui.statusbar.NotificationData;
44import com.android.systemui.statusbar.NotificationTestHelper;
45
46import org.junit.Assert;
47import org.junit.Before;
Geoffrey Pitsch351a3212017-05-22 15:20:20 -040048import org.junit.Ignore;
Selim Cinek2630dc72017-04-20 15:16:10 -070049import org.junit.Test;
50import org.junit.runner.RunWith;
51
Selim Cinekd246bed2017-06-19 16:58:35 -070052import java.util.HashMap;
Selim Cinek2630dc72017-04-20 15:16:10 -070053import java.util.concurrent.CountDownLatch;
Selim Cinekd246bed2017-06-19 16:58:35 -070054import java.util.concurrent.Executor;
Selim Cinek2630dc72017-04-20 15:16:10 -070055
56@SmallTest
57@RunWith(AndroidJUnit4.class)
Jason Monkfba8faf2017-05-23 10:42:59 -040058public class NotificationInflaterTest extends SysuiTestCase {
Selim Cinek2630dc72017-04-20 15:16:10 -070059
Selim Cinek2630dc72017-04-20 15:16:10 -070060 private NotificationInflater mNotificationInflater;
61 private Notification.Builder mBuilder;
62 private ExpandableNotificationRow mRow;
63
64 @Before
65 public void setUp() throws Exception {
Selim Cinek2630dc72017-04-20 15:16:10 -070066 mBuilder = new Notification.Builder(mContext).setSmallIcon(
67 R.drawable.ic_person)
68 .setContentTitle("Title")
69 .setContentText("Text")
70 .setStyle(new Notification.BigTextStyle().bigText("big text"));
71 ExpandableNotificationRow row = new NotificationTestHelper(mContext).createRow(
72 mBuilder.build());
73 mRow = spy(row);
74 mNotificationInflater = new NotificationInflater(mRow);
75 mNotificationInflater.setInflationCallback(new NotificationInflater.InflationCallback() {
76 @Override
77 public void handleInflationException(StatusBarNotification notification,
Selim Cinek01d3da62017-04-28 15:03:48 -070078 Exception e) {
Selim Cinek2630dc72017-04-20 15:16:10 -070079 }
80
81 @Override
82 public void onAsyncInflationFinished(NotificationData.Entry entry) {
83 }
84 });
85 }
86
87 @Test
Selim Cinek01d3da62017-04-28 15:03:48 -070088 @UiThreadTest
Selim Cinek2630dc72017-04-20 15:16:10 -070089 public void testIncreasedHeadsUpBeingUsed() {
90 mNotificationInflater.setUsesIncreasedHeadsUpHeight(true);
91 Notification.Builder builder = spy(mBuilder);
92 mNotificationInflater.inflateNotificationViews(FLAG_REINFLATE_ALL, builder, mContext);
93 verify(builder).createHeadsUpContentView(true);
94 }
95
96 @Test
Selim Cinek01d3da62017-04-28 15:03:48 -070097 @UiThreadTest
Selim Cinek2630dc72017-04-20 15:16:10 -070098 public void testIncreasedHeightBeingUsed() {
99 mNotificationInflater.setUsesIncreasedHeight(true);
100 Notification.Builder builder = spy(mBuilder);
101 mNotificationInflater.inflateNotificationViews(FLAG_REINFLATE_ALL, builder, mContext);
102 verify(builder).createContentView(true);
103 }
104
105 @Test
106 public void testInflationCallsUpdated() throws Exception {
107 runThenWaitForInflation(() -> mNotificationInflater.inflateNotificationViews(),
108 mNotificationInflater);
109 verify(mRow).onNotificationUpdated();
110 }
111
112 @Test
113 public void testInflationCallsOnlyRightMethod() throws Exception {
114 mRow.getPrivateLayout().removeAllViews();
115 mRow.getEntry().cachedBigContentView = null;
116 runThenWaitForInflation(() -> mNotificationInflater.inflateNotificationViews(
117 NotificationInflater.FLAG_REINFLATE_EXPANDED_VIEW), mNotificationInflater);
118 Assert.assertTrue(mRow.getPrivateLayout().getChildCount() == 1);
119 Assert.assertTrue(mRow.getPrivateLayout().getChildAt(0)
120 == mRow.getPrivateLayout().getExpandedChild());
121 verify(mRow).onNotificationUpdated();
122 }
123
124 @Test
125 public void testInflationThrowsErrorDoesntCallUpdated() throws Exception {
126 mRow.getPrivateLayout().removeAllViews();
127 mRow.getStatusBarNotification().getNotification().contentView
Selim Cinekdc1231c2017-04-27 17:30:50 -0700128 = new RemoteViews(mContext.getPackageName(), R.layout.status_bar);
Selim Cinek2630dc72017-04-20 15:16:10 -0700129 runThenWaitForInflation(() -> mNotificationInflater.inflateNotificationViews(),
130 true /* expectingException */, mNotificationInflater);
131 Assert.assertTrue(mRow.getPrivateLayout().getChildCount() == 0);
132 verify(mRow, times(0)).onNotificationUpdated();
133 }
134
Selim Cinekdc1231c2017-04-27 17:30:50 -0700135 @Test
136 public void testAsyncTaskRemoved() throws Exception {
Selim Cinek0f66a4c2017-04-28 19:26:28 -0700137 mRow.getEntry().abortTask();
Selim Cinekdc1231c2017-04-27 17:30:50 -0700138 runThenWaitForInflation(() -> mNotificationInflater.inflateNotificationViews(),
139 mNotificationInflater);
Selim Cinek67ff2482017-05-25 10:27:28 -0700140 verify(mRow).onNotificationUpdated();
141 }
142
143 @Test
144 public void testRemovedNotInflated() throws Exception {
145 mRow.setRemoved();
146 mNotificationInflater.inflateNotificationViews();
147 Assert.assertNull(mRow.getEntry().getRunningTask());
148 }
149
Selim Cinekd246bed2017-06-19 16:58:35 -0700150 @Test
Jason Monkb05395f2017-07-11 10:05:03 -0400151 @Ignore
Selim Cinekd246bed2017-06-19 16:58:35 -0700152 public void testInflationIsRetriedIfAsyncFails() throws Exception {
153 NotificationInflater.InflationProgress result =
154 new NotificationInflater.InflationProgress();
155 result.packageContext = mContext;
156 CountDownLatch countDownLatch = new CountDownLatch(1);
157 NotificationInflater.applyRemoteView(result,
158 NotificationInflater.FLAG_REINFLATE_EXPANDED_VIEW, 0, mRow,
159 false /* redactAmbient */, true /* isNewView */, new RemoteViews.OnClickHandler(),
160 new NotificationInflater.InflationCallback() {
161 @Override
162 public void handleInflationException(StatusBarNotification notification,
163 Exception e) {
164 countDownLatch.countDown();
165 throw new RuntimeException("No Exception expected");
166 }
167
168 @Override
169 public void onAsyncInflationFinished(NotificationData.Entry entry) {
170 countDownLatch.countDown();
171 }
172 }, mRow.getEntry(), mRow.getPrivateLayout(), null, null, new HashMap<>(),
173 new NotificationInflater.ApplyCallback() {
174 @Override
175 public void setResultView(View v) {
176 }
177
178 @Override
179 public RemoteViews getRemoteView() {
180 return new AsyncFailRemoteView(mContext.getPackageName(),
181 R.layout.custom_view_dark);
182 }
183 });
184 countDownLatch.await();
185 }
Selim Cinek67ff2482017-05-25 10:27:28 -0700186
187 @Test
188 public void testSupersedesExistingTask() throws Exception {
189 mNotificationInflater.inflateNotificationViews();
190 mNotificationInflater.setIsLowPriority(true);
191 mNotificationInflater.setIsChildInGroup(true);
192 InflationTask runningTask = mRow.getEntry().getRunningTask();
193 NotificationInflater.AsyncInflationTask asyncInflationTask =
194 (NotificationInflater.AsyncInflationTask) runningTask;
195 Assert.assertSame("Successive inflations don't inherit the previous flags!",
196 asyncInflationTask.getReInflateFlags(),
197 NotificationInflater.FLAG_REINFLATE_ALL);
198 runningTask.abort();
Selim Cinekdc1231c2017-04-27 17:30:50 -0700199 }
200
Selim Cinekfc8073c2017-08-16 17:50:20 -0700201 @Test
202 public void doesntReapplyDisallowedRemoteView() throws Exception {
203 mBuilder.setStyle(new Notification.MediaStyle());
204 RemoteViews mediaView = mBuilder.createContentView();
205 mBuilder.setStyle(new Notification.DecoratedCustomViewStyle());
206 mBuilder.setCustomContentView(new RemoteViews(getContext().getPackageName(),
207 R.layout.custom_view_dark));
208 RemoteViews decoratedMediaView = mBuilder.createContentView();
209 Assert.assertFalse("The decorated media style doesn't allow a view to be reapplied!",
210 NotificationInflater.canReapplyRemoteView(mediaView, decoratedMediaView));
211 }
212
Selim Cinek2630dc72017-04-20 15:16:10 -0700213 public static void runThenWaitForInflation(Runnable block,
214 NotificationInflater inflater) throws Exception {
215 runThenWaitForInflation(block, false /* expectingException */, inflater);
216 }
217
218 private static void runThenWaitForInflation(Runnable block, boolean expectingException,
219 NotificationInflater inflater) throws Exception {
220 com.android.systemui.util.Assert.isNotMainThread();
221 CountDownLatch countDownLatch = new CountDownLatch(1);
222 final ExceptionHolder exceptionHolder = new ExceptionHolder();
223 inflater.setInflationCallback(new NotificationInflater.InflationCallback() {
224 @Override
225 public void handleInflationException(StatusBarNotification notification,
Selim Cinek01d3da62017-04-28 15:03:48 -0700226 Exception e) {
Selim Cinek2630dc72017-04-20 15:16:10 -0700227 if (!expectingException) {
228 exceptionHolder.setException(e);
229 }
230 countDownLatch.countDown();
231 }
232
233 @Override
234 public void onAsyncInflationFinished(NotificationData.Entry entry) {
235 if (expectingException) {
236 exceptionHolder.setException(new RuntimeException(
237 "Inflation finished even though there should be an error"));
238 }
239 countDownLatch.countDown();
240 }
241 });
242 block.run();
Selim Cinek454a5fc42017-05-11 16:56:55 -0700243 countDownLatch.await();
Selim Cinek2630dc72017-04-20 15:16:10 -0700244 if (exceptionHolder.mException != null) {
245 throw exceptionHolder.mException;
246 }
247 }
248
249 private static class ExceptionHolder {
250 private Exception mException;
251
252 public void setException(Exception exception) {
253 mException = exception;
254 }
255 }
Selim Cinekd246bed2017-06-19 16:58:35 -0700256
257 private class AsyncFailRemoteView extends RemoteViews {
258 Handler mHandler = new Handler(Looper.getMainLooper());
259
260 public AsyncFailRemoteView(String packageName, int layoutId) {
261 super(packageName, layoutId);
262 }
263
264 @Override
265 public View apply(Context context, ViewGroup parent) {
266 return super.apply(context, parent);
267 }
268
269 @Override
270 public CancellationSignal applyAsync(Context context, ViewGroup parent, Executor executor,
271 OnViewAppliedListener listener, OnClickHandler handler) {
272 mHandler.post(() -> listener.onError(new RuntimeException("Failed to inflate async")));
273 return new CancellationSignal();
274 }
275
276 @Override
277 public CancellationSignal applyAsync(Context context, ViewGroup parent, Executor executor,
278 OnViewAppliedListener listener) {
279 return applyAsync(context, parent, executor, listener, null);
280 }
281 }
Selim Cinek2630dc72017-04-20 15:16:10 -0700282}