Added systemui controller to control system bars.
When config_remoteInsetsControllerControlsSystemBars is true, DisplaySystemBarsController provides its own policy of how system bars are displayed for specific packages. Currently limited to only auto versions of Android. Bug: 149585273 Test: Manual, atest BarControlPolicyTest, atest InsetsPolicyTest, atest DisplaySystemBarsControllerTest Change-Id: Ie6b1cc3e2760cbc9e48d62dfbd8bc3e23ffca20c Merged-In: Ie6b1cc3e2760cbc9e48d62dfbd8bc3e23ffca20c
This commit is contained in:
parent
ccf383243a
commit
2c403a6526
@ -34,6 +34,7 @@ package android {
|
||||
public static final class R.bool {
|
||||
field public static final int config_assistantOnTopOfDream = 17891333; // 0x1110005
|
||||
field public static final int config_perDisplayFocusEnabled = 17891332; // 0x1110004
|
||||
field public static final int config_remoteInsetsControllerControlsSystemBars = 17891334; // 0x1110006
|
||||
}
|
||||
|
||||
public static final class R.string {
|
||||
|
@ -26,6 +26,13 @@ import android.view.InsetsState;
|
||||
*/
|
||||
oneway interface IDisplayWindowInsetsController {
|
||||
|
||||
/**
|
||||
* 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 packageName: Passes the top package name
|
||||
*/
|
||||
void topFocusedWindowChanged(String packageName);
|
||||
|
||||
/**
|
||||
* @see IWindow#insetsChanged
|
||||
*/
|
||||
|
@ -387,16 +387,6 @@ interface IWindowManager
|
||||
*/
|
||||
oneway void hideTransientBars(int displayId);
|
||||
|
||||
/**
|
||||
* When set to {@code true} the system bars will always be shown. This is true even if an app
|
||||
* requests to be fullscreen by setting the system ui visibility flags. The
|
||||
* functionality was added for the automotive case as a way to guarantee required content stays
|
||||
* on screen at all times.
|
||||
*
|
||||
* @hide
|
||||
*/
|
||||
oneway void setForceShowSystemBars(boolean show);
|
||||
|
||||
/**
|
||||
* Called by System UI to notify of changes to the visibility of Recents.
|
||||
*/
|
||||
|
@ -712,10 +712,17 @@
|
||||
case, this can be disabled (set to false). -->
|
||||
<bool name="config_enableCarDockHomeLaunch">true</bool>
|
||||
|
||||
<!-- Control whether to force the display of System UI Bars at all times regardless of
|
||||
System Ui Flags. This can be useful in the Automotive case if there's a requirement for
|
||||
a UI element to be on screen at all times. -->
|
||||
<bool name="config_forceShowSystemBars">false</bool>
|
||||
<!-- Control whether to force apps to give up control over the display of system bars at all
|
||||
times regardless of System Ui Flags.
|
||||
In the Automotive case, this is helpful if there's a requirement for an UI element to be on
|
||||
screen at all times. Setting this to true also gives System UI the ability to override the
|
||||
visibility controls for the system through the usage of the
|
||||
"SYSTEM_BAR_VISIBILITY_OVERRIDE" setting.
|
||||
Ex: Only setting the config to true will force show system bars for the entire system.
|
||||
Ex: Setting the config to true and the "SYSTEM_BAR_VISIBILITY_OVERRIDE" setting to
|
||||
"immersive.status=apps" will force show navigation bar for all apps and force hide status
|
||||
bar for all apps. -->
|
||||
<bool name="config_remoteInsetsControllerControlsSystemBars">false</bool>
|
||||
|
||||
<!-- HDMI behavior -->
|
||||
|
||||
|
@ -3024,6 +3024,8 @@
|
||||
|
||||
<!-- @hide @TestApi -->
|
||||
<public type="bool" name="config_assistantOnTopOfDream" id="0x01110005" />
|
||||
<!-- @hide @TestApi -->
|
||||
<public type="bool" name="config_remoteInsetsControllerControlsSystemBars" id="0x01110006" />
|
||||
<!-- ===============================================================
|
||||
Resources added in version S of the platform
|
||||
|
||||
|
@ -1669,7 +1669,7 @@
|
||||
<java-symbol type="bool" name="config_enableCarDockHomeLaunch" />
|
||||
<java-symbol type="bool" name="config_enableLockBeforeUnlockScreen" />
|
||||
<java-symbol type="bool" name="config_enableLockScreenRotation" />
|
||||
<java-symbol type="bool" name="config_forceShowSystemBars" />
|
||||
<java-symbol type="bool" name="config_remoteInsetsControllerControlsSystemBars" />
|
||||
<java-symbol type="bool" name="config_lidControlsScreenLock" />
|
||||
<java-symbol type="bool" name="config_lidControlsSleep" />
|
||||
<java-symbol type="bool" name="config_lockDayNightMode" />
|
||||
|
@ -67,6 +67,8 @@ import com.android.systemui.statusbar.policy.ConfigurationController;
|
||||
import com.android.systemui.statusbar.policy.DeviceProvisionedController;
|
||||
import com.android.systemui.statusbar.policy.HeadsUpManager;
|
||||
import com.android.systemui.volume.VolumeDialogComponent;
|
||||
import com.android.systemui.wm.DisplayImeController;
|
||||
import com.android.systemui.wm.DisplaySystemBarsController;
|
||||
|
||||
import javax.inject.Named;
|
||||
import javax.inject.Singleton;
|
||||
@ -97,6 +99,10 @@ public abstract class CarSystemUIModule {
|
||||
groupManager, configurationController);
|
||||
}
|
||||
|
||||
@Binds
|
||||
abstract DisplayImeController bindDisplayImeController(
|
||||
DisplaySystemBarsController displaySystemBarsController);
|
||||
|
||||
@Singleton
|
||||
@Provides
|
||||
@Named(LEAK_REPORT_EMAIL_NAME)
|
||||
|
@ -0,0 +1,250 @@
|
||||
/*
|
||||
* Copyright (C) 2020 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 android.car.settings.CarSettings;
|
||||
import android.content.Context;
|
||||
import android.database.ContentObserver;
|
||||
import android.os.Handler;
|
||||
import android.os.UserHandle;
|
||||
import android.provider.Settings;
|
||||
import android.util.ArraySet;
|
||||
import android.util.Slog;
|
||||
import android.view.WindowInsets;
|
||||
|
||||
import androidx.annotation.VisibleForTesting;
|
||||
|
||||
import java.io.PrintWriter;
|
||||
import java.io.StringWriter;
|
||||
|
||||
/**
|
||||
* Util class to load PolicyControl and allow for querying if a package matches immersive filters.
|
||||
* Similar to {@link com.android.server.wm.PolicyControl}, but separate due to CarSystemUI needing
|
||||
* to set its own policies for system bar visibilities.
|
||||
*
|
||||
* This forces immersive mode behavior for one or both system bars (based on a package
|
||||
* list).
|
||||
*
|
||||
* Control by setting {@link Settings.Global#POLICY_CONTROL_AUTO} to one or more name-value pairs.
|
||||
* e.g.
|
||||
* to force immersive mode everywhere:
|
||||
* "immersive.full=*"
|
||||
* to force hide status bars for com.package1 but not com.package2:
|
||||
* "immersive.status=com.package1,-com.package2"
|
||||
*
|
||||
* Separate multiple name-value pairs with ':'
|
||||
* e.g. "immersive.status=com.package:immersive.navigation=*"
|
||||
*/
|
||||
public class BarControlPolicy {
|
||||
|
||||
private static final String TAG = "BarControlPolicy";
|
||||
private static final boolean DEBUG = false;
|
||||
|
||||
private static final String NAME_IMMERSIVE_FULL = "immersive.full";
|
||||
private static final String NAME_IMMERSIVE_STATUS = "immersive.status";
|
||||
private static final String NAME_IMMERSIVE_NAVIGATION = "immersive.navigation";
|
||||
|
||||
@VisibleForTesting
|
||||
static String sSettingValue;
|
||||
@VisibleForTesting
|
||||
static Filter sImmersiveStatusFilter;
|
||||
private static Filter sImmersiveNavigationFilter;
|
||||
|
||||
/** Loads values from the POLICY_CONTROL setting to set filters. */
|
||||
static boolean reloadFromSetting(Context context) {
|
||||
if (DEBUG) Slog.d(TAG, "reloadFromSetting()");
|
||||
String value = null;
|
||||
try {
|
||||
value = Settings.Global.getStringForUser(context.getContentResolver(),
|
||||
CarSettings.Global.SYSTEM_BAR_VISIBILITY_OVERRIDE,
|
||||
UserHandle.USER_CURRENT);
|
||||
if (sSettingValue == value || sSettingValue != null && sSettingValue.equals(value)) {
|
||||
return false;
|
||||
}
|
||||
setFilters(value);
|
||||
sSettingValue = value;
|
||||
} catch (Throwable t) {
|
||||
Slog.w(TAG, "Error loading policy control, value=" + value, t);
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
/** Used in testing to reset BarControlPolicy. */
|
||||
@VisibleForTesting
|
||||
static void reset() {
|
||||
sSettingValue = null;
|
||||
sImmersiveStatusFilter = null;
|
||||
sImmersiveNavigationFilter = null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Registers a content observer to listen to updates to the SYSTEM_BAR_VISIBILITY_OVERRIDE flag.
|
||||
*/
|
||||
static void registerContentObserver(Context context, Handler handler, FilterListener listener) {
|
||||
context.getContentResolver().registerContentObserver(
|
||||
Settings.Global.getUriFor(CarSettings.Global.SYSTEM_BAR_VISIBILITY_OVERRIDE), false,
|
||||
new ContentObserver(handler) {
|
||||
@Override
|
||||
public void onChange(boolean selfChange) {
|
||||
if (reloadFromSetting(context)) {
|
||||
listener.onFilterUpdated();
|
||||
}
|
||||
}
|
||||
}, UserHandle.USER_ALL);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns bar visibilities based on POLICY_CONTROL_AUTO filters and window policies.
|
||||
* @return int[], where the first value is the inset types that should be shown, and the second
|
||||
* is the inset types that should be hidden.
|
||||
*/
|
||||
@WindowInsets.Type.InsetsType
|
||||
static int[] getBarVisibilities(String packageName) {
|
||||
int hideTypes = 0;
|
||||
int showTypes = 0;
|
||||
if (matchesStatusFilter(packageName)) {
|
||||
hideTypes |= WindowInsets.Type.statusBars();
|
||||
} else {
|
||||
showTypes |= WindowInsets.Type.statusBars();
|
||||
}
|
||||
if (matchesNavigationFilter(packageName)) {
|
||||
hideTypes |= WindowInsets.Type.navigationBars();
|
||||
} else {
|
||||
showTypes |= WindowInsets.Type.navigationBars();
|
||||
}
|
||||
|
||||
return new int[] {showTypes, hideTypes};
|
||||
}
|
||||
|
||||
private static boolean matchesStatusFilter(String packageName) {
|
||||
return sImmersiveStatusFilter != null && sImmersiveStatusFilter.matches(packageName);
|
||||
}
|
||||
|
||||
private static boolean matchesNavigationFilter(String packageName) {
|
||||
return sImmersiveNavigationFilter != null
|
||||
&& sImmersiveNavigationFilter.matches(packageName);
|
||||
}
|
||||
|
||||
private static void setFilters(String value) {
|
||||
if (DEBUG) Slog.d(TAG, "setFilters: " + value);
|
||||
sImmersiveStatusFilter = null;
|
||||
sImmersiveNavigationFilter = null;
|
||||
if (value != null) {
|
||||
String[] nvps = value.split(":");
|
||||
for (String nvp : nvps) {
|
||||
int i = nvp.indexOf('=');
|
||||
if (i == -1) continue;
|
||||
String n = nvp.substring(0, i);
|
||||
String v = nvp.substring(i + 1);
|
||||
if (n.equals(NAME_IMMERSIVE_FULL)) {
|
||||
Filter f = Filter.parse(v);
|
||||
sImmersiveStatusFilter = sImmersiveNavigationFilter = f;
|
||||
} else if (n.equals(NAME_IMMERSIVE_STATUS)) {
|
||||
Filter f = Filter.parse(v);
|
||||
sImmersiveStatusFilter = f;
|
||||
} else if (n.equals(NAME_IMMERSIVE_NAVIGATION)) {
|
||||
Filter f = Filter.parse(v);
|
||||
sImmersiveNavigationFilter = f;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (DEBUG) {
|
||||
Slog.d(TAG, "immersiveStatusFilter: " + sImmersiveStatusFilter);
|
||||
Slog.d(TAG, "immersiveNavigationFilter: " + sImmersiveNavigationFilter);
|
||||
}
|
||||
}
|
||||
|
||||
private static class Filter {
|
||||
private static final String ALL = "*";
|
||||
|
||||
private final ArraySet<String> mWhitelist;
|
||||
private final ArraySet<String> mBlacklist;
|
||||
|
||||
private Filter(ArraySet<String> whitelist, ArraySet<String> blacklist) {
|
||||
mWhitelist = whitelist;
|
||||
mBlacklist = blacklist;
|
||||
}
|
||||
|
||||
boolean matches(String packageName) {
|
||||
if (packageName == null) return false;
|
||||
if (onBlacklist(packageName)) return false;
|
||||
return onWhitelist(packageName);
|
||||
}
|
||||
|
||||
private boolean onBlacklist(String packageName) {
|
||||
return mBlacklist.contains(packageName) || mBlacklist.contains(ALL);
|
||||
}
|
||||
|
||||
private boolean onWhitelist(String packageName) {
|
||||
return mWhitelist.contains(ALL) || mWhitelist.contains(packageName);
|
||||
}
|
||||
|
||||
void dump(PrintWriter pw) {
|
||||
pw.print("Filter[");
|
||||
dump("whitelist", mWhitelist, pw); pw.print(',');
|
||||
dump("blacklist", mBlacklist, pw); pw.print(']');
|
||||
}
|
||||
|
||||
private void dump(String name, ArraySet<String> set, PrintWriter pw) {
|
||||
pw.print(name); pw.print("=(");
|
||||
int n = set.size();
|
||||
for (int i = 0; i < n; i++) {
|
||||
if (i > 0) pw.print(',');
|
||||
pw.print(set.valueAt(i));
|
||||
}
|
||||
pw.print(')');
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
StringWriter sw = new StringWriter();
|
||||
dump(new PrintWriter(sw, true));
|
||||
return sw.toString();
|
||||
}
|
||||
|
||||
// value = comma-delimited list of tokens, where token = (package name|*)
|
||||
// e.g. "com.package1", or "com.android.systemui, com.android.keyguard" or "*"
|
||||
static Filter parse(String value) {
|
||||
if (value == null) return null;
|
||||
ArraySet<String> whitelist = new ArraySet<String>();
|
||||
ArraySet<String> blacklist = new ArraySet<String>();
|
||||
for (String token : value.split(",")) {
|
||||
token = token.trim();
|
||||
if (token.startsWith("-") && token.length() > 1) {
|
||||
token = token.substring(1);
|
||||
blacklist.add(token);
|
||||
} else {
|
||||
whitelist.add(token);
|
||||
}
|
||||
}
|
||||
return new Filter(whitelist, blacklist);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Interface to listen for updates to the filter triggered by the content observer listening to
|
||||
* the SYSTEM_BAR_VISIBILITY_OVERRIDE flag.
|
||||
*/
|
||||
interface FilterListener {
|
||||
|
||||
/** Callback triggered when the content observer updates the filter. */
|
||||
void onFilterUpdated();
|
||||
}
|
||||
|
||||
private BarControlPolicy() {}
|
||||
}
|
@ -0,0 +1,168 @@
|
||||
/*
|
||||
* Copyright (C) 2020 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 android.os.Handler;
|
||||
import android.os.RemoteException;
|
||||
import android.util.ArraySet;
|
||||
import android.util.Slog;
|
||||
import android.util.SparseArray;
|
||||
import android.view.IDisplayWindowInsetsController;
|
||||
import android.view.InsetsController;
|
||||
import android.view.InsetsSourceControl;
|
||||
import android.view.InsetsState;
|
||||
import android.view.WindowInsets;
|
||||
|
||||
import androidx.annotation.VisibleForTesting;
|
||||
|
||||
import com.android.systemui.TransactionPool;
|
||||
import com.android.systemui.dagger.qualifiers.Main;
|
||||
|
||||
import javax.inject.Inject;
|
||||
import javax.inject.Singleton;
|
||||
|
||||
/**
|
||||
* Controller that maps between displays and {@link IDisplayWindowInsetsController} in order to
|
||||
* give system bar control to SystemUI.
|
||||
* {@link R.bool#config_remoteInsetsControllerControlsSystemBars} determines whether this controller
|
||||
* takes control or not.
|
||||
*/
|
||||
@Singleton
|
||||
public class DisplaySystemBarsController extends DisplayImeController {
|
||||
|
||||
private static final String TAG = "DisplaySystemBarsController";
|
||||
|
||||
private SparseArray<PerDisplay> mPerDisplaySparseArray;
|
||||
|
||||
@Inject
|
||||
public DisplaySystemBarsController(
|
||||
SystemWindows syswin,
|
||||
DisplayController displayController,
|
||||
@Main Handler mainHandler,
|
||||
TransactionPool transactionPool) {
|
||||
super(syswin, displayController, mainHandler, transactionPool);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onDisplayAdded(int displayId) {
|
||||
PerDisplay pd = new PerDisplay(displayId);
|
||||
try {
|
||||
mSystemWindows.mWmService.setDisplayWindowInsetsController(displayId, pd);
|
||||
} catch (RemoteException e) {
|
||||
Slog.w(TAG, "Unable to set insets controller on display " + displayId);
|
||||
}
|
||||
// Lazy loading policy control filters instead of during boot.
|
||||
if (mPerDisplaySparseArray == null) {
|
||||
mPerDisplaySparseArray = new SparseArray<>();
|
||||
BarControlPolicy.reloadFromSetting(mSystemWindows.mContext);
|
||||
BarControlPolicy.registerContentObserver(mSystemWindows.mContext, mHandler, () -> {
|
||||
int size = mPerDisplaySparseArray.size();
|
||||
for (int i = 0; i < size; i++) {
|
||||
mPerDisplaySparseArray.valueAt(i).modifyDisplayWindowInsets();
|
||||
}
|
||||
});
|
||||
}
|
||||
mPerDisplaySparseArray.put(displayId, pd);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onDisplayRemoved(int displayId) {
|
||||
try {
|
||||
mSystemWindows.mWmService.setDisplayWindowInsetsController(displayId, null);
|
||||
} catch (RemoteException e) {
|
||||
Slog.w(TAG, "Unable to remove insets controller on display " + displayId);
|
||||
}
|
||||
mPerDisplaySparseArray.remove(displayId);
|
||||
}
|
||||
|
||||
@VisibleForTesting
|
||||
class PerDisplay extends IDisplayWindowInsetsController.Stub {
|
||||
|
||||
int mDisplayId;
|
||||
InsetsController mInsetsController;
|
||||
InsetsState mInsetsState = new InsetsState();
|
||||
String mPackageName;
|
||||
|
||||
PerDisplay(int displayId) {
|
||||
mDisplayId = displayId;
|
||||
mInsetsController = new InsetsController(
|
||||
new DisplaySystemBarsInsetsControllerHost(mHandler, this));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void insetsChanged(InsetsState insetsState) {
|
||||
if (mInsetsState.equals(insetsState)) {
|
||||
return;
|
||||
}
|
||||
mInsetsState.set(insetsState, true /* copySources */);
|
||||
mInsetsController.onStateChanged(insetsState);
|
||||
if (mPackageName != null) {
|
||||
modifyDisplayWindowInsets();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void insetsControlChanged(InsetsState insetsState,
|
||||
InsetsSourceControl[] activeControls) {
|
||||
mInsetsController.onControlsChanged(activeControls);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void hideInsets(@WindowInsets.Type.InsetsType int types, boolean fromIme) {
|
||||
mInsetsController.hide(types);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void showInsets(@WindowInsets.Type.InsetsType int types, boolean fromIme) {
|
||||
mInsetsController.show(types);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void topFocusedWindowChanged(String packageName) {
|
||||
// If both package names are null or both package names are equal, return.
|
||||
if (mPackageName == packageName
|
||||
|| (mPackageName != null && mPackageName.equals(packageName))) {
|
||||
return;
|
||||
}
|
||||
mPackageName = packageName;
|
||||
modifyDisplayWindowInsets();
|
||||
}
|
||||
|
||||
private void modifyDisplayWindowInsets() {
|
||||
if (mPackageName == null) {
|
||||
return;
|
||||
}
|
||||
int[] barVisibilities = BarControlPolicy.getBarVisibilities(mPackageName);
|
||||
updateInsetsState(barVisibilities[0], /* visible= */ true);
|
||||
updateInsetsState(barVisibilities[1], /* visible= */ false);
|
||||
showInsets(barVisibilities[0], /* fromIme= */ false);
|
||||
hideInsets(barVisibilities[1], /* fromIme= */ false);
|
||||
try {
|
||||
mSystemWindows.mWmService.modifyDisplayWindowInsets(mDisplayId, mInsetsState);
|
||||
} catch (RemoteException e) {
|
||||
Slog.w(TAG, "Unable to update window manager service.");
|
||||
}
|
||||
}
|
||||
|
||||
private void updateInsetsState(@WindowInsets.Type.InsetsType int types, boolean visible) {
|
||||
ArraySet<Integer> internalTypes = InsetsState.toInternalType(types);
|
||||
for (int i = internalTypes.size() - 1; i >= 0; i--) {
|
||||
mInsetsState.getSource(internalTypes.valueAt(i)).setVisible(visible);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,173 @@
|
||||
/*
|
||||
* Copyright (C) 2020 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 android.annotation.NonNull;
|
||||
import android.os.Handler;
|
||||
import android.os.IBinder;
|
||||
import android.os.RemoteException;
|
||||
import android.util.Log;
|
||||
import android.view.IDisplayWindowInsetsController;
|
||||
import android.view.InsetsController;
|
||||
import android.view.InsetsState;
|
||||
import android.view.SurfaceControl;
|
||||
import android.view.SyncRtSurfaceTransactionApplier;
|
||||
import android.view.WindowInsets;
|
||||
import android.view.WindowInsetsAnimation;
|
||||
import android.view.WindowInsetsController;
|
||||
import android.view.inputmethod.InputMethodManager;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* Implements {@link InsetsController.Host} for usage by
|
||||
* {@link DisplaySystemBarsController.PerDisplay} instances in {@link DisplaySystemBarsController}.
|
||||
* @hide
|
||||
*/
|
||||
public class DisplaySystemBarsInsetsControllerHost implements InsetsController.Host {
|
||||
|
||||
private static final String TAG = DisplaySystemBarsInsetsControllerHost.class.getSimpleName();
|
||||
|
||||
private final Handler mHandler;
|
||||
private final IDisplayWindowInsetsController mController;
|
||||
private final float[] mTmpFloat9 = new float[9];
|
||||
|
||||
public DisplaySystemBarsInsetsControllerHost(
|
||||
Handler handler, IDisplayWindowInsetsController controller) {
|
||||
mHandler = handler;
|
||||
mController = controller;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Handler getHandler() {
|
||||
return mHandler;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void notifyInsetsChanged() {
|
||||
// no-op
|
||||
}
|
||||
|
||||
@Override
|
||||
public void dispatchWindowInsetsAnimationPrepare(@NonNull WindowInsetsAnimation animation) {
|
||||
// no-op
|
||||
}
|
||||
|
||||
@Override
|
||||
public WindowInsetsAnimation.Bounds dispatchWindowInsetsAnimationStart(
|
||||
@NonNull WindowInsetsAnimation animation,
|
||||
@NonNull WindowInsetsAnimation.Bounds bounds) {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public WindowInsets dispatchWindowInsetsAnimationProgress(@NonNull WindowInsets insets,
|
||||
@NonNull List<WindowInsetsAnimation> runningAnimations) {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void dispatchWindowInsetsAnimationEnd(@NonNull WindowInsetsAnimation animation) {
|
||||
// no-op
|
||||
}
|
||||
|
||||
@Override
|
||||
public void applySurfaceParams(final SyncRtSurfaceTransactionApplier.SurfaceParams... params) {
|
||||
for (int i = params.length - 1; i >= 0; i--) {
|
||||
SyncRtSurfaceTransactionApplier.applyParams(
|
||||
new SurfaceControl.Transaction(), params[i], mTmpFloat9);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void updateCompatSysUiVisibility(
|
||||
@InsetsState.InternalInsetsType int type, boolean visible, boolean hasControl) {
|
||||
// no-op
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onInsetsModified(InsetsState insetsState) {
|
||||
try {
|
||||
mController.insetsChanged(insetsState);
|
||||
} catch (RemoteException e) {
|
||||
Log.e(TAG, "Failed to send insets to controller");
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean hasAnimationCallbacks() {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setSystemBarsAppearance(
|
||||
@WindowInsetsController.Appearance int appearance,
|
||||
@WindowInsetsController.Appearance int mask) {
|
||||
// no-op
|
||||
}
|
||||
|
||||
@Override
|
||||
public @WindowInsetsController.Appearance int getSystemBarsAppearance() {
|
||||
return 0;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setSystemBarsBehavior(@WindowInsetsController.Behavior int behavior) {
|
||||
// no-op
|
||||
}
|
||||
|
||||
@Override
|
||||
public @WindowInsetsController.Behavior int getSystemBarsBehavior() {
|
||||
return 0;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void releaseSurfaceControlFromRt(SurfaceControl surfaceControl) {
|
||||
surfaceControl.release();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void addOnPreDrawRunnable(Runnable r) {
|
||||
mHandler.post(r);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void postInsetsAnimationCallback(Runnable r) {
|
||||
mHandler.post(r);
|
||||
}
|
||||
|
||||
@Override
|
||||
public InputMethodManager getInputMethodManager() {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getRootViewTitle() {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int dipToPx(int dips) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
@Override
|
||||
public IBinder getWindowToken() {
|
||||
return null;
|
||||
}
|
||||
}
|
@ -0,0 +1,195 @@
|
||||
/*
|
||||
* Copyright (C) 2020 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.WindowInsets.Type.navigationBars;
|
||||
import static android.view.WindowInsets.Type.statusBars;
|
||||
|
||||
import static com.google.common.truth.Truth.assertThat;
|
||||
|
||||
import android.car.settings.CarSettings;
|
||||
import android.provider.Settings;
|
||||
import android.testing.AndroidTestingRunner;
|
||||
import android.testing.TestableLooper;
|
||||
|
||||
import androidx.test.filters.SmallTest;
|
||||
|
||||
import com.android.systemui.SysuiTestCase;
|
||||
|
||||
import org.junit.After;
|
||||
import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
|
||||
@RunWith(AndroidTestingRunner.class)
|
||||
@TestableLooper.RunWithLooper
|
||||
@SmallTest
|
||||
public class BarControlPolicyTest extends SysuiTestCase {
|
||||
|
||||
private static final String PACKAGE_NAME = "sample.app";
|
||||
|
||||
@Before
|
||||
public void setUp() {
|
||||
BarControlPolicy.reset();
|
||||
}
|
||||
|
||||
@After
|
||||
public void tearDown() {
|
||||
Settings.Global.clearProviderForTest();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void reloadFromSetting_notSet_doesNotSetFilters() {
|
||||
BarControlPolicy.reloadFromSetting(mContext);
|
||||
|
||||
assertThat(BarControlPolicy.sImmersiveStatusFilter).isNull();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void reloadFromSetting_invalidPolicyControlString_doesNotSetFilters() {
|
||||
String text = "sample text";
|
||||
Settings.Global.putString(
|
||||
mContext.getContentResolver(),
|
||||
CarSettings.Global.SYSTEM_BAR_VISIBILITY_OVERRIDE,
|
||||
text
|
||||
);
|
||||
|
||||
BarControlPolicy.reloadFromSetting(mContext);
|
||||
|
||||
assertThat(BarControlPolicy.sImmersiveStatusFilter).isNull();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void reloadFromSetting_validPolicyControlString_setsFilters() {
|
||||
String text = "immersive.status=" + PACKAGE_NAME;
|
||||
Settings.Global.putString(
|
||||
mContext.getContentResolver(),
|
||||
CarSettings.Global.SYSTEM_BAR_VISIBILITY_OVERRIDE,
|
||||
text
|
||||
);
|
||||
|
||||
BarControlPolicy.reloadFromSetting(mContext);
|
||||
|
||||
assertThat(BarControlPolicy.sImmersiveStatusFilter).isNotNull();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void reloadFromSetting_filtersSet_doesNotSetFiltersAgain() {
|
||||
String text = "immersive.status=" + PACKAGE_NAME;
|
||||
Settings.Global.putString(
|
||||
mContext.getContentResolver(),
|
||||
CarSettings.Global.SYSTEM_BAR_VISIBILITY_OVERRIDE,
|
||||
text
|
||||
);
|
||||
|
||||
BarControlPolicy.reloadFromSetting(mContext);
|
||||
|
||||
assertThat(BarControlPolicy.reloadFromSetting(mContext)).isFalse();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void getBarVisibilities_policyControlNotSet_showsSystemBars() {
|
||||
int[] visibilities = BarControlPolicy.getBarVisibilities(PACKAGE_NAME);
|
||||
|
||||
assertThat(visibilities[0]).isEqualTo(statusBars() | navigationBars());
|
||||
assertThat(visibilities[1]).isEqualTo(0);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void getBarVisibilities_immersiveStatusForAppAndMatchingApp_hidesStatusBar() {
|
||||
Settings.Global.putString(
|
||||
mContext.getContentResolver(),
|
||||
CarSettings.Global.SYSTEM_BAR_VISIBILITY_OVERRIDE,
|
||||
"immersive.status=" + PACKAGE_NAME);
|
||||
BarControlPolicy.reloadFromSetting(mContext);
|
||||
|
||||
int[] visibilities = BarControlPolicy.getBarVisibilities(PACKAGE_NAME);
|
||||
|
||||
assertThat(visibilities[0]).isEqualTo(navigationBars());
|
||||
assertThat(visibilities[1]).isEqualTo(statusBars());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void getBarVisibilities_immersiveStatusForAppAndNonMatchingApp_showsSystemBars() {
|
||||
Settings.Global.putString(
|
||||
mContext.getContentResolver(),
|
||||
CarSettings.Global.SYSTEM_BAR_VISIBILITY_OVERRIDE,
|
||||
"immersive.status=" + PACKAGE_NAME);
|
||||
BarControlPolicy.reloadFromSetting(mContext);
|
||||
|
||||
int[] visibilities = BarControlPolicy.getBarVisibilities("sample2.app");
|
||||
|
||||
assertThat(visibilities[0]).isEqualTo(statusBars() | navigationBars());
|
||||
assertThat(visibilities[1]).isEqualTo(0);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void getBarVisibilities_immersiveStatusForAppsAndNonApp_showsSystemBars() {
|
||||
Settings.Global.putString(
|
||||
mContext.getContentResolver(),
|
||||
CarSettings.Global.SYSTEM_BAR_VISIBILITY_OVERRIDE,
|
||||
"immersive.status=apps");
|
||||
BarControlPolicy.reloadFromSetting(mContext);
|
||||
|
||||
int[] visibilities = BarControlPolicy.getBarVisibilities(PACKAGE_NAME);
|
||||
|
||||
assertThat(visibilities[0]).isEqualTo(statusBars() | navigationBars());
|
||||
assertThat(visibilities[1]).isEqualTo(0);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void getBarVisibilities_immersiveFullForAppAndMatchingApp_hidesSystemBars() {
|
||||
Settings.Global.putString(
|
||||
mContext.getContentResolver(),
|
||||
CarSettings.Global.SYSTEM_BAR_VISIBILITY_OVERRIDE,
|
||||
"immersive.full=" + PACKAGE_NAME);
|
||||
BarControlPolicy.reloadFromSetting(mContext);
|
||||
|
||||
int[] visibilities = BarControlPolicy.getBarVisibilities(PACKAGE_NAME);
|
||||
|
||||
assertThat(visibilities[0]).isEqualTo(0);
|
||||
assertThat(visibilities[1]).isEqualTo(statusBars() | navigationBars());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void getBarVisibilities_immersiveFullForAppAndNonMatchingApp_showsSystemBars() {
|
||||
Settings.Global.putString(
|
||||
mContext.getContentResolver(),
|
||||
CarSettings.Global.SYSTEM_BAR_VISIBILITY_OVERRIDE,
|
||||
"immersive.full=" + PACKAGE_NAME);
|
||||
BarControlPolicy.reloadFromSetting(mContext);
|
||||
|
||||
int[] visibilities = BarControlPolicy.getBarVisibilities("sample2.app");
|
||||
|
||||
assertThat(visibilities[0]).isEqualTo(statusBars() | navigationBars());
|
||||
assertThat(visibilities[1]).isEqualTo(0);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void getBarVisibilities_immersiveFullForAppsAndNonApp_showsSystemBars() {
|
||||
Settings.Global.putString(
|
||||
mContext.getContentResolver(),
|
||||
CarSettings.Global.SYSTEM_BAR_VISIBILITY_OVERRIDE,
|
||||
"immersive.full=apps");
|
||||
BarControlPolicy.reloadFromSetting(mContext);
|
||||
|
||||
int[] visibilities = BarControlPolicy.getBarVisibilities(PACKAGE_NAME);
|
||||
|
||||
assertThat(visibilities[0]).isEqualTo(statusBars() | navigationBars());
|
||||
assertThat(visibilities[1]).isEqualTo(0);
|
||||
}
|
||||
}
|
@ -0,0 +1,111 @@
|
||||
/*
|
||||
* Copyright (C) 2020 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 com.google.common.truth.Truth.assertThat;
|
||||
|
||||
import static org.mockito.ArgumentMatchers.any;
|
||||
import static org.mockito.Mockito.eq;
|
||||
import static org.mockito.Mockito.verify;
|
||||
|
||||
import android.car.settings.CarSettings;
|
||||
import android.os.Handler;
|
||||
import android.os.RemoteException;
|
||||
import android.provider.Settings;
|
||||
import android.testing.AndroidTestingRunner;
|
||||
import android.testing.TestableLooper;
|
||||
import android.view.IWindowManager;
|
||||
|
||||
import androidx.test.filters.SmallTest;
|
||||
|
||||
import com.android.systemui.SysuiTestCase;
|
||||
import com.android.systemui.TransactionPool;
|
||||
|
||||
import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
import org.mockito.Mock;
|
||||
import org.mockito.MockitoAnnotations;
|
||||
|
||||
@RunWith(AndroidTestingRunner.class)
|
||||
@TestableLooper.RunWithLooper
|
||||
@SmallTest
|
||||
public class DisplaySystemBarsControllerTest extends SysuiTestCase {
|
||||
|
||||
private DisplaySystemBarsController mController;
|
||||
|
||||
private static final int DISPLAY_ID = 1;
|
||||
|
||||
@Mock
|
||||
private SystemWindows mSystemWindows;
|
||||
@Mock
|
||||
private IWindowManager mIWindowManager;
|
||||
@Mock
|
||||
private DisplayController mDisplayController;
|
||||
@Mock
|
||||
private Handler mHandler;
|
||||
@Mock
|
||||
private TransactionPool mTransactionPool;
|
||||
|
||||
@Before
|
||||
public void setUp() {
|
||||
MockitoAnnotations.initMocks(this);
|
||||
mSystemWindows.mContext = mContext;
|
||||
mSystemWindows.mWmService = mIWindowManager;
|
||||
|
||||
mController = new DisplaySystemBarsController(
|
||||
mSystemWindows,
|
||||
mDisplayController,
|
||||
mHandler,
|
||||
mTransactionPool
|
||||
);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void onDisplayAdded_setsDisplayWindowInsetsControllerOnWMService()
|
||||
throws RemoteException {
|
||||
mController.onDisplayAdded(DISPLAY_ID);
|
||||
|
||||
verify(mIWindowManager).setDisplayWindowInsetsController(
|
||||
eq(DISPLAY_ID), any(DisplaySystemBarsController.PerDisplay.class));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void onDisplayAdded_loadsBarControlPolicyFilters() {
|
||||
String text = "sample text";
|
||||
Settings.Global.putString(
|
||||
mContext.getContentResolver(),
|
||||
CarSettings.Global.SYSTEM_BAR_VISIBILITY_OVERRIDE,
|
||||
text
|
||||
);
|
||||
|
||||
mController.onDisplayAdded(DISPLAY_ID);
|
||||
|
||||
assertThat(BarControlPolicy.sSettingValue).isEqualTo(text);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void onDisplayRemoved_unsetsDisplayWindowInsetsControllerInWMService()
|
||||
throws RemoteException {
|
||||
mController.onDisplayAdded(DISPLAY_ID);
|
||||
|
||||
mController.onDisplayRemoved(DISPLAY_ID);
|
||||
|
||||
verify(mIWindowManager).setDisplayWindowInsetsController(
|
||||
DISPLAY_ID, /* displayWindowInsetsController= */ null);
|
||||
}
|
||||
}
|
@ -251,6 +251,11 @@ public class DisplayImeController implements DisplayController.OnDisplaysChanged
|
||||
mHandler.post(() -> startAnimation(false /* show */, false /* forceRestart */));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void topFocusedWindowChanged(String packageName) {
|
||||
// no-op
|
||||
}
|
||||
|
||||
/**
|
||||
* Sends the local visibility state back to window manager. Needed for legacy adjustForIme.
|
||||
*/
|
||||
|
@ -5747,6 +5747,19 @@ class DisplayContent extends WindowContainer<DisplayContent.DisplayChildWindowCo
|
||||
mRemoteInsetsController = controller;
|
||||
}
|
||||
|
||||
/**
|
||||
* Notifies the remote insets controller that the top focused window has changed.
|
||||
*
|
||||
* @param packageName The name of the package that is open in the top focused window.
|
||||
*/
|
||||
void topFocusedWindowChanged(String packageName) {
|
||||
try {
|
||||
mRemoteInsetsController.topFocusedWindowChanged(packageName);
|
||||
} catch (RemoteException e) {
|
||||
Slog.w(TAG, "Failed to deliver package in top focused window change", e);
|
||||
}
|
||||
}
|
||||
|
||||
void notifyInsetsChanged() {
|
||||
try {
|
||||
mRemoteInsetsController.insetsChanged(
|
||||
|
@ -386,11 +386,6 @@ public class DisplayPolicy {
|
||||
private int mForcingShowNavBarLayer;
|
||||
private boolean mForceShowSystemBars;
|
||||
|
||||
/**
|
||||
* Force the display of system bars regardless of other settings.
|
||||
*/
|
||||
private boolean mForceShowSystemBarsFromExternal;
|
||||
|
||||
private boolean mShowingDream;
|
||||
private boolean mLastShowingDream;
|
||||
private boolean mDreamingLockscreen;
|
||||
@ -480,7 +475,6 @@ public class DisplayPolicy {
|
||||
final Resources r = mContext.getResources();
|
||||
mCarDockEnablesAccelerometer = r.getBoolean(R.bool.config_carDockEnablesAccelerometer);
|
||||
mDeskDockEnablesAccelerometer = r.getBoolean(R.bool.config_deskDockEnablesAccelerometer);
|
||||
mForceShowSystemBarsFromExternal = r.getBoolean(R.bool.config_forceShowSystemBars);
|
||||
|
||||
mAccessibilityManager = (AccessibilityManager) mContext.getSystemService(
|
||||
Context.ACCESSIBILITY_SERVICE);
|
||||
@ -698,17 +692,6 @@ public class DisplayPolicy {
|
||||
return mDockMode;
|
||||
}
|
||||
|
||||
/**
|
||||
* @see WindowManagerService.setForceShowSystemBars
|
||||
*/
|
||||
void setForceShowSystemBars(boolean forceShowSystemBars) {
|
||||
mForceShowSystemBarsFromExternal = forceShowSystemBars;
|
||||
}
|
||||
|
||||
boolean getForceShowSystemBars() {
|
||||
return mForceShowSystemBarsFromExternal;
|
||||
}
|
||||
|
||||
public boolean hasNavigationBar() {
|
||||
return mHasNavigationBar;
|
||||
}
|
||||
@ -3550,8 +3533,7 @@ public class DisplayPolicy {
|
||||
// We need to force system bars when the docked stack is visible, when the freeform stack
|
||||
// is focused but also when we are resizing for the transitions when docked stack
|
||||
// visibility changes.
|
||||
mForceShowSystemBars = dockedStackVisible || win.inFreeformWindowingMode() || resizing
|
||||
|| mForceShowSystemBarsFromExternal;
|
||||
mForceShowSystemBars = dockedStackVisible || win.inFreeformWindowingMode() || resizing;
|
||||
final boolean forceOpaqueStatusBar = mForceShowSystemBars && !isKeyguardShowing();
|
||||
|
||||
// apply translucent bar vis flags
|
||||
@ -3926,8 +3908,8 @@ public class DisplayPolicy {
|
||||
}
|
||||
pw.print(prefix); pw.print("mTopIsFullscreen="); pw.println(mTopIsFullscreen);
|
||||
pw.print(prefix); pw.print("mForceStatusBar="); pw.print(mForceStatusBar);
|
||||
pw.print(prefix); pw.print("mForceShowSystemBarsFromExternal=");
|
||||
pw.print(mForceShowSystemBarsFromExternal);
|
||||
pw.print(prefix); pw.print("mRemoteInsetsControllerControlsSystemBars");
|
||||
pw.print(mDisplayContent.getInsetsPolicy().getRemoteInsetsControllerControlsSystemBars());
|
||||
pw.print(" mAllowLockscreenWhenOn="); pw.println(mAllowLockscreenWhenOn);
|
||||
mStatusBarController.dump(pw, prefix);
|
||||
mNavigationBarController.dump(pw, prefix);
|
||||
|
@ -46,7 +46,9 @@ import android.view.WindowInsets.Type.InsetsType;
|
||||
import android.view.WindowInsetsAnimation;
|
||||
import android.view.WindowInsetsAnimation.Bounds;
|
||||
import android.view.WindowInsetsAnimationControlListener;
|
||||
import android.view.WindowManager;
|
||||
|
||||
import com.android.internal.R;
|
||||
import com.android.internal.annotations.VisibleForTesting;
|
||||
import com.android.server.DisplayThread;
|
||||
|
||||
@ -97,12 +99,31 @@ class InsetsPolicy {
|
||||
private BarWindow mStatusBar = new BarWindow(StatusBarManager.WINDOW_STATUS_BAR);
|
||||
private BarWindow mNavBar = new BarWindow(StatusBarManager.WINDOW_NAVIGATION_BAR);
|
||||
private boolean mAnimatingShown;
|
||||
|
||||
/**
|
||||
* Let remote insets controller control system bars regardless of other settings.
|
||||
*/
|
||||
private boolean mRemoteInsetsControllerControlsSystemBars;
|
||||
private final float[] mTmpFloat9 = new float[9];
|
||||
|
||||
InsetsPolicy(InsetsStateController stateController, DisplayContent displayContent) {
|
||||
mStateController = stateController;
|
||||
mDisplayContent = displayContent;
|
||||
mPolicy = displayContent.getDisplayPolicy();
|
||||
mRemoteInsetsControllerControlsSystemBars = mPolicy.getContext().getResources().getBoolean(
|
||||
R.bool.config_remoteInsetsControllerControlsSystemBars);
|
||||
}
|
||||
|
||||
boolean getRemoteInsetsControllerControlsSystemBars() {
|
||||
return mRemoteInsetsControllerControlsSystemBars;
|
||||
}
|
||||
|
||||
/**
|
||||
* Used only for testing.
|
||||
*/
|
||||
@VisibleForTesting
|
||||
void setRemoteInsetsControllerControlsSystemBars(boolean controlsSystemBars) {
|
||||
mRemoteInsetsControllerControlsSystemBars = controlsSystemBars;
|
||||
}
|
||||
|
||||
/** Updates the target which can control system bars. */
|
||||
@ -256,6 +277,11 @@ class InsetsPolicy {
|
||||
// Notification shade has control anyways, no reason to force anything.
|
||||
return focusedWin;
|
||||
}
|
||||
if (remoteInsetsControllerControlsSystemBars(focusedWin)) {
|
||||
mDisplayContent.mRemoteInsetsControlTarget.topFocusedWindowChanged(
|
||||
focusedWin.mAttrs.packageName);
|
||||
return mDisplayContent.mRemoteInsetsControlTarget;
|
||||
}
|
||||
if (forceShowsSystemBarsForWindowingMode) {
|
||||
// Status bar is forcibly shown for the windowing mode which is a steady state.
|
||||
// We don't want the client to control the status bar, and we will dispatch the real
|
||||
@ -285,6 +311,11 @@ class InsetsPolicy {
|
||||
// Notification shade has control anyways, no reason to force anything.
|
||||
return focusedWin;
|
||||
}
|
||||
if (remoteInsetsControllerControlsSystemBars(focusedWin)) {
|
||||
mDisplayContent.mRemoteInsetsControlTarget.topFocusedWindowChanged(
|
||||
focusedWin.mAttrs.packageName);
|
||||
return mDisplayContent.mRemoteInsetsControlTarget;
|
||||
}
|
||||
if (forceShowsSystemBarsForWindowingMode) {
|
||||
// Navigation bar is forcibly shown for the windowing mode which is a steady state.
|
||||
// We don't want the client to control the navigation bar, and we will dispatch the real
|
||||
@ -300,6 +331,28 @@ class InsetsPolicy {
|
||||
return focusedWin;
|
||||
}
|
||||
|
||||
/**
|
||||
* Determines whether the remote insets controller should take control of system bars for all
|
||||
* windows.
|
||||
*/
|
||||
boolean remoteInsetsControllerControlsSystemBars(@Nullable WindowState focusedWin) {
|
||||
if (focusedWin == null) {
|
||||
return false;
|
||||
}
|
||||
if (!mRemoteInsetsControllerControlsSystemBars) {
|
||||
return false;
|
||||
}
|
||||
if (mDisplayContent == null || mDisplayContent.mRemoteInsetsControlTarget == null) {
|
||||
// No remote insets control target to take control of insets.
|
||||
return false;
|
||||
}
|
||||
// If necessary, auto can control application windows when
|
||||
// config_remoteInsetsControllerControlsSystemBars is set to true. This is useful in cases
|
||||
// where we want to dictate system bar inset state for applications.
|
||||
return focusedWin.getAttrs().type >= WindowManager.LayoutParams.FIRST_APPLICATION_WINDOW
|
||||
&& focusedWin.getAttrs().type <= WindowManager.LayoutParams.LAST_APPLICATION_WINDOW;
|
||||
}
|
||||
|
||||
private boolean forceShowsStatusBarTransiently() {
|
||||
final WindowState win = mPolicy.getStatusBar();
|
||||
return win != null && (win.mAttrs.privateFlags & PRIVATE_FLAG_FORCE_SHOW_STATUS_BAR) != 0;
|
||||
@ -321,10 +374,7 @@ class InsetsPolicy {
|
||||
// We need to force system bars when the docked stack is visible, when the freeform stack
|
||||
// is visible but also when we are resizing for the transitions when docked stack
|
||||
// visibility changes.
|
||||
return isDockedStackVisible
|
||||
|| isFreeformStackVisible
|
||||
|| isResizing
|
||||
|| mPolicy.getForceShowSystemBars();
|
||||
return isDockedStackVisible || isFreeformStackVisible || isResizing;
|
||||
}
|
||||
|
||||
@VisibleForTesting
|
||||
|
@ -5826,27 +5826,6 @@ public class WindowManagerService extends IWindowManager.Stub
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setForceShowSystemBars(boolean show) {
|
||||
boolean isAutomotive = mContext.getPackageManager().hasSystemFeature(
|
||||
PackageManager.FEATURE_AUTOMOTIVE);
|
||||
if (!isAutomotive) {
|
||||
throw new UnsupportedOperationException("Force showing system bars is only supported"
|
||||
+ "for Automotive use cases.");
|
||||
}
|
||||
if (mContext.checkCallingOrSelfPermission(android.Manifest.permission.STATUS_BAR)
|
||||
!= PackageManager.PERMISSION_GRANTED) {
|
||||
throw new SecurityException("Caller does not hold permission "
|
||||
+ android.Manifest.permission.STATUS_BAR);
|
||||
}
|
||||
synchronized (mGlobalLock) {
|
||||
final PooledConsumer c = PooledLambda.obtainConsumer(
|
||||
DisplayPolicy::setForceShowSystemBars, PooledLambda.__(), show);
|
||||
mRoot.forAllDisplayPolicies(c);
|
||||
c.recycle();
|
||||
}
|
||||
}
|
||||
|
||||
public void setNavBarVirtualKeyHapticFeedbackEnabled(boolean enabled) {
|
||||
if (mContext.checkCallingOrSelfPermission(android.Manifest.permission.STATUS_BAR)
|
||||
!= PackageManager.PERMISSION_GRANTED) {
|
||||
|
@ -96,13 +96,10 @@ import android.platform.test.annotations.Presubmit;
|
||||
import android.util.DisplayMetrics;
|
||||
import android.view.DisplayCutout;
|
||||
import android.view.Gravity;
|
||||
import android.view.IDisplayWindowInsetsController;
|
||||
import android.view.IDisplayWindowRotationCallback;
|
||||
import android.view.IDisplayWindowRotationController;
|
||||
import android.view.ISystemGestureExclusionListener;
|
||||
import android.view.IWindowManager;
|
||||
import android.view.InsetsSourceControl;
|
||||
import android.view.InsetsState;
|
||||
import android.view.MotionEvent;
|
||||
import android.view.Surface;
|
||||
import android.view.SurfaceControl.Transaction;
|
||||
@ -929,28 +926,6 @@ public class DisplayContentTests extends WindowTestsBase {
|
||||
assertEquals(mAppWindow, mDisplayContent.computeImeControlTarget());
|
||||
}
|
||||
|
||||
private IDisplayWindowInsetsController createDisplayWindowInsetsController() {
|
||||
return new IDisplayWindowInsetsController.Stub() {
|
||||
|
||||
@Override
|
||||
public void insetsChanged(InsetsState insetsState) throws RemoteException {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void insetsControlChanged(InsetsState insetsState,
|
||||
InsetsSourceControl[] insetsSourceControls) throws RemoteException {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void showInsets(int i, boolean b) throws RemoteException {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void hideInsets(int i, boolean b) throws RemoteException {
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testUpdateSystemGestureExclusion() throws Exception {
|
||||
final DisplayContent dc = createNewDisplay();
|
||||
|
@ -67,7 +67,6 @@ import android.view.WindowManager;
|
||||
|
||||
import androidx.test.filters.SmallTest;
|
||||
|
||||
import com.android.server.policy.WindowManagerPolicy;
|
||||
import com.android.server.wm.utils.WmDisplayCutout;
|
||||
|
||||
import org.junit.Before;
|
||||
@ -806,27 +805,6 @@ public class DisplayPolicyLayoutTests extends DisplayPolicyTestsBase {
|
||||
new ToStringComparatorWrapper<>(simulatedInsetsState));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void forceShowSystemBars_clearsSystemUIFlags() {
|
||||
mDisplayPolicy.mLastSystemUiFlags |= SYSTEM_UI_FLAG_FULLSCREEN;
|
||||
mWindow.mAttrs.subtreeSystemUiVisibility |= SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN;
|
||||
mWindow.mAttrs.flags =
|
||||
FLAG_LAYOUT_IN_SCREEN | FLAG_LAYOUT_INSET_DECOR | FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS;
|
||||
mWindow.mSystemUiVisibility = SYSTEM_UI_FLAG_FULLSCREEN;
|
||||
mDisplayPolicy.setForceShowSystemBars(true);
|
||||
addWindow(mWindow);
|
||||
|
||||
mDisplayPolicy.beginLayoutLw(mFrames, 0 /* UI mode */);
|
||||
mDisplayPolicy.layoutWindowLw(mWindow, null, mFrames);
|
||||
// triggers updateSystemUiVisibilityLw which will reset the flags as needed
|
||||
int finishPostLayoutPolicyLw = mDisplayPolicy.focusChangedLw(mWindow, mWindow);
|
||||
|
||||
assertEquals(WindowManagerPolicy.FINISH_LAYOUT_REDO_LAYOUT, finishPostLayoutPolicyLw);
|
||||
assertEquals(0, mDisplayPolicy.mLastSystemUiFlags);
|
||||
assertEquals(0, mWindow.mAttrs.systemUiVisibility);
|
||||
assertInsetByTopBottom(mWindow.getContentFrameLw(), STATUS_BAR_HEIGHT, NAV_BAR_HEIGHT);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testScreenDecorWindows() {
|
||||
final WindowState decorWindow = createWindow(null, TYPE_APPLICATION_OVERLAY, "decorWindow");
|
||||
|
@ -172,8 +172,9 @@ public class InsetsPolicyTest extends WindowTestsBase {
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testControlsForDispatch_forceShowSystemBarsFromExternal_appHasNoControl() {
|
||||
mDisplayContent.getDisplayPolicy().setForceShowSystemBars(true);
|
||||
public void testControlsForDispatch_remoteInsetsControllerControlsBars_appHasNoControl() {
|
||||
mDisplayContent.setRemoteInsetsController(createDisplayWindowInsetsController());
|
||||
mDisplayContent.getInsetsPolicy().setRemoteInsetsControllerControlsSystemBars(true);
|
||||
addWindow(TYPE_STATUS_BAR, "statusBar");
|
||||
addWindow(TYPE_NAVIGATION_BAR, "navBar");
|
||||
|
||||
|
@ -60,24 +60,6 @@ public class WindowManagerServiceTests extends WindowTestsBase {
|
||||
@Rule
|
||||
public ExpectedException mExpectedException = ExpectedException.none();
|
||||
|
||||
@Test
|
||||
public void testForceShowSystemBarsThrowsExceptionForNonAutomotive() {
|
||||
if (!isAutomotive()) {
|
||||
mExpectedException.expect(UnsupportedOperationException.class);
|
||||
|
||||
mWm.setForceShowSystemBars(true);
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testForceShowSystemBarsDoesNotThrowExceptionForAutomotiveWithStatusBarPermission() {
|
||||
if (isAutomotive()) {
|
||||
mExpectedException.none();
|
||||
|
||||
mWm.setForceShowSystemBars(true);
|
||||
}
|
||||
}
|
||||
|
||||
private boolean isAutomotive() {
|
||||
return getInstrumentation().getTargetContext().getPackageManager().hasSystemFeature(
|
||||
PackageManager.FEATURE_AUTOMOTIVE);
|
||||
|
@ -41,11 +41,15 @@ import static org.mockito.Mockito.mock;
|
||||
|
||||
import android.content.Context;
|
||||
import android.content.Intent;
|
||||
import android.os.RemoteException;
|
||||
import android.os.UserHandle;
|
||||
import android.util.Log;
|
||||
import android.view.Display;
|
||||
import android.view.DisplayInfo;
|
||||
import android.view.IDisplayWindowInsetsController;
|
||||
import android.view.IWindow;
|
||||
import android.view.InsetsSourceControl;
|
||||
import android.view.InsetsState;
|
||||
import android.view.SurfaceControl.Transaction;
|
||||
import android.view.View;
|
||||
import android.view.WindowManager;
|
||||
@ -123,7 +127,7 @@ class WindowTestsBase extends SystemServiceTestsBase {
|
||||
mChildAppWindowBelow = createCommonWindow(mAppWindow,
|
||||
TYPE_APPLICATION_MEDIA_OVERLAY,
|
||||
"mChildAppWindowBelow");
|
||||
mDisplayContent.getDisplayPolicy().setForceShowSystemBars(false);
|
||||
mDisplayContent.getInsetsPolicy().setRemoteInsetsControllerControlsSystemBars(false);
|
||||
|
||||
// Adding a display will cause freezing the display. Make sure to wait until it's
|
||||
// unfrozen to not run into race conditions with the tests.
|
||||
@ -344,6 +348,32 @@ class WindowTestsBase extends SystemServiceTestsBase {
|
||||
return createNewDisplay(displayInfo, false /* supportIme */);
|
||||
}
|
||||
|
||||
IDisplayWindowInsetsController createDisplayWindowInsetsController() {
|
||||
return new IDisplayWindowInsetsController.Stub() {
|
||||
|
||||
@Override
|
||||
public void insetsChanged(InsetsState insetsState) throws RemoteException {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void insetsControlChanged(InsetsState insetsState,
|
||||
InsetsSourceControl[] insetsSourceControls) throws RemoteException {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void showInsets(int i, boolean b) throws RemoteException {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void hideInsets(int i, boolean b) throws RemoteException {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void topFocusedWindowChanged(String packageName) {
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
/** Sets the default minimum task size to 1 so that tests can use small task sizes */
|
||||
void removeGlobalMinSizeRestriction() {
|
||||
mWm.mAtmService.mRootWindowContainer.mDefaultMinSizeOfResizeableTaskDp = 1;
|
||||
|
Loading…
x
Reference in New Issue
Block a user