blob: 0d5261dcb7f33e2b461191a6398f523b6bf1c308 [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 Mellorc41ed322019-09-25 11:19:26 -070019import static android.content.Intent.FLAG_ACTIVITY_MULTIPLE_TASK;
20import static android.content.Intent.FLAG_ACTIVITY_NEW_DOCUMENT;
Mady Mellor390bff42019-04-05 15:09:01 -070021import static android.view.Display.INVALID_DISPLAY;
Jorim Jaggi924ef752020-01-29 17:26:55 +010022import static android.view.ViewRootImpl.NEW_INSETS_MODE_FULL;
23import static android.view.ViewRootImpl.sNewInsetsMode;
Beverlya53fb0d2020-01-29 15:26:13 -050024
Mark Renoufdb861c92019-07-26 13:58:17 -040025import static com.android.systemui.bubbles.BubbleDebugConfig.DEBUG_BUBBLE_EXPANDED_VIEW;
Issei Suzukia8d07312019-06-07 12:56:19 +020026import static com.android.systemui.bubbles.BubbleDebugConfig.TAG_BUBBLES;
27import static com.android.systemui.bubbles.BubbleDebugConfig.TAG_WITH_CLASS_NAME;
Mady Mellorca0c24c2019-05-16 16:14:32 -070028
Lyn Hanb58c7562020-01-07 14:29:20 -080029import android.annotation.Nullable;
Mady Mellor60101c92019-04-11 19:04:00 -070030import android.app.ActivityOptions;
Mark Renouf5c732b32019-06-12 15:14:54 -040031import android.app.ActivityTaskManager;
Mady Mellor3dff9e62019-02-05 18:12:53 -080032import android.app.ActivityView;
Mady Mellor9801e852019-01-22 14:50:28 -080033import android.app.PendingIntent;
Mark Renouf5c732b32019-06-12 15:14:54 -040034import android.content.ComponentName;
Mady Mellordea7ecf2018-12-10 15:47:40 -080035import android.content.Context;
Mady Mellor9801e852019-01-22 14:50:28 -080036import android.content.Intent;
Mady Mellordea7ecf2018-12-10 15:47:40 -080037import android.content.res.Resources;
Mady Mellordd497052019-01-30 17:23:48 -080038import android.content.res.TypedArray;
Mady Mellordea7ecf2018-12-10 15:47:40 -080039import android.graphics.Color;
Mady Mellor3dff9e62019-02-05 18:12:53 -080040import android.graphics.Insets;
41import android.graphics.Point;
Mady Mellor47b11e32019-07-11 19:06:21 -070042import android.graphics.Rect;
Mady Mellordea7ecf2018-12-10 15:47:40 -080043import android.graphics.drawable.ShapeDrawable;
Mark Renouf5c732b32019-06-12 15:14:54 -040044import android.os.RemoteException;
Steven Wub00225b2019-02-08 14:27:42 -050045import android.service.notification.StatusBarNotification;
Mady Mellordea7ecf2018-12-10 15:47:40 -080046import android.util.AttributeSet;
Mady Mellor9801e852019-01-22 14:50:28 -080047import android.util.Log;
Mady Mellordea7ecf2018-12-10 15:47:40 -080048import android.view.View;
Mady Mellor3dff9e62019-02-05 18:12:53 -080049import android.view.WindowInsets;
Mady Mellora96c9ed2019-06-07 12:55:26 -070050import android.view.WindowManager;
Mady Mellordea7ecf2018-12-10 15:47:40 -080051import android.widget.LinearLayout;
52
Mark Renouf34d04f32019-05-13 15:53:18 -040053import com.android.internal.policy.ScreenDecorationsUtils;
Mady Mellor3dff9e62019-02-05 18:12:53 -080054import com.android.systemui.Dependency;
Mady Mellordea7ecf2018-12-10 15:47:40 -080055import com.android.systemui.R;
56import com.android.systemui.recents.TriangleShape;
Muhammad Qureshi9bced7d2020-01-16 15:22:12 -080057import com.android.systemui.shared.system.SysUiStatsLog;
Lyn Han754e77b2019-04-30 14:34:49 -070058import com.android.systemui.statusbar.AlphaOptimizedButton;
Beverlya53fb0d2020-01-29 15:26:13 -050059import com.android.systemui.statusbar.notification.collection.NotificationEntry;
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 {
Issei Suzukia8d07312019-06-07 12:56:19 +020065 private static final String TAG = TAG_WITH_CLASS_NAME ? "BubbleExpandedView" : TAG_BUBBLES;
Mady Mellordea7ecf2018-12-10 15:47:40 -080066
Issei Suzuki734bc942019-06-05 13:59:52 +020067 private enum ActivityViewStatus {
68 // ActivityView is being initialized, cannot start an activity yet.
69 INITIALIZING,
70 // ActivityView is initialized, and ready to start an activity.
71 INITIALIZED,
72 // Activity runs in the ActivityView.
73 ACTIVITY_STARTED,
74 // ActivityView is released, so activity launching will no longer be permitted.
75 RELEASED,
76 }
Mady Mellordea7ecf2018-12-10 15:47:40 -080077
78 // The triangle pointing to the expanded view
79 private View mPointerView;
Mady Mellor44ee2fe2019-01-30 17:51:16 -080080 private int mPointerMargin;
Mady Mellore8e07712019-01-23 12:45:33 -080081
Lyn Han754e77b2019-04-30 14:34:49 -070082 private AlphaOptimizedButton mSettingsIcon;
Mady Mellore8e07712019-01-23 12:45:33 -080083
Mady Mellor3dff9e62019-02-05 18:12:53 -080084 // Views for expanded state
Mady Mellor3dff9e62019-02-05 18:12:53 -080085 private ActivityView mActivityView;
86
Issei Suzuki734bc942019-06-05 13:59:52 +020087 private ActivityViewStatus mActivityViewStatus = ActivityViewStatus.INITIALIZING;
Mark Renouf5c732b32019-06-12 15:14:54 -040088 private int mTaskId = -1;
89
Lyn Hanb58c7562020-01-07 14:29:20 -080090 private PendingIntent mPendingIntent;
Mady Mellor3dff9e62019-02-05 18:12:53 -080091
Mady Mellor5d8f1402019-02-21 18:23:52 -080092 private boolean mKeyboardVisible;
93 private boolean mNeedsNewHeight;
94
Mady Mellora96c9ed2019-06-07 12:55:26 -070095 private Point mDisplaySize;
Mady Mellorfe7ec032019-01-30 17:32:49 -080096 private int mMinHeight;
Lyn Han02cca812019-04-02 16:27:32 -070097 private int mSettingsIconHeight;
Lyn Han02cca812019-04-02 16:27:32 -070098 private int mPointerWidth;
99 private int mPointerHeight;
Mark Renouf34d04f32019-05-13 15:53:18 -0400100 private ShapeDrawable mPointerDrawable;
Mady Mellor47b11e32019-07-11 19:06:21 -0700101 private Rect mTempRect = new Rect();
102 private int[] mTempLoc = new int[2];
103 private int mExpandedViewTouchSlop;
Mady Mellordea7ecf2018-12-10 15:47:40 -0800104
Lyn Hanb58c7562020-01-07 14:29:20 -0800105 @Nullable private Bubble mBubble;
106
107 private boolean mIsOverflow;
Mady Mellore8e07712019-01-23 12:45:33 -0800108
Mady Mellor3dff9e62019-02-05 18:12:53 -0800109 private BubbleController mBubbleController = Dependency.get(BubbleController.class);
Mady Mellor9be3bed2019-08-21 17:26:26 -0700110 private WindowManager mWindowManager;
Mady Mellor9801e852019-01-22 14:50:28 -0800111
Mady Mellor9801e852019-01-22 14:50:28 -0800112 private BubbleStackView mStackView;
113
Mady Mellor3dff9e62019-02-05 18:12:53 -0800114 private ActivityView.StateCallback mStateCallback = new ActivityView.StateCallback() {
115 @Override
116 public void onActivityViewReady(ActivityView view) {
Mark Renoufdb861c92019-07-26 13:58:17 -0400117 if (DEBUG_BUBBLE_EXPANDED_VIEW) {
118 Log.d(TAG, "onActivityViewReady: mActivityViewStatus=" + mActivityViewStatus
119 + " bubble=" + getBubbleKey());
120 }
Issei Suzuki734bc942019-06-05 13:59:52 +0200121 switch (mActivityViewStatus) {
122 case INITIALIZING:
123 case INITIALIZED:
124 // Custom options so there is no activity transition animation
125 ActivityOptions options = ActivityOptions.makeCustomAnimation(getContext(),
126 0 /* enterResId */, 0 /* exitResId */);
127 // Post to keep the lifecycle normal
Mark Renoufdb861c92019-07-26 13:58:17 -0400128 post(() -> {
129 if (DEBUG_BUBBLE_EXPANDED_VIEW) {
130 Log.d(TAG, "onActivityViewReady: calling startActivity, "
131 + "bubble=" + getBubbleKey());
132 }
Mady Mellor8454ddf2019-08-15 11:16:23 -0700133 try {
Lyn Hanb58c7562020-01-07 14:29:20 -0800134 if (!mIsOverflow && mBubble.usingShortcutInfo()) {
Mady Mellorb547e5e2019-12-02 10:15:56 -0800135 mActivityView.startShortcutActivity(mBubble.getShortcutInfo(),
136 options, null /* sourceBounds */);
137 } else {
138 Intent fillInIntent = new Intent();
139 // Apply flags to make behaviour match documentLaunchMode=always.
140 fillInIntent.addFlags(FLAG_ACTIVITY_NEW_DOCUMENT);
141 fillInIntent.addFlags(FLAG_ACTIVITY_MULTIPLE_TASK);
Lyn Hanb58c7562020-01-07 14:29:20 -0800142 mActivityView.startActivity(mPendingIntent, fillInIntent, options);
Mady Mellorb547e5e2019-12-02 10:15:56 -0800143 }
Mady Mellor8454ddf2019-08-15 11:16:23 -0700144 } catch (RuntimeException e) {
145 // If there's a runtime exception here then there's something
146 // wrong with the intent, we can't really recover / try to populate
147 // the bubble again so we'll just remove it.
148 Log.w(TAG, "Exception while displaying bubble: " + getBubbleKey()
149 + ", " + e.getMessage() + "; removing bubble");
Beverlya53fb0d2020-01-29 15:26:13 -0500150 mBubbleController.removeBubble(getBubbleEntry(),
Mady Mellor8454ddf2019-08-15 11:16:23 -0700151 BubbleController.DISMISS_INVALID_INTENT);
152 }
Mark Renoufdb861c92019-07-26 13:58:17 -0400153 });
Issei Suzuki734bc942019-06-05 13:59:52 +0200154 mActivityViewStatus = ActivityViewStatus.ACTIVITY_STARTED;
Mady Mellor6d002032019-02-13 13:45:17 -0800155 }
Mady Mellor3dff9e62019-02-05 18:12:53 -0800156 }
157
158 @Override
159 public void onActivityViewDestroyed(ActivityView view) {
Mark Renoufdb861c92019-07-26 13:58:17 -0400160 if (DEBUG_BUBBLE_EXPANDED_VIEW) {
161 Log.d(TAG, "onActivityViewDestroyed: mActivityViewStatus=" + mActivityViewStatus
162 + " bubble=" + getBubbleKey());
163 }
Issei Suzuki734bc942019-06-05 13:59:52 +0200164 mActivityViewStatus = ActivityViewStatus.RELEASED;
Mady Mellor3dff9e62019-02-05 18:12:53 -0800165 }
166
Mark Renouf5c732b32019-06-12 15:14:54 -0400167 @Override
168 public void onTaskCreated(int taskId, ComponentName componentName) {
Mark Renoufdb861c92019-07-26 13:58:17 -0400169 if (DEBUG_BUBBLE_EXPANDED_VIEW) {
170 Log.d(TAG, "onTaskCreated: taskId=" + taskId
171 + " bubble=" + getBubbleKey());
172 }
Mark Renouf5c732b32019-06-12 15:14:54 -0400173 // Since Bubble ActivityView applies singleTaskDisplay this is
174 // guaranteed to only be called once per ActivityView. The taskId is
175 // saved to use for removeTask, preventing appearance in recent tasks.
176 mTaskId = taskId;
Mady Mellor3dff9e62019-02-05 18:12:53 -0800177 }
178
179 /**
180 * This is only called for tasks on this ActivityView, which is also set to
181 * single-task mode -- meaning never more than one task on this display. If a task
182 * is being removed, it's the top Activity finishing and this bubble should
183 * be removed or collapsed.
184 */
185 @Override
186 public void onTaskRemovalStarted(int taskId) {
Mark Renoufdb861c92019-07-26 13:58:17 -0400187 if (DEBUG_BUBBLE_EXPANDED_VIEW) {
188 Log.d(TAG, "onTaskRemovalStarted: taskId=" + taskId
189 + " mActivityViewStatus=" + mActivityViewStatus
190 + " bubble=" + getBubbleKey());
191 }
Mady Mellorff076eb2019-11-13 10:12:06 -0800192 if (mBubble != null && !mBubbleController.isUserCreatedBubble(mBubble.getKey())) {
193 // Must post because this is called from a binder thread.
Beverlya53fb0d2020-01-29 15:26:13 -0500194 post(() -> mBubbleController.removeBubble(mBubble.getEntry(),
Mady Mellorff076eb2019-11-13 10:12:06 -0800195 BubbleController.DISMISS_TASK_FINISHED));
Mady Mellor3dff9e62019-02-05 18:12:53 -0800196 }
197 }
198 };
Mady Mellore8e07712019-01-23 12:45:33 -0800199
Mady Mellor3d82e682019-02-05 13:34:48 -0800200 public BubbleExpandedView(Context context) {
Mady Mellordea7ecf2018-12-10 15:47:40 -0800201 this(context, null);
202 }
203
Mady Mellor3d82e682019-02-05 13:34:48 -0800204 public BubbleExpandedView(Context context, AttributeSet attrs) {
Mady Mellordea7ecf2018-12-10 15:47:40 -0800205 this(context, attrs, 0);
206 }
207
Mady Mellor3d82e682019-02-05 13:34:48 -0800208 public BubbleExpandedView(Context context, AttributeSet attrs, int defStyleAttr) {
Mady Mellordea7ecf2018-12-10 15:47:40 -0800209 this(context, attrs, defStyleAttr, 0);
210 }
211
Mady Mellor3d82e682019-02-05 13:34:48 -0800212 public BubbleExpandedView(Context context, AttributeSet attrs, int defStyleAttr,
Mady Mellordea7ecf2018-12-10 15:47:40 -0800213 int defStyleRes) {
214 super(context, attrs, defStyleAttr, defStyleRes);
Mady Mellora96c9ed2019-06-07 12:55:26 -0700215 mDisplaySize = new Point();
Mady Mellor9be3bed2019-08-21 17:26:26 -0700216 mWindowManager = (WindowManager) context.getSystemService(Context.WINDOW_SERVICE);
Mady Mellor8fe411d2019-08-16 11:27:53 -0700217 // Get the real size -- this includes screen decorations (notches, statusbar, navbar).
Mady Mellor9be3bed2019-08-21 17:26:26 -0700218 mWindowManager.getDefaultDisplay().getRealSize(mDisplaySize);
Mady Mellor47b11e32019-07-11 19:06:21 -0700219 Resources res = getResources();
220 mMinHeight = res.getDimensionPixelSize(R.dimen.bubble_expanded_default_height);
221 mPointerMargin = res.getDimensionPixelSize(R.dimen.bubble_pointer_margin);
222 mExpandedViewTouchSlop = res.getDimensionPixelSize(R.dimen.bubble_expanded_view_slop);
Mady Mellordea7ecf2018-12-10 15:47:40 -0800223 }
224
225 @Override
226 protected void onFinishInflate() {
227 super.onFinishInflate();
Mark Renoufdb861c92019-07-26 13:58:17 -0400228 if (DEBUG_BUBBLE_EXPANDED_VIEW) {
229 Log.d(TAG, "onFinishInflate: bubble=" + getBubbleKey());
230 }
Mady Mellordea7ecf2018-12-10 15:47:40 -0800231
232 Resources res = getResources();
233 mPointerView = findViewById(R.id.pointer_view);
Lyn Han02cca812019-04-02 16:27:32 -0700234 mPointerWidth = res.getDimensionPixelSize(R.dimen.bubble_pointer_width);
235 mPointerHeight = res.getDimensionPixelSize(R.dimen.bubble_pointer_height);
Mady Mellordd497052019-01-30 17:23:48 -0800236
Mady Mellordd497052019-01-30 17:23:48 -0800237
Mark Renouf34d04f32019-05-13 15:53:18 -0400238 mPointerDrawable = new ShapeDrawable(TriangleShape.create(
Lyn Han5aa27e22019-05-15 10:55:07 -0700239 mPointerWidth, mPointerHeight, true /* pointUp */));
Mark Renouf34d04f32019-05-13 15:53:18 -0400240 mPointerView.setBackground(mPointerDrawable);
Mady Mellora96c9ed2019-06-07 12:55:26 -0700241 mPointerView.setVisibility(INVISIBLE);
Mady Mellor9801e852019-01-22 14:50:28 -0800242
Lyn Han02cca812019-04-02 16:27:32 -0700243 mSettingsIconHeight = getContext().getResources().getDimensionPixelSize(
Mady Mellor818eef02019-08-16 16:12:29 -0700244 R.dimen.bubble_settings_size);
Lyn Han02cca812019-04-02 16:27:32 -0700245 mSettingsIcon = findViewById(R.id.settings_button);
Lyn Han02cca812019-04-02 16:27:32 -0700246 mSettingsIcon.setOnClickListener(this);
Lyn Han02cca812019-04-02 16:27:32 -0700247
Mady Mellor3dff9e62019-02-05 18:12:53 -0800248 mActivityView = new ActivityView(mContext, null /* attrs */, 0 /* defStyle */,
249 true /* singleTaskInstance */);
Lyn Hanb58c7562020-01-07 14:29:20 -0800250
lumarkdc9b3192019-07-23 21:18:17 +0800251 // Set ActivityView's alpha value as zero, since there is no view content to be shown.
Issei Suzukicac2a502019-04-16 16:52:50 +0200252 setContentVisibility(false);
Mady Mellor3dff9e62019-02-05 18:12:53 -0800253 addView(mActivityView);
254
Lyn Han5aa27e22019-05-15 10:55:07 -0700255 // Expanded stack layout, top to bottom:
256 // Expanded view container
257 // ==> bubble row
258 // ==> expanded view
259 // ==> activity view
260 // ==> manage button
261 bringChildToFront(mActivityView);
262 bringChildToFront(mSettingsIcon);
Mady Mellor52b1ac62019-04-10 16:59:03 -0700263
Mark Renouf34d04f32019-05-13 15:53:18 -0400264 applyThemeAttrs();
265
Mady Mellor5d8f1402019-02-21 18:23:52 -0800266 setOnApplyWindowInsetsListener((View view, WindowInsets insets) -> {
267 // Keep track of IME displaying because we should not make any adjustments that might
268 // cause a config change while the IME is displayed otherwise it'll loose focus.
Mady Mellor3dff9e62019-02-05 18:12:53 -0800269 final int keyboardHeight = insets.getSystemWindowInsetBottom()
270 - insets.getStableInsetBottom();
Mady Mellor5d8f1402019-02-21 18:23:52 -0800271 mKeyboardVisible = keyboardHeight != 0;
272 if (!mKeyboardVisible && mNeedsNewHeight) {
273 updateHeight();
274 }
Mady Mellor3dff9e62019-02-05 18:12:53 -0800275 return view.onApplyWindowInsets(insets);
276 });
Mady Mellore8e07712019-01-23 12:45:33 -0800277 }
278
Mark Renoufdb861c92019-07-26 13:58:17 -0400279 private String getBubbleKey() {
280 return mBubble != null ? mBubble.getKey() : "null";
281 }
282
Beverlya53fb0d2020-01-29 15:26:13 -0500283 private NotificationEntry getBubbleEntry() {
284 return mBubble != null ? mBubble.getEntry() : null;
285 }
286
Mark Renouf34d04f32019-05-13 15:53:18 -0400287 void applyThemeAttrs() {
Lyn Han61bfdb32019-12-10 14:34:12 -0800288 final TypedArray ta = mContext.obtainStyledAttributes(
289 new int[] {
290 android.R.attr.colorBackgroundFloating,
291 android.R.attr.dialogCornerRadius});
292 int bgColor = ta.getColor(0, Color.WHITE);
293 float cornerRadius = ta.getDimensionPixelSize(1, 0);
Mark Renouf34d04f32019-05-13 15:53:18 -0400294 ta.recycle();
295
Mark Renouf34d04f32019-05-13 15:53:18 -0400296 mPointerDrawable.setTint(bgColor);
Mark Renouf34d04f32019-05-13 15:53:18 -0400297 if (ScreenDecorationsUtils.supportsRoundedCornersOnWindows(mContext.getResources())) {
298 mActivityView.setCornerRadius(cornerRadius);
299 }
300 }
301
Mady Mellor5d8f1402019-02-21 18:23:52 -0800302 @Override
303 protected void onDetachedFromWindow() {
304 super.onDetachedFromWindow();
305 mKeyboardVisible = false;
306 mNeedsNewHeight = false;
307 if (mActivityView != null) {
308 mActivityView.setForwardedInsets(Insets.of(0, 0, 0, 0));
309 }
Mark Renoufdb861c92019-07-26 13:58:17 -0400310 if (DEBUG_BUBBLE_EXPANDED_VIEW) {
311 Log.d(TAG, "onDetachedFromWindow: bubble=" + getBubbleKey());
312 }
Mady Mellor5d8f1402019-02-21 18:23:52 -0800313 }
314
315 /**
Issei Suzukicac2a502019-04-16 16:52:50 +0200316 * Set visibility of contents in the expanded state.
317 *
318 * @param visibility {@code true} if the contents should be visible on the screen.
319 *
320 * Note that this contents visibility doesn't affect visibility at {@link android.view.View},
321 * and setting {@code false} actually means rendering the contents in transparent.
322 */
323 void setContentVisibility(boolean visibility) {
Mark Renoufdb861c92019-07-26 13:58:17 -0400324 if (DEBUG_BUBBLE_EXPANDED_VIEW) {
325 Log.d(TAG, "setContentVisibility: visibility=" + visibility
326 + " bubble=" + getBubbleKey());
327 }
Issei Suzukicac2a502019-04-16 16:52:50 +0200328 final float alpha = visibility ? 1f : 0f;
329 mPointerView.setAlpha(alpha);
330 if (mActivityView != null) {
331 mActivityView.setAlpha(alpha);
332 }
333 }
334
335 /**
Mady Mellor5d8f1402019-02-21 18:23:52 -0800336 * Called by {@link BubbleStackView} when the insets for the expanded state should be updated.
337 * This should be done post-move and post-animation.
338 */
339 void updateInsets(WindowInsets insets) {
340 if (usingActivityView()) {
Mady Mellor8fe411d2019-08-16 11:27:53 -0700341 int[] screenLoc = mActivityView.getLocationOnScreen();
342 final int activityViewBottom = screenLoc[1] + mActivityView.getHeight();
Mady Mellor9be3bed2019-08-21 17:26:26 -0700343 final int keyboardTop = mDisplaySize.y - Math.max(insets.getSystemWindowInsetBottom(),
344 insets.getDisplayCutout() != null
345 ? insets.getDisplayCutout().getSafeInsetBottom()
346 : 0);
Mady Mellor8fe411d2019-08-16 11:27:53 -0700347 final int insetsBottom = Math.max(activityViewBottom - keyboardTop, 0);
Jorim Jaggi924ef752020-01-29 17:26:55 +0100348
349 // TODO: Temporary hack to offset the view until we can properly inset Bubbles again.
350 if (sNewInsetsMode == NEW_INSETS_MODE_FULL) {
351 mStackView.animate().translationY(-insetsBottom);
352 } else {
353 mActivityView.setForwardedInsets(Insets.of(0, 0, 0, insetsBottom));
354 }
Mady Mellor5d8f1402019-02-21 18:23:52 -0800355 }
356 }
357
Mady Mellor247ca2c2019-12-02 16:18:59 -0800358 void setStackView(BubbleStackView stackView) {
Mady Mellor9801e852019-01-22 14:50:28 -0800359 mStackView = stackView;
Mady Mellor6d002032019-02-13 13:45:17 -0800360 }
361
Lyn Hanb58c7562020-01-07 14:29:20 -0800362 public void setOverflow(boolean overflow) {
363 mIsOverflow = overflow;
364
365 Intent target = new Intent(mContext, BubbleOverflowActivity.class);
366 mPendingIntent = PendingIntent.getActivity(mContext, /* requestCode */ 0,
367 target, PendingIntent.FLAG_UPDATE_CURRENT);
368 mSettingsIcon.setVisibility(GONE);
369 }
370
Mady Mellor6d002032019-02-13 13:45:17 -0800371 /**
Mady Mellor247ca2c2019-12-02 16:18:59 -0800372 * Sets the bubble used to populate this view.
Mady Mellor6d002032019-02-13 13:45:17 -0800373 */
Mady Mellor247ca2c2019-12-02 16:18:59 -0800374 void update(Bubble bubble) {
375 if (DEBUG_BUBBLE_EXPANDED_VIEW) {
376 Log.d(TAG, "update: bubble=" + (bubble != null ? bubble.getKey() : "null"));
377 }
378 boolean isNew = mBubble == null;
Lyn Hanb58c7562020-01-07 14:29:20 -0800379 if (isNew || bubble != null && bubble.getKey().equals(mBubble.getKey())) {
Mady Mellor247ca2c2019-12-02 16:18:59 -0800380 mBubble = bubble;
381 mSettingsIcon.setContentDescription(getResources().getString(
382 R.string.bubbles_settings_button_description, bubble.getAppName()));
383
384 if (isNew) {
Lyn Hanb58c7562020-01-07 14:29:20 -0800385 mPendingIntent = mBubble.getBubbleIntent();
386 if (mPendingIntent != null || mBubble.getShortcutInfo() != null) {
Mady Mellor247ca2c2019-12-02 16:18:59 -0800387 setContentVisibility(false);
388 mActivityView.setVisibility(VISIBLE);
389 }
390 }
391 applyThemeAttrs();
392 } else {
393 Log.w(TAG, "Trying to update entry with different key, new bubble: "
394 + bubble.getKey() + " old bubble: " + bubble.getKey());
395 }
396 }
397
398 /**
399 * Lets activity view know it should be shown / populated with activity content.
400 */
401 void populateExpandedView() {
Mark Renoufdb861c92019-07-26 13:58:17 -0400402 if (DEBUG_BUBBLE_EXPANDED_VIEW) {
403 Log.d(TAG, "populateExpandedView: "
404 + "bubble=" + getBubbleKey());
405 }
406
Mady Mellor5029fa62019-03-05 12:16:21 -0800407 if (usingActivityView()) {
408 mActivityView.setCallback(mStateCallback);
409 } else {
Issei Suzukia91f3962019-06-07 11:48:23 +0200410 Log.e(TAG, "Cannot populate expanded view.");
Mady Mellor5029fa62019-03-05 12:16:21 -0800411 }
Mady Mellor9801e852019-01-22 14:50:28 -0800412 }
413
Mark Renouf041d7262019-02-06 12:09:41 -0500414 boolean performBackPressIfNeeded() {
Mady Mellor323fb0b2019-03-25 12:15:22 -0700415 if (!usingActivityView()) {
Mark Renouf041d7262019-02-06 12:09:41 -0500416 return false;
417 }
418 mActivityView.performBackPress();
419 return true;
420 }
421
Lyn Hanb58c7562020-01-07 14:29:20 -0800422 // TODO(138116789) Fix overflow height.
Mady Mellorfe7ec032019-01-30 17:32:49 -0800423 void updateHeight() {
Mark Renoufdb861c92019-07-26 13:58:17 -0400424 if (DEBUG_BUBBLE_EXPANDED_VIEW) {
425 Log.d(TAG, "updateHeight: bubble=" + getBubbleKey());
426 }
Mady Mellorfe7ec032019-01-30 17:32:49 -0800427 if (usingActivityView()) {
Lyn Hanb58c7562020-01-07 14:29:20 -0800428 float desiredHeight = mMinHeight;
429 if (!mIsOverflow) {
430 desiredHeight = Math.max(mBubble.getDesiredHeight(mContext), mMinHeight);
431 }
Mady Mellor8fe411d2019-08-16 11:27:53 -0700432 float height = Math.min(desiredHeight, getMaxExpandedHeight());
Mady Mellorfe7ec032019-01-30 17:32:49 -0800433 height = Math.max(height, mMinHeight);
434 LayoutParams lp = (LayoutParams) mActivityView.getLayoutParams();
Mady Mellor5d8f1402019-02-21 18:23:52 -0800435 mNeedsNewHeight = lp.height != height;
436 if (!mKeyboardVisible) {
437 // If the keyboard is visible... don't adjust the height because that will cause
438 // a configuration change and the keyboard will be lost.
Mady Mellor7af771a2019-03-07 15:04:54 -0800439 lp.height = (int) height;
Mady Mellor5d8f1402019-02-21 18:23:52 -0800440 mActivityView.setLayoutParams(lp);
441 mNeedsNewHeight = false;
442 }
Mark Renoufdb861c92019-07-26 13:58:17 -0400443 if (DEBUG_BUBBLE_EXPANDED_VIEW) {
444 Log.d(TAG, "updateHeight: bubble=" + getBubbleKey() + " height=" + height
445 + " mNeedsNewHeight=" + mNeedsNewHeight);
446 }
Mady Mellorfe7ec032019-01-30 17:32:49 -0800447 }
448 }
449
Mady Mellora96c9ed2019-06-07 12:55:26 -0700450 private int getMaxExpandedHeight() {
Mady Mellor9be3bed2019-08-21 17:26:26 -0700451 mWindowManager.getDefaultDisplay().getRealSize(mDisplaySize);
Mady Mellora96c9ed2019-06-07 12:55:26 -0700452 int[] windowLocation = mActivityView.getLocationOnScreen();
Mady Mellor8fe411d2019-08-16 11:27:53 -0700453 int bottomInset = getRootWindowInsets() != null
454 ? getRootWindowInsets().getStableInsetBottom()
455 : 0;
Lyn Han0b955ed2020-01-31 09:47:35 -0800456 return mDisplaySize.y - windowLocation[1] - mSettingsIconHeight - mPointerHeight
Mady Mellor8fe411d2019-08-16 11:27:53 -0700457 - mPointerMargin - bottomInset;
Mady Mellora96c9ed2019-06-07 12:55:26 -0700458 }
459
Mady Mellor47b11e32019-07-11 19:06:21 -0700460 /**
461 * Whether the provided x, y values (in raw coordinates) are in a touchable area of the
462 * expanded view.
463 *
464 * The touchable areas are the ActivityView (plus some slop around it) and the manage button.
465 */
466 boolean intersectingTouchableContent(int rawX, int rawY) {
467 mTempRect.setEmpty();
468 if (mActivityView != null) {
469 mTempLoc = mActivityView.getLocationOnScreen();
470 mTempRect.set(mTempLoc[0] - mExpandedViewTouchSlop,
471 mTempLoc[1] - mExpandedViewTouchSlop,
472 mTempLoc[0] + mActivityView.getWidth() + mExpandedViewTouchSlop,
473 mTempLoc[1] + mActivityView.getHeight() + mExpandedViewTouchSlop);
474 }
475 if (mTempRect.contains(rawX, rawY)) {
476 return true;
477 }
478 mTempLoc = mSettingsIcon.getLocationOnScreen();
479 mTempRect.set(mTempLoc[0],
480 mTempLoc[1],
481 mTempLoc[0] + mSettingsIcon.getWidth(),
482 mTempLoc[1] + mSettingsIcon.getHeight());
483 if (mTempRect.contains(rawX, rawY)) {
484 return true;
485 }
486 return false;
487 }
488
Mady Mellor9801e852019-01-22 14:50:28 -0800489 @Override
490 public void onClick(View view) {
Mady Mellor99a302602019-06-14 11:39:56 -0700491 if (mBubble == null) {
Mady Mellor9801e852019-01-22 14:50:28 -0800492 return;
493 }
Mady Mellor9801e852019-01-22 14:50:28 -0800494 int id = view.getId();
Lyn Hanc26ff122019-03-29 16:46:07 -0700495 if (id == R.id.settings_button) {
Mady Mellor99a302602019-06-14 11:39:56 -0700496 Intent intent = mBubble.getSettingsIntent();
Steven Wub00225b2019-02-08 14:27:42 -0500497 mStackView.collapseStack(() -> {
Ned Burns00b4b2d2019-10-17 22:09:27 -0400498 mContext.startActivityAsUser(intent, mBubble.getEntry().getSbn().getUser());
Mady Mellor99a302602019-06-14 11:39:56 -0700499 logBubbleClickEvent(mBubble,
Muhammad Qureshi9bced7d2020-01-16 15:22:12 -0800500 SysUiStatsLog.BUBBLE_UICHANGED__ACTION__HEADER_GO_TO_SETTINGS);
Steven Wub00225b2019-02-08 14:27:42 -0500501 });
Mady Mellor9801e852019-01-22 14:50:28 -0800502 }
Mady Mellordea7ecf2018-12-10 15:47:40 -0800503 }
504
505 /**
Mady Mellor3dff9e62019-02-05 18:12:53 -0800506 * Update appearance of the expanded view being displayed.
507 */
508 public void updateView() {
Mark Renoufdb861c92019-07-26 13:58:17 -0400509 if (DEBUG_BUBBLE_EXPANDED_VIEW) {
510 Log.d(TAG, "updateView: bubble="
511 + getBubbleKey());
512 }
Mady Mellor3dff9e62019-02-05 18:12:53 -0800513 if (usingActivityView()
514 && mActivityView.getVisibility() == VISIBLE
515 && mActivityView.isAttachedToWindow()) {
516 mActivityView.onLocationChanged();
Mady Mellor3dff9e62019-02-05 18:12:53 -0800517 }
Mady Mellorfe7ec032019-01-30 17:32:49 -0800518 updateHeight();
Mady Mellor3dff9e62019-02-05 18:12:53 -0800519 }
520
521 /**
Mady Mellordea7ecf2018-12-10 15:47:40 -0800522 * Set the x position that the tip of the triangle should point to.
523 */
Mady Mellor3dff9e62019-02-05 18:12:53 -0800524 public void setPointerPosition(float x) {
Lyn Han9a2f5cf2019-05-23 11:01:41 -0700525 float halfPointerWidth = mPointerWidth / 2f;
526 float pointerLeft = x - halfPointerWidth;
527 mPointerView.setTranslationX(pointerLeft);
Lyn Hanf74ba672019-05-20 16:08:48 -0700528 mPointerView.setVisibility(VISIBLE);
Mady Mellordea7ecf2018-12-10 15:47:40 -0800529 }
530
531 /**
Mady Mellor3dff9e62019-02-05 18:12:53 -0800532 * Removes and releases an ActivityView if one was previously created for this bubble.
Mady Mellordea7ecf2018-12-10 15:47:40 -0800533 */
Mady Mellor94d94a72019-03-05 18:16:59 -0800534 public void cleanUpExpandedState() {
Mark Renoufdb861c92019-07-26 13:58:17 -0400535 if (DEBUG_BUBBLE_EXPANDED_VIEW) {
536 Log.d(TAG, "cleanUpExpandedState: mActivityViewStatus=" + mActivityViewStatus
537 + ", bubble=" + getBubbleKey());
538 }
Mady Mellor3dff9e62019-02-05 18:12:53 -0800539 if (mActivityView == null) {
Mark Renouf89b1a4a2018-12-04 14:59:45 -0500540 return;
541 }
Issei Suzuki734bc942019-06-05 13:59:52 +0200542 switch (mActivityViewStatus) {
543 case INITIALIZED:
544 case ACTIVITY_STARTED:
545 mActivityView.release();
Mady Mellordea7ecf2018-12-10 15:47:40 -0800546 }
Mark Renouf5c732b32019-06-12 15:14:54 -0400547 if (mTaskId != -1) {
548 try {
549 ActivityTaskManager.getService().removeTask(mTaskId);
550 } catch (RemoteException e) {
551 Log.w(TAG, "Failed to remove taskId " + mTaskId);
552 }
553 mTaskId = -1;
Mady Mellordea7ecf2018-12-10 15:47:40 -0800554 }
Mark Renouf28c250d2019-02-25 16:47:34 -0500555 removeView(mActivityView);
Mark Renouf5c732b32019-06-12 15:14:54 -0400556
Mady Mellor3dff9e62019-02-05 18:12:53 -0800557 mActivityView = null;
Issei Suzuki734bc942019-06-05 13:59:52 +0200558 }
559
560 /**
561 * Called when the last task is removed from a {@link android.hardware.display.VirtualDisplay}
562 * which {@link ActivityView} uses.
563 */
564 void notifyDisplayEmpty() {
Mark Renoufdb861c92019-07-26 13:58:17 -0400565 if (DEBUG_BUBBLE_EXPANDED_VIEW) {
566 Log.d(TAG, "notifyDisplayEmpty: bubble="
567 + getBubbleKey()
568 + " mActivityViewStatus=" + mActivityViewStatus);
569 }
Issei Suzuki734bc942019-06-05 13:59:52 +0200570 if (mActivityViewStatus == ActivityViewStatus.ACTIVITY_STARTED) {
571 mActivityViewStatus = ActivityViewStatus.INITIALIZED;
572 }
Mady Mellordea7ecf2018-12-10 15:47:40 -0800573 }
574
Mady Mellor3dff9e62019-02-05 18:12:53 -0800575 private boolean usingActivityView() {
Lyn Hanb58c7562020-01-07 14:29:20 -0800576 return (mPendingIntent != null || mBubble.getShortcutInfo() != null)
Mady Mellor2ac2d3a2020-01-08 17:18:54 -0800577 && mActivityView != null;
Mady Mellor3dff9e62019-02-05 18:12:53 -0800578 }
579
Mady Mellor390bff42019-04-05 15:09:01 -0700580 /**
581 * @return the display id of the virtual display.
582 */
583 public int getVirtualDisplayId() {
584 if (usingActivityView()) {
585 return mActivityView.getVirtualDisplayId();
586 }
587 return INVALID_DISPLAY;
588 }
589
Steven Wub00225b2019-02-08 14:27:42 -0500590 /**
591 * Logs bubble UI click event.
592 *
Mady Mellor99a302602019-06-14 11:39:56 -0700593 * @param bubble the bubble notification entry that user is interacting with.
Steven Wub00225b2019-02-08 14:27:42 -0500594 * @param action the user interaction enum.
595 */
Mady Mellor99a302602019-06-14 11:39:56 -0700596 private void logBubbleClickEvent(Bubble bubble, int action) {
Ned Burns00b4b2d2019-10-17 22:09:27 -0400597 StatusBarNotification notification = bubble.getEntry().getSbn();
Muhammad Qureshi9bced7d2020-01-16 15:22:12 -0800598 SysUiStatsLog.write(SysUiStatsLog.BUBBLE_UI_CHANGED,
Steven Wub00225b2019-02-08 14:27:42 -0500599 notification.getPackageName(),
600 notification.getNotification().getChannelId(),
601 notification.getId(),
602 mStackView.getBubbleIndex(mStackView.getExpandedBubble()),
603 mStackView.getBubbleCount(),
604 action,
605 mStackView.getNormalizedXPosition(),
Steven Wu45e38ae2019-03-25 16:16:59 -0400606 mStackView.getNormalizedYPosition(),
Mady Mellorb8aaf972019-11-26 10:28:00 -0800607 bubble.showInShade(),
Mady Mellor99a302602019-06-14 11:39:56 -0700608 bubble.isOngoing(),
Mady Mellor6cec3502019-06-17 19:20:49 -0700609 false /* isAppForeground (unused) */);
Mady Mellor7af771a2019-03-07 15:04:54 -0800610 }
Mady Mellordea7ecf2018-12-10 15:47:40 -0800611}