blob: ec15dd16f46e9dc744a40ce24f03173c74b9a142 [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;
Hongwei Wang081ecb32020-04-13 16:49:03 -070057import android.os.Looper;
Winson Chung15504af2016-11-02 18:11:36 -070058import android.os.Message;
59import android.os.Messenger;
60import android.os.RemoteException;
Winson Chunga556fe62017-12-19 14:46:23 -080061import android.os.UserHandle;
Winson Chung15504af2016-11-02 18:11:36 -070062import android.util.Log;
Winson Chunga556fe62017-12-19 14:46:23 -080063import android.util.Pair;
Ben Lin8e90d0a2020-02-26 14:57:14 -080064import android.view.KeyEvent;
Winson Chunga29eb982016-12-14 12:01:27 -080065import android.view.LayoutInflater;
Winson Chungc75ffe82016-12-16 16:20:16 -080066import android.view.MotionEvent;
Winson Chung15504af2016-11-02 18:11:36 -070067import android.view.View;
Winson Chunga29eb982016-12-14 12:01:27 -080068import android.view.ViewGroup;
Winson Chungc75ffe82016-12-16 16:20:16 -080069import android.view.WindowManager.LayoutParams;
Rhed Jao5e00c982018-11-27 19:41:05 +080070import android.view.accessibility.AccessibilityManager;
Mady Mellora7f69742017-02-03 11:00:20 -080071import android.widget.FrameLayout;
Hongwei Wanga6e034e2019-06-20 13:18:43 -070072import android.widget.ImageButton;
Mady Mellora7f69742017-02-03 11:00:20 -080073import android.widget.LinearLayout;
Winson Chunga29eb982016-12-14 12:01:27 -080074
Winson Chungc75ffe82016-12-16 16:20:16 -080075import com.android.systemui.Interpolators;
Winson Chung15504af2016-11-02 18:11:36 -070076import com.android.systemui.R;
77
Winson Chunga29eb982016-12-14 12:01:27 -080078import java.util.ArrayList;
Winson Chung26db3972017-03-23 15:17:01 -070079import java.util.Collections;
Winson Chunga29eb982016-12-14 12:01:27 -080080import java.util.List;
81
Winson Chung15504af2016-11-02 18:11:36 -070082/**
83 * Translucent activity that gets started on top of a task in PIP to allow the user to control it.
84 */
85public class PipMenuActivity extends Activity {
86
87 private static final String TAG = "PipMenuActivity";
88
Winson Chungc75ffe82016-12-16 16:20:16 -080089 public static final int MESSAGE_SHOW_MENU = 1;
Mady Mellora7f69742017-02-03 11:00:20 -080090 public static final int MESSAGE_POKE_MENU = 2;
91 public static final int MESSAGE_HIDE_MENU = 3;
92 public static final int MESSAGE_UPDATE_ACTIONS = 4;
Mady Mellor81d40612017-03-10 15:14:10 -080093 public static final int MESSAGE_UPDATE_DISMISS_FRACTION = 5;
Winson Chung34488242017-04-26 15:53:51 -070094 public static final int MESSAGE_ANIMATION_ENDED = 6;
Arthur Hungd8961972019-10-14 17:00:17 +080095 public static final int MESSAGE_POINTER_EVENT = 7;
Winson Chung15504af2016-11-02 18:11:36 -070096
Rhed Jao5e00c982018-11-27 19:41:05 +080097 private static final int INITIAL_DISMISS_DELAY = 3500;
98 private static final int POST_INTERACTION_DISMISS_DELAY = 2000;
Winson Chungc75ffe82016-12-16 16:20:16 -080099 private static final long MENU_FADE_DURATION = 125;
Winson Chung15504af2016-11-02 18:11:36 -0700100
Mady Mellor81d40612017-03-10 15:14:10 -0800101 private static final float MENU_BACKGROUND_ALPHA = 0.3f;
Mady Mellor60421c92017-03-29 15:27:37 -0700102 private static final float DISMISS_BACKGROUND_ALPHA = 0.6f;
Mady Mellor81d40612017-03-10 15:14:10 -0800103
Winson Chung6b88baf2017-03-16 17:10:21 -0700104 private static final float DISABLED_ACTION_ALPHA = 0.54f;
105
Mady Mellor637cd482017-03-21 10:39:42 -0700106 private int mMenuState;
Ben Lin559ea4d2019-11-06 12:36:41 -0800107 private boolean mResize = true;
Winson Chung379b9ab2017-04-04 11:57:36 -0700108 private boolean mAllowMenuTimeout = true;
Winson Chung34488242017-04-26 15:53:51 -0700109 private boolean mAllowTouches = true;
Winson Chung87e5d552017-04-05 11:49:38 -0700110
Winson Chungc75ffe82016-12-16 16:20:16 -0800111 private final List<RemoteAction> mActions = new ArrayList<>();
Winson Chung87e5d552017-04-05 11:49:38 -0700112
Rhed Jao5e00c982018-11-27 19:41:05 +0800113 private AccessibilityManager mAccessibilityManager;
Mady Mellor81d40612017-03-10 15:14:10 -0800114 private View mViewRoot;
115 private Drawable mBackgroundDrawable;
Winson Chungc75ffe82016-12-16 16:20:16 -0800116 private View mMenuContainer;
Mady Mellora7f69742017-02-03 11:00:20 -0800117 private LinearLayout mActionsGroup;
Winson Chunga556fe62017-12-19 14:46:23 -0800118 private View mSettingsButton;
Winson Chunga29eb982016-12-14 12:01:27 -0800119 private View mDismissButton;
Mady Mellora7f69742017-02-03 11:00:20 -0800120 private int mBetweenActionPaddingLand;
Winson Chunga29eb982016-12-14 12:01:27 -0800121
Mady Mellor637cd482017-03-21 10:39:42 -0700122 private AnimatorSet mMenuContainerAnimator;
123
Mady Mellor81d40612017-03-10 15:14:10 -0800124 private ValueAnimator.AnimatorUpdateListener mMenuBgUpdateListener =
125 new ValueAnimator.AnimatorUpdateListener() {
126 @Override
127 public void onAnimationUpdate(ValueAnimator animation) {
128 final float alpha = (float) animation.getAnimatedValue();
129 mBackgroundDrawable.setAlpha((int) (MENU_BACKGROUND_ALPHA*alpha*255));
130 }
131 };
Winson Chungc75ffe82016-12-16 16:20:16 -0800132
Hongwei Wang081ecb32020-04-13 16:49:03 -0700133 private Handler mHandler = new Handler(Looper.getMainLooper()) {
Winson Chung15504af2016-11-02 18:11:36 -0700134 @Override
135 public void handleMessage(Message msg) {
136 switch (msg.what) {
Winson Chung853c99a2017-03-21 22:16:42 -0700137 case MESSAGE_SHOW_MENU: {
138 final Bundle data = (Bundle) msg.obj;
Mady Mellor637cd482017-03-21 10:39:42 -0700139 showMenu(data.getInt(EXTRA_MENU_STATE),
140 data.getParcelable(EXTRA_STACK_BOUNDS),
Winson Chung0f873de2017-03-30 10:26:48 -0700141 data.getParcelable(EXTRA_MOVEMENT_BOUNDS),
Winson Chungbb787442017-09-01 11:33:47 -0700142 data.getBoolean(EXTRA_ALLOW_TIMEOUT),
143 data.getBoolean(EXTRA_WILL_RESIZE_MENU));
Winson Chungc75ffe82016-12-16 16:20:16 -0800144 break;
Winson Chung853c99a2017-03-21 22:16:42 -0700145 }
Mady Mellora7f69742017-02-03 11:00:20 -0800146 case MESSAGE_POKE_MENU:
147 cancelDelayedFinish();
148 break;
Winson Chungc75ffe82016-12-16 16:20:16 -0800149 case MESSAGE_HIDE_MENU:
Winson Chung2dbcf092018-10-24 13:00:41 -0700150 hideMenu((Runnable) msg.obj);
Winson Chungc75ffe82016-12-16 16:20:16 -0800151 break;
Winson Chung853c99a2017-03-21 22:16:42 -0700152 case MESSAGE_UPDATE_ACTIONS: {
153 final Bundle data = (Bundle) msg.obj;
Winson Chung26db3972017-03-23 15:17:01 -0700154 final ParceledListSlice actions = data.getParcelable(EXTRA_ACTIONS);
155 setActions(data.getParcelable(EXTRA_STACK_BOUNDS), actions != null
156 ? actions.getList() : Collections.EMPTY_LIST);
Winson Chunga29eb982016-12-14 12:01:27 -0800157 break;
Winson Chung853c99a2017-03-21 22:16:42 -0700158 }
159 case MESSAGE_UPDATE_DISMISS_FRACTION: {
160 final Bundle data = (Bundle) msg.obj;
161 updateDismissFraction(data.getFloat(EXTRA_DISMISS_FRACTION));
Mady Mellor81d40612017-03-10 15:14:10 -0800162 break;
Winson Chung853c99a2017-03-21 22:16:42 -0700163 }
Winson Chung34488242017-04-26 15:53:51 -0700164 case MESSAGE_ANIMATION_ENDED: {
165 mAllowTouches = true;
166 break;
167 }
Arthur Hungd52d1192019-08-30 16:06:59 +0800168
Arthur Hungd8961972019-10-14 17:00:17 +0800169 case MESSAGE_POINTER_EVENT: {
Arthur Hungd52d1192019-08-30 16:06:59 +0800170 final MotionEvent ev = (MotionEvent) msg.obj;
Arthur Hungd8961972019-10-14 17:00:17 +0800171 dispatchPointerEvent(ev);
Arthur Hungd52d1192019-08-30 16:06:59 +0800172 break;
173 }
Winson Chung15504af2016-11-02 18:11:36 -0700174 }
175 }
Hongwei Wang081ecb32020-04-13 16:49:03 -0700176 };
177 private Messenger mToControllerMessenger;
178 private Messenger mMessenger = new Messenger(mHandler);
Winson Chung15504af2016-11-02 18:11:36 -0700179
180 private final Runnable mFinishRunnable = new Runnable() {
181 @Override
182 public void run() {
Winson Chungc75ffe82016-12-16 16:20:16 -0800183 hideMenu();
Winson Chung15504af2016-11-02 18:11:36 -0700184 }
185 };
186
187 @Override
188 protected void onCreate(@Nullable Bundle savedInstanceState) {
Winson Chungc75ffe82016-12-16 16:20:16 -0800189 // Set the flags to allow us to watch for outside touches and also hide the menu and start
190 // manipulating the PIP in the same touch gesture
Arthur Hungd52d1192019-08-30 16:06:59 +0800191 getWindow().addFlags(LayoutParams.FLAG_WATCH_OUTSIDE_TOUCH);
Winson Chungc75ffe82016-12-16 16:20:16 -0800192
Winson Chung15504af2016-11-02 18:11:36 -0700193 super.onCreate(savedInstanceState);
Winson Chung15504af2016-11-02 18:11:36 -0700194 setContentView(R.layout.pip_menu_activity);
Winson Chunga29eb982016-12-14 12:01:27 -0800195
Rhed Jao5e00c982018-11-27 19:41:05 +0800196 mAccessibilityManager = getSystemService(AccessibilityManager.class);
Mady Mellor81d40612017-03-10 15:14:10 -0800197 mBackgroundDrawable = new ColorDrawable(Color.BLACK);
198 mBackgroundDrawable.setAlpha(0);
199 mViewRoot = findViewById(R.id.background);
200 mViewRoot.setBackground(mBackgroundDrawable);
201 mMenuContainer = findViewById(R.id.menu_container);
202 mMenuContainer.setAlpha(0);
Winson Chunga556fe62017-12-19 14:46:23 -0800203 mSettingsButton = findViewById(R.id.settings);
204 mSettingsButton.setAlpha(0);
205 mSettingsButton.setOnClickListener((v) -> {
Tracy Zhou2c6117a2018-06-21 11:32:43 -0700206 if (v.getAlpha() != 0) {
207 showSettings();
208 }
Winson Chunga556fe62017-12-19 14:46:23 -0800209 });
Winson Chunga29eb982016-12-14 12:01:27 -0800210 mDismissButton = findViewById(R.id.dismiss);
Mady Mellor637cd482017-03-21 10:39:42 -0700211 mDismissButton.setAlpha(0);
Hongwei Wang10649502019-06-26 13:39:24 -0700212 mDismissButton.setOnClickListener(v -> dismissPip());
Arthur Hungd52d1192019-08-30 16:06:59 +0800213 findViewById(R.id.expand_button).setOnClickListener(v -> {
214 if (mMenuContainer.getAlpha() != 0) {
215 expandPip();
216 }
217 });
Alan Viverette51efddb2017-04-05 10:00:01 -0400218 mActionsGroup = findViewById(R.id.actions_group);
Mady Mellora7f69742017-02-03 11:00:20 -0800219 mBetweenActionPaddingLand = getResources().getDimensionPixelSize(
220 R.dimen.pip_between_action_padding_land);
Winson Chungc75ffe82016-12-16 16:20:16 -0800221
Winson Chunge7a3d222017-03-09 13:26:45 -0800222 updateFromIntent(getIntent());
Phil Weaverf00cd142017-03-03 13:44:00 -0800223 setTitle(R.string.pip_menu_title);
Wale Ogunwale5e8ecb02017-04-14 13:15:55 -0700224 setDisablePreviewScreenshots(true);
Arthur Hungd8961972019-10-14 17:00:17 +0800225
226 // Hide without an animation.
227 getWindow().setExitTransition(null);
Winson Chungc75ffe82016-12-16 16:20:16 -0800228 }
229
230 @Override
Ben Lin8e90d0a2020-02-26 14:57:14 -0800231 public boolean onKeyUp(int keyCode, KeyEvent event) {
232 if (keyCode == KeyEvent.KEYCODE_ESCAPE) {
233 hideMenu();
234 return true;
235 }
236 return super.onKeyUp(keyCode, event);
237 }
238
239 @Override
Winson Chungc75ffe82016-12-16 16:20:16 -0800240 protected void onNewIntent(Intent intent) {
241 super.onNewIntent(intent);
Winson Chunge7a3d222017-03-09 13:26:45 -0800242 updateFromIntent(intent);
Winson Chung15504af2016-11-02 18:11:36 -0700243 }
244
245 @Override
Winson Chungd2d90972017-02-28 11:40:41 -0800246 public void onUserInteraction() {
Winson Chung379b9ab2017-04-04 11:57:36 -0700247 if (mAllowMenuTimeout) {
248 repostDelayedFinish(POST_INTERACTION_DISMISS_DELAY);
249 }
Winson Chungd2d90972017-02-28 11:40:41 -0800250 }
251
252 @Override
Winson Chung61ecc1b2017-02-17 10:46:17 -0800253 protected void onUserLeaveHint() {
254 super.onUserLeaveHint();
255
Winson Chung9f69b782017-05-16 15:27:49 -0700256 // 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 -0800257 // recreated on the top next time it starts
Winson Chung9f69b782017-05-16 15:27:49 -0700258 hideMenu();
Winson Chung61ecc1b2017-02-17 10:46:17 -0800259 }
260
261 @Override
Winson Chungef4dc812017-04-11 13:31:44 -0700262 protected void onStop() {
263 super.onStop();
264
Hongwei Wang9cdf4522019-09-26 10:42:38 -0700265 // In cases such as device lock, hide and finish it so that it can be recreated on the top
266 // next time it starts, see also {@link #onUserLeaveHint}
267 hideMenu();
Winson Chungef4dc812017-04-11 13:31:44 -0700268 cancelDelayedFinish();
269 }
270
271 @Override
Winson Chung61ecc1b2017-02-17 10:46:17 -0800272 protected void onDestroy() {
273 super.onDestroy();
274
275 // Fallback, if we are destroyed for any other reason (like when the task is being reset),
276 // also reset the callback.
277 notifyActivityCallback(null);
278 }
279
280 @Override
Winson Chungc75ffe82016-12-16 16:20:16 -0800281 public void onPictureInPictureModeChanged(boolean isInPictureInPictureMode) {
282 if (!isInPictureInPictureMode) {
283 finish();
284 }
285 }
286
Arthur Hungd8961972019-10-14 17:00:17 +0800287 /**
288 * Dispatch a pointer event from {@link PipTouchHandler}.
289 */
290 private void dispatchPointerEvent(MotionEvent event) {
291 if (event.isTouchEvent()) {
292 dispatchTouchEvent(event);
293 } else {
294 dispatchGenericMotionEvent(event);
295 }
296 }
297
Winson Chungc75ffe82016-12-16 16:20:16 -0800298 @Override
Winson Chungc75ffe82016-12-16 16:20:16 -0800299 public boolean dispatchTouchEvent(MotionEvent ev) {
Winson Chung34488242017-04-26 15:53:51 -0700300 if (!mAllowTouches) {
Arthur Hungd52d1192019-08-30 16:06:59 +0800301 return false;
Winson Chung34488242017-04-26 15:53:51 -0700302 }
303
Winson Chungc75ffe82016-12-16 16:20:16 -0800304 // On the first action outside the window, hide the menu
305 switch (ev.getAction()) {
306 case MotionEvent.ACTION_OUTSIDE:
307 hideMenu();
Arthur Hungd52d1192019-08-30 16:06:59 +0800308 return true;
Winson Chungc75ffe82016-12-16 16:20:16 -0800309 }
310 return super.dispatchTouchEvent(ev);
Winson Chung15504af2016-11-02 18:11:36 -0700311 }
312
313 @Override
314 public void finish() {
Winson Chungc75ffe82016-12-16 16:20:16 -0800315 notifyActivityCallback(null);
Winson Chung15504af2016-11-02 18:11:36 -0700316 super.finish();
Winson Chung15504af2016-11-02 18:11:36 -0700317 }
318
319 @Override
320 public void setTaskDescription(ActivityManager.TaskDescription taskDescription) {
321 // Do nothing
322 }
323
Mady Mellor637cd482017-03-21 10:39:42 -0700324 private void showMenu(int menuState, Rect stackBounds, Rect movementBounds,
Winson Chungbb787442017-09-01 11:33:47 -0700325 boolean allowMenuTimeout, boolean resizeMenuOnShow) {
Winson Chung379b9ab2017-04-04 11:57:36 -0700326 mAllowMenuTimeout = allowMenuTimeout;
Mady Mellor637cd482017-03-21 10:39:42 -0700327 if (mMenuState != menuState) {
Winson Chungbb787442017-09-01 11:33:47 -0700328 // Disallow touches if the menu needs to resize while showing, and we are transitioning
329 // to/from a full menu state.
330 boolean disallowTouchesUntilAnimationEnd = resizeMenuOnShow &&
331 (mMenuState == MENU_STATE_FULL || menuState == MENU_STATE_FULL);
332 mAllowTouches = !disallowTouchesUntilAnimationEnd;
Mady Mellor637cd482017-03-21 10:39:42 -0700333 cancelDelayedFinish();
Mady Mellor81d40612017-03-10 15:14:10 -0800334 updateActionViews(stackBounds);
Winson Chungc75ffe82016-12-16 16:20:16 -0800335 if (mMenuContainerAnimator != null) {
336 mMenuContainerAnimator.cancel();
337 }
Ben Lin559ea4d2019-11-06 12:36:41 -0800338 notifyMenuStateChange(menuState, resizeMenuOnShow);
Mady Mellor637cd482017-03-21 10:39:42 -0700339 mMenuContainerAnimator = new AnimatorSet();
340 ObjectAnimator menuAnim = ObjectAnimator.ofFloat(mMenuContainer, View.ALPHA,
Winson Chungc75ffe82016-12-16 16:20:16 -0800341 mMenuContainer.getAlpha(), 1f);
Mady Mellor637cd482017-03-21 10:39:42 -0700342 menuAnim.addUpdateListener(mMenuBgUpdateListener);
Winson Chunga556fe62017-12-19 14:46:23 -0800343 ObjectAnimator settingsAnim = ObjectAnimator.ofFloat(mSettingsButton, View.ALPHA,
344 mSettingsButton.getAlpha(), 1f);
Mady Mellor637cd482017-03-21 10:39:42 -0700345 ObjectAnimator dismissAnim = ObjectAnimator.ofFloat(mDismissButton, View.ALPHA,
346 mDismissButton.getAlpha(), 1f);
347 if (menuState == MENU_STATE_FULL) {
Winson Chunga556fe62017-12-19 14:46:23 -0800348 mMenuContainerAnimator.playTogether(menuAnim, settingsAnim, dismissAnim);
Mady Mellor637cd482017-03-21 10:39:42 -0700349 } else {
Winson Chung2a35e6d2018-01-13 14:27:50 -0800350 mMenuContainerAnimator.playTogether(dismissAnim);
Mady Mellor637cd482017-03-21 10:39:42 -0700351 }
Winson Chungc75ffe82016-12-16 16:20:16 -0800352 mMenuContainerAnimator.setInterpolator(Interpolators.ALPHA_IN);
353 mMenuContainerAnimator.setDuration(MENU_FADE_DURATION);
Winson Chung0f873de2017-03-30 10:26:48 -0700354 if (allowMenuTimeout) {
355 mMenuContainerAnimator.addListener(new AnimatorListenerAdapter() {
356 @Override
357 public void onAnimationEnd(Animator animation) {
358 repostDelayedFinish(INITIAL_DISMISS_DELAY);
359 }
360 });
361 }
Winson Chungc75ffe82016-12-16 16:20:16 -0800362 mMenuContainerAnimator.start();
Winson Chungd2d90972017-02-28 11:40:41 -0800363 } else {
Winson Chung2824d7c2017-03-15 19:43:00 -0700364 // If we are already visible, then just start the delayed dismiss and unregister any
365 // existing input consumers from the previous drag
Winson Chung0f873de2017-03-30 10:26:48 -0700366 if (allowMenuTimeout) {
367 repostDelayedFinish(POST_INTERACTION_DISMISS_DELAY);
368 }
Winson Chungc75ffe82016-12-16 16:20:16 -0800369 }
370 }
371
372 private void hideMenu() {
Winson Chung2dbcf092018-10-24 13:00:41 -0700373 hideMenu(null);
374 }
375
376 private void hideMenu(Runnable animationEndCallback) {
377 hideMenu(animationEndCallback, true /* notifyMenuVisibility */, false /* isDismissing */);
Winson Chungc75ffe82016-12-16 16:20:16 -0800378 }
379
Winson Chung21004d52018-06-04 17:09:32 -0700380 private void hideMenu(final Runnable animationFinishedRunnable, boolean notifyMenuVisibility,
381 boolean isDismissing) {
Mady Mellor637cd482017-03-21 10:39:42 -0700382 if (mMenuState != MENU_STATE_NONE) {
Winson Chungc75ffe82016-12-16 16:20:16 -0800383 cancelDelayedFinish();
Winson Chungd2d90972017-02-28 11:40:41 -0800384 if (notifyMenuVisibility) {
Ben Lin559ea4d2019-11-06 12:36:41 -0800385 notifyMenuStateChange(MENU_STATE_NONE, mResize);
Winson Chungd2d90972017-02-28 11:40:41 -0800386 }
Mady Mellor637cd482017-03-21 10:39:42 -0700387 mMenuContainerAnimator = new AnimatorSet();
388 ObjectAnimator menuAnim = ObjectAnimator.ofFloat(mMenuContainer, View.ALPHA,
Winson Chungc75ffe82016-12-16 16:20:16 -0800389 mMenuContainer.getAlpha(), 0f);
Mady Mellor637cd482017-03-21 10:39:42 -0700390 menuAnim.addUpdateListener(mMenuBgUpdateListener);
Winson Chunga556fe62017-12-19 14:46:23 -0800391 ObjectAnimator settingsAnim = ObjectAnimator.ofFloat(mSettingsButton, View.ALPHA,
392 mSettingsButton.getAlpha(), 0f);
Mady Mellor637cd482017-03-21 10:39:42 -0700393 ObjectAnimator dismissAnim = ObjectAnimator.ofFloat(mDismissButton, View.ALPHA,
394 mDismissButton.getAlpha(), 0f);
Winson Chunga556fe62017-12-19 14:46:23 -0800395 mMenuContainerAnimator.playTogether(menuAnim, settingsAnim, dismissAnim);
Winson Chungc75ffe82016-12-16 16:20:16 -0800396 mMenuContainerAnimator.setInterpolator(Interpolators.ALPHA_OUT);
397 mMenuContainerAnimator.setDuration(MENU_FADE_DURATION);
398 mMenuContainerAnimator.addListener(new AnimatorListenerAdapter() {
399 @Override
400 public void onAnimationEnd(Animator animation) {
401 if (animationFinishedRunnable != null) {
402 animationFinishedRunnable.run();
403 }
Winson Chung21004d52018-06-04 17:09:32 -0700404
405 if (!isDismissing) {
406 // If we are dismissing the PiP, then don't try to pre-emptively finish the
407 // menu activity
408 finish();
409 }
Winson Chungc75ffe82016-12-16 16:20:16 -0800410 }
411 });
412 mMenuContainerAnimator.start();
Winson Chung87e5d552017-04-05 11:49:38 -0700413 } else {
414 // If the menu is not visible, just finish now
415 finish();
Winson Chungc75ffe82016-12-16 16:20:16 -0800416 }
417 }
418
Winson Chunge7a3d222017-03-09 13:26:45 -0800419 private void updateFromIntent(Intent intent) {
Winson Chunge7a3d222017-03-09 13:26:45 -0800420 mToControllerMessenger = intent.getParcelableExtra(EXTRA_CONTROLLER_MESSENGER);
Jaewan Kimc1d15242017-07-07 18:12:04 +0900421 if (mToControllerMessenger == null) {
422 Log.w(TAG, "Controller messenger is null. Stopping.");
423 finish();
424 return;
425 }
Winson Chungccbc4b92017-03-20 14:01:36 -0700426 notifyActivityCallback(mMessenger);
Winson Chungb5026902017-05-03 12:45:13 -0700427
Winson Chunge7a3d222017-03-09 13:26:45 -0800428 ParceledListSlice actions = intent.getParcelableExtra(EXTRA_ACTIONS);
429 if (actions != null) {
Mady Mellor81d40612017-03-10 15:14:10 -0800430 mActions.clear();
431 mActions.addAll(actions.getList());
Winson Chunge7a3d222017-03-09 13:26:45 -0800432 }
Winson Chungb5026902017-05-03 12:45:13 -0700433
Mady Mellor637cd482017-03-21 10:39:42 -0700434 final int menuState = intent.getIntExtra(EXTRA_MENU_STATE, MENU_STATE_NONE);
435 if (menuState != MENU_STATE_NONE) {
Winson Chung853c99a2017-03-21 22:16:42 -0700436 Rect stackBounds = intent.getParcelableExtra(EXTRA_STACK_BOUNDS);
437 Rect movementBounds = intent.getParcelableExtra(EXTRA_MOVEMENT_BOUNDS);
Winson Chung0f873de2017-03-30 10:26:48 -0700438 boolean allowMenuTimeout = intent.getBooleanExtra(EXTRA_ALLOW_TIMEOUT, true);
Winson Chungbb787442017-09-01 11:33:47 -0700439 boolean willResizeMenu = intent.getBooleanExtra(EXTRA_WILL_RESIZE_MENU, false);
440 showMenu(menuState, stackBounds, movementBounds, allowMenuTimeout, willResizeMenu);
Mady Mellor81d40612017-03-10 15:14:10 -0800441 }
Winson Chunga29eb982016-12-14 12:01:27 -0800442 }
443
Winson Chunge7a3d222017-03-09 13:26:45 -0800444 private void setActions(Rect stackBounds, List<RemoteAction> actions) {
445 mActions.clear();
446 mActions.addAll(actions);
447 updateActionViews(stackBounds);
448 }
449
450 private void updateActionViews(Rect stackBounds) {
Alan Viverette51efddb2017-04-05 10:00:01 -0400451 ViewGroup expandContainer = findViewById(R.id.expand_container);
452 ViewGroup actionsContainer = findViewById(R.id.actions_container);
Winson Chunga5acf182017-01-05 16:02:27 -0800453 actionsContainer.setOnTouchListener((v, ev) -> {
454 // Do nothing, prevent click through to parent
455 return true;
456 });
Winson Chunga29eb982016-12-14 12:01:27 -0800457
Mady Mellor637cd482017-03-21 10:39:42 -0700458 if (mActions.isEmpty() || mMenuState == MENU_STATE_CLOSE) {
Winson Chunga5acf182017-01-05 16:02:27 -0800459 actionsContainer.setVisibility(View.INVISIBLE);
460 } else {
461 actionsContainer.setVisibility(View.VISIBLE);
Mady Mellora7f69742017-02-03 11:00:20 -0800462 if (mActionsGroup != null) {
Winson Chungb6de8722017-06-02 12:45:51 -0700463 // Ensure we have as many buttons as actions
Winson Chung7aab0c12017-04-18 12:54:25 -0700464 final LayoutInflater inflater = LayoutInflater.from(this);
465 while (mActionsGroup.getChildCount() < mActions.size()) {
Hongwei Wanga6e034e2019-06-20 13:18:43 -0700466 final ImageButton actionView = (ImageButton) inflater.inflate(
Winson Chung7aab0c12017-04-18 12:54:25 -0700467 R.layout.pip_menu_action, mActionsGroup, false);
468 mActionsGroup.addView(actionView);
469 }
Winson Chunga29eb982016-12-14 12:01:27 -0800470
Winson Chungb6de8722017-06-02 12:45:51 -0700471 // Update the visibility of all views
472 for (int i = 0; i < mActionsGroup.getChildCount(); i++) {
473 mActionsGroup.getChildAt(i).setVisibility(i < mActions.size()
474 ? View.VISIBLE
475 : View.GONE);
476 }
477
Winson Chunga5acf182017-01-05 16:02:27 -0800478 // Recreate the layout
Winson Chunge7a3d222017-03-09 13:26:45 -0800479 final boolean isLandscapePip = stackBounds != null &&
480 (stackBounds.width() > stackBounds.height());
Winson Chunga5acf182017-01-05 16:02:27 -0800481 for (int i = 0; i < mActions.size(); i++) {
482 final RemoteAction action = mActions.get(i);
Hongwei Wanga6e034e2019-06-20 13:18:43 -0700483 final ImageButton actionView = (ImageButton) mActionsGroup.getChildAt(i);
Winson Chung7aab0c12017-04-18 12:54:25 -0700484
485 // TODO: Check if the action drawable has changed before we reload it
Winson Chunga5acf182017-01-05 16:02:27 -0800486 action.getIcon().loadDrawableAsync(this, d -> {
Winson Chung56fe8832017-02-03 14:37:53 -0800487 d.setTint(Color.WHITE);
Winson Chunga5acf182017-01-05 16:02:27 -0800488 actionView.setImageDrawable(d);
489 }, mHandler);
490 actionView.setContentDescription(action.getContentDescription());
Winson Chung6b88baf2017-03-16 17:10:21 -0700491 if (action.isEnabled()) {
492 actionView.setOnClickListener(v -> {
Arthur Hungd52d1192019-08-30 16:06:59 +0800493 mHandler.post(() -> {
494 try {
495 action.getActionIntent().send();
496 } catch (CanceledException e) {
497 Log.w(TAG, "Failed to send action", e);
498 }
499 });
Winson Chung6b88baf2017-03-16 17:10:21 -0700500 });
Winson Chung6b88baf2017-03-16 17:10:21 -0700501 }
Winson Chungd61a19e2017-04-28 16:36:51 -0700502 actionView.setEnabled(action.isEnabled());
Winson Chungb6de8722017-06-02 12:45:51 -0700503 actionView.setAlpha(action.isEnabled() ? 1f : DISABLED_ACTION_ALPHA);
Winson Chung7aab0c12017-04-18 12:54:25 -0700504
505 // Update the margin between actions
506 LinearLayout.LayoutParams lp = (LinearLayout.LayoutParams)
507 actionView.getLayoutParams();
508 lp.leftMargin = (isLandscapePip && i > 0) ? mBetweenActionPaddingLand : 0;
Winson Chunga5acf182017-01-05 16:02:27 -0800509 }
Winson Chunga29eb982016-12-14 12:01:27 -0800510 }
Mady Mellora7f69742017-02-03 11:00:20 -0800511
Winson Chunge7a3d222017-03-09 13:26:45 -0800512 // Update the expand container margin to adjust the center of the expand button to
513 // account for the existence of the action container
514 FrameLayout.LayoutParams expandedLp =
515 (FrameLayout.LayoutParams) expandContainer.getLayoutParams();
516 expandedLp.topMargin = getResources().getDimensionPixelSize(
517 R.dimen.pip_action_padding);
518 expandedLp.bottomMargin = getResources().getDimensionPixelSize(
519 R.dimen.pip_expand_container_edge_margin);
520 expandContainer.requestLayout();
521 }
Winson Chunga29eb982016-12-14 12:01:27 -0800522 }
523
Mady Mellor81d40612017-03-10 15:14:10 -0800524 private void updateDismissFraction(float fraction) {
525 int alpha;
Mady Mellor637cd482017-03-21 10:39:42 -0700526 final float menuAlpha = 1 - fraction;
527 if (mMenuState == MENU_STATE_FULL) {
528 mMenuContainer.setAlpha(menuAlpha);
Winson Chunga556fe62017-12-19 14:46:23 -0800529 mSettingsButton.setAlpha(menuAlpha);
Mady Mellor637cd482017-03-21 10:39:42 -0700530 mDismissButton.setAlpha(menuAlpha);
Mady Mellor81d40612017-03-10 15:14:10 -0800531 final float interpolatedAlpha =
Mady Mellor637cd482017-03-21 10:39:42 -0700532 MENU_BACKGROUND_ALPHA * menuAlpha + DISMISS_BACKGROUND_ALPHA * fraction;
Winson Chungc3545d32017-04-03 10:44:57 -0700533 alpha = (int) (interpolatedAlpha * 255);
Mady Mellor81d40612017-03-10 15:14:10 -0800534 } else {
Mady Mellor637cd482017-03-21 10:39:42 -0700535 if (mMenuState == MENU_STATE_CLOSE) {
536 mDismissButton.setAlpha(menuAlpha);
537 }
Winson Chungc3545d32017-04-03 10:44:57 -0700538 alpha = (int) (fraction * DISMISS_BACKGROUND_ALPHA * 255);
Mady Mellor81d40612017-03-10 15:14:10 -0800539 }
540 mBackgroundDrawable.setAlpha(alpha);
541 }
542
Ben Lin559ea4d2019-11-06 12:36:41 -0800543 private void notifyMenuStateChange(int menuState, boolean resize) {
Mady Mellor637cd482017-03-21 10:39:42 -0700544 mMenuState = menuState;
Ben Lin559ea4d2019-11-06 12:36:41 -0800545 mResize = resize;
Winson Chung15504af2016-11-02 18:11:36 -0700546 Message m = Message.obtain();
Mady Mellor637cd482017-03-21 10:39:42 -0700547 m.what = PipMenuActivityController.MESSAGE_MENU_STATE_CHANGED;
548 m.arg1 = menuState;
Ben Lin559ea4d2019-11-06 12:36:41 -0800549 m.arg2 = resize ? 1 : 0;
Winson Chunga29eb982016-12-14 12:01:27 -0800550 sendMessage(m, "Could not notify controller of PIP menu visibility");
Winson Chung15504af2016-11-02 18:11:36 -0700551 }
552
Winson Chunga29eb982016-12-14 12:01:27 -0800553 private void expandPip() {
Winson Chungd2d90972017-02-28 11:40:41 -0800554 // Do not notify menu visibility when hiding the menu, the controller will do this when it
555 // handles the message
Winson Chungc75ffe82016-12-16 16:20:16 -0800556 hideMenu(() -> {
557 sendEmptyMessage(PipMenuActivityController.MESSAGE_EXPAND_PIP,
558 "Could not notify controller to expand PIP");
Winson Chung21004d52018-06-04 17:09:32 -0700559 }, false /* notifyMenuVisibility */, false /* isDismissing */);
Winson Chunga29eb982016-12-14 12:01:27 -0800560 }
561
Winson Chunga29eb982016-12-14 12:01:27 -0800562 private void dismissPip() {
Winson Chungd2d90972017-02-28 11:40:41 -0800563 // Do not notify menu visibility when hiding the menu, the controller will do this when it
564 // handles the message
Winson Chungc75ffe82016-12-16 16:20:16 -0800565 hideMenu(() -> {
566 sendEmptyMessage(PipMenuActivityController.MESSAGE_DISMISS_PIP,
567 "Could not notify controller to dismiss PIP");
Winson Chung21004d52018-06-04 17:09:32 -0700568 }, false /* notifyMenuVisibility */, true /* isDismissing */);
Winson Chungc75ffe82016-12-16 16:20:16 -0800569 }
570
Winson Chunga556fe62017-12-19 14:46:23 -0800571 private void showSettings() {
572 final Pair<ComponentName, Integer> topPipActivityInfo =
Winson Chungbe00a832020-03-10 11:08:35 -0700573 PipUtils.getTopPipActivity(this, ActivityManager.getService());
Winson Chunga556fe62017-12-19 14:46:23 -0800574 if (topPipActivityInfo.first != null) {
575 final UserHandle user = UserHandle.of(topPipActivityInfo.second);
576 final Intent settingsIntent = new Intent(ACTION_PICTURE_IN_PICTURE_SETTINGS,
577 Uri.fromParts("package", topPipActivityInfo.first.getPackageName(), null));
578 settingsIntent.putExtra(Intent.EXTRA_USER_HANDLE, user);
579 settingsIntent.setFlags(FLAG_ACTIVITY_NEW_TASK | FLAG_ACTIVITY_CLEAR_TASK);
580 startActivity(settingsIntent);
581 }
582 }
583
Winson Chungc75ffe82016-12-16 16:20:16 -0800584 private void notifyActivityCallback(Messenger callback) {
585 Message m = Message.obtain();
586 m.what = PipMenuActivityController.MESSAGE_UPDATE_ACTIVITY_CALLBACK;
587 m.replyTo = callback;
Ben Lin559ea4d2019-11-06 12:36:41 -0800588 m.arg1 = mResize ? 1 : 0;
Winson Chungc75ffe82016-12-16 16:20:16 -0800589 sendMessage(m, "Could not notify controller of activity finished");
Winson Chunga29eb982016-12-14 12:01:27 -0800590 }
591
592 private void sendEmptyMessage(int what, String errorMsg) {
Winson Chung15504af2016-11-02 18:11:36 -0700593 Message m = Message.obtain();
Winson Chunga29eb982016-12-14 12:01:27 -0800594 m.what = what;
595 sendMessage(m, errorMsg);
596 }
597
598 private void sendMessage(Message m, String errorMsg) {
Jaewan Kimc1d15242017-07-07 18:12:04 +0900599 if (mToControllerMessenger == null) {
600 return;
601 }
Winson Chung15504af2016-11-02 18:11:36 -0700602 try {
603 mToControllerMessenger.send(m);
604 } catch (RemoteException e) {
Winson Chunga29eb982016-12-14 12:01:27 -0800605 Log.e(TAG, errorMsg, e);
Winson Chung15504af2016-11-02 18:11:36 -0700606 }
607 }
608
Winson Chungc75ffe82016-12-16 16:20:16 -0800609 private void cancelDelayedFinish() {
Winson Chung4a526c12017-05-16 13:35:43 -0700610 mHandler.removeCallbacks(mFinishRunnable);
Winson Chungc75ffe82016-12-16 16:20:16 -0800611 }
612
Rhed Jao5e00c982018-11-27 19:41:05 +0800613 private void repostDelayedFinish(int delay) {
614 int recommendedTimeout = mAccessibilityManager.getRecommendedTimeoutMillis(delay,
615 FLAG_CONTENT_ICONS | FLAG_CONTENT_CONTROLS);
Winson Chung4a526c12017-05-16 13:35:43 -0700616 mHandler.removeCallbacks(mFinishRunnable);
Rhed Jao5e00c982018-11-27 19:41:05 +0800617 mHandler.postDelayed(mFinishRunnable, recommendedTimeout);
Winson Chung15504af2016-11-02 18:11:36 -0700618 }
Winson Chung15504af2016-11-02 18:11:36 -0700619}