blob: 9bdda14cf00b0c403589cea4934f7665eddac824 [file] [log] [blame]
/*
* Copyright (C) 2021 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.common;
import android.annotation.Nullable;
import android.content.ComponentName;
import android.os.RemoteException;
import android.util.Slog;
import android.util.SparseArray;
import android.view.IDisplayWindowInsetsController;
import android.view.IWindowManager;
import android.view.InsetsSourceControl;
import android.view.InsetsState;
import android.view.WindowInsets.Type.InsetsType;
import android.view.inputmethod.ImeTracker;
import androidx.annotation.BinderThread;
import com.android.wm.shell.common.annotations.ShellMainThread;
import com.android.wm.shell.sysui.ShellInit;
import java.util.concurrent.CopyOnWriteArrayList;
/**
* Manages insets from the core.
*/
public class DisplayInsetsController implements DisplayController.OnDisplaysChangedListener {
private static final String TAG = "DisplayInsetsController";
private final IWindowManager mWmService;
private final ShellExecutor mMainExecutor;
private final DisplayController mDisplayController;
private final SparseArray<PerDisplay> mInsetsPerDisplay = new SparseArray<>();
private final SparseArray<CopyOnWriteArrayList<OnInsetsChangedListener>> mListeners =
new SparseArray<>();
public DisplayInsetsController(IWindowManager wmService,
ShellInit shellInit,
DisplayController displayController,
ShellExecutor mainExecutor) {
mWmService = wmService;
mDisplayController = displayController;
mMainExecutor = mainExecutor;
shellInit.addInitCallback(this::onInit, this);
}
/**
* Starts listening for insets for each display.
**/
public void onInit() {
mDisplayController.addDisplayWindowListener(this);
}
/**
* Adds a callback to listen for insets changes for a particular display. Note that the
* listener will not be updated with the existing state of the insets on that display.
*/
public void addInsetsChangedListener(int displayId, OnInsetsChangedListener listener) {
CopyOnWriteArrayList<OnInsetsChangedListener> listeners = mListeners.get(displayId);
if (listeners == null) {
listeners = new CopyOnWriteArrayList<>();
mListeners.put(displayId, listeners);
}
if (!listeners.contains(listener)) {
listeners.add(listener);
}
}
/**
* Removes a callback listening for insets changes from a particular display.
*/
public void removeInsetsChangedListener(int displayId, OnInsetsChangedListener listener) {
CopyOnWriteArrayList<OnInsetsChangedListener> listeners = mListeners.get(displayId);
if (listeners == null) {
return;
}
listeners.remove(listener);
}
@Override
public void onDisplayAdded(int displayId) {
PerDisplay pd = new PerDisplay(displayId);
pd.register();
mInsetsPerDisplay.put(displayId, pd);
}
@Override
public void onDisplayRemoved(int displayId) {
PerDisplay pd = mInsetsPerDisplay.get(displayId);
if (pd == null) {
return;
}
pd.unregister();
mInsetsPerDisplay.remove(displayId);
}
/**
* An implementation of {@link IDisplayWindowInsetsController} for a given display id.
**/
public class PerDisplay {
private final int mDisplayId;
private final DisplayWindowInsetsControllerImpl mInsetsControllerImpl =
new DisplayWindowInsetsControllerImpl();
public PerDisplay(int displayId) {
mDisplayId = displayId;
}
public void register() {
try {
mWmService.setDisplayWindowInsetsController(mDisplayId, mInsetsControllerImpl);
} catch (RemoteException e) {
Slog.w(TAG, "Unable to set insets controller on display " + mDisplayId);
}
}
public void unregister() {
try {
mWmService.setDisplayWindowInsetsController(mDisplayId, null);
} catch (RemoteException e) {
Slog.w(TAG, "Unable to remove insets controller on display " + mDisplayId);
}
}
private void insetsChanged(InsetsState insetsState) {
CopyOnWriteArrayList<OnInsetsChangedListener> listeners = mListeners.get(mDisplayId);
if (listeners == null) {
return;
}
mDisplayController.updateDisplayInsets(mDisplayId, insetsState);
for (OnInsetsChangedListener listener : listeners) {
listener.insetsChanged(insetsState);
}
}
private void insetsControlChanged(InsetsState insetsState,
InsetsSourceControl[] activeControls) {
CopyOnWriteArrayList<OnInsetsChangedListener> listeners = mListeners.get(mDisplayId);
if (listeners == null) {
return;
}
for (OnInsetsChangedListener listener : listeners) {
listener.insetsControlChanged(insetsState, activeControls);
}
}
private void showInsets(@InsetsType int types, boolean fromIme,
@Nullable ImeTracker.Token statsToken) {
CopyOnWriteArrayList<OnInsetsChangedListener> listeners = mListeners.get(mDisplayId);
if (listeners == null) {
ImeTracker.forLogging().onFailed(
statsToken, ImeTracker.PHASE_WM_REMOTE_INSETS_CONTROLLER);
return;
}
ImeTracker.forLogging().onProgress(
statsToken, ImeTracker.PHASE_WM_REMOTE_INSETS_CONTROLLER);
for (OnInsetsChangedListener listener : listeners) {
listener.showInsets(types, fromIme, statsToken);
}
}
private void hideInsets(@InsetsType int types, boolean fromIme,
@Nullable ImeTracker.Token statsToken) {
CopyOnWriteArrayList<OnInsetsChangedListener> listeners = mListeners.get(mDisplayId);
if (listeners == null) {
ImeTracker.forLogging().onFailed(
statsToken, ImeTracker.PHASE_WM_REMOTE_INSETS_CONTROLLER);
return;
}
ImeTracker.forLogging().onProgress(
statsToken, ImeTracker.PHASE_WM_REMOTE_INSETS_CONTROLLER);
for (OnInsetsChangedListener listener : listeners) {
listener.hideInsets(types, fromIme, statsToken);
}
}
private void topFocusedWindowChanged(ComponentName component,
@InsetsType int requestedVisibleTypes) {
CopyOnWriteArrayList<OnInsetsChangedListener> listeners = mListeners.get(mDisplayId);
if (listeners == null) {
return;
}
for (OnInsetsChangedListener listener : listeners) {
listener.topFocusedWindowChanged(component, requestedVisibleTypes);
}
}
@BinderThread
private class DisplayWindowInsetsControllerImpl
extends IDisplayWindowInsetsController.Stub {
@Override
public void topFocusedWindowChanged(ComponentName component,
@InsetsType int requestedVisibleTypes) throws RemoteException {
mMainExecutor.execute(() -> {
PerDisplay.this.topFocusedWindowChanged(component, requestedVisibleTypes);
});
}
@Override
public void insetsChanged(InsetsState insetsState) throws RemoteException {
mMainExecutor.execute(() -> {
PerDisplay.this.insetsChanged(insetsState);
});
}
@Override
public void insetsControlChanged(InsetsState insetsState,
InsetsSourceControl[] activeControls) throws RemoteException {
mMainExecutor.execute(() -> {
PerDisplay.this.insetsControlChanged(insetsState, activeControls);
});
}
@Override
public void showInsets(@InsetsType int types, boolean fromIme,
@Nullable ImeTracker.Token statsToken) throws RemoteException {
mMainExecutor.execute(() -> {
PerDisplay.this.showInsets(types, fromIme, statsToken);
});
}
@Override
public void hideInsets(@InsetsType int types, boolean fromIme,
@Nullable ImeTracker.Token statsToken) throws RemoteException {
mMainExecutor.execute(() -> {
PerDisplay.this.hideInsets(types, fromIme, statsToken);
});
}
}
}
/**
* Gets notified whenever the insets change.
*
* @see IDisplayWindowInsetsController
*/
@ShellMainThread
public interface OnInsetsChangedListener {
/**
* Called when top focused window changes to determine whether or not to take over insets
* control. Won't be called if config_remoteInsetsControllerControlsSystemBars is false.
*
* @param component The application component that is open in the top focussed window.
* @param requestedVisibleTypes The {@link InsetsType} requested visible by the focused
* window.
*/
default void topFocusedWindowChanged(ComponentName component,
@InsetsType int requestedVisibleTypes) {}
/**
* Called when the window insets configuration has changed.
*/
default void insetsChanged(InsetsState insetsState) {}
/**
* Called when this window retrieved control over a specified set of insets sources.
*/
default void insetsControlChanged(InsetsState insetsState,
InsetsSourceControl[] activeControls) {}
/**
* Called when a set of insets source window should be shown by policy.
*
* @param types {@link InsetsType} to show
* @param fromIme true if this request originated from IME (InputMethodService).
* @param statsToken the token tracking the current IME show request
* or {@code null} otherwise.
*/
default void showInsets(@InsetsType int types, boolean fromIme,
@Nullable ImeTracker.Token statsToken) {}
/**
* Called when a set of insets source window should be hidden by policy.
*
* @param types {@link InsetsType} to hide
* @param fromIme true if this request originated from IME (InputMethodService).
* @param statsToken the token tracking the current IME hide request
* or {@code null} otherwise.
*/
default void hideInsets(@InsetsType int types, boolean fromIme,
@Nullable ImeTracker.Token statsToken) {}
}
}