blob: 923ca20b3925178fd3c2460674c691ceaa5be727 [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 Mellor390bff42019-04-05 15:09:01 -070019import static android.view.Display.INVALID_DISPLAY;
Mady Mellor3dff9e62019-02-05 18:12:53 -080020
Mady Mellorca0c24c2019-05-16 16:14:32 -070021import static com.android.systemui.bubbles.BubbleController.DEBUG_ENABLE_AUTO_BUBBLE;
22
Mady Mellordea7ecf2018-12-10 15:47:40 -080023import android.annotation.Nullable;
Mady Mellor60101c92019-04-11 19:04:00 -070024import android.app.ActivityOptions;
Mady Mellor3dff9e62019-02-05 18:12:53 -080025import android.app.ActivityView;
Mady Mellore8e07712019-01-23 12:45:33 -080026import android.app.INotificationManager;
Mady Mellor9801e852019-01-22 14:50:28 -080027import android.app.Notification;
28import android.app.PendingIntent;
Mady Mellordea7ecf2018-12-10 15:47:40 -080029import android.content.Context;
Mady Mellor9801e852019-01-22 14:50:28 -080030import android.content.Intent;
31import android.content.pm.ApplicationInfo;
32import android.content.pm.PackageManager;
Mady Mellordea7ecf2018-12-10 15:47:40 -080033import android.content.res.Resources;
Mady Mellordd497052019-01-30 17:23:48 -080034import android.content.res.TypedArray;
Mady Mellordea7ecf2018-12-10 15:47:40 -080035import android.graphics.Color;
Mady Mellor3dff9e62019-02-05 18:12:53 -080036import android.graphics.Insets;
37import android.graphics.Point;
Mady Mellore8e07712019-01-23 12:45:33 -080038import android.graphics.drawable.Drawable;
Mady Mellordea7ecf2018-12-10 15:47:40 -080039import android.graphics.drawable.ShapeDrawable;
Mady Mellore8e07712019-01-23 12:45:33 -080040import android.os.ServiceManager;
Mady Mellor7af771a2019-03-07 15:04:54 -080041import android.os.UserHandle;
Mady Mellor9801e852019-01-22 14:50:28 -080042import android.provider.Settings;
Steven Wub00225b2019-02-08 14:27:42 -050043import android.service.notification.StatusBarNotification;
Mady Mellordea7ecf2018-12-10 15:47:40 -080044import android.util.AttributeSet;
Mady Mellor9801e852019-01-22 14:50:28 -080045import android.util.Log;
Steven Wub00225b2019-02-08 14:27:42 -050046import android.util.StatsLog;
Mady Mellordea7ecf2018-12-10 15:47:40 -080047import android.view.View;
Mady Mellor5029fa62019-03-05 12:16:21 -080048import android.view.ViewGroup;
Mady Mellor3dff9e62019-02-05 18:12:53 -080049import android.view.WindowInsets;
Mady Mellordea7ecf2018-12-10 15:47:40 -080050import android.widget.LinearLayout;
51
Mark Renouf34d04f32019-05-13 15:53:18 -040052import com.android.internal.policy.ScreenDecorationsUtils;
Mady Mellor3dff9e62019-02-05 18:12:53 -080053import com.android.systemui.Dependency;
Mady Mellordea7ecf2018-12-10 15:47:40 -080054import com.android.systemui.R;
55import com.android.systemui.recents.TriangleShape;
Lyn Han754e77b2019-04-30 14:34:49 -070056import com.android.systemui.statusbar.AlphaOptimizedButton;
Mady Mellor9801e852019-01-22 14:50:28 -080057import com.android.systemui.statusbar.notification.collection.NotificationEntry;
Mady Mellor3dff9e62019-02-05 18:12:53 -080058import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow;
59import com.android.systemui.statusbar.notification.stack.ExpandableViewState;
Mady Mellordea7ecf2018-12-10 15:47:40 -080060
61/**
Lyn Han02cca812019-04-02 16:27:32 -070062 * Container for the expanded bubble view, handles rendering the caret and settings icon.
Mady Mellordea7ecf2018-12-10 15:47:40 -080063 */
Mady Mellor3d82e682019-02-05 13:34:48 -080064public class BubbleExpandedView extends LinearLayout implements View.OnClickListener {
Mady Mellor9801e852019-01-22 14:50:28 -080065 private static final String TAG = "BubbleExpandedView";
Mady Mellordea7ecf2018-12-10 15:47:40 -080066
67 // The triangle pointing to the expanded view
68 private View mPointerView;
Mady Mellor44ee2fe2019-01-30 17:51:16 -080069 private int mPointerMargin;
Mady Mellore8e07712019-01-23 12:45:33 -080070
Lyn Han754e77b2019-04-30 14:34:49 -070071 private AlphaOptimizedButton mSettingsIcon;
Mady Mellore8e07712019-01-23 12:45:33 -080072
Mady Mellor3dff9e62019-02-05 18:12:53 -080073 // Views for expanded state
74 private ExpandableNotificationRow mNotifRow;
75 private ActivityView mActivityView;
76
77 private boolean mActivityViewReady = false;
78 private PendingIntent mBubbleIntent;
79
Mady Mellor5d8f1402019-02-21 18:23:52 -080080 private boolean mKeyboardVisible;
81 private boolean mNeedsNewHeight;
82
Mady Mellorfe7ec032019-01-30 17:32:49 -080083 private int mMinHeight;
Lyn Han02cca812019-04-02 16:27:32 -070084 private int mSettingsIconHeight;
Mady Mellor44ee2fe2019-01-30 17:51:16 -080085 private int mBubbleHeight;
Lyn Han02cca812019-04-02 16:27:32 -070086 private int mPointerWidth;
87 private int mPointerHeight;
Mark Renouf34d04f32019-05-13 15:53:18 -040088 private ShapeDrawable mPointerDrawable;
Mady Mellordea7ecf2018-12-10 15:47:40 -080089
Mady Mellor9801e852019-01-22 14:50:28 -080090 private NotificationEntry mEntry;
91 private PackageManager mPm;
92 private String mAppName;
Mady Mellore8e07712019-01-23 12:45:33 -080093 private Drawable mAppIcon;
94
95 private INotificationManager mNotificationManagerService;
Mady Mellor3dff9e62019-02-05 18:12:53 -080096 private BubbleController mBubbleController = Dependency.get(BubbleController.class);
Mady Mellor9801e852019-01-22 14:50:28 -080097
Mady Mellor9801e852019-01-22 14:50:28 -080098 private BubbleStackView mStackView;
99
Mady Mellor3dff9e62019-02-05 18:12:53 -0800100 private BubbleExpandedView.OnBubbleBlockedListener mOnBubbleBlockedListener;
101
102 private ActivityView.StateCallback mStateCallback = new ActivityView.StateCallback() {
103 @Override
104 public void onActivityViewReady(ActivityView view) {
Mady Mellor6d002032019-02-13 13:45:17 -0800105 if (!mActivityViewReady) {
106 mActivityViewReady = true;
Mady Mellor60101c92019-04-11 19:04:00 -0700107 // Custom options so there is no activity transition animation
108 ActivityOptions options = ActivityOptions.makeCustomAnimation(getContext(),
109 0 /* enterResId */, 0 /* exitResId */);
110 // Post to keep the lifecycle normal
111 post(() -> mActivityView.startActivity(mBubbleIntent, options));
Mady Mellor6d002032019-02-13 13:45:17 -0800112 }
Mady Mellor3dff9e62019-02-05 18:12:53 -0800113 }
114
115 @Override
116 public void onActivityViewDestroyed(ActivityView view) {
117 mActivityViewReady = false;
118 }
119
120 /**
121 * This is only called for tasks on this ActivityView, which is also set to
122 * single-task mode -- meaning never more than one task on this display. If a task
123 * is being removed, it's the top Activity finishing and this bubble should
124 * be removed or collapsed.
125 */
126 @Override
127 public void onTaskRemovalStarted(int taskId) {
128 if (mEntry != null) {
129 // Must post because this is called from a binder thread.
Mark Renouf08bc42a2019-03-07 13:01:59 -0500130 post(() -> mBubbleController.removeBubble(mEntry.key,
131 BubbleController.DISMISS_TASK_FINISHED));
Mady Mellor3dff9e62019-02-05 18:12:53 -0800132 }
133 }
134 };
Mady Mellore8e07712019-01-23 12:45:33 -0800135
Mady Mellor3d82e682019-02-05 13:34:48 -0800136 public BubbleExpandedView(Context context) {
Mady Mellordea7ecf2018-12-10 15:47:40 -0800137 this(context, null);
138 }
139
Mady Mellor3d82e682019-02-05 13:34:48 -0800140 public BubbleExpandedView(Context context, AttributeSet attrs) {
Mady Mellordea7ecf2018-12-10 15:47:40 -0800141 this(context, attrs, 0);
142 }
143
Mady Mellor3d82e682019-02-05 13:34:48 -0800144 public BubbleExpandedView(Context context, AttributeSet attrs, int defStyleAttr) {
Mady Mellordea7ecf2018-12-10 15:47:40 -0800145 this(context, attrs, defStyleAttr, 0);
146 }
147
Mady Mellor3d82e682019-02-05 13:34:48 -0800148 public BubbleExpandedView(Context context, AttributeSet attrs, int defStyleAttr,
Mady Mellordea7ecf2018-12-10 15:47:40 -0800149 int defStyleRes) {
150 super(context, attrs, defStyleAttr, defStyleRes);
Mady Mellor9801e852019-01-22 14:50:28 -0800151 mPm = context.getPackageManager();
Mady Mellorfe7ec032019-01-30 17:32:49 -0800152 mMinHeight = getResources().getDimensionPixelSize(
Mady Mellor3dff9e62019-02-05 18:12:53 -0800153 R.dimen.bubble_expanded_default_height);
Mady Mellor44ee2fe2019-01-30 17:51:16 -0800154 mPointerMargin = getResources().getDimensionPixelSize(R.dimen.bubble_pointer_margin);
Mady Mellore8e07712019-01-23 12:45:33 -0800155 try {
156 mNotificationManagerService = INotificationManager.Stub.asInterface(
157 ServiceManager.getServiceOrThrow(Context.NOTIFICATION_SERVICE));
158 } catch (ServiceManager.ServiceNotFoundException e) {
159 Log.w(TAG, e);
160 }
Mady Mellordea7ecf2018-12-10 15:47:40 -0800161 }
162
163 @Override
164 protected void onFinishInflate() {
165 super.onFinishInflate();
166
167 Resources res = getResources();
168 mPointerView = findViewById(R.id.pointer_view);
Lyn Han02cca812019-04-02 16:27:32 -0700169 mPointerWidth = res.getDimensionPixelSize(R.dimen.bubble_pointer_width);
170 mPointerHeight = res.getDimensionPixelSize(R.dimen.bubble_pointer_height);
Mady Mellordd497052019-01-30 17:23:48 -0800171
Mady Mellordd497052019-01-30 17:23:48 -0800172
Mark Renouf34d04f32019-05-13 15:53:18 -0400173 mPointerDrawable = new ShapeDrawable(TriangleShape.create(
Lyn Han5aa27e22019-05-15 10:55:07 -0700174 mPointerWidth, mPointerHeight, true /* pointUp */));
Mark Renouf34d04f32019-05-13 15:53:18 -0400175 mPointerView.setBackground(mPointerDrawable);
Lyn Hanf74ba672019-05-20 16:08:48 -0700176 mPointerView.setVisibility(GONE);
Mady Mellor9801e852019-01-22 14:50:28 -0800177
Lyn Han02cca812019-04-02 16:27:32 -0700178 mSettingsIconHeight = getContext().getResources().getDimensionPixelSize(
Mady Mellorfe7ec032019-01-30 17:32:49 -0800179 R.dimen.bubble_expanded_header_height);
Lyn Han02cca812019-04-02 16:27:32 -0700180 mSettingsIcon = findViewById(R.id.settings_button);
Lyn Han02cca812019-04-02 16:27:32 -0700181 mSettingsIcon.setOnClickListener(this);
Lyn Han02cca812019-04-02 16:27:32 -0700182
Mady Mellor3dff9e62019-02-05 18:12:53 -0800183 mActivityView = new ActivityView(mContext, null /* attrs */, 0 /* defStyle */,
184 true /* singleTaskInstance */);
Issei Suzukicac2a502019-04-16 16:52:50 +0200185
186 setContentVisibility(false);
Mady Mellor3dff9e62019-02-05 18:12:53 -0800187 addView(mActivityView);
188
Lyn Han5aa27e22019-05-15 10:55:07 -0700189 // Expanded stack layout, top to bottom:
190 // Expanded view container
191 // ==> bubble row
192 // ==> expanded view
193 // ==> activity view
194 // ==> manage button
195 bringChildToFront(mActivityView);
196 bringChildToFront(mSettingsIcon);
Mady Mellor52b1ac62019-04-10 16:59:03 -0700197
Mark Renouf34d04f32019-05-13 15:53:18 -0400198 applyThemeAttrs();
199
Mady Mellor5d8f1402019-02-21 18:23:52 -0800200 setOnApplyWindowInsetsListener((View view, WindowInsets insets) -> {
201 // Keep track of IME displaying because we should not make any adjustments that might
202 // cause a config change while the IME is displayed otherwise it'll loose focus.
Mady Mellor3dff9e62019-02-05 18:12:53 -0800203 final int keyboardHeight = insets.getSystemWindowInsetBottom()
204 - insets.getStableInsetBottom();
Mady Mellor5d8f1402019-02-21 18:23:52 -0800205 mKeyboardVisible = keyboardHeight != 0;
206 if (!mKeyboardVisible && mNeedsNewHeight) {
207 updateHeight();
208 }
Mady Mellor3dff9e62019-02-05 18:12:53 -0800209 return view.onApplyWindowInsets(insets);
210 });
Mady Mellore8e07712019-01-23 12:45:33 -0800211 }
212
Mark Renouf34d04f32019-05-13 15:53:18 -0400213 void applyThemeAttrs() {
214 TypedArray ta = getContext().obtainStyledAttributes(R.styleable.BubbleExpandedView);
215 int bgColor = ta.getColor(
216 R.styleable.BubbleExpandedView_android_colorBackgroundFloating, Color.WHITE);
217 float cornerRadius = ta.getDimension(
218 R.styleable.BubbleExpandedView_android_dialogCornerRadius, 0);
219 ta.recycle();
220
221 // Update triangle color.
222 mPointerDrawable.setTint(bgColor);
223
224 // Update ActivityView cornerRadius
225 if (ScreenDecorationsUtils.supportsRoundedCornersOnWindows(mContext.getResources())) {
226 mActivityView.setCornerRadius(cornerRadius);
227 }
228 }
229
Mady Mellor5d8f1402019-02-21 18:23:52 -0800230 @Override
231 protected void onDetachedFromWindow() {
232 super.onDetachedFromWindow();
233 mKeyboardVisible = false;
234 mNeedsNewHeight = false;
235 if (mActivityView != null) {
236 mActivityView.setForwardedInsets(Insets.of(0, 0, 0, 0));
237 }
238 }
239
240 /**
Issei Suzukicac2a502019-04-16 16:52:50 +0200241 * Set visibility of contents in the expanded state.
242 *
243 * @param visibility {@code true} if the contents should be visible on the screen.
244 *
245 * Note that this contents visibility doesn't affect visibility at {@link android.view.View},
246 * and setting {@code false} actually means rendering the contents in transparent.
247 */
248 void setContentVisibility(boolean visibility) {
249 final float alpha = visibility ? 1f : 0f;
250 mPointerView.setAlpha(alpha);
251 if (mActivityView != null) {
252 mActivityView.setAlpha(alpha);
253 }
254 }
255
256 /**
Mady Mellor5d8f1402019-02-21 18:23:52 -0800257 * Called by {@link BubbleStackView} when the insets for the expanded state should be updated.
258 * This should be done post-move and post-animation.
259 */
260 void updateInsets(WindowInsets insets) {
261 if (usingActivityView()) {
262 Point displaySize = new Point();
263 mActivityView.getContext().getDisplay().getSize(displaySize);
264 int[] windowLocation = mActivityView.getLocationOnScreen();
265 final int windowBottom = windowLocation[1] + mActivityView.getHeight();
266 final int keyboardHeight = insets.getSystemWindowInsetBottom()
267 - insets.getStableInsetBottom();
268 final int insetsBottom = Math.max(0,
269 windowBottom + keyboardHeight - displaySize.y);
270 mActivityView.setForwardedInsets(Insets.of(0, 0, 0, insetsBottom));
271 }
272 }
273
Mady Mellore8e07712019-01-23 12:45:33 -0800274 /**
275 * Sets the listener to notify when a bubble has been blocked.
276 */
277 public void setOnBlockedListener(OnBubbleBlockedListener listener) {
278 mOnBubbleBlockedListener = listener;
Mady Mellor9801e852019-01-22 14:50:28 -0800279 }
280
281 /**
282 * Sets the notification entry used to populate this view.
283 */
Lyn Han6c40fe72019-05-08 14:06:33 -0700284 public void setEntry(NotificationEntry entry, BubbleStackView stackView, String appName) {
Mady Mellor9801e852019-01-22 14:50:28 -0800285 mStackView = stackView;
286 mEntry = entry;
Lyn Han6c40fe72019-05-08 14:06:33 -0700287 mAppName = appName;
Mady Mellor9801e852019-01-22 14:50:28 -0800288
289 ApplicationInfo info;
290 try {
291 info = mPm.getApplicationInfo(
292 entry.notification.getPackageName(),
293 PackageManager.MATCH_UNINSTALLED_PACKAGES
294 | PackageManager.MATCH_DISABLED_COMPONENTS
295 | PackageManager.MATCH_DIRECT_BOOT_UNAWARE
296 | PackageManager.MATCH_DIRECT_BOOT_AWARE);
297 if (info != null) {
Mady Mellore8e07712019-01-23 12:45:33 -0800298 mAppIcon = mPm.getApplicationIcon(info);
Mady Mellor9801e852019-01-22 14:50:28 -0800299 }
300 } catch (PackageManager.NameNotFoundException e) {
Lyn Han6c40fe72019-05-08 14:06:33 -0700301 // Do nothing.
Mady Mellor9801e852019-01-22 14:50:28 -0800302 }
Mady Mellore8e07712019-01-23 12:45:33 -0800303 if (mAppIcon == null) {
304 mAppIcon = mPm.getDefaultActivityIcon();
305 }
Mark Renouf34d04f32019-05-13 15:53:18 -0400306 applyThemeAttrs();
Lyn Han69149122019-04-30 12:03:12 -0700307 showSettingsIcon();
Mady Mellor3dff9e62019-02-05 18:12:53 -0800308 updateExpandedView();
Mady Mellor6d002032019-02-13 13:45:17 -0800309 }
310
311 /**
312 * Lets activity view know it should be shown / populated.
313 */
Mady Mellor5029fa62019-03-05 12:16:21 -0800314 public void populateExpandedView() {
315 if (usingActivityView()) {
316 mActivityView.setCallback(mStateCallback);
317 } else {
318 // We're using notification template
319 ViewGroup parent = (ViewGroup) mNotifRow.getParent();
320 if (parent == this) {
321 // Already added
322 return;
323 } else if (parent != null) {
324 // Still in the shade... remove it
325 parent.removeView(mNotifRow);
326 }
Lyn Han3f5c3a42019-04-01 15:59:56 -0700327 addView(mNotifRow, 1 /* index */);
Issei Suzukicac2a502019-04-16 16:52:50 +0200328 mPointerView.setAlpha(1f);
Mady Mellor5029fa62019-03-05 12:16:21 -0800329 }
Mady Mellor9801e852019-01-22 14:50:28 -0800330 }
331
Mady Mellorfe7ec032019-01-30 17:32:49 -0800332 /**
333 * Updates the entry backing this view. This will not re-populate ActivityView, it will
Lyn Han02cca812019-04-02 16:27:32 -0700334 * only update the deep-links in the title, and the height of the view.
Mady Mellorfe7ec032019-01-30 17:32:49 -0800335 */
336 public void update(NotificationEntry entry) {
337 if (entry.key.equals(mEntry.key)) {
338 mEntry = entry;
Lyn Han02cca812019-04-02 16:27:32 -0700339 updateSettingsContentDescription();
Mady Mellorfe7ec032019-01-30 17:32:49 -0800340 updateHeight();
341 } else {
342 Log.w(TAG, "Trying to update entry with different key, new entry: "
343 + entry.key + " old entry: " + mEntry.key);
344 }
345 }
346
Mady Mellor3dff9e62019-02-05 18:12:53 -0800347 private void updateExpandedView() {
348 mBubbleIntent = getBubbleIntent(mEntry);
349 if (mBubbleIntent != null) {
350 if (mNotifRow != null) {
351 // Clear out the row if we had it previously
352 removeView(mNotifRow);
353 mNotifRow = null;
354 }
Issei Suzukicac2a502019-04-16 16:52:50 +0200355 setContentVisibility(false);
Mady Mellor3dff9e62019-02-05 18:12:53 -0800356 mActivityView.setVisibility(VISIBLE);
Mady Mellorca0c24c2019-05-16 16:14:32 -0700357 } else if (DEBUG_ENABLE_AUTO_BUBBLE) {
Mady Mellor3dff9e62019-02-05 18:12:53 -0800358 // Hide activity view if we had it previously
359 mActivityView.setVisibility(GONE);
Mady Mellor3dff9e62019-02-05 18:12:53 -0800360 mNotifRow = mEntry.getRow();
Mady Mellor3dff9e62019-02-05 18:12:53 -0800361 }
362 updateView();
363 }
364
Mark Renouf041d7262019-02-06 12:09:41 -0500365 boolean performBackPressIfNeeded() {
Mady Mellor323fb0b2019-03-25 12:15:22 -0700366 if (!usingActivityView()) {
Mark Renouf041d7262019-02-06 12:09:41 -0500367 return false;
368 }
369 mActivityView.performBackPress();
370 return true;
371 }
372
Mady Mellorfe7ec032019-01-30 17:32:49 -0800373 void updateHeight() {
374 if (usingActivityView()) {
375 Notification.BubbleMetadata data = mEntry.getBubbleMetadata();
Mady Mellor7af771a2019-03-07 15:04:54 -0800376 float desiredHeight;
Mady Mellorfe7ec032019-01-30 17:32:49 -0800377 if (data == null) {
378 // This is a contentIntent based bubble, lets allow it to be the max height
379 // as it was forced into this mode and not prepared to be small
380 desiredHeight = mStackView.getMaxExpandedHeight();
381 } else {
Mady Mellor7af771a2019-03-07 15:04:54 -0800382 boolean useRes = data.getDesiredHeightResId() != 0;
383 float desiredPx;
384 if (useRes) {
385 desiredPx = getDimenForPackageUser(data.getDesiredHeightResId(),
386 mEntry.notification.getPackageName(),
387 mEntry.notification.getUser().getIdentifier());
388 } else {
389 desiredPx = data.getDesiredHeight()
390 * getContext().getResources().getDisplayMetrics().density;
391 }
392 desiredHeight = desiredPx > 0 ? desiredPx : mMinHeight;
Mady Mellorfe7ec032019-01-30 17:32:49 -0800393 }
Lyn Han9a2f5cf2019-05-23 11:01:41 -0700394 int max = mStackView.getMaxExpandedHeight() - mSettingsIconHeight - mPointerHeight
395 - mPointerMargin;
Mady Mellor7af771a2019-03-07 15:04:54 -0800396 float height = Math.min(desiredHeight, max);
Mady Mellorfe7ec032019-01-30 17:32:49 -0800397 height = Math.max(height, mMinHeight);
398 LayoutParams lp = (LayoutParams) mActivityView.getLayoutParams();
Mady Mellor5d8f1402019-02-21 18:23:52 -0800399 mNeedsNewHeight = lp.height != height;
400 if (!mKeyboardVisible) {
401 // If the keyboard is visible... don't adjust the height because that will cause
402 // a configuration change and the keyboard will be lost.
Mady Mellor7af771a2019-03-07 15:04:54 -0800403 lp.height = (int) height;
404 mBubbleHeight = (int) height;
Mady Mellor5d8f1402019-02-21 18:23:52 -0800405 mActivityView.setLayoutParams(lp);
406 mNeedsNewHeight = false;
407 }
Mady Mellor44ee2fe2019-01-30 17:51:16 -0800408 } else {
409 mBubbleHeight = mNotifRow != null ? mNotifRow.getIntrinsicHeight() : mMinHeight;
Mady Mellorfe7ec032019-01-30 17:32:49 -0800410 }
411 }
412
Mady Mellor9801e852019-01-22 14:50:28 -0800413 @Override
414 public void onClick(View view) {
415 if (mEntry == null) {
416 return;
417 }
418 Notification n = mEntry.notification.getNotification();
419 int id = view.getId();
Lyn Hanc26ff122019-03-29 16:46:07 -0700420 if (id == R.id.settings_button) {
Mady Mellor9801e852019-01-22 14:50:28 -0800421 Intent intent = getSettingsIntent(mEntry.notification.getPackageName(),
422 mEntry.notification.getUid());
Steven Wub00225b2019-02-08 14:27:42 -0500423 mStackView.collapseStack(() -> {
Mark Renouf76176192019-05-20 09:29:44 -0400424 mContext.startActivityAsUser(intent, mEntry.notification.getUser());
Steven Wu45e38ae2019-03-25 16:16:59 -0400425 logBubbleClickEvent(mEntry,
Steven Wub00225b2019-02-08 14:27:42 -0500426 StatsLog.BUBBLE_UICHANGED__ACTION__HEADER_GO_TO_SETTINGS);
427 });
Mady Mellor9801e852019-01-22 14:50:28 -0800428 }
Mady Mellordea7ecf2018-12-10 15:47:40 -0800429 }
430
Lyn Han02cca812019-04-02 16:27:32 -0700431 private void updateSettingsContentDescription() {
432 mSettingsIcon.setContentDescription(getResources().getString(
433 R.string.bubbles_settings_button_description, mAppName));
434 }
435
Lyn Hanc26ff122019-03-29 16:46:07 -0700436 void showSettingsIcon() {
Lyn Han02cca812019-04-02 16:27:32 -0700437 updateSettingsContentDescription();
Lyn Hanc26ff122019-03-29 16:46:07 -0700438 mSettingsIcon.setVisibility(VISIBLE);
439 }
440
Mady Mellordea7ecf2018-12-10 15:47:40 -0800441 /**
Mady Mellor3dff9e62019-02-05 18:12:53 -0800442 * Update appearance of the expanded view being displayed.
443 */
444 public void updateView() {
445 if (usingActivityView()
446 && mActivityView.getVisibility() == VISIBLE
447 && mActivityView.isAttachedToWindow()) {
448 mActivityView.onLocationChanged();
449 } else if (mNotifRow != null) {
450 applyRowState(mNotifRow);
Issei Suzukicac2a502019-04-16 16:52:50 +0200451 mPointerView.setAlpha(1f);
Mady Mellor3dff9e62019-02-05 18:12:53 -0800452 }
Mady Mellorfe7ec032019-01-30 17:32:49 -0800453 updateHeight();
Mady Mellor3dff9e62019-02-05 18:12:53 -0800454 }
455
456 /**
Mady Mellordea7ecf2018-12-10 15:47:40 -0800457 * Set the x position that the tip of the triangle should point to.
458 */
Mady Mellor3dff9e62019-02-05 18:12:53 -0800459 public void setPointerPosition(float x) {
Lyn Han9a2f5cf2019-05-23 11:01:41 -0700460 float halfPointerWidth = mPointerWidth / 2f;
461 float pointerLeft = x - halfPointerWidth;
462 mPointerView.setTranslationX(pointerLeft);
Lyn Hanf74ba672019-05-20 16:08:48 -0700463 mPointerView.setVisibility(VISIBLE);
Mady Mellordea7ecf2018-12-10 15:47:40 -0800464 }
465
466 /**
Mady Mellor3dff9e62019-02-05 18:12:53 -0800467 * Removes and releases an ActivityView if one was previously created for this bubble.
Mady Mellordea7ecf2018-12-10 15:47:40 -0800468 */
Mady Mellor94d94a72019-03-05 18:16:59 -0800469 public void cleanUpExpandedState() {
470 removeView(mNotifRow);
471
Mady Mellor3dff9e62019-02-05 18:12:53 -0800472 if (mActivityView == null) {
Mark Renouf89b1a4a2018-12-04 14:59:45 -0500473 return;
474 }
Mark Renouf28c250d2019-02-25 16:47:34 -0500475 if (mActivityViewReady) {
476 mActivityView.release();
Mady Mellordea7ecf2018-12-10 15:47:40 -0800477 }
Mark Renouf28c250d2019-02-25 16:47:34 -0500478 removeView(mActivityView);
Mady Mellor3dff9e62019-02-05 18:12:53 -0800479 mActivityView = null;
480 mActivityViewReady = false;
Mady Mellordea7ecf2018-12-10 15:47:40 -0800481 }
482
Mady Mellor3dff9e62019-02-05 18:12:53 -0800483 private boolean usingActivityView() {
Mady Mellor323fb0b2019-03-25 12:15:22 -0700484 return mBubbleIntent != null && mActivityView != null;
Mady Mellor3dff9e62019-02-05 18:12:53 -0800485 }
486
Mady Mellor390bff42019-04-05 15:09:01 -0700487 /**
488 * @return the display id of the virtual display.
489 */
490 public int getVirtualDisplayId() {
491 if (usingActivityView()) {
492 return mActivityView.getVirtualDisplayId();
493 }
494 return INVALID_DISPLAY;
495 }
496
Mady Mellor3dff9e62019-02-05 18:12:53 -0800497 private void applyRowState(ExpandableNotificationRow view) {
498 view.reset();
499 view.setHeadsUp(false);
500 view.resetTranslation();
501 view.setOnKeyguard(false);
Mady Mellor3dff9e62019-02-05 18:12:53 -0800502 view.setClipBottomAmount(0);
503 view.setClipTopAmount(0);
504 view.setContentTransformationAmount(0, false);
505 view.setIconsVisible(true);
506
507 // TODO - Need to reset this (and others) when view goes back in shade, leave for now
508 // view.setTopRoundness(1, false);
509 // view.setBottomRoundness(1, false);
510
511 ExpandableViewState viewState = view.getViewState();
512 viewState = viewState == null ? new ExpandableViewState() : viewState;
513 viewState.height = view.getIntrinsicHeight();
514 viewState.gone = false;
515 viewState.hidden = false;
516 viewState.dimmed = false;
Mady Mellor3dff9e62019-02-05 18:12:53 -0800517 viewState.alpha = 1f;
518 viewState.notGoneIndex = -1;
519 viewState.xTranslation = 0;
520 viewState.yTranslation = 0;
521 viewState.zTranslation = 0;
522 viewState.scaleX = 1;
523 viewState.scaleY = 1;
524 viewState.inShelf = true;
525 viewState.headsUpIsVisible = false;
526 viewState.applyToView(view);
Mady Mellordea7ecf2018-12-10 15:47:40 -0800527 }
Mady Mellor9801e852019-01-22 14:50:28 -0800528
529 private Intent getSettingsIntent(String packageName, final int appUid) {
Lyn Han754e77b2019-04-30 14:34:49 -0700530 final Intent intent = new Intent(Settings.ACTION_APP_NOTIFICATION_BUBBLE_SETTINGS);
Mady Mellor9801e852019-01-22 14:50:28 -0800531 intent.putExtra(Settings.EXTRA_APP_PACKAGE, packageName);
532 intent.putExtra(Settings.EXTRA_APP_UID, appUid);
533 intent.addFlags(Intent.FLAG_ACTIVITY_MULTIPLE_TASK);
534 intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
Lyn Han754e77b2019-04-30 14:34:49 -0700535 intent.addFlags(Intent.FLAG_ACTIVITY_SINGLE_TOP);
Mady Mellor9801e852019-01-22 14:50:28 -0800536 return intent;
537 }
Mady Mellore8e07712019-01-23 12:45:33 -0800538
Mady Mellor3dff9e62019-02-05 18:12:53 -0800539 @Nullable
540 private PendingIntent getBubbleIntent(NotificationEntry entry) {
541 Notification notif = entry.notification.getNotification();
Mady Mellor3dff9e62019-02-05 18:12:53 -0800542 Notification.BubbleMetadata data = notif.getBubbleMetadata();
Mady Mellorca0c24c2019-05-16 16:14:32 -0700543 if (BubbleController.canLaunchInActivityView(mContext, entry) && data != null) {
Mady Mellor3dff9e62019-02-05 18:12:53 -0800544 return data.getIntent();
Mady Mellor3dff9e62019-02-05 18:12:53 -0800545 }
546 return null;
547 }
548
549 /**
Mady Mellore8e07712019-01-23 12:45:33 -0800550 * Listener that is notified when a bubble is blocked.
551 */
552 public interface OnBubbleBlockedListener {
553 /**
554 * Called when a bubble is blocked for the provided entry.
555 */
556 void onBubbleBlocked(NotificationEntry entry);
557 }
Steven Wub00225b2019-02-08 14:27:42 -0500558
559 /**
560 * Logs bubble UI click event.
561 *
Steven Wu45e38ae2019-03-25 16:16:59 -0400562 * @param entry the bubble notification entry that user is interacting with.
Steven Wub00225b2019-02-08 14:27:42 -0500563 * @param action the user interaction enum.
564 */
Steven Wu45e38ae2019-03-25 16:16:59 -0400565 private void logBubbleClickEvent(NotificationEntry entry, int action) {
566 StatusBarNotification notification = entry.notification;
Steven Wub00225b2019-02-08 14:27:42 -0500567 StatsLog.write(StatsLog.BUBBLE_UI_CHANGED,
568 notification.getPackageName(),
569 notification.getNotification().getChannelId(),
570 notification.getId(),
571 mStackView.getBubbleIndex(mStackView.getExpandedBubble()),
572 mStackView.getBubbleCount(),
573 action,
574 mStackView.getNormalizedXPosition(),
Steven Wu45e38ae2019-03-25 16:16:59 -0400575 mStackView.getNormalizedYPosition(),
Steven Wu8ba8ca92019-04-11 10:47:42 -0400576 entry.showInShadeWhenBubble(),
577 entry.isForegroundService(),
578 BubbleController.isForegroundApp(mContext, notification.getPackageName()));
Steven Wub00225b2019-02-08 14:27:42 -0500579 }
Mady Mellor7af771a2019-03-07 15:04:54 -0800580
581 private int getDimenForPackageUser(int resId, String pkg, int userId) {
582 Resources r;
583 if (pkg != null) {
584 try {
585 if (userId == UserHandle.USER_ALL) {
586 userId = UserHandle.USER_SYSTEM;
587 }
588 r = mPm.getResourcesForApplicationAsUser(pkg, userId);
589 return r.getDimensionPixelSize(resId);
590 } catch (PackageManager.NameNotFoundException ex) {
591 // Uninstalled, don't care
592 } catch (Resources.NotFoundException e) {
593 // Invalid res id, return 0 and user our default
594 Log.e(TAG, "Couldn't find desired height res id", e);
595 }
596 }
597 return 0;
598 }
Mady Mellordea7ecf2018-12-10 15:47:40 -0800599}