blob: 2b9b1716cb1862a8c653bb4079f8e6f87955307e [file] [log] [blame]
Winson Chung15504af2016-11-02 18:11:36 -07001/*
2 * Copyright (C) 2016 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.pip.phone;
18
Winson Chunga556fe62017-12-19 14:46:23 -080019import static android.content.Intent.FLAG_ACTIVITY_CLEAR_TASK;
20import static android.content.Intent.FLAG_ACTIVITY_NEW_TASK;
21import static android.provider.Settings.ACTION_PICTURE_IN_PICTURE_SETTINGS;
Rhed Jao5e00c982018-11-27 19:41:05 +080022import static android.view.accessibility.AccessibilityManager.FLAG_CONTENT_CONTROLS;
23import static android.view.accessibility.AccessibilityManager.FLAG_CONTENT_ICONS;
Winson Chunga556fe62017-12-19 14:46:23 -080024
Winson Chunge7a3d222017-03-09 13:26:45 -080025import static com.android.systemui.pip.phone.PipMenuActivityController.EXTRA_ACTIONS;
Winson Chung0f873de2017-03-30 10:26:48 -070026import static com.android.systemui.pip.phone.PipMenuActivityController.EXTRA_ALLOW_TIMEOUT;
Winson Chunge7a3d222017-03-09 13:26:45 -080027import static com.android.systemui.pip.phone.PipMenuActivityController.EXTRA_CONTROLLER_MESSENGER;
Winson Chung853c99a2017-03-21 22:16:42 -070028import static com.android.systemui.pip.phone.PipMenuActivityController.EXTRA_DISMISS_FRACTION;
Mady Mellor637cd482017-03-21 10:39:42 -070029import static com.android.systemui.pip.phone.PipMenuActivityController.EXTRA_MENU_STATE;
Gus Prevasab336792018-11-14 13:52:20 -050030import static com.android.systemui.pip.phone.PipMenuActivityController.EXTRA_MOVEMENT_BOUNDS;
Winson Chung6b88baf2017-03-16 17:10:21 -070031import static com.android.systemui.pip.phone.PipMenuActivityController.EXTRA_STACK_BOUNDS;
Gus Prevasab336792018-11-14 13:52:20 -050032import static com.android.systemui.pip.phone.PipMenuActivityController.EXTRA_WILL_RESIZE_MENU;
Mady Mellor637cd482017-03-21 10:39:42 -070033import static com.android.systemui.pip.phone.PipMenuActivityController.MENU_STATE_CLOSE;
34import static com.android.systemui.pip.phone.PipMenuActivityController.MENU_STATE_FULL;
Gus Prevasab336792018-11-14 13:52:20 -050035import static com.android.systemui.pip.phone.PipMenuActivityController.MENU_STATE_NONE;
Mady Mellor637cd482017-03-21 10:39:42 -070036
Winson Chungc75ffe82016-12-16 16:20:16 -080037import android.animation.Animator;
38import android.animation.AnimatorListenerAdapter;
Mady Mellor637cd482017-03-21 10:39:42 -070039import android.animation.AnimatorSet;
Winson Chungc75ffe82016-12-16 16:20:16 -080040import android.animation.ObjectAnimator;
Mady Mellor81d40612017-03-10 15:14:10 -080041import android.animation.ValueAnimator;
Winson Chung15504af2016-11-02 18:11:36 -070042import android.annotation.Nullable;
43import android.app.Activity;
44import android.app.ActivityManager;
Winson Chung9c2a6862017-02-03 12:11:00 -080045import android.app.PendingIntent.CanceledException;
Winson Chunga29eb982016-12-14 12:01:27 -080046import android.app.RemoteAction;
Winson Chunga556fe62017-12-19 14:46:23 -080047import android.content.ComponentName;
Winson Chung15504af2016-11-02 18:11:36 -070048import android.content.Intent;
Winson Chunga29eb982016-12-14 12:01:27 -080049import android.content.pm.ParceledListSlice;
Winson Chung56fe8832017-02-03 14:37:53 -080050import android.graphics.Color;
Winson Chunge7a3d222017-03-09 13:26:45 -080051import android.graphics.Rect;
Mady Mellor81d40612017-03-10 15:14:10 -080052import android.graphics.drawable.ColorDrawable;
53import android.graphics.drawable.Drawable;
Winson Chunga556fe62017-12-19 14:46:23 -080054import android.net.Uri;
Winson Chung15504af2016-11-02 18:11:36 -070055import android.os.Bundle;
56import android.os.Handler;
57import android.os.Message;
58import android.os.Messenger;
59import android.os.RemoteException;
Winson Chunga556fe62017-12-19 14:46:23 -080060import android.os.UserHandle;
Winson Chung15504af2016-11-02 18:11:36 -070061import android.util.Log;
Winson Chunga556fe62017-12-19 14:46:23 -080062import android.util.Pair;
Ben Lin8e90d0a2020-02-26 14:57:14 -080063import android.view.KeyEvent;
Winson Chunga29eb982016-12-14 12:01:27 -080064import android.view.LayoutInflater;
Winson Chungc75ffe82016-12-16 16:20:16 -080065import android.view.MotionEvent;
Winson Chung15504af2016-11-02 18:11:36 -070066import android.view.View;
Winson Chunga29eb982016-12-14 12:01:27 -080067import android.view.ViewGroup;
Winson Chungc75ffe82016-12-16 16:20:16 -080068import android.view.WindowManager.LayoutParams;
Rhed Jao5e00c982018-11-27 19:41:05 +080069import android.view.accessibility.AccessibilityManager;
Mady Mellora7f69742017-02-03 11:00:20 -080070import android.widget.FrameLayout;
Hongwei Wanga6e034e2019-06-20 13:18:43 -070071import android.widget.ImageButton;
Mady Mellora7f69742017-02-03 11:00:20 -080072import android.widget.LinearLayout;
Winson Chunga29eb982016-12-14 12:01:27 -080073
Winson Chungc75ffe82016-12-16 16:20:16 -080074import com.android.systemui.Interpolators;
Winson Chung15504af2016-11-02 18:11:36 -070075import com.android.systemui.R;
76
Winson Chunga29eb982016-12-14 12:01:27 -080077import java.util.ArrayList;
Winson Chung26db3972017-03-23 15:17:01 -070078import java.util.Collections;
Winson Chunga29eb982016-12-14 12:01:27 -080079import java.util.List;
80
Winson Chung15504af2016-11-02 18:11:36 -070081/**
82 * Translucent activity that gets started on top of a task in PIP to allow the user to control it.
83 */
84public class PipMenuActivity extends Activity {
85
86 private static final String TAG = "PipMenuActivity";
87
Winson Chungc75ffe82016-12-16 16:20:16 -080088 public static final int MESSAGE_SHOW_MENU = 1;
Mady Mellora7f69742017-02-03 11:00:20 -080089 public static final int MESSAGE_POKE_MENU = 2;
90 public static final int MESSAGE_HIDE_MENU = 3;
91 public static final int MESSAGE_UPDATE_ACTIONS = 4;
Mady Mellor81d40612017-03-10 15:14:10 -080092 public static final int MESSAGE_UPDATE_DISMISS_FRACTION = 5;
Winson Chung34488242017-04-26 15:53:51 -070093 public static final int MESSAGE_ANIMATION_ENDED = 6;
Arthur Hungd8961972019-10-14 17:00:17 +080094 public static final int MESSAGE_POINTER_EVENT = 7;
Winson Chung15504af2016-11-02 18:11:36 -070095
Rhed Jao5e00c982018-11-27 19:41:05 +080096 private static final int INITIAL_DISMISS_DELAY = 3500;
97 private static final int POST_INTERACTION_DISMISS_DELAY = 2000;
Winson Chungc75ffe82016-12-16 16:20:16 -080098 private static final long MENU_FADE_DURATION = 125;
Winson Chung15504af2016-11-02 18:11:36 -070099
Mady Mellor81d40612017-03-10 15:14:10 -0800100 private static final float MENU_BACKGROUND_ALPHA = 0.3f;
Mady Mellor60421c92017-03-29 15:27:37 -0700101 private static final float DISMISS_BACKGROUND_ALPHA = 0.6f;
Mady Mellor81d40612017-03-10 15:14:10 -0800102
Winson Chung6b88baf2017-03-16 17:10:21 -0700103 private static final float DISABLED_ACTION_ALPHA = 0.54f;
104
Mady Mellor637cd482017-03-21 10:39:42 -0700105 private int mMenuState;
Ben Lin559ea4d2019-11-06 12:36:41 -0800106 private boolean mResize = true;
Winson Chung379b9ab2017-04-04 11:57:36 -0700107 private boolean mAllowMenuTimeout = true;
Winson Chung34488242017-04-26 15:53:51 -0700108 private boolean mAllowTouches = true;
Winson Chung87e5d552017-04-05 11:49:38 -0700109
Winson Chungc75ffe82016-12-16 16:20:16 -0800110 private final List<RemoteAction> mActions = new ArrayList<>();
Winson Chung87e5d552017-04-05 11:49:38 -0700111
Rhed Jao5e00c982018-11-27 19:41:05 +0800112 private AccessibilityManager mAccessibilityManager;
Mady Mellor81d40612017-03-10 15:14:10 -0800113 private View mViewRoot;
114 private Drawable mBackgroundDrawable;
Winson Chungc75ffe82016-12-16 16:20:16 -0800115 private View mMenuContainer;
Mady Mellora7f69742017-02-03 11:00:20 -0800116 private LinearLayout mActionsGroup;
Winson Chunga556fe62017-12-19 14:46:23 -0800117 private View mSettingsButton;
Winson Chunga29eb982016-12-14 12:01:27 -0800118 private View mDismissButton;
Mady Mellora7f69742017-02-03 11:00:20 -0800119 private int mBetweenActionPaddingLand;
Winson Chunga29eb982016-12-14 12:01:27 -0800120
Mady Mellor637cd482017-03-21 10:39:42 -0700121 private AnimatorSet mMenuContainerAnimator;
122
Mady Mellor81d40612017-03-10 15:14:10 -0800123 private ValueAnimator.AnimatorUpdateListener mMenuBgUpdateListener =
124 new ValueAnimator.AnimatorUpdateListener() {
125 @Override
126 public void onAnimationUpdate(ValueAnimator animation) {
127 final float alpha = (float) animation.getAnimatedValue();
128 mBackgroundDrawable.setAlpha((int) (MENU_BACKGROUND_ALPHA*alpha*255));
129 }
130 };
Winson Chungc75ffe82016-12-16 16:20:16 -0800131
Winson Chunga29eb982016-12-14 12:01:27 -0800132 private Handler mHandler = new Handler();
Winson Chung15504af2016-11-02 18:11:36 -0700133 private Messenger mToControllerMessenger;
134 private Messenger mMessenger = new Messenger(new Handler() {
135 @Override
136 public void handleMessage(Message msg) {
137 switch (msg.what) {
Winson Chung853c99a2017-03-21 22:16:42 -0700138 case MESSAGE_SHOW_MENU: {
139 final Bundle data = (Bundle) msg.obj;
Mady Mellor637cd482017-03-21 10:39:42 -0700140 showMenu(data.getInt(EXTRA_MENU_STATE),
141 data.getParcelable(EXTRA_STACK_BOUNDS),
Winson Chung0f873de2017-03-30 10:26:48 -0700142 data.getParcelable(EXTRA_MOVEMENT_BOUNDS),
Winson Chungbb787442017-09-01 11:33:47 -0700143 data.getBoolean(EXTRA_ALLOW_TIMEOUT),
144 data.getBoolean(EXTRA_WILL_RESIZE_MENU));
Winson Chungc75ffe82016-12-16 16:20:16 -0800145 break;
Winson Chung853c99a2017-03-21 22:16:42 -0700146 }
Mady Mellora7f69742017-02-03 11:00:20 -0800147 case MESSAGE_POKE_MENU:
148 cancelDelayedFinish();
149 break;
Winson Chungc75ffe82016-12-16 16:20:16 -0800150 case MESSAGE_HIDE_MENU:
Winson Chung2dbcf092018-10-24 13:00:41 -0700151 hideMenu((Runnable) msg.obj);
Winson Chungc75ffe82016-12-16 16:20:16 -0800152 break;
Winson Chung853c99a2017-03-21 22:16:42 -0700153 case MESSAGE_UPDATE_ACTIONS: {
154 final Bundle data = (Bundle) msg.obj;
Winson Chung26db3972017-03-23 15:17:01 -0700155 final ParceledListSlice actions = data.getParcelable(EXTRA_ACTIONS);
156 setActions(data.getParcelable(EXTRA_STACK_BOUNDS), actions != null
157 ? actions.getList() : Collections.EMPTY_LIST);
Winson Chunga29eb982016-12-14 12:01:27 -0800158 break;
Winson Chung853c99a2017-03-21 22:16:42 -0700159 }
160 case MESSAGE_UPDATE_DISMISS_FRACTION: {
161 final Bundle data = (Bundle) msg.obj;
162 updateDismissFraction(data.getFloat(EXTRA_DISMISS_FRACTION));
Mady Mellor81d40612017-03-10 15:14:10 -0800163 break;
Winson Chung853c99a2017-03-21 22:16:42 -0700164 }
Winson Chung34488242017-04-26 15:53:51 -0700165 case MESSAGE_ANIMATION_ENDED: {
166 mAllowTouches = true;
167 break;
168 }
Arthur Hungd52d1192019-08-30 16:06:59 +0800169
Arthur Hungd8961972019-10-14 17:00:17 +0800170 case MESSAGE_POINTER_EVENT: {
Arthur Hungd52d1192019-08-30 16:06:59 +0800171 final MotionEvent ev = (MotionEvent) msg.obj;
Arthur Hungd8961972019-10-14 17:00:17 +0800172 dispatchPointerEvent(ev);
Arthur Hungd52d1192019-08-30 16:06:59 +0800173 break;
174 }
Winson Chung15504af2016-11-02 18:11:36 -0700175 }
176 }
177 });
178
179 private final Runnable mFinishRunnable = new Runnable() {
180 @Override
181 public void run() {
Winson Chungc75ffe82016-12-16 16:20:16 -0800182 hideMenu();
Winson Chung15504af2016-11-02 18:11:36 -0700183 }
184 };
185
186 @Override
187 protected void onCreate(@Nullable Bundle savedInstanceState) {
Winson Chungc75ffe82016-12-16 16:20:16 -0800188 // Set the flags to allow us to watch for outside touches and also hide the menu and start
189 // manipulating the PIP in the same touch gesture
Arthur Hungd52d1192019-08-30 16:06:59 +0800190 getWindow().addFlags(LayoutParams.FLAG_WATCH_OUTSIDE_TOUCH);
Winson Chungc75ffe82016-12-16 16:20:16 -0800191
Winson Chung15504af2016-11-02 18:11:36 -0700192 super.onCreate(savedInstanceState);
Winson Chung15504af2016-11-02 18:11:36 -0700193 setContentView(R.layout.pip_menu_activity);
Winson Chunga29eb982016-12-14 12:01:27 -0800194
Rhed Jao5e00c982018-11-27 19:41:05 +0800195 mAccessibilityManager = getSystemService(AccessibilityManager.class);
Mady Mellor81d40612017-03-10 15:14:10 -0800196 mBackgroundDrawable = new ColorDrawable(Color.BLACK);
197 mBackgroundDrawable.setAlpha(0);
198 mViewRoot = findViewById(R.id.background);
199 mViewRoot.setBackground(mBackgroundDrawable);
200 mMenuContainer = findViewById(R.id.menu_container);
201 mMenuContainer.setAlpha(0);
Winson Chunga556fe62017-12-19 14:46:23 -0800202 mSettingsButton = findViewById(R.id.settings);
203 mSettingsButton.setAlpha(0);
204 mSettingsButton.setOnClickListener((v) -> {
Tracy Zhou2c6117a2018-06-21 11:32:43 -0700205 if (v.getAlpha() != 0) {
206 showSettings();
207 }
Winson Chunga556fe62017-12-19 14:46:23 -0800208 });
Winson Chunga29eb982016-12-14 12:01:27 -0800209 mDismissButton = findViewById(R.id.dismiss);
Mady Mellor637cd482017-03-21 10:39:42 -0700210 mDismissButton.setAlpha(0);
Hongwei Wang10649502019-06-26 13:39:24 -0700211 mDismissButton.setOnClickListener(v -> dismissPip());
Arthur Hungd52d1192019-08-30 16:06:59 +0800212 findViewById(R.id.expand_button).setOnClickListener(v -> {
213 if (mMenuContainer.getAlpha() != 0) {
214 expandPip();
215 }
216 });
Alan Viverette51efddb2017-04-05 10:00:01 -0400217 mActionsGroup = findViewById(R.id.actions_group);
Mady Mellora7f69742017-02-03 11:00:20 -0800218 mBetweenActionPaddingLand = getResources().getDimensionPixelSize(
219 R.dimen.pip_between_action_padding_land);
Winson Chungc75ffe82016-12-16 16:20:16 -0800220
Winson Chunge7a3d222017-03-09 13:26:45 -0800221 updateFromIntent(getIntent());
Phil Weaverf00cd142017-03-03 13:44:00 -0800222 setTitle(R.string.pip_menu_title);
Wale Ogunwale5e8ecb02017-04-14 13:15:55 -0700223 setDisablePreviewScreenshots(true);
Arthur Hungd8961972019-10-14 17:00:17 +0800224
225 // Hide without an animation.
226 getWindow().setExitTransition(null);
Winson Chungc75ffe82016-12-16 16:20:16 -0800227 }
228
229 @Override
Ben Lin8e90d0a2020-02-26 14:57:14 -0800230 public boolean onKeyUp(int keyCode, KeyEvent event) {
231 if (keyCode == KeyEvent.KEYCODE_ESCAPE) {
232 hideMenu();
233 return true;
234 }
235 return super.onKeyUp(keyCode, event);
236 }
237
238 @Override
Winson Chungc75ffe82016-12-16 16:20:16 -0800239 protected void onNewIntent(Intent intent) {
240 super.onNewIntent(intent);
Winson Chunge7a3d222017-03-09 13:26:45 -0800241 updateFromIntent(intent);
Winson Chung15504af2016-11-02 18:11:36 -0700242 }
243
244 @Override
Winson Chungd2d90972017-02-28 11:40:41 -0800245 public void onUserInteraction() {
Winson Chung379b9ab2017-04-04 11:57:36 -0700246 if (mAllowMenuTimeout) {
247 repostDelayedFinish(POST_INTERACTION_DISMISS_DELAY);
248 }
Winson Chungd2d90972017-02-28 11:40:41 -0800249 }
250
251 @Override
Winson Chung61ecc1b2017-02-17 10:46:17 -0800252 protected void onUserLeaveHint() {
253 super.onUserLeaveHint();
254
Winson Chung9f69b782017-05-16 15:27:49 -0700255 // If another task is starting on top of the menu, then hide and finish it so that it can be
Winson Chung61ecc1b2017-02-17 10:46:17 -0800256 // recreated on the top next time it starts
Winson Chung9f69b782017-05-16 15:27:49 -0700257 hideMenu();
Winson Chung61ecc1b2017-02-17 10:46:17 -0800258 }
259
260 @Override
Winson Chungef4dc812017-04-11 13:31:44 -0700261 protected void onStop() {
262 super.onStop();
263
Hongwei Wang9cdf4522019-09-26 10:42:38 -0700264 // In cases such as device lock, hide and finish it so that it can be recreated on the top
265 // next time it starts, see also {@link #onUserLeaveHint}
266 hideMenu();
Winson Chungef4dc812017-04-11 13:31:44 -0700267 cancelDelayedFinish();
268 }
269
270 @Override
Winson Chung61ecc1b2017-02-17 10:46:17 -0800271 protected void onDestroy() {
272 super.onDestroy();
273
274 // Fallback, if we are destroyed for any other reason (like when the task is being reset),
275 // also reset the callback.
276 notifyActivityCallback(null);
277 }
278
279 @Override
Winson Chungc75ffe82016-12-16 16:20:16 -0800280 public void onPictureInPictureModeChanged(boolean isInPictureInPictureMode) {
281 if (!isInPictureInPictureMode) {
282 finish();
283 }
284 }
285
Arthur Hungd8961972019-10-14 17:00:17 +0800286 /**
287 * Dispatch a pointer event from {@link PipTouchHandler}.
288 */
289 private void dispatchPointerEvent(MotionEvent event) {
290 if (event.isTouchEvent()) {
291 dispatchTouchEvent(event);
292 } else {
293 dispatchGenericMotionEvent(event);
294 }
295 }
296
Winson Chungc75ffe82016-12-16 16:20:16 -0800297 @Override
Winson Chungc75ffe82016-12-16 16:20:16 -0800298 public boolean dispatchTouchEvent(MotionEvent ev) {
Winson Chung34488242017-04-26 15:53:51 -0700299 if (!mAllowTouches) {
Arthur Hungd52d1192019-08-30 16:06:59 +0800300 return false;
Winson Chung34488242017-04-26 15:53:51 -0700301 }
302
Winson Chungc75ffe82016-12-16 16:20:16 -0800303 // On the first action outside the window, hide the menu
304 switch (ev.getAction()) {
305 case MotionEvent.ACTION_OUTSIDE:
306 hideMenu();
Arthur Hungd52d1192019-08-30 16:06:59 +0800307 return true;
Winson Chungc75ffe82016-12-16 16:20:16 -0800308 }
309 return super.dispatchTouchEvent(ev);
Winson Chung15504af2016-11-02 18:11:36 -0700310 }
311
312 @Override
313 public void finish() {
Winson Chungc75ffe82016-12-16 16:20:16 -0800314 notifyActivityCallback(null);
Winson Chung15504af2016-11-02 18:11:36 -0700315 super.finish();
Winson Chung15504af2016-11-02 18:11:36 -0700316 }
317
318 @Override
319 public void setTaskDescription(ActivityManager.TaskDescription taskDescription) {
320 // Do nothing
321 }
322
Mady Mellor637cd482017-03-21 10:39:42 -0700323 private void showMenu(int menuState, Rect stackBounds, Rect movementBounds,
Winson Chungbb787442017-09-01 11:33:47 -0700324 boolean allowMenuTimeout, boolean resizeMenuOnShow) {
Winson Chung379b9ab2017-04-04 11:57:36 -0700325 mAllowMenuTimeout = allowMenuTimeout;
Mady Mellor637cd482017-03-21 10:39:42 -0700326 if (mMenuState != menuState) {
Winson Chungbb787442017-09-01 11:33:47 -0700327 // Disallow touches if the menu needs to resize while showing, and we are transitioning
328 // to/from a full menu state.
329 boolean disallowTouchesUntilAnimationEnd = resizeMenuOnShow &&
330 (mMenuState == MENU_STATE_FULL || menuState == MENU_STATE_FULL);
331 mAllowTouches = !disallowTouchesUntilAnimationEnd;
Mady Mellor637cd482017-03-21 10:39:42 -0700332 cancelDelayedFinish();
Mady Mellor81d40612017-03-10 15:14:10 -0800333 updateActionViews(stackBounds);
Winson Chungc75ffe82016-12-16 16:20:16 -0800334 if (mMenuContainerAnimator != null) {
335 mMenuContainerAnimator.cancel();
336 }
Ben Lin559ea4d2019-11-06 12:36:41 -0800337 notifyMenuStateChange(menuState, resizeMenuOnShow);
Mady Mellor637cd482017-03-21 10:39:42 -0700338 mMenuContainerAnimator = new AnimatorSet();
339 ObjectAnimator menuAnim = ObjectAnimator.ofFloat(mMenuContainer, View.ALPHA,
Winson Chungc75ffe82016-12-16 16:20:16 -0800340 mMenuContainer.getAlpha(), 1f);
Mady Mellor637cd482017-03-21 10:39:42 -0700341 menuAnim.addUpdateListener(mMenuBgUpdateListener);
Winson Chunga556fe62017-12-19 14:46:23 -0800342 ObjectAnimator settingsAnim = ObjectAnimator.ofFloat(mSettingsButton, View.ALPHA,
343 mSettingsButton.getAlpha(), 1f);
Mady Mellor637cd482017-03-21 10:39:42 -0700344 ObjectAnimator dismissAnim = ObjectAnimator.ofFloat(mDismissButton, View.ALPHA,
345 mDismissButton.getAlpha(), 1f);
346 if (menuState == MENU_STATE_FULL) {
Winson Chunga556fe62017-12-19 14:46:23 -0800347 mMenuContainerAnimator.playTogether(menuAnim, settingsAnim, dismissAnim);
Mady Mellor637cd482017-03-21 10:39:42 -0700348 } else {
Winson Chung2a35e6d2018-01-13 14:27:50 -0800349 mMenuContainerAnimator.playTogether(dismissAnim);
Mady Mellor637cd482017-03-21 10:39:42 -0700350 }
Winson Chungc75ffe82016-12-16 16:20:16 -0800351 mMenuContainerAnimator.setInterpolator(Interpolators.ALPHA_IN);
352 mMenuContainerAnimator.setDuration(MENU_FADE_DURATION);
Winson Chung0f873de2017-03-30 10:26:48 -0700353 if (allowMenuTimeout) {
354 mMenuContainerAnimator.addListener(new AnimatorListenerAdapter() {
355 @Override
356 public void onAnimationEnd(Animator animation) {
357 repostDelayedFinish(INITIAL_DISMISS_DELAY);
358 }
359 });
360 }
Winson Chungc75ffe82016-12-16 16:20:16 -0800361 mMenuContainerAnimator.start();
Winson Chungd2d90972017-02-28 11:40:41 -0800362 } else {
Winson Chung2824d7c2017-03-15 19:43:00 -0700363 // If we are already visible, then just start the delayed dismiss and unregister any
364 // existing input consumers from the previous drag
Winson Chung0f873de2017-03-30 10:26:48 -0700365 if (allowMenuTimeout) {
366 repostDelayedFinish(POST_INTERACTION_DISMISS_DELAY);
367 }
Winson Chungc75ffe82016-12-16 16:20:16 -0800368 }
369 }
370
371 private void hideMenu() {
Winson Chung2dbcf092018-10-24 13:00:41 -0700372 hideMenu(null);
373 }
374
375 private void hideMenu(Runnable animationEndCallback) {
376 hideMenu(animationEndCallback, true /* notifyMenuVisibility */, false /* isDismissing */);
Winson Chungc75ffe82016-12-16 16:20:16 -0800377 }
378
Winson Chung21004d52018-06-04 17:09:32 -0700379 private void hideMenu(final Runnable animationFinishedRunnable, boolean notifyMenuVisibility,
380 boolean isDismissing) {
Mady Mellor637cd482017-03-21 10:39:42 -0700381 if (mMenuState != MENU_STATE_NONE) {
Winson Chungc75ffe82016-12-16 16:20:16 -0800382 cancelDelayedFinish();
Winson Chungd2d90972017-02-28 11:40:41 -0800383 if (notifyMenuVisibility) {
Ben Lin559ea4d2019-11-06 12:36:41 -0800384 notifyMenuStateChange(MENU_STATE_NONE, mResize);
Winson Chungd2d90972017-02-28 11:40:41 -0800385 }
Mady Mellor637cd482017-03-21 10:39:42 -0700386 mMenuContainerAnimator = new AnimatorSet();
387 ObjectAnimator menuAnim = ObjectAnimator.ofFloat(mMenuContainer, View.ALPHA,
Winson Chungc75ffe82016-12-16 16:20:16 -0800388 mMenuContainer.getAlpha(), 0f);
Mady Mellor637cd482017-03-21 10:39:42 -0700389 menuAnim.addUpdateListener(mMenuBgUpdateListener);
Winson Chunga556fe62017-12-19 14:46:23 -0800390 ObjectAnimator settingsAnim = ObjectAnimator.ofFloat(mSettingsButton, View.ALPHA,
391 mSettingsButton.getAlpha(), 0f);
Mady Mellor637cd482017-03-21 10:39:42 -0700392 ObjectAnimator dismissAnim = ObjectAnimator.ofFloat(mDismissButton, View.ALPHA,
393 mDismissButton.getAlpha(), 0f);
Winson Chunga556fe62017-12-19 14:46:23 -0800394 mMenuContainerAnimator.playTogether(menuAnim, settingsAnim, dismissAnim);
Winson Chungc75ffe82016-12-16 16:20:16 -0800395 mMenuContainerAnimator.setInterpolator(Interpolators.ALPHA_OUT);
396 mMenuContainerAnimator.setDuration(MENU_FADE_DURATION);
397 mMenuContainerAnimator.addListener(new AnimatorListenerAdapter() {
398 @Override
399 public void onAnimationEnd(Animator animation) {
400 if (animationFinishedRunnable != null) {
401 animationFinishedRunnable.run();
402 }
Winson Chung21004d52018-06-04 17:09:32 -0700403
404 if (!isDismissing) {
405 // If we are dismissing the PiP, then don't try to pre-emptively finish the
406 // menu activity
407 finish();
408 }
Winson Chungc75ffe82016-12-16 16:20:16 -0800409 }
410 });
411 mMenuContainerAnimator.start();
Winson Chung87e5d552017-04-05 11:49:38 -0700412 } else {
413 // If the menu is not visible, just finish now
414 finish();
Winson Chungc75ffe82016-12-16 16:20:16 -0800415 }
416 }
417
Winson Chunge7a3d222017-03-09 13:26:45 -0800418 private void updateFromIntent(Intent intent) {
Winson Chunge7a3d222017-03-09 13:26:45 -0800419 mToControllerMessenger = intent.getParcelableExtra(EXTRA_CONTROLLER_MESSENGER);
Jaewan Kimc1d15242017-07-07 18:12:04 +0900420 if (mToControllerMessenger == null) {
421 Log.w(TAG, "Controller messenger is null. Stopping.");
422 finish();
423 return;
424 }
Winson Chungccbc4b92017-03-20 14:01:36 -0700425 notifyActivityCallback(mMessenger);
Winson Chungb5026902017-05-03 12:45:13 -0700426
Winson Chunge7a3d222017-03-09 13:26:45 -0800427 ParceledListSlice actions = intent.getParcelableExtra(EXTRA_ACTIONS);
428 if (actions != null) {
Mady Mellor81d40612017-03-10 15:14:10 -0800429 mActions.clear();
430 mActions.addAll(actions.getList());
Winson Chunge7a3d222017-03-09 13:26:45 -0800431 }
Winson Chungb5026902017-05-03 12:45:13 -0700432
Mady Mellor637cd482017-03-21 10:39:42 -0700433 final int menuState = intent.getIntExtra(EXTRA_MENU_STATE, MENU_STATE_NONE);
434 if (menuState != MENU_STATE_NONE) {
Winson Chung853c99a2017-03-21 22:16:42 -0700435 Rect stackBounds = intent.getParcelableExtra(EXTRA_STACK_BOUNDS);
436 Rect movementBounds = intent.getParcelableExtra(EXTRA_MOVEMENT_BOUNDS);
Winson Chung0f873de2017-03-30 10:26:48 -0700437 boolean allowMenuTimeout = intent.getBooleanExtra(EXTRA_ALLOW_TIMEOUT, true);
Winson Chungbb787442017-09-01 11:33:47 -0700438 boolean willResizeMenu = intent.getBooleanExtra(EXTRA_WILL_RESIZE_MENU, false);
439 showMenu(menuState, stackBounds, movementBounds, allowMenuTimeout, willResizeMenu);
Mady Mellor81d40612017-03-10 15:14:10 -0800440 }
Winson Chunga29eb982016-12-14 12:01:27 -0800441 }
442
Winson Chunge7a3d222017-03-09 13:26:45 -0800443 private void setActions(Rect stackBounds, List<RemoteAction> actions) {
444 mActions.clear();
445 mActions.addAll(actions);
446 updateActionViews(stackBounds);
447 }
448
449 private void updateActionViews(Rect stackBounds) {
Alan Viverette51efddb2017-04-05 10:00:01 -0400450 ViewGroup expandContainer = findViewById(R.id.expand_container);
451 ViewGroup actionsContainer = findViewById(R.id.actions_container);
Winson Chunga5acf182017-01-05 16:02:27 -0800452 actionsContainer.setOnTouchListener((v, ev) -> {
453 // Do nothing, prevent click through to parent
454 return true;
455 });
Winson Chunga29eb982016-12-14 12:01:27 -0800456
Mady Mellor637cd482017-03-21 10:39:42 -0700457 if (mActions.isEmpty() || mMenuState == MENU_STATE_CLOSE) {
Winson Chunga5acf182017-01-05 16:02:27 -0800458 actionsContainer.setVisibility(View.INVISIBLE);
459 } else {
460 actionsContainer.setVisibility(View.VISIBLE);
Mady Mellora7f69742017-02-03 11:00:20 -0800461 if (mActionsGroup != null) {
Winson Chungb6de8722017-06-02 12:45:51 -0700462 // Ensure we have as many buttons as actions
Winson Chung7aab0c12017-04-18 12:54:25 -0700463 final LayoutInflater inflater = LayoutInflater.from(this);
464 while (mActionsGroup.getChildCount() < mActions.size()) {
Hongwei Wanga6e034e2019-06-20 13:18:43 -0700465 final ImageButton actionView = (ImageButton) inflater.inflate(
Winson Chung7aab0c12017-04-18 12:54:25 -0700466 R.layout.pip_menu_action, mActionsGroup, false);
467 mActionsGroup.addView(actionView);
468 }
Winson Chunga29eb982016-12-14 12:01:27 -0800469
Winson Chungb6de8722017-06-02 12:45:51 -0700470 // Update the visibility of all views
471 for (int i = 0; i < mActionsGroup.getChildCount(); i++) {
472 mActionsGroup.getChildAt(i).setVisibility(i < mActions.size()
473 ? View.VISIBLE
474 : View.GONE);
475 }
476
Winson Chunga5acf182017-01-05 16:02:27 -0800477 // Recreate the layout
Winson Chunge7a3d222017-03-09 13:26:45 -0800478 final boolean isLandscapePip = stackBounds != null &&
479 (stackBounds.width() > stackBounds.height());
Winson Chunga5acf182017-01-05 16:02:27 -0800480 for (int i = 0; i < mActions.size(); i++) {
481 final RemoteAction action = mActions.get(i);
Hongwei Wanga6e034e2019-06-20 13:18:43 -0700482 final ImageButton actionView = (ImageButton) mActionsGroup.getChildAt(i);
Winson Chung7aab0c12017-04-18 12:54:25 -0700483
484 // TODO: Check if the action drawable has changed before we reload it
Winson Chunga5acf182017-01-05 16:02:27 -0800485 action.getIcon().loadDrawableAsync(this, d -> {
Winson Chung56fe8832017-02-03 14:37:53 -0800486 d.setTint(Color.WHITE);
Winson Chunga5acf182017-01-05 16:02:27 -0800487 actionView.setImageDrawable(d);
488 }, mHandler);
489 actionView.setContentDescription(action.getContentDescription());
Winson Chung6b88baf2017-03-16 17:10:21 -0700490 if (action.isEnabled()) {
491 actionView.setOnClickListener(v -> {
Arthur Hungd52d1192019-08-30 16:06:59 +0800492 mHandler.post(() -> {
493 try {
494 action.getActionIntent().send();
495 } catch (CanceledException e) {
496 Log.w(TAG, "Failed to send action", e);
497 }
498 });
Winson Chung6b88baf2017-03-16 17:10:21 -0700499 });
Winson Chung6b88baf2017-03-16 17:10:21 -0700500 }
Winson Chungd61a19e2017-04-28 16:36:51 -0700501 actionView.setEnabled(action.isEnabled());
Winson Chungb6de8722017-06-02 12:45:51 -0700502 actionView.setAlpha(action.isEnabled() ? 1f : DISABLED_ACTION_ALPHA);
Winson Chung7aab0c12017-04-18 12:54:25 -0700503
504 // Update the margin between actions
505 LinearLayout.LayoutParams lp = (LinearLayout.LayoutParams)
506 actionView.getLayoutParams();
507 lp.leftMargin = (isLandscapePip && i > 0) ? mBetweenActionPaddingLand : 0;
Winson Chunga5acf182017-01-05 16:02:27 -0800508 }
Winson Chunga29eb982016-12-14 12:01:27 -0800509 }
Mady Mellora7f69742017-02-03 11:00:20 -0800510
Winson Chunge7a3d222017-03-09 13:26:45 -0800511 // Update the expand container margin to adjust the center of the expand button to
512 // account for the existence of the action container
513 FrameLayout.LayoutParams expandedLp =
514 (FrameLayout.LayoutParams) expandContainer.getLayoutParams();
515 expandedLp.topMargin = getResources().getDimensionPixelSize(
516 R.dimen.pip_action_padding);
517 expandedLp.bottomMargin = getResources().getDimensionPixelSize(
518 R.dimen.pip_expand_container_edge_margin);
519 expandContainer.requestLayout();
520 }
Winson Chunga29eb982016-12-14 12:01:27 -0800521 }
522
Mady Mellor81d40612017-03-10 15:14:10 -0800523 private void updateDismissFraction(float fraction) {
524 int alpha;
Mady Mellor637cd482017-03-21 10:39:42 -0700525 final float menuAlpha = 1 - fraction;
526 if (mMenuState == MENU_STATE_FULL) {
527 mMenuContainer.setAlpha(menuAlpha);
Winson Chunga556fe62017-12-19 14:46:23 -0800528 mSettingsButton.setAlpha(menuAlpha);
Mady Mellor637cd482017-03-21 10:39:42 -0700529 mDismissButton.setAlpha(menuAlpha);
Mady Mellor81d40612017-03-10 15:14:10 -0800530 final float interpolatedAlpha =
Mady Mellor637cd482017-03-21 10:39:42 -0700531 MENU_BACKGROUND_ALPHA * menuAlpha + DISMISS_BACKGROUND_ALPHA * fraction;
Winson Chungc3545d32017-04-03 10:44:57 -0700532 alpha = (int) (interpolatedAlpha * 255);
Mady Mellor81d40612017-03-10 15:14:10 -0800533 } else {
Mady Mellor637cd482017-03-21 10:39:42 -0700534 if (mMenuState == MENU_STATE_CLOSE) {
535 mDismissButton.setAlpha(menuAlpha);
536 }
Winson Chungc3545d32017-04-03 10:44:57 -0700537 alpha = (int) (fraction * DISMISS_BACKGROUND_ALPHA * 255);
Mady Mellor81d40612017-03-10 15:14:10 -0800538 }
539 mBackgroundDrawable.setAlpha(alpha);
540 }
541
Ben Lin559ea4d2019-11-06 12:36:41 -0800542 private void notifyMenuStateChange(int menuState, boolean resize) {
Mady Mellor637cd482017-03-21 10:39:42 -0700543 mMenuState = menuState;
Ben Lin559ea4d2019-11-06 12:36:41 -0800544 mResize = resize;
Winson Chung15504af2016-11-02 18:11:36 -0700545 Message m = Message.obtain();
Mady Mellor637cd482017-03-21 10:39:42 -0700546 m.what = PipMenuActivityController.MESSAGE_MENU_STATE_CHANGED;
547 m.arg1 = menuState;
Ben Lin559ea4d2019-11-06 12:36:41 -0800548 m.arg2 = resize ? 1 : 0;
Winson Chunga29eb982016-12-14 12:01:27 -0800549 sendMessage(m, "Could not notify controller of PIP menu visibility");
Winson Chung15504af2016-11-02 18:11:36 -0700550 }
551
Winson Chunga29eb982016-12-14 12:01:27 -0800552 private void expandPip() {
Winson Chungd2d90972017-02-28 11:40:41 -0800553 // Do not notify menu visibility when hiding the menu, the controller will do this when it
554 // handles the message
Winson Chungc75ffe82016-12-16 16:20:16 -0800555 hideMenu(() -> {
556 sendEmptyMessage(PipMenuActivityController.MESSAGE_EXPAND_PIP,
557 "Could not notify controller to expand PIP");
Winson Chung21004d52018-06-04 17:09:32 -0700558 }, false /* notifyMenuVisibility */, false /* isDismissing */);
Winson Chunga29eb982016-12-14 12:01:27 -0800559 }
560
Winson Chunga29eb982016-12-14 12:01:27 -0800561 private void dismissPip() {
Winson Chungd2d90972017-02-28 11:40:41 -0800562 // Do not notify menu visibility when hiding the menu, the controller will do this when it
563 // handles the message
Winson Chungc75ffe82016-12-16 16:20:16 -0800564 hideMenu(() -> {
565 sendEmptyMessage(PipMenuActivityController.MESSAGE_DISMISS_PIP,
566 "Could not notify controller to dismiss PIP");
Winson Chung21004d52018-06-04 17:09:32 -0700567 }, false /* notifyMenuVisibility */, true /* isDismissing */);
Winson Chungc75ffe82016-12-16 16:20:16 -0800568 }
569
Winson Chunga556fe62017-12-19 14:46:23 -0800570 private void showSettings() {
571 final Pair<ComponentName, Integer> topPipActivityInfo =
Winson Chungbe00a832020-03-10 11:08:35 -0700572 PipUtils.getTopPipActivity(this, ActivityManager.getService());
Winson Chunga556fe62017-12-19 14:46:23 -0800573 if (topPipActivityInfo.first != null) {
574 final UserHandle user = UserHandle.of(topPipActivityInfo.second);
575 final Intent settingsIntent = new Intent(ACTION_PICTURE_IN_PICTURE_SETTINGS,
576 Uri.fromParts("package", topPipActivityInfo.first.getPackageName(), null));
577 settingsIntent.putExtra(Intent.EXTRA_USER_HANDLE, user);
578 settingsIntent.setFlags(FLAG_ACTIVITY_NEW_TASK | FLAG_ACTIVITY_CLEAR_TASK);
579 startActivity(settingsIntent);
580 }
581 }
582
Winson Chungc75ffe82016-12-16 16:20:16 -0800583 private void notifyActivityCallback(Messenger callback) {
584 Message m = Message.obtain();
585 m.what = PipMenuActivityController.MESSAGE_UPDATE_ACTIVITY_CALLBACK;
586 m.replyTo = callback;
Ben Lin559ea4d2019-11-06 12:36:41 -0800587 m.arg1 = mResize ? 1 : 0;
Winson Chungc75ffe82016-12-16 16:20:16 -0800588 sendMessage(m, "Could not notify controller of activity finished");
Winson Chunga29eb982016-12-14 12:01:27 -0800589 }
590
591 private void sendEmptyMessage(int what, String errorMsg) {
Winson Chung15504af2016-11-02 18:11:36 -0700592 Message m = Message.obtain();
Winson Chunga29eb982016-12-14 12:01:27 -0800593 m.what = what;
594 sendMessage(m, errorMsg);
595 }
596
597 private void sendMessage(Message m, String errorMsg) {
Jaewan Kimc1d15242017-07-07 18:12:04 +0900598 if (mToControllerMessenger == null) {
599 return;
600 }
Winson Chung15504af2016-11-02 18:11:36 -0700601 try {
602 mToControllerMessenger.send(m);
603 } catch (RemoteException e) {
Winson Chunga29eb982016-12-14 12:01:27 -0800604 Log.e(TAG, errorMsg, e);
Winson Chung15504af2016-11-02 18:11:36 -0700605 }
606 }
607
Winson Chungc75ffe82016-12-16 16:20:16 -0800608 private void cancelDelayedFinish() {
Winson Chung4a526c12017-05-16 13:35:43 -0700609 mHandler.removeCallbacks(mFinishRunnable);
Winson Chungc75ffe82016-12-16 16:20:16 -0800610 }
611
Rhed Jao5e00c982018-11-27 19:41:05 +0800612 private void repostDelayedFinish(int delay) {
613 int recommendedTimeout = mAccessibilityManager.getRecommendedTimeoutMillis(delay,
614 FLAG_CONTENT_ICONS | FLAG_CONTENT_CONTROLS);
Winson Chung4a526c12017-05-16 13:35:43 -0700615 mHandler.removeCallbacks(mFinishRunnable);
Rhed Jao5e00c982018-11-27 19:41:05 +0800616 mHandler.postDelayed(mFinishRunnable, recommendedTimeout);
Winson Chung15504af2016-11-02 18:11:36 -0700617 }
Winson Chung15504af2016-11-02 18:11:36 -0700618}