blob: 900b6623e1e4dae4beffa8c0b350dcc2356dd22c [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
Issei Suzukia8d07312019-06-07 12:56:19 +020021import static com.android.systemui.bubbles.BubbleDebugConfig.TAG_BUBBLES;
22import static com.android.systemui.bubbles.BubbleDebugConfig.TAG_WITH_CLASS_NAME;
Mady Mellorca0c24c2019-05-16 16:14:32 -070023
Mady Mellor60101c92019-04-11 19:04:00 -070024import android.app.ActivityOptions;
Mark Renouf5c732b32019-06-12 15:14:54 -040025import android.app.ActivityTaskManager;
Mady Mellor3dff9e62019-02-05 18:12:53 -080026import android.app.ActivityView;
Mady Mellor9801e852019-01-22 14:50:28 -080027import android.app.PendingIntent;
Mark Renouf5c732b32019-06-12 15:14:54 -040028import android.content.ComponentName;
Mady Mellordea7ecf2018-12-10 15:47:40 -080029import android.content.Context;
Mady Mellor9801e852019-01-22 14:50:28 -080030import android.content.Intent;
31import android.content.pm.ApplicationInfo;
32import android.content.pm.PackageManager;
Mady Mellordea7ecf2018-12-10 15:47:40 -080033import android.content.res.Resources;
Mady Mellordd497052019-01-30 17:23:48 -080034import android.content.res.TypedArray;
Mady Mellordea7ecf2018-12-10 15:47:40 -080035import android.graphics.Color;
Mady Mellor3dff9e62019-02-05 18:12:53 -080036import android.graphics.Insets;
37import android.graphics.Point;
Mady Mellor47b11e32019-07-11 19:06:21 -070038import android.graphics.Rect;
Mady Mellore8e07712019-01-23 12:45:33 -080039import android.graphics.drawable.Drawable;
Mady Mellordea7ecf2018-12-10 15:47:40 -080040import android.graphics.drawable.ShapeDrawable;
Mark Renouf5c732b32019-06-12 15:14:54 -040041import android.os.RemoteException;
Steven Wub00225b2019-02-08 14:27:42 -050042import android.service.notification.StatusBarNotification;
Mady Mellordea7ecf2018-12-10 15:47:40 -080043import android.util.AttributeSet;
Mady Mellor9801e852019-01-22 14:50:28 -080044import android.util.Log;
Steven Wub00225b2019-02-08 14:27:42 -050045import android.util.StatsLog;
Mady Mellordea7ecf2018-12-10 15:47:40 -080046import android.view.View;
Mady Mellor3dff9e62019-02-05 18:12:53 -080047import android.view.WindowInsets;
Mady Mellora96c9ed2019-06-07 12:55:26 -070048import android.view.WindowManager;
Mady Mellordea7ecf2018-12-10 15:47:40 -080049import android.widget.LinearLayout;
50
Mark Renouf34d04f32019-05-13 15:53:18 -040051import com.android.internal.policy.ScreenDecorationsUtils;
Mady Mellor3dff9e62019-02-05 18:12:53 -080052import com.android.systemui.Dependency;
Mady Mellordea7ecf2018-12-10 15:47:40 -080053import com.android.systemui.R;
54import com.android.systemui.recents.TriangleShape;
Lyn Han754e77b2019-04-30 14:34:49 -070055import com.android.systemui.statusbar.AlphaOptimizedButton;
Mady Mellordea7ecf2018-12-10 15:47:40 -080056
57/**
Lyn Han02cca812019-04-02 16:27:32 -070058 * Container for the expanded bubble view, handles rendering the caret and settings icon.
Mady Mellordea7ecf2018-12-10 15:47:40 -080059 */
Mady Mellor3d82e682019-02-05 13:34:48 -080060public class BubbleExpandedView extends LinearLayout implements View.OnClickListener {
Issei Suzukia8d07312019-06-07 12:56:19 +020061 private static final String TAG = TAG_WITH_CLASS_NAME ? "BubbleExpandedView" : TAG_BUBBLES;
Mady Mellordea7ecf2018-12-10 15:47:40 -080062
Issei Suzuki734bc942019-06-05 13:59:52 +020063 private enum ActivityViewStatus {
64 // ActivityView is being initialized, cannot start an activity yet.
65 INITIALIZING,
66 // ActivityView is initialized, and ready to start an activity.
67 INITIALIZED,
68 // Activity runs in the ActivityView.
69 ACTIVITY_STARTED,
70 // ActivityView is released, so activity launching will no longer be permitted.
71 RELEASED,
72 }
73
Mady Mellordea7ecf2018-12-10 15:47:40 -080074 // The triangle pointing to the expanded view
75 private View mPointerView;
Mady Mellor44ee2fe2019-01-30 17:51:16 -080076 private int mPointerMargin;
Mady Mellore8e07712019-01-23 12:45:33 -080077
Lyn Han754e77b2019-04-30 14:34:49 -070078 private AlphaOptimizedButton mSettingsIcon;
Mady Mellore8e07712019-01-23 12:45:33 -080079
Mady Mellor3dff9e62019-02-05 18:12:53 -080080 // Views for expanded state
Mady Mellor3dff9e62019-02-05 18:12:53 -080081 private ActivityView mActivityView;
82
Issei Suzuki734bc942019-06-05 13:59:52 +020083 private ActivityViewStatus mActivityViewStatus = ActivityViewStatus.INITIALIZING;
Mark Renouf5c732b32019-06-12 15:14:54 -040084 private int mTaskId = -1;
85
Mady Mellor3dff9e62019-02-05 18:12:53 -080086 private PendingIntent mBubbleIntent;
87
Mady Mellor5d8f1402019-02-21 18:23:52 -080088 private boolean mKeyboardVisible;
89 private boolean mNeedsNewHeight;
90
Mady Mellora96c9ed2019-06-07 12:55:26 -070091 private Point mDisplaySize;
Mady Mellorfe7ec032019-01-30 17:32:49 -080092 private int mMinHeight;
Lyn Han02cca812019-04-02 16:27:32 -070093 private int mSettingsIconHeight;
Lyn Han02cca812019-04-02 16:27:32 -070094 private int mPointerWidth;
95 private int mPointerHeight;
Mark Renouf34d04f32019-05-13 15:53:18 -040096 private ShapeDrawable mPointerDrawable;
Mady Mellor47b11e32019-07-11 19:06:21 -070097 private Rect mTempRect = new Rect();
98 private int[] mTempLoc = new int[2];
99 private int mExpandedViewTouchSlop;
Mady Mellordea7ecf2018-12-10 15:47:40 -0800100
Mady Mellor99a302602019-06-14 11:39:56 -0700101 private Bubble mBubble;
Mady Mellor9801e852019-01-22 14:50:28 -0800102 private PackageManager mPm;
103 private String mAppName;
Mady Mellore8e07712019-01-23 12:45:33 -0800104 private Drawable mAppIcon;
105
Mady Mellor3dff9e62019-02-05 18:12:53 -0800106 private BubbleController mBubbleController = Dependency.get(BubbleController.class);
Mady Mellor9801e852019-01-22 14:50:28 -0800107
Mady Mellor9801e852019-01-22 14:50:28 -0800108 private BubbleStackView mStackView;
109
Mady Mellor3dff9e62019-02-05 18:12:53 -0800110 private ActivityView.StateCallback mStateCallback = new ActivityView.StateCallback() {
111 @Override
112 public void onActivityViewReady(ActivityView view) {
Issei Suzuki734bc942019-06-05 13:59:52 +0200113 switch (mActivityViewStatus) {
114 case INITIALIZING:
115 case INITIALIZED:
116 // Custom options so there is no activity transition animation
117 ActivityOptions options = ActivityOptions.makeCustomAnimation(getContext(),
118 0 /* enterResId */, 0 /* exitResId */);
119 // Post to keep the lifecycle normal
120 post(() -> mActivityView.startActivity(mBubbleIntent, options));
121 mActivityViewStatus = ActivityViewStatus.ACTIVITY_STARTED;
Mady Mellor6d002032019-02-13 13:45:17 -0800122 }
Mady Mellor3dff9e62019-02-05 18:12:53 -0800123 }
124
125 @Override
126 public void onActivityViewDestroyed(ActivityView view) {
Issei Suzuki734bc942019-06-05 13:59:52 +0200127 mActivityViewStatus = ActivityViewStatus.RELEASED;
Mady Mellor3dff9e62019-02-05 18:12:53 -0800128 }
129
Mark Renouf5c732b32019-06-12 15:14:54 -0400130 @Override
131 public void onTaskCreated(int taskId, ComponentName componentName) {
132 // Since Bubble ActivityView applies singleTaskDisplay this is
133 // guaranteed to only be called once per ActivityView. The taskId is
134 // saved to use for removeTask, preventing appearance in recent tasks.
135 mTaskId = taskId;
136 }
137
Mady Mellor3dff9e62019-02-05 18:12:53 -0800138 /**
139 * This is only called for tasks on this ActivityView, which is also set to
140 * single-task mode -- meaning never more than one task on this display. If a task
141 * is being removed, it's the top Activity finishing and this bubble should
142 * be removed or collapsed.
143 */
144 @Override
145 public void onTaskRemovalStarted(int taskId) {
Mady Mellor99a302602019-06-14 11:39:56 -0700146 if (mBubble != null) {
Mady Mellor3dff9e62019-02-05 18:12:53 -0800147 // Must post because this is called from a binder thread.
Mady Mellor99a302602019-06-14 11:39:56 -0700148 post(() -> mBubbleController.removeBubble(mBubble.getKey(),
Mark Renouf08bc42a2019-03-07 13:01:59 -0500149 BubbleController.DISMISS_TASK_FINISHED));
Mady Mellor3dff9e62019-02-05 18:12:53 -0800150 }
151 }
152 };
Mady Mellore8e07712019-01-23 12:45:33 -0800153
Mady Mellor3d82e682019-02-05 13:34:48 -0800154 public BubbleExpandedView(Context context) {
Mady Mellordea7ecf2018-12-10 15:47:40 -0800155 this(context, null);
156 }
157
Mady Mellor3d82e682019-02-05 13:34:48 -0800158 public BubbleExpandedView(Context context, AttributeSet attrs) {
Mady Mellordea7ecf2018-12-10 15:47:40 -0800159 this(context, attrs, 0);
160 }
161
Mady Mellor3d82e682019-02-05 13:34:48 -0800162 public BubbleExpandedView(Context context, AttributeSet attrs, int defStyleAttr) {
Mady Mellordea7ecf2018-12-10 15:47:40 -0800163 this(context, attrs, defStyleAttr, 0);
164 }
165
Mady Mellor3d82e682019-02-05 13:34:48 -0800166 public BubbleExpandedView(Context context, AttributeSet attrs, int defStyleAttr,
Mady Mellordea7ecf2018-12-10 15:47:40 -0800167 int defStyleRes) {
168 super(context, attrs, defStyleAttr, defStyleRes);
Mady Mellor9801e852019-01-22 14:50:28 -0800169 mPm = context.getPackageManager();
Mady Mellora96c9ed2019-06-07 12:55:26 -0700170 mDisplaySize = new Point();
171 WindowManager wm = (WindowManager) context.getSystemService(Context.WINDOW_SERVICE);
172 wm.getDefaultDisplay().getSize(mDisplaySize);
Mady Mellor47b11e32019-07-11 19:06:21 -0700173 Resources res = getResources();
174 mMinHeight = res.getDimensionPixelSize(R.dimen.bubble_expanded_default_height);
175 mPointerMargin = res.getDimensionPixelSize(R.dimen.bubble_pointer_margin);
176 mExpandedViewTouchSlop = res.getDimensionPixelSize(R.dimen.bubble_expanded_view_slop);
Mady Mellordea7ecf2018-12-10 15:47:40 -0800177 }
178
179 @Override
180 protected void onFinishInflate() {
181 super.onFinishInflate();
182
183 Resources res = getResources();
184 mPointerView = findViewById(R.id.pointer_view);
Lyn Han02cca812019-04-02 16:27:32 -0700185 mPointerWidth = res.getDimensionPixelSize(R.dimen.bubble_pointer_width);
186 mPointerHeight = res.getDimensionPixelSize(R.dimen.bubble_pointer_height);
Mady Mellordd497052019-01-30 17:23:48 -0800187
Mady Mellordd497052019-01-30 17:23:48 -0800188
Mark Renouf34d04f32019-05-13 15:53:18 -0400189 mPointerDrawable = new ShapeDrawable(TriangleShape.create(
Lyn Han5aa27e22019-05-15 10:55:07 -0700190 mPointerWidth, mPointerHeight, true /* pointUp */));
Mark Renouf34d04f32019-05-13 15:53:18 -0400191 mPointerView.setBackground(mPointerDrawable);
Mady Mellora96c9ed2019-06-07 12:55:26 -0700192 mPointerView.setVisibility(INVISIBLE);
Mady Mellor9801e852019-01-22 14:50:28 -0800193
Lyn Han02cca812019-04-02 16:27:32 -0700194 mSettingsIconHeight = getContext().getResources().getDimensionPixelSize(
Mady Mellorfe7ec032019-01-30 17:32:49 -0800195 R.dimen.bubble_expanded_header_height);
Lyn Han02cca812019-04-02 16:27:32 -0700196 mSettingsIcon = findViewById(R.id.settings_button);
Lyn Han02cca812019-04-02 16:27:32 -0700197 mSettingsIcon.setOnClickListener(this);
Lyn Han02cca812019-04-02 16:27:32 -0700198
Mady Mellor3dff9e62019-02-05 18:12:53 -0800199 mActivityView = new ActivityView(mContext, null /* attrs */, 0 /* defStyle */,
200 true /* singleTaskInstance */);
Issei Suzukicac2a502019-04-16 16:52:50 +0200201
202 setContentVisibility(false);
Mady Mellor3dff9e62019-02-05 18:12:53 -0800203 addView(mActivityView);
204
Lyn Han5aa27e22019-05-15 10:55:07 -0700205 // Expanded stack layout, top to bottom:
206 // Expanded view container
207 // ==> bubble row
208 // ==> expanded view
209 // ==> activity view
210 // ==> manage button
211 bringChildToFront(mActivityView);
212 bringChildToFront(mSettingsIcon);
Mady Mellor52b1ac62019-04-10 16:59:03 -0700213
Mark Renouf34d04f32019-05-13 15:53:18 -0400214 applyThemeAttrs();
215
Mady Mellor5d8f1402019-02-21 18:23:52 -0800216 setOnApplyWindowInsetsListener((View view, WindowInsets insets) -> {
217 // Keep track of IME displaying because we should not make any adjustments that might
218 // cause a config change while the IME is displayed otherwise it'll loose focus.
Mady Mellor3dff9e62019-02-05 18:12:53 -0800219 final int keyboardHeight = insets.getSystemWindowInsetBottom()
220 - insets.getStableInsetBottom();
Mady Mellor5d8f1402019-02-21 18:23:52 -0800221 mKeyboardVisible = keyboardHeight != 0;
222 if (!mKeyboardVisible && mNeedsNewHeight) {
223 updateHeight();
224 }
Mady Mellor3dff9e62019-02-05 18:12:53 -0800225 return view.onApplyWindowInsets(insets);
226 });
Mady Mellore8e07712019-01-23 12:45:33 -0800227 }
228
Mark Renouf34d04f32019-05-13 15:53:18 -0400229 void applyThemeAttrs() {
230 TypedArray ta = getContext().obtainStyledAttributes(R.styleable.BubbleExpandedView);
231 int bgColor = ta.getColor(
232 R.styleable.BubbleExpandedView_android_colorBackgroundFloating, Color.WHITE);
233 float cornerRadius = ta.getDimension(
234 R.styleable.BubbleExpandedView_android_dialogCornerRadius, 0);
235 ta.recycle();
236
237 // Update triangle color.
238 mPointerDrawable.setTint(bgColor);
239
240 // Update ActivityView cornerRadius
241 if (ScreenDecorationsUtils.supportsRoundedCornersOnWindows(mContext.getResources())) {
242 mActivityView.setCornerRadius(cornerRadius);
243 }
244 }
245
Mady Mellor5d8f1402019-02-21 18:23:52 -0800246 @Override
247 protected void onDetachedFromWindow() {
248 super.onDetachedFromWindow();
249 mKeyboardVisible = false;
250 mNeedsNewHeight = false;
251 if (mActivityView != null) {
252 mActivityView.setForwardedInsets(Insets.of(0, 0, 0, 0));
253 }
254 }
255
256 /**
Issei Suzukicac2a502019-04-16 16:52:50 +0200257 * Set visibility of contents in the expanded state.
258 *
259 * @param visibility {@code true} if the contents should be visible on the screen.
260 *
261 * Note that this contents visibility doesn't affect visibility at {@link android.view.View},
262 * and setting {@code false} actually means rendering the contents in transparent.
263 */
264 void setContentVisibility(boolean visibility) {
265 final float alpha = visibility ? 1f : 0f;
266 mPointerView.setAlpha(alpha);
267 if (mActivityView != null) {
268 mActivityView.setAlpha(alpha);
269 }
270 }
271
272 /**
Mady Mellor5d8f1402019-02-21 18:23:52 -0800273 * Called by {@link BubbleStackView} when the insets for the expanded state should be updated.
274 * This should be done post-move and post-animation.
275 */
276 void updateInsets(WindowInsets insets) {
277 if (usingActivityView()) {
Mady Mellor5d8f1402019-02-21 18:23:52 -0800278 int[] windowLocation = mActivityView.getLocationOnScreen();
279 final int windowBottom = windowLocation[1] + mActivityView.getHeight();
280 final int keyboardHeight = insets.getSystemWindowInsetBottom()
281 - insets.getStableInsetBottom();
282 final int insetsBottom = Math.max(0,
Mady Mellora96c9ed2019-06-07 12:55:26 -0700283 windowBottom + keyboardHeight - mDisplaySize.y);
Mady Mellor5d8f1402019-02-21 18:23:52 -0800284 mActivityView.setForwardedInsets(Insets.of(0, 0, 0, insetsBottom));
285 }
286 }
287
Mady Mellore8e07712019-01-23 12:45:33 -0800288 /**
Mady Mellor99a302602019-06-14 11:39:56 -0700289 * Sets the bubble used to populate this view.
Mady Mellor9801e852019-01-22 14:50:28 -0800290 */
Mady Mellor99a302602019-06-14 11:39:56 -0700291 public void setBubble(Bubble bubble, BubbleStackView stackView, String appName) {
Mady Mellor9801e852019-01-22 14:50:28 -0800292 mStackView = stackView;
Mady Mellor99a302602019-06-14 11:39:56 -0700293 mBubble = bubble;
Lyn Han6c40fe72019-05-08 14:06:33 -0700294 mAppName = appName;
Mady Mellor9801e852019-01-22 14:50:28 -0800295
Mady Mellor9801e852019-01-22 14:50:28 -0800296 try {
Issei Suzukia91f3962019-06-07 11:48:23 +0200297 ApplicationInfo info = mPm.getApplicationInfo(
Mady Mellor99a302602019-06-14 11:39:56 -0700298 bubble.getPackageName(),
Mady Mellor9801e852019-01-22 14:50:28 -0800299 PackageManager.MATCH_UNINSTALLED_PACKAGES
300 | PackageManager.MATCH_DISABLED_COMPONENTS
301 | PackageManager.MATCH_DIRECT_BOOT_UNAWARE
302 | PackageManager.MATCH_DIRECT_BOOT_AWARE);
Issei Suzukia91f3962019-06-07 11:48:23 +0200303 mAppIcon = mPm.getApplicationIcon(info);
Mady Mellor9801e852019-01-22 14:50:28 -0800304 } catch (PackageManager.NameNotFoundException e) {
Lyn Han6c40fe72019-05-08 14:06:33 -0700305 // Do nothing.
Mady Mellor9801e852019-01-22 14:50:28 -0800306 }
Mady Mellore8e07712019-01-23 12:45:33 -0800307 if (mAppIcon == null) {
308 mAppIcon = mPm.getDefaultActivityIcon();
309 }
Mark Renouf34d04f32019-05-13 15:53:18 -0400310 applyThemeAttrs();
Lyn Han69149122019-04-30 12:03:12 -0700311 showSettingsIcon();
Mady Mellor3dff9e62019-02-05 18:12:53 -0800312 updateExpandedView();
Mady Mellor6d002032019-02-13 13:45:17 -0800313 }
314
315 /**
316 * Lets activity view know it should be shown / populated.
317 */
Mady Mellor5029fa62019-03-05 12:16:21 -0800318 public void populateExpandedView() {
319 if (usingActivityView()) {
320 mActivityView.setCallback(mStateCallback);
321 } else {
Issei Suzukia91f3962019-06-07 11:48:23 +0200322 Log.e(TAG, "Cannot populate expanded view.");
Mady Mellor5029fa62019-03-05 12:16:21 -0800323 }
Mady Mellor9801e852019-01-22 14:50:28 -0800324 }
325
Mady Mellorfe7ec032019-01-30 17:32:49 -0800326 /**
Mady Mellor99a302602019-06-14 11:39:56 -0700327 * Updates the bubble backing this view. This will not re-populate ActivityView, it will
Lyn Han02cca812019-04-02 16:27:32 -0700328 * only update the deep-links in the title, and the height of the view.
Mady Mellorfe7ec032019-01-30 17:32:49 -0800329 */
Mady Mellor99a302602019-06-14 11:39:56 -0700330 public void update(Bubble bubble) {
331 if (bubble.getKey().equals(mBubble.getKey())) {
332 mBubble = bubble;
Lyn Han02cca812019-04-02 16:27:32 -0700333 updateSettingsContentDescription();
Mady Mellorfe7ec032019-01-30 17:32:49 -0800334 updateHeight();
335 } else {
Mady Mellor99a302602019-06-14 11:39:56 -0700336 Log.w(TAG, "Trying to update entry with different key, new bubble: "
337 + bubble.getKey() + " old bubble: " + bubble.getKey());
Mady Mellorfe7ec032019-01-30 17:32:49 -0800338 }
339 }
340
Mady Mellor3dff9e62019-02-05 18:12:53 -0800341 private void updateExpandedView() {
Mady Mellor99a302602019-06-14 11:39:56 -0700342 mBubbleIntent = mBubble.getBubbleIntent(mContext);
Mady Mellor3dff9e62019-02-05 18:12:53 -0800343 if (mBubbleIntent != null) {
Issei Suzukicac2a502019-04-16 16:52:50 +0200344 setContentVisibility(false);
Mady Mellor3dff9e62019-02-05 18:12:53 -0800345 mActivityView.setVisibility(VISIBLE);
Mady Mellor3dff9e62019-02-05 18:12:53 -0800346 }
347 updateView();
348 }
349
Mark Renouf041d7262019-02-06 12:09:41 -0500350 boolean performBackPressIfNeeded() {
Mady Mellor323fb0b2019-03-25 12:15:22 -0700351 if (!usingActivityView()) {
Mark Renouf041d7262019-02-06 12:09:41 -0500352 return false;
353 }
354 mActivityView.performBackPress();
355 return true;
356 }
357
Mady Mellorfe7ec032019-01-30 17:32:49 -0800358 void updateHeight() {
359 if (usingActivityView()) {
Mady Mellor99a302602019-06-14 11:39:56 -0700360 int max = getMaxExpandedHeight() - mSettingsIconHeight - mPointerHeight
361 - mPointerMargin;
362 float desiredHeight = Math.max(mBubble.getDesiredHeight(mContext), mMinHeight);
363 float height = Math.min(desiredHeight, max);
Mady Mellorfe7ec032019-01-30 17:32:49 -0800364 height = Math.max(height, mMinHeight);
365 LayoutParams lp = (LayoutParams) mActivityView.getLayoutParams();
Mady Mellor5d8f1402019-02-21 18:23:52 -0800366 mNeedsNewHeight = lp.height != height;
367 if (!mKeyboardVisible) {
368 // If the keyboard is visible... don't adjust the height because that will cause
369 // a configuration change and the keyboard will be lost.
Mady Mellor7af771a2019-03-07 15:04:54 -0800370 lp.height = (int) height;
Mady Mellor5d8f1402019-02-21 18:23:52 -0800371 mActivityView.setLayoutParams(lp);
372 mNeedsNewHeight = false;
373 }
Mady Mellorfe7ec032019-01-30 17:32:49 -0800374 }
375 }
376
Mady Mellora96c9ed2019-06-07 12:55:26 -0700377 private int getMaxExpandedHeight() {
378 int[] windowLocation = mActivityView.getLocationOnScreen();
379 return mDisplaySize.y - windowLocation[1] - mSettingsIconHeight;
380 }
381
Mady Mellor47b11e32019-07-11 19:06:21 -0700382 /**
383 * Whether the provided x, y values (in raw coordinates) are in a touchable area of the
384 * expanded view.
385 *
386 * The touchable areas are the ActivityView (plus some slop around it) and the manage button.
387 */
388 boolean intersectingTouchableContent(int rawX, int rawY) {
389 mTempRect.setEmpty();
390 if (mActivityView != null) {
391 mTempLoc = mActivityView.getLocationOnScreen();
392 mTempRect.set(mTempLoc[0] - mExpandedViewTouchSlop,
393 mTempLoc[1] - mExpandedViewTouchSlop,
394 mTempLoc[0] + mActivityView.getWidth() + mExpandedViewTouchSlop,
395 mTempLoc[1] + mActivityView.getHeight() + mExpandedViewTouchSlop);
396 }
397 if (mTempRect.contains(rawX, rawY)) {
398 return true;
399 }
400 mTempLoc = mSettingsIcon.getLocationOnScreen();
401 mTempRect.set(mTempLoc[0],
402 mTempLoc[1],
403 mTempLoc[0] + mSettingsIcon.getWidth(),
404 mTempLoc[1] + mSettingsIcon.getHeight());
405 if (mTempRect.contains(rawX, rawY)) {
406 return true;
407 }
408 return false;
409 }
410
Mady Mellor9801e852019-01-22 14:50:28 -0800411 @Override
412 public void onClick(View view) {
Mady Mellor99a302602019-06-14 11:39:56 -0700413 if (mBubble == null) {
Mady Mellor9801e852019-01-22 14:50:28 -0800414 return;
415 }
Mady Mellor9801e852019-01-22 14:50:28 -0800416 int id = view.getId();
Lyn Hanc26ff122019-03-29 16:46:07 -0700417 if (id == R.id.settings_button) {
Mady Mellor99a302602019-06-14 11:39:56 -0700418 Intent intent = mBubble.getSettingsIntent();
Steven Wub00225b2019-02-08 14:27:42 -0500419 mStackView.collapseStack(() -> {
Mady Mellor99a302602019-06-14 11:39:56 -0700420 mContext.startActivityAsUser(intent, mBubble.getEntry().notification.getUser());
421 logBubbleClickEvent(mBubble,
Steven Wub00225b2019-02-08 14:27:42 -0500422 StatsLog.BUBBLE_UICHANGED__ACTION__HEADER_GO_TO_SETTINGS);
423 });
Mady Mellor9801e852019-01-22 14:50:28 -0800424 }
Mady Mellordea7ecf2018-12-10 15:47:40 -0800425 }
426
Lyn Han02cca812019-04-02 16:27:32 -0700427 private void updateSettingsContentDescription() {
428 mSettingsIcon.setContentDescription(getResources().getString(
429 R.string.bubbles_settings_button_description, mAppName));
430 }
431
Lyn Hanc26ff122019-03-29 16:46:07 -0700432 void showSettingsIcon() {
Lyn Han02cca812019-04-02 16:27:32 -0700433 updateSettingsContentDescription();
Lyn Hanc26ff122019-03-29 16:46:07 -0700434 mSettingsIcon.setVisibility(VISIBLE);
435 }
436
Mady Mellordea7ecf2018-12-10 15:47:40 -0800437 /**
Mady Mellor3dff9e62019-02-05 18:12:53 -0800438 * Update appearance of the expanded view being displayed.
439 */
440 public void updateView() {
441 if (usingActivityView()
442 && mActivityView.getVisibility() == VISIBLE
443 && mActivityView.isAttachedToWindow()) {
444 mActivityView.onLocationChanged();
Mady Mellor3dff9e62019-02-05 18:12:53 -0800445 }
Mady Mellorfe7ec032019-01-30 17:32:49 -0800446 updateHeight();
Mady Mellor3dff9e62019-02-05 18:12:53 -0800447 }
448
449 /**
Mady Mellordea7ecf2018-12-10 15:47:40 -0800450 * Set the x position that the tip of the triangle should point to.
451 */
Mady Mellor3dff9e62019-02-05 18:12:53 -0800452 public void setPointerPosition(float x) {
Lyn Han9a2f5cf2019-05-23 11:01:41 -0700453 float halfPointerWidth = mPointerWidth / 2f;
454 float pointerLeft = x - halfPointerWidth;
455 mPointerView.setTranslationX(pointerLeft);
Lyn Hanf74ba672019-05-20 16:08:48 -0700456 mPointerView.setVisibility(VISIBLE);
Mady Mellordea7ecf2018-12-10 15:47:40 -0800457 }
458
459 /**
Mady Mellor3dff9e62019-02-05 18:12:53 -0800460 * Removes and releases an ActivityView if one was previously created for this bubble.
Mady Mellordea7ecf2018-12-10 15:47:40 -0800461 */
Mady Mellor94d94a72019-03-05 18:16:59 -0800462 public void cleanUpExpandedState() {
Mady Mellor3dff9e62019-02-05 18:12:53 -0800463 if (mActivityView == null) {
Mark Renouf89b1a4a2018-12-04 14:59:45 -0500464 return;
465 }
Issei Suzuki734bc942019-06-05 13:59:52 +0200466 switch (mActivityViewStatus) {
467 case INITIALIZED:
468 case ACTIVITY_STARTED:
469 mActivityView.release();
Mady Mellordea7ecf2018-12-10 15:47:40 -0800470 }
Mark Renouf5c732b32019-06-12 15:14:54 -0400471 if (mTaskId != -1) {
472 try {
473 ActivityTaskManager.getService().removeTask(mTaskId);
474 } catch (RemoteException e) {
475 Log.w(TAG, "Failed to remove taskId " + mTaskId);
476 }
477 mTaskId = -1;
478 }
Mark Renouf28c250d2019-02-25 16:47:34 -0500479 removeView(mActivityView);
Mark Renouf5c732b32019-06-12 15:14:54 -0400480
Mady Mellor3dff9e62019-02-05 18:12:53 -0800481 mActivityView = null;
Issei Suzuki734bc942019-06-05 13:59:52 +0200482 }
483
484 /**
485 * Called when the last task is removed from a {@link android.hardware.display.VirtualDisplay}
486 * which {@link ActivityView} uses.
487 */
488 void notifyDisplayEmpty() {
489 if (mActivityViewStatus == ActivityViewStatus.ACTIVITY_STARTED) {
490 mActivityViewStatus = ActivityViewStatus.INITIALIZED;
491 }
Mady Mellordea7ecf2018-12-10 15:47:40 -0800492 }
493
Mady Mellor3dff9e62019-02-05 18:12:53 -0800494 private boolean usingActivityView() {
Mady Mellor323fb0b2019-03-25 12:15:22 -0700495 return mBubbleIntent != null && mActivityView != null;
Mady Mellor3dff9e62019-02-05 18:12:53 -0800496 }
497
Mady Mellor390bff42019-04-05 15:09:01 -0700498 /**
499 * @return the display id of the virtual display.
500 */
501 public int getVirtualDisplayId() {
502 if (usingActivityView()) {
503 return mActivityView.getVirtualDisplayId();
504 }
505 return INVALID_DISPLAY;
506 }
507
Mady Mellor3dff9e62019-02-05 18:12:53 -0800508 /**
Steven Wub00225b2019-02-08 14:27:42 -0500509 * Logs bubble UI click event.
510 *
Mady Mellor99a302602019-06-14 11:39:56 -0700511 * @param bubble the bubble notification entry that user is interacting with.
Steven Wub00225b2019-02-08 14:27:42 -0500512 * @param action the user interaction enum.
513 */
Mady Mellor99a302602019-06-14 11:39:56 -0700514 private void logBubbleClickEvent(Bubble bubble, int action) {
515 StatusBarNotification notification = bubble.getEntry().notification;
Steven Wub00225b2019-02-08 14:27:42 -0500516 StatsLog.write(StatsLog.BUBBLE_UI_CHANGED,
517 notification.getPackageName(),
518 notification.getNotification().getChannelId(),
519 notification.getId(),
520 mStackView.getBubbleIndex(mStackView.getExpandedBubble()),
521 mStackView.getBubbleCount(),
522 action,
523 mStackView.getNormalizedXPosition(),
Steven Wu45e38ae2019-03-25 16:16:59 -0400524 mStackView.getNormalizedYPosition(),
Mady Mellor99a302602019-06-14 11:39:56 -0700525 bubble.showInShadeWhenBubble(),
526 bubble.isOngoing(),
Mady Mellor6cec3502019-06-17 19:20:49 -0700527 false /* isAppForeground (unused) */);
Steven Wub00225b2019-02-08 14:27:42 -0500528 }
Mady Mellordea7ecf2018-12-10 15:47:40 -0800529}