Merge "Using WindowContext for listening to configuration changes" into sc-dev
This commit is contained in:
committed by
Android (Google) Code Review
commit
6dcd0272f1
@@ -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) {
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
|
||||
@@ -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<OnIDPChangeListener> 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<DisplayOption> 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) ->
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
|
||||
@@ -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<Context> mCallback;
|
||||
|
||||
public ConfigMonitor(Context context, Consumer<Context> 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<Context> 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);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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<DisplayController> INSTANCE =
|
||||
new MainThreadInitializedObject<>(DisplayController::new);
|
||||
|
||||
private final SparseArray<DisplayHolder> 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<DisplayListChangeListener> 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<DisplayInfoChangeListener> 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<DisplayInfoChangeListener> 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;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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() {
|
||||
|
||||
Reference in New Issue
Block a user