Files
packages_apps_Settings/src/com/android/settings/connecteddevice/display/ExternalDisplaySettingsConfiguration.java
Pierre Barbier de Reuille 761920b4a1 Update external display settings to use DesktopExperienceFlags
Done to keep the injector and allow the same testing as before.

Flag: EXEMPT (update flag infra)
Test: build, run, use developer option
Fix: 401058586
Change-Id: Ie841f5a86d41f573e888cccec46d9c3dc700e561
2025-03-06 11:13:44 +00:00

366 lines
12 KiB
Java

/*
* Copyright (C) 2024 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.settings.connecteddevice.display;
import static android.content.Context.DISPLAY_SERVICE;
import static android.hardware.display.DisplayManager.DISPLAY_CATEGORY_ALL_INCLUDING_DISABLED;
import static android.hardware.display.DisplayManager.EVENT_TYPE_DISPLAY_ADDED;
import static android.hardware.display.DisplayManager.EVENT_TYPE_DISPLAY_CHANGED;
import static android.hardware.display.DisplayManager.EVENT_TYPE_DISPLAY_REMOVED;
import static android.hardware.display.DisplayManager.PRIVATE_EVENT_TYPE_DISPLAY_CONNECTION_CHANGED;
import static android.view.Display.INVALID_DISPLAY;
import static com.android.server.display.feature.flags.Flags.enableModeLimitForExternalDisplay;
import android.content.Context;
import android.hardware.display.DisplayManager;
import android.hardware.display.DisplayManagerGlobal;
import android.os.Handler;
import android.os.Looper;
import android.os.RemoteException;
import android.os.SystemProperties;
import android.view.Display;
import android.view.Display.Mode;
import android.view.IWindowManager;
import android.view.WindowManagerGlobal;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import com.android.settings.R;
import com.android.settings.flags.FeatureFlags;
import com.android.settings.flags.FeatureFlagsImpl;
import java.util.ArrayList;
import java.util.HashSet;
import java.util.List;
public class ExternalDisplaySettingsConfiguration {
static final String VIRTUAL_DISPLAY_PACKAGE_NAME_SYSTEM_PROPERTY =
"persist.demo.userrotation.package_name";
static final String DISPLAY_ID_ARG = "display_id";
static final int EXTERNAL_DISPLAY_NOT_FOUND_RESOURCE = R.string.external_display_not_found;
static final int EXTERNAL_DISPLAY_HELP_URL = R.string.help_url_external_display;
public static class SystemServicesProvider {
@Nullable
private IWindowManager mWindowManager;
@Nullable
private DisplayManager mDisplayManager;
@Nullable
protected Context mContext;
/**
* @param name of a system property.
* @return the value of the system property.
*/
@NonNull
public String getSystemProperty(@NonNull String name) {
return SystemProperties.get(name);
}
/**
* @return return public Display manager.
*/
@Nullable
public DisplayManager getDisplayManager() {
if (mDisplayManager == null && getContext() != null) {
mDisplayManager = (DisplayManager) getContext().getSystemService(DISPLAY_SERVICE);
}
return mDisplayManager;
}
/**
* @return internal IWindowManager
*/
@Nullable
public IWindowManager getWindowManager() {
if (mWindowManager == null) {
mWindowManager = WindowManagerGlobal.getWindowManagerService();
}
return mWindowManager;
}
/**
* @return context.
*/
@Nullable
public Context getContext() {
return mContext;
}
}
public static class Injector extends SystemServicesProvider {
@NonNull
private final FeatureFlags mFlags;
@NonNull
private final Handler mHandler;
Injector(@Nullable Context context) {
this(context, new DesktopExperienceFlags(new FeatureFlagsImpl()),
new Handler(Looper.getMainLooper()));
}
Injector(@Nullable Context context, @NonNull FeatureFlags flags, @NonNull Handler handler) {
mContext = context;
mFlags = flags;
mHandler = handler;
}
private static DisplayDevice wrapDmDisplay(Display display, DisplayIsEnabled isEnabled) {
return new DisplayDevice(display.getDisplayId(), display.getName(),
display.getMode(), List.<Mode>of(display.getSupportedModes()), isEnabled);
}
/**
* @return all displays including disabled.
*/
@NonNull
public List<DisplayDevice> getConnectedDisplays() {
var dm = getDisplayManager();
if (dm == null) {
return List.of();
}
var enabledIds = new HashSet<Integer>();
for (Display d : dm.getDisplays()) {
enabledIds.add(d.getDisplayId());
}
var displays = new ArrayList<DisplayDevice>();
for (Display d : dm.getDisplays(DISPLAY_CATEGORY_ALL_INCLUDING_DISABLED)) {
if (!isDisplayAllowed(d, this)) {
continue;
}
var isEnabled = enabledIds.contains(d.getDisplayId())
? DisplayIsEnabled.YES : DisplayIsEnabled.NO;
displays.add(wrapDmDisplay(d, isEnabled));
}
return displays;
}
/**
* @param displayId which must be returned
* @return display object for the displayId, or null if display is not a connected display,
* the ID was not found, or the ID was invalid
*/
@Nullable
public DisplayDevice getDisplay(int displayId) {
if (displayId == INVALID_DISPLAY) {
return null;
}
var dm = getDisplayManager();
if (dm == null) {
return null;
}
var display = dm.getDisplay(displayId);
if (display == null || !isDisplayAllowed(display, this)) {
return null;
}
return wrapDmDisplay(display, DisplayIsEnabled.UNKNOWN);
}
/**
* Register display listener.
*/
public void registerDisplayListener(@NonNull DisplayManager.DisplayListener listener) {
var dm = getDisplayManager();
if (dm == null) {
return;
}
dm.registerDisplayListener(listener, mHandler, EVENT_TYPE_DISPLAY_ADDED
| EVENT_TYPE_DISPLAY_CHANGED | EVENT_TYPE_DISPLAY_REMOVED,
PRIVATE_EVENT_TYPE_DISPLAY_CONNECTION_CHANGED);
}
/**
* Unregister display listener.
*/
public void unregisterDisplayListener(@NonNull DisplayManager.DisplayListener listener) {
var dm = getDisplayManager();
if (dm == null) {
return;
}
dm.unregisterDisplayListener(listener);
}
/**
* @return feature flags.
*/
@NonNull
public FeatureFlags getFlags() {
return mFlags;
}
/**
* Enable connected display.
*/
public boolean enableConnectedDisplay(int displayId) {
var dm = getDisplayManager();
if (dm == null) {
return false;
}
dm.enableConnectedDisplay(displayId);
return true;
}
/**
* Disable connected display.
*/
public boolean disableConnectedDisplay(int displayId) {
var dm = getDisplayManager();
if (dm == null) {
return false;
}
dm.disableConnectedDisplay(displayId);
return true;
}
/**
* @return handler
*/
@NonNull
public Handler getHandler() {
return mHandler;
}
/**
* Get display rotation
* @param displayId display identifier
* @return rotation
*/
public int getDisplayUserRotation(int displayId) {
var wm = getWindowManager();
if (wm == null) {
return 0;
}
try {
return wm.getDisplayUserRotation(displayId);
} catch (RemoteException e) {
return 0;
}
}
/**
* Freeze rotation of the display in the specified rotation.
* @param displayId display identifier
* @param rotation [0, 1, 2, 3]
* @return true if successful
*/
public boolean freezeDisplayRotation(int displayId, int rotation) {
var wm = getWindowManager();
if (wm == null) {
return false;
}
try {
wm.freezeDisplayRotation(displayId, rotation,
"ExternalDisplayPreferenceFragment");
return true;
} catch (RemoteException e) {
return false;
}
}
/**
* Enforce display mode on the given display.
*/
public void setUserPreferredDisplayMode(int displayId, @NonNull Mode mode) {
DisplayManagerGlobal.getInstance().setUserPreferredDisplayMode(displayId, mode);
}
/**
* @return true if the display mode limit flag enabled.
*/
public boolean isModeLimitForExternalDisplayEnabled() {
return enableModeLimitForExternalDisplay();
}
}
public abstract static class DisplayListener implements DisplayManager.DisplayListener {
@Override
public void onDisplayAdded(int displayId) {
update(displayId);
}
@Override
public void onDisplayRemoved(int displayId) {
update(displayId);
}
@Override
public void onDisplayChanged(int displayId) {
update(displayId);
}
@Override
public void onDisplayConnected(int displayId) {
update(displayId);
}
@Override
public void onDisplayDisconnected(int displayId) {
update(displayId);
}
/**
* Called from other listener methods to trigger update of the settings page.
*/
public abstract void update(int displayId);
}
/**
* @return whether the settings page is enabled or not.
*/
public static boolean isExternalDisplaySettingsPageEnabled(@NonNull FeatureFlags flags) {
return flags.rotationConnectedDisplaySetting()
|| flags.resolutionAndEnableConnectedDisplaySetting()
|| flags.displayTopologyPaneInDisplayList();
}
private static boolean isDisplayAllowed(Display display, SystemServicesProvider props) {
return display.getType() == Display.TYPE_EXTERNAL
|| display.getType() == Display.TYPE_OVERLAY
|| isVirtualDisplayAllowed(display, props);
}
static boolean isTopologyPaneEnabled(@Nullable Injector injector) {
return injector != null && injector.getFlags().displayTopologyPaneInDisplayList();
}
private static boolean isVirtualDisplayAllowed(@NonNull Display display,
@NonNull SystemServicesProvider properties) {
var sysProp = properties.getSystemProperty(VIRTUAL_DISPLAY_PACKAGE_NAME_SYSTEM_PROPERTY);
return !sysProp.isEmpty() && display.getType() == Display.TYPE_VIRTUAL
&& sysProp.equals(display.getOwnerPackageName());
}
static boolean isUseDisplaySettingEnabled(@Nullable Injector injector) {
return injector != null && injector.getFlags().resolutionAndEnableConnectedDisplaySetting()
&& !injector.getFlags().displayTopologyPaneInDisplayList();
}
static boolean isResolutionSettingEnabled(@Nullable Injector injector) {
return injector != null && injector.getFlags().resolutionAndEnableConnectedDisplaySetting();
}
static boolean isRotationSettingEnabled(@Nullable Injector injector) {
return injector != null && injector.getFlags().rotationConnectedDisplaySetting();
}
static boolean isDisplaySizeSettingEnabled(@Nullable Injector injector) {
return injector != null && injector.getFlags().displaySizeConnectedDisplaySetting();
}
}