blob: 521ebde7d2f0aaaee6d214923c5c3cbdf9419e79 [file] [log] [blame]
Mady Mellordea7ecf2018-12-10 15:47:40 -08001/*
2 * Copyright (C) 2018 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17package com.android.systemui.bubbles;
18
Mady Mellor390bff42019-04-05 15:09:01 -070019import static android.view.Display.INVALID_DISPLAY;
Mady Mellor3dff9e62019-02-05 18:12:53 -080020
Mark Renoufdb861c92019-07-26 13:58:17 -040021import static com.android.systemui.bubbles.BubbleDebugConfig.DEBUG_BUBBLE_EXPANDED_VIEW;
Issei Suzukia8d07312019-06-07 12:56:19 +020022import static com.android.systemui.bubbles.BubbleDebugConfig.TAG_BUBBLES;
23import static com.android.systemui.bubbles.BubbleDebugConfig.TAG_WITH_CLASS_NAME;
Mady Mellorca0c24c2019-05-16 16:14:32 -070024
Mady Mellor60101c92019-04-11 19:04:00 -070025import android.app.ActivityOptions;
Mark Renouf5c732b32019-06-12 15:14:54 -040026import android.app.ActivityTaskManager;
Mady Mellor3dff9e62019-02-05 18:12:53 -080027import android.app.ActivityView;
Mady Mellor9801e852019-01-22 14:50:28 -080028import android.app.PendingIntent;
Mark Renouf5c732b32019-06-12 15:14:54 -040029import android.content.ComponentName;
Mady Mellordea7ecf2018-12-10 15:47:40 -080030import android.content.Context;
Mady Mellor9801e852019-01-22 14:50:28 -080031import android.content.Intent;
32import android.content.pm.ApplicationInfo;
33import android.content.pm.PackageManager;
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 Mellore8e07712019-01-23 12:45:33 -080040import android.graphics.drawable.Drawable;
Mady Mellordea7ecf2018-12-10 15:47:40 -080041import android.graphics.drawable.ShapeDrawable;
Mark Renouf5c732b32019-06-12 15:14:54 -040042import android.os.RemoteException;
Steven Wub00225b2019-02-08 14:27:42 -050043import android.service.notification.StatusBarNotification;
Mady Mellordea7ecf2018-12-10 15:47:40 -080044import android.util.AttributeSet;
Mady Mellor9801e852019-01-22 14:50:28 -080045import android.util.Log;
Steven Wub00225b2019-02-08 14:27:42 -050046import android.util.StatsLog;
Mady Mellordea7ecf2018-12-10 15:47:40 -080047import android.view.View;
Mady Mellor3dff9e62019-02-05 18:12:53 -080048import android.view.WindowInsets;
Mady Mellora96c9ed2019-06-07 12:55:26 -070049import android.view.WindowManager;
Mady Mellordea7ecf2018-12-10 15:47:40 -080050import android.widget.LinearLayout;
51
Mark Renouf34d04f32019-05-13 15:53:18 -040052import com.android.internal.policy.ScreenDecorationsUtils;
Mady Mellor3dff9e62019-02-05 18:12:53 -080053import com.android.systemui.Dependency;
Mady Mellordea7ecf2018-12-10 15:47:40 -080054import com.android.systemui.R;
55import com.android.systemui.recents.TriangleShape;
Lyn Han754e77b2019-04-30 14:34:49 -070056import com.android.systemui.statusbar.AlphaOptimizedButton;
Mady Mellordea7ecf2018-12-10 15:47:40 -080057
58/**
Lyn Han02cca812019-04-02 16:27:32 -070059 * Container for the expanded bubble view, handles rendering the caret and settings icon.
Mady Mellordea7ecf2018-12-10 15:47:40 -080060 */
Mady Mellor3d82e682019-02-05 13:34:48 -080061public class BubbleExpandedView extends LinearLayout implements View.OnClickListener {
Issei Suzukia8d07312019-06-07 12:56:19 +020062 private static final String TAG = TAG_WITH_CLASS_NAME ? "BubbleExpandedView" : TAG_BUBBLES;
Mady Mellordea7ecf2018-12-10 15:47:40 -080063
Issei Suzuki734bc942019-06-05 13:59:52 +020064 private enum ActivityViewStatus {
65 // ActivityView is being initialized, cannot start an activity yet.
66 INITIALIZING,
67 // ActivityView is initialized, and ready to start an activity.
68 INITIALIZED,
69 // Activity runs in the ActivityView.
70 ACTIVITY_STARTED,
71 // ActivityView is released, so activity launching will no longer be permitted.
72 RELEASED,
73 }
Mady Mellordea7ecf2018-12-10 15:47:40 -080074
75 // The triangle pointing to the expanded view
76 private View mPointerView;
Mady Mellor44ee2fe2019-01-30 17:51:16 -080077 private int mPointerMargin;
Mady Mellore8e07712019-01-23 12:45:33 -080078
Lyn Han754e77b2019-04-30 14:34:49 -070079 private AlphaOptimizedButton mSettingsIcon;
Mady Mellore8e07712019-01-23 12:45:33 -080080
Mady Mellor3dff9e62019-02-05 18:12:53 -080081 // Views for expanded state
Mady Mellor3dff9e62019-02-05 18:12:53 -080082 private ActivityView mActivityView;
83
Issei Suzuki734bc942019-06-05 13:59:52 +020084 private ActivityViewStatus mActivityViewStatus = ActivityViewStatus.INITIALIZING;
Mark Renouf5c732b32019-06-12 15:14:54 -040085 private int mTaskId = -1;
86
Mady Mellor3dff9e62019-02-05 18:12:53 -080087 private PendingIntent mBubbleIntent;
88
Mady Mellor5d8f1402019-02-21 18:23:52 -080089 private boolean mKeyboardVisible;
90 private boolean mNeedsNewHeight;
91
Mady Mellora96c9ed2019-06-07 12:55:26 -070092 private Point mDisplaySize;
Mady Mellorfe7ec032019-01-30 17:32:49 -080093 private int mMinHeight;
Lyn Han02cca812019-04-02 16:27:32 -070094 private int mSettingsIconHeight;
Lyn Han02cca812019-04-02 16:27:32 -070095 private int mPointerWidth;
96 private int mPointerHeight;
Mark Renouf34d04f32019-05-13 15:53:18 -040097 private ShapeDrawable mPointerDrawable;
Mady Mellor47b11e32019-07-11 19:06:21 -070098 private Rect mTempRect = new Rect();
99 private int[] mTempLoc = new int[2];
100 private int mExpandedViewTouchSlop;
Mady Mellordea7ecf2018-12-10 15:47:40 -0800101
Mady Mellor99a302602019-06-14 11:39:56 -0700102 private Bubble mBubble;
Mady Mellor9801e852019-01-22 14:50:28 -0800103 private PackageManager mPm;
104 private String mAppName;
Mady Mellore8e07712019-01-23 12:45:33 -0800105 private Drawable mAppIcon;
106
Mady Mellor3dff9e62019-02-05 18:12:53 -0800107 private BubbleController mBubbleController = Dependency.get(BubbleController.class);
Mady Mellor9be3bed2019-08-21 17:26:26 -0700108 private WindowManager mWindowManager;
Mady Mellor9801e852019-01-22 14:50:28 -0800109
Mady Mellor9801e852019-01-22 14:50:28 -0800110 private BubbleStackView mStackView;
111
Mady Mellor3dff9e62019-02-05 18:12:53 -0800112 private ActivityView.StateCallback mStateCallback = new ActivityView.StateCallback() {
113 @Override
114 public void onActivityViewReady(ActivityView view) {
Mark Renoufdb861c92019-07-26 13:58:17 -0400115 if (DEBUG_BUBBLE_EXPANDED_VIEW) {
116 Log.d(TAG, "onActivityViewReady: mActivityViewStatus=" + mActivityViewStatus
117 + " bubble=" + getBubbleKey());
118 }
Issei Suzuki734bc942019-06-05 13:59:52 +0200119 switch (mActivityViewStatus) {
120 case INITIALIZING:
121 case INITIALIZED:
122 // Custom options so there is no activity transition animation
123 ActivityOptions options = ActivityOptions.makeCustomAnimation(getContext(),
124 0 /* enterResId */, 0 /* exitResId */);
125 // Post to keep the lifecycle normal
Mark Renoufdb861c92019-07-26 13:58:17 -0400126 post(() -> {
127 if (DEBUG_BUBBLE_EXPANDED_VIEW) {
128 Log.d(TAG, "onActivityViewReady: calling startActivity, "
129 + "bubble=" + getBubbleKey());
130 }
Mady Mellor8454ddf2019-08-15 11:16:23 -0700131 try {
132 mActivityView.startActivity(mBubbleIntent, options);
133 } catch (RuntimeException e) {
134 // If there's a runtime exception here then there's something
135 // wrong with the intent, we can't really recover / try to populate
136 // the bubble again so we'll just remove it.
137 Log.w(TAG, "Exception while displaying bubble: " + getBubbleKey()
138 + ", " + e.getMessage() + "; removing bubble");
139 mBubbleController.removeBubble(mBubble.getKey(),
140 BubbleController.DISMISS_INVALID_INTENT);
141 }
Mark Renoufdb861c92019-07-26 13:58:17 -0400142 });
Issei Suzuki734bc942019-06-05 13:59:52 +0200143 mActivityViewStatus = ActivityViewStatus.ACTIVITY_STARTED;
Mady Mellor6d002032019-02-13 13:45:17 -0800144 }
Mady Mellor3dff9e62019-02-05 18:12:53 -0800145 }
146
147 @Override
148 public void onActivityViewDestroyed(ActivityView view) {
Mark Renoufdb861c92019-07-26 13:58:17 -0400149 if (DEBUG_BUBBLE_EXPANDED_VIEW) {
150 Log.d(TAG, "onActivityViewDestroyed: mActivityViewStatus=" + mActivityViewStatus
151 + " bubble=" + getBubbleKey());
152 }
Issei Suzuki734bc942019-06-05 13:59:52 +0200153 mActivityViewStatus = ActivityViewStatus.RELEASED;
Mady Mellor3dff9e62019-02-05 18:12:53 -0800154 }
155
Mark Renouf5c732b32019-06-12 15:14:54 -0400156 @Override
157 public void onTaskCreated(int taskId, ComponentName componentName) {
Mark Renoufdb861c92019-07-26 13:58:17 -0400158 if (DEBUG_BUBBLE_EXPANDED_VIEW) {
159 Log.d(TAG, "onTaskCreated: taskId=" + taskId
160 + " bubble=" + getBubbleKey());
161 }
Mark Renouf5c732b32019-06-12 15:14:54 -0400162 // Since Bubble ActivityView applies singleTaskDisplay this is
163 // guaranteed to only be called once per ActivityView. The taskId is
164 // saved to use for removeTask, preventing appearance in recent tasks.
165 mTaskId = taskId;
Mady Mellor3dff9e62019-02-05 18:12:53 -0800166 }
167
168 /**
169 * This is only called for tasks on this ActivityView, which is also set to
170 * single-task mode -- meaning never more than one task on this display. If a task
171 * is being removed, it's the top Activity finishing and this bubble should
172 * be removed or collapsed.
173 */
174 @Override
175 public void onTaskRemovalStarted(int taskId) {
Mark Renoufdb861c92019-07-26 13:58:17 -0400176 if (DEBUG_BUBBLE_EXPANDED_VIEW) {
177 Log.d(TAG, "onTaskRemovalStarted: taskId=" + taskId
178 + " mActivityViewStatus=" + mActivityViewStatus
179 + " bubble=" + getBubbleKey());
180 }
Mady Mellor99a302602019-06-14 11:39:56 -0700181 if (mBubble != null) {
Mady Mellor3dff9e62019-02-05 18:12:53 -0800182 // Must post because this is called from a binder thread.
Mady Mellor99a302602019-06-14 11:39:56 -0700183 post(() -> mBubbleController.removeBubble(mBubble.getKey(),
Mark Renouf08bc42a2019-03-07 13:01:59 -0500184 BubbleController.DISMISS_TASK_FINISHED));
Mady Mellor3dff9e62019-02-05 18:12:53 -0800185 }
186 }
187 };
Mady Mellore8e07712019-01-23 12:45:33 -0800188
Mady Mellor3d82e682019-02-05 13:34:48 -0800189 public BubbleExpandedView(Context context) {
Mady Mellordea7ecf2018-12-10 15:47:40 -0800190 this(context, null);
191 }
192
Mady Mellor3d82e682019-02-05 13:34:48 -0800193 public BubbleExpandedView(Context context, AttributeSet attrs) {
Mady Mellordea7ecf2018-12-10 15:47:40 -0800194 this(context, attrs, 0);
195 }
196
Mady Mellor3d82e682019-02-05 13:34:48 -0800197 public BubbleExpandedView(Context context, AttributeSet attrs, int defStyleAttr) {
Mady Mellordea7ecf2018-12-10 15:47:40 -0800198 this(context, attrs, defStyleAttr, 0);
199 }
200
Mady Mellor3d82e682019-02-05 13:34:48 -0800201 public BubbleExpandedView(Context context, AttributeSet attrs, int defStyleAttr,
Mady Mellordea7ecf2018-12-10 15:47:40 -0800202 int defStyleRes) {
203 super(context, attrs, defStyleAttr, defStyleRes);
Mady Mellor9801e852019-01-22 14:50:28 -0800204 mPm = context.getPackageManager();
Mady Mellora96c9ed2019-06-07 12:55:26 -0700205 mDisplaySize = new Point();
Mady Mellor9be3bed2019-08-21 17:26:26 -0700206 mWindowManager = (WindowManager) context.getSystemService(Context.WINDOW_SERVICE);
Mady Mellor8fe411d2019-08-16 11:27:53 -0700207 // Get the real size -- this includes screen decorations (notches, statusbar, navbar).
Mady Mellor9be3bed2019-08-21 17:26:26 -0700208 mWindowManager.getDefaultDisplay().getRealSize(mDisplaySize);
Mady Mellor47b11e32019-07-11 19:06:21 -0700209 Resources res = getResources();
210 mMinHeight = res.getDimensionPixelSize(R.dimen.bubble_expanded_default_height);
211 mPointerMargin = res.getDimensionPixelSize(R.dimen.bubble_pointer_margin);
212 mExpandedViewTouchSlop = res.getDimensionPixelSize(R.dimen.bubble_expanded_view_slop);
Mady Mellordea7ecf2018-12-10 15:47:40 -0800213 }
214
215 @Override
216 protected void onFinishInflate() {
217 super.onFinishInflate();
Mark Renoufdb861c92019-07-26 13:58:17 -0400218 if (DEBUG_BUBBLE_EXPANDED_VIEW) {
219 Log.d(TAG, "onFinishInflate: bubble=" + getBubbleKey());
220 }
Mady Mellordea7ecf2018-12-10 15:47:40 -0800221
222 Resources res = getResources();
223 mPointerView = findViewById(R.id.pointer_view);
Lyn Han02cca812019-04-02 16:27:32 -0700224 mPointerWidth = res.getDimensionPixelSize(R.dimen.bubble_pointer_width);
225 mPointerHeight = res.getDimensionPixelSize(R.dimen.bubble_pointer_height);
Mady Mellordd497052019-01-30 17:23:48 -0800226
Mady Mellordd497052019-01-30 17:23:48 -0800227
Mark Renouf34d04f32019-05-13 15:53:18 -0400228 mPointerDrawable = new ShapeDrawable(TriangleShape.create(
Lyn Han5aa27e22019-05-15 10:55:07 -0700229 mPointerWidth, mPointerHeight, true /* pointUp */));
Mark Renouf34d04f32019-05-13 15:53:18 -0400230 mPointerView.setBackground(mPointerDrawable);
Mady Mellora96c9ed2019-06-07 12:55:26 -0700231 mPointerView.setVisibility(INVISIBLE);
Mady Mellor9801e852019-01-22 14:50:28 -0800232
Lyn Han02cca812019-04-02 16:27:32 -0700233 mSettingsIconHeight = getContext().getResources().getDimensionPixelSize(
Mady Mellor818eef02019-08-16 16:12:29 -0700234 R.dimen.bubble_settings_size);
Lyn Han02cca812019-04-02 16:27:32 -0700235 mSettingsIcon = findViewById(R.id.settings_button);
Lyn Han02cca812019-04-02 16:27:32 -0700236 mSettingsIcon.setOnClickListener(this);
Lyn Han02cca812019-04-02 16:27:32 -0700237
Mady Mellor3dff9e62019-02-05 18:12:53 -0800238 mActivityView = new ActivityView(mContext, null /* attrs */, 0 /* defStyle */,
239 true /* singleTaskInstance */);
lumarkdc9b3192019-07-23 21:18:17 +0800240 // Set ActivityView's alpha value as zero, since there is no view content to be shown.
Issei Suzukicac2a502019-04-16 16:52:50 +0200241 setContentVisibility(false);
Mady Mellor3dff9e62019-02-05 18:12:53 -0800242 addView(mActivityView);
243
Lyn Han5aa27e22019-05-15 10:55:07 -0700244 // Expanded stack layout, top to bottom:
245 // Expanded view container
246 // ==> bubble row
247 // ==> expanded view
248 // ==> activity view
249 // ==> manage button
250 bringChildToFront(mActivityView);
251 bringChildToFront(mSettingsIcon);
Mady Mellor52b1ac62019-04-10 16:59:03 -0700252
Mark Renouf34d04f32019-05-13 15:53:18 -0400253 applyThemeAttrs();
254
Mady Mellor5d8f1402019-02-21 18:23:52 -0800255 setOnApplyWindowInsetsListener((View view, WindowInsets insets) -> {
256 // Keep track of IME displaying because we should not make any adjustments that might
257 // cause a config change while the IME is displayed otherwise it'll loose focus.
Mady Mellor3dff9e62019-02-05 18:12:53 -0800258 final int keyboardHeight = insets.getSystemWindowInsetBottom()
259 - insets.getStableInsetBottom();
Mady Mellor5d8f1402019-02-21 18:23:52 -0800260 mKeyboardVisible = keyboardHeight != 0;
261 if (!mKeyboardVisible && mNeedsNewHeight) {
262 updateHeight();
263 }
Mady Mellor3dff9e62019-02-05 18:12:53 -0800264 return view.onApplyWindowInsets(insets);
265 });
Mady Mellore8e07712019-01-23 12:45:33 -0800266 }
267
Mark Renoufdb861c92019-07-26 13:58:17 -0400268 private String getBubbleKey() {
269 return mBubble != null ? mBubble.getKey() : "null";
270 }
271
Mark Renouf34d04f32019-05-13 15:53:18 -0400272 void applyThemeAttrs() {
273 TypedArray ta = getContext().obtainStyledAttributes(R.styleable.BubbleExpandedView);
274 int bgColor = ta.getColor(
275 R.styleable.BubbleExpandedView_android_colorBackgroundFloating, Color.WHITE);
276 float cornerRadius = ta.getDimension(
277 R.styleable.BubbleExpandedView_android_dialogCornerRadius, 0);
278 ta.recycle();
279
280 // Update triangle color.
281 mPointerDrawable.setTint(bgColor);
282
283 // Update ActivityView cornerRadius
284 if (ScreenDecorationsUtils.supportsRoundedCornersOnWindows(mContext.getResources())) {
285 mActivityView.setCornerRadius(cornerRadius);
286 }
287 }
288
Mady Mellor5d8f1402019-02-21 18:23:52 -0800289 @Override
290 protected void onDetachedFromWindow() {
291 super.onDetachedFromWindow();
292 mKeyboardVisible = false;
293 mNeedsNewHeight = false;
294 if (mActivityView != null) {
295 mActivityView.setForwardedInsets(Insets.of(0, 0, 0, 0));
296 }
Mark Renoufdb861c92019-07-26 13:58:17 -0400297 if (DEBUG_BUBBLE_EXPANDED_VIEW) {
298 Log.d(TAG, "onDetachedFromWindow: bubble=" + getBubbleKey());
299 }
Mady Mellor5d8f1402019-02-21 18:23:52 -0800300 }
301
302 /**
Issei Suzukicac2a502019-04-16 16:52:50 +0200303 * Set visibility of contents in the expanded state.
304 *
305 * @param visibility {@code true} if the contents should be visible on the screen.
306 *
307 * Note that this contents visibility doesn't affect visibility at {@link android.view.View},
308 * and setting {@code false} actually means rendering the contents in transparent.
309 */
310 void setContentVisibility(boolean visibility) {
Mark Renoufdb861c92019-07-26 13:58:17 -0400311 if (DEBUG_BUBBLE_EXPANDED_VIEW) {
312 Log.d(TAG, "setContentVisibility: visibility=" + visibility
313 + " bubble=" + getBubbleKey());
314 }
Issei Suzukicac2a502019-04-16 16:52:50 +0200315 final float alpha = visibility ? 1f : 0f;
316 mPointerView.setAlpha(alpha);
317 if (mActivityView != null) {
318 mActivityView.setAlpha(alpha);
319 }
320 }
321
322 /**
Mady Mellor5d8f1402019-02-21 18:23:52 -0800323 * Called by {@link BubbleStackView} when the insets for the expanded state should be updated.
324 * This should be done post-move and post-animation.
325 */
326 void updateInsets(WindowInsets insets) {
327 if (usingActivityView()) {
Mady Mellor8fe411d2019-08-16 11:27:53 -0700328 int[] screenLoc = mActivityView.getLocationOnScreen();
329 final int activityViewBottom = screenLoc[1] + mActivityView.getHeight();
Mady Mellor9be3bed2019-08-21 17:26:26 -0700330 final int keyboardTop = mDisplaySize.y - Math.max(insets.getSystemWindowInsetBottom(),
331 insets.getDisplayCutout() != null
332 ? insets.getDisplayCutout().getSafeInsetBottom()
333 : 0);
Mady Mellor8fe411d2019-08-16 11:27:53 -0700334 final int insetsBottom = Math.max(activityViewBottom - keyboardTop, 0);
Mady Mellor5d8f1402019-02-21 18:23:52 -0800335 mActivityView.setForwardedInsets(Insets.of(0, 0, 0, insetsBottom));
336 }
337 }
338
Mady Mellore8e07712019-01-23 12:45:33 -0800339 /**
Mady Mellor99a302602019-06-14 11:39:56 -0700340 * Sets the bubble used to populate this view.
Mady Mellore8e07712019-01-23 12:45:33 -0800341 */
Mady Mellor99a302602019-06-14 11:39:56 -0700342 public void setBubble(Bubble bubble, BubbleStackView stackView, String appName) {
Mark Renoufdb861c92019-07-26 13:58:17 -0400343 if (DEBUG_BUBBLE_EXPANDED_VIEW) {
344 Log.d(TAG, "setBubble: bubble=" + (bubble != null ? bubble.getKey() : "null"));
345 }
Mady Mellor9801e852019-01-22 14:50:28 -0800346
Mady Mellor9801e852019-01-22 14:50:28 -0800347 mStackView = stackView;
Mady Mellor99a302602019-06-14 11:39:56 -0700348 mBubble = bubble;
Lyn Han6c40fe72019-05-08 14:06:33 -0700349 mAppName = appName;
Mady Mellor9801e852019-01-22 14:50:28 -0800350
Mady Mellor9801e852019-01-22 14:50:28 -0800351 try {
Issei Suzukia91f3962019-06-07 11:48:23 +0200352 ApplicationInfo info = mPm.getApplicationInfo(
Mady Mellor99a302602019-06-14 11:39:56 -0700353 bubble.getPackageName(),
Mady Mellor9801e852019-01-22 14:50:28 -0800354 PackageManager.MATCH_UNINSTALLED_PACKAGES
355 | PackageManager.MATCH_DISABLED_COMPONENTS
356 | PackageManager.MATCH_DIRECT_BOOT_UNAWARE
357 | PackageManager.MATCH_DIRECT_BOOT_AWARE);
Issei Suzukia91f3962019-06-07 11:48:23 +0200358 mAppIcon = mPm.getApplicationIcon(info);
Mady Mellor9801e852019-01-22 14:50:28 -0800359 } catch (PackageManager.NameNotFoundException e) {
Lyn Han6c40fe72019-05-08 14:06:33 -0700360 // Do nothing.
Mady Mellor9801e852019-01-22 14:50:28 -0800361 }
Mady Mellore8e07712019-01-23 12:45:33 -0800362 if (mAppIcon == null) {
363 mAppIcon = mPm.getDefaultActivityIcon();
364 }
Mark Renouf34d04f32019-05-13 15:53:18 -0400365 applyThemeAttrs();
Lyn Han69149122019-04-30 12:03:12 -0700366 showSettingsIcon();
Mady Mellor3dff9e62019-02-05 18:12:53 -0800367 updateExpandedView();
Mady Mellor6d002032019-02-13 13:45:17 -0800368 }
369
370 /**
371 * Lets activity view know it should be shown / populated.
372 */
Mady Mellor5029fa62019-03-05 12:16:21 -0800373 public void populateExpandedView() {
Mark Renoufdb861c92019-07-26 13:58:17 -0400374 if (DEBUG_BUBBLE_EXPANDED_VIEW) {
375 Log.d(TAG, "populateExpandedView: "
376 + "bubble=" + getBubbleKey());
377 }
378
Mady Mellor5029fa62019-03-05 12:16:21 -0800379 if (usingActivityView()) {
380 mActivityView.setCallback(mStateCallback);
381 } else {
Issei Suzukia91f3962019-06-07 11:48:23 +0200382 Log.e(TAG, "Cannot populate expanded view.");
Mady Mellor5029fa62019-03-05 12:16:21 -0800383 }
Mady Mellor9801e852019-01-22 14:50:28 -0800384 }
385
Mady Mellorfe7ec032019-01-30 17:32:49 -0800386 /**
Mady Mellor99a302602019-06-14 11:39:56 -0700387 * Updates the bubble backing this view. This will not re-populate ActivityView, it will
Lyn Han02cca812019-04-02 16:27:32 -0700388 * only update the deep-links in the title, and the height of the view.
Mady Mellorfe7ec032019-01-30 17:32:49 -0800389 */
Mady Mellor99a302602019-06-14 11:39:56 -0700390 public void update(Bubble bubble) {
Mark Renoufdb861c92019-07-26 13:58:17 -0400391 if (DEBUG_BUBBLE_EXPANDED_VIEW) {
392 Log.d(TAG, "update: bubble=" + (bubble != null ? bubble.getKey() : "null"));
393 }
Mady Mellor99a302602019-06-14 11:39:56 -0700394 if (bubble.getKey().equals(mBubble.getKey())) {
395 mBubble = bubble;
Lyn Han02cca812019-04-02 16:27:32 -0700396 updateSettingsContentDescription();
Mady Mellorfe7ec032019-01-30 17:32:49 -0800397 updateHeight();
398 } else {
Mady Mellor99a302602019-06-14 11:39:56 -0700399 Log.w(TAG, "Trying to update entry with different key, new bubble: "
400 + bubble.getKey() + " old bubble: " + bubble.getKey());
Mady Mellorfe7ec032019-01-30 17:32:49 -0800401 }
402 }
403
Mady Mellor3dff9e62019-02-05 18:12:53 -0800404 private void updateExpandedView() {
Mark Renoufdb861c92019-07-26 13:58:17 -0400405 if (DEBUG_BUBBLE_EXPANDED_VIEW) {
406 Log.d(TAG, "updateExpandedView: bubble="
407 + getBubbleKey());
408 }
409
Mady Mellor99a302602019-06-14 11:39:56 -0700410 mBubbleIntent = mBubble.getBubbleIntent(mContext);
Mady Mellor3dff9e62019-02-05 18:12:53 -0800411 if (mBubbleIntent != null) {
Issei Suzukicac2a502019-04-16 16:52:50 +0200412 setContentVisibility(false);
Mady Mellor3dff9e62019-02-05 18:12:53 -0800413 mActivityView.setVisibility(VISIBLE);
Mady Mellor3dff9e62019-02-05 18:12:53 -0800414 }
415 updateView();
416 }
417
Mark Renouf041d7262019-02-06 12:09:41 -0500418 boolean performBackPressIfNeeded() {
Mady Mellor323fb0b2019-03-25 12:15:22 -0700419 if (!usingActivityView()) {
Mark Renouf041d7262019-02-06 12:09:41 -0500420 return false;
421 }
422 mActivityView.performBackPress();
423 return true;
424 }
425
Mady Mellorfe7ec032019-01-30 17:32:49 -0800426 void updateHeight() {
Mark Renoufdb861c92019-07-26 13:58:17 -0400427 if (DEBUG_BUBBLE_EXPANDED_VIEW) {
428 Log.d(TAG, "updateHeight: bubble=" + getBubbleKey());
429 }
Mady Mellorfe7ec032019-01-30 17:32:49 -0800430 if (usingActivityView()) {
Mady Mellor99a302602019-06-14 11:39:56 -0700431 float desiredHeight = Math.max(mBubble.getDesiredHeight(mContext), mMinHeight);
Mady Mellor8fe411d2019-08-16 11:27:53 -0700432 float height = Math.min(desiredHeight, getMaxExpandedHeight());
Mady Mellorfe7ec032019-01-30 17:32:49 -0800433 height = Math.max(height, mMinHeight);
434 LayoutParams lp = (LayoutParams) mActivityView.getLayoutParams();
Mady Mellor5d8f1402019-02-21 18:23:52 -0800435 mNeedsNewHeight = lp.height != height;
436 if (!mKeyboardVisible) {
437 // If the keyboard is visible... don't adjust the height because that will cause
438 // a configuration change and the keyboard will be lost.
Mady Mellor7af771a2019-03-07 15:04:54 -0800439 lp.height = (int) height;
Mady Mellor5d8f1402019-02-21 18:23:52 -0800440 mActivityView.setLayoutParams(lp);
441 mNeedsNewHeight = false;
442 }
Mark Renoufdb861c92019-07-26 13:58:17 -0400443 if (DEBUG_BUBBLE_EXPANDED_VIEW) {
444 Log.d(TAG, "updateHeight: bubble=" + getBubbleKey() + " height=" + height
445 + " mNeedsNewHeight=" + mNeedsNewHeight);
446 }
Mady Mellorfe7ec032019-01-30 17:32:49 -0800447 }
448 }
449
Mady Mellora96c9ed2019-06-07 12:55:26 -0700450 private int getMaxExpandedHeight() {
Mady Mellor9be3bed2019-08-21 17:26:26 -0700451 mWindowManager.getDefaultDisplay().getRealSize(mDisplaySize);
Mady Mellora96c9ed2019-06-07 12:55:26 -0700452 int[] windowLocation = mActivityView.getLocationOnScreen();
Mady Mellor8fe411d2019-08-16 11:27:53 -0700453 int bottomInset = getRootWindowInsets() != null
454 ? getRootWindowInsets().getStableInsetBottom()
455 : 0;
456 return mDisplaySize.y - windowLocation[1] - mSettingsIconHeight - mPointerHeight
457 - mPointerMargin - bottomInset;
Mady Mellora96c9ed2019-06-07 12:55:26 -0700458 }
459
Mady Mellor47b11e32019-07-11 19:06:21 -0700460 /**
461 * Whether the provided x, y values (in raw coordinates) are in a touchable area of the
462 * expanded view.
463 *
464 * The touchable areas are the ActivityView (plus some slop around it) and the manage button.
465 */
466 boolean intersectingTouchableContent(int rawX, int rawY) {
467 mTempRect.setEmpty();
468 if (mActivityView != null) {
469 mTempLoc = mActivityView.getLocationOnScreen();
470 mTempRect.set(mTempLoc[0] - mExpandedViewTouchSlop,
471 mTempLoc[1] - mExpandedViewTouchSlop,
472 mTempLoc[0] + mActivityView.getWidth() + mExpandedViewTouchSlop,
473 mTempLoc[1] + mActivityView.getHeight() + mExpandedViewTouchSlop);
474 }
475 if (mTempRect.contains(rawX, rawY)) {
476 return true;
477 }
478 mTempLoc = mSettingsIcon.getLocationOnScreen();
479 mTempRect.set(mTempLoc[0],
480 mTempLoc[1],
481 mTempLoc[0] + mSettingsIcon.getWidth(),
482 mTempLoc[1] + mSettingsIcon.getHeight());
483 if (mTempRect.contains(rawX, rawY)) {
484 return true;
485 }
486 return false;
487 }
488
Mady Mellor9801e852019-01-22 14:50:28 -0800489 @Override
490 public void onClick(View view) {
Mady Mellor99a302602019-06-14 11:39:56 -0700491 if (mBubble == null) {
Mady Mellor9801e852019-01-22 14:50:28 -0800492 return;
493 }
Mady Mellor9801e852019-01-22 14:50:28 -0800494 int id = view.getId();
Lyn Hanc26ff122019-03-29 16:46:07 -0700495 if (id == R.id.settings_button) {
Mady Mellor99a302602019-06-14 11:39:56 -0700496 Intent intent = mBubble.getSettingsIntent();
Steven Wub00225b2019-02-08 14:27:42 -0500497 mStackView.collapseStack(() -> {
Mady Mellor99a302602019-06-14 11:39:56 -0700498 mContext.startActivityAsUser(intent, mBubble.getEntry().notification.getUser());
499 logBubbleClickEvent(mBubble,
Steven Wub00225b2019-02-08 14:27:42 -0500500 StatsLog.BUBBLE_UICHANGED__ACTION__HEADER_GO_TO_SETTINGS);
501 });
Mady Mellor9801e852019-01-22 14:50:28 -0800502 }
Mady Mellordea7ecf2018-12-10 15:47:40 -0800503 }
504
Lyn Han02cca812019-04-02 16:27:32 -0700505 private void updateSettingsContentDescription() {
506 mSettingsIcon.setContentDescription(getResources().getString(
507 R.string.bubbles_settings_button_description, mAppName));
508 }
509
Lyn Hanc26ff122019-03-29 16:46:07 -0700510 void showSettingsIcon() {
Lyn Han02cca812019-04-02 16:27:32 -0700511 updateSettingsContentDescription();
Lyn Hanc26ff122019-03-29 16:46:07 -0700512 mSettingsIcon.setVisibility(VISIBLE);
513 }
514
Mady Mellordea7ecf2018-12-10 15:47:40 -0800515 /**
Mady Mellor3dff9e62019-02-05 18:12:53 -0800516 * Update appearance of the expanded view being displayed.
517 */
518 public void updateView() {
Mark Renoufdb861c92019-07-26 13:58:17 -0400519 if (DEBUG_BUBBLE_EXPANDED_VIEW) {
520 Log.d(TAG, "updateView: bubble="
521 + getBubbleKey());
522 }
Mady Mellor3dff9e62019-02-05 18:12:53 -0800523 if (usingActivityView()
524 && mActivityView.getVisibility() == VISIBLE
525 && mActivityView.isAttachedToWindow()) {
526 mActivityView.onLocationChanged();
Mady Mellor3dff9e62019-02-05 18:12:53 -0800527 }
Mady Mellorfe7ec032019-01-30 17:32:49 -0800528 updateHeight();
Mady Mellor3dff9e62019-02-05 18:12:53 -0800529 }
530
531 /**
Mady Mellordea7ecf2018-12-10 15:47:40 -0800532 * Set the x position that the tip of the triangle should point to.
533 */
Mady Mellor3dff9e62019-02-05 18:12:53 -0800534 public void setPointerPosition(float x) {
Lyn Han9a2f5cf2019-05-23 11:01:41 -0700535 float halfPointerWidth = mPointerWidth / 2f;
536 float pointerLeft = x - halfPointerWidth;
537 mPointerView.setTranslationX(pointerLeft);
Lyn Hanf74ba672019-05-20 16:08:48 -0700538 mPointerView.setVisibility(VISIBLE);
Mady Mellordea7ecf2018-12-10 15:47:40 -0800539 }
540
541 /**
Mady Mellor3dff9e62019-02-05 18:12:53 -0800542 * Removes and releases an ActivityView if one was previously created for this bubble.
Mady Mellordea7ecf2018-12-10 15:47:40 -0800543 */
Mady Mellor94d94a72019-03-05 18:16:59 -0800544 public void cleanUpExpandedState() {
Mark Renoufdb861c92019-07-26 13:58:17 -0400545 if (DEBUG_BUBBLE_EXPANDED_VIEW) {
546 Log.d(TAG, "cleanUpExpandedState: mActivityViewStatus=" + mActivityViewStatus
547 + ", bubble=" + getBubbleKey());
548 }
Mady Mellor3dff9e62019-02-05 18:12:53 -0800549 if (mActivityView == null) {
Mark Renouf89b1a4a2018-12-04 14:59:45 -0500550 return;
551 }
Issei Suzuki734bc942019-06-05 13:59:52 +0200552 switch (mActivityViewStatus) {
553 case INITIALIZED:
554 case ACTIVITY_STARTED:
555 mActivityView.release();
Mady Mellordea7ecf2018-12-10 15:47:40 -0800556 }
Mark Renouf5c732b32019-06-12 15:14:54 -0400557 if (mTaskId != -1) {
558 try {
559 ActivityTaskManager.getService().removeTask(mTaskId);
560 } catch (RemoteException e) {
561 Log.w(TAG, "Failed to remove taskId " + mTaskId);
562 }
563 mTaskId = -1;
Mady Mellordea7ecf2018-12-10 15:47:40 -0800564 }
Mark Renouf28c250d2019-02-25 16:47:34 -0500565 removeView(mActivityView);
Mark Renouf5c732b32019-06-12 15:14:54 -0400566
Mady Mellor3dff9e62019-02-05 18:12:53 -0800567 mActivityView = null;
Issei Suzuki734bc942019-06-05 13:59:52 +0200568 }
569
570 /**
571 * Called when the last task is removed from a {@link android.hardware.display.VirtualDisplay}
572 * which {@link ActivityView} uses.
573 */
574 void notifyDisplayEmpty() {
Mark Renoufdb861c92019-07-26 13:58:17 -0400575 if (DEBUG_BUBBLE_EXPANDED_VIEW) {
576 Log.d(TAG, "notifyDisplayEmpty: bubble="
577 + getBubbleKey()
578 + " mActivityViewStatus=" + mActivityViewStatus);
579 }
Issei Suzuki734bc942019-06-05 13:59:52 +0200580 if (mActivityViewStatus == ActivityViewStatus.ACTIVITY_STARTED) {
581 mActivityViewStatus = ActivityViewStatus.INITIALIZED;
582 }
Mady Mellordea7ecf2018-12-10 15:47:40 -0800583 }
584
Mady Mellor3dff9e62019-02-05 18:12:53 -0800585 private boolean usingActivityView() {
Mady Mellor323fb0b2019-03-25 12:15:22 -0700586 return mBubbleIntent != null && mActivityView != null;
Mady Mellor3dff9e62019-02-05 18:12:53 -0800587 }
588
Mady Mellor390bff42019-04-05 15:09:01 -0700589 /**
590 * @return the display id of the virtual display.
591 */
592 public int getVirtualDisplayId() {
593 if (usingActivityView()) {
594 return mActivityView.getVirtualDisplayId();
595 }
596 return INVALID_DISPLAY;
597 }
598
Steven Wub00225b2019-02-08 14:27:42 -0500599 /**
600 * Logs bubble UI click event.
601 *
Mady Mellor99a302602019-06-14 11:39:56 -0700602 * @param bubble the bubble notification entry that user is interacting with.
Steven Wub00225b2019-02-08 14:27:42 -0500603 * @param action the user interaction enum.
604 */
Mady Mellor99a302602019-06-14 11:39:56 -0700605 private void logBubbleClickEvent(Bubble bubble, int action) {
606 StatusBarNotification notification = bubble.getEntry().notification;
Steven Wub00225b2019-02-08 14:27:42 -0500607 StatsLog.write(StatsLog.BUBBLE_UI_CHANGED,
608 notification.getPackageName(),
609 notification.getNotification().getChannelId(),
610 notification.getId(),
611 mStackView.getBubbleIndex(mStackView.getExpandedBubble()),
612 mStackView.getBubbleCount(),
613 action,
614 mStackView.getNormalizedXPosition(),
Steven Wu45e38ae2019-03-25 16:16:59 -0400615 mStackView.getNormalizedYPosition(),
Mady Mellor99a302602019-06-14 11:39:56 -0700616 bubble.showInShadeWhenBubble(),
617 bubble.isOngoing(),
Mady Mellor6cec3502019-06-17 19:20:49 -0700618 false /* isAppForeground (unused) */);
Mady Mellor7af771a2019-03-07 15:04:54 -0800619 }
Mady Mellordea7ecf2018-12-10 15:47:40 -0800620}