blob: 64cac84ff24e1ae14f2b9e3703a1bb5ed9c52b54 [file] [log] [blame]
/*
* Copyright (C) 2019 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.systemui.wm;
import static android.view.WindowManager.LayoutParams.FLAG_HARDWARE_ACCELERATED;
import android.annotation.NonNull;
import android.content.Context;
import android.content.res.Configuration;
import android.graphics.Point;
import android.graphics.Rect;
import android.graphics.Region;
import android.os.Bundle;
import android.os.IBinder;
import android.os.ParcelFileDescriptor;
import android.os.RemoteException;
import android.util.MergedConfiguration;
import android.util.Slog;
import android.util.SparseArray;
import android.view.Display;
import android.view.DisplayCutout;
import android.view.DragEvent;
import android.view.IScrollCaptureController;
import android.view.IWindow;
import android.view.IWindowManager;
import android.view.IWindowSession;
import android.view.IWindowSessionCallback;
import android.view.InsetsSourceControl;
import android.view.InsetsState;
import android.view.SurfaceControl;
import android.view.SurfaceControlViewHost;
import android.view.View;
import android.view.ViewGroup;
import android.view.WindowManager;
import android.view.WindowlessWindowManager;
import com.android.internal.os.IResultReceiver;
import java.util.HashMap;
import javax.inject.Inject;
import javax.inject.Singleton;
/**
* Represents the "windowing" layer of the System-UI. This layer allows system-ui components to
* place and manipulate windows without talking to WindowManager.
*/
@Singleton
public class SystemWindows {
private static final String TAG = "SystemWindows";
private final SparseArray<PerDisplay> mPerDisplay = new SparseArray<>();
final HashMap<View, SurfaceControlViewHost> mViewRoots = new HashMap<>();
Context mContext;
IWindowSession mSession;
DisplayController mDisplayController;
IWindowManager mWmService;
private final DisplayController.OnDisplaysChangedListener mDisplayListener =
new DisplayController.OnDisplaysChangedListener() {
@Override
public void onDisplayAdded(int displayId) { }
@Override
public void onDisplayConfigurationChanged(int displayId, Configuration newConfig) {
PerDisplay pd = mPerDisplay.get(displayId);
if (pd == null) {
return;
}
pd.updateConfiguration(newConfig);
}
@Override
public void onDisplayRemoved(int displayId) { }
};
@Inject
public SystemWindows(Context context, DisplayController displayController,
IWindowManager wmService) {
mContext = context;
mWmService = wmService;
mDisplayController = displayController;
mDisplayController.addDisplayWindowListener(mDisplayListener);
try {
mSession = wmService.openSession(
new IWindowSessionCallback.Stub() {
@Override
public void onAnimatorScaleChanged(float scale) {}
});
} catch (RemoteException e) {
Slog.e(TAG, "Unable to create layer", e);
}
}
/**
* Adds a view to system-ui window management.
*/
public void addView(View view, WindowManager.LayoutParams attrs, int displayId,
int windowType) {
PerDisplay pd = mPerDisplay.get(displayId);
if (pd == null) {
pd = new PerDisplay(displayId);
mPerDisplay.put(displayId, pd);
}
pd.addView(view, attrs, windowType);
}
/**
* Removes a view from system-ui window management.
* @param view
*/
public void removeView(View view) {
SurfaceControlViewHost root = mViewRoots.remove(view);
root.die();
}
/**
* Updates the layout params of a view.
*/
public void updateViewLayout(@NonNull View view, ViewGroup.LayoutParams params) {
SurfaceControlViewHost root = mViewRoots.get(view);
if (root == null || !(params instanceof WindowManager.LayoutParams)) {
return;
}
view.setLayoutParams(params);
root.relayout((WindowManager.LayoutParams) params);
}
/**
* Sets the touchable region of a view's window. This will be cropped to the window size.
* @param view
* @param region
*/
public void setTouchableRegion(@NonNull View view, Region region) {
SurfaceControlViewHost root = mViewRoots.get(view);
if (root == null) {
return;
}
WindowlessWindowManager wwm = root.getWindowlessWM();
if (!(wwm instanceof SysUiWindowManager)) {
return;
}
((SysUiWindowManager) wwm).setTouchableRegionForWindow(view, region);
}
/**
* Adds a root for system-ui window management with no views. Only useful for IME.
*/
public void addRoot(int displayId, int windowType) {
PerDisplay pd = mPerDisplay.get(displayId);
if (pd == null) {
pd = new PerDisplay(displayId);
mPerDisplay.put(displayId, pd);
}
pd.addRoot(windowType);
}
/**
* Get the IWindow token for a specific root.
*
* @param windowType A window type from {@link android.view.WindowManager}.
*/
IWindow getWindow(int displayId, int windowType) {
PerDisplay pd = mPerDisplay.get(displayId);
if (pd == null) {
return null;
}
return pd.getWindow(windowType);
}
/**
* Gets the SurfaceControl associated with a root view. This is the same surface that backs the
* ViewRootImpl.
*/
public SurfaceControl getViewSurface(View rootView) {
for (int i = 0; i < mPerDisplay.size(); ++i) {
for (int iWm = 0; iWm < mPerDisplay.valueAt(i).mWwms.size(); ++iWm) {
SurfaceControl out =
mPerDisplay.valueAt(i).mWwms.get(iWm).getSurfaceControlForWindow(rootView);
if (out != null) {
return out;
}
}
}
return null;
}
private class PerDisplay {
final int mDisplayId;
private final SparseArray<SysUiWindowManager> mWwms = new SparseArray<>();
PerDisplay(int displayId) {
mDisplayId = displayId;
}
public void addView(View view, WindowManager.LayoutParams attrs, int windowType) {
SysUiWindowManager wwm = addRoot(windowType);
if (wwm == null) {
Slog.e(TAG, "Unable to create systemui root");
return;
}
final Display display = mDisplayController.getDisplay(mDisplayId);
SurfaceControlViewHost viewRoot =
new SurfaceControlViewHost(mContext, display, wwm,
true /* useSfChoreographer */);
attrs.flags |= FLAG_HARDWARE_ACCELERATED;
viewRoot.setView(view, attrs);
mViewRoots.put(view, viewRoot);
try {
mWmService.setShellRootAccessibilityWindow(mDisplayId, windowType,
viewRoot.getWindowToken());
} catch (RemoteException e) {
Slog.e(TAG, "Error setting accessibility window for " + mDisplayId + ":"
+ windowType, e);
}
}
SysUiWindowManager addRoot(int windowType) {
SysUiWindowManager wwm = mWwms.get(windowType);
if (wwm != null) {
return wwm;
}
SurfaceControl rootSurface = null;
ContainerWindow win = new ContainerWindow();
try {
rootSurface = mWmService.addShellRoot(mDisplayId, win, windowType);
} catch (RemoteException e) {
}
if (rootSurface == null) {
Slog.e(TAG, "Unable to get root surfacecontrol for systemui");
return null;
}
Context displayContext = mDisplayController.getDisplayContext(mDisplayId);
wwm = new SysUiWindowManager(mDisplayId, displayContext, rootSurface, win);
mWwms.put(windowType, wwm);
return wwm;
}
IWindow getWindow(int windowType) {
SysUiWindowManager wwm = mWwms.get(windowType);
if (wwm == null) {
return null;
}
return wwm.mContainerWindow;
}
void updateConfiguration(Configuration configuration) {
for (int i = 0; i < mWwms.size(); ++i) {
mWwms.valueAt(i).updateConfiguration(configuration);
}
}
}
/**
* A subclass of WindowlessWindowManager that provides insets to its viewroots.
*/
public class SysUiWindowManager extends WindowlessWindowManager {
final int mDisplayId;
ContainerWindow mContainerWindow;
public SysUiWindowManager(int displayId, Context ctx, SurfaceControl rootSurface,
ContainerWindow container) {
super(ctx.getResources().getConfiguration(), rootSurface, null /* hostInputToken */);
mContainerWindow = container;
mDisplayId = displayId;
}
@Override
public int relayout(IWindow window, int seq, WindowManager.LayoutParams attrs,
int requestedWidth, int requestedHeight, int viewVisibility, int flags,
long frameNumber, Rect outFrame, Rect outOverscanInsets, Rect outContentInsets,
Rect outVisibleInsets, Rect outStableInsets,
DisplayCutout.ParcelableWrapper cutout, MergedConfiguration mergedConfiguration,
SurfaceControl outSurfaceControl, InsetsState outInsetsState,
InsetsSourceControl[] outActiveControls, Point outSurfaceSize,
SurfaceControl outBLASTSurfaceControl) {
int res = super.relayout(window, seq, attrs, requestedWidth, requestedHeight,
viewVisibility, flags, frameNumber, outFrame, outOverscanInsets,
outContentInsets, outVisibleInsets, outStableInsets,
cutout, mergedConfiguration, outSurfaceControl, outInsetsState,
outActiveControls, outSurfaceSize, outBLASTSurfaceControl);
if (res != 0) {
return res;
}
DisplayLayout dl = mDisplayController.getDisplayLayout(mDisplayId);
outStableInsets.set(dl.stableInsets());
return 0;
}
void updateConfiguration(Configuration configuration) {
setConfiguration(configuration);
}
SurfaceControl getSurfaceControlForWindow(View rootView) {
return getSurfaceControl(rootView);
}
void setTouchableRegionForWindow(View rootView, Region region) {
IBinder token = rootView.getWindowToken();
if (token == null) {
return;
}
setTouchRegion(token, region);
}
}
class ContainerWindow extends IWindow.Stub {
ContainerWindow() {}
@Override
public void resized(Rect frame, Rect contentInsets, Rect visibleInsets, Rect stableInsets,
boolean reportDraw, MergedConfiguration newMergedConfiguration, Rect backDropFrame,
boolean forceLayout, boolean alwaysConsumeSystemBars, int displayId,
DisplayCutout.ParcelableWrapper displayCutout) {}
@Override
public void locationInParentDisplayChanged(Point offset) {}
@Override
public void insetsChanged(InsetsState insetsState) {}
@Override
public void insetsControlChanged(InsetsState insetsState,
InsetsSourceControl[] activeControls) {}
@Override
public void showInsets(int types, boolean fromIme) {}
@Override
public void hideInsets(int types, boolean fromIme) {}
@Override
public void moved(int newX, int newY) {}
@Override
public void dispatchAppVisibility(boolean visible) {}
@Override
public void dispatchGetNewSurface() {}
@Override
public void windowFocusChanged(boolean hasFocus, boolean inTouchMode) {}
@Override
public void executeCommand(String command, String parameters, ParcelFileDescriptor out) {}
@Override
public void closeSystemDialogs(String reason) {}
@Override
public void dispatchWallpaperOffsets(float x, float y, float xStep, float yStep,
float zoom, boolean sync) {}
@Override
public void dispatchWallpaperCommand(String action, int x, int y,
int z, Bundle extras, boolean sync) {}
/* Drag/drop */
@Override
public void dispatchDragEvent(DragEvent event) {}
@Override
public void updatePointerIcon(float x, float y) {}
@Override
public void dispatchSystemUiVisibilityChanged(int seq, int globalVisibility,
int localValue, int localChanges) {}
@Override
public void dispatchWindowShown() {}
@Override
public void requestAppKeyboardShortcuts(IResultReceiver receiver, int deviceId) {}
@Override
public void dispatchPointerCaptureChanged(boolean hasCapture) {}
@Override
public void requestScrollCapture(IScrollCaptureController controller) {
try {
controller.onClientUnavailable();
} catch (RemoteException ex) {
// ignore
}
}
}
}