blob: 5723afd4ae950efdcf499ff4f5422a4b65e78c68 [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;
Winson Chunga29eb982016-12-14 12:01:27 -080063import android.view.LayoutInflater;
Winson Chungc75ffe82016-12-16 16:20:16 -080064import android.view.MotionEvent;
Winson Chung15504af2016-11-02 18:11:36 -070065import android.view.View;
Winson Chunga29eb982016-12-14 12:01:27 -080066import android.view.ViewGroup;
Winson Chungc75ffe82016-12-16 16:20:16 -080067import android.view.WindowManager.LayoutParams;
Rhed Jao5e00c982018-11-27 19:41:05 +080068import android.view.accessibility.AccessibilityManager;
Mady Mellora7f69742017-02-03 11:00:20 -080069import android.widget.FrameLayout;
Hongwei Wanga6e034e2019-06-20 13:18:43 -070070import android.widget.ImageButton;
Mady Mellora7f69742017-02-03 11:00:20 -080071import android.widget.LinearLayout;
Winson Chunga29eb982016-12-14 12:01:27 -080072
Winson Chungc75ffe82016-12-16 16:20:16 -080073import com.android.systemui.Interpolators;
Winson Chung15504af2016-11-02 18:11:36 -070074import com.android.systemui.R;
75
Winson Chunga29eb982016-12-14 12:01:27 -080076import java.util.ArrayList;
Winson Chung26db3972017-03-23 15:17:01 -070077import java.util.Collections;
Winson Chunga29eb982016-12-14 12:01:27 -080078import java.util.List;
79
Winson Chung15504af2016-11-02 18:11:36 -070080/**
81 * Translucent activity that gets started on top of a task in PIP to allow the user to control it.
82 */
83public class PipMenuActivity extends Activity {
84
85 private static final String TAG = "PipMenuActivity";
86
Winson Chungc75ffe82016-12-16 16:20:16 -080087 public static final int MESSAGE_SHOW_MENU = 1;
Mady Mellora7f69742017-02-03 11:00:20 -080088 public static final int MESSAGE_POKE_MENU = 2;
89 public static final int MESSAGE_HIDE_MENU = 3;
90 public static final int MESSAGE_UPDATE_ACTIONS = 4;
Mady Mellor81d40612017-03-10 15:14:10 -080091 public static final int MESSAGE_UPDATE_DISMISS_FRACTION = 5;
Winson Chung34488242017-04-26 15:53:51 -070092 public static final int MESSAGE_ANIMATION_ENDED = 6;
Arthur Hungd52d1192019-08-30 16:06:59 +080093 public static final int MESSAGE_TOUCH_EVENT = 7;
Winson Chung15504af2016-11-02 18:11:36 -070094
Rhed Jao5e00c982018-11-27 19:41:05 +080095 private static final int INITIAL_DISMISS_DELAY = 3500;
96 private static final int POST_INTERACTION_DISMISS_DELAY = 2000;
Winson Chungc75ffe82016-12-16 16:20:16 -080097 private static final long MENU_FADE_DURATION = 125;
Winson Chung15504af2016-11-02 18:11:36 -070098
Mady Mellor81d40612017-03-10 15:14:10 -080099 private static final float MENU_BACKGROUND_ALPHA = 0.3f;
Mady Mellor60421c92017-03-29 15:27:37 -0700100 private static final float DISMISS_BACKGROUND_ALPHA = 0.6f;
Mady Mellor81d40612017-03-10 15:14:10 -0800101
Winson Chung6b88baf2017-03-16 17:10:21 -0700102 private static final float DISABLED_ACTION_ALPHA = 0.54f;
103
Mady Mellor637cd482017-03-21 10:39:42 -0700104 private int mMenuState;
Winson Chung379b9ab2017-04-04 11:57:36 -0700105 private boolean mAllowMenuTimeout = true;
Winson Chung34488242017-04-26 15:53:51 -0700106 private boolean mAllowTouches = true;
Winson Chung87e5d552017-04-05 11:49:38 -0700107
Winson Chungc75ffe82016-12-16 16:20:16 -0800108 private final List<RemoteAction> mActions = new ArrayList<>();
Winson Chung87e5d552017-04-05 11:49:38 -0700109
Rhed Jao5e00c982018-11-27 19:41:05 +0800110 private AccessibilityManager mAccessibilityManager;
Mady Mellor81d40612017-03-10 15:14:10 -0800111 private View mViewRoot;
112 private Drawable mBackgroundDrawable;
Winson Chungc75ffe82016-12-16 16:20:16 -0800113 private View mMenuContainer;
Mady Mellora7f69742017-02-03 11:00:20 -0800114 private LinearLayout mActionsGroup;
Winson Chunga556fe62017-12-19 14:46:23 -0800115 private View mSettingsButton;
Winson Chunga29eb982016-12-14 12:01:27 -0800116 private View mDismissButton;
Mady Mellora7f69742017-02-03 11:00:20 -0800117 private int mBetweenActionPaddingLand;
Winson Chunga29eb982016-12-14 12:01:27 -0800118
Mady Mellor637cd482017-03-21 10:39:42 -0700119 private AnimatorSet mMenuContainerAnimator;
120
Mady Mellor81d40612017-03-10 15:14:10 -0800121 private ValueAnimator.AnimatorUpdateListener mMenuBgUpdateListener =
122 new ValueAnimator.AnimatorUpdateListener() {
123 @Override
124 public void onAnimationUpdate(ValueAnimator animation) {
125 final float alpha = (float) animation.getAnimatedValue();
126 mBackgroundDrawable.setAlpha((int) (MENU_BACKGROUND_ALPHA*alpha*255));
127 }
128 };
Winson Chungc75ffe82016-12-16 16:20:16 -0800129
Winson Chunga29eb982016-12-14 12:01:27 -0800130 private Handler mHandler = new Handler();
Winson Chung15504af2016-11-02 18:11:36 -0700131 private Messenger mToControllerMessenger;
132 private Messenger mMessenger = new Messenger(new Handler() {
133 @Override
134 public void handleMessage(Message msg) {
135 switch (msg.what) {
Winson Chung853c99a2017-03-21 22:16:42 -0700136 case MESSAGE_SHOW_MENU: {
137 final Bundle data = (Bundle) msg.obj;
Mady Mellor637cd482017-03-21 10:39:42 -0700138 showMenu(data.getInt(EXTRA_MENU_STATE),
139 data.getParcelable(EXTRA_STACK_BOUNDS),
Winson Chung0f873de2017-03-30 10:26:48 -0700140 data.getParcelable(EXTRA_MOVEMENT_BOUNDS),
Winson Chungbb787442017-09-01 11:33:47 -0700141 data.getBoolean(EXTRA_ALLOW_TIMEOUT),
142 data.getBoolean(EXTRA_WILL_RESIZE_MENU));
Winson Chungc75ffe82016-12-16 16:20:16 -0800143 break;
Winson Chung853c99a2017-03-21 22:16:42 -0700144 }
Mady Mellora7f69742017-02-03 11:00:20 -0800145 case MESSAGE_POKE_MENU:
146 cancelDelayedFinish();
147 break;
Winson Chungc75ffe82016-12-16 16:20:16 -0800148 case MESSAGE_HIDE_MENU:
Winson Chung2dbcf092018-10-24 13:00:41 -0700149 hideMenu((Runnable) msg.obj);
Winson Chungc75ffe82016-12-16 16:20:16 -0800150 break;
Winson Chung853c99a2017-03-21 22:16:42 -0700151 case MESSAGE_UPDATE_ACTIONS: {
152 final Bundle data = (Bundle) msg.obj;
Winson Chung26db3972017-03-23 15:17:01 -0700153 final ParceledListSlice actions = data.getParcelable(EXTRA_ACTIONS);
154 setActions(data.getParcelable(EXTRA_STACK_BOUNDS), actions != null
155 ? actions.getList() : Collections.EMPTY_LIST);
Winson Chunga29eb982016-12-14 12:01:27 -0800156 break;
Winson Chung853c99a2017-03-21 22:16:42 -0700157 }
158 case MESSAGE_UPDATE_DISMISS_FRACTION: {
159 final Bundle data = (Bundle) msg.obj;
160 updateDismissFraction(data.getFloat(EXTRA_DISMISS_FRACTION));
Mady Mellor81d40612017-03-10 15:14:10 -0800161 break;
Winson Chung853c99a2017-03-21 22:16:42 -0700162 }
Winson Chung34488242017-04-26 15:53:51 -0700163 case MESSAGE_ANIMATION_ENDED: {
164 mAllowTouches = true;
165 break;
166 }
Arthur Hungd52d1192019-08-30 16:06:59 +0800167
168 case MESSAGE_TOUCH_EVENT: {
169 final MotionEvent ev = (MotionEvent) msg.obj;
170 dispatchTouchEvent(ev);
171 break;
172 }
Winson Chung15504af2016-11-02 18:11:36 -0700173 }
174 }
175 });
176
177 private final Runnable mFinishRunnable = new Runnable() {
178 @Override
179 public void run() {
Winson Chungc75ffe82016-12-16 16:20:16 -0800180 hideMenu();
Winson Chung15504af2016-11-02 18:11:36 -0700181 }
182 };
183
184 @Override
185 protected void onCreate(@Nullable Bundle savedInstanceState) {
Winson Chungc75ffe82016-12-16 16:20:16 -0800186 // Set the flags to allow us to watch for outside touches and also hide the menu and start
187 // manipulating the PIP in the same touch gesture
Arthur Hungd52d1192019-08-30 16:06:59 +0800188 getWindow().addFlags(LayoutParams.FLAG_WATCH_OUTSIDE_TOUCH);
Winson Chungc75ffe82016-12-16 16:20:16 -0800189
Winson Chung15504af2016-11-02 18:11:36 -0700190 super.onCreate(savedInstanceState);
Winson Chung15504af2016-11-02 18:11:36 -0700191 setContentView(R.layout.pip_menu_activity);
Winson Chunga29eb982016-12-14 12:01:27 -0800192
Rhed Jao5e00c982018-11-27 19:41:05 +0800193 mAccessibilityManager = getSystemService(AccessibilityManager.class);
Mady Mellor81d40612017-03-10 15:14:10 -0800194 mBackgroundDrawable = new ColorDrawable(Color.BLACK);
195 mBackgroundDrawable.setAlpha(0);
196 mViewRoot = findViewById(R.id.background);
197 mViewRoot.setBackground(mBackgroundDrawable);
198 mMenuContainer = findViewById(R.id.menu_container);
199 mMenuContainer.setAlpha(0);
Winson Chunga556fe62017-12-19 14:46:23 -0800200 mSettingsButton = findViewById(R.id.settings);
201 mSettingsButton.setAlpha(0);
202 mSettingsButton.setOnClickListener((v) -> {
Tracy Zhou2c6117a2018-06-21 11:32:43 -0700203 if (v.getAlpha() != 0) {
204 showSettings();
205 }
Winson Chunga556fe62017-12-19 14:46:23 -0800206 });
Winson Chunga29eb982016-12-14 12:01:27 -0800207 mDismissButton = findViewById(R.id.dismiss);
Mady Mellor637cd482017-03-21 10:39:42 -0700208 mDismissButton.setAlpha(0);
Hongwei Wang10649502019-06-26 13:39:24 -0700209 mDismissButton.setOnClickListener(v -> dismissPip());
Arthur Hungd52d1192019-08-30 16:06:59 +0800210 findViewById(R.id.expand_button).setOnClickListener(v -> {
211 if (mMenuContainer.getAlpha() != 0) {
212 expandPip();
213 }
214 });
Alan Viverette51efddb2017-04-05 10:00:01 -0400215 mActionsGroup = findViewById(R.id.actions_group);
Mady Mellora7f69742017-02-03 11:00:20 -0800216 mBetweenActionPaddingLand = getResources().getDimensionPixelSize(
217 R.dimen.pip_between_action_padding_land);
Winson Chungc75ffe82016-12-16 16:20:16 -0800218
Winson Chunge7a3d222017-03-09 13:26:45 -0800219 updateFromIntent(getIntent());
Phil Weaverf00cd142017-03-03 13:44:00 -0800220 setTitle(R.string.pip_menu_title);
Wale Ogunwale5e8ecb02017-04-14 13:15:55 -0700221 setDisablePreviewScreenshots(true);
Winson Chungc75ffe82016-12-16 16:20:16 -0800222 }
223
224 @Override
225 protected void onNewIntent(Intent intent) {
226 super.onNewIntent(intent);
Winson Chunge7a3d222017-03-09 13:26:45 -0800227 updateFromIntent(intent);
Winson Chung15504af2016-11-02 18:11:36 -0700228 }
229
230 @Override
Winson Chungd2d90972017-02-28 11:40:41 -0800231 public void onUserInteraction() {
Winson Chung379b9ab2017-04-04 11:57:36 -0700232 if (mAllowMenuTimeout) {
233 repostDelayedFinish(POST_INTERACTION_DISMISS_DELAY);
234 }
Winson Chungd2d90972017-02-28 11:40:41 -0800235 }
236
237 @Override
Winson Chung61ecc1b2017-02-17 10:46:17 -0800238 protected void onUserLeaveHint() {
239 super.onUserLeaveHint();
240
Winson Chung9f69b782017-05-16 15:27:49 -0700241 // 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 -0800242 // recreated on the top next time it starts
Winson Chung9f69b782017-05-16 15:27:49 -0700243 hideMenu();
Winson Chung61ecc1b2017-02-17 10:46:17 -0800244 }
245
246 @Override
Winson Chungef4dc812017-04-11 13:31:44 -0700247 protected void onStop() {
248 super.onStop();
249
Hongwei Wang9cdf4522019-09-26 10:42:38 -0700250 // In cases such as device lock, hide and finish it so that it can be recreated on the top
251 // next time it starts, see also {@link #onUserLeaveHint}
252 hideMenu();
Winson Chungef4dc812017-04-11 13:31:44 -0700253 cancelDelayedFinish();
254 }
255
256 @Override
Winson Chung61ecc1b2017-02-17 10:46:17 -0800257 protected void onDestroy() {
258 super.onDestroy();
259
260 // Fallback, if we are destroyed for any other reason (like when the task is being reset),
261 // also reset the callback.
262 notifyActivityCallback(null);
263 }
264
265 @Override
Winson Chungc75ffe82016-12-16 16:20:16 -0800266 public void onPictureInPictureModeChanged(boolean isInPictureInPictureMode) {
267 if (!isInPictureInPictureMode) {
268 finish();
269 }
270 }
271
272 @Override
Winson Chungc75ffe82016-12-16 16:20:16 -0800273 public boolean dispatchTouchEvent(MotionEvent ev) {
Winson Chung34488242017-04-26 15:53:51 -0700274 if (!mAllowTouches) {
Arthur Hungd52d1192019-08-30 16:06:59 +0800275 return false;
Winson Chung34488242017-04-26 15:53:51 -0700276 }
277
Winson Chungc75ffe82016-12-16 16:20:16 -0800278 // On the first action outside the window, hide the menu
279 switch (ev.getAction()) {
280 case MotionEvent.ACTION_OUTSIDE:
281 hideMenu();
Arthur Hungd52d1192019-08-30 16:06:59 +0800282 return true;
Winson Chungc75ffe82016-12-16 16:20:16 -0800283 }
284 return super.dispatchTouchEvent(ev);
Winson Chung15504af2016-11-02 18:11:36 -0700285 }
286
287 @Override
288 public void finish() {
Winson Chungc75ffe82016-12-16 16:20:16 -0800289 notifyActivityCallback(null);
Winson Chung15504af2016-11-02 18:11:36 -0700290 super.finish();
Winson Chungc75ffe82016-12-16 16:20:16 -0800291 // Hide without an animation (the menu should already be invisible at this point)
292 overridePendingTransition(0, 0);
Winson Chung15504af2016-11-02 18:11:36 -0700293 }
294
295 @Override
296 public void setTaskDescription(ActivityManager.TaskDescription taskDescription) {
297 // Do nothing
298 }
299
Mady Mellor637cd482017-03-21 10:39:42 -0700300 private void showMenu(int menuState, Rect stackBounds, Rect movementBounds,
Winson Chungbb787442017-09-01 11:33:47 -0700301 boolean allowMenuTimeout, boolean resizeMenuOnShow) {
Winson Chung379b9ab2017-04-04 11:57:36 -0700302 mAllowMenuTimeout = allowMenuTimeout;
Mady Mellor637cd482017-03-21 10:39:42 -0700303 if (mMenuState != menuState) {
Winson Chungbb787442017-09-01 11:33:47 -0700304 // Disallow touches if the menu needs to resize while showing, and we are transitioning
305 // to/from a full menu state.
306 boolean disallowTouchesUntilAnimationEnd = resizeMenuOnShow &&
307 (mMenuState == MENU_STATE_FULL || menuState == MENU_STATE_FULL);
308 mAllowTouches = !disallowTouchesUntilAnimationEnd;
Mady Mellor637cd482017-03-21 10:39:42 -0700309 cancelDelayedFinish();
Mady Mellor81d40612017-03-10 15:14:10 -0800310 updateActionViews(stackBounds);
Winson Chungc75ffe82016-12-16 16:20:16 -0800311 if (mMenuContainerAnimator != null) {
312 mMenuContainerAnimator.cancel();
313 }
Mady Mellor637cd482017-03-21 10:39:42 -0700314 notifyMenuStateChange(menuState);
Mady Mellor637cd482017-03-21 10:39:42 -0700315 mMenuContainerAnimator = new AnimatorSet();
316 ObjectAnimator menuAnim = ObjectAnimator.ofFloat(mMenuContainer, View.ALPHA,
Winson Chungc75ffe82016-12-16 16:20:16 -0800317 mMenuContainer.getAlpha(), 1f);
Mady Mellor637cd482017-03-21 10:39:42 -0700318 menuAnim.addUpdateListener(mMenuBgUpdateListener);
Winson Chunga556fe62017-12-19 14:46:23 -0800319 ObjectAnimator settingsAnim = ObjectAnimator.ofFloat(mSettingsButton, View.ALPHA,
320 mSettingsButton.getAlpha(), 1f);
Mady Mellor637cd482017-03-21 10:39:42 -0700321 ObjectAnimator dismissAnim = ObjectAnimator.ofFloat(mDismissButton, View.ALPHA,
322 mDismissButton.getAlpha(), 1f);
323 if (menuState == MENU_STATE_FULL) {
Winson Chunga556fe62017-12-19 14:46:23 -0800324 mMenuContainerAnimator.playTogether(menuAnim, settingsAnim, dismissAnim);
Mady Mellor637cd482017-03-21 10:39:42 -0700325 } else {
Winson Chung2a35e6d2018-01-13 14:27:50 -0800326 mMenuContainerAnimator.playTogether(dismissAnim);
Mady Mellor637cd482017-03-21 10:39:42 -0700327 }
Winson Chungc75ffe82016-12-16 16:20:16 -0800328 mMenuContainerAnimator.setInterpolator(Interpolators.ALPHA_IN);
329 mMenuContainerAnimator.setDuration(MENU_FADE_DURATION);
Winson Chung0f873de2017-03-30 10:26:48 -0700330 if (allowMenuTimeout) {
331 mMenuContainerAnimator.addListener(new AnimatorListenerAdapter() {
332 @Override
333 public void onAnimationEnd(Animator animation) {
334 repostDelayedFinish(INITIAL_DISMISS_DELAY);
335 }
336 });
337 }
Winson Chungc75ffe82016-12-16 16:20:16 -0800338 mMenuContainerAnimator.start();
Winson Chungd2d90972017-02-28 11:40:41 -0800339 } else {
Winson Chung2824d7c2017-03-15 19:43:00 -0700340 // If we are already visible, then just start the delayed dismiss and unregister any
341 // existing input consumers from the previous drag
Winson Chung0f873de2017-03-30 10:26:48 -0700342 if (allowMenuTimeout) {
343 repostDelayedFinish(POST_INTERACTION_DISMISS_DELAY);
344 }
Winson Chungc75ffe82016-12-16 16:20:16 -0800345 }
346 }
347
348 private void hideMenu() {
Winson Chung2dbcf092018-10-24 13:00:41 -0700349 hideMenu(null);
350 }
351
352 private void hideMenu(Runnable animationEndCallback) {
353 hideMenu(animationEndCallback, true /* notifyMenuVisibility */, false /* isDismissing */);
Winson Chungc75ffe82016-12-16 16:20:16 -0800354 }
355
Winson Chung21004d52018-06-04 17:09:32 -0700356 private void hideMenu(final Runnable animationFinishedRunnable, boolean notifyMenuVisibility,
357 boolean isDismissing) {
Mady Mellor637cd482017-03-21 10:39:42 -0700358 if (mMenuState != MENU_STATE_NONE) {
Winson Chungc75ffe82016-12-16 16:20:16 -0800359 cancelDelayedFinish();
Winson Chungd2d90972017-02-28 11:40:41 -0800360 if (notifyMenuVisibility) {
Mady Mellor637cd482017-03-21 10:39:42 -0700361 notifyMenuStateChange(MENU_STATE_NONE);
Winson Chungd2d90972017-02-28 11:40:41 -0800362 }
Mady Mellor637cd482017-03-21 10:39:42 -0700363 mMenuContainerAnimator = new AnimatorSet();
364 ObjectAnimator menuAnim = ObjectAnimator.ofFloat(mMenuContainer, View.ALPHA,
Winson Chungc75ffe82016-12-16 16:20:16 -0800365 mMenuContainer.getAlpha(), 0f);
Mady Mellor637cd482017-03-21 10:39:42 -0700366 menuAnim.addUpdateListener(mMenuBgUpdateListener);
Winson Chunga556fe62017-12-19 14:46:23 -0800367 ObjectAnimator settingsAnim = ObjectAnimator.ofFloat(mSettingsButton, View.ALPHA,
368 mSettingsButton.getAlpha(), 0f);
Mady Mellor637cd482017-03-21 10:39:42 -0700369 ObjectAnimator dismissAnim = ObjectAnimator.ofFloat(mDismissButton, View.ALPHA,
370 mDismissButton.getAlpha(), 0f);
Winson Chunga556fe62017-12-19 14:46:23 -0800371 mMenuContainerAnimator.playTogether(menuAnim, settingsAnim, dismissAnim);
Winson Chungc75ffe82016-12-16 16:20:16 -0800372 mMenuContainerAnimator.setInterpolator(Interpolators.ALPHA_OUT);
373 mMenuContainerAnimator.setDuration(MENU_FADE_DURATION);
374 mMenuContainerAnimator.addListener(new AnimatorListenerAdapter() {
375 @Override
376 public void onAnimationEnd(Animator animation) {
377 if (animationFinishedRunnable != null) {
378 animationFinishedRunnable.run();
379 }
Winson Chung21004d52018-06-04 17:09:32 -0700380
381 if (!isDismissing) {
382 // If we are dismissing the PiP, then don't try to pre-emptively finish the
383 // menu activity
384 finish();
385 }
Winson Chungc75ffe82016-12-16 16:20:16 -0800386 }
387 });
388 mMenuContainerAnimator.start();
Winson Chung87e5d552017-04-05 11:49:38 -0700389 } else {
390 // If the menu is not visible, just finish now
391 finish();
Winson Chungc75ffe82016-12-16 16:20:16 -0800392 }
393 }
394
Winson Chunge7a3d222017-03-09 13:26:45 -0800395 private void updateFromIntent(Intent intent) {
Winson Chunge7a3d222017-03-09 13:26:45 -0800396 mToControllerMessenger = intent.getParcelableExtra(EXTRA_CONTROLLER_MESSENGER);
Jaewan Kimc1d15242017-07-07 18:12:04 +0900397 if (mToControllerMessenger == null) {
398 Log.w(TAG, "Controller messenger is null. Stopping.");
399 finish();
400 return;
401 }
Winson Chungccbc4b92017-03-20 14:01:36 -0700402 notifyActivityCallback(mMessenger);
Winson Chungb5026902017-05-03 12:45:13 -0700403
Winson Chunge7a3d222017-03-09 13:26:45 -0800404 ParceledListSlice actions = intent.getParcelableExtra(EXTRA_ACTIONS);
405 if (actions != null) {
Mady Mellor81d40612017-03-10 15:14:10 -0800406 mActions.clear();
407 mActions.addAll(actions.getList());
Winson Chunge7a3d222017-03-09 13:26:45 -0800408 }
Winson Chungb5026902017-05-03 12:45:13 -0700409
Mady Mellor637cd482017-03-21 10:39:42 -0700410 final int menuState = intent.getIntExtra(EXTRA_MENU_STATE, MENU_STATE_NONE);
411 if (menuState != MENU_STATE_NONE) {
Winson Chung853c99a2017-03-21 22:16:42 -0700412 Rect stackBounds = intent.getParcelableExtra(EXTRA_STACK_BOUNDS);
413 Rect movementBounds = intent.getParcelableExtra(EXTRA_MOVEMENT_BOUNDS);
Winson Chung0f873de2017-03-30 10:26:48 -0700414 boolean allowMenuTimeout = intent.getBooleanExtra(EXTRA_ALLOW_TIMEOUT, true);
Winson Chungbb787442017-09-01 11:33:47 -0700415 boolean willResizeMenu = intent.getBooleanExtra(EXTRA_WILL_RESIZE_MENU, false);
416 showMenu(menuState, stackBounds, movementBounds, allowMenuTimeout, willResizeMenu);
Mady Mellor81d40612017-03-10 15:14:10 -0800417 }
Winson Chunga29eb982016-12-14 12:01:27 -0800418 }
419
Winson Chunge7a3d222017-03-09 13:26:45 -0800420 private void setActions(Rect stackBounds, List<RemoteAction> actions) {
421 mActions.clear();
422 mActions.addAll(actions);
423 updateActionViews(stackBounds);
424 }
425
426 private void updateActionViews(Rect stackBounds) {
Alan Viverette51efddb2017-04-05 10:00:01 -0400427 ViewGroup expandContainer = findViewById(R.id.expand_container);
428 ViewGroup actionsContainer = findViewById(R.id.actions_container);
Winson Chunga5acf182017-01-05 16:02:27 -0800429 actionsContainer.setOnTouchListener((v, ev) -> {
430 // Do nothing, prevent click through to parent
431 return true;
432 });
Winson Chunga29eb982016-12-14 12:01:27 -0800433
Mady Mellor637cd482017-03-21 10:39:42 -0700434 if (mActions.isEmpty() || mMenuState == MENU_STATE_CLOSE) {
Winson Chunga5acf182017-01-05 16:02:27 -0800435 actionsContainer.setVisibility(View.INVISIBLE);
436 } else {
437 actionsContainer.setVisibility(View.VISIBLE);
Mady Mellora7f69742017-02-03 11:00:20 -0800438 if (mActionsGroup != null) {
Winson Chungb6de8722017-06-02 12:45:51 -0700439 // Ensure we have as many buttons as actions
Winson Chung7aab0c12017-04-18 12:54:25 -0700440 final LayoutInflater inflater = LayoutInflater.from(this);
441 while (mActionsGroup.getChildCount() < mActions.size()) {
Hongwei Wanga6e034e2019-06-20 13:18:43 -0700442 final ImageButton actionView = (ImageButton) inflater.inflate(
Winson Chung7aab0c12017-04-18 12:54:25 -0700443 R.layout.pip_menu_action, mActionsGroup, false);
444 mActionsGroup.addView(actionView);
445 }
Winson Chunga29eb982016-12-14 12:01:27 -0800446
Winson Chungb6de8722017-06-02 12:45:51 -0700447 // Update the visibility of all views
448 for (int i = 0; i < mActionsGroup.getChildCount(); i++) {
449 mActionsGroup.getChildAt(i).setVisibility(i < mActions.size()
450 ? View.VISIBLE
451 : View.GONE);
452 }
453
Winson Chunga5acf182017-01-05 16:02:27 -0800454 // Recreate the layout
Winson Chunge7a3d222017-03-09 13:26:45 -0800455 final boolean isLandscapePip = stackBounds != null &&
456 (stackBounds.width() > stackBounds.height());
Winson Chunga5acf182017-01-05 16:02:27 -0800457 for (int i = 0; i < mActions.size(); i++) {
458 final RemoteAction action = mActions.get(i);
Hongwei Wanga6e034e2019-06-20 13:18:43 -0700459 final ImageButton actionView = (ImageButton) mActionsGroup.getChildAt(i);
Winson Chung7aab0c12017-04-18 12:54:25 -0700460
461 // TODO: Check if the action drawable has changed before we reload it
Winson Chunga5acf182017-01-05 16:02:27 -0800462 action.getIcon().loadDrawableAsync(this, d -> {
Winson Chung56fe8832017-02-03 14:37:53 -0800463 d.setTint(Color.WHITE);
Winson Chunga5acf182017-01-05 16:02:27 -0800464 actionView.setImageDrawable(d);
465 }, mHandler);
466 actionView.setContentDescription(action.getContentDescription());
Winson Chung6b88baf2017-03-16 17:10:21 -0700467 if (action.isEnabled()) {
468 actionView.setOnClickListener(v -> {
Arthur Hungd52d1192019-08-30 16:06:59 +0800469 mHandler.post(() -> {
470 try {
471 action.getActionIntent().send();
472 } catch (CanceledException e) {
473 Log.w(TAG, "Failed to send action", e);
474 }
475 });
Winson Chung6b88baf2017-03-16 17:10:21 -0700476 });
Winson Chung6b88baf2017-03-16 17:10:21 -0700477 }
Winson Chungd61a19e2017-04-28 16:36:51 -0700478 actionView.setEnabled(action.isEnabled());
Winson Chungb6de8722017-06-02 12:45:51 -0700479 actionView.setAlpha(action.isEnabled() ? 1f : DISABLED_ACTION_ALPHA);
Winson Chung7aab0c12017-04-18 12:54:25 -0700480
481 // Update the margin between actions
482 LinearLayout.LayoutParams lp = (LinearLayout.LayoutParams)
483 actionView.getLayoutParams();
484 lp.leftMargin = (isLandscapePip && i > 0) ? mBetweenActionPaddingLand : 0;
Winson Chunga5acf182017-01-05 16:02:27 -0800485 }
Winson Chunga29eb982016-12-14 12:01:27 -0800486 }
Mady Mellora7f69742017-02-03 11:00:20 -0800487
Winson Chunge7a3d222017-03-09 13:26:45 -0800488 // Update the expand container margin to adjust the center of the expand button to
489 // account for the existence of the action container
490 FrameLayout.LayoutParams expandedLp =
491 (FrameLayout.LayoutParams) expandContainer.getLayoutParams();
492 expandedLp.topMargin = getResources().getDimensionPixelSize(
493 R.dimen.pip_action_padding);
494 expandedLp.bottomMargin = getResources().getDimensionPixelSize(
495 R.dimen.pip_expand_container_edge_margin);
496 expandContainer.requestLayout();
497 }
Winson Chunga29eb982016-12-14 12:01:27 -0800498 }
499
Mady Mellor81d40612017-03-10 15:14:10 -0800500 private void updateDismissFraction(float fraction) {
501 int alpha;
Mady Mellor637cd482017-03-21 10:39:42 -0700502 final float menuAlpha = 1 - fraction;
503 if (mMenuState == MENU_STATE_FULL) {
504 mMenuContainer.setAlpha(menuAlpha);
Winson Chunga556fe62017-12-19 14:46:23 -0800505 mSettingsButton.setAlpha(menuAlpha);
Mady Mellor637cd482017-03-21 10:39:42 -0700506 mDismissButton.setAlpha(menuAlpha);
Mady Mellor81d40612017-03-10 15:14:10 -0800507 final float interpolatedAlpha =
Mady Mellor637cd482017-03-21 10:39:42 -0700508 MENU_BACKGROUND_ALPHA * menuAlpha + DISMISS_BACKGROUND_ALPHA * fraction;
Winson Chungc3545d32017-04-03 10:44:57 -0700509 alpha = (int) (interpolatedAlpha * 255);
Mady Mellor81d40612017-03-10 15:14:10 -0800510 } else {
Mady Mellor637cd482017-03-21 10:39:42 -0700511 if (mMenuState == MENU_STATE_CLOSE) {
512 mDismissButton.setAlpha(menuAlpha);
513 }
Winson Chungc3545d32017-04-03 10:44:57 -0700514 alpha = (int) (fraction * DISMISS_BACKGROUND_ALPHA * 255);
Mady Mellor81d40612017-03-10 15:14:10 -0800515 }
516 mBackgroundDrawable.setAlpha(alpha);
517 }
518
Mady Mellor637cd482017-03-21 10:39:42 -0700519 private void notifyMenuStateChange(int menuState) {
520 mMenuState = menuState;
Winson Chung15504af2016-11-02 18:11:36 -0700521 Message m = Message.obtain();
Mady Mellor637cd482017-03-21 10:39:42 -0700522 m.what = PipMenuActivityController.MESSAGE_MENU_STATE_CHANGED;
523 m.arg1 = menuState;
Winson Chunga29eb982016-12-14 12:01:27 -0800524 sendMessage(m, "Could not notify controller of PIP menu visibility");
Winson Chung15504af2016-11-02 18:11:36 -0700525 }
526
Winson Chunga29eb982016-12-14 12:01:27 -0800527 private void expandPip() {
Winson Chungd2d90972017-02-28 11:40:41 -0800528 // Do not notify menu visibility when hiding the menu, the controller will do this when it
529 // handles the message
Winson Chungc75ffe82016-12-16 16:20:16 -0800530 hideMenu(() -> {
531 sendEmptyMessage(PipMenuActivityController.MESSAGE_EXPAND_PIP,
532 "Could not notify controller to expand PIP");
Winson Chung21004d52018-06-04 17:09:32 -0700533 }, false /* notifyMenuVisibility */, false /* isDismissing */);
Winson Chunga29eb982016-12-14 12:01:27 -0800534 }
535
Winson Chunga29eb982016-12-14 12:01:27 -0800536 private void dismissPip() {
Winson Chungd2d90972017-02-28 11:40:41 -0800537 // Do not notify menu visibility when hiding the menu, the controller will do this when it
538 // handles the message
Winson Chungc75ffe82016-12-16 16:20:16 -0800539 hideMenu(() -> {
540 sendEmptyMessage(PipMenuActivityController.MESSAGE_DISMISS_PIP,
541 "Could not notify controller to dismiss PIP");
Winson Chung21004d52018-06-04 17:09:32 -0700542 }, false /* notifyMenuVisibility */, true /* isDismissing */);
Winson Chungc75ffe82016-12-16 16:20:16 -0800543 }
544
Winson Chunga556fe62017-12-19 14:46:23 -0800545 private void showSettings() {
546 final Pair<ComponentName, Integer> topPipActivityInfo =
547 PipUtils.getTopPinnedActivity(this, ActivityManager.getService());
548 if (topPipActivityInfo.first != null) {
549 final UserHandle user = UserHandle.of(topPipActivityInfo.second);
550 final Intent settingsIntent = new Intent(ACTION_PICTURE_IN_PICTURE_SETTINGS,
551 Uri.fromParts("package", topPipActivityInfo.first.getPackageName(), null));
552 settingsIntent.putExtra(Intent.EXTRA_USER_HANDLE, user);
553 settingsIntent.setFlags(FLAG_ACTIVITY_NEW_TASK | FLAG_ACTIVITY_CLEAR_TASK);
554 startActivity(settingsIntent);
555 }
556 }
557
Winson Chungc75ffe82016-12-16 16:20:16 -0800558 private void notifyActivityCallback(Messenger callback) {
559 Message m = Message.obtain();
560 m.what = PipMenuActivityController.MESSAGE_UPDATE_ACTIVITY_CALLBACK;
561 m.replyTo = callback;
562 sendMessage(m, "Could not notify controller of activity finished");
Winson Chunga29eb982016-12-14 12:01:27 -0800563 }
564
565 private void sendEmptyMessage(int what, String errorMsg) {
Winson Chung15504af2016-11-02 18:11:36 -0700566 Message m = Message.obtain();
Winson Chunga29eb982016-12-14 12:01:27 -0800567 m.what = what;
568 sendMessage(m, errorMsg);
569 }
570
571 private void sendMessage(Message m, String errorMsg) {
Jaewan Kimc1d15242017-07-07 18:12:04 +0900572 if (mToControllerMessenger == null) {
573 return;
574 }
Winson Chung15504af2016-11-02 18:11:36 -0700575 try {
576 mToControllerMessenger.send(m);
577 } catch (RemoteException e) {
Winson Chunga29eb982016-12-14 12:01:27 -0800578 Log.e(TAG, errorMsg, e);
Winson Chung15504af2016-11-02 18:11:36 -0700579 }
580 }
581
Winson Chungc75ffe82016-12-16 16:20:16 -0800582 private void cancelDelayedFinish() {
Winson Chung4a526c12017-05-16 13:35:43 -0700583 mHandler.removeCallbacks(mFinishRunnable);
Winson Chungc75ffe82016-12-16 16:20:16 -0800584 }
585
Rhed Jao5e00c982018-11-27 19:41:05 +0800586 private void repostDelayedFinish(int delay) {
587 int recommendedTimeout = mAccessibilityManager.getRecommendedTimeoutMillis(delay,
588 FLAG_CONTENT_ICONS | FLAG_CONTENT_CONTROLS);
Winson Chung4a526c12017-05-16 13:35:43 -0700589 mHandler.removeCallbacks(mFinishRunnable);
Rhed Jao5e00c982018-11-27 19:41:05 +0800590 mHandler.postDelayed(mFinishRunnable, recommendedTimeout);
Winson Chung15504af2016-11-02 18:11:36 -0700591 }
Winson Chung15504af2016-11-02 18:11:36 -0700592}