2020-08-11 12:06:49 -07:00
|
|
|
/*
|
|
|
|
|
* Copyright (C) 2019 The Android Open Source Project
|
|
|
|
|
*
|
|
|
|
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
|
|
|
* you may not use this file except in compliance with the License.
|
|
|
|
|
* You may obtain a copy of the License at
|
|
|
|
|
*
|
|
|
|
|
* http://www.apache.org/licenses/LICENSE-2.0
|
|
|
|
|
*
|
|
|
|
|
* Unless required by applicable law or agreed to in writing, software
|
|
|
|
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
|
|
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
|
|
|
* See the License for the specific language governing permissions and
|
|
|
|
|
* limitations under the License.
|
|
|
|
|
*/
|
|
|
|
|
package com.android.launcher3.util;
|
|
|
|
|
|
|
|
|
|
import static android.view.Display.DEFAULT_DISPLAY;
|
2021-04-20 16:51:10 -07:00
|
|
|
import static android.view.WindowManager.LayoutParams.TYPE_APPLICATION;
|
2020-08-11 12:06:49 -07:00
|
|
|
|
|
|
|
|
import static com.android.launcher3.util.Executors.MAIN_EXECUTOR;
|
|
|
|
|
import static com.android.launcher3.util.Executors.UI_HELPER_EXECUTOR;
|
|
|
|
|
|
2021-04-20 16:51:10 -07:00
|
|
|
import android.annotation.SuppressLint;
|
|
|
|
|
import android.annotation.TargetApi;
|
|
|
|
|
import android.content.ComponentCallbacks;
|
2020-08-11 12:06:49 -07:00
|
|
|
import android.content.Context;
|
2021-04-20 16:51:10 -07:00
|
|
|
import android.content.Intent;
|
|
|
|
|
import android.content.IntentFilter;
|
|
|
|
|
import android.content.res.Configuration;
|
2020-08-11 12:06:49 -07:00
|
|
|
import android.graphics.Point;
|
|
|
|
|
import android.hardware.display.DisplayManager;
|
|
|
|
|
import android.hardware.display.DisplayManager.DisplayListener;
|
2021-04-20 16:51:10 -07:00
|
|
|
import android.os.Build;
|
2020-08-11 12:06:49 -07:00
|
|
|
import android.util.Log;
|
|
|
|
|
import android.view.Display;
|
|
|
|
|
|
2021-04-20 16:51:10 -07:00
|
|
|
import androidx.annotation.AnyThread;
|
|
|
|
|
import androidx.annotation.UiThread;
|
|
|
|
|
import androidx.annotation.WorkerThread;
|
2020-08-11 12:06:49 -07:00
|
|
|
|
2021-01-27 14:03:51 -08:00
|
|
|
import com.android.launcher3.Utilities;
|
|
|
|
|
|
2020-08-11 12:06:49 -07:00
|
|
|
import java.util.ArrayList;
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* Utility class to cache properties of default display to avoid a system RPC on every call.
|
|
|
|
|
*/
|
2021-04-20 16:51:10 -07:00
|
|
|
@SuppressLint("NewApi")
|
|
|
|
|
public class DisplayController implements DisplayListener, ComponentCallbacks {
|
2020-08-11 12:06:49 -07:00
|
|
|
|
|
|
|
|
private static final String TAG = "DisplayController";
|
|
|
|
|
|
|
|
|
|
public static final MainThreadInitializedObject<DisplayController> INSTANCE =
|
|
|
|
|
new MainThreadInitializedObject<>(DisplayController::new);
|
|
|
|
|
|
2021-04-20 16:51:10 -07:00
|
|
|
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;
|
2020-08-11 12:06:49 -07:00
|
|
|
|
2021-04-20 16:51:10 -07:00
|
|
|
public static final int CHANGE_ALL = CHANGE_SIZE | CHANGE_ROTATION
|
|
|
|
|
| CHANGE_FRAME_DELAY | CHANGE_DENSITY;
|
2020-08-11 12:06:49 -07:00
|
|
|
|
2021-04-20 16:51:10 -07:00
|
|
|
private final Context mContext;
|
|
|
|
|
private final DisplayManager mDM;
|
2020-08-11 12:06:49 -07:00
|
|
|
|
2021-04-20 16:51:10 -07:00
|
|
|
// Null for SDK < S
|
|
|
|
|
private final Context mWindowContext;
|
2020-08-11 12:06:49 -07:00
|
|
|
|
2021-04-20 16:51:10 -07:00
|
|
|
private final ArrayList<DisplayInfoChangeListener> mListeners = new ArrayList<>();
|
|
|
|
|
private Info mInfo;
|
2020-08-11 12:06:49 -07:00
|
|
|
|
2021-04-20 16:51:10 -07:00
|
|
|
private DisplayController(Context context) {
|
|
|
|
|
mContext = context;
|
|
|
|
|
mDM = context.getSystemService(DisplayManager.class);
|
2020-08-11 12:06:49 -07:00
|
|
|
|
2021-04-20 16:51:10 -07:00
|
|
|
Display display = mDM.getDisplay(DEFAULT_DISPLAY);
|
|
|
|
|
if (Utilities.ATLEAST_S) {
|
|
|
|
|
mWindowContext = mContext.createWindowContext(display, TYPE_APPLICATION, null);
|
|
|
|
|
mWindowContext.registerComponentCallbacks(this);
|
2020-08-11 12:06:49 -07:00
|
|
|
} else {
|
2021-04-20 16:51:10 -07:00
|
|
|
mWindowContext = null;
|
|
|
|
|
SimpleBroadcastReceiver configChangeReceiver =
|
|
|
|
|
new SimpleBroadcastReceiver(this::onConfigChanged);
|
|
|
|
|
mContext.registerReceiver(configChangeReceiver,
|
|
|
|
|
new IntentFilter(Intent.ACTION_CONFIGURATION_CHANGED));
|
2020-08-11 12:06:49 -07:00
|
|
|
}
|
|
|
|
|
|
2021-04-20 16:51:10 -07:00
|
|
|
mInfo = createInfo(display);
|
|
|
|
|
mDM.registerDisplayListener(this, UI_HELPER_EXECUTOR.getHandler());
|
2020-08-11 12:06:49 -07:00
|
|
|
}
|
|
|
|
|
|
2021-04-20 16:51:10 -07:00
|
|
|
@Override
|
|
|
|
|
public final void onDisplayAdded(int displayId) { }
|
2020-08-11 12:06:49 -07:00
|
|
|
|
2021-04-20 16:51:10 -07:00
|
|
|
@Override
|
|
|
|
|
public final void onDisplayRemoved(int displayId) { }
|
|
|
|
|
|
|
|
|
|
@WorkerThread
|
2020-08-11 12:06:49 -07:00
|
|
|
@Override
|
|
|
|
|
public final void onDisplayChanged(int displayId) {
|
2021-04-20 16:51:10 -07:00
|
|
|
if (displayId != DEFAULT_DISPLAY) {
|
|
|
|
|
return;
|
2020-08-11 12:06:49 -07:00
|
|
|
}
|
2021-04-20 16:51:10 -07:00
|
|
|
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);
|
2020-08-11 12:06:49 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public static int getSingleFrameMs(Context context) {
|
2021-04-20 16:51:10 -07:00
|
|
|
return INSTANCE.get(context).getInfo().singleFrameMs;
|
2020-08-11 12:06:49 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* Interface for listening for display changes
|
|
|
|
|
*/
|
|
|
|
|
public interface DisplayInfoChangeListener {
|
|
|
|
|
|
|
|
|
|
void onDisplayInfoChanged(Info info, int flags);
|
|
|
|
|
}
|
|
|
|
|
|
2021-04-20 16:51:10 -07:00
|
|
|
/**
|
|
|
|
|
* 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);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
2020-08-11 12:06:49 -07:00
|
|
|
|
2021-04-20 16:51:10 -07:00
|
|
|
@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);
|
|
|
|
|
}
|
|
|
|
|
}
|
2020-08-11 12:06:49 -07:00
|
|
|
|
2021-04-20 16:51:10 -07:00
|
|
|
@Override
|
|
|
|
|
public final void onLowMemory() { }
|
2020-08-11 12:06:49 -07:00
|
|
|
|
2021-04-20 16:51:10 -07:00
|
|
|
public void addChangeListener(DisplayInfoChangeListener listener) {
|
|
|
|
|
mListeners.add(listener);
|
|
|
|
|
}
|
2020-08-11 12:06:49 -07:00
|
|
|
|
2021-04-20 16:51:10 -07:00
|
|
|
public void removeChangeListener(DisplayInfoChangeListener listener) {
|
|
|
|
|
mListeners.remove(listener);
|
|
|
|
|
}
|
2020-08-11 12:06:49 -07:00
|
|
|
|
2021-04-20 16:51:10 -07:00
|
|
|
public Info getInfo() {
|
|
|
|
|
return mInfo;
|
|
|
|
|
}
|
2020-08-11 12:06:49 -07:00
|
|
|
|
2021-04-20 16:51:10 -07:00
|
|
|
private Info createInfo(Display display) {
|
|
|
|
|
return new Info(mContext.createDisplayContext(display), display);
|
|
|
|
|
}
|
2020-08-11 12:06:49 -07:00
|
|
|
|
2021-04-20 16:51:10 -07:00
|
|
|
@AnyThread
|
|
|
|
|
private void handleInfoChange(Display display) {
|
|
|
|
|
Info oldInfo = mInfo;
|
|
|
|
|
Info newInfo = createInfo(display);
|
|
|
|
|
int change = 0;
|
|
|
|
|
if (newInfo.hasDifferentSize(oldInfo)) {
|
|
|
|
|
change |= CHANGE_SIZE;
|
2020-08-11 12:06:49 -07:00
|
|
|
}
|
2021-04-20 16:51:10 -07:00
|
|
|
if (newInfo.rotation != oldInfo.rotation) {
|
|
|
|
|
change |= CHANGE_ROTATION;
|
2021-01-27 14:03:51 -08:00
|
|
|
}
|
2021-04-20 16:51:10 -07:00
|
|
|
if (newInfo.singleFrameMs != oldInfo.singleFrameMs) {
|
|
|
|
|
change |= CHANGE_FRAME_DELAY;
|
2021-02-12 11:24:40 -08:00
|
|
|
}
|
2021-04-20 16:51:10 -07:00
|
|
|
if (newInfo.densityDpi != oldInfo.densityDpi || newInfo.fontScale != oldInfo.fontScale) {
|
|
|
|
|
change |= CHANGE_DENSITY;
|
2020-08-11 12:06:49 -07:00
|
|
|
}
|
|
|
|
|
|
2021-04-20 16:51:10 -07:00
|
|
|
if (change != 0) {
|
|
|
|
|
mInfo = newInfo;
|
|
|
|
|
final int flags = change;
|
|
|
|
|
MAIN_EXECUTOR.execute(() -> notifyChange(flags));
|
2020-08-11 12:06:49 -07:00
|
|
|
}
|
2021-04-20 16:51:10 -07:00
|
|
|
}
|
2020-08-11 12:06:49 -07:00
|
|
|
|
2021-04-20 16:51:10 -07:00
|
|
|
private void notifyChange(int flags) {
|
|
|
|
|
for (int i = mListeners.size() - 1; i >= 0; i--) {
|
|
|
|
|
mListeners.get(i).onDisplayInfoChanged(mInfo, flags);
|
2020-08-19 16:47:05 -07:00
|
|
|
}
|
2020-08-11 12:06:49 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public static class Info {
|
|
|
|
|
|
|
|
|
|
public final int id;
|
|
|
|
|
public final int singleFrameMs;
|
|
|
|
|
|
2021-04-20 16:51:10 -07:00
|
|
|
// Configuration properties
|
|
|
|
|
public final int rotation;
|
|
|
|
|
public final float fontScale;
|
|
|
|
|
public final int densityDpi;
|
|
|
|
|
|
|
|
|
|
private final Point mScreenSizeDp;
|
|
|
|
|
|
2020-08-11 12:06:49 -07:00
|
|
|
public final Point realSize;
|
|
|
|
|
public final Point smallestSize;
|
|
|
|
|
public final Point largestSize;
|
|
|
|
|
|
2021-01-26 15:22:18 +00:00
|
|
|
public Info(Context context, Display display) {
|
2020-08-11 12:06:49 -07:00
|
|
|
id = display.getDisplayId();
|
2021-04-20 16:51:10 -07:00
|
|
|
|
2020-08-11 12:06:49 -07:00
|
|
|
rotation = display.getRotation();
|
|
|
|
|
|
2021-04-20 16:51:10 -07:00
|
|
|
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);
|
2020-08-11 12:06:49 -07:00
|
|
|
|
|
|
|
|
realSize = new Point();
|
|
|
|
|
smallestSize = new Point();
|
|
|
|
|
largestSize = new Point();
|
2021-04-20 16:51:10 -07:00
|
|
|
|
2020-08-11 12:06:49 -07:00
|
|
|
display.getRealSize(realSize);
|
|
|
|
|
display.getCurrentSizeRange(smallestSize, largestSize);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private boolean hasDifferentSize(Info info) {
|
|
|
|
|
if (!realSize.equals(info.realSize)
|
|
|
|
|
&& !realSize.equals(info.realSize.y, info.realSize.x)) {
|
|
|
|
|
Log.d(TAG, String.format("Display size changed from %s to %s",
|
|
|
|
|
info.realSize, realSize));
|
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (!smallestSize.equals(info.smallestSize) || !largestSize.equals(info.largestSize)) {
|
|
|
|
|
Log.d(TAG, String.format("Available size changed from [%s, %s] to [%s, %s]",
|
|
|
|
|
smallestSize, largestSize, info.smallestSize, info.largestSize));
|
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
}
|
2021-04-20 16:51:10 -07:00
|
|
|
|
|
|
|
|
private static int getSingleFrameMs(Display display) {
|
|
|
|
|
float refreshRate = display.getRefreshRate();
|
|
|
|
|
return refreshRate > 0 ? (int) (1000 / refreshRate) : 16;
|
|
|
|
|
}
|
2020-08-11 12:06:49 -07:00
|
|
|
}
|