Merge "Desktop Mode Taskbar Recreate Animation" into main
diff --git a/AndroidManifest-common.xml b/AndroidManifest-common.xml
index fe57da1..46f0e41 100644
--- a/AndroidManifest-common.xml
+++ b/AndroidManifest-common.xml
@@ -131,13 +131,11 @@
android:writePermission="${applicationId}.permission.WRITE_SETTINGS"
android:readPermission="${applicationId}.permission.READ_SETTINGS" />
- <!--
- The content provider for exposing various launcher grid options.
- TODO: Add proper permissions
- -->
+ <!-- The content provider for exposing various launcher grid options. -->
<provider
android:name="com.android.launcher3.graphics.LauncherCustomizationProvider"
android:authorities="${applicationId}.grid_control"
+ android:permission="android.permission.BIND_WALLPAPER"
android:exported="true" />
<!--
diff --git a/quickstep/res/layout/activity_allset.xml b/quickstep/res/layout/activity_allset.xml
index 625d9b3..3d68dfb 100644
--- a/quickstep/res/layout/activity_allset.xml
+++ b/quickstep/res/layout/activity_allset.xml
@@ -29,7 +29,6 @@
android:layout_height="match_parent"
android:gravity="center"
android:scaleType="centerCrop"
- app:lottie_autoPlay="true"
app:lottie_loop="true"
app:layout_constraintTop_toTopOf="parent"
@@ -49,11 +48,10 @@
app:layout_constraintEnd_toEndOf="parent">
<androidx.constraintlayout.widget.ConstraintLayout
- android:id="@+id/text_content_view"
+ android:id="@+id/content"
android:layout_width="match_parent"
android:layout_height="wrap_content"
- android:layout_marginStart="@dimen/allset_page_margin_horizontal"
- android:layout_marginEnd="@dimen/allset_page_margin_horizontal"
+ android:paddingHorizontal="@dimen/allset_page_padding_horizontal"
android:layoutDirection="locale"
android:textDirection="locale"
android:forceHasOverlappingRendering="false"
diff --git a/quickstep/res/values-sw600dp-land/dimens.xml b/quickstep/res/values-sw600dp-land/dimens.xml
index 0052a73..cf7ba00 100644
--- a/quickstep/res/values-sw600dp-land/dimens.xml
+++ b/quickstep/res/values-sw600dp-land/dimens.xml
@@ -16,7 +16,7 @@
-->
<resources>
<!-- All Set page -->
- <dimen name="allset_page_margin_horizontal">48dp</dimen>
+ <dimen name="allset_page_padding_horizontal">48dp</dimen>
<!-- Gesture Tutorial menu page -->
<dimen name="gesture_tutorial_menu_padding_horizontal">48dp</dimen>
diff --git a/quickstep/res/values-sw600dp/dimens.xml b/quickstep/res/values-sw600dp/dimens.xml
index 4996582..3e72651 100644
--- a/quickstep/res/values-sw600dp/dimens.xml
+++ b/quickstep/res/values-sw600dp/dimens.xml
@@ -37,7 +37,7 @@
<dimen name="overview_actions_top_margin">24dp</dimen>
<!-- All Set page -->
- <dimen name="allset_page_margin_horizontal">120dp</dimen>
+ <dimen name="allset_page_padding_horizontal">120dp</dimen>
<dimen name="allset_page_allset_text_size">38sp</dimen>
<dimen name="allset_page_swipe_up_text_size">15sp</dimen>
</resources>
diff --git a/quickstep/res/values/dimens.xml b/quickstep/res/values/dimens.xml
index 05f0695..52ebdae 100644
--- a/quickstep/res/values/dimens.xml
+++ b/quickstep/res/values/dimens.xml
@@ -277,7 +277,7 @@
<dimen name="gesture_tutorial_taskbar_margin_bottom">24dp</dimen>
<!-- All Set page -->
- <dimen name="allset_page_margin_horizontal">40dp</dimen>
+ <dimen name="allset_page_padding_horizontal">40dp</dimen>
<dimen name="allset_page_allset_text_size">36sp</dimen>
<dimen name="allset_page_swipe_up_text_size">14sp</dimen>
diff --git a/quickstep/src/com/android/launcher3/QuickstepTransitionManager.java b/quickstep/src/com/android/launcher3/QuickstepTransitionManager.java
index 5b78c0a..84ae0fe 100644
--- a/quickstep/src/com/android/launcher3/QuickstepTransitionManager.java
+++ b/quickstep/src/com/android/launcher3/QuickstepTransitionManager.java
@@ -56,7 +56,6 @@
import static com.android.launcher3.testing.shared.TestProtocol.WALLPAPER_OPEN_ANIMATION_FINISHED_MESSAGE;
import static com.android.launcher3.util.DisplayController.isTransientTaskbar;
import static com.android.launcher3.util.Executors.MAIN_EXECUTOR;
-import static com.android.launcher3.util.Executors.ORDERED_BG_EXECUTOR;
import static com.android.launcher3.util.MultiPropertyFactory.MULTI_PROPERTY_VALUE;
import static com.android.launcher3.util.window.RefreshRateTracker.getSingleFrameMs;
import static com.android.launcher3.views.FloatingIconView.SHAPE_PROGRESS_DURATION;
@@ -79,7 +78,6 @@
import android.content.ComponentName;
import android.content.Context;
import android.content.res.Resources;
-import android.database.ContentObserver;
import android.graphics.Color;
import android.graphics.Matrix;
import android.graphics.Point;
@@ -94,7 +92,6 @@
import android.os.SystemProperties;
import android.os.UserHandle;
import android.provider.Settings;
-import android.provider.Settings.Global;
import android.util.Pair;
import android.util.Size;
import android.view.CrossWindowBlurListeners;
@@ -249,16 +246,6 @@
// {@link TaskRestartedDuringLaunchListener}, and remove them on activity destroy.
private final List<TaskRestartedDuringLaunchListener> mRegisteredTaskStackChangeListener =
new ArrayList<>();
- private final ContentObserver mAnimationRemovalObserver = new ContentObserver(
- ORDERED_BG_EXECUTOR.getHandler()) {
- @Override
- public void onChange(boolean selfChange) {
- mAreAnimationsEnabled = Global.getFloat(mLauncher.getContentResolver(),
- Global.ANIMATOR_DURATION_SCALE, 1f) > 0
- || Global.getFloat(mLauncher.getContentResolver(),
- Global.TRANSITION_ANIMATION_SCALE, 1f) > 0;
- }
- };
private DeviceProfile mDeviceProfile;
@@ -287,7 +274,6 @@
// Pairs of window starting type and starting window background color for starting tasks
// Will never be larger than MAX_NUM_TASKS
private LinkedHashMap<Integer, Pair<Integer, Integer>> mTaskStartParams;
- private boolean mAreAnimationsEnabled = true;
private final Interpolator mOpeningXInterpolator;
private final Interpolator mOpeningInterpolator;
@@ -298,7 +284,6 @@
mHandler = new Handler(Looper.getMainLooper());
mDeviceProfile = mLauncher.getDeviceProfile();
mBackAnimationController = new LauncherBackAnimationController(mLauncher, this);
- checkAndMonitorIfAnimationsAreEnabled();
Resources res = mLauncher.getResources();
mClosingWindowTransY = res.getDimensionPixelSize(R.dimen.closing_window_trans_y);
@@ -1220,8 +1205,6 @@
unregisterRemoteTransitions();
mLauncher.removeOnDeviceProfileChangeListener(this);
SystemUiProxy.INSTANCE.get(mLauncher).setStartingWindowListener(null);
- ORDERED_BG_EXECUTOR.execute(() -> mLauncher.getContentResolver()
- .unregisterContentObserver(mAnimationRemovalObserver));
if (BuildConfig.IS_STUDIO_BUILD && !mRegisteredTaskStackChangeListener.isEmpty()) {
throw new IllegalStateException("Failed to run onEndCallback created from"
+ " getActivityLaunchOptions()");
@@ -1275,17 +1258,6 @@
}
}
- private void checkAndMonitorIfAnimationsAreEnabled() {
- ORDERED_BG_EXECUTOR.execute(() -> {
- mAnimationRemovalObserver.onChange(true);
- mLauncher.getContentResolver().registerContentObserver(Global.getUriFor(
- Global.ANIMATOR_DURATION_SCALE), false, mAnimationRemovalObserver);
- mLauncher.getContentResolver().registerContentObserver(Global.getUriFor(
- Global.TRANSITION_ANIMATION_SCALE), false, mAnimationRemovalObserver);
-
- });
- }
-
private boolean launcherIsATargetWithMode(RemoteAnimationTarget[] targets, int mode) {
for (RemoteAnimationTarget target : targets) {
if (target.mode == mode && target.taskInfo != null
@@ -1421,7 +1393,8 @@
(LauncherAppWidgetHostView) launcherView, targetRect, windowSize,
mDeviceProfile.isMultiWindowMode ? 0 : getWindowCornerRadius(mLauncher),
isTransluscent, fallbackBackgroundColor);
- } else if (launcherView != null && mAreAnimationsEnabled) {
+ } else if (launcherView != null && !RemoveAnimationSettingsTracker.INSTANCE.get(
+ mLauncher).isRemoveAnimationEnabled()) {
floatingIconView = getFloatingIconView(mLauncher, launcherView, null,
mLauncher.getTaskbarUIController() == null
? null
diff --git a/quickstep/src/com/android/launcher3/taskbar/TaskbarEduTooltipController.kt b/quickstep/src/com/android/launcher3/taskbar/TaskbarEduTooltipController.kt
index b4ffb74..5d1288c 100644
--- a/quickstep/src/com/android/launcher3/taskbar/TaskbarEduTooltipController.kt
+++ b/quickstep/src/com/android/launcher3/taskbar/TaskbarEduTooltipController.kt
@@ -39,6 +39,7 @@
import com.airbnb.lottie.LottieAnimationView
import com.android.launcher3.LauncherPrefs
import com.android.launcher3.R
+import com.android.launcher3.RemoveAnimationSettingsTracker
import com.android.launcher3.Utilities
import com.android.launcher3.config.FeatureFlags.enableTaskbarPinning
import com.android.launcher3.taskbar.TaskbarAutohideSuspendController.FLAG_AUTOHIDE_SUSPEND_EDU_OPEN
@@ -128,6 +129,26 @@
activityContext.dragLayer.post { maybeShowSearchEdu() }
}
+ /**
+ * Turns off auto play of lottie animations if user has opted to remove animation else attaches
+ * click listener to allow user to play or pause animations.
+ */
+ fun handleEduAnimations(animationViews: List<LottieAnimationView>) {
+ for (animationView in animationViews) {
+ if (
+ RemoveAnimationSettingsTracker.INSTANCE.get(animationView.context)
+ .isRemoveAnimationEnabled()
+ ) {
+ animationView.pauseAnimation()
+ } else {
+ animationView.setOnClickListener {
+ if (animationView.isAnimating) animationView.pauseAnimation()
+ else animationView.playAnimation()
+ }
+ }
+ }
+ }
+
/** Shows swipe EDU tooltip if it is the current [tooltipStep]. */
fun maybeShowSwipeEdu() {
if (
@@ -145,7 +166,9 @@
requireViewById(R.id.taskbar_edu_title),
TypefaceUtils.FONT_FAMILY_HEADLINE_SMALL_EMPHASIZED,
)
- requireViewById<LottieAnimationView>(R.id.swipe_animation).supportLightTheme()
+ val swipeAnimation = requireViewById<LottieAnimationView>(R.id.swipe_animation)
+ swipeAnimation.supportLightTheme()
+ handleEduAnimations(listOf(swipeAnimation))
show()
}
}
@@ -174,6 +197,7 @@
splitscreenAnim.supportLightTheme()
suggestionsAnim.supportLightTheme()
pinningAnim.supportLightTheme()
+ handleEduAnimations(listOf(splitscreenAnim, suggestionsAnim, pinningAnim))
if (DisplayController.isTransientTaskbar(activityContext)) {
splitscreenAnim.setAnimation(R.raw.taskbar_edu_splitscreen_transient)
suggestionsAnim.setAnimation(R.raw.taskbar_edu_suggestions_transient)
@@ -249,9 +273,6 @@
tooltip?.run {
allowTouchDismissal = true
- requireViewById<LottieAnimationView>(R.id.standalone_pinning_animation)
- .supportLightTheme()
-
TypefaceUtils.setTypeface(
requireViewById(R.id.taskbar_edu_title),
TypefaceUtils.FONT_FAMILY_HEADLINE_SMALL_EMPHASIZED,
@@ -261,6 +282,10 @@
TypefaceUtils.FONT_FAMILY_BODY_MEDIUM_BASELINE,
)
+ val pinningAnim =
+ requireViewById<LottieAnimationView>(R.id.standalone_pinning_animation)
+ pinningAnim.supportLightTheme()
+ handleEduAnimations(listOf(pinningAnim))
updateLayoutParams<BaseDragLayer.LayoutParams> {
if (DisplayController.isTransientTaskbar(activityContext)) {
bottomMargin += activityContext.deviceProfile.taskbarHeight
@@ -304,7 +329,9 @@
inflateTooltip(R.layout.taskbar_edu_search)
tooltip?.run {
allowTouchDismissal = true
- requireViewById<LottieAnimationView>(R.id.search_edu_animation).supportLightTheme()
+ val searchEdu = requireViewById<LottieAnimationView>(R.id.search_edu_animation)
+ searchEdu.supportLightTheme()
+ handleEduAnimations(listOf(searchEdu))
val eduSubtitle: TextView = requireViewById(R.id.search_edu_text)
TypefaceUtils.setTypeface(
diff --git a/quickstep/src/com/android/launcher3/taskbar/TypefaceUtils.kt b/quickstep/src/com/android/launcher3/taskbar/TypefaceUtils.kt
index 8b53ff1..e9c62d1 100644
--- a/quickstep/src/com/android/launcher3/taskbar/TypefaceUtils.kt
+++ b/quickstep/src/com/android/launcher3/taskbar/TypefaceUtils.kt
@@ -30,11 +30,14 @@
class TypefaceUtils {
companion object {
- const val FONT_FAMILY_HEADLINE_SMALL_EMPHASIZED = "variable-headline-small-emphasized"
- const val FONT_FAMILY_HEADLINE_LARGE_EMPHASIZED = "variable-headline-large-emphasized"
const val FONT_FAMILY_BODY_SMALL_BASELINE = "variable-body-small"
const val FONT_FAMILY_BODY_MEDIUM_BASELINE = "variable-body-medium"
+ const val FONT_FAMILY_BODY_LARGE_BASELINE = "variable-body-large"
const val FONT_FAMILY_LABEL_LARGE_BASELINE = "variable-label-large"
+ const val FONT_FAMILY_DISPLAY_SMALL_EMPHASIZED = "variable-display-small-emphasized"
+ const val FONT_FAMILY_DISPLAY_MEDIUM_EMPHASIZED = "variable-display-medium-emphasized"
+ const val FONT_FAMILY_HEADLINE_SMALL_EMPHASIZED = "variable-headline-small-emphasized"
+ const val FONT_FAMILY_HEADLINE_LARGE_EMPHASIZED = "variable-headline-large-emphasized"
@JvmStatic
@JvmOverloads
diff --git a/quickstep/src/com/android/quickstep/interaction/AllSetActivity.java b/quickstep/src/com/android/quickstep/interaction/AllSetActivity.java
index 953b0c5..c1bb250 100644
--- a/quickstep/src/com/android/quickstep/interaction/AllSetActivity.java
+++ b/quickstep/src/com/android/quickstep/interaction/AllSetActivity.java
@@ -64,6 +64,7 @@
import com.android.launcher3.InvariantDeviceProfile;
import com.android.launcher3.LauncherAppState;
import com.android.launcher3.R;
+import com.android.launcher3.RemoveAnimationSettingsTracker;
import com.android.launcher3.Utilities;
import com.android.launcher3.anim.AnimatedFloat;
import com.android.launcher3.anim.AnimatorPlaybackController;
@@ -107,6 +108,9 @@
private static final float ANIMATION_PAUSE_ALPHA_THRESHOLD = 0.1f;
+ private static final String KEY_BACKGROUND_ANIMATION_TOGGLED_ON =
+ "background_animation_toggled_on";
+
private final AnimatedFloat mSwipeProgress = new AnimatedFloat(this::onSwipeProgressUpdate);
private final InvariantDeviceProfile.OnIDPChangeListener mOnIDPChangeListener =
@@ -124,6 +128,9 @@
private AnimatorPlaybackController mLauncherStartAnim = null;
+ // Auto play background animation by default
+ private boolean mBackgroundAnimationToggledOn = true;
+
private TextView mHintView;
private final OverviewChangeListener mOverviewChangeListener = this::onOverviewTargetChange;
@@ -200,6 +207,15 @@
LOTTIE_TERTIARY_COLOR_TOKEN, R.color.all_set_bg_tertiary),
getTheme());
+ mBackgroundAnimationToggledOn = savedInstanceState == null
+ || savedInstanceState.getBoolean(KEY_BACKGROUND_ANIMATION_TOGGLED_ON, true);
+ // The animated background is behind a scroll view, which intercepts all input.
+ // However, the content view also covers the full screen
+ requireViewById(R.id.content).setOnClickListener(v -> {
+ mBackgroundAnimationToggledOn = !mBackgroundAnimationToggledOn;
+ maybeResumeOrPauseBackgroundAnimation();
+ });
+
setUpBackgroundAnimation(getDP().isTablet);
getIDP().addOnChangeListener(mOnIDPChangeListener);
@@ -208,6 +224,12 @@
ActivityPreloadUtil.preloadOverviewForSUWAllSet(this);
}
+ @Override
+ protected void onSaveInstanceState(Bundle outState) {
+ super.onSaveInstanceState(outState);
+ outState.putBoolean(KEY_BACKGROUND_ANIMATION_TOGGLED_ON, mBackgroundAnimationToggledOn);
+ }
+
private InvariantDeviceProfile getIDP() {
return LauncherAppState.getInstance(this).getInvariantDeviceProfile();
}
@@ -368,8 +390,10 @@
private void maybeResumeOrPauseBackgroundAnimation() {
boolean shouldPlayAnimation =
- getContentViewAlphaForSwipeProgress() > ANIMATION_PAUSE_ALPHA_THRESHOLD
- && isResumed();
+ !RemoveAnimationSettingsTracker.INSTANCE.get(this).isRemoveAnimationEnabled()
+ && getContentViewAlphaForSwipeProgress() > ANIMATION_PAUSE_ALPHA_THRESHOLD
+ && isResumed()
+ && mBackgroundAnimationToggledOn;
if (mAnimatedBackground.isAnimating() && !shouldPlayAnimation) {
mAnimatedBackground.pauseAnimation();
} else if (!mAnimatedBackground.isAnimating() && shouldPlayAnimation) {
diff --git a/quickstep/src/com/android/quickstep/interaction/TutorialController.java b/quickstep/src/com/android/quickstep/interaction/TutorialController.java
index 0fc95e2..e73fb3b 100644
--- a/quickstep/src/com/android/quickstep/interaction/TutorialController.java
+++ b/quickstep/src/com/android/quickstep/interaction/TutorialController.java
@@ -57,6 +57,7 @@
import com.android.launcher3.R;
import com.android.launcher3.Utilities;
import com.android.launcher3.anim.AnimatorListeners;
+import com.android.launcher3.taskbar.TypefaceUtils;
import com.android.launcher3.views.ClipIconView;
import com.android.quickstep.interaction.EdgeBackGestureHandler.BackGestureAttemptCallback;
import com.android.quickstep.interaction.NavBarGestureHandler.NavBarGestureAttemptCallback;
@@ -177,6 +178,7 @@
mFeedbackTitleView.setText(getIntroductionTitle());
mFeedbackSubtitleView.setText(getIntroductionSubtitle());
+ setTitleTypefaces();
mExitingAppView.setClipToOutline(true);
mExitingAppView.setOutlineProvider(new ViewOutlineProvider() {
@@ -434,6 +436,10 @@
if (isGestureSuccessful) {
if (mTutorialFragment.isAtFinalStep()) {
+ TypefaceUtils.setTypeface(
+ mDoneButton,
+ TypefaceUtils.FONT_FAMILY_LABEL_LARGE_BASELINE
+ );
showActionButton();
}
@@ -458,7 +464,8 @@
pauseAndHideLottieAnimation();
mCheckmarkAnimation.setVisibility(View.VISIBLE);
mCheckmarkAnimation.playAnimation();
- mFeedbackTitleView.setTextAppearance(mContext, getSuccessTitleTextAppearance());
+ mFeedbackTitleView.setTextAppearance(getSuccessTitleTextAppearance());
+ setTitleTypefaces();
}
public boolean isGestureCompleted() {
@@ -513,8 +520,10 @@
updateDrawables();
updateLayout();
- mFeedbackTitleView.setTextAppearance(mContext, getTitleTextAppearance());
- mDoneButton.setTextAppearance(mContext, getDoneButtonTextAppearance());
+ mFeedbackTitleView.setTextAppearance(getTitleTextAppearance());
+ mDoneButton.setTextAppearance(getDoneButtonTextAppearance());
+
+ setTitleTypefaces();
mDoneButton.getBackground().setTint(getDoneButtonColor());
mCheckmarkAnimation.setAnimation(mTutorialFragment.isAtFinalStep()
? R.raw.checkmark_animation_end
@@ -533,6 +542,21 @@
}
}
+ /**
+ * Apply expressive typefaces to the feedback title and subtitle views.
+ */
+ private void setTitleTypefaces() {
+ TypefaceUtils.setTypeface(
+ mFeedbackTitleView,
+ mTutorialFragment.isLargeScreen()
+ ? TypefaceUtils.FONT_FAMILY_DISPLAY_MEDIUM_EMPHASIZED
+ : TypefaceUtils.FONT_FAMILY_DISPLAY_SMALL_EMPHASIZED);
+ TypefaceUtils.setTypeface(
+ mFeedbackSubtitleView,
+ TypefaceUtils.FONT_FAMILY_BODY_LARGE_BASELINE
+ );
+ }
+
protected void resetViewsForBackGesture() {
mFakeTaskView.setVisibility(View.VISIBLE);
mFakeTaskView.setBackgroundColor(getFakeTaskViewColor());
diff --git a/quickstep/src/com/android/quickstep/views/RecentsView.java b/quickstep/src/com/android/quickstep/views/RecentsView.java
index f53583a..d17dfb8 100644
--- a/quickstep/src/com/android/quickstep/views/RecentsView.java
+++ b/quickstep/src/com/android/quickstep/views/RecentsView.java
@@ -3908,6 +3908,22 @@
// the only invariant point in landscape split screen.
snapToLastTask = true;
}
+ if (mUtils.getGridTaskCount() == 1 && dismissedTaskView.isGridTask()) {
+ TaskView lastLargeTile = mUtils.getLastLargeTaskView();
+ if (lastLargeTile != null) {
+ // Calculate the distance to put last large tile back to middle of the screen.
+ int primaryScroll = getPagedOrientationHandler().getPrimaryScroll(this);
+ int lastLargeTileScroll = getScrollForPage(indexOfChild(lastLargeTile));
+ longGridRowWidthDiff = primaryScroll - lastLargeTileScroll;
+
+ if (!isClearAllHidden) {
+ // If ClearAllButton is visible, reduce the distance by scroll difference
+ // between ClearAllButton and the last task.
+ longGridRowWidthDiff += getLastTaskScroll(/*clearAllScroll=*/0,
+ getPagedOrientationHandler().getPrimarySize(mClearAllButton));
+ }
+ }
+ }
// If we need to animate the grid to compensate the clear all gap, we split the second
// half of the dismiss pending animation (in which the non-dismissed tasks slide into
diff --git a/quickstep/src/com/android/quickstep/views/RecentsViewUtils.kt b/quickstep/src/com/android/quickstep/views/RecentsViewUtils.kt
index 8df7430..67318ac 100644
--- a/quickstep/src/com/android/quickstep/views/RecentsViewUtils.kt
+++ b/quickstep/src/com/android/quickstep/views/RecentsViewUtils.kt
@@ -85,6 +85,9 @@
/** Counts [TaskView]s that are large tiles. */
fun getLargeTileCount(): Int = taskViews.count { it.isLargeTile }
+ /** Counts [TaskView]s that are grid tasks. */
+ fun getGridTaskCount(): Int = taskViews.count { it.isGridTask }
+
/** Returns the first TaskView that should be displayed as a large tile. */
fun getFirstLargeTaskView(): TaskView? =
taskViews.firstOrNull {
diff --git a/src/com/android/launcher3/PagedView.java b/src/com/android/launcher3/PagedView.java
index 5072e37..d6abb56 100644
--- a/src/com/android/launcher3/PagedView.java
+++ b/src/com/android/launcher3/PagedView.java
@@ -17,6 +17,7 @@
package com.android.launcher3;
import static com.android.app.animation.Interpolators.SCROLL;
+import static com.android.launcher3.RemoveAnimationSettingsTracker.WINDOW_ANIMATION_SCALE_URI;
import static com.android.launcher3.compat.AccessibilityManagerCompat.isAccessibilityEnabled;
import static com.android.launcher3.compat.AccessibilityManagerCompat.isObservedEventType;
import static com.android.launcher3.testing.shared.TestProtocol.SCROLL_FINISHED_MESSAGE;
@@ -33,7 +34,6 @@
import android.graphics.Canvas;
import android.graphics.Rect;
import android.os.Bundle;
-import android.provider.Settings;
import android.util.AttributeSet;
import android.util.Log;
import android.view.InputDevice;
@@ -1756,8 +1756,8 @@
}
if (FeatureFlags.IS_STUDIO_BUILD && !Utilities.isRunningInTestHarness()) {
- duration *= Settings.Global.getFloat(getContext().getContentResolver(),
- Settings.Global.WINDOW_ANIMATION_SCALE, 1);
+ duration *= RemoveAnimationSettingsTracker.INSTANCE.get(getContext()).getValue(
+ WINDOW_ANIMATION_SCALE_URI);
}
whichPage = validateNewPage(whichPage);
diff --git a/src/com/android/launcher3/RemoveAnimationSettingsTracker.kt b/src/com/android/launcher3/RemoveAnimationSettingsTracker.kt
new file mode 100644
index 0000000..dbc04f1
--- /dev/null
+++ b/src/com/android/launcher3/RemoveAnimationSettingsTracker.kt
@@ -0,0 +1,110 @@
+/*
+ * Copyright (C) 2025 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.launcher3
+
+import android.content.Context
+import android.database.ContentObserver
+import android.net.Uri
+import android.os.Handler
+import android.os.Looper
+import android.provider.Settings
+import android.provider.Settings.Global.ANIMATOR_DURATION_SCALE
+import android.provider.Settings.Global.TRANSITION_ANIMATION_SCALE
+import android.provider.Settings.Global.WINDOW_ANIMATION_SCALE
+import com.android.launcher3.dagger.ApplicationContext
+import com.android.launcher3.dagger.LauncherAppComponent
+import com.android.launcher3.dagger.LauncherAppSingleton
+import com.android.launcher3.util.DaggerSingletonObject
+import com.android.launcher3.util.DaggerSingletonTracker
+import com.android.launcher3.util.Executors.UI_HELPER_EXECUTOR
+import java.util.concurrent.ConcurrentHashMap
+import javax.inject.Inject
+
+/** Tracker Class for when user turns on/off remove animation setting. */
+@LauncherAppSingleton
+class RemoveAnimationSettingsTracker
+@Inject
+constructor(@ApplicationContext val context: Context, tracker: DaggerSingletonTracker) :
+ ContentObserver(Handler(Looper.getMainLooper())) {
+
+ private val contentResolver = context.contentResolver
+
+ /** Caches the last seen value for registered keys. */
+ private val cache: MutableMap<Uri, Float> = ConcurrentHashMap()
+
+ init {
+ UI_HELPER_EXECUTOR.execute {
+ contentResolver.registerContentObserver(WINDOW_ANIMATION_SCALE_URI, false, this)
+ contentResolver.registerContentObserver(TRANSITION_ANIMATION_SCALE_URI, false, this)
+ contentResolver.registerContentObserver(ANIMATOR_DURATION_SCALE_URI, false, this)
+ }
+
+ tracker.addCloseable {
+ UI_HELPER_EXECUTOR.execute { contentResolver.unregisterContentObserver(this) }
+ }
+ }
+
+ /**
+ * Returns the value for this classes key from the cache. If not in cache, will call
+ * [updateValue] to fetch.
+ */
+ fun getValue(uri: Uri): Float {
+ return getValue(uri, 1f)
+ }
+
+ /**
+ * Returns the value for this classes key from the cache. If not in cache, will call
+ * [getValueFromSettingsGlobal] to fetch.
+ */
+ private fun getValue(uri: Uri, defaultValue: Float): Float {
+ return cache.computeIfAbsent(uri) { getValueFromSettingsGlobal(uri, defaultValue) }
+ }
+
+ /** Returns if user has opted into having no animation on their device. */
+ fun isRemoveAnimationEnabled(): Boolean {
+ return getValue(WINDOW_ANIMATION_SCALE_URI) == 0f &&
+ getValue(TRANSITION_ANIMATION_SCALE_URI) == 0f &&
+ getValue(ANIMATOR_DURATION_SCALE_URI) == 0f
+ }
+
+ override fun onChange(selfChange: Boolean, uri: Uri?) {
+ if (uri == null) return
+ updateValue(uri)
+ }
+
+ private fun getValueFromSettingsGlobal(uri: Uri, defaultValue: Float = 1f): Float {
+ return Settings.Global.getFloat(contentResolver, uri.lastPathSegment, defaultValue)
+ }
+
+ private fun updateValue(uri: Uri, defaultValue: Float = 1f) {
+ val newValue = getValueFromSettingsGlobal(uri, defaultValue)
+ cache[uri] = newValue
+ }
+
+ companion object {
+ @JvmField
+ val INSTANCE =
+ DaggerSingletonObject(LauncherAppComponent::getRemoveAnimationSettingsTracker)
+ @JvmField
+ val WINDOW_ANIMATION_SCALE_URI: Uri = Settings.Global.getUriFor(WINDOW_ANIMATION_SCALE)
+ @JvmField
+ val TRANSITION_ANIMATION_SCALE_URI: Uri =
+ Settings.Global.getUriFor(TRANSITION_ANIMATION_SCALE)
+ @JvmField
+ val ANIMATOR_DURATION_SCALE_URI: Uri = Settings.Global.getUriFor(ANIMATOR_DURATION_SCALE)
+ }
+}
diff --git a/src/com/android/launcher3/dagger/LauncherBaseAppComponent.java b/src/com/android/launcher3/dagger/LauncherBaseAppComponent.java
index 249a214..150761f 100644
--- a/src/com/android/launcher3/dagger/LauncherBaseAppComponent.java
+++ b/src/com/android/launcher3/dagger/LauncherBaseAppComponent.java
@@ -23,6 +23,7 @@
import com.android.launcher3.InvariantDeviceProfile;
import com.android.launcher3.LauncherAppState;
import com.android.launcher3.LauncherPrefs;
+import com.android.launcher3.RemoveAnimationSettingsTracker;
import com.android.launcher3.graphics.GridCustomizationsProxy;
import com.android.launcher3.graphics.ThemeManager;
import com.android.launcher3.icons.LauncherIcons.IconPool;
@@ -80,8 +81,8 @@
LockedUserState getLockedUserState();
InvariantDeviceProfile getIDP();
IconPool getIconPool();
+ RemoveAnimationSettingsTracker getRemoveAnimationSettingsTracker();
LauncherAppState getLauncherAppState();
-
GridCustomizationsProxy getGridCustomizationsProxy();
/** Builder for LauncherBaseAppComponent. */
diff --git a/src/com/android/launcher3/graphics/GridCustomizationsProxy.java b/src/com/android/launcher3/graphics/GridCustomizationsProxy.java
index 062c753..40863c4 100644
--- a/src/com/android/launcher3/graphics/GridCustomizationsProxy.java
+++ b/src/com/android/launcher3/graphics/GridCustomizationsProxy.java
@@ -24,11 +24,9 @@
import android.content.ContentValues;
import android.content.Context;
-import android.content.pm.PackageManager;
import android.database.Cursor;
import android.database.MatrixCursor;
import android.net.Uri;
-import android.os.Binder;
import android.os.Bundle;
import android.os.Handler;
import android.os.IBinder.DeathRecipient;
@@ -297,12 +295,6 @@
@Override
public Bundle call(@NonNull String method, String arg, Bundle extras) {
- if (mContext.checkPermission("android.permission.BIND_WALLPAPER",
- Binder.getCallingPid(), Binder.getCallingUid())
- != PackageManager.PERMISSION_GRANTED) {
- return null;
- }
-
if (METHOD_GET_PREVIEW.equals(method)) {
return getPreview(extras);
} else {
diff --git a/src/com/android/launcher3/model/PackageUpdatedTask.java b/src/com/android/launcher3/model/PackageUpdatedTask.java
index d1eceb9..3cdb250 100644
--- a/src/com/android/launcher3/model/PackageUpdatedTask.java
+++ b/src/com/android/launcher3/model/PackageUpdatedTask.java
@@ -239,25 +239,31 @@
boolean isTargetValid = !cn.getClassName().equals(
IconCache.EMPTY_CLASS_NAME);
if (itemInfo.itemType == ITEM_TYPE_DEEP_SHORTCUT) {
+ int requestQuery = ShortcutRequest.PINNED;
+ if (Flags.restoreArchivedShortcuts()) {
+ // Avoid race condition where shortcut service has no record of
+ // unarchived shortcut being pinned after restore.
+ // Launcher should be source-of-truth for if shortcut is pinned.
+ requestQuery = ShortcutRequest.ALL;
+ }
List<ShortcutInfo> shortcut =
new ShortcutRequest(context, mUser)
.forPackage(cn.getPackageName(),
itemInfo.getDeepShortcutId())
- .query(ShortcutRequest.PINNED);
- if (shortcut.isEmpty()
- && !(Flags.restoreArchivedShortcuts()
- && !itemInfo.isArchived())
- ) {
+ .query(requestQuery);
+ if (shortcut.isEmpty()) {
isTargetValid = false;
if (DEBUG) {
- Log.d(TAG, "Pinned Shortcut not found for updated"
- + " package=" + itemInfo.getTargetPackage());
- }
- } else if (!shortcut.isEmpty()) {
- if (DEBUG) {
- Log.d(TAG, "Found pinned shortcut for updated"
+ Log.d(TAG, "Shortcut not found for updated"
+ " package=" + itemInfo.getTargetPackage()
- + ", isTargetValid=" + isTargetValid);
+ + ", isArchived=" + itemInfo.isArchived());
+ }
+ } else {
+ if (DEBUG) {
+ Log.d(TAG, "Found shortcut for updated"
+ + " package=" + itemInfo.getTargetPackage()
+ + ", isTargetValid=" + isTargetValid
+ + ", isArchived=" + itemInfo.isArchived());
}
itemInfo.updateFromDeepShortcutInfo(shortcut.get(0), context);
infoUpdated = true;
diff --git a/tests/src/com/android/launcher3/allapps/KeyboardFocusTest.java b/tests/src/com/android/launcher3/allapps/KeyboardFocusTest.java
index 1e21ee5..44df5b8 100644
--- a/tests/src/com/android/launcher3/allapps/KeyboardFocusTest.java
+++ b/tests/src/com/android/launcher3/allapps/KeyboardFocusTest.java
@@ -23,7 +23,6 @@
import com.android.launcher3.Launcher;
import com.android.launcher3.LauncherState;
import com.android.launcher3.util.BaseLauncherActivityTest;
-import com.android.launcher3.util.rule.ScreenRecordRule.ScreenRecord;
import com.android.launcher3.views.ActivityContext;
import org.junit.Test;
@@ -64,7 +63,6 @@
}
@Test
- @ScreenRecord //b/378167329
public void testAllAppsExitSearchAndFocusSearchResults() {
loadLauncherSync();
goToState(LauncherState.ALL_APPS);
diff --git a/tests/src/com/android/launcher3/ui/workspace/ThemeIconsTest.java b/tests/src/com/android/launcher3/ui/workspace/ThemeIconsTest.java
index a123170..38970fe 100644
--- a/tests/src/com/android/launcher3/ui/workspace/ThemeIconsTest.java
+++ b/tests/src/com/android/launcher3/ui/workspace/ThemeIconsTest.java
@@ -23,7 +23,6 @@
import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertTrue;
-import android.content.ContentProviderClient;
import android.content.ContentResolver;
import android.content.ContentValues;
import android.net.Uri;
@@ -37,6 +36,7 @@
import com.android.launcher3.LauncherState;
import com.android.launcher3.allapps.AllAppsRecyclerView;
import com.android.launcher3.celllayout.FavoriteItemsTransaction;
+import com.android.launcher3.dagger.LauncherComponentProvider;
import com.android.launcher3.icons.mono.ThemedIconDrawable;
import com.android.launcher3.popup.ArrowPopup;
import com.android.launcher3.util.BaseLauncherActivityTest;
@@ -139,7 +139,7 @@
return icon;
}
- private void setThemeEnabled(boolean isEnabled) throws Exception {
+ private void setThemeEnabled(boolean isEnabled) {
Uri uri = new Uri.Builder()
.scheme(ContentResolver.SCHEME_CONTENT)
.authority(targetContext().getPackageName() + ".grid_control")
@@ -147,11 +147,10 @@
.build();
ContentValues values = new ContentValues();
values.put("boolean_value", isEnabled);
- try (ContentProviderClient client = targetContext().getContentResolver()
- .acquireContentProviderClient(uri)) {
- int result = client.update(uri, values, null);
- assertTrue(result > 0);
- }
+
+ int result = LauncherComponentProvider.get(targetContext()).getGridCustomizationsProxy()
+ .update(uri, values, null, null);
+ assertTrue(result > 0);
}
private void switchToAllApps() {