blob: 66703ee6321911e40af0b493f4c8490e54d610d5 [file] [log] [blame]
Selim Cinek1a48bab2017-02-17 19:38:40 -08001/*
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 android.app.Notification;
20import android.content.Context;
21import android.service.notification.StatusBarNotification;
22import android.util.Log;
23import android.view.View;
24import android.widget.RemoteViews;
25
26import com.android.systemui.statusbar.ExpandableNotificationRow;
27import com.android.systemui.statusbar.NotificationContentView;
28import com.android.systemui.statusbar.NotificationData;
29import com.android.systemui.statusbar.phone.StatusBar;
30
31import java.util.Objects;
32
33/**
34 * A utility that inflates the right kind of contentView based on the state
35 */
36public class NotificationInflater {
37
Selim Cinekc478f902017-02-22 20:55:44 -080038 private static final int FLAG_REINFLATE_ALL = ~0;
39 private static final int FLAG_REINFLATE_CONTENT_VIEW = 1<<0;
40 private static final int FLAG_REINFLATE_EXPANDED_VIEW = 1<<1;
41 private static final int FLAG_REINFLATE_HEADS_UP_VIEW = 1<<2;
42 private static final int FLAG_REINFLATE_PUBLIC_VIEW = 1<<3;
43 private static final int FLAG_REINFLATE_AMBIENT_VIEW = 1<<4;
44
Selim Cinek1a48bab2017-02-17 19:38:40 -080045 private final ExpandableNotificationRow mRow;
46 private boolean mIsLowPriority;
47 private boolean mUsesIncreasedHeight;
48 private boolean mUsesIncreasedHeadsUpHeight;
49 private RemoteViews.OnClickHandler mRemoteViewClickHandler;
Selim Cinekc478f902017-02-22 20:55:44 -080050 private boolean mIsChildInGroup;
51 private InflationExceptionHandler mInflateExceptionHandler;
Selim Cinek1a48bab2017-02-17 19:38:40 -080052
53 public NotificationInflater(ExpandableNotificationRow row) {
54 mRow = row;
55 }
56
57 public void setIsLowPriority(boolean isLowPriority) {
58 mIsLowPriority = isLowPriority;
59 }
60
Selim Cinekc478f902017-02-22 20:55:44 -080061 /**
62 * Set whether the notification is a child in a group
63 *
64 * @return whether the view was re-inflated
65 */
66 public boolean setIsChildInGroup(boolean childInGroup) {
67 if (childInGroup != mIsChildInGroup) {
68 mIsChildInGroup = childInGroup;
69 if (mIsLowPriority) {
70 try {
71 int flags = FLAG_REINFLATE_CONTENT_VIEW | FLAG_REINFLATE_EXPANDED_VIEW;
72 inflateNotificationViews(flags);
73 } catch (InflationException e) {
74 mInflateExceptionHandler.handleInflationException(
75 mRow.getStatusBarNotification(), e);
76 }
77 }
78 return true;
79 }
80 return false;
81 }
82
Selim Cinek1a48bab2017-02-17 19:38:40 -080083 public void setUsesIncreasedHeight(boolean usesIncreasedHeight) {
84 mUsesIncreasedHeight = usesIncreasedHeight;
85 }
86
87 public void setUsesIncreasedHeadsUpHeight(boolean usesIncreasedHeight) {
88 mUsesIncreasedHeadsUpHeight = usesIncreasedHeight;
89 }
90
91 public void setRemoteViewClickHandler(RemoteViews.OnClickHandler remoteViewClickHandler) {
92 mRemoteViewClickHandler = remoteViewClickHandler;
93 }
94
95 public void inflateNotificationViews() throws InflationException {
Selim Cinekc478f902017-02-22 20:55:44 -080096 inflateNotificationViews(FLAG_REINFLATE_ALL);
97 }
98
99 /**
100 * reinflate all views for the specified flags
101 * @param reInflateFlags flags which views should be reinflated. Use {@link #FLAG_REINFLATE_ALL}
102 * to reinflate all of views.
103 * @throws InflationException
104 */
105 private void inflateNotificationViews(int reInflateFlags)
106 throws InflationException {
Selim Cinek1a48bab2017-02-17 19:38:40 -0800107 NotificationData.Entry entry = mRow.getEntry();
108 StatusBarNotification sbn = entry.notification;
109 Context context = mRow.getContext();
110 NotificationContentView privateLayout = mRow.getPrivateLayout();
111 try {
112 final Notification.Builder recoveredBuilder
113 = Notification.Builder.recoverBuilder(context, sbn.getNotification());
Selim Cinekc478f902017-02-22 20:55:44 -0800114 boolean isLowPriority = mIsLowPriority && !mIsChildInGroup;
115 if ((reInflateFlags & FLAG_REINFLATE_CONTENT_VIEW) != 0) {
116 final RemoteViews newContentView = createContentView(recoveredBuilder,
117 isLowPriority, mUsesIncreasedHeadsUpHeight);
118 if (!compareRemoteViews(newContentView,
119 entry.cachedContentView)) {
120 View contentViewLocal = newContentView.apply(
Selim Cinek1a48bab2017-02-17 19:38:40 -0800121 sbn.getPackageContext(context),
122 privateLayout,
123 mRemoteViewClickHandler);
Selim Cinekc478f902017-02-22 20:55:44 -0800124 contentViewLocal.setIsRootNamespace(true);
125 privateLayout.setContractedChild(contentViewLocal);
Selim Cinek1a48bab2017-02-17 19:38:40 -0800126 } else {
Selim Cinekc478f902017-02-22 20:55:44 -0800127 newContentView.reapply(sbn.getPackageContext(context),
128 privateLayout.getContractedChild(),
Selim Cinek1a48bab2017-02-17 19:38:40 -0800129 mRemoteViewClickHandler);
130 }
Selim Cinekc478f902017-02-22 20:55:44 -0800131 entry.cachedContentView = newContentView;
Selim Cinek1a48bab2017-02-17 19:38:40 -0800132 }
Selim Cinek1a48bab2017-02-17 19:38:40 -0800133
Selim Cinekc478f902017-02-22 20:55:44 -0800134 if ((reInflateFlags & FLAG_REINFLATE_EXPANDED_VIEW) != 0) {
135 final RemoteViews newBigContentView = createBigContentView(
136 recoveredBuilder, isLowPriority);
137 if (newBigContentView != null) {
138 if (!compareRemoteViews(newBigContentView, entry.cachedBigContentView)) {
139 View bigContentViewLocal = newBigContentView.apply(
140 sbn.getPackageContext(context),
141 privateLayout,
142 mRemoteViewClickHandler);
143 bigContentViewLocal.setIsRootNamespace(true);
144 privateLayout.setExpandedChild(bigContentViewLocal);
145 } else {
146 newBigContentView.reapply(sbn.getPackageContext(context),
147 privateLayout.getExpandedChild(),
148 mRemoteViewClickHandler);
149 }
150 } else if (entry.cachedBigContentView != null) {
151 privateLayout.setExpandedChild(null);
152 }
153 entry.cachedBigContentView = newBigContentView;
154 mRow.setExpandable(newBigContentView != null);
155 }
156
157 if ((reInflateFlags & FLAG_REINFLATE_HEADS_UP_VIEW) != 0) {
158 final RemoteViews newHeadsUpContentView =
159 recoveredBuilder.createHeadsUpContentView(mUsesIncreasedHeight);
160 if (newHeadsUpContentView != null) {
161 if (!compareRemoteViews(newHeadsUpContentView,
162 entry.cachedHeadsUpContentView)) {
163 View headsUpContentViewLocal = newHeadsUpContentView.apply(
164 sbn.getPackageContext(context),
165 privateLayout,
166 mRemoteViewClickHandler);
167 headsUpContentViewLocal.setIsRootNamespace(true);
168 privateLayout.setHeadsUpChild(headsUpContentViewLocal);
169 } else {
170 newHeadsUpContentView.reapply(sbn.getPackageContext(context),
171 privateLayout.getHeadsUpChild(),
172 mRemoteViewClickHandler);
173 }
174 } else if (entry.cachedHeadsUpContentView != null) {
175 privateLayout.setHeadsUpChild(null);
176 }
177 entry.cachedHeadsUpContentView = newHeadsUpContentView;
178 }
179
180 if ((reInflateFlags & FLAG_REINFLATE_PUBLIC_VIEW) != 0) {
181 NotificationContentView publicLayout = mRow.getPublicLayout();
182 final RemoteViews newPublicNotification
183 = recoveredBuilder.makePublicContentView();
184 if (!compareRemoteViews(newPublicNotification, entry.cachedPublicContentView)) {
185 View publicContentView = newPublicNotification.apply(
186 sbn.getPackageContext(context),
187 publicLayout,
188 mRemoteViewClickHandler);
189 publicContentView.setIsRootNamespace(true);
190 publicLayout.setContractedChild(publicContentView);
191 } else {
192 newPublicNotification.reapply(sbn.getPackageContext(context),
193 publicLayout.getContractedChild(),
194 mRemoteViewClickHandler);
195 }
196 entry.cachedPublicContentView = newPublicNotification;
197 }
198
199 if ((reInflateFlags & FLAG_REINFLATE_AMBIENT_VIEW) != 0) {
200 final RemoteViews newAmbientNotification
201 = recoveredBuilder.makeAmbientNotification();
202 if (!compareRemoteViews(newAmbientNotification, entry.cachedAmbientContentView)) {
203 View ambientContentView = newAmbientNotification.apply(
Selim Cinek1a48bab2017-02-17 19:38:40 -0800204 sbn.getPackageContext(context),
205 privateLayout,
206 mRemoteViewClickHandler);
Selim Cinekc478f902017-02-22 20:55:44 -0800207 ambientContentView.setIsRootNamespace(true);
208 privateLayout.setAmbientChild(ambientContentView);
Selim Cinek1a48bab2017-02-17 19:38:40 -0800209 } else {
Selim Cinekc478f902017-02-22 20:55:44 -0800210 newAmbientNotification.reapply(sbn.getPackageContext(context),
211 privateLayout.getAmbientChild(),
Selim Cinek1a48bab2017-02-17 19:38:40 -0800212 mRemoteViewClickHandler);
213 }
Selim Cinekc478f902017-02-22 20:55:44 -0800214 entry.cachedAmbientContentView = newAmbientNotification;
Selim Cinek1a48bab2017-02-17 19:38:40 -0800215 }
Selim Cinek1a48bab2017-02-17 19:38:40 -0800216
217 } catch (RuntimeException e) {
218 final String ident = sbn.getPackageName() + "/0x" + Integer.toHexString(sbn.getId());
219 Log.e(StatusBar.TAG, "couldn't inflate view for notification " + ident, e);
220 throw new InflationException("Couldn't inflate contentViews");
221 }
222 }
223
224 private RemoteViews createBigContentView(Notification.Builder builder,
225 boolean isLowPriority) {
226 RemoteViews bigContentView = builder.createBigContentView();
227 if (bigContentView != null) {
228 return bigContentView;
229 }
230 if (isLowPriority) {
231 RemoteViews contentView = builder.createContentView();
232 Notification.Builder.makeHeaderExpanded(contentView);
233 return contentView;
234 }
235 return null;
236 }
237
238 private RemoteViews createContentView(Notification.Builder builder,
239 boolean isLowPriority, boolean useLarge) {
240 if (isLowPriority) {
241 return builder.makeLowPriorityContentView(false /* useRegularSubtext */);
242 }
243 return builder.createContentView(useLarge);
244 }
245
246 // Returns true if the RemoteViews are the same.
247 private boolean compareRemoteViews(final RemoteViews a, final RemoteViews b) {
248 return (a == null && b == null) ||
249 (a != null && b != null
250 && b.getPackage() != null
251 && a.getPackage() != null
252 && a.getPackage().equals(b.getPackage())
253 && a.getLayoutId() == b.getLayoutId());
254 }
Selim Cinekc478f902017-02-22 20:55:44 -0800255
256 public void setInflateExceptionHandler(InflationExceptionHandler inflateExceptionHandler) {
257 mInflateExceptionHandler = inflateExceptionHandler;
258 }
259
260 public interface InflationExceptionHandler {
261 void handleInflationException(StatusBarNotification notification, InflationException e);
262 }
Selim Cinek1a48bab2017-02-17 19:38:40 -0800263 public void onDensityOrFontScaleChanged() {
264 NotificationData.Entry entry = mRow.getEntry();
265 entry.cachedAmbientContentView = null;
266 entry.cachedBigContentView = null;
267 entry.cachedContentView = null;
268 entry.cachedHeadsUpContentView = null;
269 entry.cachedPublicContentView = null;
270 try {
271 inflateNotificationViews();
272 } catch (InflationException e) {
273 mInflateExceptionHandler.handleInflationException(
274 mRow.getStatusBarNotification(), e);
275 }
276 }
277
278}