blob: 48ce4e9b00976795e684d58eb13fecae64e4c589 [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;
Mady Mellor3dff9e62019-02-05 18:12:53 -080022
Mark Renoufdb861c92019-07-26 13:58:17 -040023import static com.android.systemui.bubbles.BubbleDebugConfig.DEBUG_BUBBLE_EXPANDED_VIEW;
Issei Suzukia8d07312019-06-07 12:56:19 +020024import static com.android.systemui.bubbles.BubbleDebugConfig.TAG_BUBBLES;
25import static com.android.systemui.bubbles.BubbleDebugConfig.TAG_WITH_CLASS_NAME;
Mady Mellorca0c24c2019-05-16 16:14:32 -070026
Mady Mellor60101c92019-04-11 19:04:00 -070027import android.app.ActivityOptions;
Mark Renouf5c732b32019-06-12 15:14:54 -040028import android.app.ActivityTaskManager;
Mady Mellor3dff9e62019-02-05 18:12:53 -080029import android.app.ActivityView;
Mady Mellor9801e852019-01-22 14:50:28 -080030import android.app.PendingIntent;
Mark Renouf5c732b32019-06-12 15:14:54 -040031import android.content.ComponentName;
Mady Mellordea7ecf2018-12-10 15:47:40 -080032import android.content.Context;
Mady Mellor9801e852019-01-22 14:50:28 -080033import android.content.Intent;
Mady Mellordea7ecf2018-12-10 15:47:40 -080034import android.content.res.Resources;
Mady Mellordd497052019-01-30 17:23:48 -080035import android.content.res.TypedArray;
Mady Mellordea7ecf2018-12-10 15:47:40 -080036import android.graphics.Color;
Mady Mellor3dff9e62019-02-05 18:12:53 -080037import android.graphics.Insets;
38import android.graphics.Point;
Mady Mellor47b11e32019-07-11 19:06:21 -070039import android.graphics.Rect;
Mady Mellordea7ecf2018-12-10 15:47:40 -080040import android.graphics.drawable.ShapeDrawable;
Mark Renouf5c732b32019-06-12 15:14:54 -040041import android.os.RemoteException;
Steven Wub00225b2019-02-08 14:27:42 -050042import android.service.notification.StatusBarNotification;
Mady Mellordea7ecf2018-12-10 15:47:40 -080043import android.util.AttributeSet;
Mady Mellor9801e852019-01-22 14:50:28 -080044import android.util.Log;
Mady Mellordea7ecf2018-12-10 15:47:40 -080045import android.view.View;
Mady Mellor3dff9e62019-02-05 18:12:53 -080046import android.view.WindowInsets;
Mady Mellora96c9ed2019-06-07 12:55:26 -070047import android.view.WindowManager;
Mady Mellordea7ecf2018-12-10 15:47:40 -080048import android.widget.LinearLayout;
49
Mark Renouf34d04f32019-05-13 15:53:18 -040050import com.android.internal.policy.ScreenDecorationsUtils;
Mady Mellor3dff9e62019-02-05 18:12:53 -080051import com.android.systemui.Dependency;
Mady Mellordea7ecf2018-12-10 15:47:40 -080052import com.android.systemui.R;
53import com.android.systemui.recents.TriangleShape;
Muhammad Qureshi9bced7d2020-01-16 15:22:12 -080054import com.android.systemui.shared.system.SysUiStatsLog;
Lyn Han754e77b2019-04-30 14:34:49 -070055import com.android.systemui.statusbar.AlphaOptimizedButton;
Mady Mellordea7ecf2018-12-10 15:47:40 -080056
57/**
Lyn Han02cca812019-04-02 16:27:32 -070058 * Container for the expanded bubble view, handles rendering the caret and settings icon.
Mady Mellordea7ecf2018-12-10 15:47:40 -080059 */
Mady Mellor3d82e682019-02-05 13:34:48 -080060public class BubbleExpandedView extends LinearLayout implements View.OnClickListener {
Issei Suzukia8d07312019-06-07 12:56:19 +020061 private static final String TAG = TAG_WITH_CLASS_NAME ? "BubbleExpandedView" : TAG_BUBBLES;
Mady Mellordea7ecf2018-12-10 15:47:40 -080062
Issei Suzuki734bc942019-06-05 13:59:52 +020063 private enum ActivityViewStatus {
64 // ActivityView is being initialized, cannot start an activity yet.
65 INITIALIZING,
66 // ActivityView is initialized, and ready to start an activity.
67 INITIALIZED,
68 // Activity runs in the ActivityView.
69 ACTIVITY_STARTED,
70 // ActivityView is released, so activity launching will no longer be permitted.
71 RELEASED,
72 }
Mady Mellordea7ecf2018-12-10 15:47:40 -080073
74 // The triangle pointing to the expanded view
75 private View mPointerView;
Mady Mellor44ee2fe2019-01-30 17:51:16 -080076 private int mPointerMargin;
Mady Mellore8e07712019-01-23 12:45:33 -080077
Lyn Han754e77b2019-04-30 14:34:49 -070078 private AlphaOptimizedButton mSettingsIcon;
Mady Mellore8e07712019-01-23 12:45:33 -080079
Mady Mellor3dff9e62019-02-05 18:12:53 -080080 // Views for expanded state
Mady Mellor3dff9e62019-02-05 18:12:53 -080081 private ActivityView mActivityView;
82
Issei Suzuki734bc942019-06-05 13:59:52 +020083 private ActivityViewStatus mActivityViewStatus = ActivityViewStatus.INITIALIZING;
Mark Renouf5c732b32019-06-12 15:14:54 -040084 private int mTaskId = -1;
85
Mady Mellor3dff9e62019-02-05 18:12:53 -080086 private PendingIntent mBubbleIntent;
87
Mady Mellor5d8f1402019-02-21 18:23:52 -080088 private boolean mKeyboardVisible;
89 private boolean mNeedsNewHeight;
90
Mady Mellora96c9ed2019-06-07 12:55:26 -070091 private Point mDisplaySize;
Mady Mellorfe7ec032019-01-30 17:32:49 -080092 private int mMinHeight;
Lyn Han02cca812019-04-02 16:27:32 -070093 private int mSettingsIconHeight;
Lyn Han02cca812019-04-02 16:27:32 -070094 private int mPointerWidth;
95 private int mPointerHeight;
Mark Renouf34d04f32019-05-13 15:53:18 -040096 private ShapeDrawable mPointerDrawable;
Mady Mellor47b11e32019-07-11 19:06:21 -070097 private Rect mTempRect = new Rect();
98 private int[] mTempLoc = new int[2];
99 private int mExpandedViewTouchSlop;
Mady Mellordea7ecf2018-12-10 15:47:40 -0800100
Mady Mellor99a302602019-06-14 11:39:56 -0700101 private Bubble mBubble;
Mady Mellore8e07712019-01-23 12:45:33 -0800102
Mady Mellor3dff9e62019-02-05 18:12:53 -0800103 private BubbleController mBubbleController = Dependency.get(BubbleController.class);
Mady Mellor9be3bed2019-08-21 17:26:26 -0700104 private WindowManager mWindowManager;
Mady Mellor9801e852019-01-22 14:50:28 -0800105
Mady Mellor9801e852019-01-22 14:50:28 -0800106 private BubbleStackView mStackView;
107
Mady Mellor3dff9e62019-02-05 18:12:53 -0800108 private ActivityView.StateCallback mStateCallback = new ActivityView.StateCallback() {
109 @Override
110 public void onActivityViewReady(ActivityView view) {
Mark Renoufdb861c92019-07-26 13:58:17 -0400111 if (DEBUG_BUBBLE_EXPANDED_VIEW) {
112 Log.d(TAG, "onActivityViewReady: mActivityViewStatus=" + mActivityViewStatus
113 + " bubble=" + getBubbleKey());
114 }
Issei Suzuki734bc942019-06-05 13:59:52 +0200115 switch (mActivityViewStatus) {
116 case INITIALIZING:
117 case INITIALIZED:
118 // Custom options so there is no activity transition animation
119 ActivityOptions options = ActivityOptions.makeCustomAnimation(getContext(),
120 0 /* enterResId */, 0 /* exitResId */);
121 // Post to keep the lifecycle normal
Mark Renoufdb861c92019-07-26 13:58:17 -0400122 post(() -> {
123 if (DEBUG_BUBBLE_EXPANDED_VIEW) {
124 Log.d(TAG, "onActivityViewReady: calling startActivity, "
125 + "bubble=" + getBubbleKey());
126 }
Mady Mellor8454ddf2019-08-15 11:16:23 -0700127 try {
Mady Mellorb547e5e2019-12-02 10:15:56 -0800128 if (mBubble.usingShortcutInfo()) {
129 mActivityView.startShortcutActivity(mBubble.getShortcutInfo(),
130 options, null /* sourceBounds */);
131 } else {
132 Intent fillInIntent = new Intent();
133 // Apply flags to make behaviour match documentLaunchMode=always.
134 fillInIntent.addFlags(FLAG_ACTIVITY_NEW_DOCUMENT);
135 fillInIntent.addFlags(FLAG_ACTIVITY_MULTIPLE_TASK);
136 mActivityView.startActivity(mBubbleIntent, fillInIntent, options);
137 }
Mady Mellor8454ddf2019-08-15 11:16:23 -0700138 } catch (RuntimeException e) {
139 // If there's a runtime exception here then there's something
140 // wrong with the intent, we can't really recover / try to populate
141 // the bubble again so we'll just remove it.
142 Log.w(TAG, "Exception while displaying bubble: " + getBubbleKey()
143 + ", " + e.getMessage() + "; removing bubble");
144 mBubbleController.removeBubble(mBubble.getKey(),
145 BubbleController.DISMISS_INVALID_INTENT);
146 }
Mark Renoufdb861c92019-07-26 13:58:17 -0400147 });
Issei Suzuki734bc942019-06-05 13:59:52 +0200148 mActivityViewStatus = ActivityViewStatus.ACTIVITY_STARTED;
Mady Mellor6d002032019-02-13 13:45:17 -0800149 }
Mady Mellor3dff9e62019-02-05 18:12:53 -0800150 }
151
152 @Override
153 public void onActivityViewDestroyed(ActivityView view) {
Mark Renoufdb861c92019-07-26 13:58:17 -0400154 if (DEBUG_BUBBLE_EXPANDED_VIEW) {
155 Log.d(TAG, "onActivityViewDestroyed: mActivityViewStatus=" + mActivityViewStatus
156 + " bubble=" + getBubbleKey());
157 }
Issei Suzuki734bc942019-06-05 13:59:52 +0200158 mActivityViewStatus = ActivityViewStatus.RELEASED;
Mady Mellor3dff9e62019-02-05 18:12:53 -0800159 }
160
Mark Renouf5c732b32019-06-12 15:14:54 -0400161 @Override
162 public void onTaskCreated(int taskId, ComponentName componentName) {
Mark Renoufdb861c92019-07-26 13:58:17 -0400163 if (DEBUG_BUBBLE_EXPANDED_VIEW) {
164 Log.d(TAG, "onTaskCreated: taskId=" + taskId
165 + " bubble=" + getBubbleKey());
166 }
Mark Renouf5c732b32019-06-12 15:14:54 -0400167 // Since Bubble ActivityView applies singleTaskDisplay this is
168 // guaranteed to only be called once per ActivityView. The taskId is
169 // saved to use for removeTask, preventing appearance in recent tasks.
170 mTaskId = taskId;
Mady Mellor3dff9e62019-02-05 18:12:53 -0800171 }
172
173 /**
174 * This is only called for tasks on this ActivityView, which is also set to
175 * single-task mode -- meaning never more than one task on this display. If a task
176 * is being removed, it's the top Activity finishing and this bubble should
177 * be removed or collapsed.
178 */
179 @Override
180 public void onTaskRemovalStarted(int taskId) {
Mark Renoufdb861c92019-07-26 13:58:17 -0400181 if (DEBUG_BUBBLE_EXPANDED_VIEW) {
182 Log.d(TAG, "onTaskRemovalStarted: taskId=" + taskId
183 + " mActivityViewStatus=" + mActivityViewStatus
184 + " bubble=" + getBubbleKey());
185 }
Mady Mellorff076eb2019-11-13 10:12:06 -0800186 if (mBubble != null && !mBubbleController.isUserCreatedBubble(mBubble.getKey())) {
187 // Must post because this is called from a binder thread.
188 post(() -> mBubbleController.removeBubble(mBubble.getKey(),
189 BubbleController.DISMISS_TASK_FINISHED));
Mady Mellor3dff9e62019-02-05 18:12:53 -0800190 }
191 }
192 };
Mady Mellore8e07712019-01-23 12:45:33 -0800193
Mady Mellor3d82e682019-02-05 13:34:48 -0800194 public BubbleExpandedView(Context context) {
Mady Mellordea7ecf2018-12-10 15:47:40 -0800195 this(context, null);
196 }
197
Mady Mellor3d82e682019-02-05 13:34:48 -0800198 public BubbleExpandedView(Context context, AttributeSet attrs) {
Mady Mellordea7ecf2018-12-10 15:47:40 -0800199 this(context, attrs, 0);
200 }
201
Mady Mellor3d82e682019-02-05 13:34:48 -0800202 public BubbleExpandedView(Context context, AttributeSet attrs, int defStyleAttr) {
Mady Mellordea7ecf2018-12-10 15:47:40 -0800203 this(context, attrs, defStyleAttr, 0);
204 }
205
Mady Mellor3d82e682019-02-05 13:34:48 -0800206 public BubbleExpandedView(Context context, AttributeSet attrs, int defStyleAttr,
Mady Mellordea7ecf2018-12-10 15:47:40 -0800207 int defStyleRes) {
208 super(context, attrs, defStyleAttr, defStyleRes);
Mady Mellora96c9ed2019-06-07 12:55:26 -0700209 mDisplaySize = new Point();
Mady Mellor9be3bed2019-08-21 17:26:26 -0700210 mWindowManager = (WindowManager) context.getSystemService(Context.WINDOW_SERVICE);
Mady Mellor8fe411d2019-08-16 11:27:53 -0700211 // Get the real size -- this includes screen decorations (notches, statusbar, navbar).
Mady Mellor9be3bed2019-08-21 17:26:26 -0700212 mWindowManager.getDefaultDisplay().getRealSize(mDisplaySize);
Mady Mellor47b11e32019-07-11 19:06:21 -0700213 Resources res = getResources();
214 mMinHeight = res.getDimensionPixelSize(R.dimen.bubble_expanded_default_height);
215 mPointerMargin = res.getDimensionPixelSize(R.dimen.bubble_pointer_margin);
216 mExpandedViewTouchSlop = res.getDimensionPixelSize(R.dimen.bubble_expanded_view_slop);
Mady Mellordea7ecf2018-12-10 15:47:40 -0800217 }
218
219 @Override
220 protected void onFinishInflate() {
221 super.onFinishInflate();
Mark Renoufdb861c92019-07-26 13:58:17 -0400222 if (DEBUG_BUBBLE_EXPANDED_VIEW) {
223 Log.d(TAG, "onFinishInflate: bubble=" + getBubbleKey());
224 }
Mady Mellordea7ecf2018-12-10 15:47:40 -0800225
226 Resources res = getResources();
227 mPointerView = findViewById(R.id.pointer_view);
Lyn Han02cca812019-04-02 16:27:32 -0700228 mPointerWidth = res.getDimensionPixelSize(R.dimen.bubble_pointer_width);
229 mPointerHeight = res.getDimensionPixelSize(R.dimen.bubble_pointer_height);
Mady Mellordd497052019-01-30 17:23:48 -0800230
Mady Mellordd497052019-01-30 17:23:48 -0800231
Mark Renouf34d04f32019-05-13 15:53:18 -0400232 mPointerDrawable = new ShapeDrawable(TriangleShape.create(
Lyn Han5aa27e22019-05-15 10:55:07 -0700233 mPointerWidth, mPointerHeight, true /* pointUp */));
Mark Renouf34d04f32019-05-13 15:53:18 -0400234 mPointerView.setBackground(mPointerDrawable);
Mady Mellora96c9ed2019-06-07 12:55:26 -0700235 mPointerView.setVisibility(INVISIBLE);
Mady Mellor9801e852019-01-22 14:50:28 -0800236
Lyn Han02cca812019-04-02 16:27:32 -0700237 mSettingsIconHeight = getContext().getResources().getDimensionPixelSize(
Mady Mellor818eef02019-08-16 16:12:29 -0700238 R.dimen.bubble_settings_size);
Lyn Han02cca812019-04-02 16:27:32 -0700239 mSettingsIcon = findViewById(R.id.settings_button);
Lyn Han02cca812019-04-02 16:27:32 -0700240 mSettingsIcon.setOnClickListener(this);
Lyn Han02cca812019-04-02 16:27:32 -0700241
Mady Mellor3dff9e62019-02-05 18:12:53 -0800242 mActivityView = new ActivityView(mContext, null /* attrs */, 0 /* defStyle */,
243 true /* singleTaskInstance */);
lumarkdc9b3192019-07-23 21:18:17 +0800244 // Set ActivityView's alpha value as zero, since there is no view content to be shown.
Issei Suzukicac2a502019-04-16 16:52:50 +0200245 setContentVisibility(false);
Mady Mellor3dff9e62019-02-05 18:12:53 -0800246 addView(mActivityView);
247
Lyn Han5aa27e22019-05-15 10:55:07 -0700248 // Expanded stack layout, top to bottom:
249 // Expanded view container
250 // ==> bubble row
251 // ==> expanded view
252 // ==> activity view
253 // ==> manage button
254 bringChildToFront(mActivityView);
255 bringChildToFront(mSettingsIcon);
Mady Mellor52b1ac62019-04-10 16:59:03 -0700256
Mark Renouf34d04f32019-05-13 15:53:18 -0400257 applyThemeAttrs();
258
Mady Mellor5d8f1402019-02-21 18:23:52 -0800259 setOnApplyWindowInsetsListener((View view, WindowInsets insets) -> {
260 // Keep track of IME displaying because we should not make any adjustments that might
261 // cause a config change while the IME is displayed otherwise it'll loose focus.
Mady Mellor3dff9e62019-02-05 18:12:53 -0800262 final int keyboardHeight = insets.getSystemWindowInsetBottom()
263 - insets.getStableInsetBottom();
Mady Mellor5d8f1402019-02-21 18:23:52 -0800264 mKeyboardVisible = keyboardHeight != 0;
265 if (!mKeyboardVisible && mNeedsNewHeight) {
266 updateHeight();
267 }
Mady Mellor3dff9e62019-02-05 18:12:53 -0800268 return view.onApplyWindowInsets(insets);
269 });
Mady Mellore8e07712019-01-23 12:45:33 -0800270 }
271
Mark Renoufdb861c92019-07-26 13:58:17 -0400272 private String getBubbleKey() {
273 return mBubble != null ? mBubble.getKey() : "null";
274 }
275
Mark Renouf34d04f32019-05-13 15:53:18 -0400276 void applyThemeAttrs() {
Lyn Han61bfdb32019-12-10 14:34:12 -0800277 final TypedArray ta = mContext.obtainStyledAttributes(
278 new int[] {
279 android.R.attr.colorBackgroundFloating,
280 android.R.attr.dialogCornerRadius});
281 int bgColor = ta.getColor(0, Color.WHITE);
282 float cornerRadius = ta.getDimensionPixelSize(1, 0);
Mark Renouf34d04f32019-05-13 15:53:18 -0400283 ta.recycle();
284
Mark Renouf34d04f32019-05-13 15:53:18 -0400285 mPointerDrawable.setTint(bgColor);
Mark Renouf34d04f32019-05-13 15:53:18 -0400286 if (ScreenDecorationsUtils.supportsRoundedCornersOnWindows(mContext.getResources())) {
287 mActivityView.setCornerRadius(cornerRadius);
288 }
289 }
290
Mady Mellor5d8f1402019-02-21 18:23:52 -0800291 @Override
292 protected void onDetachedFromWindow() {
293 super.onDetachedFromWindow();
294 mKeyboardVisible = false;
295 mNeedsNewHeight = false;
296 if (mActivityView != null) {
297 mActivityView.setForwardedInsets(Insets.of(0, 0, 0, 0));
298 }
Mark Renoufdb861c92019-07-26 13:58:17 -0400299 if (DEBUG_BUBBLE_EXPANDED_VIEW) {
300 Log.d(TAG, "onDetachedFromWindow: bubble=" + getBubbleKey());
301 }
Mady Mellor5d8f1402019-02-21 18:23:52 -0800302 }
303
304 /**
Issei Suzukicac2a502019-04-16 16:52:50 +0200305 * Set visibility of contents in the expanded state.
306 *
307 * @param visibility {@code true} if the contents should be visible on the screen.
308 *
309 * Note that this contents visibility doesn't affect visibility at {@link android.view.View},
310 * and setting {@code false} actually means rendering the contents in transparent.
311 */
312 void setContentVisibility(boolean visibility) {
Mark Renoufdb861c92019-07-26 13:58:17 -0400313 if (DEBUG_BUBBLE_EXPANDED_VIEW) {
314 Log.d(TAG, "setContentVisibility: visibility=" + visibility
315 + " bubble=" + getBubbleKey());
316 }
Issei Suzukicac2a502019-04-16 16:52:50 +0200317 final float alpha = visibility ? 1f : 0f;
318 mPointerView.setAlpha(alpha);
319 if (mActivityView != null) {
320 mActivityView.setAlpha(alpha);
321 }
322 }
323
324 /**
Mady Mellor5d8f1402019-02-21 18:23:52 -0800325 * Called by {@link BubbleStackView} when the insets for the expanded state should be updated.
326 * This should be done post-move and post-animation.
327 */
328 void updateInsets(WindowInsets insets) {
329 if (usingActivityView()) {
Mady Mellor8fe411d2019-08-16 11:27:53 -0700330 int[] screenLoc = mActivityView.getLocationOnScreen();
331 final int activityViewBottom = screenLoc[1] + mActivityView.getHeight();
Mady Mellor9be3bed2019-08-21 17:26:26 -0700332 final int keyboardTop = mDisplaySize.y - Math.max(insets.getSystemWindowInsetBottom(),
333 insets.getDisplayCutout() != null
334 ? insets.getDisplayCutout().getSafeInsetBottom()
335 : 0);
Mady Mellor8fe411d2019-08-16 11:27:53 -0700336 final int insetsBottom = Math.max(activityViewBottom - keyboardTop, 0);
Mady Mellor5d8f1402019-02-21 18:23:52 -0800337 mActivityView.setForwardedInsets(Insets.of(0, 0, 0, insetsBottom));
338 }
339 }
340
Mady Mellor247ca2c2019-12-02 16:18:59 -0800341 void setStackView(BubbleStackView stackView) {
Mady Mellor9801e852019-01-22 14:50:28 -0800342 mStackView = stackView;
Mady Mellor6d002032019-02-13 13:45:17 -0800343 }
344
345 /**
Mady Mellor247ca2c2019-12-02 16:18:59 -0800346 * Sets the bubble used to populate this view.
Mady Mellor6d002032019-02-13 13:45:17 -0800347 */
Mady Mellor247ca2c2019-12-02 16:18:59 -0800348 void update(Bubble bubble) {
349 if (DEBUG_BUBBLE_EXPANDED_VIEW) {
350 Log.d(TAG, "update: bubble=" + (bubble != null ? bubble.getKey() : "null"));
351 }
352 boolean isNew = mBubble == null;
353 if (isNew || bubble.getKey().equals(mBubble.getKey())) {
354 mBubble = bubble;
355 mSettingsIcon.setContentDescription(getResources().getString(
356 R.string.bubbles_settings_button_description, bubble.getAppName()));
357
358 if (isNew) {
359 mBubbleIntent = mBubble.getBubbleIntent();
Mady Mellor2ac2d3a2020-01-08 17:18:54 -0800360 if (mBubbleIntent != null || mBubble.getShortcutInfo() != null) {
Mady Mellor247ca2c2019-12-02 16:18:59 -0800361 setContentVisibility(false);
362 mActivityView.setVisibility(VISIBLE);
363 }
364 }
365 applyThemeAttrs();
366 } else {
367 Log.w(TAG, "Trying to update entry with different key, new bubble: "
368 + bubble.getKey() + " old bubble: " + bubble.getKey());
369 }
370 }
371
372 /**
373 * Lets activity view know it should be shown / populated with activity content.
374 */
375 void populateExpandedView() {
Mark Renoufdb861c92019-07-26 13:58:17 -0400376 if (DEBUG_BUBBLE_EXPANDED_VIEW) {
377 Log.d(TAG, "populateExpandedView: "
378 + "bubble=" + getBubbleKey());
379 }
380
Mady Mellor5029fa62019-03-05 12:16:21 -0800381 if (usingActivityView()) {
382 mActivityView.setCallback(mStateCallback);
383 } else {
Issei Suzukia91f3962019-06-07 11:48:23 +0200384 Log.e(TAG, "Cannot populate expanded view.");
Mady Mellor5029fa62019-03-05 12:16:21 -0800385 }
Mady Mellor9801e852019-01-22 14:50:28 -0800386 }
387
Mark Renouf041d7262019-02-06 12:09:41 -0500388 boolean performBackPressIfNeeded() {
Mady Mellor323fb0b2019-03-25 12:15:22 -0700389 if (!usingActivityView()) {
Mark Renouf041d7262019-02-06 12:09:41 -0500390 return false;
391 }
392 mActivityView.performBackPress();
393 return true;
394 }
395
Mady Mellorfe7ec032019-01-30 17:32:49 -0800396 void updateHeight() {
Mark Renoufdb861c92019-07-26 13:58:17 -0400397 if (DEBUG_BUBBLE_EXPANDED_VIEW) {
398 Log.d(TAG, "updateHeight: bubble=" + getBubbleKey());
399 }
Mady Mellorfe7ec032019-01-30 17:32:49 -0800400 if (usingActivityView()) {
Mady Mellor99a302602019-06-14 11:39:56 -0700401 float desiredHeight = Math.max(mBubble.getDesiredHeight(mContext), mMinHeight);
Mady Mellor8fe411d2019-08-16 11:27:53 -0700402 float height = Math.min(desiredHeight, getMaxExpandedHeight());
Mady Mellorfe7ec032019-01-30 17:32:49 -0800403 height = Math.max(height, mMinHeight);
404 LayoutParams lp = (LayoutParams) mActivityView.getLayoutParams();
Mady Mellor5d8f1402019-02-21 18:23:52 -0800405 mNeedsNewHeight = lp.height != height;
406 if (!mKeyboardVisible) {
407 // If the keyboard is visible... don't adjust the height because that will cause
408 // a configuration change and the keyboard will be lost.
Mady Mellor7af771a2019-03-07 15:04:54 -0800409 lp.height = (int) height;
Mady Mellor5d8f1402019-02-21 18:23:52 -0800410 mActivityView.setLayoutParams(lp);
411 mNeedsNewHeight = false;
412 }
Mark Renoufdb861c92019-07-26 13:58:17 -0400413 if (DEBUG_BUBBLE_EXPANDED_VIEW) {
414 Log.d(TAG, "updateHeight: bubble=" + getBubbleKey() + " height=" + height
415 + " mNeedsNewHeight=" + mNeedsNewHeight);
416 }
Mady Mellorfe7ec032019-01-30 17:32:49 -0800417 }
418 }
419
Mady Mellora96c9ed2019-06-07 12:55:26 -0700420 private int getMaxExpandedHeight() {
Mady Mellor9be3bed2019-08-21 17:26:26 -0700421 mWindowManager.getDefaultDisplay().getRealSize(mDisplaySize);
Mady Mellora96c9ed2019-06-07 12:55:26 -0700422 int[] windowLocation = mActivityView.getLocationOnScreen();
Mady Mellor8fe411d2019-08-16 11:27:53 -0700423 int bottomInset = getRootWindowInsets() != null
424 ? getRootWindowInsets().getStableInsetBottom()
425 : 0;
426 return mDisplaySize.y - windowLocation[1] - mSettingsIconHeight - mPointerHeight
427 - mPointerMargin - bottomInset;
Mady Mellora96c9ed2019-06-07 12:55:26 -0700428 }
429
Mady Mellor47b11e32019-07-11 19:06:21 -0700430 /**
431 * Whether the provided x, y values (in raw coordinates) are in a touchable area of the
432 * expanded view.
433 *
434 * The touchable areas are the ActivityView (plus some slop around it) and the manage button.
435 */
436 boolean intersectingTouchableContent(int rawX, int rawY) {
437 mTempRect.setEmpty();
438 if (mActivityView != null) {
439 mTempLoc = mActivityView.getLocationOnScreen();
440 mTempRect.set(mTempLoc[0] - mExpandedViewTouchSlop,
441 mTempLoc[1] - mExpandedViewTouchSlop,
442 mTempLoc[0] + mActivityView.getWidth() + mExpandedViewTouchSlop,
443 mTempLoc[1] + mActivityView.getHeight() + mExpandedViewTouchSlop);
444 }
445 if (mTempRect.contains(rawX, rawY)) {
446 return true;
447 }
448 mTempLoc = mSettingsIcon.getLocationOnScreen();
449 mTempRect.set(mTempLoc[0],
450 mTempLoc[1],
451 mTempLoc[0] + mSettingsIcon.getWidth(),
452 mTempLoc[1] + mSettingsIcon.getHeight());
453 if (mTempRect.contains(rawX, rawY)) {
454 return true;
455 }
456 return false;
457 }
458
Mady Mellor9801e852019-01-22 14:50:28 -0800459 @Override
460 public void onClick(View view) {
Mady Mellor99a302602019-06-14 11:39:56 -0700461 if (mBubble == null) {
Mady Mellor9801e852019-01-22 14:50:28 -0800462 return;
463 }
Mady Mellor9801e852019-01-22 14:50:28 -0800464 int id = view.getId();
Lyn Hanc26ff122019-03-29 16:46:07 -0700465 if (id == R.id.settings_button) {
Mady Mellor99a302602019-06-14 11:39:56 -0700466 Intent intent = mBubble.getSettingsIntent();
Steven Wub00225b2019-02-08 14:27:42 -0500467 mStackView.collapseStack(() -> {
Ned Burns00b4b2d2019-10-17 22:09:27 -0400468 mContext.startActivityAsUser(intent, mBubble.getEntry().getSbn().getUser());
Mady Mellor99a302602019-06-14 11:39:56 -0700469 logBubbleClickEvent(mBubble,
Muhammad Qureshi9bced7d2020-01-16 15:22:12 -0800470 SysUiStatsLog.BUBBLE_UICHANGED__ACTION__HEADER_GO_TO_SETTINGS);
Steven Wub00225b2019-02-08 14:27:42 -0500471 });
Mady Mellor9801e852019-01-22 14:50:28 -0800472 }
Mady Mellordea7ecf2018-12-10 15:47:40 -0800473 }
474
475 /**
Mady Mellor3dff9e62019-02-05 18:12:53 -0800476 * Update appearance of the expanded view being displayed.
477 */
478 public void updateView() {
Mark Renoufdb861c92019-07-26 13:58:17 -0400479 if (DEBUG_BUBBLE_EXPANDED_VIEW) {
480 Log.d(TAG, "updateView: bubble="
481 + getBubbleKey());
482 }
Mady Mellor3dff9e62019-02-05 18:12:53 -0800483 if (usingActivityView()
484 && mActivityView.getVisibility() == VISIBLE
485 && mActivityView.isAttachedToWindow()) {
486 mActivityView.onLocationChanged();
Mady Mellor3dff9e62019-02-05 18:12:53 -0800487 }
Mady Mellorfe7ec032019-01-30 17:32:49 -0800488 updateHeight();
Mady Mellor3dff9e62019-02-05 18:12:53 -0800489 }
490
491 /**
Mady Mellordea7ecf2018-12-10 15:47:40 -0800492 * Set the x position that the tip of the triangle should point to.
493 */
Mady Mellor3dff9e62019-02-05 18:12:53 -0800494 public void setPointerPosition(float x) {
Lyn Han9a2f5cf2019-05-23 11:01:41 -0700495 float halfPointerWidth = mPointerWidth / 2f;
496 float pointerLeft = x - halfPointerWidth;
497 mPointerView.setTranslationX(pointerLeft);
Lyn Hanf74ba672019-05-20 16:08:48 -0700498 mPointerView.setVisibility(VISIBLE);
Mady Mellordea7ecf2018-12-10 15:47:40 -0800499 }
500
501 /**
Mady Mellor3dff9e62019-02-05 18:12:53 -0800502 * Removes and releases an ActivityView if one was previously created for this bubble.
Mady Mellordea7ecf2018-12-10 15:47:40 -0800503 */
Mady Mellor94d94a72019-03-05 18:16:59 -0800504 public void cleanUpExpandedState() {
Mark Renoufdb861c92019-07-26 13:58:17 -0400505 if (DEBUG_BUBBLE_EXPANDED_VIEW) {
506 Log.d(TAG, "cleanUpExpandedState: mActivityViewStatus=" + mActivityViewStatus
507 + ", bubble=" + getBubbleKey());
508 }
Mady Mellor3dff9e62019-02-05 18:12:53 -0800509 if (mActivityView == null) {
Mark Renouf89b1a4a2018-12-04 14:59:45 -0500510 return;
511 }
Issei Suzuki734bc942019-06-05 13:59:52 +0200512 switch (mActivityViewStatus) {
513 case INITIALIZED:
514 case ACTIVITY_STARTED:
515 mActivityView.release();
Mady Mellordea7ecf2018-12-10 15:47:40 -0800516 }
Mark Renouf5c732b32019-06-12 15:14:54 -0400517 if (mTaskId != -1) {
518 try {
519 ActivityTaskManager.getService().removeTask(mTaskId);
520 } catch (RemoteException e) {
521 Log.w(TAG, "Failed to remove taskId " + mTaskId);
522 }
523 mTaskId = -1;
Mady Mellordea7ecf2018-12-10 15:47:40 -0800524 }
Mark Renouf28c250d2019-02-25 16:47:34 -0500525 removeView(mActivityView);
Mark Renouf5c732b32019-06-12 15:14:54 -0400526
Mady Mellor3dff9e62019-02-05 18:12:53 -0800527 mActivityView = null;
Issei Suzuki734bc942019-06-05 13:59:52 +0200528 }
529
530 /**
531 * Called when the last task is removed from a {@link android.hardware.display.VirtualDisplay}
532 * which {@link ActivityView} uses.
533 */
534 void notifyDisplayEmpty() {
Mark Renoufdb861c92019-07-26 13:58:17 -0400535 if (DEBUG_BUBBLE_EXPANDED_VIEW) {
536 Log.d(TAG, "notifyDisplayEmpty: bubble="
537 + getBubbleKey()
538 + " mActivityViewStatus=" + mActivityViewStatus);
539 }
Issei Suzuki734bc942019-06-05 13:59:52 +0200540 if (mActivityViewStatus == ActivityViewStatus.ACTIVITY_STARTED) {
541 mActivityViewStatus = ActivityViewStatus.INITIALIZED;
542 }
Mady Mellordea7ecf2018-12-10 15:47:40 -0800543 }
544
Mady Mellor3dff9e62019-02-05 18:12:53 -0800545 private boolean usingActivityView() {
Mady Mellor2ac2d3a2020-01-08 17:18:54 -0800546 return (mBubbleIntent != null || mBubble.getShortcutInfo() != null)
547 && mActivityView != null;
Mady Mellor3dff9e62019-02-05 18:12:53 -0800548 }
549
Mady Mellor390bff42019-04-05 15:09:01 -0700550 /**
551 * @return the display id of the virtual display.
552 */
553 public int getVirtualDisplayId() {
554 if (usingActivityView()) {
555 return mActivityView.getVirtualDisplayId();
556 }
557 return INVALID_DISPLAY;
558 }
559
Steven Wub00225b2019-02-08 14:27:42 -0500560 /**
561 * Logs bubble UI click event.
562 *
Mady Mellor99a302602019-06-14 11:39:56 -0700563 * @param bubble the bubble notification entry that user is interacting with.
Steven Wub00225b2019-02-08 14:27:42 -0500564 * @param action the user interaction enum.
565 */
Mady Mellor99a302602019-06-14 11:39:56 -0700566 private void logBubbleClickEvent(Bubble bubble, int action) {
Ned Burns00b4b2d2019-10-17 22:09:27 -0400567 StatusBarNotification notification = bubble.getEntry().getSbn();
Muhammad Qureshi9bced7d2020-01-16 15:22:12 -0800568 SysUiStatsLog.write(SysUiStatsLog.BUBBLE_UI_CHANGED,
Steven Wub00225b2019-02-08 14:27:42 -0500569 notification.getPackageName(),
570 notification.getNotification().getChannelId(),
571 notification.getId(),
572 mStackView.getBubbleIndex(mStackView.getExpandedBubble()),
573 mStackView.getBubbleCount(),
574 action,
575 mStackView.getNormalizedXPosition(),
Steven Wu45e38ae2019-03-25 16:16:59 -0400576 mStackView.getNormalizedYPosition(),
Mady Mellorb8aaf972019-11-26 10:28:00 -0800577 bubble.showInShade(),
Mady Mellor99a302602019-06-14 11:39:56 -0700578 bubble.isOngoing(),
Mady Mellor6cec3502019-06-17 19:20:49 -0700579 false /* isAppForeground (unused) */);
Mady Mellor7af771a2019-03-07 15:04:54 -0800580 }
Mady Mellordea7ecf2018-12-10 15:47:40 -0800581}