diff --git a/quickstep/robolectric_tests/src/com/android/quickstep/OrientationTouchTransformerTest.java b/quickstep/robolectric_tests/src/com/android/quickstep/OrientationTouchTransformerTest.java index e771962ba6..2b1b57c450 100644 --- a/quickstep/robolectric_tests/src/com/android/quickstep/OrientationTouchTransformerTest.java +++ b/quickstep/robolectric_tests/src/com/android/quickstep/OrientationTouchTransformerTest.java @@ -17,17 +17,26 @@ package com.android.quickstep; +import static android.view.Display.DEFAULT_DISPLAY; + import static com.android.quickstep.SysUINavigationMode.Mode.NO_BUTTON; import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertTrue; +import static org.mockito.ArgumentMatchers.any; import static org.mockito.ArgumentMatchers.anyInt; +import static org.mockito.Mockito.doAnswer; +import static org.mockito.Mockito.doReturn; import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.spy; import static org.mockito.Mockito.when; +import android.content.Context; import android.content.res.Resources; import android.graphics.Point; +import android.hardware.display.DisplayManager; import android.util.DisplayMetrics; +import android.view.Display; import android.view.MotionEvent; import android.view.Surface; @@ -35,11 +44,11 @@ import com.android.launcher3.ResourceUtils; import com.android.launcher3.util.DisplayController; import org.junit.Before; -import org.junit.Ignore; import org.junit.Test; import org.junit.runner.RunWith; import org.mockito.MockitoAnnotations; import org.robolectric.RobolectricTestRunner; +import org.robolectric.RuntimeEnvironment; @RunWith(RobolectricTestRunner.class) public class OrientationTouchTransformerTest { @@ -284,11 +293,26 @@ public class OrientationTouchTransformerTest { } private DisplayController.Info createDisplayInfo(ScreenSize screenSize, int rotation) { + Context context = RuntimeEnvironment.application; + Display display = spy(context.getSystemService(DisplayManager.class) + .getDisplay(DEFAULT_DISPLAY)); + Point p = new Point(screenSize.mWidth, screenSize.mHeight); if (rotation == Surface.ROTATION_90 || rotation == Surface.ROTATION_270) { - p = new Point(screenSize.mHeight, screenSize.mWidth); + p.set(screenSize.mHeight, screenSize.mWidth); } - return new DisplayController.Info(0, rotation, 0, p, p, p, null); + + doReturn(rotation).when(display).getRotation(); + doAnswer(i -> { + ((Point) i.getArgument(0)).set(p.x, p.y); + return null; + }).when(display).getRealSize(any(Point.class)); + doAnswer(i -> { + ((Point) i.getArgument(0)).set(p.x, p.y); + ((Point) i.getArgument(1)).set(p.x, p.y); + return null; + }).when(display).getCurrentSizeRange(any(Point.class), any(Point.class)); + return new DisplayController.Info(context, display); } private float generateTouchRegionHeight(ScreenSize screenSize, int rotation) { diff --git a/quickstep/robolectric_tests/src/com/android/quickstep/util/TaskViewSimulatorTest.java b/quickstep/robolectric_tests/src/com/android/quickstep/util/TaskViewSimulatorTest.java index 88079aee73..fd93d98bed 100644 --- a/quickstep/robolectric_tests/src/com/android/quickstep/util/TaskViewSimulatorTest.java +++ b/quickstep/robolectric_tests/src/com/android/quickstep/util/TaskViewSimulatorTest.java @@ -147,7 +147,7 @@ public class TaskViewSimulatorTest { LauncherActivityInterface.INSTANCE); tvs.setDp(mDeviceProfile); - int launcherRotation = DisplayController.getDefaultDisplay(mContext).getInfo().rotation; + int launcherRotation = DisplayController.INSTANCE.get(mContext).getInfo().rotation; if (mAppRotation < 0) { mAppRotation = launcherRotation; } diff --git a/quickstep/src/com/android/launcher3/BaseQuickstepLauncher.java b/quickstep/src/com/android/launcher3/BaseQuickstepLauncher.java index 0524b213b2..fb9765c633 100644 --- a/quickstep/src/com/android/launcher3/BaseQuickstepLauncher.java +++ b/quickstep/src/com/android/launcher3/BaseQuickstepLauncher.java @@ -19,7 +19,7 @@ import static com.android.launcher3.AbstractFloatingView.TYPE_ALL; import static com.android.launcher3.AbstractFloatingView.TYPE_HIDE_BACK_BUTTON; import static com.android.launcher3.LauncherState.FLAG_HIDE_BACK_BUTTON; import static com.android.launcher3.LauncherState.NORMAL; -import static com.android.launcher3.util.DisplayController.DisplayHolder.CHANGE_SIZE; +import static com.android.launcher3.util.DisplayController.CHANGE_SIZE; import static com.android.launcher3.util.Executors.UI_HELPER_EXECUTOR; import static com.android.quickstep.SysUINavigationMode.Mode.TWO_BUTTONS; import static com.android.systemui.shared.system.ActivityManagerWrapper.CLOSE_SYSTEM_WINDOWS_REASON_HOME_KEY; diff --git a/quickstep/src/com/android/launcher3/uioverrides/touchcontrollers/NoButtonNavbarToOverviewTouchController.java b/quickstep/src/com/android/launcher3/uioverrides/touchcontrollers/NoButtonNavbarToOverviewTouchController.java index 74a253eb3a..694998c476 100644 --- a/quickstep/src/com/android/launcher3/uioverrides/touchcontrollers/NoButtonNavbarToOverviewTouchController.java +++ b/quickstep/src/com/android/launcher3/uioverrides/touchcontrollers/NoButtonNavbarToOverviewTouchController.java @@ -267,7 +267,7 @@ public class NoButtonNavbarToOverviewTouchController extends PortraitStatesTouch } private float dpiFromPx(float pixels) { - return Utilities.dpiFromPx(pixels, mLauncher.getResources().getDisplayMetrics()); + return Utilities.dpiFromPx(pixels, mLauncher.getResources().getDisplayMetrics().densityDpi); } @Override diff --git a/quickstep/src/com/android/quickstep/RecentsAnimationDeviceState.java b/quickstep/src/com/android/quickstep/RecentsAnimationDeviceState.java index 23e35f6307..6cf12a3865 100644 --- a/quickstep/src/com/android/quickstep/RecentsAnimationDeviceState.java +++ b/quickstep/src/com/android/quickstep/RecentsAnimationDeviceState.java @@ -17,8 +17,8 @@ package com.android.quickstep; import static android.content.Intent.ACTION_USER_UNLOCKED; -import static com.android.launcher3.util.DisplayController.DisplayHolder.CHANGE_ALL; -import static com.android.launcher3.util.DisplayController.DisplayHolder.CHANGE_FRAME_DELAY; +import static com.android.launcher3.util.DisplayController.CHANGE_ALL; +import static com.android.launcher3.util.DisplayController.CHANGE_FRAME_DELAY; import static com.android.launcher3.util.SettingsCache.ONE_HANDED_ENABLED; import static com.android.launcher3.util.SettingsCache.ONE_HANDED_SWIPE_BOTTOM_TO_NOTIFICATION_ENABLED; import static com.android.quickstep.SysUINavigationMode.Mode.NO_BUTTON; @@ -62,7 +62,6 @@ import androidx.annotation.BinderThread; import com.android.launcher3.R; import com.android.launcher3.Utilities; import com.android.launcher3.util.DisplayController; -import com.android.launcher3.util.DisplayController.DisplayHolder; import com.android.launcher3.util.DisplayController.DisplayInfoChangeListener; import com.android.launcher3.util.DisplayController.Info; import com.android.launcher3.util.SettingsCache; @@ -91,7 +90,7 @@ public class RecentsAnimationDeviceState implements private final Context mContext; private final SysUINavigationMode mSysUiNavMode; - private final DisplayHolder mDisplayHolder; + private final DisplayController mDisplayController; private final int mDisplayId; private final RotationTouchHelper mRotationTouchHelper; @@ -128,17 +127,13 @@ public class RecentsAnimationDeviceState implements private boolean mIsUserSetupComplete; public RecentsAnimationDeviceState(Context context) { - this(context, DisplayController.getDefaultDisplay(context)); - } - - public RecentsAnimationDeviceState(Context context, DisplayHolder displayHolder) { mContext = context; - mDisplayHolder = displayHolder; + mDisplayController = DisplayController.INSTANCE.get(context); mSysUiNavMode = SysUINavigationMode.INSTANCE.get(context); - mDisplayId = mDisplayHolder.getInfo().id; + mDisplayId = mDisplayController.getInfo().id; mIsOneHandedModeSupported = SystemProperties.getBoolean(SUPPORT_ONE_HANDED_MODE, false); - runOnDestroy(() -> mDisplayHolder.removeChangeListener(this)); - mRotationTouchHelper = new RotationTouchHelper(context, mDisplayHolder); + runOnDestroy(() -> mDisplayController.removeChangeListener(this)); + mRotationTouchHelper = new RotationTouchHelper(context, mDisplayController); runOnDestroy(mRotationTouchHelper::destroy); // Register for user unlocked if necessary @@ -244,9 +239,9 @@ public class RecentsAnimationDeviceState implements @Override public void onNavigationModeChanged(SysUINavigationMode.Mode newMode) { - mDisplayHolder.removeChangeListener(this); - mDisplayHolder.addChangeListener(this); - onDisplayInfoChanged(mDisplayHolder.getInfo(), CHANGE_ALL); + mDisplayController.removeChangeListener(this); + mDisplayController.addChangeListener(this); + onDisplayInfoChanged(mDisplayController.getInfo(), CHANGE_ALL); if (newMode == NO_BUTTON) { mExclusionListener.register(); @@ -254,7 +249,7 @@ public class RecentsAnimationDeviceState implements mExclusionListener.unregister(); } - mNavBarPosition = new NavBarPosition(newMode, mDisplayHolder.getInfo()); + mNavBarPosition = new NavBarPosition(newMode, mDisplayController.getInfo()); mMode = newMode; } @@ -556,11 +551,11 @@ public class RecentsAnimationDeviceState implements } if (mIsOneHandedModeEnabled || mIsSwipeToNotificationEnabled) { - final Info displayInfo = mDisplayHolder.getInfo(); + final Info displayInfo = mDisplayController.getInfo(); return (mRotationTouchHelper.touchInOneHandedModeRegion(ev) && displayInfo.rotation != Surface.ROTATION_90 && displayInfo.rotation != Surface.ROTATION_270 - && displayInfo.metrics.densityDpi < DisplayMetrics.DENSITY_600); + && displayInfo.densityDpi < DisplayMetrics.DENSITY_600); } return false; } diff --git a/quickstep/src/com/android/quickstep/RotationTouchHelper.java b/quickstep/src/com/android/quickstep/RotationTouchHelper.java index 2cf32122cf..d4ad1760ef 100644 --- a/quickstep/src/com/android/quickstep/RotationTouchHelper.java +++ b/quickstep/src/com/android/quickstep/RotationTouchHelper.java @@ -17,8 +17,8 @@ package com.android.quickstep; import static android.view.Surface.ROTATION_0; -import static com.android.launcher3.util.DisplayController.DisplayHolder.CHANGE_ALL; -import static com.android.launcher3.util.DisplayController.DisplayHolder.CHANGE_FRAME_DELAY; +import static com.android.launcher3.util.DisplayController.CHANGE_ALL; +import static com.android.launcher3.util.DisplayController.CHANGE_FRAME_DELAY; import static com.android.launcher3.util.Executors.UI_HELPER_EXECUTOR; import static com.android.quickstep.SysUINavigationMode.Mode.THREE_BUTTONS; @@ -28,7 +28,7 @@ import android.view.MotionEvent; import android.view.OrientationEventListener; import com.android.launcher3.testing.TestProtocol; -import com.android.launcher3.util.DisplayController.DisplayHolder; +import com.android.launcher3.util.DisplayController; import com.android.launcher3.util.DisplayController.DisplayInfoChangeListener; import com.android.launcher3.util.DisplayController.Info; import com.android.quickstep.util.RecentsOrientedState; @@ -44,7 +44,7 @@ public class RotationTouchHelper implements DisplayInfoChangeListener { private final OrientationTouchTransformer mOrientationTouchTransformer; - private final DisplayHolder mDisplayHolder; + private final DisplayController mDisplayController; private final SysUINavigationMode mSysUiNavMode; private final int mDisplayId; private int mDisplayRotation; @@ -121,12 +121,12 @@ public class RotationTouchHelper implements private final Context mContext; - public RotationTouchHelper(Context context, DisplayHolder displayHolder) { + public RotationTouchHelper(Context context, DisplayController displayController) { mContext = context; - mDisplayHolder = displayHolder; + mDisplayController = displayController; Resources resources = mContext.getResources(); mSysUiNavMode = SysUINavigationMode.INSTANCE.get(context); - mDisplayId = mDisplayHolder.getInfo().id; + mDisplayId = mDisplayController.getInfo().id; mOrientationTouchTransformer = new OrientationTouchTransformer(resources, mMode, () -> QuickStepContract.getWindowCornerRadius(resources)); @@ -201,7 +201,7 @@ public class RotationTouchHelper implements return; } - mOrientationTouchTransformer.createOrAddTouchRegion(mDisplayHolder.getInfo()); + mOrientationTouchTransformer.createOrAddTouchRegion(mDisplayController.getInfo()); } /** @@ -223,11 +223,11 @@ public class RotationTouchHelper implements @Override public void onNavigationModeChanged(SysUINavigationMode.Mode newMode) { - mDisplayHolder.removeChangeListener(this); - mDisplayHolder.addChangeListener(this); - onDisplayInfoChanged(mDisplayHolder.getInfo(), CHANGE_ALL); + mDisplayController.removeChangeListener(this); + mDisplayController.addChangeListener(this); + onDisplayInfoChanged(mDisplayController.getInfo(), CHANGE_ALL); - mOrientationTouchTransformer.setNavigationMode(newMode, mDisplayHolder.getInfo(), + mOrientationTouchTransformer.setNavigationMode(newMode, mDisplayController.getInfo(), mContext.getResources()); if (!mMode.hasGestures && newMode.hasGestures) { setupOrientationSwipeHandler(); @@ -276,8 +276,8 @@ public class RotationTouchHelper implements * Sets the gestural height. */ void setGesturalHeight(int newGesturalHeight) { - mOrientationTouchTransformer.setGesturalHeight(newGesturalHeight, mDisplayHolder.getInfo(), - mContext.getResources()); + mOrientationTouchTransformer.setGesturalHeight( + newGesturalHeight, mDisplayController.getInfo(), mContext.getResources()); } /** @@ -293,7 +293,7 @@ public class RotationTouchHelper implements } private void enableMultipleRegions(boolean enable) { - mOrientationTouchTransformer.enableMultipleRegions(enable, mDisplayHolder.getInfo()); + mOrientationTouchTransformer.enableMultipleRegions(enable, mDisplayController.getInfo()); notifySysuiOfCurrentRotation(mOrientationTouchTransformer.getQuickStepStartingRotation()); if (enable && !mInOverview && !TestProtocol.sDisableSensorRotation) { // Clear any previous state from sensor manager @@ -356,7 +356,7 @@ public class RotationTouchHelper implements * notifies system UI of the primary rotation the user is interacting with */ private void toggleSecondaryNavBarsForRotation() { - mOrientationTouchTransformer.setSingleActiveRegion(mDisplayHolder.getInfo()); + mOrientationTouchTransformer.setSingleActiveRegion(mDisplayController.getInfo()); notifySysuiOfCurrentRotation(mOrientationTouchTransformer.getCurrentActiveRotation()); } diff --git a/quickstep/src/com/android/quickstep/TaskViewUtils.java b/quickstep/src/com/android/quickstep/TaskViewUtils.java index 1db587285e..a59ba5136d 100644 --- a/quickstep/src/com/android/quickstep/TaskViewUtils.java +++ b/quickstep/src/com/android/quickstep/TaskViewUtils.java @@ -209,7 +209,7 @@ public final class TaskViewUtils { // RecentsView never updates the display rotation until swipe-up so the value may // be stale. Use the display value instead. - int displayRotation = DisplayController.getDefaultDisplay(context).getInfo().rotation; + int displayRotation = DisplayController.INSTANCE.get(context).getInfo().rotation; tsv.getOrientationState().update(displayRotation, displayRotation); tsv.setPreview(targets.apps[targets.apps.length - 1]); @@ -437,7 +437,7 @@ public final class TaskViewUtils { // RecentsView never updates the display rotation until swipe-up so the value may // be stale. Use the display value instead. - int displayRotation = DisplayController.getDefaultDisplay(recentsView.getContext()) + int displayRotation = DisplayController.INSTANCE.get(recentsView.getContext()) .getInfo().rotation; tvs.getOrientationState().update(displayRotation, displayRotation); diff --git a/quickstep/src/com/android/quickstep/fallback/FallbackNavBarTouchController.java b/quickstep/src/com/android/quickstep/fallback/FallbackNavBarTouchController.java index a4a7bd3ae9..9d9ef94169 100644 --- a/quickstep/src/com/android/quickstep/fallback/FallbackNavBarTouchController.java +++ b/quickstep/src/com/android/quickstep/fallback/FallbackNavBarTouchController.java @@ -43,7 +43,7 @@ public class FallbackNavBarTouchController implements TouchController, SysUINavigationMode.Mode sysUINavigationMode = SysUINavigationMode.getMode(mActivity); if (sysUINavigationMode == SysUINavigationMode.Mode.NO_BUTTON) { NavBarPosition navBarPosition = new NavBarPosition(sysUINavigationMode, - DisplayController.getDefaultDisplay(mActivity).getInfo()); + DisplayController.INSTANCE.get(mActivity).getInfo()); mTriggerSwipeUpTracker = new TriggerSwipeUpTouchTracker(mActivity, true /* disableHorizontalSwipe */, navBarPosition, null /* onInterceptTouch */, this); diff --git a/quickstep/src/com/android/quickstep/inputconsumers/DeviceLockedInputConsumer.java b/quickstep/src/com/android/quickstep/inputconsumers/DeviceLockedInputConsumer.java index 85ecab15a1..9c647946ea 100644 --- a/quickstep/src/com/android/quickstep/inputconsumers/DeviceLockedInputConsumer.java +++ b/quickstep/src/com/android/quickstep/inputconsumers/DeviceLockedInputConsumer.java @@ -116,7 +116,7 @@ public class DeviceLockedInputConsumer implements InputConsumer, R.dimen.device_locked_y_offset); // Do not use DeviceProfile as the user data might be locked - mDisplaySize = DisplayController.getDefaultDisplay(context).getInfo().realSize; + mDisplaySize = DisplayController.INSTANCE.get(context).getInfo().realSize; // Init states mStateCallback = new MultiStateCallback(STATE_NAMES); diff --git a/quickstep/src/com/android/quickstep/inputconsumers/OneHandedModeInputConsumer.java b/quickstep/src/com/android/quickstep/inputconsumers/OneHandedModeInputConsumer.java index cd69cf1366..d82d43d7f8 100644 --- a/quickstep/src/com/android/quickstep/inputconsumers/OneHandedModeInputConsumer.java +++ b/quickstep/src/com/android/quickstep/inputconsumers/OneHandedModeInputConsumer.java @@ -44,12 +44,10 @@ import com.android.systemui.shared.system.InputMonitorCompat; */ public class OneHandedModeInputConsumer extends DelegateInputConsumer { - private static final String TAG = "OneHandedModeInputConsumer"; private static final int ANGLE_MAX = 150; private static final int ANGLE_MIN = 30; private final Context mContext; - private final DisplayController.DisplayHolder mDisplayHolder; private final Point mDisplaySize; private final RecentsAnimationDeviceState mDeviceState; @@ -68,12 +66,11 @@ public class OneHandedModeInputConsumer extends DelegateInputConsumer { InputConsumer delegate, InputMonitorCompat inputMonitor) { super(delegate, inputMonitor); mContext = context; - mDisplayHolder = DisplayController.getDefaultDisplay(mContext); mDeviceState = deviceState; mDragDistThreshold = context.getResources().getDimensionPixelSize( R.dimen.gestures_onehanded_drag_threshold); mSquaredSlop = Utilities.squaredTouchSlop(context); - mDisplaySize = mDisplayHolder.getInfo().realSize; + mDisplaySize = DisplayController.INSTANCE.get(mContext).getInfo().realSize; mNavBarSize = ResourceUtils.getNavbarSize(NAVBAR_BOTTOM_GESTURE_SIZE, mContext.getResources()); } diff --git a/quickstep/src/com/android/quickstep/util/SplitScreenBounds.java b/quickstep/src/com/android/quickstep/util/SplitScreenBounds.java index 176478f1dc..88cc650276 100644 --- a/quickstep/src/com/android/quickstep/util/SplitScreenBounds.java +++ b/quickstep/src/com/android/quickstep/util/SplitScreenBounds.java @@ -77,7 +77,7 @@ public class SplitScreenBounds { WindowBounds bounds = new WindowBounds(wm.getBounds(), new Rect(insets.left, insets.top, insets.right, insets.bottom)); - int rotation = DisplayController.getDefaultDisplay(context).getInfo().rotation; + int rotation = DisplayController.INSTANCE.get(context).getInfo().rotation; int halfDividerSize = context.getResources() .getDimensionPixelSize(R.dimen.multi_window_task_divider_size) / 2; diff --git a/src/com/android/launcher3/BaseDraggingActivity.java b/src/com/android/launcher3/BaseDraggingActivity.java index 5ba03edcb4..e9412d9aa8 100644 --- a/src/com/android/launcher3/BaseDraggingActivity.java +++ b/src/com/android/launcher3/BaseDraggingActivity.java @@ -17,7 +17,7 @@ package com.android.launcher3; import static com.android.launcher3.logging.StatsLogManager.LauncherEvent.LAUNCHER_APP_LAUNCH_TAP; -import static com.android.launcher3.util.DisplayController.DisplayHolder.CHANGE_ROTATION; +import static com.android.launcher3.util.DisplayController.CHANGE_ROTATION; import android.app.ActivityOptions; import android.content.ActivityNotFoundException; @@ -28,7 +28,6 @@ import android.graphics.Insets; import android.graphics.Point; import android.graphics.Rect; import android.graphics.drawable.Drawable; -import android.os.Binder; import android.os.Bundle; import android.os.Process; import android.os.StrictMode; @@ -92,7 +91,7 @@ public abstract class BaseDraggingActivity extends BaseActivity mIsSafeModeEnabled = TraceHelper.allowIpcs("isSafeMode", () -> getPackageManager().isSafeMode()); - DisplayController.getDefaultDisplay(this).addChangeListener(this); + DisplayController.INSTANCE.get(this).addChangeListener(this); // Update theme WallpaperColorInfo.INSTANCE.get(this).addOnChangeListener(this); @@ -279,7 +278,7 @@ public abstract class BaseDraggingActivity extends BaseActivity protected void onDestroy() { super.onDestroy(); WallpaperColorInfo.INSTANCE.get(this).removeOnChangeListener(this); - DisplayController.getDefaultDisplay(this).removeChangeListener(this); + DisplayController.INSTANCE.get(this).removeChangeListener(this); } public void runOnceOnStart(Runnable action) { diff --git a/src/com/android/launcher3/DeviceProfile.java b/src/com/android/launcher3/DeviceProfile.java index dae4f3b8dd..a6fc0f34c0 100644 --- a/src/com/android/launcher3/DeviceProfile.java +++ b/src/com/android/launcher3/DeviceProfile.java @@ -16,15 +16,20 @@ package com.android.launcher3; +import static android.view.WindowManager.LayoutParams.TYPE_APPLICATION; + import static com.android.launcher3.ResourceUtils.pxFromDp; import static com.android.launcher3.Utilities.dpiFromPx; +import android.annotation.SuppressLint; import android.content.Context; import android.content.res.Configuration; import android.content.res.Resources; import android.graphics.Point; import android.graphics.PointF; import android.graphics.Rect; +import android.hardware.display.DisplayManager; +import android.util.DisplayMetrics; import android.util.Log; import android.view.Surface; import android.view.WindowInsets; @@ -43,6 +48,7 @@ import com.android.launcher3.util.WindowBounds; import java.io.PrintWriter; +@SuppressLint("NewApi") public class DeviceProfile { private static final float TABLET_MIN_DPS = 600; @@ -51,6 +57,7 @@ public class DeviceProfile { public final InvariantDeviceProfile inv; private final Info mInfo; + private final DisplayMetrics mMetrics; // Device properties public final boolean isTablet; @@ -214,7 +221,8 @@ public class DeviceProfile { mInfo = info; // Constants from resources - float swDPs = dpiFromPx(Math.min(info.smallestSize.x, info.smallestSize.y), info.metrics); + float swDPs = dpiFromPx(Math.min(info.smallestSize.x, info.smallestSize.y), + info.densityDpi); boolean allowRotation = context.getResources().getBoolean(R.bool.allow_rotation); // Tablet UI is built with assumption that simulated landscape is disabled. isTablet = allowRotation && swDPs >= TABLET_MIN_DPS; @@ -227,6 +235,7 @@ public class DeviceProfile { context = getContext(context, info, isVerticalBarLayout() ? Configuration.ORIENTATION_LANDSCAPE : Configuration.ORIENTATION_PORTRAIT); + mMetrics = context.getResources().getDisplayMetrics(); final Resources res = context.getResources(); isTaskbarPresent = isTablet && FeatureFlags.ENABLE_TASKBAR.get(); @@ -234,11 +243,13 @@ public class DeviceProfile { // Taskbar will be added later, but provides bottom insets that we should subtract // from availableHeightPx. taskbarSize = res.getDimensionPixelSize(R.dimen.taskbar_size); - WindowInsets windowInsets = DisplayController.INSTANCE.get(context).getHolder(mInfo.id) - .getDisplayContext().getSystemService(WindowManager.class) + WindowInsets windowInsets = + context.createWindowContext( + context.getSystemService(DisplayManager.class).getDisplay(mInfo.id), + TYPE_APPLICATION, null) + .getSystemService(WindowManager.class) .getCurrentWindowMetrics().getWindowInsets(); - nonOverlappingTaskbarInset = - taskbarSize - windowInsets.getSystemWindowInsetBottom(); + nonOverlappingTaskbarInset = taskbarSize - windowInsets.getSystemWindowInsetBottom(); if (nonOverlappingTaskbarInset > 0) { nonFinalAvailableHeightPx -= nonOverlappingTaskbarInset; } @@ -261,7 +272,7 @@ public class DeviceProfile { res.getDimensionPixelSize(R.dimen.folder_content_padding_left_right); folderContentPaddingTop = res.getDimensionPixelSize(R.dimen.folder_content_padding_top); - setCellLayoutBorderSpacing(pxFromDp(inv.borderSpacing, mInfo.metrics, 1f)); + setCellLayoutBorderSpacing(pxFromDp(inv.borderSpacing, mMetrics, 1f)); cellLayoutBorderSpacingOriginalPx = cellLayoutBorderSpacingPx; folderCellLayoutBorderSpacingPx = cellLayoutBorderSpacingPx; @@ -308,7 +319,7 @@ public class DeviceProfile { hotseatBarSidePaddingStartPx = isVerticalBarLayout() ? workspacePageIndicatorHeight : 0; int hotseatExtraVerticalSize = res.getDimensionPixelSize(R.dimen.dynamic_grid_hotseat_extra_vertical_size); - hotseatBarSizePx = pxFromDp(inv.iconSize, mInfo.metrics, 1f) + hotseatBarSizePx = pxFromDp(inv.iconSize, mMetrics, 1f) + (isVerticalBarLayout() ? (hotseatBarSidePaddingStartPx + hotseatBarSidePaddingEndPx) : (hotseatBarTopPaddingPx + hotseatBarBottomPaddingPx @@ -511,16 +522,16 @@ public class DeviceProfile { // Workspace final boolean isVerticalLayout = isVerticalBarLayout(); float invIconSizeDp = isLandscape ? inv.landscapeIconSize : inv.iconSize; - iconSizePx = Math.max(1, pxFromDp(invIconSizeDp, mInfo.metrics, scale)); + iconSizePx = Math.max(1, pxFromDp(invIconSizeDp, mMetrics, scale)); float invIconTextSizeSp = isLandscape ? inv.landscapeIconTextSize : inv.iconTextSize; - iconTextSizePx = (int) (Utilities.pxFromSp(invIconTextSizeSp, mInfo.metrics) * scale); + iconTextSizePx = (int) (Utilities.pxFromSp(invIconTextSizeSp, mMetrics) * scale); iconDrawablePaddingPx = (int) (iconDrawablePaddingOriginalPx * scale); setCellLayoutBorderSpacing((int) (cellLayoutBorderSpacingOriginalPx * scale)); if (isScalableGrid) { - cellWidthPx = pxFromDp(inv.minCellWidth, mInfo.metrics, scale); - cellHeightPx = pxFromDp(inv.minCellHeight, mInfo.metrics, scale); + cellWidthPx = pxFromDp(inv.minCellWidth, mMetrics, scale); + cellHeightPx = pxFromDp(inv.minCellHeight, mMetrics, scale); int cellContentHeight = iconSizePx + iconDrawablePaddingPx + Utilities.calculateTextHeight(iconTextSizePx); cellYPaddingPx = Math.max(0, cellHeightPx - cellContentHeight) / 2; @@ -542,8 +553,8 @@ public class DeviceProfile { // All apps if (allAppsHasDifferentNumColumns()) { - allAppsIconSizePx = pxFromDp(inv.allAppsIconSize, mInfo.metrics); - allAppsIconTextSizePx = Utilities.pxFromSp(inv.allAppsIconTextSize, mInfo.metrics); + allAppsIconSizePx = pxFromDp(inv.allAppsIconSize, mMetrics); + allAppsIconTextSizePx = Utilities.pxFromSp(inv.allAppsIconTextSize, mMetrics); allAppsIconDrawablePaddingPx = iconDrawablePaddingOriginalPx; autoResizeAllAppsCells(); } else { @@ -613,8 +624,8 @@ public class DeviceProfile { private void updateFolderCellSize(float scale, Resources res) { float invIconSizeDp = isVerticalBarLayout() ? inv.landscapeIconSize : inv.iconSize; - folderChildIconSizePx = Math.max(1, pxFromDp(invIconSizeDp, mInfo.metrics, scale)); - folderChildTextSizePx = pxFromDp(inv.iconTextSize, mInfo.metrics, scale); + folderChildIconSizePx = Math.max(1, pxFromDp(invIconSizeDp, mMetrics, scale)); + folderChildTextSizePx = pxFromDp(inv.iconTextSize, mMetrics, scale); folderLabelTextSizePx = (int) (folderChildTextSizePx * folderLabelTextScale); int textHeight = Utilities.calculateTextHeight(folderChildTextSizePx); @@ -801,14 +812,8 @@ public class DeviceProfile { */ public boolean updateIsSeascape(Context context) { if (isVerticalBarLayout()) { - // Check an up-to-date info. - DisplayController.Info displayInfo = DisplayController.getDefaultDisplay(context) - .createInfoForContext(context); - if (displayInfo == null) { - return false; - } - - boolean isSeascape = displayInfo.rotation == Surface.ROTATION_270; + boolean isSeascape = DisplayController.INSTANCE.get(context) + .getInfo().rotation == Surface.ROTATION_270; if (mIsSeascape != isSeascape) { mIsSeascape = isSeascape; return true; @@ -840,12 +845,12 @@ public class DeviceProfile { } private String pxToDpStr(String name, float value) { - return "\t" + name + ": " + value + "px (" + dpiFromPx(value, mInfo.metrics) + "dp)"; + return "\t" + name + ": " + value + "px (" + dpiFromPx(value, mMetrics.densityDpi) + "dp)"; } public void dump(String prefix, PrintWriter writer) { writer.println(prefix + "DeviceProfile:"); - writer.println(prefix + "\t1 dp = " + mInfo.metrics.density + " px"); + writer.println(prefix + "\t1 dp = " + mMetrics.density + " px"); writer.println(prefix + "\tisTablet:" + isTablet); writer.println(prefix + "\tisLargeTablet:" + isLargeTablet); @@ -938,7 +943,7 @@ public class DeviceProfile { private static Context getContext(Context c, Info info, int orientation) { Configuration config = new Configuration(c.getResources().getConfiguration()); config.orientation = orientation; - config.densityDpi = info.metrics.densityDpi; + config.densityDpi = info.densityDpi; return c.createConfigurationContext(config); } diff --git a/src/com/android/launcher3/InvariantDeviceProfile.java b/src/com/android/launcher3/InvariantDeviceProfile.java index 61023be019..3c85564125 100644 --- a/src/com/android/launcher3/InvariantDeviceProfile.java +++ b/src/com/android/launcher3/InvariantDeviceProfile.java @@ -20,6 +20,8 @@ import static com.android.launcher3.Utilities.getDevicePrefs; import static com.android.launcher3.Utilities.getPointString; import static com.android.launcher3.config.FeatureFlags.ENABLE_FOUR_COLUMNS; import static com.android.launcher3.config.FeatureFlags.ENABLE_TWO_PANEL_HOME; +import static com.android.launcher3.util.DisplayController.CHANGE_DENSITY; +import static com.android.launcher3.util.DisplayController.CHANGE_SIZE; import static com.android.launcher3.util.Executors.MAIN_EXECUTOR; import static com.android.launcher3.util.PackageManagerHelper.getPackageFilter; @@ -49,7 +51,6 @@ import androidx.annotation.VisibleForTesting; import com.android.launcher3.graphics.IconShape; import com.android.launcher3.testing.TestProtocol; -import com.android.launcher3.util.ConfigMonitor; import com.android.launcher3.util.DisplayController; import com.android.launcher3.util.DisplayController.Info; import com.android.launcher3.util.IntArray; @@ -156,7 +157,6 @@ public class InvariantDeviceProfile { public Rect defaultWidgetPadding; private final ArrayList mChangeListeners = new ArrayList<>(); - private ConfigMonitor mConfigMonitor; private OverlayMonitor mOverlayMonitor; @VisibleForTesting @@ -203,7 +203,12 @@ public class InvariantDeviceProfile { .putString(KEY_MIGRATION_SRC_WORKSPACE_SIZE, getPointString(numColumns, numRows)) .apply(); - mConfigMonitor = new ConfigMonitor(context, this::onConfigChanged); + DisplayController.INSTANCE.get(context).addChangeListener( + (info, flags) -> { + if ((flags & (CHANGE_SIZE | CHANGE_DENSITY)) != 0) { + onConfigChanged(context); + } + }); mOverlayMonitor = new OverlayMonitor(context); } @@ -227,7 +232,7 @@ public class InvariantDeviceProfile { // Get the display info based on default display and interpolate it to existing display DisplayOption defaultDisplayOption = invDistWeightedInterpolate( - DisplayController.getDefaultDisplay(context).getInfo(), + DisplayController.INSTANCE.get(context).getInfo(), getPredefinedDeviceProfiles(context, gridName)); Info myInfo = new Info(context, display); @@ -276,7 +281,7 @@ public class InvariantDeviceProfile { } private String initGrid(Context context, String gridName) { - Info displayInfo = DisplayController.getDefaultDisplay(context).getInfo(); + Info displayInfo = DisplayController.INSTANCE.get(context).getInfo(); ArrayList allOptions = getPredefinedDeviceProfiles(context, gridName); DisplayOption displayOption = invDistWeightedInterpolate(displayInfo, allOptions); @@ -286,6 +291,7 @@ public class InvariantDeviceProfile { private void initGrid( Context context, Info displayInfo, DisplayOption displayOption) { + DisplayMetrics metrics = context.getResources().getDisplayMetrics(); GridOption closestProfile = displayOption.grid; numRows = closestProfile.numRows; numColumns = closestProfile.numColumns; @@ -303,7 +309,7 @@ public class InvariantDeviceProfile { iconSize = displayOption.iconSize; iconShapePath = getIconShapePath(context); landscapeIconSize = displayOption.landscapeIconSize; - iconBitmapSize = ResourceUtils.pxFromDp(iconSize, displayInfo.metrics); + iconBitmapSize = ResourceUtils.pxFromDp(iconSize, metrics); iconTextSize = displayOption.iconTextSize; landscapeIconTextSize = displayOption.landscapeIconTextSize; fillResIconDpi = getLauncherIconDensity(iconBitmapSize); @@ -328,7 +334,7 @@ public class InvariantDeviceProfile { // If the partner customization apk contains any grid overrides, apply them // Supported overrides: numRows, numColumns, iconSize - applyPartnerDeviceProfileOverrides(context, displayInfo.metrics); + applyPartnerDeviceProfileOverrides(context, metrics); Point realSize = new Point(displayInfo.realSize); // The real size never changes. smallSide and largeSide will remain the @@ -425,10 +431,6 @@ public class InvariantDeviceProfile { } private void apply(Context context, int changeFlags) { - // Create a new config monitor - mConfigMonitor.unregister(); - mConfigMonitor = new ConfigMonitor(context, this::onConfigChanged); - for (OnIDPChangeListener listener : mChangeListeners) { listener.onIdpChanged(changeFlags, this); } @@ -530,10 +532,10 @@ public class InvariantDeviceProfile { Point largestSize = new Point(displayInfo.largestSize); // This guarantees that width < height - float width = Utilities.dpiFromPx(Math.min(smallestSize.x, smallestSize.y), - displayInfo.metrics); - float height = Utilities.dpiFromPx(Math.min(largestSize.x, largestSize.y), - displayInfo.metrics); + float width = Utilities.dpiFromPx((float) Math.min(smallestSize.x, smallestSize.y), + displayInfo.densityDpi); + float height = Utilities.dpiFromPx((float) Math.min(largestSize.x, largestSize.y), + displayInfo.densityDpi); // Sort the profiles based on the closeness to the device size Collections.sort(points, (a, b) -> diff --git a/src/com/android/launcher3/Partner.java b/src/com/android/launcher3/Partner.java index d79f62d470..0bdb37c076 100644 --- a/src/com/android/launcher3/Partner.java +++ b/src/com/android/launcher3/Partner.java @@ -129,7 +129,7 @@ public class Partner { "dimen", getPackageName()); if (resId > 0) { int px = getResources().getDimensionPixelSize(resId); - iconSize = Utilities.dpiFromPx(px, dm); + iconSize = Utilities.dpiFromPx((float) px, dm.densityDpi); } } catch (Resources.NotFoundException ex) { Log.e(TAG, "Invalid Partner grid resource!", ex); diff --git a/src/com/android/launcher3/Utilities.java b/src/com/android/launcher3/Utilities.java index 6f12ec784f..3312915fd3 100644 --- a/src/com/android/launcher3/Utilities.java +++ b/src/com/android/launcher3/Utilities.java @@ -404,8 +404,8 @@ public final class Utilities { return res.getConfiguration().getLayoutDirection() == View.LAYOUT_DIRECTION_RTL; } - public static float dpiFromPx(float size, DisplayMetrics metrics) { - float densityRatio = (float) metrics.densityDpi / DisplayMetrics.DENSITY_DEFAULT; + public static float dpiFromPx(float size, int densityDpi) { + float densityRatio = (float) densityDpi / DisplayMetrics.DENSITY_DEFAULT; return (size / densityRatio); } diff --git a/src/com/android/launcher3/util/ConfigMonitor.java b/src/com/android/launcher3/util/ConfigMonitor.java deleted file mode 100644 index f7023e8f44..0000000000 --- a/src/com/android/launcher3/util/ConfigMonitor.java +++ /dev/null @@ -1,132 +0,0 @@ -package com.android.launcher3.util; - -/** - * Copyright (C) 2015 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. - */ - -import static com.android.launcher3.util.Executors.MAIN_EXECUTOR; - -import android.content.BroadcastReceiver; -import android.content.Context; -import android.content.Intent; -import android.content.IntentFilter; -import android.content.res.Configuration; -import android.graphics.Point; -import android.util.Log; - -import com.android.launcher3.testing.TestProtocol; -import com.android.launcher3.util.DisplayController.DisplayInfoChangeListener; -import com.android.launcher3.util.DisplayController.Info; - -import java.util.function.Consumer; - -/** - * {@link BroadcastReceiver} which watches configuration changes and - * notifies the callback in case changes which affect the device profile occur. - */ -public class ConfigMonitor extends BroadcastReceiver implements DisplayInfoChangeListener { - - private static final String TAG = "ConfigMonitor"; - - private final Point mTmpPoint1 = new Point(); - private final Point mTmpPoint2 = new Point(); - - private final Context mContext; - private final float mFontScale; - private final int mDensity; - - private final int mDisplayId; - private final Point mRealSize; - private final Point mSmallestSize, mLargestSize; - - private Consumer mCallback; - - public ConfigMonitor(Context context, Consumer callback) { - mContext = context; - - Configuration config = context.getResources().getConfiguration(); - mFontScale = config.fontScale; - mDensity = config.densityDpi; - - DisplayController.DisplayHolder display = DisplayController.getDefaultDisplay(context); - display.addChangeListener(this); - Info displayInfo = display.getInfo(); - mDisplayId = displayInfo.id; - - mRealSize = new Point(displayInfo.realSize); - mSmallestSize = new Point(displayInfo.smallestSize); - mLargestSize = new Point(displayInfo.largestSize); - - mCallback = callback; - - // Listen for configuration change - mContext.registerReceiver(this, new IntentFilter(Intent.ACTION_CONFIGURATION_CHANGED)); - if (TestProtocol.sDebugTracing) { - Log.d(TestProtocol.LAUNCHER_NOT_TRANSPOSED, "ConfigMonitor.register: this=" - + System.identityHashCode(this) + " callback=" + callback.getClass().getName()); - } - } - - @Override - public void onReceive(Context context, Intent intent) { - Configuration config = context.getResources().getConfiguration(); - if (mFontScale != config.fontScale || mDensity != config.densityDpi) { - Log.d(TAG, "Configuration changed."); - notifyChange(); - } - } - - @Override - public void onDisplayInfoChanged(Info info, int flags) { - if (info.id != mDisplayId) { - return; - } - mTmpPoint1.set(info.realSize.x, info.realSize.y); - if (!mRealSize.equals(mTmpPoint1) && !mRealSize.equals(mTmpPoint1.y, mTmpPoint1.x)) { - Log.d(TAG, String.format("Display size changed from %s to %s", mRealSize, mTmpPoint1)); - notifyChange(); - return; - } - - mTmpPoint1.set(info.smallestSize.x, info.smallestSize.y); - mTmpPoint2.set(info.largestSize.x, info.largestSize.y); - if (!mSmallestSize.equals(mTmpPoint1) || !mLargestSize.equals(mTmpPoint2)) { - Log.d(TAG, String.format("Available size changed from [%s, %s] to [%s, %s]", - mSmallestSize, mLargestSize, mTmpPoint1, mTmpPoint2)); - notifyChange(); - } - } - - private synchronized void notifyChange() { - if (mCallback != null) { - Consumer callback = mCallback; - mCallback = null; - MAIN_EXECUTOR.execute(() -> callback.accept(mContext)); - } - } - - public void unregister() { - if (TestProtocol.sDebugTracing) { - Log.d(TestProtocol.LAUNCHER_NOT_TRANSPOSED, "ConfigMonitor.unregister: this=" - + System.identityHashCode(this)); - } - try { - mContext.unregisterReceiver(this); - DisplayController.getDefaultDisplay(mContext).removeChangeListener(this); - } catch (Exception e) { - Log.e(TAG, "Failed to unregister config monitor", e); - } - } -} diff --git a/src/com/android/launcher3/util/DisplayController.java b/src/com/android/launcher3/util/DisplayController.java index d0e8bb1b39..07c89b9b43 100644 --- a/src/com/android/launcher3/util/DisplayController.java +++ b/src/com/android/launcher3/util/DisplayController.java @@ -16,21 +16,28 @@ package com.android.launcher3.util; import static android.view.Display.DEFAULT_DISPLAY; +import static android.view.WindowManager.LayoutParams.TYPE_APPLICATION; import static com.android.launcher3.util.Executors.MAIN_EXECUTOR; import static com.android.launcher3.util.Executors.UI_HELPER_EXECUTOR; +import android.annotation.SuppressLint; +import android.annotation.TargetApi; +import android.content.ComponentCallbacks; import android.content.Context; +import android.content.Intent; +import android.content.IntentFilter; +import android.content.res.Configuration; import android.graphics.Point; import android.hardware.display.DisplayManager; import android.hardware.display.DisplayManager.DisplayListener; -import android.util.DisplayMetrics; +import android.os.Build; import android.util.Log; -import android.util.SparseArray; import android.view.Display; -import androidx.annotation.Nullable; -import androidx.annotation.VisibleForTesting; +import androidx.annotation.AnyThread; +import androidx.annotation.UiThread; +import androidx.annotation.WorkerThread; import com.android.launcher3.Utilities; @@ -39,104 +46,78 @@ import java.util.ArrayList; /** * Utility class to cache properties of default display to avoid a system RPC on every call. */ -public class DisplayController implements DisplayListener { +@SuppressLint("NewApi") +public class DisplayController implements DisplayListener, ComponentCallbacks { private static final String TAG = "DisplayController"; public static final MainThreadInitializedObject INSTANCE = new MainThreadInitializedObject<>(DisplayController::new); - private final SparseArray mOtherDisplays = new SparseArray<>(0); - // We store the default display separately, to avoid null checks for primary use case. - private final DisplayHolder mDefaultDisplay; + public static final int CHANGE_SIZE = 1 << 0; + public static final int CHANGE_ROTATION = 1 << 1; + public static final int CHANGE_FRAME_DELAY = 1 << 2; + public static final int CHANGE_DENSITY = 1 << 3; - private final ArrayList mListListeners = new ArrayList<>(); + public static final int CHANGE_ALL = CHANGE_SIZE | CHANGE_ROTATION + | CHANGE_FRAME_DELAY | CHANGE_DENSITY; + + private final Context mContext; + private final DisplayManager mDM; + + // Null for SDK < S + private final Context mWindowContext; + + private final ArrayList mListeners = new ArrayList<>(); + private Info mInfo; private DisplayController(Context context) { - mDefaultDisplay = DisplayHolder.create(context, DEFAULT_DISPLAY); + mContext = context; + mDM = context.getSystemService(DisplayManager.class); - DisplayManager dm = context.getSystemService(DisplayManager.class); - dm.registerDisplayListener(this, UI_HELPER_EXECUTOR.getHandler()); - } - - @Override - public final void onDisplayAdded(int displayId) { - DisplayHolder holder = DisplayHolder.create(mDefaultDisplay.mDisplayContext, displayId); - if (holder == null) { - // Display is already removed by the time we dot this. - return; - } - synchronized (mOtherDisplays) { - mOtherDisplays.put(displayId, holder); - } - MAIN_EXECUTOR.execute(() -> mListListeners.forEach(l-> l.onDisplayAdded(holder))); - } - - @Override - public final void onDisplayRemoved(int displayId) { - synchronized (mOtherDisplays) { - mOtherDisplays.remove(displayId); - } - MAIN_EXECUTOR.execute(() -> mListListeners.forEach(l-> l.onDisplayRemoved(displayId))); - } - - /** - * Returns the holder corresponding to the given display - */ - public DisplayHolder getHolder(int displayId) { - if (displayId == mDefaultDisplay.mId) { - return mDefaultDisplay; + Display display = mDM.getDisplay(DEFAULT_DISPLAY); + if (Utilities.ATLEAST_S) { + mWindowContext = mContext.createWindowContext(display, TYPE_APPLICATION, null); + mWindowContext.registerComponentCallbacks(this); } else { - synchronized (mOtherDisplays) { - return mOtherDisplays.get(displayId); - } + mWindowContext = null; + SimpleBroadcastReceiver configChangeReceiver = + new SimpleBroadcastReceiver(this::onConfigChanged); + mContext.registerReceiver(configChangeReceiver, + new IntentFilter(Intent.ACTION_CONFIGURATION_CHANGED)); } + + mInfo = createInfo(display); + mDM.registerDisplayListener(this, UI_HELPER_EXECUTOR.getHandler()); } - /** - * Adds a listener for display list changes - */ - public void addListChangeListener(DisplayListChangeListener listener) { - mListListeners.add(listener); - } + @Override + public final void onDisplayAdded(int displayId) { } - /** - * Removes a previously added display list change listener - */ - public void removeListChangeListener(DisplayListChangeListener listener) { - mListListeners.remove(listener); - } + @Override + public final void onDisplayRemoved(int displayId) { } + @WorkerThread @Override public final void onDisplayChanged(int displayId) { - DisplayHolder holder = getHolder(displayId); - if (holder != null) { - holder.handleOnChange(); + if (displayId != DEFAULT_DISPLAY) { + return; } + Display display = mDM.getDisplay(DEFAULT_DISPLAY); + if (display == null) { + return; + } + if (Utilities.ATLEAST_S) { + // Only check for refresh rate. Everything else comes from component callbacks + if (getSingleFrameMs(display) == mInfo.singleFrameMs) { + return; + } + } + handleInfoChange(display); } public static int getSingleFrameMs(Context context) { - return getDefaultDisplay(context).getInfo().singleFrameMs; - } - - public static DisplayHolder getDefaultDisplay(Context context) { - return INSTANCE.get(context).mDefaultDisplay; - } - - /** - * A listener to receiving addition or removal of new displays - */ - public interface DisplayListChangeListener { - - /** - * Called when a new display is added - */ - void onDisplayAdded(DisplayHolder holder); - - /** - * Called when a previously added display is removed - */ - void onDisplayRemoved(int displayId); + return INSTANCE.get(context).getInfo().singleFrameMs; } /** @@ -147,147 +128,121 @@ public class DisplayController implements DisplayListener { void onDisplayInfoChanged(Info info, int flags); } - public static class DisplayHolder { - - public static final int CHANGE_SIZE = 1 << 0; - public static final int CHANGE_ROTATION = 1 << 1; - public static final int CHANGE_FRAME_DELAY = 1 << 2; - - public static final int CHANGE_ALL = CHANGE_SIZE | CHANGE_ROTATION | CHANGE_FRAME_DELAY; - - final Context mDisplayContext; - final int mId; - private final ArrayList mListeners = new ArrayList<>(); - private DisplayController.Info mInfo; - - private DisplayHolder(Context displayContext) { - mDisplayContext = displayContext; - // Note that the Display object must be obtained from DisplayManager which is - // associated to the display context, so the Display is isolated from Activity and - // Application to provide the actual state of device that excludes the additional - // adjustment and override. - mInfo = new DisplayController.Info(mDisplayContext); - mId = mInfo.id; - } - - public void addChangeListener(DisplayInfoChangeListener listener) { - mListeners.add(listener); - } - - public void removeChangeListener(DisplayInfoChangeListener listener) { - mListeners.remove(listener); - } - - public DisplayController.Info getInfo() { - return mInfo; - } - - /** Creates and up-to-date DisplayController.Info for the given context. */ - @Nullable - public Info createInfoForContext(Context context) { - Display display = Utilities.ATLEAST_R ? context.getDisplay() : null; - if (display == null) { - display = context.getSystemService(DisplayManager.class).getDisplay(mId); - } - if (display == null) { - return null; - } - // Refresh the Context the prevent stale DisplayMetrics. - Context displayContext = context.getApplicationContext().createDisplayContext(display); - return new Info(displayContext, display); - } - - public Context getDisplayContext() { - return mDisplayContext; - } - - protected void handleOnChange() { - Info oldInfo = mInfo; - Info newInfo = createInfoForContext(mDisplayContext); - if (newInfo == null) { - return; - } - - int change = 0; - if (newInfo.hasDifferentSize(oldInfo)) { - change |= CHANGE_SIZE; - } - if (newInfo.rotation != oldInfo.rotation) { - change |= CHANGE_ROTATION; - } - if (newInfo.singleFrameMs != oldInfo.singleFrameMs) { - change |= CHANGE_FRAME_DELAY; - } - - if (change != 0) { - mInfo = newInfo; - final int flags = change; - MAIN_EXECUTOR.execute(() -> notifyChange(flags)); + /** + * Only used for pre-S + */ + private void onConfigChanged(Intent intent) { + Configuration config = mContext.getResources().getConfiguration(); + if (config.fontScale != config.fontScale || mInfo.densityDpi != config.densityDpi) { + Log.d(TAG, "Configuration changed, notifying listeners"); + Display display = mDM.getDisplay(DEFAULT_DISPLAY); + if (display != null) { + handleInfoChange(display); } } + } - private void notifyChange(int flags) { - for (int i = mListeners.size() - 1; i >= 0; i--) { - mListeners.get(i).onDisplayInfoChanged(mInfo, flags); - } + @UiThread + @Override + @TargetApi(Build.VERSION_CODES.S) + public final void onConfigurationChanged(Configuration config) { + Display display = mWindowContext.getDisplay(); + if (config.densityDpi != mInfo.densityDpi + || config.fontScale != mInfo.fontScale + || display.getRotation() != mInfo.rotation + || !mInfo.mScreenSizeDp.equals( + Math.min(config.screenHeightDp, config.screenWidthDp), + Math.max(config.screenHeightDp, config.screenWidthDp))) { + handleInfoChange(display); + } + } + + @Override + public final void onLowMemory() { } + + public void addChangeListener(DisplayInfoChangeListener listener) { + mListeners.add(listener); + } + + public void removeChangeListener(DisplayInfoChangeListener listener) { + mListeners.remove(listener); + } + + public Info getInfo() { + return mInfo; + } + + private Info createInfo(Display display) { + return new Info(mContext.createDisplayContext(display), display); + } + + @AnyThread + private void handleInfoChange(Display display) { + Info oldInfo = mInfo; + Info newInfo = createInfo(display); + int change = 0; + if (newInfo.hasDifferentSize(oldInfo)) { + change |= CHANGE_SIZE; + } + if (newInfo.rotation != oldInfo.rotation) { + change |= CHANGE_ROTATION; + } + if (newInfo.singleFrameMs != oldInfo.singleFrameMs) { + change |= CHANGE_FRAME_DELAY; + } + if (newInfo.densityDpi != oldInfo.densityDpi || newInfo.fontScale != oldInfo.fontScale) { + change |= CHANGE_DENSITY; } - private static DisplayHolder create(Context context, int id) { - DisplayManager dm = context.getSystemService(DisplayManager.class); - Display display = dm.getDisplay(id); - if (display == null) { - return null; - } - // Use application context to create display context so that it can have its own - // Resources. - Context displayContext = context.getApplicationContext().createDisplayContext(display); - return new DisplayHolder(displayContext); + if (change != 0) { + mInfo = newInfo; + final int flags = change; + MAIN_EXECUTOR.execute(() -> notifyChange(flags)); + } + } + + private void notifyChange(int flags) { + for (int i = mListeners.size() - 1; i >= 0; i--) { + mListeners.get(i).onDisplayInfoChanged(mInfo, flags); } } public static class Info { public final int id; - public final int rotation; public final int singleFrameMs; + // Configuration properties + public final int rotation; + public final float fontScale; + public final int densityDpi; + + private final Point mScreenSizeDp; + public final Point realSize; public final Point smallestSize; public final Point largestSize; - public final DisplayMetrics metrics; - - @VisibleForTesting - public Info(int id, int rotation, int singleFrameMs, Point realSize, Point smallestSize, - Point largestSize, DisplayMetrics metrics) { - this.id = id; - this.rotation = rotation; - this.singleFrameMs = singleFrameMs; - this.realSize = realSize; - this.smallestSize = smallestSize; - this.largestSize = largestSize; - this.metrics = metrics; - } - - private Info(Context context) { - this(context, context.getSystemService(DisplayManager.class) - .getDisplay(DEFAULT_DISPLAY)); - } - public Info(Context context, Display display) { id = display.getDisplayId(); + rotation = display.getRotation(); - float refreshRate = display.getRefreshRate(); - singleFrameMs = refreshRate > 0 ? (int) (1000 / refreshRate) : 16; + Configuration config = context.getResources().getConfiguration(); + fontScale = config.fontScale; + densityDpi = config.densityDpi; + mScreenSizeDp = new Point( + Math.min(config.screenHeightDp, config.screenWidthDp), + Math.max(config.screenHeightDp, config.screenWidthDp)); + + singleFrameMs = getSingleFrameMs(display); realSize = new Point(); smallestSize = new Point(); largestSize = new Point(); + display.getRealSize(realSize); display.getCurrentSizeRange(smallestSize, largestSize); - - metrics = context.getResources().getDisplayMetrics(); } private boolean hasDifferentSize(Info info) { @@ -307,4 +262,9 @@ public class DisplayController implements DisplayListener { return false; } } + + private static int getSingleFrameMs(Display display) { + float refreshRate = display.getRefreshRate(); + return refreshRate > 0 ? (int) (1000 / refreshRate) : 16; + } } diff --git a/src/com/android/launcher3/views/FloatingSurfaceView.java b/src/com/android/launcher3/views/FloatingSurfaceView.java index 011f6de8e2..f13484f131 100644 --- a/src/com/android/launcher3/views/FloatingSurfaceView.java +++ b/src/com/android/launcher3/views/FloatingSurfaceView.java @@ -97,7 +97,7 @@ public class FloatingSurfaceView extends AbstractFloatingView implements // Remove after some time, to avoid flickering Executors.MAIN_EXECUTOR.getHandler().postDelayed(mRemoveViewRunnable, - DisplayController.getDefaultDisplay(mLauncher).getInfo().singleFrameMs); + DisplayController.INSTANCE.get(mLauncher).getInfo().singleFrameMs); } private void removeViewFromParent() {