From accfe86e59e8f89ab90e85195a48cfc7d0e47179 Mon Sep 17 00:00:00 2001 From: zakcohen Date: Thu, 22 Jul 2021 10:44:02 -0700 Subject: [PATCH 1/6] Overview - leave modal state on orientation changes. Bug: 194424293 Test: Local Change-Id: I913dd5cb85aa1c52d79a8876a78a677a586da9c7 (cherry picked from commit 5b144ea31faea195c05783ec5aab48d1f3ae0bab) From 0ba6058f8c3add3fd6ffee9bbe9820769d980bc5 Mon Sep 17 00:00:00 2001 From: Zak Cohen Date: Thu, 5 Aug 2021 11:14:47 -0700 Subject: [PATCH 2/6] Remove screen actions plugin Bug: 201115460 Test: local build Change-Id: I22774e464d1230fffff3d512b0b906f999d51080 --- .../quickstep/views/TaskThumbnailView.java | 36 +--------------- .../plugins/OverviewScreenshotActions.java | 41 ------------------- 2 files changed, 1 insertion(+), 76 deletions(-) delete mode 100644 src_plugins/com/android/systemui/plugins/OverviewScreenshotActions.java diff --git a/quickstep/src/com/android/quickstep/views/TaskThumbnailView.java b/quickstep/src/com/android/quickstep/views/TaskThumbnailView.java index 28044e4423..a9db400df2 100644 --- a/quickstep/src/com/android/quickstep/views/TaskThumbnailView.java +++ b/quickstep/src/com/android/quickstep/views/TaskThumbnailView.java @@ -47,20 +47,17 @@ import androidx.core.graphics.ColorUtils; import com.android.launcher3.BaseActivity; import com.android.launcher3.DeviceProfile; import com.android.launcher3.Utilities; -import com.android.launcher3.uioverrides.plugins.PluginManagerWrapper; import com.android.launcher3.util.MainThreadInitializedObject; import com.android.launcher3.util.SystemUiController; import com.android.quickstep.TaskOverlayFactory.TaskOverlay; import com.android.quickstep.views.TaskView.FullscreenDrawParams; -import com.android.systemui.plugins.OverviewScreenshotActions; -import com.android.systemui.plugins.PluginListener; import com.android.systemui.shared.recents.model.Task; import com.android.systemui.shared.recents.model.ThumbnailData; /** * A task in the Recents view. */ -public class TaskThumbnailView extends View implements PluginListener { +public class TaskThumbnailView extends View { private static final MainThreadInitializedObject TEMP_PARAMS = new MainThreadInitializedObject<>(FullscreenDrawParams::new); @@ -97,7 +94,6 @@ public class TaskThumbnailView extends View implements PluginListener Date: Thu, 23 Sep 2021 19:56:24 -0400 Subject: [PATCH 3/6] Refactor arrow popups to allow easier code overriding and sharing. Test: Existing menus work as intended. Bug: 188222480 Change-Id: I7d19b06ce8bb7d765624c64c0042c4efd6faf348 --- src/com/android/launcher3/Launcher.java | 19 ++++++- .../android/launcher3/popup/ArrowPopup.java | 57 ++++++++++--------- .../popup/PopupContainerWithArrow.java | 8 +-- .../launcher3/views/ActivityContext.java | 7 +++ .../launcher3/views/OptionsPopupView.java | 8 ++- 5 files changed, 65 insertions(+), 34 deletions(-) diff --git a/src/com/android/launcher3/Launcher.java b/src/com/android/launcher3/Launcher.java index 8a3518561f..409ee83782 100644 --- a/src/com/android/launcher3/Launcher.java +++ b/src/com/android/launcher3/Launcher.java @@ -23,9 +23,11 @@ import static android.view.View.IMPORTANT_FOR_ACCESSIBILITY_NO; import static android.view.accessibility.AccessibilityEvent.TYPE_WINDOW_STATE_CHANGED; import static com.android.launcher3.AbstractFloatingView.TYPE_ALL; +import static com.android.launcher3.AbstractFloatingView.TYPE_FOLDER; import static com.android.launcher3.AbstractFloatingView.TYPE_ICON_SURFACE; import static com.android.launcher3.AbstractFloatingView.TYPE_REBIND_SAFE; import static com.android.launcher3.AbstractFloatingView.TYPE_SNACKBAR; +import static com.android.launcher3.AbstractFloatingView.getTopOpenViewWithType; import static com.android.launcher3.LauncherAnimUtils.SPRING_LOADED_EXIT_DELAY; import static com.android.launcher3.LauncherState.ALL_APPS; import static com.android.launcher3.LauncherState.FLAG_CLOSE_POPUPS; @@ -2901,13 +2903,26 @@ public class Launcher extends StatefulActivity implements Launche * Shows the default options popup */ public void showDefaultOptions(float x, float y) { + OptionsPopupView.show(this, getPopupTarget(x, y), OptionsPopupView.getOptions(this), + false); + } + + /** + * Returns target rectangle for anchoring a popup menu. + */ + protected RectF getPopupTarget(float x, float y) { float halfSize = getResources().getDimension(R.dimen.options_menu_thumb_size) / 2; if (x < 0 || y < 0) { x = mDragLayer.getWidth() / 2; y = mDragLayer.getHeight() / 2; } - RectF target = new RectF(x - halfSize, y - halfSize, x + halfSize, y + halfSize); - OptionsPopupView.show(this, target, OptionsPopupView.getOptions(this), false); + return new RectF(x - halfSize, y - halfSize, x + halfSize, y + halfSize); + } + + @Override + public boolean shouldUseColorExtractionForPopup() { + return getTopOpenViewWithType(this, TYPE_FOLDER) == null + && getStateManager().getState() != LauncherState.ALL_APPS; } @Override diff --git a/src/com/android/launcher3/popup/ArrowPopup.java b/src/com/android/launcher3/popup/ArrowPopup.java index 112a24e37f..117ae4212b 100644 --- a/src/com/android/launcher3/popup/ArrowPopup.java +++ b/src/com/android/launcher3/popup/ArrowPopup.java @@ -51,21 +51,20 @@ import android.widget.FrameLayout; import androidx.annotation.NonNull; import com.android.launcher3.AbstractFloatingView; -import com.android.launcher3.BaseDraggingActivity; import com.android.launcher3.InsettableFrameLayout; import com.android.launcher3.Launcher; -import com.android.launcher3.LauncherState; import com.android.launcher3.R; import com.android.launcher3.Utilities; import com.android.launcher3.Workspace; import com.android.launcher3.dragndrop.DragLayer; import com.android.launcher3.shortcuts.DeepShortcutView; -import com.android.launcher3.statemanager.StatefulActivity; import com.android.launcher3.util.Themes; +import com.android.launcher3.views.ActivityContext; import com.android.launcher3.views.BaseDragLayer; import com.android.launcher3.widget.LocalColorExtractor; import java.util.ArrayList; +import java.util.Arrays; import java.util.Collections; import java.util.List; @@ -74,7 +73,7 @@ import java.util.List; * * @param The activity on with the popup shows */ -public abstract class ArrowPopup> +public abstract class ArrowPopup extends AbstractFloatingView { // Duration values (ms) for popup open and close animations. @@ -98,7 +97,7 @@ public abstract class ArrowPopup> protected final LayoutInflater mInflater; private final float mOutlineRadius; - protected final T mLauncher; + protected final T mActivityContext; protected final boolean mIsRtl; private final int mArrowOffsetVertical; @@ -131,13 +130,13 @@ public abstract class ArrowPopup> private final String mIterateChildrenTag; - private final int[] mColors; + private final int[] mColorIds; public ArrowPopup(Context context, AttributeSet attrs, int defStyleAttr) { super(context, attrs, defStyleAttr); mInflater = LayoutInflater.from(context); mOutlineRadius = Themes.getDialogCornerRadius(context); - mLauncher = BaseDraggingActivity.fromContext(context); + mActivityContext = ActivityContext.lookupContext(context); mIsRtl = Utilities.isRtl(getResources()); mBackgroundColor = Themes.getAttrColor(context, R.attr.popupColorPrimary); @@ -169,22 +168,18 @@ public abstract class ArrowPopup> mIterateChildrenTag = getContext().getString(R.string.popup_container_iterate_children); - boolean isAboveAnotherSurface = getTopOpenViewWithType(mLauncher, TYPE_FOLDER) != null - || mLauncher.getStateManager().getState() == LauncherState.ALL_APPS; - if (!isAboveAnotherSurface && Utilities.ATLEAST_S && ENABLE_LOCAL_COLOR_POPUPS.get()) { + boolean shouldUseColorExtraction = mActivityContext.shouldUseColorExtractionForPopup(); + if (shouldUseColorExtraction && Utilities.ATLEAST_S && ENABLE_LOCAL_COLOR_POPUPS.get()) { mColorExtractors = new ArrayList<>(); } else { mColorExtractors = null; } - if (isAboveAnotherSurface) { - mColors = new int[] { - getColorStateList(context, R.color.popup_shade_first).getDefaultColor()}; + if (shouldUseColorExtraction) { + mColorIds = new int[]{R.color.popup_shade_first, R.color.popup_shade_second, + R.color.popup_shade_third}; } else { - mColors = new int[] { - getColorStateList(context, R.color.popup_shade_first).getDefaultColor(), - getColorStateList(context, R.color.popup_shade_second).getDefaultColor(), - getColorStateList(context, R.color.popup_shade_third).getDefaultColor()}; + mColorIds = new int[]{R.color.popup_shade_first}; } } @@ -236,17 +231,22 @@ public abstract class ArrowPopup> } /** - * @param backgroundColor When Color.TRANSPARENT, we get color from {@link #mColors}. + * @param backgroundColor When Color.TRANSPARENT, we get color from {@link #mColorIds}. * Otherwise, we will use this color for all child views. */ private void assignMarginsAndBackgrounds(ViewGroup viewGroup, int backgroundColor) { - final boolean getColorFromColorArray = backgroundColor == Color.TRANSPARENT; + int[] colors = null; + if (backgroundColor == Color.TRANSPARENT) { + // Lazily get the colors so they match the current wallpaper colors. + colors = Arrays.stream(mColorIds).map( + r -> getColorStateList(getContext(), r).getDefaultColor()).toArray(); + } int count = viewGroup.getChildCount(); int totalVisibleShortcuts = 0; for (int i = 0; i < count; i++) { View view = viewGroup.getChildAt(i); - if (view.getVisibility() == VISIBLE && view instanceof DeepShortcutView) { + if (view.getVisibility() == VISIBLE && isShortcutOrWrapper(view)) { totalVisibleShortcuts++; } } @@ -266,9 +266,8 @@ public abstract class ArrowPopup> MarginLayoutParams mlp = (MarginLayoutParams) lastView.getLayoutParams(); mlp.bottomMargin = 0; - - if (getColorFromColorArray) { - backgroundColor = mColors[numVisibleChild % mColors.length]; + if (colors != null) { + backgroundColor = colors[numVisibleChild % colors.length]; } if (view instanceof ViewGroup && mIterateChildrenTag.equals(view.getTag())) { @@ -277,7 +276,7 @@ public abstract class ArrowPopup> continue; } - if (view instanceof DeepShortcutView) { + if (isShortcutOrWrapper(view)) { if (totalVisibleShortcuts == 1) { view.setBackgroundResource(R.drawable.single_item_primary); } else if (totalVisibleShortcuts > 1) { @@ -310,6 +309,12 @@ public abstract class ArrowPopup> measure(MeasureSpec.UNSPECIFIED, MeasureSpec.UNSPECIFIED); } + /** + * Returns {@code true} if the child is a shortcut or wraps a shortcut. + */ + protected boolean isShortcutOrWrapper(View view) { + return view instanceof DeepShortcutView; + } @TargetApi(Build.VERSION_CODES.S) private int getExtractedColor(SparseIntArray colors) { @@ -427,7 +432,7 @@ public abstract class ArrowPopup> /** * Shows the popup at the desired location. */ - protected void show() { + public void show() { setupForDisplay(); onInflationComplete(false); assignMarginsAndBackgrounds(this); @@ -807,6 +812,6 @@ public abstract class ArrowPopup> } protected BaseDragLayer getPopupContainer() { - return mLauncher.getDragLayer(); + return mActivityContext.getDragLayer(); } } diff --git a/src/com/android/launcher3/popup/PopupContainerWithArrow.java b/src/com/android/launcher3/popup/PopupContainerWithArrow.java index 454dc6e651..e340b21d53 100644 --- a/src/com/android/launcher3/popup/PopupContainerWithArrow.java +++ b/src/com/android/launcher3/popup/PopupContainerWithArrow.java @@ -151,7 +151,7 @@ public class PopupContainerWithArrow> public OnClickListener getItemClickListener() { return (view) -> { - mLauncher.getItemOnClickListener().onClick(view); + mActivityContext.getItemOnClickListener().onClick(view); close(true); }; } @@ -326,7 +326,7 @@ public class PopupContainerWithArrow> // Load the shortcuts on a background thread and update the container as it animates. MODEL_EXECUTOR.getHandler().postAtFrontOfQueue(PopupPopulator.createUpdateRunnable( - mLauncher, originalItemInfo, new Handler(Looper.getMainLooper()), + mActivityContext, originalItemInfo, new Handler(Looper.getMainLooper()), this, mShortcuts, notificationKeys)); } @@ -439,7 +439,7 @@ public class PopupContainerWithArrow> private void updateNotificationHeader() { ItemInfoWithIcon itemInfo = (ItemInfoWithIcon) mOriginalIcon.getTag(); - DotInfo dotInfo = mLauncher.getDotInfoForItem(itemInfo); + DotInfo dotInfo = mActivityContext.getDotInfoForItem(itemInfo); if (mNotificationContainer != null && dotInfo != null) { mNotificationContainer.updateHeader(dotInfo.getNotificationCount()); } @@ -481,7 +481,7 @@ public class PopupContainerWithArrow> @Override protected void closeComplete() { super.closeComplete(); - PopupContainerWithArrow openPopup = getOpen(mLauncher); + PopupContainerWithArrow openPopup = getOpen(mActivityContext); if (openPopup == null || openPopup.mOriginalIcon != mOriginalIcon) { mOriginalIcon.setTextVisibility(mOriginalIcon.shouldTextBeVisible()); mOriginalIcon.setForceHideDot(false); diff --git a/src/com/android/launcher3/views/ActivityContext.java b/src/com/android/launcher3/views/ActivityContext.java index ebcd379f74..e07d71e3af 100644 --- a/src/com/android/launcher3/views/ActivityContext.java +++ b/src/com/android/launcher3/views/ActivityContext.java @@ -115,6 +115,13 @@ public interface ActivityContext { return StatsLogManager.newInstance((Context) this); } + /** + * Returns {@code true} if popups should use color extraction. + */ + default boolean shouldUseColorExtractionForPopup() { + return true; + } + /** * Returns whether we can show the IME for elements hosted by this ActivityContext. */ diff --git a/src/com/android/launcher3/views/OptionsPopupView.java b/src/com/android/launcher3/views/OptionsPopupView.java index 257b18fcfb..33ab0d28fb 100644 --- a/src/com/android/launcher3/views/OptionsPopupView.java +++ b/src/com/android/launcher3/views/OptionsPopupView.java @@ -59,7 +59,7 @@ import java.util.List; /** * Popup shown on long pressing an empty space in launcher */ -public class OptionsPopupView extends ArrowPopup +public class OptionsPopupView extends ArrowPopup implements OnClickListener, OnLongClickListener { private final ArrayMap mItemMap = new ArrayMap<>(); @@ -74,6 +74,10 @@ public class OptionsPopupView extends ArrowPopup super(context, attrs, defStyleAttr); } + public void setTargetRect(RectF targetRect) { + mTargetRect = targetRect; + } + @Override public void onClick(View view) { handleViewClick(view); @@ -90,7 +94,7 @@ public class OptionsPopupView extends ArrowPopup return false; } if (item.eventId.getId() > 0) { - mLauncher.getStatsLogManager().logger().log(item.eventId); + mActivityContext.getStatsLogManager().logger().log(item.eventId); } if (item.clickListener.onLongClick(view)) { close(true); From 6d8deb32f6a2c15127af363288f188ddde6e5638 Mon Sep 17 00:00:00 2001 From: Steven Ng Date: Fri, 24 Sep 2021 17:32:51 +0100 Subject: [PATCH 4/6] Fix getWidgetItemSizePx estimation The size returned by getWidgetItemSizePx is used by the widget picker for the NavigableAppWidgetHostView. We should NOT deduct the padding applied to NavigableAppWidgetHostView to this size. If a launcher grid insets NavigableAppWidgetHostView, then we must add the inset to the NavigableAppWidgetHostView width / height. Test: Compare the size of widgets in preview and home screen using layout inspector. See screenshots in the bug Fix: 200983939 Change-Id: I35022861b65f2624f69940cf3856d9c47f8dbbd9 --- .../launcher3/widget/util/WidgetSizes.java | 20 +++++++++++++++---- 1 file changed, 16 insertions(+), 4 deletions(-) diff --git a/src/com/android/launcher3/widget/util/WidgetSizes.java b/src/com/android/launcher3/widget/util/WidgetSizes.java index 451ed6efcc..b211f4c22b 100644 --- a/src/com/android/launcher3/widget/util/WidgetSizes.java +++ b/src/com/android/launcher3/widget/util/WidgetSizes.java @@ -17,7 +17,6 @@ package com.android.launcher3.widget.util; import static android.appwidget.AppWidgetHostView.getDefaultPaddingForWidget; - import android.appwidget.AppWidgetHostView; import android.appwidget.AppWidgetManager; import android.content.ComponentName; @@ -87,7 +86,13 @@ public final class WidgetSizes { } /** - * Returns the size of a WidgetItem. + * Returns the size of a {@link WidgetItem}. + * + *

This size is used by the widget picker. It should NEVER be shared with app widgets. + * + *

For sizes shared with app widgets, please refer to + * {@link #getWidgetPaddedSizes(Context, ComponentName, int, int)} & + * {@link #getWidgetPaddedSizePx(Context, ComponentName, DeviceProfile, int, int)}. */ public static Size getWidgetItemSizePx(Context context, DeviceProfile profile, WidgetItem widgetItem) { @@ -96,8 +101,15 @@ public final class WidgetSizes { .getDimensionPixelSize(R.dimen.widget_preview_shortcut_padding); return new Size(dimension, dimension); } - return getWidgetPaddedSizePx(context, widgetItem.componentName, profile, widgetItem.spanX, - widgetItem.spanY); + Size widgetItemSize = getWidgetSizePx(profile, widgetItem.spanX, + widgetItem.spanY, /* recycledCellSize= */ null); + if (profile.shouldInsetWidgets()) { + Rect inset = new Rect(); + AppWidgetHostView.getDefaultPaddingForWidget(context, widgetItem.componentName, inset); + return new Size(widgetItemSize.getWidth() + inset.left + inset.right, + widgetItemSize.getHeight() + inset.top + inset.bottom); + } + return widgetItemSize; } private static Size getWidgetSizePx(DeviceProfile profile, int spanX, int spanY, From 5ade8e890d34e0af036cee5047a7b0857833091f Mon Sep 17 00:00:00 2001 From: vadimt Date: Thu, 23 Sep 2021 18:00:13 -0700 Subject: [PATCH 5/6] Moving activity tracker to Launcher process This will improve diagnostics for OOP tests, like we now have a list of leaked activity classes. Also some cleanups. Bug: 187761685 Test: local runs Change-Id: I8b5711ac727874fd826cfef9c742ea97048763e0 --- .../testing/DebugTestInformationHandler.java | 58 ++++++++++++ .../launcher3/testing/TestProtocol.java | 3 + tests/Android.bp | 1 - .../launcher3/ui/AbstractLauncherUiTest.java | 40 ++------- .../launcher3/ui/ActivityLeakTracker.java | 90 ------------------- .../tapl/LauncherInstrumentation.java | 24 +++++ 6 files changed, 93 insertions(+), 123 deletions(-) delete mode 100644 tests/src/com/android/launcher3/ui/ActivityLeakTracker.java diff --git a/ext_tests/src/com/android/launcher3/testing/DebugTestInformationHandler.java b/ext_tests/src/com/android/launcher3/testing/DebugTestInformationHandler.java index dacd8a22ab..0f61d149d0 100644 --- a/ext_tests/src/com/android/launcher3/testing/DebugTestInformationHandler.java +++ b/ext_tests/src/com/android/launcher3/testing/DebugTestInformationHandler.java @@ -18,6 +18,8 @@ package com.android.launcher3.testing; import static com.android.launcher3.util.Executors.MAIN_EXECUTOR; +import android.app.Activity; +import android.app.Application; import android.content.Context; import android.os.Binder; import android.os.Bundle; @@ -31,7 +33,10 @@ import com.android.launcher3.LauncherSettings; import java.util.ArrayList; import java.util.Collection; +import java.util.Collections; import java.util.LinkedList; +import java.util.Map; +import java.util.WeakHashMap; import java.util.concurrent.CountDownLatch; import java.util.concurrent.TimeUnit; @@ -41,9 +46,48 @@ import java.util.concurrent.TimeUnit; public class DebugTestInformationHandler extends TestInformationHandler { private static LinkedList sLeaks; private static Collection sEvents; + private static Application.ActivityLifecycleCallbacks sActivityLifecycleCallbacks; + private static final Map 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() { @@ -160,6 +204,20 @@ public class DebugTestInformationHandler extends TestInformationHandler { } } + 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; + } + default: return super.call(method, arg); } diff --git a/src/com/android/launcher3/testing/TestProtocol.java b/src/com/android/launcher3/testing/TestProtocol.java index ed52e20a34..b25fefb8aa 100644 --- a/src/com/android/launcher3/testing/TestProtocol.java +++ b/src/com/android/launcher3/testing/TestProtocol.java @@ -99,6 +99,9 @@ public final class TestProtocol { public static final String REQUEST_CLEAR_DATA = "clear-data"; public static final String REQUEST_IS_TABLET = "is-tablet"; public static final String REQUEST_IS_TWO_PANELS = "is-two-panel"; + public static final String REQUEST_GET_ACTIVITIES_CREATED_COUNT = + "get-activities-created-count"; + public static final String REQUEST_GET_ACTIVITIES = "get-activities"; public static Long sForcePauseTimeout; public static final String REQUEST_SET_FORCE_PAUSE_TIMEOUT = "set-force-pause-timeout"; diff --git a/tests/Android.bp b/tests/Android.bp index aeddc4cbaf..3670c37add 100644 --- a/tests/Android.bp +++ b/tests/Android.bp @@ -31,7 +31,6 @@ filegroup { name: "launcher-oop-tests-src", srcs: [ "src/com/android/launcher3/ui/AbstractLauncherUiTest.java", - "src/com/android/launcher3/ui/ActivityLeakTracker.java", "src/com/android/launcher3/ui/PortraitLandscapeRunner.java", "src/com/android/launcher3/util/Wait.java", "src/com/android/launcher3/util/WidgetUtils.java", diff --git a/tests/src/com/android/launcher3/ui/AbstractLauncherUiTest.java b/tests/src/com/android/launcher3/ui/AbstractLauncherUiTest.java index 1a6ce8ccae..b6b6cdd5b7 100644 --- a/tests/src/com/android/launcher3/ui/AbstractLauncherUiTest.java +++ b/tests/src/com/android/launcher3/ui/AbstractLauncherUiTest.java @@ -36,7 +36,6 @@ import android.content.pm.PackageManager; import android.os.Debug; import android.os.Process; import android.os.RemoteException; -import android.os.StrictMode; import android.os.UserHandle; import android.os.UserManager; import android.util.Log; @@ -99,11 +98,9 @@ public abstract class AbstractLauncherUiTest { public static final long DEFAULT_UI_TIMEOUT = 10000; private static final String TAG = "AbstractLauncherUiTest"; - private static String sStrictmodeDetectedActivityLeak; private static boolean sDumpWasGenerated = false; - private static boolean sActivityLeakReported; + private static boolean sActivityLeakReported = false; private static final String SYSTEMUI_PACKAGE = "com.android.systemui"; - protected static final ActivityLeakTracker ACTIVITY_LEAK_TRACKER = new ActivityLeakTracker(); protected LooperExecutor mMainThreadExecutor = MAIN_EXECUTOR; protected final UiDevice mDevice = UiDevice.getInstance(getInstrumentation()); @@ -112,45 +109,25 @@ public abstract class AbstractLauncherUiTest { protected String mTargetPackage; private int mLauncherPid; - static { - if (TestHelpers.isInLauncherProcess()) { - StrictMode.VmPolicy.Builder builder = - new StrictMode.VmPolicy.Builder() - .penaltyLog() - .penaltyListener(Runnable::run, violation -> { - if (sStrictmodeDetectedActivityLeak == null) { - sStrictmodeDetectedActivityLeak = violation.toString() + ", " - + dumpHprofData() + "."; - } - }); - StrictMode.setVmPolicy(builder.build()); - } - } - public static void checkDetectedLeaks(LauncherInstrumentation launcher) { if (sActivityLeakReported) return; - if (sStrictmodeDetectedActivityLeak != null) { - // Report from the test thread strictmode violations detected in the main thread. - sActivityLeakReported = true; - Assert.fail(sStrictmodeDetectedActivityLeak); - } - // Check whether activity leak detector has found leaked activities. - Wait.atMost(AbstractLauncherUiTest::getActivityLeakErrorMessage, + Wait.atMost(() -> getActivityLeakErrorMessage(launcher), () -> { launcher.forceGc(); return MAIN_EXECUTOR.submit( - () -> ACTIVITY_LEAK_TRACKER.noLeakedActivities()).get(); + () -> launcher.noLeakedActivities()).get(); }, DEFAULT_UI_TIMEOUT, launcher); } - private static String getActivityLeakErrorMessage() { + private static String getActivityLeakErrorMessage(LauncherInstrumentation launcher) { sActivityLeakReported = true; - return "Activity leak detector has found leaked activities, " + dumpHprofData() + "."; + return "Activity leak detector has found leaked activities, " + + dumpHprofData(launcher) + "."; } - public static String dumpHprofData() { + public static String dumpHprofData(LauncherInstrumentation launcher) { String result; if (sDumpWasGenerated) { Log.d("b/195319692", "dump has already been generated by another test", @@ -176,8 +153,7 @@ public abstract class AbstractLauncherUiTest { result = "failed to save memory dump"; } } - return result - + ". Full list of activities: " + ACTIVITY_LEAK_TRACKER.getActivitiesList(); + return result + ". Full list of activities: " + launcher.getRootedActivitiesList(); } protected AbstractLauncherUiTest() { diff --git a/tests/src/com/android/launcher3/ui/ActivityLeakTracker.java b/tests/src/com/android/launcher3/ui/ActivityLeakTracker.java deleted file mode 100644 index 2db7472912..0000000000 --- a/tests/src/com/android/launcher3/ui/ActivityLeakTracker.java +++ /dev/null @@ -1,90 +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.ui; - -import android.app.Activity; -import android.app.Application; -import android.os.Bundle; - -import androidx.test.InstrumentationRegistry; - -import com.android.launcher3.tapl.TestHelpers; - -import java.util.WeakHashMap; -import java.util.stream.Collectors; - -public class ActivityLeakTracker implements Application.ActivityLifecycleCallbacks { - private final WeakHashMap mActivities = new WeakHashMap<>(); - - private int mActivitiesCreated; - - ActivityLeakTracker() { - if (!TestHelpers.isInLauncherProcess()) return; - final Application app = - (Application) InstrumentationRegistry.getTargetContext().getApplicationContext(); - app.registerActivityLifecycleCallbacks(this); - } - - public int getActivitiesCreated() { - return mActivitiesCreated; - } - - @Override - public void onActivityCreated(Activity activity, Bundle bundle) { - mActivities.put(activity, true); - ++mActivitiesCreated; - } - - @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) { - } - - public boolean noLeakedActivities() { - for (Activity activity : mActivities.keySet()) { - if (activity.isDestroyed()) { - return false; - } - } - - return mActivities.size() <= 2; - } - - public String getActivitiesList() { - return mActivities.keySet().stream().map(a -> a.getClass().getSimpleName()) - .collect(Collectors.joining(",")); - } -} diff --git a/tests/tapl/com/android/launcher3/tapl/LauncherInstrumentation.java b/tests/tapl/com/android/launcher3/tapl/LauncherInstrumentation.java index 98d081b70a..cb48fe6bd0 100644 --- a/tests/tapl/com/android/launcher3/tapl/LauncherInstrumentation.java +++ b/tests/tapl/com/android/launcher3/tapl/LauncherInstrumentation.java @@ -1498,6 +1498,30 @@ public final class LauncherInstrumentation { getTestInfo(TestProtocol.REQUEST_CLEAR_DATA); } + private String[] getActivities() { + return getTestInfo(TestProtocol.REQUEST_GET_ACTIVITIES) + .getStringArray(TestProtocol.TEST_INFO_RESPONSE_FIELD); + } + + public String getRootedActivitiesList() { + return String.join(", ", getActivities()); + } + + public boolean noLeakedActivities() { + final String[] activities = getActivities(); + for (String activity : activities) { + if (activity.contains("(destroyed)")) { + return false; + } + } + return activities.length <= 2; + } + + public int getActivitiesCreated() { + return getTestInfo(TestProtocol.REQUEST_GET_ACTIVITIES_CREATED_COUNT) + .getInt(TestProtocol.TEST_INFO_RESPONSE_FIELD); + } + public Closable eventsCheck() { Assert.assertTrue("Nested event checking", mEventChecker == null); disableSensorRotation(); From a26e4f8e436837e83a3058f92ec03ae18d837de2 Mon Sep 17 00:00:00 2001 From: Vinit Nayak Date: Mon, 27 Sep 2021 13:23:13 -0700 Subject: [PATCH 6/6] Unbind service only if it was bound Removed unused SplitPlaceholderView Fixes: 200004580 Change-Id: I54ee819260ad6e4e6da2d3efafd38bad0336486e --- .../launcher3/BaseQuickstepLauncher.java | 26 +++++++++++-------- 1 file changed, 15 insertions(+), 11 deletions(-) diff --git a/quickstep/src/com/android/launcher3/BaseQuickstepLauncher.java b/quickstep/src/com/android/launcher3/BaseQuickstepLauncher.java index b8ce818da0..a68322d5cd 100644 --- a/quickstep/src/com/android/launcher3/BaseQuickstepLauncher.java +++ b/quickstep/src/com/android/launcher3/BaseQuickstepLauncher.java @@ -85,7 +85,6 @@ import com.android.quickstep.util.RemoteFadeOutAnimationListener; import com.android.quickstep.util.SplitSelectStateController; import com.android.quickstep.views.OverviewActionsView; import com.android.quickstep.views.RecentsView; -import com.android.quickstep.views.SplitPlaceholderView; import com.android.systemui.shared.system.ActivityManagerWrapper; import com.android.systemui.shared.system.ActivityOptionsCompat; import com.android.systemui.shared.system.RemoteAnimationTargetCompat; @@ -131,7 +130,7 @@ public abstract class BaseQuickstepLauncher extends Launcher // Seems like there can be a race condition when user unlocks, which kills the TIS // process and re-starts it. I guess in the meantime service can be connected to // a killed TIS? Either way, unbind and try to re-connect in that case. - unbindService(mTisBinderConnection); + internalUnbindToTIS(); mHandler.postDelayed(mConnectionRunnable, BACKOFF_MILLIS); return; } @@ -159,10 +158,10 @@ public abstract class BaseQuickstepLauncher extends Launcher private short mConnectionAttempts; private final TaskbarStateHandler mTaskbarStateHandler = new TaskbarStateHandler(this); private final Handler mHandler = new Handler(); + private boolean mTisServiceBound; // Will be updated when dragging from taskbar. private @Nullable DragOptions mNextWorkspaceDragOptions = null; - private SplitPlaceholderView mSplitPlaceholderView; private @Nullable UnfoldTransitionProgressProvider mUnfoldTransitionProgressProvider; private @Nullable LauncherUnfoldAnimationController mLauncherUnfoldAnimationController; @@ -202,7 +201,7 @@ public abstract class BaseQuickstepLauncher extends Launcher SysUINavigationMode.INSTANCE.get(this).removeModeChangeListener(this); - unbindService(mTisBinderConnection); + internalUnbindToTIS(); if (mTaskbarManager != null) { mTaskbarManager.clearLauncher(this); } @@ -363,12 +362,13 @@ public abstract class BaseQuickstepLauncher extends Launcher /** * Binds {@link #mTisBinderConnection} to {@link TouchInteractionService}. If the binding fails, - * attempts to retry via {@link #mConnectionRunnable} + * attempts to retry via {@link #mConnectionRunnable}. + * Unbind via {@link #internalUnbindToTIS()} */ private void internalBindToTIS() { - boolean bound = bindService(new Intent(this, TouchInteractionService.class), + mTisServiceBound = bindService(new Intent(this, TouchInteractionService.class), mTisBinderConnection, 0); - if (bound) { + if (mTisServiceBound) { resetServiceBindRetryState(); return; } @@ -380,6 +380,14 @@ public abstract class BaseQuickstepLauncher extends Launcher mConnectionAttempts++; } + /** See {@link #internalBindToTIS()} */ + private void internalUnbindToTIS() { + if (mTisServiceBound) { + unbindService(mTisBinderConnection); + mTisServiceBound = false; + } + } + private void resetServiceBindRetryState() { if (mHandler.hasCallbacks(mConnectionRunnable)) { mHandler.removeCallbacks(mConnectionRunnable); @@ -417,10 +425,6 @@ public abstract class BaseQuickstepLauncher extends Launcher return (T) mActionsView; } - public SplitPlaceholderView getSplitPlaceholderView() { - return mSplitPlaceholderView; - } - @Override protected void closeOpenViews(boolean animate) { super.closeOpenViews(animate);