Merge "Updating window manager estimation logic:" into tm-dev am: d586e1948b
Original change: https://googleplex-android-review.googlesource.com/c/platform/packages/apps/Launcher3/+/17070486 Change-Id: I02864003cf66b733a420afec69756edade427cc4
This commit is contained in:
@@ -29,7 +29,6 @@ import android.content.Context;
|
||||
import android.content.Intent;
|
||||
import android.content.pm.LauncherApps;
|
||||
import android.content.res.Configuration;
|
||||
import android.graphics.Insets;
|
||||
import android.graphics.Point;
|
||||
import android.graphics.Rect;
|
||||
import android.graphics.drawable.Drawable;
|
||||
@@ -41,8 +40,6 @@ import android.util.Log;
|
||||
import android.view.ActionMode;
|
||||
import android.view.Display;
|
||||
import android.view.View;
|
||||
import android.view.WindowInsets.Type;
|
||||
import android.view.WindowMetrics;
|
||||
import android.widget.Toast;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
@@ -322,11 +319,7 @@ public abstract class BaseDraggingActivity extends BaseActivity
|
||||
|
||||
protected WindowBounds getMultiWindowDisplaySize() {
|
||||
if (Utilities.ATLEAST_R) {
|
||||
WindowMetrics wm = getWindowManager().getCurrentWindowMetrics();
|
||||
|
||||
Insets insets = wm.getWindowInsets().getInsets(Type.systemBars());
|
||||
return new WindowBounds(wm.getBounds(),
|
||||
new Rect(insets.left, insets.top, insets.right, insets.bottom));
|
||||
return WindowBounds.fromWindowMetrics(getWindowManager().getCurrentWindowMetrics());
|
||||
}
|
||||
// Note: Calls to getSize() can't rely on our cached DefaultDisplay since it can return
|
||||
// the app window size
|
||||
|
||||
@@ -75,6 +75,7 @@ public class DeviceProfile {
|
||||
public final int heightPx;
|
||||
public final int availableWidthPx;
|
||||
public final int availableHeightPx;
|
||||
public final int rotationHint;
|
||||
|
||||
public final float aspectRatio;
|
||||
|
||||
@@ -239,6 +240,7 @@ public class DeviceProfile {
|
||||
this.isGestureMode = isGestureMode;
|
||||
windowX = windowBounds.bounds.left;
|
||||
windowY = windowBounds.bounds.top;
|
||||
this.rotationHint = windowBounds.rotationHint;
|
||||
|
||||
isScalableGrid = inv.isScalable && !isVerticalBarLayout() && !isMultiWindowMode;
|
||||
|
||||
@@ -548,8 +550,8 @@ public class DeviceProfile {
|
||||
}
|
||||
|
||||
public Builder toBuilder(Context context) {
|
||||
WindowBounds bounds =
|
||||
new WindowBounds(widthPx, heightPx, availableWidthPx, availableHeightPx);
|
||||
WindowBounds bounds = new WindowBounds(
|
||||
widthPx, heightPx, availableWidthPx, availableHeightPx, rotationHint);
|
||||
bounds.bounds.offsetTo(windowX, windowY);
|
||||
return new Builder(context, inv, mInfo)
|
||||
.setWindowBounds(bounds)
|
||||
|
||||
@@ -55,6 +55,7 @@ import com.android.launcher3.util.IntArray;
|
||||
import com.android.launcher3.util.MainThreadInitializedObject;
|
||||
import com.android.launcher3.util.Themes;
|
||||
import com.android.launcher3.util.WindowBounds;
|
||||
import com.android.launcher3.util.window.WindowManagerProxy;
|
||||
|
||||
import org.xmlpull.v1.XmlPullParser;
|
||||
import org.xmlpull.v1.XmlPullParserException;
|
||||
@@ -180,8 +181,7 @@ public class InvariantDeviceProfile {
|
||||
private final ArrayList<OnIDPChangeListener> mChangeListeners = new ArrayList<>();
|
||||
|
||||
@VisibleForTesting
|
||||
public InvariantDeviceProfile() {
|
||||
}
|
||||
public InvariantDeviceProfile() { }
|
||||
|
||||
@TargetApi(23)
|
||||
private InvariantDeviceProfile(Context context) {
|
||||
@@ -278,11 +278,16 @@ public class InvariantDeviceProfile {
|
||||
}
|
||||
|
||||
private static @DeviceType int getDeviceType(Info displayInfo) {
|
||||
// Each screen has two profiles (portrait/landscape), so devices with four or more
|
||||
// supported profiles implies two or more internal displays.
|
||||
if (displayInfo.supportedBounds.size() >= 4 && ENABLE_TWO_PANEL_HOME.get()) {
|
||||
int flagPhone = 1 << 0;
|
||||
int flagTablet = 1 << 1;
|
||||
|
||||
int type = displayInfo.supportedBounds.stream()
|
||||
.mapToInt(bounds -> displayInfo.isTablet(bounds) ? flagTablet : flagPhone)
|
||||
.reduce(0, (a, b) -> a | b);
|
||||
if ((type == (flagPhone | flagTablet)) && ENABLE_TWO_PANEL_HOME.get()) {
|
||||
// device has profiles supporting both phone and table modes
|
||||
return TYPE_MULTI_DISPLAY;
|
||||
} else if (displayInfo.supportedBounds.stream().allMatch(displayInfo::isTablet)) {
|
||||
} else if (type == flagTablet) {
|
||||
return TYPE_TABLET;
|
||||
} else {
|
||||
return TYPE_PHONE;
|
||||
@@ -613,10 +618,14 @@ public class InvariantDeviceProfile {
|
||||
|
||||
float screenWidth = config.screenWidthDp * res.getDisplayMetrics().density;
|
||||
float screenHeight = config.screenHeightDp * res.getDisplayMetrics().density;
|
||||
return getBestMatch(screenWidth, screenHeight);
|
||||
return getBestMatch(screenWidth, screenHeight,
|
||||
WindowManagerProxy.INSTANCE.get(context).getRotation(context));
|
||||
}
|
||||
|
||||
public DeviceProfile getBestMatch(float screenWidth, float screenHeight) {
|
||||
/**
|
||||
* Returns the device profile matching the provided screen configuration
|
||||
*/
|
||||
public DeviceProfile getBestMatch(float screenWidth, float screenHeight, int rotation) {
|
||||
DeviceProfile bestMatch = supportedProfiles.get(0);
|
||||
float minDiff = Float.MAX_VALUE;
|
||||
|
||||
@@ -626,6 +635,8 @@ public class InvariantDeviceProfile {
|
||||
if (diff < minDiff) {
|
||||
minDiff = diff;
|
||||
bestMatch = profile;
|
||||
} else if (diff == minDiff && profile.rotationHint == rotation) {
|
||||
bestMatch = profile;
|
||||
}
|
||||
}
|
||||
return bestMatch;
|
||||
|
||||
@@ -1,24 +1,19 @@
|
||||
package com.android.launcher3;
|
||||
|
||||
import static com.android.launcher3.ResourceUtils.INVALID_RESOURCE_HANDLE;
|
||||
import static com.android.launcher3.config.FeatureFlags.SEPARATE_RECENTS_ACTIVITY;
|
||||
|
||||
import android.annotation.TargetApi;
|
||||
import android.content.Context;
|
||||
import android.content.res.Resources;
|
||||
import android.graphics.Canvas;
|
||||
import android.graphics.Insets;
|
||||
import android.graphics.Rect;
|
||||
import android.os.Build;
|
||||
import android.util.AttributeSet;
|
||||
import android.view.ViewDebug;
|
||||
import android.view.WindowInsets;
|
||||
|
||||
import androidx.annotation.RequiresApi;
|
||||
|
||||
import com.android.launcher3.graphics.SysUiScrim;
|
||||
import com.android.launcher3.statemanager.StatefulActivity;
|
||||
import com.android.launcher3.uioverrides.ApiWrapper;
|
||||
import com.android.launcher3.util.window.WindowManagerProxy;
|
||||
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
@@ -60,76 +55,12 @@ public class LauncherRootView extends InsettableFrameLayout {
|
||||
|
||||
@Override
|
||||
public WindowInsets onApplyWindowInsets(WindowInsets insets) {
|
||||
if (Utilities.ATLEAST_R) {
|
||||
insets = updateInsetsDueToTaskbar(insets);
|
||||
Insets systemWindowInsets = insets.getInsetsIgnoringVisibility(
|
||||
WindowInsets.Type.systemBars() | WindowInsets.Type.displayCutout());
|
||||
mTempRect.set(systemWindowInsets.left, systemWindowInsets.top, systemWindowInsets.right,
|
||||
systemWindowInsets.bottom);
|
||||
} else {
|
||||
mTempRect.set(insets.getSystemWindowInsetLeft(), insets.getSystemWindowInsetTop(),
|
||||
insets.getSystemWindowInsetRight(), insets.getSystemWindowInsetBottom());
|
||||
}
|
||||
insets = WindowManagerProxy.INSTANCE.get(getContext())
|
||||
.normalizeWindowInsets(getContext(), insets, mTempRect);
|
||||
handleSystemWindowInsets(mTempRect);
|
||||
return insets;
|
||||
}
|
||||
|
||||
/**
|
||||
* Taskbar provides nav bar and tappable insets. However, taskbar is not attached immediately,
|
||||
* and can be destroyed and recreated. Thus, instead of relying on taskbar being present to
|
||||
* get its insets, we calculate them ourselves so they are stable regardless of whether taskbar
|
||||
* is currently attached.
|
||||
*
|
||||
* @param oldInsets The system-provided insets, which we are modifying.
|
||||
* @return The updated insets.
|
||||
*/
|
||||
@RequiresApi(api = Build.VERSION_CODES.R)
|
||||
private WindowInsets updateInsetsDueToTaskbar(WindowInsets oldInsets) {
|
||||
if (!ApiWrapper.TASKBAR_DRAWN_IN_PROCESS) {
|
||||
// 3P launchers based on Launcher3 should still be inset like normal.
|
||||
return oldInsets;
|
||||
}
|
||||
|
||||
WindowInsets.Builder updatedInsetsBuilder = new WindowInsets.Builder(oldInsets);
|
||||
|
||||
DeviceProfile dp = mActivity.getDeviceProfile();
|
||||
Resources resources = getResources();
|
||||
|
||||
Insets oldNavInsets = oldInsets.getInsets(WindowInsets.Type.navigationBars());
|
||||
Rect newNavInsets = new Rect(oldNavInsets.left, oldNavInsets.top, oldNavInsets.right,
|
||||
oldNavInsets.bottom);
|
||||
|
||||
if (dp.isLandscape) {
|
||||
boolean isGesturalMode = ResourceUtils.getIntegerByName(
|
||||
"config_navBarInteractionMode",
|
||||
resources,
|
||||
INVALID_RESOURCE_HANDLE) == 2;
|
||||
if (dp.isTablet || isGesturalMode) {
|
||||
newNavInsets.bottom = dp.isTaskbarPresent
|
||||
? 0
|
||||
: ResourceUtils.getNavbarSize("navigation_bar_height_landscape", resources);
|
||||
} else {
|
||||
int navWidth = ResourceUtils.getNavbarSize("navigation_bar_width", resources);
|
||||
if (dp.isSeascape()) {
|
||||
newNavInsets.left = navWidth;
|
||||
} else {
|
||||
newNavInsets.right = navWidth;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
newNavInsets.bottom = dp.isTaskbarPresent
|
||||
? 0
|
||||
: ResourceUtils.getNavbarSize("navigation_bar_height", resources);
|
||||
}
|
||||
updatedInsetsBuilder.setInsets(WindowInsets.Type.navigationBars(), Insets.of(newNavInsets));
|
||||
updatedInsetsBuilder.setInsetsIgnoringVisibility(WindowInsets.Type.navigationBars(),
|
||||
Insets.of(newNavInsets));
|
||||
|
||||
mActivity.updateWindowInsets(updatedInsetsBuilder, oldInsets);
|
||||
|
||||
return updatedInsetsBuilder.build();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setInsets(Rect insets) {
|
||||
// If the insets haven't changed, this is a no-op. Avoid unnecessary layout caused by
|
||||
|
||||
@@ -28,6 +28,9 @@ public class ResourceUtils {
|
||||
public static final String NAVBAR_BOTTOM_GESTURE_LARGER_SIZE =
|
||||
"navigation_bar_gesture_larger_height";
|
||||
|
||||
public static final String NAVBAR_HEIGHT = "navigation_bar_height";
|
||||
public static final String NAVBAR_HEIGHT_LANDSCAPE = "navigation_bar_height_landscape";
|
||||
|
||||
public static int getNavbarSize(String resName, Resources res) {
|
||||
return getDimenByName(resName, res, DEFAULT_NAVBAR_VALUE);
|
||||
}
|
||||
|
||||
@@ -87,6 +87,7 @@ import com.android.launcher3.util.ComponentKey;
|
||||
import com.android.launcher3.util.IntArray;
|
||||
import com.android.launcher3.util.IntSet;
|
||||
import com.android.launcher3.util.MainThreadInitializedObject.SandboxContext;
|
||||
import com.android.launcher3.util.window.WindowManagerProxy;
|
||||
import com.android.launcher3.views.ActivityContext;
|
||||
import com.android.launcher3.views.BaseDragLayer;
|
||||
import com.android.launcher3.widget.BaseLauncherAppWidgetHostView;
|
||||
@@ -128,7 +129,8 @@ public class LauncherPreviewRenderer extends ContextWrapper
|
||||
public PreviewContext(Context base, InvariantDeviceProfile idp) {
|
||||
super(base, UserCache.INSTANCE, InstallSessionHelper.INSTANCE,
|
||||
LauncherAppState.INSTANCE, InvariantDeviceProfile.INSTANCE,
|
||||
CustomWidgetManager.INSTANCE, PluginManagerWrapper.INSTANCE);
|
||||
CustomWidgetManager.INSTANCE, PluginManagerWrapper.INSTANCE,
|
||||
WindowManagerProxy.INSTANCE);
|
||||
mIdp = idp;
|
||||
mObjectMap.put(InvariantDeviceProfile.INSTANCE, idp);
|
||||
mObjectMap.put(LauncherAppState.INSTANCE,
|
||||
|
||||
@@ -17,15 +17,11 @@ package com.android.launcher3.statemanager;
|
||||
|
||||
import static com.android.launcher3.LauncherState.FLAG_NON_INTERACTIVE;
|
||||
|
||||
import android.graphics.Insets;
|
||||
import android.os.Build;
|
||||
import android.os.Handler;
|
||||
import android.view.LayoutInflater;
|
||||
import android.view.View;
|
||||
import android.view.WindowInsets;
|
||||
|
||||
import androidx.annotation.CallSuper;
|
||||
import androidx.annotation.RequiresApi;
|
||||
|
||||
import com.android.launcher3.BaseDraggingActivity;
|
||||
import com.android.launcher3.LauncherRootView;
|
||||
@@ -178,14 +174,6 @@ public abstract class StatefulActivity<STATE_TYPE extends BaseState<STATE_TYPE>>
|
||||
Utilities.postAsyncCallback(mHandler, mHandleDeferredResume);
|
||||
}
|
||||
|
||||
/**
|
||||
* Gives subclasses a chance to override some window insets (via
|
||||
* {@link android.view.WindowInsets.Builder#setInsets(int, Insets)}).
|
||||
*/
|
||||
@RequiresApi(api = Build.VERSION_CODES.R)
|
||||
public void updateWindowInsets(WindowInsets.Builder updatedInsetsBuilder,
|
||||
WindowInsets oldInsets) { }
|
||||
|
||||
/**
|
||||
* Runs the given {@param r} runnable when this activity binds to the touch interaction service.
|
||||
*/
|
||||
|
||||
@@ -21,7 +21,7 @@ import static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_UNSPECIFIED;
|
||||
import static android.util.DisplayMetrics.DENSITY_DEVICE_STABLE;
|
||||
|
||||
import static com.android.launcher3.Utilities.dpiFromPx;
|
||||
import static com.android.launcher3.util.WindowManagerCompat.MIN_TABLET_WIDTH;
|
||||
import static com.android.launcher3.util.window.WindowManagerProxy.MIN_TABLET_WIDTH;
|
||||
|
||||
import android.content.SharedPreferences;
|
||||
import android.content.SharedPreferences.OnSharedPreferenceChangeListener;
|
||||
|
||||
@@ -26,10 +26,8 @@ import static com.android.launcher3.logging.StatsLogManager.LauncherEvent.LAUNCH
|
||||
import static com.android.launcher3.logging.StatsLogManager.LauncherEvent.LAUNCHER_NAVIGATION_MODE_GESTURE_BUTTON;
|
||||
import static com.android.launcher3.util.Executors.MAIN_EXECUTOR;
|
||||
import static com.android.launcher3.util.PackageManagerHelper.getPackageFilter;
|
||||
import static com.android.launcher3.util.WindowManagerCompat.MIN_LARGE_TABLET_WIDTH;
|
||||
import static com.android.launcher3.util.WindowManagerCompat.MIN_TABLET_WIDTH;
|
||||
|
||||
import static java.util.Collections.emptyMap;
|
||||
import static com.android.launcher3.util.window.WindowManagerProxy.MIN_LARGE_TABLET_WIDTH;
|
||||
import static com.android.launcher3.util.window.WindowManagerProxy.MIN_TABLET_WIDTH;
|
||||
|
||||
import android.annotation.SuppressLint;
|
||||
import android.annotation.TargetApi;
|
||||
@@ -38,12 +36,13 @@ import android.content.Context;
|
||||
import android.content.Intent;
|
||||
import android.content.res.Configuration;
|
||||
import android.graphics.Point;
|
||||
import android.graphics.Rect;
|
||||
import android.hardware.display.DisplayManager;
|
||||
import android.os.Build;
|
||||
import android.text.TextUtils;
|
||||
import android.util.ArrayMap;
|
||||
import android.util.ArraySet;
|
||||
import android.util.Log;
|
||||
import android.util.Pair;
|
||||
import android.view.Display;
|
||||
|
||||
import androidx.annotation.AnyThread;
|
||||
@@ -52,11 +51,12 @@ import androidx.annotation.UiThread;
|
||||
import com.android.launcher3.ResourceUtils;
|
||||
import com.android.launcher3.Utilities;
|
||||
import com.android.launcher3.logging.StatsLogManager.LauncherEvent;
|
||||
import com.android.launcher3.uioverrides.ApiWrapper;
|
||||
import com.android.launcher3.util.window.CachedDisplayInfo;
|
||||
import com.android.launcher3.util.window.WindowManagerProxy;
|
||||
|
||||
import java.io.PrintWriter;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Map;
|
||||
import java.util.Collections;
|
||||
import java.util.Objects;
|
||||
import java.util.Set;
|
||||
|
||||
@@ -89,6 +89,7 @@ public class DisplayController implements ComponentCallbacks, SafeCloseable {
|
||||
|
||||
// Null for SDK < S
|
||||
private final Context mWindowContext;
|
||||
|
||||
// The callback in this listener updates DeviceProfile, which other listeners might depend on
|
||||
private DisplayInfoChangeListener mPriorityListener;
|
||||
private final ArrayList<DisplayInfoChangeListener> mListeners = new ArrayList<>();
|
||||
@@ -115,23 +116,9 @@ public class DisplayController implements ComponentCallbacks, SafeCloseable {
|
||||
mContext.registerReceiver(mReceiver,
|
||||
getPackageFilter(TARGET_OVERLAY_PACKAGE, ACTION_OVERLAY_CHANGED));
|
||||
|
||||
WindowManagerProxy wmProxy = WindowManagerProxy.INSTANCE.get(context);
|
||||
mInfo = new Info(getDisplayInfoContext(display), display,
|
||||
getInternalDisplays(mDM), emptyMap());
|
||||
}
|
||||
|
||||
private static ArrayMap<String, PortraitSize> getInternalDisplays(
|
||||
DisplayManager displayManager) {
|
||||
Display[] displays = displayManager.getDisplays();
|
||||
ArrayMap<String, PortraitSize> internalDisplays = new ArrayMap<>();
|
||||
for (Display display : displays) {
|
||||
if (ApiWrapper.isInternalDisplay(display)) {
|
||||
Point size = new Point();
|
||||
display.getRealSize(size);
|
||||
internalDisplays.put(ApiWrapper.getUniqueId(display),
|
||||
new PortraitSize(size.x, size.y));
|
||||
}
|
||||
}
|
||||
return internalDisplays;
|
||||
wmProxy, wmProxy.estimateInternalDisplayBounds(context));
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -226,16 +213,17 @@ public class DisplayController implements ComponentCallbacks, SafeCloseable {
|
||||
|
||||
@AnyThread
|
||||
private void handleInfoChange(Display display) {
|
||||
WindowManagerProxy wmProxy = WindowManagerProxy.INSTANCE.get(mContext);
|
||||
Info oldInfo = mInfo;
|
||||
|
||||
Context displayContext = getDisplayInfoContext(display);
|
||||
Info newInfo = new Info(displayContext, display,
|
||||
oldInfo.mInternalDisplays, oldInfo.mPerDisplayBounds);
|
||||
Info newInfo = new Info(displayContext, display, wmProxy, oldInfo.mPerDisplayBounds);
|
||||
|
||||
if (newInfo.densityDpi != oldInfo.densityDpi || newInfo.fontScale != oldInfo.fontScale
|
||||
|| newInfo.navigationMode != oldInfo.navigationMode) {
|
||||
// Cache may not be valid anymore, recreate without cache
|
||||
newInfo = new Info(displayContext, display, getInternalDisplays(mDM), emptyMap());
|
||||
newInfo = new Info(displayContext, display, wmProxy,
|
||||
wmProxy.estimateInternalDisplayBounds(displayContext));
|
||||
}
|
||||
|
||||
int change = 0;
|
||||
@@ -254,9 +242,8 @@ public class DisplayController implements ComponentCallbacks, SafeCloseable {
|
||||
if (!newInfo.supportedBounds.equals(oldInfo.supportedBounds)) {
|
||||
change |= CHANGE_SUPPORTED_BOUNDS;
|
||||
|
||||
PortraitSize realSize = new PortraitSize(newInfo.currentSize.x, newInfo.currentSize.y);
|
||||
PortraitSize expectedSize = oldInfo.mInternalDisplays.get(
|
||||
ApiWrapper.getUniqueId(display));
|
||||
Point currentS = newInfo.currentSize;
|
||||
Point expectedS = oldInfo.mPerDisplayBounds.get(newInfo.displayId).first.size;
|
||||
if (newInfo.supportedBounds.size() != oldInfo.supportedBounds.size()) {
|
||||
Log.e("b/198965093",
|
||||
"Inconsistent number of displays"
|
||||
@@ -264,7 +251,9 @@ public class DisplayController implements ComponentCallbacks, SafeCloseable {
|
||||
+ "\noldInfo.supportedBounds: " + oldInfo.supportedBounds
|
||||
+ "\nnewInfo.supportedBounds: " + newInfo.supportedBounds);
|
||||
}
|
||||
if (!realSize.equals(expectedSize) && display.getState() == Display.STATE_OFF) {
|
||||
if ((Math.min(currentS.x, currentS.y) != Math.min(expectedS.x, expectedS.y)
|
||||
|| Math.max(currentS.x, currentS.y) != Math.max(expectedS.x, expectedS.y))
|
||||
&& display.getState() == Display.STATE_OFF) {
|
||||
Log.e("b/198965093", "Display size changed while display is off, ignoring change");
|
||||
return;
|
||||
}
|
||||
@@ -290,30 +279,38 @@ public class DisplayController implements ComponentCallbacks, SafeCloseable {
|
||||
|
||||
public static class Info {
|
||||
|
||||
// Configuration properties
|
||||
// Cached property
|
||||
public final int rotation;
|
||||
public final String displayId;
|
||||
public final Point currentSize;
|
||||
public final Rect cutout;
|
||||
|
||||
// Configuration property
|
||||
public final float fontScale;
|
||||
public final int densityDpi;
|
||||
public final NavigationMode navigationMode;
|
||||
|
||||
private final PortraitSize mScreenSizeDp;
|
||||
|
||||
public final Point currentSize;
|
||||
|
||||
public String displayId;
|
||||
public final Set<WindowBounds> supportedBounds = new ArraySet<>();
|
||||
private final Map<String, Set<WindowBounds>> mPerDisplayBounds = new ArrayMap<>();
|
||||
private final ArrayMap<String, PortraitSize> mInternalDisplays;
|
||||
|
||||
private final ArrayMap<String, Pair<CachedDisplayInfo, WindowBounds[]>> mPerDisplayBounds =
|
||||
new ArrayMap<>();
|
||||
|
||||
public Info(Context context, Display display) {
|
||||
this(context, display, new ArrayMap<>(), emptyMap());
|
||||
/* don't need system overrides for external displays */
|
||||
this(context, display, new WindowManagerProxy(), new ArrayMap<>());
|
||||
}
|
||||
|
||||
private Info(Context context, Display display,
|
||||
ArrayMap<String, PortraitSize> internalDisplays,
|
||||
Map<String, Set<WindowBounds>> perDisplayBoundsCache) {
|
||||
mInternalDisplays = internalDisplays;
|
||||
rotation = display.getRotation();
|
||||
// Used for testing
|
||||
public Info(Context context, Display display,
|
||||
WindowManagerProxy wmProxy,
|
||||
ArrayMap<String, Pair<CachedDisplayInfo, WindowBounds[]>> perDisplayBoundsCache) {
|
||||
CachedDisplayInfo displayInfo = wmProxy.getDisplayInfo(display);
|
||||
rotation = displayInfo.rotation;
|
||||
currentSize = displayInfo.size;
|
||||
displayId = displayInfo.id;
|
||||
cutout = displayInfo.cutout;
|
||||
|
||||
Configuration config = context.getResources().getConfiguration();
|
||||
fontScale = config.fontScale;
|
||||
@@ -321,54 +318,29 @@ public class DisplayController implements ComponentCallbacks, SafeCloseable {
|
||||
mScreenSizeDp = new PortraitSize(config.screenHeightDp, config.screenWidthDp);
|
||||
navigationMode = parseNavigationMode(context);
|
||||
|
||||
currentSize = new Point();
|
||||
display.getRealSize(currentSize);
|
||||
mPerDisplayBounds.putAll(perDisplayBoundsCache);
|
||||
Pair<CachedDisplayInfo, WindowBounds[]> cachedValue = mPerDisplayBounds.get(displayId);
|
||||
|
||||
displayId = ApiWrapper.getUniqueId(display);
|
||||
Set<WindowBounds> currentSupportedBounds =
|
||||
getSupportedBoundsForDisplay(display, currentSize);
|
||||
mPerDisplayBounds.put(displayId, currentSupportedBounds);
|
||||
supportedBounds.addAll(currentSupportedBounds);
|
||||
|
||||
if (ApiWrapper.isInternalDisplay(display) && internalDisplays.size() > 1) {
|
||||
int displayCount = internalDisplays.size();
|
||||
for (int i = 0; i < displayCount; i++) {
|
||||
String displayKey = internalDisplays.keyAt(i);
|
||||
if (TextUtils.equals(displayId, displayKey)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
Set<WindowBounds> displayBounds = perDisplayBoundsCache.get(displayKey);
|
||||
if (displayBounds == null) {
|
||||
// We assume densityDpi is the same across all internal displays
|
||||
displayBounds = WindowManagerCompat.estimateDisplayProfiles(
|
||||
context, internalDisplays.valueAt(i), densityDpi,
|
||||
ApiWrapper.TASKBAR_DRAWN_IN_PROCESS);
|
||||
}
|
||||
|
||||
supportedBounds.addAll(displayBounds);
|
||||
mPerDisplayBounds.put(displayKey, displayBounds);
|
||||
WindowBounds realBounds = wmProxy.getRealBounds(context, display, displayInfo);
|
||||
if (cachedValue == null) {
|
||||
supportedBounds.add(realBounds);
|
||||
} else {
|
||||
// Verify that the real bounds are a match
|
||||
WindowBounds expectedBounds = cachedValue.second[displayInfo.rotation];
|
||||
if (!realBounds.equals(expectedBounds)) {
|
||||
WindowBounds[] clone = new WindowBounds[4];
|
||||
System.arraycopy(cachedValue.second, 0, clone, 0, 4);
|
||||
clone[displayInfo.rotation] = realBounds;
|
||||
cachedValue = Pair.create(displayInfo.normalize(), clone);
|
||||
mPerDisplayBounds.put(displayId, cachedValue);
|
||||
}
|
||||
}
|
||||
mPerDisplayBounds.values().forEach(
|
||||
pair -> Collections.addAll(supportedBounds, pair.second));
|
||||
Log.d("b/211775278", "displayId: " + displayId + ", currentSize: " + currentSize);
|
||||
Log.d("b/211775278", "perDisplayBounds: " + mPerDisplayBounds);
|
||||
}
|
||||
|
||||
private static Set<WindowBounds> getSupportedBoundsForDisplay(Display display, Point size) {
|
||||
Point smallestSize = new Point();
|
||||
Point largestSize = new Point();
|
||||
display.getCurrentSizeRange(smallestSize, largestSize);
|
||||
|
||||
int portraitWidth = Math.min(size.x, size.y);
|
||||
int portraitHeight = Math.max(size.x, size.y);
|
||||
Set<WindowBounds> result = new ArraySet<>();
|
||||
result.add(new WindowBounds(portraitWidth, portraitHeight,
|
||||
smallestSize.x, largestSize.y));
|
||||
result.add(new WindowBounds(portraitHeight, portraitWidth,
|
||||
largestSize.x, smallestSize.y));
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns {@code true} if the bounds represent a tablet.
|
||||
*/
|
||||
|
||||
@@ -0,0 +1,75 @@
|
||||
/*
|
||||
* Copyright (C) 2022 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.launcher3.util;
|
||||
|
||||
import static android.view.Surface.ROTATION_0;
|
||||
import static android.view.Surface.ROTATION_180;
|
||||
import static android.view.Surface.ROTATION_270;
|
||||
import static android.view.Surface.ROTATION_90;
|
||||
|
||||
import android.graphics.Point;
|
||||
import android.graphics.Rect;
|
||||
|
||||
/**
|
||||
* Utility methods based on {@code frameworks/base/core/java/android/util/RotationUtils.java}
|
||||
*/
|
||||
public class RotationUtils {
|
||||
|
||||
/**
|
||||
* Rotates an Rect according to the given rotation.
|
||||
*/
|
||||
public static void rotateRect(Rect rect, int rotation) {
|
||||
switch (rotation) {
|
||||
case ROTATION_0:
|
||||
return;
|
||||
case ROTATION_90:
|
||||
rect.set(rect.top, rect.right, rect.bottom, rect.left);
|
||||
return;
|
||||
case ROTATION_180:
|
||||
rect.set(rect.right, rect.bottom, rect.left, rect.top);
|
||||
return;
|
||||
case ROTATION_270:
|
||||
rect.set(rect.bottom, rect.left, rect.top, rect.right);
|
||||
return;
|
||||
default:
|
||||
throw new IllegalArgumentException("unknown rotation: " + rotation);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Rotates an size according to the given rotation.
|
||||
*/
|
||||
public static void rotateSize(Point size, int rotation) {
|
||||
switch (rotation) {
|
||||
case ROTATION_0:
|
||||
case ROTATION_180:
|
||||
return;
|
||||
case ROTATION_90:
|
||||
case ROTATION_270:
|
||||
size.set(size.y, size.x);
|
||||
return;
|
||||
default:
|
||||
throw new IllegalArgumentException("unknown rotation: " + rotation);
|
||||
}
|
||||
}
|
||||
|
||||
/** @return the rotation needed to rotate from oldRotation to newRotation. */
|
||||
public static int deltaRotation(int oldRotation, int newRotation) {
|
||||
int delta = newRotation - oldRotation;
|
||||
if (delta < 0) delta += 4;
|
||||
return delta;
|
||||
}
|
||||
}
|
||||
@@ -33,19 +33,27 @@ public class WindowBounds {
|
||||
public final Rect bounds;
|
||||
public final Rect insets;
|
||||
public final Point availableSize;
|
||||
public final int rotationHint;
|
||||
|
||||
public WindowBounds(Rect bounds, Rect insets) {
|
||||
this(bounds, insets, -1);
|
||||
}
|
||||
|
||||
public WindowBounds(Rect bounds, Rect insets, int rotationHint) {
|
||||
this.bounds = bounds;
|
||||
this.insets = insets;
|
||||
this.rotationHint = rotationHint;
|
||||
availableSize = new Point(bounds.width() - insets.left - insets.right,
|
||||
bounds.height() - insets.top - insets.bottom);
|
||||
}
|
||||
|
||||
public WindowBounds(int width, int height, int availableWidth, int availableHeight) {
|
||||
public WindowBounds(int width, int height, int availableWidth, int availableHeight,
|
||||
int rotationHint) {
|
||||
this.bounds = new Rect(0, 0, width, height);
|
||||
this.availableSize = new Point(availableWidth, availableHeight);
|
||||
// We don't care about insets in this case
|
||||
this.insets = new Rect(0, 0, width - availableWidth, height - availableHeight);
|
||||
this.rotationHint = rotationHint;
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
@@ -1,109 +0,0 @@
|
||||
/*
|
||||
* 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.launcher3.util;
|
||||
|
||||
import static com.android.launcher3.ResourceUtils.INVALID_RESOURCE_HANDLE;
|
||||
import static com.android.launcher3.Utilities.dpiFromPx;
|
||||
|
||||
import android.annotation.TargetApi;
|
||||
import android.content.Context;
|
||||
import android.content.res.Configuration;
|
||||
import android.graphics.Insets;
|
||||
import android.graphics.Rect;
|
||||
import android.os.Build;
|
||||
import android.util.ArraySet;
|
||||
import android.view.WindowInsets;
|
||||
import android.view.WindowInsets.Type;
|
||||
import android.view.WindowManager;
|
||||
import android.view.WindowMetrics;
|
||||
|
||||
import com.android.launcher3.R;
|
||||
import com.android.launcher3.ResourceUtils;
|
||||
import com.android.launcher3.Utilities;
|
||||
import com.android.launcher3.util.DisplayController.PortraitSize;
|
||||
|
||||
import java.util.Collections;
|
||||
import java.util.Set;
|
||||
|
||||
/**
|
||||
* Utility class to estimate window manager values
|
||||
*/
|
||||
@TargetApi(Build.VERSION_CODES.S)
|
||||
public class WindowManagerCompat {
|
||||
|
||||
public static final int MIN_TABLET_WIDTH = 600;
|
||||
public static final int MIN_LARGE_TABLET_WIDTH = 720;
|
||||
|
||||
/**
|
||||
* Returns a set of supported render sizes for a internal display.
|
||||
* This is a temporary workaround which assumes only nav-bar insets change across displays, and
|
||||
* is only used until we eventually get the real values
|
||||
* @param consumeTaskBar if true, it assumes that task bar is part of the app window
|
||||
* and ignores any insets because of task bar.
|
||||
*/
|
||||
public static Set<WindowBounds> estimateDisplayProfiles(
|
||||
Context windowContext, PortraitSize size, int densityDpi, boolean consumeTaskBar) {
|
||||
if (!Utilities.ATLEAST_S) {
|
||||
return Collections.emptySet();
|
||||
}
|
||||
WindowInsets defaultInsets = windowContext.getSystemService(WindowManager.class)
|
||||
.getMaximumWindowMetrics().getWindowInsets();
|
||||
boolean isGesturalMode = ResourceUtils.getIntegerByName(
|
||||
"config_navBarInteractionMode",
|
||||
windowContext.getResources(),
|
||||
INVALID_RESOURCE_HANDLE) == 2;
|
||||
|
||||
WindowInsets.Builder insetsBuilder = new WindowInsets.Builder(defaultInsets);
|
||||
Set<WindowBounds> result = new ArraySet<>();
|
||||
int swDP = (int) dpiFromPx(size.width, densityDpi);
|
||||
boolean isTablet = swDP >= MIN_TABLET_WIDTH;
|
||||
|
||||
final Insets portraitNav, landscapeNav;
|
||||
if (isTablet && !consumeTaskBar) {
|
||||
portraitNav = landscapeNav = Insets.of(0, 0, 0, windowContext.getResources()
|
||||
.getDimensionPixelSize(R.dimen.taskbar_size));
|
||||
} else if (!isGesturalMode) {
|
||||
portraitNav = Insets.of(0, 0, 0,
|
||||
getSystemResource(windowContext, "navigation_bar_height", swDP));
|
||||
landscapeNav = isTablet
|
||||
? Insets.of(0, 0, 0, getSystemResource(windowContext,
|
||||
"navigation_bar_height_landscape", swDP))
|
||||
: Insets.of(0, 0, getSystemResource(windowContext,
|
||||
"navigation_bar_width", swDP), 0);
|
||||
} else {
|
||||
portraitNav = landscapeNav = Insets.of(0, 0, 0, 0);
|
||||
}
|
||||
|
||||
result.add(WindowBounds.fromWindowMetrics(new WindowMetrics(
|
||||
new Rect(0, 0, size.width, size.height),
|
||||
insetsBuilder.setInsets(Type.navigationBars(), portraitNav).build())));
|
||||
result.add(WindowBounds.fromWindowMetrics(new WindowMetrics(
|
||||
new Rect(0, 0, size.height, size.width),
|
||||
insetsBuilder.setInsets(Type.navigationBars(), landscapeNav).build())));
|
||||
return result;
|
||||
}
|
||||
|
||||
private static int getSystemResource(Context context, String key, int swDp) {
|
||||
int resourceId = context.getResources().getIdentifier(key, "dimen", "android");
|
||||
if (resourceId > 0) {
|
||||
Configuration conf = new Configuration();
|
||||
conf.smallestScreenWidthDp = swDp;
|
||||
return context.createConfigurationContext(conf)
|
||||
.getResources().getDimensionPixelSize(resourceId);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,92 @@
|
||||
/*
|
||||
* Copyright (C) 2022 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.launcher3.util.window;
|
||||
|
||||
import static com.android.launcher3.util.RotationUtils.deltaRotation;
|
||||
import static com.android.launcher3.util.RotationUtils.rotateRect;
|
||||
import static com.android.launcher3.util.RotationUtils.rotateSize;
|
||||
|
||||
import android.graphics.Point;
|
||||
import android.graphics.Rect;
|
||||
import android.view.Surface;
|
||||
|
||||
import java.util.Objects;
|
||||
|
||||
/**
|
||||
* Properties on a display
|
||||
*/
|
||||
public class CachedDisplayInfo {
|
||||
|
||||
public final String id;
|
||||
public final Point size;
|
||||
public final int rotation;
|
||||
public final Rect cutout;
|
||||
|
||||
public CachedDisplayInfo() {
|
||||
this(new Point(0, 0), 0);
|
||||
}
|
||||
|
||||
public CachedDisplayInfo(Point size, int rotation) {
|
||||
this("", size, rotation, new Rect());
|
||||
}
|
||||
|
||||
public CachedDisplayInfo(String id, Point size, int rotation, Rect cutout) {
|
||||
this.id = id;
|
||||
this.size = size;
|
||||
this.rotation = rotation;
|
||||
this.cutout = cutout;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a CachedDisplayInfo where the properties are normalized to {@link Surface#ROTATION_0}
|
||||
*/
|
||||
public CachedDisplayInfo normalize() {
|
||||
if (rotation == Surface.ROTATION_0) {
|
||||
return this;
|
||||
}
|
||||
Point newSize = new Point(size);
|
||||
rotateSize(newSize, deltaRotation(rotation, Surface.ROTATION_0));
|
||||
|
||||
Rect newCutout = new Rect(cutout);
|
||||
rotateRect(newCutout, deltaRotation(rotation, Surface.ROTATION_0));
|
||||
return new CachedDisplayInfo(id, newSize, Surface.ROTATION_0, newCutout);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "CachedDisplayInfo{"
|
||||
+ "id='" + id + '\''
|
||||
+ ", size=" + size
|
||||
+ ", rotation=" + rotation
|
||||
+ ", cutout=" + cutout
|
||||
+ '}';
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object o) {
|
||||
if (this == o) return true;
|
||||
if (!(o instanceof CachedDisplayInfo)) return false;
|
||||
CachedDisplayInfo that = (CachedDisplayInfo) o;
|
||||
return rotation == that.rotation && Objects.equals(id, that.id)
|
||||
&& Objects.equals(size, that.size) && Objects.equals(cutout,
|
||||
that.cutout);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
return Objects.hash(id, size, rotation, cutout);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,311 @@
|
||||
/*
|
||||
* Copyright (C) 2022 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.launcher3.util.window;
|
||||
|
||||
import static com.android.launcher3.ResourceUtils.INVALID_RESOURCE_HANDLE;
|
||||
import static com.android.launcher3.ResourceUtils.NAVBAR_HEIGHT;
|
||||
import static com.android.launcher3.ResourceUtils.NAVBAR_HEIGHT_LANDSCAPE;
|
||||
import static com.android.launcher3.ResourceUtils.NAVBAR_LANDSCAPE_LEFT_RIGHT_SIZE;
|
||||
import static com.android.launcher3.ResourceUtils.getDimenByName;
|
||||
import static com.android.launcher3.Utilities.dpiFromPx;
|
||||
import static com.android.launcher3.util.MainThreadInitializedObject.forOverride;
|
||||
import static com.android.launcher3.util.RotationUtils.deltaRotation;
|
||||
import static com.android.launcher3.util.RotationUtils.rotateRect;
|
||||
import static com.android.launcher3.util.RotationUtils.rotateSize;
|
||||
|
||||
import android.annotation.TargetApi;
|
||||
import android.content.Context;
|
||||
import android.content.res.Configuration;
|
||||
import android.content.res.Resources;
|
||||
import android.graphics.Insets;
|
||||
import android.graphics.Point;
|
||||
import android.graphics.Rect;
|
||||
import android.hardware.display.DisplayManager;
|
||||
import android.os.Build;
|
||||
import android.util.ArrayMap;
|
||||
import android.util.Pair;
|
||||
import android.view.Display;
|
||||
import android.view.DisplayCutout;
|
||||
import android.view.Surface;
|
||||
import android.view.WindowInsets;
|
||||
import android.view.WindowManager;
|
||||
import android.view.WindowMetrics;
|
||||
|
||||
import com.android.launcher3.R;
|
||||
import com.android.launcher3.ResourceUtils;
|
||||
import com.android.launcher3.Utilities;
|
||||
import com.android.launcher3.util.DisplayController;
|
||||
import com.android.launcher3.util.MainThreadInitializedObject;
|
||||
import com.android.launcher3.util.ResourceBasedOverride;
|
||||
import com.android.launcher3.util.WindowBounds;
|
||||
|
||||
/**
|
||||
* Utility class for mocking some window manager behaviours
|
||||
*/
|
||||
public class WindowManagerProxy implements ResourceBasedOverride {
|
||||
|
||||
public static final int MIN_TABLET_WIDTH = 600;
|
||||
public static final int MIN_LARGE_TABLET_WIDTH = 720;
|
||||
|
||||
public static final MainThreadInitializedObject<WindowManagerProxy> INSTANCE =
|
||||
forOverride(WindowManagerProxy.class, R.string.window_manager_proxy_class);
|
||||
|
||||
protected final boolean mTaskbarDrawnInProcess;
|
||||
|
||||
/**
|
||||
* Creates a new instance of proxy, applying any overrides
|
||||
*/
|
||||
public static WindowManagerProxy newInstance(Context context) {
|
||||
return Overrides.getObject(WindowManagerProxy.class, context,
|
||||
R.string.window_manager_proxy_class);
|
||||
}
|
||||
|
||||
public WindowManagerProxy() {
|
||||
this(false);
|
||||
}
|
||||
|
||||
protected WindowManagerProxy(boolean taskbarDrawnInProcess) {
|
||||
mTaskbarDrawnInProcess = taskbarDrawnInProcess;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a map of normalized info of internal displays to estimated window bounds
|
||||
* for that display
|
||||
*/
|
||||
public ArrayMap<String, Pair<CachedDisplayInfo, WindowBounds[]>> estimateInternalDisplayBounds(
|
||||
Context context) {
|
||||
Display[] displays = context.getSystemService(DisplayManager.class).getDisplays();
|
||||
ArrayMap<String, Pair<CachedDisplayInfo, WindowBounds[]>> result = new ArrayMap<>();
|
||||
for (Display display : displays) {
|
||||
if (isInternalDisplay(display)) {
|
||||
CachedDisplayInfo info = getDisplayInfo(display).normalize();
|
||||
WindowBounds[] bounds = estimateWindowBounds(context, info);
|
||||
result.put(info.id, Pair.create(info, bounds));
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the real bounds for the provided display after applying any insets normalization
|
||||
*/
|
||||
@TargetApi(Build.VERSION_CODES.R)
|
||||
public WindowBounds getRealBounds(Context windowContext,
|
||||
Display display, CachedDisplayInfo info) {
|
||||
if (!Utilities.ATLEAST_R) {
|
||||
Point smallestSize = new Point();
|
||||
Point largestSize = new Point();
|
||||
display.getCurrentSizeRange(smallestSize, largestSize);
|
||||
|
||||
if (info.size.y > info.size.x) {
|
||||
// Portrait
|
||||
return new WindowBounds(info.size.x, info.size.y, smallestSize.x, largestSize.y,
|
||||
info.rotation);
|
||||
} else {
|
||||
// Landscape
|
||||
new WindowBounds(info.size.x, info.size.y, largestSize.x, smallestSize.y,
|
||||
info.rotation);
|
||||
}
|
||||
}
|
||||
|
||||
WindowMetrics wm = windowContext.getSystemService(WindowManager.class)
|
||||
.getCurrentWindowMetrics();
|
||||
|
||||
Rect insets = new Rect();
|
||||
normalizeWindowInsets(windowContext, wm.getWindowInsets(), insets);
|
||||
return new WindowBounds(wm.getBounds(), insets, info.rotation);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns an updated insets, accounting for various Launcher UI specific overrides like taskbar
|
||||
*/
|
||||
@TargetApi(Build.VERSION_CODES.R)
|
||||
public WindowInsets normalizeWindowInsets(Context context, WindowInsets oldInsets,
|
||||
Rect outInsets) {
|
||||
if (!Utilities.ATLEAST_R || !mTaskbarDrawnInProcess) {
|
||||
outInsets.set(oldInsets.getSystemWindowInsetLeft(), oldInsets.getSystemWindowInsetTop(),
|
||||
oldInsets.getSystemWindowInsetRight(), oldInsets.getSystemWindowInsetBottom());
|
||||
return oldInsets;
|
||||
}
|
||||
|
||||
WindowInsets.Builder insetsBuilder = new WindowInsets.Builder(oldInsets);
|
||||
Insets navInsets = oldInsets.getInsets(WindowInsets.Type.navigationBars());
|
||||
|
||||
Resources systemRes = context.getResources();
|
||||
Configuration config = systemRes.getConfiguration();
|
||||
|
||||
boolean isTablet = config.smallestScreenWidthDp > MIN_TABLET_WIDTH;
|
||||
boolean isGesture = isGestureNav(context);
|
||||
|
||||
int bottomNav = isTablet
|
||||
? 0
|
||||
: (config.screenHeightDp > config.screenWidthDp
|
||||
? getDimenByName(NAVBAR_HEIGHT, systemRes, 0)
|
||||
: (isGesture
|
||||
? getDimenByName(NAVBAR_HEIGHT_LANDSCAPE, systemRes, 0)
|
||||
: 0));
|
||||
Insets newNavInsets = Insets.of(navInsets.left, navInsets.top, navInsets.right, bottomNav);
|
||||
insetsBuilder.setInsets(WindowInsets.Type.navigationBars(), newNavInsets);
|
||||
insetsBuilder.setInsetsIgnoringVisibility(WindowInsets.Type.navigationBars(), newNavInsets);
|
||||
|
||||
// Override the tappable insets to be 0 on the bottom for gesture nav (otherwise taskbar
|
||||
// would count towards it). This is used for the bottom protection in All Apps for example.
|
||||
if (isGesture) {
|
||||
Insets oldTappableInsets = oldInsets.getInsets(WindowInsets.Type.tappableElement());
|
||||
Insets newTappableInsets = Insets.of(oldTappableInsets.left, oldTappableInsets.top,
|
||||
oldTappableInsets.right, 0);
|
||||
insetsBuilder.setInsets(WindowInsets.Type.tappableElement(), newTappableInsets);
|
||||
}
|
||||
|
||||
WindowInsets result = insetsBuilder.build();
|
||||
Insets systemWindowInsets = result.getInsetsIgnoringVisibility(
|
||||
WindowInsets.Type.systemBars() | WindowInsets.Type.displayCutout());
|
||||
outInsets.set(systemWindowInsets.left, systemWindowInsets.top, systemWindowInsets.right,
|
||||
systemWindowInsets.bottom);
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns true if the display is an internal displays
|
||||
*/
|
||||
protected boolean isInternalDisplay(Display display) {
|
||||
return display.getDisplayId() == Display.DEFAULT_DISPLAY;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a list of possible WindowBounds for the display keyed on the 4 surface rotations
|
||||
*/
|
||||
public WindowBounds[] estimateWindowBounds(Context context, CachedDisplayInfo display) {
|
||||
int densityDpi = context.getResources().getConfiguration().densityDpi;
|
||||
int rotation = display.rotation;
|
||||
Rect safeCutout = display.cutout;
|
||||
|
||||
int minSize = Math.min(display.size.x, display.size.y);
|
||||
int swDp = (int) dpiFromPx(minSize, densityDpi);
|
||||
|
||||
Resources systemRes;
|
||||
{
|
||||
Configuration conf = new Configuration();
|
||||
conf.smallestScreenWidthDp = swDp;
|
||||
systemRes = context.createConfigurationContext(conf).getResources();
|
||||
}
|
||||
|
||||
boolean isTablet = swDp >= MIN_TABLET_WIDTH;
|
||||
boolean isTabletOrGesture = isTablet
|
||||
|| (Utilities.ATLEAST_R && isGestureNav(context));
|
||||
|
||||
int statusBarHeight = getDimenByName("status_bar_height", systemRes, 0);
|
||||
|
||||
int navBarHeightPortrait, navBarHeightLandscape, navbarWidthLandscape;
|
||||
|
||||
navBarHeightPortrait = isTablet
|
||||
? (mTaskbarDrawnInProcess
|
||||
? 0 : systemRes.getDimensionPixelSize(R.dimen.taskbar_size))
|
||||
: getDimenByName(NAVBAR_HEIGHT, systemRes, 0);
|
||||
|
||||
navBarHeightLandscape = isTablet
|
||||
? (mTaskbarDrawnInProcess
|
||||
? 0 : systemRes.getDimensionPixelSize(R.dimen.taskbar_size))
|
||||
: (isTabletOrGesture
|
||||
? getDimenByName(NAVBAR_HEIGHT_LANDSCAPE, systemRes, 0) : 0);
|
||||
navbarWidthLandscape = isTabletOrGesture
|
||||
? 0
|
||||
: getDimenByName(NAVBAR_LANDSCAPE_LEFT_RIGHT_SIZE, systemRes, 0);
|
||||
|
||||
WindowBounds[] result = new WindowBounds[4];
|
||||
Point tempSize = new Point();
|
||||
for (int i = 0; i < 4; i++) {
|
||||
int rotationChange = deltaRotation(rotation, i);
|
||||
tempSize.set(display.size.x, display.size.y);
|
||||
rotateSize(tempSize, rotationChange);
|
||||
Rect bounds = new Rect(0, 0, tempSize.x, tempSize.y);
|
||||
|
||||
int navBarHeight, navbarWidth;
|
||||
if (tempSize.y > tempSize.x) {
|
||||
navBarHeight = navBarHeightPortrait;
|
||||
navbarWidth = 0;
|
||||
} else {
|
||||
navBarHeight = navBarHeightLandscape;
|
||||
navbarWidth = navbarWidthLandscape;
|
||||
}
|
||||
|
||||
Rect insets = new Rect(safeCutout);
|
||||
rotateRect(insets, rotationChange);
|
||||
insets.top = Math.max(insets.top, statusBarHeight);
|
||||
insets.bottom = Math.max(insets.bottom, navBarHeight);
|
||||
|
||||
if (i == Surface.ROTATION_270 || i == Surface.ROTATION_180) {
|
||||
// On reverse landscape (and in rare-case when the natural orientation of the
|
||||
// device is landscape), navigation bar is on the right.
|
||||
insets.left = Math.max(insets.left, navbarWidth);
|
||||
} else {
|
||||
insets.right = Math.max(insets.right, navbarWidth);
|
||||
}
|
||||
result[i] = new WindowBounds(bounds, insets, i);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
protected boolean isGestureNav(Context context) {
|
||||
return ResourceUtils.getIntegerByName("config_navBarInteractionMode",
|
||||
context.getResources(), INVALID_RESOURCE_HANDLE) == 2;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a CachedDisplayInfo initialized for the current display
|
||||
*/
|
||||
@TargetApi(Build.VERSION_CODES.S)
|
||||
public CachedDisplayInfo getDisplayInfo(Display display) {
|
||||
int rotation = display.getRotation();
|
||||
|
||||
Point size = new Point();
|
||||
display.getRealSize(size);
|
||||
|
||||
Rect cutoutRect = new Rect();
|
||||
if (Utilities.ATLEAST_S) {
|
||||
DisplayCutout cutout = display.getCutout();
|
||||
if (cutout != null) {
|
||||
cutoutRect.set(cutout.getSafeInsetLeft(), cutout.getSafeInsetTop(),
|
||||
cutout.getSafeInsetRight(), cutout.getSafeInsetBottom());
|
||||
}
|
||||
}
|
||||
|
||||
return new CachedDisplayInfo(getDisplayId(display), size, rotation, cutoutRect);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a unique ID representing the display
|
||||
*/
|
||||
protected String getDisplayId(Display display) {
|
||||
return Integer.toString(display.getDisplayId());
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns rotation of the display associated with the context.
|
||||
*/
|
||||
public int getRotation(Context context) {
|
||||
Display d = null;
|
||||
if (Utilities.ATLEAST_R) {
|
||||
try {
|
||||
d = context.getDisplay();
|
||||
} catch (UnsupportedOperationException e) {
|
||||
// Ignore
|
||||
}
|
||||
}
|
||||
return d == null ? DisplayController.INSTANCE.get(context).getInfo().rotation
|
||||
: d.getRotation();
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user