blob: 5aba013a7fb8e096878f01f501184ecffaadca76 [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.os.Bundle;
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.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;
DisplayWindowController mDisplayController;
IWindowManager mWmService;
private final DisplayWindowController.DisplayWindowListener mDisplayListener =
new DisplayWindowController.DisplayWindowListener() {
@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, DisplayWindowController 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);
}
/**
* 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);
}
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);
attrs.flags |= FLAG_HARDWARE_ACCELERATED;
viewRoot.addView(view, attrs);
mViewRoots.put(view, viewRoot);
}
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,
Point outSurfaceSize) {
int res = super.relayout(window, seq, attrs, requestedWidth, requestedHeight,
viewVisibility, flags, frameNumber, outFrame, outOverscanInsets,
outContentInsets, outVisibleInsets, outStableInsets,
cutout, mergedConfiguration, outSurfaceControl, outInsetsState,
outSurfaceSize);
if (res != 0) {
return res;
}
DisplayLayout dl = mDisplayController.getDisplayLayout(mDisplayId);
outStableInsets.set(dl.stableInsets());
return 0;
}
void updateConfiguration(Configuration configuration) {
setConfiguration(configuration);
}
}
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,
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) {}
}
}