blob: 6f35525475a65ac738ad6496e983ca6bb76627b3 [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 }
74
Mady Mellordea7ecf2018-12-10 15:47:40 -080075 // 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 Mellor9801e852019-01-22 14:50:28 -0800108
Mady Mellor9801e852019-01-22 14:50:28 -0800109 private BubbleStackView mStackView;
110
Mady Mellor3dff9e62019-02-05 18:12:53 -0800111 private ActivityView.StateCallback mStateCallback = new ActivityView.StateCallback() {
112 @Override
113 public void onActivityViewReady(ActivityView view) {
Mark Renoufdb861c92019-07-26 13:58:17 -0400114 if (DEBUG_BUBBLE_EXPANDED_VIEW) {
115 Log.d(TAG, "onActivityViewReady: mActivityViewStatus=" + mActivityViewStatus
116 + " bubble=" + getBubbleKey());
117 }
Issei Suzuki734bc942019-06-05 13:59:52 +0200118 switch (mActivityViewStatus) {
119 case INITIALIZING:
120 case INITIALIZED:
121 // Custom options so there is no activity transition animation
122 ActivityOptions options = ActivityOptions.makeCustomAnimation(getContext(),
123 0 /* enterResId */, 0 /* exitResId */);
124 // Post to keep the lifecycle normal
Mark Renoufdb861c92019-07-26 13:58:17 -0400125 post(() -> {
126 if (DEBUG_BUBBLE_EXPANDED_VIEW) {
127 Log.d(TAG, "onActivityViewReady: calling startActivity, "
128 + "bubble=" + getBubbleKey());
129 }
Mady Mellor8454ddf2019-08-15 11:16:23 -0700130 try {
131 mActivityView.startActivity(mBubbleIntent, options);
132 } catch (RuntimeException e) {
133 // If there's a runtime exception here then there's something
134 // wrong with the intent, we can't really recover / try to populate
135 // the bubble again so we'll just remove it.
136 Log.w(TAG, "Exception while displaying bubble: " + getBubbleKey()
137 + ", " + e.getMessage() + "; removing bubble");
138 mBubbleController.removeBubble(mBubble.getKey(),
139 BubbleController.DISMISS_INVALID_INTENT);
140 }
Mark Renoufdb861c92019-07-26 13:58:17 -0400141 });
Issei Suzuki734bc942019-06-05 13:59:52 +0200142 mActivityViewStatus = ActivityViewStatus.ACTIVITY_STARTED;
Mady Mellor6d002032019-02-13 13:45:17 -0800143 }
Mady Mellor3dff9e62019-02-05 18:12:53 -0800144 }
145
146 @Override
147 public void onActivityViewDestroyed(ActivityView view) {
Mark Renoufdb861c92019-07-26 13:58:17 -0400148 if (DEBUG_BUBBLE_EXPANDED_VIEW) {
149 Log.d(TAG, "onActivityViewDestroyed: mActivityViewStatus=" + mActivityViewStatus
150 + " bubble=" + getBubbleKey());
151 }
Issei Suzuki734bc942019-06-05 13:59:52 +0200152 mActivityViewStatus = ActivityViewStatus.RELEASED;
Mady Mellor3dff9e62019-02-05 18:12:53 -0800153 }
154
Mark Renouf5c732b32019-06-12 15:14:54 -0400155 @Override
156 public void onTaskCreated(int taskId, ComponentName componentName) {
Mark Renoufdb861c92019-07-26 13:58:17 -0400157 if (DEBUG_BUBBLE_EXPANDED_VIEW) {
158 Log.d(TAG, "onTaskCreated: taskId=" + taskId
159 + " bubble=" + getBubbleKey());
160 }
Mark Renouf5c732b32019-06-12 15:14:54 -0400161 // Since Bubble ActivityView applies singleTaskDisplay this is
162 // guaranteed to only be called once per ActivityView. The taskId is
163 // saved to use for removeTask, preventing appearance in recent tasks.
164 mTaskId = taskId;
165 }
166
Mady Mellor3dff9e62019-02-05 18:12:53 -0800167 /**
168 * This is only called for tasks on this ActivityView, which is also set to
169 * single-task mode -- meaning never more than one task on this display. If a task
170 * is being removed, it's the top Activity finishing and this bubble should
171 * be removed or collapsed.
172 */
173 @Override
174 public void onTaskRemovalStarted(int taskId) {
Mark Renoufdb861c92019-07-26 13:58:17 -0400175 if (DEBUG_BUBBLE_EXPANDED_VIEW) {
176 Log.d(TAG, "onTaskRemovalStarted: taskId=" + taskId
177 + " mActivityViewStatus=" + mActivityViewStatus
178 + " bubble=" + getBubbleKey());
179 }
Mady Mellor99a302602019-06-14 11:39:56 -0700180 if (mBubble != null) {
Mady Mellor3dff9e62019-02-05 18:12:53 -0800181 // Must post because this is called from a binder thread.
Mady Mellor99a302602019-06-14 11:39:56 -0700182 post(() -> mBubbleController.removeBubble(mBubble.getKey(),
Mark Renouf08bc42a2019-03-07 13:01:59 -0500183 BubbleController.DISMISS_TASK_FINISHED));
Mady Mellor3dff9e62019-02-05 18:12:53 -0800184 }
185 }
186 };
Mady Mellore8e07712019-01-23 12:45:33 -0800187
Mady Mellor3d82e682019-02-05 13:34:48 -0800188 public BubbleExpandedView(Context context) {
Mady Mellordea7ecf2018-12-10 15:47:40 -0800189 this(context, null);
190 }
191
Mady Mellor3d82e682019-02-05 13:34:48 -0800192 public BubbleExpandedView(Context context, AttributeSet attrs) {
Mady Mellordea7ecf2018-12-10 15:47:40 -0800193 this(context, attrs, 0);
194 }
195
Mady Mellor3d82e682019-02-05 13:34:48 -0800196 public BubbleExpandedView(Context context, AttributeSet attrs, int defStyleAttr) {
Mady Mellordea7ecf2018-12-10 15:47:40 -0800197 this(context, attrs, defStyleAttr, 0);
198 }
199
Mady Mellor3d82e682019-02-05 13:34:48 -0800200 public BubbleExpandedView(Context context, AttributeSet attrs, int defStyleAttr,
Mady Mellordea7ecf2018-12-10 15:47:40 -0800201 int defStyleRes) {
202 super(context, attrs, defStyleAttr, defStyleRes);
Mady Mellor9801e852019-01-22 14:50:28 -0800203 mPm = context.getPackageManager();
Mady Mellora96c9ed2019-06-07 12:55:26 -0700204 mDisplaySize = new Point();
205 WindowManager wm = (WindowManager) context.getSystemService(Context.WINDOW_SERVICE);
206 wm.getDefaultDisplay().getSize(mDisplaySize);
Mady Mellor47b11e32019-07-11 19:06:21 -0700207 Resources res = getResources();
208 mMinHeight = res.getDimensionPixelSize(R.dimen.bubble_expanded_default_height);
209 mPointerMargin = res.getDimensionPixelSize(R.dimen.bubble_pointer_margin);
210 mExpandedViewTouchSlop = res.getDimensionPixelSize(R.dimen.bubble_expanded_view_slop);
Mady Mellordea7ecf2018-12-10 15:47:40 -0800211 }
212
213 @Override
214 protected void onFinishInflate() {
215 super.onFinishInflate();
Mark Renoufdb861c92019-07-26 13:58:17 -0400216 if (DEBUG_BUBBLE_EXPANDED_VIEW) {
217 Log.d(TAG, "onFinishInflate: bubble=" + getBubbleKey());
218 }
Mady Mellordea7ecf2018-12-10 15:47:40 -0800219
220 Resources res = getResources();
221 mPointerView = findViewById(R.id.pointer_view);
Lyn Han02cca812019-04-02 16:27:32 -0700222 mPointerWidth = res.getDimensionPixelSize(R.dimen.bubble_pointer_width);
223 mPointerHeight = res.getDimensionPixelSize(R.dimen.bubble_pointer_height);
Mady Mellordd497052019-01-30 17:23:48 -0800224
Mady Mellordd497052019-01-30 17:23:48 -0800225
Mark Renouf34d04f32019-05-13 15:53:18 -0400226 mPointerDrawable = new ShapeDrawable(TriangleShape.create(
Lyn Han5aa27e22019-05-15 10:55:07 -0700227 mPointerWidth, mPointerHeight, true /* pointUp */));
Mark Renouf34d04f32019-05-13 15:53:18 -0400228 mPointerView.setBackground(mPointerDrawable);
Mady Mellora96c9ed2019-06-07 12:55:26 -0700229 mPointerView.setVisibility(INVISIBLE);
Mady Mellor9801e852019-01-22 14:50:28 -0800230
Lyn Han02cca812019-04-02 16:27:32 -0700231 mSettingsIconHeight = getContext().getResources().getDimensionPixelSize(
Mady Mellorfe7ec032019-01-30 17:32:49 -0800232 R.dimen.bubble_expanded_header_height);
Lyn Han02cca812019-04-02 16:27:32 -0700233 mSettingsIcon = findViewById(R.id.settings_button);
Lyn Han02cca812019-04-02 16:27:32 -0700234 mSettingsIcon.setOnClickListener(this);
Lyn Han02cca812019-04-02 16:27:32 -0700235
Mady Mellor3dff9e62019-02-05 18:12:53 -0800236 mActivityView = new ActivityView(mContext, null /* attrs */, 0 /* defStyle */,
237 true /* singleTaskInstance */);
Issei Suzukicac2a502019-04-16 16:52:50 +0200238
239 setContentVisibility(false);
Mady Mellor3dff9e62019-02-05 18:12:53 -0800240 addView(mActivityView);
241
Lyn Han5aa27e22019-05-15 10:55:07 -0700242 // Expanded stack layout, top to bottom:
243 // Expanded view container
244 // ==> bubble row
245 // ==> expanded view
246 // ==> activity view
247 // ==> manage button
248 bringChildToFront(mActivityView);
249 bringChildToFront(mSettingsIcon);
Mady Mellor52b1ac62019-04-10 16:59:03 -0700250
Mark Renouf34d04f32019-05-13 15:53:18 -0400251 applyThemeAttrs();
252
Mady Mellor5d8f1402019-02-21 18:23:52 -0800253 setOnApplyWindowInsetsListener((View view, WindowInsets insets) -> {
254 // Keep track of IME displaying because we should not make any adjustments that might
255 // cause a config change while the IME is displayed otherwise it'll loose focus.
Mady Mellor3dff9e62019-02-05 18:12:53 -0800256 final int keyboardHeight = insets.getSystemWindowInsetBottom()
257 - insets.getStableInsetBottom();
Mady Mellor5d8f1402019-02-21 18:23:52 -0800258 mKeyboardVisible = keyboardHeight != 0;
259 if (!mKeyboardVisible && mNeedsNewHeight) {
260 updateHeight();
261 }
Mady Mellor3dff9e62019-02-05 18:12:53 -0800262 return view.onApplyWindowInsets(insets);
263 });
Mady Mellore8e07712019-01-23 12:45:33 -0800264 }
265
Mark Renoufdb861c92019-07-26 13:58:17 -0400266 private String getBubbleKey() {
267 return mBubble != null ? mBubble.getKey() : "null";
268 }
269
Mark Renouf34d04f32019-05-13 15:53:18 -0400270 void applyThemeAttrs() {
271 TypedArray ta = getContext().obtainStyledAttributes(R.styleable.BubbleExpandedView);
272 int bgColor = ta.getColor(
273 R.styleable.BubbleExpandedView_android_colorBackgroundFloating, Color.WHITE);
274 float cornerRadius = ta.getDimension(
275 R.styleable.BubbleExpandedView_android_dialogCornerRadius, 0);
276 ta.recycle();
277
278 // Update triangle color.
279 mPointerDrawable.setTint(bgColor);
280
281 // Update ActivityView cornerRadius
282 if (ScreenDecorationsUtils.supportsRoundedCornersOnWindows(mContext.getResources())) {
283 mActivityView.setCornerRadius(cornerRadius);
284 }
285 }
286
Mady Mellor5d8f1402019-02-21 18:23:52 -0800287 @Override
288 protected void onDetachedFromWindow() {
289 super.onDetachedFromWindow();
290 mKeyboardVisible = false;
291 mNeedsNewHeight = false;
292 if (mActivityView != null) {
293 mActivityView.setForwardedInsets(Insets.of(0, 0, 0, 0));
294 }
Mark Renoufdb861c92019-07-26 13:58:17 -0400295 if (DEBUG_BUBBLE_EXPANDED_VIEW) {
296 Log.d(TAG, "onDetachedFromWindow: bubble=" + getBubbleKey());
297 }
Mady Mellor5d8f1402019-02-21 18:23:52 -0800298 }
299
300 /**
Issei Suzukicac2a502019-04-16 16:52:50 +0200301 * Set visibility of contents in the expanded state.
302 *
303 * @param visibility {@code true} if the contents should be visible on the screen.
304 *
305 * Note that this contents visibility doesn't affect visibility at {@link android.view.View},
306 * and setting {@code false} actually means rendering the contents in transparent.
307 */
308 void setContentVisibility(boolean visibility) {
Mark Renoufdb861c92019-07-26 13:58:17 -0400309 if (DEBUG_BUBBLE_EXPANDED_VIEW) {
310 Log.d(TAG, "setContentVisibility: visibility=" + visibility
311 + " bubble=" + getBubbleKey());
312 }
Issei Suzukicac2a502019-04-16 16:52:50 +0200313 final float alpha = visibility ? 1f : 0f;
314 mPointerView.setAlpha(alpha);
315 if (mActivityView != null) {
316 mActivityView.setAlpha(alpha);
317 }
318 }
319
320 /**
Mady Mellor5d8f1402019-02-21 18:23:52 -0800321 * Called by {@link BubbleStackView} when the insets for the expanded state should be updated.
322 * This should be done post-move and post-animation.
323 */
324 void updateInsets(WindowInsets insets) {
325 if (usingActivityView()) {
Mady Mellor5d8f1402019-02-21 18:23:52 -0800326 int[] windowLocation = mActivityView.getLocationOnScreen();
327 final int windowBottom = windowLocation[1] + mActivityView.getHeight();
328 final int keyboardHeight = insets.getSystemWindowInsetBottom()
329 - insets.getStableInsetBottom();
330 final int insetsBottom = Math.max(0,
Mady Mellora96c9ed2019-06-07 12:55:26 -0700331 windowBottom + keyboardHeight - mDisplaySize.y);
Mady Mellor5d8f1402019-02-21 18:23:52 -0800332 mActivityView.setForwardedInsets(Insets.of(0, 0, 0, insetsBottom));
333 }
334 }
335
Mady Mellore8e07712019-01-23 12:45:33 -0800336 /**
Mady Mellor99a302602019-06-14 11:39:56 -0700337 * Sets the bubble used to populate this view.
Mady Mellor9801e852019-01-22 14:50:28 -0800338 */
Mady Mellor99a302602019-06-14 11:39:56 -0700339 public void setBubble(Bubble bubble, BubbleStackView stackView, String appName) {
Mark Renoufdb861c92019-07-26 13:58:17 -0400340 if (DEBUG_BUBBLE_EXPANDED_VIEW) {
341 Log.d(TAG, "setBubble: bubble=" + (bubble != null ? bubble.getKey() : "null"));
342 }
343
Mady Mellor9801e852019-01-22 14:50:28 -0800344 mStackView = stackView;
Mady Mellor99a302602019-06-14 11:39:56 -0700345 mBubble = bubble;
Lyn Han6c40fe72019-05-08 14:06:33 -0700346 mAppName = appName;
Mady Mellor9801e852019-01-22 14:50:28 -0800347
Mady Mellor9801e852019-01-22 14:50:28 -0800348 try {
Issei Suzukia91f3962019-06-07 11:48:23 +0200349 ApplicationInfo info = mPm.getApplicationInfo(
Mady Mellor99a302602019-06-14 11:39:56 -0700350 bubble.getPackageName(),
Mady Mellor9801e852019-01-22 14:50:28 -0800351 PackageManager.MATCH_UNINSTALLED_PACKAGES
352 | PackageManager.MATCH_DISABLED_COMPONENTS
353 | PackageManager.MATCH_DIRECT_BOOT_UNAWARE
354 | PackageManager.MATCH_DIRECT_BOOT_AWARE);
Issei Suzukia91f3962019-06-07 11:48:23 +0200355 mAppIcon = mPm.getApplicationIcon(info);
Mady Mellor9801e852019-01-22 14:50:28 -0800356 } catch (PackageManager.NameNotFoundException e) {
Lyn Han6c40fe72019-05-08 14:06:33 -0700357 // Do nothing.
Mady Mellor9801e852019-01-22 14:50:28 -0800358 }
Mady Mellore8e07712019-01-23 12:45:33 -0800359 if (mAppIcon == null) {
360 mAppIcon = mPm.getDefaultActivityIcon();
361 }
Mark Renouf34d04f32019-05-13 15:53:18 -0400362 applyThemeAttrs();
Lyn Han69149122019-04-30 12:03:12 -0700363 showSettingsIcon();
Mady Mellor3dff9e62019-02-05 18:12:53 -0800364 updateExpandedView();
Mady Mellor6d002032019-02-13 13:45:17 -0800365 }
366
367 /**
368 * Lets activity view know it should be shown / populated.
369 */
Mady Mellor5029fa62019-03-05 12:16:21 -0800370 public void populateExpandedView() {
Mark Renoufdb861c92019-07-26 13:58:17 -0400371 if (DEBUG_BUBBLE_EXPANDED_VIEW) {
372 Log.d(TAG, "populateExpandedView: "
373 + "bubble=" + getBubbleKey());
374 }
375
Mady Mellor5029fa62019-03-05 12:16:21 -0800376 if (usingActivityView()) {
377 mActivityView.setCallback(mStateCallback);
378 } else {
Issei Suzukia91f3962019-06-07 11:48:23 +0200379 Log.e(TAG, "Cannot populate expanded view.");
Mady Mellor5029fa62019-03-05 12:16:21 -0800380 }
Mady Mellor9801e852019-01-22 14:50:28 -0800381 }
382
Mady Mellorfe7ec032019-01-30 17:32:49 -0800383 /**
Mady Mellor99a302602019-06-14 11:39:56 -0700384 * Updates the bubble backing this view. This will not re-populate ActivityView, it will
Lyn Han02cca812019-04-02 16:27:32 -0700385 * only update the deep-links in the title, and the height of the view.
Mady Mellorfe7ec032019-01-30 17:32:49 -0800386 */
Mady Mellor99a302602019-06-14 11:39:56 -0700387 public void update(Bubble bubble) {
Mark Renoufdb861c92019-07-26 13:58:17 -0400388 if (DEBUG_BUBBLE_EXPANDED_VIEW) {
389 Log.d(TAG, "update: bubble=" + (bubble != null ? bubble.getKey() : "null"));
390 }
Mady Mellor99a302602019-06-14 11:39:56 -0700391 if (bubble.getKey().equals(mBubble.getKey())) {
392 mBubble = bubble;
Lyn Han02cca812019-04-02 16:27:32 -0700393 updateSettingsContentDescription();
Mady Mellorfe7ec032019-01-30 17:32:49 -0800394 updateHeight();
395 } else {
Mady Mellor99a302602019-06-14 11:39:56 -0700396 Log.w(TAG, "Trying to update entry with different key, new bubble: "
397 + bubble.getKey() + " old bubble: " + bubble.getKey());
Mady Mellorfe7ec032019-01-30 17:32:49 -0800398 }
399 }
400
Mady Mellor3dff9e62019-02-05 18:12:53 -0800401 private void updateExpandedView() {
Mark Renoufdb861c92019-07-26 13:58:17 -0400402 if (DEBUG_BUBBLE_EXPANDED_VIEW) {
403 Log.d(TAG, "updateExpandedView: bubble="
404 + getBubbleKey());
405 }
406
Mady Mellor99a302602019-06-14 11:39:56 -0700407 mBubbleIntent = mBubble.getBubbleIntent(mContext);
Mady Mellor3dff9e62019-02-05 18:12:53 -0800408 if (mBubbleIntent != null) {
Issei Suzukicac2a502019-04-16 16:52:50 +0200409 setContentVisibility(false);
Mady Mellor3dff9e62019-02-05 18:12:53 -0800410 mActivityView.setVisibility(VISIBLE);
Mady Mellor3dff9e62019-02-05 18:12:53 -0800411 }
412 updateView();
413 }
414
Mark Renouf041d7262019-02-06 12:09:41 -0500415 boolean performBackPressIfNeeded() {
Mady Mellor323fb0b2019-03-25 12:15:22 -0700416 if (!usingActivityView()) {
Mark Renouf041d7262019-02-06 12:09:41 -0500417 return false;
418 }
419 mActivityView.performBackPress();
420 return true;
421 }
422
Mady Mellorfe7ec032019-01-30 17:32:49 -0800423 void updateHeight() {
Mark Renoufdb861c92019-07-26 13:58:17 -0400424 if (DEBUG_BUBBLE_EXPANDED_VIEW) {
425 Log.d(TAG, "updateHeight: bubble=" + getBubbleKey());
426 }
Mady Mellorfe7ec032019-01-30 17:32:49 -0800427 if (usingActivityView()) {
Mady Mellor99a302602019-06-14 11:39:56 -0700428 int max = getMaxExpandedHeight() - mSettingsIconHeight - mPointerHeight
429 - mPointerMargin;
430 float desiredHeight = Math.max(mBubble.getDesiredHeight(mContext), mMinHeight);
431 float height = Math.min(desiredHeight, max);
Mady Mellorfe7ec032019-01-30 17:32:49 -0800432 height = Math.max(height, mMinHeight);
433 LayoutParams lp = (LayoutParams) mActivityView.getLayoutParams();
Mady Mellor5d8f1402019-02-21 18:23:52 -0800434 mNeedsNewHeight = lp.height != height;
435 if (!mKeyboardVisible) {
436 // If the keyboard is visible... don't adjust the height because that will cause
437 // a configuration change and the keyboard will be lost.
Mady Mellor7af771a2019-03-07 15:04:54 -0800438 lp.height = (int) height;
Mady Mellor5d8f1402019-02-21 18:23:52 -0800439 mActivityView.setLayoutParams(lp);
440 mNeedsNewHeight = false;
441 }
Mark Renoufdb861c92019-07-26 13:58:17 -0400442 if (DEBUG_BUBBLE_EXPANDED_VIEW) {
443 Log.d(TAG, "updateHeight: bubble=" + getBubbleKey() + " height=" + height
444 + " mNeedsNewHeight=" + mNeedsNewHeight);
445 }
Mady Mellorfe7ec032019-01-30 17:32:49 -0800446 }
447 }
448
Mady Mellora96c9ed2019-06-07 12:55:26 -0700449 private int getMaxExpandedHeight() {
450 int[] windowLocation = mActivityView.getLocationOnScreen();
451 return mDisplaySize.y - windowLocation[1] - mSettingsIconHeight;
452 }
453
Mady Mellor47b11e32019-07-11 19:06:21 -0700454 /**
455 * Whether the provided x, y values (in raw coordinates) are in a touchable area of the
456 * expanded view.
457 *
458 * The touchable areas are the ActivityView (plus some slop around it) and the manage button.
459 */
460 boolean intersectingTouchableContent(int rawX, int rawY) {
461 mTempRect.setEmpty();
462 if (mActivityView != null) {
463 mTempLoc = mActivityView.getLocationOnScreen();
464 mTempRect.set(mTempLoc[0] - mExpandedViewTouchSlop,
465 mTempLoc[1] - mExpandedViewTouchSlop,
466 mTempLoc[0] + mActivityView.getWidth() + mExpandedViewTouchSlop,
467 mTempLoc[1] + mActivityView.getHeight() + mExpandedViewTouchSlop);
468 }
469 if (mTempRect.contains(rawX, rawY)) {
470 return true;
471 }
472 mTempLoc = mSettingsIcon.getLocationOnScreen();
473 mTempRect.set(mTempLoc[0],
474 mTempLoc[1],
475 mTempLoc[0] + mSettingsIcon.getWidth(),
476 mTempLoc[1] + mSettingsIcon.getHeight());
477 if (mTempRect.contains(rawX, rawY)) {
478 return true;
479 }
480 return false;
481 }
482
Mady Mellor9801e852019-01-22 14:50:28 -0800483 @Override
484 public void onClick(View view) {
Mady Mellor99a302602019-06-14 11:39:56 -0700485 if (mBubble == null) {
Mady Mellor9801e852019-01-22 14:50:28 -0800486 return;
487 }
Mady Mellor9801e852019-01-22 14:50:28 -0800488 int id = view.getId();
Lyn Hanc26ff122019-03-29 16:46:07 -0700489 if (id == R.id.settings_button) {
Mady Mellor99a302602019-06-14 11:39:56 -0700490 Intent intent = mBubble.getSettingsIntent();
Steven Wub00225b2019-02-08 14:27:42 -0500491 mStackView.collapseStack(() -> {
Mady Mellor99a302602019-06-14 11:39:56 -0700492 mContext.startActivityAsUser(intent, mBubble.getEntry().notification.getUser());
493 logBubbleClickEvent(mBubble,
Steven Wub00225b2019-02-08 14:27:42 -0500494 StatsLog.BUBBLE_UICHANGED__ACTION__HEADER_GO_TO_SETTINGS);
495 });
Mady Mellor9801e852019-01-22 14:50:28 -0800496 }
Mady Mellordea7ecf2018-12-10 15:47:40 -0800497 }
498
Lyn Han02cca812019-04-02 16:27:32 -0700499 private void updateSettingsContentDescription() {
500 mSettingsIcon.setContentDescription(getResources().getString(
501 R.string.bubbles_settings_button_description, mAppName));
502 }
503
Lyn Hanc26ff122019-03-29 16:46:07 -0700504 void showSettingsIcon() {
Lyn Han02cca812019-04-02 16:27:32 -0700505 updateSettingsContentDescription();
Lyn Hanc26ff122019-03-29 16:46:07 -0700506 mSettingsIcon.setVisibility(VISIBLE);
507 }
508
Mady Mellordea7ecf2018-12-10 15:47:40 -0800509 /**
Mady Mellor3dff9e62019-02-05 18:12:53 -0800510 * Update appearance of the expanded view being displayed.
511 */
512 public void updateView() {
Mark Renoufdb861c92019-07-26 13:58:17 -0400513 if (DEBUG_BUBBLE_EXPANDED_VIEW) {
514 Log.d(TAG, "updateView: bubble="
515 + getBubbleKey());
516 }
Mady Mellor3dff9e62019-02-05 18:12:53 -0800517 if (usingActivityView()
518 && mActivityView.getVisibility() == VISIBLE
519 && mActivityView.isAttachedToWindow()) {
520 mActivityView.onLocationChanged();
Mady Mellor3dff9e62019-02-05 18:12:53 -0800521 }
Mady Mellorfe7ec032019-01-30 17:32:49 -0800522 updateHeight();
Mady Mellor3dff9e62019-02-05 18:12:53 -0800523 }
524
525 /**
Mady Mellordea7ecf2018-12-10 15:47:40 -0800526 * Set the x position that the tip of the triangle should point to.
527 */
Mady Mellor3dff9e62019-02-05 18:12:53 -0800528 public void setPointerPosition(float x) {
Lyn Han9a2f5cf2019-05-23 11:01:41 -0700529 float halfPointerWidth = mPointerWidth / 2f;
530 float pointerLeft = x - halfPointerWidth;
531 mPointerView.setTranslationX(pointerLeft);
Lyn Hanf74ba672019-05-20 16:08:48 -0700532 mPointerView.setVisibility(VISIBLE);
Mady Mellordea7ecf2018-12-10 15:47:40 -0800533 }
534
535 /**
Mady Mellor3dff9e62019-02-05 18:12:53 -0800536 * Removes and releases an ActivityView if one was previously created for this bubble.
Mady Mellordea7ecf2018-12-10 15:47:40 -0800537 */
Mady Mellor94d94a72019-03-05 18:16:59 -0800538 public void cleanUpExpandedState() {
Mark Renoufdb861c92019-07-26 13:58:17 -0400539 if (DEBUG_BUBBLE_EXPANDED_VIEW) {
540 Log.d(TAG, "cleanUpExpandedState: mActivityViewStatus=" + mActivityViewStatus
541 + ", bubble=" + getBubbleKey());
542 }
Mady Mellor3dff9e62019-02-05 18:12:53 -0800543 if (mActivityView == null) {
Mark Renouf89b1a4a2018-12-04 14:59:45 -0500544 return;
545 }
Issei Suzuki734bc942019-06-05 13:59:52 +0200546 switch (mActivityViewStatus) {
547 case INITIALIZED:
548 case ACTIVITY_STARTED:
549 mActivityView.release();
Mady Mellordea7ecf2018-12-10 15:47:40 -0800550 }
Mark Renouf5c732b32019-06-12 15:14:54 -0400551 if (mTaskId != -1) {
552 try {
553 ActivityTaskManager.getService().removeTask(mTaskId);
554 } catch (RemoteException e) {
555 Log.w(TAG, "Failed to remove taskId " + mTaskId);
556 }
557 mTaskId = -1;
558 }
Mark Renouf28c250d2019-02-25 16:47:34 -0500559 removeView(mActivityView);
Mark Renouf5c732b32019-06-12 15:14:54 -0400560
Mady Mellor3dff9e62019-02-05 18:12:53 -0800561 mActivityView = null;
Issei Suzuki734bc942019-06-05 13:59:52 +0200562 }
563
564 /**
565 * Called when the last task is removed from a {@link android.hardware.display.VirtualDisplay}
566 * which {@link ActivityView} uses.
567 */
568 void notifyDisplayEmpty() {
Mark Renoufdb861c92019-07-26 13:58:17 -0400569 if (DEBUG_BUBBLE_EXPANDED_VIEW) {
570 Log.d(TAG, "notifyDisplayEmpty: bubble="
571 + getBubbleKey()
572 + " mActivityViewStatus=" + mActivityViewStatus);
573 }
Issei Suzuki734bc942019-06-05 13:59:52 +0200574 if (mActivityViewStatus == ActivityViewStatus.ACTIVITY_STARTED) {
575 mActivityViewStatus = ActivityViewStatus.INITIALIZED;
576 }
Mady Mellordea7ecf2018-12-10 15:47:40 -0800577 }
578
Mady Mellor3dff9e62019-02-05 18:12:53 -0800579 private boolean usingActivityView() {
Mady Mellor323fb0b2019-03-25 12:15:22 -0700580 return mBubbleIntent != null && mActivityView != null;
Mady Mellor3dff9e62019-02-05 18:12:53 -0800581 }
582
Mady Mellor390bff42019-04-05 15:09:01 -0700583 /**
584 * @return the display id of the virtual display.
585 */
586 public int getVirtualDisplayId() {
587 if (usingActivityView()) {
588 return mActivityView.getVirtualDisplayId();
589 }
590 return INVALID_DISPLAY;
591 }
592
Mady Mellor3dff9e62019-02-05 18:12:53 -0800593 /**
Steven Wub00225b2019-02-08 14:27:42 -0500594 * Logs bubble UI click event.
595 *
Mady Mellor99a302602019-06-14 11:39:56 -0700596 * @param bubble the bubble notification entry that user is interacting with.
Steven Wub00225b2019-02-08 14:27:42 -0500597 * @param action the user interaction enum.
598 */
Mady Mellor99a302602019-06-14 11:39:56 -0700599 private void logBubbleClickEvent(Bubble bubble, int action) {
600 StatusBarNotification notification = bubble.getEntry().notification;
Steven Wub00225b2019-02-08 14:27:42 -0500601 StatsLog.write(StatsLog.BUBBLE_UI_CHANGED,
602 notification.getPackageName(),
603 notification.getNotification().getChannelId(),
604 notification.getId(),
605 mStackView.getBubbleIndex(mStackView.getExpandedBubble()),
606 mStackView.getBubbleCount(),
607 action,
608 mStackView.getNormalizedXPosition(),
Steven Wu45e38ae2019-03-25 16:16:59 -0400609 mStackView.getNormalizedYPosition(),
Mady Mellor99a302602019-06-14 11:39:56 -0700610 bubble.showInShadeWhenBubble(),
611 bubble.isOngoing(),
Mady Mellor6cec3502019-06-17 19:20:49 -0700612 false /* isAppForeground (unused) */);
Steven Wub00225b2019-02-08 14:27:42 -0500613 }
Mady Mellordea7ecf2018-12-10 15:47:40 -0800614}