1/* 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 19import static android.app.ActivityManager.StackId.PINNED_STACK_ID; 20import static android.view.Display.DEFAULT_DISPLAY; 21 22import android.app.ActivityManager; 23import android.app.ActivityManager.StackInfo; 24import android.app.IActivityManager; 25import android.content.ComponentName; 26import android.content.Context; 27import android.content.pm.ParceledListSlice; 28import android.content.res.Configuration; 29import android.graphics.Rect; 30import android.os.Handler; 31import android.os.RemoteException; 32import android.util.Log; 33import android.view.IPinnedStackController; 34import android.view.IPinnedStackListener; 35import android.view.IWindowManager; 36import android.view.WindowManagerGlobal; 37 38import com.android.systemui.pip.BasePipManager; 39import com.android.systemui.recents.events.EventBus; 40import com.android.systemui.recents.events.component.ExpandPipEvent; 41import com.android.systemui.recents.misc.SystemServicesProxy; 42import com.android.systemui.recents.misc.SystemServicesProxy.TaskStackListener; 43import com.android.systemui.statusbar.CommandQueue; 44 45import java.io.PrintWriter; 46 47/** 48 * Manages the picture-in-picture (PIP) UI and states for Phones. 49 */ 50public class PipManager implements BasePipManager { 51 private static final String TAG = "PipManager"; 52 53 private static PipManager sPipController; 54 55 private Context mContext; 56 private IActivityManager mActivityManager; 57 private IWindowManager mWindowManager; 58 private Handler mHandler = new Handler(); 59 60 private final PinnedStackListener mPinnedStackListener = new PinnedStackListener(); 61 62 private InputConsumerController mInputConsumerController; 63 private PipMenuActivityController mMenuController; 64 private PipMediaController mMediaController; 65 private PipNotificationController mNotificationController; 66 private PipTouchHandler mTouchHandler; 67 68 /** 69 * Handler for system task stack changes. 70 */ 71 TaskStackListener mTaskStackListener = new TaskStackListener() { 72 @Override 73 public void onActivityPinned(String packageName, int taskId) { 74 if (!checkCurrentUserId(mContext, false /* debug */)) { 75 return; 76 } 77 78 mTouchHandler.onActivityPinned(); 79 mMediaController.onActivityPinned(); 80 mMenuController.onActivityPinned(); 81 mNotificationController.onActivityPinned(packageName, 82 true /* deferUntilAnimationEnds */); 83 84 SystemServicesProxy.getInstance(mContext).setPipVisibility(true); 85 } 86 87 @Override 88 public void onActivityUnpinned() { 89 if (!checkCurrentUserId(mContext, false /* debug */)) { 90 return; 91 } 92 93 ComponentName topPipActivity = PipUtils.getTopPinnedActivity(mContext, 94 mActivityManager); 95 mMenuController.hideMenu(); 96 mNotificationController.onActivityUnpinned(topPipActivity); 97 98 SystemServicesProxy.getInstance(mContext).setPipVisibility(topPipActivity != null); 99 } 100 101 @Override 102 public void onPinnedStackAnimationStarted() { 103 // Disable touches while the animation is running 104 mTouchHandler.setTouchEnabled(false); 105 } 106 107 @Override 108 public void onPinnedStackAnimationEnded() { 109 // Re-enable touches after the animation completes 110 mTouchHandler.setTouchEnabled(true); 111 mTouchHandler.onPinnedStackAnimationEnded(); 112 mMenuController.onPinnedStackAnimationEnded(); 113 mNotificationController.onPinnedStackAnimationEnded(); 114 } 115 116 @Override 117 public void onPinnedActivityRestartAttempt(boolean clearedTask) { 118 if (!checkCurrentUserId(mContext, false /* debug */)) { 119 return; 120 } 121 122 mTouchHandler.getMotionHelper().expandPip(clearedTask /* skipAnimation */); 123 } 124 }; 125 126 /** 127 * Handler for messages from the PIP controller. 128 */ 129 private class PinnedStackListener extends IPinnedStackListener.Stub { 130 131 @Override 132 public void onListenerRegistered(IPinnedStackController controller) { 133 mHandler.post(() -> { 134 mTouchHandler.setPinnedStackController(controller); 135 }); 136 } 137 138 @Override 139 public void onImeVisibilityChanged(boolean imeVisible, int imeHeight) { 140 mHandler.post(() -> { 141 mTouchHandler.onImeVisibilityChanged(imeVisible, imeHeight); 142 }); 143 } 144 145 @Override 146 public void onMinimizedStateChanged(boolean isMinimized) { 147 mHandler.post(() -> { 148 mTouchHandler.setMinimizedState(isMinimized, true /* fromController */); 149 }); 150 } 151 152 @Override 153 public void onMovementBoundsChanged(Rect insetBounds, Rect normalBounds, 154 Rect animatingBounds, boolean fromImeAdjustement, int displayRotation) { 155 mHandler.post(() -> { 156 mTouchHandler.onMovementBoundsChanged(insetBounds, normalBounds, animatingBounds, 157 fromImeAdjustement, displayRotation); 158 }); 159 } 160 161 @Override 162 public void onActionsChanged(ParceledListSlice actions) { 163 mHandler.post(() -> { 164 mMenuController.setAppActions(actions); 165 }); 166 } 167 } 168 169 private PipManager() {} 170 171 /** 172 * Initializes {@link PipManager}. 173 */ 174 public void initialize(Context context) { 175 mContext = context; 176 mActivityManager = ActivityManager.getService(); 177 mWindowManager = WindowManagerGlobal.getWindowManagerService(); 178 179 try { 180 mWindowManager.registerPinnedStackListener(DEFAULT_DISPLAY, mPinnedStackListener); 181 } catch (RemoteException e) { 182 Log.e(TAG, "Failed to register pinned stack listener", e); 183 } 184 SystemServicesProxy.getInstance(mContext).registerTaskStackListener(mTaskStackListener); 185 186 mInputConsumerController = new InputConsumerController(mWindowManager); 187 mMediaController = new PipMediaController(context, mActivityManager); 188 mMenuController = new PipMenuActivityController(context, mActivityManager, mMediaController, 189 mInputConsumerController); 190 mTouchHandler = new PipTouchHandler(context, mActivityManager, mMenuController, 191 mInputConsumerController); 192 mNotificationController = new PipNotificationController(context, mActivityManager, 193 mTouchHandler.getMotionHelper()); 194 EventBus.getDefault().register(this); 195 } 196 197 /** 198 * Updates the PIP per configuration changed. 199 */ 200 public void onConfigurationChanged(Configuration newConfig) { 201 mTouchHandler.onConfigurationChanged(); 202 } 203 204 /** 205 * Expands the PIP. 206 */ 207 public final void onBusEvent(ExpandPipEvent event) { 208 if (event.clearThumbnailWindows) { 209 try { 210 StackInfo stackInfo = mActivityManager.getStackInfo(PINNED_STACK_ID); 211 if (stackInfo != null && stackInfo.taskIds != null) { 212 SystemServicesProxy ssp = SystemServicesProxy.getInstance(mContext); 213 for (int taskId : stackInfo.taskIds) { 214 ssp.cancelThumbnailTransition(taskId); 215 } 216 } 217 } catch (RemoteException e) { 218 // Do nothing 219 } 220 } 221 mTouchHandler.getMotionHelper().expandPip(false /* skipAnimation */); 222 } 223 224 /** 225 * Sent from KEYCODE_WINDOW handler in PhoneWindowManager, to request the menu to be shown. 226 */ 227 public void showPictureInPictureMenu() { 228 mTouchHandler.showPictureInPictureMenu(); 229 } 230 231 /** 232 * Gets an instance of {@link PipManager}. 233 */ 234 public static PipManager getInstance() { 235 if (sPipController == null) { 236 sPipController = new PipManager(); 237 } 238 return sPipController; 239 } 240 241 public void dump(PrintWriter pw) { 242 final String innerPrefix = " "; 243 pw.println(TAG); 244 mInputConsumerController.dump(pw, innerPrefix); 245 mMenuController.dump(pw, innerPrefix); 246 mTouchHandler.dump(pw, innerPrefix); 247 } 248} 249