mirror of
https://github.com/LawnchairLauncher/lawnchair.git
synced 2026-03-04 18:06:48 +00:00
Moving some initializations to the background thread
HandlerThread.getLooper blocks until the thread is ready. Instead moving all looper dependency to the new thread itself. Change-Id: I240e8c56b855a991433a7fe93875059e6dab146b
This commit is contained in:
@@ -18,8 +18,7 @@ package com.android.launcher3.model;
|
||||
|
||||
import static android.content.ContentResolver.SCHEME_CONTENT;
|
||||
|
||||
import static com.android.launcher3.util.Executors.MAIN_EXECUTOR;
|
||||
import static com.android.launcher3.util.Executors.createAndStartNewLooper;
|
||||
import static com.android.launcher3.Utilities.newContentObserver;
|
||||
|
||||
import android.annotation.TargetApi;
|
||||
import android.app.RemoteAction;
|
||||
@@ -35,7 +34,7 @@ import android.os.Build;
|
||||
import android.os.Bundle;
|
||||
import android.os.DeadObjectException;
|
||||
import android.os.Handler;
|
||||
import android.os.Message;
|
||||
import android.os.Looper;
|
||||
import android.os.Process;
|
||||
import android.os.UserHandle;
|
||||
import android.text.TextUtils;
|
||||
@@ -43,7 +42,7 @@ import android.util.ArrayMap;
|
||||
import android.util.Log;
|
||||
|
||||
import androidx.annotation.MainThread;
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.annotation.Nullable;
|
||||
import androidx.annotation.WorkerThread;
|
||||
|
||||
import com.android.launcher3.BaseDraggingActivity;
|
||||
@@ -55,6 +54,7 @@ import com.android.launcher3.config.FeatureFlags;
|
||||
import com.android.launcher3.model.data.ItemInfo;
|
||||
import com.android.launcher3.popup.RemoteActionShortcut;
|
||||
import com.android.launcher3.popup.SystemShortcut;
|
||||
import com.android.launcher3.util.BgObjectWithLooper;
|
||||
import com.android.launcher3.util.MainThreadInitializedObject;
|
||||
import com.android.launcher3.util.PackageManagerHelper;
|
||||
import com.android.launcher3.util.Preconditions;
|
||||
@@ -68,15 +68,11 @@ import java.util.Map;
|
||||
* Data model for digital wellbeing status of apps.
|
||||
*/
|
||||
@TargetApi(Build.VERSION_CODES.Q)
|
||||
public final class WellbeingModel {
|
||||
public final class WellbeingModel extends BgObjectWithLooper {
|
||||
private static final String TAG = "WellbeingModel";
|
||||
private static final int[] RETRY_TIMES_MS = {5000, 15000, 30000};
|
||||
private static final boolean DEBUG = false;
|
||||
|
||||
private static final int MSG_PACKAGE_ADDED = 1;
|
||||
private static final int MSG_PACKAGE_REMOVED = 2;
|
||||
private static final int MSG_FULL_REFRESH = 3;
|
||||
|
||||
private static final int UNKNOWN_MINIMAL_DEVICE_STATE = 0;
|
||||
private static final int IN_MINIMAL_DEVICE = 2;
|
||||
|
||||
@@ -98,9 +94,9 @@ public final class WellbeingModel {
|
||||
|
||||
private final Context mContext;
|
||||
private final String mWellbeingProviderPkg;
|
||||
private final Handler mWorkerHandler;
|
||||
|
||||
private final ContentObserver mContentObserver;
|
||||
private Handler mWorkerHandler;
|
||||
private ContentObserver mContentObserver;
|
||||
|
||||
private final Object mModelLock = new Object();
|
||||
// Maps the action Id to the corresponding RemoteAction
|
||||
@@ -111,60 +107,67 @@ public final class WellbeingModel {
|
||||
|
||||
private WellbeingModel(final Context context) {
|
||||
mContext = context;
|
||||
mWorkerHandler =
|
||||
new Handler(createAndStartNewLooper("WellbeingHandler"), this::handleMessage);
|
||||
|
||||
mWellbeingProviderPkg = mContext.getString(R.string.wellbeing_provider_pkg);
|
||||
mContentObserver = new ContentObserver(MAIN_EXECUTOR.getHandler()) {
|
||||
@Override
|
||||
public void onChange(boolean selfChange, Uri uri) {
|
||||
if (DEBUG || mIsInTest) {
|
||||
Log.d(TAG, "ContentObserver.onChange() called with: selfChange = ["
|
||||
+ selfChange + "], uri = [" + uri + "]");
|
||||
}
|
||||
Preconditions.assertUIThread();
|
||||
|
||||
if (uri.getPath().contains(PATH_ACTIONS)) {
|
||||
// Wellbeing reports that app actions have changed.
|
||||
updateWellbeingData();
|
||||
} else if (uri.getPath().contains(PATH_MINIMAL_DEVICE)) {
|
||||
// Wellbeing reports that minimal device state or config is changed.
|
||||
updateLauncherModel(context);
|
||||
}
|
||||
}
|
||||
};
|
||||
FeatureFlags.ENABLE_MINIMAL_DEVICE.addChangeListener(mContext, () ->
|
||||
updateLauncherModel(context));
|
||||
initializeInBackground("WellbeingHandler");
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onInitialized(Looper looper) {
|
||||
mWorkerHandler = new Handler(looper);
|
||||
mContentObserver = newContentObserver(mWorkerHandler, this::onWellbeingUriChanged);
|
||||
if (!TextUtils.isEmpty(mWellbeingProviderPkg)) {
|
||||
context.registerReceiver(
|
||||
new SimpleBroadcastReceiver(this::onWellbeingProviderChanged),
|
||||
mContext.registerReceiver(
|
||||
new SimpleBroadcastReceiver(t -> restartObserver()),
|
||||
PackageManagerHelper.getPackageFilter(mWellbeingProviderPkg,
|
||||
Intent.ACTION_PACKAGE_ADDED, Intent.ACTION_PACKAGE_CHANGED,
|
||||
Intent.ACTION_PACKAGE_REMOVED, Intent.ACTION_PACKAGE_DATA_CLEARED,
|
||||
Intent.ACTION_PACKAGE_RESTARTED));
|
||||
Intent.ACTION_PACKAGE_RESTARTED),
|
||||
null, mWorkerHandler);
|
||||
|
||||
IntentFilter filter = new IntentFilter(Intent.ACTION_PACKAGE_ADDED);
|
||||
filter.addAction(Intent.ACTION_PACKAGE_REMOVED);
|
||||
filter.addDataScheme("package");
|
||||
context.registerReceiver(new SimpleBroadcastReceiver(this::onAppPackageChanged),
|
||||
filter);
|
||||
mContext.registerReceiver(new SimpleBroadcastReceiver(this::onAppPackageChanged),
|
||||
filter, null, mWorkerHandler);
|
||||
|
||||
restartObserver();
|
||||
}
|
||||
}
|
||||
|
||||
@WorkerThread
|
||||
private void onWellbeingUriChanged(Uri uri) {
|
||||
Preconditions.assertNonUiThread();
|
||||
if (DEBUG || mIsInTest) {
|
||||
Log.d(TAG, "ContentObserver.onChange() called with: uri = [" + uri + "]");
|
||||
}
|
||||
if (uri.getPath().contains(PATH_ACTIONS)) {
|
||||
// Wellbeing reports that app actions have changed.
|
||||
updateAllPackages();
|
||||
} else if (uri.getPath().contains(PATH_MINIMAL_DEVICE)) {
|
||||
// Wellbeing reports that minimal device state or config is changed.
|
||||
if (!FeatureFlags.ENABLE_MINIMAL_DEVICE.get()) {
|
||||
return;
|
||||
}
|
||||
final Bundle extras = new Bundle();
|
||||
String dbFile;
|
||||
if (isInMinimalDeviceMode()) {
|
||||
dbFile = DB_NAME_MINIMAL_DEVICE;
|
||||
extras.putString(LauncherProvider.KEY_LAYOUT_PROVIDER_AUTHORITY,
|
||||
mWellbeingProviderPkg + ".api");
|
||||
} else {
|
||||
dbFile = InvariantDeviceProfile.INSTANCE.get(mContext).dbFile;
|
||||
}
|
||||
LauncherSettings.Settings.call(mContext.getContentResolver(),
|
||||
LauncherSettings.Settings.METHOD_SWITCH_DATABASE,
|
||||
dbFile, extras);
|
||||
}
|
||||
}
|
||||
|
||||
public void setInTest(boolean inTest) {
|
||||
mIsInTest = inTest;
|
||||
}
|
||||
|
||||
protected void onWellbeingProviderChanged(Intent intent) {
|
||||
if (DEBUG || mIsInTest) {
|
||||
Log.d(TAG, "Changes to Wellbeing package: intent = [" + intent + "]");
|
||||
}
|
||||
restartObserver();
|
||||
}
|
||||
|
||||
@WorkerThread
|
||||
private void restartObserver() {
|
||||
final ContentResolver resolver = mContext.getContentResolver();
|
||||
resolver.unregisterContentObserver(mContentObserver);
|
||||
@@ -179,7 +182,7 @@ public final class WellbeingModel {
|
||||
Log.e(TAG, "Failed to register content observer for " + actionsUri + ": " + e);
|
||||
if (mIsInTest) throw new RuntimeException(e);
|
||||
}
|
||||
updateWellbeingData();
|
||||
updateAllPackages();
|
||||
}
|
||||
|
||||
@MainThread
|
||||
@@ -212,39 +215,6 @@ public final class WellbeingModel {
|
||||
}
|
||||
}
|
||||
|
||||
private void updateWellbeingData() {
|
||||
mWorkerHandler.sendEmptyMessage(MSG_FULL_REFRESH);
|
||||
}
|
||||
|
||||
private void updateLauncherModel(@NonNull final Context context) {
|
||||
if (!FeatureFlags.ENABLE_MINIMAL_DEVICE.get()) {
|
||||
reloadLauncherInNormalMode(context);
|
||||
return;
|
||||
}
|
||||
mWorkerHandler.post(() -> {
|
||||
if (isInMinimalDeviceMode()) {
|
||||
reloadLauncherInMinimalMode(context);
|
||||
} else {
|
||||
reloadLauncherInNormalMode(context);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
private void reloadLauncherInNormalMode(@NonNull final Context context) {
|
||||
LauncherSettings.Settings.call(context.getContentResolver(),
|
||||
LauncherSettings.Settings.METHOD_SWITCH_DATABASE,
|
||||
InvariantDeviceProfile.INSTANCE.get(context).dbFile);
|
||||
}
|
||||
|
||||
private void reloadLauncherInMinimalMode(@NonNull final Context context) {
|
||||
final Bundle extras = new Bundle();
|
||||
extras.putString(LauncherProvider.KEY_LAYOUT_PROVIDER_AUTHORITY,
|
||||
mWellbeingProviderPkg + ".api");
|
||||
LauncherSettings.Settings.call(context.getContentResolver(),
|
||||
LauncherSettings.Settings.METHOD_SWITCH_DATABASE,
|
||||
DB_NAME_MINIMAL_DEVICE, extras);
|
||||
}
|
||||
|
||||
private Uri.Builder apiBuilder() {
|
||||
return new Uri.Builder()
|
||||
.scheme(SCHEME_CONTENT)
|
||||
@@ -277,7 +247,8 @@ public final class WellbeingModel {
|
||||
return false;
|
||||
}
|
||||
|
||||
private boolean updateActions(String... packageNames) {
|
||||
@WorkerThread
|
||||
private boolean updateActions(String[] packageNames) {
|
||||
if (packageNames.length == 0) {
|
||||
return true;
|
||||
}
|
||||
@@ -340,68 +311,51 @@ public final class WellbeingModel {
|
||||
return true;
|
||||
}
|
||||
|
||||
private boolean handleMessage(Message msg) {
|
||||
switch (msg.what) {
|
||||
case MSG_PACKAGE_REMOVED: {
|
||||
String packageName = (String) msg.obj;
|
||||
mWorkerHandler.removeCallbacksAndMessages(packageName);
|
||||
synchronized (mModelLock) {
|
||||
mPackageToActionId.remove(packageName);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
case MSG_PACKAGE_ADDED: {
|
||||
String packageName = (String) msg.obj;
|
||||
mWorkerHandler.removeCallbacksAndMessages(packageName);
|
||||
if (!updateActions(packageName)) {
|
||||
scheduleRefreshRetry(msg);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
@WorkerThread
|
||||
private void updateActionsWithRetry(int retryCount, @Nullable String packageName) {
|
||||
String[] packageNames = TextUtils.isEmpty(packageName)
|
||||
? mContext.getSystemService(LauncherApps.class)
|
||||
.getActivityList(null, Process.myUserHandle()).stream()
|
||||
.map(li -> li.getApplicationInfo().packageName).distinct()
|
||||
.toArray(String[]::new)
|
||||
: new String[] { packageName };
|
||||
|
||||
case MSG_FULL_REFRESH: {
|
||||
// Remove all existing messages
|
||||
mWorkerHandler.removeCallbacksAndMessages(null);
|
||||
final String[] packageNames = mContext.getSystemService(LauncherApps.class)
|
||||
.getActivityList(null, Process.myUserHandle()).stream()
|
||||
.map(li -> li.getApplicationInfo().packageName).distinct()
|
||||
.toArray(String[]::new);
|
||||
if (!updateActions(packageNames)) {
|
||||
scheduleRefreshRetry(msg);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
mWorkerHandler.removeCallbacksAndMessages(packageName);
|
||||
if (updateActions(packageNames)) {
|
||||
return;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
private void scheduleRefreshRetry(Message originalMsg) {
|
||||
int retryCount = originalMsg.arg1;
|
||||
if (retryCount >= RETRY_TIMES_MS.length) {
|
||||
// To many retries, skip
|
||||
return;
|
||||
}
|
||||
|
||||
Message msg = Message.obtain(originalMsg);
|
||||
msg.arg1 = retryCount + 1;
|
||||
mWorkerHandler.sendMessageDelayed(msg, RETRY_TIMES_MS[retryCount]);
|
||||
mWorkerHandler.postDelayed(
|
||||
() -> updateActionsWithRetry(retryCount + 1, packageName),
|
||||
packageName, RETRY_TIMES_MS[retryCount]);
|
||||
}
|
||||
|
||||
@WorkerThread
|
||||
private void updateAllPackages() {
|
||||
updateActionsWithRetry(0, null);
|
||||
}
|
||||
|
||||
@WorkerThread
|
||||
private void onAppPackageChanged(Intent intent) {
|
||||
if (DEBUG || mIsInTest) Log.d(TAG, "Changes in apps: intent = [" + intent + "]");
|
||||
Preconditions.assertUIThread();
|
||||
Preconditions.assertNonUiThread();
|
||||
|
||||
final String packageName = intent.getData().getSchemeSpecificPart();
|
||||
if (packageName == null || packageName.length() == 0) {
|
||||
// they sent us a bad intent
|
||||
return;
|
||||
}
|
||||
|
||||
final String action = intent.getAction();
|
||||
if (Intent.ACTION_PACKAGE_REMOVED.equals(action)) {
|
||||
Message.obtain(mWorkerHandler, MSG_PACKAGE_REMOVED, packageName).sendToTarget();
|
||||
mWorkerHandler.removeCallbacksAndMessages(packageName);
|
||||
synchronized (mModelLock) {
|
||||
mPackageToActionId.remove(packageName);
|
||||
}
|
||||
} else if (Intent.ACTION_PACKAGE_ADDED.equals(action)) {
|
||||
Message.obtain(mWorkerHandler, MSG_PACKAGE_ADDED, packageName).sendToTarget();
|
||||
updateActionsWithRetry(0, packageName);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user