Merge "Create an animation for instance filtering" into udc-dev
diff --git a/protos/launcher_atom.proto b/protos/launcher_atom.proto
index b1064f7..bf84820 100644
--- a/protos/launcher_atom.proto
+++ b/protos/launcher_atom.proto
@@ -177,7 +177,7 @@
ALL_APPS_SEARCH_RESULT_SUGGEST = 22 [deprecated = true];
ALL_APPS_SEARCH_RESULT_ASSISTANT = 23;
ALL_APPS_SEARCH_RESULT_CHROMETAB = 24;
- ALL_APPS_SEARCH_RESULT_NAVVYSITE = 25;
+ ALL_APPS_SEARCH_RESULT_NAVVYSITE = 25 [deprecated = true];
ALL_APPS_SEARCH_RESULT_TIPS = 26;
ALL_APPS_SEARCH_RESULT_PEOPLE_TILE = 27;
ALL_APPS_SEARCH_RESULT_LEGACY_SHORTCUT = 30;
diff --git a/quickstep/res/drawable/ic_save_app_pair.xml b/quickstep/res/drawable/ic_save_app_pair.xml
new file mode 100644
index 0000000..4a7ee1a
--- /dev/null
+++ b/quickstep/res/drawable/ic_save_app_pair.xml
@@ -0,0 +1,28 @@
+<?xml version="1.0" encoding="utf-8"?>
+
+<!--
+ 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.
+-->
+
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="24dp"
+ android:height="24dp"
+ android:viewportWidth="24"
+ android:viewportHeight="24">
+ <path
+ android:pathData="M13.329,2.305H4.242C2.751,2.305 1.542,3.514 1.542,5.005V13.005C1.542,14.496 2.751,15.705 4.242,15.705H7.875V19.011C7.875,20.502 9.084,21.711 10.575,21.711H19.662C21.153,21.711 22.362,20.502 22.362,19.011V10.011C22.362,8.52 21.153,7.311 19.662,7.311H16.029V5.005C16.029,3.514 14.821,2.305 13.329,2.305ZM14.329,7.311V5.005C14.329,4.452 13.882,4.005 13.329,4.005H4.242C3.69,4.005 3.242,4.452 3.242,5.005V13.005C3.242,13.557 3.69,14.005 4.242,14.005H7.875V10.011C7.875,8.52 9.084,7.311 10.575,7.311H14.329ZM9.575,14.005V10.011C9.575,9.611 9.81,9.266 10.15,9.106C10.285,9.037 10.438,8.999 10.6,8.999H19.687C20.239,8.999 20.687,9.447 20.687,9.999V18.999C20.687,19.399 20.452,19.744 20.113,19.904C19.977,19.972 19.824,20.011 19.662,20.011H10.575C10.023,20.011 9.575,19.563 9.575,19.011V15.705H9.6V14.005H9.575ZM15.542,11.996V14H17.588V15H15.542V16.996H14.542V15H12.464V14H14.542V11.996H15.542Z"
+ android:fillColor="#000000"
+ android:fillType="evenOdd"/>
+</vector>
diff --git a/quickstep/res/layout/taskbar_edu.xml b/quickstep/res/layout/taskbar_edu.xml
index d7daea3..f3856ba 100644
--- a/quickstep/res/layout/taskbar_edu.xml
+++ b/quickstep/res/layout/taskbar_edu.xml
@@ -55,7 +55,7 @@
style="@style/TaskbarEdu.Button.Close"
android:textColor="?android:attr/textColorPrimary"/>
- <com.android.launcher3.pageindicators.PageIndicatorDots
+ <com.android.launcher3.pageindicators.LauncherDotsPageIndicator
android:id="@+id/content_page_indicator"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
@@ -63,6 +63,7 @@
app:layout_constraintBottom_toBottomOf="@id/edu_start_button"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintEnd_toEndOf="parent"
+ app:indicatorDotColor="@color/folder_pagination_color"
android:elevation="1dp" />
<Button
diff --git a/quickstep/src/com/android/launcher3/QuickstepTransitionManager.java b/quickstep/src/com/android/launcher3/QuickstepTransitionManager.java
index 8baa324..7eecb29 100644
--- a/quickstep/src/com/android/launcher3/QuickstepTransitionManager.java
+++ b/quickstep/src/com/android/launcher3/QuickstepTransitionManager.java
@@ -321,7 +321,7 @@
ActivityOptions options = ActivityOptions.makeRemoteAnimation(
new RemoteAnimationAdapter(runner, duration, statusBarTransitionDelay),
new RemoteTransition(runner.toRemoteTransition(),
- mLauncher.getIApplicationThread()));
+ mLauncher.getIApplicationThread(), "QuickstepLaunch"));
return new ActivityOptionsWrapper(options, onEndCallback);
}
@@ -1122,7 +1122,7 @@
mLauncherOpenTransition = new RemoteTransition(
new LauncherAnimationRunner(mHandler, mWallpaperOpenTransitionRunner,
false /* startAtFrontOfQueue */).toRemoteTransition(),
- mLauncher.getIApplicationThread());
+ mLauncher.getIApplicationThread(), "QuickstepLaunchHome");
TransitionFilter homeCheck = new TransitionFilter();
// No need to handle the transition that also dismisses keyguard.
@@ -1472,13 +1472,13 @@
RemoteAnimationTarget target = appTargets[i];
SurfaceProperties builder = transaction.forSurface(target.leash);
- if (target.localBounds != null) {
- tmpPos.set(target.localBounds.left, target.localBounds.top);
+ if (target.screenSpaceBounds != null) {
+ tmpPos.set(target.screenSpaceBounds.left, target.screenSpaceBounds.top);
} else {
tmpPos.set(target.position.x, target.position.y);
}
- final Rect crop = new Rect(target.screenSpaceBounds);
+ final Rect crop = new Rect(target.localBounds);
crop.offsetTo(0, 0);
if (target.mode == MODE_CLOSING) {
tmpRect.set(target.screenSpaceBounds);
diff --git a/quickstep/src/com/android/launcher3/appprediction/PredictionRowView.java b/quickstep/src/com/android/launcher3/appprediction/PredictionRowView.java
index e8f2496..3510fbe 100644
--- a/quickstep/src/com/android/launcher3/appprediction/PredictionRowView.java
+++ b/quickstep/src/com/android/launcher3/appprediction/PredictionRowView.java
@@ -76,7 +76,6 @@
mFocusHelper = new SimpleFocusIndicatorHelper(this);
mActivityContext = ActivityContext.lookupContext(context);
- mActivityContext.addOnDeviceProfileChangeListener(this);
mNumPredictedAppsPerRow = mActivityContext.getDeviceProfile().numShownAllAppsColumns;
updateVisibility();
}
@@ -84,6 +83,13 @@
@Override
protected void onAttachedToWindow() {
super.onAttachedToWindow();
+ mActivityContext.addOnDeviceProfileChangeListener(this);
+ }
+
+ @Override
+ protected void onDetachedFromWindow() {
+ super.onDetachedFromWindow();
+ mActivityContext.removeOnDeviceProfileChangeListener(this);
}
public void setup(FloatingHeaderView parent, FloatingHeaderRow[] rows, boolean tabsHidden) {
diff --git a/quickstep/src/com/android/launcher3/taskbar/TaskbarActivityContext.java b/quickstep/src/com/android/launcher3/taskbar/TaskbarActivityContext.java
index 084e804..b61e9fc 100644
--- a/quickstep/src/com/android/launcher3/taskbar/TaskbarActivityContext.java
+++ b/quickstep/src/com/android/launcher3/taskbar/TaskbarActivityContext.java
@@ -264,6 +264,13 @@
}
}
+ /**
+ * Show Taskbar upon receiving broadcast
+ */
+ public void showTaskbarFromBroadcast() {
+ mControllers.taskbarStashController.showTaskbarFromBroadcast();
+ }
+
@Override
public DeviceProfile getDeviceProfile() {
return mDeviceProfile;
diff --git a/quickstep/src/com/android/launcher3/taskbar/TaskbarControllers.java b/quickstep/src/com/android/launcher3/taskbar/TaskbarControllers.java
index a6da56d..a71e5db 100644
--- a/quickstep/src/com/android/launcher3/taskbar/TaskbarControllers.java
+++ b/quickstep/src/com/android/launcher3/taskbar/TaskbarControllers.java
@@ -154,7 +154,7 @@
taskbarKeyguardController.init(navbarButtonsViewController);
taskbarSpringOnStashController.init(this);
stashedHandleViewController.init(this);
- taskbarStashController.init(this, sharedState.setupUIVisible);
+ taskbarStashController.init(this, sharedState.setupUIVisible, mSharedState);
taskbarEduController.init(this);
taskbarPopupController.init(this);
taskbarForceVisibleImmersiveController.init(this);
@@ -225,6 +225,7 @@
voiceInteractionWindowController.onDestroy();
taskbarRecentAppsController.onDestroy();
keyboardQuickSwitchController.onDestroy();
+ taskbarStashController.onDestroy();
mControllersToLog = null;
mBackgroundRendererControllers = null;
diff --git a/quickstep/src/com/android/launcher3/taskbar/TaskbarEduPagedView.java b/quickstep/src/com/android/launcher3/taskbar/TaskbarEduPagedView.java
index 6cd6512..d0e6386 100644
--- a/quickstep/src/com/android/launcher3/taskbar/TaskbarEduPagedView.java
+++ b/quickstep/src/com/android/launcher3/taskbar/TaskbarEduPagedView.java
@@ -25,12 +25,12 @@
import com.android.launcher3.AbstractFloatingView;
import com.android.launcher3.PagedView;
import com.android.launcher3.R;
-import com.android.launcher3.pageindicators.PageIndicatorDots;
+import com.android.launcher3.pageindicators.LauncherDotsPageIndicator;
import com.android.launcher3.taskbar.TaskbarEduController.TaskbarEduCallbacks;
import com.android.launcher3.views.ActivityContext;
/** Horizontal carousel of tutorial screens for Taskbar Edu. */
-public class TaskbarEduPagedView extends PagedView<PageIndicatorDots> {
+public class TaskbarEduPagedView extends PagedView<LauncherDotsPageIndicator> {
private TaskbarEduView mTaskbarEduView;
private TaskbarEduCallbacks mControllerCallbacks;
diff --git a/quickstep/src/com/android/launcher3/taskbar/TaskbarEduTooltipController.kt b/quickstep/src/com/android/launcher3/taskbar/TaskbarEduTooltipController.kt
index bd8e346..22ed284 100644
--- a/quickstep/src/com/android/launcher3/taskbar/TaskbarEduTooltipController.kt
+++ b/quickstep/src/com/android/launcher3/taskbar/TaskbarEduTooltipController.kt
@@ -62,7 +62,8 @@
get() = !Utilities.isRunningInTestHarness() && ENABLE_TASKBAR_EDU_TOOLTIP.get()
private val isOpen: Boolean
get() = tooltip?.isOpen ?: false
-
+ val isBeforeTooltipFeaturesStep: Boolean
+ get() = isTooltipEnabled && tooltipStep <= TOOLTIP_STEP_FEATURES
private lateinit var controllers: TaskbarControllers
@TaskbarEduTooltipStep
diff --git a/quickstep/src/com/android/launcher3/taskbar/TaskbarInsetsController.kt b/quickstep/src/com/android/launcher3/taskbar/TaskbarInsetsController.kt
index 1adbf39..5fedb3d 100644
--- a/quickstep/src/com/android/launcher3/taskbar/TaskbarInsetsController.kt
+++ b/quickstep/src/com/android/launcher3/taskbar/TaskbarInsetsController.kt
@@ -70,20 +70,9 @@
fun init(controllers: TaskbarControllers) {
this.controllers = controllers
windowLayoutParams = context.windowLayoutParams
- windowLayoutParams.providedInsets =
- arrayOf(
- InsetsFrameProvider(insetsOwner, 0, navigationBars()),
- InsetsFrameProvider(insetsOwner, 0, tappableElement()),
- InsetsFrameProvider(insetsOwner, 0, mandatorySystemGestures()),
- InsetsFrameProvider(insetsOwner, INDEX_LEFT, systemGestures())
- .setSource(SOURCE_DISPLAY),
- InsetsFrameProvider(insetsOwner, INDEX_RIGHT, systemGestures())
- .setSource(SOURCE_DISPLAY)
- )
-
+ windowLayoutParams.insetsRoundedCornerFrame = true
onTaskbarWindowHeightOrInsetsChanged()
- windowLayoutParams.insetsRoundedCornerFrame = true
context.addOnDeviceProfileChangeListener(deviceProfileChangeListener)
gestureNavSettingsObserver.registerForCallingUser()
}
@@ -94,6 +83,25 @@
}
fun onTaskbarWindowHeightOrInsetsChanged() {
+ if (context.isGestureNav) {
+ windowLayoutParams.providedInsets =
+ arrayOf(
+ InsetsFrameProvider(insetsOwner, 0, navigationBars()),
+ InsetsFrameProvider(insetsOwner, 0, tappableElement()),
+ InsetsFrameProvider(insetsOwner, 0, mandatorySystemGestures()),
+ InsetsFrameProvider(insetsOwner, INDEX_LEFT, systemGestures())
+ .setSource(SOURCE_DISPLAY),
+ InsetsFrameProvider(insetsOwner, INDEX_RIGHT, systemGestures())
+ .setSource(SOURCE_DISPLAY)
+ )
+ } else {
+ windowLayoutParams.providedInsets =
+ arrayOf(
+ InsetsFrameProvider(insetsOwner, 0, navigationBars()),
+ InsetsFrameProvider(insetsOwner, 0, tappableElement())
+ )
+ }
+
val touchableHeight = controllers.taskbarStashController.touchableHeight
touchableRegion.set(
0,
@@ -146,7 +154,8 @@
for (provider in windowLayoutParams.providedInsets) {
if (context.isGestureNav && provider.type == tappableElement()) {
provider.insetsSizeOverrides = insetsSizeOverrideForGestureNavTappableElement
- } else {
+ } else if (provider.type != systemGestures()) {
+ // We only override insets at the bottom of the screen
provider.insetsSizeOverrides = insetsSizeOverride
}
}
diff --git a/quickstep/src/com/android/launcher3/taskbar/TaskbarManager.java b/quickstep/src/com/android/launcher3/taskbar/TaskbarManager.java
index 4c131ee..c537106 100644
--- a/quickstep/src/com/android/launcher3/taskbar/TaskbarManager.java
+++ b/quickstep/src/com/android/launcher3/taskbar/TaskbarManager.java
@@ -15,17 +15,22 @@
*/
package com.android.launcher3.taskbar;
+import static android.content.Context.RECEIVER_NOT_EXPORTED;
import static android.content.pm.PackageManager.FEATURE_PC;
import static android.view.Display.DEFAULT_DISPLAY;
import static android.view.WindowManager.LayoutParams.TYPE_NAVIGATION_BAR_PANEL;
import static com.android.launcher3.util.DisplayController.CHANGE_DENSITY;
import static com.android.launcher3.util.DisplayController.CHANGE_NAVIGATION_MODE;
+import static com.android.launcher3.util.Executors.UI_HELPER_EXECUTOR;
import static com.android.launcher3.util.FlagDebugUtils.formatFlagChange;
+import android.annotation.SuppressLint;
+import android.app.PendingIntent;
import android.content.ComponentCallbacks;
import android.content.Context;
import android.content.Intent;
+import android.content.IntentFilter;
import android.content.pm.ActivityInfo;
import android.content.res.Configuration;
import android.hardware.display.DisplayManager;
@@ -116,6 +121,17 @@
private boolean mUserUnlocked = false;
+ public static final int SYSTEM_ACTION_ID_TASKBAR = 499;
+
+ /**
+ * For Taskbar broadcast intent filter.
+ */
+ public static final String ACTION_SHOW_TASKBAR = "ACTION_SHOW_TASKBAR";
+
+ private final SimpleBroadcastReceiver mTaskbarBroadcastReceiver =
+ new SimpleBroadcastReceiver(this::showTaskbarFromBroadcast);
+
+ @SuppressLint("WrongConstant")
public TaskbarManager(TouchInteractionService service) {
mDisplayController = DisplayController.INSTANCE.get(service);
Display display =
@@ -200,7 +216,17 @@
mNavBarKidsModeListener);
mContext.registerComponentCallbacks(mComponentCallbacks);
mShutdownReceiver.register(mContext, Intent.ACTION_SHUTDOWN);
-
+ UI_HELPER_EXECUTOR.execute(() -> {
+ mSharedState.taskbarSystemActionPendingIntent = PendingIntent.getBroadcast(
+ mContext,
+ SYSTEM_ACTION_ID_TASKBAR,
+ new Intent(ACTION_SHOW_TASKBAR).setPackage(mContext.getPackageName()),
+ PendingIntent.FLAG_UPDATE_CURRENT | PendingIntent.FLAG_IMMUTABLE);
+ mContext.registerReceiver(
+ mTaskbarBroadcastReceiver,
+ new IntentFilter(ACTION_SHOW_TASKBAR),
+ RECEIVER_NOT_EXPORTED);
+ });
recreateTaskbar();
}
@@ -214,6 +240,15 @@
}
/**
+ * Show Taskbar upon receiving broadcast
+ */
+ private void showTaskbarFromBroadcast(Intent intent) {
+ if (ACTION_SHOW_TASKBAR.equals(intent.getAction()) && mTaskbarActivityContext != null) {
+ mTaskbarActivityContext.showTaskbarFromBroadcast();
+ }
+ }
+
+ /**
* Displays a frame of the first Launcher reveal animation.
*
* This should be used to run a first Launcher reveal animation whose progress matches a swipe
@@ -405,6 +440,8 @@
* Called when the manager is no longer needed
*/
public void destroy() {
+ UI_HELPER_EXECUTOR.execute(
+ () -> mTaskbarBroadcastReceiver.unregisterReceiverSafely(mContext));
destroyExistingTaskbar();
mDisplayController.removeChangeListener(mDispInfoChangeListener);
SettingsCache.INSTANCE.get(mContext).unregister(USER_SETUP_COMPLETE_URI,
diff --git a/quickstep/src/com/android/launcher3/taskbar/TaskbarSharedState.java b/quickstep/src/com/android/launcher3/taskbar/TaskbarSharedState.java
index 6092998..66ca7d9 100644
--- a/quickstep/src/com/android/launcher3/taskbar/TaskbarSharedState.java
+++ b/quickstep/src/com/android/launcher3/taskbar/TaskbarSharedState.java
@@ -17,6 +17,8 @@
import static com.android.launcher3.taskbar.LauncherTaskbarUIController.DISPLAY_PROGRESS_COUNT;
+import android.app.PendingIntent;
+
/**
* State shared across different taskbar instance
*/
@@ -43,4 +45,7 @@
// LauncherTaskbarUIController#mTaskbarInAppDisplayProgressMultiProp
public float[] inAppDisplayProgressMultiPropValues = new float[DISPLAY_PROGRESS_COUNT];
+
+ // Taskbar System Action
+ public PendingIntent taskbarSystemActionPendingIntent;
}
diff --git a/quickstep/src/com/android/launcher3/taskbar/TaskbarStashController.java b/quickstep/src/com/android/launcher3/taskbar/TaskbarStashController.java
index b98f172..c43b621 100644
--- a/quickstep/src/com/android/launcher3/taskbar/TaskbarStashController.java
+++ b/quickstep/src/com/android/launcher3/taskbar/TaskbarStashController.java
@@ -28,6 +28,8 @@
import static com.android.launcher3.logging.StatsLogManager.LauncherEvent.LAUNCHER_TRANSIENT_TASKBAR_HIDE;
import static com.android.launcher3.logging.StatsLogManager.LauncherEvent.LAUNCHER_TRANSIENT_TASKBAR_SHOW;
import static com.android.launcher3.taskbar.TaskbarKeyguardController.MASK_ANY_SYSUI_LOCKED;
+import static com.android.launcher3.taskbar.TaskbarManager.SYSTEM_ACTION_ID_TASKBAR;
+import static com.android.launcher3.util.Executors.UI_HELPER_EXECUTOR;
import static com.android.launcher3.util.FlagDebugUtils.appendFlag;
import static com.android.launcher3.util.FlagDebugUtils.formatFlagChange;
import static com.android.systemui.shared.system.QuickStepContract.SYSUI_STATE_IME_SHOWING;
@@ -38,7 +40,9 @@
import android.animation.Animator;
import android.animation.AnimatorListenerAdapter;
import android.animation.AnimatorSet;
+import android.app.RemoteAction;
import android.content.SharedPreferences;
+import android.graphics.drawable.Icon;
import android.util.Log;
import android.view.InsetsController;
import android.view.View;
@@ -224,6 +228,9 @@
return (inApp && stashedInApp) || (!inApp && stashedLauncherState) || forceStashed;
});
+ private boolean mIsTaskbarSystemActionRegistered = false;
+ private TaskbarSharedState mTaskbarSharedState;
+
public TaskbarStashController(TaskbarActivityContext activity) {
mActivity = activity;
mPrefs = LauncherPrefs.getPrefs(mActivity);
@@ -234,8 +241,27 @@
mStashedHeight = mActivity.getDeviceProfile().stashedTaskbarHeight;
}
- public void init(TaskbarControllers controllers, boolean setupUIVisible) {
+ /**
+ * Show Taskbar upon receiving broadcast
+ */
+ public void showTaskbarFromBroadcast() {
+ // If user is in middle of taskbar education handle go to next step of education
+ if (mControllers.taskbarEduTooltipController.isBeforeTooltipFeaturesStep()) {
+ mControllers.taskbarEduTooltipController.hide();
+ mControllers.taskbarEduTooltipController.maybeShowFeaturesEdu();
+ }
+ updateAndAnimateTransientTaskbar(false);
+ }
+
+ /**
+ * Initializes the controller
+ */
+ public void init(
+ TaskbarControllers controllers,
+ boolean setupUIVisible,
+ TaskbarSharedState sharedState) {
mControllers = controllers;
+ mTaskbarSharedState = sharedState;
TaskbarDragLayerController dragLayerController = controllers.taskbarDragLayerController;
mTaskbarBackgroundOffset = dragLayerController.getTaskbarBackgroundOffset();
@@ -992,6 +1018,7 @@
private void notifyStashChange(boolean visible, boolean stashed) {
mSystemUiProxy.notifyTaskbarStatus(visible, stashed);
+ setUpTaskbarSystemAction(visible);
// If stashing taskbar is caused by IME visibility, we could just skip updating rounded
// corner insets since the rounded corners will be covered by IME during IME is showing and
// taskbar will be restored back to unstashed when IME is hidden.
@@ -1001,6 +1028,39 @@
}
/**
+ * Setup system action for showing Taskbar depending on its visibility.
+ */
+ public void setUpTaskbarSystemAction(boolean visible) {
+ UI_HELPER_EXECUTOR.execute(() -> {
+ if (!visible || !DisplayController.isTransientTaskbar(mActivity)) {
+ mAccessibilityManager.unregisterSystemAction(SYSTEM_ACTION_ID_TASKBAR);
+ mIsTaskbarSystemActionRegistered = false;
+ return;
+ }
+
+ if (!mIsTaskbarSystemActionRegistered) {
+ RemoteAction taskbarRemoteAction = new RemoteAction(
+ Icon.createWithResource(mActivity, R.drawable.ic_info_no_shadow),
+ mActivity.getString(R.string.taskbar_a11y_title),
+ mActivity.getString(R.string.taskbar_a11y_title),
+ mTaskbarSharedState.taskbarSystemActionPendingIntent);
+
+ mAccessibilityManager.registerSystemAction(taskbarRemoteAction,
+ SYSTEM_ACTION_ID_TASKBAR);
+ mIsTaskbarSystemActionRegistered = true;
+ }
+ });
+ }
+
+ /**
+ * Clean up on destroy from TaskbarControllers
+ */
+ public void onDestroy() {
+ UI_HELPER_EXECUTOR.execute(
+ () -> mAccessibilityManager.unregisterSystemAction(SYSTEM_ACTION_ID_TASKBAR));
+ }
+
+ /**
* Cancels a timeout if any exists.
*/
public void cancelTimeoutIfExists() {
diff --git a/quickstep/src/com/android/launcher3/taskbar/TaskbarView.java b/quickstep/src/com/android/launcher3/taskbar/TaskbarView.java
index b7d5993..87df5b0 100644
--- a/quickstep/src/com/android/launcher3/taskbar/TaskbarView.java
+++ b/quickstep/src/com/android/launcher3/taskbar/TaskbarView.java
@@ -92,7 +92,7 @@
private float mTransientTaskbarAllAppsButtonTranslationXOffset;
- private final boolean mStartAlignTaskbar;
+ private final boolean mShouldTryStartAlign;
public TaskbarView(@NonNull Context context) {
this(context, null);
@@ -121,7 +121,7 @@
resources.getDimension(isTransientTaskbar
? R.dimen.transient_taskbar_all_apps_button_translation_x_offset
: R.dimen.taskbar_all_apps_button_translation_x_offset);
- mStartAlignTaskbar = mActivityContext.isThreeButtonNav()
+ mShouldTryStartAlign = mActivityContext.isThreeButtonNav()
&& resources.getBoolean(R.bool.start_align_taskbar);
int actualMargin = resources.getDimensionPixelSize(R.dimen.taskbar_icon_spacing);
@@ -353,12 +353,10 @@
}
int navSpaceNeeded = deviceProfile.hotseatBarEndOffset;
boolean layoutRtl = isLayoutRtl();
- int iconEnd = right - (right - left - spaceNeeded) / 2;
- boolean needMoreSpaceForNav = layoutRtl ?
- navSpaceNeeded > (iconEnd - spaceNeeded) :
- iconEnd > (right - navSpaceNeeded);
+ int centerAlignIconEnd = right - (right - left - spaceNeeded) / 2;
+ int iconEnd;
- if (mStartAlignTaskbar) {
+ if (mShouldTryStartAlign) {
// Taskbar is aligned to the start
int startSpacingPx = deviceProfile.inlineNavButtonsEndSpacingPx;
@@ -367,13 +365,20 @@
} else {
iconEnd = startSpacingPx + spaceNeeded;
}
- } else if (needMoreSpaceForNav) {
+ } else {
+ iconEnd = centerAlignIconEnd;
+ }
+
+ boolean needMoreSpaceForNav = layoutRtl
+ ? navSpaceNeeded > (iconEnd - spaceNeeded)
+ : iconEnd > (right - navSpaceNeeded);
+ if (needMoreSpaceForNav) {
// Add offset to account for nav bar when taskbar is centered
int offset = layoutRtl
- ? navSpaceNeeded - (iconEnd - spaceNeeded)
- : (right - navSpaceNeeded) - iconEnd;
+ ? navSpaceNeeded - (centerAlignIconEnd - spaceNeeded)
+ : (right - navSpaceNeeded) - centerAlignIconEnd;
- iconEnd += offset;
+ iconEnd = centerAlignIconEnd + offset;
}
sTmpRect.set(mIconLayoutBounds);
diff --git a/quickstep/src/com/android/launcher3/taskbar/allapps/TaskbarAllAppsSlideInView.java b/quickstep/src/com/android/launcher3/taskbar/allapps/TaskbarAllAppsSlideInView.java
index 9db4ddd..d69769a 100644
--- a/quickstep/src/com/android/launcher3/taskbar/allapps/TaskbarAllAppsSlideInView.java
+++ b/quickstep/src/com/android/launcher3/taskbar/allapps/TaskbarAllAppsSlideInView.java
@@ -112,13 +112,24 @@
DeviceProfile dp = mActivityContext.getDeviceProfile();
setShiftRange(dp.allAppsShiftRange);
- mActivityContext.addOnDeviceProfileChangeListener(this);
setContentBackgroundWithParent(
getContext().getDrawable(R.drawable.bg_rounded_corner_bottom_sheet),
mAppsView.getBottomSheetBackground());
}
@Override
+ protected void onAttachedToWindow() {
+ super.onAttachedToWindow();
+ mActivityContext.addOnDeviceProfileChangeListener(this);
+ }
+
+ @Override
+ protected void onDetachedFromWindow() {
+ super.onDetachedFromWindow();
+ mActivityContext.removeOnDeviceProfileChangeListener(this);
+ }
+
+ @Override
protected void onScaleProgressChanged() {
super.onScaleProgressChanged();
mAppsView.setClipChildren(!mIsBackProgressing);
diff --git a/quickstep/src/com/android/quickstep/BaseActivityInterface.java b/quickstep/src/com/android/quickstep/BaseActivityInterface.java
index ce41c60..fd7aa58 100644
--- a/quickstep/src/com/android/quickstep/BaseActivityInterface.java
+++ b/quickstep/src/com/android/quickstep/BaseActivityInterface.java
@@ -16,6 +16,7 @@
package com.android.quickstep;
import static com.android.launcher3.LauncherAnimUtils.VIEW_BACKGROUND_COLOR;
+import static com.android.launcher3.MotionEventsUtils.isTrackpadMultiFingerSwipe;
import static com.android.launcher3.anim.Interpolators.ACCEL_2;
import static com.android.launcher3.anim.Interpolators.INSTANT;
import static com.android.launcher3.anim.Interpolators.LINEAR;
@@ -187,7 +188,8 @@
public abstract boolean allowMinimizeSplitScreen();
public boolean deferStartingActivity(RecentsAnimationDeviceState deviceState, MotionEvent ev) {
- return deviceState.isInDeferredGestureRegion(ev) || deviceState.isImeRenderingNavButtons();
+ return deviceState.isInDeferredGestureRegion(ev) || deviceState.isImeRenderingNavButtons()
+ || isTrackpadMultiFingerSwipe(ev);
}
/**
diff --git a/quickstep/src/com/android/quickstep/RecentsActivity.java b/quickstep/src/com/android/quickstep/RecentsActivity.java
index 3f8da56..253341d 100644
--- a/quickstep/src/com/android/quickstep/RecentsActivity.java
+++ b/quickstep/src/com/android/quickstep/RecentsActivity.java
@@ -267,7 +267,8 @@
new RemoteAnimationAdapter(wrapper, RECENTS_LAUNCH_DURATION,
RECENTS_LAUNCH_DURATION - STATUS_BAR_TRANSITION_DURATION
- STATUS_BAR_TRANSITION_PRE_DELAY),
- new RemoteTransition(wrapper.toRemoteTransition(), getIApplicationThread()));
+ new RemoteTransition(wrapper.toRemoteTransition(), getIApplicationThread(),
+ "LaunchFromRecents"));
final ActivityOptionsWrapper activityOptions = new ActivityOptionsWrapper(options,
onEndCallback);
activityOptions.options.setSplashScreenStyle(SplashScreen.SPLASH_SCREEN_STYLE_ICON);
@@ -405,7 +406,8 @@
getMainThreadHandler(), mAnimationToHomeFactory, true);
ActivityOptions options = ActivityOptions.makeRemoteAnimation(
new RemoteAnimationAdapter(runner, HOME_APPEAR_DURATION, 0),
- new RemoteTransition(runner.toRemoteTransition(), getIApplicationThread()));
+ new RemoteTransition(runner.toRemoteTransition(), getIApplicationThread(),
+ "StartHomeFromRecents"));
startHomeIntentSafely(this, options.toBundle());
}
diff --git a/quickstep/src/com/android/quickstep/SystemUiProxy.java b/quickstep/src/com/android/quickstep/SystemUiProxy.java
index 7cc3e11..1f99d6e 100644
--- a/quickstep/src/com/android/quickstep/SystemUiProxy.java
+++ b/quickstep/src/com/android/quickstep/SystemUiProxy.java
@@ -766,7 +766,7 @@
*/
@Nullable
public RemoteAnimationTarget[] onGoingToRecentsLegacy(RemoteAnimationTarget[] apps) {
- if (mSplitScreen != null) {
+ if (!TaskAnimationManager.ENABLE_SHELL_TRANSITIONS && mSplitScreen != null) {
try {
return mSplitScreen.onGoingToRecentsLegacy(apps);
} catch (RemoteException e) {
@@ -1117,6 +1117,9 @@
*/
public boolean startRecentsActivity(Intent intent, ActivityOptions options,
RecentsAnimationListener listener) {
+ if (mRecentTasks == null) {
+ return false;
+ }
final IRecentsAnimationRunner runner = new IRecentsAnimationRunner.Stub() {
@Override
public void onAnimationStart(IRecentsAnimationController controller,
diff --git a/quickstep/src/com/android/quickstep/TaskOverlayFactory.java b/quickstep/src/com/android/quickstep/TaskOverlayFactory.java
index 6e47ff4..2aa0be6 100644
--- a/quickstep/src/com/android/quickstep/TaskOverlayFactory.java
+++ b/quickstep/src/com/android/quickstep/TaskOverlayFactory.java
@@ -129,7 +129,8 @@
TaskShortcutFactory.PIN,
TaskShortcutFactory.INSTALL,
TaskShortcutFactory.FREE_FORM,
- TaskShortcutFactory.WELLBEING
+ TaskShortcutFactory.WELLBEING,
+ TaskShortcutFactory.SAVE_APP_PAIR
};
/**
diff --git a/quickstep/src/com/android/quickstep/TaskShortcutFactory.java b/quickstep/src/com/android/quickstep/TaskShortcutFactory.java
index 255b910..fd7b343 100644
--- a/quickstep/src/com/android/quickstep/TaskShortcutFactory.java
+++ b/quickstep/src/com/android/quickstep/TaskShortcutFactory.java
@@ -40,6 +40,7 @@
import com.android.launcher3.BaseDraggingActivity;
import com.android.launcher3.DeviceProfile;
import com.android.launcher3.R;
+import com.android.launcher3.config.FeatureFlags;
import com.android.launcher3.logging.StatsLogManager.LauncherEvent;
import com.android.launcher3.model.WellbeingModel;
import com.android.launcher3.popup.SystemShortcut;
@@ -63,7 +64,8 @@
import java.util.stream.Collectors;
/**
- * Represents a system shortcut that can be shown for a recent task.
+ * Represents a system shortcut that can be shown for a recent task. Appears as a single entry in
+ * the dropdown menu that shows up when you tap an app icon in Overview.
*/
public interface TaskShortcutFactory {
@Nullable
@@ -122,6 +124,26 @@
}
}
+ /**
+ * A menu item, "Save app pair", that allows the user to preserve the current app combination as
+ * a single persistent icon on the Home screen, allowing for quick split screen initialization.
+ */
+ class SaveAppPairSystemShortcut extends SystemShortcut {
+
+ private final TaskView mTaskView;
+
+ public SaveAppPairSystemShortcut(BaseDraggingActivity target, TaskView taskView) {
+ super(R.drawable.ic_save_app_pair, R.string.save_app_pair, target,
+ taskView.getItemInfo(), taskView);
+ mTaskView = taskView;
+ }
+
+ @Override
+ public void onClick(View view) {
+ // TODO (b/274189428): Call "saveAppPair" function in new AppPairController class
+ }
+ }
+
class FreeformSystemShortcut extends SystemShortcut<BaseDraggingActivity> {
private static final String TAG = "FreeformSystemShortcut";
@@ -257,9 +279,6 @@
final PagedOrientationHandler orientationHandler =
recentsView.getPagedOrientationHandler();
- int[] taskViewTaskIds = taskView.getTaskIds();
- boolean taskViewHasMultipleTasks = taskViewTaskIds[0] != -1 &&
- taskViewTaskIds[1] != -1;
boolean notEnoughTasksToSplit = recentsView.getTaskViewCount() < 2;
boolean isFocusedTask = deviceProfile.isTablet && taskView.isFocusedTask();
boolean isTaskInExpectedScrollPosition =
@@ -267,11 +286,11 @@
boolean isTaskSplitNotSupported = !task.isDockable;
boolean hideForExistingMultiWindow = activity.getDeviceProfile().isMultiWindowMode;
- if (taskViewHasMultipleTasks ||
- notEnoughTasksToSplit ||
- isTaskSplitNotSupported ||
- hideForExistingMultiWindow ||
- (isFocusedTask && isTaskInExpectedScrollPosition)) {
+ if (taskView.containsMultipleTasks()
+ || notEnoughTasksToSplit
+ || isTaskSplitNotSupported
+ || hideForExistingMultiWindow
+ || (isFocusedTask && isTaskInExpectedScrollPosition)) {
return null;
}
@@ -283,6 +302,26 @@
}
};
+ TaskShortcutFactory SAVE_APP_PAIR = new TaskShortcutFactory() {
+ @Nullable
+ @Override
+ public List<SystemShortcut> getShortcuts(BaseDraggingActivity activity,
+ TaskIdAttributeContainer taskContainer) {
+ final TaskView taskView = taskContainer.getTaskView();
+
+ if (!FeatureFlags.ENABLE_APP_PAIRS.get() || !taskView.containsMultipleTasks()) {
+ return null;
+ }
+
+ return Collections.singletonList(new SaveAppPairSystemShortcut(activity, taskView));
+ }
+
+ @Override
+ public boolean showForSplitscreen() {
+ return true;
+ }
+ };
+
TaskShortcutFactory FREE_FORM = new TaskShortcutFactory() {
@Override
public List<SystemShortcut> getShortcuts(BaseDraggingActivity activity,
diff --git a/quickstep/src/com/android/quickstep/util/SplitSelectStateController.java b/quickstep/src/com/android/quickstep/util/SplitSelectStateController.java
index 00d6571..c537ef8 100644
--- a/quickstep/src/com/android/quickstep/util/SplitSelectStateController.java
+++ b/quickstep/src/com/android/quickstep/util/SplitSelectStateController.java
@@ -284,7 +284,8 @@
final RemoteSplitLaunchTransitionRunner animationRunner =
new RemoteSplitLaunchTransitionRunner(taskId1, taskId2, callback);
final RemoteTransition remoteTransition = new RemoteTransition(animationRunner,
- ActivityThread.currentActivityThread().getApplicationThread());
+ ActivityThread.currentActivityThread().getApplicationThread(),
+ "LaunchSplitPair");
if (intent1 == null && intent2 == null) {
mSystemUiProxy.startTasks(taskId1, options1.toBundle(), taskId2,
null /* options2 */, stagePosition, splitRatio, remoteTransition,
diff --git a/quickstep/tests/src/com/android/quickstep/TaplTestsQuickstep.java b/quickstep/tests/src/com/android/quickstep/TaplTestsQuickstep.java
index 9a65b4f..c39d095 100644
--- a/quickstep/tests/src/com/android/quickstep/TaplTestsQuickstep.java
+++ b/quickstep/tests/src/com/android/quickstep/TaplTestsQuickstep.java
@@ -186,35 +186,6 @@
actionsView.clickAndDismissScreenshot();
}
- @Test
- @PortraitLandscape
- public void testSplitFromOverview() {
- assumeTrue(!mLauncher.isTablet());
-
- startTestActivity(2);
- startTestActivity(3);
-
- mLauncher.goHome().switchToOverview().getCurrentTask()
- .tapMenu()
- .tapSplitMenuItem()
- .getCurrentTask()
- .open();
- }
-
- @Test
- @PortraitLandscape
- public void testSplitFromOverviewForTablet() {
- assumeTrue(mLauncher.isTablet());
-
- startTestActivity(2);
- startTestActivity(3);
-
- mLauncher.goHome().switchToOverview().getOverviewActions()
- .clickSplit()
- .getTestActivityTask(2)
- .open();
- }
-
private int getCurrentOverviewPage(Launcher launcher) {
return launcher.<RecentsView>getOverviewPanel().getCurrentPage();
}
diff --git a/quickstep/tests/src/com/android/quickstep/TaplTestsSplitscreen.java b/quickstep/tests/src/com/android/quickstep/TaplTestsSplitscreen.java
index d3fbe93..2ae512a 100644
--- a/quickstep/tests/src/com/android/quickstep/TaplTestsSplitscreen.java
+++ b/quickstep/tests/src/com/android/quickstep/TaplTestsSplitscreen.java
@@ -15,14 +15,18 @@
*/
package com.android.quickstep;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertTrue;
+import static org.junit.Assume.assumeTrue;
+
import android.content.Intent;
+import com.android.launcher3.config.FeatureFlags;
import com.android.launcher3.ui.TaplTestsLauncher3;
import com.android.launcher3.util.rule.TestStabilityRule;
import com.android.quickstep.TaskbarModeSwitchRule.TaskbarModeSwitch;
import org.junit.After;
-import org.junit.Assume;
import org.junit.Before;
import org.junit.Test;
@@ -37,13 +41,6 @@
super.setUp();
TaplTestsLauncher3.initialize(this);
- mLauncher.getWorkspace()
- .deleteAppIcon(mLauncher.getWorkspace().getHotseatAppIcon(0))
- .switchToAllApps()
- .getAppIcon(CALCULATOR_APP_NAME)
- .dragToHotseat(0);
-
- startAppFast(CALCULATOR_APP_PACKAGE);
if (mLauncher.isTablet()) {
mLauncher.enableBlockTimeout(true);
mLauncher.showTaskbarIfHidden();
@@ -58,14 +55,29 @@
}
@Test
+ @PortraitLandscape
+ public void testSplitFromOverview() {
+ createAndLaunchASplitPair();
+ }
+
+ @Test
// TODO (b/270201357): When this test is proven stable, remove this TestStabilityRule and
- // introduce into presubmit as well.
+ // introduce into presubmit as well.
@TestStabilityRule.Stability(
flavors = TestStabilityRule.LOCAL | TestStabilityRule.PLATFORM_POSTSUBMIT)
@PortraitLandscape
@TaskbarModeSwitch
public void testSplitAppFromHomeWithItself() throws Exception {
- Assume.assumeTrue(mLauncher.isTablet());
+ // Currently only tablets have Taskbar in Overview, so test is only active on tablets
+ assumeTrue(mLauncher.isTablet());
+
+ mLauncher.getWorkspace()
+ .deleteAppIcon(mLauncher.getWorkspace().getHotseatAppIcon(0))
+ .switchToAllApps()
+ .getAppIcon(CALCULATOR_APP_NAME)
+ .dragToHotseat(0);
+
+ startAppFast(CALCULATOR_APP_PACKAGE);
mLauncher.goHome()
.switchToAllApps()
@@ -79,4 +91,50 @@
.getAppIcon(CALCULATOR_APP_NAME)
.launchIntoSplitScreen();
}
+
+ @Test
+ public void testSaveAppPairMenuItemExistsOnSplitPair() throws Exception {
+ assumeTrue(FeatureFlags.ENABLE_APP_PAIRS.get());
+
+ createAndLaunchASplitPair();
+
+ assertTrue("Save app pair menu item is missing",
+ mLauncher.goHome()
+ .switchToOverview()
+ .getCurrentTask()
+ .tapMenu()
+ .hasMenuItem("Save app pair"));
+ }
+
+ @Test
+ public void testSaveAppPairMenuItemDoesNotExistOnSingleTask() throws Exception {
+ assumeTrue(FeatureFlags.ENABLE_APP_PAIRS.get());
+
+ startAppFast(CALCULATOR_APP_PACKAGE);
+
+ assertFalse("Save app pair menu item is erroneously appearing on single task",
+ mLauncher.goHome()
+ .switchToOverview()
+ .getCurrentTask()
+ .tapMenu()
+ .hasMenuItem("Save app pair"));
+ }
+
+ private void createAndLaunchASplitPair() {
+ startTestActivity(2);
+ startTestActivity(3);
+
+ if (mLauncher.isTablet()) {
+ mLauncher.goHome().switchToOverview().getOverviewActions()
+ .clickSplit()
+ .getTestActivityTask(2)
+ .open();
+ } else {
+ mLauncher.goHome().switchToOverview().getCurrentTask()
+ .tapMenu()
+ .tapSplitMenuItem()
+ .getCurrentTask()
+ .open();
+ }
+ }
}
diff --git a/res/drawable/page_indicator.xml b/res/drawable/page_indicator.xml
deleted file mode 100644
index c0ccc49..0000000
--- a/res/drawable/page_indicator.xml
+++ /dev/null
@@ -1,6 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<shape xmlns:android="http://schemas.android.com/apk/res/android"
- android:shape="rectangle">
- <solid android:color="?attr/folderPaginationColor"/>
- <size android:width="@dimen/page_indicator_size" android:height="@dimen/page_indicator_size"/>
-</shape>
\ No newline at end of file
diff --git a/res/layout/page_indicator_dots.xml b/res/layout/page_indicator_dots.xml
index d5fe51e..41844b7 100644
--- a/res/layout/page_indicator_dots.xml
+++ b/res/layout/page_indicator_dots.xml
@@ -14,9 +14,13 @@
limitations under the License.
-->
-<com.android.launcher3.pageindicators.PageIndicatorDots xmlns:android="http://schemas.android.com/apk/res/android"
+<com.android.launcher3.pageindicators.LauncherDotsPageIndicator
+ xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:launcher="http://schemas.android.com/apk/res-auto"
android:id="@+id/page_indicator"
android:layout_width="match_parent"
android:layout_height="@dimen/workspace_page_indicator_height"
android:layout_gravity="bottom | center_horizontal"
- android:theme="@style/HomeScreenElementTheme" />
\ No newline at end of file
+ android:theme="@style/HomeScreenElementTheme"
+ launcher:indicatorDotColor="?attr/workspaceTextColor"
+ />
\ No newline at end of file
diff --git a/res/layout/popup_container_material_u.xml b/res/layout/popup_container_material_u.xml
index d3036b6..d34c500 100644
--- a/res/layout/popup_container_material_u.xml
+++ b/res/layout/popup_container_material_u.xml
@@ -18,4 +18,6 @@
android:id="@+id/popup_container"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
+ android:clipChildren="false"
+ android:clipToPadding="false"
android:orientation="vertical"/>
\ No newline at end of file
diff --git a/res/layout/user_folder_icon_normalized.xml b/res/layout/user_folder_icon_normalized.xml
index 5518dc8..35ccc5a 100644
--- a/res/layout/user_folder_icon_normalized.xml
+++ b/res/layout/user_folder_icon_normalized.xml
@@ -53,12 +53,13 @@
android:textColorHighlight="?android:attr/colorControlHighlight"
android:textColorHint="?attr/folderHintColor"/>
- <com.android.launcher3.pageindicators.PageIndicatorDots
+ <com.android.launcher3.pageindicators.LauncherDotsPageIndicator
android:id="@+id/folder_page_indicator"
android:layout_gravity="center_vertical"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:elevation="1dp"
+ launcher:indicatorDotColor="@color/folder_pagination_color"
/>
</LinearLayout>
diff --git a/res/values-night-v31/colors.xml b/res/values-night-v31/colors.xml
index f331361..3b85b88 100644
--- a/res/values-night-v31/colors.xml
+++ b/res/values-night-v31/colors.xml
@@ -26,4 +26,6 @@
<color name="home_settings_track_off_color">@android:color/system_neutral1_700</color>
<color name="all_apps_button_color">@android:color/system_neutral2_200</color>
+
+ <color name="folder_pagination_color">@android:color/system_accent2_100</color>
</resources>
\ No newline at end of file
diff --git a/res/values-night/colors.xml b/res/values-night/colors.xml
index 17fe419..ee27d99 100644
--- a/res/values-night/colors.xml
+++ b/res/values-night/colors.xml
@@ -18,4 +18,6 @@
<resources>
<color name="all_apps_button_color">#BFC8CC</color>
+ <color name="folder_pagination_color">#ffbfebe3</color>
+
</resources>
\ No newline at end of file
diff --git a/res/values-sw720dp/dimens.xml b/res/values-sw720dp/dimens.xml
index d7a5881..2b0382d 100644
--- a/res/values-sw720dp/dimens.xml
+++ b/res/values-sw720dp/dimens.xml
@@ -26,7 +26,7 @@
<dimen name="drop_target_text_size">20sp</dimen>
<dimen name="dynamic_grid_drop_target_size">72dp</dimen>
<dimen name="drop_target_button_drawable_horizontal_padding">24dp</dimen>
- <dimen name="drop_target_button_drawable_vertical_padding">20dp</dimen>
+ <dimen name="drop_target_button_drawable_vertical_padding">8dp</dimen>
<dimen name="drop_target_button_gap">32dp</dimen>
<dimen name="drop_target_button_workspace_edge_gap">32dp</dimen>
<dimen name="drop_target_top_margin">110dp</dimen>
diff --git a/res/values-v31/colors.xml b/res/values-v31/colors.xml
index 054fe47..0c87ff4 100644
--- a/res/values-v31/colors.xml
+++ b/res/values-v31/colors.xml
@@ -41,8 +41,7 @@
<color name="wallpaper_popup_scrim">@android:color/system_neutral1_900</color>
<color name="folder_dot_color">@android:color/system_accent3_100</color>
- <color name="folder_pagination_color_light">@android:color/system_accent1_600</color>
- <color name="folder_pagination_color_dark">@android:color/system_accent2_100</color>
+ <color name="folder_pagination_color">@android:color/system_accent1_600</color>
<color name="home_settings_header_accent">@android:color/system_accent1_600</color>
<color name="home_settings_header_collapsed">@android:color/system_neutral1_100</color>
diff --git a/res/values/attrs.xml b/res/values/attrs.xml
index 682153f..04eb0db 100644
--- a/res/values/attrs.xml
+++ b/res/values/attrs.xml
@@ -42,7 +42,6 @@
<attr name="popupNotificationDotColor" format="color" />
<attr name="folderDotColor" format="color" />
- <attr name="folderPaginationColor" format="color" />
<attr name="folderPreviewColor" format="color" />
<attr name="folderBackgroundColor" format="color" />
<attr name="folderIconRadius" format="float" />
@@ -491,4 +490,10 @@
<!-- The icon drawable of a widget category. -->
<attr name="sectionDrawable" format="reference" />
</declare-styleable>
+
+ <!-- Attributes for PagedIndicator -->
+ <declare-styleable name="PagedIndicator">
+ <!-- Color used to draw dots -->
+ <attr name="indicatorDotColor" format="color" />
+ </declare-styleable>
</resources>
diff --git a/res/values/colors.xml b/res/values/colors.xml
index 8788557..1ef918d 100644
--- a/res/values/colors.xml
+++ b/res/values/colors.xml
@@ -67,8 +67,7 @@
<color name="folder_preview_dark">#464746</color>
<color name="folder_dot_color">?attr/colorPrimary</color>
- <color name="folder_pagination_color_light">#ff006c5f</color>
- <color name="folder_pagination_color_dark">#ffbfebe3</color>
+ <color name="folder_pagination_color">#ff006c5f</color>
<color name="text_color_primary_dark">#FFFFFFFF</color>
<color name="text_color_secondary_dark">#FFFFFFFF</color>
diff --git a/res/values/strings.xml b/res/values/strings.xml
index f2fb8f5..7552b22 100644
--- a/res/values/strings.xml
+++ b/res/values/strings.xml
@@ -42,6 +42,9 @@
<string name="recent_task_option_split_screen">Split screen</string>
<string name="split_app_info_accessibility">App info for %1$s</string>
+ <!-- App pairs -->
+ <string name="save_app_pair">Save app pair</string>
+
<!-- Widgets -->
<!-- Message to tell the user to press and hold on a widget to add it [CHAR_LIMIT=50] -->
<string name="long_press_widget_to_add">Touch & hold to move a widget.</string>
diff --git a/res/values/styles.xml b/res/values/styles.xml
index 5dc4f0a..517bb87 100644
--- a/res/values/styles.xml
+++ b/res/values/styles.xml
@@ -50,7 +50,6 @@
<item name="workspaceStatusBarScrim">@drawable/workspace_bg</item>
<item name="widgetsTheme">@style/WidgetContainerTheme</item>
<item name="folderDotColor">@color/folder_dot_color</item>
- <item name="folderPaginationColor">@color/folder_pagination_color_light</item>
<item name="folderPreviewColor">@color/folder_preview_light</item>
<item name="folderBackgroundColor">@color/folder_background_light</item>
<item name="folderIconBorderColor">?android:attr/colorPrimary</item>
@@ -104,7 +103,6 @@
<item name="popupShadeThird">@color/popup_shade_third_dark</item>
<item name="widgetsTheme">@style/WidgetContainerTheme.Dark</item>
<item name="folderDotColor">@color/folder_dot_color</item>
- <item name="folderPaginationColor">@color/folder_pagination_color_dark</item>
<item name="folderPreviewColor">@color/folder_preview_dark</item>
<item name="folderBackgroundColor">@color/folder_background_dark</item>
<item name="folderIconBorderColor">?android:attr/colorPrimary</item>
diff --git a/src/com/android/launcher3/ButtonDropTarget.java b/src/com/android/launcher3/ButtonDropTarget.java
index 376cab7..e653283 100644
--- a/src/com/android/launcher3/ButtonDropTarget.java
+++ b/src/com/android/launcher3/ButtonDropTarget.java
@@ -416,7 +416,7 @@
getPaint().getTextBounds(mText.toString(), 0, mText.length(), mTempRect);
// Add bounds bottom to height, as text bounds height measures from the text baseline and
// above, which characters can descend below
- return mTempRect.bottom + mTempRect.height() <= availableHeight;
+ return mTempRect.bottom + mTempRect.height() >= availableHeight;
}
/**
diff --git a/src/com/android/launcher3/Launcher.java b/src/com/android/launcher3/Launcher.java
index 2e382c8..7beac0b 100644
--- a/src/com/android/launcher3/Launcher.java
+++ b/src/com/android/launcher3/Launcher.java
@@ -85,7 +85,6 @@
import android.content.pm.PackageManager;
import android.content.res.Configuration;
import android.database.sqlite.SQLiteDatabase;
-import android.graphics.Color;
import android.graphics.Rect;
import android.graphics.RectF;
import android.os.Build;
@@ -1353,10 +1352,6 @@
if (SHOW_DOT_PAGINATION.get()) {
mWorkspace.getPageIndicator().setShouldAutoHide(true);
- mWorkspace.getPageIndicator().setPaintColor(
- Themes.getAttrBoolean(this, R.attr.isWorkspaceDarkText)
- ? Color.BLACK
- : Color.WHITE);
}
}
diff --git a/src/com/android/launcher3/WorkspaceStateTransitionAnimation.java b/src/com/android/launcher3/WorkspaceStateTransitionAnimation.java
index 62e7ef3..26ad4b5 100644
--- a/src/com/android/launcher3/WorkspaceStateTransitionAnimation.java
+++ b/src/com/android/launcher3/WorkspaceStateTransitionAnimation.java
@@ -153,8 +153,8 @@
Interpolator workspaceFadeInterpolator = config.getInterpolator(ANIM_WORKSPACE_FADE,
pageAlphaProvider.interpolator);
float workspacePageIndicatorAlpha = (elements & WORKSPACE_PAGE_INDICATOR) != 0 ? 1 : 0;
- propertySetter.setViewAlpha(mLauncher.getWorkspace().getPageIndicator(),
- workspacePageIndicatorAlpha, workspaceFadeInterpolator);
+ mLauncher.getWorkspace().getPageIndicator().setAlpha(
+ propertySetter, workspacePageIndicatorAlpha, workspaceFadeInterpolator);
Interpolator hotseatFadeInterpolator = config.getInterpolator(ANIM_HOTSEAT_FADE,
workspaceFadeInterpolator);
float hotseatIconsAlpha = (elements & HOTSEAT_ICONS) != 0 ? 1 : 0;
diff --git a/src/com/android/launcher3/allapps/ActivityAllAppsContainerView.java b/src/com/android/launcher3/allapps/ActivityAllAppsContainerView.java
index c3ac53e..c5cf647 100644
--- a/src/com/android/launcher3/allapps/ActivityAllAppsContainerView.java
+++ b/src/com/android/launcher3/allapps/ActivityAllAppsContainerView.java
@@ -192,7 +192,6 @@
onAppsUpdated);
}
mAllAppsStore.addUpdateListener(onAppsUpdated);
- mActivityContext.addOnDeviceProfileChangeListener(this);
// This is a focus listener that proxies focus from a view into the list view. This is to
// work around the search box from getting first focus and showing the cursor.
@@ -263,6 +262,18 @@
mSearchUiManager.initializeSearch(this);
}
+ @Override
+ protected void onAttachedToWindow() {
+ super.onAttachedToWindow();
+ mActivityContext.addOnDeviceProfileChangeListener(this);
+ }
+
+ @Override
+ protected void onDetachedFromWindow() {
+ super.onDetachedFromWindow();
+ mActivityContext.removeOnDeviceProfileChangeListener(this);
+ }
+
public SearchUiManager getSearchUiManager() {
return mSearchUiManager;
}
@@ -347,16 +358,10 @@
public boolean shouldContainerScroll(MotionEvent ev) {
BaseDragLayer dragLayer = mActivityContext.getDragLayer();
- // IF the MotionEvent is inside the search box, and the container keeps on receiving
- // touch input, container should move down.
- if (dragLayer.isEventOverView(mSearchContainer, ev)) {
- return true;
- }
- // Scroll if not within the container view (e.g. over large-screen scrim).
- if (!dragLayer.isEventOverView(getVisibleContainerView(), ev)) {
- return true;
- }
- if (dragLayer.isEventOverView(mBottomSheetHandleArea, ev)) {
+ // IF the MotionEvent is inside the search box or handle area, and the container keeps on
+ // receiving touch input, container should move down.
+ if (dragLayer.isEventOverView(mSearchContainer, ev)
+ || dragLayer.isEventOverView(mBottomSheetHandleArea, ev)) {
return true;
}
AllAppsRecyclerView rv = getActiveRecyclerView();
@@ -368,6 +373,10 @@
&& dragLayer.isEventOverView(rv.getScrollbar(), ev)) {
return false;
}
+ // Scroll if not within the container view (e.g. over large-screen scrim).
+ if (!dragLayer.isEventOverView(getVisibleContainerView(), ev)) {
+ return true;
+ }
return rv.shouldContainerScroll(ev, dragLayer);
}
diff --git a/src/com/android/launcher3/allapps/SearchUiManager.java b/src/com/android/launcher3/allapps/SearchUiManager.java
index 228b02b..2174936 100644
--- a/src/com/android/launcher3/allapps/SearchUiManager.java
+++ b/src/com/android/launcher3/allapps/SearchUiManager.java
@@ -65,7 +65,7 @@
* sets highlight result's title
*/
default void setFocusedResultTitle(
- @Nullable CharSequence title, @Nullable CharSequence subtitle) {}
+ @Nullable CharSequence title, @Nullable CharSequence subtitle, boolean showArrow) {}
/** Refresh the currently displayed list of results. */
default void refreshResults() {}
diff --git a/src/com/android/launcher3/folder/FolderPagedView.java b/src/com/android/launcher3/folder/FolderPagedView.java
index d43731b..9fe3c0b 100644
--- a/src/com/android/launcher3/folder/FolderPagedView.java
+++ b/src/com/android/launcher3/folder/FolderPagedView.java
@@ -45,7 +45,7 @@
import com.android.launcher3.keyboard.ViewGroupFocusHelper;
import com.android.launcher3.model.data.ItemInfo;
import com.android.launcher3.model.data.WorkspaceItemInfo;
-import com.android.launcher3.pageindicators.PageIndicatorDots;
+import com.android.launcher3.pageindicators.LauncherDotsPageIndicator;
import com.android.launcher3.touch.ItemClickHandler;
import com.android.launcher3.util.LauncherBindableItemsContainer.ItemOperator;
import com.android.launcher3.util.Thunk;
@@ -60,7 +60,7 @@
import java.util.function.ToIntFunction;
import java.util.stream.Collectors;
-public class FolderPagedView extends PagedView<PageIndicatorDots> implements ClipPathView {
+public class FolderPagedView extends PagedView<LauncherDotsPageIndicator> implements ClipPathView {
private static final String TAG = "FolderPagedView";
diff --git a/src/com/android/launcher3/pageindicators/LauncherDotsPageIndicator.java b/src/com/android/launcher3/pageindicators/LauncherDotsPageIndicator.java
new file mode 100644
index 0000000..8a21d13
--- /dev/null
+++ b/src/com/android/launcher3/pageindicators/LauncherDotsPageIndicator.java
@@ -0,0 +1,197 @@
+/*
+ * 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.pageindicators;
+
+import static com.android.launcher3.anim.AnimatorListeners.forEndCallback;
+import static com.android.launcher3.util.MultiPropertyFactory.MULTI_PROPERTY_VALUE;
+
+import android.animation.ObjectAnimator;
+import android.animation.TimeInterpolator;
+import android.content.Context;
+import android.graphics.Canvas;
+import android.graphics.Rect;
+import android.util.AttributeSet;
+import android.view.ViewConfiguration;
+
+import androidx.annotation.Nullable;
+
+import com.android.launcher3.Alarm;
+import com.android.launcher3.Insettable;
+import com.android.launcher3.anim.PropertySetter;
+import com.android.launcher3.util.MultiValueAlpha;
+
+/**
+ * Extension of {@link PageIndicatorDots} with Launcher specific page-indicator functionality
+ */
+public class LauncherDotsPageIndicator extends PageIndicatorDots
+ implements Insettable, PageIndicator {
+
+ private static final int PAGINATION_FADE_DELAY = ViewConfiguration.getScrollDefaultDelay();
+ private static final int PAGINATION_FADE_IN_DURATION = 83;
+ private static final int PAGINATION_FADE_OUT_DURATION = 167;
+
+ private static final int INDEX_VIEW_ALPHA = 0;
+ private static final int INDEX_AUTO_HIDE = 1;
+ private static final int ALPHA_CHANNEL_COUNT = 2;
+
+ private final Alarm mAutoHideAlarm;
+ private final MultiValueAlpha mMultiValueAlpha;
+
+ private @Nullable ObjectAnimator mAlphaAnimator;
+ private boolean mShouldAutoHide;
+ private float mTargetAutoHideAlpha;
+
+ private boolean mIsSettled = true;
+ private int mTotalScroll;
+
+ public LauncherDotsPageIndicator(Context context) {
+ this(context, null);
+ }
+
+ public LauncherDotsPageIndicator(Context context, AttributeSet attrs) {
+ this(context, attrs, 0);
+ }
+
+ public LauncherDotsPageIndicator(Context context, AttributeSet attrs, int defStyleAttr) {
+ super(context, attrs, defStyleAttr);
+ mMultiValueAlpha = new MultiValueAlpha(this, ALPHA_CHANNEL_COUNT);
+ mMultiValueAlpha.setUpdateVisibility(true);
+
+ mTargetAutoHideAlpha = mMultiValueAlpha.get(INDEX_AUTO_HIDE).getValue();
+
+ mAutoHideAlarm = new Alarm();
+ mAutoHideAlarm.setOnAlarmListener(a -> animatePaginationToAlpha(0));
+ }
+
+ @Override
+ public void setScroll(int currentScroll, int totalScroll) {
+ mTotalScroll = totalScroll;
+ super.setScroll(currentScroll, totalScroll);
+ }
+
+ @Override
+ public void setShouldAutoHide(boolean shouldAutoHide) {
+ mShouldAutoHide = shouldAutoHide;
+ mAutoHideAlarm.cancelAlarm();
+ if (!mIsSettled || !mShouldAutoHide) {
+ animatePaginationToAlpha(1);
+ } else {
+ mAutoHideAlarm.setAlarm(PAGINATION_FADE_DELAY);
+ }
+ }
+
+ @Override
+ protected void onDraw(Canvas canvas) {
+ if (mShouldAutoHide && mTotalScroll == 0) {
+ return;
+ }
+ super.onDraw(canvas);
+ }
+
+ @Override
+ public void setActiveMarker(int activePage) {
+ super.setActiveMarker(activePage);
+ }
+
+ @Override
+ public void setMarkersCount(int numMarkers) {
+ super.setMarkersCount(numMarkers);
+ }
+
+ @Override
+ public void pauseAnimations() {
+ if (mAlphaAnimator != null) {
+ mAlphaAnimator.pause();
+ }
+ }
+
+ @Override
+ public void skipAnimationsToEnd() {
+ if (mAlphaAnimator != null) {
+ mAlphaAnimator.end();
+ }
+ }
+
+ @Override
+ protected void onAnimationStateChanged(boolean isSettled) {
+ mIsSettled = isSettled;
+ if (!mShouldAutoHide) {
+ return;
+ }
+ mAutoHideAlarm.cancelAlarm();
+ if (isSettled) {
+ mAutoHideAlarm.setAlarm(PAGINATION_FADE_DELAY);
+ } else {
+ animatePaginationToAlpha(1f);
+ }
+ }
+
+ private void animatePaginationToAlpha(float targetAlpha) {
+ if (mTargetAutoHideAlpha == targetAlpha) {
+ // Ignore the new animation if it is going to the same alpha as the current animation.
+ return;
+ }
+
+ if (mAlphaAnimator != null) {
+ mAlphaAnimator.cancel();
+ }
+ mAlphaAnimator = ObjectAnimator.ofFloat(mMultiValueAlpha.get(INDEX_AUTO_HIDE),
+ MULTI_PROPERTY_VALUE, targetAlpha);
+ // If we are animating to decrease the alpha, then it's a fade out animation
+ // whereas if we are animating to increase the alpha, it's a fade in animation.
+ mAlphaAnimator.setDuration(targetAlpha == 0
+ ? PAGINATION_FADE_OUT_DURATION
+ : PAGINATION_FADE_IN_DURATION);
+ mAlphaAnimator.addListener(forEndCallback(() -> mAlphaAnimator = null));
+ mAlphaAnimator.start();
+ mTargetAutoHideAlpha = targetAlpha;
+ }
+
+
+ @Override
+ public void stopAllAnimations() {
+ super.stopAllAnimations();
+ }
+
+ @Override
+ public void prepareEntryAnimation() {
+ super.prepareEntryAnimation();
+ }
+
+ @Override
+ public void playEntryAnimation() {
+ super.playEntryAnimation();
+ }
+
+ /**
+ * We need to override setInsets to prevent InsettableFrameLayout from applying different
+ * margins on the pagination.
+ */
+ @Override
+ public void setInsets(Rect insets) {
+ }
+
+ @Override
+ public boolean hasOverlappingRendering() {
+ return false;
+ }
+
+ @Override
+ public void setAlpha(PropertySetter setter, float alpha, TimeInterpolator interpolator) {
+ setter.setFloat(mMultiValueAlpha.get(INDEX_VIEW_ALPHA),
+ MULTI_PROPERTY_VALUE, alpha, interpolator);
+ }
+}
diff --git a/src/com/android/launcher3/pageindicators/PageIndicator.java b/src/com/android/launcher3/pageindicators/PageIndicator.java
index 570d6ff..193f50d 100644
--- a/src/com/android/launcher3/pageindicators/PageIndicator.java
+++ b/src/com/android/launcher3/pageindicators/PageIndicator.java
@@ -15,6 +15,11 @@
*/
package com.android.launcher3.pageindicators;
+import android.animation.TimeInterpolator;
+import android.view.View;
+
+import com.android.launcher3.anim.PropertySetter;
+
/**
* Base class for a page indicator.
*/
@@ -48,9 +53,9 @@
}
/**
- * Sets the paint color.
+ * Sets the provided alpha on the pageIndicator
*/
- default void setPaintColor(int color) {
- // No-op by default
+ default void setAlpha(PropertySetter setter, float alpha, TimeInterpolator interpolator) {
+ setter.setViewAlpha((View) this, alpha, interpolator);
}
}
diff --git a/src/com/android/launcher3/pageindicators/PageIndicatorDots.java b/src/com/android/launcher3/pageindicators/PageIndicatorDots.java
index b2c64b3..95452b9 100644
--- a/src/com/android/launcher3/pageindicators/PageIndicatorDots.java
+++ b/src/com/android/launcher3/pageindicators/PageIndicatorDots.java
@@ -25,42 +25,30 @@
import android.animation.ValueAnimator;
import android.animation.ValueAnimator.AnimatorUpdateListener;
import android.content.Context;
+import android.content.res.TypedArray;
import android.graphics.Canvas;
import android.graphics.Outline;
import android.graphics.Paint;
import android.graphics.Paint.Style;
-import android.graphics.Rect;
import android.graphics.RectF;
-import android.os.Handler;
-import android.os.Looper;
import android.util.AttributeSet;
import android.util.FloatProperty;
-import android.util.IntProperty;
import android.view.View;
-import android.view.ViewConfiguration;
import android.view.ViewOutlineProvider;
import android.view.animation.Interpolator;
import android.view.animation.OvershootInterpolator;
-import androidx.annotation.Nullable;
-
-import com.android.launcher3.Insettable;
import com.android.launcher3.R;
-import com.android.launcher3.Utilities;
-import com.android.launcher3.util.Themes;
/**
* {@link PageIndicator} which shows dots per page. The active page is shown with the current
* accent color.
*/
-public class PageIndicatorDots extends View implements Insettable, PageIndicator {
+public class PageIndicatorDots extends View {
private static final float SHIFT_PER_ANIMATION = 0.5f;
private static final float SHIFT_THRESHOLD = 0.1f;
private static final long ANIMATION_DURATION = 150;
- private static final int PAGINATION_FADE_DELAY = ViewConfiguration.getScrollDefaultDelay();
- private static final int PAGINATION_FADE_IN_DURATION = 83;
- private static final int PAGINATION_FADE_OUT_DURATION = 167;
private static final int ENTER_ANIMATION_START_DELAY = 300;
private static final int ENTER_ANIMATION_STAGGERED_DELAY = 150;
@@ -70,9 +58,6 @@
private static final int DOT_ALPHA = 128;
private static final float DOT_ALPHA_FRACTION = 0.5f;
private static final int DOT_GAP_FACTOR = SHOW_DOT_PAGINATION.get() ? 4 : 3;
- private static final int VISIBLE_ALPHA = 255;
- private static final int INVISIBLE_ALPHA = 0;
- private Paint mPaginationPaint;
// This value approximately overshoots to 1.5 times the original size.
private static final float ENTER_ANIMATION_OVERSHOOT_TENSION = 4.9f;
@@ -94,30 +79,14 @@
}
};
- private static final IntProperty<PageIndicatorDots> PAGINATION_ALPHA =
- new IntProperty<PageIndicatorDots>("pagination_alpha") {
- @Override
- public Integer get(PageIndicatorDots obj) {
- return obj.mPaginationPaint.getAlpha();
- }
-
- @Override
- public void setValue(PageIndicatorDots obj, int alpha) {
- obj.mPaginationPaint.setAlpha(alpha);
- obj.invalidate();
- }
- };
-
- private final Handler mDelayedPaginationFadeHandler = new Handler(Looper.getMainLooper());
private final float mDotRadius;
private final float mCircleGap;
private final boolean mIsRtl;
+ private final Paint mPaginationPaint;
+
private int mNumPages;
private int mActivePage;
- private int mTotalScroll;
- private boolean mShouldAutoHide;
- private int mToAlpha;
/**
* The current position of the active dot including the animation progress.
@@ -131,13 +100,9 @@
private float mCurrentPosition;
private float mFinalPosition;
private ObjectAnimator mAnimator;
- private @Nullable ObjectAnimator mAlphaAnimator;
private float[] mEntryAnimationRadiusFactors;
- private final Runnable mHidePaginationRunnable =
- () -> animatePaginationToAlpha(INVISIBLE_ALPHA);
-
public PageIndicatorDots(Context context) {
this(context, null);
}
@@ -151,37 +116,34 @@
mPaginationPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
mPaginationPaint.setStyle(Style.FILL);
- mPaginationPaint.setColor(Themes.getAttrColor(context, R.attr.folderPaginationColor));
+
+ TypedArray ta = context.obtainStyledAttributes(attrs, R.styleable.PagedIndicator);
+ mPaginationPaint.setColor(ta.getColor(R.styleable.PagedIndicator_indicatorDotColor, 0));
+ ta.recycle();
+
mDotRadius = (SHOW_DOT_PAGINATION.get()
? getResources().getDimension(R.dimen.page_indicator_dot_size_v2)
: getResources().getDimension(R.dimen.page_indicator_dot_size))
/ 2;
mCircleGap = DOT_GAP_FACTOR * mDotRadius;
setOutlineProvider(new MyOutlineProver());
- mIsRtl = Utilities.isRtl(getResources());
+ mIsRtl = getLayoutDirection() == View.LAYOUT_DIRECTION_RTL;
}
- @Override
public void setScroll(int currentScroll, int totalScroll) {
if (SHOW_DOT_PAGINATION.get() && mActivePage != 0 && currentScroll == 0) {
CURRENT_POSITION.set(this, (float) mActivePage);
return;
}
- if (mNumPages <= 1) {
+ if (mNumPages <= 1 || totalScroll == 0) {
return;
}
- if (mShouldAutoHide) {
- animatePaginationToAlpha(VISIBLE_ALPHA);
- }
-
if (mIsRtl) {
currentScroll = totalScroll - currentScroll;
}
- mTotalScroll = totalScroll;
-
int scrollPerPage = totalScroll / (mNumPages - 1);
int pageToLeft = scrollPerPage == 0 ? 0 : currentScroll / scrollPerPage;
int pageToLeftScroll = pageToLeft * scrollPerPage;
@@ -191,87 +153,12 @@
if (currentScroll < pageToLeftScroll + scrollThreshold) {
// scroll is within the left page's threshold
animateToPosition(pageToLeft);
- if (mShouldAutoHide) {
- hideAfterDelay();
- }
} else if (currentScroll > pageToRightScroll - scrollThreshold) {
// scroll is far enough from left page to go to the right page
animateToPosition(pageToLeft + 1);
- if (mShouldAutoHide) {
- hideAfterDelay();
- }
} else {
// scroll is between left and right page
animateToPosition(pageToLeft + SHIFT_PER_ANIMATION);
- if (mShouldAutoHide) {
- mDelayedPaginationFadeHandler.removeCallbacksAndMessages(null);
- }
- }
- }
-
- @Override
- public void setShouldAutoHide(boolean shouldAutoHide) {
- mShouldAutoHide = shouldAutoHide && SHOW_DOT_PAGINATION.get();
- if (shouldAutoHide && mPaginationPaint.getAlpha() > INVISIBLE_ALPHA) {
- hideAfterDelay();
- } else if (!shouldAutoHide) {
- mDelayedPaginationFadeHandler.removeCallbacksAndMessages(null);
- }
- }
-
- @Override
- public void setPaintColor(int color) {
- mPaginationPaint.setColor(color);
- }
-
- private void hideAfterDelay() {
- mDelayedPaginationFadeHandler.removeCallbacksAndMessages(null);
- mDelayedPaginationFadeHandler.postDelayed(mHidePaginationRunnable, PAGINATION_FADE_DELAY);
- }
-
- private void animatePaginationToAlpha(int alpha) {
- if (alpha == mToAlpha) {
- // Ignore the new animation if it is going to the same alpha as the current animation.
- return;
- }
-
- if (mAlphaAnimator != null) {
- mAlphaAnimator.cancel();
- }
- mAlphaAnimator = ObjectAnimator.ofInt(this, PAGINATION_ALPHA,
- alpha);
- // If we are animating to decrease the alpha, then it's a fade out animation
- // whereas if we are animating to increase the alpha, it's a fade in animation.
- mAlphaAnimator.setDuration(alpha < mToAlpha
- ? PAGINATION_FADE_OUT_DURATION
- : PAGINATION_FADE_IN_DURATION);
- mAlphaAnimator.addListener(new AnimatorListenerAdapter() {
- @Override
- public void onAnimationEnd(Animator animation) {
- mAlphaAnimator = null;
- }
- });
- mAlphaAnimator.start();
- mToAlpha = alpha;
- }
-
- /**
- * Pauses all currently running animations.
- */
- @Override
- public void pauseAnimations() {
- if (mAlphaAnimator != null) {
- mAlphaAnimator.pause();
- }
- }
-
- /**
- * Force-ends all currently running or paused animations.
- */
- @Override
- public void skipAnimationsToEnd() {
- if (mAlphaAnimator != null) {
- mAlphaAnimator.end();
}
}
@@ -281,15 +168,25 @@
mCurrentPosition = mFinalPosition;
}
if (mAnimator == null && Float.compare(mCurrentPosition, mFinalPosition) != 0) {
+ onAnimationStateChanged(false);
float positionForThisAnim = mCurrentPosition > mFinalPosition ?
mCurrentPosition - SHIFT_PER_ANIMATION : mCurrentPosition + SHIFT_PER_ANIMATION;
mAnimator = ObjectAnimator.ofFloat(this, CURRENT_POSITION, positionForThisAnim);
mAnimator.addListener(new AnimationCycleListener());
mAnimator.setDuration(ANIMATION_DURATION);
mAnimator.start();
+ } else if (mAnimator == null) {
+ // The state is only settled if the indicator lands on a int value
+ onAnimationStateChanged(Float.compare(Math.round(mFinalPosition), mFinalPosition) == 0);
}
}
+ /**
+ * Called when the animation state of the page indicator changes.
+ * @param isSettled true if the page indicator has settled at its final position
+ */
+ protected void onAnimationStateChanged(boolean isSettled) { }
+
public void stopAllAnimations() {
if (mAnimator != null) {
mAnimator.cancel();
@@ -345,14 +242,10 @@
animSet.start();
}
- @Override
public void setActiveMarker(int activePage) {
- if (mActivePage != activePage) {
- mActivePage = activePage;
- }
+ mActivePage = activePage;
}
- @Override
public void setMarkersCount(int numMarkers) {
mNumPages = numMarkers;
requestLayout();
@@ -374,11 +267,6 @@
return;
}
- if (mShouldAutoHide && mTotalScroll == 0) {
- mPaginationPaint.setAlpha(INVISIBLE_ALPHA);
- return;
- }
-
// Draw all page indicators;
float circleGap = mCircleGap;
float startX = (getWidth() - (mNumPages * circleGap) + mDotRadius) / 2;
@@ -480,20 +368,9 @@
@Override
public void onAnimationEnd(Animator animation) {
if (!mCancelled) {
- if (mShouldAutoHide && SHOW_DOT_PAGINATION.get()) {
- hideAfterDelay();
- }
mAnimator = null;
animateToPosition(mFinalPosition);
}
}
}
-
- /**
- * We need to override setInsets to prevent InsettableFrameLayout from applying different
- * margins on the pagination.
- */
- @Override
- public void setInsets(Rect insets) {
- }
}
diff --git a/src/com/android/launcher3/views/RecyclerViewFastScroller.java b/src/com/android/launcher3/views/RecyclerViewFastScroller.java
index a941833..c0b24fa 100644
--- a/src/com/android/launcher3/views/RecyclerViewFastScroller.java
+++ b/src/com/android/launcher3/views/RecyclerViewFastScroller.java
@@ -333,6 +333,7 @@
}
}
+ @Override
public void onDraw(Canvas canvas) {
if (mThumbOffsetY < 0 || mRv == null) {
return;
diff --git a/tests/src/com/android/launcher3/ui/WorkProfileTest.java b/tests/src/com/android/launcher3/ui/WorkProfileTest.java
index 199653b..54da7de 100644
--- a/tests/src/com/android/launcher3/ui/WorkProfileTest.java
+++ b/tests/src/com/android/launcher3/ui/WorkProfileTest.java
@@ -19,6 +19,8 @@
import static com.android.launcher3.LauncherState.NORMAL;
import static com.android.launcher3.allapps.AllAppsStore.DEFER_UPDATES_TEST;
import static com.android.launcher3.testing.shared.TestProtocol.WORK_TAB_MISSING;
+import static com.android.launcher3.util.rule.TestStabilityRule.LOCAL;
+import static com.android.launcher3.util.rule.TestStabilityRule.PLATFORM_POSTSUBMIT;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertTrue;
@@ -37,6 +39,7 @@
import com.android.launcher3.allapps.WorkProfileManager;
import com.android.launcher3.tapl.LauncherInstrumentation;
import com.android.launcher3.testing.shared.TestProtocol;
+import com.android.launcher3.util.rule.TestStabilityRule.Stability;
import org.junit.After;
import org.junit.Before;
@@ -113,6 +116,7 @@
}
@Test
+ @Stability(flavors = LOCAL | PLATFORM_POSTSUBMIT) // b/243688989
public void workTabExists() {
assumeTrue(mWorkProfileSetupSuccessful);
waitForWorkTabSetup();
@@ -173,6 +177,7 @@
}
@Test
+ @Stability(flavors = LOCAL | PLATFORM_POSTSUBMIT) // b/243688989
public void testEdu() {
assumeTrue(mWorkProfileSetupSuccessful);
waitForWorkTabSetup();
diff --git a/tests/tapl/com/android/launcher3/tapl/AllApps.java b/tests/tapl/com/android/launcher3/tapl/AllApps.java
index 2d4d2cd..885707c 100644
--- a/tests/tapl/com/android/launcher3/tapl/AllApps.java
+++ b/tests/tapl/com/android/launcher3/tapl/AllApps.java
@@ -147,6 +147,9 @@
getAppsListRecyclerBottomPadding());
verifyActiveContainer();
final int newScroll = getAllAppsScroll();
+ LauncherInstrumentation.log(
+ String.format("tryGetAppIcon: scrolled from %d to %d", scroll,
+ newScroll));
mLauncher.assertTrue(
"Scrolled in a wrong direction in AllApps: from " + scroll + " to "
+ newScroll, newScroll >= scroll);
@@ -259,8 +262,7 @@
}
private int getAllAppsScroll() {
- return mLauncher.getTestInfo(
- TestProtocol.REQUEST_APPS_LIST_SCROLL_Y)
+ return mLauncher.getTestInfo(TestProtocol.REQUEST_APPS_LIST_SCROLL_Y)
.getInt(TestProtocol.TEST_INFO_RESPONSE_FIELD);
}
diff --git a/tests/tapl/com/android/launcher3/tapl/OverviewTaskMenu.java b/tests/tapl/com/android/launcher3/tapl/OverviewTaskMenu.java
index 8cdc8a0..54be3c3 100644
--- a/tests/tapl/com/android/launcher3/tapl/OverviewTaskMenu.java
+++ b/tests/tapl/com/android/launcher3/tapl/OverviewTaskMenu.java
@@ -49,4 +49,10 @@
}
}
}
+
+ /** Returns true if an item matching the given string is present in the menu. */
+ public boolean hasMenuItem(String expectedMenuItemText) {
+ UiObject2 menuItem = mLauncher.findObjectInContainer(mMenu, By.text(expectedMenuItemText));
+ return menuItem != null;
+ }
}