blob: f49732d98ae8e8766376f74b1b9cf2c73436d3dd [file] [log] [blame]
Youngsang Chof1647922015-12-17 13:39:39 -08001/*
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
Winsonbf8c2c02016-10-18 18:56:24 -070017package com.android.systemui.pip.tv;
Youngsang Chof1647922015-12-17 13:39:39 -080018
Winson Chung67f5c8b2018-09-24 12:09:19 -070019import static android.app.ActivityTaskManager.INVALID_STACK_ID;
20import static android.app.WindowConfiguration.ACTIVITY_TYPE_UNDEFINED;
21import static android.app.WindowConfiguration.WINDOWING_MODE_PINNED;
Winson Chung67f5c8b2018-09-24 12:09:19 -070022
Winsonc0d70582016-01-29 10:24:39 -080023import android.app.ActivityManager.RunningTaskInfo;
Youngsang Chof1647922015-12-17 13:39:39 -080024import android.app.ActivityManager.StackInfo;
Wale Ogunwale65ebd952018-04-25 15:41:44 -070025import android.app.ActivityTaskManager;
Wale Ogunwale65ebd952018-04-25 15:41:44 -070026import android.app.IActivityTaskManager;
Youngsang Chof1647922015-12-17 13:39:39 -080027import android.content.BroadcastReceiver;
Jaewan Kim62338192016-02-25 10:00:05 -080028import android.content.ComponentName;
Youngsang Chof1647922015-12-17 13:39:39 -080029import android.content.Context;
30import android.content.Intent;
31import android.content.IntentFilter;
Winson Chung2a82fe52017-02-02 14:43:34 -080032import android.content.pm.ParceledListSlice;
Winson Chung0c5a5922017-05-22 17:41:06 -070033import android.content.res.Configuration;
Youngsang Chof1647922015-12-17 13:39:39 -080034import android.content.res.Resources;
35import android.graphics.Rect;
Jaewan Kim62338192016-02-25 10:00:05 -080036import android.media.session.MediaController;
37import android.media.session.MediaSessionManager;
Jaewan Kim8f584b82016-03-22 22:16:59 +090038import android.media.session.PlaybackState;
Wale Ogunwale480dca02016-02-06 13:58:29 -080039import android.os.Debug;
Youngsang Chof1647922015-12-17 13:39:39 -080040import android.os.Handler;
41import android.os.RemoteException;
Kyeongkab.Namd61de152018-10-09 14:55:42 +090042import android.os.UserHandle;
Dongwon Kang353d8d72016-04-13 21:55:22 -070043import android.text.TextUtils;
Youngsang Chof1647922015-12-17 13:39:39 -080044import android.util.Log;
Jaewan Kimd89a6942016-04-08 11:41:45 +090045import android.util.Pair;
Hongwei Wang43a752b2019-09-17 20:20:30 +000046import android.view.DisplayInfo;
Gus Prevasab336792018-11-14 13:52:20 -050047
Winson Chung67f5c8b2018-09-24 12:09:19 -070048import com.android.systemui.Dependency;
Jaewan Kim8f584b82016-03-22 22:16:59 +090049import com.android.systemui.R;
Winson Chung67f5c8b2018-09-24 12:09:19 -070050import com.android.systemui.UiOffloadThread;
Fabian Kozynski5ca7a512019-10-16 19:56:11 +000051import com.android.systemui.broadcast.BroadcastDispatcher;
Winson Chung29a78652017-02-09 18:35:26 -080052import com.android.systemui.pip.BasePipManager;
Hongwei Wang43a752b2019-09-17 20:20:30 +000053import com.android.systemui.pip.PipBoundsHandler;
Hongwei Wangec3cb3c2020-03-09 10:43:21 -070054import com.android.systemui.pip.PipSurfaceTransactionHelper;
Hongwei Wang85cf41f2020-01-15 15:14:47 -080055import com.android.systemui.pip.PipTaskOrganizer;
Winson Chung2cf6ad82017-11-09 17:36:59 -080056import com.android.systemui.shared.system.ActivityManagerWrapper;
Hongwei Wang43a752b2019-09-17 20:20:30 +000057import com.android.systemui.shared.system.PinnedStackListenerForwarder.PinnedStackListener;
Winson Chung67f5c8b2018-09-24 12:09:19 -070058import com.android.systemui.shared.system.TaskStackChangeListener;
59import com.android.systemui.shared.system.WindowManagerWrapper;
Hongwei Wang5c52ff82020-04-20 16:02:30 -070060import com.android.systemui.stackdivider.Divider;
Gus Prevasab336792018-11-14 13:52:20 -050061
Youngsang Chof1647922015-12-17 13:39:39 -080062import java.util.ArrayList;
63import java.util.List;
64
Dave Mankofff5019142019-12-20 16:22:57 -050065import javax.inject.Inject;
66import javax.inject.Singleton;
67
Youngsang Chof1647922015-12-17 13:39:39 -080068/**
69 * Manages the picture-in-picture (PIP) UI and states.
70 */
Dave Mankofff5019142019-12-20 16:22:57 -050071@Singleton
Hongwei Wang85cf41f2020-01-15 15:14:47 -080072public class PipManager implements BasePipManager, PipTaskOrganizer.PipTransitionCallback {
Youngsang Chof1647922015-12-17 13:39:39 -080073 private static final String TAG = "PipManager";
Jaewan Kim26c63562017-04-26 15:41:43 +090074 static final boolean DEBUG = Log.isLoggable(TAG, Log.DEBUG);
75
Jaewan Kim8af250e2017-02-14 19:12:01 +090076 private static final String SETTINGS_PACKAGE_AND_CLASS_DELIMITER = "/";
Youngsang Chof1647922015-12-17 13:39:39 -080077
Jaewan Kim8af250e2017-02-14 19:12:01 +090078 private static List<Pair<String, String>> sSettingsPackageAndClassNamePairList;
Jaewan Kimd89a6942016-04-08 11:41:45 +090079
80 /**
Jaewan Kima0d4d252016-03-31 13:37:10 +090081 * State when there's no PIP.
82 */
Wale Ogunwale480dca02016-02-06 13:58:29 -080083 public static final int STATE_NO_PIP = 0;
Jaewan Kima0d4d252016-03-31 13:37:10 +090084 /**
Jaewan Kime6309232017-04-14 15:26:20 +090085 * State when PIP is shown. This is used as default PIP state.
Jaewan Kima0d4d252016-03-31 13:37:10 +090086 */
Jaewan Kime6309232017-04-14 15:26:20 +090087 public static final int STATE_PIP = 1;
Jaewan Kima0d4d252016-03-31 13:37:10 +090088 /**
89 * State when PIP menu dialog is shown.
90 */
Wale Ogunwale480dca02016-02-06 13:58:29 -080091 public static final int STATE_PIP_MENU = 2;
Youngsang Chof1647922015-12-17 13:39:39 -080092
Youngsang Choad8ceb02016-01-15 16:59:27 -080093 private static final int TASK_ID_NO_PIP = -1;
94 private static final int INVALID_RESOURCE_TYPE = -1;
95
Wale Ogunwale480dca02016-02-06 13:58:29 -080096 public static final int SUSPEND_PIP_RESIZE_REASON_WAITING_FOR_MENU_ACTIVITY_FINISH = 0x1;
Jaewan Kimf40fcdc2016-03-04 17:58:22 +090097
Jaewan Kim8f584b82016-03-22 22:16:59 +090098 /**
99 * PIPed activity is playing a media and it can be paused.
100 */
101 static final int PLAYBACK_STATE_PLAYING = 0;
102 /**
103 * PIPed activity has a paused media and it can be played.
104 */
105 static final int PLAYBACK_STATE_PAUSED = 1;
106 /**
107 * Users are unable to control PIPed activity's media playback.
108 */
109 static final int PLAYBACK_STATE_UNAVAILABLE = 2;
110
Jaewan Kimf40fcdc2016-03-04 17:58:22 +0900111 private static final int CLOSE_PIP_WHEN_MEDIA_SESSION_GONE_TIMEOUT_MS = 3000;
112
Wale Ogunwale480dca02016-02-06 13:58:29 -0800113 private int mSuspendPipResizingReason;
114
Youngsang Chof1647922015-12-17 13:39:39 -0800115 private Context mContext;
Hongwei Wang43a752b2019-09-17 20:20:30 +0000116 private PipBoundsHandler mPipBoundsHandler;
Hongwei Wang85cf41f2020-01-15 15:14:47 -0800117 private PipTaskOrganizer mPipTaskOrganizer;
Wale Ogunwale65ebd952018-04-25 15:41:44 -0700118 private IActivityTaskManager mActivityTaskManager;
Jaewan Kim62338192016-02-25 10:00:05 -0800119 private MediaSessionManager mMediaSessionManager;
Youngsang Chof1647922015-12-17 13:39:39 -0800120 private int mState = STATE_NO_PIP;
Winson Chung0a657372017-05-12 15:04:54 -0700121 private int mResumeResizePinnedStackRunnableState = STATE_NO_PIP;
Youngsang Chof1647922015-12-17 13:39:39 -0800122 private final Handler mHandler = new Handler();
123 private List<Listener> mListeners = new ArrayList<>();
Jaewan Kima0d4d252016-03-31 13:37:10 +0900124 private List<MediaListener> mMediaListeners = new ArrayList<>();
Jaewan Kimc92a7d12016-02-15 17:33:25 -0800125 private Rect mCurrentPipBounds;
126 private Rect mPipBounds;
Winson Chungdb2ad5d2017-02-07 17:18:01 -0800127 private Rect mDefaultPipBounds = new Rect();
Jaewan Kimd89a6942016-04-08 11:41:45 +0900128 private Rect mSettingsPipBounds;
Jaewan Kimc92a7d12016-02-15 17:33:25 -0800129 private Rect mMenuModePipBounds;
Winson Chung0c5a5922017-05-22 17:41:06 -0700130 private int mLastOrientation = Configuration.ORIENTATION_UNDEFINED;
Youngsang Chof1647922015-12-17 13:39:39 -0800131 private boolean mInitialized;
Youngsang Choad8ceb02016-01-15 16:59:27 -0800132 private int mPipTaskId = TASK_ID_NO_PIP;
Wale Ogunwale89be5762017-10-04 13:27:49 -0700133 private int mPinnedStackId = INVALID_STACK_ID;
Jaewan Kim62338192016-02-25 10:00:05 -0800134 private ComponentName mPipComponentName;
135 private MediaController mPipMediaController;
Dongwon Kang353d8d72016-04-13 21:55:22 -0700136 private String[] mLastPackagesResourceGranted;
Jaewan Kim26c63562017-04-26 15:41:43 +0900137 private PipNotification mPipNotification;
Winson Chungb6de8722017-06-02 12:45:51 -0700138 private ParceledListSlice mCustomActions;
Ben Linede9a602020-02-26 12:16:09 -0800139 private int mResizeAnimationDuration;
Youngsang Choad8ceb02016-01-15 16:59:27 -0800140
Hongwei Wang43a752b2019-09-17 20:20:30 +0000141 // Used to calculate the movement bounds
142 private final DisplayInfo mTmpDisplayInfo = new DisplayInfo();
143 private final Rect mTmpInsetBounds = new Rect();
144 private final Rect mTmpNormalBounds = new Rect();
145
Winson Chung44315c62017-08-24 13:35:24 -0700146 // Keeps track of the IME visibility to adjust the PiP when the IME is visible
147 private boolean mImeVisible;
148 private int mImeHeightAdjustment;
149
Hongwei Wang43a752b2019-09-17 20:20:30 +0000150 private final PinnedStackListener mPinnedStackListener = new PipManagerPinnedStackListener();
Winson Chung2a82fe52017-02-02 14:43:34 -0800151
Wale Ogunwale480dca02016-02-06 13:58:29 -0800152 private final Runnable mResizePinnedStackRunnable = new Runnable() {
153 @Override
154 public void run() {
Winson Chung0a657372017-05-12 15:04:54 -0700155 resizePinnedStack(mResumeResizePinnedStackRunnableState);
Wale Ogunwale480dca02016-02-06 13:58:29 -0800156 }
157 };
Jaewan Kimf40fcdc2016-03-04 17:58:22 +0900158 private final Runnable mClosePipRunnable = new Runnable() {
159 @Override
160 public void run() {
161 closePip();
162 }
163 };
Youngsang Chof1647922015-12-17 13:39:39 -0800164
Youngsang Choad8ceb02016-01-15 16:59:27 -0800165 private final BroadcastReceiver mBroadcastReceiver = new BroadcastReceiver() {
Youngsang Chof1647922015-12-17 13:39:39 -0800166 @Override
167 public void onReceive(Context context, Intent intent) {
Youngsang Choad8ceb02016-01-15 16:59:27 -0800168 String action = intent.getAction();
Jaewan Kimc552b042016-01-18 16:08:45 +0900169 if (Intent.ACTION_MEDIA_RESOURCE_GRANTED.equals(action)) {
Youngsang Choad8ceb02016-01-15 16:59:27 -0800170 String[] packageNames = intent.getStringArrayExtra(Intent.EXTRA_PACKAGES);
171 int resourceType = intent.getIntExtra(Intent.EXTRA_MEDIA_RESOURCE_TYPE,
172 INVALID_RESOURCE_TYPE);
Dongwon Kang353d8d72016-04-13 21:55:22 -0700173 if (packageNames != null && packageNames.length > 0
Youngsang Choad8ceb02016-01-15 16:59:27 -0800174 && resourceType == Intent.EXTRA_MEDIA_RESOURCE_TYPE_VIDEO_CODEC) {
175 handleMediaResourceGranted(packageNames);
176 }
Youngsang Chof1647922015-12-17 13:39:39 -0800177 }
Youngsang Choad8ceb02016-01-15 16:59:27 -0800178
Youngsang Chof1647922015-12-17 13:39:39 -0800179 }
180 };
Jaewan Kim62338192016-02-25 10:00:05 -0800181 private final MediaSessionManager.OnActiveSessionsChangedListener mActiveMediaSessionListener =
182 new MediaSessionManager.OnActiveSessionsChangedListener() {
183 @Override
184 public void onActiveSessionsChanged(List<MediaController> controllers) {
185 updateMediaController(controllers);
186 }
187 };
Youngsang Chof1647922015-12-17 13:39:39 -0800188
Winson Chung2a82fe52017-02-02 14:43:34 -0800189 /**
190 * Handler for messages from the PIP controller.
191 */
Hongwei Wang43a752b2019-09-17 20:20:30 +0000192 private class PipManagerPinnedStackListener extends PinnedStackListener {
Winson Chung2a82fe52017-02-02 14:43:34 -0800193 @Override
Winson Chung44315c62017-08-24 13:35:24 -0700194 public void onImeVisibilityChanged(boolean imeVisible, int imeHeight) {
195 if (mState == STATE_PIP) {
196 if (mImeVisible != imeVisible) {
197 if (imeVisible) {
198 // Save the IME height adjustment, and offset to not occlude the IME
199 mPipBounds.offset(0, -imeHeight);
200 mImeHeightAdjustment = imeHeight;
201 } else {
202 // Apply the inverse adjustment when the IME is hidden
203 mPipBounds.offset(0, mImeHeightAdjustment);
204 }
205 mImeVisible = imeVisible;
206 resizePinnedStack(STATE_PIP);
207 }
208 }
209 }
Winson Chung2a82fe52017-02-02 14:43:34 -0800210
211 @Override
Hongwei Wang3c981f62020-04-07 16:16:26 -0700212 public void onMovementBoundsChanged(boolean fromImeAdjustment) {
Winson Chung2a82fe52017-02-02 14:43:34 -0800213 mHandler.post(() -> {
Hongwei Wang43a752b2019-09-17 20:20:30 +0000214 // Populate the inset / normal bounds and DisplayInfo from mPipBoundsHandler first.
Hongwei Wang3c981f62020-04-07 16:16:26 -0700215 final Rect destinationBounds = new Rect();
Hongwei Wang43a752b2019-09-17 20:20:30 +0000216 mPipBoundsHandler.onMovementBoundsChanged(mTmpInsetBounds, mTmpNormalBounds,
Hongwei Wang3c981f62020-04-07 16:16:26 -0700217 destinationBounds, mTmpDisplayInfo);
218 mDefaultPipBounds.set(destinationBounds);
Winson Chung2a82fe52017-02-02 14:43:34 -0800219 });
220 }
221
222 @Override
Winson Chungb6de8722017-06-02 12:45:51 -0700223 public void onActionsChanged(ParceledListSlice actions) {
224 mCustomActions = actions;
225 mHandler.post(() -> {
226 for (int i = mListeners.size() - 1; i >= 0; --i) {
227 mListeners.get(i).onPipMenuActionsChanged(mCustomActions);
228 }
229 });
230 }
Winson Chung2a82fe52017-02-02 14:43:34 -0800231 }
232
Dave Mankofff5019142019-12-20 16:22:57 -0500233 @Inject
jorgegil@google.com5bdec5f2020-02-28 13:14:39 -0800234 public PipManager(Context context, BroadcastDispatcher broadcastDispatcher,
Hongwei Wangec3cb3c2020-03-09 10:43:21 -0700235 PipBoundsHandler pipBoundsHandler,
Ben Lin6189fa42020-04-29 14:59:16 -0700236 PipTaskOrganizer pipTaskOrganizer,
Hongwei Wang5c52ff82020-04-20 16:02:30 -0700237 PipSurfaceTransactionHelper surfaceTransactionHelper,
238 Divider divider) {
Youngsang Chof1647922015-12-17 13:39:39 -0800239 if (mInitialized) {
240 return;
241 }
Winson Chung55701472020-03-04 19:30:30 -0800242
Youngsang Chof1647922015-12-17 13:39:39 -0800243 mInitialized = true;
244 mContext = context;
jorgegil@google.com5bdec5f2020-02-28 13:14:39 -0800245 mPipBoundsHandler = pipBoundsHandler;
Sergey Nikolaienkove767da42020-04-10 15:47:22 +0200246 // Ensure that we have the display info in case we get calls to update the bounds before the
247 // listener calls back
248 final DisplayInfo displayInfo = new DisplayInfo();
249 context.getDisplay().getDisplayInfo(displayInfo);
250 mPipBoundsHandler.onDisplayInfoChanged(displayInfo);
251
Ben Linede9a602020-02-26 12:16:09 -0800252 mResizeAnimationDuration = context.getResources()
253 .getInteger(R.integer.config_pipResizeAnimationDuration);
Ben Lin6189fa42020-04-29 14:59:16 -0700254 mPipTaskOrganizer = pipTaskOrganizer;
Hongwei Wang85cf41f2020-01-15 15:14:47 -0800255 mPipTaskOrganizer.registerPipTransitionCallback(this);
Wale Ogunwale65ebd952018-04-25 15:41:44 -0700256 mActivityTaskManager = ActivityTaskManager.getService();
Winson Chung2cf6ad82017-11-09 17:36:59 -0800257 ActivityManagerWrapper.getInstance().registerTaskStackListener(mTaskStackListener);
Jaewan Kim73ef3512016-07-18 13:50:33 +0900258 IntentFilter intentFilter = new IntentFilter();
259 intentFilter.addAction(Intent.ACTION_MEDIA_RESOURCE_GRANTED);
Fabian Kozynski5ca7a512019-10-16 19:56:11 +0000260 broadcastDispatcher.registerReceiver(mBroadcastReceiver, intentFilter,
261 null /* handler */, UserHandle.ALL);
Jaewan Kim73ef3512016-07-18 13:50:33 +0900262
Jaewan Kim8af250e2017-02-14 19:12:01 +0900263 if (sSettingsPackageAndClassNamePairList == null) {
264 String[] settings = mContext.getResources().getStringArray(
265 R.array.tv_pip_settings_class_name);
266 sSettingsPackageAndClassNamePairList = new ArrayList<>();
267 if (settings != null) {
268 for (int i = 0; i < settings.length; i++) {
269 Pair<String, String> entry = null;
270 String[] packageAndClassName =
271 settings[i].split(SETTINGS_PACKAGE_AND_CLASS_DELIMITER);
272 switch (packageAndClassName.length) {
273 case 1:
274 entry = Pair.<String, String>create(packageAndClassName[0], null);
275 break;
276 case 2:
Shuichi.Noguchid2067822018-01-13 21:47:04 +0900277 if (packageAndClassName[1] != null) {
278 entry = Pair.<String, String>create(packageAndClassName[0],
279 packageAndClassName[1].startsWith(".")
280 ? packageAndClassName[0] + packageAndClassName[1]
281 : packageAndClassName[1]);
Jaewan Kimdc298752017-02-24 10:45:12 +0900282 }
Shuichi.Noguchid2067822018-01-13 21:47:04 +0900283 break;
Jaewan Kim8af250e2017-02-14 19:12:01 +0900284 }
285 if (entry != null) {
286 sSettingsPackageAndClassNamePairList.add(entry);
Jaewan Kimdc298752017-02-24 10:45:12 +0900287 } else {
288 Log.w(TAG, "Ignoring malformed settings name " + settings[i]);
Jaewan Kim8af250e2017-02-14 19:12:01 +0900289 }
290 }
291 }
292 }
293
Winson Chung8068ec22017-06-02 09:58:00 -0700294 // Initialize the last orientation and apply the current configuration
295 Configuration initialConfig = mContext.getResources().getConfiguration();
296 mLastOrientation = initialConfig.orientation;
297 loadConfigurationsAndApply(initialConfig);
298
Jaewan Kim73ef3512016-07-18 13:50:33 +0900299 mMediaSessionManager =
300 (MediaSessionManager) mContext.getSystemService(Context.MEDIA_SESSION_SERVICE);
Winson Chung2a82fe52017-02-02 14:43:34 -0800301
302 try {
Hongwei Wang43a752b2019-09-17 20:20:30 +0000303 WindowManagerWrapper.getInstance().addPinnedStackListener(mPinnedStackListener);
Wale Ogunwaleadf116e2020-03-27 16:36:01 -0700304 mPipTaskOrganizer.registerOrganizer(WINDOWING_MODE_PINNED);
Hongwei Wangc90eb8e2020-03-27 12:45:13 -0700305 } catch (RemoteException | UnsupportedOperationException e) {
Winson Chung2a82fe52017-02-02 14:43:34 -0800306 Log.e(TAG, "Failed to register pinned stack listener", e);
307 }
Jaewan Kim26c63562017-04-26 15:41:43 +0900308
Dave Mankofff5019142019-12-20 16:22:57 -0500309 mPipNotification = new PipNotification(context, broadcastDispatcher, this);
Jaewan Kim73ef3512016-07-18 13:50:33 +0900310 }
311
Winson Chung0c5a5922017-05-22 17:41:06 -0700312 private void loadConfigurationsAndApply(Configuration newConfig) {
313 if (mLastOrientation != newConfig.orientation) {
314 // Don't resize the pinned stack on orientation change. TV does not care about this case
315 // and this could clobber the existing animation to the new bounds calculated by WM.
316 mLastOrientation = newConfig.orientation;
317 return;
318 }
319
Jaewan Kim73ef3512016-07-18 13:50:33 +0900320 Resources res = mContext.getResources();
Jaewan Kimd89a6942016-04-08 11:41:45 +0900321 mSettingsPipBounds = Rect.unflattenFromString(res.getString(
322 R.string.pip_settings_bounds));
Jaewan Kimc92a7d12016-02-15 17:33:25 -0800323 mMenuModePipBounds = Rect.unflattenFromString(res.getString(
Jaewan Kim8f584b82016-03-22 22:16:59 +0900324 R.string.pip_menu_bounds));
Youngsang Chof1647922015-12-17 13:39:39 -0800325
Jaewan Kim73ef3512016-07-18 13:50:33 +0900326 // Reset the PIP bounds and apply. PIP bounds can be changed by two reasons.
327 // 1. Configuration changed due to the language change (RTL <-> RTL)
328 // 2. SystemUI restarts after the crash
329 mPipBounds = isSettingsShown() ? mSettingsPipBounds : mDefaultPipBounds;
Jaewan Kime6309232017-04-14 15:26:20 +0900330 resizePinnedStack(getPinnedStackInfo() == null ? STATE_NO_PIP : STATE_PIP);
Youngsang Chof1647922015-12-17 13:39:39 -0800331 }
332
Jaewan Kimc552b042016-01-18 16:08:45 +0900333 /**
Jaewan Kim2d4d07c2016-05-20 10:28:17 +0900334 * Updates the PIP per configuration changed.
335 */
Winson Chung0c5a5922017-05-22 17:41:06 -0700336 public void onConfigurationChanged(Configuration newConfig) {
337 loadConfigurationsAndApply(newConfig);
Jaewan Kim26c63562017-04-26 15:41:43 +0900338 mPipNotification.onConfigurationChanged(mContext);
Jaewan Kim2d4d07c2016-05-20 10:28:17 +0900339 }
340
341 /**
Jaewan Kimf0fd2182016-04-20 21:17:58 +0900342 * Shows the picture-in-picture menu if an activity is in picture-in-picture mode.
Jaewan Kimc552b042016-01-18 16:08:45 +0900343 */
Winson Chungac52f282017-03-30 14:44:52 -0700344 public void showPictureInPictureMenu() {
Sergey Nikolaienkov53da4da2020-04-08 14:31:16 +0200345 if (DEBUG) Log.d(TAG, "showPictureInPictureMenu(), current state=" + getStateDescription());
346
Winson Chung0a657372017-05-12 15:04:54 -0700347 if (getState() == STATE_PIP) {
Wale Ogunwale480dca02016-02-06 13:58:29 -0800348 resizePinnedStack(STATE_PIP_MENU);
Jaewan Kimc552b042016-01-18 16:08:45 +0900349 }
350 }
351
Youngsang Chof1647922015-12-17 13:39:39 -0800352 /**
Jaewan Kim977dcdc2016-01-20 19:21:08 +0900353 * Closes PIP (PIPed activity and PIP system UI).
Youngsang Chof1647922015-12-17 13:39:39 -0800354 */
355 public void closePip() {
Sergey Nikolaienkov53da4da2020-04-08 14:31:16 +0200356 if (DEBUG) Log.d(TAG, "closePip(), current state=" + getStateDescription());
357
Youngsang Cho336007b2016-02-22 11:17:29 -0800358 closePipInternal(true);
Youngsang Cho23df6992016-01-26 17:51:33 -0800359 }
360
Youngsang Cho336007b2016-02-22 11:17:29 -0800361 private void closePipInternal(boolean removePipStack) {
Sergey Nikolaienkov53da4da2020-04-08 14:31:16 +0200362 if (DEBUG) {
363 Log.d(TAG,
364 "closePipInternal() removePipStack=" + removePipStack + ", current state="
365 + getStateDescription());
366 }
367
Youngsang Chof1647922015-12-17 13:39:39 -0800368 mState = STATE_NO_PIP;
Youngsang Choad8ceb02016-01-15 16:59:27 -0800369 mPipTaskId = TASK_ID_NO_PIP;
Jaewan Kim62338192016-02-25 10:00:05 -0800370 mPipMediaController = null;
371 mMediaSessionManager.removeOnActiveSessionsChangedListener(mActiveMediaSessionListener);
Youngsang Cho23df6992016-01-26 17:51:33 -0800372 if (removePipStack) {
373 try {
Wale Ogunwale04d9cb52018-04-30 13:55:07 -0700374 mActivityTaskManager.removeStack(mPinnedStackId);
Youngsang Cho23df6992016-01-26 17:51:33 -0800375 } catch (RemoteException e) {
376 Log.e(TAG, "removeStack failed", e);
Wale Ogunwale89be5762017-10-04 13:27:49 -0700377 } finally {
378 mPinnedStackId = INVALID_STACK_ID;
Youngsang Cho23df6992016-01-26 17:51:33 -0800379 }
Youngsang Chof1647922015-12-17 13:39:39 -0800380 }
Jaewan Kimc92a7d12016-02-15 17:33:25 -0800381 for (int i = mListeners.size() - 1; i >= 0; --i) {
382 mListeners.get(i).onPipActivityClosed();
383 }
Jaewan Kimf40fcdc2016-03-04 17:58:22 +0900384 mHandler.removeCallbacks(mClosePipRunnable);
Jaewan Kimf0fd2182016-04-20 21:17:58 +0900385 updatePipVisibility(false);
Youngsang Chof1647922015-12-17 13:39:39 -0800386 }
387
388 /**
Jaewan Kim977dcdc2016-01-20 19:21:08 +0900389 * Moves the PIPed activity to the fullscreen and closes PIP system UI.
Youngsang Chof1647922015-12-17 13:39:39 -0800390 */
Jaewan Kima0d4d252016-03-31 13:37:10 +0900391 void movePipToFullscreen() {
Sergey Nikolaienkov53da4da2020-04-08 14:31:16 +0200392 if (DEBUG) Log.d(TAG, "movePipToFullscreen(), current state=" + getStateDescription());
393
Youngsang Choad8ceb02016-01-15 16:59:27 -0800394 mPipTaskId = TASK_ID_NO_PIP;
Jaewan Kimb0033642016-04-22 18:41:37 +0900395 for (int i = mListeners.size() - 1; i >= 0; --i) {
396 mListeners.get(i).onMoveToFullscreen();
397 }
Winson Chungf04c1dd2017-02-24 16:47:37 -0800398 resizePinnedStack(STATE_NO_PIP);
Jaewan Kim47fe2862016-10-10 16:23:28 +0900399 updatePipVisibility(false);
Youngsang Chof1647922015-12-17 13:39:39 -0800400 }
401
402 /**
Wale Ogunwale480dca02016-02-06 13:58:29 -0800403 * Suspends resizing operation on the Pip until {@link #resumePipResizing} is called
404 * @param reason The reason for suspending resizing operations on the Pip.
405 */
406 public void suspendPipResizing(int reason) {
407 if (DEBUG) Log.d(TAG,
408 "suspendPipResizing() reason=" + reason + " callers=" + Debug.getCallers(2));
Sergey Nikolaienkov53da4da2020-04-08 14:31:16 +0200409
Wale Ogunwale480dca02016-02-06 13:58:29 -0800410 mSuspendPipResizingReason |= reason;
411 }
412
413 /**
414 * Resumes resizing operation on the Pip that was previously suspended.
415 * @param reason The reason resizing operations on the Pip was suspended.
416 */
417 public void resumePipResizing(int reason) {
418 if ((mSuspendPipResizingReason & reason) == 0) {
419 return;
420 }
421 if (DEBUG) Log.d(TAG,
422 "resumePipResizing() reason=" + reason + " callers=" + Debug.getCallers(2));
423 mSuspendPipResizingReason &= ~reason;
424 mHandler.post(mResizePinnedStackRunnable);
425 }
426
427 /**
428 * Resize the Pip to the appropriate size for the input state.
429 * @param state In Pip state also used to determine the new size for the Pip.
430 */
Jaewan Kima0d4d252016-03-31 13:37:10 +0900431 void resizePinnedStack(int state) {
Sergey Nikolaienkov53da4da2020-04-08 14:31:16 +0200432 if (DEBUG) {
433 Log.d(TAG, "resizePinnedStack() state=" + stateToName(state) + ", current state="
434 + getStateDescription(), new Exception());
435 }
436
Winson Chungf04c1dd2017-02-24 16:47:37 -0800437 boolean wasStateNoPip = (mState == STATE_NO_PIP);
Wale Ogunwale480dca02016-02-06 13:58:29 -0800438 for (int i = mListeners.size() - 1; i >= 0; --i) {
439 mListeners.get(i).onPipResizeAboutToStart();
440 }
Wale Ogunwale480dca02016-02-06 13:58:29 -0800441 if (mSuspendPipResizingReason != 0) {
Winson Chung0a657372017-05-12 15:04:54 -0700442 mResumeResizePinnedStackRunnableState = state;
Winson Chungd62acec2017-03-31 19:42:32 -0700443 if (DEBUG) Log.d(TAG, "resizePinnedStack() deferring"
444 + " mSuspendPipResizingReason=" + mSuspendPipResizingReason
Winson Chung0a657372017-05-12 15:04:54 -0700445 + " mResumeResizePinnedStackRunnableState="
Sergey Nikolaienkov53da4da2020-04-08 14:31:16 +0200446 + stateToName(mResumeResizePinnedStackRunnableState));
Wale Ogunwale480dca02016-02-06 13:58:29 -0800447 return;
448 }
Winson Chungd62acec2017-03-31 19:42:32 -0700449 mState = state;
Jaewan Kimc92a7d12016-02-15 17:33:25 -0800450 switch (mState) {
451 case STATE_NO_PIP:
452 mCurrentPipBounds = null;
Winson Chungf04c1dd2017-02-24 16:47:37 -0800453 // If the state was already STATE_NO_PIP, then do not resize the stack below as it
454 // will not exist
455 if (wasStateNoPip) {
456 return;
457 }
Jaewan Kimc92a7d12016-02-15 17:33:25 -0800458 break;
459 case STATE_PIP_MENU:
460 mCurrentPipBounds = mMenuModePipBounds;
461 break;
Hongwei Wang85cf41f2020-01-15 15:14:47 -0800462 case STATE_PIP: // fallthrough
Jaewan Kimc92a7d12016-02-15 17:33:25 -0800463 default:
464 mCurrentPipBounds = mPipBounds;
465 break;
466 }
Sergey Nikolaienkovfe0723f2020-04-13 09:14:23 +0200467 if (mCurrentPipBounds != null) {
468 mPipTaskOrganizer.scheduleAnimateResizePip(mCurrentPipBounds, mResizeAnimationDuration,
469 null);
470 } else {
471 mPipTaskOrganizer.dismissPip(mResizeAnimationDuration);
472 }
Wale Ogunwale480dca02016-02-06 13:58:29 -0800473 }
474
475 /**
Winson Chung0a657372017-05-12 15:04:54 -0700476 * @return the current state, or the pending state if the state change was previously suspended.
477 */
478 private int getState() {
479 if (mSuspendPipResizingReason != 0) {
480 return mResumeResizePinnedStackRunnableState;
481 }
482 return mState;
483 }
484
485 /**
Youngsang Chof1647922015-12-17 13:39:39 -0800486 * Shows PIP menu UI by launching {@link PipMenuActivity}. It also locates the pinned
Jaewan Kim8f584b82016-03-22 22:16:59 +0900487 * stack to the centered PIP bound {@link R.config_centeredPictureInPictureBounds}.
Youngsang Chof1647922015-12-17 13:39:39 -0800488 */
Wale Ogunwale480dca02016-02-06 13:58:29 -0800489 private void showPipMenu() {
Sergey Nikolaienkov53da4da2020-04-08 14:31:16 +0200490 if (DEBUG) Log.d(TAG, "showPipMenu(), current state=" + getStateDescription());
491
Youngsang Chof1647922015-12-17 13:39:39 -0800492 mState = STATE_PIP_MENU;
493 for (int i = mListeners.size() - 1; i >= 0; --i) {
494 mListeners.get(i).onShowPipMenu();
495 }
496 Intent intent = new Intent(mContext, PipMenuActivity.class);
497 intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
Winson Chungb6de8722017-06-02 12:45:51 -0700498 intent.putExtra(PipMenuActivity.EXTRA_CUSTOM_ACTIONS, mCustomActions);
Jaewan Kim1a9dc562016-02-17 13:41:51 -0800499 mContext.startActivity(intent);
Youngsang Chof1647922015-12-17 13:39:39 -0800500 }
501
Jaewan Kima0d4d252016-03-31 13:37:10 +0900502 /**
503 * Adds a {@link Listener} to PipManager.
504 */
Youngsang Chof1647922015-12-17 13:39:39 -0800505 public void addListener(Listener listener) {
506 mListeners.add(listener);
507 }
508
Jaewan Kima0d4d252016-03-31 13:37:10 +0900509 /**
510 * Removes a {@link Listener} from PipManager.
511 */
Youngsang Chof1647922015-12-17 13:39:39 -0800512 public void removeListener(Listener listener) {
513 mListeners.remove(listener);
514 }
515
Jaewan Kima0d4d252016-03-31 13:37:10 +0900516 /**
517 * Adds a {@link MediaListener} to PipManager.
518 */
519 public void addMediaListener(MediaListener listener) {
520 mMediaListeners.add(listener);
521 }
522
523 /**
524 * Removes a {@link MediaListener} from PipManager.
525 */
526 public void removeMediaListener(MediaListener listener) {
527 mMediaListeners.remove(listener);
528 }
529
Jaewan Kimc92a7d12016-02-15 17:33:25 -0800530 /**
531 * Returns {@code true} if PIP is shown.
532 */
533 public boolean isPipShown() {
Jaewan Kim82ac50d2016-03-21 17:34:28 +0900534 return mState != STATE_NO_PIP;
Youngsang Chof1647922015-12-17 13:39:39 -0800535 }
536
Jaewan Kim73ef3512016-07-18 13:50:33 +0900537 private StackInfo getPinnedStackInfo() {
538 StackInfo stackInfo = null;
539 try {
Wale Ogunwale04d9cb52018-04-30 13:55:07 -0700540 stackInfo = ActivityTaskManager.getService().getStackInfo(
Wale Ogunwale68278562017-09-23 17:13:55 -0700541 WINDOWING_MODE_PINNED, ACTIVITY_TYPE_UNDEFINED);
Jaewan Kim73ef3512016-07-18 13:50:33 +0900542 } catch (RemoteException e) {
543 Log.e(TAG, "getStackInfo failed", e);
544 }
545 return stackInfo;
546 }
547
Youngsang Choad8ceb02016-01-15 16:59:27 -0800548 private void handleMediaResourceGranted(String[] packageNames) {
Winson Chung0a657372017-05-12 15:04:54 -0700549 if (getState() == STATE_NO_PIP) {
Dongwon Kang353d8d72016-04-13 21:55:22 -0700550 mLastPackagesResourceGranted = packageNames;
551 } else {
552 boolean requestedFromLastPackages = false;
553 if (mLastPackagesResourceGranted != null) {
554 for (String packageName : mLastPackagesResourceGranted) {
555 for (String newPackageName : packageNames) {
556 if (TextUtils.equals(newPackageName, packageName)) {
557 requestedFromLastPackages = true;
558 break;
559 }
Youngsang Choad8ceb02016-01-15 16:59:27 -0800560 }
561 }
562 }
Dongwon Kang353d8d72016-04-13 21:55:22 -0700563 mLastPackagesResourceGranted = packageNames;
564 if (!requestedFromLastPackages) {
565 closePip();
566 }
Youngsang Choad8ceb02016-01-15 16:59:27 -0800567 }
568 }
569
Jaewan Kim62338192016-02-25 10:00:05 -0800570 private void updateMediaController(List<MediaController> controllers) {
571 MediaController mediaController = null;
Winson Chung0a657372017-05-12 15:04:54 -0700572 if (controllers != null && getState() != STATE_NO_PIP && mPipComponentName != null) {
Jaewan Kim62338192016-02-25 10:00:05 -0800573 for (int i = controllers.size() - 1; i >= 0; i--) {
574 MediaController controller = controllers.get(i);
575 // We assumes that an app with PIPable activity
576 // keeps the single instance of media controller especially when PIP is on.
577 if (controller.getPackageName().equals(mPipComponentName.getPackageName())) {
578 mediaController = controller;
579 break;
580 }
581 }
582 }
583 if (mPipMediaController != mediaController) {
584 mPipMediaController = mediaController;
Jaewan Kima0d4d252016-03-31 13:37:10 +0900585 for (int i = mMediaListeners.size() - 1; i >= 0; i--) {
586 mMediaListeners.get(i).onMediaControllerChanged();
Jaewan Kim62338192016-02-25 10:00:05 -0800587 }
Jaewan Kimf40fcdc2016-03-04 17:58:22 +0900588 if (mPipMediaController == null) {
589 mHandler.postDelayed(mClosePipRunnable,
590 CLOSE_PIP_WHEN_MEDIA_SESSION_GONE_TIMEOUT_MS);
591 } else {
592 mHandler.removeCallbacks(mClosePipRunnable);
593 }
Jaewan Kim62338192016-02-25 10:00:05 -0800594 }
595 }
596
597 /**
598 * Gets the {@link android.media.session.MediaController} for the PIPed activity.
599 */
600 MediaController getMediaController() {
601 return mPipMediaController;
602 }
603
Jaewan Kim8f584b82016-03-22 22:16:59 +0900604 /**
605 * Returns the PIPed activity's playback state.
Jaewan Kim26c63562017-04-26 15:41:43 +0900606 * This returns one of {@link #PLAYBACK_STATE_PLAYING}, {@link #PLAYBACK_STATE_PAUSED},
607 * or {@link #PLAYBACK_STATE_UNAVAILABLE}.
Jaewan Kim8f584b82016-03-22 22:16:59 +0900608 */
609 int getPlaybackState() {
610 if (mPipMediaController == null || mPipMediaController.getPlaybackState() == null) {
611 return PLAYBACK_STATE_UNAVAILABLE;
612 }
613 int state = mPipMediaController.getPlaybackState().getState();
614 boolean isPlaying = (state == PlaybackState.STATE_BUFFERING
615 || state == PlaybackState.STATE_CONNECTING
616 || state == PlaybackState.STATE_PLAYING
617 || state == PlaybackState.STATE_FAST_FORWARDING
618 || state == PlaybackState.STATE_REWINDING
619 || state == PlaybackState.STATE_SKIPPING_TO_PREVIOUS
620 || state == PlaybackState.STATE_SKIPPING_TO_NEXT);
621 long actions = mPipMediaController.getPlaybackState().getActions();
622 if (!isPlaying && ((actions & PlaybackState.ACTION_PLAY) != 0)) {
623 return PLAYBACK_STATE_PAUSED;
624 } else if (isPlaying && ((actions & PlaybackState.ACTION_PAUSE) != 0)) {
625 return PLAYBACK_STATE_PLAYING;
626 }
627 return PLAYBACK_STATE_UNAVAILABLE;
628 }
629
Jaewan Kim73ef3512016-07-18 13:50:33 +0900630 private boolean isSettingsShown() {
631 List<RunningTaskInfo> runningTasks;
632 try {
Wale Ogunwale04d9cb52018-04-30 13:55:07 -0700633 runningTasks = mActivityTaskManager.getTasks(1);
Winson Chung61c9e5a2017-10-11 10:39:32 -0700634 if (runningTasks.isEmpty()) {
Jaewan Kim73ef3512016-07-18 13:50:33 +0900635 return false;
636 }
637 } catch (RemoteException e) {
638 Log.d(TAG, "Failed to detect top activity", e);
639 return false;
640 }
641 ComponentName topActivity = runningTasks.get(0).topActivity;
Jaewan Kimd89a6942016-04-08 11:41:45 +0900642 for (Pair<String, String> componentName : sSettingsPackageAndClassNamePairList) {
643 String packageName = componentName.first;
Andrii Kulian8290f8f2016-06-30 17:51:32 -0700644 if (topActivity.getPackageName().equals(packageName)) {
Jaewan Kimd89a6942016-04-08 11:41:45 +0900645 String className = componentName.second;
646 if (className == null || topActivity.getClassName().equals(className)) {
647 return true;
648 }
649 }
650 }
651 return false;
652 }
653
Winson Chung67f5c8b2018-09-24 12:09:19 -0700654 private TaskStackChangeListener mTaskStackListener = new TaskStackChangeListener() {
Youngsang Chof1647922015-12-17 13:39:39 -0800655 @Override
Jaewan Kim938a50b2016-03-14 17:35:43 +0900656 public void onTaskStackChanged() {
Jaewan Kim17ca4e32016-10-31 13:55:43 +0900657 if (DEBUG) Log.d(TAG, "onTaskStackChanged()");
Shuichi.Noguchie9d2dd62017-11-22 17:24:17 +0900658
Winson Chung0a657372017-05-12 15:04:54 -0700659 if (getState() != STATE_NO_PIP) {
Jaewan Kimd89a6942016-04-08 11:41:45 +0900660 boolean hasPip = false;
661
Jaewan Kim73ef3512016-07-18 13:50:33 +0900662 StackInfo stackInfo = getPinnedStackInfo();
663 if (stackInfo == null || stackInfo.taskIds == null) {
664 Log.w(TAG, "There is nothing in pinned stack");
665 closePipInternal(false);
Jaewan Kim938a50b2016-03-14 17:35:43 +0900666 return;
667 }
668 for (int i = stackInfo.taskIds.length - 1; i >= 0; --i) {
669 if (stackInfo.taskIds[i] == mPipTaskId) {
670 // PIP task is still alive.
Jaewan Kimd89a6942016-04-08 11:41:45 +0900671 hasPip = true;
672 break;
Jaewan Kim938a50b2016-03-14 17:35:43 +0900673 }
674 }
Jaewan Kimd89a6942016-04-08 11:41:45 +0900675 if (!hasPip) {
676 // PIP task doesn't exist anymore in PINNED_STACK.
677 closePipInternal(true);
678 return;
679 }
680 }
Winson Chung0a657372017-05-12 15:04:54 -0700681 if (getState() == STATE_PIP) {
Jaewan Kim73ef3512016-07-18 13:50:33 +0900682 Rect bounds = isSettingsShown() ? mSettingsPipBounds : mDefaultPipBounds;
683 if (mPipBounds != bounds) {
684 mPipBounds = bounds;
Jaewan Kime6309232017-04-14 15:26:20 +0900685 resizePinnedStack(STATE_PIP);
Jaewan Kimd89a6942016-04-08 11:41:45 +0900686 }
Jaewan Kim938a50b2016-03-14 17:35:43 +0900687 }
Youngsang Chof1647922015-12-17 13:39:39 -0800688 }
689
690 @Override
Wale Ogunwale89be5762017-10-04 13:27:49 -0700691 public void onActivityPinned(String packageName, int userId, int taskId, int stackId) {
Wale Ogunwale480dca02016-02-06 13:58:29 -0800692 if (DEBUG) Log.d(TAG, "onActivityPinned()");
Shuichi.Noguchie9d2dd62017-11-22 17:24:17 +0900693
Jaewan Kim73ef3512016-07-18 13:50:33 +0900694 StackInfo stackInfo = getPinnedStackInfo();
695 if (stackInfo == null) {
696 Log.w(TAG, "Cannot find pinned stack");
Jaewan Kim938a50b2016-03-14 17:35:43 +0900697 return;
698 }
699 if (DEBUG) Log.d(TAG, "PINNED_STACK:" + stackInfo);
Wale Ogunwale89be5762017-10-04 13:27:49 -0700700 mPinnedStackId = stackInfo.stackId;
Jaewan Kim938a50b2016-03-14 17:35:43 +0900701 mPipTaskId = stackInfo.taskIds[stackInfo.taskIds.length - 1];
702 mPipComponentName = ComponentName.unflattenFromString(
703 stackInfo.taskNames[stackInfo.taskNames.length - 1]);
Jaewan Kime6309232017-04-14 15:26:20 +0900704 // Set state to STATE_PIP so we show it when the pinned stack animation ends.
705 mState = STATE_PIP;
Jaewan Kim938a50b2016-03-14 17:35:43 +0900706 mCurrentPipBounds = mPipBounds;
Jaewan Kim938a50b2016-03-14 17:35:43 +0900707 mMediaSessionManager.addOnActiveSessionsChangedListener(
708 mActiveMediaSessionListener, null);
709 updateMediaController(mMediaSessionManager.getActiveSessions(null));
Jaewan Kim938a50b2016-03-14 17:35:43 +0900710 for (int i = mListeners.size() - 1; i >= 0; i--) {
Sergey Nikolaienkov00861da2020-04-27 10:56:26 +0200711 mListeners.get(i).onPipEntered(packageName);
Jaewan Kim938a50b2016-03-14 17:35:43 +0900712 }
Jaewan Kimf0fd2182016-04-20 21:17:58 +0900713 updatePipVisibility(true);
Youngsang Chof1647922015-12-17 13:39:39 -0800714 }
Wale Ogunwalecc25a8a2016-01-23 14:31:37 -0800715
716 @Override
Winson Chunge789ff62020-02-24 14:40:23 -0800717 public void onActivityRestartAttempt(RunningTaskInfo task, boolean homeTaskVisible,
Evan Rosky8d1c24e2020-04-23 09:21:16 -0700718 boolean clearedTask, boolean wasVisible) {
719 if (!wasVisible || task.configuration.windowConfiguration.getWindowingMode()
Winson Chunge789ff62020-02-24 14:40:23 -0800720 != WINDOWING_MODE_PINNED) {
721 return;
722 }
Wale Ogunwale480dca02016-02-06 13:58:29 -0800723 if (DEBUG) Log.d(TAG, "onPinnedActivityRestartAttempt()");
Shuichi.Noguchie9d2dd62017-11-22 17:24:17 +0900724
Jaewan Kim938a50b2016-03-14 17:35:43 +0900725 // If PIPed activity is launched again by Launcher or intent, make it fullscreen.
726 movePipToFullscreen();
Wale Ogunwalecc25a8a2016-01-23 14:31:37 -0800727 }
Jaewan Kim938a50b2016-03-14 17:35:43 +0900728 };
Youngsang Chof1647922015-12-17 13:39:39 -0800729
Hongwei Wang85cf41f2020-01-15 15:14:47 -0800730 @Override
Hongwei Wang221fe3d2020-03-26 13:13:04 -0700731 public void onPipTransitionStarted(ComponentName activity, int direction) { }
Hongwei Wang85cf41f2020-01-15 15:14:47 -0800732
733 @Override
Hongwei Wang221fe3d2020-03-26 13:13:04 -0700734 public void onPipTransitionFinished(ComponentName activity, int direction) {
Hongwei Wang85cf41f2020-01-15 15:14:47 -0800735 onPipTransitionFinishedOrCanceled();
736 }
737
738 @Override
Hongwei Wang221fe3d2020-03-26 13:13:04 -0700739 public void onPipTransitionCanceled(ComponentName activity, int direction) {
Hongwei Wang85cf41f2020-01-15 15:14:47 -0800740 onPipTransitionFinishedOrCanceled();
741 }
742
743 private void onPipTransitionFinishedOrCanceled() {
744 if (DEBUG) Log.d(TAG, "onPipTransitionFinishedOrCanceled()");
Sergey Nikolaienkov53da4da2020-04-08 14:31:16 +0200745
Hongwei Wang85cf41f2020-01-15 15:14:47 -0800746 if (getState() == STATE_PIP_MENU) {
747 showPipMenu();
748 }
749 }
750
Youngsang Chof1647922015-12-17 13:39:39 -0800751 /**
752 * A listener interface to receive notification on changes in PIP.
753 */
754 public interface Listener {
Jaewan Kim82ac50d2016-03-21 17:34:28 +0900755 /**
756 * Invoked when an activity is pinned and PIP manager is set corresponding information.
757 * Classes must use this instead of {@link android.app.ITaskStackListener.onActivityPinned}
758 * because there's no guarantee for the PIP manager be return relavent information
Jaewan Kima0d4d252016-03-31 13:37:10 +0900759 * correctly. (e.g. {@link isPipShown}).
Jaewan Kim82ac50d2016-03-21 17:34:28 +0900760 */
Sergey Nikolaienkov00861da2020-04-27 10:56:26 +0200761 void onPipEntered(String packageName);
Wale Ogunwale480dca02016-02-06 13:58:29 -0800762 /** Invoked when a PIPed activity is closed. */
Youngsang Chof1647922015-12-17 13:39:39 -0800763 void onPipActivityClosed();
Wale Ogunwale480dca02016-02-06 13:58:29 -0800764 /** Invoked when the PIP menu gets shown. */
Youngsang Chof1647922015-12-17 13:39:39 -0800765 void onShowPipMenu();
Winson Chungb6de8722017-06-02 12:45:51 -0700766 /** Invoked when the PIP menu actions change. */
767 void onPipMenuActionsChanged(ParceledListSlice actions);
Jaewan Kim10a86912016-04-04 16:01:51 +0900768 /** Invoked when the PIPed activity is about to return back to the fullscreen. */
Youngsang Chof1647922015-12-17 13:39:39 -0800769 void onMoveToFullscreen();
Wale Ogunwale480dca02016-02-06 13:58:29 -0800770 /** Invoked when we are above to start resizing the Pip. */
771 void onPipResizeAboutToStart();
Jaewan Kima0d4d252016-03-31 13:37:10 +0900772 }
773
774 /**
775 * A listener interface to receive change in PIP's media controller
776 */
777 public interface MediaListener {
Jaewan Kim62338192016-02-25 10:00:05 -0800778 /** Invoked when the MediaController on PIPed activity is changed. */
779 void onMediaControllerChanged();
Youngsang Chof1647922015-12-17 13:39:39 -0800780 }
781
Winsonab216602016-08-09 14:05:20 -0700782 private void updatePipVisibility(final boolean visible) {
Dave Mankoffc7cf9fc2019-12-19 15:43:20 -0500783 Dependency.get(UiOffloadThread.class).execute(() -> {
Winson Chung67f5c8b2018-09-24 12:09:19 -0700784 WindowManagerWrapper.getInstance().setPipVisibility(visible);
785 });
Jaewan Kimf0fd2182016-04-20 21:17:58 +0900786 }
Sergey Nikolaienkov53da4da2020-04-08 14:31:16 +0200787
788 private String getStateDescription() {
789 if (mSuspendPipResizingReason == 0) {
790 return stateToName(mState);
791 }
792 return stateToName(mResumeResizePinnedStackRunnableState) + " (while " + stateToName(mState)
793 + " is suspended)";
794 }
795
796 private static String stateToName(int state) {
797 switch (state) {
798 case STATE_NO_PIP:
799 return "NO_PIP";
800
801 case STATE_PIP:
802 return "PIP";
803
804 case STATE_PIP_MENU:
805 return "PIP_MENU";
806
807 default:
808 return "UNKNOWN(" + state + ")";
809 }
810 }
Youngsang Chof1647922015-12-17 13:39:39 -0800811}