Removing ext_test directory
Except for a few methods, most of the testing code was already in the main
codebase. Maintaining the additional source directory adds unnecessary overhead
Bug: 330920490
Test: Presubmit
Flag: None
Change-Id: I56c43ab7a4869b26858d622d0b8b14f286d50e90
diff --git a/Android.bp b/Android.bp
index cdada0a..f45394a 100644
--- a/Android.bp
+++ b/Android.bp
@@ -17,7 +17,7 @@
default_applicable_licenses: ["Android-Apache-2.0"],
}
-min_launcher3_sdk_version = "26"
+min_launcher3_sdk_version = "30"
// Common source files used to build launcher (java and kotlin)
// All sources are split so they can be reused in many other libraries/apps in other folders
@@ -69,22 +69,6 @@
],
}
-filegroup {
- name: "launcher-ext_tests",
- srcs: [
- "ext_tests/**/*.java",
- "ext_tests/**/*.kt",
- ],
-}
-
-filegroup {
- name: "launcher-quickstep-ext_tests",
- srcs: [
- "quickstep/ext_tests/**/*.java",
- "quickstep/ext_tests/**/*.kt",
- ],
-}
-
// Proguard files for Launcher3
filegroup {
name: "launcher-proguard-rules",
@@ -186,7 +170,7 @@
sdk_version: "current",
min_sdk_version: min_launcher3_sdk_version,
lint: {
- baseline_filename: "lint-baseline2.xml",
+ baseline_filename: "lint-baseline.xml",
},
}
@@ -212,7 +196,7 @@
min_sdk_version: min_launcher3_sdk_version,
manifest: "AndroidManifest-common.xml",
lint: {
- baseline_filename: "lint-baseline2.xml",
+ baseline_filename: "lint-baseline.xml",
},
}
@@ -229,11 +213,8 @@
":launcher-src",
":launcher-src_shortcuts_overrides",
":launcher-src_ui_overrides",
- ":launcher-ext_tests",
],
- resource_dirs: [
- "ext_tests/res",
- ],
+
optimize: {
proguard_flags_files: ["proguard.flags"],
// Proguard is disable for testing. Derivarive prjects to keep proguard enabled
diff --git a/ext_tests/res/values/overrides.xml b/ext_tests/res/values/overrides.xml
deleted file mode 100644
index 3f071d4..0000000
--- a/ext_tests/res/values/overrides.xml
+++ /dev/null
@@ -1,5 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="test_information_handler_class" translatable="false">com.android.launcher3.testing.DebugTestInformationHandler</string>
-</resources>
-
diff --git a/ext_tests/src/com/android/launcher3/testing/DebugTestInformationHandler.java b/ext_tests/src/com/android/launcher3/testing/DebugTestInformationHandler.java
deleted file mode 100644
index 6e7a82a..0000000
--- a/ext_tests/src/com/android/launcher3/testing/DebugTestInformationHandler.java
+++ /dev/null
@@ -1,258 +0,0 @@
-/*
- * Copyright (C) 2020 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.testing;
-
-import static com.android.launcher3.util.Executors.MAIN_EXECUTOR;
-import static com.android.launcher3.util.Executors.MODEL_EXECUTOR;
-
-import android.app.Activity;
-import android.app.Application;
-import android.content.Context;
-import android.os.Binder;
-import android.os.Bundle;
-import android.system.Os;
-
-import androidx.annotation.Keep;
-import androidx.annotation.Nullable;
-
-import com.android.launcher3.BubbleTextView;
-import com.android.launcher3.LauncherAppState;
-import com.android.launcher3.LauncherModel;
-import com.android.launcher3.ShortcutAndWidgetContainer;
-import com.android.launcher3.icons.ClockDrawableWrapper;
-import com.android.launcher3.testing.shared.TestProtocol;
-
-import java.util.ArrayList;
-import java.util.Collection;
-import java.util.Collections;
-import java.util.Map;
-import java.util.WeakHashMap;
-import java.util.concurrent.CountDownLatch;
-import java.util.concurrent.TimeUnit;
-
-/**
- * Class to handle requests from tests, including debug ones.
- */
-public class DebugTestInformationHandler extends TestInformationHandler {
- private static Collection<String> sEvents;
- private static Application.ActivityLifecycleCallbacks sActivityLifecycleCallbacks;
- private static final Map<Activity, Boolean> sActivities =
- Collections.synchronizedMap(new WeakHashMap<>());
- private static int sActivitiesCreatedCount = 0;
-
- public DebugTestInformationHandler(Context context) {
- init(context);
- if (sActivityLifecycleCallbacks == null) {
- sActivityLifecycleCallbacks = new Application.ActivityLifecycleCallbacks() {
- @Override
- public void onActivityCreated(Activity activity, Bundle bundle) {
- sActivities.put(activity, true);
- ++sActivitiesCreatedCount;
- }
-
- @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) {
- }
- };
- ((Application) context.getApplicationContext())
- .registerActivityLifecycleCallbacks(sActivityLifecycleCallbacks);
- }
- }
-
- private static void runGcAndFinalizersSync() {
- Runtime.getRuntime().gc();
- Runtime.getRuntime().runFinalization();
-
- final CountDownLatch fence = new CountDownLatch(1);
- createFinalizationObserver(fence);
- try {
- do {
- Runtime.getRuntime().gc();
- Runtime.getRuntime().runFinalization();
- } while (!fence.await(100, TimeUnit.MILLISECONDS));
- } catch (InterruptedException ex) {
- throw new RuntimeException(ex);
- }
- }
-
- // Create the observer in the scope of a method to minimize the chance that
- // it remains live in a DEX/machine register at the point of the fence guard.
- // This must be kept to avoid R8 inlining it.
- @Keep
- private static void createFinalizationObserver(CountDownLatch fence) {
- new Object() {
- @Override
- protected void finalize() throws Throwable {
- try {
- fence.countDown();
- } finally {
- super.finalize();
- }
- }
- };
- }
-
- @Override
- public Bundle call(String method, String arg, @Nullable Bundle extras) {
- final Bundle response = new Bundle();
- switch (method) {
- case TestProtocol.REQUEST_APP_LIST_FREEZE_FLAGS: {
- return getLauncherUIProperty(Bundle::putInt,
- l -> l.getAppsView().getAppsStore().getDeferUpdatesFlags());
- }
-
- case TestProtocol.REQUEST_ENABLE_DEBUG_TRACING:
- TestProtocol.sDebugTracing = true;
- ClockDrawableWrapper.sRunningInTest = true;
- return response;
-
- case TestProtocol.REQUEST_DISABLE_DEBUG_TRACING:
- TestProtocol.sDebugTracing = false;
- ClockDrawableWrapper.sRunningInTest = false;
- return response;
-
- case TestProtocol.REQUEST_PID: {
- response.putInt(TestProtocol.TEST_INFO_RESPONSE_FIELD, Os.getpid());
- return response;
- }
-
- case TestProtocol.REQUEST_FORCE_GC: {
- runGcAndFinalizersSync();
- return response;
- }
-
- case TestProtocol.REQUEST_START_EVENT_LOGGING: {
- sEvents = new ArrayList<>();
- TestLogging.setEventConsumer(
- (sequence, event) -> {
- final Collection<String> events = sEvents;
- if (events != null) {
- synchronized (events) {
- events.add(sequence + '/' + event);
- }
- }
- });
- return response;
- }
-
- case TestProtocol.REQUEST_STOP_EVENT_LOGGING: {
- TestLogging.setEventConsumer(null);
- sEvents = null;
- return response;
- }
-
- case TestProtocol.REQUEST_GET_TEST_EVENTS: {
- if (sEvents == null) {
- // sEvents can be null if Launcher died and restarted after
- // REQUEST_START_EVENT_LOGGING.
- return response;
- }
-
- synchronized (sEvents) {
- response.putStringArrayList(
- TestProtocol.TEST_INFO_RESPONSE_FIELD, new ArrayList<>(sEvents));
- }
- return response;
- }
-
- case TestProtocol.REQUEST_REINITIALIZE_DATA: {
- final long identity = Binder.clearCallingIdentity();
- try {
- MODEL_EXECUTOR.execute(() -> {
- LauncherModel model = LauncherAppState.getInstance(mContext).getModel();
- model.getModelDbController().createEmptyDB();
- MAIN_EXECUTOR.execute(model::forceReload);
- });
- return response;
- } finally {
- Binder.restoreCallingIdentity(identity);
- }
- }
-
- case TestProtocol.REQUEST_CLEAR_DATA: {
- final long identity = Binder.clearCallingIdentity();
- try {
- MODEL_EXECUTOR.execute(() -> {
- LauncherModel model = LauncherAppState.getInstance(mContext).getModel();
- model.getModelDbController().createEmptyDB();
- model.getModelDbController().clearEmptyDbFlag();
- MAIN_EXECUTOR.execute(model::forceReload);
- });
- return response;
- } finally {
- Binder.restoreCallingIdentity(identity);
- }
- }
-
- case TestProtocol.REQUEST_HOTSEAT_ICON_NAMES: {
- return getLauncherUIProperty(Bundle::putStringArrayList, l -> {
- ShortcutAndWidgetContainer hotseatIconsContainer =
- l.getHotseat().getShortcutsAndWidgets();
- ArrayList<String> hotseatIconNames = new ArrayList<>();
-
- for (int i = 0; i < hotseatIconsContainer.getChildCount(); i++) {
- // Use unchecked cast to catch changes in hotseat layout
- BubbleTextView icon = (BubbleTextView) hotseatIconsContainer.getChildAt(i);
- hotseatIconNames.add((String) icon.getText());
- }
-
- return hotseatIconNames;
- });
- }
-
- case TestProtocol.REQUEST_GET_ACTIVITIES_CREATED_COUNT: {
- response.putInt(TestProtocol.TEST_INFO_RESPONSE_FIELD, sActivitiesCreatedCount);
- return response;
- }
-
- case TestProtocol.REQUEST_GET_ACTIVITIES: {
- response.putStringArray(TestProtocol.TEST_INFO_RESPONSE_FIELD,
- sActivities.keySet().stream().map(
- a -> a.getClass().getSimpleName() + " ("
- + (a.isDestroyed() ? "destroyed" : "current") + ")")
- .toArray(String[]::new));
- return response;
- }
-
- case TestProtocol.REQUEST_MODEL_QUEUE_CLEARED:
- return getFromExecutorSync(MODEL_EXECUTOR, Bundle::new);
-
- default:
- return super.call(method, arg, extras);
- }
- }
-}
diff --git a/lint-baseline.xml b/lint-baseline.xml
index fe005ca..2ee9531 100644
--- a/lint-baseline.xml
+++ b/lint-baseline.xml
@@ -3,13 +3,13 @@
<issue
id="NewApi"
- message="Call requires API level 29 (current min is 26): `android.content.res.Resources#getFloat`"
- errorLine1=" return mContext.getResources().getFloat(resId);"
- errorLine2=" ~~~~~~~~">
+ message="`@android:dimen/system_app_widget_background_radius` requires API level 31 (current min is 26)"
+ errorLine1=' <corners android:radius="@android:dimen/system_app_widget_background_radius" />'
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
<location
- file="packages/apps/Launcher3/src/com/android/launcher3/util/DynamicResource.java"
- line="73"
- column="40"/>
+ file="packages/apps/Launcher3/res/drawable/widget_resize_frame.xml"
+ line="20"
+ column="14"/>
</issue>
<issue
@@ -34,136 +34,4 @@
column="18"/>
</issue>
- <issue
- id="NewApi"
- message="Call requires API level 28 (current min is 26): `android.app.Person#getKey`"
- errorLine1=" return people.stream().filter(person -> person.getKey() != null)"
- errorLine2=" ~~~~~~">
- <location
- file="packages/apps/Launcher3/src/com/android/launcher3/notification/NotificationKeyData.java"
- line="72"
- column="56"/>
- </issue>
-
- <issue
- id="NewApi"
- message="Method reference requires API level 28 (current min is 26): `Person::getKey`"
- errorLine1=" .map(Person::getKey).sorted().toArray(String[]::new);"
- errorLine2=" ~~~~~~~~~~~~~~">
- <location
- file="packages/apps/Launcher3/src/com/android/launcher3/notification/NotificationKeyData.java"
- line="73"
- column="22"/>
- </issue>
-
- <issue
- id="NewApi"
- message="Field requires API level 29 (current min is 26): `android.view.accessibility.AccessibilityNodeInfo.AccessibilityAction#ACTION_PAGE_LEFT`"
- errorLine1=" AccessibilityNodeInfo.AccessibilityAction.ACTION_PAGE_LEFT"
- errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
- <location
- file="packages/apps/Launcher3/src/com/android/launcher3/PagedView.java"
- line="1814"
- column="17"/>
- </issue>
-
- <issue
- id="NewApi"
- message="Field requires API level 29 (current min is 26): `android.view.accessibility.AccessibilityNodeInfo.AccessibilityAction#ACTION_PAGE_RIGHT`"
- errorLine1=" : AccessibilityNodeInfo.AccessibilityAction.ACTION_PAGE_RIGHT);"
- errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
- <location
- file="packages/apps/Launcher3/src/com/android/launcher3/PagedView.java"
- line="1815"
- column="19"/>
- </issue>
-
- <issue
- id="NewApi"
- message="Field requires API level 29 (current min is 26): `android.view.accessibility.AccessibilityNodeInfo.AccessibilityAction#ACTION_PAGE_RIGHT`"
- errorLine1=" AccessibilityNodeInfo.AccessibilityAction.ACTION_PAGE_RIGHT"
- errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
- <location
- file="packages/apps/Launcher3/src/com/android/launcher3/PagedView.java"
- line="1823"
- column="17"/>
- </issue>
-
- <issue
- id="NewApi"
- message="Field requires API level 29 (current min is 26): `android.view.accessibility.AccessibilityNodeInfo.AccessibilityAction#ACTION_PAGE_LEFT`"
- errorLine1=" : AccessibilityNodeInfo.AccessibilityAction.ACTION_PAGE_LEFT);"
- errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
- <location
- file="packages/apps/Launcher3/src/com/android/launcher3/PagedView.java"
- line="1824"
- column="19"/>
- </issue>
-
- <issue
- id="NewApi"
- message="Call requires API level 30 (current min is 26): `android.graphics.Outline#setPath`"
- errorLine1=" outline.setPath(mPath);"
- errorLine2=" ~~~~~~~">
- <location
- file="packages/apps/Launcher3/src/com/android/launcher3/popup/RoundedArrowDrawable.java"
- line="114"
- column="17"/>
- </issue>
-
- <issue
- id="NewApi"
- message="Field requires API level 28 (current min is 26): `android.appwidget.AppWidgetProviderInfo#widgetFeatures`"
- errorLine1=" int featureFlags = mProviderInfo.widgetFeatures;"
- errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
- <location
- file="packages/apps/Launcher3/src/com/android/launcher3/widget/WidgetAddFlowHandler.java"
- line="93"
- column="28"/>
- </issue>
-
- <issue
- id="NewApi"
- message="Call requires API level 30 (current min is 26): `android.view.View#getWindowInsetsController`"
- errorLine1=" WindowInsetsController insetsController = getWindowInsetsController();"
- errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~">
- <location
- file="packages/apps/Launcher3/src/com/android/launcher3/widget/picker/WidgetsFullSheet.java"
- line="820"
- column="51"/>
- </issue>
-
- <issue
- id="NewApi"
- message="Call requires API level 30 (current min is 26): `android.view.WindowInsets.Type#ime`"
- errorLine1=" insetsController.hide(WindowInsets.Type.ime());"
- errorLine2=" ~~~">
- <location
- file="packages/apps/Launcher3/src/com/android/launcher3/widget/picker/WidgetsFullSheet.java"
- line="822"
- column="53"/>
- </issue>
-
- <issue
- id="NewApi"
- message="Call requires API level 30 (current min is 26): `android.view.WindowInsetsController#hide`"
- errorLine1=" insetsController.hide(WindowInsets.Type.ime());"
- errorLine2=" ~~~~">
- <location
- file="packages/apps/Launcher3/src/com/android/launcher3/widget/picker/WidgetsFullSheet.java"
- line="822"
- column="30"/>
- </issue>
-
- <issue
- id="NewApi"
- message="Method reference requires API level 28 (current min is 26): `Person::getKey`"
- errorLine1=" : Arrays.stream(persons).map(Person::getKey).sorted().toArray(String[]::new);"
- errorLine2=" ~~~~~~~~~~~~~~">
- <location
- file="packages/apps/Launcher3/src/com/android/launcher3/model/data/WorkspaceItemInfo.java"
- line="194"
- column="42"/>
- </issue>
-
</issues>
\ No newline at end of file
diff --git a/lint-baseline2.xml b/lint-baseline2.xml
deleted file mode 100644
index 84f1b15..0000000
--- a/lint-baseline2.xml
+++ /dev/null
@@ -1,37 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<issues format="6" by="lint 8.4.0-alpha01" type="baseline" client="" dependencies="true" name="" variant="all" version="8.4.0-alpha01">
-
- <issue
- id="NewApi"
- message="`?android:attr/dialogCornerRadius` requires API level 28 (current min is 26)"
- errorLine1=' android:topLeftRadius="?android:attr/dialogCornerRadius"'
- errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
- <location
- file="packages/apps/Launcher3/res/drawable/add_item_dialog_background.xml"
- line="6"
- column="9"/>
- </issue>
-
- <issue
- id="NewApi"
- message="`?android:attr/dialogCornerRadius` requires API level 28 (current min is 26)"
- errorLine1=' android:topRightRadius="?android:attr/dialogCornerRadius" />'
- errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
- <location
- file="packages/apps/Launcher3/res/drawable/add_item_dialog_background.xml"
- line="7"
- column="9"/>
- </issue>
-
- <issue
- id="NewApi"
- message="`@android:dimen/system_app_widget_background_radius` requires API level 31 (current min is 26)"
- errorLine1=' <corners android:radius="@android:dimen/system_app_widget_background_radius" />'
- errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
- <location
- file="packages/apps/Launcher3/res/drawable/widget_resize_frame.xml"
- line="20"
- column="14"/>
- </issue>
-
-</issues>
\ No newline at end of file
diff --git a/quickstep/ext_tests/src/com/android/quickstep/DebugQuickstepTestInformationHandler.java b/quickstep/ext_tests/src/com/android/quickstep/DebugQuickstepTestInformationHandler.java
deleted file mode 100644
index 0b17a7b..0000000
--- a/quickstep/ext_tests/src/com/android/quickstep/DebugQuickstepTestInformationHandler.java
+++ /dev/null
@@ -1,51 +0,0 @@
-/*
- * Copyright (C) 2022 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 android.os.Bundle;
-
-import androidx.annotation.Nullable;
-
-import com.android.launcher3.testing.DebugTestInformationHandler;
-import com.android.launcher3.testing.shared.TestProtocol;
-
-/**
- * Class to handle requests from tests, including debug ones, to Quickstep Launcher builds.
- */
-public abstract class DebugQuickstepTestInformationHandler extends QuickstepTestInformationHandler {
-
- private final DebugTestInformationHandler mDebugTestInformationHandler;
-
- public DebugQuickstepTestInformationHandler(Context context) {
- super(context);
- mDebugTestInformationHandler = new DebugTestInformationHandler(context);
- }
-
- @Override
- public Bundle call(String method, String arg, @Nullable Bundle extras) {
- Bundle response = new Bundle();
- if (TestProtocol.REQUEST_RECREATE_TASKBAR.equals(method)) {
- // Allow null-pointer to catch illegal states.
- runOnTISBinder(tisBinder -> tisBinder.getTaskbarManager().recreateTaskbar());
- return response;
- }
- response = super.call(method, arg, extras);
- if (response != null) return response;
- return mDebugTestInformationHandler.call(method, arg, extras);
- }
-}
-
diff --git a/src/com/android/launcher3/testing/TestInformationHandler.java b/src/com/android/launcher3/testing/TestInformationHandler.java
index 12cdd67..d5c9b9f 100644
--- a/src/com/android/launcher3/testing/TestInformationHandler.java
+++ b/src/com/android/launcher3/testing/TestInformationHandler.java
@@ -23,39 +23,56 @@
import static com.android.launcher3.config.FeatureFlags.enableSplitContextually;
import static com.android.launcher3.testing.shared.TestProtocol.TEST_INFO_RESPONSE_FIELD;
import static com.android.launcher3.util.Executors.MAIN_EXECUTOR;
+import static com.android.launcher3.util.Executors.MODEL_EXECUTOR;
import android.app.Activity;
+import android.app.Application;
import android.content.Context;
import android.content.res.Resources;
import android.graphics.Insets;
import android.graphics.Point;
import android.graphics.Rect;
+import android.os.Binder;
import android.os.Bundle;
+import android.system.Os;
import android.view.WindowInsets;
+import androidx.annotation.Keep;
import androidx.annotation.Nullable;
import androidx.core.view.WindowInsetsCompat;
+import com.android.launcher3.BubbleTextView;
import com.android.launcher3.CellLayout;
import com.android.launcher3.DeviceProfile;
import com.android.launcher3.Hotseat;
import com.android.launcher3.InvariantDeviceProfile;
import com.android.launcher3.Launcher;
import com.android.launcher3.LauncherAppState;
+import com.android.launcher3.LauncherModel;
import com.android.launcher3.LauncherState;
import com.android.launcher3.R;
+import com.android.launcher3.ShortcutAndWidgetContainer;
import com.android.launcher3.Workspace;
import com.android.launcher3.dragndrop.DragLayer;
+import com.android.launcher3.icons.ClockDrawableWrapper;
import com.android.launcher3.testing.shared.HotseatCellCenterRequest;
import com.android.launcher3.testing.shared.TestProtocol;
import com.android.launcher3.testing.shared.WorkspaceCellCenterRequest;
+import com.android.launcher3.util.ActivityLifecycleCallbacksAdapter;
import com.android.launcher3.util.DisplayController;
import com.android.launcher3.util.ResourceBasedOverride;
import com.android.launcher3.widget.picker.WidgetsFullSheet;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.Set;
+import java.util.WeakHashMap;
import java.util.concurrent.Callable;
+import java.util.concurrent.CountDownLatch;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.ExecutorService;
+import java.util.concurrent.TimeUnit;
import java.util.function.Function;
import java.util.function.Supplier;
@@ -69,6 +86,12 @@
context, R.string.test_information_handler_class);
}
+ private static Collection<String> sEvents;
+ private static Application.ActivityLifecycleCallbacks sActivityLifecycleCallbacks;
+ private static final Set<Activity> sActivities =
+ Collections.synchronizedSet(Collections.newSetFromMap(new WeakHashMap<>()));
+ private static int sActivitiesCreatedCount = 0;
+
protected Context mContext;
protected DeviceProfile mDeviceProfile;
protected LauncherAppState mLauncherAppState;
@@ -78,6 +101,17 @@
mDeviceProfile = InvariantDeviceProfile.INSTANCE.
get(context).getDeviceProfile(context);
mLauncherAppState = LauncherAppState.getInstanceNoCreate();
+ if (sActivityLifecycleCallbacks == null) {
+ sActivityLifecycleCallbacks = new ActivityLifecycleCallbacksAdapter() {
+ @Override
+ public void onActivityCreated(Activity activity, Bundle bundle) {
+ sActivities.add(activity);
+ ++sActivitiesCreatedCount;
+ }
+ };
+ ((Application) context.getApplicationContext())
+ .registerActivityLifecycleCallbacks(sActivityLifecycleCallbacks);
+ }
}
/**
@@ -309,6 +343,127 @@
return response;
}
+ case TestProtocol.REQUEST_APP_LIST_FREEZE_FLAGS: {
+ return getLauncherUIProperty(Bundle::putInt,
+ l -> l.getAppsView().getAppsStore().getDeferUpdatesFlags());
+ }
+
+ case TestProtocol.REQUEST_ENABLE_DEBUG_TRACING:
+ TestProtocol.sDebugTracing = true;
+ ClockDrawableWrapper.sRunningInTest = true;
+ return response;
+
+ case TestProtocol.REQUEST_DISABLE_DEBUG_TRACING:
+ TestProtocol.sDebugTracing = false;
+ ClockDrawableWrapper.sRunningInTest = false;
+ return response;
+
+ case TestProtocol.REQUEST_PID: {
+ response.putInt(TestProtocol.TEST_INFO_RESPONSE_FIELD, Os.getpid());
+ return response;
+ }
+
+ case TestProtocol.REQUEST_FORCE_GC: {
+ runGcAndFinalizersSync();
+ return response;
+ }
+
+ case TestProtocol.REQUEST_START_EVENT_LOGGING: {
+ sEvents = new ArrayList<>();
+ TestLogging.setEventConsumer(
+ (sequence, event) -> {
+ final Collection<String> events = sEvents;
+ if (events != null) {
+ synchronized (events) {
+ events.add(sequence + '/' + event);
+ }
+ }
+ });
+ return response;
+ }
+
+ case TestProtocol.REQUEST_STOP_EVENT_LOGGING: {
+ TestLogging.setEventConsumer(null);
+ sEvents = null;
+ return response;
+ }
+
+ case TestProtocol.REQUEST_GET_TEST_EVENTS: {
+ if (sEvents == null) {
+ // sEvents can be null if Launcher died and restarted after
+ // REQUEST_START_EVENT_LOGGING.
+ return response;
+ }
+
+ synchronized (sEvents) {
+ response.putStringArrayList(
+ TestProtocol.TEST_INFO_RESPONSE_FIELD, new ArrayList<>(sEvents));
+ }
+ return response;
+ }
+
+ case TestProtocol.REQUEST_REINITIALIZE_DATA: {
+ final long identity = Binder.clearCallingIdentity();
+ try {
+ MODEL_EXECUTOR.execute(() -> {
+ LauncherModel model = LauncherAppState.getInstance(mContext).getModel();
+ model.getModelDbController().createEmptyDB();
+ MAIN_EXECUTOR.execute(model::forceReload);
+ });
+ return response;
+ } finally {
+ Binder.restoreCallingIdentity(identity);
+ }
+ }
+
+ case TestProtocol.REQUEST_CLEAR_DATA: {
+ final long identity = Binder.clearCallingIdentity();
+ try {
+ MODEL_EXECUTOR.execute(() -> {
+ LauncherModel model = LauncherAppState.getInstance(mContext).getModel();
+ model.getModelDbController().createEmptyDB();
+ model.getModelDbController().clearEmptyDbFlag();
+ MAIN_EXECUTOR.execute(model::forceReload);
+ });
+ return response;
+ } finally {
+ Binder.restoreCallingIdentity(identity);
+ }
+ }
+
+ case TestProtocol.REQUEST_HOTSEAT_ICON_NAMES: {
+ return getLauncherUIProperty(Bundle::putStringArrayList, l -> {
+ ShortcutAndWidgetContainer hotseatIconsContainer =
+ l.getHotseat().getShortcutsAndWidgets();
+ ArrayList<String> hotseatIconNames = new ArrayList<>();
+
+ for (int i = 0; i < hotseatIconsContainer.getChildCount(); i++) {
+ // Use unchecked cast to catch changes in hotseat layout
+ BubbleTextView icon = (BubbleTextView) hotseatIconsContainer.getChildAt(i);
+ hotseatIconNames.add((String) icon.getText());
+ }
+
+ return hotseatIconNames;
+ });
+ }
+
+ case TestProtocol.REQUEST_GET_ACTIVITIES_CREATED_COUNT: {
+ response.putInt(TestProtocol.TEST_INFO_RESPONSE_FIELD, sActivitiesCreatedCount);
+ return response;
+ }
+
+ case TestProtocol.REQUEST_GET_ACTIVITIES: {
+ response.putStringArray(TestProtocol.TEST_INFO_RESPONSE_FIELD,
+ sActivities.stream().map(
+ a -> a.getClass().getSimpleName() + " ("
+ + (a.isDestroyed() ? "destroyed" : "current") + ")")
+ .toArray(String[]::new));
+ return response;
+ }
+
+ case TestProtocol.REQUEST_MODEL_QUEUE_CLEARED:
+ return getFromExecutorSync(MODEL_EXECUTOR, Bundle::new);
+
default:
return null;
}
@@ -387,4 +542,38 @@
*/
void set(Bundle b, String key, T value);
}
+
+
+ private static void runGcAndFinalizersSync() {
+ Runtime.getRuntime().gc();
+ Runtime.getRuntime().runFinalization();
+
+ final CountDownLatch fence = new CountDownLatch(1);
+ createFinalizationObserver(fence);
+ try {
+ do {
+ Runtime.getRuntime().gc();
+ Runtime.getRuntime().runFinalization();
+ } while (!fence.await(100, TimeUnit.MILLISECONDS));
+ } catch (InterruptedException ex) {
+ throw new RuntimeException(ex);
+ }
+ }
+
+ // Create the observer in the scope of a method to minimize the chance that
+ // it remains live in a DEX/machine register at the point of the fence guard.
+ // This must be kept to avoid R8 inlining it.
+ @Keep
+ private static void createFinalizationObserver(CountDownLatch fence) {
+ new Object() {
+ @Override
+ protected void finalize() throws Throwable {
+ try {
+ fence.countDown();
+ } finally {
+ super.finalize();
+ }
+ }
+ };
+ }
}