Merge "Handling configuration changes at runtime instead of killing the process" into ub-launcher3-master
This commit is contained in:
commit
dcaa2d2ae7
|
@ -16,6 +16,8 @@
|
|||
|
||||
package com.android.launcher3;
|
||||
|
||||
import static com.android.launcher3.config.FeatureFlags.APPLY_CONFIG_AT_RUNTIME;
|
||||
|
||||
import android.annotation.TargetApi;
|
||||
import android.content.Context;
|
||||
import android.content.res.Configuration;
|
||||
|
@ -23,6 +25,7 @@ import android.content.res.TypedArray;
|
|||
import android.content.res.XmlResourceParser;
|
||||
import android.graphics.Point;
|
||||
import android.util.DisplayMetrics;
|
||||
import android.util.Log;
|
||||
import android.util.Xml;
|
||||
import android.view.Display;
|
||||
import android.view.WindowManager;
|
||||
|
@ -45,13 +48,13 @@ public class InvariantDeviceProfile {
|
|||
|
||||
// We do not need any synchronization for this variable as its only written on UI thread.
|
||||
public static final MainThreadInitializedObject<InvariantDeviceProfile> INSTANCE =
|
||||
new MainThreadInitializedObject<>((c) -> {
|
||||
new ConfigMonitor(c).register();
|
||||
return new InvariantDeviceProfile(c);
|
||||
});
|
||||
new MainThreadInitializedObject<>(InvariantDeviceProfile::new);
|
||||
|
||||
private static final float ICON_SIZE_DEFINED_IN_APP_DP = 48;
|
||||
|
||||
public static final int CHANGE_FLAG_GRID = 1 << 0;
|
||||
public static final int CHANGE_FLAG_ICON_SIZE = 1 << 1;
|
||||
|
||||
// Constants that affects the interpolation curve between statically defined device profile
|
||||
// buckets.
|
||||
private static float KNEARESTNEIGHBOR = 3;
|
||||
|
@ -61,9 +64,9 @@ public class InvariantDeviceProfile {
|
|||
private static float WEIGHT_EFFICIENT = 100000f;
|
||||
|
||||
// Profile-defining invariant properties
|
||||
String name;
|
||||
float minWidthDps;
|
||||
float minHeightDps;
|
||||
private String name;
|
||||
private float minWidthDps;
|
||||
private float minHeightDps;
|
||||
|
||||
/**
|
||||
* Number of icons per row and column in the workspace.
|
||||
|
@ -95,9 +98,11 @@ public class InvariantDeviceProfile {
|
|||
|
||||
public Point defaultWallpaperSize;
|
||||
|
||||
private final ArrayList<OnIDPChangeListener> mChangeListeners = new ArrayList<>();
|
||||
private ConfigMonitor mConfigMonitor;
|
||||
|
||||
@VisibleForTesting
|
||||
public InvariantDeviceProfile() {
|
||||
}
|
||||
public InvariantDeviceProfile() {}
|
||||
|
||||
private InvariantDeviceProfile(InvariantDeviceProfile p) {
|
||||
this(p.name, p.minWidthDps, p.minHeightDps, p.numRows, p.numColumns,
|
||||
|
@ -125,6 +130,12 @@ public class InvariantDeviceProfile {
|
|||
|
||||
@TargetApi(23)
|
||||
private InvariantDeviceProfile(Context context) {
|
||||
initGrid(context);
|
||||
mConfigMonitor = new ConfigMonitor(context,
|
||||
APPLY_CONFIG_AT_RUNTIME.get() ? this::onConfigChanged : this::killProcess);
|
||||
}
|
||||
|
||||
private void initGrid(Context context) {
|
||||
WindowManager wm = (WindowManager) context.getSystemService(Context.WINDOW_SERVICE);
|
||||
Display display = wm.getDefaultDisplay();
|
||||
DisplayMetrics dm = new DisplayMetrics();
|
||||
|
@ -185,6 +196,44 @@ public class InvariantDeviceProfile {
|
|||
}
|
||||
}
|
||||
|
||||
public void addOnChangeListener(OnIDPChangeListener listener) {
|
||||
mChangeListeners.add(listener);
|
||||
}
|
||||
|
||||
private void killProcess(Context context) {
|
||||
Log.e("ConfigMonitor", "restarting launcher");
|
||||
android.os.Process.killProcess(android.os.Process.myPid());
|
||||
}
|
||||
|
||||
private void onConfigChanged(Context context) {
|
||||
// Config changes, what shall we do?
|
||||
InvariantDeviceProfile oldProfile = new InvariantDeviceProfile(this);
|
||||
|
||||
// Re-init grid
|
||||
initGrid(context);
|
||||
|
||||
int changeFlags = 0;
|
||||
if (numRows != oldProfile.numRows ||
|
||||
numColumns != oldProfile.numColumns ||
|
||||
numFolderColumns != oldProfile.numFolderColumns ||
|
||||
numFolderRows != oldProfile.numFolderRows ||
|
||||
numHotseatIcons != oldProfile.numHotseatIcons) {
|
||||
changeFlags |= CHANGE_FLAG_GRID;
|
||||
}
|
||||
|
||||
if (iconSize != oldProfile.iconSize || iconBitmapSize != oldProfile.iconBitmapSize) {
|
||||
changeFlags |= CHANGE_FLAG_ICON_SIZE;
|
||||
}
|
||||
|
||||
// Create a new config monitor
|
||||
mConfigMonitor.unregister();
|
||||
mConfigMonitor = new ConfigMonitor(context, this::onConfigChanged);
|
||||
|
||||
for (OnIDPChangeListener listener : mChangeListeners) {
|
||||
listener.onIdpChanged(changeFlags, this);
|
||||
}
|
||||
}
|
||||
|
||||
ArrayList<InvariantDeviceProfile> getPredefinedDeviceProfiles(Context context) {
|
||||
ArrayList<InvariantDeviceProfile> profiles = new ArrayList<>();
|
||||
try (XmlResourceParser parser = context.getResources().getXml(R.xml.device_profiles)) {
|
||||
|
@ -356,4 +405,8 @@ public class InvariantDeviceProfile {
|
|||
return x * aspectRatio + y;
|
||||
}
|
||||
|
||||
public interface OnIDPChangeListener {
|
||||
|
||||
void onIdpChanged(int changeFlags, InvariantDeviceProfile profile);
|
||||
}
|
||||
}
|
|
@ -17,6 +17,7 @@
|
|||
package com.android.launcher3;
|
||||
|
||||
import static com.android.launcher3.util.SecureSettingsObserver.newNotificationSettingsObserver;
|
||||
import static com.android.launcher3.InvariantDeviceProfile.CHANGE_FLAG_ICON_SIZE;
|
||||
|
||||
import android.content.ComponentName;
|
||||
import android.content.ContentProviderClient;
|
||||
|
@ -30,6 +31,7 @@ import com.android.launcher3.compat.PackageInstallerCompat;
|
|||
import com.android.launcher3.compat.UserManagerCompat;
|
||||
import com.android.launcher3.config.FeatureFlags;
|
||||
import com.android.launcher3.icons.IconCache;
|
||||
import com.android.launcher3.icons.LauncherIcons;
|
||||
import com.android.launcher3.notification.NotificationListener;
|
||||
import com.android.launcher3.util.MainThreadInitializedObject;
|
||||
import com.android.launcher3.util.Preconditions;
|
||||
|
@ -94,6 +96,7 @@ public class LauncherAppState {
|
|||
|
||||
mContext.registerReceiver(mModel, filter);
|
||||
UserManagerCompat.getInstance(mContext).enableAndResetCache();
|
||||
mInvariantDeviceProfile.addOnChangeListener(this::onIdpChanged);
|
||||
|
||||
if (!mContext.getResources().getBoolean(R.bool.notification_badging_enabled)) {
|
||||
mNotificationBadgingObserver = null;
|
||||
|
@ -113,6 +116,19 @@ public class LauncherAppState {
|
|||
}
|
||||
}
|
||||
|
||||
private void onIdpChanged(int changeFlags, InvariantDeviceProfile idp) {
|
||||
if (changeFlags == 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
if ((changeFlags & CHANGE_FLAG_ICON_SIZE) != 0) {
|
||||
LauncherIcons.clearPool();
|
||||
mIconCache.updateIconParams(idp.fillResIconDpi, idp.iconBitmapSize);
|
||||
}
|
||||
|
||||
mModel.forceReload();
|
||||
}
|
||||
|
||||
/**
|
||||
* Call from Application.onTerminate(), which is not guaranteed to ever be called.
|
||||
*/
|
||||
|
|
|
@ -87,6 +87,12 @@ abstract class BaseFlags {
|
|||
// trying to make them fit the orientation the device is in.
|
||||
public static final boolean OVERVIEW_USE_SCREENSHOT_ORIENTATION = true;
|
||||
|
||||
/**
|
||||
* Feature flag to handle define config changes dynamically instead of killing the process.
|
||||
*/
|
||||
public static final TogglableFlag APPLY_CONFIG_AT_RUNTIME = new TogglableFlag(
|
||||
"APPLY_CONFIG_AT_RUNTIME", false, "Apply display changes dynamically");
|
||||
|
||||
public static void initialize(Context context) {
|
||||
// Avoid the disk read for user builds
|
||||
if (Utilities.IS_DEBUG_DEVICE) {
|
||||
|
@ -191,5 +197,4 @@ abstract class BaseFlags {
|
|||
return h$;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -90,11 +90,11 @@ public class BaseIconCache {
|
|||
private final HashMap<ComponentKey, CacheEntry> mCache =
|
||||
new HashMap<>(INITIAL_ICON_CACHE_CAPACITY);
|
||||
private final InstantAppResolver mInstantAppResolver;
|
||||
final int mIconDpi;
|
||||
|
||||
final IconDB mIconDb;
|
||||
final Handler mWorkerHandler;
|
||||
|
||||
int mIconDpi;
|
||||
IconDB mIconDb;
|
||||
|
||||
private final BitmapFactory.Options mDecodeOptions;
|
||||
|
||||
public BaseIconCache(Context context, int iconDpi, int iconPixelSize) {
|
||||
|
@ -103,8 +103,6 @@ public class BaseIconCache {
|
|||
mUserManager = UserManagerCompat.getInstance(mContext);
|
||||
mLauncherApps = LauncherAppsCompat.getInstance(mContext);
|
||||
mInstantAppResolver = InstantAppResolver.newInstance(mContext);
|
||||
mIconDpi = iconDpi;
|
||||
mIconDb = new IconDB(context, iconPixelSize);
|
||||
|
||||
mIconProvider = IconProvider.newInstance(context);
|
||||
mWorkerHandler = new Handler(LauncherModel.getWorkerLooper());
|
||||
|
@ -115,6 +113,22 @@ public class BaseIconCache {
|
|||
} else {
|
||||
mDecodeOptions = null;
|
||||
}
|
||||
|
||||
mIconDpi = iconDpi;
|
||||
mIconDb = new IconDB(context, iconPixelSize);
|
||||
}
|
||||
|
||||
public void updateIconParams(int iconDpi, int iconPixelSize) {
|
||||
mWorkerHandler.post(() -> updateIconParamsBg(iconDpi, iconPixelSize));
|
||||
}
|
||||
|
||||
private synchronized void updateIconParamsBg(int iconDpi, int iconPixelSize) {
|
||||
mIconDpi = iconDpi;
|
||||
mDefaultIcons.clear();
|
||||
|
||||
mIconDb.close();
|
||||
mIconDb = new IconDB(mContext, iconPixelSize);
|
||||
mCache.clear();
|
||||
}
|
||||
|
||||
private Drawable getFullResDefaultActivityIcon() {
|
||||
|
|
|
@ -27,11 +27,11 @@ import android.os.UserHandle;
|
|||
|
||||
import com.android.launcher3.AppInfo;
|
||||
import com.android.launcher3.FastBitmapDrawable;
|
||||
import com.android.launcher3.graphics.BitmapRenderer;
|
||||
import com.android.launcher3.InvariantDeviceProfile;
|
||||
import com.android.launcher3.ItemInfoWithIcon;
|
||||
import com.android.launcher3.LauncherAppState;
|
||||
import com.android.launcher3.Utilities;
|
||||
import com.android.launcher3.graphics.BitmapRenderer;
|
||||
import com.android.launcher3.model.PackageItemInfo;
|
||||
import com.android.launcher3.shortcuts.DeepShortcutManager;
|
||||
import com.android.launcher3.shortcuts.ShortcutInfoCompat;
|
||||
|
@ -48,13 +48,14 @@ public class LauncherIcons extends BaseIconFactory implements AutoCloseable {
|
|||
|
||||
private static final Object sPoolSync = new Object();
|
||||
private static LauncherIcons sPool;
|
||||
private LauncherIcons next;
|
||||
private static int sPoolId = 0;
|
||||
|
||||
/**
|
||||
* Return a new Message instance from the global pool. Allows us to
|
||||
* avoid allocating new objects in many cases.
|
||||
*/
|
||||
public static LauncherIcons obtain(Context context) {
|
||||
int poolId;
|
||||
synchronized (sPoolSync) {
|
||||
if (sPool != null) {
|
||||
LauncherIcons m = sPool;
|
||||
|
@ -62,9 +63,33 @@ public class LauncherIcons extends BaseIconFactory implements AutoCloseable {
|
|||
m.next = null;
|
||||
return m;
|
||||
}
|
||||
poolId = sPoolId;
|
||||
}
|
||||
|
||||
InvariantDeviceProfile idp = LauncherAppState.getIDP(context);
|
||||
return new LauncherIcons(context, idp.fillResIconDpi, idp.iconBitmapSize);
|
||||
return new LauncherIcons(context, idp.fillResIconDpi, idp.iconBitmapSize, poolId);
|
||||
}
|
||||
|
||||
public static void clearPool() {
|
||||
synchronized (sPoolSync) {
|
||||
sPool = null;
|
||||
sPoolId++;
|
||||
}
|
||||
}
|
||||
|
||||
private final Context mContext;
|
||||
private final int mFillResIconDpi;
|
||||
private final int mIconBitmapSize;
|
||||
private final int mPoolId;
|
||||
|
||||
private LauncherIcons next;
|
||||
|
||||
private LauncherIcons(Context context, int fillResIconDpi, int iconBitmapSize, int poolId) {
|
||||
super(context, fillResIconDpi, iconBitmapSize);
|
||||
mContext = context.getApplicationContext();
|
||||
mFillResIconDpi = fillResIconDpi;
|
||||
mIconBitmapSize = iconBitmapSize;
|
||||
mPoolId = poolId;
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -72,6 +97,9 @@ public class LauncherIcons extends BaseIconFactory implements AutoCloseable {
|
|||
*/
|
||||
public void recycle() {
|
||||
synchronized (sPoolSync) {
|
||||
if (sPoolId != mPoolId) {
|
||||
return;
|
||||
}
|
||||
// Clear any temporary state variables
|
||||
clear();
|
||||
|
||||
|
@ -85,17 +113,6 @@ public class LauncherIcons extends BaseIconFactory implements AutoCloseable {
|
|||
recycle();
|
||||
}
|
||||
|
||||
private final Context mContext;
|
||||
private final int mFillResIconDpi;
|
||||
private final int mIconBitmapSize;
|
||||
|
||||
private LauncherIcons(Context context, int fillResIconDpi, int iconBitmapSize) {
|
||||
super(context, fillResIconDpi, iconBitmapSize);
|
||||
mContext = context.getApplicationContext();
|
||||
mFillResIconDpi = fillResIconDpi;
|
||||
mIconBitmapSize = iconBitmapSize;
|
||||
}
|
||||
|
||||
public BitmapInfo createBadgedIconBitmap(Drawable icon, UserHandle user,
|
||||
int iconAppTargetSdk) {
|
||||
return createBadgedIconBitmap(icon, user, iconAppTargetSdk, false);
|
||||
|
|
|
@ -29,9 +29,12 @@ import android.util.Log;
|
|||
import android.view.Display;
|
||||
import android.view.WindowManager;
|
||||
|
||||
import com.android.launcher3.MainThreadExecutor;
|
||||
import com.android.launcher3.Utilities.Consumer;
|
||||
|
||||
/**
|
||||
* {@link BroadcastReceiver} which watches configuration changes and
|
||||
* restarts the process in case changes which affect the device profile occur.
|
||||
* notifies the callback in case changes which affect the device profile occur.
|
||||
*/
|
||||
public class ConfigMonitor extends BroadcastReceiver implements DisplayListener {
|
||||
|
||||
|
@ -48,7 +51,9 @@ public class ConfigMonitor extends BroadcastReceiver implements DisplayListener
|
|||
private final Point mRealSize;
|
||||
private final Point mSmallestSize, mLargestSize;
|
||||
|
||||
public ConfigMonitor(Context context) {
|
||||
private Consumer<Context> mCallback;
|
||||
|
||||
public ConfigMonitor(Context context, Consumer<Context> callback) {
|
||||
mContext = context;
|
||||
|
||||
Configuration config = context.getResources().getConfiguration();
|
||||
|
@ -64,6 +69,12 @@ public class ConfigMonitor extends BroadcastReceiver implements DisplayListener
|
|||
mSmallestSize = new Point();
|
||||
mLargestSize = new Point();
|
||||
display.getCurrentSizeRange(mSmallestSize, mLargestSize);
|
||||
|
||||
mCallback = callback;
|
||||
|
||||
mContext.registerReceiver(this, new IntentFilter(Intent.ACTION_CONFIGURATION_CHANGED));
|
||||
mContext.getSystemService(DisplayManager.class)
|
||||
.registerDisplayListener(this, new Handler(UiThreadHelper.getBackgroundLooper()));
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -71,16 +82,10 @@ public class ConfigMonitor extends BroadcastReceiver implements DisplayListener
|
|||
Configuration config = context.getResources().getConfiguration();
|
||||
if (mFontScale != config.fontScale || mDensity != config.densityDpi) {
|
||||
Log.d(TAG, "Configuration changed");
|
||||
killProcess();
|
||||
notifyChange();
|
||||
}
|
||||
}
|
||||
|
||||
public void register() {
|
||||
mContext.registerReceiver(this, new IntentFilter(Intent.ACTION_CONFIGURATION_CHANGED));
|
||||
mContext.getSystemService(DisplayManager.class)
|
||||
.registerDisplayListener(this, new Handler(UiThreadHelper.getBackgroundLooper()));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onDisplayAdded(int displayId) { }
|
||||
|
||||
|
@ -97,7 +102,7 @@ public class ConfigMonitor extends BroadcastReceiver implements DisplayListener
|
|||
|
||||
if (!mRealSize.equals(mTmpPoint1) && !mRealSize.equals(mTmpPoint1.y, mTmpPoint1.x)) {
|
||||
Log.d(TAG, String.format("Display size changed from %s to %s", mRealSize, mTmpPoint1));
|
||||
killProcess();
|
||||
notifyChange();
|
||||
return;
|
||||
}
|
||||
|
||||
|
@ -105,22 +110,28 @@ public class ConfigMonitor extends BroadcastReceiver implements DisplayListener
|
|||
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));
|
||||
killProcess();
|
||||
notifyChange();
|
||||
}
|
||||
}
|
||||
|
||||
private void killProcess() {
|
||||
Log.d(TAG, "restarting launcher");
|
||||
try {
|
||||
mContext.unregisterReceiver(this);
|
||||
mContext.getSystemService(DisplayManager.class).unregisterDisplayListener(this);
|
||||
} catch (Exception e) {
|
||||
// We are going to die anyway, ignore any error die to race condition in registering.
|
||||
private synchronized void notifyChange() {
|
||||
if (mCallback != null) {
|
||||
Consumer<Context> callback = mCallback;
|
||||
mCallback = null;
|
||||
new MainThreadExecutor().execute(() -> callback.accept(mContext));
|
||||
}
|
||||
android.os.Process.killProcess(android.os.Process.myPid());
|
||||
}
|
||||
|
||||
private Display getDefaultDisplay(Context context) {
|
||||
return context.getSystemService(WindowManager.class).getDefaultDisplay();
|
||||
}
|
||||
|
||||
public void unregister() {
|
||||
try {
|
||||
mContext.unregisterReceiver(this);
|
||||
mContext.getSystemService(DisplayManager.class).unregisterDisplayListener(this);
|
||||
} catch (Exception e) {
|
||||
Log.e(TAG, "Failed to unregister config monitor", e);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -87,6 +87,10 @@ public abstract class SQLiteCacheHelper {
|
|||
mOpenHelper.clearDB(mOpenHelper.getWritableDatabase());
|
||||
}
|
||||
|
||||
public void close() {
|
||||
mOpenHelper.close();
|
||||
}
|
||||
|
||||
protected abstract void onCreateTable(SQLiteDatabase db);
|
||||
|
||||
/**
|
||||
|
|
Loading…
Reference in New Issue