Merge "Updating window manager estimation logic:" into tm-dev
This commit is contained in:
committed by
Android (Google) Code Review
commit
d586e1948b
@@ -23,8 +23,8 @@
|
||||
</string-array>
|
||||
|
||||
<string name="stats_log_manager_class" translatable="false">com.android.quickstep.logging.StatsLogCompatManager</string>
|
||||
|
||||
<string name="test_information_handler_class" translatable="false">com.android.quickstep.QuickstepTestInformationHandler</string>
|
||||
<string name="window_manager_proxy_class" translatable="false">com.android.quickstep.util.SystemWindowManagerProxy</string>
|
||||
|
||||
<!-- The number of thumbnails and icons to keep in the cache. The thumbnail cache size also
|
||||
determines how many thumbnails will be fetched in the background. -->
|
||||
|
||||
@@ -26,7 +26,6 @@ import static com.android.launcher3.model.data.ItemInfo.NO_MATCHING_ID;
|
||||
import static com.android.launcher3.popup.QuickstepSystemShortcut.getSplitSelectShortcutByPosition;
|
||||
import static com.android.launcher3.util.DisplayController.CHANGE_ACTIVE_SCREEN;
|
||||
import static com.android.launcher3.util.DisplayController.CHANGE_NAVIGATION_MODE;
|
||||
import static com.android.launcher3.util.DisplayController.NavigationMode.NO_BUTTON;
|
||||
import static com.android.launcher3.util.DisplayController.NavigationMode.TWO_BUTTONS;
|
||||
import static com.android.launcher3.util.Executors.THREAD_POOL_EXECUTOR;
|
||||
import static com.android.launcher3.util.Executors.UI_HELPER_EXECUTOR;
|
||||
@@ -38,7 +37,6 @@ import android.app.ActivityOptions;
|
||||
import android.content.Context;
|
||||
import android.content.Intent;
|
||||
import android.content.IntentSender;
|
||||
import android.graphics.Insets;
|
||||
import android.hardware.SensorManager;
|
||||
import android.hardware.devicestate.DeviceStateManager;
|
||||
import android.os.Bundle;
|
||||
@@ -46,7 +44,6 @@ import android.os.CancellationSignal;
|
||||
import android.os.IBinder;
|
||||
import android.view.Display;
|
||||
import android.view.View;
|
||||
import android.view.WindowInsets;
|
||||
import android.window.SplashScreen;
|
||||
|
||||
import androidx.annotation.Nullable;
|
||||
@@ -614,17 +611,4 @@ public abstract class BaseQuickstepLauncher extends Launcher {
|
||||
mDepthController.dump(prefix, writer);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void updateWindowInsets(WindowInsets.Builder updatedInsetsBuilder,
|
||||
WindowInsets oldInsets) {
|
||||
// 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 (DisplayController.getNavigationMode(this) == NO_BUTTON) {
|
||||
Insets oldTappableInsets = oldInsets.getInsets(WindowInsets.Type.tappableElement());
|
||||
Insets newTappableInsets = Insets.of(oldTappableInsets.left, oldTappableInsets.top,
|
||||
oldTappableInsets.right, 0);
|
||||
updatedInsetsBuilder.setInsets(WindowInsets.Type.tappableElement(), newTappableInsets);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -20,7 +20,6 @@ import android.app.Person;
|
||||
import android.content.Context;
|
||||
import android.content.pm.ShortcutInfo;
|
||||
import android.content.res.Resources;
|
||||
import android.view.Display;
|
||||
|
||||
import com.android.launcher3.R;
|
||||
import com.android.launcher3.Utilities;
|
||||
@@ -36,20 +35,6 @@ public class ApiWrapper {
|
||||
return persons == null ? Utilities.EMPTY_PERSON_ARRAY : persons;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns true if the display is an internal displays
|
||||
*/
|
||||
public static boolean isInternalDisplay(Display display) {
|
||||
return display.getType() == Display.TYPE_INTERNAL;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a unique ID representing the display
|
||||
*/
|
||||
public static String getUniqueId(Display display) {
|
||||
return display.getUniqueId();
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the minimum space that should be left empty at the end of hotseat
|
||||
*/
|
||||
|
||||
@@ -35,11 +35,11 @@ import com.android.launcher3.R;
|
||||
import com.android.launcher3.ResourceUtils;
|
||||
import com.android.launcher3.util.DisplayController.Info;
|
||||
import com.android.launcher3.util.DisplayController.NavigationMode;
|
||||
import com.android.launcher3.util.window.CachedDisplayInfo;
|
||||
|
||||
import java.io.PrintWriter;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
import java.util.Objects;
|
||||
|
||||
/**
|
||||
* Maintains state for supporting nav bars and tracking their gestures in multiple orientations.
|
||||
@@ -51,55 +51,17 @@ import java.util.Objects;
|
||||
*/
|
||||
class OrientationTouchTransformer {
|
||||
|
||||
private static class CurrentDisplay {
|
||||
public Point size;
|
||||
public int rotation;
|
||||
|
||||
CurrentDisplay() {
|
||||
this.size = new Point(0, 0);
|
||||
this.rotation = 0;
|
||||
}
|
||||
|
||||
CurrentDisplay(Point size, int rotation) {
|
||||
this.size = size;
|
||||
this.rotation = rotation;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "CurrentDisplay:"
|
||||
+ " rotation: " + rotation
|
||||
+ " size: " + size;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object o) {
|
||||
if (this == o) return true;
|
||||
if (o == null || getClass() != o.getClass()) return false;
|
||||
|
||||
CurrentDisplay display = (CurrentDisplay) o;
|
||||
if (rotation != display.rotation) return false;
|
||||
|
||||
return Objects.equals(size, display.size);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
return Objects.hash(size, rotation);
|
||||
}
|
||||
};
|
||||
|
||||
private static final String TAG = "OrientationTouchTransformer";
|
||||
private static final boolean DEBUG = false;
|
||||
|
||||
private static final int QUICKSTEP_ROTATION_UNINITIALIZED = -1;
|
||||
|
||||
private final Map<CurrentDisplay, OrientationRectF> mSwipeTouchRegions =
|
||||
new HashMap<CurrentDisplay, OrientationRectF>();
|
||||
private final Map<CachedDisplayInfo, OrientationRectF> mSwipeTouchRegions =
|
||||
new HashMap<CachedDisplayInfo, OrientationRectF>();
|
||||
private final RectF mAssistantLeftRegion = new RectF();
|
||||
private final RectF mAssistantRightRegion = new RectF();
|
||||
private final RectF mOneHandedModeRegion = new RectF();
|
||||
private CurrentDisplay mCurrentDisplay = new CurrentDisplay();
|
||||
private CachedDisplayInfo mCachedDisplayInfo = new CachedDisplayInfo();
|
||||
private int mNavBarGesturalHeight;
|
||||
private final int mNavBarLargerGesturalHeight;
|
||||
private boolean mEnableMultipleRegions;
|
||||
@@ -184,22 +146,22 @@ class OrientationTouchTransformer {
|
||||
* @see #enableMultipleRegions(boolean, Info)
|
||||
*/
|
||||
void createOrAddTouchRegion(Info info) {
|
||||
mCurrentDisplay = new CurrentDisplay(info.currentSize, info.rotation);
|
||||
mCachedDisplayInfo = new CachedDisplayInfo(info.currentSize, info.rotation);
|
||||
|
||||
if (mQuickStepStartingRotation > QUICKSTEP_ROTATION_UNINITIALIZED
|
||||
&& mCurrentDisplay.rotation == mQuickStepStartingRotation) {
|
||||
&& mCachedDisplayInfo.rotation == mQuickStepStartingRotation) {
|
||||
// User already was swiping and the current screen is same rotation as the starting one
|
||||
// Remove active nav bars in other rotations except for the one we started out in
|
||||
resetSwipeRegions(info);
|
||||
return;
|
||||
}
|
||||
OrientationRectF region = mSwipeTouchRegions.get(mCurrentDisplay);
|
||||
OrientationRectF region = mSwipeTouchRegions.get(mCachedDisplayInfo);
|
||||
if (region != null) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (mEnableMultipleRegions) {
|
||||
mSwipeTouchRegions.put(mCurrentDisplay, createRegionForDisplay(info));
|
||||
mSwipeTouchRegions.put(mCachedDisplayInfo, createRegionForDisplay(info));
|
||||
} else {
|
||||
resetSwipeRegions(info);
|
||||
}
|
||||
@@ -245,31 +207,31 @@ class OrientationTouchTransformer {
|
||||
*/
|
||||
private void resetSwipeRegions(Info region) {
|
||||
if (DEBUG) {
|
||||
Log.d(TAG, "clearing all regions except rotation: " + mCurrentDisplay.rotation);
|
||||
Log.d(TAG, "clearing all regions except rotation: " + mCachedDisplayInfo.rotation);
|
||||
}
|
||||
|
||||
mCurrentDisplay = new CurrentDisplay(region.currentSize, region.rotation);
|
||||
OrientationRectF regionToKeep = mSwipeTouchRegions.get(mCurrentDisplay);
|
||||
mCachedDisplayInfo = new CachedDisplayInfo(region.currentSize, region.rotation);
|
||||
OrientationRectF regionToKeep = mSwipeTouchRegions.get(mCachedDisplayInfo);
|
||||
if (regionToKeep == null) {
|
||||
regionToKeep = createRegionForDisplay(region);
|
||||
}
|
||||
mSwipeTouchRegions.clear();
|
||||
mSwipeTouchRegions.put(mCurrentDisplay, regionToKeep);
|
||||
mSwipeTouchRegions.put(mCachedDisplayInfo, regionToKeep);
|
||||
updateAssistantRegions(regionToKeep);
|
||||
}
|
||||
|
||||
private void resetSwipeRegions() {
|
||||
OrientationRectF regionToKeep = mSwipeTouchRegions.get(mCurrentDisplay);
|
||||
OrientationRectF regionToKeep = mSwipeTouchRegions.get(mCachedDisplayInfo);
|
||||
mSwipeTouchRegions.clear();
|
||||
if (regionToKeep != null) {
|
||||
mSwipeTouchRegions.put(mCurrentDisplay, regionToKeep);
|
||||
mSwipeTouchRegions.put(mCachedDisplayInfo, regionToKeep);
|
||||
updateAssistantRegions(regionToKeep);
|
||||
}
|
||||
}
|
||||
|
||||
private OrientationRectF createRegionForDisplay(Info display) {
|
||||
if (DEBUG) {
|
||||
Log.d(TAG, "creating rotation region for: " + mCurrentDisplay.rotation
|
||||
Log.d(TAG, "creating rotation region for: " + mCachedDisplayInfo.rotation
|
||||
+ " with mode: " + mMode + " displayRotation: " + display.rotation);
|
||||
}
|
||||
|
||||
@@ -368,7 +330,7 @@ class OrientationTouchTransformer {
|
||||
true);
|
||||
}
|
||||
} else {
|
||||
mLastRectTouched.applyTransformFromRotation(event, mCurrentDisplay.rotation,
|
||||
mLastRectTouched.applyTransformFromRotation(event, mCachedDisplayInfo.rotation,
|
||||
true);
|
||||
}
|
||||
break;
|
||||
@@ -387,7 +349,7 @@ class OrientationTouchTransformer {
|
||||
true);
|
||||
}
|
||||
} else {
|
||||
mLastRectTouched.applyTransformFromRotation(event, mCurrentDisplay.rotation,
|
||||
mLastRectTouched.applyTransformFromRotation(event, mCachedDisplayInfo.rotation,
|
||||
true);
|
||||
}
|
||||
mLastRectTouched = null;
|
||||
@@ -403,11 +365,12 @@ class OrientationTouchTransformer {
|
||||
if (rect == null) {
|
||||
continue;
|
||||
}
|
||||
if (rect.applyTransformFromRotation(event, mCurrentDisplay.rotation, false)) {
|
||||
if (rect.applyTransformFromRotation(
|
||||
event, mCachedDisplayInfo.rotation, false)) {
|
||||
mLastRectTouched = rect;
|
||||
mActiveTouchRotation = rect.getRotation();
|
||||
if (mEnableMultipleRegions
|
||||
&& mCurrentDisplay.rotation == mActiveTouchRotation) {
|
||||
&& mCachedDisplayInfo.rotation == mActiveTouchRotation) {
|
||||
// TODO(b/154580671) might make this block unnecessary
|
||||
// Start a touch session for the default nav region for the display
|
||||
mQuickStepStartingRotation = mLastRectTouched.getRotation();
|
||||
@@ -430,7 +393,7 @@ class OrientationTouchTransformer {
|
||||
pw.println(" lastTouchedRegion=" + mLastRectTouched);
|
||||
pw.println(" multipleRegionsEnabled=" + mEnableMultipleRegions);
|
||||
StringBuilder regions = new StringBuilder(" currentTouchableRotations=");
|
||||
for (CurrentDisplay key: mSwipeTouchRegions.keySet()) {
|
||||
for (CachedDisplayInfo key: mSwipeTouchRegions.keySet()) {
|
||||
OrientationRectF rectF = mSwipeTouchRegions.get(key);
|
||||
regions.append(rectF).append(" ");
|
||||
}
|
||||
|
||||
@@ -606,7 +606,7 @@ public class RecentsOrientedState implements
|
||||
width = Math.min(currentSize.x, currentSize.y);
|
||||
height = Math.max(currentSize.x, currentSize.y);
|
||||
}
|
||||
return idp.getBestMatch(width, height);
|
||||
return idp.getBestMatch(width, height, mRecentsActivityRotation);
|
||||
}
|
||||
|
||||
private static String nameAndAddress(Object obj) {
|
||||
|
||||
@@ -0,0 +1,46 @@
|
||||
/*
|
||||
* 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.quickstep.util;
|
||||
|
||||
import android.content.Context;
|
||||
import android.view.Display;
|
||||
|
||||
import com.android.launcher3.util.window.WindowManagerProxy;
|
||||
|
||||
/**
|
||||
* Extension of {@link WindowManagerProxy} with some assumption for the default system Launcher
|
||||
*/
|
||||
public class SystemWindowManagerProxy extends WindowManagerProxy {
|
||||
|
||||
public SystemWindowManagerProxy(Context context) {
|
||||
super(true);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected String getDisplayId(Display display) {
|
||||
return display.getUniqueId();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isInternalDisplay(Display display) {
|
||||
return display.getType() == Display.TYPE_INTERNAL;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getRotation(Context context) {
|
||||
return context.getResources().getConfiguration().windowConfiguration.getRotation();
|
||||
}
|
||||
}
|
||||
@@ -15,16 +15,15 @@
|
||||
*/
|
||||
package com.android.quickstep.util;
|
||||
|
||||
import static android.view.Display.DEFAULT_DISPLAY;
|
||||
|
||||
import static org.mockito.ArgumentMatchers.any;
|
||||
import static org.mockito.Mockito.doAnswer;
|
||||
import static org.mockito.Mockito.doReturn;
|
||||
import static org.mockito.Mockito.mock;
|
||||
|
||||
import android.graphics.Point;
|
||||
import android.graphics.Rect;
|
||||
import android.graphics.RectF;
|
||||
import android.util.ArrayMap;
|
||||
import android.util.Pair;
|
||||
import android.view.Display;
|
||||
import android.view.Surface;
|
||||
import android.view.SurfaceControl;
|
||||
@@ -38,6 +37,10 @@ import com.android.launcher3.util.DisplayController;
|
||||
import com.android.launcher3.util.DisplayController.Info;
|
||||
import com.android.launcher3.util.LauncherModelHelper;
|
||||
import com.android.launcher3.util.ReflectionHelpers;
|
||||
import com.android.launcher3.util.RotationUtils;
|
||||
import com.android.launcher3.util.WindowBounds;
|
||||
import com.android.launcher3.util.window.CachedDisplayInfo;
|
||||
import com.android.launcher3.util.window.WindowManagerProxy;
|
||||
import com.android.quickstep.FallbackActivityInterface;
|
||||
import com.android.quickstep.SystemUiProxy;
|
||||
import com.android.systemui.shared.system.RemoteAnimationTargetCompat;
|
||||
@@ -142,29 +145,34 @@ public class TaskViewSimulatorTest {
|
||||
LauncherModelHelper helper = new LauncherModelHelper();
|
||||
try {
|
||||
helper.sandboxContext.allow(SystemUiProxy.INSTANCE);
|
||||
int rotation = mDisplaySize.x > mDisplaySize.y
|
||||
? Surface.ROTATION_90 : Surface.ROTATION_0;
|
||||
CachedDisplayInfo cdi =
|
||||
new CachedDisplayInfo("test-display", mDisplaySize, rotation , new Rect());
|
||||
WindowBounds wm = new WindowBounds(
|
||||
new Rect(0, 0, mDisplaySize.x, mDisplaySize.y),
|
||||
mDisplayInsets);
|
||||
WindowBounds[] allBounds = new WindowBounds[4];
|
||||
for (int i = 0; i < 4; i++) {
|
||||
Rect boundsR = new Rect(wm.bounds);
|
||||
Rect insetsR = new Rect(wm.insets);
|
||||
|
||||
Display display = mock(Display.class);
|
||||
doReturn(DEFAULT_DISPLAY).when(display).getDisplayId();
|
||||
doReturn(mDisplaySize.x > mDisplaySize.y ? Surface.ROTATION_90 : Surface.ROTATION_0)
|
||||
.when(display).getRotation();
|
||||
doAnswer(i -> {
|
||||
((Point) i.getArgument(0)).set(mDisplaySize.x, mDisplaySize.y);
|
||||
return null;
|
||||
}).when(display).getRealSize(any());
|
||||
doAnswer(i -> {
|
||||
Point smallestSize = i.getArgument(0);
|
||||
Point largestSize = i.getArgument(1);
|
||||
smallestSize.x = smallestSize.y = Math.min(mDisplaySize.x, mDisplaySize.y);
|
||||
largestSize.x = largestSize.y = Math.max(mDisplaySize.x, mDisplaySize.y);
|
||||
RotationUtils.rotateRect(insetsR, RotationUtils.deltaRotation(rotation, i));
|
||||
RotationUtils.rotateRect(boundsR, RotationUtils.deltaRotation(rotation, i));
|
||||
boundsR.set(0, 0, Math.abs(boundsR.width()), Math.abs(boundsR.height()));
|
||||
allBounds[i] = new WindowBounds(boundsR, insetsR);
|
||||
}
|
||||
|
||||
smallestSize.x -= mDisplayInsets.left + mDisplayInsets.right;
|
||||
largestSize.x -= mDisplayInsets.left + mDisplayInsets.right;
|
||||
WindowManagerProxy wmProxy = mock(WindowManagerProxy.class);
|
||||
doReturn(cdi).when(wmProxy).getDisplayInfo(any());
|
||||
doReturn(wm).when(wmProxy).getRealBounds(any(), any(), any());
|
||||
|
||||
smallestSize.y -= mDisplayInsets.top + mDisplayInsets.bottom;
|
||||
largestSize.y -= mDisplayInsets.top + mDisplayInsets.bottom;
|
||||
return null;
|
||||
}).when(display).getCurrentSizeRange(any(), any());
|
||||
DisplayController.Info mockInfo = new Info(helper.sandboxContext, display);
|
||||
ArrayMap<String, Pair<CachedDisplayInfo, WindowBounds[]>> perDisplayBoundsCache =
|
||||
new ArrayMap<>();
|
||||
perDisplayBoundsCache.put(cdi.id, Pair.create(cdi.normalize(), allBounds));
|
||||
|
||||
DisplayController.Info mockInfo = new Info(
|
||||
helper.sandboxContext, mock(Display.class), wmProxy, perDisplayBoundsCache);
|
||||
|
||||
DisplayController controller =
|
||||
DisplayController.INSTANCE.get(helper.sandboxContext);
|
||||
@@ -172,7 +180,7 @@ public class TaskViewSimulatorTest {
|
||||
ReflectionHelpers.setField(controller, "mInfo", mockInfo);
|
||||
|
||||
mDeviceProfile = InvariantDeviceProfile.INSTANCE.get(helper.sandboxContext)
|
||||
.getBestMatch(mAppBounds.width(), mAppBounds.height());
|
||||
.getBestMatch(mAppBounds.width(), mAppBounds.height(), rotation);
|
||||
mDeviceProfile.updateInsets(mLauncherInsets);
|
||||
|
||||
TaskViewSimulator tvs = new TaskViewSimulator(helper.sandboxContext,
|
||||
|
||||
@@ -69,6 +69,7 @@
|
||||
<string name="test_information_handler_class" translatable="false"></string>
|
||||
<string name="launcher_activity_logic_class" translatable="false"></string>
|
||||
<string name="model_delegate_class" translatable="false"></string>
|
||||
<string name="window_manager_proxy_class" translatable="false"></string>
|
||||
|
||||
<!-- View ID to use for QSB widget -->
|
||||
<item type="id" name="qsb_widget" />
|
||||
|
||||
@@ -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();
|
||||
}
|
||||
}
|
||||
@@ -19,7 +19,6 @@ package com.android.launcher3.uioverrides;
|
||||
import android.app.Person;
|
||||
import android.content.Context;
|
||||
import android.content.pm.ShortcutInfo;
|
||||
import android.view.Display;
|
||||
|
||||
import com.android.launcher3.Utilities;
|
||||
|
||||
@@ -31,20 +30,6 @@ public class ApiWrapper {
|
||||
return Utilities.EMPTY_PERSON_ARRAY;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns true if the display is an internal displays
|
||||
*/
|
||||
public static boolean isInternalDisplay(Display display) {
|
||||
return display.getDisplayId() == Display.DEFAULT_DISPLAY;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a unique ID representing the display
|
||||
*/
|
||||
public static String getUniqueId(Display display) {
|
||||
return Integer.toString(display.getDisplayId());
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the minimum space that should be left empty at the end of hotseat
|
||||
*/
|
||||
|
||||
@@ -139,7 +139,7 @@ class DeviceProfileTest {
|
||||
else
|
||||
Pair(1440, 3120)
|
||||
|
||||
windowBounds = WindowBounds(x, y, x, y - 100)
|
||||
windowBounds = WindowBounds(x, y, x, y - 100, 0)
|
||||
|
||||
`when`(info.isTablet(any())).thenReturn(false)
|
||||
`when`(info.isLargeTablet(any())).thenReturn(false)
|
||||
@@ -153,7 +153,7 @@ class DeviceProfileTest {
|
||||
else
|
||||
Pair(1600, 2560)
|
||||
|
||||
windowBounds = WindowBounds(x, y, x, y - 100)
|
||||
windowBounds = WindowBounds(x, y, x, y - 100, 0)
|
||||
|
||||
`when`(info.isTablet(any())).thenReturn(true)
|
||||
`when`(info.isLargeTablet(any())).thenReturn(false)
|
||||
@@ -167,7 +167,7 @@ class DeviceProfileTest {
|
||||
else
|
||||
Pair(1600, 2560)
|
||||
|
||||
windowBounds = WindowBounds(x, y, x, y - 100)
|
||||
windowBounds = WindowBounds(x, y, x, y - 100, 0)
|
||||
|
||||
`when`(info.isTablet(any())).thenReturn(true)
|
||||
`when`(info.isLargeTablet(any())).thenReturn(true)
|
||||
|
||||
@@ -67,6 +67,7 @@ import com.android.launcher3.pm.UserCache;
|
||||
import com.android.launcher3.testing.TestInformationProvider;
|
||||
import com.android.launcher3.uioverrides.plugins.PluginManagerWrapper;
|
||||
import com.android.launcher3.util.MainThreadInitializedObject.SandboxContext;
|
||||
import com.android.launcher3.util.window.WindowManagerProxy;
|
||||
import com.android.launcher3.widget.custom.CustomWidgetManager;
|
||||
|
||||
import org.mockito.ArgumentCaptor;
|
||||
@@ -501,7 +502,7 @@ public class LauncherModelHelper {
|
||||
LauncherAppState.INSTANCE, InvariantDeviceProfile.INSTANCE,
|
||||
DisplayController.INSTANCE, CustomWidgetManager.INSTANCE,
|
||||
SettingsCache.INSTANCE, PluginManagerWrapper.INSTANCE,
|
||||
ItemInstallQueue.INSTANCE);
|
||||
ItemInstallQueue.INSTANCE, WindowManagerProxy.INSTANCE);
|
||||
mPm = spy(getBaseContext().getPackageManager());
|
||||
mDbDir = new File(getCacheDir(), UUID.randomUUID().toString());
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user