blob: 93fb6972fad59d0cf1ce674dcd63e11fa9e6f7e6 [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
Yuncheol Heo5d62e752020-04-08 14:54:24 -070019import static android.app.WindowConfiguration.WINDOWING_MODE_MULTI_WINDOW;
Mady Mellorc41ed322019-09-25 11:19:26 -070020import static android.content.Intent.FLAG_ACTIVITY_MULTIPLE_TASK;
21import static android.content.Intent.FLAG_ACTIVITY_NEW_DOCUMENT;
Mady Mellor390bff42019-04-05 15:09:01 -070022import static android.view.Display.INVALID_DISPLAY;
Jorim Jaggi924ef752020-01-29 17:26:55 +010023import static android.view.ViewRootImpl.NEW_INSETS_MODE_FULL;
24import static android.view.ViewRootImpl.sNewInsetsMode;
Beverlya53fb0d2020-01-29 15:26:13 -050025
Mark Renoufdb861c92019-07-26 13:58:17 -040026import static com.android.systemui.bubbles.BubbleDebugConfig.DEBUG_BUBBLE_EXPANDED_VIEW;
Issei Suzukia8d07312019-06-07 12:56:19 +020027import static com.android.systemui.bubbles.BubbleDebugConfig.TAG_BUBBLES;
28import static com.android.systemui.bubbles.BubbleDebugConfig.TAG_WITH_CLASS_NAME;
Mady Mellorca0c24c2019-05-16 16:14:32 -070029
Lyn Hanb58c7562020-01-07 14:29:20 -080030import android.annotation.Nullable;
Mady Mellor60101c92019-04-11 19:04:00 -070031import android.app.ActivityOptions;
Mark Renouf5c732b32019-06-12 15:14:54 -040032import android.app.ActivityTaskManager;
Mady Mellor3dff9e62019-02-05 18:12:53 -080033import android.app.ActivityView;
Mady Mellor9801e852019-01-22 14:50:28 -080034import android.app.PendingIntent;
Mark Renouf5c732b32019-06-12 15:14:54 -040035import android.content.ComponentName;
Mady Mellordea7ecf2018-12-10 15:47:40 -080036import android.content.Context;
Mady Mellor9801e852019-01-22 14:50:28 -080037import android.content.Intent;
Mady Mellordea7ecf2018-12-10 15:47:40 -080038import android.content.res.Resources;
Mady Mellordd497052019-01-30 17:23:48 -080039import android.content.res.TypedArray;
Mady Mellordea7ecf2018-12-10 15:47:40 -080040import android.graphics.Color;
Mady Mellor3dff9e62019-02-05 18:12:53 -080041import android.graphics.Insets;
42import android.graphics.Point;
Mady Mellor47b11e32019-07-11 19:06:21 -070043import android.graphics.Rect;
Mady Mellordea7ecf2018-12-10 15:47:40 -080044import android.graphics.drawable.ShapeDrawable;
Mark Renouf5c732b32019-06-12 15:14:54 -040045import android.os.RemoteException;
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;
Mady Mellordea7ecf2018-12-10 15:47:40 -080049import android.view.View;
Mady Mellor3dff9e62019-02-05 18:12:53 -080050import android.view.WindowInsets;
Mady Mellora96c9ed2019-06-07 12:55:26 -070051import android.view.WindowManager;
Mady Mellordea7ecf2018-12-10 15:47:40 -080052import android.widget.LinearLayout;
53
Mark Renouf34d04f32019-05-13 15:53:18 -040054import com.android.internal.policy.ScreenDecorationsUtils;
Mady Mellor3dff9e62019-02-05 18:12:53 -080055import com.android.systemui.Dependency;
Mady Mellordea7ecf2018-12-10 15:47:40 -080056import com.android.systemui.R;
57import com.android.systemui.recents.TriangleShape;
Muhammad Qureshi9bced7d2020-01-16 15:22:12 -080058import com.android.systemui.shared.system.SysUiStatsLog;
Lyn Han754e77b2019-04-30 14:34:49 -070059import com.android.systemui.statusbar.AlphaOptimizedButton;
Beverlya53fb0d2020-01-29 15:26:13 -050060import com.android.systemui.statusbar.notification.collection.NotificationEntry;
Mady Mellordea7ecf2018-12-10 15:47:40 -080061
62/**
Lyn Han02cca812019-04-02 16:27:32 -070063 * Container for the expanded bubble view, handles rendering the caret and settings icon.
Mady Mellordea7ecf2018-12-10 15:47:40 -080064 */
Mady Mellor3d82e682019-02-05 13:34:48 -080065public class BubbleExpandedView extends LinearLayout implements View.OnClickListener {
Issei Suzukia8d07312019-06-07 12:56:19 +020066 private static final String TAG = TAG_WITH_CLASS_NAME ? "BubbleExpandedView" : TAG_BUBBLES;
Mady Mellordea7ecf2018-12-10 15:47:40 -080067
Issei Suzuki734bc942019-06-05 13:59:52 +020068 private enum ActivityViewStatus {
69 // ActivityView is being initialized, cannot start an activity yet.
70 INITIALIZING,
71 // ActivityView is initialized, and ready to start an activity.
72 INITIALIZED,
73 // Activity runs in the ActivityView.
74 ACTIVITY_STARTED,
75 // ActivityView is released, so activity launching will no longer be permitted.
76 RELEASED,
77 }
Mady Mellordea7ecf2018-12-10 15:47:40 -080078
79 // The triangle pointing to the expanded view
80 private View mPointerView;
Mady Mellor44ee2fe2019-01-30 17:51:16 -080081 private int mPointerMargin;
Mady Mellore8e07712019-01-23 12:45:33 -080082
Lyn Han754e77b2019-04-30 14:34:49 -070083 private AlphaOptimizedButton mSettingsIcon;
Mady Mellore8e07712019-01-23 12:45:33 -080084
Mady Mellor3dff9e62019-02-05 18:12:53 -080085 // Views for expanded state
Mady Mellor3dff9e62019-02-05 18:12:53 -080086 private ActivityView mActivityView;
87
Issei Suzuki734bc942019-06-05 13:59:52 +020088 private ActivityViewStatus mActivityViewStatus = ActivityViewStatus.INITIALIZING;
Mark Renouf5c732b32019-06-12 15:14:54 -040089 private int mTaskId = -1;
90
Lyn Hanb58c7562020-01-07 14:29:20 -080091 private PendingIntent mPendingIntent;
Mady Mellor3dff9e62019-02-05 18:12:53 -080092
Mady Mellor5d8f1402019-02-21 18:23:52 -080093 private boolean mKeyboardVisible;
94 private boolean mNeedsNewHeight;
95
Mady Mellora96c9ed2019-06-07 12:55:26 -070096 private Point mDisplaySize;
Mady Mellorfe7ec032019-01-30 17:32:49 -080097 private int mMinHeight;
Lyn Hancd4f87e2020-02-19 20:33:45 -080098 private int mOverflowHeight;
Lyn Han02cca812019-04-02 16:27:32 -070099 private int mSettingsIconHeight;
Lyn Han02cca812019-04-02 16:27:32 -0700100 private int mPointerWidth;
101 private int mPointerHeight;
Mark Renouf34d04f32019-05-13 15:53:18 -0400102 private ShapeDrawable mPointerDrawable;
Mady Mellor47b11e32019-07-11 19:06:21 -0700103 private Rect mTempRect = new Rect();
104 private int[] mTempLoc = new int[2];
105 private int mExpandedViewTouchSlop;
Mady Mellordea7ecf2018-12-10 15:47:40 -0800106
Lyn Hanb58c7562020-01-07 14:29:20 -0800107 @Nullable private Bubble mBubble;
108
109 private boolean mIsOverflow;
Mady Mellore8e07712019-01-23 12:45:33 -0800110
Mady Mellor3dff9e62019-02-05 18:12:53 -0800111 private BubbleController mBubbleController = Dependency.get(BubbleController.class);
Mady Mellor9be3bed2019-08-21 17:26:26 -0700112 private WindowManager mWindowManager;
Mady Mellor9801e852019-01-22 14:50:28 -0800113
Mady Mellor9801e852019-01-22 14:50:28 -0800114 private BubbleStackView mStackView;
115
Mady Mellor3dff9e62019-02-05 18:12:53 -0800116 private ActivityView.StateCallback mStateCallback = new ActivityView.StateCallback() {
117 @Override
118 public void onActivityViewReady(ActivityView view) {
Mark Renoufdb861c92019-07-26 13:58:17 -0400119 if (DEBUG_BUBBLE_EXPANDED_VIEW) {
120 Log.d(TAG, "onActivityViewReady: mActivityViewStatus=" + mActivityViewStatus
121 + " bubble=" + getBubbleKey());
122 }
Issei Suzuki734bc942019-06-05 13:59:52 +0200123 switch (mActivityViewStatus) {
124 case INITIALIZING:
125 case INITIALIZED:
126 // Custom options so there is no activity transition animation
127 ActivityOptions options = ActivityOptions.makeCustomAnimation(getContext(),
128 0 /* enterResId */, 0 /* exitResId */);
Winson Chung06200522020-03-31 22:47:02 -0700129 options.setTaskAlwaysOnTop(true);
Yuncheol Heo5d62e752020-04-08 14:54:24 -0700130 options.setLaunchWindowingMode(WINDOWING_MODE_MULTI_WINDOW);
Issei Suzuki734bc942019-06-05 13:59:52 +0200131 // Post to keep the lifecycle normal
Mark Renoufdb861c92019-07-26 13:58:17 -0400132 post(() -> {
133 if (DEBUG_BUBBLE_EXPANDED_VIEW) {
134 Log.d(TAG, "onActivityViewReady: calling startActivity, "
135 + "bubble=" + getBubbleKey());
136 }
Mady Mellor8454ddf2019-08-15 11:16:23 -0700137 try {
Lyn Hanb58c7562020-01-07 14:29:20 -0800138 if (!mIsOverflow && mBubble.usingShortcutInfo()) {
Mady Mellorb547e5e2019-12-02 10:15:56 -0800139 mActivityView.startShortcutActivity(mBubble.getShortcutInfo(),
140 options, null /* sourceBounds */);
141 } else {
142 Intent fillInIntent = new Intent();
143 // Apply flags to make behaviour match documentLaunchMode=always.
144 fillInIntent.addFlags(FLAG_ACTIVITY_NEW_DOCUMENT);
145 fillInIntent.addFlags(FLAG_ACTIVITY_MULTIPLE_TASK);
Lyn Hanb58c7562020-01-07 14:29:20 -0800146 mActivityView.startActivity(mPendingIntent, fillInIntent, options);
Mady Mellorb547e5e2019-12-02 10:15:56 -0800147 }
Mady Mellor8454ddf2019-08-15 11:16:23 -0700148 } catch (RuntimeException e) {
149 // If there's a runtime exception here then there's something
150 // wrong with the intent, we can't really recover / try to populate
151 // the bubble again so we'll just remove it.
152 Log.w(TAG, "Exception while displaying bubble: " + getBubbleKey()
153 + ", " + e.getMessage() + "; removing bubble");
Beverlya53fb0d2020-01-29 15:26:13 -0500154 mBubbleController.removeBubble(getBubbleEntry(),
Mady Mellor8454ddf2019-08-15 11:16:23 -0700155 BubbleController.DISMISS_INVALID_INTENT);
156 }
Mark Renoufdb861c92019-07-26 13:58:17 -0400157 });
Issei Suzuki734bc942019-06-05 13:59:52 +0200158 mActivityViewStatus = ActivityViewStatus.ACTIVITY_STARTED;
Mady Mellor6d002032019-02-13 13:45:17 -0800159 }
Mady Mellor3dff9e62019-02-05 18:12:53 -0800160 }
161
162 @Override
163 public void onActivityViewDestroyed(ActivityView view) {
Mark Renoufdb861c92019-07-26 13:58:17 -0400164 if (DEBUG_BUBBLE_EXPANDED_VIEW) {
165 Log.d(TAG, "onActivityViewDestroyed: mActivityViewStatus=" + mActivityViewStatus
166 + " bubble=" + getBubbleKey());
167 }
Issei Suzuki734bc942019-06-05 13:59:52 +0200168 mActivityViewStatus = ActivityViewStatus.RELEASED;
Mady Mellor3dff9e62019-02-05 18:12:53 -0800169 }
170
Mark Renouf5c732b32019-06-12 15:14:54 -0400171 @Override
172 public void onTaskCreated(int taskId, ComponentName componentName) {
Mark Renoufdb861c92019-07-26 13:58:17 -0400173 if (DEBUG_BUBBLE_EXPANDED_VIEW) {
174 Log.d(TAG, "onTaskCreated: taskId=" + taskId
175 + " bubble=" + getBubbleKey());
176 }
Mark Renouf5c732b32019-06-12 15:14:54 -0400177 // Since Bubble ActivityView applies singleTaskDisplay this is
178 // guaranteed to only be called once per ActivityView. The taskId is
179 // saved to use for removeTask, preventing appearance in recent tasks.
180 mTaskId = taskId;
Mady Mellor3dff9e62019-02-05 18:12:53 -0800181 }
182
183 /**
184 * This is only called for tasks on this ActivityView, which is also set to
185 * single-task mode -- meaning never more than one task on this display. If a task
186 * is being removed, it's the top Activity finishing and this bubble should
187 * be removed or collapsed.
188 */
189 @Override
190 public void onTaskRemovalStarted(int taskId) {
Mark Renoufdb861c92019-07-26 13:58:17 -0400191 if (DEBUG_BUBBLE_EXPANDED_VIEW) {
192 Log.d(TAG, "onTaskRemovalStarted: taskId=" + taskId
193 + " mActivityViewStatus=" + mActivityViewStatus
194 + " bubble=" + getBubbleKey());
195 }
Mady Mellorff076eb2019-11-13 10:12:06 -0800196 if (mBubble != null && !mBubbleController.isUserCreatedBubble(mBubble.getKey())) {
197 // Must post because this is called from a binder thread.
Beverlya53fb0d2020-01-29 15:26:13 -0500198 post(() -> mBubbleController.removeBubble(mBubble.getEntry(),
Mady Mellorff076eb2019-11-13 10:12:06 -0800199 BubbleController.DISMISS_TASK_FINISHED));
Mady Mellor3dff9e62019-02-05 18:12:53 -0800200 }
201 }
202 };
Mady Mellore8e07712019-01-23 12:45:33 -0800203
Mady Mellor3d82e682019-02-05 13:34:48 -0800204 public BubbleExpandedView(Context context) {
Mady Mellordea7ecf2018-12-10 15:47:40 -0800205 this(context, null);
206 }
207
Mady Mellor3d82e682019-02-05 13:34:48 -0800208 public BubbleExpandedView(Context context, AttributeSet attrs) {
Mady Mellordea7ecf2018-12-10 15:47:40 -0800209 this(context, attrs, 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 this(context, attrs, defStyleAttr, 0);
214 }
215
Mady Mellor3d82e682019-02-05 13:34:48 -0800216 public BubbleExpandedView(Context context, AttributeSet attrs, int defStyleAttr,
Mady Mellordea7ecf2018-12-10 15:47:40 -0800217 int defStyleRes) {
218 super(context, attrs, defStyleAttr, defStyleRes);
Mady Mellora96c9ed2019-06-07 12:55:26 -0700219 mDisplaySize = new Point();
Mady Mellor9be3bed2019-08-21 17:26:26 -0700220 mWindowManager = (WindowManager) context.getSystemService(Context.WINDOW_SERVICE);
Mady Mellor8fe411d2019-08-16 11:27:53 -0700221 // Get the real size -- this includes screen decorations (notches, statusbar, navbar).
Mady Mellor9be3bed2019-08-21 17:26:26 -0700222 mWindowManager.getDefaultDisplay().getRealSize(mDisplaySize);
Mady Mellor47b11e32019-07-11 19:06:21 -0700223 Resources res = getResources();
224 mMinHeight = res.getDimensionPixelSize(R.dimen.bubble_expanded_default_height);
Lyn Hancd4f87e2020-02-19 20:33:45 -0800225 mOverflowHeight = res.getDimensionPixelSize(R.dimen.bubble_overflow_height);
Mady Mellor47b11e32019-07-11 19:06:21 -0700226 mPointerMargin = res.getDimensionPixelSize(R.dimen.bubble_pointer_margin);
227 mExpandedViewTouchSlop = res.getDimensionPixelSize(R.dimen.bubble_expanded_view_slop);
Mady Mellordea7ecf2018-12-10 15:47:40 -0800228 }
229
230 @Override
231 protected void onFinishInflate() {
232 super.onFinishInflate();
Mark Renoufdb861c92019-07-26 13:58:17 -0400233 if (DEBUG_BUBBLE_EXPANDED_VIEW) {
234 Log.d(TAG, "onFinishInflate: bubble=" + getBubbleKey());
235 }
Mady Mellordea7ecf2018-12-10 15:47:40 -0800236
237 Resources res = getResources();
238 mPointerView = findViewById(R.id.pointer_view);
Lyn Han02cca812019-04-02 16:27:32 -0700239 mPointerWidth = res.getDimensionPixelSize(R.dimen.bubble_pointer_width);
240 mPointerHeight = res.getDimensionPixelSize(R.dimen.bubble_pointer_height);
Mady Mellordd497052019-01-30 17:23:48 -0800241
Mady Mellordd497052019-01-30 17:23:48 -0800242
Mark Renouf34d04f32019-05-13 15:53:18 -0400243 mPointerDrawable = new ShapeDrawable(TriangleShape.create(
Lyn Han5aa27e22019-05-15 10:55:07 -0700244 mPointerWidth, mPointerHeight, true /* pointUp */));
Mark Renouf34d04f32019-05-13 15:53:18 -0400245 mPointerView.setBackground(mPointerDrawable);
Mady Mellora96c9ed2019-06-07 12:55:26 -0700246 mPointerView.setVisibility(INVISIBLE);
Mady Mellor9801e852019-01-22 14:50:28 -0800247
Lyn Han02cca812019-04-02 16:27:32 -0700248 mSettingsIconHeight = getContext().getResources().getDimensionPixelSize(
Mady Mellor5a3e94b2020-02-07 12:16:21 -0800249 R.dimen.bubble_manage_button_height);
Lyn Han02cca812019-04-02 16:27:32 -0700250 mSettingsIcon = findViewById(R.id.settings_button);
Lyn Han02cca812019-04-02 16:27:32 -0700251 mSettingsIcon.setOnClickListener(this);
Lyn Han02cca812019-04-02 16:27:32 -0700252
Mady Mellor3dff9e62019-02-05 18:12:53 -0800253 mActivityView = new ActivityView(mContext, null /* attrs */, 0 /* defStyle */,
254 true /* singleTaskInstance */);
Lyn Hanb58c7562020-01-07 14:29:20 -0800255
lumarkdc9b3192019-07-23 21:18:17 +0800256 // Set ActivityView's alpha value as zero, since there is no view content to be shown.
Issei Suzukicac2a502019-04-16 16:52:50 +0200257 setContentVisibility(false);
Mady Mellor3dff9e62019-02-05 18:12:53 -0800258 addView(mActivityView);
259
Lyn Han5aa27e22019-05-15 10:55:07 -0700260 // Expanded stack layout, top to bottom:
261 // Expanded view container
262 // ==> bubble row
263 // ==> expanded view
264 // ==> activity view
265 // ==> manage button
266 bringChildToFront(mActivityView);
267 bringChildToFront(mSettingsIcon);
Mady Mellor52b1ac62019-04-10 16:59:03 -0700268
Mark Renouf34d04f32019-05-13 15:53:18 -0400269 applyThemeAttrs();
270
Mady Mellor5d8f1402019-02-21 18:23:52 -0800271 setOnApplyWindowInsetsListener((View view, WindowInsets insets) -> {
272 // Keep track of IME displaying because we should not make any adjustments that might
273 // cause a config change while the IME is displayed otherwise it'll loose focus.
Mady Mellor3dff9e62019-02-05 18:12:53 -0800274 final int keyboardHeight = insets.getSystemWindowInsetBottom()
275 - insets.getStableInsetBottom();
Mady Mellor5d8f1402019-02-21 18:23:52 -0800276 mKeyboardVisible = keyboardHeight != 0;
277 if (!mKeyboardVisible && mNeedsNewHeight) {
278 updateHeight();
279 }
Mady Mellor3dff9e62019-02-05 18:12:53 -0800280 return view.onApplyWindowInsets(insets);
281 });
Mady Mellore8e07712019-01-23 12:45:33 -0800282 }
283
Mark Renoufdb861c92019-07-26 13:58:17 -0400284 private String getBubbleKey() {
285 return mBubble != null ? mBubble.getKey() : "null";
286 }
287
Beverlya53fb0d2020-01-29 15:26:13 -0500288 private NotificationEntry getBubbleEntry() {
289 return mBubble != null ? mBubble.getEntry() : null;
290 }
291
Mark Renouf34d04f32019-05-13 15:53:18 -0400292 void applyThemeAttrs() {
Lyn Han61bfdb32019-12-10 14:34:12 -0800293 final TypedArray ta = mContext.obtainStyledAttributes(
294 new int[] {
295 android.R.attr.colorBackgroundFloating,
296 android.R.attr.dialogCornerRadius});
297 int bgColor = ta.getColor(0, Color.WHITE);
298 float cornerRadius = ta.getDimensionPixelSize(1, 0);
Mark Renouf34d04f32019-05-13 15:53:18 -0400299 ta.recycle();
300
Mark Renouf34d04f32019-05-13 15:53:18 -0400301 mPointerDrawable.setTint(bgColor);
Lyn Hanedb55e22020-02-12 18:55:39 -0800302 if (mActivityView != null && ScreenDecorationsUtils.supportsRoundedCornersOnWindows(
303 mContext.getResources())) {
Mark Renouf34d04f32019-05-13 15:53:18 -0400304 mActivityView.setCornerRadius(cornerRadius);
305 }
306 }
307
Mady Mellor5d8f1402019-02-21 18:23:52 -0800308 @Override
309 protected void onDetachedFromWindow() {
310 super.onDetachedFromWindow();
311 mKeyboardVisible = false;
312 mNeedsNewHeight = false;
313 if (mActivityView != null) {
Mady Melloraf02fa42020-03-04 14:22:16 -0800314 // TODO: Temporary hack to offset the view until we can properly inset Bubbles again.
315 if (sNewInsetsMode == NEW_INSETS_MODE_FULL) {
316 mStackView.animate()
317 .setDuration(100)
318 .translationY(0);
319 } else {
320 mActivityView.setForwardedInsets(Insets.of(0, 0, 0, 0));
321 }
Mady Mellor5d8f1402019-02-21 18:23:52 -0800322 }
Mark Renoufdb861c92019-07-26 13:58:17 -0400323 if (DEBUG_BUBBLE_EXPANDED_VIEW) {
324 Log.d(TAG, "onDetachedFromWindow: bubble=" + getBubbleKey());
325 }
Mady Mellor5d8f1402019-02-21 18:23:52 -0800326 }
327
328 /**
Issei Suzukicac2a502019-04-16 16:52:50 +0200329 * Set visibility of contents in the expanded state.
330 *
331 * @param visibility {@code true} if the contents should be visible on the screen.
332 *
333 * Note that this contents visibility doesn't affect visibility at {@link android.view.View},
334 * and setting {@code false} actually means rendering the contents in transparent.
335 */
336 void setContentVisibility(boolean visibility) {
Mark Renoufdb861c92019-07-26 13:58:17 -0400337 if (DEBUG_BUBBLE_EXPANDED_VIEW) {
338 Log.d(TAG, "setContentVisibility: visibility=" + visibility
339 + " bubble=" + getBubbleKey());
340 }
Issei Suzukicac2a502019-04-16 16:52:50 +0200341 final float alpha = visibility ? 1f : 0f;
342 mPointerView.setAlpha(alpha);
343 if (mActivityView != null) {
344 mActivityView.setAlpha(alpha);
345 }
346 }
347
348 /**
Mady Mellor5d8f1402019-02-21 18:23:52 -0800349 * Called by {@link BubbleStackView} when the insets for the expanded state should be updated.
350 * This should be done post-move and post-animation.
351 */
352 void updateInsets(WindowInsets insets) {
353 if (usingActivityView()) {
Mady Mellor8fe411d2019-08-16 11:27:53 -0700354 int[] screenLoc = mActivityView.getLocationOnScreen();
355 final int activityViewBottom = screenLoc[1] + mActivityView.getHeight();
Mady Mellor9be3bed2019-08-21 17:26:26 -0700356 final int keyboardTop = mDisplaySize.y - Math.max(insets.getSystemWindowInsetBottom(),
357 insets.getDisplayCutout() != null
358 ? insets.getDisplayCutout().getSafeInsetBottom()
359 : 0);
Mady Mellor8fe411d2019-08-16 11:27:53 -0700360 final int insetsBottom = Math.max(activityViewBottom - keyboardTop, 0);
Jorim Jaggi924ef752020-01-29 17:26:55 +0100361
362 // TODO: Temporary hack to offset the view until we can properly inset Bubbles again.
363 if (sNewInsetsMode == NEW_INSETS_MODE_FULL) {
Mady Melloraf02fa42020-03-04 14:22:16 -0800364 mStackView.animate()
365 .setDuration(100)
366 .translationY(-insetsBottom)
367 .withEndAction(() -> mActivityView.onLocationChanged());
Jorim Jaggi924ef752020-01-29 17:26:55 +0100368 } else {
369 mActivityView.setForwardedInsets(Insets.of(0, 0, 0, insetsBottom));
370 }
Mady Mellor5d8f1402019-02-21 18:23:52 -0800371 }
372 }
373
Mady Mellor247ca2c2019-12-02 16:18:59 -0800374 void setStackView(BubbleStackView stackView) {
Mady Mellor9801e852019-01-22 14:50:28 -0800375 mStackView = stackView;
Mady Mellor6d002032019-02-13 13:45:17 -0800376 }
377
Lyn Hanb58c7562020-01-07 14:29:20 -0800378 public void setOverflow(boolean overflow) {
379 mIsOverflow = overflow;
380
381 Intent target = new Intent(mContext, BubbleOverflowActivity.class);
382 mPendingIntent = PendingIntent.getActivity(mContext, /* requestCode */ 0,
383 target, PendingIntent.FLAG_UPDATE_CURRENT);
384 mSettingsIcon.setVisibility(GONE);
385 }
386
Mady Mellor6d002032019-02-13 13:45:17 -0800387 /**
Mady Mellor247ca2c2019-12-02 16:18:59 -0800388 * Sets the bubble used to populate this view.
Mady Mellor6d002032019-02-13 13:45:17 -0800389 */
Mady Mellor247ca2c2019-12-02 16:18:59 -0800390 void update(Bubble bubble) {
391 if (DEBUG_BUBBLE_EXPANDED_VIEW) {
392 Log.d(TAG, "update: bubble=" + (bubble != null ? bubble.getKey() : "null"));
393 }
394 boolean isNew = mBubble == null;
Lyn Hanb58c7562020-01-07 14:29:20 -0800395 if (isNew || bubble != null && bubble.getKey().equals(mBubble.getKey())) {
Mady Mellor247ca2c2019-12-02 16:18:59 -0800396 mBubble = bubble;
397 mSettingsIcon.setContentDescription(getResources().getString(
398 R.string.bubbles_settings_button_description, bubble.getAppName()));
399
400 if (isNew) {
Lyn Hanb58c7562020-01-07 14:29:20 -0800401 mPendingIntent = mBubble.getBubbleIntent();
402 if (mPendingIntent != null || mBubble.getShortcutInfo() != null) {
Mady Mellor247ca2c2019-12-02 16:18:59 -0800403 setContentVisibility(false);
404 mActivityView.setVisibility(VISIBLE);
405 }
406 }
407 applyThemeAttrs();
408 } else {
409 Log.w(TAG, "Trying to update entry with different key, new bubble: "
410 + bubble.getKey() + " old bubble: " + bubble.getKey());
411 }
412 }
413
414 /**
415 * Lets activity view know it should be shown / populated with activity content.
416 */
417 void populateExpandedView() {
Mark Renoufdb861c92019-07-26 13:58:17 -0400418 if (DEBUG_BUBBLE_EXPANDED_VIEW) {
419 Log.d(TAG, "populateExpandedView: "
420 + "bubble=" + getBubbleKey());
421 }
422
Mady Mellor5029fa62019-03-05 12:16:21 -0800423 if (usingActivityView()) {
424 mActivityView.setCallback(mStateCallback);
425 } else {
Issei Suzukia91f3962019-06-07 11:48:23 +0200426 Log.e(TAG, "Cannot populate expanded view.");
Mady Mellor5029fa62019-03-05 12:16:21 -0800427 }
Mady Mellor9801e852019-01-22 14:50:28 -0800428 }
429
Mark Renouf041d7262019-02-06 12:09:41 -0500430 boolean performBackPressIfNeeded() {
Mady Mellor323fb0b2019-03-25 12:15:22 -0700431 if (!usingActivityView()) {
Mark Renouf041d7262019-02-06 12:09:41 -0500432 return false;
433 }
434 mActivityView.performBackPress();
435 return true;
436 }
437
Mady Mellorfe7ec032019-01-30 17:32:49 -0800438 void updateHeight() {
Mark Renoufdb861c92019-07-26 13:58:17 -0400439 if (DEBUG_BUBBLE_EXPANDED_VIEW) {
440 Log.d(TAG, "updateHeight: bubble=" + getBubbleKey());
441 }
Mady Mellorfe7ec032019-01-30 17:32:49 -0800442 if (usingActivityView()) {
Lyn Hancd4f87e2020-02-19 20:33:45 -0800443 float desiredHeight = mOverflowHeight;
Lyn Hanb58c7562020-01-07 14:29:20 -0800444 if (!mIsOverflow) {
445 desiredHeight = Math.max(mBubble.getDesiredHeight(mContext), mMinHeight);
446 }
Mady Mellor8fe411d2019-08-16 11:27:53 -0700447 float height = Math.min(desiredHeight, getMaxExpandedHeight());
Lyn Hancd4f87e2020-02-19 20:33:45 -0800448 height = Math.max(height, mIsOverflow? mOverflowHeight : mMinHeight);
Mady Mellorfe7ec032019-01-30 17:32:49 -0800449 LayoutParams lp = (LayoutParams) mActivityView.getLayoutParams();
Lyn Hancd4f87e2020-02-19 20:33:45 -0800450 mNeedsNewHeight = lp.height != height;
Mady Mellor5d8f1402019-02-21 18:23:52 -0800451 if (!mKeyboardVisible) {
452 // If the keyboard is visible... don't adjust the height because that will cause
453 // a configuration change and the keyboard will be lost.
Mady Mellor7af771a2019-03-07 15:04:54 -0800454 lp.height = (int) height;
Mady Mellor5d8f1402019-02-21 18:23:52 -0800455 mActivityView.setLayoutParams(lp);
456 mNeedsNewHeight = false;
457 }
Mark Renoufdb861c92019-07-26 13:58:17 -0400458 if (DEBUG_BUBBLE_EXPANDED_VIEW) {
459 Log.d(TAG, "updateHeight: bubble=" + getBubbleKey() + " height=" + height
460 + " mNeedsNewHeight=" + mNeedsNewHeight);
461 }
Mady Mellorfe7ec032019-01-30 17:32:49 -0800462 }
463 }
464
Mady Mellora96c9ed2019-06-07 12:55:26 -0700465 private int getMaxExpandedHeight() {
Mady Mellor9be3bed2019-08-21 17:26:26 -0700466 mWindowManager.getDefaultDisplay().getRealSize(mDisplaySize);
Mady Mellora96c9ed2019-06-07 12:55:26 -0700467 int[] windowLocation = mActivityView.getLocationOnScreen();
Mady Mellor8fe411d2019-08-16 11:27:53 -0700468 int bottomInset = getRootWindowInsets() != null
469 ? getRootWindowInsets().getStableInsetBottom()
470 : 0;
Lyn Han0b955ed2020-01-31 09:47:35 -0800471 return mDisplaySize.y - windowLocation[1] - mSettingsIconHeight - mPointerHeight
Mady Mellor8fe411d2019-08-16 11:27:53 -0700472 - mPointerMargin - bottomInset;
Mady Mellora96c9ed2019-06-07 12:55:26 -0700473 }
474
Mady Mellor47b11e32019-07-11 19:06:21 -0700475 /**
476 * Whether the provided x, y values (in raw coordinates) are in a touchable area of the
477 * expanded view.
478 *
479 * The touchable areas are the ActivityView (plus some slop around it) and the manage button.
480 */
481 boolean intersectingTouchableContent(int rawX, int rawY) {
482 mTempRect.setEmpty();
483 if (mActivityView != null) {
484 mTempLoc = mActivityView.getLocationOnScreen();
485 mTempRect.set(mTempLoc[0] - mExpandedViewTouchSlop,
486 mTempLoc[1] - mExpandedViewTouchSlop,
487 mTempLoc[0] + mActivityView.getWidth() + mExpandedViewTouchSlop,
488 mTempLoc[1] + mActivityView.getHeight() + mExpandedViewTouchSlop);
489 }
490 if (mTempRect.contains(rawX, rawY)) {
491 return true;
492 }
493 mTempLoc = mSettingsIcon.getLocationOnScreen();
494 mTempRect.set(mTempLoc[0],
495 mTempLoc[1],
496 mTempLoc[0] + mSettingsIcon.getWidth(),
497 mTempLoc[1] + mSettingsIcon.getHeight());
498 if (mTempRect.contains(rawX, rawY)) {
499 return true;
500 }
501 return false;
502 }
503
Mady Mellor9801e852019-01-22 14:50:28 -0800504 @Override
505 public void onClick(View view) {
Mady Mellor99a302602019-06-14 11:39:56 -0700506 if (mBubble == null) {
Mady Mellor9801e852019-01-22 14:50:28 -0800507 return;
508 }
Mady Mellor9801e852019-01-22 14:50:28 -0800509 int id = view.getId();
Lyn Hanc26ff122019-03-29 16:46:07 -0700510 if (id == R.id.settings_button) {
Mady Mellor99a302602019-06-14 11:39:56 -0700511 Intent intent = mBubble.getSettingsIntent();
Steven Wub00225b2019-02-08 14:27:42 -0500512 mStackView.collapseStack(() -> {
Ned Burns00b4b2d2019-10-17 22:09:27 -0400513 mContext.startActivityAsUser(intent, mBubble.getEntry().getSbn().getUser());
Mady Mellor99a302602019-06-14 11:39:56 -0700514 logBubbleClickEvent(mBubble,
Muhammad Qureshi9bced7d2020-01-16 15:22:12 -0800515 SysUiStatsLog.BUBBLE_UICHANGED__ACTION__HEADER_GO_TO_SETTINGS);
Steven Wub00225b2019-02-08 14:27:42 -0500516 });
Mady Mellor9801e852019-01-22 14:50:28 -0800517 }
Mady Mellordea7ecf2018-12-10 15:47:40 -0800518 }
519
520 /**
Mady Mellor3dff9e62019-02-05 18:12:53 -0800521 * Update appearance of the expanded view being displayed.
522 */
523 public void updateView() {
Mark Renoufdb861c92019-07-26 13:58:17 -0400524 if (DEBUG_BUBBLE_EXPANDED_VIEW) {
525 Log.d(TAG, "updateView: bubble="
526 + getBubbleKey());
527 }
Mady Mellor3dff9e62019-02-05 18:12:53 -0800528 if (usingActivityView()
529 && mActivityView.getVisibility() == VISIBLE
530 && mActivityView.isAttachedToWindow()) {
531 mActivityView.onLocationChanged();
Mady Mellor3dff9e62019-02-05 18:12:53 -0800532 }
Mady Mellorfe7ec032019-01-30 17:32:49 -0800533 updateHeight();
Mady Mellor3dff9e62019-02-05 18:12:53 -0800534 }
535
536 /**
Mady Mellordea7ecf2018-12-10 15:47:40 -0800537 * Set the x position that the tip of the triangle should point to.
538 */
Mady Mellor3dff9e62019-02-05 18:12:53 -0800539 public void setPointerPosition(float x) {
Lyn Han9a2f5cf2019-05-23 11:01:41 -0700540 float halfPointerWidth = mPointerWidth / 2f;
541 float pointerLeft = x - halfPointerWidth;
542 mPointerView.setTranslationX(pointerLeft);
Lyn Hanf74ba672019-05-20 16:08:48 -0700543 mPointerView.setVisibility(VISIBLE);
Mady Mellordea7ecf2018-12-10 15:47:40 -0800544 }
545
546 /**
Mady Mellor5a3e94b2020-02-07 12:16:21 -0800547 * Position of the manage button displayed in the expanded view. Used for placing user
548 * education about the manage button.
549 */
550 public Rect getManageButtonLocationOnScreen() {
551 mTempLoc = mSettingsIcon.getLocationOnScreen();
552 return new Rect(mTempLoc[0], mTempLoc[1], mTempLoc[0] + mSettingsIcon.getWidth(),
553 mTempLoc[1] + mSettingsIcon.getHeight());
554 }
555
556 /**
Mady Mellor3dff9e62019-02-05 18:12:53 -0800557 * Removes and releases an ActivityView if one was previously created for this bubble.
Mady Mellordea7ecf2018-12-10 15:47:40 -0800558 */
Mady Mellor94d94a72019-03-05 18:16:59 -0800559 public void cleanUpExpandedState() {
Mark Renoufdb861c92019-07-26 13:58:17 -0400560 if (DEBUG_BUBBLE_EXPANDED_VIEW) {
561 Log.d(TAG, "cleanUpExpandedState: mActivityViewStatus=" + mActivityViewStatus
562 + ", bubble=" + getBubbleKey());
563 }
Mady Mellor3dff9e62019-02-05 18:12:53 -0800564 if (mActivityView == null) {
Mark Renouf89b1a4a2018-12-04 14:59:45 -0500565 return;
566 }
Issei Suzuki734bc942019-06-05 13:59:52 +0200567 switch (mActivityViewStatus) {
568 case INITIALIZED:
569 case ACTIVITY_STARTED:
570 mActivityView.release();
Mady Mellordea7ecf2018-12-10 15:47:40 -0800571 }
Mark Renouf5c732b32019-06-12 15:14:54 -0400572 if (mTaskId != -1) {
573 try {
574 ActivityTaskManager.getService().removeTask(mTaskId);
575 } catch (RemoteException e) {
576 Log.w(TAG, "Failed to remove taskId " + mTaskId);
577 }
578 mTaskId = -1;
Mady Mellordea7ecf2018-12-10 15:47:40 -0800579 }
Mark Renouf28c250d2019-02-25 16:47:34 -0500580 removeView(mActivityView);
Mark Renouf5c732b32019-06-12 15:14:54 -0400581
Mady Mellor3dff9e62019-02-05 18:12:53 -0800582 mActivityView = null;
Issei Suzuki734bc942019-06-05 13:59:52 +0200583 }
584
585 /**
586 * Called when the last task is removed from a {@link android.hardware.display.VirtualDisplay}
587 * which {@link ActivityView} uses.
588 */
589 void notifyDisplayEmpty() {
Mark Renoufdb861c92019-07-26 13:58:17 -0400590 if (DEBUG_BUBBLE_EXPANDED_VIEW) {
591 Log.d(TAG, "notifyDisplayEmpty: bubble="
592 + getBubbleKey()
593 + " mActivityViewStatus=" + mActivityViewStatus);
594 }
Issei Suzuki734bc942019-06-05 13:59:52 +0200595 if (mActivityViewStatus == ActivityViewStatus.ACTIVITY_STARTED) {
596 mActivityViewStatus = ActivityViewStatus.INITIALIZED;
597 }
Mady Mellordea7ecf2018-12-10 15:47:40 -0800598 }
599
Mady Mellor3dff9e62019-02-05 18:12:53 -0800600 private boolean usingActivityView() {
Lyn Hanb58c7562020-01-07 14:29:20 -0800601 return (mPendingIntent != null || mBubble.getShortcutInfo() != null)
Mady Mellor2ac2d3a2020-01-08 17:18:54 -0800602 && mActivityView != null;
Mady Mellor3dff9e62019-02-05 18:12:53 -0800603 }
604
Mady Mellor390bff42019-04-05 15:09:01 -0700605 /**
606 * @return the display id of the virtual display.
607 */
608 public int getVirtualDisplayId() {
609 if (usingActivityView()) {
610 return mActivityView.getVirtualDisplayId();
611 }
612 return INVALID_DISPLAY;
613 }
614
Steven Wub00225b2019-02-08 14:27:42 -0500615 /**
616 * Logs bubble UI click event.
617 *
Mady Mellor99a302602019-06-14 11:39:56 -0700618 * @param bubble the bubble notification entry that user is interacting with.
Steven Wub00225b2019-02-08 14:27:42 -0500619 * @param action the user interaction enum.
620 */
Mady Mellor99a302602019-06-14 11:39:56 -0700621 private void logBubbleClickEvent(Bubble bubble, int action) {
Ned Burns00b4b2d2019-10-17 22:09:27 -0400622 StatusBarNotification notification = bubble.getEntry().getSbn();
Muhammad Qureshi9bced7d2020-01-16 15:22:12 -0800623 SysUiStatsLog.write(SysUiStatsLog.BUBBLE_UI_CHANGED,
Steven Wub00225b2019-02-08 14:27:42 -0500624 notification.getPackageName(),
625 notification.getNotification().getChannelId(),
626 notification.getId(),
627 mStackView.getBubbleIndex(mStackView.getExpandedBubble()),
628 mStackView.getBubbleCount(),
629 action,
630 mStackView.getNormalizedXPosition(),
Steven Wu45e38ae2019-03-25 16:16:59 -0400631 mStackView.getNormalizedYPosition(),
Mady Mellorb8aaf972019-11-26 10:28:00 -0800632 bubble.showInShade(),
Mady Mellor99a302602019-06-14 11:39:56 -0700633 bubble.isOngoing(),
Mady Mellor6cec3502019-06-17 19:20:49 -0700634 false /* isAppForeground (unused) */);
Mady Mellor7af771a2019-03-07 15:04:54 -0800635 }
Mady Mellordea7ecf2018-12-10 15:47:40 -0800636}