blob: c3dcc0b3038caed2f60d24f8626ae925b4a43ece [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
Yuncheol Heo5d62e752020-04-08 14:54:24 -070019import static android.app.WindowConfiguration.WINDOWING_MODE_MULTI_WINDOW;
Mady Mellorc41ed322019-09-25 11:19:26 -070020import static android.content.Intent.FLAG_ACTIVITY_MULTIPLE_TASK;
21import static android.content.Intent.FLAG_ACTIVITY_NEW_DOCUMENT;
Yunfan Chenb83940c2020-04-06 16:43:09 +090022import static android.graphics.PixelFormat.TRANSPARENT;
Mady Mellor390bff42019-04-05 15:09:01 -070023import static android.view.Display.INVALID_DISPLAY;
Yunfan Chenb83940c2020-04-06 16:43:09 +090024import static android.view.InsetsState.ITYPE_IME;
Jorim Jaggi924ef752020-01-29 17:26:55 +010025import static android.view.ViewRootImpl.NEW_INSETS_MODE_FULL;
26import static android.view.ViewRootImpl.sNewInsetsMode;
Yunfan Chenb83940c2020-04-06 16:43:09 +090027import static android.view.WindowManager.LayoutParams.FLAG_LAYOUT_NO_LIMITS;
28import static android.view.WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE;
29import static android.view.WindowManager.LayoutParams.FLAG_NOT_TOUCHABLE;
30import static android.view.WindowManager.LayoutParams.TYPE_NAVIGATION_BAR_PANEL;
Beverlya53fb0d2020-01-29 15:26:13 -050031
Mark Renoufdb861c92019-07-26 13:58:17 -040032import static com.android.systemui.bubbles.BubbleDebugConfig.DEBUG_BUBBLE_EXPANDED_VIEW;
Issei Suzukia8d07312019-06-07 12:56:19 +020033import static com.android.systemui.bubbles.BubbleDebugConfig.TAG_BUBBLES;
34import static com.android.systemui.bubbles.BubbleDebugConfig.TAG_WITH_CLASS_NAME;
Mady Mellorca0c24c2019-05-16 16:14:32 -070035
Lyn Hanb58c7562020-01-07 14:29:20 -080036import android.annotation.Nullable;
Joshua Tsuji847b1d422020-06-01 13:42:19 -040037import android.annotation.SuppressLint;
Mady Mellor60101c92019-04-11 19:04:00 -070038import android.app.ActivityOptions;
Mark Renouf5c732b32019-06-12 15:14:54 -040039import android.app.ActivityTaskManager;
Mady Mellor3dff9e62019-02-05 18:12:53 -080040import android.app.ActivityView;
Mady Mellor9801e852019-01-22 14:50:28 -080041import android.app.PendingIntent;
Mark Renouf5c732b32019-06-12 15:14:54 -040042import android.content.ComponentName;
Mady Mellordea7ecf2018-12-10 15:47:40 -080043import android.content.Context;
Mady Mellor9801e852019-01-22 14:50:28 -080044import android.content.Intent;
Mady Mellordea7ecf2018-12-10 15:47:40 -080045import android.content.res.Resources;
Mady Mellordd497052019-01-30 17:23:48 -080046import android.content.res.TypedArray;
Mady Mellordea7ecf2018-12-10 15:47:40 -080047import android.graphics.Color;
Mady Mellor3dff9e62019-02-05 18:12:53 -080048import android.graphics.Insets;
49import android.graphics.Point;
Mady Mellor47b11e32019-07-11 19:06:21 -070050import android.graphics.Rect;
Mady Mellordea7ecf2018-12-10 15:47:40 -080051import android.graphics.drawable.ShapeDrawable;
Yunfan Chenb83940c2020-04-06 16:43:09 +090052import android.hardware.display.VirtualDisplay;
53import android.os.Binder;
Mark Renouf5c732b32019-06-12 15:14:54 -040054import android.os.RemoteException;
Mady Mellordea7ecf2018-12-10 15:47:40 -080055import android.util.AttributeSet;
Mady Mellor9801e852019-01-22 14:50:28 -080056import android.util.Log;
Yunfan Chenb83940c2020-04-06 16:43:09 +090057import android.view.Gravity;
Mady Mellordea7ecf2018-12-10 15:47:40 -080058import android.view.View;
Mady Mellor3dff9e62019-02-05 18:12:53 -080059import android.view.WindowInsets;
Mady Mellora96c9ed2019-06-07 12:55:26 -070060import android.view.WindowManager;
Lyn Han670fa802020-05-05 12:56:33 -070061import android.view.accessibility.AccessibilityNodeInfo;
Mady Mellordea7ecf2018-12-10 15:47:40 -080062import android.widget.LinearLayout;
63
Mark Renouf34d04f32019-05-13 15:53:18 -040064import com.android.internal.policy.ScreenDecorationsUtils;
Mady Mellor3dff9e62019-02-05 18:12:53 -080065import com.android.systemui.Dependency;
Mady Mellordea7ecf2018-12-10 15:47:40 -080066import com.android.systemui.R;
67import com.android.systemui.recents.TriangleShape;
Lyn Han754e77b2019-04-30 14:34:49 -070068import com.android.systemui.statusbar.AlphaOptimizedButton;
Mady Mellordea7ecf2018-12-10 15:47:40 -080069
70/**
Lyn Han02cca812019-04-02 16:27:32 -070071 * Container for the expanded bubble view, handles rendering the caret and settings icon.
Mady Mellordea7ecf2018-12-10 15:47:40 -080072 */
Joshua Tsuji6855cab2020-04-16 01:05:39 -040073public class BubbleExpandedView extends LinearLayout {
Issei Suzukia8d07312019-06-07 12:56:19 +020074 private static final String TAG = TAG_WITH_CLASS_NAME ? "BubbleExpandedView" : TAG_BUBBLES;
Yunfan Chenb83940c2020-04-06 16:43:09 +090075 private static final String WINDOW_TITLE = "ImeInsetsWindowWithoutContent";
Mady Mellordea7ecf2018-12-10 15:47:40 -080076
Issei Suzuki734bc942019-06-05 13:59:52 +020077 private enum ActivityViewStatus {
78 // ActivityView is being initialized, cannot start an activity yet.
79 INITIALIZING,
80 // ActivityView is initialized, and ready to start an activity.
81 INITIALIZED,
82 // Activity runs in the ActivityView.
83 ACTIVITY_STARTED,
84 // ActivityView is released, so activity launching will no longer be permitted.
85 RELEASED,
86 }
Mady Mellordea7ecf2018-12-10 15:47:40 -080087
88 // The triangle pointing to the expanded view
89 private View mPointerView;
Mady Mellor44ee2fe2019-01-30 17:51:16 -080090 private int mPointerMargin;
Mady Mellore8e07712019-01-23 12:45:33 -080091
Lyn Han754e77b2019-04-30 14:34:49 -070092 private AlphaOptimizedButton mSettingsIcon;
Mady Mellore8e07712019-01-23 12:45:33 -080093
Mady Mellor3dff9e62019-02-05 18:12:53 -080094 // Views for expanded state
Mady Mellor3dff9e62019-02-05 18:12:53 -080095 private ActivityView mActivityView;
96
Issei Suzuki734bc942019-06-05 13:59:52 +020097 private ActivityViewStatus mActivityViewStatus = ActivityViewStatus.INITIALIZING;
Mark Renouf5c732b32019-06-12 15:14:54 -040098 private int mTaskId = -1;
99
Lyn Hanb58c7562020-01-07 14:29:20 -0800100 private PendingIntent mPendingIntent;
Mady Mellor3dff9e62019-02-05 18:12:53 -0800101
Mady Mellor5d8f1402019-02-21 18:23:52 -0800102 private boolean mKeyboardVisible;
103 private boolean mNeedsNewHeight;
104
Mady Mellora96c9ed2019-06-07 12:55:26 -0700105 private Point mDisplaySize;
Mady Mellorfe7ec032019-01-30 17:32:49 -0800106 private int mMinHeight;
Lyn Hancd4f87e2020-02-19 20:33:45 -0800107 private int mOverflowHeight;
Lyn Han02cca812019-04-02 16:27:32 -0700108 private int mSettingsIconHeight;
Lyn Han02cca812019-04-02 16:27:32 -0700109 private int mPointerWidth;
110 private int mPointerHeight;
Mark Renouf34d04f32019-05-13 15:53:18 -0400111 private ShapeDrawable mPointerDrawable;
Mady Mellordea7ecf2018-12-10 15:47:40 -0800112
Lyn Hanb58c7562020-01-07 14:29:20 -0800113 @Nullable private Bubble mBubble;
114
115 private boolean mIsOverflow;
Mady Mellore8e07712019-01-23 12:45:33 -0800116
Mady Mellor3dff9e62019-02-05 18:12:53 -0800117 private BubbleController mBubbleController = Dependency.get(BubbleController.class);
Mady Mellor9be3bed2019-08-21 17:26:26 -0700118 private WindowManager mWindowManager;
Mady Mellor9801e852019-01-22 14:50:28 -0800119
Mady Mellor9801e852019-01-22 14:50:28 -0800120 private BubbleStackView mStackView;
Yunfan Chenb83940c2020-04-06 16:43:09 +0900121 private View mVirtualImeView;
122 private WindowManager mVirtualDisplayWindowManager;
123 private boolean mImeShowing = false;
Mady Mellor9801e852019-01-22 14:50:28 -0800124
Mady Mellor3dff9e62019-02-05 18:12:53 -0800125 private ActivityView.StateCallback mStateCallback = new ActivityView.StateCallback() {
126 @Override
127 public void onActivityViewReady(ActivityView view) {
Mark Renoufdb861c92019-07-26 13:58:17 -0400128 if (DEBUG_BUBBLE_EXPANDED_VIEW) {
129 Log.d(TAG, "onActivityViewReady: mActivityViewStatus=" + mActivityViewStatus
130 + " bubble=" + getBubbleKey());
131 }
Issei Suzuki734bc942019-06-05 13:59:52 +0200132 switch (mActivityViewStatus) {
133 case INITIALIZING:
134 case INITIALIZED:
135 // Custom options so there is no activity transition animation
136 ActivityOptions options = ActivityOptions.makeCustomAnimation(getContext(),
137 0 /* enterResId */, 0 /* exitResId */);
Winson Chung06200522020-03-31 22:47:02 -0700138 options.setTaskAlwaysOnTop(true);
Yuncheol Heo5d62e752020-04-08 14:54:24 -0700139 options.setLaunchWindowingMode(WINDOWING_MODE_MULTI_WINDOW);
Issei Suzuki734bc942019-06-05 13:59:52 +0200140 // Post to keep the lifecycle normal
Mark Renoufdb861c92019-07-26 13:58:17 -0400141 post(() -> {
142 if (DEBUG_BUBBLE_EXPANDED_VIEW) {
143 Log.d(TAG, "onActivityViewReady: calling startActivity, "
144 + "bubble=" + getBubbleKey());
145 }
Mady Mellor8454ddf2019-08-15 11:16:23 -0700146 try {
Lyn Hanb58c7562020-01-07 14:29:20 -0800147 if (!mIsOverflow && mBubble.usingShortcutInfo()) {
Mady Mellor80d5ed12020-04-10 21:17:30 -0700148 options.setApplyActivityFlagsForBubbles(true);
Mady Mellorb547e5e2019-12-02 10:15:56 -0800149 mActivityView.startShortcutActivity(mBubble.getShortcutInfo(),
150 options, null /* sourceBounds */);
151 } else {
152 Intent fillInIntent = new Intent();
153 // Apply flags to make behaviour match documentLaunchMode=always.
154 fillInIntent.addFlags(FLAG_ACTIVITY_NEW_DOCUMENT);
155 fillInIntent.addFlags(FLAG_ACTIVITY_MULTIPLE_TASK);
Lyn Hanb58c7562020-01-07 14:29:20 -0800156 mActivityView.startActivity(mPendingIntent, fillInIntent, options);
Mady Mellorb547e5e2019-12-02 10:15:56 -0800157 }
Mady Mellor8454ddf2019-08-15 11:16:23 -0700158 } catch (RuntimeException e) {
159 // If there's a runtime exception here then there's something
160 // wrong with the intent, we can't really recover / try to populate
161 // the bubble again so we'll just remove it.
162 Log.w(TAG, "Exception while displaying bubble: " + getBubbleKey()
163 + ", " + e.getMessage() + "; removing bubble");
Pinyao Ting3c930612020-05-19 00:26:03 +0000164 mBubbleController.removeBubble(getBubbleKey(),
Mady Mellor8454ddf2019-08-15 11:16:23 -0700165 BubbleController.DISMISS_INVALID_INTENT);
166 }
Mark Renoufdb861c92019-07-26 13:58:17 -0400167 });
Issei Suzuki734bc942019-06-05 13:59:52 +0200168 mActivityViewStatus = ActivityViewStatus.ACTIVITY_STARTED;
Mady Mellor6d002032019-02-13 13:45:17 -0800169 }
Mady Mellor3dff9e62019-02-05 18:12:53 -0800170 }
171
172 @Override
173 public void onActivityViewDestroyed(ActivityView view) {
Mark Renoufdb861c92019-07-26 13:58:17 -0400174 if (DEBUG_BUBBLE_EXPANDED_VIEW) {
175 Log.d(TAG, "onActivityViewDestroyed: mActivityViewStatus=" + mActivityViewStatus
176 + " bubble=" + getBubbleKey());
177 }
Issei Suzuki734bc942019-06-05 13:59:52 +0200178 mActivityViewStatus = ActivityViewStatus.RELEASED;
Mady Mellor3dff9e62019-02-05 18:12:53 -0800179 }
180
Mark Renouf5c732b32019-06-12 15:14:54 -0400181 @Override
182 public void onTaskCreated(int taskId, ComponentName componentName) {
Mark Renoufdb861c92019-07-26 13:58:17 -0400183 if (DEBUG_BUBBLE_EXPANDED_VIEW) {
184 Log.d(TAG, "onTaskCreated: taskId=" + taskId
185 + " bubble=" + getBubbleKey());
186 }
Mark Renouf5c732b32019-06-12 15:14:54 -0400187 // Since Bubble ActivityView applies singleTaskDisplay this is
188 // guaranteed to only be called once per ActivityView. The taskId is
189 // saved to use for removeTask, preventing appearance in recent tasks.
190 mTaskId = taskId;
Mady Mellor3dff9e62019-02-05 18:12:53 -0800191 }
192
193 /**
194 * This is only called for tasks on this ActivityView, which is also set to
195 * single-task mode -- meaning never more than one task on this display. If a task
196 * is being removed, it's the top Activity finishing and this bubble should
197 * be removed or collapsed.
198 */
199 @Override
200 public void onTaskRemovalStarted(int taskId) {
Mark Renoufdb861c92019-07-26 13:58:17 -0400201 if (DEBUG_BUBBLE_EXPANDED_VIEW) {
202 Log.d(TAG, "onTaskRemovalStarted: taskId=" + taskId
203 + " mActivityViewStatus=" + mActivityViewStatus
204 + " bubble=" + getBubbleKey());
205 }
Mady Mellordd6fe612020-04-15 11:47:55 -0700206 if (mBubble != null) {
Mady Mellorff076eb2019-11-13 10:12:06 -0800207 // Must post because this is called from a binder thread.
Pinyao Ting3c930612020-05-19 00:26:03 +0000208 post(() -> mBubbleController.removeBubble(mBubble.getKey(),
Mady Mellorff076eb2019-11-13 10:12:06 -0800209 BubbleController.DISMISS_TASK_FINISHED));
Mady Mellor3dff9e62019-02-05 18:12:53 -0800210 }
211 }
212 };
Mady Mellore8e07712019-01-23 12:45:33 -0800213
Mady Mellor3d82e682019-02-05 13:34:48 -0800214 public BubbleExpandedView(Context context) {
Mady Mellordea7ecf2018-12-10 15:47:40 -0800215 this(context, null);
216 }
217
Mady Mellor3d82e682019-02-05 13:34:48 -0800218 public BubbleExpandedView(Context context, AttributeSet attrs) {
Mady Mellordea7ecf2018-12-10 15:47:40 -0800219 this(context, attrs, 0);
220 }
221
Mady Mellor3d82e682019-02-05 13:34:48 -0800222 public BubbleExpandedView(Context context, AttributeSet attrs, int defStyleAttr) {
Mady Mellordea7ecf2018-12-10 15:47:40 -0800223 this(context, attrs, defStyleAttr, 0);
224 }
225
Mady Mellor3d82e682019-02-05 13:34:48 -0800226 public BubbleExpandedView(Context context, AttributeSet attrs, int defStyleAttr,
Mady Mellordea7ecf2018-12-10 15:47:40 -0800227 int defStyleRes) {
228 super(context, attrs, defStyleAttr, defStyleRes);
Lyn Hanbc985c52020-05-18 17:57:58 -0700229 updateDimensions();
230 }
231
232 void updateDimensions() {
Mady Mellora96c9ed2019-06-07 12:55:26 -0700233 mDisplaySize = new Point();
Lyn Hanbc985c52020-05-18 17:57:58 -0700234 mWindowManager = (WindowManager) mContext.getSystemService(Context.WINDOW_SERVICE);
Mady Mellor8fe411d2019-08-16 11:27:53 -0700235 // Get the real size -- this includes screen decorations (notches, statusbar, navbar).
Mady Mellor9be3bed2019-08-21 17:26:26 -0700236 mWindowManager.getDefaultDisplay().getRealSize(mDisplaySize);
Mady Mellor47b11e32019-07-11 19:06:21 -0700237 Resources res = getResources();
238 mMinHeight = res.getDimensionPixelSize(R.dimen.bubble_expanded_default_height);
Lyn Hancd4f87e2020-02-19 20:33:45 -0800239 mOverflowHeight = res.getDimensionPixelSize(R.dimen.bubble_overflow_height);
Mady Mellor47b11e32019-07-11 19:06:21 -0700240 mPointerMargin = res.getDimensionPixelSize(R.dimen.bubble_pointer_margin);
Mady Mellordea7ecf2018-12-10 15:47:40 -0800241 }
242
Joshua Tsuji847b1d422020-06-01 13:42:19 -0400243 @SuppressLint("ClickableViewAccessibility")
Mady Mellordea7ecf2018-12-10 15:47:40 -0800244 @Override
245 protected void onFinishInflate() {
246 super.onFinishInflate();
Mark Renoufdb861c92019-07-26 13:58:17 -0400247 if (DEBUG_BUBBLE_EXPANDED_VIEW) {
248 Log.d(TAG, "onFinishInflate: bubble=" + getBubbleKey());
249 }
Mady Mellordea7ecf2018-12-10 15:47:40 -0800250
251 Resources res = getResources();
252 mPointerView = findViewById(R.id.pointer_view);
Lyn Han02cca812019-04-02 16:27:32 -0700253 mPointerWidth = res.getDimensionPixelSize(R.dimen.bubble_pointer_width);
254 mPointerHeight = res.getDimensionPixelSize(R.dimen.bubble_pointer_height);
Mady Mellordd497052019-01-30 17:23:48 -0800255
Mark Renouf34d04f32019-05-13 15:53:18 -0400256 mPointerDrawable = new ShapeDrawable(TriangleShape.create(
Lyn Han5aa27e22019-05-15 10:55:07 -0700257 mPointerWidth, mPointerHeight, true /* pointUp */));
Joshua Tsujidd082b62020-05-20 22:26:24 -0400258 mPointerDrawable.setTint(Color.WHITE);
Mark Renouf34d04f32019-05-13 15:53:18 -0400259 mPointerView.setBackground(mPointerDrawable);
Mady Mellora96c9ed2019-06-07 12:55:26 -0700260 mPointerView.setVisibility(INVISIBLE);
Mady Mellor9801e852019-01-22 14:50:28 -0800261
Lyn Han02cca812019-04-02 16:27:32 -0700262 mSettingsIconHeight = getContext().getResources().getDimensionPixelSize(
Mady Mellor5a3e94b2020-02-07 12:16:21 -0800263 R.dimen.bubble_manage_button_height);
Lyn Han02cca812019-04-02 16:27:32 -0700264 mSettingsIcon = findViewById(R.id.settings_button);
Lyn Han02cca812019-04-02 16:27:32 -0700265
Mady Mellor3dff9e62019-02-05 18:12:53 -0800266 mActivityView = new ActivityView(mContext, null /* attrs */, 0 /* defStyle */,
shawnlinba532af2020-06-02 17:48:37 +0800267 true /* singleTaskInstance */, false /* usePublicVirtualDisplay*/,
268 true /* disableSurfaceViewBackgroundLayer */);
Lyn Hanb58c7562020-01-07 14:29:20 -0800269
lumarkdc9b3192019-07-23 21:18:17 +0800270 // Set ActivityView's alpha value as zero, since there is no view content to be shown.
Issei Suzukicac2a502019-04-16 16:52:50 +0200271 setContentVisibility(false);
Mady Mellor3dff9e62019-02-05 18:12:53 -0800272 addView(mActivityView);
273
Lyn Han5aa27e22019-05-15 10:55:07 -0700274 // Expanded stack layout, top to bottom:
275 // Expanded view container
276 // ==> bubble row
277 // ==> expanded view
278 // ==> activity view
279 // ==> manage button
280 bringChildToFront(mActivityView);
281 bringChildToFront(mSettingsIcon);
Mady Mellor52b1ac62019-04-10 16:59:03 -0700282
Mark Renouf34d04f32019-05-13 15:53:18 -0400283 applyThemeAttrs();
284
Mady Mellor5d8f1402019-02-21 18:23:52 -0800285 setOnApplyWindowInsetsListener((View view, WindowInsets insets) -> {
286 // Keep track of IME displaying because we should not make any adjustments that might
287 // cause a config change while the IME is displayed otherwise it'll loose focus.
Mady Mellor3dff9e62019-02-05 18:12:53 -0800288 final int keyboardHeight = insets.getSystemWindowInsetBottom()
289 - insets.getStableInsetBottom();
Mady Mellor5d8f1402019-02-21 18:23:52 -0800290 mKeyboardVisible = keyboardHeight != 0;
291 if (!mKeyboardVisible && mNeedsNewHeight) {
292 updateHeight();
293 }
Mady Mellor3dff9e62019-02-05 18:12:53 -0800294 return view.onApplyWindowInsets(insets);
295 });
Joshua Tsuji847b1d422020-06-01 13:42:19 -0400296
297 final int expandedViewPadding =
298 res.getDimensionPixelSize(R.dimen.bubble_expanded_view_padding);
299
300 setPadding(
301 expandedViewPadding, expandedViewPadding, expandedViewPadding, expandedViewPadding);
302 setOnTouchListener((view, motionEvent) -> {
303 if (!usingActivityView()) {
304 return false;
305 }
306
307 final Rect avBounds = new Rect();
308 mActivityView.getBoundsOnScreen(avBounds);
309
310 // Consume and ignore events on the expanded view padding that are within the
311 // ActivityView's vertical bounds. These events are part of a back gesture, and so they
312 // should not collapse the stack (which all other touches on areas around the AV would
313 // do).
314 if (motionEvent.getRawY() >= avBounds.top && motionEvent.getRawY() <= avBounds.bottom) {
315 return true;
316 }
317
318 return false;
319 });
Joshua Tsuji519d4c12020-05-29 15:22:31 -0400320
321 // BubbleStackView is forced LTR, but we want to respect the locale for expanded view layout
322 // so the Manage button appears on the right.
323 setLayoutDirection(LAYOUT_DIRECTION_LOCALE);
Mady Mellore8e07712019-01-23 12:45:33 -0800324 }
325
Mark Renoufdb861c92019-07-26 13:58:17 -0400326 private String getBubbleKey() {
327 return mBubble != null ? mBubble.getKey() : "null";
328 }
329
Joshua Tsuji6855cab2020-04-16 01:05:39 -0400330 void setManageClickListener(OnClickListener manageClickListener) {
331 findViewById(R.id.settings_button).setOnClickListener(manageClickListener);
332 }
333
334 /**
335 * Updates the ActivityView's obscured touchable region. This calls onLocationChanged, which
336 * results in a call to {@link BubbleStackView#subtractObscuredTouchableRegion}. This is useful
337 * if a view has been added or removed from on top of the ActivityView, such as the manage menu.
338 */
339 void updateObscuredTouchableRegion() {
Mady Mellor64db51a2020-05-27 19:00:14 -0700340 if (mActivityView != null) {
341 mActivityView.onLocationChanged();
342 }
Joshua Tsuji6855cab2020-04-16 01:05:39 -0400343 }
344
Mark Renouf34d04f32019-05-13 15:53:18 -0400345 void applyThemeAttrs() {
Lyn Han61bfdb32019-12-10 14:34:12 -0800346 final TypedArray ta = mContext.obtainStyledAttributes(
Joshua Tsujidd082b62020-05-20 22:26:24 -0400347 new int[] {android.R.attr.dialogCornerRadius});
348 float cornerRadius = ta.getDimensionPixelSize(0, 0);
Mark Renouf34d04f32019-05-13 15:53:18 -0400349 ta.recycle();
350
Lyn Hanedb55e22020-02-12 18:55:39 -0800351 if (mActivityView != null && ScreenDecorationsUtils.supportsRoundedCornersOnWindows(
352 mContext.getResources())) {
Mark Renouf34d04f32019-05-13 15:53:18 -0400353 mActivityView.setCornerRadius(cornerRadius);
354 }
355 }
356
Joshua Tsuji847b1d422020-06-01 13:42:19 -0400357 /**
358 * Hides the IME if it's showing. This is currently done by dispatching a back press to the AV.
359 */
360 void hideImeIfVisible() {
361 if (mKeyboardVisible) {
362 performBackPressIfNeeded();
363 }
364 }
365
Mady Mellor5d8f1402019-02-21 18:23:52 -0800366 @Override
367 protected void onDetachedFromWindow() {
368 super.onDetachedFromWindow();
Joshua Tsuji847b1d422020-06-01 13:42:19 -0400369 hideImeIfVisible();
Mady Mellor5d8f1402019-02-21 18:23:52 -0800370 mKeyboardVisible = false;
371 mNeedsNewHeight = false;
372 if (mActivityView != null) {
Mady Melloraf02fa42020-03-04 14:22:16 -0800373 if (sNewInsetsMode == NEW_INSETS_MODE_FULL) {
Yunfan Chenb83940c2020-04-06 16:43:09 +0900374 setImeWindowToDisplay(0, 0);
Mady Melloraf02fa42020-03-04 14:22:16 -0800375 } else {
376 mActivityView.setForwardedInsets(Insets.of(0, 0, 0, 0));
377 }
Mady Mellor5d8f1402019-02-21 18:23:52 -0800378 }
Mark Renoufdb861c92019-07-26 13:58:17 -0400379 if (DEBUG_BUBBLE_EXPANDED_VIEW) {
380 Log.d(TAG, "onDetachedFromWindow: bubble=" + getBubbleKey());
381 }
Mady Mellor5d8f1402019-02-21 18:23:52 -0800382 }
383
384 /**
Issei Suzukicac2a502019-04-16 16:52:50 +0200385 * Set visibility of contents in the expanded state.
386 *
387 * @param visibility {@code true} if the contents should be visible on the screen.
388 *
389 * Note that this contents visibility doesn't affect visibility at {@link android.view.View},
390 * and setting {@code false} actually means rendering the contents in transparent.
391 */
392 void setContentVisibility(boolean visibility) {
Mark Renoufdb861c92019-07-26 13:58:17 -0400393 if (DEBUG_BUBBLE_EXPANDED_VIEW) {
394 Log.d(TAG, "setContentVisibility: visibility=" + visibility
395 + " bubble=" + getBubbleKey());
396 }
Issei Suzukicac2a502019-04-16 16:52:50 +0200397 final float alpha = visibility ? 1f : 0f;
398 mPointerView.setAlpha(alpha);
399 if (mActivityView != null) {
400 mActivityView.setAlpha(alpha);
401 }
402 }
403
404 /**
Mady Mellor5d8f1402019-02-21 18:23:52 -0800405 * Called by {@link BubbleStackView} when the insets for the expanded state should be updated.
406 * This should be done post-move and post-animation.
407 */
408 void updateInsets(WindowInsets insets) {
409 if (usingActivityView()) {
Mady Mellor8fe411d2019-08-16 11:27:53 -0700410 int[] screenLoc = mActivityView.getLocationOnScreen();
411 final int activityViewBottom = screenLoc[1] + mActivityView.getHeight();
Mady Mellor9be3bed2019-08-21 17:26:26 -0700412 final int keyboardTop = mDisplaySize.y - Math.max(insets.getSystemWindowInsetBottom(),
413 insets.getDisplayCutout() != null
414 ? insets.getDisplayCutout().getSafeInsetBottom()
415 : 0);
Mady Mellor8fe411d2019-08-16 11:27:53 -0700416 final int insetsBottom = Math.max(activityViewBottom - keyboardTop, 0);
Jorim Jaggi924ef752020-01-29 17:26:55 +0100417
Jorim Jaggi924ef752020-01-29 17:26:55 +0100418 if (sNewInsetsMode == NEW_INSETS_MODE_FULL) {
Yunfan Chenb83940c2020-04-06 16:43:09 +0900419 setImeWindowToDisplay(getWidth(), insetsBottom);
Jorim Jaggi924ef752020-01-29 17:26:55 +0100420 } else {
421 mActivityView.setForwardedInsets(Insets.of(0, 0, 0, insetsBottom));
422 }
Mady Mellor5d8f1402019-02-21 18:23:52 -0800423 }
424 }
425
Yunfan Chenb83940c2020-04-06 16:43:09 +0900426 private void setImeWindowToDisplay(int w, int h) {
427 if (getVirtualDisplayId() == INVALID_DISPLAY) {
428 return;
429 }
430 if (h == 0 || w == 0) {
431 if (mImeShowing) {
432 mVirtualImeView.setVisibility(GONE);
433 mImeShowing = false;
434 }
435 return;
436 }
437 final Context virtualDisplayContext = mContext.createDisplayContext(
438 getVirtualDisplay().getDisplay());
439
440 if (mVirtualDisplayWindowManager == null) {
441 mVirtualDisplayWindowManager =
442 (WindowManager) virtualDisplayContext.getSystemService(Context.WINDOW_SERVICE);
443 }
444 if (mVirtualImeView == null) {
445 mVirtualImeView = new View(virtualDisplayContext);
446 mVirtualImeView.setVisibility(VISIBLE);
447 mVirtualDisplayWindowManager.addView(mVirtualImeView,
448 getVirtualImeViewAttrs(w, h));
449 } else {
450 mVirtualDisplayWindowManager.updateViewLayout(mVirtualImeView,
451 getVirtualImeViewAttrs(w, h));
452 mVirtualImeView.setVisibility(VISIBLE);
453 }
454
455 mImeShowing = true;
456 }
457
458 private WindowManager.LayoutParams getVirtualImeViewAttrs(int w, int h) {
459 // To use TYPE_NAVIGATION_BAR_PANEL instead of TYPE_IME_BAR to bypass the IME window type
460 // token check when adding the window.
461 final WindowManager.LayoutParams attrs =
462 new WindowManager.LayoutParams(w, h, TYPE_NAVIGATION_BAR_PANEL,
463 FLAG_LAYOUT_NO_LIMITS | FLAG_NOT_FOCUSABLE | FLAG_NOT_TOUCHABLE,
464 TRANSPARENT);
465 attrs.gravity = Gravity.BOTTOM;
466 attrs.setTitle(WINDOW_TITLE);
467 attrs.token = new Binder();
468 attrs.providesInsetsTypes = new int[]{ITYPE_IME};
469 attrs.alpha = 0.0f;
470 return attrs;
471 }
472
Mady Mellor247ca2c2019-12-02 16:18:59 -0800473 void setStackView(BubbleStackView stackView) {
Mady Mellor9801e852019-01-22 14:50:28 -0800474 mStackView = stackView;
Mady Mellor6d002032019-02-13 13:45:17 -0800475 }
476
Lyn Hanb58c7562020-01-07 14:29:20 -0800477 public void setOverflow(boolean overflow) {
478 mIsOverflow = overflow;
479
480 Intent target = new Intent(mContext, BubbleOverflowActivity.class);
481 mPendingIntent = PendingIntent.getActivity(mContext, /* requestCode */ 0,
482 target, PendingIntent.FLAG_UPDATE_CURRENT);
483 mSettingsIcon.setVisibility(GONE);
484 }
485
Mady Mellor6d002032019-02-13 13:45:17 -0800486 /**
Mady Mellor247ca2c2019-12-02 16:18:59 -0800487 * Sets the bubble used to populate this view.
Mady Mellor6d002032019-02-13 13:45:17 -0800488 */
Mady Mellor247ca2c2019-12-02 16:18:59 -0800489 void update(Bubble bubble) {
490 if (DEBUG_BUBBLE_EXPANDED_VIEW) {
491 Log.d(TAG, "update: bubble=" + (bubble != null ? bubble.getKey() : "null"));
492 }
493 boolean isNew = mBubble == null;
Lyn Hanb58c7562020-01-07 14:29:20 -0800494 if (isNew || bubble != null && bubble.getKey().equals(mBubble.getKey())) {
Mady Mellor247ca2c2019-12-02 16:18:59 -0800495 mBubble = bubble;
496 mSettingsIcon.setContentDescription(getResources().getString(
497 R.string.bubbles_settings_button_description, bubble.getAppName()));
498
Lyn Han670fa802020-05-05 12:56:33 -0700499 mSettingsIcon.setAccessibilityDelegate(
500 new AccessibilityDelegate() {
501 @Override
502 public void onInitializeAccessibilityNodeInfo(View host,
503 AccessibilityNodeInfo info) {
504 super.onInitializeAccessibilityNodeInfo(host, info);
505 // On focus, have TalkBack say
506 // "Actions available. Use swipe up then right to view."
507 // in addition to the default "double tap to activate".
508 mStackView.setupLocalMenu(info);
509 }
510 });
511
Mady Mellor247ca2c2019-12-02 16:18:59 -0800512 if (isNew) {
Lyn Hanb58c7562020-01-07 14:29:20 -0800513 mPendingIntent = mBubble.getBubbleIntent();
514 if (mPendingIntent != null || mBubble.getShortcutInfo() != null) {
Mady Mellor247ca2c2019-12-02 16:18:59 -0800515 setContentVisibility(false);
516 mActivityView.setVisibility(VISIBLE);
517 }
518 }
519 applyThemeAttrs();
520 } else {
521 Log.w(TAG, "Trying to update entry with different key, new bubble: "
522 + bubble.getKey() + " old bubble: " + bubble.getKey());
523 }
524 }
525
526 /**
527 * Lets activity view know it should be shown / populated with activity content.
528 */
529 void populateExpandedView() {
Mark Renoufdb861c92019-07-26 13:58:17 -0400530 if (DEBUG_BUBBLE_EXPANDED_VIEW) {
531 Log.d(TAG, "populateExpandedView: "
532 + "bubble=" + getBubbleKey());
533 }
534
Mady Mellor5029fa62019-03-05 12:16:21 -0800535 if (usingActivityView()) {
536 mActivityView.setCallback(mStateCallback);
537 } else {
Issei Suzukia91f3962019-06-07 11:48:23 +0200538 Log.e(TAG, "Cannot populate expanded view.");
Mady Mellor5029fa62019-03-05 12:16:21 -0800539 }
Mady Mellor9801e852019-01-22 14:50:28 -0800540 }
541
Mark Renouf041d7262019-02-06 12:09:41 -0500542 boolean performBackPressIfNeeded() {
Mady Mellor323fb0b2019-03-25 12:15:22 -0700543 if (!usingActivityView()) {
Mark Renouf041d7262019-02-06 12:09:41 -0500544 return false;
545 }
546 mActivityView.performBackPress();
547 return true;
548 }
549
Mady Mellorfe7ec032019-01-30 17:32:49 -0800550 void updateHeight() {
Mark Renoufdb861c92019-07-26 13:58:17 -0400551 if (DEBUG_BUBBLE_EXPANDED_VIEW) {
552 Log.d(TAG, "updateHeight: bubble=" + getBubbleKey());
553 }
Mady Mellorfe7ec032019-01-30 17:32:49 -0800554 if (usingActivityView()) {
Lyn Hancd4f87e2020-02-19 20:33:45 -0800555 float desiredHeight = mOverflowHeight;
Lyn Hanb58c7562020-01-07 14:29:20 -0800556 if (!mIsOverflow) {
557 desiredHeight = Math.max(mBubble.getDesiredHeight(mContext), mMinHeight);
558 }
Mady Mellor8fe411d2019-08-16 11:27:53 -0700559 float height = Math.min(desiredHeight, getMaxExpandedHeight());
Lyn Hancd4f87e2020-02-19 20:33:45 -0800560 height = Math.max(height, mIsOverflow? mOverflowHeight : mMinHeight);
Mady Mellorfe7ec032019-01-30 17:32:49 -0800561 LayoutParams lp = (LayoutParams) mActivityView.getLayoutParams();
Lyn Hancd4f87e2020-02-19 20:33:45 -0800562 mNeedsNewHeight = lp.height != height;
Mady Mellor5d8f1402019-02-21 18:23:52 -0800563 if (!mKeyboardVisible) {
564 // If the keyboard is visible... don't adjust the height because that will cause
565 // a configuration change and the keyboard will be lost.
Mady Mellor7af771a2019-03-07 15:04:54 -0800566 lp.height = (int) height;
Mady Mellor5d8f1402019-02-21 18:23:52 -0800567 mActivityView.setLayoutParams(lp);
568 mNeedsNewHeight = false;
569 }
Mark Renoufdb861c92019-07-26 13:58:17 -0400570 if (DEBUG_BUBBLE_EXPANDED_VIEW) {
571 Log.d(TAG, "updateHeight: bubble=" + getBubbleKey() + " height=" + height
572 + " mNeedsNewHeight=" + mNeedsNewHeight);
573 }
Mady Mellorfe7ec032019-01-30 17:32:49 -0800574 }
575 }
576
Mady Mellora96c9ed2019-06-07 12:55:26 -0700577 private int getMaxExpandedHeight() {
Mady Mellor9be3bed2019-08-21 17:26:26 -0700578 mWindowManager.getDefaultDisplay().getRealSize(mDisplaySize);
Mady Mellora96c9ed2019-06-07 12:55:26 -0700579 int[] windowLocation = mActivityView.getLocationOnScreen();
Mady Mellor8fe411d2019-08-16 11:27:53 -0700580 int bottomInset = getRootWindowInsets() != null
581 ? getRootWindowInsets().getStableInsetBottom()
582 : 0;
Lyn Han0b955ed2020-01-31 09:47:35 -0800583 return mDisplaySize.y - windowLocation[1] - mSettingsIconHeight - mPointerHeight
Mady Mellor8fe411d2019-08-16 11:27:53 -0700584 - mPointerMargin - bottomInset;
Mady Mellora96c9ed2019-06-07 12:55:26 -0700585 }
586
Mady Mellor47b11e32019-07-11 19:06:21 -0700587 /**
Mady Mellor3dff9e62019-02-05 18:12:53 -0800588 * Update appearance of the expanded view being displayed.
589 */
590 public void updateView() {
Mark Renoufdb861c92019-07-26 13:58:17 -0400591 if (DEBUG_BUBBLE_EXPANDED_VIEW) {
592 Log.d(TAG, "updateView: bubble="
593 + getBubbleKey());
594 }
Mady Mellor3dff9e62019-02-05 18:12:53 -0800595 if (usingActivityView()
596 && mActivityView.getVisibility() == VISIBLE
597 && mActivityView.isAttachedToWindow()) {
598 mActivityView.onLocationChanged();
Mady Mellor3dff9e62019-02-05 18:12:53 -0800599 }
Mady Mellorfe7ec032019-01-30 17:32:49 -0800600 updateHeight();
Mady Mellor3dff9e62019-02-05 18:12:53 -0800601 }
602
603 /**
Mady Mellordea7ecf2018-12-10 15:47:40 -0800604 * Set the x position that the tip of the triangle should point to.
605 */
Mady Mellor3dff9e62019-02-05 18:12:53 -0800606 public void setPointerPosition(float x) {
Lyn Han9a2f5cf2019-05-23 11:01:41 -0700607 float halfPointerWidth = mPointerWidth / 2f;
608 float pointerLeft = x - halfPointerWidth;
609 mPointerView.setTranslationX(pointerLeft);
Lyn Hanf74ba672019-05-20 16:08:48 -0700610 mPointerView.setVisibility(VISIBLE);
Mady Mellordea7ecf2018-12-10 15:47:40 -0800611 }
612
613 /**
Mady Mellor5a3e94b2020-02-07 12:16:21 -0800614 * Position of the manage button displayed in the expanded view. Used for placing user
615 * education about the manage button.
616 */
Joshua Tsuji6855cab2020-04-16 01:05:39 -0400617 public void getManageButtonBoundsOnScreen(Rect rect) {
618 mSettingsIcon.getBoundsOnScreen(rect);
Mady Mellor5a3e94b2020-02-07 12:16:21 -0800619 }
620
621 /**
Mady Mellor3dff9e62019-02-05 18:12:53 -0800622 * Removes and releases an ActivityView if one was previously created for this bubble.
Mady Mellordea7ecf2018-12-10 15:47:40 -0800623 */
Mady Mellor94d94a72019-03-05 18:16:59 -0800624 public void cleanUpExpandedState() {
Mark Renoufdb861c92019-07-26 13:58:17 -0400625 if (DEBUG_BUBBLE_EXPANDED_VIEW) {
626 Log.d(TAG, "cleanUpExpandedState: mActivityViewStatus=" + mActivityViewStatus
627 + ", bubble=" + getBubbleKey());
628 }
Mady Mellor3dff9e62019-02-05 18:12:53 -0800629 if (mActivityView == null) {
Mark Renouf89b1a4a2018-12-04 14:59:45 -0500630 return;
631 }
Issei Suzuki734bc942019-06-05 13:59:52 +0200632 switch (mActivityViewStatus) {
633 case INITIALIZED:
634 case ACTIVITY_STARTED:
635 mActivityView.release();
Mady Mellordea7ecf2018-12-10 15:47:40 -0800636 }
Mark Renouf5c732b32019-06-12 15:14:54 -0400637 if (mTaskId != -1) {
638 try {
639 ActivityTaskManager.getService().removeTask(mTaskId);
640 } catch (RemoteException e) {
641 Log.w(TAG, "Failed to remove taskId " + mTaskId);
642 }
643 mTaskId = -1;
Mady Mellordea7ecf2018-12-10 15:47:40 -0800644 }
Mark Renouf28c250d2019-02-25 16:47:34 -0500645 removeView(mActivityView);
Mark Renouf5c732b32019-06-12 15:14:54 -0400646
Mady Mellor3dff9e62019-02-05 18:12:53 -0800647 mActivityView = null;
Issei Suzuki734bc942019-06-05 13:59:52 +0200648 }
649
650 /**
651 * Called when the last task is removed from a {@link android.hardware.display.VirtualDisplay}
652 * which {@link ActivityView} uses.
653 */
654 void notifyDisplayEmpty() {
Mark Renoufdb861c92019-07-26 13:58:17 -0400655 if (DEBUG_BUBBLE_EXPANDED_VIEW) {
656 Log.d(TAG, "notifyDisplayEmpty: bubble="
657 + getBubbleKey()
658 + " mActivityViewStatus=" + mActivityViewStatus);
659 }
Issei Suzuki734bc942019-06-05 13:59:52 +0200660 if (mActivityViewStatus == ActivityViewStatus.ACTIVITY_STARTED) {
661 mActivityViewStatus = ActivityViewStatus.INITIALIZED;
662 }
Mady Mellordea7ecf2018-12-10 15:47:40 -0800663 }
664
Mady Mellor3dff9e62019-02-05 18:12:53 -0800665 private boolean usingActivityView() {
Lyn Hanb58c7562020-01-07 14:29:20 -0800666 return (mPendingIntent != null || mBubble.getShortcutInfo() != null)
Mady Mellor2ac2d3a2020-01-08 17:18:54 -0800667 && mActivityView != null;
Mady Mellor3dff9e62019-02-05 18:12:53 -0800668 }
669
Mady Mellor390bff42019-04-05 15:09:01 -0700670 /**
671 * @return the display id of the virtual display.
672 */
673 public int getVirtualDisplayId() {
674 if (usingActivityView()) {
675 return mActivityView.getVirtualDisplayId();
676 }
677 return INVALID_DISPLAY;
678 }
Yunfan Chenb83940c2020-04-06 16:43:09 +0900679
680 private VirtualDisplay getVirtualDisplay() {
681 if (usingActivityView()) {
682 return mActivityView.getVirtualDisplay();
683 }
684 return null;
685 }
Mady Mellordea7ecf2018-12-10 15:47:40 -0800686}