blob: e7948b5149a0bb7ff7ceb297008035bd1e3217e3 [file] [log] [blame]
Mady Mellordea7ecf2018-12-10 15:47:40 -08001/*
2 * Copyright (C) 2018 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.bubbles;
18
Mady Mellor3dff9e62019-02-05 18:12:53 -080019import static android.content.pm.ActivityInfo.DOCUMENT_LAUNCH_ALWAYS;
20import static android.util.StatsLogInternal.BUBBLE_DEVELOPER_ERROR_REPORTED__ERROR__ACTIVITY_INFO_MISSING;
21import static android.util.StatsLogInternal.BUBBLE_DEVELOPER_ERROR_REPORTED__ERROR__ACTIVITY_INFO_NOT_RESIZABLE;
22import static android.util.StatsLogInternal.BUBBLE_DEVELOPER_ERROR_REPORTED__ERROR__DOCUMENT_LAUNCH_NOT_ALWAYS;
Mady Mellor390bff42019-04-05 15:09:01 -070023import static android.view.Display.INVALID_DISPLAY;
Mady Mellor3dff9e62019-02-05 18:12:53 -080024
Mady Mellordea7ecf2018-12-10 15:47:40 -080025import android.annotation.Nullable;
Mady Mellor60101c92019-04-11 19:04:00 -070026import android.app.ActivityOptions;
Mady Mellor3dff9e62019-02-05 18:12:53 -080027import android.app.ActivityView;
Mady Mellore8e07712019-01-23 12:45:33 -080028import android.app.INotificationManager;
Mady Mellor9801e852019-01-22 14:50:28 -080029import android.app.Notification;
30import android.app.PendingIntent;
Mady Mellordea7ecf2018-12-10 15:47:40 -080031import android.content.Context;
Mady Mellor9801e852019-01-22 14:50:28 -080032import android.content.Intent;
Mady Mellor3dff9e62019-02-05 18:12:53 -080033import android.content.pm.ActivityInfo;
Mady Mellor9801e852019-01-22 14:50:28 -080034import android.content.pm.ApplicationInfo;
35import android.content.pm.PackageManager;
Mady Mellordea7ecf2018-12-10 15:47:40 -080036import android.content.res.Resources;
Mady Mellordd497052019-01-30 17:23:48 -080037import android.content.res.TypedArray;
Mady Mellordea7ecf2018-12-10 15:47:40 -080038import android.graphics.Color;
Mady Mellor3dff9e62019-02-05 18:12:53 -080039import android.graphics.Insets;
40import android.graphics.Point;
Mady Mellore8e07712019-01-23 12:45:33 -080041import android.graphics.drawable.Drawable;
Mady Mellordea7ecf2018-12-10 15:47:40 -080042import android.graphics.drawable.ShapeDrawable;
Mady Mellore8e07712019-01-23 12:45:33 -080043import android.os.ServiceManager;
Mady Mellor7af771a2019-03-07 15:04:54 -080044import android.os.UserHandle;
Mady Mellor9801e852019-01-22 14:50:28 -080045import android.provider.Settings;
Steven Wub00225b2019-02-08 14:27:42 -050046import android.service.notification.StatusBarNotification;
Mady Mellordea7ecf2018-12-10 15:47:40 -080047import android.util.AttributeSet;
Mady Mellor9801e852019-01-22 14:50:28 -080048import android.util.Log;
Steven Wub00225b2019-02-08 14:27:42 -050049import android.util.StatsLog;
Mady Mellordea7ecf2018-12-10 15:47:40 -080050import android.view.View;
Mady Mellor5029fa62019-03-05 12:16:21 -080051import android.view.ViewGroup;
Mady Mellor3dff9e62019-02-05 18:12:53 -080052import android.view.WindowInsets;
Mady Mellordea7ecf2018-12-10 15:47:40 -080053import android.widget.LinearLayout;
54
Mark Renouf34d04f32019-05-13 15:53:18 -040055import com.android.internal.policy.ScreenDecorationsUtils;
Mady Mellor3dff9e62019-02-05 18:12:53 -080056import com.android.systemui.Dependency;
Mady Mellordea7ecf2018-12-10 15:47:40 -080057import com.android.systemui.R;
58import com.android.systemui.recents.TriangleShape;
Lyn Han754e77b2019-04-30 14:34:49 -070059import com.android.systemui.statusbar.AlphaOptimizedButton;
Mady Mellor9801e852019-01-22 14:50:28 -080060import com.android.systemui.statusbar.notification.collection.NotificationEntry;
Mady Mellor3dff9e62019-02-05 18:12:53 -080061import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow;
62import com.android.systemui.statusbar.notification.stack.ExpandableViewState;
Mady Mellordea7ecf2018-12-10 15:47:40 -080063
64/**
Lyn Han02cca812019-04-02 16:27:32 -070065 * Container for the expanded bubble view, handles rendering the caret and settings icon.
Mady Mellordea7ecf2018-12-10 15:47:40 -080066 */
Mady Mellor3d82e682019-02-05 13:34:48 -080067public class BubbleExpandedView extends LinearLayout implements View.OnClickListener {
Mady Mellor9801e852019-01-22 14:50:28 -080068 private static final String TAG = "BubbleExpandedView";
Mady Mellordea7ecf2018-12-10 15:47:40 -080069
70 // The triangle pointing to the expanded view
71 private View mPointerView;
Mady Mellor44ee2fe2019-01-30 17:51:16 -080072 private int mPointerMargin;
Mady Mellore8e07712019-01-23 12:45:33 -080073
Lyn Han754e77b2019-04-30 14:34:49 -070074 private AlphaOptimizedButton mSettingsIcon;
Mady Mellore8e07712019-01-23 12:45:33 -080075
Mady Mellor3dff9e62019-02-05 18:12:53 -080076 // Views for expanded state
77 private ExpandableNotificationRow mNotifRow;
78 private ActivityView mActivityView;
79
80 private boolean mActivityViewReady = false;
81 private PendingIntent mBubbleIntent;
82
Mady Mellor5d8f1402019-02-21 18:23:52 -080083 private boolean mKeyboardVisible;
84 private boolean mNeedsNewHeight;
85
Mady Mellorfe7ec032019-01-30 17:32:49 -080086 private int mMinHeight;
Lyn Han02cca812019-04-02 16:27:32 -070087 private int mSettingsIconHeight;
Mady Mellor44ee2fe2019-01-30 17:51:16 -080088 private int mBubbleHeight;
Lyn Han02cca812019-04-02 16:27:32 -070089 private int mPointerWidth;
90 private int mPointerHeight;
Mark Renouf34d04f32019-05-13 15:53:18 -040091 private ShapeDrawable mPointerDrawable;
Mady Mellordea7ecf2018-12-10 15:47:40 -080092
Mady Mellor9801e852019-01-22 14:50:28 -080093 private NotificationEntry mEntry;
94 private PackageManager mPm;
95 private String mAppName;
Mady Mellore8e07712019-01-23 12:45:33 -080096 private Drawable mAppIcon;
97
98 private INotificationManager mNotificationManagerService;
Mady Mellor3dff9e62019-02-05 18:12:53 -080099 private BubbleController mBubbleController = Dependency.get(BubbleController.class);
Mady Mellor9801e852019-01-22 14:50:28 -0800100
Mady Mellor9801e852019-01-22 14:50:28 -0800101 private BubbleStackView mStackView;
102
Mady Mellor3dff9e62019-02-05 18:12:53 -0800103 private BubbleExpandedView.OnBubbleBlockedListener mOnBubbleBlockedListener;
104
105 private ActivityView.StateCallback mStateCallback = new ActivityView.StateCallback() {
106 @Override
107 public void onActivityViewReady(ActivityView view) {
Mady Mellor6d002032019-02-13 13:45:17 -0800108 if (!mActivityViewReady) {
109 mActivityViewReady = true;
Mady Mellor60101c92019-04-11 19:04:00 -0700110 // Custom options so there is no activity transition animation
111 ActivityOptions options = ActivityOptions.makeCustomAnimation(getContext(),
112 0 /* enterResId */, 0 /* exitResId */);
113 // Post to keep the lifecycle normal
114 post(() -> mActivityView.startActivity(mBubbleIntent, options));
Mady Mellor6d002032019-02-13 13:45:17 -0800115 }
Mady Mellor3dff9e62019-02-05 18:12:53 -0800116 }
117
118 @Override
119 public void onActivityViewDestroyed(ActivityView view) {
120 mActivityViewReady = false;
121 }
122
123 /**
124 * This is only called for tasks on this ActivityView, which is also set to
125 * single-task mode -- meaning never more than one task on this display. If a task
126 * is being removed, it's the top Activity finishing and this bubble should
127 * be removed or collapsed.
128 */
129 @Override
130 public void onTaskRemovalStarted(int taskId) {
131 if (mEntry != null) {
132 // Must post because this is called from a binder thread.
Mark Renouf08bc42a2019-03-07 13:01:59 -0500133 post(() -> mBubbleController.removeBubble(mEntry.key,
134 BubbleController.DISMISS_TASK_FINISHED));
Mady Mellor3dff9e62019-02-05 18:12:53 -0800135 }
136 }
137 };
Mady Mellore8e07712019-01-23 12:45:33 -0800138
Mady Mellor3d82e682019-02-05 13:34:48 -0800139 public BubbleExpandedView(Context context) {
Mady Mellordea7ecf2018-12-10 15:47:40 -0800140 this(context, null);
141 }
142
Mady Mellor3d82e682019-02-05 13:34:48 -0800143 public BubbleExpandedView(Context context, AttributeSet attrs) {
Mady Mellordea7ecf2018-12-10 15:47:40 -0800144 this(context, attrs, 0);
145 }
146
Mady Mellor3d82e682019-02-05 13:34:48 -0800147 public BubbleExpandedView(Context context, AttributeSet attrs, int defStyleAttr) {
Mady Mellordea7ecf2018-12-10 15:47:40 -0800148 this(context, attrs, defStyleAttr, 0);
149 }
150
Mady Mellor3d82e682019-02-05 13:34:48 -0800151 public BubbleExpandedView(Context context, AttributeSet attrs, int defStyleAttr,
Mady Mellordea7ecf2018-12-10 15:47:40 -0800152 int defStyleRes) {
153 super(context, attrs, defStyleAttr, defStyleRes);
Mady Mellor9801e852019-01-22 14:50:28 -0800154 mPm = context.getPackageManager();
Mady Mellorfe7ec032019-01-30 17:32:49 -0800155 mMinHeight = getResources().getDimensionPixelSize(
Mady Mellor3dff9e62019-02-05 18:12:53 -0800156 R.dimen.bubble_expanded_default_height);
Mady Mellor44ee2fe2019-01-30 17:51:16 -0800157 mPointerMargin = getResources().getDimensionPixelSize(R.dimen.bubble_pointer_margin);
Mady Mellore8e07712019-01-23 12:45:33 -0800158 try {
159 mNotificationManagerService = INotificationManager.Stub.asInterface(
160 ServiceManager.getServiceOrThrow(Context.NOTIFICATION_SERVICE));
161 } catch (ServiceManager.ServiceNotFoundException e) {
162 Log.w(TAG, e);
163 }
Mady Mellordea7ecf2018-12-10 15:47:40 -0800164 }
165
166 @Override
167 protected void onFinishInflate() {
168 super.onFinishInflate();
169
170 Resources res = getResources();
171 mPointerView = findViewById(R.id.pointer_view);
Lyn Han02cca812019-04-02 16:27:32 -0700172 mPointerWidth = res.getDimensionPixelSize(R.dimen.bubble_pointer_width);
173 mPointerHeight = res.getDimensionPixelSize(R.dimen.bubble_pointer_height);
Mady Mellordd497052019-01-30 17:23:48 -0800174
Mady Mellordd497052019-01-30 17:23:48 -0800175
Mark Renouf34d04f32019-05-13 15:53:18 -0400176 mPointerDrawable = new ShapeDrawable(TriangleShape.create(
Lyn Han5aa27e22019-05-15 10:55:07 -0700177 mPointerWidth, mPointerHeight, true /* pointUp */));
Mark Renouf34d04f32019-05-13 15:53:18 -0400178 mPointerView.setBackground(mPointerDrawable);
Lyn Hanf74ba672019-05-20 16:08:48 -0700179 mPointerView.setVisibility(GONE);
Mady Mellor9801e852019-01-22 14:50:28 -0800180
Lyn Han02cca812019-04-02 16:27:32 -0700181 mSettingsIconHeight = getContext().getResources().getDimensionPixelSize(
Mady Mellorfe7ec032019-01-30 17:32:49 -0800182 R.dimen.bubble_expanded_header_height);
Lyn Han02cca812019-04-02 16:27:32 -0700183 mSettingsIcon = findViewById(R.id.settings_button);
Lyn Han02cca812019-04-02 16:27:32 -0700184 mSettingsIcon.setOnClickListener(this);
Lyn Han02cca812019-04-02 16:27:32 -0700185
Mady Mellor3dff9e62019-02-05 18:12:53 -0800186 mActivityView = new ActivityView(mContext, null /* attrs */, 0 /* defStyle */,
187 true /* singleTaskInstance */);
188 addView(mActivityView);
189
Lyn Han5aa27e22019-05-15 10:55:07 -0700190 // Expanded stack layout, top to bottom:
191 // Expanded view container
192 // ==> bubble row
193 // ==> expanded view
194 // ==> activity view
195 // ==> manage button
196 bringChildToFront(mActivityView);
197 bringChildToFront(mSettingsIcon);
Mady Mellor52b1ac62019-04-10 16:59:03 -0700198
Mark Renouf34d04f32019-05-13 15:53:18 -0400199 applyThemeAttrs();
200
Mady Mellor5d8f1402019-02-21 18:23:52 -0800201 setOnApplyWindowInsetsListener((View view, WindowInsets insets) -> {
202 // Keep track of IME displaying because we should not make any adjustments that might
203 // cause a config change while the IME is displayed otherwise it'll loose focus.
Mady Mellor3dff9e62019-02-05 18:12:53 -0800204 final int keyboardHeight = insets.getSystemWindowInsetBottom()
205 - insets.getStableInsetBottom();
Mady Mellor5d8f1402019-02-21 18:23:52 -0800206 mKeyboardVisible = keyboardHeight != 0;
207 if (!mKeyboardVisible && mNeedsNewHeight) {
208 updateHeight();
209 }
Mady Mellor3dff9e62019-02-05 18:12:53 -0800210 return view.onApplyWindowInsets(insets);
211 });
Mady Mellore8e07712019-01-23 12:45:33 -0800212 }
213
Mark Renouf34d04f32019-05-13 15:53:18 -0400214 void applyThemeAttrs() {
215 TypedArray ta = getContext().obtainStyledAttributes(R.styleable.BubbleExpandedView);
216 int bgColor = ta.getColor(
217 R.styleable.BubbleExpandedView_android_colorBackgroundFloating, Color.WHITE);
218 float cornerRadius = ta.getDimension(
219 R.styleable.BubbleExpandedView_android_dialogCornerRadius, 0);
220 ta.recycle();
221
222 // Update triangle color.
223 mPointerDrawable.setTint(bgColor);
224
225 // Update ActivityView cornerRadius
226 if (ScreenDecorationsUtils.supportsRoundedCornersOnWindows(mContext.getResources())) {
227 mActivityView.setCornerRadius(cornerRadius);
228 }
229 }
230
Mady Mellor5d8f1402019-02-21 18:23:52 -0800231 @Override
232 protected void onDetachedFromWindow() {
233 super.onDetachedFromWindow();
234 mKeyboardVisible = false;
235 mNeedsNewHeight = false;
236 if (mActivityView != null) {
237 mActivityView.setForwardedInsets(Insets.of(0, 0, 0, 0));
238 }
239 }
240
241 /**
242 * Called by {@link BubbleStackView} when the insets for the expanded state should be updated.
243 * This should be done post-move and post-animation.
244 */
245 void updateInsets(WindowInsets insets) {
246 if (usingActivityView()) {
247 Point displaySize = new Point();
248 mActivityView.getContext().getDisplay().getSize(displaySize);
249 int[] windowLocation = mActivityView.getLocationOnScreen();
250 final int windowBottom = windowLocation[1] + mActivityView.getHeight();
251 final int keyboardHeight = insets.getSystemWindowInsetBottom()
252 - insets.getStableInsetBottom();
253 final int insetsBottom = Math.max(0,
254 windowBottom + keyboardHeight - displaySize.y);
255 mActivityView.setForwardedInsets(Insets.of(0, 0, 0, insetsBottom));
256 }
257 }
258
Mady Mellore8e07712019-01-23 12:45:33 -0800259 /**
260 * Sets the listener to notify when a bubble has been blocked.
261 */
262 public void setOnBlockedListener(OnBubbleBlockedListener listener) {
263 mOnBubbleBlockedListener = listener;
Mady Mellor9801e852019-01-22 14:50:28 -0800264 }
265
266 /**
267 * Sets the notification entry used to populate this view.
268 */
Lyn Han6c40fe72019-05-08 14:06:33 -0700269 public void setEntry(NotificationEntry entry, BubbleStackView stackView, String appName) {
Mady Mellor9801e852019-01-22 14:50:28 -0800270 mStackView = stackView;
271 mEntry = entry;
Lyn Han6c40fe72019-05-08 14:06:33 -0700272 mAppName = appName;
Mady Mellor9801e852019-01-22 14:50:28 -0800273
274 ApplicationInfo info;
275 try {
276 info = mPm.getApplicationInfo(
277 entry.notification.getPackageName(),
278 PackageManager.MATCH_UNINSTALLED_PACKAGES
279 | PackageManager.MATCH_DISABLED_COMPONENTS
280 | PackageManager.MATCH_DIRECT_BOOT_UNAWARE
281 | PackageManager.MATCH_DIRECT_BOOT_AWARE);
282 if (info != null) {
Mady Mellore8e07712019-01-23 12:45:33 -0800283 mAppIcon = mPm.getApplicationIcon(info);
Mady Mellor9801e852019-01-22 14:50:28 -0800284 }
285 } catch (PackageManager.NameNotFoundException e) {
Lyn Han6c40fe72019-05-08 14:06:33 -0700286 // Do nothing.
Mady Mellor9801e852019-01-22 14:50:28 -0800287 }
Mady Mellore8e07712019-01-23 12:45:33 -0800288 if (mAppIcon == null) {
289 mAppIcon = mPm.getDefaultActivityIcon();
290 }
Mark Renouf34d04f32019-05-13 15:53:18 -0400291 applyThemeAttrs();
Lyn Han69149122019-04-30 12:03:12 -0700292 showSettingsIcon();
Mady Mellor3dff9e62019-02-05 18:12:53 -0800293 updateExpandedView();
Mady Mellor6d002032019-02-13 13:45:17 -0800294 }
295
296 /**
297 * Lets activity view know it should be shown / populated.
298 */
Mady Mellor5029fa62019-03-05 12:16:21 -0800299 public void populateExpandedView() {
300 if (usingActivityView()) {
301 mActivityView.setCallback(mStateCallback);
302 } else {
303 // We're using notification template
304 ViewGroup parent = (ViewGroup) mNotifRow.getParent();
305 if (parent == this) {
306 // Already added
307 return;
308 } else if (parent != null) {
309 // Still in the shade... remove it
310 parent.removeView(mNotifRow);
311 }
Lyn Han3f5c3a42019-04-01 15:59:56 -0700312 addView(mNotifRow, 1 /* index */);
Mady Mellor5029fa62019-03-05 12:16:21 -0800313 }
Mady Mellor9801e852019-01-22 14:50:28 -0800314 }
315
Mady Mellorfe7ec032019-01-30 17:32:49 -0800316 /**
317 * Updates the entry backing this view. This will not re-populate ActivityView, it will
Lyn Han02cca812019-04-02 16:27:32 -0700318 * only update the deep-links in the title, and the height of the view.
Mady Mellorfe7ec032019-01-30 17:32:49 -0800319 */
320 public void update(NotificationEntry entry) {
321 if (entry.key.equals(mEntry.key)) {
322 mEntry = entry;
Lyn Han02cca812019-04-02 16:27:32 -0700323 updateSettingsContentDescription();
Mady Mellorfe7ec032019-01-30 17:32:49 -0800324 updateHeight();
325 } else {
326 Log.w(TAG, "Trying to update entry with different key, new entry: "
327 + entry.key + " old entry: " + mEntry.key);
328 }
329 }
330
Mady Mellor3dff9e62019-02-05 18:12:53 -0800331 private void updateExpandedView() {
332 mBubbleIntent = getBubbleIntent(mEntry);
333 if (mBubbleIntent != null) {
334 if (mNotifRow != null) {
335 // Clear out the row if we had it previously
336 removeView(mNotifRow);
337 mNotifRow = null;
338 }
Mady Mellor3dff9e62019-02-05 18:12:53 -0800339 mActivityView.setVisibility(VISIBLE);
340 } else {
341 // Hide activity view if we had it previously
342 mActivityView.setVisibility(GONE);
Mady Mellor3dff9e62019-02-05 18:12:53 -0800343 mNotifRow = mEntry.getRow();
Mady Mellor5029fa62019-03-05 12:16:21 -0800344
Mady Mellor3dff9e62019-02-05 18:12:53 -0800345 }
346 updateView();
347 }
348
Mark Renouf041d7262019-02-06 12:09:41 -0500349 boolean performBackPressIfNeeded() {
Mady Mellor323fb0b2019-03-25 12:15:22 -0700350 if (!usingActivityView()) {
Mark Renouf041d7262019-02-06 12:09:41 -0500351 return false;
352 }
353 mActivityView.performBackPress();
354 return true;
355 }
356
Mady Mellor44ee2fe2019-01-30 17:51:16 -0800357 /**
358 * @return total height that the expanded view occupies.
359 */
360 int getExpandedSize() {
Mady Mellor44ee2fe2019-01-30 17:51:16 -0800361 return mBubbleHeight + mPointerView.getHeight() + mPointerMargin
Lyn Han69149122019-04-30 12:03:12 -0700362 + mSettingsIconHeight;
Mady Mellor44ee2fe2019-01-30 17:51:16 -0800363 }
364
Mady Mellorfe7ec032019-01-30 17:32:49 -0800365 void updateHeight() {
366 if (usingActivityView()) {
367 Notification.BubbleMetadata data = mEntry.getBubbleMetadata();
Mady Mellor7af771a2019-03-07 15:04:54 -0800368 float desiredHeight;
Mady Mellorfe7ec032019-01-30 17:32:49 -0800369 if (data == null) {
370 // This is a contentIntent based bubble, lets allow it to be the max height
371 // as it was forced into this mode and not prepared to be small
372 desiredHeight = mStackView.getMaxExpandedHeight();
373 } else {
Mady Mellor7af771a2019-03-07 15:04:54 -0800374 boolean useRes = data.getDesiredHeightResId() != 0;
375 float desiredPx;
376 if (useRes) {
377 desiredPx = getDimenForPackageUser(data.getDesiredHeightResId(),
378 mEntry.notification.getPackageName(),
379 mEntry.notification.getUser().getIdentifier());
380 } else {
381 desiredPx = data.getDesiredHeight()
382 * getContext().getResources().getDisplayMetrics().density;
383 }
384 desiredHeight = desiredPx > 0 ? desiredPx : mMinHeight;
Mady Mellorfe7ec032019-01-30 17:32:49 -0800385 }
Lyn Han69149122019-04-30 12:03:12 -0700386 int max = mStackView.getMaxExpandedHeight() - mSettingsIconHeight
387 - mPointerView.getHeight() - mPointerMargin;
Mady Mellor7af771a2019-03-07 15:04:54 -0800388 float height = Math.min(desiredHeight, max);
Mady Mellorfe7ec032019-01-30 17:32:49 -0800389 height = Math.max(height, mMinHeight);
390 LayoutParams lp = (LayoutParams) mActivityView.getLayoutParams();
Mady Mellor5d8f1402019-02-21 18:23:52 -0800391 mNeedsNewHeight = lp.height != height;
392 if (!mKeyboardVisible) {
393 // If the keyboard is visible... don't adjust the height because that will cause
394 // a configuration change and the keyboard will be lost.
Mady Mellor7af771a2019-03-07 15:04:54 -0800395 lp.height = (int) height;
396 mBubbleHeight = (int) height;
Mady Mellor5d8f1402019-02-21 18:23:52 -0800397 mActivityView.setLayoutParams(lp);
398 mNeedsNewHeight = false;
399 }
Mady Mellor44ee2fe2019-01-30 17:51:16 -0800400 } else {
401 mBubbleHeight = mNotifRow != null ? mNotifRow.getIntrinsicHeight() : mMinHeight;
Mady Mellorfe7ec032019-01-30 17:32:49 -0800402 }
403 }
404
Mady Mellor9801e852019-01-22 14:50:28 -0800405 @Override
406 public void onClick(View view) {
407 if (mEntry == null) {
408 return;
409 }
410 Notification n = mEntry.notification.getNotification();
411 int id = view.getId();
Lyn Hanc26ff122019-03-29 16:46:07 -0700412 if (id == R.id.settings_button) {
Mady Mellor9801e852019-01-22 14:50:28 -0800413 Intent intent = getSettingsIntent(mEntry.notification.getPackageName(),
414 mEntry.notification.getUid());
Steven Wub00225b2019-02-08 14:27:42 -0500415 mStackView.collapseStack(() -> {
Mark Renouf76176192019-05-20 09:29:44 -0400416 mContext.startActivityAsUser(intent, mEntry.notification.getUser());
Steven Wu45e38ae2019-03-25 16:16:59 -0400417 logBubbleClickEvent(mEntry,
Steven Wub00225b2019-02-08 14:27:42 -0500418 StatsLog.BUBBLE_UICHANGED__ACTION__HEADER_GO_TO_SETTINGS);
419 });
Mady Mellor9801e852019-01-22 14:50:28 -0800420 }
Mady Mellordea7ecf2018-12-10 15:47:40 -0800421 }
422
Lyn Han02cca812019-04-02 16:27:32 -0700423 private void updateSettingsContentDescription() {
424 mSettingsIcon.setContentDescription(getResources().getString(
425 R.string.bubbles_settings_button_description, mAppName));
426 }
427
Lyn Hanc26ff122019-03-29 16:46:07 -0700428 void showSettingsIcon() {
Lyn Han02cca812019-04-02 16:27:32 -0700429 updateSettingsContentDescription();
Lyn Hanc26ff122019-03-29 16:46:07 -0700430 mSettingsIcon.setVisibility(VISIBLE);
431 }
432
Mady Mellordea7ecf2018-12-10 15:47:40 -0800433 /**
Mady Mellor3dff9e62019-02-05 18:12:53 -0800434 * Update appearance of the expanded view being displayed.
435 */
436 public void updateView() {
437 if (usingActivityView()
438 && mActivityView.getVisibility() == VISIBLE
439 && mActivityView.isAttachedToWindow()) {
440 mActivityView.onLocationChanged();
441 } else if (mNotifRow != null) {
442 applyRowState(mNotifRow);
443 }
Mady Mellorfe7ec032019-01-30 17:32:49 -0800444 updateHeight();
Mady Mellor3dff9e62019-02-05 18:12:53 -0800445 }
446
447 /**
Mady Mellordea7ecf2018-12-10 15:47:40 -0800448 * Set the x position that the tip of the triangle should point to.
449 */
Mady Mellor3dff9e62019-02-05 18:12:53 -0800450 public void setPointerPosition(float x) {
Mady Mellordea7ecf2018-12-10 15:47:40 -0800451 // Adjust for the pointer size
Mady Mellor3dff9e62019-02-05 18:12:53 -0800452 x -= (mPointerView.getWidth() / 2f);
Mady Mellordea7ecf2018-12-10 15:47:40 -0800453 mPointerView.setTranslationX(x);
Lyn Hanf74ba672019-05-20 16:08:48 -0700454 mPointerView.setVisibility(VISIBLE);
Mady Mellordea7ecf2018-12-10 15:47:40 -0800455 }
456
457 /**
Mady Mellor3dff9e62019-02-05 18:12:53 -0800458 * Removes and releases an ActivityView if one was previously created for this bubble.
Mady Mellordea7ecf2018-12-10 15:47:40 -0800459 */
Mady Mellor94d94a72019-03-05 18:16:59 -0800460 public void cleanUpExpandedState() {
461 removeView(mNotifRow);
462
Mady Mellor3dff9e62019-02-05 18:12:53 -0800463 if (mActivityView == null) {
Mark Renouf89b1a4a2018-12-04 14:59:45 -0500464 return;
465 }
Mark Renouf28c250d2019-02-25 16:47:34 -0500466 if (mActivityViewReady) {
467 mActivityView.release();
Mady Mellordea7ecf2018-12-10 15:47:40 -0800468 }
Mark Renouf28c250d2019-02-25 16:47:34 -0500469 removeView(mActivityView);
Mady Mellor3dff9e62019-02-05 18:12:53 -0800470 mActivityView = null;
471 mActivityViewReady = false;
Mady Mellordea7ecf2018-12-10 15:47:40 -0800472 }
473
Mady Mellor3dff9e62019-02-05 18:12:53 -0800474 private boolean usingActivityView() {
Mady Mellor323fb0b2019-03-25 12:15:22 -0700475 return mBubbleIntent != null && mActivityView != null;
Mady Mellor3dff9e62019-02-05 18:12:53 -0800476 }
477
Mady Mellor390bff42019-04-05 15:09:01 -0700478 /**
479 * @return the display id of the virtual display.
480 */
481 public int getVirtualDisplayId() {
482 if (usingActivityView()) {
483 return mActivityView.getVirtualDisplayId();
484 }
485 return INVALID_DISPLAY;
486 }
487
Mady Mellor3dff9e62019-02-05 18:12:53 -0800488 private void applyRowState(ExpandableNotificationRow view) {
489 view.reset();
490 view.setHeadsUp(false);
491 view.resetTranslation();
492 view.setOnKeyguard(false);
493 view.setOnAmbient(false);
494 view.setClipBottomAmount(0);
495 view.setClipTopAmount(0);
496 view.setContentTransformationAmount(0, false);
497 view.setIconsVisible(true);
498
499 // TODO - Need to reset this (and others) when view goes back in shade, leave for now
500 // view.setTopRoundness(1, false);
501 // view.setBottomRoundness(1, false);
502
503 ExpandableViewState viewState = view.getViewState();
504 viewState = viewState == null ? new ExpandableViewState() : viewState;
505 viewState.height = view.getIntrinsicHeight();
506 viewState.gone = false;
507 viewState.hidden = false;
508 viewState.dimmed = false;
509 viewState.dark = false;
510 viewState.alpha = 1f;
511 viewState.notGoneIndex = -1;
512 viewState.xTranslation = 0;
513 viewState.yTranslation = 0;
514 viewState.zTranslation = 0;
515 viewState.scaleX = 1;
516 viewState.scaleY = 1;
517 viewState.inShelf = true;
518 viewState.headsUpIsVisible = false;
519 viewState.applyToView(view);
Mady Mellordea7ecf2018-12-10 15:47:40 -0800520 }
Mady Mellor9801e852019-01-22 14:50:28 -0800521
522 private Intent getSettingsIntent(String packageName, final int appUid) {
Lyn Han754e77b2019-04-30 14:34:49 -0700523 final Intent intent = new Intent(Settings.ACTION_APP_NOTIFICATION_BUBBLE_SETTINGS);
Mady Mellor9801e852019-01-22 14:50:28 -0800524 intent.putExtra(Settings.EXTRA_APP_PACKAGE, packageName);
525 intent.putExtra(Settings.EXTRA_APP_UID, appUid);
526 intent.addFlags(Intent.FLAG_ACTIVITY_MULTIPLE_TASK);
527 intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
Lyn Han754e77b2019-04-30 14:34:49 -0700528 intent.addFlags(Intent.FLAG_ACTIVITY_SINGLE_TOP);
Mady Mellor9801e852019-01-22 14:50:28 -0800529 return intent;
530 }
Mady Mellore8e07712019-01-23 12:45:33 -0800531
Mady Mellor3dff9e62019-02-05 18:12:53 -0800532 @Nullable
533 private PendingIntent getBubbleIntent(NotificationEntry entry) {
534 Notification notif = entry.notification.getNotification();
535 String packageName = entry.notification.getPackageName();
536 Notification.BubbleMetadata data = notif.getBubbleMetadata();
537 if (data != null && canLaunchInActivityView(data.getIntent(), true /* enableLogging */,
538 packageName)) {
539 return data.getIntent();
540 } else if (BubbleController.shouldUseContentIntent(mContext)
541 && canLaunchInActivityView(notif.contentIntent, false /* enableLogging */,
542 packageName)) {
543 return notif.contentIntent;
544 }
545 return null;
546 }
547
548 /**
549 * Whether an intent is properly configured to display in an {@link android.app.ActivityView}.
550 *
551 * @param intent the pending intent of the bubble.
552 * @param enableLogging whether bubble developer error should be logged.
553 * @param packageName the notification package name for this bubble.
554 * @return
555 */
556 private boolean canLaunchInActivityView(PendingIntent intent, boolean enableLogging,
557 String packageName) {
558 if (intent == null) {
559 return false;
560 }
561 ActivityInfo info =
562 intent.getIntent().resolveActivityInfo(mContext.getPackageManager(), 0);
563 if (info == null) {
564 if (enableLogging) {
565 StatsLog.write(StatsLog.BUBBLE_DEVELOPER_ERROR_REPORTED, packageName,
566 BUBBLE_DEVELOPER_ERROR_REPORTED__ERROR__ACTIVITY_INFO_MISSING);
567 }
568 return false;
569 }
570 if (!ActivityInfo.isResizeableMode(info.resizeMode)) {
571 if (enableLogging) {
572 StatsLog.write(StatsLog.BUBBLE_DEVELOPER_ERROR_REPORTED, packageName,
573 BUBBLE_DEVELOPER_ERROR_REPORTED__ERROR__ACTIVITY_INFO_NOT_RESIZABLE);
574 }
575 return false;
576 }
577 if (info.documentLaunchMode != DOCUMENT_LAUNCH_ALWAYS) {
578 if (enableLogging) {
579 StatsLog.write(StatsLog.BUBBLE_DEVELOPER_ERROR_REPORTED, packageName,
580 BUBBLE_DEVELOPER_ERROR_REPORTED__ERROR__DOCUMENT_LAUNCH_NOT_ALWAYS);
581 }
582 return false;
583 }
584 return (info.flags & ActivityInfo.FLAG_ALLOW_EMBEDDED) != 0;
585 }
586
Mady Mellore8e07712019-01-23 12:45:33 -0800587 /**
588 * Listener that is notified when a bubble is blocked.
589 */
590 public interface OnBubbleBlockedListener {
591 /**
592 * Called when a bubble is blocked for the provided entry.
593 */
594 void onBubbleBlocked(NotificationEntry entry);
595 }
Steven Wub00225b2019-02-08 14:27:42 -0500596
597 /**
598 * Logs bubble UI click event.
599 *
Steven Wu45e38ae2019-03-25 16:16:59 -0400600 * @param entry the bubble notification entry that user is interacting with.
Steven Wub00225b2019-02-08 14:27:42 -0500601 * @param action the user interaction enum.
602 */
Steven Wu45e38ae2019-03-25 16:16:59 -0400603 private void logBubbleClickEvent(NotificationEntry entry, int action) {
604 StatusBarNotification notification = entry.notification;
Steven Wub00225b2019-02-08 14:27:42 -0500605 StatsLog.write(StatsLog.BUBBLE_UI_CHANGED,
606 notification.getPackageName(),
607 notification.getNotification().getChannelId(),
608 notification.getId(),
609 mStackView.getBubbleIndex(mStackView.getExpandedBubble()),
610 mStackView.getBubbleCount(),
611 action,
612 mStackView.getNormalizedXPosition(),
Steven Wu45e38ae2019-03-25 16:16:59 -0400613 mStackView.getNormalizedYPosition(),
Steven Wu8ba8ca92019-04-11 10:47:42 -0400614 entry.showInShadeWhenBubble(),
615 entry.isForegroundService(),
616 BubbleController.isForegroundApp(mContext, notification.getPackageName()));
Steven Wub00225b2019-02-08 14:27:42 -0500617 }
Mady Mellor7af771a2019-03-07 15:04:54 -0800618
619 private int getDimenForPackageUser(int resId, String pkg, int userId) {
620 Resources r;
621 if (pkg != null) {
622 try {
623 if (userId == UserHandle.USER_ALL) {
624 userId = UserHandle.USER_SYSTEM;
625 }
626 r = mPm.getResourcesForApplicationAsUser(pkg, userId);
627 return r.getDimensionPixelSize(resId);
628 } catch (PackageManager.NameNotFoundException ex) {
629 // Uninstalled, don't care
630 } catch (Resources.NotFoundException e) {
631 // Invalid res id, return 0 and user our default
632 Log.e(TAG, "Couldn't find desired height res id", e);
633 }
634 }
635 return 0;
636 }
Mady Mellordea7ecf2018-12-10 15:47:40 -0800637}