blob: 0925638ad8cc165daf8ed7dd684b784b02e10832 [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
17package com.android.systemui.tv.pip;
18
Winsonc0d70582016-01-29 10:24:39 -080019import android.app.ActivityManager.RunningTaskInfo;
Youngsang Chof1647922015-12-17 13:39:39 -080020import android.app.ActivityManager.StackInfo;
21import android.app.ActivityManagerNative;
22import android.app.ActivityOptions;
23import android.app.IActivityManager;
24import android.app.ITaskStackListener;
25import android.content.BroadcastReceiver;
26import android.content.Context;
27import android.content.Intent;
28import android.content.IntentFilter;
29import android.content.res.Resources;
30import android.graphics.Rect;
Wale Ogunwale480dca02016-02-06 13:58:29 -080031import android.os.Debug;
Youngsang Chof1647922015-12-17 13:39:39 -080032import android.os.Handler;
33import android.os.RemoteException;
34import android.util.Log;
35
Jaewan Kim977dcdc2016-01-20 19:21:08 +090036import com.android.systemui.Prefs;
37
Youngsang Chof1647922015-12-17 13:39:39 -080038import java.util.ArrayList;
39import java.util.List;
40
41import static android.app.ActivityManager.StackId.FULLSCREEN_WORKSPACE_STACK_ID;
42import static android.app.ActivityManager.StackId.PINNED_STACK_ID;
Jaewan Kim977dcdc2016-01-20 19:21:08 +090043import static com.android.systemui.Prefs.Key.TV_PICTURE_IN_PICTURE_ONBOARDING_SHOWN;
Youngsang Chof1647922015-12-17 13:39:39 -080044
45/**
46 * Manages the picture-in-picture (PIP) UI and states.
47 */
48public class PipManager {
49 private static final String TAG = "PipManager";
50 private static final boolean DEBUG = false;
Jaewan Kim977dcdc2016-01-20 19:21:08 +090051 private static final boolean DEBUG_FORCE_ONBOARDING = false;
Youngsang Chof1647922015-12-17 13:39:39 -080052
53 private static PipManager sPipManager;
54
Youngsang Choad8ceb02016-01-15 16:59:27 -080055 private static final int MAX_RUNNING_TASKS_COUNT = 10;
56
Wale Ogunwale480dca02016-02-06 13:58:29 -080057 public static final int STATE_NO_PIP = 0;
58 public static final int STATE_PIP_OVERLAY = 1;
59 public static final int STATE_PIP_MENU = 2;
Youngsang Chof1647922015-12-17 13:39:39 -080060
Youngsang Choad8ceb02016-01-15 16:59:27 -080061 private static final int TASK_ID_NO_PIP = -1;
62 private static final int INVALID_RESOURCE_TYPE = -1;
63
Wale Ogunwale480dca02016-02-06 13:58:29 -080064 public static final int SUSPEND_PIP_RESIZE_REASON_WAITING_FOR_MENU_ACTIVITY_FINISH = 0x1;
65 public static final int SUSPEND_PIP_RESIZE_REASON_WAITING_FOR_OVERLAY_ACTIVITY_FINISH = 0x2;
66 private int mSuspendPipResizingReason;
67
Jaewan Kimc92a7d12016-02-15 17:33:25 -080068 private static final float SCALE_FACTOR = 1.1f;
69
Youngsang Chof1647922015-12-17 13:39:39 -080070 private Context mContext;
71 private IActivityManager mActivityManager;
72 private int mState = STATE_NO_PIP;
73 private final Handler mHandler = new Handler();
74 private List<Listener> mListeners = new ArrayList<>();
Jaewan Kimc92a7d12016-02-15 17:33:25 -080075 private Rect mCurrentPipBounds;
76 private Rect mPipBounds;
77 private Rect mMenuModePipBounds;
78 private Rect mRecentsPipBounds;
79 private Rect mRecentsFocusedPipBounds;
Youngsang Chof1647922015-12-17 13:39:39 -080080 private boolean mInitialized;
Youngsang Choad8ceb02016-01-15 16:59:27 -080081 private int mPipTaskId = TASK_ID_NO_PIP;
Jaewan Kim977dcdc2016-01-20 19:21:08 +090082 private boolean mOnboardingShown;
Youngsang Choad8ceb02016-01-15 16:59:27 -080083
Jaewan Kimc92a7d12016-02-15 17:33:25 -080084 private boolean mIsRecentsShown;
85 private boolean mIsPipFocusedInRecent;
86
Youngsang Chof1647922015-12-17 13:39:39 -080087 private final Runnable mOnActivityPinnedRunnable = new Runnable() {
88 @Override
89 public void run() {
90 StackInfo stackInfo = null;
91 try {
92 stackInfo = mActivityManager.getStackInfo(PINNED_STACK_ID);
93 if (stackInfo == null) {
Jaewan Kimc92a7d12016-02-15 17:33:25 -080094 Log.w(TAG, "Cannot find pinned stack");
Youngsang Chof1647922015-12-17 13:39:39 -080095 return;
96 }
97 } catch (RemoteException e) {
98 Log.e(TAG, "getStackInfo failed", e);
99 return;
100 }
101 if (DEBUG) Log.d(TAG, "PINNED_STACK:" + stackInfo);
Youngsang Choad8ceb02016-01-15 16:59:27 -0800102 mPipTaskId = stackInfo.taskIds[stackInfo.taskIds.length - 1];
Wale Ogunwale480dca02016-02-06 13:58:29 -0800103 // Set state to overlay so we show it when the pinned stack animation ends.
104 mState = STATE_PIP_OVERLAY;
Jaewan Kimc92a7d12016-02-15 17:33:25 -0800105 mCurrentPipBounds = mPipBounds;
Jaewan Kim977dcdc2016-01-20 19:21:08 +0900106 launchPipOnboardingActivityIfNeeded();
Youngsang Chof1647922015-12-17 13:39:39 -0800107 }
108 };
109 private final Runnable mOnTaskStackChanged = new Runnable() {
110 @Override
111 public void run() {
112 if (mState != STATE_NO_PIP) {
Youngsang Cho23df6992016-01-26 17:51:33 -0800113 StackInfo stackInfo = null;
114 try {
115 stackInfo = mActivityManager.getStackInfo(PINNED_STACK_ID);
116 if (stackInfo == null) {
117 Log.w(TAG, "There is no pinned stack");
Youngsang Cho336007b2016-02-22 11:17:29 -0800118 closePipInternal(false);
Youngsang Cho23df6992016-01-26 17:51:33 -0800119 return;
120 }
121 } catch (RemoteException e) {
122 Log.e(TAG, "getStackInfo failed", e);
123 return;
124 }
125 for (int i = stackInfo.taskIds.length - 1; i >= 0; --i) {
126 if (stackInfo.taskIds[i] == mPipTaskId) {
127 // PIP task is still alive.
128 return;
129 }
130 }
131 // PIP task doesn't exist anymore in PINNED_STACK.
Youngsang Cho336007b2016-02-22 11:17:29 -0800132 closePipInternal(true);
Youngsang Chof1647922015-12-17 13:39:39 -0800133 }
134 }
135 };
Youngsang Cho6a00b702016-01-25 15:48:41 -0800136 private final Runnable mOnPinnedActivityRestartAttempt = new Runnable() {
137 @Override
138 public void run() {
139 movePipToFullscreen();
140 }
141 };
Wale Ogunwale480dca02016-02-06 13:58:29 -0800142 private final Runnable mOnPinnedStackAnimationEnded = new Runnable() {
143 @Override
144 public void run() {
Jaewan Kimc92a7d12016-02-15 17:33:25 -0800145 switch (mState) {
146 case STATE_PIP_OVERLAY:
147 showPipOverlay();
148 break;
149 case STATE_PIP_MENU:
150 showPipMenu();
151 break;
Wale Ogunwale480dca02016-02-06 13:58:29 -0800152 }
153 }
154 };
155
156 private final Runnable mResizePinnedStackRunnable = new Runnable() {
157 @Override
158 public void run() {
159 resizePinnedStack(mState);
160 }
161 };
Youngsang Chof1647922015-12-17 13:39:39 -0800162
Youngsang Choad8ceb02016-01-15 16:59:27 -0800163 private final BroadcastReceiver mBroadcastReceiver = new BroadcastReceiver() {
Youngsang Chof1647922015-12-17 13:39:39 -0800164 @Override
165 public void onReceive(Context context, Intent intent) {
Youngsang Choad8ceb02016-01-15 16:59:27 -0800166 String action = intent.getAction();
Jaewan Kimc552b042016-01-18 16:08:45 +0900167 if (Intent.ACTION_MEDIA_RESOURCE_GRANTED.equals(action)) {
Youngsang Choad8ceb02016-01-15 16:59:27 -0800168 String[] packageNames = intent.getStringArrayExtra(Intent.EXTRA_PACKAGES);
169 int resourceType = intent.getIntExtra(Intent.EXTRA_MEDIA_RESOURCE_TYPE,
170 INVALID_RESOURCE_TYPE);
171 if (mState != STATE_NO_PIP && packageNames != null && packageNames.length > 0
172 && resourceType == Intent.EXTRA_MEDIA_RESOURCE_TYPE_VIDEO_CODEC) {
173 handleMediaResourceGranted(packageNames);
174 }
Youngsang Chof1647922015-12-17 13:39:39 -0800175 }
Youngsang Choad8ceb02016-01-15 16:59:27 -0800176
Youngsang Chof1647922015-12-17 13:39:39 -0800177 }
178 };
179
180 private PipManager() { }
181
182 /**
183 * Initializes {@link PipManager}.
184 */
185 public void initialize(Context context) {
186 if (mInitialized) {
187 return;
188 }
189 mInitialized = true;
190 mContext = context;
191 Resources res = context.getResources();
Jaewan Kimc92a7d12016-02-15 17:33:25 -0800192 mPipBounds = Rect.unflattenFromString(res.getString(
Youngsang Chof1647922015-12-17 13:39:39 -0800193 com.android.internal.R.string.config_defaultPictureInPictureBounds));
Jaewan Kimc92a7d12016-02-15 17:33:25 -0800194 mMenuModePipBounds = Rect.unflattenFromString(res.getString(
Youngsang Chof1647922015-12-17 13:39:39 -0800195 com.android.internal.R.string.config_centeredPictureInPictureBounds));
Jaewan Kimc92a7d12016-02-15 17:33:25 -0800196 mRecentsPipBounds = Rect.unflattenFromString(res.getString(
197 com.android.internal.R.string.config_pictureInPictureBoundsInRecents));
198 float scaleBy = (SCALE_FACTOR - 1.0f) / 2;
199 mRecentsFocusedPipBounds = new Rect(
200 (int) (mRecentsPipBounds.left - scaleBy * mRecentsPipBounds.width()),
201 (int) (mRecentsPipBounds.top - scaleBy * mRecentsPipBounds.height()),
202 (int) (mRecentsPipBounds.right + scaleBy * mRecentsPipBounds.width()),
203 (int) (mRecentsPipBounds.bottom + scaleBy * mRecentsPipBounds.height()));
Youngsang Chof1647922015-12-17 13:39:39 -0800204
205 mActivityManager = ActivityManagerNative.getDefault();
206 TaskStackListener taskStackListener = new TaskStackListener();
207 IActivityManager iam = ActivityManagerNative.getDefault();
208 try {
209 iam.registerTaskStackListener(taskStackListener);
210 } catch (RemoteException e) {
211 Log.e(TAG, "registerTaskStackListener failed", e);
212 }
213 IntentFilter intentFilter = new IntentFilter();
Youngsang Choad8ceb02016-01-15 16:59:27 -0800214 intentFilter.addAction(Intent.ACTION_MEDIA_RESOURCE_GRANTED);
215 mContext.registerReceiver(mBroadcastReceiver, intentFilter);
Jaewan Kim977dcdc2016-01-20 19:21:08 +0900216 mOnboardingShown = Prefs.getBoolean(
217 mContext, TV_PICTURE_IN_PICTURE_ONBOARDING_SHOWN, false);
Youngsang Chof1647922015-12-17 13:39:39 -0800218 }
219
Jaewan Kimc552b042016-01-18 16:08:45 +0900220 /**
221 * Request PIP.
222 * It could either start PIP if there's none, and show PIP menu otherwise.
223 */
224 public void requestTvPictureInPicture() {
225 if (DEBUG) Log.d(TAG, "requestTvPictureInPicture()");
Jaewan Kimc92a7d12016-02-15 17:33:25 -0800226 if (!isPipShown()) {
Jaewan Kimc552b042016-01-18 16:08:45 +0900227 startPip();
228 } else if (mState == STATE_PIP_OVERLAY) {
Wale Ogunwale480dca02016-02-06 13:58:29 -0800229 resizePinnedStack(STATE_PIP_MENU);
Jaewan Kimc552b042016-01-18 16:08:45 +0900230 }
231 }
232
Youngsang Chof1647922015-12-17 13:39:39 -0800233 private void startPip() {
234 try {
Jaewan Kimc92a7d12016-02-15 17:33:25 -0800235 mActivityManager.moveTopActivityToPinnedStack(FULLSCREEN_WORKSPACE_STACK_ID, mPipBounds);
Youngsang Chof1647922015-12-17 13:39:39 -0800236 } catch (RemoteException|IllegalArgumentException e) {
237 Log.e(TAG, "moveTopActivityToPinnedStack failed", e);
238 }
Youngsang Chof1647922015-12-17 13:39:39 -0800239 }
240
241 /**
Jaewan Kim977dcdc2016-01-20 19:21:08 +0900242 * Closes PIP (PIPed activity and PIP system UI).
Youngsang Chof1647922015-12-17 13:39:39 -0800243 */
244 public void closePip() {
Youngsang Cho336007b2016-02-22 11:17:29 -0800245 closePipInternal(true);
Youngsang Cho23df6992016-01-26 17:51:33 -0800246 }
247
Youngsang Cho336007b2016-02-22 11:17:29 -0800248 private void closePipInternal(boolean removePipStack) {
Youngsang Chof1647922015-12-17 13:39:39 -0800249 mState = STATE_NO_PIP;
Youngsang Choad8ceb02016-01-15 16:59:27 -0800250 mPipTaskId = TASK_ID_NO_PIP;
Youngsang Cho23df6992016-01-26 17:51:33 -0800251 if (removePipStack) {
252 try {
253 mActivityManager.removeStack(PINNED_STACK_ID);
254 } catch (RemoteException e) {
255 Log.e(TAG, "removeStack failed", e);
256 }
Youngsang Chof1647922015-12-17 13:39:39 -0800257 }
Jaewan Kimc92a7d12016-02-15 17:33:25 -0800258 for (int i = mListeners.size() - 1; i >= 0; --i) {
259 mListeners.get(i).onPipActivityClosed();
260 }
Youngsang Chof1647922015-12-17 13:39:39 -0800261 }
262
263 /**
Jaewan Kim977dcdc2016-01-20 19:21:08 +0900264 * Moves the PIPed activity to the fullscreen and closes PIP system UI.
Youngsang Chof1647922015-12-17 13:39:39 -0800265 */
266 public void movePipToFullscreen() {
267 mState = STATE_NO_PIP;
Youngsang Choad8ceb02016-01-15 16:59:27 -0800268 mPipTaskId = TASK_ID_NO_PIP;
Youngsang Chof1647922015-12-17 13:39:39 -0800269 for (int i = mListeners.size() - 1; i >= 0; --i) {
270 mListeners.get(i).onMoveToFullscreen();
271 }
Wale Ogunwale480dca02016-02-06 13:58:29 -0800272 resizePinnedStack(mState);
Youngsang Chof1647922015-12-17 13:39:39 -0800273 }
274
275 /**
276 * Shows PIP overlay UI by launching {@link PipOverlayActivity}. It also locates the pinned
277 * stack to the default PIP bound {@link com.android.internal.R.string
278 * .config_defaultPictureInPictureBounds}.
279 */
Wale Ogunwale480dca02016-02-06 13:58:29 -0800280 private void showPipOverlay() {
Youngsang Chof1647922015-12-17 13:39:39 -0800281 if (DEBUG) Log.d(TAG, "showPipOverlay()");
Youngsang Chof1647922015-12-17 13:39:39 -0800282 mState = STATE_PIP_OVERLAY;
Youngsang Choefbbd492016-01-21 14:30:31 -0800283 Intent intent = new Intent(mContext, PipOverlayActivity.class);
284 intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
285 final ActivityOptions options = ActivityOptions.makeBasic();
286 options.setLaunchStackId(PINNED_STACK_ID);
Youngsang Choefbbd492016-01-21 14:30:31 -0800287 mContext.startActivity(intent, options.toBundle());
Youngsang Chof1647922015-12-17 13:39:39 -0800288 }
289
290 /**
Wale Ogunwale480dca02016-02-06 13:58:29 -0800291 * Suspends resizing operation on the Pip until {@link #resumePipResizing} is called
292 * @param reason The reason for suspending resizing operations on the Pip.
293 */
294 public void suspendPipResizing(int reason) {
295 if (DEBUG) Log.d(TAG,
296 "suspendPipResizing() reason=" + reason + " callers=" + Debug.getCallers(2));
297 mSuspendPipResizingReason |= reason;
298 }
299
300 /**
301 * Resumes resizing operation on the Pip that was previously suspended.
302 * @param reason The reason resizing operations on the Pip was suspended.
303 */
304 public void resumePipResizing(int reason) {
305 if ((mSuspendPipResizingReason & reason) == 0) {
306 return;
307 }
308 if (DEBUG) Log.d(TAG,
309 "resumePipResizing() reason=" + reason + " callers=" + Debug.getCallers(2));
310 mSuspendPipResizingReason &= ~reason;
311 mHandler.post(mResizePinnedStackRunnable);
312 }
313
314 /**
315 * Resize the Pip to the appropriate size for the input state.
316 * @param state In Pip state also used to determine the new size for the Pip.
317 */
318 public void resizePinnedStack(int state) {
319 if (DEBUG) Log.d(TAG, "resizePinnedStack() state=" + state);
320 mState = state;
Wale Ogunwale480dca02016-02-06 13:58:29 -0800321 for (int i = mListeners.size() - 1; i >= 0; --i) {
322 mListeners.get(i).onPipResizeAboutToStart();
323 }
Wale Ogunwale480dca02016-02-06 13:58:29 -0800324 if (mSuspendPipResizingReason != 0) {
325 if (DEBUG) Log.d(TAG,
326 "resizePinnedStack() deferring mSuspendPipResizingReason=" +
327 mSuspendPipResizingReason);
328 return;
329 }
Jaewan Kimc92a7d12016-02-15 17:33:25 -0800330 switch (mState) {
331 case STATE_NO_PIP:
332 mCurrentPipBounds = null;
333 break;
334 case STATE_PIP_MENU:
335 mCurrentPipBounds = mMenuModePipBounds;
336 break;
337 case STATE_PIP_OVERLAY:
338 if (mIsRecentsShown) {
339 if (mIsPipFocusedInRecent) {
340 mCurrentPipBounds = mRecentsFocusedPipBounds;
341 } else {
342 mCurrentPipBounds = mRecentsPipBounds;
343 }
344 } else {
345 mCurrentPipBounds = mPipBounds;
346 }
347 break;
348 default:
349 mCurrentPipBounds = mPipBounds;
350 break;
351 }
Wale Ogunwale480dca02016-02-06 13:58:29 -0800352 try {
Jaewan Kimc92a7d12016-02-15 17:33:25 -0800353 mActivityManager.resizeStack(PINNED_STACK_ID, mCurrentPipBounds, true, true, true);
Wale Ogunwale480dca02016-02-06 13:58:29 -0800354 } catch (RemoteException e) {
355 Log.e(TAG, "showPipMenu failed", e);
356 }
357 }
358
359 /**
Jaewan Kimc92a7d12016-02-15 17:33:25 -0800360 * Returns the current PIP bound for activities to sync their UI with PIP.
361 */
362 public Rect getPipBounds() {
363 return mCurrentPipBounds;
364 }
365
366 /**
367 * Called when Recents is started.
368 * PIPed activity will be resized accordingly and overlay will show available buttons.
369 */
370 public void onRecentsStarted() {
371 mIsRecentsShown = true;
372 mIsPipFocusedInRecent = false;
373 if (mState == STATE_NO_PIP) {
374 return;
375 }
376 resizePinnedStack(STATE_PIP_OVERLAY);
377 }
378
379 /**
380 * Called when Recents is stopped.
381 * PIPed activity will be resized accordingly and overlay will hide available buttons.
382 */
383 public void onRecentsStopped() {
384 mIsRecentsShown = false;
385 mIsPipFocusedInRecent = false;
386 if (mState == STATE_NO_PIP) {
387 return;
388 }
389 resizePinnedStack(STATE_PIP_OVERLAY);
390 }
391
392 /**
393 * Returns {@code true} if recents is shown.
394 */
395 boolean isRecentsShown() {
396 return mIsRecentsShown;
397 }
398
399 /**
400 * Called when the PIP view in {@link com.android.systemui.recents.tv.RecentsTvActivity}
401 * is focused.
402 * This only resizes pinned stack so it looks like it's in Recents.
403 * This should be called only by {@link com.android.systemui.recents.tv.RecentsTvActivity}.
404 */
405 public void onPipViewFocusChangedInRecents(boolean hasFocus) {
406 mIsPipFocusedInRecent = hasFocus;
407 if (mState != STATE_PIP_OVERLAY) {
408 Log.w(TAG, "There is no pinned stack to handle focus change.");
409 return;
410 }
411 resizePinnedStack(STATE_PIP_OVERLAY);
412 }
413
414 /**
Youngsang Chof1647922015-12-17 13:39:39 -0800415 * Shows PIP menu UI by launching {@link PipMenuActivity}. It also locates the pinned
416 * stack to the centered PIP bound {@link com.android.internal.R.string
417 * .config_centeredPictureInPictureBounds}.
418 */
Wale Ogunwale480dca02016-02-06 13:58:29 -0800419 private void showPipMenu() {
Youngsang Chof1647922015-12-17 13:39:39 -0800420 if (DEBUG) Log.d(TAG, "showPipMenu()");
Youngsang Chof1647922015-12-17 13:39:39 -0800421 mState = STATE_PIP_MENU;
422 for (int i = mListeners.size() - 1; i >= 0; --i) {
423 mListeners.get(i).onShowPipMenu();
424 }
425 Intent intent = new Intent(mContext, PipMenuActivity.class);
426 intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
Jaewan Kim1a9dc562016-02-17 13:41:51 -0800427 mContext.startActivity(intent);
Youngsang Chof1647922015-12-17 13:39:39 -0800428 }
429
Youngsang Chof1647922015-12-17 13:39:39 -0800430 public void addListener(Listener listener) {
431 mListeners.add(listener);
432 }
433
Youngsang Chof1647922015-12-17 13:39:39 -0800434 public void removeListener(Listener listener) {
435 mListeners.remove(listener);
436 }
437
Jaewan Kim977dcdc2016-01-20 19:21:08 +0900438 private void launchPipOnboardingActivityIfNeeded() {
439 if (DEBUG_FORCE_ONBOARDING || !mOnboardingShown) {
440 mOnboardingShown = true;
441 Prefs.putBoolean(mContext, TV_PICTURE_IN_PICTURE_ONBOARDING_SHOWN, true);
442
443 Intent intent = new Intent(mContext, PipOnboardingActivity.class);
444 intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
445 mContext.startActivity(intent);
446 }
447 }
448
Jaewan Kimc92a7d12016-02-15 17:33:25 -0800449 /**
450 * Returns {@code true} if PIP is shown.
451 */
452 public boolean isPipShown() {
453 return hasPipTasks();
454 }
455
Youngsang Chof1647922015-12-17 13:39:39 -0800456 private boolean hasPipTasks() {
457 try {
458 StackInfo stackInfo = mActivityManager.getStackInfo(PINNED_STACK_ID);
459 return stackInfo != null;
460 } catch (RemoteException e) {
461 Log.e(TAG, "getStackInfo failed", e);
462 return false;
463 }
464 }
465
Youngsang Choad8ceb02016-01-15 16:59:27 -0800466 private void handleMediaResourceGranted(String[] packageNames) {
467 StackInfo fullscreenStack = null;
468 try {
469 fullscreenStack = mActivityManager.getStackInfo(FULLSCREEN_WORKSPACE_STACK_ID);
470 } catch (RemoteException e) {
471 Log.e(TAG, "getStackInfo failed", e);
472 }
473 if (fullscreenStack == null) {
474 return;
475 }
476 int fullscreenTopTaskId = fullscreenStack.taskIds[fullscreenStack.taskIds.length - 1];
477 List<RunningTaskInfo> tasks = null;
478 try {
479 tasks = mActivityManager.getTasks(MAX_RUNNING_TASKS_COUNT, 0);
480 } catch (RemoteException e) {
481 Log.e(TAG, "getTasks failed", e);
482 }
483 if (tasks == null) {
484 return;
485 }
486 boolean wasGrantedInFullscreen = false;
487 boolean wasGrantedInPip = false;
488 for (int i = tasks.size() - 1; i >= 0; --i) {
489 RunningTaskInfo task = tasks.get(i);
490 for (int j = packageNames.length - 1; j >= 0; --j) {
491 if (task.topActivity.getPackageName().equals(packageNames[j])) {
492 if (task.id == fullscreenTopTaskId) {
493 wasGrantedInFullscreen = true;
494 } else if (task.id == mPipTaskId) {
495 wasGrantedInPip= true;
496 }
497 }
498 }
499 }
500 if (wasGrantedInFullscreen && !wasGrantedInPip) {
501 closePip();
502 }
503 }
504
Youngsang Chof1647922015-12-17 13:39:39 -0800505 private class TaskStackListener extends ITaskStackListener.Stub {
506 @Override
507 public void onTaskStackChanged() throws RemoteException {
508 // Post the message back to the UI thread.
509 mHandler.post(mOnTaskStackChanged);
510 }
511
512 @Override
513 public void onActivityPinned() throws RemoteException {
514 // Post the message back to the UI thread.
Wale Ogunwale480dca02016-02-06 13:58:29 -0800515 if (DEBUG) Log.d(TAG, "onActivityPinned()");
Youngsang Chof1647922015-12-17 13:39:39 -0800516 mHandler.post(mOnActivityPinnedRunnable);
517 }
Wale Ogunwalecc25a8a2016-01-23 14:31:37 -0800518
519 @Override
520 public void onPinnedActivityRestartAttempt() {
Youngsang Cho6a00b702016-01-25 15:48:41 -0800521 // Post the message back to the UI thread.
Wale Ogunwale480dca02016-02-06 13:58:29 -0800522 if (DEBUG) Log.d(TAG, "onPinnedActivityRestartAttempt()");
Youngsang Cho6a00b702016-01-25 15:48:41 -0800523 mHandler.post(mOnPinnedActivityRestartAttempt);
Wale Ogunwalecc25a8a2016-01-23 14:31:37 -0800524 }
Wale Ogunwale480dca02016-02-06 13:58:29 -0800525
526 @Override
527 public void onPinnedStackAnimationEnded() {
528 if (DEBUG) Log.d(TAG, "onPinnedStackAnimationEnded()");
529 mHandler.post(mOnPinnedStackAnimationEnded);
530 }
Youngsang Chof1647922015-12-17 13:39:39 -0800531 }
532
533 /**
534 * A listener interface to receive notification on changes in PIP.
535 */
536 public interface Listener {
Wale Ogunwale480dca02016-02-06 13:58:29 -0800537 /** Invoked when a PIPed activity is closed. */
Youngsang Chof1647922015-12-17 13:39:39 -0800538 void onPipActivityClosed();
Wale Ogunwale480dca02016-02-06 13:58:29 -0800539 /** Invoked when the PIP menu gets shown. */
Youngsang Chof1647922015-12-17 13:39:39 -0800540 void onShowPipMenu();
Wale Ogunwale480dca02016-02-06 13:58:29 -0800541 /** Invoked when the PIPed activity is returned back to the fullscreen. */
Youngsang Chof1647922015-12-17 13:39:39 -0800542 void onMoveToFullscreen();
Wale Ogunwale480dca02016-02-06 13:58:29 -0800543 /** Invoked when we are above to start resizing the Pip. */
544 void onPipResizeAboutToStart();
Youngsang Chof1647922015-12-17 13:39:39 -0800545 }
546
547 /**
548 * Gets an instance of {@link PipManager}.
549 */
550 public static PipManager getInstance() {
551 if (sPipManager == null) {
552 sPipManager = new PipManager();
553 }
554 return sPipManager;
555 }
556}