diff --git a/libs/WindowManager/Shell/res/drawable/tv_split_menu_ic_focus.xml b/libs/WindowManager/Shell/res/drawable/tv_split_menu_ic_focus.xml new file mode 100644 index 0000000000000..a348b148afb4e --- /dev/null +++ b/libs/WindowManager/Shell/res/drawable/tv_split_menu_ic_focus.xml @@ -0,0 +1,26 @@ + + + + + + diff --git a/libs/WindowManager/Shell/res/drawable/tv_split_menu_ic_swap.xml b/libs/WindowManager/Shell/res/drawable/tv_split_menu_ic_swap.xml new file mode 100644 index 0000000000000..c5d54c5fa4f28 --- /dev/null +++ b/libs/WindowManager/Shell/res/drawable/tv_split_menu_ic_swap.xml @@ -0,0 +1,25 @@ + + + + + \ No newline at end of file diff --git a/libs/WindowManager/Shell/res/layout/tv_split_menu_view.xml b/libs/WindowManager/Shell/res/layout/tv_split_menu_view.xml new file mode 100644 index 0000000000000..e0fa59c9f157a --- /dev/null +++ b/libs/WindowManager/Shell/res/layout/tv_split_menu_view.xml @@ -0,0 +1,125 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/TvWMShellModule.java b/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/TvWMShellModule.java index 15bfeb297b41e..e9957fd4f4f1d 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/TvWMShellModule.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/TvWMShellModule.java @@ -16,16 +16,32 @@ package com.android.wm.shell.dagger; -import android.view.IWindowManager; +import android.content.Context; +import android.os.Handler; +import com.android.launcher3.icons.IconProvider; +import com.android.wm.shell.RootTaskDisplayAreaOrganizer; +import com.android.wm.shell.ShellTaskOrganizer; import com.android.wm.shell.common.DisplayController; import com.android.wm.shell.common.DisplayImeController; import com.android.wm.shell.common.DisplayInsetsController; import com.android.wm.shell.common.ShellExecutor; +import com.android.wm.shell.common.SyncTransactionQueue; +import com.android.wm.shell.common.SystemWindows; import com.android.wm.shell.common.TransactionPool; import com.android.wm.shell.common.annotations.ShellMainThread; +import com.android.wm.shell.draganddrop.DragAndDropController; +import com.android.wm.shell.recents.RecentTasksController; +import com.android.wm.shell.splitscreen.SplitScreenController; +import com.android.wm.shell.splitscreen.tv.TvSplitScreenController; import com.android.wm.shell.startingsurface.StartingWindowTypeAlgorithm; import com.android.wm.shell.startingsurface.tv.TvStartingWindowTypeAlgorithm; +import com.android.wm.shell.sysui.ShellCommandHandler; +import com.android.wm.shell.sysui.ShellController; +import com.android.wm.shell.sysui.ShellInit; +import com.android.wm.shell.transition.Transitions; + +import java.util.Optional; import dagger.Module; import dagger.Provides; @@ -50,5 +66,33 @@ public class TvWMShellModule { @DynamicOverride static StartingWindowTypeAlgorithm provideStartingWindowTypeAlgorithm() { return new TvStartingWindowTypeAlgorithm(); - }; + } + + @WMSingleton + @Provides + @DynamicOverride + static SplitScreenController provideSplitScreenController(Context context, + ShellInit shellInit, + ShellCommandHandler shellCommandHandler, + ShellController shellController, + ShellTaskOrganizer shellTaskOrganizer, + SyncTransactionQueue syncQueue, + RootTaskDisplayAreaOrganizer rootTDAOrganizer, + DisplayController displayController, + DisplayImeController displayImeController, + DisplayInsetsController displayInsetsController, + DragAndDropController dragAndDropController, + Transitions transitions, + TransactionPool transactionPool, + IconProvider iconProvider, + Optional recentTasks, + @ShellMainThread ShellExecutor mainExecutor, + Handler mainHandler, + SystemWindows systemWindows) { + return new TvSplitScreenController(context, shellInit, shellCommandHandler, shellController, + shellTaskOrganizer, syncQueue, rootTDAOrganizer, displayController, + displayImeController, displayInsetsController, dragAndDropController, transitions, + transactionPool, iconProvider, recentTasks, mainExecutor, mainHandler, + systemWindows); + } } diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/SplitScreenController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/SplitScreenController.java index 169e17b770f33..872916197854e 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/SplitScreenController.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/SplitScreenController.java @@ -213,14 +213,18 @@ public class SplitScreenController implements DragAndDropPolicy.Starter, mShellController.addKeyguardChangeListener(this); if (mStageCoordinator == null) { // TODO: Multi-display - mStageCoordinator = new StageCoordinator(mContext, DEFAULT_DISPLAY, mSyncQueue, - mTaskOrganizer, mDisplayController, mDisplayImeController, - mDisplayInsetsController, mTransitions, mTransactionPool, mLogger, - mIconProvider, mMainExecutor, mRecentTasksOptional); + mStageCoordinator = createStageCoordinator(); } mDragAndDropController.setSplitScreenController(this); } + protected StageCoordinator createStageCoordinator() { + return new StageCoordinator(mContext, DEFAULT_DISPLAY, mSyncQueue, + mTaskOrganizer, mDisplayController, mDisplayImeController, + mDisplayInsetsController, mTransitions, mTransactionPool, mLogger, + mIconProvider, mMainExecutor, mRecentTasksOptional); + } + @Override public Context getContext() { return mContext; diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/StageCoordinator.java b/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/StageCoordinator.java index 8d405f4f6fcf3..f6478b8db39fc 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/StageCoordinator.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/StageCoordinator.java @@ -69,6 +69,7 @@ import android.annotation.CallSuper; import android.annotation.NonNull; import android.annotation.Nullable; import android.app.ActivityManager; +import android.app.IActivityTaskManager; import android.app.PendingIntent; import android.app.WindowConfiguration; import android.content.Context; @@ -81,6 +82,7 @@ import android.os.Bundle; import android.os.Debug; import android.os.IBinder; import android.os.RemoteException; +import android.os.ServiceManager; import android.util.Log; import android.util.Slog; import android.view.Choreographer; @@ -244,7 +246,7 @@ public class StageCoordinator implements SplitLayout.SplitLayoutHandler, } }; - StageCoordinator(Context context, int displayId, SyncTransactionQueue syncQueue, + protected StageCoordinator(Context context, int displayId, SyncTransactionQueue syncQueue, ShellTaskOrganizer taskOrganizer, DisplayController displayController, DisplayImeController displayImeController, DisplayInsetsController displayInsetsController, Transitions transitions, @@ -879,6 +881,8 @@ public class StageCoordinator implements SplitLayout.SplitLayoutHandler, WindowContainerTransaction wct, @ExitReason int exitReason) { if (!mMainStage.isActive() || mIsExiting) return; + onSplitScreenExit(); + mRecentTasks.ifPresent(recentTasks -> { // Notify recents if we are exiting in a way that breaks the pair, and disable further // updates to splits in the recents until we enter split again @@ -947,6 +951,47 @@ public class StageCoordinator implements SplitLayout.SplitLayoutHandler, } } + /** + * Overridden by child classes. + */ + protected void onSplitScreenEnter() { + } + + /** + * Overridden by child classes. + */ + protected void onSplitScreenExit() { + } + + /** + * Exits the split screen by finishing one of the tasks. + */ + protected void exitStage(@SplitPosition int stageToClose) { + if (ENABLE_SHELL_TRANSITIONS) { + StageTaskListener stageToTop = mSideStagePosition == stageToClose + ? mMainStage + : mSideStage; + exitSplitScreen(stageToTop, EXIT_REASON_APP_FINISHED); + } else { + boolean toEnd = stageToClose == SPLIT_POSITION_BOTTOM_OR_RIGHT; + mSplitLayout.flingDividerToDismiss(toEnd, EXIT_REASON_APP_FINISHED); + } + } + + /** + * Grants focus to the main or the side stages. + */ + protected void grantFocusToStage(@SplitPosition int stageToFocus) { + IActivityTaskManager activityTaskManagerService = IActivityTaskManager.Stub.asInterface( + ServiceManager.getService(Context.ACTIVITY_TASK_SERVICE)); + try { + activityTaskManagerService.setFocusedTask(getTaskId(stageToFocus)); + } catch (RemoteException | NullPointerException e) { + ProtoLog.e(ShellProtoLogGroup.WM_SHELL_SPLIT_SCREEN, + "%s: Unable to update focus on the chosen stage, %s", TAG, e); + } + } + /** * Returns whether the split pair in the recent tasks list should be broken. */ @@ -994,6 +1039,7 @@ public class StageCoordinator implements SplitLayout.SplitLayoutHandler, @Nullable ActivityManager.RunningTaskInfo taskInfo, @SplitPosition int startPosition) { if (mMainStage.isActive()) return; + onSplitScreenEnter(); if (taskInfo != null) { setSideStagePosition(startPosition, wct); mSideStage.addTask(taskInfo, wct); @@ -1387,6 +1433,7 @@ public class StageCoordinator implements SplitLayout.SplitLayoutHandler, } } else if (isSideStage && hasChildren && !mMainStage.isActive()) { // TODO (b/238697912) : Add the validation to prevent entering non-recovered status + onSplitScreenEnter(); final WindowContainerTransaction wct = new WindowContainerTransaction(); mSplitLayout.init(); mSplitLayout.setDividerAtBorder(mSideStagePosition == SPLIT_POSITION_TOP_OR_LEFT); diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/tv/TvSplitMenuController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/tv/TvSplitMenuController.java new file mode 100644 index 0000000000000..1d8a8d506c5c6 --- /dev/null +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/tv/TvSplitMenuController.java @@ -0,0 +1,218 @@ +/* + * Copyright (C) 2022 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.wm.shell.splitscreen.tv; + +import static android.view.Display.DEFAULT_DISPLAY; +import static android.view.View.INVISIBLE; +import static android.view.View.VISIBLE; +import static android.view.WindowManager.LayoutParams.FLAG_NOT_TOUCHABLE; +import static android.view.WindowManager.LayoutParams.PRIVATE_FLAG_NO_MOVE_ANIMATION; +import static android.view.WindowManager.LayoutParams.PRIVATE_FLAG_TRUSTED_OVERLAY; +import static android.view.WindowManager.LayoutParams.TYPE_DOCK_DIVIDER; +import static android.view.WindowManager.SHELL_ROOT_LAYER_DIVIDER; + +import android.content.BroadcastReceiver; +import android.content.Context; +import android.content.Intent; +import android.content.IntentFilter; +import android.graphics.PixelFormat; +import android.os.Handler; +import android.os.RemoteException; +import android.view.LayoutInflater; +import android.view.WindowManager; +import android.view.WindowManagerGlobal; + +import com.android.internal.protolog.common.ProtoLog; +import com.android.wm.shell.R; +import com.android.wm.shell.common.SystemWindows; +import com.android.wm.shell.common.split.SplitScreenConstants; +import com.android.wm.shell.protolog.ShellProtoLogGroup; + +/** + * Handles the interaction logic with the {@link TvSplitMenuView}. + * A bridge between {@link TvStageCoordinator} and {@link TvSplitMenuView}. + */ +public class TvSplitMenuController implements TvSplitMenuView.Listener { + + private static final String TAG = TvSplitMenuController.class.getSimpleName(); + private static final String ACTION_SHOW_MENU = "com.android.wm.shell.splitscreen.SHOW_MENU"; + private static final String SYSTEMUI_PERMISSION = "com.android.systemui.permission.SELF"; + + private final Context mContext; + private final StageController mStageController; + private final SystemWindows mSystemWindows; + private final Handler mMainHandler; + + private final TvSplitMenuView mSplitMenuView; + + private final ActionBroadcastReceiver mActionBroadcastReceiver; + + private final int mTvButtonFadeAnimationDuration; + + public TvSplitMenuController(Context context, StageController stageController, + SystemWindows systemWindows, Handler mainHandler) { + mContext = context; + mMainHandler = mainHandler; + mStageController = stageController; + mSystemWindows = systemWindows; + + mTvButtonFadeAnimationDuration = context.getResources() + .getInteger(R.integer.tv_window_menu_fade_animation_duration); + + mSplitMenuView = (TvSplitMenuView) LayoutInflater.from(context) + .inflate(R.layout.tv_split_menu_view, null); + mSplitMenuView.setListener(this); + + mActionBroadcastReceiver = new ActionBroadcastReceiver(); + } + + /** + * Adds the menu view for the splitscreen to SystemWindows. + */ + void addSplitMenuViewToSystemWindows() { + final WindowManager.LayoutParams lp = new WindowManager.LayoutParams( + mContext.getResources().getDisplayMetrics().widthPixels, + mContext.getResources().getDisplayMetrics().heightPixels, + TYPE_DOCK_DIVIDER, + FLAG_NOT_TOUCHABLE, + PixelFormat.TRANSLUCENT); + lp.privateFlags |= PRIVATE_FLAG_NO_MOVE_ANIMATION | PRIVATE_FLAG_TRUSTED_OVERLAY; + mSplitMenuView.setAlpha(0); + mSystemWindows.addView(mSplitMenuView, lp, DEFAULT_DISPLAY, SHELL_ROOT_LAYER_DIVIDER); + } + + /** + * Removes the menu view for the splitscreen from SystemWindows. + */ + void removeSplitMenuViewFromSystemWindows() { + mSystemWindows.removeView(mSplitMenuView); + } + + /** + * Registers BroadcastReceiver when split screen mode is entered. + */ + void registerBroadcastReceiver() { + mActionBroadcastReceiver.register(); + } + + /** + * Unregisters BroadcastReceiver when split screen mode is entered. + */ + void unregisterBroadcastReceiver() { + mActionBroadcastReceiver.unregister(); + } + + @Override + public void onBackPress() { + setMenuVisibility(false); + } + + @Override + public void onFocusStage(@SplitScreenConstants.SplitPosition int stageToFocus) { + setMenuVisibility(false); + mStageController.grantFocusToStage(stageToFocus); + } + + @Override + public void onCloseStage(@SplitScreenConstants.SplitPosition int stageToClose) { + setMenuVisibility(false); + mStageController.exitStage(stageToClose); + } + + @Override + public void onSwapPress() { + mStageController.swapStages(); + } + + private void setMenuVisibility(boolean visible) { + applyMenuVisibility(visible); + setMenuFocus(visible); + } + + private void applyMenuVisibility(boolean visible) { + float alphaTarget = visible ? 1F : 0F; + + if (mSplitMenuView.getAlpha() == alphaTarget) { + return; + } + + mSplitMenuView.animate() + .alpha(alphaTarget) + .setDuration(mTvButtonFadeAnimationDuration) + .withStartAction(() -> { + if (alphaTarget != 0) { + mSplitMenuView.setVisibility(VISIBLE); + } + }) + .withEndAction(() -> { + if (alphaTarget == 0) { + mSplitMenuView.setVisibility(INVISIBLE); + } + }); + + } + + private void setMenuFocus(boolean focused) { + try { + WindowManagerGlobal.getWindowSession().grantEmbeddedWindowFocus(null, + mSystemWindows.getFocusGrantToken(mSplitMenuView), focused); + } catch (RemoteException e) { + ProtoLog.e(ShellProtoLogGroup.WM_SHELL_SPLIT_SCREEN, + "%s: Unable to update focus, %s", TAG, e); + } + } + + interface StageController { + void grantFocusToStage(@SplitScreenConstants.SplitPosition int stageToFocus); + void exitStage(@SplitScreenConstants.SplitPosition int stageToClose); + void swapStages(); + } + + private class ActionBroadcastReceiver extends BroadcastReceiver { + + final IntentFilter mIntentFilter; + { + mIntentFilter = new IntentFilter(); + mIntentFilter.addAction(ACTION_SHOW_MENU); + } + boolean mRegistered = false; + + void register() { + if (mRegistered) return; + + mContext.registerReceiverForAllUsers(this, mIntentFilter, SYSTEMUI_PERMISSION, + mMainHandler); + mRegistered = true; + } + + void unregister() { + if (!mRegistered) return; + + mContext.unregisterReceiver(this); + mRegistered = false; + } + + @Override + public void onReceive(Context context, Intent intent) { + final String action = intent.getAction(); + + if (ACTION_SHOW_MENU.equals(action)) { + setMenuVisibility(true); + } + } + } +} diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/tv/TvSplitMenuView.java b/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/tv/TvSplitMenuView.java new file mode 100644 index 0000000000000..88e9757a9b315 --- /dev/null +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/tv/TvSplitMenuView.java @@ -0,0 +1,117 @@ +/* + * Copyright (C) 2022 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.wm.shell.splitscreen.tv; + +import static android.view.KeyEvent.ACTION_DOWN; +import static android.view.KeyEvent.KEYCODE_BACK; + +import static com.android.wm.shell.common.split.SplitScreenConstants.SPLIT_POSITION_BOTTOM_OR_RIGHT; +import static com.android.wm.shell.common.split.SplitScreenConstants.SPLIT_POSITION_TOP_OR_LEFT; + +import android.content.Context; +import android.util.AttributeSet; +import android.view.KeyEvent; +import android.view.View; +import android.widget.LinearLayout; + +import androidx.annotation.Nullable; + +import com.android.wm.shell.R; +import com.android.wm.shell.common.split.SplitScreenConstants; + +/** + * A View for the Menu Window. + */ +public class TvSplitMenuView extends LinearLayout implements View.OnClickListener { + + private Listener mListener; + + public TvSplitMenuView(Context context) { + super(context); + } + + public TvSplitMenuView(Context context, @Nullable AttributeSet attrs) { + super(context, attrs); + } + + public TvSplitMenuView(Context context, @Nullable AttributeSet attrs, int defStyleAttr) { + super(context, attrs, defStyleAttr); + } + + @Override + protected void onFinishInflate() { + super.onFinishInflate(); + initButtons(); + } + + @Override + public void onClick(View v) { + if (mListener == null) return; + + final int id = v.getId(); + if (id == R.id.tv_split_main_menu_focus_button) { + mListener.onFocusStage(SPLIT_POSITION_TOP_OR_LEFT); + } else if (id == R.id.tv_split_main_menu_close_button) { + mListener.onCloseStage(SPLIT_POSITION_TOP_OR_LEFT); + } else if (id == R.id.tv_split_side_menu_focus_button) { + mListener.onFocusStage(SPLIT_POSITION_BOTTOM_OR_RIGHT); + } else if (id == R.id.tv_split_side_menu_close_button) { + mListener.onCloseStage(SPLIT_POSITION_BOTTOM_OR_RIGHT); + } else if (id == R.id.tv_split_menu_swap_stages) { + mListener.onSwapPress(); + } + } + + + @Override + public boolean dispatchKeyEvent(KeyEvent event) { + if (event.getAction() == ACTION_DOWN) { + if (event.getKeyCode() == KEYCODE_BACK) { + if (mListener != null) { + mListener.onBackPress(); + return true; + } + } + } + return super.dispatchKeyEvent(event); + } + + private void initButtons() { + findViewById(R.id.tv_split_main_menu_focus_button).setOnClickListener(this); + findViewById(R.id.tv_split_main_menu_close_button).setOnClickListener(this); + findViewById(R.id.tv_split_side_menu_focus_button).setOnClickListener(this); + findViewById(R.id.tv_split_side_menu_close_button).setOnClickListener(this); + findViewById(R.id.tv_split_menu_swap_stages).setOnClickListener(this); + } + + void setListener(Listener listener) { + mListener = listener; + } + + interface Listener { + /** "Back" button from the remote control */ + void onBackPress(); + + /** Menu Action Buttons */ + + void onFocusStage(@SplitScreenConstants.SplitPosition int stageToFocus); + + void onCloseStage(@SplitScreenConstants.SplitPosition int stageToClose); + + void onSwapPress(); + } +} diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/tv/TvSplitScreenController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/tv/TvSplitScreenController.java new file mode 100644 index 0000000000000..9285e845a1581 --- /dev/null +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/tv/TvSplitScreenController.java @@ -0,0 +1,118 @@ +/* + * Copyright (C) 2022 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.wm.shell.splitscreen.tv; + +import static android.view.Display.DEFAULT_DISPLAY; + +import android.content.Context; +import android.os.Handler; + +import com.android.launcher3.icons.IconProvider; +import com.android.wm.shell.RootTaskDisplayAreaOrganizer; +import com.android.wm.shell.ShellTaskOrganizer; +import com.android.wm.shell.common.DisplayController; +import com.android.wm.shell.common.DisplayImeController; +import com.android.wm.shell.common.DisplayInsetsController; +import com.android.wm.shell.common.ShellExecutor; +import com.android.wm.shell.common.SyncTransactionQueue; +import com.android.wm.shell.common.SystemWindows; +import com.android.wm.shell.common.TransactionPool; +import com.android.wm.shell.draganddrop.DragAndDropController; +import com.android.wm.shell.recents.RecentTasksController; +import com.android.wm.shell.splitscreen.SplitScreenController; +import com.android.wm.shell.splitscreen.SplitscreenEventLogger; +import com.android.wm.shell.splitscreen.StageCoordinator; +import com.android.wm.shell.sysui.ShellCommandHandler; +import com.android.wm.shell.sysui.ShellController; +import com.android.wm.shell.sysui.ShellInit; +import com.android.wm.shell.transition.Transitions; + +import java.util.Optional; + +/** + * Class inherits from {@link SplitScreenController} and provides {@link TvStageCoordinator} + * for Split Screen on TV. + */ +public class TvSplitScreenController extends SplitScreenController { + private final ShellTaskOrganizer mTaskOrganizer; + private final SyncTransactionQueue mSyncQueue; + private final Context mContext; + private final ShellExecutor mMainExecutor; + private final DisplayController mDisplayController; + private final DisplayImeController mDisplayImeController; + private final DisplayInsetsController mDisplayInsetsController; + private final Transitions mTransitions; + private final TransactionPool mTransactionPool; + private final IconProvider mIconProvider; + private final Optional mRecentTasksOptional; + + private final Handler mMainHandler; + private final SystemWindows mSystemWindows; + + public TvSplitScreenController(Context context, + ShellInit shellInit, + ShellCommandHandler shellCommandHandler, + ShellController shellController, + ShellTaskOrganizer shellTaskOrganizer, + SyncTransactionQueue syncQueue, + RootTaskDisplayAreaOrganizer rootTDAOrganizer, + DisplayController displayController, + DisplayImeController displayImeController, + DisplayInsetsController displayInsetsController, + DragAndDropController dragAndDropController, + Transitions transitions, + TransactionPool transactionPool, + IconProvider iconProvider, + Optional recentTasks, + ShellExecutor mainExecutor, + Handler mainHandler, + SystemWindows systemWindows) { + super(context, shellInit, shellCommandHandler, shellController, shellTaskOrganizer, + syncQueue, rootTDAOrganizer, displayController, displayImeController, + displayInsetsController, dragAndDropController, transitions, transactionPool, + iconProvider, recentTasks, mainExecutor); + + mTaskOrganizer = shellTaskOrganizer; + mSyncQueue = syncQueue; + mContext = context; + mMainExecutor = mainExecutor; + mDisplayController = displayController; + mDisplayImeController = displayImeController; + mDisplayInsetsController = displayInsetsController; + mTransitions = transitions; + mTransactionPool = transactionPool; + mIconProvider = iconProvider; + mRecentTasksOptional = recentTasks; + + mMainHandler = mainHandler; + mSystemWindows = systemWindows; + } + + /** + * Provides Tv-specific StageCoordinator. + * @return {@link TvStageCoordinator} + */ + @Override + protected StageCoordinator createStageCoordinator() { + return new TvStageCoordinator(mContext, DEFAULT_DISPLAY, mSyncQueue, + mTaskOrganizer, mDisplayController, mDisplayImeController, + mDisplayInsetsController, mTransitions, mTransactionPool, + new SplitscreenEventLogger(), mIconProvider, mMainExecutor, mMainHandler, + mRecentTasksOptional, mSystemWindows); + } + +} diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/tv/TvStageCoordinator.java b/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/tv/TvStageCoordinator.java new file mode 100644 index 0000000000000..6595beb59adbe --- /dev/null +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/tv/TvStageCoordinator.java @@ -0,0 +1,95 @@ +/* + * Copyright (C) 2022 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.wm.shell.splitscreen.tv; + +import android.content.Context; +import android.os.Handler; + +import com.android.launcher3.icons.IconProvider; +import com.android.wm.shell.ShellTaskOrganizer; +import com.android.wm.shell.common.DisplayController; +import com.android.wm.shell.common.DisplayImeController; +import com.android.wm.shell.common.DisplayInsetsController; +import com.android.wm.shell.common.ShellExecutor; +import com.android.wm.shell.common.SyncTransactionQueue; +import com.android.wm.shell.common.SystemWindows; +import com.android.wm.shell.common.TransactionPool; +import com.android.wm.shell.common.split.SplitScreenConstants; +import com.android.wm.shell.recents.RecentTasksController; +import com.android.wm.shell.splitscreen.SplitscreenEventLogger; +import com.android.wm.shell.splitscreen.StageCoordinator; +import com.android.wm.shell.transition.Transitions; + +import java.util.Optional; + +/** + * Expands {@link StageCoordinator} functionality with Tv-specific methods. + */ +public class TvStageCoordinator extends StageCoordinator + implements TvSplitMenuController.StageController { + + private final TvSplitMenuController mTvSplitMenuController; + + public TvStageCoordinator(Context context, int displayId, SyncTransactionQueue syncQueue, + ShellTaskOrganizer taskOrganizer, DisplayController displayController, + DisplayImeController displayImeController, + DisplayInsetsController displayInsetsController, Transitions transitions, + TransactionPool transactionPool, SplitscreenEventLogger logger, + IconProvider iconProvider, ShellExecutor mainExecutor, + Handler mainHandler, + Optional recentTasks, + SystemWindows systemWindows) { + super(context, displayId, syncQueue, taskOrganizer, displayController, displayImeController, + displayInsetsController, transitions, transactionPool, logger, iconProvider, + mainExecutor, recentTasks); + + mTvSplitMenuController = new TvSplitMenuController(context, this, + systemWindows, mainHandler); + + } + + @Override + protected void onSplitScreenEnter() { + mTvSplitMenuController.addSplitMenuViewToSystemWindows(); + mTvSplitMenuController.registerBroadcastReceiver(); + } + + @Override + protected void onSplitScreenExit() { + mTvSplitMenuController.unregisterBroadcastReceiver(); + mTvSplitMenuController.removeSplitMenuViewFromSystemWindows(); + } + + @Override + public void grantFocusToStage(@SplitScreenConstants.SplitPosition int stageToFocus) { + super.grantFocusToStage(stageToFocus); + } + + @Override + public void exitStage(@SplitScreenConstants.SplitPosition int stageToClose) { + super.exitStage(stageToClose); + } + + /** + * Swaps the stages inside the SplitLayout. + */ + @Override + public void swapStages() { + onDoubleTappedDivider(); + } + +}