diff --git a/Android.mk b/Android.mk index 9d113d9547..985612fbfb 100644 --- a/Android.mk +++ b/Android.mk @@ -23,12 +23,7 @@ include $(CLEAR_VARS) LOCAL_USE_AAPT2 := true LOCAL_AAPT2_ONLY := true LOCAL_MODULE_TAGS := optional - -ifneq (,$(wildcard frameworks/base)) - LOCAL_STATIC_JAVA_LIBRARIES:= PluginCoreLib -else - LOCAL_STATIC_JAVA_LIBRARIES:= libPluginCore -endif +LOCAL_STATIC_JAVA_LIBRARIES:= PluginCoreLib LOCAL_SRC_FILES := \ $(call all-java-files-under, src_plugins) @@ -89,8 +84,7 @@ LOCAL_STATIC_ANDROID_LIBRARIES := \ LOCAL_SRC_FILES := \ $(call all-java-files-under, src) \ $(call all-java-files-under, src_shortcuts_overrides) \ - $(call all-java-files-under, src_ui_overrides) \ - $(call all-java-files-under, src_flags) + $(call all-java-files-under, src_ui_overrides) LOCAL_PROGUARD_FLAG_FILES := proguard.flags # Proguard is disable for testing. Derivarive prjects to keep proguard enabled @@ -151,11 +145,10 @@ LOCAL_USE_AAPT2 := true LOCAL_AAPT2_ONLY := true LOCAL_MODULE_TAGS := optional +LOCAL_STATIC_JAVA_LIBRARIES := SystemUISharedLib launcherprotosnano ifneq (,$(wildcard frameworks/base)) - LOCAL_STATIC_JAVA_LIBRARIES := SystemUISharedLib launcherprotosnano LOCAL_PRIVATE_PLATFORM_APIS := true else - LOCAL_STATIC_JAVA_LIBRARIES := libSharedSystemUI libLauncherProtos LOCAL_SDK_VERSION := system_current LOCAL_MIN_SDK_VERSION := 26 endif @@ -169,7 +162,6 @@ LOCAL_SRC_FILES := \ $(call all-java-files-under, src) \ $(call all-java-files-under, quickstep/src) \ $(call all-java-files-under, quickstep/recents_ui_overrides/src) \ - $(call all-java-files-under, src_flags) \ $(call all-java-files-under, src_shortcuts_overrides) LOCAL_RESOURCE_DIR := \ @@ -224,11 +216,10 @@ include $(CLEAR_VARS) LOCAL_USE_AAPT2 := true LOCAL_MODULE_TAGS := optional +LOCAL_STATIC_JAVA_LIBRARIES := SystemUISharedLib launcherprotosnano ifneq (,$(wildcard frameworks/base)) - LOCAL_STATIC_JAVA_LIBRARIES := SystemUISharedLib launcherprotosnano LOCAL_PRIVATE_PLATFORM_APIS := true else - LOCAL_STATIC_JAVA_LIBRARIES := libSharedSystemUI libLauncherProtos LOCAL_SDK_VERSION := system_current LOCAL_MIN_SDK_VERSION := 26 endif @@ -271,11 +262,10 @@ include $(CLEAR_VARS) LOCAL_USE_AAPT2 := true LOCAL_MODULE_TAGS := optional +LOCAL_STATIC_JAVA_LIBRARIES := SystemUISharedLib launcherprotosnano ifneq (,$(wildcard frameworks/base)) - LOCAL_STATIC_JAVA_LIBRARIES := SystemUISharedLib launcherprotosnano LOCAL_PRIVATE_PLATFORM_APIS := true else - LOCAL_STATIC_JAVA_LIBRARIES := libSharedSystemUI libLauncherProtos LOCAL_SDK_VERSION := system_current LOCAL_MIN_SDK_VERSION := 26 endif diff --git a/PREUPLOAD.cfg b/PREUPLOAD.cfg new file mode 100644 index 0000000000..f3db20e3b6 --- /dev/null +++ b/PREUPLOAD.cfg @@ -0,0 +1,2 @@ +[Hook Scripts] +checkstyle_hook = ${REPO_ROOT}/prebuilts/checkstyle/checkstyle.py --sha ${PREUPLOAD_COMMIT} diff --git a/go/quickstep/src/com/android/launcher3/uioverrides/RecentsUiFactory.java b/go/quickstep/src/com/android/launcher3/uioverrides/RecentsUiFactory.java index cbc77d2720..ae8fd82241 100644 --- a/go/quickstep/src/com/android/launcher3/uioverrides/RecentsUiFactory.java +++ b/go/quickstep/src/com/android/launcher3/uioverrides/RecentsUiFactory.java @@ -20,7 +20,6 @@ import com.android.launcher3.DeviceProfile; import com.android.launcher3.Launcher; import com.android.launcher3.LauncherStateManager.StateHandler; import com.android.launcher3.Utilities; -import com.android.launcher3.config.FeatureFlags; import com.android.launcher3.graphics.RotationMode; import com.android.launcher3.uioverrides.touchcontrollers.LandscapeEdgeSwipeController; import com.android.launcher3.uioverrides.touchcontrollers.LandscapeStatesTouchController; @@ -51,7 +50,7 @@ public abstract class RecentsUiFactory { .getMode().hasGestures; list.add(new PortraitStatesTouchController(launcher, allowDragToOverview)); } - if (FeatureFlags.PULL_DOWN_STATUS_BAR && Utilities.IS_DEBUG_DEVICE + if (Utilities.IS_DEBUG_DEVICE && !launcher.getDeviceProfile().isMultiWindowMode && !launcher.getDeviceProfile().isVerticalBarLayout()) { list.add(new StatusBarTouchController(launcher)); diff --git a/go/quickstep/src/com/android/quickstep/OverviewCommandHelper.java b/go/quickstep/src/com/android/quickstep/OverviewCommandHelper.java index 0fa3d866f6..216972cba2 100644 --- a/go/quickstep/src/com/android/quickstep/OverviewCommandHelper.java +++ b/go/quickstep/src/com/android/quickstep/OverviewCommandHelper.java @@ -15,8 +15,8 @@ */ package com.android.quickstep; -import static com.android.systemui.shared.system.ActivityManagerWrapper - .CLOSE_SYSTEM_WINDOWS_REASON_RECENTS; +import static com.android.launcher3.util.Executors.MAIN_EXECUTOR; +import static com.android.systemui.shared.system.ActivityManagerWrapper.CLOSE_SYSTEM_WINDOWS_REASON_RECENTS; import android.annotation.TargetApi; import android.content.Context; @@ -25,7 +25,6 @@ import android.os.SystemClock; import android.view.ViewConfiguration; import com.android.launcher3.BaseDraggingActivity; -import com.android.launcher3.MainThreadExecutor; import com.android.launcher3.logging.UserEventDispatcher; import com.android.launcher3.userevent.nano.LauncherLogProto; import com.android.quickstep.ActivityControlHelper.ActivityInitListener; @@ -43,7 +42,6 @@ public class OverviewCommandHelper { private final Context mContext; private final ActivityManagerWrapper mAM; private final RecentsModel mRecentsModel; - private final MainThreadExecutor mMainThreadExecutor; private final OverviewComponentObserver mOverviewComponentObserver; private long mLastToggleTime; @@ -51,7 +49,6 @@ public class OverviewCommandHelper { public OverviewCommandHelper(Context context, OverviewComponentObserver observer) { mContext = context; mAM = ActivityManagerWrapper.getInstance(); - mMainThreadExecutor = new MainThreadExecutor(); mRecentsModel = RecentsModel.INSTANCE.get(mContext); mOverviewComponentObserver = observer; } @@ -63,19 +60,19 @@ public class OverviewCommandHelper { } mAM.closeSystemWindows(CLOSE_SYSTEM_WINDOWS_REASON_RECENTS); - mMainThreadExecutor.execute(new RecentsActivityCommand<>()); + MAIN_EXECUTOR.execute(new RecentsActivityCommand<>()); } public void onOverviewShown(boolean triggeredFromAltTab) { - mMainThreadExecutor.execute(new ShowRecentsCommand()); + MAIN_EXECUTOR.execute(new ShowRecentsCommand()); } public void onOverviewHidden() { - mMainThreadExecutor.execute(new HideRecentsCommand()); + MAIN_EXECUTOR.execute(new HideRecentsCommand()); } public void onTip(int actionType, int viewType) { - mMainThreadExecutor.execute(() -> + MAIN_EXECUTOR.execute(() -> UserEventDispatcher.newInstance(mContext).logActionTip(actionType, viewType)); } @@ -161,7 +158,7 @@ public class OverviewCommandHelper { // Otherwise, start overview. mListener = mHelper.createActivityInitListener(provider::onActivityReady); mListener.registerAndStartActivity(mOverviewComponentObserver.getOverviewIntent(), - provider, mContext, mMainThreadExecutor.getHandler(), + provider, mContext, MAIN_EXECUTOR.getHandler(), provider.getRecentsLaunchDuration()); } diff --git a/go/quickstep/src/com/android/quickstep/TouchInteractionService.java b/go/quickstep/src/com/android/quickstep/TouchInteractionService.java index 577b175661..19dd82f7f7 100644 --- a/go/quickstep/src/com/android/quickstep/TouchInteractionService.java +++ b/go/quickstep/src/com/android/quickstep/TouchInteractionService.java @@ -34,8 +34,6 @@ import android.view.MotionEvent; import com.android.launcher3.Utilities; import com.android.launcher3.compat.UserManagerCompat; -import com.android.launcher3.util.LooperExecutor; -import com.android.launcher3.util.UiThreadHelper; import com.android.systemui.shared.recents.IOverviewProxy; import com.android.systemui.shared.recents.ISystemUiProxy; @@ -139,9 +137,6 @@ public class TouchInteractionService extends Service { return sConnected; } - public static final LooperExecutor BACKGROUND_EXECUTOR = - new LooperExecutor(UiThreadHelper.getBackgroundLooper()); - private RecentsModel mRecentsModel; private OverviewComponentObserver mOverviewComponentObserver; private OverviewCommandHelper mOverviewCommandHelper; diff --git a/go/src/com/android/launcher3/config/FeatureFlags.java b/go/src/com/android/launcher3/config/FeatureFlags.java deleted file mode 100644 index a90808c1e3..0000000000 --- a/go/src/com/android/launcher3/config/FeatureFlags.java +++ /dev/null @@ -1,32 +0,0 @@ -/* - * Copyright (C) 2017 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.config; - -import android.content.Context; - -/** - * Defines a set of flags used to control various launcher behaviors - */ -public final class FeatureFlags extends BaseFlags { - private FeatureFlags() { - // Prevent instantiation - } - - // Features to control Launcher3Go behavior - public static final boolean GO_DISABLE_WIDGETS = true; - public static final boolean LAUNCHER3_SPRING_ICONS = false; -} diff --git a/go/src/com/android/launcher3/model/LoaderResults.java b/go/src/com/android/launcher3/model/LoaderResults.java index b82f362496..26c3313185 100644 --- a/go/src/com/android/launcher3/model/LoaderResults.java +++ b/go/src/com/android/launcher3/model/LoaderResults.java @@ -16,9 +16,8 @@ package com.android.launcher3.model; -import com.android.launcher3.AllAppsList; import com.android.launcher3.LauncherAppState; -import com.android.launcher3.LauncherModel.Callbacks; +import com.android.launcher3.model.BgDataModel.Callbacks; import java.lang.ref.WeakReference; diff --git a/go/src/com/android/launcher3/model/WidgetsModel.java b/go/src/com/android/launcher3/model/WidgetsModel.java index 18f3f9d91c..7b8f4e6c33 100644 --- a/go/src/com/android/launcher3/model/WidgetsModel.java +++ b/go/src/com/android/launcher3/model/WidgetsModel.java @@ -37,6 +37,10 @@ import androidx.annotation.Nullable; *

The widgets and shortcuts are organized using package name as its index. */ public class WidgetsModel { + + // True is the widget support is disabled. + public static final boolean GO_DISABLE_WIDGETS = false; + private static final ArrayList EMPTY_WIDGET_LIST = new ArrayList<>(); /** diff --git a/go/src/com/android/launcher3/shortcuts/DeepShortcutManager.java b/go/src/com/android/launcher3/shortcuts/DeepShortcutManager.java index ee113dfebf..42b1194292 100644 --- a/go/src/com/android/launcher3/shortcuts/DeepShortcutManager.java +++ b/go/src/com/android/launcher3/shortcuts/DeepShortcutManager.java @@ -27,34 +27,23 @@ import android.os.UserHandle; import com.android.launcher3.ItemInfo; import com.android.launcher3.notification.NotificationKeyData; -import java.util.Collections; +import java.util.ArrayList; import java.util.List; /** * Performs operations related to deep shortcuts, such as querying for them, pinning them, etc. */ public class DeepShortcutManager { - private static DeepShortcutManager sInstance; - private static final Object sInstanceLock = new Object(); + + private static final DeepShortcutManager sInstance = new DeepShortcutManager(); public static DeepShortcutManager getInstance(Context context) { - synchronized (sInstanceLock) { - if (sInstance == null) { - sInstance = new DeepShortcutManager(context.getApplicationContext()); - } - return sInstance; - } + return sInstance; } - private DeepShortcutManager(Context context) { - } + private final QueryResult mFailure = new QueryResult(); - public boolean wasLastCallSuccess() { - return false; - } - - public void onShortcutsChanged(List shortcuts) { - } + private DeepShortcutManager() { } /** * Queries for the shortcuts with the package name and provided ids. @@ -62,18 +51,18 @@ public class DeepShortcutManager { * This method is intended to get the full details for shortcuts when they are added or updated, * because we only get "key" fields in onShortcutsChanged(). */ - public List queryForFullDetails(String packageName, + public QueryResult queryForFullDetails(String packageName, List shortcutIds, UserHandle user) { - return Collections.emptyList(); + return mFailure; } /** * Gets all the manifest and dynamic shortcuts associated with the given package and user, * to be displayed in the shortcuts container on long press. */ - public List queryForShortcutsContainer(ComponentName activity, + public QueryResult queryForShortcutsContainer(ComponentName activity, UserHandle user) { - return Collections.emptyList(); + return mFailure; } /** @@ -103,20 +92,28 @@ public class DeepShortcutManager { * * If packageName is null, returns all pinned shortcuts regardless of package. */ - public List queryForPinnedShortcuts(String packageName, UserHandle user) { - return Collections.emptyList(); + public QueryResult queryForPinnedShortcuts(String packageName, UserHandle user) { + return mFailure; } - public List queryForPinnedShortcuts(String packageName, + public QueryResult queryForPinnedShortcuts(String packageName, List shortcutIds, UserHandle user) { - return Collections.emptyList(); + return mFailure; } - public List queryForAllShortcuts(UserHandle user) { - return Collections.emptyList(); + public QueryResult queryForAllShortcuts(UserHandle user) { + return mFailure; } public boolean hasHostPermission() { return false; } + + + public static class QueryResult extends ArrayList { + + public boolean wasSuccess() { + return true; + } + } } diff --git a/gradle.properties b/gradle.properties index 5b90f08148..a77f52a22f 100644 --- a/gradle.properties +++ b/gradle.properties @@ -10,4 +10,4 @@ PROTOBUF_CLASS_PATH=com.google.protobuf:protobuf-gradle-plugin:0.8.6 PROTOBUF_DEPENDENCY=com.google.protobuf.nano:protobuf-javanano:3.0.0-alpha-7 BUILD_TOOLS_VERSION=28.0.3 -COMPILE_SDK=android-Q \ No newline at end of file +COMPILE_SDK=android-R diff --git a/iconloaderlib/src/com/android/launcher3/icons/cache/BaseIconCache.java b/iconloaderlib/src/com/android/launcher3/icons/cache/BaseIconCache.java index 36d1c3eccc..c100f05963 100644 --- a/iconloaderlib/src/com/android/launcher3/icons/cache/BaseIconCache.java +++ b/iconloaderlib/src/com/android/launcher3/icons/cache/BaseIconCache.java @@ -267,6 +267,10 @@ public abstract class BaseIconCache { entry = new CacheEntry(); cachingLogic.loadIcon(mContext, object, entry); } + // Icon can't be loaded from cachingLogic, which implies alternative icon was loaded + // (e.g. fallback icon, default icon). So we drop here since there's no point in caching + // an empty entry. + if (entry.icon == null) return; entry.title = cachingLogic.getLabel(object); entry.contentDescription = mPackageManager.getUserBadgedLabel(entry.title, user); mCache.put(key, entry); diff --git a/iconloaderlib/src/com/android/launcher3/icons/cache/CachingLogic.java b/iconloaderlib/src/com/android/launcher3/icons/cache/CachingLogic.java index 09f59b84c2..16bc7ae834 100644 --- a/iconloaderlib/src/com/android/launcher3/icons/cache/CachingLogic.java +++ b/iconloaderlib/src/com/android/launcher3/icons/cache/CachingLogic.java @@ -17,6 +17,7 @@ package com.android.launcher3.icons.cache; import android.content.ComponentName; import android.content.Context; +import android.content.pm.PackageInfo; import android.os.LocaleList; import android.os.UserHandle; @@ -41,4 +42,11 @@ public interface CachingLogic { default String getKeywords(T object, LocaleList localeList) { return null; } + + /** + * Returns the timestamp the entry was last updated in cache. + */ + default long getLastUpdatedTime(T object, PackageInfo info) { + return info.lastUpdateTime; + } } diff --git a/iconloaderlib/src/com/android/launcher3/icons/cache/IconCacheUpdateHandler.java b/iconloaderlib/src/com/android/launcher3/icons/cache/IconCacheUpdateHandler.java index 3c71bd0274..bcdbce5e29 100644 --- a/iconloaderlib/src/com/android/launcher3/icons/cache/IconCacheUpdateHandler.java +++ b/iconloaderlib/src/com/android/launcher3/icons/cache/IconCacheUpdateHandler.java @@ -171,8 +171,9 @@ public class IconCacheUpdateHandler { long updateTime = c.getLong(indexLastUpdate); int version = c.getInt(indexVersion); T app = componentMap.remove(component); - if (version == info.versionCode && updateTime == info.lastUpdateTime && - TextUtils.equals(c.getString(systemStateIndex), + if (version == info.versionCode + && updateTime == cachingLogic.getLastUpdatedTime(app, info) + && TextUtils.equals(c.getString(systemStateIndex), mIconCache.getIconSystemState(info.packageName))) { if (mFilterMode == MODE_CLEAR_VALID_ITEMS) { @@ -231,7 +232,6 @@ public class IconCacheUpdateHandler { } } - /** * A runnable that updates invalid icons and adds missing icons in the DB for the provided * LauncherActivityInfo list. Items are updated/added one at a time, so that the diff --git a/protos/launcher_log.proto b/protos/launcher_log.proto index 49fd43617e..055ade58eb 100644 --- a/protos/launcher_log.proto +++ b/protos/launcher_log.proto @@ -118,6 +118,7 @@ enum ControlType { APP_USAGE_SETTINGS = 18; BACK_GESTURE = 19; UNDO = 20; + DISMISS_PREDICTION = 21; } enum TipType { diff --git a/quickstep/recents_ui_overrides/src/com/android/launcher3/appprediction/DynamicItemCache.java b/quickstep/recents_ui_overrides/src/com/android/launcher3/appprediction/DynamicItemCache.java index 4ecc39cf6a..65e69b6046 100644 --- a/quickstep/recents_ui_overrides/src/com/android/launcher3/appprediction/DynamicItemCache.java +++ b/quickstep/recents_ui_overrides/src/com/android/launcher3/appprediction/DynamicItemCache.java @@ -17,6 +17,8 @@ package com.android.launcher3.appprediction; import static android.content.pm.PackageManager.MATCH_INSTANT; +import static com.android.launcher3.util.Executors.MODEL_EXECUTOR; + import android.content.Context; import android.content.Intent; import android.content.pm.ApplicationInfo; @@ -30,8 +32,12 @@ import android.os.Message; import android.util.ArrayMap; import android.util.Log; +import androidx.annotation.MainThread; +import androidx.annotation.Nullable; +import androidx.annotation.UiThread; +import androidx.annotation.WorkerThread; + import com.android.launcher3.LauncherAppState; -import com.android.launcher3.LauncherModel; import com.android.launcher3.WorkspaceItemInfo; import com.android.launcher3.icons.IconCache; import com.android.launcher3.icons.LauncherIcons; @@ -45,11 +51,6 @@ import java.util.HashMap; import java.util.List; import java.util.Map; -import androidx.annotation.MainThread; -import androidx.annotation.Nullable; -import androidx.annotation.UiThread; -import androidx.annotation.WorkerThread; - /** * Utility class which loads and caches predicted items like instant apps and shortcuts, before * they can be displayed on the UI @@ -77,7 +78,7 @@ public class DynamicItemCache { public DynamicItemCache(Context context, Runnable onUpdateCallback) { mContext = context; - mWorker = new Handler(LauncherModel.getWorkerLooper(), this::handleWorkerMessage); + mWorker = new Handler(MODEL_EXECUTOR.getLooper(), this::handleWorkerMessage); mUiHandler = new Handler(Looper.getMainLooper(), this::handleUiMessage); mInstantAppResolver = InstantAppResolver.newInstance(context); mOnUpdateCallback = onUpdateCallback; diff --git a/quickstep/recents_ui_overrides/src/com/android/launcher3/appprediction/PredictionAppTracker.java b/quickstep/recents_ui_overrides/src/com/android/launcher3/appprediction/PredictionAppTracker.java index 24fc61bef9..4c7943b10b 100644 --- a/quickstep/recents_ui_overrides/src/com/android/launcher3/appprediction/PredictionAppTracker.java +++ b/quickstep/recents_ui_overrides/src/com/android/launcher3/appprediction/PredictionAppTracker.java @@ -1,4 +1,4 @@ -/** +/* * Copyright (C) 2019 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); @@ -16,6 +16,7 @@ package com.android.launcher3.appprediction; import static com.android.launcher3.InvariantDeviceProfile.CHANGE_FLAG_GRID; +import static com.android.launcher3.util.Executors.UI_HELPER_EXECUTOR; import android.annotation.TargetApi; import android.app.prediction.AppPredictionContext; @@ -34,19 +35,25 @@ import android.os.UserHandle; import android.util.Log; import androidx.annotation.Nullable; +import androidx.annotation.UiThread; +import androidx.annotation.WorkerThread; + import com.android.launcher3.InvariantDeviceProfile; import com.android.launcher3.appprediction.PredictionUiStateManager.Client; import com.android.launcher3.model.AppLaunchTracker; -import com.android.launcher3.util.UiThreadHelper; +import com.android.launcher3.uioverrides.plugins.PluginManagerWrapper; +import com.android.systemui.plugins.AppLaunchEventsPlugin; +import com.android.systemui.plugins.PluginListener; -import androidx.annotation.UiThread; -import androidx.annotation.WorkerThread; +import java.util.ArrayList; +import java.util.List; /** * Subclass of app tracker which publishes the data to the prediction engine and gets back results. */ @TargetApi(Build.VERSION_CODES.Q) -public class PredictionAppTracker extends AppLaunchTracker { +public class PredictionAppTracker extends AppLaunchTracker + implements PluginListener { private static final String TAG = "PredictionAppTracker"; private static final boolean DBG = false; @@ -58,6 +65,7 @@ public class PredictionAppTracker extends AppLaunchTracker { protected final Context mContext; private final Handler mMessageHandler; + private final List mAppLaunchEventsPluginsList; // Accessed only on worker thread private AppPredictor mHomeAppPredictor; @@ -65,10 +73,14 @@ public class PredictionAppTracker extends AppLaunchTracker { public PredictionAppTracker(Context context) { mContext = context; - mMessageHandler = new Handler(UiThreadHelper.getBackgroundLooper(), this::handleMessage); + mMessageHandler = new Handler(UI_HELPER_EXECUTOR.getLooper(), this::handleMessage); InvariantDeviceProfile.INSTANCE.get(mContext).addOnChangeListener(this::onIdpChanged); mMessageHandler.sendEmptyMessage(MSG_INIT); + + mAppLaunchEventsPluginsList = new ArrayList<>(); + PluginManagerWrapper.INSTANCE.get(context) + .addPluginListener(this, AppLaunchEventsPlugin.class, true); } @UiThread @@ -96,7 +108,7 @@ public class PredictionAppTracker extends AppLaunchTracker { AppPredictionManager apm = mContext.getSystemService(AppPredictionManager.class); if (apm == null) { - return null; + return null; } AppPredictor predictor = apm.createAppPredictionSession( @@ -116,7 +128,7 @@ public class PredictionAppTracker extends AppLaunchTracker { */ @WorkerThread @Nullable - public Bundle getAppPredictionContextExtras(Client client){ + public Bundle getAppPredictionContextExtras(Client client) { return null; } @@ -128,7 +140,7 @@ public class PredictionAppTracker extends AppLaunchTracker { destroy(); // Initialize the clients - int count = InvariantDeviceProfile.INSTANCE.get(mContext).numColumns; + int count = InvariantDeviceProfile.INSTANCE.get(mContext).numAllAppsColumns; mHomeAppPredictor = createPredictor(Client.HOME, count); mRecentsOverviewPredictor = createPredictor(Client.OVERVIEW, count); return true; @@ -167,37 +179,98 @@ public class PredictionAppTracker extends AppLaunchTracker { if (DBG) { Log.d(TAG, String.format("Sent immediate message to update %s", client)); } + + // Relay onReturnedToHome to every plugin. + mAppLaunchEventsPluginsList.forEach(AppLaunchEventsPlugin::onReturnedToHome); } @Override @UiThread public void onStartShortcut(String packageName, String shortcutId, UserHandle user, - String container) { + String container) { // TODO: Use the full shortcut info - AppTarget target = new AppTarget - .Builder(new AppTargetId("shortcut:" + shortcutId), packageName, user) - .setClassName(shortcutId) - .build(); + AppTarget target = new AppTarget.Builder( + new AppTargetId("shortcut:" + shortcutId), packageName, user) + .setClassName(shortcutId) + .build(); + sendLaunch(target, container); + + // Relay onStartShortcut info to every connected plugin. + mAppLaunchEventsPluginsList + .forEach(plugin -> plugin.onStartShortcut( + packageName, + shortcutId, + user, + container != null ? container : CONTAINER_DEFAULT) + ); + } @Override @UiThread public void onStartApp(ComponentName cn, UserHandle user, String container) { if (cn != null) { - AppTarget target = new AppTarget - .Builder(new AppTargetId("app:" + cn), cn.getPackageName(), user) - .setClassName(cn.getClassName()) - .build(); + AppTarget target = new AppTarget.Builder( + new AppTargetId("app:" + cn), cn.getPackageName(), user) + .setClassName(cn.getClassName()) + .build(); sendLaunch(target, container); + + // Relay onStartApp to every connected plugin. + mAppLaunchEventsPluginsList + .forEach(plugin -> plugin.onStartApp( + cn, + user, + container != null ? container : CONTAINER_DEFAULT) + ); } } + @Override @UiThread - private void sendLaunch(AppTarget target, String container) { - AppTargetEvent event = new AppTargetEvent.Builder(target, AppTargetEvent.ACTION_LAUNCH) + public void onDismissApp(ComponentName cn, UserHandle user, String container) { + if (cn == null) return; + AppTarget target = new AppTarget.Builder( + new AppTargetId("app: " + cn), cn.getPackageName(), user) + .setClassName(cn.getClassName()) + .build(); + sendDismiss(target, container); + + // Relay onDismissApp to every connected plugin. + mAppLaunchEventsPluginsList + .forEach(plugin -> plugin.onDismissApp( + cn, + user, + container != null ? container : CONTAINER_DEFAULT) + ); + } + + @UiThread + private void sendEvent(AppTarget target, String container, int eventId) { + AppTargetEvent event = new AppTargetEvent.Builder(target, eventId) .setLaunchLocation(container == null ? CONTAINER_DEFAULT : container) .build(); Message.obtain(mMessageHandler, MSG_LAUNCH, event).sendToTarget(); } + + @UiThread + private void sendLaunch(AppTarget target, String container) { + sendEvent(target, container, AppTargetEvent.ACTION_LAUNCH); + } + + @UiThread + private void sendDismiss(AppTarget target, String container) { + sendEvent(target, container, AppTargetEvent.ACTION_DISMISS); + } + + @Override + public void onPluginConnected(AppLaunchEventsPlugin appLaunchEventsPlugin, Context context) { + mAppLaunchEventsPluginsList.add(appLaunchEventsPlugin); + } + + @Override + public void onPluginDisconnected(AppLaunchEventsPlugin appLaunchEventsPlugin) { + mAppLaunchEventsPluginsList.remove(appLaunchEventsPlugin); + } } diff --git a/quickstep/recents_ui_overrides/src/com/android/launcher3/appprediction/PredictionRowView.java b/quickstep/recents_ui_overrides/src/com/android/launcher3/appprediction/PredictionRowView.java index 0c7ba9c95d..23db5df2ed 100644 --- a/quickstep/recents_ui_overrides/src/com/android/launcher3/appprediction/PredictionRowView.java +++ b/quickstep/recents_ui_overrides/src/com/android/launcher3/appprediction/PredictionRowView.java @@ -43,6 +43,7 @@ import com.android.launcher3.ItemInfo; import com.android.launcher3.ItemInfoWithIcon; import com.android.launcher3.Launcher; import com.android.launcher3.LauncherAppState; +import com.android.launcher3.LauncherSettings; import com.android.launcher3.R; import com.android.launcher3.WorkspaceItemInfo; import com.android.launcher3.allapps.AllAppsStore; @@ -92,7 +93,7 @@ public class PredictionRowView extends LinearLayout implements private final Launcher mLauncher; private final PredictionUiStateManager mPredictionUiStateManager; - private final int mNumPredictedAppsPerRow; + private int mNumPredictedAppsPerRow; // The set of predicted app component names private final List mPredictedAppComponents = new ArrayList<>(); @@ -128,7 +129,7 @@ public class PredictionRowView extends LinearLayout implements mFocusHelper = new SimpleFocusIndicatorHelper(this); - mNumPredictedAppsPerRow = LauncherAppState.getIDP(context).numColumns; + mNumPredictedAppsPerRow = LauncherAppState.getIDP(context).numAllAppsColumns; mLauncher = Launcher.getLauncher(context); mLauncher.addOnDeviceProfileChangeListener(this); @@ -226,6 +227,7 @@ public class PredictionRowView extends LinearLayout implements @Override public void onDeviceProfileChanged(DeviceProfile dp) { + mNumPredictedAppsPerRow = dp.inv.numAllAppsColumns; removeAllViews(); applyPredictionApps(); } @@ -281,7 +283,7 @@ public class PredictionRowView extends LinearLayout implements } private List processPredictedAppComponents(List components) { - if (getAppsStore().getApps().isEmpty()) { + if (getAppsStore().getApps().length == 0) { // Apps have not been bound yet. return Collections.emptyList(); } @@ -290,7 +292,9 @@ public class PredictionRowView extends LinearLayout implements for (ComponentKeyMapper mapper : components) { ItemInfoWithIcon info = mapper.getApp(getAppsStore()); if (info != null) { - predictedApps.add(info); + ItemInfoWithIcon predictedApp = info.clone(); + predictedApp.container = LauncherSettings.Favorites.CONTAINER_PREDICTION; + predictedApps.add(predictedApp); } else { if (FeatureFlags.IS_DOGFOOD_BUILD) { Log.e(TAG, "Predicted app not found: " + mapper); diff --git a/quickstep/recents_ui_overrides/src/com/android/launcher3/uioverrides/RecentsUiFactory.java b/quickstep/recents_ui_overrides/src/com/android/launcher3/uioverrides/RecentsUiFactory.java index 596bc4f44c..e691566df2 100644 --- a/quickstep/recents_ui_overrides/src/com/android/launcher3/uioverrides/RecentsUiFactory.java +++ b/quickstep/recents_ui_overrides/src/com/android/launcher3/uioverrides/RecentsUiFactory.java @@ -28,17 +28,15 @@ import com.android.launcher3.DeviceProfile; import com.android.launcher3.Launcher; import com.android.launcher3.LauncherState; import com.android.launcher3.LauncherStateManager.StateHandler; -import com.android.launcher3.Utilities; import com.android.launcher3.anim.AnimatorPlaybackController; -import com.android.launcher3.config.FeatureFlags; import com.android.launcher3.graphics.RotationMode; import com.android.launcher3.uioverrides.touchcontrollers.FlingAndHoldTouchController; import com.android.launcher3.uioverrides.touchcontrollers.LandscapeEdgeSwipeController; import com.android.launcher3.uioverrides.touchcontrollers.NavBarToHomeTouchController; import com.android.launcher3.uioverrides.touchcontrollers.OverviewToAllAppsTouchController; import com.android.launcher3.uioverrides.touchcontrollers.PortraitStatesTouchController; -import com.android.launcher3.uioverrides.touchcontrollers.StatusBarTouchController; import com.android.launcher3.uioverrides.touchcontrollers.QuickSwitchTouchController; +import com.android.launcher3.uioverrides.touchcontrollers.StatusBarTouchController; import com.android.launcher3.uioverrides.touchcontrollers.TaskViewTouchController; import com.android.launcher3.uioverrides.touchcontrollers.TransposedQuickSwitchTouchController; import com.android.launcher3.util.TouchController; @@ -164,8 +162,7 @@ public abstract class RecentsUiFactory { } } - if (FeatureFlags.PULL_DOWN_STATUS_BAR - && !launcher.getDeviceProfile().isMultiWindowMode) { + if (!launcher.getDeviceProfile().isMultiWindowMode) { list.add(new StatusBarTouchController(launcher)); } diff --git a/quickstep/recents_ui_overrides/src/com/android/launcher3/uioverrides/states/BackgroundAppState.java b/quickstep/recents_ui_overrides/src/com/android/launcher3/uioverrides/states/BackgroundAppState.java index 50cfac8e43..468b8afc97 100644 --- a/quickstep/recents_ui_overrides/src/com/android/launcher3/uioverrides/states/BackgroundAppState.java +++ b/quickstep/recents_ui_overrides/src/com/android/launcher3/uioverrides/states/BackgroundAppState.java @@ -19,7 +19,6 @@ import static com.android.launcher3.LauncherAnimUtils.OVERVIEW_TRANSITION_MS; import com.android.launcher3.AbstractFloatingView; import com.android.launcher3.Launcher; -import com.android.launcher3.Utilities; import com.android.launcher3.allapps.AllAppsTransitionController; import com.android.launcher3.userevent.nano.LauncherLogProto; import com.android.quickstep.util.LayoutUtils; @@ -69,8 +68,16 @@ public class BackgroundAppState extends OverviewState { if (taskCount == 0) { return super.getOverviewScaleAndTranslation(launcher); } - TaskView dummyTask = recentsView.getTaskViewAt(Utilities.boundToRange( - recentsView.getCurrentPage(), 0, taskCount - 1)); + TaskView dummyTask; + if (recentsView.getCurrentPage() >= 0) { + if (recentsView.getCurrentPage() <= taskCount - 1) { + dummyTask = recentsView.getCurrentPageTaskView(); + } else { + dummyTask = recentsView.getTaskViewAt(taskCount - 1); + } + } else { + dummyTask = recentsView.getTaskViewAt(0); + } return recentsView.getTempClipAnimationHelper().updateForFullscreenOverview(dummyTask) .getScaleAndTranslation(); } diff --git a/quickstep/recents_ui_overrides/src/com/android/launcher3/uioverrides/touchcontrollers/FlingAndHoldTouchController.java b/quickstep/recents_ui_overrides/src/com/android/launcher3/uioverrides/touchcontrollers/FlingAndHoldTouchController.java index ab346c0599..4a3ad1d721 100644 --- a/quickstep/recents_ui_overrides/src/com/android/launcher3/uioverrides/touchcontrollers/FlingAndHoldTouchController.java +++ b/quickstep/recents_ui_overrides/src/com/android/launcher3/uioverrides/touchcontrollers/FlingAndHoldTouchController.java @@ -42,6 +42,7 @@ import android.animation.AnimatorListenerAdapter; import android.animation.AnimatorSet; import android.view.HapticFeedbackConstants; import android.view.MotionEvent; +import android.view.View; import android.view.ViewConfiguration; import com.android.launcher3.Launcher; @@ -179,6 +180,11 @@ public class FlingAndHoldTouchController extends PortraitStatesTouchController { } else { super.onDragEnd(velocity, fling); } + + View searchView = mLauncher.getAppsView().getSearchView(); + if (searchView instanceof FeedbackHandler) { + ((FeedbackHandler) searchView).resetFeedback(); + } mMotionPauseDetector.clear(); } @@ -205,4 +211,16 @@ public class FlingAndHoldTouchController extends PortraitStatesTouchController { builder.addFlag(AnimatorSetBuilder.FLAG_DONT_ANIMATE_OVERVIEW); } } + + /** + * Interface for views with feedback animation requiring reset + */ + public interface FeedbackHandler { + + /** + * reset searchWidget feedback + */ + void resetFeedback(); + } + } diff --git a/quickstep/recents_ui_overrides/src/com/android/launcher3/uioverrides/touchcontrollers/PortraitOverviewStateTouchHelper.java b/quickstep/recents_ui_overrides/src/com/android/launcher3/uioverrides/touchcontrollers/PortraitOverviewStateTouchHelper.java index 20a2487349..03862db1d2 100644 --- a/quickstep/recents_ui_overrides/src/com/android/launcher3/uioverrides/touchcontrollers/PortraitOverviewStateTouchHelper.java +++ b/quickstep/recents_ui_overrides/src/com/android/launcher3/uioverrides/touchcontrollers/PortraitOverviewStateTouchHelper.java @@ -47,9 +47,9 @@ public final class PortraitOverviewStateTouchHelper { * @return true if we should intercept the motion event */ boolean canInterceptTouch(MotionEvent ev) { - if (mRecentsView.getChildCount() > 0) { + if (mRecentsView.getTaskViewCount() > 0) { // Allow swiping up in the gap between the hotseat and overview. - return ev.getY() >= mRecentsView.getChildAt(0).getBottom(); + return ev.getY() >= mRecentsView.getTaskViewAt(0).getBottom(); } else { // If there are no tasks, we only intercept if we're below the hotseat height. return isTouchOverHotseat(mLauncher, ev); @@ -63,7 +63,7 @@ public final class PortraitOverviewStateTouchHelper { * @return true if going back should take the user to the currently running task */ boolean shouldSwipeDownReturnToApp() { - TaskView taskView = mRecentsView.getTaskViewAt(mRecentsView.getNextPage()); + TaskView taskView = mRecentsView.getNextPageTaskView(); return taskView != null && mRecentsView.shouldSwipeDownLaunchApp(); } @@ -76,7 +76,7 @@ public final class PortraitOverviewStateTouchHelper { */ PendingAnimation createSwipeDownToTaskAppAnimation(long duration) { mRecentsView.setCurrentPage(mRecentsView.getPageNearestToCenterOfScreen()); - TaskView taskView = mRecentsView.getTaskViewAt(mRecentsView.getCurrentPage()); + TaskView taskView = mRecentsView.getCurrentPageTaskView(); if (taskView == null) { throw new IllegalStateException("There is no task view to animate to."); } diff --git a/quickstep/recents_ui_overrides/src/com/android/launcher3/uioverrides/touchcontrollers/QuickSwitchTouchController.java b/quickstep/recents_ui_overrides/src/com/android/launcher3/uioverrides/touchcontrollers/QuickSwitchTouchController.java index eb571f607a..6576ec5737 100644 --- a/quickstep/recents_ui_overrides/src/com/android/launcher3/uioverrides/touchcontrollers/QuickSwitchTouchController.java +++ b/quickstep/recents_ui_overrides/src/com/android/launcher3/uioverrides/touchcontrollers/QuickSwitchTouchController.java @@ -50,7 +50,6 @@ import com.android.quickstep.SysUINavigationMode; import com.android.quickstep.SysUINavigationMode.Mode; import com.android.quickstep.views.RecentsView; import com.android.quickstep.views.TaskView; -import com.android.systemui.shared.system.QuickStepContract; /** * Handles quick switching to a recent task from the home screen. diff --git a/quickstep/recents_ui_overrides/src/com/android/quickstep/AppToOverviewAnimationProvider.java b/quickstep/recents_ui_overrides/src/com/android/quickstep/AppToOverviewAnimationProvider.java index 5ebefa337f..ad90e1686e 100644 --- a/quickstep/recents_ui_overrides/src/com/android/quickstep/AppToOverviewAnimationProvider.java +++ b/quickstep/recents_ui_overrides/src/com/android/quickstep/AppToOverviewAnimationProvider.java @@ -102,6 +102,7 @@ final class AppToOverviewAnimationProvider imple anim.addListener(new AnimationSuccessListener() { @Override public void onAnimationSuccess(Animator animator) { + mHelper.onSwipeUpToRecentsComplete(mActivity); if (mRecentsView != null) { mRecentsView.animateUpRunningTaskIconScale(); } diff --git a/quickstep/recents_ui_overrides/src/com/android/quickstep/BaseSwipeUpHandler.java b/quickstep/recents_ui_overrides/src/com/android/quickstep/BaseSwipeUpHandler.java index d627a7f14f..7196f7c6f3 100644 --- a/quickstep/recents_ui_overrides/src/com/android/quickstep/BaseSwipeUpHandler.java +++ b/quickstep/recents_ui_overrides/src/com/android/quickstep/BaseSwipeUpHandler.java @@ -22,9 +22,9 @@ import static com.android.launcher3.Utilities.postAsyncCallback; import static com.android.launcher3.anim.Interpolators.ACCEL_1_5; import static com.android.launcher3.anim.Interpolators.DEACCEL; import static com.android.launcher3.config.FeatureFlags.ENABLE_QUICKSTEP_LIVE_TILE; +import static com.android.launcher3.util.Executors.MAIN_EXECUTOR; +import static com.android.launcher3.util.Executors.UI_HELPER_EXECUTOR; import static com.android.launcher3.views.FloatingIconView.SHAPE_PROGRESS_DURATION; -import static com.android.quickstep.TouchInteractionService.BACKGROUND_EXECUTOR; -import static com.android.quickstep.TouchInteractionService.MAIN_THREAD_EXECUTOR; import static com.android.quickstep.TouchInteractionService.TOUCH_INTERACTION_LOG; import android.animation.Animator; @@ -44,9 +44,10 @@ import android.os.Vibrator; import android.provider.Settings; import android.view.MotionEvent; import android.view.View; -import android.view.WindowManager; import android.view.animation.Interpolator; +import androidx.annotation.UiThread; + import com.android.launcher3.BaseDraggingActivity; import com.android.launcher3.DeviceProfile; import com.android.launcher3.InvariantDeviceProfile; @@ -54,7 +55,6 @@ import com.android.launcher3.R; import com.android.launcher3.Utilities; import com.android.launcher3.anim.AnimationSuccessListener; import com.android.launcher3.anim.AnimatorPlaybackController; -import com.android.launcher3.anim.Interpolators; import com.android.launcher3.graphics.RotationMode; import com.android.launcher3.views.FloatingIconView; import com.android.quickstep.ActivityControlHelper.ActivityInitListener; @@ -75,8 +75,6 @@ import com.android.systemui.shared.system.SyncRtSurfaceTransactionApplierCompat; import java.util.function.Consumer; -import androidx.annotation.UiThread; - /** * Base class for swipe up handler with some utility methods */ @@ -126,7 +124,7 @@ public abstract class BaseSwipeUpHandler mVibrator.vibrate(effect)); + UI_HELPER_EXECUTOR.execute(() -> mVibrator.vibrate(effect)); } public Consumer getRecentsViewDispatcher(RotationMode rotationMode) { @@ -229,10 +227,10 @@ public abstract class BaseSwipeUpHandler { if (!mCanceled) { @@ -275,7 +273,7 @@ public abstract class BaseSwipeUpHandler()); + MAIN_EXECUTOR.execute(new RecentsActivityCommand<>()); } public void onOverviewShown(boolean triggeredFromAltTab) { - mMainThreadExecutor.execute(new ShowRecentsCommand(triggeredFromAltTab)); + MAIN_EXECUTOR.execute(new ShowRecentsCommand(triggeredFromAltTab)); } public void onOverviewHidden() { - mMainThreadExecutor.execute(new HideRecentsCommand()); + MAIN_EXECUTOR.execute(new HideRecentsCommand()); } public void onTip(int actionType, int viewType) { - mMainThreadExecutor.execute(() -> + MAIN_EXECUTOR.execute(() -> UserEventDispatcher.newInstance(mContext).logActionTip(actionType, viewType)); } @@ -112,7 +109,7 @@ public class OverviewCommandHelper { TaskView taskView = rv.getNextTaskView(); if (taskView == null) { if (rv.getTaskViewCount() > 0) { - taskView = (TaskView) rv.getPageAt(0); + taskView = rv.getTaskViewAt(0); taskView.requestFocus(); } else { rv.requestFocus(); @@ -183,7 +180,7 @@ public class OverviewCommandHelper { // Otherwise, start overview. mListener = mHelper.createActivityInitListener(this::onActivityReady); mListener.registerAndStartActivity(mOverviewComponentObserver.getOverviewIntent(), - this::createWindowAnimation, mContext, mMainThreadExecutor.getHandler(), + this::createWindowAnimation, mContext, MAIN_EXECUTOR.getHandler(), mAnimationProvider.getRecentsLaunchDuration()); } diff --git a/quickstep/recents_ui_overrides/src/com/android/quickstep/QuickstepTestInformationHandler.java b/quickstep/recents_ui_overrides/src/com/android/quickstep/QuickstepTestInformationHandler.java index da4642636d..9a01c546af 100644 --- a/quickstep/recents_ui_overrides/src/com/android/quickstep/QuickstepTestInformationHandler.java +++ b/quickstep/recents_ui_overrides/src/com/android/quickstep/QuickstepTestInformationHandler.java @@ -1,9 +1,10 @@ package com.android.quickstep; +import static com.android.launcher3.util.Executors.MAIN_EXECUTOR; + import android.content.Context; import android.os.Bundle; -import com.android.launcher3.MainThreadExecutor; import com.android.launcher3.testing.TestInformationHandler; import com.android.launcher3.testing.TestProtocol; import com.android.launcher3.uioverrides.states.OverviewState; @@ -15,7 +16,9 @@ import java.util.concurrent.ExecutionException; public class QuickstepTestInformationHandler extends TestInformationHandler { + private final Context mContext; public QuickstepTestInformationHandler(Context context) { + mContext = context; } @Override @@ -52,8 +55,8 @@ public class QuickstepTestInformationHandler extends TestInformationHandler { case TestProtocol.REQUEST_OVERVIEW_LEFT_GESTURE_MARGIN: { try { - final int leftMargin = new MainThreadExecutor().submit(() -> - mLauncher.getOverviewPanel().getLeftGestureMargin()).get(); + final int leftMargin = MAIN_EXECUTOR.submit(() -> + getRecentsView().getLeftGestureMargin()).get(); response.putInt(TestProtocol.TEST_INFO_RESPONSE_FIELD, leftMargin); } catch (ExecutionException e) { e.printStackTrace(); @@ -65,9 +68,8 @@ public class QuickstepTestInformationHandler extends TestInformationHandler { case TestProtocol.REQUEST_OVERVIEW_RIGHT_GESTURE_MARGIN: { try { - final int rightMargin = new MainThreadExecutor().submit(() -> - mLauncher.getOverviewPanel().getRightGestureMargin()). - get(); + final int rightMargin = MAIN_EXECUTOR.submit(() -> + getRecentsView().getRightGestureMargin()).get(); response.putInt(TestProtocol.TEST_INFO_RESPONSE_FIELD, rightMargin); } catch (ExecutionException e) { e.printStackTrace(); @@ -80,4 +82,13 @@ public class QuickstepTestInformationHandler extends TestInformationHandler { return super.call(method); } + + private RecentsView getRecentsView() { + OverviewComponentObserver observer = new OverviewComponentObserver(mContext); + try { + return observer.getActivityControlHelper().getCreatedActivity().getOverviewPanel(); + } finally { + observer.onDestroy(); + } + } } diff --git a/quickstep/recents_ui_overrides/src/com/android/quickstep/RecentsActivity.java b/quickstep/recents_ui_overrides/src/com/android/quickstep/RecentsActivity.java index f08ae4a827..9bdc98bf8d 100644 --- a/quickstep/recents_ui_overrides/src/com/android/quickstep/RecentsActivity.java +++ b/quickstep/recents_ui_overrides/src/com/android/quickstep/RecentsActivity.java @@ -135,6 +135,12 @@ public final class RecentsActivity extends BaseRecentsActivity { return (T) mFallbackRecentsView; } + @Override + public void returnToHomescreen() { + super.returnToHomescreen(); + // TODO(b/137318995) This should go home, but doing so removes freeform windows + } + @Override public ActivityOptions getActivityLaunchOptions(final View v) { if (!(v instanceof TaskView)) { diff --git a/quickstep/recents_ui_overrides/src/com/android/quickstep/SwipeSharedState.java b/quickstep/recents_ui_overrides/src/com/android/quickstep/SwipeSharedState.java index c55f656dfd..8783ee3cc7 100644 --- a/quickstep/recents_ui_overrides/src/com/android/quickstep/SwipeSharedState.java +++ b/quickstep/recents_ui_overrides/src/com/android/quickstep/SwipeSharedState.java @@ -15,7 +15,7 @@ */ package com.android.quickstep; -import static com.android.quickstep.TouchInteractionService.MAIN_THREAD_EXECUTOR; +import static com.android.launcher3.util.Executors.MAIN_EXECUTOR; import android.util.Log; @@ -25,6 +25,7 @@ import com.android.launcher3.util.Preconditions; import com.android.quickstep.util.RecentsAnimationListenerSet; import com.android.quickstep.util.SwipeAnimationTargetSet; import com.android.quickstep.util.SwipeAnimationTargetSet.SwipeAnimationListener; + import java.io.PrintWriter; /** @@ -44,6 +45,7 @@ public class SwipeSharedState implements SwipeAnimationListener { public boolean goingToLauncher; public boolean recentsAnimationFinishInterrupted; public int nextRunningTaskId = -1; + private int mLogId; public void setOverviewComponentObserver(OverviewComponentObserver observer) { mOverviewComponentObserver = observer; @@ -77,7 +79,7 @@ public class SwipeSharedState implements SwipeAnimationListener { mRecentsAnimationListener.removeListener(this); mRecentsAnimationListener.cancelListener(); if (mLastAnimationRunning && mLastAnimationTarget != null) { - Utilities.postAsyncCallback(MAIN_THREAD_EXECUTOR.getHandler(), + Utilities.postAsyncCallback(MAIN_EXECUTOR.getHandler(), finishAnimation ? mLastAnimationTarget::finishAnimation : mLastAnimationTarget::cancelAnimation); @@ -155,5 +157,10 @@ public class SwipeSharedState implements SwipeAnimationListener { pw.println(prefix + "nextRunningTaskId=" + nextRunningTaskId); pw.println(prefix + "lastAnimationCancelled=" + mLastAnimationCancelled); pw.println(prefix + "lastAnimationRunning=" + mLastAnimationRunning); + pw.println(prefix + "logTraceId=" + mLogId); + } + + public void setLogTraceId(int logId) { + this.mLogId = logId; } } diff --git a/quickstep/recents_ui_overrides/src/com/android/quickstep/TaskSystemShortcut.java b/quickstep/recents_ui_overrides/src/com/android/quickstep/TaskSystemShortcut.java index fd45923070..1af0db07d8 100644 --- a/quickstep/recents_ui_overrides/src/com/android/quickstep/TaskSystemShortcut.java +++ b/quickstep/recents_ui_overrides/src/com/android/quickstep/TaskSystemShortcut.java @@ -36,8 +36,6 @@ import android.view.View; import com.android.launcher3.BaseDraggingActivity; import com.android.launcher3.DeviceProfile; import com.android.launcher3.ItemInfo; -import com.android.launcher3.Launcher; -import com.android.launcher3.LauncherState; import com.android.launcher3.R; import com.android.launcher3.WorkspaceItemInfo; import com.android.launcher3.popup.SystemShortcut; @@ -267,12 +265,16 @@ public class TaskSystemShortcut extends SystemShortcut @Override protected ActivityOptions makeLaunchOptions(Activity activity) { - return ActivityOptionsCompat.makeFreeformOptions(); + ActivityOptions activityOptions = ActivityOptionsCompat.makeFreeformOptions(); + // Arbitrary bounds only because freeform is in dev mode right now + Rect r = new Rect(50, 50, 200, 200); + activityOptions.setLaunchBounds(r); + return activityOptions; } @Override protected boolean onActivityStarted(BaseDraggingActivity activity) { - Launcher.getLauncher(activity).getStateManager().goToState(LauncherState.NORMAL); + activity.returnToHomescreen(); return true; } } diff --git a/quickstep/recents_ui_overrides/src/com/android/quickstep/TouchInteractionService.java b/quickstep/recents_ui_overrides/src/com/android/quickstep/TouchInteractionService.java index 86ba855784..7c1bc4ed8a 100644 --- a/quickstep/recents_ui_overrides/src/com/android/quickstep/TouchInteractionService.java +++ b/quickstep/recents_ui_overrides/src/com/android/quickstep/TouchInteractionService.java @@ -23,6 +23,8 @@ import static com.android.launcher3.config.FeatureFlags.ENABLE_HINTS_IN_OVERVIEW import static com.android.launcher3.config.FeatureFlags.ENABLE_QUICKSTEP_LIVE_TILE; import static com.android.launcher3.config.FeatureFlags.FAKE_LANDSCAPE_UI; import static com.android.launcher3.config.FeatureFlags.QUICKSTEP_SPRINGS; +import static com.android.launcher3.util.Executors.MAIN_EXECUTOR; +import static com.android.launcher3.util.Executors.UI_HELPER_EXECUTOR; import static com.android.systemui.shared.system.QuickStepContract.KEY_EXTRA_INPUT_MONITOR; import static com.android.systemui.shared.system.QuickStepContract.KEY_EXTRA_SYSUI_PROXY; import static com.android.systemui.shared.system.QuickStepContract.SYSUI_STATE_A11Y_BUTTON_CLICKABLE; @@ -50,8 +52,6 @@ import android.content.res.Configuration; import android.graphics.Point; import android.graphics.RectF; import android.graphics.Region; -import android.hardware.display.DisplayManager; -import android.hardware.display.DisplayManager.DisplayListener; import android.os.Build; import android.os.Bundle; import android.os.IBinder; @@ -61,18 +61,15 @@ import android.os.RemoteException; import android.text.TextUtils; import android.util.Log; import android.view.Choreographer; -import android.view.Display; import android.view.InputEvent; import android.view.MotionEvent; import android.view.Surface; -import android.view.WindowManager; import androidx.annotation.BinderThread; import androidx.annotation.UiThread; import androidx.annotation.WorkerThread; import com.android.launcher3.BaseDraggingActivity; -import com.android.launcher3.MainThreadExecutor; import com.android.launcher3.R; import com.android.launcher3.ResourceUtils; import com.android.launcher3.Utilities; @@ -82,8 +79,8 @@ import com.android.launcher3.logging.UserEventDispatcher; import com.android.launcher3.model.AppLaunchTracker; import com.android.launcher3.provider.RestoreDbTask; import com.android.launcher3.testing.TestProtocol; -import com.android.launcher3.util.LooperExecutor; -import com.android.launcher3.util.UiThreadHelper; +import com.android.launcher3.uioverrides.DejankBinderTracker; +import com.android.launcher3.util.DefaultDisplay; import com.android.quickstep.SysUINavigationMode.Mode; import com.android.quickstep.SysUINavigationMode.NavigationModeChangeListener; import com.android.quickstep.inputconsumers.AccessibilityInputConsumer; @@ -106,8 +103,8 @@ import com.android.systemui.shared.system.QuickStepContract; import com.android.systemui.shared.system.QuickStepContract.SystemUiStateFlags; import com.android.systemui.shared.system.RecentsAnimationListener; import com.android.systemui.shared.system.SystemGestureExclusionListenerCompat; - import com.android.systemui.shared.system.TaskInfoCompat; + import java.io.FileDescriptor; import java.io.PrintWriter; import java.util.Arrays; @@ -136,11 +133,14 @@ class ArgList extends LinkedList { */ @TargetApi(Build.VERSION_CODES.Q) public class TouchInteractionService extends Service implements - NavigationModeChangeListener, DisplayListener { + NavigationModeChangeListener, DefaultDisplay.DisplayInfoChangeListener { + + /** + * NOTE: This value should be kept same as + * ActivityTaskManagerService#INTENT_EXTRA_LOG_TRACE_ID in platform + */ + public static final String INTENT_EXTRA_LOG_TRACE_ID = "INTENT_EXTRA_LOG_TRACE_ID"; - public static final MainThreadExecutor MAIN_THREAD_EXECUTOR = new MainThreadExecutor(); - public static final LooperExecutor BACKGROUND_EXECUTOR = - new LooperExecutor(UiThreadHelper.getBackgroundLooper()); public static final EventLogArray TOUCH_INTERACTION_LOG = new EventLogArray("touch_interaction_log", 40); @@ -161,9 +161,9 @@ public class TouchInteractionService extends Service implements public void onInitialize(Bundle bundle) { mISystemUiProxy = ISystemUiProxy.Stub .asInterface(bundle.getBinder(KEY_EXTRA_SYSUI_PROXY)); - MAIN_THREAD_EXECUTOR.execute(TouchInteractionService.this::initInputMonitor); - MAIN_THREAD_EXECUTOR.execute(TouchInteractionService.this::onSystemUiProxySet); - MAIN_THREAD_EXECUTOR.execute(() -> preloadOverview(true /* fromInit */)); + MAIN_EXECUTOR.execute(TouchInteractionService.this::initInputMonitor); + MAIN_EXECUTOR.execute(TouchInteractionService.this::onSystemUiProxySet); + MAIN_EXECUTOR.execute(() -> preloadOverview(true /* fromInit */)); sIsInitialized = true; } @@ -198,7 +198,7 @@ public class TouchInteractionService extends Service implements @Override public void onAssistantVisibilityChanged(float visibility) { mLastAssistantVisibility = visibility; - MAIN_THREAD_EXECUTOR.execute( + MAIN_EXECUTOR.execute( TouchInteractionService.this::onAssistantVisibilityChanged); } @@ -214,13 +214,13 @@ public class TouchInteractionService extends Service implements isButton, gestureSwipeLeft, activityControl.getContainerType()); if (completed && !isButton && shouldNotifyBackGesture()) { - BACKGROUND_EXECUTOR.execute(TouchInteractionService.this::tryNotifyBackGesture); + UI_HELPER_EXECUTOR.execute(TouchInteractionService.this::tryNotifyBackGesture); } } public void onSystemUiStateChanged(int stateFlags) { mSystemUiStateFlags = stateFlags; - MAIN_THREAD_EXECUTOR.execute(TouchInteractionService.this::onSystemUiFlagsChanged); + MAIN_EXECUTOR.execute(TouchInteractionService.this::onSystemUiFlagsChanged); } /** Deprecated methods **/ @@ -244,6 +244,7 @@ public class TouchInteractionService extends Service implements private static boolean sConnected = false; private static boolean sIsInitialized = false; private static final SwipeSharedState sSwipeSharedState = new SwipeSharedState(); + private int mLogId; public static boolean isConnected() { return sConnected; @@ -271,7 +272,6 @@ public class TouchInteractionService extends Service implements private OverviewCommandHelper mOverviewCommandHelper; private OverviewComponentObserver mOverviewComponentObserver; private OverviewInteractionState mOverviewInteractionState; - private OverviewCallbacks mOverviewCallbacks; private TaskOverlayFactory mTaskOverlayFactory; private InputConsumerController mInputConsumer; private boolean mAssistantAvailable; @@ -323,8 +323,7 @@ public class TouchInteractionService extends Service implements registerReceiver(mUserUnlockedReceiver, new IntentFilter(Intent.ACTION_USER_UNLOCKED)); } - mDefaultDisplayId = getSystemService(WindowManager.class).getDefaultDisplay() - .getDisplayId(); + mDefaultDisplayId = DefaultDisplay.INSTANCE.get(this).getInfo().id; String blockingActivity = getString(R.string.gesture_blocking_activity); mGestureBlockingActivity = TextUtils.isEmpty(blockingActivity) ? null : ComponentName.unflattenFromString(blockingActivity); @@ -391,9 +390,8 @@ public class TouchInteractionService extends Service implements return; } - Display defaultDisplay = getSystemService(WindowManager.class).getDefaultDisplay(); - Point realSize = new Point(); - defaultDisplay.getRealSize(realSize); + DefaultDisplay.Info displayInfo = DefaultDisplay.INSTANCE.get(this).getInfo(); + Point realSize = new Point(displayInfo.realSize); mSwipeTouchRegion.set(0, 0, realSize.x, realSize.y); if (mMode == Mode.NO_BUTTON) { int touchHeight = getNavbarSize(ResourceUtils.NAVBAR_BOTTOM_GESTURE_SIZE); @@ -415,7 +413,7 @@ public class TouchInteractionService extends Service implements } else { mAssistantLeftRegion.setEmpty(); mAssistantRightRegion.setEmpty(); - switch (defaultDisplay.getRotation()) { + switch (displayInfo.rotation) { case Surface.ROTATION_90: mSwipeTouchRegion.left = mSwipeTouchRegion.right - getNavbarSize(ResourceUtils.NAVBAR_LANDSCAPE_LEFT_RIGHT_SIZE); @@ -438,10 +436,9 @@ public class TouchInteractionService extends Service implements } if (mMode.hasGestures != newMode.hasGestures) { if (newMode.hasGestures) { - getSystemService(DisplayManager.class).registerDisplayListener( - this, MAIN_THREAD_EXECUTOR.getHandler()); + DefaultDisplay.INSTANCE.get(this).addChangeListener(this); } else { - getSystemService(DisplayManager.class).unregisterDisplayListener(this); + DefaultDisplay.INSTANCE.get(this).removeChangeListener(this); } } mMode = newMode; @@ -457,14 +454,8 @@ public class TouchInteractionService extends Service implements } @Override - public void onDisplayAdded(int i) { } - - @Override - public void onDisplayRemoved(int i) { } - - @Override - public void onDisplayChanged(int displayId) { - if (displayId != mDefaultDisplayId) { + public void onDisplayInfoChanged(DefaultDisplay.Info info, int flags) { + if (info.id != mDefaultDisplayId) { return; } @@ -477,7 +468,6 @@ public class TouchInteractionService extends Service implements mOverviewCommandHelper = new OverviewCommandHelper(this, mOverviewComponentObserver); mOverviewInteractionState = OverviewInteractionState.INSTANCE.get(this); - mOverviewCallbacks = OverviewCallbacks.get(this); mTaskOverlayFactory = TaskOverlayFactory.INSTANCE.get(this); mInputConsumer = InputConsumerController.getRecentsAnimationInputConsumer(); mIsUserUnlocked = true; @@ -529,7 +519,7 @@ public class TouchInteractionService extends Service implements } disposeEventHandlers(); if (mMode.hasGestures) { - getSystemService(DisplayManager.class).unregisterDisplayListener(this); + DefaultDisplay.INSTANCE.get(this).removeChangeListener(this); } sConnected = false; @@ -547,6 +537,7 @@ public class TouchInteractionService extends Service implements } private void onInputEvent(InputEvent ev) { + DejankBinderTracker.allowBinderTrackingInTests(); if (TestProtocol.sDebugTracing) { Log.d(TestProtocol.NO_BACKGROUND_TO_OVERVIEW_TAG, "onInputEvent " + ev); } @@ -554,9 +545,12 @@ public class TouchInteractionService extends Service implements Log.e(TAG, "Unknown event " + ev); return; } + MotionEvent event = (MotionEvent) ev; - TOUCH_INTERACTION_LOG.addLog("onMotionEvent", event.getActionMasked()); if (event.getAction() == ACTION_DOWN) { + mLogId = TOUCH_INTERACTION_LOG.generateAndSetLogId(); + sSwipeSharedState.setLogTraceId(mLogId); + if (mSwipeTouchRegion.contains(event.getX(), event.getY())) { boolean useSharedState = mConsumer.useSharedSwipeState(); mConsumer.onConsumerAboutToBeSwitched(); @@ -576,7 +570,10 @@ public class TouchInteractionService extends Service implements mUncheckedConsumer = InputConsumer.NO_OP; } } + + TOUCH_INTERACTION_LOG.addLog("onMotionEvent", event.getActionMasked()); mUncheckedConsumer.onMotionEvent(event); + DejankBinderTracker.disallowBinderTrackingInTests(); } private boolean validSystemUiFlags() { @@ -640,7 +637,8 @@ public class TouchInteractionService extends Service implements } private InputConsumer newBaseConsumer(boolean useSharedState, MotionEvent event) { - RunningTaskInfo runningTaskInfo = mAM.getRunningTask(0); + RunningTaskInfo runningTaskInfo = DejankBinderTracker.whitelistIpcs( + () -> mAM.getRunningTask(0)); if (!useSharedState) { sSwipeSharedState.clearAllState(false /* finishAnimation */); } @@ -656,12 +654,13 @@ public class TouchInteractionService extends Service implements if (isExcludedAssistant(runningTaskInfo)) { // In the case where we are in the excluded assistant state, ignore it and treat the // running activity as the task behind the assistant - runningTaskInfo = mAM.getRunningTask(ACTIVITY_TYPE_ASSISTANT); + runningTaskInfo = DejankBinderTracker.whitelistIpcs( + () -> mAM.getRunningTask(ACTIVITY_TYPE_ASSISTANT)); if (!ActivityManagerWrapper.isHomeTask(runningTaskInfo)) { final ComponentName homeComponent = mOverviewComponentObserver.getHomeIntent().getComponent(); forceOverviewInputConsumer = - runningTaskInfo.baseIntent.getComponent().equals(homeComponent); + runningTaskInfo.baseIntent.getComponent(). equals(homeComponent); } } @@ -705,26 +704,26 @@ public class TouchInteractionService extends Service implements final boolean shouldDefer; final BaseSwipeUpHandler.Factory factory; + ActivityControlHelper activityControlHelper = + mOverviewComponentObserver.getActivityControlHelper(); if (mMode == Mode.NO_BUTTON && !mOverviewComponentObserver.isHomeAndOverviewSame()) { shouldDefer = !sSwipeSharedState.recentsAnimationFinishInterrupted; factory = mFallbackNoButtonFactory; } else { - shouldDefer = mOverviewComponentObserver.getActivityControlHelper() - .deferStartingActivity(mActiveNavBarRegion, event); + shouldDefer = activityControlHelper.deferStartingActivity(mActiveNavBarRegion, event); factory = mWindowTreansformFactory; } - return new OtherActivityInputConsumer(this, runningTaskInfo, - shouldDefer, mOverviewCallbacks, this::onConsumerInactive, - sSwipeSharedState, mInputMonitorCompat, mSwipeTouchRegion, - disableHorizontalSwipe(event), factory); + return new OtherActivityInputConsumer(this, runningTaskInfo, shouldDefer, + this::onConsumerInactive, sSwipeSharedState, mInputMonitorCompat, mSwipeTouchRegion, + disableHorizontalSwipe(event), activityControlHelper, factory, mLogId); } private InputConsumer createDeviceLockedInputConsumer(RunningTaskInfo taskInfo) { if (mMode == Mode.NO_BUTTON && taskInfo != null) { return new DeviceLockedInputConsumer(this, sSwipeSharedState, mInputMonitorCompat, - mSwipeTouchRegion, taskInfo.taskId); + mSwipeTouchRegion, taskInfo.taskId, mLogId); } else { return mResetGestureInputConsumer; } @@ -740,10 +739,10 @@ public class TouchInteractionService extends Service implements if (activity.getRootView().hasWindowFocus() || sSwipeSharedState.goingToLauncher) { return new OverviewInputConsumer(activity, mInputMonitorCompat, - false /* startingInActivityBounds */); + false /* startingInActivityBounds */, activityControl); } else { return new OverviewWithoutFocusInputConsumer(activity, mInputMonitorCompat, - disableHorizontalSwipe(event)); + activityControl, disableHorizontalSwipe(event)); } } @@ -788,7 +787,8 @@ public class TouchInteractionService extends Service implements } // Pass null animation handler to indicate this start is preload. - startRecentsActivityAsync(mOverviewComponentObserver.getOverviewIntentIgnoreSysUiState(), null); + startRecentsActivityAsync(mOverviewComponentObserver.getOverviewIntentIgnoreSysUiState(), + null); } @Override @@ -897,7 +897,7 @@ public class TouchInteractionService extends Service implements } public static void startRecentsActivityAsync(Intent intent, RecentsAnimationListener listener) { - BACKGROUND_EXECUTOR.execute(() -> ActivityManagerWrapper.getInstance() + UI_HELPER_EXECUTOR.execute(() -> ActivityManagerWrapper.getInstance() .startRecentsActivity(intent, null, listener, null, null)); } } diff --git a/quickstep/recents_ui_overrides/src/com/android/quickstep/WindowTransformSwipeHandler.java b/quickstep/recents_ui_overrides/src/com/android/quickstep/WindowTransformSwipeHandler.java index a3bd348e20..db8eb27533 100644 --- a/quickstep/recents_ui_overrides/src/com/android/quickstep/WindowTransformSwipeHandler.java +++ b/quickstep/recents_ui_overrides/src/com/android/quickstep/WindowTransformSwipeHandler.java @@ -68,6 +68,7 @@ import com.android.launcher3.anim.AnimationSuccessListener; import com.android.launcher3.anim.AnimatorPlaybackController; import com.android.launcher3.anim.Interpolators; import com.android.launcher3.logging.UserEventDispatcher; +import com.android.launcher3.uioverrides.DejankBinderTracker; import com.android.launcher3.userevent.nano.LauncherLogProto.Action.Direction; import com.android.launcher3.userevent.nano.LauncherLogProto.Action.Touch; import com.android.launcher3.userevent.nano.LauncherLogProto.ContainerType; @@ -423,9 +424,13 @@ public class WindowTransformSwipeHandler private void initializeLauncherAnimationController() { buildAnimationController(); - if (LatencyTrackerCompat.isEnabled(mContext)) { - LatencyTrackerCompat.logToggleRecents((int) (mLauncherFrameDrawnTime - mTouchTimeMs)); - } + DejankBinderTracker.whitelistIpcs(() -> { + // Only used in debug builds + if (LatencyTrackerCompat.isEnabled(mContext)) { + LatencyTrackerCompat.logToggleRecents( + (int) (mLauncherFrameDrawnTime - mTouchTimeMs)); + } + }); // This method is only called when STATE_GESTURE_STARTED is set, so we can enable the // high-res thumbnail loader here once we are sure that we will end up in an overview state @@ -479,8 +484,8 @@ public class WindowTransformSwipeHandler recentsAttachedToAppWindow = mIsShelfPeeking || mIsLikelyToStartNewTask; if (animate) { // Only animate if an adjacent task view is visible on screen. - TaskView adjacentTask1 = mRecentsView.getTaskViewAt(runningTaskIndex + 1); - TaskView adjacentTask2 = mRecentsView.getTaskViewAt(runningTaskIndex - 1); + TaskView adjacentTask1 = mRecentsView.getNextTaskView(); + TaskView adjacentTask2 = mRecentsView.getPreviousTaskView(); float prevTranslationX = mRecentsView.getTranslationX(); mRecentsView.setTranslationX(0); animate = (adjacentTask1 != null && adjacentTask1.getGlobalVisibleRect(TEMP_RECT)) @@ -590,8 +595,7 @@ public class WindowTransformSwipeHandler */ private void updateSysUiFlags(float windowProgress) { if (mRecentsView != null) { - TaskView centermostTask = mRecentsView.getTaskViewAt(mRecentsView - .getPageNearestToCenterOfScreen()); + TaskView centermostTask = mRecentsView.getTaskViewNearestToCenterOfScreen(); int centermostTaskFlags = centermostTask == null ? 0 : centermostTask.getThumbnail().getSysUiStatusNavFlags(); boolean useHomeScreenFlags = windowProgress > 1 - UPDATE_SYSUI_FLAGS_THRESHOLD; @@ -685,8 +689,8 @@ public class WindowTransformSwipeHandler } BaseDraggingActivity activity = mActivityControlHelper.getCreatedActivity(); - return activity == null - ? InputConsumer.NO_OP : new OverviewInputConsumer(activity, null, true); + return activity == null ? InputConsumer.NO_OP + : new OverviewInputConsumer(activity, null, true, mActivityControlHelper); } private void endRunningWindowAnim(boolean cancel) { diff --git a/quickstep/recents_ui_overrides/src/com/android/quickstep/fallback/FallbackRecentsView.java b/quickstep/recents_ui_overrides/src/com/android/quickstep/fallback/FallbackRecentsView.java index 2e9c0a3c4d..7f1aae5e22 100644 --- a/quickstep/recents_ui_overrides/src/com/android/quickstep/fallback/FallbackRecentsView.java +++ b/quickstep/recents_ui_overrides/src/com/android/quickstep/fallback/FallbackRecentsView.java @@ -176,7 +176,7 @@ public class FallbackRecentsView extends RecentsView { protected void applyLoadPlan(ArrayList tasks) { // When quick-switching on 3p-launcher, we add a "dummy" tile corresponding to Launcher // as well. This tile is never shown as we have setCurrentTaskHidden, but allows use to - // track the index of the next task appropriately, as it we are switching on any other app. + // track the index of the next task appropriately, as if we are switching on any other app. if (mRunningTaskInfo != null && mRunningTaskInfo.taskId == mRunningTaskId) { // Check if the task list has running task boolean found = false; diff --git a/quickstep/recents_ui_overrides/src/com/android/quickstep/inputconsumers/DeviceLockedInputConsumer.java b/quickstep/recents_ui_overrides/src/com/android/quickstep/inputconsumers/DeviceLockedInputConsumer.java index 3d763ab52c..b24c788107 100644 --- a/quickstep/recents_ui_overrides/src/com/android/quickstep/inputconsumers/DeviceLockedInputConsumer.java +++ b/quickstep/recents_ui_overrides/src/com/android/quickstep/inputconsumers/DeviceLockedInputConsumer.java @@ -22,8 +22,9 @@ import static android.view.MotionEvent.ACTION_UP; import static com.android.launcher3.Utilities.squaredHypot; import static com.android.launcher3.Utilities.squaredTouchSlop; import static com.android.quickstep.MultiStateCallback.DEBUG_STATES; -import static com.android.quickstep.WindowTransformSwipeHandler.MIN_PROGRESS_FOR_OVERVIEW; +import static com.android.quickstep.TouchInteractionService.INTENT_EXTRA_LOG_TRACE_ID; import static com.android.quickstep.TouchInteractionService.startRecentsActivityAsync; +import static com.android.quickstep.WindowTransformSwipeHandler.MIN_PROGRESS_FOR_OVERVIEW; import android.content.ComponentName; import android.content.Context; @@ -35,10 +36,10 @@ import android.graphics.RectF; import android.view.MotionEvent; import android.view.VelocityTracker; import android.view.ViewConfiguration; -import android.view.WindowManager; import com.android.launcher3.R; import com.android.launcher3.Utilities; +import com.android.launcher3.util.DefaultDisplay; import com.android.quickstep.LockScreenRecentsActivity; import com.android.quickstep.MultiStateCallback; import com.android.quickstep.SwipeSharedState; @@ -76,6 +77,7 @@ public class DeviceLockedInputConsumer implements InputConsumer, private final PointF mTouchDown = new PointF(); private final ClipAnimationHelper mClipAnimationHelper; + private int mLogId; private final ClipAnimationHelper.TransformParams mTransformParams; private final Point mDisplaySize; private final MultiStateCallback mStateCallback; @@ -90,19 +92,20 @@ public class DeviceLockedInputConsumer implements InputConsumer, private SwipeAnimationTargetSet mTargetSet; public DeviceLockedInputConsumer(Context context, SwipeSharedState swipeSharedState, - InputMonitorCompat inputMonitorCompat, RectF swipeTouchRegion, int runningTaskId) { + InputMonitorCompat inputMonitorCompat, RectF swipeTouchRegion, int runningTaskId, + int logId) { mContext = context; mTouchSlopSquared = squaredTouchSlop(context); mSwipeSharedState = swipeSharedState; mClipAnimationHelper = new ClipAnimationHelper(context); + mLogId = logId; mTransformParams = new ClipAnimationHelper.TransformParams(); mInputMonitorCompat = inputMonitorCompat; mSwipeTouchRegion = swipeTouchRegion; mRunningTaskId = runningTaskId; // Do not use DeviceProfile as the user data might be locked - mDisplaySize = new Point(); - context.getSystemService(WindowManager.class).getDefaultDisplay().getRealSize(mDisplaySize); + mDisplaySize = DefaultDisplay.INSTANCE.get(context).getInfo().realSize; // Init states mStateCallback = new MultiStateCallback(STATE_NAMES); @@ -205,7 +208,8 @@ public class DeviceLockedInputConsumer implements InputConsumer, Intent intent = new Intent(Intent.ACTION_MAIN) .addCategory(Intent.CATEGORY_DEFAULT) .setComponent(new ComponentName(mContext, LockScreenRecentsActivity.class)) - .setFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_CLEAR_TASK); + .setFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_CLEAR_TASK) + .putExtra(INTENT_EXTRA_LOG_TRACE_ID, mLogId); mInputMonitorCompat.pilferPointers(); startRecentsActivityAsync(intent, newListenerSet); diff --git a/quickstep/recents_ui_overrides/src/com/android/quickstep/inputconsumers/FallbackNoButtonInputConsumer.java b/quickstep/recents_ui_overrides/src/com/android/quickstep/inputconsumers/FallbackNoButtonInputConsumer.java index 6ec1da0c48..e0ff8afe65 100644 --- a/quickstep/recents_ui_overrides/src/com/android/quickstep/inputconsumers/FallbackNoButtonInputConsumer.java +++ b/quickstep/recents_ui_overrides/src/com/android/quickstep/inputconsumers/FallbackNoButtonInputConsumer.java @@ -50,7 +50,6 @@ import com.android.quickstep.fallback.FallbackRecentsView; import com.android.quickstep.util.ObjectWrapper; import com.android.quickstep.util.RectFSpringAnim; import com.android.quickstep.util.SwipeAnimationTargetSet; -import com.android.quickstep.views.RecentsView; import com.android.quickstep.views.TaskView; import com.android.systemui.shared.recents.model.ThumbnailData; import com.android.systemui.shared.system.ActivityManagerWrapper; diff --git a/quickstep/recents_ui_overrides/src/com/android/quickstep/inputconsumers/OtherActivityInputConsumer.java b/quickstep/recents_ui_overrides/src/com/android/quickstep/inputconsumers/OtherActivityInputConsumer.java index 86766d99fb..f06702dba3 100644 --- a/quickstep/recents_ui_overrides/src/com/android/quickstep/inputconsumers/OtherActivityInputConsumer.java +++ b/quickstep/recents_ui_overrides/src/com/android/quickstep/inputconsumers/OtherActivityInputConsumer.java @@ -27,6 +27,7 @@ import static com.android.launcher3.Utilities.EDGE_NAV_BAR; import static com.android.launcher3.Utilities.squaredHypot; import static com.android.launcher3.util.RaceConditionTracker.ENTER; import static com.android.launcher3.util.RaceConditionTracker.EXIT; +import static com.android.quickstep.TouchInteractionService.INTENT_EXTRA_LOG_TRACE_ID; import static com.android.quickstep.TouchInteractionService.TOUCH_INTERACTION_LOG; import static com.android.quickstep.TouchInteractionService.startRecentsActivityAsync; import static com.android.systemui.shared.system.ActivityManagerWrapper.CLOSE_SYSTEM_WINDOWS_REASON_RECENTS; @@ -35,6 +36,7 @@ import android.annotation.TargetApi; import android.app.ActivityManager.RunningTaskInfo; import android.content.Context; import android.content.ContextWrapper; +import android.content.Intent; import android.graphics.PointF; import android.graphics.RectF; import android.os.Build; @@ -44,12 +46,15 @@ import android.view.MotionEvent; import android.view.VelocityTracker; import android.view.ViewConfiguration; +import androidx.annotation.UiThread; + import com.android.launcher3.R; import com.android.launcher3.util.Preconditions; import com.android.launcher3.util.RaceConditionTracker; import com.android.launcher3.util.TraceHelper; +import com.android.quickstep.ActivityControlHelper; import com.android.quickstep.BaseSwipeUpHandler; -import com.android.quickstep.OverviewCallbacks; +import com.android.quickstep.BaseSwipeUpHandler.Factory; import com.android.quickstep.SwipeSharedState; import com.android.quickstep.SysUINavigationMode; import com.android.quickstep.SysUINavigationMode.Mode; @@ -62,8 +67,6 @@ import com.android.systemui.shared.system.InputMonitorCompat; import java.util.function.Consumer; -import androidx.annotation.UiThread; - /** * Input consumer for handling events originating from an activity other than Launcher */ @@ -78,11 +81,11 @@ public class OtherActivityInputConsumer extends ContextWrapper implements InputC private final CachedEventDispatcher mRecentsViewDispatcher = new CachedEventDispatcher(); private final RunningTaskInfo mRunningTask; - private final OverviewCallbacks mOverviewCallbacks; private final SwipeSharedState mSwipeSharedState; private final InputMonitorCompat mInputMonitorCompat; private final SysUINavigationMode.Mode mMode; private final RectF mSwipeTouchRegion; + private final ActivityControlHelper mActivityControlHelper; private final BaseSwipeUpHandler.Factory mHandlerFactory; @@ -119,20 +122,23 @@ public class OtherActivityInputConsumer extends ContextWrapper implements InputC ActivityManagerWrapper.getInstance().cancelRecentsAnimation( true /* restoreHomeStackPosition */); }; + private int mLogId; public OtherActivityInputConsumer(Context base, RunningTaskInfo runningTaskInfo, - boolean isDeferredDownTarget, OverviewCallbacks overviewCallbacks, - Consumer onCompleteCallback, + boolean isDeferredDownTarget, Consumer onCompleteCallback, SwipeSharedState swipeSharedState, InputMonitorCompat inputMonitorCompat, RectF swipeTouchRegion, boolean disableHorizontalSwipe, - BaseSwipeUpHandler.Factory handlerFactory) { + ActivityControlHelper activityControlHelper, + Factory handlerFactory, int logId) { super(base); + mLogId = logId; mMainThreadHandler = new Handler(Looper.getMainLooper()); mRunningTask = runningTaskInfo; mMode = SysUINavigationMode.getMode(base); mSwipeTouchRegion = swipeTouchRegion; mHandlerFactory = handlerFactory; + mActivityControlHelper = activityControlHelper; mMotionPauseDetector = new MotionPauseDetector(base); mMotionPauseMinDisplacement = base.getResources().getDimension( @@ -143,7 +149,6 @@ public class OtherActivityInputConsumer extends ContextWrapper implements InputC boolean continuingPreviousGesture = swipeSharedState.getActiveListener() != null; mIsDeferredDownTarget = !continuingPreviousGesture && isDeferredDownTarget; - mOverviewCallbacks = overviewCallbacks; mSwipeSharedState = swipeSharedState; mNavBarPosition = new NavBarPosition(base); @@ -312,7 +317,7 @@ public class OtherActivityInputConsumer extends ContextWrapper implements InputC } mInputMonitorCompat.pilferPointers(); - mOverviewCallbacks.closeAllWindows(); + mActivityControlHelper.closeOverlay(); ActivityManagerWrapper.getInstance().closeSystemWindows( CLOSE_SYSTEM_WINDOWS_REASON_RECENTS); @@ -341,7 +346,9 @@ public class OtherActivityInputConsumer extends ContextWrapper implements InputC RecentsAnimationListenerSet newListenerSet = mSwipeSharedState.newRecentsAnimationListenerSet(); newListenerSet.addListener(handler); - startRecentsActivityAsync(handler.getLaunchIntent(), newListenerSet); + Intent intent = handler.getLaunchIntent(); + intent.putExtra(INTENT_EXTRA_LOG_TRACE_ID, mLogId); + startRecentsActivityAsync(intent, newListenerSet); } } diff --git a/quickstep/recents_ui_overrides/src/com/android/quickstep/inputconsumers/OverviewInputConsumer.java b/quickstep/recents_ui_overrides/src/com/android/quickstep/inputconsumers/OverviewInputConsumer.java index 581ab6d3ea..fd216b9afa 100644 --- a/quickstep/recents_ui_overrides/src/com/android/quickstep/inputconsumers/OverviewInputConsumer.java +++ b/quickstep/recents_ui_overrides/src/com/android/quickstep/inputconsumers/OverviewInputConsumer.java @@ -19,7 +19,6 @@ import static com.android.launcher3.config.FeatureFlags.ENABLE_QUICKSTEP_LIVE_TI import static com.android.quickstep.TouchInteractionService.TOUCH_INTERACTION_LOG; import static com.android.systemui.shared.system.ActivityManagerWrapper.CLOSE_SYSTEM_WINDOWS_REASON_RECENTS; -import android.util.Log; import android.view.KeyEvent; import android.view.MotionEvent; @@ -27,9 +26,8 @@ import androidx.annotation.Nullable; import com.android.launcher3.BaseDraggingActivity; import com.android.launcher3.Utilities; -import com.android.launcher3.testing.TestProtocol; import com.android.launcher3.views.BaseDragLayer; -import com.android.quickstep.OverviewCallbacks; +import com.android.quickstep.ActivityControlHelper; import com.android.systemui.shared.system.ActivityManagerWrapper; import com.android.systemui.shared.system.InputMonitorCompat; @@ -42,6 +40,7 @@ public class OverviewInputConsumer implements InputConsumer { private final T mActivity; + private final ActivityControlHelper mActivityControlHelper; private final BaseDragLayer mTarget; private final InputMonitorCompat mInputMonitor; @@ -53,10 +52,12 @@ public class OverviewInputConsumer private boolean mTargetHandledTouch; public OverviewInputConsumer(T activity, @Nullable InputMonitorCompat inputMonitor, - boolean startingInActivityBounds) { + boolean startingInActivityBounds, + ActivityControlHelper activityControlHelper) { mActivity = activity; mInputMonitor = inputMonitor; mStartingInActivityBounds = startingInActivityBounds; + mActivityControlHelper = activityControlHelper; mTarget = activity.getDragLayer(); if (startingInActivityBounds) { @@ -98,7 +99,7 @@ public class OverviewInputConsumer if (!mTargetHandledTouch && handled) { mTargetHandledTouch = true; if (!mStartingInActivityBounds) { - OverviewCallbacks.get(mActivity).closeAllWindows(); + mActivityControlHelper.closeOverlay(); ActivityManagerWrapper.getInstance() .closeSystemWindows(CLOSE_SYSTEM_WINDOWS_REASON_RECENTS); TOUCH_INTERACTION_LOG.addLog("startQuickstep"); diff --git a/quickstep/recents_ui_overrides/src/com/android/quickstep/inputconsumers/OverviewWithoutFocusInputConsumer.java b/quickstep/recents_ui_overrides/src/com/android/quickstep/inputconsumers/OverviewWithoutFocusInputConsumer.java index 05cbb789d9..e11d492331 100644 --- a/quickstep/recents_ui_overrides/src/com/android/quickstep/inputconsumers/OverviewWithoutFocusInputConsumer.java +++ b/quickstep/recents_ui_overrides/src/com/android/quickstep/inputconsumers/OverviewWithoutFocusInputConsumer.java @@ -34,10 +34,9 @@ import com.android.launcher3.BaseActivity; import com.android.launcher3.BaseDraggingActivity; import com.android.launcher3.Utilities; import com.android.launcher3.logging.StatsLogUtils; -import com.android.launcher3.userevent.nano.LauncherLogProto.ContainerType; import com.android.launcher3.userevent.nano.LauncherLogProto.Action.Direction; import com.android.launcher3.userevent.nano.LauncherLogProto.Action.Touch; -import com.android.quickstep.OverviewCallbacks; +import com.android.quickstep.ActivityControlHelper; import com.android.quickstep.util.NavBarPosition; import com.android.systemui.shared.system.ActivityManagerWrapper; import com.android.systemui.shared.system.InputMonitorCompat; @@ -50,16 +49,17 @@ public class OverviewWithoutFocusInputConsumer implements InputConsumer { private final float mSquaredTouchSlop; private final Context mContext; private final NavBarPosition mNavBarPosition; + private final ActivityControlHelper mActivityControlHelper; private boolean mInterceptedTouch; private VelocityTracker mVelocityTracker; - public OverviewWithoutFocusInputConsumer(Context context, InputMonitorCompat inputMonitor, - boolean disableHorizontalSwipe) { + ActivityControlHelper activityControlHelper, boolean disableHorizontalSwipe) { mInputMonitor = inputMonitor; mDisableHorizontalSwipe = disableHorizontalSwipe; mContext = context; + mActivityControlHelper = activityControlHelper; mSquaredTouchSlop = Utilities.squaredTouchSlop(context); mNavBarPosition = new NavBarPosition(context); @@ -148,7 +148,7 @@ public class OverviewWithoutFocusInputConsumer implements InputConsumer { } if (triggerQuickstep) { - OverviewCallbacks.get(mContext).closeAllWindows(); + mActivityControlHelper.closeOverlay(); ActivityManagerWrapper.getInstance() .closeSystemWindows(CLOSE_SYSTEM_WINDOWS_REASON_RECENTS); TOUCH_INTERACTION_LOG.addLog("startQuickstep"); diff --git a/quickstep/recents_ui_overrides/src/com/android/quickstep/util/NavBarPosition.java b/quickstep/recents_ui_overrides/src/com/android/quickstep/util/NavBarPosition.java index 3ce341d8cf..bbb318a027 100644 --- a/quickstep/recents_ui_overrides/src/com/android/quickstep/util/NavBarPosition.java +++ b/quickstep/recents_ui_overrides/src/com/android/quickstep/util/NavBarPosition.java @@ -21,9 +21,9 @@ import static com.android.quickstep.SysUINavigationMode.Mode.NO_BUTTON; import android.content.Context; import android.view.Surface; -import android.view.WindowManager; import com.android.launcher3.graphics.RotationMode; +import com.android.launcher3.util.DefaultDisplay; import com.android.quickstep.SysUINavigationMode; /** @@ -36,8 +36,7 @@ public class NavBarPosition { public NavBarPosition(Context context) { mMode = SysUINavigationMode.getMode(context); - mDisplayRotation = context.getSystemService(WindowManager.class) - .getDefaultDisplay().getRotation(); + mDisplayRotation = DefaultDisplay.INSTANCE.get(context).getInfo().rotation; } public boolean isRightEdge() { diff --git a/quickstep/recents_ui_overrides/src/com/android/quickstep/util/RecentsAnimationListenerSet.java b/quickstep/recents_ui_overrides/src/com/android/quickstep/util/RecentsAnimationListenerSet.java index 14083dd958..4299d1a84a 100644 --- a/quickstep/recents_ui_overrides/src/com/android/quickstep/util/RecentsAnimationListenerSet.java +++ b/quickstep/recents_ui_overrides/src/com/android/quickstep/util/RecentsAnimationListenerSet.java @@ -15,11 +15,14 @@ */ package com.android.quickstep.util; -import static com.android.quickstep.TouchInteractionService.MAIN_THREAD_EXECUTOR; +import static com.android.launcher3.util.Executors.MAIN_EXECUTOR; import android.graphics.Rect; import android.util.ArraySet; +import androidx.annotation.BinderThread; +import androidx.annotation.UiThread; + import com.android.launcher3.Utilities; import com.android.launcher3.util.Preconditions; import com.android.quickstep.util.SwipeAnimationTargetSet.SwipeAnimationListener; @@ -31,8 +34,6 @@ import com.android.systemui.shared.system.RemoteAnimationTargetCompat; import java.util.Set; import java.util.function.Consumer; -import androidx.annotation.UiThread; - /** * Wrapper around {@link RecentsAnimationListener} which delegates callbacks to multiple listeners * on the main thread @@ -70,19 +71,21 @@ public class RecentsAnimationListenerSet implements RecentsAnimationListener { mListeners.remove(listener); } - @Override + // Called only in R+ platform + @BinderThread public final void onAnimationStart(RecentsAnimationControllerCompat controller, - RemoteAnimationTargetCompat[] targets, Rect homeContentInsets, - Rect minimizedHomeBounds) { + RemoteAnimationTargetCompat[] appTargets, + RemoteAnimationTargetCompat[] wallpaperTargets, + Rect homeContentInsets, Rect minimizedHomeBounds) { mController = controller; - SwipeAnimationTargetSet targetSet = new SwipeAnimationTargetSet(controller, targets, + SwipeAnimationTargetSet targetSet = new SwipeAnimationTargetSet(controller, appTargets, homeContentInsets, minimizedHomeBounds, mShouldMinimizeSplitScreen, mOnFinishListener); if (mCancelled) { targetSet.cancelAnimation(); } else { - Utilities.postAsyncCallback(MAIN_THREAD_EXECUTOR.getHandler(), () -> { + Utilities.postAsyncCallback(MAIN_EXECUTOR.getHandler(), () -> { for (SwipeAnimationListener listener : getListeners()) { listener.onRecentsAnimationStart(targetSet); } @@ -90,16 +93,27 @@ public class RecentsAnimationListenerSet implements RecentsAnimationListener { } } + // Called only in Q platform + @BinderThread + @Deprecated + public final void onAnimationStart(RecentsAnimationControllerCompat controller, + RemoteAnimationTargetCompat[] appTargets, Rect homeContentInsets, + Rect minimizedHomeBounds) { + onAnimationStart(controller, appTargets, new RemoteAnimationTargetCompat[0], + homeContentInsets, minimizedHomeBounds); + } + + @BinderThread @Override public final void onAnimationCanceled(ThumbnailData thumbnailData) { - Utilities.postAsyncCallback(MAIN_THREAD_EXECUTOR.getHandler(), () -> { + Utilities.postAsyncCallback(MAIN_EXECUTOR.getHandler(), () -> { for (SwipeAnimationListener listener : getListeners()) { listener.onRecentsAnimationCanceled(); } }); // TODO: handle the transition better instead of simply using a transition delay. if (thumbnailData != null) { - MAIN_THREAD_EXECUTOR.getHandler().postDelayed(() -> mController.cleanupScreenshot(), + MAIN_EXECUTOR.getHandler().postDelayed(() -> mController.cleanupScreenshot(), TRANSITION_DELAY); } } diff --git a/quickstep/recents_ui_overrides/src/com/android/quickstep/util/SwipeAnimationTargetSet.java b/quickstep/recents_ui_overrides/src/com/android/quickstep/util/SwipeAnimationTargetSet.java index 381c27a284..3619d3a0e8 100644 --- a/quickstep/recents_ui_overrides/src/com/android/quickstep/util/SwipeAnimationTargetSet.java +++ b/quickstep/recents_ui_overrides/src/com/android/quickstep/util/SwipeAnimationTargetSet.java @@ -15,8 +15,8 @@ */ package com.android.quickstep.util; -import static com.android.quickstep.TouchInteractionService.BACKGROUND_EXECUTOR; -import static com.android.quickstep.TouchInteractionService.MAIN_THREAD_EXECUTOR; +import static com.android.launcher3.util.Executors.MAIN_EXECUTOR; +import static com.android.launcher3.util.Executors.UI_HELPER_EXECUTOR; import static com.android.systemui.shared.system.RemoteAnimationTargetCompat.MODE_CLOSING; import android.graphics.Rect; @@ -68,25 +68,25 @@ public class SwipeAnimationTargetSet extends RemoteAnimationTargetSet { public void finishController(boolean toRecents, Runnable callback, boolean sendUserLeaveHint) { mOnFinishListener.accept(this); - BACKGROUND_EXECUTOR.execute(() -> { + UI_HELPER_EXECUTOR.execute(() -> { controller.setInputConsumerEnabled(false); controller.finish(toRecents, sendUserLeaveHint); if (callback != null) { - MAIN_THREAD_EXECUTOR.execute(callback); + MAIN_EXECUTOR.execute(callback); } }); } public void enableInputConsumer() { - BACKGROUND_EXECUTOR.submit(() -> { + UI_HELPER_EXECUTOR.submit(() -> { controller.hideCurrentInputMethod(); controller.setInputConsumerEnabled(true); }); } public void setWindowThresholdCrossed(boolean thresholdCrossed) { - BACKGROUND_EXECUTOR.execute(() -> { + UI_HELPER_EXECUTOR.execute(() -> { controller.setAnimationTargetsBehindSystemBars(!thresholdCrossed); if (mShouldMinimizeSplitScreen && thresholdCrossed) { // NOTE: As a workaround for conflicting animations (Launcher animating the task diff --git a/quickstep/recents_ui_overrides/src/com/android/quickstep/views/DigitalWellBeingToast.java b/quickstep/recents_ui_overrides/src/com/android/quickstep/views/DigitalWellBeingToast.java index 7fac813854..b06d4bc355 100644 --- a/quickstep/recents_ui_overrides/src/com/android/quickstep/views/DigitalWellBeingToast.java +++ b/quickstep/recents_ui_overrides/src/com/android/quickstep/views/DigitalWellBeingToast.java @@ -19,6 +19,7 @@ package com.android.quickstep.views; import static android.provider.Settings.ACTION_APP_USAGE_SETTINGS; import static com.android.launcher3.Utilities.prefixTextWithIcon; +import static com.android.launcher3.util.Executors.THREAD_POOL_EXECUTOR; import android.annotation.TargetApi; import android.app.ActivityOptions; @@ -41,7 +42,6 @@ import androidx.annotation.StringRes; import com.android.launcher3.BaseActivity; import com.android.launcher3.BaseDraggingActivity; import com.android.launcher3.R; -import com.android.launcher3.Utilities; import com.android.launcher3.userevent.nano.LauncherLogProto; import com.android.systemui.shared.recents.model.Task; @@ -117,7 +117,7 @@ public final class DigitalWellBeingToast { return; } - Utilities.THREAD_POOL_EXECUTOR.execute(() -> { + THREAD_POOL_EXECUTOR.execute(() -> { final AppUsageLimit usageLimit = mLauncherApps.getAppUsageLimit( task.getTopComponent().getPackageName(), UserHandle.of(task.key.userId)); diff --git a/quickstep/recents_ui_overrides/src/com/android/quickstep/views/LauncherRecentsView.java b/quickstep/recents_ui_overrides/src/com/android/quickstep/views/LauncherRecentsView.java index 03441c87ec..c117361480 100644 --- a/quickstep/recents_ui_overrides/src/com/android/quickstep/views/LauncherRecentsView.java +++ b/quickstep/recents_ui_overrides/src/com/android/quickstep/views/LauncherRecentsView.java @@ -45,6 +45,7 @@ import com.android.launcher3.R; import com.android.launcher3.anim.Interpolators; import com.android.launcher3.appprediction.PredictionUiStateManager; import com.android.launcher3.appprediction.PredictionUiStateManager.Client; +import com.android.launcher3.uioverrides.DejankBinderTracker; import com.android.launcher3.views.ScrimView; import com.android.quickstep.SysUINavigationMode; import com.android.quickstep.util.ClipAnimationHelper; @@ -172,7 +173,7 @@ public class LauncherRecentsView extends RecentsView implements StateL @Override public boolean shouldUseMultiWindowTaskSizeStrategy() { - return mActivity.isInMultiWindowMode(); + return DejankBinderTracker.whitelistIpcs(() -> mActivity.isInMultiWindowMode()); } @Override diff --git a/quickstep/recents_ui_overrides/src/com/android/quickstep/views/RecentsView.java b/quickstep/recents_ui_overrides/src/com/android/quickstep/views/RecentsView.java index 3e1d61aca1..12b37cbd6f 100644 --- a/quickstep/recents_ui_overrides/src/com/android/quickstep/views/RecentsView.java +++ b/quickstep/recents_ui_overrides/src/com/android/quickstep/views/RecentsView.java @@ -35,9 +35,9 @@ import static com.android.launcher3.config.FeatureFlags.QUICKSTEP_SPRINGS; import static com.android.launcher3.uioverrides.touchcontrollers.TaskViewTouchController.SUCCESS_TRANSITION_PROGRESS; import static com.android.launcher3.userevent.nano.LauncherLogProto.Action.Touch.TAP; import static com.android.launcher3.userevent.nano.LauncherLogProto.ControlType.CLEAR_ALL_BUTTON; +import static com.android.launcher3.util.Executors.UI_HELPER_EXECUTOR; import static com.android.launcher3.util.SystemUiController.UI_STATE_OVERVIEW; import static com.android.quickstep.TaskUtils.checkCurrentOrManagedUserId; -import static com.android.quickstep.TouchInteractionService.BACKGROUND_EXECUTOR; import android.animation.Animator; import android.animation.AnimatorSet; @@ -228,7 +228,7 @@ public abstract class RecentsView extends PagedView impl return; } - BACKGROUND_EXECUTOR.execute(() -> { + UI_HELPER_EXECUTOR.execute(() -> { TaskView taskView = getTaskView(taskId); if (taskView == null) { return; @@ -306,6 +306,9 @@ public abstract class RecentsView extends PagedView impl private Layout mEmptyTextLayout; private LiveTileOverlay mLiveTileOverlay; + // Keeps track of the index where the first TaskView should be + private int mTaskViewStartIndex = 0; + private BaseActivity.MultiWindowModeChangedListener mMultiWindowModeChangedListener = (inMultiWindowMode) -> { if (!inMultiWindowMode && mOverviewStateEnabled) { @@ -329,7 +332,6 @@ public abstract class RecentsView extends PagedView impl mClearAllButton = (ClearAllButton) LayoutInflater.from(context) .inflate(R.layout.overview_clear_all_button, this, false); mClearAllButton.setOnClickListener(this::dismissAllTasks); - mTaskViewPool = new ViewPool<>(context, this, R.layout.task, 20 /* max size */, 10 /* initial size */); @@ -430,7 +432,7 @@ public abstract class RecentsView extends PagedView impl super.onViewRemoved(child); // Clear the task data for the removed child if it was visible - if (child != mClearAllButton) { + if (child instanceof TaskView) { TaskView taskView = (TaskView) child; mHasVisibleTaskData.delete(taskView.getTask().key.id); mTaskViewPool.recycle(taskView); @@ -444,7 +446,7 @@ public abstract class RecentsView extends PagedView impl public TaskView getTaskView(int taskId) { for (int i = 0; i < getTaskViewCount(); i++) { - TaskView tv = (TaskView) getChildAt(i); + TaskView tv = getTaskViewAt(i); if (tv.getTask() != null && tv.getTask().key != null && tv.getTask().key.id == taskId) { return tv; } @@ -536,28 +538,26 @@ public abstract class RecentsView extends PagedView impl } if (tasks == null || tasks.isEmpty()) { - removeAllViews(); + removeTasksViewsAndClearAllButton(); onTaskStackUpdated(); return; } - int oldChildCount = getChildCount(); - // Unload existing visible task data unloadVisibleTaskData(); - TaskView ignoreRestTaskView = + TaskView ignoreResetTaskView = mIgnoreResetTaskId == -1 ? null : getTaskView(mIgnoreResetTaskId); final int requiredTaskCount = tasks.size(); if (getTaskViewCount() != requiredTaskCount) { - if (oldChildCount > 0) { + if (indexOfChild(mClearAllButton) != -1) { removeView(mClearAllButton); } - for (int i = getChildCount(); i < requiredTaskCount; i++) { + for (int i = getTaskViewCount(); i < requiredTaskCount; i++) { addView(mTaskViewPool.getView()); } - while (getChildCount() > requiredTaskCount) { + while (getTaskViewCount() > requiredTaskCount) { removeView(getChildAt(getChildCount() - 1)); } if (requiredTaskCount > 0) { @@ -567,17 +567,20 @@ public abstract class RecentsView extends PagedView impl // Rebind and reset all task views for (int i = requiredTaskCount - 1; i >= 0; i--) { - final int pageIndex = requiredTaskCount - i - 1; + final int pageIndex = requiredTaskCount - i - 1 + mTaskViewStartIndex; final Task task = tasks.get(i); final TaskView taskView = (TaskView) getChildAt(pageIndex); taskView.bind(task); } + TaskView runningTaskView = getRunningTaskView(); if (runningTaskView != null) { setCurrentPage(indexOfChild(runningTaskView)); + } else if (getTaskViewCount() > 0) { + setCurrentPage(indexOfChild(getTaskViewAt(0))); } - if (mIgnoreResetTaskId != -1 && getTaskView(mIgnoreResetTaskId) != ignoreRestTaskView) { + if (mIgnoreResetTaskId != -1 && getTaskView(mIgnoreResetTaskId) != ignoreResetTaskView) { // If the taskView mapping is changing, do not preserve the visuals. Since we are // mostly preserving the first task, and new taskViews are added to the end, it should // generally map to the same task. @@ -588,17 +591,28 @@ public abstract class RecentsView extends PagedView impl updateEnabledOverlays(); } + private void removeTasksViewsAndClearAllButton() { + for (int i = getTaskViewCount() - 1; i >= 0; i--) { + removeView(getTaskViewAt(i)); + } + if (indexOfChild(mClearAllButton) != -1) { + removeView(mClearAllButton); + } + } + public int getTaskViewCount() { - // Account for the clear all button. - int childCount = getChildCount(); - return childCount == 0 ? 0 : childCount - 1; + int taskViewCount = getChildCount() - mTaskViewStartIndex; + if (indexOfChild(mClearAllButton) != -1) { + taskViewCount--; + } + return taskViewCount; } protected void onTaskStackUpdated() { } public void resetTaskVisuals() { for (int i = getTaskViewCount() - 1; i >= 0; i--) { - TaskView taskView = (TaskView) getChildAt(i); + TaskView taskView = getTaskViewAt(i); if (mIgnoreResetTaskId != taskView.getTask().key.id) { taskView.resetVisualProperties(); taskView.setStableAlpha(mContentAlpha); @@ -704,7 +718,7 @@ public abstract class RecentsView extends PagedView impl } /** - * Iterates through all thet asks, and loads the associated task data for newly visible tasks, + * Iterates through all the tasks, and loads the associated task data for newly visible tasks, * and unloads the associated task data for tasks that are no longer visible. */ public void loadVisibleTaskData() { @@ -715,15 +729,16 @@ public abstract class RecentsView extends PagedView impl } int centerPageIndex = getPageNearestToCenterOfScreen(); - int numChildren = getTaskViewCount(); + int numChildren = getChildCount(); int lower = Math.max(0, centerPageIndex - 2); int upper = Math.min(centerPageIndex + 2, numChildren - 1); // Update the task data for the in/visible children - for (int i = 0; i < numChildren; i++) { - TaskView taskView = (TaskView) getChildAt(i); + for (int i = 0; i < getTaskViewCount(); i++) { + TaskView taskView = getTaskViewAt(i); Task task = taskView.getTask(); - boolean visible = lower <= i && i <= upper; + int index = indexOfChild(taskView); + boolean visible = lower <= index && index <= upper; if (visible) { if (task == mTmpRunningTask) { // Skip loading if this is the task that we are animating into @@ -798,6 +813,10 @@ public abstract class RecentsView extends PagedView impl return tv == null ? -1 : indexOfChild(tv); } + public int getTaskViewStartIndex() { + return mTaskViewStartIndex; + } + /** * Reloads the view if anything in recents changed. */ @@ -853,10 +872,10 @@ public abstract class RecentsView extends PagedView impl */ public void showCurrentTask(int runningTaskId) { if (getTaskView(runningTaskId) == null) { - boolean wasEmpty = getChildCount() == 0; + boolean wasEmpty = getTaskViewCount() == 0; // Add an empty view for now until the task plan is loaded and applied final TaskView taskView = mTaskViewPool.getView(); - addView(taskView, 0); + addView(taskView, mTaskViewStartIndex); if (wasEmpty) { addView(mClearAllButton); } @@ -922,14 +941,13 @@ public abstract class RecentsView extends PagedView impl if (runningTaskView == null) { // Launch the first task if (getTaskViewCount() > 0) { - getTaskViewAt(0).launchTask(true /* animate */); + getTaskViewAt(0).launchTask(true); } } else { - TaskView nextTaskView = getNextTaskView(); - if (nextTaskView != null) { - nextTaskView.launchTask(true /* animate */); + if (getNextTaskView() != null) { + getNextTaskView().launchTask(true); } else { - runningTaskView.launchTask(true /* animate */); + runningTaskView.launchTask(true); } } } @@ -941,6 +959,10 @@ public abstract class RecentsView extends PagedView impl } } + public boolean isTaskIconScaledDown(TaskView taskView) { + return mRunningTaskIconScaledDown && getRunningTaskView() == taskView; + } + private void applyRunningTaskIconScale() { TaskView firstTask = getRunningTaskView(); if (firstTask != null) { @@ -1189,7 +1211,7 @@ public abstract class RecentsView extends PagedView impl int count = getTaskViewCount(); for (int i = 0; i < count; i++) { - addDismissedTaskAnimations(getChildAt(i), anim, duration); + addDismissedTaskAnimations(getTaskViewAt(i), anim, duration); } mPendingAnimation = pendingAnimation; @@ -1197,7 +1219,7 @@ public abstract class RecentsView extends PagedView impl if (onEndListener.isSuccess) { // Remove all the task views now ActivityManagerWrapper.getInstance().removeAllRecentTasks(); - removeAllViews(); + removeTasksViewsAndClearAllButton(); startHome(); } mPendingAnimation = null; @@ -1344,26 +1366,50 @@ public abstract class RecentsView extends PagedView impl child.setAlpha(mContentAlpha); } - /** - * @return The most recent task that is older than the currently running task. If there is - * currently no running task or there is no task older than it, then return null. - */ @Nullable public TaskView getNextTaskView() { - TaskView runningTaskView = getRunningTaskView(); - if (runningTaskView == null) { - return null; - } - return getTaskViewAt(indexOfChild(runningTaskView) + 1); + return getTaskViewAtByAbsoluteIndex(getRunningTaskIndex() + 1); } + @Nullable + public TaskView getPreviousTaskView() { + return getTaskViewAtByAbsoluteIndex(getRunningTaskIndex() - 1); + } + + @Nullable + public TaskView getCurrentPageTaskView() { + return getTaskViewAtByAbsoluteIndex(getCurrentPage()); + } + + @Nullable + public TaskView getNextPageTaskView() { + return getTaskViewAtByAbsoluteIndex(getNextPage()); + } + + @Nullable + public TaskView getTaskViewNearestToCenterOfScreen() { + return getTaskViewAtByAbsoluteIndex(getPageNearestToCenterOfScreen()); + } + + /** + * Returns null instead of indexOutOfBoundsError when index is not in range + */ + @Nullable public TaskView getTaskViewAt(int index) { - View child = getChildAt(index); - return child == mClearAllButton ? null : (TaskView) child; + return getTaskViewAtByAbsoluteIndex(index + mTaskViewStartIndex); + } + + @Nullable + private TaskView getTaskViewAtByAbsoluteIndex(int index) { + if (index < getChildCount() && index >= 0) { + View child = getChildAt(index); + return child instanceof TaskView ? (TaskView) child : null; + } + return null; } public void updateEmptyMessage() { - boolean isEmpty = getChildCount() == 0; + boolean isEmpty = getTaskViewCount() == 0; boolean hasSizeChanged = mLastMeasureSize.x != getWidth() || mLastMeasureSize.y != getHeight(); if (isEmpty == mShowEmptyMessage && !hasSizeChanged) { @@ -1497,7 +1543,7 @@ public abstract class RecentsView extends PagedView impl throw new IllegalStateException("Another pending animation is still running"); } - int count = getChildCount(); + int count = getTaskViewCount(); if (count == 0) { return new PendingAnimation(new AnimatorSet()); } @@ -1670,18 +1716,38 @@ public abstract class RecentsView extends PagedView impl @Override protected int computeMinScrollX() { - if (mIsRtl && mDisallowScrollToClearAll) { - // We aren't showing the clear all button, so use the leftmost task as the min scroll. - return getScrollForPage(getTaskViewCount() - 1); + if (getTaskViewCount() > 0) { + if (mDisallowScrollToClearAll) { + // We aren't showing the clear all button, + // so use the leftmost task as the min scroll. + if (mIsRtl) { + return getScrollForPage(indexOfChild(getTaskViewAt(getTaskViewCount() - 1))); + } + return getScrollForPage(mTaskViewStartIndex); + } + if (mIsRtl) { + return getScrollForPage(indexOfChild(getTaskViewAt(getTaskViewCount() - 1)) + 1); + } + return getScrollForPage(mTaskViewStartIndex); } return super.computeMinScrollX(); } @Override protected int computeMaxScrollX() { - if (!mIsRtl && mDisallowScrollToClearAll) { - // We aren't showing the clear all button, so use the rightmost task as the max scroll. - return getScrollForPage(getTaskViewCount() - 1); + if (getTaskViewCount() > 0) { + if (mDisallowScrollToClearAll) { + // We aren't showing the clear all button, + // so use the rightmost task as the min scroll. + if (mIsRtl) { + return getScrollForPage(mTaskViewStartIndex); + } + return getScrollForPage(indexOfChild(getTaskViewAt(getTaskViewCount() - 1))); + } + if (mIsRtl) { + return getScrollForPage(mTaskViewStartIndex); + } + return getScrollForPage(indexOfChild(getTaskViewAt(getTaskViewCount() - 1)) + 1); } return super.computeMaxScrollX(); } @@ -1733,7 +1799,7 @@ public abstract class RecentsView extends PagedView impl int overlayEnabledPage = mOverlayEnabled ? getNextPage() : -1; int taskCount = getTaskViewCount(); for (int i = 0; i < taskCount; i++) { - ((TaskView) getChildAt(i)).setOverlayEnabled(i == overlayEnabledPage); + getTaskViewAt(i).setOverlayEnabled(i == overlayEnabledPage); } } diff --git a/quickstep/recents_ui_overrides/src/com/android/quickstep/views/TaskThumbnailView.java b/quickstep/recents_ui_overrides/src/com/android/quickstep/views/TaskThumbnailView.java index 7f1e8980b9..2e6b662bf8 100644 --- a/quickstep/recents_ui_overrides/src/com/android/quickstep/views/TaskThumbnailView.java +++ b/quickstep/recents_ui_overrides/src/com/android/quickstep/views/TaskThumbnailView.java @@ -44,7 +44,6 @@ import com.android.launcher3.BaseActivity; import com.android.launcher3.DeviceProfile; import com.android.launcher3.R; import com.android.launcher3.Utilities; -import com.android.launcher3.config.FeatureFlags; import com.android.launcher3.util.SystemUiController; import com.android.launcher3.util.Themes; import com.android.quickstep.TaskOverlayFactory; @@ -308,8 +307,7 @@ public class TaskThumbnailView extends View { final Configuration configuration = getContext().getResources().getConfiguration(); // Rotate the screenshot if not in multi-window mode - isRotated = FeatureFlags.OVERVIEW_USE_SCREENSHOT_ORIENTATION && - configuration.orientation != mThumbnailData.orientation && + isRotated = configuration.orientation != mThumbnailData.orientation && !mActivity.isInMultiWindowMode() && mThumbnailData.windowingMode == WINDOWING_MODE_FULLSCREEN; // Scale the screenshot to always fit the width of the card. diff --git a/quickstep/recents_ui_overrides/src/com/android/quickstep/views/TaskView.java b/quickstep/recents_ui_overrides/src/com/android/quickstep/views/TaskView.java index 2211eb4dd7..bfb961320d 100644 --- a/quickstep/recents_ui_overrides/src/com/android/quickstep/views/TaskView.java +++ b/quickstep/recents_ui_overrides/src/com/android/quickstep/views/TaskView.java @@ -28,7 +28,6 @@ import android.animation.AnimatorListenerAdapter; import android.animation.ObjectAnimator; import android.animation.TimeInterpolator; import android.animation.ValueAnimator; -import android.animation.ValueAnimator.AnimatorUpdateListener; import android.app.ActivityOptions; import android.content.Context; import android.content.res.Resources; @@ -54,7 +53,6 @@ import com.android.launcher3.Utilities; import com.android.launcher3.anim.AnimatorPlaybackController; import com.android.launcher3.anim.Interpolators; import com.android.launcher3.logging.UserEventDispatcher; -import com.android.launcher3.testing.TestProtocol; import com.android.launcher3.userevent.nano.LauncherLogProto; import com.android.launcher3.userevent.nano.LauncherLogProto.Action.Direction; import com.android.launcher3.userevent.nano.LauncherLogProto.Action.Touch; @@ -818,8 +816,11 @@ public class TaskView extends FrameLayout implements PageCallbacks, Reusable { / (getWidth() + currentInsetsLeft + currentInsetsRight)); } - // Some of the items in here are dependent on the current fullscreen params - setIconScaleAndDim(progress, true /* invert */); + if (!getRecentsView().isTaskIconScaledDown(this)) { + // Some of the items in here are dependent on the current fullscreen params, but don't + // update them if the icon is supposed to be scaled down. + setIconScaleAndDim(progress, true /* invert */); + } thumbnail.setFullscreenParams(mCurrentFullscreenParams); mOutlineProvider.setFullscreenParams(mCurrentFullscreenParams); diff --git a/quickstep/res/values/config.xml b/quickstep/res/values/config.xml index 5c4d6d869f..98aaceb0bf 100644 --- a/quickstep/res/values/config.xml +++ b/quickstep/res/values/config.xml @@ -16,8 +16,6 @@ - - diff --git a/quickstep/src/com/android/launcher3/LauncherAnimationRunner.java b/quickstep/src/com/android/launcher3/LauncherAnimationRunner.java index a8e29569b1..a7aab6d080 100644 --- a/quickstep/src/com/android/launcher3/LauncherAnimationRunner.java +++ b/quickstep/src/com/android/launcher3/LauncherAnimationRunner.java @@ -28,12 +28,12 @@ import android.content.Context; import android.os.Build; import android.os.Handler; -import com.android.systemui.shared.system.RemoteAnimationRunnerCompat; -import com.android.systemui.shared.system.RemoteAnimationTargetCompat; - import androidx.annotation.BinderThread; import androidx.annotation.UiThread; +import com.android.systemui.shared.system.RemoteAnimationRunnerCompat; +import com.android.systemui.shared.system.RemoteAnimationTargetCompat; + @TargetApi(Build.VERSION_CODES.P) public abstract class LauncherAnimationRunner implements RemoteAnimationRunnerCompat { @@ -50,13 +50,14 @@ public abstract class LauncherAnimationRunner implements RemoteAnimationRunnerCo mStartAtFrontOfQueue = startAtFrontOfQueue; } + // Called only in R+ platform @BinderThread - @Override - public void onAnimationStart(RemoteAnimationTargetCompat[] targetCompats, Runnable runnable) { + public void onAnimationStart(RemoteAnimationTargetCompat[] appTargets, + RemoteAnimationTargetCompat[] wallpaperTargets, Runnable runnable) { Runnable r = () -> { finishExistingAnimation(); mAnimationResult = new AnimationResult(runnable); - onCreateAnimation(targetCompats, mAnimationResult); + onCreateAnimation(appTargets, mAnimationResult); }; if (mStartAtFrontOfQueue) { postAtFrontOfQueueAsynchronously(mHandler, r); @@ -65,6 +66,13 @@ public abstract class LauncherAnimationRunner implements RemoteAnimationRunnerCo } } + // Called only in Q platform + @BinderThread + @Deprecated + public void onAnimationStart(RemoteAnimationTargetCompat[] appTargets, Runnable runnable) { + onAnimationStart(appTargets, new RemoteAnimationTargetCompat[0], runnable); + } + /** * Called on the UI thread when the animation targets are received. The implementation must * call {@link AnimationResult#setAnimation} with the target animation to be run. diff --git a/quickstep/src/com/android/launcher3/LauncherInitListener.java b/quickstep/src/com/android/launcher3/LauncherInitListener.java index b9ce1ceee5..38f9956203 100644 --- a/quickstep/src/com/android/launcher3/LauncherInitListener.java +++ b/quickstep/src/com/android/launcher3/LauncherInitListener.java @@ -25,7 +25,6 @@ import android.os.Handler; import com.android.launcher3.states.InternalStateHandler; import com.android.quickstep.ActivityControlHelper.ActivityInitListener; -import com.android.quickstep.OverviewCallbacks; import com.android.quickstep.util.RemoteAnimationProvider; import java.util.function.BiPredicate; @@ -63,7 +62,7 @@ public class LauncherInitListener extends InternalStateHandler implements Activi return null; }, cancellationSignal); } - OverviewCallbacks.get(launcher).onInitOverviewTransition(); + launcher.deferOverlayCallbacksUntilNextResumeOrStop(); return mOnInitListener.test(launcher, alreadyOnHome); } diff --git a/quickstep/src/com/android/launcher3/uioverrides/DejankBinderTracker.java b/quickstep/src/com/android/launcher3/uioverrides/DejankBinderTracker.java new file mode 100644 index 0000000000..ff4c0f03d1 --- /dev/null +++ b/quickstep/src/com/android/launcher3/uioverrides/DejankBinderTracker.java @@ -0,0 +1,159 @@ +/** + * 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.uioverrides; + +import static android.os.IBinder.FLAG_ONEWAY; + +import android.os.Binder; +import android.os.Build; +import android.os.IBinder; +import android.os.Looper; +import android.os.RemoteException; +import android.util.Log; + +import androidx.annotation.MainThread; + +import java.util.HashSet; +import java.util.Locale; +import java.util.function.BiConsumer; +import java.util.function.Supplier; + +/** + * A binder proxy transaction listener for tracking non-whitelisted binder calls. + */ +public class DejankBinderTracker implements Binder.ProxyTransactListener { + private static final String TAG = "DejankBinderTracker"; + + private static final Object sLock = new Object(); + private static final HashSet sWhitelistedFrameworkClasses = new HashSet<>(); + static { + // Common IPCs that are ok to block the main thread. + sWhitelistedFrameworkClasses.add("android.view.IWindowSession"); + sWhitelistedFrameworkClasses.add("android.os.IPowerManager"); + } + private static boolean sTemporarilyIgnoreTracking = false; + + // Used by the client to limit binder tracking to specific regions + private static boolean sTrackingAllowed = false; + + private BiConsumer mUnexpectedTransactionCallback; + private boolean mIsTracking = false; + + /** + * Temporarily ignore blocking binder calls for the duration of this {@link Runnable}. + */ + @MainThread + public static void whitelistIpcs(Runnable runnable) { + sTemporarilyIgnoreTracking = true; + runnable.run(); + sTemporarilyIgnoreTracking = false; + } + + /** + * Temporarily ignore blocking binder calls for the duration of this {@link Supplier}. + */ + @MainThread + public static T whitelistIpcs(Supplier supplier) { + sTemporarilyIgnoreTracking = true; + T value = supplier.get(); + sTemporarilyIgnoreTracking = false; + return value; + } + + /** + * Enables binder tracking during a test. + */ + @MainThread + public static void allowBinderTrackingInTests() { + sTrackingAllowed = true; + } + + /** + * Disables binder tracking during a test. + */ + @MainThread + public static void disallowBinderTrackingInTests() { + sTrackingAllowed = false; + } + + public DejankBinderTracker(BiConsumer unexpectedTransactionCallback) { + mUnexpectedTransactionCallback = unexpectedTransactionCallback; + } + + @MainThread + public void startTracking() { + if (Build.TYPE.toLowerCase(Locale.ROOT).contains("debug") + || Build.TYPE.toLowerCase(Locale.ROOT).equals("eng")) { + Log.wtf(TAG, "Unexpected use of binder tracker in non-debug build", new Exception()); + return; + } + if (mIsTracking) { + return; + } + mIsTracking = true; + Binder.setProxyTransactListener(this); + } + + @MainThread + public void stopTracking() { + if (!mIsTracking) { + return; + } + mIsTracking = false; + Binder.setProxyTransactListener(null); + } + + // Override the hidden Binder#onTransactStarted method + public synchronized Object onTransactStarted(IBinder binder, int transactionCode, int flags) { + if (!mIsTracking + || !sTrackingAllowed + || sTemporarilyIgnoreTracking + || (flags & FLAG_ONEWAY) == FLAG_ONEWAY + || !isMainThread()) { + return null; + } + + String descriptor; + try { + descriptor = binder.getInterfaceDescriptor(); + if (sWhitelistedFrameworkClasses.contains(descriptor)) { + return null; + } + } catch (RemoteException e) { + e.printStackTrace(); + descriptor = binder.getClass().getSimpleName(); + } + + mUnexpectedTransactionCallback.accept(descriptor, transactionCode); + return null; + } + + @Override + public Object onTransactStarted(IBinder binder, int transactionCode) { + // Do nothing + return null; + } + + @Override + public void onTransactEnded(Object session) { + // Do nothing + } + + public static boolean isMainThread() { + return Thread.currentThread() == Looper.getMainLooper().getThread(); + } +} diff --git a/quickstep/src/com/android/launcher3/uioverrides/TogglableFlag.java b/quickstep/src/com/android/launcher3/uioverrides/TogglableFlag.java index 853a1c6acb..27d81eff06 100644 --- a/quickstep/src/com/android/launcher3/uioverrides/TogglableFlag.java +++ b/quickstep/src/com/android/launcher3/uioverrides/TogglableFlag.java @@ -18,7 +18,8 @@ package com.android.launcher3.uioverrides; import android.content.Context; import android.provider.DeviceConfig; -import com.android.launcher3.config.BaseFlags.BaseTogglableFlag; + +import com.android.launcher3.config.FeatureFlags.BaseTogglableFlag; public class TogglableFlag extends BaseTogglableFlag { public static final String NAMESPACE_LAUNCHER = "launcher"; @@ -36,14 +37,14 @@ public class TogglableFlag extends BaseTogglableFlag { @Override public void addChangeListener(Context context, Runnable r) { DeviceConfig.addOnPropertiesChangedListener( - NAMESPACE_LAUNCHER, - context.getMainExecutor(), - (properties) -> { - if (!NAMESPACE_LAUNCHER.equals(properties.getNamespace())) { - return; - } - initialize(context); - r.run(); - }); + NAMESPACE_LAUNCHER, + context.getMainExecutor(), + (properties) -> { + if (!NAMESPACE_LAUNCHER.equals(properties.getNamespace())) { + return; + } + initialize(context); + r.run(); + }); } } diff --git a/quickstep/src/com/android/launcher3/uioverrides/plugins/PluginInitializerImpl.java b/quickstep/src/com/android/launcher3/uioverrides/plugins/PluginInitializerImpl.java index 910fa0df6c..7beb9db841 100644 --- a/quickstep/src/com/android/launcher3/uioverrides/plugins/PluginInitializerImpl.java +++ b/quickstep/src/com/android/launcher3/uioverrides/plugins/PluginInitializerImpl.java @@ -14,16 +14,17 @@ package com.android.launcher3.uioverrides.plugins; +import static com.android.launcher3.util.Executors.MODEL_EXECUTOR; + import android.content.Context; import android.os.Looper; -import com.android.launcher3.LauncherModel; import com.android.systemui.shared.plugins.PluginInitializer; public class PluginInitializerImpl implements PluginInitializer { @Override public Looper getBgLooper() { - return LauncherModel.getWorkerLooper(); + return MODEL_EXECUTOR.getLooper(); } @Override diff --git a/quickstep/src/com/android/quickstep/ActivityControlHelper.java b/quickstep/src/com/android/quickstep/ActivityControlHelper.java index 5c9c7d4cab..609fb26d40 100644 --- a/quickstep/src/com/android/quickstep/ActivityControlHelper.java +++ b/quickstep/src/com/android/quickstep/ActivityControlHelper.java @@ -97,6 +97,8 @@ public interface ActivityControlHelper { void onLaunchTaskSuccess(T activity); + default void closeOverlay() { } + interface ActivityInitListener { void register(); diff --git a/quickstep/src/com/android/quickstep/BaseRecentsActivity.java b/quickstep/src/com/android/quickstep/BaseRecentsActivity.java index c84013257b..1ac7ed4a3c 100644 --- a/quickstep/src/com/android/quickstep/BaseRecentsActivity.java +++ b/quickstep/src/com/android/quickstep/BaseRecentsActivity.java @@ -157,6 +157,6 @@ public abstract class BaseRecentsActivity extends BaseDraggingActivity { public void dump(String prefix, FileDescriptor fd, PrintWriter writer, String[] args) { super.dump(prefix, fd, writer, args); writer.println(prefix + "Misc:"); - dumpMisc(writer); + dumpMisc(prefix + "\t", writer); } } diff --git a/quickstep/src/com/android/quickstep/OverviewCallbacks.java b/quickstep/src/com/android/quickstep/OverviewCallbacks.java deleted file mode 100644 index f5573baa7f..0000000000 --- a/quickstep/src/com/android/quickstep/OverviewCallbacks.java +++ /dev/null @@ -1,43 +0,0 @@ -/* - * Copyright (C) 2018 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.quickstep; - -import android.content.Context; - -import com.android.launcher3.R; -import com.android.launcher3.util.Preconditions; -import com.android.launcher3.util.ResourceBasedOverride; - -/** - * Callbacks related to overview/quicksteps. - */ -public class OverviewCallbacks implements ResourceBasedOverride { - - private static OverviewCallbacks sInstance; - - public static OverviewCallbacks get(Context context) { - Preconditions.assertUIThread(); - if (sInstance == null) { - sInstance = Overrides.getObject(OverviewCallbacks.class, - context.getApplicationContext(), R.string.overview_callbacks_class); - } - return sInstance; - } - - public void onInitOverviewTransition() { } - - public void closeAllWindows() { } -} diff --git a/quickstep/src/com/android/quickstep/OverviewInteractionState.java b/quickstep/src/com/android/quickstep/OverviewInteractionState.java index 78b48d77a4..858c3b6b3f 100644 --- a/quickstep/src/com/android/quickstep/OverviewInteractionState.java +++ b/quickstep/src/com/android/quickstep/OverviewInteractionState.java @@ -15,20 +15,21 @@ */ package com.android.quickstep; +import static com.android.launcher3.util.Executors.UI_HELPER_EXECUTOR; + import android.content.Context; import android.os.Handler; import android.os.Message; import android.os.RemoteException; import android.util.Log; +import androidx.annotation.WorkerThread; + import com.android.launcher3.Utilities; import com.android.launcher3.allapps.DiscoveryBounce; import com.android.launcher3.util.MainThreadInitializedObject; -import com.android.launcher3.util.UiThreadHelper; import com.android.systemui.shared.recents.ISystemUiProxy; -import androidx.annotation.WorkerThread; - /** * Sets alpha for the back button */ @@ -62,7 +63,7 @@ public class OverviewInteractionState { // because of its high send frequency and data may be very different than the previous value // For example, send back alpha on uihandler to avoid flickering when setting its visibility mUiHandler = new Handler(this::handleUiMessage); - mBgHandler = new Handler(UiThreadHelper.getBackgroundLooper(), this::handleBgMessage); + mBgHandler = new Handler(UI_HELPER_EXECUTOR.getLooper(), this::handleBgMessage); onNavigationModeChanged(SysUINavigationMode.INSTANCE.get(context) .addModeChangeListener(this::onNavigationModeChanged)); diff --git a/quickstep/src/com/android/quickstep/RecentTasksList.java b/quickstep/src/com/android/quickstep/RecentTasksList.java index e41dba94cc..10f9febfb7 100644 --- a/quickstep/src/com/android/quickstep/RecentTasksList.java +++ b/quickstep/src/com/android/quickstep/RecentTasksList.java @@ -16,19 +16,22 @@ package com.android.quickstep; -import static com.android.quickstep.TouchInteractionService.BACKGROUND_EXECUTOR; +import static com.android.launcher3.util.Executors.UI_HELPER_EXECUTOR; import android.annotation.TargetApi; import android.app.ActivityManager; -import android.content.Context; import android.os.Build; import android.os.Process; import android.util.SparseBooleanArray; -import com.android.launcher3.MainThreadExecutor; + +import androidx.annotation.VisibleForTesting; + +import com.android.launcher3.util.LooperExecutor; import com.android.systemui.shared.recents.model.Task; import com.android.systemui.shared.system.ActivityManagerWrapper; import com.android.systemui.shared.system.KeyguardManagerCompat; import com.android.systemui.shared.system.TaskStackChangeListener; + import java.util.ArrayList; import java.util.Collections; import java.util.List; @@ -41,7 +44,8 @@ import java.util.function.Consumer; public class RecentTasksList extends TaskStackChangeListener { private final KeyguardManagerCompat mKeyguardManager; - private final MainThreadExecutor mMainThreadExecutor; + private final LooperExecutor mMainThreadExecutor; + private final ActivityManagerWrapper mActivityManagerWrapper; // The list change id, increments as the task list changes in the system private int mChangeId; @@ -52,11 +56,13 @@ public class RecentTasksList extends TaskStackChangeListener { ArrayList mTasks = new ArrayList<>(); - public RecentTasksList(Context context) { - mMainThreadExecutor = new MainThreadExecutor(); - mKeyguardManager = new KeyguardManagerCompat(context); + public RecentTasksList(LooperExecutor mainThreadExecutor, + KeyguardManagerCompat keyguardManager, ActivityManagerWrapper activityManagerWrapper) { + mMainThreadExecutor = mainThreadExecutor; + mKeyguardManager = keyguardManager; mChangeId = 1; - ActivityManagerWrapper.getInstance().registerTaskStackListener(this); + mActivityManagerWrapper = activityManagerWrapper; + mActivityManagerWrapper.registerTaskStackListener(this); } /** @@ -64,7 +70,7 @@ public class RecentTasksList extends TaskStackChangeListener { */ public void getTaskKeys(int numTasks, Consumer> callback) { // Kick off task loading in the background - BACKGROUND_EXECUTOR.execute(() -> { + UI_HELPER_EXECUTOR.execute(() -> { ArrayList tasks = loadTasksInBackground(numTasks, true /* loadKeysOnly */); mMainThreadExecutor.execute(() -> callback.accept(tasks)); }); @@ -86,12 +92,12 @@ public class RecentTasksList extends TaskStackChangeListener { if (mLastLoadedId == mChangeId && (!mLastLoadHadKeysOnly || loadKeysOnly)) { // The list is up to date, send the callback on the next frame, // so that requestID can be returned first. - mMainThreadExecutor.getHandler().post(resultCallback); + mMainThreadExecutor.post(resultCallback); return requestLoadId; } // Kick off task loading in the background - BACKGROUND_EXECUTOR.execute(() -> { + UI_HELPER_EXECUTOR.execute(() -> { ArrayList tasks = loadTasksInBackground(Integer.MAX_VALUE, loadKeysOnly); mMainThreadExecutor.execute(() -> { @@ -136,12 +142,13 @@ public class RecentTasksList extends TaskStackChangeListener { /** * Loads and creates a list of all the recent tasks. */ - private ArrayList loadTasksInBackground(int numTasks, + @VisibleForTesting + ArrayList loadTasksInBackground(int numTasks, boolean loadKeysOnly) { int currentUserId = Process.myUserHandle().getIdentifier(); ArrayList allTasks = new ArrayList<>(); List rawTasks = - ActivityManagerWrapper.getInstance().getRecentTasks(numTasks, currentUserId); + mActivityManagerWrapper.getRecentTasks(numTasks, currentUserId); // The raw tasks are given in most-recent to least-recent order, we need to reverse it Collections.reverse(rawTasks); diff --git a/quickstep/src/com/android/quickstep/RecentsActivityTracker.java b/quickstep/src/com/android/quickstep/RecentsActivityTracker.java index f9d2f11cba..4d1d9ef8a7 100644 --- a/quickstep/src/com/android/quickstep/RecentsActivityTracker.java +++ b/quickstep/src/com/android/quickstep/RecentsActivityTracker.java @@ -15,6 +15,8 @@ */ package com.android.quickstep; +import static com.android.launcher3.util.Executors.MAIN_EXECUTOR; + import android.annotation.TargetApi; import android.content.Context; import android.content.Intent; @@ -22,7 +24,6 @@ import android.os.Build; import android.os.Bundle; import android.os.Handler; -import com.android.launcher3.MainThreadExecutor; import com.android.quickstep.ActivityControlHelper.ActivityInitListener; import com.android.quickstep.util.RemoteAnimationProvider; @@ -92,14 +93,10 @@ public class RecentsActivityTracker implements Ac private static class Scheduler implements Runnable { private WeakReference mPendingTracker = new WeakReference<>(null); - private MainThreadExecutor mMainThreadExecutor; public synchronized void schedule(RecentsActivityTracker tracker) { mPendingTracker = new WeakReference<>(tracker); - if (mMainThreadExecutor == null) { - mMainThreadExecutor = new MainThreadExecutor(); - } - mMainThreadExecutor.execute(this); + MAIN_EXECUTOR.execute(this); } @Override diff --git a/quickstep/src/com/android/quickstep/RecentsModel.java b/quickstep/src/com/android/quickstep/RecentsModel.java index dfab43459d..2e59ed5e48 100644 --- a/quickstep/src/com/android/quickstep/RecentsModel.java +++ b/quickstep/src/com/android/quickstep/RecentsModel.java @@ -15,6 +15,10 @@ */ package com.android.quickstep; +import static android.os.Process.THREAD_PRIORITY_BACKGROUND; + +import static com.android.launcher3.util.Executors.MAIN_EXECUTOR; +import static com.android.launcher3.util.Executors.createAndStartNewLooper; import static com.android.quickstep.TaskUtils.checkCurrentOrManagedUserId; import android.annotation.TargetApi; @@ -22,16 +26,20 @@ import android.app.ActivityManager; import android.content.ComponentCallbacks2; import android.content.Context; import android.os.Build; -import android.os.HandlerThread; +import android.os.Looper; import android.os.Process; import android.os.RemoteException; +import android.os.UserHandle; import android.util.Log; +import com.android.launcher3.compat.LauncherAppsCompat; +import com.android.launcher3.compat.LauncherAppsCompat.OnAppsChangedCallbackCompat; import com.android.launcher3.util.MainThreadInitializedObject; import com.android.systemui.shared.recents.ISystemUiProxy; import com.android.systemui.shared.recents.model.Task; import com.android.systemui.shared.recents.model.ThumbnailData; import com.android.systemui.shared.system.ActivityManagerWrapper; +import com.android.systemui.shared.system.KeyguardManagerCompat; import com.android.systemui.shared.system.TaskStackChangeListener; import java.util.ArrayList; @@ -61,13 +69,14 @@ public class RecentsModel extends TaskStackChangeListener { private RecentsModel(Context context) { mContext = context; - HandlerThread loaderThread = new HandlerThread("TaskThumbnailIconCache", - Process.THREAD_PRIORITY_BACKGROUND); - loaderThread.start(); - mTaskList = new RecentTasksList(context); - mIconCache = new TaskIconCache(context, loaderThread.getLooper()); - mThumbnailCache = new TaskThumbnailCache(context, loaderThread.getLooper()); + Looper looper = + createAndStartNewLooper("TaskThumbnailIconCache", THREAD_PRIORITY_BACKGROUND); + mTaskList = new RecentTasksList(MAIN_EXECUTOR, + new KeyguardManagerCompat(context), ActivityManagerWrapper.getInstance()); + mIconCache = new TaskIconCache(context, looper); + mThumbnailCache = new TaskThumbnailCache(context, looper); ActivityManagerWrapper.getInstance().registerTaskStackListener(this); + setupPackageListener(); } public TaskIconCache getIconCache() { @@ -166,6 +175,7 @@ public class RecentsModel extends TaskStackChangeListener { public void onTaskRemoved(int taskId) { Task.TaskKey dummyKey = new Task.TaskKey(taskId, 0, null, null, 0, 0); mThumbnailCache.remove(dummyKey); + mIconCache.onTaskRemoved(dummyKey); } public void setSystemUiProxy(ISystemUiProxy systemUiProxy) { @@ -200,6 +210,21 @@ public class RecentsModel extends TaskStackChangeListener { } } + private void setupPackageListener() { + LauncherAppsCompat.getInstance(mContext) + .addOnAppsChangedCallback(new OnAppsChangedCallbackCompat() { + @Override + public void onPackageRemoved(String packageName, UserHandle user) { + mIconCache.invalidatePackage(packageName); + } + + @Override + public void onPackageChanged(String packageName, UserHandle user) { + mIconCache.invalidatePackage(packageName); + } + }); + } + public void addThumbnailChangeListener(TaskThumbnailChangeListener listener) { mThumbnailChangeListeners.add(listener); } diff --git a/quickstep/src/com/android/quickstep/TaskIconCache.java b/quickstep/src/com/android/quickstep/TaskIconCache.java index 07af9b3858..289a129706 100644 --- a/quickstep/src/com/android/quickstep/TaskIconCache.java +++ b/quickstep/src/com/android/quickstep/TaskIconCache.java @@ -16,6 +16,7 @@ package com.android.quickstep; import static com.android.launcher3.uioverrides.RecentsUiFactory.GO_LOW_RAM_RECENTS_ENABLED; +import static com.android.launcher3.util.Executors.MAIN_EXECUTOR; import android.content.ComponentName; import android.content.Context; @@ -27,26 +28,25 @@ import android.os.Looper; import android.util.LruCache; import android.view.accessibility.AccessibilityManager; -import com.android.launcher3.MainThreadExecutor; import com.android.launcher3.R; import com.android.launcher3.Utilities; import com.android.launcher3.icons.cache.HandlerRunnable; -import com.android.launcher3.uioverrides.RecentsUiFactory; import com.android.launcher3.util.Preconditions; import com.android.systemui.shared.recents.model.Task; +import com.android.systemui.shared.recents.model.Task.TaskKey; import com.android.systemui.shared.recents.model.TaskKeyLruCache; import com.android.systemui.shared.system.ActivityManagerWrapper; +import java.util.Map; import java.util.function.Consumer; /** * Manages the caching of task icons and related data. - * TODO: This class should later be merged into IconCache. + * TODO(b/138944598): This class should later be merged into IconCache. */ public class TaskIconCache { private final Handler mBackgroundHandler; - private final MainThreadExecutor mMainThreadExecutor; private final AccessibilityManager mAccessibilityManager; private final NormalizedIconLoader mIconLoader; @@ -67,7 +67,6 @@ public class TaskIconCache { public TaskIconCache(Context context, Looper backgroundLooper) { mBackgroundHandler = new Handler(backgroundLooper); - mMainThreadExecutor = new MainThreadExecutor(); mAccessibilityManager = context.getSystemService(AccessibilityManager.class); Resources res = context.getResources(); @@ -103,7 +102,7 @@ public class TaskIconCache { // We don't call back to the provided callback in this case return; } - mMainThreadExecutor.execute(() -> { + MAIN_EXECUTOR.execute(() -> { task.icon = icon; task.titleDescription = contentDescription; callback.accept(task); @@ -149,6 +148,21 @@ public class TaskIconCache { return label; } + + void onTaskRemoved(TaskKey taskKey) { + mIconCache.remove(taskKey); + } + + void invalidatePackage(String packageName) { + // TODO(b/138944598): Merge this class into IconCache so we can do this at the base level + Map activityInfoCache = mActivityInfoCache.snapshot(); + for (ComponentName cn : activityInfoCache.keySet()) { + if (cn.getPackageName().equals(packageName)) { + mActivityInfoCache.remove(cn); + } + } + } + public static abstract class IconLoadRequest extends HandlerRunnable { IconLoadRequest(Handler handler) { super(handler, null); diff --git a/quickstep/src/com/android/quickstep/TaskThumbnailCache.java b/quickstep/src/com/android/quickstep/TaskThumbnailCache.java index 57c5a27833..3b50c2623b 100644 --- a/quickstep/src/com/android/quickstep/TaskThumbnailCache.java +++ b/quickstep/src/com/android/quickstep/TaskThumbnailCache.java @@ -15,12 +15,14 @@ */ package com.android.quickstep; +import static com.android.launcher3.util.Executors.MAIN_EXECUTOR; + import android.app.ActivityManager; import android.content.Context; import android.content.res.Resources; import android.os.Handler; import android.os.Looper; -import com.android.launcher3.MainThreadExecutor; + import com.android.launcher3.R; import com.android.launcher3.Utilities; import com.android.launcher3.icons.cache.HandlerRunnable; @@ -30,13 +32,13 @@ import com.android.systemui.shared.recents.model.Task.TaskKey; import com.android.systemui.shared.recents.model.TaskKeyLruCache; import com.android.systemui.shared.recents.model.ThumbnailData; import com.android.systemui.shared.system.ActivityManagerWrapper; + import java.util.ArrayList; import java.util.function.Consumer; public class TaskThumbnailCache { private final Handler mBackgroundHandler; - private final MainThreadExecutor mMainThreadExecutor; private final int mCacheSize; private final ThumbnailCache mCache; @@ -94,7 +96,6 @@ public class TaskThumbnailCache { public TaskThumbnailCache(Context context, Looper backgroundLooper) { mBackgroundHandler = new Handler(backgroundLooper); - mMainThreadExecutor = new MainThreadExecutor(); mHighResLoadingState = new HighResLoadingState(context); Resources res = context.getResources(); @@ -168,7 +169,7 @@ public class TaskThumbnailCache { // We don't call back to the provided callback in this case return; } - mMainThreadExecutor.execute(() -> { + MAIN_EXECUTOR.execute(() -> { mCache.put(key, thumbnail); callback.accept(thumbnail); onEnd(); diff --git a/quickstep/tests/src/com/android/quickstep/AppPredictionsUITests.java b/quickstep/tests/src/com/android/quickstep/AppPredictionsUITests.java index d0956d1f6d..7801775d48 100644 --- a/quickstep/tests/src/com/android/quickstep/AppPredictionsUITests.java +++ b/quickstep/tests/src/com/android/quickstep/AppPredictionsUITests.java @@ -150,10 +150,10 @@ public class AppPredictionsUITests extends AbstractQuickStepTest { List targets = new ArrayList<>(activities.length); for (LauncherActivityInfo info : activities) { ComponentName cn = info.getComponentName(); - AppTarget target = - new AppTarget.Builder(new AppTargetId("app:" + cn), cn.getPackageName(), info.getUser()) - .setClassName(cn.getClassName()) - .build(); + AppTarget target = new AppTarget.Builder( + new AppTargetId("app:" + cn), cn.getPackageName(), info.getUser()) + .setClassName(cn.getClassName()) + .build(); targets.add(target); } mCallback.onTargetsAvailable(targets); diff --git a/quickstep/tests/src/com/android/quickstep/FallbackRecentsTest.java b/quickstep/tests/src/com/android/quickstep/FallbackRecentsTest.java index e5f949b880..a15fc3e287 100644 --- a/quickstep/tests/src/com/android/quickstep/FallbackRecentsTest.java +++ b/quickstep/tests/src/com/android/quickstep/FallbackRecentsTest.java @@ -22,11 +22,18 @@ import static androidx.test.InstrumentationRegistry.getInstrumentation; import static com.android.launcher3.tapl.LauncherInstrumentation.WAIT_TIME_MS; import static com.android.launcher3.tapl.TestHelpers.getHomeIntentInPackage; import static com.android.launcher3.tapl.TestHelpers.getLauncherInMyProcess; +import static com.android.launcher3.ui.AbstractLauncherUiTest.DEFAULT_UI_TIMEOUT; import static com.android.launcher3.ui.AbstractLauncherUiTest.resolveSystemApp; +import static com.android.launcher3.ui.AbstractLauncherUiTest.startAppFast; +import static com.android.launcher3.ui.AbstractLauncherUiTest.startTestActivity; +import static com.android.launcher3.ui.TaplTestsLauncher3.getAppPackageName; +import static com.android.launcher3.util.Executors.MAIN_EXECUTOR; import static com.android.launcher3.util.rule.ShellCommandRule.disableHeadsUpNotification; import static com.android.launcher3.util.rule.ShellCommandRule.getLauncherCommand; -import static com.android.quickstep.NavigationModeSwitchRule.Mode.THREE_BUTTON; +import static com.android.quickstep.NavigationModeSwitchRule.Mode.ZERO_BUTTON; +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertNotNull; import static org.junit.Assert.assertTrue; import android.app.Instrumentation; @@ -41,10 +48,16 @@ import androidx.test.uiautomator.By; import androidx.test.uiautomator.UiDevice; import androidx.test.uiautomator.Until; +import com.android.launcher3.Utilities; +import com.android.launcher3.tapl.BaseOverview; import com.android.launcher3.tapl.LauncherInstrumentation; +import com.android.launcher3.tapl.OverviewTask; +import com.android.launcher3.tapl.TestHelpers; import com.android.launcher3.testcomponent.TestCommandReceiver; import com.android.launcher3.util.rule.FailureWatcher; +import com.android.launcher3.util.rule.SimpleActivityRule; import com.android.quickstep.NavigationModeSwitchRule.NavigationModeSwitch; +import com.android.quickstep.views.RecentsView; import org.junit.Rule; import org.junit.Test; @@ -53,11 +66,11 @@ import org.junit.rules.TestRule; import org.junit.runner.RunWith; import org.junit.runners.model.Statement; +import java.util.function.Consumer; +import java.util.function.Function; + @LargeTest @RunWith(AndroidJUnit4.class) -/** - * TODO: Fix fallback when quickstep is enabled - */ public class FallbackRecentsTest { private final UiDevice mDevice; @@ -73,6 +86,11 @@ public class FallbackRecentsTest { @Rule public final TestRule mOrderSensitiveRules; + @Rule + public final SimpleActivityRule mActivityMonitor = + new SimpleActivityRule(RecentsActivity.class); + + public FallbackRecentsTest() throws RemoteException { Instrumentation instrumentation = getInstrumentation(); Context context = instrumentation.getContext(); @@ -80,9 +98,12 @@ public class FallbackRecentsTest { mDevice.setOrientationNatural(); mLauncher = new LauncherInstrumentation(instrumentation); - mOrderSensitiveRules = RuleChain. - outerRule(new NavigationModeSwitchRule(mLauncher)). - around(new FailureWatcher(mDevice)); + if (TestHelpers.isInLauncherProcess()) { + Utilities.enableRunningInTestHarnessForTests(); + } + + mOrderSensitiveRules = RuleChain.outerRule(new NavigationModeSwitchRule(mLauncher)) + .around(new FailureWatcher(mDevice)); mOtherLauncherActivity = context.getPackageManager().queryIntentActivities( getHomeIntentInPackage(context), @@ -105,7 +126,7 @@ public class FallbackRecentsTest { }; } - @NavigationModeSwitch(mode = THREE_BUTTON) + @NavigationModeSwitch @Test public void goToOverviewFromHome() { mDevice.pressHome(); @@ -115,7 +136,7 @@ public class FallbackRecentsTest { mLauncher.getBackground().switchToOverview(); } - @NavigationModeSwitch(mode = THREE_BUTTON) + @NavigationModeSwitch @Test public void goToOverviewFromApp() { startAppFast(resolveSystemApp(Intent.CATEGORY_APP_CALCULATOR)); @@ -123,15 +144,77 @@ public class FallbackRecentsTest { mLauncher.getBackground().switchToOverview(); } - private void startAppFast(String packageName) { - final Instrumentation instrumentation = getInstrumentation(); - final Intent intent = instrumentation.getContext().getPackageManager(). - getLaunchIntentForPackage(packageName); - intent.addCategory(Intent.CATEGORY_LAUNCHER); - intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK); - instrumentation.getTargetContext().startActivity(intent); - assertTrue(packageName + " didn't start", - mDevice.wait(Until.hasObject(By.pkg(packageName).depth(0)), WAIT_TIME_MS)); + protected void executeOnRecents(Consumer f) throws Exception { + getFromRecents(r -> { + f.accept(r); + return null; + }); } + protected T getFromRecents(Function f) throws Exception { + if (!TestHelpers.isInLauncherProcess()) return null; + return MAIN_EXECUTOR.submit(() -> f.apply(mActivityMonitor.getActivity())).get(); + } + + private BaseOverview pressHomeAndGoToOverview() { + mDevice.pressHome(); + return mLauncher.getBackground().switchToOverview(); + } + + // TODO: Enable all modes after b/141184247 is fixed + @NavigationModeSwitch(mode = ZERO_BUTTON) + @Test + public void testOverview() throws Exception { + startAppFast(getAppPackageName()); + startAppFast(resolveSystemApp(Intent.CATEGORY_APP_CALCULATOR)); + startTestActivity(2); + + BaseOverview overview = mLauncher.getBackground().switchToOverview(); + executeOnRecents( + recents -> assertTrue("Don't have at least 3 tasks", getTaskCount(recents) >= 3)); + + // Test flinging forward and backward. + overview.flingForward(); + final Integer currentTaskAfterFlingForward = getFromRecents(this::getCurrentOverviewPage); + executeOnRecents(recents -> assertTrue("Current task in Overview is still 0", + currentTaskAfterFlingForward > 0)); + + overview.flingBackward(); + executeOnRecents(recents -> assertTrue("Flinging back in Overview did nothing", + getCurrentOverviewPage(recents) < currentTaskAfterFlingForward)); + + // Test opening a task. + overview = pressHomeAndGoToOverview(); + + OverviewTask task = overview.getCurrentTask(); + assertNotNull("overview.getCurrentTask() returned null (1)", task); + assertNotNull("OverviewTask.open returned null", task.open()); + assertTrue("Test activity didn't open from Overview", mDevice.wait(Until.hasObject( + By.pkg(getAppPackageName()).text("TestActivity2")), + DEFAULT_UI_TIMEOUT)); + + + // Test dismissing a task. + overview = pressHomeAndGoToOverview(); + final Integer numTasks = getFromRecents(this::getTaskCount); + task = overview.getCurrentTask(); + assertNotNull("overview.getCurrentTask() returned null (2)", task); + task.dismiss(); + executeOnRecents( + recents -> assertEquals("Dismissing a task didn't remove 1 task from Overview", + numTasks - 1, getTaskCount(recents))); + + // Test dismissing all tasks. + pressHomeAndGoToOverview().dismissAllTasks(); + assertTrue("Fallback Launcher not visible", mDevice.wait(Until.hasObject(By.pkg( + mOtherLauncherActivity.packageName)), WAIT_TIME_MS)); + } + + private int getCurrentOverviewPage(RecentsActivity recents) { + return recents.getOverviewPanel().getCurrentPage(); + } + + private int getTaskCount(RecentsActivity recents) { + return recents.getOverviewPanel().getTaskViewCount(); + } } diff --git a/quickstep/tests/src/com/android/quickstep/RecentTasksListTest.java b/quickstep/tests/src/com/android/quickstep/RecentTasksListTest.java new file mode 100644 index 0000000000..34eb7f8177 --- /dev/null +++ b/quickstep/tests/src/com/android/quickstep/RecentTasksListTest.java @@ -0,0 +1,99 @@ +/* + * 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.quickstep; + +import static junit.framework.TestCase.assertNull; + +import static org.junit.Assert.assertEquals; +import static org.mockito.Matchers.anyInt; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.times; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.when; + +import android.app.ActivityManager; + +import androidx.test.filters.SmallTest; + +import com.android.launcher3.util.LooperExecutor; +import com.android.systemui.shared.recents.model.Task; +import com.android.systemui.shared.system.ActivityManagerWrapper; +import com.android.systemui.shared.system.KeyguardManagerCompat; + +import org.junit.Before; +import org.junit.Test; + +import java.util.Collections; +import java.util.List; + +@SmallTest +public class RecentTasksListTest { + + private ActivityManagerWrapper mockActivityManagerWrapper; + + // Class under test + private RecentTasksList mRecentTasksList; + + @Before + public void setup() { + LooperExecutor mockMainThreadExecutor = mock(LooperExecutor.class); + KeyguardManagerCompat mockKeyguardManagerCompat = mock(KeyguardManagerCompat.class); + mockActivityManagerWrapper = mock(ActivityManagerWrapper.class); + mRecentTasksList = new RecentTasksList(mockMainThreadExecutor, mockKeyguardManagerCompat, + mockActivityManagerWrapper); + } + + @Test + public void onTaskRemoved_reloadsAllTasks() { + mRecentTasksList.onTaskRemoved(0); + verify(mockActivityManagerWrapper, times(1)) + .getRecentTasks(anyInt(), anyInt()); + } + + @Test + public void onTaskStackChanged_doesNotFetchTasks() { + mRecentTasksList.onTaskStackChanged(); + verify(mockActivityManagerWrapper, times(0)) + .getRecentTasks(anyInt(), anyInt()); + } + + @Test + public void loadTasksInBackground_onlyKeys_noValidTaskDescription() { + ActivityManager.RecentTaskInfo recentTaskInfo = new ActivityManager.RecentTaskInfo(); + when(mockActivityManagerWrapper.getRecentTasks(anyInt(), anyInt())) + .thenReturn(Collections.singletonList(recentTaskInfo)); + + List taskList = mRecentTasksList.loadTasksInBackground(Integer.MAX_VALUE, true); + + assertEquals(1, taskList.size()); + assertNull(taskList.get(0).taskDescription.getLabel()); + } + + @Test + public void loadTasksInBackground_moreThanKeys_hasValidTaskDescription() { + String taskDescription = "Wheeee!"; + ActivityManager.RecentTaskInfo recentTaskInfo = new ActivityManager.RecentTaskInfo(); + recentTaskInfo.taskDescription = new ActivityManager.TaskDescription(taskDescription); + when(mockActivityManagerWrapper.getRecentTasks(anyInt(), anyInt())) + .thenReturn(Collections.singletonList(recentTaskInfo)); + + List taskList = mRecentTasksList.loadTasksInBackground(Integer.MAX_VALUE, false); + + assertEquals(1, taskList.size()); + assertEquals(taskDescription, taskList.get(0).taskDescription.getLabel()); + } +} diff --git a/res/layout/folder_application.xml b/res/layout/folder_application.xml index c156e113fb..32a5419b8e 100644 --- a/res/layout/folder_application.xml +++ b/res/layout/folder_application.xml @@ -20,4 +20,5 @@ style="@style/BaseIcon" android:textColor="?attr/folderTextColor" android:includeFontPadding="false" + android:hapticFeedbackEnabled="false" launcher:iconDisplay="folder" /> diff --git a/res/values-am/strings.xml b/res/values-am/strings.xml index d942e9e67c..0396df6179 100644 --- a/res/values-am/strings.xml +++ b/res/values-am/strings.xml @@ -20,7 +20,6 @@ "Launcher3" - "ስራ" "መተግበሪያ አልተጫነም።" "መተግበሪያ አይገኝም" diff --git a/res/values-az/strings.xml b/res/values-az/strings.xml index 5528a19121..7c1ce8447d 100644 --- a/res/values-az/strings.xml +++ b/res/values-az/strings.xml @@ -20,7 +20,6 @@ "Launcher3" - "İş" "Tətbiq quraşdırılmayıb." "Tətbiq əlçatmazdır" diff --git a/res/values-bn/strings.xml b/res/values-bn/strings.xml index 775885caac..09608b5426 100644 --- a/res/values-bn/strings.xml +++ b/res/values-bn/strings.xml @@ -20,7 +20,6 @@ "Launcher3" - "কাজ" "অ্যাপ্লিকেশান ইনস্টল করা নেই৷" "অ্যাপ্লিকেশান অনুপলব্ধ" diff --git a/res/values-bs/strings.xml b/res/values-bs/strings.xml index 5650457071..65ad91e9b0 100644 --- a/res/values-bs/strings.xml +++ b/res/values-bs/strings.xml @@ -20,7 +20,6 @@ "Launcher3" - "Posao" "Aplikacija nije instalirana." "Aplikacija nije dostupna" diff --git a/res/values-ca/strings.xml b/res/values-ca/strings.xml index 50f92daf28..4ef9ec3a2c 100644 --- a/res/values-ca/strings.xml +++ b/res/values-ca/strings.xml @@ -20,7 +20,6 @@ "Launcher3" - "Feina" "L\'aplicació no s\'ha instal·lat." "L\'aplicació no està disponible." diff --git a/res/values-el/strings.xml b/res/values-el/strings.xml index 2363e6115b..d80e905d8c 100644 --- a/res/values-el/strings.xml +++ b/res/values-el/strings.xml @@ -20,7 +20,6 @@ "Launcher3" - "Εργασία" "Η εφαρμογή δεν έχει εγκατασταθεί." "Η εφαρμογή δεν είναι διαθέσιμη" diff --git a/res/values-en-rAU/strings.xml b/res/values-en-rAU/strings.xml index 0c48b5d2cb..7adc2187ec 100644 --- a/res/values-en-rAU/strings.xml +++ b/res/values-en-rAU/strings.xml @@ -20,7 +20,6 @@ "Launcher3" - "Work" "App isn\'t installed." "App isn\'t available" diff --git a/res/values-en-rGB/strings.xml b/res/values-en-rGB/strings.xml index 0c48b5d2cb..7adc2187ec 100644 --- a/res/values-en-rGB/strings.xml +++ b/res/values-en-rGB/strings.xml @@ -20,7 +20,6 @@ "Launcher3" - "Work" "App isn\'t installed." "App isn\'t available" diff --git a/res/values-en-rIN/strings.xml b/res/values-en-rIN/strings.xml index 0c48b5d2cb..7adc2187ec 100644 --- a/res/values-en-rIN/strings.xml +++ b/res/values-en-rIN/strings.xml @@ -20,7 +20,6 @@ "Launcher3" - "Work" "App isn\'t installed." "App isn\'t available" diff --git a/res/values-eu/strings.xml b/res/values-eu/strings.xml index 9ba46c171d..10aebe7ad6 100644 --- a/res/values-eu/strings.xml +++ b/res/values-eu/strings.xml @@ -20,7 +20,6 @@ "Launcher3" - "Lana" "Aplikazioa instalatu gabe dago." "Ez dago erabilgarri aplikazioa" diff --git a/res/values-fr/strings.xml b/res/values-fr/strings.xml index 7b4bcb1d82..65db47e8a7 100644 --- a/res/values-fr/strings.xml +++ b/res/values-fr/strings.xml @@ -20,7 +20,6 @@ "Launcher3" - "Android Work" "L\'application n\'est pas installée." "Application indisponible" @@ -136,7 +135,7 @@ "Retrouvez ici vos applications professionnelles" "Les applications professionnelles sont accompagnées d\'un badge et sont sécurisées par votre organisation. Vous pouvez les déplacer vers votre écran d\'accueil pour y accéder plus facilement." "Géré par votre organisation" - "Les notifications et les applications sont désactivées" + "Notifications et applications désactivées" "Fermer" "Fermé" "Échec : %1$s" diff --git a/res/values-gl/strings.xml b/res/values-gl/strings.xml index e115a72624..ca5ba3c5e4 100644 --- a/res/values-gl/strings.xml +++ b/res/values-gl/strings.xml @@ -20,7 +20,6 @@ "Launcher3" - "Traballo" "A aplicación non está instalada" "A aplicación non está dispoñible" diff --git a/res/values-gu/strings.xml b/res/values-gu/strings.xml index 7def3ace9a..3228dea004 100644 --- a/res/values-gu/strings.xml +++ b/res/values-gu/strings.xml @@ -20,7 +20,6 @@ "Launcher3" - "કાર્યાલય" "ઍપ્લિકેશન ઇન્સ્ટોલ થઈ નથી." "ઍપ્લિકેશન ઉપલબ્ધ નથી" diff --git a/res/values-hi/strings.xml b/res/values-hi/strings.xml index 3b0432ee4a..8fa02c0165 100644 --- a/res/values-hi/strings.xml +++ b/res/values-hi/strings.xml @@ -20,7 +20,6 @@ "Launcher3" - "कार्यस्‍थल" "ऐप्‍लिकेशन इंस्‍टॉल नहीं है." "ऐप्लिकेशन उपलब्ध नहीं है" diff --git a/res/values-hr/strings.xml b/res/values-hr/strings.xml index 1caa27feae..b738bc0739 100644 --- a/res/values-hr/strings.xml +++ b/res/values-hr/strings.xml @@ -20,7 +20,6 @@ "Launcher3" - "Posao" "Aplikacija nije instalirana." "Aplikacija nije dostupna" diff --git a/res/values-hy/strings.xml b/res/values-hy/strings.xml index fbb657ebd9..93404e6f5f 100644 --- a/res/values-hy/strings.xml +++ b/res/values-hy/strings.xml @@ -20,7 +20,6 @@ "Launcher3" - "Աշխատանքային" "Ծրագիրը տեղադրված չէ:" "Հավելվածը հասանելի չէ" @@ -87,7 +86,7 @@ "Հեռախոսը պտտելու դեպքում" "Ծանուցումների կետիկներ" "Միացված է" - "Անջատած է" + "Անջատված է" "Անհրաժեշտ է ծանուցման թույլտվություն" "Ծանուցումների կետիկները ցուցադրելու համար միացրեք ծանուցումները %1$s-ի համար" "Փոխել կարգավորումները" diff --git a/res/values-in/strings.xml b/res/values-in/strings.xml index 211f735b49..92c7accaa8 100644 --- a/res/values-in/strings.xml +++ b/res/values-in/strings.xml @@ -20,7 +20,6 @@ "Launcher3" - "Kantor" "Aplikasi tidak dipasang." "Aplikasi tidak tersedia" diff --git a/res/values-km/strings.xml b/res/values-km/strings.xml index f4a328df14..973804200a 100644 --- a/res/values-km/strings.xml +++ b/res/values-km/strings.xml @@ -20,7 +20,6 @@ "Launcher3" - "ការងារ" "មិន​បាន​ដំឡើង​កម្មវិធី។" "មិន​មាន​កម្មវិធី" diff --git a/res/values-kn/strings.xml b/res/values-kn/strings.xml index 14c830064b..67ea6a830a 100644 --- a/res/values-kn/strings.xml +++ b/res/values-kn/strings.xml @@ -20,7 +20,6 @@ "Launcher3" - "ಕೆಲಸ" "ಅಪ್ಲಿಕೇಶನ್‌ ಅನ್ನು ಸ್ಥಾಪಿಸಲಾಗಿಲ್ಲ" "ಅಪ್ಲಿಕೇಶನ್ ಲಭ್ಯವಿಲ್ಲ" diff --git a/res/values-ky/strings.xml b/res/values-ky/strings.xml index 046a66253e..d52446c83c 100644 --- a/res/values-ky/strings.xml +++ b/res/values-ky/strings.xml @@ -20,7 +20,6 @@ "Launcher3" - "Жумуш" "Колдонмо орнотулган эмес." "Колдонмо жеткиликтүү эмес" diff --git a/res/values-mk/strings.xml b/res/values-mk/strings.xml index 9550a5f0f5..ab14e8900b 100644 --- a/res/values-mk/strings.xml +++ b/res/values-mk/strings.xml @@ -20,7 +20,6 @@ "Стартер3" - "Работа" "Апликацијата не е инсталирана." "Апликацијата не е достапна" diff --git a/res/values-my/strings.xml b/res/values-my/strings.xml index fd1f7d9535..78856f657d 100644 --- a/res/values-my/strings.xml +++ b/res/values-my/strings.xml @@ -20,7 +20,6 @@ "ဖွင့်တင်စက်၃" - "အလုပ်" "အက်ပ်မထည့်သွင်းထားပါ" "အက်ပ်လက်လှမ်း မမှီပါ" diff --git a/res/values-pt-rPT/strings.xml b/res/values-pt-rPT/strings.xml index 071b71e38f..e0d2c74645 100644 --- a/res/values-pt-rPT/strings.xml +++ b/res/values-pt-rPT/strings.xml @@ -20,7 +20,6 @@ "Launcher3" - "Trabalho" "A aplicação não está instalada." "A aplicação não está disponível" diff --git a/res/values-pt/strings.xml b/res/values-pt/strings.xml index e88ff18b42..bb4834f69d 100644 --- a/res/values-pt/strings.xml +++ b/res/values-pt/strings.xml @@ -20,7 +20,6 @@ "Tela de início 3" - "Trabalho" "O app não está instalado." "O app não está disponível" diff --git a/res/values-ro/strings.xml b/res/values-ro/strings.xml index 1251d7e3f8..f8a61f11f7 100644 --- a/res/values-ro/strings.xml +++ b/res/values-ro/strings.xml @@ -20,7 +20,6 @@ "Launcher3" - "Work" "Aplicația nu este instalată." "Aplicația nu este disponibilă" diff --git a/res/values-sk/strings.xml b/res/values-sk/strings.xml index 340a128d2b..5bbf7c3cdc 100644 --- a/res/values-sk/strings.xml +++ b/res/values-sk/strings.xml @@ -20,7 +20,6 @@ "Launcher3" - "Pracovné" "Aplikácia nie je nainštalovaná." "Aplikácia nie je k dispozícii" diff --git a/res/values-sq/strings.xml b/res/values-sq/strings.xml index 48ae26ed84..7f2567c60c 100644 --- a/res/values-sq/strings.xml +++ b/res/values-sq/strings.xml @@ -20,7 +20,6 @@ "Nisësi3" - "Puna" "Aplikacioni nuk është i instaluar." "Aplikacioni nuk mundësohet" @@ -41,8 +40,8 @@ "Kërko për më shumë aplikacione" "Aplikacioni" "Njoftimet" - "Prek dhe mbaj prekur për të zgjedhur një shkurtore." - "Prek dy herë dhe mbaj prekur për të zgjedhur një shkurtore ose për të përdorur veprimet e personalizuara." + "Prek dhe mbaj të shtypur për të zgjedhur një shkurtore." + "Prek dy herë dhe mbaj të shtypur për të zgjedhur një shkurtore ose për të përdorur veprimet e personalizuara." "Nuk ka më hapësirë në këtë ekran bazë." "Nuk ka më hapësirë në tabakanë \"Të preferuarat\"" "Lista e aplikacioneve" diff --git a/res/values-sw/strings.xml b/res/values-sw/strings.xml index 6f33c4f046..445e382f2d 100644 --- a/res/values-sw/strings.xml +++ b/res/values-sw/strings.xml @@ -20,7 +20,6 @@ "Launcher3" - "Kazini" "Programu haijasakinishwa." "Programu haipatikani" diff --git a/res/values-ta/strings.xml b/res/values-ta/strings.xml index c926bc185b..6e933a12e3 100644 --- a/res/values-ta/strings.xml +++ b/res/values-ta/strings.xml @@ -20,7 +20,6 @@ "Launcher3" - "பணியிடம்" "ஆப்ஸ் நிறுவப்படவில்லை." "ஆப்ஸ் இல்லை" @@ -36,7 +35,7 @@ "நீங்களே சேர்க்க, தொட்டுப் பிடித்திருக்கவும்" "தானாகவே சேர்" "பயன்பாடுகளில் தேடுக" - "பயன்பாடுகளை ஏற்றுகிறது…" + "ஆப்ஸை ஏற்றுகிறது…" "\"%1$s\" உடன் பொருந்தும் பயன்பாடுகள் இல்லை" "கூடுதல் பயன்பாடுகளைத் தேடு" "ஆப்ஸ்" @@ -45,7 +44,7 @@ "ஷார்ட்கட்டைச் சேர்க்க, இருமுறை தட்டிப் பிடித்திருக்கவும் (அ) தனிப்பயன் செயல்களைப் பயன்படுத்தவும்." "முகப்புத் திரையில் இடமில்லை." "பிடித்தவை ட்ரேயில் இடமில்லை" - "பயன்பாடுகளின் பட்டியல்" + "ஆப்ஸின் பட்டியல்" "தனிப்பட்ட ஆப்ஸ் பட்டியல்" "பணி ஆப்ஸ் பட்டியல்" "முகப்பு" diff --git a/res/values-te/strings.xml b/res/values-te/strings.xml index c8f70fe058..1f8d3b8c4f 100644 --- a/res/values-te/strings.xml +++ b/res/values-te/strings.xml @@ -20,7 +20,6 @@ "Launcher3" - "కార్యాలయం" "యాప్ ఇన్‌స్టాల్ చేయబడలేదు." "యాప్ అందుబాటులో లేదు" @@ -53,7 +52,7 @@ "అన్ఇన్‌స్టాల్ చేయి" "యాప్ సమాచారం" "ఇన్‌స్టాల్ చేయండి" - "సత్వరమార్గాలను ఇన్‌స్టాల్ చేయడం" + "షార్ట్‌కట్‌లను ఇన్‌స్టాల్ చేయడం" "వినియోగదారు ప్రమేయం లేకుండా సత్వరమార్గాలను జోడించడానికి అనువర్తనాన్ని అనుమతిస్తుంది." "హోమ్ సెట్టింగ్‌లు మరియు సత్వరమార్గాలను చదవడం" "హోమ్‌లో సెట్టింగ్‌లు మరియు సత్వరమార్గాలను చదవడానికి అనువర్తనాన్ని అనుమతిస్తుంది." diff --git a/res/values-th/strings.xml b/res/values-th/strings.xml index 03c02ad50e..5ab9b860ef 100644 --- a/res/values-th/strings.xml +++ b/res/values-th/strings.xml @@ -20,7 +20,6 @@ "Launcher3" - "งาน" "ไม่ได้ติดตั้งแอป" "แอปไม่พร้อมใช้งาน" diff --git a/res/values-zu/strings.xml b/res/values-zu/strings.xml index 83178fe739..b937764ace 100644 --- a/res/values-zu/strings.xml +++ b/res/values-zu/strings.xml @@ -20,7 +20,6 @@ "Isiqalisi3" - "Umsebenzi" "Uhlelo lokusebenza alufakiwe." "Uhlelo lokusebenza alutholakali" diff --git a/res/values/attrs.xml b/res/values/attrs.xml index 69b8c8a228..7be584e932 100644 --- a/res/values/attrs.xml +++ b/res/values/attrs.xml @@ -115,7 +115,8 @@ - + + @@ -131,6 +132,12 @@ + + + + + + @@ -152,21 +159,6 @@ - - - - - - - - - - - - - - - diff --git a/res/values/strings.xml b/res/values/strings.xml index 13e096c95b..9d9c2e8ded 100644 --- a/res/values/strings.xml +++ b/res/values/strings.xml @@ -23,8 +23,6 @@ Launcher3 - - Work @@ -105,6 +103,9 @@ Install + + Dismiss prediction + diff --git a/res/xml/custom_widgets.xml b/res/xml/custom_widgets.xml deleted file mode 100644 index 4b54386501..0000000000 --- a/res/xml/custom_widgets.xml +++ /dev/null @@ -1,31 +0,0 @@ - - - - - - \ No newline at end of file diff --git a/robolectric_tests/src/com/android/launcher3/config/FlagOverrideRule.java b/robolectric_tests/src/com/android/launcher3/config/FlagOverrideRule.java index a3d121676f..e49c67cfe0 100644 --- a/robolectric_tests/src/com/android/launcher3/config/FlagOverrideRule.java +++ b/robolectric_tests/src/com/android/launcher3/config/FlagOverrideRule.java @@ -1,8 +1,9 @@ package com.android.launcher3.config; -import com.android.launcher3.config.BaseFlags.BaseTogglableFlag; +import com.android.launcher3.config.FeatureFlags.BaseTogglableFlag; import com.android.launcher3.uioverrides.TogglableFlag; + import org.junit.rules.TestRule; import org.junit.runner.Description; import org.junit.runners.model.Statement; diff --git a/robolectric_tests/src/com/android/launcher3/config/FlagOverrideSampleTest.java b/robolectric_tests/src/com/android/launcher3/config/FlagOverrideSampleTest.java index 1351348164..ace1420fc2 100644 --- a/robolectric_tests/src/com/android/launcher3/config/FlagOverrideSampleTest.java +++ b/robolectric_tests/src/com/android/launcher3/config/FlagOverrideSampleTest.java @@ -1,5 +1,8 @@ package com.android.launcher3.config; +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertTrue; + import com.android.launcher3.config.FlagOverrideRule.FlagOverride; import org.junit.Rule; @@ -7,9 +10,6 @@ import org.junit.Test; import org.junit.runner.RunWith; import org.robolectric.RobolectricTestRunner; -import static org.junit.Assert.assertFalse; -import static org.junit.Assert.assertTrue; - /** * Sample Robolectric test that demonstrates flag-overriding. */ @@ -24,13 +24,12 @@ public class FlagOverrideSampleTest { @FlagOverride(key = "EXAMPLE_FLAG", value = true) @Test public void withFlagOn() { - assertTrue(FeatureFlags.EXAMPLE_FLAG.get()); + assertTrue(FeatureFlags.FAKE_LANDSCAPE_UI.get()); } - @FlagOverride(key = "EXAMPLE_FLAG", value = false) @Test public void withFlagOff() { - assertFalse(FeatureFlags.EXAMPLE_FLAG.get()); + assertFalse(FeatureFlags.FAKE_LANDSCAPE_UI.get()); } } diff --git a/robolectric_tests/src/com/android/launcher3/model/BaseModelUpdateTaskTestCase.java b/robolectric_tests/src/com/android/launcher3/model/BaseModelUpdateTaskTestCase.java index ab39274664..bc936b7b21 100644 --- a/robolectric_tests/src/com/android/launcher3/model/BaseModelUpdateTaskTestCase.java +++ b/robolectric_tests/src/com/android/launcher3/model/BaseModelUpdateTaskTestCase.java @@ -15,14 +15,13 @@ import android.graphics.Color; import android.os.Process; import android.os.UserHandle; -import com.android.launcher3.AllAppsList; import com.android.launcher3.AppFilter; import com.android.launcher3.AppInfo; import com.android.launcher3.InvariantDeviceProfile; import com.android.launcher3.ItemInfo; import com.android.launcher3.LauncherAppState; import com.android.launcher3.LauncherModel; -import com.android.launcher3.LauncherModel.Callbacks; +import com.android.launcher3.model.BgDataModel.Callbacks; import com.android.launcher3.LauncherModel.ModelUpdateTask; import com.android.launcher3.LauncherProvider; import com.android.launcher3.icons.IconCache; diff --git a/robolectric_tests/src/com/android/launcher3/util/IntArrayTest.java b/robolectric_tests/src/com/android/launcher3/util/IntArrayTest.java new file mode 100644 index 0000000000..c08e198a8c --- /dev/null +++ b/robolectric_tests/src/com/android/launcher3/util/IntArrayTest.java @@ -0,0 +1,38 @@ +/* + * 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 com.google.common.truth.Truth.assertThat; + +import org.junit.Test; +import org.junit.runner.RunWith; +import org.robolectric.RobolectricTestRunner; + +/** + * Robolectric unit tests for {@link IntArray} + */ +@RunWith(RobolectricTestRunner.class) +public class IntArrayTest { + + @Test + public void concatAndParseString() { + int[] array = new int[] {0, 2, 3, 9}; + String concat = IntArray.wrap(array).toConcatString(); + + int[] parsed = IntArray.fromConcatString(concat).toArray(); + assertThat(array).isEqualTo(parsed); + } +} diff --git a/robolectric_tests/src/com/android/launcher3/util/IntSetTest.java b/robolectric_tests/src/com/android/launcher3/util/IntSetTest.java index f846de5e5d..8513353dc6 100644 --- a/robolectric_tests/src/com/android/launcher3/util/IntSetTest.java +++ b/robolectric_tests/src/com/android/launcher3/util/IntSetTest.java @@ -21,7 +21,6 @@ import org.junit.Test; import org.junit.runner.RunWith; import org.robolectric.RobolectricTestRunner; -import org.robolectric.annotation.Config; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertFalse; diff --git a/robolectric_tests/src/com/android/launcher3/util/TestLauncherProvider.java b/robolectric_tests/src/com/android/launcher3/util/TestLauncherProvider.java index 31e303eb34..9f833b15d8 100644 --- a/robolectric_tests/src/com/android/launcher3/util/TestLauncherProvider.java +++ b/robolectric_tests/src/com/android/launcher3/util/TestLauncherProvider.java @@ -2,7 +2,6 @@ package com.android.launcher3.util; import android.content.Context; import android.database.sqlite.SQLiteDatabase; -import android.database.sqlite.SQLiteOpenHelper; import com.android.launcher3.LauncherProvider; @@ -28,9 +27,6 @@ public class TestLauncherProvider extends LauncherProvider { return mOpenHelper.getWritableDatabase(); } - @Override - protected void notifyListeners() { } - private static class MyDatabaseHelper extends DatabaseHelper { public MyDatabaseHelper(Context context) { super(context, null, null); diff --git a/src/com/android/launcher3/AbstractFloatingView.java b/src/com/android/launcher3/AbstractFloatingView.java index 65f9d6bc09..8fddf3cff9 100644 --- a/src/com/android/launcher3/AbstractFloatingView.java +++ b/src/com/android/launcher3/AbstractFloatingView.java @@ -86,7 +86,7 @@ public abstract class AbstractFloatingView extends LinearLayout implements Touch // Type of popups which should be kept open during launcher rebind public static final int TYPE_REBIND_SAFE = TYPE_WIDGETS_FULL_SHEET - | TYPE_ON_BOARD_POPUP | TYPE_DISCOVERY_BOUNCE; + | TYPE_WIDGETS_BOTTOM_SHEET | TYPE_ON_BOARD_POPUP | TYPE_DISCOVERY_BOUNCE; // Usually we show the back button when a floating view is open. Instead, hide for these types. public static final int TYPE_HIDE_BACK_BUTTON = TYPE_ON_BOARD_POPUP | TYPE_DISCOVERY_BOUNCE diff --git a/src/com/android/launcher3/AppInfo.java b/src/com/android/launcher3/AppInfo.java index d884049f5a..c8e7619c7b 100644 --- a/src/com/android/launcher3/AppInfo.java +++ b/src/com/android/launcher3/AppInfo.java @@ -29,11 +29,19 @@ import com.android.launcher3.compat.UserManagerCompat; import com.android.launcher3.util.ComponentKey; import com.android.launcher3.util.PackageManagerHelper; +import java.util.Comparator; + /** * Represents an app in AllAppsView. */ public class AppInfo extends ItemInfoWithIcon { + public static AppInfo[] EMPTY_ARRAY = new AppInfo[0]; + public static Comparator COMPONENT_KEY_COMPARATOR = (a, b) -> { + int uc = a.user.hashCode() - b.user.hashCode(); + return uc != 0 ? uc : a.componentName.compareTo(b.componentName); + }; + /** * The intent used to start the application. */ @@ -41,6 +49,9 @@ public class AppInfo extends ItemInfoWithIcon { public ComponentName componentName; + // Section name used for indexing. + public String sectionName = ""; + public AppInfo() { itemType = LauncherSettings.Favorites.ITEM_TYPE_APPLICATION; } @@ -74,6 +85,8 @@ public class AppInfo extends ItemInfoWithIcon { componentName = info.componentName; title = Utilities.trim(info.title); intent = new Intent(info.intent); + user = info.user; + runtimeStatusFlags = info.runtimeStatusFlags; } @Override @@ -116,4 +129,9 @@ public class AppInfo extends ItemInfoWithIcon { info.runtimeStatusFlags |= FLAG_ADAPTIVE_ICON; } } + + @Override + public AppInfo clone() { + return new AppInfo(this); + } } diff --git a/src/com/android/launcher3/AppWidgetsRestoredReceiver.java b/src/com/android/launcher3/AppWidgetsRestoredReceiver.java index e3ef5d64e7..5f1be94e38 100644 --- a/src/com/android/launcher3/AppWidgetsRestoredReceiver.java +++ b/src/com/android/launcher3/AppWidgetsRestoredReceiver.java @@ -1,5 +1,7 @@ package com.android.launcher3; +import static android.os.Process.myUserHandle; + import android.appwidget.AppWidgetHost; import android.appwidget.AppWidgetManager; import android.appwidget.AppWidgetProviderInfo; @@ -12,15 +14,13 @@ import android.util.Log; import com.android.launcher3.LauncherSettings.Favorites; import com.android.launcher3.compat.UserManagerCompat; -import com.android.launcher3.config.FeatureFlags; import com.android.launcher3.model.LoaderTask; +import com.android.launcher3.model.WidgetsModel; import com.android.launcher3.provider.RestoreDbTask; import com.android.launcher3.util.ContentWriter; import androidx.annotation.WorkerThread; -import static android.os.Process.myUserHandle; - public class AppWidgetsRestoredReceiver extends BroadcastReceiver { private static final String TAG = "AWRestoredReceiver"; @@ -50,7 +50,7 @@ public class AppWidgetsRestoredReceiver extends BroadcastReceiver { @WorkerThread public static void restoreAppWidgetIds(Context context, int[] oldWidgetIds, int[] newWidgetIds) { AppWidgetHost appWidgetHost = new LauncherAppWidgetHost(context); - if (FeatureFlags.GO_DISABLE_WIDGETS) { + if (WidgetsModel.GO_DISABLE_WIDGETS) { Log.e(TAG, "Skipping widget ID remap as widgets not supported"); appWidgetHost.deleteHost(); return; diff --git a/src/com/android/launcher3/AutoInstallsLayout.java b/src/com/android/launcher3/AutoInstallsLayout.java index 9724869bf4..ac4396778a 100644 --- a/src/com/android/launcher3/AutoInstallsLayout.java +++ b/src/com/android/launcher3/AutoInstallsLayout.java @@ -38,11 +38,15 @@ import android.util.Pair; import android.util.Patterns; import android.util.Xml; +import androidx.annotation.Nullable; + import com.android.launcher3.LauncherProvider.SqlArguments; import com.android.launcher3.LauncherSettings.Favorites; import com.android.launcher3.icons.GraphicsUtils; import com.android.launcher3.icons.LauncherIcons; +import com.android.launcher3.qsb.QsbContainerView; import com.android.launcher3.util.IntArray; +import com.android.launcher3.util.PackageManagerHelper; import com.android.launcher3.util.Thunk; import org.xmlpull.v1.XmlPullParser; @@ -72,7 +76,7 @@ public class AutoInstallsLayout { static AutoInstallsLayout get(Context context, AppWidgetHost appWidgetHost, LayoutParserCallback callback) { - Pair customizationApkInfo = Utilities.findSystemApk( + Pair customizationApkInfo = PackageManagerHelper.findSystemApk( ACTION_LAUNCHER_CUSTOMIZATION, context.getPackageManager()); if (customizationApkInfo == null) { return null; @@ -83,7 +87,7 @@ public class AutoInstallsLayout { // Try with grid size and hotseat count String layoutName = String.format(Locale.ENGLISH, FORMATTED_LAYOUT_RES_WITH_HOSTEAT, - grid.numColumns, grid.numRows, grid.numHotseatIcons); + grid.numColumns, grid.numRows, grid.numHotseatIcons); int layoutId = targetRes.getIdentifier(layoutName, "xml", pkg); // Try with only grid size @@ -91,7 +95,7 @@ public class AutoInstallsLayout { Log.d(TAG, "Formatted layout: " + layoutName + " not found. Trying layout without hosteat"); layoutName = String.format(Locale.ENGLISH, FORMATTED_LAYOUT_RES, - grid.numColumns, grid.numRows); + grid.numColumns, grid.numRows); layoutId = targetRes.getIdentifier(layoutName, "xml", pkg); } @@ -116,6 +120,7 @@ public class AutoInstallsLayout { private static final String TAG_AUTO_INSTALL = "autoinstall"; private static final String TAG_FOLDER = "folder"; private static final String TAG_APPWIDGET = "appwidget"; + protected static final String TAG_SEARCH_WIDGET = "searchwidget"; private static final String TAG_SHORTCUT = "shortcut"; private static final String TAG_EXTRA = "extra"; @@ -147,8 +152,10 @@ public class AutoInstallsLayout { private static final String HOTSEAT_CONTAINER_NAME = Favorites.containerToString(Favorites.CONTAINER_HOTSEAT); - @Thunk final Context mContext; - @Thunk final AppWidgetHost mAppWidgetHost; + @Thunk + final Context mContext; + @Thunk + final AppWidgetHost mAppWidgetHost; protected final LayoutParserCallback mCallback; protected final PackageManager mPackageManager; @@ -160,7 +167,8 @@ public class AutoInstallsLayout { private final int mColumnCount; private final int[] mTemp = new int[2]; - @Thunk final ContentValues mValues; + @Thunk + final ContentValues mValues; protected final String mRootTag; protected SQLiteDatabase mDb; @@ -244,7 +252,7 @@ public class AutoInstallsLayout { */ protected int parseAndAddNode( XmlPullParser parser, ArrayMap tagParserMap, IntArray screenIds) - throws XmlPullParserException, IOException { + throws XmlPullParserException, IOException { if (TAG_INCLUDE.equals(parser.getName())) { final int resId = getAttributeResourceValue(parser, ATTR_WORKSPACE, 0); @@ -315,6 +323,7 @@ public class AutoInstallsLayout { parsers.put(TAG_AUTO_INSTALL, new AutoInstallParser()); parsers.put(TAG_FOLDER, new FolderParser()); parsers.put(TAG_APPWIDGET, new PendingWidgetParser()); + parsers.put(TAG_SEARCH_WIDGET, new SearchWidgetParser()); parsers.put(TAG_SHORTCUT, new ShortcutParser(mSourceRes)); return parsers; } @@ -347,15 +356,15 @@ public class AutoInstallsLayout { info = mPackageManager.getActivityInfo(cn, 0); } catch (PackageManager.NameNotFoundException nnfe) { String[] packages = mPackageManager.currentToCanonicalPackageNames( - new String[] { packageName }); + new String[]{packageName}); cn = new ComponentName(packages[0], className); info = mPackageManager.getActivityInfo(cn, 0); } final Intent intent = new Intent(Intent.ACTION_MAIN, null) - .addCategory(Intent.CATEGORY_LAUNCHER) - .setComponent(cn) - .setFlags(Intent.FLAG_ACTIVITY_NEW_TASK | - Intent.FLAG_ACTIVITY_RESET_TASK_IF_NEEDED); + .addCategory(Intent.CATEGORY_LAUNCHER) + .setComponent(cn) + .setFlags(Intent.FLAG_ACTIVITY_NEW_TASK + | Intent.FLAG_ACTIVITY_RESET_TASK_IF_NEEDED); return addShortcut(info.loadLabel(mPackageManager).toString(), intent, Favorites.ITEM_TYPE_APPLICATION); @@ -393,10 +402,10 @@ public class AutoInstallsLayout { mValues.put(Favorites.RESTORED, WorkspaceItemInfo.FLAG_AUTOINSTALL_ICON); final Intent intent = new Intent(Intent.ACTION_MAIN, null) - .addCategory(Intent.CATEGORY_LAUNCHER) - .setComponent(new ComponentName(packageName, className)) - .setFlags(Intent.FLAG_ACTIVITY_NEW_TASK | - Intent.FLAG_ACTIVITY_RESET_TASK_IF_NEEDED); + .addCategory(Intent.CATEGORY_LAUNCHER) + .setComponent(new ComponentName(packageName, className)) + .setFlags(Intent.FLAG_ACTIVITY_NEW_TASK + | Intent.FLAG_ACTIVITY_RESET_TASK_IF_NEEDED); return addShortcut(mContext.getString(R.string.package_state_unknown), intent, Favorites.ITEM_TYPE_APPLICATION); } @@ -444,7 +453,7 @@ public class AutoInstallsLayout { mValues.put(Favorites.ICON_RESOURCE, mIconRes.getResourceName(iconId)); intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK | - Intent.FLAG_ACTIVITY_RESET_TASK_IF_NEEDED); + Intent.FLAG_ACTIVITY_RESET_TASK_IF_NEEDED); return addShortcut(mSourceRes.getString(titleResId), intent, Favorites.ITEM_TYPE_SHORTCUT); } @@ -469,12 +478,22 @@ public class AutoInstallsLayout { */ protected class PendingWidgetParser implements TagParser { - @Override - public int parseAndAdd(XmlPullParser parser) - throws XmlPullParserException, IOException { + @Nullable + public ComponentName getComponentName(XmlPullParser parser) { final String packageName = getAttributeValue(parser, ATTR_PACKAGE_NAME); final String className = getAttributeValue(parser, ATTR_CLASS_NAME); if (TextUtils.isEmpty(packageName) || TextUtils.isEmpty(className)) { + return null; + } + return new ComponentName(packageName, className); + } + + + @Override + public int parseAndAdd(XmlPullParser parser) + throws XmlPullParserException, IOException { + ComponentName cn = getComponentName(parser); + if (cn == null) { if (LOGD) Log.d(TAG, "Skipping invalid with no component"); return -1; } @@ -505,16 +524,15 @@ public class AutoInstallsLayout { throw new RuntimeException("Widgets can contain only extras"); } } - - return verifyAndInsert(new ComponentName(packageName, className), extras); + return verifyAndInsert(cn, extras); } protected int verifyAndInsert(ComponentName cn, Bundle extras) { mValues.put(Favorites.APPWIDGET_PROVIDER, cn.flattenToString()); mValues.put(Favorites.RESTORED, - LauncherAppWidgetInfo.FLAG_ID_NOT_VALID | - LauncherAppWidgetInfo.FLAG_PROVIDER_NOT_READY | - LauncherAppWidgetInfo.FLAG_DIRECT_CONFIG); + LauncherAppWidgetInfo.FLAG_ID_NOT_VALID + | LauncherAppWidgetInfo.FLAG_PROVIDER_NOT_READY + | LauncherAppWidgetInfo.FLAG_DIRECT_CONFIG); mValues.put(Favorites._ID, mCallback.generateNewItemId()); if (!extras.isEmpty()) { mValues.put(Favorites.INTENT, new Intent().putExtras(extras).toUri(0)); @@ -529,6 +547,23 @@ public class AutoInstallsLayout { } } + protected class SearchWidgetParser extends PendingWidgetParser { + @Override + @Nullable + public ComponentName getComponentName(XmlPullParser parser) { + return QsbContainerView.getSearchComponentName(mContext); + } + + @Override + protected int verifyAndInsert(ComponentName cn, Bundle extras) { + mValues.put(Favorites.OPTIONS, LauncherAppWidgetInfo.OPTION_SEARCH_WIDGET); + int flags = mValues.getAsInteger(Favorites.RESTORED) + | WorkspaceItemInfo.FLAG_RESTORE_STARTED; + mValues.put(Favorites.RESTORED, flags); + return super.verifyAndInsert(cn, extras); + } + } + protected class FolderParser implements TagParser { private final ArrayMap mFolderElements; @@ -548,7 +583,7 @@ public class AutoInstallsLayout { if (titleResId != 0) { title = mSourceRes.getString(titleResId); } else { - title = mContext.getResources().getString(R.string.folder_name); + title = ""; } mValues.put(Favorites.TITLE, title); @@ -680,7 +715,8 @@ public class AutoInstallsLayout { int insertAndCheck(SQLiteDatabase db, ContentValues values); } - @Thunk static void copyInteger(ContentValues from, ContentValues to, String key) { + @Thunk + static void copyInteger(ContentValues from, ContentValues to, String key) { to.put(key, from.getAsInteger(key)); } } diff --git a/src/com/android/launcher3/BaseActivity.java b/src/com/android/launcher3/BaseActivity.java index 64550560ff..b28077f7cd 100644 --- a/src/com/android/launcher3/BaseActivity.java +++ b/src/com/android/launcher3/BaseActivity.java @@ -27,6 +27,8 @@ import android.content.Intent; import android.content.res.Configuration; import android.view.ContextThemeWrapper; +import androidx.annotation.IntDef; + import com.android.launcher3.DeviceProfile.OnDeviceProfileChangeListener; import com.android.launcher3.logging.StatsLogManager; import com.android.launcher3.logging.StatsLogUtils; @@ -44,8 +46,6 @@ import java.io.PrintWriter; import java.lang.annotation.Retention; import java.util.ArrayList; -import androidx.annotation.IntDef; - public abstract class BaseActivity extends Activity implements UserEventDelegate, LogStateProvider, ActivityContext { @@ -265,12 +265,13 @@ public abstract class BaseActivity extends Activity } } - protected void dumpMisc(PrintWriter writer) { - writer.println(" deviceProfile isTransposed=" + getDeviceProfile().isVerticalBarLayout()); - writer.println(" orientation=" + getResources().getConfiguration().orientation); - writer.println(" mSystemUiController: " + mSystemUiController); - writer.println(" mActivityFlags: " + mActivityFlags); - writer.println(" mForceInvisible: " + mForceInvisible); + protected void dumpMisc(String prefix, PrintWriter writer) { + writer.println(prefix + "deviceProfile isTransposed=" + + getDeviceProfile().isVerticalBarLayout()); + writer.println(prefix + "orientation=" + getResources().getConfiguration().orientation); + writer.println(prefix + "mSystemUiController: " + mSystemUiController); + writer.println(prefix + "mActivityFlags: " + mActivityFlags); + writer.println(prefix + "mForceInvisible: " + mForceInvisible); } public static T fromContext(Context context) { diff --git a/src/com/android/launcher3/BaseDraggingActivity.java b/src/com/android/launcher3/BaseDraggingActivity.java index f0b3afd14a..e2ef337c53 100644 --- a/src/com/android/launcher3/BaseDraggingActivity.java +++ b/src/com/android/launcher3/BaseDraggingActivity.java @@ -30,17 +30,18 @@ import android.view.ActionMode; import android.view.View; import android.widget.Toast; +import androidx.annotation.Nullable; + import com.android.launcher3.LauncherSettings.Favorites; import com.android.launcher3.compat.LauncherAppsCompat; import com.android.launcher3.model.AppLaunchTracker; import com.android.launcher3.shortcuts.DeepShortcutManager; -import com.android.launcher3.testing.TestProtocol; +import com.android.launcher3.uioverrides.DejankBinderTracker; import com.android.launcher3.uioverrides.DisplayRotationListener; import com.android.launcher3.uioverrides.WallpaperColorInfo; +import com.android.launcher3.util.PackageManagerHelper; import com.android.launcher3.util.Themes; -import androidx.annotation.Nullable; - /** * Extension of BaseActivity allowing support for drag-n-drop */ @@ -65,7 +66,8 @@ public abstract class BaseDraggingActivity extends BaseActivity @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); - mIsSafeModeEnabled = getPackageManager().isSafeMode(); + mIsSafeModeEnabled = DejankBinderTracker.whitelistIpcs(() -> + getPackageManager().isSafeMode()); mRotationListener = new DisplayRotationListener(this, this::onDeviceRotationChanged); // Update theme @@ -120,6 +122,10 @@ public abstract class BaseDraggingActivity extends BaseActivity public abstract View getRootView(); + public void returnToHomescreen() { + // no-op + } + public Rect getViewBounds(View v) { int[] pos = new int[2]; v.getLocationOnScreen(pos); @@ -135,7 +141,7 @@ public abstract class BaseDraggingActivity extends BaseActivity public boolean startActivitySafely(View v, Intent intent, @Nullable ItemInfo item, @Nullable String sourceContainer) { - if (mIsSafeModeEnabled && !Utilities.isSystemApp(this, intent)) { + if (mIsSafeModeEnabled && !PackageManagerHelper.isSystemApp(this, intent)) { Toast.makeText(this, R.string.safemode_shortcut_error, Toast.LENGTH_SHORT).show(); return false; } @@ -237,14 +243,14 @@ public abstract class BaseDraggingActivity extends BaseActivity protected void onDeviceProfileInitiated() { if (mDeviceProfile.isVerticalBarLayout()) { mRotationListener.enable(); - mDeviceProfile.updateIsSeascape(getWindowManager()); + mDeviceProfile.updateIsSeascape(this); } else { mRotationListener.disable(); } } private void onDeviceRotationChanged() { - if (mDeviceProfile.updateIsSeascape(getWindowManager())) { + if (mDeviceProfile.updateIsSeascape(this)) { reapplyUi(); } } diff --git a/src/com/android/launcher3/CellLayout.java b/src/com/android/launcher3/CellLayout.java index 09fb2446da..976ccd52ed 100644 --- a/src/com/android/launcher3/CellLayout.java +++ b/src/com/android/launcher3/CellLayout.java @@ -2702,6 +2702,14 @@ public class CellLayout extends ViewGroup implements Transposable { } } + /** + * Sets the position to the provided point + */ + public void setXY(Point point) { + cellX = point.x; + cellY = point.y; + } + public String toString() { return "(" + this.cellX + ", " + this.cellY + ")"; } diff --git a/src/com/android/launcher3/DefaultLayoutParser.java b/src/com/android/launcher3/DefaultLayoutParser.java index 75297f63b5..af85594779 100644 --- a/src/com/android/launcher3/DefaultLayoutParser.java +++ b/src/com/android/launcher3/DefaultLayoutParser.java @@ -14,13 +14,16 @@ import android.os.Bundle; import android.text.TextUtils; import android.util.ArrayMap; import android.util.Log; + import com.android.launcher3.LauncherSettings.Favorites; import com.android.launcher3.util.Thunk; + +import org.xmlpull.v1.XmlPullParser; +import org.xmlpull.v1.XmlPullParserException; + import java.io.IOException; import java.net.URISyntaxException; import java.util.List; -import org.xmlpull.v1.XmlPullParser; -import org.xmlpull.v1.XmlPullParserException; /** * Implements the layout parser with rules for internal layouts and partner layouts. @@ -55,7 +58,8 @@ public class DefaultLayoutParser extends AutoInstallsLayout { return getFolderElementsMap(mSourceRes); } - @Thunk ArrayMap getFolderElementsMap(Resources res) { + @Thunk + ArrayMap getFolderElementsMap(Resources res) { ArrayMap parsers = new ArrayMap<>(); parsers.put(TAG_FAVORITE, new AppShortcutWithUriParser()); parsers.put(TAG_SHORTCUT, new UriShortcutParser(res)); @@ -67,6 +71,7 @@ public class DefaultLayoutParser extends AutoInstallsLayout { ArrayMap parsers = new ArrayMap<>(); parsers.put(TAG_FAVORITE, new AppShortcutWithUriParser()); parsers.put(TAG_APPWIDGET, new AppWidgetParser()); + parsers.put(TAG_SEARCH_WIDGET, new SearchWidgetParser()); parsers.put(TAG_SHORTCUT, new UriShortcutParser(mSourceRes)); parsers.put(TAG_RESOLVE, new ResolveParser()); parsers.put(TAG_FOLDER, new MyFolderParser()); @@ -229,7 +234,8 @@ public class DefaultLayoutParser extends AutoInstallsLayout { /** * A parser which adds a folder whose contents come from partner apk. */ - @Thunk class PartnerFolderParser implements TagParser { + @Thunk + class PartnerFolderParser implements TagParser { @Override public int parseAndAdd(XmlPullParser parser) throws XmlPullParserException, @@ -255,7 +261,8 @@ public class DefaultLayoutParser extends AutoInstallsLayout { /** * An extension of FolderParser which allows adding items from a different xml. */ - @Thunk class MyFolderParser extends FolderParser { + @Thunk + class MyFolderParser extends FolderParser { @Override public int parseAndAdd(XmlPullParser parser) throws XmlPullParserException, @@ -281,7 +288,7 @@ public class DefaultLayoutParser extends AutoInstallsLayout { mPackageManager.getReceiverInfo(cn, 0); } catch (Exception e) { String[] packages = mPackageManager.currentToCanonicalPackageNames( - new String[] { cn.getPackageName() }); + new String[]{cn.getPackageName()}); cn = new ComponentName(packages[0], cn.getClassName()); try { mPackageManager.getReceiverInfo(cn, 0); diff --git a/src/com/android/launcher3/DeviceProfile.java b/src/com/android/launcher3/DeviceProfile.java index 883e8c642b..44c307058d 100644 --- a/src/com/android/launcher3/DeviceProfile.java +++ b/src/com/android/launcher3/DeviceProfile.java @@ -24,12 +24,12 @@ import android.graphics.PointF; import android.graphics.Rect; import android.util.DisplayMetrics; import android.view.Surface; -import android.view.WindowManager; import com.android.launcher3.CellLayout.ContainerType; import com.android.launcher3.graphics.IconShape; import com.android.launcher3.icons.DotRenderer; import com.android.launcher3.icons.IconNormalizer; +import com.android.launcher3.util.DefaultDisplay; public class DeviceProfile { @@ -307,11 +307,16 @@ public class DeviceProfile { updateAvailableFolderCellDimensions(dm, res); } + /** + * Updating the iconSize affects many aspects of the launcher layout, such as: iconSizePx, + * iconTextSizePx, iconDrawablePaddingPx, cellWidth/Height, allApps* variants, + * hotseat sizes, workspaceSpringLoadedShrinkFactor, folderIconSizePx, and folderIconOffsetYPx. + */ private void updateIconSize(float scale, Resources res, DisplayMetrics dm) { // Workspace final boolean isVerticalLayout = isVerticalBarLayout(); - float invIconSizePx = isVerticalLayout ? inv.landscapeIconSize : inv.iconSize; - iconSizePx = Math.max(1, (int) (ResourceUtils.pxFromDp(invIconSizePx, dm) * scale)); + float invIconSizeDp = isVerticalLayout ? inv.landscapeIconSize : inv.iconSize; + iconSizePx = Math.max(1, (int) (ResourceUtils.pxFromDp(invIconSizeDp, dm) * scale)); iconTextSizePx = (int) (Utilities.pxFromSp(inv.iconTextSize, dm) * scale); iconDrawablePaddingPx = (int) (iconDrawablePaddingOriginalPx * scale); @@ -329,12 +334,19 @@ public class DeviceProfile { cellWidthPx = iconSizePx + iconDrawablePaddingPx; // All apps - allAppsIconTextSizePx = iconTextSizePx; - allAppsIconSizePx = iconSizePx; - allAppsIconDrawablePaddingPx = iconDrawablePaddingPx; - allAppsCellHeightPx = getCellSize().y; + if (allAppsHasDifferentNumColumns()) { + allAppsIconSizePx = ResourceUtils.pxFromDp(inv.allAppsIconSize, dm); + allAppsIconTextSizePx = Utilities.pxFromSp(inv.allAppsIconTextSize, dm); + allAppsCellHeightPx = getCellSize(inv.numAllAppsColumns, inv.numAllAppsColumns).y; + allAppsIconDrawablePaddingPx = iconDrawablePaddingOriginalPx; + } else { + allAppsIconSizePx = iconSizePx; + allAppsIconTextSizePx = iconTextSizePx; + allAppsIconDrawablePaddingPx = iconDrawablePaddingPx; + allAppsCellHeightPx = getCellSize().y; + } - if (isVerticalLayout) { + if (isVerticalBarLayout()) { // Always hide the Workspace text with vertical bar layout. adjustToHideWorkspaceLabels(); } @@ -419,14 +431,18 @@ public class DeviceProfile { } public Point getCellSize() { + return getCellSize(inv.numColumns, inv.numRows); + } + + private Point getCellSize(int numColumns, int numRows) { Point result = new Point(); // Since we are only concerned with the overall padding, layout direction does // not matter. Point padding = getTotalWorkspacePadding(); result.x = calculateCellWidth(availableWidthPx - padding.x - - cellLayoutPaddingLeftRightPx * 2, inv.numColumns); + - cellLayoutPaddingLeftRightPx * 2, numColumns); result.y = calculateCellHeight(availableHeightPx - padding.y - - cellLayoutBottomPaddingPx, inv.numRows); + - cellLayoutBottomPaddingPx, numRows); return result; } @@ -541,12 +557,20 @@ public class DeviceProfile { return isLandscape && transposeLayoutWithOrientation; } + /** + * Returns true when the number of workspace columns and all apps columns differs. + */ + private boolean allAppsHasDifferentNumColumns() { + return inv.numAllAppsColumns != inv.numColumns; + } + /** * Updates orientation information and returns true if it has changed from the previous value. */ - public boolean updateIsSeascape(WindowManager wm) { + public boolean updateIsSeascape(Context context) { if (isVerticalBarLayout()) { - boolean isSeascape = wm.getDefaultDisplay().getRotation() == Surface.ROTATION_270; + boolean isSeascape = DefaultDisplay.INSTANCE.get(context).getInfo().rotation + == Surface.ROTATION_270; if (mIsSeascape != isSeascape) { mIsSeascape = isSeascape; return true; diff --git a/src/com/android/launcher3/FolderInfo.java b/src/com/android/launcher3/FolderInfo.java index b747d62aef..e2b7b68a97 100644 --- a/src/com/android/launcher3/FolderInfo.java +++ b/src/com/android/launcher3/FolderInfo.java @@ -50,9 +50,9 @@ public class FolderInfo extends ItemInfo { /** * The apps and shortcuts */ - public ArrayList contents = new ArrayList(); + public ArrayList contents = new ArrayList<>(); - ArrayList listeners = new ArrayList(); + private ArrayList mListeners = new ArrayList<>(); public FolderInfo() { itemType = LauncherSettings.Favorites.ITEM_TYPE_FOLDER; @@ -72,10 +72,10 @@ public class FolderInfo extends ItemInfo { * Add an app or shortcut for a specified rank. */ public void add(WorkspaceItemInfo item, int rank, boolean animate) { - rank = Utilities.boundToRange(rank, 0, contents.size()); + rank = Utilities.boundToRange(rank, 0, contents.size() + 1); contents.add(rank, item); - for (int i = 0; i < listeners.size(); i++) { - listeners.get(i).onAdd(item, rank); + for (int i = 0; i < mListeners.size(); i++) { + mListeners.get(i).onAdd(item, rank); } itemsChanged(animate); } @@ -87,53 +87,37 @@ public class FolderInfo extends ItemInfo { */ public void remove(WorkspaceItemInfo item, boolean animate) { contents.remove(item); - for (int i = 0; i < listeners.size(); i++) { - listeners.get(i).onRemove(item); + for (int i = 0; i < mListeners.size(); i++) { + mListeners.get(i).onRemove(item); } itemsChanged(animate); } - public void setTitle(CharSequence title) { - this.title = title; - for (int i = 0; i < listeners.size(); i++) { - listeners.get(i).onTitleChanged(title); - } - } - @Override public void onAddToDatabase(ContentWriter writer) { super.onAddToDatabase(writer); writer.put(LauncherSettings.Favorites.TITLE, title) .put(LauncherSettings.Favorites.OPTIONS, options); - } public void addListener(FolderListener listener) { - listeners.add(listener); + mListeners.add(listener); } public void removeListener(FolderListener listener) { - listeners.remove(listener); + mListeners.remove(listener); } public void itemsChanged(boolean animate) { - for (int i = 0; i < listeners.size(); i++) { - listeners.get(i).onItemsChanged(animate); - } - } - - public void prepareAutoUpdate() { - for (int i = 0; i < listeners.size(); i++) { - listeners.get(i).prepareAutoUpdate(); + for (int i = 0; i < mListeners.size(); i++) { + mListeners.get(i).onItemsChanged(animate); } } public interface FolderListener { public void onAdd(WorkspaceItemInfo item, int rank); public void onRemove(WorkspaceItemInfo item); - public void onTitleChanged(CharSequence title); public void onItemsChanged(boolean animate); - public void prepareAutoUpdate(); } public boolean hasOption(int optionFlag) { diff --git a/src/com/android/launcher3/InstallShortcutReceiver.java b/src/com/android/launcher3/InstallShortcutReceiver.java index 670cd2877d..c9e7d91c0f 100644 --- a/src/com/android/launcher3/InstallShortcutReceiver.java +++ b/src/com/android/launcher3/InstallShortcutReceiver.java @@ -16,10 +16,11 @@ package com.android.launcher3; +import static com.android.launcher3.util.Executors.MODEL_EXECUTOR; + import android.appwidget.AppWidgetManager; import android.appwidget.AppWidgetProviderInfo; import android.content.BroadcastReceiver; -import android.content.ComponentName; import android.content.Context; import android.content.Intent; import android.content.SharedPreferences; @@ -29,8 +30,6 @@ import android.content.pm.PackageManager; import android.content.pm.ShortcutInfo; import android.graphics.Bitmap; import android.graphics.BitmapFactory; -import android.os.Handler; -import android.os.Message; import android.os.Parcelable; import android.os.Process; import android.os.UserHandle; @@ -39,6 +38,8 @@ import android.util.Base64; import android.util.Log; import android.util.Pair; +import androidx.annotation.WorkerThread; + import com.android.launcher3.compat.LauncherAppsCompat; import com.android.launcher3.compat.UserManagerCompat; import com.android.launcher3.icons.BitmapInfo; @@ -65,9 +66,6 @@ import java.util.Set; public class InstallShortcutReceiver extends BroadcastReceiver { - private static final int MSG_ADD_TO_QUEUE = 1; - private static final int MSG_FLUSH_QUEUE = 2; - public static final int FLAG_ACTIVITY_PAUSED = 1; public static final int FLAG_LOADER_RUNNING = 2; public static final int FLAG_DRAG_AND_DROP = 4; @@ -100,66 +98,57 @@ public class InstallShortcutReceiver extends BroadcastReceiver { public static final int NEW_SHORTCUT_BOUNCE_DURATION = 450; public static final int NEW_SHORTCUT_STAGGER_DELAY = 85; - private static final Handler sHandler = new Handler(LauncherModel.getWorkerLooper()) { + @WorkerThread + private static void addToQueue(Context context, PendingInstallShortcutInfo info) { + String encoded = info.encodeToString(); + SharedPreferences prefs = Utilities.getPrefs(context); + Set strings = prefs.getStringSet(APPS_PENDING_INSTALL, null); + strings = (strings != null) ? new HashSet<>(strings) : new HashSet<>(1); + strings.add(encoded); + prefs.edit().putStringSet(APPS_PENDING_INSTALL, strings).apply(); + } - @Override - public void handleMessage(Message msg) { - switch (msg.what) { - case MSG_ADD_TO_QUEUE: { - Pair pair = - (Pair) msg.obj; - String encoded = pair.second.encodeToString(); - SharedPreferences prefs = Utilities.getPrefs(pair.first); - Set strings = prefs.getStringSet(APPS_PENDING_INSTALL, null); - strings = (strings != null) ? new HashSet<>(strings) : new HashSet(1); - strings.add(encoded); - prefs.edit().putStringSet(APPS_PENDING_INSTALL, strings).apply(); - return; - } - case MSG_FLUSH_QUEUE: { - Context context = (Context) msg.obj; - LauncherModel model = LauncherAppState.getInstance(context).getModel(); - if (model.getCallback() == null) { - // Launcher not loaded - return; - } - - ArrayList> installQueue = new ArrayList<>(); - SharedPreferences prefs = Utilities.getPrefs(context); - Set strings = prefs.getStringSet(APPS_PENDING_INSTALL, null); - if (DBG) Log.d(TAG, "Getting and clearing APPS_PENDING_INSTALL: " + strings); - if (strings == null) { - return; - } - - LauncherAppsCompat launcherApps = LauncherAppsCompat.getInstance(context); - for (String encoded : strings) { - PendingInstallShortcutInfo info = decode(encoded, context); - if (info == null) { - continue; - } - - String pkg = getIntentPackage(info.launchIntent); - if (!TextUtils.isEmpty(pkg) - && !launcherApps.isPackageEnabledForProfile(pkg, info.user) - && !info.isActivity) { - if (DBG) Log.d(TAG, "Ignoring shortcut for absent package: " - + info.launchIntent); - continue; - } - - // Generate a shortcut info to add into the model - installQueue.add(info.getItemInfo()); - } - prefs.edit().remove(APPS_PENDING_INSTALL).apply(); - if (!installQueue.isEmpty()) { - model.addAndBindAddedWorkspaceItems(installQueue); - } - return; - } - } + @WorkerThread + private static void flushQueueInBackground(Context context) { + LauncherModel model = LauncherAppState.getInstance(context).getModel(); + if (model.getCallback() == null) { + // Launcher not loaded + return; } - }; + + ArrayList> installQueue = new ArrayList<>(); + SharedPreferences prefs = Utilities.getPrefs(context); + Set strings = prefs.getStringSet(APPS_PENDING_INSTALL, null); + if (DBG) Log.d(TAG, "Getting and clearing APPS_PENDING_INSTALL: " + strings); + if (strings == null) { + return; + } + + LauncherAppsCompat launcherApps = LauncherAppsCompat.getInstance(context); + for (String encoded : strings) { + PendingInstallShortcutInfo info = decode(encoded, context); + if (info == null) { + continue; + } + + String pkg = getIntentPackage(info.launchIntent); + if (!TextUtils.isEmpty(pkg) + && !launcherApps.isPackageEnabledForProfile(pkg, info.user) + && !info.isActivity) { + if (DBG) { + Log.d(TAG, "Ignoring shortcut for absent package: " + info.launchIntent); + } + continue; + } + + // Generate a shortcut info to add into the model + installQueue.add(info.getItemInfo()); + } + prefs.edit().remove(APPS_PENDING_INSTALL).apply(); + if (!installQueue.isEmpty()) { + model.addAndBindAddedWorkspaceItems(installQueue); + } + } public static void removeFromInstallQueue(Context context, HashSet packageNames, UserHandle user) { @@ -291,7 +280,7 @@ public class InstallShortcutReceiver extends BroadcastReceiver { private static void queuePendingShortcutInfo(PendingInstallShortcutInfo info, Context context) { // Queue the item up for adding if launcher has not loaded properly yet - Message.obtain(sHandler, MSG_ADD_TO_QUEUE, Pair.create(context, info)).sendToTarget(); + MODEL_EXECUTOR.post(() -> addToQueue(context, info)); flushInstallQueue(context); } @@ -307,7 +296,7 @@ public class InstallShortcutReceiver extends BroadcastReceiver { if (sInstallQueueDisabledFlags != 0) { return; } - Message.obtain(sHandler, MSG_FLUSH_QUEUE, context.getApplicationContext()).sendToTarget(); + MODEL_EXECUTOR.post(() -> flushQueueInBackground(context)); } /** @@ -617,7 +606,7 @@ public class InstallShortcutReceiver extends BroadcastReceiver { // Already an activity target return original; } - if (!Utilities.isLauncherAppTarget(original.launchIntent)) { + if (!PackageManagerHelper.isLauncherAppTarget(original.launchIntent)) { return original; } diff --git a/src/com/android/launcher3/InvariantDeviceProfile.java b/src/com/android/launcher3/InvariantDeviceProfile.java index bde87cb505..310a9e92ff 100644 --- a/src/com/android/launcher3/InvariantDeviceProfile.java +++ b/src/com/android/launcher3/InvariantDeviceProfile.java @@ -18,6 +18,8 @@ package com.android.launcher3; import static com.android.launcher3.Utilities.getDevicePrefs; import static com.android.launcher3.config.FeatureFlags.APPLY_CONFIG_AT_RUNTIME; +import static com.android.launcher3.util.Executors.MAIN_EXECUTOR; +import static com.android.launcher3.settings.SettingsActivity.GRID_OPTIONS_PREFERENCE_KEY; import static com.android.launcher3.util.PackageManagerHelper.getPackageFilter; import android.annotation.TargetApi; @@ -39,11 +41,13 @@ import android.util.Log; import android.util.SparseArray; import android.util.TypedValue; import android.util.Xml; -import android.view.Display; -import android.view.WindowManager; + +import androidx.annotation.Nullable; +import androidx.annotation.VisibleForTesting; import com.android.launcher3.graphics.IconShape; import com.android.launcher3.util.ConfigMonitor; +import com.android.launcher3.util.DefaultDisplay; import com.android.launcher3.util.IntArray; import com.android.launcher3.util.MainThreadInitializedObject; import com.android.launcher3.util.Themes; @@ -55,9 +59,6 @@ import java.io.IOException; import java.util.ArrayList; import java.util.Collections; -import androidx.annotation.Nullable; -import androidx.annotation.VisibleForTesting; - public class InvariantDeviceProfile { public static final String TAG = "IDP"; @@ -102,6 +103,8 @@ public class InvariantDeviceProfile { public int iconBitmapSize; public int fillResIconDpi; public float iconTextSize; + public float allAppsIconSize; + public float allAppsIconTextSize; private SparseArray mExtraAttrs; @@ -110,6 +113,11 @@ public class InvariantDeviceProfile { */ public int numHotseatIcons; + /** + * Number of columns in the all apps list. + */ + public int numAllAppsColumns; + public int defaultLayoutId; int demoModeLayoutId; @@ -136,6 +144,9 @@ public class InvariantDeviceProfile { landscapeIconSize = p.landscapeIconSize; iconTextSize = p.iconTextSize; numHotseatIcons = p.numHotseatIcons; + numAllAppsColumns = p.numAllAppsColumns; + allAppsIconSize = p.allAppsIconSize; + allAppsIconTextSize = p.allAppsIconTextSize; defaultLayoutId = p.defaultLayoutId; demoModeLayoutId = p.demoModeLayoutId; mExtraAttrs = p.mExtraAttrs; @@ -144,7 +155,10 @@ public class InvariantDeviceProfile { @TargetApi(23) private InvariantDeviceProfile(Context context) { - initGrid(context, Utilities.getPrefs(context).getString(KEY_IDP_GRID_NAME, null)); + String gridName = Utilities.getPrefs(context).getBoolean(GRID_OPTIONS_PREFERENCE_KEY, false) + ? Utilities.getPrefs(context).getString(KEY_IDP_GRID_NAME, null) + : null; + initGrid(context, gridName); mConfigMonitor = new ConfigMonitor(context, APPLY_CONFIG_AT_RUNTIME.get() ? this::onConfigChanged : this::killProcess); mOverlayMonitor = new OverlayMonitor(context); @@ -172,19 +186,17 @@ public class InvariantDeviceProfile { } private String initGrid(Context context, String gridName) { - WindowManager wm = (WindowManager) context.getSystemService(Context.WINDOW_SERVICE); - Display display = wm.getDefaultDisplay(); - DisplayMetrics dm = new DisplayMetrics(); - display.getMetrics(dm); + DefaultDisplay.Info displayInfo = DefaultDisplay.INSTANCE.get(context).getInfo(); - Point smallestSize = new Point(); - Point largestSize = new Point(); - display.getCurrentSizeRange(smallestSize, largestSize); + Point smallestSize = new Point(displayInfo.smallestSize); + Point largestSize = new Point(displayInfo.largestSize); ArrayList allOptions = getPredefinedDeviceProfiles(context, gridName); // This guarantees that width < height - float minWidthDps = Utilities.dpiFromPx(Math.min(smallestSize.x, smallestSize.y), dm); - float minHeightDps = Utilities.dpiFromPx(Math.min(largestSize.x, largestSize.y), dm); + float minWidthDps = Utilities.dpiFromPx(Math.min(smallestSize.x, smallestSize.y), + displayInfo.metrics); + float minHeightDps = Utilities.dpiFromPx(Math.min(largestSize.x, largestSize.y), + displayInfo.metrics); // Sort the profiles based on the closeness to the device size Collections.sort(allOptions, (a, b) -> Float.compare(dist(minWidthDps, minHeightDps, a.minWidthDps, a.minHeightDps), @@ -200,6 +212,8 @@ public class InvariantDeviceProfile { demoModeLayoutId = closestProfile.demoModeLayoutId; numFolderRows = closestProfile.numFolderRows; numFolderColumns = closestProfile.numFolderColumns; + numAllAppsColumns = closestProfile.numAllAppsColumns; + mExtraAttrs = closestProfile.extraAttrs; if (!closestProfile.name.equals(gridName)) { @@ -210,16 +224,23 @@ public class InvariantDeviceProfile { iconSize = interpolatedDisplayOption.iconSize; iconShapePath = getIconShapePath(context); landscapeIconSize = interpolatedDisplayOption.landscapeIconSize; - iconBitmapSize = ResourceUtils.pxFromDp(iconSize, dm); + iconBitmapSize = ResourceUtils.pxFromDp(iconSize, displayInfo.metrics); iconTextSize = interpolatedDisplayOption.iconTextSize; fillResIconDpi = getLauncherIconDensity(iconBitmapSize); + if (Utilities.getPrefs(context).getBoolean(GRID_OPTIONS_PREFERENCE_KEY, false)) { + allAppsIconSize = interpolatedDisplayOption.allAppsIconSize; + allAppsIconTextSize = interpolatedDisplayOption.allAppsIconTextSize; + } else { + allAppsIconSize = iconSize; + allAppsIconTextSize = iconTextSize; + } + // If the partner customization apk contains any grid overrides, apply them // Supported overrides: numRows, numColumns, iconSize - applyPartnerDeviceProfileOverrides(context, dm); + applyPartnerDeviceProfileOverrides(context, displayInfo.metrics); - Point realSize = new Point(); - display.getRealSize(realSize); + Point realSize = new Point(displayInfo.realSize); // The real size never changes. smallSide and largeSide will remain the // same in any orientation. int smallSide = Math.min(realSize.x, realSize.y); @@ -280,7 +301,7 @@ public class InvariantDeviceProfile { public void setCurrentGrid(Context context, String gridName) { Context appContext = context.getApplicationContext(); Utilities.getPrefs(appContext).edit().putString(KEY_IDP_GRID_NAME, gridName).apply(); - new MainThreadExecutor().execute(() -> onConfigChanged(appContext)); + MAIN_EXECUTOR.execute(() -> onConfigChanged(appContext)); } private void onConfigChanged(Context context) { @@ -288,9 +309,10 @@ public class InvariantDeviceProfile { InvariantDeviceProfile oldProfile = new InvariantDeviceProfile(this); // Re-init grid - // TODO(b/131867841): We pass in null here so that we can calculate the closest profile - // without the bias of the grid name. - initGrid(context, null); + String gridName = Utilities.getPrefs(context).getBoolean(GRID_OPTIONS_PREFERENCE_KEY, false) + ? Utilities.getPrefs(context).getString(KEY_IDP_GRID_NAME, null) + : null; + initGrid(context, gridName); int changeFlags = 0; if (numRows != oldProfile.numRows || @@ -492,6 +514,8 @@ public class InvariantDeviceProfile { private final int numHotseatIcons; + private final int numAllAppsColumns; + private final int defaultLayoutId; private final int demoModeLayoutId; @@ -514,6 +538,9 @@ public class InvariantDeviceProfile { R.styleable.GridDisplayOption_numFolderRows, numRows); numFolderColumns = a.getInt( R.styleable.GridDisplayOption_numFolderColumns, numColumns); + numAllAppsColumns = a.getInt( + R.styleable.GridDisplayOption_numAllAppsColumns, numColumns); + a.recycle(); extraAttrs = Themes.createValueMap(context, attrs, @@ -530,8 +557,10 @@ public class InvariantDeviceProfile { private final boolean canBeDefault; private float iconSize; - private float landscapeIconSize; private float iconTextSize; + private float landscapeIconSize; + private float allAppsIconSize; + private float allAppsIconTextSize; DisplayOption(GridOption grid, Context context, AttributeSet attrs) { this.grid = grid; @@ -549,6 +578,11 @@ public class InvariantDeviceProfile { landscapeIconSize = a.getFloat(R.styleable.ProfileDisplayOption_landscapeIconSize, iconSize); iconTextSize = a.getFloat(R.styleable.ProfileDisplayOption_iconTextSize, 0); + + allAppsIconSize = a.getFloat(R.styleable.ProfileDisplayOption_allAppsIconSize, + iconSize); + allAppsIconTextSize = a.getFloat(R.styleable.ProfileDisplayOption_allAppsIconTextSize, + iconTextSize); a.recycle(); } @@ -563,14 +597,18 @@ public class InvariantDeviceProfile { private DisplayOption multiply(float w) { iconSize *= w; landscapeIconSize *= w; + allAppsIconSize *= w; iconTextSize *= w; + allAppsIconTextSize *= w; return this; } private DisplayOption add(DisplayOption p) { iconSize += p.iconSize; landscapeIconSize += p.landscapeIconSize; + allAppsIconSize += p.allAppsIconSize; iconTextSize += p.iconTextSize; + allAppsIconTextSize += p.allAppsIconTextSize; return this; } } diff --git a/src/com/android/launcher3/ItemInfoWithIcon.java b/src/com/android/launcher3/ItemInfoWithIcon.java index e29f92713d..1550bb080d 100644 --- a/src/com/android/launcher3/ItemInfoWithIcon.java +++ b/src/com/android/launcher3/ItemInfoWithIcon.java @@ -27,6 +27,8 @@ import com.android.launcher3.icons.BitmapInfo; */ public abstract class ItemInfoWithIcon extends ItemInfo { + public static final String TAG = "ItemInfoDebug"; + /** * A bitmap version of the application icon. */ @@ -126,4 +128,8 @@ public abstract class ItemInfoWithIcon extends ItemInfo { iconColor = info.color; } + /** + * @return a copy of this + */ + public abstract ItemInfoWithIcon clone(); } diff --git a/src/com/android/launcher3/Launcher.java b/src/com/android/launcher3/Launcher.java index 257f0dfcf3..4f8b20e0b4 100644 --- a/src/com/android/launcher3/Launcher.java +++ b/src/com/android/launcher3/Launcher.java @@ -16,16 +16,18 @@ package com.android.launcher3; -import static android.content.pm.ActivityInfo.CONFIG_LOCALE; import static android.content.pm.ActivityInfo.CONFIG_ORIENTATION; import static android.content.pm.ActivityInfo.CONFIG_SCREEN_SIZE; +import static com.android.launcher3.AbstractFloatingView.TYPE_ALL; +import static com.android.launcher3.AbstractFloatingView.TYPE_REBIND_SAFE; import static com.android.launcher3.AbstractFloatingView.TYPE_SNACKBAR; import static com.android.launcher3.LauncherAnimUtils.SPRING_LOADED_EXIT_DELAY; import static com.android.launcher3.LauncherState.ALL_APPS; import static com.android.launcher3.LauncherState.NORMAL; import static com.android.launcher3.LauncherState.OVERVIEW; import static com.android.launcher3.LauncherState.OVERVIEW_PEEK; +import static com.android.launcher3.Utilities.postAsyncCallback; import static com.android.launcher3.dragndrop.DragLayer.ALPHA_INDEX_LAUNCHER_LOAD; import static com.android.launcher3.logging.LoggerUtils.newContainerTarget; import static com.android.launcher3.logging.LoggerUtils.newTarget; @@ -77,6 +79,9 @@ import android.view.accessibility.AccessibilityEvent; import android.view.animation.OvershootInterpolator; import android.widget.Toast; +import androidx.annotation.Nullable; +import androidx.annotation.VisibleForTesting; + import com.android.launcher3.DropTarget.DragObject; import com.android.launcher3.accessibility.LauncherAccessibilityDelegate; import com.android.launcher3.allapps.AllAppsContainerView; @@ -91,9 +96,8 @@ import com.android.launcher3.dot.DotInfo; import com.android.launcher3.dragndrop.DragController; import com.android.launcher3.dragndrop.DragLayer; import com.android.launcher3.dragndrop.DragView; -import com.android.launcher3.folder.Folder; +import com.android.launcher3.folder.FolderGridOrganizer; import com.android.launcher3.folder.FolderIcon; -import com.android.launcher3.folder.FolderIconPreviewVerifier; import com.android.launcher3.graphics.RotationMode; import com.android.launcher3.icons.IconCache; import com.android.launcher3.keyboard.CustomActionsPopup; @@ -103,16 +107,18 @@ import com.android.launcher3.logging.StatsLogUtils; import com.android.launcher3.logging.UserEventDispatcher; import com.android.launcher3.logging.UserEventDispatcher.UserEventDelegate; import com.android.launcher3.model.AppLaunchTracker; +import com.android.launcher3.model.BgDataModel.Callbacks; import com.android.launcher3.model.ModelWriter; import com.android.launcher3.notification.NotificationListener; import com.android.launcher3.popup.PopupContainerWithArrow; import com.android.launcher3.popup.PopupDataProvider; -import com.android.launcher3.shortcuts.DeepShortcutManager; +import com.android.launcher3.qsb.QsbContainerView; import com.android.launcher3.states.InternalStateHandler; import com.android.launcher3.states.RotationHelper; -import com.android.launcher3.testing.TestProtocol; import com.android.launcher3.touch.ItemClickHandler; +import com.android.launcher3.uioverrides.DejankBinderTracker; import com.android.launcher3.uioverrides.UiFactory; +import com.android.launcher3.uioverrides.plugins.PluginManagerWrapper; import com.android.launcher3.userevent.nano.LauncherLogProto; import com.android.launcher3.userevent.nano.LauncherLogProto.Action; import com.android.launcher3.userevent.nano.LauncherLogProto.ContainerType; @@ -145,7 +151,13 @@ import com.android.launcher3.widget.WidgetAddFlowHandler; import com.android.launcher3.widget.WidgetHostViewLoader; import com.android.launcher3.widget.WidgetListRowEntry; import com.android.launcher3.widget.WidgetsFullSheet; -import com.android.launcher3.widget.custom.CustomWidgetParser; +import com.android.launcher3.widget.custom.CustomWidgetManager; +import com.android.systemui.plugins.OverlayPlugin; +import com.android.systemui.plugins.PluginListener; +import com.android.systemui.plugins.shared.LauncherExterns; +import com.android.systemui.plugins.shared.LauncherOverlayManager; +import com.android.systemui.plugins.shared.LauncherOverlayManager.LauncherOverlay; +import com.android.systemui.plugins.shared.LauncherOverlayManager.LauncherOverlayCallbacks; import java.io.FileDescriptor; import java.io.PrintWriter; @@ -155,16 +167,14 @@ import java.util.HashMap; import java.util.HashSet; import java.util.List; import java.util.function.Predicate; - -import androidx.annotation.Nullable; -import androidx.annotation.VisibleForTesting; +import java.util.function.Supplier; /** * Default launcher application. */ public class Launcher extends BaseDraggingActivity implements LauncherExterns, - LauncherModel.Callbacks, LauncherProviderChangeListener, UserEventDelegate, - InvariantDeviceProfile.OnIDPChangeListener { + Callbacks, LauncherProviderChangeListener, UserEventDelegate, + InvariantDeviceProfile.OnIDPChangeListener, PluginListener { public static final String TAG = "Launcher"; static final boolean LOGD = false; @@ -220,9 +230,11 @@ public class Launcher extends BaseDraggingActivity implements LauncherExterns, private LauncherAppTransitionManager mAppTransitionManager; private Configuration mOldConfig; - @Thunk Workspace mWorkspace; + @Thunk + Workspace mWorkspace; private View mLauncherView; - @Thunk DragLayer mDragLayer; + @Thunk + DragLayer mDragLayer; private DragController mDragController; private AppWidgetManagerCompat mAppWidgetManager; @@ -230,21 +242,25 @@ public class Launcher extends BaseDraggingActivity implements LauncherExterns, private final int[] mTmpAddItemCellCoordinates = new int[2]; - @Thunk Hotseat mHotseat; + @Thunk + Hotseat mHotseat; private DropTargetBar mDropTargetBar; // Main container view for the all apps screen. - @Thunk AllAppsContainerView mAppsView; + @Thunk + AllAppsContainerView mAppsView; AllAppsTransitionController mAllAppsController; // Scrim view for the all apps and overview state. - @Thunk ScrimView mScrimView; + @Thunk + ScrimView mScrimView; // UI and state for the overview panel private View mOverviewPanel; - @Thunk boolean mWorkspaceLoading = true; + @Thunk + boolean mWorkspaceLoading = true; private ArrayList mOnResumeCallbacks = new ArrayList<>(); @@ -287,8 +303,14 @@ public class Launcher extends BaseDraggingActivity implements LauncherExterns, private DeviceProfile mStableDeviceProfile; private RotationMode mRotationMode = RotationMode.NORMAL; + protected LauncherOverlayManager mOverlayManager; + // If true, overlay callbacks are deferred + private boolean mDeferOverlayCallbacks; + private final Runnable mDeferredOverlayCallbacks = this::checkIfOverlayStillDeferred; + @Override protected void onCreate(Bundle savedInstanceState) { + DejankBinderTracker.allowBinderTrackingInTests(); RaceConditionTracker.onEvent(ON_CREATE_EVT, ENTER); if (DEBUG_STRICT_MODE) { StrictMode.setThreadPolicy(new StrictMode.ThreadPolicy.Builder() @@ -326,8 +348,8 @@ public class Launcher extends BaseDraggingActivity implements LauncherExterns, UiFactory.onCreate(this); mAppWidgetManager = AppWidgetManagerCompat.getInstance(this); - - mAppWidgetHost = new LauncherAppWidgetHost(this); + mAppWidgetHost = new LauncherAppWidgetHost(this, + appWidgetId -> getWorkspace().removeWidget(appWidgetId)); mAppWidgetHost.startListening(); mLauncherView = LayoutInflater.from(this).inflate(R.layout.launcher, null); @@ -383,13 +405,18 @@ public class Launcher extends BaseDraggingActivity implements LauncherExterns, if (mLauncherCallbacks != null) { mLauncherCallbacks.onCreate(savedInstanceState); } + mOverlayManager = getDefaultOverlay(); + PluginManagerWrapper.INSTANCE.get(this).addPluginListener(this, + OverlayPlugin.class, false /* allowedMultiple */); + mRotationHelper.initialize(); TraceHelper.endSection("Launcher-onCreate"); RaceConditionTracker.onEvent(ON_CREATE_EVT, EXIT); mStateManager.addStateListener(new LauncherStateManager.StateListener() { @Override - public void onStateTransitionStart(LauncherState toState) {} + public void onStateTransitionStart(LauncherState toState) { + } @Override public void onStateTransitionComplete(LauncherState finalState) { @@ -405,6 +432,39 @@ public class Launcher extends BaseDraggingActivity implements LauncherExterns, } } }); + DejankBinderTracker.disallowBinderTrackingInTests(); + } + + protected LauncherOverlayManager getDefaultOverlay() { + return new LauncherOverlayManager() { }; + } + + @Override + public void onPluginConnected(OverlayPlugin overlayManager, Context context) { + switchOverlay(() -> overlayManager.createOverlayManager(this, this)); + } + + @Override + public void onPluginDisconnected(OverlayPlugin plugin) { + switchOverlay(this::getDefaultOverlay); + } + + private void switchOverlay(Supplier overlaySupplier) { + if (mOverlayManager != null) { + mOverlayManager.onActivityDestroyed(this); + } + mOverlayManager = overlaySupplier.get(); + if (getRootView().isAttachedToWindow()) { + mOverlayManager.onAttachedToWindow(); + } + mDeferOverlayCallbacks = true; + checkIfOverlayStillDeferred(); + } + + @Override + protected void dispatchDeviceProfileChanged() { + super.dispatchDeviceProfileChanged(); + mOverlayManager.onDeviceProvideChanged(); } @Override @@ -419,10 +479,6 @@ public class Launcher extends BaseDraggingActivity implements LauncherExterns, public void onConfigurationChanged(Configuration newConfig) { int diff = newConfig.diff(mOldConfig); - if ((diff & CONFIG_LOCALE) != 0) { - Folder.setLocaleDependentFields(getResources(), true /* force */); - } - if ((diff & (CONFIG_ORIENTATION | CONFIG_SCREEN_SIZE)) != 0) { onIdpChanged(mDeviceProfile.inv); } @@ -497,6 +553,8 @@ public class Launcher extends BaseDraggingActivity implements LauncherExterns, // Load configuration-specific DeviceProfile mDeviceProfile = idp.getDeviceProfile(this); if (isInMultiWindowMode()) { + // Note: Calls to getSize() can't rely on our cached DefaultDisplay since it can return + // the app window size Display display = getWindowManager().getDefaultDisplay(); Point mwSize = new Point(); display.getSize(mwSize); @@ -565,6 +623,7 @@ public class Launcher extends BaseDraggingActivity implements LauncherExterns, /** * Call this after onCreate to set or clear overlay. */ + @Override public void setLauncherOverlay(LauncherOverlay overlay) { if (overlay != null) { overlay.setOverlayCallbacks(new LauncherOverlayCallbacksImpl()); @@ -572,18 +631,16 @@ public class Launcher extends BaseDraggingActivity implements LauncherExterns, mWorkspace.setLauncherOverlay(overlay); } + @Override + public void runOnOverlayHidden(Runnable runnable) { + getWorkspace().runOnOverlayHidden(runnable); + } + public boolean setLauncherCallbacks(LauncherCallbacks callbacks) { mLauncherCallbacks = callbacks; return true; } - @Override - public void onLauncherProviderChanged() { - if (mLauncherCallbacks != null) { - mLauncherCallbacks.onLauncherProviderChange(); - } - } - public boolean isDraggingEnabled() { // We prevent dragging when we are loading the workspace as it is possible to pick up a view // that is subsequently removed from the workspace in startBinding(). @@ -604,10 +661,9 @@ public class Launcher extends BaseDraggingActivity implements LauncherExterns, if (info.container >= 0) { View folderIcon = getWorkspace().getHomescreenIconByItemId(info.container); if (folderIcon instanceof FolderIcon && folderIcon.getTag() instanceof FolderInfo) { - FolderIconPreviewVerifier verifier = - new FolderIconPreviewVerifier(getDeviceProfile().inv); - verifier.setFolderInfo((FolderInfo) folderIcon.getTag()); - if (verifier.isItemInPreview(info.rank)) { + if (new FolderGridOrganizer(getDeviceProfile().inv) + .setFolderInfo((FolderInfo) folderIcon.getTag()) + .isItemInPreview(info.rank)) { folderIcon.invalidate(); } } @@ -647,7 +703,8 @@ public class Launcher extends BaseDraggingActivity implements LauncherExterns, .getLauncherAppWidgetInfo(widgetId); if (provider != null) { new WidgetAddFlowHandler(provider) - .startConfigActivity(this, widgetInfo, REQUEST_RECONFIGURE_APPWIDGET); + .startConfigActivity(this, widgetInfo, + REQUEST_RECONFIGURE_APPWIDGET); } } break; @@ -782,9 +839,6 @@ public class Launcher extends BaseDraggingActivity implements LauncherExterns, final int requestCode, final int resultCode, final Intent data) { mPendingActivityRequestCode = -1; handleActivityResult(requestCode, resultCode, data); - if (mLauncherCallbacks != null) { - mLauncherCallbacks.onActivityResult(requestCode, resultCode, data); - } } @Override @@ -811,10 +865,6 @@ public class Launcher extends BaseDraggingActivity implements LauncherExterns, getString(R.string.derived_app_name)), Toast.LENGTH_SHORT).show(); } } - if (mLauncherCallbacks != null) { - mLauncherCallbacks.onRequestPermissionsResult(requestCode, permissions, - grantResults); - } } /** @@ -835,7 +885,8 @@ public class Launcher extends BaseDraggingActivity implements LauncherExterns, } } - @Thunk void completeTwoStageWidgetDrop( + @Thunk + void completeTwoStageWidgetDrop( final int resultCode, final int appWidgetId, final PendingRequestArgs requestArgs) { CellLayout cellLayout = mWorkspace.getScreenWithId(requestArgs.screenId); Runnable onCompleteRunnable = null; @@ -872,9 +923,12 @@ public class Launcher extends BaseDraggingActivity implements LauncherExterns, protected void onStop() { super.onStop(); - if (mLauncherCallbacks != null) { - mLauncherCallbacks.onStop(); + if (mDeferOverlayCallbacks) { + checkIfOverlayStillDeferred(); + } else { + mOverlayManager.onActivityStopped(this); } + logStopAndResume(Action.Command.STOP); mAppWidgetHost.setListenIfResumed(false); @@ -890,13 +944,16 @@ public class Launcher extends BaseDraggingActivity implements LauncherExterns, @Override protected void onStart() { + DejankBinderTracker.allowBinderTrackingInTests(); RaceConditionTracker.onEvent(ON_START_EVT, ENTER); super.onStart(); - if (mLauncherCallbacks != null) { - mLauncherCallbacks.onStart(); + if (!mDeferOverlayCallbacks) { + mOverlayManager.onActivityStarted(this); } + mAppWidgetHost.setListenIfResumed(true); RaceConditionTracker.onEvent(ON_START_EVT, EXIT); + DejankBinderTracker.disallowBinderTrackingInTests(); } private void handleDeferredResume() { @@ -932,25 +989,66 @@ public class Launcher extends BaseDraggingActivity implements LauncherExterns, int containerType = mStateManager.getState().containerType; if (containerType == ContainerType.WORKSPACE && mWorkspace != null) { getUserEventDispatcher().logActionCommand(command, - containerType, -1, mWorkspace.isOverlayShown() ? -1 : 0); + containerType, -1, mWorkspace.isOverlayShown() ? -1 : 0); } else { getUserEventDispatcher().logActionCommand(command, containerType, -1); } - } - protected void onStateSet(LauncherState state) { - getAppWidgetHost().setResumed(state == LauncherState.NORMAL); + private void scheduleDeferredCheck() { + mHandler.removeCallbacks(mDeferredOverlayCallbacks); + postAsyncCallback(mHandler, mDeferredOverlayCallbacks); + } + + private void checkIfOverlayStillDeferred() { + if (!mDeferOverlayCallbacks) { + return; + } + if (isStarted() && (!hasBeenResumed() || mStateManager.getState().disableInteraction)) { + return; + } + mDeferOverlayCallbacks = false; + + // Move the client to the correct state. Calling the same method twice is no-op. + if (isStarted()) { + mOverlayManager.onActivityStarted(this); + } + if (hasBeenResumed()) { + mOverlayManager.onActivityResumed(this); + } else { + mOverlayManager.onActivityPaused(this); + } + if (!isStarted()) { + mOverlayManager.onActivityStopped(this); + } + } + + public void deferOverlayCallbacksUntilNextResumeOrStop() { + mDeferOverlayCallbacks = true; + } + + public LauncherOverlayManager getOverlayManager() { + return mOverlayManager; + } + + public void onStateSetStart(LauncherState state) { if (mDeferredResumePending) { handleDeferredResume(); } - if (mLauncherCallbacks != null) { - mLauncherCallbacks.onStateChanged(); + if (mDeferOverlayCallbacks) { + scheduleDeferredCheck(); } } + public void onStateSetEnd(LauncherState state) { + getAppWidgetHost().setResumed(state == LauncherState.NORMAL); + getWorkspace().setClipChildren(!state.disablePageClipping); + finishAutoCancelActionMode(); + } + @Override protected void onResume() { + DejankBinderTracker.allowBinderTrackingInTests(); RaceConditionTracker.onEvent(ON_RESUME_EVT, ENTER); TraceHelper.beginSection("ON_RESUME"); super.onResume(); @@ -968,12 +1066,15 @@ public class Launcher extends BaseDraggingActivity implements LauncherExterns, resumeCallbacks.clear(); } - if (mLauncherCallbacks != null) { - mLauncherCallbacks.onResume(); + if (mDeferOverlayCallbacks) { + scheduleDeferredCheck(); + } else { + mOverlayManager.onActivityResumed(this); } TraceHelper.endSection("ON_RESUME"); RaceConditionTracker.onEvent(ON_RESUME_EVT, EXIT); + DejankBinderTracker.disallowBinderTrackingInTests(); } @Override @@ -985,8 +1086,9 @@ public class Launcher extends BaseDraggingActivity implements LauncherExterns, mDragController.cancelDrag(); mDragController.resetLastGestureUpTime(); mDropTargetBar.animateToVisibility(false); - if (mLauncherCallbacks != null) { - mLauncherCallbacks.onPause(); + + if (!mDeferOverlayCallbacks) { + mOverlayManager.onActivityPaused(this); } } @@ -1002,35 +1104,6 @@ public class Launcher extends BaseDraggingActivity implements LauncherExterns, mStateManager.onWindowFocusChanged(); } - public interface LauncherOverlay { - - /** - * Touch interaction leading to overscroll has begun - */ - void onScrollInteractionBegin(); - - /** - * Touch interaction related to overscroll has ended - */ - void onScrollInteractionEnd(); - - /** - * Scroll progress, between 0 and 100, when the user scrolls beyond the leftmost - * screen (or in the case of RTL, the rightmost screen). - */ - void onScrollChange(float progress, boolean rtl); - - /** - * Called when the launcher is ready to use the overlay - * @param callbacks A set of callbacks provided by Launcher in relation to the overlay - */ - void setOverlayCallbacks(LauncherOverlayCallbacks callbacks); - } - - public interface LauncherOverlayCallbacks { - void onScrollChanged(float progress); - } - class LauncherOverlayCallbacksImpl implements LauncherOverlayCallbacks { public void onScrollChanged(float progress) { @@ -1061,7 +1134,8 @@ public class Launcher extends BaseDraggingActivity implements LauncherExterns, mStateManager.goToState(state, false /* animated */); } - PendingRequestArgs requestArgs = savedState.getParcelable(RUNTIME_STATE_PENDING_REQUEST_ARGS); + PendingRequestArgs requestArgs = savedState.getParcelable( + RUNTIME_STATE_PENDING_REQUEST_ARGS); if (requestArgs != null) { setWaitingForResult(requestArgs); } @@ -1131,8 +1205,7 @@ public class Launcher extends BaseDraggingActivity implements LauncherExterns, * Creates a view representing a shortcut inflated from the specified resource. * * @param parent The group the shortcut belongs to. - * @param info The data structure describing the shortcut. - * + * @param info The data structure describing the shortcut. * @return A View inflated from layoutResId. */ public View createShortcut(ViewGroup parent, WorkspaceItemInfo info) { @@ -1234,7 +1307,8 @@ public class Launcher extends BaseDraggingActivity implements LauncherExterns, * * @param appWidgetId The app widget id */ - @Thunk void completeAddAppWidget(int appWidgetId, ItemInfo itemInfo, + @Thunk + void completeAddAppWidget(int appWidgetId, ItemInfo itemInfo, AppWidgetHostView hostView, LauncherAppWidgetProviderInfo appWidgetInfo) { if (appWidgetInfo == null) { @@ -1287,19 +1361,14 @@ public class Launcher extends BaseDraggingActivity implements LauncherExterns, @Override public void onAttachedToWindow() { super.onAttachedToWindow(); - - if (mLauncherCallbacks != null) { - mLauncherCallbacks.onAttachedToWindow(); - } + mOverlayManager.onAttachedToWindow(); } @Override public void onDetachedFromWindow() { super.onDetachedFromWindow(); - - if (mLauncherCallbacks != null) { - mLauncherCallbacks.onDetachedFromWindow(); - } + mOverlayManager.onDetachedFromWindow(); + closeContextMenu(); } public AllAppsTransitionController getAllAppsController() { @@ -1348,11 +1417,19 @@ public class Launcher extends BaseDraggingActivity implements LauncherExterns, return mModelWriter; } + @Override public SharedPreferences getSharedPrefs() { return mSharedPrefs; } - public int getOrientation() { return mOldConfig.orientation; } + @Override + public SharedPreferences getDevicePrefs() { + return Utilities.getDevicePrefs(this); + } + + public int getOrientation() { + return mOldConfig.orientation; + } @Override protected void onNewIntent(Intent intent) { @@ -1406,6 +1483,7 @@ public class Launcher extends BaseDraggingActivity implements LauncherExterns, if (mLauncherCallbacks != null) { mLauncherCallbacks.onHomeIntent(internalStateHandled); } + mOverlayManager.hideOverlay(isStarted() && !isForceInvisible()); } TraceHelper.endSection("NEW_INTENT"); @@ -1436,9 +1514,10 @@ public class Launcher extends BaseDraggingActivity implements LauncherExterns, outState.remove(RUNTIME_STATE_WIDGET_PANEL); } - // We close any open folders and shortcut containers since they will not be re-opened, + // We close any open folders and shortcut containers that are not safe for rebind, // and we need to make sure this state is reflected. - AbstractFloatingView.closeAllOpenViews(this, false); + AbstractFloatingView.closeOpenViews(this, false, TYPE_ALL & ~TYPE_REBIND_SAFE); + finishAutoCancelActionMode(); if (mPendingRequestArgs != null) { outState.putParcelable(RUNTIME_STATE_PENDING_REQUEST_ARGS, mPendingRequestArgs); @@ -1450,10 +1529,7 @@ public class Launcher extends BaseDraggingActivity implements LauncherExterns, } super.onSaveInstanceState(outState); - - if (mLauncherCallbacks != null) { - mLauncherCallbacks.onSaveInstanceState(outState); - } + mOverlayManager.onActivitySaveInstanceState(this, outState); } @Override @@ -1462,6 +1538,7 @@ public class Launcher extends BaseDraggingActivity implements LauncherExterns, unregisterReceiver(mScreenOffReceiver); mWorkspace.removeFolderListeners(); + PluginManagerWrapper.INSTANCE.get(this).removePluginListener(this); if (mCancelTouchController != null) { mCancelTouchController.run(); @@ -1486,9 +1563,8 @@ public class Launcher extends BaseDraggingActivity implements LauncherExterns, TextKeyListener.getInstance().release(); clearPendingBinds(); LauncherAppState.getIDP(this).removeOnChangeListener(this); - if (mLauncherCallbacks != null) { - mLauncherCallbacks.onDestroy(); - } + + mOverlayManager.onActivityDestroyed(this); } public LauncherAccessibilityDelegate getAccessibilityDelegate() { @@ -1575,7 +1651,8 @@ public class Launcher extends BaseDraggingActivity implements LauncherExterns, void addAppWidgetImpl(int appWidgetId, ItemInfo info, AppWidgetHostView boundWidget, WidgetAddFlowHandler addFlowHandler, int delay) { - if (!addFlowHandler.startConfigActivity(this, appWidgetId, info, REQUEST_CREATE_APPWIDGET)) { + if (!addFlowHandler.startConfigActivity(this, appWidgetId, info, + REQUEST_CREATE_APPWIDGET)) { // If the configuration flow was not started, add the widget Runnable onComplete = new Runnable() { @@ -1585,7 +1662,8 @@ public class Launcher extends BaseDraggingActivity implements LauncherExterns, mStateManager.goToState(NORMAL, SPRING_LOADED_EXIT_DELAY); } }; - completeAddAppWidget(appWidgetId, info, boundWidget, addFlowHandler.getProviderInfo(this)); + completeAddAppWidget(appWidgetId, info, boundWidget, + addFlowHandler.getProviderInfo(this)); mWorkspace.removeExtraEmptyScreenDelayed(true, onComplete, delay, false); } } @@ -1611,7 +1689,7 @@ public class Launcher extends BaseDraggingActivity implements LauncherExterns, break; default: throw new IllegalStateException("Unknown item type: " + info.itemType); - } + } } /** @@ -1647,10 +1725,9 @@ public class Launcher extends BaseDraggingActivity implements LauncherExterns, } else { // In this case, we either need to start an activity to get permission to bind // the widget, or we need to start an activity to configure the widget, or both. - if (FeatureFlags.ENABLE_CUSTOM_WIDGETS && - info.itemType == LauncherSettings.Favorites.ITEM_TYPE_CUSTOM_APPWIDGET) { - appWidgetId = CustomWidgetParser.getWidgetIdForCustomProvider( - this, info.componentName); + if (info.itemType == LauncherSettings.Favorites.ITEM_TYPE_CUSTOM_APPWIDGET) { + appWidgetId = CustomWidgetManager.INSTANCE.get(this).getWidgetIdForCustomProvider( + info.componentName); } else { appWidgetId = getAppWidgetHost().allocateAppWidgetId(); } @@ -1669,7 +1746,7 @@ public class Launcher extends BaseDraggingActivity implements LauncherExterns, FolderIcon addFolder(CellLayout layout, int container, final int screenId, int cellX, int cellY) { final FolderInfo folderInfo = new FolderInfo(); - folderInfo.title = getText(R.string.folder_name); + folderInfo.title = ""; // Update the model getModelWriter().addItemToDatabase(folderInfo, container, screenId, cellX, cellY); @@ -1723,8 +1800,6 @@ public class Launcher extends BaseDraggingActivity implements LauncherExterns, return true; } - - @Override public boolean dispatchKeyEvent(KeyEvent event) { return (event.getKeyCode() == KeyEvent.KEYCODE_HOME) || super.dispatchKeyEvent(event); @@ -1735,9 +1810,6 @@ public class Launcher extends BaseDraggingActivity implements LauncherExterns, if (finishAutoCancelActionMode()) { return; } - if (mLauncherCallbacks != null && mLauncherCallbacks.handleBackPressed()) { - return; - } if (mDragController.isDragging()) { mDragController.cancelDrag(); @@ -1788,7 +1860,7 @@ public class Launcher extends BaseDraggingActivity implements LauncherExterns, @Override public int getCurrentState() { - if(mStateManager.getState() == LauncherState.ALL_APPS) { + if (mStateManager.getState() == LauncherState.ALL_APPS) { return StatsLogUtils.LAUNCHER_STATE_ALLAPPS; } else if (mStateManager.getState() == OVERVIEW) { return StatsLogUtils.LAUNCHER_STATE_OVERVIEW; @@ -1862,9 +1934,6 @@ public class Launcher extends BaseDraggingActivity implements LauncherExterns, // This clears all widget bitmaps from the widget tray // TODO(hyunyoungs) } - if (mLauncherCallbacks != null) { - mLauncherCallbacks.onTrimMemory(level); - } UiFactory.onTrimMemory(this, level); } @@ -1923,8 +1992,7 @@ public class Launcher extends BaseDraggingActivity implements LauncherExterns, // Floating panels (except the full widget sheet) are associated with individual icons. If // we are starting a fresh bind, close all such panels as all the icons are about // to go away. - AbstractFloatingView.closeOpenViews(this, true, - AbstractFloatingView.TYPE_ALL & ~AbstractFloatingView.TYPE_REBIND_SAFE); + AbstractFloatingView.closeOpenViews(this, true, TYPE_ALL & ~TYPE_REBIND_SAFE); setWorkspaceLoading(true); @@ -2052,7 +2120,7 @@ public class Launcher extends BaseDraggingActivity implements LauncherExterns, throw new RuntimeException("Invalid Item Type"); } - /* + /* * Remove colliding items. */ if (item.container == LauncherSettings.Favorites.CONTAINER_DESKTOP) { @@ -2124,6 +2192,14 @@ public class Launcher extends BaseDraggingActivity implements LauncherExterns, } private View inflateAppWidget(LauncherAppWidgetInfo item) { + if (item.hasOptionFlag(LauncherAppWidgetInfo.OPTION_SEARCH_WIDGET)) { + item.providerName = QsbContainerView.getSearchComponentName(this); + if (item.providerName == null) { + getModelWriter().deleteItemFromDatabase(item); + return null; + } + } + if (mIsSafeModeEnabled) { PendingAppWidgetHostView view = new PendingAppWidgetHostView(this, item, mIconCache, true); @@ -2170,7 +2246,8 @@ public class Launcher extends BaseDraggingActivity implements LauncherExterns, pendingInfo.spanY = item.spanY; pendingInfo.minSpanX = item.minSpanX; pendingInfo.minSpanY = item.minSpanY; - Bundle options = WidgetHostViewLoader.getDefaultOptionsForWidget(this, pendingInfo); + Bundle options = WidgetHostViewLoader.getDefaultOptionsForWidget(this, + pendingInfo); boolean isDirectConfig = item.hasRestoreFlag(LauncherAppWidgetInfo.FLAG_DIRECT_CONFIG); @@ -2324,6 +2401,11 @@ public class Launcher extends BaseDraggingActivity implements LauncherExterns, // override the previous page so we don't log the page switch. mWorkspace.setCurrentPage(pageBoundFirst, pageBoundFirst /* overridePrevPage */); + // Cache one page worth of icons + getViewCache().setCacheSize(R.layout.folder_application, + mDeviceProfile.inv.numFolderColumns * mDeviceProfile.inv.numFolderRows); + getViewCache().setCacheSize(R.layout.folder_page, 2); + TraceHelper.endSection("finishBindingItems"); } @@ -2345,7 +2427,7 @@ public class Launcher extends BaseDraggingActivity implements LauncherExterns, * * Implementation of the method from LauncherModel.Callbacks. */ - public void bindAllApplications(ArrayList apps) { + public void bindAllApplications(AppInfo[] apps) { mAppsView.getAppsStore().setApps(apps); } @@ -2358,16 +2440,6 @@ public class Launcher extends BaseDraggingActivity implements LauncherExterns, mPopupDataProvider.setDeepShortcutMap(deepShortcutMapCopy); } - /** - * A package was updated. - * - * Implementation of the method from LauncherModel.Callbacks. - */ - @Override - public void bindAppsAddedOrUpdated(ArrayList apps) { - mAppsView.getAppsStore().addOrUpdateApps(apps); - } - @Override public void bindPromiseAppProgressUpdated(PromiseAppInfo app) { mAppsView.getAppsStore().updatePromiseAppProgress(app); @@ -2414,11 +2486,6 @@ public class Launcher extends BaseDraggingActivity implements LauncherExterns, mDragController.onAppsRemoved(matcher); } - @Override - public void bindAppInfosRemoved(final ArrayList appInfos) { - mAppsView.getAppsStore().removeApps(appInfos); - } - @Override public void bindAllWidgets(final ArrayList allWidgets) { mPopupDataProvider.setAllWidgets(allWidgets); @@ -2464,14 +2531,16 @@ public class Launcher extends BaseDraggingActivity implements LauncherExterns, } writer.println(prefix + "Misc:"); - writer.print(prefix + "\tmWorkspaceLoading=" + mWorkspaceLoading); - writer.print(" mPendingRequestArgs=" + mPendingRequestArgs); - writer.println(" mPendingActivityResult=" + mPendingActivityResult); - writer.println(" mRotationHelper: " + mRotationHelper); + dumpMisc(prefix + "\t", writer); + writer.println(prefix + "\tmWorkspaceLoading=" + mWorkspaceLoading); + writer.println(prefix + "\tmPendingRequestArgs=" + mPendingRequestArgs + + " mPendingActivityResult=" + mPendingActivityResult); + writer.println(prefix + "\tmRotationHelper: " + mRotationHelper); + writer.println(prefix + "\tmAppWidgetHost.isListening: " + mAppWidgetHost.isListening()); + // Extra logging for b/116853349 mDragLayer.dump(prefix, writer); mStateManager.dump(prefix, writer); - dumpMisc(writer); try { FileLog.flushAll(writer); @@ -2484,6 +2553,7 @@ public class Launcher extends BaseDraggingActivity implements LauncherExterns, if (mLauncherCallbacks != null) { mLauncherCallbacks.dump(prefix, fd, writer, args); } + mOverlayManager.dump(prefix, writer); } @Override @@ -2532,8 +2602,8 @@ public class Launcher extends BaseDraggingActivity implements LauncherExterns, if (focusedView instanceof BubbleTextView && focusedView.getTag() instanceof ItemInfo && mAccessibilityDelegate.performAction(focusedView, - (ItemInfo) focusedView.getTag(), - LauncherAccessibilityDelegate.DEEP_SHORTCUTS)) { + (ItemInfo) focusedView.getTag(), + LauncherAccessibilityDelegate.DEEP_SHORTCUTS)) { PopupContainerWithArrow.getOpen(this).requestFocus(); return true; } @@ -2578,6 +2648,12 @@ public class Launcher extends BaseDraggingActivity implements LauncherExterns, return (Launcher) fromContext(context); } + @Override + public void returnToHomescreen() { + super.returnToHomescreen(); + getStateManager().goToState(LauncherState.NORMAL); + } + /** * Just a wrapper around the type cast to allow easier tracking of calls. */ diff --git a/src/com/android/launcher3/LauncherAppState.java b/src/com/android/launcher3/LauncherAppState.java index b4a2216c57..d70abc2a95 100644 --- a/src/com/android/launcher3/LauncherAppState.java +++ b/src/com/android/launcher3/LauncherAppState.java @@ -37,6 +37,7 @@ import com.android.launcher3.notification.NotificationListener; import com.android.launcher3.util.MainThreadInitializedObject; import com.android.launcher3.util.Preconditions; import com.android.launcher3.util.SecureSettingsObserver; +import com.android.launcher3.widget.custom.CustomWidgetManager; public class LauncherAppState { @@ -149,6 +150,8 @@ public class LauncherAppState { LauncherModel setLauncher(Launcher launcher) { getLocalProvider(mContext).setLauncherProviderChangeListener(launcher); mModel.initialize(launcher); + CustomWidgetManager.INSTANCE.get(launcher) + .setWidgetRefreshCallback(mModel::refreshAndBindWidgetsAndShortcuts); return mModel; } diff --git a/src/com/android/launcher3/LauncherAppWidgetHost.java b/src/com/android/launcher3/LauncherAppWidgetHost.java index 7f5ac52760..4e29a95fb2 100644 --- a/src/com/android/launcher3/LauncherAppWidgetHost.java +++ b/src/com/android/launcher3/LauncherAppWidgetHost.java @@ -27,14 +27,15 @@ import android.content.Context; import android.content.Intent; import android.os.Handler; import android.util.SparseArray; -import android.view.LayoutInflater; import android.widget.Toast; -import com.android.launcher3.config.FeatureFlags; +import com.android.launcher3.model.WidgetsModel; import com.android.launcher3.widget.DeferredAppWidgetHostView; import com.android.launcher3.widget.LauncherAppWidgetHostView; +import com.android.launcher3.widget.custom.CustomWidgetManager; import java.util.ArrayList; +import java.util.function.IntConsumer; /** @@ -56,9 +57,17 @@ public class LauncherAppWidgetHost extends AppWidgetHost { private final Context mContext; private int mFlags = FLAG_RESUMED; + private IntConsumer mAppWidgetRemovedCallback = null; + public LauncherAppWidgetHost(Context context) { + this(context, null); + } + + public LauncherAppWidgetHost(Context context, + IntConsumer appWidgetRemovedCallback) { super(context, APPWIDGET_HOST_ID); mContext = context; + mAppWidgetRemovedCallback = appWidgetRemovedCallback; } @Override @@ -71,7 +80,7 @@ public class LauncherAppWidgetHost extends AppWidgetHost { @Override public void startListening() { - if (FeatureFlags.GO_DISABLE_WIDGETS) { + if (WidgetsModel.GO_DISABLE_WIDGETS) { return; } mFlags |= FLAG_LISTENING; @@ -98,13 +107,17 @@ public class LauncherAppWidgetHost extends AppWidgetHost { @Override public void stopListening() { - if (FeatureFlags.GO_DISABLE_WIDGETS) { + if (WidgetsModel.GO_DISABLE_WIDGETS) { return; } mFlags &= ~FLAG_LISTENING; super.stopListening(); } + public boolean isListening() { + return (mFlags & FLAG_LISTENING) != 0; + } + /** * Updates the resumed state of the host. * When a host is not resumed, it defers calls to startListening until host is resumed again. @@ -153,7 +166,7 @@ public class LauncherAppWidgetHost extends AppWidgetHost { @Override public int allocateAppWidgetId() { - if (FeatureFlags.GO_DISABLE_WIDGETS) { + if (WidgetsModel.GO_DISABLE_WIDGETS) { return AppWidgetManager.INVALID_APPWIDGET_ID; } @@ -180,10 +193,8 @@ public class LauncherAppWidgetHost extends AppWidgetHost { LauncherAppWidgetProviderInfo appWidget) { if (appWidget.isCustomWidget()) { LauncherAppWidgetHostView lahv = new LauncherAppWidgetHostView(context); - LayoutInflater inflater = (LayoutInflater) - context.getSystemService(Context.LAYOUT_INFLATER_SERVICE); - inflater.inflate(appWidget.initialLayout, lahv); lahv.setAppWidget(0, appWidget); + CustomWidgetManager.INSTANCE.get(context).onViewCreated(lahv); return lahv; } else if ((mFlags & FLAG_LISTENING) == 0) { DeferredAppWidgetHostView view = new DeferredAppWidgetHostView(context); @@ -207,7 +218,7 @@ public class LauncherAppWidgetHost extends AppWidgetHost { } view.setAppWidget(appWidgetId, appWidget); view.switchToErrorView(); - return view; + return view; } } } @@ -225,6 +236,18 @@ public class LauncherAppWidgetHost extends AppWidgetHost { info.initSpans(mContext); } + /** + * Called on an appWidget is removed for a widgetId + * @param appWidgetId + * TODO: make this override when SDK is updated + */ + public void onAppWidgetRemoved(int appWidgetId) { + if (mAppWidgetRemovedCallback == null) { + return; + } + mAppWidgetRemovedCallback.accept(appWidgetId); + } + @Override public void deleteAppWidgetId(int appWidgetId) { super.deleteAppWidgetId(appWidgetId); @@ -240,7 +263,7 @@ public class LauncherAppWidgetHost extends AppWidgetHost { public void startBindFlow(BaseActivity activity, int appWidgetId, AppWidgetProviderInfo info, int requestCode) { - if (FeatureFlags.GO_DISABLE_WIDGETS) { + if (WidgetsModel.GO_DISABLE_WIDGETS) { sendActionCancelled(activity, requestCode); return; } @@ -256,7 +279,7 @@ public class LauncherAppWidgetHost extends AppWidgetHost { public void startConfigActivity(BaseActivity activity, int widgetId, int requestCode) { - if (FeatureFlags.GO_DISABLE_WIDGETS) { + if (WidgetsModel.GO_DISABLE_WIDGETS) { sendActionCancelled(activity, requestCode); return; } diff --git a/src/com/android/launcher3/LauncherAppWidgetInfo.java b/src/com/android/launcher3/LauncherAppWidgetInfo.java index 051846c871..b82430184a 100644 --- a/src/com/android/launcher3/LauncherAppWidgetInfo.java +++ b/src/com/android/launcher3/LauncherAppWidgetInfo.java @@ -29,6 +29,9 @@ import com.android.launcher3.util.ContentWriter; */ public class LauncherAppWidgetInfo extends ItemInfo { + public static final int OPTION_SEARCH_WIDGET = 1; + + public static final int RESTORE_COMPLETED = 0; /** @@ -96,6 +99,11 @@ public class LauncherAppWidgetInfo extends ItemInfo { */ public Intent bindOptions; + /** + * Widget options + */ + public int options; + /** * Nonnull for pending widgets. We use this to get the icon and title for the widget. */ @@ -137,6 +145,7 @@ public class LauncherAppWidgetInfo extends ItemInfo { writer.put(LauncherSettings.Favorites.APPWIDGET_ID, appWidgetId) .put(LauncherSettings.Favorites.APPWIDGET_PROVIDER, providerName.flattenToString()) .put(LauncherSettings.Favorites.RESTORED, restoreStatus) + .put(LauncherSettings.Favorites.OPTIONS, options) .put(LauncherSettings.Favorites.INTENT, bindOptions); } @@ -164,4 +173,13 @@ public class LauncherAppWidgetInfo extends ItemInfo { public final boolean hasRestoreFlag(int flag) { return (restoreStatus & flag) == flag; } + + /** + * returns if widget options include an option or not + * @param option + * @return + */ + public final boolean hasOptionFlag(int option) { + return (options & option) != 0; + } } diff --git a/src/com/android/launcher3/LauncherCallbacks.java b/src/com/android/launcher3/LauncherCallbacks.java index dfe75ec34a..0e529bd376 100644 --- a/src/com/android/launcher3/LauncherCallbacks.java +++ b/src/com/android/launcher3/LauncherCallbacks.java @@ -16,7 +16,6 @@ package com.android.launcher3; -import android.content.Intent; import android.os.Bundle; import java.io.FileDescriptor; @@ -36,31 +35,8 @@ public interface LauncherCallbacks { * the code in the corresponding Launcher method is executed. */ void onCreate(Bundle savedInstanceState); - void onResume(); - void onStart(); - void onStop(); - void onPause(); - void onDestroy(); - void onSaveInstanceState(Bundle outState); - void onActivityResult(int requestCode, int resultCode, Intent data); - void onRequestPermissionsResult(int requestCode, String[] permissions, - int[] grantResults); - void onAttachedToWindow(); - void onDetachedFromWindow(); void dump(String prefix, FileDescriptor fd, PrintWriter w, String[] args); void onHomeIntent(boolean internalStateHandled); - boolean handleBackPressed(); - void onTrimMemory(int level); - - /** - * Called when the launcher state changed - */ - default void onStateChanged() { } - - /* - * Extension points for providing custom behavior on certain user interactions. - */ - void onLauncherProviderChange(); /** * Starts a search with {@param initialQuery}. Return false if search was not started. diff --git a/src/com/android/launcher3/LauncherModel.java b/src/com/android/launcher3/LauncherModel.java index a0414894b1..c0cf135441 100644 --- a/src/com/android/launcher3/LauncherModel.java +++ b/src/com/android/launcher3/LauncherModel.java @@ -18,29 +18,32 @@ package com.android.launcher3; import static com.android.launcher3.LauncherAppState.ACTION_FORCE_ROLOAD; import static com.android.launcher3.config.FeatureFlags.IS_DOGFOOD_BUILD; +import static com.android.launcher3.util.Executors.MAIN_EXECUTOR; +import static com.android.launcher3.util.Executors.MODEL_EXECUTOR; import android.content.BroadcastReceiver; -import android.content.ComponentName; import android.content.Context; import android.content.Intent; import android.content.pm.ShortcutInfo; -import android.os.Handler; -import android.os.HandlerThread; -import android.os.Looper; import android.os.Process; import android.os.UserHandle; import android.text.TextUtils; import android.util.Log; import android.util.Pair; +import androidx.annotation.Nullable; + import com.android.launcher3.compat.LauncherAppsCompat; import com.android.launcher3.compat.PackageInstallerCompat.PackageInstallInfo; import com.android.launcher3.compat.UserManagerCompat; import com.android.launcher3.icons.IconCache; import com.android.launcher3.icons.LauncherIcons; +import com.android.launcher3.logging.FileLog; import com.android.launcher3.model.AddWorkspaceItemsTask; +import com.android.launcher3.model.AllAppsList; import com.android.launcher3.model.BaseModelUpdateTask; import com.android.launcher3.model.BgDataModel; +import com.android.launcher3.model.BgDataModel.Callbacks; import com.android.launcher3.model.CacheDataUpdatedTask; import com.android.launcher3.model.LoaderResults; import com.android.launcher3.model.LoaderTask; @@ -50,29 +53,22 @@ import com.android.launcher3.model.PackageUpdatedTask; import com.android.launcher3.model.ShortcutsChangedTask; import com.android.launcher3.model.UserLockStateChangedTask; import com.android.launcher3.shortcuts.DeepShortcutManager; -import com.android.launcher3.util.ComponentKey; -import com.android.launcher3.util.IntArray; import com.android.launcher3.util.IntSparseArrayMap; import com.android.launcher3.util.ItemInfoMatcher; import com.android.launcher3.util.PackageUserKey; import com.android.launcher3.util.Preconditions; import com.android.launcher3.util.Thunk; -import com.android.launcher3.util.ViewOnDrawExecutor; -import com.android.launcher3.widget.WidgetListRowEntry; import java.io.FileDescriptor; import java.io.PrintWriter; import java.lang.ref.WeakReference; import java.util.ArrayList; -import java.util.HashMap; import java.util.HashSet; import java.util.List; import java.util.concurrent.CancellationException; import java.util.concurrent.Executor; import java.util.function.Supplier; -import androidx.annotation.Nullable; - /** * Maintains in-memory state of the Launcher. It is expected that there should be only one * LauncherModel object held in a static. Also provide APIs for updating the database state @@ -84,21 +80,12 @@ public class LauncherModel extends BroadcastReceiver static final String TAG = "Launcher.Model"; - private final MainThreadExecutor mUiExecutor = new MainThreadExecutor(); @Thunk final LauncherAppState mApp; @Thunk final Object mLock = new Object(); @Thunk LoaderTask mLoaderTask; @Thunk boolean mIsLoaderTaskRunning; - @Thunk static final HandlerThread sWorkerThread = new HandlerThread("launcher-loader"); - private static final Looper mWorkerLooper; - static { - sWorkerThread.start(); - mWorkerLooper = sWorkerThread.getLooper(); - } - @Thunk static final Handler sWorker = new Handler(mWorkerLooper); - // Indicates whether the current model data is valid or not. // We start off with everything not loaded. After that, we assume that // our monitoring of the package manager provides all updates and we never @@ -135,33 +122,6 @@ public class LauncherModel extends BroadcastReceiver } }; - public interface Callbacks { - public void rebindModel(); - - public int getCurrentWorkspaceScreen(); - public void clearPendingBinds(); - public void startBinding(); - public void bindItems(List shortcuts, boolean forceAnimateIcons); - public void bindScreens(IntArray orderedScreenIds); - public void finishFirstPageBind(ViewOnDrawExecutor executor); - public void finishBindingItems(int pageBoundFirst); - public void bindAllApplications(ArrayList apps); - public void bindAppsAddedOrUpdated(ArrayList apps); - public void preAddApps(); - public void bindAppsAdded(IntArray newScreens, - ArrayList addNotAnimated, ArrayList addAnimated); - public void bindPromiseAppProgressUpdated(PromiseAppInfo app); - public void bindWorkspaceItemsChanged(ArrayList updated); - public void bindWidgetsRestored(ArrayList widgets); - public void bindRestoreItemsChange(HashSet updates); - public void bindWorkspaceComponentsRemoved(ItemInfoMatcher matcher); - public void bindAppInfosRemoved(ArrayList appInfos); - public void bindAllWidgets(ArrayList widgets); - public void onPageBoundSynchronously(int page); - public void executeOnNextDraw(ViewOnDrawExecutor executor); - public void bindDeepShortcutMap(HashMap deepShortcutMap); - } - LauncherModel(LauncherAppState app, IconCache iconCache, AppFilter appFilter) { mApp = app; mBgAllAppsList = new AllAppsList(iconCache, appFilter); @@ -244,6 +204,7 @@ public class LauncherModel extends BroadcastReceiver public void onPackagesRemoved(UserHandle user, String... packages) { int op = PackageUpdatedTask.OP_REMOVE; + FileLog.d(TAG, "package removed received " + String.join("," + packages)); enqueueModelUpdateTask(new PackageUpdatedTask(op, user, packages)); } @@ -299,7 +260,6 @@ public class LauncherModel extends BroadcastReceiver @Override public void onReceive(Context context, Intent intent) { if (DEBUG_RECEIVER) Log.d(TAG, "onReceive intent=" + intent); - final String action = intent.getAction(); if (Intent.ACTION_LOCALE_CHANGED.equals(action)) { // If we have changed locale we need to clear out the labels in all apps/workspace. @@ -375,7 +335,7 @@ public class LauncherModel extends BroadcastReceiver if (mCallbacks != null && mCallbacks.get() != null) { final Callbacks oldCallbacks = mCallbacks.get(); // Clear any pending bind-runnables from the synchronized load process. - mUiExecutor.execute(oldCallbacks::clearPendingBinds); + MAIN_EXECUTOR.execute(oldCallbacks::clearPendingBinds); // If there is already one running, tell it to stop. stopLoader(); @@ -419,7 +379,7 @@ public class LauncherModel extends BroadcastReceiver // Always post the loader task, instead of running directly (even on same thread) so // that we exit any nested synchronized blocks - sWorker.post(mLoaderTask); + MODEL_EXECUTOR.post(mLoaderTask); } } @@ -437,16 +397,7 @@ public class LauncherModel extends BroadcastReceiver @Override public void execute(LauncherAppState app, BgDataModel dataModel, AllAppsList apps) { apps.addPromiseApp(app.getContext(), sessionInfo); - if (!apps.added.isEmpty()) { - final ArrayList arrayList = new ArrayList<>(apps.added); - apps.added.clear(); - scheduleCallbackTask(new CallbackTask() { - @Override - public void execute(Callbacks callbacks) { - callbacks.bindAppsAddedOrUpdated(arrayList); - } - }); - } + bindApplicationsIfNeeded(); } }); } @@ -495,8 +446,8 @@ public class LauncherModel extends BroadcastReceiver * use partial updates similar to {@link UserManagerCompat} */ public void refreshShortcutsIfRequired() { - sWorker.removeCallbacks(mShortcutPermissionCheckRunnable); - sWorker.post(mShortcutPermissionCheckRunnable); + MODEL_EXECUTOR.getHandler().removeCallbacks(mShortcutPermissionCheckRunnable); + MODEL_EXECUTOR.post(mShortcutPermissionCheckRunnable); } /** @@ -523,14 +474,8 @@ public class LauncherModel extends BroadcastReceiver } public void enqueueModelUpdateTask(ModelUpdateTask task) { - task.init(mApp, this, sBgDataModel, mBgAllAppsList, mUiExecutor); - - if (sWorkerThread.getThreadId() == Process.myTid()) { - task.run(); - } else { - // If we are not on the worker thread, then post to the worker handler - sWorker.post(task); - } + task.init(mApp, this, sBgDataModel, mBgAllAppsList, MAIN_EXECUTOR); + MODEL_EXECUTOR.execute(task); } /** @@ -605,15 +550,4 @@ public class LauncherModel extends BroadcastReceiver public Callbacks getCallback() { return mCallbacks != null ? mCallbacks.get() : null; } - - /** - * @return the looper for the worker thread which can be used to start background tasks. - */ - public static Looper getWorkerLooper() { - return mWorkerLooper; - } - - public static void setWorkerPriority(final int priority) { - Process.setThreadPriority(sWorkerThread.getThreadId(), priority); - } } diff --git a/src/com/android/launcher3/LauncherProvider.java b/src/com/android/launcher3/LauncherProvider.java index 6ad5c3684c..d78c1b3cc8 100644 --- a/src/com/android/launcher3/LauncherProvider.java +++ b/src/com/android/launcher3/LauncherProvider.java @@ -33,7 +33,6 @@ import android.content.Context; import android.content.Intent; import android.content.OperationApplicationException; import android.content.SharedPreferences; -import android.content.pm.PackageManager.NameNotFoundException; import android.content.pm.ProviderInfo; import android.content.res.Resources; import android.database.Cursor; @@ -50,7 +49,6 @@ import android.os.Handler; import android.os.Message; import android.os.Process; import android.os.UserHandle; -import android.os.UserManager; import android.provider.BaseColumns; import android.provider.Settings; import android.text.TextUtils; @@ -70,6 +68,7 @@ import com.android.launcher3.util.IOUtils; import com.android.launcher3.util.IntArray; import com.android.launcher3.util.IntSet; import com.android.launcher3.util.NoLocaleSQLiteHelper; +import com.android.launcher3.util.PackageManagerHelper; import com.android.launcher3.util.Preconditions; import com.android.launcher3.util.Thunk; @@ -77,7 +76,6 @@ import org.xmlpull.v1.XmlPullParser; import java.io.File; import java.io.FileDescriptor; -import java.io.IOException; import java.io.InputStream; import java.io.PrintWriter; import java.io.StringReader; @@ -225,7 +223,6 @@ public class LauncherProvider extends ContentProvider { mOpenHelper.onAddOrDeleteOp(db); uri = ContentUris.withAppendedId(uri, rowId); - notifyListeners(); reloadLauncherIfExternal(); return uri; } @@ -285,7 +282,6 @@ public class LauncherProvider extends ContentProvider { t.commit(); } - notifyListeners(); reloadLauncherIfExternal(); return values.length; } @@ -331,7 +327,6 @@ public class LauncherProvider extends ContentProvider { int count = db.delete(args.table, args.where, args.args); if (count > 0) { mOpenHelper.onAddOrDeleteOp(db); - notifyListeners(); reloadLauncherIfExternal(); } return count; @@ -345,8 +340,6 @@ public class LauncherProvider extends ContentProvider { addModifiedTime(values); SQLiteDatabase db = mOpenHelper.getWritableDatabase(); int count = db.update(args.table, values, args.where, args.args); - if (count > 0) notifyListeners(); - reloadLauncherIfExternal(); return count; } @@ -440,13 +433,6 @@ public class LauncherProvider extends ContentProvider { } } - /** - * Overridden in tests - */ - protected void notifyListeners() { - mListenerHandler.sendEmptyMessage(ChangeListenerWrapper.MSG_LAUNCHER_PROVIDER_CHANGED); - } - @Thunk static void addModifiedTime(ContentValues values) { values.put(LauncherSettings.Favorites.MODIFIED, System.currentTimeMillis()); } @@ -873,7 +859,7 @@ public class LauncherProvider extends ContentProvider { continue; } - if (!Utilities.isLauncherAppTarget(intent)) { + if (!PackageManagerHelper.isLauncherAppTarget(intent)) { continue; } @@ -1044,7 +1030,6 @@ public class LauncherProvider extends ContentProvider { private static class ChangeListenerWrapper implements Handler.Callback { - private static final int MSG_LAUNCHER_PROVIDER_CHANGED = 1; private static final int MSG_APP_WIDGET_HOST_RESET = 2; private LauncherProviderChangeListener mListener; @@ -1053,9 +1038,6 @@ public class LauncherProvider extends ContentProvider { public boolean handleMessage(Message msg) { if (mListener != null) { switch (msg.what) { - case MSG_LAUNCHER_PROVIDER_CHANGED: - mListener.onLauncherProviderChanged(); - break; case MSG_APP_WIDGET_HOST_RESET: mListener.onAppWidgetHostReset(); break; diff --git a/src/com/android/launcher3/LauncherProviderChangeListener.java b/src/com/android/launcher3/LauncherProviderChangeListener.java index 024308863a..6afe88526d 100644 --- a/src/com/android/launcher3/LauncherProviderChangeListener.java +++ b/src/com/android/launcher3/LauncherProviderChangeListener.java @@ -7,7 +7,5 @@ package com.android.launcher3; */ public interface LauncherProviderChangeListener { - void onLauncherProviderChanged(); - void onAppWidgetHostReset(); } diff --git a/src/com/android/launcher3/LauncherSettings.java b/src/com/android/launcher3/LauncherSettings.java index e248ba016d..c509680981 100644 --- a/src/com/android/launcher3/LauncherSettings.java +++ b/src/com/android/launcher3/LauncherSettings.java @@ -58,6 +58,10 @@ public class LauncherSettings { */ public static final String ITEM_TYPE = "itemType"; + /** + * The gesture is a package + */ + public static final int ITEM_TYPE_NON_ACTIONABLE = -1; /** * The gesture is an application */ @@ -122,11 +126,13 @@ public class LauncherSettings { */ public static final int CONTAINER_DESKTOP = -100; public static final int CONTAINER_HOTSEAT = -101; + public static final int CONTAINER_PREDICTION = -102; static final String containerToString(int container) { switch (container) { case CONTAINER_DESKTOP: return "desktop"; case CONTAINER_HOTSEAT: return "hotseat"; + case CONTAINER_PREDICTION: return "prediction"; default: return String.valueOf(container); } } diff --git a/src/com/android/launcher3/LauncherStateManager.java b/src/com/android/launcher3/LauncherStateManager.java index 63914b0eb5..6bfae1311f 100644 --- a/src/com/android/launcher3/LauncherStateManager.java +++ b/src/com/android/launcher3/LauncherStateManager.java @@ -136,7 +136,7 @@ public class LauncherStateManager { } public void dump(String prefix, PrintWriter writer) { - writer.println(prefix + "LauncherState"); + writer.println(prefix + "LauncherState:"); writer.println(prefix + "\tmLastStableState:" + mLastStableState); writer.println(prefix + "\tmCurrentStableState:" + mCurrentStableState); writer.println(prefix + "\tmState:" + mState); @@ -418,7 +418,7 @@ public class LauncherStateManager { " @ " + Log.getStackTraceString(new Throwable())); } mState.onStateEnabled(mLauncher); - mLauncher.onStateSet(mState); + mLauncher.onStateSetStart(mState); if (state.disablePageClipping) { // Only disable clipping if needed, otherwise leave it as previous value. @@ -444,8 +444,7 @@ public class LauncherStateManager { } state.onStateTransitionEnd(mLauncher); - mLauncher.getWorkspace().setClipChildren(!state.disablePageClipping); - mLauncher.finishAutoCancelActionMode(); + mLauncher.onStateSetEnd(state); if (state == NORMAL) { setRestState(null); diff --git a/src/com/android/launcher3/MainThreadExecutor.java b/src/com/android/launcher3/MainThreadExecutor.java deleted file mode 100644 index 5094682333..0000000000 --- a/src/com/android/launcher3/MainThreadExecutor.java +++ /dev/null @@ -1,33 +0,0 @@ -/* - * Copyright (C) 2014 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; - -import android.os.Looper; - -import com.android.launcher3.util.LooperExecutor; - -/** - * An executor service that executes its tasks on the main thread. - * - * Shutting down this executor is not supported. - */ -public class MainThreadExecutor extends LooperExecutor { - - public MainThreadExecutor() { - super(Looper.getMainLooper()); - } -} diff --git a/src/com/android/launcher3/PagedView.java b/src/com/android/launcher3/PagedView.java index bbb3915b58..a99c7c28f9 100644 --- a/src/com/android/launcher3/PagedView.java +++ b/src/com/android/launcher3/PagedView.java @@ -890,23 +890,7 @@ public abstract class PagedView extends ViewGrou mTotalMotionX = 0; mActivePointerId = ev.getPointerId(0); - /* - * If being flinged and user touches the screen, initiate drag; - * otherwise don't. mScroller.isFinished should be false when - * being flinged. - */ - final int xDist = Math.abs(mScroller.getFinalPos() - mScroller.getCurrPos()); - final boolean finishedScrolling = (mScroller.isFinished() || xDist < mTouchSlop / 3); - - if (finishedScrolling) { - mIsBeingDragged = false; - if (!mScroller.isFinished() && !mFreeScroll) { - setCurrentPage(getNextPage()); - pageEndTransition(); - } - } else { - mIsBeingDragged = true; - } + updateIsBeingDraggedOnTouchDown(); break; } @@ -929,6 +913,25 @@ public abstract class PagedView extends ViewGrou return mIsBeingDragged; } + /** + * If being flinged and user touches the screen, initiate drag; otherwise don't. + */ + private void updateIsBeingDraggedOnTouchDown() { + // mScroller.isFinished should be false when being flinged. + final int xDist = Math.abs(mScroller.getFinalPos() - mScroller.getCurrPos()); + final boolean finishedScrolling = (mScroller.isFinished() || xDist < mTouchSlop / 3); + + if (finishedScrolling) { + mIsBeingDragged = false; + if (!mScroller.isFinished() && !mFreeScroll) { + setCurrentPage(getNextPage()); + pageEndTransition(); + } + } else { + mIsBeingDragged = true; + } + } + public boolean isHandlingTouch() { return mIsBeingDragged; } @@ -1104,6 +1107,8 @@ public abstract class PagedView extends ViewGrou switch (action & MotionEvent.ACTION_MASK) { case MotionEvent.ACTION_DOWN: + updateIsBeingDraggedOnTouchDown(); + /* * If being flinged and user touches, stop the fling. isFinished * will be false if being flinged. @@ -1289,6 +1294,10 @@ public abstract class PagedView extends ViewGrou if ((event.getSource() & InputDevice.SOURCE_CLASS_POINTER) != 0) { switch (event.getAction()) { case MotionEvent.ACTION_SCROLL: { + Launcher launcher = Launcher.getLauncher(getContext()); + if (launcher != null) { + AbstractFloatingView.closeAllOpenViews(launcher); + } // Handle mouse (or ext. device) by shifting the page depending on the scroll final float vscroll; final float hscroll; @@ -1299,6 +1308,9 @@ public abstract class PagedView extends ViewGrou vscroll = -event.getAxisValue(MotionEvent.AXIS_VSCROLL); hscroll = event.getAxisValue(MotionEvent.AXIS_HSCROLL); } + if (Math.abs(vscroll) > Math.abs(hscroll) && !isVerticalScrollable()) { + return true; + } if (hscroll != 0 || vscroll != 0) { boolean isForwardScroll = mIsRtl ? (hscroll < 0 || vscroll < 0) : (hscroll > 0 || vscroll > 0); @@ -1315,6 +1327,10 @@ public abstract class PagedView extends ViewGrou return super.onGenericMotionEvent(event); } + protected boolean isVerticalScrollable() { + return true; + } + private void acquireVelocityTrackerAndAddMovement(MotionEvent ev) { if (mVelocityTracker == null) { mVelocityTracker = VelocityTracker.obtain(); diff --git a/src/com/android/launcher3/Partner.java b/src/com/android/launcher3/Partner.java index 380078b267..af5402ae2c 100644 --- a/src/com/android/launcher3/Partner.java +++ b/src/com/android/launcher3/Partner.java @@ -16,6 +16,8 @@ package com.android.launcher3; +import static com.android.launcher3.util.PackageManagerHelper.findSystemApk; + import android.content.pm.PackageManager; import android.content.res.Resources; import android.util.DisplayMetrics; @@ -59,7 +61,7 @@ public class Partner { */ public static synchronized Partner get(PackageManager pm) { if (!sSearched) { - Pair apkInfo = Utilities.findSystemApk(ACTION_PARTNER_CUSTOMIZATION, pm); + Pair apkInfo = findSystemApk(ACTION_PARTNER_CUSTOMIZATION, pm); if (apkInfo != null) { sPartner = new Partner(apkInfo.first, apkInfo.second); } diff --git a/src/com/android/launcher3/SecondaryDropTarget.java b/src/com/android/launcher3/SecondaryDropTarget.java index 55cb6f2143..c8c590d9de 100644 --- a/src/com/android/launcher3/SecondaryDropTarget.java +++ b/src/com/android/launcher3/SecondaryDropTarget.java @@ -2,6 +2,7 @@ package com.android.launcher3; import static android.appwidget.AppWidgetManager.INVALID_APPWIDGET_ID; import static android.appwidget.AppWidgetProviderInfo.WIDGET_FEATURE_RECONFIGURABLE; + import static com.android.launcher3.ItemInfoWithIcon.FLAG_SYSTEM_MASK; import static com.android.launcher3.ItemInfoWithIcon.FLAG_SYSTEM_NO; import static com.android.launcher3.LauncherSettings.Favorites.CONTAINER_DESKTOP; @@ -29,6 +30,7 @@ import android.widget.Toast; import com.android.launcher3.Launcher.OnResumeCallback; import com.android.launcher3.compat.LauncherAppsCompat; import com.android.launcher3.dragndrop.DragOptions; +import com.android.launcher3.logging.FileLog; import com.android.launcher3.logging.LoggerUtils; import com.android.launcher3.userevent.nano.LauncherLogProto.ControlType; import com.android.launcher3.userevent.nano.LauncherLogProto.Target; @@ -240,6 +242,7 @@ public class SecondaryDropTarget extends ButtonDropTarget implements OnAlarmList .setData(Uri.fromParts("package", cn.getPackageName(), cn.getClassName())) .putExtra(Intent.EXTRA_USER, info.user); mLauncher.startActivity(i); + FileLog.d(TAG, "start uninstall activity " + cn.getPackageName()); return cn; } catch (URISyntaxException e) { Log.e(TAG, "Failed to parse intent to start uninstall activity for item=" + info); diff --git a/src/com/android/launcher3/SessionCommitReceiver.java b/src/com/android/launcher3/SessionCommitReceiver.java index b4078ee0ea..6853bf6941 100644 --- a/src/com/android/launcher3/SessionCommitReceiver.java +++ b/src/com/android/launcher3/SessionCommitReceiver.java @@ -22,7 +22,6 @@ import android.content.ComponentName; import android.content.Context; import android.content.Intent; import android.content.SharedPreferences; -import android.content.pm.ApplicationInfo; import android.content.pm.LauncherActivityInfo; import android.content.pm.PackageInstaller; import android.content.pm.PackageInstaller.SessionInfo; @@ -39,6 +38,7 @@ import android.text.TextUtils; import android.util.Log; import com.android.launcher3.compat.LauncherAppsCompat; +import com.android.launcher3.util.Executors; import com.android.launcher3.compat.PackageInstallerCompat; import java.util.List; @@ -133,7 +133,7 @@ public class SessionCommitReceiver extends BroadcastReceiver { // grid. prefs.edit().putBoolean(ADD_ICON_PREFERENCE_KEY, true).apply(); } else if (!prefs.contains(ADD_ICON_PREFERENCE_INITIALIZED_KEY)) { - new PrefInitTask(context).executeOnExecutor(Utilities.THREAD_POOL_EXECUTOR); + new PrefInitTask(context).executeOnExecutor(Executors.THREAD_POOL_EXECUTOR); } } diff --git a/src/com/android/launcher3/Utilities.java b/src/com/android/launcher3/Utilities.java index ba122f9441..6ddebe75d0 100644 --- a/src/com/android/launcher3/Utilities.java +++ b/src/com/android/launcher3/Utilities.java @@ -24,15 +24,9 @@ import android.app.ActivityManager; import android.app.Person; import android.app.WallpaperManager; import android.content.BroadcastReceiver; -import android.content.ComponentName; import android.content.Context; -import android.content.Intent; import android.content.SharedPreferences; -import android.content.pm.ApplicationInfo; import android.content.pm.LauncherActivityInfo; -import android.content.pm.PackageInfo; -import android.content.pm.PackageManager; -import android.content.pm.PackageManager.NameNotFoundException; import android.content.pm.ResolveInfo; import android.content.pm.ShortcutInfo; import android.content.res.Resources; @@ -47,7 +41,6 @@ import android.graphics.drawable.ColorDrawable; import android.graphics.drawable.Drawable; import android.graphics.drawable.InsetDrawable; import android.os.Build; -import android.os.Bundle; import android.os.DeadObjectException; import android.os.Handler; import android.os.Message; @@ -60,7 +53,6 @@ import android.text.TextUtils; import android.text.style.TtsSpan; import android.util.DisplayMetrics; import android.util.Log; -import android.util.Pair; import android.util.TypedValue; import android.view.MotionEvent; import android.view.View; @@ -69,7 +61,6 @@ import android.view.animation.Interpolator; import com.android.launcher3.compat.LauncherAppsCompat; import com.android.launcher3.compat.ShortcutConfigActivityInfo; -import com.android.launcher3.config.FeatureFlags; import com.android.launcher3.dragndrop.FolderAdaptiveIcon; import com.android.launcher3.graphics.RotationMode; import com.android.launcher3.graphics.TintedDrawableSpan; @@ -81,17 +72,10 @@ import com.android.launcher3.util.PackageManagerHelper; import com.android.launcher3.views.Transposable; import com.android.launcher3.widget.PendingAddShortcutInfo; -import java.io.Closeable; -import java.io.IOException; import java.lang.reflect.Method; import java.util.Arrays; import java.util.List; import java.util.Locale; -import java.util.StringTokenizer; -import java.util.concurrent.Executor; -import java.util.concurrent.LinkedBlockingQueue; -import java.util.concurrent.ThreadPoolExecutor; -import java.util.concurrent.TimeUnit; import java.util.regex.Matcher; import java.util.regex.Pattern; @@ -124,8 +108,6 @@ public final class Utilities { public static final boolean ATLEAST_OREO = Build.VERSION.SDK_INT >= Build.VERSION_CODES.O; - public static final int SINGLE_FRAME_MS = 16; - /** * Set on a motion event dispatched from the nav bar. See {@link MotionEvent#setEdgeFlags(int)}. */ @@ -148,18 +130,6 @@ public final class Utilities { public static final String EXTRA_WALLPAPER_OFFSET = "com.android.launcher3.WALLPAPER_OFFSET"; public static final String EXTRA_WALLPAPER_FLAVOR = "com.android.launcher3.WALLPAPER_FLAVOR"; - // These values are same as that in {@link AsyncTask}. - private static final int CPU_COUNT = Runtime.getRuntime().availableProcessors(); - private static final int CORE_POOL_SIZE = CPU_COUNT + 1; - private static final int MAXIMUM_POOL_SIZE = CPU_COUNT * 2 + 1; - private static final int KEEP_ALIVE = 1; - /** - * An {@link Executor} to be used with async task with no limit on the queue size. - */ - public static final Executor THREAD_POOL_EXECUTOR = new ThreadPoolExecutor( - CORE_POOL_SIZE, MAXIMUM_POOL_SIZE, KEEP_ALIVE, - TimeUnit.SECONDS, new LinkedBlockingQueue<>()); - public static boolean IS_RUNNING_IN_TEST_HARNESS = ActivityManager.isRunningInTestHarness(); @@ -247,7 +217,6 @@ public final class Utilities { return scale; } - /** * Inverse of {@link #getDescendantCoordRelativeToAncestor(View, View, float[], boolean)}. */ @@ -383,53 +352,6 @@ public final class Utilities { return min + (value * (max - min)); } - public static boolean isSystemApp(Context context, Intent intent) { - PackageManager pm = context.getPackageManager(); - ComponentName cn = intent.getComponent(); - String packageName = null; - if (cn == null) { - ResolveInfo info = pm.resolveActivity(intent, PackageManager.MATCH_DEFAULT_ONLY); - if ((info != null) && (info.activityInfo != null)) { - packageName = info.activityInfo.packageName; - } - } else { - packageName = cn.getPackageName(); - } - if (packageName != null) { - try { - PackageInfo info = pm.getPackageInfo(packageName, 0); - return (info != null) && (info.applicationInfo != null) && - ((info.applicationInfo.flags & ApplicationInfo.FLAG_SYSTEM) != 0); - } catch (NameNotFoundException e) { - return false; - } - } else { - return false; - } - } - - /* - * Finds a system apk which had a broadcast receiver listening to a particular action. - * @param action intent action used to find the apk - * @return a pair of apk package name and the resources. - */ - static Pair findSystemApk(String action, PackageManager pm) { - final Intent intent = new Intent(action); - for (ResolveInfo info : pm.queryBroadcastReceivers(intent, 0)) { - if (info.activityInfo != null && - (info.activityInfo.applicationInfo.flags & ApplicationInfo.FLAG_SYSTEM) != 0) { - final String packageName = info.activityInfo.packageName; - try { - final Resources res = pm.getResourcesForApplication(packageName); - return Pair.create(packageName, res); - } catch (NameNotFoundException e) { - Log.w(TAG, "Failed to find resources for " + packageName); - } - } - } - return null; - } - /** * Trims the string, removing all whitespace at the beginning and end of the string. * Non-breaking whitespaces are also removed. @@ -454,51 +376,10 @@ public final class Utilities { return (int) Math.ceil(fm.bottom - fm.top); } - /** - * Convenience println with multiple args. - */ - public static void println(String key, Object... args) { - StringBuilder b = new StringBuilder(); - b.append(key); - b.append(": "); - boolean isFirstArgument = true; - for (Object arg : args) { - if (isFirstArgument) { - isFirstArgument = false; - } else { - b.append(", "); - } - b.append(arg); - } - System.out.println(b.toString()); - } - public static boolean isRtl(Resources res) { return res.getConfiguration().getLayoutDirection() == View.LAYOUT_DIRECTION_RTL; } - /** - * Returns true if the intent is a valid launch intent for a launcher activity of an app. - * This is used to identify shortcuts which are different from the ones exposed by the - * applications' manifest file. - * - * @param launchIntent The intent that will be launched when the shortcut is clicked. - */ - public static boolean isLauncherAppTarget(Intent launchIntent) { - if (launchIntent != null - && Intent.ACTION_MAIN.equals(launchIntent.getAction()) - && launchIntent.getComponent() != null - && launchIntent.getCategories() != null - && launchIntent.getCategories().size() == 1 - && launchIntent.hasCategory(Intent.CATEGORY_LAUNCHER) - && TextUtils.isEmpty(launchIntent.getDataString())) { - // An app target can either have no extra or have ItemInfo.EXTRA_PROFILE. - Bundle extras = launchIntent.getExtras(); - return extras == null || extras.keySet().isEmpty(); - } - return false; - } - public static float dpiFromPx(int size, DisplayMetrics metrics){ float densityRatio = (float) metrics.densityDpi / DisplayMetrics.DENSITY_DEFAULT; return (size / densityRatio); @@ -601,18 +482,6 @@ public final class Utilities { return context.getSystemService(WallpaperManager.class).isSetWallpaperAllowed(); } - public static void closeSilently(Closeable c) { - if (c != null) { - try { - c.close(); - } catch (IOException e) { - if (FeatureFlags.IS_DOGFOOD_BUILD) { - Log.d(TAG, "Error closing", e); - } - } - } - } - public static boolean isBinderSizeError(Exception e) { return e.getCause() instanceof TransactionTooLargeException || e.getCause() instanceof DeadObjectException; @@ -727,25 +596,6 @@ public final class Utilities { } } - public static int[] getIntArrayFromString(String tokenized) { - StringTokenizer tokenizer = new StringTokenizer(tokenized, ","); - int[] array = new int[tokenizer.countTokens()]; - int count = 0; - while (tokenizer.hasMoreTokens()) { - array[count] = Integer.parseInt(tokenizer.nextToken().trim()); - count++; - } - return array; - } - - public static String getStringFromIntArray(int[] array) { - StringBuilder str = new StringBuilder(); - for (int value : array) { - str.append(value).append(","); - } - return str.toString(); - } - public static float squaredHypot(float x, float y) { return x * x + y * y; } diff --git a/src/com/android/launcher3/WidgetPreviewLoader.java b/src/com/android/launcher3/WidgetPreviewLoader.java index 6d1bc1a9c0..abf6cbd5ad 100644 --- a/src/com/android/launcher3/WidgetPreviewLoader.java +++ b/src/com/android/launcher3/WidgetPreviewLoader.java @@ -1,5 +1,8 @@ package com.android.launcher3; +import static com.android.launcher3.util.Executors.MAIN_EXECUTOR; +import static com.android.launcher3.util.Executors.MODEL_EXECUTOR; + import android.content.ComponentName; import android.content.ContentValues; import android.content.Context; @@ -23,21 +26,24 @@ import android.graphics.RectF; import android.graphics.drawable.Drawable; import android.os.AsyncTask; import android.os.CancellationSignal; -import android.os.Handler; import android.os.Process; import android.os.UserHandle; import android.util.Log; import android.util.LongSparseArray; +import android.util.Pair; + +import androidx.annotation.Nullable; import com.android.launcher3.compat.AppWidgetManagerCompat; import com.android.launcher3.compat.ShortcutConfigActivityInfo; import com.android.launcher3.compat.UserManagerCompat; import com.android.launcher3.icons.GraphicsUtils; +import com.android.launcher3.icons.IconCache; import com.android.launcher3.icons.LauncherIcons; import com.android.launcher3.icons.ShadowGenerator; -import com.android.launcher3.icons.IconCache; import com.android.launcher3.model.WidgetItem; import com.android.launcher3.util.ComponentKey; +import com.android.launcher3.util.Executors; import com.android.launcher3.util.PackageUserKey; import com.android.launcher3.util.Preconditions; import com.android.launcher3.util.SQLiteCacheHelper; @@ -50,11 +56,8 @@ import java.util.HashMap; import java.util.HashSet; import java.util.Set; import java.util.WeakHashMap; -import java.util.concurrent.Callable; import java.util.concurrent.ExecutionException; -import androidx.annotation.Nullable; - public class WidgetPreviewLoader { private static final String TAG = "WidgetPreviewLoader"; @@ -68,23 +71,18 @@ public class WidgetPreviewLoader { * Note: synchronized block used for this variable is expensive and the block should always * be posted to a background thread. */ - @Thunk final Set mUnusedBitmaps = - Collections.newSetFromMap(new WeakHashMap()); + @Thunk final Set mUnusedBitmaps = Collections.newSetFromMap(new WeakHashMap<>()); private final Context mContext; private final IconCache mIconCache; private final UserManagerCompat mUserManager; private final CacheDb mDb; - private final MainThreadExecutor mMainThreadExecutor = new MainThreadExecutor(); - @Thunk final Handler mWorkerHandler; - public WidgetPreviewLoader(Context context, IconCache iconCache) { mContext = context; mIconCache = iconCache; mUserManager = UserManagerCompat.getInstance(context); mDb = new CacheDb(context); - mWorkerHandler = new Handler(LauncherModel.getWorkerLooper()); } /** @@ -99,7 +97,7 @@ public class WidgetPreviewLoader { WidgetCacheKey key = new WidgetCacheKey(item.componentName, item.user, size); PreviewLoadTask task = new PreviewLoadTask(key, item, previewWidth, previewHeight, caller); - task.executeOnExecutor(Utilities.THREAD_POOL_EXECUTOR); + task.executeOnExecutor(Executors.THREAD_POOL_EXECUTOR); CancellationSignal signal = new CancellationSignal(); signal.setOnCancelListener(task); @@ -292,14 +290,25 @@ public class WidgetPreviewLoader { return null; } - private Bitmap generatePreview(BaseActivity launcher, WidgetItem item, Bitmap recycle, + /** + * Returns generatedPreview for a widget and if the preview should be saved in persistent + * storage. + * @param launcher + * @param item + * @param recycle + * @param previewWidth + * @param previewHeight + * @return Pair + */ + private Pair generatePreview(BaseActivity launcher, WidgetItem item, + Bitmap recycle, int previewWidth, int previewHeight) { if (item.widgetInfo != null) { return generateWidgetPreview(launcher, item.widgetInfo, previewWidth, recycle, null); } else { - return generateShortcutPreview(launcher, item.activityInfo, - previewWidth, previewHeight, recycle); + return new Pair<>(generateShortcutPreview(launcher, item.activityInfo, + previewWidth, previewHeight, recycle), false); } } @@ -312,9 +321,10 @@ public class WidgetPreviewLoader { * @param maxPreviewWidth width of the preview on either workspace or tray * @param preview bitmap that can be recycled * @param preScaledWidthOut return the width of the returned bitmap - * @return + * @return Pair */ - public Bitmap generateWidgetPreview(BaseActivity launcher, LauncherAppWidgetProviderInfo info, + public Pair generateWidgetPreview(BaseActivity launcher, + LauncherAppWidgetProviderInfo info, int maxPreviewWidth, Bitmap preview, int[] preScaledWidthOut) { // Load the preview image if possible if (maxPreviewWidth < 0) maxPreviewWidth = Integer.MAX_VALUE; @@ -344,6 +354,8 @@ public class WidgetPreviewLoader { int previewWidth; int previewHeight; + boolean savePreviewImage = widgetPreviewExists || info.previewImage == 0; + if (widgetPreviewExists && drawable.getIntrinsicWidth() > 0 && drawable.getIntrinsicHeight() > 0) { previewWidth = drawable.getIntrinsicWidth(); @@ -430,10 +442,12 @@ public class WidgetPreviewLoader { icon.setBounds(hoffset, yoffset, hoffset + iconSize, yoffset + iconSize); icon.draw(c); } - } catch (Resources.NotFoundException e) { } + } catch (Resources.NotFoundException e) { + savePreviewImage = false; + } c.setBitmap(null); } - return preview; + return new Pair<>(preview, savePreviewImage); } private RectF drawBoxWithShadow(Canvas c, int width, int height) { @@ -494,12 +508,7 @@ public class WidgetPreviewLoader { private Drawable mutateOnMainThread(final Drawable drawable) { try { - return mMainThreadExecutor.submit(new Callable() { - @Override - public Drawable call() throws Exception { - return drawable.mutate(); - } - }).get(); + return MAIN_EXECUTOR.submit(drawable::mutate).get(); } catch (InterruptedException e) { Thread.currentThread().interrupt(); throw new RuntimeException(e); @@ -541,6 +550,8 @@ public class WidgetPreviewLoader { @Thunk long[] mVersions; @Thunk Bitmap mBitmapToRecycle; + private boolean mSaveToDB = false; + PreviewLoadTask(WidgetCacheKey key, WidgetItem info, int previewWidth, int previewHeight, WidgetCell caller) { mKey = key; @@ -596,7 +607,10 @@ public class WidgetPreviewLoader { : null; // it's not in the db... we need to generate it - preview = generatePreview(mActivity, mInfo, unusedBitmap, mPreviewWidth, mPreviewHeight); + Pair pair = generatePreview(mActivity, mInfo, unusedBitmap, + mPreviewWidth, mPreviewHeight); + preview = pair.first; + this.mSaveToDB = pair.second; } return preview; } @@ -607,10 +621,10 @@ public class WidgetPreviewLoader { // Write the generated preview to the DB in the worker thread if (mVersions != null) { - mWorkerHandler.post(new Runnable() { + MODEL_EXECUTOR.post(new Runnable() { @Override public void run() { - if (!isCancelled()) { + if (!isCancelled() && mSaveToDB) { // If we are still using this preview, then write it to the DB and then // let the normal clear mechanism recycle the bitmap writeToDb(mKey, mVersions, preview); @@ -637,7 +651,7 @@ public class WidgetPreviewLoader { // recycled set immediately. Otherwise, it will be recycled after the preview is written // to disk. if (preview != null) { - mWorkerHandler.post(new Runnable() { + MODEL_EXECUTOR.post(new Runnable() { @Override public void run() { synchronized (mUnusedBitmaps) { @@ -658,7 +672,7 @@ public class WidgetPreviewLoader { // in the tasks's onCancelled() call, and if cancelled while the task is writing to // disk, it will be cancelled in the task's onPostExecute() call. if (mBitmapToRecycle != null) { - mWorkerHandler.post(new Runnable() { + MODEL_EXECUTOR.post(new Runnable() { @Override public void run() { synchronized (mUnusedBitmaps) { diff --git a/src/com/android/launcher3/Workspace.java b/src/com/android/launcher3/Workspace.java index 6612662ea5..98c67e2acc 100644 --- a/src/com/android/launcher3/Workspace.java +++ b/src/com/android/launcher3/Workspace.java @@ -60,16 +60,15 @@ import android.view.ViewTreeObserver; import android.view.accessibility.AccessibilityNodeInfo; import android.widget.Toast; -import com.android.launcher3.Launcher.LauncherOverlay; import com.android.launcher3.LauncherAppWidgetHost.ProviderChangedListener; import com.android.launcher3.LauncherStateManager.AnimationConfig; import com.android.launcher3.accessibility.AccessibleDragListenerAdapter; import com.android.launcher3.accessibility.WorkspaceAccessibilityHelper; import com.android.launcher3.anim.AnimatorSetBuilder; import com.android.launcher3.anim.Interpolators; -import com.android.launcher3.dot.FolderDotInfo; import com.android.launcher3.compat.AppWidgetManagerCompat; import com.android.launcher3.config.FeatureFlags; +import com.android.launcher3.dot.FolderDotInfo; import com.android.launcher3.dragndrop.DragController; import com.android.launcher3.dragndrop.DragLayer; import com.android.launcher3.dragndrop.DragOptions; @@ -90,8 +89,8 @@ import com.android.launcher3.touch.WorkspaceTouchListener; import com.android.launcher3.userevent.nano.LauncherLogProto.Action; import com.android.launcher3.userevent.nano.LauncherLogProto.ContainerType; import com.android.launcher3.userevent.nano.LauncherLogProto.Target; +import com.android.launcher3.util.Executors; import com.android.launcher3.util.IntArray; -import com.android.launcher3.util.IntSet; import com.android.launcher3.util.IntSparseArrayMap; import com.android.launcher3.util.ItemInfoMatcher; import com.android.launcher3.util.PackageUserKey; @@ -101,6 +100,7 @@ import com.android.launcher3.widget.LauncherAppWidgetHostView; import com.android.launcher3.widget.PendingAddShortcutInfo; import com.android.launcher3.widget.PendingAddWidgetInfo; import com.android.launcher3.widget.PendingAppWidgetHostView; +import com.android.systemui.plugins.shared.LauncherOverlayManager.LauncherOverlay; import java.util.ArrayList; import java.util.HashSet; @@ -133,9 +133,6 @@ public class Workspace extends PagedView private static final int DEFAULT_PAGE = 0; - public static final boolean MAP_NO_RECURSE = false; - public static final boolean MAP_RECURSE = true; - private LayoutTransition mLayoutTransition; @Thunk final WallpaperManager mWallpaperManager; @@ -968,6 +965,9 @@ public class Workspace extends PagedView onOverlayScrollChanged(0); } + public boolean hasOverlay() { + return mLauncherOverlay != null; + } private boolean isScrollingOverlay() { return mLauncherOverlay != null && @@ -1161,7 +1161,7 @@ public class Workspace extends PagedView } protected void setWallpaperDimension() { - Utilities.THREAD_POOL_EXECUTOR.execute(new Runnable() { + Executors.THREAD_POOL_EXECUTOR.execute(new Runnable() { @Override public void run() { final Point size = LauncherAppState.getIDP(getContext()).defaultWallpaperSize; @@ -2532,22 +2532,22 @@ public class Workspace extends PagedView View view; switch (info.itemType) { - case ITEM_TYPE_APPLICATION: - case LauncherSettings.Favorites.ITEM_TYPE_SHORTCUT: - case LauncherSettings.Favorites.ITEM_TYPE_DEEP_SHORTCUT: - if (info.container == NO_ID && info instanceof AppInfo) { - // Came from all apps -- make a copy - info = ((AppInfo) info).makeWorkspaceItem(); - d.dragInfo = info; - } - view = mLauncher.createShortcut(cellLayout, (WorkspaceItemInfo) info); - break; - case LauncherSettings.Favorites.ITEM_TYPE_FOLDER: - view = FolderIcon.fromXml(R.layout.folder_icon, mLauncher, cellLayout, - (FolderInfo) info); - break; - default: - throw new IllegalStateException("Unknown item type: " + info.itemType); + case ITEM_TYPE_APPLICATION: + case LauncherSettings.Favorites.ITEM_TYPE_SHORTCUT: + case LauncherSettings.Favorites.ITEM_TYPE_DEEP_SHORTCUT: + if (info instanceof AppInfo) { + // Came from all apps -- make a copy + info = ((AppInfo) info).makeWorkspaceItem(); + d.dragInfo = info; + } + view = mLauncher.createShortcut(cellLayout, (WorkspaceItemInfo) info); + break; + case LauncherSettings.Favorites.ITEM_TYPE_FOLDER: + view = FolderIcon.fromXml(R.layout.folder_icon, mLauncher, cellLayout, + (FolderInfo) info); + break; + default: + throw new IllegalStateException("Unknown item type: " + info.itemType); } // First we find the cell nearest to point at which the item is @@ -2804,11 +2804,28 @@ public class Workspace extends PagedView } } + /** + * Removed widget from workspace by appWidgetId + * @param appWidgetId + */ + public void removeWidget(int appWidgetId) { + mapOverItems((info, view) -> { + if (info instanceof LauncherAppWidgetInfo) { + LauncherAppWidgetInfo appWidgetInfo = (LauncherAppWidgetInfo) info; + if (appWidgetInfo.appWidgetId == appWidgetId) { + mLauncher.removeItem(view, appWidgetInfo, true); + return true; + } + } + return false; + }); + } + /** * Removes all folder listeners */ public void removeFolderListeners() { - mapOverItems(false, new ItemOperator() { + mapOverItems(new ItemOperator() { @Override public boolean evaluate(ItemInfo info, View view) { if (view instanceof FolderIcon) { @@ -2960,7 +2977,7 @@ public class Workspace extends PagedView public View getFirstMatch(final ItemOperator operator) { final View[] value = new View[1]; - mapOverItems(MAP_NO_RECURSE, new ItemOperator() { + mapOverItems(new ItemOperator() { @Override public boolean evaluate(ItemInfo info, View v) { if (operator.evaluate(info, v)) { @@ -2983,7 +3000,7 @@ public class Workspace extends PagedView final View[] matches = new View[operators.length]; // For efficiency, the outer loop should be CellLayout. for (CellLayout cellLayout : cellLayouts) { - mapOverCellLayout(MAP_NO_RECURSE, cellLayout, (info, v) -> { + mapOverCellLayout(cellLayout, (info, v) -> { for (int i = 0; i < operators.length; ++i) { if (matches[i] == null && operators[i].evaluate(info, v)) { matches[i] = v; @@ -3008,7 +3025,7 @@ public class Workspace extends PagedView } void clearDropTargets() { - mapOverItems(MAP_NO_RECURSE, new ItemOperator() { + mapOverItems(new ItemOperator() { @Override public boolean evaluate(ItemInfo info, View v) { if (v instanceof DropTarget) { @@ -3053,10 +3070,12 @@ public class Workspace extends PagedView } else if (itemToRemove.container >= 0) { // The item may belong to a folder. View parent = idToViewMap.get(itemToRemove.container); - if (parent != null) { + if (parent instanceof FolderIcon) { FolderInfo folderInfo = (FolderInfo) parent.getTag(); - folderInfo.prepareAutoUpdate(); folderInfo.remove((WorkspaceItemInfo) itemToRemove, false); + if (((FolderIcon) parent).getFolder().isOpen()) { + ((FolderIcon) parent).getFolder().close(false /* animate */); + } } } } @@ -3080,18 +3099,17 @@ public class Workspace extends PagedView /** * Map the operator over the shortcuts and widgets, return the first-non-null value. * - * @param recurse true: iterate over folder children. false: op get the folders themselves. * @param op the operator to map over the shortcuts */ - public void mapOverItems(boolean recurse, ItemOperator op) { + public void mapOverItems(ItemOperator op) { for (CellLayout layout : getWorkspaceAndHotseatCellLayouts()) { - if (mapOverCellLayout(recurse, layout, op)) { + if (mapOverCellLayout(layout, op)) { return; } } } - private boolean mapOverCellLayout(boolean recurse, CellLayout layout, ItemOperator op) { + private boolean mapOverCellLayout(CellLayout layout, ItemOperator op) { // TODO(b/128460496) Potential race condition where layout is not yet loaded if (layout == null) { return false; @@ -3101,103 +3119,68 @@ public class Workspace extends PagedView final int itemCount = container.getChildCount(); for (int itemIdx = 0; itemIdx < itemCount; itemIdx++) { View item = container.getChildAt(itemIdx); - ItemInfo info = (ItemInfo) item.getTag(); - if (recurse && info instanceof FolderInfo && item instanceof FolderIcon) { - FolderIcon folder = (FolderIcon) item; - ArrayList folderChildren = folder.getFolder().getItemsInReadingOrder(); - // map over all the children in the folder - final int childCount = folderChildren.size(); - for (int childIdx = 0; childIdx < childCount; childIdx++) { - View child = folderChildren.get(childIdx); - info = (ItemInfo) child.getTag(); - if (op.evaluate(info, child)) { - return true; - } - } - } else { - if (op.evaluate(info, item)) { - return true; - } + if (op.evaluate((ItemInfo) item.getTag(), item)) { + return true; } } return false; } void updateShortcuts(ArrayList shortcuts) { - int total = shortcuts.size(); - final HashSet updates = new HashSet<>(total); - final IntSet folderIds = new IntSet(); + final HashSet updates = new HashSet<>(shortcuts); + ItemOperator op = (info, v) -> { + if (v instanceof BubbleTextView && updates.contains(info)) { + WorkspaceItemInfo si = (WorkspaceItemInfo) info; + BubbleTextView shortcut = (BubbleTextView) v; + Drawable oldIcon = shortcut.getIcon(); + boolean oldPromiseState = (oldIcon instanceof PreloadIconDrawable) + && ((PreloadIconDrawable) oldIcon).hasNotCompleted(); + shortcut.applyFromWorkspaceItem(si, si.isPromise() != oldPromiseState); + } else if (info instanceof FolderInfo && v instanceof FolderIcon) { + ((FolderIcon) v).updatePreviewItems(updates::contains); + } - for (int i = 0; i < total; i++) { - WorkspaceItemInfo s = shortcuts.get(i); - updates.add(s); - folderIds.add(s.container); + // Iterate all items + return false; + }; + + mapOverItems(op); + Folder openFolder = Folder.getOpen(mLauncher); + if (openFolder != null) { + openFolder.iterateOverItems(op); } - - mapOverItems(MAP_RECURSE, new ItemOperator() { - @Override - public boolean evaluate(ItemInfo info, View v) { - if (info instanceof WorkspaceItemInfo && v instanceof BubbleTextView && - updates.contains(info)) { - WorkspaceItemInfo si = (WorkspaceItemInfo) info; - BubbleTextView shortcut = (BubbleTextView) v; - Drawable oldIcon = shortcut.getIcon(); - boolean oldPromiseState = (oldIcon instanceof PreloadIconDrawable) - && ((PreloadIconDrawable) oldIcon).hasNotCompleted(); - shortcut.applyFromWorkspaceItem(si, si.isPromise() != oldPromiseState); - } - // process all the shortcuts - return false; - } - }); - - // Update folder icons - mapOverItems(MAP_NO_RECURSE, new ItemOperator() { - @Override - public boolean evaluate(ItemInfo info, View v) { - if (info instanceof FolderInfo && folderIds.contains(info.id)) { - ((FolderInfo) info).itemsChanged(false); - } - // process all the shortcuts - return false; - } - }); } public void updateNotificationDots(Predicate updatedDots) { final PackageUserKey packageUserKey = new PackageUserKey(null, null); - final IntSet folderIds = new IntSet(); - mapOverItems(MAP_RECURSE, new ItemOperator() { - @Override - public boolean evaluate(ItemInfo info, View v) { - if (info instanceof WorkspaceItemInfo && v instanceof BubbleTextView) { - if (!packageUserKey.updateFromItemInfo(info) - || updatedDots.test(packageUserKey)) { - ((BubbleTextView) v).applyDotState(info, true /* animate */); - folderIds.add(info.container); - } - } - // process all the shortcuts - return false; - } - }); + Predicate matcher = info -> !packageUserKey.updateFromItemInfo(info) + || updatedDots.test(packageUserKey); - // Update folder icons - mapOverItems(MAP_NO_RECURSE, new ItemOperator() { - @Override - public boolean evaluate(ItemInfo info, View v) { - if (info instanceof FolderInfo && folderIds.contains(info.id) - && v instanceof FolderIcon) { + ItemOperator op = (info, v) -> { + if (info instanceof WorkspaceItemInfo && v instanceof BubbleTextView) { + if (matcher.test(info)) { + ((BubbleTextView) v).applyDotState(info, true /* animate */); + } + } else if (info instanceof FolderInfo && v instanceof FolderIcon) { + FolderInfo fi = (FolderInfo) info; + if (fi.contents.stream().anyMatch(matcher)) { FolderDotInfo folderDotInfo = new FolderDotInfo(); - for (WorkspaceItemInfo si : ((FolderInfo) info).contents) { + for (WorkspaceItemInfo si : fi.contents) { folderDotInfo.addDotInfo(mLauncher.getDotInfoForItem(si)); } ((FolderIcon) v).setDotInfo(folderDotInfo); } - // process all the shortcuts - return false; } - }); + + // process all the shortcuts + return false; + }; + + mapOverItems(op); + Folder folder = Folder.getOpen(mLauncher); + if (folder != null) { + folder.iterateOverItems(op); + } } public void removeAbandonedPromise(String packageName, UserHandle user) { @@ -3209,21 +3192,25 @@ public class Workspace extends PagedView } public void updateRestoreItems(final HashSet updates) { - mapOverItems(MAP_RECURSE, new ItemOperator() { - @Override - public boolean evaluate(ItemInfo info, View v) { - if (info instanceof WorkspaceItemInfo && v instanceof BubbleTextView - && updates.contains(info)) { - ((BubbleTextView) v).applyPromiseState(false /* promiseStateChanged */); - } else if (v instanceof PendingAppWidgetHostView - && info instanceof LauncherAppWidgetInfo - && updates.contains(info)) { - ((PendingAppWidgetHostView) v).applyState(); - } - // process all the shortcuts - return false; + ItemOperator op = (info, v) -> { + if (info instanceof WorkspaceItemInfo && v instanceof BubbleTextView + && updates.contains(info)) { + ((BubbleTextView) v).applyPromiseState(false /* promiseStateChanged */); + } else if (v instanceof PendingAppWidgetHostView + && info instanceof LauncherAppWidgetInfo + && updates.contains(info)) { + ((PendingAppWidgetHostView) v).applyState(); + } else if (v instanceof FolderIcon && info instanceof FolderInfo) { + ((FolderIcon) v).updatePreviewItems(updates::contains); } - }); + // process all the shortcuts + return false; + }; + mapOverItems(op); + Folder folder = Folder.getOpen(mLauncher); + if (folder != null) { + folder.iterateOverItems(op); + } } public void widgetsRestored(final ArrayList changedInfo) { @@ -3247,7 +3234,7 @@ public class Workspace extends PagedView } else { // widgetRefresh will automatically run when the packages are updated. // For now just update the progress bars - mapOverItems(MAP_NO_RECURSE, new ItemOperator() { + mapOverItems(new ItemOperator() { @Override public boolean evaluate(ItemInfo info, View view) { if (view instanceof PendingAppWidgetHostView @@ -3371,7 +3358,7 @@ public class Workspace extends PagedView mRefreshPending = false; ArrayList views = new ArrayList<>(mInfos.size()); - mapOverItems(MAP_NO_RECURSE, (info, view) -> { + mapOverItems((info, view) -> { if (view instanceof PendingAppWidgetHostView && mInfos.contains(info)) { views.add((PendingAppWidgetHostView) view); } diff --git a/src/com/android/launcher3/WorkspaceItemInfo.java b/src/com/android/launcher3/WorkspaceItemInfo.java index 050a8bef75..1323588e32 100644 --- a/src/com/android/launcher3/WorkspaceItemInfo.java +++ b/src/com/android/launcher3/WorkspaceItemInfo.java @@ -221,4 +221,9 @@ public class WorkspaceItemInfo extends ItemInfoWithIcon { } return cn; } + + @Override + public ItemInfoWithIcon clone() { + return new WorkspaceItemInfo(this); + } } diff --git a/src/com/android/launcher3/allapps/AllAppsContainerView.java b/src/com/android/launcher3/allapps/AllAppsContainerView.java index 293b86722d..d240a7256e 100644 --- a/src/com/android/launcher3/allapps/AllAppsContainerView.java +++ b/src/com/android/launcher3/allapps/AllAppsContainerView.java @@ -26,7 +26,6 @@ import android.os.Process; import android.text.Selection; import android.text.SpannableStringBuilder; import android.util.AttributeSet; -import android.util.Log; import android.view.KeyEvent; import android.view.LayoutInflater; import android.view.MotionEvent; @@ -45,11 +44,10 @@ import com.android.launcher3.ItemInfo; import com.android.launcher3.Launcher; import com.android.launcher3.LauncherState; import com.android.launcher3.R; -import com.android.launcher3.testing.TestProtocol; import com.android.launcher3.Utilities; import com.android.launcher3.compat.AccessibilityManagerCompat; -import com.android.launcher3.config.FeatureFlags; import com.android.launcher3.keyboard.FocusedItemDecorator; +import com.android.launcher3.testing.TestProtocol; import com.android.launcher3.userevent.nano.LauncherLogProto.ContainerType; import com.android.launcher3.userevent.nano.LauncherLogProto.Target; import com.android.launcher3.util.ItemInfoMatcher; @@ -163,16 +161,14 @@ public class AllAppsContainerView extends SpringRelativeLayout implements DragSo } private void onAppsUpdated() { - if (FeatureFlags.ALL_APPS_TABS_ENABLED) { - boolean hasWorkApps = false; - for (AppInfo app : mAllAppsStore.getApps()) { - if (mWorkMatcher.matches(app, null)) { - hasWorkApps = true; - break; - } + boolean hasWorkApps = false; + for (AppInfo app : mAllAppsStore.getApps()) { + if (mWorkMatcher.matches(app, null)) { + hasWorkApps = true; + break; } - rebindAdapters(hasWorkApps); } + rebindAdapters(hasWorkApps); } /** @@ -312,6 +308,7 @@ public class AllAppsContainerView extends SpringRelativeLayout implements DragSo + grid.cellLayoutPaddingLeftRightPx; for (int i = 0; i < mAH.length; i++) { + mAH[i].adapter.setAppsPerRow(grid.inv.numAllAppsColumns); mAH[i].padding.bottom = insets.bottom; mAH[i].padding.left = mAH[i].padding.right = leftRightPadding; mAH[i].applyPadding(); diff --git a/src/com/android/launcher3/allapps/AllAppsGridAdapter.java b/src/com/android/launcher3/allapps/AllAppsGridAdapter.java index 3cfa0b1ecb..bb212686e9 100644 --- a/src/com/android/launcher3/allapps/AllAppsGridAdapter.java +++ b/src/com/android/launcher3/allapps/AllAppsGridAdapter.java @@ -180,7 +180,7 @@ public class AllAppsGridAdapter extends RecyclerView.Adapter { public boolean hasOverlappingRendering() { return false; } + + @Override + protected boolean isVerticalScrollable() { + return false; + } } diff --git a/src/com/android/launcher3/allapps/AllAppsStore.java b/src/com/android/launcher3/allapps/AllAppsStore.java index ca8dbebfc2..c4b2f68c94 100644 --- a/src/com/android/launcher3/allapps/AllAppsStore.java +++ b/src/com/android/launcher3/allapps/AllAppsStore.java @@ -15,6 +15,9 @@ */ package com.android.launcher3.allapps; +import static com.android.launcher3.AppInfo.COMPONENT_KEY_COMPARATOR; +import static com.android.launcher3.AppInfo.EMPTY_ARRAY; + import android.view.View; import android.view.ViewGroup; @@ -26,8 +29,7 @@ import com.android.launcher3.util.ComponentKey; import com.android.launcher3.util.PackageUserKey; import java.util.ArrayList; -import java.util.Collection; -import java.util.HashMap; +import java.util.Arrays; import java.util.List; import java.util.function.Consumer; import java.util.function.Predicate; @@ -43,27 +45,33 @@ public class AllAppsStore { public static final int DEFER_UPDATES_TEST = 1 << 1; private PackageUserKey mTempKey = new PackageUserKey(null, null); - private final HashMap mComponentToAppMap = new HashMap<>(); + private AppInfo mTempInfo = new AppInfo(); + + private AppInfo[] mApps = EMPTY_ARRAY; + private final List mUpdateListeners = new ArrayList<>(); private final ArrayList mIconContainers = new ArrayList<>(); private int mDeferUpdatesFlags = 0; private boolean mUpdatePending = false; - public Collection getApps() { - return mComponentToAppMap.values(); + public AppInfo[] getApps() { + return mApps; } /** * Sets the current set of apps. */ - public void setApps(List apps) { - mComponentToAppMap.clear(); - addOrUpdateApps(apps); + public void setApps(AppInfo[] apps) { + mApps = apps; + notifyUpdate(); } public AppInfo getApp(ComponentKey key) { - return mComponentToAppMap.get(key); + mTempInfo.componentName = key.componentName; + mTempInfo.user = key.user; + int index = Arrays.binarySearch(mApps, mTempInfo, COMPONENT_KEY_COMPARATOR); + return index < 0 ? null : mApps[index]; } public void enableDeferUpdates(int flag) { @@ -86,27 +94,6 @@ public class AllAppsStore { return mDeferUpdatesFlags; } - /** - * Adds or updates existing apps in the list - */ - public void addOrUpdateApps(List apps) { - for (AppInfo app : apps) { - mComponentToAppMap.put(app.toComponentKey(), app); - } - notifyUpdate(); - } - - /** - * Removes some apps from the list. - */ - public void removeApps(List apps) { - for (AppInfo app : apps) { - mComponentToAppMap.remove(app.toComponentKey()); - } - notifyUpdate(); - } - - private void notifyUpdate() { if (mDeferUpdatesFlags != 0) { mUpdatePending = true; diff --git a/src/com/android/launcher3/allapps/AlphabeticalAppsList.java b/src/com/android/launcher3/allapps/AlphabeticalAppsList.java index 1369441fe1..0c4be62879 100644 --- a/src/com/android/launcher3/allapps/AlphabeticalAppsList.java +++ b/src/com/android/launcher3/allapps/AlphabeticalAppsList.java @@ -21,16 +21,13 @@ import android.content.pm.PackageManager; import com.android.launcher3.AppInfo; import com.android.launcher3.Launcher; import com.android.launcher3.Utilities; -import com.android.launcher3.compat.AlphabeticIndexCompat; import com.android.launcher3.shortcuts.DeepShortcutManager; -import com.android.launcher3.testing.TestProtocol; import com.android.launcher3.util.ComponentKey; import com.android.launcher3.util.ItemInfoMatcher; import com.android.launcher3.util.LabelComparator; import java.util.ArrayList; import java.util.Collections; -import java.util.HashMap; import java.util.List; import java.util.Locale; import java.util.Map; @@ -145,9 +142,7 @@ public class AlphabeticalAppsList implements AllAppsStore.OnUpdateListener { // The of ordered component names as a result of a search query private ArrayList mSearchResults; - private HashMap mCachedSectionNames = new HashMap<>(); private AllAppsGridAdapter mAdapter; - private AlphabeticIndexCompat mIndexer; private AppInfoComparator mAppNameComparator; private final int mNumAppsPerRow; private int mNumAppRowsInAdapter; @@ -156,7 +151,6 @@ public class AlphabeticalAppsList implements AllAppsStore.OnUpdateListener { public AlphabeticalAppsList(Context context, AllAppsStore appsStore, boolean isWork) { mAllAppsStore = appsStore; mLauncher = Launcher.getLauncher(context); - mIndexer = new AlphabeticIndexCompat(context); mAppNameComparator = new AppInfoComparator(context); mIsWork = isWork; mNumAppsPerRow = mLauncher.getDeviceProfile().inv.numColumns; @@ -263,7 +257,7 @@ public class AlphabeticalAppsList implements AllAppsStore.OnUpdateListener { TreeMap> sectionMap = new TreeMap<>(new LabelComparator()); for (AppInfo info : mApps) { // Add the section to the cache - String sectionName = getAndUpdateCachedSectionName(info.title); + String sectionName = info.sectionName; // Add it to the mapping ArrayList sectionApps = sectionMap.get(sectionName); @@ -279,12 +273,6 @@ public class AlphabeticalAppsList implements AllAppsStore.OnUpdateListener { for (Map.Entry> entry : sectionMap.entrySet()) { mApps.addAll(entry.getValue()); } - } else { - // Just compute the section headers for use below - for (AppInfo info : mApps) { - // Add the section to the cache - getAndUpdateCachedSectionName(info.title); - } } // Recompose the set of adapter items from the current set of apps @@ -320,7 +308,7 @@ public class AlphabeticalAppsList implements AllAppsStore.OnUpdateListener { // Recreate the filtered and sectioned apps (for convenience for the grid layout) from the // ordered set of sections for (AppInfo info : getFiltersAppInfos()) { - String sectionName = getAndUpdateCachedSectionName(info.title); + String sectionName = info.sectionName; // Create a new section if the section names do not match if (!sectionName.equals(lastSectionName)) { @@ -428,18 +416,4 @@ public class AlphabeticalAppsList implements AllAppsStore.OnUpdateListener { } return result; } - - /** - * Returns the cached section name for the given title, recomputing and updating the cache if - * the title has no cached section name. - */ - private String getAndUpdateCachedSectionName(CharSequence title) { - String sectionName = mCachedSectionNames.get(title); - if (sectionName == null) { - sectionName = mIndexer.computeSectionName(title); - mCachedSectionNames.put(title, sectionName); - } - return sectionName; - } - } diff --git a/src/com/android/launcher3/compat/AlphabeticIndexCompat.java b/src/com/android/launcher3/compat/AlphabeticIndexCompat.java index dfdcc7089f..46c9006ddf 100644 --- a/src/com/android/launcher3/compat/AlphabeticIndexCompat.java +++ b/src/com/android/launcher3/compat/AlphabeticIndexCompat.java @@ -3,7 +3,6 @@ package com.android.launcher3.compat; import android.content.Context; import android.icu.text.AlphabeticIndex; import android.os.LocaleList; -import android.util.Log; import com.android.launcher3.Utilities; @@ -12,28 +11,32 @@ import java.util.Locale; import androidx.annotation.NonNull; public class AlphabeticIndexCompat { - private static final String TAG = "AlphabeticIndexCompat"; private static final String MID_DOT = "\u2219"; - private final BaseIndex mBaseIndex; private final String mDefaultMiscLabel; + private final AlphabeticIndex.ImmutableIndex mBaseIndex; + public AlphabeticIndexCompat(Context context) { - BaseIndex index = null; + this(context.getResources().getConfiguration().getLocales()); + } - try { - index = new AlphabeticIndexVN(context); - } catch (Exception e) { - Log.d(TAG, "Unable to load the system index", e); + public AlphabeticIndexCompat(LocaleList locales) { + int localeCount = locales.size(); + + Locale primaryLocale = localeCount == 0 ? Locale.ENGLISH : locales.get(0); + AlphabeticIndex indexBuilder = new AlphabeticIndex(primaryLocale); + for (int i = 1; i < localeCount; i++) { + indexBuilder.addLabels(locales.get(i)); } + indexBuilder.addLabels(Locale.ENGLISH); + mBaseIndex = indexBuilder.buildImmutableIndex(); - mBaseIndex = index == null ? new BaseIndex() : index; - - if (context.getResources().getConfiguration().locale - .getLanguage().equals(Locale.JAPANESE.getLanguage())) { + if (primaryLocale.getLanguage().equals(Locale.JAPANESE.getLanguage())) { // Japanese character 他 ("misc") mDefaultMiscLabel = "\u4ed6"; - // TODO(winsonc, omakoto): We need to handle Japanese sections better, especially the kanji + // TODO(winsonc, omakoto): We need to handle Japanese sections better, + // especially the kanji } else { // Dot mDefaultMiscLabel = MID_DOT; @@ -45,7 +48,7 @@ public class AlphabeticIndexCompat { */ public String computeSectionName(@NonNull CharSequence cs) { String s = Utilities.trim(cs); - String sectionName = mBaseIndex.getBucketLabel(mBaseIndex.getBucketIndex(s)); + String sectionName = mBaseIndex.getBucket(mBaseIndex.getBucketIndex(s)).getLabel(); if (Utilities.trim(sectionName).isEmpty() && s.length() > 0) { int c = s.codePointAt(0); boolean startsWithDigit = Character.isDigit(c); @@ -66,71 +69,4 @@ public class AlphabeticIndexCompat { } return sectionName; } - - /** - * Base class to support Alphabetic indexing if not supported by the framework. - * TODO(winsonc): disable for non-english locales - */ - private static class BaseIndex { - - private static final String BUCKETS = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ-"; - private static final int UNKNOWN_BUCKET_INDEX = BUCKETS.length() - 1; - - /** - * Returns the index of the bucket in which the given string should appear. - */ - protected int getBucketIndex(@NonNull String s) { - if (s.isEmpty()) { - return UNKNOWN_BUCKET_INDEX; - } - int index = BUCKETS.indexOf(s.substring(0, 1).toUpperCase()); - if (index != -1) { - return index; - } - return UNKNOWN_BUCKET_INDEX; - } - - /** - * Returns the label for the bucket at the given index (as returned by getBucketIndex). - */ - protected String getBucketLabel(int index) { - return BUCKETS.substring(index, index + 1); - } - } - - /** - * Implementation based on {@link AlphabeticIndex}. - */ - private static class AlphabeticIndexVN extends BaseIndex { - - private final AlphabeticIndex.ImmutableIndex mAlphabeticIndex; - - public AlphabeticIndexVN(Context context) { - LocaleList locales = context.getResources().getConfiguration().getLocales(); - int localeCount = locales.size(); - - Locale primaryLocale = localeCount == 0 ? Locale.ENGLISH : locales.get(0); - AlphabeticIndex indexBuilder = new AlphabeticIndex(primaryLocale); - for (int i = 1; i < localeCount; i++) { - indexBuilder.addLabels(locales.get(i)); - } - indexBuilder.addLabels(Locale.ENGLISH); - - mAlphabeticIndex = indexBuilder.buildImmutableIndex(); - } - - /** - * Returns the index of the bucket in which {@param s} should appear. - */ - protected int getBucketIndex(String s) { - return mAlphabeticIndex.getBucketIndex(s); - } - - /** - * Returns the label for the bucket at the given index - */ - protected String getBucketLabel(int index) { - return mAlphabeticIndex.getBucket(index).getLabel(); - } - } } diff --git a/src/com/android/launcher3/compat/AppWidgetManagerCompat.java b/src/com/android/launcher3/compat/AppWidgetManagerCompat.java index 32432561b0..fc5d11c23d 100644 --- a/src/com/android/launcher3/compat/AppWidgetManagerCompat.java +++ b/src/com/android/launcher3/compat/AppWidgetManagerCompat.java @@ -23,19 +23,18 @@ import android.content.Context; import android.os.Bundle; import android.os.UserHandle; +import androidx.annotation.Nullable; + import com.android.launcher3.LauncherAppWidgetInfo; import com.android.launcher3.LauncherAppWidgetProviderInfo; import com.android.launcher3.Utilities; -import com.android.launcher3.config.FeatureFlags; import com.android.launcher3.util.ComponentKey; import com.android.launcher3.util.PackageUserKey; -import com.android.launcher3.widget.custom.CustomWidgetParser; +import com.android.launcher3.widget.custom.CustomWidgetManager; import java.util.HashMap; import java.util.List; -import androidx.annotation.Nullable; - public abstract class AppWidgetManagerCompat { private static final Object sInstanceLock = new Object(); @@ -63,11 +62,9 @@ public abstract class AppWidgetManagerCompat { } public LauncherAppWidgetProviderInfo getLauncherAppWidgetInfo(int appWidgetId) { - if (FeatureFlags.ENABLE_CUSTOM_WIDGETS - && appWidgetId <= LauncherAppWidgetInfo.CUSTOM_WIDGET_ID) { - return CustomWidgetParser.getWidgetProvider(mContext, appWidgetId); + if (appWidgetId <= LauncherAppWidgetInfo.CUSTOM_WIDGET_ID) { + return CustomWidgetManager.INSTANCE.get(mContext).getWidgetProvider(appWidgetId); } - AppWidgetProviderInfo info = mAppWidgetManager.getAppWidgetInfo(appWidgetId); return info == null ? null : LauncherAppWidgetProviderInfo.fromProviderInfo(mContext, info); } diff --git a/src/com/android/launcher3/compat/AppWidgetManagerCompatVL.java b/src/com/android/launcher3/compat/AppWidgetManagerCompatVL.java index 1065748328..8f6500baf8 100644 --- a/src/com/android/launcher3/compat/AppWidgetManagerCompatVL.java +++ b/src/com/android/launcher3/compat/AppWidgetManagerCompatVL.java @@ -24,12 +24,15 @@ import android.os.Process; import android.os.UserHandle; import android.os.UserManager; +import androidx.annotation.Nullable; + import com.android.launcher3.LauncherAppWidgetInfo; import com.android.launcher3.LauncherAppWidgetProviderInfo; -import com.android.launcher3.config.FeatureFlags; +import com.android.launcher3.model.WidgetsModel; import com.android.launcher3.util.ComponentKey; import com.android.launcher3.util.PackageUserKey; -import com.android.launcher3.widget.custom.CustomWidgetParser; +import com.android.launcher3.widget.custom.CustomAppWidgetProviderInfo; +import com.android.launcher3.widget.custom.CustomWidgetManager; import java.util.ArrayList; import java.util.Collections; @@ -37,8 +40,6 @@ import java.util.HashMap; import java.util.Iterator; import java.util.List; -import androidx.annotation.Nullable; - class AppWidgetManagerCompatVL extends AppWidgetManagerCompat { private final UserManager mUserManager; @@ -50,18 +51,15 @@ class AppWidgetManagerCompatVL extends AppWidgetManagerCompat { @Override public List getAllProviders(@Nullable PackageUserKey packageUser) { - if (FeatureFlags.GO_DISABLE_WIDGETS) { + if (WidgetsModel.GO_DISABLE_WIDGETS) { return Collections.emptyList(); } if (packageUser == null) { - ArrayList providers = new ArrayList(); + ArrayList providers = new ArrayList<>(); for (UserHandle user : mUserManager.getUserProfiles()) { providers.addAll(mAppWidgetManager.getInstalledProvidersForProfile(user)); } - - if (FeatureFlags.ENABLE_CUSTOM_WIDGETS) { - providers.addAll(CustomWidgetParser.getCustomWidgets(mContext)); - } + providers.addAll(getCustomWidgets()); return providers; } // Only get providers for the given package/user. @@ -74,9 +72,9 @@ class AppWidgetManagerCompatVL extends AppWidgetManagerCompat { } } - if (FeatureFlags.ENABLE_CUSTOM_WIDGETS && Process.myUserHandle().equals(packageUser.mUser) + if (Process.myUserHandle().equals(packageUser.mUser) && mContext.getPackageName().equals(packageUser.mPackageName)) { - providers.addAll(CustomWidgetParser.getCustomWidgets(mContext)); + providers.addAll(getCustomWidgets()); } return providers; } @@ -84,12 +82,10 @@ class AppWidgetManagerCompatVL extends AppWidgetManagerCompat { @Override public boolean bindAppWidgetIdIfAllowed(int appWidgetId, AppWidgetProviderInfo info, Bundle options) { - if (FeatureFlags.GO_DISABLE_WIDGETS) { + if (WidgetsModel.GO_DISABLE_WIDGETS) { return false; } - - if (FeatureFlags.ENABLE_CUSTOM_WIDGETS - && appWidgetId <= LauncherAppWidgetInfo.CUSTOM_WIDGET_ID) { + if (appWidgetId <= LauncherAppWidgetInfo.CUSTOM_WIDGET_ID) { return true; } return mAppWidgetManager.bindAppWidgetIdIfAllowed( @@ -98,7 +94,7 @@ class AppWidgetManagerCompatVL extends AppWidgetManagerCompat { @Override public LauncherAppWidgetProviderInfo findProvider(ComponentName provider, UserHandle user) { - if (FeatureFlags.GO_DISABLE_WIDGETS) { + if (WidgetsModel.GO_DISABLE_WIDGETS) { return null; } for (AppWidgetProviderInfo info : @@ -108,9 +104,8 @@ class AppWidgetManagerCompatVL extends AppWidgetManagerCompat { } } - if (FeatureFlags.ENABLE_CUSTOM_WIDGETS && Process.myUserHandle().equals(user)) { - for (LauncherAppWidgetProviderInfo info : - CustomWidgetParser.getCustomWidgets(mContext)) { + if (Process.myUserHandle().equals(user)) { + for (LauncherAppWidgetProviderInfo info : getCustomWidgets()) { if (info.provider.equals(provider)) { return info; } @@ -122,7 +117,7 @@ class AppWidgetManagerCompatVL extends AppWidgetManagerCompat { @Override public HashMap getAllProvidersMap() { HashMap result = new HashMap<>(); - if (FeatureFlags.GO_DISABLE_WIDGETS) { + if (WidgetsModel.GO_DISABLE_WIDGETS) { return result; } for (UserHandle user : mUserManager.getUserProfiles()) { @@ -131,13 +126,13 @@ class AppWidgetManagerCompatVL extends AppWidgetManagerCompat { result.put(new ComponentKey(info.provider, user), info); } } - - if (FeatureFlags.ENABLE_CUSTOM_WIDGETS) { - for (LauncherAppWidgetProviderInfo info : - CustomWidgetParser.getCustomWidgets(mContext)) { - result.put(new ComponentKey(info.provider, info.getProfile()), info); - } + for (LauncherAppWidgetProviderInfo info : getCustomWidgets()) { + result.put(new ComponentKey(info.provider, info.getProfile()), info); } return result; } + + List getCustomWidgets() { + return CustomWidgetManager.INSTANCE.get(mContext).getCustomWidgets(); + } } diff --git a/src/com/android/launcher3/compat/AppWidgetManagerCompatVO.java b/src/com/android/launcher3/compat/AppWidgetManagerCompatVO.java index b7b0563ba6..2814afc6c5 100644 --- a/src/com/android/launcher3/compat/AppWidgetManagerCompatVO.java +++ b/src/com/android/launcher3/compat/AppWidgetManagerCompatVO.java @@ -19,14 +19,14 @@ package com.android.launcher3.compat; import android.appwidget.AppWidgetProviderInfo; import android.content.Context; -import com.android.launcher3.config.FeatureFlags; +import androidx.annotation.Nullable; + +import com.android.launcher3.model.WidgetsModel; import com.android.launcher3.util.PackageUserKey; import java.util.Collections; import java.util.List; -import androidx.annotation.Nullable; - class AppWidgetManagerCompatVO extends AppWidgetManagerCompatVL { AppWidgetManagerCompatVO(Context context) { @@ -35,7 +35,7 @@ class AppWidgetManagerCompatVO extends AppWidgetManagerCompatVL { @Override public List getAllProviders(@Nullable PackageUserKey packageUser) { - if (FeatureFlags.GO_DISABLE_WIDGETS) { + if (WidgetsModel.GO_DISABLE_WIDGETS) { return Collections.emptyList(); } if (packageUser == null) { diff --git a/src/com/android/launcher3/compat/LauncherAppsCompat.java b/src/com/android/launcher3/compat/LauncherAppsCompat.java index 58fc73d235..cdb5c4d2b7 100644 --- a/src/com/android/launcher3/compat/LauncherAppsCompat.java +++ b/src/com/android/launcher3/compat/LauncherAppsCompat.java @@ -27,25 +27,27 @@ import android.graphics.Rect; import android.os.Bundle; import android.os.UserHandle; +import androidx.annotation.Nullable; + import com.android.launcher3.Utilities; import com.android.launcher3.util.PackageUserKey; import java.util.List; -import androidx.annotation.Nullable; - public abstract class LauncherAppsCompat { public interface OnAppsChangedCallbackCompat { - void onPackageRemoved(String packageName, UserHandle user); - void onPackageAdded(String packageName, UserHandle user); - void onPackageChanged(String packageName, UserHandle user); - void onPackagesAvailable(String[] packageNames, UserHandle user, boolean replacing); - void onPackagesUnavailable(String[] packageNames, UserHandle user, boolean replacing); - void onPackagesSuspended(String[] packageNames, UserHandle user); - void onPackagesUnsuspended(String[] packageNames, UserHandle user); - void onShortcutsChanged(String packageName, List shortcuts, - UserHandle user); + default void onPackageRemoved(String packageName, UserHandle user) { } + default void onPackageAdded(String packageName, UserHandle user) { } + default void onPackageChanged(String packageName, UserHandle user) { } + default void onPackagesAvailable(String[] packageNames, UserHandle user, + boolean replacing) { } + default void onPackagesUnavailable(String[] packageNames, UserHandle user, + boolean replacing) { } + default void onPackagesSuspended(String[] packageNames, UserHandle user) { } + default void onPackagesUnsuspended(String[] packageNames, UserHandle user) { } + default void onShortcutsChanged(String packageName, List shortcuts, + UserHandle user) { } } protected LauncherAppsCompat() { diff --git a/src/com/android/launcher3/compat/LauncherAppsCompatVO.java b/src/com/android/launcher3/compat/LauncherAppsCompatVO.java index 6e7a1bdcfe..5e13d00b74 100644 --- a/src/com/android/launcher3/compat/LauncherAppsCompatVO.java +++ b/src/com/android/launcher3/compat/LauncherAppsCompatVO.java @@ -16,6 +16,8 @@ package com.android.launcher3.compat; +import static com.android.launcher3.util.Executors.MODEL_EXECUTOR; + import android.annotation.TargetApi; import android.content.Context; import android.content.Intent; @@ -30,19 +32,17 @@ import android.os.Parcelable; import android.os.Process; import android.os.UserHandle; +import androidx.annotation.Nullable; + import com.android.launcher3.LauncherAppState; -import com.android.launcher3.LauncherModel; import com.android.launcher3.WorkspaceItemInfo; import com.android.launcher3.compat.ShortcutConfigActivityInfo.ShortcutConfigActivityInfoVO; import com.android.launcher3.icons.LauncherIcons; -import com.android.launcher3.util.LooperExecutor; import com.android.launcher3.util.PackageUserKey; import java.util.ArrayList; import java.util.List; -import androidx.annotation.Nullable; - @TargetApi(26) public class LauncherAppsCompatVO extends LauncherAppsCompatVL { @@ -120,7 +120,7 @@ public class LauncherAppsCompatVO extends LauncherAppsCompatVL { } } else { // Block the worker thread until the accept() is called. - new LooperExecutor(LauncherModel.getWorkerLooper()).execute(new Runnable() { + MODEL_EXECUTOR.execute(new Runnable() { @Override public void run() { try { diff --git a/src/com/android/launcher3/compat/PackageInstallerCompat.java b/src/com/android/launcher3/compat/PackageInstallerCompat.java index 11cb1f88d0..55df98b48f 100644 --- a/src/com/android/launcher3/compat/PackageInstallerCompat.java +++ b/src/com/android/launcher3/compat/PackageInstallerCompat.java @@ -19,6 +19,7 @@ package com.android.launcher3.compat; import android.content.ComponentName; import android.content.Context; import android.content.pm.PackageInstaller; +import android.content.pm.PackageInstaller.SessionInfo; import android.os.Process; import android.os.UserHandle; @@ -29,6 +30,7 @@ import java.util.List; import androidx.annotation.NonNull; import com.android.launcher3.Utilities; +import com.android.launcher3.util.PackageUserKey; public abstract class PackageInstallerCompat { @@ -52,19 +54,19 @@ public abstract class PackageInstallerCompat { } } - public static UserHandle getUserHandle(PackageInstaller.SessionInfo info) { + public static UserHandle getUserHandle(SessionInfo info) { return Utilities.ATLEAST_Q ? info.getUser() : Process.myUserHandle(); } /** * @return a map of active installs to their progress */ - public abstract HashMap updateAndGetActiveSessionCache(); + public abstract HashMap updateAndGetActiveSessionCache(); /** * @return an active SessionInfo for {@param pkg} or null if none exists. */ - public abstract PackageInstaller.SessionInfo getActiveSessionInfo(UserHandle user, String pkg); + public abstract SessionInfo getActiveSessionInfo(UserHandle user, String pkg); public abstract void onStop(); @@ -75,7 +77,7 @@ public abstract class PackageInstallerCompat { public final int progress; public final UserHandle user; - private PackageInstallInfo(@NonNull PackageInstaller.SessionInfo info) { + private PackageInstallInfo(@NonNull SessionInfo info) { this.state = STATUS_INSTALLING; this.packageName = info.getAppPackageName(); this.componentName = new ComponentName(packageName, ""); @@ -91,7 +93,7 @@ public abstract class PackageInstallerCompat { this.user = user; } - public static PackageInstallInfo fromInstallingState(PackageInstaller.SessionInfo info) { + public static PackageInstallInfo fromInstallingState(SessionInfo info) { return new PackageInstallInfo(info); } @@ -101,7 +103,7 @@ public abstract class PackageInstallerCompat { } - public abstract List getAllVerifiedSessions(); + public abstract List getAllVerifiedSessions(); /** * Returns true if a promise icon was already added to the home screen for {@param sessionId}. diff --git a/src/com/android/launcher3/compat/PackageInstallerCompatVL.java b/src/com/android/launcher3/compat/PackageInstallerCompatVL.java index ee9da73727..f421f7c016 100644 --- a/src/com/android/launcher3/compat/PackageInstallerCompatVL.java +++ b/src/com/android/launcher3/compat/PackageInstallerCompatVL.java @@ -16,23 +16,25 @@ package com.android.launcher3.compat; +import static com.android.launcher3.Utilities.getPrefs; +import static com.android.launcher3.util.Executors.MODEL_EXECUTOR; + import android.content.Context; import android.content.pm.ApplicationInfo; import android.content.pm.PackageInstaller; import android.content.pm.PackageInstaller.SessionCallback; import android.content.pm.PackageInstaller.SessionInfo; import android.content.pm.PackageManager; -import android.os.Handler; import android.os.UserHandle; import android.text.TextUtils; import android.util.SparseArray; -import com.android.launcher3.SessionCommitReceiver; -import com.android.launcher3.Utilities; -import com.android.launcher3.icons.IconCache; import com.android.launcher3.LauncherAppState; import com.android.launcher3.LauncherModel; +import com.android.launcher3.SessionCommitReceiver; +import com.android.launcher3.Utilities; import com.android.launcher3.config.FeatureFlags; +import com.android.launcher3.icons.IconCache; import com.android.launcher3.util.IntArray; import com.android.launcher3.util.IntSet; import com.android.launcher3.util.PackageUserKey; @@ -43,8 +45,6 @@ import java.util.HashMap; import java.util.Iterator; import java.util.List; -import static com.android.launcher3.Utilities.getPrefs; - public class PackageInstallerCompatVL extends PackageInstallerCompat { private static final boolean DEBUG = false; @@ -53,7 +53,6 @@ public class PackageInstallerCompatVL extends PackageInstallerCompat { @Thunk final PackageInstaller mInstaller; private final IconCache mCache; - private final Handler mWorker; private final Context mAppContext; private final HashMap mSessionVerifiedMap = new HashMap<>(); private final LauncherAppsCompat mLauncherApps; @@ -63,11 +62,10 @@ public class PackageInstallerCompatVL extends PackageInstallerCompat { mAppContext = context.getApplicationContext(); mInstaller = context.getPackageManager().getPackageInstaller(); mCache = LauncherAppState.getInstance(context).getIconCache(); - mWorker = new Handler(LauncherModel.getWorkerLooper()); - mInstaller.registerSessionCallback(mCallback, mWorker); + mInstaller.registerSessionCallback(mCallback, MODEL_EXECUTOR.getHandler()); mLauncherApps = LauncherAppsCompat.getInstance(context); - mPromiseIconIds = IntSet.wrap(IntArray.wrap(Utilities.getIntArrayFromString( - getPrefs(context).getString(PROMISE_ICON_IDS, "")))); + mPromiseIconIds = IntSet.wrap(IntArray.fromConcatString( + getPrefs(context).getString(PROMISE_ICON_IDS, ""))); cleanUpPromiseIconIds(); } @@ -90,12 +88,13 @@ public class PackageInstallerCompatVL extends PackageInstallerCompat { } @Override - public HashMap updateAndGetActiveSessionCache() { - HashMap activePackages = new HashMap<>(); + public HashMap updateAndGetActiveSessionCache() { + HashMap activePackages = new HashMap<>(); for (SessionInfo info : getAllVerifiedSessions()) { addSessionInfoToCache(info, getUserHandle(info)); if (info.getAppPackageName() != null) { - activePackages.put(info.getAppPackageName(), info); + activePackages.put(new PackageUserKey(info.getAppPackageName(), + getUserHandle(info)), info); mActiveSessions.put(info.getSessionId(), new PackageUserKey(info.getAppPackageName(), getUserHandle(info))); } @@ -165,7 +164,7 @@ public class PackageInstallerCompatVL extends PackageInstallerCompat { @Override public void onCreated(int sessionId) { SessionInfo sessionInfo = pushSessionDisplayToLauncher(sessionId); - if (FeatureFlags.LAUNCHER3_PROMISE_APPS_IN_ALL_APPS && sessionInfo != null) { + if (FeatureFlags.PROMISE_APPS_IN_ALL_APPS.get() && sessionInfo != null) { LauncherAppState app = LauncherAppState.getInstanceNoCreate(); if (app != null) { app.getModel().onInstallSessionCreated( diff --git a/src/com/android/launcher3/config/BaseFlags.java b/src/com/android/launcher3/config/FeatureFlags.java similarity index 84% rename from src/com/android/launcher3/config/BaseFlags.java rename to src/com/android/launcher3/config/FeatureFlags.java index 025087b9d8..e6eced1279 100644 --- a/src/com/android/launcher3/config/BaseFlags.java +++ b/src/com/android/launcher3/config/FeatureFlags.java @@ -23,11 +23,12 @@ import android.content.SharedPreferences; import androidx.annotation.GuardedBy; import androidx.annotation.Keep; - import androidx.annotation.VisibleForTesting; -import com.android.launcher3.Utilities; +import com.android.launcher3.BuildConfig; +import com.android.launcher3.Utilities; import com.android.launcher3.uioverrides.TogglableFlag; + import java.util.ArrayList; import java.util.List; import java.util.SortedMap; @@ -39,7 +40,7 @@ import java.util.TreeMap; *

All the flags should be defined here with appropriate default values. */ @Keep -public abstract class BaseFlags { +public final class FeatureFlags { private static final Object sLock = new Object(); @GuardedBy("sLock") @@ -47,49 +48,47 @@ public abstract class BaseFlags { static final String FLAGS_PREF_NAME = "featureFlags"; - BaseFlags() { - throw new UnsupportedOperationException("Don't instantiate BaseFlags"); - } + private FeatureFlags() { } public static boolean showFlagTogglerUi(Context context) { return Utilities.IS_DEBUG_DEVICE && Utilities.isDevelopersOptionsEnabled(context); } - public static final boolean IS_DOGFOOD_BUILD = false; + public static final boolean IS_DOGFOOD_BUILD = BuildConfig.DEBUG; + /** + * Enable moving the QSB on the 0th screen of the workspace. This is not a configuration feature + * and should be modified at a project level. + */ + public static final boolean QSB_ON_FIRST_SCREEN = true; + + + /** + * Feature flag to handle define config changes dynamically instead of killing the process. + * + * + * To add a new flag that can be toggled through the flags UI: + * + * 1. Declare a new ToggleableFlag below. Give it a unique key (e.g. "QSB_ON_FIRST_SCREEN"), + * and set a default value for the flag. This will be the default value on Debug builds. + * + * 2. Add your flag to mTogglableFlags. + * + * 3. Create a getter method (an 'is' method) for the flag by copying an existing one. + * + * 4. Create a getter method with the same name in the release flags copy of FeatureFlags.java. + * This should returns a constant (true/false). This will be the value of the flag used on + * release builds. + */ // When enabled the promise icon is visible in all apps while installation an app. - public static final boolean LAUNCHER3_PROMISE_APPS_IN_ALL_APPS = false; + public static final TogglableFlag PROMISE_APPS_IN_ALL_APPS = new TogglableFlag( + "PROMISE_APPS_IN_ALL_APPS", false, "Add promise icon in all-apps"); // When enabled a promise icon is added to the home screen when install session is active. public static final TogglableFlag PROMISE_APPS_NEW_INSTALLS = new TogglableFlag("PROMISE_APPS_NEW_INSTALLS", true, "Adds a promise icon to the home screen for new install sessions."); - // Enable moving the QSB on the 0th screen of the workspace - public static final boolean QSB_ON_FIRST_SCREEN = true; - - public static final TogglableFlag EXAMPLE_FLAG = new TogglableFlag("EXAMPLE_FLAG", true, - "An example flag that doesn't do anything. Useful for testing"); - - //Feature flag to enable pulling down navigation shade from workspace. - public static final boolean PULL_DOWN_STATUS_BAR = true; - - // When true, custom widgets are loaded using CustomWidgetParser. - public static final boolean ENABLE_CUSTOM_WIDGETS = false; - - // Features to control Launcher3Go behavior - public static final boolean GO_DISABLE_WIDGETS = false; - - // When enabled shows a work profile tab in all apps - public static final boolean ALL_APPS_TABS_ENABLED = true; - - // When true, overview shows screenshots in the orientation they were taken rather than - // 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", true, "Apply display changes dynamically"); @@ -115,6 +114,10 @@ public abstract class BaseFlags { "APP_SEARCH_IMPROVEMENTS", false, "Adds localized title and keyword search and ranking"); + public static final TogglableFlag ENABLE_PREDICTION_DISMISS = new TogglableFlag( + "ENABLE_PREDICTION_DISMISS", false, "Allow option to dimiss apps from predicted list"); + + public static void initialize(Context context) { // Avoid the disk read for user builds if (Utilities.IS_DEBUG_DEVICE) { @@ -129,13 +132,13 @@ public abstract class BaseFlags { static List getTogglableFlags() { // By Java Language Spec 12.4.2 // https://docs.oracle.com/javase/specs/jls/se7/html/jls-12.html#jls-12.4.2, the - // TogglableFlag instances on BaseFlags will be created before those on the FeatureFlags + // TogglableFlag instances on FeatureFlags will be created before those on the FeatureFlags // subclass. This code handles flags that are redeclared in FeatureFlags, ensuring the // FeatureFlags one takes priority. SortedMap flagsByKey = new TreeMap<>(); synchronized (sLock) { for (TogglableFlag flag : sFlags) { - flagsByKey.put(((BaseTogglableFlag) flag).getKey(), flag); + flagsByKey.put(flag.getKey(), flag); } } return new ArrayList<>(flagsByKey.values()); diff --git a/src/com/android/launcher3/config/FlagTogglerPrefUi.java b/src/com/android/launcher3/config/FlagTogglerPrefUi.java index 54e5322bd5..200938d27b 100644 --- a/src/com/android/launcher3/config/FlagTogglerPrefUi.java +++ b/src/com/android/launcher3/config/FlagTogglerPrefUi.java @@ -26,13 +26,13 @@ import android.view.MenuItem; import android.widget.Toast; import com.android.launcher3.R; +import com.android.launcher3.config.FeatureFlags.BaseTogglableFlag; +import com.android.launcher3.uioverrides.TogglableFlag; import androidx.preference.PreferenceDataStore; import androidx.preference.PreferenceFragment; import androidx.preference.PreferenceGroup; import androidx.preference.SwitchPreference; -import com.android.launcher3.config.BaseFlags.BaseTogglableFlag; -import com.android.launcher3.uioverrides.TogglableFlag; /** * Dev-build only UI allowing developers to toggle flag settings. See {@link FeatureFlags}. diff --git a/src/com/android/launcher3/dragndrop/AddItemActivity.java b/src/com/android/launcher3/dragndrop/AddItemActivity.java index a2dcbf87d7..9fb10905f8 100644 --- a/src/com/android/launcher3/dragndrop/AddItemActivity.java +++ b/src/com/android/launcher3/dragndrop/AddItemActivity.java @@ -20,6 +20,7 @@ import static com.android.launcher3.logging.LoggerUtils.newCommandAction; import static com.android.launcher3.logging.LoggerUtils.newContainerTarget; import static com.android.launcher3.logging.LoggerUtils.newItemTarget; import static com.android.launcher3.logging.LoggerUtils.newLauncherEvent; +import static com.android.launcher3.util.Executors.MODEL_EXECUTOR; import android.annotation.TargetApi; import android.app.ActivityOptions; @@ -47,7 +48,6 @@ import com.android.launcher3.InvariantDeviceProfile; import com.android.launcher3.LauncherAppState; import com.android.launcher3.LauncherAppWidgetHost; import com.android.launcher3.LauncherAppWidgetProviderInfo; -import com.android.launcher3.LauncherModel; import com.android.launcher3.R; import com.android.launcher3.compat.AppWidgetManagerCompat; import com.android.launcher3.compat.LauncherAppsCompatVO; @@ -55,7 +55,6 @@ import com.android.launcher3.model.WidgetItem; import com.android.launcher3.userevent.nano.LauncherLogProto.Action; import com.android.launcher3.userevent.nano.LauncherLogProto.ContainerType; import com.android.launcher3.util.InstantAppResolver; -import com.android.launcher3.util.LooperExecutor; import com.android.launcher3.views.BaseDragLayer; import com.android.launcher3.widget.PendingAddShortcutInfo; import com.android.launcher3.widget.PendingAddWidgetInfo; @@ -234,7 +233,7 @@ public class AddItemActivity extends BaseActivity implements OnLongClickListener mWidgetCell.applyFromCellItem(item, mApp.getWidgetCache()); mWidgetCell.ensurePreview(); } - }.executeOnExecutor(new LooperExecutor(LauncherModel.getWorkerLooper())); + }.executeOnExecutor(MODEL_EXECUTOR); // TODO: Create a worker looper executor and reuse that everywhere. } diff --git a/src/com/android/launcher3/dragndrop/DragView.java b/src/com/android/launcher3/dragndrop/DragView.java index 09c5e5b2b2..f66d07e3d8 100644 --- a/src/com/android/launcher3/dragndrop/DragView.java +++ b/src/com/android/launcher3/dragndrop/DragView.java @@ -17,6 +17,7 @@ package com.android.launcher3.dragndrop; import static com.android.launcher3.Utilities.getBadge; +import static com.android.launcher3.util.Executors.MODEL_EXECUTOR; import android.animation.Animator; import android.animation.AnimatorListenerAdapter; @@ -41,16 +42,19 @@ import android.os.Handler; import android.os.Looper; import android.view.View; +import androidx.dynamicanimation.animation.FloatPropertyCompat; +import androidx.dynamicanimation.animation.SpringAnimation; +import androidx.dynamicanimation.animation.SpringForce; + import com.android.launcher3.FastBitmapDrawable; +import com.android.launcher3.FirstFrameAnimatorHelper; import com.android.launcher3.ItemInfo; import com.android.launcher3.Launcher; -import com.android.launcher3.LauncherModel; import com.android.launcher3.LauncherSettings; import com.android.launcher3.LauncherState; import com.android.launcher3.LauncherStateManager; import com.android.launcher3.R; import com.android.launcher3.Utilities; -import com.android.launcher3.FirstFrameAnimatorHelper; import com.android.launcher3.anim.Interpolators; import com.android.launcher3.icons.LauncherIcons; import com.android.launcher3.util.Themes; @@ -58,10 +62,6 @@ import com.android.launcher3.util.Thunk; import java.util.Arrays; -import androidx.dynamicanimation.animation.FloatPropertyCompat; -import androidx.dynamicanimation.animation.SpringAnimation; -import androidx.dynamicanimation.animation.SpringForce; - public class DragView extends View implements LauncherStateManager.StateListener { private static final ColorMatrix sTempMatrix1 = new ColorMatrix(); private static final ColorMatrix sTempMatrix2 = new ColorMatrix(); @@ -210,7 +210,7 @@ public class DragView extends View implements LauncherStateManager.StateListener return; } // Load the adaptive icon on a background thread and add the view in ui thread. - new Handler(LauncherModel.getWorkerLooper()).postAtFrontOfQueue(new Runnable() { + MODEL_EXECUTOR.getHandler().postAtFrontOfQueue(new Runnable() { @Override public void run() { Object[] outObj = new Object[1]; diff --git a/src/com/android/launcher3/dragndrop/FlingToDeleteHelper.java b/src/com/android/launcher3/dragndrop/FlingToDeleteHelper.java index 589ad25e4f..06b5c409de 100644 --- a/src/com/android/launcher3/dragndrop/FlingToDeleteHelper.java +++ b/src/com/android/launcher3/dragndrop/FlingToDeleteHelper.java @@ -106,6 +106,7 @@ public class FlingToDeleteHelper { * @return the vector at which the item was flung, or null if no fling was detected. */ private PointF isFlingingToDelete() { + if (mVelocityTracker == null) return null; if (mDropTarget == null) { mDropTarget = (ButtonDropTarget) mLauncher.findViewById(R.id.delete_target_text); } diff --git a/src/com/android/launcher3/dragndrop/FolderAdaptiveIcon.java b/src/com/android/launcher3/dragndrop/FolderAdaptiveIcon.java index d8a1f99516..0bb3fbac52 100644 --- a/src/com/android/launcher3/dragndrop/FolderAdaptiveIcon.java +++ b/src/com/android/launcher3/dragndrop/FolderAdaptiveIcon.java @@ -16,6 +16,8 @@ package com.android.launcher3.dragndrop; +import static com.android.launcher3.util.Executors.MAIN_EXECUTOR; + import android.annotation.TargetApi; import android.graphics.Bitmap; import android.graphics.Canvas; @@ -31,7 +33,6 @@ import android.util.Log; import androidx.annotation.Nullable; import com.android.launcher3.Launcher; -import com.android.launcher3.MainThreadExecutor; import com.android.launcher3.R; import com.android.launcher3.folder.FolderIcon; import com.android.launcher3.folder.PreviewBackground; @@ -85,7 +86,7 @@ public class FolderAdaptiveIcon extends AdaptiveIconDrawable { // Create the actual drawable on the UI thread to avoid race conditions with // FolderIcon draw pass try { - return new MainThreadExecutor().submit(() -> { + return MAIN_EXECUTOR.submit(() -> { FolderIcon icon = launcher.findFolderIcon(folderId); return icon == null ? null : createDrawableOnUiThread(icon, badge, dragViewSize); }).get(); diff --git a/src/com/android/launcher3/folder/Folder.java b/src/com/android/launcher3/folder/Folder.java index f22b533380..65d593cdc3 100644 --- a/src/com/android/launcher3/folder/Folder.java +++ b/src/com/android/launcher3/folder/Folder.java @@ -26,12 +26,12 @@ import android.animation.AnimatorSet; import android.annotation.SuppressLint; import android.appwidget.AppWidgetHostView; import android.content.Context; -import android.content.res.Resources; import android.graphics.Canvas; import android.graphics.Path; import android.graphics.Rect; import android.text.InputType; import android.text.Selection; +import android.text.TextUtils; import android.util.AttributeSet; import android.util.Log; import android.util.Pair; @@ -126,9 +126,6 @@ public class Folder extends AbstractFloatingView implements ClipPathView, DragSo private static final Rect sTempRect = new Rect(); private static final int MIN_FOLDERS_FOR_HARDWARE_OPTIMIZATION = 10; - private static String sDefaultFolderName; - private static String sHintText; - private final Alarm mReorderAlarm = new Alarm(); private final Alarm mOnExitAlarm = new Alarm(); private final Alarm mOnScrollHintAlarm = new Alarm(); @@ -173,8 +170,6 @@ public class Folder extends AbstractFloatingView implements ClipPathView, DragSo private boolean mDeleteFolderOnDropCompleted = false; private boolean mSuppressFolderDeletion = false; private boolean mItemAddedBackToSelfViaIcon = false; - @Thunk float mFolderIconPivotX; - @Thunk float mFolderIconPivotY; private boolean mIsEditingName = false; @ViewDebug.ExportedProperty(category = "launcher") @@ -196,8 +191,6 @@ public class Folder extends AbstractFloatingView implements ClipPathView, DragSo super(context, attrs); setAlwaysDrawnWithCacheEnabled(false); - setLocaleDependentFields(getResources(), false /* force */); - mLauncher = Launcher.getLauncher(context); // We need this view to be focusable in touch mode so that when text editing of the folder // name is complete, we have something to focus on, thus hiding the cursor and giving @@ -315,10 +308,15 @@ public class Folder extends AbstractFloatingView implements ClipPathView, DragSo // Convert to a string here to ensure that no other state associated with the text field // gets saved. String newTitle = mFolderName.getText().toString(); - mInfo.setTitle(newTitle); + mInfo.title = newTitle; + mFolderIcon.onTitleChanged(newTitle); mLauncher.getModelWriter().updateItemInDatabase(mInfo); - mFolderName.setHint(sDefaultFolderName.contentEquals(newTitle) ? sHintText : null); + if (TextUtils.isEmpty(mInfo.title)) { + mFolderName.setHint(R.string.folder_hint_text); + } else { + mFolderName.setHint(null); + } sendCustomAccessibilityEvent( this, AccessibilityEvent.TYPE_WINDOW_STATE_CHANGED, @@ -385,7 +383,7 @@ public class Folder extends AbstractFloatingView implements ClipPathView, DragSo mInfo = info; ArrayList children = info.contents; Collections.sort(children, ITEM_POS_COMPARATOR); - mContent.bindItems(children); + updateItemLocationsInDatabaseBatch(); DragLayer.LayoutParams lp = (DragLayer.LayoutParams) getLayoutParams(); if (lp == null) { @@ -393,26 +391,21 @@ public class Folder extends AbstractFloatingView implements ClipPathView, DragSo lp.customPosition = true; setLayoutParams(lp); } - centerAboutIcon(); - mItemsInvalidated = true; - updateTextViewFocus(); mInfo.addListener(this); - if (!sDefaultFolderName.contentEquals(mInfo.title)) { + if (!TextUtils.isEmpty(mInfo.title)) { mFolderName.setText(mInfo.title); mFolderName.setHint(null); } else { mFolderName.setText(""); - mFolderName.setHint(sHintText); + mFolderName.setHint(R.string.folder_hint_text); } // In case any children didn't come across during loading, clean up the folder accordingly - mFolderIcon.post(new Runnable() { - public void run() { - if (getItemCount() <= 1) { - replaceFolderWithFinalItem(); - } + mFolderIcon.post(() -> { + if (getItemCount() <= 1) { + replaceFolderWithFinalItem(); } }); } @@ -472,18 +465,50 @@ public class Folder extends AbstractFloatingView implements ClipPathView, DragSo return folderCount >= MIN_FOLDERS_FOR_HARDWARE_OPTIMIZATION; } + /** + * Opens the folder as part of a drag operation + */ + public void beginExternalDrag() { + mIsExternalDrag = true; + mDragInProgress = true; + + // Since this folder opened by another controller, it might not get onDrop or + // onDropComplete. Perform cleanup once drag-n-drop ends. + mDragController.addDragListener(this); + + ArrayList items = new ArrayList<>(mInfo.contents); + mEmptyCellRank = items.size(); + items.add(null); // Add an empty spot at the end + + animateOpen(items, mEmptyCellRank / mContent.itemsPerPage()); + } + /** * Opens the user folder described by the specified tag. The opening of the folder * is animated relative to the specified View. If the View is null, no animation * is played. */ public void animateOpen() { + animateOpen(mInfo.contents, 0); + } + + /** + * Opens the user folder described by the specified tag. The opening of the folder + * is animated relative to the specified View. If the View is null, no animation + * is played. + */ + private void animateOpen(List items, int pageNo) { Folder openFolder = getOpen(mLauncher); if (openFolder != null && openFolder != this) { // Close any open folder before opening a folder. openFolder.close(true); } + mContent.bindItems(items); + centerAboutIcon(); + mItemsInvalidated = true; + updateTextViewFocus(); + mIsOpen = true; DragLayer dragLayer = mLauncher.getDragLayer(); @@ -500,10 +525,7 @@ public class Folder extends AbstractFloatingView implements ClipPathView, DragSo } mContent.completePendingPageChanges(); - if (!mDragInProgress) { - // Open on the first page. - mContent.snapToPageImmediately(0); - } + mContent.snapToPageImmediately(pageNo); // This is set to true in close(), but isn't reset to false until onDropCompleted(). This // leads to an inconsistent state if you drag out of the folder and drag back in without @@ -574,16 +596,6 @@ public class Folder extends AbstractFloatingView implements ClipPathView, DragSo mContent.verifyVisibleHighResIcons(mContent.getNextPage()); } - public void beginExternalDrag() { - mEmptyCellRank = mContent.allocateRankForNewItem(); - mIsExternalDrag = true; - mDragInProgress = true; - - // Since this folder opened by another controller, it might not get onDrop or - // onDropComplete. Perform cleanup once drag-n-drop ends. - mDragController.addDragListener(this); - } - @Override protected boolean isOfType(int type) { return (type & TYPE_FOLDER) != 0; @@ -668,6 +680,8 @@ public class Folder extends AbstractFloatingView implements ClipPathView, DragSo } else if (mDragInProgress) { mDeleteFolderOnDropCompleted = true; } + } else if (!mDragInProgress) { + mContent.unbindItems(); } mSuppressFolderDeletion = false; clearDragInfo(); @@ -822,9 +836,9 @@ public class Folder extends AbstractFloatingView implements ClipPathView, DragSo } } + @Override public void onDropCompleted(final View target, final DragObject d, final boolean success) { - if (success) { if (mDeleteFolderOnDropCompleted && !mItemAddedBackToSelfViaIcon && target != this) { replaceFolderWithFinalItem(); @@ -834,9 +848,9 @@ public class Folder extends AbstractFloatingView implements ClipPathView, DragSo WorkspaceItemInfo info = (WorkspaceItemInfo) d.dragInfo; View icon = (mCurrentDragView != null && mCurrentDragView.getTag() == info) ? mCurrentDragView : mContent.createNewView(info); - ArrayList views = getItemsInReadingOrder(); + ArrayList views = getIconsInReadingOrder(); views.add(info.rank, icon); - mContent.arrangeChildren(views, views.size()); + mContent.arrangeChildren(views); mItemsInvalidated = true; try (SuppressInfoChanges s = new SuppressInfoChanges()) { @@ -874,16 +888,21 @@ public class Folder extends AbstractFloatingView implements ClipPathView, DragSo } private void updateItemLocationsInDatabaseBatch() { - ArrayList list = getItemsInReadingOrder(); - ArrayList items = new ArrayList(); - for (int i = 0; i < list.size(); i++) { - View v = list.get(i); - ItemInfo info = (ItemInfo) v.getTag(); - info.rank = i; - items.add(info); + FolderGridOrganizer verifier = new FolderGridOrganizer( + mLauncher.getDeviceProfile().inv).setFolderInfo(mInfo); + + ArrayList items = new ArrayList<>(); + int total = mInfo.contents.size(); + for (int i = 0; i < total; i++) { + WorkspaceItemInfo itemInfo = mInfo.contents.get(i); + if (verifier.updateRankAndPos(itemInfo, i)) { + items.add(itemInfo); + } } - mLauncher.getModelWriter().moveItemsInDatabase(items, mInfo.id, 0); + if (!items.isEmpty()) { + mLauncher.getModelWriter().moveItemsInDatabase(items, mInfo.id, 0); + } } public void notifyDrop() { @@ -948,24 +967,12 @@ public class Folder extends AbstractFloatingView implements ClipPathView, DragSo setPivotX(folderPivotX); setPivotY(folderPivotY); - mFolderIconPivotX = (int) (mFolderIcon.getMeasuredWidth() * - (1.0f * folderPivotX / width)); - mFolderIconPivotY = (int) (mFolderIcon.getMeasuredHeight() * - (1.0f * folderPivotY / height)); - lp.width = width; lp.height = height; lp.x = left; lp.y = top; } - public float getPivotXForIconAnimation() { - return mFolderIconPivotX; - } - public float getPivotYForIconAnimation() { - return mFolderIconPivotY; - } - private int getContentAreaHeight() { DeviceProfile grid = mLauncher.getDeviceProfile(); int maxContentAreaHeight = grid.availableHeightPx @@ -1021,22 +1028,12 @@ public class Folder extends AbstractFloatingView implements ClipPathView, DragSo * Rearranges the children based on their rank. */ public void rearrangeChildren() { - rearrangeChildren(-1); - } - - /** - * Rearranges the children based on their rank. - * @param itemCount if greater than the total children count, empty spaces are left at the end, - * otherwise it is ignored. - */ - public void rearrangeChildren(int itemCount) { - ArrayList views = getItemsInReadingOrder(); - mContent.arrangeChildren(views, Math.max(itemCount, views.size())); + mContent.arrangeChildren(getIconsInReadingOrder()); mItemsInvalidated = true; } public int getItemCount() { - return mContent.getItemCount(); + return mInfo.contents.size(); } @Thunk void replaceFolderWithFinalItem() { @@ -1044,7 +1041,7 @@ public class Folder extends AbstractFloatingView implements ClipPathView, DragSo Runnable onCompleteRunnable = new Runnable() { @Override public void run() { - int itemCount = mInfo.contents.size(); + int itemCount = getItemCount(); if (itemCount <= 1) { View newIcon = null; @@ -1121,9 +1118,12 @@ public class Folder extends AbstractFloatingView implements ClipPathView, DragSo return false; } }); + } else { + setOnKeyListener(null); } } + @Override public void onDrop(DragObject d, DragOptions options) { // If the icon was dropped while the page was being scrolled, we need to compute // the target location again such that the icon is placed of the final page. @@ -1171,12 +1171,6 @@ public class Folder extends AbstractFloatingView implements ClipPathView, DragSo // before creating the view, so that WorkspaceItemInfo is updated appropriately. mLauncher.getModelWriter().addOrMoveItemInDatabase( si, mInfo.id, 0, si.cellX, si.cellY); - - // We only need to update the locations if it doesn't get handled in - // #onDropCompleted. - if (d.dragSource != this) { - updateItemLocationsInDatabaseBatch(); - } mIsExternalDrag = false; } else { currentDragView = mCurrentDragView; @@ -1203,7 +1197,13 @@ public class Folder extends AbstractFloatingView implements ClipPathView, DragSo // Temporarily suppress the listener, as we did all the work already here. try (SuppressInfoChanges s = new SuppressInfoChanges()) { - mInfo.add(si, false); + mInfo.add(si, mEmptyCellRank, false); + } + + // We only need to update the locations if it doesn't get handled in + // #onDropCompleted. + if (d.dragSource != this) { + updateItemLocationsInDatabaseBatch(); } } @@ -1226,22 +1226,29 @@ public class Folder extends AbstractFloatingView implements ClipPathView, DragSo // to correspond to the animation of the icon back into the folder. This is public void hideItem(WorkspaceItemInfo info) { View v = getViewForInfo(info); - v.setVisibility(INVISIBLE); + if (v != null) { + v.setVisibility(INVISIBLE); + } } public void showItem(WorkspaceItemInfo info) { View v = getViewForInfo(info); - v.setVisibility(VISIBLE); + if (v != null) { + v.setVisibility(VISIBLE); + } } @Override public void onAdd(WorkspaceItemInfo item, int rank) { - View view = mContent.createAndAddViewForRank(item, rank); + FolderGridOrganizer verifier = new FolderGridOrganizer( + mLauncher.getDeviceProfile().inv).setFolderInfo(mInfo); + verifier.updateRankAndPos(item, rank); mLauncher.getModelWriter().addOrMoveItemInDatabase(item, mInfo.id, 0, item.cellX, item.cellY); + updateItemLocationsInDatabaseBatch(); - ArrayList items = new ArrayList<>(getItemsInReadingOrder()); - items.add(rank, view); - mContent.arrangeChildren(items, items.size()); + if (mContent.areViewsBound()) { + mContent.createAndAddViewForRank(item, rank); + } mItemsInvalidated = true; } @@ -1264,13 +1271,7 @@ public class Folder extends AbstractFloatingView implements ClipPathView, DragSo } private View getViewForInfo(final WorkspaceItemInfo item) { - return mContent.iterateOverItems(new ItemOperator() { - - @Override - public boolean evaluate(ItemInfo info, View view) { - return info == item; - } - }); + return mContent.iterateOverItems((info, view) -> info == item); } @Override @@ -1278,32 +1279,27 @@ public class Folder extends AbstractFloatingView implements ClipPathView, DragSo updateTextViewFocus(); } - @Override - public void prepareAutoUpdate() { - close(false); + /** + * Utility methods to iterate over items of the view + */ + public void iterateOverItems(ItemOperator op) { + mContent.iterateOverItems(op); } - public void onTitleChanged(CharSequence title) { - } - - public ArrayList getItemsInReadingOrder() { + /** + * Returns the sorted list of all the icons in the folder + */ + public ArrayList getIconsInReadingOrder() { if (mItemsInvalidated) { mItemsInReadingOrder.clear(); - mContent.iterateOverItems(new ItemOperator() { - - @Override - public boolean evaluate(ItemInfo info, View view) { - mItemsInReadingOrder.add(view); - return false; - } - }); + mContent.iterateOverItems((i, v) -> !mItemsInReadingOrder.add(v)); mItemsInvalidated = false; } return mItemsInReadingOrder; } public List getItemsOnPage(int page) { - ArrayList allItems = getItemsInReadingOrder(); + ArrayList allItems = getIconsInReadingOrder(); int lastPage = mContent.getPageCount() - 1; int totalItemsInFolder = allItems.size(); int itemsPerPage = mContent.itemsPerPage(); @@ -1482,15 +1478,6 @@ public class Folder extends AbstractFloatingView implements ClipPathView, DragSo return false; } - public static void setLocaleDependentFields(Resources res, boolean force) { - if (sDefaultFolderName == null || force) { - sDefaultFolderName = res.getString(R.string.folder_name); - } - if (sHintText == null || force) { - sHintText = res.getString(R.string.folder_hint_text); - } - } - /** * Alternative to using {@link #getClipToOutline()} as it only works with derivatives of * rounded rect. diff --git a/src/com/android/launcher3/folder/FolderAnimationManager.java b/src/com/android/launcher3/folder/FolderAnimationManager.java index 962f215608..9eb06938a3 100644 --- a/src/com/android/launcher3/folder/FolderAnimationManager.java +++ b/src/com/android/launcher3/folder/FolderAnimationManager.java @@ -79,7 +79,7 @@ public class FolderAnimationManager { private final TimeInterpolator mLargeFolderPreviewItemCloseInterpolator; private final PreviewItemDrawingParams mTmpParams = new PreviewItemDrawingParams(0, 0, 0, 0); - + private final FolderGridOrganizer mPreviewVerifier; public FolderAnimationManager(Folder folder, boolean isOpening) { mFolder = folder; @@ -91,6 +91,7 @@ public class FolderAnimationManager { mContext = folder.getContext(); mLauncher = folder.mLauncher; + mPreviewVerifier = new FolderGridOrganizer(mLauncher.getDeviceProfile().inv); mIsOpening = isOpening; @@ -113,7 +114,7 @@ public class FolderAnimationManager { public AnimatorSet getAnimator() { final DragLayer.LayoutParams lp = (DragLayer.LayoutParams) mFolder.getLayoutParams(); ClippedFolderIconLayoutRule rule = mFolderIcon.getLayoutRule(); - final List itemsInPreview = mFolderIcon.getPreviewItems(); + final List itemsInPreview = getPreviewIconsOnPage(0); // Match position of the FolderIcon final Rect folderIconPos = new Rect(); @@ -225,6 +226,14 @@ public class FolderAnimationManager { return a; } + /** + * Returns the list of "preview items" on {@param page}. + */ + private List getPreviewIconsOnPage(int page) { + return mPreviewVerifier.setFolderInfo(mFolder.mInfo) + .previewItemsForPage(page, mFolder.getIconsInReadingOrder()); + } + /** * Animate the items on the current page. */ @@ -232,9 +241,8 @@ public class FolderAnimationManager { int previewItemOffsetX, int previewItemOffsetY) { ClippedFolderIconLayoutRule rule = mFolderIcon.getLayoutRule(); boolean isOnFirstPage = mFolder.mContent.getCurrentPage() == 0; - final List itemsInPreview = isOnFirstPage - ? mFolderIcon.getPreviewItems() - : mFolderIcon.getPreviewItemsOnPage(mFolder.mContent.getCurrentPage()); + final List itemsInPreview = getPreviewIconsOnPage( + isOnFirstPage ? 0 : mFolder.mContent.getCurrentPage()); final int numItemsInPreview = itemsInPreview.size(); final int numItemsInFirstPagePreview = isOnFirstPage ? numItemsInPreview : MAX_NUM_ITEMS_IN_PREVIEW; diff --git a/src/com/android/launcher3/folder/FolderGridOrganizer.java b/src/com/android/launcher3/folder/FolderGridOrganizer.java new file mode 100644 index 0000000000..9d14a5ffb3 --- /dev/null +++ b/src/com/android/launcher3/folder/FolderGridOrganizer.java @@ -0,0 +1,196 @@ +/* + * 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.folder; + +import static com.android.launcher3.folder.ClippedFolderIconLayoutRule.MAX_NUM_ITEMS_IN_PREVIEW; + +import android.graphics.Point; + +import com.android.launcher3.FolderInfo; +import com.android.launcher3.InvariantDeviceProfile; +import com.android.launcher3.ItemInfo; + +import java.util.ArrayList; +import java.util.List; + +/** + * Utility class for managing item positions in a folder based on rank + */ +public class FolderGridOrganizer { + + private final Point mPoint = new Point(); + private final int mMaxCountX; + private final int mMaxCountY; + private final int mMaxItemsPerPage; + + private int mNumItemsInFolder; + private int mCountX; + private int mCountY; + private boolean mDisplayingUpperLeftQuadrant = false; + + /** + * Note: must call {@link #setFolderInfo(FolderInfo)} manually for verifier to work. + */ + public FolderGridOrganizer(InvariantDeviceProfile profile) { + mMaxCountX = profile.numFolderColumns; + mMaxCountY = profile.numFolderRows; + mMaxItemsPerPage = mMaxCountX * mMaxCountY; + } + + /** + * Updates the organizer with the provided folder info + */ + public FolderGridOrganizer setFolderInfo(FolderInfo info) { + return setContentSize(info.contents.size()); + } + + /** + * Updates the organizer to reflect the content size + */ + public FolderGridOrganizer setContentSize(int contentSize) { + if (contentSize != mNumItemsInFolder) { + calculateGridSize(contentSize); + + mDisplayingUpperLeftQuadrant = contentSize > MAX_NUM_ITEMS_IN_PREVIEW; + mNumItemsInFolder = contentSize; + } + return this; + } + + public int getCountX() { + return mCountX; + } + + public int getCountY() { + return mCountY; + } + + public int getMaxItemsPerPage() { + return mMaxItemsPerPage; + } + + /** + * Calculates the grid size such that {@param count} items can fit in the grid. + * The grid size is calculated such that countY <= countX and countX = ceil(sqrt(count)) while + * maintaining the restrictions of {@link #mMaxCountX} & {@link #mMaxCountY}. + */ + private void calculateGridSize(int count) { + boolean done; + int gridCountX = mCountX; + int gridCountY = mCountY; + + if (count >= mMaxItemsPerPage) { + gridCountX = mMaxCountX; + gridCountY = mMaxCountY; + done = true; + } else { + done = false; + } + + while (!done) { + int oldCountX = gridCountX; + int oldCountY = gridCountY; + if (gridCountX * gridCountY < count) { + // Current grid is too small, expand it + if ((gridCountX <= gridCountY || gridCountY == mMaxCountY) + && gridCountX < mMaxCountX) { + gridCountX++; + } else if (gridCountY < mMaxCountY) { + gridCountY++; + } + if (gridCountY == 0) gridCountY++; + } else if ((gridCountY - 1) * gridCountX >= count && gridCountY >= gridCountX) { + gridCountY = Math.max(0, gridCountY - 1); + } else if ((gridCountX - 1) * gridCountY >= count) { + gridCountX = Math.max(0, gridCountX - 1); + } + done = gridCountX == oldCountX && gridCountY == oldCountY; + } + + mCountX = gridCountX; + mCountY = gridCountY; + } + + /** + * Updates the item's cellX, cellY and rank corresponding to the provided rank. + * @return true if there was any change + */ + public boolean updateRankAndPos(ItemInfo item, int rank) { + Point pos = getPosForRank(rank); + if (!pos.equals(item.cellX, item.cellY) || rank != item.rank) { + item.rank = rank; + item.cellX = pos.x; + item.cellY = pos.y; + return true; + } + return false; + } + + /** + * Returns the position of the item in the grid + */ + public Point getPosForRank(int rank) { + int pagePos = rank % mMaxItemsPerPage; + mPoint.x = pagePos % mCountX; + mPoint.y = pagePos / mCountX; + return mPoint; + } + + /** + * Returns the preview items for the provided pageNo using the full list of contents + */ + public ArrayList previewItemsForPage(int page, List contents) { + ArrayList result = new ArrayList<>(); + int itemsPerPage = mCountX * mCountY; + int start = itemsPerPage * page; + int end = Math.min(start + itemsPerPage, contents.size()); + + for (int i = start, rank = 0; i < end; i++, rank++) { + if (isItemInPreview(page, rank)) { + result.add((R) contents.get(i)); + } + + if (result.size() == MAX_NUM_ITEMS_IN_PREVIEW) { + break; + } + } + return result; + } + + /** + * Returns whether the item with rank is in the default Folder icon preview. + */ + public boolean isItemInPreview(int rank) { + return isItemInPreview(0, rank); + } + + /** + * @param page The page the item is on. + * @param rank The rank of the item. + * @return True iff the icon is in the 2x2 upper left quadrant of the Folder. + */ + public boolean isItemInPreview(int page, int rank) { + // First page items are laid out such that the first 4 items are always in the upper + // left quadrant. For all other pages, we need to check the row and col. + if (page > 0 || mDisplayingUpperLeftQuadrant) { + int col = rank % mCountX; + int row = rank / mCountX; + return col < 2 && row < 2; + } + return rank < MAX_NUM_ITEMS_IN_PREVIEW; + } +} diff --git a/src/com/android/launcher3/folder/FolderIcon.java b/src/com/android/launcher3/folder/FolderIcon.java index 0e2d4673e4..a463c7a30f 100644 --- a/src/com/android/launcher3/folder/FolderIcon.java +++ b/src/com/android/launcher3/folder/FolderIcon.java @@ -70,6 +70,7 @@ import com.android.launcher3.widget.PendingAddShortcutInfo; import java.util.ArrayList; import java.util.List; +import java.util.function.Predicate; /** * An icon that can appear on in the workspace representing an {@link Folder}. @@ -96,11 +97,11 @@ public class FolderIcon extends FrameLayout implements FolderListener, IconLabel PreviewBackground mBackground = new PreviewBackground(); private boolean mBackgroundIsVisible = true; - FolderIconPreviewVerifier mPreviewVerifier; + FolderGridOrganizer mPreviewVerifier; ClippedFolderIconLayoutRule mPreviewLayoutRule; private PreviewItemManager mPreviewItemManager; private PreviewItemDrawingParams mTmpParams = new PreviewItemDrawingParams(0, 0, 0, 0); - private List mCurrentPreviewItems = new ArrayList<>(); + private List mCurrentPreviewItems = new ArrayList<>(); boolean mAnimating = false; @@ -214,7 +215,7 @@ public class FolderIcon extends FrameLayout implements FolderListener, IconLabel private void setFolder(Folder folder) { mFolder = folder; - mPreviewVerifier = new FolderIconPreviewVerifier(mLauncher.getDeviceProfile().inv); + mPreviewVerifier = new FolderGridOrganizer(mLauncher.getDeviceProfile().inv); mPreviewVerifier.setFolderInfo(mFolder.getInfo()); updatePreviewItems(false); } @@ -232,11 +233,7 @@ public class FolderIcon extends FrameLayout implements FolderListener, IconLabel } public void addItem(WorkspaceItemInfo item) { - addItem(item, true); - } - - public void addItem(WorkspaceItemInfo item, boolean animate) { - mInfo.add(item, animate); + mInfo.add(item, true); } public void removeItem(WorkspaceItemInfo item, boolean animate) { @@ -261,7 +258,6 @@ public class FolderIcon extends FrameLayout implements FolderListener, IconLabel OnAlarmListener mOnOpenListener = new OnAlarmListener() { public void onAlarm(Alarm alarm) { mFolder.beginExternalDrag(); - mFolder.animateOpen(); } }; @@ -296,8 +292,7 @@ public class FolderIcon extends FrameLayout implements FolderListener, IconLabel } private void onDrop(final WorkspaceItemInfo item, DragView animateView, Rect finalRect, - float scaleRelativeToDragLayer, int index, - boolean itemReturnedOnFailedDrop) { + float scaleRelativeToDragLayer, int index, boolean itemReturnedOnFailedDrop) { item.cellX = -1; item.cellY = -1; @@ -328,18 +323,17 @@ public class FolderIcon extends FrameLayout implements FolderListener, IconLabel int numItemsInPreview = Math.min(MAX_NUM_ITEMS_IN_PREVIEW, index + 1); boolean itemAdded = false; if (itemReturnedOnFailedDrop || index >= MAX_NUM_ITEMS_IN_PREVIEW) { - List oldPreviewItems = new ArrayList<>(mCurrentPreviewItems); - addItem(item, false); + List oldPreviewItems = new ArrayList<>(mCurrentPreviewItems); + mInfo.add(item, index, false); mCurrentPreviewItems.clear(); - mCurrentPreviewItems.addAll(getPreviewItems()); + mCurrentPreviewItems.addAll(getPreviewItemsOnPage(0)); if (!oldPreviewItems.equals(mCurrentPreviewItems)) { - for (int i = 0; i < mCurrentPreviewItems.size(); ++i) { - if (mCurrentPreviewItems.get(i).getTag().equals(item)) { - // If the item dropped is going to be in the preview, we update the - // index here to reflect its position in the preview. - index = i; - } + int newIndex = mCurrentPreviewItems.indexOf(item); + if (newIndex >= 0) { + // If the item dropped is going to be in the preview, we update the + // index here to reflect its position in the preview. + index = newIndex; } mPreviewItemManager.hidePreviewItem(index, true); @@ -351,13 +345,13 @@ public class FolderIcon extends FrameLayout implements FolderListener, IconLabel } if (!itemAdded) { - addItem(item); + mInfo.add(item, index, true); } int[] center = new int[2]; float scale = getLocalCenterForIndex(index, numItemsInPreview, center); - center[0] = (int) Math.round(scaleRelativeToDragLayer * center[0]); - center[1] = (int) Math.round(scaleRelativeToDragLayer * center[1]); + center[0] = Math.round(scaleRelativeToDragLayer * center[0]); + center[1] = Math.round(scaleRelativeToDragLayer * center[1]); to.offset(center[0] - animateView.getMeasuredWidth() / 2, center[1] - animateView.getMeasuredHeight() / 2); @@ -398,7 +392,8 @@ public class FolderIcon extends FrameLayout implements FolderListener, IconLabel item = (WorkspaceItemInfo) d.dragInfo; } mFolder.notifyDrop(); - onDrop(item, d.dragView, null, 1.0f, mInfo.contents.size(), + onDrop(item, d.dragView, null, 1.0f, + itemReturnedOnFailedDrop ? item.rank : mInfo.contents.size(), itemReturnedOnFailedDrop); } @@ -510,8 +505,7 @@ public class FolderIcon extends FrameLayout implements FolderListener, IconLabel mBackground.drawBackground(canvas); } - if (mFolder == null) return; - if (mFolder.getItemCount() == 0 && !mAnimating) return; + if (mCurrentPreviewItems.isEmpty() && !mAnimating) return; final int saveCount = canvas.save(); canvas.clipPath(mBackground.getClipPath()); @@ -553,31 +547,10 @@ public class FolderIcon extends FrameLayout implements FolderListener, IconLabel } /** - * Returns the list of preview items displayed in the icon. + * Returns the list of items which should be visible in the preview */ - public List getPreviewItems() { - return getPreviewItemsOnPage(0); - } - - /** - * Returns the list of "preview items" on {@param page}. - */ - public List getPreviewItemsOnPage(int page) { - mPreviewVerifier.setFolderInfo(mFolder.getInfo()); - - List itemsToDisplay = new ArrayList<>(); - List itemsOnPage = mFolder.getItemsOnPage(page); - int numItems = itemsOnPage.size(); - for (int rank = 0; rank < numItems; ++rank) { - if (mPreviewVerifier.isItemInPreview(page, rank)) { - itemsToDisplay.add(itemsOnPage.get(rank)); - } - - if (itemsToDisplay.size() == MAX_NUM_ITEMS_IN_PREVIEW) { - break; - } - } - return itemsToDisplay; + public List getPreviewItemsOnPage(int page) { + return mPreviewVerifier.setFolderInfo(mInfo).previewItemsForPage(page, mInfo.contents); } @Override @@ -595,11 +568,14 @@ public class FolderIcon extends FrameLayout implements FolderListener, IconLabel private void updatePreviewItems(boolean animate) { mPreviewItemManager.updatePreviewItems(animate); mCurrentPreviewItems.clear(); - mCurrentPreviewItems.addAll(getPreviewItems()); + mCurrentPreviewItems.addAll(getPreviewItemsOnPage(0)); } - @Override - public void prepareAutoUpdate() { + /** + * Updates the preview items which match the provided condition + */ + public void updatePreviewItems(Predicate itemCheck) { + mPreviewItemManager.updatePreviewItems(itemCheck); } @Override @@ -622,7 +598,6 @@ public class FolderIcon extends FrameLayout implements FolderListener, IconLabel requestLayout(); } - @Override public void onTitleChanged(CharSequence title) { mFolderName.setText(title); setContentDescription(getContext().getString(R.string.folder_name_format, title)); diff --git a/src/com/android/launcher3/folder/FolderIconPreviewVerifier.java b/src/com/android/launcher3/folder/FolderIconPreviewVerifier.java deleted file mode 100644 index 4c84e354db..0000000000 --- a/src/com/android/launcher3/folder/FolderIconPreviewVerifier.java +++ /dev/null @@ -1,89 +0,0 @@ -/* - * Copyright (C) 2017 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.folder; - -import android.util.Log; - -import com.android.launcher3.FolderInfo; -import com.android.launcher3.InvariantDeviceProfile; - -import static com.android.launcher3.folder.ClippedFolderIconLayoutRule.MAX_NUM_ITEMS_IN_PREVIEW; - -/** - * Verifies whether an item in a Folder is displayed in the FolderIcon preview. - */ -public class FolderIconPreviewVerifier { - - private static final String TAG = "FolderPreviewVerifier"; - - private final int mMaxGridCountX; - private final int mMaxGridCountY; - private final int mMaxItemsPerPage; - private final int[] mGridSize = new int[] { 1, 1 }; - - private int mNumItemsInFolder; - private int mGridCountX; - private boolean mDisplayingUpperLeftQuadrant = false; - - /** - * Note: must call {@link #setFolderInfo(FolderInfo)} manually for verifier to work. - */ - public FolderIconPreviewVerifier(InvariantDeviceProfile profile) { - mMaxGridCountX = profile.numFolderColumns; - mMaxGridCountY = profile.numFolderRows; - mMaxItemsPerPage = mMaxGridCountX * mMaxGridCountY; - } - - public void setFolderInfo(FolderInfo info) { - int numItemsInFolder = info.contents.size(); - if (numItemsInFolder != mNumItemsInFolder) { - FolderPagedView.calculateGridSize(numItemsInFolder, 0, 0, mMaxGridCountX, - mMaxGridCountY, mMaxItemsPerPage, mGridSize); - mGridCountX = mGridSize[0]; - - mDisplayingUpperLeftQuadrant = numItemsInFolder > MAX_NUM_ITEMS_IN_PREVIEW; - mNumItemsInFolder = numItemsInFolder; - } - } - - /** - * Returns whether the item with {@param rank} is in the default Folder icon preview. - */ - public boolean isItemInPreview(int rank) { - return isItemInPreview(0, rank); - } - - /** - * @param page The page the item is on. - * @param rank The rank of the item. - * @return True iff the icon is in the 2x2 upper left quadrant of the Folder. - */ - public boolean isItemInPreview(int page, int rank) { - if (mGridSize[0] == 1) { - Log.w(TAG, "setFolderInfo not called before checking if item is in preview."); - } - - // First page items are laid out such that the first 4 items are always in the upper - // left quadrant. For all other pages, we need to check the row and col. - if (page > 0 || mDisplayingUpperLeftQuadrant) { - int col = rank % mGridCountX; - int row = rank / mGridCountX; - return col < 2 && row < 2; - } - return rank < MAX_NUM_ITEMS_IN_PREVIEW; - } -} diff --git a/src/com/android/launcher3/folder/FolderPagedView.java b/src/com/android/launcher3/folder/FolderPagedView.java index 57105e7ca3..54b363e909 100644 --- a/src/com/android/launcher3/folder/FolderPagedView.java +++ b/src/com/android/launcher3/folder/FolderPagedView.java @@ -24,10 +24,10 @@ import android.util.ArrayMap; import android.util.AttributeSet; import android.util.Log; import android.view.Gravity; -import android.view.LayoutInflater; import android.view.View; import android.view.ViewDebug; +import com.android.launcher3.BaseActivity; import com.android.launcher3.BubbleTextView; import com.android.launcher3.CellLayout; import com.android.launcher3.DeviceProfile; @@ -38,18 +38,22 @@ import com.android.launcher3.LauncherAppState; import com.android.launcher3.PagedView; import com.android.launcher3.R; import com.android.launcher3.ShortcutAndWidgetContainer; -import com.android.launcher3.WorkspaceItemInfo; import com.android.launcher3.Utilities; import com.android.launcher3.Workspace.ItemOperator; +import com.android.launcher3.WorkspaceItemInfo; import com.android.launcher3.anim.Interpolators; import com.android.launcher3.keyboard.ViewGroupFocusHelper; import com.android.launcher3.pageindicators.PageIndicatorDots; import com.android.launcher3.touch.ItemClickHandler; import com.android.launcher3.util.Thunk; +import com.android.launcher3.util.ViewCache; import java.util.ArrayList; import java.util.Iterator; +import java.util.List; import java.util.Map; +import java.util.function.ToIntFunction; +import java.util.stream.Collectors; public class FolderPagedView extends PagedView { @@ -68,17 +72,12 @@ public class FolderPagedView extends PagedView { public final boolean mIsRtl; - private final LayoutInflater mInflater; private final ViewGroupFocusHelper mFocusIndicatorHelper; @Thunk final ArrayMap mPendingAnimations = new ArrayMap<>(); - @ViewDebug.ExportedProperty(category = "launcher") - private final int mMaxCountX; - @ViewDebug.ExportedProperty(category = "launcher") - private final int mMaxCountY; - @ViewDebug.ExportedProperty(category = "launcher") - private final int mMaxItemsPerPage; + private final FolderGridOrganizer mOrganizer; + private final ViewCache mViewCache; private int mAllocatedContentSize; @ViewDebug.ExportedProperty(category = "launcher") @@ -88,20 +87,20 @@ public class FolderPagedView extends PagedView { private Folder mFolder; + // If the views are attached to the folder or not. A folder should be bound when its + // animating or is open. + private boolean mViewsBound = false; + public FolderPagedView(Context context, AttributeSet attrs) { super(context, attrs); InvariantDeviceProfile profile = LauncherAppState.getIDP(context); - mMaxCountX = profile.numFolderColumns; - mMaxCountY = profile.numFolderRows; - - mMaxItemsPerPage = mMaxCountX * mMaxCountY; - - mInflater = LayoutInflater.from(context); + mOrganizer = new FolderGridOrganizer(profile); mIsRtl = Utilities.isRtl(getResources()); setImportantForAccessibility(IMPORTANT_FOR_ACCESSIBILITY_YES); mFocusIndicatorHelper = new ViewGroupFocusHelper(this); + mViewCache = BaseActivity.fromContext(context).getViewCache(); } public void setFolder(Folder folder) { @@ -110,58 +109,14 @@ public class FolderPagedView extends PagedView { initParentViews(folder); } - /** - * Calculates the grid size such that {@param count} items can fit in the grid. - * The grid size is calculated such that countY <= countX and countX = ceil(sqrt(count)) while - * maintaining the restrictions of {@link #mMaxCountX} & {@link #mMaxCountY}. - */ - public static void calculateGridSize(int count, int countX, int countY, int maxCountX, - int maxCountY, int maxItemsPerPage, int[] out) { - boolean done; - int gridCountX = countX; - int gridCountY = countY; - - if (count >= maxItemsPerPage) { - gridCountX = maxCountX; - gridCountY = maxCountY; - done = true; - } else { - done = false; - } - - while (!done) { - int oldCountX = gridCountX; - int oldCountY = gridCountY; - if (gridCountX * gridCountY < count) { - // Current grid is too small, expand it - if ((gridCountX <= gridCountY || gridCountY == maxCountY) - && gridCountX < maxCountX) { - gridCountX++; - } else if (gridCountY < maxCountY) { - gridCountY++; - } - if (gridCountY == 0) gridCountY++; - } else if ((gridCountY - 1) * gridCountX >= count && gridCountY >= gridCountX) { - gridCountY = Math.max(0, gridCountY - 1); - } else if ((gridCountX - 1) * gridCountY >= count) { - gridCountX = Math.max(0, gridCountX - 1); - } - done = gridCountX == oldCountX && gridCountY == oldCountY; - } - - out[0] = gridCountX; - out[1] = gridCountY; - } - /** * Sets up the grid size such that {@param count} items can fit in the grid. */ - public void setupContentDimensions(int count) { + private void setupContentDimensions(int count) { mAllocatedContentSize = count; - calculateGridSize(count, mGridCountX, mGridCountY, mMaxCountX, mMaxCountY, mMaxItemsPerPage, - sTmpArray); - mGridCountX = sTmpArray[0]; - mGridCountY = sTmpArray[1]; + mOrganizer.setContentSize(count); + mGridCountX = mOrganizer.getCountX(); + mGridCountY = mOrganizer.getCountY(); // Update grid size for (int i = getPageCount() - 1; i >= 0; i--) { @@ -178,35 +133,50 @@ public class FolderPagedView extends PagedView { /** * Binds items to the layout. */ - public void bindItems(ArrayList items) { - ArrayList icons = new ArrayList<>(); - for (WorkspaceItemInfo item : items) { - icons.add(createNewView(item)); + public void bindItems(List items) { + if (mViewsBound) { + unbindItems(); } - arrangeChildren(icons, icons.size(), false); - } - - public void allocateSpaceForRank(int rank) { - ArrayList views = new ArrayList<>(mFolder.getItemsInReadingOrder()); - views.add(rank, null); - arrangeChildren(views, views.size(), false); + arrangeChildren(items.stream().map(this::createNewView).collect(Collectors.toList())); + mViewsBound = true; } /** - * Create space for a new item at the end, and returns the rank for that item. - * Also sets the current page to the last page. + * Removes all the icons from the folder */ - public int allocateRankForNewItem() { - int rank = getItemCount(); - allocateSpaceForRank(rank); - setCurrentPage(rank / mMaxItemsPerPage); - return rank; + public void unbindItems() { + for (int i = getChildCount() - 1; i >= 0; i--) { + CellLayout page = (CellLayout) getChildAt(i); + ShortcutAndWidgetContainer container = page.getShortcutsAndWidgets(); + for (int j = container.getChildCount() - 1; j >= 0; j--) { + mViewCache.recycleView(R.layout.folder_application, container.getChildAt(j)); + } + page.removeAllViews(); + mViewCache.recycleView(R.layout.folder_page, page); + } + removeAllViews(); + mViewsBound = false; } + /** + * Returns true if the icons are bound to the folder + */ + public boolean areViewsBound() { + return mViewsBound; + } + + /** + * Creates and adds an icon corresponding to the provided rank + * @return the created icon + */ public View createAndAddViewForRank(WorkspaceItemInfo item, int rank) { View icon = createNewView(item); - allocateSpaceForRank(rank); - addViewForRank(icon, item, rank); + if (!mViewsBound) { + return icon; + } + ArrayList views = new ArrayList<>(mFolder.getIconsInReadingOrder()); + views.add(rank, icon); + arrangeChildren(views); return icon; } @@ -215,31 +185,33 @@ public class FolderPagedView extends PagedView { * related attributes. It assumes that {@param item} is already attached to the view. */ public void addViewForRank(View view, WorkspaceItemInfo item, int rank) { - int pagePos = rank % mMaxItemsPerPage; - int pageNo = rank / mMaxItemsPerPage; - - item.rank = rank; - item.cellX = pagePos % mGridCountX; - item.cellY = pagePos / mGridCountX; + int pageNo = rank / mOrganizer.getMaxItemsPerPage(); CellLayout.LayoutParams lp = (CellLayout.LayoutParams) view.getLayoutParams(); - lp.cellX = item.cellX; - lp.cellY = item.cellY; + lp.setXY(mOrganizer.getPosForRank(rank)); getPageAt(pageNo).addViewToCellLayout(view, -1, item.getViewId(), lp, true); } @SuppressLint("InflateParams") public View createNewView(WorkspaceItemInfo item) { - final BubbleTextView textView = (BubbleTextView) mInflater.inflate( - R.layout.folder_application, null, false); + if (item == null) { + return null; + } + final BubbleTextView textView = mViewCache.getView( + R.layout.folder_application, getContext(), null); textView.applyFromWorkspaceItem(item); - textView.setHapticFeedbackEnabled(false); textView.setOnClickListener(ItemClickHandler.INSTANCE); textView.setOnLongClickListener(mFolder); textView.setOnFocusChangeListener(mFocusIndicatorHelper); - - textView.setLayoutParams(new CellLayout.LayoutParams( - item.cellX, item.cellY, item.spanX, item.spanY)); + CellLayout.LayoutParams lp = (CellLayout.LayoutParams) textView.getLayoutParams(); + if (lp == null) { + textView.setLayoutParams(new CellLayout.LayoutParams( + item.cellX, item.cellY, item.spanX, item.spanY)); + } else { + lp.cellX = item.cellX; + lp.cellY = item.cellY; + lp.cellHSpan = lp.cellVSpan = 1; + } return textView; } @@ -254,7 +226,7 @@ public class FolderPagedView extends PagedView { private CellLayout createAndAddNewPage() { DeviceProfile grid = Launcher.getLauncher(getContext()).getDeviceProfile(); - CellLayout page = (CellLayout) mInflater.inflate(R.layout.folder_page, this, false); + CellLayout page = mViewCache.getView(R.layout.folder_page, getContext(), this); page.setCellDimensions(grid.folderCellWidthPx, grid.folderCellHeightPx); page.getShortcutsAndWidgets().setMotionEventSplittingEnabled(false); page.setInvertIfRtl(true); @@ -295,16 +267,10 @@ public class FolderPagedView extends PagedView { * page. * * @param list the ordered list of children. - * @param itemCount if greater than the total children count, empty spaces are left - * at the end, otherwise it is ignored. - * */ - public void arrangeChildren(ArrayList list, int itemCount) { - arrangeChildren(list, itemCount, true); - } - @SuppressLint("RtlHardcoded") - private void arrangeChildren(ArrayList list, int itemCount, boolean saveChanges) { + public void arrangeChildren(List list) { + int itemCount = list.size(); ArrayList pages = new ArrayList<>(); for (int i = 0; i < getChildCount(); i++) { CellLayout page = (CellLayout) getChildAt(i); @@ -317,15 +283,12 @@ public class FolderPagedView extends PagedView { CellLayout currentPage = null; int position = 0; - int newX, newY, rank; + int rank = 0; - FolderIconPreviewVerifier verifier = new FolderIconPreviewVerifier( - Launcher.getLauncher(getContext()).getDeviceProfile().inv); - verifier.setFolderInfo(mFolder.getInfo()); - rank = 0; + mOrganizer.setFolderInfo(mFolder.getInfo()); for (int i = 0; i < itemCount; i++) { View v = list.size() > i ? list.get(i) : null; - if (currentPage == null || position >= mMaxItemsPerPage) { + if (currentPage == null || position >= mOrganizer.getMaxItemsPerPage()) { // Next page if (pageItr.hasNext()) { currentPage = pageItr.next(); @@ -337,28 +300,16 @@ public class FolderPagedView extends PagedView { if (v != null) { CellLayout.LayoutParams lp = (CellLayout.LayoutParams) v.getLayoutParams(); - newX = position % mGridCountX; - newY = position / mGridCountX; ItemInfo info = (ItemInfo) v.getTag(); - if (info.cellX != newX || info.cellY != newY || info.rank != rank) { - info.cellX = newX; - info.cellY = newY; - info.rank = rank; - if (saveChanges) { - mFolder.mLauncher.getModelWriter().addOrMoveItemInDatabase(info, - mFolder.mInfo.id, 0, info.cellX, info.cellY); - } - } - lp.cellX = info.cellX; - lp.cellY = info.cellY; + lp.setXY(mOrganizer.getPosForRank(rank)); currentPage.addViewToCellLayout(v, -1, info.getViewId(), lp, true); - if (verifier.isItemInPreview(rank) && v instanceof BubbleTextView) { + if (mOrganizer.isItemInPreview(rank) && v instanceof BubbleTextView) { ((BubbleTextView) v).verifyHighRes(); } } - rank ++; + rank++; position++; } @@ -391,16 +342,6 @@ public class FolderPagedView extends PagedView { (getPageAt(0).getDesiredHeight() + getPaddingTop() + getPaddingBottom()) : 0; } - public int getItemCount() { - int lastPageIndex = getChildCount() - 1; - if (lastPageIndex < 0) { - // If there are no pages, nothing has yet been added to the folder. - return 0; - } - return getPageAt(lastPageIndex).getShortcutsAndWidgets().getChildCount() - + lastPageIndex * mMaxItemsPerPage; - } - /** * @return the rank of the cell nearest to the provided pixel position. */ @@ -412,31 +353,28 @@ public class FolderPagedView extends PagedView { sTmpArray[0] = page.getCountX() - sTmpArray[0] - 1; } return Math.min(mAllocatedContentSize - 1, - pageIndex * mMaxItemsPerPage + sTmpArray[1] * mGridCountX + sTmpArray[0]); + pageIndex * mOrganizer.getMaxItemsPerPage() + + sTmpArray[1] * mGridCountX + sTmpArray[0]); } public View getFirstItem() { - if (getChildCount() < 1) { - return null; - } - ShortcutAndWidgetContainer currContainer = getCurrentCellLayout().getShortcutsAndWidgets(); - if (mGridCountX > 0) { - return currContainer.getChildAt(0, 0); - } else { - return currContainer.getChildAt(0); - } + return getViewInCurrentPage(c -> 0); } public View getLastItem() { + return getViewInCurrentPage(c -> c.getChildCount() - 1); + } + + private View getViewInCurrentPage(ToIntFunction rankProvider) { if (getChildCount() < 1) { return null; } - ShortcutAndWidgetContainer currContainer = getCurrentCellLayout().getShortcutsAndWidgets(); - int lastRank = currContainer.getChildCount() - 1; + ShortcutAndWidgetContainer container = getCurrentCellLayout().getShortcutsAndWidgets(); + int rank = rankProvider.applyAsInt(container); if (mGridCountX > 0) { - return currContainer.getChildAt(lastRank % mGridCountX, lastRank / mGridCountX); + return container.getChildAt(rank % mGridCountX, rank / mGridCountX); } else { - return currContainer.getChildAt(lastRank); + return container.getChildAt(rank); } } @@ -517,7 +455,7 @@ public class FolderPagedView extends PagedView { } public boolean rankOnCurrentPage(int rank) { - int p = rank / mMaxItemsPerPage; + int p = rank / mOrganizer.getMaxItemsPerPage(); return p == getNextPage(); } @@ -563,15 +501,16 @@ public class FolderPagedView extends PagedView { // Animation only happens on the current page. int pageToAnimate = getNextPage(); + int maxItemsPerPage = mOrganizer.getMaxItemsPerPage(); - int pageT = target / mMaxItemsPerPage; - int pagePosT = target % mMaxItemsPerPage; + int pageT = target / maxItemsPerPage; + int pagePosT = target % maxItemsPerPage; if (pageT != pageToAnimate) { Log.e(TAG, "Cannot animate when the target cell is invisible"); } - int pagePosE = empty % mMaxItemsPerPage; - int pageE = empty / mMaxItemsPerPage; + int pagePosE = empty % maxItemsPerPage; + int pageE = empty / maxItemsPerPage; int startPos, endPos; int moveStart, moveEnd; @@ -588,7 +527,7 @@ public class FolderPagedView extends PagedView { if (pageE < pageToAnimate) { moveStart = empty; // Instantly move the first item in the current page. - moveEnd = pageToAnimate * mMaxItemsPerPage; + moveEnd = pageToAnimate * maxItemsPerPage; // Animate the 2nd item in the current page, as the first item was already moved to // the last page. startPos = 0; @@ -606,10 +545,10 @@ public class FolderPagedView extends PagedView { // Move the items immediately. moveStart = empty; // Instantly move the last item in the current page. - moveEnd = (pageToAnimate + 1) * mMaxItemsPerPage - 1; + moveEnd = (pageToAnimate + 1) * maxItemsPerPage - 1; // Animations start with the second last item in the page - startPos = mMaxItemsPerPage - 1; + startPos = maxItemsPerPage - 1; } else { moveStart = moveEnd = -1; startPos = pagePosE; @@ -621,8 +560,8 @@ public class FolderPagedView extends PagedView { // Instant moving views. while (moveStart != moveEnd) { int rankToMove = moveStart + direction; - int p = rankToMove / mMaxItemsPerPage; - int pagePos = rankToMove % mMaxItemsPerPage; + int p = rankToMove / maxItemsPerPage; + int pagePos = rankToMove % maxItemsPerPage; int x = pagePos % mGridCountX; int y = pagePos / mGridCountX; @@ -667,9 +606,6 @@ public class FolderPagedView extends PagedView { for (int i = startPos; i != endPos; i += direction) { int nextPos = i + direction; View v = page.getChildAt(nextPos % mGridCountX, nextPos / mGridCountX); - if (v != null) { - ((ItemInfo) v.getTag()).rank -= direction; - } if (page.animateChildToPosition(v, i % mGridCountX, i / mGridCountX, REORDER_ANIMATION_DURATION, delay, true, true)) { delay += delayAmount; @@ -679,6 +615,6 @@ public class FolderPagedView extends PagedView { } public int itemsPerPage() { - return mMaxItemsPerPage; + return mOrganizer.getMaxItemsPerPage(); } } diff --git a/src/com/android/launcher3/folder/PreviewItemDrawingParams.java b/src/com/android/launcher3/folder/PreviewItemDrawingParams.java index c818462560..caf6e55b78 100644 --- a/src/com/android/launcher3/folder/PreviewItemDrawingParams.java +++ b/src/com/android/launcher3/folder/PreviewItemDrawingParams.java @@ -17,6 +17,8 @@ package com.android.launcher3.folder; import android.graphics.drawable.Drawable; +import com.android.launcher3.WorkspaceItemInfo; + /** * Manages the parameters used to draw a Folder preview item. */ @@ -25,9 +27,10 @@ class PreviewItemDrawingParams { float transY; float scale; float overlayAlpha; - FolderPreviewItemAnim anim; + public FolderPreviewItemAnim anim; public boolean hidden; - Drawable drawable; + public Drawable drawable; + public WorkspaceItemInfo item; PreviewItemDrawingParams(float transX, float transY, float scale, float overlayAlpha) { this.transX = transX; diff --git a/src/com/android/launcher3/folder/PreviewItemManager.java b/src/com/android/launcher3/folder/PreviewItemManager.java index 49763ba9de..2d817e6d2d 100644 --- a/src/com/android/launcher3/folder/PreviewItemManager.java +++ b/src/com/android/launcher3/folder/PreviewItemManager.java @@ -23,28 +23,51 @@ import static com.android.launcher3.folder.FolderIcon.DROP_IN_ANIMATION_DURATION import android.animation.Animator; import android.animation.AnimatorListenerAdapter; +import android.animation.ObjectAnimator; import android.animation.ValueAnimator; +import android.content.Context; import android.graphics.Canvas; import android.graphics.Rect; import android.graphics.drawable.Drawable; +import android.util.FloatProperty; import android.view.View; import android.widget.TextView; -import com.android.launcher3.BubbleTextView; -import com.android.launcher3.WorkspaceItemInfo; +import androidx.annotation.NonNull; + +import com.android.launcher3.Launcher; import com.android.launcher3.Utilities; +import com.android.launcher3.WorkspaceItemInfo; +import com.android.launcher3.graphics.DrawableFactory; +import com.android.launcher3.graphics.PreloadIconDrawable; import java.util.ArrayList; import java.util.List; - -import androidx.annotation.NonNull; +import java.util.function.Predicate; /** * Manages the drawing and animations of {@link PreviewItemDrawingParams} for a {@link FolderIcon}. */ public class PreviewItemManager { - private FolderIcon mIcon; + private static final FloatProperty CURRENT_PAGE_ITEMS_TRANS_X = + new FloatProperty("currentPageItemsTransX") { + @Override + public void setValue(PreviewItemManager manager, float v) { + manager.mCurrentPageItemsTransX = v; + manager.onParamsChanged(); + } + + @Override + public Float get(PreviewItemManager manager) { + return manager.mCurrentPageItemsTransX; + } + }; + + private final Context mContext; + private final FolderIcon mIcon; + private final DrawableFactory mDrawableFactory; + private final int mIconSize; // These variables are all associated with the drawing of the preview; they are stored // as member variables for shared usage and to avoid computation on each frame @@ -69,7 +92,10 @@ public class PreviewItemManager { private static final int ITEM_SLIDE_IN_OUT_DISTANCE_PX = 200; public PreviewItemManager(FolderIcon icon) { + mContext = icon.getContext(); mIcon = icon; + mDrawableFactory = DrawableFactory.INSTANCE.get(mContext); + mIconSize = Launcher.getLauncher(mContext).getDeviceProfile().folderChildIconSizePx; } /** @@ -200,7 +226,7 @@ public class PreviewItemManager { } void buildParamsForPage(int page, ArrayList params, boolean animate) { - List items = mIcon.getPreviewItemsOnPage(page); + List items = mIcon.getPreviewItemsOnPage(page); int prevNumItems = params.size(); // We adjust the size of the list to match the number of items in the preview. @@ -214,13 +240,7 @@ public class PreviewItemManager { int numItemsInFirstPagePreview = page == 0 ? items.size() : MAX_NUM_ITEMS_IN_PREVIEW; for (int i = 0; i < params.size(); i++) { PreviewItemDrawingParams p = params.get(i); - p.drawable = items.get(i).getCompoundDrawables()[1]; - - if (p.drawable != null && !mIcon.mFolder.isOpen()) { - // Set the callback to FolderIcon as it is responsible to drawing the icon. The - // callback will be released when the folder is opened. - p.drawable.setCallback(mIcon); - } + setDrawable(p, items.get(i)); if (!animate) { computePreviewItemDrawingParams(i, numItemsInFirstPagePreview, p); @@ -253,14 +273,8 @@ public class PreviewItemManager { buildParamsForPage(currentPage, mCurrentPageParams, false); onParamsChanged(); - ValueAnimator slideAnimator = ValueAnimator.ofFloat(0, ITEM_SLIDE_IN_OUT_DISTANCE_PX); - slideAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() { - @Override - public void onAnimationUpdate(ValueAnimator valueAnimator) { - mCurrentPageItemsTransX = (float) valueAnimator.getAnimatedValue(); - onParamsChanged(); - } - }); + ValueAnimator slideAnimator = ObjectAnimator + .ofFloat(this, CURRENT_PAGE_ITEMS_TRANS_X, 0, ITEM_SLIDE_IN_OUT_DISTANCE_PX); slideAnimator.addListener(new AnimatorListenerAdapter() { @Override public void onAnimationEnd(Animator animation) { @@ -277,6 +291,25 @@ public class PreviewItemManager { buildParamsForPage(0, mFirstPageParams, animate); } + void updatePreviewItems(Predicate itemCheck) { + boolean modified = false; + for (PreviewItemDrawingParams param : mFirstPageParams) { + if (itemCheck.test(param.item)) { + setDrawable(param, param.item); + modified = true; + } + } + for (PreviewItemDrawingParams param : mCurrentPageParams) { + if (itemCheck.test(param.item)) { + setDrawable(param, param.item); + modified = true; + } + } + if (modified) { + mIcon.invalidate(); + } + } + boolean verifyDrawable(@NonNull Drawable who) { for (int i = 0; i < mFirstPageParams.size(); i++) { if (mFirstPageParams.get(i).drawable == who) { @@ -296,46 +329,46 @@ public class PreviewItemManager { * - Moving into a new position * - Moving out of the preview * - * @param oldParams The list of items in the old preview. - * @param newParams The list of items in the new preview. + * @param oldItems The list of items in the old preview. + * @param newItems The list of items in the new preview. * @param dropped The item that was dropped onto the FolderIcon. */ - public void onDrop(List oldParams, List newParams, + public void onDrop(List oldItems, List newItems, WorkspaceItemInfo dropped) { - int numItems = newParams.size(); + int numItems = newItems.size(); final ArrayList params = mFirstPageParams; buildParamsForPage(0, params, false); // New preview items for items that are moving in (except for the dropped item). - List moveIn = new ArrayList<>(); - for (BubbleTextView btv : newParams) { - if (!oldParams.contains(btv) && !btv.getTag().equals(dropped)) { - moveIn.add(btv); + List moveIn = new ArrayList<>(); + for (WorkspaceItemInfo newItem : newItems) { + if (!oldItems.contains(newItem) && !newItem.equals(dropped)) { + moveIn.add(newItem); } } for (int i = 0; i < moveIn.size(); ++i) { - int prevIndex = newParams.indexOf(moveIn.get(i)); + int prevIndex = newItems.indexOf(moveIn.get(i)); PreviewItemDrawingParams p = params.get(prevIndex); computePreviewItemDrawingParams(prevIndex, numItems, p); - updateTransitionParam(p, moveIn.get(i), ENTER_INDEX, newParams.indexOf(moveIn.get(i)), + updateTransitionParam(p, moveIn.get(i), ENTER_INDEX, newItems.indexOf(moveIn.get(i)), numItems); } // Items that are moving into new positions within the preview. - for (int newIndex = 0; newIndex < newParams.size(); ++newIndex) { - int oldIndex = oldParams.indexOf(newParams.get(newIndex)); + for (int newIndex = 0; newIndex < newItems.size(); ++newIndex) { + int oldIndex = oldItems.indexOf(newItems.get(newIndex)); if (oldIndex >= 0 && newIndex != oldIndex) { PreviewItemDrawingParams p = params.get(newIndex); - updateTransitionParam(p, newParams.get(newIndex), oldIndex, newIndex, numItems); + updateTransitionParam(p, newItems.get(newIndex), oldIndex, newIndex, numItems); } } // Old preview items that need to be moved out. - List moveOut = new ArrayList<>(oldParams); - moveOut.removeAll(newParams); + List moveOut = new ArrayList<>(oldItems); + moveOut.removeAll(newItems); for (int i = 0; i < moveOut.size(); ++i) { - BubbleTextView item = moveOut.get(i); - int oldIndex = oldParams.indexOf(item); + WorkspaceItemInfo item = moveOut.get(i); + int oldIndex = oldItems.indexOf(item); PreviewItemDrawingParams p = computePreviewItemDrawingParams(oldIndex, numItems, null); updateTransitionParam(p, item, oldIndex, EXIT_INDEX, numItems); params.add(0, p); // We want these items first so that they are on drawn last. @@ -348,14 +381,9 @@ public class PreviewItemManager { } } - private void updateTransitionParam(final PreviewItemDrawingParams p, BubbleTextView btv, + private void updateTransitionParam(final PreviewItemDrawingParams p, WorkspaceItemInfo item, int prevIndex, int newIndex, int numItems) { - p.drawable = btv.getCompoundDrawables()[1]; - if (!mIcon.mFolder.isOpen()) { - // Set the callback to FolderIcon as it is responsible to drawing the icon. The - // callback will be released when the folder is opened. - p.drawable.setCallback(mIcon); - } + setDrawable(p, item); FolderPreviewItemAnim anim = new FolderPreviewItemAnim(this, p, prevIndex, numItems, newIndex, numItems, DROP_IN_ANIMATION_DURATION, null); @@ -364,4 +392,20 @@ public class PreviewItemManager { } p.anim = anim; } + + private void setDrawable(PreviewItemDrawingParams p, WorkspaceItemInfo item) { + if (item.hasPromiseIconUi()) { + PreloadIconDrawable drawable = mDrawableFactory.newPendingIcon(mContext, item); + drawable.setLevel(item.getInstallProgress()); + p.drawable = drawable; + } else { + p.drawable = mDrawableFactory.newIcon(mContext, item); + } + p.drawable.setBounds(0, 0, mIconSize, mIconSize); + p.item = item; + + // Set the callback to FolderIcon as it is responsible to drawing the icon. The + // callback will be released when the folder is opened. + p.drawable.setCallback(mIcon); + } } diff --git a/src/com/android/launcher3/graphics/DragPreviewProvider.java b/src/com/android/launcher3/graphics/DragPreviewProvider.java index 9263a2ac94..747efe3075 100644 --- a/src/com/android/launcher3/graphics/DragPreviewProvider.java +++ b/src/com/android/launcher3/graphics/DragPreviewProvider.java @@ -16,6 +16,8 @@ package com.android.launcher3.graphics; +import static com.android.launcher3.util.Executors.UI_HELPER_EXECUTOR; + import android.content.Context; import android.graphics.Bitmap; import android.graphics.BlurMaskFilter; @@ -25,7 +27,6 @@ import android.graphics.PorterDuff; import android.graphics.PorterDuffXfermode; import android.graphics.Rect; import android.graphics.drawable.Drawable; -import android.os.Handler; import android.view.View; import com.android.launcher3.BubbleTextView; @@ -35,7 +36,6 @@ import com.android.launcher3.R; import com.android.launcher3.config.FeatureFlags; import com.android.launcher3.folder.FolderIcon; import com.android.launcher3.icons.BitmapRenderer; -import com.android.launcher3.util.UiThreadHelper; import com.android.launcher3.widget.LauncherAppWidgetHostView; import com.android.launcher3.widget.PendingAppWidgetHostView; @@ -157,7 +157,7 @@ public class DragPreviewProvider { } mOutlineGeneratorCallback = new OutlineGeneratorCallback(preview); - new Handler(UiThreadHelper.getBackgroundLooper()).post(mOutlineGeneratorCallback); + UI_HELPER_EXECUTOR.post(mOutlineGeneratorCallback); } protected static Rect getDrawableBounds(Drawable d) { diff --git a/src/com/android/launcher3/graphics/GridOptionsProvider.java b/src/com/android/launcher3/graphics/GridOptionsProvider.java index efd39ee8ed..71b436600d 100644 --- a/src/com/android/launcher3/graphics/GridOptionsProvider.java +++ b/src/com/android/launcher3/graphics/GridOptionsProvider.java @@ -1,5 +1,7 @@ package com.android.launcher3.graphics; +import static com.android.launcher3.util.Executors.UI_HELPER_EXECUTOR; + import android.content.ContentProvider; import android.content.ContentValues; import android.content.res.XmlResourceParser; @@ -17,8 +19,6 @@ import android.util.Xml; import com.android.launcher3.InvariantDeviceProfile; import com.android.launcher3.InvariantDeviceProfile.GridOption; import com.android.launcher3.R; -import com.android.launcher3.util.LooperExecutor; -import com.android.launcher3.util.UiThreadHelper; import org.xmlpull.v1.XmlPullParser; import org.xmlpull.v1.XmlPullParserException; @@ -180,10 +180,10 @@ public class GridOptionsProvider extends ContentProvider { throw new FileNotFoundException(e.getMessage()); } - LooperExecutor executor = new LooperExecutor(UiThreadHelper.getBackgroundLooper()); try { return openPipeHelper(uri, MIME_TYPE_PNG, null, - executor.submit(new LauncherPreviewRenderer(getContext(), idp)), BITMAP_WRITER); + UI_HELPER_EXECUTOR.submit(new LauncherPreviewRenderer(getContext(), idp)), + BITMAP_WRITER); } catch (Exception e) { throw new FileNotFoundException(e.getMessage()); } diff --git a/src/com/android/launcher3/icons/IconCache.java b/src/com/android/launcher3/icons/IconCache.java index abff237e8b..50b5222d3e 100644 --- a/src/com/android/launcher3/icons/IconCache.java +++ b/src/com/android/launcher3/icons/IconCache.java @@ -16,6 +16,9 @@ package com.android.launcher3.icons; +import static com.android.launcher3.util.Executors.MAIN_EXECUTOR; +import static com.android.launcher3.util.Executors.MODEL_EXECUTOR; + import android.content.Context; import android.content.Intent; import android.content.pm.ApplicationInfo; @@ -23,6 +26,7 @@ import android.content.pm.LauncherActivityInfo; import android.content.pm.PackageInfo; import android.content.pm.PackageManager; import android.content.pm.PackageManager.NameNotFoundException; +import android.content.pm.ShortcutInfo; import android.graphics.drawable.Drawable; import android.os.Handler; import android.os.Process; @@ -36,8 +40,6 @@ import com.android.launcher3.IconProvider; import com.android.launcher3.InvariantDeviceProfile; import com.android.launcher3.ItemInfoWithIcon; import com.android.launcher3.LauncherFiles; -import com.android.launcher3.LauncherModel; -import com.android.launcher3.MainThreadExecutor; import com.android.launcher3.Utilities; import com.android.launcher3.WorkspaceItemInfo; import com.android.launcher3.compat.LauncherAppsCompat; @@ -48,6 +50,7 @@ import com.android.launcher3.icons.cache.BaseIconCache; import com.android.launcher3.icons.cache.CachingLogic; import com.android.launcher3.icons.cache.HandlerRunnable; import com.android.launcher3.model.PackageItemInfo; +import com.android.launcher3.shortcuts.ShortcutKey; import com.android.launcher3.util.InstantAppResolver; import com.android.launcher3.util.Preconditions; @@ -60,10 +63,9 @@ public class IconCache extends BaseIconCache { private static final String TAG = "Launcher.IconCache"; - private final MainThreadExecutor mMainThreadExecutor = new MainThreadExecutor(); - private final CachingLogic mComponentWithLabelCachingLogic; private final CachingLogic mLauncherActivityInfoCachingLogic; + private final CachingLogic mShortcutCachingLogic; private final LauncherAppsCompat mLauncherApps; private final UserManagerCompat mUserManager; @@ -73,10 +75,11 @@ public class IconCache extends BaseIconCache { private int mPendingIconRequestCount = 0; public IconCache(Context context, InvariantDeviceProfile inv) { - super(context, LauncherFiles.APP_ICONS_DB, LauncherModel.getWorkerLooper(), + super(context, LauncherFiles.APP_ICONS_DB, MODEL_EXECUTOR.getLooper(), inv.fillResIconDpi, inv.iconBitmapSize, true /* inMemoryCache */); mComponentWithLabelCachingLogic = new ComponentCachingLogic(context); mLauncherActivityInfoCachingLogic = LauncherActivityCachingLogic.newInstance(context); + mShortcutCachingLogic = new ShortcutCachingLogic(); mLauncherApps = LauncherAppsCompat.getInstance(mContext); mUserManager = UserManagerCompat.getInstance(mContext); mInstantAppResolver = InstantAppResolver.newInstance(mContext); @@ -124,7 +127,7 @@ public class IconCache extends BaseIconCache { final ItemInfoWithIcon info) { Preconditions.assertUIThread(); if (mPendingIconRequestCount <= 0) { - LauncherModel.setWorkerPriority(Process.THREAD_PRIORITY_FOREGROUND); + MODEL_EXECUTOR.setThreadPriority(Process.THREAD_PRIORITY_FOREGROUND); } mPendingIconRequestCount ++; @@ -136,7 +139,7 @@ public class IconCache extends BaseIconCache { } else if (info instanceof PackageItemInfo) { getTitleAndIconForApp((PackageItemInfo) info, false); } - mMainThreadExecutor.execute(() -> { + MAIN_EXECUTOR.execute(() -> { caller.reapplyItemInfo(info); onEnd(); }); @@ -149,7 +152,7 @@ public class IconCache extends BaseIconCache { private void onIconRequestEnd() { mPendingIconRequestCount --; if (mPendingIconRequestCount <= 0) { - LauncherModel.setWorkerPriority(Process.THREAD_PRIORITY_BACKGROUND); + MODEL_EXECUTOR.setThreadPriority(Process.THREAD_PRIORITY_BACKGROUND); } } @@ -174,6 +177,14 @@ public class IconCache extends BaseIconCache { getTitleAndIcon(info, () -> activityInfo, false, useLowResIcon); } + /** + * Fill in info with the icon and label for deep shortcut. + */ + public synchronized CacheEntry getDeepShortcutTitleAndIcon(ShortcutInfo info) { + return cacheLocked(ShortcutKey.fromInfo(info).componentName, info.getUserHandle(), + () -> info, mShortcutCachingLogic, false, false); + } + /** * Fill in {@param info} with the icon and label. If the * corresponding activity is not found, it reverts to the package icon. diff --git a/src/com/android/launcher3/icons/LauncherIcons.java b/src/com/android/launcher3/icons/LauncherIcons.java index 763240885e..c6949afc37 100644 --- a/src/com/android/launcher3/icons/LauncherIcons.java +++ b/src/com/android/launcher3/icons/LauncherIcons.java @@ -21,9 +21,10 @@ import android.content.Context; import android.content.Intent; import android.content.pm.ShortcutInfo; import android.graphics.Bitmap; -import android.graphics.drawable.Drawable; import android.os.Process; +import androidx.annotation.Nullable; + import com.android.launcher3.AppInfo; import com.android.launcher3.FastBitmapDrawable; import com.android.launcher3.InvariantDeviceProfile; @@ -31,14 +32,12 @@ import com.android.launcher3.ItemInfoWithIcon; import com.android.launcher3.LauncherAppState; import com.android.launcher3.R; import com.android.launcher3.graphics.IconShape; +import com.android.launcher3.icons.cache.BaseIconCache; import com.android.launcher3.model.PackageItemInfo; -import com.android.launcher3.shortcuts.DeepShortcutManager; import com.android.launcher3.util.Themes; import java.util.function.Supplier; -import androidx.annotation.Nullable; - /** * Wrapper class to provide access to {@link BaseIconFactory} and also to provide pool of this class * that are threadsafe. @@ -126,13 +125,12 @@ public class LauncherIcons extends BaseIconFactory implements AutoCloseable { public BitmapInfo createShortcutIcon(ShortcutInfo shortcutInfo, boolean badged, @Nullable Supplier fallbackIconProvider) { - Drawable unbadgedDrawable = DeepShortcutManager.getInstance(mContext) - .getShortcutIconDrawable(shortcutInfo, mFillResIconDpi); IconCache cache = LauncherAppState.getInstance(mContext).getIconCache(); + BaseIconCache.CacheEntry entry = cache.getDeepShortcutTitleAndIcon(shortcutInfo); final Bitmap unbadgedBitmap; - if (unbadgedDrawable != null) { - unbadgedBitmap = createScaledBitmapWithoutShadow(unbadgedDrawable, 0); + if (entry.icon != null) { + unbadgedBitmap = entry.icon; } else { if (fallbackIconProvider != null) { // Fallback icons are already badged and with appropriate shadow diff --git a/src/com/android/launcher3/icons/ShortcutCachingLogic.java b/src/com/android/launcher3/icons/ShortcutCachingLogic.java new file mode 100644 index 0000000000..ee79b249ce --- /dev/null +++ b/src/com/android/launcher3/icons/ShortcutCachingLogic.java @@ -0,0 +1,67 @@ +/* + * 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.icons; + +import android.content.ComponentName; +import android.content.Context; +import android.content.pm.PackageInfo; +import android.content.pm.ShortcutInfo; +import android.graphics.drawable.Drawable; +import android.os.UserHandle; + +import com.android.launcher3.LauncherAppState; +import com.android.launcher3.icons.cache.CachingLogic; +import com.android.launcher3.shortcuts.DeepShortcutManager; +import com.android.launcher3.shortcuts.ShortcutKey; + +/** + * Caching logic for shortcuts. + */ +public class ShortcutCachingLogic implements CachingLogic { + + @Override + public ComponentName getComponent(ShortcutInfo info) { + return ShortcutKey.fromInfo(info).componentName; + } + + @Override + public UserHandle getUser(ShortcutInfo info) { + return info.getUserHandle(); + } + + @Override + public CharSequence getLabel(ShortcutInfo info) { + return info.getShortLabel(); + } + + @Override + public void loadIcon(Context context, ShortcutInfo info, BitmapInfo target) { + LauncherIcons li = LauncherIcons.obtain(context); + Drawable unbadgedDrawable = DeepShortcutManager.getInstance(context) + .getShortcutIconDrawable(info, LauncherAppState.getIDP(context).fillResIconDpi); + if (unbadgedDrawable != null) { + target.icon = li.createScaledBitmapWithoutShadow(unbadgedDrawable, 0); + } + li.recycle(); + } + + @Override + public long getLastUpdatedTime(ShortcutInfo shortcutInfo, PackageInfo info) { + if (shortcutInfo == null) return info.lastUpdateTime; + return Math.max(shortcutInfo.getLastChangedTimestamp(), info.lastUpdateTime); + } +} diff --git a/src/com/android/launcher3/logging/EventLogArray.java b/src/com/android/launcher3/logging/EventLogArray.java index f20f3659e2..3ecfb23c2c 100644 --- a/src/com/android/launcher3/logging/EventLogArray.java +++ b/src/com/android/launcher3/logging/EventLogArray.java @@ -16,11 +16,13 @@ package com.android.launcher3.logging; +import android.util.Log; import java.io.PrintWriter; import java.text.SimpleDateFormat; import java.util.Arrays; import java.util.Date; import java.util.Locale; +import java.util.Random; /** * A utility class to record and log events. Events are stored in a fixed size array and old logs @@ -37,6 +39,7 @@ public class EventLogArray { private final String name; private final EventEntry[] logs; private int nextIndex; + private int mLogId; public EventLogArray(String name, int size) { this.name = name; @@ -52,10 +55,6 @@ public class EventLogArray { addLog(TYPE_INTEGER, event, extras); } - public void addLog(String event, float extras) { - addLog(TYPE_FLOAT, event, extras); - } - public void addLog(String event, boolean extras) { addLog(extras ? TYPE_BOOL_TRUE : TYPE_BOOL_FALSE, event, 0); } @@ -65,7 +64,7 @@ public class EventLogArray { int last = (nextIndex + logs.length - 1) % logs.length; int secondLast = (nextIndex + logs.length - 2) % logs.length; if (isEntrySame(logs[last], type, event) && isEntrySame(logs[secondLast], type, event)) { - logs[last].update(type, event, extras); + logs[last].update(type, event, extras, mLogId); logs[secondLast].duplicateCount++; return; } @@ -73,7 +72,7 @@ public class EventLogArray { if (logs[nextIndex] == null) { logs[nextIndex] = new EventEntry(); } - logs[nextIndex].update(type, event, extras); + logs[nextIndex].update(type, event, extras, mLogId); nextIndex = (nextIndex + 1) % logs.length; } @@ -113,10 +112,18 @@ public class EventLogArray { if (log.duplicateCount > 0) { msg.append(" & ").append(log.duplicateCount).append(" similar events"); } + msg.append(" traceId: ").append(log.traceId); writer.println(msg); } } + /** Returns a 3 digit random number between 100-999 */ + public int generateAndSetLogId() { + Random r = new Random(); + mLogId = r.nextInt(900) + 100; + return mLogId; + } + private boolean isEntrySame(EventEntry entry, int type, String event) { return entry != null && entry.type == type && entry.event.equals(event); } @@ -129,11 +136,13 @@ public class EventLogArray { private float extras; private long time; private int duplicateCount; + private int traceId; - public void update(int type, String event, float extras) { + public void update(int type, String event, float extras, int traceId) { this.type = type; this.event = event; this.extras = extras; + this.traceId = traceId; time = System.currentTimeMillis(); duplicateCount = 0; } diff --git a/src/com/android/launcher3/logging/FileLog.java b/src/com/android/launcher3/logging/FileLog.java index f7f8ef18f6..923a89b1cc 100644 --- a/src/com/android/launcher3/logging/FileLog.java +++ b/src/com/android/launcher3/logging/FileLog.java @@ -1,13 +1,14 @@ package com.android.launcher3.logging; +import static com.android.launcher3.util.Executors.createAndStartNewLooper; + import android.os.Handler; import android.os.HandlerThread; import android.os.Message; import android.util.Log; import android.util.Pair; -import com.android.launcher3.Utilities; -import com.android.launcher3.config.FeatureFlags; +import com.android.launcher3.util.IOUtils; import java.io.BufferedReader; import java.io.File; @@ -29,8 +30,7 @@ import java.util.concurrent.TimeUnit; */ public final class FileLog { - protected static final boolean ENABLED = - FeatureFlags.IS_DOGFOOD_BUILD || Utilities.IS_DEBUG_DEVICE; + protected static final boolean ENABLED = true; private static final String FILE_NAME_PREFIX = "log-"; private static final DateFormat DATE_FORMAT = DateFormat.getDateTimeInstance(DateFormat.SHORT, DateFormat.SHORT); @@ -91,9 +91,8 @@ public final class FileLog { private static Handler getHandler() { synchronized (DATE_FORMAT) { if (sHandler == null) { - HandlerThread thread = new HandlerThread("file-logger"); - thread.start(); - sHandler = new Handler(thread.getLooper(), new LogWriterCallback()); + sHandler = new Handler(createAndStartNewLooper("file-logger"), + new LogWriterCallback()); } } return sHandler; @@ -131,7 +130,7 @@ public final class FileLog { private PrintWriter mCurrentWriter = null; private void closeWriter() { - Utilities.closeSilently(mCurrentWriter); + IOUtils.closeSilently(mCurrentWriter); mCurrentWriter = null; } @@ -219,7 +218,7 @@ public final class FileLog { } catch (Exception e) { // ignore } finally { - Utilities.closeSilently(in); + IOUtils.closeSilently(in); } } } diff --git a/src/com/android/launcher3/logging/UserEventDispatcher.java b/src/com/android/launcher3/logging/UserEventDispatcher.java index c72b07a7f6..21ca74e12b 100644 --- a/src/com/android/launcher3/logging/UserEventDispatcher.java +++ b/src/com/android/launcher3/logging/UserEventDispatcher.java @@ -427,10 +427,16 @@ public class UserEventDispatcher implements ResourceBasedOverride { mAppOrTaskLaunch = false; ev.elapsedContainerMillis = SystemClock.uptimeMillis() - mElapsedContainerMillis; ev.elapsedSessionMillis = SystemClock.uptimeMillis() - mElapsedSessionMillis; - if (!IS_VERBOSE) { return; } + Log.d(TAG, generateLog(ev)); + } + + /** + * Returns a human-readable log for given user event. + */ + public static String generateLog(LauncherEvent ev) { String log = "\n-----------------------------------------------------" + "\naction:" + LoggerUtils.getActionStr(ev.action); if (ev.srcTarget != null && ev.srcTarget.length > 0) { @@ -445,8 +451,7 @@ public class UserEventDispatcher implements ResourceBasedOverride { ev.elapsedSessionMillis, ev.actionDurationMillis); log += "\n\n"; - Log.d(TAG, log); - return; + return log; } private static String getTargetsStr(Target[] targets) { diff --git a/src/com/android/launcher3/model/AddWorkspaceItemsTask.java b/src/com/android/launcher3/model/AddWorkspaceItemsTask.java index 7d4f2f7225..dfd5a708bf 100644 --- a/src/com/android/launcher3/model/AddWorkspaceItemsTask.java +++ b/src/com/android/launcher3/model/AddWorkspaceItemsTask.java @@ -22,7 +22,6 @@ import android.os.UserHandle; import android.util.LongSparseArray; import android.util.Pair; -import com.android.launcher3.AllAppsList; import com.android.launcher3.AppInfo; import com.android.launcher3.FolderInfo; import com.android.launcher3.InvariantDeviceProfile; @@ -30,14 +29,14 @@ import com.android.launcher3.ItemInfo; import com.android.launcher3.LauncherAppState; import com.android.launcher3.LauncherAppWidgetInfo; import com.android.launcher3.LauncherModel.CallbackTask; -import com.android.launcher3.LauncherModel.Callbacks; +import com.android.launcher3.model.BgDataModel.Callbacks; import com.android.launcher3.LauncherSettings; import com.android.launcher3.WorkspaceItemInfo; -import com.android.launcher3.Utilities; import com.android.launcher3.compat.LauncherAppsCompat; import com.android.launcher3.compat.PackageInstallerCompat; import com.android.launcher3.util.GridOccupancy; import com.android.launcher3.util.IntArray; +import com.android.launcher3.util.PackageManagerHelper; import java.util.ArrayList; import java.util.List; @@ -200,7 +199,7 @@ public class AddWorkspaceItemsTask extends BaseModelUpdateTask { intentWithoutPkg = intent.toUri(0); } - boolean isLauncherAppTarget = Utilities.isLauncherAppTarget(intent); + boolean isLauncherAppTarget = PackageManagerHelper.isLauncherAppTarget(intent); synchronized (dataModel) { for (ItemInfo item : dataModel.itemsIdMap) { if (item instanceof WorkspaceItemInfo) { diff --git a/src/com/android/launcher3/AllAppsList.java b/src/com/android/launcher3/model/AllAppsList.java similarity index 69% rename from src/com/android/launcher3/AllAppsList.java rename to src/com/android/launcher3/model/AllAppsList.java index 8b49c06389..3873a17e03 100644 --- a/src/com/android/launcher3/AllAppsList.java +++ b/src/com/android/launcher3/model/AllAppsList.java @@ -14,25 +14,37 @@ * limitations under the License. */ -package com.android.launcher3; +package com.android.launcher3.model; + +import static com.android.launcher3.AppInfo.COMPONENT_KEY_COMPARATOR; +import static com.android.launcher3.AppInfo.EMPTY_ARRAY; import android.content.ComponentName; import android.content.Context; import android.content.pm.ApplicationInfo; import android.content.pm.LauncherActivityInfo; +import android.os.LocaleList; import android.os.Process; import android.os.UserHandle; import android.util.Log; +import com.android.launcher3.AppFilter; +import com.android.launcher3.AppInfo; +import com.android.launcher3.PromiseAppInfo; +import com.android.launcher3.compat.AlphabeticIndexCompat; import com.android.launcher3.compat.LauncherAppsCompat; import com.android.launcher3.compat.PackageInstallerCompat; +import com.android.launcher3.compat.PackageInstallerCompat.PackageInstallInfo; import com.android.launcher3.icons.IconCache; import com.android.launcher3.util.FlagOp; import com.android.launcher3.util.ItemInfoMatcher; +import com.android.launcher3.util.SafeCloseable; import java.util.ArrayList; +import java.util.Arrays; import java.util.HashSet; import java.util.List; +import java.util.function.Consumer; import androidx.annotation.NonNull; import androidx.annotation.Nullable; @@ -42,29 +54,40 @@ import androidx.annotation.Nullable; * Stores the list of all applications for the all apps view. */ public class AllAppsList { + private static final String TAG = "AllAppsList"; + private static final Consumer NO_OP_CONSUMER = a -> { }; + public static final int DEFAULT_APPLICATIONS_NUMBER = 42; /** The list off all apps. */ public final ArrayList data = new ArrayList<>(DEFAULT_APPLICATIONS_NUMBER); - /** The list of apps that have been added since the last notify() call. */ - public ArrayList added = new ArrayList<>(DEFAULT_APPLICATIONS_NUMBER); - /** The list of apps that have been removed since the last notify() call. */ - public ArrayList removed = new ArrayList<>(); - /** The list of apps that have been modified since the last notify() call. */ - public ArrayList modified = new ArrayList<>(); private IconCache mIconCache; - private AppFilter mAppFilter; + private boolean mDataChanged = false; + private Consumer mRemoveListener = NO_OP_CONSUMER; + + private AlphabeticIndexCompat mIndex; + /** * Boring constructor. */ public AllAppsList(IconCache iconCache, AppFilter appFilter) { mIconCache = iconCache; mAppFilter = appFilter; + mIndex = new AlphabeticIndexCompat(LocaleList.getDefault()); + } + + /** + * Returns true if there have been any changes since last call. + */ + public boolean getAndResetChangeFlag() { + boolean result = mDataChanged; + mDataChanged = false; + return result; } /** @@ -81,9 +104,10 @@ public class AllAppsList { return; } mIconCache.getTitleAndIcon(info, activityInfo, true /* useLowResIcon */); + info.sectionName = mIndex.computeSectionName(info.title); data.add(info); - added.add(info); + mDataChanged = true; } public void addPromiseApp(Context context, @@ -94,31 +118,46 @@ public class AllAppsList { if (applicationInfo == null) { PromiseAppInfo info = new PromiseAppInfo(installInfo); mIconCache.getTitleAndIcon(info, info.usingLowResIcon()); + info.sectionName = mIndex.computeSectionName(info.title); + data.add(info); - added.add(info); + mDataChanged = true; } } - public void removePromiseApp(AppInfo appInfo) { - // the removed list is handled by the caller - // so not adding it here - data.remove(appInfo); + public PromiseAppInfo updatePromiseInstallInfo(PackageInstallInfo installInfo) { + UserHandle user = Process.myUserHandle(); + for (int i=0; i < data.size(); i++) { + final AppInfo appInfo = data.get(i); + final ComponentName tgtComp = appInfo.getTargetComponent(); + if (tgtComp != null && tgtComp.getPackageName().equals(installInfo.packageName) + && appInfo.user.equals(user) + && appInfo instanceof PromiseAppInfo) { + final PromiseAppInfo promiseAppInfo = (PromiseAppInfo) appInfo; + if (installInfo.state == PackageInstallerCompat.STATUS_INSTALLING) { + promiseAppInfo.level = installInfo.progress; + return promiseAppInfo; + } else if (installInfo.state == PackageInstallerCompat.STATUS_FAILED) { + removeApp(i); + } + } + } + return null; + } + + private void removeApp(int index) { + AppInfo removed = data.remove(index); + if (removed != null) { + mDataChanged = true; + mRemoveListener.accept(removed); + } } public void clear() { data.clear(); - // TODO: do we clear these too? - added.clear(); - removed.clear(); - modified.clear(); - } - - public int size() { - return data.size(); - } - - public AppInfo get(int index) { - return data.get(index); + mDataChanged = false; + // Reset the index as locales might have changed + mIndex = new AlphabeticIndexCompat(LocaleList.getDefault()); } /** @@ -142,8 +181,7 @@ public class AllAppsList { for (int i = data.size() - 1; i >= 0; i--) { AppInfo info = data.get(i); if (info.user.equals(user) && packageName.equals(info.componentName.getPackageName())) { - removed.add(info); - data.remove(i); + removeApp(i); } } } @@ -157,17 +195,17 @@ public class AllAppsList { AppInfo info = data.get(i); if (matcher.matches(info, info.componentName)) { info.runtimeStatusFlags = op.apply(info.runtimeStatusFlags); - modified.add(info); + mDataChanged = true; } } } - public void updateIconsAndLabels(HashSet packages, UserHandle user, - ArrayList outUpdates) { + public void updateIconsAndLabels(HashSet packages, UserHandle user) { for (AppInfo info : data) { if (info.user.equals(user) && packages.contains(info.componentName.getPackageName())) { mIconCache.updateTitleAndIcon(info); - outUpdates.add(info); + info.sectionName = mIndex.computeSectionName(info.title); + mDataChanged = true; } } } @@ -188,8 +226,7 @@ public class AllAppsList { && packageName.equals(applicationInfo.componentName.getPackageName())) { if (!findActivity(matches, applicationInfo.componentName)) { Log.w(TAG, "Changing shortcut target due to app component name change."); - removed.add(applicationInfo); - data.remove(i); + removeApp(i); } } } @@ -202,7 +239,9 @@ public class AllAppsList { add(new AppInfo(context, info, user), info); } else { mIconCache.getTitleAndIcon(applicationInfo, info, true /* useLowResIcon */); - modified.add(applicationInfo); + applicationInfo.sectionName = mIndex.computeSectionName(applicationInfo.title); + + mDataChanged = true; } } } else { @@ -211,15 +250,13 @@ public class AllAppsList { final AppInfo applicationInfo = data.get(i); if (user.equals(applicationInfo.user) && packageName.equals(applicationInfo.componentName.getPackageName())) { - removed.add(applicationInfo); mIconCache.remove(applicationInfo.componentName, user); - data.remove(i); + removeApp(i); } } } } - /** * Returns whether apps contains component. */ @@ -247,4 +284,16 @@ public class AllAppsList { } return null; } + + public AppInfo[] copyData() { + AppInfo[] result = data.toArray(EMPTY_ARRAY); + Arrays.sort(result, COMPONENT_KEY_COMPARATOR); + return result; + } + + public SafeCloseable trackRemoves(Consumer removeListener) { + mRemoveListener = removeListener; + + return () -> mRemoveListener = NO_OP_CONSUMER; + } } diff --git a/src/com/android/launcher3/model/AppLaunchTracker.java b/src/com/android/launcher3/model/AppLaunchTracker.java index 29a46cfa5c..13ab033e4a 100644 --- a/src/com/android/launcher3/model/AppLaunchTracker.java +++ b/src/com/android/launcher3/model/AppLaunchTracker.java @@ -51,5 +51,8 @@ public class AppLaunchTracker implements ResourceBasedOverride { public void onStartApp(ComponentName componentName, UserHandle user, @Nullable String container) { } + public void onDismissApp(ComponentName componentName, UserHandle user, + @Nullable String container){} + public void onReturnedToHome() { } } diff --git a/src/com/android/launcher3/model/BaseLoaderResults.java b/src/com/android/launcher3/model/BaseLoaderResults.java index 97cf267d34..0a4f00582d 100644 --- a/src/com/android/launcher3/model/BaseLoaderResults.java +++ b/src/com/android/launcher3/model/BaseLoaderResults.java @@ -16,21 +16,21 @@ package com.android.launcher3.model; +import static com.android.launcher3.util.Executors.MAIN_EXECUTOR; + import android.os.Looper; import android.util.Log; -import com.android.launcher3.AllAppsList; import com.android.launcher3.AppInfo; import com.android.launcher3.InvariantDeviceProfile; import com.android.launcher3.ItemInfo; import com.android.launcher3.LauncherAppState; import com.android.launcher3.LauncherAppWidgetInfo; import com.android.launcher3.LauncherModel.CallbackTask; -import com.android.launcher3.LauncherModel.Callbacks; import com.android.launcher3.LauncherSettings; -import com.android.launcher3.MainThreadExecutor; import com.android.launcher3.PagedView; import com.android.launcher3.config.FeatureFlags; +import com.android.launcher3.model.BgDataModel.Callbacks; import com.android.launcher3.util.IntArray; import com.android.launcher3.util.IntSet; import com.android.launcher3.util.LooperIdleLock; @@ -65,7 +65,7 @@ public abstract class BaseLoaderResults { public BaseLoaderResults(LauncherAppState app, BgDataModel dataModel, AllAppsList allAppsList, int pageToBindFirst, WeakReference callbacks) { - mUiExecutor = new MainThreadExecutor(); + mUiExecutor = MAIN_EXECUTOR; mApp = app; mBgDataModel = dataModel; mBgAllAppsList = allAppsList; @@ -279,9 +279,8 @@ public abstract class BaseLoaderResults { public void bindAllApps() { // shallow copy - @SuppressWarnings("unchecked") - ArrayList list = (ArrayList) mBgAllAppsList.data.clone(); - executeCallbacksTask(c -> c.bindAllApplications(list), mUiExecutor); + AppInfo[] apps = mBgAllAppsList.copyData(); + executeCallbacksTask(c -> c.bindAllApplications(apps), mUiExecutor); } public abstract void bindWidgets(); diff --git a/src/com/android/launcher3/model/BaseModelUpdateTask.java b/src/com/android/launcher3/model/BaseModelUpdateTask.java index eea3d8c35e..e12633bcd1 100644 --- a/src/com/android/launcher3/model/BaseModelUpdateTask.java +++ b/src/com/android/launcher3/model/BaseModelUpdateTask.java @@ -17,12 +17,12 @@ package com.android.launcher3.model; import android.util.Log; -import com.android.launcher3.AllAppsList; +import com.android.launcher3.AppInfo; import com.android.launcher3.LauncherAppState; import com.android.launcher3.LauncherModel; import com.android.launcher3.LauncherModel.ModelUpdateTask; import com.android.launcher3.LauncherModel.CallbackTask; -import com.android.launcher3.LauncherModel.Callbacks; +import com.android.launcher3.model.BgDataModel.Callbacks; import com.android.launcher3.WorkspaceItemInfo; import com.android.launcher3.util.ComponentKey; import com.android.launcher3.util.ItemInfoMatcher; @@ -30,6 +30,7 @@ import com.android.launcher3.widget.WidgetListRowEntry; import java.util.ArrayList; import java.util.HashMap; +import java.util.List; import java.util.concurrent.Executor; /** @@ -95,12 +96,7 @@ public abstract class BaseModelUpdateTask implements ModelUpdateTask { public void bindUpdatedWorkspaceItems(final ArrayList updatedShortcuts) { if (!updatedShortcuts.isEmpty()) { - scheduleCallbackTask(new CallbackTask() { - @Override - public void execute(Callbacks callbacks) { - callbacks.bindWorkspaceItemsChanged(updatedShortcuts); - } - }); + scheduleCallbackTask(c -> c.bindWorkspaceItemsChanged(updatedShortcuts)); } } @@ -113,23 +109,20 @@ public abstract class BaseModelUpdateTask implements ModelUpdateTask { public void bindUpdatedWidgets(BgDataModel dataModel) { final ArrayList widgets = dataModel.widgetsModel.getWidgetsList(mApp.getContext()); - scheduleCallbackTask(new CallbackTask() { - @Override - public void execute(Callbacks callbacks) { - callbacks.bindAllWidgets(widgets); - } - }); + scheduleCallbackTask(c -> c.bindAllWidgets(widgets)); } public void deleteAndBindComponentsRemoved(final ItemInfoMatcher matcher) { getModelWriter().deleteItemsFromDatabase(matcher); // Call the components-removed callback - scheduleCallbackTask(new CallbackTask() { - @Override - public void execute(Callbacks callbacks) { - callbacks.bindWorkspaceComponentsRemoved(matcher); - } - }); + scheduleCallbackTask(c -> c.bindWorkspaceComponentsRemoved(matcher)); + } + + public void bindApplicationsIfNeeded() { + if (mAllAppsList.getAndResetChangeFlag()) { + AppInfo[] apps = mAllAppsList.copyData(); + scheduleCallbackTask(c -> c.bindAllApplications(apps)); + } } } diff --git a/src/com/android/launcher3/model/BgDataModel.java b/src/com/android/launcher3/model/BgDataModel.java index 8f0cd08a90..0e20270504 100644 --- a/src/com/android/launcher3/model/BgDataModel.java +++ b/src/com/android/launcher3/model/BgDataModel.java @@ -22,11 +22,13 @@ import android.text.TextUtils; import android.util.Log; import android.util.MutableInt; +import com.android.launcher3.AppInfo; import com.android.launcher3.FolderInfo; import com.android.launcher3.InstallShortcutReceiver; import com.android.launcher3.ItemInfo; import com.android.launcher3.LauncherAppWidgetInfo; import com.android.launcher3.LauncherSettings; +import com.android.launcher3.PromiseAppInfo; import com.android.launcher3.WorkspaceItemInfo; import com.android.launcher3.Workspace; import com.android.launcher3.config.FeatureFlags; @@ -40,6 +42,10 @@ import com.android.launcher3.util.ComponentKey; import com.android.launcher3.util.IntArray; import com.android.launcher3.util.IntSet; import com.android.launcher3.util.IntSparseArrayMap; +import com.android.launcher3.util.ItemInfoMatcher; +import com.android.launcher3.util.ViewOnDrawExecutor; +import com.android.launcher3.widget.WidgetListRowEntry; + import com.google.protobuf.nano.MessageNano; import java.io.FileDescriptor; @@ -49,6 +55,7 @@ import java.io.PrintWriter; import java.util.ArrayList; import java.util.Arrays; import java.util.HashMap; +import java.util.HashSet; import java.util.Iterator; import java.util.List; import java.util.Map; @@ -391,4 +398,30 @@ public class BgDataModel { } } } + + public interface Callbacks { + void rebindModel(); + + int getCurrentWorkspaceScreen(); + void clearPendingBinds(); + void startBinding(); + void bindItems(List shortcuts, boolean forceAnimateIcons); + void bindScreens(IntArray orderedScreenIds); + void finishFirstPageBind(ViewOnDrawExecutor executor); + void finishBindingItems(int pageBoundFirst); + void preAddApps(); + void bindAppsAdded(IntArray newScreens, + ArrayList addNotAnimated, ArrayList addAnimated); + void bindPromiseAppProgressUpdated(PromiseAppInfo app); + void bindWorkspaceItemsChanged(ArrayList updated); + void bindWidgetsRestored(ArrayList widgets); + void bindRestoreItemsChange(HashSet updates); + void bindWorkspaceComponentsRemoved(ItemInfoMatcher matcher); + void bindAllWidgets(ArrayList widgets); + void onPageBoundSynchronously(int page); + void executeOnNextDraw(ViewOnDrawExecutor executor); + void bindDeepShortcutMap(HashMap deepShortcutMap); + + void bindAllApplications(AppInfo[] apps); + } } diff --git a/src/com/android/launcher3/model/CacheDataUpdatedTask.java b/src/com/android/launcher3/model/CacheDataUpdatedTask.java index 7852444de4..c1c8be3169 100644 --- a/src/com/android/launcher3/model/CacheDataUpdatedTask.java +++ b/src/com/android/launcher3/model/CacheDataUpdatedTask.java @@ -18,14 +18,10 @@ package com.android.launcher3.model; import android.content.ComponentName; import android.os.UserHandle; -import com.android.launcher3.AllAppsList; -import com.android.launcher3.AppInfo; import com.android.launcher3.WorkspaceItemInfo; import com.android.launcher3.icons.IconCache; import com.android.launcher3.ItemInfo; import com.android.launcher3.LauncherAppState; -import com.android.launcher3.LauncherModel.CallbackTask; -import com.android.launcher3.LauncherModel.Callbacks; import com.android.launcher3.LauncherSettings; import java.util.ArrayList; @@ -53,9 +49,9 @@ public class CacheDataUpdatedTask extends BaseModelUpdateTask { public void execute(LauncherAppState app, BgDataModel dataModel, AllAppsList apps) { IconCache iconCache = app.getIconCache(); - final ArrayList updatedApps = new ArrayList<>(); ArrayList updatedShortcuts = new ArrayList<>(); + synchronized (dataModel) { for (ItemInfo info : dataModel.itemsIdMap) { if (info instanceof WorkspaceItemInfo && mUser.equals(info.user)) { @@ -69,18 +65,10 @@ public class CacheDataUpdatedTask extends BaseModelUpdateTask { } } } - apps.updateIconsAndLabels(mPackages, mUser, updatedApps); + apps.updateIconsAndLabels(mPackages, mUser); } bindUpdatedWorkspaceItems(updatedShortcuts); - - if (!updatedApps.isEmpty()) { - scheduleCallbackTask(new CallbackTask() { - @Override - public void execute(Callbacks callbacks) { - callbacks.bindAppsAddedOrUpdated(updatedApps); - } - }); - } + bindApplicationsIfNeeded(); } public boolean isValidShortcut(WorkspaceItemInfo si) { diff --git a/src/com/android/launcher3/model/FirstScreenBroadcast.java b/src/com/android/launcher3/model/FirstScreenBroadcast.java index 1149b553f6..a0b7177630 100644 --- a/src/com/android/launcher3/model/FirstScreenBroadcast.java +++ b/src/com/android/launcher3/model/FirstScreenBroadcast.java @@ -26,6 +26,7 @@ import com.android.launcher3.ItemInfo; import com.android.launcher3.LauncherAppWidgetInfo; import com.android.launcher3.LauncherSettings; import com.android.launcher3.util.MultiHashMap; +import com.android.launcher3.util.PackageUserKey; import java.util.ArrayList; import java.util.HashMap; @@ -34,6 +35,8 @@ import java.util.List; import java.util.Map; import java.util.Set; +import static android.os.Process.myUserHandle; + /** * Helper class to send broadcasts to package installers that have: * - Items on the first screen @@ -60,7 +63,7 @@ public class FirstScreenBroadcast { private final MultiHashMap mPackagesForInstaller; - public FirstScreenBroadcast(HashMap sessionInfoForPackage) { + public FirstScreenBroadcast(HashMap sessionInfoForPackage) { mPackagesForInstaller = getPackagesForInstaller(sessionInfoForPackage); } @@ -69,11 +72,13 @@ public class FirstScreenBroadcast { * of packages with active sessions for that installer. */ private MultiHashMap getPackagesForInstaller( - HashMap sessionInfoForPackage) { + HashMap sessionInfoForPackage) { MultiHashMap packagesForInstaller = new MultiHashMap<>(); - for (Map.Entry entry : sessionInfoForPackage.entrySet()) { - packagesForInstaller.addToList(entry.getValue().getInstallerPackageName(), - entry.getKey()); + for (Map.Entry entry : sessionInfoForPackage.entrySet()) { + if (myUserHandle().equals(entry.getKey().mUser)) { + packagesForInstaller.addToList(entry.getValue().getInstallerPackageName(), + entry.getKey().mPackageName); + } } return packagesForInstaller; } diff --git a/src/com/android/launcher3/model/GridSizeMigrationTask.java b/src/com/android/launcher3/model/GridSizeMigrationTask.java index faecc067e4..783e908e30 100644 --- a/src/com/android/launcher3/model/GridSizeMigrationTask.java +++ b/src/com/android/launcher3/model/GridSizeMigrationTask.java @@ -34,10 +34,12 @@ import com.android.launcher3.provider.LauncherDbUtils.SQLiteTransaction; import com.android.launcher3.util.GridOccupancy; import com.android.launcher3.util.IntArray; import com.android.launcher3.util.IntSparseArrayMap; +import com.android.launcher3.util.PackageUserKey; import java.util.ArrayList; import java.util.Collections; import java.util.HashSet; +import java.util.function.Consumer; import androidx.annotation.VisibleForTesting; @@ -970,8 +972,9 @@ public class GridSizeMigrationTask { .getInstalledPackages(PackageManager.GET_UNINSTALLED_PACKAGES)) { validPackages.add(info.packageName); } - validPackages.addAll(PackageInstallerCompat.getInstance(context) - .updateAndGetActiveSessionCache().keySet()); + PackageInstallerCompat.getInstance(context) + .updateAndGetActiveSessionCache().keySet() + .forEach(packageUserKey -> validPackages.add(packageUserKey.mPackageName)); return validPackages; } diff --git a/src/com/android/launcher3/model/LoaderTask.java b/src/com/android/launcher3/model/LoaderTask.java index 4b01b5ed17..754c33f0e5 100644 --- a/src/com/android/launcher3/model/LoaderTask.java +++ b/src/com/android/launcher3/model/LoaderTask.java @@ -20,6 +20,8 @@ import static com.android.launcher3.ItemInfoWithIcon.FLAG_DISABLED_LOCKED_USER; import static com.android.launcher3.ItemInfoWithIcon.FLAG_DISABLED_SAFEMODE; import static com.android.launcher3.ItemInfoWithIcon.FLAG_DISABLED_SUSPENDED; import static com.android.launcher3.model.LoaderResults.filterCurrentWorkspaceItems; +import static com.android.launcher3.util.Executors.MODEL_EXECUTOR; +import static com.android.launcher3.util.PackageManagerHelper.isSystemApp; import android.appwidget.AppWidgetProviderInfo; import android.content.ComponentName; @@ -31,7 +33,6 @@ import android.content.pm.LauncherActivityInfo; import android.content.pm.PackageInstaller; import android.content.pm.PackageInstaller.SessionInfo; import android.content.pm.ShortcutInfo; -import android.os.Handler; import android.os.Process; import android.os.UserHandle; import android.text.TextUtils; @@ -39,7 +40,6 @@ import android.util.Log; import android.util.LongSparseArray; import android.util.MutableInt; -import com.android.launcher3.AllAppsList; import com.android.launcher3.AppInfo; import com.android.launcher3.FolderInfo; import com.android.launcher3.InstallShortcutReceiver; @@ -57,21 +57,25 @@ import com.android.launcher3.compat.PackageInstallerCompat; import com.android.launcher3.compat.UserManagerCompat; import com.android.launcher3.config.FeatureFlags; import com.android.launcher3.folder.Folder; -import com.android.launcher3.folder.FolderIconPreviewVerifier; +import com.android.launcher3.folder.FolderGridOrganizer; import com.android.launcher3.icons.ComponentWithLabel; import com.android.launcher3.icons.ComponentWithLabel.ComponentCachingLogic; import com.android.launcher3.icons.IconCache; import com.android.launcher3.icons.LauncherActivityCachingLogic; import com.android.launcher3.icons.LauncherIcons; +import com.android.launcher3.icons.ShortcutCachingLogic; import com.android.launcher3.icons.cache.IconCacheUpdateHandler; import com.android.launcher3.logging.FileLog; import com.android.launcher3.provider.ImportDataTask; +import com.android.launcher3.qsb.QsbContainerView; import com.android.launcher3.shortcuts.DeepShortcutManager; import com.android.launcher3.shortcuts.ShortcutKey; import com.android.launcher3.util.ComponentKey; +import com.android.launcher3.util.IOUtils; import com.android.launcher3.util.LooperIdleLock; import com.android.launcher3.util.MultiHashMap; import com.android.launcher3.util.PackageManagerHelper; +import com.android.launcher3.util.PackageUserKey; import com.android.launcher3.util.TraceHelper; import java.util.ArrayList; @@ -168,7 +172,8 @@ public class LoaderTask implements Runnable { TraceHelper.beginSection(TAG); try (LauncherModel.LoaderTransaction transaction = mApp.getModel().beginLoader(this)) { TraceHelper.partitionSection(TAG, "step 1.1: loading workspace"); - loadWorkspace(); + List allShortcuts = new ArrayList<>(); + loadWorkspace(allShortcuts); verifyNotStopped(); TraceHelper.partitionSection(TAG, "step 1.2: bind workspace workspace"); @@ -187,18 +192,23 @@ public class LoaderTask implements Runnable { TraceHelper.partitionSection(TAG, "step 2.1: loading all apps"); List allActivityList = loadAllApps(); - TraceHelper.partitionSection(TAG, "step 2.2: Binding all apps"); + TraceHelper.partitionSection(TAG, "step 2.2: binding all apps"); verifyNotStopped(); mResults.bindAllApps(); verifyNotStopped(); - TraceHelper.partitionSection(TAG, "step 2.3: Update icon cache"); + TraceHelper.partitionSection(TAG, "step 2.3: save app icons in icon cache"); IconCacheUpdateHandler updateHandler = mIconCache.getUpdateHandler(); setIgnorePackages(updateHandler); updateHandler.updateIcons(allActivityList, LauncherActivityCachingLogic.newInstance(mApp.getContext()), mApp.getModel()::onPackageIconsUpdated); + verifyNotStopped(); + TraceHelper.partitionSection(TAG, "step 2.4: save shortcuts in icon cache"); + updateHandler.updateIcons(allShortcuts, new ShortcutCachingLogic(), + mApp.getModel()::onPackageIconsUpdated); + // Take a break TraceHelper.partitionSection(TAG, "step 2 completed, wait for idle"); waitForIdle(); @@ -206,12 +216,17 @@ public class LoaderTask implements Runnable { // third step TraceHelper.partitionSection(TAG, "step 3.1: loading deep shortcuts"); - loadDeepShortcuts(); + List allDeepShortcuts = loadDeepShortcuts(); verifyNotStopped(); TraceHelper.partitionSection(TAG, "step 3.2: bind deep shortcuts"); mResults.bindDeepShortcuts(); + verifyNotStopped(); + TraceHelper.partitionSection(TAG, "step 3.3: save deep shortcuts in icon cache"); + updateHandler.updateIcons(allDeepShortcuts, + new ShortcutCachingLogic(), (pkgs, user) -> { }); + // Take a break TraceHelper.partitionSection(TAG, "step 3 completed, wait for idle"); waitForIdle(); @@ -226,7 +241,7 @@ public class LoaderTask implements Runnable { mResults.bindWidgets(); verifyNotStopped(); - TraceHelper.partitionSection(TAG, "step 4.3: Update icon cache"); + TraceHelper.partitionSection(TAG, "step 4.3: save widgets in icon cache"); updateHandler.updateIcons(allWidgetsList, new ComponentCachingLogic(mApp.getContext()), mApp.getModel()::onWidgetLabelsUpdated); @@ -247,7 +262,7 @@ public class LoaderTask implements Runnable { this.notify(); } - private void loadWorkspace() { + private void loadWorkspace(List allDeepShortcuts) { final Context context = mApp.getContext(); final ContentResolver contentResolver = context.getContentResolver(); final PackageManagerHelper pmHelper = new PackageManagerHelper(context); @@ -281,8 +296,9 @@ public class LoaderTask implements Runnable { synchronized (mBgDataModel) { mBgDataModel.clear(); - final HashMap installingPkgs = + final HashMap installingPkgs = mPackageInstaller.updateAndGetActiveSessionCache(); + final PackageUserKey tempPackageKey = new PackageUserKey(null, null); mFirstScreenBroadcast = new FirstScreenBroadcast(installingPkgs); Map shortcutKeyToPinnedShortcuts = new HashMap<>(); @@ -317,9 +333,9 @@ public class LoaderTask implements Runnable { // We can only query for shortcuts when the user is unlocked. if (userUnlocked) { - List pinnedShortcuts = + DeepShortcutManager.QueryResult pinnedShortcuts = mShortcutManager.queryForPinnedShortcuts(null, user); - if (mShortcutManager.wasLastCallSuccess()) { + if (pinnedShortcuts.wasSuccess()) { for (ShortcutInfo shortcut : pinnedShortcuts) { shortcutKeyToPinnedShortcuts.put(ShortcutKey.fromInfo(shortcut), shortcut); @@ -384,7 +400,9 @@ public class LoaderTask implements Runnable { boolean validTarget = TextUtils.isEmpty(targetPkg) || mLauncherApps.isPackageEnabledForProfile(targetPkg, c.user); - if (cn != null && validTarget) { + // If it's a deep shortcut, we'll use pinned shortcuts to restore it + if (cn != null && validTarget && c.itemType + != LauncherSettings.Favorites.ITEM_TYPE_DEEP_SHORTCUT) { // If the apk is present and the shortcut points to a specific // component. @@ -419,9 +437,10 @@ public class LoaderTask implements Runnable { // installed later. FileLog.d(TAG, "package not yet restored: " + targetPkg); + tempPackageKey.update(targetPkg, c.user); if (c.hasRestoreFlag(WorkspaceItemInfo.FLAG_RESTORE_STARTED)) { // Restore has started once. - } else if (installingPkgs.containsKey(targetPkg)) { + } else if (installingPkgs.containsKey(tempPackageKey)) { // App restore has started. Update the flag c.restoreFlag |= WorkspaceItemInfo.FLAG_RESTORE_STARTED; c.updater().put(LauncherSettings.Favorites.RESTORED, @@ -496,6 +515,7 @@ public class LoaderTask implements Runnable { info.runtimeStatusFlags |= FLAG_DISABLED_SUSPENDED; } intent = info.intent; + allDeepShortcuts.add(pinnedShortcut); } else { // Create a shortcut info in disabled mode for now. info = c.loadSimpleWorkspaceItem(); @@ -531,12 +551,13 @@ public class LoaderTask implements Runnable { info.spanX = 1; info.spanY = 1; info.runtimeStatusFlags |= disabledState; - if (isSafeMode && !Utilities.isSystemApp(context, intent)) { + if (isSafeMode && !isSystemApp(context, intent)) { info.runtimeStatusFlags |= FLAG_DISABLED_SAFEMODE; } if (c.restoreFlag != 0 && !TextUtils.isEmpty(targetPkg)) { - SessionInfo si = installingPkgs.get(targetPkg); + tempPackageKey.update(targetPkg, c.user); + SessionInfo si = installingPkgs.get(tempPackageKey); if (si == null) { info.status &= ~WorkspaceItemInfo.FLAG_INSTALL_SESSION_ACTIVE; } else { @@ -567,7 +588,7 @@ public class LoaderTask implements Runnable { break; case LauncherSettings.Favorites.ITEM_TYPE_APPWIDGET: - if (FeatureFlags.GO_DISABLE_WIDGETS) { + if (WidgetsModel.GO_DISABLE_WIDGETS) { c.markDeleted("Only legacy shortcuts can have null package"); continue; } @@ -579,10 +600,19 @@ public class LoaderTask implements Runnable { int appWidgetId = c.getInt(appWidgetIdIndex); String savedProvider = c.getString(appWidgetProviderIndex); + final ComponentName component; - final ComponentName component = - ComponentName.unflattenFromString(savedProvider); - + boolean isSearchWidget = (c.getInt(optionsIndex) + & LauncherAppWidgetInfo.OPTION_SEARCH_WIDGET) != 0; + if (isSearchWidget) { + component = QsbContainerView.getSearchComponentName(context); + if (component == null) { + c.markDeleted("Discarding SearchWidget without packagename "); + continue; + } + } else { + component = ComponentName.unflattenFromString(savedProvider); + } final boolean isIdValid = !c.hasRestoreFlag( LauncherAppWidgetInfo.FLAG_ID_NOT_VALID); final boolean wasProviderReady = !c.hasRestoreFlag( @@ -592,9 +622,7 @@ public class LoaderTask implements Runnable { widgetProvidersMap = mAppWidgetManager.getAllProvidersMap(); } final AppWidgetProviderInfo provider = widgetProvidersMap.get( - new ComponentKey( - ComponentName.unflattenFromString(savedProvider), - c.user)); + new ComponentKey(component, c.user)); final boolean isProviderReady = isValidProvider(provider); if (!isSafeMode && !customWidget && @@ -630,8 +658,10 @@ public class LoaderTask implements Runnable { appWidgetInfo = new LauncherAppWidgetInfo(appWidgetId, component); appWidgetInfo.restoreStatus = c.restoreFlag; + + tempPackageKey.update(component.getPackageName(), c.user); SessionInfo si = - installingPkgs.get(component.getPackageName()); + installingPkgs.get(tempPackageKey); Integer installProgress = si == null ? null : (int) (si.getProgress() * 100); @@ -658,6 +688,7 @@ public class LoaderTask implements Runnable { c.applyCommonProperties(appWidgetInfo); appWidgetInfo.spanX = c.getInt(spanXIndex); appWidgetInfo.spanY = c.getInt(spanYIndex); + appWidgetInfo.options = c.getInt(optionsIndex); appWidgetInfo.user = c.user; if (appWidgetInfo.spanX <= 0 || appWidgetInfo.spanY <= 0) { @@ -703,7 +734,7 @@ public class LoaderTask implements Runnable { } } } finally { - Utilities.closeSilently(c); + IOUtils.closeSilently(c); } // Break early if we've stopped loading @@ -743,8 +774,8 @@ public class LoaderTask implements Runnable { } // Sort the folder items, update ranks, and make sure all preview items are high res. - FolderIconPreviewVerifier verifier = - new FolderIconPreviewVerifier(mApp.getInvariantDeviceProfile()); + FolderGridOrganizer verifier = + new FolderGridOrganizer(mApp.getInvariantDeviceProfile()); for (FolderInfo folder : mBgDataModel.folders) { Collections.sort(folder.contents, Folder.ITEM_POS_COMPARATOR); verifier.setFolderInfo(folder); @@ -771,7 +802,7 @@ public class LoaderTask implements Runnable { new SdCardAvailableReceiver(mApp, pendingPackages), new IntentFilter(Intent.ACTION_BOOT_COMPLETED), null, - new Handler(LauncherModel.getWorkerLooper())); + MODEL_EXECUTOR.getHandler()); } } } @@ -820,7 +851,7 @@ public class LoaderTask implements Runnable { allActivityList.addAll(apps); } - if (FeatureFlags.LAUNCHER3_PROMISE_APPS_IN_ALL_APPS) { + if (FeatureFlags.PROMISE_APPS_IN_ALL_APPS.get()) { // get all active sessions and add them to the all apps list for (PackageInstaller.SessionInfo info : mPackageInstaller.getAllVerifiedSessions()) { @@ -829,11 +860,12 @@ public class LoaderTask implements Runnable { } } - mBgAllAppsList.added = new ArrayList<>(); + mBgAllAppsList.getAndResetChangeFlag(); return allActivityList; } - private void loadDeepShortcuts() { + private List loadDeepShortcuts() { + List allShortcuts = new ArrayList<>(); mBgDataModel.deepShortcutMap.clear(); mBgDataModel.hasShortcutHostPermission = mShortcutManager.hasHostPermission(); if (mBgDataModel.hasShortcutHostPermission) { @@ -841,10 +873,12 @@ public class LoaderTask implements Runnable { if (mUserManager.isUserUnlocked(user)) { List shortcuts = mShortcutManager.queryForAllShortcuts(user); + allShortcuts.addAll(shortcuts); mBgDataModel.updateDeepShortcutCounts(null, user, shortcuts); } } } + return allShortcuts; } public static boolean isValidProvider(AppWidgetProviderInfo provider) { diff --git a/src/com/android/launcher3/model/ModelPreload.java b/src/com/android/launcher3/model/ModelPreload.java index b353810ef5..2bd6cd4db6 100644 --- a/src/com/android/launcher3/model/ModelPreload.java +++ b/src/com/android/launcher3/model/ModelPreload.java @@ -18,7 +18,6 @@ package com.android.launcher3.model; import android.content.Context; import android.util.Log; -import com.android.launcher3.AllAppsList; import com.android.launcher3.LauncherAppState; import com.android.launcher3.LauncherModel; import com.android.launcher3.LauncherModel.ModelUpdateTask; diff --git a/src/com/android/launcher3/model/ModelWriter.java b/src/com/android/launcher3/model/ModelWriter.java index 4ce2f4ba99..c69ace946c 100644 --- a/src/com/android/launcher3/model/ModelWriter.java +++ b/src/com/android/launcher3/model/ModelWriter.java @@ -16,6 +16,8 @@ package com.android.launcher3.model; +import static com.android.launcher3.util.Executors.MODEL_EXECUTOR; + import android.content.ContentProviderOperation; import android.content.ContentResolver; import android.content.ContentValues; @@ -31,23 +33,25 @@ import com.android.launcher3.LauncherAppState; import com.android.launcher3.LauncherAppWidgetHost; import com.android.launcher3.LauncherAppWidgetInfo; import com.android.launcher3.LauncherModel; -import com.android.launcher3.LauncherModel.Callbacks; import com.android.launcher3.LauncherProvider; import com.android.launcher3.LauncherSettings; import com.android.launcher3.LauncherSettings.Favorites; import com.android.launcher3.LauncherSettings.Settings; -import com.android.launcher3.WorkspaceItemInfo; import com.android.launcher3.Utilities; +import com.android.launcher3.WorkspaceItemInfo; import com.android.launcher3.config.FeatureFlags; +import com.android.launcher3.logging.FileLog; +import com.android.launcher3.model.BgDataModel.Callbacks; import com.android.launcher3.util.ContentWriter; import com.android.launcher3.util.ItemInfoMatcher; -import com.android.launcher3.util.LooperExecutor; import java.util.ArrayList; import java.util.Arrays; +import java.util.Collection; import java.util.List; import java.util.concurrent.Executor; import java.util.function.Supplier; +import java.util.stream.Collectors; /** * Class for handling model updates. @@ -61,7 +65,6 @@ public class ModelWriter { private final BgDataModel mBgDataModel; private final Handler mUiHandler; - private final Executor mWorkerExecutor; private final boolean mHasVerticalHotseat; private final boolean mVerifyChanges; @@ -74,7 +77,6 @@ public class ModelWriter { mContext = context; mModel = model; mBgDataModel = dataModel; - mWorkerExecutor = new LooperExecutor(LauncherModel.getWorkerLooper()); mHasVerticalHotseat = hasVerticalHotseat; mVerifyChanges = verifyChanges; mUiHandler = new Handler(Looper.getMainLooper()); @@ -194,7 +196,7 @@ public class ModelWriter { item.spanX = spanX; item.spanY = spanY; - mWorkerExecutor.execute(new UpdateItemRunnable(item, () -> + ((Executor) MODEL_EXECUTOR).execute(new UpdateItemRunnable(item, () -> new ContentWriter(mContext) .put(Favorites.CONTAINER, item.container) .put(Favorites.CELLX, item.cellX) @@ -209,7 +211,7 @@ public class ModelWriter { * Update an item to the database in a specified container. */ public void updateItemInDatabase(ItemInfo item) { - mWorkerExecutor.execute(new UpdateItemRunnable(item, () -> { + ((Executor) MODEL_EXECUTOR).execute(new UpdateItemRunnable(item, () -> { ContentWriter writer = new ContentWriter(mContext); item.onAddToDatabase(writer); return writer; @@ -229,7 +231,7 @@ public class ModelWriter { ModelVerifier verifier = new ModelVerifier(); final StackTraceElement[] stackTrace = new Throwable().getStackTrace(); - mWorkerExecutor.execute(() -> { + ((Executor) MODEL_EXECUTOR).execute(() -> { // Write the item on background thread, as some properties might have been updated in // the background. final ContentWriter writer = new ContentWriter(mContext); @@ -263,9 +265,12 @@ public class ModelWriter { /** * Removes the specified items from the database */ - public void deleteItemsFromDatabase(final Iterable items) { + public void deleteItemsFromDatabase(final Collection items) { ModelVerifier verifier = new ModelVerifier(); - + FileLog.d(TAG, "removing items from db " + items.stream().map( + (item) -> item.getTargetComponent() == null ? "" + : item.getTargetComponent().getPackageName()).collect( + Collectors.joining(",")), new Exception()); enqueueDeleteRunnable(() -> { for (ItemInfo item : items) { final Uri uri = Favorites.getContentUri(item.id); @@ -333,14 +338,14 @@ public class ModelWriter { if (mPreparingToUndo) { mDeleteRunnables.add(r); } else { - mWorkerExecutor.execute(r); + ((Executor) MODEL_EXECUTOR).execute(r); } } public void commitDelete() { mPreparingToUndo = false; for (Runnable runnable : mDeleteRunnables) { - mWorkerExecutor.execute(runnable); + ((Executor) MODEL_EXECUTOR).execute(runnable); } mDeleteRunnables.clear(); } diff --git a/src/com/android/launcher3/model/PackageInstallStateChangedTask.java b/src/com/android/launcher3/model/PackageInstallStateChangedTask.java index 9fcab38870..802cbc7c5e 100644 --- a/src/com/android/launcher3/model/PackageInstallStateChangedTask.java +++ b/src/com/android/launcher3/model/PackageInstallStateChangedTask.java @@ -19,20 +19,18 @@ import android.content.ComponentName; import android.content.pm.ApplicationInfo; import android.content.pm.PackageManager; -import com.android.launcher3.AllAppsList; import com.android.launcher3.AppInfo; import com.android.launcher3.ItemInfo; import com.android.launcher3.LauncherAppState; import com.android.launcher3.LauncherAppWidgetInfo; import com.android.launcher3.LauncherModel.CallbackTask; -import com.android.launcher3.LauncherModel.Callbacks; +import com.android.launcher3.model.BgDataModel.Callbacks; import com.android.launcher3.PromiseAppInfo; import com.android.launcher3.WorkspaceItemInfo; import com.android.launcher3.compat.PackageInstallerCompat; import com.android.launcher3.compat.PackageInstallerCompat.PackageInstallInfo; import com.android.launcher3.util.InstantAppResolver; -import java.util.ArrayList; import java.util.HashSet; /** @@ -65,41 +63,11 @@ public class PackageInstallStateChangedTask extends BaseModelUpdateTask { } synchronized (apps) { - PromiseAppInfo updated = null; - final ArrayList removed = new ArrayList<>(); - for (int i=0; i < apps.size(); i++) { - final AppInfo appInfo = apps.get(i); - final ComponentName tgtComp = appInfo.getTargetComponent(); - if (tgtComp != null && tgtComp.getPackageName().equals(mInstallInfo.packageName)) { - if (appInfo instanceof PromiseAppInfo) { - final PromiseAppInfo promiseAppInfo = (PromiseAppInfo) appInfo; - if (mInstallInfo.state == PackageInstallerCompat.STATUS_INSTALLING) { - promiseAppInfo.level = mInstallInfo.progress; - updated = promiseAppInfo; - } else if (mInstallInfo.state == PackageInstallerCompat.STATUS_FAILED) { - apps.removePromiseApp(appInfo); - removed.add(appInfo); - } - } - } - } + PromiseAppInfo updated = apps.updatePromiseInstallInfo(mInstallInfo); if (updated != null) { - final PromiseAppInfo updatedPromiseApp = updated; - scheduleCallbackTask(new CallbackTask() { - @Override - public void execute(Callbacks callbacks) { - callbacks.bindPromiseAppProgressUpdated(updatedPromiseApp); - } - }); - } - if (!removed.isEmpty()) { - scheduleCallbackTask(new CallbackTask() { - @Override - public void execute(Callbacks callbacks) { - callbacks.bindAppInfosRemoved(removed); - } - }); + scheduleCallbackTask(c -> c.bindPromiseAppProgressUpdated(updated)); } + bindApplicationsIfNeeded(); } synchronized (dataModel) { diff --git a/src/com/android/launcher3/model/PackageItemInfo.java b/src/com/android/launcher3/model/PackageItemInfo.java index baeaa94923..3ef48cd8fd 100644 --- a/src/com/android/launcher3/model/PackageItemInfo.java +++ b/src/com/android/launcher3/model/PackageItemInfo.java @@ -17,6 +17,7 @@ package com.android.launcher3.model; import com.android.launcher3.ItemInfoWithIcon; +import com.android.launcher3.LauncherSettings; /** * Represents a {@link Package} in the widget tray section. @@ -30,10 +31,21 @@ public class PackageItemInfo extends ItemInfoWithIcon { public PackageItemInfo(String packageName) { this.packageName = packageName; + this.itemType = LauncherSettings.Favorites.ITEM_TYPE_NON_ACTIONABLE; + } + + public PackageItemInfo(PackageItemInfo copy) { + this.packageName = copy.packageName; + this.itemType = LauncherSettings.Favorites.ITEM_TYPE_NON_ACTIONABLE; } @Override protected String dumpProperties() { return super.dumpProperties() + " packageName=" + packageName; } + + @Override + public PackageItemInfo clone() { + return new PackageItemInfo(this); + } } diff --git a/src/com/android/launcher3/model/PackageUpdatedTask.java b/src/com/android/launcher3/model/PackageUpdatedTask.java index 4428c8e6df..74dd17005a 100644 --- a/src/com/android/launcher3/model/PackageUpdatedTask.java +++ b/src/com/android/launcher3/model/PackageUpdatedTask.java @@ -15,6 +15,8 @@ */ package com.android.launcher3.model; +import static com.android.launcher3.WorkspaceItemInfo.FLAG_AUTOINSTALL_ICON; + import android.content.ComponentName; import android.content.Context; import android.content.Intent; @@ -23,23 +25,19 @@ import android.os.Process; import android.os.UserHandle; import android.util.Log; -import com.android.launcher3.AllAppsList; -import com.android.launcher3.AppInfo; -import com.android.launcher3.WorkspaceItemInfo; -import com.android.launcher3.icons.IconCache; import com.android.launcher3.InstallShortcutReceiver; import com.android.launcher3.ItemInfo; import com.android.launcher3.LauncherAppState; import com.android.launcher3.LauncherAppWidgetInfo; -import com.android.launcher3.LauncherModel.CallbackTask; -import com.android.launcher3.LauncherModel.Callbacks; import com.android.launcher3.LauncherSettings.Favorites; import com.android.launcher3.SessionCommitReceiver; import com.android.launcher3.Utilities; +import com.android.launcher3.WorkspaceItemInfo; import com.android.launcher3.compat.LauncherAppsCompat; import com.android.launcher3.compat.UserManagerCompat; import com.android.launcher3.config.FeatureFlags; import com.android.launcher3.icons.BitmapInfo; +import com.android.launcher3.icons.IconCache; import com.android.launcher3.icons.LauncherIcons; import com.android.launcher3.logging.FileLog; import com.android.launcher3.shortcuts.DeepShortcutManager; @@ -49,6 +47,7 @@ import com.android.launcher3.util.IntSparseArrayMap; import com.android.launcher3.util.ItemInfoMatcher; import com.android.launcher3.util.PackageManagerHelper; import com.android.launcher3.util.PackageUserKey; +import com.android.launcher3.util.SafeCloseable; import java.util.ArrayList; import java.util.Arrays; @@ -56,8 +55,6 @@ import java.util.Collections; import java.util.HashSet; import java.util.List; -import static com.android.launcher3.WorkspaceItemInfo.FLAG_AUTOINSTALL_ICON; - /** * Handles updates due to changes in package manager (app installed/updated/removed) * or when a user availability changes. @@ -100,12 +97,14 @@ public class PackageUpdatedTask extends BaseModelUpdateTask { FlagOp flagOp = FlagOp.NO_OP; final HashSet packageSet = new HashSet<>(Arrays.asList(packages)); ItemInfoMatcher matcher = ItemInfoMatcher.ofPackages(packageSet, mUser); + final HashSet removedComponents = new HashSet<>(); + switch (mOp) { case OP_ADD: { for (int i = 0; i < N; i++) { if (DEBUG) Log.d(TAG, "mAllAppsList.addPackage " + packages[i]); iconCache.updateIconsForPkg(packages[i], mUser); - if (FeatureFlags.LAUNCHER3_PROMISE_APPS_IN_ALL_APPS) { + if (FeatureFlags.PROMISE_APPS_IN_ALL_APPS.get()) { appsList.removePackage(packages[i], mUser); } appsList.addPackage(context, packages[i], mUser); @@ -119,17 +118,21 @@ public class PackageUpdatedTask extends BaseModelUpdateTask { break; } case OP_UPDATE: - for (int i = 0; i < N; i++) { - if (DEBUG) Log.d(TAG, "mAllAppsList.updatePackage " + packages[i]); - iconCache.updateIconsForPkg(packages[i], mUser); - appsList.updatePackage(context, packages[i], mUser); - app.getWidgetCache().removePackage(packages[i], mUser); + try (SafeCloseable t = + appsList.trackRemoves(a -> removedComponents.add(a.componentName))) { + for (int i = 0; i < N; i++) { + if (DEBUG) Log.d(TAG, "mAllAppsList.updatePackage " + packages[i]); + iconCache.updateIconsForPkg(packages[i], mUser); + appsList.updatePackage(context, packages[i], mUser); + app.getWidgetCache().removePackage(packages[i], mUser); + } } // Since package was just updated, the target must be available now. flagOp = FlagOp.removeFlag(WorkspaceItemInfo.FLAG_DISABLED_NOT_AVAILABLE); break; case OP_REMOVE: { for (int i = 0; i < N; i++) { + FileLog.d(TAG, "Removing app icon" + packages[i]); iconCache.removeIconsForPkg(packages[i], mUser); } // Fall through @@ -160,23 +163,7 @@ public class PackageUpdatedTask extends BaseModelUpdateTask { break; } - final ArrayList addedOrModified = new ArrayList<>(); - addedOrModified.addAll(appsList.added); - appsList.added.clear(); - addedOrModified.addAll(appsList.modified); - appsList.modified.clear(); - if (!addedOrModified.isEmpty()) { - scheduleCallbackTask((callbacks) -> callbacks.bindAppsAddedOrUpdated(addedOrModified)); - } - - final ArrayList removedApps = new ArrayList<>(appsList.removed); - appsList.removed.clear(); - final HashSet removedComponents = new HashSet<>(); - if (mOp == OP_UPDATE) { - for (AppInfo ai : removedApps) { - removedComponents.add(ai.componentName); - } - } + bindApplicationsIfNeeded(); final IntSparseArrayMap removedShortcuts = new IntSparseArrayMap<>(); @@ -302,12 +289,7 @@ public class PackageUpdatedTask extends BaseModelUpdateTask { } if (!widgets.isEmpty()) { - scheduleCallbackTask(new CallbackTask() { - @Override - public void execute(Callbacks callbacks) { - callbacks.bindWidgetsRestored(widgets); - } - }); + scheduleCallbackTask(c -> c.bindWidgetsRestored(widgets)); } } @@ -338,16 +320,6 @@ public class PackageUpdatedTask extends BaseModelUpdateTask { InstallShortcutReceiver.removeFromInstallQueue(context, removedPackages, mUser); } - if (!removedApps.isEmpty()) { - // Remove corresponding apps from All-Apps - scheduleCallbackTask(new CallbackTask() { - @Override - public void execute(Callbacks callbacks) { - callbacks.bindAppInfosRemoved(removedApps); - } - }); - } - if (Utilities.ATLEAST_OREO && mOp == OP_ADD) { // Load widgets for the new package. Changes due to app updates are handled through // AppWidgetHost events, this is just to initialize the long-press options. diff --git a/src/com/android/launcher3/model/ShortcutsChangedTask.java b/src/com/android/launcher3/model/ShortcutsChangedTask.java index 8528228f2d..6c358b1709 100644 --- a/src/com/android/launcher3/model/ShortcutsChangedTask.java +++ b/src/com/android/launcher3/model/ShortcutsChangedTask.java @@ -19,7 +19,6 @@ import android.content.Context; import android.content.pm.ShortcutInfo; import android.os.UserHandle; -import com.android.launcher3.AllAppsList; import com.android.launcher3.ItemInfo; import com.android.launcher3.LauncherAppState; import com.android.launcher3.LauncherSettings; @@ -65,7 +64,7 @@ public class ShortcutsChangedTask extends BaseModelUpdateTask { for (ItemInfo itemInfo : dataModel.itemsIdMap) { if (itemInfo.itemType == LauncherSettings.Favorites.ITEM_TYPE_DEEP_SHORTCUT) { WorkspaceItemInfo si = (WorkspaceItemInfo) itemInfo; - if (si.getIntent().getPackage().equals(mPackageName) && si.user.equals(mUser)) { + if (mPackageName.equals(si.getIntent().getPackage()) && si.user.equals(mUser)) { keyToShortcutInfo.addToList(ShortcutKey.fromItemInfo(si), si); allIds.add(si.getDeepShortcutId()); } diff --git a/src/com/android/launcher3/model/UserLockStateChangedTask.java b/src/com/android/launcher3/model/UserLockStateChangedTask.java index 2cb256e092..4b773d7204 100644 --- a/src/com/android/launcher3/model/UserLockStateChangedTask.java +++ b/src/com/android/launcher3/model/UserLockStateChangedTask.java @@ -21,7 +21,6 @@ import android.content.Context; import android.content.pm.ShortcutInfo; import android.os.UserHandle; -import com.android.launcher3.AllAppsList; import com.android.launcher3.ItemInfo; import com.android.launcher3.LauncherAppState; import com.android.launcher3.LauncherSettings; @@ -37,7 +36,6 @@ import java.util.ArrayList; import java.util.HashMap; import java.util.HashSet; import java.util.Iterator; -import java.util.List; /** * Task to handle changing of lock state of the user @@ -58,9 +56,9 @@ public class UserLockStateChangedTask extends BaseModelUpdateTask { HashMap pinnedShortcuts = new HashMap<>(); if (isUserUnlocked) { - List shortcuts = + DeepShortcutManager.QueryResult shortcuts = deepShortcutManager.queryForPinnedShortcuts(null, mUser); - if (deepShortcutManager.wasLastCallSuccess()) { + if (shortcuts.wasSuccess()) { for (ShortcutInfo shortcut : shortcuts) { pinnedShortcuts.put(ShortcutKey.fromInfo(shortcut), shortcut); } diff --git a/src/com/android/launcher3/notification/NotificationListener.java b/src/com/android/launcher3/notification/NotificationListener.java index e57a051f88..10378ee437 100644 --- a/src/com/android/launcher3/notification/NotificationListener.java +++ b/src/com/android/launcher3/notification/NotificationListener.java @@ -16,6 +16,7 @@ package com.android.launcher3.notification; +import static com.android.launcher3.util.Executors.MODEL_EXECUTOR; import static com.android.launcher3.util.SecureSettingsObserver.newNotificationSettingsObserver; import android.annotation.TargetApi; @@ -31,7 +32,8 @@ import android.text.TextUtils; import android.util.Log; import android.util.Pair; -import com.android.launcher3.LauncherModel; +import androidx.annotation.Nullable; + import com.android.launcher3.util.IntSet; import com.android.launcher3.util.PackageUserKey; import com.android.launcher3.util.SecureSettingsObserver; @@ -43,8 +45,6 @@ import java.util.HashMap; import java.util.List; import java.util.Map; -import androidx.annotation.Nullable; - /** * A {@link NotificationListenerService} that sends updates to its * {@link NotificationsChangedListener} when notifications are posted or canceled, @@ -141,7 +141,7 @@ public class NotificationListener extends NotificationListenerService { public NotificationListener() { super(); - mWorkerHandler = new Handler(LauncherModel.getWorkerLooper(), mWorkerCallback); + mWorkerHandler = new Handler(MODEL_EXECUTOR.getLooper(), mWorkerCallback); mUiHandler = new Handler(Looper.getMainLooper(), mUiCallback); sNotificationListenerInstance = this; } diff --git a/src/com/android/launcher3/popup/PopupContainerWithArrow.java b/src/com/android/launcher3/popup/PopupContainerWithArrow.java index 15fb4cea6f..c2aabca2cb 100644 --- a/src/com/android/launcher3/popup/PopupContainerWithArrow.java +++ b/src/com/android/launcher3/popup/PopupContainerWithArrow.java @@ -24,6 +24,7 @@ import static com.android.launcher3.popup.PopupPopulator.MAX_SHORTCUTS_IF_NOTIFI import static com.android.launcher3.userevent.nano.LauncherLogProto.ContainerType; import static com.android.launcher3.userevent.nano.LauncherLogProto.ItemType; import static com.android.launcher3.userevent.nano.LauncherLogProto.Target; +import static com.android.launcher3.util.Executors.MODEL_EXECUTOR; import android.animation.AnimatorSet; import android.animation.LayoutTransition; @@ -50,7 +51,6 @@ import com.android.launcher3.DropTarget.DragObject; import com.android.launcher3.ItemInfo; import com.android.launcher3.ItemInfoWithIcon; import com.android.launcher3.Launcher; -import com.android.launcher3.LauncherModel; import com.android.launcher3.R; import com.android.launcher3.accessibility.LauncherAccessibilityDelegate; import com.android.launcher3.accessibility.ShortcutMenuAccessibilityDelegate; @@ -166,7 +166,10 @@ public class PopupContainerWithArrow extends ArrowPopup implements DragSource, } public OnClickListener getItemClickListener() { - return ItemClickHandler.INSTANCE; + return (view) -> { + ItemClickHandler.INSTANCE.onClick(view); + close(true); + }; } @Override @@ -303,8 +306,7 @@ public class PopupContainerWithArrow extends ArrowPopup implements DragSource, setLayoutTransition(new LayoutTransition()); // Load the shortcuts on a background thread and update the container as it animates. - final Looper workerLooper = LauncherModel.getWorkerLooper(); - new Handler(workerLooper).postAtFrontOfQueue(PopupPopulator.createUpdateRunnable( + MODEL_EXECUTOR.getHandler().postAtFrontOfQueue(PopupPopulator.createUpdateRunnable( mLauncher, originalItemInfo, new Handler(Looper.getMainLooper()), this, mShortcuts, notificationKeys)); } @@ -565,9 +567,9 @@ public class PopupContainerWithArrow extends ArrowPopup implements DragSource, @Override protected void closeComplete() { - super.closeComplete(); mOriginalIcon.setTextVisibility(mOriginalIcon.shouldTextBeVisible()); mOriginalIcon.setForceHideDot(false); + super.closeComplete(); } @Override diff --git a/src/com/android/launcher3/popup/SystemShortcut.java b/src/com/android/launcher3/popup/SystemShortcut.java index 78bd81b464..a87b7b8971 100644 --- a/src/com/android/launcher3/popup/SystemShortcut.java +++ b/src/com/android/launcher3/popup/SystemShortcut.java @@ -1,14 +1,11 @@ package com.android.launcher3.popup; -import static com.android.launcher3.userevent.nano.LauncherLogProto.Action; -import static com.android.launcher3.userevent.nano.LauncherLogProto.ControlType; import android.app.ActivityOptions; import android.content.Context; import android.content.Intent; import android.graphics.Rect; import android.graphics.drawable.Icon; -import android.os.Bundle; import android.os.Handler; import android.os.Looper; import android.view.View; @@ -20,23 +17,30 @@ import com.android.launcher3.AbstractFloatingView; import com.android.launcher3.BaseDraggingActivity; import com.android.launcher3.ItemInfo; import com.android.launcher3.Launcher; +import com.android.launcher3.LauncherSettings; import com.android.launcher3.R; import com.android.launcher3.WorkspaceItemInfo; +import com.android.launcher3.config.FeatureFlags; +import com.android.launcher3.model.AppLaunchTracker; import com.android.launcher3.model.WidgetItem; +import com.android.launcher3.userevent.nano.LauncherLogProto.Action; +import com.android.launcher3.userevent.nano.LauncherLogProto.ContainerType; +import com.android.launcher3.userevent.nano.LauncherLogProto.ControlType; import com.android.launcher3.util.InstantAppResolver; import com.android.launcher3.util.PackageManagerHelper; import com.android.launcher3.util.PackageUserKey; import com.android.launcher3.widget.WidgetsBottomSheet; import java.util.List; - /** * Represents a system shortcut for a given app. The shortcut should have a label and icon, and an * onClickListener that depends on the item that the shortcut services. * * Example system shortcuts, defined as inner classes, include Widgets and AppInfo. + * @param */ -public abstract class SystemShortcut extends ItemInfo { +public abstract class SystemShortcut + extends ItemInfo { private final int mIconResId; private final int mLabelResId; private final Icon mIcon; @@ -202,6 +206,27 @@ public abstract class SystemShortcut extends Ite } } + public static class DismissPrediction extends SystemShortcut { + public DismissPrediction() { + super(R.drawable.ic_remove_no_shadow, R.string.dismiss_prediction_label); + } + + @Override + public View.OnClickListener getOnClickListener(Launcher activity, ItemInfo itemInfo) { + if (!FeatureFlags.ENABLE_PREDICTION_DISMISS.get()) return null; + if (itemInfo.container != LauncherSettings.Favorites.CONTAINER_PREDICTION) return null; + return (view) -> { + PopupContainerWithArrow.closeAllOpenViews(activity); + activity.getUserEventDispatcher().logActionOnControl(Action.Touch.TAP, + ControlType.DISMISS_PREDICTION, ContainerType.DEEPSHORTCUTS); + AppLaunchTracker.INSTANCE.get(view.getContext()) + .onDismissApp(itemInfo.getTargetComponent(), + itemInfo.user, + AppLaunchTracker.CONTAINER_PREDICTIONS); + }; + } + } + protected static void dismissTaskMenuView(BaseDraggingActivity activity) { AbstractFloatingView.closeOpenViews(activity, true, AbstractFloatingView.TYPE_ALL & ~AbstractFloatingView.TYPE_REBIND_SAFE); diff --git a/src/com/android/launcher3/popup/SystemShortcutFactory.java b/src/com/android/launcher3/popup/SystemShortcutFactory.java index 37a209289e..dfcc2f8224 100644 --- a/src/com/android/launcher3/popup/SystemShortcutFactory.java +++ b/src/com/android/launcher3/popup/SystemShortcutFactory.java @@ -39,7 +39,9 @@ public class SystemShortcutFactory implements ResourceBasedOverride { @SuppressWarnings("unused") public SystemShortcutFactory() { this(new SystemShortcut.AppInfo(), - new SystemShortcut.Widgets(), new SystemShortcut.Install()); + new SystemShortcut.Widgets(), + new SystemShortcut.Install(), + new SystemShortcut.DismissPrediction()); } protected SystemShortcutFactory(SystemShortcut... shortcuts) { @@ -53,6 +55,7 @@ public class SystemShortcutFactory implements ResourceBasedOverride { systemShortcuts.add(systemShortcut); } } + return systemShortcuts; } } diff --git a/src/com/android/launcher3/provider/ImportDataTask.java b/src/com/android/launcher3/provider/ImportDataTask.java index 7b62f53fe6..970a03e802 100644 --- a/src/com/android/launcher3/provider/ImportDataTask.java +++ b/src/com/android/launcher3/provider/ImportDataTask.java @@ -42,7 +42,6 @@ import com.android.launcher3.LauncherProvider; import com.android.launcher3.LauncherSettings; import com.android.launcher3.LauncherSettings.Favorites; import com.android.launcher3.LauncherSettings.Settings; -import com.android.launcher3.Utilities; import com.android.launcher3.Workspace; import com.android.launcher3.compat.UserManagerCompat; import com.android.launcher3.config.FeatureFlags; @@ -50,6 +49,7 @@ import com.android.launcher3.logging.FileLog; import com.android.launcher3.model.GridSizeMigrationTask; import com.android.launcher3.util.IntArray; import com.android.launcher3.util.IntSparseArrayMap; +import com.android.launcher3.util.PackageManagerHelper; import java.net.URISyntaxException; import java.util.ArrayList; @@ -223,7 +223,7 @@ public class ImportDataTask { case Favorites.ITEM_TYPE_SHORTCUT: case Favorites.ITEM_TYPE_APPLICATION: { intent = Intent.parseUri(c.getString(intentIndex), 0); - if (Utilities.isLauncherAppTarget(intent)) { + if (PackageManagerHelper.isLauncherAppTarget(intent)) { type = Favorites.ITEM_TYPE_APPLICATION; } else { values.put(Favorites.ICON_PACKAGE, c.getString(iconPackageIndex)); diff --git a/src/com/android/launcher3/provider/RestoreDbTask.java b/src/com/android/launcher3/provider/RestoreDbTask.java index d643a0b493..fb335516fb 100644 --- a/src/com/android/launcher3/provider/RestoreDbTask.java +++ b/src/com/android/launcher3/provider/RestoreDbTask.java @@ -16,8 +16,6 @@ package com.android.launcher3.provider; -import static com.android.launcher3.Utilities.getIntArrayFromString; -import static com.android.launcher3.Utilities.getStringFromIntArray; import static com.android.launcher3.provider.LauncherDbUtils.dropTable; import android.app.backup.BackupManager; @@ -40,6 +38,7 @@ import com.android.launcher3.WorkspaceItemInfo; import com.android.launcher3.Utilities; import com.android.launcher3.logging.FileLog; import com.android.launcher3.provider.LauncherDbUtils.SQLiteTransaction; +import com.android.launcher3.util.IntArray; import com.android.launcher3.util.LogConfig; import java.io.InvalidObjectException; @@ -240,8 +239,8 @@ public class RestoreDbTask { SharedPreferences prefs = Utilities.getPrefs(context); if (prefs.contains(APPWIDGET_OLD_IDS) && prefs.contains(APPWIDGET_IDS)) { AppWidgetsRestoredReceiver.restoreAppWidgetIds(context, - getIntArrayFromString(prefs.getString(APPWIDGET_OLD_IDS, "")), - getIntArrayFromString(prefs.getString(APPWIDGET_IDS, ""))); + IntArray.fromConcatString(prefs.getString(APPWIDGET_OLD_IDS, "")).toArray(), + IntArray.fromConcatString(prefs.getString(APPWIDGET_IDS, "")).toArray()); } else { FileLog.d(TAG, "No app widget ids to restore."); } @@ -253,8 +252,8 @@ public class RestoreDbTask { public static void setRestoredAppWidgetIds(Context context, @NonNull int[] oldIds, @NonNull int[] newIds) { Utilities.getPrefs(context).edit() - .putString(APPWIDGET_OLD_IDS, getStringFromIntArray(oldIds)) - .putString(APPWIDGET_IDS, getStringFromIntArray(newIds)) + .putString(APPWIDGET_OLD_IDS, IntArray.wrap(oldIds).toConcatString()) + .putString(APPWIDGET_IDS, IntArray.wrap(newIds).toConcatString()) .commit(); } diff --git a/src/com/android/launcher3/qsb/QsbContainerView.java b/src/com/android/launcher3/qsb/QsbContainerView.java index 857ea05d61..0eb4285273 100644 --- a/src/com/android/launcher3/qsb/QsbContainerView.java +++ b/src/com/android/launcher3/qsb/QsbContainerView.java @@ -32,12 +32,16 @@ import android.content.Context; import android.content.Intent; import android.graphics.Rect; import android.os.Bundle; +import android.provider.Settings; import android.util.AttributeSet; import android.view.LayoutInflater; import android.view.View; import android.view.ViewGroup; import android.widget.FrameLayout; +import androidx.annotation.NonNull; +import androidx.annotation.Nullable; + import com.android.launcher3.AppWidgetResizeFrame; import com.android.launcher3.InvariantDeviceProfile; import com.android.launcher3.LauncherAppState; @@ -55,6 +59,74 @@ import com.android.launcher3.graphics.FragmentWithPreview; */ public class QsbContainerView extends FrameLayout { + public static final String SEARCH_PROVIDER_SETTINGS_KEY = "SEARCH_PROVIDER_PACKAGE_NAME"; + + /** + * Returns the package name for user configured search provider or from searchManager + * @param context + * @return String + */ + @Nullable + public static String getSearchWidgetPackageName(@NonNull Context context) { + String providerPkg = Settings.Global.getString(context.getContentResolver(), + SEARCH_PROVIDER_SETTINGS_KEY); + if (providerPkg == null) { + SearchManager searchManager = context.getSystemService(SearchManager.class); + ComponentName componentName = searchManager.getGlobalSearchActivity(); + if (componentName != null) { + providerPkg = searchManager.getGlobalSearchActivity().getPackageName(); + } + } + return providerPkg; + } + + /** + * returns it's AppWidgetProviderInfo using package name from getSearchWidgetPackageName + * @param context + * @return AppWidgetProviderInfo + */ + @Nullable + public static AppWidgetProviderInfo getSearchWidgetProviderInfo(@NonNull Context context) { + String providerPkg = getSearchWidgetPackageName(context); + if (providerPkg == null) { + return null; + } + + AppWidgetProviderInfo defaultWidgetForSearchPackage = null; + AppWidgetManager appWidgetManager = AppWidgetManager.getInstance(context); + for (AppWidgetProviderInfo info : + appWidgetManager.getInstalledProvidersForPackage(providerPkg, null)) { + if (info.provider.getPackageName().equals(providerPkg) && info.configure == null) { + if ((info.widgetCategory + & AppWidgetProviderInfo.WIDGET_CATEGORY_SEARCHBOX) != 0) { + return info; + } else if (defaultWidgetForSearchPackage == null) { + defaultWidgetForSearchPackage = info; + } + } + } + return defaultWidgetForSearchPackage; + } + + /** + * returns componentName for searchWidget if package name is known. + */ + @Nullable + public static ComponentName getSearchComponentName(@NonNull Context context) { + AppWidgetProviderInfo providerInfo = + QsbContainerView.getSearchWidgetProviderInfo(context); + if (providerInfo != null) { + return providerInfo.provider; + } else { + String pkgName = QsbContainerView.getSearchWidgetPackageName(context); + if (pkgName != null) { + //we don't know the class name yet. we'll put the package name as placeholder + return new ComponentName(pkgName, pkgName); + } + return null; + } + } + public QsbContainerView(Context context) { super(context); } @@ -101,7 +173,7 @@ public class QsbContainerView extends FrameLayout { protected QsbWidgetHost createHost() { return new QsbWidgetHost(getContext(), QSB_WIDGET_HOST_ID, - (c) -> new QsbWidgetHostView(c)); + (c) -> new QsbWidgetHostView(c), this::rebindFragment); } private FrameLayout mWrapper; @@ -111,9 +183,9 @@ public class QsbContainerView extends FrameLayout { LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { mWrapper = new FrameLayout(getContext()); - // Only add the view when enabled if (isQsbEnabled()) { + mQsbWidgetHost.startListening(); mWrapper.addView(createQsb(mWrapper)); } return mWrapper; @@ -155,7 +227,8 @@ public class QsbContainerView extends FrameLayout { } if (isWidgetBound) { - mQsb = (QsbWidgetHostView) mQsbWidgetHost.createView(context, widgetId, mWidgetInfo); + mQsb = (QsbWidgetHostView) mQsbWidgetHost.createView(context, widgetId, + mWidgetInfo); mQsb.setId(R.id.qsb_widget); if (!isInPreviewMode()) { @@ -163,7 +236,6 @@ public class QsbContainerView extends FrameLayout { .getAppWidgetOptions(widgetId), opts)) { mQsb.updateAppWidgetOptions(opts); } - mQsbWidgetHost.startListening(); } return mQsb; } @@ -246,43 +318,32 @@ public class QsbContainerView extends FrameLayout { return v; } + /** * Returns a widget with category {@link AppWidgetProviderInfo#WIDGET_CATEGORY_SEARCHBOX} - * provided by the same package which is set to be global search activity. + * provided by the package from getSearchProviderPackageName * If widgetCategory is not supported, or no such widget is found, returns the first widget * provided by the package. */ protected AppWidgetProviderInfo getSearchWidgetProvider() { - SearchManager searchManager = - (SearchManager) getContext().getSystemService(Context.SEARCH_SERVICE); - ComponentName searchComponent = searchManager.getGlobalSearchActivity(); - if (searchComponent == null) return null; - String providerPkg = searchComponent.getPackageName(); - - AppWidgetProviderInfo defaultWidgetForSearchPackage = null; - - AppWidgetManager appWidgetManager = AppWidgetManager.getInstance(getContext()); - for (AppWidgetProviderInfo info : appWidgetManager.getInstalledProviders()) { - if (info.provider.getPackageName().equals(providerPkg) && info.configure == null) { - if ((info.widgetCategory - & AppWidgetProviderInfo.WIDGET_CATEGORY_SEARCHBOX) != 0) { - return info; - } else if (defaultWidgetForSearchPackage == null) { - defaultWidgetForSearchPackage = info; - } - } - } - return defaultWidgetForSearchPackage; + return getSearchWidgetProviderInfo(getContext()); } } public static class QsbWidgetHost extends AppWidgetHost { private final WidgetViewFactory mViewFactory; + private final WidgetProvidersUpdateCallback mWidgetsUpdateCallback; - public QsbWidgetHost(Context context, int hostId, WidgetViewFactory viewFactory) { + public QsbWidgetHost(Context context, int hostId, WidgetViewFactory viewFactory, + WidgetProvidersUpdateCallback widgetProvidersUpdateCallback) { super(context, hostId); mViewFactory = viewFactory; + mWidgetsUpdateCallback = widgetProvidersUpdateCallback; + } + + public QsbWidgetHost(Context context, int hostId, WidgetViewFactory viewFactory) { + this(context, hostId, viewFactory, null); } @Override @@ -290,6 +351,14 @@ public class QsbContainerView extends FrameLayout { Context context, int appWidgetId, AppWidgetProviderInfo appWidget) { return mViewFactory.newView(context); } + + @Override + protected void onProvidersChanged() { + super.onProvidersChanged(); + if (mWidgetsUpdateCallback != null) { + mWidgetsUpdateCallback.onProvidersUpdated(); + } + } } public interface WidgetViewFactory { @@ -297,6 +366,17 @@ public class QsbContainerView extends FrameLayout { QsbWidgetHostView newView(Context context); } + /** + * Callback interface for packages list update. + */ + @FunctionalInterface + public interface WidgetProvidersUpdateCallback { + /** + * Gets called when widget providers list changes + */ + void onProvidersUpdated(); + } + /** * Returns true if {@param original} contains all entries defined in {@param updates} and * have the same value. @@ -316,4 +396,5 @@ public class QsbContainerView extends FrameLayout { } return true; } + } diff --git a/src/com/android/launcher3/states/InternalStateHandler.java b/src/com/android/launcher3/states/InternalStateHandler.java index c6370c5c5f..a23cd6d2e3 100644 --- a/src/com/android/launcher3/states/InternalStateHandler.java +++ b/src/com/android/launcher3/states/InternalStateHandler.java @@ -15,6 +15,8 @@ */ package com.android.launcher3.states; +import static com.android.launcher3.util.Executors.MAIN_EXECUTOR; + import android.content.Intent; import android.os.Binder; import android.os.Bundle; @@ -22,8 +24,7 @@ import android.os.IBinder; import com.android.launcher3.Launcher; import com.android.launcher3.LauncherAppState; -import com.android.launcher3.LauncherModel.Callbacks; -import com.android.launcher3.MainThreadExecutor; +import com.android.launcher3.model.BgDataModel.Callbacks; import java.lang.ref.WeakReference; @@ -94,16 +95,12 @@ public abstract class InternalStateHandler extends Binder { private static class Scheduler implements Runnable { private WeakReference mPendingHandler = new WeakReference<>(null); - private MainThreadExecutor mMainThreadExecutor; public void schedule(InternalStateHandler handler) { synchronized (this) { mPendingHandler = new WeakReference<>(handler); - if (mMainThreadExecutor == null) { - mMainThreadExecutor = new MainThreadExecutor(); - } } - mMainThreadExecutor.execute(this); + MAIN_EXECUTOR.execute(this); } @Override diff --git a/src/com/android/launcher3/testing/TestInformationHandler.java b/src/com/android/launcher3/testing/TestInformationHandler.java index 243ff6f965..0d1d1a6709 100644 --- a/src/com/android/launcher3/testing/TestInformationHandler.java +++ b/src/com/android/launcher3/testing/TestInformationHandler.java @@ -15,7 +15,12 @@ */ package com.android.launcher3.testing; +import static com.android.launcher3.util.Executors.MAIN_EXECUTOR; +import static android.graphics.Bitmap.Config.ARGB_8888; + import android.content.Context; +import android.graphics.Bitmap; +import android.graphics.Color; import android.os.Bundle; import android.os.Debug; @@ -24,11 +29,11 @@ import com.android.launcher3.InvariantDeviceProfile; import com.android.launcher3.Launcher; import com.android.launcher3.LauncherAppState; import com.android.launcher3.LauncherState; -import com.android.launcher3.MainThreadExecutor; import com.android.launcher3.R; import com.android.launcher3.allapps.AllAppsStore; import com.android.launcher3.util.ResourceBasedOverride; +import java.util.LinkedList; import java.util.concurrent.ExecutionException; public class TestInformationHandler implements ResourceBasedOverride { @@ -42,6 +47,7 @@ public class TestInformationHandler implements ResourceBasedOverride { protected DeviceProfile mDeviceProfile; protected LauncherAppState mLauncherAppState; protected Launcher mLauncher; + private static LinkedList mLeaks; public void init(Context context) { mContext = context; @@ -89,20 +95,20 @@ public class TestInformationHandler implements ResourceBasedOverride { break; case TestProtocol.REQUEST_FREEZE_APP_LIST: - new MainThreadExecutor().execute(() -> + MAIN_EXECUTOR.execute(() -> mLauncher.getAppsView().getAppsStore().enableDeferUpdates( AllAppsStore.DEFER_UPDATES_TEST)); break; case TestProtocol.REQUEST_UNFREEZE_APP_LIST: - new MainThreadExecutor().execute(() -> + MAIN_EXECUTOR.execute(() -> mLauncher.getAppsView().getAppsStore().disableDeferUpdates( AllAppsStore.DEFER_UPDATES_TEST)); break; case TestProtocol.REQUEST_APP_LIST_FREEZE_FLAGS: { try { - final int deferUpdatesFlags = new MainThreadExecutor().submit(() -> + final int deferUpdatesFlags = MAIN_EXECUTOR.submit(() -> mLauncher.getAppsView().getAppsStore().getDeferUpdatesFlags()).get(); response.putInt(TestProtocol.TEST_INFO_RESPONSE_FIELD, deferUpdatesFlags); @@ -120,7 +126,30 @@ public class TestInformationHandler implements ResourceBasedOverride { response.putInt(TestProtocol.TEST_INFO_RESPONSE_FIELD, mem.getTotalPss()); break; } + + case TestProtocol.REQUEST_JAVA_LEAK: { + if (mLeaks == null) mLeaks = new LinkedList(); + + // Allocate and dirty the memory. + final int leakSize = 1024 * 1024; + final byte[] bytes = new byte[leakSize]; + for (int i = 0; i < leakSize; i += 239) { + bytes[i] = (byte) (i % 256); + } + mLeaks.add(bytes); + break; + } + + case TestProtocol.REQUEST_NATIVE_LEAK: { + if (mLeaks == null) mLeaks = new LinkedList(); + + // Allocate and dirty a bitmap. + final Bitmap bitmap = Bitmap.createBitmap(512, 512, ARGB_8888); + bitmap.eraseColor(Color.RED); + mLeaks.add(bitmap); + break; + } } return response; } -} \ No newline at end of file +} diff --git a/src/com/android/launcher3/testing/TestProtocol.java b/src/com/android/launcher3/testing/TestProtocol.java index 60a59ddfac..232a764e01 100644 --- a/src/com/android/launcher3/testing/TestProtocol.java +++ b/src/com/android/launcher3/testing/TestProtocol.java @@ -74,6 +74,8 @@ public final class TestProtocol { public static final String REQUEST_OVERVIEW_LEFT_GESTURE_MARGIN = "overview-left-margin"; public static final String REQUEST_OVERVIEW_RIGHT_GESTURE_MARGIN = "overview-right-margin"; public static final String REQUEST_TOTAL_PSS_KB = "total_pss"; + public static final String REQUEST_JAVA_LEAK = "java-leak"; + public static final String REQUEST_NATIVE_LEAK = "native-leak"; public static boolean sDebugTracing = false; public static final String REQUEST_ENABLE_DEBUG_TRACING = "enable-debug-tracing"; diff --git a/src/com/android/launcher3/touch/ItemLongClickListener.java b/src/com/android/launcher3/touch/ItemLongClickListener.java index babbcdd16f..6cd2b2dc2e 100644 --- a/src/com/android/launcher3/touch/ItemLongClickListener.java +++ b/src/com/android/launcher3/touch/ItemLongClickListener.java @@ -17,6 +17,7 @@ package com.android.launcher3.touch; import static android.view.View.INVISIBLE; import static android.view.View.VISIBLE; + import static com.android.launcher3.LauncherState.ALL_APPS; import static com.android.launcher3.LauncherState.NORMAL; import static com.android.launcher3.LauncherState.OVERVIEW; @@ -60,7 +61,7 @@ public class ItemLongClickListener { if (info.container >= 0) { Folder folder = Folder.getOpen(launcher); if (folder != null) { - if (!folder.getItemsInReadingOrder().contains(v)) { + if (!folder.getIconsInReadingOrder().contains(v)) { folder.close(true); } else { folder.startDrag(v, dragOptions); diff --git a/src/com/android/launcher3/util/ConfigMonitor.java b/src/com/android/launcher3/util/ConfigMonitor.java index 12d35e9628..0f81520578 100644 --- a/src/com/android/launcher3/util/ConfigMonitor.java +++ b/src/com/android/launcher3/util/ConfigMonitor.java @@ -16,20 +16,15 @@ package com.android.launcher3.util; * 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.hardware.display.DisplayManager; -import android.hardware.display.DisplayManager.DisplayListener; -import android.os.Handler; import android.util.Log; -import android.view.Display; -import android.view.WindowManager; - -import com.android.launcher3.MainThreadExecutor; import java.util.function.Consumer; @@ -37,7 +32,8 @@ 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 DisplayListener { +public class ConfigMonitor extends BroadcastReceiver implements + DefaultDisplay.DisplayInfoChangeListener { private static final String TAG = "ConfigMonitor"; @@ -61,24 +57,19 @@ public class ConfigMonitor extends BroadcastReceiver implements DisplayListener mFontScale = config.fontScale; mDensity = config.densityDpi; - Display display = getDefaultDisplay(context); - mDisplayId = display.getDisplayId(); + DefaultDisplay display = DefaultDisplay.INSTANCE.get(context); + display.addChangeListener(this); + DefaultDisplay.Info displayInfo = display.getInfo(); + mDisplayId = displayInfo.id; - mRealSize = new Point(); - display.getRealSize(mRealSize); - - mSmallestSize = new Point(); - mLargestSize = new Point(); - display.getCurrentSizeRange(mSmallestSize, mLargestSize); + 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)); - - // Listen for display manager change - mContext.getSystemService(DisplayManager.class) - .registerDisplayListener(this, new Handler(UiThreadHelper.getBackgroundLooper())); } @Override @@ -91,26 +82,19 @@ public class ConfigMonitor extends BroadcastReceiver implements DisplayListener } @Override - public void onDisplayAdded(int displayId) { } - - @Override - public void onDisplayRemoved(int displayId) { } - - @Override - public void onDisplayChanged(int displayId) { - if (displayId != mDisplayId) { + public void onDisplayInfoChanged(DefaultDisplay.Info info, int flags) { + if (info.id != mDisplayId) { return; } - Display display = getDefaultDisplay(mContext); - display.getRealSize(mTmpPoint1); - + 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; } - display.getCurrentSizeRange(mTmpPoint1, mTmpPoint2); + 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)); @@ -122,18 +106,15 @@ public class ConfigMonitor extends BroadcastReceiver implements DisplayListener if (mCallback != null) { Consumer callback = mCallback; mCallback = null; - new MainThreadExecutor().execute(() -> callback.accept(mContext)); + MAIN_EXECUTOR.execute(() -> callback.accept(mContext)); } } - private Display getDefaultDisplay(Context context) { - return context.getSystemService(WindowManager.class).getDefaultDisplay(); - } - public void unregister() { try { mContext.unregisterReceiver(this); - mContext.getSystemService(DisplayManager.class).unregisterDisplayListener(this); + DefaultDisplay display = DefaultDisplay.INSTANCE.get(mContext); + display.removeChangeListener(this); } catch (Exception e) { Log.e(TAG, "Failed to unregister config monitor", e); } diff --git a/src/com/android/launcher3/util/DefaultDisplay.java b/src/com/android/launcher3/util/DefaultDisplay.java index 7719f084d7..8529d50cad 100644 --- a/src/com/android/launcher3/util/DefaultDisplay.java +++ b/src/com/android/launcher3/util/DefaultDisplay.java @@ -15,12 +15,15 @@ */ package com.android.launcher3.util; +import static com.android.launcher3.util.Executors.UI_HELPER_EXECUTOR; + import android.content.Context; import android.graphics.Point; import android.hardware.display.DisplayManager; import android.hardware.display.DisplayManager.DisplayListener; import android.os.Handler; import android.os.Message; +import android.util.DisplayMetrics; import android.util.Log; import android.view.Display; import android.view.WindowManager; @@ -54,7 +57,7 @@ public class DefaultDisplay implements DisplayListener { mChangeHandler = new Handler(this::onChange); context.getSystemService(DisplayManager.class) - .registerDisplayListener(this, new Handler(UiThreadHelper.getBackgroundLooper())); + .registerDisplayListener(this, UI_HELPER_EXECUTOR.getHandler()); } @Override @@ -122,6 +125,8 @@ public class DefaultDisplay implements DisplayListener { public final Point smallestSize; public final Point largestSize; + public final DisplayMetrics metrics; + private Info(Context context) { Display display = context.getSystemService(WindowManager.class).getDefaultDisplay(); @@ -136,6 +141,9 @@ public class DefaultDisplay implements DisplayListener { largestSize = new Point(); display.getRealSize(realSize); display.getCurrentSizeRange(smallestSize, largestSize); + + metrics = new DisplayMetrics(); + display.getMetrics(metrics); } private boolean hasDifferentSize(Info info) { diff --git a/src/com/android/launcher3/util/Executors.java b/src/com/android/launcher3/util/Executors.java new file mode 100644 index 0000000000..4d5ee49e83 --- /dev/null +++ b/src/com/android/launcher3/util/Executors.java @@ -0,0 +1,87 @@ +/* + * Copyright (C) 2008 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 android.os.HandlerThread; +import android.os.Looper; +import android.os.Process; + +import java.util.concurrent.Executor; +import java.util.concurrent.LinkedBlockingQueue; +import java.util.concurrent.ThreadPoolExecutor; +import java.util.concurrent.TimeUnit; + +/** + * Various different executors used in Launcher + */ +public class Executors { + + // These values are same as that in {@link AsyncTask}. + private static final int CPU_COUNT = Runtime.getRuntime().availableProcessors(); + private static final int CORE_POOL_SIZE = CPU_COUNT + 1; + private static final int MAXIMUM_POOL_SIZE = CPU_COUNT * 2 + 1; + private static final int KEEP_ALIVE = 1; + + /** + * An {@link Executor} to be used with async task with no limit on the queue size. + */ + public static final Executor THREAD_POOL_EXECUTOR = new ThreadPoolExecutor( + CORE_POOL_SIZE, MAXIMUM_POOL_SIZE, KEEP_ALIVE, + TimeUnit.SECONDS, new LinkedBlockingQueue<>()); + + /** + * Returns the executor for running tasks on the main thread. + */ + public static final LooperExecutor MAIN_EXECUTOR = + new LooperExecutor(Looper.getMainLooper()); + + /** + * A background executor for using time sensitive actions where user is waiting for response. + */ + public static final LooperExecutor UI_HELPER_EXECUTOR = + new LooperExecutor(createAndStartNewForegroundLooper("UiThreadHelper")); + + /** + * Utility method to get a started handler thread statically + */ + public static Looper createAndStartNewLooper(String name) { + return createAndStartNewLooper(name, Process.THREAD_PRIORITY_DEFAULT); + } + + /** + * Utility method to get a started handler thread statically with the provided priority + */ + public static Looper createAndStartNewLooper(String name, int priority) { + HandlerThread thread = new HandlerThread(name, priority); + thread.start(); + return thread.getLooper(); + } + + /** + * Similar to {@link #createAndStartNewLooper(String)}, but starts the thread with + * foreground priority. + * Think before using + */ + public static Looper createAndStartNewForegroundLooper(String name) { + return createAndStartNewLooper(name, Process.THREAD_PRIORITY_FOREGROUND); + } + + /** + * Executor used for running Launcher model related tasks (eg loading icons or updated db) + */ + public static final LooperExecutor MODEL_EXECUTOR = + new LooperExecutor(createAndStartNewLooper("launcher-loader")); +} diff --git a/src/com/android/launcher3/util/IOUtils.java b/src/com/android/launcher3/util/IOUtils.java index f95f74d600..4a4a5ca22e 100644 --- a/src/com/android/launcher3/util/IOUtils.java +++ b/src/com/android/launcher3/util/IOUtils.java @@ -17,10 +17,13 @@ package com.android.launcher3.util; import android.content.Context; +import android.util.Log; +import com.android.launcher3.Utilities; import com.android.launcher3.config.FeatureFlags; import java.io.ByteArrayOutputStream; +import java.io.Closeable; import java.io.File; import java.io.FileInputStream; import java.io.FileOutputStream; @@ -35,6 +38,7 @@ import java.util.UUID; public class IOUtils { private static final int BUF_SIZE = 0x1000; // 4K + private static final String TAG = "IOUtils"; public static byte[] toByteArray(File file) throws IOException { try (InputStream in = new FileInputStream(file)) { @@ -77,4 +81,16 @@ public class IOUtils { } return file.getAbsolutePath(); } + + public static void closeSilently(Closeable c) { + if (c != null) { + try { + c.close(); + } catch (IOException e) { + if (FeatureFlags.IS_DOGFOOD_BUILD) { + Log.d(TAG, "Error closing", e); + } + } + } + } } diff --git a/src/com/android/launcher3/util/IntArray.java b/src/com/android/launcher3/util/IntArray.java index d2a551f454..7252f7ac1d 100644 --- a/src/com/android/launcher3/util/IntArray.java +++ b/src/com/android/launcher3/util/IntArray.java @@ -17,6 +17,7 @@ package com.android.launcher3.util; import java.util.Arrays; +import java.util.StringTokenizer; /** * Copy of the platform hidden implementation of android.util.IntArray. @@ -248,6 +249,17 @@ public class IntArray implements Cloneable { return b.toString(); } + public static IntArray fromConcatString(String concatString) { + StringTokenizer tokenizer = new StringTokenizer(concatString, ","); + int[] array = new int[tokenizer.countTokens()]; + int count = 0; + while (tokenizer.hasMoreTokens()) { + array[count] = Integer.parseInt(tokenizer.nextToken().trim()); + count++; + } + return new IntArray(array, array.length); + } + /** * Throws {@link ArrayIndexOutOfBoundsException} if the index is out of bounds. * diff --git a/src/com/android/launcher3/util/LooperExecutor.java b/src/com/android/launcher3/util/LooperExecutor.java index cc07469971..8ac600f735 100644 --- a/src/com/android/launcher3/util/LooperExecutor.java +++ b/src/com/android/launcher3/util/LooperExecutor.java @@ -16,7 +16,9 @@ package com.android.launcher3.util; import android.os.Handler; +import android.os.HandlerThread; import android.os.Looper; +import android.os.Process; import java.util.List; import java.util.concurrent.AbstractExecutorService; @@ -46,6 +48,13 @@ public class LooperExecutor extends AbstractExecutorService { } } + /** + * Same as execute, but never runs the action inline. + */ + public void post(Runnable runnable) { + mHandler.post(runnable); + } + /** * Not supported and throws an exception when used. */ @@ -79,7 +88,31 @@ public class LooperExecutor extends AbstractExecutorService { */ @Override @Deprecated - public boolean awaitTermination(long l, TimeUnit timeUnit) throws InterruptedException { + public boolean awaitTermination(long l, TimeUnit timeUnit) { throw new UnsupportedOperationException(); } + + /** + * Returns the thread for this executor + */ + public Thread getThread() { + return mHandler.getLooper().getThread(); + } + + /** + * Returns the looper for this executor + */ + public Looper getLooper() { + return mHandler.getLooper(); + } + + /** + * Set the priority of a thread, based on Linux priorities. + * @param priority Linux priority level, from -20 for highest scheduling priority + * to 19 for lowest scheduling priority. + * @see Process#setThreadPriority(int, int) + */ + public void setThreadPriority(int priority) { + Process.setThreadPriority(((HandlerThread) getThread()).getThreadId(), priority); + } } diff --git a/src/com/android/launcher3/util/MainThreadInitializedObject.java b/src/com/android/launcher3/util/MainThreadInitializedObject.java index e185a31990..cf4e8c796b 100644 --- a/src/com/android/launcher3/util/MainThreadInitializedObject.java +++ b/src/com/android/launcher3/util/MainThreadInitializedObject.java @@ -15,12 +15,14 @@ */ package com.android.launcher3.util; +import static com.android.launcher3.util.Executors.MAIN_EXECUTOR; + import android.content.Context; import android.os.Looper; import androidx.annotation.VisibleForTesting; -import com.android.launcher3.MainThreadExecutor; +import com.android.launcher3.uioverrides.DejankBinderTracker; import com.android.launcher3.util.ResourceBasedOverride.Overrides; import java.util.concurrent.ExecutionException; @@ -40,10 +42,11 @@ public class MainThreadInitializedObject { public T get(Context context) { if (mValue == null) { if (Looper.myLooper() == Looper.getMainLooper()) { - mValue = mProvider.get(context.getApplicationContext()); + mValue = DejankBinderTracker.whitelistIpcs(() -> + mProvider.get(context.getApplicationContext())); } else { try { - return new MainThreadExecutor().submit(() -> get(context)).get(); + return MAIN_EXECUTOR.submit(() -> get(context)).get(); } catch (InterruptedException|ExecutionException e) { throw new RuntimeException(e); } diff --git a/src/com/android/launcher3/util/PackageManagerHelper.java b/src/com/android/launcher3/util/PackageManagerHelper.java index 7d3a94162b..ef4307ec59 100644 --- a/src/com/android/launcher3/util/PackageManagerHelper.java +++ b/src/com/android/launcher3/util/PackageManagerHelper.java @@ -24,9 +24,11 @@ import android.content.Intent; import android.content.IntentFilter; import android.content.pm.ApplicationInfo; import android.content.pm.LauncherActivityInfo; +import android.content.pm.PackageInfo; import android.content.pm.PackageManager; import android.content.pm.PackageManager.NameNotFoundException; import android.content.pm.ResolveInfo; +import android.content.res.Resources; import android.graphics.Rect; import android.net.Uri; import android.os.Build; @@ -35,6 +37,7 @@ import android.os.PatternMatcher; import android.os.UserHandle; import android.text.TextUtils; import android.util.Log; +import android.util.Pair; import android.widget.Toast; import com.android.launcher3.AppInfo; @@ -220,4 +223,73 @@ public class PackageManagerHelper { packageFilter.addDataSchemeSpecificPart(pkg, PatternMatcher.PATTERN_LITERAL); return packageFilter; } + + public static boolean isSystemApp(Context context, Intent intent) { + PackageManager pm = context.getPackageManager(); + ComponentName cn = intent.getComponent(); + String packageName = null; + if (cn == null) { + ResolveInfo info = pm.resolveActivity(intent, PackageManager.MATCH_DEFAULT_ONLY); + if ((info != null) && (info.activityInfo != null)) { + packageName = info.activityInfo.packageName; + } + } else { + packageName = cn.getPackageName(); + } + if (packageName != null) { + try { + PackageInfo info = pm.getPackageInfo(packageName, 0); + return (info != null) && (info.applicationInfo != null) && + ((info.applicationInfo.flags & ApplicationInfo.FLAG_SYSTEM) != 0); + } catch (NameNotFoundException e) { + return false; + } + } else { + return false; + } + } + + /** + * Finds a system apk which had a broadcast receiver listening to a particular action. + * @param action intent action used to find the apk + * @return a pair of apk package name and the resources. + */ + public static Pair findSystemApk(String action, PackageManager pm) { + final Intent intent = new Intent(action); + for (ResolveInfo info : pm.queryBroadcastReceivers(intent, 0)) { + if (info.activityInfo != null && + (info.activityInfo.applicationInfo.flags & ApplicationInfo.FLAG_SYSTEM) != 0) { + final String packageName = info.activityInfo.packageName; + try { + final Resources res = pm.getResourcesForApplication(packageName); + return Pair.create(packageName, res); + } catch (NameNotFoundException e) { + Log.w(TAG, "Failed to find resources for " + packageName); + } + } + } + return null; + } + + /** + * Returns true if the intent is a valid launch intent for a launcher activity of an app. + * This is used to identify shortcuts which are different from the ones exposed by the + * applications' manifest file. + * + * @param launchIntent The intent that will be launched when the shortcut is clicked. + */ + public static boolean isLauncherAppTarget(Intent launchIntent) { + if (launchIntent != null + && Intent.ACTION_MAIN.equals(launchIntent.getAction()) + && launchIntent.getComponent() != null + && launchIntent.getCategories() != null + && launchIntent.getCategories().size() == 1 + && launchIntent.hasCategory(Intent.CATEGORY_LAUNCHER) + && TextUtils.isEmpty(launchIntent.getDataString())) { + // An app target can either have no extra or have ItemInfo.EXTRA_PROFILE. + Bundle extras = launchIntent.getExtras(); + return extras == null || extras.keySet().isEmpty(); + } + return false; + } } diff --git a/src/com/android/launcher3/util/PackageUserKey.java b/src/com/android/launcher3/util/PackageUserKey.java index 8dc45f5476..f243ca6283 100644 --- a/src/com/android/launcher3/util/PackageUserKey.java +++ b/src/com/android/launcher3/util/PackageUserKey.java @@ -6,7 +6,6 @@ import android.service.notification.StatusBarNotification; import androidx.annotation.Nullable; import com.android.launcher3.ItemInfo; -import com.android.launcher3.shortcuts.DeepShortcutManager; import java.util.Arrays; @@ -31,7 +30,7 @@ public class PackageUserKey { update(packageName, user); } - private void update(String packageName, UserHandle user) { + public void update(String packageName, UserHandle user) { mPackageName = packageName; mUser = user; mHashCode = Arrays.hashCode(new Object[] {packageName, user}); diff --git a/src/com/android/launcher3/util/Preconditions.java b/src/com/android/launcher3/util/Preconditions.java index 7ab0d3103d..ed66422be0 100644 --- a/src/com/android/launcher3/util/Preconditions.java +++ b/src/com/android/launcher3/util/Preconditions.java @@ -16,9 +16,10 @@ package com.android.launcher3.util; +import static com.android.launcher3.util.Executors.MODEL_EXECUTOR; + import android.os.Looper; -import com.android.launcher3.LauncherModel; import com.android.launcher3.config.FeatureFlags; /** @@ -33,7 +34,7 @@ public class Preconditions { } public static void assertWorkerThread() { - if (FeatureFlags.IS_DOGFOOD_BUILD && !isSameLooper(LauncherModel.getWorkerLooper())) { + if (FeatureFlags.IS_DOGFOOD_BUILD && !isSameLooper(MODEL_EXECUTOR.getLooper())) { throw new IllegalStateException(); } } diff --git a/src_flags/com/android/launcher3/config/FeatureFlags.java b/src/com/android/launcher3/util/SafeCloseable.java similarity index 64% rename from src_flags/com/android/launcher3/config/FeatureFlags.java rename to src/com/android/launcher3/util/SafeCloseable.java index 73c699633a..ba8ee04d27 100644 --- a/src_flags/com/android/launcher3/config/FeatureFlags.java +++ b/src/com/android/launcher3/util/SafeCloseable.java @@ -1,5 +1,5 @@ /* - * Copyright (C) 2015 The Android Open Source Project + * 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. @@ -14,15 +14,13 @@ * limitations under the License. */ -package com.android.launcher3.config; - -import android.content.Context; +package com.android.launcher3.util; /** - * Defines a set of flags used to control various launcher behaviors + * Extension of closeable which does not throw an exception */ -public final class FeatureFlags extends BaseFlags { - private FeatureFlags() { - // Prevent instantiation - } +public interface SafeCloseable extends AutoCloseable { + + @Override + void close(); } diff --git a/src/com/android/launcher3/util/ShortcutUtil.java b/src/com/android/launcher3/util/ShortcutUtil.java index af99713a1a..49c97daf35 100644 --- a/src/com/android/launcher3/util/ShortcutUtil.java +++ b/src/com/android/launcher3/util/ShortcutUtil.java @@ -19,7 +19,7 @@ import com.android.launcher3.ItemInfo; import com.android.launcher3.LauncherSettings; import com.android.launcher3.Utilities; import com.android.launcher3.WorkspaceItemInfo; -import com.android.launcher3.config.FeatureFlags; +import com.android.launcher3.model.WidgetsModel; import com.android.launcher3.shortcuts.ShortcutKey; public class ShortcutUtil { @@ -64,7 +64,7 @@ public class ShortcutUtil { private static boolean isActive(ItemInfo info) { boolean isLoading = info instanceof WorkspaceItemInfo && ((WorkspaceItemInfo) info).hasPromiseIconUi(); - return !isLoading && !info.isDisabled() && !FeatureFlags.GO_DISABLE_WIDGETS; + return !isLoading && !info.isDisabled() && !WidgetsModel.GO_DISABLE_WIDGETS; } private static boolean isApp(ItemInfo info) { @@ -76,4 +76,4 @@ public class ShortcutUtil { && info.container != ItemInfo.NO_ID && info instanceof WorkspaceItemInfo; } -} \ No newline at end of file +} diff --git a/src/com/android/launcher3/util/UiThreadHelper.java b/src/com/android/launcher3/util/UiThreadHelper.java index cc442f988e..f8d163230d 100644 --- a/src/com/android/launcher3/util/UiThreadHelper.java +++ b/src/com/android/launcher3/util/UiThreadHelper.java @@ -15,14 +15,13 @@ */ package com.android.launcher3.util; +import static com.android.launcher3.util.Executors.UI_HELPER_EXECUTOR; + import android.app.Activity; import android.content.Context; import android.os.Handler; -import android.os.HandlerThread; import android.os.IBinder; -import android.os.Looper; import android.os.Message; -import android.os.Process; import android.view.inputmethod.InputMethodManager; /** @@ -30,25 +29,15 @@ import android.view.inputmethod.InputMethodManager; */ public class UiThreadHelper { - private static HandlerThread sHandlerThread; private static Handler sHandler; private static final int MSG_HIDE_KEYBOARD = 1; private static final int MSG_SET_ORIENTATION = 2; private static final int MSG_RUN_COMMAND = 3; - public static Looper getBackgroundLooper() { - if (sHandlerThread == null) { - sHandlerThread = - new HandlerThread("UiThreadHelper", Process.THREAD_PRIORITY_FOREGROUND); - sHandlerThread.start(); - } - return sHandlerThread.getLooper(); - } - private static Handler getHandler(Context context) { if (sHandler == null) { - sHandler = new Handler(getBackgroundLooper(), + sHandler = new Handler(UI_HELPER_EXECUTOR.getLooper(), new UiCallbacks(context.getApplicationContext())); } return sHandler; diff --git a/src/com/android/launcher3/util/ViewOnDrawExecutor.java b/src/com/android/launcher3/util/ViewOnDrawExecutor.java index acce308607..61ba4e5662 100644 --- a/src/com/android/launcher3/util/ViewOnDrawExecutor.java +++ b/src/com/android/launcher3/util/ViewOnDrawExecutor.java @@ -16,13 +16,14 @@ package com.android.launcher3.util; +import static com.android.launcher3.util.Executors.MODEL_EXECUTOR; + import android.os.Process; import android.view.View; import android.view.View.OnAttachStateChangeListener; import android.view.ViewTreeObserver.OnDrawListener; import com.android.launcher3.Launcher; -import com.android.launcher3.LauncherModel; import java.util.ArrayList; import java.util.concurrent.Executor; @@ -66,7 +67,7 @@ public class ViewOnDrawExecutor implements Executor, OnDrawListener, Runnable, @Override public void execute(Runnable command) { mTasks.add(command); - LauncherModel.setWorkerPriority(Process.THREAD_PRIORITY_BACKGROUND); + MODEL_EXECUTOR.setThreadPriority(Process.THREAD_PRIORITY_BACKGROUND); } @Override @@ -108,7 +109,7 @@ public class ViewOnDrawExecutor implements Executor, OnDrawListener, Runnable, if (mLauncher != null) { mLauncher.clearPendingExecutor(this); } - LauncherModel.setWorkerPriority(Process.THREAD_PRIORITY_DEFAULT); + MODEL_EXECUTOR.setThreadPriority(Process.THREAD_PRIORITY_DEFAULT); } protected boolean isCompleted() { diff --git a/src/com/android/launcher3/util/WallpaperOffsetInterpolator.java b/src/com/android/launcher3/util/WallpaperOffsetInterpolator.java index 5c24687404..2ad80cf665 100644 --- a/src/com/android/launcher3/util/WallpaperOffsetInterpolator.java +++ b/src/com/android/launcher3/util/WallpaperOffsetInterpolator.java @@ -1,5 +1,7 @@ package com.android.launcher3.util; +import static com.android.launcher3.util.Executors.UI_HELPER_EXECUTOR; + import android.app.WallpaperManager; import android.content.BroadcastReceiver; import android.content.Context; @@ -198,7 +200,7 @@ public class WallpaperOffsetInterpolator extends BroadcastReceiver { private float mOffsetX; public OffsetHandler(Context context) { - super(UiThreadHelper.getBackgroundLooper()); + super(UI_HELPER_EXECUTOR.getLooper()); mInterpolator = Interpolators.DEACCEL_1_5; mWM = WallpaperManager.getInstance(context); } diff --git a/src/com/android/launcher3/views/BaseDragLayer.java b/src/com/android/launcher3/views/BaseDragLayer.java index c08b659313..2a4c5a7bfe 100644 --- a/src/com/android/launcher3/views/BaseDragLayer.java +++ b/src/com/android/launcher3/views/BaseDragLayer.java @@ -465,7 +465,7 @@ public abstract class BaseDragLayer } public void dump(String prefix, PrintWriter writer) { - writer.println(prefix + "DragLayer"); + writer.println(prefix + "DragLayer:"); if (mActiveController != null) { writer.println(prefix + "\tactiveController: " + mActiveController); mActiveController.dump(prefix + "\t", writer); diff --git a/src/com/android/launcher3/views/FloatingIconView.java b/src/com/android/launcher3/views/FloatingIconView.java index f728a67764..3912b44684 100644 --- a/src/com/android/launcher3/views/FloatingIconView.java +++ b/src/com/android/launcher3/views/FloatingIconView.java @@ -22,6 +22,7 @@ import static com.android.launcher3.Utilities.mapToRange; import static com.android.launcher3.anim.Interpolators.LINEAR; import static com.android.launcher3.config.FeatureFlags.ADAPTIVE_ICON_WINDOW_ANIM; import static com.android.launcher3.states.RotationHelper.REQUEST_LOCK; +import static com.android.launcher3.util.Executors.MODEL_EXECUTOR; import android.animation.Animator; import android.animation.AnimatorListenerAdapter; @@ -41,7 +42,6 @@ import android.graphics.drawable.ColorDrawable; import android.graphics.drawable.Drawable; import android.os.Build; import android.os.CancellationSignal; -import android.os.Handler; import android.util.AttributeSet; import android.util.Log; import android.view.View; @@ -50,11 +50,17 @@ import android.view.ViewOutlineProvider; import android.view.ViewTreeObserver.OnGlobalLayoutListener; import android.widget.ImageView; +import androidx.annotation.Nullable; +import androidx.annotation.UiThread; +import androidx.annotation.WorkerThread; +import androidx.dynamicanimation.animation.FloatPropertyCompat; +import androidx.dynamicanimation.animation.SpringAnimation; +import androidx.dynamicanimation.animation.SpringForce; + import com.android.launcher3.BubbleTextView; import com.android.launcher3.InsettableFrameLayout.LayoutParams; import com.android.launcher3.ItemInfo; import com.android.launcher3.Launcher; -import com.android.launcher3.LauncherModel; import com.android.launcher3.R; import com.android.launcher3.Utilities; import com.android.launcher3.dragndrop.DragLayer; @@ -66,13 +72,6 @@ import com.android.launcher3.icons.LauncherIcons; import com.android.launcher3.popup.SystemShortcut; import com.android.launcher3.shortcuts.DeepShortcutView; -import androidx.annotation.Nullable; -import androidx.annotation.UiThread; -import androidx.annotation.WorkerThread; -import androidx.dynamicanimation.animation.FloatPropertyCompat; -import androidx.dynamicanimation.animation.SpringAnimation; -import androidx.dynamicanimation.animation.SpringForce; - /** * A view that is created to look like another view with the purpose of creating fluid animations. */ @@ -722,7 +721,7 @@ public class FloatingIconView extends View implements @UiThread public static IconLoadResult fetchIcon(Launcher l, View v, ItemInfo info, boolean isOpening) { IconLoadResult result = new IconLoadResult(info); - new Handler(LauncherModel.getWorkerLooper()).postAtFrontOfQueue(() -> { + MODEL_EXECUTOR.getHandler().postAtFrontOfQueue(() -> { RectF position = new RectF(); getLocationBoundsForView(l, v, isOpening, position); getIconResult(l, v, info, position, result); diff --git a/src/com/android/launcher3/views/OptionsPopupView.java b/src/com/android/launcher3/views/OptionsPopupView.java index 465df448e3..88d34daac9 100644 --- a/src/com/android/launcher3/views/OptionsPopupView.java +++ b/src/com/android/launcher3/views/OptionsPopupView.java @@ -37,7 +37,7 @@ import androidx.annotation.VisibleForTesting; import com.android.launcher3.Launcher; import com.android.launcher3.R; import com.android.launcher3.Utilities; -import com.android.launcher3.config.FeatureFlags; +import com.android.launcher3.model.WidgetsModel; import com.android.launcher3.popup.ArrowPopup; import com.android.launcher3.shortcuts.DeepShortcutView; import com.android.launcher3.userevent.nano.LauncherLogProto.Action; @@ -158,7 +158,7 @@ public class OptionsPopupView extends ArrowPopup R.drawable.ic_palette : R.drawable.ic_wallpaper; options.add(new OptionItem(resString, resDrawable, ControlType.WALLPAPER_BUTTON, OptionsPopupView::startWallpaperPicker)); - if (!FeatureFlags.GO_DISABLE_WIDGETS) { + if (!WidgetsModel.GO_DISABLE_WIDGETS) { options.add(new OptionItem(R.string.widget_button_text, R.drawable.ic_widget, ControlType.WIDGETS_BUTTON, OptionsPopupView::onWidgetsClicked)); } diff --git a/src/com/android/launcher3/widget/LauncherAppWidgetHostView.java b/src/com/android/launcher3/widget/LauncherAppWidgetHostView.java index dce839f397..f3fd7ca4da 100644 --- a/src/com/android/launcher3/widget/LauncherAppWidgetHostView.java +++ b/src/com/android/launcher3/widget/LauncherAppWidgetHostView.java @@ -44,6 +44,7 @@ import com.android.launcher3.SimpleOnStylusPressListener; import com.android.launcher3.StylusEventHelper; import com.android.launcher3.Utilities; import com.android.launcher3.dragndrop.DragLayer; +import com.android.launcher3.util.Executors; import com.android.launcher3.util.Themes; import com.android.launcher3.views.BaseDragLayer.TouchCompleteListener; @@ -96,7 +97,7 @@ public class LauncherAppWidgetHostView extends NavigableAppWidgetHostView setBackgroundResource(R.drawable.widget_internal_focus_bg); if (Utilities.ATLEAST_OREO) { - setExecutor(Utilities.THREAD_POOL_EXECUTOR); + setExecutor(Executors.THREAD_POOL_EXECUTOR); } if (Utilities.ATLEAST_Q && Themes.getAttrBoolean(mLauncher, R.attr.isWorkspaceDarkText)) { setOnLightBackground(true); @@ -332,12 +333,7 @@ public class LauncherAppWidgetHostView extends NavigableAppWidgetHostView if (shouldRegisterAutoAdvance != mIsAutoAdvanceRegistered) { mIsAutoAdvanceRegistered = shouldRegisterAutoAdvance; if (mAutoAdvanceRunnable == null) { - mAutoAdvanceRunnable = new Runnable() { - @Override - public void run() { - runAutoAdvance(); - } - }; + mAutoAdvanceRunnable = this::runAutoAdvance; } handler.removeCallbacks(mAutoAdvanceRunnable); diff --git a/src/com/android/launcher3/widget/PendingItemDragHelper.java b/src/com/android/launcher3/widget/PendingItemDragHelper.java index 8ea9bd479c..662e627d99 100644 --- a/src/com/android/launcher3/widget/PendingItemDragHelper.java +++ b/src/com/android/launcher3/widget/PendingItemDragHelper.java @@ -91,8 +91,8 @@ public class PendingItemDragHelper extends DragPreviewProvider { createWidgetInfo.info, maxWidth, previewSizeBeforeScale); } if (preview == null) { - preview = app.getWidgetCache().generateWidgetPreview( - launcher, createWidgetInfo.info, maxWidth, null, previewSizeBeforeScale); + preview = app.getWidgetCache().generateWidgetPreview(launcher, + createWidgetInfo.info, maxWidth, null, previewSizeBeforeScale).first; } if (previewSizeBeforeScale[0] < previewBitmapWidth) { diff --git a/src/com/android/launcher3/widget/custom/CustomWidgetManager.java b/src/com/android/launcher3/widget/custom/CustomWidgetManager.java new file mode 100644 index 0000000000..cf3e26d03d --- /dev/null +++ b/src/com/android/launcher3/widget/custom/CustomWidgetManager.java @@ -0,0 +1,163 @@ +/* + * 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.widget.custom; + +import static com.android.launcher3.LauncherAppWidgetProviderInfo.CLS_CUSTOM_WIDGET_PREFIX; + +import android.appwidget.AppWidgetManager; +import android.appwidget.AppWidgetProviderInfo; +import android.content.ComponentName; +import android.content.Context; +import android.os.Parcel; +import android.os.Process; +import android.util.SparseArray; + +import androidx.annotation.NonNull; +import androidx.annotation.Nullable; + +import com.android.launcher3.LauncherAppWidgetInfo; +import com.android.launcher3.LauncherAppWidgetProviderInfo; +import com.android.launcher3.uioverrides.plugins.PluginManagerWrapper; +import com.android.launcher3.util.MainThreadInitializedObject; +import com.android.launcher3.util.PackageUserKey; +import com.android.launcher3.widget.LauncherAppWidgetHostView; +import com.android.systemui.plugins.CustomWidgetPlugin; +import com.android.systemui.plugins.PluginListener; + +import java.util.ArrayList; +import java.util.List; +import java.util.function.Consumer; + +/** + * CustomWidgetManager handles custom widgets implemented as a plugin. + */ +public class CustomWidgetManager implements PluginListener { + + public static final MainThreadInitializedObject INSTANCE = + new MainThreadInitializedObject<>(CustomWidgetManager::new); + + private final List mPlugins; + private final List mCustomWidgets; + private final SparseArray mWidgetsIdMap; + private Consumer mWidgetRefreshCallback; + + private CustomWidgetManager(Context context) { + mPlugins = new ArrayList<>(); + mCustomWidgets = new ArrayList<>(); + mWidgetsIdMap = new SparseArray<>(); + PluginManagerWrapper.INSTANCE.get(context) + .addPluginListener(this, CustomWidgetPlugin.class, true); + } + + @Override + public void onPluginConnected(CustomWidgetPlugin plugin, Context context) { + mPlugins.add(plugin); + List providers = AppWidgetManager.getInstance(context) + .getInstalledProvidersForProfile(Process.myUserHandle()); + if (providers.isEmpty()) return; + Parcel parcel = Parcel.obtain(); + providers.get(0).writeToParcel(parcel, 0); + parcel.setDataPosition(0); + CustomAppWidgetProviderInfo info = newInfo(plugin, parcel, context); + parcel.recycle(); + mCustomWidgets.add(info); + mWidgetsIdMap.put(plugin.getProviderId(), info.provider); + mWidgetRefreshCallback.accept(null); + } + + @Override + public void onPluginDisconnected(CustomWidgetPlugin plugin) { + mPlugins.remove(plugin); + mCustomWidgets.remove(getWidgetProvider(plugin.getProviderId())); + mWidgetsIdMap.remove(plugin.getProviderId()); + } + + /** + * Inject a callback function to refresh the widgets. + */ + public void setWidgetRefreshCallback(Consumer cb) { + mWidgetRefreshCallback = cb; + } + + /** + * Callback method to inform a plugin it's corresponding widget has been created. + */ + public void onViewCreated(LauncherAppWidgetHostView view) { + CustomAppWidgetProviderInfo info = (CustomAppWidgetProviderInfo) view.getAppWidgetInfo(); + CustomWidgetPlugin plugin = findPlugin(info.providerId); + if (plugin == null) return; + plugin.onViewCreated(view); + } + + /** + * Returns the list of custom widgets. + */ + @NonNull + public List getCustomWidgets() { + return mCustomWidgets; + } + + /** + * Returns the widget id for a specific provider. + */ + public int getWidgetIdForCustomProvider(@NonNull ComponentName provider) { + int index = mWidgetsIdMap.indexOfValue(provider); + if (index >= 0) { + return LauncherAppWidgetInfo.CUSTOM_WIDGET_ID - mWidgetsIdMap.keyAt(index); + } else { + return AppWidgetManager.INVALID_APPWIDGET_ID; + } + } + + /** + * Returns the widget provider in respect to given widget id. + */ + @Nullable + public LauncherAppWidgetProviderInfo getWidgetProvider(int widgetId) { + ComponentName cn = mWidgetsIdMap.get(LauncherAppWidgetInfo.CUSTOM_WIDGET_ID - widgetId); + for (LauncherAppWidgetProviderInfo info : mCustomWidgets) { + if (info.provider.equals(cn)) return info; + } + return null; + } + + private static CustomAppWidgetProviderInfo newInfo( + CustomWidgetPlugin plugin, Parcel parcel, Context context) { + int providerId = plugin.getProviderId(); + CustomAppWidgetProviderInfo info = new CustomAppWidgetProviderInfo( + parcel, false, providerId); + info.provider = new ComponentName( + context.getPackageName(), CLS_CUSTOM_WIDGET_PREFIX + providerId); + + info.label = plugin.getLabel(); + info.icon = plugin.getIcon(); + info.previewImage = plugin.getPreviewImage(); + info.resizeMode = plugin.getResizeMode(); + + info.spanX = plugin.getSpanX(); + info.spanY = plugin.getSpanY(); + info.minSpanX = plugin.getMinSpanX(); + info.minSpanY = plugin.getMinSpanY(); + return info; + } + + @Nullable + private CustomWidgetPlugin findPlugin(int providerId) { + return mPlugins.stream().filter((p) -> p.getProviderId() == providerId).findFirst() + .orElse(null); + } +} diff --git a/src/com/android/launcher3/widget/custom/CustomWidgetParser.java b/src/com/android/launcher3/widget/custom/CustomWidgetParser.java deleted file mode 100644 index 00720c4496..0000000000 --- a/src/com/android/launcher3/widget/custom/CustomWidgetParser.java +++ /dev/null @@ -1,142 +0,0 @@ -/* - * Copyright (C) 2017 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.widget.custom; - -import android.appwidget.AppWidgetManager; -import android.appwidget.AppWidgetProviderInfo; -import android.content.ComponentName; -import android.content.Context; -import android.content.res.TypedArray; -import android.content.res.XmlResourceParser; -import android.os.Parcel; -import android.os.Process; -import android.util.SparseArray; -import android.util.Xml; - -import com.android.launcher3.LauncherAppWidgetInfo; -import com.android.launcher3.LauncherAppWidgetProviderInfo; -import com.android.launcher3.R; - -import org.xmlpull.v1.XmlPullParser; -import org.xmlpull.v1.XmlPullParserException; - -import java.io.IOException; -import java.util.ArrayList; -import java.util.List; - -import static com.android.launcher3.LauncherAppWidgetProviderInfo.CLS_CUSTOM_WIDGET_PREFIX; - -/** - * Utility class to parse {@ink CustomAppWidgetProviderInfo} definitions from xml - */ -public class CustomWidgetParser { - - private static List sCustomWidgets; - private static SparseArray sWidgetsIdMap; - - public static List getCustomWidgets(Context context) { - if (sCustomWidgets == null) { - // Synchronization not needed as it it safe to load multiple times - parseCustomWidgets(context); - } - - return sCustomWidgets; - } - - public static int getWidgetIdForCustomProvider(Context context, ComponentName provider) { - if (sWidgetsIdMap == null) { - parseCustomWidgets(context); - } - int index = sWidgetsIdMap.indexOfValue(provider); - if (index >= 0) { - return LauncherAppWidgetInfo.CUSTOM_WIDGET_ID - sWidgetsIdMap.keyAt(index); - } else { - return AppWidgetManager.INVALID_APPWIDGET_ID; - } - } - - public static LauncherAppWidgetProviderInfo getWidgetProvider(Context context, int widgetId) { - if (sWidgetsIdMap == null || sCustomWidgets == null) { - parseCustomWidgets(context); - } - ComponentName cn = sWidgetsIdMap.get(LauncherAppWidgetInfo.CUSTOM_WIDGET_ID - widgetId); - for (LauncherAppWidgetProviderInfo info : sCustomWidgets) { - if (info.provider.equals(cn)) { - return info; - } - } - return null; - } - - private static void parseCustomWidgets(Context context) { - ArrayList widgets = new ArrayList<>(); - SparseArray idMap = new SparseArray<>(); - - List providers = AppWidgetManager.getInstance(context) - .getInstalledProvidersForProfile(Process.myUserHandle()); - if (providers.isEmpty()) { - sCustomWidgets = widgets; - sWidgetsIdMap = idMap; - return; - } - - Parcel parcel = Parcel.obtain(); - providers.get(0).writeToParcel(parcel, 0); - - try (XmlResourceParser parser = context.getResources().getXml(R.xml.custom_widgets)) { - final int depth = parser.getDepth(); - int type; - - while (((type = parser.next()) != XmlPullParser.END_TAG || - parser.getDepth() > depth) && type != XmlPullParser.END_DOCUMENT) { - if ((type == XmlPullParser.START_TAG) && "widget".equals(parser.getName())) { - TypedArray a = context.obtainStyledAttributes( - Xml.asAttributeSet(parser), R.styleable.CustomAppWidgetProviderInfo); - - parcel.setDataPosition(0); - CustomAppWidgetProviderInfo info = newInfo(a, parcel, context); - widgets.add(info); - a.recycle(); - - idMap.put(info.providerId, info.provider); - } - } - } catch (IOException | XmlPullParserException e) { - throw new RuntimeException(e); - } - parcel.recycle(); - sCustomWidgets = widgets; - sWidgetsIdMap = idMap; - } - - private static CustomAppWidgetProviderInfo newInfo(TypedArray a, Parcel parcel, Context context) { - int providerId = a.getInt(R.styleable.CustomAppWidgetProviderInfo_providerId, 0); - CustomAppWidgetProviderInfo info = new CustomAppWidgetProviderInfo(parcel, false, providerId); - info.provider = new ComponentName(context.getPackageName(), CLS_CUSTOM_WIDGET_PREFIX + providerId); - - info.label = a.getString(R.styleable.CustomAppWidgetProviderInfo_android_label); - info.initialLayout = a.getResourceId(R.styleable.CustomAppWidgetProviderInfo_android_initialLayout, 0); - info.icon = a.getResourceId(R.styleable.CustomAppWidgetProviderInfo_android_icon, 0); - info.previewImage = a.getResourceId(R.styleable.CustomAppWidgetProviderInfo_android_previewImage, 0); - info.resizeMode = a.getInt(R.styleable.CustomAppWidgetProviderInfo_android_resizeMode, 0); - - info.spanX = a.getInt(R.styleable.CustomAppWidgetProviderInfo_numColumns, 1); - info.spanY = a.getInt(R.styleable.CustomAppWidgetProviderInfo_numRows, 1); - info.minSpanX = a.getInt(R.styleable.CustomAppWidgetProviderInfo_numMinColumns, 1); - info.minSpanY = a.getInt(R.styleable.CustomAppWidgetProviderInfo_numMinRows, 1); - return info; - } -} diff --git a/src_build_config/BuildConfig.java b/src_build_config/BuildConfig.java index 36d7f4b362..49aadf61ff 100644 --- a/src_build_config/BuildConfig.java +++ b/src_build_config/BuildConfig.java @@ -18,4 +18,5 @@ package com.android.launcher3; public final class BuildConfig { public static final String APPLICATION_ID = "com.android.launcher3"; + public static final boolean DEBUG = false; } diff --git a/src_plugins/com/android/systemui/plugins/AppLaunchEventsPlugin.java b/src_plugins/com/android/systemui/plugins/AppLaunchEventsPlugin.java new file mode 100644 index 0000000000..15a0ffaa0c --- /dev/null +++ b/src_plugins/com/android/systemui/plugins/AppLaunchEventsPlugin.java @@ -0,0 +1,54 @@ +/* + * 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.systemui.plugins; + +import android.content.ComponentName; +import android.os.UserHandle; + +import com.android.systemui.plugins.annotations.ProvidesInterface; + +/** + * Plugin interface which sends app launch events. + */ +@ProvidesInterface(action = AppLaunchEventsPlugin.ACTION, version = AppLaunchEventsPlugin.VERSION) +public interface AppLaunchEventsPlugin extends Plugin { + String ACTION = "com.android.systemui.action.PLUGIN_APP_EVENTS"; + int VERSION = 1; + + /** + * Receives onStartShortcut event from + * {@link com.android.launcher3.appprediction.PredictionAppTracker}. + */ + void onStartShortcut(String packageName, String shortcutId, UserHandle user, String container); + + /** + * Receives onStartApp event from + * {@link com.android.launcher3.appprediction.PredictionAppTracker}. + */ + void onStartApp(ComponentName componentName, UserHandle user, String container); + + /** + * Receives onDismissApp event from + * {@link com.android.launcher3.appprediction.PredictionAppTracker}. + */ + void onDismissApp(ComponentName componentName, UserHandle user, String container); + + /** + * Receives onReturnedToHome event from + * {@link com.android.launcher3.appprediction.PredictionAppTracker}. + */ + void onReturnedToHome(); +} diff --git a/src_plugins/com/android/systemui/plugins/CustomWidgetPlugin.java b/src_plugins/com/android/systemui/plugins/CustomWidgetPlugin.java new file mode 100644 index 0000000000..77ad7ea30a --- /dev/null +++ b/src_plugins/com/android/systemui/plugins/CustomWidgetPlugin.java @@ -0,0 +1,88 @@ +/* + * 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.systemui.plugins; + +import android.appwidget.AppWidgetHostView; + +import com.android.systemui.plugins.annotations.ProvidesInterface; + +/** + * Implement this plugin interface to add a custom widget. + */ +@ProvidesInterface(action = CustomWidgetPlugin.ACTION, version = CustomWidgetPlugin.VERSION) +public interface CustomWidgetPlugin extends Plugin { + + String ACTION = "com.android.systemui.action.PLUGIN_CUSTOM_WIDGET"; + int VERSION = 1; + + /** + * An unique identifier for this widget. Must be a non-negative integer. + */ + int getProviderId(); + + /** + * The label to display to the user in the AppWidget picker. + */ + String getLabel(); + + /** + * A preview of what the AppWidget will look like after it's configured. + * If not supplied, the AppWidget's icon will be used. + */ + int getPreviewImage(); + + /** + * The icon to display for this AppWidget in the AppWidget picker. If not supplied in the + * xml, the application icon will be used. + */ + int getIcon(); + + /** + * The default width of the widget when added to a host, in dp. The widget will get + * at least this width, and will often be given more, depending on the host. + */ + int getSpanX(); + + /** + * The default height of the widget when added to a host, in dp. The widget will get + * at least this height, and will often be given more, depending on the host. + */ + int getSpanY(); + + /** + * Minimum width (in dp) which the widget can be resized to. This field has no effect if it + * is greater than minWidth or if horizontal resizing isn't enabled. + */ + int getMinSpanX(); + + /** + * Minimum height (in dp) which the widget can be resized to. This field has no effect if it + * is greater than minHeight or if vertical resizing isn't enabled. + */ + int getMinSpanY(); + + /** + * The rules by which a widget can be resized. + */ + int getResizeMode(); + + /** + * Notify the plugin that container of the widget has been rendered, where the custom widget + * can be attached to. + */ + void onViewCreated(AppWidgetHostView parent); +} diff --git a/src_plugins/com/android/systemui/plugins/HotseatPlugin.java b/src_plugins/com/android/systemui/plugins/HotseatPlugin.java new file mode 100644 index 0000000000..1264e0d3b0 --- /dev/null +++ b/src_plugins/com/android/systemui/plugins/HotseatPlugin.java @@ -0,0 +1,20 @@ +package com.android.systemui.plugins; + +import android.view.View; +import android.view.ViewGroup; + +import com.android.systemui.plugins.annotations.ProvidesInterface; + +/** + * Implement this plugin interface to add a sub-view in the Hotseat. + */ +@ProvidesInterface(action = HotseatPlugin.ACTION, version = HotseatPlugin.VERSION) +public interface HotseatPlugin extends Plugin { + String ACTION = "com.android.systemui.action.PLUGIN_HOTSEAT"; + int VERSION = 1; + + /** + * Creates a plugin view which will be added to the Hotseat. + */ + View createView(ViewGroup parent); +} diff --git a/src_plugins/com/android/systemui/plugins/OverlayPlugin.java b/src_plugins/com/android/systemui/plugins/OverlayPlugin.java new file mode 100644 index 0000000000..1edb69273f --- /dev/null +++ b/src_plugins/com/android/systemui/plugins/OverlayPlugin.java @@ -0,0 +1,34 @@ +/* + * 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.systemui.plugins; + +import android.app.Activity; + +import com.android.systemui.plugins.annotations.ProvidesInterface; +import com.android.systemui.plugins.shared.LauncherExterns; +import com.android.systemui.plugins.shared.LauncherOverlayManager; + +/** + * Implement this interface to add a -1 content on the home screen. + */ +@ProvidesInterface(action = OverlayPlugin.ACTION, version = OverlayPlugin.VERSION) +public interface OverlayPlugin extends Plugin { + String ACTION = "com.android.systemui.action.PLUGIN_LAUNCHER_OVERLAY"; + int VERSION = 1; + + LauncherOverlayManager createOverlayManager(Activity activity, LauncherExterns externs); + +} diff --git a/src_plugins/com/android/systemui/plugins/UserEventPlugin.java b/src_plugins/com/android/systemui/plugins/UserEventPlugin.java new file mode 100644 index 0000000000..0e3664ae3f --- /dev/null +++ b/src_plugins/com/android/systemui/plugins/UserEventPlugin.java @@ -0,0 +1,34 @@ +/* + * 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.systemui.plugins; + +import com.android.systemui.plugins.annotations.ProvidesInterface; + +/** + * Implement this plugin interface to access user event log on the device for prototype purpose. + * NOTE: plugin is for internal prototype only and is not visible in production environment. + */ +@ProvidesInterface(action = UserEventPlugin.ACTION, version = UserEventPlugin.VERSION) +public interface UserEventPlugin extends Plugin { + String ACTION = "com.android.launcher3.action.PLUGIN_USER_EVENT_LOG"; + int VERSION = 1; + + /** + * Callback to be triggered whenever an user event occurs. + */ + void onUserEvent(Object event); +} diff --git a/src/com/android/launcher3/LauncherExterns.java b/src_plugins/com/android/systemui/plugins/shared/LauncherExterns.java similarity index 63% rename from src/com/android/launcher3/LauncherExterns.java rename to src_plugins/com/android/systemui/plugins/shared/LauncherExterns.java index 272bbf6987..13e4999623 100644 --- a/src/com/android/launcher3/LauncherExterns.java +++ b/src_plugins/com/android/systemui/plugins/shared/LauncherExterns.java @@ -14,19 +14,36 @@ * limitations under the License. */ -package com.android.launcher3; +package com.android.systemui.plugins.shared; import android.content.SharedPreferences; +import com.android.systemui.plugins.shared.LauncherOverlayManager.LauncherOverlay; + /** * This interface defines the set of methods that the Launcher activity exposes. Methods * here should be safe to call from classes outside of com.android.launcher3.* */ public interface LauncherExterns { - boolean setLauncherCallbacks(LauncherCallbacks callbacks); - + /** + * Returns the shared main preference + */ SharedPreferences getSharedPrefs(); - void setLauncherOverlay(Launcher.LauncherOverlay overlay); + /** + * Returns the device specific preference + */ + SharedPreferences getDevicePrefs(); + + /** + * Sets the overlay on the target activity + */ + void setLauncherOverlay(LauncherOverlay overlay); + + /** + * Executes the command, next time the overlay is hidden + */ + void runOnOverlayHidden(Runnable runnable); + } diff --git a/src_plugins/com/android/systemui/plugins/shared/LauncherOverlayManager.java b/src_plugins/com/android/systemui/plugins/shared/LauncherOverlayManager.java new file mode 100644 index 0000000000..ac02ba4528 --- /dev/null +++ b/src_plugins/com/android/systemui/plugins/shared/LauncherOverlayManager.java @@ -0,0 +1,98 @@ +/* + * 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.systemui.plugins.shared; + +import android.app.Activity; +import android.app.Application; +import android.os.Bundle; + +import java.io.PrintWriter; + +/** + * Interface to control the overlay on Launcher + */ +public interface LauncherOverlayManager extends Application.ActivityLifecycleCallbacks { + + default void onDeviceProvideChanged() { } + + default void onAttachedToWindow() { } + default void onDetachedFromWindow() { } + + default void dump(String prefix, PrintWriter w) { } + + default void openOverlay() { } + + default void hideOverlay(boolean animate) { + hideOverlay(animate ? 200 : 0); + } + + default void hideOverlay(int duration) { } + + default boolean startSearch(byte[] config, Bundle extras) { + return false; + } + + @Override + default void onActivityCreated(Activity activity, Bundle bundle) { } + + @Override + default void onActivityStarted(Activity activity) { } + + @Override + default void onActivityResumed(Activity activity) { } + + @Override + default void onActivityPaused(Activity activity) { } + + @Override + default void onActivityStopped(Activity activity) { } + + @Override + default void onActivitySaveInstanceState(Activity activity, Bundle bundle) { } + + @Override + default void onActivityDestroyed(Activity activity) { } + + interface LauncherOverlay { + + /** + * Touch interaction leading to overscroll has begun + */ + void onScrollInteractionBegin(); + + /** + * Touch interaction related to overscroll has ended + */ + void onScrollInteractionEnd(); + + /** + * Scroll progress, between 0 and 100, when the user scrolls beyond the leftmost + * screen (or in the case of RTL, the rightmost screen). + */ + void onScrollChange(float progress, boolean rtl); + + /** + * Called when the launcher is ready to use the overlay + * @param callbacks A set of callbacks provided by Launcher in relation to the overlay + */ + void setOverlayCallbacks(LauncherOverlayCallbacks callbacks); + } + + interface LauncherOverlayCallbacks { + + void onScrollChanged(float progress); + } +} diff --git a/src_shortcuts_overrides/com/android/launcher3/model/LoaderResults.java b/src_shortcuts_overrides/com/android/launcher3/model/LoaderResults.java index 1710aef12b..789bfd8688 100644 --- a/src_shortcuts_overrides/com/android/launcher3/model/LoaderResults.java +++ b/src_shortcuts_overrides/com/android/launcher3/model/LoaderResults.java @@ -16,9 +16,8 @@ package com.android.launcher3.model; -import com.android.launcher3.AllAppsList; import com.android.launcher3.LauncherAppState; -import com.android.launcher3.LauncherModel.Callbacks; +import com.android.launcher3.model.BgDataModel.Callbacks; import com.android.launcher3.util.ComponentKey; import com.android.launcher3.widget.WidgetListRowEntry; diff --git a/src_shortcuts_overrides/com/android/launcher3/model/WidgetsModel.java b/src_shortcuts_overrides/com/android/launcher3/model/WidgetsModel.java index 7a7f828396..0f6ad27130 100644 --- a/src_shortcuts_overrides/com/android/launcher3/model/WidgetsModel.java +++ b/src_shortcuts_overrides/com/android/launcher3/model/WidgetsModel.java @@ -11,8 +11,6 @@ import android.os.UserHandle; import android.util.Log; import com.android.launcher3.AppFilter; -import com.android.launcher3.icons.ComponentWithLabel; -import com.android.launcher3.icons.IconCache; import com.android.launcher3.InvariantDeviceProfile; import com.android.launcher3.LauncherAppState; import com.android.launcher3.LauncherAppWidgetProviderInfo; @@ -22,6 +20,8 @@ import com.android.launcher3.compat.AppWidgetManagerCompat; import com.android.launcher3.compat.LauncherAppsCompat; import com.android.launcher3.compat.ShortcutConfigActivityInfo; import com.android.launcher3.config.FeatureFlags; +import com.android.launcher3.icons.ComponentWithLabel; +import com.android.launcher3.icons.IconCache; import com.android.launcher3.util.MultiHashMap; import com.android.launcher3.util.PackageUserKey; import com.android.launcher3.util.Preconditions; @@ -46,6 +46,9 @@ import androidx.annotation.Nullable; */ public class WidgetsModel { + // True is the widget support is disabled. + public static final boolean GO_DISABLE_WIDGETS = false; + private static final String TAG = "WidgetsModel"; private static final boolean DEBUG = false; diff --git a/src_shortcuts_overrides/com/android/launcher3/shortcuts/DeepShortcutManager.java b/src_shortcuts_overrides/com/android/launcher3/shortcuts/DeepShortcutManager.java index 09b1890491..57f416495f 100644 --- a/src_shortcuts_overrides/com/android/launcher3/shortcuts/DeepShortcutManager.java +++ b/src_shortcuts_overrides/com/android/launcher3/shortcuts/DeepShortcutManager.java @@ -43,35 +43,27 @@ public class DeepShortcutManager { | ShortcutQuery.FLAG_MATCH_MANIFEST | ShortcutQuery.FLAG_MATCH_PINNED; private static DeepShortcutManager sInstance; - private static final Object sInstanceLock = new Object(); public static DeepShortcutManager getInstance(Context context) { - synchronized (sInstanceLock) { - if (sInstance == null) { - sInstance = new DeepShortcutManager(context.getApplicationContext()); - } - return sInstance; + if (sInstance == null) { + sInstance = new DeepShortcutManager(context.getApplicationContext()); } + return sInstance; } private final LauncherApps mLauncherApps; - private boolean mWasLastCallSuccess; private DeepShortcutManager(Context context) { mLauncherApps = (LauncherApps) context.getSystemService(Context.LAUNCHER_APPS_SERVICE); } - public boolean wasLastCallSuccess() { - return mWasLastCallSuccess; - } - /** * Queries for the shortcuts with the package name and provided ids. * * This method is intended to get the full details for shortcuts when they are added or updated, * because we only get "key" fields in onShortcutsChanged(). */ - public List queryForFullDetails(String packageName, + public QueryResult queryForFullDetails(String packageName, List shortcutIds, UserHandle user) { return query(FLAG_GET_ALL, packageName, null, shortcutIds, user); } @@ -80,9 +72,9 @@ public class DeepShortcutManager { * Gets all the manifest and dynamic shortcuts associated with the given package and user, * to be displayed in the shortcuts container on long press. */ - public List queryForShortcutsContainer(@Nullable ComponentName activity, + public QueryResult queryForShortcutsContainer(@Nullable ComponentName activity, UserHandle user) { - if (activity == null) return Collections.EMPTY_LIST; + if (activity == null) return QueryResult.FAILURE; return query(ShortcutQuery.FLAG_MATCH_MANIFEST | ShortcutQuery.FLAG_MATCH_DYNAMIC, activity.getPackageName(), activity, null, user); } @@ -99,10 +91,8 @@ public class DeepShortcutManager { pinnedIds.remove(id); try { mLauncherApps.pinShortcuts(packageName, pinnedIds, user); - mWasLastCallSuccess = true; } catch (SecurityException|IllegalStateException e) { Log.w(TAG, "Failed to unpin shortcut", e); - mWasLastCallSuccess = false; } } @@ -118,10 +108,8 @@ public class DeepShortcutManager { pinnedIds.add(id); try { mLauncherApps.pinShortcuts(packageName, pinnedIds, user); - mWasLastCallSuccess = true; } catch (SecurityException|IllegalStateException e) { Log.w(TAG, "Failed to pin shortcut", e); - mWasLastCallSuccess = false; } } @@ -130,23 +118,18 @@ public class DeepShortcutManager { try { mLauncherApps.startShortcut(packageName, id, sourceBounds, startActivityOptions, user); - mWasLastCallSuccess = true; } catch (SecurityException|IllegalStateException e) { Log.e(TAG, "Failed to start shortcut", e); - mWasLastCallSuccess = false; } } public Drawable getShortcutIconDrawable(ShortcutInfo shortcutInfo, int density) { try { - Drawable icon = mLauncherApps.getShortcutIconDrawable(shortcutInfo, density); - mWasLastCallSuccess = true; - return icon; + return mLauncherApps.getShortcutIconDrawable(shortcutInfo, density); } catch (SecurityException|IllegalStateException e) { Log.e(TAG, "Failed to get shortcut icon", e); - mWasLastCallSuccess = false; + return null; } - return null; } /** @@ -154,20 +137,20 @@ public class DeepShortcutManager { * * If packageName is null, returns all pinned shortcuts regardless of package. */ - public List queryForPinnedShortcuts(String packageName, UserHandle user) { + public QueryResult queryForPinnedShortcuts(String packageName, UserHandle user) { return queryForPinnedShortcuts(packageName, null, user); } - public List queryForPinnedShortcuts(String packageName, - List shortcutIds, UserHandle user) { + public QueryResult queryForPinnedShortcuts(String packageName, List shortcutIds, + UserHandle user) { return query(ShortcutQuery.FLAG_MATCH_PINNED, packageName, null, shortcutIds, user); } - public List queryForAllShortcuts(UserHandle user) { + public QueryResult queryForAllShortcuts(UserHandle user) { return query(FLAG_GET_ALL, null, null, null, user); } - private List extractIds(List shortcuts) { + private static List extractIds(List shortcuts) { List shortcutIds = new ArrayList<>(shortcuts.size()); for (ShortcutInfo shortcut : shortcuts) { shortcutIds.add(shortcut.getId()); @@ -181,8 +164,8 @@ public class DeepShortcutManager { * * TODO: Use the cache to optimize this so we don't make an RPC every time. */ - private List query(int flags, String packageName, - ComponentName activity, List shortcutIds, UserHandle user) { + private QueryResult query(int flags, String packageName, ComponentName activity, + List shortcutIds, UserHandle user) { ShortcutQuery q = new ShortcutQuery(); q.setQueryFlags(flags); if (packageName != null) { @@ -190,18 +173,12 @@ public class DeepShortcutManager { q.setActivity(activity); q.setShortcutIds(shortcutIds); } - List shortcutInfos = null; try { - shortcutInfos = mLauncherApps.getShortcuts(q, user); - mWasLastCallSuccess = true; + return new QueryResult(mLauncherApps.getShortcuts(q, user)); } catch (SecurityException|IllegalStateException e) { Log.e(TAG, "Failed to query for shortcuts", e); - mWasLastCallSuccess = false; + return QueryResult.FAILURE; } - if (shortcutInfos == null) { - return Collections.EMPTY_LIST; - } - return shortcutInfos; } public boolean hasHostPermission() { @@ -212,4 +189,25 @@ public class DeepShortcutManager { } return false; } + + public static class QueryResult extends ArrayList { + + static QueryResult FAILURE = new QueryResult(); + + private final boolean mWasSuccess; + + QueryResult(List result) { + super(result == null ? Collections.emptyList() : result); + mWasSuccess = true; + } + + QueryResult() { + mWasSuccess = false; + } + + + public boolean wasSuccess() { + return mWasSuccess; + } + } } diff --git a/src_ui_overrides/com/android/launcher3/uioverrides/DejankBinderTracker.java b/src_ui_overrides/com/android/launcher3/uioverrides/DejankBinderTracker.java new file mode 100644 index 0000000000..47f6ac6f75 --- /dev/null +++ b/src_ui_overrides/com/android/launcher3/uioverrides/DejankBinderTracker.java @@ -0,0 +1,57 @@ +/** + * 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.uioverrides; + +import android.os.IBinder; + +import java.util.function.BiConsumer; +import java.util.function.Supplier; + +/** + * A binder proxy transaction listener for tracking non-whitelisted binder calls. + */ +public class DejankBinderTracker { + public static void whitelistIpcs(Runnable runnable) {} + + public static T whitelistIpcs(Supplier supplier) { + return null; + } + + public static void allowBinderTrackingInTests() {} + + public static void disallowBinderTrackingInTests() {} + + public DejankBinderTracker(BiConsumer unexpectedTransactionCallback) { } + + public void startTracking() {} + + public void stopTracking() {} + + public Object onTransactStarted(IBinder binder, int transactionCode, int flags) { + return null; + } + + public Object onTransactStarted(IBinder binder, int transactionCode) { + return null; + } + + public void onTransactEnded(Object session) {} + + public static boolean isMainThread() { + return true; + } +} diff --git a/src_ui_overrides/com/android/launcher3/uioverrides/TogglableFlag.java b/src_ui_overrides/com/android/launcher3/uioverrides/TogglableFlag.java index 60f12d82a7..d7bb2939e2 100644 --- a/src_ui_overrides/com/android/launcher3/uioverrides/TogglableFlag.java +++ b/src_ui_overrides/com/android/launcher3/uioverrides/TogglableFlag.java @@ -17,7 +17,8 @@ package com.android.launcher3.uioverrides; import android.content.Context; -import com.android.launcher3.config.BaseFlags.BaseTogglableFlag; + +import com.android.launcher3.config.FeatureFlags.BaseTogglableFlag; public class TogglableFlag extends BaseTogglableFlag { diff --git a/tests/Android.mk b/tests/Android.mk index 0c412415c2..b5c1dae612 100644 --- a/tests/Android.mk +++ b/tests/Android.mk @@ -27,16 +27,17 @@ LOCAL_STATIC_JAVA_LIBRARIES := \ ifneq (,$(wildcard frameworks/base)) else - LOCAL_STATIC_JAVA_LIBRARIES += libSharedSystemUI + LOCAL_STATIC_JAVA_LIBRARIES += SystemUISharedLib LOCAL_SRC_FILES := $(call all-java-files-under, tapl) \ + ../quickstep/src/com/android/launcher3/uioverrides/DejankBinderTracker.java \ ../src/com/android/launcher3/ResourceUtils.java \ ../src/com/android/launcher3/util/SecureSettingsObserver.java \ ../src/com/android/launcher3/testing/TestProtocol.java endif LOCAL_MODULE := ub-launcher-aosp-tapl -LOCAL_SDK_VERSION := current +LOCAL_SDK_VERSION := system_current include $(BUILD_STATIC_JAVA_LIBRARY) diff --git a/tests/src/com/android/launcher3/compat/PromiseIconUiTest.java b/tests/src/com/android/launcher3/compat/PromiseIconUiTest.java index efbd9c99c3..7ef946d4c4 100644 --- a/tests/src/com/android/launcher3/compat/PromiseIconUiTest.java +++ b/tests/src/com/android/launcher3/compat/PromiseIconUiTest.java @@ -48,7 +48,6 @@ public class PromiseIconUiTest extends AbstractLauncherUiTest { public void setUp() throws Exception { super.setUp(); mDevice.pressHome(); - waitForLauncherCondition("Launcher didn't start", launcher -> launcher != null); waitForState("Launcher internal state didn't switch to Home", LauncherState.NORMAL); mSessionId = -1; } diff --git a/tests/src/com/android/launcher3/model/LoaderCursorTest.java b/tests/src/com/android/launcher3/model/LoaderCursorTest.java index 7d60ad65bb..64df8e0e95 100644 --- a/tests/src/com/android/launcher3/model/LoaderCursorTest.java +++ b/tests/src/com/android/launcher3/model/LoaderCursorTest.java @@ -15,9 +15,9 @@ import com.android.launcher3.icons.IconCache; import com.android.launcher3.InvariantDeviceProfile; import com.android.launcher3.ItemInfo; import com.android.launcher3.LauncherAppState; -import com.android.launcher3.Utilities; import com.android.launcher3.compat.LauncherAppsCompat; import com.android.launcher3.icons.BitmapInfo; +import com.android.launcher3.util.PackageManagerHelper; import org.junit.Before; import org.junit.Test; @@ -115,7 +115,7 @@ public class LoaderCursorTest { WorkspaceItemInfo info = mLoaderCursor.getAppShortcutInfo( new Intent().setComponent(cn), false /* allowMissingTarget */, true); assertNotNull(info); - assertTrue(Utilities.isLauncherAppTarget(info.intent)); + assertTrue(PackageManagerHelper.isLauncherAppTarget(info.intent)); } @Test @@ -127,7 +127,7 @@ public class LoaderCursorTest { WorkspaceItemInfo info = mLoaderCursor.getAppShortcutInfo( new Intent().setComponent(cn), true /* allowMissingTarget */, true); assertNotNull(info); - assertTrue(Utilities.isLauncherAppTarget(info.intent)); + assertTrue(PackageManagerHelper.isLauncherAppTarget(info.intent)); } @Test diff --git a/tests/src/com/android/launcher3/ui/AbstractLauncherUiTest.java b/tests/src/com/android/launcher3/ui/AbstractLauncherUiTest.java index dc890bb005..1fac7081b7 100644 --- a/tests/src/com/android/launcher3/ui/AbstractLauncherUiTest.java +++ b/tests/src/com/android/launcher3/ui/AbstractLauncherUiTest.java @@ -19,6 +19,7 @@ import static androidx.test.InstrumentationRegistry.getInstrumentation; import static com.android.launcher3.tapl.LauncherInstrumentation.ContainerType; import static com.android.launcher3.ui.TaplTestsLauncher3.getAppPackageName; +import static com.android.launcher3.util.Executors.MAIN_EXECUTOR; import static org.junit.Assert.assertTrue; @@ -29,6 +30,7 @@ import android.content.ComponentName; import android.content.Context; import android.content.Intent; import android.content.IntentFilter; +import android.content.pm.ActivityInfo; import android.content.pm.LauncherActivityInfo; import android.content.pm.PackageManager; import android.os.Process; @@ -47,7 +49,6 @@ import com.android.launcher3.LauncherModel; import com.android.launcher3.LauncherSettings; import com.android.launcher3.LauncherState; import com.android.launcher3.LauncherStateManager; -import com.android.launcher3.MainThreadExecutor; import com.android.launcher3.Utilities; import com.android.launcher3.compat.LauncherAppsCompat; import com.android.launcher3.model.AppLaunchTracker; @@ -55,11 +56,13 @@ import com.android.launcher3.tapl.LauncherInstrumentation; import com.android.launcher3.tapl.TestHelpers; import com.android.launcher3.testcomponent.TestCommandReceiver; import com.android.launcher3.testing.TestProtocol; +import com.android.launcher3.util.LooperExecutor; import com.android.launcher3.util.PackageManagerHelper; import com.android.launcher3.util.Wait; import com.android.launcher3.util.rule.FailureWatcher; import com.android.launcher3.util.rule.LauncherActivityRule; import com.android.launcher3.util.rule.ShellCommandRule; +import com.android.launcher3.util.rule.TestStabilityRule; import org.junit.After; import org.junit.Before; @@ -89,7 +92,7 @@ public abstract class AbstractLauncherUiTest { public static final long DEFAULT_UI_TIMEOUT = 60000; // b/136278866 private static final String TAG = "AbstractLauncherUiTest"; - protected MainThreadExecutor mMainThreadExecutor = new MainThreadExecutor(); + protected LooperExecutor mMainThreadExecutor = MAIN_EXECUTOR; protected final UiDevice mDevice = UiDevice.getInstance(getInstrumentation()); protected final LauncherInstrumentation mLauncher = new LauncherInstrumentation(getInstrumentation()); @@ -155,7 +158,8 @@ public abstract class AbstractLauncherUiTest { @Rule public TestRule mOrderSensitiveRules = RuleChain. - outerRule(mActivityMonitor). + outerRule(new TestStabilityRule()). + around(mActivityMonitor). around(getRulesInsideActivityMonitor()); public UiDevice getDevice() { @@ -343,14 +347,14 @@ public abstract class AbstractLauncherUiTest { } } - protected void startAppFast(String packageName) { + public static void startAppFast(String packageName) { startIntent( getInstrumentation().getContext().getPackageManager().getLaunchIntentForPackage( packageName), By.pkg(packageName).depth(0)); } - protected void startTestActivity(int activityNumber) { + public static void startTestActivity(int activityNumber) { final String packageName = getAppPackageName(); final Intent intent = getInstrumentation().getContext().getPackageManager(). getLaunchIntentForPackage(packageName); @@ -359,19 +363,25 @@ public abstract class AbstractLauncherUiTest { startIntent(intent, By.pkg(packageName).text("TestActivity" + activityNumber)); } - private void startIntent(Intent intent, BySelector selector) { + private static void startIntent(Intent intent, BySelector selector) { intent.addCategory(Intent.CATEGORY_LAUNCHER); intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_CLEAR_TASK); getInstrumentation().getTargetContext().startActivity(intent); assertTrue("App didn't start: " + selector, - mDevice.wait(Until.hasObject(selector), DEFAULT_UI_TIMEOUT)); + UiDevice.getInstance(getInstrumentation()) + .wait(Until.hasObject(selector), DEFAULT_UI_TIMEOUT)); } - public static String resolveSystemApp(String category) { + public static ActivityInfo resolveSystemAppInfo(String category) { return getInstrumentation().getContext().getPackageManager().resolveActivity( new Intent(Intent.ACTION_MAIN).addCategory(category), PackageManager.MATCH_SYSTEM_ONLY). - activityInfo.packageName; + activityInfo; + } + + + public static String resolveSystemApp(String category) { + return resolveSystemAppInfo(category).packageName; } protected void closeLauncherActivity() { diff --git a/tests/src/com/android/launcher3/ui/TaplTestsLauncher3.java b/tests/src/com/android/launcher3/ui/TaplTestsLauncher3.java index c2a3c1c524..2cf6c2ba7f 100644 --- a/tests/src/com/android/launcher3/ui/TaplTestsLauncher3.java +++ b/tests/src/com/android/launcher3/ui/TaplTestsLauncher3.java @@ -24,6 +24,8 @@ import static org.junit.Assert.assertNotNull; import static org.junit.Assert.assertNull; import static org.junit.Assert.assertTrue; +import android.util.Log; + import androidx.test.filters.LargeTest; import androidx.test.runner.AndroidJUnit4; @@ -34,9 +36,9 @@ import com.android.launcher3.tapl.AllApps; import com.android.launcher3.tapl.AppIcon; import com.android.launcher3.tapl.AppIconMenu; import com.android.launcher3.tapl.AppIconMenuItem; -import com.android.launcher3.tapl.TestHelpers; import com.android.launcher3.tapl.Widgets; import com.android.launcher3.tapl.Workspace; +import com.android.launcher3.util.rule.TestStabilityRule.Stability; import com.android.launcher3.views.OptionsPopupView; import com.android.launcher3.widget.WidgetsFullSheet; import com.android.launcher3.widget.WidgetsRecyclerView; @@ -343,4 +345,10 @@ public class TaplTestsLauncher3 extends AbstractLauncherUiTest { public static String getAppPackageName() { return getInstrumentation().getContext().getPackageName(); } + + @Test + @Stability + public void testTestStabilityAttribute() { + Log.d("TestStabilityRule", "Hello world!"); + } } diff --git a/tests/src/com/android/launcher3/ui/widget/AddConfigWidgetTest.java b/tests/src/com/android/launcher3/ui/widget/AddConfigWidgetTest.java index 7f427b303b..3f35a3a73a 100644 --- a/tests/src/com/android/launcher3/ui/widget/AddConfigWidgetTest.java +++ b/tests/src/com/android/launcher3/ui/widget/AddConfigWidgetTest.java @@ -70,12 +70,14 @@ public class AddConfigWidgetTest extends AbstractLauncherUiTest { @Test @PortraitLandscape + @org.junit.Ignore public void testWidgetConfig() throws Throwable { runTest(true); } @Test @PortraitLandscape + @org.junit.Ignore public void testConfigCancelled() throws Throwable { runTest(false); } diff --git a/tests/src/com/android/launcher3/ui/widget/AddWidgetTest.java b/tests/src/com/android/launcher3/ui/widget/AddWidgetTest.java index cb586acebb..1edce22ec6 100644 --- a/tests/src/com/android/launcher3/ui/widget/AddWidgetTest.java +++ b/tests/src/com/android/launcher3/ui/widget/AddWidgetTest.java @@ -46,6 +46,7 @@ public class AddWidgetTest extends AbstractLauncherUiTest { @Test @PortraitLandscape + @org.junit.Ignore public void testDragIcon() throws Throwable { clearHomescreen(); mDevice.pressHome(); diff --git a/tests/src/com/android/launcher3/ui/widget/BindWidgetTest.java b/tests/src/com/android/launcher3/ui/widget/BindWidgetTest.java index 3a7df64e81..e6348d9c02 100644 --- a/tests/src/com/android/launcher3/ui/widget/BindWidgetTest.java +++ b/tests/src/com/android/launcher3/ui/widget/BindWidgetTest.java @@ -44,6 +44,7 @@ import com.android.launcher3.tapl.Workspace; import com.android.launcher3.ui.AbstractLauncherUiTest; import com.android.launcher3.ui.TestViewHelpers; import com.android.launcher3.util.ContentWriter; +import com.android.launcher3.util.PackageUserKey; import com.android.launcher3.util.rule.ShellCommandRule; import com.android.launcher3.widget.PendingAddWidgetInfo; import com.android.launcher3.widget.WidgetHostViewLoader; @@ -54,7 +55,9 @@ import org.junit.Rule; import org.junit.Test; import org.junit.runner.RunWith; +import java.util.HashSet; import java.util.Set; +import java.util.function.Consumer; /** * Tests for bind widget flow. @@ -326,9 +329,12 @@ public class BindWidgetTest extends AbstractLauncherUiTest { int count = 0; String pkg = invalidPackage; - Set activePackage = getOnUiThread(() -> - PackageInstallerCompat.getInstance(mTargetContext) - .updateAndGetActiveSessionCache().keySet()); + Set activePackage = getOnUiThread(() -> { + Set packages = new HashSet<>(); + PackageInstallerCompat.getInstance(mTargetContext).updateAndGetActiveSessionCache() + .keySet().forEach(packageUserKey -> packages.add(packageUserKey.mPackageName)); + return packages; + }); while(true) { try { mTargetContext.getPackageManager().getPackageInfo( diff --git a/tests/src/com/android/launcher3/util/Condition.java b/tests/src/com/android/launcher3/util/Condition.java index b564a1a873..d85dd3a2d7 100644 --- a/tests/src/com/android/launcher3/util/Condition.java +++ b/tests/src/com/android/launcher3/util/Condition.java @@ -1,8 +1,8 @@ package com.android.launcher3.util; -import androidx.test.uiautomator.UiObject2; +import static com.android.launcher3.util.Executors.MAIN_EXECUTOR; -import com.android.launcher3.MainThreadExecutor; +import androidx.test.uiautomator.UiObject2; import java.util.concurrent.CountDownLatch; import java.util.concurrent.TimeUnit; @@ -16,7 +16,7 @@ public interface Condition { * Converts the condition to be run on UI thread. */ static Condition runOnUiThread(final Condition condition) { - final MainThreadExecutor executor = new MainThreadExecutor(); + final LooperExecutor executor = MAIN_EXECUTOR; return () -> { final AtomicBoolean value = new AtomicBoolean(false); final Throwable[] exceptions = new Throwable[1]; diff --git a/tests/src/com/android/launcher3/util/RaceConditionReproducer.java b/tests/src/com/android/launcher3/util/RaceConditionReproducer.java index 0235f95d0b..8f89173203 100644 --- a/tests/src/com/android/launcher3/util/RaceConditionReproducer.java +++ b/tests/src/com/android/launcher3/util/RaceConditionReproducer.java @@ -16,6 +16,7 @@ package com.android.launcher3.util; +import static com.android.launcher3.util.Executors.createAndStartNewLooper; import static com.android.launcher3.util.RaceConditionTracker.ENTER_POSTFIX; import static com.android.launcher3.util.RaceConditionTracker.EXIT_POSTFIX; @@ -23,7 +24,6 @@ import static org.junit.Assert.assertTrue; import static org.junit.Assert.fail; import android.os.Handler; -import android.os.HandlerThread; import android.util.Log; import java.util.ArrayList; @@ -72,9 +72,7 @@ public class RaceConditionReproducer implements RaceConditionTracker.EventProces private static final Handler POSTPONED_EVENT_RESUME_HANDLER = createEventResumeHandler(); private static Handler createEventResumeHandler() { - final HandlerThread thread = new HandlerThread("RaceConditionEventResumer"); - thread.start(); - return new Handler(thread.getLooper()); + return new Handler(createAndStartNewLooper("RaceConditionEventResumer")); } /** diff --git a/tests/src/com/android/launcher3/util/rule/LauncherActivityRule.java b/tests/src/com/android/launcher3/util/rule/LauncherActivityRule.java index 62fe26d13b..6a6ec3e41d 100644 --- a/tests/src/com/android/launcher3/util/rule/LauncherActivityRule.java +++ b/tests/src/com/android/launcher3/util/rule/LauncherActivityRule.java @@ -16,16 +16,10 @@ package com.android.launcher3.util.rule; import android.app.Activity; -import android.app.Application; -import android.app.Application.ActivityLifecycleCallbacks; -import android.os.Bundle; - -import androidx.test.InstrumentationRegistry; import com.android.launcher3.Launcher; import com.android.launcher3.Workspace.ItemOperator; -import org.junit.rules.TestRule; import org.junit.runner.Description; import org.junit.runners.model.Statement; @@ -34,17 +28,23 @@ import java.util.concurrent.Callable; /** * Test rule to get the current Launcher activity. */ -public class LauncherActivityRule implements TestRule { +public class LauncherActivityRule extends SimpleActivityRule { - private Launcher mActivity; + public LauncherActivityRule() { + super(Launcher.class); + } @Override public Statement apply(Statement base, Description description) { - return new MyStatement(base); - } - public Launcher getActivity() { - return mActivity; + return new MyStatement(base) { + @Override + public void onActivityStarted(Activity activity) { + if (activity instanceof Launcher) { + ((Launcher) activity).getRotationHelper().forceAllowRotationForTesting(true); + } + } + }; } public Callable itemExists(final ItemOperator op) { @@ -56,62 +56,4 @@ public class LauncherActivityRule implements TestRule { return launcher.getWorkspace().getFirstMatch(op) != null; }; } - - private class MyStatement extends Statement implements ActivityLifecycleCallbacks { - - private final Statement mBase; - - public MyStatement(Statement base) { - mBase = base; - } - - @Override - public void evaluate() throws Throwable { - Application app = (Application) - InstrumentationRegistry.getTargetContext().getApplicationContext(); - app.registerActivityLifecycleCallbacks(this); - try { - mBase.evaluate(); - } finally { - app.unregisterActivityLifecycleCallbacks(this); - } - } - - @Override - public void onActivityCreated(Activity activity, Bundle bundle) { - if (activity instanceof Launcher) { - mActivity = (Launcher) activity; - } - } - - @Override - public void onActivityStarted(Activity activity) { - if (activity instanceof Launcher) { - mActivity.getRotationHelper().forceAllowRotationForTesting(true); - } - } - - @Override - public void onActivityResumed(Activity activity) { - } - - @Override - public void onActivityPaused(Activity activity) { - } - - @Override - public void onActivityStopped(Activity activity) { - } - - @Override - public void onActivitySaveInstanceState(Activity activity, Bundle bundle) { - } - - @Override - public void onActivityDestroyed(Activity activity) { - if (activity == mActivity) { - mActivity = null; - } - } - } } diff --git a/tests/src/com/android/launcher3/util/rule/SimpleActivityRule.java b/tests/src/com/android/launcher3/util/rule/SimpleActivityRule.java new file mode 100644 index 0000000000..33a6cf9766 --- /dev/null +++ b/tests/src/com/android/launcher3/util/rule/SimpleActivityRule.java @@ -0,0 +1,104 @@ +/* + * 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.rule; + +import android.app.Activity; +import android.app.Application; +import android.app.Application.ActivityLifecycleCallbacks; +import android.os.Bundle; + +import androidx.test.InstrumentationRegistry; + +import org.junit.rules.TestRule; +import org.junit.runner.Description; +import org.junit.runners.model.Statement; + +/** + * Test rule to get the current activity. + */ +public class SimpleActivityRule implements TestRule { + + private final Class mClass; + private T mActivity; + + public SimpleActivityRule(Class clazz) { + mClass = clazz; + } + + @Override + public Statement apply(Statement base, Description description) { + return new MyStatement(base); + } + + public T getActivity() { + return mActivity; + } + + protected class MyStatement extends Statement implements ActivityLifecycleCallbacks { + + private final Statement mBase; + + public MyStatement(Statement base) { + mBase = base; + } + + @Override + public void evaluate() throws Throwable { + Application app = (Application) + InstrumentationRegistry.getTargetContext().getApplicationContext(); + app.registerActivityLifecycleCallbacks(this); + try { + mBase.evaluate(); + } finally { + app.unregisterActivityLifecycleCallbacks(this); + } + } + + @Override + public void onActivityCreated(Activity activity, Bundle bundle) { + if (activity != null && mClass.isInstance(activity)) { + mActivity = (T) activity; + } + } + + @Override + public void onActivityStarted(Activity activity) { + } + + @Override + public void onActivityResumed(Activity activity) { + } + + @Override + public void onActivityPaused(Activity activity) { + } + + @Override + public void onActivityStopped(Activity activity) { + } + + @Override + public void onActivitySaveInstanceState(Activity activity, Bundle bundle) { + } + + @Override + public void onActivityDestroyed(Activity activity) { + if (activity == mActivity) { + mActivity = null; + } + } + } +} diff --git a/tests/src/com/android/launcher3/util/rule/TestStabilityRule.java b/tests/src/com/android/launcher3/util/rule/TestStabilityRule.java new file mode 100644 index 0000000000..8391ae713c --- /dev/null +++ b/tests/src/com/android/launcher3/util/rule/TestStabilityRule.java @@ -0,0 +1,137 @@ +/* + * 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.rule; + +import static androidx.test.InstrumentationRegistry.getInstrumentation; + +import android.os.Build; +import android.util.Log; + +import androidx.test.uiautomator.UiDevice; + +import org.junit.rules.TestRule; +import org.junit.runner.Description; +import org.junit.runners.model.Statement; + +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; +import java.util.regex.Matcher; +import java.util.regex.Pattern; + +public class TestStabilityRule implements TestRule { + private static final String TAG = "TestStabilityRule"; + private static final Pattern LAUNCHER_BUILD = + Pattern.compile("^(" + + "(?BuildFromAndroidStudio)|" + + "(?[0-9]+-eng\\.[a-z]+\\.[0-9]+\\.[0-9]+)|" + + "(?[0-9]+-P[0-9]+)|" + + "(?[0-9]+-[0-9]+|" + + "(?[0-9]+))" + + ")$"); + private static final Pattern PLATFORM_BUILD = + Pattern.compile("^(" + + "(?eng\\.[a-z]+\\.[0-9]+\\.[0-9]+)|" + + "(?P[0-9]+)|" + + "(?[0-9]+)" + + ")$"); + + @Retention(RetentionPolicy.RUNTIME) + @Target(ElementType.METHOD) + public @interface Stability { + } + + @Override + public Statement apply(Statement base, Description description) { + if (description.getAnnotation(Stability.class) != null) { + return new Statement() { + @Override + public void evaluate() throws Throwable { + final String launcherVersion = + getInstrumentation(). + getContext(). + getPackageManager(). + getPackageInfo( + UiDevice.getInstance(getInstrumentation()). + getLauncherPackageName(), + 0). + versionName; + + final Matcher launcherBuildMatcher = LAUNCHER_BUILD.matcher(launcherVersion); + + boolean launcherLocalBuild = false; + boolean launcherUnbundledPresubmit = false; + boolean launcherUnbundledPostsubmit = false; + boolean launcherPlatform = false; + + if (!launcherBuildMatcher.find()) { + Log.e(TAG, "Match not found"); + } else if (launcherBuildMatcher.group("androidStudio") != null + || launcherBuildMatcher.group("commandLine") != null) { + launcherLocalBuild = true; + } else if (launcherBuildMatcher.group("presubmit") != null) { + launcherUnbundledPresubmit = true; + } else if (launcherBuildMatcher.group("postsubmit") != null) { + launcherUnbundledPostsubmit = true; + } else if (launcherBuildMatcher.group("platform") != null) { + launcherPlatform = true; + } else { + Log.e(TAG, "ERROR1"); + } + + boolean platformLocalBuild = false; + boolean platformPresubmit = false; + boolean platformPostsubmit = false; + + final String platformVersion = Build.VERSION.INCREMENTAL; + final Matcher platformBuildMatcher = PLATFORM_BUILD.matcher(platformVersion); + if (!platformBuildMatcher.find()) { + Log.e(TAG, "Match not found"); + } else if (platformBuildMatcher.group("commandLine") != null) { + platformLocalBuild = true; + } else if (platformBuildMatcher.group("presubmit") != null) { + platformPresubmit = true; + } else if (platformBuildMatcher.group("postsubmit") != null) { + platformPostsubmit = true; + } else { + Log.e(TAG, "ERROR2"); + } + + Log.d(TAG, "Launcher: " + launcherVersion + ", platform: " + platformVersion); + + if (launcherLocalBuild && (platformLocalBuild || platformPostsubmit)) { + Log.d(TAG, "LOCAL RUN"); + } else if (launcherUnbundledPresubmit && platformPostsubmit) { + Log.d(TAG, "UNBUNDLED PRESUBMIT"); + } else if (launcherUnbundledPostsubmit && platformPostsubmit) { + Log.d(TAG, "UNBUNDLED POSTSUBMIT"); + } else if (launcherPlatform && platformPresubmit) { + Log.d(TAG, "PLATFORM PRESUBMIT"); + } else if (launcherPlatform && platformPostsubmit) { + Log.d(TAG, "PLATFORM POSTSUBMIT"); + } else { + Log.e(TAG, "ERROR3"); + } + + base.evaluate(); + } + }; + } else { + return base; + } + } +} diff --git a/tests/tapl/com/android/launcher3/tapl/BaseOverview.java b/tests/tapl/com/android/launcher3/tapl/BaseOverview.java index 25e6e8c8f7..eaa21aeebf 100644 --- a/tests/tapl/com/android/launcher3/tapl/BaseOverview.java +++ b/tests/tapl/com/android/launcher3/tapl/BaseOverview.java @@ -63,10 +63,10 @@ public class BaseOverview extends LauncherInstrumentation.VisibleContainer { /** * Dismissed all tasks by scrolling to Clear-all button and pressing it. */ - public Workspace dismissAllTasks() { + public void dismissAllTasks() { try (LauncherInstrumentation.Closable c = mLauncher.addContextLayer( "dismissing all tasks")) { - final BySelector clearAllSelector = mLauncher.getLauncherObjectSelector("clear_all"); + final BySelector clearAllSelector = mLauncher.getOverviewObjectSelector("clear_all"); for (int i = 0; i < FLINGS_FOR_DISMISS_LIMIT && !verifyActiveContainer().hasObject(clearAllSelector); @@ -75,10 +75,6 @@ public class BaseOverview extends LauncherInstrumentation.VisibleContainer { } mLauncher.waitForObjectInContainer(verifyActiveContainer(), clearAllSelector).click(); - try (LauncherInstrumentation.Closable c1 = mLauncher.addContextLayer( - "dismissed all tasks")) { - return new Workspace(mLauncher); - } } } @@ -109,7 +105,7 @@ public class BaseOverview extends LauncherInstrumentation.VisibleContainer { "want to get current task")) { verifyActiveContainer(); final List taskViews = mLauncher.getDevice().findObjects( - mLauncher.getLauncherObjectSelector("snapshot")); + mLauncher.getOverviewObjectSelector("snapshot")); mLauncher.assertNotEquals("Unable to find a task", 0, taskViews.size()); // taskViews contains up to 3 task views: the 'main' (having the widest visible diff --git a/tests/tapl/com/android/launcher3/tapl/LauncherInstrumentation.java b/tests/tapl/com/android/launcher3/tapl/LauncherInstrumentation.java index c6e7daceb3..383c3cef31 100644 --- a/tests/tapl/com/android/launcher3/tapl/LauncherInstrumentation.java +++ b/tests/tapl/com/android/launcher3/tapl/LauncherInstrumentation.java @@ -725,7 +725,7 @@ public final class LauncherInstrumentation { @NonNull UiObject2 waitForFallbackLauncherObject(String resName) { - return waitForObjectBySelector(getFallbackLauncherObjectSelector(resName)); + return waitForObjectBySelector(getOverviewObjectSelector(resName)); } private UiObject2 waitForObjectBySelector(BySelector selector) { @@ -742,7 +742,7 @@ public final class LauncherInstrumentation { return By.res(getLauncherPackageName(), resName); } - BySelector getFallbackLauncherObjectSelector(String resName) { + BySelector getOverviewObjectSelector(String resName) { return By.res(getOverviewPackageName(), resName); } @@ -961,4 +961,12 @@ public final class LauncherInstrumentation { return getTestInfo(TestProtocol.REQUEST_TOTAL_PSS_KB). getInt(TestProtocol.TEST_INFO_RESPONSE_FIELD); } + + public void produceJavaLeak() { + getTestInfo(TestProtocol.REQUEST_JAVA_LEAK); + } + + public void produceNativeLeak() { + getTestInfo(TestProtocol.REQUEST_NATIVE_LEAK); + } } \ No newline at end of file diff --git a/tests/tapl/com/android/launcher3/tapl/Overview.java b/tests/tapl/com/android/launcher3/tapl/Overview.java index da68da3ba6..4f8aeb1123 100644 --- a/tests/tapl/com/android/launcher3/tapl/Overview.java +++ b/tests/tapl/com/android/launcher3/tapl/Overview.java @@ -67,4 +67,13 @@ public final class Overview extends BaseOverview { } } } + + @Override + public void dismissAllTasks() { + super.dismissAllTasks(); + try (LauncherInstrumentation.Closable c1 = mLauncher.addContextLayer( + "dismissed all tasks")) { + new Workspace(mLauncher); + } + } }