blob: 86ce60d1811426d04be30d60c7155d985cd71973 [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 Chungc75ffe82016-12-16 16:20:16 -080051import android.graphics.PointF;
Winson Chunge7a3d222017-03-09 13:26:45 -080052import android.graphics.Rect;
Mady Mellor81d40612017-03-10 15:14:10 -080053import android.graphics.drawable.ColorDrawable;
54import android.graphics.drawable.Drawable;
Winson Chunga556fe62017-12-19 14:46:23 -080055import android.net.Uri;
Winson Chung15504af2016-11-02 18:11:36 -070056import android.os.Bundle;
57import android.os.Handler;
58import 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;
Winson Chunga29eb982016-12-14 12:01:27 -080064import android.view.LayoutInflater;
Winson Chungc75ffe82016-12-16 16:20:16 -080065import android.view.MotionEvent;
Winson Chung15504af2016-11-02 18:11:36 -070066import android.view.View;
Winson Chungc75ffe82016-12-16 16:20:16 -080067import android.view.ViewConfiguration;
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;
Winson Chunga29eb982016-12-14 12:01:27 -080072import android.widget.ImageView;
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;
Winson Chung15504af2016-11-02 18:11:36 -070095
Rhed Jao5e00c982018-11-27 19:41:05 +080096 private static final int INITIAL_DISMISS_DELAY = 3500;
97 private static final int POST_INTERACTION_DISMISS_DELAY = 2000;
Winson Chungc75ffe82016-12-16 16:20:16 -080098 private static final long MENU_FADE_DURATION = 125;
Winson Chung15504af2016-11-02 18:11:36 -070099
Mady Mellor81d40612017-03-10 15:14:10 -0800100 private static final float MENU_BACKGROUND_ALPHA = 0.3f;
Mady Mellor60421c92017-03-29 15:27:37 -0700101 private static final float DISMISS_BACKGROUND_ALPHA = 0.6f;
Mady Mellor81d40612017-03-10 15:14:10 -0800102
Winson Chung6b88baf2017-03-16 17:10:21 -0700103 private static final float DISABLED_ACTION_ALPHA = 0.54f;
104
Mady Mellor637cd482017-03-21 10:39:42 -0700105 private int mMenuState;
Winson Chung379b9ab2017-04-04 11:57:36 -0700106 private boolean mAllowMenuTimeout = true;
Winson Chung34488242017-04-26 15:53:51 -0700107 private boolean mAllowTouches = true;
Winson Chung87e5d552017-04-05 11:49:38 -0700108
Winson Chungc75ffe82016-12-16 16:20:16 -0800109 private final List<RemoteAction> mActions = new ArrayList<>();
Winson Chung87e5d552017-04-05 11:49:38 -0700110
Rhed Jao5e00c982018-11-27 19:41:05 +0800111 private AccessibilityManager mAccessibilityManager;
Mady Mellor81d40612017-03-10 15:14:10 -0800112 private View mViewRoot;
113 private Drawable mBackgroundDrawable;
Winson Chungc75ffe82016-12-16 16:20:16 -0800114 private View mMenuContainer;
Mady Mellora7f69742017-02-03 11:00:20 -0800115 private LinearLayout mActionsGroup;
Winson Chunga556fe62017-12-19 14:46:23 -0800116 private View mSettingsButton;
Winson Chunga29eb982016-12-14 12:01:27 -0800117 private View mDismissButton;
Winson Chunge7a3d222017-03-09 13:26:45 -0800118 private ImageView mExpandButton;
Mady Mellora7f69742017-02-03 11:00:20 -0800119 private int mBetweenActionPaddingLand;
Winson Chunga29eb982016-12-14 12:01:27 -0800120
Mady Mellor637cd482017-03-21 10:39:42 -0700121 private AnimatorSet mMenuContainerAnimator;
122
Mady Mellor81d40612017-03-10 15:14:10 -0800123 private ValueAnimator.AnimatorUpdateListener mMenuBgUpdateListener =
124 new ValueAnimator.AnimatorUpdateListener() {
125 @Override
126 public void onAnimationUpdate(ValueAnimator animation) {
127 final float alpha = (float) animation.getAnimatedValue();
128 mBackgroundDrawable.setAlpha((int) (MENU_BACKGROUND_ALPHA*alpha*255));
129 }
130 };
Winson Chungc75ffe82016-12-16 16:20:16 -0800131
Winson Chungbca03112017-08-16 10:38:15 -0700132 private PipTouchState mTouchState;
Winson Chungc75ffe82016-12-16 16:20:16 -0800133 private PointF mDownPosition = new PointF();
134 private PointF mDownDelta = new PointF();
135 private ViewConfiguration mViewConfig;
Winson Chunga29eb982016-12-14 12:01:27 -0800136 private Handler mHandler = new Handler();
Winson Chung15504af2016-11-02 18:11:36 -0700137 private Messenger mToControllerMessenger;
138 private Messenger mMessenger = new Messenger(new Handler() {
139 @Override
140 public void handleMessage(Message msg) {
141 switch (msg.what) {
Winson Chung853c99a2017-03-21 22:16:42 -0700142 case MESSAGE_SHOW_MENU: {
143 final Bundle data = (Bundle) msg.obj;
Mady Mellor637cd482017-03-21 10:39:42 -0700144 showMenu(data.getInt(EXTRA_MENU_STATE),
145 data.getParcelable(EXTRA_STACK_BOUNDS),
Winson Chung0f873de2017-03-30 10:26:48 -0700146 data.getParcelable(EXTRA_MOVEMENT_BOUNDS),
Winson Chungbb787442017-09-01 11:33:47 -0700147 data.getBoolean(EXTRA_ALLOW_TIMEOUT),
148 data.getBoolean(EXTRA_WILL_RESIZE_MENU));
Winson Chungc75ffe82016-12-16 16:20:16 -0800149 break;
Winson Chung853c99a2017-03-21 22:16:42 -0700150 }
Mady Mellora7f69742017-02-03 11:00:20 -0800151 case MESSAGE_POKE_MENU:
152 cancelDelayedFinish();
153 break;
Winson Chungc75ffe82016-12-16 16:20:16 -0800154 case MESSAGE_HIDE_MENU:
Winson Chung2dbcf092018-10-24 13:00:41 -0700155 hideMenu((Runnable) msg.obj);
Winson Chungc75ffe82016-12-16 16:20:16 -0800156 break;
Winson Chung853c99a2017-03-21 22:16:42 -0700157 case MESSAGE_UPDATE_ACTIONS: {
158 final Bundle data = (Bundle) msg.obj;
Winson Chung26db3972017-03-23 15:17:01 -0700159 final ParceledListSlice actions = data.getParcelable(EXTRA_ACTIONS);
160 setActions(data.getParcelable(EXTRA_STACK_BOUNDS), actions != null
161 ? actions.getList() : Collections.EMPTY_LIST);
Winson Chunga29eb982016-12-14 12:01:27 -0800162 break;
Winson Chung853c99a2017-03-21 22:16:42 -0700163 }
164 case MESSAGE_UPDATE_DISMISS_FRACTION: {
165 final Bundle data = (Bundle) msg.obj;
166 updateDismissFraction(data.getFloat(EXTRA_DISMISS_FRACTION));
Mady Mellor81d40612017-03-10 15:14:10 -0800167 break;
Winson Chung853c99a2017-03-21 22:16:42 -0700168 }
Winson Chung34488242017-04-26 15:53:51 -0700169 case MESSAGE_ANIMATION_ENDED: {
170 mAllowTouches = true;
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
188 mViewConfig = ViewConfiguration.get(this);
Winson Chungbca03112017-08-16 10:38:15 -0700189 mTouchState = new PipTouchState(mViewConfig, mHandler, () -> {
190 if (mMenuState == MENU_STATE_CLOSE) {
191 showPipMenu();
192 } else {
193 expandPip();
194 }
195 });
Winson Chungc75ffe82016-12-16 16:20:16 -0800196 getWindow().addFlags(LayoutParams.FLAG_WATCH_OUTSIDE_TOUCH | LayoutParams.FLAG_SLIPPERY);
197
Winson Chung15504af2016-11-02 18:11:36 -0700198 super.onCreate(savedInstanceState);
Winson Chung15504af2016-11-02 18:11:36 -0700199 setContentView(R.layout.pip_menu_activity);
Winson Chunga29eb982016-12-14 12:01:27 -0800200
Rhed Jao5e00c982018-11-27 19:41:05 +0800201 mAccessibilityManager = getSystemService(AccessibilityManager.class);
Mady Mellor81d40612017-03-10 15:14:10 -0800202 mBackgroundDrawable = new ColorDrawable(Color.BLACK);
203 mBackgroundDrawable.setAlpha(0);
204 mViewRoot = findViewById(R.id.background);
205 mViewRoot.setBackground(mBackgroundDrawable);
206 mMenuContainer = findViewById(R.id.menu_container);
207 mMenuContainer.setAlpha(0);
Winson Chungbca03112017-08-16 10:38:15 -0700208 mMenuContainer.setOnTouchListener((v, event) -> {
209 mTouchState.onTouchEvent(event);
210 switch (event.getAction()) {
211 case MotionEvent.ACTION_UP:
212 if (mTouchState.isDoubleTap() || mMenuState == MENU_STATE_FULL) {
213 // Expand to fullscreen if this is a double tap or we are already expanded
214 expandPip();
215 } else if (!mTouchState.isWaitingForDoubleTap()) {
216 // User has stalled long enough for this not to be a drag or a double tap,
217 // just expand the menu if necessary
218 if (mMenuState == MENU_STATE_CLOSE) {
219 showPipMenu();
220 }
221 } else {
222 // Next touch event _may_ be the second tap for the double-tap, schedule a
223 // fallback runnable to trigger the menu if no touch event occurs before the
224 // next tap
225 mTouchState.scheduleDoubleTapTimeoutCallback();
226 }
Alex Lau7d88c132018-12-07 14:22:37 +0900227 // Fall through
228 case MotionEvent.ACTION_CANCEL:
229 mTouchState.reset();
Winson Chungbca03112017-08-16 10:38:15 -0700230 break;
Mady Mellor637cd482017-03-21 10:39:42 -0700231 }
Winson Chungbca03112017-08-16 10:38:15 -0700232 return true;
Winson Chunga29eb982016-12-14 12:01:27 -0800233 });
Winson Chunga556fe62017-12-19 14:46:23 -0800234 mSettingsButton = findViewById(R.id.settings);
235 mSettingsButton.setAlpha(0);
236 mSettingsButton.setOnClickListener((v) -> {
Tracy Zhou2c6117a2018-06-21 11:32:43 -0700237 if (v.getAlpha() != 0) {
238 showSettings();
239 }
Winson Chunga556fe62017-12-19 14:46:23 -0800240 });
Winson Chunga29eb982016-12-14 12:01:27 -0800241 mDismissButton = findViewById(R.id.dismiss);
Mady Mellor637cd482017-03-21 10:39:42 -0700242 mDismissButton.setAlpha(0);
Winson Chunga29eb982016-12-14 12:01:27 -0800243 mDismissButton.setOnClickListener((v) -> {
244 dismissPip();
245 });
Alan Viverette51efddb2017-04-05 10:00:01 -0400246 mActionsGroup = findViewById(R.id.actions_group);
Mady Mellora7f69742017-02-03 11:00:20 -0800247 mBetweenActionPaddingLand = getResources().getDimensionPixelSize(
248 R.dimen.pip_between_action_padding_land);
Alan Viverette51efddb2017-04-05 10:00:01 -0400249 mExpandButton = findViewById(R.id.expand_button);
Winson Chungc75ffe82016-12-16 16:20:16 -0800250
Winson Chunge7a3d222017-03-09 13:26:45 -0800251 updateFromIntent(getIntent());
Phil Weaverf00cd142017-03-03 13:44:00 -0800252 setTitle(R.string.pip_menu_title);
Wale Ogunwale5e8ecb02017-04-14 13:15:55 -0700253 setDisablePreviewScreenshots(true);
Winson Chungc75ffe82016-12-16 16:20:16 -0800254 }
255
256 @Override
257 protected void onNewIntent(Intent intent) {
258 super.onNewIntent(intent);
Winson Chunge7a3d222017-03-09 13:26:45 -0800259 updateFromIntent(intent);
Winson Chung15504af2016-11-02 18:11:36 -0700260 }
261
262 @Override
Winson Chungd2d90972017-02-28 11:40:41 -0800263 public void onUserInteraction() {
Winson Chung379b9ab2017-04-04 11:57:36 -0700264 if (mAllowMenuTimeout) {
265 repostDelayedFinish(POST_INTERACTION_DISMISS_DELAY);
266 }
Winson Chungd2d90972017-02-28 11:40:41 -0800267 }
268
269 @Override
Winson Chung61ecc1b2017-02-17 10:46:17 -0800270 protected void onUserLeaveHint() {
271 super.onUserLeaveHint();
272
Winson Chung9f69b782017-05-16 15:27:49 -0700273 // 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 -0800274 // recreated on the top next time it starts
Winson Chung9f69b782017-05-16 15:27:49 -0700275 hideMenu();
Winson Chung61ecc1b2017-02-17 10:46:17 -0800276 }
277
278 @Override
Winson Chungef4dc812017-04-11 13:31:44 -0700279 protected void onStop() {
280 super.onStop();
281
282 cancelDelayedFinish();
283 }
284
285 @Override
Winson Chung61ecc1b2017-02-17 10:46:17 -0800286 protected void onDestroy() {
287 super.onDestroy();
288
289 // Fallback, if we are destroyed for any other reason (like when the task is being reset),
290 // also reset the callback.
291 notifyActivityCallback(null);
292 }
293
294 @Override
Winson Chungc75ffe82016-12-16 16:20:16 -0800295 public void onPictureInPictureModeChanged(boolean isInPictureInPictureMode) {
296 if (!isInPictureInPictureMode) {
297 finish();
298 }
299 }
300
301 @Override
Winson Chungc75ffe82016-12-16 16:20:16 -0800302 public boolean dispatchTouchEvent(MotionEvent ev) {
Winson Chung34488242017-04-26 15:53:51 -0700303 if (!mAllowTouches) {
304 return super.dispatchTouchEvent(ev);
305 }
306
Winson Chungc75ffe82016-12-16 16:20:16 -0800307 // On the first action outside the window, hide the menu
308 switch (ev.getAction()) {
309 case MotionEvent.ACTION_OUTSIDE:
310 hideMenu();
311 break;
312 case MotionEvent.ACTION_DOWN:
313 mDownPosition.set(ev.getX(), ev.getY());
Winson Chungd2d90972017-02-28 11:40:41 -0800314 mDownDelta.set(0f, 0f);
Winson Chungc75ffe82016-12-16 16:20:16 -0800315 break;
316 case MotionEvent.ACTION_MOVE:
317 mDownDelta.set(ev.getX() - mDownPosition.x, ev.getY() - mDownPosition.y);
Mady Mellor637cd482017-03-21 10:39:42 -0700318 if (mDownDelta.length() > mViewConfig.getScaledTouchSlop()
319 && mMenuState != MENU_STATE_NONE) {
Winson Chungd2d90972017-02-28 11:40:41 -0800320 // Restore the input consumer and let that drive the movement of this menu
321 notifyRegisterInputConsumer();
322 cancelDelayedFinish();
Winson Chungc75ffe82016-12-16 16:20:16 -0800323 }
Winson Chungd2d90972017-02-28 11:40:41 -0800324 break;
Winson Chungc75ffe82016-12-16 16:20:16 -0800325 }
326 return super.dispatchTouchEvent(ev);
Winson Chung15504af2016-11-02 18:11:36 -0700327 }
328
329 @Override
330 public void finish() {
Winson Chungc75ffe82016-12-16 16:20:16 -0800331 notifyActivityCallback(null);
Winson Chung15504af2016-11-02 18:11:36 -0700332 super.finish();
Winson Chungc75ffe82016-12-16 16:20:16 -0800333 // Hide without an animation (the menu should already be invisible at this point)
334 overridePendingTransition(0, 0);
Winson Chung15504af2016-11-02 18:11:36 -0700335 }
336
337 @Override
338 public void setTaskDescription(ActivityManager.TaskDescription taskDescription) {
339 // Do nothing
340 }
341
Mady Mellor637cd482017-03-21 10:39:42 -0700342 private void showMenu(int menuState, Rect stackBounds, Rect movementBounds,
Winson Chungbb787442017-09-01 11:33:47 -0700343 boolean allowMenuTimeout, boolean resizeMenuOnShow) {
Winson Chung379b9ab2017-04-04 11:57:36 -0700344 mAllowMenuTimeout = allowMenuTimeout;
Mady Mellor637cd482017-03-21 10:39:42 -0700345 if (mMenuState != menuState) {
Winson Chungbb787442017-09-01 11:33:47 -0700346 // Disallow touches if the menu needs to resize while showing, and we are transitioning
347 // to/from a full menu state.
348 boolean disallowTouchesUntilAnimationEnd = resizeMenuOnShow &&
349 (mMenuState == MENU_STATE_FULL || menuState == MENU_STATE_FULL);
350 mAllowTouches = !disallowTouchesUntilAnimationEnd;
Mady Mellor637cd482017-03-21 10:39:42 -0700351 cancelDelayedFinish();
Mady Mellor81d40612017-03-10 15:14:10 -0800352 updateActionViews(stackBounds);
Winson Chungc75ffe82016-12-16 16:20:16 -0800353 if (mMenuContainerAnimator != null) {
354 mMenuContainerAnimator.cancel();
355 }
Mady Mellor637cd482017-03-21 10:39:42 -0700356 notifyMenuStateChange(menuState);
Mady Mellor637cd482017-03-21 10:39:42 -0700357 mMenuContainerAnimator = new AnimatorSet();
358 ObjectAnimator menuAnim = ObjectAnimator.ofFloat(mMenuContainer, View.ALPHA,
Winson Chungc75ffe82016-12-16 16:20:16 -0800359 mMenuContainer.getAlpha(), 1f);
Mady Mellor637cd482017-03-21 10:39:42 -0700360 menuAnim.addUpdateListener(mMenuBgUpdateListener);
Winson Chunga556fe62017-12-19 14:46:23 -0800361 ObjectAnimator settingsAnim = ObjectAnimator.ofFloat(mSettingsButton, View.ALPHA,
362 mSettingsButton.getAlpha(), 1f);
Mady Mellor637cd482017-03-21 10:39:42 -0700363 ObjectAnimator dismissAnim = ObjectAnimator.ofFloat(mDismissButton, View.ALPHA,
364 mDismissButton.getAlpha(), 1f);
365 if (menuState == MENU_STATE_FULL) {
Winson Chunga556fe62017-12-19 14:46:23 -0800366 mMenuContainerAnimator.playTogether(menuAnim, settingsAnim, dismissAnim);
Mady Mellor637cd482017-03-21 10:39:42 -0700367 } else {
Winson Chung2a35e6d2018-01-13 14:27:50 -0800368 mMenuContainerAnimator.playTogether(dismissAnim);
Mady Mellor637cd482017-03-21 10:39:42 -0700369 }
Winson Chungc75ffe82016-12-16 16:20:16 -0800370 mMenuContainerAnimator.setInterpolator(Interpolators.ALPHA_IN);
371 mMenuContainerAnimator.setDuration(MENU_FADE_DURATION);
Winson Chung0f873de2017-03-30 10:26:48 -0700372 if (allowMenuTimeout) {
373 mMenuContainerAnimator.addListener(new AnimatorListenerAdapter() {
374 @Override
375 public void onAnimationEnd(Animator animation) {
376 repostDelayedFinish(INITIAL_DISMISS_DELAY);
377 }
378 });
379 }
Winson Chungc75ffe82016-12-16 16:20:16 -0800380 mMenuContainerAnimator.start();
Winson Chungd2d90972017-02-28 11:40:41 -0800381 } else {
Winson Chung2824d7c2017-03-15 19:43:00 -0700382 // If we are already visible, then just start the delayed dismiss and unregister any
383 // existing input consumers from the previous drag
Winson Chung0f873de2017-03-30 10:26:48 -0700384 if (allowMenuTimeout) {
385 repostDelayedFinish(POST_INTERACTION_DISMISS_DELAY);
386 }
Winson Chung2824d7c2017-03-15 19:43:00 -0700387 notifyUnregisterInputConsumer();
Winson Chungc75ffe82016-12-16 16:20:16 -0800388 }
389 }
390
391 private void hideMenu() {
Winson Chung2dbcf092018-10-24 13:00:41 -0700392 hideMenu(null);
393 }
394
395 private void hideMenu(Runnable animationEndCallback) {
396 hideMenu(animationEndCallback, true /* notifyMenuVisibility */, false /* isDismissing */);
Winson Chungc75ffe82016-12-16 16:20:16 -0800397 }
398
Winson Chung21004d52018-06-04 17:09:32 -0700399 private void hideMenu(final Runnable animationFinishedRunnable, boolean notifyMenuVisibility,
400 boolean isDismissing) {
Mady Mellor637cd482017-03-21 10:39:42 -0700401 if (mMenuState != MENU_STATE_NONE) {
Winson Chungc75ffe82016-12-16 16:20:16 -0800402 cancelDelayedFinish();
Winson Chungd2d90972017-02-28 11:40:41 -0800403 if (notifyMenuVisibility) {
Mady Mellor637cd482017-03-21 10:39:42 -0700404 notifyMenuStateChange(MENU_STATE_NONE);
Winson Chungd2d90972017-02-28 11:40:41 -0800405 }
Mady Mellor637cd482017-03-21 10:39:42 -0700406 mMenuContainerAnimator = new AnimatorSet();
407 ObjectAnimator menuAnim = ObjectAnimator.ofFloat(mMenuContainer, View.ALPHA,
Winson Chungc75ffe82016-12-16 16:20:16 -0800408 mMenuContainer.getAlpha(), 0f);
Mady Mellor637cd482017-03-21 10:39:42 -0700409 menuAnim.addUpdateListener(mMenuBgUpdateListener);
Winson Chunga556fe62017-12-19 14:46:23 -0800410 ObjectAnimator settingsAnim = ObjectAnimator.ofFloat(mSettingsButton, View.ALPHA,
411 mSettingsButton.getAlpha(), 0f);
Mady Mellor637cd482017-03-21 10:39:42 -0700412 ObjectAnimator dismissAnim = ObjectAnimator.ofFloat(mDismissButton, View.ALPHA,
413 mDismissButton.getAlpha(), 0f);
Winson Chunga556fe62017-12-19 14:46:23 -0800414 mMenuContainerAnimator.playTogether(menuAnim, settingsAnim, dismissAnim);
Winson Chungc75ffe82016-12-16 16:20:16 -0800415 mMenuContainerAnimator.setInterpolator(Interpolators.ALPHA_OUT);
416 mMenuContainerAnimator.setDuration(MENU_FADE_DURATION);
417 mMenuContainerAnimator.addListener(new AnimatorListenerAdapter() {
418 @Override
419 public void onAnimationEnd(Animator animation) {
420 if (animationFinishedRunnable != null) {
421 animationFinishedRunnable.run();
422 }
Winson Chung21004d52018-06-04 17:09:32 -0700423
424 if (!isDismissing) {
425 // If we are dismissing the PiP, then don't try to pre-emptively finish the
426 // menu activity
427 finish();
428 }
Winson Chungc75ffe82016-12-16 16:20:16 -0800429 }
430 });
431 mMenuContainerAnimator.start();
Winson Chung87e5d552017-04-05 11:49:38 -0700432 } else {
433 // If the menu is not visible, just finish now
434 finish();
Winson Chungc75ffe82016-12-16 16:20:16 -0800435 }
436 }
437
Winson Chunge7a3d222017-03-09 13:26:45 -0800438 private void updateFromIntent(Intent intent) {
Winson Chunge7a3d222017-03-09 13:26:45 -0800439 mToControllerMessenger = intent.getParcelableExtra(EXTRA_CONTROLLER_MESSENGER);
Jaewan Kimc1d15242017-07-07 18:12:04 +0900440 if (mToControllerMessenger == null) {
441 Log.w(TAG, "Controller messenger is null. Stopping.");
442 finish();
443 return;
444 }
Winson Chungccbc4b92017-03-20 14:01:36 -0700445 notifyActivityCallback(mMessenger);
Winson Chungb5026902017-05-03 12:45:13 -0700446
Winson Chunge7a3d222017-03-09 13:26:45 -0800447 ParceledListSlice actions = intent.getParcelableExtra(EXTRA_ACTIONS);
448 if (actions != null) {
Mady Mellor81d40612017-03-10 15:14:10 -0800449 mActions.clear();
450 mActions.addAll(actions.getList());
Winson Chunge7a3d222017-03-09 13:26:45 -0800451 }
Winson Chungb5026902017-05-03 12:45:13 -0700452
Mady Mellor637cd482017-03-21 10:39:42 -0700453 final int menuState = intent.getIntExtra(EXTRA_MENU_STATE, MENU_STATE_NONE);
454 if (menuState != MENU_STATE_NONE) {
Winson Chung853c99a2017-03-21 22:16:42 -0700455 Rect stackBounds = intent.getParcelableExtra(EXTRA_STACK_BOUNDS);
456 Rect movementBounds = intent.getParcelableExtra(EXTRA_MOVEMENT_BOUNDS);
Winson Chung0f873de2017-03-30 10:26:48 -0700457 boolean allowMenuTimeout = intent.getBooleanExtra(EXTRA_ALLOW_TIMEOUT, true);
Winson Chungbb787442017-09-01 11:33:47 -0700458 boolean willResizeMenu = intent.getBooleanExtra(EXTRA_WILL_RESIZE_MENU, false);
459 showMenu(menuState, stackBounds, movementBounds, allowMenuTimeout, willResizeMenu);
Mady Mellor81d40612017-03-10 15:14:10 -0800460 }
Winson Chunga29eb982016-12-14 12:01:27 -0800461 }
462
Winson Chunge7a3d222017-03-09 13:26:45 -0800463 private void setActions(Rect stackBounds, List<RemoteAction> actions) {
464 mActions.clear();
465 mActions.addAll(actions);
466 updateActionViews(stackBounds);
467 }
468
469 private void updateActionViews(Rect stackBounds) {
Alan Viverette51efddb2017-04-05 10:00:01 -0400470 ViewGroup expandContainer = findViewById(R.id.expand_container);
471 ViewGroup actionsContainer = findViewById(R.id.actions_container);
Winson Chunga5acf182017-01-05 16:02:27 -0800472 actionsContainer.setOnTouchListener((v, ev) -> {
473 // Do nothing, prevent click through to parent
474 return true;
475 });
Winson Chunga29eb982016-12-14 12:01:27 -0800476
Mady Mellor637cd482017-03-21 10:39:42 -0700477 if (mActions.isEmpty() || mMenuState == MENU_STATE_CLOSE) {
Winson Chunga5acf182017-01-05 16:02:27 -0800478 actionsContainer.setVisibility(View.INVISIBLE);
479 } else {
480 actionsContainer.setVisibility(View.VISIBLE);
Mady Mellora7f69742017-02-03 11:00:20 -0800481 if (mActionsGroup != null) {
Winson Chungb6de8722017-06-02 12:45:51 -0700482 // Ensure we have as many buttons as actions
Winson Chung7aab0c12017-04-18 12:54:25 -0700483 final LayoutInflater inflater = LayoutInflater.from(this);
484 while (mActionsGroup.getChildCount() < mActions.size()) {
485 final ImageView actionView = (ImageView) inflater.inflate(
486 R.layout.pip_menu_action, mActionsGroup, false);
487 mActionsGroup.addView(actionView);
488 }
Winson Chunga29eb982016-12-14 12:01:27 -0800489
Winson Chungb6de8722017-06-02 12:45:51 -0700490 // Update the visibility of all views
491 for (int i = 0; i < mActionsGroup.getChildCount(); i++) {
492 mActionsGroup.getChildAt(i).setVisibility(i < mActions.size()
493 ? View.VISIBLE
494 : View.GONE);
495 }
496
Winson Chunga5acf182017-01-05 16:02:27 -0800497 // Recreate the layout
Winson Chunge7a3d222017-03-09 13:26:45 -0800498 final boolean isLandscapePip = stackBounds != null &&
499 (stackBounds.width() > stackBounds.height());
Winson Chunga5acf182017-01-05 16:02:27 -0800500 for (int i = 0; i < mActions.size(); i++) {
501 final RemoteAction action = mActions.get(i);
Winson Chung7aab0c12017-04-18 12:54:25 -0700502 final ImageView actionView = (ImageView) mActionsGroup.getChildAt(i);
503
504 // TODO: Check if the action drawable has changed before we reload it
Winson Chunga5acf182017-01-05 16:02:27 -0800505 action.getIcon().loadDrawableAsync(this, d -> {
Winson Chung56fe8832017-02-03 14:37:53 -0800506 d.setTint(Color.WHITE);
Winson Chunga5acf182017-01-05 16:02:27 -0800507 actionView.setImageDrawable(d);
508 }, mHandler);
509 actionView.setContentDescription(action.getContentDescription());
Winson Chung6b88baf2017-03-16 17:10:21 -0700510 if (action.isEnabled()) {
511 actionView.setOnClickListener(v -> {
512 try {
513 action.getActionIntent().send();
514 } catch (CanceledException e) {
515 Log.w(TAG, "Failed to send action", e);
516 }
517 });
Winson Chung6b88baf2017-03-16 17:10:21 -0700518 }
Winson Chungd61a19e2017-04-28 16:36:51 -0700519 actionView.setEnabled(action.isEnabled());
Winson Chungb6de8722017-06-02 12:45:51 -0700520 actionView.setAlpha(action.isEnabled() ? 1f : DISABLED_ACTION_ALPHA);
Winson Chung7aab0c12017-04-18 12:54:25 -0700521
522 // Update the margin between actions
523 LinearLayout.LayoutParams lp = (LinearLayout.LayoutParams)
524 actionView.getLayoutParams();
525 lp.leftMargin = (isLandscapePip && i > 0) ? mBetweenActionPaddingLand : 0;
Winson Chunga5acf182017-01-05 16:02:27 -0800526 }
Winson Chunga29eb982016-12-14 12:01:27 -0800527 }
Mady Mellora7f69742017-02-03 11:00:20 -0800528
Winson Chunge7a3d222017-03-09 13:26:45 -0800529 // Update the expand container margin to adjust the center of the expand button to
530 // account for the existence of the action container
531 FrameLayout.LayoutParams expandedLp =
532 (FrameLayout.LayoutParams) expandContainer.getLayoutParams();
533 expandedLp.topMargin = getResources().getDimensionPixelSize(
534 R.dimen.pip_action_padding);
535 expandedLp.bottomMargin = getResources().getDimensionPixelSize(
536 R.dimen.pip_expand_container_edge_margin);
537 expandContainer.requestLayout();
538 }
Winson Chunga29eb982016-12-14 12:01:27 -0800539 }
540
Mady Mellor81d40612017-03-10 15:14:10 -0800541 private void updateDismissFraction(float fraction) {
542 int alpha;
Mady Mellor637cd482017-03-21 10:39:42 -0700543 final float menuAlpha = 1 - fraction;
544 if (mMenuState == MENU_STATE_FULL) {
545 mMenuContainer.setAlpha(menuAlpha);
Winson Chunga556fe62017-12-19 14:46:23 -0800546 mSettingsButton.setAlpha(menuAlpha);
Mady Mellor637cd482017-03-21 10:39:42 -0700547 mDismissButton.setAlpha(menuAlpha);
Mady Mellor81d40612017-03-10 15:14:10 -0800548 final float interpolatedAlpha =
Mady Mellor637cd482017-03-21 10:39:42 -0700549 MENU_BACKGROUND_ALPHA * menuAlpha + DISMISS_BACKGROUND_ALPHA * fraction;
Winson Chungc3545d32017-04-03 10:44:57 -0700550 alpha = (int) (interpolatedAlpha * 255);
Mady Mellor81d40612017-03-10 15:14:10 -0800551 } else {
Mady Mellor637cd482017-03-21 10:39:42 -0700552 if (mMenuState == MENU_STATE_CLOSE) {
553 mDismissButton.setAlpha(menuAlpha);
554 }
Winson Chungc3545d32017-04-03 10:44:57 -0700555 alpha = (int) (fraction * DISMISS_BACKGROUND_ALPHA * 255);
Mady Mellor81d40612017-03-10 15:14:10 -0800556 }
557 mBackgroundDrawable.setAlpha(alpha);
558 }
559
Winson Chungd2d90972017-02-28 11:40:41 -0800560 private void notifyRegisterInputConsumer() {
561 Message m = Message.obtain();
562 m.what = PipMenuActivityController.MESSAGE_REGISTER_INPUT_CONSUMER;
563 sendMessage(m, "Could not notify controller to register input consumer");
564 }
565
Winson Chung2824d7c2017-03-15 19:43:00 -0700566 private void notifyUnregisterInputConsumer() {
567 Message m = Message.obtain();
568 m.what = PipMenuActivityController.MESSAGE_UNREGISTER_INPUT_CONSUMER;
569 sendMessage(m, "Could not notify controller to unregister input consumer");
570 }
571
Mady Mellor637cd482017-03-21 10:39:42 -0700572 private void notifyMenuStateChange(int menuState) {
573 mMenuState = menuState;
Winson Chung15504af2016-11-02 18:11:36 -0700574 Message m = Message.obtain();
Mady Mellor637cd482017-03-21 10:39:42 -0700575 m.what = PipMenuActivityController.MESSAGE_MENU_STATE_CHANGED;
576 m.arg1 = menuState;
Winson Chunga29eb982016-12-14 12:01:27 -0800577 sendMessage(m, "Could not notify controller of PIP menu visibility");
Winson Chung15504af2016-11-02 18:11:36 -0700578 }
579
Winson Chunga29eb982016-12-14 12:01:27 -0800580 private void expandPip() {
Winson Chungd2d90972017-02-28 11:40:41 -0800581 // Do not notify menu visibility when hiding the menu, the controller will do this when it
582 // handles the message
Winson Chungc75ffe82016-12-16 16:20:16 -0800583 hideMenu(() -> {
584 sendEmptyMessage(PipMenuActivityController.MESSAGE_EXPAND_PIP,
585 "Could not notify controller to expand PIP");
Winson Chung21004d52018-06-04 17:09:32 -0700586 }, false /* notifyMenuVisibility */, false /* isDismissing */);
Winson Chunga29eb982016-12-14 12:01:27 -0800587 }
588
589 private void minimizePip() {
590 sendEmptyMessage(PipMenuActivityController.MESSAGE_MINIMIZE_PIP,
591 "Could not notify controller to minimize PIP");
592 }
593
594 private void dismissPip() {
Winson Chungd2d90972017-02-28 11:40:41 -0800595 // Do not notify menu visibility when hiding the menu, the controller will do this when it
596 // handles the message
Winson Chungc75ffe82016-12-16 16:20:16 -0800597 hideMenu(() -> {
598 sendEmptyMessage(PipMenuActivityController.MESSAGE_DISMISS_PIP,
599 "Could not notify controller to dismiss PIP");
Winson Chung21004d52018-06-04 17:09:32 -0700600 }, false /* notifyMenuVisibility */, true /* isDismissing */);
Winson Chungc75ffe82016-12-16 16:20:16 -0800601 }
602
Mady Mellor637cd482017-03-21 10:39:42 -0700603 private void showPipMenu() {
604 Message m = Message.obtain();
605 m.what = PipMenuActivityController.MESSAGE_SHOW_MENU;
606 sendMessage(m, "Could not notify controller to show PIP menu");
607 }
608
Winson Chunga556fe62017-12-19 14:46:23 -0800609 private void showSettings() {
610 final Pair<ComponentName, Integer> topPipActivityInfo =
611 PipUtils.getTopPinnedActivity(this, ActivityManager.getService());
612 if (topPipActivityInfo.first != null) {
613 final UserHandle user = UserHandle.of(topPipActivityInfo.second);
614 final Intent settingsIntent = new Intent(ACTION_PICTURE_IN_PICTURE_SETTINGS,
615 Uri.fromParts("package", topPipActivityInfo.first.getPackageName(), null));
616 settingsIntent.putExtra(Intent.EXTRA_USER_HANDLE, user);
617 settingsIntent.setFlags(FLAG_ACTIVITY_NEW_TASK | FLAG_ACTIVITY_CLEAR_TASK);
618 startActivity(settingsIntent);
619 }
620 }
621
Winson Chungc75ffe82016-12-16 16:20:16 -0800622 private void notifyActivityCallback(Messenger callback) {
623 Message m = Message.obtain();
624 m.what = PipMenuActivityController.MESSAGE_UPDATE_ACTIVITY_CALLBACK;
625 m.replyTo = callback;
626 sendMessage(m, "Could not notify controller of activity finished");
Winson Chunga29eb982016-12-14 12:01:27 -0800627 }
628
629 private void sendEmptyMessage(int what, String errorMsg) {
Winson Chung15504af2016-11-02 18:11:36 -0700630 Message m = Message.obtain();
Winson Chunga29eb982016-12-14 12:01:27 -0800631 m.what = what;
632 sendMessage(m, errorMsg);
633 }
634
635 private void sendMessage(Message m, String errorMsg) {
Jaewan Kimc1d15242017-07-07 18:12:04 +0900636 if (mToControllerMessenger == null) {
637 return;
638 }
Winson Chung15504af2016-11-02 18:11:36 -0700639 try {
640 mToControllerMessenger.send(m);
641 } catch (RemoteException e) {
Winson Chunga29eb982016-12-14 12:01:27 -0800642 Log.e(TAG, errorMsg, e);
Winson Chung15504af2016-11-02 18:11:36 -0700643 }
644 }
645
Winson Chungc75ffe82016-12-16 16:20:16 -0800646 private void cancelDelayedFinish() {
Winson Chung4a526c12017-05-16 13:35:43 -0700647 mHandler.removeCallbacks(mFinishRunnable);
Winson Chungc75ffe82016-12-16 16:20:16 -0800648 }
649
Rhed Jao5e00c982018-11-27 19:41:05 +0800650 private void repostDelayedFinish(int delay) {
651 int recommendedTimeout = mAccessibilityManager.getRecommendedTimeoutMillis(delay,
652 FLAG_CONTENT_ICONS | FLAG_CONTENT_CONTROLS);
Winson Chung4a526c12017-05-16 13:35:43 -0700653 mHandler.removeCallbacks(mFinishRunnable);
Rhed Jao5e00c982018-11-27 19:41:05 +0800654 mHandler.postDelayed(mFinishRunnable, recommendedTimeout);
Winson Chung15504af2016-11-02 18:11:36 -0700655 }
Winson Chung15504af2016-11-02 18:11:36 -0700656}