@Override
public void start() {
sDebugFlags = new RecentsDebugFlags(mContext);
- sSystemServicesProxy = new SystemServicesProxy(mContext);
+ sSystemServicesProxy = SystemServicesProxy.getInstance(mContext);
sTaskLoader = new RecentsTaskLoader(mContext);
sConfiguration = new RecentsConfiguration(mContext);
mHandler = new Handler();
import android.app.ActivityManager;
import android.app.ActivityOptions;
-import android.app.ITaskStackListener;
import android.appwidget.AppWidgetProviderInfo;
import android.content.ActivityNotFoundException;
import android.content.Context;
import com.android.systemui.recents.misc.DozeTrigger;
import com.android.systemui.recents.misc.ForegroundThread;
import com.android.systemui.recents.misc.SystemServicesProxy;
+import com.android.systemui.recents.misc.SystemServicesProxy.TaskStackListener;
import com.android.systemui.recents.model.RecentsTaskLoadPlan;
import com.android.systemui.recents.model.RecentsTaskLoader;
import com.android.systemui.recents.model.Task;
public final static String RECENTS_ACTIVITY = "com.android.systemui.recents.RecentsActivity";
/**
- * An implementation of ITaskStackListener, that allows us to listen for changes to the system
+ * An implementation of TaskStackListener, that allows us to listen for changes to the system
* task stacks and update recents accordingly.
*/
- class TaskStackListenerImpl extends ITaskStackListener.Stub implements Runnable {
- Handler mHandler;
-
- public TaskStackListenerImpl(Handler handler) {
- mHandler = handler;
- }
-
+ class TaskStackListenerImpl extends TaskStackListener {
@Override
public void onTaskStackChanged() {
- // Debounce any task stack changes
- mHandler.removeCallbacks(this);
- mHandler.post(this);
- }
-
- @Override
- public void onActivityPinned() {
- }
-
- @Override
- public void onPinnedActivityRestartAttempt() {
- }
-
- @Override
- public void onPinnedStackAnimationEnded() {
- }
-
- /** Preloads the next task */
- public void run() {
+ // Preloads the next task
RecentsConfiguration config = Recents.getConfiguration();
if (config.svelteLevel == RecentsConfiguration.SVELTE_NONE) {
RecentsTaskLoader loader = Recents.getTaskLoader();
ForegroundThread.get();
// Register the task stack listener
- mTaskStackListener = new TaskStackListenerImpl(mHandler);
+ mTaskStackListener = new TaskStackListenerImpl();
SystemServicesProxy ssp = Recents.getSystemServices();
ssp.registerTaskStackListener(mTaskStackListener);
import android.graphics.drawable.ColorDrawable;
import android.graphics.drawable.Drawable;
import android.os.Bundle;
+import android.os.Handler;
+import android.os.Looper;
+import android.os.Message;
import android.os.ParcelFileDescriptor;
import android.os.RemoteException;
import android.os.SystemProperties;
sRecentsBlacklist.add("com.android.systemui.tv.pip.PipMenuActivity");
}
+ private static SystemServicesProxy sSystemServicesProxy;
+
AccessibilityManager mAccm;
ActivityManager mAm;
IActivityManager mIam;
Paint mBgProtectionPaint;
Canvas mBgProtectionCanvas;
+ private final Handler mHandler = new H();
+
+ /**
+ * An abstract class to track task stack changes.
+ * Classes should implement this instead of {@link android.app.ITaskStackListener}
+ * to reduce IPC calls from system services. These callbacks will be called on the main thread.
+ */
+ public abstract static class TaskStackListener {
+ public void onTaskStackChanged() { }
+ public void onActivityPinned() { }
+ public void onPinnedActivityRestartAttempt() { }
+ public void onPinnedStackAnimationEnded() { }
+ }
+
+ /**
+ * Implementation of {@link android.app.ITaskStackListener} to listen task stack changes from
+ * ActivityManagerNative.
+ * This simply passes callbacks to listeners through {@link H}.
+ * */
+ private ITaskStackListener.Stub mTaskStackListener = new ITaskStackListener.Stub() {
+ @Override
+ public void onTaskStackChanged() throws RemoteException {
+ mHandler.removeMessages(H.ON_TASK_STACK_CHANGED);
+ mHandler.sendEmptyMessage(H.ON_TASK_STACK_CHANGED);
+ }
+
+ @Override
+ public void onActivityPinned() throws RemoteException {
+ mHandler.removeMessages(H.ON_ACTIVITY_PINNED);
+ mHandler.sendEmptyMessage(H.ON_ACTIVITY_PINNED);
+ }
+
+ @Override
+ public void onPinnedActivityRestartAttempt() throws RemoteException{
+ mHandler.removeMessages(H.ON_PINNED_ACTIVITY_RESTART_ATTEMPT);
+ mHandler.sendEmptyMessage(H.ON_PINNED_ACTIVITY_RESTART_ATTEMPT);
+ }
+
+ @Override
+ public void onPinnedStackAnimationEnded() throws RemoteException {
+ mHandler.removeMessages(H.ON_PINNED_STACK_ANIMATION_ENDED);
+ mHandler.sendEmptyMessage(H.ON_PINNED_STACK_ANIMATION_ENDED);
+ }
+ };
+
+ /**
+ * List of {@link TaskStackListener} registered from {@link registerTaskStackListener}.
+ */
+ private List<TaskStackListener> mTaskStackListeners = new ArrayList<>();
+
/** Private constructor */
- public SystemServicesProxy(Context context) {
+ private SystemServicesProxy(Context context) {
mAccm = AccessibilityManager.getInstance(context);
mAm = (ActivityManager) context.getSystemService(Context.ACTIVITY_SERVICE);
mIam = ActivityManagerNative.getDefault();
}
}
+ /**
+ * Returns the single instance of the {@link SystemServicesProxy}.
+ * This should only be called on the main thread.
+ */
+ public static SystemServicesProxy getInstance(Context context) {
+ if (!Looper.getMainLooper().isCurrentThread()) {
+ throw new RuntimeException("Must be called on the UI thread");
+ }
+ if (sSystemServicesProxy == null) {
+ sSystemServicesProxy = new SystemServicesProxy(context);
+ }
+ return sSystemServicesProxy;
+ }
+
/** Returns a list of the recents tasks */
public List<ActivityManager.RecentTaskInfo> getRecentTasks(int numLatestTasks, int userId,
boolean isTopTaskHome, ArraySet<Integer> quietProfileIds) {
}
}
- /** Registers a task stack listener with the system. */
- public void registerTaskStackListener(ITaskStackListener listener) {
+ /**
+ * Registers a task stack listener with the system.
+ * This should be called on the main thread.
+ */
+ public void registerTaskStackListener(TaskStackListener listener) {
if (mIam == null) return;
- try {
- mIam.registerTaskStackListener(listener);
- } catch (Exception e) {
- e.printStackTrace();
+ mTaskStackListeners.add(listener);
+ if (mTaskStackListeners.size() == 1) {
+ // Register mTaskStackListener to IActivityManager only once if needed.
+ try {
+ mIam.registerTaskStackListener(mTaskStackListener);
+ } catch (Exception e) {
+ Log.w(TAG, "Failed to call registerTaskStackListener", e);
+ }
}
}
e.printStackTrace();
}
}
+
+ private final class H extends Handler {
+ private static final int ON_TASK_STACK_CHANGED = 1;
+ private static final int ON_ACTIVITY_PINNED = 2;
+ private static final int ON_PINNED_ACTIVITY_RESTART_ATTEMPT = 3;
+ private static final int ON_PINNED_STACK_ANIMATION_ENDED = 4;
+
+ @Override
+ public void handleMessage(Message msg) {
+ switch (msg.what) {
+ case ON_TASK_STACK_CHANGED: {
+ for (int i = mTaskStackListeners.size() - 1; i >= 0; i--) {
+ mTaskStackListeners.get(i).onTaskStackChanged();
+ }
+ break;
+ }
+ case ON_ACTIVITY_PINNED: {
+ for (int i = mTaskStackListeners.size() - 1; i >= 0; i--) {
+ mTaskStackListeners.get(i).onActivityPinned();
+ }
+ break;
+ }
+ case ON_PINNED_ACTIVITY_RESTART_ATTEMPT: {
+ for (int i = mTaskStackListeners.size() - 1; i >= 0; i--) {
+ mTaskStackListeners.get(i).onPinnedActivityRestartAttempt();
+ }
+ break;
+ }
+ case ON_PINNED_STACK_ANIMATION_ENDED: {
+ for (int i = mTaskStackListeners.size() - 1; i >= 0; i--) {
+ mTaskStackListeners.get(i).onPinnedStackAnimationEnded();
+ }
+ break;
+ }
+ }
+ }
+ }
}
package com.android.systemui.statusbar.car;
import android.app.ActivityManager;
-import android.app.ITaskStackListener;
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import com.android.systemui.R;
import com.android.systemui.recents.Recents;
import com.android.systemui.recents.misc.SystemServicesProxy;
+import com.android.systemui.recents.misc.SystemServicesProxy.TaskStackListener;
import com.android.systemui.statusbar.StatusBarState;
import com.android.systemui.statusbar.phone.PhoneStatusBar;
* A status bar (and navigation bar) tailored for the automotive use case.
*/
public class CarStatusBar extends PhoneStatusBar {
- private SystemServicesProxy mSystemServicesProxy;
private TaskStackListenerImpl mTaskStackListener;
- private Handler mHandler;
private CarNavigationBarView mCarNavigationBar;
private CarNavigationBarController mController;
@Override
public void start() {
super.start();
- mHandler = new Handler();
- mTaskStackListener = new TaskStackListenerImpl(mHandler);
- mSystemServicesProxy = new SystemServicesProxy(mContext);
- mSystemServicesProxy.registerTaskStackListener(mTaskStackListener);
+ mTaskStackListener = new TaskStackListenerImpl();
+ SystemServicesProxy.getInstance(mContext).registerTaskStackListener(mTaskStackListener);
registerPackageChangeReceivers();
}
}
/**
- * An implementation of ITaskStackListener, that listens for changes in the system task
+ * An implementation of TaskStackListener, that listens for changes in the system task
* stack and notifies the navigation bar.
*/
- private class TaskStackListenerImpl extends ITaskStackListener.Stub implements Runnable {
- private Handler mHandler;
-
- public TaskStackListenerImpl(Handler handler) {
- this.mHandler = handler;
- }
-
- @Override
- public void onActivityPinned() {
- }
-
- @Override
- public void onPinnedActivityRestartAttempt() {
- }
-
- @Override
- public void onPinnedStackAnimationEnded() {
- }
-
+ private class TaskStackListenerImpl extends TaskStackListener {
@Override
public void onTaskStackChanged() {
- mHandler.removeCallbacks(this);
- mHandler.post(this);
- }
-
- @Override
- public void run() {
- ensureMainThread();
SystemServicesProxy ssp = Recents.getSystemServices();
ActivityManager.RunningTaskInfo runningTaskInfo = ssp.getTopMostTask();
mController.taskChanged(runningTaskInfo.baseActivity.getPackageName());
}
-
- private void ensureMainThread() {
- if (!Looper.getMainLooper().isCurrentThread()) {
- throw new RuntimeException("Must be called on the UI thread");
- }
- }
}
@Override
import android.app.ActivityManagerNative;
import android.app.ActivityOptions;
import android.app.IActivityManager;
-import android.app.ITaskStackListener;
import android.content.BroadcastReceiver;
import android.content.ComponentName;
import android.content.Context;
import android.util.Log;
import com.android.systemui.Prefs;
+import com.android.systemui.recents.misc.SystemServicesProxy;
+import com.android.systemui.recents.misc.SystemServicesProxy.TaskStackListener;
import java.util.ArrayList;
import java.util.List;
private boolean mIsRecentsShown;
private boolean mIsPipFocusedInRecent;
- private final Runnable mOnActivityPinnedRunnable = new Runnable() {
- @Override
- public void run() {
- StackInfo stackInfo = null;
- try {
- stackInfo = mActivityManager.getStackInfo(PINNED_STACK_ID);
- if (stackInfo == null) {
- Log.w(TAG, "Cannot find pinned stack");
- return;
- }
- } catch (RemoteException e) {
- Log.e(TAG, "getStackInfo failed", e);
- return;
- }
- if (DEBUG) Log.d(TAG, "PINNED_STACK:" + stackInfo);
- mPipTaskId = stackInfo.taskIds[stackInfo.taskIds.length - 1];
- mPipComponentName = ComponentName.unflattenFromString(
- stackInfo.taskNames[stackInfo.taskNames.length - 1]);
- // Set state to overlay so we show it when the pinned stack animation ends.
- mState = STATE_PIP_OVERLAY;
- mCurrentPipBounds = mPipBounds;
- launchPipOnboardingActivityIfNeeded();
- mMediaSessionManager.addOnActiveSessionsChangedListener(
- mActiveMediaSessionListener, null);
- updateMediaController(mMediaSessionManager.getActiveSessions(null));
- if (mIsRecentsShown) {
- // If an activity becomes PIPed again after the fullscreen, the Recents is shown
- // behind so we need to resize the pinned stack and show the correct overlay.
- resizePinnedStack(STATE_PIP_OVERLAY);
- }
- for (int i = mListeners.size() - 1; i >= 0; i--) {
- mListeners.get(i).onPipEntered();
- }
- }
- };
- private final Runnable mOnTaskStackChanged = new Runnable() {
- @Override
- public void run() {
- if (mState != STATE_NO_PIP) {
- StackInfo stackInfo = null;
- try {
- stackInfo = mActivityManager.getStackInfo(PINNED_STACK_ID);
- if (stackInfo == null) {
- Log.w(TAG, "There is no pinned stack");
- closePipInternal(false);
- return;
- }
- } catch (RemoteException e) {
- Log.e(TAG, "getStackInfo failed", e);
- return;
- }
- for (int i = stackInfo.taskIds.length - 1; i >= 0; --i) {
- if (stackInfo.taskIds[i] == mPipTaskId) {
- // PIP task is still alive.
- return;
- }
- }
- // PIP task doesn't exist anymore in PINNED_STACK.
- closePipInternal(true);
- }
- }
- };
- private final Runnable mOnPinnedActivityRestartAttempt = new Runnable() {
- @Override
- public void run() {
- // If PIPed activity is launched again by Launcher or intent, make it fullscreen.
- movePipToFullscreen();
- }
- };
- private final Runnable mOnPinnedStackAnimationEnded = new Runnable() {
- @Override
- public void run() {
- switch (mState) {
- case STATE_PIP_OVERLAY:
- showPipOverlay();
- break;
- case STATE_PIP_MENU:
- showPipMenu();
- break;
- }
- }
- };
-
private final Runnable mResizePinnedStackRunnable = new Runnable() {
@Override
public void run() {
(int) (mRecentsPipBounds.bottom + scaleBy * mRecentsPipBounds.height()));
mActivityManager = ActivityManagerNative.getDefault();
- TaskStackListener taskStackListener = new TaskStackListener();
- IActivityManager iam = ActivityManagerNative.getDefault();
- try {
- iam.registerTaskStackListener(taskStackListener);
- } catch (RemoteException e) {
- Log.e(TAG, "registerTaskStackListener failed", e);
- }
+ SystemServicesProxy.getInstance(context).registerTaskStackListener(mTaskStackListener);
IntentFilter intentFilter = new IntentFilter();
intentFilter.addAction(Intent.ACTION_MEDIA_RESOURCE_GRANTED);
mContext.registerReceiver(mBroadcastReceiver, intentFilter);
return mPipMediaController;
}
- private class TaskStackListener extends ITaskStackListener.Stub {
+ TaskStackListener mTaskStackListener = new TaskStackListener() {
@Override
- public void onTaskStackChanged() throws RemoteException {
- // Post the message back to the UI thread.
- mHandler.post(mOnTaskStackChanged);
+ public void onTaskStackChanged() {
+ if (mState != STATE_NO_PIP) {
+ StackInfo stackInfo = null;
+ try {
+ stackInfo = mActivityManager.getStackInfo(PINNED_STACK_ID);
+ if (stackInfo == null) {
+ Log.w(TAG, "There is no pinned stack");
+ closePipInternal(false);
+ return;
+ }
+ } catch (RemoteException e) {
+ Log.e(TAG, "getStackInfo failed", e);
+ return;
+ }
+ for (int i = stackInfo.taskIds.length - 1; i >= 0; --i) {
+ if (stackInfo.taskIds[i] == mPipTaskId) {
+ // PIP task is still alive.
+ return;
+ }
+ }
+ // PIP task doesn't exist anymore in PINNED_STACK.
+ closePipInternal(true);
+ }
}
@Override
- public void onActivityPinned() throws RemoteException {
- // Post the message back to the UI thread.
+ public void onActivityPinned() {
if (DEBUG) Log.d(TAG, "onActivityPinned()");
- mHandler.post(mOnActivityPinnedRunnable);
+ StackInfo stackInfo = null;
+ try {
+ stackInfo = mActivityManager.getStackInfo(PINNED_STACK_ID);
+ if (stackInfo == null) {
+ Log.w(TAG, "Cannot find pinned stack");
+ return;
+ }
+ } catch (RemoteException e) {
+ Log.e(TAG, "getStackInfo failed", e);
+ return;
+ }
+ if (DEBUG) Log.d(TAG, "PINNED_STACK:" + stackInfo);
+ mPipTaskId = stackInfo.taskIds[stackInfo.taskIds.length - 1];
+ mPipComponentName = ComponentName.unflattenFromString(
+ stackInfo.taskNames[stackInfo.taskNames.length - 1]);
+ // Set state to overlay so we show it when the pinned stack animation ends.
+ mState = STATE_PIP_OVERLAY;
+ mCurrentPipBounds = mPipBounds;
+ launchPipOnboardingActivityIfNeeded();
+ mMediaSessionManager.addOnActiveSessionsChangedListener(
+ mActiveMediaSessionListener, null);
+ updateMediaController(mMediaSessionManager.getActiveSessions(null));
+ if (mIsRecentsShown) {
+ // If an activity becomes PIPed again after the fullscreen, the Recents is shown
+ // behind so we need to resize the pinned stack and show the correct overlay.
+ resizePinnedStack(STATE_PIP_OVERLAY);
+ }
+ for (int i = mListeners.size() - 1; i >= 0; i--) {
+ mListeners.get(i).onPipEntered();
+ }
}
@Override
public void onPinnedActivityRestartAttempt() {
- // Post the message back to the UI thread.
if (DEBUG) Log.d(TAG, "onPinnedActivityRestartAttempt()");
- mHandler.post(mOnPinnedActivityRestartAttempt);
+ // If PIPed activity is launched again by Launcher or intent, make it fullscreen.
+ movePipToFullscreen();
}
@Override
public void onPinnedStackAnimationEnded() {
if (DEBUG) Log.d(TAG, "onPinnedStackAnimationEnded()");
- mHandler.post(mOnPinnedStackAnimationEnded);
+ switch (mState) {
+ case STATE_PIP_OVERLAY:
+ showPipOverlay();
+ break;
+ case STATE_PIP_MENU:
+ showPipMenu();
+ break;
+ }
}
- }
+ };
/**
* A listener interface to receive notification on changes in PIP.