Evan Rosky | 69cace4 | 2019-09-20 16:28:13 -0700 | [diff] [blame] | 1 | /* |
| 2 | * Copyright (C) 2019 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.systemui.wm; |
| 18 | |
Evan Rosky | cfa31de | 2019-11-21 19:09:43 -0800 | [diff] [blame] | 19 | import android.annotation.Nullable; |
| 20 | import android.content.Context; |
Evan Rosky | 8220708 | 2019-10-08 17:28:29 -0700 | [diff] [blame] | 21 | import android.content.res.Configuration; |
Evan Rosky | cfa31de | 2019-11-21 19:09:43 -0800 | [diff] [blame] | 22 | import android.hardware.display.DisplayManager; |
Evan Rosky | 69cace4 | 2019-09-20 16:28:13 -0700 | [diff] [blame] | 23 | import android.os.Handler; |
| 24 | import android.os.RemoteException; |
Evan Rosky | 8220708 | 2019-10-08 17:28:29 -0700 | [diff] [blame] | 25 | import android.util.Slog; |
Evan Rosky | 69cace4 | 2019-09-20 16:28:13 -0700 | [diff] [blame] | 26 | import android.util.SparseArray; |
Evan Rosky | cfa31de | 2019-11-21 19:09:43 -0800 | [diff] [blame] | 27 | import android.view.Display; |
Evan Rosky | 69cace4 | 2019-09-20 16:28:13 -0700 | [diff] [blame] | 28 | import android.view.IDisplayWindowListener; |
Evan Rosky | cfa31de | 2019-11-21 19:09:43 -0800 | [diff] [blame] | 29 | import android.view.IWindowManager; |
Evan Rosky | 69cace4 | 2019-09-20 16:28:13 -0700 | [diff] [blame] | 30 | |
Dave Mankoff | 00e8a2f | 2019-12-18 16:59:49 -0500 | [diff] [blame] | 31 | import com.android.systemui.dagger.qualifiers.Main; |
Winson Chung | 95c9fca | 2020-01-22 09:48:40 -0800 | [diff] [blame] | 32 | import com.android.systemui.wm.DisplayChangeController.OnDisplayChangingListener; |
Evan Rosky | 69cace4 | 2019-09-20 16:28:13 -0700 | [diff] [blame] | 33 | |
| 34 | import java.util.ArrayList; |
| 35 | |
| 36 | import javax.inject.Inject; |
| 37 | import javax.inject.Singleton; |
| 38 | |
| 39 | /** |
| 40 | * This module deals with display rotations coming from WM. When WM starts a rotation: after it has |
| 41 | * frozen the screen, it will call into this class. This will then call all registered local |
| 42 | * controllers and give them a chance to queue up task changes to be applied synchronously with that |
| 43 | * rotation. |
| 44 | */ |
| 45 | @Singleton |
Winson Chung | 95c9fca | 2020-01-22 09:48:40 -0800 | [diff] [blame] | 46 | public class DisplayController { |
| 47 | private static final String TAG = "DisplayController"; |
Evan Rosky | 8220708 | 2019-10-08 17:28:29 -0700 | [diff] [blame] | 48 | |
Evan Rosky | 69cace4 | 2019-09-20 16:28:13 -0700 | [diff] [blame] | 49 | private final Handler mHandler; |
Evan Rosky | cfa31de | 2019-11-21 19:09:43 -0800 | [diff] [blame] | 50 | private final Context mContext; |
| 51 | private final IWindowManager mWmService; |
Winson Chung | 95c9fca | 2020-01-22 09:48:40 -0800 | [diff] [blame] | 52 | private final DisplayChangeController mChangeController; |
Evan Rosky | 69cace4 | 2019-09-20 16:28:13 -0700 | [diff] [blame] | 53 | |
| 54 | private final SparseArray<DisplayRecord> mDisplays = new SparseArray<>(); |
Winson Chung | 95c9fca | 2020-01-22 09:48:40 -0800 | [diff] [blame] | 55 | private final ArrayList<OnDisplaysChangedListener> mDisplayChangedListeners = new ArrayList<>(); |
Evan Rosky | 69cace4 | 2019-09-20 16:28:13 -0700 | [diff] [blame] | 56 | |
Evan Rosky | cfa31de | 2019-11-21 19:09:43 -0800 | [diff] [blame] | 57 | /** |
| 58 | * Get's a display by id from DisplayManager. |
| 59 | */ |
| 60 | public Display getDisplay(int displayId) { |
| 61 | final DisplayManager displayManager = mContext.getSystemService(DisplayManager.class); |
| 62 | return displayManager.getDisplay(displayId); |
| 63 | } |
| 64 | |
Evan Rosky | 69cace4 | 2019-09-20 16:28:13 -0700 | [diff] [blame] | 65 | private final IDisplayWindowListener mDisplayContainerListener = |
| 66 | new IDisplayWindowListener.Stub() { |
| 67 | @Override |
| 68 | public void onDisplayAdded(int displayId) { |
| 69 | mHandler.post(() -> { |
| 70 | synchronized (mDisplays) { |
| 71 | if (mDisplays.get(displayId) != null) { |
| 72 | return; |
| 73 | } |
Evan Rosky | e577f78 | 2019-12-19 17:19:32 -0800 | [diff] [blame] | 74 | Display display = getDisplay(displayId); |
| 75 | if (display == null) { |
| 76 | // It's likely that the display is private to some app and thus not |
| 77 | // accessible by system-ui. |
| 78 | return; |
| 79 | } |
Evan Rosky | 69cace4 | 2019-09-20 16:28:13 -0700 | [diff] [blame] | 80 | DisplayRecord record = new DisplayRecord(); |
| 81 | record.mDisplayId = displayId; |
Evan Rosky | e577f78 | 2019-12-19 17:19:32 -0800 | [diff] [blame] | 82 | record.mContext = (displayId == Display.DEFAULT_DISPLAY) ? mContext |
| 83 | : mContext.createDisplayContext(display); |
| 84 | record.mDisplayLayout = new DisplayLayout(record.mContext, display); |
Evan Rosky | 69cace4 | 2019-09-20 16:28:13 -0700 | [diff] [blame] | 85 | mDisplays.put(displayId, record); |
Evan Rosky | 8220708 | 2019-10-08 17:28:29 -0700 | [diff] [blame] | 86 | for (int i = 0; i < mDisplayChangedListeners.size(); ++i) { |
| 87 | mDisplayChangedListeners.get(i).onDisplayAdded(displayId); |
| 88 | } |
| 89 | } |
| 90 | }); |
| 91 | } |
| 92 | |
| 93 | @Override |
| 94 | public void onDisplayConfigurationChanged(int displayId, Configuration newConfig) { |
| 95 | mHandler.post(() -> { |
| 96 | synchronized (mDisplays) { |
| 97 | DisplayRecord dr = mDisplays.get(displayId); |
| 98 | if (dr == null) { |
| 99 | Slog.w(TAG, "Skipping Display Configuration change on non-added" |
| 100 | + " display."); |
| 101 | return; |
| 102 | } |
Evan Rosky | e577f78 | 2019-12-19 17:19:32 -0800 | [diff] [blame] | 103 | Display display = getDisplay(displayId); |
| 104 | Context perDisplayContext = mContext; |
| 105 | if (displayId != Display.DEFAULT_DISPLAY) { |
| 106 | perDisplayContext = mContext.createDisplayContext(display); |
| 107 | } |
| 108 | dr.mContext = perDisplayContext.createConfigurationContext(newConfig); |
| 109 | dr.mDisplayLayout = new DisplayLayout(dr.mContext, display); |
Evan Rosky | 8220708 | 2019-10-08 17:28:29 -0700 | [diff] [blame] | 110 | for (int i = 0; i < mDisplayChangedListeners.size(); ++i) { |
| 111 | mDisplayChangedListeners.get(i).onDisplayConfigurationChanged( |
| 112 | displayId, newConfig); |
Evan Rosky | 69cace4 | 2019-09-20 16:28:13 -0700 | [diff] [blame] | 113 | } |
| 114 | } |
| 115 | }); |
| 116 | } |
| 117 | |
| 118 | @Override |
| 119 | public void onDisplayRemoved(int displayId) { |
| 120 | mHandler.post(() -> { |
| 121 | synchronized (mDisplays) { |
Evan Rosky | e577f78 | 2019-12-19 17:19:32 -0800 | [diff] [blame] | 122 | if (mDisplays.get(displayId) == null) { |
| 123 | return; |
| 124 | } |
Evan Rosky | 69cace4 | 2019-09-20 16:28:13 -0700 | [diff] [blame] | 125 | for (int i = mDisplayChangedListeners.size() - 1; i >= 0; --i) { |
| 126 | mDisplayChangedListeners.get(i).onDisplayRemoved(displayId); |
| 127 | } |
| 128 | mDisplays.remove(displayId); |
| 129 | } |
| 130 | }); |
| 131 | } |
| 132 | }; |
| 133 | |
| 134 | @Inject |
Winson Chung | 95c9fca | 2020-01-22 09:48:40 -0800 | [diff] [blame] | 135 | public DisplayController(Context context, @Main Handler mainHandler, |
Evan Rosky | cfa31de | 2019-11-21 19:09:43 -0800 | [diff] [blame] | 136 | IWindowManager wmService) { |
Evan Rosky | 69cace4 | 2019-09-20 16:28:13 -0700 | [diff] [blame] | 137 | mHandler = mainHandler; |
Evan Rosky | cfa31de | 2019-11-21 19:09:43 -0800 | [diff] [blame] | 138 | mContext = context; |
| 139 | mWmService = wmService; |
Winson Chung | 95c9fca | 2020-01-22 09:48:40 -0800 | [diff] [blame] | 140 | mChangeController = new DisplayChangeController(mHandler, mWmService); |
Evan Rosky | 69cace4 | 2019-09-20 16:28:13 -0700 | [diff] [blame] | 141 | try { |
Evan Rosky | cfa31de | 2019-11-21 19:09:43 -0800 | [diff] [blame] | 142 | mWmService.registerDisplayWindowListener(mDisplayContainerListener); |
Evan Rosky | 69cace4 | 2019-09-20 16:28:13 -0700 | [diff] [blame] | 143 | } catch (RemoteException e) { |
| 144 | throw new RuntimeException("Unable to register hierarchy listener"); |
| 145 | } |
| 146 | } |
| 147 | |
| 148 | /** |
Evan Rosky | cfa31de | 2019-11-21 19:09:43 -0800 | [diff] [blame] | 149 | * Gets the DisplayLayout associated with a display. |
| 150 | */ |
| 151 | public @Nullable DisplayLayout getDisplayLayout(int displayId) { |
| 152 | final DisplayRecord r = mDisplays.get(displayId); |
| 153 | return r != null ? r.mDisplayLayout : null; |
| 154 | } |
| 155 | |
| 156 | /** |
| 157 | * Gets a display-specific context for a display. |
| 158 | */ |
| 159 | public @Nullable Context getDisplayContext(int displayId) { |
| 160 | final DisplayRecord r = mDisplays.get(displayId); |
| 161 | return r != null ? r.mContext : null; |
| 162 | } |
| 163 | |
| 164 | /** |
Evan Rosky | 8220708 | 2019-10-08 17:28:29 -0700 | [diff] [blame] | 165 | * Add a display window-container listener. It will get notified whenever a display's |
| 166 | * configuration changes or when displays are added/removed from the WM hierarchy. |
Evan Rosky | 69cace4 | 2019-09-20 16:28:13 -0700 | [diff] [blame] | 167 | */ |
Winson Chung | 95c9fca | 2020-01-22 09:48:40 -0800 | [diff] [blame] | 168 | public void addDisplayWindowListener(OnDisplaysChangedListener listener) { |
Evan Rosky | 69cace4 | 2019-09-20 16:28:13 -0700 | [diff] [blame] | 169 | synchronized (mDisplays) { |
| 170 | if (mDisplayChangedListeners.contains(listener)) { |
| 171 | return; |
| 172 | } |
| 173 | mDisplayChangedListeners.add(listener); |
| 174 | for (int i = 0; i < mDisplays.size(); ++i) { |
| 175 | listener.onDisplayAdded(mDisplays.keyAt(i)); |
| 176 | } |
| 177 | } |
| 178 | } |
| 179 | |
| 180 | /** |
| 181 | * Remove a display window-container listener. |
| 182 | */ |
Winson Chung | 95c9fca | 2020-01-22 09:48:40 -0800 | [diff] [blame] | 183 | public void removeDisplayWindowListener(OnDisplaysChangedListener listener) { |
Evan Rosky | 69cace4 | 2019-09-20 16:28:13 -0700 | [diff] [blame] | 184 | synchronized (mDisplays) { |
| 185 | mDisplayChangedListeners.remove(listener); |
| 186 | } |
| 187 | } |
| 188 | |
| 189 | /** |
| 190 | * Adds a display rotation controller. |
| 191 | */ |
Winson Chung | 95c9fca | 2020-01-22 09:48:40 -0800 | [diff] [blame] | 192 | public void addDisplayChangingController(OnDisplayChangingListener controller) { |
| 193 | mChangeController.addRotationListener(controller); |
Evan Rosky | 69cace4 | 2019-09-20 16:28:13 -0700 | [diff] [blame] | 194 | } |
| 195 | |
| 196 | /** |
| 197 | * Removes a display rotation controller. |
| 198 | */ |
Winson Chung | 95c9fca | 2020-01-22 09:48:40 -0800 | [diff] [blame] | 199 | public void removeDisplayChangingController(OnDisplayChangingListener controller) { |
| 200 | mChangeController.removeRotationListener(controller); |
Evan Rosky | 69cace4 | 2019-09-20 16:28:13 -0700 | [diff] [blame] | 201 | } |
| 202 | |
| 203 | private static class DisplayRecord { |
| 204 | int mDisplayId; |
Evan Rosky | cfa31de | 2019-11-21 19:09:43 -0800 | [diff] [blame] | 205 | Context mContext; |
| 206 | DisplayLayout mDisplayLayout; |
Evan Rosky | 69cace4 | 2019-09-20 16:28:13 -0700 | [diff] [blame] | 207 | } |
| 208 | |
| 209 | /** |
Evan Rosky | 8220708 | 2019-10-08 17:28:29 -0700 | [diff] [blame] | 210 | * Gets notified when a display is added/removed to the WM hierarchy and when a display's |
| 211 | * window-configuration changes. |
Evan Rosky | 69cace4 | 2019-09-20 16:28:13 -0700 | [diff] [blame] | 212 | * |
| 213 | * @see IDisplayWindowListener |
| 214 | */ |
Winson Chung | 95c9fca | 2020-01-22 09:48:40 -0800 | [diff] [blame] | 215 | public interface OnDisplaysChangedListener { |
Evan Rosky | 69cace4 | 2019-09-20 16:28:13 -0700 | [diff] [blame] | 216 | /** |
| 217 | * Called when a display has been added to the WM hierarchy. |
| 218 | */ |
Winson Chung | 95c9fca | 2020-01-22 09:48:40 -0800 | [diff] [blame] | 219 | default void onDisplayAdded(int displayId) {} |
Evan Rosky | 69cace4 | 2019-09-20 16:28:13 -0700 | [diff] [blame] | 220 | |
| 221 | /** |
Evan Rosky | 8220708 | 2019-10-08 17:28:29 -0700 | [diff] [blame] | 222 | * Called when a display's window-container configuration changes. |
| 223 | */ |
Winson Chung | 95c9fca | 2020-01-22 09:48:40 -0800 | [diff] [blame] | 224 | default void onDisplayConfigurationChanged(int displayId, Configuration newConfig) {} |
Evan Rosky | 8220708 | 2019-10-08 17:28:29 -0700 | [diff] [blame] | 225 | |
| 226 | /** |
Evan Rosky | 69cace4 | 2019-09-20 16:28:13 -0700 | [diff] [blame] | 227 | * Called when a display is removed. |
| 228 | */ |
Winson Chung | 95c9fca | 2020-01-22 09:48:40 -0800 | [diff] [blame] | 229 | default void onDisplayRemoved(int displayId) {} |
Evan Rosky | 69cace4 | 2019-09-20 16:28:13 -0700 | [diff] [blame] | 230 | } |
| 231 | } |