1 /* 2 * Copyright (C) 2025 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 17 package com.android.wm.shell.appzoomout; 18 19 import static android.app.WindowConfiguration.ROTATION_UNDEFINED; 20 import static android.view.Display.DEFAULT_DISPLAY; 21 import static com.android.systemui.Flags.spatialModelAppPushback; 22 23 import android.app.ActivityManager; 24 import android.app.WindowConfiguration; 25 import android.content.Context; 26 import android.content.res.Configuration; 27 import android.util.Slog; 28 import android.window.DisplayAreaInfo; 29 import android.window.WindowContainerTransaction; 30 31 import androidx.annotation.Nullable; 32 import androidx.annotation.VisibleForTesting; 33 34 import com.android.wm.shell.ShellTaskOrganizer; 35 import com.android.wm.shell.common.DisplayChangeController; 36 import com.android.wm.shell.common.DisplayController; 37 import com.android.wm.shell.common.DisplayLayout; 38 import com.android.wm.shell.common.RemoteCallable; 39 import com.android.wm.shell.common.ShellExecutor; 40 import com.android.wm.shell.shared.annotations.ExternalThread; 41 import com.android.wm.shell.shared.annotations.ShellMainThread; 42 import com.android.wm.shell.sysui.ShellInit; 43 44 /** Class that manages the app zoom out UI and states. */ 45 public class AppZoomOutController implements RemoteCallable<AppZoomOutController>, 46 ShellTaskOrganizer.FocusListener, DisplayChangeController.OnDisplayChangingListener { 47 48 private static final String TAG = "AppZoomOutController"; 49 50 private final Context mContext; 51 private final ShellTaskOrganizer mTaskOrganizer; 52 private final DisplayController mDisplayController; 53 private final AppZoomOutDisplayAreaOrganizer mDisplayAreaOrganizer; 54 private final ShellExecutor mMainExecutor; 55 private final AppZoomOutImpl mImpl = new AppZoomOutImpl(); 56 57 private final DisplayController.OnDisplaysChangedListener mDisplaysChangedListener = 58 new DisplayController.OnDisplaysChangedListener() { 59 @Override 60 public void onDisplayConfigurationChanged(int displayId, Configuration newConfig) { 61 if (displayId != DEFAULT_DISPLAY) { 62 return; 63 } 64 updateDisplayLayout(displayId); 65 } 66 67 @Override 68 public void onDisplayAdded(int displayId) { 69 if (displayId != DEFAULT_DISPLAY) { 70 return; 71 } 72 updateDisplayLayout(displayId); 73 } 74 }; 75 76 create(Context context, ShellInit shellInit, ShellTaskOrganizer shellTaskOrganizer, DisplayController displayController, DisplayLayout displayLayout, @ShellMainThread ShellExecutor mainExecutor)77 public static AppZoomOutController create(Context context, ShellInit shellInit, 78 ShellTaskOrganizer shellTaskOrganizer, DisplayController displayController, 79 DisplayLayout displayLayout, @ShellMainThread ShellExecutor mainExecutor) { 80 AppZoomOutDisplayAreaOrganizer displayAreaOrganizer = new AppZoomOutDisplayAreaOrganizer( 81 context, displayLayout, mainExecutor); 82 return new AppZoomOutController(context, shellInit, shellTaskOrganizer, displayController, 83 displayAreaOrganizer, mainExecutor); 84 } 85 86 @VisibleForTesting AppZoomOutController(Context context, ShellInit shellInit, ShellTaskOrganizer shellTaskOrganizer, DisplayController displayController, AppZoomOutDisplayAreaOrganizer displayAreaOrganizer, @ShellMainThread ShellExecutor mainExecutor)87 AppZoomOutController(Context context, ShellInit shellInit, 88 ShellTaskOrganizer shellTaskOrganizer, DisplayController displayController, 89 AppZoomOutDisplayAreaOrganizer displayAreaOrganizer, 90 @ShellMainThread ShellExecutor mainExecutor) { 91 mContext = context; 92 mTaskOrganizer = shellTaskOrganizer; 93 mDisplayController = displayController; 94 mDisplayAreaOrganizer = displayAreaOrganizer; 95 mMainExecutor = mainExecutor; 96 97 if (spatialModelAppPushback()) { 98 shellInit.addInitCallback(this::onInit, this); 99 } 100 } 101 onInit()102 private void onInit() { 103 mTaskOrganizer.addFocusListener(this); 104 105 mDisplayController.addDisplayWindowListener(mDisplaysChangedListener); 106 mDisplayController.addDisplayChangingController(this); 107 updateDisplayLayout(mContext.getDisplayId()); 108 109 mDisplayAreaOrganizer.registerOrganizer(); 110 } 111 asAppZoomOut()112 public AppZoomOut asAppZoomOut() { 113 return mImpl; 114 } 115 setProgress(float progress)116 public void setProgress(float progress) { 117 mDisplayAreaOrganizer.setProgress(progress); 118 } 119 updateDisplayLayout(int displayId)120 void updateDisplayLayout(int displayId) { 121 final DisplayLayout newDisplayLayout = mDisplayController.getDisplayLayout(displayId); 122 if (newDisplayLayout == null) { 123 Slog.w(TAG, "Failed to get new DisplayLayout."); 124 return; 125 } 126 mDisplayAreaOrganizer.setDisplayLayout(newDisplayLayout); 127 } 128 129 @Override onFocusTaskChanged(ActivityManager.RunningTaskInfo taskInfo)130 public void onFocusTaskChanged(ActivityManager.RunningTaskInfo taskInfo) { 131 if (taskInfo == null) { 132 return; 133 } 134 if (taskInfo.getActivityType() == WindowConfiguration.ACTIVITY_TYPE_HOME) { 135 mDisplayAreaOrganizer.setIsHomeTaskFocused(taskInfo.isFocused); 136 } 137 } 138 139 @Override onDisplayChange(int displayId, int fromRotation, int toRotation, @Nullable DisplayAreaInfo newDisplayAreaInfo, WindowContainerTransaction wct)140 public void onDisplayChange(int displayId, int fromRotation, int toRotation, 141 @Nullable DisplayAreaInfo newDisplayAreaInfo, WindowContainerTransaction wct) { 142 // TODO: verify if there is synchronization issues. 143 if (toRotation != ROTATION_UNDEFINED) { 144 mDisplayAreaOrganizer.onRotateDisplay(mContext, toRotation); 145 } 146 } 147 148 @Override getContext()149 public Context getContext() { 150 return mContext; 151 } 152 153 @Override getRemoteCallExecutor()154 public ShellExecutor getRemoteCallExecutor() { 155 return mMainExecutor; 156 } 157 158 @ExternalThread 159 private class AppZoomOutImpl implements AppZoomOut { 160 @Override setProgress(float progress)161 public void setProgress(float progress) { 162 mMainExecutor.execute(() -> AppZoomOutController.this.setProgress(progress)); 163 } 164 } 165 } 166