Merge "Removing unused/unmaintained files" into main
diff --git a/quickstep/res/drawable/keyboard_quick_switch_thumbnail_background.xml b/quickstep/res/drawable/keyboard_quick_switch_thumbnail_background.xml
new file mode 100644
index 0000000..961f5aa
--- /dev/null
+++ b/quickstep/res/drawable/keyboard_quick_switch_thumbnail_background.xml
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2024 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.
+-->
+<shape
+ xmlns:android="http://schemas.android.com/apk/res/android"
+ android:shape="rectangle">
+ <solid android:color="@android:color/white" />
+ <corners android:radius="@dimen/keyboard_quick_switch_task_view_radius" />
+</shape>
diff --git a/quickstep/res/layout/keyboard_quick_switch_taskview_thumbnail.xml b/quickstep/res/layout/keyboard_quick_switch_taskview_thumbnail.xml
index dde9cac..8cd8560 100644
--- a/quickstep/res/layout/keyboard_quick_switch_taskview_thumbnail.xml
+++ b/quickstep/res/layout/keyboard_quick_switch_taskview_thumbnail.xml
@@ -18,6 +18,6 @@
android:layout_width="match_parent"
android:layout_height="match_parent"
android:scaleType="centerCrop"
- android:background="@drawable/keyboard_quick_switch_task_view_background"
+ android:background="@drawable/keyboard_quick_switch_thumbnail_background"
android:clipToOutline="true"
android:importantForAccessibility="no"/>
diff --git a/quickstep/src/com/android/launcher3/model/AppEventProducer.java b/quickstep/src/com/android/launcher3/model/AppEventProducer.java
index a931f36..a621259 100644
--- a/quickstep/src/com/android/launcher3/model/AppEventProducer.java
+++ b/quickstep/src/com/android/launcher3/model/AppEventProducer.java
@@ -41,6 +41,7 @@
import static com.android.launcher3.logging.StatsLogManager.LauncherEvent.LAUNCHER_SYSTEM_SHORTCUT_DONT_SUGGEST_APP_TAP;
import static com.android.launcher3.logging.StatsLogManager.LauncherEvent.LAUNCHER_TASK_LAUNCH_SWIPE_DOWN;
import static com.android.launcher3.logging.StatsLogManager.LauncherEvent.LAUNCHER_TASK_LAUNCH_TAP;
+import static com.android.launcher3.logging.StatsLogManager.LauncherEvent.LAUNCHER_WIDGET_ADD_BUTTON_TAP;
import static com.android.launcher3.model.PredictionHelper.isTrackedForHotseatPrediction;
import static com.android.launcher3.model.PredictionHelper.isTrackedForWidgetPrediction;
import static com.android.launcher3.util.Executors.MODEL_EXECUTOR;
@@ -184,6 +185,10 @@
sendEvent(target, atomInfo, ACTION_LAUNCH, CONTAINER_PREDICTION);
} else if (event == LAUNCHER_DISMISS_PREDICTION_UNDO) {
sendEvent(atomInfo, ACTION_UNDISMISS, CONTAINER_HOTSEAT_PREDICTION);
+ } else if (event == LAUNCHER_WIDGET_ADD_BUTTON_TAP) {
+ if (isTrackedForWidgetPrediction(atomInfo)) {
+ sendEvent(atomInfo, ACTION_PIN, CONTAINER_WIDGETS_PREDICTION);
+ }
}
}
diff --git a/quickstep/src/com/android/launcher3/taskbar/KeyboardQuickSwitchController.java b/quickstep/src/com/android/launcher3/taskbar/KeyboardQuickSwitchController.java
index 0bcf2d1..bed85d7 100644
--- a/quickstep/src/com/android/launcher3/taskbar/KeyboardQuickSwitchController.java
+++ b/quickstep/src/com/android/launcher3/taskbar/KeyboardQuickSwitchController.java
@@ -97,7 +97,11 @@
private void openQuickSwitchView(int currentFocusedIndex) {
if (mQuickSwitchViewController != null) {
- return;
+ if (!mQuickSwitchViewController.isCloseAnimationRunning()) {
+ return;
+ }
+ // Allow the KQS to be reopened during the close animation to make it more responsive
+ closeQuickSwitchView(false);
}
TaskbarOverlayContext overlayContext =
mControllers.taskbarOverlayController.requestWindow();
diff --git a/quickstep/src/com/android/launcher3/taskbar/KeyboardQuickSwitchTaskView.java b/quickstep/src/com/android/launcher3/taskbar/KeyboardQuickSwitchTaskView.java
index 5b407f0..48fc7d1 100644
--- a/quickstep/src/com/android/launcher3/taskbar/KeyboardQuickSwitchTaskView.java
+++ b/quickstep/src/com/android/launcher3/taskbar/KeyboardQuickSwitchTaskView.java
@@ -23,6 +23,7 @@
import android.content.res.TypedArray;
import android.graphics.Bitmap;
import android.graphics.Canvas;
+import android.graphics.Color;
import android.graphics.drawable.Drawable;
import android.util.AttributeSet;
import android.view.View;
@@ -174,21 +175,23 @@
return;
}
if (updateFunction == null) {
- applyThumbnail(thumbnailView, task.thumbnail);
+ applyThumbnail(thumbnailView, task.colorBackground, task.thumbnail);
return;
}
- updateFunction.updateThumbnailInBackground(
- task, thumbnailData -> applyThumbnail(thumbnailView, thumbnailData));
+ updateFunction.updateThumbnailInBackground(task, thumbnailData ->
+ applyThumbnail(thumbnailView, task.colorBackground, thumbnailData));
}
private void applyThumbnail(
@NonNull ImageView thumbnailView,
- ThumbnailData thumbnailData) {
+ @ColorInt int backgroundColor,
+ @Nullable ThumbnailData thumbnailData) {
Bitmap bm = thumbnailData == null ? null : thumbnailData.thumbnail;
if (thumbnailView.getVisibility() != VISIBLE) {
thumbnailView.setVisibility(VISIBLE);
}
+ thumbnailView.getBackground().setTint(bm == null ? backgroundColor : Color.TRANSPARENT);
thumbnailView.setImageDrawable(new BlurredBitmapDrawable(bm, THUMBNAIL_BLUR_RADIUS));
}
diff --git a/quickstep/src/com/android/launcher3/taskbar/KeyboardQuickSwitchViewController.java b/quickstep/src/com/android/launcher3/taskbar/KeyboardQuickSwitchViewController.java
index 5f53cc3..25a97d4 100644
--- a/quickstep/src/com/android/launcher3/taskbar/KeyboardQuickSwitchViewController.java
+++ b/quickstep/src/com/android/launcher3/taskbar/KeyboardQuickSwitchViewController.java
@@ -94,8 +94,12 @@
mViewCallbacks);
}
+ boolean isCloseAnimationRunning() {
+ return mCloseAnimation != null;
+ }
+
protected void closeQuickSwitchView(boolean animate) {
- if (mCloseAnimation != null) {
+ if (isCloseAnimationRunning()) {
// Let currently-running animation finish.
if (!animate) {
mCloseAnimation.end();
@@ -130,7 +134,7 @@
}
private int launchTaskAt(int index) {
- if (mCloseAnimation != null) {
+ if (isCloseAnimationRunning()) {
// Ignore taps on task views and alt key unpresses while the close animation is running.
return -1;
}
@@ -186,7 +190,7 @@
pw.println(prefix + "KeyboardQuickSwitchViewController:");
pw.println(prefix + "\thasFocus=" + mKeyboardQuickSwitchView.hasFocus());
- pw.println(prefix + "\tcloseAnimationRunning=" + (mCloseAnimation != null));
+ pw.println(prefix + "\tisCloseAnimationRunning=" + isCloseAnimationRunning());
pw.println(prefix + "\tmCurrentFocusIndex=" + mCurrentFocusIndex);
}
diff --git a/quickstep/src/com/android/launcher3/taskbar/TaskbarView.java b/quickstep/src/com/android/launcher3/taskbar/TaskbarView.java
index 367bf6c..c81bf7a 100644
--- a/quickstep/src/com/android/launcher3/taskbar/TaskbarView.java
+++ b/quickstep/src/com/android/launcher3/taskbar/TaskbarView.java
@@ -354,7 +354,8 @@
break;
case ITEM_TYPE_APP_PAIR:
hotseatView = AppPairIcon.inflateIcon(
- expectedLayoutResId, mActivityContext, this, folderInfo);
+ expectedLayoutResId, mActivityContext, this, folderInfo,
+ BubbleTextView.DISPLAY_TASKBAR);
((AppPairIcon) hotseatView).setTextVisible(false);
break;
default:
diff --git a/quickstep/src/com/android/launcher3/uioverrides/PredictedAppIconInflater.java b/quickstep/src/com/android/launcher3/uioverrides/PredictedAppIconInflater.java
deleted file mode 100644
index 8f1d319..0000000
--- a/quickstep/src/com/android/launcher3/uioverrides/PredictedAppIconInflater.java
+++ /dev/null
@@ -1,33 +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.uioverrides;
-
-import android.view.LayoutInflater;
-import android.view.View;
-import android.view.ViewGroup;
-
-import com.android.launcher3.R;
-import com.android.launcher3.model.data.WorkspaceItemInfo;
-
-/** A util class that inflates a predicted app icon */
-public class PredictedAppIconInflater {
- public static View inflate(LayoutInflater inflater, ViewGroup parent, WorkspaceItemInfo info) {
- PredictedAppIcon icon = (PredictedAppIcon) inflater.inflate(
- R.layout.predicted_app_icon, parent, false);
- icon.applyFromWorkspaceItem(info);
- return icon;
- }
-}
diff --git a/quickstep/src/com/android/quickstep/AbsSwipeUpHandler.java b/quickstep/src/com/android/quickstep/AbsSwipeUpHandler.java
index 0320f50..dc1c6a6 100644
--- a/quickstep/src/com/android/quickstep/AbsSwipeUpHandler.java
+++ b/quickstep/src/com/android/quickstep/AbsSwipeUpHandler.java
@@ -1002,7 +1002,7 @@
ActiveGestureLog.INSTANCE.addLog(
/* event= */ "cancelRecentsAnimation",
/* gestureEvent= */ CANCEL_RECENTS_ANIMATION);
- mActivityInitListener.unregister();
+ mActivityInitListener.unregister("AbsSwipeUpHandler.onRecentsAnimationCanceled");
// Cache the recents animation controller so we can defer its cleanup to after having
// properly cleaned up the screenshot without accidentally using it.
mDeferredCleanupRecentsAnimationController = mRecentsAnimationController;
@@ -1964,7 +1964,7 @@
// Cleanup when switching handlers
mInputConsumerProxy.unregisterOnTouchDownCallback();
- mActivityInitListener.unregister();
+ mActivityInitListener.unregister("AbsSwipeUpHandler.cancelCurrentAnimation");
TaskStackChangeListeners.getInstance().unregisterTaskStackListener(
mActivityRestartListener);
mTaskSnapshotCache.clear();
@@ -1982,7 +1982,7 @@
mGestureEndCallback.run();
}
- mActivityInitListener.unregister();
+ mActivityInitListener.unregister("AbsSwipeUpHandler.invalidateHandler");
TaskStackChangeListeners.getInstance().unregisterTaskStackListener(
mActivityRestartListener);
mTaskSnapshotCache.clear();
@@ -2486,11 +2486,11 @@
/**
* Registers a callback to run when the activity is ready.
*/
- public void initWhenReady() {
+ public void initWhenReady(String reasonString) {
// Preload the plan
RecentsModel.INSTANCE.get(mContext).getTasks(null);
- mActivityInitListener.register();
+ mActivityInitListener.register(reasonString);
}
private boolean shouldFadeOutTargetsForKeyboardQuickSwitch(
diff --git a/quickstep/src/com/android/quickstep/LauncherBackAnimationController.java b/quickstep/src/com/android/quickstep/LauncherBackAnimationController.java
index cc5a923..2d25295 100644
--- a/quickstep/src/com/android/quickstep/LauncherBackAnimationController.java
+++ b/quickstep/src/com/android/quickstep/LauncherBackAnimationController.java
@@ -44,7 +44,6 @@
import android.view.RemoteAnimationTarget;
import android.view.SurfaceControl;
import android.view.View;
-import android.view.animation.AnimationUtils;
import android.view.animation.DecelerateInterpolator;
import android.view.animation.Interpolator;
import android.window.BackEvent;
@@ -84,7 +83,6 @@
*
*/
public class LauncherBackAnimationController {
- private static final int CANCEL_TRANSITION_DURATION = 233;
private static final int SCRIM_FADE_DURATION = 233;
private static final float MIN_WINDOW_SCALE = 0.85f;
private static final float MAX_SCRIM_ALPHA_DARK = 0.8f;
@@ -95,15 +93,12 @@
private final Matrix mTransformMatrix = new Matrix();
/** The window position at the beginning of the back animation. */
private final Rect mStartRect = new Rect();
- /** The window position when the back gesture is cancelled. */
- private final RectF mCancelRect = new RectF();
/** The current window position. */
private final RectF mCurrentRect = new RectF();
private final QuickstepLauncher mLauncher;
private final int mWindowScaleMarginX;
private float mWindowScaleEndCornerRadius;
private float mWindowScaleStartCornerRadius;
- private final Interpolator mCancelInterpolator;
private final Interpolator mProgressInterpolator = Interpolators.STANDARD_DECELERATE;
private final Interpolator mVerticalMoveInterpolator = new DecelerateInterpolator();
private final PointF mInitialTouchPos = new PointF();
@@ -142,8 +137,6 @@
loadCornerRadius();
mWindowScaleMarginX = mLauncher.getResources().getDimensionPixelSize(
R.dimen.swipe_back_window_scale_x_margin);
- mCancelInterpolator =
- AnimationUtils.loadInterpolator(mLauncher, R.interpolator.standard_interpolator);
}
/**
@@ -181,8 +174,7 @@
mHandler.post(() -> {
LauncherBackAnimationController controller = mControllerRef.get();
if (controller != null) {
- mProgressAnimator.onBackCancelled(
- controller::resetPositionAnimated);
+ mProgressAnimator.onBackCancelled(controller::onCancelFinished);
}
});
}
@@ -262,24 +254,9 @@
public void onAnimationCancelled() {}
}
- private void resetPositionAnimated() {
- ValueAnimator cancelAnimator = ValueAnimator.ofFloat(0, 1);
- mCancelRect.set(mCurrentRect);
- cancelAnimator.setDuration(CANCEL_TRANSITION_DURATION);
- cancelAnimator.setInterpolator(mCancelInterpolator);
- cancelAnimator.addUpdateListener(
- animation -> {
- updateCancelProgress((float) animation.getAnimatedValue());
- });
- cancelAnimator.addListener(new AnimatorListenerAdapter() {
- @Override
- public void onAnimationEnd(Animator animation) {
- // Refresh the status bar appearance to the original one.
- customizeStatusBarAppearance(false);
- finishAnimation();
- }
- });
- cancelAnimator.start();
+ private void onCancelFinished() {
+ customizeStatusBarAppearance(false);
+ finishAnimation();
}
/** Unregisters the back to launcher callback in shell. */
@@ -292,6 +269,14 @@
}
private void startBack(BackMotionEvent backEvent) {
+ // in case we're still animating an onBackCancelled event, let's remove the finish-
+ // callback from the progress animator to prevent calling finishAnimation() before
+ // restarting a new animation
+ // Side note: startBack is never called during the post-commit phase if the back gesture
+ // was committed (not cancelled). BackAnimationController prevents that. Therefore we
+ // don't have to handle that case.
+ mProgressAnimator.removeOnBackCancelledFinishCallback();
+
mBackInProgress = true;
RemoteAnimationTarget appTarget = backEvent.getDepartingAnimationTarget();
@@ -314,7 +299,9 @@
new RemoteAnimationTarget[]{ mBackTarget });
setLauncherTargetViewVisible(false);
mCurrentRect.set(mStartRect);
- addScrimLayer();
+ if (mScrimLayer == null) {
+ addScrimLayer();
+ }
mTransaction.apply();
}
@@ -397,23 +384,6 @@
customizeStatusBarAppearance(progress > UPDATE_SYSUI_FLAGS_THRESHOLD);
}
- private void updateCancelProgress(float progress) {
- if (mBackTarget == null) {
- return;
- }
- mCurrentRect.set(
- Utilities.mapRange(progress, mCancelRect.left, mStartRect.left),
- Utilities.mapRange(progress, mCancelRect.top, mStartRect.top),
- Utilities.mapRange(progress, mCancelRect.right, mStartRect.right),
- Utilities.mapRange(progress, mCancelRect.bottom, mStartRect.bottom));
-
- float endCornerRadius = Utilities.mapRange(
- mBackProgress, mWindowScaleStartCornerRadius, mWindowScaleEndCornerRadius);
- float cornerRadius = Utilities.mapRange(
- progress, endCornerRadius, mWindowScaleStartCornerRadius);
- applyTransform(mCurrentRect, cornerRadius);
- }
-
/** Transform the target window to match the target rect. */
private void applyTransform(RectF targetRect, float cornerRadius) {
final float scale = targetRect.width() / mStartRect.width();
@@ -484,7 +454,6 @@
mBackInProgress = false;
mBackProgress = 0;
mTransformMatrix.reset();
- mCancelRect.setEmpty();
mCurrentRect.setEmpty();
mStartRect.setEmpty();
mInitialTouchPos.set(0, 0);
diff --git a/quickstep/src/com/android/quickstep/OverviewCommandHelper.java b/quickstep/src/com/android/quickstep/OverviewCommandHelper.java
index 56c9a00..0db50bf 100644
--- a/quickstep/src/com/android/quickstep/OverviewCommandHelper.java
+++ b/quickstep/src/com/android/quickstep/OverviewCommandHelper.java
@@ -310,7 +310,7 @@
.newHandler(gestureState, cmd.createTime);
interactionHandler.setGestureEndCallback(
() -> onTransitionComplete(cmd, interactionHandler));
- interactionHandler.initWhenReady();
+ interactionHandler.initWhenReady("OverviewCommandHelper: cmd.type=" + cmd.type);
RecentsAnimationListener recentAnimListener = new RecentsAnimationListener() {
@Override
diff --git a/quickstep/src/com/android/quickstep/inputconsumers/OtherActivityInputConsumer.java b/quickstep/src/com/android/quickstep/inputconsumers/OtherActivityInputConsumer.java
index 0f8ceba..fbbfc16 100644
--- a/quickstep/src/com/android/quickstep/inputconsumers/OtherActivityInputConsumer.java
+++ b/quickstep/src/com/android/quickstep/inputconsumers/OtherActivityInputConsumer.java
@@ -394,7 +394,8 @@
mInteractionHandler = mHandlerFactory.newHandler(mGestureState, touchTimeMs);
mInteractionHandler.setGestureEndCallback(this::onInteractionGestureFinished);
mMotionPauseDetector.setOnMotionPauseListener(mInteractionHandler.getMotionPauseListener());
- mInteractionHandler.initWhenReady();
+ mInteractionHandler.initWhenReady(
+ "OtherActivityInputConsumer.startTouchTrackingForWindowAnimation");
if (mTaskAnimationManager.isRecentsAnimationRunning()) {
mActiveCallbacks = mTaskAnimationManager.continueRecentsAnimation(mGestureState);
diff --git a/quickstep/src/com/android/quickstep/util/ActivityInitListener.java b/quickstep/src/com/android/quickstep/util/ActivityInitListener.java
index aeec36f..5efbb40 100644
--- a/quickstep/src/com/android/quickstep/util/ActivityInitListener.java
+++ b/quickstep/src/com/android/quickstep/util/ActivityInitListener.java
@@ -57,16 +57,16 @@
* Registers the activity-created listener. If the activity is already created, then the
* callback provided in the constructor will be called synchronously.
*/
- public void register() {
+ public void register(String reasonString) {
mIsRegistered = true;
- mActivityTracker.registerCallback(this);
+ mActivityTracker.registerCallback(this, reasonString);
}
/**
* After calling this, we won't {@link #init} even when the activity is ready.
*/
- public void unregister() {
- mActivityTracker.unregisterCallback(this);
+ public void unregister(String reasonString) {
+ mActivityTracker.unregisterCallback(this, reasonString);
mIsRegistered = false;
mOnInitListener = null;
}
diff --git a/quickstep/tests/src/com/android/quickstep/FallbackRecentsTest.java b/quickstep/tests/src/com/android/quickstep/FallbackRecentsTest.java
index 077ca60..e4d8e92 100644
--- a/quickstep/tests/src/com/android/quickstep/FallbackRecentsTest.java
+++ b/quickstep/tests/src/com/android/quickstep/FallbackRecentsTest.java
@@ -22,6 +22,7 @@
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.testing.shared.TestProtocol.UPDATE_OVERVIEW_TARGETS_RUNNING_LATE;
import static com.android.launcher3.ui.AbstractLauncherUiTest.DEFAULT_ACTIVITY_TIMEOUT;
import static com.android.launcher3.ui.AbstractLauncherUiTest.DEFAULT_BROADCAST_TIMEOUT_SECS;
import static com.android.launcher3.ui.AbstractLauncherUiTest.DEFAULT_UI_TIMEOUT;
@@ -44,6 +45,7 @@
import android.content.Intent;
import android.content.pm.ActivityInfo;
import android.os.RemoteException;
+import android.util.Log;
import androidx.test.filters.LargeTest;
import androidx.test.runner.AndroidJUnit4;
@@ -59,6 +61,7 @@
import com.android.launcher3.testcomponent.TestCommandReceiver;
import com.android.launcher3.ui.AbstractLauncherUiTest;
import com.android.launcher3.util.Wait;
+import com.android.launcher3.util.rule.ExtendedLongPressTimeoutRule;
import com.android.launcher3.util.rule.FailureWatcher;
import com.android.launcher3.util.rule.SamplerRule;
import com.android.launcher3.util.rule.ScreenRecordRule;
@@ -105,6 +108,9 @@
@Rule
public ScreenRecordRule mScreenRecordRule = new ScreenRecordRule();
+ @Rule
+ public ExtendedLongPressTimeoutRule mLongPressTimeoutRule = new ExtendedLongPressTimeoutRule();
+
public FallbackRecentsTest() throws RemoteException {
Instrumentation instrumentation = getInstrumentation();
Context context = instrumentation.getContext();
@@ -129,6 +135,13 @@
getLauncherCommand(mOtherLauncherActivity));
updateHandler.mChangeCounter
.await(DEFAULT_BROADCAST_TIMEOUT_SECS, TimeUnit.SECONDS);
+ Log.d(UPDATE_OVERVIEW_TARGETS_RUNNING_LATE,
+ "AFTER AWAIT: mObserver home intent package name="
+ + updateHandler.mObserver.getHomeIntent()
+ .getComponent().getPackageName());
+ Log.d(UPDATE_OVERVIEW_TARGETS_RUNNING_LATE,
+ "AFTER AWAIT: mOtherLauncherActivity package name="
+ + mOtherLauncherActivity.packageName);
try {
base.evaluate();
} finally {
@@ -340,12 +353,25 @@
mRads = new RecentsAnimationDeviceState(ctx);
mObserver = new OverviewComponentObserver(ctx, mRads);
mChangeCounter = new CountDownLatch(1);
+ Log.d(UPDATE_OVERVIEW_TARGETS_RUNNING_LATE,
+ "OverviewUpdateHandler(Constructor): mObserver home intent package name="
+ + mObserver.getHomeIntent().getComponent().getPackageName());
+ Log.d(UPDATE_OVERVIEW_TARGETS_RUNNING_LATE,
+ "OverviewUpdateHandler(Constructor): mOtherLauncherActivity package name="
+ + mOtherLauncherActivity.packageName);
if (mObserver.getHomeIntent().getComponent()
.getPackageName().equals(mOtherLauncherActivity.packageName)) {
// Home already same
mChangeCounter.countDown();
} else {
- mObserver.setOverviewChangeListener(b -> mChangeCounter.countDown());
+ mObserver.setOverviewChangeListener(b -> {
+ Log.d(UPDATE_OVERVIEW_TARGETS_RUNNING_LATE,
+ "OverviewChangeListener(Callback): isHomeAndOverviewSame=" + b);
+ Log.d(UPDATE_OVERVIEW_TARGETS_RUNNING_LATE,
+ "OverviewChangeListener(Callback): mObserver home intent package name="
+ + mObserver.getHomeIntent().getComponent().getPackageName());
+ mChangeCounter.countDown();
+ });
}
}
diff --git a/res/layout/predicted_app_icon.xml b/res/layout/predicted_app_icon.xml
new file mode 100644
index 0000000..52df694
--- /dev/null
+++ b/res/layout/predicted_app_icon.xml
@@ -0,0 +1,17 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- 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.
+-->
+
+<com.android.launcher3.views.DoubleShadowBubbleTextView style="@style/BaseIcon.Workspace" />
diff --git a/src/com/android/launcher3/BubbleTextView.java b/src/com/android/launcher3/BubbleTextView.java
index 3ee1c61..1285aca 100644
--- a/src/com/android/launcher3/BubbleTextView.java
+++ b/src/com/android/launcher3/BubbleTextView.java
@@ -58,7 +58,6 @@
import androidx.annotation.VisibleForTesting;
import com.android.launcher3.accessibility.BaseAccessibilityDelegate;
-import com.android.launcher3.config.FeatureFlags;
import com.android.launcher3.dot.DotInfo;
import com.android.launcher3.dragndrop.DragOptions.PreDragCondition;
import com.android.launcher3.dragndrop.DraggableView;
@@ -96,10 +95,10 @@
public class BubbleTextView extends TextView implements ItemInfoUpdateReceiver,
IconLabelDotView, DraggableView, Reorderable {
- private static final int DISPLAY_WORKSPACE = 0;
+ public static final int DISPLAY_WORKSPACE = 0;
public static final int DISPLAY_ALL_APPS = 1;
- private static final int DISPLAY_FOLDER = 2;
- protected static final int DISPLAY_TASKBAR = 5;
+ public static final int DISPLAY_FOLDER = 2;
+ public static final int DISPLAY_TASKBAR = 5;
public static final int DISPLAY_SEARCH_RESULT = 6;
public static final int DISPLAY_SEARCH_RESULT_SMALL = 7;
public static final int DISPLAY_PREDICTION_ROW = 8;
diff --git a/src/com/android/launcher3/Launcher.java b/src/com/android/launcher3/Launcher.java
index e7d2843..269603c 100644
--- a/src/com/android/launcher3/Launcher.java
+++ b/src/com/android/launcher3/Launcher.java
@@ -2651,6 +2651,7 @@
mModel.dumpState(prefix, fd, writer, args);
mOverlayManager.dump(prefix, writer);
+ ACTIVITY_TRACKER.dump(prefix, writer);
}
/**
diff --git a/src/com/android/launcher3/apppairs/AppPairIcon.java b/src/com/android/launcher3/apppairs/AppPairIcon.java
index 13fefc4..bbeb341 100644
--- a/src/com/android/launcher3/apppairs/AppPairIcon.java
+++ b/src/com/android/launcher3/apppairs/AppPairIcon.java
@@ -16,12 +16,13 @@
package com.android.launcher3.apppairs;
+import static com.android.launcher3.BubbleTextView.DISPLAY_FOLDER;
+
import android.content.Context;
+import android.graphics.Paint;
import android.graphics.Rect;
import android.util.AttributeSet;
-import android.util.Log;
import android.view.LayoutInflater;
-import android.view.View;
import android.view.ViewGroup;
import android.widget.FrameLayout;
@@ -37,7 +38,6 @@
import com.android.launcher3.util.MultiTranslateDelegate;
import com.android.launcher3.views.ActivityContext;
-import java.util.Collections;
import java.util.Comparator;
import java.util.function.Predicate;
@@ -61,6 +61,9 @@
private BubbleTextView mAppPairName;
// The underlying ItemInfo that stores info about the app pair members, etc.
private FolderInfo mInfo;
+ // The containing element that holds this icon: workspace, taskbar, folder, etc. Affects certain
+ // aspects of how the icon is drawn.
+ private int mContainer;
// Required for Reorderable -- handles translation and bouncing movements
private final MultiTranslateDelegate mTranslateDelegate = new MultiTranslateDelegate(this);
@@ -78,7 +81,7 @@
* Builds an AppPairIcon to be added to the Launcher.
*/
public static AppPairIcon inflateIcon(int resId, ActivityContext activity,
- @Nullable ViewGroup group, FolderInfo appPairInfo) {
+ @Nullable ViewGroup group, FolderInfo appPairInfo, int container) {
DeviceProfile grid = activity.getDeviceProfile();
LayoutInflater inflater = (group != null)
? LayoutInflater.from(group.getContext())
@@ -86,31 +89,32 @@
AppPairIcon icon = (AppPairIcon) inflater.inflate(resId, group, false);
// Sort contents, so that left-hand app comes first
- Collections.sort(appPairInfo.contents, Comparator.comparingInt(a -> a.rank));
+ appPairInfo.contents.sort(Comparator.comparingInt(a -> a.rank));
- icon.setClipToPadding(false);
icon.setTag(appPairInfo);
icon.setOnClickListener(activity.getItemOnClickListener());
icon.mInfo = appPairInfo;
-
- // TODO (b/326664798): Delete this check, instead check at launcher load time
- if (icon.mInfo.contents.size() != 2) {
- Log.wtf(TAG, "AppPair contents not 2, size: " + icon.mInfo.contents.size());
- return icon;
- }
+ icon.mContainer = container;
// Set up icon drawable area
icon.mIconGraphic = icon.findViewById(R.id.app_pair_icon_graphic);
- icon.mIconGraphic.init(activity, icon);
+ icon.mIconGraphic.init(icon, container);
icon.checkDisabledState();
// Set up app pair title
icon.mAppPairName = icon.findViewById(R.id.app_pair_icon_name);
- icon.mAppPairName.setCompoundDrawablePadding(0);
FrameLayout.LayoutParams lp =
(FrameLayout.LayoutParams) icon.mAppPairName.getLayoutParams();
- lp.topMargin = grid.iconSizePx + grid.iconDrawablePaddingPx;
+ // Shift the title text down to leave room for the icon graphic. Since the icon graphic is
+ // a separate element (and not set as a CompoundDrawable on the BubbleTextView), we need to
+ // shift the text down manually.
+ lp.topMargin = container == DISPLAY_FOLDER
+ ? grid.folderChildIconSizePx + grid.folderChildDrawablePaddingPx
+ : grid.iconSizePx + grid.iconDrawablePaddingPx;
+ // For some reason, app icons have setIncludeFontPadding(false) inside folders, so we set it
+ // here to match that.
+ icon.mAppPairName.setIncludeFontPadding(container != DISPLAY_FOLDER);
icon.mAppPairName.setText(appPairInfo.title);
// Set up accessibility
@@ -174,7 +178,11 @@
return mInfo;
}
- public View getIconDrawableArea() {
+ public BubbleTextView getTitleTextView() {
+ return mAppPairName;
+ }
+
+ public AppPairIconGraphic getIconDrawableArea() {
return mIconGraphic;
}
@@ -195,8 +203,8 @@
mIsLaunchableAtScreenSize =
dp.isTablet || getInfo().contents.stream().noneMatch(
wii -> wii.hasStatusFlag(WorkspaceItemInfo.FLAG_NON_RESIZEABLE));
- // Call applyIcons to check and update icons
- mIconGraphic.applyIcons();
+ // Invalidate to update icons
+ mIconGraphic.redraw();
}
/**
@@ -207,7 +215,25 @@
// updated apps), redraw the icon graphic (icon background and both icons).
if (getInfo().contents.stream().anyMatch(itemCheck)) {
checkDisabledState();
- mIconGraphic.invalidate();
}
}
+
+ /**
+ * Inside folders, icons are vertically centered in their rows. See
+ * {@link BubbleTextView#onMeasure(int, int)} for comparison.
+ */
+ @Override
+ protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
+ if (mContainer == DISPLAY_FOLDER) {
+ int height = MeasureSpec.getSize(heightMeasureSpec);
+ ActivityContext activity = ActivityContext.lookupContext(getContext());
+ Paint.FontMetrics fm = mAppPairName.getPaint().getFontMetrics();
+ int cellHeightPx = activity.getDeviceProfile().folderChildIconSizePx
+ + activity.getDeviceProfile().folderChildDrawablePaddingPx
+ + (int) Math.ceil(fm.bottom - fm.top);
+ setPadding(getPaddingLeft(), (height - cellHeightPx) / 2, getPaddingRight(),
+ getPaddingBottom());
+ }
+ super.onMeasure(widthMeasureSpec, heightMeasureSpec);
+ }
}
diff --git a/src/com/android/launcher3/apppairs/AppPairIconBackground.java b/src/com/android/launcher3/apppairs/AppPairIconBackground.java
deleted file mode 100644
index 187541f..0000000
--- a/src/com/android/launcher3/apppairs/AppPairIconBackground.java
+++ /dev/null
@@ -1,167 +0,0 @@
-/*
- * Copyright (C) 2023 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.apppairs;
-
-import android.content.Context;
-import android.content.res.TypedArray;
-import android.graphics.Canvas;
-import android.graphics.ColorFilter;
-import android.graphics.Paint;
-import android.graphics.PixelFormat;
-import android.graphics.RectF;
-import android.graphics.drawable.Drawable;
-import android.os.Build;
-
-import com.android.launcher3.R;
-
-/**
- * A Drawable for the background behind the twin app icons (looks like two rectangles).
- */
-class AppPairIconBackground extends Drawable {
- // The underlying view that we are drawing this background on.
- private final AppPairIconGraphic icon;
- private final Paint mBackgroundPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
-
- /**
- * Null values to use with
- * {@link Canvas#drawDoubleRoundRect(RectF, float[], RectF, float[], Paint)}, since there
- * doesn't seem to be any other API for drawing rectangles with 4 different corner radii.
- */
- private static final RectF EMPTY_RECT = new RectF();
- private static final float[] ARRAY_OF_ZEROES = new float[8];
-
- AppPairIconBackground(Context context, AppPairIconGraphic iconGraphic) {
- icon = iconGraphic;
- // Set up background paint color
- TypedArray ta = context.getTheme().obtainStyledAttributes(R.styleable.FolderIconPreview);
- mBackgroundPaint.setStyle(Paint.Style.FILL);
- mBackgroundPaint.setColor(
- ta.getColor(R.styleable.FolderIconPreview_folderPreviewColor, 0));
- ta.recycle();
- }
-
- @Override
- public void draw(Canvas canvas) {
- if (icon.isLeftRightSplit()) {
- drawLeftRightSplit(canvas);
- } else {
- drawTopBottomSplit(canvas);
- }
- }
-
- /**
- * When device is in landscape, we draw the rectangles with a left-right split.
- */
- private void drawLeftRightSplit(Canvas canvas) {
- // Get the bounds where we will draw the background image
- int width = getBounds().width();
- int height = getBounds().height();
-
- // The left half of the background image, excluding center channel
- RectF leftSide = new RectF(
- 0,
- 0,
- (width / 2f) - (icon.getCenterChannelSize() / 2f),
- height
- );
- // The right half of the background image, excluding center channel
- RectF rightSide = new RectF(
- (width / 2f) + (icon.getCenterChannelSize() / 2f),
- 0,
- width,
- height
- );
-
- drawCustomRoundedRect(canvas, leftSide, new float[]{
- icon.getBigRadius(), icon.getBigRadius(),
- icon.getSmallRadius(), icon.getSmallRadius(),
- icon.getSmallRadius(), icon.getSmallRadius(),
- icon.getBigRadius(), icon.getBigRadius()});
- drawCustomRoundedRect(canvas, rightSide, new float[]{
- icon.getSmallRadius(), icon.getSmallRadius(),
- icon.getBigRadius(), icon.getBigRadius(),
- icon.getBigRadius(), icon.getBigRadius(),
- icon.getSmallRadius(), icon.getSmallRadius()});
- }
-
- /**
- * When device is in portrait, we draw the rectangles with a top-bottom split.
- */
- private void drawTopBottomSplit(Canvas canvas) {
- // Get the bounds where we will draw the background image
- int width = getBounds().width();
- int height = getBounds().height();
-
- // The top half of the background image, excluding center channel
- RectF topSide = new RectF(
- 0,
- 0,
- width,
- (height / 2f) - (icon.getCenterChannelSize() / 2f)
- );
- // The bottom half of the background image, excluding center channel
- RectF bottomSide = new RectF(
- 0,
- (height / 2f) + (icon.getCenterChannelSize() / 2f),
- width,
- height
- );
-
- drawCustomRoundedRect(canvas, topSide, new float[]{
- icon.getBigRadius(), icon.getBigRadius(),
- icon.getBigRadius(), icon.getBigRadius(),
- icon.getSmallRadius(), icon.getSmallRadius(),
- icon.getSmallRadius(), icon.getSmallRadius()});
- drawCustomRoundedRect(canvas, bottomSide, new float[]{
- icon.getSmallRadius(), icon.getSmallRadius(),
- icon.getSmallRadius(), icon.getSmallRadius(),
- icon.getBigRadius(), icon.getBigRadius(),
- icon.getBigRadius(), icon.getBigRadius()});
- }
-
- /**
- * Draws a rectangle with custom rounded corners.
- * @param c The Canvas to draw on.
- * @param rect The bounds of the rectangle.
- * @param radii An array of 8 radii for the corners: top left x, top left y, top right x, top
- * right y, bottom right x, and so on.
- */
- private void drawCustomRoundedRect(Canvas c, RectF rect, float[] radii) {
- if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) {
- // Canvas.drawDoubleRoundRect is supported from Q onward
- c.drawDoubleRoundRect(rect, radii, EMPTY_RECT, ARRAY_OF_ZEROES, mBackgroundPaint);
- } else {
- // Fallback rectangle with uniform rounded corners
- c.drawRoundRect(rect, icon.getBigRadius(), icon.getBigRadius(), mBackgroundPaint);
- }
- }
-
- @Override
- public int getOpacity() {
- return PixelFormat.OPAQUE;
- }
-
- @Override
- public void setAlpha(int i) {
- mBackgroundPaint.setAlpha(i);
- }
-
- @Override
- public void setColorFilter(ColorFilter colorFilter) {
- mBackgroundPaint.setColorFilter(colorFilter);
- }
-}
diff --git a/src/com/android/launcher3/apppairs/AppPairIconDrawable.java b/src/com/android/launcher3/apppairs/AppPairIconDrawable.java
new file mode 100644
index 0000000..c0ac11a
--- /dev/null
+++ b/src/com/android/launcher3/apppairs/AppPairIconDrawable.java
@@ -0,0 +1,208 @@
+/*
+ * Copyright (C) 2023 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.apppairs;
+
+import android.graphics.Canvas;
+import android.graphics.ColorFilter;
+import android.graphics.Paint;
+import android.graphics.PixelFormat;
+import android.graphics.RectF;
+import android.graphics.drawable.Drawable;
+import android.os.Build;
+
+import androidx.annotation.NonNull;
+
+import com.android.launcher3.icons.FastBitmapDrawable;
+
+/**
+ * A composed Drawable consisting of the two app pair icons and the background behind them (looks
+ * like two rectangles).
+ */
+class AppPairIconDrawable extends Drawable {
+ private final Paint mBackgroundPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
+ private final AppPairIconDrawingParams mP;
+ private final FastBitmapDrawable mIcon1;
+ private final FastBitmapDrawable mIcon2;
+
+ /**
+ * Null values to use with
+ * {@link Canvas#drawDoubleRoundRect(RectF, float[], RectF, float[], Paint)}, since there
+ * doesn't seem to be any other API for drawing rectangles with 4 different corner radii.
+ */
+ private static final RectF EMPTY_RECT = new RectF();
+ private static final float[] ARRAY_OF_ZEROES = new float[8];
+
+ AppPairIconDrawable(
+ AppPairIconDrawingParams p, FastBitmapDrawable icon1, FastBitmapDrawable icon2) {
+ mP = p;
+ mBackgroundPaint.setStyle(Paint.Style.FILL);
+ mBackgroundPaint.setColor(p.getBgColor());
+ mIcon1 = icon1;
+ mIcon2 = icon2;
+ }
+
+ @Override
+ public void draw(@NonNull Canvas canvas) {
+ if (mP.isLeftRightSplit()) {
+ drawLeftRightSplit(canvas);
+ } else {
+ drawTopBottomSplit(canvas);
+ }
+
+ canvas.translate(
+ mP.getStandardIconPadding() + mP.getOuterPadding(),
+ mP.getStandardIconPadding() + mP.getOuterPadding()
+ );
+
+ // Draw first icon.
+ canvas.save();
+ // The app icons are placed differently depending on device orientation.
+ if (mP.isLeftRightSplit()) {
+ canvas.translate(
+ mP.getInnerPadding(),
+ mP.getBackgroundSize() / 2f - mP.getMemberIconSize() / 2f
+ );
+ } else {
+ canvas.translate(
+ mP.getBackgroundSize() / 2f - mP.getMemberIconSize() / 2f,
+ mP.getInnerPadding()
+ );
+ }
+
+ mIcon1.draw(canvas);
+ canvas.restore();
+
+ // Draw second icon.
+ canvas.save();
+ // The app icons are placed differently depending on device orientation.
+ if (mP.isLeftRightSplit()) {
+ canvas.translate(
+ mP.getBackgroundSize() - (mP.getInnerPadding() + mP.getMemberIconSize()),
+ mP.getBackgroundSize() / 2f - mP.getMemberIconSize() / 2f
+ );
+ } else {
+ canvas.translate(
+ mP.getBackgroundSize() / 2f - mP.getMemberIconSize() / 2f,
+ mP.getBackgroundSize() - (mP.getInnerPadding() + mP.getMemberIconSize())
+ );
+ }
+
+ mIcon2.draw(canvas);
+ }
+
+ /**
+ * When device is in landscape, we draw the rectangles with a left-right split.
+ */
+ private void drawLeftRightSplit(Canvas canvas) {
+ // Get the bounds where we will draw the background image
+ int width = mP.getIconSize();
+ int height = mP.getIconSize();
+
+ // The left half of the background image, excluding center channel
+ RectF leftSide = new RectF(
+ mP.getStandardIconPadding() + mP.getOuterPadding(),
+ mP.getStandardIconPadding() + mP.getOuterPadding(),
+ (width / 2f) - (mP.getCenterChannelSize() / 2f),
+ height - (mP.getStandardIconPadding() + mP.getOuterPadding())
+ );
+ // The right half of the background image, excluding center channel
+ RectF rightSide = new RectF(
+ (width / 2f) + (mP.getCenterChannelSize() / 2f),
+ (mP.getStandardIconPadding() + mP.getOuterPadding()),
+ width - (mP.getStandardIconPadding() + mP.getOuterPadding()),
+ height - (mP.getStandardIconPadding() + mP.getOuterPadding())
+ );
+
+ drawCustomRoundedRect(canvas, leftSide, new float[]{
+ mP.getBigRadius(), mP.getBigRadius(),
+ mP.getSmallRadius(), mP.getSmallRadius(),
+ mP.getSmallRadius(), mP.getSmallRadius(),
+ mP.getBigRadius(), mP.getBigRadius()});
+ drawCustomRoundedRect(canvas, rightSide, new float[]{
+ mP.getSmallRadius(), mP.getSmallRadius(),
+ mP.getBigRadius(), mP.getBigRadius(),
+ mP.getBigRadius(), mP.getBigRadius(),
+ mP.getSmallRadius(), mP.getSmallRadius()});
+ }
+
+ /**
+ * When device is in portrait, we draw the rectangles with a top-bottom split.
+ */
+ private void drawTopBottomSplit(Canvas canvas) {
+ // Get the bounds where we will draw the background image
+ int width = mP.getIconSize();
+ int height = mP.getIconSize();
+
+ // The top half of the background image, excluding center channel
+ RectF topSide = new RectF(
+ (mP.getStandardIconPadding() + mP.getOuterPadding()),
+ (mP.getStandardIconPadding() + mP.getOuterPadding()),
+ width - (mP.getStandardIconPadding() + mP.getOuterPadding()),
+ (height / 2f) - (mP.getCenterChannelSize() / 2f)
+ );
+ // The bottom half of the background image, excluding center channel
+ RectF bottomSide = new RectF(
+ (mP.getStandardIconPadding() + mP.getOuterPadding()),
+ (height / 2f) + (mP.getCenterChannelSize() / 2f),
+ width - (mP.getStandardIconPadding() + mP.getOuterPadding()),
+ height - (mP.getStandardIconPadding() + mP.getOuterPadding())
+ );
+
+ drawCustomRoundedRect(canvas, topSide, new float[]{
+ mP.getBigRadius(), mP.getBigRadius(),
+ mP.getBigRadius(), mP.getBigRadius(),
+ mP.getSmallRadius(), mP.getSmallRadius(),
+ mP.getSmallRadius(), mP.getSmallRadius()});
+ drawCustomRoundedRect(canvas, bottomSide, new float[]{
+ mP.getSmallRadius(), mP.getSmallRadius(),
+ mP.getSmallRadius(), mP.getSmallRadius(),
+ mP.getBigRadius(), mP.getBigRadius(),
+ mP.getBigRadius(), mP.getBigRadius()});
+ }
+
+ /**
+ * Draws a rectangle with custom rounded corners.
+ * @param c The Canvas to draw on.
+ * @param rect The bounds of the rectangle.
+ * @param radii An array of 8 radii for the corners: top left x, top left y, top right x, top
+ * right y, bottom right x, and so on.
+ */
+ private void drawCustomRoundedRect(Canvas c, RectF rect, float[] radii) {
+ if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) {
+ // Canvas.drawDoubleRoundRect is supported from Q onward
+ c.drawDoubleRoundRect(rect, radii, EMPTY_RECT, ARRAY_OF_ZEROES, mBackgroundPaint);
+ } else {
+ // Fallback rectangle with uniform rounded corners
+ c.drawRoundRect(rect, mP.getBigRadius(), mP.getBigRadius(), mBackgroundPaint);
+ }
+ }
+
+ @Override
+ public int getOpacity() {
+ return PixelFormat.OPAQUE;
+ }
+
+ @Override
+ public void setAlpha(int i) {
+ mBackgroundPaint.setAlpha(i);
+ }
+
+ @Override
+ public void setColorFilter(ColorFilter colorFilter) {
+ mBackgroundPaint.setColorFilter(colorFilter);
+ }
+}
diff --git a/src/com/android/launcher3/apppairs/AppPairIconDrawingParams.kt b/src/com/android/launcher3/apppairs/AppPairIconDrawingParams.kt
new file mode 100644
index 0000000..62e5771
--- /dev/null
+++ b/src/com/android/launcher3/apppairs/AppPairIconDrawingParams.kt
@@ -0,0 +1,98 @@
+/*
+ * Copyright (C) 2024 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.apppairs
+
+import android.content.Context
+import com.android.launcher3.BubbleTextView.DISPLAY_FOLDER
+import com.android.launcher3.DeviceProfile
+import com.android.launcher3.R
+import com.android.launcher3.views.ActivityContext
+
+class AppPairIconDrawingParams(val context: Context, container: Int) {
+ companion object {
+ // Design specs -- the below ratios are in relation to the size of a standard app icon.
+ // Note: The standard app icon has two sizes. One is the full size of the drawable (returned
+ // by dp.iconSizePx), and one is the visual size of the icon on-screen (11/12 of that).
+ // Hence the calculations below.
+ const val STANDARD_ICON_PADDING = 1 / 24f
+ const val STANDARD_ICON_SHRINK = 1 - STANDARD_ICON_PADDING * 2
+ // App pairs are slightly smaller than the *visual* size of a standard icon, so all ratios
+ // are calculated with that in mind.
+ const val OUTER_PADDING_SCALE = 1 / 30f * STANDARD_ICON_SHRINK
+ const val INNER_PADDING_SCALE = 1 / 24f * STANDARD_ICON_SHRINK
+ const val CENTER_CHANNEL_SCALE = 1 / 30f * STANDARD_ICON_SHRINK
+ const val BIG_RADIUS_SCALE = 1 / 5f * STANDARD_ICON_SHRINK
+ const val SMALL_RADIUS_SCALE = 1 / 15f * STANDARD_ICON_SHRINK
+ const val MEMBER_ICON_SCALE = 11 / 30f * STANDARD_ICON_SHRINK
+ }
+
+ // The size at which this graphic will be drawn.
+ val iconSize: Int
+ // Standard app icons are padded by this amount on each side.
+ val standardIconPadding: Float
+ // App pair icons are slightly smaller than regular icons, so we pad the icon by this much on
+ // each side.
+ val outerPadding: Float
+ // The colored background (two rectangles in a square area) is this big.
+ val backgroundSize: Float
+ // The size of the channel between the two halves of the app pair icon.
+ val centerChannelSize: Float
+ // The corner radius of the outside corners.
+ val bigRadius: Float
+ // The corner radius of the inside corners, touching the center channel.
+ val smallRadius: Float
+ // Inside of the icon, the two member apps are padded by this much.
+ val innerPadding: Float
+ // The two member apps have icons that are this big (in diameter).
+ val memberIconSize: Float
+ // The app pair icon appears differently in portrait and landscape.
+ var isLeftRightSplit: Boolean = true
+ // The background paint color (based on container).
+ val bgColor: Int
+
+ init {
+ val activity: ActivityContext = ActivityContext.lookupContext(context)
+ val dp = activity.deviceProfile
+ iconSize = if (container == DISPLAY_FOLDER) dp.folderChildIconSizePx else dp.iconSizePx
+ standardIconPadding = iconSize * STANDARD_ICON_PADDING
+ outerPadding = iconSize * OUTER_PADDING_SCALE
+ backgroundSize = iconSize * STANDARD_ICON_SHRINK - (outerPadding * 2)
+ centerChannelSize = iconSize * CENTER_CHANNEL_SCALE
+ bigRadius = iconSize * BIG_RADIUS_SCALE
+ smallRadius = iconSize * SMALL_RADIUS_SCALE
+ innerPadding = iconSize * INNER_PADDING_SCALE
+ memberIconSize = iconSize * MEMBER_ICON_SCALE
+ updateOrientation(dp)
+ if (container == DISPLAY_FOLDER) {
+ val ta =
+ context.theme.obtainStyledAttributes(
+ intArrayOf(R.attr.materialColorSurfaceContainerLowest)
+ )
+ bgColor = ta.getColor(0, 0)
+ ta.recycle()
+ } else {
+ val ta = context.theme.obtainStyledAttributes(R.styleable.FolderIconPreview)
+ bgColor = ta.getColor(R.styleable.FolderIconPreview_folderPreviewColor, 0)
+ ta.recycle()
+ }
+ }
+
+ /** Checks the device orientation and updates isLeftRightSplit accordingly. */
+ fun updateOrientation(dp: DeviceProfile) {
+ isLeftRightSplit = dp.isLeftRightSplit
+ }
+}
diff --git a/src/com/android/launcher3/apppairs/AppPairIconGraphic.kt b/src/com/android/launcher3/apppairs/AppPairIconGraphic.kt
index 777831b..04050b0 100644
--- a/src/com/android/launcher3/apppairs/AppPairIconGraphic.kt
+++ b/src/com/android/launcher3/apppairs/AppPairIconGraphic.kt
@@ -21,14 +21,14 @@
import android.graphics.Rect
import android.graphics.drawable.Drawable
import android.util.AttributeSet
-import android.util.Log
import android.view.Gravity
import android.widget.FrameLayout
import com.android.launcher3.DeviceProfile
import com.android.launcher3.DeviceProfile.OnDeviceProfileChangeListener
import com.android.launcher3.icons.BitmapInfo
-import com.android.launcher3.icons.FastBitmapDrawable
import com.android.launcher3.icons.FastBitmapDrawable.getDisabledColorFilter
+import com.android.launcher3.model.data.FolderInfo
+import com.android.launcher3.model.data.WorkspaceItemInfo
import com.android.launcher3.util.Themes
import com.android.launcher3.views.ActivityContext
@@ -41,161 +41,101 @@
private val TAG = "AppPairIconGraphic"
companion object {
- // Design specs -- the below ratios are in relation to the size of a standard app icon.
- private const val OUTER_PADDING_SCALE = 1 / 30f
- private const val INNER_PADDING_SCALE = 1 / 24f
- private const val MEMBER_ICON_SCALE = 11 / 30f
- private const val CENTER_CHANNEL_SCALE = 1 / 30f
- private const val BIG_RADIUS_SCALE = 1 / 5f
- private const val SMALL_RADIUS_SCALE = 1 / 15f
+ /** Composes a drawable for this icon, consisting of a background and 2 app icons. */
+ @JvmStatic
+ fun composeDrawable(appPairInfo: FolderInfo, p: AppPairIconDrawingParams): Drawable {
+ // Generate new icons, using themed flag if needed.
+ val flags = if (Themes.isThemedIconEnabled(p.context)) BitmapInfo.FLAG_THEMED else 0
+ val appIcon1 = appPairInfo.contents[0].newIcon(p.context, flags)
+ val appIcon2 = appPairInfo.contents[1].newIcon(p.context, flags)
+ appIcon1.setBounds(0, 0, p.memberIconSize.toInt(), p.memberIconSize.toInt())
+ appIcon2.setBounds(0, 0, p.memberIconSize.toInt(), p.memberIconSize.toInt())
+
+ // Check disabled status.
+ val activity: ActivityContext = ActivityContext.lookupContext(p.context)
+ val isLaunchableAtScreenSize =
+ activity.deviceProfile.isTablet ||
+ appPairInfo.contents.stream().noneMatch { wii: WorkspaceItemInfo ->
+ wii.hasStatusFlag(WorkspaceItemInfo.FLAG_NON_RESIZEABLE)
+ }
+ val shouldDrawAsDisabled = appPairInfo.isDisabled || !isLaunchableAtScreenSize
+
+ // Set disabled status on icons.
+ appIcon1.setIsDisabled(shouldDrawAsDisabled)
+ appIcon2.setIsDisabled(shouldDrawAsDisabled)
+
+ // Create icon drawable.
+ val fullIconDrawable = AppPairIconDrawable(p, appIcon1, appIcon2)
+ fullIconDrawable.setBounds(0, 0, p.iconSize, p.iconSize)
+
+ // Set disabled color filter on background paint.
+ fullIconDrawable.colorFilter =
+ if (shouldDrawAsDisabled) getDisabledColorFilter() else null
+
+ return fullIconDrawable
+ }
}
- // App pair icons are slightly smaller than regular icons, so we pad the icon by this much on
- // each side.
- private var outerPadding = 0f
- // Inside of the icon, the two member apps are padded by this much.
- private var innerPadding = 0f
- // The colored background (two rectangles in a square area) is this big.
- private var backgroundSize = 0f
- // The two member apps have icons that are this big (in diameter).
- private var memberIconSize = 0f
- // The size of the center channel.
- var centerChannelSize = 0f
- // The large outer radius of the background rectangles.
- var bigRadius = 0f
- // The small inner radius of the background rectangles.
- var smallRadius = 0f
- // The app pairs icon appears differently in portrait and landscape.
- var isLeftRightSplit = false
-
- private lateinit var activityContext: ActivityContext
private lateinit var parentIcon: AppPairIcon
- private lateinit var appPairBackground: Drawable
- private lateinit var appIcon1: FastBitmapDrawable
- private lateinit var appIcon2: FastBitmapDrawable
+ private lateinit var drawParams: AppPairIconDrawingParams
+ private lateinit var drawable: Drawable
- fun init(activity: ActivityContext, icon: AppPairIcon) {
- activityContext = activity
-
- // Calculate device-specific measurements
- val defaultIconSize = activity.deviceProfile.iconSizePx
- outerPadding = OUTER_PADDING_SCALE * defaultIconSize
- innerPadding = INNER_PADDING_SCALE * defaultIconSize
- backgroundSize = defaultIconSize - outerPadding * 2
- memberIconSize = MEMBER_ICON_SCALE * defaultIconSize
- centerChannelSize = CENTER_CHANNEL_SCALE * defaultIconSize
- bigRadius = BIG_RADIUS_SCALE * defaultIconSize
- smallRadius = SMALL_RADIUS_SCALE * defaultIconSize
+ fun init(icon: AppPairIcon, container: Int) {
parentIcon = icon
- updateOrientation()
-
- appPairBackground = AppPairIconBackground(context, this)
- appPairBackground.setBounds(0, 0, backgroundSize.toInt(), backgroundSize.toInt())
-
- applyIcons()
+ drawParams = AppPairIconDrawingParams(context, container)
+ drawable = composeDrawable(icon.info, drawParams)
// Center the drawable area in the larger icon canvas
val lp: LayoutParams = layoutParams as LayoutParams
lp.gravity = Gravity.CENTER_HORIZONTAL
- lp.topMargin = outerPadding.toInt()
- lp.height = backgroundSize.toInt()
- lp.width = backgroundSize.toInt()
+ lp.height = drawParams.iconSize
+ lp.width = drawParams.iconSize
layoutParams = lp
}
override fun onAttachedToWindow() {
super.onAttachedToWindow()
- activityContext.addOnDeviceProfileChangeListener(this)
+ getActivityContext().addOnDeviceProfileChangeListener(this)
}
override fun onDetachedFromWindow() {
super.onDetachedFromWindow()
- activityContext.removeOnDeviceProfileChangeListener(this)
+ getActivityContext().removeOnDeviceProfileChangeListener(this)
}
- /** Checks the device orientation and updates isLeftRightSplit accordingly. */
- private fun updateOrientation() {
- val activity: ActivityContext = ActivityContext.lookupContext(context)
- isLeftRightSplit = activity.deviceProfile.isLeftRightSplit
+ private fun getActivityContext(): ActivityContext {
+ return ActivityContext.lookupContext(context)
}
/** When device profile changes, update orientation */
- override fun onDeviceProfileChanged(dp: DeviceProfile?) {
- updateOrientation()
+ override fun onDeviceProfileChanged(dp: DeviceProfile) {
+ drawParams.updateOrientation(dp)
+ redraw()
+ }
+
+ /** Updates the icon drawable and redraws it */
+ fun redraw() {
+ drawable = composeDrawable(parentIcon.info, drawParams)
invalidate()
}
- /** Sets up app pair member icons for drawing. */
- fun applyIcons() {
- val apps = parentIcon.info.contents
-
- // TODO (b/326664798): Delete this check, instead check at launcher load time
- if (apps.size != 2) {
- Log.wtf(TAG, "AppPair contents not 2, size: " + apps.size, Throwable())
- return
- }
-
- // Generate new icons, using themed flag if needed
- val flags = if (Themes.isThemedIconEnabled(context)) BitmapInfo.FLAG_THEMED else 0
- appIcon1 = apps[0].newIcon(context, flags)
- appIcon2 = apps[1].newIcon(context, flags)
- appIcon1.setBounds(0, 0, memberIconSize.toInt(), memberIconSize.toInt())
- appIcon2.setBounds(0, 0, memberIconSize.toInt(), memberIconSize.toInt())
-
- // Check disabled state
- val shouldDrawAsDisabled =
- parentIcon.info.isDisabled || !parentIcon.isLaunchableAtScreenSize
-
- appPairBackground.colorFilter = if (shouldDrawAsDisabled) getDisabledColorFilter() else null
- appIcon1.setIsDisabled(shouldDrawAsDisabled)
- appIcon2.setIsDisabled(shouldDrawAsDisabled)
- }
-
- /** Gets this icon graphic's bounds, with respect to the parent icon's coordinate system. */
+ /**
+ * Gets this icon graphic's visual bounds, with respect to the parent icon's coordinate system.
+ */
fun getIconBounds(outBounds: Rect) {
- outBounds.set(0, 0, backgroundSize.toInt(), backgroundSize.toInt())
+ outBounds.set(0, 0, drawParams.backgroundSize.toInt(), drawParams.backgroundSize.toInt())
+
outBounds.offset(
// x-coordinate in parent's coordinate system
- ((parentIcon.width - backgroundSize) / 2).toInt(),
+ ((parentIcon.width - drawParams.backgroundSize) / 2).toInt(),
// y-coordinate in parent's coordinate system
- parentIcon.paddingTop + outerPadding.toInt()
+ (parentIcon.paddingTop + drawParams.standardIconPadding + drawParams.outerPadding)
+ .toInt()
)
}
override fun dispatchDraw(canvas: Canvas) {
super.dispatchDraw(canvas)
-
- // Draw background
- appPairBackground.draw(canvas)
-
- // Draw first icon
- canvas.save()
- // The app icons are placed differently depending on device orientation.
- if (isLeftRightSplit) {
- canvas.translate(innerPadding, height / 2f - memberIconSize / 2f)
- } else {
- canvas.translate(width / 2f - memberIconSize / 2f, innerPadding)
- }
-
- appIcon1.draw(canvas)
- canvas.restore()
-
- // Draw second icon
- canvas.save()
- // The app icons are placed differently depending on device orientation.
- if (isLeftRightSplit) {
- canvas.translate(
- width - (innerPadding + memberIconSize),
- height / 2f - memberIconSize / 2f
- )
- } else {
- canvas.translate(
- width / 2f - memberIconSize / 2f,
- height - (innerPadding + memberIconSize)
- )
- }
-
- appIcon2.draw(canvas)
- canvas.restore()
+ drawable.draw(canvas)
}
}
diff --git a/src/com/android/launcher3/dragndrop/AddItemActivity.java b/src/com/android/launcher3/dragndrop/AddItemActivity.java
index 213c458..29aa216 100644
--- a/src/com/android/launcher3/dragndrop/AddItemActivity.java
+++ b/src/com/android/launcher3/dragndrop/AddItemActivity.java
@@ -258,7 +258,7 @@
.addCategory(Intent.CATEGORY_HOME)
.setPackage(getPackageName())
.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
- Launcher.ACTIVITY_TRACKER.registerCallback(listener);
+ Launcher.ACTIVITY_TRACKER.registerCallback(listener, "AddItemActivity.onLongClick");
startActivity(homeIntent, ApiWrapper.createFadeOutAnimOptions(this).toBundle());
logCommand(LAUNCHER_ADD_EXTERNAL_ITEM_DRAGGED);
mFinishOnPause = true;
diff --git a/src/com/android/launcher3/graphics/LauncherPreviewRenderer.java b/src/com/android/launcher3/graphics/LauncherPreviewRenderer.java
index e0a6627..9aee379 100644
--- a/src/com/android/launcher3/graphics/LauncherPreviewRenderer.java
+++ b/src/com/android/launcher3/graphics/LauncherPreviewRenderer.java
@@ -20,6 +20,8 @@
import static android.view.View.MeasureSpec.makeMeasureSpec;
import static android.view.View.VISIBLE;
+import static com.android.launcher3.BubbleTextView.DISPLAY_TASKBAR;
+import static com.android.launcher3.BubbleTextView.DISPLAY_WORKSPACE;
import static com.android.launcher3.DeviceProfile.DEFAULT_SCALE;
import static com.android.launcher3.LauncherSettings.Favorites.CONTAINER_HOTSEAT_PREDICTION;
import static com.android.launcher3.config.FeatureFlags.shouldShowFirstPageWidget;
@@ -86,7 +88,6 @@
import com.android.launcher3.model.data.WorkspaceItemInfo;
import com.android.launcher3.pm.InstallSessionHelper;
import com.android.launcher3.pm.UserCache;
-import com.android.launcher3.uioverrides.PredictedAppIconInflater;
import com.android.launcher3.uioverrides.plugins.PluginManagerWrapper;
import com.android.launcher3.util.ComponentKey;
import com.android.launcher3.util.DisplayController;
@@ -388,12 +389,14 @@
}
private void inflateAndAddCollectionIcon(FolderInfo info) {
- CellLayout screen = info.container == Favorites.CONTAINER_DESKTOP
+ boolean isOnDesktop = info.container == Favorites.CONTAINER_DESKTOP;
+ CellLayout screen = isOnDesktop
? mWorkspaceScreens.get(info.screenId)
: mHotseat;
FrameLayout folderIcon = info.itemType == Favorites.ITEM_TYPE_FOLDER
? FolderIcon.inflateIcon(R.layout.folder_icon, this, screen, info)
- : AppPairIcon.inflateIcon(R.layout.app_pair_icon, this, screen, info);
+ : AppPairIcon.inflateIcon(R.layout.app_pair_icon, this, screen, info,
+ isOnDesktop ? DISPLAY_WORKSPACE : DISPLAY_TASKBAR);
addInScreenFromBind(folderIcon, info);
}
@@ -453,10 +456,10 @@
private void inflateAndAddPredictedIcon(WorkspaceItemInfo info) {
CellLayout screen = mWorkspaceScreens.get(info.screenId);
- View view = PredictedAppIconInflater.inflate(mHomeElementInflater, screen, info);
- if (view != null) {
- addInScreenFromBind(view, info);
- }
+ BubbleTextView icon = (BubbleTextView) mHomeElementInflater.inflate(
+ R.layout.predicted_app_icon, screen, false);
+ icon.applyFromWorkspaceItem(info);
+ addInScreenFromBind(icon, info);
}
private void dispatchVisibilityAggregated(View view, boolean isVisible) {
diff --git a/src/com/android/launcher3/util/ActivityTracker.java b/src/com/android/launcher3/util/ActivityTracker.java
index 405d2bb..95a0511 100644
--- a/src/com/android/launcher3/util/ActivityTracker.java
+++ b/src/com/android/launcher3/util/ActivityTracker.java
@@ -18,10 +18,13 @@
import static com.android.launcher3.testing.shared.TestProtocol.GET_FROM_RECENTS_FAILURE;
import static com.android.launcher3.testing.shared.TestProtocol.testLogD;
+import android.util.Log;
+
import androidx.annotation.Nullable;
import com.android.launcher3.BaseActivity;
+import java.io.PrintWriter;
import java.lang.ref.WeakReference;
import java.util.concurrent.CopyOnWriteArrayList;
@@ -31,6 +34,8 @@
*/
public final class ActivityTracker<T extends BaseActivity> {
+ private static final String TAG = "ActivityTracker";
+
private WeakReference<T> mCurrentActivity = new WeakReference<>(null);
private CopyOnWriteArrayList<SchedulerCallback<T>> mCallbacks = new CopyOnWriteArrayList<>();
@@ -57,12 +62,13 @@
*
* @param callback The callback to call init() on when the activity is ready.
*/
- public void registerCallback(SchedulerCallback<T> callback) {
+ public void registerCallback(SchedulerCallback<T> callback, String reasonString) {
+ Log.d(TAG, "Registering callback: " + callback + ", reason=" + reasonString);
T activity = mCurrentActivity.get();
mCallbacks.add(callback);
if (activity != null) {
if (!callback.init(activity, activity.isStarted())) {
- unregisterCallback(callback);
+ unregisterCallback(callback, "ActivityTracker.registerCallback: Intent handled");
}
}
}
@@ -70,7 +76,8 @@
/**
* Unregisters a registered callback.
*/
- public void unregisterCallback(SchedulerCallback<T> callback) {
+ public void unregisterCallback(SchedulerCallback<T> callback, String reasonString) {
+ Log.d(TAG, "Unregistering callback: " + callback + ", reason=" + reasonString);
mCallbacks.remove(callback);
}
@@ -87,16 +94,25 @@
private boolean handleIntent(T activity, boolean alreadyOnHome) {
boolean handled = false;
+ if (!mCallbacks.isEmpty()) {
+ Log.d(TAG, "handleIntent: mCallbacks=" + mCallbacks);
+ }
for (SchedulerCallback<T> cb : mCallbacks) {
if (!cb.init(activity, alreadyOnHome)) {
// Callback doesn't want any more updates
- unregisterCallback(cb);
+ unregisterCallback(cb, "ActivityTracker.handleIntent: Intent handled");
}
handled = true;
}
return handled;
}
+ public void dump(String prefix, PrintWriter writer) {
+ writer.println(prefix + "ActivityTracker:");
+ writer.println(prefix + "\tmCurrentActivity=" + mCurrentActivity.get());
+ writer.println(prefix + "\tmCallbacks=" + mCallbacks);
+ }
+
public interface SchedulerCallback<T extends BaseActivity> {
/**
diff --git a/src/com/android/launcher3/util/ItemInflater.kt b/src/com/android/launcher3/util/ItemInflater.kt
index cc66af1..0f8311d 100644
--- a/src/com/android/launcher3/util/ItemInflater.kt
+++ b/src/com/android/launcher3/util/ItemInflater.kt
@@ -81,7 +81,8 @@
R.layout.app_pair_icon,
context,
parent,
- item as FolderInfo
+ item as FolderInfo,
+ BubbleTextView.DISPLAY_WORKSPACE
)
Favorites.ITEM_TYPE_APPWIDGET,
Favorites.ITEM_TYPE_CUSTOM_APPWIDGET ->
diff --git a/src/com/android/launcher3/widget/BaseWidgetSheet.java b/src/com/android/launcher3/widget/BaseWidgetSheet.java
index 5266448..76ffbbd 100644
--- a/src/com/android/launcher3/widget/BaseWidgetSheet.java
+++ b/src/com/android/launcher3/widget/BaseWidgetSheet.java
@@ -164,11 +164,11 @@
* Click handler for tap to add button.
*/
public void addWidget(PendingAddItemInfo info) {
- mActivityContext.getStatsLogManager().logger().withItemInfo(info).log(
- StatsLogManager.LauncherEvent.LAUNCHER_WIDGET_ADD_BUTTON_TAP);
handleClose(true);
Launcher.getLauncher(mActivityContext).getAccessibilityDelegate()
.addToWorkspace(info, /*accessibility=*/ false, /*finishCallback=*/ null);
+ mActivityContext.getStatsLogManager().logger().withItemInfo(info).log(
+ StatsLogManager.LauncherEvent.LAUNCHER_WIDGET_ADD_BUTTON_TAP);
}
@Override
diff --git a/src_ui_overrides/com/android/launcher3/uioverrides/PredictedAppIconInflater.java b/src_ui_overrides/com/android/launcher3/uioverrides/PredictedAppIconInflater.java
deleted file mode 100644
index 4893c17..0000000
--- a/src_ui_overrides/com/android/launcher3/uioverrides/PredictedAppIconInflater.java
+++ /dev/null
@@ -1,29 +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.uioverrides;
-
-import android.view.LayoutInflater;
-import android.view.View;
-import android.view.ViewGroup;
-
-import com.android.launcher3.model.data.WorkspaceItemInfo;
-
-/** A util class that inflates a predicted app icon */
-public class PredictedAppIconInflater {
- public static View inflate(LayoutInflater inflater, ViewGroup parent, WorkspaceItemInfo info) {
- return null;
- }
-}
diff --git a/tests/multivalentTests/shared/com/android/launcher3/testing/shared/TestProtocol.java b/tests/multivalentTests/shared/com/android/launcher3/testing/shared/TestProtocol.java
index e0fafcc..fea0330 100644
--- a/tests/multivalentTests/shared/com/android/launcher3/testing/shared/TestProtocol.java
+++ b/tests/multivalentTests/shared/com/android/launcher3/testing/shared/TestProtocol.java
@@ -180,6 +180,7 @@
public static final String TEST_TAPL_OVERVIEW_ACTIONS_MENU_FAILURE = "b/326073471";
public static final String WIDGET_CONFIG_NULL_EXTRA_INTENT = "b/324419890";
public static final String ACTIVITY_NOT_RESUMED_AFTER_BACK = "b/322823209";
+ public static final String UPDATE_OVERVIEW_TARGETS_RUNNING_LATE = "b/321775748";
public static final String REQUEST_EMULATE_DISPLAY = "emulate-display";
public static final String REQUEST_STOP_EMULATE_DISPLAY = "stop-emulate-display";
diff --git a/tests/src/com/android/launcher3/ui/AbstractLauncherUiTest.java b/tests/src/com/android/launcher3/ui/AbstractLauncherUiTest.java
index 972be80..d8635f9 100644
--- a/tests/src/com/android/launcher3/ui/AbstractLauncherUiTest.java
+++ b/tests/src/com/android/launcher3/ui/AbstractLauncherUiTest.java
@@ -66,6 +66,7 @@
import com.android.launcher3.util.SimpleBroadcastReceiver;
import com.android.launcher3.util.TestUtil;
import com.android.launcher3.util.Wait;
+import com.android.launcher3.util.rule.ExtendedLongPressTimeoutRule;
import com.android.launcher3.util.rule.FailureWatcher;
import com.android.launcher3.util.rule.SamplerRule;
import com.android.launcher3.util.rule.ScreenRecordRule;
@@ -219,6 +220,9 @@
@Rule
public SetFlagsRule mSetFlagsRule = new SetFlagsRule(DEVICE_DEFAULT);
+ @Rule
+ public ExtendedLongPressTimeoutRule mLongPressTimeoutRule = new ExtendedLongPressTimeoutRule();
+
public static void initialize(AbstractLauncherUiTest test) throws Exception {
test.reinitializeLauncherData();
test.mDevice.pressHome();
diff --git a/tests/src/com/android/launcher3/util/rule/ExtendedLongPressTimeoutRule.java b/tests/src/com/android/launcher3/util/rule/ExtendedLongPressTimeoutRule.java
new file mode 100644
index 0000000..702988c
--- /dev/null
+++ b/tests/src/com/android/launcher3/util/rule/ExtendedLongPressTimeoutRule.java
@@ -0,0 +1,74 @@
+/*
+ * Copyright (C) 2024 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.content.ContentResolver;
+import android.provider.Settings;
+import android.util.Log;
+import android.view.ViewConfiguration;
+
+import androidx.test.InstrumentationRegistry;
+
+import org.junit.rules.TestRule;
+import org.junit.runner.Description;
+import org.junit.runners.model.Statement;
+
+public class ExtendedLongPressTimeoutRule implements TestRule {
+
+ private static final String TAG = "ExtendedLongPressTimeoutRule";
+
+ private static final float LONG_PRESS_TIMEOUT_MULTIPLIER = 10f;
+
+ @Override
+ public Statement apply(Statement base, Description description) {
+ return new Statement() {
+ @Override
+ public void evaluate() throws Throwable {
+ ContentResolver contentResolver = InstrumentationRegistry.getInstrumentation()
+ .getContext()
+ .getContentResolver();
+ int prevLongPressTimeout = Settings.Secure.getInt(
+ contentResolver,
+ Settings.Secure.LONG_PRESS_TIMEOUT,
+ ViewConfiguration.getLongPressTimeout());
+ int newLongPressTimeout =
+ (int) (prevLongPressTimeout * LONG_PRESS_TIMEOUT_MULTIPLIER);
+
+ try {
+ Log.d(TAG, "In try-block: Setting long press timeout from "
+ + prevLongPressTimeout + "ms to " + newLongPressTimeout + "ms");
+ Settings.Secure.putInt(
+ contentResolver,
+ Settings.Secure.LONG_PRESS_TIMEOUT,
+ (int) (prevLongPressTimeout * LONG_PRESS_TIMEOUT_MULTIPLIER));
+
+ base.evaluate();
+ } catch (Exception e) {
+ Log.e(TAG, "Error", e);
+ throw e;
+ } finally {
+ Log.d(TAG, "In finally-block: resetting long press timeout to "
+ + prevLongPressTimeout + "ms");
+ Settings.Secure.putInt(
+ contentResolver,
+ Settings.Secure.LONG_PRESS_TIMEOUT,
+ prevLongPressTimeout);
+ }
+ }
+ };
+ }
+}
diff --git a/tests/tapl/com/android/launcher3/tapl/LauncherInstrumentation.java b/tests/tapl/com/android/launcher3/tapl/LauncherInstrumentation.java
index 0e523c3..c7d3754 100644
--- a/tests/tapl/com/android/launcher3/tapl/LauncherInstrumentation.java
+++ b/tests/tapl/com/android/launcher3/tapl/LauncherInstrumentation.java
@@ -1919,17 +1919,21 @@
}
private static MotionEvent getMotionEvent(long downTime, long eventTime, int action,
- float x, float y, int source) {
+ float x, float y, int source, int toolType) {
return MotionEvent.obtain(downTime, eventTime, action, 1,
- new MotionEvent.PointerProperties[]{getPointerProperties(0)},
+ new MotionEvent.PointerProperties[]{getPointerProperties(0, toolType)},
new MotionEvent.PointerCoords[]{getPointerCoords(x, y)},
0, 0, 1.0f, 1.0f, 0, 0, source, 0);
}
private static MotionEvent.PointerProperties getPointerProperties(int pointerId) {
+ return getPointerProperties(pointerId, Configurator.getInstance().getToolType());
+ }
+
+ private static MotionEvent.PointerProperties getPointerProperties(int pointerId, int toolType) {
MotionEvent.PointerProperties properties = new MotionEvent.PointerProperties();
properties.id = pointerId;
- properties.toolType = Configurator.getInstance().getToolType();
+ properties.toolType = toolType;
return properties;
}
@@ -1975,6 +1979,19 @@
public void sendPointer(long downTime, long currentTime, int action, Point point,
GestureScope gestureScope, int source, boolean isRightClick) {
+ sendPointer(
+ downTime,
+ currentTime,
+ action,
+ point,
+ gestureScope,
+ source,
+ isRightClick,
+ Configurator.getInstance().getToolType());
+ }
+
+ public void sendPointer(long downTime, long currentTime, int action, Point point,
+ GestureScope gestureScope, int source, boolean isRightClick, int toolType) {
final boolean hasTIS = hasTIS();
int pointerCount = mPointerCount;
@@ -2009,13 +2026,13 @@
? getTrackpadMotionEvent(
downTime, currentTime, action, point.x, point.y, pointerCount,
mTrackpadGestureType)
- : getMotionEvent(downTime, currentTime, action, point.x, point.y, source);
+ : getMotionEvent(downTime, currentTime, action, point.x, point.y, source, toolType);
if (action == MotionEvent.ACTION_BUTTON_PRESS
|| action == MotionEvent.ACTION_BUTTON_RELEASE) {
event.setActionButton(MotionEvent.BUTTON_PRIMARY);
}
if (isRightClick) {
- event.setButtonState(event.getButtonState() & MotionEvent.BUTTON_SECONDARY);
+ event.setButtonState(event.getButtonState() | MotionEvent.BUTTON_SECONDARY);
}
injectEvent(event);
}
@@ -2114,15 +2131,19 @@
@NonNull final UiObject2 target, @NonNull String resName, Pattern longClickEvent) {
final Point targetCenter = target.getVisibleCenter();
final long downTime = SystemClock.uptimeMillis();
+ // Use stylus secondary button press to prevent using the exteded long press timeout rule
+ // unnecessarily
sendPointer(downTime, downTime, MotionEvent.ACTION_DOWN, targetCenter,
- GestureScope.DONT_EXPECT_PILFER);
+ GestureScope.DONT_EXPECT_PILFER, InputDevice.SOURCE_TOUCHSCREEN,
+ /* isRightClick= */ true, MotionEvent.TOOL_TYPE_STYLUS);
try {
expectEvent(TestProtocol.SEQUENCE_MAIN, longClickEvent);
final UiObject2 result = waitForLauncherObject(resName);
return result;
} finally {
sendPointer(downTime, SystemClock.uptimeMillis(), MotionEvent.ACTION_UP, targetCenter,
- GestureScope.DONT_EXPECT_PILFER);
+ GestureScope.DONT_EXPECT_PILFER, InputDevice.SOURCE_TOUCHSCREEN,
+ /* isRightClick= */ true, MotionEvent.TOOL_TYPE_STYLUS);
}
}