blob: ec6d7ffaedb0fb3ce3ffb5679983f55ad4c63662 [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;
Winson Chunga29eb982016-12-14 12:01:27 -080070import android.widget.ImageView;
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 Hung0ede6e92019-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;
Winson Chunge7a3d222017-03-09 13:26:45 -0800117 private ImageView mExpandButton;
Mady Mellora7f69742017-02-03 11:00:20 -0800118 private int mBetweenActionPaddingLand;
Winson Chunga29eb982016-12-14 12:01:27 -0800119
Mady Mellor637cd482017-03-21 10:39:42 -0700120 private AnimatorSet mMenuContainerAnimator;
121
Mady Mellor81d40612017-03-10 15:14:10 -0800122 private ValueAnimator.AnimatorUpdateListener mMenuBgUpdateListener =
123 new ValueAnimator.AnimatorUpdateListener() {
124 @Override
125 public void onAnimationUpdate(ValueAnimator animation) {
126 final float alpha = (float) animation.getAnimatedValue();
127 mBackgroundDrawable.setAlpha((int) (MENU_BACKGROUND_ALPHA*alpha*255));
128 }
129 };
Winson Chungc75ffe82016-12-16 16:20:16 -0800130
Winson Chunga29eb982016-12-14 12:01:27 -0800131 private Handler mHandler = new Handler();
Winson Chung15504af2016-11-02 18:11:36 -0700132 private Messenger mToControllerMessenger;
133 private Messenger mMessenger = new Messenger(new Handler() {
134 @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 Hung0ede6e92019-08-30 16:06:59 +0800168
169 case MESSAGE_TOUCH_EVENT: {
170 final MotionEvent ev = (MotionEvent) msg.obj;
171 dispatchTouchEvent(ev);
172 break;
173 }
Winson Chung15504af2016-11-02 18:11:36 -0700174 }
175 }
176 });
177
178 private final Runnable mFinishRunnable = new Runnable() {
179 @Override
180 public void run() {
Winson Chungc75ffe82016-12-16 16:20:16 -0800181 hideMenu();
Winson Chung15504af2016-11-02 18:11:36 -0700182 }
183 };
184
185 @Override
186 protected void onCreate(@Nullable Bundle savedInstanceState) {
Winson Chungc75ffe82016-12-16 16:20:16 -0800187 // Set the flags to allow us to watch for outside touches and also hide the menu and start
188 // manipulating the PIP in the same touch gesture
Arthur Hung0ede6e92019-08-30 16:06:59 +0800189 getWindow().addFlags(LayoutParams.FLAG_WATCH_OUTSIDE_TOUCH);
Winson Chungc75ffe82016-12-16 16:20:16 -0800190
Winson Chung15504af2016-11-02 18:11:36 -0700191 super.onCreate(savedInstanceState);
Winson Chung15504af2016-11-02 18:11:36 -0700192 setContentView(R.layout.pip_menu_activity);
Winson Chunga29eb982016-12-14 12:01:27 -0800193
Rhed Jao5e00c982018-11-27 19:41:05 +0800194 mAccessibilityManager = getSystemService(AccessibilityManager.class);
Mady Mellor81d40612017-03-10 15:14:10 -0800195 mBackgroundDrawable = new ColorDrawable(Color.BLACK);
196 mBackgroundDrawable.setAlpha(0);
197 mViewRoot = findViewById(R.id.background);
198 mViewRoot.setBackground(mBackgroundDrawable);
199 mMenuContainer = findViewById(R.id.menu_container);
200 mMenuContainer.setAlpha(0);
Winson Chunga556fe62017-12-19 14:46:23 -0800201 mSettingsButton = findViewById(R.id.settings);
202 mSettingsButton.setAlpha(0);
203 mSettingsButton.setOnClickListener((v) -> {
Tracy Zhou2c6117a2018-06-21 11:32:43 -0700204 if (v.getAlpha() != 0) {
205 showSettings();
206 }
Winson Chunga556fe62017-12-19 14:46:23 -0800207 });
Winson Chunga29eb982016-12-14 12:01:27 -0800208 mDismissButton = findViewById(R.id.dismiss);
Mady Mellor637cd482017-03-21 10:39:42 -0700209 mDismissButton.setAlpha(0);
Arthur Hung0ede6e92019-08-30 16:06:59 +0800210 mDismissButton.setOnClickListener(v -> dismissPip());
211 findViewById(R.id.expand_button).setOnClickListener(v -> {
212 if (mMenuContainer.getAlpha() != 0) {
213 expandPip();
214 }
Winson Chunga29eb982016-12-14 12:01:27 -0800215 });
Alan Viverette51efddb2017-04-05 10:00:01 -0400216 mActionsGroup = findViewById(R.id.actions_group);
Mady Mellora7f69742017-02-03 11:00:20 -0800217 mBetweenActionPaddingLand = getResources().getDimensionPixelSize(
218 R.dimen.pip_between_action_padding_land);
Alan Viverette51efddb2017-04-05 10:00:01 -0400219 mExpandButton = findViewById(R.id.expand_button);
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);
Winson Chungc75ffe82016-12-16 16:20:16 -0800224 }
225
226 @Override
227 protected void onNewIntent(Intent intent) {
228 super.onNewIntent(intent);
Winson Chunge7a3d222017-03-09 13:26:45 -0800229 updateFromIntent(intent);
Winson Chung15504af2016-11-02 18:11:36 -0700230 }
231
232 @Override
Winson Chungd2d90972017-02-28 11:40:41 -0800233 public void onUserInteraction() {
Winson Chung379b9ab2017-04-04 11:57:36 -0700234 if (mAllowMenuTimeout) {
235 repostDelayedFinish(POST_INTERACTION_DISMISS_DELAY);
236 }
Winson Chungd2d90972017-02-28 11:40:41 -0800237 }
238
239 @Override
Winson Chung61ecc1b2017-02-17 10:46:17 -0800240 protected void onUserLeaveHint() {
241 super.onUserLeaveHint();
242
Winson Chung9f69b782017-05-16 15:27:49 -0700243 // 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 -0800244 // recreated on the top next time it starts
Winson Chung9f69b782017-05-16 15:27:49 -0700245 hideMenu();
Winson Chung61ecc1b2017-02-17 10:46:17 -0800246 }
247
248 @Override
Winson Chungef4dc812017-04-11 13:31:44 -0700249 protected void onStop() {
250 super.onStop();
251
252 cancelDelayedFinish();
253 }
254
255 @Override
Winson Chung61ecc1b2017-02-17 10:46:17 -0800256 protected void onDestroy() {
257 super.onDestroy();
258
259 // Fallback, if we are destroyed for any other reason (like when the task is being reset),
260 // also reset the callback.
261 notifyActivityCallback(null);
262 }
263
264 @Override
Winson Chungc75ffe82016-12-16 16:20:16 -0800265 public void onPictureInPictureModeChanged(boolean isInPictureInPictureMode) {
266 if (!isInPictureInPictureMode) {
267 finish();
268 }
269 }
270
271 @Override
Winson Chungc75ffe82016-12-16 16:20:16 -0800272 public boolean dispatchTouchEvent(MotionEvent ev) {
Winson Chung34488242017-04-26 15:53:51 -0700273 if (!mAllowTouches) {
Arthur Hung0ede6e92019-08-30 16:06:59 +0800274 return false;
Winson Chung34488242017-04-26 15:53:51 -0700275 }
276
Winson Chungc75ffe82016-12-16 16:20:16 -0800277 // On the first action outside the window, hide the menu
278 switch (ev.getAction()) {
279 case MotionEvent.ACTION_OUTSIDE:
280 hideMenu();
Arthur Hung0ede6e92019-08-30 16:06:59 +0800281 return true;
Winson Chungc75ffe82016-12-16 16:20:16 -0800282 }
283 return super.dispatchTouchEvent(ev);
Winson Chung15504af2016-11-02 18:11:36 -0700284 }
285
286 @Override
287 public void finish() {
Winson Chungc75ffe82016-12-16 16:20:16 -0800288 notifyActivityCallback(null);
Winson Chung15504af2016-11-02 18:11:36 -0700289 super.finish();
Winson Chungc75ffe82016-12-16 16:20:16 -0800290 // Hide without an animation (the menu should already be invisible at this point)
291 overridePendingTransition(0, 0);
Winson Chung15504af2016-11-02 18:11:36 -0700292 }
293
294 @Override
295 public void setTaskDescription(ActivityManager.TaskDescription taskDescription) {
296 // Do nothing
297 }
298
Mady Mellor637cd482017-03-21 10:39:42 -0700299 private void showMenu(int menuState, Rect stackBounds, Rect movementBounds,
Winson Chungbb787442017-09-01 11:33:47 -0700300 boolean allowMenuTimeout, boolean resizeMenuOnShow) {
Winson Chung379b9ab2017-04-04 11:57:36 -0700301 mAllowMenuTimeout = allowMenuTimeout;
Mady Mellor637cd482017-03-21 10:39:42 -0700302 if (mMenuState != menuState) {
Winson Chungbb787442017-09-01 11:33:47 -0700303 // Disallow touches if the menu needs to resize while showing, and we are transitioning
304 // to/from a full menu state.
305 boolean disallowTouchesUntilAnimationEnd = resizeMenuOnShow &&
306 (mMenuState == MENU_STATE_FULL || menuState == MENU_STATE_FULL);
307 mAllowTouches = !disallowTouchesUntilAnimationEnd;
Mady Mellor637cd482017-03-21 10:39:42 -0700308 cancelDelayedFinish();
Mady Mellor81d40612017-03-10 15:14:10 -0800309 updateActionViews(stackBounds);
Winson Chungc75ffe82016-12-16 16:20:16 -0800310 if (mMenuContainerAnimator != null) {
311 mMenuContainerAnimator.cancel();
312 }
Mady Mellor637cd482017-03-21 10:39:42 -0700313 notifyMenuStateChange(menuState);
Mady Mellor637cd482017-03-21 10:39:42 -0700314 mMenuContainerAnimator = new AnimatorSet();
315 ObjectAnimator menuAnim = ObjectAnimator.ofFloat(mMenuContainer, View.ALPHA,
Winson Chungc75ffe82016-12-16 16:20:16 -0800316 mMenuContainer.getAlpha(), 1f);
Mady Mellor637cd482017-03-21 10:39:42 -0700317 menuAnim.addUpdateListener(mMenuBgUpdateListener);
Winson Chunga556fe62017-12-19 14:46:23 -0800318 ObjectAnimator settingsAnim = ObjectAnimator.ofFloat(mSettingsButton, View.ALPHA,
319 mSettingsButton.getAlpha(), 1f);
Mady Mellor637cd482017-03-21 10:39:42 -0700320 ObjectAnimator dismissAnim = ObjectAnimator.ofFloat(mDismissButton, View.ALPHA,
321 mDismissButton.getAlpha(), 1f);
322 if (menuState == MENU_STATE_FULL) {
Winson Chunga556fe62017-12-19 14:46:23 -0800323 mMenuContainerAnimator.playTogether(menuAnim, settingsAnim, dismissAnim);
Mady Mellor637cd482017-03-21 10:39:42 -0700324 } else {
Winson Chung2a35e6d2018-01-13 14:27:50 -0800325 mMenuContainerAnimator.playTogether(dismissAnim);
Mady Mellor637cd482017-03-21 10:39:42 -0700326 }
Winson Chungc75ffe82016-12-16 16:20:16 -0800327 mMenuContainerAnimator.setInterpolator(Interpolators.ALPHA_IN);
328 mMenuContainerAnimator.setDuration(MENU_FADE_DURATION);
Winson Chung0f873de2017-03-30 10:26:48 -0700329 if (allowMenuTimeout) {
330 mMenuContainerAnimator.addListener(new AnimatorListenerAdapter() {
331 @Override
332 public void onAnimationEnd(Animator animation) {
333 repostDelayedFinish(INITIAL_DISMISS_DELAY);
334 }
335 });
336 }
Winson Chungc75ffe82016-12-16 16:20:16 -0800337 mMenuContainerAnimator.start();
Winson Chungd2d90972017-02-28 11:40:41 -0800338 } else {
Winson Chung2824d7c2017-03-15 19:43:00 -0700339 // If we are already visible, then just start the delayed dismiss and unregister any
340 // existing input consumers from the previous drag
Winson Chung0f873de2017-03-30 10:26:48 -0700341 if (allowMenuTimeout) {
342 repostDelayedFinish(POST_INTERACTION_DISMISS_DELAY);
343 }
Winson Chungc75ffe82016-12-16 16:20:16 -0800344 }
345 }
346
347 private void hideMenu() {
Winson Chung2dbcf092018-10-24 13:00:41 -0700348 hideMenu(null);
349 }
350
351 private void hideMenu(Runnable animationEndCallback) {
352 hideMenu(animationEndCallback, true /* notifyMenuVisibility */, false /* isDismissing */);
Winson Chungc75ffe82016-12-16 16:20:16 -0800353 }
354
Winson Chung21004d52018-06-04 17:09:32 -0700355 private void hideMenu(final Runnable animationFinishedRunnable, boolean notifyMenuVisibility,
356 boolean isDismissing) {
Mady Mellor637cd482017-03-21 10:39:42 -0700357 if (mMenuState != MENU_STATE_NONE) {
Winson Chungc75ffe82016-12-16 16:20:16 -0800358 cancelDelayedFinish();
Winson Chungd2d90972017-02-28 11:40:41 -0800359 if (notifyMenuVisibility) {
Mady Mellor637cd482017-03-21 10:39:42 -0700360 notifyMenuStateChange(MENU_STATE_NONE);
Winson Chungd2d90972017-02-28 11:40:41 -0800361 }
Mady Mellor637cd482017-03-21 10:39:42 -0700362 mMenuContainerAnimator = new AnimatorSet();
363 ObjectAnimator menuAnim = ObjectAnimator.ofFloat(mMenuContainer, View.ALPHA,
Winson Chungc75ffe82016-12-16 16:20:16 -0800364 mMenuContainer.getAlpha(), 0f);
Mady Mellor637cd482017-03-21 10:39:42 -0700365 menuAnim.addUpdateListener(mMenuBgUpdateListener);
Winson Chunga556fe62017-12-19 14:46:23 -0800366 ObjectAnimator settingsAnim = ObjectAnimator.ofFloat(mSettingsButton, View.ALPHA,
367 mSettingsButton.getAlpha(), 0f);
Mady Mellor637cd482017-03-21 10:39:42 -0700368 ObjectAnimator dismissAnim = ObjectAnimator.ofFloat(mDismissButton, View.ALPHA,
369 mDismissButton.getAlpha(), 0f);
Winson Chunga556fe62017-12-19 14:46:23 -0800370 mMenuContainerAnimator.playTogether(menuAnim, settingsAnim, dismissAnim);
Winson Chungc75ffe82016-12-16 16:20:16 -0800371 mMenuContainerAnimator.setInterpolator(Interpolators.ALPHA_OUT);
372 mMenuContainerAnimator.setDuration(MENU_FADE_DURATION);
373 mMenuContainerAnimator.addListener(new AnimatorListenerAdapter() {
374 @Override
375 public void onAnimationEnd(Animator animation) {
376 if (animationFinishedRunnable != null) {
377 animationFinishedRunnable.run();
378 }
Winson Chung21004d52018-06-04 17:09:32 -0700379
380 if (!isDismissing) {
381 // If we are dismissing the PiP, then don't try to pre-emptively finish the
382 // menu activity
383 finish();
384 }
Winson Chungc75ffe82016-12-16 16:20:16 -0800385 }
386 });
387 mMenuContainerAnimator.start();
Winson Chung87e5d552017-04-05 11:49:38 -0700388 } else {
389 // If the menu is not visible, just finish now
390 finish();
Winson Chungc75ffe82016-12-16 16:20:16 -0800391 }
392 }
393
Winson Chunge7a3d222017-03-09 13:26:45 -0800394 private void updateFromIntent(Intent intent) {
Winson Chunge7a3d222017-03-09 13:26:45 -0800395 mToControllerMessenger = intent.getParcelableExtra(EXTRA_CONTROLLER_MESSENGER);
Jaewan Kimc1d15242017-07-07 18:12:04 +0900396 if (mToControllerMessenger == null) {
397 Log.w(TAG, "Controller messenger is null. Stopping.");
398 finish();
399 return;
400 }
Winson Chungccbc4b92017-03-20 14:01:36 -0700401 notifyActivityCallback(mMessenger);
Winson Chungb5026902017-05-03 12:45:13 -0700402
Winson Chunge7a3d222017-03-09 13:26:45 -0800403 ParceledListSlice actions = intent.getParcelableExtra(EXTRA_ACTIONS);
404 if (actions != null) {
Mady Mellor81d40612017-03-10 15:14:10 -0800405 mActions.clear();
406 mActions.addAll(actions.getList());
Winson Chunge7a3d222017-03-09 13:26:45 -0800407 }
Winson Chungb5026902017-05-03 12:45:13 -0700408
Mady Mellor637cd482017-03-21 10:39:42 -0700409 final int menuState = intent.getIntExtra(EXTRA_MENU_STATE, MENU_STATE_NONE);
410 if (menuState != MENU_STATE_NONE) {
Winson Chung853c99a2017-03-21 22:16:42 -0700411 Rect stackBounds = intent.getParcelableExtra(EXTRA_STACK_BOUNDS);
412 Rect movementBounds = intent.getParcelableExtra(EXTRA_MOVEMENT_BOUNDS);
Winson Chung0f873de2017-03-30 10:26:48 -0700413 boolean allowMenuTimeout = intent.getBooleanExtra(EXTRA_ALLOW_TIMEOUT, true);
Winson Chungbb787442017-09-01 11:33:47 -0700414 boolean willResizeMenu = intent.getBooleanExtra(EXTRA_WILL_RESIZE_MENU, false);
415 showMenu(menuState, stackBounds, movementBounds, allowMenuTimeout, willResizeMenu);
Mady Mellor81d40612017-03-10 15:14:10 -0800416 }
Winson Chunga29eb982016-12-14 12:01:27 -0800417 }
418
Winson Chunge7a3d222017-03-09 13:26:45 -0800419 private void setActions(Rect stackBounds, List<RemoteAction> actions) {
420 mActions.clear();
421 mActions.addAll(actions);
422 updateActionViews(stackBounds);
423 }
424
425 private void updateActionViews(Rect stackBounds) {
Alan Viverette51efddb2017-04-05 10:00:01 -0400426 ViewGroup expandContainer = findViewById(R.id.expand_container);
427 ViewGroup actionsContainer = findViewById(R.id.actions_container);
Winson Chunga5acf182017-01-05 16:02:27 -0800428 actionsContainer.setOnTouchListener((v, ev) -> {
429 // Do nothing, prevent click through to parent
430 return true;
431 });
Winson Chunga29eb982016-12-14 12:01:27 -0800432
Mady Mellor637cd482017-03-21 10:39:42 -0700433 if (mActions.isEmpty() || mMenuState == MENU_STATE_CLOSE) {
Winson Chunga5acf182017-01-05 16:02:27 -0800434 actionsContainer.setVisibility(View.INVISIBLE);
435 } else {
436 actionsContainer.setVisibility(View.VISIBLE);
Mady Mellora7f69742017-02-03 11:00:20 -0800437 if (mActionsGroup != null) {
Winson Chungb6de8722017-06-02 12:45:51 -0700438 // Ensure we have as many buttons as actions
Winson Chung7aab0c12017-04-18 12:54:25 -0700439 final LayoutInflater inflater = LayoutInflater.from(this);
440 while (mActionsGroup.getChildCount() < mActions.size()) {
441 final ImageView actionView = (ImageView) inflater.inflate(
442 R.layout.pip_menu_action, mActionsGroup, false);
443 mActionsGroup.addView(actionView);
444 }
Winson Chunga29eb982016-12-14 12:01:27 -0800445
Winson Chungb6de8722017-06-02 12:45:51 -0700446 // Update the visibility of all views
447 for (int i = 0; i < mActionsGroup.getChildCount(); i++) {
448 mActionsGroup.getChildAt(i).setVisibility(i < mActions.size()
449 ? View.VISIBLE
450 : View.GONE);
451 }
452
Winson Chunga5acf182017-01-05 16:02:27 -0800453 // Recreate the layout
Winson Chunge7a3d222017-03-09 13:26:45 -0800454 final boolean isLandscapePip = stackBounds != null &&
455 (stackBounds.width() > stackBounds.height());
Winson Chunga5acf182017-01-05 16:02:27 -0800456 for (int i = 0; i < mActions.size(); i++) {
457 final RemoteAction action = mActions.get(i);
Winson Chung7aab0c12017-04-18 12:54:25 -0700458 final ImageView actionView = (ImageView) mActionsGroup.getChildAt(i);
459
460 // TODO: Check if the action drawable has changed before we reload it
Winson Chunga5acf182017-01-05 16:02:27 -0800461 action.getIcon().loadDrawableAsync(this, d -> {
Winson Chung56fe8832017-02-03 14:37:53 -0800462 d.setTint(Color.WHITE);
Winson Chunga5acf182017-01-05 16:02:27 -0800463 actionView.setImageDrawable(d);
464 }, mHandler);
465 actionView.setContentDescription(action.getContentDescription());
Winson Chung6b88baf2017-03-16 17:10:21 -0700466 if (action.isEnabled()) {
467 actionView.setOnClickListener(v -> {
Arthur Hung0ede6e92019-08-30 16:06:59 +0800468 mHandler.post(() -> {
469 try {
470 action.getActionIntent().send();
471 } catch (CanceledException e) {
472 Log.w(TAG, "Failed to send action", e);
473 }
474 });
Winson Chung6b88baf2017-03-16 17:10:21 -0700475 });
Winson Chung6b88baf2017-03-16 17:10:21 -0700476 }
Winson Chungd61a19e2017-04-28 16:36:51 -0700477 actionView.setEnabled(action.isEnabled());
Winson Chungb6de8722017-06-02 12:45:51 -0700478 actionView.setAlpha(action.isEnabled() ? 1f : DISABLED_ACTION_ALPHA);
Winson Chung7aab0c12017-04-18 12:54:25 -0700479
480 // Update the margin between actions
481 LinearLayout.LayoutParams lp = (LinearLayout.LayoutParams)
482 actionView.getLayoutParams();
483 lp.leftMargin = (isLandscapePip && i > 0) ? mBetweenActionPaddingLand : 0;
Winson Chunga5acf182017-01-05 16:02:27 -0800484 }
Winson Chunga29eb982016-12-14 12:01:27 -0800485 }
Mady Mellora7f69742017-02-03 11:00:20 -0800486
Winson Chunge7a3d222017-03-09 13:26:45 -0800487 // Update the expand container margin to adjust the center of the expand button to
488 // account for the existence of the action container
489 FrameLayout.LayoutParams expandedLp =
490 (FrameLayout.LayoutParams) expandContainer.getLayoutParams();
491 expandedLp.topMargin = getResources().getDimensionPixelSize(
492 R.dimen.pip_action_padding);
493 expandedLp.bottomMargin = getResources().getDimensionPixelSize(
494 R.dimen.pip_expand_container_edge_margin);
495 expandContainer.requestLayout();
496 }
Winson Chunga29eb982016-12-14 12:01:27 -0800497 }
498
Mady Mellor81d40612017-03-10 15:14:10 -0800499 private void updateDismissFraction(float fraction) {
500 int alpha;
Mady Mellor637cd482017-03-21 10:39:42 -0700501 final float menuAlpha = 1 - fraction;
502 if (mMenuState == MENU_STATE_FULL) {
503 mMenuContainer.setAlpha(menuAlpha);
Winson Chunga556fe62017-12-19 14:46:23 -0800504 mSettingsButton.setAlpha(menuAlpha);
Mady Mellor637cd482017-03-21 10:39:42 -0700505 mDismissButton.setAlpha(menuAlpha);
Mady Mellor81d40612017-03-10 15:14:10 -0800506 final float interpolatedAlpha =
Mady Mellor637cd482017-03-21 10:39:42 -0700507 MENU_BACKGROUND_ALPHA * menuAlpha + DISMISS_BACKGROUND_ALPHA * fraction;
Winson Chungc3545d32017-04-03 10:44:57 -0700508 alpha = (int) (interpolatedAlpha * 255);
Mady Mellor81d40612017-03-10 15:14:10 -0800509 } else {
Mady Mellor637cd482017-03-21 10:39:42 -0700510 if (mMenuState == MENU_STATE_CLOSE) {
511 mDismissButton.setAlpha(menuAlpha);
512 }
Winson Chungc3545d32017-04-03 10:44:57 -0700513 alpha = (int) (fraction * DISMISS_BACKGROUND_ALPHA * 255);
Mady Mellor81d40612017-03-10 15:14:10 -0800514 }
515 mBackgroundDrawable.setAlpha(alpha);
516 }
517
Mady Mellor637cd482017-03-21 10:39:42 -0700518 private void notifyMenuStateChange(int menuState) {
519 mMenuState = menuState;
Winson Chung15504af2016-11-02 18:11:36 -0700520 Message m = Message.obtain();
Mady Mellor637cd482017-03-21 10:39:42 -0700521 m.what = PipMenuActivityController.MESSAGE_MENU_STATE_CHANGED;
522 m.arg1 = menuState;
Winson Chunga29eb982016-12-14 12:01:27 -0800523 sendMessage(m, "Could not notify controller of PIP menu visibility");
Winson Chung15504af2016-11-02 18:11:36 -0700524 }
525
Winson Chunga29eb982016-12-14 12:01:27 -0800526 private void expandPip() {
Winson Chungd2d90972017-02-28 11:40:41 -0800527 // Do not notify menu visibility when hiding the menu, the controller will do this when it
528 // handles the message
Winson Chungc75ffe82016-12-16 16:20:16 -0800529 hideMenu(() -> {
530 sendEmptyMessage(PipMenuActivityController.MESSAGE_EXPAND_PIP,
531 "Could not notify controller to expand PIP");
Winson Chung21004d52018-06-04 17:09:32 -0700532 }, false /* notifyMenuVisibility */, false /* isDismissing */);
Winson Chunga29eb982016-12-14 12:01:27 -0800533 }
534
Winson Chunga29eb982016-12-14 12:01:27 -0800535 private void dismissPip() {
Winson Chungd2d90972017-02-28 11:40:41 -0800536 // Do not notify menu visibility when hiding the menu, the controller will do this when it
537 // handles the message
Winson Chungc75ffe82016-12-16 16:20:16 -0800538 hideMenu(() -> {
539 sendEmptyMessage(PipMenuActivityController.MESSAGE_DISMISS_PIP,
540 "Could not notify controller to dismiss PIP");
Winson Chung21004d52018-06-04 17:09:32 -0700541 }, false /* notifyMenuVisibility */, true /* isDismissing */);
Winson Chungc75ffe82016-12-16 16:20:16 -0800542 }
543
Winson Chunga556fe62017-12-19 14:46:23 -0800544 private void showSettings() {
545 final Pair<ComponentName, Integer> topPipActivityInfo =
546 PipUtils.getTopPinnedActivity(this, ActivityManager.getService());
547 if (topPipActivityInfo.first != null) {
548 final UserHandle user = UserHandle.of(topPipActivityInfo.second);
549 final Intent settingsIntent = new Intent(ACTION_PICTURE_IN_PICTURE_SETTINGS,
550 Uri.fromParts("package", topPipActivityInfo.first.getPackageName(), null));
551 settingsIntent.putExtra(Intent.EXTRA_USER_HANDLE, user);
552 settingsIntent.setFlags(FLAG_ACTIVITY_NEW_TASK | FLAG_ACTIVITY_CLEAR_TASK);
553 startActivity(settingsIntent);
554 }
555 }
556
Winson Chungc75ffe82016-12-16 16:20:16 -0800557 private void notifyActivityCallback(Messenger callback) {
558 Message m = Message.obtain();
559 m.what = PipMenuActivityController.MESSAGE_UPDATE_ACTIVITY_CALLBACK;
560 m.replyTo = callback;
561 sendMessage(m, "Could not notify controller of activity finished");
Winson Chunga29eb982016-12-14 12:01:27 -0800562 }
563
564 private void sendEmptyMessage(int what, String errorMsg) {
Winson Chung15504af2016-11-02 18:11:36 -0700565 Message m = Message.obtain();
Winson Chunga29eb982016-12-14 12:01:27 -0800566 m.what = what;
567 sendMessage(m, errorMsg);
568 }
569
570 private void sendMessage(Message m, String errorMsg) {
Jaewan Kimc1d15242017-07-07 18:12:04 +0900571 if (mToControllerMessenger == null) {
572 return;
573 }
Winson Chung15504af2016-11-02 18:11:36 -0700574 try {
575 mToControllerMessenger.send(m);
576 } catch (RemoteException e) {
Winson Chunga29eb982016-12-14 12:01:27 -0800577 Log.e(TAG, errorMsg, e);
Winson Chung15504af2016-11-02 18:11:36 -0700578 }
579 }
580
Winson Chungc75ffe82016-12-16 16:20:16 -0800581 private void cancelDelayedFinish() {
Winson Chung4a526c12017-05-16 13:35:43 -0700582 mHandler.removeCallbacks(mFinishRunnable);
Winson Chungc75ffe82016-12-16 16:20:16 -0800583 }
584
Rhed Jao5e00c982018-11-27 19:41:05 +0800585 private void repostDelayedFinish(int delay) {
586 int recommendedTimeout = mAccessibilityManager.getRecommendedTimeoutMillis(delay,
587 FLAG_CONTENT_ICONS | FLAG_CONTENT_CONTROLS);
Winson Chung4a526c12017-05-16 13:35:43 -0700588 mHandler.removeCallbacks(mFinishRunnable);
Rhed Jao5e00c982018-11-27 19:41:05 +0800589 mHandler.postDelayed(mFinishRunnable, recommendedTimeout);
Winson Chung15504af2016-11-02 18:11:36 -0700590 }
Winson Chung15504af2016-11-02 18:11:36 -0700591}