Merge "Removing diagnostics" into ub-launcher3-master
diff --git a/AndroidManifest.xml b/AndroidManifest.xml
index 9500a2f..70f0c01 100644
--- a/AndroidManifest.xml
+++ b/AndroidManifest.xml
@@ -20,7 +20,7 @@
<manifest
xmlns:android="http://schemas.android.com/apk/res/android"
package="com.android.launcher3">
- <uses-sdk android:targetSdkVersion="28" android:minSdkVersion="21"/>
+ <uses-sdk android:targetSdkVersion="28" android:minSdkVersion="25"/>
<!--
Manifest entries specific to Launcher3. This is merged with AndroidManifest-common.xml.
Refer comments around specific entries on how to extend individual components.
@@ -63,7 +63,7 @@
</intent-filter>
<meta-data
android:name="com.android.launcher3.grid.control"
- android:value="${packageName}.grid.control" />
+ android:value="${packageName}.grid_control" />
</activity>
</application>
diff --git a/build.gradle b/build.gradle
index 4191d47..ab97687 100644
--- a/build.gradle
+++ b/build.gradle
@@ -17,7 +17,7 @@
buildToolsVersion BUILD_TOOLS_VERSION
defaultConfig {
- minSdkVersion 21
+ minSdkVersion 25
targetSdkVersion 28
versionCode 1
versionName "1.0"
diff --git a/go/AndroidManifest.xml b/go/AndroidManifest.xml
index 25518af..fae1eff 100644
--- a/go/AndroidManifest.xml
+++ b/go/AndroidManifest.xml
@@ -22,7 +22,7 @@
xmlns:tools="http://schemas.android.com/tools"
package="com.android.launcher3" >
- <uses-sdk android:targetSdkVersion="28" android:minSdkVersion="21"/>
+ <uses-sdk android:targetSdkVersion="28" android:minSdkVersion="25"/>
<application
android:backupAgent="com.android.launcher3.LauncherBackupAgent"
diff --git a/go/quickstep/src/com/android/launcher3/uioverrides/RecentsUiFactory.java b/go/quickstep/src/com/android/launcher3/uioverrides/RecentsUiFactory.java
index d0c255c..7381574 100644
--- a/go/quickstep/src/com/android/launcher3/uioverrides/RecentsUiFactory.java
+++ b/go/quickstep/src/com/android/launcher3/uioverrides/RecentsUiFactory.java
@@ -24,27 +24,39 @@
import com.android.launcher3.Launcher;
import com.android.launcher3.LauncherStateManager.StateHandler;
+import com.android.launcher3.Utilities;
+import com.android.launcher3.config.FeatureFlags;
import com.android.launcher3.util.TouchController;
+import com.android.quickstep.OverviewInteractionState;
+
+import java.util.ArrayList;
/**
* Provides recents-related {@link UiFactory} logic and classes.
*/
-public final class RecentsUiFactory {
+public abstract class RecentsUiFactory {
// Scale recents takes before animating in
private static final float RECENTS_PREPARE_SCALE = 1.33f;
- private RecentsUiFactory() {}
+ public static TouchController[] createTouchControllers(Launcher launcher) {
+ ArrayList<TouchController> list = new ArrayList<>();
+ list.add(launcher.getDragController());
- /**
- * Creates and returns a touch controller for swiping recents tasks.
- *
- * @param launcher the launcher activity
- * @return the touch controller for recents tasks
- */
- public static TouchController createTaskSwipeController(Launcher launcher) {
- // We leave all input handling to the view itself.
- return null;
+ if (launcher.getDeviceProfile().isVerticalBarLayout()) {
+ list.add(new OverviewToAllAppsTouchController(launcher));
+ list.add(new LandscapeEdgeSwipeController(launcher));
+ } else {
+ boolean allowDragToOverview = OverviewInteractionState.INSTANCE.get(launcher)
+ .isSwipeUpGestureEnabled();
+ list.add(new PortraitStatesTouchController(launcher, allowDragToOverview));
+ }
+ if (FeatureFlags.PULL_DOWN_STATUS_BAR && Utilities.IS_DEBUG_DEVICE
+ && !launcher.getDeviceProfile().isMultiWindowMode
+ && !launcher.getDeviceProfile().isVerticalBarLayout()) {
+ list.add(new StatusBarTouchController(launcher));
+ }
+ return list.toArray(new TouchController[list.size()]);
}
/**
@@ -62,7 +74,7 @@
*
* @param launcher the launcher activity
*/
- public static void prepareToShowRecents(Launcher launcher) {
+ public static void prepareToShowOverview(Launcher launcher) {
View overview = launcher.getOverviewPanel();
if (overview.getVisibility() != VISIBLE) {
SCALE_PROPERTY.set(overview, RECENTS_PREPARE_SCALE);
@@ -74,7 +86,7 @@
*
* @param launcher the launcher activity
*/
- public static void resetRecents(Launcher launcher) {}
+ public static void resetOverview(Launcher launcher) {}
/**
* Recents logic that triggers when launcher state changes or launcher activity stops/resumes.
diff --git a/gradle.properties b/gradle.properties
index b299cfe..e31f59e 100644
--- a/gradle.properties
+++ b/gradle.properties
@@ -4,7 +4,7 @@
ANDROID_X_VERSION=1.0.0-beta01
-GRADLE_CLASS_PATH=com.android.tools.build:gradle:3.2.0-rc03
+GRADLE_CLASS_PATH=com.android.tools.build:gradle:3.3.0
PROTOBUF_CLASS_PATH=com.google.protobuf:protobuf-gradle-plugin:0.8.6
PROTOBUF_DEPENDENCY=com.google.protobuf.nano:protobuf-javanano:3.0.0-alpha-7
diff --git a/iconloaderlib/build.gradle b/iconloaderlib/build.gradle
index f6a820a..4fd3189 100644
--- a/iconloaderlib/build.gradle
+++ b/iconloaderlib/build.gradle
@@ -16,7 +16,7 @@
publishNonDefault true
defaultConfig {
- minSdkVersion 21
+ minSdkVersion 25
targetSdkVersion 28
versionCode 1
versionName "1.0"
diff --git a/iconloaderlib/src/com/android/launcher3/icons/FixedScaleDrawable.java b/iconloaderlib/src/com/android/launcher3/icons/FixedScaleDrawable.java
index e594f47..516965e 100644
--- a/iconloaderlib/src/com/android/launcher3/icons/FixedScaleDrawable.java
+++ b/iconloaderlib/src/com/android/launcher3/icons/FixedScaleDrawable.java
@@ -1,12 +1,10 @@
package com.android.launcher3.icons;
-import android.annotation.TargetApi;
import android.content.res.Resources;
import android.content.res.Resources.Theme;
import android.graphics.Canvas;
import android.graphics.drawable.ColorDrawable;
import android.graphics.drawable.DrawableWrapper;
-import android.os.Build;
import android.util.AttributeSet;
import org.xmlpull.v1.XmlPullParser;
@@ -14,7 +12,6 @@
/**
* Extension of {@link DrawableWrapper} which scales the child drawables by a fixed amount.
*/
-@TargetApi(Build.VERSION_CODES.N)
public class FixedScaleDrawable extends DrawableWrapper {
// TODO b/33553066 use the constant defined in MaskableIconDrawable
diff --git a/iconloaderlib/src/com/android/launcher3/icons/cache/BaseIconCache.java b/iconloaderlib/src/com/android/launcher3/icons/cache/BaseIconCache.java
index ce66448..2966cb1 100644
--- a/iconloaderlib/src/com/android/launcher3/icons/cache/BaseIconCache.java
+++ b/iconloaderlib/src/com/android/launcher3/icons/cache/BaseIconCache.java
@@ -47,16 +47,15 @@
import com.android.launcher3.icons.BitmapRenderer;
import com.android.launcher3.icons.GraphicsUtils;
import com.android.launcher3.util.ComponentKey;
-import com.android.launcher3.util.Provider;
import com.android.launcher3.util.SQLiteCacheHelper;
import java.util.AbstractMap;
import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;
-import java.util.Locale;
import java.util.Map;
import java.util.Set;
+import java.util.function.Supplier;
import androidx.annotation.NonNull;
@@ -231,13 +230,8 @@
* incorporates all the properties that can affect the cache like locale and system-version.
*/
private void updateSystemState() {
- final String locale;
- if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
- locale = mContext.getResources().getConfiguration().getLocales().toLanguageTags();
- } else {
- locale = Locale.getDefault().toString();
- }
-
+ final String locale =
+ mContext.getResources().getConfiguration().getLocales().toLanguageTags();
mSystemState = locale + "," + Build.VERSION.SDK_INT;
}
@@ -309,7 +303,7 @@
*/
protected <T> CacheEntry cacheLocked(
@NonNull ComponentName componentName, @NonNull UserHandle user,
- @NonNull Provider<T> infoProvider, @NonNull CachingLogic<T> cachingLogic,
+ @NonNull Supplier<T> infoProvider, @NonNull CachingLogic<T> cachingLogic,
boolean usePackageIcon, boolean useLowResIcon) {
return cacheLocked(componentName, user, infoProvider, cachingLogic, usePackageIcon,
useLowResIcon, true);
@@ -317,7 +311,7 @@
protected <T> CacheEntry cacheLocked(
@NonNull ComponentName componentName, @NonNull UserHandle user,
- @NonNull Provider<T> infoProvider, @NonNull CachingLogic<T> cachingLogic,
+ @NonNull Supplier<T> infoProvider, @NonNull CachingLogic<T> cachingLogic,
boolean usePackageIcon, boolean useLowResIcon, boolean addToMemCache) {
assertWorkerThread();
ComponentKey cacheKey = new ComponentKey(componentName, user);
diff --git a/iconloaderlib/src/com/android/launcher3/util/Provider.java b/iconloaderlib/src/com/android/launcher3/util/Provider.java
deleted file mode 100644
index 4a54c0f..0000000
--- a/iconloaderlib/src/com/android/launcher3/util/Provider.java
+++ /dev/null
@@ -1,33 +0,0 @@
-/*
- * Copyright (C) 2016 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.launcher3.util;
-
-/**
- * Utility class to allow lazy initialization of objects.
- */
-public interface Provider<T> {
-
- /**
- * Initializes and returns the object. This may contain expensive operations not suitable
- * to UI thread.
- */
- T get();
-
- static <T> Provider<T> of (T value) {
- return() -> value;
- }
-}
diff --git a/quickstep/recents_ui_overrides/src/com/android/launcher3/uioverrides/FlingAndHoldTouchController.java b/quickstep/recents_ui_overrides/src/com/android/launcher3/uioverrides/FlingAndHoldTouchController.java
new file mode 100644
index 0000000..fb83cd3
--- /dev/null
+++ b/quickstep/recents_ui_overrides/src/com/android/launcher3/uioverrides/FlingAndHoldTouchController.java
@@ -0,0 +1,95 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.launcher3.uioverrides;
+
+import static com.android.launcher3.LauncherState.NORMAL;
+import static com.android.launcher3.LauncherState.OVERVIEW;
+import static com.android.launcher3.LauncherStateManager.NON_ATOMIC_COMPONENT;
+
+import android.animation.ValueAnimator;
+
+import com.android.launcher3.Launcher;
+import com.android.launcher3.anim.AnimatorSetBuilder;
+import com.android.launcher3.userevent.nano.LauncherLogProto.Action.Touch;
+import com.android.quickstep.util.MotionPauseDetector;
+import com.android.quickstep.views.RecentsView;
+
+/**
+ * Touch controller which handles swipe and hold to go to Overview
+ */
+public class FlingAndHoldTouchController extends PortraitStatesTouchController {
+
+ private final MotionPauseDetector mMotionPauseDetector;
+
+ public FlingAndHoldTouchController(Launcher l) {
+ super(l, false /* allowDragToOverview */);
+ mMotionPauseDetector = new MotionPauseDetector(l);
+ }
+
+ @Override
+ public void onDragStart(boolean start) {
+ mMotionPauseDetector.clear();
+
+ super.onDragStart(start);
+
+ if (mStartState == NORMAL) {
+ mMotionPauseDetector.setOnMotionPauseListener(isPaused -> {
+ RecentsView recentsView = mLauncher.getOverviewPanel();
+ recentsView.setOverviewStateEnabled(isPaused);
+ maybeUpdateAtomicAnim(NORMAL, OVERVIEW, isPaused ? 1 : 0);
+ });
+ }
+ }
+
+ @Override
+ public boolean onDrag(float displacement) {
+ mMotionPauseDetector.addPosition(displacement);
+ return super.onDrag(displacement);
+ }
+
+ @Override
+ public void onDragEnd(float velocity, boolean fling) {
+ if (mMotionPauseDetector.isPaused() && mStartState == NORMAL) {
+ float range = getShiftRange();
+ long maxAccuracy = (long) (2 * range);
+
+ // Let the state manager know that the animation didn't go to the target state,
+ // but don't cancel ourselves (we already clean up when the animation completes).
+ Runnable onCancel = mCurrentAnimation.getOnCancelRunnable();
+ mCurrentAnimation.setOnCancelRunnable(null);
+ mCurrentAnimation.dispatchOnCancel();
+ mCurrentAnimation = mLauncher.getStateManager()
+ .createAnimationToNewWorkspace(OVERVIEW, new AnimatorSetBuilder(), maxAccuracy,
+ onCancel, NON_ATOMIC_COMPONENT);
+
+ final int logAction = fling ? Touch.FLING : Touch.SWIPE;
+ mCurrentAnimation.setEndAction(() -> onSwipeInteractionCompleted(OVERVIEW, logAction));
+
+
+ ValueAnimator anim = mCurrentAnimation.getAnimationPlayer();
+ maybeUpdateAtomicAnim(NORMAL, OVERVIEW, 1f);
+ mCurrentAnimation.dispatchOnStartWithVelocity(1, velocity);
+
+ // TODO: Find a better duration
+ anim.setDuration(100);
+ anim.start();
+ settleAtomicAnimation(1f, anim.getDuration());
+ } else {
+ super.onDragEnd(velocity, fling);
+ }
+ }
+}
diff --git a/quickstep/recents_ui_overrides/src/com/android/launcher3/uioverrides/RecentsUiFactory.java b/quickstep/recents_ui_overrides/src/com/android/launcher3/uioverrides/RecentsUiFactory.java
index f18f43c..51e9495 100644
--- a/quickstep/recents_ui_overrides/src/com/android/launcher3/uioverrides/RecentsUiFactory.java
+++ b/quickstep/recents_ui_overrides/src/com/android/launcher3/uioverrides/RecentsUiFactory.java
@@ -21,32 +21,65 @@
import static com.android.launcher3.LauncherAnimUtils.SCALE_PROPERTY;
import static com.android.launcher3.LauncherState.NORMAL;
import static com.android.launcher3.LauncherState.OVERVIEW;
+import static com.android.launcher3.config.FeatureFlags.SWIPE_HOME;
+import com.android.launcher3.DeviceProfile;
import com.android.launcher3.Launcher;
import com.android.launcher3.LauncherState;
import com.android.launcher3.LauncherStateManager.StateHandler;
+import com.android.launcher3.Utilities;
import com.android.launcher3.anim.AnimatorPlaybackController;
+import com.android.launcher3.config.FeatureFlags;
import com.android.launcher3.util.TouchController;
+import com.android.launcher3.util.UiThreadHelper;
+import com.android.launcher3.util.UiThreadHelper.AsyncCommand;
+import com.android.quickstep.OverviewInteractionState;
import com.android.quickstep.views.RecentsView;
+import com.android.systemui.shared.system.WindowManagerWrapper;
+
+import java.util.ArrayList;
/**
* Provides recents-related {@link UiFactory} logic and classes.
*/
-public final class RecentsUiFactory {
+public abstract class RecentsUiFactory {
+
+ private static final AsyncCommand SET_SHELF_HEIGHT_CMD = (visible, height) ->
+ WindowManagerWrapper.getInstance().setShelfHeight(visible != 0, height);
// Scale recents takes before animating in
private static final float RECENTS_PREPARE_SCALE = 1.33f;
- private RecentsUiFactory() {}
+ public static TouchController[] createTouchControllers(Launcher launcher) {
+ boolean swipeUpEnabled = OverviewInteractionState.INSTANCE.get(launcher)
+ .isSwipeUpGestureEnabled();
+ boolean swipeUpToHome = swipeUpEnabled && SWIPE_HOME.get();
- /**
- * Creates and returns a touch controller for swiping recents tasks.
- *
- * @param launcher the launcher activity
- * @return the touch controller for recents tasks
- */
- public static TouchController createTaskSwipeController(Launcher launcher) {
- return new LauncherTaskViewController(launcher);
+
+ ArrayList<TouchController> list = new ArrayList<>();
+ list.add(launcher.getDragController());
+
+ if (swipeUpToHome) {
+ list.add(new FlingAndHoldTouchController(launcher));
+ list.add(new OverviewToAllAppsTouchController(launcher));
+ } else {
+ if (launcher.getDeviceProfile().isVerticalBarLayout()) {
+ list.add(new OverviewToAllAppsTouchController(launcher));
+ list.add(new LandscapeEdgeSwipeController(launcher));
+ } else {
+ list.add(new PortraitStatesTouchController(launcher,
+ swipeUpEnabled /* allowDragToOverview */));
+ }
+ }
+
+ if (FeatureFlags.PULL_DOWN_STATUS_BAR && Utilities.IS_DEBUG_DEVICE
+ && !launcher.getDeviceProfile().isMultiWindowMode
+ && !launcher.getDeviceProfile().isVerticalBarLayout()) {
+ list.add(new StatusBarTouchController(launcher));
+ }
+
+ list.add(new LauncherTaskViewController(launcher));
+ return list.toArray(new TouchController[list.size()]);
}
/**
@@ -64,7 +97,7 @@
*
* @param launcher the launcher activity
*/
- public static void prepareToShowRecents(Launcher launcher) {
+ public static void prepareToShowOverview(Launcher launcher) {
RecentsView overview = launcher.getOverviewPanel();
if (overview.getVisibility() != VISIBLE || overview.getContentAlpha() == 0) {
SCALE_PROPERTY.set(overview, RECENTS_PREPARE_SCALE);
@@ -76,9 +109,8 @@
*
* @param launcher the launcher activity
*/
- public static void resetRecents(Launcher launcher) {
- RecentsView recents = launcher.getOverviewPanel();
- recents.reset();
+ public static void resetOverview(Launcher launcher) {
+ launcher.<RecentsView>getOverviewPanel().reset();
}
/**
@@ -88,6 +120,14 @@
*/
public static void onLauncherStateOrResumeChanged(Launcher launcher) {
LauncherState state = launcher.getStateManager().getState();
+ if (!OverviewInteractionState.INSTANCE.get(launcher).swipeGestureInitializing()) {
+ DeviceProfile profile = launcher.getDeviceProfile();
+ boolean visible = (state == NORMAL || state == OVERVIEW) && launcher.isUserActive()
+ && !profile.isVerticalBarLayout();
+ UiThreadHelper.runAsyncCommand(launcher, SET_SHELF_HEIGHT_CMD,
+ visible ? 1 : 0, profile.hotseatBarSizePx);
+ }
+
if (state == NORMAL) {
launcher.<RecentsView>getOverviewPanel().setSwipeDownShouldLaunchApp(false);
}
diff --git a/quickstep/res/values-af/strings.xml b/quickstep/res/values-af/strings.xml
index 5ce3c0d..6f02728 100644
--- a/quickstep/res/values-af/strings.xml
+++ b/quickstep/res/values-af/strings.xml
@@ -28,4 +28,12 @@
<string name="accessibility_app_usage_settings" msgid="6312864233673544149">"Programgebruikinstellings"</string>
<string name="recents_clear_all" msgid="5328176793634888831">"Vee alles uit"</string>
<string name="accessibility_recent_apps" msgid="4058661986695117371">"Onlangse programme"</string>
+ <!-- no translation found for task_contents_description_with_remaining_time (4479688746574672685) -->
+ <skip />
+ <!-- no translation found for shorter_duration_less_than_one_minute (4722015666335015336) -->
+ <skip />
+ <!-- no translation found for app_in_grayscale (1108706002158384887) -->
+ <skip />
+ <!-- no translation found for time_left_for_app (3111996412933644358) -->
+ <skip />
</resources>
diff --git a/quickstep/res/values-am/strings.xml b/quickstep/res/values-am/strings.xml
index 99dc137..03dec1b 100644
--- a/quickstep/res/values-am/strings.xml
+++ b/quickstep/res/values-am/strings.xml
@@ -28,4 +28,12 @@
<string name="accessibility_app_usage_settings" msgid="6312864233673544149">"የመተግበሪያ አጠቃቀም ቅንብሮች"</string>
<string name="recents_clear_all" msgid="5328176793634888831">"ሁሉንም አጽዳ"</string>
<string name="accessibility_recent_apps" msgid="4058661986695117371">"የቅርብ ጊዜ መተግበሪያዎች"</string>
+ <!-- no translation found for task_contents_description_with_remaining_time (4479688746574672685) -->
+ <skip />
+ <!-- no translation found for shorter_duration_less_than_one_minute (4722015666335015336) -->
+ <skip />
+ <!-- no translation found for app_in_grayscale (1108706002158384887) -->
+ <skip />
+ <!-- no translation found for time_left_for_app (3111996412933644358) -->
+ <skip />
</resources>
diff --git a/quickstep/res/values-ar/strings.xml b/quickstep/res/values-ar/strings.xml
index 808082e..268e9bd 100644
--- a/quickstep/res/values-ar/strings.xml
+++ b/quickstep/res/values-ar/strings.xml
@@ -28,4 +28,12 @@
<string name="accessibility_app_usage_settings" msgid="6312864233673544149">"إعدادات استخدام التطبيق"</string>
<string name="recents_clear_all" msgid="5328176793634888831">"محو الكل"</string>
<string name="accessibility_recent_apps" msgid="4058661986695117371">"التطبيقات التي تمّ استخدامها مؤخرًا"</string>
+ <!-- no translation found for task_contents_description_with_remaining_time (4479688746574672685) -->
+ <skip />
+ <!-- no translation found for shorter_duration_less_than_one_minute (4722015666335015336) -->
+ <skip />
+ <!-- no translation found for app_in_grayscale (1108706002158384887) -->
+ <skip />
+ <!-- no translation found for time_left_for_app (3111996412933644358) -->
+ <skip />
</resources>
diff --git a/quickstep/res/values-as/strings.xml b/quickstep/res/values-as/strings.xml
index eac2878..89ca0f7 100644
--- a/quickstep/res/values-as/strings.xml
+++ b/quickstep/res/values-as/strings.xml
@@ -28,4 +28,12 @@
<string name="accessibility_app_usage_settings" msgid="6312864233673544149">"এপে ব্যৱহাৰ কৰা ডেটাৰ ছেটিংসমূহ"</string>
<string name="recents_clear_all" msgid="5328176793634888831">"সকলো মচক"</string>
<string name="accessibility_recent_apps" msgid="4058661986695117371">"শেহতীয়া এপসমূহ"</string>
+ <!-- no translation found for task_contents_description_with_remaining_time (4479688746574672685) -->
+ <skip />
+ <!-- no translation found for shorter_duration_less_than_one_minute (4722015666335015336) -->
+ <skip />
+ <!-- no translation found for app_in_grayscale (1108706002158384887) -->
+ <skip />
+ <!-- no translation found for time_left_for_app (3111996412933644358) -->
+ <skip />
</resources>
diff --git a/quickstep/res/values-az/strings.xml b/quickstep/res/values-az/strings.xml
index d707108..4353b75 100644
--- a/quickstep/res/values-az/strings.xml
+++ b/quickstep/res/values-az/strings.xml
@@ -28,4 +28,12 @@
<string name="accessibility_app_usage_settings" msgid="6312864233673544149">"Tətbiq istifadə ayarları"</string>
<string name="recents_clear_all" msgid="5328176793634888831">"Hamısını silin"</string>
<string name="accessibility_recent_apps" msgid="4058661986695117371">"Son tətbiqlər"</string>
+ <!-- no translation found for task_contents_description_with_remaining_time (4479688746574672685) -->
+ <skip />
+ <!-- no translation found for shorter_duration_less_than_one_minute (4722015666335015336) -->
+ <skip />
+ <!-- no translation found for app_in_grayscale (1108706002158384887) -->
+ <skip />
+ <!-- no translation found for time_left_for_app (3111996412933644358) -->
+ <skip />
</resources>
diff --git a/quickstep/res/values-b+sr+Latn/strings.xml b/quickstep/res/values-b+sr+Latn/strings.xml
index fd449b8..5a80ebd 100644
--- a/quickstep/res/values-b+sr+Latn/strings.xml
+++ b/quickstep/res/values-b+sr+Latn/strings.xml
@@ -28,4 +28,12 @@
<string name="accessibility_app_usage_settings" msgid="6312864233673544149">"Podešavanja korišćenja aplikacije"</string>
<string name="recents_clear_all" msgid="5328176793634888831">"Obriši sve"</string>
<string name="accessibility_recent_apps" msgid="4058661986695117371">"Nedavne aplikacije"</string>
+ <!-- no translation found for task_contents_description_with_remaining_time (4479688746574672685) -->
+ <skip />
+ <!-- no translation found for shorter_duration_less_than_one_minute (4722015666335015336) -->
+ <skip />
+ <!-- no translation found for app_in_grayscale (1108706002158384887) -->
+ <skip />
+ <!-- no translation found for time_left_for_app (3111996412933644358) -->
+ <skip />
</resources>
diff --git a/quickstep/res/values-be/strings.xml b/quickstep/res/values-be/strings.xml
index fa1f28e..e2a1940 100644
--- a/quickstep/res/values-be/strings.xml
+++ b/quickstep/res/values-be/strings.xml
@@ -28,4 +28,12 @@
<string name="accessibility_app_usage_settings" msgid="6312864233673544149">"Налады выкарыстання праграмы"</string>
<string name="recents_clear_all" msgid="5328176793634888831">"Ачысціць усё"</string>
<string name="accessibility_recent_apps" msgid="4058661986695117371">"Нядаўнія праграмы"</string>
+ <!-- no translation found for task_contents_description_with_remaining_time (4479688746574672685) -->
+ <skip />
+ <!-- no translation found for shorter_duration_less_than_one_minute (4722015666335015336) -->
+ <skip />
+ <!-- no translation found for app_in_grayscale (1108706002158384887) -->
+ <skip />
+ <!-- no translation found for time_left_for_app (3111996412933644358) -->
+ <skip />
</resources>
diff --git a/quickstep/res/values-bg/strings.xml b/quickstep/res/values-bg/strings.xml
index 0f817ac..5e23876 100644
--- a/quickstep/res/values-bg/strings.xml
+++ b/quickstep/res/values-bg/strings.xml
@@ -28,4 +28,12 @@
<string name="accessibility_app_usage_settings" msgid="6312864233673544149">"Настройки за използването на приложенията"</string>
<string name="recents_clear_all" msgid="5328176793634888831">"Изчистване на всички"</string>
<string name="accessibility_recent_apps" msgid="4058661986695117371">"Скорошни приложения"</string>
+ <!-- no translation found for task_contents_description_with_remaining_time (4479688746574672685) -->
+ <skip />
+ <!-- no translation found for shorter_duration_less_than_one_minute (4722015666335015336) -->
+ <skip />
+ <!-- no translation found for app_in_grayscale (1108706002158384887) -->
+ <skip />
+ <!-- no translation found for time_left_for_app (3111996412933644358) -->
+ <skip />
</resources>
diff --git a/quickstep/res/values-bn/strings.xml b/quickstep/res/values-bn/strings.xml
index c7342f3..9490692 100644
--- a/quickstep/res/values-bn/strings.xml
+++ b/quickstep/res/values-bn/strings.xml
@@ -28,4 +28,12 @@
<string name="accessibility_app_usage_settings" msgid="6312864233673544149">"অ্যাপ ব্যবহারের সেটিংস"</string>
<string name="recents_clear_all" msgid="5328176793634888831">"সবকিছু খালি করুন"</string>
<string name="accessibility_recent_apps" msgid="4058661986695117371">"সম্প্রতি ব্যবহৃত অ্যাপ"</string>
+ <!-- no translation found for task_contents_description_with_remaining_time (4479688746574672685) -->
+ <skip />
+ <!-- no translation found for shorter_duration_less_than_one_minute (4722015666335015336) -->
+ <skip />
+ <!-- no translation found for app_in_grayscale (1108706002158384887) -->
+ <skip />
+ <!-- no translation found for time_left_for_app (3111996412933644358) -->
+ <skip />
</resources>
diff --git a/quickstep/res/values-bs/strings.xml b/quickstep/res/values-bs/strings.xml
index d0d0aa6..3af9436 100644
--- a/quickstep/res/values-bs/strings.xml
+++ b/quickstep/res/values-bs/strings.xml
@@ -28,4 +28,12 @@
<string name="accessibility_app_usage_settings" msgid="6312864233673544149">"Postavke korištenja aplikacije"</string>
<string name="recents_clear_all" msgid="5328176793634888831">"Obriši sve"</string>
<string name="accessibility_recent_apps" msgid="4058661986695117371">"Nedavne aplikacije"</string>
+ <!-- no translation found for task_contents_description_with_remaining_time (4479688746574672685) -->
+ <skip />
+ <!-- no translation found for shorter_duration_less_than_one_minute (4722015666335015336) -->
+ <skip />
+ <!-- no translation found for app_in_grayscale (1108706002158384887) -->
+ <skip />
+ <!-- no translation found for time_left_for_app (3111996412933644358) -->
+ <skip />
</resources>
diff --git a/quickstep/res/values-ca/strings.xml b/quickstep/res/values-ca/strings.xml
index 5bdd67d..728c0ea 100644
--- a/quickstep/res/values-ca/strings.xml
+++ b/quickstep/res/values-ca/strings.xml
@@ -28,4 +28,12 @@
<string name="accessibility_app_usage_settings" msgid="6312864233673544149">"Configuració d\'ús d\'aplicacions"</string>
<string name="recents_clear_all" msgid="5328176793634888831">"Esborra-ho tot"</string>
<string name="accessibility_recent_apps" msgid="4058661986695117371">"Aplicacions recents"</string>
+ <!-- no translation found for task_contents_description_with_remaining_time (4479688746574672685) -->
+ <skip />
+ <!-- no translation found for shorter_duration_less_than_one_minute (4722015666335015336) -->
+ <skip />
+ <!-- no translation found for app_in_grayscale (1108706002158384887) -->
+ <skip />
+ <!-- no translation found for time_left_for_app (3111996412933644358) -->
+ <skip />
</resources>
diff --git a/quickstep/res/values-cs/strings.xml b/quickstep/res/values-cs/strings.xml
index 5fe7088..a9a1ba2 100644
--- a/quickstep/res/values-cs/strings.xml
+++ b/quickstep/res/values-cs/strings.xml
@@ -28,4 +28,12 @@
<string name="accessibility_app_usage_settings" msgid="6312864233673544149">"Nastavení využití aplikací"</string>
<string name="recents_clear_all" msgid="5328176793634888831">"Vymazat vše"</string>
<string name="accessibility_recent_apps" msgid="4058661986695117371">"Poslední aplikace"</string>
+ <!-- no translation found for task_contents_description_with_remaining_time (4479688746574672685) -->
+ <skip />
+ <!-- no translation found for shorter_duration_less_than_one_minute (4722015666335015336) -->
+ <skip />
+ <!-- no translation found for app_in_grayscale (1108706002158384887) -->
+ <skip />
+ <!-- no translation found for time_left_for_app (3111996412933644358) -->
+ <skip />
</resources>
diff --git a/quickstep/res/values-da/strings.xml b/quickstep/res/values-da/strings.xml
index 4c3ec59..d10fd63 100644
--- a/quickstep/res/values-da/strings.xml
+++ b/quickstep/res/values-da/strings.xml
@@ -28,4 +28,12 @@
<string name="accessibility_app_usage_settings" msgid="6312864233673544149">"Indstillinger for appforbrug"</string>
<string name="recents_clear_all" msgid="5328176793634888831">"Ryd alt"</string>
<string name="accessibility_recent_apps" msgid="4058661986695117371">"Seneste apps"</string>
+ <!-- no translation found for task_contents_description_with_remaining_time (4479688746574672685) -->
+ <skip />
+ <!-- no translation found for shorter_duration_less_than_one_minute (4722015666335015336) -->
+ <skip />
+ <!-- no translation found for app_in_grayscale (1108706002158384887) -->
+ <skip />
+ <!-- no translation found for time_left_for_app (3111996412933644358) -->
+ <skip />
</resources>
diff --git a/quickstep/res/values-de/strings.xml b/quickstep/res/values-de/strings.xml
index fb428f7..0a65bee 100644
--- a/quickstep/res/values-de/strings.xml
+++ b/quickstep/res/values-de/strings.xml
@@ -28,4 +28,12 @@
<string name="accessibility_app_usage_settings" msgid="6312864233673544149">"Einstellungen zur App-Nutzung"</string>
<string name="recents_clear_all" msgid="5328176793634888831">"Alle Apps schließen"</string>
<string name="accessibility_recent_apps" msgid="4058661986695117371">"Zuletzt aktive Apps"</string>
+ <!-- no translation found for task_contents_description_with_remaining_time (4479688746574672685) -->
+ <skip />
+ <!-- no translation found for shorter_duration_less_than_one_minute (4722015666335015336) -->
+ <skip />
+ <!-- no translation found for app_in_grayscale (1108706002158384887) -->
+ <skip />
+ <!-- no translation found for time_left_for_app (3111996412933644358) -->
+ <skip />
</resources>
diff --git a/quickstep/res/values-el/strings.xml b/quickstep/res/values-el/strings.xml
index 54d4677..edb6381 100644
--- a/quickstep/res/values-el/strings.xml
+++ b/quickstep/res/values-el/strings.xml
@@ -28,4 +28,12 @@
<string name="accessibility_app_usage_settings" msgid="6312864233673544149">"Ρυθμίσεις χρήσης εφαρμογής"</string>
<string name="recents_clear_all" msgid="5328176793634888831">"Διαγραφή όλων"</string>
<string name="accessibility_recent_apps" msgid="4058661986695117371">"Πρόσφατες εφαρμογές"</string>
+ <!-- no translation found for task_contents_description_with_remaining_time (4479688746574672685) -->
+ <skip />
+ <!-- no translation found for shorter_duration_less_than_one_minute (4722015666335015336) -->
+ <skip />
+ <!-- no translation found for app_in_grayscale (1108706002158384887) -->
+ <skip />
+ <!-- no translation found for time_left_for_app (3111996412933644358) -->
+ <skip />
</resources>
diff --git a/quickstep/res/values-en-rAU/strings.xml b/quickstep/res/values-en-rAU/strings.xml
index df2fee8..de9a648 100644
--- a/quickstep/res/values-en-rAU/strings.xml
+++ b/quickstep/res/values-en-rAU/strings.xml
@@ -28,4 +28,12 @@
<string name="accessibility_app_usage_settings" msgid="6312864233673544149">"App usage settings"</string>
<string name="recents_clear_all" msgid="5328176793634888831">"Clear all"</string>
<string name="accessibility_recent_apps" msgid="4058661986695117371">"Recent apps"</string>
+ <!-- no translation found for task_contents_description_with_remaining_time (4479688746574672685) -->
+ <skip />
+ <!-- no translation found for shorter_duration_less_than_one_minute (4722015666335015336) -->
+ <skip />
+ <!-- no translation found for app_in_grayscale (1108706002158384887) -->
+ <skip />
+ <!-- no translation found for time_left_for_app (3111996412933644358) -->
+ <skip />
</resources>
diff --git a/quickstep/res/values-en-rGB/strings.xml b/quickstep/res/values-en-rGB/strings.xml
index df2fee8..de9a648 100644
--- a/quickstep/res/values-en-rGB/strings.xml
+++ b/quickstep/res/values-en-rGB/strings.xml
@@ -28,4 +28,12 @@
<string name="accessibility_app_usage_settings" msgid="6312864233673544149">"App usage settings"</string>
<string name="recents_clear_all" msgid="5328176793634888831">"Clear all"</string>
<string name="accessibility_recent_apps" msgid="4058661986695117371">"Recent apps"</string>
+ <!-- no translation found for task_contents_description_with_remaining_time (4479688746574672685) -->
+ <skip />
+ <!-- no translation found for shorter_duration_less_than_one_minute (4722015666335015336) -->
+ <skip />
+ <!-- no translation found for app_in_grayscale (1108706002158384887) -->
+ <skip />
+ <!-- no translation found for time_left_for_app (3111996412933644358) -->
+ <skip />
</resources>
diff --git a/quickstep/res/values-en-rIN/strings.xml b/quickstep/res/values-en-rIN/strings.xml
index df2fee8..de9a648 100644
--- a/quickstep/res/values-en-rIN/strings.xml
+++ b/quickstep/res/values-en-rIN/strings.xml
@@ -28,4 +28,12 @@
<string name="accessibility_app_usage_settings" msgid="6312864233673544149">"App usage settings"</string>
<string name="recents_clear_all" msgid="5328176793634888831">"Clear all"</string>
<string name="accessibility_recent_apps" msgid="4058661986695117371">"Recent apps"</string>
+ <!-- no translation found for task_contents_description_with_remaining_time (4479688746574672685) -->
+ <skip />
+ <!-- no translation found for shorter_duration_less_than_one_minute (4722015666335015336) -->
+ <skip />
+ <!-- no translation found for app_in_grayscale (1108706002158384887) -->
+ <skip />
+ <!-- no translation found for time_left_for_app (3111996412933644358) -->
+ <skip />
</resources>
diff --git a/quickstep/res/values-es-rUS/strings.xml b/quickstep/res/values-es-rUS/strings.xml
index 9d98d6b..e9abdb5 100644
--- a/quickstep/res/values-es-rUS/strings.xml
+++ b/quickstep/res/values-es-rUS/strings.xml
@@ -28,4 +28,12 @@
<string name="accessibility_app_usage_settings" msgid="6312864233673544149">"Configuración de uso de la app"</string>
<string name="recents_clear_all" msgid="5328176793634888831">"Borrar todo"</string>
<string name="accessibility_recent_apps" msgid="4058661986695117371">"Apps recientes"</string>
+ <!-- no translation found for task_contents_description_with_remaining_time (4479688746574672685) -->
+ <skip />
+ <!-- no translation found for shorter_duration_less_than_one_minute (4722015666335015336) -->
+ <skip />
+ <!-- no translation found for app_in_grayscale (1108706002158384887) -->
+ <skip />
+ <!-- no translation found for time_left_for_app (3111996412933644358) -->
+ <skip />
</resources>
diff --git a/quickstep/res/values-es/strings.xml b/quickstep/res/values-es/strings.xml
index b39262b..8d05c1a 100644
--- a/quickstep/res/values-es/strings.xml
+++ b/quickstep/res/values-es/strings.xml
@@ -28,4 +28,12 @@
<string name="accessibility_app_usage_settings" msgid="6312864233673544149">"Ajustes de uso de la aplicación"</string>
<string name="recents_clear_all" msgid="5328176793634888831">"Borrar todo"</string>
<string name="accessibility_recent_apps" msgid="4058661986695117371">"Aplicaciones recientes"</string>
+ <!-- no translation found for task_contents_description_with_remaining_time (4479688746574672685) -->
+ <skip />
+ <!-- no translation found for shorter_duration_less_than_one_minute (4722015666335015336) -->
+ <skip />
+ <!-- no translation found for app_in_grayscale (1108706002158384887) -->
+ <skip />
+ <!-- no translation found for time_left_for_app (3111996412933644358) -->
+ <skip />
</resources>
diff --git a/quickstep/res/values-et/strings.xml b/quickstep/res/values-et/strings.xml
index 9b859fd..54b4af8 100644
--- a/quickstep/res/values-et/strings.xml
+++ b/quickstep/res/values-et/strings.xml
@@ -28,4 +28,12 @@
<string name="accessibility_app_usage_settings" msgid="6312864233673544149">"Rakenduse kasutuse seaded"</string>
<string name="recents_clear_all" msgid="5328176793634888831">"Sule kõik"</string>
<string name="accessibility_recent_apps" msgid="4058661986695117371">"Hiljutised rakendused"</string>
+ <!-- no translation found for task_contents_description_with_remaining_time (4479688746574672685) -->
+ <skip />
+ <!-- no translation found for shorter_duration_less_than_one_minute (4722015666335015336) -->
+ <skip />
+ <!-- no translation found for app_in_grayscale (1108706002158384887) -->
+ <skip />
+ <!-- no translation found for time_left_for_app (3111996412933644358) -->
+ <skip />
</resources>
diff --git a/quickstep/res/values-eu/strings.xml b/quickstep/res/values-eu/strings.xml
index 096a849..3dffe0f 100644
--- a/quickstep/res/values-eu/strings.xml
+++ b/quickstep/res/values-eu/strings.xml
@@ -28,4 +28,12 @@
<string name="accessibility_app_usage_settings" msgid="6312864233673544149">"Aplikazioen erabileraren ezarpenak"</string>
<string name="recents_clear_all" msgid="5328176793634888831">"Garbitu guztiak"</string>
<string name="accessibility_recent_apps" msgid="4058661986695117371">"Azken aplikazioak"</string>
+ <!-- no translation found for task_contents_description_with_remaining_time (4479688746574672685) -->
+ <skip />
+ <!-- no translation found for shorter_duration_less_than_one_minute (4722015666335015336) -->
+ <skip />
+ <!-- no translation found for app_in_grayscale (1108706002158384887) -->
+ <skip />
+ <!-- no translation found for time_left_for_app (3111996412933644358) -->
+ <skip />
</resources>
diff --git a/quickstep/res/values-fa/strings.xml b/quickstep/res/values-fa/strings.xml
index 3ae1bd8..67ebbc7 100644
--- a/quickstep/res/values-fa/strings.xml
+++ b/quickstep/res/values-fa/strings.xml
@@ -28,4 +28,12 @@
<string name="accessibility_app_usage_settings" msgid="6312864233673544149">"تنظیمات استفاده از برنامه"</string>
<string name="recents_clear_all" msgid="5328176793634888831">"پاک کردن همه"</string>
<string name="accessibility_recent_apps" msgid="4058661986695117371">"برنامههای اخیر"</string>
+ <!-- no translation found for task_contents_description_with_remaining_time (4479688746574672685) -->
+ <skip />
+ <!-- no translation found for shorter_duration_less_than_one_minute (4722015666335015336) -->
+ <skip />
+ <!-- no translation found for app_in_grayscale (1108706002158384887) -->
+ <skip />
+ <!-- no translation found for time_left_for_app (3111996412933644358) -->
+ <skip />
</resources>
diff --git a/quickstep/res/values-fi/strings.xml b/quickstep/res/values-fi/strings.xml
index 79c2d6b..31ac3ca 100644
--- a/quickstep/res/values-fi/strings.xml
+++ b/quickstep/res/values-fi/strings.xml
@@ -28,4 +28,12 @@
<string name="accessibility_app_usage_settings" msgid="6312864233673544149">"Sovelluksen käyttöasetukset"</string>
<string name="recents_clear_all" msgid="5328176793634888831">"Poista kaikki"</string>
<string name="accessibility_recent_apps" msgid="4058661986695117371">"Viimeisimmät sovellukset"</string>
+ <!-- no translation found for task_contents_description_with_remaining_time (4479688746574672685) -->
+ <skip />
+ <!-- no translation found for shorter_duration_less_than_one_minute (4722015666335015336) -->
+ <skip />
+ <!-- no translation found for app_in_grayscale (1108706002158384887) -->
+ <skip />
+ <!-- no translation found for time_left_for_app (3111996412933644358) -->
+ <skip />
</resources>
diff --git a/quickstep/res/values-fr-rCA/strings.xml b/quickstep/res/values-fr-rCA/strings.xml
index e64985e..d090d10 100644
--- a/quickstep/res/values-fr-rCA/strings.xml
+++ b/quickstep/res/values-fr-rCA/strings.xml
@@ -28,4 +28,12 @@
<string name="accessibility_app_usage_settings" msgid="6312864233673544149">"Paramètres d\'utilisation de l\'application"</string>
<string name="recents_clear_all" msgid="5328176793634888831">"Tout effacer"</string>
<string name="accessibility_recent_apps" msgid="4058661986695117371">"Applications récentes"</string>
+ <!-- no translation found for task_contents_description_with_remaining_time (4479688746574672685) -->
+ <skip />
+ <!-- no translation found for shorter_duration_less_than_one_minute (4722015666335015336) -->
+ <skip />
+ <!-- no translation found for app_in_grayscale (1108706002158384887) -->
+ <skip />
+ <!-- no translation found for time_left_for_app (3111996412933644358) -->
+ <skip />
</resources>
diff --git a/quickstep/res/values-fr/strings.xml b/quickstep/res/values-fr/strings.xml
index 03b2f68..7659ed2 100644
--- a/quickstep/res/values-fr/strings.xml
+++ b/quickstep/res/values-fr/strings.xml
@@ -28,4 +28,12 @@
<string name="accessibility_app_usage_settings" msgid="6312864233673544149">"Paramètres de consommation de l\'application"</string>
<string name="recents_clear_all" msgid="5328176793634888831">"Tout effacer"</string>
<string name="accessibility_recent_apps" msgid="4058661986695117371">"Applications récentes"</string>
+ <!-- no translation found for task_contents_description_with_remaining_time (4479688746574672685) -->
+ <skip />
+ <!-- no translation found for shorter_duration_less_than_one_minute (4722015666335015336) -->
+ <skip />
+ <!-- no translation found for app_in_grayscale (1108706002158384887) -->
+ <skip />
+ <!-- no translation found for time_left_for_app (3111996412933644358) -->
+ <skip />
</resources>
diff --git a/quickstep/res/values-gl/strings.xml b/quickstep/res/values-gl/strings.xml
index 2fefd82..1358d7f 100644
--- a/quickstep/res/values-gl/strings.xml
+++ b/quickstep/res/values-gl/strings.xml
@@ -28,4 +28,12 @@
<string name="accessibility_app_usage_settings" msgid="6312864233673544149">"Configuración do uso de aplicacións"</string>
<string name="recents_clear_all" msgid="5328176793634888831">"Borrar todo"</string>
<string name="accessibility_recent_apps" msgid="4058661986695117371">"Aplicacións recentes"</string>
+ <!-- no translation found for task_contents_description_with_remaining_time (4479688746574672685) -->
+ <skip />
+ <!-- no translation found for shorter_duration_less_than_one_minute (4722015666335015336) -->
+ <skip />
+ <!-- no translation found for app_in_grayscale (1108706002158384887) -->
+ <skip />
+ <!-- no translation found for time_left_for_app (3111996412933644358) -->
+ <skip />
</resources>
diff --git a/quickstep/res/values-gu/strings.xml b/quickstep/res/values-gu/strings.xml
index 2e5fdde..746ec1a 100644
--- a/quickstep/res/values-gu/strings.xml
+++ b/quickstep/res/values-gu/strings.xml
@@ -28,4 +28,12 @@
<string name="accessibility_app_usage_settings" msgid="6312864233673544149">"ઍપ વપરાશનું સેટિંગ"</string>
<string name="recents_clear_all" msgid="5328176793634888831">"બધું સાફ કરો"</string>
<string name="accessibility_recent_apps" msgid="4058661986695117371">"તાજેતરની ઍપ"</string>
+ <!-- no translation found for task_contents_description_with_remaining_time (4479688746574672685) -->
+ <skip />
+ <!-- no translation found for shorter_duration_less_than_one_minute (4722015666335015336) -->
+ <skip />
+ <!-- no translation found for app_in_grayscale (1108706002158384887) -->
+ <skip />
+ <!-- no translation found for time_left_for_app (3111996412933644358) -->
+ <skip />
</resources>
diff --git a/quickstep/res/values-hi/strings.xml b/quickstep/res/values-hi/strings.xml
index a1107a2..55d8ccc 100644
--- a/quickstep/res/values-hi/strings.xml
+++ b/quickstep/res/values-hi/strings.xml
@@ -28,4 +28,12 @@
<string name="accessibility_app_usage_settings" msgid="6312864233673544149">"ऐप्लिकेशन इस्तेमाल की सेटिंग"</string>
<string name="recents_clear_all" msgid="5328176793634888831">"सभी ऐप्लिकेशन बंद करें"</string>
<string name="accessibility_recent_apps" msgid="4058661986695117371">"हाल ही में इस्तेमाल किए गए एेप्लिकेशन"</string>
+ <!-- no translation found for task_contents_description_with_remaining_time (4479688746574672685) -->
+ <skip />
+ <!-- no translation found for shorter_duration_less_than_one_minute (4722015666335015336) -->
+ <skip />
+ <!-- no translation found for app_in_grayscale (1108706002158384887) -->
+ <skip />
+ <!-- no translation found for time_left_for_app (3111996412933644358) -->
+ <skip />
</resources>
diff --git a/quickstep/res/values-hr/strings.xml b/quickstep/res/values-hr/strings.xml
index f7e0caa..213897c 100644
--- a/quickstep/res/values-hr/strings.xml
+++ b/quickstep/res/values-hr/strings.xml
@@ -28,4 +28,12 @@
<string name="accessibility_app_usage_settings" msgid="6312864233673544149">"Postavke upotrebe aplikacija"</string>
<string name="recents_clear_all" msgid="5328176793634888831">"Izbriši sve"</string>
<string name="accessibility_recent_apps" msgid="4058661986695117371">"Nedavne aplikacije"</string>
+ <!-- no translation found for task_contents_description_with_remaining_time (4479688746574672685) -->
+ <skip />
+ <!-- no translation found for shorter_duration_less_than_one_minute (4722015666335015336) -->
+ <skip />
+ <!-- no translation found for app_in_grayscale (1108706002158384887) -->
+ <skip />
+ <!-- no translation found for time_left_for_app (3111996412933644358) -->
+ <skip />
</resources>
diff --git a/quickstep/res/values-hu/strings.xml b/quickstep/res/values-hu/strings.xml
index c2f042c..40bea2a 100644
--- a/quickstep/res/values-hu/strings.xml
+++ b/quickstep/res/values-hu/strings.xml
@@ -28,4 +28,12 @@
<string name="accessibility_app_usage_settings" msgid="6312864233673544149">"Alkalmazáshasználati beállítások"</string>
<string name="recents_clear_all" msgid="5328176793634888831">"Összes törlése"</string>
<string name="accessibility_recent_apps" msgid="4058661986695117371">"Legutóbbi alkalmazások"</string>
+ <!-- no translation found for task_contents_description_with_remaining_time (4479688746574672685) -->
+ <skip />
+ <!-- no translation found for shorter_duration_less_than_one_minute (4722015666335015336) -->
+ <skip />
+ <!-- no translation found for app_in_grayscale (1108706002158384887) -->
+ <skip />
+ <!-- no translation found for time_left_for_app (3111996412933644358) -->
+ <skip />
</resources>
diff --git a/quickstep/res/values-hy/strings.xml b/quickstep/res/values-hy/strings.xml
index b8a12c9..7780fa8 100644
--- a/quickstep/res/values-hy/strings.xml
+++ b/quickstep/res/values-hy/strings.xml
@@ -28,4 +28,12 @@
<string name="accessibility_app_usage_settings" msgid="6312864233673544149">"Հավելվածի օգտագործման կարգավորումներ"</string>
<string name="recents_clear_all" msgid="5328176793634888831">"Փակել բոլորը"</string>
<string name="accessibility_recent_apps" msgid="4058661986695117371">"Վերջին օգտագործած հավելվածները"</string>
+ <!-- no translation found for task_contents_description_with_remaining_time (4479688746574672685) -->
+ <skip />
+ <!-- no translation found for shorter_duration_less_than_one_minute (4722015666335015336) -->
+ <skip />
+ <!-- no translation found for app_in_grayscale (1108706002158384887) -->
+ <skip />
+ <!-- no translation found for time_left_for_app (3111996412933644358) -->
+ <skip />
</resources>
diff --git a/quickstep/res/values-in/strings.xml b/quickstep/res/values-in/strings.xml
index 276c144..5bdb830 100644
--- a/quickstep/res/values-in/strings.xml
+++ b/quickstep/res/values-in/strings.xml
@@ -28,4 +28,12 @@
<string name="accessibility_app_usage_settings" msgid="6312864233673544149">"Setelan penggunaan aplikasi"</string>
<string name="recents_clear_all" msgid="5328176793634888831">"Hapus semua"</string>
<string name="accessibility_recent_apps" msgid="4058661986695117371">"Aplikasi baru-baru ini"</string>
+ <!-- no translation found for task_contents_description_with_remaining_time (4479688746574672685) -->
+ <skip />
+ <!-- no translation found for shorter_duration_less_than_one_minute (4722015666335015336) -->
+ <skip />
+ <!-- no translation found for app_in_grayscale (1108706002158384887) -->
+ <skip />
+ <!-- no translation found for time_left_for_app (3111996412933644358) -->
+ <skip />
</resources>
diff --git a/quickstep/res/values-is/strings.xml b/quickstep/res/values-is/strings.xml
index b4fb172..7c3bbd6 100644
--- a/quickstep/res/values-is/strings.xml
+++ b/quickstep/res/values-is/strings.xml
@@ -28,4 +28,12 @@
<string name="accessibility_app_usage_settings" msgid="6312864233673544149">"Notkunarstillingar forrits"</string>
<string name="recents_clear_all" msgid="5328176793634888831">"Hreinsa allt"</string>
<string name="accessibility_recent_apps" msgid="4058661986695117371">"Nýleg forrit"</string>
+ <!-- no translation found for task_contents_description_with_remaining_time (4479688746574672685) -->
+ <skip />
+ <!-- no translation found for shorter_duration_less_than_one_minute (4722015666335015336) -->
+ <skip />
+ <!-- no translation found for app_in_grayscale (1108706002158384887) -->
+ <skip />
+ <!-- no translation found for time_left_for_app (3111996412933644358) -->
+ <skip />
</resources>
diff --git a/quickstep/res/values-it/strings.xml b/quickstep/res/values-it/strings.xml
index c44cc01..314e768 100644
--- a/quickstep/res/values-it/strings.xml
+++ b/quickstep/res/values-it/strings.xml
@@ -28,4 +28,12 @@
<string name="accessibility_app_usage_settings" msgid="6312864233673544149">"Impostazioni di utilizzo delle app"</string>
<string name="recents_clear_all" msgid="5328176793634888831">"Cancella tutto"</string>
<string name="accessibility_recent_apps" msgid="4058661986695117371">"App recenti"</string>
+ <!-- no translation found for task_contents_description_with_remaining_time (4479688746574672685) -->
+ <skip />
+ <!-- no translation found for shorter_duration_less_than_one_minute (4722015666335015336) -->
+ <skip />
+ <!-- no translation found for app_in_grayscale (1108706002158384887) -->
+ <skip />
+ <!-- no translation found for time_left_for_app (3111996412933644358) -->
+ <skip />
</resources>
diff --git a/quickstep/res/values-iw/strings.xml b/quickstep/res/values-iw/strings.xml
index f7a0384..4b6a045 100644
--- a/quickstep/res/values-iw/strings.xml
+++ b/quickstep/res/values-iw/strings.xml
@@ -28,4 +28,12 @@
<string name="accessibility_app_usage_settings" msgid="6312864233673544149">"הגדרות שימוש באפליקציה"</string>
<string name="recents_clear_all" msgid="5328176793634888831">"ניקוי הכול"</string>
<string name="accessibility_recent_apps" msgid="4058661986695117371">"אפליקציות אחרונות"</string>
+ <!-- no translation found for task_contents_description_with_remaining_time (4479688746574672685) -->
+ <skip />
+ <!-- no translation found for shorter_duration_less_than_one_minute (4722015666335015336) -->
+ <skip />
+ <!-- no translation found for app_in_grayscale (1108706002158384887) -->
+ <skip />
+ <!-- no translation found for time_left_for_app (3111996412933644358) -->
+ <skip />
</resources>
diff --git a/quickstep/res/values-ja/strings.xml b/quickstep/res/values-ja/strings.xml
index fff8a0d..626cb9a 100644
--- a/quickstep/res/values-ja/strings.xml
+++ b/quickstep/res/values-ja/strings.xml
@@ -28,4 +28,12 @@
<string name="accessibility_app_usage_settings" msgid="6312864233673544149">"アプリの使用状況の設定"</string>
<string name="recents_clear_all" msgid="5328176793634888831">"すべてクリア"</string>
<string name="accessibility_recent_apps" msgid="4058661986695117371">"最近使ったアプリ"</string>
+ <!-- no translation found for task_contents_description_with_remaining_time (4479688746574672685) -->
+ <skip />
+ <!-- no translation found for shorter_duration_less_than_one_minute (4722015666335015336) -->
+ <skip />
+ <!-- no translation found for app_in_grayscale (1108706002158384887) -->
+ <skip />
+ <!-- no translation found for time_left_for_app (3111996412933644358) -->
+ <skip />
</resources>
diff --git a/quickstep/res/values-ka/strings.xml b/quickstep/res/values-ka/strings.xml
index cc0d594..a61d670 100644
--- a/quickstep/res/values-ka/strings.xml
+++ b/quickstep/res/values-ka/strings.xml
@@ -28,4 +28,12 @@
<string name="accessibility_app_usage_settings" msgid="6312864233673544149">"აპების გამოყენების პარამეტრები"</string>
<string name="recents_clear_all" msgid="5328176793634888831">"ყველას გასუფთავება"</string>
<string name="accessibility_recent_apps" msgid="4058661986695117371">"ბოლოდროინდელი აპები"</string>
+ <!-- no translation found for task_contents_description_with_remaining_time (4479688746574672685) -->
+ <skip />
+ <!-- no translation found for shorter_duration_less_than_one_minute (4722015666335015336) -->
+ <skip />
+ <!-- no translation found for app_in_grayscale (1108706002158384887) -->
+ <skip />
+ <!-- no translation found for time_left_for_app (3111996412933644358) -->
+ <skip />
</resources>
diff --git a/quickstep/res/values-kk/strings.xml b/quickstep/res/values-kk/strings.xml
index b0a1b4e..d3ada26 100644
--- a/quickstep/res/values-kk/strings.xml
+++ b/quickstep/res/values-kk/strings.xml
@@ -28,4 +28,12 @@
<string name="accessibility_app_usage_settings" msgid="6312864233673544149">"Қолданбаны пайдалану параметрлері"</string>
<string name="recents_clear_all" msgid="5328176793634888831">"Барлығын өшіру"</string>
<string name="accessibility_recent_apps" msgid="4058661986695117371">"Соңғы пайдаланылған қолданбалар"</string>
+ <!-- no translation found for task_contents_description_with_remaining_time (4479688746574672685) -->
+ <skip />
+ <!-- no translation found for shorter_duration_less_than_one_minute (4722015666335015336) -->
+ <skip />
+ <!-- no translation found for app_in_grayscale (1108706002158384887) -->
+ <skip />
+ <!-- no translation found for time_left_for_app (3111996412933644358) -->
+ <skip />
</resources>
diff --git a/quickstep/res/values-km/strings.xml b/quickstep/res/values-km/strings.xml
index 7aa407c..d4d6bfc 100644
--- a/quickstep/res/values-km/strings.xml
+++ b/quickstep/res/values-km/strings.xml
@@ -28,4 +28,12 @@
<string name="accessibility_app_usage_settings" msgid="6312864233673544149">"ការកំណត់ការប្រើប្រាស់កម្មវិធី"</string>
<string name="recents_clear_all" msgid="5328176793634888831">"សម្អាតទាំងអស់"</string>
<string name="accessibility_recent_apps" msgid="4058661986695117371">"កម្មវិធីថ្មីៗ"</string>
+ <!-- no translation found for task_contents_description_with_remaining_time (4479688746574672685) -->
+ <skip />
+ <!-- no translation found for shorter_duration_less_than_one_minute (4722015666335015336) -->
+ <skip />
+ <!-- no translation found for app_in_grayscale (1108706002158384887) -->
+ <skip />
+ <!-- no translation found for time_left_for_app (3111996412933644358) -->
+ <skip />
</resources>
diff --git a/quickstep/res/values-kn/strings.xml b/quickstep/res/values-kn/strings.xml
index e5ac0df..bfd53ba 100644
--- a/quickstep/res/values-kn/strings.xml
+++ b/quickstep/res/values-kn/strings.xml
@@ -28,4 +28,12 @@
<string name="accessibility_app_usage_settings" msgid="6312864233673544149">"ಆ್ಯಪ್ ಬಳಕೆಯ ಸೆಟ್ಟಿಂಗ್ಗಳು"</string>
<string name="recents_clear_all" msgid="5328176793634888831">"ಎಲ್ಲವನ್ನೂ ತೆರವುಗೊಳಿಸಿ"</string>
<string name="accessibility_recent_apps" msgid="4058661986695117371">"ಇತ್ತೀಚಿನ ಅಪ್ಲಿಕೇಶನ್ಗಳು"</string>
+ <!-- no translation found for task_contents_description_with_remaining_time (4479688746574672685) -->
+ <skip />
+ <!-- no translation found for shorter_duration_less_than_one_minute (4722015666335015336) -->
+ <skip />
+ <!-- no translation found for app_in_grayscale (1108706002158384887) -->
+ <skip />
+ <!-- no translation found for time_left_for_app (3111996412933644358) -->
+ <skip />
</resources>
diff --git a/quickstep/res/values-ko/strings.xml b/quickstep/res/values-ko/strings.xml
index 8e7dcb1..1f49b01 100644
--- a/quickstep/res/values-ko/strings.xml
+++ b/quickstep/res/values-ko/strings.xml
@@ -28,4 +28,12 @@
<string name="accessibility_app_usage_settings" msgid="6312864233673544149">"앱 사용 설정"</string>
<string name="recents_clear_all" msgid="5328176793634888831">"모두 삭제"</string>
<string name="accessibility_recent_apps" msgid="4058661986695117371">"최근 앱"</string>
+ <!-- no translation found for task_contents_description_with_remaining_time (4479688746574672685) -->
+ <skip />
+ <!-- no translation found for shorter_duration_less_than_one_minute (4722015666335015336) -->
+ <skip />
+ <!-- no translation found for app_in_grayscale (1108706002158384887) -->
+ <skip />
+ <!-- no translation found for time_left_for_app (3111996412933644358) -->
+ <skip />
</resources>
diff --git a/quickstep/res/values-ky/strings.xml b/quickstep/res/values-ky/strings.xml
index 13d8399..25a9c3c 100644
--- a/quickstep/res/values-ky/strings.xml
+++ b/quickstep/res/values-ky/strings.xml
@@ -28,4 +28,12 @@
<string name="accessibility_app_usage_settings" msgid="6312864233673544149">"Колдонмону пайдалануу жөндөөлөрү"</string>
<string name="recents_clear_all" msgid="5328176793634888831">"Баарын тазалоо"</string>
<string name="accessibility_recent_apps" msgid="4058661986695117371">"Акыркы колдонмолор"</string>
+ <!-- no translation found for task_contents_description_with_remaining_time (4479688746574672685) -->
+ <skip />
+ <!-- no translation found for shorter_duration_less_than_one_minute (4722015666335015336) -->
+ <skip />
+ <!-- no translation found for app_in_grayscale (1108706002158384887) -->
+ <skip />
+ <!-- no translation found for time_left_for_app (3111996412933644358) -->
+ <skip />
</resources>
diff --git a/quickstep/res/values-lo/strings.xml b/quickstep/res/values-lo/strings.xml
index 622118c..26d53c9 100644
--- a/quickstep/res/values-lo/strings.xml
+++ b/quickstep/res/values-lo/strings.xml
@@ -28,4 +28,12 @@
<string name="accessibility_app_usage_settings" msgid="6312864233673544149">"ການຕັ້ງຄ່າການນຳໃຊ້ແອັບ"</string>
<string name="recents_clear_all" msgid="5328176793634888831">"ລຶບລ້າງທັງໝົດ"</string>
<string name="accessibility_recent_apps" msgid="4058661986695117371">"ແອັບຫຼ້າສຸດ"</string>
+ <!-- no translation found for task_contents_description_with_remaining_time (4479688746574672685) -->
+ <skip />
+ <!-- no translation found for shorter_duration_less_than_one_minute (4722015666335015336) -->
+ <skip />
+ <!-- no translation found for app_in_grayscale (1108706002158384887) -->
+ <skip />
+ <!-- no translation found for time_left_for_app (3111996412933644358) -->
+ <skip />
</resources>
diff --git a/quickstep/res/values-lt/strings.xml b/quickstep/res/values-lt/strings.xml
index 42e4fe8..49635c8 100644
--- a/quickstep/res/values-lt/strings.xml
+++ b/quickstep/res/values-lt/strings.xml
@@ -28,4 +28,12 @@
<string name="accessibility_app_usage_settings" msgid="6312864233673544149">"Programos naudojimo nustatymai"</string>
<string name="recents_clear_all" msgid="5328176793634888831">"Išvalyti viską"</string>
<string name="accessibility_recent_apps" msgid="4058661986695117371">"Naujausios programos"</string>
+ <!-- no translation found for task_contents_description_with_remaining_time (4479688746574672685) -->
+ <skip />
+ <!-- no translation found for shorter_duration_less_than_one_minute (4722015666335015336) -->
+ <skip />
+ <!-- no translation found for app_in_grayscale (1108706002158384887) -->
+ <skip />
+ <!-- no translation found for time_left_for_app (3111996412933644358) -->
+ <skip />
</resources>
diff --git a/quickstep/res/values-lv/strings.xml b/quickstep/res/values-lv/strings.xml
index c249928..a25b55c 100644
--- a/quickstep/res/values-lv/strings.xml
+++ b/quickstep/res/values-lv/strings.xml
@@ -28,4 +28,12 @@
<string name="accessibility_app_usage_settings" msgid="6312864233673544149">"Lietotņu izmantošanas iestatījumi"</string>
<string name="recents_clear_all" msgid="5328176793634888831">"Notīrīt visu"</string>
<string name="accessibility_recent_apps" msgid="4058661986695117371">"Pēdējās izmantotās lietotnes"</string>
+ <!-- no translation found for task_contents_description_with_remaining_time (4479688746574672685) -->
+ <skip />
+ <!-- no translation found for shorter_duration_less_than_one_minute (4722015666335015336) -->
+ <skip />
+ <!-- no translation found for app_in_grayscale (1108706002158384887) -->
+ <skip />
+ <!-- no translation found for time_left_for_app (3111996412933644358) -->
+ <skip />
</resources>
diff --git a/quickstep/res/values-mk/strings.xml b/quickstep/res/values-mk/strings.xml
index fa4e401..24e1d13 100644
--- a/quickstep/res/values-mk/strings.xml
+++ b/quickstep/res/values-mk/strings.xml
@@ -28,4 +28,12 @@
<string name="accessibility_app_usage_settings" msgid="6312864233673544149">"Поставки за користење на апликациите"</string>
<string name="recents_clear_all" msgid="5328176793634888831">"Исчисти ги сите"</string>
<string name="accessibility_recent_apps" msgid="4058661986695117371">"Неодамнешни апликации"</string>
+ <!-- no translation found for task_contents_description_with_remaining_time (4479688746574672685) -->
+ <skip />
+ <!-- no translation found for shorter_duration_less_than_one_minute (4722015666335015336) -->
+ <skip />
+ <!-- no translation found for app_in_grayscale (1108706002158384887) -->
+ <skip />
+ <!-- no translation found for time_left_for_app (3111996412933644358) -->
+ <skip />
</resources>
diff --git a/quickstep/res/values-ml/strings.xml b/quickstep/res/values-ml/strings.xml
index 4be84c8..d76ef41 100644
--- a/quickstep/res/values-ml/strings.xml
+++ b/quickstep/res/values-ml/strings.xml
@@ -28,4 +28,12 @@
<string name="accessibility_app_usage_settings" msgid="6312864233673544149">"ആപ്പ് ഉപയോഗ ക്രമീകരണം"</string>
<string name="recents_clear_all" msgid="5328176793634888831">"എല്ലാം മായ്ക്കുക"</string>
<string name="accessibility_recent_apps" msgid="4058661986695117371">"സമീപകാല ആപ്പുകൾ"</string>
+ <!-- no translation found for task_contents_description_with_remaining_time (4479688746574672685) -->
+ <skip />
+ <!-- no translation found for shorter_duration_less_than_one_minute (4722015666335015336) -->
+ <skip />
+ <!-- no translation found for app_in_grayscale (1108706002158384887) -->
+ <skip />
+ <!-- no translation found for time_left_for_app (3111996412933644358) -->
+ <skip />
</resources>
diff --git a/quickstep/res/values-mn/strings.xml b/quickstep/res/values-mn/strings.xml
index b3e3d7f..53ff7bb 100644
--- a/quickstep/res/values-mn/strings.xml
+++ b/quickstep/res/values-mn/strings.xml
@@ -28,4 +28,12 @@
<string name="accessibility_app_usage_settings" msgid="6312864233673544149">"Апп ашиглалтын тохиргоо"</string>
<string name="recents_clear_all" msgid="5328176793634888831">"Бүгдийг устгах"</string>
<string name="accessibility_recent_apps" msgid="4058661986695117371">"Саяхны аппууд"</string>
+ <!-- no translation found for task_contents_description_with_remaining_time (4479688746574672685) -->
+ <skip />
+ <!-- no translation found for shorter_duration_less_than_one_minute (4722015666335015336) -->
+ <skip />
+ <!-- no translation found for app_in_grayscale (1108706002158384887) -->
+ <skip />
+ <!-- no translation found for time_left_for_app (3111996412933644358) -->
+ <skip />
</resources>
diff --git a/quickstep/res/values-mr/strings.xml b/quickstep/res/values-mr/strings.xml
index fa8574a..8e18643 100644
--- a/quickstep/res/values-mr/strings.xml
+++ b/quickstep/res/values-mr/strings.xml
@@ -28,4 +28,12 @@
<string name="accessibility_app_usage_settings" msgid="6312864233673544149">"अॅप वापर सेटिंग्ज"</string>
<string name="recents_clear_all" msgid="5328176793634888831">"सर्व साफ करा"</string>
<string name="accessibility_recent_apps" msgid="4058661986695117371">"अलीकडील अॅप्स"</string>
+ <!-- no translation found for task_contents_description_with_remaining_time (4479688746574672685) -->
+ <skip />
+ <!-- no translation found for shorter_duration_less_than_one_minute (4722015666335015336) -->
+ <skip />
+ <!-- no translation found for app_in_grayscale (1108706002158384887) -->
+ <skip />
+ <!-- no translation found for time_left_for_app (3111996412933644358) -->
+ <skip />
</resources>
diff --git a/quickstep/res/values-ms/strings.xml b/quickstep/res/values-ms/strings.xml
index 975e127..e390820 100644
--- a/quickstep/res/values-ms/strings.xml
+++ b/quickstep/res/values-ms/strings.xml
@@ -28,4 +28,12 @@
<string name="accessibility_app_usage_settings" msgid="6312864233673544149">"Tetapan penggunaan apl"</string>
<string name="recents_clear_all" msgid="5328176793634888831">"Kosongkan semua"</string>
<string name="accessibility_recent_apps" msgid="4058661986695117371">"Apl terbaharu"</string>
+ <!-- no translation found for task_contents_description_with_remaining_time (4479688746574672685) -->
+ <skip />
+ <!-- no translation found for shorter_duration_less_than_one_minute (4722015666335015336) -->
+ <skip />
+ <!-- no translation found for app_in_grayscale (1108706002158384887) -->
+ <skip />
+ <!-- no translation found for time_left_for_app (3111996412933644358) -->
+ <skip />
</resources>
diff --git a/quickstep/res/values-my/strings.xml b/quickstep/res/values-my/strings.xml
index 52d4dae..45a052f 100644
--- a/quickstep/res/values-my/strings.xml
+++ b/quickstep/res/values-my/strings.xml
@@ -28,4 +28,12 @@
<string name="accessibility_app_usage_settings" msgid="6312864233673544149">"အက်ပ်အသုံးပြုမှု ဆက်တင်များ"</string>
<string name="recents_clear_all" msgid="5328176793634888831">"အားလုံးကို ရှင်းရန်"</string>
<string name="accessibility_recent_apps" msgid="4058661986695117371">"လတ်တလောသုံး အက်ပ်များ"</string>
+ <!-- no translation found for task_contents_description_with_remaining_time (4479688746574672685) -->
+ <skip />
+ <!-- no translation found for shorter_duration_less_than_one_minute (4722015666335015336) -->
+ <skip />
+ <!-- no translation found for app_in_grayscale (1108706002158384887) -->
+ <skip />
+ <!-- no translation found for time_left_for_app (3111996412933644358) -->
+ <skip />
</resources>
diff --git a/quickstep/res/values-nb/strings.xml b/quickstep/res/values-nb/strings.xml
index c681a1e..08b5093 100644
--- a/quickstep/res/values-nb/strings.xml
+++ b/quickstep/res/values-nb/strings.xml
@@ -28,4 +28,12 @@
<string name="accessibility_app_usage_settings" msgid="6312864233673544149">"Innstillinger for appbruk"</string>
<string name="recents_clear_all" msgid="5328176793634888831">"Fjern alt"</string>
<string name="accessibility_recent_apps" msgid="4058661986695117371">"Nylige apper"</string>
+ <!-- no translation found for task_contents_description_with_remaining_time (4479688746574672685) -->
+ <skip />
+ <!-- no translation found for shorter_duration_less_than_one_minute (4722015666335015336) -->
+ <skip />
+ <!-- no translation found for app_in_grayscale (1108706002158384887) -->
+ <skip />
+ <!-- no translation found for time_left_for_app (3111996412933644358) -->
+ <skip />
</resources>
diff --git a/quickstep/res/values-ne/strings.xml b/quickstep/res/values-ne/strings.xml
index 46e0eaf..25da447 100644
--- a/quickstep/res/values-ne/strings.xml
+++ b/quickstep/res/values-ne/strings.xml
@@ -28,4 +28,12 @@
<string name="accessibility_app_usage_settings" msgid="6312864233673544149">"अनुप्रयोगको उपयोगका सेटिङहरू"</string>
<string name="recents_clear_all" msgid="5328176793634888831">"सबै खाली गर्नुहोस्"</string>
<string name="accessibility_recent_apps" msgid="4058661986695117371">"हालसालैका अनुप्रयोगहरू"</string>
+ <!-- no translation found for task_contents_description_with_remaining_time (4479688746574672685) -->
+ <skip />
+ <!-- no translation found for shorter_duration_less_than_one_minute (4722015666335015336) -->
+ <skip />
+ <!-- no translation found for app_in_grayscale (1108706002158384887) -->
+ <skip />
+ <!-- no translation found for time_left_for_app (3111996412933644358) -->
+ <skip />
</resources>
diff --git a/quickstep/res/values-nl/strings.xml b/quickstep/res/values-nl/strings.xml
index dbbd2ec..2dbd58b 100644
--- a/quickstep/res/values-nl/strings.xml
+++ b/quickstep/res/values-nl/strings.xml
@@ -28,4 +28,12 @@
<string name="accessibility_app_usage_settings" msgid="6312864233673544149">"Instellingen voor app-gebruik"</string>
<string name="recents_clear_all" msgid="5328176793634888831">"Alles wissen"</string>
<string name="accessibility_recent_apps" msgid="4058661986695117371">"Recente apps"</string>
+ <!-- no translation found for task_contents_description_with_remaining_time (4479688746574672685) -->
+ <skip />
+ <!-- no translation found for shorter_duration_less_than_one_minute (4722015666335015336) -->
+ <skip />
+ <!-- no translation found for app_in_grayscale (1108706002158384887) -->
+ <skip />
+ <!-- no translation found for time_left_for_app (3111996412933644358) -->
+ <skip />
</resources>
diff --git a/quickstep/res/values-or/strings.xml b/quickstep/res/values-or/strings.xml
index 60f7823..a15196f 100644
--- a/quickstep/res/values-or/strings.xml
+++ b/quickstep/res/values-or/strings.xml
@@ -28,4 +28,12 @@
<string name="accessibility_app_usage_settings" msgid="6312864233673544149">"ଆପ୍ ବ୍ୟବହାର ସେଟିଂସ୍"</string>
<string name="recents_clear_all" msgid="5328176793634888831">"ସବୁ ଖାଲି କରନ୍ତୁ"</string>
<string name="accessibility_recent_apps" msgid="4058661986695117371">"ସାମ୍ପ୍ରତିକ ଆପ୍"</string>
+ <!-- no translation found for task_contents_description_with_remaining_time (4479688746574672685) -->
+ <skip />
+ <!-- no translation found for shorter_duration_less_than_one_minute (4722015666335015336) -->
+ <skip />
+ <!-- no translation found for app_in_grayscale (1108706002158384887) -->
+ <skip />
+ <!-- no translation found for time_left_for_app (3111996412933644358) -->
+ <skip />
</resources>
diff --git a/quickstep/res/values-pa/strings.xml b/quickstep/res/values-pa/strings.xml
index 18b0468..e9d7300 100644
--- a/quickstep/res/values-pa/strings.xml
+++ b/quickstep/res/values-pa/strings.xml
@@ -28,4 +28,12 @@
<string name="accessibility_app_usage_settings" msgid="6312864233673544149">"ਐਪ ਵਰਤੋਂ ਦੀਆਂ ਸੈਟਿੰਗਾਂ"</string>
<string name="recents_clear_all" msgid="5328176793634888831">"ਸਭ ਕਲੀਅਰ ਕਰੋ"</string>
<string name="accessibility_recent_apps" msgid="4058661986695117371">"ਹਾਲੀਆ ਐਪਾਂ"</string>
+ <!-- no translation found for task_contents_description_with_remaining_time (4479688746574672685) -->
+ <skip />
+ <!-- no translation found for shorter_duration_less_than_one_minute (4722015666335015336) -->
+ <skip />
+ <!-- no translation found for app_in_grayscale (1108706002158384887) -->
+ <skip />
+ <!-- no translation found for time_left_for_app (3111996412933644358) -->
+ <skip />
</resources>
diff --git a/quickstep/res/values-pl/strings.xml b/quickstep/res/values-pl/strings.xml
index 447cd60..b1c3f25 100644
--- a/quickstep/res/values-pl/strings.xml
+++ b/quickstep/res/values-pl/strings.xml
@@ -28,4 +28,12 @@
<string name="accessibility_app_usage_settings" msgid="6312864233673544149">"Ustawienia użycia aplikacji"</string>
<string name="recents_clear_all" msgid="5328176793634888831">"Wyczyść wszystko"</string>
<string name="accessibility_recent_apps" msgid="4058661986695117371">"Ostatnie aplikacje"</string>
+ <!-- no translation found for task_contents_description_with_remaining_time (4479688746574672685) -->
+ <skip />
+ <!-- no translation found for shorter_duration_less_than_one_minute (4722015666335015336) -->
+ <skip />
+ <!-- no translation found for app_in_grayscale (1108706002158384887) -->
+ <skip />
+ <!-- no translation found for time_left_for_app (3111996412933644358) -->
+ <skip />
</resources>
diff --git a/quickstep/res/values-pt-rPT/strings.xml b/quickstep/res/values-pt-rPT/strings.xml
index e7dfedf..153c34a 100644
--- a/quickstep/res/values-pt-rPT/strings.xml
+++ b/quickstep/res/values-pt-rPT/strings.xml
@@ -28,4 +28,12 @@
<string name="accessibility_app_usage_settings" msgid="6312864233673544149">"Definições de utilização de aplicações"</string>
<string name="recents_clear_all" msgid="5328176793634888831">"Limpar tudo"</string>
<string name="accessibility_recent_apps" msgid="4058661986695117371">"Aplicações recentes"</string>
+ <!-- no translation found for task_contents_description_with_remaining_time (4479688746574672685) -->
+ <skip />
+ <!-- no translation found for shorter_duration_less_than_one_minute (4722015666335015336) -->
+ <skip />
+ <!-- no translation found for app_in_grayscale (1108706002158384887) -->
+ <skip />
+ <!-- no translation found for time_left_for_app (3111996412933644358) -->
+ <skip />
</resources>
diff --git a/quickstep/res/values-pt/strings.xml b/quickstep/res/values-pt/strings.xml
index 5ebd0bd..24e56c2 100644
--- a/quickstep/res/values-pt/strings.xml
+++ b/quickstep/res/values-pt/strings.xml
@@ -28,4 +28,12 @@
<string name="accessibility_app_usage_settings" msgid="6312864233673544149">"Configurações de uso do app"</string>
<string name="recents_clear_all" msgid="5328176793634888831">"Limpar tudo"</string>
<string name="accessibility_recent_apps" msgid="4058661986695117371">"Apps recentes"</string>
+ <!-- no translation found for task_contents_description_with_remaining_time (4479688746574672685) -->
+ <skip />
+ <!-- no translation found for shorter_duration_less_than_one_minute (4722015666335015336) -->
+ <skip />
+ <!-- no translation found for app_in_grayscale (1108706002158384887) -->
+ <skip />
+ <!-- no translation found for time_left_for_app (3111996412933644358) -->
+ <skip />
</resources>
diff --git a/quickstep/res/values-ro/strings.xml b/quickstep/res/values-ro/strings.xml
index 5d9887d..8e697eb 100644
--- a/quickstep/res/values-ro/strings.xml
+++ b/quickstep/res/values-ro/strings.xml
@@ -28,4 +28,12 @@
<string name="accessibility_app_usage_settings" msgid="6312864233673544149">"Setări de utilizare a aplicației"</string>
<string name="recents_clear_all" msgid="5328176793634888831">"Ștergeți tot"</string>
<string name="accessibility_recent_apps" msgid="4058661986695117371">"Aplicații recente"</string>
+ <!-- no translation found for task_contents_description_with_remaining_time (4479688746574672685) -->
+ <skip />
+ <!-- no translation found for shorter_duration_less_than_one_minute (4722015666335015336) -->
+ <skip />
+ <!-- no translation found for app_in_grayscale (1108706002158384887) -->
+ <skip />
+ <!-- no translation found for time_left_for_app (3111996412933644358) -->
+ <skip />
</resources>
diff --git a/quickstep/res/values-ru/strings.xml b/quickstep/res/values-ru/strings.xml
index 992683c..0dc5ba1 100644
--- a/quickstep/res/values-ru/strings.xml
+++ b/quickstep/res/values-ru/strings.xml
@@ -28,4 +28,12 @@
<string name="accessibility_app_usage_settings" msgid="6312864233673544149">"Настройки использования приложения"</string>
<string name="recents_clear_all" msgid="5328176793634888831">"Очистить все"</string>
<string name="accessibility_recent_apps" msgid="4058661986695117371">"Недавние приложения"</string>
+ <!-- no translation found for task_contents_description_with_remaining_time (4479688746574672685) -->
+ <skip />
+ <!-- no translation found for shorter_duration_less_than_one_minute (4722015666335015336) -->
+ <skip />
+ <!-- no translation found for app_in_grayscale (1108706002158384887) -->
+ <skip />
+ <!-- no translation found for time_left_for_app (3111996412933644358) -->
+ <skip />
</resources>
diff --git a/quickstep/res/values-si/strings.xml b/quickstep/res/values-si/strings.xml
index 050a765..614ca2b 100644
--- a/quickstep/res/values-si/strings.xml
+++ b/quickstep/res/values-si/strings.xml
@@ -28,4 +28,12 @@
<string name="accessibility_app_usage_settings" msgid="6312864233673544149">"යෙදුම් භාවිත සැකසීම්"</string>
<string name="recents_clear_all" msgid="5328176793634888831">"සියල්ල හිස් කරන්න"</string>
<string name="accessibility_recent_apps" msgid="4058661986695117371">"මෑත යෙදුම්"</string>
+ <!-- no translation found for task_contents_description_with_remaining_time (4479688746574672685) -->
+ <skip />
+ <!-- no translation found for shorter_duration_less_than_one_minute (4722015666335015336) -->
+ <skip />
+ <!-- no translation found for app_in_grayscale (1108706002158384887) -->
+ <skip />
+ <!-- no translation found for time_left_for_app (3111996412933644358) -->
+ <skip />
</resources>
diff --git a/quickstep/res/values-sk/strings.xml b/quickstep/res/values-sk/strings.xml
index 510220b..2766c0c 100644
--- a/quickstep/res/values-sk/strings.xml
+++ b/quickstep/res/values-sk/strings.xml
@@ -28,4 +28,12 @@
<string name="accessibility_app_usage_settings" msgid="6312864233673544149">"Nastavenia využívania aplikácie"</string>
<string name="recents_clear_all" msgid="5328176793634888831">"Vymazať všetko"</string>
<string name="accessibility_recent_apps" msgid="4058661986695117371">"Nedávne aplikácie"</string>
+ <!-- no translation found for task_contents_description_with_remaining_time (4479688746574672685) -->
+ <skip />
+ <!-- no translation found for shorter_duration_less_than_one_minute (4722015666335015336) -->
+ <skip />
+ <!-- no translation found for app_in_grayscale (1108706002158384887) -->
+ <skip />
+ <!-- no translation found for time_left_for_app (3111996412933644358) -->
+ <skip />
</resources>
diff --git a/quickstep/res/values-sl/strings.xml b/quickstep/res/values-sl/strings.xml
index 29d1eec..115bcf8 100644
--- a/quickstep/res/values-sl/strings.xml
+++ b/quickstep/res/values-sl/strings.xml
@@ -28,4 +28,12 @@
<string name="accessibility_app_usage_settings" msgid="6312864233673544149">"Nastavitve uporabe aplikacij"</string>
<string name="recents_clear_all" msgid="5328176793634888831">"Počisti vse"</string>
<string name="accessibility_recent_apps" msgid="4058661986695117371">"Nedavne aplikacije"</string>
+ <!-- no translation found for task_contents_description_with_remaining_time (4479688746574672685) -->
+ <skip />
+ <!-- no translation found for shorter_duration_less_than_one_minute (4722015666335015336) -->
+ <skip />
+ <!-- no translation found for app_in_grayscale (1108706002158384887) -->
+ <skip />
+ <!-- no translation found for time_left_for_app (3111996412933644358) -->
+ <skip />
</resources>
diff --git a/quickstep/res/values-sq/strings.xml b/quickstep/res/values-sq/strings.xml
index f2c955c..7107dcc 100644
--- a/quickstep/res/values-sq/strings.xml
+++ b/quickstep/res/values-sq/strings.xml
@@ -28,4 +28,12 @@
<string name="accessibility_app_usage_settings" msgid="6312864233673544149">"Cilësimet e përdorimit të aplikacionit"</string>
<string name="recents_clear_all" msgid="5328176793634888831">"Pastroji të gjitha"</string>
<string name="accessibility_recent_apps" msgid="4058661986695117371">"Aplikacionet e fundit"</string>
+ <!-- no translation found for task_contents_description_with_remaining_time (4479688746574672685) -->
+ <skip />
+ <!-- no translation found for shorter_duration_less_than_one_minute (4722015666335015336) -->
+ <skip />
+ <!-- no translation found for app_in_grayscale (1108706002158384887) -->
+ <skip />
+ <!-- no translation found for time_left_for_app (3111996412933644358) -->
+ <skip />
</resources>
diff --git a/quickstep/res/values-sr/strings.xml b/quickstep/res/values-sr/strings.xml
index e3837e7..3f87bab 100644
--- a/quickstep/res/values-sr/strings.xml
+++ b/quickstep/res/values-sr/strings.xml
@@ -28,4 +28,12 @@
<string name="accessibility_app_usage_settings" msgid="6312864233673544149">"Подешавања коришћења апликације"</string>
<string name="recents_clear_all" msgid="5328176793634888831">"Обриши све"</string>
<string name="accessibility_recent_apps" msgid="4058661986695117371">"Недавне апликације"</string>
+ <!-- no translation found for task_contents_description_with_remaining_time (4479688746574672685) -->
+ <skip />
+ <!-- no translation found for shorter_duration_less_than_one_minute (4722015666335015336) -->
+ <skip />
+ <!-- no translation found for app_in_grayscale (1108706002158384887) -->
+ <skip />
+ <!-- no translation found for time_left_for_app (3111996412933644358) -->
+ <skip />
</resources>
diff --git a/quickstep/res/values-sv/strings.xml b/quickstep/res/values-sv/strings.xml
index 4eda58c..cff4362 100644
--- a/quickstep/res/values-sv/strings.xml
+++ b/quickstep/res/values-sv/strings.xml
@@ -28,4 +28,12 @@
<string name="accessibility_app_usage_settings" msgid="6312864233673544149">"Inställningar för appanvändning"</string>
<string name="recents_clear_all" msgid="5328176793634888831">"Rensa alla"</string>
<string name="accessibility_recent_apps" msgid="4058661986695117371">"Senaste apparna"</string>
+ <!-- no translation found for task_contents_description_with_remaining_time (4479688746574672685) -->
+ <skip />
+ <!-- no translation found for shorter_duration_less_than_one_minute (4722015666335015336) -->
+ <skip />
+ <!-- no translation found for app_in_grayscale (1108706002158384887) -->
+ <skip />
+ <!-- no translation found for time_left_for_app (3111996412933644358) -->
+ <skip />
</resources>
diff --git a/quickstep/res/values-sw/strings.xml b/quickstep/res/values-sw/strings.xml
index 8888fef..eebd64b 100644
--- a/quickstep/res/values-sw/strings.xml
+++ b/quickstep/res/values-sw/strings.xml
@@ -28,4 +28,12 @@
<string name="accessibility_app_usage_settings" msgid="6312864233673544149">"Mipangilio ya matumizi ya programu"</string>
<string name="recents_clear_all" msgid="5328176793634888831">"Ondoa zote"</string>
<string name="accessibility_recent_apps" msgid="4058661986695117371">"Programu za hivi karibuni"</string>
+ <!-- no translation found for task_contents_description_with_remaining_time (4479688746574672685) -->
+ <skip />
+ <!-- no translation found for shorter_duration_less_than_one_minute (4722015666335015336) -->
+ <skip />
+ <!-- no translation found for app_in_grayscale (1108706002158384887) -->
+ <skip />
+ <!-- no translation found for time_left_for_app (3111996412933644358) -->
+ <skip />
</resources>
diff --git a/quickstep/res/values-ta/strings.xml b/quickstep/res/values-ta/strings.xml
index 5187564..d8e0177 100644
--- a/quickstep/res/values-ta/strings.xml
+++ b/quickstep/res/values-ta/strings.xml
@@ -28,4 +28,12 @@
<string name="accessibility_app_usage_settings" msgid="6312864233673544149">"ஆப்ஸ் உபயோக அமைப்புகள்"</string>
<string name="recents_clear_all" msgid="5328176793634888831">"எல்லாம் அழி"</string>
<string name="accessibility_recent_apps" msgid="4058661986695117371">"சமீபத்திய ஆப்ஸ்"</string>
+ <!-- no translation found for task_contents_description_with_remaining_time (4479688746574672685) -->
+ <skip />
+ <!-- no translation found for shorter_duration_less_than_one_minute (4722015666335015336) -->
+ <skip />
+ <!-- no translation found for app_in_grayscale (1108706002158384887) -->
+ <skip />
+ <!-- no translation found for time_left_for_app (3111996412933644358) -->
+ <skip />
</resources>
diff --git a/quickstep/res/values-te/strings.xml b/quickstep/res/values-te/strings.xml
index ba9a558..77c3a40 100644
--- a/quickstep/res/values-te/strings.xml
+++ b/quickstep/res/values-te/strings.xml
@@ -28,4 +28,12 @@
<string name="accessibility_app_usage_settings" msgid="6312864233673544149">"యాప్ వినియోగ సెట్టింగ్లు"</string>
<string name="recents_clear_all" msgid="5328176793634888831">"అన్నీ తీసివేయండి"</string>
<string name="accessibility_recent_apps" msgid="4058661986695117371">"ఇటీవలి యాప్లు"</string>
+ <!-- no translation found for task_contents_description_with_remaining_time (4479688746574672685) -->
+ <skip />
+ <!-- no translation found for shorter_duration_less_than_one_minute (4722015666335015336) -->
+ <skip />
+ <!-- no translation found for app_in_grayscale (1108706002158384887) -->
+ <skip />
+ <!-- no translation found for time_left_for_app (3111996412933644358) -->
+ <skip />
</resources>
diff --git a/quickstep/res/values-th/strings.xml b/quickstep/res/values-th/strings.xml
index c3a9a81..2d49b8a 100644
--- a/quickstep/res/values-th/strings.xml
+++ b/quickstep/res/values-th/strings.xml
@@ -28,4 +28,12 @@
<string name="accessibility_app_usage_settings" msgid="6312864233673544149">"การตั้งค่าการใช้แอป"</string>
<string name="recents_clear_all" msgid="5328176793634888831">"ล้างทั้งหมด"</string>
<string name="accessibility_recent_apps" msgid="4058661986695117371">"แอปล่าสุด"</string>
+ <!-- no translation found for task_contents_description_with_remaining_time (4479688746574672685) -->
+ <skip />
+ <!-- no translation found for shorter_duration_less_than_one_minute (4722015666335015336) -->
+ <skip />
+ <!-- no translation found for app_in_grayscale (1108706002158384887) -->
+ <skip />
+ <!-- no translation found for time_left_for_app (3111996412933644358) -->
+ <skip />
</resources>
diff --git a/quickstep/res/values-tl/strings.xml b/quickstep/res/values-tl/strings.xml
index 6ceaf21..da5eb8b 100644
--- a/quickstep/res/values-tl/strings.xml
+++ b/quickstep/res/values-tl/strings.xml
@@ -28,4 +28,12 @@
<string name="accessibility_app_usage_settings" msgid="6312864233673544149">"Mga setting ng paggamit ng app"</string>
<string name="recents_clear_all" msgid="5328176793634888831">"I-clear lahat"</string>
<string name="accessibility_recent_apps" msgid="4058661986695117371">"Mga kamakailang app"</string>
+ <!-- no translation found for task_contents_description_with_remaining_time (4479688746574672685) -->
+ <skip />
+ <!-- no translation found for shorter_duration_less_than_one_minute (4722015666335015336) -->
+ <skip />
+ <!-- no translation found for app_in_grayscale (1108706002158384887) -->
+ <skip />
+ <!-- no translation found for time_left_for_app (3111996412933644358) -->
+ <skip />
</resources>
diff --git a/quickstep/res/values-tr/strings.xml b/quickstep/res/values-tr/strings.xml
index cbebb26..a3c665a 100644
--- a/quickstep/res/values-tr/strings.xml
+++ b/quickstep/res/values-tr/strings.xml
@@ -28,4 +28,12 @@
<string name="accessibility_app_usage_settings" msgid="6312864233673544149">"Uygulama kullanım ayarları"</string>
<string name="recents_clear_all" msgid="5328176793634888831">"Tümünü temizle"</string>
<string name="accessibility_recent_apps" msgid="4058661986695117371">"Son uygulamalar"</string>
+ <!-- no translation found for task_contents_description_with_remaining_time (4479688746574672685) -->
+ <skip />
+ <!-- no translation found for shorter_duration_less_than_one_minute (4722015666335015336) -->
+ <skip />
+ <!-- no translation found for app_in_grayscale (1108706002158384887) -->
+ <skip />
+ <!-- no translation found for time_left_for_app (3111996412933644358) -->
+ <skip />
</resources>
diff --git a/quickstep/res/values-uk/strings.xml b/quickstep/res/values-uk/strings.xml
index b9666b2..956a16f 100644
--- a/quickstep/res/values-uk/strings.xml
+++ b/quickstep/res/values-uk/strings.xml
@@ -28,4 +28,12 @@
<string name="accessibility_app_usage_settings" msgid="6312864233673544149">"Налаштування використання додатка"</string>
<string name="recents_clear_all" msgid="5328176793634888831">"Очистити все"</string>
<string name="accessibility_recent_apps" msgid="4058661986695117371">"Нещодавні додатки"</string>
+ <!-- no translation found for task_contents_description_with_remaining_time (4479688746574672685) -->
+ <skip />
+ <!-- no translation found for shorter_duration_less_than_one_minute (4722015666335015336) -->
+ <skip />
+ <!-- no translation found for app_in_grayscale (1108706002158384887) -->
+ <skip />
+ <!-- no translation found for time_left_for_app (3111996412933644358) -->
+ <skip />
</resources>
diff --git a/quickstep/res/values-ur/strings.xml b/quickstep/res/values-ur/strings.xml
index 69ff632..061aade 100644
--- a/quickstep/res/values-ur/strings.xml
+++ b/quickstep/res/values-ur/strings.xml
@@ -28,4 +28,12 @@
<string name="accessibility_app_usage_settings" msgid="6312864233673544149">"ایپ کے استعمال کی ترتیبات"</string>
<string name="recents_clear_all" msgid="5328176793634888831">"سبھی کو صاف کریں"</string>
<string name="accessibility_recent_apps" msgid="4058661986695117371">"حالیہ ایپس"</string>
+ <!-- no translation found for task_contents_description_with_remaining_time (4479688746574672685) -->
+ <skip />
+ <!-- no translation found for shorter_duration_less_than_one_minute (4722015666335015336) -->
+ <skip />
+ <!-- no translation found for app_in_grayscale (1108706002158384887) -->
+ <skip />
+ <!-- no translation found for time_left_for_app (3111996412933644358) -->
+ <skip />
</resources>
diff --git a/quickstep/res/values-uz/strings.xml b/quickstep/res/values-uz/strings.xml
index 9edb9a5..4c6e83f 100644
--- a/quickstep/res/values-uz/strings.xml
+++ b/quickstep/res/values-uz/strings.xml
@@ -28,4 +28,12 @@
<string name="accessibility_app_usage_settings" msgid="6312864233673544149">"Ilovadan foydalanish sozlamalari"</string>
<string name="recents_clear_all" msgid="5328176793634888831">"Hammasini tozalash"</string>
<string name="accessibility_recent_apps" msgid="4058661986695117371">"Yaqinda ishlatilgan ilovalar"</string>
+ <!-- no translation found for task_contents_description_with_remaining_time (4479688746574672685) -->
+ <skip />
+ <!-- no translation found for shorter_duration_less_than_one_minute (4722015666335015336) -->
+ <skip />
+ <!-- no translation found for app_in_grayscale (1108706002158384887) -->
+ <skip />
+ <!-- no translation found for time_left_for_app (3111996412933644358) -->
+ <skip />
</resources>
diff --git a/quickstep/res/values-vi/strings.xml b/quickstep/res/values-vi/strings.xml
index 649e44d..89d718e 100644
--- a/quickstep/res/values-vi/strings.xml
+++ b/quickstep/res/values-vi/strings.xml
@@ -28,4 +28,12 @@
<string name="accessibility_app_usage_settings" msgid="6312864233673544149">"Cài đặt mức sử dụng ứng dụng"</string>
<string name="recents_clear_all" msgid="5328176793634888831">"Xóa tất cả"</string>
<string name="accessibility_recent_apps" msgid="4058661986695117371">"Ứng dụng gần đây"</string>
+ <!-- no translation found for task_contents_description_with_remaining_time (4479688746574672685) -->
+ <skip />
+ <!-- no translation found for shorter_duration_less_than_one_minute (4722015666335015336) -->
+ <skip />
+ <!-- no translation found for app_in_grayscale (1108706002158384887) -->
+ <skip />
+ <!-- no translation found for time_left_for_app (3111996412933644358) -->
+ <skip />
</resources>
diff --git a/quickstep/res/values-zh-rCN/strings.xml b/quickstep/res/values-zh-rCN/strings.xml
index b27931c..48af192 100644
--- a/quickstep/res/values-zh-rCN/strings.xml
+++ b/quickstep/res/values-zh-rCN/strings.xml
@@ -28,4 +28,12 @@
<string name="accessibility_app_usage_settings" msgid="6312864233673544149">"应用使用设置"</string>
<string name="recents_clear_all" msgid="5328176793634888831">"全部清除"</string>
<string name="accessibility_recent_apps" msgid="4058661986695117371">"最近用过的应用"</string>
+ <!-- no translation found for task_contents_description_with_remaining_time (4479688746574672685) -->
+ <skip />
+ <!-- no translation found for shorter_duration_less_than_one_minute (4722015666335015336) -->
+ <skip />
+ <!-- no translation found for app_in_grayscale (1108706002158384887) -->
+ <skip />
+ <!-- no translation found for time_left_for_app (3111996412933644358) -->
+ <skip />
</resources>
diff --git a/quickstep/res/values-zh-rHK/strings.xml b/quickstep/res/values-zh-rHK/strings.xml
index f8fe8de..80f648b 100644
--- a/quickstep/res/values-zh-rHK/strings.xml
+++ b/quickstep/res/values-zh-rHK/strings.xml
@@ -28,4 +28,12 @@
<string name="accessibility_app_usage_settings" msgid="6312864233673544149">"應用程式使用情況設定"</string>
<string name="recents_clear_all" msgid="5328176793634888831">"全部清除"</string>
<string name="accessibility_recent_apps" msgid="4058661986695117371">"最近使用的應用程式"</string>
+ <!-- no translation found for task_contents_description_with_remaining_time (4479688746574672685) -->
+ <skip />
+ <!-- no translation found for shorter_duration_less_than_one_minute (4722015666335015336) -->
+ <skip />
+ <!-- no translation found for app_in_grayscale (1108706002158384887) -->
+ <skip />
+ <!-- no translation found for time_left_for_app (3111996412933644358) -->
+ <skip />
</resources>
diff --git a/quickstep/res/values-zh-rTW/strings.xml b/quickstep/res/values-zh-rTW/strings.xml
index ba51932..5e66514 100644
--- a/quickstep/res/values-zh-rTW/strings.xml
+++ b/quickstep/res/values-zh-rTW/strings.xml
@@ -28,4 +28,12 @@
<string name="accessibility_app_usage_settings" msgid="6312864233673544149">"應用程式使用情況設定"</string>
<string name="recents_clear_all" msgid="5328176793634888831">"全部清除"</string>
<string name="accessibility_recent_apps" msgid="4058661986695117371">"最近使用的應用程式"</string>
+ <!-- no translation found for task_contents_description_with_remaining_time (4479688746574672685) -->
+ <skip />
+ <!-- no translation found for shorter_duration_less_than_one_minute (4722015666335015336) -->
+ <skip />
+ <!-- no translation found for app_in_grayscale (1108706002158384887) -->
+ <skip />
+ <!-- no translation found for time_left_for_app (3111996412933644358) -->
+ <skip />
</resources>
diff --git a/quickstep/res/values-zu/strings.xml b/quickstep/res/values-zu/strings.xml
index d04a026..5753e55 100644
--- a/quickstep/res/values-zu/strings.xml
+++ b/quickstep/res/values-zu/strings.xml
@@ -28,4 +28,12 @@
<string name="accessibility_app_usage_settings" msgid="6312864233673544149">"Izilungiselelo zokusetshenziswa kohlelo lokusebenza"</string>
<string name="recents_clear_all" msgid="5328176793634888831">"Sula konke"</string>
<string name="accessibility_recent_apps" msgid="4058661986695117371">"Izinhlelo zokusebenza zakamuva"</string>
+ <!-- no translation found for task_contents_description_with_remaining_time (4479688746574672685) -->
+ <skip />
+ <!-- no translation found for shorter_duration_less_than_one_minute (4722015666335015336) -->
+ <skip />
+ <!-- no translation found for app_in_grayscale (1108706002158384887) -->
+ <skip />
+ <!-- no translation found for time_left_for_app (3111996412933644358) -->
+ <skip />
</resources>
diff --git a/quickstep/res/values/dimens.xml b/quickstep/res/values/dimens.xml
index 8d62ab8..04fd59c 100644
--- a/quickstep/res/values/dimens.xml
+++ b/quickstep/res/values/dimens.xml
@@ -30,9 +30,16 @@
loading full resolution screenshots. -->
<dimen name="recents_fast_fling_velocity">600dp</dimen>
+ <!-- These velocities are in dp / s -->
<dimen name="quickstep_fling_threshold_velocity">500dp</dimen>
<dimen name="quickstep_fling_min_velocity">250dp</dimen>
+ <!-- These speeds are in dp / ms -->
+ <dimen name="motion_pause_detector_speed_very_slow">0.0285dp</dimen>
+ <dimen name="motion_pause_detector_speed_somewhat_fast">0.285dp</dimen>
+ <dimen name="motion_pause_detector_speed_fast">0.5dp</dimen>
+ <dimen name="motion_pause_detector_min_displacement">48dp</dimen>
+
<!-- Launcher app transition -->
<dimen name="content_trans_y">50dp</dimen>
<dimen name="workspace_trans_y">50dp</dimen>
diff --git a/quickstep/src/com/android/launcher3/QuickstepAppTransitionManagerImpl.java b/quickstep/src/com/android/launcher3/QuickstepAppTransitionManagerImpl.java
index 07a5b72..4930dc5 100644
--- a/quickstep/src/com/android/launcher3/QuickstepAppTransitionManagerImpl.java
+++ b/quickstep/src/com/android/launcher3/QuickstepAppTransitionManagerImpl.java
@@ -54,9 +54,6 @@
import android.view.View;
import android.view.ViewGroup;
-import androidx.annotation.NonNull;
-import androidx.annotation.Nullable;
-
import com.android.launcher3.DeviceProfile.OnDeviceProfileChangeListener;
import com.android.launcher3.InsettableFrameLayout.LayoutParams;
import com.android.launcher3.allapps.AllAppsTransitionController;
@@ -80,6 +77,9 @@
import com.android.systemui.shared.system.SyncRtSurfaceTransactionApplierCompat.SurfaceParams;
import com.android.systemui.shared.system.WindowManagerWrapper;
+import androidx.annotation.NonNull;
+import androidx.annotation.Nullable;
+
/**
* {@link LauncherAppTransitionManager} with Quickstep-specific app transitions for launching from
* home and/or all-apps.
@@ -308,7 +308,7 @@
*/
private Rect getWindowTargetBounds(RemoteAnimationTargetCompat[] targets) {
Rect bounds = new Rect(0, 0, mDeviceProfile.widthPx, mDeviceProfile.heightPx);
- if (mLauncher.isInMultiWindowModeCompat()) {
+ if (mLauncher.isInMultiWindowMode()) {
for (RemoteAnimationTargetCompat target : targets) {
if (target.mode == MODE_OPENING) {
bounds.set(target.sourceContainerBounds);
diff --git a/quickstep/src/com/android/launcher3/uioverrides/OverviewToAllAppsTouchController.java b/quickstep/src/com/android/launcher3/uioverrides/OverviewToAllAppsTouchController.java
index 0f9b57f..a069ed5 100644
--- a/quickstep/src/com/android/launcher3/uioverrides/OverviewToAllAppsTouchController.java
+++ b/quickstep/src/com/android/launcher3/uioverrides/OverviewToAllAppsTouchController.java
@@ -36,7 +36,7 @@
public class OverviewToAllAppsTouchController extends PortraitStatesTouchController {
public OverviewToAllAppsTouchController(Launcher l) {
- super(l);
+ super(l, true /* allowDragToOverview */);
}
@Override
diff --git a/quickstep/src/com/android/launcher3/uioverrides/PortraitStatesTouchController.java b/quickstep/src/com/android/launcher3/uioverrides/PortraitStatesTouchController.java
index ea0e552..d20ffbb 100644
--- a/quickstep/src/com/android/launcher3/uioverrides/PortraitStatesTouchController.java
+++ b/quickstep/src/com/android/launcher3/uioverrides/PortraitStatesTouchController.java
@@ -25,6 +25,7 @@
import static com.android.launcher3.anim.Interpolators.ACCEL;
import static com.android.launcher3.anim.Interpolators.DEACCEL;
import static com.android.launcher3.anim.Interpolators.LINEAR;
+import static com.android.launcher3.config.FeatureFlags.QUICKSTEP_SPRINGS;
import android.animation.TimeInterpolator;
import android.animation.ValueAnimator;
@@ -67,14 +68,17 @@
private final PortraitOverviewStateTouchHelper mOverviewPortraitStateTouchHelper;
- private InterpolatorWrapper mAllAppsInterpolatorWrapper = new InterpolatorWrapper();
+ private final InterpolatorWrapper mAllAppsInterpolatorWrapper = new InterpolatorWrapper();
+
+ private final boolean mAllowDragToOverview;
// If true, we will finish the current animation instantly on second touch.
private boolean mFinishFastOnSecondTouch;
- public PortraitStatesTouchController(Launcher l) {
+ public PortraitStatesTouchController(Launcher l, boolean allowDragToOverview) {
super(l, SwipeDetector.VERTICAL);
mOverviewPortraitStateTouchHelper = new PortraitOverviewStateTouchHelper(l);
+ mAllowDragToOverview = allowDragToOverview;
}
@Override
@@ -82,7 +86,7 @@
if (mCurrentAnimation != null) {
if (mFinishFastOnSecondTouch) {
// TODO: Animate to finish instead.
- mCurrentAnimation.getAnimationPlayer().end();
+ mCurrentAnimation.skipToEnd();
}
AllAppsTransitionController allAppsController = mLauncher.getAllAppsController();
@@ -128,7 +132,8 @@
} else if (fromState == OVERVIEW) {
return isDragTowardPositive ? ALL_APPS : NORMAL;
} else if (fromState == NORMAL && isDragTowardPositive) {
- return TouchInteractionService.isConnected() ? OVERVIEW : ALL_APPS;
+ return mAllowDragToOverview && TouchInteractionService.isConnected()
+ ? OVERVIEW : ALL_APPS;
}
return fromState;
}
@@ -241,7 +246,10 @@
private void handleFirstSwipeToOverview(final ValueAnimator animator,
final long expectedDuration, final LauncherState targetState, final float velocity,
final boolean isFling) {
- if (mFromState == NORMAL && mToState == OVERVIEW && targetState == OVERVIEW) {
+ if (QUICKSTEP_SPRINGS.get() && mFromState == OVERVIEW && mToState == ALL_APPS
+ && targetState == OVERVIEW) {
+ mFinishFastOnSecondTouch = true;
+ } else if (mFromState == NORMAL && mToState == OVERVIEW && targetState == OVERVIEW) {
mFinishFastOnSecondTouch = true;
if (isFling && expectedDuration != 0) {
// Update all apps interpolator to add a bit of overshoot starting from currFraction
diff --git a/quickstep/src/com/android/launcher3/uioverrides/TaskViewTouchController.java b/quickstep/src/com/android/launcher3/uioverrides/TaskViewTouchController.java
index 50af4a1..fb1828b 100644
--- a/quickstep/src/com/android/launcher3/uioverrides/TaskViewTouchController.java
+++ b/quickstep/src/com/android/launcher3/uioverrides/TaskViewTouchController.java
@@ -18,6 +18,7 @@
import static com.android.launcher3.AbstractFloatingView.TYPE_ACCESSIBLE;
import static com.android.launcher3.Utilities.SINGLE_FRAME_MS;
import static com.android.launcher3.anim.Interpolators.scrollInterpolatorForVelocity;
+import static com.android.launcher3.config.FeatureFlags.QUICKSTEP_SPRINGS;
import static com.android.launcher3.config.FeatureFlags.ENABLE_QUICKSTEP_LIVE_TILE;
import android.animation.Animator;
@@ -281,6 +282,9 @@
}
});
}
+ if (QUICKSTEP_SPRINGS.get()) {
+ mCurrentAnimation.dispatchOnStartWithVelocity(goingToEnd ? 1f : 0f, velocity);
+ }
anim.start();
}
diff --git a/quickstep/src/com/android/launcher3/uioverrides/UiFactory.java b/quickstep/src/com/android/launcher3/uioverrides/UiFactory.java
index ff9d601..70aae13 100644
--- a/quickstep/src/com/android/launcher3/uioverrides/UiFactory.java
+++ b/quickstep/src/com/android/launcher3/uioverrides/UiFactory.java
@@ -21,7 +21,10 @@
import static com.android.launcher3.LauncherState.ALL_APPS;
import static com.android.launcher3.LauncherState.NORMAL;
import static com.android.launcher3.LauncherState.OVERVIEW;
+import static com.android.launcher3.allapps.DiscoveryBounce.BOUNCE_MAX_COUNT;
+import static com.android.launcher3.allapps.DiscoveryBounce.HOME_BOUNCE_COUNT;
import static com.android.launcher3.allapps.DiscoveryBounce.HOME_BOUNCE_SEEN;
+import static com.android.launcher3.allapps.DiscoveryBounce.SHELF_BOUNCE_COUNT;
import static com.android.launcher3.allapps.DiscoveryBounce.SHELF_BOUNCE_SEEN;
import android.animation.AnimatorSet;
@@ -32,68 +35,32 @@
import android.util.Base64;
import com.android.launcher3.AbstractFloatingView;
-import com.android.launcher3.DeviceProfile;
import com.android.launcher3.Launcher;
import com.android.launcher3.LauncherState;
import com.android.launcher3.LauncherStateManager;
import com.android.launcher3.LauncherStateManager.StateHandler;
import com.android.launcher3.QuickstepAppTransitionManagerImpl;
import com.android.launcher3.Utilities;
-import com.android.launcher3.config.FeatureFlags;
-import com.android.launcher3.util.TouchController;
-import com.android.launcher3.util.UiThreadHelper;
-import com.android.launcher3.util.UiThreadHelper.AsyncCommand;
import com.android.quickstep.OverviewInteractionState;
import com.android.quickstep.RecentsModel;
import com.android.quickstep.util.RemoteFadeOutAnimationListener;
import com.android.systemui.shared.system.ActivityCompat;
-import com.android.systemui.shared.system.WindowManagerWrapper;
import java.io.ByteArrayOutputStream;
import java.io.PrintWriter;
-import java.util.ArrayList;
import java.util.zip.Deflater;
-public class UiFactory {
-
- private static final AsyncCommand SET_SHELF_HEIGHT_CMD = (visible, height) ->
- WindowManagerWrapper.getInstance().setShelfHeight(visible != 0, height);
-
- public static TouchController[] createTouchControllers(Launcher launcher) {
- boolean swipeUpEnabled = OverviewInteractionState.INSTANCE.get(launcher)
- .isSwipeUpGestureEnabled();
- ArrayList<TouchController> list = new ArrayList<>();
- list.add(launcher.getDragController());
-
- if (!swipeUpEnabled || launcher.getDeviceProfile().isVerticalBarLayout()) {
- list.add(new OverviewToAllAppsTouchController(launcher));
- }
-
- if (launcher.getDeviceProfile().isVerticalBarLayout()) {
- list.add(new LandscapeEdgeSwipeController(launcher));
- } else {
- list.add(new PortraitStatesTouchController(launcher));
- }
- if (FeatureFlags.PULL_DOWN_STATUS_BAR && Utilities.IS_DEBUG_DEVICE
- && !launcher.getDeviceProfile().isMultiWindowMode
- && !launcher.getDeviceProfile().isVerticalBarLayout()) {
- list.add(new StatusBarTouchController(launcher));
- }
- TouchController taskSwipeController =
- RecentsUiFactory.createTaskSwipeController(launcher);
- if (taskSwipeController != null) {
- list.add(taskSwipeController);
- }
- return list.toArray(new TouchController[list.size()]);
- }
+public class UiFactory extends RecentsUiFactory {
public static void setOnTouchControllersChangedListener(Context context, Runnable listener) {
OverviewInteractionState.INSTANCE.get(context).setOnSwipeUpSettingChangedListener(listener);
}
public static StateHandler[] getStateHandler(Launcher launcher) {
- return new StateHandler[] {launcher.getAllAppsController(), launcher.getWorkspace(),
- RecentsUiFactory.createRecentsViewStateController(launcher),
+ return new StateHandler[] {
+ launcher.getAllAppsController(),
+ launcher.getWorkspace(),
+ createRecentsViewStateController(launcher),
new BackButtonAlphaHandler(launcher)};
}
@@ -113,19 +80,10 @@
.setBackButtonAlpha(shouldBackButtonBeHidden ? 0 : 1, true /* animate */);
}
- public static void resetOverview(Launcher launcher) {
- RecentsUiFactory.resetRecents(launcher);
- }
-
public static void onCreate(Launcher launcher) {
if (!launcher.getSharedPrefs().getBoolean(HOME_BOUNCE_SEEN, false)) {
launcher.getStateManager().addStateListener(new LauncherStateManager.StateListener() {
@Override
- public void onStateSetImmediately(LauncherState state) {
- onStateTransitionComplete(state);
- }
-
- @Override
public void onStateTransitionStart(LauncherState toState) {
}
@@ -136,7 +94,8 @@
LauncherState prevState = launcher.getStateManager().getLastState();
if (((swipeUpEnabled && finalState == OVERVIEW) || (!swipeUpEnabled
- && finalState == ALL_APPS && prevState == NORMAL))) {
+ && finalState == ALL_APPS && prevState == NORMAL) || BOUNCE_MAX_COUNT <=
+ launcher.getSharedPrefs().getInt(HOME_BOUNCE_COUNT, 0))) {
launcher.getSharedPrefs().edit().putBoolean(HOME_BOUNCE_SEEN, true).apply();
launcher.getStateManager().removeStateListener(this);
}
@@ -147,11 +106,6 @@
if (!launcher.getSharedPrefs().getBoolean(SHELF_BOUNCE_SEEN, false)) {
launcher.getStateManager().addStateListener(new LauncherStateManager.StateListener() {
@Override
- public void onStateSetImmediately(LauncherState state) {
- onStateTransitionComplete(state);
- }
-
- @Override
public void onStateTransitionStart(LauncherState toState) {
}
@@ -159,7 +113,8 @@
public void onStateTransitionComplete(LauncherState finalState) {
LauncherState prevState = launcher.getStateManager().getLastState();
- if (finalState == ALL_APPS && prevState == OVERVIEW) {
+ if ((finalState == ALL_APPS && prevState == OVERVIEW) || BOUNCE_MAX_COUNT <=
+ launcher.getSharedPrefs().getInt(SHELF_BOUNCE_COUNT, 0)) {
launcher.getSharedPrefs().edit().putBoolean(SHELF_BOUNCE_SEEN, true).apply();
launcher.getStateManager().removeStateListener(this);
}
@@ -176,19 +131,6 @@
.getHighResLoadingState().setVisible(true);
}
- public static void onLauncherStateOrResumeChanged(Launcher launcher) {
- LauncherState state = launcher.getStateManager().getState();
- if (!OverviewInteractionState.INSTANCE.get(launcher).swipeGestureInitializing()) {
- DeviceProfile profile = launcher.getDeviceProfile();
- boolean visible = (state == NORMAL || state == OVERVIEW) && launcher.isUserActive()
- && !profile.isVerticalBarLayout();
- UiThreadHelper.runAsyncCommand(launcher, SET_SHELF_HEIGHT_CMD,
- visible ? 1 : 0, profile.hotseatBarSizePx);
- }
-
- RecentsUiFactory.onLauncherStateOrResumeChanged(launcher);
- }
-
public static void onTrimMemory(Context context, int level) {
RecentsModel model = RecentsModel.INSTANCE.get(context);
if (model != null) {
@@ -238,8 +180,4 @@
out.toByteArray(), Base64.NO_WRAP | Base64.NO_PADDING));
return true;
}
-
- public static void prepareToShowOverview(Launcher launcher) {
- RecentsUiFactory.prepareToShowRecents(launcher);
- }
}
diff --git a/quickstep/src/com/android/quickstep/ActivityControlHelper.java b/quickstep/src/com/android/quickstep/ActivityControlHelper.java
index c3df9c7..fbb3618 100644
--- a/quickstep/src/com/android/quickstep/ActivityControlHelper.java
+++ b/quickstep/src/com/android/quickstep/ActivityControlHelper.java
@@ -16,12 +16,15 @@
package com.android.quickstep;
import static android.view.View.TRANSLATION_Y;
+
import static com.android.launcher3.LauncherAnimUtils.OVERVIEW_TRANSITION_MS;
import static com.android.launcher3.LauncherAnimUtils.SCALE_PROPERTY;
import static com.android.launcher3.LauncherState.BACKGROUND_APP;
import static com.android.launcher3.LauncherState.FAST_OVERVIEW;
+import static com.android.launcher3.LauncherState.NORMAL;
import static com.android.launcher3.LauncherState.OVERVIEW;
-import static com.android.launcher3.allapps.AllAppsTransitionController.ALL_APPS_PROGRESS_SPRING;
+import static com.android.launcher3.allapps.AllAppsTransitionController.SPRING_DAMPING_RATIO;
+import static com.android.launcher3.allapps.AllAppsTransitionController.SPRING_STIFFNESS;
import static com.android.launcher3.anim.Interpolators.LINEAR;
import static com.android.launcher3.states.RotationHelper.REQUEST_LOCK;
import static com.android.quickstep.TouchConsumer.INTERACTION_NORMAL;
@@ -31,6 +34,7 @@
import static com.android.systemui.shared.system.NavigationBarCompat.HIT_TARGET_ROTATION;
import android.animation.Animator;
+import android.animation.AnimatorListenerAdapter;
import android.animation.AnimatorSet;
import android.animation.ObjectAnimator;
import android.annotation.TargetApi;
@@ -44,6 +48,9 @@
import android.os.Handler;
import android.os.Looper;
import android.view.View;
+import android.view.animation.Interpolator;
+
+import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.annotation.UiThread;
@@ -56,6 +63,7 @@
import com.android.launcher3.R;
import com.android.launcher3.TestProtocol;
import com.android.launcher3.allapps.DiscoveryBounce;
+import com.android.launcher3.anim.AnimationSuccessListener;
import com.android.launcher3.anim.AnimatorPlaybackController;
import com.android.launcher3.anim.SpringObjectAnimator;
import com.android.launcher3.compat.AccessibilityManagerCompat;
@@ -105,6 +113,8 @@
void onSwipeUpComplete(T activity);
+ @NonNull HomeAnimationFactory prepareHomeUI(T activity);
+
AnimationFactory prepareRecentsUI(T activity, boolean activityVisible,
boolean animateActivity, Consumer<AnimatorPlaybackController> callback);
@@ -234,6 +244,32 @@
DiscoveryBounce.showForOverviewIfNeeded(activity);
}
+ @NonNull
+ @Override
+ public HomeAnimationFactory prepareHomeUI(Launcher activity) {
+ DeviceProfile dp = activity.getDeviceProfile();
+
+ return new HomeAnimationFactory() {
+ @NonNull
+ @Override
+ public RectF getWindowTargetRect() {
+ int halfIconSize = dp.iconSizePx / 2;
+ float targetCenterX = dp.availableWidthPx / 2;
+ float targetCenterY = dp.availableHeightPx - dp.hotseatBarSizePx;
+ return new RectF(targetCenterX - halfIconSize, targetCenterY - halfIconSize,
+ targetCenterX + halfIconSize, targetCenterY + halfIconSize);
+ }
+
+ @NonNull
+ @Override
+ public Animator createActivityAnimationToHome() {
+ long accuracy = 2 * Math.max(dp.widthPx, dp.heightPx);
+ return activity.getStateManager().createAnimationToNewWorkspace(
+ NORMAL, accuracy).getTarget();
+ }
+ };
+ }
+
@Override
public AnimationFactory prepareRecentsUI(Launcher activity, boolean activityVisible,
boolean animateActivity, Consumer<AnimatorPlaybackController> callback) {
@@ -263,6 +299,9 @@
}
return new AnimationFactory() {
+ private Animator mShelfAnim;
+ private ShelfAnimState mShelfState;
+
@Override
public void createActivityController(long transitionLength,
@InteractionType int interactionType) {
@@ -274,6 +313,40 @@
public void onTransitionCancelled() {
activity.getStateManager().goToState(startState, false /* animate */);
}
+
+ @Override
+ public void setShelfState(ShelfAnimState shelfState, Interpolator interpolator,
+ long duration) {
+ if (mShelfState == shelfState) {
+ return;
+ }
+ mShelfState = shelfState;
+ if (mShelfAnim != null) {
+ mShelfAnim.cancel();
+ }
+ if (mShelfState == ShelfAnimState.CANCEL) {
+ return;
+ }
+ float shelfHiddenProgress = BACKGROUND_APP.getVerticalProgress(activity);
+ float shelfOverviewProgress = OVERVIEW.getVerticalProgress(activity);
+ float shelfPeekingProgress = shelfHiddenProgress
+ - (shelfHiddenProgress - shelfOverviewProgress) * 0.25f;
+ float toProgress = mShelfState == ShelfAnimState.HIDE
+ ? shelfHiddenProgress
+ : mShelfState == ShelfAnimState.PEEK
+ ? shelfPeekingProgress
+ : shelfOverviewProgress;
+ mShelfAnim = createShelfAnim(activity, toProgress);
+ mShelfAnim.addListener(new AnimatorListenerAdapter() {
+ @Override
+ public void onAnimationEnd(Animator animation) {
+ mShelfAnim = null;
+ }
+ });
+ mShelfAnim.setInterpolator(interpolator);
+ mShelfAnim.setDuration(duration);
+ mShelfAnim.start();
+ }
};
}
@@ -295,13 +368,12 @@
}
AnimatorSet anim = new AnimatorSet();
- if (!activity.getDeviceProfile().isVerticalBarLayout()) {
- Animator shiftAnim = new SpringObjectAnimator<>(activity.getAllAppsController(),
- ALL_APPS_PROGRESS_SPRING, "allAppsSpringFromACH",
- activity.getAllAppsController().getShiftRange(),
+ if (!activity.getDeviceProfile().isVerticalBarLayout()
+ && !FeatureFlags.SWIPE_HOME.get()) {
+ // Don't animate the shelf when SWIPE_HOME is true, because we update it atomically.
+ Animator shiftAnim = createShelfAnim(activity,
fromState.getVerticalProgress(activity),
endState.getVerticalProgress(activity));
- shiftAnim.setInterpolator(LINEAR);
anim.play(shiftAnim);
}
@@ -322,6 +394,14 @@
callback.accept(controller);
}
+ private Animator createShelfAnim(Launcher activity, float ... progressValues) {
+ Animator shiftAnim = new SpringObjectAnimator<>(activity.getAllAppsController(),
+ "allAppsSpringFromACH", activity.getAllAppsController().getShiftRange(),
+ SPRING_DAMPING_RATIO, SPRING_STIFFNESS, progressValues);
+ shiftAnim.setInterpolator(LINEAR);
+ return shiftAnim;
+ }
+
/**
* Scale down recents from the center task being full screen to being in overview.
*/
@@ -512,6 +592,35 @@
// TODO:
}
+ @NonNull
+ @Override
+ public HomeAnimationFactory prepareHomeUI(RecentsActivity activity) {
+ RecentsView recentsView = activity.getOverviewPanel();
+
+ return new HomeAnimationFactory() {
+ @NonNull
+ @Override
+ public RectF getWindowTargetRect() {
+ float centerX = recentsView.getPivotX();
+ float centerY = recentsView.getPivotY();
+ return new RectF(centerX, centerY, centerX, centerY);
+ }
+
+ @NonNull
+ @Override
+ public Animator createActivityAnimationToHome() {
+ Animator anim = ObjectAnimator.ofFloat(recentsView, CONTENT_ALPHA, 0);
+ anim.addListener(new AnimationSuccessListener() {
+ @Override
+ public void onAnimationSuccess(Animator animator) {
+ recentsView.startHome();
+ }
+ });
+ return anim;
+ }
+ };
+ }
+
@Override
public AnimationFactory prepareRecentsUI(RecentsActivity activity, boolean activityVisible,
boolean animateActivity, Consumer<AnimatorPlaybackController> callback) {
@@ -524,12 +633,12 @@
return new AnimationFactory() {
- boolean isAnimatingHome = false;
+ boolean isAnimatingToRecents = false;
@Override
public void onRemoteAnimationReceived(RemoteAnimationTargetSet targets) {
- isAnimatingHome = targets != null && targets.isAnimatingHome();
- if (!isAnimatingHome) {
+ isAnimatingToRecents = targets != null && targets.isAnimatingHome();
+ if (!isAnimatingToRecents) {
rv.setContentAlpha(1);
}
createActivityController(getSwipeUpDestinationAndLength(
@@ -539,7 +648,7 @@
@Override
public void createActivityController(long transitionLength, int interactionType) {
- if (!isAnimatingHome) {
+ if (!isAnimatingToRecents) {
return;
}
@@ -667,10 +776,30 @@
interface AnimationFactory {
+ enum ShelfAnimState {
+ HIDE(true), PEEK(true), OVERVIEW(false), CANCEL(false);
+
+ ShelfAnimState(boolean shouldPreformHaptic) {
+ this.shouldPreformHaptic = shouldPreformHaptic;
+ }
+
+ public final boolean shouldPreformHaptic;
+ }
+
default void onRemoteAnimationReceived(RemoteAnimationTargetSet targets) { }
void createActivityController(long transitionLength, @InteractionType int interactionType);
default void onTransitionCancelled() { }
+
+ default void setShelfState(ShelfAnimState animState, Interpolator interpolator,
+ long duration) { }
+ }
+
+ interface HomeAnimationFactory {
+
+ @NonNull RectF getWindowTargetRect();
+
+ @NonNull Animator createActivityAnimationToHome();
}
}
diff --git a/quickstep/src/com/android/quickstep/MultiStateCallback.java b/quickstep/src/com/android/quickstep/MultiStateCallback.java
index 98d723f..1592a97 100644
--- a/quickstep/src/com/android/quickstep/MultiStateCallback.java
+++ b/quickstep/src/com/android/quickstep/MultiStateCallback.java
@@ -17,7 +17,7 @@
import android.util.SparseArray;
-import com.android.launcher3.Utilities.Consumer;
+import java.util.function.Consumer;
/**
* Utility class to help manage multiple callbacks based on different states.
diff --git a/quickstep/src/com/android/quickstep/OtherActivityTouchConsumer.java b/quickstep/src/com/android/quickstep/OtherActivityTouchConsumer.java
index cd71f3d..5755205 100644
--- a/quickstep/src/com/android/quickstep/OtherActivityTouchConsumer.java
+++ b/quickstep/src/com/android/quickstep/OtherActivityTouchConsumer.java
@@ -21,6 +21,7 @@
import static android.view.MotionEvent.ACTION_POINTER_UP;
import static android.view.MotionEvent.ACTION_UP;
import static android.view.MotionEvent.INVALID_POINTER_ID;
+
import static com.android.launcher3.util.RaceConditionTracker.ENTER;
import static com.android.launcher3.util.RaceConditionTracker.EXIT;
import static com.android.systemui.shared.system.ActivityManagerWrapper.CLOSE_SYSTEM_WINDOWS_REASON_RECENTS;
@@ -47,8 +48,10 @@
import android.view.WindowManager;
import com.android.launcher3.MainThreadExecutor;
+import com.android.launcher3.config.FeatureFlags;
import com.android.launcher3.util.RaceConditionTracker;
import com.android.launcher3.util.TraceHelper;
+import com.android.quickstep.util.MotionPauseDetector;
import com.android.quickstep.util.RemoteAnimationTargetSet;
import com.android.systemui.shared.system.ActivityManagerWrapper;
import com.android.systemui.shared.system.AssistDataReceiver;
@@ -64,6 +67,8 @@
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.TimeUnit;
+import androidx.annotation.Nullable;
+
/**
* Touch consumer for handling events originating from an activity other than Launcher
*/
@@ -99,6 +104,7 @@
private Rect mStableInsets = new Rect();
private VelocityTracker mVelocityTracker;
+ private MotionPauseDetector mMotionPauseDetector;
private MotionEventQueue mEventQueue;
private boolean mIsGoingToLauncher;
@@ -114,6 +120,7 @@
mRecentsModel = recentsModel;
mHomeIntent = homeIntent;
mVelocityTracker = velocityTracker;
+ mMotionPauseDetector = new MotionPauseDetector(base);
mActivityControlHelper = activityControl;
mMainThreadExecutor = mainThreadExecutor;
mBackgroundThreadChoreographer = backgroundThreadChoreographer;
@@ -191,7 +198,11 @@
if (mPassedInitialSlop && mInteractionHandler != null) {
// Move
- dispatchMotion(ev, displacement - mStartDisplacement);
+ dispatchMotion(ev, displacement - mStartDisplacement, null);
+
+ if (FeatureFlags.SWIPE_HOME.get()) {
+ mMotionPauseDetector.addPosition(displacement);
+ }
}
break;
}
@@ -208,11 +219,11 @@
}
}
- private void dispatchMotion(MotionEvent ev, float displacement) {
+ private void dispatchMotion(MotionEvent ev, float displacement, @Nullable Float velocityX) {
mInteractionHandler.updateDisplacement(displacement);
boolean isLandscape = isNavBarOnLeft() || isNavBarOnRight();
if (!isLandscape) {
- mInteractionHandler.dispatchMotionEventToRecentsView(ev);
+ mInteractionHandler.dispatchMotionEventToRecentsView(ev, velocityX);
}
}
@@ -250,6 +261,7 @@
mRecentsModel.getTasks(null);
mInteractionHandler = handler;
handler.setGestureEndCallback(mEventQueue::reset);
+ mMotionPauseDetector.setOnMotionPauseListener(handler::onMotionPauseChanged);
CountDownLatch drawWaitLock = new CountDownLatch(1);
handler.setLauncherOnDrawCallback(() -> {
@@ -306,15 +318,16 @@
*/
private void finishTouchTracking(MotionEvent ev) {
if (mPassedInitialSlop && mInteractionHandler != null) {
- dispatchMotion(ev, getDisplacement(ev) - mStartDisplacement);
mVelocityTracker.computeCurrentVelocity(1000,
ViewConfiguration.get(this).getScaledMaximumFlingVelocity());
-
float velocityX = mVelocityTracker.getXVelocity(mActivePointerId);
float velocity = isNavBarOnRight() ? velocityX
: isNavBarOnLeft() ? -velocityX
: mVelocityTracker.getYVelocity(mActivePointerId);
+
+ dispatchMotion(ev, getDisplacement(ev) - mStartDisplacement, velocityX);
+
mInteractionHandler.onGestureEnded(velocity, velocityX);
} else {
// Since we start touch tracking on DOWN, we may reach this state without actually
@@ -336,7 +349,7 @@
if (mInteractionHandler != null) {
final WindowTransformSwipeHandler handler = mInteractionHandler;
mInteractionHandler = null;
- mIsGoingToLauncher = handler.mIsGoingToRecents;
+ mIsGoingToLauncher = handler.mIsGoingToRecents || handler.mIsGoingToHome;
mMainThreadExecutor.execute(handler::reset);
}
}
@@ -387,8 +400,10 @@
// touch slop
startTouchTrackingForWindowAnimation(ev.getEventTime());
}
- mPassedInitialSlop = true;
- mStartDisplacement = getDisplacement(ev);
+ if (!mPassedInitialSlop) {
+ mPassedInitialSlop = true;
+ mStartDisplacement = getDisplacement(ev);
+ }
notifyGestureStarted();
}
@@ -414,6 +429,7 @@
mVelocityTracker.addMovement(ev);
if (ev.getActionMasked() == ACTION_POINTER_UP) {
mVelocityTracker.clear();
+ mMotionPauseDetector.clear();
}
}
}
diff --git a/quickstep/src/com/android/quickstep/OverviewCommandHelper.java b/quickstep/src/com/android/quickstep/OverviewCommandHelper.java
index 9bbe57a..da4a3de 100644
--- a/quickstep/src/com/android/quickstep/OverviewCommandHelper.java
+++ b/quickstep/src/com/android/quickstep/OverviewCommandHelper.java
@@ -15,17 +15,11 @@
*/
package com.android.quickstep;
-import static android.content.Intent.ACTION_PACKAGE_ADDED;
-import static android.content.Intent.ACTION_PACKAGE_CHANGED;
-import static android.content.Intent.ACTION_PACKAGE_REMOVED;
-
import static com.android.launcher3.anim.Interpolators.FAST_OUT_SLOW_IN;
import static com.android.launcher3.anim.Interpolators.TOUCH_RESPONSE_INTERPOLATOR;
import static com.android.quickstep.TouchConsumer.INTERACTION_NORMAL;
import static com.android.systemui.shared.system.ActivityManagerWrapper
.CLOSE_SYSTEM_WINDOWS_REASON_RECENTS;
-import static com.android.systemui.shared.system.PackageManagerWrapper
- .ACTION_PREFERRED_ACTIVITY_CHANGED;
import static com.android.systemui.shared.system.RemoteAnimationTargetCompat.MODE_CLOSING;
import static com.android.systemui.shared.system.RemoteAnimationTargetCompat.MODE_OPENING;
@@ -33,15 +27,9 @@
import android.animation.AnimatorSet;
import android.animation.ValueAnimator;
import android.annotation.TargetApi;
-import android.content.BroadcastReceiver;
-import android.content.ComponentName;
import android.content.Context;
-import android.content.Intent;
-import android.content.IntentFilter;
-import android.content.pm.ResolveInfo;
import android.graphics.Rect;
import android.os.Build;
-import android.os.PatternMatcher;
import android.os.SystemClock;
import android.util.Log;
import android.view.View;
@@ -56,21 +44,16 @@
import com.android.launcher3.userevent.nano.LauncherLogProto.ContainerType;
import com.android.quickstep.ActivityControlHelper.ActivityInitListener;
import com.android.quickstep.ActivityControlHelper.AnimationFactory;
-import com.android.quickstep.ActivityControlHelper.FallbackActivityControllerHelper;
-import com.android.quickstep.ActivityControlHelper.LauncherActivityControllerHelper;
import com.android.quickstep.util.ClipAnimationHelper;
-import com.android.quickstep.util.TransformedRect;
import com.android.quickstep.util.RemoteAnimationTargetSet;
+import com.android.quickstep.util.TransformedRect;
import com.android.quickstep.views.RecentsView;
import com.android.systemui.shared.system.ActivityManagerWrapper;
import com.android.systemui.shared.system.LatencyTrackerCompat;
-import com.android.systemui.shared.system.PackageManagerWrapper;
import com.android.systemui.shared.system.RemoteAnimationTargetCompat;
import com.android.systemui.shared.system.SyncRtSurfaceTransactionApplierCompat;
import com.android.systemui.shared.system.TransactionCompat;
-import java.util.ArrayList;
-
/**
* Helper class to handle various atomic commands for switching between Overview.
*/
@@ -85,100 +68,16 @@
private final ActivityManagerWrapper mAM;
private final RecentsModel mRecentsModel;
private final MainThreadExecutor mMainThreadExecutor;
- private final ComponentName mMyHomeComponent;
-
- private final BroadcastReceiver mUserPreferenceChangeReceiver = new BroadcastReceiver() {
- @Override
- public void onReceive(Context context, Intent intent) {
- initOverviewTargets();
- }
- };
- private final BroadcastReceiver mOtherHomeAppUpdateReceiver = new BroadcastReceiver() {
- @Override
- public void onReceive(Context context, Intent intent) {
- initOverviewTargets();
- }
- };
- private String mUpdateRegisteredPackage;
-
- public Intent overviewIntent;
- public ComponentName overviewComponent;
- private ActivityControlHelper mActivityControlHelper;
+ private final OverviewComponentObserver mOverviewComponentObserver;
private long mLastToggleTime;
- public OverviewCommandHelper(Context context) {
+ public OverviewCommandHelper(Context context, OverviewComponentObserver observer) {
mContext = context;
mAM = ActivityManagerWrapper.getInstance();
mMainThreadExecutor = new MainThreadExecutor();
mRecentsModel = RecentsModel.INSTANCE.get(mContext);
-
- Intent myHomeIntent = new Intent(Intent.ACTION_MAIN)
- .addCategory(Intent.CATEGORY_HOME)
- .setPackage(mContext.getPackageName());
- ResolveInfo info = context.getPackageManager().resolveActivity(myHomeIntent, 0);
- mMyHomeComponent = new ComponentName(context.getPackageName(), info.activityInfo.name);
-
- mContext.registerReceiver(mUserPreferenceChangeReceiver,
- new IntentFilter(ACTION_PREFERRED_ACTIVITY_CHANGED));
- initOverviewTargets();
- }
-
- private void initOverviewTargets() {
- ComponentName defaultHome = PackageManagerWrapper.getInstance()
- .getHomeActivities(new ArrayList<>());
-
- final String overviewIntentCategory;
- if (defaultHome == null || mMyHomeComponent.equals(defaultHome)) {
- // User default home is same as out home app. Use Overview integrated in Launcher.
- overviewComponent = mMyHomeComponent;
- mActivityControlHelper = new LauncherActivityControllerHelper();
- overviewIntentCategory = Intent.CATEGORY_HOME;
-
- if (mUpdateRegisteredPackage != null) {
- // Remove any update listener as we don't care about other packages.
- mContext.unregisterReceiver(mOtherHomeAppUpdateReceiver);
- mUpdateRegisteredPackage = null;
- }
- } else {
- // The default home app is a different launcher. Use the fallback Overview instead.
- overviewComponent = new ComponentName(mContext, RecentsActivity.class);
- mActivityControlHelper = new FallbackActivityControllerHelper(defaultHome);
- overviewIntentCategory = Intent.CATEGORY_DEFAULT;
-
- // User's default home app can change as a result of package updates of this app (such
- // as uninstalling the app or removing the "Launcher" feature in an update).
- // Listen for package updates of this app (and remove any previously attached
- // package listener).
- if (!defaultHome.getPackageName().equals(mUpdateRegisteredPackage)) {
- if (mUpdateRegisteredPackage != null) {
- mContext.unregisterReceiver(mOtherHomeAppUpdateReceiver);
- }
-
- mUpdateRegisteredPackage = defaultHome.getPackageName();
- IntentFilter updateReceiver = new IntentFilter(ACTION_PACKAGE_ADDED);
- updateReceiver.addAction(ACTION_PACKAGE_CHANGED);
- updateReceiver.addAction(ACTION_PACKAGE_REMOVED);
- updateReceiver.addDataScheme("package");
- updateReceiver.addDataSchemeSpecificPart(mUpdateRegisteredPackage,
- PatternMatcher.PATTERN_LITERAL);
- mContext.registerReceiver(mOtherHomeAppUpdateReceiver, updateReceiver);
- }
- }
-
- overviewIntent = new Intent(Intent.ACTION_MAIN)
- .addCategory(overviewIntentCategory)
- .setComponent(overviewComponent)
- .setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
- }
-
- public void onDestroy() {
- mContext.unregisterReceiver(mUserPreferenceChangeReceiver);
-
- if (mUpdateRegisteredPackage != null) {
- mContext.unregisterReceiver(mOtherHomeAppUpdateReceiver);
- mUpdateRegisteredPackage = null;
- }
+ mOverviewComponentObserver = observer;
}
public void onOverviewToggle() {
@@ -200,10 +99,6 @@
UserEventDispatcher.newInstance(mContext).logActionTip(actionType, viewType));
}
- public ActivityControlHelper getActivityControlHelper() {
- return mActivityControlHelper;
- }
-
private class ShowRecentsCommand extends RecentsActivityCommand {
@Override
@@ -225,7 +120,7 @@
private boolean mUserEventLogged;
public RecentsActivityCommand() {
- mHelper = getActivityControlHelper();
+ mHelper = mOverviewComponentObserver.getActivityControlHelper();
mCreateTime = SystemClock.elapsedRealtime();
mRunningTaskId = RecentsModel.getRunningTaskId();
@@ -242,8 +137,10 @@
// Start overview
if (!mHelper.switchToRecentsIfVisible(true)) {
mListener = mHelper.createActivityInitListener(this::onActivityReady);
- mListener.registerAndStartActivity(overviewIntent, this::createWindowAnimation,
- mContext, mMainThreadExecutor.getHandler(), RECENTS_LAUNCH_DURATION);
+ mListener.registerAndStartActivity(
+ mOverviewComponentObserver.getOverviewIntent(),
+ this::createWindowAnimation, mContext, mMainThreadExecutor.getHandler(),
+ RECENTS_LAUNCH_DURATION);
}
}
}
diff --git a/quickstep/src/com/android/quickstep/OverviewComponentObserver.java b/quickstep/src/com/android/quickstep/OverviewComponentObserver.java
new file mode 100644
index 0000000..e119e53
--- /dev/null
+++ b/quickstep/src/com/android/quickstep/OverviewComponentObserver.java
@@ -0,0 +1,159 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.quickstep;
+
+import static android.content.Intent.ACTION_PACKAGE_ADDED;
+import static android.content.Intent.ACTION_PACKAGE_CHANGED;
+import static android.content.Intent.ACTION_PACKAGE_REMOVED;
+
+import static com.android.systemui.shared.system.PackageManagerWrapper
+ .ACTION_PREFERRED_ACTIVITY_CHANGED;
+
+import android.content.BroadcastReceiver;
+import android.content.ComponentName;
+import android.content.Context;
+import android.content.Intent;
+import android.content.IntentFilter;
+import android.content.pm.ResolveInfo;
+import android.os.PatternMatcher;
+
+import com.android.quickstep.ActivityControlHelper.FallbackActivityControllerHelper;
+import com.android.quickstep.ActivityControlHelper.LauncherActivityControllerHelper;
+import com.android.systemui.shared.system.PackageManagerWrapper;
+
+import java.util.ArrayList;
+
+/**
+ * Class to keep track of the current overview component based off user preferences and app updates
+ * and provide callers the relevant classes.
+ */
+public final class OverviewComponentObserver {
+ private final BroadcastReceiver mUserPreferenceChangeReceiver = new BroadcastReceiver() {
+ @Override
+ public void onReceive(Context context, Intent intent) {
+ updateOverviewTargets();
+ }
+ };
+ private final BroadcastReceiver mOtherHomeAppUpdateReceiver = new BroadcastReceiver() {
+ @Override
+ public void onReceive(Context context, Intent intent) {
+ updateOverviewTargets();
+ }
+ };
+ private final Context mContext;
+ private final ComponentName mMyHomeComponent;
+ private String mUpdateRegisteredPackage;
+ private ActivityControlHelper mActivityControlHelper;
+ private Intent mOverviewIntent;
+
+ public OverviewComponentObserver(Context context) {
+ mContext = context;
+
+ Intent myHomeIntent = new Intent(Intent.ACTION_MAIN)
+ .addCategory(Intent.CATEGORY_HOME)
+ .setPackage(mContext.getPackageName());
+ ResolveInfo info = context.getPackageManager().resolveActivity(myHomeIntent, 0);
+ mMyHomeComponent = new ComponentName(context.getPackageName(), info.activityInfo.name);
+
+ mContext.registerReceiver(mUserPreferenceChangeReceiver,
+ new IntentFilter(ACTION_PREFERRED_ACTIVITY_CHANGED));
+ updateOverviewTargets();
+ }
+
+ /**
+ * Update overview intent and {@link ActivityControlHelper} based off the current launcher home
+ * component.
+ */
+ private void updateOverviewTargets() {
+ ComponentName defaultHome = PackageManagerWrapper.getInstance()
+ .getHomeActivities(new ArrayList<>());
+
+ final String overviewIntentCategory;
+ ComponentName overviewComponent;
+ if (defaultHome == null || mMyHomeComponent.equals(defaultHome)) {
+ // User default home is same as out home app. Use Overview integrated in Launcher.
+ overviewComponent = mMyHomeComponent;
+ mActivityControlHelper = new LauncherActivityControllerHelper();
+ overviewIntentCategory = Intent.CATEGORY_HOME;
+
+ if (mUpdateRegisteredPackage != null) {
+ // Remove any update listener as we don't care about other packages.
+ mContext.unregisterReceiver(mOtherHomeAppUpdateReceiver);
+ mUpdateRegisteredPackage = null;
+ }
+ } else {
+ // The default home app is a different launcher. Use the fallback Overview instead.
+ overviewComponent = new ComponentName(mContext, RecentsActivity.class);
+ mActivityControlHelper = new FallbackActivityControllerHelper(defaultHome);
+ overviewIntentCategory = Intent.CATEGORY_DEFAULT;
+
+ // User's default home app can change as a result of package updates of this app (such
+ // as uninstalling the app or removing the "Launcher" feature in an update).
+ // Listen for package updates of this app (and remove any previously attached
+ // package listener).
+ if (!defaultHome.getPackageName().equals(mUpdateRegisteredPackage)) {
+ if (mUpdateRegisteredPackage != null) {
+ mContext.unregisterReceiver(mOtherHomeAppUpdateReceiver);
+ }
+
+ mUpdateRegisteredPackage = defaultHome.getPackageName();
+ IntentFilter updateReceiver = new IntentFilter(ACTION_PACKAGE_ADDED);
+ updateReceiver.addAction(ACTION_PACKAGE_CHANGED);
+ updateReceiver.addAction(ACTION_PACKAGE_REMOVED);
+ updateReceiver.addDataScheme("package");
+ updateReceiver.addDataSchemeSpecificPart(mUpdateRegisteredPackage,
+ PatternMatcher.PATTERN_LITERAL);
+ mContext.registerReceiver(mOtherHomeAppUpdateReceiver, updateReceiver);
+ }
+ }
+
+ mOverviewIntent = new Intent(Intent.ACTION_MAIN)
+ .addCategory(overviewIntentCategory)
+ .setComponent(overviewComponent)
+ .setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
+ }
+
+ /**
+ * Clean up any registered receivers.
+ */
+ public void onDestroy() {
+ mContext.unregisterReceiver(mUserPreferenceChangeReceiver);
+
+ if (mUpdateRegisteredPackage != null) {
+ mContext.unregisterReceiver(mOtherHomeAppUpdateReceiver);
+ mUpdateRegisteredPackage = null;
+ }
+ }
+
+ /**
+ * Get the current intent for going to the overview activity.
+ *
+ * @return the overview intent
+ */
+ public Intent getOverviewIntent() {
+ return mOverviewIntent;
+ }
+
+ /**
+ * Get the current activity control helper for managing interactions to the overview activity.
+ *
+ * @return the current activity control helper
+ */
+ public ActivityControlHelper getActivityControlHelper() {
+ return mActivityControlHelper;
+ }
+}
diff --git a/quickstep/src/com/android/quickstep/RecentsActivity.java b/quickstep/src/com/android/quickstep/RecentsActivity.java
index 89c7aba..b76a1ab 100644
--- a/quickstep/src/com/android/quickstep/RecentsActivity.java
+++ b/quickstep/src/com/android/quickstep/RecentsActivity.java
@@ -19,10 +19,8 @@
import static android.content.pm.ActivityInfo.CONFIG_SCREEN_SIZE;
import static com.android.launcher3.QuickstepAppTransitionManagerImpl.RECENTS_LAUNCH_DURATION;
-import static com.android.launcher3.QuickstepAppTransitionManagerImpl
- .STATUS_BAR_TRANSITION_DURATION;
-import static com.android.launcher3.QuickstepAppTransitionManagerImpl
- .STATUS_BAR_TRANSITION_PRE_DELAY;
+import static com.android.launcher3.QuickstepAppTransitionManagerImpl.STATUS_BAR_TRANSITION_DURATION;
+import static com.android.launcher3.QuickstepAppTransitionManagerImpl.STATUS_BAR_TRANSITION_PRE_DELAY;
import static com.android.quickstep.TaskUtils.getRecentsWindowAnimator;
import static com.android.quickstep.TaskUtils.taskIsATargetWithMode;
import static com.android.systemui.shared.system.RemoteAnimationTargetCompat.MODE_CLOSING;
@@ -42,7 +40,6 @@
import com.android.launcher3.BaseDraggingActivity;
import com.android.launcher3.DeviceProfile;
import com.android.launcher3.InvariantDeviceProfile;
-import com.android.launcher3.ItemInfo;
import com.android.launcher3.LauncherAnimationRunner;
import com.android.launcher3.R;
import com.android.launcher3.anim.Interpolators;
@@ -108,7 +105,7 @@
}
public void onRootViewSizeChanged() {
- if (isInMultiWindowModeCompat()) {
+ if (isInMultiWindowMode()) {
onHandleConfigChanged();
}
}
@@ -135,7 +132,7 @@
// In case we are reusing IDP, create a copy so that we don't conflict with Launcher
// activity.
- mDeviceProfile = (mRecentsRootView != null) && isInMultiWindowModeCompat()
+ mDeviceProfile = (mRecentsRootView != null) && isInMultiWindowMode()
? dp.getMultiWindowProfile(this, mRecentsRootView.getLastKnownSize())
: dp.copy(this);
onDeviceProfileInitiated();
diff --git a/quickstep/src/com/android/quickstep/TouchConsumer.java b/quickstep/src/com/android/quickstep/TouchConsumer.java
index 225d29b..057a2ee 100644
--- a/quickstep/src/com/android/quickstep/TouchConsumer.java
+++ b/quickstep/src/com/android/quickstep/TouchConsumer.java
@@ -20,12 +20,12 @@
import android.view.Choreographer;
import android.view.MotionEvent;
+import androidx.annotation.IntDef;
+
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.util.function.Consumer;
-import androidx.annotation.IntDef;
-
@TargetApi(Build.VERSION_CODES.O)
@FunctionalInterface
public interface TouchConsumer extends Consumer<MotionEvent> {
diff --git a/quickstep/src/com/android/quickstep/TouchInteractionService.java b/quickstep/src/com/android/quickstep/TouchInteractionService.java
index 8b6867f..d5de3ff 100644
--- a/quickstep/src/com/android/quickstep/TouchInteractionService.java
+++ b/quickstep/src/com/android/quickstep/TouchInteractionService.java
@@ -183,6 +183,7 @@
private MainThreadExecutor mMainThreadExecutor;
private ISystemUiProxy mISystemUiProxy;
private OverviewCommandHelper mOverviewCommandHelper;
+ private OverviewComponentObserver mOverviewComponentObserver;
private OverviewInteractionState mOverviewInteractionState;
private OverviewCallbacks mOverviewCallbacks;
private TaskOverlayFactory mTaskOverlayFactory;
@@ -198,7 +199,8 @@
mAM = ActivityManagerWrapper.getInstance();
mRecentsModel = RecentsModel.INSTANCE.get(this);
mMainThreadExecutor = new MainThreadExecutor();
- mOverviewCommandHelper = new OverviewCommandHelper(this);
+ mOverviewComponentObserver = new OverviewComponentObserver(this);
+ mOverviewCommandHelper = new OverviewCommandHelper(this, mOverviewComponentObserver);
mMainThreadChoreographer = Choreographer.getInstance();
mEventQueue = new MotionEventQueue(mMainThreadChoreographer, TouchConsumer.NO_OP);
mOverviewInteractionState = OverviewInteractionState.INSTANCE.get(this);
@@ -218,7 +220,7 @@
@Override
public void onDestroy() {
mInputConsumer.unregisterInputConsumer();
- mOverviewCommandHelper.onDestroy();
+ mOverviewComponentObserver.onDestroy();
sConnected = false;
super.onDestroy();
}
@@ -250,21 +252,22 @@
if (runningTaskInfo == null && !forceToLauncher) {
return TouchConsumer.NO_OP;
} else if (forceToLauncher ||
- mOverviewCommandHelper.getActivityControlHelper().isResumed()) {
+ mOverviewComponentObserver.getActivityControlHelper().isResumed()) {
return OverviewTouchConsumer.newInstance(
- mOverviewCommandHelper.getActivityControlHelper(), false, mTouchInteractionLog);
+ mOverviewComponentObserver.getActivityControlHelper(), false,
+ mTouchInteractionLog);
} else if (ENABLE_QUICKSTEP_LIVE_TILE.get() &&
- mOverviewCommandHelper.getActivityControlHelper().isInLiveTileMode()) {
+ mOverviewComponentObserver.getActivityControlHelper().isInLiveTileMode()) {
return OverviewTouchConsumer.newInstance(
- mOverviewCommandHelper.getActivityControlHelper(), false, mTouchInteractionLog,
- false /* waitForWindowAvailable */);
+ mOverviewComponentObserver.getActivityControlHelper(), false,
+ mTouchInteractionLog, false /* waitForWindowAvailable */);
} else {
if (tracker == null) {
tracker = VelocityTracker.obtain();
}
return new OtherActivityTouchConsumer(this, runningTaskInfo, mRecentsModel,
- mOverviewCommandHelper.overviewIntent,
- mOverviewCommandHelper.getActivityControlHelper(), mMainThreadExecutor,
+ mOverviewComponentObserver.getOverviewIntent(),
+ mOverviewComponentObserver.getActivityControlHelper(), mMainThreadExecutor,
mBackgroundThreadChoreographer, downHitTarget, mOverviewCallbacks,
mTaskOverlayFactory, mInputConsumer, tracker, mTouchInteractionLog);
}
@@ -344,7 +347,8 @@
startTouchTracking(ev, true /* updateLocationOffset */);
break;
case ACTION_MOVE: {
- float displacement = ev.getY() - mDownPos.y;
+ float displacement = mActivity.getDeviceProfile().isLandscape ?
+ ev.getX() - mDownPos.x : ev.getY() - mDownPos.y;
if (Math.abs(displacement) >= mTouchSlop) {
// Start tracking only when mTouchSlop is crossed.
startTouchTracking(ev, true /* updateLocationOffset */);
diff --git a/quickstep/src/com/android/quickstep/WindowTransformSwipeHandler.java b/quickstep/src/com/android/quickstep/WindowTransformSwipeHandler.java
index 33c7c4d..86a8081 100644
--- a/quickstep/src/com/android/quickstep/WindowTransformSwipeHandler.java
+++ b/quickstep/src/com/android/quickstep/WindowTransformSwipeHandler.java
@@ -20,21 +20,32 @@
import static com.android.launcher3.Utilities.SINGLE_FRAME_MS;
import static com.android.launcher3.Utilities.postAsyncCallback;
import static com.android.launcher3.anim.Interpolators.DEACCEL;
+import static com.android.launcher3.anim.Interpolators.FAST_OUT_SLOW_IN;
import static com.android.launcher3.anim.Interpolators.LINEAR;
import static com.android.launcher3.anim.Interpolators.OVERSHOOT_1_2;
import static com.android.launcher3.config.FeatureFlags.ENABLE_QUICKSTEP_LIVE_TILE;
import static com.android.launcher3.config.FeatureFlags.QUICKSTEP_SPRINGS;
import static com.android.launcher3.util.RaceConditionTracker.ENTER;
import static com.android.launcher3.util.RaceConditionTracker.EXIT;
+import static com.android.launcher3.config.FeatureFlags.SWIPE_HOME;
+import static com.android.quickstep.ActivityControlHelper.AnimationFactory.ShelfAnimState.HIDE;
+import static com.android.quickstep.ActivityControlHelper.AnimationFactory.ShelfAnimState.PEEK;
import static com.android.quickstep.QuickScrubController.QUICK_SCRUB_FROM_APP_START_DURATION;
import static com.android.quickstep.QuickScrubController.QUICK_SWITCH_FROM_APP_START_DURATION;
import static com.android.quickstep.TouchConsumer.INTERACTION_NORMAL;
import static com.android.quickstep.TouchConsumer.INTERACTION_QUICK_SCRUB;
+import static com.android.quickstep.WindowTransformSwipeHandler.GestureEndTarget.HOME;
+import static com.android.quickstep.WindowTransformSwipeHandler.GestureEndTarget.LAST_TASK;
+import static com.android.quickstep.WindowTransformSwipeHandler.GestureEndTarget.NEW_TASK;
+import static com.android.quickstep.WindowTransformSwipeHandler.GestureEndTarget.RECENTS;
import static com.android.quickstep.views.RecentsView.UPDATE_SYSUI_FLAGS_THRESHOLD;
import android.animation.Animator;
import android.animation.AnimatorListenerAdapter;
+import android.animation.AnimatorSet;
import android.animation.ObjectAnimator;
+import android.animation.TimeInterpolator;
+import android.animation.ValueAnimator;
import android.annotation.TargetApi;
import android.app.ActivityManager.RunningTaskInfo;
import android.content.Context;
@@ -42,6 +53,7 @@
import android.graphics.Canvas;
import android.graphics.Point;
import android.graphics.Rect;
+import android.graphics.RectF;
import android.os.Build;
import android.os.Bundle;
import android.os.Handler;
@@ -57,6 +69,8 @@
import android.view.animation.Interpolator;
import androidx.annotation.AnyThread;
+import androidx.annotation.NonNull;
+import androidx.annotation.Nullable;
import androidx.annotation.UiThread;
import androidx.annotation.WorkerThread;
@@ -80,6 +94,7 @@
import com.android.launcher3.util.TraceHelper;
import com.android.quickstep.ActivityControlHelper.ActivityInitListener;
import com.android.quickstep.ActivityControlHelper.AnimationFactory;
+import com.android.quickstep.ActivityControlHelper.AnimationFactory.ShelfAnimState;
import com.android.quickstep.ActivityControlHelper.LayoutListener;
import com.android.quickstep.TouchConsumer.InteractionType;
import com.android.quickstep.TouchInteractionService.OverviewTouchConsumer;
@@ -89,6 +104,7 @@
import com.android.quickstep.views.RecentsView;
import com.android.quickstep.views.TaskView;
import com.android.systemui.shared.recents.model.ThumbnailData;
+import com.android.systemui.shared.recents.utilities.RectFEvaluator;
import com.android.systemui.shared.system.ActivityManagerWrapper;
import com.android.systemui.shared.system.InputConsumerController;
import com.android.systemui.shared.system.LatencyTrackerCompat;
@@ -115,27 +131,28 @@
private static final int STATE_APP_CONTROLLER_RECEIVED = 1 << 4;
// Interaction finish states
- private static final int STATE_SCALED_CONTROLLER_RECENTS = 1 << 5;
- private static final int STATE_SCALED_CONTROLLER_LAST_TASK = 1 << 6;
+ private static final int STATE_SCALED_CONTROLLER_HOME = 1 << 5;
+ private static final int STATE_SCALED_CONTROLLER_RECENTS = 1 << 6;
+ private static final int STATE_SCALED_CONTROLLER_LAST_TASK = 1 << 7;
- private static final int STATE_HANDLER_INVALIDATED = 1 << 7;
- private static final int STATE_GESTURE_STARTED_QUICKSTEP = 1 << 8;
- private static final int STATE_GESTURE_STARTED_QUICKSCRUB = 1 << 9;
- private static final int STATE_GESTURE_CANCELLED = 1 << 10;
- private static final int STATE_GESTURE_COMPLETED = 1 << 11;
+ private static final int STATE_HANDLER_INVALIDATED = 1 << 8;
+ private static final int STATE_GESTURE_STARTED_QUICKSTEP = 1 << 9;
+ private static final int STATE_GESTURE_STARTED_QUICKSCRUB = 1 << 10;
+ private static final int STATE_GESTURE_CANCELLED = 1 << 11;
+ private static final int STATE_GESTURE_COMPLETED = 1 << 12;
// States for quick switch/scrub
- private static final int STATE_CURRENT_TASK_FINISHED = 1 << 12;
- private static final int STATE_QUICK_SCRUB_START = 1 << 13;
- private static final int STATE_QUICK_SCRUB_END = 1 << 14;
+ private static final int STATE_CURRENT_TASK_FINISHED = 1 << 13;
+ private static final int STATE_QUICK_SCRUB_START = 1 << 14;
+ private static final int STATE_QUICK_SCRUB_END = 1 << 15;
- private static final int STATE_CAPTURE_SCREENSHOT = 1 << 15;
- private static final int STATE_SCREENSHOT_CAPTURED = 1 << 16;
- private static final int STATE_SCREENSHOT_VIEW_SHOWN = 1 << 17;
+ private static final int STATE_CAPTURE_SCREENSHOT = 1 << 16;
+ private static final int STATE_SCREENSHOT_CAPTURED = 1 << 17;
+ private static final int STATE_SCREENSHOT_VIEW_SHOWN = 1 << 18;
- private static final int STATE_RESUME_LAST_TASK = 1 << 18;
- private static final int STATE_START_NEW_TASK = 1 << 19;
- private static final int STATE_ASSIST_DATA_RECEIVED = 1 << 20;
+ private static final int STATE_RESUME_LAST_TASK = 1 << 19;
+ private static final int STATE_START_NEW_TASK = 1 << 20;
+ private static final int STATE_ASSIST_DATA_RECEIVED = 1 << 21;
private static final int LAUNCHER_UI_STATES =
@@ -160,6 +177,7 @@
"STATE_LAUNCHER_DRAWN",
"STATE_ACTIVITY_MULTIPLIER_COMPLETE",
"STATE_APP_CONTROLLER_RECEIVED",
+ "STATE_SCALED_CONTROLLER_HOME",
"STATE_SCALED_CONTROLLER_RECENTS",
"STATE_SCALED_CONTROLLER_LAST_TASK",
"STATE_HANDLER_INVALIDATED",
@@ -178,6 +196,30 @@
"STATE_ASSIST_DATA_RECEIVED",
};
+ enum GestureEndTarget {
+ HOME(1, STATE_SCALED_CONTROLLER_HOME, true, ContainerType.WORKSPACE),
+
+ RECENTS(1, STATE_SCALED_CONTROLLER_RECENTS | STATE_CAPTURE_SCREENSHOT
+ | STATE_SCREENSHOT_VIEW_SHOWN, true, ContainerType.TASKSWITCHER),
+
+ NEW_TASK(0, STATE_START_NEW_TASK, false, ContainerType.APP),
+
+ LAST_TASK(0, STATE_SCALED_CONTROLLER_LAST_TASK, false, ContainerType.APP);
+
+ GestureEndTarget(float endShift, int endState, boolean isLauncher, int containerType) {
+ this.endShift = endShift;
+ this.endState = endState;
+ this.isLauncher = isLauncher;
+ this.containerType = containerType;
+ }
+
+ // 0 is app, 1 is overview
+ public final float endShift;
+ public final int endState;
+ public final boolean isLauncher;
+ public final int containerType;
+ }
+
public static final long MAX_SWIPE_DURATION = 350;
public static final long MIN_SWIPE_DURATION = 80;
public static final long MIN_OVERSHOOT_DURATION = 120;
@@ -187,11 +229,15 @@
Math.min(1 / MIN_PROGRESS_FOR_OVERVIEW, 1 / (1 - MIN_PROGRESS_FOR_OVERVIEW));
private static final String SCREENSHOT_CAPTURED_EVT = "ScreenshotCaptured";
+ private static final long SHELF_ANIM_DURATION = 120;
+
private final ClipAnimationHelper mClipAnimationHelper;
private final ClipAnimationHelper.TransformParams mTransformParams;
protected Runnable mGestureEndCallback;
protected boolean mIsGoingToRecents;
+ protected boolean mIsGoingToHome;
+ private boolean mIsShelfPeeking;
private DeviceProfile mDp;
private int mTransitionDragLength;
@@ -325,6 +371,13 @@
| STATE_SCALED_CONTROLLER_RECENTS,
this::finishCurrentTransitionToRecents);
+ mStateCallback.addCallback(STATE_LAUNCHER_PRESENT | STATE_GESTURE_COMPLETED
+ | STATE_SCALED_CONTROLLER_HOME | STATE_APP_CONTROLLER_RECEIVED
+ | STATE_ACTIVITY_MULTIPLIER_COMPLETE,
+ this::finishCurrentTransitionToHome);
+ mStateCallback.addCallback(STATE_SCALED_CONTROLLER_HOME | STATE_CURRENT_TASK_FINISHED,
+ this::reset);
+
mStateCallback.addCallback(STATE_LAUNCHER_PRESENT | STATE_APP_CONTROLLER_RECEIVED
| STATE_ACTIVITY_MULTIPLIER_COMPLETE | STATE_SCALED_CONTROLLER_RECENTS
| STATE_CURRENT_TASK_FINISHED | STATE_GESTURE_COMPLETED
@@ -426,7 +479,7 @@
mSyncTransactionApplier = applier;
});
mRecentsView.setOnScrollChangeListener((v, scrollX, scrollY, oldScrollX, oldScrollY) -> {
- if (!mBgLongSwipeMode) {
+ if (!mBgLongSwipeMode && !mIsGoingToHome) {
updateFinalShift();
}
});
@@ -572,23 +625,37 @@
}
@WorkerThread
- public void dispatchMotionEventToRecentsView(MotionEvent event) {
+ @SuppressWarnings("WrongThread")
+ public void dispatchMotionEventToRecentsView(MotionEvent event, @Nullable Float velocityX) {
if (mRecentsView == null) {
return;
}
- // Pass the motion events to RecentsView to allow scrolling during swipe up.
- if (mDispatchedDownEvent) {
- mRecentsView.dispatchTouchEvent(event);
+ if (Looper.myLooper() == mMainThreadHandler.getLooper()) {
+ dispatchMotionEventToRecentsViewUi(event, velocityX);
} else {
+ MotionEvent ev = MotionEvent.obtain(event);
+ postAsyncCallback(mMainThreadHandler, () -> {
+ dispatchMotionEventToRecentsViewUi(ev, velocityX);
+ ev.recycle();
+ });
+ }
+ }
+
+ @UiThread
+ private void dispatchMotionEventToRecentsViewUi(MotionEvent event, @Nullable Float velocityX) {
+ // Pass the motion events to RecentsView to allow scrolling during swipe up.
+ if (!mDispatchedDownEvent) {
// The first event we dispatch should be ACTION_DOWN.
mDispatchedDownEvent = true;
MotionEvent downEvent = MotionEvent.obtain(event);
downEvent.setAction(MotionEvent.ACTION_DOWN);
int flags = downEvent.getEdgeFlags();
downEvent.setEdgeFlags(flags | TouchInteractionService.EDGE_NAV_BAR);
- mRecentsView.dispatchTouchEvent(downEvent);
+ mRecentsView.simulateTouchEvent(downEvent, velocityX);
downEvent.recycle();
}
+
+ mRecentsView.simulateTouchEvent(event, velocityX);
}
@WorkerThread
@@ -598,7 +665,7 @@
if (displacement > mTransitionDragLength && mTransitionDragLength > 0) {
mCurrentShift.updateValue(1);
- if (!mBgLongSwipeMode) {
+ if (!mBgLongSwipeMode && !FeatureFlags.SWIPE_HOME.get()) {
mBgLongSwipeMode = true;
executeOnUiThread(this::onLongSwipeEnabledUi);
}
@@ -615,6 +682,23 @@
}
}
+ public void onMotionPauseChanged(boolean isPaused) {
+ setShelfState(isPaused ? PEEK : HIDE, FAST_OUT_SLOW_IN, SHELF_ANIM_DURATION);
+ }
+
+ public void setShelfState(ShelfAnimState shelfState, Interpolator interpolator, long duration) {
+ if (mInteractionType == INTERACTION_NORMAL) {
+ executeOnUiThread(() -> {
+ mAnimationFactory.setShelfState(shelfState, interpolator, duration);
+ mIsShelfPeeking = shelfState == PEEK;
+ if (mRecentsView != null && shelfState.shouldPreformHaptic) {
+ mRecentsView.performHapticFeedback(HapticFeedbackConstants.VIRTUAL_KEY,
+ HapticFeedbackConstants.FLAG_IGNORE_VIEW_SETTING);
+ }
+ });
+ }
+ }
+
/**
* Called by {@link #mLayoutListener} when launcher layout changes
*/
@@ -676,7 +760,8 @@
final boolean passed = mCurrentShift.value >= MIN_PROGRESS_FOR_OVERVIEW;
if (passed != mPassedOverviewThreshold) {
mPassedOverviewThreshold = passed;
- if (mInteractionType == INTERACTION_NORMAL && mRecentsView != null) {
+ if (mInteractionType == INTERACTION_NORMAL && mRecentsView != null
+ && !SWIPE_HOME.get()) {
mRecentsView.performHapticFeedback(HapticFeedbackConstants.VIRTUAL_KEY,
HapticFeedbackConstants.FLAG_IGNORE_VIEW_SETTING);
}
@@ -702,6 +787,9 @@
}
private void updateLauncherTransitionProgress() {
+ if (mIsGoingToHome) {
+ return;
+ }
float progress = mCurrentShift.value;
mLauncherTransitionController.setPlayFraction(
progress <= mShiftAtGestureStart || mShiftAtGestureStart >= 1
@@ -816,7 +904,7 @@
float velocityXPxPerMs = velocityX / 1000;
long duration = MAX_SWIPE_DURATION;
float currentShift = mCurrentShift.value;
- final boolean goingToRecents;
+ final GestureEndTarget endTarget;
float endShift;
final float startShift;
Interpolator interpolator = DEACCEL;
@@ -825,24 +913,40 @@
boolean goingToNewTask = mRecentsView != null && nextPage != runningTaskIndex;
final boolean reachedOverviewThreshold = currentShift >= MIN_PROGRESS_FOR_OVERVIEW;
if (!isFling) {
- goingToRecents = reachedOverviewThreshold && mGestureStarted;
- endShift = goingToRecents ? 1 : 0;
+ if (SWIPE_HOME.get()) {
+ if (mIsShelfPeeking) {
+ endTarget = RECENTS;
+ } else if (goingToNewTask) {
+ endTarget = NEW_TASK;
+ } else {
+ endTarget = currentShift < MIN_PROGRESS_FOR_OVERVIEW ? LAST_TASK : HOME;
+ }
+ } else {
+ endTarget = reachedOverviewThreshold && mGestureStarted ? RECENTS : LAST_TASK;
+ }
+ endShift = endTarget.endShift;
long expectedDuration = Math.abs(Math.round((endShift - currentShift)
* MAX_SWIPE_DURATION * SWIPE_DURATION_MULTIPLIER));
duration = Math.min(MAX_SWIPE_DURATION, expectedDuration);
startShift = currentShift;
- interpolator = goingToRecents ? OVERSHOOT_1_2 : DEACCEL;
+ interpolator = endTarget == RECENTS ? OVERSHOOT_1_2 : DEACCEL;
} else {
- // If user scrolled to a new task, only go to recents if they already passed
- // the overview threshold. Otherwise, we'll snap to the new task and launch it.
- goingToRecents = endVelocity < 0 && (!goingToNewTask || reachedOverviewThreshold);
- endShift = goingToRecents ? 1 : 0;
+ if (SWIPE_HOME.get() && endVelocity < 0 && !mIsShelfPeeking) {
+ endTarget = HOME;
+ } else if (endVelocity < 0 && (!goingToNewTask || reachedOverviewThreshold)) {
+ // If user scrolled to a new task, only go to recents if they already passed
+ // the overview threshold. Otherwise, we'll snap to the new task and launch it.
+ endTarget = RECENTS;
+ } else {
+ endTarget = goingToNewTask ? NEW_TASK : LAST_TASK;
+ }
+ endShift = endTarget.endShift;
startShift = Utilities.boundToRange(currentShift - velocityPxPerMs
* SINGLE_FRAME_MS / mTransitionDragLength, 0, 1);
float minFlingVelocity = mContext.getResources()
.getDimension(R.dimen.quickstep_fling_min_velocity);
if (Math.abs(endVelocity) > minFlingVelocity && mTransitionDragLength > 0) {
- if (goingToRecents) {
+ if (endTarget == RECENTS) {
Interpolators.OvershootParams overshoot = new Interpolators.OvershootParams(
startShift, endShift, endShift, velocityPxPerMs, mTransitionDragLength);
endShift = overshoot.end;
@@ -860,12 +964,19 @@
}
}
}
- if (goingToRecents) {
+
+ if (endTarget == HOME) {
+ setShelfState(ShelfAnimState.CANCEL, LINEAR, 0);
+ duration = Math.max(MIN_OVERSHOOT_DURATION, duration);
+ } else if (endTarget == RECENTS) {
mRecentsAnimationWrapper.enableTouchProxy();
- } else if (goingToNewTask) {
+ if (SWIPE_HOME.get()) {
+ setShelfState(ShelfAnimState.OVERVIEW, interpolator, duration);
+ }
+ } else if (endTarget == NEW_TASK) {
// We aren't goingToRecents, and user scrolled/flung to a new task; snap to the closest
// task in that direction and launch it (in startNewTask()).
- int taskToLaunch = runningTaskIndex + (nextPage > runningTaskIndex ? 1 : - 1);
+ int taskToLaunch = runningTaskIndex + (nextPage > runningTaskIndex ? 1 : -1);
if (taskToLaunch >= mRecentsView.getTaskViewCount()) {
// Scrolled to Clear all button, snap back to current task and resume it.
mRecentsView.snapToPage(runningTaskIndex, Math.toIntExact(duration));
@@ -882,17 +993,16 @@
duration = Math.max(duration, durationX);
}
}
-
- animateToProgress(startShift, endShift, duration, interpolator, goingToRecents,
- goingToNewTask, velocityPxPerMs);
+ animateToProgress(startShift, endShift, duration, interpolator, endTarget, velocityPxPerMs);
}
- private void doLogGesture(boolean toLauncher) {
+ private void doLogGesture(GestureEndTarget endTarget) {
DeviceProfile dp = mDp;
if (dp == null) {
// We probably never received an animation controller, skip logging.
return;
}
+ boolean toLauncher = endTarget.isLauncher;
final int direction;
if (dp.isVerticalBarLayout()) {
direction = (dp.isSeascape() ^ toLauncher) ? Direction.LEFT : Direction.RIGHT;
@@ -900,60 +1010,92 @@
direction = toLauncher ? Direction.UP : Direction.DOWN;
}
- int dstContainerType = toLauncher ? ContainerType.TASKSWITCHER : ContainerType.APP;
UserEventDispatcher.newInstance(mContext).logStateChangeAction(
mLogAction, direction,
ContainerType.NAVBAR, ContainerType.APP,
- dstContainerType,
+ endTarget.containerType,
0);
}
/** Animates to the given progress, where 0 is the current app and 1 is overview. */
private void animateToProgress(float start, float end, long duration, Interpolator interpolator,
- boolean goingToRecents, boolean goingToNewTask, float velocityPxPerMs) {
+ GestureEndTarget target, float velocityPxPerMs) {
mRecentsAnimationWrapper.runOnInit(() -> animateToProgressInternal(start, end, duration,
- interpolator, goingToRecents, goingToNewTask, velocityPxPerMs));
+ interpolator, target, velocityPxPerMs));
}
private void animateToProgressInternal(float start, float end, long duration,
- Interpolator interpolator, boolean goingToRecents, boolean goingToNewTask,
- float velocityPxPerMs) {
- mIsGoingToRecents = goingToRecents;
- ObjectAnimator anim = mCurrentShift.animateToValue(start, end).setDuration(duration);
- anim.setInterpolator(interpolator);
- anim.addListener(new AnimationSuccessListener() {
+ Interpolator interpolator, GestureEndTarget target, float velocityPxPerMs) {
+ mIsGoingToHome = target == HOME;
+ mIsGoingToRecents = target == RECENTS;
+ ActivityControlHelper.HomeAnimationFactory homeAnimFactory;
+ Animator windowAnim;
+ if (mIsGoingToHome) {
+ if (mActivity != null) {
+ homeAnimFactory = mActivityControlHelper.prepareHomeUI(mActivity);
+ } else {
+ homeAnimFactory = new ActivityControlHelper.HomeAnimationFactory() {
+ @NonNull
+ @Override
+ public RectF getWindowTargetRect() {
+ RectF fallbackTarget = new RectF(mClipAnimationHelper.getTargetRect());
+ Utilities.scaleRectFAboutCenter(fallbackTarget, 0.25f);
+ return fallbackTarget;
+ }
+
+ @NonNull
+ @Override
+ public Animator createActivityAnimationToHome() {
+ return new AnimatorSet();
+ }
+ };
+ mStateCallback.addChangeHandler(STATE_LAUNCHER_PRESENT | STATE_HANDLER_INVALIDATED,
+ isPresent -> mRecentsView.startHome());
+ }
+ windowAnim = createWindowAnimationToHome(start, homeAnimFactory.getWindowTargetRect());
+ mLauncherTransitionController = null;
+ } else {
+ windowAnim = mCurrentShift.animateToValue(start, end);
+ homeAnimFactory = null;
+ }
+ windowAnim.setDuration(duration).setInterpolator(interpolator);
+ windowAnim.addListener(new AnimationSuccessListener() {
@Override
public void onAnimationSuccess(Animator animator) {
- int recentsState = STATE_SCALED_CONTROLLER_RECENTS | STATE_CAPTURE_SCREENSHOT
- | STATE_SCREENSHOT_VIEW_SHOWN;
- setStateOnUiThread(mIsGoingToRecents
- ? recentsState
- : goingToNewTask
- ? STATE_START_NEW_TASK
- : STATE_SCALED_CONTROLLER_LAST_TASK);
+ setStateOnUiThread(target.endState);
}
});
- anim.start();
+ windowAnim.start();
long startMillis = SystemClock.uptimeMillis();
+ // Always play the entire launcher animation when going home, since it is separate from
+ // the animation that has been controlled thus far.
+ final float finalStart = mIsGoingToHome ? 0 : start;
executeOnUiThread(() -> {
// Animate the launcher components at the same time as the window, always on UI thread.
+ // Adjust start progress and duration in case we are on a different thread.
+ long elapsedMillis = SystemClock.uptimeMillis() - startMillis;
+ elapsedMillis = Utilities.boundToRange(elapsedMillis, 0, duration);
+ float elapsedProgress = (float) elapsedMillis / duration;
+ float adjustedStart = Utilities.mapRange(elapsedProgress, finalStart, end);
+ long adjustedDuration = duration - elapsedMillis;
+ // We want to use the same interpolator as the window, but need to adjust it to
+ // interpolate over the remaining progress (end - start).
+ TimeInterpolator adjustedInterpolator = Interpolators.mapToProgress(
+ interpolator, adjustedStart, end);
+ if (homeAnimFactory != null) {
+ Animator homeAnim = homeAnimFactory.createActivityAnimationToHome();
+ homeAnim.setDuration(adjustedDuration).setInterpolator(adjustedInterpolator);
+ homeAnim.start();
+ mLauncherTransitionController = null;
+ }
if (mLauncherTransitionController == null) {
return;
}
- if (start == end || duration <= 0) {
+ if (finalStart == end || duration <= 0) {
mLauncherTransitionController.dispatchSetInterpolator(t -> end);
mLauncherTransitionController.getAnimationPlayer().end();
} else {
- // Adjust start progress and duration in case we are on a different thread.
- long elapsedMillis = SystemClock.uptimeMillis() - startMillis;
- elapsedMillis = Utilities.boundToRange(elapsedMillis, 0, duration);
- float elapsedProgress = (float) elapsedMillis / duration;
- float adjustedStart = Utilities.mapRange(elapsedProgress, start, end);
- long adjustedDuration = duration - elapsedMillis;
- // We want to use the same interpolator as the window, but need to adjust it to
- // interpolate over the remaining progress (end - start).
- mLauncherTransitionController.dispatchSetInterpolator(Interpolators.mapToProgress(
- interpolator, adjustedStart, end));
+ mLauncherTransitionController.dispatchSetInterpolator(adjustedInterpolator);
mLauncherTransitionController.getAnimationPlayer().setDuration(adjustedDuration);
if (QUICKSTEP_SPRINGS.get()) {
@@ -965,10 +1107,49 @@
});
}
+ /**
+ * Creates an Animator that transforms the current app window into the home app.
+ * @param startProgress The progress of {@link #mCurrentShift} to start the window from.
+ * @param endTarget Where to animate the window towards.
+ */
+ private Animator createWindowAnimationToHome(float startProgress, RectF endTarget) {
+ final RemoteAnimationTargetSet targetSet = mRecentsAnimationWrapper.targetSet;
+ RectF startRect = new RectF(mClipAnimationHelper.applyTransform(targetSet,
+ mTransformParams.setProgress(startProgress)));
+ RectF originalTarget = new RectF(mClipAnimationHelper.getTargetRect());
+ final RectF finalTarget = endTarget;
+
+ final RectFEvaluator rectFEvaluator = new RectFEvaluator();
+ final RectF targetRect = new RectF();
+ final RectF currentRect = new RectF();
+
+ ValueAnimator anim = ValueAnimator.ofFloat(0, 1);
+ anim.addUpdateListener(animation -> {
+ float progress = animation.getAnimatedFraction();
+ float interpolatedProgress = Interpolators.ACCEL_2.getInterpolation(progress);
+ // Initially go towards original target (task view in recents),
+ // but accelerate towards the final target.
+ // TODO: This is technically not correct. Instead, motion should continue at
+ // the released velocity but accelerate towards the target.
+ targetRect.set(rectFEvaluator.evaluate(interpolatedProgress,
+ originalTarget, finalTarget));
+ currentRect.set(rectFEvaluator.evaluate(progress, startRect, targetRect));
+ float alpha = 1 - interpolatedProgress;
+ SyncRtSurfaceTransactionApplierCompat syncTransactionApplier
+ = Looper.myLooper() == mMainThreadHandler.getLooper()
+ ? mSyncTransactionApplier
+ : null;
+ mTransformParams.setCurrentRectAndTargetAlpha(currentRect, alpha)
+ .setSyncTransactionApplier(syncTransactionApplier);
+ mClipAnimationHelper.applyTransform(targetSet, mTransformParams);
+ });
+ return anim;
+ }
+
@UiThread
private void resumeLastTaskForQuickstep() {
setStateOnUiThread(STATE_RESUME_LAST_TASK);
- doLogGesture(false /* toLauncher */);
+ doLogGesture(LAST_TASK);
reset();
}
@@ -987,7 +1168,7 @@
mMainThreadHandler);
});
mTouchInteractionLog.finishRecentsAnimation(false);
- doLogGesture(false /* toLauncher */);
+ doLogGesture(NEW_TASK);
}
public void reset() {
@@ -1007,10 +1188,6 @@
mActivityInitListener.unregister();
mTaskSnapshot = null;
-
- if (mRecentsView != null) {
- mRecentsView.setOnScrollChangeListener(null);
- }
}
private void invalidateHandlerWithLauncher() {
@@ -1019,6 +1196,7 @@
mActivityControlHelper.getAlphaProperty(mActivity).setValue(1);
mRecentsView.setRunningTaskIconScaledDown(false);
+ mRecentsView.setOnScrollChangeListener(null);
mQuickScrubController.cancelActiveQuickscrub();
}
@@ -1101,6 +1279,15 @@
mTouchInteractionLog.finishRecentsAnimation(true);
}
+ private void finishCurrentTransitionToHome() {
+ synchronized (mRecentsAnimationWrapper) {
+ mRecentsAnimationWrapper.finish(true /* toRecents */,
+ () -> setStateOnUiThread(STATE_CURRENT_TASK_FINISHED));
+ }
+ mTouchInteractionLog.finishRecentsAnimation(true);
+ doLogGesture(HOME);
+ }
+
private void setupLauncherUiAfterSwipeUpAnimation() {
if (mLauncherTransitionController != null) {
mLauncherTransitionController.getAnimationPlayer().end();
@@ -1114,7 +1301,7 @@
RecentsModel.INSTANCE.get(mContext).onOverviewShown(false, TAG);
- doLogGesture(true /* toLauncher */);
+ doLogGesture(RECENTS);
reset();
}
@@ -1132,8 +1319,7 @@
long duration = FeatureFlags.QUICK_SWITCH.get()
? QUICK_SWITCH_FROM_APP_START_DURATION
: QUICK_SCRUB_FROM_APP_START_DURATION;
- animateToProgress(mCurrentShift.value, 1f, duration, LINEAR, true /* goingToRecents */,
- false /* goingToNewTask */, 1f);
+ animateToProgress(mCurrentShift.value, 1f, duration, LINEAR, RECENTS, 1f);
}
private void onQuickScrubStartUi() {
diff --git a/quickstep/src/com/android/quickstep/fallback/FallbackRecentsView.java b/quickstep/src/com/android/quickstep/fallback/FallbackRecentsView.java
index 261f45d..9679b81 100644
--- a/quickstep/src/com/android/quickstep/fallback/FallbackRecentsView.java
+++ b/quickstep/src/com/android/quickstep/fallback/FallbackRecentsView.java
@@ -39,7 +39,7 @@
}
@Override
- protected void startHome() {
+ public void startHome() {
mActivity.startHome();
}
diff --git a/quickstep/src/com/android/quickstep/util/ClipAnimationHelper.java b/quickstep/src/com/android/quickstep/util/ClipAnimationHelper.java
index 31de683..c612b05 100644
--- a/quickstep/src/com/android/quickstep/util/ClipAnimationHelper.java
+++ b/quickstep/src/com/android/quickstep/util/ClipAnimationHelper.java
@@ -364,8 +364,8 @@
public static class TransformParams {
float progress;
- float offsetX;
- float offsetScale;
+ public float offsetX;
+ public float offsetScale;
@Nullable RectF currentRect;
float targetAlpha;
boolean forLiveTile;
diff --git a/quickstep/src/com/android/quickstep/util/MotionPauseDetector.java b/quickstep/src/com/android/quickstep/util/MotionPauseDetector.java
new file mode 100644
index 0000000..7969eec
--- /dev/null
+++ b/quickstep/src/com/android/quickstep/util/MotionPauseDetector.java
@@ -0,0 +1,145 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.quickstep.util;
+
+import android.content.Context;
+import android.content.res.Resources;
+import android.os.SystemClock;
+import android.view.MotionEvent;
+
+import com.android.launcher3.R;
+
+/**
+ * Given positions along x- or y-axis, tracks velocity and acceleration and determines when there is
+ * a pause in motion.
+ */
+public class MotionPauseDetector {
+
+ // The percentage of the previous speed that determines whether this is a rapid deceleration.
+ // The bigger this number, the easier it is to trigger the first pause.
+ private static final float RAPID_DECELERATION_FACTOR = 0.6f;
+
+ private final float mSpeedVerySlow;
+ private final float mSpeedSomewhatFast;
+ private final float mSpeedFast;
+ private final float mMinDisplacementForPause;
+
+ private Long mPreviousTime = null;
+ private Float mPreviousPosition = null;
+ private Float mPreviousVelocity = null;
+
+ private Float mFirstPosition = null;
+
+ private OnMotionPauseListener mOnMotionPauseListener;
+ private boolean mIsPaused;
+ // Bias more for the first pause to make it feel extra responsive.
+ private boolean mHasEverBeenPaused;
+
+ public MotionPauseDetector(Context context) {
+ Resources res = context.getResources();
+ mSpeedVerySlow = res.getDimension(R.dimen.motion_pause_detector_speed_very_slow);
+ mSpeedSomewhatFast = res.getDimension(R.dimen.motion_pause_detector_speed_somewhat_fast);
+ mSpeedFast = res.getDimension(R.dimen.motion_pause_detector_speed_fast);
+ mMinDisplacementForPause = res.getDimension(R.dimen.motion_pause_detector_min_displacement);
+ }
+
+ /**
+ * Get callbacks for when motion pauses and resumes, including an
+ * immediate callback with the current pause state.
+ */
+ public void setOnMotionPauseListener(OnMotionPauseListener listener) {
+ mOnMotionPauseListener = listener;
+ if (mOnMotionPauseListener != null) {
+ mOnMotionPauseListener.onMotionPauseChanged(mIsPaused);
+ }
+ }
+
+ /**
+ * Computes velocity and acceleration to determine whether the motion is paused.
+ * @param position The x or y component of the motion being tracked.
+ *
+ * TODO: Use historical positions as well, e.g. {@link MotionEvent#getHistoricalY(int, int)}.
+ */
+ public void addPosition(float position) {
+ if (mFirstPosition == null) {
+ mFirstPosition = position;
+ }
+ long time = SystemClock.uptimeMillis();
+ if (mPreviousTime != null && mPreviousPosition != null) {
+ long changeInTime = Math.max(1, time - mPreviousTime);
+ float changeInPosition = position - mPreviousPosition;
+ float velocity = changeInPosition / changeInTime;
+ if (mPreviousVelocity != null) {
+ checkMotionPaused(velocity, mPreviousVelocity, Math.abs(position - mFirstPosition));
+ }
+ mPreviousVelocity = velocity;
+ }
+ mPreviousTime = time;
+ mPreviousPosition = position;
+ }
+
+ private void checkMotionPaused(float velocity, float prevVelocity, float totalDisplacement) {
+ float speed = Math.abs(velocity);
+ float previousSpeed = Math.abs(prevVelocity);
+ boolean isPaused;
+ if (mIsPaused) {
+ // Continue to be paused until moving at a fast speed.
+ isPaused = speed < mSpeedFast || previousSpeed < mSpeedFast;
+ } else {
+ if (velocity < 0 != prevVelocity < 0) {
+ // We're just changing directions, not necessarily stopping.
+ isPaused = false;
+ } else {
+ isPaused = speed < mSpeedVerySlow && previousSpeed < mSpeedVerySlow;
+ if (!isPaused && !mHasEverBeenPaused) {
+ // We want to be more aggressive about detecting the first pause to ensure it
+ // feels as responsive as possible; getting two very slow speeds back to back
+ // takes too long, so also check for a rapid deceleration.
+ boolean isRapidDeceleration = speed < previousSpeed * RAPID_DECELERATION_FACTOR;
+ isPaused = isRapidDeceleration && speed < mSpeedSomewhatFast;
+ }
+ }
+ }
+ boolean passedMinDisplacement = totalDisplacement >= mMinDisplacementForPause;
+ isPaused &= passedMinDisplacement;
+ if (mIsPaused != isPaused) {
+ mIsPaused = isPaused;
+ if (mIsPaused) {
+ mHasEverBeenPaused = true;
+ }
+ if (mOnMotionPauseListener != null) {
+ mOnMotionPauseListener.onMotionPauseChanged(mIsPaused);
+ }
+ }
+ }
+
+ public void clear() {
+ mPreviousTime = null;
+ mPreviousPosition = null;
+ mPreviousVelocity = null;
+ mFirstPosition = null;
+ setOnMotionPauseListener(null);
+ mIsPaused = mHasEverBeenPaused = false;
+ }
+
+ public boolean isPaused() {
+ return mIsPaused;
+ }
+
+ public interface OnMotionPauseListener {
+ void onMotionPauseChanged(boolean isPaused);
+ }
+}
diff --git a/quickstep/src/com/android/quickstep/views/LauncherRecentsView.java b/quickstep/src/com/android/quickstep/views/LauncherRecentsView.java
index f8eced0..ff85003 100644
--- a/quickstep/src/com/android/quickstep/views/LauncherRecentsView.java
+++ b/quickstep/src/com/android/quickstep/views/LauncherRecentsView.java
@@ -92,7 +92,7 @@
}
@Override
- protected void startHome() {
+ public void startHome() {
if (ENABLE_QUICKSTEP_LIVE_TILE.get()) {
takeScreenshotAndFinishRecentsAnimation(true,
() -> mActivity.getStateManager().goToState(NORMAL));
@@ -187,7 +187,7 @@
@Override
public boolean shouldUseMultiWindowTaskSizeStrategy() {
- return mActivity.isInMultiWindowModeCompat();
+ return mActivity.isInMultiWindowMode();
}
@Override
diff --git a/quickstep/src/com/android/quickstep/views/RecentsView.java b/quickstep/src/com/android/quickstep/views/RecentsView.java
index 5cbae65..c6f293d 100644
--- a/quickstep/src/com/android/quickstep/views/RecentsView.java
+++ b/quickstep/src/com/android/quickstep/views/RecentsView.java
@@ -22,6 +22,7 @@
import static com.android.launcher3.anim.Interpolators.ACCEL_2;
import static com.android.launcher3.anim.Interpolators.FAST_OUT_SLOW_IN;
import static com.android.launcher3.anim.Interpolators.LINEAR;
+import static com.android.launcher3.config.FeatureFlags.QUICKSTEP_SPRINGS;
import static com.android.launcher3.config.FeatureFlags.ENABLE_QUICKSTEP_LIVE_TILE;
import static com.android.launcher3.uioverrides.TaskViewTouchController.SUCCESS_TRANSITION_PROGRESS;
import static com.android.quickstep.util.ClipAnimationHelper.TransformParams;
@@ -66,16 +67,19 @@
import android.widget.ListView;
import androidx.annotation.Nullable;
+import androidx.dynamicanimation.animation.SpringForce;
import com.android.launcher3.BaseActivity;
import com.android.launcher3.DeviceProfile;
import com.android.launcher3.Insettable;
import com.android.launcher3.InvariantDeviceProfile;
+import com.android.launcher3.LauncherAnimUtils.ViewProgressProperty;
import com.android.launcher3.PagedView;
import com.android.launcher3.R;
import com.android.launcher3.Utilities;
import com.android.launcher3.anim.AnimatorPlaybackController;
import com.android.launcher3.anim.PropertyListBuilder;
+import com.android.launcher3.anim.SpringObjectAnimator;
import com.android.launcher3.config.FeatureFlags;
import com.android.launcher3.userevent.nano.LauncherLogProto.Action.Direction;
import com.android.launcher3.userevent.nano.LauncherLogProto.Action.Touch;
@@ -113,6 +117,10 @@
private static final String TAG = RecentsView.class.getSimpleName();
+ public static final float SPRING_MIN_VISIBLE_CHANGE = 0.001f;
+ public static final float SPRING_DAMPING_RATIO = SpringForce.DAMPING_RATIO_MEDIUM_BOUNCY;
+ public static final float SPRING_STIFFNESS = SpringForce.STIFFNESS_LOW;
+
public static final FloatProperty<RecentsView> CONTENT_ALPHA =
new FloatProperty<RecentsView>("contentAlpha") {
@Override
@@ -159,6 +167,8 @@
private final ViewPool<TaskView> mTaskViewPool;
+ @Nullable Float mSimulatedVelocityX = null;
+
/**
* TODO: Call reloadIdNeeded in onTaskStackChanged.
*/
@@ -719,7 +729,7 @@
}
}
- protected abstract void startHome();
+ public abstract void startHome();
public void reset() {
setRunningTaskViewShowScreenshot(false);
@@ -939,8 +949,15 @@
private void addDismissedTaskAnimations(View taskView, AnimatorSet anim, long duration) {
addAnim(ObjectAnimator.ofFloat(taskView, ALPHA, 0), duration, ACCEL_2, anim);
- addAnim(ObjectAnimator.ofFloat(taskView, TRANSLATION_Y, -taskView.getHeight()),
- duration, LINEAR, anim);
+ if (QUICKSTEP_SPRINGS.get() && taskView instanceof TaskView)
+ addAnim(new SpringObjectAnimator<>(new ViewProgressProperty(taskView,
+ View.TRANSLATION_Y), "taskViewTransY", SPRING_MIN_VISIBLE_CHANGE,
+ SPRING_DAMPING_RATIO, SPRING_STIFFNESS, 0, -taskView.getHeight()),
+ duration, LINEAR, anim);
+ else {
+ addAnim(ObjectAnimator.ofFloat(taskView, TRANSLATION_Y, -taskView.getHeight()),
+ duration, LINEAR, anim);
+ }
}
private void removeTask(Task task, int index, PendingAnimation.OnEndListener onEndListener,
@@ -1010,8 +1027,16 @@
}
int scrollDiff = newScroll[i] - oldScroll[i] + offset;
if (scrollDiff != 0) {
- addAnim(ObjectAnimator.ofFloat(child, TRANSLATION_X, scrollDiff),
- duration, ACCEL, anim);
+ if (QUICKSTEP_SPRINGS.get() && child instanceof TaskView) {
+ addAnim(new SpringObjectAnimator<>(
+ new ViewProgressProperty(child, View.TRANSLATION_X),
+ "taskViewTransX", SPRING_MIN_VISIBLE_CHANGE, SPRING_DAMPING_RATIO,
+ SPRING_STIFFNESS, 0, scrollDiff), duration, ACCEL, anim);
+ } else {
+ addAnim(ObjectAnimator.ofFloat(child, TRANSLATION_X, scrollDiff), duration,
+ ACCEL, anim);
+ }
+
needsCurveUpdates = true;
}
}
@@ -1092,7 +1117,7 @@
return pendingAnimation;
}
- private static void addAnim(ObjectAnimator anim, long duration,
+ private static void addAnim(Animator anim, long duration,
TimeInterpolator interpolator, AnimatorSet set) {
anim.setDuration(duration).setInterpolator(interpolator);
set.play(anim);
@@ -1610,4 +1635,18 @@
}
}
}
+
+ public void simulateTouchEvent(MotionEvent event, @Nullable Float velocityX) {
+ mSimulatedVelocityX = velocityX;
+ dispatchTouchEvent(event);
+ mSimulatedVelocityX = null;
+ }
+
+ @Override
+ protected int computeXVelocity() {
+ if (mSimulatedVelocityX != null) {
+ return mSimulatedVelocityX.intValue();
+ }
+ return super.computeXVelocity();
+ }
}
diff --git a/quickstep/src/com/android/quickstep/views/ShelfScrimView.java b/quickstep/src/com/android/quickstep/views/ShelfScrimView.java
index d2b3bcc..fe05c4f 100644
--- a/quickstep/src/com/android/quickstep/views/ShelfScrimView.java
+++ b/quickstep/src/com/android/quickstep/views/ShelfScrimView.java
@@ -15,6 +15,7 @@
*/
package com.android.quickstep.views;
+import static com.android.launcher3.LauncherState.BACKGROUND_APP;
import static com.android.launcher3.LauncherState.OVERVIEW;
import static com.android.launcher3.anim.Interpolators.ACCEL;
import static com.android.launcher3.anim.Interpolators.LINEAR;
@@ -33,6 +34,7 @@
import com.android.launcher3.R;
import com.android.launcher3.Utilities;
import com.android.launcher3.anim.Interpolators;
+import com.android.launcher3.config.FeatureFlags;
import com.android.launcher3.util.Themes;
import com.android.launcher3.views.ScrimView;
@@ -135,6 +137,11 @@
if (mProgress >= 1) {
mRemainingScreenColor = 0;
mShelfColor = 0;
+ if (FeatureFlags.SWIPE_HOME.get()
+ && mLauncher.getStateManager().getState() == BACKGROUND_APP) {
+ // Show the shelf background when peeking during swipe up.
+ mShelfColor = setColorAlphaBound(mEndScrim, mMidAlpha);
+ }
} else if (mProgress >= mMidProgress) {
mRemainingScreenColor = 0;
diff --git a/quickstep/src/com/android/quickstep/views/TaskThumbnailView.java b/quickstep/src/com/android/quickstep/views/TaskThumbnailView.java
index 8169d73..d0289d0 100644
--- a/quickstep/src/com/android/quickstep/views/TaskThumbnailView.java
+++ b/quickstep/src/com/android/quickstep/views/TaskThumbnailView.java
@@ -291,7 +291,7 @@
// Rotate the screenshot if not in multi-window mode
mIsRotated = FeatureFlags.OVERVIEW_USE_SCREENSHOT_ORIENTATION &&
configuration.orientation != mThumbnailData.orientation &&
- !mActivity.isInMultiWindowModeCompat() &&
+ !mActivity.isInMultiWindowMode() &&
mThumbnailData.windowingMode == WINDOWING_MODE_FULLSCREEN;
// Scale the screenshot to always fit the width of the card.
thumbnailScale = mIsRotated
diff --git a/quickstep/tests/src/com/android/quickstep/TaplTests.java b/quickstep/tests/src/com/android/quickstep/TaplTests.java
deleted file mode 100644
index 347b7ac..0000000
--- a/quickstep/tests/src/com/android/quickstep/TaplTests.java
+++ /dev/null
@@ -1,462 +0,0 @@
-/*
- * Copyright (C) 2018 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.quickstep;
-
-import static androidx.test.InstrumentationRegistry.getInstrumentation;
-
-import static org.junit.Assert.assertEquals;
-import static org.junit.Assert.assertFalse;
-import static org.junit.Assert.assertNotNull;
-import static org.junit.Assert.assertNull;
-import static org.junit.Assert.assertTrue;
-
-import android.content.Intent;
-import android.os.RemoteException;
-import android.util.Log;
-
-import androidx.test.filters.LargeTest;
-import androidx.test.runner.AndroidJUnit4;
-import androidx.test.uiautomator.By;
-import androidx.test.uiautomator.Until;
-
-import com.android.launcher3.Launcher;
-import com.android.launcher3.LauncherState;
-import com.android.launcher3.tapl.AllApps;
-import com.android.launcher3.tapl.AllAppsFromOverview;
-import com.android.launcher3.tapl.AppIcon;
-import com.android.launcher3.tapl.Background;
-import com.android.launcher3.tapl.Overview;
-import com.android.launcher3.tapl.OverviewTask;
-import com.android.launcher3.tapl.TestHelpers;
-import com.android.launcher3.tapl.Widgets;
-import com.android.launcher3.tapl.Workspace;
-import com.android.launcher3.views.OptionsPopupView;
-import com.android.launcher3.widget.WidgetsFullSheet;
-import com.android.launcher3.widget.WidgetsRecyclerView;
-import com.android.quickstep.QuickStepOnOffRule.QuickstepOnOff;
-import com.android.quickstep.views.RecentsView;
-
-import org.junit.Before;
-import org.junit.Rule;
-import org.junit.Test;
-import org.junit.rules.TestWatcher;
-import org.junit.runner.Description;
-import org.junit.runner.RunWith;
-
-import java.io.ByteArrayOutputStream;
-import java.io.File;
-import java.io.IOException;
-
-@LargeTest
-@RunWith(AndroidJUnit4.class)
-public class TaplTests extends AbstractQuickStepTest {
- private static final String TAG = "TaplTests";
-
- private static int sScreenshotCount = 0;
-
- @Rule
- public TestWatcher mFailureWatcher = new TestWatcher() {
- private void dumpViewHierarchy() {
- final ByteArrayOutputStream stream = new ByteArrayOutputStream();
- try {
- mDevice.dumpWindowHierarchy(stream);
- stream.flush();
- stream.close();
- for (String line : stream.toString().split("\\r?\\n")) {
- Log.e(TaplTests.TAG, line.trim());
- }
- } catch (IOException e) {
- Log.e(TaplTests.TAG, "error dumping XML to logcat", e);
- }
- }
-
- @Override
- protected void failed(Throwable e, Description description) {
- if (mDevice == null) return;
- final String pathname = getInstrumentation().getTargetContext().
- getFilesDir().getPath() + "/TaplTestScreenshot" + sScreenshotCount++ + ".png";
- Log.e(TaplTests.TAG, "Failed test " + description.getMethodName() +
- ", screenshot will be saved to " + pathname +
- ", track trace is below, UI object dump is further below:\n" +
- Log.getStackTraceString(e));
- dumpViewHierarchy();
- mDevice.takeScreenshot(new File(pathname));
- }
- };
-
- @Before
- public void setUp() throws Exception {
- super.setUp();
-
- clearLauncherData();
-
- mLauncher.pressHome();
- waitForState("Launcher internal state didn't switch to Home", LauncherState.NORMAL);
- waitForResumed("Launcher internal state is still Background");
- }
-
- private boolean isInState(LauncherState state) {
- if (!TestHelpers.isInLauncherProcess()) return true;
- return getFromLauncher(launcher -> launcher.getStateManager().getState() == state);
- }
-
- // Please don't add negative test cases for methods that fail only after a long wait.
- private void expectFail(String message, Runnable action) {
- boolean failed = false;
- try {
- action.run();
- } catch (AssertionError e) {
- failed = true;
- }
- assertTrue(message, failed);
- }
-
- private boolean isWorkspaceScrollable(Launcher launcher) {
- return launcher.getWorkspace().getPageCount() > 1;
- }
-
- private boolean isInBackground(Launcher launcher) {
- return !launcher.hasBeenResumed();
- }
-
- private void startTestApps() throws Exception {
- startAppFast(resolveSystemApp(Intent.CATEGORY_APP_MESSAGING));
- startAppFast(resolveSystemApp(Intent.CATEGORY_APP_CALCULATOR));
- startAppFast(resolveSystemApp(Intent.CATEGORY_APP_CONTACTS));
-
- executeOnLauncher(launcher -> assertTrue(
- "Launcher activity is the top activity; expecting another activity to be the top "
- + "one",
- isInBackground(launcher)));
- }
-
- private int getCurrentWorkspacePage(Launcher launcher) {
- return launcher.getWorkspace().getCurrentPage();
- }
-
- private WidgetsRecyclerView getWidgetsView(Launcher launcher) {
- return WidgetsFullSheet.getWidgetsView(launcher);
- }
-
- @Test
- public void testDevicePressMenu() throws Exception {
- mDevice.pressMenu();
- mDevice.waitForIdle();
- executeOnLauncher(
- launcher -> assertTrue("Launcher internal state didn't switch to Showing Menu",
- OptionsPopupView.getOptionsPopup(launcher) != null));
- }
-
- @Test
- @PortraitLandscape
- public void testPressRecentAppsLauncherAndGetOverview() throws RemoteException {
- mDevice.pressRecentApps();
- waitForState("Launcher internal state didn't switch to Overview", LauncherState.OVERVIEW);
-
- assertNotNull("getOverview() returned null", mLauncher.getOverview());
- }
-
- private void runAllAppsTest(AllApps allApps) throws Exception {
- assertNotNull("allApps parameter is null", allApps);
-
- assertTrue("Launcher internal state is not All Apps", isInState(LauncherState.ALL_APPS));
-
- // Test flinging forward and backward.
- executeOnLauncher(launcher -> assertEquals("All Apps started in already scrolled state", 0,
- getAllAppsScroll(launcher)));
-
- allApps.flingForward();
- assertTrue("Launcher internal state is not All Apps", isInState(LauncherState.ALL_APPS));
- final Integer flingForwardY = getFromLauncher(launcher -> getAllAppsScroll(launcher));
- executeOnLauncher(
- launcher -> assertTrue("flingForward() didn't scroll App Apps", flingForwardY > 0));
-
- allApps.flingBackward();
- assertTrue("Launcher internal state is not All Apps", isInState(LauncherState.ALL_APPS));
- final Integer flingBackwardY = getFromLauncher(launcher -> getAllAppsScroll(launcher));
- executeOnLauncher(launcher -> assertTrue("flingBackward() didn't scroll App Apps",
- flingBackwardY < flingForwardY));
-
- // Test scrolling down to YouTube.
- assertNotNull("All apps: can't fine YouTube", allApps.getAppIcon("YouTube"));
- // Test scrolling up to Camera.
- assertNotNull("All apps: can't fine Camera", allApps.getAppIcon("Camera"));
- // Test failing to find a non-existing app.
- final AllApps allAppsFinal = allApps;
- expectFail("All apps: could find a non-existing app",
- () -> allAppsFinal.getAppIcon("NO APP"));
-
- assertTrue("Launcher internal state is not All Apps", isInState(LauncherState.ALL_APPS));
- }
-
- private int getAllAppsScroll(Launcher launcher) {
- return launcher.getAppsView().getActiveRecyclerView().getCurrentScrollY();
- }
-
- @Test
- @PortraitLandscape
- public void testAllAppsFromHome() throws Exception {
- // Test opening all apps
- assertNotNull("switchToAllApps() returned null",
- mLauncher.getWorkspace().switchToAllApps());
-
- runAllAppsTest(mLauncher.getAllApps());
-
- // Testing pressHome.
- assertTrue("Launcher internal state is not All Apps", isInState(LauncherState.ALL_APPS));
- assertNotNull("pressHome returned null", mLauncher.pressHome());
- assertTrue("Launcher internal state is not Home", isInState(LauncherState.NORMAL));
- assertNotNull("getHome returned null", mLauncher.getWorkspace());
- }
-
- @Test
- @QuickstepOnOff
- @PortraitLandscape
- public void testWorkspaceSwitchToAllApps() {
- assertNotNull("switchToAllApps() returned null",
- mLauncher.getWorkspace().switchToAllApps());
- assertTrue("Launcher internal state is not All Apps", isInState(LauncherState.ALL_APPS));
- }
-
- @Test
- public void testAllAppsFromOverview() throws Exception {
- // Test opening all apps from Overview.
- assertNotNull("switchToAllApps() returned null",
- mLauncher.getWorkspace().switchToOverview().switchToAllApps());
-
- runAllAppsTest(mLauncher.getAllAppsFromOverview());
- }
-
- @Test
- public void testWorkspace() throws Exception {
- final Workspace workspace = mLauncher.getWorkspace();
-
- // Test that ensureWorkspaceIsScrollable adds a page by dragging an icon there.
- executeOnLauncher(launcher -> assertFalse("Initial workspace state is scrollable",
- isWorkspaceScrollable(launcher)));
- assertNull("Messages app was found on empty workspace",
- workspace.tryGetWorkspaceAppIcon("Messages"));
-
- workspace.ensureWorkspaceIsScrollable();
-
- executeOnLauncher(
- launcher -> assertEquals("Ensuring workspace scrollable didn't switch to page #1",
- 1, getCurrentWorkspacePage(launcher)));
- executeOnLauncher(
- launcher -> assertTrue("ensureScrollable didn't make workspace scrollable",
- isWorkspaceScrollable(launcher)));
- assertNotNull("ensureScrollable didn't add Messages app",
- workspace.tryGetWorkspaceAppIcon("Messages"));
-
- // Test flinging workspace.
- workspace.flingBackward();
- assertTrue("Launcher internal state is not Home", isInState(LauncherState.NORMAL));
- executeOnLauncher(
- launcher -> assertEquals("Flinging back didn't switch workspace to page #0",
- 0, getCurrentWorkspacePage(launcher)));
-
- workspace.flingForward();
- executeOnLauncher(
- launcher -> assertEquals("Flinging forward didn't switch workspace to page #1",
- 1, getCurrentWorkspacePage(launcher)));
- assertTrue("Launcher internal state is not Home", isInState(LauncherState.NORMAL));
-
- // Test starting a workspace app.
- final AppIcon app = workspace.tryGetWorkspaceAppIcon("Messages");
- assertNotNull("No Messages app in workspace", app);
- assertNotNull("AppIcon.launch returned null",
- app.launch(resolveSystemApp(Intent.CATEGORY_APP_MESSAGING)));
- executeOnLauncher(launcher -> assertTrue(
- "Launcher activity is the top activity; expecting another activity to be the top "
- + "one",
- isInBackground(launcher)));
- }
-
- @Test
- @PortraitLandscape
- public void testOverview() throws Exception {
- startTestApps();
- Overview overview = mLauncher.pressHome().switchToOverview();
- assertTrue("Launcher internal state didn't switch to Overview",
- isInState(LauncherState.OVERVIEW));
- executeOnLauncher(
- launcher -> assertTrue("Don't have at least 3 tasks", getTaskCount(launcher) >= 3));
-
- // Test flinging forward and backward.
- executeOnLauncher(launcher -> assertEquals("Current task in Overview is not 0",
- 0, getCurrentOverviewPage(launcher)));
-
- overview.flingForward();
- assertTrue("Launcher internal state is not Overview", isInState(LauncherState.OVERVIEW));
- final Integer currentTaskAfterFlingForward = getFromLauncher(
- launcher -> getCurrentOverviewPage(launcher));
- executeOnLauncher(launcher -> assertTrue("Current task in Overview is still 0",
- currentTaskAfterFlingForward > 0));
-
- overview.flingBackward();
- assertTrue("Launcher internal state is not Overview", isInState(LauncherState.OVERVIEW));
- executeOnLauncher(launcher -> assertTrue("Flinging back in Overview did nothing",
- getCurrentOverviewPage(launcher) < currentTaskAfterFlingForward));
-
- // Test opening a task.
- OverviewTask task = mLauncher.pressHome().switchToOverview().getCurrentTask();
- assertNotNull("overview.getCurrentTask() returned null (1)", task);
- assertNotNull("OverviewTask.open returned null", task.open());
- assertTrue("Contacts app didn't open from Overview", mDevice.wait(Until.hasObject(
- By.pkg(resolveSystemApp(Intent.CATEGORY_APP_CONTACTS)).depth(0)),
- LONG_WAIT_TIME_MS));
- executeOnLauncher(launcher -> assertTrue(
- "Launcher activity is the top activity; expecting another activity to be the top "
- + "one",
- isInBackground(launcher)));
-
- // Test dismissing a task.
- overview = mLauncher.pressHome().switchToOverview();
- assertTrue("Launcher internal state didn't switch to Overview",
- isInState(LauncherState.OVERVIEW));
- final Integer numTasks = getFromLauncher(launcher -> getTaskCount(launcher));
- task = overview.getCurrentTask();
- assertNotNull("overview.getCurrentTask() returned null (2)", task);
- task.dismiss();
- executeOnLauncher(
- launcher -> assertEquals("Dismissing a task didn't remove 1 task from Overview",
- numTasks - 1, getTaskCount(launcher)));
-
- if (!TestHelpers.isInLauncherProcess() ||
- getFromLauncher(launcher -> !launcher.getDeviceProfile().isLandscape)) {
- // Test switching to all apps and back.
- final AllAppsFromOverview allApps = overview.switchToAllApps();
- assertNotNull("overview.switchToAllApps() returned null (1)", allApps);
- assertTrue("Launcher internal state is not All Apps (1)",
- isInState(LauncherState.ALL_APPS));
-
- overview = allApps.switchBackToOverview();
- assertNotNull("allApps.switchBackToOverview() returned null", overview);
- assertTrue("Launcher internal state didn't switch to Overview",
- isInState(LauncherState.OVERVIEW));
-
- // Test UIDevice.pressBack()
- overview.switchToAllApps();
- assertNotNull("overview.switchToAllApps() returned null (2)", allApps);
- assertTrue("Launcher internal state is not All Apps (2)",
- isInState(LauncherState.ALL_APPS));
- mDevice.pressBack();
- mLauncher.getOverview();
- }
-
- // Test UIDevice.pressHome, once we are in AllApps.
- mDevice.pressHome();
- waitForState("Launcher internal state didn't switch to Home", LauncherState.NORMAL);
- }
-
- private int getCurrentOverviewPage(Launcher launcher) {
- return launcher.<RecentsView>getOverviewPanel().getCurrentPage();
- }
-
- private int getTaskCount(Launcher launcher) {
- return launcher.<RecentsView>getOverviewPanel().getTaskViewCount();
- }
-
- private void runIconLaunchFromAllAppsTest(AllApps allApps) throws Exception {
- final AppIcon app = allApps.getAppIcon("Calculator");
- assertNotNull("AppIcon.launch returned null", app.launch(
- resolveSystemApp(Intent.CATEGORY_APP_CALCULATOR)));
- executeOnLauncher(launcher -> assertTrue(
- "Launcher activity is the top activity; expecting another activity to be the top "
- + "one",
- isInBackground(launcher)));
- }
-
- @Test
- @PortraitLandscape
- public void testAppIconLaunchFromAllAppsFromHome() throws Exception {
- final AllApps allApps = mLauncher.getWorkspace().switchToAllApps();
- assertTrue("Launcher internal state is not All Apps", isInState(LauncherState.ALL_APPS));
-
- runIconLaunchFromAllAppsTest(allApps);
- }
-
- @Test
- public void testAppIconLaunchFromAllAppsFromOverview() throws Exception {
- final AllApps allApps =
- mLauncher.getWorkspace().switchToOverview().switchToAllApps();
- assertTrue("Launcher internal state is not All Apps", isInState(LauncherState.ALL_APPS));
-
- runIconLaunchFromAllAppsTest(allApps);
- }
-
- @Test
- @PortraitLandscape
- public void testWidgets() throws Exception {
- // Test opening widgets.
- executeOnLauncher(launcher ->
- assertTrue("Widgets is initially opened", getWidgetsView(launcher) == null));
- Widgets widgets = mLauncher.getWorkspace().openAllWidgets();
- assertNotNull("openAllWidgets() returned null", widgets);
- widgets = mLauncher.getAllWidgets();
- assertNotNull("getAllWidgets() returned null", widgets);
- executeOnLauncher(launcher ->
- assertTrue("Widgets is not shown", getWidgetsView(launcher).isShown()));
- executeOnLauncher(launcher -> assertEquals("Widgets is scrolled upon opening",
- 0, getWidgetsScroll(launcher)));
-
- // Test flinging widgets.
- widgets.flingForward();
- Integer flingForwardY = getFromLauncher(launcher -> getWidgetsScroll(launcher));
- executeOnLauncher(launcher -> assertTrue("Flinging forward didn't scroll widgets",
- flingForwardY > 0));
-
- widgets.flingBackward();
- executeOnLauncher(launcher -> assertTrue("Flinging backward didn't scroll widgets",
- getWidgetsScroll(launcher) < flingForwardY));
-
- mLauncher.pressHome();
- waitForLauncherCondition("Widgets were not closed",
- launcher -> getWidgetsView(launcher) == null);
- }
-
- private int getWidgetsScroll(Launcher launcher) {
- return getWidgetsView(launcher).getCurrentScrollY();
- }
-
- @Test
- @QuickstepOnOff
- @PortraitLandscape
- public void testSwitchToOverview() throws Exception {
- assertNotNull("Workspace.switchToOverview() returned null",
- mLauncher.pressHome().switchToOverview());
- assertTrue("Launcher internal state didn't switch to Overview",
- isInState(LauncherState.OVERVIEW));
- }
-
- @Test
- @QuickstepOnOff
- @PortraitLandscape
- public void testBackground() throws Exception {
- startAppFast(resolveSystemApp(Intent.CATEGORY_APP_CALCULATOR));
- final Background background = mLauncher.getBackground();
- assertNotNull("Launcher.getBackground() returned null", background);
- executeOnLauncher(launcher -> assertTrue(
- "Launcher activity is the top activity; expecting another activity to be the top "
- + "one",
- isInBackground(launcher)));
-
- assertNotNull("Background.switchToOverview() returned null", background.switchToOverview());
- assertTrue("Launcher internal state didn't switch to Overview",
- isInState(LauncherState.OVERVIEW));
- }
-}
diff --git a/quickstep/tests/src/com/android/quickstep/TaplTestsQuickstep.java b/quickstep/tests/src/com/android/quickstep/TaplTestsQuickstep.java
new file mode 100644
index 0000000..3008c0b
--- /dev/null
+++ b/quickstep/tests/src/com/android/quickstep/TaplTestsQuickstep.java
@@ -0,0 +1,240 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.quickstep;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertTrue;
+
+import android.content.Intent;
+import android.os.RemoteException;
+
+import androidx.test.filters.LargeTest;
+import androidx.test.runner.AndroidJUnit4;
+import androidx.test.uiautomator.By;
+import androidx.test.uiautomator.Until;
+
+import com.android.launcher3.Launcher;
+import com.android.launcher3.LauncherState;
+import com.android.launcher3.tapl.AllApps;
+import com.android.launcher3.tapl.AllAppsFromOverview;
+import com.android.launcher3.tapl.Background;
+import com.android.launcher3.tapl.Overview;
+import com.android.launcher3.tapl.OverviewTask;
+import com.android.launcher3.tapl.TestHelpers;
+import com.android.launcher3.ui.TaplTestsLauncher3;
+import com.android.quickstep.QuickStepOnOffRule.QuickstepOnOff;
+import com.android.quickstep.views.RecentsView;
+
+import org.junit.Before;
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.rules.TestWatcher;
+import org.junit.runner.RunWith;
+
+@LargeTest
+@RunWith(AndroidJUnit4.class)
+public class TaplTestsQuickstep extends AbstractQuickStepTest {
+ @Rule
+ public TestWatcher mFailureWatcher = new TaplTestsLauncher3.FailureWatcher(mDevice);
+
+ @Before
+ public void setUp() throws Exception {
+ super.setUp();
+
+ clearLauncherData();
+
+ mLauncher.pressHome();
+ waitForState("Launcher internal state didn't switch to Home", LauncherState.NORMAL);
+ waitForResumed("Launcher internal state is still Background");
+ }
+
+ private void startTestApps() throws Exception {
+ startAppFast(resolveSystemApp(Intent.CATEGORY_APP_MESSAGING));
+ startAppFast(resolveSystemApp(Intent.CATEGORY_APP_CALCULATOR));
+ startAppFast(resolveSystemApp(Intent.CATEGORY_APP_CONTACTS));
+
+ executeOnLauncher(launcher -> assertTrue(
+ "Launcher activity is the top activity; expecting another activity to be the top "
+ + "one",
+ isInBackground(launcher)));
+ }
+
+ @Test
+ @PortraitLandscape
+ public void testPressRecentAppsLauncherAndGetOverview() throws RemoteException {
+ mDevice.pressRecentApps();
+ waitForState("Launcher internal state didn't switch to Overview", LauncherState.OVERVIEW);
+
+ assertNotNull("getOverview() returned null", mLauncher.getOverview());
+ }
+
+ @Test
+ @QuickstepOnOff
+ @PortraitLandscape
+ public void testWorkspaceSwitchToAllApps() {
+ assertNotNull("switchToAllApps() returned null",
+ mLauncher.getWorkspace().switchToAllApps());
+ assertTrue("Launcher internal state is not All Apps", isInState(LauncherState.ALL_APPS));
+ }
+
+ @Test
+ public void testAllAppsFromOverview() throws Exception {
+ // Test opening all apps from Overview.
+ assertNotNull("switchToAllApps() returned null",
+ mLauncher.getWorkspace().switchToOverview().switchToAllApps());
+
+ TaplTestsLauncher3.runAllAppsTest(this, mLauncher.getAllAppsFromOverview());
+ }
+
+ @Test
+ @PortraitLandscape
+ public void testOverview() throws Exception {
+ startTestApps();
+ Overview overview = mLauncher.pressHome().switchToOverview();
+ assertTrue("Launcher internal state didn't switch to Overview",
+ isInState(LauncherState.OVERVIEW));
+ executeOnLauncher(
+ launcher -> assertTrue("Don't have at least 3 tasks", getTaskCount(launcher) >= 3));
+
+ // Test flinging forward and backward.
+ executeOnLauncher(launcher -> assertEquals("Current task in Overview is not 0",
+ 0, getCurrentOverviewPage(launcher)));
+
+ overview.flingForward();
+ assertTrue("Launcher internal state is not Overview", isInState(LauncherState.OVERVIEW));
+ final Integer currentTaskAfterFlingForward = getFromLauncher(
+ launcher -> getCurrentOverviewPage(launcher));
+ executeOnLauncher(launcher -> assertTrue("Current task in Overview is still 0",
+ currentTaskAfterFlingForward > 0));
+
+ overview.flingBackward();
+ assertTrue("Launcher internal state is not Overview", isInState(LauncherState.OVERVIEW));
+ executeOnLauncher(launcher -> assertTrue("Flinging back in Overview did nothing",
+ getCurrentOverviewPage(launcher) < currentTaskAfterFlingForward));
+
+ // Test opening a task.
+ OverviewTask task = mLauncher.pressHome().switchToOverview().getCurrentTask();
+ assertNotNull("overview.getCurrentTask() returned null (1)", task);
+ assertNotNull("OverviewTask.open returned null", task.open());
+ assertTrue("Contacts app didn't open from Overview", mDevice.wait(Until.hasObject(
+ By.pkg(resolveSystemApp(Intent.CATEGORY_APP_CONTACTS)).depth(0)),
+ LONG_WAIT_TIME_MS));
+ executeOnLauncher(launcher -> assertTrue(
+ "Launcher activity is the top activity; expecting another activity to be the top "
+ + "one",
+ isInBackground(launcher)));
+
+ // Test dismissing a task.
+ overview = mLauncher.pressHome().switchToOverview();
+ assertTrue("Launcher internal state didn't switch to Overview",
+ isInState(LauncherState.OVERVIEW));
+ final Integer numTasks = getFromLauncher(launcher -> getTaskCount(launcher));
+ task = overview.getCurrentTask();
+ assertNotNull("overview.getCurrentTask() returned null (2)", task);
+ task.dismiss();
+ executeOnLauncher(
+ launcher -> assertEquals("Dismissing a task didn't remove 1 task from Overview",
+ numTasks - 1, getTaskCount(launcher)));
+
+ if (!TestHelpers.isInLauncherProcess() ||
+ getFromLauncher(launcher -> !launcher.getDeviceProfile().isLandscape)) {
+ // Test switching to all apps and back.
+ final AllAppsFromOverview allApps = overview.switchToAllApps();
+ assertNotNull("overview.switchToAllApps() returned null (1)", allApps);
+ assertTrue("Launcher internal state is not All Apps (1)",
+ isInState(LauncherState.ALL_APPS));
+
+ overview = allApps.switchBackToOverview();
+ assertNotNull("allApps.switchBackToOverview() returned null", overview);
+ assertTrue("Launcher internal state didn't switch to Overview",
+ isInState(LauncherState.OVERVIEW));
+
+ // Test UIDevice.pressBack()
+ overview.switchToAllApps();
+ assertNotNull("overview.switchToAllApps() returned null (2)", allApps);
+ assertTrue("Launcher internal state is not All Apps (2)",
+ isInState(LauncherState.ALL_APPS));
+ mDevice.pressBack();
+ mLauncher.getOverview();
+ }
+
+ // Test UIDevice.pressHome, once we are in AllApps.
+ mDevice.pressHome();
+ waitForState("Launcher internal state didn't switch to Home", LauncherState.NORMAL);
+ }
+
+ private int getCurrentOverviewPage(Launcher launcher) {
+ return launcher.<RecentsView>getOverviewPanel().getCurrentPage();
+ }
+
+ private int getTaskCount(Launcher launcher) {
+ return launcher.<RecentsView>getOverviewPanel().getTaskViewCount();
+ }
+
+ @Test
+ public void testAppIconLaunchFromAllAppsFromOverview() throws Exception {
+ final AllApps allApps =
+ mLauncher.getWorkspace().switchToOverview().switchToAllApps();
+ assertTrue("Launcher internal state is not All Apps", isInState(LauncherState.ALL_APPS));
+
+ TaplTestsLauncher3.runIconLaunchFromAllAppsTest(this, allApps);
+ }
+
+ @Test
+ @QuickstepOnOff
+ @PortraitLandscape
+ public void testSwitchToOverview() throws Exception {
+ assertNotNull("Workspace.switchToOverview() returned null",
+ mLauncher.pressHome().switchToOverview());
+ assertTrue("Launcher internal state didn't switch to Overview",
+ isInState(LauncherState.OVERVIEW));
+ }
+
+ @Test
+ @QuickstepOnOff
+ @PortraitLandscape
+ public void testBackground() throws Exception {
+ startAppFast(resolveSystemApp(Intent.CATEGORY_APP_CALCULATOR));
+ final Background background = mLauncher.getBackground();
+ assertNotNull("Launcher.getBackground() returned null", background);
+ executeOnLauncher(launcher -> assertTrue(
+ "Launcher activity is the top activity; expecting another activity to be the top "
+ + "one",
+ isInBackground(launcher)));
+
+ assertNotNull("Background.switchToOverview() returned null", background.switchToOverview());
+ assertTrue("Launcher internal state didn't switch to Overview",
+ isInState(LauncherState.OVERVIEW));
+ }
+
+ @Test
+ @PortraitLandscape
+ public void testAllAppsFromHome() throws Exception {
+ // Test opening all apps
+ assertNotNull("switchToAllApps() returned null",
+ mLauncher.getWorkspace().switchToAllApps());
+
+ TaplTestsLauncher3.runAllAppsTest(this, mLauncher.getAllApps());
+
+ // Testing pressHome.
+ assertTrue("Launcher internal state is not All Apps", isInState(LauncherState.ALL_APPS));
+ assertNotNull("pressHome returned null", mLauncher.pressHome());
+ assertTrue("Launcher internal state is not Home", isInState(LauncherState.NORMAL));
+ assertNotNull("getHome returned null", mLauncher.getWorkspace());
+ }
+}
diff --git a/res/values-ja/strings.xml b/res/values-ja/strings.xml
index 08abb4b..49d5f56 100644
--- a/res/values-ja/strings.xml
+++ b/res/values-ja/strings.xml
@@ -33,7 +33,7 @@
<string name="long_accessible_way_to_add" msgid="4289502106628154155">"ダブルタップ後に押し続けてウィジェットを選択するか、カスタム操作を使用してください。"</string>
<string name="widget_dims_format" msgid="2370757736025621599">"%1$dx%2$d"</string>
<string name="widget_accessible_dims_format" msgid="3640149169885301790">"幅 %1$d、高さ %2$d"</string>
- <string name="add_item_request_drag_hint" msgid="5899764264480397019">"押し続けると、手動で追加できます"</string>
+ <string name="add_item_request_drag_hint" msgid="5899764264480397019">"長押しすると、手動で追加できます"</string>
<string name="place_automatically" msgid="8064208734425456485">"自動的に追加"</string>
<string name="all_apps_search_bar_hint" msgid="1390553134053255246">"アプリを検索"</string>
<string name="all_apps_loading_message" msgid="5813968043155271636">"アプリを読み込んでいます…"</string>
diff --git a/res/values-sk/strings.xml b/res/values-sk/strings.xml
index 9f7e503..7bdf9c4 100644
--- a/res/values-sk/strings.xml
+++ b/res/values-sk/strings.xml
@@ -33,7 +33,7 @@
<string name="long_accessible_way_to_add" msgid="4289502106628154155">"Miniaplikáciu pridáte dvojitým klepnutím a pridržaním alebo pomocou vlastných akcií."</string>
<string name="widget_dims_format" msgid="2370757736025621599">"%1$d × %2$d"</string>
<string name="widget_accessible_dims_format" msgid="3640149169885301790">"šírka %1$d, výška %2$d"</string>
- <string name="add_item_request_drag_hint" msgid="5899764264480397019">"Položku umiestnite ručne klepnutím a podržaním"</string>
+ <string name="add_item_request_drag_hint" msgid="5899764264480397019">"Ak chcete položku umiestniť ručne, pridržte ju"</string>
<string name="place_automatically" msgid="8064208734425456485">"Pridať automaticky"</string>
<string name="all_apps_search_bar_hint" msgid="1390553134053255246">"Hľadať aplikácie"</string>
<string name="all_apps_loading_message" msgid="5813968043155271636">"Načítavajú sa aplikácie…"</string>
diff --git a/src/com/android/launcher3/BaseActivity.java b/src/com/android/launcher3/BaseActivity.java
index 5137774..7f72242 100644
--- a/src/com/android/launcher3/BaseActivity.java
+++ b/src/com/android/launcher3/BaseActivity.java
@@ -17,6 +17,7 @@
package com.android.launcher3;
import static com.android.launcher3.util.SystemUiController.UI_STATE_OVERVIEW;
+
import static java.lang.annotation.RetentionPolicy.SOURCE;
import android.app.Activity;
@@ -25,7 +26,6 @@
import android.content.Intent;
import android.content.res.Configuration;
import android.view.ContextThemeWrapper;
-import android.view.View.AccessibilityDelegate;
import com.android.launcher3.DeviceProfile.OnDeviceProfileChangeListener;
import com.android.launcher3.logging.StatsLogManager;
@@ -125,10 +125,6 @@
return mUserEventDispatcher;
}
- public boolean isInMultiWindowModeCompat() {
- return Utilities.ATLEAST_NOUGAT && isInMultiWindowMode();
- }
-
public SystemUiController getSystemUiController() {
if (mSystemUiController == null) {
mSystemUiController = new SystemUiController(getWindow());
diff --git a/src/com/android/launcher3/BaseDraggingActivity.java b/src/com/android/launcher3/BaseDraggingActivity.java
index 12d443b..5b9b172 100644
--- a/src/com/android/launcher3/BaseDraggingActivity.java
+++ b/src/com/android/launcher3/BaseDraggingActivity.java
@@ -174,8 +174,7 @@
intent.setSourceBounds(getViewBounds(v));
}
try {
- boolean isShortcut = Utilities.ATLEAST_MARSHMALLOW
- && (item instanceof ShortcutInfo)
+ boolean isShortcut = (item instanceof ShortcutInfo)
&& (item.itemType == Favorites.ITEM_TYPE_SHORTCUT
|| item.itemType == Favorites.ITEM_TYPE_DEEP_SHORTCUT)
&& !((ShortcutInfo) item).isPromise();
diff --git a/src/com/android/launcher3/Launcher.java b/src/com/android/launcher3/Launcher.java
index 16909f0..ec3a966 100644
--- a/src/com/android/launcher3/Launcher.java
+++ b/src/com/android/launcher3/Launcher.java
@@ -74,8 +74,6 @@
import android.view.animation.OvershootInterpolator;
import android.widget.Toast;
-import androidx.annotation.Nullable;
-
import com.android.launcher3.DropTarget.DragObject;
import com.android.launcher3.accessibility.LauncherAccessibilityDelegate;
import com.android.launcher3.allapps.AllAppsContainerView;
@@ -149,6 +147,8 @@
import java.util.List;
import java.util.Set;
+import androidx.annotation.Nullable;
+
/**
* Default launcher application.
*/
@@ -409,7 +409,7 @@
private void initDeviceProfile(InvariantDeviceProfile idp) {
// Load configuration-specific DeviceProfile
mDeviceProfile = idp.getDeviceProfile(this);
- if (isInMultiWindowModeCompat()) {
+ if (isInMultiWindowMode()) {
Display display = getWindowManager().getDefaultDisplay();
Point mwSize = new Point();
display.getSize(mwSize);
@@ -2306,7 +2306,6 @@
}
@Override
- @TargetApi(Build.VERSION_CODES.N)
public void onProvideKeyboardShortcuts(
List<KeyboardShortcutGroup> data, Menu menu, int deviceId) {
diff --git a/src/com/android/launcher3/LauncherAnimUtils.java b/src/com/android/launcher3/LauncherAnimUtils.java
index aad3449..04f2b52 100644
--- a/src/com/android/launcher3/LauncherAnimUtils.java
+++ b/src/com/android/launcher3/LauncherAnimUtils.java
@@ -91,4 +91,24 @@
lp.height = height;
}
};
+
+ public static class ViewProgressProperty implements ProgressInterface {
+ View mView;
+ Property<View, Float> mProperty;
+
+ public ViewProgressProperty(View view, Property<View, Float> property) {
+ mView = view;
+ mProperty = property;
+ }
+
+ @Override
+ public void setProgress(float progress) {
+ mProperty.set(mView, progress);
+ }
+
+ @Override
+ public float getProgress() {
+ return mProperty.get(mView);
+ }
+ }
}
diff --git a/src/com/android/launcher3/LauncherAppTransitionManager.java b/src/com/android/launcher3/LauncherAppTransitionManager.java
index 970e558..fb50dfb 100644
--- a/src/com/android/launcher3/LauncherAppTransitionManager.java
+++ b/src/com/android/launcher3/LauncherAppTransitionManager.java
@@ -36,28 +36,19 @@
}
public ActivityOptions getActivityLaunchOptions(Launcher launcher, View v) {
- if (Utilities.ATLEAST_MARSHMALLOW) {
- int left = 0, top = 0;
- int width = v.getMeasuredWidth(), height = v.getMeasuredHeight();
- if (v instanceof BubbleTextView) {
- // Launch from center of icon, not entire view
- Drawable icon = ((BubbleTextView) v).getIcon();
- if (icon != null) {
- Rect bounds = icon.getBounds();
- left = (width - bounds.width()) / 2;
- top = v.getPaddingTop();
- width = bounds.width();
- height = bounds.height();
- }
+ int left = 0, top = 0;
+ int width = v.getMeasuredWidth(), height = v.getMeasuredHeight();
+ if (v instanceof BubbleTextView) {
+ // Launch from center of icon, not entire view
+ Drawable icon = ((BubbleTextView) v).getIcon();
+ if (icon != null) {
+ Rect bounds = icon.getBounds();
+ left = (width - bounds.width()) / 2;
+ top = v.getPaddingTop();
+ width = bounds.width();
+ height = bounds.height();
}
- return ActivityOptions.makeClipRevealAnimation(v, left, top, width, height);
- } else if (Utilities.ATLEAST_LOLLIPOP_MR1) {
- // On L devices, we use the device default slide-up transition.
- // On L MR1 devices, we use a custom version of the slide-up transition which
- // doesn't have the delay present in the device default.
- return ActivityOptions.makeCustomAnimation(launcher, R.anim.task_open_enter,
- R.anim.no_anim);
}
- return null;
+ return ActivityOptions.makeClipRevealAnimation(v, left, top, width, height);
}
}
diff --git a/src/com/android/launcher3/LauncherAppWidgetHost.java b/src/com/android/launcher3/LauncherAppWidgetHost.java
index 56671a1..7f5ac52 100644
--- a/src/com/android/launcher3/LauncherAppWidgetHost.java
+++ b/src/com/android/launcher3/LauncherAppWidgetHost.java
@@ -26,7 +26,6 @@
import android.content.Context;
import android.content.Intent;
import android.os.Handler;
-import android.util.Log;
import android.util.SparseArray;
import android.view.LayoutInflater;
import android.widget.Toast;
@@ -135,9 +134,6 @@
* @see #setResumed(boolean)
*/
public void setListenIfResumed(boolean listenIfResumed) {
- if (!Utilities.ATLEAST_NOUGAT_MR1) {
- return;
- }
if (listenIfResumed == ((mFlags & FLAG_LISTEN_IF_RESUMED) != 0)) {
return;
}
diff --git a/src/com/android/launcher3/LauncherModel.java b/src/com/android/launcher3/LauncherModel.java
index 619d3ae..5ab6eea 100644
--- a/src/com/android/launcher3/LauncherModel.java
+++ b/src/com/android/launcher3/LauncherModel.java
@@ -54,7 +54,6 @@
import com.android.launcher3.util.ItemInfoMatcher;
import com.android.launcher3.util.PackageUserKey;
import com.android.launcher3.util.Preconditions;
-import com.android.launcher3.util.Provider;
import com.android.launcher3.util.Thunk;
import com.android.launcher3.util.ViewOnDrawExecutor;
import com.android.launcher3.widget.WidgetListRowEntry;
@@ -68,6 +67,7 @@
import java.util.List;
import java.util.concurrent.CancellationException;
import java.util.concurrent.Executor;
+import java.util.function.Supplier;
import androidx.annotation.Nullable;
@@ -527,10 +527,8 @@
* use partial updates similar to {@link UserManagerCompat}
*/
public void refreshShortcutsIfRequired() {
- if (Utilities.ATLEAST_NOUGAT_MR1) {
- sWorker.removeCallbacks(mShortcutPermissionCheckRunnable);
- sWorker.post(mShortcutPermissionCheckRunnable);
- }
+ sWorker.removeCallbacks(mShortcutPermissionCheckRunnable);
+ sWorker.post(mShortcutPermissionCheckRunnable);
}
/**
@@ -596,7 +594,7 @@
/**
* Utility method to update a shortcut on the background thread.
*/
- public void updateAndBindShortcutInfo(final Provider<ShortcutInfo> shortcutProvider) {
+ public void updateAndBindShortcutInfo(final Supplier<ShortcutInfo> shortcutProvider) {
enqueueModelUpdateTask(new BaseModelUpdateTask() {
@Override
public void execute(LauncherAppState app, BgDataModel dataModel, AllAppsList apps) {
diff --git a/src/com/android/launcher3/LauncherProvider.java b/src/com/android/launcher3/LauncherProvider.java
index 24dc17a..fb33694 100644
--- a/src/com/android/launcher3/LauncherProvider.java
+++ b/src/com/android/launcher3/LauncherProvider.java
@@ -189,7 +189,7 @@
}
private void reloadLauncherIfExternal() {
- if (Utilities.ATLEAST_MARSHMALLOW && Binder.getCallingPid() != Process.myPid()) {
+ if (Binder.getCallingPid() != Process.myPid()) {
LauncherAppState app = LauncherAppState.getInstanceNoCreate();
if (app != null) {
app.getModel().forceReload();
@@ -217,21 +217,7 @@
uri = ContentUris.withAppendedId(uri, rowId);
notifyListeners();
-
- if (Utilities.ATLEAST_MARSHMALLOW) {
- reloadLauncherIfExternal();
- } else {
- // Deprecated behavior to support legacy devices which rely on provider callbacks.
- LauncherAppState app = LauncherAppState.getInstanceNoCreate();
- if (app != null && "true".equals(uri.getQueryParameter("isExternalAdd"))) {
- app.getModel().forceReload();
- }
-
- String notify = uri.getQueryParameter("notify");
- if (notify == null || "true".equals(notify)) {
- getContext().getContentResolver().notifyChange(uri, null);
- }
- }
+ reloadLauncherIfExternal();
return uri;
}
@@ -301,7 +287,7 @@
throws OperationApplicationException {
createDbIfNotExists();
try (SQLiteTransaction t = new SQLiteTransaction(mOpenHelper.getWritableDatabase())) {
- boolean isAddOrDelete = !Utilities.ATLEAST_MARSHMALLOW;
+ boolean isAddOrDelete = false;
final int numOperations = operations.size();
final ContentProviderResult[] results = new ContentProviderResult[numOperations];
diff --git a/src/com/android/launcher3/LauncherRootView.java b/src/com/android/launcher3/LauncherRootView.java
index 3cf6d62..9f6e5cd 100644
--- a/src/com/android/launcher3/LauncherRootView.java
+++ b/src/com/android/launcher3/LauncherRootView.java
@@ -51,7 +51,7 @@
protected boolean fitSystemWindows(Rect insets) {
mConsumedInsets.setEmpty();
boolean drawInsetBar = false;
- if (mLauncher.isInMultiWindowModeCompat()
+ if (mLauncher.isInMultiWindowMode()
&& (insets.left > 0 || insets.right > 0 || insets.bottom > 0)) {
mConsumedInsets.left = insets.left;
mConsumedInsets.right = insets.right;
@@ -59,8 +59,7 @@
insets = new Rect(0, insets.top, 0, 0);
drawInsetBar = true;
} else if ((insets.right > 0 || insets.left > 0) &&
- (!Utilities.ATLEAST_MARSHMALLOW ||
- getContext().getSystemService(ActivityManager.class).isLowRamDevice())) {
+ getContext().getSystemService(ActivityManager.class).isLowRamDevice()) {
mConsumedInsets.left = insets.left;
mConsumedInsets.right = insets.right;
insets = new Rect(0, insets.top, 0, insets.bottom);
diff --git a/src/com/android/launcher3/LauncherStateManager.java b/src/com/android/launcher3/LauncherStateManager.java
index 5d3ff53..df8ac99 100644
--- a/src/com/android/launcher3/LauncherStateManager.java
+++ b/src/com/android/launcher3/LauncherStateManager.java
@@ -233,9 +233,6 @@
handler.setState(state);
}
- for (int i = mListeners.size() - 1; i >= 0; i--) {
- mListeners.get(i).onStateSetImmediately(state);
- }
onStateTransitionEnd(state);
// Run any queued runnable
@@ -368,9 +365,6 @@
public void onAnimationStart(Animator animation) {
// Change the internal state only when the transition actually starts
onStateTransitionStart(state);
- for (int i = mListeners.size() - 1; i >= 0; i--) {
- mListeners.get(i).onStateTransitionStart(state);
- }
}
@Override
@@ -380,9 +374,6 @@
onCompleteRunnable.run();
}
onStateTransitionEnd(state);
- for (int i = mListeners.size() - 1; i >= 0; i--) {
- mListeners.get(i).onStateTransitionComplete(state);
- }
}
});
mConfig.setAnimation(animation, state);
@@ -402,6 +393,10 @@
mLauncher.getWorkspace().setClipChildren(false);
}
UiFactory.onLauncherStateOrResumeChanged(mLauncher);
+
+ for (int i = mListeners.size() - 1; i >= 0; i--) {
+ mListeners.get(i).onStateTransitionStart(state);
+ }
}
private void onStateTransitionEnd(LauncherState state) {
@@ -420,6 +415,10 @@
}
UiFactory.onLauncherStateOrResumeChanged(mLauncher);
+
+ for (int i = mListeners.size() - 1; i >= 0; i--) {
+ mListeners.get(i).onStateTransitionComplete(state);
+ }
}
public void onWindowFocusChanged() {
@@ -598,11 +597,6 @@
public interface StateListener {
- /**
- * Called when the state is set without an animation.
- */
- void onStateSetImmediately(LauncherState state);
-
void onStateTransitionStart(LauncherState toState);
void onStateTransitionComplete(LauncherState finalState);
}
diff --git a/src/com/android/launcher3/PagedView.java b/src/com/android/launcher3/PagedView.java
index 9470635..8f9e7c8 100644
--- a/src/com/android/launcher3/PagedView.java
+++ b/src/com/android/launcher3/PagedView.java
@@ -18,6 +18,7 @@
import static com.android.launcher3.compat.AccessibilityManagerCompat.isAccessibilityEnabled;
import static com.android.launcher3.compat.AccessibilityManagerCompat.isObservedEventType;
+import static com.android.launcher3.config.FeatureFlags.QUICKSTEP_SPRINGS;
import static com.android.launcher3.touch.OverScroll.OVERSCROLL_DAMP_FACTOR;
import android.animation.LayoutTransition;
@@ -25,6 +26,7 @@
import android.annotation.SuppressLint;
import android.content.Context;
import android.content.res.TypedArray;
+import android.graphics.Canvas;
import android.graphics.Rect;
import android.os.Bundle;
import android.provider.Settings;
@@ -121,6 +123,8 @@
protected boolean mIsPageInTransition = false;
+ protected float mSpringOverScrollX;
+
protected boolean mWasInOverscroll = false;
protected int mUnboundedScrollX;
@@ -349,6 +353,11 @@
boolean isXBeforeFirstPage = mIsRtl ? (x > mMaxScrollX) : (x < 0);
boolean isXAfterLastPage = mIsRtl ? (x < 0) : (x > mMaxScrollX);
+
+ if (!isXBeforeFirstPage && !isXAfterLastPage) {
+ mSpringOverScrollX = 0;
+ }
+
if (isXBeforeFirstPage) {
super.scrollTo(mIsRtl ? mMaxScrollX : 0, y);
if (mAllowOverScroll) {
@@ -988,12 +997,35 @@
}
}
+ @Override
+ protected void dispatchDraw(Canvas canvas) {
+ if (mScroller.isSpringing() && mSpringOverScrollX != 0) {
+ int saveCount = canvas.save();
+
+ canvas.translate(-mSpringOverScrollX, 0);
+ super.dispatchDraw(canvas);
+
+ canvas.restoreToCount(saveCount);
+ } else {
+ super.dispatchDraw(canvas);
+ }
+ }
+
protected void dampedOverScroll(int amount) {
- if (amount == 0) return;
+ mSpringOverScrollX = amount;
+ if (amount == 0) {
+ return;
+ }
int overScrollAmount = OverScroll.dampedScroll(amount, getMeasuredWidth());
+ mSpringOverScrollX = overScrollAmount;
+ if (mScroller.isSpringing()) {
+ invalidate();
+ return;
+ }
+
if (amount < 0) {
- super.scrollTo(overScrollAmount, getScrollY());
+ super.scrollTo(amount, getScrollY());
} else {
super.scrollTo(mMaxScrollX + overScrollAmount, getScrollY());
}
@@ -1001,6 +1033,12 @@
}
protected void overScroll(int amount) {
+ mSpringOverScrollX = amount;
+ if (mScroller.isSpringing()) {
+ invalidate();
+ return;
+ }
+
if (amount == 0) return;
if (mFreeScroll && !mScroller.isFinished()) {
@@ -1096,9 +1134,7 @@
final int activePointerId = mActivePointerId;
final int pointerIndex = ev.findPointerIndex(activePointerId);
final float x = ev.getX(pointerIndex);
- final VelocityTracker velocityTracker = mVelocityTracker;
- velocityTracker.computeCurrentVelocity(1000, mMaximumVelocity);
- int velocityX = (int) velocityTracker.getXVelocity(activePointerId);
+ int velocityX = computeXVelocity();
final int deltaX = (int) (x - mDownMotionX);
final int pageWidth = getPageAt(mCurrentPage).getMeasuredWidth();
boolean isSignificantMove = Math.abs(deltaX) > pageWidth *
@@ -1202,6 +1238,12 @@
return true;
}
+ protected int computeXVelocity() {
+ final VelocityTracker velocityTracker = mVelocityTracker;
+ velocityTracker.computeCurrentVelocity(1000, mMaximumVelocity);
+ return (int) velocityTracker.getXVelocity(mActivePointerId);
+ }
+
protected boolean shouldFlingForVelocity(int velocityX) {
return Math.abs(velocityX) > mFlingThresholdVelocity;
}
@@ -1372,7 +1414,12 @@
// interpolator at zero, ie. 5. We use 4 to make it a little slower.
duration = 4 * Math.round(1000 * Math.abs(distance / velocity));
- return snapToPage(whichPage, delta, duration);
+ if (QUICKSTEP_SPRINGS.get()) {
+ return snapToPage(whichPage, delta, duration, false, null,
+ velocity * Math.signum(newX - getUnboundedScrollX()), true);
+ } else {
+ return snapToPage(whichPage, delta, duration);
+ }
}
public boolean snapToPage(int whichPage) {
@@ -1397,15 +1444,15 @@
int newX = getScrollForPage(whichPage);
final int delta = newX - getUnboundedScrollX();
- return snapToPage(whichPage, delta, duration, immediate, interpolator);
+ return snapToPage(whichPage, delta, duration, immediate, interpolator, 0, false);
}
protected boolean snapToPage(int whichPage, int delta, int duration) {
- return snapToPage(whichPage, delta, duration, false, null);
+ return snapToPage(whichPage, delta, duration, false, null, 0, false);
}
protected boolean snapToPage(int whichPage, int delta, int duration, boolean immediate,
- TimeInterpolator interpolator) {
+ TimeInterpolator interpolator, float velocity, boolean spring) {
if (mFirstLayout) {
setCurrentPage(whichPage);
return false;
@@ -1441,7 +1488,11 @@
mScroller.setInterpolator(mDefaultInterpolator);
}
- mScroller.startScroll(getUnboundedScrollX(), delta, duration);
+ if (spring && QUICKSTEP_SPRINGS.get()) {
+ mScroller.startScrollSpring(getUnboundedScrollX(), delta, duration, velocity);
+ } else {
+ mScroller.startScroll(getUnboundedScrollX(), delta, duration);
+ }
updatePageIndicator();
diff --git a/src/com/android/launcher3/ShortcutInfo.java b/src/com/android/launcher3/ShortcutInfo.java
index c2c3287..e53d59c 100644
--- a/src/com/android/launcher3/ShortcutInfo.java
+++ b/src/com/android/launcher3/ShortcutInfo.java
@@ -16,11 +16,9 @@
package com.android.launcher3;
-import android.annotation.TargetApi;
import android.content.ComponentName;
import android.content.Context;
import android.content.Intent;
-import android.os.Build;
import android.text.TextUtils;
import com.android.launcher3.LauncherSettings.Favorites;
@@ -111,7 +109,6 @@
/**
* Creates a {@link ShortcutInfo} from a {@link ShortcutInfoCompat}.
*/
- @TargetApi(Build.VERSION_CODES.N)
public ShortcutInfo(ShortcutInfoCompat shortcutInfo, Context context) {
user = shortcutInfo.getUserHandle();
itemType = Favorites.ITEM_TYPE_DEEP_SHORTCUT;
diff --git a/src/com/android/launcher3/Utilities.java b/src/com/android/launcher3/Utilities.java
index a0f005c..c847120 100644
--- a/src/com/android/launcher3/Utilities.java
+++ b/src/com/android/launcher3/Utilities.java
@@ -28,7 +28,6 @@
import android.content.pm.PackageManager.NameNotFoundException;
import android.content.pm.ResolveInfo;
import android.content.res.Resources;
-import android.graphics.Bitmap;
import android.graphics.Matrix;
import android.graphics.Paint;
import android.graphics.Point;
@@ -55,7 +54,6 @@
import com.android.launcher3.config.FeatureFlags;
import com.android.launcher3.util.IntArray;
-import java.io.ByteArrayOutputStream;
import java.io.Closeable;
import java.io.IOException;
import java.lang.reflect.Method;
@@ -98,18 +96,6 @@
public static final boolean ATLEAST_OREO =
Build.VERSION.SDK_INT >= Build.VERSION_CODES.O;
- public static final boolean ATLEAST_NOUGAT_MR1 =
- Build.VERSION.SDK_INT >= Build.VERSION_CODES.N_MR1;
-
- public static final boolean ATLEAST_NOUGAT =
- Build.VERSION.SDK_INT >= Build.VERSION_CODES.N;
-
- public static final boolean ATLEAST_MARSHMALLOW =
- Build.VERSION.SDK_INT >= Build.VERSION_CODES.M;
-
- public static final boolean ATLEAST_LOLLIPOP_MR1 =
- Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP_MR1;
-
public static final int SINGLE_FRAME_MS = 16;
/**
@@ -123,9 +109,6 @@
// An intent extra to indicate the horizontal scroll of the wallpaper.
public static final String EXTRA_WALLPAPER_OFFSET = "com.android.launcher3.WALLPAPER_OFFSET";
- public static final int COLOR_EXTRACTION_JOB_ID = 1;
- public static final int WALLPAPER_COMPAT_JOB_ID = 2;
-
// These values are same as that in {@link AsyncTask}.
private static final int CPU_COUNT = Runtime.getRuntime().availableProcessors();
private static final int CORE_POOL_SIZE = CPU_COUNT + 1;
@@ -512,19 +495,11 @@
// Battery saver mode no longer prevents animations.
return false;
}
- PowerManager powerManager = (PowerManager) context.getSystemService(Context.POWER_SERVICE);
- return powerManager.isPowerSaveMode();
+ return context.getSystemService(PowerManager.class).isPowerSaveMode();
}
public static boolean isWallpaperAllowed(Context context) {
- if (ATLEAST_NOUGAT) {
- try {
- WallpaperManager wm = context.getSystemService(WallpaperManager.class);
- return (Boolean) wm.getClass().getDeclaredMethod("isSetWallpaperAllowed")
- .invoke(wm);
- } catch (Exception e) { }
- }
- return true;
+ return context.getSystemService(WallpaperManager.class).isSetWallpaperAllowed();
}
public static void closeSilently(Closeable c) {
@@ -602,8 +577,4 @@
public static String getPointString(int x, int y) {
return String.format(Locale.ENGLISH, "%d,%d", x, y);
}
-
- public interface Consumer<T> {
- void accept(T var1);
- }
}
diff --git a/src/com/android/launcher3/Workspace.java b/src/com/android/launcher3/Workspace.java
index eb26961..3438a26 100644
--- a/src/com/android/launcher3/Workspace.java
+++ b/src/com/android/launcher3/Workspace.java
@@ -996,7 +996,7 @@
@Override
protected void overScroll(int amount) {
- boolean shouldScrollOverlay = mLauncherOverlay != null &&
+ boolean shouldScrollOverlay = mLauncherOverlay != null && !mScroller.isSpringing() &&
((amount <= 0 && !mIsRtl) || (amount >= 0 && mIsRtl));
boolean shouldZeroOverlay = mLauncherOverlay != null && mLastOverlayScroll != 0 &&
diff --git a/src/com/android/launcher3/allapps/AllAppsStore.java b/src/com/android/launcher3/allapps/AllAppsStore.java
index cf0f2a3..52d7d28 100644
--- a/src/com/android/launcher3/allapps/AllAppsStore.java
+++ b/src/com/android/launcher3/allapps/AllAppsStore.java
@@ -30,6 +30,7 @@
import java.util.HashMap;
import java.util.List;
import java.util.Set;
+import java.util.function.Consumer;
/**
* A utility class to maintain the collection of all apps.
@@ -140,7 +141,7 @@
});
}
- private void updateAllIcons(IconAction action) {
+ private void updateAllIcons(Consumer<BubbleTextView> action) {
for (int i = mIconContainers.size() - 1; i >= 0; i--) {
ViewGroup parent = mIconContainers.get(i);
int childCount = parent.getChildCount();
@@ -148,7 +149,7 @@
for (int j = 0; j < childCount; j++) {
View child = parent.getChildAt(j);
if (child instanceof BubbleTextView) {
- action.apply((BubbleTextView) child);
+ action.accept((BubbleTextView) child);
}
}
}
@@ -157,8 +158,4 @@
public interface OnUpdateListener {
void onAppsUpdated();
}
-
- public interface IconAction {
- void apply(BubbleTextView icon);
- }
}
diff --git a/src/com/android/launcher3/allapps/AllAppsTransitionController.java b/src/com/android/launcher3/allapps/AllAppsTransitionController.java
index e8e93fe..bcb5eec 100644
--- a/src/com/android/launcher3/allapps/AllAppsTransitionController.java
+++ b/src/com/android/launcher3/allapps/AllAppsTransitionController.java
@@ -47,6 +47,9 @@
public class AllAppsTransitionController implements StateHandler, OnDeviceProfileChangeListener,
ProgressInterface {
+ public static final float SPRING_DAMPING_RATIO = 0.9f;
+ public static final float SPRING_STIFFNESS = 600f;
+
public static final Property<AllAppsTransitionController, Float> ALL_APPS_PROGRESS =
new Property<AllAppsTransitionController, Float>(Float.class, "allAppsProgress") {
@@ -61,19 +64,6 @@
}
};
- public static final FloatPropertyCompat<AllAppsTransitionController> ALL_APPS_PROGRESS_SPRING
- = new FloatPropertyCompat<AllAppsTransitionController>("allAppsProgressSpring") {
- @Override
- public float getValue(AllAppsTransitionController controller) {
- return controller.mProgress;
- }
-
- @Override
- public void setValue(AllAppsTransitionController controller, float progress) {
- controller.setProgress(progress);
- }
- };
-
private AllAppsContainerView mAppsView;
private ScrimView mScrimView;
@@ -191,8 +181,8 @@
Interpolator interpolator = config.userControlled ? LINEAR : toState == OVERVIEW
? builder.getInterpolator(ANIM_OVERVIEW_SCALE, FAST_OUT_SLOW_IN)
: FAST_OUT_SLOW_IN;
- Animator anim = new SpringObjectAnimator<>(this, ALL_APPS_PROGRESS_SPRING,
- "allAppsSpringFromAATC", 1f / mShiftRange, mProgress, targetProgress);
+ Animator anim = new SpringObjectAnimator<>(this, "allAppsSpringFromAATC", 1f / mShiftRange,
+ SPRING_DAMPING_RATIO, SPRING_STIFFNESS, mProgress, targetProgress);
anim.setDuration(config.duration);
anim.setInterpolator(builder.getInterpolator(ANIM_VERTICAL_PROGRESS, interpolator));
anim.addListener(getProgressAnimatorListener());
diff --git a/src/com/android/launcher3/allapps/DiscoveryBounce.java b/src/com/android/launcher3/allapps/DiscoveryBounce.java
index 76b2565..7467119 100644
--- a/src/com/android/launcher3/allapps/DiscoveryBounce.java
+++ b/src/com/android/launcher3/allapps/DiscoveryBounce.java
@@ -25,6 +25,7 @@
import android.animation.AnimatorInflater;
import android.animation.AnimatorListenerAdapter;
import android.app.ActivityManager;
+import android.content.SharedPreferences;
import android.os.Handler;
import android.view.MotionEvent;
@@ -43,6 +44,10 @@
public static final String HOME_BOUNCE_SEEN = "launcher.apps_view_shown";
public static final String SHELF_BOUNCE_SEEN = "launcher.shelf_bounce_seen";
+ public static final String HOME_BOUNCE_COUNT = "launcher.home_bounce_count";
+ public static final String SHELF_BOUNCE_COUNT = "launcher.shelf_bounce_count";
+
+ public static final int BOUNCE_MAX_COUNT = 3;
private final Launcher mLauncher;
private final Animator mDiscoBounceAnimation;
@@ -137,6 +142,7 @@
new Handler().postDelayed(() -> showForHomeIfNeeded(launcher, false), DELAY_MS);
return;
}
+ incrementHomeBounceCount(launcher);
new DiscoveryBounce(launcher, 0).show(HOTSEAT);
}
@@ -165,6 +171,7 @@
// TODO: Move these checks to the top and call this method after invalidate handler.
return;
}
+ incrementShelfBounceCount(launcher);
new DiscoveryBounce(launcher, (1 - OVERVIEW.getVerticalProgress(launcher)))
.show(PREDICTION);
@@ -197,4 +204,22 @@
PersonalWorkSlidingTabStrip.KEY_SHOWED_PEEK_WORK_TAB, false)
&& UserManagerCompat.getInstance(launcher).hasWorkProfile();
}
+
+ private static void incrementShelfBounceCount(Launcher launcher) {
+ SharedPreferences sharedPrefs = launcher.getSharedPrefs();
+ int count = sharedPrefs.getInt(SHELF_BOUNCE_COUNT, 0);
+ if (count > BOUNCE_MAX_COUNT) {
+ return;
+ }
+ sharedPrefs.edit().putInt(SHELF_BOUNCE_COUNT, count + 1).apply();
+ }
+
+ private static void incrementHomeBounceCount(Launcher launcher) {
+ SharedPreferences sharedPrefs = launcher.getSharedPrefs();
+ int count = sharedPrefs.getInt(HOME_BOUNCE_COUNT, 0);
+ if (count > BOUNCE_MAX_COUNT) {
+ return;
+ }
+ sharedPrefs.edit().putInt(HOME_BOUNCE_COUNT, count + 1).apply();
+ }
}
diff --git a/src/com/android/launcher3/anim/AnimatorPlaybackController.java b/src/com/android/launcher3/anim/AnimatorPlaybackController.java
index 62f59e4..cf070c5 100644
--- a/src/com/android/launcher3/anim/AnimatorPlaybackController.java
+++ b/src/com/android/launcher3/anim/AnimatorPlaybackController.java
@@ -80,6 +80,9 @@
private OnAnimationEndDispatcher mEndListener;
private DynamicAnimation.OnAnimationEndListener mSpringEndListener;
+ // We need this variable to ensure the end listener is called immediately, otherwise we run into
+ // issues where the callback interferes with the states of the swipe detector.
+ private boolean mSkipToEnd = false;
protected AnimatorPlaybackController(AnimatorSet anim, long duration,
Runnable onCancelRunnable) {
@@ -232,7 +235,11 @@
}
private void dispatchOnStartRecursively(Animator animator) {
- for (AnimatorListener l : nonNullList(animator.getListeners())) {
+ List<AnimatorListener> listeners = animator instanceof SpringObjectAnimator
+ ? nonNullList(((SpringObjectAnimator) animator).getSuperListeners())
+ : nonNullList(animator.getListeners());
+
+ for (AnimatorListener l : listeners) {
l.onAnimationStart(animator);
}
@@ -280,6 +287,17 @@
return mOnCancelRunnable;
}
+ public void skipToEnd() {
+ mSkipToEnd = true;
+ for (SpringAnimation spring : mSprings) {
+ if (spring.canSkipToEnd()) {
+ spring.skipToEnd();
+ }
+ }
+ mAnimationPlayer.end();
+ mSkipToEnd = false;
+ }
+
public static class AnimatorPlaybackControllerVL extends AnimatorPlaybackController {
private final ValueAnimator[] mChildAnimations;
@@ -343,19 +361,34 @@
*/
private class OnAnimationEndDispatcher extends AnimationSuccessListener {
+ boolean mAnimatorDone = false;
+ boolean mSpringsDone = false;
+ boolean mDispatched = false;
+
@Override
public void onAnimationStart(Animator animation) {
mCancelled = false;
+ mDispatched = false;
}
@Override
public void onAnimationSuccess(Animator animator) {
+ if (mSprings.isEmpty()) {
+ mSpringsDone = mAnimatorDone = true;
+ }
+ if (isAnySpringRunning()) {
+ mAnimatorDone = true;
+ } else {
+ mSpringsDone = true;
+ }
+
// We wait for the spring (if any) to finish running before completing the end callback.
- if (mSprings.isEmpty() || !isAnySpringRunning()) {
+ if (!mDispatched && (mSkipToEnd || (mAnimatorDone && mSpringsDone))) {
dispatchOnEndRecursively(mAnim);
if (mEndAction != null) {
mEndAction.run();
}
+ mDispatched = true;
}
}
diff --git a/src/com/android/launcher3/anim/RevealOutlineAnimation.java b/src/com/android/launcher3/anim/RevealOutlineAnimation.java
index afb8875..f99dabc 100644
--- a/src/com/android/launcher3/anim/RevealOutlineAnimation.java
+++ b/src/com/android/launcher3/anim/RevealOutlineAnimation.java
@@ -8,8 +8,6 @@
import android.view.View;
import android.view.ViewOutlineProvider;
-import com.android.launcher3.Utilities;
-
/**
* A {@link ViewOutlineProvider} that has helper functions to create reveal animations.
* This class should be extended so that subclasses can define the reveal shape as the
@@ -58,16 +56,10 @@
});
- va.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
- @Override
- public void onAnimationUpdate(ValueAnimator arg0) {
- float progress = (Float) arg0.getAnimatedValue();
- setProgress(progress);
- revealView.invalidateOutline();
- if (!Utilities.ATLEAST_LOLLIPOP_MR1) {
- revealView.invalidate();
- }
- }
+ va.addUpdateListener(v -> {
+ float progress = (Float) v.getAnimatedValue();
+ setProgress(progress);
+ revealView.invalidateOutline();
});
return va;
}
diff --git a/src/com/android/launcher3/anim/SpringObjectAnimator.java b/src/com/android/launcher3/anim/SpringObjectAnimator.java
index 4ece909..4f45c05 100644
--- a/src/com/android/launcher3/anim/SpringObjectAnimator.java
+++ b/src/com/android/launcher3/anim/SpringObjectAnimator.java
@@ -55,17 +55,27 @@
private boolean mAnimatorEnded = false;
private boolean mEnded = false;
- private static final float SPRING_DAMPING_RATIO = 0.9f;
- private static final float SPRING_STIFFNESS = 600f;
+ private static final FloatPropertyCompat<ProgressInterface> sFloatProperty =
+ new FloatPropertyCompat<ProgressInterface>("springObjectAnimator") {
+ @Override
+ public float getValue(ProgressInterface object) {
+ return object.getProgress();
+ }
- public SpringObjectAnimator(T object, FloatPropertyCompat<T> floatProperty,
- String name, float minimumVisibleChange, float... values) {
+ @Override
+ public void setValue(ProgressInterface object, float progress) {
+ object.setProgress(progress);
+ }
+ };
+
+ public SpringObjectAnimator(T object, String name, float minimumVisibleChange, float damping,
+ float stiffness, float... values) {
mObject = object;
- mSpring = new SpringAnimation(object, floatProperty);
+ mSpring = new SpringAnimation(object, sFloatProperty);
mSpring.setMinimumVisibleChange(minimumVisibleChange);
mSpring.setSpring(new SpringForce(0)
- .setDampingRatio(SPRING_DAMPING_RATIO)
- .setStiffness(SPRING_STIFFNESS));
+ .setDampingRatio(damping)
+ .setStiffness(stiffness));
mSpring.setStartVelocity(0.01f);
mProperty = new SpringProperty<T>(name, mSpring);
mObjectAnimator = ObjectAnimator.ofFloat(object, mProperty, values);
@@ -73,7 +83,9 @@
mListeners = new ArrayList<>();
setFloatValues(values);
- mObjectAnimator.addListener(new AnimatorListenerAdapter() {
+ // We use this listener and track mListeners so that we can sync the animator and spring
+ // listeners.
+ super.addListener(new AnimatorListenerAdapter() {
@Override
public void onAnimationStart(Animator animation) {
mAnimatorEnded = false;
@@ -94,7 +106,7 @@
for (AnimatorListener l : mListeners) {
l.onAnimationCancel(animation);
}
- mSpring.animateToFinalPosition(mObject.getProgress());
+ mSpring.cancel();
}
});
@@ -145,6 +157,10 @@
mListeners.add(listener);
}
+ public ArrayList<AnimatorListener> getSuperListeners() {
+ return super.getListeners();
+ }
+
@Override
public ArrayList<AnimatorListener> getListeners() {
return mListeners;
@@ -167,8 +183,8 @@
@Override
public void cancel() {
- mSpring.animateToFinalPosition(mObject.getProgress());
mObjectAnimator.cancel();
+ mSpring.cancel();
}
@Override
diff --git a/src/com/android/launcher3/compat/AccessibilityManagerCompat.java b/src/com/android/launcher3/compat/AccessibilityManagerCompat.java
index 02da861..6feb1e9 100644
--- a/src/com/android/launcher3/compat/AccessibilityManagerCompat.java
+++ b/src/com/android/launcher3/compat/AccessibilityManagerCompat.java
@@ -26,6 +26,8 @@
import com.android.launcher3.TestProtocol;
import com.android.launcher3.Utilities;
+import java.util.function.Consumer;
+
public class AccessibilityManagerCompat {
public static boolean isAccessibilityEnabled(Context context) {
@@ -85,7 +87,7 @@
}
public static boolean processTestRequest(Context context, String eventTag, int action,
- Bundle request, Utilities.Consumer<Bundle> responseFiller) {
+ Bundle request, Consumer<Bundle> responseFiller) {
final AccessibilityManager accessibilityManager = getAccessibilityManagerForTest(context);
if (accessibilityManager == null) return false;
diff --git a/src/com/android/launcher3/compat/AlphabeticIndexCompat.java b/src/com/android/launcher3/compat/AlphabeticIndexCompat.java
index a7c0a47..dfdcc70 100644
--- a/src/com/android/launcher3/compat/AlphabeticIndexCompat.java
+++ b/src/com/android/launcher3/compat/AlphabeticIndexCompat.java
@@ -1,15 +1,12 @@
package com.android.launcher3.compat;
-import android.annotation.TargetApi;
import android.content.Context;
import android.icu.text.AlphabeticIndex;
-import android.os.Build;
import android.os.LocaleList;
import android.util.Log;
import com.android.launcher3.Utilities;
-import java.lang.reflect.Method;
import java.util.Locale;
import androidx.annotation.NonNull;
@@ -25,19 +22,10 @@
BaseIndex index = null;
try {
- if (Utilities.ATLEAST_NOUGAT) {
- index = new AlphabeticIndexVN(context);
- }
+ index = new AlphabeticIndexVN(context);
} catch (Exception e) {
Log.d(TAG, "Unable to load the system index", e);
}
- if (index == null) {
- try {
- index = new AlphabeticIndexV16(context);
- } catch (Exception e) {
- Log.d(TAG, "Unable to load the system index", e);
- }
- }
mBaseIndex = index == null ? new BaseIndex() : index;
@@ -111,59 +99,8 @@
}
/**
- * Reflected libcore.icu.AlphabeticIndex implementation, falls back to the base
- * alphabetic index.
- */
- private static class AlphabeticIndexV16 extends BaseIndex {
-
- private Object mAlphabeticIndex;
- private Method mGetBucketIndexMethod;
- private Method mGetBucketLabelMethod;
-
- public AlphabeticIndexV16(Context context) throws Exception {
- Locale curLocale = context.getResources().getConfiguration().locale;
- Class clazz = Class.forName("libcore.icu.AlphabeticIndex");
- mGetBucketIndexMethod = clazz.getDeclaredMethod("getBucketIndex", String.class);
- mGetBucketLabelMethod = clazz.getDeclaredMethod("getBucketLabel", int.class);
- mAlphabeticIndex = clazz.getConstructor(Locale.class).newInstance(curLocale);
-
- if (!curLocale.getLanguage().equals(Locale.ENGLISH.getLanguage())) {
- clazz.getDeclaredMethod("addLabels", Locale.class)
- .invoke(mAlphabeticIndex, Locale.ENGLISH);
- }
- }
-
- /**
- * Returns the index of the bucket in which {@param s} should appear.
- * Function is synchronized because underlying routine walks an iterator
- * whose state is maintained inside the index object.
- */
- protected int getBucketIndex(String s) {
- try {
- return (Integer) mGetBucketIndexMethod.invoke(mAlphabeticIndex, s);
- } catch (Exception e) {
- e.printStackTrace();
- }
- return super.getBucketIndex(s);
- }
-
- /**
- * Returns the label for the bucket at the given index (as returned by getBucketIndex).
- */
- protected String getBucketLabel(int index) {
- try {
- return (String) mGetBucketLabelMethod.invoke(mAlphabeticIndex, index);
- } catch (Exception e) {
- e.printStackTrace();
- }
- return super.getBucketLabel(index);
- }
- }
-
- /**
* Implementation based on {@link AlphabeticIndex}.
*/
- @TargetApi(Build.VERSION_CODES.N)
private static class AlphabeticIndexVN extends BaseIndex {
private final AlphabeticIndex.ImmutableIndex mAlphabeticIndex;
diff --git a/src/com/android/launcher3/compat/LauncherAppsCompat.java b/src/com/android/launcher3/compat/LauncherAppsCompat.java
index 407355c..e7d4679 100644
--- a/src/com/android/launcher3/compat/LauncherAppsCompat.java
+++ b/src/com/android/launcher3/compat/LauncherAppsCompat.java
@@ -83,8 +83,4 @@
UserHandle user);
public abstract List<ShortcutConfigActivityInfo> getCustomShortcutActivityList(
@Nullable PackageUserKey packageUser);
-
- public void showAppDetailsForProfile(ComponentName component, UserHandle user) {
- showAppDetailsForProfile(component, user, null, null);
- }
}
diff --git a/src/com/android/launcher3/compat/UserManagerCompat.java b/src/com/android/launcher3/compat/UserManagerCompat.java
index e13d2a6..2c0088e 100644
--- a/src/com/android/launcher3/compat/UserManagerCompat.java
+++ b/src/com/android/launcher3/compat/UserManagerCompat.java
@@ -35,14 +35,8 @@
if (sInstance == null) {
if (Utilities.ATLEAST_P) {
sInstance = new UserManagerCompatVP(context.getApplicationContext());
- } else if (Utilities.ATLEAST_NOUGAT_MR1) {
- sInstance = new UserManagerCompatVNMr1(context.getApplicationContext());
- } else if (Utilities.ATLEAST_NOUGAT) {
- sInstance = new UserManagerCompatVN(context.getApplicationContext());
- } else if (Utilities.ATLEAST_MARSHMALLOW) {
- sInstance = new UserManagerCompatVM(context.getApplicationContext());
} else {
- sInstance = new UserManagerCompatVL(context.getApplicationContext());
+ sInstance = new UserManagerCompatVNMr1(context.getApplicationContext());
}
}
return sInstance;
diff --git a/src/com/android/launcher3/compat/UserManagerCompatVL.java b/src/com/android/launcher3/compat/UserManagerCompatVL.java
deleted file mode 100644
index 4688052..0000000
--- a/src/com/android/launcher3/compat/UserManagerCompatVL.java
+++ /dev/null
@@ -1,126 +0,0 @@
-/*
- * Copyright (C) 2014 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.compat;
-
-import android.content.Context;
-import android.os.UserHandle;
-import android.os.UserManager;
-import android.util.ArrayMap;
-import android.util.LongSparseArray;
-
-import java.util.ArrayList;
-import java.util.Collections;
-import java.util.List;
-
-public class UserManagerCompatVL extends UserManagerCompat {
-
- protected final UserManager mUserManager;
-
- protected LongSparseArray<UserHandle> mUsers;
- // Create a separate reverse map as LongSparseArray.indexOfValue checks if objects are same
- // and not {@link Object#equals}
- protected ArrayMap<UserHandle, Long> mUserToSerialMap;
-
- UserManagerCompatVL(Context context) {
- mUserManager = (UserManager) context.getSystemService(Context.USER_SERVICE);
- }
-
- @Override
- public long getSerialNumberForUser(UserHandle user) {
- synchronized (this) {
- if (mUserToSerialMap != null) {
- Long serial = mUserToSerialMap.get(user);
- return serial == null ? 0 : serial;
- }
- }
- return mUserManager.getSerialNumberForUser(user);
- }
-
- @Override
- public UserHandle getUserForSerialNumber(long serialNumber) {
- synchronized (this) {
- if (mUsers != null) {
- return mUsers.get(serialNumber);
- }
- }
- return mUserManager.getUserForSerialNumber(serialNumber);
- }
-
- @Override
- public boolean isQuietModeEnabled(UserHandle user) {
- return false;
- }
-
- @Override
- public boolean isUserUnlocked(UserHandle user) {
- return true;
- }
-
- @Override
- public boolean isDemoUser() {
- return false;
- }
-
- @Override
- public boolean requestQuietModeEnabled(boolean enableQuietMode, UserHandle user) {
- return false;
- }
-
- @Override
- public boolean isAnyProfileQuietModeEnabled() {
- return false;
- }
-
- @Override
- public void enableAndResetCache() {
- synchronized (this) {
- mUsers = new LongSparseArray<>();
- mUserToSerialMap = new ArrayMap<>();
- List<UserHandle> users = mUserManager.getUserProfiles();
- if (users != null) {
- for (UserHandle user : users) {
- long serial = mUserManager.getSerialNumberForUser(user);
- mUsers.put(serial, user);
- mUserToSerialMap.put(user, serial);
- }
- }
- }
- }
-
- @Override
- public List<UserHandle> getUserProfiles() {
- synchronized (this) {
- if (mUsers != null) {
- return new ArrayList<>(mUserToSerialMap.keySet());
- }
- }
-
- List<UserHandle> users = mUserManager.getUserProfiles();
- return users == null ? Collections.<UserHandle>emptyList() : users;
- }
-
- @Override
- public boolean hasWorkProfile() {
- synchronized (this) {
- if (mUsers != null) {
- return mUsers.size() > 1;
- }
- }
- return getUserProfiles().size() > 1;
- }
-}
-
diff --git a/src/com/android/launcher3/compat/UserManagerCompatVM.java b/src/com/android/launcher3/compat/UserManagerCompatVM.java
deleted file mode 100644
index cf74b37..0000000
--- a/src/com/android/launcher3/compat/UserManagerCompatVM.java
+++ /dev/null
@@ -1,30 +0,0 @@
-/*
- * Copyright (C) 2016 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.compat;
-
-import android.annotation.TargetApi;
-import android.content.Context;
-import android.os.Build;
-import android.os.UserHandle;
-
-@TargetApi(Build.VERSION_CODES.M)
-public class UserManagerCompatVM extends UserManagerCompatVL {
-
- UserManagerCompatVM(Context context) {
- super(context);
- }
-}
diff --git a/src/com/android/launcher3/compat/UserManagerCompatVN.java b/src/com/android/launcher3/compat/UserManagerCompatVN.java
deleted file mode 100644
index 3733565..0000000
--- a/src/com/android/launcher3/compat/UserManagerCompatVN.java
+++ /dev/null
@@ -1,58 +0,0 @@
-/*
- * Copyright (C) 2016 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.compat;
-
-import android.annotation.TargetApi;
-import android.content.Context;
-import android.os.Build;
-import android.os.Process;
-import android.os.UserHandle;
-
-import java.util.List;
-
-@TargetApi(Build.VERSION_CODES.N)
-public class UserManagerCompatVN extends UserManagerCompatVM {
-
- UserManagerCompatVN(Context context) {
- super(context);
- }
-
- @Override
- public boolean isQuietModeEnabled(UserHandle user) {
- return mUserManager.isQuietModeEnabled(user);
- }
-
- @Override
- public boolean isUserUnlocked(UserHandle user) {
- return mUserManager.isUserUnlocked(user);
- }
-
- @Override
- public boolean isAnyProfileQuietModeEnabled() {
- List<UserHandle> userProfiles = getUserProfiles();
- for (UserHandle userProfile : userProfiles) {
- if (Process.myUserHandle().equals(userProfile)) {
- continue;
- }
- if (isQuietModeEnabled(userProfile)) {
- return true;
- }
- }
- return false;
- }
-}
-
diff --git a/src/com/android/launcher3/compat/UserManagerCompatVNMr1.java b/src/com/android/launcher3/compat/UserManagerCompatVNMr1.java
index 3f64bc8..18d9053 100644
--- a/src/com/android/launcher3/compat/UserManagerCompatVNMr1.java
+++ b/src/com/android/launcher3/compat/UserManagerCompatVNMr1.java
@@ -19,16 +19,120 @@
import android.annotation.TargetApi;
import android.content.Context;
import android.os.Build;
+import android.os.Process;
+import android.os.UserHandle;
+import android.os.UserManager;
+import android.util.ArrayMap;
+import android.util.LongSparseArray;
+
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.List;
@TargetApi(Build.VERSION_CODES.N_MR1)
-public class UserManagerCompatVNMr1 extends UserManagerCompatVN {
+public class UserManagerCompatVNMr1 extends UserManagerCompat {
+
+ protected final UserManager mUserManager;
+
+ protected LongSparseArray<UserHandle> mUsers;
+ // Create a separate reverse map as LongSparseArray.indexOfValue checks if objects are same
+ // and not {@link Object#equals}
+ protected ArrayMap<UserHandle, Long> mUserToSerialMap;
UserManagerCompatVNMr1(Context context) {
- super(context);
+ mUserManager = (UserManager) context.getSystemService(Context.USER_SERVICE);
+ }
+
+ @Override
+ public boolean isQuietModeEnabled(UserHandle user) {
+ return mUserManager.isQuietModeEnabled(user);
+ }
+
+ @Override
+ public boolean isUserUnlocked(UserHandle user) {
+ return mUserManager.isUserUnlocked(user);
+ }
+
+ @Override
+ public boolean isAnyProfileQuietModeEnabled() {
+ List<UserHandle> userProfiles = getUserProfiles();
+ for (UserHandle userProfile : userProfiles) {
+ if (Process.myUserHandle().equals(userProfile)) {
+ continue;
+ }
+ if (isQuietModeEnabled(userProfile)) {
+ return true;
+ }
+ }
+ return false;
+ }
+
+ @Override
+ public long getSerialNumberForUser(UserHandle user) {
+ synchronized (this) {
+ if (mUserToSerialMap != null) {
+ Long serial = mUserToSerialMap.get(user);
+ return serial == null ? 0 : serial;
+ }
+ }
+ return mUserManager.getSerialNumberForUser(user);
+ }
+
+ @Override
+ public UserHandle getUserForSerialNumber(long serialNumber) {
+ synchronized (this) {
+ if (mUsers != null) {
+ return mUsers.get(serialNumber);
+ }
+ }
+ return mUserManager.getUserForSerialNumber(serialNumber);
}
@Override
public boolean isDemoUser() {
return mUserManager.isDemoUser();
}
+
+ @Override
+ public boolean requestQuietModeEnabled(boolean enableQuietMode, UserHandle user) {
+ return false;
+ }
+
+ @Override
+ public void enableAndResetCache() {
+ synchronized (this) {
+ mUsers = new LongSparseArray<>();
+ mUserToSerialMap = new ArrayMap<>();
+ List<UserHandle> users = mUserManager.getUserProfiles();
+ if (users != null) {
+ for (UserHandle user : users) {
+ long serial = mUserManager.getSerialNumberForUser(user);
+ mUsers.put(serial, user);
+ mUserToSerialMap.put(user, serial);
+ }
+ }
+ }
+ }
+
+ @Override
+ public List<UserHandle> getUserProfiles() {
+ synchronized (this) {
+ if (mUsers != null) {
+ return new ArrayList<>(mUserToSerialMap.keySet());
+ }
+ }
+
+ List<UserHandle> users = mUserManager.getUserProfiles();
+ return users == null ? Collections.<UserHandle>emptyList() : users;
+ }
+
+ @Override
+ public boolean hasWorkProfile() {
+ synchronized (this) {
+ if (mUsers != null) {
+ return mUsers.size() > 1;
+ }
+ }
+ return getUserProfiles().size() > 1;
+ }
}
diff --git a/src/com/android/launcher3/config/BaseFlags.java b/src/com/android/launcher3/config/BaseFlags.java
index fa93081..fa4ebaf 100644
--- a/src/com/android/launcher3/config/BaseFlags.java
+++ b/src/com/android/launcher3/config/BaseFlags.java
@@ -17,13 +17,16 @@
package com.android.launcher3.config;
import static androidx.core.util.Preconditions.checkNotNull;
+
import android.content.ContentResolver;
import android.content.Context;
import android.content.SharedPreferences;
import android.provider.Settings;
+
import androidx.annotation.GuardedBy;
import androidx.annotation.Keep;
import androidx.annotation.VisibleForTesting;
+
import com.android.launcher3.Utilities;
import java.util.ArrayList;
@@ -107,7 +110,7 @@
public static final ToggleableGlobalSettingsFlag SWIPE_HOME
= new ToggleableGlobalSettingsFlag("SWIPE_HOME", false,
- "[WIP] Swiping up on the nav bar goes home. Swipe and hold goes to recent apps.");
+ "Swiping up on the nav bar goes home. Swipe and hold goes to recent apps.");
public static void initialize(Context context) {
// Avoid the disk read for user builds
@@ -250,11 +253,17 @@
@Override
void updateStorage(Context context, boolean value) {
+ if (contentResolver == null) {
+ return;
+ }
Settings.Global.putInt(contentResolver, getKey(), value ? 1 : 0);
}
@Override
boolean getFromStorage(Context context, boolean defaultValue) {
+ if (contentResolver == null) {
+ return defaultValue;
+ }
return Settings.Global.getInt(contentResolver, getKey(), defaultValue ? 1 : 0) == 1;
}
diff --git a/src/com/android/launcher3/dragndrop/AddItemActivity.java b/src/com/android/launcher3/dragndrop/AddItemActivity.java
index 79819cc..5ac9867 100644
--- a/src/com/android/launcher3/dragndrop/AddItemActivity.java
+++ b/src/com/android/launcher3/dragndrop/AddItemActivity.java
@@ -57,13 +57,14 @@
import com.android.launcher3.userevent.nano.LauncherLogProto.ContainerType;
import com.android.launcher3.util.InstantAppResolver;
import com.android.launcher3.util.LooperExecutor;
-import com.android.launcher3.util.Provider;
import com.android.launcher3.views.BaseDragLayer;
import com.android.launcher3.widget.PendingAddShortcutInfo;
import com.android.launcher3.widget.PendingAddWidgetInfo;
import com.android.launcher3.widget.WidgetHostViewLoader;
import com.android.launcher3.widget.WidgetImageView;
+import java.util.function.Supplier;
+
@TargetApi(Build.VERSION_CODES.O)
public class AddItemActivity extends BaseActivity implements OnLongClickListener, OnTouchListener {
@@ -220,7 +221,7 @@
return true;
}
- private void applyWidgetItemAsync(final Provider<WidgetItem> itemProvider) {
+ private void applyWidgetItemAsync(final Supplier<WidgetItem> itemProvider) {
new AsyncTask<Void, Void, WidgetItem>() {
@Override
protected WidgetItem doInBackground(Void... voids) {
diff --git a/src/com/android/launcher3/dragndrop/DragDriver.java b/src/com/android/launcher3/dragndrop/DragDriver.java
index d8a3024..84fc94d 100644
--- a/src/com/android/launcher3/dragndrop/DragDriver.java
+++ b/src/com/android/launcher3/dragndrop/DragDriver.java
@@ -19,8 +19,8 @@
import android.content.Context;
import android.view.DragEvent;
import android.view.MotionEvent;
+
import com.android.launcher3.DropTarget.DragObject;
-import com.android.launcher3.Utilities;
/**
* Base class for driving a drag/drop operation.
@@ -83,7 +83,7 @@
public static DragDriver create(Context context, DragController dragController,
DragObject dragObject, DragOptions options) {
- if (Utilities.ATLEAST_NOUGAT && options.systemDndStartPoint != null) {
+ if (options.systemDndStartPoint != null) {
return new SystemDragDriver(dragController, context, dragObject);
} else {
return new InternalDragDriver(dragController);
diff --git a/src/com/android/launcher3/folder/Folder.java b/src/com/android/launcher3/folder/Folder.java
index 51c2998..6fc81c9 100644
--- a/src/com/android/launcher3/folder/Folder.java
+++ b/src/com/android/launcher3/folder/Folder.java
@@ -34,11 +34,8 @@
import android.util.AttributeSet;
import android.util.Log;
import android.util.Pair;
-import android.view.ActionMode;
import android.view.FocusFinder;
import android.view.KeyEvent;
-import android.view.Menu;
-import android.view.MenuItem;
import android.view.MotionEvent;
import android.view.View;
import android.view.ViewDebug;
@@ -65,7 +62,6 @@
import com.android.launcher3.PagedView;
import com.android.launcher3.R;
import com.android.launcher3.ShortcutInfo;
-import com.android.launcher3.Utilities;
import com.android.launcher3.Workspace.ItemOperator;
import com.android.launcher3.accessibility.AccessibleDragListenerAdapter;
import com.android.launcher3.config.FeatureFlags;
@@ -214,27 +210,6 @@
mFolderName = findViewById(R.id.folder_name);
mFolderName.setOnBackKeyListener(this);
mFolderName.setOnFocusChangeListener(this);
-
- if (!Utilities.ATLEAST_MARSHMALLOW) {
- // We disable action mode in older OSes where floating selection menu is not yet
- // available.
- mFolderName.setCustomSelectionActionModeCallback(new ActionMode.Callback() {
- public boolean onActionItemClicked(ActionMode mode, MenuItem item) {
- return false;
- }
-
- public boolean onCreateActionMode(ActionMode mode, Menu menu) {
- return false;
- }
-
- public void onDestroyActionMode(ActionMode mode) {
- }
-
- public boolean onPrepareActionMode(ActionMode mode, Menu menu) {
- return false;
- }
- });
- }
mFolderName.setOnEditorActionListener(this);
mFolderName.setSelectAllOnFocus(true);
mFolderName.setInputType(mFolderName.getInputType()
diff --git a/src/com/android/launcher3/icons/IconCache.java b/src/com/android/launcher3/icons/IconCache.java
index 4b54bc3..fb0a367 100644
--- a/src/com/android/launcher3/icons/IconCache.java
+++ b/src/com/android/launcher3/icons/IconCache.java
@@ -47,7 +47,8 @@
import com.android.launcher3.model.PackageItemInfo;
import com.android.launcher3.util.InstantAppResolver;
import com.android.launcher3.util.Preconditions;
-import com.android.launcher3.util.Provider;
+
+import java.util.function.Supplier;
import androidx.annotation.NonNull;
@@ -156,7 +157,7 @@
*/
public synchronized void updateTitleAndIcon(AppInfo application) {
CacheEntry entry = cacheLocked(application.componentName,
- application.user, Provider.of(null), mLauncherActivityInfoCachingLogic,
+ application.user, () -> null, mLauncherActivityInfoCachingLogic,
false, application.usingLowResIcon());
if (entry.icon != null && !isDefaultIcon(entry.icon, application.user)) {
applyCacheEntry(entry, application);
@@ -169,7 +170,7 @@
public synchronized void getTitleAndIcon(ItemInfoWithIcon info,
LauncherActivityInfo activityInfo, boolean useLowResIcon) {
// If we already have activity info, no need to use package icon
- getTitleAndIcon(info, Provider.of(activityInfo), false, useLowResIcon);
+ getTitleAndIcon(info, () -> activityInfo, false, useLowResIcon);
}
/**
@@ -191,7 +192,7 @@
}
public synchronized String getTitleNoCache(ComponentWithLabel info) {
- CacheEntry entry = cacheLocked(info.getComponent(), info.getUser(), Provider.of(info),
+ CacheEntry entry = cacheLocked(info.getComponent(), info.getUser(), () -> info,
mComponentWithLabelCachingLogic, false /* usePackageIcon */,
true /* useLowResIcon */, false /* addToMemCache */);
return Utilities.trim(entry.title);
@@ -202,7 +203,7 @@
*/
private synchronized void getTitleAndIcon(
@NonNull ItemInfoWithIcon infoInOut,
- @NonNull Provider<LauncherActivityInfo> activityInfoProvider,
+ @NonNull Supplier<LauncherActivityInfo> activityInfoProvider,
boolean usePkgIcon, boolean useLowResIcon) {
CacheEntry entry = cacheLocked(infoInOut.getTargetComponent(), infoInOut.user,
activityInfoProvider, mLauncherActivityInfoCachingLogic, usePkgIcon, useLowResIcon);
diff --git a/src/com/android/launcher3/icons/LauncherIcons.java b/src/com/android/launcher3/icons/LauncherIcons.java
index f0a63ba..75f76d9 100644
--- a/src/com/android/launcher3/icons/LauncherIcons.java
+++ b/src/com/android/launcher3/icons/LauncherIcons.java
@@ -31,9 +31,10 @@
import com.android.launcher3.model.PackageItemInfo;
import com.android.launcher3.shortcuts.DeepShortcutManager;
import com.android.launcher3.shortcuts.ShortcutInfoCompat;
-import com.android.launcher3.util.Provider;
import com.android.launcher3.util.Themes;
+import java.util.function.Supplier;
+
import androidx.annotation.Nullable;
/**
@@ -114,7 +115,7 @@
}
public BitmapInfo createShortcutIcon(ShortcutInfoCompat shortcutInfo,
- boolean badged, @Nullable Provider<ItemInfoWithIcon> fallbackIconProvider) {
+ boolean badged, @Nullable Supplier<ItemInfoWithIcon> fallbackIconProvider) {
Drawable unbadgedDrawable = DeepShortcutManager.getInstance(mContext)
.getShortcutIconDrawable(shortcutInfo, mFillResIconDpi);
IconCache cache = LauncherAppState.getInstance(mContext).getIconCache();
diff --git a/src/com/android/launcher3/model/GridSizeMigrationTask.java b/src/com/android/launcher3/model/GridSizeMigrationTask.java
index 1c7bffa..243b286 100644
--- a/src/com/android/launcher3/model/GridSizeMigrationTask.java
+++ b/src/com/android/launcher3/model/GridSizeMigrationTask.java
@@ -47,8 +47,6 @@
*/
public class GridSizeMigrationTask {
- public static boolean ENABLED = Utilities.ATLEAST_NOUGAT;
-
private static final String TAG = "GridSizeMigrationTask";
private static final boolean DEBUG = true;
diff --git a/src/com/android/launcher3/model/LoaderTask.java b/src/com/android/launcher3/model/LoaderTask.java
index f1de37d..7275576 100644
--- a/src/com/android/launcher3/model/LoaderTask.java
+++ b/src/com/android/launcher3/model/LoaderTask.java
@@ -49,10 +49,8 @@
import com.android.launcher3.LauncherAppWidgetInfo;
import com.android.launcher3.LauncherModel;
import com.android.launcher3.LauncherSettings;
-import com.android.launcher3.LauncherSettings.Favorites;
import com.android.launcher3.ShortcutInfo;
import com.android.launcher3.Utilities;
-import com.android.launcher3.Workspace;
import com.android.launcher3.compat.AppWidgetManagerCompat;
import com.android.launcher3.compat.LauncherAppsCompat;
import com.android.launcher3.compat.PackageInstallerCompat;
@@ -75,7 +73,6 @@
import com.android.launcher3.util.LooperIdleLock;
import com.android.launcher3.util.MultiHashMap;
import com.android.launcher3.util.PackageManagerHelper;
-import com.android.launcher3.util.Provider;
import com.android.launcher3.util.TraceHelper;
import java.util.ArrayList;
@@ -85,6 +82,7 @@
import java.util.List;
import java.util.Map;
import java.util.concurrent.CancellationException;
+import java.util.function.Supplier;
/**
* Runnable for the thread that loads the contents of the launcher:
@@ -266,8 +264,7 @@
clearDb = true;
}
- if (!clearDb && GridSizeMigrationTask.ENABLED &&
- !GridSizeMigrationTask.migrateGridIfNeeded(context)) {
+ if (!clearDb && !GridSizeMigrationTask.migrateGridIfNeeded(context)) {
// Migration failed. Clear workspace.
clearDb = true;
}
@@ -492,7 +489,7 @@
LauncherIcons li = LauncherIcons.obtain(context);
// If the pinned deep shortcut is no longer published,
// use the last saved icon instead of the default.
- Provider<ItemInfoWithIcon> fallbackIconProvider = () ->
+ Supplier<ItemInfoWithIcon> fallbackIconProvider = () ->
c.loadIcon(finalInfo, li) ? finalInfo : null;
info.applyFrom(li.createShortcutIcon(pinnedShortcut,
true /* badged */, fallbackIconProvider));
diff --git a/src/com/android/launcher3/model/ShortcutsChangedTask.java b/src/com/android/launcher3/model/ShortcutsChangedTask.java
index 4e699f7..1644c89 100644
--- a/src/com/android/launcher3/model/ShortcutsChangedTask.java
+++ b/src/com/android/launcher3/model/ShortcutsChangedTask.java
@@ -29,7 +29,6 @@
import com.android.launcher3.shortcuts.ShortcutKey;
import com.android.launcher3.util.ItemInfoMatcher;
import com.android.launcher3.util.MultiHashMap;
-import com.android.launcher3.util.Provider;
import java.util.ArrayList;
import java.util.HashSet;
@@ -97,7 +96,7 @@
// keep the current icon instead of reverting to the default icon.
LauncherIcons li = LauncherIcons.obtain(context);
shortcutInfo.applyFrom(li.createShortcutIcon(fullDetails, true,
- Provider.of(shortcutInfo)));
+ () -> shortcutInfo));
li.recycle();
updatedShortcutInfos.add(shortcutInfo);
}
diff --git a/src/com/android/launcher3/model/UserLockStateChangedTask.java b/src/com/android/launcher3/model/UserLockStateChangedTask.java
index 59f4284..7c4e454 100644
--- a/src/com/android/launcher3/model/UserLockStateChangedTask.java
+++ b/src/com/android/launcher3/model/UserLockStateChangedTask.java
@@ -32,7 +32,6 @@
import com.android.launcher3.shortcuts.ShortcutKey;
import com.android.launcher3.util.ComponentKey;
import com.android.launcher3.util.ItemInfoMatcher;
-import com.android.launcher3.util.Provider;
import java.util.ArrayList;
import java.util.HashMap;
@@ -95,7 +94,7 @@
// If the shortcut is pinned but no longer has an icon in the system,
// keep the current icon instead of reverting to the default icon.
LauncherIcons li = LauncherIcons.obtain(context);
- si.applyFrom(li.createShortcutIcon(shortcut, true, Provider.of(si)));
+ si.applyFrom(li.createShortcutIcon(shortcut, true, () -> si));
li.recycle();
} else {
si.runtimeStatusFlags |= FLAG_DISABLED_LOCKED_USER;
diff --git a/src/com/android/launcher3/popup/PopupContainerWithArrow.java b/src/com/android/launcher3/popup/PopupContainerWithArrow.java
index 0c098da..288d568 100644
--- a/src/com/android/launcher3/popup/PopupContainerWithArrow.java
+++ b/src/com/android/launcher3/popup/PopupContainerWithArrow.java
@@ -77,7 +77,6 @@
/**
* A container for shortcuts to deep links and notifications associated with an app.
*/
-@TargetApi(Build.VERSION_CODES.N)
public class PopupContainerWithArrow extends ArrowPopup implements DragSource,
DragController.DragListener, View.OnLongClickListener,
View.OnTouchListener {
diff --git a/src/com/android/launcher3/shortcuts/ShortcutCache.java b/src/com/android/launcher3/shortcuts/ShortcutCache.java
deleted file mode 100644
index 5742d1d..0000000
--- a/src/com/android/launcher3/shortcuts/ShortcutCache.java
+++ /dev/null
@@ -1,73 +0,0 @@
-/*
- * Copyright (C) 2016 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.shortcuts;
-
-import android.annotation.TargetApi;
-import android.os.Build;
-import android.os.UserHandle;
-import android.util.ArrayMap;
-import android.util.LruCache;
-import java.util.List;
-
-/**
- * Loads {@link ShortcutInfoCompat}s on demand (e.g. when launcher
- * loads for pinned shortcuts and on long-press for dynamic shortcuts), and caches them
- * for handful of apps in an LruCache while launcher lives.
- */
-@TargetApi(Build.VERSION_CODES.N)
-public class ShortcutCache {
- private static final int CACHE_SIZE = 30; // Max number shortcuts we cache.
-
- private final LruCache<ShortcutKey, ShortcutInfoCompat> mCachedShortcuts;
- // We always keep pinned shortcuts in the cache.
- private final ArrayMap<ShortcutKey, ShortcutInfoCompat> mPinnedShortcuts;
-
- public ShortcutCache() {
- mCachedShortcuts = new LruCache<>(CACHE_SIZE);
- mPinnedShortcuts = new ArrayMap<>();
- }
-
- /**
- * Removes shortcuts from the cache when shortcuts change for a given package.
- *
- * Returns a map of ids to their evicted shortcuts.
- *
- * @see android.content.pm.LauncherApps.Callback#onShortcutsChanged(String, List, UserHandle).
- */
- public void removeShortcuts(List<ShortcutInfoCompat> shortcuts) {
- for (ShortcutInfoCompat shortcut : shortcuts) {
- ShortcutKey key = ShortcutKey.fromInfo(shortcut);
- mCachedShortcuts.remove(key);
- mPinnedShortcuts.remove(key);
- }
- }
-
- public ShortcutInfoCompat get(ShortcutKey key) {
- if (mPinnedShortcuts.containsKey(key)) {
- return mPinnedShortcuts.get(key);
- }
- return mCachedShortcuts.get(key);
- }
-
- public void put(ShortcutKey key, ShortcutInfoCompat shortcut) {
- if (shortcut.isPinned()) {
- mPinnedShortcuts.put(key, shortcut);
- } else {
- mCachedShortcuts.put(key, shortcut);
- }
- }
-}
diff --git a/src/com/android/launcher3/shortcuts/ShortcutInfoCompat.java b/src/com/android/launcher3/shortcuts/ShortcutInfoCompat.java
index 325777d..e5bd002 100644
--- a/src/com/android/launcher3/shortcuts/ShortcutInfoCompat.java
+++ b/src/com/android/launcher3/shortcuts/ShortcutInfoCompat.java
@@ -16,12 +16,10 @@
package com.android.launcher3.shortcuts;
-import android.annotation.TargetApi;
import android.content.ComponentName;
import android.content.Context;
import android.content.Intent;
import android.content.pm.ShortcutInfo;
-import android.os.Build;
import android.os.UserHandle;
import com.android.launcher3.R;
@@ -31,7 +29,6 @@
*
* Not to be confused with {@link com.android.launcher3.ShortcutInfo}.
*/
-@TargetApi(Build.VERSION_CODES.N)
public class ShortcutInfoCompat {
private static final String INTENT_CATEGORY = "com.android.launcher3.DEEP_SHORTCUT";
private static final String EXTRA_BADGEPKG = "badge_package";
@@ -42,7 +39,6 @@
mShortcutInfo = shortcutInfo;
}
- @TargetApi(Build.VERSION_CODES.N)
public Intent makeIntent() {
return new Intent(Intent.ACTION_MAIN)
.addCategory(INTENT_CATEGORY)
diff --git a/src/com/android/launcher3/states/RotationHelper.java b/src/com/android/launcher3/states/RotationHelper.java
index 65103f6..fb41ea1 100644
--- a/src/com/android/launcher3/states/RotationHelper.java
+++ b/src/com/android/launcher3/states/RotationHelper.java
@@ -20,8 +20,6 @@
import static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_UNSPECIFIED;
import static android.util.DisplayMetrics.DENSITY_DEVICE_STABLE;
-import static com.android.launcher3.Utilities.ATLEAST_NOUGAT;
-
import android.app.Activity;
import android.content.SharedPreferences;
import android.content.SharedPreferences.OnSharedPreferenceChangeListener;
@@ -39,15 +37,12 @@
public static final String ALLOW_ROTATION_PREFERENCE_KEY = "pref_allowRotation";
public static boolean getAllowRotationDefaultValue() {
- if (ATLEAST_NOUGAT) {
- // If the device was scaled, used the original dimensions to determine if rotation
- // is allowed of not.
- Resources res = Resources.getSystem();
- int originalSmallestWidth = res.getConfiguration().smallestScreenWidthDp
- * res.getDisplayMetrics().densityDpi / DENSITY_DEVICE_STABLE;
- return originalSmallestWidth >= 600;
- }
- return false;
+ // If the device was scaled, used the original dimensions to determine if rotation
+ // is allowed of not.
+ Resources res = Resources.getSystem();
+ int originalSmallestWidth = res.getConfiguration().smallestScreenWidthDp
+ * res.getDisplayMetrics().densityDpi / DENSITY_DEVICE_STABLE;
+ return originalSmallestWidth >= 600;
}
public static final int REQUEST_NONE = 0;
diff --git a/src/com/android/launcher3/touch/AbstractStateChangeTouchController.java b/src/com/android/launcher3/touch/AbstractStateChangeTouchController.java
index bb14328..0e2ed6c 100644
--- a/src/com/android/launcher3/touch/AbstractStateChangeTouchController.java
+++ b/src/com/android/launcher3/touch/AbstractStateChangeTouchController.java
@@ -46,7 +46,6 @@
import com.android.launcher3.anim.AnimatorPlaybackController;
import com.android.launcher3.anim.AnimatorSetBuilder;
import com.android.launcher3.compat.AccessibilityManagerCompat;
-import com.android.launcher3.config.FeatureFlags;
import com.android.launcher3.userevent.nano.LauncherLogProto;
import com.android.launcher3.userevent.nano.LauncherLogProto.Action.Direction;
import com.android.launcher3.userevent.nano.LauncherLogProto.Action.Touch;
@@ -298,7 +297,7 @@
* When going between normal and overview states, see if we passed the overview threshold and
* play the appropriate atomic animation if so.
*/
- private void maybeUpdateAtomicAnim(LauncherState fromState, LauncherState toState,
+ protected void maybeUpdateAtomicAnim(LauncherState fromState, LauncherState toState,
float progress) {
if (!goingBetweenNormalAndOverview(fromState, toState)) {
return;
@@ -435,7 +434,11 @@
mLauncher.getAppsView().addSpringFromFlingUpdateListener(anim, velocity);
}
anim.start();
- mAtomicAnimAutoPlayInfo = new AutoPlayAtomicAnimationInfo(endProgress, anim.getDuration());
+ settleAtomicAnimation(endProgress, anim.getDuration());
+ }
+
+ protected void settleAtomicAnimation(float endProgress, long duration) {
+ mAtomicAnimAutoPlayInfo = new AutoPlayAtomicAnimationInfo(endProgress, duration);
maybeAutoPlayAtomicComponentsAnim();
}
diff --git a/src/com/android/launcher3/touch/TouchEventTranslator.java b/src/com/android/launcher3/touch/TouchEventTranslator.java
index bf0c84c..3fcda90 100644
--- a/src/com/android/launcher3/touch/TouchEventTranslator.java
+++ b/src/com/android/launcher3/touch/TouchEventTranslator.java
@@ -23,7 +23,7 @@
import android.view.MotionEvent.PointerCoords;
import android.view.MotionEvent.PointerProperties;
-import com.android.launcher3.Utilities.Consumer;
+import java.util.function.Consumer;
/**
* To minimize the size of the MotionEvent, historic events are not copied and passed via the
diff --git a/src/com/android/launcher3/util/ConfigMonitor.java b/src/com/android/launcher3/util/ConfigMonitor.java
index 607afab..12280f8 100644
--- a/src/com/android/launcher3/util/ConfigMonitor.java
+++ b/src/com/android/launcher3/util/ConfigMonitor.java
@@ -29,9 +29,9 @@
import android.view.Display;
import android.view.WindowManager;
-import com.android.launcher3.InvariantDeviceProfile;
import com.android.launcher3.MainThreadExecutor;
-import com.android.launcher3.Utilities.Consumer;
+
+import java.util.function.Consumer;
/**
* {@link BroadcastReceiver} which watches configuration changes and
diff --git a/src/com/android/launcher3/util/LooperIdleLock.java b/src/com/android/launcher3/util/LooperIdleLock.java
index 35cac14..2896535 100644
--- a/src/com/android/launcher3/util/LooperIdleLock.java
+++ b/src/com/android/launcher3/util/LooperIdleLock.java
@@ -19,8 +19,6 @@
import android.os.Looper;
import android.os.MessageQueue;
-import com.android.launcher3.Utilities;
-
/**
* Utility class to block execution until the UI looper is idle.
*/
@@ -33,13 +31,7 @@
public LooperIdleLock(Object lock, Looper looper) {
mLock = lock;
mIsLocked = true;
- if (Utilities.ATLEAST_MARSHMALLOW) {
- looper.getQueue().addIdleHandler(this);
- } else {
- // Looper.myQueue() only gives the current queue. Move the execution to the UI thread
- // so that the IdleHandler is attached to the correct message queue.
- new LooperExecutor(looper).execute(this);
- }
+ looper.getQueue().addIdleHandler(this);
}
@Override
diff --git a/src/com/android/launcher3/util/OverScroller.java b/src/com/android/launcher3/util/OverScroller.java
index d697ece..fc8a138 100644
--- a/src/com/android/launcher3/util/OverScroller.java
+++ b/src/com/android/launcher3/util/OverScroller.java
@@ -26,6 +26,11 @@
import android.view.animation.AnimationUtils;
import android.view.animation.Interpolator;
+import androidx.dynamicanimation.animation.DynamicAnimation;
+import androidx.dynamicanimation.animation.FloatPropertyCompat;
+import androidx.dynamicanimation.animation.SpringAnimation;
+import androidx.dynamicanimation.animation.SpringForce;
+
/**
* Based on {@link android.widget.OverScroller} supporting only 1-d scrolling and with more
* customization options.
@@ -196,6 +201,9 @@
switch (mMode) {
case SCROLL_MODE:
+ if (isSpringing()) {
+ return true;
+ }
long time = AnimationUtils.currentAnimationTimeMillis();
// Any scroller can be used for time, since they were started
// together in scroll mode. We use X here.
@@ -254,6 +262,22 @@
}
/**
+ * Start scrolling using a spring by providing a starting point and the distance to travel.
+ *
+ * @param start Starting scroll offset in pixels. Positive
+ * numbers will scroll the content to the left.
+ * @param delta Distance to travel. Positive numbers will scroll the
+ * content to the left.
+ * @param duration Duration of the scroll in milliseconds.
+ * @param velocity The starting velocity for the spring in px per ms.
+ */
+ public void startScrollSpring(int start, int delta, int duration, float velocity) {
+ mMode = SCROLL_MODE;
+ mScroller.mState = mScroller.SPRING;
+ mScroller.startScroll(start, delta, duration, velocity);
+ }
+
+ /**
* Call this when you want to 'spring back' into a valid coordinate range.
*
* @param start Starting X coordinate
@@ -354,6 +378,10 @@
return (int) (time - mScroller.mStartTime);
}
+ public boolean isSpringing() {
+ return mScroller.mState == SplineOverScroller.SPRING && !isFinished();
+ }
+
static class SplineOverScroller {
// Initial position
private int mStart;
@@ -397,6 +425,8 @@
// Current state of the animation.
private int mState = SPLINE;
+ private SpringAnimation mSpring;
+
// Constant gravity value, used in the deceleration phase.
private static final float GRAVITY = 2000.0f;
@@ -417,6 +447,20 @@
private static final int SPLINE = 0;
private static final int CUBIC = 1;
private static final int BALLISTIC = 2;
+ private static final int SPRING = 3;
+
+ private static final FloatPropertyCompat<SplineOverScroller> SPRING_PROPERTY =
+ new FloatPropertyCompat<SplineOverScroller>("splineOverScrollerSpring") {
+ @Override
+ public float getValue(SplineOverScroller scroller) {
+ return scroller.mCurrentPosition;
+ }
+
+ @Override
+ public void setValue(SplineOverScroller scroller, float value) {
+ scroller.mCurrentPosition = (int) value;
+ }
+ };
static {
float x_min = 0.0f;
@@ -465,6 +509,9 @@
}
void updateScroll(float q) {
+ if (mState == SPRING) {
+ return;
+ }
mCurrentPosition = mStart + Math.round(q * (mFinal - mStart));
}
@@ -495,6 +542,10 @@
}
void startScroll(int start, int distance, int duration) {
+ startScroll(start, distance, duration, 0);
+ }
+
+ void startScroll(int start, int distance, int duration, float velocity) {
mFinished = false;
mCurrentPosition = mStart = start;
@@ -503,12 +554,31 @@
mStartTime = AnimationUtils.currentAnimationTimeMillis();
mDuration = duration;
+ if (mState == SPRING) {
+ if (mSpring != null) {
+ mSpring.cancel();
+ }
+ mSpring = new SpringAnimation(this, SPRING_PROPERTY);
+
+ mSpring.setSpring(new SpringForce(mFinal)
+ .setStiffness(SpringForce.STIFFNESS_LOW)
+ .setDampingRatio(SpringForce.DAMPING_RATIO_LOW_BOUNCY));
+ mSpring.setStartVelocity(velocity);
+ mSpring.animateToFinalPosition(mFinal);
+ mSpring.addEndListener((animation, canceled, value, velocity1) -> {
+ finish();
+ mState = SPLINE;
+ mSpring = null;
+ });
+ }
// Unused
mDeceleration = 0.0f;
mVelocity = 0;
}
void finish() {
+ if (mSpring != null && mSpring.isRunning()) mSpring.cancel();
+
mCurrentPosition = mFinal;
// Not reset since WebView relies on this value for fast fling.
// TODO: restore when WebView uses the fast fling implemented in this class.
@@ -518,6 +588,9 @@
void setFinalPosition(int position) {
mFinal = position;
+ if (mState == SPRING && mSpring != null) {
+ mSpring.animateToFinalPosition(mFinal);
+ }
mSplineDistance = mFinal - mStart;
mFinished = false;
}
@@ -722,6 +795,10 @@
* reached.
*/
boolean update() {
+ if (mState == SPRING) {
+ return mFinished;
+ }
+
final long time = AnimationUtils.currentAnimationTimeMillis();
final long currentTime = time - mStartTime;
diff --git a/src/com/android/launcher3/util/PackageManagerHelper.java b/src/com/android/launcher3/util/PackageManagerHelper.java
index 0b3b632..d71bd15 100644
--- a/src/com/android/launcher3/util/PackageManagerHelper.java
+++ b/src/com/android/launcher3/util/PackageManagerHelper.java
@@ -37,13 +37,11 @@
import com.android.launcher3.AppInfo;
import com.android.launcher3.ItemInfo;
-import com.android.launcher3.Launcher;
import com.android.launcher3.LauncherAppWidgetInfo;
import com.android.launcher3.PendingAddItemInfo;
import com.android.launcher3.PromiseAppInfo;
import com.android.launcher3.R;
import com.android.launcher3.ShortcutInfo;
-import com.android.launcher3.Utilities;
import com.android.launcher3.compat.LauncherAppsCompat;
import java.net.URISyntaxException;
@@ -100,14 +98,7 @@
* {@link android.app.admin.DevicePolicyManager#isPackageSuspended}.
*/
public static boolean isAppSuspended(ApplicationInfo info) {
- // The value of FLAG_SUSPENDED was reused by a hidden constant
- // ApplicationInfo.FLAG_PRIVILEGED prior to N, so only check for suspended flag on N
- // or later.
- if (Utilities.ATLEAST_NOUGAT) {
- return (info.flags & ApplicationInfo.FLAG_SUSPENDED) != 0;
- } else {
- return false;
- }
+ return (info.flags & ApplicationInfo.FLAG_SUSPENDED) != 0;
}
/**
@@ -136,11 +127,6 @@
return false;
}
- if (!Utilities.ATLEAST_MARSHMALLOW) {
- // These checks are sufficient for below M devices.
- return true;
- }
-
// On M and above also check AppOpsManager for compatibility mode permissions.
if (TextUtils.isEmpty(AppOpsManager.permissionToOp(target.activityInfo.permission))) {
// There is no app-op for this permission, which could have been disabled.
diff --git a/src/com/android/launcher3/views/ScrimView.java b/src/com/android/launcher3/views/ScrimView.java
index deb0965..a264f9b 100644
--- a/src/com/android/launcher3/views/ScrimView.java
+++ b/src/com/android/launcher3/views/ScrimView.java
@@ -315,7 +315,7 @@
if (enabled) {
stateManager.addStateListener(this);
- onStateSetImmediately(mLauncher.getStateManager().getState());
+ handleStateChangedComplete(mLauncher.getStateManager().getState());
} else {
setImportantForAccessibility(IMPORTANT_FOR_ACCESSIBILITY_NO_HIDE_DESCENDANTS);
}
@@ -361,12 +361,11 @@
@Override
public void onStateTransitionComplete(LauncherState finalState) {
- onStateSetImmediately(finalState);
+ handleStateChangedComplete(finalState);
}
- @Override
- public void onStateSetImmediately(LauncherState state) {
- setImportantForAccessibility(state == ALL_APPS
+ private void handleStateChangedComplete(LauncherState finalState) {
+ setImportantForAccessibility(finalState == ALL_APPS
? IMPORTANT_FOR_ACCESSIBILITY_NO_HIDE_DESCENDANTS
: IMPORTANT_FOR_ACCESSIBILITY_AUTO);
}
diff --git a/src/com/android/launcher3/widget/BaseWidgetSheet.java b/src/com/android/launcher3/widget/BaseWidgetSheet.java
index 673b3cc..508695b 100644
--- a/src/com/android/launcher3/widget/BaseWidgetSheet.java
+++ b/src/com/android/launcher3/widget/BaseWidgetSheet.java
@@ -161,7 +161,7 @@
private static View createColorScrim(Context context) {
View view = new View(context);
- if (Utilities.ATLEAST_NOUGAT) view.forceHasOverlappingRendering(false);
+ view.forceHasOverlappingRendering(false);
WallpaperColorInfo colors = WallpaperColorInfo.getInstance(context);
int alpha = context.getResources().getInteger(R.integer.extracted_color_gradient_alpha);
diff --git a/src_shortcuts_overrides/com/android/launcher3/shortcuts/DeepShortcutManager.java b/src_shortcuts_overrides/com/android/launcher3/shortcuts/DeepShortcutManager.java
index e70aac6..6c0c429 100644
--- a/src_shortcuts_overrides/com/android/launcher3/shortcuts/DeepShortcutManager.java
+++ b/src_shortcuts_overrides/com/android/launcher3/shortcuts/DeepShortcutManager.java
@@ -16,7 +16,6 @@
package com.android.launcher3.shortcuts;
-import android.annotation.TargetApi;
import android.content.ComponentName;
import android.content.Context;
import android.content.pm.LauncherApps;
@@ -30,7 +29,6 @@
import com.android.launcher3.ItemInfo;
import com.android.launcher3.LauncherSettings;
-import com.android.launcher3.Utilities;
import java.util.ArrayList;
import java.util.Collections;
@@ -94,7 +92,6 @@
* Gets all the manifest and dynamic shortcuts associated with the given package and user,
* to be displayed in the shortcuts container on long press.
*/
- @TargetApi(25)
public List<ShortcutInfoCompat> queryForShortcutsContainer(ComponentName activity,
UserHandle user) {
return query(ShortcutQuery.FLAG_MATCH_MANIFEST | ShortcutQuery.FLAG_MATCH_DYNAMIC,
@@ -105,21 +102,18 @@
* Removes the given shortcut from the current list of pinned shortcuts.
* (Runs on background thread)
*/
- @TargetApi(25)
public void unpinShortcut(final ShortcutKey key) {
- if (Utilities.ATLEAST_NOUGAT_MR1) {
- String packageName = key.componentName.getPackageName();
- String id = key.getId();
- UserHandle user = key.user;
- List<String> pinnedIds = extractIds(queryForPinnedShortcuts(packageName, user));
- pinnedIds.remove(id);
- try {
- mLauncherApps.pinShortcuts(packageName, pinnedIds, user);
- mWasLastCallSuccess = true;
- } catch (SecurityException|IllegalStateException e) {
- Log.w(TAG, "Failed to unpin shortcut", e);
- mWasLastCallSuccess = false;
- }
+ String packageName = key.componentName.getPackageName();
+ String id = key.getId();
+ UserHandle user = key.user;
+ List<String> pinnedIds = extractIds(queryForPinnedShortcuts(packageName, user));
+ pinnedIds.remove(id);
+ try {
+ mLauncherApps.pinShortcuts(packageName, pinnedIds, user);
+ mWasLastCallSuccess = true;
+ } catch (SecurityException|IllegalStateException e) {
+ Log.w(TAG, "Failed to unpin shortcut", e);
+ mWasLastCallSuccess = false;
}
}
@@ -127,51 +121,42 @@
* Adds the given shortcut to the current list of pinned shortcuts.
* (Runs on background thread)
*/
- @TargetApi(25)
public void pinShortcut(final ShortcutKey key) {
- if (Utilities.ATLEAST_NOUGAT_MR1) {
- String packageName = key.componentName.getPackageName();
- String id = key.getId();
- UserHandle user = key.user;
- List<String> pinnedIds = extractIds(queryForPinnedShortcuts(packageName, user));
- pinnedIds.add(id);
- try {
- mLauncherApps.pinShortcuts(packageName, pinnedIds, user);
- mWasLastCallSuccess = true;
- } catch (SecurityException|IllegalStateException e) {
- Log.w(TAG, "Failed to pin shortcut", e);
- mWasLastCallSuccess = false;
- }
+ String packageName = key.componentName.getPackageName();
+ String id = key.getId();
+ UserHandle user = key.user;
+ List<String> pinnedIds = extractIds(queryForPinnedShortcuts(packageName, user));
+ pinnedIds.add(id);
+ try {
+ mLauncherApps.pinShortcuts(packageName, pinnedIds, user);
+ mWasLastCallSuccess = true;
+ } catch (SecurityException|IllegalStateException e) {
+ Log.w(TAG, "Failed to pin shortcut", e);
+ mWasLastCallSuccess = false;
}
}
- @TargetApi(25)
public void startShortcut(String packageName, String id, Rect sourceBounds,
Bundle startActivityOptions, UserHandle user) {
- if (Utilities.ATLEAST_NOUGAT_MR1) {
- try {
- mLauncherApps.startShortcut(packageName, id, sourceBounds,
- startActivityOptions, user);
- mWasLastCallSuccess = true;
- } catch (SecurityException|IllegalStateException e) {
- Log.e(TAG, "Failed to start shortcut", e);
- mWasLastCallSuccess = false;
- }
+ try {
+ mLauncherApps.startShortcut(packageName, id, sourceBounds,
+ startActivityOptions, user);
+ mWasLastCallSuccess = true;
+ } catch (SecurityException|IllegalStateException e) {
+ Log.e(TAG, "Failed to start shortcut", e);
+ mWasLastCallSuccess = false;
}
}
- @TargetApi(25)
public Drawable getShortcutIconDrawable(ShortcutInfoCompat shortcutInfo, int density) {
- if (Utilities.ATLEAST_NOUGAT_MR1) {
- try {
- Drawable icon = mLauncherApps.getShortcutIconDrawable(
- shortcutInfo.getShortcutInfo(), density);
- mWasLastCallSuccess = true;
- return icon;
- } catch (SecurityException|IllegalStateException e) {
- Log.e(TAG, "Failed to get shortcut icon", e);
- mWasLastCallSuccess = false;
- }
+ try {
+ Drawable icon = mLauncherApps.getShortcutIconDrawable(
+ shortcutInfo.getShortcutInfo(), density);
+ mWasLastCallSuccess = true;
+ return icon;
+ } catch (SecurityException|IllegalStateException e) {
+ Log.e(TAG, "Failed to get shortcut icon", e);
+ mWasLastCallSuccess = false;
}
return null;
}
@@ -208,46 +193,38 @@
*
* TODO: Use the cache to optimize this so we don't make an RPC every time.
*/
- @TargetApi(25)
private List<ShortcutInfoCompat> query(int flags, String packageName,
ComponentName activity, List<String> shortcutIds, UserHandle user) {
- if (Utilities.ATLEAST_NOUGAT_MR1) {
- ShortcutQuery q = new ShortcutQuery();
- q.setQueryFlags(flags);
- if (packageName != null) {
- q.setPackage(packageName);
- q.setActivity(activity);
- q.setShortcutIds(shortcutIds);
- }
- List<ShortcutInfo> shortcutInfos = null;
- try {
- shortcutInfos = mLauncherApps.getShortcuts(q, user);
- mWasLastCallSuccess = true;
- } catch (SecurityException|IllegalStateException e) {
- Log.e(TAG, "Failed to query for shortcuts", e);
- mWasLastCallSuccess = false;
- }
- if (shortcutInfos == null) {
- return Collections.EMPTY_LIST;
- }
- List<ShortcutInfoCompat> shortcutInfoCompats = new ArrayList<>(shortcutInfos.size());
- for (ShortcutInfo shortcutInfo : shortcutInfos) {
- shortcutInfoCompats.add(new ShortcutInfoCompat(shortcutInfo));
- }
- return shortcutInfoCompats;
- } else {
+ ShortcutQuery q = new ShortcutQuery();
+ q.setQueryFlags(flags);
+ if (packageName != null) {
+ q.setPackage(packageName);
+ q.setActivity(activity);
+ q.setShortcutIds(shortcutIds);
+ }
+ List<ShortcutInfo> shortcutInfos = null;
+ try {
+ shortcutInfos = mLauncherApps.getShortcuts(q, user);
+ mWasLastCallSuccess = true;
+ } catch (SecurityException|IllegalStateException e) {
+ Log.e(TAG, "Failed to query for shortcuts", e);
+ mWasLastCallSuccess = false;
+ }
+ if (shortcutInfos == null) {
return Collections.EMPTY_LIST;
}
+ List<ShortcutInfoCompat> shortcutInfoCompats = new ArrayList<>(shortcutInfos.size());
+ for (ShortcutInfo shortcutInfo : shortcutInfos) {
+ shortcutInfoCompats.add(new ShortcutInfoCompat(shortcutInfo));
+ }
+ return shortcutInfoCompats;
}
- @TargetApi(25)
public boolean hasHostPermission() {
- if (Utilities.ATLEAST_NOUGAT_MR1) {
- try {
- return mLauncherApps.hasShortcutHostPermission();
- } catch (SecurityException|IllegalStateException e) {
- Log.e(TAG, "Failed to make shortcut manager call", e);
- }
+ try {
+ return mLauncherApps.hasShortcutHostPermission();
+ } catch (SecurityException|IllegalStateException e) {
+ Log.e(TAG, "Failed to make shortcut manager call", e);
}
return false;
}
diff --git a/src_ui_overrides/com/android/launcher3/uioverrides/dynamicui/WallpaperManagerCompatVL.java b/src_ui_overrides/com/android/launcher3/uioverrides/dynamicui/WallpaperManagerCompatVL.java
index 6808859..500fdc3 100644
--- a/src_ui_overrides/com/android/launcher3/uioverrides/dynamicui/WallpaperManagerCompatVL.java
+++ b/src_ui_overrides/com/android/launcher3/uioverrides/dynamicui/WallpaperManagerCompatVL.java
@@ -44,7 +44,6 @@
import android.util.Log;
import android.util.Pair;
-import com.android.launcher3.Utilities;
import com.android.launcher3.icons.ColorExtractor;
import java.io.IOException;
@@ -61,6 +60,8 @@
private static final String ACTION_EXTRACTION_COMPLETE =
"com.android.launcher3.uioverrides.dynamicui.WallpaperManagerCompatVL.EXTRACTION_COMPLETE";
+ public static final int WALLPAPER_COMPAT_JOB_ID = 1;
+
private final ArrayList<OnColorsChangedListenerCompat> mListeners = new ArrayList<>();
private final Context mContext;
@@ -122,7 +123,7 @@
}
private void reloadColors() {
- JobInfo job = new JobInfo.Builder(Utilities.WALLPAPER_COMPAT_JOB_ID,
+ JobInfo job = new JobInfo.Builder(WALLPAPER_COMPAT_JOB_ID,
new ComponentName(mContext, ColorExtractionService.class))
.setMinimumLatency(0).build();
((JobScheduler) mContext.getSystemService(Context.JOB_SCHEDULER_SERVICE)).schedule(job);
@@ -137,9 +138,6 @@
}
private static final int getWallpaperId(Context context) {
- if (!Utilities.ATLEAST_NOUGAT) {
- return -1;
- }
return context.getSystemService(WallpaperManager.class).getWallpaperId(FLAG_SYSTEM);
}
@@ -215,27 +213,25 @@
// For live wallpaper, extract colors from thumbnail
drawable = info.loadThumbnail(getPackageManager());
} else {
- if (Utilities.ATLEAST_NOUGAT) {
- try (ParcelFileDescriptor fd = wm.getWallpaperFile(FLAG_SYSTEM)) {
- BitmapRegionDecoder decoder = BitmapRegionDecoder
- .newInstance(fd.getFileDescriptor(), false);
+ try (ParcelFileDescriptor fd = wm.getWallpaperFile(FLAG_SYSTEM)) {
+ BitmapRegionDecoder decoder = BitmapRegionDecoder
+ .newInstance(fd.getFileDescriptor(), false);
- int requestedArea = decoder.getWidth() * decoder.getHeight();
- BitmapFactory.Options options = new BitmapFactory.Options();
+ int requestedArea = decoder.getWidth() * decoder.getHeight();
+ BitmapFactory.Options options = new BitmapFactory.Options();
- if (requestedArea > MAX_WALLPAPER_EXTRACTION_AREA) {
- double areaRatio =
- (double) requestedArea / MAX_WALLPAPER_EXTRACTION_AREA;
- double nearestPowOf2 =
- Math.floor(Math.log(areaRatio) / (2 * Math.log(2)));
- options.inSampleSize = (int) Math.pow(2, nearestPowOf2);
- }
- Rect region = new Rect(0, 0, decoder.getWidth(), decoder.getHeight());
- bitmap = decoder.decodeRegion(region, options);
- decoder.recycle();
- } catch (IOException | NullPointerException e) {
- Log.e(TAG, "Fetching partial bitmap failed, trying old method", e);
+ if (requestedArea > MAX_WALLPAPER_EXTRACTION_AREA) {
+ double areaRatio =
+ (double) requestedArea / MAX_WALLPAPER_EXTRACTION_AREA;
+ double nearestPowOf2 =
+ Math.floor(Math.log(areaRatio) / (2 * Math.log(2)));
+ options.inSampleSize = (int) Math.pow(2, nearestPowOf2);
}
+ Rect region = new Rect(0, 0, decoder.getWidth(), decoder.getHeight());
+ bitmap = decoder.decodeRegion(region, options);
+ decoder.recycle();
+ } catch (IOException | NullPointerException e) {
+ Log.e(TAG, "Fetching partial bitmap failed, trying old method", e);
}
if (bitmap == null) {
drawable = wm.getDrawable();
diff --git a/tests/AndroidManifest.xml b/tests/AndroidManifest.xml
index ebab122..24b5b02 100644
--- a/tests/AndroidManifest.xml
+++ b/tests/AndroidManifest.xml
@@ -18,7 +18,7 @@
xmlns:tools="http://schemas.android.com/tools"
package="com.android.launcher3.tests">
- <uses-sdk android:targetSdkVersion="28" android:minSdkVersion="21"
+ <uses-sdk android:targetSdkVersion="28" android:minSdkVersion="25"
tools:overrideLibrary="android.support.test.uiautomator.v18"/>
<application android:debuggable="true">
diff --git a/tests/src/com/android/launcher3/allapps/search/DefaultAppSearchAlgorithmTest.java b/tests/src/com/android/launcher3/allapps/search/DefaultAppSearchAlgorithmTest.java
index afbedba..293b04a 100644
--- a/tests/src/com/android/launcher3/allapps/search/DefaultAppSearchAlgorithmTest.java
+++ b/tests/src/com/android/launcher3/allapps/search/DefaultAppSearchAlgorithmTest.java
@@ -20,15 +20,14 @@
import android.content.ComponentName;
-import androidx.test.filters.SmallTest;
-import androidx.test.runner.AndroidJUnit4;
-
import com.android.launcher3.AppInfo;
-import com.android.launcher3.Utilities;
import org.junit.Test;
import org.junit.runner.RunWith;
+import androidx.test.filters.SmallTest;
+import androidx.test.runner.AndroidJUnit4;
+
/**
* Unit tests for {@link DefaultAppSearchAlgorithm}
*/
@@ -78,9 +77,6 @@
@Test
public void testMatchesVN() {
- if (!Utilities.ATLEAST_NOUGAT) {
- return;
- }
assertTrue(DefaultAppSearchAlgorithm.matches(getInfo("다운로드"), "다", MATCHER));
assertTrue(DefaultAppSearchAlgorithm.matches(getInfo("드라이브"), "드", MATCHER));
assertTrue(DefaultAppSearchAlgorithm.matches(getInfo("다운로드 드라이브"), "ㄷ", MATCHER));
diff --git a/tests/src/com/android/launcher3/ui/AbstractLauncherUiTest.java b/tests/src/com/android/launcher3/ui/AbstractLauncherUiTest.java
index 5b222f8..02f5502 100644
--- a/tests/src/com/android/launcher3/ui/AbstractLauncherUiTest.java
+++ b/tests/src/com/android/launcher3/ui/AbstractLauncherUiTest.java
@@ -348,4 +348,17 @@
waitForLauncherCondition(
"Launcher still active", launcher -> launcher == null, DEFAULT_UI_TIMEOUT);
}
+
+ protected boolean isInBackground(Launcher launcher) {
+ return !launcher.hasBeenResumed();
+ }
+
+ protected boolean isInState(LauncherState state) {
+ if (!TestHelpers.isInLauncherProcess()) return true;
+ return getFromLauncher(launcher -> launcher.getStateManager().getState() == state);
+ }
+
+ protected int getAllAppsScroll(Launcher launcher) {
+ return launcher.getAppsView().getActiveRecyclerView().getCurrentScrollY();
+ }
}
diff --git a/tests/src/com/android/launcher3/ui/TaplTestsLauncher3.java b/tests/src/com/android/launcher3/ui/TaplTestsLauncher3.java
new file mode 100644
index 0000000..ab5761d
--- /dev/null
+++ b/tests/src/com/android/launcher3/ui/TaplTestsLauncher3.java
@@ -0,0 +1,287 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.launcher3.ui;
+
+import static androidx.test.InstrumentationRegistry.getInstrumentation;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertNull;
+import static org.junit.Assert.assertTrue;
+
+import android.content.Intent;
+import android.util.Log;
+
+import androidx.test.filters.LargeTest;
+import androidx.test.runner.AndroidJUnit4;
+import androidx.test.uiautomator.UiDevice;
+
+import com.android.launcher3.Launcher;
+import com.android.launcher3.LauncherState;
+import com.android.launcher3.tapl.AllApps;
+import com.android.launcher3.tapl.AppIcon;
+import com.android.launcher3.tapl.Widgets;
+import com.android.launcher3.tapl.Workspace;
+import com.android.launcher3.views.OptionsPopupView;
+import com.android.launcher3.widget.WidgetsFullSheet;
+import com.android.launcher3.widget.WidgetsRecyclerView;
+
+import org.junit.Before;
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.rules.TestWatcher;
+import org.junit.runner.Description;
+import org.junit.runner.RunWith;
+
+import java.io.ByteArrayOutputStream;
+import java.io.File;
+import java.io.IOException;
+
+@LargeTest
+@RunWith(AndroidJUnit4.class)
+public class TaplTestsLauncher3 extends AbstractLauncherUiTest {
+ private static final String TAG = "TaplTestsAosp";
+
+ private static int sScreenshotCount = 0;
+
+ public static class FailureWatcher extends TestWatcher {
+ private UiDevice mDevice;
+
+ public FailureWatcher(UiDevice device) {
+ this.mDevice = device;
+ }
+
+ private void dumpViewHierarchy() {
+ final ByteArrayOutputStream stream = new ByteArrayOutputStream();
+ try {
+ mDevice.dumpWindowHierarchy(stream);
+ stream.flush();
+ stream.close();
+ for (String line : stream.toString().split("\\r?\\n")) {
+ Log.e(TAG, line.trim());
+ }
+ } catch (IOException e) {
+ Log.e(TAG, "error dumping XML to logcat", e);
+ }
+ }
+
+ @Override
+ protected void failed(Throwable e, Description description) {
+ if (mDevice == null) return;
+ final String pathname = getInstrumentation().getTargetContext().
+ getFilesDir().getPath() + "/TaplTestScreenshot" + sScreenshotCount++ + ".png";
+ Log.e(TAG, "Failed test " + description.getMethodName() +
+ ", screenshot will be saved to " + pathname +
+ ", track trace is below, UI object dump is further below:\n" +
+ Log.getStackTraceString(e));
+ dumpViewHierarchy();
+ mDevice.takeScreenshot(new File(pathname));
+ }
+ }
+
+ @Rule
+ public TestWatcher mFailureWatcher = new FailureWatcher(mDevice);
+
+ @Before
+ public void setUp() throws Exception {
+ super.setUp();
+
+ clearLauncherData();
+
+ mLauncher.pressHome();
+ waitForState("Launcher internal state didn't switch to Home", LauncherState.NORMAL);
+ waitForResumed("Launcher internal state is still Background");
+ }
+
+ // Please don't add negative test cases for methods that fail only after a long wait.
+ public static void expectFail(String message, Runnable action) {
+ boolean failed = false;
+ try {
+ action.run();
+ } catch (AssertionError e) {
+ failed = true;
+ }
+ assertTrue(message, failed);
+ }
+
+ private boolean isWorkspaceScrollable(Launcher launcher) {
+ return launcher.getWorkspace().getPageCount() > 1;
+ }
+
+ private int getCurrentWorkspacePage(Launcher launcher) {
+ return launcher.getWorkspace().getCurrentPage();
+ }
+
+ private WidgetsRecyclerView getWidgetsView(Launcher launcher) {
+ return WidgetsFullSheet.getWidgetsView(launcher);
+ }
+
+ @Test
+ public void testDevicePressMenu() throws Exception {
+ mDevice.pressMenu();
+ mDevice.waitForIdle();
+ executeOnLauncher(
+ launcher -> assertTrue("Launcher internal state didn't switch to Showing Menu",
+ OptionsPopupView.getOptionsPopup(launcher) != null));
+ }
+
+ public static void runAllAppsTest(AbstractLauncherUiTest test, AllApps allApps) {
+ assertNotNull("allApps parameter is null", allApps);
+
+ assertTrue(
+ "Launcher internal state is not All Apps", test.isInState(LauncherState.ALL_APPS));
+
+ // Test flinging forward and backward.
+ test.executeOnLauncher(launcher -> assertEquals(
+ "All Apps started in already scrolled state", 0, test.getAllAppsScroll(launcher)));
+
+ allApps.flingForward();
+ assertTrue("Launcher internal state is not All Apps",
+ test.isInState(LauncherState.ALL_APPS));
+ final Integer flingForwardY = test.getFromLauncher(
+ launcher -> test.getAllAppsScroll(launcher));
+ test.executeOnLauncher(
+ launcher -> assertTrue("flingForward() didn't scroll App Apps", flingForwardY > 0));
+
+ allApps.flingBackward();
+ assertTrue(
+ "Launcher internal state is not All Apps", test.isInState(LauncherState.ALL_APPS));
+ final Integer flingBackwardY = test.getFromLauncher(
+ launcher -> test.getAllAppsScroll(launcher));
+ test.executeOnLauncher(launcher -> assertTrue("flingBackward() didn't scroll App Apps",
+ flingBackwardY < flingForwardY));
+
+ // Test scrolling down to YouTube.
+ assertNotNull("All apps: can't fine YouTube", allApps.getAppIcon("YouTube"));
+ // Test scrolling up to Camera.
+ assertNotNull("All apps: can't fine Camera", allApps.getAppIcon("Camera"));
+ // Test failing to find a non-existing app.
+ final AllApps allAppsFinal = allApps;
+ expectFail("All apps: could find a non-existing app",
+ () -> allAppsFinal.getAppIcon("NO APP"));
+
+ assertTrue(
+ "Launcher internal state is not All Apps", test.isInState(LauncherState.ALL_APPS));
+ }
+
+ @Test
+ @PortraitLandscape
+ public void testWorkspaceSwitchToAllApps() {
+ assertNotNull("switchToAllApps() returned null",
+ mLauncher.getWorkspace().switchToAllApps());
+ assertTrue("Launcher internal state is not All Apps", isInState(LauncherState.ALL_APPS));
+ }
+
+ @Test
+ public void testWorkspace() throws Exception {
+ final Workspace workspace = mLauncher.getWorkspace();
+
+ // Test that ensureWorkspaceIsScrollable adds a page by dragging an icon there.
+ executeOnLauncher(launcher -> assertFalse("Initial workspace state is scrollable",
+ isWorkspaceScrollable(launcher)));
+ assertNull("Messages app was found on empty workspace",
+ workspace.tryGetWorkspaceAppIcon("Messages"));
+
+ workspace.ensureWorkspaceIsScrollable();
+
+ executeOnLauncher(
+ launcher -> assertEquals("Ensuring workspace scrollable didn't switch to page #1",
+ 1, getCurrentWorkspacePage(launcher)));
+ executeOnLauncher(
+ launcher -> assertTrue("ensureScrollable didn't make workspace scrollable",
+ isWorkspaceScrollable(launcher)));
+ assertNotNull("ensureScrollable didn't add Messages app",
+ workspace.tryGetWorkspaceAppIcon("Messages"));
+
+ // Test flinging workspace.
+ workspace.flingBackward();
+ assertTrue("Launcher internal state is not Home", isInState(LauncherState.NORMAL));
+ executeOnLauncher(
+ launcher -> assertEquals("Flinging back didn't switch workspace to page #0",
+ 0, getCurrentWorkspacePage(launcher)));
+
+ workspace.flingForward();
+ executeOnLauncher(
+ launcher -> assertEquals("Flinging forward didn't switch workspace to page #1",
+ 1, getCurrentWorkspacePage(launcher)));
+ assertTrue("Launcher internal state is not Home", isInState(LauncherState.NORMAL));
+
+ // Test starting a workspace app.
+ final AppIcon app = workspace.tryGetWorkspaceAppIcon("Messages");
+ assertNotNull("No Messages app in workspace", app);
+ assertNotNull("AppIcon.launch returned null",
+ app.launch(resolveSystemApp(Intent.CATEGORY_APP_MESSAGING)));
+ executeOnLauncher(launcher -> assertTrue(
+ "Launcher activity is the top activity; expecting another activity to be the top "
+ + "one",
+ isInBackground(launcher)));
+ }
+
+ public static void runIconLaunchFromAllAppsTest(AbstractLauncherUiTest test, AllApps allApps) {
+ final AppIcon app = allApps.getAppIcon("Calculator");
+ assertNotNull("AppIcon.launch returned null", app.launch(
+ test.resolveSystemApp(Intent.CATEGORY_APP_CALCULATOR)));
+ test.executeOnLauncher(launcher -> assertTrue(
+ "Launcher activity is the top activity; expecting another activity to be the top "
+ + "one",
+ test.isInBackground(launcher)));
+ }
+
+ @Test
+ @PortraitLandscape
+ public void testAppIconLaunchFromAllAppsFromHome() throws Exception {
+ final AllApps allApps = mLauncher.getWorkspace().switchToAllApps();
+ assertTrue("Launcher internal state is not All Apps", isInState(LauncherState.ALL_APPS));
+
+ runIconLaunchFromAllAppsTest(this, allApps);
+ }
+
+ @Test
+ @PortraitLandscape
+ public void testWidgets() throws Exception {
+ // Test opening widgets.
+ executeOnLauncher(launcher ->
+ assertTrue("Widgets is initially opened", getWidgetsView(launcher) == null));
+ Widgets widgets = mLauncher.getWorkspace().openAllWidgets();
+ assertNotNull("openAllWidgets() returned null", widgets);
+ widgets = mLauncher.getAllWidgets();
+ assertNotNull("getAllWidgets() returned null", widgets);
+ executeOnLauncher(launcher ->
+ assertTrue("Widgets is not shown", getWidgetsView(launcher).isShown()));
+ executeOnLauncher(launcher -> assertEquals("Widgets is scrolled upon opening",
+ 0, getWidgetsScroll(launcher)));
+
+ // Test flinging widgets.
+ widgets.flingForward();
+ Integer flingForwardY = getFromLauncher(launcher -> getWidgetsScroll(launcher));
+ executeOnLauncher(launcher -> assertTrue("Flinging forward didn't scroll widgets",
+ flingForwardY > 0));
+
+ widgets.flingBackward();
+ executeOnLauncher(launcher -> assertTrue("Flinging backward didn't scroll widgets",
+ getWidgetsScroll(launcher) < flingForwardY));
+
+ mLauncher.pressHome();
+ waitForLauncherCondition("Widgets were not closed",
+ launcher -> getWidgetsView(launcher) == null);
+ }
+
+ private int getWidgetsScroll(Launcher launcher) {
+ return getWidgetsView(launcher).getCurrentScrollY();
+ }
+}
diff --git a/tests/src/com/android/launcher3/util/Wait.java b/tests/src/com/android/launcher3/util/Wait.java
index 0e41c02..593cce8 100644
--- a/tests/src/com/android/launcher3/util/Wait.java
+++ b/tests/src/com/android/launcher3/util/Wait.java
@@ -23,7 +23,7 @@
return;
}
} catch (Throwable t) {
- // Ignore
+ throw new RuntimeException(t);
}
SystemClock.sleep(sleepMillis);
}
@@ -34,7 +34,7 @@
return;
}
} catch (Throwable t) {
- // Ignore
+ throw new RuntimeException(t);
}
Assert.fail(message);
}
diff --git a/tests/tapl/com/android/launcher3/tapl/AppIcon.java b/tests/tapl/com/android/launcher3/tapl/AppIcon.java
index 3ffd30c..d39a38e 100644
--- a/tests/tapl/com/android/launcher3/tapl/AppIcon.java
+++ b/tests/tapl/com/android/launcher3/tapl/AppIcon.java
@@ -17,6 +17,7 @@
package com.android.launcher3.tapl;
import android.graphics.Point;
+import android.view.MotionEvent;
import android.widget.TextView;
import androidx.test.uiautomator.By;
@@ -40,9 +41,10 @@
*/
public AppIconMenu openMenu() {
final Point iconCenter = mObject.getVisibleCenter();
- mLauncher.longTap(iconCenter.x, iconCenter.y);
+ mLauncher.sendPointer(MotionEvent.ACTION_DOWN, iconCenter);
final UiObject2 deepShortcutsContainer = mLauncher.waitForLauncherObject(
"deep_shortcuts_container");
+ mLauncher.sendPointer(MotionEvent.ACTION_UP, iconCenter);
return new AppIconMenu(mLauncher, deepShortcutsContainer);
}
}
diff --git a/tests/tapl/com/android/launcher3/tapl/LauncherInstrumentation.java b/tests/tapl/com/android/launcher3/tapl/LauncherInstrumentation.java
index 49bd73a..444f3bd 100644
--- a/tests/tapl/com/android/launcher3/tapl/LauncherInstrumentation.java
+++ b/tests/tapl/com/android/launcher3/tapl/LauncherInstrumentation.java
@@ -21,10 +21,13 @@
import android.app.ActivityManager;
import android.app.Instrumentation;
import android.app.UiAutomation;
+import android.graphics.Point;
import android.os.Bundle;
import android.os.Parcelable;
+import android.os.SystemClock;
import android.provider.Settings;
import android.util.Log;
+import android.view.MotionEvent;
import android.view.Surface;
import android.view.accessibility.AccessibilityEvent;
@@ -403,11 +406,6 @@
return mDevice;
}
- void longTap(int x, int y) {
- mDevice.drag(x, y, x, y, 0);
- }
-
-
void swipe(int startX, int startY, int endX, int endY) {
executeAndWaitForEvent(
() -> mDevice.swipe(startX, startY, endX, endY, 60),
@@ -419,4 +417,11 @@
void waitForIdle() {
mDevice.waitForIdle();
}
+
+ void sendPointer(int action, Point point) {
+ final MotionEvent event = MotionEvent.obtain(SystemClock.uptimeMillis(),
+ SystemClock.uptimeMillis(), action, point.x, point.y, 0);
+ mInstrumentation.sendPointerSync(event);
+ event.recycle();
+ }
}
\ No newline at end of file