blob: b7ea803eac161fd82f3ad4396bff50749ec0cbb3 [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;
34import android.content.pm.ApplicationInfo;
35import android.content.pm.PackageManager;
Mady Mellordea7ecf2018-12-10 15:47:40 -080036import android.content.res.Resources;
Mady Mellordd497052019-01-30 17:23:48 -080037import android.content.res.TypedArray;
Mady Mellordea7ecf2018-12-10 15:47:40 -080038import android.graphics.Color;
Mady Mellor3dff9e62019-02-05 18:12:53 -080039import android.graphics.Insets;
40import android.graphics.Point;
Mady Mellor47b11e32019-07-11 19:06:21 -070041import android.graphics.Rect;
Mady Mellore8e07712019-01-23 12:45:33 -080042import android.graphics.drawable.Drawable;
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;
Steven Wub00225b2019-02-08 14:27:42 -050048import android.util.StatsLog;
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;
Lyn Han754e77b2019-04-30 14:34:49 -070058import com.android.systemui.statusbar.AlphaOptimizedButton;
Mady Mellordea7ecf2018-12-10 15:47:40 -080059
60/**
Lyn Han02cca812019-04-02 16:27:32 -070061 * Container for the expanded bubble view, handles rendering the caret and settings icon.
Mady Mellordea7ecf2018-12-10 15:47:40 -080062 */
Mady Mellor3d82e682019-02-05 13:34:48 -080063public class BubbleExpandedView extends LinearLayout implements View.OnClickListener {
Issei Suzukia8d07312019-06-07 12:56:19 +020064 private static final String TAG = TAG_WITH_CLASS_NAME ? "BubbleExpandedView" : TAG_BUBBLES;
Mady Mellordea7ecf2018-12-10 15:47:40 -080065
Issei Suzuki734bc942019-06-05 13:59:52 +020066 private enum ActivityViewStatus {
67 // ActivityView is being initialized, cannot start an activity yet.
68 INITIALIZING,
69 // ActivityView is initialized, and ready to start an activity.
70 INITIALIZED,
71 // Activity runs in the ActivityView.
72 ACTIVITY_STARTED,
73 // ActivityView is released, so activity launching will no longer be permitted.
74 RELEASED,
75 }
Mady Mellordea7ecf2018-12-10 15:47:40 -080076
77 // The triangle pointing to the expanded view
78 private View mPointerView;
Mady Mellor44ee2fe2019-01-30 17:51:16 -080079 private int mPointerMargin;
Mady Mellore8e07712019-01-23 12:45:33 -080080
Lyn Han754e77b2019-04-30 14:34:49 -070081 private AlphaOptimizedButton mSettingsIcon;
Mady Mellore8e07712019-01-23 12:45:33 -080082
Mady Mellor3dff9e62019-02-05 18:12:53 -080083 // Views for expanded state
Mady Mellor3dff9e62019-02-05 18:12:53 -080084 private ActivityView mActivityView;
85
Issei Suzuki734bc942019-06-05 13:59:52 +020086 private ActivityViewStatus mActivityViewStatus = ActivityViewStatus.INITIALIZING;
Mark Renouf5c732b32019-06-12 15:14:54 -040087 private int mTaskId = -1;
88
Mady Mellor3dff9e62019-02-05 18:12:53 -080089 private PendingIntent mBubbleIntent;
90
Mady Mellor5d8f1402019-02-21 18:23:52 -080091 private boolean mKeyboardVisible;
92 private boolean mNeedsNewHeight;
93
Mady Mellora96c9ed2019-06-07 12:55:26 -070094 private Point mDisplaySize;
Mady Mellorfe7ec032019-01-30 17:32:49 -080095 private int mMinHeight;
Lyn Han02cca812019-04-02 16:27:32 -070096 private int mSettingsIconHeight;
Lyn Han02cca812019-04-02 16:27:32 -070097 private int mPointerWidth;
98 private int mPointerHeight;
Mark Renouf34d04f32019-05-13 15:53:18 -040099 private ShapeDrawable mPointerDrawable;
Mady Mellor47b11e32019-07-11 19:06:21 -0700100 private Rect mTempRect = new Rect();
101 private int[] mTempLoc = new int[2];
102 private int mExpandedViewTouchSlop;
Mady Mellordea7ecf2018-12-10 15:47:40 -0800103
Mady Mellor99a302602019-06-14 11:39:56 -0700104 private Bubble mBubble;
Mady Mellor9801e852019-01-22 14:50:28 -0800105 private PackageManager mPm;
106 private String mAppName;
Mady Mellore8e07712019-01-23 12:45:33 -0800107 private Drawable mAppIcon;
108
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 Mellorc41ed322019-09-25 11:19:26 -0700133 Intent fillInIntent = new Intent();
134 // Apply flags to make behaviour match documentLaunchMode=always.
135 fillInIntent.addFlags(FLAG_ACTIVITY_NEW_DOCUMENT);
136 fillInIntent.addFlags(FLAG_ACTIVITY_MULTIPLE_TASK);
Mady Mellor8454ddf2019-08-15 11:16:23 -0700137 try {
Mady Mellorc41ed322019-09-25 11:19:26 -0700138 mActivityView.startActivity(mBubbleIntent, fillInIntent, options);
Mady Mellor8454ddf2019-08-15 11:16:23 -0700139 } catch (RuntimeException e) {
140 // If there's a runtime exception here then there's something
141 // wrong with the intent, we can't really recover / try to populate
142 // the bubble again so we'll just remove it.
143 Log.w(TAG, "Exception while displaying bubble: " + getBubbleKey()
144 + ", " + e.getMessage() + "; removing bubble");
145 mBubbleController.removeBubble(mBubble.getKey(),
146 BubbleController.DISMISS_INVALID_INTENT);
147 }
Mark Renoufdb861c92019-07-26 13:58:17 -0400148 });
Issei Suzuki734bc942019-06-05 13:59:52 +0200149 mActivityViewStatus = ActivityViewStatus.ACTIVITY_STARTED;
Mady Mellor6d002032019-02-13 13:45:17 -0800150 }
Mady Mellor3dff9e62019-02-05 18:12:53 -0800151 }
152
153 @Override
154 public void onActivityViewDestroyed(ActivityView view) {
Mark Renoufdb861c92019-07-26 13:58:17 -0400155 if (DEBUG_BUBBLE_EXPANDED_VIEW) {
156 Log.d(TAG, "onActivityViewDestroyed: mActivityViewStatus=" + mActivityViewStatus
157 + " bubble=" + getBubbleKey());
158 }
Issei Suzuki734bc942019-06-05 13:59:52 +0200159 mActivityViewStatus = ActivityViewStatus.RELEASED;
Mady Mellor3dff9e62019-02-05 18:12:53 -0800160 }
161
Mark Renouf5c732b32019-06-12 15:14:54 -0400162 @Override
163 public void onTaskCreated(int taskId, ComponentName componentName) {
Mark Renoufdb861c92019-07-26 13:58:17 -0400164 if (DEBUG_BUBBLE_EXPANDED_VIEW) {
165 Log.d(TAG, "onTaskCreated: taskId=" + taskId
166 + " bubble=" + getBubbleKey());
167 }
Mark Renouf5c732b32019-06-12 15:14:54 -0400168 // Since Bubble ActivityView applies singleTaskDisplay this is
169 // guaranteed to only be called once per ActivityView. The taskId is
170 // saved to use for removeTask, preventing appearance in recent tasks.
171 mTaskId = taskId;
Mady Mellor3dff9e62019-02-05 18:12:53 -0800172 }
173
174 /**
175 * This is only called for tasks on this ActivityView, which is also set to
176 * single-task mode -- meaning never more than one task on this display. If a task
177 * is being removed, it's the top Activity finishing and this bubble should
178 * be removed or collapsed.
179 */
180 @Override
181 public void onTaskRemovalStarted(int taskId) {
Mark Renoufdb861c92019-07-26 13:58:17 -0400182 if (DEBUG_BUBBLE_EXPANDED_VIEW) {
183 Log.d(TAG, "onTaskRemovalStarted: taskId=" + taskId
184 + " mActivityViewStatus=" + mActivityViewStatus
185 + " bubble=" + getBubbleKey());
186 }
Mady Mellor7f234902019-10-20 12:06:29 -0700187 if (mBubble != null && !mBubble.isUserCreated()) {
188 if (mBubble != null) {
189 // Must post because this is called from a binder thread.
190 post(() -> mBubbleController.removeBubble(mBubble.getKey(),
191 BubbleController.DISMISS_TASK_FINISHED));
192 }
Mady Mellor3dff9e62019-02-05 18:12:53 -0800193 }
194 }
195 };
Mady Mellore8e07712019-01-23 12:45:33 -0800196
Mady Mellor3d82e682019-02-05 13:34:48 -0800197 public BubbleExpandedView(Context context) {
Mady Mellordea7ecf2018-12-10 15:47:40 -0800198 this(context, null);
199 }
200
Mady Mellor3d82e682019-02-05 13:34:48 -0800201 public BubbleExpandedView(Context context, AttributeSet attrs) {
Mady Mellordea7ecf2018-12-10 15:47:40 -0800202 this(context, attrs, 0);
203 }
204
Mady Mellor3d82e682019-02-05 13:34:48 -0800205 public BubbleExpandedView(Context context, AttributeSet attrs, int defStyleAttr) {
Mady Mellordea7ecf2018-12-10 15:47:40 -0800206 this(context, attrs, defStyleAttr, 0);
207 }
208
Mady Mellor3d82e682019-02-05 13:34:48 -0800209 public BubbleExpandedView(Context context, AttributeSet attrs, int defStyleAttr,
Mady Mellordea7ecf2018-12-10 15:47:40 -0800210 int defStyleRes) {
211 super(context, attrs, defStyleAttr, defStyleRes);
Mady Mellor9801e852019-01-22 14:50:28 -0800212 mPm = context.getPackageManager();
Mady Mellora96c9ed2019-06-07 12:55:26 -0700213 mDisplaySize = new Point();
Mady Mellor9be3bed2019-08-21 17:26:26 -0700214 mWindowManager = (WindowManager) context.getSystemService(Context.WINDOW_SERVICE);
Mady Mellor8fe411d2019-08-16 11:27:53 -0700215 // Get the real size -- this includes screen decorations (notches, statusbar, navbar).
Mady Mellor9be3bed2019-08-21 17:26:26 -0700216 mWindowManager.getDefaultDisplay().getRealSize(mDisplaySize);
Mady Mellor47b11e32019-07-11 19:06:21 -0700217 Resources res = getResources();
218 mMinHeight = res.getDimensionPixelSize(R.dimen.bubble_expanded_default_height);
219 mPointerMargin = res.getDimensionPixelSize(R.dimen.bubble_pointer_margin);
220 mExpandedViewTouchSlop = res.getDimensionPixelSize(R.dimen.bubble_expanded_view_slop);
Mady Mellordea7ecf2018-12-10 15:47:40 -0800221 }
222
223 @Override
224 protected void onFinishInflate() {
225 super.onFinishInflate();
Mark Renoufdb861c92019-07-26 13:58:17 -0400226 if (DEBUG_BUBBLE_EXPANDED_VIEW) {
227 Log.d(TAG, "onFinishInflate: bubble=" + getBubbleKey());
228 }
Mady Mellordea7ecf2018-12-10 15:47:40 -0800229
230 Resources res = getResources();
231 mPointerView = findViewById(R.id.pointer_view);
Lyn Han02cca812019-04-02 16:27:32 -0700232 mPointerWidth = res.getDimensionPixelSize(R.dimen.bubble_pointer_width);
233 mPointerHeight = res.getDimensionPixelSize(R.dimen.bubble_pointer_height);
Mady Mellordd497052019-01-30 17:23:48 -0800234
Mady Mellordd497052019-01-30 17:23:48 -0800235
Mark Renouf34d04f32019-05-13 15:53:18 -0400236 mPointerDrawable = new ShapeDrawable(TriangleShape.create(
Lyn Han5aa27e22019-05-15 10:55:07 -0700237 mPointerWidth, mPointerHeight, true /* pointUp */));
Mark Renouf34d04f32019-05-13 15:53:18 -0400238 mPointerView.setBackground(mPointerDrawable);
Mady Mellora96c9ed2019-06-07 12:55:26 -0700239 mPointerView.setVisibility(INVISIBLE);
Mady Mellor9801e852019-01-22 14:50:28 -0800240
Lyn Han02cca812019-04-02 16:27:32 -0700241 mSettingsIconHeight = getContext().getResources().getDimensionPixelSize(
Mady Mellor818eef02019-08-16 16:12:29 -0700242 R.dimen.bubble_settings_size);
Lyn Han02cca812019-04-02 16:27:32 -0700243 mSettingsIcon = findViewById(R.id.settings_button);
Lyn Han02cca812019-04-02 16:27:32 -0700244 mSettingsIcon.setOnClickListener(this);
Lyn Han02cca812019-04-02 16:27:32 -0700245
Mady Mellor3dff9e62019-02-05 18:12:53 -0800246 mActivityView = new ActivityView(mContext, null /* attrs */, 0 /* defStyle */,
247 true /* singleTaskInstance */);
lumarkdc9b3192019-07-23 21:18:17 +0800248 // Set ActivityView's alpha value as zero, since there is no view content to be shown.
Issei Suzukicac2a502019-04-16 16:52:50 +0200249 setContentVisibility(false);
Mady Mellor3dff9e62019-02-05 18:12:53 -0800250 addView(mActivityView);
251
Lyn Han5aa27e22019-05-15 10:55:07 -0700252 // Expanded stack layout, top to bottom:
253 // Expanded view container
254 // ==> bubble row
255 // ==> expanded view
256 // ==> activity view
257 // ==> manage button
258 bringChildToFront(mActivityView);
259 bringChildToFront(mSettingsIcon);
Mady Mellor52b1ac62019-04-10 16:59:03 -0700260
Mark Renouf34d04f32019-05-13 15:53:18 -0400261 applyThemeAttrs();
262
Mady Mellor5d8f1402019-02-21 18:23:52 -0800263 setOnApplyWindowInsetsListener((View view, WindowInsets insets) -> {
264 // Keep track of IME displaying because we should not make any adjustments that might
265 // cause a config change while the IME is displayed otherwise it'll loose focus.
Mady Mellor3dff9e62019-02-05 18:12:53 -0800266 final int keyboardHeight = insets.getSystemWindowInsetBottom()
267 - insets.getStableInsetBottom();
Mady Mellor5d8f1402019-02-21 18:23:52 -0800268 mKeyboardVisible = keyboardHeight != 0;
269 if (!mKeyboardVisible && mNeedsNewHeight) {
270 updateHeight();
271 }
Mady Mellor3dff9e62019-02-05 18:12:53 -0800272 return view.onApplyWindowInsets(insets);
273 });
Mady Mellore8e07712019-01-23 12:45:33 -0800274 }
275
Mark Renoufdb861c92019-07-26 13:58:17 -0400276 private String getBubbleKey() {
277 return mBubble != null ? mBubble.getKey() : "null";
278 }
279
Mark Renouf34d04f32019-05-13 15:53:18 -0400280 void applyThemeAttrs() {
Lyn Han61bfdb32019-12-10 14:34:12 -0800281 final TypedArray ta = mContext.obtainStyledAttributes(
282 new int[] {
283 android.R.attr.colorBackgroundFloating,
284 android.R.attr.dialogCornerRadius});
285 int bgColor = ta.getColor(0, Color.WHITE);
286 float cornerRadius = ta.getDimensionPixelSize(1, 0);
Mark Renouf34d04f32019-05-13 15:53:18 -0400287 ta.recycle();
288
Mark Renouf34d04f32019-05-13 15:53:18 -0400289 mPointerDrawable.setTint(bgColor);
Mark Renouf34d04f32019-05-13 15:53:18 -0400290 if (ScreenDecorationsUtils.supportsRoundedCornersOnWindows(mContext.getResources())) {
291 mActivityView.setCornerRadius(cornerRadius);
292 }
293 }
294
Mady Mellor5d8f1402019-02-21 18:23:52 -0800295 @Override
296 protected void onDetachedFromWindow() {
297 super.onDetachedFromWindow();
298 mKeyboardVisible = false;
299 mNeedsNewHeight = false;
300 if (mActivityView != null) {
301 mActivityView.setForwardedInsets(Insets.of(0, 0, 0, 0));
302 }
Mark Renoufdb861c92019-07-26 13:58:17 -0400303 if (DEBUG_BUBBLE_EXPANDED_VIEW) {
304 Log.d(TAG, "onDetachedFromWindow: bubble=" + getBubbleKey());
305 }
Mady Mellor5d8f1402019-02-21 18:23:52 -0800306 }
307
308 /**
Issei Suzukicac2a502019-04-16 16:52:50 +0200309 * Set visibility of contents in the expanded state.
310 *
311 * @param visibility {@code true} if the contents should be visible on the screen.
312 *
313 * Note that this contents visibility doesn't affect visibility at {@link android.view.View},
314 * and setting {@code false} actually means rendering the contents in transparent.
315 */
316 void setContentVisibility(boolean visibility) {
Mark Renoufdb861c92019-07-26 13:58:17 -0400317 if (DEBUG_BUBBLE_EXPANDED_VIEW) {
318 Log.d(TAG, "setContentVisibility: visibility=" + visibility
319 + " bubble=" + getBubbleKey());
320 }
Issei Suzukicac2a502019-04-16 16:52:50 +0200321 final float alpha = visibility ? 1f : 0f;
322 mPointerView.setAlpha(alpha);
323 if (mActivityView != null) {
324 mActivityView.setAlpha(alpha);
325 }
326 }
327
328 /**
Mady Mellor5d8f1402019-02-21 18:23:52 -0800329 * Called by {@link BubbleStackView} when the insets for the expanded state should be updated.
330 * This should be done post-move and post-animation.
331 */
332 void updateInsets(WindowInsets insets) {
333 if (usingActivityView()) {
Mady Mellor8fe411d2019-08-16 11:27:53 -0700334 int[] screenLoc = mActivityView.getLocationOnScreen();
335 final int activityViewBottom = screenLoc[1] + mActivityView.getHeight();
Mady Mellor9be3bed2019-08-21 17:26:26 -0700336 final int keyboardTop = mDisplaySize.y - Math.max(insets.getSystemWindowInsetBottom(),
337 insets.getDisplayCutout() != null
338 ? insets.getDisplayCutout().getSafeInsetBottom()
339 : 0);
Mady Mellor8fe411d2019-08-16 11:27:53 -0700340 final int insetsBottom = Math.max(activityViewBottom - keyboardTop, 0);
Mady Mellor5d8f1402019-02-21 18:23:52 -0800341 mActivityView.setForwardedInsets(Insets.of(0, 0, 0, insetsBottom));
342 }
343 }
344
Mady Mellore8e07712019-01-23 12:45:33 -0800345 /**
Mady Mellor99a302602019-06-14 11:39:56 -0700346 * Sets the bubble used to populate this view.
Mady Mellore8e07712019-01-23 12:45:33 -0800347 */
Mady Mellor99a302602019-06-14 11:39:56 -0700348 public void setBubble(Bubble bubble, BubbleStackView stackView, String appName) {
Mark Renoufdb861c92019-07-26 13:58:17 -0400349 if (DEBUG_BUBBLE_EXPANDED_VIEW) {
350 Log.d(TAG, "setBubble: bubble=" + (bubble != null ? bubble.getKey() : "null"));
351 }
Mady Mellor9801e852019-01-22 14:50:28 -0800352
Mady Mellor9801e852019-01-22 14:50:28 -0800353 mStackView = stackView;
Mady Mellor99a302602019-06-14 11:39:56 -0700354 mBubble = bubble;
Lyn Han6c40fe72019-05-08 14:06:33 -0700355 mAppName = appName;
Mady Mellor9801e852019-01-22 14:50:28 -0800356
Mady Mellor9801e852019-01-22 14:50:28 -0800357 try {
Issei Suzukia91f3962019-06-07 11:48:23 +0200358 ApplicationInfo info = mPm.getApplicationInfo(
Mady Mellor99a302602019-06-14 11:39:56 -0700359 bubble.getPackageName(),
Mady Mellor9801e852019-01-22 14:50:28 -0800360 PackageManager.MATCH_UNINSTALLED_PACKAGES
361 | PackageManager.MATCH_DISABLED_COMPONENTS
362 | PackageManager.MATCH_DIRECT_BOOT_UNAWARE
363 | PackageManager.MATCH_DIRECT_BOOT_AWARE);
Issei Suzukia91f3962019-06-07 11:48:23 +0200364 mAppIcon = mPm.getApplicationIcon(info);
Mady Mellor9801e852019-01-22 14:50:28 -0800365 } catch (PackageManager.NameNotFoundException e) {
Lyn Han6c40fe72019-05-08 14:06:33 -0700366 // Do nothing.
Mady Mellor9801e852019-01-22 14:50:28 -0800367 }
Mady Mellore8e07712019-01-23 12:45:33 -0800368 if (mAppIcon == null) {
369 mAppIcon = mPm.getDefaultActivityIcon();
370 }
Mark Renouf34d04f32019-05-13 15:53:18 -0400371 applyThemeAttrs();
Lyn Han69149122019-04-30 12:03:12 -0700372 showSettingsIcon();
Mady Mellor3dff9e62019-02-05 18:12:53 -0800373 updateExpandedView();
Mady Mellor6d002032019-02-13 13:45:17 -0800374 }
375
376 /**
377 * Lets activity view know it should be shown / populated.
378 */
Mady Mellor5029fa62019-03-05 12:16:21 -0800379 public void populateExpandedView() {
Mark Renoufdb861c92019-07-26 13:58:17 -0400380 if (DEBUG_BUBBLE_EXPANDED_VIEW) {
381 Log.d(TAG, "populateExpandedView: "
382 + "bubble=" + getBubbleKey());
383 }
384
Mady Mellor5029fa62019-03-05 12:16:21 -0800385 if (usingActivityView()) {
386 mActivityView.setCallback(mStateCallback);
387 } else {
Issei Suzukia91f3962019-06-07 11:48:23 +0200388 Log.e(TAG, "Cannot populate expanded view.");
Mady Mellor5029fa62019-03-05 12:16:21 -0800389 }
Mady Mellor9801e852019-01-22 14:50:28 -0800390 }
391
Mady Mellorfe7ec032019-01-30 17:32:49 -0800392 /**
Mady Mellor99a302602019-06-14 11:39:56 -0700393 * Updates the bubble backing this view. This will not re-populate ActivityView, it will
Lyn Han02cca812019-04-02 16:27:32 -0700394 * only update the deep-links in the title, and the height of the view.
Mady Mellorfe7ec032019-01-30 17:32:49 -0800395 */
Mady Mellor99a302602019-06-14 11:39:56 -0700396 public void update(Bubble bubble) {
Mark Renoufdb861c92019-07-26 13:58:17 -0400397 if (DEBUG_BUBBLE_EXPANDED_VIEW) {
398 Log.d(TAG, "update: bubble=" + (bubble != null ? bubble.getKey() : "null"));
399 }
Mady Mellor99a302602019-06-14 11:39:56 -0700400 if (bubble.getKey().equals(mBubble.getKey())) {
401 mBubble = bubble;
Lyn Han02cca812019-04-02 16:27:32 -0700402 updateSettingsContentDescription();
Mady Mellorfe7ec032019-01-30 17:32:49 -0800403 updateHeight();
404 } else {
Mady Mellor99a302602019-06-14 11:39:56 -0700405 Log.w(TAG, "Trying to update entry with different key, new bubble: "
406 + bubble.getKey() + " old bubble: " + bubble.getKey());
Mady Mellorfe7ec032019-01-30 17:32:49 -0800407 }
408 }
409
Mady Mellor3dff9e62019-02-05 18:12:53 -0800410 private void updateExpandedView() {
Mark Renoufdb861c92019-07-26 13:58:17 -0400411 if (DEBUG_BUBBLE_EXPANDED_VIEW) {
412 Log.d(TAG, "updateExpandedView: bubble="
413 + getBubbleKey());
414 }
415
Mady Mellor99a302602019-06-14 11:39:56 -0700416 mBubbleIntent = mBubble.getBubbleIntent(mContext);
Mady Mellor3dff9e62019-02-05 18:12:53 -0800417 if (mBubbleIntent != null) {
Issei Suzukicac2a502019-04-16 16:52:50 +0200418 setContentVisibility(false);
Mady Mellor3dff9e62019-02-05 18:12:53 -0800419 mActivityView.setVisibility(VISIBLE);
Mady Mellor3dff9e62019-02-05 18:12:53 -0800420 }
421 updateView();
422 }
423
Mark Renouf041d7262019-02-06 12:09:41 -0500424 boolean performBackPressIfNeeded() {
Mady Mellor323fb0b2019-03-25 12:15:22 -0700425 if (!usingActivityView()) {
Mark Renouf041d7262019-02-06 12:09:41 -0500426 return false;
427 }
428 mActivityView.performBackPress();
429 return true;
430 }
431
Mady Mellorfe7ec032019-01-30 17:32:49 -0800432 void updateHeight() {
Mark Renoufdb861c92019-07-26 13:58:17 -0400433 if (DEBUG_BUBBLE_EXPANDED_VIEW) {
434 Log.d(TAG, "updateHeight: bubble=" + getBubbleKey());
435 }
Mady Mellorfe7ec032019-01-30 17:32:49 -0800436 if (usingActivityView()) {
Mady Mellor99a302602019-06-14 11:39:56 -0700437 float desiredHeight = Math.max(mBubble.getDesiredHeight(mContext), mMinHeight);
Mady Mellor8fe411d2019-08-16 11:27:53 -0700438 float height = Math.min(desiredHeight, getMaxExpandedHeight());
Mady Mellorfe7ec032019-01-30 17:32:49 -0800439 height = Math.max(height, mMinHeight);
440 LayoutParams lp = (LayoutParams) mActivityView.getLayoutParams();
Mady Mellor5d8f1402019-02-21 18:23:52 -0800441 mNeedsNewHeight = lp.height != height;
442 if (!mKeyboardVisible) {
443 // If the keyboard is visible... don't adjust the height because that will cause
444 // a configuration change and the keyboard will be lost.
Mady Mellor7af771a2019-03-07 15:04:54 -0800445 lp.height = (int) height;
Mady Mellor5d8f1402019-02-21 18:23:52 -0800446 mActivityView.setLayoutParams(lp);
447 mNeedsNewHeight = false;
448 }
Mark Renoufdb861c92019-07-26 13:58:17 -0400449 if (DEBUG_BUBBLE_EXPANDED_VIEW) {
450 Log.d(TAG, "updateHeight: bubble=" + getBubbleKey() + " height=" + height
451 + " mNeedsNewHeight=" + mNeedsNewHeight);
452 }
Mady Mellorfe7ec032019-01-30 17:32:49 -0800453 }
454 }
455
Mady Mellora96c9ed2019-06-07 12:55:26 -0700456 private int getMaxExpandedHeight() {
Mady Mellor9be3bed2019-08-21 17:26:26 -0700457 mWindowManager.getDefaultDisplay().getRealSize(mDisplaySize);
Mady Mellora96c9ed2019-06-07 12:55:26 -0700458 int[] windowLocation = mActivityView.getLocationOnScreen();
Mady Mellor8fe411d2019-08-16 11:27:53 -0700459 int bottomInset = getRootWindowInsets() != null
460 ? getRootWindowInsets().getStableInsetBottom()
461 : 0;
462 return mDisplaySize.y - windowLocation[1] - mSettingsIconHeight - mPointerHeight
463 - mPointerMargin - bottomInset;
Mady Mellora96c9ed2019-06-07 12:55:26 -0700464 }
465
Mady Mellor47b11e32019-07-11 19:06:21 -0700466 /**
467 * Whether the provided x, y values (in raw coordinates) are in a touchable area of the
468 * expanded view.
469 *
470 * The touchable areas are the ActivityView (plus some slop around it) and the manage button.
471 */
472 boolean intersectingTouchableContent(int rawX, int rawY) {
473 mTempRect.setEmpty();
474 if (mActivityView != null) {
475 mTempLoc = mActivityView.getLocationOnScreen();
476 mTempRect.set(mTempLoc[0] - mExpandedViewTouchSlop,
477 mTempLoc[1] - mExpandedViewTouchSlop,
478 mTempLoc[0] + mActivityView.getWidth() + mExpandedViewTouchSlop,
479 mTempLoc[1] + mActivityView.getHeight() + mExpandedViewTouchSlop);
480 }
481 if (mTempRect.contains(rawX, rawY)) {
482 return true;
483 }
484 mTempLoc = mSettingsIcon.getLocationOnScreen();
485 mTempRect.set(mTempLoc[0],
486 mTempLoc[1],
487 mTempLoc[0] + mSettingsIcon.getWidth(),
488 mTempLoc[1] + mSettingsIcon.getHeight());
489 if (mTempRect.contains(rawX, rawY)) {
490 return true;
491 }
492 return false;
493 }
494
Mady Mellor9801e852019-01-22 14:50:28 -0800495 @Override
496 public void onClick(View view) {
Mady Mellor99a302602019-06-14 11:39:56 -0700497 if (mBubble == null) {
Mady Mellor9801e852019-01-22 14:50:28 -0800498 return;
499 }
Mady Mellor9801e852019-01-22 14:50:28 -0800500 int id = view.getId();
Lyn Hanc26ff122019-03-29 16:46:07 -0700501 if (id == R.id.settings_button) {
Mady Mellor99a302602019-06-14 11:39:56 -0700502 Intent intent = mBubble.getSettingsIntent();
Steven Wub00225b2019-02-08 14:27:42 -0500503 mStackView.collapseStack(() -> {
Ned Burns00b4b2d2019-10-17 22:09:27 -0400504 mContext.startActivityAsUser(intent, mBubble.getEntry().getSbn().getUser());
Mady Mellor99a302602019-06-14 11:39:56 -0700505 logBubbleClickEvent(mBubble,
Steven Wub00225b2019-02-08 14:27:42 -0500506 StatsLog.BUBBLE_UICHANGED__ACTION__HEADER_GO_TO_SETTINGS);
507 });
Mady Mellor9801e852019-01-22 14:50:28 -0800508 }
Mady Mellordea7ecf2018-12-10 15:47:40 -0800509 }
510
Lyn Han02cca812019-04-02 16:27:32 -0700511 private void updateSettingsContentDescription() {
512 mSettingsIcon.setContentDescription(getResources().getString(
513 R.string.bubbles_settings_button_description, mAppName));
514 }
515
Lyn Hanc26ff122019-03-29 16:46:07 -0700516 void showSettingsIcon() {
Lyn Han02cca812019-04-02 16:27:32 -0700517 updateSettingsContentDescription();
Lyn Hanc26ff122019-03-29 16:46:07 -0700518 mSettingsIcon.setVisibility(VISIBLE);
519 }
520
Mady Mellordea7ecf2018-12-10 15:47:40 -0800521 /**
Mady Mellor3dff9e62019-02-05 18:12:53 -0800522 * Update appearance of the expanded view being displayed.
523 */
524 public void updateView() {
Mark Renoufdb861c92019-07-26 13:58:17 -0400525 if (DEBUG_BUBBLE_EXPANDED_VIEW) {
526 Log.d(TAG, "updateView: bubble="
527 + getBubbleKey());
528 }
Mady Mellor3dff9e62019-02-05 18:12:53 -0800529 if (usingActivityView()
530 && mActivityView.getVisibility() == VISIBLE
531 && mActivityView.isAttachedToWindow()) {
532 mActivityView.onLocationChanged();
Mady Mellor3dff9e62019-02-05 18:12:53 -0800533 }
Mady Mellorfe7ec032019-01-30 17:32:49 -0800534 updateHeight();
Mady Mellor3dff9e62019-02-05 18:12:53 -0800535 }
536
537 /**
Mady Mellordea7ecf2018-12-10 15:47:40 -0800538 * Set the x position that the tip of the triangle should point to.
539 */
Mady Mellor3dff9e62019-02-05 18:12:53 -0800540 public void setPointerPosition(float x) {
Lyn Han9a2f5cf2019-05-23 11:01:41 -0700541 float halfPointerWidth = mPointerWidth / 2f;
542 float pointerLeft = x - halfPointerWidth;
543 mPointerView.setTranslationX(pointerLeft);
Lyn Hanf74ba672019-05-20 16:08:48 -0700544 mPointerView.setVisibility(VISIBLE);
Mady Mellordea7ecf2018-12-10 15:47:40 -0800545 }
546
547 /**
Mady Mellor3dff9e62019-02-05 18:12:53 -0800548 * Removes and releases an ActivityView if one was previously created for this bubble.
Mady Mellordea7ecf2018-12-10 15:47:40 -0800549 */
Mady Mellor94d94a72019-03-05 18:16:59 -0800550 public void cleanUpExpandedState() {
Mark Renoufdb861c92019-07-26 13:58:17 -0400551 if (DEBUG_BUBBLE_EXPANDED_VIEW) {
552 Log.d(TAG, "cleanUpExpandedState: mActivityViewStatus=" + mActivityViewStatus
553 + ", bubble=" + getBubbleKey());
554 }
Mady Mellor3dff9e62019-02-05 18:12:53 -0800555 if (mActivityView == null) {
Mark Renouf89b1a4a2018-12-04 14:59:45 -0500556 return;
557 }
Issei Suzuki734bc942019-06-05 13:59:52 +0200558 switch (mActivityViewStatus) {
559 case INITIALIZED:
560 case ACTIVITY_STARTED:
561 mActivityView.release();
Mady Mellordea7ecf2018-12-10 15:47:40 -0800562 }
Mark Renouf5c732b32019-06-12 15:14:54 -0400563 if (mTaskId != -1) {
564 try {
565 ActivityTaskManager.getService().removeTask(mTaskId);
566 } catch (RemoteException e) {
567 Log.w(TAG, "Failed to remove taskId " + mTaskId);
568 }
569 mTaskId = -1;
Mady Mellordea7ecf2018-12-10 15:47:40 -0800570 }
Mark Renouf28c250d2019-02-25 16:47:34 -0500571 removeView(mActivityView);
Mark Renouf5c732b32019-06-12 15:14:54 -0400572
Mady Mellor3dff9e62019-02-05 18:12:53 -0800573 mActivityView = null;
Issei Suzuki734bc942019-06-05 13:59:52 +0200574 }
575
576 /**
577 * Called when the last task is removed from a {@link android.hardware.display.VirtualDisplay}
578 * which {@link ActivityView} uses.
579 */
580 void notifyDisplayEmpty() {
Mark Renoufdb861c92019-07-26 13:58:17 -0400581 if (DEBUG_BUBBLE_EXPANDED_VIEW) {
582 Log.d(TAG, "notifyDisplayEmpty: bubble="
583 + getBubbleKey()
584 + " mActivityViewStatus=" + mActivityViewStatus);
585 }
Issei Suzuki734bc942019-06-05 13:59:52 +0200586 if (mActivityViewStatus == ActivityViewStatus.ACTIVITY_STARTED) {
587 mActivityViewStatus = ActivityViewStatus.INITIALIZED;
588 }
Mady Mellordea7ecf2018-12-10 15:47:40 -0800589 }
590
Mady Mellor3dff9e62019-02-05 18:12:53 -0800591 private boolean usingActivityView() {
Mady Mellor323fb0b2019-03-25 12:15:22 -0700592 return mBubbleIntent != null && mActivityView != null;
Mady Mellor3dff9e62019-02-05 18:12:53 -0800593 }
594
Mady Mellor390bff42019-04-05 15:09:01 -0700595 /**
596 * @return the display id of the virtual display.
597 */
598 public int getVirtualDisplayId() {
599 if (usingActivityView()) {
600 return mActivityView.getVirtualDisplayId();
601 }
602 return INVALID_DISPLAY;
603 }
604
Steven Wub00225b2019-02-08 14:27:42 -0500605 /**
606 * Logs bubble UI click event.
607 *
Mady Mellor99a302602019-06-14 11:39:56 -0700608 * @param bubble the bubble notification entry that user is interacting with.
Steven Wub00225b2019-02-08 14:27:42 -0500609 * @param action the user interaction enum.
610 */
Mady Mellor99a302602019-06-14 11:39:56 -0700611 private void logBubbleClickEvent(Bubble bubble, int action) {
Ned Burns00b4b2d2019-10-17 22:09:27 -0400612 StatusBarNotification notification = bubble.getEntry().getSbn();
Steven Wub00225b2019-02-08 14:27:42 -0500613 StatsLog.write(StatsLog.BUBBLE_UI_CHANGED,
614 notification.getPackageName(),
615 notification.getNotification().getChannelId(),
616 notification.getId(),
617 mStackView.getBubbleIndex(mStackView.getExpandedBubble()),
618 mStackView.getBubbleCount(),
619 action,
620 mStackView.getNormalizedXPosition(),
Steven Wu45e38ae2019-03-25 16:16:59 -0400621 mStackView.getNormalizedYPosition(),
Mady Mellor99a302602019-06-14 11:39:56 -0700622 bubble.showInShadeWhenBubble(),
623 bubble.isOngoing(),
Mady Mellor6cec3502019-06-17 19:20:49 -0700624 false /* isAppForeground (unused) */);
Mady Mellor7af771a2019-03-07 15:04:54 -0800625 }
Mady Mellordea7ecf2018-12-10 15:47:40 -0800626}