From a93c2dcd564c1d3278f5f5e52674aba9938399be Mon Sep 17 00:00:00 2001 From: Hongwei Wang Date: Mon, 10 Feb 2020 14:35:35 -0800 Subject: [PATCH 0001/1664] Indicate PiP animation type from Launcher to SysUI Also, need callback from SysUI to Launcher once the pip animation is started to keep back compatibility. Bug: 146594635 Test: manually Change-Id: I617a17fd5f6049d9f1d49f9f834623a2bb49f6ca --- .../quickstep/FallbackSwipeHandler.java | 2 ++ .../quickstep/LauncherSwipeHandler.java | 2 ++ .../android/quickstep/views/RecentsView.java | 9 +++++ .../com/android/quickstep/SystemUiProxy.java | 33 +++++++++++++++++-- 4 files changed, 43 insertions(+), 3 deletions(-) diff --git a/quickstep/recents_ui_overrides/src/com/android/quickstep/FallbackSwipeHandler.java b/quickstep/recents_ui_overrides/src/com/android/quickstep/FallbackSwipeHandler.java index 700feef790..cd001a128b 100644 --- a/quickstep/recents_ui_overrides/src/com/android/quickstep/FallbackSwipeHandler.java +++ b/quickstep/recents_ui_overrides/src/com/android/quickstep/FallbackSwipeHandler.java @@ -332,6 +332,8 @@ public class FallbackSwipeHandler extends BaseSwipeUpHandler { if (!mTouchedHomeDuringTransition) { // If the user hasn't interacted with the screen during the transition, diff --git a/quickstep/recents_ui_overrides/src/com/android/quickstep/LauncherSwipeHandler.java b/quickstep/recents_ui_overrides/src/com/android/quickstep/LauncherSwipeHandler.java index 345a147ca5..646d01fb8f 100644 --- a/quickstep/recents_ui_overrides/src/com/android/quickstep/LauncherSwipeHandler.java +++ b/quickstep/recents_ui_overrides/src/com/android/quickstep/LauncherSwipeHandler.java @@ -699,6 +699,8 @@ public class LauncherSwipeHandler switch (mGestureState.getEndTarget()) { case HOME: mStateCallback.setState(STATE_SCALED_CONTROLLER_HOME | STATE_CAPTURE_SCREENSHOT); + // Notify swipe-to-home (recents animation) is finished + SystemUiProxy.INSTANCE.get(mContext).notifySwipeToHomeFinished(); break; case RECENTS: mStateCallback.setState(STATE_SCALED_CONTROLLER_RECENTS | STATE_CAPTURE_SCREENSHOT diff --git a/quickstep/recents_ui_overrides/src/com/android/quickstep/views/RecentsView.java b/quickstep/recents_ui_overrides/src/com/android/quickstep/views/RecentsView.java index a673ab61b7..a7968ca055 100644 --- a/quickstep/recents_ui_overrides/src/com/android/quickstep/views/RecentsView.java +++ b/quickstep/recents_ui_overrides/src/com/android/quickstep/views/RecentsView.java @@ -17,6 +17,7 @@ package com.android.quickstep.views; import static androidx.dynamicanimation.animation.DynamicAnimation.MIN_VISIBLE_CHANGE_PIXELS; + import static com.android.launcher3.BaseActivity.STATE_HANDLER_INVISIBILITY_FLAGS; import static com.android.launcher3.InvariantDeviceProfile.CHANGE_FLAG_ICON_PARAMS; import static com.android.launcher3.LauncherAnimUtils.SCALE_PROPERTY; @@ -105,11 +106,13 @@ import com.android.quickstep.RecentsAnimationController; import com.android.quickstep.RecentsAnimationTargets; import com.android.quickstep.RecentsModel; import com.android.quickstep.RecentsModel.TaskVisualsChangeListener; +import com.android.quickstep.SystemUiProxy; import com.android.quickstep.TaskThumbnailCache; import com.android.quickstep.TaskUtils; import com.android.quickstep.ViewUtils; import com.android.quickstep.util.AppWindowAnimationHelper; import com.android.quickstep.util.LayoutUtils; +import com.android.systemui.shared.recents.IPinnedStackAnimationListener; import com.android.systemui.shared.recents.model.Task; import com.android.systemui.shared.recents.model.ThumbnailData; import com.android.systemui.shared.system.ActivityManagerWrapper; @@ -261,7 +264,10 @@ public abstract class RecentsView extends PagedView impl } }); } + }; + private final IPinnedStackAnimationListener mIPinnedStackAnimationListener = + new IPinnedStackAnimationListener.Stub() { @Override public void onPinnedStackAnimationStarted() { // Needed for activities that auto-enter PiP, which will not trigger a remote @@ -443,6 +449,8 @@ public abstract class RecentsView extends PagedView impl mSyncTransactionApplier = new SyncRtSurfaceTransactionApplierCompat(this); RecentsModel.INSTANCE.get(getContext()).addThumbnailChangeListener(this); mIdp.addOnChangeListener(this); + SystemUiProxy.INSTANCE.get(getContext()).setPinnedStackAnimationListener( + mIPinnedStackAnimationListener); } @Override @@ -455,6 +463,7 @@ public abstract class RecentsView extends PagedView impl mSyncTransactionApplier = null; RecentsModel.INSTANCE.get(getContext()).removeThumbnailChangeListener(this); mIdp.removeOnChangeListener(this); + SystemUiProxy.INSTANCE.get(getContext()).setPinnedStackAnimationListener(null); } @Override diff --git a/quickstep/src/com/android/quickstep/SystemUiProxy.java b/quickstep/src/com/android/quickstep/SystemUiProxy.java index 458d6a9553..eb60601c44 100644 --- a/quickstep/src/com/android/quickstep/SystemUiProxy.java +++ b/quickstep/src/com/android/quickstep/SystemUiProxy.java @@ -29,6 +29,7 @@ import android.util.Log; import android.view.MotionEvent; import com.android.launcher3.util.MainThreadInitializedObject; +import com.android.systemui.shared.recents.IPinnedStackAnimationListener; import com.android.systemui.shared.recents.ISystemUiProxy; /** @@ -268,9 +269,7 @@ public class SystemUiProxy implements ISystemUiProxy { } } - /** - * See SharedApiCompat#setShelfHeight() - */ + @Override public void setShelfHeight(boolean visible, int shelfHeight) { boolean changed = visible != mLastShelfVisible || shelfHeight != mLastShelfHeight; if (mSystemUiProxy != null && changed) { @@ -306,4 +305,32 @@ public class SystemUiProxy implements ISystemUiProxy { } } } + + /** + * Notifies that swipe-to-home action is finished. + */ + @Override + public void notifySwipeToHomeFinished() { + if (mSystemUiProxy != null) { + try { + mSystemUiProxy.notifySwipeToHomeFinished(); + } catch (RemoteException e) { + Log.w(TAG, "Failed call setPinnedStackAnimationType", e); + } + } + } + + /** + * Sets listener to get pinned stack animation callbacks. + */ + @Override + public void setPinnedStackAnimationListener(IPinnedStackAnimationListener listener) { + if (mSystemUiProxy != null) { + try { + mSystemUiProxy.setPinnedStackAnimationListener(listener); + } catch (RemoteException e) { + Log.w(TAG, "Failed call setPinnedStackAnimationListener", e); + } + } + } } -- GitLab From e1aff298891495a422d3595f90749a457e7c4ae6 Mon Sep 17 00:00:00 2001 From: Vadim Caen Date: Tue, 25 Feb 2020 12:48:21 +0100 Subject: [PATCH 0002/1664] Fixed portrait orientation if fixed_rotation flag Use SCREEN_ORIENTATION_PORTRAIT for the launcher if the fixed_rotation is enabled Test: manual, check that the launcher doesn't rotates when the flag is enabled and we are coming from a landscape activity Bug: 147469164 Bug: 146416687 Change-Id: I0fe0d16ec1fbfe096fbdfcf2c315e12fb3423cf8 --- src/com/android/launcher3/PagedView.java | 23 +++++++++++++++++-- src/com/android/launcher3/Utilities.java | 9 ++++++++ .../launcher3/states/RotationHelper.java | 6 ++++- 3 files changed, 35 insertions(+), 3 deletions(-) diff --git a/src/com/android/launcher3/PagedView.java b/src/com/android/launcher3/PagedView.java index 4c6e1f3ebc..66dba98e0f 100644 --- a/src/com/android/launcher3/PagedView.java +++ b/src/com/android/launcher3/PagedView.java @@ -20,12 +20,15 @@ import static com.android.launcher3.compat.AccessibilityManagerCompat.isAccessib 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 static com.android.launcher3.util.Executors.MAIN_EXECUTOR; import android.animation.LayoutTransition; import android.animation.TimeInterpolator; import android.annotation.SuppressLint; +import android.content.ContentResolver; import android.content.Context; import android.content.res.TypedArray; +import android.database.ContentObserver; import android.graphics.Canvas; import android.graphics.Rect; import android.os.Bundle; @@ -64,6 +67,9 @@ public abstract class PagedView extends ViewGrou private static final String TAG = "PagedView"; private static final boolean DEBUG = false; + public static boolean sFlagForcedRotation = false; + public static final String FIXED_ROTATION_TRANSFORM_SETTING_NAME = "fixed_rotation_transform"; + public static final int INVALID_PAGE = -1; protected static final ComputePageScrollsLogic SIMPLE_SCROLL_LOGIC = (v) -> v.getVisibility() != GONE; @@ -166,11 +172,12 @@ public abstract class PagedView extends ViewGrou * Initializes various states for this workspace. */ protected void init() { - mScroller = new OverScroller(getContext()); + Context context = getContext(); + mScroller = new OverScroller(context); setDefaultInterpolator(Interpolators.SCROLL); mCurrentPage = 0; - final ViewConfiguration configuration = ViewConfiguration.get(getContext()); + final ViewConfiguration configuration = ViewConfiguration.get(context); mTouchSlop = configuration.getScaledPagingTouchSlop(); mMaximumVelocity = configuration.getScaledMaximumFlingVelocity(); @@ -182,6 +189,18 @@ public abstract class PagedView extends ViewGrou if (Utilities.ATLEAST_OREO) { setDefaultFocusHighlightEnabled(false); } + + sFlagForcedRotation = Utilities.isFixedRotationTransformEnabled(context); + final ContentResolver resolver = context.getContentResolver(); + final ContentObserver observer = new ContentObserver(MAIN_EXECUTOR.getHandler()) { + @Override + public void onChange(boolean selfChange) { + sFlagForcedRotation = Utilities.isFixedRotationTransformEnabled(context); + } + }; + resolver.registerContentObserver(Settings.Global.getUriFor( + FIXED_ROTATION_TRANSFORM_SETTING_NAME), + false, observer); } protected void setDefaultInterpolator(Interpolator interpolator) { diff --git a/src/com/android/launcher3/Utilities.java b/src/com/android/launcher3/Utilities.java index e0e4cc0ce4..571f745080 100644 --- a/src/com/android/launcher3/Utilities.java +++ b/src/com/android/launcher3/Utilities.java @@ -17,6 +17,7 @@ package com.android.launcher3; import static com.android.launcher3.ItemInfoWithIcon.FLAG_ICON_BADGED; +import static com.android.launcher3.PagedView.FIXED_ROTATION_TRANSFORM_SETTING_NAME; import android.animation.ValueAnimator; import android.annotation.TargetApi; @@ -127,6 +128,14 @@ public final class Utilities { Settings.Global.DEVELOPMENT_SETTINGS_ENABLED, 0) != 0; } + /** + * Checks whether the fixed_rotation_flag is enabled. + */ + public static boolean isFixedRotationTransformEnabled(Context context) { + return Settings.Global.getInt(context.getContentResolver(), + FIXED_ROTATION_TRANSFORM_SETTING_NAME, 0) != 0; + } + // 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 String EXTRA_WALLPAPER_FLAVOR = "com.android.launcher3.WALLPAPER_FLAVOR"; diff --git a/src/com/android/launcher3/states/RotationHelper.java b/src/com/android/launcher3/states/RotationHelper.java index 852928b416..3a64f9aeb8 100644 --- a/src/com/android/launcher3/states/RotationHelper.java +++ b/src/com/android/launcher3/states/RotationHelper.java @@ -17,6 +17,7 @@ package com.android.launcher3.states; import static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_LOCKED; import static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_NOSENSOR; +import static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_PORTRAIT; import static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_UNSPECIFIED; import static android.util.DisplayMetrics.DENSITY_DEVICE_STABLE; @@ -26,6 +27,7 @@ import android.content.res.Resources; import android.view.WindowManager; import com.android.launcher3.Launcher; +import com.android.launcher3.PagedView; import com.android.launcher3.R; import com.android.launcher3.Utilities; import com.android.launcher3.config.FeatureFlags; @@ -182,7 +184,9 @@ public class RotationHelper implements OnSharedPreferenceChangeListener { } final int activityFlags; - if (mStateHandlerRequest != REQUEST_NONE) { + if (PagedView.sFlagForcedRotation) { + activityFlags = SCREEN_ORIENTATION_PORTRAIT; + } else if (mStateHandlerRequest != REQUEST_NONE) { activityFlags = mStateHandlerRequest == REQUEST_LOCK ? SCREEN_ORIENTATION_LOCKED : SCREEN_ORIENTATION_UNSPECIFIED; } else if (mCurrentTransitionRequest != REQUEST_NONE) { -- GitLab From 38d2e3086ce3a6636bd7647c170d51e04a56186a Mon Sep 17 00:00:00 2001 From: Tony Wickham Date: Thu, 5 Mar 2020 18:04:41 +0000 Subject: [PATCH 0003/1664] Revert "Fixed portrait orientation if fixed_rotation flag" This reverts commit e1aff298891495a422d3595f90749a457e7c4ae6. Reason for revert: An equivalent change was made in ub-launcher3-master with slight variations, causing conflicts when merging from there to platform master. Once this is reverted, conflicts should go away and the new solution should be good to go. Change-Id: I19ba4af5f1e5f089fb771ada8b20c28a4b09b68c --- src/com/android/launcher3/PagedView.java | 23 ++----------------- src/com/android/launcher3/Utilities.java | 9 -------- .../launcher3/states/RotationHelper.java | 6 +---- 3 files changed, 3 insertions(+), 35 deletions(-) diff --git a/src/com/android/launcher3/PagedView.java b/src/com/android/launcher3/PagedView.java index 66dba98e0f..4c6e1f3ebc 100644 --- a/src/com/android/launcher3/PagedView.java +++ b/src/com/android/launcher3/PagedView.java @@ -20,15 +20,12 @@ import static com.android.launcher3.compat.AccessibilityManagerCompat.isAccessib 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 static com.android.launcher3.util.Executors.MAIN_EXECUTOR; import android.animation.LayoutTransition; import android.animation.TimeInterpolator; import android.annotation.SuppressLint; -import android.content.ContentResolver; import android.content.Context; import android.content.res.TypedArray; -import android.database.ContentObserver; import android.graphics.Canvas; import android.graphics.Rect; import android.os.Bundle; @@ -67,9 +64,6 @@ public abstract class PagedView extends ViewGrou private static final String TAG = "PagedView"; private static final boolean DEBUG = false; - public static boolean sFlagForcedRotation = false; - public static final String FIXED_ROTATION_TRANSFORM_SETTING_NAME = "fixed_rotation_transform"; - public static final int INVALID_PAGE = -1; protected static final ComputePageScrollsLogic SIMPLE_SCROLL_LOGIC = (v) -> v.getVisibility() != GONE; @@ -172,12 +166,11 @@ public abstract class PagedView extends ViewGrou * Initializes various states for this workspace. */ protected void init() { - Context context = getContext(); - mScroller = new OverScroller(context); + mScroller = new OverScroller(getContext()); setDefaultInterpolator(Interpolators.SCROLL); mCurrentPage = 0; - final ViewConfiguration configuration = ViewConfiguration.get(context); + final ViewConfiguration configuration = ViewConfiguration.get(getContext()); mTouchSlop = configuration.getScaledPagingTouchSlop(); mMaximumVelocity = configuration.getScaledMaximumFlingVelocity(); @@ -189,18 +182,6 @@ public abstract class PagedView extends ViewGrou if (Utilities.ATLEAST_OREO) { setDefaultFocusHighlightEnabled(false); } - - sFlagForcedRotation = Utilities.isFixedRotationTransformEnabled(context); - final ContentResolver resolver = context.getContentResolver(); - final ContentObserver observer = new ContentObserver(MAIN_EXECUTOR.getHandler()) { - @Override - public void onChange(boolean selfChange) { - sFlagForcedRotation = Utilities.isFixedRotationTransformEnabled(context); - } - }; - resolver.registerContentObserver(Settings.Global.getUriFor( - FIXED_ROTATION_TRANSFORM_SETTING_NAME), - false, observer); } protected void setDefaultInterpolator(Interpolator interpolator) { diff --git a/src/com/android/launcher3/Utilities.java b/src/com/android/launcher3/Utilities.java index 571f745080..e0e4cc0ce4 100644 --- a/src/com/android/launcher3/Utilities.java +++ b/src/com/android/launcher3/Utilities.java @@ -17,7 +17,6 @@ package com.android.launcher3; import static com.android.launcher3.ItemInfoWithIcon.FLAG_ICON_BADGED; -import static com.android.launcher3.PagedView.FIXED_ROTATION_TRANSFORM_SETTING_NAME; import android.animation.ValueAnimator; import android.annotation.TargetApi; @@ -128,14 +127,6 @@ public final class Utilities { Settings.Global.DEVELOPMENT_SETTINGS_ENABLED, 0) != 0; } - /** - * Checks whether the fixed_rotation_flag is enabled. - */ - public static boolean isFixedRotationTransformEnabled(Context context) { - return Settings.Global.getInt(context.getContentResolver(), - FIXED_ROTATION_TRANSFORM_SETTING_NAME, 0) != 0; - } - // 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 String EXTRA_WALLPAPER_FLAVOR = "com.android.launcher3.WALLPAPER_FLAVOR"; diff --git a/src/com/android/launcher3/states/RotationHelper.java b/src/com/android/launcher3/states/RotationHelper.java index 3a64f9aeb8..852928b416 100644 --- a/src/com/android/launcher3/states/RotationHelper.java +++ b/src/com/android/launcher3/states/RotationHelper.java @@ -17,7 +17,6 @@ package com.android.launcher3.states; import static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_LOCKED; import static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_NOSENSOR; -import static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_PORTRAIT; import static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_UNSPECIFIED; import static android.util.DisplayMetrics.DENSITY_DEVICE_STABLE; @@ -27,7 +26,6 @@ import android.content.res.Resources; import android.view.WindowManager; import com.android.launcher3.Launcher; -import com.android.launcher3.PagedView; import com.android.launcher3.R; import com.android.launcher3.Utilities; import com.android.launcher3.config.FeatureFlags; @@ -184,9 +182,7 @@ public class RotationHelper implements OnSharedPreferenceChangeListener { } final int activityFlags; - if (PagedView.sFlagForcedRotation) { - activityFlags = SCREEN_ORIENTATION_PORTRAIT; - } else if (mStateHandlerRequest != REQUEST_NONE) { + if (mStateHandlerRequest != REQUEST_NONE) { activityFlags = mStateHandlerRequest == REQUEST_LOCK ? SCREEN_ORIENTATION_LOCKED : SCREEN_ORIENTATION_UNSPECIFIED; } else if (mCurrentTransitionRequest != REQUEST_NONE) { -- GitLab From 30c29bfc08c2429a382d9d34a7728f8f73e9b8fd Mon Sep 17 00:00:00 2001 From: Andy Wickham Date: Tue, 3 Mar 2020 01:15:27 +0000 Subject: [PATCH 0004/1664] Removes iconloaderlib from Launcher3. Bug: 138964382 Test: builds Change-Id: Ic60adfb2ebdcf1a72b440df26023b861fd6e62d5 --- iconloaderlib/Android.bp | 44 -- iconloaderlib/AndroidManifest.xml | 20 - iconloaderlib/build.gradle | 38 -- .../adaptive_icon_drawable_wrapper.xml | 22 - .../res/drawable/ic_instant_app_badge.xml | 39 -- iconloaderlib/res/values/colors.xml | 21 - iconloaderlib/res/values/config.xml | 27 - iconloaderlib/res/values/dimens.xml | 19 - .../launcher3/icons/BaseIconFactory.java | 396 ------------ .../android/launcher3/icons/BitmapInfo.java | 72 --- .../launcher3/icons/BitmapRenderer.java | 70 --- .../launcher3/icons/ColorExtractor.java | 127 ---- .../android/launcher3/icons/DotRenderer.java | 143 ----- .../launcher3/icons/FixedScaleDrawable.java | 53 -- .../launcher3/icons/GraphicsUtils.java | 85 --- .../launcher3/icons/IconNormalizer.java | 411 ------------- .../launcher3/icons/ShadowGenerator.java | 167 ----- .../launcher3/icons/cache/BaseIconCache.java | 582 ------------------ .../launcher3/icons/cache/CachingLogic.java | 65 -- .../icons/cache/HandlerRunnable.java | 67 -- .../icons/cache/IconCacheUpdateHandler.java | 313 ---------- .../android/launcher3/util/ComponentKey.java | 59 -- .../launcher3/util/NoLocaleSQLiteHelper.java | 58 -- .../launcher3/util/SQLiteCacheHelper.java | 125 ---- .../android/launcher3/icons/IconFactory.java | 89 --- .../launcher3/icons/SimpleIconCache.java | 114 ---- 26 files changed, 3226 deletions(-) delete mode 100644 iconloaderlib/Android.bp delete mode 100644 iconloaderlib/AndroidManifest.xml delete mode 100644 iconloaderlib/build.gradle delete mode 100644 iconloaderlib/res/drawable-v26/adaptive_icon_drawable_wrapper.xml delete mode 100644 iconloaderlib/res/drawable/ic_instant_app_badge.xml delete mode 100644 iconloaderlib/res/values/colors.xml delete mode 100644 iconloaderlib/res/values/config.xml delete mode 100644 iconloaderlib/res/values/dimens.xml delete mode 100644 iconloaderlib/src/com/android/launcher3/icons/BaseIconFactory.java delete mode 100644 iconloaderlib/src/com/android/launcher3/icons/BitmapInfo.java delete mode 100644 iconloaderlib/src/com/android/launcher3/icons/BitmapRenderer.java delete mode 100644 iconloaderlib/src/com/android/launcher3/icons/ColorExtractor.java delete mode 100644 iconloaderlib/src/com/android/launcher3/icons/DotRenderer.java delete mode 100644 iconloaderlib/src/com/android/launcher3/icons/FixedScaleDrawable.java delete mode 100644 iconloaderlib/src/com/android/launcher3/icons/GraphicsUtils.java delete mode 100644 iconloaderlib/src/com/android/launcher3/icons/IconNormalizer.java delete mode 100644 iconloaderlib/src/com/android/launcher3/icons/ShadowGenerator.java delete mode 100644 iconloaderlib/src/com/android/launcher3/icons/cache/BaseIconCache.java delete mode 100644 iconloaderlib/src/com/android/launcher3/icons/cache/CachingLogic.java delete mode 100644 iconloaderlib/src/com/android/launcher3/icons/cache/HandlerRunnable.java delete mode 100644 iconloaderlib/src/com/android/launcher3/icons/cache/IconCacheUpdateHandler.java delete mode 100644 iconloaderlib/src/com/android/launcher3/util/ComponentKey.java delete mode 100644 iconloaderlib/src/com/android/launcher3/util/NoLocaleSQLiteHelper.java delete mode 100644 iconloaderlib/src/com/android/launcher3/util/SQLiteCacheHelper.java delete mode 100644 iconloaderlib/src_full_lib/com/android/launcher3/icons/IconFactory.java delete mode 100644 iconloaderlib/src_full_lib/com/android/launcher3/icons/SimpleIconCache.java diff --git a/iconloaderlib/Android.bp b/iconloaderlib/Android.bp deleted file mode 100644 index f12d16e42a..0000000000 --- a/iconloaderlib/Android.bp +++ /dev/null @@ -1,44 +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. - -android_library { - name: "iconloader_base", - sdk_version: "28", - min_sdk_version: "21", - static_libs: [ - "androidx.core_core", - ], - resource_dirs: [ - "res", - ], - srcs: [ - "src/**/*.java", - ], -} - -android_library { - name: "iconloader", - sdk_version: "system_current", - min_sdk_version: "21", - static_libs: [ - "androidx.core_core", - ], - resource_dirs: [ - "res", - ], - srcs: [ - "src/**/*.java", - "src_full_lib/**/*.java", - ], -} diff --git a/iconloaderlib/AndroidManifest.xml b/iconloaderlib/AndroidManifest.xml deleted file mode 100644 index b30258da2a..0000000000 --- a/iconloaderlib/AndroidManifest.xml +++ /dev/null @@ -1,20 +0,0 @@ - - - - - diff --git a/iconloaderlib/build.gradle b/iconloaderlib/build.gradle deleted file mode 100644 index d7a62e1114..0000000000 --- a/iconloaderlib/build.gradle +++ /dev/null @@ -1,38 +0,0 @@ -apply plugin: 'com.android.library' - -android { - compileSdkVersion COMPILE_SDK - buildToolsVersion BUILD_TOOLS_VERSION - - defaultConfig { - minSdkVersion 25 - targetSdkVersion 28 - versionCode 1 - versionName "1.0" - } - - sourceSets { - main { - java.srcDirs = ['src', 'src_full_lib'] - manifest.srcFile 'AndroidManifest.xml' - res.srcDirs = ['res'] - } - } - - lintOptions { - abortOnError false - } - - tasks.withType(JavaCompile) { - options.compilerArgs << "-Xlint:unchecked" << "-Xlint:deprecation" - } - - compileOptions { - sourceCompatibility JavaVersion.VERSION_1_8 - targetCompatibility JavaVersion.VERSION_1_8 - } -} - -dependencies { - implementation "androidx.core:core:${ANDROID_X_VERSION}" -} diff --git a/iconloaderlib/res/drawable-v26/adaptive_icon_drawable_wrapper.xml b/iconloaderlib/res/drawable-v26/adaptive_icon_drawable_wrapper.xml deleted file mode 100644 index 9f13cf5719..0000000000 --- a/iconloaderlib/res/drawable-v26/adaptive_icon_drawable_wrapper.xml +++ /dev/null @@ -1,22 +0,0 @@ - - - - - - - - diff --git a/iconloaderlib/res/drawable/ic_instant_app_badge.xml b/iconloaderlib/res/drawable/ic_instant_app_badge.xml deleted file mode 100644 index b74317e5f2..0000000000 --- a/iconloaderlib/res/drawable/ic_instant_app_badge.xml +++ /dev/null @@ -1,39 +0,0 @@ - - - - - - - - - diff --git a/iconloaderlib/res/values/colors.xml b/iconloaderlib/res/values/colors.xml deleted file mode 100644 index 873b2fc5fb..0000000000 --- a/iconloaderlib/res/values/colors.xml +++ /dev/null @@ -1,21 +0,0 @@ - - - - #FFFFFF - diff --git a/iconloaderlib/res/values/config.xml b/iconloaderlib/res/values/config.xml deleted file mode 100644 index 68c2d2e3aa..0000000000 --- a/iconloaderlib/res/values/config.xml +++ /dev/null @@ -1,27 +0,0 @@ - - - - - - - 56dp - false - app_icons.db - - \ No newline at end of file diff --git a/iconloaderlib/res/values/dimens.xml b/iconloaderlib/res/values/dimens.xml deleted file mode 100644 index e8c0c44f72..0000000000 --- a/iconloaderlib/res/values/dimens.xml +++ /dev/null @@ -1,19 +0,0 @@ - - - - - 24dp - diff --git a/iconloaderlib/src/com/android/launcher3/icons/BaseIconFactory.java b/iconloaderlib/src/com/android/launcher3/icons/BaseIconFactory.java deleted file mode 100644 index 31a923e314..0000000000 --- a/iconloaderlib/src/com/android/launcher3/icons/BaseIconFactory.java +++ /dev/null @@ -1,396 +0,0 @@ -package com.android.launcher3.icons; - -import static android.graphics.Paint.DITHER_FLAG; -import static android.graphics.Paint.FILTER_BITMAP_FLAG; - -import static com.android.launcher3.icons.ShadowGenerator.BLUR_FACTOR; - -import android.content.Context; -import android.content.Intent; -import android.content.pm.PackageManager; -import android.content.res.Resources; -import android.graphics.Bitmap; -import android.graphics.Canvas; -import android.graphics.Color; -import android.graphics.PaintFlagsDrawFilter; -import android.graphics.Rect; -import android.graphics.RectF; -import android.graphics.drawable.AdaptiveIconDrawable; -import android.graphics.drawable.BitmapDrawable; -import android.graphics.drawable.ColorDrawable; -import android.graphics.drawable.Drawable; -import android.os.Build; -import android.os.Process; -import android.os.UserHandle; - -import androidx.annotation.NonNull; - -/** - * This class will be moved to androidx library. There shouldn't be any dependency outside - * this package. - */ -public class BaseIconFactory implements AutoCloseable { - - private static final String TAG = "BaseIconFactory"; - private static final int DEFAULT_WRAPPER_BACKGROUND = Color.WHITE; - static final boolean ATLEAST_OREO = Build.VERSION.SDK_INT >= Build.VERSION_CODES.O; - static final boolean ATLEAST_P = Build.VERSION.SDK_INT >= Build.VERSION_CODES.P; - - private static final float ICON_BADGE_SCALE = 0.444f; - - private final Rect mOldBounds = new Rect(); - protected final Context mContext; - private final Canvas mCanvas; - private final PackageManager mPm; - private final ColorExtractor mColorExtractor; - private boolean mDisableColorExtractor; - private boolean mBadgeOnLeft = false; - - protected final int mFillResIconDpi; - protected final int mIconBitmapSize; - - private IconNormalizer mNormalizer; - private ShadowGenerator mShadowGenerator; - private final boolean mShapeDetection; - - private Drawable mWrapperIcon; - private int mWrapperBackgroundColor = DEFAULT_WRAPPER_BACKGROUND; - - protected BaseIconFactory(Context context, int fillResIconDpi, int iconBitmapSize, - boolean shapeDetection) { - mContext = context.getApplicationContext(); - mShapeDetection = shapeDetection; - mFillResIconDpi = fillResIconDpi; - mIconBitmapSize = iconBitmapSize; - - mPm = mContext.getPackageManager(); - mColorExtractor = new ColorExtractor(); - - mCanvas = new Canvas(); - mCanvas.setDrawFilter(new PaintFlagsDrawFilter(DITHER_FLAG, FILTER_BITMAP_FLAG)); - clear(); - } - - protected BaseIconFactory(Context context, int fillResIconDpi, int iconBitmapSize) { - this(context, fillResIconDpi, iconBitmapSize, false); - } - - protected void clear() { - mWrapperBackgroundColor = DEFAULT_WRAPPER_BACKGROUND; - mDisableColorExtractor = false; - mBadgeOnLeft = false; - } - - public ShadowGenerator getShadowGenerator() { - if (mShadowGenerator == null) { - mShadowGenerator = new ShadowGenerator(mIconBitmapSize); - } - return mShadowGenerator; - } - - public IconNormalizer getNormalizer() { - if (mNormalizer == null) { - mNormalizer = new IconNormalizer(mContext, mIconBitmapSize, mShapeDetection); - } - return mNormalizer; - } - - @SuppressWarnings("deprecation") - public BitmapInfo createIconBitmap(Intent.ShortcutIconResource iconRes) { - try { - Resources resources = mPm.getResourcesForApplication(iconRes.packageName); - if (resources != null) { - final int id = resources.getIdentifier(iconRes.resourceName, null, null); - // do not stamp old legacy shortcuts as the app may have already forgotten about it - return createBadgedIconBitmap( - resources.getDrawableForDensity(id, mFillResIconDpi), - Process.myUserHandle() /* only available on primary user */, - false /* do not apply legacy treatment */); - } - } catch (Exception e) { - // Icon not found. - } - return null; - } - - public BitmapInfo createIconBitmap(Bitmap icon) { - if (mIconBitmapSize != icon.getWidth() || mIconBitmapSize != icon.getHeight()) { - icon = createIconBitmap(new BitmapDrawable(mContext.getResources(), icon), 1f); - } - - return BitmapInfo.of(icon, extractColor(icon)); - } - - public BitmapInfo createBadgedIconBitmap(Drawable icon, UserHandle user, - boolean shrinkNonAdaptiveIcons) { - return createBadgedIconBitmap(icon, user, shrinkNonAdaptiveIcons, false, null); - } - - public BitmapInfo createBadgedIconBitmap(Drawable icon, UserHandle user, - int iconAppTargetSdk) { - return createBadgedIconBitmap(icon, user, iconAppTargetSdk, false); - } - - public BitmapInfo createBadgedIconBitmap(Drawable icon, UserHandle user, - int iconAppTargetSdk, boolean isInstantApp) { - return createBadgedIconBitmap(icon, user, iconAppTargetSdk, isInstantApp, null); - } - - public BitmapInfo createBadgedIconBitmap(Drawable icon, UserHandle user, - int iconAppTargetSdk, boolean isInstantApp, float[] scale) { - boolean shrinkNonAdaptiveIcons = ATLEAST_P || - (ATLEAST_OREO && iconAppTargetSdk >= Build.VERSION_CODES.O); - return createBadgedIconBitmap(icon, user, shrinkNonAdaptiveIcons, isInstantApp, scale); - } - - public Bitmap createScaledBitmapWithoutShadow(Drawable icon, int iconAppTargetSdk) { - boolean shrinkNonAdaptiveIcons = ATLEAST_P || - (ATLEAST_OREO && iconAppTargetSdk >= Build.VERSION_CODES.O); - return createScaledBitmapWithoutShadow(icon, shrinkNonAdaptiveIcons); - } - - /** - * Creates bitmap using the source drawable and various parameters. - * The bitmap is visually normalized with other icons and has enough spacing to add shadow. - * - * @param icon source of the icon - * @param user info can be used for a badge - * @param shrinkNonAdaptiveIcons {@code true} if non adaptive icons should be treated - * @param isInstantApp info can be used for a badge - * @param scale returns the scale result from normalization - * @return a bitmap suitable for disaplaying as an icon at various system UIs. - */ - public BitmapInfo createBadgedIconBitmap(@NonNull Drawable icon, UserHandle user, - boolean shrinkNonAdaptiveIcons, boolean isInstantApp, float[] scale) { - if (scale == null) { - scale = new float[1]; - } - icon = normalizeAndWrapToAdaptiveIcon(icon, shrinkNonAdaptiveIcons, null, scale); - Bitmap bitmap = createIconBitmap(icon, scale[0]); - if (ATLEAST_OREO && icon instanceof AdaptiveIconDrawable) { - mCanvas.setBitmap(bitmap); - getShadowGenerator().recreateIcon(Bitmap.createBitmap(bitmap), mCanvas); - mCanvas.setBitmap(null); - } - - if (isInstantApp) { - badgeWithDrawable(bitmap, mContext.getDrawable(R.drawable.ic_instant_app_badge)); - } - if (user != null) { - BitmapDrawable drawable = new FixedSizeBitmapDrawable(bitmap); - Drawable badged = mPm.getUserBadgedIcon(drawable, user); - if (badged instanceof BitmapDrawable) { - bitmap = ((BitmapDrawable) badged).getBitmap(); - } else { - bitmap = createIconBitmap(badged, 1f); - } - } - int color = extractColor(bitmap); - return icon instanceof BitmapInfo.Extender - ? ((BitmapInfo.Extender) icon).getExtendedInfo(bitmap, color, this) - : BitmapInfo.of(bitmap, color); - } - - public Bitmap createScaledBitmapWithoutShadow(Drawable icon, boolean shrinkNonAdaptiveIcons) { - RectF iconBounds = new RectF(); - float[] scale = new float[1]; - icon = normalizeAndWrapToAdaptiveIcon(icon, shrinkNonAdaptiveIcons, iconBounds, scale); - return createIconBitmap(icon, - Math.min(scale[0], ShadowGenerator.getScaleForBounds(iconBounds))); - } - - /** - * Switches badging to left/right - */ - public void setBadgeOnLeft(boolean badgeOnLeft) { - mBadgeOnLeft = badgeOnLeft; - } - - /** - * Sets the background color used for wrapped adaptive icon - */ - public void setWrapperBackgroundColor(int color) { - mWrapperBackgroundColor = (Color.alpha(color) < 255) ? DEFAULT_WRAPPER_BACKGROUND : color; - } - - /** - * Disables the dominant color extraction for all icons loaded. - */ - public void disableColorExtraction() { - mDisableColorExtractor = true; - } - - private Drawable normalizeAndWrapToAdaptiveIcon(@NonNull Drawable icon, - boolean shrinkNonAdaptiveIcons, RectF outIconBounds, float[] outScale) { - if (icon == null) { - return null; - } - float scale = 1f; - - if (shrinkNonAdaptiveIcons && ATLEAST_OREO) { - if (mWrapperIcon == null) { - mWrapperIcon = mContext.getDrawable(R.drawable.adaptive_icon_drawable_wrapper) - .mutate(); - } - AdaptiveIconDrawable dr = (AdaptiveIconDrawable) mWrapperIcon; - dr.setBounds(0, 0, 1, 1); - boolean[] outShape = new boolean[1]; - scale = getNormalizer().getScale(icon, outIconBounds, dr.getIconMask(), outShape); - if (!(icon instanceof AdaptiveIconDrawable) && !outShape[0]) { - FixedScaleDrawable fsd = ((FixedScaleDrawable) dr.getForeground()); - fsd.setDrawable(icon); - fsd.setScale(scale); - icon = dr; - scale = getNormalizer().getScale(icon, outIconBounds, null, null); - - ((ColorDrawable) dr.getBackground()).setColor(mWrapperBackgroundColor); - } - } else { - scale = getNormalizer().getScale(icon, outIconBounds, null, null); - } - - outScale[0] = scale; - return icon; - } - - /** - * Adds the {@param badge} on top of {@param target} using the badge dimensions. - */ - public void badgeWithDrawable(Bitmap target, Drawable badge) { - mCanvas.setBitmap(target); - badgeWithDrawable(mCanvas, badge); - mCanvas.setBitmap(null); - } - - /** - * Adds the {@param badge} on top of {@param target} using the badge dimensions. - */ - public void badgeWithDrawable(Canvas target, Drawable badge) { - int badgeSize = getBadgeSizeForIconSize(mIconBitmapSize); - if (mBadgeOnLeft) { - badge.setBounds(0, mIconBitmapSize - badgeSize, badgeSize, mIconBitmapSize); - } else { - badge.setBounds(mIconBitmapSize - badgeSize, mIconBitmapSize - badgeSize, - mIconBitmapSize, mIconBitmapSize); - } - badge.draw(target); - } - - private Bitmap createIconBitmap(Drawable icon, float scale) { - return createIconBitmap(icon, scale, mIconBitmapSize); - } - - /** - * @param icon drawable that should be flattened to a bitmap - * @param scale the scale to apply before drawing {@param icon} on the canvas - */ - public Bitmap createIconBitmap(@NonNull Drawable icon, float scale, int size) { - Bitmap bitmap = Bitmap.createBitmap(size, size, Bitmap.Config.ARGB_8888); - if (icon == null) { - return bitmap; - } - mCanvas.setBitmap(bitmap); - mOldBounds.set(icon.getBounds()); - - if (ATLEAST_OREO && icon instanceof AdaptiveIconDrawable) { - int offset = Math.max((int) Math.ceil(BLUR_FACTOR * size), - Math.round(size * (1 - scale) / 2 )); - icon.setBounds(offset, offset, size - offset, size - offset); - icon.draw(mCanvas); - } else { - if (icon instanceof BitmapDrawable) { - BitmapDrawable bitmapDrawable = (BitmapDrawable) icon; - Bitmap b = bitmapDrawable.getBitmap(); - if (bitmap != null && b.getDensity() == Bitmap.DENSITY_NONE) { - bitmapDrawable.setTargetDensity(mContext.getResources().getDisplayMetrics()); - } - } - int width = size; - int height = size; - - int intrinsicWidth = icon.getIntrinsicWidth(); - int intrinsicHeight = icon.getIntrinsicHeight(); - if (intrinsicWidth > 0 && intrinsicHeight > 0) { - // Scale the icon proportionally to the icon dimensions - final float ratio = (float) intrinsicWidth / intrinsicHeight; - if (intrinsicWidth > intrinsicHeight) { - height = (int) (width / ratio); - } else if (intrinsicHeight > intrinsicWidth) { - width = (int) (height * ratio); - } - } - final int left = (size - width) / 2; - final int top = (size - height) / 2; - icon.setBounds(left, top, left + width, top + height); - mCanvas.save(); - mCanvas.scale(scale, scale, size / 2, size / 2); - icon.draw(mCanvas); - mCanvas.restore(); - - } - icon.setBounds(mOldBounds); - mCanvas.setBitmap(null); - return bitmap; - } - - @Override - public void close() { - clear(); - } - - public BitmapInfo makeDefaultIcon(UserHandle user) { - return createBadgedIconBitmap(getFullResDefaultActivityIcon(mFillResIconDpi), - user, Build.VERSION.SDK_INT); - } - - public static Drawable getFullResDefaultActivityIcon(int iconDpi) { - return Resources.getSystem().getDrawableForDensity( - Build.VERSION.SDK_INT >= Build.VERSION_CODES.O - ? android.R.drawable.sym_def_app_icon : android.R.mipmap.sym_def_app_icon, - iconDpi); - } - - /** - * Badges the provided source with the badge info - */ - public BitmapInfo badgeBitmap(Bitmap source, BitmapInfo badgeInfo) { - Bitmap icon = BitmapRenderer.createHardwareBitmap(mIconBitmapSize, mIconBitmapSize, (c) -> { - getShadowGenerator().recreateIcon(source, c); - badgeWithDrawable(c, new FixedSizeBitmapDrawable(badgeInfo.icon)); - }); - return BitmapInfo.of(icon, badgeInfo.color); - } - - private int extractColor(Bitmap bitmap) { - return mDisableColorExtractor ? 0 : mColorExtractor.findDominantColorByHue(bitmap); - } - - /** - * Returns the correct badge size given an icon size - */ - public static int getBadgeSizeForIconSize(int iconSize) { - return (int) (ICON_BADGE_SCALE * iconSize); - } - - /** - * An extension of {@link BitmapDrawable} which returns the bitmap pixel size as intrinsic size. - * This allows the badging to be done based on the action bitmap size rather than - * the scaled bitmap size. - */ - private static class FixedSizeBitmapDrawable extends BitmapDrawable { - - public FixedSizeBitmapDrawable(Bitmap bitmap) { - super(null, bitmap); - } - - @Override - public int getIntrinsicHeight() { - return getBitmap().getWidth(); - } - - @Override - public int getIntrinsicWidth() { - return getBitmap().getWidth(); - } - } -} diff --git a/iconloaderlib/src/com/android/launcher3/icons/BitmapInfo.java b/iconloaderlib/src/com/android/launcher3/icons/BitmapInfo.java deleted file mode 100644 index d33f9b1112..0000000000 --- a/iconloaderlib/src/com/android/launcher3/icons/BitmapInfo.java +++ /dev/null @@ -1,72 +0,0 @@ -/* - * Copyright (C) 2017 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.icons; - -import android.graphics.Bitmap; -import android.graphics.Bitmap.Config; - -import androidx.annotation.NonNull; - -public class BitmapInfo { - - public static final Bitmap LOW_RES_ICON = Bitmap.createBitmap(1, 1, Config.ALPHA_8); - public static final BitmapInfo LOW_RES_INFO = fromBitmap(LOW_RES_ICON); - - public final Bitmap icon; - public final int color; - - public BitmapInfo(Bitmap icon, int color) { - this.icon = icon; - this.color = color; - } - - /** - * Ideally icon should not be null, except in cases when generating hardware bitmap failed - */ - public final boolean isNullOrLowRes() { - return icon == null || icon == LOW_RES_ICON; - } - - public final boolean isLowRes() { - return LOW_RES_ICON == icon; - } - - public static BitmapInfo fromBitmap(@NonNull Bitmap bitmap) { - return of(bitmap, 0); - } - - public static BitmapInfo of(@NonNull Bitmap bitmap, int color) { - return new BitmapInfo(bitmap, color); - } - - /** - * Interface to be implemented by drawables to provide a custom BitmapInfo - */ - public interface Extender { - - /** - * Called for creating a custom BitmapInfo - */ - default BitmapInfo getExtendedInfo(Bitmap bitmap, int color, BaseIconFactory iconFactory) { - return BitmapInfo.of(bitmap, color); - } - - /** - * Notifies the drawable that it will be drawn directly in the UI, without any preprocessing - */ - default void prepareToDrawOnUi() { } - } -} diff --git a/iconloaderlib/src/com/android/launcher3/icons/BitmapRenderer.java b/iconloaderlib/src/com/android/launcher3/icons/BitmapRenderer.java deleted file mode 100644 index 5751ed95cb..0000000000 --- a/iconloaderlib/src/com/android/launcher3/icons/BitmapRenderer.java +++ /dev/null @@ -1,70 +0,0 @@ -/* - * Copyright (C) 2017 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.icons; - -import android.annotation.TargetApi; -import android.graphics.Bitmap; -import android.graphics.Bitmap.Config; -import android.graphics.Canvas; -import android.graphics.Picture; -import android.graphics.Rect; -import android.graphics.RectF; -import android.os.Build; -import android.os.Build.VERSION_CODES; - -/** - * Interface representing a bitmap draw operation. - */ -public interface BitmapRenderer { - - boolean USE_HARDWARE_BITMAP = Build.VERSION.SDK_INT >= Build.VERSION_CODES.P; - - static Bitmap createSoftwareBitmap(int width, int height, BitmapRenderer renderer) { - GraphicsUtils.noteNewBitmapCreated(); - Bitmap result = Bitmap.createBitmap(width, height, Bitmap.Config.ARGB_8888); - renderer.draw(new Canvas(result)); - return result; - } - - @TargetApi(Build.VERSION_CODES.P) - static Bitmap createHardwareBitmap(int width, int height, BitmapRenderer renderer) { - if (!USE_HARDWARE_BITMAP) { - return createSoftwareBitmap(width, height, renderer); - } - - GraphicsUtils.noteNewBitmapCreated(); - Picture picture = new Picture(); - renderer.draw(picture.beginRecording(width, height)); - picture.endRecording(); - return Bitmap.createBitmap(picture); - } - - /** - * Returns a bitmap from subset of the source bitmap. The new bitmap may be the - * same object as source, or a copy may have been made. - */ - static Bitmap createBitmap(Bitmap source, int x, int y, int width, int height) { - if (Build.VERSION.SDK_INT >= VERSION_CODES.O && source.getConfig() == Config.HARDWARE) { - return createHardwareBitmap(width, height, c -> c.drawBitmap(source, - new Rect(x, y, x + width, y + height), new RectF(0, 0, width, height), null)); - } else { - GraphicsUtils.noteNewBitmapCreated(); - return Bitmap.createBitmap(source, x, y, width, height); - } - } - - void draw(Canvas out); -} diff --git a/iconloaderlib/src/com/android/launcher3/icons/ColorExtractor.java b/iconloaderlib/src/com/android/launcher3/icons/ColorExtractor.java deleted file mode 100644 index 87bda825cc..0000000000 --- a/iconloaderlib/src/com/android/launcher3/icons/ColorExtractor.java +++ /dev/null @@ -1,127 +0,0 @@ -/* - * Copyright (C) 2017 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.icons; - -import android.graphics.Bitmap; -import android.graphics.Color; -import android.util.SparseArray; -import java.util.Arrays; - -/** - * Utility class for extracting colors from a bitmap. - */ -public class ColorExtractor { - - private final int NUM_SAMPLES = 20; - private final float[] mTmpHsv = new float[3]; - private final float[] mTmpHueScoreHistogram = new float[360]; - private final int[] mTmpPixels = new int[NUM_SAMPLES]; - private final SparseArray mTmpRgbScores = new SparseArray<>(); - - /** - * This picks a dominant color, looking for high-saturation, high-value, repeated hues. - * @param bitmap The bitmap to scan - */ - public int findDominantColorByHue(Bitmap bitmap) { - return findDominantColorByHue(bitmap, NUM_SAMPLES); - } - - /** - * This picks a dominant color, looking for high-saturation, high-value, repeated hues. - * @param bitmap The bitmap to scan - */ - public int findDominantColorByHue(Bitmap bitmap, int samples) { - final int height = bitmap.getHeight(); - final int width = bitmap.getWidth(); - int sampleStride = (int) Math.sqrt((height * width) / samples); - if (sampleStride < 1) { - sampleStride = 1; - } - - // This is an out-param, for getting the hsv values for an rgb - float[] hsv = mTmpHsv; - Arrays.fill(hsv, 0); - - // First get the best hue, by creating a histogram over 360 hue buckets, - // where each pixel contributes a score weighted by saturation, value, and alpha. - float[] hueScoreHistogram = mTmpHueScoreHistogram; - Arrays.fill(hueScoreHistogram, 0); - float highScore = -1; - int bestHue = -1; - - int[] pixels = mTmpPixels; - Arrays.fill(pixels, 0); - int pixelCount = 0; - - for (int y = 0; y < height; y += sampleStride) { - for (int x = 0; x < width; x += sampleStride) { - int argb = bitmap.getPixel(x, y); - int alpha = 0xFF & (argb >> 24); - if (alpha < 0x80) { - // Drop mostly-transparent pixels. - continue; - } - // Remove the alpha channel. - int rgb = argb | 0xFF000000; - Color.colorToHSV(rgb, hsv); - // Bucket colors by the 360 integer hues. - int hue = (int) hsv[0]; - if (hue < 0 || hue >= hueScoreHistogram.length) { - // Defensively avoid array bounds violations. - continue; - } - if (pixelCount < samples) { - pixels[pixelCount++] = rgb; - } - float score = hsv[1] * hsv[2]; - hueScoreHistogram[hue] += score; - if (hueScoreHistogram[hue] > highScore) { - highScore = hueScoreHistogram[hue]; - bestHue = hue; - } - } - } - - SparseArray rgbScores = mTmpRgbScores; - rgbScores.clear(); - int bestColor = 0xff000000; - highScore = -1; - // Go back over the RGB colors that match the winning hue, - // creating a histogram of weighted s*v scores, for up to 100*100 [s,v] buckets. - // The highest-scoring RGB color wins. - for (int i = 0; i < pixelCount; i++) { - int rgb = pixels[i]; - Color.colorToHSV(rgb, hsv); - int hue = (int) hsv[0]; - if (hue == bestHue) { - float s = hsv[1]; - float v = hsv[2]; - int bucket = (int) (s * 100) + (int) (v * 10000); - // Score by cumulative saturation * value. - float score = s * v; - Float oldTotal = rgbScores.get(bucket); - float newTotal = oldTotal == null ? score : oldTotal + score; - rgbScores.put(bucket, newTotal); - if (newTotal > highScore) { - highScore = newTotal; - // All the colors in the winning bucket are very similar. Last in wins. - bestColor = rgb; - } - } - } - return bestColor; - } -} diff --git a/iconloaderlib/src/com/android/launcher3/icons/DotRenderer.java b/iconloaderlib/src/com/android/launcher3/icons/DotRenderer.java deleted file mode 100644 index 97a0fd3ffc..0000000000 --- a/iconloaderlib/src/com/android/launcher3/icons/DotRenderer.java +++ /dev/null @@ -1,143 +0,0 @@ -/* - * Copyright (C) 2017 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.icons; - -import static android.graphics.Paint.ANTI_ALIAS_FLAG; -import static android.graphics.Paint.FILTER_BITMAP_FLAG; - -import android.graphics.Bitmap; -import android.graphics.Canvas; -import android.graphics.Color; -import android.graphics.Paint; -import android.graphics.Path; -import android.graphics.PathMeasure; -import android.graphics.Rect; -import android.graphics.RectF; -import android.util.Log; -import android.view.ViewDebug; - -/** - * Used to draw a notification dot on top of an icon. - */ -public class DotRenderer { - - private static final String TAG = "DotRenderer"; - - // The dot size is defined as a percentage of the app icon size. - private static final float SIZE_PERCENTAGE = 0.228f; - - private final float mCircleRadius; - private final Paint mCirclePaint = new Paint(ANTI_ALIAS_FLAG | FILTER_BITMAP_FLAG); - - private final Bitmap mBackgroundWithShadow; - private final float mBitmapOffset; - - // Stores the center x and y position as a percentage (0 to 1) of the icon size - private final float[] mRightDotPosition; - private final float[] mLeftDotPosition; - - public DotRenderer(int iconSizePx, Path iconShapePath, int pathSize) { - int size = Math.round(SIZE_PERCENTAGE * iconSizePx); - ShadowGenerator.Builder builder = new ShadowGenerator.Builder(Color.TRANSPARENT); - builder.ambientShadowAlpha = 88; - mBackgroundWithShadow = builder.setupBlurForSize(size).createPill(size, size); - mCircleRadius = builder.radius; - - mBitmapOffset = -mBackgroundWithShadow.getHeight() * 0.5f; // Same as width. - - // Find the points on the path that are closest to the top left and right corners. - mLeftDotPosition = getPathPoint(iconShapePath, pathSize, -1); - mRightDotPosition = getPathPoint(iconShapePath, pathSize, 1); - } - - private static float[] getPathPoint(Path path, float size, float direction) { - float halfSize = size / 2; - // Small delta so that we don't get a zero size triangle - float delta = 1; - - float x = halfSize + direction * halfSize; - Path trianglePath = new Path(); - trianglePath.moveTo(halfSize, halfSize); - trianglePath.lineTo(x + delta * direction, 0); - trianglePath.lineTo(x, -delta); - trianglePath.close(); - - trianglePath.op(path, Path.Op.INTERSECT); - float[] pos = new float[2]; - new PathMeasure(trianglePath, false).getPosTan(0, pos, null); - - pos[0] = pos[0] / size; - pos[1] = pos[1] / size; - return pos; - } - - public float[] getLeftDotPosition() { - return mLeftDotPosition; - } - - public float[] getRightDotPosition() { - return mRightDotPosition; - } - - /** - * Draw a circle on top of the canvas according to the given params. - */ - public void draw(Canvas canvas, DrawParams params) { - if (params == null) { - Log.e(TAG, "Invalid null argument(s) passed in call to draw."); - return; - } - canvas.save(); - - Rect iconBounds = params.iconBounds; - float[] dotPosition = params.leftAlign ? mLeftDotPosition : mRightDotPosition; - float dotCenterX = iconBounds.left + iconBounds.width() * dotPosition[0]; - float dotCenterY = iconBounds.top + iconBounds.height() * dotPosition[1]; - - // Ensure dot fits entirely in canvas clip bounds. - Rect canvasBounds = canvas.getClipBounds(); - float offsetX = params.leftAlign - ? Math.max(0, canvasBounds.left - (dotCenterX + mBitmapOffset)) - : Math.min(0, canvasBounds.right - (dotCenterX - mBitmapOffset)); - float offsetY = Math.max(0, canvasBounds.top - (dotCenterY + mBitmapOffset)); - - // We draw the dot relative to its center. - canvas.translate(dotCenterX + offsetX, dotCenterY + offsetY); - canvas.scale(params.scale, params.scale); - - mCirclePaint.setColor(Color.BLACK); - canvas.drawBitmap(mBackgroundWithShadow, mBitmapOffset, mBitmapOffset, mCirclePaint); - mCirclePaint.setColor(params.color); - canvas.drawCircle(0, 0, mCircleRadius, mCirclePaint); - canvas.restore(); - } - - public static class DrawParams { - /** The color (possibly based on the icon) to use for the dot. */ - @ViewDebug.ExportedProperty(category = "notification dot", formatToHexString = true) - public int color; - /** The bounds of the icon that the dot is drawn on top of. */ - @ViewDebug.ExportedProperty(category = "notification dot") - public Rect iconBounds = new Rect(); - /** The progress of the animation, from 0 to 1. */ - @ViewDebug.ExportedProperty(category = "notification dot") - public float scale; - /** Whether the dot should align to the top left of the icon rather than the top right. */ - @ViewDebug.ExportedProperty(category = "notification dot") - public boolean leftAlign; - } -} diff --git a/iconloaderlib/src/com/android/launcher3/icons/FixedScaleDrawable.java b/iconloaderlib/src/com/android/launcher3/icons/FixedScaleDrawable.java deleted file mode 100644 index 516965ec2b..0000000000 --- a/iconloaderlib/src/com/android/launcher3/icons/FixedScaleDrawable.java +++ /dev/null @@ -1,53 +0,0 @@ -package com.android.launcher3.icons; - -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.util.AttributeSet; - -import org.xmlpull.v1.XmlPullParser; - -/** - * Extension of {@link DrawableWrapper} which scales the child drawables by a fixed amount. - */ -public class FixedScaleDrawable extends DrawableWrapper { - - // TODO b/33553066 use the constant defined in MaskableIconDrawable - private static final float LEGACY_ICON_SCALE = .7f * .6667f; - private float mScaleX, mScaleY; - - public FixedScaleDrawable() { - super(new ColorDrawable()); - mScaleX = LEGACY_ICON_SCALE; - mScaleY = LEGACY_ICON_SCALE; - } - - @Override - public void draw(Canvas canvas) { - int saveCount = canvas.save(); - canvas.scale(mScaleX, mScaleY, - getBounds().exactCenterX(), getBounds().exactCenterY()); - super.draw(canvas); - canvas.restoreToCount(saveCount); - } - - @Override - public void inflate(Resources r, XmlPullParser parser, AttributeSet attrs) { } - - @Override - public void inflate(Resources r, XmlPullParser parser, AttributeSet attrs, Theme theme) { } - - public void setScale(float scale) { - float h = getIntrinsicHeight(); - float w = getIntrinsicWidth(); - mScaleX = scale * LEGACY_ICON_SCALE; - mScaleY = scale * LEGACY_ICON_SCALE; - if (h > w && w > 0) { - mScaleX *= w / h; - } else if (w > h && h > 0) { - mScaleY *= h / w; - } - } -} diff --git a/iconloaderlib/src/com/android/launcher3/icons/GraphicsUtils.java b/iconloaderlib/src/com/android/launcher3/icons/GraphicsUtils.java deleted file mode 100644 index 22f1f2357e..0000000000 --- a/iconloaderlib/src/com/android/launcher3/icons/GraphicsUtils.java +++ /dev/null @@ -1,85 +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.launcher3.icons; - -import android.graphics.Bitmap; -import android.graphics.Rect; -import android.graphics.Region; -import android.graphics.RegionIterator; -import android.util.Log; - -import androidx.annotation.ColorInt; - -import java.io.ByteArrayOutputStream; -import java.io.IOException; - -public class GraphicsUtils { - - private static final String TAG = "GraphicsUtils"; - - public static Runnable sOnNewBitmapRunnable = () -> { }; - - /** - * Set the alpha component of {@code color} to be {@code alpha}. Unlike the support lib version, - * it bounds the alpha in valid range instead of throwing an exception to allow for safer - * interpolation of color animations - */ - @ColorInt - public static int setColorAlphaBound(int color, int alpha) { - if (alpha < 0) { - alpha = 0; - } else if (alpha > 255) { - alpha = 255; - } - return (color & 0x00ffffff) | (alpha << 24); - } - - /** - * Compresses the bitmap to a byte array for serialization. - */ - public static byte[] flattenBitmap(Bitmap bitmap) { - // Try go guesstimate how much space the icon will take when serialized - // to avoid unnecessary allocations/copies during the write (4 bytes per pixel). - int size = bitmap.getWidth() * bitmap.getHeight() * 4; - ByteArrayOutputStream out = new ByteArrayOutputStream(size); - try { - bitmap.compress(Bitmap.CompressFormat.PNG, 100, out); - out.flush(); - out.close(); - return out.toByteArray(); - } catch (IOException e) { - Log.w(TAG, "Could not write bitmap"); - return null; - } - } - - public static int getArea(Region r) { - RegionIterator itr = new RegionIterator(r); - int area = 0; - Rect tempRect = new Rect(); - while (itr.next(tempRect)) { - area += tempRect.width() * tempRect.height(); - } - return area; - } - - /** - * Utility method to track new bitmap creation - */ - public static void noteNewBitmapCreated() { - sOnNewBitmapRunnable.run(); - } -} diff --git a/iconloaderlib/src/com/android/launcher3/icons/IconNormalizer.java b/iconloaderlib/src/com/android/launcher3/icons/IconNormalizer.java deleted file mode 100644 index de39e79fec..0000000000 --- a/iconloaderlib/src/com/android/launcher3/icons/IconNormalizer.java +++ /dev/null @@ -1,411 +0,0 @@ -/* - * Copyright (C) 2015 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.icons; - -import android.annotation.TargetApi; -import android.content.Context; -import android.content.res.Resources; -import android.graphics.Bitmap; -import android.graphics.Canvas; -import android.graphics.Color; -import android.graphics.Matrix; -import android.graphics.Paint; -import android.graphics.Path; -import android.graphics.PorterDuff; -import android.graphics.PorterDuffXfermode; -import android.graphics.Rect; -import android.graphics.RectF; -import android.graphics.Region; -import android.graphics.drawable.AdaptiveIconDrawable; -import android.graphics.drawable.Drawable; -import android.os.Build; -import android.util.Log; - -import java.nio.ByteBuffer; - -import androidx.annotation.NonNull; -import androidx.annotation.Nullable; - -public class IconNormalizer { - - private static final String TAG = "IconNormalizer"; - private static final boolean DEBUG = false; - // Ratio of icon visible area to full icon size for a square shaped icon - private static final float MAX_SQUARE_AREA_FACTOR = 375.0f / 576; - // Ratio of icon visible area to full icon size for a circular shaped icon - private static final float MAX_CIRCLE_AREA_FACTOR = 380.0f / 576; - - private static final float CIRCLE_AREA_BY_RECT = (float) Math.PI / 4; - - // Slope used to calculate icon visible area to full icon size for any generic shaped icon. - private static final float LINEAR_SCALE_SLOPE = - (MAX_CIRCLE_AREA_FACTOR - MAX_SQUARE_AREA_FACTOR) / (1 - CIRCLE_AREA_BY_RECT); - - private static final int MIN_VISIBLE_ALPHA = 40; - - // Shape detection related constants - private static final float BOUND_RATIO_MARGIN = .05f; - private static final float PIXEL_DIFF_PERCENTAGE_THRESHOLD = 0.005f; - private static final float SCALE_NOT_INITIALIZED = 0; - - // Ratio of the diameter of an normalized circular icon to the actual icon size. - public static final float ICON_VISIBLE_AREA_FACTOR = 0.92f; - - private final int mMaxSize; - private final Bitmap mBitmap; - private final Canvas mCanvas; - private final Paint mPaintMaskShape; - private final Paint mPaintMaskShapeOutline; - private final byte[] mPixels; - - private final RectF mAdaptiveIconBounds; - private float mAdaptiveIconScale; - - private boolean mEnableShapeDetection; - - // for each y, stores the position of the leftmost x and the rightmost x - private final float[] mLeftBorder; - private final float[] mRightBorder; - private final Rect mBounds; - private final Path mShapePath; - private final Matrix mMatrix; - - /** package private **/ - IconNormalizer(Context context, int iconBitmapSize, boolean shapeDetection) { - // Use twice the icon size as maximum size to avoid scaling down twice. - mMaxSize = iconBitmapSize * 2; - mBitmap = Bitmap.createBitmap(mMaxSize, mMaxSize, Bitmap.Config.ALPHA_8); - mCanvas = new Canvas(mBitmap); - mPixels = new byte[mMaxSize * mMaxSize]; - mLeftBorder = new float[mMaxSize]; - mRightBorder = new float[mMaxSize]; - mBounds = new Rect(); - mAdaptiveIconBounds = new RectF(); - - mPaintMaskShape = new Paint(); - mPaintMaskShape.setColor(Color.RED); - mPaintMaskShape.setStyle(Paint.Style.FILL); - mPaintMaskShape.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.XOR)); - - mPaintMaskShapeOutline = new Paint(); - mPaintMaskShapeOutline.setStrokeWidth( - 2 * context.getResources().getDisplayMetrics().density); - mPaintMaskShapeOutline.setStyle(Paint.Style.STROKE); - mPaintMaskShapeOutline.setColor(Color.BLACK); - mPaintMaskShapeOutline.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.CLEAR)); - - mShapePath = new Path(); - mMatrix = new Matrix(); - mAdaptiveIconScale = SCALE_NOT_INITIALIZED; - mEnableShapeDetection = shapeDetection; - } - - private static float getScale(float hullArea, float boundingArea, float fullArea) { - float hullByRect = hullArea / boundingArea; - float scaleRequired; - if (hullByRect < CIRCLE_AREA_BY_RECT) { - scaleRequired = MAX_CIRCLE_AREA_FACTOR; - } else { - scaleRequired = MAX_SQUARE_AREA_FACTOR + LINEAR_SCALE_SLOPE * (1 - hullByRect); - } - - float areaScale = hullArea / fullArea; - // Use sqrt of the final ratio as the images is scaled across both width and height. - return areaScale > scaleRequired ? (float) Math.sqrt(scaleRequired / areaScale) : 1; - } - - /** - * @param d Should be AdaptiveIconDrawable - * @param size Canvas size to use - */ - @TargetApi(Build.VERSION_CODES.O) - public static float normalizeAdaptiveIcon(Drawable d, int size, @Nullable RectF outBounds) { - Rect tmpBounds = new Rect(d.getBounds()); - d.setBounds(0, 0, size, size); - - Path path = ((AdaptiveIconDrawable) d).getIconMask(); - Region region = new Region(); - region.setPath(path, new Region(0, 0, size, size)); - - Rect hullBounds = region.getBounds(); - int hullArea = GraphicsUtils.getArea(region); - - if (outBounds != null) { - float sizeF = size; - outBounds.set( - hullBounds.left / sizeF, - hullBounds.top / sizeF, - 1 - (hullBounds.right / sizeF), - 1 - (hullBounds.bottom / sizeF)); - } - d.setBounds(tmpBounds); - return getScale(hullArea, hullArea, size * size); - } - - /** - * Returns if the shape of the icon is same as the path. - * For this method to work, the shape path bounds should be in [0,1]x[0,1] bounds. - */ - private boolean isShape(Path maskPath) { - // Condition1: - // If width and height of the path not close to a square, then the icon shape is - // not same as the mask shape. - float iconRatio = ((float) mBounds.width()) / mBounds.height(); - if (Math.abs(iconRatio - 1) > BOUND_RATIO_MARGIN) { - if (DEBUG) { - Log.d(TAG, "Not same as mask shape because width != height. " + iconRatio); - } - return false; - } - - // Condition 2: - // Actual icon (white) and the fitted shape (e.g., circle)(red) XOR operation - // should generate transparent image, if the actual icon is equivalent to the shape. - - // Fit the shape within the icon's bounding box - mMatrix.reset(); - mMatrix.setScale(mBounds.width(), mBounds.height()); - mMatrix.postTranslate(mBounds.left, mBounds.top); - maskPath.transform(mMatrix, mShapePath); - - // XOR operation - mCanvas.drawPath(mShapePath, mPaintMaskShape); - - // DST_OUT operation around the mask path outline - mCanvas.drawPath(mShapePath, mPaintMaskShapeOutline); - - // Check if the result is almost transparent - return isTransparentBitmap(); - } - - /** - * Used to determine if certain the bitmap is transparent. - */ - private boolean isTransparentBitmap() { - ByteBuffer buffer = ByteBuffer.wrap(mPixels); - buffer.rewind(); - mBitmap.copyPixelsToBuffer(buffer); - - int y = mBounds.top; - // buffer position - int index = y * mMaxSize; - // buffer shift after every row, width of buffer = mMaxSize - int rowSizeDiff = mMaxSize - mBounds.right; - - int sum = 0; - for (; y < mBounds.bottom; y++) { - index += mBounds.left; - for (int x = mBounds.left; x < mBounds.right; x++) { - if ((mPixels[index] & 0xFF) > MIN_VISIBLE_ALPHA) { - sum++; - } - index++; - } - index += rowSizeDiff; - } - - float percentageDiffPixels = ((float) sum) / (mBounds.width() * mBounds.height()); - return percentageDiffPixels < PIXEL_DIFF_PERCENTAGE_THRESHOLD; - } - - /** - * Returns the amount by which the {@param d} should be scaled (in both dimensions) so that it - * matches the design guidelines for a launcher icon. - * - * We first calculate the convex hull of the visible portion of the icon. - * This hull then compared with the bounding rectangle of the hull to find how closely it - * resembles a circle and a square, by comparing the ratio of the areas. Note that this is not an - * ideal solution but it gives satisfactory result without affecting the performance. - * - * This closeness is used to determine the ratio of hull area to the full icon size. - * Refer {@link #MAX_CIRCLE_AREA_FACTOR} and {@link #MAX_SQUARE_AREA_FACTOR} - * - * @param outBounds optional rect to receive the fraction distance from each edge. - */ - public synchronized float getScale(@NonNull Drawable d, @Nullable RectF outBounds, - @Nullable Path path, @Nullable boolean[] outMaskShape) { - if (BaseIconFactory.ATLEAST_OREO && d instanceof AdaptiveIconDrawable) { - if (mAdaptiveIconScale == SCALE_NOT_INITIALIZED) { - mAdaptiveIconScale = normalizeAdaptiveIcon(d, mMaxSize, mAdaptiveIconBounds); - } - if (outBounds != null) { - outBounds.set(mAdaptiveIconBounds); - } - return mAdaptiveIconScale; - } - int width = d.getIntrinsicWidth(); - int height = d.getIntrinsicHeight(); - if (width <= 0 || height <= 0) { - width = width <= 0 || width > mMaxSize ? mMaxSize : width; - height = height <= 0 || height > mMaxSize ? mMaxSize : height; - } else if (width > mMaxSize || height > mMaxSize) { - int max = Math.max(width, height); - width = mMaxSize * width / max; - height = mMaxSize * height / max; - } - - mBitmap.eraseColor(Color.TRANSPARENT); - d.setBounds(0, 0, width, height); - d.draw(mCanvas); - - ByteBuffer buffer = ByteBuffer.wrap(mPixels); - buffer.rewind(); - mBitmap.copyPixelsToBuffer(buffer); - - // Overall bounds of the visible icon. - int topY = -1; - int bottomY = -1; - int leftX = mMaxSize + 1; - int rightX = -1; - - // Create border by going through all pixels one row at a time and for each row find - // the first and the last non-transparent pixel. Set those values to mLeftBorder and - // mRightBorder and use -1 if there are no visible pixel in the row. - - // buffer position - int index = 0; - // buffer shift after every row, width of buffer = mMaxSize - int rowSizeDiff = mMaxSize - width; - // first and last position for any row. - int firstX, lastX; - - for (int y = 0; y < height; y++) { - firstX = lastX = -1; - for (int x = 0; x < width; x++) { - if ((mPixels[index] & 0xFF) > MIN_VISIBLE_ALPHA) { - if (firstX == -1) { - firstX = x; - } - lastX = x; - } - index++; - } - index += rowSizeDiff; - - mLeftBorder[y] = firstX; - mRightBorder[y] = lastX; - - // If there is at least one visible pixel, update the overall bounds. - if (firstX != -1) { - bottomY = y; - if (topY == -1) { - topY = y; - } - - leftX = Math.min(leftX, firstX); - rightX = Math.max(rightX, lastX); - } - } - - if (topY == -1 || rightX == -1) { - // No valid pixels found. Do not scale. - return 1; - } - - convertToConvexArray(mLeftBorder, 1, topY, bottomY); - convertToConvexArray(mRightBorder, -1, topY, bottomY); - - // Area of the convex hull - float area = 0; - for (int y = 0; y < height; y++) { - if (mLeftBorder[y] <= -1) { - continue; - } - area += mRightBorder[y] - mLeftBorder[y] + 1; - } - - mBounds.left = leftX; - mBounds.right = rightX; - - mBounds.top = topY; - mBounds.bottom = bottomY; - - if (outBounds != null) { - outBounds.set(((float) mBounds.left) / width, ((float) mBounds.top) / height, - 1 - ((float) mBounds.right) / width, - 1 - ((float) mBounds.bottom) / height); - } - if (outMaskShape != null && mEnableShapeDetection && outMaskShape.length > 0) { - outMaskShape[0] = isShape(path); - } - // Area of the rectangle required to fit the convex hull - float rectArea = (bottomY + 1 - topY) * (rightX + 1 - leftX); - return getScale(area, rectArea, width * height); - } - - /** - * Modifies {@param xCoordinates} to represent a convex border. Fills in all missing values - * (except on either ends) with appropriate values. - * @param xCoordinates map of x coordinate per y. - * @param direction 1 for left border and -1 for right border. - * @param topY the first Y position (inclusive) with a valid value. - * @param bottomY the last Y position (inclusive) with a valid value. - */ - private static void convertToConvexArray( - float[] xCoordinates, int direction, int topY, int bottomY) { - int total = xCoordinates.length; - // The tangent at each pixel. - float[] angles = new float[total - 1]; - - int first = topY; // First valid y coordinate - int last = -1; // Last valid y coordinate which didn't have a missing value - - float lastAngle = Float.MAX_VALUE; - - for (int i = topY + 1; i <= bottomY; i++) { - if (xCoordinates[i] <= -1) { - continue; - } - int start; - - if (lastAngle == Float.MAX_VALUE) { - start = first; - } else { - float currentAngle = (xCoordinates[i] - xCoordinates[last]) / (i - last); - start = last; - // If this position creates a concave angle, keep moving up until we find a - // position which creates a convex angle. - if ((currentAngle - lastAngle) * direction < 0) { - while (start > first) { - start --; - currentAngle = (xCoordinates[i] - xCoordinates[start]) / (i - start); - if ((currentAngle - angles[start]) * direction >= 0) { - break; - } - } - } - } - - // Reset from last check - lastAngle = (xCoordinates[i] - xCoordinates[start]) / (i - start); - // Update all the points from start. - for (int j = start; j < i; j++) { - angles[j] = lastAngle; - xCoordinates[j] = xCoordinates[start] + lastAngle * (j - start); - } - last = i; - } - } - - /** - * @return The diameter of the normalized circle that fits inside of the square (size x size). - */ - public static int getNormalizedCircleSize(int size) { - float area = size * size * MAX_CIRCLE_AREA_FACTOR; - return (int) Math.round(Math.sqrt((4 * area) / Math.PI)); - } -} diff --git a/iconloaderlib/src/com/android/launcher3/icons/ShadowGenerator.java b/iconloaderlib/src/com/android/launcher3/icons/ShadowGenerator.java deleted file mode 100644 index 770272728d..0000000000 --- a/iconloaderlib/src/com/android/launcher3/icons/ShadowGenerator.java +++ /dev/null @@ -1,167 +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.icons; - -import static com.android.launcher3.icons.GraphicsUtils.setColorAlphaBound; - -import android.graphics.Bitmap; -import android.graphics.BlurMaskFilter; -import android.graphics.BlurMaskFilter.Blur; -import android.graphics.Canvas; -import android.graphics.Color; -import android.graphics.Paint; -import android.graphics.PorterDuff; -import android.graphics.PorterDuffXfermode; -import android.graphics.RectF; - -/** - * Utility class to add shadows to bitmaps. - */ -public class ShadowGenerator { - public static final float BLUR_FACTOR = 0.5f/48; - - // Percent of actual icon size - public static final float KEY_SHADOW_DISTANCE = 1f/48; - private static final int KEY_SHADOW_ALPHA = 61; - // Percent of actual icon size - private static final float HALF_DISTANCE = 0.5f; - private static final int AMBIENT_SHADOW_ALPHA = 30; - - private final int mIconSize; - - private final Paint mBlurPaint; - private final Paint mDrawPaint; - private final BlurMaskFilter mDefaultBlurMaskFilter; - - public ShadowGenerator(int iconSize) { - mIconSize = iconSize; - mBlurPaint = new Paint(Paint.ANTI_ALIAS_FLAG | Paint.FILTER_BITMAP_FLAG); - mDrawPaint = new Paint(Paint.ANTI_ALIAS_FLAG | Paint.FILTER_BITMAP_FLAG); - mDefaultBlurMaskFilter = new BlurMaskFilter(mIconSize * BLUR_FACTOR, Blur.NORMAL); - } - - public synchronized void recreateIcon(Bitmap icon, Canvas out) { - recreateIcon(icon, mDefaultBlurMaskFilter, AMBIENT_SHADOW_ALPHA, KEY_SHADOW_ALPHA, out); - } - - public synchronized void recreateIcon(Bitmap icon, BlurMaskFilter blurMaskFilter, - int ambientAlpha, int keyAlpha, Canvas out) { - int[] offset = new int[2]; - mBlurPaint.setMaskFilter(blurMaskFilter); - Bitmap shadow = icon.extractAlpha(mBlurPaint, offset); - - // Draw ambient shadow - mDrawPaint.setAlpha(ambientAlpha); - out.drawBitmap(shadow, offset[0], offset[1], mDrawPaint); - - // Draw key shadow - mDrawPaint.setAlpha(keyAlpha); - out.drawBitmap(shadow, offset[0], offset[1] + KEY_SHADOW_DISTANCE * mIconSize, mDrawPaint); - - // Draw the icon - mDrawPaint.setAlpha(255); - out.drawBitmap(icon, 0, 0, mDrawPaint); - } - - /** - * Returns the minimum amount by which an icon with {@param bounds} should be scaled - * so that the shadows do not get clipped. - */ - public static float getScaleForBounds(RectF bounds) { - float scale = 1; - - // For top, left & right, we need same space. - float minSide = Math.min(Math.min(bounds.left, bounds.right), bounds.top); - if (minSide < BLUR_FACTOR) { - scale = (HALF_DISTANCE - BLUR_FACTOR) / (HALF_DISTANCE - minSide); - } - - float bottomSpace = BLUR_FACTOR + KEY_SHADOW_DISTANCE; - if (bounds.bottom < bottomSpace) { - scale = Math.min(scale, (HALF_DISTANCE - bottomSpace) / (HALF_DISTANCE - bounds.bottom)); - } - return scale; - } - - public static class Builder { - - public final RectF bounds = new RectF(); - public final int color; - - public int ambientShadowAlpha = AMBIENT_SHADOW_ALPHA; - - public float shadowBlur; - - public float keyShadowDistance; - public int keyShadowAlpha = KEY_SHADOW_ALPHA; - public float radius; - - public Builder(int color) { - this.color = color; - } - - public Builder setupBlurForSize(int height) { - shadowBlur = height * 1f / 24; - keyShadowDistance = height * 1f / 16; - return this; - } - - public Bitmap createPill(int width, int height) { - return createPill(width, height, height / 2f); - } - - public Bitmap createPill(int width, int height, float r) { - radius = r; - - int centerX = Math.round(width / 2f + shadowBlur); - int centerY = Math.round(radius + shadowBlur + keyShadowDistance); - int center = Math.max(centerX, centerY); - bounds.set(0, 0, width, height); - bounds.offsetTo(center - width / 2f, center - height / 2f); - - int size = center * 2; - return BitmapRenderer.createHardwareBitmap(size, size, this::drawShadow); - } - - public void drawShadow(Canvas c) { - Paint p = new Paint(Paint.ANTI_ALIAS_FLAG | Paint.FILTER_BITMAP_FLAG); - p.setColor(color); - - // Key shadow - p.setShadowLayer(shadowBlur, 0, keyShadowDistance, - setColorAlphaBound(Color.BLACK, keyShadowAlpha)); - c.drawRoundRect(bounds, radius, radius, p); - - // Ambient shadow - p.setShadowLayer(shadowBlur, 0, 0, - setColorAlphaBound(Color.BLACK, ambientShadowAlpha)); - c.drawRoundRect(bounds, radius, radius, p); - - if (Color.alpha(color) < 255) { - // Clear any content inside the pill-rect for translucent fill. - p.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.CLEAR)); - p.clearShadowLayer(); - p.setColor(Color.BLACK); - c.drawRoundRect(bounds, radius, radius, p); - - p.setXfermode(null); - p.setColor(color); - c.drawRoundRect(bounds, radius, radius, p); - } - } - } -} diff --git a/iconloaderlib/src/com/android/launcher3/icons/cache/BaseIconCache.java b/iconloaderlib/src/com/android/launcher3/icons/cache/BaseIconCache.java deleted file mode 100644 index 4c634cbc26..0000000000 --- a/iconloaderlib/src/com/android/launcher3/icons/cache/BaseIconCache.java +++ /dev/null @@ -1,582 +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.launcher3.icons.cache; - -import static com.android.launcher3.icons.BaseIconFactory.getFullResDefaultActivityIcon; -import static com.android.launcher3.icons.BitmapInfo.LOW_RES_ICON; -import static com.android.launcher3.icons.GraphicsUtils.setColorAlphaBound; - -import android.content.ComponentName; -import android.content.ContentValues; -import android.content.Context; -import android.content.pm.ActivityInfo; -import android.content.pm.ApplicationInfo; -import android.content.pm.PackageInfo; -import android.content.pm.PackageManager; -import android.content.pm.PackageManager.NameNotFoundException; -import android.content.res.Resources; -import android.database.Cursor; -import android.database.sqlite.SQLiteDatabase; -import android.database.sqlite.SQLiteException; -import android.graphics.Bitmap; -import android.graphics.BitmapFactory; -import android.graphics.drawable.Drawable; -import android.os.Build; -import android.os.Handler; -import android.os.LocaleList; -import android.os.Looper; -import android.os.Process; -import android.os.UserHandle; -import android.text.TextUtils; -import android.util.Log; - -import androidx.annotation.NonNull; -import androidx.annotation.Nullable; -import androidx.annotation.VisibleForTesting; - -import com.android.launcher3.icons.BaseIconFactory; -import com.android.launcher3.icons.BitmapInfo; -import com.android.launcher3.icons.BitmapRenderer; -import com.android.launcher3.icons.GraphicsUtils; -import com.android.launcher3.util.ComponentKey; -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.Map; -import java.util.Set; -import java.util.function.Supplier; - -public abstract class BaseIconCache { - - private static final String TAG = "BaseIconCache"; - private static final boolean DEBUG = false; - - private static final int INITIAL_ICON_CACHE_CAPACITY = 50; - - // Empty class name is used for storing package default entry. - public static final String EMPTY_CLASS_NAME = "."; - - public static class CacheEntry { - - @NonNull - public BitmapInfo bitmap = BitmapInfo.LOW_RES_INFO; - public CharSequence title = ""; - public CharSequence contentDescription = ""; - } - - private final HashMap mDefaultIcons = new HashMap<>(); - - protected final Context mContext; - protected final PackageManager mPackageManager; - - private final Map mCache; - protected final Handler mWorkerHandler; - - protected int mIconDpi; - protected IconDB mIconDb; - protected LocaleList mLocaleList = LocaleList.getEmptyLocaleList(); - protected String mSystemState = ""; - - private final String mDbFileName; - private final BitmapFactory.Options mDecodeOptions; - private final Looper mBgLooper; - - public BaseIconCache(Context context, String dbFileName, Looper bgLooper, - int iconDpi, int iconPixelSize, boolean inMemoryCache) { - mContext = context; - mDbFileName = dbFileName; - mPackageManager = context.getPackageManager(); - mBgLooper = bgLooper; - mWorkerHandler = new Handler(mBgLooper); - - if (inMemoryCache) { - mCache = new HashMap<>(INITIAL_ICON_CACHE_CAPACITY); - } else { - // Use a dummy cache - mCache = new AbstractMap() { - @Override - public Set> entrySet() { - return Collections.emptySet(); - } - - @Override - public CacheEntry put(ComponentKey key, CacheEntry value) { - return value; - } - }; - } - - if (BitmapRenderer.USE_HARDWARE_BITMAP && Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) { - mDecodeOptions = new BitmapFactory.Options(); - mDecodeOptions.inPreferredConfig = Bitmap.Config.HARDWARE; - } else { - mDecodeOptions = null; - } - - updateSystemState(); - mIconDpi = iconDpi; - mIconDb = new IconDB(context, dbFileName, iconPixelSize); - } - - /** - * Returns the persistable serial number for {@param user}. Subclass should implement proper - * caching strategy to avoid making binder call every time. - */ - protected abstract long getSerialNumberForUser(UserHandle user); - - /** - * Return true if the given app is an instant app and should be badged appropriately. - */ - protected abstract boolean isInstantApp(ApplicationInfo info); - - /** - * Opens and returns an icon factory. The factory is recycled by the caller. - */ - protected abstract BaseIconFactory getIconFactory(); - - public void updateIconParams(int iconDpi, int iconPixelSize) { - mWorkerHandler.post(() -> updateIconParamsBg(iconDpi, iconPixelSize)); - } - - private synchronized void updateIconParamsBg(int iconDpi, int iconPixelSize) { - mIconDpi = iconDpi; - mDefaultIcons.clear(); - mIconDb.clear(); - mIconDb.close(); - mIconDb = new IconDB(mContext, mDbFileName, iconPixelSize); - mCache.clear(); - } - - private Drawable getFullResIcon(Resources resources, int iconId) { - if (resources != null && iconId != 0) { - try { - return resources.getDrawableForDensity(iconId, mIconDpi); - } catch (Resources.NotFoundException e) { } - } - return getFullResDefaultActivityIcon(mIconDpi); - } - - public Drawable getFullResIcon(String packageName, int iconId) { - try { - return getFullResIcon(mPackageManager.getResourcesForApplication(packageName), iconId); - } catch (PackageManager.NameNotFoundException e) { } - return getFullResDefaultActivityIcon(mIconDpi); - } - - public Drawable getFullResIcon(ActivityInfo info) { - try { - return getFullResIcon(mPackageManager.getResourcesForApplication(info.applicationInfo), - info.getIconResource()); - } catch (PackageManager.NameNotFoundException e) { } - return getFullResDefaultActivityIcon(mIconDpi); - } - - private BitmapInfo makeDefaultIcon(UserHandle user) { - try (BaseIconFactory li = getIconFactory()) { - return li.makeDefaultIcon(user); - } - } - - /** - * Remove any records for the supplied ComponentName. - */ - public synchronized void remove(ComponentName componentName, UserHandle user) { - mCache.remove(new ComponentKey(componentName, user)); - } - - /** - * Remove any records for the supplied package name from memory. - */ - private void removeFromMemCacheLocked(String packageName, UserHandle user) { - HashSet forDeletion = new HashSet<>(); - for (ComponentKey key: mCache.keySet()) { - if (key.componentName.getPackageName().equals(packageName) - && key.user.equals(user)) { - forDeletion.add(key); - } - } - for (ComponentKey condemned: forDeletion) { - mCache.remove(condemned); - } - } - - /** - * Removes the entries related to the given package in memory and persistent DB. - */ - public synchronized void removeIconsForPkg(String packageName, UserHandle user) { - removeFromMemCacheLocked(packageName, user); - long userSerial = getSerialNumberForUser(user); - mIconDb.delete( - IconDB.COLUMN_COMPONENT + " LIKE ? AND " + IconDB.COLUMN_USER + " = ?", - new String[]{packageName + "/%", Long.toString(userSerial)}); - } - - public IconCacheUpdateHandler getUpdateHandler() { - updateSystemState(); - return new IconCacheUpdateHandler(this); - } - - /** - * Refreshes the system state definition used to check the validity of the cache. It - * incorporates all the properties that can affect the cache like the list of enabled locale - * and system-version. - */ - private void updateSystemState() { - mLocaleList = mContext.getResources().getConfiguration().getLocales(); - mSystemState = mLocaleList.toLanguageTags() + "," + Build.VERSION.SDK_INT; - } - - protected String getIconSystemState(String packageName) { - return mSystemState; - } - - /** - * Adds an entry into the DB and the in-memory cache. - * @param replaceExisting if true, it will recreate the bitmap even if it already exists in - * the memory. This is useful then the previous bitmap was created using - * old data. - */ - @VisibleForTesting - public synchronized void addIconToDBAndMemCache(T object, CachingLogic cachingLogic, - PackageInfo info, long userSerial, boolean replaceExisting) { - UserHandle user = cachingLogic.getUser(object); - ComponentName componentName = cachingLogic.getComponent(object); - - final ComponentKey key = new ComponentKey(componentName, user); - CacheEntry entry = null; - if (!replaceExisting) { - entry = mCache.get(key); - // We can't reuse the entry if the high-res icon is not present. - if (entry == null || entry.bitmap.isNullOrLowRes()) { - entry = null; - } - } - if (entry == null) { - entry = new CacheEntry(); - entry.bitmap = cachingLogic.loadIcon(mContext, object); - } - // Icon can't be loaded from cachingLogic, which implies alternative icon was loaded - // (e.g. fallback icon, default icon). So we drop here since there's no point in caching - // an empty entry. - if (entry.bitmap.isNullOrLowRes()) return; - entry.title = cachingLogic.getLabel(object); - entry.contentDescription = mPackageManager.getUserBadgedLabel(entry.title, user); - if (cachingLogic.addToMemCache()) mCache.put(key, entry); - - ContentValues values = newContentValues(entry.bitmap, entry.title.toString(), - componentName.getPackageName(), cachingLogic.getKeywords(object, mLocaleList)); - addIconToDB(values, componentName, info, userSerial, - cachingLogic.getLastUpdatedTime(object, info)); - } - - /** - * Updates {@param values} to contain versioning information and adds it to the DB. - * @param values {@link ContentValues} containing icon & title - */ - private void addIconToDB(ContentValues values, ComponentName key, - PackageInfo info, long userSerial, long lastUpdateTime) { - values.put(IconDB.COLUMN_COMPONENT, key.flattenToString()); - values.put(IconDB.COLUMN_USER, userSerial); - values.put(IconDB.COLUMN_LAST_UPDATED, lastUpdateTime); - values.put(IconDB.COLUMN_VERSION, info.versionCode); - mIconDb.insertOrReplace(values); - } - - public synchronized BitmapInfo getDefaultIcon(UserHandle user) { - if (!mDefaultIcons.containsKey(user)) { - mDefaultIcons.put(user, makeDefaultIcon(user)); - } - return mDefaultIcons.get(user); - } - - public boolean isDefaultIcon(BitmapInfo icon, UserHandle user) { - return getDefaultIcon(user).icon == icon.icon; - } - - /** - * Retrieves the entry from the cache. If the entry is not present, it creates a new entry. - * This method is not thread safe, it must be called from a synchronized method. - */ - protected CacheEntry cacheLocked( - @NonNull ComponentName componentName, @NonNull UserHandle user, - @NonNull Supplier infoProvider, @NonNull CachingLogic cachingLogic, - boolean usePackageIcon, boolean useLowResIcon) { - assertWorkerThread(); - ComponentKey cacheKey = new ComponentKey(componentName, user); - CacheEntry entry = mCache.get(cacheKey); - if (entry == null || (entry.bitmap.isLowRes() && !useLowResIcon)) { - entry = new CacheEntry(); - if (cachingLogic.addToMemCache()) { - mCache.put(cacheKey, entry); - } - - // Check the DB first. - T object = null; - boolean providerFetchedOnce = false; - - if (!getEntryFromDB(cacheKey, entry, useLowResIcon)) { - object = infoProvider.get(); - providerFetchedOnce = true; - - if (object != null) { - entry.bitmap = cachingLogic.loadIcon(mContext, object); - } else { - if (usePackageIcon) { - CacheEntry packageEntry = getEntryForPackageLocked( - componentName.getPackageName(), user, false); - if (packageEntry != null) { - if (DEBUG) Log.d(TAG, "using package default icon for " + - componentName.toShortString()); - entry.bitmap = packageEntry.bitmap; - entry.title = packageEntry.title; - entry.contentDescription = packageEntry.contentDescription; - } - } - if (entry.bitmap == null) { - if (DEBUG) Log.d(TAG, "using default icon for " + - componentName.toShortString()); - entry.bitmap = getDefaultIcon(user); - } - } - } - - if (TextUtils.isEmpty(entry.title)) { - if (object == null && !providerFetchedOnce) { - object = infoProvider.get(); - providerFetchedOnce = true; - } - if (object != null) { - entry.title = cachingLogic.getLabel(object); - entry.contentDescription = mPackageManager.getUserBadgedLabel( - cachingLogic.getDescription(object, entry.title), user); - } - } - } - return entry; - } - - public synchronized void clear() { - assertWorkerThread(); - mIconDb.clear(); - } - - /** - * Adds a default package entry in the cache. This entry is not persisted and will be removed - * when the cache is flushed. - */ - protected synchronized void cachePackageInstallInfo(String packageName, UserHandle user, - Bitmap icon, CharSequence title) { - removeFromMemCacheLocked(packageName, user); - - ComponentKey cacheKey = getPackageKey(packageName, user); - CacheEntry entry = mCache.get(cacheKey); - - // For icon caching, do not go through DB. Just update the in-memory entry. - if (entry == null) { - entry = new CacheEntry(); - } - if (!TextUtils.isEmpty(title)) { - entry.title = title; - } - if (icon != null) { - BaseIconFactory li = getIconFactory(); - entry.bitmap = li.createIconBitmap(icon); - li.close(); - } - if (!TextUtils.isEmpty(title) && entry.bitmap.icon != null) { - mCache.put(cacheKey, entry); - } - } - - private static ComponentKey getPackageKey(String packageName, UserHandle user) { - ComponentName cn = new ComponentName(packageName, packageName + EMPTY_CLASS_NAME); - return new ComponentKey(cn, user); - } - - /** - * Gets an entry for the package, which can be used as a fallback entry for various components. - * This method is not thread safe, it must be called from a synchronized method. - */ - protected CacheEntry getEntryForPackageLocked(String packageName, UserHandle user, - boolean useLowResIcon) { - assertWorkerThread(); - ComponentKey cacheKey = getPackageKey(packageName, user); - CacheEntry entry = mCache.get(cacheKey); - - if (entry == null || (entry.bitmap.isLowRes() && !useLowResIcon)) { - entry = new CacheEntry(); - boolean entryUpdated = true; - - // Check the DB first. - if (!getEntryFromDB(cacheKey, entry, useLowResIcon)) { - try { - int flags = Process.myUserHandle().equals(user) ? 0 : - PackageManager.GET_UNINSTALLED_PACKAGES; - PackageInfo info = mPackageManager.getPackageInfo(packageName, flags); - ApplicationInfo appInfo = info.applicationInfo; - if (appInfo == null) { - throw new NameNotFoundException("ApplicationInfo is null"); - } - - BaseIconFactory li = getIconFactory(); - // Load the full res icon for the application, but if useLowResIcon is set, then - // only keep the low resolution icon instead of the larger full-sized icon - BitmapInfo iconInfo = li.createBadgedIconBitmap( - appInfo.loadIcon(mPackageManager), user, appInfo.targetSdkVersion, - isInstantApp(appInfo)); - li.close(); - - entry.title = appInfo.loadLabel(mPackageManager); - entry.contentDescription = mPackageManager.getUserBadgedLabel(entry.title, user); - entry.bitmap = BitmapInfo.of( - useLowResIcon ? LOW_RES_ICON : iconInfo.icon, iconInfo.color); - - // Add the icon in the DB here, since these do not get written during - // package updates. - ContentValues values = newContentValues( - iconInfo, entry.title.toString(), packageName, null); - addIconToDB(values, cacheKey.componentName, info, getSerialNumberForUser(user), - info.lastUpdateTime); - - } catch (NameNotFoundException e) { - if (DEBUG) Log.d(TAG, "Application not installed " + packageName); - entryUpdated = false; - } - } - - // Only add a filled-out entry to the cache - if (entryUpdated) { - mCache.put(cacheKey, entry); - } - } - return entry; - } - - protected boolean getEntryFromDB(ComponentKey cacheKey, CacheEntry entry, boolean lowRes) { - Cursor c = null; - try { - c = mIconDb.query( - lowRes ? IconDB.COLUMNS_LOW_RES : IconDB.COLUMNS_HIGH_RES, - IconDB.COLUMN_COMPONENT + " = ? AND " + IconDB.COLUMN_USER + " = ?", - new String[]{ - cacheKey.componentName.flattenToString(), - Long.toString(getSerialNumberForUser(cacheKey.user))}); - if (c.moveToNext()) { - // Set the alpha to be 255, so that we never have a wrong color - entry.bitmap = BitmapInfo.of(LOW_RES_ICON, setColorAlphaBound(c.getInt(0), 255)); - entry.title = c.getString(1); - if (entry.title == null) { - entry.title = ""; - entry.contentDescription = ""; - } else { - entry.contentDescription = mPackageManager.getUserBadgedLabel( - entry.title, cacheKey.user); - } - - if (!lowRes) { - byte[] data = c.getBlob(2); - try { - entry.bitmap = BitmapInfo.of( - BitmapFactory.decodeByteArray(data, 0, data.length, mDecodeOptions), - entry.bitmap.color); - } catch (Exception e) { } - } - return true; - } - } catch (SQLiteException e) { - Log.d(TAG, "Error reading icon cache", e); - } finally { - if (c != null) { - c.close(); - } - } - return false; - } - - /** - * Returns a cursor for an arbitrary query to the cache db - */ - public synchronized Cursor queryCacheDb(String[] columns, String selection, - String[] selectionArgs) { - return mIconDb.query(columns, selection, selectionArgs); - } - - /** - * Cache class to store the actual entries on disk - */ - public static final class IconDB extends SQLiteCacheHelper { - private static final int RELEASE_VERSION = 27; - - public static final String TABLE_NAME = "icons"; - public static final String COLUMN_ROWID = "rowid"; - public static final String COLUMN_COMPONENT = "componentName"; - public static final String COLUMN_USER = "profileId"; - public static final String COLUMN_LAST_UPDATED = "lastUpdated"; - public static final String COLUMN_VERSION = "version"; - public static final String COLUMN_ICON = "icon"; - public static final String COLUMN_ICON_COLOR = "icon_color"; - public static final String COLUMN_LABEL = "label"; - public static final String COLUMN_SYSTEM_STATE = "system_state"; - public static final String COLUMN_KEYWORDS = "keywords"; - - public static final String[] COLUMNS_HIGH_RES = new String[] { - IconDB.COLUMN_ICON_COLOR, IconDB.COLUMN_LABEL, IconDB.COLUMN_ICON }; - public static final String[] COLUMNS_LOW_RES = new String[] { - IconDB.COLUMN_ICON_COLOR, IconDB.COLUMN_LABEL }; - - public IconDB(Context context, String dbFileName, int iconPixelSize) { - super(context, dbFileName, (RELEASE_VERSION << 16) + iconPixelSize, TABLE_NAME); - } - - @Override - protected void onCreateTable(SQLiteDatabase db) { - db.execSQL("CREATE TABLE IF NOT EXISTS " + TABLE_NAME + " (" - + COLUMN_COMPONENT + " TEXT NOT NULL, " - + COLUMN_USER + " INTEGER NOT NULL, " - + COLUMN_LAST_UPDATED + " INTEGER NOT NULL DEFAULT 0, " - + COLUMN_VERSION + " INTEGER NOT NULL DEFAULT 0, " - + COLUMN_ICON + " BLOB, " - + COLUMN_ICON_COLOR + " INTEGER NOT NULL DEFAULT 0, " - + COLUMN_LABEL + " TEXT, " - + COLUMN_SYSTEM_STATE + " TEXT, " - + COLUMN_KEYWORDS + " TEXT, " - + "PRIMARY KEY (" + COLUMN_COMPONENT + ", " + COLUMN_USER + ") " - + ");"); - } - } - - private ContentValues newContentValues(BitmapInfo bitmapInfo, String label, - String packageName, @Nullable String keywords) { - ContentValues values = new ContentValues(); - values.put(IconDB.COLUMN_ICON, - bitmapInfo.isLowRes() ? null : GraphicsUtils.flattenBitmap(bitmapInfo.icon)); - values.put(IconDB.COLUMN_ICON_COLOR, bitmapInfo.color); - - values.put(IconDB.COLUMN_LABEL, label); - values.put(IconDB.COLUMN_SYSTEM_STATE, getIconSystemState(packageName)); - values.put(IconDB.COLUMN_KEYWORDS, keywords); - return values; - } - - private void assertWorkerThread() { - if (Looper.myLooper() != mBgLooper) { - throw new IllegalStateException("Cache accessed on wrong thread " + Looper.myLooper()); - } - } -} diff --git a/iconloaderlib/src/com/android/launcher3/icons/cache/CachingLogic.java b/iconloaderlib/src/com/android/launcher3/icons/cache/CachingLogic.java deleted file mode 100644 index c12e9dcc15..0000000000 --- a/iconloaderlib/src/com/android/launcher3/icons/cache/CachingLogic.java +++ /dev/null @@ -1,65 +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.launcher3.icons.cache; - -import android.content.ComponentName; -import android.content.Context; -import android.content.pm.PackageInfo; -import android.os.LocaleList; -import android.os.UserHandle; - -import androidx.annotation.NonNull; -import androidx.annotation.Nullable; - -import com.android.launcher3.icons.BitmapInfo; - -public interface CachingLogic { - - ComponentName getComponent(T object); - - UserHandle getUser(T object); - - CharSequence getLabel(T object); - - default CharSequence getDescription(T object, CharSequence fallback) { - return fallback; - } - - @NonNull - BitmapInfo loadIcon(Context context, T object); - - /** - * Provides a option list of keywords to associate with this object - */ - @Nullable - default String getKeywords(T object, LocaleList localeList) { - return null; - } - - /** - * Returns the timestamp the entry was last updated in cache. - */ - default long getLastUpdatedTime(T object, PackageInfo info) { - return info.lastUpdateTime; - } - - /** - * Returns true the object should be added to mem cache; otherwise returns false. - */ - default boolean addToMemCache() { - return true; - } -} diff --git a/iconloaderlib/src/com/android/launcher3/icons/cache/HandlerRunnable.java b/iconloaderlib/src/com/android/launcher3/icons/cache/HandlerRunnable.java deleted file mode 100644 index ee52934543..0000000000 --- a/iconloaderlib/src/com/android/launcher3/icons/cache/HandlerRunnable.java +++ /dev/null @@ -1,67 +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.launcher3.icons.cache; - -import android.os.Handler; - -/** - * A runnable that can be posted to a {@link Handler} which can be canceled. - */ -public abstract class HandlerRunnable implements Runnable { - - private final Handler mHandler; - private final Runnable mEndRunnable; - - private boolean mEnded = false; - private boolean mCanceled = false; - - public HandlerRunnable(Handler handler, Runnable endRunnable) { - mHandler = handler; - mEndRunnable = endRunnable; - } - - /** - * Cancels this runnable from being run, only if it has not already run. - */ - public void cancel() { - mHandler.removeCallbacks(this); - // TODO: This can actually cause onEnd to be called twice if the handler is already running - // this runnable - // NOTE: This is currently run on whichever thread the caller is run on. - mCanceled = true; - onEnd(); - } - - /** - * @return whether this runnable was canceled. - */ - protected boolean isCanceled() { - return mCanceled; - } - - /** - * To be called by the implemention of this runnable. The end callback is done on whichever - * thread the caller is calling from. - */ - public void onEnd() { - if (!mEnded) { - mEnded = true; - if (mEndRunnable != null) { - mEndRunnable.run(); - } - } - } -} diff --git a/iconloaderlib/src/com/android/launcher3/icons/cache/IconCacheUpdateHandler.java b/iconloaderlib/src/com/android/launcher3/icons/cache/IconCacheUpdateHandler.java deleted file mode 100644 index 9e1ad7b7b2..0000000000 --- a/iconloaderlib/src/com/android/launcher3/icons/cache/IconCacheUpdateHandler.java +++ /dev/null @@ -1,313 +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.launcher3.icons.cache; - -import android.content.ComponentName; -import android.content.pm.ApplicationInfo; -import android.content.pm.PackageInfo; -import android.content.pm.PackageManager; -import android.database.Cursor; -import android.database.sqlite.SQLiteException; -import android.os.SystemClock; -import android.os.UserHandle; -import android.text.TextUtils; -import android.util.ArrayMap; -import android.util.Log; -import android.util.SparseBooleanArray; - -import com.android.launcher3.icons.cache.BaseIconCache.IconDB; - -import java.util.Collections; -import java.util.HashMap; -import java.util.HashSet; -import java.util.List; -import java.util.Map.Entry; -import java.util.Set; -import java.util.Stack; - -/** - * Utility class to handle updating the Icon cache - */ -public class IconCacheUpdateHandler { - - private static final String TAG = "IconCacheUpdateHandler"; - - /** - * In this mode, all invalid icons are marked as to-be-deleted in {@link #mItemsToDelete}. - * This mode is used for the first run. - */ - private static final boolean MODE_SET_INVALID_ITEMS = true; - - /** - * In this mode, any valid icon is removed from {@link #mItemsToDelete}. This is used for all - * subsequent runs, which essentially acts as set-union of all valid items. - */ - private static final boolean MODE_CLEAR_VALID_ITEMS = false; - - private static final Object ICON_UPDATE_TOKEN = new Object(); - - private final HashMap mPkgInfoMap; - private final BaseIconCache mIconCache; - - private final ArrayMap> mPackagesToIgnore = new ArrayMap<>(); - - private final SparseBooleanArray mItemsToDelete = new SparseBooleanArray(); - private boolean mFilterMode = MODE_SET_INVALID_ITEMS; - - IconCacheUpdateHandler(BaseIconCache cache) { - mIconCache = cache; - - mPkgInfoMap = new HashMap<>(); - - // Remove all active icon update tasks. - mIconCache.mWorkerHandler.removeCallbacksAndMessages(ICON_UPDATE_TOKEN); - - createPackageInfoMap(); - } - - /** - * Sets a package to ignore for processing - */ - public void addPackagesToIgnore(UserHandle userHandle, String packageName) { - Set packages = mPackagesToIgnore.get(userHandle); - if (packages == null) { - packages = new HashSet<>(); - mPackagesToIgnore.put(userHandle, packages); - } - packages.add(packageName); - } - - private void createPackageInfoMap() { - PackageManager pm = mIconCache.mPackageManager; - for (PackageInfo info : - pm.getInstalledPackages(PackageManager.MATCH_UNINSTALLED_PACKAGES)) { - mPkgInfoMap.put(info.packageName, info); - } - } - - /** - * Updates the persistent DB, such that only entries corresponding to {@param apps} remain in - * the DB and are updated. - * @return The set of packages for which icons have updated. - */ - public void updateIcons(List apps, CachingLogic cachingLogic, - OnUpdateCallback onUpdateCallback) { - // Filter the list per user - HashMap> userComponentMap = new HashMap<>(); - int count = apps.size(); - for (int i = 0; i < count; i++) { - T app = apps.get(i); - UserHandle userHandle = cachingLogic.getUser(app); - HashMap componentMap = userComponentMap.get(userHandle); - if (componentMap == null) { - componentMap = new HashMap<>(); - userComponentMap.put(userHandle, componentMap); - } - componentMap.put(cachingLogic.getComponent(app), app); - } - - for (Entry> entry : userComponentMap.entrySet()) { - updateIconsPerUser(entry.getKey(), entry.getValue(), cachingLogic, onUpdateCallback); - } - - // From now on, clear every valid item from the global valid map. - mFilterMode = MODE_CLEAR_VALID_ITEMS; - } - - /** - * Updates the persistent DB, such that only entries corresponding to {@param apps} remain in - * the DB and are updated. - * @return The set of packages for which icons have updated. - */ - @SuppressWarnings("unchecked") - private void updateIconsPerUser(UserHandle user, HashMap componentMap, - CachingLogic cachingLogic, OnUpdateCallback onUpdateCallback) { - Set ignorePackages = mPackagesToIgnore.get(user); - if (ignorePackages == null) { - ignorePackages = Collections.emptySet(); - } - long userSerial = mIconCache.getSerialNumberForUser(user); - - Stack appsToUpdate = new Stack<>(); - - try (Cursor c = mIconCache.mIconDb.query( - new String[]{IconDB.COLUMN_ROWID, IconDB.COLUMN_COMPONENT, - IconDB.COLUMN_LAST_UPDATED, IconDB.COLUMN_VERSION, - IconDB.COLUMN_SYSTEM_STATE}, - IconDB.COLUMN_USER + " = ? ", - new String[]{Long.toString(userSerial)})) { - - final int indexComponent = c.getColumnIndex(IconDB.COLUMN_COMPONENT); - final int indexLastUpdate = c.getColumnIndex(IconDB.COLUMN_LAST_UPDATED); - final int indexVersion = c.getColumnIndex(IconDB.COLUMN_VERSION); - final int rowIndex = c.getColumnIndex(IconDB.COLUMN_ROWID); - final int systemStateIndex = c.getColumnIndex(IconDB.COLUMN_SYSTEM_STATE); - - while (c.moveToNext()) { - String cn = c.getString(indexComponent); - ComponentName component = ComponentName.unflattenFromString(cn); - PackageInfo info = mPkgInfoMap.get(component.getPackageName()); - - int rowId = c.getInt(rowIndex); - if (info == null) { - if (!ignorePackages.contains(component.getPackageName())) { - - if (mFilterMode == MODE_SET_INVALID_ITEMS) { - mIconCache.remove(component, user); - mItemsToDelete.put(rowId, true); - } - } - continue; - } - if ((info.applicationInfo.flags & ApplicationInfo.FLAG_IS_DATA_ONLY) != 0) { - // Application is not present - continue; - } - - long updateTime = c.getLong(indexLastUpdate); - int version = c.getInt(indexVersion); - T app = componentMap.remove(component); - if (version == info.versionCode && updateTime == info.lastUpdateTime - && TextUtils.equals(c.getString(systemStateIndex), - mIconCache.getIconSystemState(info.packageName))) { - - if (mFilterMode == MODE_CLEAR_VALID_ITEMS) { - mItemsToDelete.put(rowId, false); - } - continue; - } - - if (app == null) { - if (mFilterMode == MODE_SET_INVALID_ITEMS) { - mIconCache.remove(component, user); - mItemsToDelete.put(rowId, true); - } - } else { - appsToUpdate.add(app); - } - } - } catch (SQLiteException e) { - Log.d(TAG, "Error reading icon cache", e); - // Continue updating whatever we have read so far - } - - // Insert remaining apps. - if (!componentMap.isEmpty() || !appsToUpdate.isEmpty()) { - Stack appsToAdd = new Stack<>(); - appsToAdd.addAll(componentMap.values()); - new SerializedIconUpdateTask(userSerial, user, appsToAdd, appsToUpdate, cachingLogic, - onUpdateCallback).scheduleNext(); - } - } - - /** - * Commits all updates as part of the update handler to disk. Not more calls should be made - * to this class after this. - */ - public void finish() { - // Commit all deletes - int deleteCount = 0; - StringBuilder queryBuilder = new StringBuilder() - .append(IconDB.COLUMN_ROWID) - .append(" IN ("); - - int count = mItemsToDelete.size(); - for (int i = 0; i < count; i++) { - if (mItemsToDelete.valueAt(i)) { - if (deleteCount > 0) { - queryBuilder.append(", "); - } - queryBuilder.append(mItemsToDelete.keyAt(i)); - deleteCount++; - } - } - queryBuilder.append(')'); - - if (deleteCount > 0) { - mIconCache.mIconDb.delete(queryBuilder.toString(), null); - } - } - - /** - * A runnable that updates invalid icons and adds missing icons in the DB for the provided - * LauncherActivityInfo list. Items are updated/added one at a time, so that the - * worker thread doesn't get blocked. - */ - private class SerializedIconUpdateTask implements Runnable { - private final long mUserSerial; - private final UserHandle mUserHandle; - private final Stack mAppsToAdd; - private final Stack mAppsToUpdate; - private final CachingLogic mCachingLogic; - private final HashSet mUpdatedPackages = new HashSet<>(); - private final OnUpdateCallback mOnUpdateCallback; - - SerializedIconUpdateTask(long userSerial, UserHandle userHandle, - Stack appsToAdd, Stack appsToUpdate, CachingLogic cachingLogic, - OnUpdateCallback onUpdateCallback) { - mUserHandle = userHandle; - mUserSerial = userSerial; - mAppsToAdd = appsToAdd; - mAppsToUpdate = appsToUpdate; - mCachingLogic = cachingLogic; - mOnUpdateCallback = onUpdateCallback; - } - - @Override - public void run() { - if (!mAppsToUpdate.isEmpty()) { - T app = mAppsToUpdate.pop(); - String pkg = mCachingLogic.getComponent(app).getPackageName(); - PackageInfo info = mPkgInfoMap.get(pkg); - - mIconCache.addIconToDBAndMemCache( - app, mCachingLogic, info, mUserSerial, true /*replace existing*/); - mUpdatedPackages.add(pkg); - - if (mAppsToUpdate.isEmpty() && !mUpdatedPackages.isEmpty()) { - // No more app to update. Notify callback. - mOnUpdateCallback.onPackageIconsUpdated(mUpdatedPackages, mUserHandle); - } - - // Let it run one more time. - scheduleNext(); - } else if (!mAppsToAdd.isEmpty()) { - T app = mAppsToAdd.pop(); - PackageInfo info = mPkgInfoMap.get(mCachingLogic.getComponent(app).getPackageName()); - // We do not check the mPkgInfoMap when generating the mAppsToAdd. Although every - // app should have package info, this is not guaranteed by the api - if (info != null) { - mIconCache.addIconToDBAndMemCache(app, mCachingLogic, info, - mUserSerial, false /*replace existing*/); - } - - if (!mAppsToAdd.isEmpty()) { - scheduleNext(); - } - } - } - - public void scheduleNext() { - mIconCache.mWorkerHandler.postAtTime(this, ICON_UPDATE_TOKEN, - SystemClock.uptimeMillis() + 1); - } - } - - public interface OnUpdateCallback { - - void onPackageIconsUpdated(HashSet updatedPackages, UserHandle user); - } -} diff --git a/iconloaderlib/src/com/android/launcher3/util/ComponentKey.java b/iconloaderlib/src/com/android/launcher3/util/ComponentKey.java deleted file mode 100644 index 34bed94270..0000000000 --- a/iconloaderlib/src/com/android/launcher3/util/ComponentKey.java +++ /dev/null @@ -1,59 +0,0 @@ -package com.android.launcher3.util; - -/** - * Copyright (C) 2015 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. - */ - -import android.content.ComponentName; -import android.os.UserHandle; - -import java.util.Arrays; - -public class ComponentKey { - - public final ComponentName componentName; - public final UserHandle user; - - private final int mHashCode; - - public ComponentKey(ComponentName componentName, UserHandle user) { - if (componentName == null || user == null) { - throw new NullPointerException(); - } - this.componentName = componentName; - this.user = user; - mHashCode = Arrays.hashCode(new Object[] {componentName, user}); - - } - - @Override - public int hashCode() { - return mHashCode; - } - - @Override - public boolean equals(Object o) { - ComponentKey other = (ComponentKey) o; - return other.componentName.equals(componentName) && other.user.equals(user); - } - - /** - * Encodes a component key as a string of the form [flattenedComponentString#userId]. - */ - @Override - public String toString() { - return componentName.flattenToString() + "#" + user; - } -} \ No newline at end of file diff --git a/iconloaderlib/src/com/android/launcher3/util/NoLocaleSQLiteHelper.java b/iconloaderlib/src/com/android/launcher3/util/NoLocaleSQLiteHelper.java deleted file mode 100644 index fe864a2847..0000000000 --- a/iconloaderlib/src/com/android/launcher3/util/NoLocaleSQLiteHelper.java +++ /dev/null @@ -1,58 +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.launcher3.util; - -import static android.database.sqlite.SQLiteDatabase.NO_LOCALIZED_COLLATORS; - -import android.content.Context; -import android.content.ContextWrapper; -import android.database.DatabaseErrorHandler; -import android.database.sqlite.SQLiteDatabase; -import android.database.sqlite.SQLiteDatabase.CursorFactory; -import android.database.sqlite.SQLiteDatabase.OpenParams; -import android.database.sqlite.SQLiteOpenHelper; -import android.os.Build; - -/** - * Extension of {@link SQLiteOpenHelper} which avoids creating default locale table by - * A context wrapper which creates databases without support for localized collators. - */ -public abstract class NoLocaleSQLiteHelper extends SQLiteOpenHelper { - - private static final boolean ATLEAST_P = - Build.VERSION.SDK_INT >= Build.VERSION_CODES.P; - - public NoLocaleSQLiteHelper(Context context, String name, int version) { - super(ATLEAST_P ? context : new NoLocalContext(context), name, null, version); - if (ATLEAST_P) { - setOpenParams(new OpenParams.Builder().addOpenFlags(NO_LOCALIZED_COLLATORS).build()); - } - } - - private static class NoLocalContext extends ContextWrapper { - public NoLocalContext(Context base) { - super(base); - } - - @Override - public SQLiteDatabase openOrCreateDatabase( - String name, int mode, CursorFactory factory, DatabaseErrorHandler errorHandler) { - return super.openOrCreateDatabase( - name, mode | Context.MODE_NO_LOCALIZED_COLLATORS, factory, errorHandler); - } - } -} diff --git a/iconloaderlib/src/com/android/launcher3/util/SQLiteCacheHelper.java b/iconloaderlib/src/com/android/launcher3/util/SQLiteCacheHelper.java deleted file mode 100644 index 49de4bd1bf..0000000000 --- a/iconloaderlib/src/com/android/launcher3/util/SQLiteCacheHelper.java +++ /dev/null @@ -1,125 +0,0 @@ -package com.android.launcher3.util; - -import android.content.ContentValues; -import android.content.Context; -import android.database.Cursor; -import android.database.sqlite.SQLiteDatabase; -import android.database.sqlite.SQLiteException; -import android.database.sqlite.SQLiteFullException; -import android.database.sqlite.SQLiteOpenHelper; -import android.util.Log; - -/** - * An extension of {@link SQLiteOpenHelper} with utility methods for a single table cache DB. - * Any exception during write operations are ignored, and any version change causes a DB reset. - */ -public abstract class SQLiteCacheHelper { - private static final String TAG = "SQLiteCacheHelper"; - - private static final boolean IN_MEMORY_CACHE = false; - - private final String mTableName; - private final MySQLiteOpenHelper mOpenHelper; - - private boolean mIgnoreWrites; - - public SQLiteCacheHelper(Context context, String name, int version, String tableName) { - if (IN_MEMORY_CACHE) { - name = null; - } - mTableName = tableName; - mOpenHelper = new MySQLiteOpenHelper(context, name, version); - - mIgnoreWrites = false; - } - - /** - * @see SQLiteDatabase#delete(String, String, String[]) - */ - public void delete(String whereClause, String[] whereArgs) { - if (mIgnoreWrites) { - return; - } - try { - mOpenHelper.getWritableDatabase().delete(mTableName, whereClause, whereArgs); - } catch (SQLiteFullException e) { - onDiskFull(e); - } catch (SQLiteException e) { - Log.d(TAG, "Ignoring sqlite exception", e); - } - } - - /** - * @see SQLiteDatabase#insertWithOnConflict(String, String, ContentValues, int) - */ - public void insertOrReplace(ContentValues values) { - if (mIgnoreWrites) { - return; - } - try { - mOpenHelper.getWritableDatabase().insertWithOnConflict( - mTableName, null, values, SQLiteDatabase.CONFLICT_REPLACE); - } catch (SQLiteFullException e) { - onDiskFull(e); - } catch (SQLiteException e) { - Log.d(TAG, "Ignoring sqlite exception", e); - } - } - - private void onDiskFull(SQLiteFullException e) { - Log.e(TAG, "Disk full, all write operations will be ignored", e); - mIgnoreWrites = true; - } - - /** - * @see SQLiteDatabase#query(String, String[], String, String[], String, String, String) - */ - public Cursor query(String[] columns, String selection, String[] selectionArgs) { - return mOpenHelper.getReadableDatabase().query( - mTableName, columns, selection, selectionArgs, null, null, null); - } - - public void clear() { - mOpenHelper.clearDB(mOpenHelper.getWritableDatabase()); - } - - public void close() { - mOpenHelper.close(); - } - - protected abstract void onCreateTable(SQLiteDatabase db); - - /** - * A private inner class to prevent direct DB access. - */ - private class MySQLiteOpenHelper extends NoLocaleSQLiteHelper { - - public MySQLiteOpenHelper(Context context, String name, int version) { - super(context, name, version); - } - - @Override - public void onCreate(SQLiteDatabase db) { - onCreateTable(db); - } - - @Override - public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) { - if (oldVersion != newVersion) { - clearDB(db); - } - } - - @Override - public void onDowngrade(SQLiteDatabase db, int oldVersion, int newVersion) { - if (oldVersion != newVersion) { - clearDB(db); - } - } - - private void clearDB(SQLiteDatabase db) { - db.execSQL("DROP TABLE IF EXISTS " + mTableName); - onCreate(db); - } - } -} diff --git a/iconloaderlib/src_full_lib/com/android/launcher3/icons/IconFactory.java b/iconloaderlib/src_full_lib/com/android/launcher3/icons/IconFactory.java deleted file mode 100644 index 48f11fde3b..0000000000 --- a/iconloaderlib/src_full_lib/com/android/launcher3/icons/IconFactory.java +++ /dev/null @@ -1,89 +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.launcher3.icons; - -import android.content.Context; - -/** - * Wrapper class to provide access to {@link BaseIconFactory} and also to provide pool of this class - * that are threadsafe. - */ -public class IconFactory extends BaseIconFactory { - - private static final Object sPoolSync = new Object(); - private static IconFactory sPool; - private static int sPoolId = 0; - - /** - * Return a new Message instance from the global pool. Allows us to - * avoid allocating new objects in many cases. - */ - public static IconFactory obtain(Context context) { - int poolId; - synchronized (sPoolSync) { - if (sPool != null) { - IconFactory m = sPool; - sPool = m.next; - m.next = null; - return m; - } - poolId = sPoolId; - } - - return new IconFactory(context, - context.getResources().getConfiguration().densityDpi, - context.getResources().getDimensionPixelSize(R.dimen.default_icon_bitmap_size), - poolId); - } - - public static void clearPool() { - synchronized (sPoolSync) { - sPool = null; - sPoolId++; - } - } - - private final int mPoolId; - - private IconFactory next; - - private IconFactory(Context context, int fillResIconDpi, int iconBitmapSize, int poolId) { - super(context, fillResIconDpi, iconBitmapSize); - mPoolId = poolId; - } - - /** - * Recycles a LauncherIcons that may be in-use. - */ - public void recycle() { - synchronized (sPoolSync) { - if (sPoolId != mPoolId) { - return; - } - // Clear any temporary state variables - clear(); - - next = sPool; - sPool = this; - } - } - - @Override - public void close() { - recycle(); - } -} diff --git a/iconloaderlib/src_full_lib/com/android/launcher3/icons/SimpleIconCache.java b/iconloaderlib/src_full_lib/com/android/launcher3/icons/SimpleIconCache.java deleted file mode 100644 index 1337975f19..0000000000 --- a/iconloaderlib/src_full_lib/com/android/launcher3/icons/SimpleIconCache.java +++ /dev/null @@ -1,114 +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.launcher3.icons; - -import static android.content.Intent.ACTION_MANAGED_PROFILE_ADDED; -import static android.content.Intent.ACTION_MANAGED_PROFILE_REMOVED; - -import android.annotation.TargetApi; -import android.content.BroadcastReceiver; -import android.content.Context; -import android.content.Intent; -import android.content.IntentFilter; -import android.content.pm.ApplicationInfo; -import android.os.Build; -import android.os.Handler; -import android.os.HandlerThread; -import android.os.Looper; -import android.os.UserHandle; -import android.os.UserManager; -import android.util.SparseLongArray; - -import com.android.launcher3.icons.cache.BaseIconCache; - -/** - * Wrapper class to provide access to {@link BaseIconFactory} and also to provide pool of this class - * that are threadsafe. - */ -@TargetApi(Build.VERSION_CODES.P) -public class SimpleIconCache extends BaseIconCache { - - private static SimpleIconCache sIconCache = null; - private static final Object CACHE_LOCK = new Object(); - - private final SparseLongArray mUserSerialMap = new SparseLongArray(2); - private final UserManager mUserManager; - - public SimpleIconCache(Context context, String dbFileName, Looper bgLooper, int iconDpi, - int iconPixelSize, boolean inMemoryCache) { - super(context, dbFileName, bgLooper, iconDpi, iconPixelSize, inMemoryCache); - mUserManager = context.getSystemService(UserManager.class); - - // Listen for user cache changes. - IntentFilter filter = new IntentFilter(ACTION_MANAGED_PROFILE_ADDED); - filter.addAction(ACTION_MANAGED_PROFILE_REMOVED); - context.registerReceiver(new BroadcastReceiver() { - @Override - public void onReceive(Context context, Intent intent) { - resetUserCache(); - } - }, filter, null, new Handler(bgLooper), 0); - } - - @Override - protected long getSerialNumberForUser(UserHandle user) { - synchronized (mUserSerialMap) { - int index = mUserSerialMap.indexOfKey(user.getIdentifier()); - if (index >= 0) { - return mUserSerialMap.valueAt(index); - } - long serial = mUserManager.getSerialNumberForUser(user); - mUserSerialMap.put(user.getIdentifier(), serial); - return serial; - } - } - - private void resetUserCache() { - synchronized (mUserSerialMap) { - mUserSerialMap.clear(); - } - } - - @Override - protected boolean isInstantApp(ApplicationInfo info) { - return info.isInstantApp(); - } - - @Override - protected BaseIconFactory getIconFactory() { - return IconFactory.obtain(mContext); - } - - public static SimpleIconCache getIconCache(Context context) { - synchronized (CACHE_LOCK) { - if (sIconCache != null) { - return sIconCache; - } - boolean inMemoryCache = - context.getResources().getBoolean(R.bool.simple_cache_enable_im_memory); - String dbFileName = context.getString(R.string.cache_db_name); - - HandlerThread bgThread = new HandlerThread("simple-icon-cache"); - bgThread.start(); - - sIconCache = new SimpleIconCache(context.getApplicationContext(), dbFileName, - bgThread.getLooper(), context.getResources().getConfiguration().densityDpi, - context.getResources().getDimensionPixelSize(R.dimen.default_icon_bitmap_size), - inMemoryCache); - return sIconCache; - } - } -} -- GitLab From ca4f2e81facaa0637bedee918f1dc04f8d460c05 Mon Sep 17 00:00:00 2001 From: Bill Yi Date: Wed, 18 Mar 2020 22:06:50 -0700 Subject: [PATCH 0005/1664] Import translations. DO NOT MERGE BUG: 151750123 Auto-generated-cl: translation import Change-Id: I5c7e8ffccb7830d2159bfe0de0d35fe011938e74 --- res/values-rm/strings.xml | 203 -------------------------------------- 1 file changed, 203 deletions(-) delete mode 100644 res/values-rm/strings.xml diff --git a/res/values-rm/strings.xml b/res/values-rm/strings.xml deleted file mode 100644 index 0758148f58..0000000000 --- a/res/values-rm/strings.xml +++ /dev/null @@ -1,203 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -- GitLab From 0b65822f308902bb1dc98ea98e0362a830d9aa25 Mon Sep 17 00:00:00 2001 From: vadimt Date: Mon, 13 Apr 2020 18:30:39 -0700 Subject: [PATCH 0006/1664] Temporarily disabling events checking in git_master Bug: 153824894 Change-Id: I5f294d1a808b1ceefd0b753de6c9207cf92de06d --- .../com/android/launcher3/tapl/LauncherInstrumentation.java | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/tests/tapl/com/android/launcher3/tapl/LauncherInstrumentation.java b/tests/tapl/com/android/launcher3/tapl/LauncherInstrumentation.java index 975fe9c1e7..b8e55651ae 100644 --- a/tests/tapl/com/android/launcher3/tapl/LauncherInstrumentation.java +++ b/tests/tapl/com/android/launcher3/tapl/LauncherInstrumentation.java @@ -250,7 +250,8 @@ public final class LauncherInstrumentation { } public void enableCheckEventsForSuccessfulGestures() { - mCheckEventsForSuccessfulGestures = true; + // b/153824894 + // mCheckEventsForSuccessfulGestures = true; } public void setOnLauncherCrashed(Runnable onLauncherCrashed) { -- GitLab From 7d375e31fe5d221750f9128c20121e34c1ab142f Mon Sep 17 00:00:00 2001 From: Tony Huang Date: Tue, 26 Nov 2019 14:00:14 +0800 Subject: [PATCH 0007/1664] Enable one-handed mode gestural in QuickStep Handling swipe-down/swipe-up gestural in device bottom area for one-handed mode 1) The regsion is larger than gesture navigationbar view 2) One handed gestural in quickstep only active on NO_BUTTON, TWO_BUTTONS mode 3) One handed gestural only support on portrait mode Bug: 150747547 Bug: 154189137 Bug: 156988988 Test: make and install Test: manual enable one handed mode and swipe down to trigger Test: manual start one handed and rotate device Change-Id: I7b2447bfb2fe4082c95176b62934b98077b84920 --- .../quickstep/TouchInteractionService.java | 18 ++- .../OneHandedModeInputConsumer.java | 143 ++++++++++++++++++ quickstep/res/values/dimens.xml | 4 + .../com/android/quickstep/InputConsumer.java | 2 + .../OrientationTouchTransformer.java | 14 +- .../RecentsAnimationDeviceState.java | 45 +++++- .../com/android/quickstep/SystemUiProxy.java | 22 +++ .../util/SecureSettingsObserver.java | 8 + 8 files changed, 250 insertions(+), 6 deletions(-) create mode 100644 quickstep/recents_ui_overrides/src/com/android/quickstep/inputconsumers/OneHandedModeInputConsumer.java diff --git a/quickstep/recents_ui_overrides/src/com/android/quickstep/TouchInteractionService.java b/quickstep/recents_ui_overrides/src/com/android/quickstep/TouchInteractionService.java index 8bffc7805c..a88e413c84 100644 --- a/quickstep/recents_ui_overrides/src/com/android/quickstep/TouchInteractionService.java +++ b/quickstep/recents_ui_overrides/src/com/android/quickstep/TouchInteractionService.java @@ -75,6 +75,7 @@ import com.android.launcher3.util.WindowBounds; import com.android.quickstep.inputconsumers.AccessibilityInputConsumer; import com.android.quickstep.inputconsumers.AssistantInputConsumer; import com.android.quickstep.inputconsumers.DeviceLockedInputConsumer; +import com.android.quickstep.inputconsumers.OneHandedModeInputConsumer; import com.android.quickstep.inputconsumers.OtherActivityInputConsumer; import com.android.quickstep.inputconsumers.OverscrollInputConsumer; import com.android.quickstep.inputconsumers.OverviewInputConsumer; @@ -85,7 +86,6 @@ import com.android.quickstep.util.ActiveGestureLog; import com.android.quickstep.util.AssistantUtilities; import com.android.quickstep.util.ProtoTracer; import com.android.quickstep.util.SplitScreenBounds; -import com.android.quickstep.views.RecentsView; import com.android.systemui.plugins.OverscrollPlugin; import com.android.systemui.plugins.PluginListener; import com.android.systemui.shared.recents.IOverviewProxy; @@ -492,6 +492,12 @@ public class TouchInteractionService extends Service implements PluginListener mSquaredSlop) { + mStartDragPos.set(mLastPos.x, mLastPos.y); + if ((!mDeviceState.isOneHandedModeActive() && isValidStartAngle( + mDownPos.x - mLastPos.x, mDownPos.y - mLastPos.y)) + || (mDeviceState.isOneHandedModeActive() && isValidExitAngle( + mDownPos.x - mLastPos.x, mDownPos.y - mLastPos.y))) { + mPassedSlop = true; + setActive(ev); + } else { + mState = STATE_DELEGATE_ACTIVE; + } + } + } else { + float distance = (float) Math.hypot(mLastPos.x - mStartDragPos.x, + mLastPos.y - mStartDragPos.y); + if (distance > mDragDistThreshold && mPassedSlop + && mDeviceState.isOneHandedModeActive()) { + SystemUiProxy.INSTANCE.get(mContext).stopOneHandedMode(); + } + } + break; + } + case ACTION_UP: + case ACTION_CANCEL: { + if (mLastPos.y >= mStartDragPos.y && mPassedSlop + && !mDeviceState.isOneHandedModeActive()) { + SystemUiProxy.INSTANCE.get(mContext).startOneHandedMode(); + } + + mPassedSlop = false; + mState = STATE_INACTIVE; + break; + } + } + + if (mState != STATE_ACTIVE) { + mDelegate.onMotionEvent(ev); + } + } + + private boolean isValidStartAngle(float deltaX, float deltaY) { + final float angle = (float) Math.toDegrees(Math.atan2(deltaY, deltaX)); + return angle > -(ANGLE_MAX) && angle < -(ANGLE_MIN); + } + + private boolean isValidExitAngle(float deltaX, float deltaY) { + final float angle = (float) Math.toDegrees(Math.atan2(deltaY, deltaX)); + return angle > ANGLE_MIN && angle < ANGLE_MAX; + } +} diff --git a/quickstep/res/values/dimens.xml b/quickstep/res/values/dimens.xml index 9d703167d9..6737c5f61d 100644 --- a/quickstep/res/values/dimens.xml +++ b/quickstep/res/values/dimens.xml @@ -76,6 +76,10 @@ 55dp 55dp + + + 20dp + 28dp diff --git a/quickstep/src/com/android/quickstep/InputConsumer.java b/quickstep/src/com/android/quickstep/InputConsumer.java index a4861dce01..0eed3e322d 100644 --- a/quickstep/src/com/android/quickstep/InputConsumer.java +++ b/quickstep/src/com/android/quickstep/InputConsumer.java @@ -34,6 +34,7 @@ public interface InputConsumer { int TYPE_OVERVIEW_WITHOUT_FOCUS = 1 << 7; int TYPE_RESET_GESTURE = 1 << 8; int TYPE_OVERSCROLL = 1 << 9; + int TYPE_ONE_HANDED = 1 << 10; String[] NAMES = new String[] { "TYPE_NO_OP", // 0 @@ -46,6 +47,7 @@ public interface InputConsumer { "TYPE_OVERVIEW_WITHOUT_FOCUS", // 7 "TYPE_RESET_GESTURE", // 8 "TYPE_OVERSCROLL", // 9 + "TYPE_ONE_HANDED", // 10 }; InputConsumer NO_OP = () -> TYPE_NO_OP; diff --git a/quickstep/src/com/android/quickstep/OrientationTouchTransformer.java b/quickstep/src/com/android/quickstep/OrientationTouchTransformer.java index 4cf7aab4ab..6b94a79000 100644 --- a/quickstep/src/com/android/quickstep/OrientationTouchTransformer.java +++ b/quickstep/src/com/android/quickstep/OrientationTouchTransformer.java @@ -37,7 +37,6 @@ import android.view.Surface; import com.android.launcher3.R; import com.android.launcher3.ResourceUtils; import com.android.launcher3.util.DefaultDisplay; -import com.android.quickstep.util.RecentsOrientedState.SurfaceRotation; import java.io.PrintWriter; @@ -63,6 +62,7 @@ class OrientationTouchTransformer { private SparseArray mSwipeTouchRegions = new SparseArray<>(MAX_ORIENTATIONS); private final RectF mAssistantLeftRegion = new RectF(); private final RectF mAssistantRightRegion = new RectF(); + private final RectF mOneHandedModeRegion = new RectF(); private int mCurrentDisplayRotation; private boolean mEnableMultipleRegions; private Resources mResources; @@ -185,10 +185,10 @@ class OrientationTouchTransformer { Point size = display.realSize; int rotation = display.rotation; + int touchHeight = getNavbarSize(ResourceUtils.NAVBAR_BOTTOM_GESTURE_SIZE); OrientationRectF orientationRectF = new OrientationRectF(0, 0, size.x, size.y, rotation); if (mMode == SysUINavigationMode.Mode.NO_BUTTON) { - int touchHeight = getNavbarSize(ResourceUtils.NAVBAR_BOTTOM_GESTURE_SIZE); orientationRectF.top = orientationRectF.bottom - touchHeight; final int assistantWidth = mResources @@ -217,10 +217,11 @@ class OrientationTouchTransformer { + getNavbarSize(ResourceUtils.NAVBAR_LANDSCAPE_LEFT_RIGHT_SIZE); break; default: - orientationRectF.top = orientationRectF.bottom - - getNavbarSize(ResourceUtils.NAVBAR_BOTTOM_GESTURE_SIZE); + orientationRectF.top = orientationRectF.bottom - touchHeight; } } + // One handed gestural only active on portrait mode + mOneHandedModeRegion.set(0, orientationRectF.bottom - touchHeight, size.x, size.y); return orientationRectF; } @@ -231,6 +232,10 @@ class OrientationTouchTransformer { } + boolean touchInOneHandedModeRegion(MotionEvent ev) { + return mOneHandedModeRegion.contains(ev.getX(), ev.getY()); + } + private int getNavbarSize(String resName) { return ResourceUtils.getNavbarSize(resName, mResources); } @@ -313,6 +318,7 @@ class OrientationTouchTransformer { regions.append(rectF.mRotation).append(" "); } pw.println(regions.toString()); + pw.println(" mOneHandedModeRegion=" + mOneHandedModeRegion); } private class OrientationRectF extends RectF { diff --git a/quickstep/src/com/android/quickstep/RecentsAnimationDeviceState.java b/quickstep/src/com/android/quickstep/RecentsAnimationDeviceState.java index a892ddcccd..c32abcf0d8 100644 --- a/quickstep/src/com/android/quickstep/RecentsAnimationDeviceState.java +++ b/quickstep/src/com/android/quickstep/RecentsAnimationDeviceState.java @@ -29,6 +29,7 @@ import static com.android.systemui.shared.system.QuickStepContract.SYSUI_STATE_B import static com.android.systemui.shared.system.QuickStepContract.SYSUI_STATE_HOME_DISABLED; import static com.android.systemui.shared.system.QuickStepContract.SYSUI_STATE_NAV_BAR_HIDDEN; import static com.android.systemui.shared.system.QuickStepContract.SYSUI_STATE_NOTIFICATION_PANEL_EXPANDED; +import static com.android.systemui.shared.system.QuickStepContract.SYSUI_STATE_ONE_HANDED_ACTIVE; import static com.android.systemui.shared.system.QuickStepContract.SYSUI_STATE_OVERVIEW_DISABLED; import static com.android.systemui.shared.system.QuickStepContract.SYSUI_STATE_QUICK_SETTINGS_EXPANDED; import static com.android.systemui.shared.system.QuickStepContract.SYSUI_STATE_SCREEN_PINNING; @@ -43,16 +44,19 @@ import android.content.IntentFilter; import android.content.res.Resources; import android.graphics.Region; import android.os.Process; +import android.os.SystemProperties; import android.os.UserManager; import android.text.TextUtils; -import android.util.Log; +import android.util.DisplayMetrics; import android.view.MotionEvent; +import android.view.Surface; import androidx.annotation.BinderThread; import com.android.launcher3.R; import com.android.launcher3.Utilities; import com.android.launcher3.util.DefaultDisplay; +import com.android.launcher3.util.SecureSettingsObserver; import com.android.quickstep.SysUINavigationMode.NavigationModeChangeListener; import com.android.quickstep.util.NavBarPosition; import com.android.systemui.shared.system.ActivityManagerWrapper; @@ -73,6 +77,8 @@ public class RecentsAnimationDeviceState implements NavigationModeChangeListener, DefaultDisplay.DisplayInfoChangeListener { + private static boolean sIsOneHandedEnabled; + private final Context mContext; private final SysUINavigationMode mSysUiNavMode; private final DefaultDisplay mDefaultDisplay; @@ -84,6 +90,7 @@ public class RecentsAnimationDeviceState implements private @SystemUiStateFlags int mSystemUiStateFlags; private SysUINavigationMode.Mode mMode = THREE_BUTTONS; private NavBarPosition mNavBarPosition; + private SecureSettingsObserver mOneHandedEnabledObserver; private final Region mDeferredGestureRegion = new Region(); private boolean mAssistantAvailable; @@ -169,6 +176,13 @@ public class RecentsAnimationDeviceState implements ComponentName.unflattenFromString(blockingActivity)); } } + + if (SystemProperties.getBoolean("ro.support_one_handed_mode", false)) { + mOneHandedEnabledObserver = SecureSettingsObserver.newOneHandedSettingsObserver( + mContext, this::onOneHandedEnabledSettingsChanged); + mOneHandedEnabledObserver.register(); + mOneHandedEnabledObserver.dispatchOnChange(); + } } private void setupOrientationSwipeHandler() { @@ -436,6 +450,13 @@ public class RecentsAnimationDeviceState implements event.getY(pointerIndex)); } + /** + * @return whether screen pinning is enabled and active + */ + public boolean isOneHandedModeActive() { + return (mSystemUiStateFlags & SYSUI_STATE_ONE_HANDED_ACTIVE) != 0; + } + /** * Sets the region in screen space where the gestures should be deferred (ie. due to specific * nav bar ui). @@ -496,6 +517,28 @@ public class RecentsAnimationDeviceState implements && !isLockToAppActive(); } + /** + * One handed gestural in quickstep only active on NO_BUTTON, TWO_BUTTONS, and portrait mode + * + * @param ev The touch screen motion event. + * @return whether the given motion event can trigger the one handed mode. + */ + public boolean canTriggerOneHandedAction(MotionEvent ev) { + if (!sIsOneHandedEnabled) { + return false; + } + + final DefaultDisplay.Info displayInfo = mDefaultDisplay.getInfo(); + return (mOrientationTouchTransformer.touchInOneHandedModeRegion(ev) + && displayInfo.rotation != Surface.ROTATION_90 + && displayInfo.rotation != Surface.ROTATION_270 + && displayInfo.metrics.densityDpi < DisplayMetrics.DENSITY_600); + } + + private void onOneHandedEnabledSettingsChanged(boolean isOneHandedEnabled) { + sIsOneHandedEnabled = isOneHandedEnabled; + } + /** * *May* apply a transform on the motion event if it lies in the nav bar region for another * orientation that is currently being tracked as a part of quickstep diff --git a/quickstep/src/com/android/quickstep/SystemUiProxy.java b/quickstep/src/com/android/quickstep/SystemUiProxy.java index 20d133c3c6..a3eabc966c 100644 --- a/quickstep/src/com/android/quickstep/SystemUiProxy.java +++ b/quickstep/src/com/android/quickstep/SystemUiProxy.java @@ -344,4 +344,26 @@ public class SystemUiProxy implements ISystemUiProxy { } } } + + @Override + public void startOneHandedMode() { + if (mSystemUiProxy != null) { + try { + mSystemUiProxy.startOneHandedMode(); + } catch (RemoteException e) { + Log.w(TAG, "Failed call startOneHandedMode", e); + } + } + } + + @Override + public void stopOneHandedMode() { + if (mSystemUiProxy != null) { + try { + mSystemUiProxy.stopOneHandedMode(); + } catch (RemoteException e) { + Log.w(TAG, "Failed call stopOneHandedMode", e); + } + } + } } diff --git a/src/com/android/launcher3/util/SecureSettingsObserver.java b/src/com/android/launcher3/util/SecureSettingsObserver.java index 48aa02b799..08a8e6d255 100644 --- a/src/com/android/launcher3/util/SecureSettingsObserver.java +++ b/src/com/android/launcher3/util/SecureSettingsObserver.java @@ -29,6 +29,8 @@ public class SecureSettingsObserver extends ContentObserver { /** Hidden field Settings.Secure.NOTIFICATION_BADGING */ public static final String NOTIFICATION_BADGING = "notification_badging"; + /** Hidden field Settings.Secure.ONE_HANDED_MODE_ENABLED */ + public static final String ONE_HANDED_ENABLED = "one_handed_mode_enabled"; private final ContentResolver mResolver; private final String mKeySetting; @@ -79,4 +81,10 @@ public class SecureSettingsObserver extends ContentObserver { return new SecureSettingsObserver( context.getContentResolver(), listener, NOTIFICATION_BADGING, 1); } + + public static SecureSettingsObserver newOneHandedSettingsObserver(Context context, + OnChangeListener listener) { + return new SecureSettingsObserver( + context.getContentResolver(), listener, ONE_HANDED_ENABLED, 1); + } } -- GitLab From 5ca2980e54c2b1f50f5cb4262e79c30a9f180864 Mon Sep 17 00:00:00 2001 From: Adam Cohen Date: Tue, 2 Jun 2020 18:21:18 -0400 Subject: [PATCH 0008/1664] Merging from ub-launcher3-rvc-dev @ build 6552182 Bug:150504032 Test: manual, presubmit on the source branch x20/teams/android-launcher/merge/ub-launcher3-rvc-dev_rvc-dev_6552182.html Change-Id: I795aafd151c6ce53077e1aebb97567eedd84f274 --- protos/launcher_atom.proto | 6 + .../hybridhotseat/HotseatEduController.java | 73 +--- .../HotseatPredictionController.java | 4 +- .../uioverrides/QuickstepLauncher.java | 15 + .../QuickstepAtomicAnimationFactory.java | 3 +- .../NavBarToHomeTouchController.java | 3 +- .../android/quickstep/BaseSwipeUpHandler.java | 322 +--------------- .../quickstep/BaseSwipeUpHandlerV2.java | 9 +- .../quickstep/FallbackActivityInterface.java | 2 +- .../quickstep/LauncherActivityInterface.java | 16 +- .../QuickstepTestInformationHandler.java | 5 +- .../quickstep/SwipeUpAnimationLogic.java | 350 ++++++++++++++++++ .../quickstep/TouchInteractionService.java | 13 +- .../FallbackNavBarTouchController.java | 12 +- .../OtherActivityInputConsumer.java | 5 + .../OverviewWithoutFocusInputConsumer.java | 12 +- .../SysUiOverlayInputConsumer.java | 86 +++++ .../quickstep/util/TaskViewSimulator.java | 21 +- .../util/TriggerSwipeUpTouchTracker.java | 14 +- .../quickstep/views/OverviewActionsView.java | 2 - .../android/quickstep/views/RecentsView.java | 5 + .../res/layout/gesture_tutorial_fragment.xml | 7 + quickstep/res/values/strings.xml | 6 - .../statehandlers/DepthController.java | 21 +- .../quickstep/BaseActivityInterface.java | 2 +- .../com/android/quickstep/InputConsumer.java | 11 +- .../OrientationTouchTransformer.java | 27 +- .../android/quickstep/RecentTasksList.java | 83 +++-- .../quickstep/RecentsAnimationController.java | 31 +- .../RecentsAnimationDeviceState.java | 100 ++++- .../BackGestureTutorialController.java | 8 +- .../HomeGestureTutorialController.java | 227 +++++++++++- .../interaction/NavBarGestureHandler.java | 46 ++- .../interaction/TutorialController.java | 5 + .../interaction/TutorialFragment.java | 5 - .../logging/StatsLogCompatManager.java | 19 +- .../quickstep/util/RecentsOrientedState.java | 6 +- .../quickstep/RecentTasksListTest.java | 8 +- res/values/colors.xml | 1 + .../launcher3/BaseDraggingActivity.java | 14 +- src/com/android/launcher3/DeviceProfile.java | 16 +- .../launcher3/InvariantDeviceProfile.java | 2 +- .../android/launcher3/LauncherSettings.java | 3 +- .../allapps/AllAppsTransitionController.java | 6 +- .../android/launcher3/anim/Interpolators.java | 5 + src/com/android/launcher3/folder/Folder.java | 1 + .../launcher3/logging/StatsLogManager.java | 34 +- .../launcher3/model/data/ItemInfo.java | 6 + .../popup/PopupContainerWithArrow.java | 6 +- .../launcher3/popup/PopupPopulator.java | 3 + .../launcher3/popup/SystemShortcut.java | 9 +- .../touch/WorkspaceTouchListener.java | 7 +- .../launcher3/util/DefaultDisplay.java | 9 +- .../launcher3/views/BaseDragLayer.java | 5 + .../launcher3/views/FloatingIconView.java | 10 + .../android/launcher3/views/ListenerView.java | 25 +- .../launcher3/views/OptionsPopupView.java | 35 +- .../uioverrides/PreviewSurfaceRenderer.java | 27 -- 58 files changed, 1192 insertions(+), 622 deletions(-) create mode 100644 quickstep/recents_ui_overrides/src/com/android/quickstep/SwipeUpAnimationLogic.java create mode 100644 quickstep/recents_ui_overrides/src/com/android/quickstep/inputconsumers/SysUiOverlayInputConsumer.java delete mode 100644 src_ui_overrides/com/android/launcher3/uioverrides/PreviewSurfaceRenderer.java diff --git a/protos/launcher_atom.proto b/protos/launcher_atom.proto index 5f5fab0f8c..d552daff43 100644 --- a/protos/launcher_atom.proto +++ b/protos/launcher_atom.proto @@ -51,6 +51,7 @@ message ContainerInfo { WidgetsContainer widgets_container = 5; PredictionContainer prediction_container = 6; SearchResultContainer search_result_container = 7; + ShortcutsContainer shortcuts_container = 8; } } @@ -69,6 +70,11 @@ message PredictionContainer { message SearchResultContainer { } +// Container for package specific shortcuts to deep links and notifications. +// Typically shown as popup window by longpressing on an icon. +message ShortcutsContainer { +} + enum Origin { UNKNOWN = 0; DEFAULT_LAYOUT = 1; // icon automatically placed in workspace, folder, hotseat diff --git a/quickstep/recents_ui_overrides/src/com/android/launcher3/hybridhotseat/HotseatEduController.java b/quickstep/recents_ui_overrides/src/com/android/launcher3/hybridhotseat/HotseatEduController.java index e4d0adf0c0..7c4f3ecd01 100644 --- a/quickstep/recents_ui_overrides/src/com/android/launcher3/hybridhotseat/HotseatEduController.java +++ b/quickstep/recents_ui_overrides/src/com/android/launcher3/hybridhotseat/HotseatEduController.java @@ -15,17 +15,9 @@ */ package com.android.launcher3.hybridhotseat; -import android.app.Notification; -import android.app.NotificationChannel; -import android.app.NotificationManager; -import android.app.PendingIntent; import android.content.Intent; -import android.content.res.Configuration; -import android.os.Build; import android.view.View; -import androidx.core.app.NotificationCompat; - import com.android.launcher3.CellLayout; import com.android.launcher3.Hotseat; import com.android.launcher3.InvariantDeviceProfile; @@ -37,11 +29,8 @@ import com.android.launcher3.config.FeatureFlags; import com.android.launcher3.model.data.FolderInfo; import com.android.launcher3.model.data.ItemInfo; import com.android.launcher3.model.data.WorkspaceItemInfo; -import com.android.launcher3.uioverrides.QuickstepLauncher; -import com.android.launcher3.util.ActivityTracker; import com.android.launcher3.util.GridOccupancy; import com.android.launcher3.util.IntArray; -import com.android.launcher3.util.Themes; import com.android.launcher3.views.ArrowTipView; import com.android.launcher3.views.Snackbar; @@ -54,18 +43,15 @@ import java.util.stream.IntStream; * Controller class for managing user onboaridng flow for hybrid hotseat */ public class HotseatEduController { - public static final String KEY_HOTSEAT_EDU_SEEN = "hotseat_edu_seen"; - - private static final String NOTIFICATION_CHANNEL_ID = "launcher_onboarding"; - private static final int ONBOARDING_NOTIFICATION_ID = 7641; + public static final String KEY_HOTSEAT_EDU_SEEN = "hotseat_edu_seen"; + public static final String HOTSEAT_EDU_ACTION = + "com.android.launcher3.action.SHOW_HYBRID_HOTSEAT_EDU"; private static final String SETTINGS_ACTION = "android.settings.ACTION_CONTENT_SUGGESTIONS_SETTINGS"; private final Launcher mLauncher; private final Hotseat mHotseat; - private final NotificationManager mNotificationManager; - private final Notification mNotification; private List mPredictedApps; private HotseatEduDialog mActiveDialog; @@ -77,9 +63,6 @@ public class HotseatEduController { mLauncher = launcher; mHotseat = launcher.getHotseat(); mOnOnboardingComplete = runnable; - mNotificationManager = mLauncher.getSystemService(NotificationManager.class); - createNotificationChannel(); - mNotification = createNotification(); } /** @@ -216,11 +199,6 @@ public class HotseatEduController { return pageId; } - - void removeNotification() { - mNotificationManager.cancel(ONBOARDING_NOTIFICATION_ID); - } - void moveHotseatItems() { mHotseat.removeAllViewsInLayout(); if (!mNewItems.isEmpty()) { @@ -258,45 +236,9 @@ public class HotseatEduController { void setPredictedApps(List predictedApps) { mPredictedApps = predictedApps; - if (!mPredictedApps.isEmpty() - && mLauncher.getOrientation() == Configuration.ORIENTATION_PORTRAIT) { - mNotificationManager.notify(ONBOARDING_NOTIFICATION_ID, mNotification); - } - else { - removeNotification(); - } - } - - private void createNotificationChannel() { - if (Build.VERSION.SDK_INT < Build.VERSION_CODES.O) return; - CharSequence name = mLauncher.getString(R.string.hotseat_edu_prompt_title); - int importance = NotificationManager.IMPORTANCE_LOW; - NotificationChannel channel = new NotificationChannel(NOTIFICATION_CHANNEL_ID, name, - importance); - mNotificationManager.createNotificationChannel(channel); - } - - private Notification createNotification() { - Intent intent = new Intent(mLauncher.getApplicationContext(), mLauncher.getClass()); - intent = new NotificationHandler().addToIntent(intent); - - CharSequence name = mLauncher.getString(R.string.hotseat_edu_prompt_title); - String description = mLauncher.getString(R.string.hotseat_edu_prompt_content); - NotificationCompat.Builder builder = new NotificationCompat.Builder(mLauncher, - NOTIFICATION_CHANNEL_ID) - .setContentTitle(name) - .setOngoing(true) - .setColor(Themes.getColorAccent(mLauncher)) - .setContentIntent(PendingIntent.getActivity(mLauncher, 0, intent, - PendingIntent.FLAG_CANCEL_CURRENT)) - .setSmallIcon(R.drawable.hotseat_edu_notification_icon) - .setContentText(description); - return builder.build(); - } void destroy() { - removeNotification(); if (mActiveDialog != null) { mActiveDialog.setHotseatEduController(null); } @@ -334,14 +276,5 @@ public class HotseatEduController { mActiveDialog.setHotseatEduController(this); mActiveDialog.show(mPredictedApps); } - - static class NotificationHandler implements - ActivityTracker.SchedulerCallback { - @Override - public boolean init(QuickstepLauncher activity, boolean alreadyOnHome) { - activity.getHotseatPredictionController().showEdu(); - return true; - } - } } diff --git a/quickstep/recents_ui_overrides/src/com/android/launcher3/hybridhotseat/HotseatPredictionController.java b/quickstep/recents_ui_overrides/src/com/android/launcher3/hybridhotseat/HotseatPredictionController.java index e2acf9358d..05bcb57adc 100644 --- a/quickstep/recents_ui_overrides/src/com/android/launcher3/hybridhotseat/HotseatPredictionController.java +++ b/quickstep/recents_ui_overrides/src/com/android/launcher3/hybridhotseat/HotseatPredictionController.java @@ -41,7 +41,6 @@ import com.android.launcher3.InvariantDeviceProfile; import com.android.launcher3.Launcher; import com.android.launcher3.LauncherAppState; import com.android.launcher3.LauncherSettings; -import com.android.launcher3.LauncherState; import com.android.launcher3.R; import com.android.launcher3.Utilities; import com.android.launcher3.allapps.AllAppsStore; @@ -148,8 +147,7 @@ public class HotseatPredictionController implements DragController.DragListener, */ public void showEdu() { if (mHotseatEduController == null) return; - mLauncher.getStateManager().goToState(LauncherState.NORMAL, true, - () -> mHotseatEduController.showEdu()); + mHotseatEduController.showEdu(); } @Override diff --git a/quickstep/recents_ui_overrides/src/com/android/launcher3/uioverrides/QuickstepLauncher.java b/quickstep/recents_ui_overrides/src/com/android/launcher3/uioverrides/QuickstepLauncher.java index 1dc5be6141..39e0f88c40 100644 --- a/quickstep/recents_ui_overrides/src/com/android/launcher3/uioverrides/QuickstepLauncher.java +++ b/quickstep/recents_ui_overrides/src/com/android/launcher3/uioverrides/QuickstepLauncher.java @@ -44,6 +44,7 @@ import com.android.launcher3.allapps.DiscoveryBounce; import com.android.launcher3.anim.AnimatorPlaybackController; import com.android.launcher3.config.FeatureFlags; import com.android.launcher3.folder.Folder; +import com.android.launcher3.hybridhotseat.HotseatEduController; import com.android.launcher3.hybridhotseat.HotseatPredictionController; import com.android.launcher3.model.data.AppInfo; import com.android.launcher3.model.data.ItemInfo; @@ -98,6 +99,20 @@ public class QuickstepLauncher extends BaseQuickstepLauncher { } } + @Override + protected void onNewIntent(Intent intent) { + super.onNewIntent(intent); + if (HotseatEduController.HOTSEAT_EDU_ACTION.equals(intent.getAction()) + && mHotseatPredictionController != null) { + boolean alreadyOnHome = hasWindowFocus() && ((intent.getFlags() + & Intent.FLAG_ACTIVITY_BROUGHT_TO_FRONT) + != Intent.FLAG_ACTIVITY_BROUGHT_TO_FRONT); + getStateManager().goToState(NORMAL, alreadyOnHome, () -> { + mHotseatPredictionController.showEdu(); + }); + } + } + @Override public void onConfigurationChanged(Configuration newConfig) { super.onConfigurationChanged(newConfig); diff --git a/quickstep/recents_ui_overrides/src/com/android/launcher3/uioverrides/states/QuickstepAtomicAnimationFactory.java b/quickstep/recents_ui_overrides/src/com/android/launcher3/uioverrides/states/QuickstepAtomicAnimationFactory.java index 94c7771513..a487869316 100644 --- a/quickstep/recents_ui_overrides/src/com/android/launcher3/uioverrides/states/QuickstepAtomicAnimationFactory.java +++ b/quickstep/recents_ui_overrides/src/com/android/launcher3/uioverrides/states/QuickstepAtomicAnimationFactory.java @@ -29,6 +29,7 @@ import static com.android.launcher3.anim.Interpolators.DEACCEL; import static com.android.launcher3.anim.Interpolators.DEACCEL_1_7; import static com.android.launcher3.anim.Interpolators.DEACCEL_3; import static com.android.launcher3.anim.Interpolators.FAST_OUT_SLOW_IN; +import static com.android.launcher3.anim.Interpolators.FINAL_FRAME; import static com.android.launcher3.anim.Interpolators.INSTANT; import static com.android.launcher3.anim.Interpolators.LINEAR; import static com.android.launcher3.anim.Interpolators.OVERSHOOT_1_2; @@ -188,7 +189,7 @@ public class QuickstepAtomicAnimationFactory extends } } else if (toState == NORMAL && fromState == OVERVIEW_PEEK) { // Keep fully visible until the very end (when overview is offscreen) to make invisible. - config.setInterpolator(ANIM_OVERVIEW_FADE, t -> t < 1 ? 0 : 1); + config.setInterpolator(ANIM_OVERVIEW_FADE, FINAL_FRAME); } else if (toState == OVERVIEW_PEEK && fromState == NORMAL) { config.setInterpolator(ANIM_OVERVIEW_FADE, INSTANT); config.setInterpolator(ANIM_OVERVIEW_TRANSLATE_X, OVERSHOOT_1_7); diff --git a/quickstep/recents_ui_overrides/src/com/android/launcher3/uioverrides/touchcontrollers/NavBarToHomeTouchController.java b/quickstep/recents_ui_overrides/src/com/android/launcher3/uioverrides/touchcontrollers/NavBarToHomeTouchController.java index bf0690c77d..39bbfb93bc 100644 --- a/quickstep/recents_ui_overrides/src/com/android/launcher3/uioverrides/touchcontrollers/NavBarToHomeTouchController.java +++ b/quickstep/recents_ui_overrides/src/com/android/launcher3/uioverrides/touchcontrollers/NavBarToHomeTouchController.java @@ -116,8 +116,7 @@ public class NavBarToHomeTouchController implements TouchController, if (TestProtocol.sDebugTracing) { Log.d(TestProtocol.PAUSE_NOT_DETECTED, "NavBarToHomeTouchController.canInterceptTouch true 2 " - + AbstractFloatingView.getTopOpenView(mLauncher).getClass() - .getSimpleName()); + + AbstractFloatingView.getTopOpenView(mLauncher), new Exception()); } return true; } diff --git a/quickstep/recents_ui_overrides/src/com/android/quickstep/BaseSwipeUpHandler.java b/quickstep/recents_ui_overrides/src/com/android/quickstep/BaseSwipeUpHandler.java index 5b396dd547..737d837ac7 100644 --- a/quickstep/recents_ui_overrides/src/com/android/quickstep/BaseSwipeUpHandler.java +++ b/quickstep/recents_ui_overrides/src/com/android/quickstep/BaseSwipeUpHandler.java @@ -15,57 +15,38 @@ */ package com.android.quickstep; -import static com.android.launcher3.anim.Interpolators.ACCEL_1_5; -import static com.android.launcher3.anim.Interpolators.DEACCEL; import static com.android.launcher3.config.FeatureFlags.ENABLE_QUICKSTEP_LIVE_TILE; import static com.android.launcher3.util.Executors.MAIN_EXECUTOR; import static com.android.launcher3.util.VibratorWrapper.OVERVIEW_HAPTIC; -import static com.android.launcher3.views.FloatingIconView.SHAPE_PROGRESS_DURATION; -import android.animation.Animator; import android.annotation.TargetApi; import android.content.Context; import android.content.Intent; -import android.graphics.Matrix; -import android.graphics.Matrix.ScaleToFit; import android.graphics.PointF; import android.graphics.Rect; -import android.graphics.RectF; import android.os.Build; import android.util.Log; import android.view.MotionEvent; -import android.view.animation.Interpolator; import androidx.annotation.CallSuper; -import androidx.annotation.NonNull; -import androidx.annotation.Nullable; import androidx.annotation.UiThread; import com.android.launcher3.DeviceProfile; import com.android.launcher3.InvariantDeviceProfile; -import com.android.launcher3.Utilities; -import com.android.launcher3.anim.AnimationSuccessListener; -import com.android.launcher3.anim.AnimatorPlaybackController; -import com.android.launcher3.anim.PendingAnimation; import com.android.launcher3.statemanager.StatefulActivity; import com.android.launcher3.testing.TestProtocol; -import com.android.launcher3.touch.PagedOrientationHandler; import com.android.launcher3.util.VibratorWrapper; -import com.android.launcher3.views.FloatingIconView; +import com.android.launcher3.util.WindowBounds; import com.android.quickstep.RecentsAnimationCallbacks.RecentsAnimationListener; import com.android.quickstep.util.ActiveGestureLog; import com.android.quickstep.util.ActivityInitListener; -import com.android.quickstep.util.RectFSpringAnim; -import com.android.quickstep.util.TaskViewSimulator; import com.android.quickstep.util.TransformParams; -import com.android.quickstep.util.TransformParams.BuilderProxy; 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.system.InputConsumerController; import com.android.systemui.shared.system.RemoteAnimationTargetCompat; import com.android.systemui.shared.system.SyncRtSurfaceTransactionApplierCompat; -import com.android.systemui.shared.system.SyncRtSurfaceTransactionApplierCompat.SurfaceParams.Builder; import java.util.ArrayList; import java.util.function.Consumer; @@ -75,40 +56,13 @@ import java.util.function.Consumer; */ @TargetApi(Build.VERSION_CODES.Q) public abstract class BaseSwipeUpHandler, Q extends RecentsView> - implements RecentsAnimationListener { + extends SwipeUpAnimationLogic implements RecentsAnimationListener { private static final String TAG = "BaseSwipeUpHandler"; - protected static final Rect TEMP_RECT = new Rect(); - - public static final float MIN_PROGRESS_FOR_OVERVIEW = 0.7f; - private static final Interpolator PULLBACK_INTERPOLATOR = DEACCEL; - - // The distance needed to drag to reach the task size in recents. - protected int mTransitionDragLength; - // How much further we can drag past recents, as a factor of mTransitionDragLength. - protected float mDragLengthFactor = 1; - // Start resisting when swiping past this factor of mTransitionDragLength. - private float mDragLengthFactorStartPullback = 1f; - // This is how far down we can scale down, where 0f is full screen and 1f is recents. - private float mDragLengthFactorMaxPullback = 1f; - - protected final Context mContext; - protected final RecentsAnimationDeviceState mDeviceState; - protected final GestureState mGestureState; + protected final BaseActivityInterface mActivityInterface; protected final InputConsumerController mInputConsumer; - protected final TaskViewSimulator mTaskViewSimulator; - private AnimatorPlaybackController mWindowTransitionController; - - protected final TransformParams mTransformParams = new TransformParams(); - - // Shift in the range of [0, 1]. - // 0 => preview snapShot is completely visible, and hotseat is completely translated down - // 1 => preview snapShot is completely aligned with the recents view and hotseat is completely - // visible. - protected final AnimatedFloat mCurrentShift = new AnimatedFloat(this::updateFinalShift); - protected final ActivityInitListener mActivityInitListener; protected RecentsAnimationController mRecentsAnimationController; @@ -119,7 +73,6 @@ public abstract class BaseSwipeUpHandler, Q extend protected T mActivity; protected Q mRecentsView; - protected DeviceProfile mDp; protected Runnable mGestureEndCallback; @@ -131,13 +84,10 @@ public abstract class BaseSwipeUpHandler, Q extend protected BaseSwipeUpHandler(Context context, RecentsAnimationDeviceState deviceState, GestureState gestureState, InputConsumerController inputConsumer) { - mContext = context; - mDeviceState = deviceState; - mGestureState = gestureState; + super(context, deviceState, gestureState, new TransformParams()); mActivityInterface = gestureState.getActivityInterface(); mActivityInitListener = mActivityInterface.createActivityInitListener(this::onActivityInit); mInputConsumer = inputConsumer; - mTaskViewSimulator = new TaskViewSimulator(context, gestureState.getActivityInterface()); } /** @@ -157,28 +107,6 @@ public abstract class BaseSwipeUpHandler, Q extend return mRecentsView != null ? mRecentsView.getEventDispatcher(navbarRotation) : null; } - @UiThread - public void updateDisplacement(float displacement) { - // We are moving in the negative x/y direction - displacement = -displacement; - float shift; - if (displacement > mTransitionDragLength * mDragLengthFactor && mTransitionDragLength > 0) { - shift = mDragLengthFactor; - } else { - float translation = Math.max(displacement, 0); - shift = mTransitionDragLength == 0 ? 0 : translation / mTransitionDragLength; - if (shift > mDragLengthFactorStartPullback) { - float pullbackProgress = Utilities.getProgress(shift, - mDragLengthFactorStartPullback, mDragLengthFactor); - pullbackProgress = PULLBACK_INTERPOLATOR.getInterpolation(pullbackProgress); - shift = mDragLengthFactorStartPullback + pullbackProgress - * (mDragLengthFactorMaxPullback - mDragLengthFactorStartPullback); - } - } - - mCurrentShift.updateValue(shift); - } - public void setGestureEndCallback(Runnable gestureEndCallback) { mGestureEndCallback = gestureEndCallback; } @@ -275,6 +203,7 @@ public abstract class BaseSwipeUpHandler, Q extend RecentsAnimationTargets targets) { mRecentsAnimationController = recentsAnimationController; mRecentsAnimationTargets = targets; + mTransformParams.setTargetSet(mRecentsAnimationTargets); DeviceProfile dp = InvariantDeviceProfile.INSTANCE.get(mContext).getDeviceProfile(mContext); RemoteAnimationTargetCompat runningTaskTarget = targets.findTask( mGestureState.getRunningTaskId()); @@ -282,7 +211,8 @@ public abstract class BaseSwipeUpHandler, Q extend if (targets.minimizedHomeBounds != null && runningTaskTarget != null) { Rect overviewStackBounds = mActivityInterface .getOverviewWindowBounds(targets.minimizedHomeBounds, runningTaskTarget); - dp = dp.getMultiWindowProfile(mContext, overviewStackBounds); + dp = dp.getMultiWindowProfile(mContext, + new WindowBounds(overviewStackBounds, targets.homeContentInsets)); } else { // If we are not in multi-window mode, home insets should be same as system insets. dp = dp.copy(mContext); @@ -355,35 +285,6 @@ public abstract class BaseSwipeUpHandler, Q extend return mGestureState.getLastStartedTaskId() != -1; } - protected void initTransitionEndpoints(DeviceProfile dp) { - mDp = dp; - - mTaskViewSimulator.setDp(dp); - mTaskViewSimulator.setLayoutRotation( - mDeviceState.getCurrentActiveRotation(), - mDeviceState.getDisplayRotation()); - mTransitionDragLength = mActivityInterface.getSwipeUpDestinationAndLength( - dp, mContext, TEMP_RECT, - mTaskViewSimulator.getOrientationState().getOrientationHandler()); - - if (mDeviceState.isFullyGesturalNavMode()) { - // We can drag all the way to the top of the screen. - mDragLengthFactor = (float) dp.heightPx / mTransitionDragLength; - - float startScale = mTaskViewSimulator.getFullScreenScale(); - // Start pulling back when RecentsView scale is 0.75f, and let it go down to 0.5f. - mDragLengthFactorStartPullback = (0.75f - startScale) / (1 - startScale); - mDragLengthFactorMaxPullback = (0.5f - startScale) / (1 - startScale); - } else { - mDragLengthFactor = 1; - mDragLengthFactorStartPullback = mDragLengthFactorMaxPullback = 1; - } - - PendingAnimation pa = new PendingAnimation(mTransitionDragLength * 2); - mTaskViewSimulator.addAppToOverviewAnim(pa, t -> t * mDragLengthFactor); - mWindowTransitionController = pa.createPlaybackController(); - } - /** * Return true if the window should be translated horizontally if the recents view scrolls */ @@ -455,7 +356,6 @@ public abstract class BaseSwipeUpHandler, Q extend if (mWindowTransitionController != null) { float progress = mCurrentShift.value / mDragLengthFactor; mWindowTransitionController.setPlayFraction(progress); - mTransformParams.setTargetSet(mRecentsAnimationTargets); if (mRecentsViewScrollLinked) { mTaskViewSimulator.setScroll(mRecentsView.getScrollOffset()); @@ -464,217 +364,9 @@ public abstract class BaseSwipeUpHandler, Q extend } } - protected PagedOrientationHandler getOrientationHandler() { - return mTaskViewSimulator.getOrientationState().getOrientationHandler(); - } - - /** - * Creates an animation that transforms the current app window into the home app. - * @param startProgress The progress of {@link #mCurrentShift} to start the window from. - * @param homeAnimationFactory The home animation factory. - */ - protected RectFSpringAnim createWindowAnimationToHome(float startProgress, - HomeAnimationFactory homeAnimationFactory) { - final RectF targetRect = homeAnimationFactory.getWindowTargetRect(); - final FloatingIconView fiv = homeAnimationFactory.mIconView; - final boolean isFloatingIconView = fiv != null; - - mWindowTransitionController.setPlayFraction(startProgress / mDragLengthFactor); - mTaskViewSimulator.apply(mTransformParams - .setProgress(startProgress) - .setTargetSet(mRecentsAnimationTargets)); - RectF cropRectF = new RectF(mTaskViewSimulator.getCurrentCropRect()); - - // Matrix to map a rect in Launcher space to window space - Matrix homeToWindowPositionMap = new Matrix(); - mTaskViewSimulator.applyWindowToHomeRotation(homeToWindowPositionMap); - - final RectF startRect = new RectF(cropRectF); - mTaskViewSimulator.getCurrentMatrix().mapRect(startRect); - // Move the startRect to Launcher space as floatingIconView runs in Launcher - Matrix windowToHomePositionMap = new Matrix(); - homeToWindowPositionMap.invert(windowToHomePositionMap); - windowToHomePositionMap.mapRect(startRect); - - RectFSpringAnim anim = new RectFSpringAnim(startRect, targetRect, mContext); - if (isFloatingIconView) { - anim.addAnimatorListener(fiv); - fiv.setOnTargetChangeListener(anim::onTargetPositionChanged); - fiv.setFastFinishRunnable(anim::end); - } - - SpringAnimationRunner runner = new SpringAnimationRunner( - homeAnimationFactory, cropRectF, homeToWindowPositionMap); - anim.addOnUpdateListener(runner); - anim.addAnimatorListener(runner); - return anim; - } - public interface Factory { BaseSwipeUpHandler newHandler( GestureState gestureState, long touchTimeMs, boolean continuingLastGesture); } - - protected interface RunningWindowAnim { - void end(); - - void cancel(); - - static RunningWindowAnim wrap(Animator animator) { - return new RunningWindowAnim() { - @Override - public void end() { - animator.end(); - } - - @Override - public void cancel() { - animator.cancel(); - } - }; - } - - static RunningWindowAnim wrap(RectFSpringAnim rectFSpringAnim) { - return new RunningWindowAnim() { - @Override - public void end() { - rectFSpringAnim.end(); - } - - @Override - public void cancel() { - rectFSpringAnim.cancel(); - } - }; - } - } - - /** - * @param progress The progress of the animation to the home screen. - * @return The current alpha to set on the animating app window. - */ - protected float getWindowAlpha(float progress) { - // Alpha interpolates between [1, 0] between progress values [start, end] - final float start = 0f; - final float end = 0.85f; - - if (progress <= start) { - return 1f; - } - if (progress >= end) { - return 0f; - } - return Utilities.mapToRange(progress, start, end, 1, 0, ACCEL_1_5); - } - - protected abstract class HomeAnimationFactory { - - private FloatingIconView mIconView; - - public HomeAnimationFactory(@Nullable FloatingIconView iconView) { - mIconView = iconView; - } - - public @NonNull RectF getWindowTargetRect() { - PagedOrientationHandler orientationHandler = getOrientationHandler(); - DeviceProfile dp = mDp; - final int halfIconSize = dp.iconSizePx / 2; - float primaryDimension = orientationHandler - .getPrimaryValue(dp.availableWidthPx, dp.availableHeightPx); - float secondaryDimension = orientationHandler - .getSecondaryValue(dp.availableWidthPx, dp.availableHeightPx); - final float targetX = primaryDimension / 2f; - final float targetY = secondaryDimension - dp.hotseatBarSizePx; - // Fallback to animate to center of screen. - return new RectF(targetX - halfIconSize, targetY - halfIconSize, - targetX + halfIconSize, targetY + halfIconSize); - } - - public abstract @NonNull AnimatorPlaybackController createActivityAnimationToHome(); - - public void playAtomicAnimation(float velocity) { - // No-op - } - } - - private class SpringAnimationRunner extends AnimationSuccessListener - implements RectFSpringAnim.OnUpdateListener, BuilderProxy { - - final Rect mCropRect = new Rect(); - final Matrix mMatrix = new Matrix(); - - final RectF mWindowCurrentRect = new RectF(); - final Matrix mHomeToWindowPositionMap; - - final FloatingIconView mFIV; - final AnimatorPlaybackController mHomeAnim; - final RectF mCropRectF; - - final float mStartRadius; - final float mEndRadius; - final float mWindowAlphaThreshold; - - SpringAnimationRunner(HomeAnimationFactory factory, RectF cropRectF, - Matrix homeToWindowPositionMap) { - mHomeAnim = factory.createActivityAnimationToHome(); - mCropRectF = cropRectF; - mHomeToWindowPositionMap = homeToWindowPositionMap; - - cropRectF.roundOut(mCropRect); - mFIV = factory.mIconView; - - // End on a "round-enough" radius so that the shape reveal doesn't have to do too much - // rounding at the end of the animation. - mStartRadius = mTaskViewSimulator.getCurrentCornerRadius(); - mEndRadius = cropRectF.width() / 2f; - - // We want the window alpha to be 0 once this threshold is met, so that the - // FolderIconView can be seen morphing into the icon shape. - mWindowAlphaThreshold = mFIV != null ? 1f - SHAPE_PROGRESS_DURATION : 1f; - } - - @Override - public void onUpdate(RectF currentRect, float progress) { - mHomeAnim.setPlayFraction(progress); - mHomeToWindowPositionMap.mapRect(mWindowCurrentRect, currentRect); - - mMatrix.setRectToRect(mCropRectF, mWindowCurrentRect, ScaleToFit.FILL); - float cornerRadius = Utilities.mapRange(progress, mStartRadius, mEndRadius); - mTransformParams - .setTargetAlpha(getWindowAlpha(progress)) - .setCornerRadius(cornerRadius); - - mTransformParams.applySurfaceParams(mTransformParams.createSurfaceParams(this)); - if (mFIV != null) { - mFIV.update(currentRect, 1f, progress, - mWindowAlphaThreshold, mMatrix.mapRadius(cornerRadius), false); - } - } - - @Override - public void onBuildTargetParams( - Builder builder, RemoteAnimationTargetCompat app, TransformParams params) { - builder.withMatrix(mMatrix) - .withWindowCrop(mCropRect) - .withCornerRadius(params.getCornerRadius()); - } - - @Override - public void onCancel() { - if (mFIV != null) { - mFIV.fastFinish(); - } - } - - @Override - public void onAnimationStart(Animator animation) { - mHomeAnim.dispatchOnStart(); - } - - @Override - public void onAnimationSuccess(Animator animator) { - mHomeAnim.getAnimationPlayer().end(); - } - } } diff --git a/quickstep/recents_ui_overrides/src/com/android/quickstep/BaseSwipeUpHandlerV2.java b/quickstep/recents_ui_overrides/src/com/android/quickstep/BaseSwipeUpHandlerV2.java index 46799ff272..92e10b14f9 100644 --- a/quickstep/recents_ui_overrides/src/com/android/quickstep/BaseSwipeUpHandlerV2.java +++ b/quickstep/recents_ui_overrides/src/com/android/quickstep/BaseSwipeUpHandlerV2.java @@ -29,6 +29,7 @@ import static com.android.quickstep.GestureState.GestureEndTarget.LAST_TASK; import static com.android.quickstep.GestureState.GestureEndTarget.NEW_TASK; import static com.android.quickstep.GestureState.GestureEndTarget.RECENTS; import static com.android.quickstep.GestureState.STATE_END_TARGET_ANIMATION_FINISHED; +import static com.android.quickstep.GestureState.STATE_END_TARGET_SET; import static com.android.quickstep.GestureState.STATE_RECENTS_SCROLLING_FINISHED; import static com.android.quickstep.MultiStateCallback.DEBUG_STATES; import static com.android.quickstep.SysUINavigationMode.Mode.TWO_BUTTONS; @@ -253,6 +254,10 @@ public abstract class BaseSwipeUpHandlerV2, Q exte mStateCallback.runOnceAtState(STATE_HANDLER_INVALIDATED | STATE_RESUME_LAST_TASK, this::notifyTransitionCancelled); + mGestureState.runOnceAtState(STATE_END_TARGET_SET, + () -> mDeviceState.onEndTargetCalculated(mGestureState.getEndTarget(), + mActivityInterface)); + if (!ENABLE_QUICKSTEP_LIVE_TILE.get()) { mStateCallback.addChangeListener(STATE_APP_CONTROLLER_RECEIVED | STATE_LAUNCHER_PRESENT | STATE_SCREENSHOT_VIEW_SHOWN | STATE_CAPTURE_SCREENSHOT, @@ -1035,7 +1040,7 @@ public abstract class BaseSwipeUpHandlerV2, Q exte } // Make sure recents is in its final state maybeUpdateRecentsAttachedState(false); - mActivityInterface.onSwipeUpToHomeComplete(); + mActivityInterface.onSwipeUpToHomeComplete(mDeviceState); } }); return anim; @@ -1232,7 +1237,6 @@ public abstract class BaseSwipeUpHandlerV2, Q exte } ActiveGestureLog.INSTANCE.addLog("finishRecentsAnimation", true); doLogGesture(HOME); - mDeviceState.enableMultipleRegions(false); } protected abstract void finishRecentsControllerToHome(Runnable callback); @@ -1248,7 +1252,6 @@ public abstract class BaseSwipeUpHandlerV2, Q exte SystemUiProxy.INSTANCE.get(mContext).onOverviewShown(false, TAG); doLogGesture(RECENTS); - mDeviceState.onSwipeUpToOverview(mActivityInterface); reset(); } diff --git a/quickstep/recents_ui_overrides/src/com/android/quickstep/FallbackActivityInterface.java b/quickstep/recents_ui_overrides/src/com/android/quickstep/FallbackActivityInterface.java index c9ff88409a..70be3ab8af 100644 --- a/quickstep/recents_ui_overrides/src/com/android/quickstep/FallbackActivityInterface.java +++ b/quickstep/recents_ui_overrides/src/com/android/quickstep/FallbackActivityInterface.java @@ -69,7 +69,7 @@ public final class FallbackActivityInterface extends /** 4 */ @Override - public void onSwipeUpToHomeComplete() { + public void onSwipeUpToHomeComplete(RecentsAnimationDeviceState deviceState) { onSwipeUpToRecentsComplete(); } diff --git a/quickstep/recents_ui_overrides/src/com/android/quickstep/LauncherActivityInterface.java b/quickstep/recents_ui_overrides/src/com/android/quickstep/LauncherActivityInterface.java index 13b84e036e..62eb235251 100644 --- a/quickstep/recents_ui_overrides/src/com/android/quickstep/LauncherActivityInterface.java +++ b/quickstep/recents_ui_overrides/src/com/android/quickstep/LauncherActivityInterface.java @@ -96,7 +96,7 @@ public final class LauncherActivityInterface extends } @Override - public void onSwipeUpToHomeComplete() { + public void onSwipeUpToHomeComplete(RecentsAnimationDeviceState deviceState) { Launcher launcher = getCreatedActivity(); if (launcher == null) { return; @@ -105,6 +105,7 @@ public final class LauncherActivityInterface extends // recents, we assume the first task is invisible, making translation off by one task. launcher.getStateManager().reapplyState(); launcher.getRootView().setForceHideBackArrow(false); + notifyRecentsOfOrientation(deviceState); } @Override @@ -235,17 +236,20 @@ public final class LauncherActivityInterface extends // Are we going from Recents to Workspace? if (toState == LauncherState.NORMAL) { exitRunnable.run(); - - // reset layout on swipe to home - RecentsView recentsView = getCreatedActivity().getOverviewPanel(); - recentsView.setLayoutRotation(deviceState.getCurrentActiveRotation(), - deviceState.getDisplayRotation()); + notifyRecentsOfOrientation(deviceState); stateManager.removeStateListener(this); } } }); } + private void notifyRecentsOfOrientation(RecentsAnimationDeviceState deviceState) { + // reset layout on swipe to home + RecentsView recentsView = getCreatedActivity().getOverviewPanel(); + recentsView.setLayoutRotation(deviceState.getCurrentActiveRotation(), + deviceState.getDisplayRotation()); + } + @Override public Rect getOverviewWindowBounds(Rect homeBounds, RemoteAnimationTargetCompat target) { return homeBounds; diff --git a/quickstep/recents_ui_overrides/src/com/android/quickstep/QuickstepTestInformationHandler.java b/quickstep/recents_ui_overrides/src/com/android/quickstep/QuickstepTestInformationHandler.java index 0d49b2b500..a28dabc6a3 100644 --- a/quickstep/recents_ui_overrides/src/com/android/quickstep/QuickstepTestInformationHandler.java +++ b/quickstep/recents_ui_overrides/src/com/android/quickstep/QuickstepTestInformationHandler.java @@ -79,12 +79,13 @@ public class QuickstepTestInformationHandler extends TestInformationHandler { @Override protected Activity getCurrentActivity() { - OverviewComponentObserver observer = new OverviewComponentObserver(mContext, - new RecentsAnimationDeviceState(mContext)); + RecentsAnimationDeviceState rads = new RecentsAnimationDeviceState(mContext); + OverviewComponentObserver observer = new OverviewComponentObserver(mContext, rads); try { return observer.getActivityInterface().getCreatedActivity(); } finally { observer.onDestroy(); + rads.destroy(); } } diff --git a/quickstep/recents_ui_overrides/src/com/android/quickstep/SwipeUpAnimationLogic.java b/quickstep/recents_ui_overrides/src/com/android/quickstep/SwipeUpAnimationLogic.java new file mode 100644 index 0000000000..b17730ba6d --- /dev/null +++ b/quickstep/recents_ui_overrides/src/com/android/quickstep/SwipeUpAnimationLogic.java @@ -0,0 +1,350 @@ +/* + * Copyright (C) 2020 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.android.quickstep; + +import static com.android.launcher3.anim.Interpolators.ACCEL_1_5; +import static com.android.launcher3.anim.Interpolators.DEACCEL; +import static com.android.launcher3.views.FloatingIconView.SHAPE_PROGRESS_DURATION; + +import android.animation.Animator; +import android.content.Context; +import android.graphics.Matrix; +import android.graphics.Matrix.ScaleToFit; +import android.graphics.Rect; +import android.graphics.RectF; +import android.view.animation.Interpolator; + +import androidx.annotation.NonNull; +import androidx.annotation.Nullable; +import androidx.annotation.UiThread; + +import com.android.launcher3.DeviceProfile; +import com.android.launcher3.Utilities; +import com.android.launcher3.anim.AnimationSuccessListener; +import com.android.launcher3.anim.AnimatorPlaybackController; +import com.android.launcher3.anim.PendingAnimation; +import com.android.launcher3.touch.PagedOrientationHandler; +import com.android.launcher3.views.FloatingIconView; +import com.android.quickstep.util.RectFSpringAnim; +import com.android.quickstep.util.TaskViewSimulator; +import com.android.quickstep.util.TransformParams; +import com.android.quickstep.util.TransformParams.BuilderProxy; +import com.android.systemui.shared.system.RemoteAnimationTargetCompat; +import com.android.systemui.shared.system.SyncRtSurfaceTransactionApplierCompat.SurfaceParams.Builder; + +public abstract class SwipeUpAnimationLogic { + + protected static final Rect TEMP_RECT = new Rect(); + private static final Interpolator PULLBACK_INTERPOLATOR = DEACCEL; + + protected DeviceProfile mDp; + + protected final Context mContext; + protected final RecentsAnimationDeviceState mDeviceState; + protected final GestureState mGestureState; + protected final TaskViewSimulator mTaskViewSimulator; + + protected final TransformParams mTransformParams; + + // Shift in the range of [0, 1]. + // 0 => preview snapShot is completely visible, and hotseat is completely translated down + // 1 => preview snapShot is completely aligned with the recents view and hotseat is completely + // visible. + protected final AnimatedFloat mCurrentShift = new AnimatedFloat(this::updateFinalShift); + + // The distance needed to drag to reach the task size in recents. + protected int mTransitionDragLength; + // How much further we can drag past recents, as a factor of mTransitionDragLength. + protected float mDragLengthFactor = 1; + // Start resisting when swiping past this factor of mTransitionDragLength. + private float mDragLengthFactorStartPullback = 1f; + // This is how far down we can scale down, where 0f is full screen and 1f is recents. + private float mDragLengthFactorMaxPullback = 1f; + + protected AnimatorPlaybackController mWindowTransitionController; + + public SwipeUpAnimationLogic(Context context, RecentsAnimationDeviceState deviceState, + GestureState gestureState, TransformParams transformParams) { + mContext = context; + mDeviceState = deviceState; + mGestureState = gestureState; + mTaskViewSimulator = new TaskViewSimulator(context, gestureState.getActivityInterface()); + mTransformParams = transformParams; + } + + protected void initTransitionEndpoints(DeviceProfile dp) { + mDp = dp; + + mTaskViewSimulator.setDp(dp); + mTaskViewSimulator.setLayoutRotation( + mDeviceState.getCurrentActiveRotation(), + mDeviceState.getDisplayRotation()); + mTransitionDragLength = mGestureState.getActivityInterface().getSwipeUpDestinationAndLength( + dp, mContext, TEMP_RECT, + mTaskViewSimulator.getOrientationState().getOrientationHandler()); + + if (mDeviceState.isFullyGesturalNavMode()) { + // We can drag all the way to the top of the screen. + mDragLengthFactor = (float) dp.heightPx / mTransitionDragLength; + + float startScale = mTaskViewSimulator.getFullScreenScale(); + // Start pulling back when RecentsView scale is 0.75f, and let it go down to 0.5f. + mDragLengthFactorStartPullback = (0.75f - startScale) / (1 - startScale); + mDragLengthFactorMaxPullback = (0.5f - startScale) / (1 - startScale); + } else { + mDragLengthFactor = 1; + mDragLengthFactorStartPullback = mDragLengthFactorMaxPullback = 1; + } + + PendingAnimation pa = new PendingAnimation(mTransitionDragLength * 2); + mTaskViewSimulator.addAppToOverviewAnim(pa, t -> t * mDragLengthFactor); + mWindowTransitionController = pa.createPlaybackController(); + } + + @UiThread + public void updateDisplacement(float displacement) { + // We are moving in the negative x/y direction + displacement = -displacement; + float shift; + if (displacement > mTransitionDragLength * mDragLengthFactor && mTransitionDragLength > 0) { + shift = mDragLengthFactor; + } else { + float translation = Math.max(displacement, 0); + shift = mTransitionDragLength == 0 ? 0 : translation / mTransitionDragLength; + if (shift > mDragLengthFactorStartPullback) { + float pullbackProgress = Utilities.getProgress(shift, + mDragLengthFactorStartPullback, mDragLengthFactor); + pullbackProgress = PULLBACK_INTERPOLATOR.getInterpolation(pullbackProgress); + shift = mDragLengthFactorStartPullback + pullbackProgress + * (mDragLengthFactorMaxPullback - mDragLengthFactorStartPullback); + } + } + + mCurrentShift.updateValue(shift); + } + + /** + * Called when the value of {@link #mCurrentShift} changes + */ + @UiThread + public abstract void updateFinalShift(); + + protected PagedOrientationHandler getOrientationHandler() { + return mTaskViewSimulator.getOrientationState().getOrientationHandler(); + } + + protected abstract class HomeAnimationFactory { + + public FloatingIconView mIconView; + + public HomeAnimationFactory(@Nullable FloatingIconView iconView) { + mIconView = iconView; + } + + public @NonNull RectF getWindowTargetRect() { + PagedOrientationHandler orientationHandler = getOrientationHandler(); + DeviceProfile dp = mDp; + final int halfIconSize = dp.iconSizePx / 2; + float primaryDimension = orientationHandler + .getPrimaryValue(dp.availableWidthPx, dp.availableHeightPx); + float secondaryDimension = orientationHandler + .getSecondaryValue(dp.availableWidthPx, dp.availableHeightPx); + final float targetX = primaryDimension / 2f; + final float targetY = secondaryDimension - dp.hotseatBarSizePx; + // Fallback to animate to center of screen. + return new RectF(targetX - halfIconSize, targetY - halfIconSize, + targetX + halfIconSize, targetY + halfIconSize); + } + + public abstract @NonNull AnimatorPlaybackController createActivityAnimationToHome(); + + public void playAtomicAnimation(float velocity) { + // No-op + } + } + + /** + * Creates an animation that transforms the current app window into the home app. + * @param startProgress The progress of {@link #mCurrentShift} to start the window from. + * @param homeAnimationFactory The home animation factory. + */ + protected RectFSpringAnim createWindowAnimationToHome(float startProgress, + HomeAnimationFactory homeAnimationFactory) { + final RectF targetRect = homeAnimationFactory.getWindowTargetRect(); + final FloatingIconView fiv = homeAnimationFactory.mIconView; + final boolean isFloatingIconView = fiv != null; + + mWindowTransitionController.setPlayFraction(startProgress / mDragLengthFactor); + mTaskViewSimulator.apply(mTransformParams.setProgress(startProgress)); + RectF cropRectF = new RectF(mTaskViewSimulator.getCurrentCropRect()); + + // Matrix to map a rect in Launcher space to window space + Matrix homeToWindowPositionMap = new Matrix(); + mTaskViewSimulator.applyWindowToHomeRotation(homeToWindowPositionMap); + + final RectF startRect = new RectF(cropRectF); + mTaskViewSimulator.getCurrentMatrix().mapRect(startRect); + // Move the startRect to Launcher space as floatingIconView runs in Launcher + Matrix windowToHomePositionMap = new Matrix(); + homeToWindowPositionMap.invert(windowToHomePositionMap); + windowToHomePositionMap.mapRect(startRect); + + RectFSpringAnim anim = new RectFSpringAnim(startRect, targetRect, mContext); + if (isFloatingIconView) { + anim.addAnimatorListener(fiv); + fiv.setOnTargetChangeListener(anim::onTargetPositionChanged); + fiv.setFastFinishRunnable(anim::end); + } + + SpringAnimationRunner runner = new SpringAnimationRunner( + homeAnimationFactory, cropRectF, homeToWindowPositionMap); + anim.addOnUpdateListener(runner); + anim.addAnimatorListener(runner); + return anim; + } + + /** + * @param progress The progress of the animation to the home screen. + * @return The current alpha to set on the animating app window. + */ + protected float getWindowAlpha(float progress) { + // Alpha interpolates between [1, 0] between progress values [start, end] + final float start = 0f; + final float end = 0.85f; + + if (progress <= start) { + return 1f; + } + if (progress >= end) { + return 0f; + } + return Utilities.mapToRange(progress, start, end, 1, 0, ACCEL_1_5); + } + + protected class SpringAnimationRunner extends AnimationSuccessListener + implements RectFSpringAnim.OnUpdateListener, BuilderProxy { + + final Rect mCropRect = new Rect(); + final Matrix mMatrix = new Matrix(); + + final RectF mWindowCurrentRect = new RectF(); + final Matrix mHomeToWindowPositionMap; + + final FloatingIconView mFIV; + final AnimatorPlaybackController mHomeAnim; + final RectF mCropRectF; + + final float mStartRadius; + final float mEndRadius; + final float mWindowAlphaThreshold; + + SpringAnimationRunner(HomeAnimationFactory factory, RectF cropRectF, + Matrix homeToWindowPositionMap) { + mHomeAnim = factory.createActivityAnimationToHome(); + mCropRectF = cropRectF; + mHomeToWindowPositionMap = homeToWindowPositionMap; + + cropRectF.roundOut(mCropRect); + mFIV = factory.mIconView; + + // End on a "round-enough" radius so that the shape reveal doesn't have to do too much + // rounding at the end of the animation. + mStartRadius = mTaskViewSimulator.getCurrentCornerRadius(); + mEndRadius = cropRectF.width() / 2f; + + // We want the window alpha to be 0 once this threshold is met, so that the + // FolderIconView can be seen morphing into the icon shape. + mWindowAlphaThreshold = mFIV != null ? 1f - SHAPE_PROGRESS_DURATION : 1f; + } + + @Override + public void onUpdate(RectF currentRect, float progress) { + mHomeAnim.setPlayFraction(progress); + mHomeToWindowPositionMap.mapRect(mWindowCurrentRect, currentRect); + + mMatrix.setRectToRect(mCropRectF, mWindowCurrentRect, ScaleToFit.FILL); + float cornerRadius = Utilities.mapRange(progress, mStartRadius, mEndRadius); + mTransformParams + .setTargetAlpha(getWindowAlpha(progress)) + .setCornerRadius(cornerRadius); + + mTransformParams.applySurfaceParams(mTransformParams.createSurfaceParams(this)); + if (mFIV != null) { + mFIV.update(currentRect, 1f, progress, + mWindowAlphaThreshold, mMatrix.mapRadius(cornerRadius), false); + } + } + + @Override + public void onBuildTargetParams( + Builder builder, RemoteAnimationTargetCompat app, TransformParams params) { + builder.withMatrix(mMatrix) + .withWindowCrop(mCropRect) + .withCornerRadius(params.getCornerRadius()); + } + + @Override + public void onCancel() { + if (mFIV != null) { + mFIV.fastFinish(); + } + } + + @Override + public void onAnimationStart(Animator animation) { + mHomeAnim.dispatchOnStart(); + } + + @Override + public void onAnimationSuccess(Animator animator) { + mHomeAnim.getAnimationPlayer().end(); + } + } + + public interface RunningWindowAnim { + void end(); + + void cancel(); + + static RunningWindowAnim wrap(Animator animator) { + return new RunningWindowAnim() { + @Override + public void end() { + animator.end(); + } + + @Override + public void cancel() { + animator.cancel(); + } + }; + } + + static RunningWindowAnim wrap(RectFSpringAnim rectFSpringAnim) { + return new RunningWindowAnim() { + @Override + public void end() { + rectFSpringAnim.end(); + } + + @Override + public void cancel() { + rectFSpringAnim.cancel(); + } + }; + } + } +} diff --git a/quickstep/recents_ui_overrides/src/com/android/quickstep/TouchInteractionService.java b/quickstep/recents_ui_overrides/src/com/android/quickstep/TouchInteractionService.java index c4d4ab850a..c8619bdd12 100644 --- a/quickstep/recents_ui_overrides/src/com/android/quickstep/TouchInteractionService.java +++ b/quickstep/recents_ui_overrides/src/com/android/quickstep/TouchInteractionService.java @@ -82,6 +82,7 @@ import com.android.quickstep.inputconsumers.OverviewInputConsumer; import com.android.quickstep.inputconsumers.OverviewWithoutFocusInputConsumer; import com.android.quickstep.inputconsumers.ResetGestureInputConsumer; import com.android.quickstep.inputconsumers.ScreenPinnedInputConsumer; +import com.android.quickstep.inputconsumers.SysUiOverlayInputConsumer; import com.android.quickstep.util.ActiveGestureLog; import com.android.quickstep.util.AssistantUtilities; import com.android.quickstep.util.ProtoTracer; @@ -595,6 +596,13 @@ public class TouchInteractionService extends Service implements PluginListenergetOverviewPanel().startHome(); } + + @Override + public void onSwipeUpCancelled() {} } diff --git a/quickstep/recents_ui_overrides/src/com/android/quickstep/inputconsumers/OtherActivityInputConsumer.java b/quickstep/recents_ui_overrides/src/com/android/quickstep/inputconsumers/OtherActivityInputConsumer.java index 8b08ea76fe..14215a1f67 100644 --- a/quickstep/recents_ui_overrides/src/com/android/quickstep/inputconsumers/OtherActivityInputConsumer.java +++ b/quickstep/recents_ui_overrides/src/com/android/quickstep/inputconsumers/OtherActivityInputConsumer.java @@ -403,6 +403,11 @@ public class OtherActivityInputConsumer extends ContextWrapper implements InputC TraceHelper.INSTANCE.endSection(traceToken); } + @Override + public void notifyOrientationSetup() { + mDeviceState.onStartGesture(); + } + @Override public void onConsumerAboutToBeSwitched() { Preconditions.assertUIThread(); diff --git a/quickstep/recents_ui_overrides/src/com/android/quickstep/inputconsumers/OverviewWithoutFocusInputConsumer.java b/quickstep/recents_ui_overrides/src/com/android/quickstep/inputconsumers/OverviewWithoutFocusInputConsumer.java index ac1c3a8742..4440a04bea 100644 --- a/quickstep/recents_ui_overrides/src/com/android/quickstep/inputconsumers/OverviewWithoutFocusInputConsumer.java +++ b/quickstep/recents_ui_overrides/src/com/android/quickstep/inputconsumers/OverviewWithoutFocusInputConsumer.java @@ -17,6 +17,7 @@ package com.android.quickstep.inputconsumers; import android.content.Context; import android.content.Intent; +import android.graphics.PointF; import android.view.MotionEvent; import com.android.launcher3.BaseActivity; @@ -33,7 +34,8 @@ import com.android.quickstep.util.ActiveGestureLog; import com.android.quickstep.util.TriggerSwipeUpTouchTracker; import com.android.systemui.shared.system.InputMonitorCompat; -public class OverviewWithoutFocusInputConsumer implements InputConsumer { +public class OverviewWithoutFocusInputConsumer implements InputConsumer, + TriggerSwipeUpTouchTracker.OnSwipeUpListener { private final Context mContext; private final InputMonitorCompat mInputMonitor; @@ -45,7 +47,7 @@ public class OverviewWithoutFocusInputConsumer implements InputConsumer { mContext = context; mInputMonitor = inputMonitor; mTriggerSwipeUpTracker = new TriggerSwipeUpTouchTracker(context, disableHorizontalSwipe, - deviceState.getNavBarPosition(), this::onInterceptTouch, this::onSwipeUp); + deviceState.getNavBarPosition(), this::onInterceptTouch, this); } @Override @@ -70,7 +72,8 @@ public class OverviewWithoutFocusInputConsumer implements InputConsumer { } } - private void onSwipeUp(boolean wasFling) { + @Override + public void onSwipeUp(boolean wasFling, PointF finalVelocity) { mContext.startActivity(new Intent(Intent.ACTION_MAIN) .addCategory(Intent.CATEGORY_HOME) .setFlags(Intent.FLAG_ACTIVITY_NEW_TASK)); @@ -83,4 +86,7 @@ public class OverviewWithoutFocusInputConsumer implements InputConsumer { wasFling ? Touch.FLING : Touch.SWIPE, Direction.UP, containerType, pageIndex); activity.getUserEventDispatcher().setPreviousHomeGesture(true); } + + @Override + public void onSwipeUpCancelled() {} } diff --git a/quickstep/recents_ui_overrides/src/com/android/quickstep/inputconsumers/SysUiOverlayInputConsumer.java b/quickstep/recents_ui_overrides/src/com/android/quickstep/inputconsumers/SysUiOverlayInputConsumer.java new file mode 100644 index 0000000000..3f833c0a6d --- /dev/null +++ b/quickstep/recents_ui_overrides/src/com/android/quickstep/inputconsumers/SysUiOverlayInputConsumer.java @@ -0,0 +1,86 @@ +/* + * Copyright (C) 2020 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.android.quickstep.inputconsumers; + +import android.content.Context; +import android.content.Intent; +import android.graphics.PointF; +import android.view.MotionEvent; + +import com.android.launcher3.testing.TestLogging; +import com.android.launcher3.testing.TestProtocol; +import com.android.quickstep.InputConsumer; +import com.android.quickstep.RecentsAnimationDeviceState; +import com.android.quickstep.util.TriggerSwipeUpTouchTracker; +import com.android.systemui.shared.system.InputMonitorCompat; + +/** + * Input consumer used when a fullscreen System UI overlay is showing (such as the expanded Bubbles + * UI). + * + * This responds to swipes up by sending a closeSystemDialogs broadcast (causing overlays to close) + * rather than closing the app behind the overlay and sending the user all the way home. + */ +public class SysUiOverlayInputConsumer implements InputConsumer, + TriggerSwipeUpTouchTracker.OnSwipeUpListener { + + private final Context mContext; + private final InputMonitorCompat mInputMonitor; + private final TriggerSwipeUpTouchTracker mTriggerSwipeUpTracker; + + public SysUiOverlayInputConsumer( + Context context, + RecentsAnimationDeviceState deviceState, + InputMonitorCompat inputMonitor) { + mContext = context; + mInputMonitor = inputMonitor; + mTriggerSwipeUpTracker = new TriggerSwipeUpTouchTracker(context, true, + deviceState.getNavBarPosition(), this::onInterceptTouch, this); + } + + @Override + public int getType() { + return TYPE_SYSUI_OVERLAY; + } + + @Override + public boolean allowInterceptByParent() { + return !mTriggerSwipeUpTracker.interceptedTouch(); + } + + @Override + public void onMotionEvent(MotionEvent ev) { + mTriggerSwipeUpTracker.onMotionEvent(ev); + } + + private void onInterceptTouch() { + if (mInputMonitor != null) { + TestLogging.recordEvent(TestProtocol.SEQUENCE_PILFER, "pilferPointers"); + mInputMonitor.pilferPointers(); + } + } + + @Override + public void onSwipeUp(boolean wasFling, PointF finalVelocity) { + // Close system dialogs when a swipe up is detected. + mContext.sendBroadcast(new Intent(Intent.ACTION_CLOSE_SYSTEM_DIALOGS)); + } + + @Override + public void onSwipeUpCancelled() { + + } +} diff --git a/quickstep/recents_ui_overrides/src/com/android/quickstep/util/TaskViewSimulator.java b/quickstep/recents_ui_overrides/src/com/android/quickstep/util/TaskViewSimulator.java index 3b08675885..3c9762be6d 100644 --- a/quickstep/recents_ui_overrides/src/com/android/quickstep/util/TaskViewSimulator.java +++ b/quickstep/recents_ui_overrides/src/com/android/quickstep/util/TaskViewSimulator.java @@ -23,6 +23,7 @@ import static com.android.systemui.shared.system.WindowManagerWrapper.WINDOWING_ import android.animation.TimeInterpolator; import android.content.Context; import android.graphics.Matrix; +import android.graphics.Point; import android.graphics.PointF; import android.graphics.Rect; import android.graphics.RectF; @@ -74,7 +75,7 @@ public class TaskViewSimulator implements TransformParams.BuilderProxy { private DeviceProfile mDp; private final Matrix mMatrix = new Matrix(); - private RemoteAnimationTargetCompat mRunningTarget; + private final Point mRunningTargetWindowPosition = new Point(); // Thumbnail view properties private final Rect mThumbnailPosition = new Rect(); @@ -139,13 +140,19 @@ public class TaskViewSimulator implements TransformParams.BuilderProxy { * Sets the targets which the simulator will control */ public void setPreview(RemoteAnimationTargetCompat runningTarget) { - mRunningTarget = runningTarget; + setPreviewBounds(runningTarget.screenSpaceBounds, runningTarget.contentInsets); + mRunningTargetWindowPosition.set(runningTarget.position.x, runningTarget.position.y); + } - mThumbnailData.insets.set(mRunningTarget.contentInsets); + /** + * Sets the targets which the simulator will control + */ + public void setPreviewBounds(Rect bounds, Rect insets) { + mThumbnailData.insets.set(insets); // TODO: What is this? mThumbnailData.windowingMode = WINDOWING_MODE_FULLSCREEN; - mThumbnailPosition.set(runningTarget.screenSpaceBounds); + mThumbnailPosition.set(bounds); mLayoutValid = false; } @@ -199,16 +206,14 @@ public class TaskViewSimulator implements TransformParams.BuilderProxy { postDisplayRotation(deltaRotation( mOrientationState.getLauncherRotation(), mOrientationState.getDisplayRotation()), mDp.widthPx, mDp.heightPx, matrix); - if (mRunningTarget != null) { - matrix.postTranslate(-mRunningTarget.position.x, -mRunningTarget.position.y); - } + matrix.postTranslate(-mRunningTargetWindowPosition.x, -mRunningTargetWindowPosition.y); } /** * Applies the target to the previously set parameters */ public void apply(TransformParams params) { - if (mDp == null || mRunningTarget == null) { + if (mDp == null || mThumbnailPosition.isEmpty()) { return; } if (!mLayoutValid) { diff --git a/quickstep/recents_ui_overrides/src/com/android/quickstep/util/TriggerSwipeUpTouchTracker.java b/quickstep/recents_ui_overrides/src/com/android/quickstep/util/TriggerSwipeUpTouchTracker.java index c71258b87b..29b95589f3 100644 --- a/quickstep/recents_ui_overrides/src/com/android/quickstep/util/TriggerSwipeUpTouchTracker.java +++ b/quickstep/recents_ui_overrides/src/com/android/quickstep/util/TriggerSwipeUpTouchTracker.java @@ -149,8 +149,12 @@ public class TriggerSwipeUpTouchTracker { isSwipeUp = squaredHypot(displacementX, displacementY) >= mSquaredTouchSlop; } - if (isSwipeUp && mOnSwipeUp != null) { - mOnSwipeUp.onSwipeUp(wasFling); + if (mOnSwipeUp != null) { + if (isSwipeUp) { + mOnSwipeUp.onSwipeUp(wasFling, new PointF(velocityX, velocityY)); + } else { + mOnSwipeUp.onSwipeUpCancelled(); + } } } @@ -161,7 +165,11 @@ public class TriggerSwipeUpTouchTracker { /** * Called on touch up if a swipe up was detected. * @param wasFling Whether the swipe was a fling, or just passed touch slop at low velocity. + * @param finalVelocity The final velocity of the swipe. */ - void onSwipeUp(boolean wasFling); + void onSwipeUp(boolean wasFling, PointF finalVelocity); + + /** Called on touch up if a swipe up was not detected. */ + void onSwipeUpCancelled(); } } diff --git a/quickstep/recents_ui_overrides/src/com/android/quickstep/views/OverviewActionsView.java b/quickstep/recents_ui_overrides/src/com/android/quickstep/views/OverviewActionsView.java index ea33d007f4..83287c463a 100644 --- a/quickstep/recents_ui_overrides/src/com/android/quickstep/views/OverviewActionsView.java +++ b/quickstep/recents_ui_overrides/src/com/android/quickstep/views/OverviewActionsView.java @@ -45,8 +45,6 @@ import java.lang.annotation.RetentionPolicy; public class OverviewActionsView extends FrameLayout implements OnClickListener { - public static final long VISIBILITY_TRANSITION_DURATION_MS = 80; - @IntDef(flag = true, value = { HIDDEN_UNSUPPORTED_NAVIGATION, HIDDEN_DISABLED_FEATURE, diff --git a/quickstep/recents_ui_overrides/src/com/android/quickstep/views/RecentsView.java b/quickstep/recents_ui_overrides/src/com/android/quickstep/views/RecentsView.java index a506b7eb0c..324aaecf0e 100644 --- a/quickstep/recents_ui_overrides/src/com/android/quickstep/views/RecentsView.java +++ b/quickstep/recents_ui_overrides/src/com/android/quickstep/views/RecentsView.java @@ -1039,6 +1039,11 @@ public abstract class RecentsView extends PagedView impl } private void animateRecentsRotationInPlace(int newRotation) { + if (mOrientationState.canLauncherRotate()) { + // Update the rotation but let system take care of the rotation animation + setLayoutRotation(newRotation, mOrientationState.getDisplayRotation()); + return; + } AnimatorSet pa = setRecentsChangedOrientation(true); pa.addListener(AnimationSuccessListener.forRunnable(() -> { setLayoutRotation(newRotation, mOrientationState.getDisplayRotation()); diff --git a/quickstep/res/layout/gesture_tutorial_fragment.xml b/quickstep/res/layout/gesture_tutorial_fragment.xml index 190290ebb4..74197bef40 100644 --- a/quickstep/res/layout/gesture_tutorial_fragment.xml +++ b/quickstep/res/layout/gesture_tutorial_fragment.xml @@ -24,6 +24,13 @@ android:layout_height="match_parent" android:background="@drawable/gesture_tutorial_ripple"/> + + Close - - - Easily access your most-used apps - - Pixel predicts apps you\’ll need next, right on your Home screen. Tap to set up. - Get app suggestions on the bottom row of your Home screen diff --git a/quickstep/src/com/android/launcher3/statehandlers/DepthController.java b/quickstep/src/com/android/launcher3/statehandlers/DepthController.java index 8292a92af6..fcffaedfd8 100644 --- a/quickstep/src/com/android/launcher3/statehandlers/DepthController.java +++ b/quickstep/src/com/android/launcher3/statehandlers/DepthController.java @@ -102,11 +102,30 @@ public class DepthController implements StateHandler { */ private float mDepth; + private View.OnAttachStateChangeListener mOnAttachListener; + public DepthController(Launcher l) { mLauncher = l; } private void ensureDependencies() { + if (mLauncher.getRootView() != null && mOnAttachListener == null) { + mOnAttachListener = new View.OnAttachStateChangeListener() { + @Override + public void onViewAttachedToWindow(View view) { + // To handle the case where window token is invalid during last setDepth call. + IBinder windowToken = mLauncher.getRootView().getWindowToken(); + if (windowToken != null) { + mWallpaperManager.setWallpaperZoomOut(windowToken, mDepth); + } + } + + @Override + public void onViewDetachedFromWindow(View view) { + } + }; + mLauncher.getRootView().addOnAttachStateChangeListener(mOnAttachListener); + } if (mWallpaperManager != null) { return; } @@ -184,10 +203,10 @@ public class DepthController implements StateHandler { return; } - mDepth = depthF; if (mSurface == null || !mSurface.isValid()) { return; } + mDepth = depthF; ensureDependencies(); IBinder windowToken = mLauncher.getRootView().getWindowToken(); if (windowToken != null) { diff --git a/quickstep/src/com/android/quickstep/BaseActivityInterface.java b/quickstep/src/com/android/quickstep/BaseActivityInterface.java index 91249250f5..7122647e6f 100644 --- a/quickstep/src/com/android/quickstep/BaseActivityInterface.java +++ b/quickstep/src/com/android/quickstep/BaseActivityInterface.java @@ -101,7 +101,7 @@ public abstract class BaseActivityInterface TYPE_NO_OP; @@ -71,6 +73,11 @@ public interface InputConsumer { return false; } + /** + * Handle and specific setup necessary based on the orientation of the device + */ + default void notifyOrientationSetup() {} + /** * Returns the active input consumer is in the hierarchy of this input consumer. */ diff --git a/quickstep/src/com/android/quickstep/OrientationTouchTransformer.java b/quickstep/src/com/android/quickstep/OrientationTouchTransformer.java index 6b94a79000..b59f0118af 100644 --- a/quickstep/src/com/android/quickstep/OrientationTouchTransformer.java +++ b/quickstep/src/com/android/quickstep/OrientationTouchTransformer.java @@ -67,6 +67,14 @@ class OrientationTouchTransformer { private boolean mEnableMultipleRegions; private Resources mResources; private OrientationRectF mLastRectTouched; + /** + * The rotation of the last touched nav bar. Derived from {@link #mLastRectTouched}, but has a + * longer lifetime than the rect. Note this is different than {@link #mQuickStepStartingRotation} + * as it always updates its value on every touch whereas mQuickstepStartingRotation only + * updates when device rotation matches touch rotation. Maybe this will be only one necessary + * after TODO(b/154580671) is in. TBD. + */ + private int mLastRectRotation; private SysUINavigationMode.Mode mMode; private QuickStepContractInfo mContractInfo; @@ -143,15 +151,18 @@ class OrientationTouchTransformer { * ALSO, you BETTER call this with {@param enableMultipleRegions} set to false once you're done. * * @param enableMultipleRegions Set to true to start tracking multiple nav bar regions - * @param info The current displayInfo + * @param info The current displayInfo which will be the start of the quickswitch gesture */ void enableMultipleRegions(boolean enableMultipleRegions, DefaultDisplay.Info info) { mEnableMultipleRegions = enableMultipleRegions && mMode != SysUINavigationMode.Mode.TWO_BUTTONS; - if (!enableMultipleRegions) { + if (mEnableMultipleRegions) { + mQuickStepStartingRotation = info.rotation; + } else { + mLastRectRotation = 0; mQuickStepStartingRotation = QUICKSTEP_ROTATION_UNINITIALIZED; - resetSwipeRegions(info); } + resetSwipeRegions(info); } /** @@ -248,11 +259,7 @@ class OrientationTouchTransformer { } int getCurrentActiveRotation() { - if (mLastRectTouched == null) { - return 0; - } else { - return mLastRectTouched.mRotation; - } + return mLastRectRotation; } int getQuickStepStartingRotation() { @@ -291,7 +298,9 @@ class OrientationTouchTransformer { } if (rect.applyTransform(event, false)) { mLastRectTouched = rect; - if (mCurrentDisplayRotation == mLastRectTouched.mRotation) { + mLastRectRotation = rect.mRotation; + if (mEnableMultipleRegions && mCurrentDisplayRotation == mLastRectRotation) { + // TODO(b/154580671) might make this block unnecessary // Start a touch session for the default nav region for the display mQuickStepStartingRotation = mLastRectTouched.mRotation; resetSwipeRegions(); diff --git a/quickstep/src/com/android/quickstep/RecentTasksList.java b/quickstep/src/com/android/quickstep/RecentTasksList.java index 2d9c56f673..70b4f20c2b 100644 --- a/quickstep/src/com/android/quickstep/RecentTasksList.java +++ b/quickstep/src/com/android/quickstep/RecentTasksList.java @@ -40,21 +40,20 @@ import java.util.function.Consumer; /** * Manages the recent task list from the system, caching it as necessary. */ -@TargetApi(Build.VERSION_CODES.P) +@TargetApi(Build.VERSION_CODES.R) public class RecentTasksList extends TaskStackChangeListener { + private static final TaskLoadResult INVALID_RESULT = new TaskLoadResult(-1, false, 0); + private final KeyguardManagerCompat mKeyguardManager; private final LooperExecutor mMainThreadExecutor; private final ActivityManagerWrapper mActivityManagerWrapper; // The list change id, increments as the task list changes in the system private int mChangeId; - // The last change id when the list was last loaded completely, must be <= the list change id - private int mLastLoadedId; - // The last change id was loaded with keysOnly = true - private boolean mLastLoadHadKeysOnly; - ArrayList mTasks = new ArrayList<>(); + private TaskLoadResult mResultsBg = INVALID_RESULT; + private TaskLoadResult mResultsUi = INVALID_RESULT; public RecentTasksList(LooperExecutor mainThreadExecutor, KeyguardManagerCompat keyguardManager, ActivityManagerWrapper activityManagerWrapper) { @@ -71,7 +70,7 @@ public class RecentTasksList extends TaskStackChangeListener { public void getTaskKeys(int numTasks, Consumer> callback) { // Kick off task loading in the background UI_HELPER_EXECUTOR.execute(() -> { - ArrayList tasks = loadTasksInBackground(numTasks, true /* loadKeysOnly */); + ArrayList tasks = loadTasksInBackground(numTasks, -1, true /* loadKeysOnly */); mMainThreadExecutor.execute(() -> callback.accept(tasks)); }); } @@ -85,26 +84,30 @@ public class RecentTasksList extends TaskStackChangeListener { */ public synchronized int getTasks(boolean loadKeysOnly, Consumer> callback) { final int requestLoadId = mChangeId; - Runnable resultCallback = callback == null - ? () -> { } - : () -> callback.accept(copyOf(mTasks)); - - if (mLastLoadedId == mChangeId && (!mLastLoadHadKeysOnly || loadKeysOnly)) { + if (mResultsUi.isValidForRequest(requestLoadId, loadKeysOnly)) { // The list is up to date, send the callback on the next frame, // so that requestID can be returned first. - mMainThreadExecutor.post(resultCallback); + if (callback != null) { + // Copy synchronously as the changeId might change by next frame + ArrayList result = copyOf(mResultsUi); + mMainThreadExecutor.post(() -> callback.accept(result)); + } + return requestLoadId; } // Kick off task loading in the background UI_HELPER_EXECUTOR.execute(() -> { - ArrayList tasks = loadTasksInBackground(Integer.MAX_VALUE, loadKeysOnly); - + if (!mResultsBg.isValidForRequest(requestLoadId, loadKeysOnly)) { + mResultsBg = loadTasksInBackground(Integer.MAX_VALUE, requestLoadId, loadKeysOnly); + } + TaskLoadResult loadResult = mResultsBg; mMainThreadExecutor.execute(() -> { - mTasks = tasks; - mLastLoadedId = requestLoadId; - mLastLoadHadKeysOnly = loadKeysOnly; - resultCallback.run(); + mResultsUi = loadResult; + if (callback != null) { + ArrayList result = copyOf(mResultsUi); + callback.accept(result); + } }); }); @@ -119,8 +122,8 @@ public class RecentTasksList extends TaskStackChangeListener { } @Override - public synchronized void onTaskStackChanged() { - mChangeId++; + public void onTaskStackChanged() { + invalidateLoadedTasks(); } @Override @@ -131,22 +134,28 @@ public class RecentTasksList extends TaskStackChangeListener { // callback (those are for changes to the active tasks), but the task list is still updated, // so we should also invalidate the change id to ensure we load a new list instead of // reusing a stale list. - mChangeId++; + invalidateLoadedTasks(); } @Override public void onTaskRemoved(int taskId) { - mTasks = loadTasksInBackground(Integer.MAX_VALUE, false); + invalidateLoadedTasks(); } + @Override - public synchronized void onActivityPinned(String packageName, int userId, int taskId, - int stackId) { - mChangeId++; + public void onActivityPinned(String packageName, int userId, int taskId, int stackId) { + invalidateLoadedTasks(); } @Override public synchronized void onActivityUnpinned() { + invalidateLoadedTasks(); + } + + private synchronized void invalidateLoadedTasks() { + UI_HELPER_EXECUTOR.execute(() -> mResultsBg = INVALID_RESULT); + mResultsUi = INVALID_RESULT; mChangeId++; } @@ -154,9 +163,8 @@ public class RecentTasksList extends TaskStackChangeListener { * Loads and creates a list of all the recent tasks. */ @VisibleForTesting - ArrayList loadTasksInBackground(int numTasks, boolean loadKeysOnly) { + TaskLoadResult loadTasksInBackground(int numTasks, int requestId, boolean loadKeysOnly) { int currentUserId = Process.myUserHandle().getIdentifier(); - ArrayList allTasks = new ArrayList<>(); List rawTasks = mActivityManagerWrapper.getRecentTasks(numTasks, currentUserId); // The raw tasks are given in most-recent to least-recent order, we need to reverse it @@ -173,6 +181,7 @@ public class RecentTasksList extends TaskStackChangeListener { } }; + TaskLoadResult allTasks = new TaskLoadResult(requestId, loadKeysOnly, rawTasks.size()); for (ActivityManager.RecentTaskInfo rawTask : rawTasks) { Task.TaskKey taskKey = new Task.TaskKey(rawTask); Task task; @@ -197,4 +206,22 @@ public class RecentTasksList extends TaskStackChangeListener { } return newTasks; } + + private static class TaskLoadResult extends ArrayList { + + final int mId; + + // If the result was loaded with keysOnly = true + final boolean mKeysOnly; + + TaskLoadResult(int id, boolean keysOnly, int size) { + super(size); + mId = id; + mKeysOnly = keysOnly; + } + + boolean isValidForRequest(int requestId, boolean loadKeysOnly) { + return mId == requestId && (!mKeysOnly || loadKeysOnly); + } + } } \ No newline at end of file diff --git a/quickstep/src/com/android/quickstep/RecentsAnimationController.java b/quickstep/src/com/android/quickstep/RecentsAnimationController.java index b0a3cd2a1e..4e9aa6135e 100644 --- a/quickstep/src/com/android/quickstep/RecentsAnimationController.java +++ b/quickstep/src/com/android/quickstep/RecentsAnimationController.java @@ -29,7 +29,6 @@ import android.view.KeyEvent; import android.view.MotionEvent; import androidx.annotation.NonNull; -import androidx.annotation.Nullable; import androidx.annotation.UiThread; import com.android.launcher3.util.Preconditions; @@ -58,8 +57,7 @@ public class RecentsAnimationController { private boolean mUseLauncherSysBarFlags = false; private boolean mSplitScreenMinimized = false; private boolean mTouchInProgress; - private boolean mFinishPending; - private @Nullable Runnable mFinishPendingCallback; + private boolean mDisableInputProxyPending; public RecentsAnimationController(RecentsAnimationControllerCompat controller, boolean allowMinimizeSplitScreen, @@ -138,12 +136,12 @@ public class RecentsAnimationController { @UiThread public void finishAnimationToHome() { - finishAndClear(true /* toRecents */, null, false /* sendUserLeaveHint */); + finishAndDisableInputProxy(true /* toRecents */, null, false /* sendUserLeaveHint */); } @UiThread public void finishAnimationToApp() { - finishAndClear(false /* toRecents */, null, false /* sendUserLeaveHint */); + finishAndDisableInputProxy(false /* toRecents */, null, false /* sendUserLeaveHint */); } /** See {@link #finish(boolean, Runnable, boolean)} */ @@ -162,19 +160,16 @@ public class RecentsAnimationController { @UiThread public void finish(boolean toRecents, Runnable onFinishComplete, boolean sendUserLeaveHint) { Preconditions.assertUIThread(); - if (!toRecents) { - finishAndClear(false, onFinishComplete, sendUserLeaveHint); + if (toRecents && mTouchInProgress) { + // Finish the controller as requested, but don't disable input proxy yet. + mDisableInputProxyPending = true; + finishController(toRecents, onFinishComplete, sendUserLeaveHint); } else { - if (mTouchInProgress) { - mFinishPending = true; - mFinishPendingCallback = onFinishComplete; - } else { - finishAndClear(true, onFinishComplete, sendUserLeaveHint); - } + finishAndDisableInputProxy(toRecents, onFinishComplete, sendUserLeaveHint); } } - private void finishAndClear(boolean toRecents, Runnable onFinishComplete, + private void finishAndDisableInputProxy(boolean toRecents, Runnable onFinishComplete, boolean sendUserLeaveHint) { disableInputProxy(); finishController(toRecents, onFinishComplete, sendUserLeaveHint); @@ -262,11 +257,9 @@ public class RecentsAnimationController { } else if (action == ACTION_CANCEL || action == ACTION_UP) { // Finish any pending actions mTouchInProgress = false; - if (mFinishPending) { - mFinishPending = false; - finishAndClear(true /* toRecents */, mFinishPendingCallback, - false /* sendUserLeaveHint */); - mFinishPendingCallback = null; + if (mDisableInputProxyPending) { + mDisableInputProxyPending = false; + disableInputProxy(); } } if (mInputConsumer != null) { diff --git a/quickstep/src/com/android/quickstep/RecentsAnimationDeviceState.java b/quickstep/src/com/android/quickstep/RecentsAnimationDeviceState.java index f7c29851af..c83de13618 100644 --- a/quickstep/src/com/android/quickstep/RecentsAnimationDeviceState.java +++ b/quickstep/src/com/android/quickstep/RecentsAnimationDeviceState.java @@ -18,6 +18,7 @@ package com.android.quickstep; import static android.content.Intent.ACTION_USER_UNLOCKED; import static com.android.launcher3.util.DefaultDisplay.CHANGE_ALL; +import static com.android.launcher3.util.DefaultDisplay.CHANGE_FRAME_DELAY; import static com.android.launcher3.util.Executors.UI_HELPER_EXECUTOR; import static com.android.quickstep.SysUINavigationMode.Mode.NO_BUTTON; import static com.android.quickstep.SysUINavigationMode.Mode.THREE_BUTTONS; @@ -46,6 +47,7 @@ import android.graphics.Region; import android.os.Process; import android.os.SystemProperties; import android.os.UserManager; +import android.provider.Settings; import android.text.TextUtils; import android.util.DisplayMetrics; import android.view.MotionEvent; @@ -111,7 +113,8 @@ public class RecentsAnimationDeviceState implements private TaskStackChangeListener mFrozenTaskListener = new TaskStackChangeListener() { @Override public void onRecentTaskListFrozenChanged(boolean frozen) { - if (frozen) { + mTaskListFrozen = frozen; + if (frozen || mInOverview) { return; } enableMultipleRegions(false); @@ -132,6 +135,9 @@ public class RecentsAnimationDeviceState implements * TODO: (b/156984037) For when user rotates after entering overview */ private boolean mInOverview; + private boolean mTaskListFrozen; + + private boolean mIsUserSetupComplete; public RecentsAnimationDeviceState(Context context) { mContext = context; @@ -190,6 +196,17 @@ public class RecentsAnimationDeviceState implements mOneHandedEnabledObserver.register(); mOneHandedEnabledObserver.dispatchOnChange(); } + + SecureSettingsObserver userSetupObserver = new SecureSettingsObserver( + context.getContentResolver(), + e -> mIsUserSetupComplete = e, + Settings.Secure.USER_SETUP_COMPLETE, + 0); + mIsUserSetupComplete = userSetupObserver.getValue(); + if (!mIsUserSetupComplete) { + userSetupObserver.register(); + runOnDestroy(userSetupObserver::unregister); + } } private void setupOrientationSwipeHandler() { @@ -258,7 +275,8 @@ public class RecentsAnimationDeviceState implements @Override public void onDisplayInfoChanged(DefaultDisplay.Info info, int flags) { - if (info.id != getDisplayId()) { + if (info.id != getDisplayId() || (flags & CHANGE_FRAME_DELAY) == CHANGE_FRAME_DELAY) { + // ignore displays that aren't running launcher and frame refresh rate changes return; } @@ -329,6 +347,13 @@ public class RecentsAnimationDeviceState implements return mIsUserUnlocked; } + /** + * @return whether the user has completed setup wizard + */ + public boolean isUserSetupComplete() { + return mIsUserSetupComplete; + } + private void notifyUserUnlocked() { for (Runnable action : mUserUnlockedActions) { action.run(); @@ -375,7 +400,6 @@ public class RecentsAnimationDeviceState implements return (mSystemUiStateFlags & SYSUI_STATE_NAV_BAR_HIDDEN) == 0 && (mSystemUiStateFlags & SYSUI_STATE_NOTIFICATION_PANEL_EXPANDED) == 0 && (mSystemUiStateFlags & SYSUI_STATE_QUICK_SETTINGS_EXPANDED) == 0 - && (mSystemUiStateFlags & SYSUI_STATE_BUBBLES_EXPANDED) == 0 && ((mSystemUiStateFlags & SYSUI_STATE_HOME_DISABLED) == 0 || (mSystemUiStateFlags & SYSUI_STATE_OVERVIEW_DISABLED) == 0); } @@ -395,6 +419,13 @@ public class RecentsAnimationDeviceState implements return (mSystemUiStateFlags & SYSUI_STATE_SCREEN_PINNING) != 0; } + /** + * @return whether the bubble stack is expanded + */ + public boolean isBubblesExpanded() { + return (mSystemUiStateFlags & SYSUI_STATE_BUBBLES_EXPANDED) != 0; + } + /** * @return whether lock-task mode is active */ @@ -550,7 +581,7 @@ public class RecentsAnimationDeviceState implements * *May* apply a transform on the motion event if it lies in the nav bar region for another * orientation that is currently being tracked as a part of quickstep */ - public void setOrientationTransformIfNeeded(MotionEvent event) { + void setOrientationTransformIfNeeded(MotionEvent event) { // negative coordinates bug b/143901881 if (event.getX() < 0 || event.getY() < 0) { event.setLocation(Math.max(0, event.getX()), Math.max(0, event.getY())); @@ -558,25 +589,54 @@ public class RecentsAnimationDeviceState implements mOrientationTouchTransformer.transform(event); } - void onSwipeUpToOverview(BaseActivityInterface activityInterface) { - mInOverview = true; - activityInterface.onExitOverview(this, () -> { - mInOverview = false; - enableMultipleRegions(false); - }); + void enableMultipleRegions(boolean enable) { + mOrientationTouchTransformer.enableMultipleRegions(enable, mDefaultDisplay.getInfo()); + notifySysuiForRotation(mOrientationTouchTransformer.getQuickStepStartingRotation()); } - void enableMultipleRegions(boolean enable) { - if (mInOverview) { - return; + private void notifySysuiForRotation(int rotation) { + UI_HELPER_EXECUTOR.execute(() -> + SystemUiProxy.INSTANCE.get(mContext).onQuickSwitchToNewTask(rotation)); + } + + public void onStartGesture() { + if (mTaskListFrozen) { + // Prioritize whatever nav bar user touches once in quickstep + // This case is specifically when user changes what nav bar they are using mid + // quickswitch session before tasks list is unfrozen + notifySysuiForRotation(mOrientationTouchTransformer.getCurrentActiveRotation()); + } + } + + + void onEndTargetCalculated(GestureState.GestureEndTarget endTarget, + BaseActivityInterface activityInterface) { + if (endTarget == GestureState.GestureEndTarget.RECENTS) { + mInOverview = true; + if (!mTaskListFrozen) { + // If we're in landscape w/o ever quickswitching, show the navbar in landscape + enableMultipleRegions(true); + } + activityInterface.onExitOverview(this, () -> { + mInOverview = false; + enableMultipleRegions(false); + }); + } else if (endTarget == GestureState.GestureEndTarget.HOME) { + enableMultipleRegions(false); + } else if (endTarget == GestureState.GestureEndTarget.NEW_TASK) { + if (mOrientationTouchTransformer.getQuickStepStartingRotation() == -1) { + // First gesture to start quickswitch + enableMultipleRegions(true); + } else { + notifySysuiForRotation(mOrientationTouchTransformer.getCurrentActiveRotation()); + } + } else if (endTarget == GestureState.GestureEndTarget.LAST_TASK) { + if (!mTaskListFrozen) { + // touched nav bar but didn't go anywhere and not quickswitching, do nothing + return; + } + notifySysuiForRotation(mOrientationTouchTransformer.getCurrentActiveRotation()); } - mOrientationTouchTransformer.enableMultipleRegions(enable, mDefaultDisplay.getInfo()); - UI_HELPER_EXECUTOR.execute(() -> { - int quickStepStartingRotation = - mOrientationTouchTransformer.getQuickStepStartingRotation(); - SystemUiProxy.INSTANCE.get(mContext) - .onQuickSwitchToNewTask(quickStepStartingRotation); - }); } public int getCurrentActiveRotation() { diff --git a/quickstep/src/com/android/quickstep/interaction/BackGestureTutorialController.java b/quickstep/src/com/android/quickstep/interaction/BackGestureTutorialController.java index 58870edece..41e86e00b6 100644 --- a/quickstep/src/com/android/quickstep/interaction/BackGestureTutorialController.java +++ b/quickstep/src/com/android/quickstep/interaction/BackGestureTutorialController.java @@ -18,8 +18,11 @@ package com.android.quickstep.interaction; import static com.android.quickstep.interaction.TutorialController.TutorialType.BACK_NAVIGATION_COMPLETE; import static com.android.quickstep.interaction.TutorialController.TutorialType.LEFT_EDGE_BACK_NAVIGATION; +import android.graphics.PointF; import android.view.View; +import androidx.annotation.Nullable; + import com.android.launcher3.R; import com.android.quickstep.interaction.EdgeBackGestureHandler.BackGestureResult; import com.android.quickstep.interaction.NavBarGestureHandler.NavBarGestureResult; @@ -154,11 +157,14 @@ final class BackGestureTutorialController extends TutorialController { } @Override - public void onNavBarGestureAttempted(NavBarGestureResult result) { + public void onNavBarGestureAttempted(NavBarGestureResult result, PointF finalVelocity) { if (mTutorialType == BACK_NAVIGATION_COMPLETE) { if (result == NavBarGestureResult.HOME_GESTURE_COMPLETED) { mTutorialFragment.closeTutorial(); } } } + + @Override + public void setNavBarGestureProgress(@Nullable Float displacement) {} } diff --git a/quickstep/src/com/android/quickstep/interaction/HomeGestureTutorialController.java b/quickstep/src/com/android/quickstep/interaction/HomeGestureTutorialController.java index 524cbaf06f..65f41a4a6c 100644 --- a/quickstep/src/com/android/quickstep/interaction/HomeGestureTutorialController.java +++ b/quickstep/src/com/android/quickstep/interaction/HomeGestureTutorialController.java @@ -15,19 +15,135 @@ */ package com.android.quickstep.interaction; +import static com.android.launcher3.anim.Interpolators.ACCEL; +import static com.android.launcher3.util.DefaultDisplay.getSingleFrameMs; +import static com.android.quickstep.BaseSwipeUpHandlerV2.MAX_SWIPE_DURATION; import static com.android.quickstep.interaction.TutorialController.TutorialType.HOME_NAVIGATION_COMPLETE; +import android.animation.Animator; +import android.animation.AnimatorListenerAdapter; +import android.animation.AnimatorSet; +import android.annotation.TargetApi; +import android.content.Context; +import android.graphics.Insets; +import android.graphics.Outline; +import android.graphics.PointF; +import android.graphics.Rect; +import android.graphics.RectF; +import android.os.Build; +import android.view.SurfaceControl; import android.view.View; +import android.view.ViewOutlineProvider; +import android.view.WindowInsets.Type; +import android.view.WindowManager; +import androidx.annotation.NonNull; +import androidx.annotation.Nullable; + +import com.android.launcher3.DeviceProfile; +import com.android.launcher3.InvariantDeviceProfile; import com.android.launcher3.R; +import com.android.launcher3.Utilities; +import com.android.launcher3.anim.AnimationSuccessListener; +import com.android.launcher3.anim.AnimatorPlaybackController; +import com.android.launcher3.anim.PendingAnimation; +import com.android.quickstep.AnimatedFloat; +import com.android.quickstep.GestureState; +import com.android.quickstep.OverviewComponentObserver; +import com.android.quickstep.RecentsAnimationDeviceState; +import com.android.quickstep.SwipeUpAnimationLogic; +import com.android.quickstep.SwipeUpAnimationLogic.RunningWindowAnim; import com.android.quickstep.interaction.EdgeBackGestureHandler.BackGestureResult; import com.android.quickstep.interaction.NavBarGestureHandler.NavBarGestureResult; +import com.android.quickstep.util.RectFSpringAnim; +import com.android.quickstep.util.TransformParams; +import com.android.systemui.shared.system.QuickStepContract; +import com.android.systemui.shared.system.SyncRtSurfaceTransactionApplierCompat.SurfaceParams; /** A {@link TutorialController} for the Home tutorial. */ +@TargetApi(Build.VERSION_CODES.R) final class HomeGestureTutorialController extends TutorialController { + private float mFakeTaskViewRadius; + private Rect mFakeTaskViewRect = new Rect(); + + private final ViewSwipeUpAnimation mViewSwipeUpAnimation; + private RunningWindowAnim mRunningWindowAnim; + HomeGestureTutorialController(HomeGestureTutorialFragment fragment, TutorialType tutorialType) { super(fragment, tutorialType); + + RecentsAnimationDeviceState deviceState = new RecentsAnimationDeviceState(mContext); + OverviewComponentObserver observer = new OverviewComponentObserver(mContext, deviceState); + mViewSwipeUpAnimation = new ViewSwipeUpAnimation(mContext, deviceState, + new GestureState(observer, -1)); + observer.onDestroy(); + deviceState.destroy(); + + DeviceProfile dp = InvariantDeviceProfile.INSTANCE.get(mContext) + .getDeviceProfile(mContext) + .copy(mContext); + Insets insets = mContext.getSystemService(WindowManager.class) + .getCurrentWindowMetrics() + .getWindowInsets() + .getInsets(Type.systemBars()); + dp.updateInsets(new Rect(insets.left, insets.top, insets.right, insets.bottom)); + mViewSwipeUpAnimation.initDp(dp); + + mFakeTaskViewRadius = QuickStepContract.getWindowCornerRadius(mContext.getResources()); + + mFakeTaskView.setClipToOutline(true); + mFakeTaskView.setOutlineProvider(new ViewOutlineProvider() { + @Override + public void getOutline(View view, Outline outline) { + outline.setRoundRect(mFakeTaskViewRect, mFakeTaskViewRadius); + } + }); + } + + private void cancelRunningAnimation() { + if (mRunningWindowAnim != null) { + mRunningWindowAnim.cancel(); + } + mRunningWindowAnim = null; + } + + /** Fades the task view, optionally after animating to a fake Overview. */ + private void fadeOutFakeTaskView(boolean toOverviewFirst, @Nullable Runnable onEndRunnable) { + cancelRunningAnimation(); + PendingAnimation anim = new PendingAnimation(300); + AnimatorListenerAdapter resetTaskView = new AnimatorListenerAdapter() { + @Override + public void onAnimationEnd(Animator animation, boolean isReverse) { + mFakeTaskView.setVisibility(View.INVISIBLE); + mFakeTaskView.setAlpha(1); + mRunningWindowAnim = null; + } + }; + if (toOverviewFirst) { + anim.setFloat(mViewSwipeUpAnimation.getCurrentShift(), AnimatedFloat.VALUE, 1, ACCEL); + anim.addListener(new AnimatorListenerAdapter() { + @Override + public void onAnimationEnd(Animator animation, boolean isReverse) { + PendingAnimation fadeAnim = new PendingAnimation(300); + fadeAnim.setViewAlpha(mFakeTaskView, 0, ACCEL); + fadeAnim.addListener(resetTaskView); + AnimatorSet animset = fadeAnim.buildAnim(); + animset.setStartDelay(100); + animset.start(); + mRunningWindowAnim = RunningWindowAnim.wrap(animset); + } + }); + } else { + anim.setViewAlpha(mFakeTaskView, 0, ACCEL); + anim.addListener(resetTaskView); + } + if (onEndRunnable != null) { + anim.addListener(AnimationSuccessListener.forRunnable(onEndRunnable)); + } + AnimatorSet animset = anim.buildAnim(); + animset.start(); + mRunningWindowAnim = RunningWindowAnim.wrap(animset); } @Override @@ -85,22 +201,35 @@ final class HomeGestureTutorialController extends TutorialController { } @Override - public void onNavBarGestureAttempted(NavBarGestureResult result) { + public void onNavBarGestureAttempted(NavBarGestureResult result, PointF finalVelocity) { switch (mTutorialType) { case HOME_NAVIGATION: switch (result) { - case HOME_GESTURE_COMPLETED: + case HOME_GESTURE_COMPLETED: { + hideFeedback(); + cancelRunningAnimation(); hideHandCoachingAnimation(); - mTutorialFragment.changeController(HOME_NAVIGATION_COMPLETE); + RectFSpringAnim rectAnim = + mViewSwipeUpAnimation.handleSwipeUpToHome(finalVelocity); + // After home animation finishes, fade out and then move to the next screen. + rectAnim.addAnimatorListener(AnimationSuccessListener.forRunnable( + () -> fadeOutFakeTaskView(false, + () -> mTutorialFragment.changeController( + HOME_NAVIGATION_COMPLETE)))); + mRunningWindowAnim = RunningWindowAnim.wrap(rectAnim); break; + } case HOME_NOT_STARTED_TOO_FAR_FROM_EDGE: case OVERVIEW_NOT_STARTED_TOO_FAR_FROM_EDGE: showFeedback(R.string.home_gesture_feedback_swipe_too_far_from_edge); break; case OVERVIEW_GESTURE_COMPLETED: - showFeedback(R.string.home_gesture_feedback_overview_detected); + fadeOutFakeTaskView(true, () -> + showFeedback(R.string.home_gesture_feedback_overview_detected)); break; case HOME_OR_OVERVIEW_NOT_STARTED_WRONG_SWIPE_DIRECTION: + case HOME_OR_OVERVIEW_CANCELLED: + fadeOutFakeTaskView(false, null); showFeedback(R.string.home_gesture_feedback_wrong_swipe_direction); break; } @@ -112,4 +241,94 @@ final class HomeGestureTutorialController extends TutorialController { break; } } + + @Override + public void setNavBarGestureProgress(@Nullable Float displacement) { + if (displacement == null || mTutorialType == HOME_NAVIGATION_COMPLETE) { + mFakeTaskView.setVisibility(View.INVISIBLE); + } else { + mFakeTaskView.setVisibility(View.VISIBLE); + if (mRunningWindowAnim == null) { + mViewSwipeUpAnimation.updateDisplacement(displacement); + } + } + } + + private class ViewSwipeUpAnimation extends SwipeUpAnimationLogic { + + ViewSwipeUpAnimation(Context context, RecentsAnimationDeviceState deviceState, + GestureState gestureState) { + super(context, deviceState, gestureState, new FakeTransformParams()); + } + + void initDp(DeviceProfile dp) { + initTransitionEndpoints(dp); + mTaskViewSimulator.setPreviewBounds( + new Rect(0, 0, dp.widthPx, dp.heightPx), dp.getInsets()); + } + + @Override + public void updateFinalShift() { + float progress = mCurrentShift.value / mDragLengthFactor; + mWindowTransitionController.setPlayFraction(progress); + mTaskViewSimulator.apply(mTransformParams); + } + + AnimatedFloat getCurrentShift() { + return mCurrentShift; + } + + RectFSpringAnim handleSwipeUpToHome(PointF velocity) { + PointF velocityPxPerMs = new PointF(velocity.x, velocity.y); + float currentShift = mCurrentShift.value; + final float startShift = Utilities.boundToRange(currentShift - velocityPxPerMs.y + * getSingleFrameMs(mContext) / mTransitionDragLength, 0, mDragLengthFactor); + float distanceToTravel = (1 - currentShift) * mTransitionDragLength; + + // we want the page's snap velocity to approximately match the velocity at + // which the user flings, so we scale the duration by a value near to the + // derivative of the scroll interpolator at zero, ie. 2. + long baseDuration = Math.round(Math.abs(distanceToTravel / velocityPxPerMs.y)); + long duration = Math.min(MAX_SWIPE_DURATION, 2 * baseDuration); + HomeAnimationFactory homeAnimFactory = new HomeAnimationFactory(null) { + @Override + public AnimatorPlaybackController createActivityAnimationToHome() { + return AnimatorPlaybackController.wrap(new AnimatorSet(), duration); + } + + @NonNull + @Override + public RectF getWindowTargetRect() { + int fakeHomeIconSizePx = mDp.allAppsIconSizePx; + int fakeHomeIconLeft = (mDp.widthPx - fakeHomeIconSizePx) / 2; + int fakeHomeIconTop = mDp.heightPx - (mDp.allAppsCellHeightPx * 3); + return new RectF(fakeHomeIconLeft, fakeHomeIconTop, + fakeHomeIconLeft + fakeHomeIconSizePx, + fakeHomeIconTop + fakeHomeIconSizePx); + } + }; + RectFSpringAnim windowAnim = createWindowAnimationToHome(startShift, homeAnimFactory); + windowAnim.start(mContext, velocityPxPerMs); + return windowAnim; + } + } + + private class FakeTransformParams extends TransformParams { + + @Override + public SurfaceParams[] createSurfaceParams(BuilderProxy proxy) { + SurfaceParams.Builder builder = new SurfaceParams.Builder((SurfaceControl) null); + proxy.onBuildTargetParams(builder, null, this); + return new SurfaceParams[] {builder.build()}; + } + + @Override + public void applySurfaceParams(SurfaceParams[] params) { + SurfaceParams p = params[0]; + mFakeTaskView.setAnimationMatrix(p.matrix); + mFakeTaskViewRect.set(p.windowCrop); + mFakeTaskViewRadius = p.cornerRadius; + mFakeTaskView.invalidateOutline(); + } + } } diff --git a/quickstep/src/com/android/quickstep/interaction/NavBarGestureHandler.java b/quickstep/src/com/android/quickstep/interaction/NavBarGestureHandler.java index 6d8caa2ec5..4069c09a28 100644 --- a/quickstep/src/com/android/quickstep/interaction/NavBarGestureHandler.java +++ b/quickstep/src/com/android/quickstep/interaction/NavBarGestureHandler.java @@ -17,6 +17,7 @@ package com.android.quickstep.interaction; import static com.android.quickstep.interaction.NavBarGestureHandler.NavBarGestureResult.HOME_GESTURE_COMPLETED; import static com.android.quickstep.interaction.NavBarGestureHandler.NavBarGestureResult.HOME_NOT_STARTED_TOO_FAR_FROM_EDGE; +import static com.android.quickstep.interaction.NavBarGestureHandler.NavBarGestureResult.HOME_OR_OVERVIEW_CANCELLED; import static com.android.quickstep.interaction.NavBarGestureHandler.NavBarGestureResult.HOME_OR_OVERVIEW_NOT_STARTED_WRONG_SWIPE_DIRECTION; import static com.android.quickstep.interaction.NavBarGestureHandler.NavBarGestureResult.OVERVIEW_GESTURE_COMPLETED; import static com.android.quickstep.interaction.NavBarGestureHandler.NavBarGestureResult.OVERVIEW_NOT_STARTED_TOO_FAR_FROM_EDGE; @@ -24,19 +25,23 @@ import static com.android.quickstep.interaction.NavBarGestureHandler.NavBarGestu import android.content.Context; import android.content.res.Resources; import android.graphics.Point; +import android.graphics.PointF; import android.view.Display; import android.view.MotionEvent; import android.view.Surface; import android.view.View; import android.view.View.OnTouchListener; +import androidx.annotation.Nullable; + import com.android.launcher3.ResourceUtils; import com.android.quickstep.SysUINavigationMode.Mode; import com.android.quickstep.util.NavBarPosition; import com.android.quickstep.util.TriggerSwipeUpTouchTracker; /** Utility class to handle home gestures. */ -public class NavBarGestureHandler implements OnTouchListener { +public class NavBarGestureHandler implements OnTouchListener, + TriggerSwipeUpTouchTracker.OnSwipeUpListener { private static final String LOG_TAG = "NavBarGestureHandler"; @@ -44,6 +49,7 @@ public class NavBarGestureHandler implements OnTouchListener { private final TriggerSwipeUpTouchTracker mSwipeUpTouchTracker; private int mBottomGestureHeight; private boolean mTouchCameFromNavBar; + private float mDownY; private NavBarGestureAttemptCallback mGestureCallback; NavBarGestureHandler(Context context) { @@ -55,10 +61,11 @@ public class NavBarGestureHandler implements OnTouchListener { displayRotation = display.getRotation(); display.getRealSize(mDisplaySize); } + mDownY = mDisplaySize.y; mSwipeUpTouchTracker = new TriggerSwipeUpTouchTracker(context, true /*disableHorizontalSwipe*/, new NavBarPosition(Mode.NO_BUTTON, displayRotation), - null /*onInterceptTouch*/, this::onSwipeUp); + null /*onInterceptTouch*/, this); final Resources resources = context.getResources(); mBottomGestureHeight = @@ -73,16 +80,26 @@ public class NavBarGestureHandler implements OnTouchListener { mGestureCallback = null; } - private void onSwipeUp(boolean wasFling) { + @Override + public void onSwipeUp(boolean wasFling, PointF finalVelocity) { if (mGestureCallback == null) { return; } + finalVelocity.set(finalVelocity.x / 1000, finalVelocity.y / 1000); if (mTouchCameFromNavBar) { mGestureCallback.onNavBarGestureAttempted(wasFling - ? HOME_GESTURE_COMPLETED : OVERVIEW_GESTURE_COMPLETED); + ? HOME_GESTURE_COMPLETED : OVERVIEW_GESTURE_COMPLETED, finalVelocity); } else { mGestureCallback.onNavBarGestureAttempted(wasFling - ? HOME_NOT_STARTED_TOO_FAR_FROM_EDGE : OVERVIEW_NOT_STARTED_TOO_FAR_FROM_EDGE); + ? HOME_NOT_STARTED_TOO_FAR_FROM_EDGE : OVERVIEW_NOT_STARTED_TOO_FAR_FROM_EDGE, + finalVelocity); + } + } + + @Override + public void onSwipeUpCancelled() { + if (mGestureCallback != null) { + mGestureCallback.onNavBarGestureAttempted(HOME_OR_OVERVIEW_CANCELLED, new PointF()); } } @@ -91,15 +108,22 @@ public class NavBarGestureHandler implements OnTouchListener { int action = motionEvent.getAction(); boolean intercepted = mSwipeUpTouchTracker.interceptedTouch(); if (action == MotionEvent.ACTION_DOWN) { - mTouchCameFromNavBar = motionEvent.getRawY() >= mDisplaySize.y - mBottomGestureHeight; + mDownY = motionEvent.getY(); + mTouchCameFromNavBar = mDownY >= mDisplaySize.y - mBottomGestureHeight; + if (!mTouchCameFromNavBar) { + mGestureCallback.setNavBarGestureProgress(null); + } mSwipeUpTouchTracker.init(); } else if (action == MotionEvent.ACTION_UP || action == MotionEvent.ACTION_CANCEL) { if (mGestureCallback != null && !intercepted && mTouchCameFromNavBar) { mGestureCallback.onNavBarGestureAttempted( - HOME_OR_OVERVIEW_NOT_STARTED_WRONG_SWIPE_DIRECTION); + HOME_OR_OVERVIEW_NOT_STARTED_WRONG_SWIPE_DIRECTION, new PointF()); intercepted = true; } } + if (mTouchCameFromNavBar && mGestureCallback != null) { + mGestureCallback.setNavBarGestureProgress(motionEvent.getY() - mDownY); + } mSwipeUpTouchTracker.onMotionEvent(motionEvent); return intercepted; } @@ -110,12 +134,16 @@ public class NavBarGestureHandler implements OnTouchListener { OVERVIEW_GESTURE_COMPLETED, HOME_NOT_STARTED_TOO_FAR_FROM_EDGE, OVERVIEW_NOT_STARTED_TOO_FAR_FROM_EDGE, - HOME_OR_OVERVIEW_NOT_STARTED_WRONG_SWIPE_DIRECTION // Side swipe on nav bar. + HOME_OR_OVERVIEW_NOT_STARTED_WRONG_SWIPE_DIRECTION, // Side swipe on nav bar. + HOME_OR_OVERVIEW_CANCELLED } /** Callback to let the UI react to attempted nav bar gestures. */ interface NavBarGestureAttemptCallback { /** Called whenever any touch is completed. */ - void onNavBarGestureAttempted(NavBarGestureResult result); + void onNavBarGestureAttempted(NavBarGestureResult result, PointF finalVelocity); + + /** Indicates how far a touch originating in the nav bar has moved from the nav bar. */ + void setNavBarGestureProgress(@Nullable Float displacement); } } diff --git a/quickstep/src/com/android/quickstep/interaction/TutorialController.java b/quickstep/src/com/android/quickstep/interaction/TutorialController.java index 1e29f44704..f27d500eff 100644 --- a/quickstep/src/com/android/quickstep/interaction/TutorialController.java +++ b/quickstep/src/com/android/quickstep/interaction/TutorialController.java @@ -15,6 +15,7 @@ */ package com.android.quickstep.interaction; +import android.content.Context; import android.graphics.drawable.RippleDrawable; import android.view.View; import android.view.View.OnClickListener; @@ -39,11 +40,13 @@ abstract class TutorialController implements BackGestureAttemptCallback, final TutorialFragment mTutorialFragment; TutorialType mTutorialType; + final Context mContext; final ImageButton mCloseButton; final TextView mTitleTextView; final TextView mSubtitleTextView; final TextView mFeedbackView; + final View mFakeTaskView; final View mRippleView; final RippleDrawable mRippleDrawable; final TutorialHandAnimation mHandCoachingAnimation; @@ -55,6 +58,7 @@ abstract class TutorialController implements BackGestureAttemptCallback, TutorialController(TutorialFragment tutorialFragment, TutorialType tutorialType) { mTutorialFragment = tutorialFragment; mTutorialType = tutorialType; + mContext = mTutorialFragment.getContext(); View rootView = tutorialFragment.getRootView(); mCloseButton = rootView.findViewById(R.id.gesture_tutorial_fragment_close_button); @@ -62,6 +66,7 @@ abstract class TutorialController implements BackGestureAttemptCallback, mTitleTextView = rootView.findViewById(R.id.gesture_tutorial_fragment_title_view); mSubtitleTextView = rootView.findViewById(R.id.gesture_tutorial_fragment_subtitle_view); mFeedbackView = rootView.findViewById(R.id.gesture_tutorial_fragment_feedback_view); + mFakeTaskView = rootView.findViewById(R.id.gesture_tutorial_fake_task_view); mRippleView = rootView.findViewById(R.id.gesture_tutorial_ripple_view); mRippleDrawable = (RippleDrawable) mRippleView.getBackground(); mHandCoachingAnimation = tutorialFragment.getHandAnimation(); diff --git a/quickstep/src/com/android/quickstep/interaction/TutorialFragment.java b/quickstep/src/com/android/quickstep/interaction/TutorialFragment.java index 44c1a5da04..a3881cffb0 100644 --- a/quickstep/src/com/android/quickstep/interaction/TutorialFragment.java +++ b/quickstep/src/com/android/quickstep/interaction/TutorialFragment.java @@ -37,11 +37,6 @@ import com.android.quickstep.interaction.TutorialController.TutorialType; abstract class TutorialFragment extends Fragment implements OnTouchListener { private static final String LOG_TAG = "TutorialFragment"; - private static final String SYSTEM_NAVIGATION_SETTING_INTENT = - "#Intent;action=com.android.settings.SEARCH_RESULT_TRAMPOLINE;S" - + ".:settings:fragment_args_key=gesture_system_navigation_input_summary;S" - + ".:settings:show_fragment=com.android.settings.gestures" - + ".SystemNavigationGestureSettings;end"; static final String KEY_TUTORIAL_TYPE = "tutorial_type"; TutorialType mTutorialType; diff --git a/quickstep/src/com/android/quickstep/logging/StatsLogCompatManager.java b/quickstep/src/com/android/quickstep/logging/StatsLogCompatManager.java index 69812b6154..d4d46fbb2c 100644 --- a/quickstep/src/com/android/quickstep/logging/StatsLogCompatManager.java +++ b/quickstep/src/com/android/quickstep/logging/StatsLogCompatManager.java @@ -65,10 +65,10 @@ public class StatsLogCompatManager extends StatsLogManager { } /** - * Logs a {@link LauncherEvent}. + * Logs a {@link EventEnum}. */ @Override - public void log(LauncherEvent event) { + public void log(EventEnum event) { log(event, DEFAULT_INSTANCE_ID, LauncherAtom.ItemInfo.getDefaultInstance()); } @@ -76,7 +76,7 @@ public class StatsLogCompatManager extends StatsLogManager { * Logs an event and accompanying {@link InstanceId}. */ @Override - public void log(LauncherEvent event, InstanceId instanceId) { + public void log(EventEnum event, InstanceId instanceId) { log(event, instanceId, LauncherAtom.ItemInfo.getDefaultInstance()); } @@ -84,7 +84,7 @@ public class StatsLogCompatManager extends StatsLogManager { * Logs an event and accompanying {@link ItemInfo}. */ @Override - public void log(LauncherEvent event, @Nullable LauncherAtom.ItemInfo info) { + public void log(EventEnum event, @Nullable LauncherAtom.ItemInfo info) { log(event, DEFAULT_INSTANCE_ID, info); } @@ -92,7 +92,7 @@ public class StatsLogCompatManager extends StatsLogManager { * Logs an event and accompanying {@link InstanceId} and {@link LauncherAtom.ItemInfo}. */ @Override - public void log(LauncherEvent event, InstanceId instanceId, + public void log(EventEnum event, InstanceId instanceId, @Nullable LauncherAtom.ItemInfo info) { logInternal(event, instanceId, info, LAUNCHER_UICHANGED__DST_STATE__HOME, @@ -102,14 +102,17 @@ public class StatsLogCompatManager extends StatsLogManager { /** * Logs an event and accompanying {@link InstanceId} and {@link LauncherAtom.ItemInfo}. */ - private void logInternal(LauncherEvent event, InstanceId instanceId, + private void logInternal(EventEnum event, InstanceId instanceId, @Nullable LauncherAtom.ItemInfo info, int startState, int endState) { info = info == null ? LauncherAtom.ItemInfo.getDefaultInstance() : info; if (IS_VERBOSE) { + String name = (event instanceof LauncherEvent) ? ((LauncherEvent) event).name() : + event.getId() + ""; + Log.d(TAG, instanceId == DEFAULT_INSTANCE_ID - ? String.format("\n%s\n%s", event.name(), info) - : String.format("%s(InstanceId:%s)\n%s", event.name(), instanceId, info)); + ? String.format("\n%s\n%s", name, info) + : String.format("%s(InstanceId:%s)\n%s", name, instanceId, info)); } if (!Utilities.ATLEAST_R) { diff --git a/quickstep/src/com/android/quickstep/util/RecentsOrientedState.java b/quickstep/src/com/android/quickstep/util/RecentsOrientedState.java index 5745990614..498c232ec3 100644 --- a/quickstep/src/com/android/quickstep/util/RecentsOrientedState.java +++ b/quickstep/src/com/android/quickstep/util/RecentsOrientedState.java @@ -208,7 +208,7 @@ public final class RecentsOrientedState implements SharedPreferences.OnSharedPre mDisplayRotation = displayRotation; mTouchRotation = touchRotation; - if (mLauncherRotation == mTouchRotation) { + if (mLauncherRotation == mTouchRotation || canLauncherRotate()) { mOrientationHandler = PagedOrientationHandler.HOME_ROTATED; if (DEBUG) { Log.d(TAG, "current RecentsOrientedState: " + this); @@ -240,7 +240,7 @@ public final class RecentsOrientedState implements SharedPreferences.OnSharedPre private void setFlag(int mask, boolean enabled) { boolean wasRotationEnabled = !TestProtocol.sDisableSensorRotation - && mFlags == VALUE_ROTATION_WATCHER_ENABLED; + && (mFlags & VALUE_ROTATION_WATCHER_ENABLED) == VALUE_ROTATION_WATCHER_ENABLED; if (enabled) { mFlags |= mask; } else { @@ -248,7 +248,7 @@ public final class RecentsOrientedState implements SharedPreferences.OnSharedPre } boolean isRotationEnabled = !TestProtocol.sDisableSensorRotation - && mFlags == VALUE_ROTATION_WATCHER_ENABLED; + && (mFlags & VALUE_ROTATION_WATCHER_ENABLED) == VALUE_ROTATION_WATCHER_ENABLED; if (wasRotationEnabled != isRotationEnabled) { UI_HELPER_EXECUTOR.execute(() -> { if (isRotationEnabled) { diff --git a/quickstep/tests/src/com/android/quickstep/RecentTasksListTest.java b/quickstep/tests/src/com/android/quickstep/RecentTasksListTest.java index 34eb7f8177..79ddf7a399 100644 --- a/quickstep/tests/src/com/android/quickstep/RecentTasksListTest.java +++ b/quickstep/tests/src/com/android/quickstep/RecentTasksListTest.java @@ -58,9 +58,9 @@ public class RecentTasksListTest { } @Test - public void onTaskRemoved_reloadsAllTasks() { + public void onTaskRemoved_doesNotFetchTasks() { mRecentTasksList.onTaskRemoved(0); - verify(mockActivityManagerWrapper, times(1)) + verify(mockActivityManagerWrapper, times(0)) .getRecentTasks(anyInt(), anyInt()); } @@ -77,7 +77,7 @@ public class RecentTasksListTest { when(mockActivityManagerWrapper.getRecentTasks(anyInt(), anyInt())) .thenReturn(Collections.singletonList(recentTaskInfo)); - List taskList = mRecentTasksList.loadTasksInBackground(Integer.MAX_VALUE, true); + List taskList = mRecentTasksList.loadTasksInBackground(Integer.MAX_VALUE, -1, true); assertEquals(1, taskList.size()); assertNull(taskList.get(0).taskDescription.getLabel()); @@ -91,7 +91,7 @@ public class RecentTasksListTest { when(mockActivityManagerWrapper.getRecentTasks(anyInt(), anyInt())) .thenReturn(Collections.singletonList(recentTaskInfo)); - List taskList = mRecentTasksList.loadTasksInBackground(Integer.MAX_VALUE, false); + List taskList = mRecentTasksList.loadTasksInBackground(Integer.MAX_VALUE, -1, false); assertEquals(1, taskList.size()); assertEquals(taskDescription, taskList.get(0).taskDescription.getLabel()); diff --git a/res/values/colors.xml b/res/values/colors.xml index c9c893e511..c4ec7dd7f0 100644 --- a/res/values/colors.xml +++ b/res/values/colors.xml @@ -43,6 +43,7 @@ #99000000 #FF000000 #A0C2F9 + #6DA1FF #FFFFFFFF #1A73E8 diff --git a/src/com/android/launcher3/BaseDraggingActivity.java b/src/com/android/launcher3/BaseDraggingActivity.java index 0ac278457a..d1d5e2624d 100644 --- a/src/com/android/launcher3/BaseDraggingActivity.java +++ b/src/com/android/launcher3/BaseDraggingActivity.java @@ -24,6 +24,7 @@ import android.content.ActivityNotFoundException; import android.content.Intent; import android.content.pm.LauncherApps; import android.content.res.Configuration; +import android.graphics.Insets; import android.graphics.Point; import android.graphics.Rect; import android.os.Bundle; @@ -35,6 +36,8 @@ import android.view.ActionMode; import android.view.Display; import android.view.View; import android.view.View.OnClickListener; +import android.view.WindowInsets.Type; +import android.view.WindowMetrics; import android.widget.Toast; import androidx.annotation.Nullable; @@ -51,6 +54,7 @@ import com.android.launcher3.util.DefaultDisplay.Info; import com.android.launcher3.util.PackageManagerHelper; import com.android.launcher3.util.Themes; import com.android.launcher3.util.TraceHelper; +import com.android.launcher3.util.WindowBounds; /** * Extension of BaseActivity allowing support for drag-n-drop @@ -272,15 +276,19 @@ public abstract class BaseDraggingActivity extends BaseActivity protected abstract void reapplyUi(); - protected Rect getMultiWindowDisplaySize() { + protected WindowBounds getMultiWindowDisplaySize() { if (Utilities.ATLEAST_R) { - return new Rect(getWindowManager().getCurrentWindowMetrics().getBounds()); + WindowMetrics wm = getWindowManager().getCurrentWindowMetrics(); + + Insets insets = wm.getWindowInsets().getInsets(Type.systemBars()); + return new WindowBounds(wm.getBounds(), + new Rect(insets.left, insets.top, insets.right, insets.bottom)); } // Note: Calls to getSize() can't rely on our cached DefaultDisplay since it can return // the app window size Display display = getWindowManager().getDefaultDisplay(); Point mwSize = new Point(); display.getSize(mwSize); - return new Rect(0, 0, mwSize.x, mwSize.y); + return new WindowBounds(new Rect(0, 0, mwSize.x, mwSize.y), new Rect()); } } diff --git a/src/com/android/launcher3/DeviceProfile.java b/src/com/android/launcher3/DeviceProfile.java index 51b21aa795..72831f4d09 100644 --- a/src/com/android/launcher3/DeviceProfile.java +++ b/src/com/android/launcher3/DeviceProfile.java @@ -29,6 +29,7 @@ import com.android.launcher3.graphics.IconShape; import com.android.launcher3.icons.DotRenderer; import com.android.launcher3.icons.IconNormalizer; import com.android.launcher3.util.DefaultDisplay; +import com.android.launcher3.util.WindowBounds; public class DeviceProfile { @@ -265,19 +266,16 @@ public class DeviceProfile { /** * TODO: Move this to the builder as part of setMultiWindowMode */ - public DeviceProfile getMultiWindowProfile(Context context, Rect windowPosition) { + public DeviceProfile getMultiWindowProfile(Context context, WindowBounds windowBounds) { // We take the minimum sizes of this profile and it's multi-window variant to ensure that // the system decor is always excluded. - Point mwSize = new Point(Math.min(availableWidthPx, windowPosition.width()), - Math.min(availableHeightPx, windowPosition.height())); + Point mwSize = new Point(Math.min(availableWidthPx, windowBounds.availableSize.x), + Math.min(availableHeightPx, windowBounds.availableSize.y)); - // In multi-window mode, we can have widthPx = availableWidthPx - // and heightPx = availableHeightPx because Launcher uses the InvariantDeviceProfiles' - // widthPx and heightPx values where it's needed. DeviceProfile profile = toBuilder(context) .setSizeRange(mwSize, mwSize) - .setSize(mwSize.x, mwSize.y) - .setWindowPosition(windowPosition.left, windowPosition.top) + .setSize(windowBounds.bounds.width(), windowBounds.bounds.height()) + .setWindowPosition(windowBounds.bounds.left, windowBounds.bounds.top) .setMultiWindowMode(true) .build(); @@ -299,7 +297,7 @@ public class DeviceProfile { } /** - * Inverse of {@link #getMultiWindowProfile(Context, Rect)} + * Inverse of {@link #getMultiWindowProfile(Context, WindowBounds)} * @return device profile corresponding to the current orientation in non multi-window mode. */ public DeviceProfile getFullScreenProfile() { diff --git a/src/com/android/launcher3/InvariantDeviceProfile.java b/src/com/android/launcher3/InvariantDeviceProfile.java index 8951674879..60abc66b2b 100644 --- a/src/com/android/launcher3/InvariantDeviceProfile.java +++ b/src/com/android/launcher3/InvariantDeviceProfile.java @@ -200,7 +200,7 @@ public class InvariantDeviceProfile { DefaultDisplay.INSTANCE.get(context).getInfo(), getPredefinedDeviceProfiles(context, gridName)); - Info myInfo = new Info(display); + Info myInfo = new Info(context, display); DisplayOption myDisplayOption = invDistWeightedInterpolate( myInfo, getPredefinedDeviceProfiles(context, gridName)); diff --git a/src/com/android/launcher3/LauncherSettings.java b/src/com/android/launcher3/LauncherSettings.java index 67890726b1..2cb3910793 100644 --- a/src/com/android/launcher3/LauncherSettings.java +++ b/src/com/android/launcher3/LauncherSettings.java @@ -154,9 +154,9 @@ public class LauncherSettings { public static final int CONTAINER_HOTSEAT_PREDICTION = -103; public static final int CONTAINER_ALL_APPS = -104; public static final int CONTAINER_WIDGETS_TRAY = -105; - // Represents search results view. public static final int CONTAINER_SEARCH_RESULTS = -106; + public static final int CONTAINER_SHORTCUTS = -107; public static final String containerToString(int container) { switch (container) { @@ -166,6 +166,7 @@ public class LauncherSettings { case CONTAINER_ALL_APPS: return "all_apps"; case CONTAINER_WIDGETS_TRAY: return "widgets_tray"; case CONTAINER_SEARCH_RESULTS: return "search_result"; + case CONTAINER_SHORTCUTS: return "shortcuts"; default: return String.valueOf(container); } } diff --git a/src/com/android/launcher3/allapps/AllAppsTransitionController.java b/src/com/android/launcher3/allapps/AllAppsTransitionController.java index aa9edda582..99ed0ad52b 100644 --- a/src/com/android/launcher3/allapps/AllAppsTransitionController.java +++ b/src/com/android/launcher3/allapps/AllAppsTransitionController.java @@ -6,6 +6,8 @@ import static com.android.launcher3.LauncherState.APPS_VIEW_ITEM_MASK; import static com.android.launcher3.LauncherState.OVERVIEW; import static com.android.launcher3.LauncherState.VERTICAL_SWIPE_INDICATOR; import static com.android.launcher3.anim.Interpolators.FAST_OUT_SLOW_IN; +import static com.android.launcher3.anim.Interpolators.FINAL_FRAME; +import static com.android.launcher3.anim.Interpolators.INSTANT; import static com.android.launcher3.anim.Interpolators.LINEAR; import static com.android.launcher3.anim.PropertySetter.NO_ANIM_PROPERTY_SETTER; import static com.android.launcher3.states.StateAnimationConfig.ANIM_ALL_APPS_FADE; @@ -227,7 +229,9 @@ public class AllAppsTransitionController implements StateHandler, setter.setInt(mScrimView, ScrimView.DRAG_HANDLE_ALPHA, (visibleElements & VERTICAL_SWIPE_INDICATOR) != 0 ? 255 : 0, allAppsFade); - setter.setViewAlpha(mAppsView, hasAnyVisibleItem ? 1 : 0, allAppsFade); + // Set visibility of the container at the very beginning or end of the transition. + setter.setViewAlpha(mAppsView, hasAnyVisibleItem ? 1 : 0, + hasAnyVisibleItem ? INSTANT : FINAL_FRAME); } public AnimatorListenerAdapter getProgressAnimatorListener() { diff --git a/src/com/android/launcher3/anim/Interpolators.java b/src/com/android/launcher3/anim/Interpolators.java index fccc120900..860ccebef4 100644 --- a/src/com/android/launcher3/anim/Interpolators.java +++ b/src/com/android/launcher3/anim/Interpolators.java @@ -61,6 +61,11 @@ public class Interpolators { public static final Interpolator EXAGGERATED_EASE; public static final Interpolator INSTANT = t -> 1; + /** + * All values of t map to 0 until t == 1. This is primarily useful for setting view visibility, + * which should only happen at the very end of the animation (when it's already hidden). + */ + public static final Interpolator FINAL_FRAME = t -> t < 1 ? 0 : 1; private static final int MIN_SETTLE_DURATION = 200; private static final float OVERSHOOT_FACTOR = 0.9f; diff --git a/src/com/android/launcher3/folder/Folder.java b/src/com/android/launcher3/folder/Folder.java index f7fe535a48..fdf0ea46f2 100644 --- a/src/com/android/launcher3/folder/Folder.java +++ b/src/com/android/launcher3/folder/Folder.java @@ -466,6 +466,7 @@ public class Folder extends AbstractFloatingView implements ClipPathView, DragSo if (!isEmpty(firstLabel)) { mFolderName.setHint(""); mFolderName.setText(firstLabel); + mFolderName.selectAll(); } } mFolderName.showKeyboard(); diff --git a/src/com/android/launcher3/logging/StatsLogManager.java b/src/com/android/launcher3/logging/StatsLogManager.java index 4c2c79d6dd..dda39e8a51 100644 --- a/src/com/android/launcher3/logging/StatsLogManager.java +++ b/src/com/android/launcher3/logging/StatsLogManager.java @@ -32,7 +32,7 @@ import com.android.launcher3.util.ResourceBasedOverride; */ public class StatsLogManager implements ResourceBasedOverride { - interface EventEnum { + public interface EventEnum { int getId(); } @@ -63,6 +63,18 @@ public class StatsLogManager implements ResourceBasedOverride { + "new/same value.") LAUNCHER_FOLDER_LABEL_UPDATED(460), + @UiEvent(doc = "User long pressed on the workspace empty space.") + LAUNCHER_WORKSPACE_LONGPRESS(461), + + @UiEvent(doc = "User tapped or long pressed on a wallpaper icon inside launcher settings.") + LAUNCHER_WALLPAPER_BUTTON_TAP_OR_LONGPRESS(462), + + @UiEvent(doc = "User tapped or long pressed on settings icon inside launcher settings.") + LAUNCHER_SETTINGS_BUTTON_TAP_OR_LONGPRESS(463), + + @UiEvent(doc = "User tapped or long pressed on widget tray icon inside launcher settings.") + LAUNCHER_WIDGETSTRAY_BUTTON_TAP_OR_LONGPRESS(464), + @UiEvent(doc = "A dragged item is dropped on 'Remove' button in the target bar") LAUNCHER_ITEM_DROPPED_ON_REMOVE(465), @@ -82,7 +94,15 @@ public class StatsLogManager implements ResourceBasedOverride { @UiEvent(doc = "User cancelled uninstalling the package after dropping on " + "the icon onto 'Uninstall' button in the target bar") - LAUNCHER_ITEM_UNINSTALL_CANCELLED(470); + LAUNCHER_ITEM_UNINSTALL_CANCELLED(470), + + @UiEvent(doc = "User opened package specific widgets list by tapping on widgets system " + + "shortcut within longpress popup window.") + LAUNCHER_SYSTEM_SHORTCUT_WIDGETS_TAP(514), + + @UiEvent(doc = "User opened app info of the package by tapping on appinfo system shortcut " + + "within longpress popup window.") + LAUNCHER_SYSTEM_SHORTCUT_APP_INFO_TAP(515); // ADD MORE private final int mId; @@ -113,27 +133,27 @@ public class StatsLogManager implements ResourceBasedOverride { } /** - * Logs a {@link LauncherEvent}. + * Logs a {@link EventEnum}. */ - public void log(LauncherEvent event) { + public void log(EventEnum event) { } /** * Logs an event and accompanying {@link InstanceId}. */ - public void log(LauncherEvent event, InstanceId instanceId) { + public void log(EventEnum event, InstanceId instanceId) { } /** * Logs an event and accompanying {@link ItemInfo}. */ - public void log(LauncherEvent event, @Nullable ItemInfo info) { + public void log(EventEnum event, @Nullable ItemInfo itemInfo) { } /** * Logs an event and accompanying {@link InstanceId} and {@link ItemInfo}. */ - public void log(LauncherEvent event, InstanceId instanceId, @Nullable ItemInfo info) { + public void log(EventEnum event, InstanceId instanceId, @Nullable ItemInfo itemInfo) { } /** diff --git a/src/com/android/launcher3/model/data/ItemInfo.java b/src/com/android/launcher3/model/data/ItemInfo.java index 3a89236138..0c815d145a 100644 --- a/src/com/android/launcher3/model/data/ItemInfo.java +++ b/src/com/android/launcher3/model/data/ItemInfo.java @@ -22,6 +22,7 @@ import static com.android.launcher3.LauncherSettings.Favorites.CONTAINER_HOTSEAT import static com.android.launcher3.LauncherSettings.Favorites.CONTAINER_HOTSEAT_PREDICTION; import static com.android.launcher3.LauncherSettings.Favorites.CONTAINER_PREDICTION; import static com.android.launcher3.LauncherSettings.Favorites.CONTAINER_SEARCH_RESULTS; +import static com.android.launcher3.LauncherSettings.Favorites.CONTAINER_SHORTCUTS; import static com.android.launcher3.LauncherSettings.Favorites.CONTAINER_WIDGETS_TRAY; import static com.android.launcher3.LauncherSettings.Favorites.ITEM_TYPE_APPLICATION; import static com.android.launcher3.LauncherSettings.Favorites.ITEM_TYPE_APPWIDGET; @@ -45,6 +46,7 @@ import com.android.launcher3.logger.LauncherAtom.AllAppsContainer; import com.android.launcher3.logger.LauncherAtom.ContainerInfo; import com.android.launcher3.logger.LauncherAtom.PredictionContainer; import com.android.launcher3.logger.LauncherAtom.SearchResultContainer; +import com.android.launcher3.logger.LauncherAtom.ShortcutsContainer; import com.android.launcher3.util.ContentWriter; import java.util.Optional; @@ -363,6 +365,10 @@ public class ItemInfo { return ContainerInfo.newBuilder() .setSearchResultContainer(SearchResultContainer.getDefaultInstance()) .build(); + case CONTAINER_SHORTCUTS: + return ContainerInfo.newBuilder() + .setShortcutsContainer(ShortcutsContainer.getDefaultInstance()) + .build(); } return ContainerInfo.getDefaultInstance(); } diff --git a/src/com/android/launcher3/popup/PopupContainerWithArrow.java b/src/com/android/launcher3/popup/PopupContainerWithArrow.java index 331298f414..614cf1401d 100644 --- a/src/com/android/launcher3/popup/PopupContainerWithArrow.java +++ b/src/com/android/launcher3/popup/PopupContainerWithArrow.java @@ -16,6 +16,7 @@ package com.android.launcher3.popup; +import static com.android.launcher3.LauncherSettings.Favorites.CONTAINER_SHORTCUTS; import static com.android.launcher3.Utilities.squaredHypot; import static com.android.launcher3.Utilities.squaredTouchSlop; import static com.android.launcher3.logging.LoggerUtils.newContainerTarget; @@ -61,6 +62,7 @@ import com.android.launcher3.dragndrop.DragView; import com.android.launcher3.dragndrop.DraggableView; import com.android.launcher3.model.data.ItemInfo; import com.android.launcher3.model.data.ItemInfoWithIcon; +import com.android.launcher3.model.data.WorkspaceItemInfo; import com.android.launcher3.notification.NotificationInfo; import com.android.launcher3.notification.NotificationItemView; import com.android.launcher3.notification.NotificationKeyData; @@ -675,8 +677,10 @@ public class PopupContainerWithArrow extends Arr iconShift.y = mIconLastTouchPos.y - mLauncher.getDeviceProfile().iconSizePx; DraggableView draggableView = DraggableView.ofType(DraggableView.DRAGGABLE_ICON); + WorkspaceItemInfo itemInfo = sv.getFinalInfo(); + itemInfo.container = CONTAINER_SHORTCUTS; DragView dv = mLauncher.getWorkspace().beginDragShared(sv.getIconView(), draggableView, - mContainer, sv.getFinalInfo(), + mContainer, itemInfo, new ShortcutDragPreviewProvider(sv.getIconView(), iconShift), new DragOptions()); dv.animateShift(-iconShift.x, -iconShift.y); diff --git a/src/com/android/launcher3/popup/PopupPopulator.java b/src/com/android/launcher3/popup/PopupPopulator.java index 7da86cc0af..6d3bc1451a 100644 --- a/src/com/android/launcher3/popup/PopupPopulator.java +++ b/src/com/android/launcher3/popup/PopupPopulator.java @@ -16,6 +16,8 @@ package com.android.launcher3.popup; +import static com.android.launcher3.LauncherSettings.Favorites.CONTAINER_SHORTCUTS; + import android.content.ComponentName; import android.content.pm.ShortcutInfo; import android.os.Handler; @@ -160,6 +162,7 @@ public class PopupPopulator { final WorkspaceItemInfo si = new WorkspaceItemInfo(shortcut, launcher); cache.getUnbadgedShortcutIcon(si, shortcut); si.rank = i; + si.container = CONTAINER_SHORTCUTS; final DeepShortcutView view = shortcutViews.get(i); uiHandler.post(() -> view.applyShortcutInfo(si, shortcut, container)); diff --git a/src/com/android/launcher3/popup/SystemShortcut.java b/src/com/android/launcher3/popup/SystemShortcut.java index ae35d4c594..58ed5e8132 100644 --- a/src/com/android/launcher3/popup/SystemShortcut.java +++ b/src/com/android/launcher3/popup/SystemShortcut.java @@ -1,5 +1,7 @@ package com.android.launcher3.popup; +import static com.android.launcher3.logging.StatsLogManager.LauncherEvent.LAUNCHER_SYSTEM_SHORTCUT_APP_INFO_TAP; +import static com.android.launcher3.logging.StatsLogManager.LauncherEvent.LAUNCHER_SYSTEM_SHORTCUT_WIDGETS_TAP; import android.app.ActivityOptions; import android.content.Context; @@ -103,7 +105,6 @@ public abstract class SystemShortcut extends Ite }; public static class Widgets extends SystemShortcut { - public Widgets(Launcher target, ItemInfo itemInfo) { super(R.drawable.ic_widget, R.string.widget_button_text, target, itemInfo); } @@ -117,6 +118,9 @@ public abstract class SystemShortcut extends Ite widgetsBottomSheet.populateAndShow(mItemInfo); mTarget.getUserEventDispatcher().logActionOnControl(Action.Touch.TAP, ControlType.WIDGETS_BUTTON, view); + // TODO(thiruram): Fix missing container info when item is inside folder. + mTarget.getStatsLogManager().log(LAUNCHER_SYSTEM_SHORTCUT_WIDGETS_TAP, + mItemInfo.buildProto()); } } @@ -137,6 +141,9 @@ public abstract class SystemShortcut extends Ite mItemInfo, sourceBounds, ActivityOptions.makeBasic().toBundle()); mTarget.getUserEventDispatcher().logActionOnControl(Action.Touch.TAP, ControlType.APPINFO_TARGET, view); + // TODO(thiruram): Fix missing container info when item is inside folder. + mTarget.getStatsLogManager().log(LAUNCHER_SYSTEM_SHORTCUT_APP_INFO_TAP, + mItemInfo.buildProto()); } } diff --git a/src/com/android/launcher3/touch/WorkspaceTouchListener.java b/src/com/android/launcher3/touch/WorkspaceTouchListener.java index e6de06d6f0..7270ce28fb 100644 --- a/src/com/android/launcher3/touch/WorkspaceTouchListener.java +++ b/src/com/android/launcher3/touch/WorkspaceTouchListener.java @@ -22,6 +22,7 @@ import static android.view.MotionEvent.ACTION_POINTER_UP; import static android.view.MotionEvent.ACTION_UP; import static com.android.launcher3.LauncherState.NORMAL; +import static com.android.launcher3.logging.StatsLogManager.LauncherEvent.LAUNCHER_WORKSPACE_LONGPRESS; import android.graphics.PointF; import android.graphics.Rect; @@ -41,8 +42,6 @@ import com.android.launcher3.dragndrop.DragLayer; import com.android.launcher3.testing.TestLogging; import com.android.launcher3.testing.TestProtocol; import com.android.launcher3.views.OptionsPopupView; -import com.android.launcher3.userevent.nano.LauncherLogProto.Action; -import com.android.launcher3.userevent.nano.LauncherLogProto.ContainerType; /** * Helper class to handle touch on empty space in workspace and show options popup on long press @@ -175,9 +174,7 @@ public class WorkspaceTouchListener extends GestureDetector.SimpleOnGestureListe mWorkspace.performHapticFeedback(HapticFeedbackConstants.LONG_PRESS, HapticFeedbackConstants.FLAG_IGNORE_VIEW_SETTING); - mLauncher.getUserEventDispatcher().logActionOnContainer(Action.Touch.LONGPRESS, - Action.Direction.NONE, ContainerType.WORKSPACE, - mWorkspace.getCurrentPage()); + mLauncher.getStatsLogManager().log(LAUNCHER_WORKSPACE_LONGPRESS); OptionsPopupView.showDefaultOptions(mLauncher, mTouchDownPoint.x, mTouchDownPoint.y); } else { cancelLongPress(); diff --git a/src/com/android/launcher3/util/DefaultDisplay.java b/src/com/android/launcher3/util/DefaultDisplay.java index fabdb4e86c..150fb5b043 100644 --- a/src/com/android/launcher3/util/DefaultDisplay.java +++ b/src/com/android/launcher3/util/DefaultDisplay.java @@ -145,10 +145,11 @@ public class DefaultDisplay implements DisplayListener { } private Info(Context context) { - this(context.getSystemService(DisplayManager.class).getDisplay(DEFAULT_DISPLAY)); + this(context, context.getSystemService(DisplayManager.class) + .getDisplay(DEFAULT_DISPLAY)); } - public Info(Display display) { + public Info(Context context, Display display) { id = display.getDisplayId(); rotation = display.getRotation(); @@ -161,8 +162,8 @@ public class DefaultDisplay implements DisplayListener { display.getRealSize(realSize); display.getCurrentSizeRange(smallestSize, largestSize); - metrics = new DisplayMetrics(); - display.getMetrics(metrics); + Context defaultDisplayContext = context.createDisplayContext(display); + metrics = defaultDisplayContext.getResources().getDisplayMetrics(); } private boolean hasDifferentSize(Info info) { diff --git a/src/com/android/launcher3/views/BaseDragLayer.java b/src/com/android/launcher3/views/BaseDragLayer.java index 6915953068..c37c47c0a0 100644 --- a/src/com/android/launcher3/views/BaseDragLayer.java +++ b/src/com/android/launcher3/views/BaseDragLayer.java @@ -181,6 +181,11 @@ public abstract class BaseDragLayer } private TouchController findControllerToHandleTouch(MotionEvent ev) { + if (TestProtocol.sDebugTracing) { + Log.d(TestProtocol.PAUSE_NOT_DETECTED, "findControllerToHandleTouch ev=" + ev + + ", isEventInLauncher=" + isEventInLauncher(ev) + + ", topOpenView=" + AbstractFloatingView.getTopOpenView(mActivity)); + } if (isEventInLauncher(ev)) { AbstractFloatingView topView = AbstractFloatingView.getTopOpenView(mActivity); if (topView != null && topView.onControllerInterceptTouchEvent(ev)) { diff --git a/src/com/android/launcher3/views/FloatingIconView.java b/src/com/android/launcher3/views/FloatingIconView.java index bd12e06b60..177aff4a89 100644 --- a/src/com/android/launcher3/views/FloatingIconView.java +++ b/src/com/android/launcher3/views/FloatingIconView.java @@ -59,6 +59,7 @@ import com.android.launcher3.icons.LauncherIcons; import com.android.launcher3.model.data.ItemInfo; import com.android.launcher3.popup.SystemShortcut; import com.android.launcher3.shortcuts.DeepShortcutView; +import com.android.launcher3.testing.TestProtocol; /** * A view that is created to look like another view with the purpose of creating fluid animations. @@ -560,6 +561,11 @@ public class FloatingIconView extends FrameLayout implements view.setVisibility(INVISIBLE); parent.addView(view); dragLayer.addView(view.mListenerView); + if (TestProtocol.sDebugTracing) { + Log.d(TestProtocol.PAUSE_NOT_DETECTED, "getFloatingIconView. listenerView " + + "added to dragLayer. listenerView=" + view.mListenerView + ", fiv=" + view, + new Exception()); + } view.mListenerView.setListener(view::fastFinish); view.mEndRunnable = () -> { @@ -639,6 +645,10 @@ public class FloatingIconView extends FrameLayout implements private void finish(DragLayer dragLayer) { ((ViewGroup) dragLayer.getParent()).removeView(this); dragLayer.removeView(mListenerView); + if (TestProtocol.sDebugTracing) { + Log.d(TestProtocol.PAUSE_NOT_DETECTED, "listenerView removed from dragLayer. " + + "listenerView=" + mListenerView + ", fiv=" + this, new Exception()); + } recycle(); mLauncher.getViewCache().recycleView(R.layout.floating_icon_view, this); } diff --git a/src/com/android/launcher3/views/ListenerView.java b/src/com/android/launcher3/views/ListenerView.java index 263f7c4c32..575f86454c 100644 --- a/src/com/android/launcher3/views/ListenerView.java +++ b/src/com/android/launcher3/views/ListenerView.java @@ -17,18 +17,20 @@ package com.android.launcher3.views; import android.content.Context; import android.util.AttributeSet; +import android.util.Log; import android.view.MotionEvent; import android.view.View; import android.view.ViewGroup; import com.android.launcher3.AbstractFloatingView; +import com.android.launcher3.testing.TestProtocol; /** * An invisible AbstractFloatingView that can run a callback when it is being closed. */ public class ListenerView extends AbstractFloatingView { - public Runnable mCloseListener; + private Runnable mCloseListener; public ListenerView(Context context, AttributeSet attrs) { super(context, attrs); @@ -36,12 +38,20 @@ public class ListenerView extends AbstractFloatingView { } public void setListener(Runnable listener) { + if (TestProtocol.sDebugTracing) { + Log.d(TestProtocol.PAUSE_NOT_DETECTED, "ListenerView setListener lv=" + this + + ", listener=" + listener, new Exception()); + } mCloseListener = listener; } @Override protected void onAttachedToWindow() { super.onAttachedToWindow(); + if (TestProtocol.sDebugTracing) { + Log.d(TestProtocol.PAUSE_NOT_DETECTED, "ListenerView onAttachedToWindow lv=" + this, + new Exception()); + } mIsOpen = true; } @@ -49,10 +59,19 @@ public class ListenerView extends AbstractFloatingView { protected void onDetachedFromWindow() { super.onDetachedFromWindow(); mIsOpen = false; + if (TestProtocol.sDebugTracing) { + Log.d(TestProtocol.PAUSE_NOT_DETECTED, "ListenerView onDetachedFromView lv=" + this, + new Exception()); + } } @Override protected void handleClose(boolean animate) { + if (TestProtocol.sDebugTracing) { + Log.d(TestProtocol.PAUSE_NOT_DETECTED, "ListenerView handeClose lv=" + this + + ", mIsOpen=" + mIsOpen + ", mCloseListener=" + mCloseListener + + ", getParent()=" + getParent(), new Exception()); + } if (mIsOpen) { if (mCloseListener != null) { mCloseListener.run(); @@ -77,6 +96,10 @@ public class ListenerView extends AbstractFloatingView { @Override public boolean onControllerInterceptTouchEvent(MotionEvent ev) { + if (TestProtocol.sDebugTracing) { + Log.d(TestProtocol.PAUSE_NOT_DETECTED, "ListenerView touchEvent lv=" + this + + ", ev=" + ev, new Exception()); + } if (ev.getAction() == MotionEvent.ACTION_DOWN) { handleClose(false); } diff --git a/src/com/android/launcher3/views/OptionsPopupView.java b/src/com/android/launcher3/views/OptionsPopupView.java index d5c3c1d30e..7467186721 100644 --- a/src/com/android/launcher3/views/OptionsPopupView.java +++ b/src/com/android/launcher3/views/OptionsPopupView.java @@ -17,6 +17,9 @@ package com.android.launcher3.views; import static com.android.launcher3.Utilities.EXTRA_WALLPAPER_FLAVOR; import static com.android.launcher3.Utilities.EXTRA_WALLPAPER_OFFSET; +import static com.android.launcher3.logging.StatsLogManager.LauncherEvent.LAUNCHER_SETTINGS_BUTTON_TAP_OR_LONGPRESS; +import static com.android.launcher3.logging.StatsLogManager.LauncherEvent.LAUNCHER_WALLPAPER_BUTTON_TAP_OR_LONGPRESS; +import static com.android.launcher3.logging.StatsLogManager.LauncherEvent.LAUNCHER_WIDGETSTRAY_BUTTON_TAP_OR_LONGPRESS; import android.content.Context; import android.content.Intent; @@ -37,13 +40,12 @@ import androidx.annotation.VisibleForTesting; import com.android.launcher3.Launcher; import com.android.launcher3.R; import com.android.launcher3.Utilities; +import com.android.launcher3.logging.StatsLogManager.EventEnum; import com.android.launcher3.model.WidgetsModel; import com.android.launcher3.popup.ArrowPopup; import com.android.launcher3.shortcuts.DeepShortcutView; import com.android.launcher3.testing.TestLogging; import com.android.launcher3.testing.TestProtocol; -import com.android.launcher3.userevent.nano.LauncherLogProto.Action; -import com.android.launcher3.userevent.nano.LauncherLogProto.ControlType; import com.android.launcher3.widget.WidgetsFullSheet; import java.util.ArrayList; @@ -68,21 +70,21 @@ public class OptionsPopupView extends ArrowPopup @Override public void onClick(View view) { - handleViewClick(view, Action.Touch.TAP); + handleViewClick(view); } @Override public boolean onLongClick(View view) { - return handleViewClick(view, Action.Touch.LONGPRESS); + return handleViewClick(view); } - private boolean handleViewClick(View view, int action) { + private boolean handleViewClick(View view) { OptionItem item = mItemMap.get(view); if (item == null) { return false; } - if (item.mControlTypeForLog > 0) { - logTap(action, item.mControlTypeForLog); + if (item.mEventId.getId() > 0) { + mLauncher.getStatsLogManager().log(item.mEventId); } if (item.mClickListener.onLongClick(view)) { close(true); @@ -91,10 +93,6 @@ public class OptionsPopupView extends ArrowPopup return false; } - private void logTap(int action, int controlType) { - mLauncher.getUserEventDispatcher().logActionOnControl(action, controlType); - } - @Override public boolean onControllerInterceptTouchEvent(MotionEvent ev) { if (ev.getAction() != MotionEvent.ACTION_DOWN) { @@ -159,13 +157,16 @@ public class OptionsPopupView extends ArrowPopup int resDrawable = Utilities.existsStyleWallpapers(launcher) ? R.drawable.ic_palette : R.drawable.ic_wallpaper; options.add(new OptionItem(resString, resDrawable, - ControlType.WALLPAPER_BUTTON, OptionsPopupView::startWallpaperPicker)); + LAUNCHER_WALLPAPER_BUTTON_TAP_OR_LONGPRESS, + OptionsPopupView::startWallpaperPicker)); if (!WidgetsModel.GO_DISABLE_WIDGETS) { options.add(new OptionItem(R.string.widget_button_text, R.drawable.ic_widget, - ControlType.WIDGETS_BUTTON, OptionsPopupView::onWidgetsClicked)); + LAUNCHER_WIDGETSTRAY_BUTTON_TAP_OR_LONGPRESS, + OptionsPopupView::onWidgetsClicked)); } options.add(new OptionItem(R.string.settings_button_text, R.drawable.ic_setting, - ControlType.SETTINGS_BUTTON, OptionsPopupView::startSettings)); + LAUNCHER_SETTINGS_BUTTON_TAP_OR_LONGPRESS, + OptionsPopupView::startSettings)); show(launcher, target, options); } @@ -224,14 +225,14 @@ public class OptionsPopupView extends ArrowPopup private final int mLabelRes; private final int mIconRes; - private final int mControlTypeForLog; + private final EventEnum mEventId; private final OnLongClickListener mClickListener; - public OptionItem(int labelRes, int iconRes, int controlTypeForLog, + public OptionItem(int labelRes, int iconRes, EventEnum eventId, OnLongClickListener clickListener) { mLabelRes = labelRes; mIconRes = iconRes; - mControlTypeForLog = controlTypeForLog; + mEventId = eventId; mClickListener = clickListener; } } diff --git a/src_ui_overrides/com/android/launcher3/uioverrides/PreviewSurfaceRenderer.java b/src_ui_overrides/com/android/launcher3/uioverrides/PreviewSurfaceRenderer.java deleted file mode 100644 index 4913cadb5a..0000000000 --- a/src_ui_overrides/com/android/launcher3/uioverrides/PreviewSurfaceRenderer.java +++ /dev/null @@ -1,27 +0,0 @@ -/* - * Copyright (C) 2020 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package com.android.launcher3.uioverrides; - -import android.content.Context; -import android.os.Bundle; - -/** Render preview using surface view. */ -public class PreviewSurfaceRenderer { - - /** Handle a received surface view request. */ - public static void render(Context context, Bundle bundle) { } -} -- GitLab From f903d2c910e76432ebbaa3a803b8d4e817f9ea7f Mon Sep 17 00:00:00 2001 From: Adam Cohen Date: Wed, 3 Jun 2020 18:56:27 -0400 Subject: [PATCH 0009/1664] Merging from ub-launcher3-rvc-dev @ build 6557059 Bug:150504032 Test: manual, presubmit on the source branch x20/teams/android-launcher/merge/ub-launcher3-rvc-dev_rvc-dev_6557059.html Change-Id: I280025d9e58626fe725fe24ca60c28e89d0cee74 --- .../hybridhotseat/HotseatEduController.java | 8 +- .../hybridhotseat/HotseatEduDialog.java | 3 +- .../HotseatPredictionController.java | 10 +- .../hybridhotseat/HotseatRestoreHelper.java | 100 +++++++ .../QuickstepAtomicAnimationFactory.java | 2 + .../AppToOverviewAnimationProvider.java | 5 +- .../android/quickstep/BaseSwipeUpHandler.java | 21 +- .../quickstep/LauncherActivityInterface.java | 14 +- .../quickstep/TaskShortcutFactory.java | 26 +- .../com/android/quickstep/TaskViewUtils.java | 7 +- .../quickstep/TouchInteractionService.java | 48 ++-- .../quickstep/util/RectFSpringAnim.java | 9 +- .../util/StaggeredWorkspaceAnim.java | 7 +- .../util/SurfaceTransactionApplier.java | 136 ++++++++++ .../quickstep/util/TransformParams.java | 4 +- .../quickstep/views/OverviewActionsView.java | 19 +- .../android/quickstep/views/RecentsView.java | 11 +- .../com/android/quickstep/views/TaskView.java | 6 +- .../res/layout/gesture_tutorial_fragment.xml | 3 +- quickstep/res/values/colors.xml | 1 + quickstep/res/values/strings.xml | 14 +- quickstep/res/values/styles.xml | 6 +- .../launcher3/BaseQuickstepLauncher.java | 4 +- .../QuickstepAppTransitionManagerImpl.java | 14 +- .../launcher3/model/WellbeingModel.java | 8 +- .../statehandlers/DepthController.java | 4 +- .../OrientationTouchTransformer.java | 11 + .../RecentsAnimationDeviceState.java | 8 +- .../quickstep/RemoteAnimationTargets.java | 73 ++++-- .../com/android/quickstep/SystemUiProxy.java | 5 + .../BackGestureTutorialController.java | 8 - .../interaction/EdgeBackGesturePanel.java | 6 +- .../HomeGestureTutorialController.java | 232 +---------------- .../OverviewGestureTutorialController.java | 124 +++++++++ .../OverviewGestureTutorialFragment.java | 37 +++ .../SwipeUpGestureTutorialController.java | 246 ++++++++++++++++++ .../interaction/TutorialController.java | 19 +- .../interaction/TutorialFragment.java | 3 + res/drawable/gesture_tutorial_ripple.xml | 5 +- res/values/colors.xml | 6 - res/values/styles.xml | 1 - .../config/robolectric.properties | 2 +- ...a => ShadowSurfaceTransactionApplier.java} | 6 +- .../android/launcher3/LauncherProvider.java | 12 + .../android/launcher3/LauncherSettings.java | 7 + .../launcher3/allapps/SearchUiManager.java | 9 + .../search/AppsSearchContainerLayout.java | 3 +- .../launcher3/config/FeatureFlags.java | 2 +- .../launcher3/logging/StatsLogManager.java | 12 +- .../launcher3/model/GridBackupTable.java | 26 ++ .../settings/DeveloperOptionsFragment.java | 9 + .../states/StateAnimationConfig.java | 6 +- .../launcher3/testing/TestProtocol.java | 1 + 53 files changed, 1018 insertions(+), 351 deletions(-) create mode 100644 quickstep/recents_ui_overrides/src/com/android/launcher3/hybridhotseat/HotseatRestoreHelper.java create mode 100644 quickstep/recents_ui_overrides/src/com/android/quickstep/util/SurfaceTransactionApplier.java create mode 100644 quickstep/src/com/android/quickstep/interaction/OverviewGestureTutorialController.java create mode 100644 quickstep/src/com/android/quickstep/interaction/OverviewGestureTutorialFragment.java create mode 100644 quickstep/src/com/android/quickstep/interaction/SwipeUpGestureTutorialController.java rename robolectric_tests/src/com/android/launcher3/shadows/{ShadowSyncRtSurfaceTransactionApplierCompat.java => ShadowSurfaceTransactionApplier.java} (83%) diff --git a/quickstep/recents_ui_overrides/src/com/android/launcher3/hybridhotseat/HotseatEduController.java b/quickstep/recents_ui_overrides/src/com/android/launcher3/hybridhotseat/HotseatEduController.java index 7c4f3ecd01..f1ce72e708 100644 --- a/quickstep/recents_ui_overrides/src/com/android/launcher3/hybridhotseat/HotseatEduController.java +++ b/quickstep/recents_ui_overrides/src/com/android/launcher3/hybridhotseat/HotseatEduController.java @@ -52,6 +52,7 @@ public class HotseatEduController { private final Launcher mLauncher; private final Hotseat mHotseat; + private final HotseatRestoreHelper mRestoreHelper; private List mPredictedApps; private HotseatEduDialog mActiveDialog; @@ -59,9 +60,10 @@ public class HotseatEduController { private IntArray mNewScreens = null; private Runnable mOnOnboardingComplete; - HotseatEduController(Launcher launcher, Runnable runnable) { + HotseatEduController(Launcher launcher, HotseatRestoreHelper restoreHelper, Runnable runnable) { mLauncher = launcher; mHotseat = launcher.getHotseat(); + mRestoreHelper = restoreHelper; mOnOnboardingComplete = runnable; } @@ -69,11 +71,14 @@ public class HotseatEduController { * Checks what type of migration should be used and migrates hotseat */ void migrate() { + mRestoreHelper.createBackup(); if (FeatureFlags.HOTSEAT_MIGRATE_TO_FOLDER.get()) { migrateToFolder(); } else { migrateHotseatWhole(); } + Snackbar.show(mLauncher, R.string.hotsaet_tip_prediction_enabled, R.string.hotseat_turn_off, + null, () -> mLauncher.startActivity(new Intent(SETTINGS_ACTION))); } /** @@ -84,7 +89,6 @@ public class HotseatEduController { */ private int migrateToFolder() { ArrayDeque folders = new ArrayDeque<>(); - ArrayList putIntoFolder = new ArrayList<>(); //separate folders and items that can get in folders diff --git a/quickstep/recents_ui_overrides/src/com/android/launcher3/hybridhotseat/HotseatEduDialog.java b/quickstep/recents_ui_overrides/src/com/android/launcher3/hybridhotseat/HotseatEduDialog.java index 66c60bca1f..fdd3562a2e 100644 --- a/quickstep/recents_ui_overrides/src/com/android/launcher3/hybridhotseat/HotseatEduDialog.java +++ b/quickstep/recents_ui_overrides/src/com/android/launcher3/hybridhotseat/HotseatEduDialog.java @@ -16,7 +16,8 @@ package com.android.launcher3.hybridhotseat; import static com.android.launcher3.logging.LoggerUtils.newLauncherEvent; -import static com.android.launcher3.userevent.nano.LauncherLogProto.ControlType.HYBRID_HOTSEAT_CANCELED; +import static com.android.launcher3.userevent.nano.LauncherLogProto.ControlType + .HYBRID_HOTSEAT_CANCELED; import android.animation.PropertyValuesHolder; import android.content.Context; diff --git a/quickstep/recents_ui_overrides/src/com/android/launcher3/hybridhotseat/HotseatPredictionController.java b/quickstep/recents_ui_overrides/src/com/android/launcher3/hybridhotseat/HotseatPredictionController.java index 05bcb57adc..725f516335 100644 --- a/quickstep/recents_ui_overrides/src/com/android/launcher3/hybridhotseat/HotseatPredictionController.java +++ b/quickstep/recents_ui_overrides/src/com/android/launcher3/hybridhotseat/HotseatPredictionController.java @@ -92,6 +92,8 @@ public class HotseatPredictionController implements DragController.DragListener, private Launcher mLauncher; private final Hotseat mHotseat; + private final HotseatRestoreHelper mRestoreHelper; + private List mComponentKeyMappers = new ArrayList<>(); private DynamicItemCache mDynamicItemCache; @@ -129,6 +131,7 @@ public class HotseatPredictionController implements DragController.DragListener, mHotSeatItemsCount = mLauncher.getDeviceProfile().inv.numHotseatIcons; launcher.getDeviceProfile().inv.addOnChangeListener(this); mHotseat.addOnAttachStateChangeListener(this); + mRestoreHelper = new HotseatRestoreHelper(mLauncher); if (mHotseat.isAttachedToWindow()) { onViewAttachedToWindow(mHotseat); } @@ -297,7 +300,8 @@ public class HotseatPredictionController implements DragController.DragListener, }); setPauseUIUpdate(false); if (!isEduSeen()) { - mHotseatEduController = new HotseatEduController(mLauncher, this::createPredictor); + mHotseatEduController = new HotseatEduController(mLauncher, mRestoreHelper, + this::createPredictor); } } @@ -320,9 +324,11 @@ public class HotseatPredictionController implements DragController.DragListener, updateDependencies(); bindItems(items, false, null); } - private void setPredictedApps(List appTargets) { mComponentKeyMappers.clear(); + if (appTargets.isEmpty() && mRestoreHelper.shouldRestoreToBackup()) { + mRestoreHelper.restoreBackup(); + } StringBuilder predictionLog = new StringBuilder("predictedApps: [\n"); ArrayList componentKeys = new ArrayList<>(); for (AppTarget appTarget : appTargets) { diff --git a/quickstep/recents_ui_overrides/src/com/android/launcher3/hybridhotseat/HotseatRestoreHelper.java b/quickstep/recents_ui_overrides/src/com/android/launcher3/hybridhotseat/HotseatRestoreHelper.java new file mode 100644 index 0000000000..c95ff7a2d1 --- /dev/null +++ b/quickstep/recents_ui_overrides/src/com/android/launcher3/hybridhotseat/HotseatRestoreHelper.java @@ -0,0 +1,100 @@ +/* + * Copyright (C) 2020 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.android.launcher3.hybridhotseat; + +import static com.android.launcher3.LauncherSettings.Favorites.HYBRID_HOTSEAT_BACKUP_TABLE; +import static com.android.launcher3.provider.LauncherDbUtils.tableExists; + +import com.android.launcher3.InvariantDeviceProfile; +import com.android.launcher3.Launcher; +import com.android.launcher3.LauncherSettings; +import com.android.launcher3.model.GridBackupTable; +import com.android.launcher3.provider.LauncherDbUtils; + +/** + * A helper class to manage migration revert restoration for hybrid hotseat + */ +public class HotseatRestoreHelper { + private final Launcher mLauncher; + private boolean mBackupExists; + + HotseatRestoreHelper(Launcher context) { + mLauncher = context; + setupBackupTable(); + } + + /** + * Creates a snapshot backup of Favorite table for future restoration use. + */ + public synchronized void createBackup() { + try (LauncherDbUtils.SQLiteTransaction transaction = (LauncherDbUtils.SQLiteTransaction) + LauncherSettings.Settings.call( + mLauncher.getContentResolver(), + LauncherSettings.Settings.METHOD_NEW_TRANSACTION) + .getBinder(LauncherSettings.Settings.EXTRA_VALUE)) { + InvariantDeviceProfile idp = mLauncher.getDeviceProfile().inv; + GridBackupTable backupTable = new GridBackupTable(mLauncher, + transaction.getDb(), idp.numHotseatIcons, idp.numColumns, + idp.numRows); + backupTable.createCustomBackupTable(HYBRID_HOTSEAT_BACKUP_TABLE); + transaction.commit(); + LauncherSettings.Settings.call(mLauncher.getContentResolver(), + LauncherSettings.Settings.METHOD_REFRESH_HOTSEAT_RESTORE_TABLE); + mBackupExists = true; + } + } + + /** + * Finds and restores a previously saved snapshow of Favorites table + */ + public void restoreBackup() { + try (LauncherDbUtils.SQLiteTransaction transaction = (LauncherDbUtils.SQLiteTransaction) + LauncherSettings.Settings.call( + mLauncher.getContentResolver(), + LauncherSettings.Settings.METHOD_NEW_TRANSACTION) + .getBinder(LauncherSettings.Settings.EXTRA_VALUE)) { + if (!tableExists(transaction.getDb(), HYBRID_HOTSEAT_BACKUP_TABLE)) { + mBackupExists = false; + return; + } + InvariantDeviceProfile idp = mLauncher.getDeviceProfile().inv; + GridBackupTable backupTable = new GridBackupTable(mLauncher, + transaction.getDb(), idp.numHotseatIcons, idp.numColumns, + idp.numRows); + backupTable.restoreFromCustomBackupTable(HYBRID_HOTSEAT_BACKUP_TABLE, true); + transaction.commit(); + mBackupExists = false; + mLauncher.getModel().forceReload(); + } + } + + /** + * Returns if prediction controller should attempt restoring a backup + */ + public synchronized boolean shouldRestoreToBackup() { + return mBackupExists; + } + + private synchronized void setupBackupTable() { + try (LauncherDbUtils.SQLiteTransaction transaction = (LauncherDbUtils.SQLiteTransaction) + LauncherSettings.Settings.call( + mLauncher.getContentResolver(), + LauncherSettings.Settings.METHOD_NEW_TRANSACTION) + .getBinder(LauncherSettings.Settings.EXTRA_VALUE)) { + mBackupExists = tableExists(transaction.getDb(), HYBRID_HOTSEAT_BACKUP_TABLE); + } + } +} diff --git a/quickstep/recents_ui_overrides/src/com/android/launcher3/uioverrides/states/QuickstepAtomicAnimationFactory.java b/quickstep/recents_ui_overrides/src/com/android/launcher3/uioverrides/states/QuickstepAtomicAnimationFactory.java index a487869316..79dc3e25c3 100644 --- a/quickstep/recents_ui_overrides/src/com/android/launcher3/uioverrides/states/QuickstepAtomicAnimationFactory.java +++ b/quickstep/recents_ui_overrides/src/com/android/launcher3/uioverrides/states/QuickstepAtomicAnimationFactory.java @@ -37,6 +37,7 @@ import static com.android.launcher3.anim.Interpolators.OVERSHOOT_1_7; import static com.android.launcher3.anim.Interpolators.clampToProgress; import static com.android.launcher3.config.FeatureFlags.ENABLE_OVERVIEW_ACTIONS; import static com.android.launcher3.states.StateAnimationConfig.ANIM_ALL_APPS_FADE; +import static com.android.launcher3.states.StateAnimationConfig.ANIM_DEPTH; import static com.android.launcher3.states.StateAnimationConfig.ANIM_HOTSEAT_SCALE; import static com.android.launcher3.states.StateAnimationConfig.ANIM_HOTSEAT_TRANSLATE; import static com.android.launcher3.states.StateAnimationConfig.ANIM_OVERVIEW_FADE; @@ -210,6 +211,7 @@ public class QuickstepAtomicAnimationFactory extends } config.setInterpolator(ANIM_WORKSPACE_FADE, OVERSHOOT_1_2); config.setInterpolator(ANIM_OVERVIEW_SCALE, OVERSHOOT_1_2); + config.setInterpolator(ANIM_DEPTH, OVERSHOOT_1_2); Interpolator translationInterpolator = ENABLE_OVERVIEW_ACTIONS.get() && removeShelfFromOverview(mActivity) ? OVERSHOOT_1_2 diff --git a/quickstep/recents_ui_overrides/src/com/android/quickstep/AppToOverviewAnimationProvider.java b/quickstep/recents_ui_overrides/src/com/android/quickstep/AppToOverviewAnimationProvider.java index c18a0fdebb..e5782e7a45 100644 --- a/quickstep/recents_ui_overrides/src/com/android/quickstep/AppToOverviewAnimationProvider.java +++ b/quickstep/recents_ui_overrides/src/com/android/quickstep/AppToOverviewAnimationProvider.java @@ -33,11 +33,11 @@ import com.android.launcher3.anim.PendingAnimation; import com.android.launcher3.statehandlers.DepthController; import com.android.launcher3.statemanager.StatefulActivity; import com.android.quickstep.util.RemoteAnimationProvider; +import com.android.quickstep.util.SurfaceTransactionApplier; import com.android.quickstep.util.TaskViewSimulator; import com.android.quickstep.util.TransformParams; import com.android.quickstep.views.RecentsView; import com.android.systemui.shared.system.RemoteAnimationTargetCompat; -import com.android.systemui.shared.system.SyncRtSurfaceTransactionApplierCompat; /** * Provider for the atomic (for 3-button mode) remote window animation from the app to the overview. @@ -132,8 +132,7 @@ final class AppToOverviewAnimationProvider> extend TransformParams params = new TransformParams() .setTargetSet(targets) - .setSyncTransactionApplier( - new SyncRtSurfaceTransactionApplierCompat(mActivity.getRootView())); + .setSyncTransactionApplier(new SurfaceTransactionApplier(mActivity.getRootView())); AnimatedFloat recentsAlpha = new AnimatedFloat(() -> { }); params.setBaseBuilderProxy((builder, app, p) diff --git a/quickstep/recents_ui_overrides/src/com/android/quickstep/BaseSwipeUpHandler.java b/quickstep/recents_ui_overrides/src/com/android/quickstep/BaseSwipeUpHandler.java index 737d837ac7..6cbe794860 100644 --- a/quickstep/recents_ui_overrides/src/com/android/quickstep/BaseSwipeUpHandler.java +++ b/quickstep/recents_ui_overrides/src/com/android/quickstep/BaseSwipeUpHandler.java @@ -40,13 +40,14 @@ import com.android.launcher3.util.WindowBounds; import com.android.quickstep.RecentsAnimationCallbacks.RecentsAnimationListener; import com.android.quickstep.util.ActiveGestureLog; import com.android.quickstep.util.ActivityInitListener; +import com.android.quickstep.util.RectFSpringAnim; +import com.android.quickstep.util.SurfaceTransactionApplier; import com.android.quickstep.util.TransformParams; 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.system.InputConsumerController; import com.android.systemui.shared.system.RemoteAnimationTargetCompat; -import com.android.systemui.shared.system.SyncRtSurfaceTransactionApplierCompat; import java.util.ArrayList; import java.util.function.Consumer; @@ -114,10 +115,10 @@ public abstract class BaseSwipeUpHandler, Q extend public abstract Intent getLaunchIntent(); protected void linkRecentsViewScroll() { - SyncRtSurfaceTransactionApplierCompat.create(mRecentsView, applier -> { + SurfaceTransactionApplier.create(mRecentsView, applier -> { mTransformParams.setSyncTransactionApplier(applier); runOnRecentsAnimationStart(() -> - mRecentsAnimationTargets.addDependentTransactionApplier(applier)); + mRecentsAnimationTargets.addReleaseCheck(applier)); }); mRecentsView.setOnScrollChangeListener((v, scrollX, scrollY, oldScrollX, oldScrollY) -> { @@ -356,7 +357,8 @@ public abstract class BaseSwipeUpHandler, Q extend if (mWindowTransitionController != null) { float progress = mCurrentShift.value / mDragLengthFactor; mWindowTransitionController.setPlayFraction(progress); - + } + if (mRecentsAnimationTargets != null) { if (mRecentsViewScrollLinked) { mTaskViewSimulator.setScroll(mRecentsView.getScrollOffset()); } @@ -364,6 +366,17 @@ public abstract class BaseSwipeUpHandler, Q extend } } + @Override + protected RectFSpringAnim createWindowAnimationToHome(float startProgress, + HomeAnimationFactory homeAnimationFactory) { + RectFSpringAnim anim = + super.createWindowAnimationToHome(startProgress, homeAnimationFactory); + if (mRecentsAnimationTargets != null) { + mRecentsAnimationTargets.addReleaseCheck(anim); + } + return anim; + } + public interface Factory { BaseSwipeUpHandler newHandler( diff --git a/quickstep/recents_ui_overrides/src/com/android/quickstep/LauncherActivityInterface.java b/quickstep/recents_ui_overrides/src/com/android/quickstep/LauncherActivityInterface.java index 62eb235251..4801d71779 100644 --- a/quickstep/recents_ui_overrides/src/com/android/quickstep/LauncherActivityInterface.java +++ b/quickstep/recents_ui_overrides/src/com/android/quickstep/LauncherActivityInterface.java @@ -310,8 +310,8 @@ public final class LauncherActivityInterface extends @Override protected float getExtraSpace(Context context, DeviceProfile dp, PagedOrientationHandler orientationHandler) { - if (dp.isVerticalBarLayout() || - hideShelfInTwoButtonLandscape(context, orientationHandler)) { + if ((dp.isVerticalBarLayout() && !showOverviewActions(context)) + || hideShelfInTwoButtonLandscape(context, orientationHandler)) { return 0; } else { Resources res = context.getResources(); @@ -319,12 +319,14 @@ public final class LauncherActivityInterface extends //TODO: this needs to account for the swipe gesture height and accessibility // UI when shown. float actionsBottomMargin = 0; - if (getMode(context) == Mode.THREE_BUTTONS) { - actionsBottomMargin = res.getDimensionPixelSize( + if (!dp.isVerticalBarLayout()) { + if (getMode(context) == Mode.THREE_BUTTONS) { + actionsBottomMargin = res.getDimensionPixelSize( R.dimen.overview_actions_bottom_margin_three_button); - } else { - actionsBottomMargin = res.getDimensionPixelSize( + } else { + actionsBottomMargin = res.getDimensionPixelSize( R.dimen.overview_actions_bottom_margin_gesture); + } } float actionsHeight = actionsBottomMargin + res.getDimensionPixelSize(R.dimen.overview_actions_height); diff --git a/quickstep/recents_ui_overrides/src/com/android/quickstep/TaskShortcutFactory.java b/quickstep/recents_ui_overrides/src/com/android/quickstep/TaskShortcutFactory.java index 021d39dd6e..cdaa6555a0 100644 --- a/quickstep/recents_ui_overrides/src/com/android/quickstep/TaskShortcutFactory.java +++ b/quickstep/recents_ui_overrides/src/com/android/quickstep/TaskShortcutFactory.java @@ -18,6 +18,8 @@ package com.android.quickstep; import static android.view.Display.DEFAULT_DISPLAY; +import static com.android.launcher3.logging.StatsLogManager.LauncherEvent.LAUNCHER_SYSTEM_SHORTCUT_FREE_FORM_TAP; +import static com.android.launcher3.logging.StatsLogManager.LauncherEvent.LAUNCHER_SYSTEM_SHORTCUT_SPLIT_SCREEN_TAP; import static com.android.launcher3.userevent.nano.LauncherLogProto.Action.Touch.TAP; import android.app.Activity; @@ -35,6 +37,7 @@ import android.view.View; import com.android.launcher3.BaseDraggingActivity; import com.android.launcher3.DeviceProfile; import com.android.launcher3.R; +import com.android.launcher3.logging.StatsLogManager.LauncherEvent; import com.android.launcher3.model.WellbeingModel; import com.android.launcher3.model.data.WorkspaceItemInfo; import com.android.launcher3.popup.SystemShortcut; @@ -83,10 +86,12 @@ public interface TaskShortcutFactory { private final int mIconRes; private final int mTextRes; + private final LauncherEvent mLauncherEvent; - MultiWindowFactory(int iconRes, int textRes) { + MultiWindowFactory(int iconRes, int textRes, LauncherEvent launcherEvent) { mIconRes = iconRes; mTextRes = textRes; + mLauncherEvent = launcherEvent; } protected abstract boolean isAvailable(BaseDraggingActivity activity, int displayId); @@ -102,7 +107,8 @@ public interface TaskShortcutFactory { if (!isAvailable(activity, task.key.displayId)) { return null; } - return new MultiWindowSystemShortcut(mIconRes, mTextRes, activity, taskView, this); + return new MultiWindowSystemShortcut(mIconRes, mTextRes, activity, taskView, this, + mLauncherEvent); } } @@ -114,11 +120,12 @@ public interface TaskShortcutFactory { private final TaskThumbnailView mThumbnailView; private final TaskView mTaskView; private final MultiWindowFactory mFactory; + private final LauncherEvent mLauncherEvent; - public MultiWindowSystemShortcut(int iconRes, int textRes, - BaseDraggingActivity activity, TaskView taskView, MultiWindowFactory factory) { + public MultiWindowSystemShortcut(int iconRes, int textRes, BaseDraggingActivity activity, + TaskView taskView, MultiWindowFactory factory, LauncherEvent launcherEvent) { super(iconRes, textRes, activity, dummyInfo(taskView)); - + mLauncherEvent = launcherEvent; mHandler = new Handler(Looper.getMainLooper()); mTaskView = taskView; mRecentsView = activity.getOverviewPanel(); @@ -203,12 +210,13 @@ public interface TaskShortcutFactory { WindowManagerWrapper.getInstance().overridePendingAppTransitionMultiThumbFuture( future, animStartedListener, mHandler, true /* scaleUp */, taskKey.displayId); + mTarget.getStatsLogManager().log(mLauncherEvent, mTaskView.buildProto()); } } } - TaskShortcutFactory SPLIT_SCREEN = new MultiWindowFactory( - R.drawable.ic_split_screen, R.string.recent_task_option_split_screen) { + TaskShortcutFactory SPLIT_SCREEN = new MultiWindowFactory(R.drawable.ic_split_screen, + R.string.recent_task_option_split_screen, LAUNCHER_SYSTEM_SHORTCUT_SPLIT_SCREEN_TAP) { @Override protected boolean isAvailable(BaseDraggingActivity activity, int displayId) { @@ -241,8 +249,8 @@ public interface TaskShortcutFactory { } }; - TaskShortcutFactory FREE_FORM = new MultiWindowFactory( - R.drawable.ic_split_screen, R.string.recent_task_option_freeform) { + TaskShortcutFactory FREE_FORM = new MultiWindowFactory(R.drawable.ic_split_screen, + R.string.recent_task_option_freeform, LAUNCHER_SYSTEM_SHORTCUT_FREE_FORM_TAP) { @Override protected boolean isAvailable(BaseDraggingActivity activity, int displayId) { diff --git a/quickstep/recents_ui_overrides/src/com/android/quickstep/TaskViewUtils.java b/quickstep/recents_ui_overrides/src/com/android/quickstep/TaskViewUtils.java index c68d6e2e0d..e2e25f32d3 100644 --- a/quickstep/recents_ui_overrides/src/com/android/quickstep/TaskViewUtils.java +++ b/quickstep/recents_ui_overrides/src/com/android/quickstep/TaskViewUtils.java @@ -42,6 +42,7 @@ import com.android.launcher3.anim.PendingAnimation; import com.android.launcher3.model.data.ItemInfo; import com.android.launcher3.statehandlers.DepthController; import com.android.launcher3.util.DefaultDisplay; +import com.android.quickstep.util.SurfaceTransactionApplier; import com.android.quickstep.util.TaskViewSimulator; import com.android.quickstep.util.TransformParams; import com.android.quickstep.views.RecentsView; @@ -49,7 +50,6 @@ import com.android.quickstep.views.TaskThumbnailView; import com.android.quickstep.views.TaskView; import com.android.systemui.shared.recents.model.Task; import com.android.systemui.shared.system.RemoteAnimationTargetCompat; -import com.android.systemui.shared.system.SyncRtSurfaceTransactionApplierCompat; /** * Utility class for helpful methods related to {@link TaskView} objects and their tasks. @@ -128,11 +128,10 @@ public final class TaskViewUtils { RemoteAnimationTargetCompat[] wallpaperTargets, DepthController depthController, PendingAnimation out) { - SyncRtSurfaceTransactionApplierCompat applier = - new SyncRtSurfaceTransactionApplierCompat(v); + SurfaceTransactionApplier applier = new SurfaceTransactionApplier(v); final RemoteAnimationTargets targets = new RemoteAnimationTargets(appTargets, wallpaperTargets, MODE_OPENING); - targets.addDependentTransactionApplier(applier); + targets.addReleaseCheck(applier); TransformParams params = new TransformParams() .setSyncTransactionApplier(applier) diff --git a/quickstep/recents_ui_overrides/src/com/android/quickstep/TouchInteractionService.java b/quickstep/recents_ui_overrides/src/com/android/quickstep/TouchInteractionService.java index c8619bdd12..d6c65efe53 100644 --- a/quickstep/recents_ui_overrides/src/com/android/quickstep/TouchInteractionService.java +++ b/quickstep/recents_ui_overrides/src/com/android/quickstep/TouchInteractionService.java @@ -30,6 +30,7 @@ import static com.android.systemui.shared.system.QuickStepContract.KEY_EXTRA_SYS import static com.android.systemui.shared.system.QuickStepContract.SYSUI_STATE_TRACING_ENABLED; import android.annotation.TargetApi; +import android.app.ActivityManager; import android.app.PendingIntent; import android.app.RemoteAction; import android.app.Service; @@ -135,10 +136,10 @@ public class TouchInteractionService extends Service implements PluginListener RECT_CENTER_X = new FloatPropertyCompat("rectCenterXSpring") { @@ -116,6 +117,7 @@ public class RectFSpringAnim { ResourceProvider rp = DynamicResource.provider(context); mMinVisChange = rp.getDimension(R.dimen.swipe_up_fling_min_visible_change); mYOvershoot = rp.getDimension(R.dimen.swipe_up_y_overshoot); + setCanRelease(true); } public void onTargetPositionChanged() { @@ -190,10 +192,12 @@ public class RectFSpringAnim { maybeOnEnd(); }); + setCanRelease(false); + mAnimsStarted = true; + mRectXAnim.start(); mRectYAnim.start(); mRectScaleAnim.start(); - mAnimsStarted = true; for (Animator.AnimatorListener animatorListener : mAnimatorListeners) { animatorListener.onAnimationStart(null); } @@ -245,6 +249,7 @@ public class RectFSpringAnim { private void maybeOnEnd() { if (mAnimsStarted && isEnded()) { mAnimsStarted = false; + setCanRelease(true); for (Animator.AnimatorListener animatorListener : mAnimatorListeners) { animatorListener.onAnimationEnd(null); } diff --git a/quickstep/recents_ui_overrides/src/com/android/quickstep/util/StaggeredWorkspaceAnim.java b/quickstep/recents_ui_overrides/src/com/android/quickstep/util/StaggeredWorkspaceAnim.java index ebc9f96394..70903935bf 100644 --- a/quickstep/recents_ui_overrides/src/com/android/quickstep/util/StaggeredWorkspaceAnim.java +++ b/quickstep/recents_ui_overrides/src/com/android/quickstep/util/StaggeredWorkspaceAnim.java @@ -121,8 +121,11 @@ public class StaggeredWorkspaceAnim { addStaggeredAnimationForView(child, grid.inv.numRows + 1, totalRows); } - View qsb = launcher.findViewById(R.id.search_container_all_apps); - addStaggeredAnimationForView(qsb, grid.inv.numRows + 2, totalRows); + if (launcher.getAppsView().getSearchUiManager() + .isQsbVisible(NORMAL.getVisibleElements(launcher))) { + addStaggeredAnimationForView(launcher.getAppsView().getSearchView(), + grid.inv.numRows + 2, totalRows); + } } if (animateOverviewScrim) { diff --git a/quickstep/recents_ui_overrides/src/com/android/quickstep/util/SurfaceTransactionApplier.java b/quickstep/recents_ui_overrides/src/com/android/quickstep/util/SurfaceTransactionApplier.java new file mode 100644 index 0000000000..0a3e3ecf11 --- /dev/null +++ b/quickstep/recents_ui_overrides/src/com/android/quickstep/util/SurfaceTransactionApplier.java @@ -0,0 +1,136 @@ +/* + * Copyright (C) 2020 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.android.quickstep.util; + +import static com.android.systemui.shared.system.TransactionCompat.deferTransactionUntil; +import static com.android.systemui.shared.system.TransactionCompat.setEarlyWakeup; + +import android.annotation.TargetApi; +import android.os.Build; +import android.os.Handler; +import android.os.Message; +import android.view.SurfaceControl; +import android.view.SurfaceControl.Transaction; +import android.view.View; + +import com.android.quickstep.RemoteAnimationTargets.ReleaseCheck; +import com.android.systemui.shared.system.SyncRtSurfaceTransactionApplierCompat.SurfaceParams; +import com.android.systemui.shared.system.ViewRootImplCompat; + +import java.util.function.Consumer; + + +/** + * Helper class to apply surface transactions in sync with RenderThread similar to + * android.view.SyncRtSurfaceTransactionApplier + * with some Launcher specific utility methods + */ +@TargetApi(Build.VERSION_CODES.R) +public class SurfaceTransactionApplier extends ReleaseCheck { + + private static final int MSG_UPDATE_SEQUENCE_NUMBER = 0; + + private final SurfaceControl mBarrierSurfaceControl; + private final ViewRootImplCompat mTargetViewRootImpl; + private final Handler mApplyHandler; + + private int mLastSequenceNumber = 0; + + /** + * @param targetView The view in the surface that acts as synchronization anchor. + */ + public SurfaceTransactionApplier(View targetView) { + mTargetViewRootImpl = new ViewRootImplCompat(targetView); + mBarrierSurfaceControl = mTargetViewRootImpl.getRenderSurfaceControl(); + mApplyHandler = new Handler(this::onApplyMessage); + } + + protected boolean onApplyMessage(Message msg) { + if (msg.what == MSG_UPDATE_SEQUENCE_NUMBER) { + setCanRelease(msg.arg1 == mLastSequenceNumber); + return true; + } + return false; + } + + /** + * Schedules applying surface parameters on the next frame. + * + * @param params The surface parameters to apply. DO NOT MODIFY the list after passing into + * this method to avoid synchronization issues. + */ + public void scheduleApply(final SurfaceParams... params) { + View view = mTargetViewRootImpl.getView(); + if (view == null) { + return; + } + + mLastSequenceNumber++; + final int toApplySeqNo = mLastSequenceNumber; + setCanRelease(false); + mTargetViewRootImpl.registerRtFrameCallback(frame -> { + if (mBarrierSurfaceControl == null || !mBarrierSurfaceControl.isValid()) { + Message.obtain(mApplyHandler, MSG_UPDATE_SEQUENCE_NUMBER, toApplySeqNo, 0) + .sendToTarget(); + return; + } + Transaction t = new Transaction(); + for (int i = params.length - 1; i >= 0; i--) { + SurfaceParams surfaceParams = params[i]; + if (surfaceParams.surface.isValid()) { + deferTransactionUntil(t, surfaceParams.surface, mBarrierSurfaceControl, frame); + surfaceParams.applyTo(t); + } + } + setEarlyWakeup(t); + t.apply(); + Message.obtain(mApplyHandler, MSG_UPDATE_SEQUENCE_NUMBER, toApplySeqNo, 0) + .sendToTarget(); + }); + + // Make sure a frame gets scheduled. + view.invalidate(); + } + + /** + * Creates an instance of SyncRtSurfaceTransactionApplier, deferring until the target view is + * attached if necessary. + */ + public static void create( + final View targetView, final Consumer callback) { + if (targetView == null) { + // No target view, no applier + callback.accept(null); + } else if (new ViewRootImplCompat(targetView).isValid()) { + // Already attached, we're good to go + callback.accept(new SurfaceTransactionApplier(targetView)); + } else { + // Haven't been attached before we can get the view root + targetView.addOnAttachStateChangeListener(new View.OnAttachStateChangeListener() { + @Override + public void onViewAttachedToWindow(View v) { + targetView.removeOnAttachStateChangeListener(this); + callback.accept(new SurfaceTransactionApplier(targetView)); + } + + @Override + public void onViewDetachedFromWindow(View v) { + // Do nothing + } + }); + } + } +} diff --git a/quickstep/recents_ui_overrides/src/com/android/quickstep/util/TransformParams.java b/quickstep/recents_ui_overrides/src/com/android/quickstep/util/TransformParams.java index 9bb508eb37..7a62e83c39 100644 --- a/quickstep/recents_ui_overrides/src/com/android/quickstep/util/TransformParams.java +++ b/quickstep/recents_ui_overrides/src/com/android/quickstep/util/TransformParams.java @@ -57,7 +57,7 @@ public class TransformParams { private float mTargetAlpha; private float mCornerRadius; private RemoteAnimationTargets mTargetSet; - private SyncRtSurfaceTransactionApplierCompat mSyncTransactionApplier; + private SurfaceTransactionApplier mSyncTransactionApplier; private BuilderProxy mHomeBuilderProxy = BuilderProxy.ALWAYS_VISIBLE; private BuilderProxy mBaseBuilderProxy = BuilderProxy.ALWAYS_VISIBLE; @@ -112,7 +112,7 @@ public class TransformParams { * are computed based on these TransformParams. */ public TransformParams setSyncTransactionApplier( - SyncRtSurfaceTransactionApplierCompat applier) { + SurfaceTransactionApplier applier) { mSyncTransactionApplier = applier; return this; } diff --git a/quickstep/recents_ui_overrides/src/com/android/quickstep/views/OverviewActionsView.java b/quickstep/recents_ui_overrides/src/com/android/quickstep/views/OverviewActionsView.java index 83287c463a..08c3dc9c27 100644 --- a/quickstep/recents_ui_overrides/src/com/android/quickstep/views/OverviewActionsView.java +++ b/quickstep/recents_ui_overrides/src/com/android/quickstep/views/OverviewActionsView.java @@ -21,6 +21,7 @@ import static com.android.launcher3.config.FeatureFlags.ENABLE_OVERVIEW_SHARE; import static com.android.quickstep.SysUINavigationMode.removeShelfFromOverview; import android.content.Context; +import android.content.res.Configuration; import android.util.AttributeSet; import android.util.Log; import android.view.View; @@ -33,6 +34,7 @@ import androidx.annotation.Nullable; import com.android.launcher3.R; import com.android.launcher3.util.MultiValueAlpha; import com.android.launcher3.util.MultiValueAlpha.AlphaProperty; +import com.android.quickstep.SysUINavigationMode; import com.android.quickstep.SysUINavigationMode.Mode; import com.android.quickstep.TaskOverlayFactory.OverlayUICallbacks; @@ -129,6 +131,12 @@ public class OverviewActionsView extends FrameLayo updateHiddenFlags(HIDDEN_UNSUPPORTED_NAVIGATION, !removeShelfFromOverview(getContext())); } + @Override + protected void onConfigurationChanged(Configuration newConfig) { + super.onConfigurationChanged(newConfig); + updateVerticalMargin(SysUINavigationMode.getMode(getContext())); + } + public void updateHiddenFlags(@ActionsHiddenFlags int visibilityFlags, boolean enable) { if (enable) { mHiddenFlags |= visibilityFlags; @@ -152,10 +160,13 @@ public class OverviewActionsView extends FrameLayo return mMultiValueAlpha.getProperty(INDEX_FULLSCREEN_ALPHA); } - /** Updates vertical margins for different navigation mode. */ - public void updateVerticalMarginForNavModeChange(Mode mode) { - int bottomMargin = 0; - if (mode == Mode.THREE_BUTTONS) { + /** Updates vertical margins for different navigation mode or configuration changes. */ + public void updateVerticalMargin(Mode mode) { + int bottomMargin; + int orientation = getResources().getConfiguration().orientation; + if (orientation == Configuration.ORIENTATION_LANDSCAPE) { + bottomMargin = 0; + } else if (mode == Mode.THREE_BUTTONS) { bottomMargin = getResources() .getDimensionPixelSize(R.dimen.overview_actions_bottom_margin_three_button); } else { diff --git a/quickstep/recents_ui_overrides/src/com/android/quickstep/views/RecentsView.java b/quickstep/recents_ui_overrides/src/com/android/quickstep/views/RecentsView.java index 324aaecf0e..64d90cf4f7 100644 --- a/quickstep/recents_ui_overrides/src/com/android/quickstep/views/RecentsView.java +++ b/quickstep/recents_ui_overrides/src/com/android/quickstep/views/RecentsView.java @@ -127,6 +127,7 @@ import com.android.quickstep.ViewUtils; import com.android.quickstep.util.LayoutUtils; import com.android.quickstep.util.RecentsOrientedState; import com.android.quickstep.util.SplitScreenBounds; +import com.android.quickstep.util.SurfaceTransactionApplier; import com.android.quickstep.util.TransformParams; import com.android.systemui.plugins.ResourceProvider; import com.android.systemui.shared.recents.IPinnedStackAnimationListener; @@ -135,7 +136,6 @@ import com.android.systemui.shared.recents.model.ThumbnailData; import com.android.systemui.shared.system.ActivityManagerWrapper; import com.android.systemui.shared.system.LauncherEventUtil; import com.android.systemui.shared.system.PackageManagerWrapper; -import com.android.systemui.shared.system.SyncRtSurfaceTransactionApplierCompat; import com.android.systemui.shared.system.TaskStackChangeListener; import java.util.ArrayList; @@ -211,7 +211,7 @@ public abstract class RecentsView extends PagedView impl protected final BaseActivityInterface mSizeStrategy; protected RecentsAnimationController mRecentsAnimationController; protected RecentsAnimationTargets mRecentsAnimationTargets; - protected SyncRtSurfaceTransactionApplierCompat mSyncTransactionApplier; + protected SurfaceTransactionApplier mSyncTransactionApplier; protected int mTaskWidth; protected int mTaskHeight; protected boolean mEnableDrawingLiveTile = false; @@ -492,6 +492,7 @@ public abstract class RecentsView extends PagedView impl public void init(OverviewActionsView actionsView) { mActionsView = actionsView; + mActionsView.updateHiddenFlags(HIDDEN_NO_TASKS, getTaskViewCount() == 0); } @Override @@ -501,7 +502,7 @@ public abstract class RecentsView extends PagedView impl mModel.getThumbnailCache().getHighResLoadingState().addCallback(this); mActivity.addMultiWindowModeChangedListener(mMultiWindowModeChangedListener); ActivityManagerWrapper.getInstance().registerTaskStackListener(mTaskStackListener); - mSyncTransactionApplier = new SyncRtSurfaceTransactionApplierCompat(this); + mSyncTransactionApplier = new SurfaceTransactionApplier(this); RecentsModel.INSTANCE.get(getContext()).addThumbnailChangeListener(this); mIdp.addOnChangeListener(this); mIPinnedStackAnimationListener.setActivity(mActivity); @@ -1626,8 +1627,10 @@ public abstract class RecentsView extends PagedView impl : View.LAYOUT_DIRECTION_RTL); mClearAllButton.setRotation(mOrientationHandler.getDegreesRotated()); mActivity.getDragLayer().recreateControllers(); + boolean isInLandscape = touchRotation != 0 + || mOrientationState.getLauncherRotation() != ROTATION_0; mActionsView.updateHiddenFlags(HIDDEN_NON_ZERO_ROTATION, - touchRotation != 0 || mOrientationState.getLauncherRotation() != ROTATION_0); + !mOrientationState.canLauncherRotate() && isInLandscape); resetPaddingFromTaskSize(); requestLayout(); } diff --git a/quickstep/recents_ui_overrides/src/com/android/quickstep/views/TaskView.java b/quickstep/recents_ui_overrides/src/com/android/quickstep/views/TaskView.java index 6b759ba43a..af9d7f76bc 100644 --- a/quickstep/recents_ui_overrides/src/com/android/quickstep/views/TaskView.java +++ b/quickstep/recents_ui_overrides/src/com/android/quickstep/views/TaskView.java @@ -30,6 +30,7 @@ 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.TOUCH_RESPONSE_INTERPOLATOR; import static com.android.launcher3.config.FeatureFlags.ENABLE_QUICKSTEP_LIVE_TILE; +import static com.android.launcher3.logging.StatsLogManager.LauncherEvent.LAUNCHER_TASK_ICON_TAP_OR_LONGPRESS; import static com.android.launcher3.logging.StatsLogManager.LauncherEvent.LAUNCHER_TASK_LAUNCH_TAP; import android.animation.Animator; @@ -222,8 +223,8 @@ public class TaskView extends FrameLayout implements PageCallbacks, Reusable { setOutlineProvider(mOutlineProvider); } - /* Builds proto for logging */ - protected LauncherAtom.ItemInfo buildProto() { + /** Builds proto for logging */ + public LauncherAtom.ItemInfo buildProto() { ComponentKey componentKey = TaskUtils.getLaunchComponentKeyForTask(getTask().key); LauncherAtom.ItemInfo.Builder itemBuilder = LauncherAtom.ItemInfo.newBuilder(); itemBuilder.setIsWork(componentKey.user != Process.myUserHandle()); @@ -424,6 +425,7 @@ public class TaskView extends FrameLayout implements PageCallbacks, Reusable { private boolean showTaskMenu(int action) { getRecentsView().snapToPage(getRecentsView().indexOfChild(this)); mMenuView = TaskMenuView.showForTask(this); + mActivity.getStatsLogManager().log(LAUNCHER_TASK_ICON_TAP_OR_LONGPRESS, buildProto()); UserEventDispatcher.newInstance(getContext()).logActionOnItem(action, Direction.NONE, LauncherLogProto.ItemType.TASK_ICON); if (mMenuView != null) { diff --git a/quickstep/res/layout/gesture_tutorial_fragment.xml b/quickstep/res/layout/gesture_tutorial_fragment.xml index 74197bef40..459d65faf7 100644 --- a/quickstep/res/layout/gesture_tutorial_fragment.xml +++ b/quickstep/res/layout/gesture_tutorial_fragment.xml @@ -16,7 +16,7 @@ + android:background="?android:attr/colorBackground"> + #FFFFFFFF #99000000 \ No newline at end of file diff --git a/quickstep/res/values/strings.xml b/quickstep/res/values/strings.xml index 37d5ca3715..1ed3b2457c 100644 --- a/quickstep/res/values/strings.xml +++ b/quickstep/res/values/strings.xml @@ -83,6 +83,8 @@ Drag apps off the bottom row to get app suggestions App suggestions added to empty space + + App suggestions Enabled Predicted app: %1$s @@ -110,7 +112,6 @@ To change the sensitivity of the back gesture, go to Settings - Tutorial: Go Home @@ -122,6 +123,17 @@ Make sure you swipe straight up + + Tutorial: Switch Apps + + Swipe up from the bottom of the screen and hold + + Make sure you swipe from the bottom edge of the screen + + Try holding the window for longer before releasing + + Make sure you swipe straight up and pause + All set diff --git a/quickstep/res/values/styles.xml b/quickstep/res/values/styles.xml index 90957e42ff..8d054b4be5 100644 --- a/quickstep/res/values/styles.xml +++ b/quickstep/res/values/styles.xml @@ -35,14 +35,14 @@ @@ -50,7 +50,7 @@ diff --git a/quickstep/src/com/android/launcher3/BaseQuickstepLauncher.java b/quickstep/src/com/android/launcher3/BaseQuickstepLauncher.java index 0968d8e4ad..48743070d8 100644 --- a/quickstep/src/com/android/launcher3/BaseQuickstepLauncher.java +++ b/quickstep/src/com/android/launcher3/BaseQuickstepLauncher.java @@ -93,7 +93,7 @@ public abstract class BaseQuickstepLauncher extends Launcher public void onNavigationModeChanged(Mode newMode) { getDragLayer().recreateControllers(); if (mActionsView != null && isOverviewActionsEnabled()) { - mActionsView.updateVerticalMarginForNavModeChange(newMode); + mActionsView.updateVerticalMargin(newMode); } } @@ -175,7 +175,7 @@ public abstract class BaseQuickstepLauncher extends Launcher // Overview is above all other launcher elements, including qsb, so move it to the top. getOverviewPanel().bringToFront(); mActionsView.bringToFront(); - mActionsView.updateVerticalMarginForNavModeChange(SysUINavigationMode.getMode(this)); + mActionsView.updateVerticalMargin(SysUINavigationMode.getMode(this)); } } diff --git a/quickstep/src/com/android/launcher3/QuickstepAppTransitionManagerImpl.java b/quickstep/src/com/android/launcher3/QuickstepAppTransitionManagerImpl.java index c0d16c9ba8..10f789d428 100644 --- a/quickstep/src/com/android/launcher3/QuickstepAppTransitionManagerImpl.java +++ b/quickstep/src/com/android/launcher3/QuickstepAppTransitionManagerImpl.java @@ -82,6 +82,7 @@ import com.android.quickstep.RemoteAnimationTargets; import com.android.quickstep.util.MultiValueUpdateListener; import com.android.quickstep.util.RemoteAnimationProvider; import com.android.quickstep.util.StaggeredWorkspaceAnim; +import com.android.quickstep.util.SurfaceTransactionApplier; import com.android.systemui.shared.system.ActivityCompat; import com.android.systemui.shared.system.ActivityOptionsCompat; import com.android.systemui.shared.system.QuickStepContract; @@ -89,7 +90,6 @@ import com.android.systemui.shared.system.RemoteAnimationAdapterCompat; import com.android.systemui.shared.system.RemoteAnimationDefinitionCompat; import com.android.systemui.shared.system.RemoteAnimationRunnerCompat; import com.android.systemui.shared.system.RemoteAnimationTargetCompat; -import com.android.systemui.shared.system.SyncRtSurfaceTransactionApplierCompat; import com.android.systemui.shared.system.SyncRtSurfaceTransactionApplierCompat.SurfaceParams; import com.android.systemui.shared.system.WindowManagerWrapper; @@ -455,9 +455,9 @@ public abstract class QuickstepAppTransitionManagerImpl extends LauncherAppTrans RemoteAnimationTargets openingTargets = new RemoteAnimationTargets(appTargets, wallpaperTargets, MODE_OPENING); - SyncRtSurfaceTransactionApplierCompat surfaceApplier = - new SyncRtSurfaceTransactionApplierCompat(floatingView); - openingTargets.addDependentTransactionApplier(surfaceApplier); + SurfaceTransactionApplier surfaceApplier = + new SurfaceTransactionApplier(floatingView); + openingTargets.addReleaseCheck(surfaceApplier); // Scale the app icon to take up the entire screen. This simplifies the math when // animating the app window position / scale. @@ -714,8 +714,7 @@ public abstract class QuickstepAppTransitionManagerImpl extends LauncherAppTrans */ private Animator getUnlockWindowAnimator(RemoteAnimationTargetCompat[] appTargets, RemoteAnimationTargetCompat[] wallpaperTargets) { - SyncRtSurfaceTransactionApplierCompat surfaceApplier = - new SyncRtSurfaceTransactionApplierCompat(mDragLayer); + SurfaceTransactionApplier surfaceApplier = new SurfaceTransactionApplier(mDragLayer); ValueAnimator unlockAnimator = ValueAnimator.ofFloat(0, 1); unlockAnimator.setDuration(CLOSING_TRANSITION_DURATION_MS); float cornerRadius = mDeviceProfile.isMultiWindowMode ? 0 : @@ -743,8 +742,7 @@ public abstract class QuickstepAppTransitionManagerImpl extends LauncherAppTrans */ private Animator getClosingWindowAnimators(RemoteAnimationTargetCompat[] appTargets, RemoteAnimationTargetCompat[] wallpaperTargets) { - SyncRtSurfaceTransactionApplierCompat surfaceApplier = - new SyncRtSurfaceTransactionApplierCompat(mDragLayer); + SurfaceTransactionApplier surfaceApplier = new SurfaceTransactionApplier(mDragLayer); Matrix matrix = new Matrix(); Point tmpPos = new Point(); ValueAnimator closingAnimator = ValueAnimator.ofFloat(0, 1); diff --git a/quickstep/src/com/android/launcher3/model/WellbeingModel.java b/quickstep/src/com/android/launcher3/model/WellbeingModel.java index 2181aa826a..f42b124fd6 100644 --- a/quickstep/src/com/android/launcher3/model/WellbeingModel.java +++ b/quickstep/src/com/android/launcher3/model/WellbeingModel.java @@ -77,6 +77,7 @@ public final class WellbeingModel { private static final String EXTRA_ACTION = "action"; private static final String EXTRA_MAX_NUM_ACTIONS_SHOWN = "max_num_actions_shown"; private static final String EXTRA_PACKAGES = "packages"; + private static final String EXTRA_SUCCESS = "success"; public static final MainThreadInitializedObject INSTANCE = new MainThreadInitializedObject<>(WellbeingModel::new); @@ -221,6 +222,7 @@ public final class WellbeingModel { params.putInt(EXTRA_MAX_NUM_ACTIONS_SHOWN, 1); // Perform wellbeing call . remoteActionBundle = client.call(METHOD_GET_ACTIONS, null, params); + if (!remoteActionBundle.getBoolean(EXTRA_SUCCESS, true)) return false; synchronized (mModelLock) { // Remove the entries for requested packages, and then update the fist with what we @@ -281,9 +283,9 @@ public final class WellbeingModel { // Remove all existing messages mWorkerHandler.removeCallbacksAndMessages(null); final String[] packageNames = mContext.getSystemService(LauncherApps.class) - .getActivityList(null, Process.myUserHandle()).stream() - .map(li -> li.getApplicationInfo().packageName).distinct() - .toArray(String[]::new); + .getActivityList(null, Process.myUserHandle()).stream() + .map(li -> li.getApplicationInfo().packageName).distinct() + .toArray(String[]::new); if (!updateActions(packageNames)) { scheduleRefreshRetry(msg); } diff --git a/quickstep/src/com/android/launcher3/statehandlers/DepthController.java b/quickstep/src/com/android/launcher3/statehandlers/DepthController.java index fcffaedfd8..ebe9e26c02 100644 --- a/quickstep/src/com/android/launcher3/statehandlers/DepthController.java +++ b/quickstep/src/com/android/launcher3/statehandlers/DepthController.java @@ -17,6 +17,7 @@ package com.android.launcher3.statehandlers; import static com.android.launcher3.anim.Interpolators.LINEAR; +import static com.android.launcher3.states.StateAnimationConfig.ANIM_DEPTH; import static com.android.launcher3.states.StateAnimationConfig.SKIP_DEPTH_CONTROLLER; import android.os.IBinder; @@ -191,11 +192,12 @@ public class DepthController implements StateHandler { float toDepth = toState.getDepth(mLauncher); if (Float.compare(mDepth, toDepth) != 0) { - animation.setFloat(this, DEPTH, toDepth, LINEAR); + animation.setFloat(this, DEPTH, toDepth, config.getInterpolator(ANIM_DEPTH, LINEAR)); } } private void setDepth(float depth) { + depth = Utilities.boundToRange(depth, 0, 1); // Round out the depth to dedupe frequent, non-perceptable updates int depthI = (int) (depth * 256); float depthF = depthI / 256f; diff --git a/quickstep/src/com/android/quickstep/OrientationTouchTransformer.java b/quickstep/src/com/android/quickstep/OrientationTouchTransformer.java index b59f0118af..b49bc5a378 100644 --- a/quickstep/src/com/android/quickstep/OrientationTouchTransformer.java +++ b/quickstep/src/com/android/quickstep/OrientationTouchTransformer.java @@ -36,6 +36,7 @@ import android.view.Surface; import com.android.launcher3.R; import com.android.launcher3.ResourceUtils; +import com.android.launcher3.testing.TestProtocol; import com.android.launcher3.util.DefaultDisplay; import java.io.PrintWriter; @@ -252,6 +253,10 @@ class OrientationTouchTransformer { } boolean touchInValidSwipeRegions(float x, float y) { + if (TestProtocol.sDebugTracing) { + Log.d(TestProtocol.NO_SWIPE_TO_HOME, "touchInValidSwipeRegions " + x + "," + y + " in " + + mLastRectTouched); + } if (mLastRectTouched != null) { return mLastRectTouched.contains(x, y); } @@ -293,10 +298,16 @@ class OrientationTouchTransformer { for (int i = 0; i < MAX_ORIENTATIONS; i++) { OrientationRectF rect = mSwipeTouchRegions.get(i); + if (TestProtocol.sDebugTracing) { + Log.d(TestProtocol.NO_SWIPE_TO_HOME, "transform:DOWN, rect=" + rect); + } if (rect == null) { continue; } if (rect.applyTransform(event, false)) { + if (TestProtocol.sDebugTracing) { + Log.d(TestProtocol.NO_SWIPE_TO_HOME, "setting mLastRectTouched"); + } mLastRectTouched = rect; mLastRectRotation = rect.mRotation; if (mEnableMultipleRegions && mCurrentDisplayRotation == mLastRectRotation) { diff --git a/quickstep/src/com/android/quickstep/RecentsAnimationDeviceState.java b/quickstep/src/com/android/quickstep/RecentsAnimationDeviceState.java index c83de13618..995e9187fa 100644 --- a/quickstep/src/com/android/quickstep/RecentsAnimationDeviceState.java +++ b/quickstep/src/com/android/quickstep/RecentsAnimationDeviceState.java @@ -546,13 +546,15 @@ public class RecentsAnimationDeviceState implements /** * @param ev An ACTION_DOWN motion event - * @return whether the given motion event can trigger the assistant. + * @param task Info for the currently running task + * @return whether the given motion event can trigger the assistant over the current task. */ - public boolean canTriggerAssistantAction(MotionEvent ev) { + public boolean canTriggerAssistantAction(MotionEvent ev, ActivityManager.RunningTaskInfo task) { return mAssistantAvailable && !QuickStepContract.isAssistantGestureDisabled(mSystemUiStateFlags) && mOrientationTouchTransformer.touchInAssistantRegion(ev) - && !isLockToAppActive(); + && !isLockToAppActive() + && !isGestureBlockedActivity(task); } /** diff --git a/quickstep/src/com/android/quickstep/RemoteAnimationTargets.java b/quickstep/src/com/android/quickstep/RemoteAnimationTargets.java index f90df4563f..ab5e3ba0cc 100644 --- a/quickstep/src/com/android/quickstep/RemoteAnimationTargets.java +++ b/quickstep/src/com/android/quickstep/RemoteAnimationTargets.java @@ -16,19 +16,16 @@ package com.android.quickstep; import com.android.systemui.shared.system.RemoteAnimationTargetCompat; -import com.android.systemui.shared.system.SyncRtSurfaceTransactionApplierCompat; -import java.util.ArrayDeque; import java.util.ArrayList; -import java.util.Queue; +import java.util.concurrent.CopyOnWriteArrayList; /** * Holds a collection of RemoteAnimationTargets, filtered by different properties. */ public class RemoteAnimationTargets { - private final Queue mDependentTransactionAppliers = - new ArrayDeque<>(1); + private final CopyOnWriteArrayList mReleaseChecks = new CopyOnWriteArrayList<>(); public final RemoteAnimationTargetCompat[] unfilteredApps; public final RemoteAnimationTargetCompat[] apps; @@ -36,6 +33,8 @@ public class RemoteAnimationTargets { public final int targetMode; public final boolean hasRecents; + private boolean mReleased = false; + public RemoteAnimationTargets(RemoteAnimationTargetCompat[] apps, RemoteAnimationTargetCompat[] wallpapers, int targetMode) { ArrayList filteredApps = new ArrayList<>(); @@ -76,21 +75,65 @@ public class RemoteAnimationTargets { return false; } - public void addDependentTransactionApplier(SyncRtSurfaceTransactionApplierCompat delay) { - mDependentTransactionAppliers.add(delay); + public void addReleaseCheck(ReleaseCheck check) { + mReleaseChecks.add(check); } public void release() { - SyncRtSurfaceTransactionApplierCompat applier = mDependentTransactionAppliers.poll(); - if (applier == null) { - for (RemoteAnimationTargetCompat target : unfilteredApps) { - target.release(); + if (mReleased) { + return; + } + for (ReleaseCheck check : mReleaseChecks) { + if (!check.mCanRelease) { + check.addOnSafeToReleaseCallback(this::release); + return; } - for (RemoteAnimationTargetCompat target : wallpapers) { - target.release(); + } + mReleaseChecks.clear(); + mReleased = true; + + for (RemoteAnimationTargetCompat target : unfilteredApps) { + target.release(); + } + for (RemoteAnimationTargetCompat target : wallpapers) { + target.release(); + } + } + + /** + * Interface for intercepting surface release method + */ + public static class ReleaseCheck { + + boolean mCanRelease = false; + private Runnable mAfterApplyCallback; + + protected void setCanRelease(boolean canRelease) { + mCanRelease = canRelease; + if (mCanRelease && mAfterApplyCallback != null) { + Runnable r = mAfterApplyCallback; + mAfterApplyCallback = null; + r.run(); + } + } + + /** + * Adds a callback to notify when the surface can safely be released + */ + void addOnSafeToReleaseCallback(Runnable callback) { + if (mCanRelease) { + callback.run(); + } else { + if (mAfterApplyCallback == null) { + mAfterApplyCallback = callback; + } else { + final Runnable oldCallback = mAfterApplyCallback; + mAfterApplyCallback = () -> { + callback.run(); + oldCallback.run(); + }; + } } - } else { - applier.addAfterApplyCallback(this::release); } } } diff --git a/quickstep/src/com/android/quickstep/SystemUiProxy.java b/quickstep/src/com/android/quickstep/SystemUiProxy.java index a3eabc966c..2ca5dc68ac 100644 --- a/quickstep/src/com/android/quickstep/SystemUiProxy.java +++ b/quickstep/src/com/android/quickstep/SystemUiProxy.java @@ -31,6 +31,7 @@ import android.view.MotionEvent; import com.android.launcher3.util.MainThreadInitializedObject; import com.android.systemui.shared.recents.IPinnedStackAnimationListener; import com.android.systemui.shared.recents.ISystemUiProxy; +import com.android.systemui.shared.recents.model.Task; /** * Holds the reference to SystemUI. @@ -366,4 +367,8 @@ public class SystemUiProxy implements ISystemUiProxy { } } } + + public void handleImageBundleAsScreenshot(Bundle screenImageBundle, Rect locationInScreen, + Insets visibleInsets, Task.TaskKey task) { + } } diff --git a/quickstep/src/com/android/quickstep/interaction/BackGestureTutorialController.java b/quickstep/src/com/android/quickstep/interaction/BackGestureTutorialController.java index 41e86e00b6..1f398fc5ea 100644 --- a/quickstep/src/com/android/quickstep/interaction/BackGestureTutorialController.java +++ b/quickstep/src/com/android/quickstep/interaction/BackGestureTutorialController.java @@ -34,14 +34,6 @@ final class BackGestureTutorialController extends TutorialController { super(fragment, tutorialType); } - @Override - void transitToController() { - super.transitToController(); - if (mTutorialType != BACK_NAVIGATION_COMPLETE) { - showHandCoachingAnimation(); - } - } - @Override Integer getTitleStringId() { switch (mTutorialType) { diff --git a/quickstep/src/com/android/quickstep/interaction/EdgeBackGesturePanel.java b/quickstep/src/com/android/quickstep/interaction/EdgeBackGesturePanel.java index 5bf50260aa..0521db4d1b 100644 --- a/quickstep/src/com/android/quickstep/interaction/EdgeBackGesturePanel.java +++ b/quickstep/src/com/android/quickstep/interaction/EdgeBackGesturePanel.java @@ -280,7 +280,11 @@ public class EdgeBackGesturePanel extends View { new SpringForce() .setStiffness(SpringForce.STIFFNESS_MEDIUM) .setDampingRatio(SpringForce.DAMPING_RATIO_LOW_BOUNCY)); - mPaint.setColor(context.getColor(R.color.back_arrow_color_dark)); + int currentNightMode = + context.getResources().getConfiguration().uiMode & Configuration.UI_MODE_NIGHT_MASK; + mPaint.setColor(context.getColor(currentNightMode == Configuration.UI_MODE_NIGHT_YES + ? R.color.back_arrow_color_light + : R.color.back_arrow_color_dark)); loadDimens(); updateArrowDirection(); diff --git a/quickstep/src/com/android/quickstep/interaction/HomeGestureTutorialController.java b/quickstep/src/com/android/quickstep/interaction/HomeGestureTutorialController.java index 65f41a4a6c..0edabd45c5 100644 --- a/quickstep/src/com/android/quickstep/interaction/HomeGestureTutorialController.java +++ b/quickstep/src/com/android/quickstep/interaction/HomeGestureTutorialController.java @@ -15,143 +15,23 @@ */ package com.android.quickstep.interaction; -import static com.android.launcher3.anim.Interpolators.ACCEL; -import static com.android.launcher3.util.DefaultDisplay.getSingleFrameMs; -import static com.android.quickstep.BaseSwipeUpHandlerV2.MAX_SWIPE_DURATION; import static com.android.quickstep.interaction.TutorialController.TutorialType.HOME_NAVIGATION_COMPLETE; -import android.animation.Animator; -import android.animation.AnimatorListenerAdapter; -import android.animation.AnimatorSet; import android.annotation.TargetApi; -import android.content.Context; -import android.graphics.Insets; -import android.graphics.Outline; import android.graphics.PointF; -import android.graphics.Rect; -import android.graphics.RectF; import android.os.Build; -import android.view.SurfaceControl; import android.view.View; -import android.view.ViewOutlineProvider; -import android.view.WindowInsets.Type; -import android.view.WindowManager; -import androidx.annotation.NonNull; -import androidx.annotation.Nullable; - -import com.android.launcher3.DeviceProfile; -import com.android.launcher3.InvariantDeviceProfile; import com.android.launcher3.R; -import com.android.launcher3.Utilities; -import com.android.launcher3.anim.AnimationSuccessListener; -import com.android.launcher3.anim.AnimatorPlaybackController; -import com.android.launcher3.anim.PendingAnimation; -import com.android.quickstep.AnimatedFloat; -import com.android.quickstep.GestureState; -import com.android.quickstep.OverviewComponentObserver; -import com.android.quickstep.RecentsAnimationDeviceState; -import com.android.quickstep.SwipeUpAnimationLogic; -import com.android.quickstep.SwipeUpAnimationLogic.RunningWindowAnim; import com.android.quickstep.interaction.EdgeBackGestureHandler.BackGestureResult; import com.android.quickstep.interaction.NavBarGestureHandler.NavBarGestureResult; -import com.android.quickstep.util.RectFSpringAnim; -import com.android.quickstep.util.TransformParams; -import com.android.systemui.shared.system.QuickStepContract; -import com.android.systemui.shared.system.SyncRtSurfaceTransactionApplierCompat.SurfaceParams; /** A {@link TutorialController} for the Home tutorial. */ @TargetApi(Build.VERSION_CODES.R) -final class HomeGestureTutorialController extends TutorialController { - - private float mFakeTaskViewRadius; - private Rect mFakeTaskViewRect = new Rect(); - - private final ViewSwipeUpAnimation mViewSwipeUpAnimation; - private RunningWindowAnim mRunningWindowAnim; +final class HomeGestureTutorialController extends SwipeUpGestureTutorialController { HomeGestureTutorialController(HomeGestureTutorialFragment fragment, TutorialType tutorialType) { super(fragment, tutorialType); - - RecentsAnimationDeviceState deviceState = new RecentsAnimationDeviceState(mContext); - OverviewComponentObserver observer = new OverviewComponentObserver(mContext, deviceState); - mViewSwipeUpAnimation = new ViewSwipeUpAnimation(mContext, deviceState, - new GestureState(observer, -1)); - observer.onDestroy(); - deviceState.destroy(); - - DeviceProfile dp = InvariantDeviceProfile.INSTANCE.get(mContext) - .getDeviceProfile(mContext) - .copy(mContext); - Insets insets = mContext.getSystemService(WindowManager.class) - .getCurrentWindowMetrics() - .getWindowInsets() - .getInsets(Type.systemBars()); - dp.updateInsets(new Rect(insets.left, insets.top, insets.right, insets.bottom)); - mViewSwipeUpAnimation.initDp(dp); - - mFakeTaskViewRadius = QuickStepContract.getWindowCornerRadius(mContext.getResources()); - - mFakeTaskView.setClipToOutline(true); - mFakeTaskView.setOutlineProvider(new ViewOutlineProvider() { - @Override - public void getOutline(View view, Outline outline) { - outline.setRoundRect(mFakeTaskViewRect, mFakeTaskViewRadius); - } - }); - } - - private void cancelRunningAnimation() { - if (mRunningWindowAnim != null) { - mRunningWindowAnim.cancel(); - } - mRunningWindowAnim = null; - } - - /** Fades the task view, optionally after animating to a fake Overview. */ - private void fadeOutFakeTaskView(boolean toOverviewFirst, @Nullable Runnable onEndRunnable) { - cancelRunningAnimation(); - PendingAnimation anim = new PendingAnimation(300); - AnimatorListenerAdapter resetTaskView = new AnimatorListenerAdapter() { - @Override - public void onAnimationEnd(Animator animation, boolean isReverse) { - mFakeTaskView.setVisibility(View.INVISIBLE); - mFakeTaskView.setAlpha(1); - mRunningWindowAnim = null; - } - }; - if (toOverviewFirst) { - anim.setFloat(mViewSwipeUpAnimation.getCurrentShift(), AnimatedFloat.VALUE, 1, ACCEL); - anim.addListener(new AnimatorListenerAdapter() { - @Override - public void onAnimationEnd(Animator animation, boolean isReverse) { - PendingAnimation fadeAnim = new PendingAnimation(300); - fadeAnim.setViewAlpha(mFakeTaskView, 0, ACCEL); - fadeAnim.addListener(resetTaskView); - AnimatorSet animset = fadeAnim.buildAnim(); - animset.setStartDelay(100); - animset.start(); - mRunningWindowAnim = RunningWindowAnim.wrap(animset); - } - }); - } else { - anim.setViewAlpha(mFakeTaskView, 0, ACCEL); - anim.addListener(resetTaskView); - } - if (onEndRunnable != null) { - anim.addListener(AnimationSuccessListener.forRunnable(onEndRunnable)); - } - AnimatorSet animset = anim.buildAnim(); - animset.start(); - mRunningWindowAnim = RunningWindowAnim.wrap(animset); - } - - @Override - void transitToController() { - super.transitToController(); - if (mTutorialType != HOME_NAVIGATION_COMPLETE) { - showHandCoachingAnimation(); - } } @Override @@ -190,6 +70,14 @@ final class HomeGestureTutorialController extends TutorialController { public void onBackGestureAttempted(BackGestureResult result) { switch (mTutorialType) { case HOME_NAVIGATION: + switch (result) { + case BACK_COMPLETED_FROM_LEFT: + case BACK_COMPLETED_FROM_RIGHT: + case BACK_CANCELLED_FROM_LEFT: + case BACK_CANCELLED_FROM_RIGHT: + showFeedback(R.string.home_gesture_feedback_swipe_too_far_from_edge); + break; + } break; case HOME_NAVIGATION_COMPLETE: if (result == BackGestureResult.BACK_COMPLETED_FROM_LEFT @@ -206,17 +94,8 @@ final class HomeGestureTutorialController extends TutorialController { case HOME_NAVIGATION: switch (result) { case HOME_GESTURE_COMPLETED: { - hideFeedback(); - cancelRunningAnimation(); - hideHandCoachingAnimation(); - RectFSpringAnim rectAnim = - mViewSwipeUpAnimation.handleSwipeUpToHome(finalVelocity); - // After home animation finishes, fade out and then move to the next screen. - rectAnim.addAnimatorListener(AnimationSuccessListener.forRunnable( - () -> fadeOutFakeTaskView(false, - () -> mTutorialFragment.changeController( - HOME_NAVIGATION_COMPLETE)))); - mRunningWindowAnim = RunningWindowAnim.wrap(rectAnim); + animateFakeTaskViewHome(finalVelocity, () -> + mTutorialFragment.changeController(HOME_NAVIGATION_COMPLETE)); break; } case HOME_NOT_STARTED_TOO_FAR_FROM_EDGE: @@ -242,93 +121,4 @@ final class HomeGestureTutorialController extends TutorialController { } } - @Override - public void setNavBarGestureProgress(@Nullable Float displacement) { - if (displacement == null || mTutorialType == HOME_NAVIGATION_COMPLETE) { - mFakeTaskView.setVisibility(View.INVISIBLE); - } else { - mFakeTaskView.setVisibility(View.VISIBLE); - if (mRunningWindowAnim == null) { - mViewSwipeUpAnimation.updateDisplacement(displacement); - } - } - } - - private class ViewSwipeUpAnimation extends SwipeUpAnimationLogic { - - ViewSwipeUpAnimation(Context context, RecentsAnimationDeviceState deviceState, - GestureState gestureState) { - super(context, deviceState, gestureState, new FakeTransformParams()); - } - - void initDp(DeviceProfile dp) { - initTransitionEndpoints(dp); - mTaskViewSimulator.setPreviewBounds( - new Rect(0, 0, dp.widthPx, dp.heightPx), dp.getInsets()); - } - - @Override - public void updateFinalShift() { - float progress = mCurrentShift.value / mDragLengthFactor; - mWindowTransitionController.setPlayFraction(progress); - mTaskViewSimulator.apply(mTransformParams); - } - - AnimatedFloat getCurrentShift() { - return mCurrentShift; - } - - RectFSpringAnim handleSwipeUpToHome(PointF velocity) { - PointF velocityPxPerMs = new PointF(velocity.x, velocity.y); - float currentShift = mCurrentShift.value; - final float startShift = Utilities.boundToRange(currentShift - velocityPxPerMs.y - * getSingleFrameMs(mContext) / mTransitionDragLength, 0, mDragLengthFactor); - float distanceToTravel = (1 - currentShift) * mTransitionDragLength; - - // we want the page's snap velocity to approximately match the velocity at - // which the user flings, so we scale the duration by a value near to the - // derivative of the scroll interpolator at zero, ie. 2. - long baseDuration = Math.round(Math.abs(distanceToTravel / velocityPxPerMs.y)); - long duration = Math.min(MAX_SWIPE_DURATION, 2 * baseDuration); - HomeAnimationFactory homeAnimFactory = new HomeAnimationFactory(null) { - @Override - public AnimatorPlaybackController createActivityAnimationToHome() { - return AnimatorPlaybackController.wrap(new AnimatorSet(), duration); - } - - @NonNull - @Override - public RectF getWindowTargetRect() { - int fakeHomeIconSizePx = mDp.allAppsIconSizePx; - int fakeHomeIconLeft = (mDp.widthPx - fakeHomeIconSizePx) / 2; - int fakeHomeIconTop = mDp.heightPx - (mDp.allAppsCellHeightPx * 3); - return new RectF(fakeHomeIconLeft, fakeHomeIconTop, - fakeHomeIconLeft + fakeHomeIconSizePx, - fakeHomeIconTop + fakeHomeIconSizePx); - } - }; - RectFSpringAnim windowAnim = createWindowAnimationToHome(startShift, homeAnimFactory); - windowAnim.start(mContext, velocityPxPerMs); - return windowAnim; - } - } - - private class FakeTransformParams extends TransformParams { - - @Override - public SurfaceParams[] createSurfaceParams(BuilderProxy proxy) { - SurfaceParams.Builder builder = new SurfaceParams.Builder((SurfaceControl) null); - proxy.onBuildTargetParams(builder, null, this); - return new SurfaceParams[] {builder.build()}; - } - - @Override - public void applySurfaceParams(SurfaceParams[] params) { - SurfaceParams p = params[0]; - mFakeTaskView.setAnimationMatrix(p.matrix); - mFakeTaskViewRect.set(p.windowCrop); - mFakeTaskViewRadius = p.cornerRadius; - mFakeTaskView.invalidateOutline(); - } - } } diff --git a/quickstep/src/com/android/quickstep/interaction/OverviewGestureTutorialController.java b/quickstep/src/com/android/quickstep/interaction/OverviewGestureTutorialController.java new file mode 100644 index 0000000000..c636ebaea9 --- /dev/null +++ b/quickstep/src/com/android/quickstep/interaction/OverviewGestureTutorialController.java @@ -0,0 +1,124 @@ +/* + * Copyright (C) 2020 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.android.quickstep.interaction; + +import static com.android.quickstep.interaction.TutorialController.TutorialType.OVERVIEW_NAVIGATION_COMPLETE; + +import android.annotation.TargetApi; +import android.graphics.PointF; +import android.os.Build; +import android.view.View; + +import com.android.launcher3.R; +import com.android.quickstep.interaction.EdgeBackGestureHandler.BackGestureResult; +import com.android.quickstep.interaction.NavBarGestureHandler.NavBarGestureResult; + +/** A {@link TutorialController} for the Overview tutorial. */ +@TargetApi(Build.VERSION_CODES.R) +final class OverviewGestureTutorialController extends SwipeUpGestureTutorialController { + + OverviewGestureTutorialController(OverviewGestureTutorialFragment fragment, + TutorialType tutorialType) { + super(fragment, tutorialType); + } + + @Override + Integer getTitleStringId() { + switch (mTutorialType) { + case OVERVIEW_NAVIGATION: + return R.string.overview_gesture_tutorial_playground_title; + case OVERVIEW_NAVIGATION_COMPLETE: + return R.string.gesture_tutorial_confirm_title; + } + return null; + } + + @Override + Integer getSubtitleStringId() { + if (mTutorialType == TutorialType.OVERVIEW_NAVIGATION) { + return R.string.overview_gesture_tutorial_playground_subtitle; + } + return null; + } + + @Override + Integer getActionButtonStringId() { + if (mTutorialType == OVERVIEW_NAVIGATION_COMPLETE) { + return R.string.gesture_tutorial_action_button_label_done; + } + return null; + } + + @Override + void onActionButtonClicked(View button) { + mTutorialFragment.closeTutorial(); + } + + @Override + public void onBackGestureAttempted(BackGestureResult result) { + switch (mTutorialType) { + case OVERVIEW_NAVIGATION: + switch (result) { + case BACK_COMPLETED_FROM_LEFT: + case BACK_COMPLETED_FROM_RIGHT: + case BACK_CANCELLED_FROM_LEFT: + case BACK_CANCELLED_FROM_RIGHT: + showFeedback(R.string.overview_gesture_feedback_swipe_too_far_from_edge); + break; + } + break; + case OVERVIEW_NAVIGATION_COMPLETE: + if (result == BackGestureResult.BACK_COMPLETED_FROM_LEFT + || result == BackGestureResult.BACK_COMPLETED_FROM_RIGHT) { + mTutorialFragment.closeTutorial(); + } + break; + } + } + + @Override + public void onNavBarGestureAttempted(NavBarGestureResult result, PointF finalVelocity) { + switch (mTutorialType) { + case OVERVIEW_NAVIGATION: + switch (result) { + case HOME_GESTURE_COMPLETED: { + animateFakeTaskViewHome(finalVelocity, () -> + showFeedback(R.string.overview_gesture_feedback_home_detected)); + break; + } + case HOME_NOT_STARTED_TOO_FAR_FROM_EDGE: + case OVERVIEW_NOT_STARTED_TOO_FAR_FROM_EDGE: + showFeedback(R.string.overview_gesture_feedback_swipe_too_far_from_edge); + break; + case OVERVIEW_GESTURE_COMPLETED: + fadeOutFakeTaskView(true, () -> + mTutorialFragment.changeController(OVERVIEW_NAVIGATION_COMPLETE)); + break; + case HOME_OR_OVERVIEW_NOT_STARTED_WRONG_SWIPE_DIRECTION: + case HOME_OR_OVERVIEW_CANCELLED: + fadeOutFakeTaskView(false, null); + showFeedback(R.string.overview_gesture_feedback_wrong_swipe_direction); + break; + } + break; + case OVERVIEW_NAVIGATION_COMPLETE: + if (result == NavBarGestureResult.HOME_GESTURE_COMPLETED) { + mTutorialFragment.closeTutorial(); + } + break; + } + } +} diff --git a/quickstep/src/com/android/quickstep/interaction/OverviewGestureTutorialFragment.java b/quickstep/src/com/android/quickstep/interaction/OverviewGestureTutorialFragment.java new file mode 100644 index 0000000000..3357b70497 --- /dev/null +++ b/quickstep/src/com/android/quickstep/interaction/OverviewGestureTutorialFragment.java @@ -0,0 +1,37 @@ +/* + * Copyright (C) 2020 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.android.quickstep.interaction; + +import com.android.launcher3.R; +import com.android.quickstep.interaction.TutorialController.TutorialType; + +/** Shows the Overview gesture interactive tutorial. */ +public class OverviewGestureTutorialFragment extends TutorialFragment { + @Override + int getHandAnimationResId() { + return R.drawable.overview_gesture; + } + + @Override + TutorialController createController(TutorialType type) { + return new OverviewGestureTutorialController(this, type); + } + + @Override + Class getControllerClass() { + return OverviewGestureTutorialController.class; + } +} diff --git a/quickstep/src/com/android/quickstep/interaction/SwipeUpGestureTutorialController.java b/quickstep/src/com/android/quickstep/interaction/SwipeUpGestureTutorialController.java new file mode 100644 index 0000000000..14e00dce7a --- /dev/null +++ b/quickstep/src/com/android/quickstep/interaction/SwipeUpGestureTutorialController.java @@ -0,0 +1,246 @@ +/* + * Copyright (C) 2020 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.android.quickstep.interaction; + +import static com.android.launcher3.anim.Interpolators.ACCEL; +import static com.android.launcher3.util.DefaultDisplay.getSingleFrameMs; +import static com.android.quickstep.BaseSwipeUpHandlerV2.MAX_SWIPE_DURATION; +import static com.android.quickstep.interaction.TutorialController.TutorialType.HOME_NAVIGATION_COMPLETE; +import static com.android.quickstep.interaction.TutorialController.TutorialType.OVERVIEW_NAVIGATION_COMPLETE; + +import android.animation.Animator; +import android.animation.AnimatorListenerAdapter; +import android.animation.AnimatorSet; +import android.annotation.TargetApi; +import android.content.Context; +import android.graphics.Insets; +import android.graphics.Outline; +import android.graphics.PointF; +import android.graphics.Rect; +import android.graphics.RectF; +import android.os.Build; +import android.view.SurfaceControl; +import android.view.View; +import android.view.ViewOutlineProvider; +import android.view.WindowInsets; +import android.view.WindowManager; + +import androidx.annotation.NonNull; +import androidx.annotation.Nullable; + +import com.android.launcher3.DeviceProfile; +import com.android.launcher3.InvariantDeviceProfile; +import com.android.launcher3.Utilities; +import com.android.launcher3.anim.AnimationSuccessListener; +import com.android.launcher3.anim.AnimatorPlaybackController; +import com.android.launcher3.anim.PendingAnimation; +import com.android.quickstep.AnimatedFloat; +import com.android.quickstep.GestureState; +import com.android.quickstep.OverviewComponentObserver; +import com.android.quickstep.RecentsAnimationDeviceState; +import com.android.quickstep.SwipeUpAnimationLogic; +import com.android.quickstep.SwipeUpAnimationLogic.RunningWindowAnim; +import com.android.quickstep.util.RectFSpringAnim; +import com.android.quickstep.util.TransformParams; +import com.android.systemui.shared.system.QuickStepContract; +import com.android.systemui.shared.system.SyncRtSurfaceTransactionApplierCompat.SurfaceParams; + +@TargetApi(Build.VERSION_CODES.R) +abstract class SwipeUpGestureTutorialController extends TutorialController { + private final ViewSwipeUpAnimation mViewSwipeUpAnimation; + private float mFakeTaskViewRadius; + private Rect mFakeTaskViewRect = new Rect(); + private RunningWindowAnim mRunningWindowAnim; + + SwipeUpGestureTutorialController(TutorialFragment tutorialFragment, TutorialType tutorialType) { + super(tutorialFragment, tutorialType); + RecentsAnimationDeviceState deviceState = new RecentsAnimationDeviceState(mContext); + OverviewComponentObserver observer = new OverviewComponentObserver(mContext, deviceState); + mViewSwipeUpAnimation = new ViewSwipeUpAnimation(mContext, deviceState, + new GestureState(observer, -1)); + observer.onDestroy(); + deviceState.destroy(); + + DeviceProfile dp = InvariantDeviceProfile.INSTANCE.get(mContext) + .getDeviceProfile(mContext) + .copy(mContext); + Insets insets = mContext.getSystemService(WindowManager.class) + .getCurrentWindowMetrics() + .getWindowInsets() + .getInsets(WindowInsets.Type.systemBars()); + dp.updateInsets(new Rect(insets.left, insets.top, insets.right, insets.bottom)); + mViewSwipeUpAnimation.initDp(dp); + + mFakeTaskViewRadius = QuickStepContract.getWindowCornerRadius(mContext.getResources()); + mFakeTaskView.setClipToOutline(true); + mFakeTaskView.setOutlineProvider(new ViewOutlineProvider() { + @Override + public void getOutline(View view, Outline outline) { + outline.setRoundRect(mFakeTaskViewRect, mFakeTaskViewRadius); + } + }); + } + + private void cancelRunningAnimation() { + if (mRunningWindowAnim != null) { + mRunningWindowAnim.cancel(); + } + mRunningWindowAnim = null; + } + + /** Fades the task view, optionally after animating to a fake Overview. */ + void fadeOutFakeTaskView(boolean toOverviewFirst, @Nullable Runnable onEndRunnable) { + hideFeedback(); + hideHandCoachingAnimation(); + cancelRunningAnimation(); + PendingAnimation anim = new PendingAnimation(300); + AnimatorListenerAdapter resetTaskView = new AnimatorListenerAdapter() { + @Override + public void onAnimationEnd(Animator animation, boolean isReverse) { + mFakeTaskView.setVisibility(View.INVISIBLE); + mFakeTaskView.setAlpha(1); + mRunningWindowAnim = null; + } + }; + if (toOverviewFirst) { + anim.setFloat(mViewSwipeUpAnimation.getCurrentShift(), AnimatedFloat.VALUE, 1, ACCEL); + anim.addListener(new AnimatorListenerAdapter() { + @Override + public void onAnimationEnd(Animator animation, boolean isReverse) { + PendingAnimation fadeAnim = new PendingAnimation(300); + fadeAnim.setViewAlpha(mFakeTaskView, 0, ACCEL); + fadeAnim.addListener(resetTaskView); + AnimatorSet animset = fadeAnim.buildAnim(); + animset.setStartDelay(100); + animset.start(); + mRunningWindowAnim = RunningWindowAnim.wrap(animset); + } + }); + } else { + anim.setViewAlpha(mFakeTaskView, 0, ACCEL); + anim.addListener(resetTaskView); + } + if (onEndRunnable != null) { + anim.addListener(AnimationSuccessListener.forRunnable(onEndRunnable)); + } + AnimatorSet animset = anim.buildAnim(); + animset.start(); + mRunningWindowAnim = RunningWindowAnim.wrap(animset); + } + + void animateFakeTaskViewHome(PointF finalVelocity, @Nullable Runnable onEndRunnable) { + hideFeedback(); + hideHandCoachingAnimation(); + cancelRunningAnimation(); + RectFSpringAnim rectAnim = + mViewSwipeUpAnimation.handleSwipeUpToHome(finalVelocity); + // After home animation finishes, fade out and run onEndRunnable. + rectAnim.addAnimatorListener(AnimationSuccessListener.forRunnable( + () -> fadeOutFakeTaskView(false, onEndRunnable))); + mRunningWindowAnim = RunningWindowAnim.wrap(rectAnim); + } + + @Override + public void setNavBarGestureProgress(@Nullable Float displacement) { + if (displacement == null || mTutorialType == HOME_NAVIGATION_COMPLETE + || mTutorialType == OVERVIEW_NAVIGATION_COMPLETE) { + mFakeTaskView.setVisibility(View.INVISIBLE); + } else { + mFakeTaskView.setVisibility(View.VISIBLE); + if (mRunningWindowAnim == null) { + mViewSwipeUpAnimation.updateDisplacement(displacement); + } + } + } + + class ViewSwipeUpAnimation extends SwipeUpAnimationLogic { + + ViewSwipeUpAnimation(Context context, RecentsAnimationDeviceState deviceState, + GestureState gestureState) { + super(context, deviceState, gestureState, new FakeTransformParams()); + } + + void initDp(DeviceProfile dp) { + initTransitionEndpoints(dp); + mTaskViewSimulator.setPreviewBounds( + new Rect(0, 0, dp.widthPx, dp.heightPx), dp.getInsets()); + } + + @Override + public void updateFinalShift() { + float progress = mCurrentShift.value / mDragLengthFactor; + mWindowTransitionController.setPlayFraction(progress); + mTaskViewSimulator.apply(mTransformParams); + } + + AnimatedFloat getCurrentShift() { + return mCurrentShift; + } + + RectFSpringAnim handleSwipeUpToHome(PointF velocity) { + PointF velocityPxPerMs = new PointF(velocity.x, velocity.y); + float currentShift = mCurrentShift.value; + final float startShift = Utilities.boundToRange(currentShift - velocityPxPerMs.y + * getSingleFrameMs(mContext) / mTransitionDragLength, 0, mDragLengthFactor); + float distanceToTravel = (1 - currentShift) * mTransitionDragLength; + + // we want the page's snap velocity to approximately match the velocity at + // which the user flings, so we scale the duration by a value near to the + // derivative of the scroll interpolator at zero, ie. 2. + long baseDuration = Math.round(Math.abs(distanceToTravel / velocityPxPerMs.y)); + long duration = Math.min(MAX_SWIPE_DURATION, 2 * baseDuration); + HomeAnimationFactory homeAnimFactory = new HomeAnimationFactory(null) { + @Override + public AnimatorPlaybackController createActivityAnimationToHome() { + return AnimatorPlaybackController.wrap(new AnimatorSet(), duration); + } + + @NonNull + @Override + public RectF getWindowTargetRect() { + int fakeHomeIconSizePx = mDp.allAppsIconSizePx; + int fakeHomeIconLeft = (mDp.widthPx - fakeHomeIconSizePx) / 2; + int fakeHomeIconTop = mDp.heightPx - (mDp.allAppsCellHeightPx * 3); + return new RectF(fakeHomeIconLeft, fakeHomeIconTop, + fakeHomeIconLeft + fakeHomeIconSizePx, + fakeHomeIconTop + fakeHomeIconSizePx); + } + }; + RectFSpringAnim windowAnim = createWindowAnimationToHome(startShift, homeAnimFactory); + windowAnim.start(mContext, velocityPxPerMs); + return windowAnim; + } + } + + private class FakeTransformParams extends TransformParams { + + @Override + public SurfaceParams[] createSurfaceParams(BuilderProxy proxy) { + SurfaceParams.Builder builder = new SurfaceParams.Builder((SurfaceControl) null); + proxy.onBuildTargetParams(builder, null, this); + return new SurfaceParams[] {builder.build()}; + } + + @Override + public void applySurfaceParams(SurfaceParams[] params) { + SurfaceParams p = params[0]; + mFakeTaskView.setAnimationMatrix(p.matrix); + mFakeTaskViewRect.set(p.windowCrop); + mFakeTaskViewRadius = p.cornerRadius; + mFakeTaskView.invalidateOutline(); + } + } +} diff --git a/quickstep/src/com/android/quickstep/interaction/TutorialController.java b/quickstep/src/com/android/quickstep/interaction/TutorialController.java index f27d500eff..511c8b6dc6 100644 --- a/quickstep/src/com/android/quickstep/interaction/TutorialController.java +++ b/quickstep/src/com/android/quickstep/interaction/TutorialController.java @@ -140,6 +140,9 @@ abstract class TutorialController implements BackGestureAttemptCallback, void onActionTextButtonClicked(View button) {} void showHandCoachingAnimation() { + if (isComplete()) { + return; + } mHandCoachingAnimation.startLoopedAnimation(mTutorialType); } @@ -153,6 +156,12 @@ abstract class TutorialController implements BackGestureAttemptCallback, hideFeedback(); updateTitles(); updateActionButtons(); + + if (isComplete()) { + hideHandCoachingAnimation(); + } else { + showHandCoachingAnimation(); + } } private void updateTitles() { @@ -190,12 +199,20 @@ abstract class TutorialController implements BackGestureAttemptCallback, button.setOnClickListener(listener); } + private boolean isComplete() { + return mTutorialType == TutorialType.BACK_NAVIGATION_COMPLETE + || mTutorialType == TutorialType.HOME_NAVIGATION_COMPLETE + || mTutorialType == TutorialType.OVERVIEW_NAVIGATION_COMPLETE; + } + /** Denotes the type of the tutorial. */ enum TutorialType { RIGHT_EDGE_BACK_NAVIGATION, LEFT_EDGE_BACK_NAVIGATION, BACK_NAVIGATION_COMPLETE, HOME_NAVIGATION, - HOME_NAVIGATION_COMPLETE + HOME_NAVIGATION_COMPLETE, + OVERVIEW_NAVIGATION, + OVERVIEW_NAVIGATION_COMPLETE } } diff --git a/quickstep/src/com/android/quickstep/interaction/TutorialFragment.java b/quickstep/src/com/android/quickstep/interaction/TutorialFragment.java index a3881cffb0..da6815d776 100644 --- a/quickstep/src/com/android/quickstep/interaction/TutorialFragment.java +++ b/quickstep/src/com/android/quickstep/interaction/TutorialFragment.java @@ -68,6 +68,9 @@ abstract class TutorialFragment extends Fragment implements OnTouchListener { case HOME_NAVIGATION: case HOME_NAVIGATION_COMPLETE: return new HomeGestureTutorialFragment(); + case OVERVIEW_NAVIGATION: + case OVERVIEW_NAVIGATION_COMPLETE: + return new OverviewGestureTutorialFragment(); default: Log.e(LOG_TAG, "Failed to find an appropriate fragment for " + tutorialType.name()); } diff --git a/res/drawable/gesture_tutorial_ripple.xml b/res/drawable/gesture_tutorial_ripple.xml index ca456627c5..782af33ab3 100644 --- a/res/drawable/gesture_tutorial_ripple.xml +++ b/res/drawable/gesture_tutorial_ripple.xml @@ -1,6 +1,3 @@ - - \ No newline at end of file + xmlns:android="http://schemas.android.com/apk/res/android"/> \ No newline at end of file diff --git a/res/values/colors.xml b/res/values/colors.xml index c4ec7dd7f0..043ad9a9d5 100644 --- a/res/values/colors.xml +++ b/res/values/colors.xml @@ -38,14 +38,8 @@ #E5E5E5 #9AA0A6 - #FFFFFFFF - #FF000000 - #99000000 - #FF000000 #A0C2F9 #6DA1FF #FFFFFFFF #1A73E8 - - diff --git a/res/values/styles.xml b/res/values/styles.xml index 26b72052fd..e470c42459 100644 --- a/res/values/styles.xml +++ b/res/values/styles.xml @@ -81,7 +81,6 @@ #CDFFFFFF #FF80868B ?attr/workspaceTextColor - + + + + - diff --git a/res/drawable/drag_handle_indicator_no_shadow.xml b/res/drawable/drag_handle_indicator_no_shadow.xml deleted file mode 100644 index 341e60cda9..0000000000 --- a/res/drawable/drag_handle_indicator_no_shadow.xml +++ /dev/null @@ -1,29 +0,0 @@ - - - - - - - diff --git a/res/layout/launcher.xml b/res/layout/launcher.xml index a13790826b..0c18c8a2ba 100644 --- a/res/layout/launcher.xml +++ b/res/layout/launcher.xml @@ -28,6 +28,12 @@ android:clipToPadding="false" android:importantForAccessibility="no"> + + 1dp 0dp - - 4dp - 18dp - 6dp - 1dp - 48dp - 16dp - 48dp 20dp diff --git a/res/values/drawables.xml b/res/values/drawables.xml index 7d631426cb..9c57ec1214 100644 --- a/res/values/drawables.xml +++ b/res/values/drawables.xml @@ -18,5 +18,4 @@ @drawable/ic_remove_no_shadow @drawable/ic_uninstall_no_shadow @drawable/ic_block_no_shadow - @drawable/drag_handle_indicator_no_shadow \ No newline at end of file diff --git a/res/values/strings.xml b/res/values/strings.xml index 80b511a54c..12beeeb566 100644 --- a/res/values/strings.xml +++ b/res/values/strings.xml @@ -36,7 +36,7 @@ Shortcut isn\'t available - Home screen + Home Custom actions @@ -88,10 +88,6 @@ Personal apps list Work apps list - - Home - Remove diff --git a/src/com/android/launcher3/Launcher.java b/src/com/android/launcher3/Launcher.java index e49c4559f0..f9fba6f80f 100644 --- a/src/com/android/launcher3/Launcher.java +++ b/src/com/android/launcher3/Launcher.java @@ -453,10 +453,10 @@ public class Launcher extends StatefulActivity implements Launche mAppsView.getAlphaProperty(APPS_VIEW_ALPHA_CHANNEL_INDEX).setValue(alpha); } else if (finalState == OVERVIEW || finalState == OVERVIEW_PEEK) { mAppsView.getAlphaProperty(APPS_VIEW_ALPHA_CHANNEL_INDEX).setValue(alpha); - mScrimView.getAlphaProperty(SCRIM_VIEW_ALPHA_CHANNEL_INDEX).setValue(alpha); + mScrimView.setAlpha(alpha); } else { mAppsView.getAlphaProperty(APPS_VIEW_ALPHA_CHANNEL_INDEX).setValue(1f); - mScrimView.getAlphaProperty(SCRIM_VIEW_ALPHA_CHANNEL_INDEX).setValue(1f); + mScrimView.setAlpha(1f); } } }); @@ -553,7 +553,7 @@ public class Launcher extends StatefulActivity implements Launche mAppsView.getAlphaProperty(APPS_VIEW_ALPHA_CHANNEL_INDEX).setValue(alpha); } else if (state == OVERVIEW || state == OVERVIEW_PEEK) { mAppsView.getAlphaProperty(APPS_VIEW_ALPHA_CHANNEL_INDEX).setValue(alpha); - mScrimView.getAlphaProperty(SCRIM_VIEW_ALPHA_CHANNEL_INDEX).setValue(alpha); + mScrimView.setAlpha(alpha); } } @@ -1936,7 +1936,7 @@ public class Launcher extends StatefulActivity implements Launche // Populate event with a fake title based on the current state. // TODO: When can workspace be null? text.add(mWorkspace == null - ? getString(R.string.all_apps_home_button_label) + ? getString(R.string.home_screen) : mStateManager.getState().getDescription(this)); return result; } diff --git a/src/com/android/launcher3/Workspace.java b/src/com/android/launcher3/Workspace.java index 15e0daa8ea..3be9ac7e07 100644 --- a/src/com/android/launcher3/Workspace.java +++ b/src/com/android/launcher3/Workspace.java @@ -3263,7 +3263,7 @@ public class Workspace extends PagedView } if (nScreens == 0) { // When the workspace is not loaded, we do not know how many screen will be bound. - return getContext().getString(R.string.all_apps_home_button_label); + return getContext().getString(R.string.home_screen); } return getContext().getString(R.string.workspace_scroll_format, page + 1, nScreens); } diff --git a/src/com/android/launcher3/allapps/AllAppsTransitionController.java b/src/com/android/launcher3/allapps/AllAppsTransitionController.java index 5b00631659..1224cb0080 100644 --- a/src/com/android/launcher3/allapps/AllAppsTransitionController.java +++ b/src/com/android/launcher3/allapps/AllAppsTransitionController.java @@ -19,7 +19,6 @@ import static com.android.launcher3.LauncherState.ALL_APPS_CONTENT; import static com.android.launcher3.LauncherState.ALL_APPS_HEADER_EXTRA; import static com.android.launcher3.LauncherState.APPS_VIEW_ITEM_MASK; import static com.android.launcher3.LauncherState.OVERVIEW; -import static com.android.launcher3.LauncherState.VERTICAL_SWIPE_INDICATOR; import static com.android.launcher3.anim.Interpolators.FAST_OUT_SLOW_IN; import static com.android.launcher3.anim.Interpolators.FINAL_FRAME; import static com.android.launcher3.anim.Interpolators.INSTANT; @@ -222,9 +221,6 @@ public class AllAppsTransitionController implements StateHandler, mAppsView.getSearchUiManager().setContentVisibility(visibleElements, setter, allAppsFade); - setter.setInt(mScrimView, ScrimView.DRAG_HANDLE_ALPHA, - (visibleElements & VERTICAL_SWIPE_INDICATOR) != 0 ? 255 : 0, allAppsFade); - // Set visibility of the container at the very beginning or end of the transition. setter.setViewAlpha(mAppsView, hasAnyVisibleItem ? 1 : 0, hasAnyVisibleItem ? INSTANT : FINAL_FRAME); diff --git a/src/com/android/launcher3/util/OnboardingPrefs.java b/src/com/android/launcher3/util/OnboardingPrefs.java index 26313e5a21..6e5e7d9355 100644 --- a/src/com/android/launcher3/util/OnboardingPrefs.java +++ b/src/com/android/launcher3/util/OnboardingPrefs.java @@ -36,7 +36,6 @@ public class OnboardingPrefs { 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 String ALL_APPS_COUNT = "launcher.all_apps_count"; public static final String HOTSEAT_DISCOVERY_TIP_COUNT = "launcher.hotseat_discovery_tip_count"; public static final String HOTSEAT_LONGPRESS_TIP_SEEN = "launcher.hotseat_longpress_tip_seen"; @@ -56,7 +55,6 @@ public class OnboardingPrefs { @StringDef(value = { HOME_BOUNCE_COUNT, SHELF_BOUNCE_COUNT, - ALL_APPS_COUNT, HOTSEAT_DISCOVERY_TIP_COUNT }) @Retention(RetentionPolicy.SOURCE) @@ -67,7 +65,6 @@ public class OnboardingPrefs { Map maxCounts = new ArrayMap<>(4); maxCounts.put(HOME_BOUNCE_COUNT, 3); maxCounts.put(SHELF_BOUNCE_COUNT, 3); - maxCounts.put(ALL_APPS_COUNT, 5); maxCounts.put(HOTSEAT_DISCOVERY_TIP_COUNT, 5); MAX_COUNTS = Collections.unmodifiableMap(maxCounts); } diff --git a/src/com/android/launcher3/views/AccessibilityActionsView.java b/src/com/android/launcher3/views/AccessibilityActionsView.java new file mode 100644 index 0000000000..0eacaa3009 --- /dev/null +++ b/src/com/android/launcher3/views/AccessibilityActionsView.java @@ -0,0 +1,97 @@ +/* + * Copyright (C) 2020 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.launcher3.views; + +import static com.android.launcher3.LauncherState.ALL_APPS; +import static com.android.launcher3.LauncherState.NORMAL; + +import android.content.Context; +import android.os.Bundle; +import android.util.AttributeSet; +import android.view.View; +import android.view.accessibility.AccessibilityNodeInfo; +import android.view.accessibility.AccessibilityNodeInfo.AccessibilityAction; + +import androidx.annotation.Nullable; + +import com.android.launcher3.Launcher; +import com.android.launcher3.LauncherState; +import com.android.launcher3.R; +import com.android.launcher3.statemanager.StateManager.StateListener; +import com.android.launcher3.views.OptionsPopupView.OptionItem; + +/** + * Placeholder view to expose additional Launcher actions via accessibility actions + */ +public class AccessibilityActionsView extends View implements StateListener { + + public AccessibilityActionsView(Context context) { + this(context, null); + } + + public AccessibilityActionsView(Context context, @Nullable AttributeSet attrs) { + this(context, attrs, 0); + } + + public AccessibilityActionsView(Context context, @Nullable AttributeSet attrs, + int defStyleAttr) { + super(context, attrs, defStyleAttr); + Launcher.getLauncher(context).getStateManager().addStateListener(this); + setWillNotDraw(true); + } + + @Override + public void onStateTransitionComplete(LauncherState finalState) { + setImportantForAccessibility(finalState == NORMAL + ? IMPORTANT_FOR_ACCESSIBILITY_YES : IMPORTANT_FOR_ACCESSIBILITY_NO); + } + + @Override + public AccessibilityNodeInfo createAccessibilityNodeInfo() { + AccessibilityNodeInfo info = super.createAccessibilityNodeInfo(); + Launcher l = Launcher.getLauncher(getContext()); + info.addAction(new AccessibilityAction( + R.string.all_apps_button_label, l.getText(R.string.all_apps_button_label))); + for (OptionItem item : OptionsPopupView.getOptions(l)) { + info.addAction(new AccessibilityAction(item.labelRes, l.getText(item.labelRes))); + } + return info; + } + + @Override + public boolean performAccessibilityAction(int action, Bundle arguments) { + if (super.performAccessibilityAction(action, arguments)) { + return true; + } + Launcher l = Launcher.getLauncher(getContext()); + if (action == R.string.all_apps_button_label) { + l.getStateManager().goToState(ALL_APPS); + return true; + } + for (OptionItem item : OptionsPopupView.getOptions(l)) { + if (item.labelRes == action) { + if (item.eventId.getId() > 0) { + l.getStatsLogManager().logger().log(item.eventId); + } + if (item.clickListener.onLongClick(this)) { + return true; + } + } + } + return false; + } +} diff --git a/src/com/android/launcher3/views/OptionsPopupView.java b/src/com/android/launcher3/views/OptionsPopupView.java index 049a1acbf3..e95dc5b83f 100644 --- a/src/com/android/launcher3/views/OptionsPopupView.java +++ b/src/com/android/launcher3/views/OptionsPopupView.java @@ -85,10 +85,10 @@ public class OptionsPopupView extends ArrowPopup if (item == null) { return false; } - if (item.mEventId.getId() > 0) { - mLauncher.getStatsLogManager().logger().log(item.mEventId); + if (item.eventId.getId() > 0) { + mLauncher.getStatsLogManager().logger().log(item.eventId); } - if (item.mClickListener.onLongClick(view)) { + if (item.clickListener.onLongClick(view)) { close(true); return true; } @@ -130,8 +130,8 @@ public class OptionsPopupView extends ArrowPopup for (OptionItem item : items) { DeepShortcutView view = (DeepShortcutView) popup.inflateAndAdd(R.layout.system_shortcut, popup); - view.getIconView().setBackgroundResource(item.mIconRes); - view.getBubbleText().setText(item.mLabelRes); + view.getIconView().setBackgroundResource(item.iconRes); + view.getBubbleText().setText(item.labelRes); view.setDividerVisibility(View.INVISIBLE); view.setOnClickListener(popup); view.setOnLongClickListener(popup); @@ -152,7 +152,13 @@ public class OptionsPopupView extends ArrowPopup y = launcher.getDragLayer().getHeight() / 2; } RectF target = new RectF(x - halfSize, y - halfSize, x + halfSize, y + halfSize); + show(launcher, target, getOptions(launcher)); + } + /** + * Returns the list of supported actions + */ + public static ArrayList getOptions(Launcher launcher) { ArrayList options = new ArrayList<>(); int resString = Utilities.existsStyleWallpapers(launcher) ? R.string.styles_wallpaper_button_text : R.string.wallpaper_button_text; @@ -170,10 +176,10 @@ public class OptionsPopupView extends ArrowPopup LAUNCHER_SETTINGS_BUTTON_TAP_OR_LONGPRESS, OptionsPopupView::startSettings)); - show(launcher, target, options); + return options; } - public static boolean onWidgetsClicked(View view) { + private static boolean onWidgetsClicked(View view) { return openWidgets(Launcher.getLauncher(view.getContext())) != null; } @@ -188,7 +194,7 @@ public class OptionsPopupView extends ArrowPopup } } - public static boolean startSettings(View view) { + private static boolean startSettings(View view) { TestLogging.recordEvent(TestProtocol.SEQUENCE_MAIN, "start: startSettings"); Launcher launcher = Launcher.getLauncher(view.getContext()); launcher.startActivity(new Intent(Intent.ACTION_APPLICATION_PREFERENCES) @@ -201,7 +207,7 @@ public class OptionsPopupView extends ArrowPopup * Event handler for the wallpaper picker button that appears after a long press * on the home screen. */ - public static boolean startWallpaperPicker(View v) { + private static boolean startWallpaperPicker(View v) { Launcher launcher = Launcher.getLauncher(v.getContext()); if (!Utilities.isWallpaperAllowed(launcher)) { Toast.makeText(launcher, R.string.msg_disabled_by_admin, Toast.LENGTH_SHORT).show(); @@ -233,17 +239,17 @@ public class OptionsPopupView extends ArrowPopup public static class OptionItem { - private final int mLabelRes; - private final int mIconRes; - private final EventEnum mEventId; - private final OnLongClickListener mClickListener; + public final int labelRes; + public final int iconRes; + public final EventEnum eventId; + public final OnLongClickListener clickListener; public OptionItem(int labelRes, int iconRes, EventEnum eventId, OnLongClickListener clickListener) { - mLabelRes = labelRes; - mIconRes = iconRes; - mEventId = eventId; - mClickListener = clickListener; + this.labelRes = labelRes; + this.iconRes = iconRes; + this.eventId = eventId; + this.clickListener = clickListener; } } } diff --git a/src/com/android/launcher3/views/ScrimView.java b/src/com/android/launcher3/views/ScrimView.java index 22faf97cc4..7f0765becd 100644 --- a/src/com/android/launcher3/views/ScrimView.java +++ b/src/com/android/launcher3/views/ScrimView.java @@ -15,119 +15,37 @@ */ package com.android.launcher3.views; -import static android.content.Context.ACCESSIBILITY_SERVICE; -import static android.view.MotionEvent.ACTION_DOWN; - import static androidx.core.graphics.ColorUtils.compositeColors; -import static com.android.launcher3.LauncherState.ALL_APPS; -import static com.android.launcher3.LauncherState.NORMAL; -import static com.android.launcher3.anim.Interpolators.ACCEL_DEACCEL; -import static com.android.launcher3.anim.Interpolators.DEACCEL; -import static com.android.launcher3.anim.Interpolators.LINEAR; -import static com.android.launcher3.anim.Interpolators.clampToProgress; import static com.android.launcher3.icons.GraphicsUtils.setColorAlphaBound; import static com.android.launcher3.util.SystemUiController.UI_STATE_SCRIM_VIEW; -import android.animation.Animator; -import android.animation.AnimatorListenerAdapter; -import android.animation.Keyframe; -import android.animation.ObjectAnimator; -import android.animation.PropertyValuesHolder; -import android.animation.RectEvaluator; import android.content.Context; -import android.content.res.Resources; import android.graphics.Canvas; import android.graphics.Color; -import android.graphics.Point; import android.graphics.Rect; -import android.graphics.RectF; -import android.graphics.drawable.Drawable; -import android.os.Bundle; import android.util.AttributeSet; -import android.util.IntProperty; -import android.view.KeyEvent; -import android.view.MotionEvent; import android.view.View; -import android.view.accessibility.AccessibilityManager; -import android.view.accessibility.AccessibilityManager.AccessibilityStateChangeListener; -import androidx.annotation.NonNull; -import androidx.annotation.Nullable; import androidx.core.graphics.ColorUtils; -import androidx.core.view.ViewCompat; -import androidx.core.view.accessibility.AccessibilityNodeInfoCompat; -import androidx.core.view.accessibility.AccessibilityNodeInfoCompat.AccessibilityActionCompat; -import androidx.customview.widget.ExploreByTouchHelper; -import com.android.launcher3.DeviceProfile; import com.android.launcher3.Insettable; import com.android.launcher3.Launcher; -import com.android.launcher3.LauncherState; import com.android.launcher3.R; -import com.android.launcher3.Utilities; -import com.android.launcher3.statemanager.StateManager; -import com.android.launcher3.statemanager.StateManager.StateListener; import com.android.launcher3.uioverrides.WallpaperColorInfo; import com.android.launcher3.uioverrides.WallpaperColorInfo.OnChangeListener; -import com.android.launcher3.userevent.nano.LauncherLogProto.Action; -import com.android.launcher3.userevent.nano.LauncherLogProto.ControlType; -import com.android.launcher3.util.MultiValueAlpha; -import com.android.launcher3.util.MultiValueAlpha.AlphaProperty; import com.android.launcher3.util.Themes; -import com.android.launcher3.widget.WidgetsFullSheet; - -import java.util.List; /** * Simple scrim which draws a flat color */ -public class ScrimView extends View implements Insettable, OnChangeListener, - AccessibilityStateChangeListener { - - public static final IntProperty DRAG_HANDLE_ALPHA = - new IntProperty("dragHandleAlpha") { - - @Override - public Integer get(ScrimView scrimView) { - return scrimView.mDragHandleAlpha; - } - - @Override - public void setValue(ScrimView scrimView, int value) { - scrimView.setDragHandleAlpha(value); - } - }; - private static final int WALLPAPERS = R.string.wallpaper_button_text; - private static final int WIDGETS = R.string.widget_button_text; - private static final int SETTINGS = R.string.settings_button_text; - private static final int ALPHA_CHANNEL_COUNT = 1; - - private static final long DRAG_HANDLE_BOUNCE_DURATION_MS = 300; - // How much to delay before repeating the bounce. - private static final long DRAG_HANDLE_BOUNCE_DELAY_MS = 200; - // Repeat this many times (i.e. total number of bounces is 1 + this). - private static final int DRAG_HANDLE_BOUNCE_REPEAT_COUNT = 2; - - private final Rect mTempRect = new Rect(); - private final int[] mTempPos = new int[2]; +public class ScrimView extends View implements Insettable, OnChangeListener { protected final T mLauncher; private final WallpaperColorInfo mWallpaperColorInfo; - private final AccessibilityManager mAM; protected final int mEndScrim; protected final boolean mIsScrimDark; - private final StateListener mAccessibilityLauncherStateListener = - new StateListener() { - @Override - public void onStateTransitionComplete(LauncherState finalState) { - setImportantForAccessibility(finalState == ALL_APPS - ? IMPORTANT_FOR_ACCESSIBILITY_NO_HIDE_DESCENDANTS - : IMPORTANT_FOR_ACCESSIBILITY_AUTO); - } - }; - protected float mMaxScrimAlpha; protected float mProgress = 1; @@ -137,22 +55,6 @@ public class ScrimView extends View implements Insettable, O protected int mEndFlatColor; protected int mEndFlatColorAlpha; - protected final Point mDragHandleSize; - private final int mDragHandleTouchSize; - private final int mDragHandlePaddingInVerticalBarLayout; - protected float mDragHandleOffset; - private final Rect mDragHandleBounds; - private final RectF mHitRect = new RectF(); - private ObjectAnimator mDragHandleAnim; - - private final MultiValueAlpha mMultiValueAlpha; - - private final AccessibilityHelper mAccessibilityHelper; - @Nullable - protected Drawable mDragHandle; - - private int mDragHandleAlpha = 255; - public ScrimView(Context context, AttributeSet attrs) { super(context, attrs); mLauncher = Launcher.cast(Launcher.getLauncher(context)); @@ -161,59 +63,24 @@ public class ScrimView extends View implements Insettable, O mIsScrimDark = ColorUtils.calculateLuminance(mEndScrim) < 0.5f; mMaxScrimAlpha = 0.7f; - - Resources res = context.getResources(); - mDragHandleSize = new Point(res.getDimensionPixelSize(R.dimen.vertical_drag_handle_width), - res.getDimensionPixelSize(R.dimen.vertical_drag_handle_height)); - mDragHandleBounds = new Rect(0, 0, mDragHandleSize.x, mDragHandleSize.y); - mDragHandleTouchSize = res.getDimensionPixelSize(R.dimen.vertical_drag_handle_touch_size); - mDragHandlePaddingInVerticalBarLayout = context.getResources() - .getDimensionPixelSize(R.dimen.vertical_drag_handle_padding_in_vertical_bar_layout); - - mAccessibilityHelper = createAccessibilityHelper(); - ViewCompat.setAccessibilityDelegate(this, mAccessibilityHelper); - - mAM = (AccessibilityManager) context.getSystemService(ACCESSIBILITY_SERVICE); setFocusable(false); - mMultiValueAlpha = new MultiValueAlpha(this, ALPHA_CHANNEL_COUNT); - } - - public AlphaProperty getAlphaProperty(int index) { - return mMultiValueAlpha.getProperty(index); - } - - @NonNull - protected AccessibilityHelper createAccessibilityHelper() { - return new AccessibilityHelper(); } @Override - public void setInsets(Rect insets) { - updateDragHandleBounds(); - updateDragHandleVisibility(); - } + public void setInsets(Rect insets) { } - @Override - protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { - super.onMeasure(widthMeasureSpec, heightMeasureSpec); - updateDragHandleBounds(); - } @Override protected void onAttachedToWindow() { super.onAttachedToWindow(); mWallpaperColorInfo.addOnChangeListener(this); onExtractedColorsChanged(mWallpaperColorInfo); - - mAM.addAccessibilityStateChangeListener(this); - onAccessibilityStateChanged(mAM.isEnabled()); } @Override protected void onDetachedFromWindow() { super.onDetachedFromWindow(); mWallpaperColorInfo.removeOnChangeListener(this); - mAM.removeAccessibilityStateChangeListener(this); } @Override @@ -234,10 +101,8 @@ public class ScrimView extends View implements Insettable, O public void setProgress(float progress) { if (mProgress != progress) { mProgress = progress; - stopDragHandleEducationAnim(); updateColors(); updateSysUiColors(); - updateDragHandleAlpha(); invalidate(); } } @@ -260,286 +125,10 @@ public class ScrimView extends View implements Insettable, O } } - protected void updateDragHandleAlpha() { - if (mDragHandle != null) { - mDragHandle.setAlpha(mDragHandleAlpha); - } - } - - private void setDragHandleAlpha(int alpha) { - if (alpha != mDragHandleAlpha) { - mDragHandleAlpha = alpha; - if (mDragHandle != null) { - mDragHandle.setAlpha(mDragHandleAlpha); - invalidate(); - } - } - } - @Override protected void onDraw(Canvas canvas) { if (mCurrentFlatColor != 0) { canvas.drawColor(mCurrentFlatColor); } - drawDragHandle(canvas); - } - - protected void drawDragHandle(Canvas canvas) { - if (mDragHandle != null) { - canvas.translate(0, -mDragHandleOffset); - mDragHandle.draw(canvas); - canvas.translate(0, mDragHandleOffset); - } - } - - @Override - public boolean onTouchEvent(MotionEvent event) { - boolean superHandledTouch = super.onTouchEvent(event); - if (event.getAction() == ACTION_DOWN) { - if (!superHandledTouch && mHitRect.contains(event.getX(), event.getY())) { - if (startDragHandleEducationAnim()) { - return true; - } - } - stopDragHandleEducationAnim(); - } - return superHandledTouch; - } - - /** - * Animates the drag handle to demonstrate how to get to all apps. - * @return Whether the animation was started (false if drag handle is invisible). - */ - public boolean startDragHandleEducationAnim() { - stopDragHandleEducationAnim(); - - if (mDragHandle == null || mDragHandle.getAlpha() != 255) { - return false; - } - - final Drawable drawable = mDragHandle; - mDragHandle = null; - - Rect bounds = new Rect(mDragHandleBounds); - bounds.offset(0, -(int) mDragHandleOffset); - drawable.setBounds(bounds); - - Rect topBounds = new Rect(bounds); - topBounds.offset(0, -bounds.height()); - - Rect invalidateRegion = new Rect(bounds); - invalidateRegion.top = topBounds.top; - - final float progressToReachTop = 0.6f; - Keyframe frameTop = Keyframe.ofObject(progressToReachTop, topBounds); - frameTop.setInterpolator(DEACCEL); - Keyframe frameBot = Keyframe.ofObject(1, bounds); - frameBot.setInterpolator(ACCEL_DEACCEL); - PropertyValuesHolder holder = PropertyValuesHolder.ofKeyframe("bounds", - Keyframe.ofObject(0, bounds), frameTop, frameBot); - holder.setEvaluator(new RectEvaluator()); - - mDragHandleAnim = ObjectAnimator.ofPropertyValuesHolder(drawable, holder); - long totalBounceDuration = DRAG_HANDLE_BOUNCE_DURATION_MS + DRAG_HANDLE_BOUNCE_DELAY_MS; - // The bounce finishes by this progress, the rest of the duration just delays next bounce. - float delayStartProgress = 1f - (float) DRAG_HANDLE_BOUNCE_DELAY_MS / totalBounceDuration; - mDragHandleAnim.addUpdateListener((v) -> invalidate(invalidateRegion)); - mDragHandleAnim.setDuration(totalBounceDuration); - mDragHandleAnim.setInterpolator(clampToProgress(LINEAR, 0, delayStartProgress)); - mDragHandleAnim.setRepeatCount(DRAG_HANDLE_BOUNCE_REPEAT_COUNT); - getOverlay().add(drawable); - - mDragHandleAnim.addListener(new AnimatorListenerAdapter() { - @Override - public void onAnimationEnd(Animator animation) { - mDragHandleAnim = null; - getOverlay().remove(drawable); - updateDragHandleVisibility(drawable); - } - }); - mDragHandleAnim.start(); - return true; - } - - private void stopDragHandleEducationAnim() { - if (mDragHandleAnim != null) { - mDragHandleAnim.end(); - } - } - - protected void updateDragHandleBounds() { - DeviceProfile grid = mLauncher.getDeviceProfile(); - final int left; - final int width = getMeasuredWidth(); - final int top = getMeasuredHeight() - mDragHandleSize.y - grid.getInsets().bottom; - final int topMargin; - - if (grid.isVerticalBarLayout()) { - topMargin = grid.workspacePadding.bottom + mDragHandlePaddingInVerticalBarLayout; - if (grid.isSeascape()) { - left = width - grid.getInsets().right - mDragHandleSize.x - - mDragHandlePaddingInVerticalBarLayout; - } else { - left = grid.getInsets().left + mDragHandlePaddingInVerticalBarLayout; - } - } else { - left = Math.round((width - mDragHandleSize.x) / 2f); - topMargin = grid.hotseatBarSizePx; - } - mDragHandleBounds.offsetTo(left, top - topMargin); - mHitRect.set(mDragHandleBounds); - // Inset outwards to increase touch size. - mHitRect.inset((mDragHandleSize.x - mDragHandleTouchSize) / 2f, - (mDragHandleSize.y - mDragHandleTouchSize) / 2f); - - if (mDragHandle != null) { - mDragHandle.setBounds(mDragHandleBounds); - } - } - - @Override - public void onAccessibilityStateChanged(boolean enabled) { - StateManager stateManager = mLauncher.getStateManager(); - stateManager.removeStateListener(mAccessibilityLauncherStateListener); - - if (enabled) { - stateManager.addStateListener(mAccessibilityLauncherStateListener); - mAccessibilityLauncherStateListener.onStateTransitionComplete(stateManager.getState()); - } else { - setImportantForAccessibility(IMPORTANT_FOR_ACCESSIBILITY_NO_HIDE_DESCENDANTS); - } - updateDragHandleVisibility(); - } - - public void updateDragHandleVisibility() { - updateDragHandleVisibility(null); - } - - private void updateDragHandleVisibility(@Nullable Drawable recycle) { - boolean visible = shouldDragHandleBeVisible(); - boolean wasVisible = mDragHandle != null; - if (visible != wasVisible) { - if (visible) { - mDragHandle = recycle != null ? recycle : - mLauncher.getDrawable(R.drawable.drag_handle_indicator_shadow); - mDragHandle.setBounds(mDragHandleBounds); - - updateDragHandleAlpha(); - } else { - mDragHandle = null; - } - invalidate(); - } - } - - protected boolean shouldDragHandleBeVisible() { - return mLauncher.getDeviceProfile().isVerticalBarLayout() || mAM.isEnabled(); - } - - @Override - public boolean dispatchHoverEvent(MotionEvent event) { - return mAccessibilityHelper.dispatchHoverEvent(event) || super.dispatchHoverEvent(event); - } - - @Override - public boolean dispatchKeyEvent(KeyEvent event) { - return mAccessibilityHelper.dispatchKeyEvent(event) || super.dispatchKeyEvent(event); - } - - @Override - public void onFocusChanged(boolean gainFocus, int direction, - Rect previouslyFocusedRect) { - super.onFocusChanged(gainFocus, direction, previouslyFocusedRect); - mAccessibilityHelper.onFocusChanged(gainFocus, direction, previouslyFocusedRect); - } - - protected class AccessibilityHelper extends ExploreByTouchHelper { - - private static final int DRAG_HANDLE_ID = 1; - - public AccessibilityHelper() { - super(ScrimView.this); - } - - @Override - protected int getVirtualViewAt(float x, float y) { - return mHitRect.contains((int) x, (int) y) - ? DRAG_HANDLE_ID : INVALID_ID; - } - - @Override - protected void getVisibleVirtualViews(List virtualViewIds) { - virtualViewIds.add(DRAG_HANDLE_ID); - } - - @Override - protected void onPopulateNodeForVirtualView(int virtualViewId, - AccessibilityNodeInfoCompat node) { - node.setContentDescription(getContext().getString(R.string.all_apps_button_label)); - node.setBoundsInParent(mDragHandleBounds); - - getLocationOnScreen(mTempPos); - mTempRect.set(mDragHandleBounds); - mTempRect.offset(mTempPos[0], mTempPos[1]); - node.setBoundsInScreen(mTempRect); - - node.addAction(AccessibilityNodeInfoCompat.ACTION_CLICK); - node.setClickable(true); - node.setFocusable(true); - - if (mLauncher.isInState(NORMAL)) { - Context context = getContext(); - if (Utilities.isWallpaperAllowed(context)) { - node.addAction( - new AccessibilityActionCompat(WALLPAPERS, context.getText(WALLPAPERS))); - } - node.addAction(new AccessibilityActionCompat(WIDGETS, context.getText(WIDGETS))); - node.addAction(new AccessibilityActionCompat(SETTINGS, context.getText(SETTINGS))); - } - } - - @Override - protected boolean onPerformActionForVirtualView( - int virtualViewId, int action, Bundle arguments) { - if (action == AccessibilityNodeInfoCompat.ACTION_CLICK) { - mLauncher.getUserEventDispatcher().logActionOnControl( - Action.Touch.TAP, ControlType.ALL_APPS_BUTTON, - mLauncher.getStateManager().getState().containerType); - mLauncher.getStateManager().goToState(ALL_APPS); - return true; - } else if (action == WALLPAPERS) { - return OptionsPopupView.startWallpaperPicker(ScrimView.this); - } else if (action == WIDGETS) { - int originalImportanceForAccessibility = getImportantForAccessibility(); - setImportantForAccessibility(IMPORTANT_FOR_ACCESSIBILITY_NO_HIDE_DESCENDANTS); - WidgetsFullSheet widgetsFullSheet = OptionsPopupView.openWidgets(mLauncher); - if (widgetsFullSheet == null) { - setImportantForAccessibility(originalImportanceForAccessibility); - return false; - } - widgetsFullSheet.addOnAttachStateChangeListener(new OnAttachStateChangeListener() { - @Override - public void onViewAttachedToWindow(View view) {} - - @Override - public void onViewDetachedFromWindow(View view) { - setImportantForAccessibility(originalImportanceForAccessibility); - widgetsFullSheet.removeOnAttachStateChangeListener(this); - } - }); - return true; - } else if (action == SETTINGS) { - return OptionsPopupView.startSettings(ScrimView.this); - } - - return false; - } - } - - /** - * @return The top of this scrim view, or {@link Float#MAX_VALUE} if there's no distinct top. - */ - public float getVisualTop() { - return Float.MAX_VALUE; } } -- GitLab From df10ff46bd916e7bd0f685f8c59af8783d52906d Mon Sep 17 00:00:00 2001 From: Samuel Fufa Date: Sun, 26 Jul 2020 19:11:14 -0700 Subject: [PATCH 0051/1664] Migrate AllAppsSearch [part 1/3] [Video attached to bug report] Setup DeviceSearchAlgorithm to handle on device search Bug: 161801950 Test: Manual Change-Id: Ib55f415f9992ceab687bbbfe904d153157541648 --- res/layout/search_section_title.xml | 25 +++++ res/values/strings.xml | 4 + .../launcher3/allapps/AllAppsGridAdapter.java | 9 ++ .../allapps/AllAppsRecyclerView.java | 1 + .../allapps/AlphabeticalAppsList.java | 94 ++++++++++--------- .../search/AllAppsSearchBarController.java | 7 +- .../search/AppsSearchContainerLayout.java | 13 +-- .../allapps/search/AppsSearchPipeline.java | 92 ++++++++++++++++++ .../search/DefaultAppSearchAlgorithm.java | 34 ++----- .../allapps/search/SearchPipeline.java | 32 +++++++ .../allapps/search/SearchSectionInfo.java | 36 +++++++ 11 files changed, 267 insertions(+), 80 deletions(-) create mode 100644 res/layout/search_section_title.xml create mode 100644 src/com/android/launcher3/allapps/search/AppsSearchPipeline.java create mode 100644 src/com/android/launcher3/allapps/search/SearchPipeline.java create mode 100644 src/com/android/launcher3/allapps/search/SearchSectionInfo.java diff --git a/res/layout/search_section_title.xml b/res/layout/search_section_title.xml new file mode 100644 index 0000000000..9ab27c1620 --- /dev/null +++ b/res/layout/search_section_title.xml @@ -0,0 +1,25 @@ + + + \ No newline at end of file diff --git a/res/values/strings.xml b/res/values/strings.xml index 80b511a54c..0ec0581457 100644 --- a/res/values/strings.xml +++ b/res/values/strings.xml @@ -67,6 +67,10 @@ App + + + Apps + Notifications diff --git a/src/com/android/launcher3/allapps/AllAppsGridAdapter.java b/src/com/android/launcher3/allapps/AllAppsGridAdapter.java index d65316090c..dec92df2d3 100644 --- a/src/com/android/launcher3/allapps/AllAppsGridAdapter.java +++ b/src/com/android/launcher3/allapps/AllAppsGridAdapter.java @@ -66,6 +66,8 @@ public class AllAppsGridAdapter extends RecyclerView.Adapter mSearchResults; + private ArrayList mSearchResults; private AllAppsGridAdapter mAdapter; private AppInfoComparator mAppNameComparator; private final int mNumAppsPerRow; @@ -210,10 +225,10 @@ public class AlphabeticalAppsList implements AllAppsStore.OnUpdateListener { } /** - * Sets the sorted list of filtered components. + * Sets results list for search */ - public boolean setOrderedFilter(ArrayList f) { - if (mSearchResults != f) { + public boolean setSearchResults(ArrayList f) { + if (f == null || mSearchResults != f) { boolean same = mSearchResults != null && mSearchResults.equals(f); mSearchResults = f; onAppsUpdated(); @@ -298,35 +313,42 @@ public class AlphabeticalAppsList implements AllAppsStore.OnUpdateListener { // Recreate the filtered and sectioned apps (for convenience for the grid layout) from the // ordered set of sections - for (AppInfo info : getFiltersAppInfos()) { - String sectionName = info.sectionName; - - // Create a new section if the section names do not match - if (!sectionName.equals(lastSectionName)) { - lastSectionName = sectionName; - lastFastScrollerSectionInfo = new FastScrollSectionInfo(sectionName); - mFastScrollerSections.add(lastFastScrollerSectionInfo); - } + if (!hasFilter()) { + for (AppInfo info : mApps) { + String sectionName = info.sectionName; + + // Create a new section if the section names do not match + if (!sectionName.equals(lastSectionName)) { + lastSectionName = sectionName; + lastFastScrollerSectionInfo = new FastScrollSectionInfo(sectionName); + mFastScrollerSections.add(lastFastScrollerSectionInfo); + } - // Create an app item - AdapterItem appItem = AdapterItem.asApp(position++, sectionName, info, appIndex++); - if (lastFastScrollerSectionInfo.fastScrollToItem == null) { - lastFastScrollerSectionInfo.fastScrollToItem = appItem; + // Create an app item + AdapterItem appItem = AdapterItem.asApp(position++, sectionName, info, appIndex++); + if (lastFastScrollerSectionInfo.fastScrollToItem == null) { + lastFastScrollerSectionInfo.fastScrollToItem = appItem; + } + mAdapterItems.add(appItem); + mFilteredApps.add(info); } - mAdapterItems.add(appItem); - mFilteredApps.add(info); - } + } else { + mAdapterItems.addAll(mSearchResults); + List appInfos = mSearchResults.stream().filter( + i -> AllAppsGridAdapter.isIconViewType(i.viewType)).map(i -> i.appInfo).collect( + Collectors.toList()); + mFilteredApps.addAll(appInfos); + if (!FeatureFlags.ENABLE_DEVICE_SEARCH.get()) { + // Append the search market item + if (hasNoFilteredResults()) { + mAdapterItems.add(AdapterItem.asEmptySearch(position++)); + } else { + mAdapterItems.add(AdapterItem.asAllAppsDivider(position++)); + } + mAdapterItems.add(AdapterItem.asMarketSearch(position++)); - if (hasFilter()) { - // Append the search market item - if (hasNoFilteredResults()) { - mAdapterItems.add(AdapterItem.asEmptySearch(position++)); - } else { - mAdapterItems.add(AdapterItem.asAllAppsDivider(position++)); } - mAdapterItems.add(AdapterItem.asMarketSearch(position++)); } - if (mNumAppsPerRow != 0) { // Update the number of rows in the adapter after we do all the merging (otherwise, we // would have to shift the values again) @@ -381,18 +403,4 @@ public class AlphabeticalAppsList implements AllAppsStore.OnUpdateListener { } } } - - private List getFiltersAppInfos() { - if (mSearchResults == null) { - return mApps; - } - ArrayList result = new ArrayList<>(); - for (ComponentKey key : mSearchResults) { - AppInfo match = mAllAppsStore.getApp(key); - if (match != null) { - result.add(match); - } - } - return result; - } } diff --git a/src/com/android/launcher3/allapps/search/AllAppsSearchBarController.java b/src/com/android/launcher3/allapps/search/AllAppsSearchBarController.java index df1cd26da4..db94e8b6e8 100644 --- a/src/com/android/launcher3/allapps/search/AllAppsSearchBarController.java +++ b/src/com/android/launcher3/allapps/search/AllAppsSearchBarController.java @@ -28,7 +28,7 @@ import android.widget.TextView.OnEditorActionListener; import com.android.launcher3.BaseDraggingActivity; import com.android.launcher3.ExtendedEditText; import com.android.launcher3.Utilities; -import com.android.launcher3.util.ComponentKey; +import com.android.launcher3.allapps.AlphabeticalAppsList; import com.android.launcher3.util.PackageManagerHelper; import java.util.ArrayList; @@ -50,6 +50,7 @@ public class AllAppsSearchBarController public void setVisibility(int visibility) { mInput.setVisibility(visibility); } + /** * Sets the references to the apps model and the search result callback. */ @@ -164,9 +165,9 @@ public class AllAppsSearchBarController /** * Called when the search is complete. * - * @param apps sorted list of matching components or null if in case of failure. + * @param items sorted list of search result adapter items. */ - void onSearchResult(String query, ArrayList apps); + void onSearchResult(String query, ArrayList items); /** * Called when the search results should be cleared. diff --git a/src/com/android/launcher3/allapps/search/AppsSearchContainerLayout.java b/src/com/android/launcher3/allapps/search/AppsSearchContainerLayout.java index 356c52ca39..df6a89bcb1 100644 --- a/src/com/android/launcher3/allapps/search/AppsSearchContainerLayout.java +++ b/src/com/android/launcher3/allapps/search/AppsSearchContainerLayout.java @@ -38,13 +38,13 @@ import com.android.launcher3.BaseDraggingActivity; import com.android.launcher3.DeviceProfile; import com.android.launcher3.ExtendedEditText; import com.android.launcher3.Insettable; +import com.android.launcher3.LauncherAppState; import com.android.launcher3.R; import com.android.launcher3.allapps.AllAppsContainerView; import com.android.launcher3.allapps.AllAppsStore; import com.android.launcher3.allapps.AlphabeticalAppsList; import com.android.launcher3.allapps.SearchUiManager; import com.android.launcher3.anim.PropertySetter; -import com.android.launcher3.util.ComponentKey; import java.util.ArrayList; @@ -135,7 +135,8 @@ public class AppsSearchContainerLayout extends ExtendedEditText mApps = appsView.getApps(); mAppsView = appsView; mSearchBarController.initialize( - new DefaultAppSearchAlgorithm(mApps.getApps()), this, mLauncher, this); + new DefaultAppSearchAlgorithm(LauncherAppState.getInstance(mLauncher)), this, + mLauncher, this); } @Override @@ -168,9 +169,9 @@ public class AppsSearchContainerLayout extends ExtendedEditText } @Override - public void onSearchResult(String query, ArrayList apps) { - if (apps != null) { - mApps.setOrderedFilter(apps); + public void onSearchResult(String query, ArrayList items) { + if (items != null) { + mApps.setSearchResults(items); notifyResultChanged(); mAppsView.setLastSearchQuery(query); } @@ -178,7 +179,7 @@ public class AppsSearchContainerLayout extends ExtendedEditText @Override public void clearSearchResult() { - if (mApps.setOrderedFilter(null)) { + if (mApps.setSearchResults(null)) { notifyResultChanged(); } diff --git a/src/com/android/launcher3/allapps/search/AppsSearchPipeline.java b/src/com/android/launcher3/allapps/search/AppsSearchPipeline.java new file mode 100644 index 0000000000..5beb956079 --- /dev/null +++ b/src/com/android/launcher3/allapps/search/AppsSearchPipeline.java @@ -0,0 +1,92 @@ +/* + * Copyright (C) 2020 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.android.launcher3.allapps.search; + +import androidx.annotation.WorkerThread; + +import com.android.launcher3.LauncherAppState; +import com.android.launcher3.R; +import com.android.launcher3.allapps.AlphabeticalAppsList.AdapterItem; +import com.android.launcher3.model.AllAppsList; +import com.android.launcher3.model.BaseModelUpdateTask; +import com.android.launcher3.model.BgDataModel; +import com.android.launcher3.model.data.AppInfo; + +import java.util.ArrayList; +import java.util.List; +import java.util.function.Consumer; + +/** + * A device search section for handling app searches + */ +public class AppsSearchPipeline implements SearchPipeline { + + private static final int MAX_RESULTS_COUNT = 5; + + private final SearchSectionInfo mSearchSectionInfo; + private final LauncherAppState mLauncherAppState; + + public AppsSearchPipeline(LauncherAppState launcherAppState) { + mLauncherAppState = launcherAppState; + mSearchSectionInfo = new SearchSectionInfo(R.string.search_corpus_apps); + } + + @Override + @WorkerThread + public void performSearch(String query, Consumer> callback) { + mLauncherAppState.getModel().enqueueModelUpdateTask(new BaseModelUpdateTask() { + @Override + public void execute(LauncherAppState app, BgDataModel dataModel, AllAppsList apps) { + callback.accept(getAdapterItems(getTitleMatchResult(apps.data, query))); + } + }); + } + + /** + * Filters {@link AppInfo}s matching specified query + */ + public static ArrayList getTitleMatchResult(List apps, String query) { + // Do an intersection of the words in the query and each title, and filter out all the + // apps that don't match all of the words in the query. + final String queryTextLower = query.toLowerCase(); + final ArrayList result = new ArrayList<>(); + DefaultAppSearchAlgorithm.StringMatcher matcher = + DefaultAppSearchAlgorithm.StringMatcher.getInstance(); + for (AppInfo info : apps) { + if (DefaultAppSearchAlgorithm.matches(info, queryTextLower, matcher)) { + result.add(info); + } + } + return result; + } + + private ArrayList getAdapterItems(List matchingApps) { + ArrayList items = new ArrayList<>(); + if (matchingApps.isEmpty()) { + return items; + } + items.add(AdapterItem.asSearchTitle(mSearchSectionInfo, 0)); + int existingItems = items.size(); + int searchResultsCount = Math.min(matchingApps.size(), MAX_RESULTS_COUNT); + for (int i = 0; i < searchResultsCount; i++) { + AdapterItem appItem = AdapterItem.asApp(i + existingItems, "", matchingApps.get(i), i); + appItem.searchSectionInfo = mSearchSectionInfo; + items.add(appItem); + } + + return items; + } +} diff --git a/src/com/android/launcher3/allapps/search/DefaultAppSearchAlgorithm.java b/src/com/android/launcher3/allapps/search/DefaultAppSearchAlgorithm.java index f72a9888cf..db10311675 100644 --- a/src/com/android/launcher3/allapps/search/DefaultAppSearchAlgorithm.java +++ b/src/com/android/launcher3/allapps/search/DefaultAppSearchAlgorithm.java @@ -17,24 +17,22 @@ package com.android.launcher3.allapps.search; import android.os.Handler; +import com.android.launcher3.LauncherAppState; import com.android.launcher3.model.data.AppInfo; -import com.android.launcher3.util.ComponentKey; import java.text.Collator; -import java.util.ArrayList; -import java.util.List; /** * The default search implementation. */ public class DefaultAppSearchAlgorithm implements SearchAlgorithm { - private final List mApps; protected final Handler mResultHandler; + private final AppsSearchPipeline mAppsSearchPipeline; - public DefaultAppSearchAlgorithm(List apps) { - mApps = apps; + public DefaultAppSearchAlgorithm(LauncherAppState launcherAppState) { mResultHandler = new Handler(); + mAppsSearchPipeline = new AppsSearchPipeline(launcherAppState); } @Override @@ -47,28 +45,8 @@ public class DefaultAppSearchAlgorithm implements SearchAlgorithm { @Override public void doSearch(final String query, final AllAppsSearchBarController.Callbacks callback) { - final ArrayList result = getTitleMatchResult(query); - mResultHandler.post(new Runnable() { - - @Override - public void run() { - callback.onSearchResult(query, result); - } - }); - } - - private ArrayList getTitleMatchResult(String query) { - // Do an intersection of the words in the query and each title, and filter out all the - // apps that don't match all of the words in the query. - final String queryTextLower = query.toLowerCase(); - final ArrayList result = new ArrayList<>(); - StringMatcher matcher = StringMatcher.getInstance(); - for (AppInfo info : mApps) { - if (matches(info, queryTextLower, matcher)) { - result.add(info.toComponentKey()); - } - } - return result; + mAppsSearchPipeline.performSearch(query, + results -> mResultHandler.post(() -> callback.onSearchResult(query, results))); } public static boolean matches(AppInfo info, String query, StringMatcher matcher) { diff --git a/src/com/android/launcher3/allapps/search/SearchPipeline.java b/src/com/android/launcher3/allapps/search/SearchPipeline.java new file mode 100644 index 0000000000..321674025a --- /dev/null +++ b/src/com/android/launcher3/allapps/search/SearchPipeline.java @@ -0,0 +1,32 @@ +/* + * Copyright (C) 2020 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.android.launcher3.allapps.search; + +import com.android.launcher3.allapps.AlphabeticalAppsList; + +import java.util.ArrayList; +import java.util.function.Consumer; + +/** + * An interface for handling search within pipeline + */ +public interface SearchPipeline { + + /** + * Perform query + */ + void performSearch(String query, Consumer> cb); +} diff --git a/src/com/android/launcher3/allapps/search/SearchSectionInfo.java b/src/com/android/launcher3/allapps/search/SearchSectionInfo.java new file mode 100644 index 0000000000..880b2466dc --- /dev/null +++ b/src/com/android/launcher3/allapps/search/SearchSectionInfo.java @@ -0,0 +1,36 @@ +/* + * Copyright (C) 2020 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.android.launcher3.allapps.search; + +import android.content.Context; + +/** + * Info class for a search section + */ +public class SearchSectionInfo { + private final int mTitleResId; + + public SearchSectionInfo(int titleResId) { + mTitleResId = titleResId; + } + + /** + * Returns the section's title + */ + public String getTitle(Context context) { + return context.getString(mTitleResId); + } +} -- GitLab From 261d817a95a039a49f971254140b4ce07d53ccb4 Mon Sep 17 00:00:00 2001 From: Vadim Tryshev Date: Wed, 29 Jul 2020 02:50:35 +0000 Subject: [PATCH 0052/1664] Revert "Recycling already running activities" This reverts commit 0bc32dcce64b20459ff6f8eff960b217810d59d8. Reason for revert: Tests are broken, perhaps caused by this one Change-Id: Ie409f04931a27bd6da616c1e52e279eb07479631 --- .../StartLauncherViaGestureTests.java | 4 +--- .../android/quickstep/TaplTestsQuickstep.java | 18 ++++++++---------- 2 files changed, 9 insertions(+), 13 deletions(-) diff --git a/quickstep/tests/src/com/android/quickstep/StartLauncherViaGestureTests.java b/quickstep/tests/src/com/android/quickstep/StartLauncherViaGestureTests.java index 664958a18e..0c5b9ada5d 100644 --- a/quickstep/tests/src/com/android/quickstep/StartLauncherViaGestureTests.java +++ b/quickstep/tests/src/com/android/quickstep/StartLauncherViaGestureTests.java @@ -48,9 +48,7 @@ public class StartLauncherViaGestureTests extends AbstractQuickStepTest { // b/143488140 mLauncher.pressHome(); // Start an activity where the gestures start. - if (mLauncher.getRecentTasks().isEmpty()) { - startAppFast(resolveSystemApp(Intent.CATEGORY_APP_CALCULATOR)); - } + startAppFast(resolveSystemApp(Intent.CATEGORY_APP_CALCULATOR)); } private void runTest(String... eventSequence) { diff --git a/quickstep/tests/src/com/android/quickstep/TaplTestsQuickstep.java b/quickstep/tests/src/com/android/quickstep/TaplTestsQuickstep.java index ed25cc71ca..bf093fdabf 100644 --- a/quickstep/tests/src/com/android/quickstep/TaplTestsQuickstep.java +++ b/quickstep/tests/src/com/android/quickstep/TaplTestsQuickstep.java @@ -69,16 +69,14 @@ public class TaplTestsQuickstep extends AbstractQuickStepTest { } private void startTestApps() throws Exception { - if (mLauncher.getRecentTasks().size() < 3) { - startAppFast(getAppPackageName()); - startAppFast(resolveSystemApp(Intent.CATEGORY_APP_CALCULATOR)); - startTestActivity(2); - - executeOnLauncher(launcher -> assertTrue( - "Launcher activity is the top activity; expecting another activity to be the " - + "top one", - isInBackground(launcher))); - } + startAppFast(getAppPackageName()); + startAppFast(resolveSystemApp(Intent.CATEGORY_APP_CALCULATOR)); + startTestActivity(2); + + executeOnLauncher(launcher -> assertTrue( + "Launcher activity is the top activity; expecting another activity to be the top " + + "one", + isInBackground(launcher))); } @Test -- GitLab From 12baaadeca97160ab567f4380991470ff536ff5f Mon Sep 17 00:00:00 2001 From: Yuchen He Date: Wed, 29 Jul 2020 17:15:03 +0000 Subject: [PATCH 0053/1664] Enable memory dump for Launcher Bug: 154772063 Change-Id: I3b2bc2e341037746891658f2596999248ec272ef --- tests/src/com/android/launcher3/ui/AbstractLauncherUiTest.java | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/tests/src/com/android/launcher3/ui/AbstractLauncherUiTest.java b/tests/src/com/android/launcher3/ui/AbstractLauncherUiTest.java index 858e183ab7..a1a2ed2a72 100644 --- a/tests/src/com/android/launcher3/ui/AbstractLauncherUiTest.java +++ b/tests/src/com/android/launcher3/ui/AbstractLauncherUiTest.java @@ -116,8 +116,7 @@ public abstract class AbstractLauncherUiTest { if (TestHelpers.isInLauncherProcess()) { StrictMode.VmPolicy.Builder builder = new StrictMode.VmPolicy.Builder() -// b/154772063 -// .detectActivityLeaks() + .detectActivityLeaks() .penaltyLog() .penaltyListener(Runnable::run, violation -> { if (sStrictmodeDetectedActivityLeak == null) { -- GitLab From 7383d4e6d2769937f8a8fe33e82e960aeb5771f4 Mon Sep 17 00:00:00 2001 From: Tony Wickham Date: Tue, 7 Jul 2020 19:25:25 -0700 Subject: [PATCH 0054/1664] Fix adjacent task offset distance Instead of calculating an overall distance for tasks to translate based on RecentsView width, calculate the distance for the tasks to the left and right of the midpoint based on how far the first adjacent tasks in those directions are from being offscreen. Changes made to make "distance to offscreen" calculations possible: - Update TaskView curve scale to reach final scale as soon as it is completely offscreen. Before, it would reach its final scale just shy of that point (calculations were off). - As we update RecentsView scale, calculate how much the new scale will push out tasks that are just offscreen. - With both above, we can calculate the scale and position of a TaskView such that it is just offscreen, and interpolate between its current position and that position. Tests: - Task comes in immediately when quick switching from home, and doesn't shift as you swipe directly upwards. - When swiping far up from an app, tasks come in from all the way offscreen, and cover distance appropriately (e.g. if you're scrolled a bit to the right when you pause, the left adjacent app will move faster to cover the farther distance). - Task modalness: entering Select mode now animates adjacent tasks at the same rate as the scaling up, because they move only the distance needed to get offscreen (before they moved way too far and thus seemed to be much faster than the rest of the animation). Bug: 149934536 Change-Id: Ie3fffe0e5c304cb16e7637f058f5ce72cee40aeb --- .../QuickstepAtomicAnimationFactory.java | 4 +- .../NoButtonQuickSwitchTouchController.java | 7 +- .../FallbackRecentsStateController.java | 4 +- .../quickstep/util/TaskViewSimulator.java | 6 +- .../android/quickstep/views/RecentsView.java | 126 ++++++++++++++++-- .../BaseRecentsViewStateController.java | 6 +- .../quickstep/BaseActivityInterface.java | 4 +- src/com/android/launcher3/PagedView.java | 21 ++- .../touch/LandscapePagedViewHandler.java | 1 + .../touch/PagedOrientationHandler.java | 1 + .../touch/PortraitPagedViewHandler.java | 1 + 11 files changed, 146 insertions(+), 35 deletions(-) diff --git a/quickstep/recents_ui_overrides/src/com/android/launcher3/uioverrides/states/QuickstepAtomicAnimationFactory.java b/quickstep/recents_ui_overrides/src/com/android/launcher3/uioverrides/states/QuickstepAtomicAnimationFactory.java index a0af79743b..131fcbfbf1 100644 --- a/quickstep/recents_ui_overrides/src/com/android/launcher3/uioverrides/states/QuickstepAtomicAnimationFactory.java +++ b/quickstep/recents_ui_overrides/src/com/android/launcher3/uioverrides/states/QuickstepAtomicAnimationFactory.java @@ -17,7 +17,6 @@ package com.android.launcher3.uioverrides.states; import static android.view.View.VISIBLE; -import static com.android.launcher3.LauncherAnimUtils.SCALE_PROPERTY; import static com.android.launcher3.LauncherState.BACKGROUND_APP; import static com.android.launcher3.LauncherState.HINT_STATE; import static com.android.launcher3.LauncherState.HOTSEAT_ICONS; @@ -52,6 +51,7 @@ import static com.android.launcher3.states.StateAnimationConfig.ANIM_WORKSPACE_S import static com.android.launcher3.states.StateAnimationConfig.ANIM_WORKSPACE_TRANSLATE; import static com.android.quickstep.SysUINavigationMode.Mode.NO_BUTTON; import static com.android.quickstep.SysUINavigationMode.removeShelfFromOverview; +import static com.android.quickstep.views.RecentsView.RECENTS_SCALE_PROPERTY; import android.animation.Animator; import android.animation.AnimatorSet; @@ -212,7 +212,7 @@ public class QuickstepAtomicAnimationFactory extends // Scale up the recents, if it is not coming from the side RecentsView overview = mActivity.getOverviewPanel(); if (overview.getVisibility() != VISIBLE || overview.getContentAlpha() == 0) { - SCALE_PROPERTY.set(overview, RECENTS_PREPARE_SCALE); + RECENTS_SCALE_PROPERTY.set(overview, RECENTS_PREPARE_SCALE); } } config.setInterpolator(ANIM_WORKSPACE_FADE, OVERSHOOT_1_2); diff --git a/quickstep/recents_ui_overrides/src/com/android/launcher3/uioverrides/touchcontrollers/NoButtonQuickSwitchTouchController.java b/quickstep/recents_ui_overrides/src/com/android/launcher3/uioverrides/touchcontrollers/NoButtonQuickSwitchTouchController.java index 1b439d1d31..821ada400e 100644 --- a/quickstep/recents_ui_overrides/src/com/android/launcher3/uioverrides/touchcontrollers/NoButtonQuickSwitchTouchController.java +++ b/quickstep/recents_ui_overrides/src/com/android/launcher3/uioverrides/touchcontrollers/NoButtonQuickSwitchTouchController.java @@ -15,7 +15,6 @@ */ package com.android.launcher3.uioverrides.touchcontrollers; -import static com.android.launcher3.LauncherAnimUtils.SCALE_PROPERTY; import static com.android.launcher3.LauncherState.HOTSEAT_ICONS; import static com.android.launcher3.LauncherState.NORMAL; import static com.android.launcher3.LauncherState.OVERVIEW; @@ -47,6 +46,7 @@ import static com.android.quickstep.util.ShelfPeekAnim.ShelfAnimState.HIDE; import static com.android.quickstep.util.ShelfPeekAnim.ShelfAnimState.PEEK; import static com.android.quickstep.views.RecentsView.ADJACENT_PAGE_OFFSET; import static com.android.quickstep.views.RecentsView.FULLSCREEN_PROGRESS; +import static com.android.quickstep.views.RecentsView.RECENTS_SCALE_PROPERTY; import static com.android.systemui.shared.system.QuickStepContract.SYSUI_STATE_OVERVIEW_DISABLED; import android.animation.Animator; @@ -244,7 +244,7 @@ public class NoButtonQuickSwitchTouchController implements TouchController, final LauncherState toState = OVERVIEW; // Set RecentView's initial properties. - SCALE_PROPERTY.set(mRecentsView, fromState.getOverviewScaleAndOffset(mLauncher)[0]); + RECENTS_SCALE_PROPERTY.set(mRecentsView, fromState.getOverviewScaleAndOffset(mLauncher)[0]); ADJACENT_PAGE_OFFSET.set(mRecentsView, 1f); mRecentsView.setContentAlpha(1); mRecentsView.setFullscreenProgress(fromState.getOverviewFullscreenProgress()); @@ -266,7 +266,8 @@ public class NoButtonQuickSwitchTouchController implements TouchController, // - RecentsView scale // - RecentsView fullscreenProgress PendingAnimation yAnim = new PendingAnimation((long) (mYRange * 2)); - yAnim.setFloat(mRecentsView, SCALE_PROPERTY, scaleAndOffset[0], SCALE_DOWN_INTERPOLATOR); + yAnim.setFloat(mRecentsView, RECENTS_SCALE_PROPERTY, scaleAndOffset[0], + SCALE_DOWN_INTERPOLATOR); yAnim.setFloat(mRecentsView, FULLSCREEN_PROGRESS, toState.getOverviewFullscreenProgress(), SCALE_DOWN_INTERPOLATOR); mYOverviewAnim = yAnim.createPlaybackController(); diff --git a/quickstep/recents_ui_overrides/src/com/android/quickstep/fallback/FallbackRecentsStateController.java b/quickstep/recents_ui_overrides/src/com/android/quickstep/fallback/FallbackRecentsStateController.java index 3f1e7bad9b..163c23268b 100644 --- a/quickstep/recents_ui_overrides/src/com/android/quickstep/fallback/FallbackRecentsStateController.java +++ b/quickstep/recents_ui_overrides/src/com/android/quickstep/fallback/FallbackRecentsStateController.java @@ -15,7 +15,6 @@ */ package com.android.quickstep.fallback; -import static com.android.launcher3.LauncherAnimUtils.SCALE_PROPERTY; import static com.android.launcher3.anim.Interpolators.LINEAR; import static com.android.launcher3.states.StateAnimationConfig.ANIM_OVERVIEW_MODAL; import static com.android.launcher3.states.StateAnimationConfig.ANIM_OVERVIEW_SCALE; @@ -25,6 +24,7 @@ import static com.android.launcher3.states.StateAnimationConfig.PLAY_ATOMIC_OVER import static com.android.launcher3.states.StateAnimationConfig.SKIP_OVERVIEW; import static com.android.quickstep.views.RecentsView.ADJACENT_PAGE_OFFSET; import static com.android.quickstep.views.RecentsView.FULLSCREEN_PROGRESS; +import static com.android.quickstep.views.RecentsView.RECENTS_SCALE_PROPERTY; import static com.android.quickstep.views.RecentsView.TASK_MODALNESS; import com.android.launcher3.anim.PendingAnimation; @@ -82,7 +82,7 @@ public class FallbackRecentsStateController implements StateHandler extends PagedView } }; + /** Same as normal SCALE_PROPERTY, but also updates page offsets that depend on this scale. */ + public static final FloatProperty RECENTS_SCALE_PROPERTY = + new FloatProperty("recentsScale") { + @Override + public void setValue(RecentsView view, float scale) { + view.setScaleX(scale); + view.setScaleY(scale); + view.mLastComputedTaskPushOutDistance = null; + view.updatePageOffsets(); + } + + @Override + public Float get(RecentsView view) { + return view.getScaleX(); + } + }; + protected RecentsOrientedState mOrientationState; protected final BaseActivityInterface mSizeStrategy; protected RecentsAnimationController mRecentsAnimationController; @@ -219,8 +236,12 @@ public abstract class RecentsView extends PagedView protected int mTaskHeight; protected final TransformParams mLiveTileParams = new TransformParams(); protected final TaskViewSimulator mLiveTileTaskViewSimulator; + protected final Rect mLastComputedTaskSize = new Rect(); + // How much a task that is directly offscreen will be pushed out due to RecentsView scale/pivot. + protected Float mLastComputedTaskPushOutDistance = null; protected boolean mEnableDrawingLiveTile = false; protected final Rect mTempRect = new Rect(); + protected final RectF mTempRectF = new RectF(); private final PointF mTempPointF = new PointF(); private static final int DISMISS_TASK_DURATION = 300; @@ -887,6 +908,7 @@ public abstract class RecentsView extends PagedView public void getTaskSize(Rect outRect) { mSizeStrategy.calculateTaskSize(mActivity, mActivity.getDeviceProfile(), outRect, mOrientationHandler); + mLastComputedTaskSize.set(outRect); } /** Gets the task size for modal state. */ @@ -934,8 +956,8 @@ public abstract class RecentsView extends PagedView final int pageCount = getPageCount(); for (int i = 0; i < pageCount; i++) { View page = getPageAt(i); - mScrollState.updateInterpolation(mOrientationHandler.getChildStartWithTranslation(page), - mPageSpacing); + mScrollState.updateInterpolation( + mOrientationHandler.getChildStartWithTranslation(page)); ((PageCallbacks) page).onPageScroll(mScrollState); } } @@ -1357,10 +1379,14 @@ public abstract class RecentsView extends PagedView /** * Updates linearInterpolation for the provided child position */ - public void updateInterpolation(float childStart, int pageSpacing) { - float pageCenter = childStart + halfPageSize; + public void updateInterpolation(float childStart) { + float scaledHalfPageSize = halfPageSize / pageParentScale; + float pageCenter = childStart + scaledHalfPageSize; float distanceFromScreenCenter = screenCenter - pageCenter; - float distanceToReachEdge = halfScreenSize + halfPageSize + pageSpacing; + // How far the page has to move from the center to be offscreen, taking into account + // the EDGE_SCALE_DOWN_FACTOR that will be applied at that position. + float distanceToReachEdge = halfScreenSize + + scaledHalfPageSize * (1 - TaskView.EDGE_SCALE_DOWN_FACTOR); linearInterpolation = Math.min(1, Math.abs(distanceFromScreenCenter) / distanceToReachEdge); } @@ -1802,14 +1828,15 @@ public abstract class RecentsView extends PagedView setPivotX(mTempPointF.x); setPivotY(mTempPointF.y); setTaskModalness(mTaskModalness); + mLastComputedTaskPushOutDistance = null; updatePageOffsets(); setImportantForAccessibility(isModal() ? IMPORTANT_FOR_ACCESSIBILITY_NO : IMPORTANT_FOR_ACCESSIBILITY_AUTO); } private void updatePageOffsets() { - float offset = mAdjacentPageOffset * getWidth(); - float modalOffset = ACCEL_0_75.getInterpolation(mTaskModalness) * getWidth(); + float offset = mAdjacentPageOffset; + float modalOffset = ACCEL_0_75.getInterpolation(mTaskModalness); if (mIsRtl) { offset = -offset; modalOffset = -modalOffset; @@ -1818,18 +1845,89 @@ public abstract class RecentsView extends PagedView TaskView runningTask = mRunningTaskId == -1 || !mRunningTaskTileHidden ? null : getTaskView(mRunningTaskId); - int midPoint = runningTask == null ? -1 : indexOfChild(runningTask); - int currentPage = getCurrentPage(); + int midpoint = runningTask == null ? -1 : indexOfChild(runningTask); + int modalMidpoint = getCurrentPage(); + + float midpointOffsetSize = 0; + float leftOffsetSize = midpoint - 1 >= 0 + ? -getOffsetSize(midpoint - 1, midpoint, offset) + : 0; + float rightOffsetSize = midpoint + 1 < count + ? getOffsetSize(midpoint + 1, midpoint, offset) + : 0; + + float modalMidpointOffsetSize = 0; + float modalLeftOffsetSize = modalMidpoint - 1 >= 0 + ? -getOffsetSize(modalMidpoint - 1, modalMidpoint, modalOffset) + : 0; + float modalRightOffsetSize = modalMidpoint + 1 < count + ? getOffsetSize(modalMidpoint + 1, modalMidpoint, modalOffset) + : 0; for (int i = 0; i < count; i++) { - float translation = i == midPoint ? 0 : (i < midPoint ? -offset : offset); - float modalTranslation = - i == currentPage ? 0 : (i < currentPage ? -modalOffset : modalOffset); + float translation = i == midpoint + ? midpointOffsetSize + : i < midpoint + ? leftOffsetSize + : rightOffsetSize; + float modalTranslation = i == modalMidpoint + ? modalMidpointOffsetSize + : i < modalMidpoint + ? modalLeftOffsetSize + : modalRightOffsetSize; getChildAt(i).setTranslationX(translation + modalTranslation); } updateCurveProperties(); } + /** + * Computes the distance to offset the given child such that it is completely offscreen when + * translating away from the given midpoint. + * @param offsetProgress From 0 to 1 where 0 means no offset and 1 means offset offscreen. + */ + private float getOffsetSize(int childIndex, int midpointIndex, float offsetProgress) { + if (offsetProgress == 0) { + // Don't bother calculating everything below if we won't offset anyway. + return 0; + } + // First, get the position of the task relative to the midpoint. If there is no midpoint + // then we just use the normal (centered) task position. + mTempRectF.set(mLastComputedTaskSize); + RectF taskPosition = mTempRectF; + float desiredLeft = getWidth(); + float distanceToOffscreen = desiredLeft - taskPosition.left; + // Used to calculate the scale of the task view based on its new offset. + float centerToOffscreenProgress = Math.abs(offsetProgress); + if (midpointIndex > -1) { + // When there is a midpoint reference task, adjacent tasks have less distance to travel + // to reach offscreen. Offset the task position to the task's starting point. + View child = getChildAt(childIndex); + View midpointChild = getChildAt(midpointIndex); + int distanceFromMidpoint = Math.abs(mOrientationHandler.getChildStart(child) + - mOrientationHandler.getChildStart(midpointChild) + + getDisplacementFromScreenCenter(midpointIndex)); + taskPosition.offset(distanceFromMidpoint, 0); + centerToOffscreenProgress = Utilities.mapRange(centerToOffscreenProgress, + distanceFromMidpoint / distanceToOffscreen, 1); + } + // Find the task's scale based on its offscreen progress, then see how far it still needs to + // move to be completely offscreen. + Utilities.scaleRectFAboutCenter(taskPosition, + TaskView.getCurveScaleForInterpolation(centerToOffscreenProgress)); + distanceToOffscreen = desiredLeft - taskPosition.left; + // Finally, we need to account for RecentsView scale, because it moves tasks based on its + // pivot. To do this, we move the task position to where it would be offscreen at scale = 1 + // (computed above), then we apply the scale via getMatrix() to determine how much that + // moves the task from its desired position, and adjust the computed distance accordingly. + if (mLastComputedTaskPushOutDistance == null) { + taskPosition.offsetTo(desiredLeft, 0); + getMatrix().mapRect(taskPosition); + mLastComputedTaskPushOutDistance = (taskPosition.left - desiredLeft) / getScaleX(); + } + distanceToOffscreen -= mLastComputedTaskPushOutDistance; + return distanceToOffscreen * offsetProgress; + } + /** * TODO: Do not assume motion across X axis for adjacent page */ @@ -1928,7 +2026,7 @@ public abstract class RecentsView extends PagedView float toScale = getMaxScaleForFullScreen(); if (launchingCenterTask) { RecentsView recentsView = tv.getRecentsView(); - anim.play(ObjectAnimator.ofFloat(recentsView, SCALE_PROPERTY, toScale)); + anim.play(ObjectAnimator.ofFloat(recentsView, RECENTS_SCALE_PROPERTY, toScale)); anim.play(ObjectAnimator.ofFloat(recentsView, FULLSCREEN_PROGRESS, 1)); } else { // We are launching an adjacent task, so parallax the center and other adjacent task. diff --git a/quickstep/src/com/android/launcher3/uioverrides/BaseRecentsViewStateController.java b/quickstep/src/com/android/launcher3/uioverrides/BaseRecentsViewStateController.java index ec3a4905f5..6e120e8bd8 100644 --- a/quickstep/src/com/android/launcher3/uioverrides/BaseRecentsViewStateController.java +++ b/quickstep/src/com/android/launcher3/uioverrides/BaseRecentsViewStateController.java @@ -16,7 +16,6 @@ package com.android.launcher3.uioverrides; -import static com.android.launcher3.LauncherAnimUtils.SCALE_PROPERTY; import static com.android.launcher3.anim.Interpolators.AGGRESSIVE_EASE_IN_OUT; import static com.android.launcher3.anim.Interpolators.LINEAR; import static com.android.launcher3.graphics.Scrim.SCRIM_PROGRESS; @@ -29,6 +28,7 @@ import static com.android.launcher3.states.StateAnimationConfig.PLAY_ATOMIC_OVER import static com.android.launcher3.states.StateAnimationConfig.PLAY_ATOMIC_OVERVIEW_SCALE; import static com.android.launcher3.states.StateAnimationConfig.SKIP_OVERVIEW; import static com.android.quickstep.views.RecentsView.ADJACENT_PAGE_OFFSET; +import static com.android.quickstep.views.RecentsView.RECENTS_SCALE_PROPERTY; import android.util.FloatProperty; @@ -61,7 +61,7 @@ public abstract class BaseRecentsViewStateController @Override public void setState(@NonNull LauncherState state) { float[] scaleAndOffset = state.getOverviewScaleAndOffset(mLauncher); - SCALE_PROPERTY.set(mRecentsView, scaleAndOffset[0]); + RECENTS_SCALE_PROPERTY.set(mRecentsView, scaleAndOffset[0]); ADJACENT_PAGE_OFFSET.set(mRecentsView, scaleAndOffset[1]); getContentAlphaProperty().set(mRecentsView, state.overviewUi ? 1f : 0); @@ -93,7 +93,7 @@ public abstract class BaseRecentsViewStateController void setStateWithAnimationInternal(@NonNull final LauncherState toState, @NonNull StateAnimationConfig config, @NonNull PendingAnimation setter) { float[] scaleAndOffset = toState.getOverviewScaleAndOffset(mLauncher); - setter.setFloat(mRecentsView, SCALE_PROPERTY, scaleAndOffset[0], + setter.setFloat(mRecentsView, RECENTS_SCALE_PROPERTY, scaleAndOffset[0], config.getInterpolator(ANIM_OVERVIEW_SCALE, LINEAR)); setter.setFloat(mRecentsView, ADJACENT_PAGE_OFFSET, scaleAndOffset[1], config.getInterpolator(ANIM_OVERVIEW_TRANSLATE_X, LINEAR)); diff --git a/quickstep/src/com/android/quickstep/BaseActivityInterface.java b/quickstep/src/com/android/quickstep/BaseActivityInterface.java index 0ae386f39a..9dc2132683 100644 --- a/quickstep/src/com/android/quickstep/BaseActivityInterface.java +++ b/quickstep/src/com/android/quickstep/BaseActivityInterface.java @@ -15,7 +15,6 @@ */ package com.android.quickstep; -import static com.android.launcher3.LauncherAnimUtils.SCALE_PROPERTY; import static com.android.launcher3.anim.Interpolators.ACCEL_2; import static com.android.launcher3.anim.Interpolators.INSTANT; import static com.android.launcher3.anim.Interpolators.LINEAR; @@ -28,6 +27,7 @@ import static com.android.quickstep.util.RecentsAtomicAnimationFactory.INDEX_REC import static com.android.quickstep.util.RecentsAtomicAnimationFactory.INDEX_RECENTS_TRANSLATE_X_ANIM; import static com.android.quickstep.views.RecentsView.ADJACENT_PAGE_OFFSET; import static com.android.quickstep.views.RecentsView.FULLSCREEN_PROGRESS; +import static com.android.quickstep.views.RecentsView.RECENTS_SCALE_PROPERTY; import android.animation.Animator; import android.annotation.TargetApi; @@ -393,7 +393,7 @@ public abstract class BaseActivityInterface extends ViewGrou int minDistanceFromScreenCenterIndex = -1; final int childCount = getChildCount(); for (int i = 0; i < childCount; ++i) { - View layout = getPageAt(i); - int childSize = mOrientationHandler.getMeasuredSize(layout); - int halfChildSize = (childSize / 2); - int childCenter = getChildOffset(i) + halfChildSize; - int distanceFromScreenCenter = Math.abs(childCenter - screenCenter); + int distanceFromScreenCenter = Math.abs( + getDisplacementFromScreenCenter(i, screenCenter)); if (distanceFromScreenCenter < minDistanceFromScreenCenter) { minDistanceFromScreenCenter = distanceFromScreenCenter; minDistanceFromScreenCenterIndex = i; @@ -1461,6 +1458,20 @@ public abstract class PagedView extends ViewGrou return minDistanceFromScreenCenterIndex; } + private int getDisplacementFromScreenCenter(int childIndex, int screenCenter) { + View layout = getPageAt(childIndex); + int childSize = mOrientationHandler.getMeasuredSize(layout); + int halfChildSize = (childSize / 2); + int childCenter = getChildOffset(childIndex) + halfChildSize; + return childCenter - screenCenter; + } + + protected int getDisplacementFromScreenCenter(int childIndex) { + int pageOrientationSize = mOrientationHandler.getMeasuredSize(this); + int screenCenter = mOrientationHandler.getPrimaryScroll(this) + (pageOrientationSize / 2); + return getDisplacementFromScreenCenter(childIndex, screenCenter); + } + protected void snapToDestination() { snapToPage(getPageNearestToCenterOfScreen(), getPageSnapDuration()); } diff --git a/src/com/android/launcher3/touch/LandscapePagedViewHandler.java b/src/com/android/launcher3/touch/LandscapePagedViewHandler.java index ac1ade260d..1aaa608657 100644 --- a/src/com/android/launcher3/touch/LandscapePagedViewHandler.java +++ b/src/com/android/launcher3/touch/LandscapePagedViewHandler.java @@ -72,6 +72,7 @@ public class LandscapePagedViewHandler implements PagedOrientationHandler { out.halfPageSize = view.getNormalChildHeight() / 2; out.halfScreenSize = view.getMeasuredHeight() / 2; out.screenCenter = insets.top + view.getPaddingTop() + out.scroll + out.halfPageSize; + out.pageParentScale = view.getScaleY(); } @Override diff --git a/src/com/android/launcher3/touch/PagedOrientationHandler.java b/src/com/android/launcher3/touch/PagedOrientationHandler.java index d4f5cba40e..f88cdb3b0d 100644 --- a/src/com/android/launcher3/touch/PagedOrientationHandler.java +++ b/src/com/android/launcher3/touch/PagedOrientationHandler.java @@ -109,6 +109,7 @@ public interface PagedOrientationHandler { public int halfPageSize; public int screenCenter; public int halfScreenSize; + public float pageParentScale; } class ChildBounds { diff --git a/src/com/android/launcher3/touch/PortraitPagedViewHandler.java b/src/com/android/launcher3/touch/PortraitPagedViewHandler.java index 3341996383..f18b109515 100644 --- a/src/com/android/launcher3/touch/PortraitPagedViewHandler.java +++ b/src/com/android/launcher3/touch/PortraitPagedViewHandler.java @@ -70,6 +70,7 @@ public class PortraitPagedViewHandler implements PagedOrientationHandler { out.halfPageSize = view.getNormalChildWidth() / 2; out.halfScreenSize = view.getMeasuredWidth() / 2; out.screenCenter = insets.left + view.getPaddingLeft() + out.scroll + out.halfPageSize; + out.pageParentScale = view.getScaleX(); } @Override -- GitLab From 8b74cc761b5a3185f3cfffe0a3833bbf7dd32636 Mon Sep 17 00:00:00 2001 From: Sunny Goyal Date: Mon, 27 Jul 2020 17:50:33 -0700 Subject: [PATCH 0055/1664] Moving all-apps predictions to Launcher model Predictions are loaded and managed by Launcher model and follow the model lifecycle. They are then bound to the callback which handles the UI Bug: 160748731 Change-Id: I4a3ea0698d80fafe94afb4ce66ffa7f4a6a91c68 --- .../res/values/override.xml | 5 +- .../appprediction/PredictionRowView.java | 78 +--- .../PredictionUiStateManager.java | 348 ------------------ .../AppEventProducer.java} | 125 +------ .../launcher3/model/PredictionUpdateTask.java | 117 ++++++ .../model/QuickstepModelDelegate.java | 138 +++++++ .../uioverrides/QuickstepLauncher.java | 39 +- .../quickstep/LauncherActivityInterface.java | 1 - ...EventDispatcherAppPredictionExtension.java | 49 --- .../quickstep/AppPredictionsUITests.java | 162 -------- res/values/config.xml | 1 + src/com/android/launcher3/Launcher.java | 5 +- .../android/launcher3/LauncherAppState.java | 3 +- src/com/android/launcher3/LauncherModel.java | 30 +- .../graphics/LauncherPreviewRenderer.java | 3 +- .../launcher3/model/AppLaunchTracker.java | 33 -- .../android/launcher3/model/BgDataModel.java | 7 - .../android/launcher3/model/LoaderTask.java | 7 +- .../launcher3/model/ModelDelegate.java | 75 ++++ .../launcher3/ui/AbstractLauncherUiTest.java | 3 - 20 files changed, 424 insertions(+), 805 deletions(-) delete mode 100644 quickstep/recents_ui_overrides/src/com/android/launcher3/appprediction/PredictionUiStateManager.java rename quickstep/recents_ui_overrides/src/com/android/launcher3/{appprediction/PredictionAppTracker.java => model/AppEventProducer.java} (65%) create mode 100644 quickstep/recents_ui_overrides/src/com/android/launcher3/model/PredictionUpdateTask.java create mode 100644 quickstep/recents_ui_overrides/src/com/android/launcher3/model/QuickstepModelDelegate.java delete mode 100644 quickstep/recents_ui_overrides/src/com/android/quickstep/logging/UserEventDispatcherAppPredictionExtension.java delete mode 100644 quickstep/tests/src/com/android/quickstep/AppPredictionsUITests.java delete mode 100644 src/com/android/launcher3/model/AppLaunchTracker.java create mode 100644 src/com/android/launcher3/model/ModelDelegate.java diff --git a/quickstep/recents_ui_overrides/res/values/override.xml b/quickstep/recents_ui_overrides/res/values/override.xml index 6aa9619cc9..1937164b7d 100644 --- a/quickstep/recents_ui_overrides/res/values/override.xml +++ b/quickstep/recents_ui_overrides/res/values/override.xml @@ -25,8 +25,11 @@ com.android.quickstep.QuickstepProcessInitializer - com.android.quickstep.logging.UserEventDispatcherAppPredictionExtension + com.android.quickstep.logging.UserEventDispatcherExtension com.android.launcher3.hybridhotseat.HotseatPredictionModel + + com.android.launcher3.model.QuickstepModelDelegate + diff --git a/quickstep/recents_ui_overrides/src/com/android/launcher3/appprediction/PredictionRowView.java b/quickstep/recents_ui_overrides/src/com/android/launcher3/appprediction/PredictionRowView.java index e11c701640..55384af96b 100644 --- a/quickstep/recents_ui_overrides/src/com/android/launcher3/appprediction/PredictionRowView.java +++ b/quickstep/recents_ui_overrides/src/com/android/launcher3/appprediction/PredictionRowView.java @@ -30,7 +30,6 @@ import android.graphics.Rect; import android.os.Build; import android.util.AttributeSet; import android.util.IntProperty; -import android.util.Log; import android.view.LayoutInflater; import android.view.View; import android.view.animation.Interpolator; @@ -44,19 +43,15 @@ import com.android.launcher3.DeviceProfile; import com.android.launcher3.DeviceProfile.OnDeviceProfileChangeListener; import com.android.launcher3.Launcher; import com.android.launcher3.LauncherAppState; -import com.android.launcher3.LauncherSettings; import com.android.launcher3.LauncherState; import com.android.launcher3.R; -import com.android.launcher3.allapps.AllAppsStore; import com.android.launcher3.allapps.FloatingHeaderRow; import com.android.launcher3.allapps.FloatingHeaderView; import com.android.launcher3.anim.AlphaUpdateListener; import com.android.launcher3.anim.PropertySetter; -import com.android.launcher3.config.FeatureFlags; import com.android.launcher3.keyboard.FocusIndicatorHelper; import com.android.launcher3.keyboard.FocusIndicatorHelper.SimpleFocusIndicatorHelper; import com.android.launcher3.logging.StatsLogUtils.LogContainerProvider; -import com.android.launcher3.model.data.AppInfo; import com.android.launcher3.model.data.ItemInfo; import com.android.launcher3.model.data.ItemInfoWithIcon; import com.android.launcher3.model.data.WorkspaceItemInfo; @@ -67,15 +62,12 @@ import com.android.launcher3.util.Themes; import com.android.quickstep.AnimatedFloat; import java.util.ArrayList; -import java.util.Collections; import java.util.List; @TargetApi(Build.VERSION_CODES.P) public class PredictionRowView extends LinearLayout implements LogContainerProvider, OnDeviceProfileChangeListener, FloatingHeaderRow { - private static final String TAG = "PredictionRowView"; - private static final IntProperty TEXT_ALPHA = new IntProperty("textAlpha") { @Override @@ -93,16 +85,14 @@ public class PredictionRowView extends LinearLayout implements (t) -> (t < 0.8f) ? 0 : (t - 0.8f) / 0.2f; private final Launcher mLauncher; - private final PredictionUiStateManager mPredictionUiStateManager; private int mNumPredictedAppsPerRow; - // The set of predicted app component names - private final List mPredictedAppComponents = new ArrayList<>(); - // The set of predicted apps resolved from the component names and the current set of apps - private final ArrayList mPredictedApps = new ArrayList<>(); // Helper to drawing the focus indicator. private final FocusIndicatorHelper mFocusHelper; + // The set of predicted apps resolved from the component names and the current set of apps + private final List mPredictedApps = new ArrayList<>(); + private final int mIconTextColor; private final int mIconFullTextAlpha; private int mIconLastSetTextAlpha; @@ -134,8 +124,6 @@ public class PredictionRowView extends LinearLayout implements mLauncher = Launcher.getLauncher(context); mLauncher.addOnDeviceProfileChangeListener(this); - mPredictionUiStateManager = PredictionUiStateManager.INSTANCE.get(context); - mIconTextColor = Themes.getAttrColor(context, android.R.attr.textColorSecondary); mIconFullTextAlpha = Color.alpha(mIconTextColor); mIconCurrentTextAlpha = mIconFullTextAlpha; @@ -146,24 +134,9 @@ public class PredictionRowView extends LinearLayout implements @Override protected void onAttachedToWindow() { super.onAttachedToWindow(); - - mPredictionUiStateManager.setTargetAppsView(mLauncher.getAppsView()); - getAppsStore().registerIconContainer(this); AllAppsTipView.scheduleShowIfNeeded(mLauncher); } - private AllAppsStore getAppsStore() { - return mLauncher.getAppsView().getAppsStore(); - } - - @Override - protected void onDetachedFromWindow() { - super.onDetachedFromWindow(); - - mPredictionUiStateManager.setTargetAppsView(null); - getAppsStore().unregisterIconContainer(this); - } - public void setup(FloatingHeaderView parent, FloatingHeaderRow[] rows, boolean tabsHidden) { mParent = parent; } @@ -205,7 +178,7 @@ public class PredictionRowView extends LinearLayout implements * Returns the predicted apps. */ public List getPredictedApps() { - return mPredictedApps; + return new ArrayList<>(mPredictedApps); } /** @@ -217,12 +190,12 @@ public class PredictionRowView extends LinearLayout implements * If the number of predicted apps is the same as the previous list of predicted apps, * we can optimize by swapping them in place. */ - public void setPredictedApps(List apps) { - mPredictedAppComponents.clear(); - mPredictedAppComponents.addAll(apps); - + public void setPredictedApps(List items) { mPredictedApps.clear(); - mPredictedApps.addAll(processPredictedAppComponents(mPredictedAppComponents)); + items.stream() + .filter(itemInfo -> itemInfo instanceof WorkspaceItemInfo) + .map(itemInfo -> (WorkspaceItemInfo) itemInfo) + .forEach(mPredictedApps::add); applyPredictionApps(); } @@ -264,11 +237,7 @@ public class PredictionRowView extends LinearLayout implements icon.reset(); if (predictionCount > i) { icon.setVisibility(View.VISIBLE); - if (mPredictedApps.get(i) instanceof AppInfo) { - icon.applyFromApplicationInfo((AppInfo) mPredictedApps.get(i)); - } else if (mPredictedApps.get(i) instanceof WorkspaceItemInfo) { - icon.applyFromWorkspaceItem((WorkspaceItemInfo) mPredictedApps.get(i)); - } + icon.applyFromWorkspaceItem(mPredictedApps.get(i)); icon.setTextColor(iconColor); } else { icon.setVisibility(predictionCount == 0 ? GONE : INVISIBLE); @@ -284,33 +253,6 @@ public class PredictionRowView extends LinearLayout implements mParent.onHeightUpdated(); } - private List processPredictedAppComponents( - List components) { - if (getAppsStore().getApps().length == 0) { - // Apps have not been bound yet. - return Collections.emptyList(); - } - - List predictedApps = new ArrayList<>(); - for (ComponentKeyMapper mapper : components) { - ItemInfoWithIcon info = mapper.getApp(getAppsStore()); - if (info != null) { - ItemInfoWithIcon predictedApp = info.clone(); - predictedApp.container = LauncherSettings.Favorites.CONTAINER_PREDICTION; - predictedApps.add(predictedApp); - } else { - if (FeatureFlags.IS_STUDIO_BUILD) { - Log.e(TAG, "Predicted app not found: " + mapper); - } - } - // Stop at the number of predicted apps - if (predictedApps.size() == mNumPredictedAppsPerRow) { - break; - } - } - return predictedApps; - } - @Override public void fillInLogContainerData(ItemInfo childInfo, LauncherLogProto.Target child, ArrayList parents) { diff --git a/quickstep/recents_ui_overrides/src/com/android/launcher3/appprediction/PredictionUiStateManager.java b/quickstep/recents_ui_overrides/src/com/android/launcher3/appprediction/PredictionUiStateManager.java deleted file mode 100644 index 830c1032ba..0000000000 --- a/quickstep/recents_ui_overrides/src/com/android/launcher3/appprediction/PredictionUiStateManager.java +++ /dev/null @@ -1,348 +0,0 @@ -/* - * 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.appprediction; - -import static com.android.launcher3.LauncherSettings.Favorites.ITEM_TYPE_APPLICATION; -import static com.android.launcher3.LauncherSettings.Favorites.ITEM_TYPE_DEEP_SHORTCUT; -import static com.android.launcher3.LauncherSettings.Favorites.ITEM_TYPE_SHORTCUT; -import static com.android.launcher3.LauncherState.BACKGROUND_APP; -import static com.android.launcher3.LauncherState.OVERVIEW; - -import android.app.prediction.AppPredictor; -import android.app.prediction.AppTarget; -import android.content.ComponentName; -import android.content.Context; - -import androidx.annotation.NonNull; -import androidx.annotation.Nullable; - -import com.android.launcher3.InvariantDeviceProfile; -import com.android.launcher3.InvariantDeviceProfile.OnIDPChangeListener; -import com.android.launcher3.Launcher; -import com.android.launcher3.LauncherAppState; -import com.android.launcher3.LauncherSettings; -import com.android.launcher3.LauncherState; -import com.android.launcher3.Utilities; -import com.android.launcher3.allapps.AllAppsContainerView; -import com.android.launcher3.allapps.AllAppsStore.OnUpdateListener; -import com.android.launcher3.hybridhotseat.HotseatPredictionController; -import com.android.launcher3.icons.IconCache.ItemInfoUpdateReceiver; -import com.android.launcher3.model.data.ItemInfo; -import com.android.launcher3.model.data.ItemInfoWithIcon; -import com.android.launcher3.shortcuts.ShortcutKey; -import com.android.launcher3.statemanager.StateManager.StateListener; -import com.android.launcher3.userevent.nano.LauncherLogProto; -import com.android.launcher3.util.ComponentKey; -import com.android.launcher3.util.MainThreadInitializedObject; - -import java.util.ArrayList; -import java.util.Collections; -import java.util.List; -import java.util.OptionalInt; -import java.util.stream.IntStream; - -/** - * Handler responsible to updating the UI due to predicted apps changes. Operations: - * 1) Pushes the predicted apps to all-apps. If all-apps is visible, waits until it becomes - * invisible again before applying the changes. This ensures that the UI does not change abruptly - * in front of the user, even if an app launched and user pressed back button to return to the - * all-apps UI again. - * 2) Prefetch high-res icons for predicted apps. This ensures that we have the icons in memory - * even if all-apps is not opened as they are shown in search UI as well - * 3) Load instant app if it is not already in memory. As predictions are persisted on disk, - * instant app will not be in memory when launcher starts. - * 4) Maintains the current active client id (for the predictions) and all updates are performed on - * that client id. - */ -public class PredictionUiStateManager implements StateListener, - ItemInfoUpdateReceiver, OnIDPChangeListener, OnUpdateListener { - - public static final String LAST_PREDICTION_ENABLED_STATE = "last_prediction_enabled_state"; - - // TODO (b/129421797): Update the client constants - public enum Client { - HOME("home"); - - public final String id; - - Client(String id) { - this.id = id; - } - } - - public static final MainThreadInitializedObject INSTANCE = - new MainThreadInitializedObject<>(PredictionUiStateManager::new); - - private final Context mContext; - - private final DynamicItemCache mDynamicItemCache; - private List mPredictionServicePredictions = Collections.emptyList(); - - private int mMaxIconsPerRow; - - private AllAppsContainerView mAppsView; - - private PredictionState mPendingState; - private PredictionState mCurrentState; - - private boolean mGettingValidPredictionResults; - - private PredictionUiStateManager(Context context) { - mContext = context; - - mDynamicItemCache = new DynamicItemCache(context, this::onAppsUpdated); - - InvariantDeviceProfile idp = LauncherAppState.getIDP(context); - mMaxIconsPerRow = idp.numColumns; - - idp.addOnChangeListener(this); - mGettingValidPredictionResults = Utilities.getDevicePrefs(context) - .getBoolean(LAST_PREDICTION_ENABLED_STATE, true); - - // Call this last - mCurrentState = parseLastState(); - } - - @Override - public void onIdpChanged(int changeFlags, InvariantDeviceProfile profile) { - mMaxIconsPerRow = profile.numColumns; - } - - public void setTargetAppsView(AllAppsContainerView appsView) { - if (mAppsView != null) { - mAppsView.getAppsStore().removeUpdateListener(this); - } - mAppsView = appsView; - if (mAppsView != null) { - mAppsView.getAppsStore().addUpdateListener(this); - } - if (mPendingState != null) { - applyState(mPendingState); - mPendingState = null; - } else { - applyState(mCurrentState); - } - updateDependencies(mCurrentState); - } - - @Override - public void reapplyItemInfo(ItemInfoWithIcon info) { } - - @Override - public void onStateTransitionComplete(LauncherState state) { - if (mAppsView == null) { - return; - } - if (mPendingState != null && canApplyPredictions(mPendingState)) { - applyState(mPendingState); - mPendingState = null; - } - if (mPendingState == null) { - Launcher.getLauncher(mAppsView.getContext()).getStateManager() - .removeStateListener(this); - } - } - - private void scheduleApplyPredictedApps(PredictionState state) { - boolean registerListener = mPendingState == null; - mPendingState = state; - if (registerListener) { - // Add a listener and wait until appsView is invisible again. - Launcher.getLauncher(mAppsView.getContext()).getStateManager().addStateListener(this); - } - } - - private void applyState(PredictionState state) { - mCurrentState = state; - if (mAppsView != null) { - mAppsView.getFloatingHeaderView().findFixedRowByType(PredictionRowView.class) - .setPredictedApps(mCurrentState.apps); - } - } - - private void updatePredictionStateAfterCallback() { - boolean validResults = mPredictionServicePredictions != null - && !mPredictionServicePredictions.isEmpty(); - if (validResults != mGettingValidPredictionResults) { - mGettingValidPredictionResults = validResults; - Utilities.getDevicePrefs(mContext).edit() - .putBoolean(LAST_PREDICTION_ENABLED_STATE, true) - .apply(); - } - dispatchOnChange(true); - } - - public AppPredictor.Callback appPredictorCallback(Client client) { - return targets -> { - mPredictionServicePredictions = targets; - updatePredictionStateAfterCallback(); - }; - } - - private void dispatchOnChange(boolean changed) { - PredictionState newState = changed - ? parseLastState() - : mPendingState != null && canApplyPredictions(mPendingState) - ? mPendingState - : mCurrentState; - if (changed && mAppsView != null && !canApplyPredictions(newState)) { - scheduleApplyPredictedApps(newState); - } else { - applyState(newState); - } - } - - private PredictionState parseLastState() { - PredictionState state = new PredictionState(); - state.isEnabled = mGettingValidPredictionResults; - if (!state.isEnabled) { - state.apps = Collections.EMPTY_LIST; - return state; - } - - state.apps = new ArrayList<>(); - - List appTargets = mPredictionServicePredictions; - if (!appTargets.isEmpty()) { - for (AppTarget appTarget : appTargets) { - ComponentKey key; - if (appTarget.getShortcutInfo() != null) { - key = ShortcutKey.fromInfo(appTarget.getShortcutInfo()); - } else { - key = new ComponentKey(new ComponentName(appTarget.getPackageName(), - appTarget.getClassName()), appTarget.getUser()); - } - state.apps.add(new ComponentKeyMapper(key, mDynamicItemCache)); - } - } - updateDependencies(state); - return state; - } - - private void updateDependencies(PredictionState state) { - if (!state.isEnabled || mAppsView == null) { - return; - } - mDynamicItemCache.updateDependencies(state.apps, mAppsView.getAppsStore(), this, - mMaxIconsPerRow); - } - - @Override - public void onAppsUpdated() { - dispatchOnChange(false); - } - - private boolean canApplyPredictions(PredictionState newState) { - if (mAppsView == null) { - // If there is no apps view, no need to schedule. - return true; - } - Launcher launcher = Launcher.getLauncher(mAppsView.getContext()); - PredictionRowView predictionRow = mAppsView.getFloatingHeaderView(). - findFixedRowByType(PredictionRowView.class); - if (!predictionRow.isShown() || predictionRow.getAlpha() == 0 || - launcher.isForceInvisible()) { - return true; - } - - if (mCurrentState.isEnabled != newState.isEnabled - || mCurrentState.apps.isEmpty() != newState.apps.isEmpty()) { - // If the visibility of the prediction row is changing, apply immediately. - return true; - } - - if (launcher.getDeviceProfile().isVerticalBarLayout()) { - // If we are here & mAppsView.isShown() = true, we are probably in all-apps or mid way - return false; - } - if (!launcher.isInState(OVERVIEW) && !launcher.isInState(BACKGROUND_APP)) { - // Just a fallback as we dont need to apply instantly, if we are not in the swipe-up UI - return false; - } - - // Instead of checking against 1, we should check against (1 + delta), where delta accounts - // for the nav-bar height (as app icon can still be visible under the nav-bar). Checking - // against 1, keeps the logic simple :) - return launcher.getAllAppsController().getProgress() > 1; - } - - public PredictionState getCurrentState() { - return mCurrentState; - } - - /** - * Returns ranking info for the app within all apps prediction. - * Only applicable when {@link ItemInfo#itemType} is one of the followings: - * {@link LauncherSettings.Favorites#ITEM_TYPE_APPLICATION}, - * {@link LauncherSettings.Favorites#ITEM_TYPE_SHORTCUT}, - * {@link LauncherSettings.Favorites#ITEM_TYPE_DEEP_SHORTCUT} - */ - public OptionalInt getAllAppsRank(@Nullable ItemInfo itemInfo) { - if (itemInfo == null || itemInfo.getTargetComponent() == null || itemInfo.user == null) { - return OptionalInt.empty(); - } - - if (itemInfo.itemType == ITEM_TYPE_APPLICATION - || itemInfo.itemType == ITEM_TYPE_SHORTCUT - || itemInfo.itemType == ITEM_TYPE_DEEP_SHORTCUT) { - ComponentKey key = new ComponentKey(itemInfo.getTargetComponent(), - itemInfo.user); - final List apps = getCurrentState().apps; - return IntStream.range(0, apps.size()) - .filter(index -> key.equals(apps.get(index).getComponentKey())) - .findFirst(); - } - - return OptionalInt.empty(); - } - - /** - * Fill in predicted_rank field based on app prediction. - * Only applicable when {@link ItemInfo#itemType} is one of the followings: - * {@link LauncherSettings.Favorites#ITEM_TYPE_APPLICATION}, - * {@link LauncherSettings.Favorites#ITEM_TYPE_SHORTCUT}, - * {@link LauncherSettings.Favorites#ITEM_TYPE_DEEP_SHORTCUT} - */ - public static void fillInPredictedRank( - @NonNull ItemInfo itemInfo, @NonNull LauncherLogProto.Target target) { - - final PredictionUiStateManager manager = PredictionUiStateManager.INSTANCE.getNoCreate(); - if (manager == null || itemInfo.getTargetComponent() == null || itemInfo.user == null - || (itemInfo.itemType != LauncherSettings.Favorites.ITEM_TYPE_APPLICATION - && itemInfo.itemType != LauncherSettings.Favorites.ITEM_TYPE_SHORTCUT - && itemInfo.itemType != LauncherSettings.Favorites.ITEM_TYPE_DEEP_SHORTCUT)) { - return; - } - if (itemInfo.container != LauncherSettings.Favorites.CONTAINER_PREDICTION) { - HotseatPredictionController.encodeHotseatLayoutIntoPredictionRank(itemInfo, target); - return; - } - - final ComponentKey k = new ComponentKey(itemInfo.getTargetComponent(), itemInfo.user); - final List predictedApps = manager.getCurrentState().apps; - IntStream.range(0, predictedApps.size()) - .filter((i) -> k.equals(predictedApps.get(i).getComponentKey())) - .findFirst() - .ifPresent((rank) -> target.predictedRank = 0 - rank); - } - - public static class PredictionState { - - public boolean isEnabled; - public List apps; - } -} diff --git a/quickstep/recents_ui_overrides/src/com/android/launcher3/appprediction/PredictionAppTracker.java b/quickstep/recents_ui_overrides/src/com/android/launcher3/model/AppEventProducer.java similarity index 65% rename from quickstep/recents_ui_overrides/src/com/android/launcher3/appprediction/PredictionAppTracker.java rename to quickstep/recents_ui_overrides/src/com/android/launcher3/model/AppEventProducer.java index 8cabe3d80b..8e4c43f464 100644 --- a/quickstep/recents_ui_overrides/src/com/android/launcher3/appprediction/PredictionAppTracker.java +++ b/quickstep/recents_ui_overrides/src/com/android/launcher3/model/AppEventProducer.java @@ -13,175 +13,76 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package com.android.launcher3.appprediction; +package com.android.launcher3.model; -import static com.android.launcher3.InvariantDeviceProfile.CHANGE_FLAG_GRID; import static com.android.launcher3.logging.StatsLogManager.LauncherEvent.LAUNCHER_APP_LAUNCH_TAP; import static com.android.launcher3.logging.StatsLogManager.LauncherEvent.LAUNCHER_ITEM_DROPPED_ON_DONT_SUGGEST; import static com.android.launcher3.logging.StatsLogManager.LauncherEvent.LAUNCHER_QUICKSWITCH_LEFT; import static com.android.launcher3.logging.StatsLogManager.LauncherEvent.LAUNCHER_QUICKSWITCH_RIGHT; import static com.android.launcher3.logging.StatsLogManager.LauncherEvent.LAUNCHER_TASK_LAUNCH_SWIPE_DOWN; import static com.android.launcher3.logging.StatsLogManager.LauncherEvent.LAUNCHER_TASK_LAUNCH_TAP; -import static com.android.launcher3.util.Executors.UI_HELPER_EXECUTOR; +import static com.android.launcher3.util.Executors.MODEL_EXECUTOR; import android.annotation.TargetApi; -import android.app.prediction.AppPredictionContext; -import android.app.prediction.AppPredictionManager; -import android.app.prediction.AppPredictor; import android.app.prediction.AppTarget; import android.app.prediction.AppTargetEvent; import android.app.prediction.AppTargetId; import android.content.ComponentName; import android.content.Context; import android.os.Build; -import android.os.Bundle; import android.os.Handler; import android.os.Message; import android.os.Process; import android.os.SystemClock; import android.os.UserHandle; import android.text.TextUtils; -import android.util.Log; import androidx.annotation.AnyThread; import androidx.annotation.Nullable; -import androidx.annotation.UiThread; import androidx.annotation.WorkerThread; -import com.android.launcher3.InvariantDeviceProfile; -import com.android.launcher3.appprediction.PredictionUiStateManager.Client; import com.android.launcher3.logger.LauncherAtom; import com.android.launcher3.logger.LauncherAtom.ContainerInfo; import com.android.launcher3.logger.LauncherAtom.FolderContainer; import com.android.launcher3.logger.LauncherAtom.HotseatContainer; import com.android.launcher3.logger.LauncherAtom.WorkspaceContainer; import com.android.launcher3.logging.StatsLogManager.EventEnum; -import com.android.launcher3.model.AppLaunchTracker; import com.android.launcher3.pm.UserCache; -import com.android.quickstep.logging.StatsLogCompatManager; import com.android.quickstep.logging.StatsLogCompatManager.StatsLogConsumer; import java.util.Locale; +import java.util.function.Consumer; import java.util.function.Predicate; /** - * Subclass of app tracker which publishes the data to the prediction engine and gets back results. + * Utility class to track stats log and emit corresponding app events */ -@TargetApi(Build.VERSION_CODES.Q) -public class PredictionAppTracker extends AppLaunchTracker implements StatsLogConsumer { +@TargetApi(Build.VERSION_CODES.R) +public class AppEventProducer implements StatsLogConsumer { - private static final String TAG = "PredictionAppTracker"; - private static final boolean DBG = false; + private static final int MSG_LAUNCH = 0; - private static final int MSG_INIT = 0; - private static final int MSG_DESTROY = 1; - private static final int MSG_LAUNCH = 2; - private static final int MSG_PREDICT = 3; - - protected final Context mContext; + private final Context mContext; private final Handler mMessageHandler; + private final Consumer mCallback; - // Accessed only on worker thread - private AppPredictor mHomeAppPredictor; - - public PredictionAppTracker(Context context) { + public AppEventProducer(Context context, Consumer callback) { mContext = context; - mMessageHandler = new Handler(UI_HELPER_EXECUTOR.getLooper(), this::handleMessage); - InvariantDeviceProfile.INSTANCE.get(mContext).addOnChangeListener(this::onIdpChanged); - - mMessageHandler.sendEmptyMessage(MSG_INIT); - } - - @UiThread - private void onIdpChanged(int changeFlags, InvariantDeviceProfile profile) { - if ((changeFlags & CHANGE_FLAG_GRID) != 0) { - // Reinitialize everything - mMessageHandler.sendEmptyMessage(MSG_INIT); - } - } - - @WorkerThread - private void destroy() { - if (mHomeAppPredictor != null) { - mHomeAppPredictor.destroy(); - mHomeAppPredictor = null; - } - StatsLogCompatManager.LOGS_CONSUMER.remove(this); - } - - @WorkerThread - private AppPredictor createPredictor(Client client, int count) { - AppPredictionManager apm = mContext.getSystemService(AppPredictionManager.class); - - if (apm == null) { - return null; - } - - AppPredictor predictor = apm.createAppPredictionSession( - new AppPredictionContext.Builder(mContext) - .setUiSurface(client.id) - .setPredictedTargetCount(count) - .setExtras(getAppPredictionContextExtras(client)) - .build()); - predictor.registerPredictionUpdates(mContext.getMainExecutor(), - PredictionUiStateManager.INSTANCE.get(mContext).appPredictorCallback(client)); - predictor.requestPredictionUpdate(); - return predictor; - } - - /** - * Override to add custom extras. - */ - @WorkerThread - @Nullable - public Bundle getAppPredictionContextExtras(Client client) { - return null; + mMessageHandler = new Handler(MODEL_EXECUTOR.getLooper(), this::handleMessage); + mCallback = callback; } @WorkerThread private boolean handleMessage(Message msg) { switch (msg.what) { - case MSG_INIT: { - // Destroy any existing clients - destroy(); - - // Initialize the clients - int count = InvariantDeviceProfile.INSTANCE.get(mContext).numAllAppsColumns; - mHomeAppPredictor = createPredictor(Client.HOME, count); - StatsLogCompatManager.LOGS_CONSUMER.add(this); - return true; - } - case MSG_DESTROY: { - destroy(); - return true; - } case MSG_LAUNCH: { - if (mHomeAppPredictor != null) { - mHomeAppPredictor.notifyAppTargetEvent((AppTargetEvent) msg.obj); - } - return true; - } - case MSG_PREDICT: { - if (mHomeAppPredictor != null) { - mHomeAppPredictor.requestPredictionUpdate(); - } + mCallback.accept((AppTargetEvent) msg.obj); return true; } } return false; } - @Override - @UiThread - public void onReturnedToHome() { - String client = Client.HOME.id; - mMessageHandler.removeMessages(MSG_PREDICT, client); - Message.obtain(mMessageHandler, MSG_PREDICT, client).sendToTarget(); - if (DBG) { - Log.d(TAG, String.format("Sent immediate message to update %s", client)); - } - } - @AnyThread private void sendEvent(LauncherAtom.ItemInfo atomInfo, int eventId) { AppTarget target = toAppTarget(atomInfo); diff --git a/quickstep/recents_ui_overrides/src/com/android/launcher3/model/PredictionUpdateTask.java b/quickstep/recents_ui_overrides/src/com/android/launcher3/model/PredictionUpdateTask.java new file mode 100644 index 0000000000..721e2bebd5 --- /dev/null +++ b/quickstep/recents_ui_overrides/src/com/android/launcher3/model/PredictionUpdateTask.java @@ -0,0 +1,117 @@ +/* + * Copyright (C) 2020 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.android.launcher3.model; + +import static com.android.launcher3.LauncherSettings.Favorites.ITEM_TYPE_DEEP_SHORTCUT; +import static com.android.launcher3.model.QuickstepModelDelegate.LAST_PREDICTION_ENABLED_STATE; +import static com.android.quickstep.InstantAppResolverImpl.COMPONENT_CLASS_MARKER; + +import android.app.prediction.AppTarget; +import android.content.ComponentName; +import android.content.pm.LauncherActivityInfo; +import android.content.pm.LauncherApps; +import android.content.pm.ShortcutInfo; +import android.os.UserHandle; + +import com.android.launcher3.LauncherAppState; +import com.android.launcher3.Utilities; +import com.android.launcher3.model.BgDataModel.FixedContainerItems; +import com.android.launcher3.model.data.AppInfo; +import com.android.launcher3.model.data.WorkspaceItemInfo; + +import java.util.HashSet; +import java.util.List; +import java.util.Set; +import java.util.stream.Collectors; + +/** + * Task to update model as a result of predicted apps update + */ +public class PredictionUpdateTask extends BaseModelUpdateTask { + + private final List mTargets; + private final int mContainerId; + + PredictionUpdateTask(int containerId, List targets) { + mContainerId = containerId; + mTargets = targets; + } + + @Override + public void execute(LauncherAppState app, BgDataModel dataModel, AllAppsList apps) { + // TODO: persist the whole list + Utilities.getDevicePrefs(app.getContext()).edit() + .putBoolean(LAST_PREDICTION_ENABLED_STATE, !mTargets.isEmpty()).apply(); + + FixedContainerItems fci; + synchronized (dataModel) { + fci = dataModel.extraItems.get(mContainerId); + if (fci == null) { + return; + } + } + + Set usersForChangedShortcuts = new HashSet<>(fci.items.stream() + .filter(info -> info.itemType == ITEM_TYPE_DEEP_SHORTCUT) + .map(info -> info.user) + .collect(Collectors.toSet())); + fci.items.clear(); + + for (AppTarget target : mTargets) { + WorkspaceItemInfo itemInfo; + ShortcutInfo si = target.getShortcutInfo(); + if (si != null) { + usersForChangedShortcuts.add(si.getUserHandle()); + itemInfo = new WorkspaceItemInfo(si, app.getContext()); + app.getIconCache().getShortcutIcon(itemInfo, si); + } else { + String className = target.getClassName(); + if (COMPONENT_CLASS_MARKER.equals(className)) { + // TODO: Implement this + continue; + } + ComponentName cn = new ComponentName(target.getPackageName(), className); + UserHandle user = target.getUser(); + itemInfo = apps.data.stream() + .filter(info -> user.equals(info.user) && cn.equals(info.componentName)) + .map(AppInfo::makeWorkspaceItem) + .findAny() + .orElseGet(() -> { + LauncherActivityInfo lai = app.getContext() + .getSystemService(LauncherApps.class) + .resolveActivity(AppInfo.makeLaunchIntent(cn), user); + if (lai == null) { + return null; + } + AppInfo ai = new AppInfo(app.getContext(), lai, user); + app.getIconCache().getTitleAndIcon(ai, lai, false); + return ai.makeWorkspaceItem(); + }); + + if (itemInfo == null) { + continue; + } + } + + itemInfo.container = mContainerId; + fci.items.add(itemInfo); + } + + bindExtraContainerItems(fci); + usersForChangedShortcuts.forEach( + u -> dataModel.updateShortcutPinnedState(app.getContext(), u)); + } +} diff --git a/quickstep/recents_ui_overrides/src/com/android/launcher3/model/QuickstepModelDelegate.java b/quickstep/recents_ui_overrides/src/com/android/launcher3/model/QuickstepModelDelegate.java new file mode 100644 index 0000000000..b5164695d5 --- /dev/null +++ b/quickstep/recents_ui_overrides/src/com/android/launcher3/model/QuickstepModelDelegate.java @@ -0,0 +1,138 @@ +/* + * Copyright (C) 2020 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.android.launcher3.model; + +import static com.android.launcher3.InvariantDeviceProfile.CHANGE_FLAG_GRID; +import static com.android.launcher3.LauncherSettings.Favorites.CONTAINER_PREDICTION; + +import android.app.prediction.AppPredictionContext; +import android.app.prediction.AppPredictionManager; +import android.app.prediction.AppPredictor; +import android.app.prediction.AppTarget; +import android.app.prediction.AppTargetEvent; +import android.content.Context; + +import androidx.annotation.WorkerThread; + +import com.android.launcher3.InvariantDeviceProfile; +import com.android.launcher3.InvariantDeviceProfile.OnIDPChangeListener; +import com.android.launcher3.model.BgDataModel.FixedContainerItems; +import com.android.launcher3.util.Executors; +import com.android.quickstep.logging.StatsLogCompatManager; + +import java.util.List; + +/** + * Model delegate which loads prediction items + */ +public class QuickstepModelDelegate extends ModelDelegate implements OnIDPChangeListener { + + public static final String LAST_PREDICTION_ENABLED_STATE = "last_prediction_enabled_state"; + + private final InvariantDeviceProfile mIDP; + private final AppEventProducer mAppEventProducer; + + private AppPredictor mAllAppsPredictor; + private boolean mActive = false; + + public QuickstepModelDelegate(Context context) { + mAppEventProducer = new AppEventProducer(context, this::onAppTargetEvent); + + mIDP = InvariantDeviceProfile.INSTANCE.get(context); + mIDP.addOnChangeListener(this); + StatsLogCompatManager.LOGS_CONSUMER.add(mAppEventProducer); + } + + @Override + public void loadItems() { + // TODO: Implement caching and preloading + super.loadItems(); + mDataModel.extraItems.put( + CONTAINER_PREDICTION, new FixedContainerItems(CONTAINER_PREDICTION)); + + mActive = true; + recreatePredictors(); + } + + @Override + public void validateData() { + super.validateData(); + if (mAllAppsPredictor != null) { + mAllAppsPredictor.requestPredictionUpdate(); + } + } + + @Override + public void destroy() { + super.destroy(); + mActive = false; + StatsLogCompatManager.LOGS_CONSUMER.remove(mAppEventProducer); + + destroyPredictors(); + mIDP.removeOnChangeListener(this); + } + + private void destroyPredictors() { + if (mAllAppsPredictor != null) { + mAllAppsPredictor.destroy(); + mAllAppsPredictor = null; + } + } + + @WorkerThread + private void recreatePredictors() { + destroyPredictors(); + if (!mActive) { + return; + } + + Context context = mApp.getContext(); + AppPredictionManager apm = context.getSystemService(AppPredictionManager.class); + if (apm == null) { + return; + } + + int count = mIDP.numAllAppsColumns; + + mAllAppsPredictor = apm.createAppPredictionSession( + new AppPredictionContext.Builder(context) + .setUiSurface("home") + .setPredictedTargetCount(count) + .build()); + mAllAppsPredictor.registerPredictionUpdates( + Executors.MODEL_EXECUTOR, this::onAllAppsPredictionChanged); + mAllAppsPredictor.requestPredictionUpdate(); + } + + private void onAllAppsPredictionChanged(List targets) { + mApp.getModel().enqueueModelUpdateTask( + new PredictionUpdateTask(CONTAINER_PREDICTION, targets)); + } + + @Override + public void onIdpChanged(int changeFlags, InvariantDeviceProfile profile) { + if ((changeFlags & CHANGE_FLAG_GRID) != 0) { + // Reinitialize everything + Executors.MODEL_EXECUTOR.execute(this::recreatePredictors); + } + } + + private void onAppTargetEvent(AppTargetEvent event) { + if (mAllAppsPredictor != null) { + mAllAppsPredictor.notifyAppTargetEvent(event); + } + } +} diff --git a/quickstep/recents_ui_overrides/src/com/android/launcher3/uioverrides/QuickstepLauncher.java b/quickstep/recents_ui_overrides/src/com/android/launcher3/uioverrides/QuickstepLauncher.java index f87138e536..23a05295bf 100644 --- a/quickstep/recents_ui_overrides/src/com/android/launcher3/uioverrides/QuickstepLauncher.java +++ b/quickstep/recents_ui_overrides/src/com/android/launcher3/uioverrides/QuickstepLauncher.java @@ -17,6 +17,9 @@ package com.android.launcher3.uioverrides; import static android.view.accessibility.AccessibilityEvent.TYPE_VIEW_FOCUSED; +import static com.android.launcher3.LauncherSettings.Favorites.ITEM_TYPE_APPLICATION; +import static com.android.launcher3.LauncherSettings.Favorites.ITEM_TYPE_DEEP_SHORTCUT; +import static com.android.launcher3.LauncherSettings.Favorites.ITEM_TYPE_SHORTCUT; import static com.android.launcher3.LauncherState.NORMAL; import static com.android.launcher3.LauncherState.OVERVIEW; import static com.android.launcher3.LauncherState.OVERVIEW_MODAL_TASK; @@ -37,16 +40,18 @@ import android.view.View; import com.android.launcher3.BaseQuickstepLauncher; import com.android.launcher3.DeviceProfile; import com.android.launcher3.Launcher; +import com.android.launcher3.LauncherSettings.Favorites; import com.android.launcher3.LauncherState; import com.android.launcher3.Workspace; import com.android.launcher3.allapps.DiscoveryBounce; import com.android.launcher3.anim.AnimatorPlaybackController; -import com.android.launcher3.appprediction.PredictionUiStateManager; +import com.android.launcher3.appprediction.PredictionRowView; import com.android.launcher3.config.FeatureFlags; import com.android.launcher3.folder.Folder; import com.android.launcher3.hybridhotseat.HotseatPredictionController; import com.android.launcher3.logging.InstanceId; import com.android.launcher3.logging.StatsLogManager.StatsLogger; +import com.android.launcher3.model.BgDataModel.FixedContainerItems; import com.android.launcher3.model.data.AppInfo; import com.android.launcher3.model.data.ItemInfo; import com.android.launcher3.model.data.WorkspaceItemInfo; @@ -79,7 +84,7 @@ import java.io.FileDescriptor; import java.io.PrintWriter; import java.util.ArrayList; import java.util.List; -import java.util.OptionalInt; +import java.util.Objects; import java.util.stream.Stream; public class QuickstepLauncher extends BaseQuickstepLauncher { @@ -91,6 +96,8 @@ public class QuickstepLauncher extends BaseQuickstepLauncher { public static final AsyncCommand SET_SHELF_HEIGHT = (context, arg1, arg2) -> SystemUiProxy.INSTANCE.get(context).setShelfHeight(arg1 != 0, arg2); + private FixedContainerItems mAllAppsPredictions; + @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); @@ -111,8 +118,23 @@ public class QuickstepLauncher extends BaseQuickstepLauncher { protected void logAppLaunch(ItemInfo info, InstanceId instanceId) { StatsLogger logger = getStatsLogManager() .logger().withItemInfo(info).withInstanceId(instanceId); - OptionalInt allAppsRank = PredictionUiStateManager.INSTANCE.get(this).getAllAppsRank(info); - allAppsRank.ifPresent(logger::withRank); + + if (mAllAppsPredictions != null + && (info.itemType == ITEM_TYPE_APPLICATION + || info.itemType == ITEM_TYPE_SHORTCUT + || info.itemType == ITEM_TYPE_DEEP_SHORTCUT)) { + int count = mAllAppsPredictions.items.size(); + for (int i = 0; i < count; i++) { + ItemInfo targetInfo = mAllAppsPredictions.items.get(i); + if (targetInfo.itemType == info.itemType + && targetInfo.user.equals(info.user) + && Objects.equals(targetInfo.getIntent(), info.getIntent())) { + logger.withRank(i); + break; + } + + } + } logger.log(LAUNCHER_APP_LAUNCH_TAP); if (mHotseatPredictionController != null) { @@ -199,6 +221,15 @@ public class QuickstepLauncher extends BaseQuickstepLauncher { } } + @Override + public void bindExtraContainerItems(FixedContainerItems item) { + if (item.containerId == Favorites.CONTAINER_PREDICTION) { + mAllAppsPredictions = item; + getAppsView().getFloatingHeaderView().findFixedRowByType(PredictionRowView.class) + .setPredictedApps(item.items); + } + } + @Override public void onDestroy() { super.onDestroy(); diff --git a/quickstep/recents_ui_overrides/src/com/android/quickstep/LauncherActivityInterface.java b/quickstep/recents_ui_overrides/src/com/android/quickstep/LauncherActivityInterface.java index b02035516d..5a35eb5eb2 100644 --- a/quickstep/recents_ui_overrides/src/com/android/quickstep/LauncherActivityInterface.java +++ b/quickstep/recents_ui_overrides/src/com/android/quickstep/LauncherActivityInterface.java @@ -41,7 +41,6 @@ import com.android.launcher3.R; import com.android.launcher3.allapps.DiscoveryBounce; import com.android.launcher3.anim.AnimatorPlaybackController; import com.android.launcher3.anim.PendingAnimation; -import com.android.launcher3.appprediction.PredictionUiStateManager; import com.android.launcher3.statehandlers.DepthController; import com.android.launcher3.statehandlers.DepthController.ClampedDepthProperty; import com.android.launcher3.statemanager.StateManager; diff --git a/quickstep/recents_ui_overrides/src/com/android/quickstep/logging/UserEventDispatcherAppPredictionExtension.java b/quickstep/recents_ui_overrides/src/com/android/quickstep/logging/UserEventDispatcherAppPredictionExtension.java deleted file mode 100644 index e0008033b4..0000000000 --- a/quickstep/recents_ui_overrides/src/com/android/quickstep/logging/UserEventDispatcherAppPredictionExtension.java +++ /dev/null @@ -1,49 +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.logging; - -import android.content.Context; - -import androidx.annotation.NonNull; - -import com.android.launcher3.appprediction.PredictionUiStateManager; -import com.android.launcher3.model.data.ItemInfo; -import com.android.launcher3.userevent.nano.LauncherLogProto; - -import java.util.ArrayList; - -/** - * This class handles AOSP MetricsLogger function calls and logging around - * quickstep interactions and app launches. - */ -@SuppressWarnings("unused") -public class UserEventDispatcherAppPredictionExtension extends UserEventDispatcherExtension { - - public static final int ALL_APPS_PREDICTION_TIPS = 2; - - private static final String TAG = "UserEventDispatcher"; - - public UserEventDispatcherAppPredictionExtension(Context context) { - super(context); - } - - @Override - protected void onFillInLogContainerData( - @NonNull ItemInfo itemInfo, @NonNull LauncherLogProto.Target target, - @NonNull ArrayList targets) { - PredictionUiStateManager.fillInPredictedRank(itemInfo, target); - } -} diff --git a/quickstep/tests/src/com/android/quickstep/AppPredictionsUITests.java b/quickstep/tests/src/com/android/quickstep/AppPredictionsUITests.java deleted file mode 100644 index 5904fcd691..0000000000 --- a/quickstep/tests/src/com/android/quickstep/AppPredictionsUITests.java +++ /dev/null @@ -1,162 +0,0 @@ -/** - * 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 org.junit.Assert.assertEquals; -import static org.junit.Assert.assertFalse; - -import android.app.prediction.AppPredictor; -import android.app.prediction.AppTarget; -import android.app.prediction.AppTargetId; -import android.content.ComponentName; -import android.content.pm.LauncherActivityInfo; -import android.content.pm.LauncherApps; -import android.os.Process; -import android.view.View; - -import androidx.test.filters.LargeTest; -import androidx.test.runner.AndroidJUnit4; - -import com.android.launcher3.BubbleTextView; -import com.android.launcher3.Launcher; -import com.android.launcher3.appprediction.PredictionRowView; -import com.android.launcher3.appprediction.PredictionUiStateManager; -import com.android.launcher3.appprediction.PredictionUiStateManager.Client; -import com.android.launcher3.model.AppLaunchTracker; - -import org.junit.After; -import org.junit.Before; -import org.junit.Test; -import org.junit.runner.RunWith; - -import java.util.ArrayList; -import java.util.List; - -@LargeTest -@RunWith(AndroidJUnit4.class) -public class AppPredictionsUITests extends AbstractQuickStepTest { - - private LauncherActivityInfo mSampleApp1; - private LauncherActivityInfo mSampleApp2; - private LauncherActivityInfo mSampleApp3; - - private AppPredictor.Callback mCallback; - - @Before - public void setUp() throws Exception { - super.setUp(); - - List activities = mTargetContext.getSystemService(LauncherApps.class) - .getActivityList(null, Process.myUserHandle()); - mSampleApp1 = activities.get(0); - mSampleApp2 = activities.get(1); - mSampleApp3 = activities.get(2); - - // Disable app tracker - AppLaunchTracker.INSTANCE.initializeForTesting(new AppLaunchTracker()); - PredictionUiStateManager.INSTANCE.initializeForTesting(null); - - mCallback = PredictionUiStateManager.INSTANCE.get(mTargetContext).appPredictorCallback( - Client.HOME); - - mDevice.setOrientationNatural(); - } - - @After - public void tearDown() throws Throwable { - AppLaunchTracker.INSTANCE.initializeForTesting(null); - PredictionUiStateManager.INSTANCE.initializeForTesting(null); - mDevice.unfreezeRotation(); - } - - /** - * Test that prediction UI is updated as soon as we get predictions from the system - */ - @Test - public void testPredictionExistsInAllApps() { - mLauncher.pressHome().switchToAllApps(); - - // Dispatch an update - sendPredictionUpdate(mSampleApp1, mSampleApp2); - // The first update should apply immediately. - waitForLauncherCondition("Predictions were not updated in loading state", - launcher -> getPredictedApp(launcher).size() == 2); - } - - /** - * Test that prediction update is deferred if it is already visible - */ - @Test - public void testPredictionsDeferredUntilHome() { - mDevice.pressHome(); - sendPredictionUpdate(mSampleApp1, mSampleApp2); - mLauncher.pressHome().switchToAllApps(); - waitForLauncherCondition("Predictions were not updated in loading state", - launcher -> getPredictedApp(launcher).size() == 2); - - // Update predictions while all-apps is visible - sendPredictionUpdate(mSampleApp1, mSampleApp2, mSampleApp3); - assertEquals(2, getFromLauncher(this::getPredictedApp).size()); - - // Go home and go back to all-apps - mLauncher.pressHome().switchToAllApps(); - assertEquals(3, getFromLauncher(this::getPredictedApp).size()); - } - - @Test - public void testPredictionsDisabled() { - mDevice.pressHome(); - sendPredictionUpdate(); - mLauncher.pressHome().switchToAllApps(); - - waitForLauncherCondition("Predictions were not updated in loading state", - launcher -> launcher.getAppsView().getFloatingHeaderView() - .findFixedRowByType(PredictionRowView.class).getVisibility() == View.GONE); - assertFalse(PredictionUiStateManager.INSTANCE.get(mTargetContext) - .getCurrentState().isEnabled); - } - - public ArrayList getPredictedApp(Launcher launcher) { - PredictionRowView container = launcher.getAppsView().getFloatingHeaderView() - .findFixedRowByType(PredictionRowView.class); - - ArrayList predictedAppViews = new ArrayList<>(); - for (int i = 0; i < container.getChildCount(); i++) { - View view = container.getChildAt(i); - if (view instanceof BubbleTextView && view.getVisibility() == View.VISIBLE) { - predictedAppViews.add((BubbleTextView) view); - } - } - return predictedAppViews; - } - - private void sendPredictionUpdate(LauncherActivityInfo... activities) { - getOnUiThread(() -> { - List targets = new ArrayList<>(activities.length); - for (LauncherActivityInfo info : activities) { - ComponentName cn = info.getComponentName(); - AppTarget target = new AppTarget.Builder( - new AppTargetId("app:" + cn), cn.getPackageName(), info.getUser()) - .setClassName(cn.getClassName()) - .build(); - targets.add(target); - } - mCallback.onTargetsAvailable(targets); - return null; - }); - } -} diff --git a/res/values/config.xml b/res/values/config.xml index 75fcc907db..dc8bdffca3 100644 --- a/res/values/config.xml +++ b/res/values/config.xml @@ -70,6 +70,7 @@ + diff --git a/src/com/android/launcher3/Launcher.java b/src/com/android/launcher3/Launcher.java index e49c4559f0..1123c79b37 100644 --- a/src/com/android/launcher3/Launcher.java +++ b/src/com/android/launcher3/Launcher.java @@ -119,7 +119,6 @@ import com.android.launcher3.logger.LauncherAtom; import com.android.launcher3.logging.FileLog; import com.android.launcher3.logging.StatsLogManager; import com.android.launcher3.logging.UserEventDispatcher; -import com.android.launcher3.model.AppLaunchTracker; import com.android.launcher3.model.BgDataModel.Callbacks; import com.android.launcher3.model.ModelWriter; import com.android.launcher3.model.data.AppInfo; @@ -911,14 +910,12 @@ public class Launcher extends StatefulActivity implements Launche logStopAndResume(Action.Command.RESUME); getUserEventDispatcher().startSession(); - AppLaunchTracker.INSTANCE.get(this).onReturnedToHome(); - // Process any items that were added while Launcher was away. InstallShortcutReceiver.disableAndFlushInstallQueue( InstallShortcutReceiver.FLAG_ACTIVITY_PAUSED, this); // Refresh shortcuts if the permission changed. - mModel.refreshShortcutsIfRequired(); + mModel.validateModelDataOnResume(); // Set the notification listener and fetch updated notifications when we resume NotificationListener.setNotificationsChangedListener(mPopupDataProvider); diff --git a/src/com/android/launcher3/LauncherAppState.java b/src/com/android/launcher3/LauncherAppState.java index 53e5274c0c..e3fe87d1e9 100644 --- a/src/com/android/launcher3/LauncherAppState.java +++ b/src/com/android/launcher3/LauncherAppState.java @@ -128,7 +128,7 @@ public class LauncherAppState { mInvariantDeviceProfile = InvariantDeviceProfile.INSTANCE.get(context); mIconCache = new IconCache(mContext, mInvariantDeviceProfile, iconCacheFileName); mWidgetCache = new WidgetPreviewLoader(mContext, mIconCache); - mModel = new LauncherModel(this, mIconCache, AppFilter.newInstance(mContext)); + mModel = new LauncherModel(context, this, mIconCache, AppFilter.newInstance(mContext)); mPredictionModel = PredictionModel.newInstance(mContext); } @@ -157,6 +157,7 @@ public class LauncherAppState { * Call from Application.onTerminate(), which is not guaranteed to ever be called. */ public void onTerminate() { + mModel.destroy(); if (mModelChangeReceiver != null) { mContext.unregisterReceiver(mModelChangeReceiver); } diff --git a/src/com/android/launcher3/LauncherModel.java b/src/com/android/launcher3/LauncherModel.java index ff4b545a06..e2568d5ef6 100644 --- a/src/com/android/launcher3/LauncherModel.java +++ b/src/com/android/launcher3/LauncherModel.java @@ -20,7 +20,6 @@ import static com.android.launcher3.LauncherAppState.ACTION_FORCE_ROLOAD; import static com.android.launcher3.config.FeatureFlags.IS_STUDIO_BUILD; import static com.android.launcher3.util.Executors.MAIN_EXECUTOR; import static com.android.launcher3.util.Executors.MODEL_EXECUTOR; -import static com.android.launcher3.util.PackageManagerHelper.hasShortcutsPermission; import android.content.Context; import android.content.Intent; @@ -46,6 +45,7 @@ import com.android.launcher3.model.BgDataModel.Callbacks; import com.android.launcher3.model.CacheDataUpdatedTask; import com.android.launcher3.model.LoaderResults; import com.android.launcher3.model.LoaderTask; +import com.android.launcher3.model.ModelDelegate; import com.android.launcher3.model.ModelWriter; import com.android.launcher3.model.PackageInstallStateChangedTask; import com.android.launcher3.model.PackageUpdatedTask; @@ -112,20 +112,22 @@ public class LauncherModel extends LauncherApps.Callback implements InstallSessi */ private final BgDataModel mBgDataModel = new BgDataModel(); + private final ModelDelegate mModelDelegate; + // Runnable to check if the shortcuts permission has changed. - private final Runnable mShortcutPermissionCheckRunnable = new Runnable() { + private final Runnable mDataValidationCheck = new Runnable() { @Override public void run() { - if (mModelLoaded && hasShortcutsPermission(mApp.getContext()) - != mBgAllAppsList.hasShortcutHostPermission()) { - forceReload(); + if (mModelLoaded) { + mModelDelegate.validateData(); } } }; - LauncherModel(LauncherAppState app, IconCache iconCache, AppFilter appFilter) { + LauncherModel(Context context, LauncherAppState app, IconCache iconCache, AppFilter appFilter) { mApp = app; mBgAllAppsList = new AllAppsList(iconCache, appFilter); + mModelDelegate = ModelDelegate.newInstance(context, app, mBgAllAppsList, mBgDataModel); } /** @@ -217,6 +219,13 @@ public class LauncherModel extends LauncherApps.Callback implements InstallSessi } } + /** + * Called when the model is destroyed + */ + public void destroy() { + MODEL_EXECUTOR.execute(mModelDelegate::destroy); + } + public void onBroadcastIntent(Intent intent) { if (DEBUG_RECEIVER) Log.d(TAG, "onReceive intent=" + intent); final String action = intent.getAction(); @@ -372,7 +381,8 @@ public class LauncherModel extends LauncherApps.Callback implements InstallSessi public void startLoaderForResults(LoaderResults results) { synchronized (mLock) { stopLoader(); - mLoaderTask = new LoaderTask(mApp, mBgAllAppsList, mBgDataModel, results); + mLoaderTask = new LoaderTask( + mApp, mBgAllAppsList, mBgDataModel, mModelDelegate, results); // Always post the loader task, instead of running directly (even on same thread) so // that we exit any nested synchronized blocks @@ -491,9 +501,9 @@ public class LauncherModel extends LauncherApps.Callback implements InstallSessi * Current implementation simply reloads the workspace, but it can be optimized to * use partial updates similar to {@link UserCache} */ - public void refreshShortcutsIfRequired() { - MODEL_EXECUTOR.getHandler().removeCallbacks(mShortcutPermissionCheckRunnable); - MODEL_EXECUTOR.post(mShortcutPermissionCheckRunnable); + public void validateModelDataOnResume() { + MODEL_EXECUTOR.getHandler().removeCallbacks(mDataValidationCheck); + MODEL_EXECUTOR.post(mDataValidationCheck); } /** diff --git a/src/com/android/launcher3/graphics/LauncherPreviewRenderer.java b/src/com/android/launcher3/graphics/LauncherPreviewRenderer.java index a424f8458a..d8eb838ed5 100644 --- a/src/com/android/launcher3/graphics/LauncherPreviewRenderer.java +++ b/src/com/android/launcher3/graphics/LauncherPreviewRenderer.java @@ -74,6 +74,7 @@ import com.android.launcher3.model.BgDataModel; import com.android.launcher3.model.BgDataModel.Callbacks; import com.android.launcher3.model.LoaderResults; import com.android.launcher3.model.LoaderTask; +import com.android.launcher3.model.ModelDelegate; import com.android.launcher3.model.WidgetItem; import com.android.launcher3.model.WidgetsModel; import com.android.launcher3.model.data.AppInfo; @@ -579,7 +580,7 @@ public class LauncherPreviewRenderer { private final FutureTask mTask = new FutureTask<>(this); WorkspaceItemsInfoFromPreviewFetcher(LauncherAppState app) { - super(app, null, new BgDataModel(), null); + super(app, null, new BgDataModel(), new ModelDelegate(), null); } @Override diff --git a/src/com/android/launcher3/model/AppLaunchTracker.java b/src/com/android/launcher3/model/AppLaunchTracker.java deleted file mode 100644 index a93c0dd77c..0000000000 --- a/src/com/android/launcher3/model/AppLaunchTracker.java +++ /dev/null @@ -1,33 +0,0 @@ -/* - * 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.model; - -import static com.android.launcher3.util.MainThreadInitializedObject.forOverride; - -import com.android.launcher3.R; -import com.android.launcher3.util.MainThreadInitializedObject; -import com.android.launcher3.util.ResourceBasedOverride; - -/** - * Callback for receiving various app launch events - */ -public class AppLaunchTracker implements ResourceBasedOverride { - - public static final MainThreadInitializedObject INSTANCE = - forOverride(AppLaunchTracker.class, R.string.app_launch_tracker_class); - - public void onReturnedToHome() { } -} diff --git a/src/com/android/launcher3/model/BgDataModel.java b/src/com/android/launcher3/model/BgDataModel.java index dfdc1387ff..6158a9c6b4 100644 --- a/src/com/android/launcher3/model/BgDataModel.java +++ b/src/com/android/launcher3/model/BgDataModel.java @@ -108,13 +108,6 @@ public class BgDataModel { */ public final ArrayList cachedPredictedItems = new ArrayList<>(); - /** - * @see Callbacks#FLAG_HAS_SHORTCUT_PERMISSION - * @see Callbacks#FLAG_QUIET_MODE_ENABLED - * @see Callbacks#FLAG_QUIET_MODE_CHANGE_PERMISSION - */ - public int flags; - /** * Maps all launcher activities to counts of their shortcuts. */ diff --git a/src/com/android/launcher3/model/LoaderTask.java b/src/com/android/launcher3/model/LoaderTask.java index bea00869ee..b0679095f3 100644 --- a/src/com/android/launcher3/model/LoaderTask.java +++ b/src/com/android/launcher3/model/LoaderTask.java @@ -109,6 +109,7 @@ public class LoaderTask implements Runnable { protected final LauncherAppState mApp; private final AllAppsList mBgAllAppsList; protected final BgDataModel mBgDataModel; + private final ModelDelegate mModelDelegate; private FirstScreenBroadcast mFirstScreenBroadcast; @@ -128,10 +129,11 @@ public class LoaderTask implements Runnable { private boolean mStopped; public LoaderTask(LauncherAppState app, AllAppsList bgAllAppsList, BgDataModel dataModel, - LoaderResults results) { + ModelDelegate modelDelegate, LoaderResults results) { mApp = app; mBgAllAppsList = bgAllAppsList; mBgDataModel = dataModel; + mModelDelegate = modelDelegate; mResults = results; mLauncherApps = mApp.getContext().getSystemService(LauncherApps.class); @@ -767,6 +769,9 @@ public class LoaderTask implements Runnable { IOUtils.closeSilently(c); } + // Load delegate items + mModelDelegate.loadItems(); + // Break early if we've stopped loading if (mStopped) { mBgDataModel.clear(); diff --git a/src/com/android/launcher3/model/ModelDelegate.java b/src/com/android/launcher3/model/ModelDelegate.java new file mode 100644 index 0000000000..ce4eed5bbd --- /dev/null +++ b/src/com/android/launcher3/model/ModelDelegate.java @@ -0,0 +1,75 @@ +/* + * Copyright (C) 2020 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.android.launcher3.model; + +import static com.android.launcher3.util.PackageManagerHelper.hasShortcutsPermission; + +import android.content.Context; + +import androidx.annotation.WorkerThread; + +import com.android.launcher3.LauncherAppState; +import com.android.launcher3.R; +import com.android.launcher3.util.ResourceBasedOverride; + +/** + * Class to extend LauncherModel functionality to provide extra data + */ +public class ModelDelegate implements ResourceBasedOverride { + + /** + * Creates and initializes a new instance of the delegate + */ + public static ModelDelegate newInstance( + Context context, LauncherAppState app, AllAppsList appsList, BgDataModel dataModel) { + ModelDelegate delegate = Overrides.getObject( + ModelDelegate.class, context, R.string.model_delegate_class); + + delegate.mApp = app; + delegate.mAppsList = appsList; + delegate.mDataModel = dataModel; + return delegate; + } + + protected LauncherAppState mApp; + protected AllAppsList mAppsList; + protected BgDataModel mDataModel; + + public ModelDelegate() { } + + /** + * Called periodically to validate and update any data + */ + @WorkerThread + public void validateData() { + if (hasShortcutsPermission(mApp.getContext()) + != mAppsList.hasShortcutHostPermission()) { + mApp.getModel().forceReload(); + } + } + + /** + * Load delegate items if any in the data model + */ + @WorkerThread + public void loadItems() { } + + /** + * Called when the delegate is no loner needed + */ + @WorkerThread + public void destroy() { } +} diff --git a/tests/src/com/android/launcher3/ui/AbstractLauncherUiTest.java b/tests/src/com/android/launcher3/ui/AbstractLauncherUiTest.java index 858e183ab7..201218b79a 100644 --- a/tests/src/com/android/launcher3/ui/AbstractLauncherUiTest.java +++ b/tests/src/com/android/launcher3/ui/AbstractLauncherUiTest.java @@ -53,7 +53,6 @@ import com.android.launcher3.LauncherSettings; import com.android.launcher3.LauncherState; import com.android.launcher3.Utilities; import com.android.launcher3.common.WidgetUtils; -import com.android.launcher3.model.AppLaunchTracker; import com.android.launcher3.model.data.ItemInfo; import com.android.launcher3.statemanager.StateManager; import com.android.launcher3.tapl.LauncherInstrumentation; @@ -272,8 +271,6 @@ public abstract class AbstractLauncherUiTest { } mLauncherPid = 0; - // Disable app tracker - AppLaunchTracker.INSTANCE.initializeForTesting(new AppLaunchTracker()); mTargetContext = InstrumentationRegistry.getTargetContext(); mTargetPackage = mTargetContext.getPackageName(); -- GitLab From eaf7a955467a5d189b982ee436a8eefaa30e3006 Mon Sep 17 00:00:00 2001 From: Sunny Goyal Date: Wed, 29 Jul 2020 16:54:20 -0700 Subject: [PATCH 0056/1664] Changing minimum supported Launcher version to 26 Change-Id: I49fcf874430ac53c3246371e179fbd828e14e4da --- Android.mk | 6 +- AndroidManifest-common.xml | 18 --- AndroidManifest.xml | 2 +- .../QuickstepAppTransitionManagerImpl.java | 3 - res/animator-v23/discovery_bounce.xml | 43 ------- res/animator/discovery_bounce.xml | 40 ++++--- .../all_apps_bg_hand_fill.xml | 0 .../all_apps_bg_hand_fill_dark.xml | 0 .../ic_deepshortcut_placeholder.xml | 20 ---- res/drawable-v26/ic_launcher_home.xml | 21 ---- .../ic_block_shadow.xml | 0 res/drawable/ic_deepshortcut_placeholder.xml | 14 +-- res/drawable/ic_launcher_home.xml | 9 +- .../ic_remove_shadow.xml | 0 .../ic_setup_shadow.xml | 0 .../ic_uninstall_shadow.xml | 0 res/mipmap-hdpi/ic_launcher_home.png | Bin 236 -> 0 bytes res/mipmap-mdpi/ic_launcher_home.png | Bin 216 -> 0 bytes res/mipmap-xhdpi/ic_launcher_home.png | Bin 235 -> 0 bytes res/mipmap-xxhdpi/ic_launcher_home.png | Bin 266 -> 0 bytes .../styles.xml | 0 res/values-v22/styles.xml | 26 ---- res/values-v26/bools.xml | 23 ---- res/values-v26/styles.xml | 31 ----- res/values/bools.xml | 23 ---- res/values/colors.xml | 3 - res/values/drawables.xml | 21 ---- res/values/styles.xml | 16 +-- .../launcher3/AbstractFloatingView.java | 3 +- src/com/android/launcher3/CellLayout.java | 4 +- .../launcher3/InstallShortcutReceiver.java | 29 +---- src/com/android/launcher3/Launcher.java | 5 +- .../android/launcher3/LauncherAppState.java | 14 +-- .../android/launcher3/LauncherProvider.java | 1 - .../launcher3/MainProcessInitializer.java | 1 - src/com/android/launcher3/PagedView.java | 4 +- .../launcher3/SessionCommitReceiver.java | 111 +----------------- src/com/android/launcher3/Utilities.java | 24 ---- .../android/launcher3/dragndrop/DragView.java | 3 - .../android/launcher3/graphics/IconShape.java | 8 +- .../launcher3/model/PackageUpdatedTask.java | 10 +- .../android/launcher3/model/data/AppInfo.java | 3 +- .../launcher3/pm/InstallSessionHelper.java | 23 +++- .../pm/ShortcutConfigActivityInfo.java | 49 +++----- .../popup/PopupContainerWithArrow.java | 5 +- .../launcher3/settings/SettingsActivity.java | 9 -- .../launcher3/statemanager/StateManager.java | 5 +- .../AbstractStateChangeTouchController.java | 10 +- .../launcher3/util/PackageManagerHelper.java | 38 +----- .../launcher3/util/SystemUiController.java | 12 +- .../launcher3/views/FloatingIconView.java | 4 +- .../widget/LauncherAppWidgetHostView.java | 4 +- .../launcher3/widget/WidgetManagerHelper.java | 20 +--- .../dynamicui/WallpaperManagerCompat.java | 5 +- .../ui/widget/RequestPinItemTest.java | 4 - 55 files changed, 130 insertions(+), 597 deletions(-) delete mode 100644 res/animator-v23/discovery_bounce.xml rename res/{color-v24 => color}/all_apps_bg_hand_fill.xml (100%) rename res/{color-v24 => color}/all_apps_bg_hand_fill_dark.xml (100%) delete mode 100644 res/drawable-v26/ic_deepshortcut_placeholder.xml delete mode 100644 res/drawable-v26/ic_launcher_home.xml rename res/{drawable-v24 => drawable}/ic_block_shadow.xml (100%) rename res/{drawable-v24 => drawable}/ic_remove_shadow.xml (100%) rename res/{drawable-v24 => drawable}/ic_setup_shadow.xml (100%) rename res/{drawable-v24 => drawable}/ic_uninstall_shadow.xml (100%) delete mode 100644 res/mipmap-hdpi/ic_launcher_home.png delete mode 100644 res/mipmap-mdpi/ic_launcher_home.png delete mode 100644 res/mipmap-xhdpi/ic_launcher_home.png delete mode 100644 res/mipmap-xxhdpi/ic_launcher_home.png rename res/{values-night-v26 => values-night}/styles.xml (100%) delete mode 100644 res/values-v22/styles.xml delete mode 100644 res/values-v26/bools.xml delete mode 100644 res/values-v26/styles.xml delete mode 100644 res/values/bools.xml delete mode 100644 res/values/drawables.xml diff --git a/Android.mk b/Android.mk index 7805b32e8e..91ecccb1e4 100644 --- a/Android.mk +++ b/Android.mk @@ -48,7 +48,7 @@ LOCAL_PROTOC_FLAGS := --proto_path=$(LOCAL_PATH)/protos/ --proto_path=$(LOCAL_PA LOCAL_PROTO_JAVA_OUTPUT_PARAMS := enum_style=java LOCAL_SDK_VERSION := current -LOCAL_MIN_SDK_VERSION := 21 +LOCAL_MIN_SDK_VERSION := 26 LOCAL_MODULE := Launcher3CommonDepsLib LOCAL_PRIVILEGED_MODULE := true LOCAL_MANIFEST_FILE := AndroidManifest-common.xml @@ -77,7 +77,7 @@ LOCAL_PROGUARD_FLAG_FILES := proguard.flags LOCAL_PROGUARD_ENABLED := disabled LOCAL_SDK_VERSION := current -LOCAL_MIN_SDK_VERSION := 21 +LOCAL_MIN_SDK_VERSION := 26 LOCAL_PACKAGE_NAME := Launcher3 LOCAL_PRIVILEGED_MODULE := true LOCAL_SYSTEM_EXT_MODULE := true @@ -108,7 +108,7 @@ LOCAL_RESOURCE_DIR := $(LOCAL_PATH)/go/res LOCAL_PROGUARD_FLAG_FILES := proguard.flags LOCAL_SDK_VERSION := current -LOCAL_MIN_SDK_VERSION := 21 +LOCAL_MIN_SDK_VERSION := 26 LOCAL_PACKAGE_NAME := Launcher3Go LOCAL_PRIVILEGED_MODULE := true LOCAL_SYSTEM_EXT_MODULE := true diff --git a/AndroidManifest-common.xml b/AndroidManifest-common.xml index 19a16e341a..97e3786a36 100644 --- a/AndroidManifest-common.xml +++ b/AndroidManifest-common.xml @@ -29,12 +29,6 @@ at compile time. Note that the components defined in AndroidManifest.xml are also required, with some minor changed based on the derivative app. --> - @@ -79,17 +73,6 @@ android:restoreAnyVersion="true" android:supportsRtl="true" > - - - - - - - diff --git a/AndroidManifest.xml b/AndroidManifest.xml index 4664c9315a..97bce9c1d7 100644 --- a/AndroidManifest.xml +++ b/AndroidManifest.xml @@ -20,7 +20,7 @@ - + - - - - - - - - - diff --git a/res/animator/discovery_bounce.xml b/res/animator/discovery_bounce.xml index f02ebdbace..f5548537b1 100644 --- a/res/animator/discovery_bounce.xml +++ b/res/animator/discovery_bounce.xml @@ -16,20 +16,28 @@ ** limitations under the License. */ --> - - + - - + android:valueType="floatType"> + + + + + + + diff --git a/res/color-v24/all_apps_bg_hand_fill.xml b/res/color/all_apps_bg_hand_fill.xml similarity index 100% rename from res/color-v24/all_apps_bg_hand_fill.xml rename to res/color/all_apps_bg_hand_fill.xml diff --git a/res/color-v24/all_apps_bg_hand_fill_dark.xml b/res/color/all_apps_bg_hand_fill_dark.xml similarity index 100% rename from res/color-v24/all_apps_bg_hand_fill_dark.xml rename to res/color/all_apps_bg_hand_fill_dark.xml diff --git a/res/drawable-v26/ic_deepshortcut_placeholder.xml b/res/drawable-v26/ic_deepshortcut_placeholder.xml deleted file mode 100644 index 3fa85065bd..0000000000 --- a/res/drawable-v26/ic_deepshortcut_placeholder.xml +++ /dev/null @@ -1,20 +0,0 @@ - - - - - - diff --git a/res/drawable-v26/ic_launcher_home.xml b/res/drawable-v26/ic_launcher_home.xml deleted file mode 100644 index 7038775f35..0000000000 --- a/res/drawable-v26/ic_launcher_home.xml +++ /dev/null @@ -1,21 +0,0 @@ - - - - - - - - diff --git a/res/drawable-v24/ic_block_shadow.xml b/res/drawable/ic_block_shadow.xml similarity index 100% rename from res/drawable-v24/ic_block_shadow.xml rename to res/drawable/ic_block_shadow.xml diff --git a/res/drawable/ic_deepshortcut_placeholder.xml b/res/drawable/ic_deepshortcut_placeholder.xml index 85a9694501..3fa85065bd 100644 --- a/res/drawable/ic_deepshortcut_placeholder.xml +++ b/res/drawable/ic_deepshortcut_placeholder.xml @@ -1,5 +1,6 @@ - - - - - + + + + diff --git a/res/drawable/ic_launcher_home.xml b/res/drawable/ic_launcher_home.xml index a6f2519954..7038775f35 100644 --- a/res/drawable/ic_launcher_home.xml +++ b/res/drawable/ic_launcher_home.xml @@ -13,6 +13,9 @@ See the License for the specific language governing permissions and limitations under the License. --> - + + + + + + diff --git a/res/drawable-v24/ic_remove_shadow.xml b/res/drawable/ic_remove_shadow.xml similarity index 100% rename from res/drawable-v24/ic_remove_shadow.xml rename to res/drawable/ic_remove_shadow.xml diff --git a/res/drawable-v24/ic_setup_shadow.xml b/res/drawable/ic_setup_shadow.xml similarity index 100% rename from res/drawable-v24/ic_setup_shadow.xml rename to res/drawable/ic_setup_shadow.xml diff --git a/res/drawable-v24/ic_uninstall_shadow.xml b/res/drawable/ic_uninstall_shadow.xml similarity index 100% rename from res/drawable-v24/ic_uninstall_shadow.xml rename to res/drawable/ic_uninstall_shadow.xml diff --git a/res/mipmap-hdpi/ic_launcher_home.png b/res/mipmap-hdpi/ic_launcher_home.png deleted file mode 100644 index d068d92361a06550fb1bcdf36fbc4bf56242d54e..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 236 zcmeAS@N?(olHy`uVBq!ia0vp^9w5xY3?!3`olAj~M1W6-E06|){rmU-|Ns9C14G(O z28NkujJG~Lkd;*dlwd3g@(X5gcy=QV$Z7I)aSW-r_4dL>z6Jx1mc(q=`~Q1QL!@ST zvx^@#{d2kXq4bSSI^01QuQhjd%Q~fAzm?V=urg{pU$2PM(daz_S{a5;E6(U}afk}8 z47;BZ=+{T*7@8zVPfLG>P=&IiV(vR zKTbn8<}=KmM{as0#4%>w<_hqP=C9KIV#aG9#kVxgf8%cH=Mf+Wdb;|#taD0e0sys( BO-=v+ diff --git a/res/mipmap-xhdpi/ic_launcher_home.png b/res/mipmap-xhdpi/ic_launcher_home.png deleted file mode 100644 index 8b2671bdd80f36d23f2f3d45842dfb78092feec3..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 235 zcmeAS@N?(olHy`uVBq!ia0vp^2_VeE3?v1%WpM*3i2$DvS0D`p`}gnv|Ns9P28Ohm z3=A{R7;k-gASrHabI#5x}f zrrE1+d3>F*Mn=YdO~U8??v%F<%Vx|LtWoD~o66j6_Tr_4iL*dP62FU^fCO{!!Tn9@ cJd9I<>I_fVT@aRE69aOyr>mdKI;Vst0EsD5ZU6uP diff --git a/res/mipmap-xxhdpi/ic_launcher_home.png b/res/mipmap-xxhdpi/ic_launcher_home.png deleted file mode 100644 index 43d8b7d886c9a928c666b0af551a1ab35c365aae..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 266 zcmeAS@N?(olHy`uVBq!ia0vp^6F``S8Ays|{O<-*5&=FTu0R?H_V3^S|Ns9p3=C;A z85m}sG2Z(0Kvq@-P=c`}$S;_|;n|HeAZLN6i(^Oybs)2ELra~Vb3uF0>TroB60O9PkFq+(Sw=ihUjiyf9KJfCE$ zyPMIKX@$*_(+#(pT#hU#bue`n6A-xYec^LvEq*~k!LJttIo_Q0KfmWG6Ua-Ru6{1- HoD!M<4rE|^ diff --git a/res/values-night-v26/styles.xml b/res/values-night/styles.xml similarity index 100% rename from res/values-night-v26/styles.xml rename to res/values-night/styles.xml diff --git a/res/values-v22/styles.xml b/res/values-v22/styles.xml deleted file mode 100644 index f86db7a493..0000000000 --- a/res/values-v22/styles.xml +++ /dev/null @@ -1,26 +0,0 @@ - - - - - - - - \ No newline at end of file diff --git a/res/values-v26/bools.xml b/res/values-v26/bools.xml deleted file mode 100644 index ad8c7a1865..0000000000 --- a/res/values-v26/bools.xml +++ /dev/null @@ -1,23 +0,0 @@ - - - - - true - - false - \ No newline at end of file diff --git a/res/values-v26/styles.xml b/res/values-v26/styles.xml deleted file mode 100644 index d2f0802fa5..0000000000 --- a/res/values-v26/styles.xml +++ /dev/null @@ -1,31 +0,0 @@ - - - - - - - - diff --git a/res/values/bools.xml b/res/values/bools.xml deleted file mode 100644 index bc2c678874..0000000000 --- a/res/values/bools.xml +++ /dev/null @@ -1,23 +0,0 @@ - - - - - false - - true - \ No newline at end of file diff --git a/res/values/colors.xml b/res/values/colors.xml index 043ad9a9d5..f56fbaaace 100644 --- a/res/values/colors.xml +++ b/res/values/colors.xml @@ -35,9 +35,6 @@ #E0E0E0 - #E5E5E5 - #9AA0A6 - #A0C2F9 #6DA1FF #FFFFFFFF diff --git a/res/values/drawables.xml b/res/values/drawables.xml deleted file mode 100644 index 9c57ec1214..0000000000 --- a/res/values/drawables.xml +++ /dev/null @@ -1,21 +0,0 @@ - - - - @drawable/ic_setting - @drawable/ic_remove_no_shadow - @drawable/ic_uninstall_no_shadow - @drawable/ic_block_no_shadow - \ No newline at end of file diff --git a/res/values/styles.xml b/res/values/styles.xml index 3b9532eeee..fd3d873635 100644 --- a/res/values/styles.xml +++ b/res/values/styles.xml @@ -143,7 +143,7 @@ @@ -171,14 +171,16 @@ ?attr/workspaceTextColor - + + - - + + + + + + + \ No newline at end of file diff --git a/quickstep/src/com/android/launcher3/BaseQuickstepLauncher.java b/quickstep/src/com/android/launcher3/BaseQuickstepLauncher.java index 09a3cfd184..cbe0eb0795 100644 --- a/quickstep/src/com/android/launcher3/BaseQuickstepLauncher.java +++ b/quickstep/src/com/android/launcher3/BaseQuickstepLauncher.java @@ -264,6 +264,11 @@ public abstract class BaseQuickstepLauncher extends Launcher return mTaskbarStateHandler; } + @Override + public boolean isViewInTaskbar(View v) { + return mTaskbarController != null && mTaskbarController.isViewInTaskbar(v); + } + @Override public void useFadeOutAnimationForLauncherStart(CancellationSignal signal) { QuickstepAppTransitionManagerImpl appTransitionManager = @@ -350,6 +355,10 @@ public abstract class BaseQuickstepLauncher extends Launcher // populating workspace. // TODO: Find a better place for this WellbeingModel.INSTANCE.get(this); + + if (mTaskbarController != null) { + mTaskbarController.onHotseatUpdated(); + } } @Override diff --git a/quickstep/src/com/android/launcher3/QuickstepAppTransitionManagerImpl.java b/quickstep/src/com/android/launcher3/QuickstepAppTransitionManagerImpl.java index 36c8bb8b99..876cabc61b 100644 --- a/quickstep/src/com/android/launcher3/QuickstepAppTransitionManagerImpl.java +++ b/quickstep/src/com/android/launcher3/QuickstepAppTransitionManagerImpl.java @@ -202,9 +202,10 @@ public abstract class QuickstepAppTransitionManagerImpl extends LauncherAppTrans } @Override - public boolean supportsAdaptiveIconAnimation() { + public boolean supportsAdaptiveIconAnimation(View clickedView) { return hasControlRemoteAppTransitionPermission() - && FeatureFlags.ADAPTIVE_ICON_WINDOW_ANIM.get(); + && FeatureFlags.ADAPTIVE_ICON_WINDOW_ANIM.get() + && !mLauncher.isViewInTaskbar(clickedView); } /** @@ -972,9 +973,12 @@ public abstract class QuickstepAppTransitionManagerImpl extends LauncherAppTrans launcherIsATargetWithMode(appTargets, MODE_CLOSING); final boolean launchingFromRecents = isLaunchingFromRecents(mV, appTargets); + final boolean launchingFromTaskbar = mLauncher.isViewInTaskbar(mV); if (launchingFromRecents) { composeRecentsLaunchAnimator(anim, mV, appTargets, wallpaperTargets, launcherClosing); + } else if (launchingFromTaskbar) { + // TODO } else { composeIconLaunchAnimator(anim, mV, appTargets, wallpaperTargets, launcherClosing); diff --git a/quickstep/src/com/android/launcher3/hybridhotseat/HotseatPredictionController.java b/quickstep/src/com/android/launcher3/hybridhotseat/HotseatPredictionController.java index b2de4c95ad..aa6601b8c8 100644 --- a/quickstep/src/com/android/launcher3/hybridhotseat/HotseatPredictionController.java +++ b/quickstep/src/com/android/launcher3/hybridhotseat/HotseatPredictionController.java @@ -37,7 +37,6 @@ import com.android.launcher3.DragSource; import com.android.launcher3.DropTarget; import com.android.launcher3.Hotseat; import com.android.launcher3.InvariantDeviceProfile; -import com.android.launcher3.Launcher; import com.android.launcher3.LauncherSettings; import com.android.launcher3.R; import com.android.launcher3.Utilities; @@ -75,7 +74,7 @@ public class HotseatPredictionController implements DragController.DragListener, private int mHotSeatItemsCount; - private Launcher mLauncher; + private QuickstepLauncher mLauncher; private final Hotseat mHotseat; private List mPredictedItems = Collections.emptyList(); @@ -108,7 +107,7 @@ public class HotseatPredictionController implements DragController.DragListener, return true; }; - public HotseatPredictionController(Launcher launcher) { + public HotseatPredictionController(QuickstepLauncher launcher) { mLauncher = launcher; mHotseat = launcher.getHotseat(); mHotSeatItemsCount = mLauncher.getDeviceProfile().inv.numHotseatIcons; @@ -229,6 +228,10 @@ public class HotseatPredictionController implements DragController.DragListener, } else { if (callback != null) callback.run(); } + + if (mLauncher.getTaskbarController() != null) { + mLauncher.getTaskbarController().onHotseatUpdated(); + } } /** @@ -242,6 +245,10 @@ public class HotseatPredictionController implements DragController.DragListener, * start and pauses predicted apps update on the hotseat */ public void setPauseUIUpdate(boolean paused) { + if (mLauncher.getTaskbarController() != null) { + // Taskbar is present, always allow updates since hotseat is still visible. + return; + } mUIUpdatePaused = paused; if (!paused) { fillGapsWithPrediction(); diff --git a/quickstep/src/com/android/launcher3/taskbar/TaskbarController.java b/quickstep/src/com/android/launcher3/taskbar/TaskbarController.java index bdf7f8ad08..f91bfb78fc 100644 --- a/quickstep/src/com/android/launcher3/taskbar/TaskbarController.java +++ b/quickstep/src/com/android/launcher3/taskbar/TaskbarController.java @@ -26,6 +26,7 @@ import android.animation.Animator; import android.graphics.PixelFormat; import android.graphics.Point; import android.view.Gravity; +import android.view.View; import android.view.WindowManager; import androidx.annotation.Nullable; @@ -36,7 +37,9 @@ import com.android.launcher3.QuickstepAppTransitionManagerImpl; import com.android.launcher3.R; import com.android.launcher3.anim.AlphaUpdateListener; import com.android.launcher3.anim.PendingAnimation; +import com.android.launcher3.model.data.ItemInfo; import com.android.launcher3.states.StateAnimationConfig; +import com.android.launcher3.touch.ItemClickHandler; import com.android.quickstep.AnimatedFloat; import com.android.systemui.shared.system.WindowManagerWrapper; @@ -55,6 +58,7 @@ public class TaskbarController { private final Point mTaskbarSize; private final TaskbarStateHandler mTaskbarStateHandler; private final TaskbarVisibilityController mTaskbarVisibilityController; + private final TaskbarHotseatController mHotseatController; // Initialized in init(). private WindowManager.LayoutParams mWindowLayoutParams; @@ -64,12 +68,15 @@ public class TaskbarController { mLauncher = launcher; mTaskbarContainerView = taskbarContainerView; mTaskbarView = mTaskbarContainerView.findViewById(R.id.taskbar_view); + mTaskbarView.setCallbacks(createTaskbarViewCallbacks()); mWindowManager = mLauncher.getWindowManager(); mTaskbarSize = new Point(MATCH_PARENT, mLauncher.getResources().getDimensionPixelSize(R.dimen.taskbar_size)); mTaskbarStateHandler = mLauncher.getTaskbarStateHandler(); mTaskbarVisibilityController = new TaskbarVisibilityController(mLauncher, createTaskbarVisibilityControllerCallbacks()); + mHotseatController = new TaskbarHotseatController(mLauncher, + createTaskbarHotseatControllerCallbacks()); } private TaskbarVisibilityControllerCallbacks createTaskbarVisibilityControllerCallbacks() { @@ -87,13 +94,33 @@ public class TaskbarController { }; } + private TaskbarViewCallbacks createTaskbarViewCallbacks() { + return new TaskbarViewCallbacks() { + @Override + public View.OnClickListener getItemOnClickListener() { + return ItemClickHandler.INSTANCE; + } + }; + } + + private TaskbarHotseatControllerCallbacks createTaskbarHotseatControllerCallbacks() { + return new TaskbarHotseatControllerCallbacks() { + @Override + public void updateHotseatItems(ItemInfo[] hotseatItemInfos) { + mTaskbarView.updateHotseatItems(hotseatItemInfos); + } + }; + } + /** * Initializes the Taskbar, including adding it to the screen. */ public void init() { + mTaskbarView.init(mHotseatController.getNumHotseatIcons()); addToWindowManager(); mTaskbarStateHandler.setTaskbarCallbacks(createTaskbarStateHandlerCallbacks()); mTaskbarVisibilityController.init(); + mHotseatController.init(); } private TaskbarStateHandlerCallbacks createTaskbarStateHandlerCallbacks() { @@ -109,9 +136,11 @@ public class TaskbarController { * Removes the Taskbar from the screen, and removes any obsolete listeners etc. */ public void cleanup() { + mTaskbarView.cleanup(); removeFromWindowManager(); mTaskbarStateHandler.setTaskbarCallbacks(null); mTaskbarVisibilityController.cleanup(); + mHotseatController.cleanup(); } private void removeFromWindowManager() { @@ -190,6 +219,20 @@ public class TaskbarController { mTaskbarVisibilityController.animateToVisibilityForIme(isImeVisible ? 0 : 1); } + /** + * Should be called when one or more items in the Hotseat have changed. + */ + public void onHotseatUpdated() { + mHotseatController.onHotseatUpdated(); + } + + /** + * @return Whether the given View is in the same window as Taskbar. + */ + public boolean isViewInTaskbar(View v) { + return mTaskbarContainerView.getWindowId().equals(v.getWindowId()); + } + /** * Contains methods that TaskbarStateHandler can call to interface with TaskbarController. */ @@ -205,4 +248,18 @@ public class TaskbarController { void updateTaskbarBackgroundAlpha(float alpha); void updateTaskbarVisibilityAlpha(float alpha); } + + /** + * Contains methods that TaskbarView can call to interface with TaskbarController. + */ + protected interface TaskbarViewCallbacks { + View.OnClickListener getItemOnClickListener(); + } + + /** + * Contains methods that TaskbarHotseatController can call to interface with TaskbarController. + */ + protected interface TaskbarHotseatControllerCallbacks { + void updateHotseatItems(ItemInfo[] hotseatItemInfos); + } } diff --git a/quickstep/src/com/android/launcher3/taskbar/TaskbarHotseatController.java b/quickstep/src/com/android/launcher3/taskbar/TaskbarHotseatController.java new file mode 100644 index 0000000000..4dc051aaa9 --- /dev/null +++ b/quickstep/src/com/android/launcher3/taskbar/TaskbarHotseatController.java @@ -0,0 +1,90 @@ +/* + * Copyright (C) 2021 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.taskbar; + +import android.view.View; + +import com.android.launcher3.BaseQuickstepLauncher; +import com.android.launcher3.CellLayout; +import com.android.launcher3.DropTarget; +import com.android.launcher3.Hotseat; +import com.android.launcher3.ShortcutAndWidgetContainer; +import com.android.launcher3.dragndrop.DragController; +import com.android.launcher3.dragndrop.DragOptions; +import com.android.launcher3.model.data.ItemInfo; + +/** + * Works with TaskbarController to update the TaskbarView's Hotseat items. + */ +public class TaskbarHotseatController { + + private final BaseQuickstepLauncher mLauncher; + private final Hotseat mHotseat; + private final TaskbarController.TaskbarHotseatControllerCallbacks mTaskbarCallbacks; + private final int mNumHotseatIcons; + + private final DragController.DragListener mDragListener = new DragController.DragListener() { + @Override + public void onDragStart(DropTarget.DragObject dragObject, DragOptions options) { + } + + @Override + public void onDragEnd() { + onHotseatUpdated(); + } + }; + + public TaskbarHotseatController(BaseQuickstepLauncher launcher, + TaskbarController.TaskbarHotseatControllerCallbacks taskbarCallbacks) { + mLauncher = launcher; + mHotseat = mLauncher.getHotseat(); + mTaskbarCallbacks = taskbarCallbacks; + mNumHotseatIcons = mLauncher.getDeviceProfile().inv.numHotseatIcons; + } + + protected void init() { + mLauncher.getDragController().addDragListener(mDragListener); + } + + protected void cleanup() { + mLauncher.getDragController().removeDragListener(mDragListener); + } + + /** + * Called when any Hotseat item changes, and reports the new list of items to TaskbarController. + */ + protected void onHotseatUpdated() { + ShortcutAndWidgetContainer shortcutsAndWidgets = mHotseat.getShortcutsAndWidgets(); + ItemInfo[] hotseatItemInfos = new ItemInfo[mNumHotseatIcons]; + for (int i = 0; i < shortcutsAndWidgets.getChildCount(); i++) { + View child = shortcutsAndWidgets.getChildAt(i); + Object tag = shortcutsAndWidgets.getChildAt(i).getTag(); + if (tag instanceof ItemInfo) { + ItemInfo itemInfo = (ItemInfo) tag; + CellLayout.LayoutParams lp = (CellLayout.LayoutParams) child.getLayoutParams(); + // Since the hotseat might be laid out vertically or horizontally, use whichever + // index is higher. + hotseatItemInfos[Math.max(lp.cellX, lp.cellY)] = itemInfo; + } + } + + mTaskbarCallbacks.updateHotseatItems(hotseatItemInfos); + } + + protected int getNumHotseatIcons() { + return mNumHotseatIcons; + } +} diff --git a/quickstep/src/com/android/launcher3/taskbar/TaskbarView.java b/quickstep/src/com/android/launcher3/taskbar/TaskbarView.java index bf6e946f0d..0d82810935 100644 --- a/quickstep/src/com/android/launcher3/taskbar/TaskbarView.java +++ b/quickstep/src/com/android/launcher3/taskbar/TaskbarView.java @@ -16,19 +16,35 @@ package com.android.launcher3.taskbar; import android.content.Context; +import android.content.res.Resources; import android.graphics.drawable.ColorDrawable; import android.util.AttributeSet; +import android.view.LayoutInflater; +import android.view.View; import android.widget.LinearLayout; +import androidx.annotation.LayoutRes; import androidx.annotation.NonNull; import androidx.annotation.Nullable; +import com.android.launcher3.BubbleTextView; +import com.android.launcher3.R; +import com.android.launcher3.model.data.ItemInfo; +import com.android.launcher3.model.data.WorkspaceItemInfo; + /** * Hosts the Taskbar content such as Hotseat and Recent Apps. Drawn on top of other apps. */ public class TaskbarView extends LinearLayout { private final ColorDrawable mBackgroundDrawable; + private final int mItemMarginLeftRight; + + // Initialized in init(). + private int mHotseatStartIndex; + private int mHotseatEndIndex; + + private TaskbarController.TaskbarViewCallbacks mControllerCallbacks; public TaskbarView(@NonNull Context context) { this(context, null); @@ -46,7 +62,24 @@ public class TaskbarView extends LinearLayout { public TaskbarView(@NonNull Context context, @Nullable AttributeSet attrs, int defStyleAttr, int defStyleRes) { super(context, attrs, defStyleAttr, defStyleRes); + + Resources resources = getResources(); mBackgroundDrawable = (ColorDrawable) getBackground(); + mItemMarginLeftRight = resources.getDimensionPixelSize(R.dimen.taskbar_icon_spacing); + } + + protected void setCallbacks(TaskbarController.TaskbarViewCallbacks taskbarViewCallbacks) { + mControllerCallbacks = taskbarViewCallbacks; + } + + protected void init(int numHotseatIcons) { + mHotseatStartIndex = 0; + mHotseatEndIndex = mHotseatStartIndex + numHotseatIcons - 1; + updateHotseatItems(new ItemInfo[numHotseatIcons]); + } + + protected void cleanup() { + removeAllViews(); } /** @@ -56,4 +89,47 @@ public class TaskbarView extends LinearLayout { public void setBackgroundAlpha(float alpha) { mBackgroundDrawable.setAlpha((int) (alpha * 255)); } + + /** + * Inflates/binds the Hotseat views to show in the Taskbar given their ItemInfos. + */ + protected void updateHotseatItems(ItemInfo[] hotseatItemInfos) { + for (int i = 0; i < hotseatItemInfos.length; i++) { + ItemInfo hotseatItemInfo = hotseatItemInfos[i]; + int hotseatIndex = mHotseatStartIndex + i; + View hotseatView = getChildAt(hotseatIndex); + + // Replace any Hotseat views with the appropriate type if it's not already that type. + final int expectedLayoutResId; + if (hotseatItemInfo != null && hotseatItemInfo.isPredictedItem()) { + expectedLayoutResId = R.layout.taskbar_predicted_app_icon; + } else { + expectedLayoutResId = R.layout.taskbar_app_icon; + } + if (hotseatView == null || hotseatView.getSourceLayoutResId() != expectedLayoutResId) { + removeView(hotseatView); + BubbleTextView btv = (BubbleTextView) inflate(expectedLayoutResId); + LayoutParams lp = new LayoutParams(btv.getIconSize(), btv.getIconSize()); + lp.setMargins(mItemMarginLeftRight, 0, mItemMarginLeftRight, 0); + hotseatView = btv; + addView(hotseatView, hotseatIndex, lp); + } + + // Apply the Hotseat ItemInfos, or hide the view if there is none for a given index. + if (hotseatView instanceof BubbleTextView + && hotseatItemInfo instanceof WorkspaceItemInfo) { + ((BubbleTextView) hotseatView).applyFromWorkspaceItem( + (WorkspaceItemInfo) hotseatItemInfo); + hotseatView.setVisibility(VISIBLE); + hotseatView.setOnClickListener(mControllerCallbacks.getItemOnClickListener()); + } else { + hotseatView.setVisibility(GONE); + hotseatView.setOnClickListener(null); + } + } + } + + private View inflate(@LayoutRes int layoutResId) { + return LayoutInflater.from(getContext()).inflate(layoutResId, this, false); + } } diff --git a/quickstep/src/com/android/launcher3/uioverrides/PredictedAppIcon.java b/quickstep/src/com/android/launcher3/uioverrides/PredictedAppIcon.java index 597c17b4e2..22c4a7eba3 100644 --- a/quickstep/src/com/android/launcher3/uioverrides/PredictedAppIcon.java +++ b/quickstep/src/com/android/launcher3/uioverrides/PredictedAppIcon.java @@ -184,7 +184,10 @@ public class PredictedAppIcon extends DoubleShadowBubbleTextView implements } private int getOutlineOffsetY() { - return getPaddingTop() + mDeviceProfile.folderIconOffsetYPx; + if (mDisplay != DISPLAY_TASKBAR) { + return getPaddingTop() + mDeviceProfile.folderIconOffsetYPx; + } + return (getMeasuredHeight() / 2) - mNormalizedIconRadius; } private void drawEffect(Canvas canvas, boolean isBadged) { diff --git a/res/values/attrs.xml b/res/values/attrs.xml index 96c30b5f03..e593fb497d 100644 --- a/res/values/attrs.xml +++ b/res/values/attrs.xml @@ -57,6 +57,7 @@ + diff --git a/src/com/android/launcher3/BubbleTextView.java b/src/com/android/launcher3/BubbleTextView.java index ecda501f1f..140340af62 100644 --- a/src/com/android/launcher3/BubbleTextView.java +++ b/src/com/android/launcher3/BubbleTextView.java @@ -92,6 +92,7 @@ public class BubbleTextView extends TextView implements ItemInfoUpdateReceiver, private static final int DISPLAY_ALL_APPS = 1; private static final int DISPLAY_FOLDER = 2; private static final int DISPLAY_HERO_APP = 5; + protected static final int DISPLAY_TASKBAR = 6; private static final int[] STATE_PRESSED = new int[]{android.R.attr.state_pressed}; private static final float HIGHLIGHT_SCALE = 1.16f; @@ -140,7 +141,7 @@ public class BubbleTextView extends TextView implements ItemInfoUpdateReceiver, private Drawable mIcon; private boolean mCenterVertically; - private final int mDisplay; + protected final int mDisplay; private final CheckLongPressHelper mLongPressHelper; @@ -206,6 +207,8 @@ public class BubbleTextView extends TextView implements ItemInfoUpdateReceiver, defaultIconSize = grid.folderChildIconSizePx; } else if (mDisplay == DISPLAY_HERO_APP) { defaultIconSize = grid.allAppsIconSizePx; + } else if (mDisplay == DISPLAY_TASKBAR) { + defaultIconSize = grid.iconSizePx; } else { // widget_selection or shortcut_popup defaultIconSize = grid.iconSizePx; @@ -487,6 +490,10 @@ public class BubbleTextView extends TextView implements ItemInfoUpdateReceiver, * @param canvas The canvas to draw to. */ protected void drawDotIfNecessary(Canvas canvas) { + if (mDisplay == DISPLAY_TASKBAR) { + // TODO: support notification dots in Taskbar + return; + } if (!mForceHideDot && (hasDot() || mDotParams.scale > 0)) { getIconBounds(mDotParams.iconBounds); Utilities.scaleRectAboutCenter(mDotParams.iconBounds, diff --git a/src/com/android/launcher3/Launcher.java b/src/com/android/launcher3/Launcher.java index 0274775244..2334267fd9 100644 --- a/src/com/android/launcher3/Launcher.java +++ b/src/com/android/launcher3/Launcher.java @@ -1917,6 +1917,13 @@ public class Launcher extends StatefulActivity implements Launche @Override public boolean startActivitySafely(View v, Intent intent, ItemInfo item) { + if (isViewInTaskbar(v)) { + // Start the activity without the hacky workarounds below, which assume the View was + // clicked when Launcher was resumed and will be hidden until Launcher is re-resumed + // (this isn't the case for Taskbar). + return super.startActivitySafely(v, intent, item); + } + if (!hasBeenResumed()) { // Workaround an issue where the WM launch animation is clobbered when finishing the // recents animation into launcher. Defer launching the activity until Launcher is @@ -2818,6 +2825,13 @@ public class Launcher extends StatefulActivity implements Launche .start(); } + /** + * @return Whether the View is in the same window as the Taskbar window. + */ + public boolean isViewInTaskbar(View v) { + return false; + } + private static class NonConfigInstance { public Configuration config; public Bitmap snapshot; diff --git a/src/com/android/launcher3/LauncherAppTransitionManager.java b/src/com/android/launcher3/LauncherAppTransitionManager.java index ac3ad9f8fd..0fa441a279 100644 --- a/src/com/android/launcher3/LauncherAppTransitionManager.java +++ b/src/com/android/launcher3/LauncherAppTransitionManager.java @@ -50,7 +50,7 @@ public class LauncherAppTransitionManager implements ResourceBasedOverride { return ActivityOptions.makeClipRevealAnimation(v, left, top, width, height); } - public boolean supportsAdaptiveIconAnimation() { + public boolean supportsAdaptiveIconAnimation(View clickedView) { return false; } diff --git a/src/com/android/launcher3/touch/ItemClickHandler.java b/src/com/android/launcher3/touch/ItemClickHandler.java index 3122c68304..2647d6fec4 100644 --- a/src/com/android/launcher3/touch/ItemClickHandler.java +++ b/src/com/android/launcher3/touch/ItemClickHandler.java @@ -302,7 +302,7 @@ public class ItemClickHandler { intent.setPackage(null); } } - if (v != null && launcher.getAppTransitionManager().supportsAdaptiveIconAnimation()) { + if (v != null && launcher.getAppTransitionManager().supportsAdaptiveIconAnimation(v)) { // Preload the icon to reduce latency b/w swapping the floating view with the original. FloatingIconView.fetchIcon(launcher, v, item, true /* isOpening */); } -- GitLab From b11e4d517d5f80f26ceb22e79030aace712ff5e2 Mon Sep 17 00:00:00 2001 From: Tony Wickham Date: Fri, 22 Jan 2021 11:49:11 -0800 Subject: [PATCH 0451/1664] Delegate Taskbar touches to nearest view to ensure 48x48dp touch size In TaskbarView#onTouchEvent(), which is only reached if a Taskbar icon didn't already consuem the event, check each child to see if the event occurs within a 48x48dp bounding box, and delegate the event and subsequent events to it until UP or CANCEL. Bug: 171917176 Change-Id: I7afafe0835828ab9213ec6abfe4e88ad7b9af3c4 --- quickstep/res/values/dimens.xml | 1 + .../launcher3/taskbar/TaskbarView.java | 81 +++++++++++++++++++ 2 files changed, 82 insertions(+) diff --git a/quickstep/res/values/dimens.xml b/quickstep/res/values/dimens.xml index f082b83c4a..39a7d09649 100644 --- a/quickstep/res/values/dimens.xml +++ b/quickstep/res/values/dimens.xml @@ -123,6 +123,7 @@ 48dp 32dp + 48dp 14dp diff --git a/quickstep/src/com/android/launcher3/taskbar/TaskbarView.java b/quickstep/src/com/android/launcher3/taskbar/TaskbarView.java index 0d82810935..adcfaec102 100644 --- a/quickstep/src/com/android/launcher3/taskbar/TaskbarView.java +++ b/quickstep/src/com/android/launcher3/taskbar/TaskbarView.java @@ -17,10 +17,13 @@ package com.android.launcher3.taskbar; import android.content.Context; import android.content.res.Resources; +import android.graphics.RectF; import android.graphics.drawable.ColorDrawable; import android.util.AttributeSet; import android.view.LayoutInflater; +import android.view.MotionEvent; import android.view.View; +import android.view.ViewConfiguration; import android.widget.LinearLayout; import androidx.annotation.LayoutRes; @@ -39,6 +42,10 @@ public class TaskbarView extends LinearLayout { private final ColorDrawable mBackgroundDrawable; private final int mItemMarginLeftRight; + private final int mIconTouchSize; + private final int mTouchSlop; + private final RectF mTempDelegateBounds = new RectF(); + private final RectF mDelegateSlopBounds = new RectF(); // Initialized in init(). private int mHotseatStartIndex; @@ -46,6 +53,10 @@ public class TaskbarView extends LinearLayout { private TaskbarController.TaskbarViewCallbacks mControllerCallbacks; + // Delegate touches to the closest view if within mIconTouchSize. + private boolean mDelegateTargeted; + private View mDelegateView; + public TaskbarView(@NonNull Context context) { this(context, null); } @@ -66,6 +77,8 @@ public class TaskbarView extends LinearLayout { Resources resources = getResources(); mBackgroundDrawable = (ColorDrawable) getBackground(); mItemMarginLeftRight = resources.getDimensionPixelSize(R.dimen.taskbar_icon_spacing); + mIconTouchSize = resources.getDimensionPixelSize(R.dimen.taskbar_icon_touch_size); + mTouchSlop = ViewConfiguration.get(context).getScaledTouchSlop(); } protected void setCallbacks(TaskbarController.TaskbarViewCallbacks taskbarViewCallbacks) { @@ -129,6 +142,74 @@ public class TaskbarView extends LinearLayout { } } + @Override + public boolean onTouchEvent(MotionEvent event) { + boolean handled = delegateTouchIfNecessary(event); + return super.onTouchEvent(event) || handled; + } + + /** + * User touched the Taskbar background. Determine whether the touch is close enough to a view + * that we should forward the touches to it. + * @return Whether a delegate view was chosen and it handled the touch event. + */ + private boolean delegateTouchIfNecessary(MotionEvent event) { + final float x = event.getX(); + final float y = event.getY(); + if (mDelegateView == null && event.getAction() == MotionEvent.ACTION_DOWN) { + for (int i = 0; i < getChildCount(); i++) { + View child = getChildAt(i); + if (!child.isShown() || !child.isClickable()) { + continue; + } + int childCenterX = child.getLeft() + child.getWidth() / 2; + int childCenterY = child.getTop() + child.getHeight() / 2; + mTempDelegateBounds.set( + childCenterX - mIconTouchSize / 2f, + childCenterY - mIconTouchSize / 2f, + childCenterX + mIconTouchSize / 2f, + childCenterY + mIconTouchSize / 2f); + mDelegateTargeted = mTempDelegateBounds.contains(x, y); + if (mDelegateTargeted) { + mDelegateView = child; + mDelegateSlopBounds.set(mTempDelegateBounds); + mDelegateSlopBounds.inset(-mTouchSlop, -mTouchSlop); + break; + } + } + } + + boolean sendToDelegate = mDelegateTargeted; + boolean inBounds = true; + switch (event.getAction()) { + case MotionEvent.ACTION_MOVE: + inBounds = mDelegateSlopBounds.contains(x, y); + break; + case MotionEvent.ACTION_UP: + case MotionEvent.ACTION_CANCEL: + mDelegateTargeted = false; + break; + } + + boolean handled = false; + if (sendToDelegate) { + if (inBounds) { + // Offset event coordinates to be inside the target view + event.setLocation(mDelegateView.getWidth() / 2f, mDelegateView.getHeight() / 2f); + } else { + // Offset event coordinates to be outside the target view (in case it does + // something like tracking pressed state) + event.setLocation(-mTouchSlop * 2, -mTouchSlop * 2); + } + handled = mDelegateView.dispatchTouchEvent(event); + // Cleanup if this was the last event to send to the delegate. + if (!mDelegateTargeted) { + mDelegateView = null; + } + } + return handled; + } + private View inflate(@LayoutRes int layoutResId) { return LayoutInflater.from(getContext()).inflate(layoutResId, this, false); } -- GitLab From e747278ee8e81bcb23715d471876fa6a861422c2 Mon Sep 17 00:00:00 2001 From: Tony Wickham Date: Fri, 22 Jan 2021 18:45:04 -0800 Subject: [PATCH 0452/1664] Support drag and drop from Taskbar - Long clicking a BubbleTextView in Taskbar will start a system drag and drop operation, setting the original view invisible meanwhile. - Defer gesture navigation when starting over a Taskbar item, and cancel any started gesture if a Taskbar drag and drop starts. Bug: 171917176 Change-Id: If5049071fbf1755f545ee937daa4edabd869f00d --- quickstep/res/values/dimens.xml | 1 + .../launcher3/taskbar/TaskbarController.java | 21 +++ .../taskbar/TaskbarDragController.java | 133 ++++++++++++++++++ .../launcher3/taskbar/TaskbarView.java | 86 ++++++++--- .../quickstep/BaseActivityInterface.java | 7 + .../quickstep/LauncherActivityInterface.java | 19 +++ .../quickstep/TouchInteractionService.java | 7 +- 7 files changed, 254 insertions(+), 20 deletions(-) create mode 100644 quickstep/src/com/android/launcher3/taskbar/TaskbarDragController.java diff --git a/quickstep/res/values/dimens.xml b/quickstep/res/values/dimens.xml index 39a7d09649..39cc0b891c 100644 --- a/quickstep/res/values/dimens.xml +++ b/quickstep/res/values/dimens.xml @@ -124,6 +124,7 @@ 48dp 32dp 48dp + 54dp 14dp diff --git a/quickstep/src/com/android/launcher3/taskbar/TaskbarController.java b/quickstep/src/com/android/launcher3/taskbar/TaskbarController.java index f91bfb78fc..7608645a63 100644 --- a/quickstep/src/com/android/launcher3/taskbar/TaskbarController.java +++ b/quickstep/src/com/android/launcher3/taskbar/TaskbarController.java @@ -26,6 +26,7 @@ import android.animation.Animator; import android.graphics.PixelFormat; import android.graphics.Point; import android.view.Gravity; +import android.view.MotionEvent; import android.view.View; import android.view.WindowManager; @@ -59,6 +60,7 @@ public class TaskbarController { private final TaskbarStateHandler mTaskbarStateHandler; private final TaskbarVisibilityController mTaskbarVisibilityController; private final TaskbarHotseatController mHotseatController; + private final TaskbarDragController mDragController; // Initialized in init(). private WindowManager.LayoutParams mWindowLayoutParams; @@ -77,6 +79,7 @@ public class TaskbarController { createTaskbarVisibilityControllerCallbacks()); mHotseatController = new TaskbarHotseatController(mLauncher, createTaskbarHotseatControllerCallbacks()); + mDragController = new TaskbarDragController(mLauncher); } private TaskbarVisibilityControllerCallbacks createTaskbarVisibilityControllerCallbacks() { @@ -100,6 +103,11 @@ public class TaskbarController { public View.OnClickListener getItemOnClickListener() { return ItemClickHandler.INSTANCE; } + + @Override + public View.OnLongClickListener getItemOnLongClickListener() { + return mDragController::startDragOnLongClick; + } }; } @@ -226,6 +234,18 @@ public class TaskbarController { mHotseatController.onHotseatUpdated(); } + /** + * @param ev MotionEvent in screen coordinates. + * @return Whether any Taskbar item could handle the given MotionEvent if given the chance. + */ + public boolean isEventOverAnyTaskbarItem(MotionEvent ev) { + return mTaskbarView.isEventOverAnyItem(ev); + } + + public boolean isDraggingItem() { + return mTaskbarView.isDraggingItem(); + } + /** * @return Whether the given View is in the same window as Taskbar. */ @@ -254,6 +274,7 @@ public class TaskbarController { */ protected interface TaskbarViewCallbacks { View.OnClickListener getItemOnClickListener(); + View.OnLongClickListener getItemOnLongClickListener(); } /** diff --git a/quickstep/src/com/android/launcher3/taskbar/TaskbarDragController.java b/quickstep/src/com/android/launcher3/taskbar/TaskbarDragController.java new file mode 100644 index 0000000000..2318ff96b6 --- /dev/null +++ b/quickstep/src/com/android/launcher3/taskbar/TaskbarDragController.java @@ -0,0 +1,133 @@ +/* + * Copyright (C) 2021 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.taskbar; + +import static android.view.View.INVISIBLE; +import static android.view.View.VISIBLE; + +import android.content.ClipData; +import android.content.ClipDescription; +import android.content.Intent; +import android.content.pm.LauncherApps; +import android.content.res.Resources; +import android.graphics.Canvas; +import android.graphics.Point; +import android.view.DragEvent; +import android.view.View; + +import com.android.launcher3.BaseQuickstepLauncher; +import com.android.launcher3.BubbleTextView; +import com.android.launcher3.LauncherSettings; +import com.android.launcher3.R; +import com.android.launcher3.model.data.WorkspaceItemInfo; +import com.android.systemui.shared.system.ClipDescriptionCompat; +import com.android.systemui.shared.system.LauncherAppsCompat; + +/** + * Handles long click on Taskbar items to start a system drag and drop operation. + */ +public class TaskbarDragController { + + private final BaseQuickstepLauncher mLauncher; + private final int mDragIconSize; + + public TaskbarDragController(BaseQuickstepLauncher launcher) { + mLauncher = launcher; + Resources resources = mLauncher.getResources(); + mDragIconSize = resources.getDimensionPixelSize(R.dimen.taskbar_icon_drag_icon_size); + } + + /** + * Attempts to start a system drag and drop operation for the given View, using its tag to + * generate the ClipDescription and Intent. + * @return Whether {@link View#startDragAndDrop} started successfully. + */ + protected boolean startDragOnLongClick(View view) { + if (!(view instanceof BubbleTextView)) { + return false; + } + + BubbleTextView btv = (BubbleTextView) view; + + View.DragShadowBuilder shadowBuilder = new View.DragShadowBuilder(view) { + @Override + public void onProvideShadowMetrics(Point shadowSize, Point shadowTouchPoint) { + shadowSize.set(mDragIconSize, mDragIconSize); + // TODO: should be based on last touch point on the icon. + shadowTouchPoint.set(shadowSize.x / 2, shadowSize.y / 2); + } + + @Override + public void onDrawShadow(Canvas canvas) { + canvas.save(); + float scale = (float) mDragIconSize / btv.getIconSize(); + canvas.scale(scale, scale); + btv.getIcon().draw(canvas); + canvas.restore(); + } + }; + + Object tag = view.getTag(); + ClipDescription clipDescription = null; + Intent intent = null; + if (tag instanceof WorkspaceItemInfo) { + WorkspaceItemInfo item = (WorkspaceItemInfo) tag; + LauncherApps launcherApps = mLauncher.getSystemService(LauncherApps.class); + clipDescription = new ClipDescription(item.title, + new String[] { + item.itemType == LauncherSettings.Favorites.ITEM_TYPE_DEEP_SHORTCUT + ? ClipDescriptionCompat.MIMETYPE_APPLICATION_SHORTCUT + : ClipDescriptionCompat.MIMETYPE_APPLICATION_ACTIVITY + }); + intent = new Intent(); + if (item.itemType == LauncherSettings.Favorites.ITEM_TYPE_DEEP_SHORTCUT) { + intent.putExtra(Intent.EXTRA_PACKAGE_NAME, item.getIntent().getPackage()); + intent.putExtra(Intent.EXTRA_SHORTCUT_ID, item.getDeepShortcutId()); + } else { + intent.putExtra(ClipDescriptionCompat.EXTRA_PENDING_INTENT, + LauncherAppsCompat.getMainActivityLaunchIntent(launcherApps, + item.getIntent().getComponent(), null, item.user)); + } + intent.putExtra(Intent.EXTRA_USER, item.user); + } + + if (clipDescription != null && intent != null) { + ClipData clipData = new ClipData(clipDescription, new ClipData.Item(intent)); + view.setOnDragListener(getDraggedViewDragListener()); + return view.startDragAndDrop(clipData, shadowBuilder, null /* localState */, + View.DRAG_FLAG_GLOBAL | View.DRAG_FLAG_OPAQUE); + } + return false; + } + + /** + * Hide the original Taskbar item while it is being dragged. + */ + private View.OnDragListener getDraggedViewDragListener() { + return (view, dragEvent) -> { + switch (dragEvent.getAction()) { + case DragEvent.ACTION_DRAG_STARTED: + view.setVisibility(INVISIBLE); + return true; + case DragEvent.ACTION_DRAG_ENDED: + view.setVisibility(VISIBLE); + view.setOnDragListener(null); + return true; + } + return false; + }; + } +} diff --git a/quickstep/src/com/android/launcher3/taskbar/TaskbarView.java b/quickstep/src/com/android/launcher3/taskbar/TaskbarView.java index adcfaec102..c98f09ca0f 100644 --- a/quickstep/src/com/android/launcher3/taskbar/TaskbarView.java +++ b/quickstep/src/com/android/launcher3/taskbar/TaskbarView.java @@ -20,6 +20,7 @@ import android.content.res.Resources; import android.graphics.RectF; import android.graphics.drawable.ColorDrawable; import android.util.AttributeSet; +import android.view.DragEvent; import android.view.LayoutInflater; import android.view.MotionEvent; import android.view.View; @@ -46,6 +47,7 @@ public class TaskbarView extends LinearLayout { private final int mTouchSlop; private final RectF mTempDelegateBounds = new RectF(); private final RectF mDelegateSlopBounds = new RectF(); + private final int[] mTempOutLocation = new int[2]; // Initialized in init(). private int mHotseatStartIndex; @@ -57,6 +59,8 @@ public class TaskbarView extends LinearLayout { private boolean mDelegateTargeted; private View mDelegateView; + private boolean mIsDraggingItem; + public TaskbarView(@NonNull Context context) { this(context, null); } @@ -135,9 +139,12 @@ public class TaskbarView extends LinearLayout { (WorkspaceItemInfo) hotseatItemInfo); hotseatView.setVisibility(VISIBLE); hotseatView.setOnClickListener(mControllerCallbacks.getItemOnClickListener()); + hotseatView.setOnLongClickListener( + mControllerCallbacks.getItemOnLongClickListener()); } else { hotseatView.setVisibility(GONE); hotseatView.setOnClickListener(null); + hotseatView.setOnLongClickListener(null); } } } @@ -157,25 +164,12 @@ public class TaskbarView extends LinearLayout { final float x = event.getX(); final float y = event.getY(); if (mDelegateView == null && event.getAction() == MotionEvent.ACTION_DOWN) { - for (int i = 0; i < getChildCount(); i++) { - View child = getChildAt(i); - if (!child.isShown() || !child.isClickable()) { - continue; - } - int childCenterX = child.getLeft() + child.getWidth() / 2; - int childCenterY = child.getTop() + child.getHeight() / 2; - mTempDelegateBounds.set( - childCenterX - mIconTouchSize / 2f, - childCenterY - mIconTouchSize / 2f, - childCenterX + mIconTouchSize / 2f, - childCenterY + mIconTouchSize / 2f); - mDelegateTargeted = mTempDelegateBounds.contains(x, y); - if (mDelegateTargeted) { - mDelegateView = child; - mDelegateSlopBounds.set(mTempDelegateBounds); - mDelegateSlopBounds.inset(-mTouchSlop, -mTouchSlop); - break; - } + View delegateView = findDelegateView(x, y); + if (delegateView != null) { + mDelegateTargeted = true; + mDelegateView = delegateView; + mDelegateSlopBounds.set(mTempDelegateBounds); + mDelegateSlopBounds.inset(-mTouchSlop, -mTouchSlop); } } @@ -210,6 +204,60 @@ public class TaskbarView extends LinearLayout { return handled; } + /** + * Return an item whose touch bounds contain the given coordinates, + * or null if no such item exists. + * + * Also sets {@link #mTempDelegateBounds} to be the touch bounds of the chosen delegate view. + */ + private @Nullable View findDelegateView(float x, float y) { + for (int i = 0; i < getChildCount(); i++) { + View child = getChildAt(i); + if (!child.isShown() || !child.isClickable()) { + continue; + } + int childCenterX = child.getLeft() + child.getWidth() / 2; + int childCenterY = child.getTop() + child.getHeight() / 2; + mTempDelegateBounds.set( + childCenterX - mIconTouchSize / 2f, + childCenterY - mIconTouchSize / 2f, + childCenterX + mIconTouchSize / 2f, + childCenterY + mIconTouchSize / 2f); + if (mTempDelegateBounds.contains(x, y)) { + return child; + } + } + return null; + } + + /** + * Returns whether the given MotionEvent, *in screen coorindates*, is within any Taskbar item's + * touch bounds. + */ + public boolean isEventOverAnyItem(MotionEvent ev) { + getLocationOnScreen(mTempOutLocation); + float xInOurCoordinates = ev.getX() - mTempOutLocation[0]; + float yInOurCoorindates = ev.getY() - mTempOutLocation[1]; + return findDelegateView(xInOurCoordinates, yInOurCoorindates) != null; + } + + @Override + public boolean onDragEvent(DragEvent event) { + switch (event.getAction()) { + case DragEvent.ACTION_DRAG_STARTED: + mIsDraggingItem = true; + return true; + case DragEvent.ACTION_DRAG_ENDED: + mIsDraggingItem = false; + break; + } + return super.onDragEvent(event); + } + + public boolean isDraggingItem() { + return mIsDraggingItem; + } + private View inflate(@LayoutRes int layoutResId) { return LayoutInflater.from(getContext()).inflate(layoutResId, this, false); } diff --git a/quickstep/src/com/android/quickstep/BaseActivityInterface.java b/quickstep/src/com/android/quickstep/BaseActivityInterface.java index 5f6e59fa11..ce14197c2e 100644 --- a/quickstep/src/com/android/quickstep/BaseActivityInterface.java +++ b/quickstep/src/com/android/quickstep/BaseActivityInterface.java @@ -153,6 +153,13 @@ public abstract class BaseActivityInterface Date: Tue, 2 Feb 2021 01:19:18 -0800 Subject: [PATCH 0453/1664] Fix issue of slice icon being rendered on non-UIthread Bug: 178989579 Test: Manual Change-Id: I23aa5055c8f07ee9877c1f3ce51cb280ac38a2b7 --- .../android/launcher3/search/SearchResultIconSlice.java | 3 ++- src/com/android/launcher3/BubbleTextView.java | 8 ++++++++ 2 files changed, 10 insertions(+), 1 deletion(-) diff --git a/quickstep/src/com/android/launcher3/search/SearchResultIconSlice.java b/quickstep/src/com/android/launcher3/search/SearchResultIconSlice.java index 0ea2f8b86e..c441e2291b 100644 --- a/quickstep/src/com/android/launcher3/search/SearchResultIconSlice.java +++ b/quickstep/src/com/android/launcher3/search/SearchResultIconSlice.java @@ -15,6 +15,7 @@ */ package com.android.launcher3.search; +import static com.android.launcher3.util.Executors.MAIN_EXECUTOR; import static com.android.launcher3.util.Executors.MODEL_EXECUTOR; import android.app.search.SearchTarget; @@ -104,7 +105,7 @@ public class SearchResultIconSlice extends LinearLayout implements SearchTargetH PackageItemInfo pkgItem = new PackageItemInfo(parentTarget.getPackageName()); pkgItem.user = parentTarget.getUserHandle(); appState.getIconCache().getTitleAndIconForApp(pkgItem, false); - mIcon.applyFromItemInfoWithIcon(pkgItem); + MAIN_EXECUTOR.post(() -> mIcon.applyFromItemInfoWithIcon(pkgItem)); }); } } diff --git a/src/com/android/launcher3/BubbleTextView.java b/src/com/android/launcher3/BubbleTextView.java index 140340af62..5007ffc7be 100644 --- a/src/com/android/launcher3/BubbleTextView.java +++ b/src/com/android/launcher3/BubbleTextView.java @@ -51,6 +51,7 @@ import android.view.ViewDebug; import android.widget.TextView; import androidx.annotation.Nullable; +import androidx.annotation.UiThread; import androidx.core.graphics.ColorUtils; import com.android.launcher3.Launcher.OnResumeCallback; @@ -271,6 +272,7 @@ public class BubbleTextView extends TextView implements ItemInfoUpdateReceiver, mDotScaleAnim.start(); } + @UiThread public void applyFromWorkspaceItem(WorkspaceItemInfo info) { applyFromWorkspaceItem(info, false); } @@ -287,6 +289,7 @@ public class BubbleTextView extends TextView implements ItemInfoUpdateReceiver, } } + @UiThread public void applyFromWorkspaceItem(WorkspaceItemInfo info, boolean promiseStateChanged) { applyIconAndLabel(info); setTag(info); @@ -295,6 +298,7 @@ public class BubbleTextView extends TextView implements ItemInfoUpdateReceiver, setDownloadStateContentDescription(info, info.getProgressLevel()); } + @UiThread public void applyFromApplicationInfo(AppInfo info) { applyIconAndLabel(info); @@ -314,6 +318,7 @@ public class BubbleTextView extends TextView implements ItemInfoUpdateReceiver, /** * Apply label and tag using a generic {@link ItemInfoWithIcon} */ + @UiThread public void applyFromItemInfoWithIcon(ItemInfoWithIcon info) { applyIconAndLabel(info); // We don't need to check the info since it's not a WorkspaceItemInfo @@ -328,11 +333,13 @@ public class BubbleTextView extends TextView implements ItemInfoUpdateReceiver, /** * Apply label and tag using a {@link SearchActionItemInfo} */ + @UiThread public void applyFromSearchActionItemInfo(SearchActionItemInfo searchActionItemInfo) { applyIconAndLabel(searchActionItemInfo); setTag(searchActionItemInfo); } + @UiThread protected void applyIconAndLabel(ItemInfoWithIcon info) { FastBitmapDrawable iconDrawable = newIcon(getContext(), info); mDotParams.color = IconPalette.getMutedColor(info.bitmap.color, 0.54f); @@ -341,6 +348,7 @@ public class BubbleTextView extends TextView implements ItemInfoUpdateReceiver, applyLabel(info); } + @UiThread private void applyLabel(ItemInfoWithIcon info) { setText(info.title); if (info.contentDescription != null) { -- GitLab From 035bb22298b0e8705cf1a67017a923a2c98bafc6 Mon Sep 17 00:00:00 2001 From: Tracy Zhou Date: Mon, 1 Feb 2021 20:34:27 -0800 Subject: [PATCH 0454/1664] [Live Tile] Fix a bunch of issues related to touch input Here are the issues - Touch down again during transition of quick switch brings back the previous app - During transition to overview, touch down on task / in the background area results in weird behaviors These issues related to touch input are caused by the z-ordering of app and launcher. Because in the current implementation we place launcher on top upon initial swipe up, during the transition (to overview / quick switch), any touch event goes directly to launcher instead of the overview input consumer (where it normally goes to in the non-live tile case), resulting in these weird behaviors mentioned above. The reason we put app below launcher upon swipe up is to render task view icon earlier (before swipe up settles into overview), which, is not as critical (we don't have the feature now), and we can find other ways to make that happen. So here are the proposed changes - Place app on top upon swipe up. Only place launcher on top after transition to overview. - Remove the temporary fix to disable click for live tile task during transition. - Remove icon logic in live tile overlay (in this change I only removed the call, but let me know if it's preferred that they are completely removed from LiveTileOverlay in this change) Test: manual Fixes: 178640174 Fixes: 175039524 Change-Id: Ib97804c8bbc487bc55160719375c0f2ff3ba7fd3 --- .../src/com/android/quickstep/AbsSwipeUpHandler.java | 10 ---------- .../com/android/quickstep/views/LiveTileOverlay.java | 5 +++++ .../src/com/android/quickstep/views/TaskView.java | 4 ---- 3 files changed, 5 insertions(+), 14 deletions(-) diff --git a/quickstep/src/com/android/quickstep/AbsSwipeUpHandler.java b/quickstep/src/com/android/quickstep/AbsSwipeUpHandler.java index 6bcc4bfdce..36b51cdf1c 100644 --- a/quickstep/src/com/android/quickstep/AbsSwipeUpHandler.java +++ b/quickstep/src/com/android/quickstep/AbsSwipeUpHandler.java @@ -731,7 +731,6 @@ public abstract class AbsSwipeUpHandler, Q extends setIsLikelyToStartNewTask(isLikelyToStartNewTask, false /* animate */); mStateCallback.setStateOnUiThread(STATE_GESTURE_STARTED); mGestureStarted = true; - mTaskViewSimulator.setDrawsBelowRecents(true); } /** @@ -958,7 +957,6 @@ public abstract class AbsSwipeUpHandler, Q extends if (endTarget == HOME) { duration = Math.max(MIN_OVERSHOOT_DURATION, duration); } else if (endTarget == RECENTS) { - LiveTileOverlay.INSTANCE.startIconAnimation(); if (mRecentsView != null) { int nearestPage = mRecentsView.getPageNearestToCenterOfScreen(); if (mRecentsView.getNextPage() != nearestPage) { @@ -971,9 +969,6 @@ public abstract class AbsSwipeUpHandler, Q extends } duration = Math.max(duration, mRecentsView.getScroller().getDuration()); } - if (ENABLE_QUICKSTEP_LIVE_TILE.get()) { - mRecentsView.getRunningTaskView().setIsClickableAsLiveTile(false); - } } // Let RecentsView handle the scrolling to the task, which we launch in startNewTask() @@ -1067,7 +1062,6 @@ public abstract class AbsSwipeUpHandler, Q extends } if (mGestureState.getEndTarget() == HOME) { - mTaskViewSimulator.setDrawsBelowRecents(false); getOrientationHandler().adjustFloatingIconStartVelocity(velocityPxPerMs); final RemoteAnimationTargetCompat runningTaskTarget = mRecentsAnimationTargets != null ? mRecentsAnimationTargets.findTask(mGestureState.getRunningTaskId()) @@ -1453,10 +1447,6 @@ public abstract class AbsSwipeUpHandler, Q extends private void finishCurrentTransitionToRecents() { if (ENABLE_QUICKSTEP_LIVE_TILE.get()) { mStateCallback.setStateOnUiThread(STATE_CURRENT_TASK_FINISHED); - final TaskView runningTaskView = mRecentsView.getRunningTaskView(); - if (runningTaskView != null) { - runningTaskView.setIsClickableAsLiveTile(true); - } } else if (!hasTargets() || mRecentsAnimationController == null) { // If there are no targets or the animation not started, then there is nothing to finish mStateCallback.setStateOnUiThread(STATE_CURRENT_TASK_FINISHED); diff --git a/quickstep/src/com/android/quickstep/views/LiveTileOverlay.java b/quickstep/src/com/android/quickstep/views/LiveTileOverlay.java index 747c3f28a1..8210ab0ca8 100644 --- a/quickstep/src/com/android/quickstep/views/LiveTileOverlay.java +++ b/quickstep/src/com/android/quickstep/views/LiveTileOverlay.java @@ -85,6 +85,11 @@ public class LiveTileOverlay extends Drawable { mIcon = icon; } + // TODO: consider cleaning this up and drawing icon in another way. Previously we place app + // below launcher during the initial swipe up and render the icon in this live tile overlay. + // However, this resulted in a bunch of touch input issues caused by Launcher getting the input + // events during transition (to overview / to another app (quick switch). So now our new + // solution places app on top in live tile until it fully settles in Overview. public void startIconAnimation() { if (mIconAnimator != null) { mIconAnimator.cancel(); diff --git a/quickstep/src/com/android/quickstep/views/TaskView.java b/quickstep/src/com/android/quickstep/views/TaskView.java index e8f590f28d..59cf3b2b8b 100644 --- a/quickstep/src/com/android/quickstep/views/TaskView.java +++ b/quickstep/src/com/android/quickstep/views/TaskView.java @@ -356,10 +356,6 @@ public class TaskView extends FrameLayout implements PageCallbacks, Reusable { return false; } - public void setIsClickableAsLiveTile(boolean isClickableAsLiveTile) { - mIsClickableAsLiveTile = isClickableAsLiveTile; - } - private void computeAndSetIconTouchDelegate() { float iconHalfSize = mIconView.getWidth() / 2f; mIconCenterCoords[0] = mIconCenterCoords[1] = iconHalfSize; -- GitLab From 2e292b634366e3a0ec453b5f0e271eac76cb3c8a Mon Sep 17 00:00:00 2001 From: Schneider Victor-tulias Date: Wed, 6 Jan 2021 11:02:39 -0500 Subject: [PATCH 0455/1664] Update PreloadIconDrawable colors when an app is not startable. Updated the progress bar and progress track colors when the app is not startable to increase contrast. Demo: https://drive.google.com/file/d/1iHYBeHgWsItY6edO3NT1RYuXjdKNlEKH/view?usp=sharing Test: manual Bug: 171005849 Change-Id: I97bc4e3299108b1e8ef47b498fb3431293f57ad2 --- .../graphics/PreloadIconDrawable.java | 88 +++++++++++++++---- src/com/android/launcher3/util/Themes.java | 5 ++ 2 files changed, 78 insertions(+), 15 deletions(-) diff --git a/src/com/android/launcher3/graphics/PreloadIconDrawable.java b/src/com/android/launcher3/graphics/PreloadIconDrawable.java index 304d49692e..ce824df3e6 100644 --- a/src/com/android/launcher3/graphics/PreloadIconDrawable.java +++ b/src/com/android/launcher3/graphics/PreloadIconDrawable.java @@ -24,6 +24,7 @@ import android.animation.Animator; import android.animation.AnimatorListenerAdapter; import android.animation.ObjectAnimator; import android.content.Context; +import android.content.res.Configuration; import android.graphics.Bitmap; import android.graphics.Canvas; import android.graphics.Matrix; @@ -34,10 +35,12 @@ import android.graphics.Rect; import android.util.Pair; import android.util.Property; import android.util.SparseArray; +import android.view.ContextThemeWrapper; import com.android.launcher3.FastBitmapDrawable; import com.android.launcher3.anim.Interpolators; import com.android.launcher3.model.data.ItemInfoWithIcon; +import com.android.launcher3.util.Themes; import java.lang.ref.WeakReference; @@ -77,6 +80,9 @@ public class PreloadIconDrawable extends FastBitmapDrawable { private static final SparseArray>> sShadowCache = new SparseArray<>(); + private static final int PRELOAD_ACCENT_COLOR_INDEX = 0; + private static final int PRELOAD_BACKGROUND_COLOR_INDEX = 1; + private final Matrix mTmpMatrix = new Matrix(); private final PathMeasure mPathMeasure = new PathMeasure(); @@ -91,6 +97,9 @@ public class PreloadIconDrawable extends FastBitmapDrawable { private Bitmap mShadowBitmap; private final int mIndicatorColor; + private final int mSystemAccentColor; + private final int mSystemBackgroundColor; + private final boolean mIsDarkMode; private int mTrackAlpha; private float mTrackLength; @@ -104,11 +113,23 @@ public class PreloadIconDrawable extends FastBitmapDrawable { private ObjectAnimator mCurrentAnim; + private boolean mIsStartable; + public PreloadIconDrawable(ItemInfoWithIcon info, Context context) { - this(info, IconPalette.getPreloadProgressColor(context, info.bitmap.color)); + this( + info, + IconPalette.getPreloadProgressColor(context, info.bitmap.color), + getPreloadColors(context), + (context.getResources().getConfiguration().uiMode + & Configuration.UI_MODE_NIGHT_MASK + & Configuration.UI_MODE_NIGHT_YES) != 0) /* isDarkMode */; } - public PreloadIconDrawable(ItemInfoWithIcon info, int indicatorColor) { + public PreloadIconDrawable( + ItemInfoWithIcon info, + int indicatorColor, + int[] preloadColors, + boolean isDarkMode) { super(info.bitmap); mItem = info; mShapePath = getShapePath(); @@ -120,9 +141,12 @@ public class PreloadIconDrawable extends FastBitmapDrawable { mProgressPaint.setStrokeCap(Paint.Cap.ROUND); mIndicatorColor = indicatorColor; - setInternalProgress(0); + mSystemAccentColor = preloadColors[PRELOAD_ACCENT_COLOR_INDEX]; + mSystemBackgroundColor = preloadColors[PRELOAD_BACKGROUND_COLOR_INDEX]; + mIsDarkMode = isDarkMode; - setIsDisabled(!info.isAppStartable()); + setInternalProgress(info.getProgressLevel()); + setIsStartable(info.isAppStartable()); } @Override @@ -148,7 +172,7 @@ public class PreloadIconDrawable extends FastBitmapDrawable { } private Bitmap getShadowBitmap(int width, int height, float shadowRadius) { - int key = (width << 16) | height; + int key = ((width << 16) | height) * (mIsDarkMode ? -1 : 1); WeakReference> shadowRef = sShadowCache.get(key); Pair cache = shadowRef != null ? shadowRef.get() : null; Bitmap shadow = cache != null && cache.first.equals(mShapePath) ? cache.second : null; @@ -157,8 +181,9 @@ public class PreloadIconDrawable extends FastBitmapDrawable { } shadow = Bitmap.createBitmap(width, height, Bitmap.Config.ARGB_8888); Canvas c = new Canvas(shadow); - mProgressPaint.setShadowLayer(shadowRadius, 0, 0, COLOR_SHADOW); - mProgressPaint.setColor(COLOR_TRACK); + mProgressPaint.setShadowLayer(shadowRadius, 0, 0, mIsStartable + ? COLOR_SHADOW : mSystemAccentColor); + mProgressPaint.setColor(mIsStartable ? COLOR_TRACK : mSystemBackgroundColor); mProgressPaint.setAlpha(MAX_PAINT_ALPHA); c.drawPath(mScaledTrackPath, mProgressPaint); mProgressPaint.clearShadowLayer(); @@ -176,7 +201,7 @@ public class PreloadIconDrawable extends FastBitmapDrawable { } // Draw track. - mProgressPaint.setColor(mIndicatorColor); + mProgressPaint.setColor(mIsStartable ? mIndicatorColor : mSystemAccentColor); mProgressPaint.setAlpha(mTrackAlpha); if (mShadowBitmap != null) { canvas.drawBitmap(mShadowBitmap, bounds.left, bounds.top, mProgressPaint); @@ -215,6 +240,14 @@ public class PreloadIconDrawable extends FastBitmapDrawable { return !mRanFinishAnimation; } + /** Sets whether this icon should display the startable app UI. */ + public void setIsStartable(boolean isStartable) { + if (mIsStartable != isStartable) { + mIsStartable = isStartable; + setIsDisabled(!isStartable); + } + } + private void updateInternalState(float finalProgress, boolean shouldAnimate, boolean isFinish) { if (mCurrentAnim != null) { mCurrentAnim.cancel(); @@ -295,6 +328,18 @@ public class PreloadIconDrawable extends FastBitmapDrawable { invalidateSelf(); } + private static int[] getPreloadColors(Context context) { + Context dayNightThemeContext = new ContextThemeWrapper( + context, android.R.style.Theme_DeviceDefault_DayNight); + int[] preloadColors = new int[2]; + + preloadColors[PRELOAD_ACCENT_COLOR_INDEX] = Themes.getColorAccent(dayNightThemeContext); + preloadColors[PRELOAD_BACKGROUND_COLOR_INDEX] = Themes.getColorBackgroundFloating( + dayNightThemeContext); + + return preloadColors; + } + /** * Returns a FastBitmapDrawable with the icon. */ @@ -305,13 +350,21 @@ public class PreloadIconDrawable extends FastBitmapDrawable { @Override public ConstantState getConstantState() { return new PreloadIconConstantState( - mBitmap, mIconColor, !mItem.isAppStartable(), mItem, mIndicatorColor); + mBitmap, + mIconColor, + !mItem.isAppStartable(), + mItem, + mIndicatorColor, + new int[] {mSystemAccentColor, mSystemBackgroundColor}, + mIsDarkMode); } protected static class PreloadIconConstantState extends FastBitmapConstantState { protected final ItemInfoWithIcon mInfo; protected final int mIndicatorColor; + protected final int[] mPreloadColors; + protected final boolean mIsDarkMode; protected final int mLevel; public PreloadIconConstantState( @@ -319,19 +372,24 @@ public class PreloadIconDrawable extends FastBitmapDrawable { int iconColor, boolean isDisabled, ItemInfoWithIcon info, - int indicatorcolor) { + int indicatorColor, + int[] preloadColors, + boolean isDarkMode) { super(bitmap, iconColor, isDisabled); mInfo = info; - mIndicatorColor = indicatorcolor; + mIndicatorColor = indicatorColor; + mPreloadColors = preloadColors; + mIsDarkMode = isDarkMode; mLevel = info.getProgressLevel(); } @Override public PreloadIconDrawable newDrawable() { - PreloadIconDrawable drawable = new PreloadIconDrawable(mInfo, mIndicatorColor); - drawable.setLevel(mLevel); - drawable.setIsDisabled(mIsDisabled); - return drawable; + return new PreloadIconDrawable( + mInfo, + mIndicatorColor, + mPreloadColors, + mIsDarkMode); } @Override diff --git a/src/com/android/launcher3/util/Themes.java b/src/com/android/launcher3/util/Themes.java index b74686fbd3..55d17fc9c0 100644 --- a/src/com/android/launcher3/util/Themes.java +++ b/src/com/android/launcher3/util/Themes.java @@ -81,6 +81,11 @@ public class Themes { return getAttrColor(context, android.R.attr.colorAccent); } + /** Returns the floating background color attribute. */ + public static int getColorBackgroundFloating(Context context) { + return getAttrColor(context, android.R.attr.colorBackgroundFloating); + } + public static int getAttrColor(Context context, int attr) { TypedArray ta = context.obtainStyledAttributes(new int[]{attr}); int colorAccent = ta.getColor(0, 0); -- GitLab From 50165042edc5954bc71c3e66b60cb87748784f8f Mon Sep 17 00:00:00 2001 From: Hyunyoung Song Date: Fri, 29 Jan 2021 23:35:22 -0800 Subject: [PATCH 0456/1664] Logs stack trace when IME doesn't get attached when all apps swipes up Bug: 178904132 Test: Manual Change-Id: I3b62a1a8e927ca8bac4db07053f781260fbff515 --- .../allapps/AllAppsInsetTransitionController.java | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/src/com/android/launcher3/allapps/AllAppsInsetTransitionController.java b/src/com/android/launcher3/allapps/AllAppsInsetTransitionController.java index 1cf98e1c9b..f6e54aaec4 100644 --- a/src/com/android/launcher3/allapps/AllAppsInsetTransitionController.java +++ b/src/com/android/launcher3/allapps/AllAppsInsetTransitionController.java @@ -164,8 +164,11 @@ public class AllAppsInsetTransitionController { @Override public void onCancelled(@Nullable WindowInsetsAnimationController controller) { if (DEBUG) { - Log.d(TAG, "Listener.onCancelled ctrl=" + controller - + " mAnimationController=" + mAnimationController); + // Keep the verbose logging to chase down IME not showing up issue. + // b/178904132 + Log.e(TAG, "Listener.onCancelled ctrl=" + controller + + " mAnimationController=" + mAnimationController, + new Exception()); } if (mState == State.DRAG_START_BOTTOM) { mState = State.DRAG_START_BOTTOM_IME_CANCELLED; -- GitLab From 7239df725803e965d6e5ee6c6136e4904e939d83 Mon Sep 17 00:00:00 2001 From: Hyunyoung Song Date: Mon, 1 Feb 2021 21:18:15 -0800 Subject: [PATCH 0457/1664] Support shorter height row layout / Disable ICON_SLICE Bug: 178815297 Bug: 178128837 Bug: 178823469 Test: manual, attached screenshot on the bugreport Change-Id: Ie69c8928f5acc430320cfc5c85547195866e829e --- .../res/layout/search_result_icon_row.xml | 8 +- .../layout/search_result_small_icon_row.xml | 73 ++++++++ .../search/DeviceSearchAdapterProvider.java | 19 +- .../launcher3/search/SearchAdapterItem.java | 24 +-- .../launcher3/search/SearchResultIconRow.java | 14 +- .../search/SearchResultSmallIconRow.java | 142 ++++++++++++++ .../launcher3/search/SearchTargetUtil.java | 36 ++-- .../launcher3/config/FeatureFlags.java | 6 +- .../systemui/plugins/AllAppsSearchPlugin.java | 105 ----------- .../shared/SearchTargetEventLegacy.java | 95 ---------- .../plugins/shared/SearchTargetLegacy.java | 175 ------------------ 11 files changed, 279 insertions(+), 418 deletions(-) create mode 100644 quickstep/res/layout/search_result_small_icon_row.xml create mode 100644 quickstep/src/com/android/launcher3/search/SearchResultSmallIconRow.java delete mode 100644 src_plugins/com/android/systemui/plugins/AllAppsSearchPlugin.java delete mode 100644 src_plugins/com/android/systemui/plugins/shared/SearchTargetEventLegacy.java delete mode 100644 src_plugins/com/android/systemui/plugins/shared/SearchTargetLegacy.java diff --git a/quickstep/res/layout/search_result_icon_row.xml b/quickstep/res/layout/search_result_icon_row.xml index 81190cfc56..084920a8e6 100644 --- a/quickstep/res/layout/search_result_icon_row.xml +++ b/quickstep/res/layout/search_result_icon_row.xml @@ -20,10 +20,10 @@ android:padding="@dimen/dynamic_grid_edge_margin"> + android:layout_width="wrap_content" + android:layout_height="wrap_content" + launcher:iconDisplay="hero_app" /> + + + + + + + + + + + + \ No newline at end of file diff --git a/quickstep/src/com/android/launcher3/search/DeviceSearchAdapterProvider.java b/quickstep/src/com/android/launcher3/search/DeviceSearchAdapterProvider.java index acf6c150cf..4a656c1acb 100644 --- a/quickstep/src/com/android/launcher3/search/DeviceSearchAdapterProvider.java +++ b/quickstep/src/com/android/launcher3/search/DeviceSearchAdapterProvider.java @@ -16,6 +16,7 @@ package com.android.launcher3.search; +import static com.android.launcher3.allapps.AllAppsGridAdapter.VIEW_TYPE_ALL_APPS_DIVIDER; import static com.android.launcher3.allapps.AllAppsGridAdapter.VIEW_TYPE_ICON; import android.app.search.SearchTarget; @@ -31,6 +32,7 @@ import com.android.launcher3.R; import com.android.launcher3.allapps.AllAppsContainerView; import com.android.launcher3.allapps.AllAppsGridAdapter; import com.android.launcher3.allapps.search.SearchAdapterProvider; +import com.android.launcher3.config.FeatureFlags; /** * Provides views for on-device search results @@ -41,11 +43,12 @@ public class DeviceSearchAdapterProvider extends SearchAdapterProvider { public static final int VIEW_TYPE_SEARCH_SLICE = 1 << 7; public static final int VIEW_TYPE_SEARCH_ICON = (1 << 8) | VIEW_TYPE_ICON; public static final int VIEW_TYPE_SEARCH_ICON_ROW = (1 << 9); + public static final int VIEW_TYPE_SEARCH_SMALL_ICON_ROW = (1 << 10); public static final int VIEW_TYPE_SEARCH_THUMBNAIL = 1 << 12; public static final int VIEW_TYPE_SEARCH_WIDGET_LIVE = 1 << 15; public static final int VIEW_TYPE_SEARCH_WIDGET_PREVIEW = 1 << 16; - private static final String TAG = "SearchServiceAdapterProvider"; + private static final String TAG = "SearchServiceAdapter"; private final AllAppsContainerView mAppsView; private final SparseIntArray mViewTypeToLayoutMap = new SparseIntArray(); @@ -57,11 +60,13 @@ public class DeviceSearchAdapterProvider extends SearchAdapterProvider { mViewTypeToLayoutMap.put(VIEW_TYPE_SEARCH_CORPUS_TITLE, R.layout.search_section_title); mViewTypeToLayoutMap.put(VIEW_TYPE_SEARCH_ICON, R.layout.search_result_icon); mViewTypeToLayoutMap.put(VIEW_TYPE_SEARCH_ICON_ROW, R.layout.search_result_icon_row); + mViewTypeToLayoutMap.put(VIEW_TYPE_SEARCH_SMALL_ICON_ROW, R.layout.search_result_small_icon_row); mViewTypeToLayoutMap.put(VIEW_TYPE_SEARCH_SLICE, R.layout.search_result_slice); mViewTypeToLayoutMap.put(VIEW_TYPE_SEARCH_THUMBNAIL, R.layout.search_result_thumbnail); mViewTypeToLayoutMap.put(VIEW_TYPE_SEARCH_WIDGET_LIVE, R.layout.search_result_widget_live); mViewTypeToLayoutMap.put(VIEW_TYPE_SEARCH_WIDGET_PREVIEW, R.layout.search_result_widget_preview); + mViewTypeToLayoutMap.put(VIEW_TYPE_ALL_APPS_DIVIDER, R.layout.all_apps_divider); } @Override @@ -116,25 +121,33 @@ public class DeviceSearchAdapterProvider extends SearchAdapterProvider { case LayoutType.ICON_SINGLE_VERTICAL_TEXT: return VIEW_TYPE_SEARCH_ICON; case LayoutType.ICON_SLICE: + if (FeatureFlags.DISABLE_SLICE_IN_ALLAPPS.get()) { + return -1; + } if (t.getSliceUri() != null) { return VIEW_TYPE_SEARCH_SLICE; } - Log.w(TAG, "Dropping as LayoutType.ICON_SLICE target doesn't contain sliceUri."); + Log.w(TAG, "LayoutType.ICON_SLICE target doesn't contain sliceUri."); break; case LayoutType.ICON_DOUBLE_HORIZONTAL_TEXT: case LayoutType.ICON_SINGLE_HORIZONTAL_TEXT: case LayoutType.ICON_DOUBLE_HORIZONTAL_TEXT_BUTTON: + case LayoutType.ICON_HORIZONTAL_TEXT: return VIEW_TYPE_SEARCH_ICON_ROW; + case LayoutType.SMALL_ICON_HORIZONTAL_TEXT: + return VIEW_TYPE_SEARCH_SMALL_ICON_ROW; case LayoutType.THUMBNAIL: if (t.getSearchAction() != null) { return VIEW_TYPE_SEARCH_THUMBNAIL; } - Log.w(TAG, "Dropping as LayoutType.THUMBNAIL target doesn't contain searchAction."); + Log.w(TAG, "LayoutType.THUMBNAIL target doesn't contain searchAction."); break; case LayoutType.WIDGET_PREVIEW: return VIEW_TYPE_SEARCH_WIDGET_PREVIEW; case LayoutType.WIDGET_LIVE: return VIEW_TYPE_SEARCH_WIDGET_LIVE; + case LayoutType.DIVIDER: + return VIEW_TYPE_ALL_APPS_DIVIDER; } return -1; diff --git a/quickstep/src/com/android/launcher3/search/SearchAdapterItem.java b/quickstep/src/com/android/launcher3/search/SearchAdapterItem.java index b402f61979..8983c4f3b8 100644 --- a/quickstep/src/com/android/launcher3/search/SearchAdapterItem.java +++ b/quickstep/src/com/android/launcher3/search/SearchAdapterItem.java @@ -18,6 +18,7 @@ package com.android.launcher3.search; import static com.android.launcher3.search.DeviceSearchAdapterProvider.VIEW_TYPE_SEARCH_ICON; import static com.android.launcher3.search.DeviceSearchAdapterProvider.VIEW_TYPE_SEARCH_ICON_ROW; +import static com.android.launcher3.search.DeviceSearchAdapterProvider.VIEW_TYPE_SEARCH_SMALL_ICON_ROW; import static com.android.launcher3.search.DeviceSearchAdapterProvider.VIEW_TYPE_SEARCH_SLICE; import static com.android.launcher3.search.DeviceSearchAdapterProvider.VIEW_TYPE_SEARCH_THUMBNAIL; import static com.android.launcher3.search.DeviceSearchAdapterProvider.VIEW_TYPE_SEARCH_WIDGET_LIVE; @@ -26,7 +27,6 @@ import static com.android.launcher3.search.DeviceSearchAdapterProvider.VIEW_TYPE import android.app.search.SearchTarget; import com.android.launcher3.allapps.AllAppsGridAdapter; -import com.android.systemui.plugins.shared.SearchTargetLegacy; import java.util.ArrayList; import java.util.List; @@ -35,31 +35,23 @@ import java.util.List; * Extension of AdapterItem that contains an extra payload specific to item */ public class SearchAdapterItem extends AllAppsGridAdapter.AdapterItem { - private SearchTargetLegacy mSearchTargetLegacy; private SearchTarget mSearchTarget; private List mInlineItems = new ArrayList<>(); - private static final int AVAILABLE_FOR_ACCESSIBILITY = - VIEW_TYPE_SEARCH_SLICE | VIEW_TYPE_SEARCH_THUMBNAIL | VIEW_TYPE_SEARCH_ICON_ROW - | VIEW_TYPE_SEARCH_ICON | VIEW_TYPE_SEARCH_WIDGET_PREVIEW - | VIEW_TYPE_SEARCH_WIDGET_LIVE; - - - public SearchAdapterItem(SearchTargetLegacy searchTargetLegacy, int type) { - mSearchTargetLegacy = searchTargetLegacy; - viewType = type; - } + private static final int AVAILABLE_FOR_ACCESSIBILITY = VIEW_TYPE_SEARCH_SLICE + | VIEW_TYPE_SEARCH_THUMBNAIL + | VIEW_TYPE_SEARCH_ICON_ROW + | VIEW_TYPE_SEARCH_ICON + | VIEW_TYPE_SEARCH_SMALL_ICON_ROW + | VIEW_TYPE_SEARCH_WIDGET_PREVIEW + | VIEW_TYPE_SEARCH_WIDGET_LIVE; public SearchAdapterItem(SearchTarget searchTarget, int type) { mSearchTarget = searchTarget; viewType = type; } - public SearchTargetLegacy getSearchTargetLegacy() { - return mSearchTargetLegacy; - } - public SearchTarget getSearchTarget() { return mSearchTarget; } diff --git a/quickstep/src/com/android/launcher3/search/SearchResultIconRow.java b/quickstep/src/com/android/launcher3/search/SearchResultIconRow.java index 7c3ed69a00..12a1a1cd34 100644 --- a/quickstep/src/com/android/launcher3/search/SearchResultIconRow.java +++ b/quickstep/src/com/android/launcher3/search/SearchResultIconRow.java @@ -46,12 +46,12 @@ public class SearchResultIconRow extends LinearLayout implements SearchTargetHan public static final int MAX_INLINE_ITEMS = 3; protected final Launcher mLauncher; - private final LauncherAppState mLauncherAppState; - protected SearchResultIcon mResultIcon; + protected final SearchResultIcon[] mInlineIcons = new SearchResultIcon[MAX_INLINE_ITEMS]; + private SearchResultIcon mResultIcon; + private final LauncherAppState mLauncherAppState; private TextView mTitleView; private TextView mSubTitleView; - protected final SearchResultIcon[] mInlineIcons = new SearchResultIcon[MAX_INLINE_ITEMS]; private PackageItemInfo mProviderInfo; @@ -77,13 +77,14 @@ public class SearchResultIconRow extends LinearLayout implements SearchTargetHan @Override protected void onFinishInflate() { super.onFinishInflate(); - int iconSize = getIconSize(); mResultIcon = findViewById(R.id.icon); + mTitleView = findViewById(R.id.title); mSubTitleView = findViewById(R.id.subtitle); mSubTitleView.setVisibility(GONE); + mResultIcon.getLayoutParams().height = iconSize; mResultIcon.getLayoutParams().width = iconSize; mResultIcon.setTextVisibility(false); @@ -94,15 +95,16 @@ public class SearchResultIconRow extends LinearLayout implements SearchTargetHan for (SearchResultIcon inlineIcon : mInlineIcons) { inlineIcon.getLayoutParams().width = getIconSize(); } - setOnClickListener(mResultIcon); setOnLongClickListener(mResultIcon); } @Override public void apply(SearchTarget parentTarget, List children) { - showSubtitleIfNeeded(null); mResultIcon.apply(parentTarget, children, this::onItemInfoCreated); + + showSubtitleIfNeeded(null); + if (parentTarget.getShortcutInfo() != null) { updateWithShortcutInfo(parentTarget.getShortcutInfo()); } else if (parentTarget.getSearchAction() != null) { diff --git a/quickstep/src/com/android/launcher3/search/SearchResultSmallIconRow.java b/quickstep/src/com/android/launcher3/search/SearchResultSmallIconRow.java new file mode 100644 index 0000000000..ca8aa81ecb --- /dev/null +++ b/quickstep/src/com/android/launcher3/search/SearchResultSmallIconRow.java @@ -0,0 +1,142 @@ +/* + * Copyright (C) 2020 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.android.launcher3.search; + +import static com.android.launcher3.util.Executors.MAIN_EXECUTOR; +import static com.android.launcher3.util.Executors.MODEL_EXECUTOR; + +import android.app.search.SearchTarget; +import android.content.Context; +import android.content.pm.ShortcutInfo; +import android.text.TextUtils; +import android.util.AttributeSet; +import android.widget.LinearLayout; +import android.widget.TextView; + +import androidx.annotation.Nullable; + +import com.android.launcher3.Launcher; +import com.android.launcher3.LauncherAppState; +import com.android.launcher3.R; +import com.android.launcher3.model.data.ItemInfoWithIcon; +import com.android.launcher3.model.data.PackageItemInfo; + +import java.util.List; + +/** + * A full width representation of {@link SearchResultIcon} with a secondary label and inline + * SearchTargets + */ +public class SearchResultSmallIconRow extends LinearLayout implements SearchTargetHandler { + + protected final Launcher mLauncher; + private final LauncherAppState mLauncherAppState; + protected SearchResultIcon mResultIcon; + + private TextView mTitleView; + private TextView mDelimeterView; + private TextView mSubTitleView; + + private PackageItemInfo mProviderInfo; + + public SearchResultSmallIconRow(Context context) { + this(context, null, 0); + } + + public SearchResultSmallIconRow(Context context, + @Nullable AttributeSet attrs) { + this(context, attrs, 0); + } + + public SearchResultSmallIconRow(Context context, + @Nullable AttributeSet attrs, int defStyleAttr) { + super(context, attrs, defStyleAttr); + mLauncher = Launcher.getLauncher(getContext()); + mLauncherAppState = LauncherAppState.getInstance(getContext()); + } + + protected int getIconSize() { + return mLauncher.getDeviceProfile().allAppsIconSizePx; + } + + @Override + protected void onFinishInflate() { + super.onFinishInflate(); + int iconSize = getIconSize(); + + mResultIcon = findViewById(R.id.icon); + + mTitleView = findViewById(R.id.title); + mDelimeterView = findViewById(R.id.delimeter); + mDelimeterView.setVisibility(GONE); + mSubTitleView = findViewById(R.id.subtitle); + mSubTitleView.setVisibility(GONE); + + mResultIcon.getLayoutParams().height = iconSize; + mResultIcon.getLayoutParams().width = iconSize; + mResultIcon.setTextVisibility(false); + + setOnClickListener(mResultIcon); + setOnLongClickListener(mResultIcon); + } + + @Override + public void apply(SearchTarget parentTarget, List children) { + mResultIcon.apply(parentTarget, children, this::onItemInfoCreated); + + showSubtitleIfNeeded(null); + + if (parentTarget.getShortcutInfo() != null) { + updateWithShortcutInfo(parentTarget.getShortcutInfo()); + } else if (parentTarget.getSearchAction() != null) { + showSubtitleIfNeeded(parentTarget.getSearchAction().getSubtitle()); + } + } + + @Override + public boolean quickSelect() { + this.performClick(); + return true; + } + + private void updateWithShortcutInfo(ShortcutInfo shortcutInfo) { + PackageItemInfo packageItemInfo = new PackageItemInfo(shortcutInfo.getPackage()); + if (packageItemInfo.equals(mProviderInfo)) return; + MODEL_EXECUTOR.post(() -> { + mLauncherAppState.getIconCache().getTitleAndIconForApp(packageItemInfo, true); + MAIN_EXECUTOR.post(() -> { + showSubtitleIfNeeded(packageItemInfo.title); + mProviderInfo = packageItemInfo; + }); + }); + } + + protected void showSubtitleIfNeeded(CharSequence subTitle) { + if (!TextUtils.isEmpty(subTitle)) { + mSubTitleView.setText(subTitle); + mSubTitleView.setVisibility(VISIBLE); + mDelimeterView.setVisibility(VISIBLE); + + } else { + mSubTitleView.setVisibility(GONE); + } + } + + protected void onItemInfoCreated(ItemInfoWithIcon info) { + setTag(info); + mTitleView.setText(info.title); + } +} diff --git a/quickstep/src/com/android/launcher3/search/SearchTargetUtil.java b/quickstep/src/com/android/launcher3/search/SearchTargetUtil.java index 0abed036e3..ede3b9d143 100644 --- a/quickstep/src/com/android/launcher3/search/SearchTargetUtil.java +++ b/quickstep/src/com/android/launcher3/search/SearchTargetUtil.java @@ -13,10 +13,12 @@ * See the License for the specific language governing permissions and * limitations under the License. */ + package com.android.launcher3.search; -import static com.android.app.search.LayoutType.ICON_DOUBLE_HORIZONTAL_TEXT; -import static com.android.app.search.LayoutType.ICON_SINGLE_HORIZONTAL_TEXT; +import static com.android.app.search.LayoutType.DIVIDER; +import static com.android.app.search.LayoutType.ICON_HORIZONTAL_TEXT; +import static com.android.app.search.LayoutType.SMALL_ICON_HORIZONTAL_TEXT; import static com.android.app.search.LayoutType.THUMBNAIL; import static com.android.app.search.ResultType.ACTION; import static com.android.app.search.ResultType.SCREENSHOT; @@ -54,17 +56,17 @@ public class SearchTargetUtil { /** - * Generate SearchTargetUtil for ICON_DOUBLE_HORIZONTAL_TEXT layout type. + * Generate SearchTargetUtil for ICON_HORIZONTAL_TEXT layout type. * * targets.add(SearchTargetUtil.generateIconDoubleHorizontalText_SearchAction( * mContext, "red", Color.RED)); * targets.add(SearchTargetUtil.generateIconDoubleHorizontalText_SearchAction( * mContext, "yellow", Color.YELLOW)); */ - public static SearchTarget generateIconDoubleHorizontalText_SearchAction( + public static SearchTarget generateIcoHorizontalText_usingSearchAction( Context context, String id, int color) { SearchTarget.Builder builder = - new SearchTarget.Builder(ACTION, ICON_DOUBLE_HORIZONTAL_TEXT, id) + new SearchTarget.Builder(ACTION, ICON_HORIZONTAL_TEXT, id) .setPackageName(PACKAGE2) /* required */ .setUserHandle(USERHANDLE); /* required */ @@ -102,7 +104,7 @@ public class SearchTargetUtil { * targets.add(SearchTargetUtil.generateThumbnail_SearchAction("red", Color.RED)); * targets.add(SearchTargetUtil.generateThumbnail_SearchAction("green", Color.GREEN)); */ - public static SearchTarget generateThumbnail_SearchAction(String id, int color) { + public static SearchTarget generateThumbnail_usingSearchAction(String id, int color) { SearchTarget.Builder builder = new SearchTarget.Builder(SCREENSHOT, THUMBNAIL, id) .setPackageName(PACKAGE2) /* required */ @@ -130,16 +132,19 @@ public class SearchTargetUtil { } /** + * Generate SearchTargetUtil for SMALL_ICON_HORIZONTAL_TEXT layout type. + * * targets.add(SearchTargetUtil.generateIconHorizontalText_SearchAction( * mContext, "red", Color.RED)); * targets.add(SearchTargetUtil.generateIconHorizontalText_SearchAction( * mContext, "yellow", Color.YELLOW)); */ - public static SearchTarget generateIconHorizontalText_SearchAction( + public static SearchTarget generateSmallIconHorizontalText_usingSearchAction( Context context, String id, int color) { - String fallbackQuery = "How to make cookie"; + String title = "Ask the assistant"; + String fallbackQuery = "sourdough bread"; SearchTarget.Builder builder = - new SearchTarget.Builder(SUGGEST, ICON_SINGLE_HORIZONTAL_TEXT, id) + new SearchTarget.Builder(SUGGEST, SMALL_ICON_HORIZONTAL_TEXT, id) .setPackageName(PACKAGE2) /* required */ .setUserHandle(USERHANDLE); /* required */ @@ -159,7 +164,8 @@ public class SearchTargetUtil { Bundle extra = new Bundle(); extra.putBoolean(BUNDLE_EXTRA_SHOULD_START_FOR_RESULT, true); - SearchAction searchAction = new SearchAction.Builder(id, fallbackQuery) + SearchAction searchAction = new SearchAction.Builder(id, title) + .setSubtitle(fallbackQuery) .setPendingIntent(pendingIntent3) .setIcon(icon) .setExtras(extra) @@ -167,6 +173,14 @@ public class SearchTargetUtil { return builder.setSearchAction(searchAction).build(); } + public static SearchTarget generateDivider() { + SearchTarget.Builder builder = + new SearchTarget.Builder(SUGGEST, DIVIDER, "divider") + .setPackageName("") /* required but not used*/ + .setUserHandle(USERHANDLE); /* required */ + return builder.build(); + } + /** * Generate SearchTargetUtil for ICON_DOUBLE_HORIZONTAL_TEXT layout type. @@ -174,7 +188,7 @@ public class SearchTargetUtil { public static SearchTarget generateIconDoubleHorizontalText_ShortcutInfo(Context context) { String id = "23456"; SearchTarget.Builder builder = - new SearchTarget.Builder(ResultType.SHORTCUT, ICON_DOUBLE_HORIZONTAL_TEXT, id) + new SearchTarget.Builder(ResultType.SHORTCUT, SMALL_ICON_HORIZONTAL_TEXT, id) .setPackageName("com.google.android.gm") /* required */ .setUserHandle(UserHandle.CURRENT); /* required */ diff --git a/src/com/android/launcher3/config/FeatureFlags.java b/src/com/android/launcher3/config/FeatureFlags.java index 086d665fcb..b61799f79c 100644 --- a/src/com/android/launcher3/config/FeatureFlags.java +++ b/src/com/android/launcher3/config/FeatureFlags.java @@ -98,12 +98,12 @@ public final class FeatureFlags { public static final BooleanFlag ENABLE_DEVICE_SEARCH = new DeviceFlag( "ENABLE_DEVICE_SEARCH", false, "Allows on device search in all apps"); - public static final BooleanFlag USE_SEARCH_API = getDebugFlag( - "USE_SEARCH_API", true, "Use SearchUIManager api for device search"); - public static final BooleanFlag DISABLE_INITIAL_IME_IN_ALLAPPS = getDebugFlag( "DISABLE_INITIAL_IME_IN_ALLAPPS", false, "Disable default IME state in all apps"); + public static final BooleanFlag DISABLE_SLICE_IN_ALLAPPS = getDebugFlag( + "DISABLE_SLICE_IN_ALLAPPS", true, "Disable slice in all apps"); + public static final BooleanFlag FOLDER_NAME_SUGGEST = new DeviceFlag( "FOLDER_NAME_SUGGEST", true, "Suggests folder names instead of blank text."); diff --git a/src_plugins/com/android/systemui/plugins/AllAppsSearchPlugin.java b/src_plugins/com/android/systemui/plugins/AllAppsSearchPlugin.java deleted file mode 100644 index 0b48c07d01..0000000000 --- a/src_plugins/com/android/systemui/plugins/AllAppsSearchPlugin.java +++ /dev/null @@ -1,105 +0,0 @@ -/* - * Copyright (C) 2020 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package com.android.systemui.plugins; - -import android.app.Activity; -import android.os.Bundle; -import android.os.CancellationSignal; -import android.os.Parcelable; -import android.view.View; - -import com.android.systemui.plugins.annotations.ProvidesInterface; -import com.android.systemui.plugins.shared.SearchTargetEventLegacy; -import com.android.systemui.plugins.shared.SearchTargetLegacy; - -import java.util.List; -import java.util.function.Consumer; - -/** - * Implement this plugin interface to fetch search result data from the plugin side. - */ -@ProvidesInterface(action = AllAppsSearchPlugin.ACTION, version = AllAppsSearchPlugin.VERSION) -public interface AllAppsSearchPlugin extends Plugin { - String ACTION = "com.android.systemui.action.PLUGIN_ALL_APPS_SEARCH_ACTIONS"; - int VERSION = 9; - - /** - * init plugin - */ - void setup(Activity activity, View view, boolean useLegacy); - - /** - * Send launcher state related signals. - */ - void onStateTransitionStart(int fromState, int toState); - - void onStateTransitionComplete(int state); - - /** - * Send launcher window focus and visibility changed signals. - */ - void onWindowFocusChanged(boolean hasFocus); - - void onWindowVisibilityChanged(int visibility); - - /** - * Send signal when user starts typing, perform search, notify search target - * event when search ends. - */ - void startedSearchSession(); - - /** - * Main function that triggers search. - * - * @param input string that has been typed by a user - * @param inputArgs extra info that may be relevant for the input query - * @param results contains the result that will be rendered in all apps search - * surface - * @param cancellationSignal {@link CancellationSignal} can be used to share status of current - */ - void queryLegacy(String input, Bundle inputArgs, Consumer> results, - CancellationSignal cancellationSignal); - - /** - * Main function that triggers search. - * - * @param input string that has been typed by a user - * @param inputArgs extra info that may be relevant for the input query - * @param results contains the result that will be rendered in all apps search - * surface - * @param cancellationSignal {@link CancellationSignal} can be used to share status of current - */ - void query(String input, Bundle inputArgs, Consumer> results, - CancellationSignal cancellationSignal); - - /** - * Send over search target interaction events to Plugin - */ - void notifySearchTargetEventLegacy(SearchTargetEventLegacy event); - - /** - * Send over search target interaction events to Plugin - */ - void notifySearchTargetEvent(Parcelable event); - - /** - * Launcher activity lifecycle callbacks - */ - void onResume(int state); - - void onStop(int state); -} \ No newline at end of file diff --git a/src_plugins/com/android/systemui/plugins/shared/SearchTargetEventLegacy.java b/src_plugins/com/android/systemui/plugins/shared/SearchTargetEventLegacy.java deleted file mode 100644 index 0fc61f04dc..0000000000 --- a/src_plugins/com/android/systemui/plugins/shared/SearchTargetEventLegacy.java +++ /dev/null @@ -1,95 +0,0 @@ -/* - * Copyright (C) 2020 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package com.android.systemui.plugins.shared; - -import android.os.Bundle; - -/** - * Event used for the feedback loop to the plugin. (and future aiai) - * - * @deprecated Use {@link android.app.search.SearchTargetEvent} - */ -@Deprecated -public class SearchTargetEventLegacy { - public static final int POSITION_NONE = -1; - - public static final int SELECT = 0; - public static final int QUICK_SELECT = 1; - public static final int LONG_PRESS = 2; - public static final int CHILD_SELECT = 3; - - private final SearchTargetLegacy mSearchTarget; - private final int mEventType; - private final int mShortcutPosition; - private final Bundle mExtras; - - public SearchTargetEventLegacy(SearchTargetLegacy searchTarget, int eventType, - int shortcutPosition, - Bundle extras) { - mSearchTarget = searchTarget; - mEventType = eventType; - mShortcutPosition = shortcutPosition; - mExtras = extras; - } - - - public SearchTargetLegacy getSearchTarget() { - return mSearchTarget; - } - - public int getShortcutPosition() { - return mShortcutPosition; - } - - public int getEventType() { - return mEventType; - } - - public Bundle getExtras() { - return mExtras; - } - - /** - * A builder for {@link SearchTargetLegacy} - */ - public static final class Builder { - private final SearchTargetLegacy mSearchTarget; - private final int mEventType; - private int mShortcutPosition = POSITION_NONE; - private Bundle mExtras; - - public Builder(SearchTargetLegacy searchTarget, int eventType) { - mSearchTarget = searchTarget; - mEventType = eventType; - } - - public Builder setShortcutPosition(int shortcutPosition) { - mShortcutPosition = shortcutPosition; - return this; - } - - public Builder setExtras(Bundle extras) { - mExtras = extras; - return this; - } - - public SearchTargetEventLegacy build() { - return new SearchTargetEventLegacy(mSearchTarget, mEventType, mShortcutPosition, - mExtras); - } - } - -} diff --git a/src_plugins/com/android/systemui/plugins/shared/SearchTargetLegacy.java b/src_plugins/com/android/systemui/plugins/shared/SearchTargetLegacy.java deleted file mode 100644 index 2a6ba8829f..0000000000 --- a/src_plugins/com/android/systemui/plugins/shared/SearchTargetLegacy.java +++ /dev/null @@ -1,175 +0,0 @@ -/* - * Copyright (C) 2020 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package com.android.systemui.plugins.shared; - -import android.app.RemoteAction; -import android.content.ComponentName; -import android.content.pm.ShortcutInfo; -import android.os.Bundle; -import android.os.UserHandle; - -import java.util.List; - -/** - * Used to return all apps search targets. - * - * @deprecated Use SearchTarget - */ -@Deprecated -public class SearchTargetLegacy implements Comparable { - - private final String mItemId; - private final String mItemType; - private final float mScore; - - private final ComponentName mComponentName; - private final UserHandle mUserHandle; - private final List mShortcutInfos; - //TODO: (sfufa) replace with a list of a custom type - private final RemoteAction mRemoteAction; - private final Bundle mExtras; - - private SearchTargetLegacy(String itemId, String itemType, float score, - ComponentName componentName, UserHandle userHandle, List shortcutInfos, - RemoteAction remoteAction, Bundle extras) { - mItemId = itemId; - mItemType = itemType; - mScore = score; - mComponentName = componentName; - mUserHandle = userHandle; - mShortcutInfos = shortcutInfos; - mExtras = extras; - mRemoteAction = remoteAction; - } - - public String getItemId() { - return mItemId; - } - - public String getItemType() { - return mItemType; - } - - public ComponentName getComponentName() { - return mComponentName; - } - - public UserHandle getUserHandle() { - return mUserHandle; - } - - public float getScore() { - return mScore; - } - - public List getShortcutInfos() { - return mShortcutInfos; - } - - public Bundle getExtras() { - return mExtras; - } - - public RemoteAction getRemoteAction() { - return mRemoteAction; - } - - @Override - public int compareTo(SearchTargetLegacy o) { - return Float.compare(o.mScore, mScore); - } - - /** - * A builder for {@link SearchTargetLegacy} - */ - public static final class Builder { - - - private String mItemId; - - private final String mItemType; - private final float mScore; - - - private ComponentName mComponentName; - private UserHandle mUserHandle; - private List mShortcutInfos; - private Bundle mExtras; - private RemoteAction mRemoteAction; - - public Builder(String itemType, float score) { - this(itemType, score, null, null); - } - - public Builder(String itemType, float score, ComponentName cn, - UserHandle user) { - mItemType = itemType; - mScore = score; - mComponentName = cn; - mUserHandle = user; - } - - public String getItemId() { - return mItemId; - } - - public float getScore() { - return mScore; - } - - public Builder setItemId(String itemId) { - mItemId = itemId; - return this; - } - - public Builder setComponentName(ComponentName componentName) { - mComponentName = componentName; - return this; - } - - public Builder setUserHandle(UserHandle userHandle) { - mUserHandle = userHandle; - return this; - } - - public Builder setShortcutInfos(List shortcutInfos) { - mShortcutInfos = shortcutInfos; - return this; - } - - public Builder setExtras(Bundle extras) { - mExtras = extras; - return this; - } - - public Builder setRemoteAction(RemoteAction remoteAction) { - mRemoteAction = remoteAction; - return this; - } - - /** - * Builds a {@link SearchTargetLegacy} - */ - public SearchTargetLegacy build() { - if (mItemId == null) { - throw new IllegalStateException("Item ID is required for building SearchTarget"); - } - return new SearchTargetLegacy(mItemId, mItemType, mScore, mComponentName, mUserHandle, - mShortcutInfos, - mRemoteAction, mExtras); - } - } -} -- GitLab From 7786595ae565ccd48efc87422508f2ff93b1b7e8 Mon Sep 17 00:00:00 2001 From: Wale Ogunwale Date: Wed, 3 Feb 2021 07:39:20 -0800 Subject: [PATCH 0458/1664] Launcher3: Added shared lib APIs for Launcher to use split-screen APIs. Bug: 179176511 Test: presubmits! Change-Id: Ib66cd533676ec28c3b18549d2b9931cdb2263654 --- .../com/android/quickstep/SystemUiProxy.java | 84 +++++++++++++++++++ 1 file changed, 84 insertions(+) diff --git a/quickstep/src/com/android/quickstep/SystemUiProxy.java b/quickstep/src/com/android/quickstep/SystemUiProxy.java index ca55468a9f..85b21e09aa 100644 --- a/quickstep/src/com/android/quickstep/SystemUiProxy.java +++ b/quickstep/src/com/android/quickstep/SystemUiProxy.java @@ -17,6 +17,7 @@ package com.android.quickstep; import static com.android.launcher3.util.Executors.MAIN_EXECUTOR; +import android.app.PendingIntent; import android.app.PictureInPictureParams; import android.content.ComponentName; import android.content.Context; @@ -24,15 +25,18 @@ import android.content.pm.ActivityInfo; import android.graphics.Bitmap; import android.graphics.Insets; import android.graphics.Rect; +import android.os.Binder; import android.os.Bundle; import android.os.IBinder; import android.os.IBinder.DeathRecipient; import android.os.RemoteException; +import android.os.UserHandle; import android.util.Log; import android.view.MotionEvent; import com.android.launcher3.util.MainThreadInitializedObject; import com.android.systemui.shared.recents.IPinnedStackAnimationListener; +import com.android.systemui.shared.recents.ISplitScreenListener; import com.android.systemui.shared.recents.ISystemUiProxy; import com.android.systemui.shared.recents.model.Task; import com.android.systemui.shared.system.RemoteTransitionCompat; @@ -431,4 +435,84 @@ public class SystemUiProxy implements ISystemUiProxy { } } } + + @Override + public void registerSplitScreenListener(ISplitScreenListener listener) { + if (mSystemUiProxy != null) { + try { + mSystemUiProxy.registerSplitScreenListener(listener); + } catch (RemoteException e) { + Log.w(TAG, "Failed call registerSplitScreenListener"); + } + } + } + + @Override + public void unregisterSplitScreenListener(ISplitScreenListener listener) { + if (mSystemUiProxy != null) { + try { + mSystemUiProxy.unregisterSplitScreenListener(listener); + } catch (RemoteException e) { + Log.w(TAG, "Failed call unregisterSplitScreenListener"); + } + } + } + + @Override + public void setSideStageVisibility(boolean visible) { + if (mSystemUiProxy != null) { + try { + mSystemUiProxy.setSideStageVisibility(visible); + } catch (RemoteException e) { + Log.w(TAG, "Failed call setSideStageVisibility"); + } + } + } + + @Override + public void exitSplitScreen() { + if (mSystemUiProxy != null) { + try { + mSystemUiProxy.exitSplitScreen(); + } catch (RemoteException e) { + Log.w(TAG, "Failed call exitSplitScreen"); + } + } + } + + @Override + public void startTask(int taskId, int stage, int position, Bundle options) { + if (mSystemUiProxy != null) { + try { + mSystemUiProxy.startTask(taskId, stage, position, options); + } catch (RemoteException e) { + Log.w(TAG, "Failed call startTask"); + } + } + } + + @Override + public void startShortcut(String packageName, String shortcutId, int stage, int position, + Bundle options, UserHandle user) { + if (mSystemUiProxy != null) { + try { + mSystemUiProxy.startShortcut(packageName, shortcutId, stage, position, options, + user); + } catch (RemoteException e) { + Log.w(TAG, "Failed call startShortcut"); + } + } + } + + @Override + public void startIntent(PendingIntent intent, int stage, int position, Bundle options) { + if (mSystemUiProxy != null) { + try { + mSystemUiProxy.startIntent(intent, stage, position, options); + } catch (RemoteException e) { + Log.w(TAG, "Failed call startIntent"); + } + } + } + } -- GitLab From cbeb13d6c7f93ec0d510a7d77e7bcdb2b46beb2b Mon Sep 17 00:00:00 2001 From: thiruram Date: Wed, 27 Jan 2021 14:45:58 -0800 Subject: [PATCH 0459/1664] [AA+] Log app launches from AA+ search result container. This change will update westworld logs for app launches from AA+ search result. Updates log's container info from AllAppsContainer -> AllAppsPlusSearchResultContainer. Bug: 178562918 Change-Id: I9ffca27fea42951a57640ef36717c04ff0251506 --- Android.bp | 20 +++++++++ Android.mk | 10 ++--- build.gradle | 4 +- protos/launcher_atom.proto | 3 ++ .../launcher_atom_extension.proto | 31 +++++++++++++ .../launcher_atom_extension.proto | 34 +++++++++++++++ .../launcher3/search/SearchResultIcon.java | 43 ++++++++++++++++--- .../logging/StatsLogCompatManager.java | 5 +++ .../launcher3/model/data/ItemInfo.java | 2 +- 9 files changed, 138 insertions(+), 14 deletions(-) create mode 100644 protos_overrides/launcher_atom_extension.proto create mode 100644 quickstep/protos_overrides/launcher_atom_extension.proto diff --git a/Android.bp b/Android.bp index b20b307a5c..a720658f9f 100644 --- a/Android.bp +++ b/Android.bp @@ -36,17 +36,37 @@ java_library_static { name: "launcher_log_protos_lite", srcs: [ "protos/*.proto", + "protos_overrides/*.proto", ], sdk_version: "current", proto: { type: "lite", local_include_dirs:[ "protos", + "protos_overrides", ], }, static_libs: ["libprotobuf-java-lite"], } +java_library_static { + name: "launcher_quickstep_log_protos_lite", + srcs: [ + "quickstep/protos_overrides/*.proto", + ], + sdk_version: "current", + proto: { + type: "lite", + local_include_dirs:[ + "quickstep/protos_overrides", + ], + }, + static_libs: [ + "libprotobuf-java-lite", + "launcher_log_protos_lite" + ], +} + java_library { name: "LauncherPluginLib", diff --git a/Android.mk b/Android.mk index ed420393dc..19ad328187 100644 --- a/Android.mk +++ b/Android.mk @@ -33,7 +33,7 @@ LOCAL_STATIC_ANDROID_LIBRARIES := \ LOCAL_STATIC_JAVA_LIBRARIES := \ LauncherPluginLib \ - launcher_log_protos_lite \ + launcher_quickstep_log_protos_lite \ search_ui LOCAL_SRC_FILES := \ @@ -65,7 +65,7 @@ LOCAL_SRC_FILES := \ $(call all-java-files-under, src_shortcuts_overrides) \ $(call all-java-files-under, src_ui_overrides) \ $(call all-java-files-under, ext_tests/src) - + LOCAL_RESOURCE_DIR := $(LOCAL_PATH)/ext_tests/res LOCAL_PROGUARD_FLAG_FILES := proguard.flags @@ -129,8 +129,7 @@ LOCAL_MODULE_TAGS := optional LOCAL_STATIC_JAVA_LIBRARIES := \ SystemUI-statsd \ - SystemUISharedLib \ - launcher_log_protos_lite + SystemUISharedLib ifneq (,$(wildcard frameworks/base)) LOCAL_PRIVATE_PLATFORM_APIS := true else @@ -196,8 +195,7 @@ LOCAL_MODULE_TAGS := optional LOCAL_STATIC_JAVA_LIBRARIES := \ SystemUI-statsd \ - SystemUISharedLib \ - launcher_log_protos_lite + SystemUISharedLib ifneq (,$(wildcard frameworks/base)) LOCAL_PRIVATE_PLATFORM_APIS := true else diff --git a/build.gradle b/build.gradle index 28a05d569c..a7eef13a3d 100644 --- a/build.gradle +++ b/build.gradle @@ -81,7 +81,7 @@ android { java.srcDirs = ['src', 'src_plugins'] manifest.srcFile 'AndroidManifest-common.xml' proto { - srcDir 'protos/' + srcDirs = ['protos/', 'protos_overrides/'] } } @@ -181,4 +181,4 @@ protobuf { } } } -} \ No newline at end of file +} diff --git a/protos/launcher_atom.proto b/protos/launcher_atom.proto index cd229ae015..b4c6138671 100644 --- a/protos/launcher_atom.proto +++ b/protos/launcher_atom.proto @@ -18,6 +18,8 @@ syntax = "proto2"; option java_package = "com.android.launcher3.logger"; option java_outer_classname = "LauncherAtom"; +import "launcher_atom_extension.proto"; + // // ItemInfos message ItemInfo { @@ -55,6 +57,7 @@ message ContainerInfo { SettingsContainer settings_container = 9; PredictedHotseatContainer predicted_hotseat_container = 10; TaskSwitcherContainer task_switcher_container = 11; + ExtendedContainers extended_containers = 20; } } diff --git a/protos_overrides/launcher_atom_extension.proto b/protos_overrides/launcher_atom_extension.proto new file mode 100644 index 0000000000..a07daf844c --- /dev/null +++ b/protos_overrides/launcher_atom_extension.proto @@ -0,0 +1,31 @@ +/* + * Copyright (C) 2020 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +syntax = "proto2"; + +option java_package = "com.android.launcher3.logger"; +option java_outer_classname = "LauncherAtomExtensions"; + + +// This proto file contains placeholder messages that can be overridden by +// other Launcher variants. +// Variants could have its own launcher_atom_extension.proto file(should have +// same messages declared here but with different implementation); when building +// variant's apk launcher_atom.proto will reference variant's extension file, +// essentially overriding this file. + + +// Wrapper message for additional containers used in variants. +message ExtendedContainers {} diff --git a/quickstep/protos_overrides/launcher_atom_extension.proto b/quickstep/protos_overrides/launcher_atom_extension.proto new file mode 100644 index 0000000000..2766acf539 --- /dev/null +++ b/quickstep/protos_overrides/launcher_atom_extension.proto @@ -0,0 +1,34 @@ +/* + * Copyright (C) 2020 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +syntax = "proto2"; + +option java_package = "com.android.launcher3.logger"; +option java_outer_classname = "LauncherAtomExtensions"; + + +// Wrapper message for containers used at the quickstep level. +// Message name should match with launcher_atom_extension.proto message at +// the AOSP level. +message ExtendedContainers { + + oneof Container{ + DeviceSearchResultContainer device_search_result_container = 1; + } +} + +// Represents on-device search result container. +message DeviceSearchResultContainer{ +} diff --git a/quickstep/src/com/android/launcher3/search/SearchResultIcon.java b/quickstep/src/com/android/launcher3/search/SearchResultIcon.java index c353d7aa3c..521174be32 100644 --- a/quickstep/src/com/android/launcher3/search/SearchResultIcon.java +++ b/quickstep/src/com/android/launcher3/search/SearchResultIcon.java @@ -49,6 +49,9 @@ import com.android.launcher3.allapps.AllAppsStore; import com.android.launcher3.icons.BitmapInfo; import com.android.launcher3.icons.BitmapRenderer; import com.android.launcher3.icons.LauncherIcons; +import com.android.launcher3.logger.LauncherAtom.ContainerInfo; +import com.android.launcher3.logger.LauncherAtomExtensions.DeviceSearchResultContainer; +import com.android.launcher3.logger.LauncherAtomExtensions.ExtendedContainers; import com.android.launcher3.model.data.AppInfo; import com.android.launcher3.model.data.ItemInfoWithIcon; import com.android.launcher3.model.data.PackageItemInfo; @@ -142,8 +145,14 @@ public class SearchResultIcon extends BubbleTextView implements SearchActionItemInfo itemInfo = new SearchActionItemInfo(searchAction.getIcon(), searchTarget.getPackageName(), searchTarget.getUserHandle(), - searchAction.getTitle() - ); + searchAction.getTitle()) { + // Workaround to log ItemInfo with DeviceSearchResultContainer without + // updating ItemInfo.container field. + @Override + protected ContainerInfo getContainerInfo() { + return buildDeviceSearchResultContainer(); + } + }; itemInfo.setIntent(searchAction.getIntent()); itemInfo.setPendingIntent(searchAction.getPendingIntent()); @@ -243,7 +252,15 @@ public class SearchResultIcon extends BubbleTextView implements private void prepareUsingApp(ComponentName componentName, UserHandle userHandle) { AllAppsStore appsStore = mLauncher.getAppsView().getAppsStore(); - AppInfo appInfo = appsStore.getApp(new ComponentKey(componentName, userHandle)); + AppInfo appInfo = new AppInfo( + appsStore.getApp(new ComponentKey(componentName, userHandle))) { + // Workaround to log ItemInfo with DeviceSearchResultContainer without + // updating ItemInfo.container field. + @Override + protected ContainerInfo getContainerInfo() { + return buildDeviceSearchResultContainer(); + } + }; if (appInfo == null) { setVisibility(GONE); @@ -253,9 +270,15 @@ public class SearchResultIcon extends BubbleTextView implements notifyItemInfoChanged(appInfo); } - private void prepareUsingShortcutInfo(ShortcutInfo shortcutInfo) { - WorkspaceItemInfo workspaceItemInfo = new WorkspaceItemInfo(shortcutInfo, getContext()); + WorkspaceItemInfo workspaceItemInfo = new WorkspaceItemInfo(shortcutInfo, getContext()) { + // Workaround to log ItemInfo with DeviceSearchResultContainer without + // updating ItemInfo.container field. + @Override + protected ContainerInfo getContainerInfo() { + return buildDeviceSearchResultContainer(); + } + }; notifyItemInfoChanged(workspaceItemInfo); LauncherAppState launcherAppState = LauncherAppState.getInstance(getContext()); MODEL_EXECUTOR.execute(() -> { @@ -293,4 +316,14 @@ public class SearchResultIcon extends BubbleTextView implements mOnItemInfoChanged = null; } } + + private static ContainerInfo buildDeviceSearchResultContainer() { + return ContainerInfo.newBuilder().setExtendedContainers( + ExtendedContainers + .newBuilder() + .setDeviceSearchResultContainer( + DeviceSearchResultContainer + .newBuilder())) + .build(); + } } diff --git a/quickstep/src/com/android/quickstep/logging/StatsLogCompatManager.java b/quickstep/src/com/android/quickstep/logging/StatsLogCompatManager.java index d949126981..d22496d56f 100644 --- a/quickstep/src/com/android/quickstep/logging/StatsLogCompatManager.java +++ b/quickstep/src/com/android/quickstep/logging/StatsLogCompatManager.java @@ -16,6 +16,7 @@ package com.android.quickstep.logging; +import static com.android.launcher3.logger.LauncherAtom.ContainerInfo.ContainerCase.EXTENDED_CONTAINERS; import static com.android.launcher3.logger.LauncherAtom.ContainerInfo.ContainerCase.FOLDER; import static com.android.launcher3.logger.LauncherAtom.ContainerInfo.ContainerCase.SEARCH_RESULT_CONTAINER; import static com.android.launcher3.logging.StatsLogManager.LauncherEvent.LAUNCHER_WORKSPACE_SNAPSHOT; @@ -72,6 +73,7 @@ public class StatsLogCompatManager extends StatsLogManager { private static final int DEFAULT_PAGE_INDEX = -2; private static final int FOLDER_HIERARCHY_OFFSET = 100; private static final int SEARCH_RESULT_HIERARCHY_OFFSET = 200; + private static final int EXTENDED_CONTAINERS_HIERARCHY_OFFSET = 300; public static final CopyOnWriteArrayList LOGS_CONSUMER = new CopyOnWriteArrayList<>(); @@ -397,6 +399,9 @@ public class StatsLogCompatManager extends StatsLogManager { } else if (info.getContainerInfo().getContainerCase() == SEARCH_RESULT_CONTAINER) { return info.getContainerInfo().getSearchResultContainer().getParentContainerCase() .getNumber() + SEARCH_RESULT_HIERARCHY_OFFSET; + } else if (info.getContainerInfo().getContainerCase() == EXTENDED_CONTAINERS) { + return info.getContainerInfo().getExtendedContainers().getContainerCase().getNumber() + + EXTENDED_CONTAINERS_HIERARCHY_OFFSET; } else { return info.getContainerInfo().getContainerCase().getNumber(); } diff --git a/src/com/android/launcher3/model/data/ItemInfo.java b/src/com/android/launcher3/model/data/ItemInfo.java index b11b419dde..2fc902336d 100644 --- a/src/com/android/launcher3/model/data/ItemInfo.java +++ b/src/com/android/launcher3/model/data/ItemInfo.java @@ -225,7 +225,7 @@ public class ItemInfo { protected String dumpProperties() { return "id=" + id + " type=" + LauncherSettings.Favorites.itemTypeToString(itemType) - + " container=" + LauncherSettings.Favorites.containerToString(container) + + " container=" + getContainerInfo() + " targetComponent=" + getTargetComponent() + " screen=" + screenId + " cell(" + cellX + "," + cellY + ")" -- GitLab From f4d19f4614cf2f15a5f9f1ad46e7204bc515dbb5 Mon Sep 17 00:00:00 2001 From: thiruram Date: Wed, 27 Jan 2021 19:05:12 -0800 Subject: [PATCH 0460/1664] [AA+] Log app launches from DeviceSearchResultContainer with instance ID. This InstanceId is used to recreate the AA+ session on the server side. Bug: 178562918 Change-Id: I1bba94417d3a142351e2470bb6153707d2cadb11 --- .../android/launcher3/search/SearchResultIcon.java | 6 +++--- .../launcher3/uioverrides/QuickstepLauncher.java | 12 ++++++++++++ .../launcher3/allapps/search/LiveSearchManager.java | 13 +++++++++++++ src/com/android/launcher3/model/data/ItemInfo.java | 5 ++++- 4 files changed, 32 insertions(+), 4 deletions(-) diff --git a/quickstep/src/com/android/launcher3/search/SearchResultIcon.java b/quickstep/src/com/android/launcher3/search/SearchResultIcon.java index 521174be32..f7d5f45bfa 100644 --- a/quickstep/src/com/android/launcher3/search/SearchResultIcon.java +++ b/quickstep/src/com/android/launcher3/search/SearchResultIcon.java @@ -149,7 +149,7 @@ public class SearchResultIcon extends BubbleTextView implements // Workaround to log ItemInfo with DeviceSearchResultContainer without // updating ItemInfo.container field. @Override - protected ContainerInfo getContainerInfo() { + public ContainerInfo getContainerInfo() { return buildDeviceSearchResultContainer(); } }; @@ -257,7 +257,7 @@ public class SearchResultIcon extends BubbleTextView implements // Workaround to log ItemInfo with DeviceSearchResultContainer without // updating ItemInfo.container field. @Override - protected ContainerInfo getContainerInfo() { + public ContainerInfo getContainerInfo() { return buildDeviceSearchResultContainer(); } }; @@ -275,7 +275,7 @@ public class SearchResultIcon extends BubbleTextView implements // Workaround to log ItemInfo with DeviceSearchResultContainer without // updating ItemInfo.container field. @Override - protected ContainerInfo getContainerInfo() { + public ContainerInfo getContainerInfo() { return buildDeviceSearchResultContainer(); } }; diff --git a/quickstep/src/com/android/launcher3/uioverrides/QuickstepLauncher.java b/quickstep/src/com/android/launcher3/uioverrides/QuickstepLauncher.java index a00ce56e6a..ee00ed2207 100644 --- a/quickstep/src/com/android/launcher3/uioverrides/QuickstepLauncher.java +++ b/quickstep/src/com/android/launcher3/uioverrides/QuickstepLauncher.java @@ -24,6 +24,8 @@ import static com.android.launcher3.LauncherState.NORMAL; import static com.android.launcher3.LauncherState.OVERVIEW; import static com.android.launcher3.LauncherState.OVERVIEW_MODAL_TASK; import static com.android.launcher3.compat.AccessibilityManagerCompat.sendCustomAccessibilityEvent; +import static com.android.launcher3.logger.LauncherAtom.ContainerInfo.ContainerCase.EXTENDED_CONTAINERS; +import static com.android.launcher3.logger.LauncherAtomExtensions.ExtendedContainers.ContainerCase.DEVICE_SEARCH_RESULT_CONTAINER; import static com.android.launcher3.logging.StatsLogManager.LauncherEvent.LAUNCHER_APP_LAUNCH_TAP; import static com.android.launcher3.testing.TestProtocol.HINT_STATE_ORDINAL; import static com.android.launcher3.testing.TestProtocol.OVERVIEW_STATE_ORDINAL; @@ -81,6 +83,7 @@ import java.io.FileDescriptor; import java.io.PrintWriter; import java.util.ArrayList; import java.util.Objects; +import java.util.Optional; import java.util.stream.Stream; public class QuickstepLauncher extends BaseQuickstepLauncher { @@ -105,6 +108,15 @@ public class QuickstepLauncher extends BaseQuickstepLauncher { @Override protected void logAppLaunch(ItemInfo info, InstanceId instanceId) { + // If the app launch is from DeviceSearchResultContainer then add the InstanceId from + // LiveSearchManager to recreate the AllApps search session on the server side. + Optional logInstanceId = this.getLiveSearchManager().getLogInstanceId(); + if (info.getContainerInfo().getContainerCase() == EXTENDED_CONTAINERS + && info.getContainerInfo().getExtendedContainers().getContainerCase() + == DEVICE_SEARCH_RESULT_CONTAINER && logInstanceId.isPresent()) { + instanceId = logInstanceId.get(); + } + StatsLogger logger = getStatsLogManager() .logger().withItemInfo(info).withInstanceId(instanceId); diff --git a/src/com/android/launcher3/allapps/search/LiveSearchManager.java b/src/com/android/launcher3/allapps/search/LiveSearchManager.java index ec33908c75..c2f0b960c8 100644 --- a/src/com/android/launcher3/allapps/search/LiveSearchManager.java +++ b/src/com/android/launcher3/allapps/search/LiveSearchManager.java @@ -33,10 +33,13 @@ import androidx.slice.widget.SliceLiveData; import com.android.launcher3.Launcher; import com.android.launcher3.LauncherAppWidgetProviderInfo; +import com.android.launcher3.logging.InstanceId; +import com.android.launcher3.logging.InstanceIdSequence; import com.android.launcher3.util.ComponentKey; import com.android.launcher3.widget.PendingAddWidgetInfo; import java.util.HashMap; +import java.util.Optional; /** * Manages Lifecycle for Live search results @@ -51,6 +54,7 @@ public class LiveSearchManager { new HashMap<>(); private final HashMap> mUriSliceMap = new HashMap<>(); private SearchWidgetHost mSearchWidgetHost; + private InstanceId mLogInstanceId; public LiveSearchManager(Launcher launcher) { mLauncher = launcher; @@ -113,6 +117,7 @@ public class LiveSearchManager { */ public void start() { stop(); + mLogInstanceId = new InstanceIdSequence().newInstanceId(); mSearchWidgetHost = new SearchWidgetHost(mLauncher); mSearchWidgetHost.startListening(); } @@ -136,6 +141,14 @@ public class LiveSearchManager { mUriSliceMap.clear(); } + /** + * Returns {@link InstanceId} that should be used for logging events within search session, if + * available. + */ + public Optional getLogInstanceId() { + return Optional.ofNullable(mLogInstanceId); + } + static class SearchWidgetHost extends AppWidgetHost { SearchWidgetHost(Context context) { super(context, SEARCH_APPWIDGET_HOST_ID); diff --git a/src/com/android/launcher3/model/data/ItemInfo.java b/src/com/android/launcher3/model/data/ItemInfo.java index 2fc902336d..3851ab044d 100644 --- a/src/com/android/launcher3/model/data/ItemInfo.java +++ b/src/com/android/launcher3/model/data/ItemInfo.java @@ -352,7 +352,10 @@ public class ItemInfo { return itemBuilder; } - protected ContainerInfo getContainerInfo() { + /** + * Returns {@link ContainerInfo} used when logging this item. + */ + public ContainerInfo getContainerInfo() { switch (container) { case CONTAINER_HOTSEAT: return ContainerInfo.newBuilder() -- GitLab From b245c552d4b6a11e60d16777521f77ee1a4e591c Mon Sep 17 00:00:00 2001 From: Sunny Goyal Date: Wed, 3 Feb 2021 12:55:57 -0800 Subject: [PATCH 0461/1664] Updating predictions if hotseat items get removed/added as a result of model callback Bug: 179284787 Test: Verified on device Change-Id: Iecfc7f291f54aacccc6d2afe674ccd2f20e2035d --- .../launcher3/BaseQuickstepLauncher.java | 4 - .../HotseatPredictionController.java | 90 ++++++++++++------- .../uioverrides/QuickstepLauncher.java | 15 +++- 3 files changed, 74 insertions(+), 35 deletions(-) diff --git a/quickstep/src/com/android/launcher3/BaseQuickstepLauncher.java b/quickstep/src/com/android/launcher3/BaseQuickstepLauncher.java index cbe0eb0795..edcd0a203a 100644 --- a/quickstep/src/com/android/launcher3/BaseQuickstepLauncher.java +++ b/quickstep/src/com/android/launcher3/BaseQuickstepLauncher.java @@ -355,10 +355,6 @@ public abstract class BaseQuickstepLauncher extends Launcher // populating workspace. // TODO: Find a better place for this WellbeingModel.INSTANCE.get(this); - - if (mTaskbarController != null) { - mTaskbarController.onHotseatUpdated(); - } } @Override diff --git a/quickstep/src/com/android/launcher3/hybridhotseat/HotseatPredictionController.java b/quickstep/src/com/android/launcher3/hybridhotseat/HotseatPredictionController.java index aa6601b8c8..88cfacb3a6 100644 --- a/quickstep/src/com/android/launcher3/hybridhotseat/HotseatPredictionController.java +++ b/quickstep/src/com/android/launcher3/hybridhotseat/HotseatPredictionController.java @@ -20,6 +20,7 @@ import static com.android.launcher3.LauncherState.NORMAL; import static com.android.launcher3.hybridhotseat.HotseatEduController.getSettingsIntent; import static com.android.launcher3.logging.StatsLogManager.LauncherEvent.LAUNCHER_HOTSEAT_PREDICTION_PINNED; import static com.android.launcher3.logging.StatsLogManager.LauncherEvent.LAUNCHER_HOTSEAT_RANKED; +import static com.android.launcher3.util.Executors.MAIN_EXECUTOR; import android.animation.Animator; import android.animation.AnimatorSet; @@ -70,7 +71,11 @@ import java.util.stream.Collectors; */ public class HotseatPredictionController implements DragController.DragListener, SystemShortcut.Factory, InvariantDeviceProfile.OnIDPChangeListener, - DragSource { + DragSource, ViewGroup.OnHierarchyChangeListener { + + private static final int FLAG_UPDATE_PAUSED = 1 << 0; + private static final int FLAG_DRAG_IN_PROGRESS = 1 << 1; + private static final int FLAG_FILL_IN_PROGRESS = 1 << 2; private int mHotSeatItemsCount; @@ -80,8 +85,7 @@ public class HotseatPredictionController implements DragController.DragListener, private List mPredictedItems = Collections.emptyList(); private AnimatorSet mIconRemoveAnimators; - private boolean mUIUpdatePaused = false; - private boolean mDragInProgress = false; + private int mPauseFlags = 0; private List mOutlineDrawings = new ArrayList<>(); @@ -114,6 +118,30 @@ public class HotseatPredictionController implements DragController.DragListener, mLauncher.getDragController().addDragListener(this); launcher.getDeviceProfile().inv.addOnChangeListener(this); + mHotseat.getShortcutsAndWidgets().setOnHierarchyChangeListener(this); + } + + @Override + public void onChildViewAdded(View parent, View child) { + onHotseatHierarchyChanged(); + } + + @Override + public void onChildViewRemoved(View parent, View child) { + onHotseatHierarchyChanged(); + } + + private void onHotseatHierarchyChanged() { + if (mPauseFlags == 0 && !mLauncher.isWorkspaceLoading()) { + // Post update after a single frame to avoid layout within layout + MAIN_EXECUTOR.getHandler().post(this::updateFillIfNotLoading); + } + } + + private void updateFillIfNotLoading() { + if (mPauseFlags == 0 && !mLauncher.isWorkspaceLoading()) { + fillGapsWithPrediction(true); + } } /** @@ -160,11 +188,11 @@ public class HotseatPredictionController implements DragController.DragListener, } private void fillGapsWithPrediction() { - fillGapsWithPrediction(false, null); + fillGapsWithPrediction(false); } - private void fillGapsWithPrediction(boolean animate, Runnable callback) { - if (mUIUpdatePaused || mDragInProgress) { + private void fillGapsWithPrediction(boolean animate) { + if (mPauseFlags != 0) { return; } @@ -175,12 +203,14 @@ public class HotseatPredictionController implements DragController.DragListener, mIconRemoveAnimators.addListener(new AnimationSuccessListener() { @Override public void onAnimationSuccess(Animator animator) { - fillGapsWithPrediction(animate, callback); + fillGapsWithPrediction(animate); mIconRemoveAnimators.removeListener(this); } }); return; } + + mPauseFlags |= FLAG_FILL_IN_PROGRESS; for (int rank = 0; rank < mHotSeatItemsCount; rank++) { View child = mHotseat.getChildAt( mHotseat.getCellXFromOrder(rank), @@ -207,10 +237,12 @@ public class HotseatPredictionController implements DragController.DragListener, } preparePredictionInfo(predictedItem, rank); } - bindItems(newItems, animate, callback); + bindItems(newItems, animate); + + mPauseFlags &= ~FLAG_FILL_IN_PROGRESS; } - private void bindItems(List itemsToAdd, boolean animate, Runnable callback) { + private void bindItems(List itemsToAdd, boolean animate) { AnimatorSet animationSet = new AnimatorSet(); for (WorkspaceItemInfo item : itemsToAdd) { PredictedAppIcon icon = PredictedAppIcon.createIcon(mHotseat, item); @@ -221,12 +253,11 @@ public class HotseatPredictionController implements DragController.DragListener, } } if (animate) { - if (callback != null) { - animationSet.addListener(AnimationSuccessListener.forRunnable(callback)); - } + animationSet.addListener(AnimationSuccessListener + .forRunnable(this::removeOutlineDrawings)); animationSet.start(); } else { - if (callback != null) callback.run(); + removeOutlineDrawings(); } if (mLauncher.getTaskbarController() != null) { @@ -234,6 +265,16 @@ public class HotseatPredictionController implements DragController.DragListener, } } + private void removeOutlineDrawings() { + if (mOutlineDrawings.isEmpty()) return; + for (PredictedAppIcon.PredictedIconOutlineDrawing outlineDrawing : mOutlineDrawings) { + mHotseat.removeDelegatedCellDrawing(outlineDrawing); + } + mHotseat.invalidate(); + mOutlineDrawings.clear(); + } + + /** * Unregisters callbacks and frees resources */ @@ -245,11 +286,9 @@ public class HotseatPredictionController implements DragController.DragListener, * start and pauses predicted apps update on the hotseat */ public void setPauseUIUpdate(boolean paused) { - if (mLauncher.getTaskbarController() != null) { - // Taskbar is present, always allow updates since hotseat is still visible. - return; - } - mUIUpdatePaused = paused; + mPauseFlags = paused + ? (mPauseFlags | FLAG_UPDATE_PAUSED) + : (mPauseFlags & ~FLAG_UPDATE_PAUSED); if (!paused) { fillGapsWithPrediction(); } @@ -365,14 +404,14 @@ public class HotseatPredictionController implements DragController.DragListener, for (PredictedAppIcon.PredictedIconOutlineDrawing outlineDrawing : mOutlineDrawings) { mHotseat.addDelegatedCellDrawing(outlineDrawing); } - mDragInProgress = true; + mPauseFlags |= FLAG_DRAG_IN_PROGRESS; mHotseat.invalidate(); } @Override public void onDragEnd() { - mDragInProgress = false; - fillGapsWithPrediction(true, this::removeOutlineDrawings); + mPauseFlags &= ~FLAG_DRAG_IN_PROGRESS; + fillGapsWithPrediction(true); } @Nullable @@ -393,15 +432,6 @@ public class HotseatPredictionController implements DragController.DragListener, itemInfo.screenId = rank; } - private void removeOutlineDrawings() { - if (mOutlineDrawings.isEmpty()) return; - for (PredictedAppIcon.PredictedIconOutlineDrawing outlineDrawing : mOutlineDrawings) { - mHotseat.removeDelegatedCellDrawing(outlineDrawing); - } - mHotseat.invalidate(); - mOutlineDrawings.clear(); - } - @Override public void onIdpChanged(int changeFlags, InvariantDeviceProfile profile) { this.mHotSeatItemsCount = profile.numHotseatIcons; diff --git a/quickstep/src/com/android/launcher3/uioverrides/QuickstepLauncher.java b/quickstep/src/com/android/launcher3/uioverrides/QuickstepLauncher.java index a00ce56e6a..3a26f25e56 100644 --- a/quickstep/src/com/android/launcher3/uioverrides/QuickstepLauncher.java +++ b/quickstep/src/com/android/launcher3/uioverrides/QuickstepLauncher.java @@ -17,6 +17,7 @@ package com.android.launcher3.uioverrides; import static android.view.accessibility.AccessibilityEvent.TYPE_VIEW_FOCUSED; +import static com.android.launcher3.LauncherSettings.Favorites.CONTAINER_HOTSEAT; import static com.android.launcher3.LauncherSettings.Favorites.ITEM_TYPE_APPLICATION; import static com.android.launcher3.LauncherSettings.Favorites.ITEM_TYPE_DEEP_SHORTCUT; import static com.android.launcher3.LauncherSettings.Favorites.ITEM_TYPE_SHORTCUT; @@ -52,6 +53,7 @@ import com.android.launcher3.logging.InstanceId; import com.android.launcher3.logging.StatsLogManager.StatsLogger; import com.android.launcher3.model.BgDataModel.FixedContainerItems; import com.android.launcher3.model.data.ItemInfo; +import com.android.launcher3.model.data.WorkspaceItemInfo; import com.android.launcher3.popup.SystemShortcut; import com.android.launcher3.search.DeviceSearchAdapterProvider; import com.android.launcher3.statemanager.StateManager.AtomicAnimationFactory; @@ -80,6 +82,7 @@ import com.android.quickstep.views.TaskView; import java.io.FileDescriptor; import java.io.PrintWriter; import java.util.ArrayList; +import java.util.List; import java.util.Objects; import java.util.stream.Stream; @@ -152,7 +155,8 @@ public class QuickstepLauncher extends BaseQuickstepLauncher { @Override public boolean startActivitySafely(View v, Intent intent, ItemInfo item) { if (mHotseatPredictionController != null) { - mHotseatPredictionController.setPauseUIUpdate(true); + // Only pause is taskbar controller is not present + mHotseatPredictionController.setPauseUIUpdate(getTaskbarController() == null); } return super.startActivitySafely(v, intent, item); } @@ -217,6 +221,15 @@ public class QuickstepLauncher extends BaseQuickstepLauncher { } } + @Override + public void bindWorkspaceItemsChanged(List updated) { + super.bindWorkspaceItemsChanged(updated); + if (getTaskbarController() != null && updated.stream() + .filter(w -> w.container == CONTAINER_HOTSEAT).findFirst().isPresent()) { + getTaskbarController().onHotseatUpdated(); + } + } + @Override public void onDestroy() { super.onDestroy(); -- GitLab From 0983b8070d5e22a5d0b83bad8d318dc4ad76ae3a Mon Sep 17 00:00:00 2001 From: Sunny Goyal Date: Fri, 29 Jan 2021 14:00:38 -0800 Subject: [PATCH 0462/1664] Catching generic app start exceptions in ProxyActivity Bug: 178815863 Test: None Change-Id: I39ae2879e107160d552625db2946a439ee14440b --- .../launcher3/proxy/ProxyActivityStarter.java | 16 +++++++++------- 1 file changed, 9 insertions(+), 7 deletions(-) diff --git a/quickstep/src/com/android/launcher3/proxy/ProxyActivityStarter.java b/quickstep/src/com/android/launcher3/proxy/ProxyActivityStarter.java index e302b4f152..4d7cc85ed1 100644 --- a/quickstep/src/com/android/launcher3/proxy/ProxyActivityStarter.java +++ b/quickstep/src/com/android/launcher3/proxy/ProxyActivityStarter.java @@ -17,6 +17,7 @@ package com.android.launcher3.proxy; import android.app.Activity; +import android.content.ActivityNotFoundException; import android.content.Context; import android.content.Intent; import android.content.IntentSender.SendIntentException; @@ -48,19 +49,20 @@ public class ProxyActivityStarter extends Activity { return; } - if (mParams.intent != null) { - startActivityForResult(mParams.intent, mParams.requestCode, mParams.options); - return; - } else if (mParams.intentSender != null) { - try { + try { + if (mParams.intent != null) { + startActivityForResult(mParams.intent, mParams.requestCode, mParams.options); + return; + } else if (mParams.intentSender != null) { startIntentSenderForResult(mParams.intentSender, mParams.requestCode, mParams.fillInIntent, mParams.flagsMask, mParams.flagsValues, mParams.extraFlags, mParams.options); return; - } catch (SendIntentException e) { - mParams.deliverResult(this, RESULT_CANCELED, null); } + } catch (NullPointerException | ActivityNotFoundException | SecurityException + | SendIntentException e) { + mParams.deliverResult(this, RESULT_CANCELED, null); } finishAndRemoveTask(); } -- GitLab From ce79488572b5c5cbc1f2710eee195937dfbcad74 Mon Sep 17 00:00:00 2001 From: Tracy Zhou Date: Wed, 3 Feb 2021 13:35:42 -0800 Subject: [PATCH 0463/1664] Make sure that RemoteAnimationTargets is not null when redrawLiveTile() Fixes: 179289159 Test: manual Change-Id: I3a5d283582f0899d98717bbbdd146925cc0a4a93 --- quickstep/src/com/android/quickstep/views/RecentsView.java | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/quickstep/src/com/android/quickstep/views/RecentsView.java b/quickstep/src/com/android/quickstep/views/RecentsView.java index 2f2b566e32..d9d0a936b2 100644 --- a/quickstep/src/com/android/quickstep/views/RecentsView.java +++ b/quickstep/src/com/android/quickstep/views/RecentsView.java @@ -2277,7 +2277,9 @@ public abstract class RecentsView extends PagedView } public void redrawLiveTile() { - mLiveTileTaskViewSimulator.apply(mLiveTileParams); + if (mLiveTileParams.getTargetSet() != null) { + mLiveTileTaskViewSimulator.apply(mLiveTileParams); + } } public TaskViewSimulator getLiveTileTaskViewSimulator() { -- GitLab From 04ebcf468627c87e4a103959735260fe2bea5998 Mon Sep 17 00:00:00 2001 From: vadimt Date: Wed, 3 Feb 2021 15:31:51 -0800 Subject: [PATCH 0464/1664] Postponing registering widget listener For the swipe-to-home gesture, the call will happen after the animation. This fixes jank during swiping up from an ap to home. Also renaming some methods for clarity. Bug: 175048504, 173072373 Test: tracing Change-Id: I070b23edb633d2ac0985d5eb9f75c789f8569772 --- src/com/android/launcher3/Launcher.java | 8 +- .../launcher3/LauncherAppWidgetHost.java | 77 +++++++++---------- 2 files changed, 43 insertions(+), 42 deletions(-) diff --git a/src/com/android/launcher3/Launcher.java b/src/com/android/launcher3/Launcher.java index 0274775244..c7d17a858a 100644 --- a/src/com/android/launcher3/Launcher.java +++ b/src/com/android/launcher3/Launcher.java @@ -922,7 +922,7 @@ public class Launcher extends StatefulActivity implements Launche } logStopAndResume(false /* isResume */); - mAppWidgetHost.setListenIfResumed(false); + mAppWidgetHost.setActivityStarted(false); NotificationListener.removeNotificationsChangedListener(); } @@ -935,7 +935,7 @@ public class Launcher extends StatefulActivity implements Launche mOverlayManager.onActivityStarted(this); } - mAppWidgetHost.setListenIfResumed(true); + mAppWidgetHost.setActivityStarted(true); TraceHelper.INSTANCE.endSection(traceToken); mLifecycleRegistry.setCurrentState(Lifecycle.State.STARTED); } @@ -956,6 +956,7 @@ public class Launcher extends StatefulActivity implements Launche NotificationListener.setNotificationsChangedListener(mPopupDataProvider); DiscoveryBounce.showForHomeIfNeeded(this); + mAppWidgetHost.setActivityResumed(true); } private void logStopAndResume(boolean isResume) { @@ -1049,7 +1050,7 @@ public class Launcher extends StatefulActivity implements Launche @Override public void onStateSetEnd(LauncherState state) { super.onStateSetEnd(state); - getAppWidgetHost().setResumed(state == LauncherState.NORMAL); + getAppWidgetHost().setStateIsNormal(state == LauncherState.NORMAL); getWorkspace().setClipChildren(!state.hasFlag(FLAG_MULTI_PAGE)); finishAutoCancelActionMode(); @@ -1108,6 +1109,7 @@ public class Launcher extends StatefulActivity implements Launche if (!mDeferOverlayCallbacks) { mOverlayManager.onActivityPaused(this); } + mAppWidgetHost.setActivityResumed(false); } class LauncherOverlayCallbacksImpl implements LauncherOverlayCallbacks { diff --git a/src/com/android/launcher3/LauncherAppWidgetHost.java b/src/com/android/launcher3/LauncherAppWidgetHost.java index 7ea68512ee..fea26df7da 100644 --- a/src/com/android/launcher3/LauncherAppWidgetHost.java +++ b/src/com/android/launcher3/LauncherAppWidgetHost.java @@ -49,8 +49,11 @@ import java.util.function.IntConsumer; public class LauncherAppWidgetHost extends AppWidgetHost { private static final int FLAG_LISTENING = 1; - private static final int FLAG_RESUMED = 1 << 1; - private static final int FLAG_LISTEN_IF_RESUMED = 1 << 2; + private static final int FLAG_STATE_IS_NORMAL = 1 << 1; + private static final int FLAG_ACTIVITY_STARTED = 1 << 2; + private static final int FLAG_ACTIVITY_RESUMED = 1 << 3; + private static final int FLAGS_SHOULD_LISTEN = + FLAG_STATE_IS_NORMAL | FLAG_ACTIVITY_STARTED | FLAG_ACTIVITY_RESUMED; public static final int APPWIDGET_HOST_ID = 1024; @@ -59,7 +62,7 @@ public class LauncherAppWidgetHost extends AppWidgetHost { private final SparseArray mPendingViews = new SparseArray<>(); private final Context mContext; - private int mFlags = FLAG_RESUMED; + private int mFlags = FLAG_STATE_IS_NORMAL; private IntConsumer mAppWidgetRemovedCallback = null; @@ -130,49 +133,45 @@ public class LauncherAppWidgetHost extends AppWidgetHost { } /** - * Updates the resumed state of the host. - * When a host is not resumed, it defers calls to startListening until host is resumed again. - * But if the host was already listening, it will not call stopListening. - * - * @see #setListenIfResumed(boolean) + * Sets or unsets a flag the can change whether the widget host should be in the listening + * state. */ - public void setResumed(boolean isResumed) { - if (isResumed == ((mFlags & FLAG_RESUMED) != 0)) { - return; - } - if (isResumed) { - mFlags |= FLAG_RESUMED; - // Start listening if we were supposed to start listening on resume - if ((mFlags & FLAG_LISTEN_IF_RESUMED) != 0 && (mFlags & FLAG_LISTENING) == 0) { - startListening(); - } + private void setShouldListenFlag(int flag, boolean on) { + if (on) { + mFlags |= flag; } else { - mFlags &= ~FLAG_RESUMED; + mFlags &= ~flag; + } + + final boolean listening = isListening(); + if (!listening && (mFlags & FLAGS_SHOULD_LISTEN) == FLAGS_SHOULD_LISTEN) { + // Postpone starting listening until all flags are on. + startListening(); + } else if (listening && (mFlags & FLAG_ACTIVITY_STARTED) == 0) { + // Postpone stopping listening until the activity is stopped. + stopListening(); } } /** - * Updates the listening state of the host. If the host is not resumed, startListening is - * deferred until next resume. - * - * @see #setResumed(boolean) + * Registers an "entering/leaving Normal state" event. */ - public void setListenIfResumed(boolean listenIfResumed) { - if (listenIfResumed == ((mFlags & FLAG_LISTEN_IF_RESUMED) != 0)) { - return; - } - if (listenIfResumed) { - mFlags |= FLAG_LISTEN_IF_RESUMED; - if ((mFlags & FLAG_RESUMED) != 0) { - // If we are resumed, start listening immediately. Note we do not check for - // duplicate calls before calling startListening as startListening is safe to call - // multiple times. - startListening(); - } - } else { - mFlags &= ~FLAG_LISTEN_IF_RESUMED; - stopListening(); - } + public void setStateIsNormal(boolean isNormal) { + setShouldListenFlag(FLAG_STATE_IS_NORMAL, isNormal); + } + + /** + * Registers an "activity started/stopped" event. + */ + public void setActivityStarted(boolean isStarted) { + setShouldListenFlag(FLAG_ACTIVITY_STARTED, isStarted); + } + + /** + * Registers an "activity paused/resumed" event. + */ + public void setActivityResumed(boolean isResumed) { + setShouldListenFlag(FLAG_ACTIVITY_RESUMED, isResumed); } @Override -- GitLab From 1b4d5034fbf56b227ec9697d0d43b3e3d085670d Mon Sep 17 00:00:00 2001 From: Schneider Victor-tulias Date: Wed, 3 Feb 2021 13:05:21 -0800 Subject: [PATCH 0465/1664] Fix icon flicker on transition from installing, to installed, to downloading incrementally. When an app finished downloading, we assumed that its progress level was 100%. This caused flicker in apps that supported incremental downloads. Added a check for the total download progress on app installation. demo: https://drive.google.com/file/d/11NwwhHj_4rmDFwpsb8gTX-uA5YSinjSj/view?usp=sharing Fixes: 178745816 Test: manual Change-Id: I08328296db2ce2c2242fc4fe87158ff4b911c659 --- .../android/launcher3/model/AllAppsList.java | 16 ++++++++++++---- .../launcher3/model/PackageUpdatedTask.java | 18 +++++++++++++++--- 2 files changed, 27 insertions(+), 7 deletions(-) diff --git a/src/com/android/launcher3/model/AllAppsList.java b/src/com/android/launcher3/model/AllAppsList.java index d4fa2789d5..e3e4b6911f 100644 --- a/src/com/android/launcher3/model/AllAppsList.java +++ b/src/com/android/launcher3/model/AllAppsList.java @@ -203,11 +203,16 @@ public class AllAppsList { /** * Add the icons for the supplied apk called packageName. */ - public void addPackage(Context context, String packageName, UserHandle user) { - for (LauncherActivityInfo info : context.getSystemService(LauncherApps.class) - .getActivityList(packageName, user)) { + public List addPackage( + Context context, String packageName, UserHandle user) { + List activities = context.getSystemService(LauncherApps.class) + .getActivityList(packageName, user); + + for (LauncherActivityInfo info : activities) { add(new AppInfo(context, info, user), info); } + + return activities; } /** @@ -250,7 +255,8 @@ public class AllAppsList { /** * Add and remove icons for this package which has been updated. */ - public void updatePackage(Context context, String packageName, UserHandle user) { + public List updatePackage( + Context context, String packageName, UserHandle user) { final List matches = context.getSystemService(LauncherApps.class) .getActivityList(packageName, user); if (matches.size() > 0) { @@ -297,6 +303,8 @@ public class AllAppsList { } } } + + return matches; } /** diff --git a/src/com/android/launcher3/model/PackageUpdatedTask.java b/src/com/android/launcher3/model/PackageUpdatedTask.java index 3275d59488..f13a109926 100644 --- a/src/com/android/launcher3/model/PackageUpdatedTask.java +++ b/src/com/android/launcher3/model/PackageUpdatedTask.java @@ -22,6 +22,7 @@ import static com.android.launcher3.model.data.WorkspaceItemInfo.FLAG_RESTORED_I import android.content.ComponentName; import android.content.Context; import android.content.Intent; +import android.content.pm.LauncherActivityInfo; import android.content.pm.LauncherApps; import android.content.pm.ShortcutInfo; import android.os.UserHandle; @@ -51,6 +52,7 @@ import com.android.launcher3.util.SafeCloseable; import java.util.ArrayList; import java.util.Arrays; import java.util.Collections; +import java.util.HashMap; import java.util.HashSet; import java.util.List; @@ -95,6 +97,7 @@ public class PackageUpdatedTask extends BaseModelUpdateTask { ? ItemInfoMatcher.ofUser(mUser) // We want to update all packages for this user : ItemInfoMatcher.ofPackages(packageSet, mUser); final HashSet removedComponents = new HashSet<>(); + final HashMap> activitiesLists = new HashMap<>(); switch (mOp) { case OP_ADD: { @@ -104,7 +107,8 @@ public class PackageUpdatedTask extends BaseModelUpdateTask { if (FeatureFlags.PROMISE_APPS_IN_ALL_APPS.get()) { appsList.removePackage(packages[i], mUser); } - appsList.addPackage(context, packages[i], mUser); + activitiesLists.put( + packages[i], appsList.addPackage(context, packages[i], mUser)); } flagOp = FlagOp.removeFlag(WorkspaceItemInfo.FLAG_DISABLED_NOT_AVAILABLE); break; @@ -115,7 +119,8 @@ public class PackageUpdatedTask extends BaseModelUpdateTask { for (int i = 0; i < N; i++) { if (DEBUG) Log.d(TAG, "mAllAppsList.updatePackage " + packages[i]); iconCache.updateIconsForPkg(packages[i], mUser); - appsList.updatePackage(context, packages[i], mUser); + activitiesLists.put( + packages[i], appsList.updatePackage(context, packages[i], mUser)); app.getWidgetCache().removePackage(packages[i], mUser); } } @@ -247,7 +252,14 @@ public class PackageUpdatedTask extends BaseModelUpdateTask { if (isNewApkAvailable && si.itemType == Favorites.ITEM_TYPE_APPLICATION) { - si.setProgressLevel(100, PackageInstallInfo.STATUS_INSTALLED); + List activities = activitiesLists.get( + packageName); + si.setProgressLevel( + activities == null || activities.isEmpty() + ? 100 + : PackageManagerHelper.getLoadingProgress( + activities.get(0)), + PackageInstallInfo.STATUS_INSTALLED_DOWNLOADING); iconCache.getTitleAndIcon(si, si.usingLowResIcon()); infoUpdated = true; } -- GitLab From aaa42bd0e85f21c78d999c20adb819d77b6df731 Mon Sep 17 00:00:00 2001 From: Tony Wickham Date: Wed, 27 Jan 2021 16:27:15 -0800 Subject: [PATCH 0466/1664] Add recent tasks to Taskbar - Also adds a divider between Hotseat and Recents - Dedupes Recents from Hotseat Test: Open some recent tasks, ensure they are deduped from Hotseat and also handle < 2 tasks. Bug: 171917176 Change-Id: Ia782c6ccbcda94cfd844aad04dc3d25a3f072c2b --- quickstep/res/layout/taskbar_divider.xml | 23 ++++ quickstep/res/values/colors.xml | 1 + quickstep/res/values/dimens.xml | 2 + .../launcher3/taskbar/TaskbarController.java | 102 +++++++++++++++- .../taskbar/TaskbarDragController.java | 11 ++ .../taskbar/TaskbarRecentsController.java | 114 ++++++++++++++++++ .../launcher3/taskbar/TaskbarView.java | 101 +++++++++++++++- src/com/android/launcher3/BubbleTextView.java | 10 ++ 8 files changed, 361 insertions(+), 3 deletions(-) create mode 100644 quickstep/res/layout/taskbar_divider.xml create mode 100644 quickstep/src/com/android/launcher3/taskbar/TaskbarRecentsController.java diff --git a/quickstep/res/layout/taskbar_divider.xml b/quickstep/res/layout/taskbar_divider.xml new file mode 100644 index 0000000000..6e1aa1e3d5 --- /dev/null +++ b/quickstep/res/layout/taskbar_divider.xml @@ -0,0 +1,23 @@ + + + + \ No newline at end of file diff --git a/quickstep/res/values/colors.xml b/quickstep/res/values/colors.xml index 3bc8ddc0a8..54730f100e 100644 --- a/quickstep/res/values/colors.xml +++ b/quickstep/res/values/colors.xml @@ -27,4 +27,5 @@ #101010 + #C0C0C0 \ No newline at end of file diff --git a/quickstep/res/values/dimens.xml b/quickstep/res/values/dimens.xml index 39cc0b891c..0f40775995 100644 --- a/quickstep/res/values/dimens.xml +++ b/quickstep/res/values/dimens.xml @@ -127,4 +127,6 @@ 54dp 14dp + 1dp + 24dp diff --git a/quickstep/src/com/android/launcher3/taskbar/TaskbarController.java b/quickstep/src/com/android/launcher3/taskbar/TaskbarController.java index 7608645a63..6a74aac2ba 100644 --- a/quickstep/src/com/android/launcher3/taskbar/TaskbarController.java +++ b/quickstep/src/com/android/launcher3/taskbar/TaskbarController.java @@ -23,6 +23,8 @@ import static com.android.systemui.shared.system.WindowManagerWrapper.ITYPE_BOTT import static com.android.systemui.shared.system.WindowManagerWrapper.ITYPE_EXTRA_NAVIGATION_BAR; import android.animation.Animator; +import android.app.ActivityOptions; +import android.content.ComponentName; import android.graphics.PixelFormat; import android.graphics.Point; import android.view.Gravity; @@ -42,8 +44,13 @@ import com.android.launcher3.model.data.ItemInfo; import com.android.launcher3.states.StateAnimationConfig; import com.android.launcher3.touch.ItemClickHandler; import com.android.quickstep.AnimatedFloat; +import com.android.systemui.shared.recents.model.Task; +import com.android.systemui.shared.system.ActivityManagerWrapper; import com.android.systemui.shared.system.WindowManagerWrapper; +import java.util.ArrayList; +import java.util.List; + /** * Interfaces with Launcher/WindowManager/SystemUI to determine what to show in TaskbarView. */ @@ -60,11 +67,17 @@ public class TaskbarController { private final TaskbarStateHandler mTaskbarStateHandler; private final TaskbarVisibilityController mTaskbarVisibilityController; private final TaskbarHotseatController mHotseatController; + private final TaskbarRecentsController mRecentsController; private final TaskbarDragController mDragController; // Initialized in init(). private WindowManager.LayoutParams mWindowLayoutParams; + // Contains all loaded Tasks, not yet deduped from Hotseat items. + private List mLatestLoadedRecentTasks; + // Contains all loaded Hotseat items. + private ItemInfo[] mLatestLoadedHotseatItems; + public TaskbarController(BaseQuickstepLauncher launcher, TaskbarContainerView taskbarContainerView) { mLauncher = launcher; @@ -79,6 +92,8 @@ public class TaskbarController { createTaskbarVisibilityControllerCallbacks()); mHotseatController = new TaskbarHotseatController(mLauncher, createTaskbarHotseatControllerCallbacks()); + mRecentsController = new TaskbarRecentsController(mLauncher, + createTaskbarRecentsControllerCallbacks()); mDragController = new TaskbarDragController(mLauncher); } @@ -101,7 +116,16 @@ public class TaskbarController { return new TaskbarViewCallbacks() { @Override public View.OnClickListener getItemOnClickListener() { - return ItemClickHandler.INSTANCE; + return view -> { + Object tag = view.getTag(); + if (tag instanceof Task) { + Task task = (Task) tag; + ActivityManagerWrapper.getInstance().startActivityFromRecents(task.key, + ActivityOptions.makeBasic()); + } else { + ItemClickHandler.INSTANCE.onClick(view); + } + }; } @Override @@ -116,6 +140,23 @@ public class TaskbarController { @Override public void updateHotseatItems(ItemInfo[] hotseatItemInfos) { mTaskbarView.updateHotseatItems(hotseatItemInfos); + mLatestLoadedHotseatItems = hotseatItemInfos; + dedupeAndUpdateRecentItems(); + } + }; + } + + private TaskbarRecentsControllerCallbacks createTaskbarRecentsControllerCallbacks() { + return new TaskbarRecentsControllerCallbacks() { + @Override + public void updateRecentItems(ArrayList recentTasks) { + mLatestLoadedRecentTasks = recentTasks; + dedupeAndUpdateRecentItems(); + } + + @Override + public void updateRecentTaskAtIndex(int taskIndex, Task task) { + mTaskbarView.updateRecentTaskAtIndex(taskIndex, task); } }; } @@ -124,11 +165,13 @@ public class TaskbarController { * Initializes the Taskbar, including adding it to the screen. */ public void init() { - mTaskbarView.init(mHotseatController.getNumHotseatIcons()); + mTaskbarView.init(mHotseatController.getNumHotseatIcons(), + mRecentsController.getNumRecentIcons()); addToWindowManager(); mTaskbarStateHandler.setTaskbarCallbacks(createTaskbarStateHandlerCallbacks()); mTaskbarVisibilityController.init(); mHotseatController.init(); + mRecentsController.init(); } private TaskbarStateHandlerCallbacks createTaskbarStateHandlerCallbacks() { @@ -149,6 +192,7 @@ public class TaskbarController { mTaskbarStateHandler.setTaskbarCallbacks(null); mTaskbarVisibilityController.cleanup(); mHotseatController.cleanup(); + mRecentsController.cleanup(); } private void removeFromWindowManager() { @@ -246,6 +290,52 @@ public class TaskbarController { return mTaskbarView.isDraggingItem(); } + private void dedupeAndUpdateRecentItems() { + if (mLatestLoadedRecentTasks == null || mLatestLoadedHotseatItems == null) { + return; + } + + final int numRecentIcons = mRecentsController.getNumRecentIcons(); + + // From most recent to least recently opened. + List dedupedTasksInDescendingOrder = new ArrayList<>(); + for (int i = mLatestLoadedRecentTasks.size() - 1; i >= 0; i--) { + Task task = mLatestLoadedRecentTasks.get(i); + boolean isTaskInHotseat = false; + for (ItemInfo hotseatItem : mLatestLoadedHotseatItems) { + if (hotseatItem == null) { + continue; + } + ComponentName hotseatActivity = hotseatItem.getTargetComponent(); + if (hotseatActivity != null && task.key.sourceComponent.getPackageName() + .equals(hotseatActivity.getPackageName())) { + isTaskInHotseat = true; + break; + } + } + if (!isTaskInHotseat) { + dedupedTasksInDescendingOrder.add(task); + if (dedupedTasksInDescendingOrder.size() == numRecentIcons) { + break; + } + } + } + + // TaskbarView expects an array of all the recent tasks to show, in the order to show them. + // So we create an array of the proper size, then fill it in such that the most recent items + // are at the end. If there aren't enough elements to fill the array, leave them null. + Task[] tasksArray = new Task[numRecentIcons]; + for (int i = 0; i < tasksArray.length; i++) { + Task task = i >= dedupedTasksInDescendingOrder.size() + ? null + : dedupedTasksInDescendingOrder.get(i); + tasksArray[tasksArray.length - 1 - i] = task; + } + + mTaskbarView.updateRecentTasks(tasksArray); + mRecentsController.loadIconsForTasks(tasksArray); + } + /** * @return Whether the given View is in the same window as Taskbar. */ @@ -283,4 +373,12 @@ public class TaskbarController { protected interface TaskbarHotseatControllerCallbacks { void updateHotseatItems(ItemInfo[] hotseatItemInfos); } + + /** + * Contains methods that TaskbarRecentsController can call to interface with TaskbarController. + */ + protected interface TaskbarRecentsControllerCallbacks { + void updateRecentItems(ArrayList recentTasks); + void updateRecentTaskAtIndex(int taskIndex, Task task); + } } diff --git a/quickstep/src/com/android/launcher3/taskbar/TaskbarDragController.java b/quickstep/src/com/android/launcher3/taskbar/TaskbarDragController.java index 2318ff96b6..baec8998f3 100644 --- a/quickstep/src/com/android/launcher3/taskbar/TaskbarDragController.java +++ b/quickstep/src/com/android/launcher3/taskbar/TaskbarDragController.java @@ -25,6 +25,7 @@ import android.content.pm.LauncherApps; import android.content.res.Resources; import android.graphics.Canvas; import android.graphics.Point; +import android.os.UserHandle; import android.view.DragEvent; import android.view.View; @@ -33,6 +34,7 @@ import com.android.launcher3.BubbleTextView; import com.android.launcher3.LauncherSettings; import com.android.launcher3.R; import com.android.launcher3.model.data.WorkspaceItemInfo; +import com.android.systemui.shared.recents.model.Task; import com.android.systemui.shared.system.ClipDescriptionCompat; import com.android.systemui.shared.system.LauncherAppsCompat; @@ -102,6 +104,15 @@ public class TaskbarDragController { item.getIntent().getComponent(), null, item.user)); } intent.putExtra(Intent.EXTRA_USER, item.user); + } else if (tag instanceof Task) { + Task task = (Task) tag; + clipDescription = new ClipDescription(task.titleDescription, + new String[] { + ClipDescriptionCompat.MIMETYPE_APPLICATION_TASK + }); + intent = new Intent(); + intent.putExtra(ClipDescriptionCompat.EXTRA_TASK_ID, task.key.id); + intent.putExtra(Intent.EXTRA_USER, UserHandle.of(task.key.userId)); } if (clipDescription != null && intent != null) { diff --git a/quickstep/src/com/android/launcher3/taskbar/TaskbarRecentsController.java b/quickstep/src/com/android/launcher3/taskbar/TaskbarRecentsController.java new file mode 100644 index 0000000000..9d4e000da0 --- /dev/null +++ b/quickstep/src/com/android/launcher3/taskbar/TaskbarRecentsController.java @@ -0,0 +1,114 @@ +/* + * Copyright (C) 2021 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.taskbar; + +import com.android.launcher3.BaseQuickstepLauncher; +import com.android.quickstep.RecentsModel; +import com.android.quickstep.util.CancellableTask; +import com.android.systemui.shared.recents.model.Task; +import com.android.systemui.shared.system.TaskStackChangeListener; +import com.android.systemui.shared.system.TaskStackChangeListeners; + +import java.util.ArrayList; + +/** + * Works with TaskbarController to update the TaskbarView's Recent items. + */ +public class TaskbarRecentsController { + + private final int mNumRecentIcons = 2; + private final BaseQuickstepLauncher mLauncher; + private final TaskbarController.TaskbarRecentsControllerCallbacks mTaskbarCallbacks; + private final RecentsModel mRecentsModel; + + private final TaskStackChangeListener mTaskStackChangeListener = new TaskStackChangeListener() { + @Override + public void onTaskStackChanged() { + reloadRecentTasksIfNeeded(); + } + }; + + // TODO: add TaskbarVisualsChangedListener as well (for calendar/clock?) + + // Used to keep track of the last requested task list id, so that we do not request to load the + // tasks again if we have already requested it and the task list has not changed + private int mTaskListChangeId = -1; + + // The current background requests to load the task icons + private CancellableTask[] mIconLoadRequests = new CancellableTask[mNumRecentIcons]; + + public TaskbarRecentsController(BaseQuickstepLauncher launcher, + TaskbarController.TaskbarRecentsControllerCallbacks taskbarCallbacks) { + mLauncher = launcher; + mTaskbarCallbacks = taskbarCallbacks; + mRecentsModel = RecentsModel.INSTANCE.get(mLauncher); + } + + protected void init() { + TaskStackChangeListeners.getInstance().registerTaskStackListener(mTaskStackChangeListener); + reloadRecentTasksIfNeeded(); + } + + protected void cleanup() { + TaskStackChangeListeners.getInstance().unregisterTaskStackListener( + mTaskStackChangeListener); + cancelAllPendingIconLoadTasks(); + } + + private void reloadRecentTasksIfNeeded() { + if (!mRecentsModel.isTaskListValid(mTaskListChangeId)) { + mTaskListChangeId = mRecentsModel.getTasks(this::onRecentTasksChanged); + } + } + + private void cancelAllPendingIconLoadTasks() { + for (int i = 0; i < mIconLoadRequests.length; i++) { + if (mIconLoadRequests[i] != null) { + mIconLoadRequests[i].cancel(); + } + mIconLoadRequests[i] = null; + } + } + + private void onRecentTasksChanged(ArrayList tasks) { + mTaskbarCallbacks.updateRecentItems(tasks); + } + + /** + * For each Task, loads its icon from the cache in the background, then calls + * {@link TaskbarController.TaskbarRecentsControllerCallbacks#updateRecentTaskAtIndex}. + */ + protected void loadIconsForTasks(Task[] tasks) { + cancelAllPendingIconLoadTasks(); + for (int i = 0; i < tasks.length; i++) { + Task task = tasks[i]; + if (task == null) { + continue; + } + final int taskIndex = i; + mIconLoadRequests[i] = mRecentsModel.getIconCache().updateIconInBackground( + task, updatedTask -> onTaskIconLoaded(task, taskIndex)); + } + } + + private void onTaskIconLoaded(Task task, int taskIndex) { + mTaskbarCallbacks.updateRecentTaskAtIndex(taskIndex, task); + } + + protected int getNumRecentIcons() { + return mNumRecentIcons; + } +} diff --git a/quickstep/src/com/android/launcher3/taskbar/TaskbarView.java b/quickstep/src/com/android/launcher3/taskbar/TaskbarView.java index c98f09ca0f..d8f3bb595e 100644 --- a/quickstep/src/com/android/launcher3/taskbar/TaskbarView.java +++ b/quickstep/src/com/android/launcher3/taskbar/TaskbarView.java @@ -19,6 +19,7 @@ import android.content.Context; import android.content.res.Resources; import android.graphics.RectF; import android.graphics.drawable.ColorDrawable; +import android.graphics.drawable.Drawable; import android.util.AttributeSet; import android.view.DragEvent; import android.view.LayoutInflater; @@ -35,6 +36,7 @@ import com.android.launcher3.BubbleTextView; import com.android.launcher3.R; import com.android.launcher3.model.data.ItemInfo; import com.android.launcher3.model.data.WorkspaceItemInfo; +import com.android.systemui.shared.recents.model.Task; /** * Hosts the Taskbar content such as Hotseat and Recent Apps. Drawn on top of other apps. @@ -52,6 +54,9 @@ public class TaskbarView extends LinearLayout { // Initialized in init(). private int mHotseatStartIndex; private int mHotseatEndIndex; + private View mHotseatRecentsDivider; + private int mRecentsStartIndex; + private int mRecentsEndIndex; private TaskbarController.TaskbarViewCallbacks mControllerCallbacks; @@ -89,10 +94,17 @@ public class TaskbarView extends LinearLayout { mControllerCallbacks = taskbarViewCallbacks; } - protected void init(int numHotseatIcons) { + protected void init(int numHotseatIcons, int numRecentIcons) { mHotseatStartIndex = 0; mHotseatEndIndex = mHotseatStartIndex + numHotseatIcons - 1; updateHotseatItems(new ItemInfo[numHotseatIcons]); + + int dividerIndex = mHotseatEndIndex + 1; + mHotseatRecentsDivider = addDivider(dividerIndex); + + mRecentsStartIndex = dividerIndex + 1; + mRecentsEndIndex = mRecentsStartIndex + numRecentIcons - 1; + updateRecentTasks(new Task[numRecentIcons]); } protected void cleanup() { @@ -147,6 +159,93 @@ public class TaskbarView extends LinearLayout { hotseatView.setOnLongClickListener(null); } } + + updateHotseatRecentsDividerVisibility(); + } + + private View addDivider(int dividerIndex) { + View divider = inflate(R.layout.taskbar_divider); + addView(divider, dividerIndex); + return divider; + } + + /** + * Inflates/binds the Recents items to show in the Taskbar given their Tasks. + */ + protected void updateRecentTasks(Task[] tasks) { + for (int i = 0; i < tasks.length; i++) { + Task task = tasks[i]; + int recentsIndex = mRecentsStartIndex + i; + View recentsView = getChildAt(recentsIndex); + + // Inflate empty icon Views. + if (recentsView == null) { + BubbleTextView btv = (BubbleTextView) inflate(R.layout.taskbar_app_icon); + LayoutParams lp = new LayoutParams(btv.getIconSize(), btv.getIconSize()); + lp.setMargins(mItemMarginLeftRight, 0, mItemMarginLeftRight, 0); + recentsView = btv; + addView(recentsView, recentsIndex, lp); + } + + // Apply the Task, or hide the view if there is none for a given index. + if (recentsView instanceof BubbleTextView && task != null) { + applyTaskToBubbleTextView((BubbleTextView) recentsView, task); + recentsView.setVisibility(VISIBLE); + recentsView.setOnClickListener(mControllerCallbacks.getItemOnClickListener()); + recentsView.setOnLongClickListener( + mControllerCallbacks.getItemOnLongClickListener()); + } else { + recentsView.setVisibility(GONE); + recentsView.setOnClickListener(null); + recentsView.setOnLongClickListener(null); + } + } + + updateHotseatRecentsDividerVisibility(); + } + + private void applyTaskToBubbleTextView(BubbleTextView btv, Task task) { + if (task.icon != null) { + Drawable icon = task.icon.getConstantState().newDrawable().mutate(); + btv.applyIconAndLabel(icon, task.titleDescription); + } + btv.setTag(task); + } + + protected void updateRecentTaskAtIndex(int taskIndex, Task task) { + View taskView = getChildAt(mRecentsStartIndex + taskIndex); + if (taskView instanceof BubbleTextView) { + applyTaskToBubbleTextView((BubbleTextView) taskView, task); + } + } + + /** + * Make the divider VISIBLE between the Hotseat and Recents if there is at least one icon in + * each, otherwise make it GONE. + */ + private void updateHotseatRecentsDividerVisibility() { + if (mHotseatRecentsDivider == null) { + return; + } + + boolean hasAtLeastOneHotseatItem = false; + for (int i = mHotseatStartIndex; i <= mHotseatEndIndex; i++) { + if (getChildAt(i).getVisibility() != GONE) { + hasAtLeastOneHotseatItem = true; + break; + } + } + + boolean hasAtLeastOneRecentItem = false; + for (int i = mRecentsStartIndex; i <= mRecentsEndIndex; i++) { + if (getChildAt(i).getVisibility() != GONE) { + hasAtLeastOneRecentItem = true; + break; + } + } + + mHotseatRecentsDivider.setVisibility(hasAtLeastOneHotseatItem && hasAtLeastOneRecentItem + ? VISIBLE : GONE); } @Override diff --git a/src/com/android/launcher3/BubbleTextView.java b/src/com/android/launcher3/BubbleTextView.java index 5007ffc7be..6f446ad910 100644 --- a/src/com/android/launcher3/BubbleTextView.java +++ b/src/com/android/launcher3/BubbleTextView.java @@ -358,6 +358,16 @@ public class BubbleTextView extends TextView implements ItemInfoUpdateReceiver, } } + /** + * Directly set the icon and label. + */ + @UiThread + public void applyIconAndLabel(Drawable icon, CharSequence label) { + setIcon(icon); + setText(label); + setContentDescription(label); + } + /** * Overrides the default long press timeout. */ -- GitLab From 4e2c25a788f1b0eae6a472c6749fdadfcf046b84 Mon Sep 17 00:00:00 2001 From: Alex Chau Date: Wed, 3 Feb 2021 16:20:04 +0000 Subject: [PATCH 0467/1664] Ensure isTablet is only enable when simulated landscape is disabled - Tablet UI (e.g. grid overview and two panel workspace) is built with assumption that simulated landscape is disabled, make sure this is the case in DeviceProfile - This is usually true as swDPs is generally smaller than sw used to determine xml files Bug: 174464656 Test: Try different combination of smallest width and Display Size, isTablet is only set when allow_rotation is true Change-Id: I3ee1c76909f29b6f14c4032be812bb8c9ea7e827 --- src/com/android/launcher3/DeviceProfile.java | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/src/com/android/launcher3/DeviceProfile.java b/src/com/android/launcher3/DeviceProfile.java index f681d75eef..4d5bd5d95f 100644 --- a/src/com/android/launcher3/DeviceProfile.java +++ b/src/com/android/launcher3/DeviceProfile.java @@ -168,8 +168,10 @@ public class DeviceProfile { // Constants from resources float swDPs = Utilities.dpiFromPx( Math.min(info.smallestSize.x, info.smallestSize.y), info.metrics); - isTablet = swDPs >= TABLET_MIN_DPS; - isLargeTablet = swDPs >= LARGE_TABLET_MIN_DPS; + boolean allowRotation = context.getResources().getBoolean(R.bool.allow_rotation); + // Tablet UI is built with assumption that simulated landscape is disabled. + isTablet = allowRotation && swDPs >= TABLET_MIN_DPS; + isLargeTablet = isTablet && swDPs >= LARGE_TABLET_MIN_DPS; isPhone = !isTablet && !isLargeTablet; aspectRatio = ((float) Math.max(widthPx, heightPx)) / Math.min(widthPx, heightPx); boolean isTallDevice = Float.compare(aspectRatio, TALL_DEVICE_ASPECT_RATIO_THRESHOLD) >= 0; -- GitLab From 63fc59b80108125eb405af7f245cc9711634ec5e Mon Sep 17 00:00:00 2001 From: Tony Wickham Date: Thu, 4 Feb 2021 08:36:08 -0800 Subject: [PATCH 0468/1664] Use SYSTEM_APPLICATION_OVERLAY instead of SYSTEM_ALERT_WINDOW for Taskbar Test: Taskbar still shows up and works, can't hide it from Settings Bug: 169054709 Change-Id: I0d2f9be8a0af2242472d8406bbc31a6aecaa9285 --- quickstep/AndroidManifest.xml | 2 +- .../src/com/android/launcher3/taskbar/TaskbarController.java | 1 + 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/quickstep/AndroidManifest.xml b/quickstep/AndroidManifest.xml index 9c14b8510e..7431551710 100644 --- a/quickstep/AndroidManifest.xml +++ b/quickstep/AndroidManifest.xml @@ -41,7 +41,7 @@ - + Date: Thu, 4 Feb 2021 13:05:07 -0600 Subject: [PATCH 0469/1664] Only show OOBE once Bug: 179333425 Test: Manual Change-Id: I23de8fffa31ae06b0f4feccfb75144c7c55143c2 --- .../launcher3/search/DeviceSearchEdu.java | 16 +++++++--------- 1 file changed, 7 insertions(+), 9 deletions(-) diff --git a/quickstep/src/com/android/launcher3/search/DeviceSearchEdu.java b/quickstep/src/com/android/launcher3/search/DeviceSearchEdu.java index 425e557ecb..71270cc94a 100644 --- a/quickstep/src/com/android/launcher3/search/DeviceSearchEdu.java +++ b/quickstep/src/com/android/launcher3/search/DeviceSearchEdu.java @@ -73,11 +73,9 @@ public class DeviceSearchEdu extends AbstractSlideInView implements } - private void close(boolean animate, boolean markAsSeen) { - handleClose(animate); - if (markAsSeen) { - mLauncher.getOnboardingPrefs().markChecked(SEARCH_EDU_SEEN); - } + private void dismiss() { + handleClose(true); + mLauncher.getOnboardingPrefs().markChecked(SEARCH_EDU_SEEN); } @Override @@ -110,7 +108,7 @@ public class DeviceSearchEdu extends AbstractSlideInView implements findViewById(R.id.dismiss_edu).setOnClickListener((view) -> { mSwitchFocusOnDismiss = true; - close(true, true); + dismiss(); }); } @@ -176,7 +174,7 @@ public class DeviceSearchEdu extends AbstractSlideInView implements @Override public void onStateTransitionStart(LauncherState toState) { - close(true, false); + dismiss(); } @Override @@ -203,7 +201,7 @@ public class DeviceSearchEdu extends AbstractSlideInView implements if (mSearchInput != null) { mSearchInput.setText(charSequence.toString()); mSwitchFocusOnDismiss = true; - close(true, true); + dismiss(); } } @@ -215,7 +213,7 @@ public class DeviceSearchEdu extends AbstractSlideInView implements @Override public boolean onEditorAction(TextView textView, int i, KeyEvent keyEvent) { mSearchInput.onEditorAction(i); - close(true, true); + dismiss(); return true; } } -- GitLab From 79e52fc23b68d300b497c1506aeeb9727ac25f39 Mon Sep 17 00:00:00 2001 From: Sunny Goyal Date: Wed, 3 Feb 2021 10:22:28 -0800 Subject: [PATCH 0470/1664] Moving slice loading on a background thread > Also fixing slice icon loading, such that the request is cancelled if views are recycled quickly > Loading widget label on dg thread and cancelling request if views are recycled quickly. Bug: 179068415 Test: verified locally Change-Id: Id5a524e2bf596862330a8170394aef9ffd708544 --- .../search/SearchResultIconSlice.java | 38 +-- .../launcher3/search/SearchResultWidget.java | 31 ++- src/com/android/launcher3/BubbleTextView.java | 4 +- src/com/android/launcher3/Launcher.java | 27 +-- .../allapps/AllAppsTransitionController.java | 3 - .../allapps/search/LiveSearchManager.java | 220 ++++++++++++++---- .../search/SearchWidgetInfoContainer.java | 6 - .../android/launcher3/icons/IconCache.java | 35 ++- 8 files changed, 235 insertions(+), 129 deletions(-) diff --git a/quickstep/src/com/android/launcher3/search/SearchResultIconSlice.java b/quickstep/src/com/android/launcher3/search/SearchResultIconSlice.java index c441e2291b..4bf34324a7 100644 --- a/quickstep/src/com/android/launcher3/search/SearchResultIconSlice.java +++ b/quickstep/src/com/android/launcher3/search/SearchResultIconSlice.java @@ -15,28 +15,22 @@ */ package com.android.launcher3.search; -import static com.android.launcher3.util.Executors.MAIN_EXECUTOR; -import static com.android.launcher3.util.Executors.MODEL_EXECUTOR; - import android.app.search.SearchTarget; import android.app.search.SearchTargetEvent; import android.content.Context; import android.util.AttributeSet; -import android.util.Log; import android.widget.LinearLayout; import androidx.annotation.NonNull; import androidx.annotation.Nullable; -import androidx.lifecycle.LiveData; -import androidx.slice.Slice; import androidx.slice.SliceItem; import androidx.slice.widget.EventInfo; import androidx.slice.widget.SliceView; import com.android.launcher3.Launcher; -import com.android.launcher3.LauncherAppState; import com.android.launcher3.R; import com.android.launcher3.model.data.PackageItemInfo; +import com.android.launcher3.util.SafeCloseable; import java.util.ArrayList; import java.util.List; @@ -47,13 +41,11 @@ import java.util.List; public class SearchResultIconSlice extends LinearLayout implements SearchTargetHandler, SliceView.OnSliceActionListener { - private static final String TAG = "SearchSliceController"; - private final Launcher mLauncher; private SliceView mSliceView; private SearchResultIcon mIcon; - private LiveData mSliceLiveData; + private SafeCloseable mSliceSession; private String mTargetId; public SearchResultIconSlice(Context context) { @@ -87,26 +79,20 @@ public class SearchResultIconSlice extends LinearLayout implements SearchTargetH mTargetId = parentTarget.getId(); reset(); updateIcon(parentTarget, children); - try { - mSliceLiveData = mLauncher.getLiveSearchManager().getSliceForUri( - parentTarget.getSliceUri()); - mSliceLiveData.observe(mLauncher, mSliceView); - } catch (Exception ex) { - Log.e(TAG, "unable to bind slice", ex); - } + mSliceSession = mLauncher.getLiveSearchManager() + .addObserver(parentTarget.getSliceUri(), mSliceView); } private void updateIcon(SearchTarget parentTarget, List children) { if (children.size() == 1) { mIcon.apply(children.get(0), new ArrayList<>()); } else { - LauncherAppState appState = LauncherAppState.getInstance(getContext()); - MODEL_EXECUTOR.post(() -> { - PackageItemInfo pkgItem = new PackageItemInfo(parentTarget.getPackageName()); - pkgItem.user = parentTarget.getUserHandle(); - appState.getIconCache().getTitleAndIconForApp(pkgItem, false); - MAIN_EXECUTOR.post(() -> mIcon.applyFromItemInfoWithIcon(pkgItem)); - }); + PackageItemInfo pkgItem = new PackageItemInfo(parentTarget.getPackageName()); + pkgItem.user = parentTarget.getUserHandle(); + if (!pkgItem.equals(mIcon.getTag())) { + // The icon will load and apply high res icon automatically + mIcon.applyFromItemInfoWithIcon(pkgItem); + } } } @@ -124,8 +110,8 @@ public class SearchResultIconSlice extends LinearLayout implements SearchTargetH private void reset() { mSliceView.setOnSliceActionListener(null); - if (mSliceLiveData != null) { - mSliceLiveData.removeObservers(mLauncher); + if (mSliceSession != null) { + mSliceSession.close(); } } diff --git a/quickstep/src/com/android/launcher3/search/SearchResultWidget.java b/quickstep/src/com/android/launcher3/search/SearchResultWidget.java index 4c64265cc3..e22f6ab992 100644 --- a/quickstep/src/com/android/launcher3/search/SearchResultWidget.java +++ b/quickstep/src/com/android/launcher3/search/SearchResultWidget.java @@ -15,6 +15,9 @@ */ package com.android.launcher3.search; +import static com.android.launcher3.util.Executors.MAIN_EXECUTOR; +import static com.android.launcher3.util.Executors.MODEL_EXECUTOR; + import android.app.search.SearchTarget; import android.app.search.SearchTargetEvent; import android.appwidget.AppWidgetHostView; @@ -36,16 +39,19 @@ import com.android.launcher3.BubbleTextView; import com.android.launcher3.CheckLongPressHelper; import com.android.launcher3.DeviceProfile; import com.android.launcher3.Launcher; +import com.android.launcher3.LauncherAppState; +import com.android.launcher3.LauncherAppWidgetProviderInfo; import com.android.launcher3.R; +import com.android.launcher3.Utilities; import com.android.launcher3.allapps.search.SearchWidgetInfoContainer; import com.android.launcher3.dragndrop.DraggableView; +import com.android.launcher3.icons.cache.HandlerRunnable; import com.android.launcher3.model.data.PackageItemInfo; import com.android.launcher3.touch.ItemLongClickListener; import com.android.launcher3.widget.PendingAddWidgetInfo; import java.util.List; - /** * displays live version of a widget upon receiving {@link AppWidgetProviderInfo} from Search * provider @@ -63,6 +69,7 @@ public class SearchResultWidget extends LinearLayout implements SearchTargetHand private final float mScaleToFit; private SearchWidgetInfoContainer mInfoContainer; + private HandlerRunnable mLabelRequest; private BubbleTextView mWidgetProvider; private TextView mWidgetLabel; @@ -124,11 +131,18 @@ public class SearchResultWidget extends LinearLayout implements SearchTargetHand } private void showWidgetInfo(AppWidgetProviderInfo providerInfo) { - String title = providerInfo.loadLabel(mLauncher.getPackageManager()); PackageItemInfo pinfo = new PackageItemInfo(providerInfo.provider.getPackageName()); pinfo.user = providerInfo.getProfile(); mWidgetProvider.applyFromItemInfoWithIcon(pinfo); - mWidgetLabel.setText(title); + + mLabelRequest = new HandlerRunnable<>( + MODEL_EXECUTOR.getHandler(), + () -> LauncherAppState.getInstance(mLauncher).getIconCache() + .getTitleNoCache(LauncherAppWidgetProviderInfo + .fromProviderInfo(mLauncher, providerInfo)), + MAIN_EXECUTOR, + mWidgetLabel::setText); + Utilities.postAsyncCallback(MODEL_EXECUTOR.getHandler(), mLabelRequest); } /** @@ -137,9 +151,20 @@ public class SearchResultWidget extends LinearLayout implements SearchTargetHand public void removeListener() { if (mInfoContainer != null) { mInfoContainer.detachWidget(mHostView); + mInfoContainer = null; + } + if (mLabelRequest != null) { + mLabelRequest.cancel(); + mLabelRequest = null; } } + @Override + protected void onDetachedFromWindow() { + super.onDetachedFromWindow(); + removeListener(); + } + private void reportEvent(int eventType) { SearchSessionTracker.INSTANCE.get(getContext()).notifyEvent( new SearchTargetEvent.Builder("search target id", eventType).build()); diff --git a/src/com/android/launcher3/BubbleTextView.java b/src/com/android/launcher3/BubbleTextView.java index 5007ffc7be..2367249163 100644 --- a/src/com/android/launcher3/BubbleTextView.java +++ b/src/com/android/launcher3/BubbleTextView.java @@ -66,9 +66,9 @@ import com.android.launcher3.graphics.IconShape; import com.android.launcher3.graphics.PlaceHolderIconDrawable; import com.android.launcher3.graphics.PreloadIconDrawable; import com.android.launcher3.icons.DotRenderer; -import com.android.launcher3.icons.IconCache.IconLoadRequest; import com.android.launcher3.icons.IconCache.ItemInfoUpdateReceiver; import com.android.launcher3.icons.LauncherIcons; +import com.android.launcher3.icons.cache.HandlerRunnable; import com.android.launcher3.model.data.AppInfo; import com.android.launcher3.model.data.ItemInfo; import com.android.launcher3.model.data.ItemInfoWithIcon; @@ -171,7 +171,7 @@ public class BubbleTextView extends TextView implements ItemInfoUpdateReceiver, @ViewDebug.ExportedProperty(category = "launcher") private boolean mDisableRelayout = false; - private IconLoadRequest mIconLoadRequest; + private HandlerRunnable mIconLoadRequest; private boolean mEnableIconUpdateAnimation = false; diff --git a/src/com/android/launcher3/Launcher.java b/src/com/android/launcher3/Launcher.java index 2334267fd9..36963f19dd 100644 --- a/src/com/android/launcher3/Launcher.java +++ b/src/com/android/launcher3/Launcher.java @@ -98,13 +98,9 @@ import android.widget.ImageView; import android.widget.Toast; import androidx.annotation.CallSuper; -import androidx.annotation.NonNull; import androidx.annotation.Nullable; import androidx.annotation.StringRes; import androidx.annotation.VisibleForTesting; -import androidx.lifecycle.Lifecycle; -import androidx.lifecycle.LifecycleOwner; -import androidx.lifecycle.LifecycleRegistry; import com.android.launcher3.DropTarget.DragObject; import com.android.launcher3.accessibility.LauncherAccessibilityDelegate; @@ -211,8 +207,7 @@ import java.util.stream.Stream; * Default launcher application. */ public class Launcher extends StatefulActivity implements LauncherExterns, - Callbacks, InvariantDeviceProfile.OnIDPChangeListener, PluginListener, - LifecycleOwner { + Callbacks, InvariantDeviceProfile.OnIDPChangeListener, PluginListener { public static final String TAG = "Launcher"; public static final ActivityTracker ACTIVITY_TRACKER = new ActivityTracker<>(); @@ -275,8 +270,6 @@ public class Launcher extends StatefulActivity implements Launche private LauncherAppTransitionManager mAppTransitionManager; private Configuration mOldConfig; - private LifecycleRegistry mLifecycleRegistry; - private LiveSearchManager mLiveSearchManager; @Thunk @@ -392,12 +385,12 @@ public class Launcher extends StatefulActivity implements Launche mIconCache = app.getIconCache(); mAccessibilityDelegate = new LauncherAccessibilityDelegate(this); - mLiveSearchManager = new LiveSearchManager(this); - mDragController = new DragController(this); mAllAppsController = new AllAppsTransitionController(this); mStateManager = new StateManager<>(this, NORMAL); + mLiveSearchManager = new LiveSearchManager(this); + mOnboardingPrefs = createOnboardingPrefs(mSharedPrefs); mAppWidgetManager = new WidgetManagerHelper(this); @@ -486,15 +479,6 @@ public class Launcher extends StatefulActivity implements Launche if (Utilities.ATLEAST_R) { getWindow().setSoftInputMode(LayoutParams.SOFT_INPUT_ADJUST_NOTHING); } - - mLifecycleRegistry = new LifecycleRegistry(this); - mLifecycleRegistry.setCurrentState(Lifecycle.State.CREATED); - } - - @NonNull - @Override - public Lifecycle getLifecycle() { - return mLifecycleRegistry; } public LiveSearchManager getLiveSearchManager() { @@ -913,7 +897,6 @@ public class Launcher extends StatefulActivity implements Launche @Override protected void onStop() { - mLifecycleRegistry.setCurrentState(Lifecycle.State.CREATED); super.onStop(); if (mDeferOverlayCallbacks) { checkIfOverlayStillDeferred(); @@ -937,7 +920,6 @@ public class Launcher extends StatefulActivity implements Launche mAppWidgetHost.setListenIfResumed(true); TraceHelper.INSTANCE.endSection(traceToken); - mLifecycleRegistry.setCurrentState(Lifecycle.State.STARTED); } @Override @@ -1091,7 +1073,6 @@ public class Launcher extends StatefulActivity implements Launche } TraceHelper.INSTANCE.endSection(traceToken); - mLifecycleRegistry.setCurrentState(Lifecycle.State.RESUMED); } @Override @@ -1099,7 +1080,6 @@ public class Launcher extends StatefulActivity implements Launche // Ensure that items added to Launcher are queued until Launcher returns ItemInstallQueue.INSTANCE.get(this).pauseModelPush(FLAG_ACTIVITY_PAUSED); - mLifecycleRegistry.setCurrentState(Lifecycle.State.STARTED); super.onPause(); mDragController.cancelDrag(); mLastTouchUpTime = -1; @@ -1598,7 +1578,6 @@ public class Launcher extends StatefulActivity implements Launche mAppTransitionManager.unregisterRemoteAnimations(); mAppTransitionManager.unregisterRemoteTransitions(); mUserChangedCallbackCloseable.close(); - mLifecycleRegistry.setCurrentState(Lifecycle.State.DESTROYED); mLiveSearchManager.stop(); } diff --git a/src/com/android/launcher3/allapps/AllAppsTransitionController.java b/src/com/android/launcher3/allapps/AllAppsTransitionController.java index 2f805fd550..a4e1f271fb 100644 --- a/src/com/android/launcher3/allapps/AllAppsTransitionController.java +++ b/src/com/android/launcher3/allapps/AllAppsTransitionController.java @@ -268,13 +268,10 @@ public class AllAppsTransitionController implements StateHandler, && !FeatureFlags.DISABLE_INITIAL_IME_IN_ALLAPPS.get() && BuildCompat.isAtLeastR()) { mInsetController.onAnimationEnd(mProgress); if (Float.compare(mProgress, 0f) == 0) { - mLauncher.getLiveSearchManager().start(); EditText editText = mAppsView.getSearchUiManager().getEditText(); if (editText != null && !mInsetController.showSearchEduIfNecessary()) { editText.requestFocus(); } - } else { - mLauncher.getLiveSearchManager().stop(); } // TODO: should make the controller hide synchronously } diff --git a/src/com/android/launcher3/allapps/search/LiveSearchManager.java b/src/com/android/launcher3/allapps/search/LiveSearchManager.java index c2f0b960c8..71aedb8272 100644 --- a/src/com/android/launcher3/allapps/search/LiveSearchManager.java +++ b/src/com/android/launcher3/allapps/search/LiveSearchManager.java @@ -15,8 +15,14 @@ */ package com.android.launcher3.allapps.search; +import static com.android.launcher3.LauncherState.ALL_APPS; +import static com.android.launcher3.util.Executors.MAIN_EXECUTOR; +import static com.android.launcher3.util.Executors.THREAD_POOL_EXECUTOR; +import static com.android.launcher3.util.Executors.UI_HELPER_EXECUTOR; import static com.android.launcher3.widget.WidgetHostViewLoader.getDefaultOptionsForWidget; +import android.app.Activity; +import android.app.Application.ActivityLifecycleCallbacks; import android.appwidget.AppWidgetHost; import android.appwidget.AppWidgetHostView; import android.appwidget.AppWidgetManager; @@ -26,39 +32,49 @@ import android.content.Context; import android.net.Uri; import android.os.Bundle; import android.os.UserHandle; +import android.util.Log; -import androidx.lifecycle.LiveData; +import androidx.annotation.Nullable; +import androidx.annotation.UiThread; +import androidx.annotation.WorkerThread; +import androidx.lifecycle.Observer; import androidx.slice.Slice; -import androidx.slice.widget.SliceLiveData; +import androidx.slice.SliceViewManager; +import androidx.slice.SliceViewManager.SliceCallback; import com.android.launcher3.Launcher; import com.android.launcher3.LauncherAppWidgetProviderInfo; +import com.android.launcher3.LauncherState; import com.android.launcher3.logging.InstanceId; -import com.android.launcher3.logging.InstanceIdSequence; +import com.android.launcher3.statemanager.StateManager.StateListener; import com.android.launcher3.util.ComponentKey; +import com.android.launcher3.util.SafeCloseable; import com.android.launcher3.widget.PendingAddWidgetInfo; +import java.util.ArrayList; import java.util.HashMap; import java.util.Optional; /** * Manages Lifecycle for Live search results */ -public class LiveSearchManager { +public class LiveSearchManager implements StateListener { + + private static final String TAG = "LiveSearchManager"; public static final int SEARCH_APPWIDGET_HOST_ID = 2048; private final Launcher mLauncher; - private final AppWidgetManager mAppWidgetManger; + private final HashMap mUriSliceMap = new HashMap<>(); + private final HashMap mWidgetPlaceholders = new HashMap<>(); - private final HashMap> mUriSliceMap = new HashMap<>(); private SearchWidgetHost mSearchWidgetHost; private InstanceId mLogInstanceId; public LiveSearchManager(Launcher launcher) { mLauncher = launcher; - mAppWidgetManger = AppWidgetManager.getInstance(launcher); + mLauncher.getStateManager().addStateListener(this); } /** @@ -67,78 +83,80 @@ public class LiveSearchManager { */ public SearchWidgetInfoContainer getPlaceHolderWidget(AppWidgetProviderInfo providerInfo) { if (mSearchWidgetHost == null) { - throw new RuntimeException("AppWidgetHost has not been created yet"); + mSearchWidgetHost = new SearchWidgetHost(mLauncher); + mSearchWidgetHost.startListening(); } ComponentName provider = providerInfo.provider; UserHandle userHandle = providerInfo.getProfile(); ComponentKey key = new ComponentKey(provider, userHandle); - SearchWidgetInfoContainer view = mWidgetPlaceholders.getOrDefault(key, null); if (mWidgetPlaceholders.containsKey(key)) { return mWidgetPlaceholders.get(key); } + LauncherAppWidgetProviderInfo pinfo = LauncherAppWidgetProviderInfo.fromProviderInfo( mLauncher, providerInfo); PendingAddWidgetInfo pendingAddWidgetInfo = new PendingAddWidgetInfo(pinfo); Bundle options = getDefaultOptionsForWidget(mLauncher, pendingAddWidgetInfo); int appWidgetId = mSearchWidgetHost.allocateAppWidgetId(); - boolean success = mAppWidgetManger.bindAppWidgetIdIfAllowed(appWidgetId, userHandle, - provider, options); + boolean success = AppWidgetManager.getInstance(mLauncher) + .bindAppWidgetIdIfAllowed(appWidgetId, userHandle, provider, options); if (!success) { + mSearchWidgetHost.deleteAppWidgetId(appWidgetId); mWidgetPlaceholders.put(key, null); return null; } - view = (SearchWidgetInfoContainer) mSearchWidgetHost.createView(mLauncher, appWidgetId, - providerInfo); + SearchWidgetInfoContainer view = (SearchWidgetInfoContainer) mSearchWidgetHost.createView( + mLauncher, appWidgetId, providerInfo); view.setTag(pendingAddWidgetInfo); mWidgetPlaceholders.put(key, view); return view; } - /** - * Creates {@link LiveData} from Slice Uri. Caches created live data to be reused - * within the same search session. Removes previous observers when new SliceView request a - * live data for observation. - */ - public LiveData getSliceForUri(Uri sliceUri) { - LiveData sliceLiveData = mUriSliceMap.getOrDefault(sliceUri, null); - if (sliceLiveData == null) { - sliceLiveData = SliceLiveData.fromUri(mLauncher, sliceUri); - mUriSliceMap.put(sliceUri, sliceLiveData); - } - return sliceLiveData; - } - - /** - * Start search session - */ - public void start() { - stop(); - mLogInstanceId = new InstanceIdSequence().newInstanceId(); - mSearchWidgetHost = new SearchWidgetHost(mLauncher); - mSearchWidgetHost.startListening(); - } - /** * Stop search session */ public void stop() { + clearWidgetHost(); + } + + private void clearWidgetHost() { if (mSearchWidgetHost != null) { mSearchWidgetHost.stopListening(); + mSearchWidgetHost.clearViews(); mSearchWidgetHost.deleteHost(); - for (SearchWidgetInfoContainer placeholder : mWidgetPlaceholders.values()) { - placeholder.clearListeners(); - } mWidgetPlaceholders.clear(); mSearchWidgetHost = null; } - for (LiveData liveData : mUriSliceMap.values()) { - liveData.removeObservers(mLauncher); + } + + @Override + public void onStateTransitionComplete(LauncherState finalState) { + if (finalState != ALL_APPS) { + // Clear all search session related objects + mUriSliceMap.values().forEach(SliceLifeCycle::destroy); + mUriSliceMap.clear(); + + clearWidgetHost(); } - mUriSliceMap.clear(); + } + + /** + * Adds a new observer for the provided uri and returns a callback to cancel this observer + */ + public SafeCloseable addObserver(Uri uri, Observer listener) { + SliceLifeCycle slc = mUriSliceMap.get(uri); + if (slc == null) { + slc = new SliceLifeCycle(uri, mLauncher); + mUriSliceMap.put(uri, slc); + } + slc.addListener(listener); + + final SliceLifeCycle sliceLifeCycle = slc; + return () -> sliceLifeCycle.removeListener(listener); } /** @@ -159,5 +177,121 @@ public class LiveSearchManager { AppWidgetProviderInfo appWidget) { return new SearchWidgetInfoContainer(context); } + + @Override + public void clearViews() { + super.clearViews(); + } + } + + private static class SliceLifeCycle + implements ActivityLifecycleCallbacks, SliceCallback { + + private final Uri mUri; + private final Launcher mLauncher; + private final SliceViewManager mSliceViewManager; + private final ArrayList> mListeners = new ArrayList<>(); + + private boolean mDestroyed = false; + private boolean mWasListening = false; + + SliceLifeCycle(Uri uri, Launcher launcher) { + mUri = uri; + mLauncher = launcher; + mSliceViewManager = SliceViewManager.getInstance(launcher); + launcher.registerActivityLifecycleCallbacks(this); + + if (launcher.isDestroyed()) { + onActivityDestroyed(launcher); + } else if (launcher.isStarted()) { + onActivityStarted(launcher); + } + } + + @Override + public void onActivityDestroyed(Activity activity) { + destroy(); + } + + @Override + public void onActivityStarted(Activity activity) { + updateListening(); + } + + @Override + public void onActivityStopped(Activity activity) { + updateListening(); + } + + private void updateListening() { + boolean isListening = mDestroyed + ? false + : (mLauncher.isStarted() && !mListeners.isEmpty()); + UI_HELPER_EXECUTOR.execute(() -> uploadListeningBg(isListening)); + } + + @WorkerThread + private void uploadListeningBg(boolean isListening) { + if (mWasListening != isListening) { + mWasListening = isListening; + if (isListening) { + mSliceViewManager.registerSliceCallback(mUri, MAIN_EXECUTOR, this); + // Update slice one-time on the different thread so that we can display + // multiple slices in parallel + THREAD_POOL_EXECUTOR.execute(this::updateSlice); + } else { + mSliceViewManager.unregisterSliceCallback(mUri, this); + } + } + } + + @UiThread + private void addListener(Observer listener) { + mListeners.add(listener); + updateListening(); + } + + @UiThread + private void removeListener(Observer listener) { + mListeners.remove(listener); + updateListening(); + } + + @WorkerThread + private void updateSlice() { + try { + Slice s = mSliceViewManager.bindSlice(mUri); + MAIN_EXECUTOR.execute(() -> onSliceUpdated(s)); + } catch (Exception e) { + Log.d(TAG, "Error fetching slice", e); + } + } + + @UiThread + @Override + public void onSliceUpdated(@Nullable Slice s) { + mListeners.forEach(l -> l.onChanged(s)); + } + + private void destroy() { + if (mDestroyed) { + return; + } + mDestroyed = true; + mLauncher.unregisterActivityLifecycleCallbacks(this); + mListeners.clear(); + } + + @Override + public void onActivityCreated(Activity activity, Bundle bundle) { } + + @Override + public void onActivityPaused(Activity activity) { } + + @Override + public void onActivityResumed(Activity activity) { } + + @Override + public void onActivitySaveInstanceState(Activity activity, Bundle bundle) { } } } diff --git a/src/com/android/launcher3/allapps/search/SearchWidgetInfoContainer.java b/src/com/android/launcher3/allapps/search/SearchWidgetInfoContainer.java index b5c22688e5..8e5f8cb9c0 100644 --- a/src/com/android/launcher3/allapps/search/SearchWidgetInfoContainer.java +++ b/src/com/android/launcher3/allapps/search/SearchWidgetInfoContainer.java @@ -70,10 +70,4 @@ public class SearchWidgetInfoContainer extends AppWidgetHostView { mListeners.remove(hostView); } - /** - * Removes all AppWidgetHost update listeners - */ - public void clearListeners() { - mListeners.clear(); - } } diff --git a/src/com/android/launcher3/icons/IconCache.java b/src/com/android/launcher3/icons/IconCache.java index 80135571b2..61f2c2adae 100644 --- a/src/com/android/launcher3/icons/IconCache.java +++ b/src/com/android/launcher3/icons/IconCache.java @@ -31,7 +31,6 @@ import android.content.pm.PackageManager; import android.content.pm.PackageManager.NameNotFoundException; import android.content.pm.ShortcutInfo; import android.graphics.drawable.Drawable; -import android.os.Handler; import android.os.Process; import android.os.UserHandle; import android.util.Log; @@ -134,7 +133,7 @@ public class IconCache extends BaseIconCache { * Fetches high-res icon for the provided ItemInfo and updates the caller when done. * @return a request ID that can be used to cancel the request. */ - public IconLoadRequest updateIconInBackground(final ItemInfoUpdateReceiver caller, + public HandlerRunnable updateIconInBackground(final ItemInfoUpdateReceiver caller, final ItemInfoWithIcon info) { Preconditions.assertUIThread(); if (mPendingIconRequestCount <= 0) { @@ -142,20 +141,18 @@ public class IconCache extends BaseIconCache { } mPendingIconRequestCount ++; - IconLoadRequest request = new IconLoadRequest(mWorkerHandler, this::onIconRequestEnd) { - @Override - public void run() { - if (info instanceof AppInfo || info instanceof WorkspaceItemInfo) { - getTitleAndIcon(info, false); - } else if (info instanceof PackageItemInfo) { - getTitleAndIconForApp((PackageItemInfo) info, false); - } - MAIN_EXECUTOR.execute(() -> { - caller.reapplyItemInfo(info); - onEnd(); - }); - } - }; + HandlerRunnable request = new HandlerRunnable<>(mWorkerHandler, + () -> { + if (info instanceof AppInfo || info instanceof WorkspaceItemInfo) { + getTitleAndIcon(info, false); + } else if (info instanceof PackageItemInfo) { + getTitleAndIconForApp((PackageItemInfo) info, false); + } + return info; + }, + MAIN_EXECUTOR, + caller::reapplyItemInfo, + this::onIconRequestEnd); Utilities.postAsyncCallback(mWorkerHandler, request); return request; } @@ -336,12 +333,6 @@ public class IconCache extends BaseIconCache { return super.getEntryFromDB(cacheKey, entry, lowRes); } - public static abstract class IconLoadRequest extends HandlerRunnable { - IconLoadRequest(Handler handler, Runnable endRunnable) { - super(handler, endRunnable); - } - } - /** * Interface for receiving itemInfo with high-res icon. */ -- GitLab From 4fc871f9776606799538fe08bfd62069e692d206 Mon Sep 17 00:00:00 2001 From: thiruram Date: Wed, 3 Feb 2021 14:55:23 -0800 Subject: [PATCH 0471/1664] [AA+] Add LAUNCHER_ALLAPPS_ENTRY, LAUNCHER_ALLAPPS_EXIT events. This would help us to define clear start and end events for AllApps sessions on the server side. Bug: 178562918 Change-Id: I77f236c9bc1a21e868545b1309e47d91d6fc1d96 --- .../allapps/search/LiveSearchManager.java | 19 +++++++++++++++++++ .../launcher3/logging/StatsLogManager.java | 6 ++++++ 2 files changed, 25 insertions(+) diff --git a/src/com/android/launcher3/allapps/search/LiveSearchManager.java b/src/com/android/launcher3/allapps/search/LiveSearchManager.java index 71aedb8272..e52c79049f 100644 --- a/src/com/android/launcher3/allapps/search/LiveSearchManager.java +++ b/src/com/android/launcher3/allapps/search/LiveSearchManager.java @@ -16,6 +16,8 @@ package com.android.launcher3.allapps.search; import static com.android.launcher3.LauncherState.ALL_APPS; +import static com.android.launcher3.logging.StatsLogManager.LauncherEvent.LAUNCHER_ALLAPPS_ENTRY; +import static com.android.launcher3.logging.StatsLogManager.LauncherEvent.LAUNCHER_ALLAPPS_EXIT; import static com.android.launcher3.util.Executors.MAIN_EXECUTOR; import static com.android.launcher3.util.Executors.THREAD_POOL_EXECUTOR; import static com.android.launcher3.util.Executors.UI_HELPER_EXECUTOR; @@ -46,6 +48,8 @@ import com.android.launcher3.Launcher; import com.android.launcher3.LauncherAppWidgetProviderInfo; import com.android.launcher3.LauncherState; import com.android.launcher3.logging.InstanceId; +import com.android.launcher3.logging.InstanceIdSequence; +import com.android.launcher3.logging.StatsLogManager.StatsLogger; import com.android.launcher3.statemanager.StateManager.StateListener; import com.android.launcher3.util.ComponentKey; import com.android.launcher3.util.SafeCloseable; @@ -71,6 +75,7 @@ public class LiveSearchManager implements StateListener { new HashMap<>(); private SearchWidgetHost mSearchWidgetHost; private InstanceId mLogInstanceId; + private LauncherState mPrevLauncherState; public LiveSearchManager(Launcher launcher) { mLauncher = launcher; @@ -133,6 +138,11 @@ public class LiveSearchManager implements StateListener { } } + @Override + public void onStateTransitionStart(LauncherState toState) { + mPrevLauncherState = mLauncher.getStateManager().getCurrentStableState(); + } + @Override public void onStateTransitionComplete(LauncherState finalState) { if (finalState != ALL_APPS) { @@ -142,6 +152,15 @@ public class LiveSearchManager implements StateListener { clearWidgetHost(); } + + StatsLogger logger = mLauncher.getStatsLogManager().logger(); + if (finalState.equals(ALL_APPS)) { + mLogInstanceId = new InstanceIdSequence().newInstanceId(); + logger.withInstanceId(mLogInstanceId).log(LAUNCHER_ALLAPPS_ENTRY); + } else if (mPrevLauncherState.equals(ALL_APPS)) { + logger.withInstanceId(mLogInstanceId).log(LAUNCHER_ALLAPPS_EXIT); + mLogInstanceId = null; + } } /** diff --git a/src/com/android/launcher3/logging/StatsLogManager.java b/src/com/android/launcher3/logging/StatsLogManager.java index 2066cd3d71..0292d20ef5 100644 --- a/src/com/android/launcher3/logging/StatsLogManager.java +++ b/src/com/android/launcher3/logging/StatsLogManager.java @@ -344,6 +344,12 @@ public class StatsLogManager implements ResourceBasedOverride { @UiEvent(doc = "Current grid size is changed to 2.") LAUNCHER_GRID_SIZE_2(665), + + @UiEvent(doc = "Launcher entered into AllApps state.") + LAUNCHER_ALLAPPS_ENTRY(692), + + @UiEvent(doc = "Launcher exited from AllApps state.") + LAUNCHER_ALLAPPS_EXIT(693), ; // ADD MORE -- GitLab From 993fed23465a10bc76f46948f8910154fafa2496 Mon Sep 17 00:00:00 2001 From: Lucas Dupin Date: Thu, 4 Feb 2021 15:40:45 -0800 Subject: [PATCH 0472/1664] Style updates - Make pop ups use system colors - Text colors in dark theme should also use system colors Test: visual Bug: 179427049 Change-Id: I9c0eb2b6970ff9bf66fe1d5c0040b10bfa2087b0 --- res/values-v31/colors.xml | 33 +++++++++++++++++++++++++++++++++ res/values/colors.xml | 15 +++++++++++++++ res/values/styles.xml | 24 ++++++++++++------------ 3 files changed, 60 insertions(+), 12 deletions(-) create mode 100644 res/values-v31/colors.xml diff --git a/res/values-v31/colors.xml b/res/values-v31/colors.xml new file mode 100644 index 0000000000..b8600a60ed --- /dev/null +++ b/res/values-v31/colors.xml @@ -0,0 +1,33 @@ + + + + @android:color/system_main_50 + @android:color/system_main_100 + @android:color/system_main_300 + @android:color/system_main_800 + @android:color/system_main_900 + @android:color/system_main_700 + + @android:color/system_main_50 + @android:color/system_main_900 + + @android:color/system_main_50 + @android:color/system_main_200 + @android:color/system_main_400 + \ No newline at end of file diff --git a/res/values/colors.xml b/res/values/colors.xml index 78c2df69e0..0b30253327 100644 --- a/res/values/colors.xml +++ b/res/values/colors.xml @@ -40,4 +40,19 @@ #9CCC65 #FFFFFFFF #1A73E8 + + #FFF + #F1F3F4 + #E0E0E0 + #3C4043 + #202124 + #757575 + + #FFF + #FF212121 + + #FFFFFFFF + #FFFFFFFF + #CCFFFFFF + diff --git a/res/values/styles.xml b/res/values/styles.xml index dc7182fe90..adc22381a8 100644 --- a/res/values/styles.xml +++ b/res/values/styles.xml @@ -35,12 +35,12 @@ 46 #66FFFFFF @style/AllAppsTheme - #FFF - #F1F3F4 - #E0E0E0 + @color/popup_color_primary_light + @color/popup_color_secondary_light + @color/popup_color_tertiary_light false false - @android:color/white + @color/workspace_text_color_light #B0000000 #33000000 #44000000 @@ -74,7 +74,7 @@ \ No newline at end of file diff --git a/quickstep/src/com/android/launcher3/BaseQuickstepLauncher.java b/quickstep/src/com/android/launcher3/BaseQuickstepLauncher.java index edcd0a203a..d1fa2fd3ef 100644 --- a/quickstep/src/com/android/launcher3/BaseQuickstepLauncher.java +++ b/quickstep/src/com/android/launcher3/BaseQuickstepLauncher.java @@ -30,7 +30,6 @@ import android.content.Intent; import android.content.IntentSender; import android.os.Bundle; import android.os.CancellationSignal; -import android.view.LayoutInflater; import android.view.View; import androidx.annotation.Nullable; @@ -43,7 +42,7 @@ import com.android.launcher3.proxy.StartActivityParams; import com.android.launcher3.statehandlers.BackButtonAlphaHandler; import com.android.launcher3.statehandlers.DepthController; import com.android.launcher3.statemanager.StateManager.StateHandler; -import com.android.launcher3.taskbar.TaskbarContainerView; +import com.android.launcher3.taskbar.TaskbarActivityContext; import com.android.launcher3.taskbar.TaskbarController; import com.android.launcher3.taskbar.TaskbarStateHandler; import com.android.launcher3.uioverrides.RecentsViewStateController; @@ -207,6 +206,7 @@ public abstract class BaseQuickstepLauncher extends Launcher mActionsView.updateVerticalMargin(SysUINavigationMode.getMode(this)); addTaskbarIfNecessary(); + addOnDeviceProfileChangeListener(newDp -> addTaskbarIfNecessary()); } @Override @@ -223,9 +223,9 @@ public abstract class BaseQuickstepLauncher extends Launcher mTaskbarController = null; } if (FeatureFlags.ENABLE_TASKBAR.get() && mDeviceProfile.isTablet) { - TaskbarContainerView taskbarContainer = (TaskbarContainerView) LayoutInflater.from(this) - .inflate(R.layout.taskbar, null, false); - mTaskbarController = new TaskbarController(this, taskbarContainer); + TaskbarActivityContext taskbarActivityContext = new TaskbarActivityContext(this); + mTaskbarController = new TaskbarController(this, + taskbarActivityContext.getTaskbarContainerView()); mTaskbarController.init(); } } diff --git a/quickstep/src/com/android/launcher3/taskbar/TaskbarActivityContext.java b/quickstep/src/com/android/launcher3/taskbar/TaskbarActivityContext.java new file mode 100644 index 0000000000..06372fe22f --- /dev/null +++ b/quickstep/src/com/android/launcher3/taskbar/TaskbarActivityContext.java @@ -0,0 +1,78 @@ +/* + * Copyright (C) 2021 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.taskbar; + +import android.content.ContextWrapper; +import android.graphics.Rect; +import android.view.LayoutInflater; + +import com.android.launcher3.BaseQuickstepLauncher; +import com.android.launcher3.DeviceProfile; +import com.android.launcher3.R; +import com.android.launcher3.views.ActivityContext; +import com.android.launcher3.views.BaseDragLayer; + +/** + * The {@link ActivityContext} with which we inflate Taskbar-related Views. This allows UI elements + * that are used by both Launcher and Taskbar (such as Folder) to reference a generic + * ActivityContext and BaseDragLayer instead of the Launcher activity and its DragLayer. + */ +public class TaskbarActivityContext extends ContextWrapper implements ActivityContext { + + private final DeviceProfile mDeviceProfile; + private final LayoutInflater mLayoutInflater; + private final TaskbarContainerView mTaskbarContainerView; + + public TaskbarActivityContext(BaseQuickstepLauncher launcher) { + super(launcher); + mDeviceProfile = launcher.getDeviceProfile().copy(this); + float taskbarIconSize = getResources().getDimension(R.dimen.taskbar_icon_size); + float iconScale = taskbarIconSize / mDeviceProfile.iconSizePx; + mDeviceProfile.updateIconSize(iconScale, getResources()); + + mLayoutInflater = LayoutInflater.from(this).cloneInContext(this); + + mTaskbarContainerView = (TaskbarContainerView) mLayoutInflater + .inflate(R.layout.taskbar, null, false); + } + + public TaskbarContainerView getTaskbarContainerView() { + return mTaskbarContainerView; + } + + /** + * @return A LayoutInflater to use in this Context. Views inflated with this LayoutInflater will + * be able to access this TaskbarActivityContext via ActivityContext.lookupContext(). + */ + public LayoutInflater getLayoutInflater() { + return mLayoutInflater; + } + + @Override + public BaseDragLayer getDragLayer() { + return mTaskbarContainerView; + } + + @Override + public DeviceProfile getDeviceProfile() { + return mDeviceProfile; + } + + @Override + public Rect getFolderBoundingBox() { + return mTaskbarContainerView.getFolderBoundingBox(); + } +} diff --git a/quickstep/src/com/android/launcher3/taskbar/TaskbarContainerView.java b/quickstep/src/com/android/launcher3/taskbar/TaskbarContainerView.java index 3b361c4a38..ddd0d15442 100644 --- a/quickstep/src/com/android/launcher3/taskbar/TaskbarContainerView.java +++ b/quickstep/src/com/android/launcher3/taskbar/TaskbarContainerView.java @@ -19,19 +19,29 @@ import static com.android.systemui.shared.system.ViewTreeObserverWrapper.InsetsI import static com.android.systemui.shared.system.ViewTreeObserverWrapper.InsetsInfo.TOUCHABLE_INSETS_REGION; import android.content.Context; +import android.graphics.Rect; import android.util.AttributeSet; -import android.widget.FrameLayout; +import android.view.View; import androidx.annotation.NonNull; import androidx.annotation.Nullable; +import com.android.launcher3.R; import com.android.launcher3.anim.AlphaUpdateListener; +import com.android.launcher3.util.TouchController; +import com.android.launcher3.views.BaseDragLayer; import com.android.systemui.shared.system.ViewTreeObserverWrapper; /** * Top-level ViewGroup that hosts the TaskbarView as well as Views created by it such as Folder. */ -public class TaskbarContainerView extends FrameLayout { +public class TaskbarContainerView extends BaseDragLayer { + + private final int[] mTempLoc = new int[2]; + private final int mFolderMargin; + + // Initialized in TaskbarController constructor. + private TaskbarController.TaskbarContainerViewCallbacks mControllerCallbacks; // Initialized in init. private TaskbarView mTaskbarView; @@ -52,12 +62,23 @@ public class TaskbarContainerView extends FrameLayout { public TaskbarContainerView(@NonNull Context context, @Nullable AttributeSet attrs, int defStyleAttr, int defStyleRes) { - super(context, attrs, defStyleAttr, defStyleRes); + super(context, attrs, 1 /* alphaChannelCount */); + mFolderMargin = getResources().getDimensionPixelSize(R.dimen.taskbar_folder_margin); + } + + protected void construct(TaskbarController.TaskbarContainerViewCallbacks callbacks) { + mControllerCallbacks = callbacks; } protected void init(TaskbarView taskbarView) { mTaskbarView = taskbarView; mTaskbarInsetsComputer = createTaskbarInsetsComputer(); + recreateControllers(); + } + + @Override + public void recreateControllers() { + mControllers = new TouchController[0]; } private ViewTreeObserverWrapper.OnComputeInsetsListener createTaskbarInsetsComputer() { @@ -70,6 +91,17 @@ public class TaskbarContainerView extends FrameLayout { // We're visible again, accept touches anywhere in our bounds. insetsInfo.setTouchableInsets(TOUCHABLE_INSETS_FRAME); } + + // TaskbarContainerView provides insets to other apps based on contentInsets. These + // insets should stay consistent even if we expand TaskbarContainerView's bounds, e.g. + // to show a floating view like Folder. Thus, we set the contentInsets to be where + // mTaskbarView is, since its position never changes and insets rather than overlays. + int[] loc = mTempLoc; + mTaskbarView.getLocationInWindow(loc); + insetsInfo.contentInsets.left = loc[0]; + insetsInfo.contentInsets.top = loc[1]; + insetsInfo.contentInsets.right = getWidth() - (loc[0] + mTaskbarView.getWidth()); + insetsInfo.contentInsets.bottom = getHeight() - (loc[1] + mTaskbarView.getHeight()); }; } @@ -91,4 +123,30 @@ public class TaskbarContainerView extends FrameLayout { cleanup(); } + + @Override + protected boolean canFindActiveController() { + // Unlike super class, we want to be able to find controllers when touches occur in the + // gesture area. For example, this allows Folder to close itself when touching the Taskbar. + return true; + } + + @Override + public void onViewRemoved(View child) { + super.onViewRemoved(child); + mControllerCallbacks.onViewRemoved(); + } + + /** + * @return Bounds (in our coordinates) where an opened Folder can display. + */ + protected Rect getFolderBoundingBox() { + Rect boundingBox = new Rect(0, 0, getWidth(), getHeight() - mTaskbarView.getHeight()); + boundingBox.inset(mFolderMargin, mFolderMargin); + return boundingBox; + } + + protected TaskbarActivityContext getTaskbarActivityContext() { + return mActivity; + } } diff --git a/quickstep/src/com/android/launcher3/taskbar/TaskbarController.java b/quickstep/src/com/android/launcher3/taskbar/TaskbarController.java index 260428dbc2..ab05fbf036 100644 --- a/quickstep/src/com/android/launcher3/taskbar/TaskbarController.java +++ b/quickstep/src/com/android/launcher3/taskbar/TaskbarController.java @@ -34,11 +34,15 @@ import android.view.WindowManager; import androidx.annotation.Nullable; +import com.android.launcher3.AbstractFloatingView; import com.android.launcher3.BaseQuickstepLauncher; import com.android.launcher3.LauncherState; import com.android.launcher3.QuickstepAppTransitionManagerImpl; import com.android.launcher3.R; import com.android.launcher3.anim.PendingAnimation; +import com.android.launcher3.folder.Folder; +import com.android.launcher3.folder.FolderIcon; +import com.android.launcher3.model.data.FolderInfo; import com.android.launcher3.model.data.ItemInfo; import com.android.launcher3.states.StateAnimationConfig; import com.android.launcher3.touch.ItemClickHandler; @@ -81,8 +85,9 @@ public class TaskbarController { TaskbarContainerView taskbarContainerView) { mLauncher = launcher; mTaskbarContainerView = taskbarContainerView; + mTaskbarContainerView.construct(createTaskbarContainerViewCallbacks()); mTaskbarView = mTaskbarContainerView.findViewById(R.id.taskbar_view); - mTaskbarView.setCallbacks(createTaskbarViewCallbacks()); + mTaskbarView.construct(createTaskbarViewCallbacks()); mWindowManager = mLauncher.getWindowManager(); mTaskbarSize = new Point(MATCH_PARENT, mLauncher.getResources().getDimensionPixelSize(R.dimen.taskbar_size)); @@ -110,6 +115,18 @@ public class TaskbarController { }; } + private TaskbarContainerViewCallbacks createTaskbarContainerViewCallbacks() { + return new TaskbarContainerViewCallbacks() { + @Override + public void onViewRemoved() { + if (mTaskbarContainerView.getChildCount() == 1) { + // Only TaskbarView remains. + setTaskbarWindowFullscreen(false); + } + } + }; + } + private TaskbarViewCallbacks createTaskbarViewCallbacks() { return new TaskbarViewCallbacks() { @Override @@ -120,9 +137,29 @@ public class TaskbarController { Task task = (Task) tag; ActivityManagerWrapper.getInstance().startActivityFromRecents(task.key, ActivityOptions.makeBasic()); + } else if (tag instanceof FolderInfo) { + FolderIcon folderIcon = (FolderIcon) view; + Folder folder = folderIcon.getFolder(); + + setTaskbarWindowFullscreen(true); + + mTaskbarContainerView.post(() -> { + folder.animateOpen(); + + folder.iterateOverItems((itemInfo, itemView) -> { + itemView.setOnClickListener(getItemOnClickListener()); + itemView.setOnLongClickListener(getItemOnLongClickListener()); + // To play haptic when dragging, like other Taskbar items do. + itemView.setHapticFeedbackEnabled(true); + return false; + }); + }); } else { ItemClickHandler.INSTANCE.onClick(view); } + + AbstractFloatingView.closeAllOpenViews( + mTaskbarContainerView.getTaskbarActivityContext()); }; } @@ -344,6 +381,20 @@ public class TaskbarController { return mTaskbarContainerView.getWindowId().equals(v.getWindowId()); } + /** + * Updates the TaskbarContainer to MATCH_PARENT vs original Taskbar size. + */ + private void setTaskbarWindowFullscreen(boolean fullscreen) { + if (fullscreen) { + mWindowLayoutParams.width = MATCH_PARENT; + mWindowLayoutParams.height = MATCH_PARENT; + } else { + mWindowLayoutParams.width = mTaskbarSize.x; + mWindowLayoutParams.height = mTaskbarSize.y; + } + mWindowManager.updateViewLayout(mTaskbarContainerView, mWindowLayoutParams); + } + /** * Contains methods that TaskbarStateHandler can call to interface with TaskbarController. */ @@ -360,6 +411,13 @@ public class TaskbarController { void updateTaskbarVisibilityAlpha(float alpha); } + /** + * Contains methods that TaskbarContainerView can call to interface with TaskbarController. + */ + protected interface TaskbarContainerViewCallbacks { + void onViewRemoved(); + } + /** * Contains methods that TaskbarView can call to interface with TaskbarController. */ diff --git a/quickstep/src/com/android/launcher3/taskbar/TaskbarView.java b/quickstep/src/com/android/launcher3/taskbar/TaskbarView.java index d8f3bb595e..df77f8778e 100644 --- a/quickstep/src/com/android/launcher3/taskbar/TaskbarView.java +++ b/quickstep/src/com/android/launcher3/taskbar/TaskbarView.java @@ -22,7 +22,6 @@ import android.graphics.drawable.ColorDrawable; import android.graphics.drawable.Drawable; import android.util.AttributeSet; import android.view.DragEvent; -import android.view.LayoutInflater; import android.view.MotionEvent; import android.view.View; import android.view.ViewConfiguration; @@ -32,10 +31,14 @@ import androidx.annotation.LayoutRes; import androidx.annotation.NonNull; import androidx.annotation.Nullable; +import com.android.launcher3.AbstractFloatingView; import com.android.launcher3.BubbleTextView; import com.android.launcher3.R; +import com.android.launcher3.folder.FolderIcon; +import com.android.launcher3.model.data.FolderInfo; import com.android.launcher3.model.data.ItemInfo; import com.android.launcher3.model.data.WorkspaceItemInfo; +import com.android.launcher3.views.ActivityContext; import com.android.systemui.shared.recents.model.Task; /** @@ -51,6 +54,9 @@ public class TaskbarView extends LinearLayout { private final RectF mDelegateSlopBounds = new RectF(); private final int[] mTempOutLocation = new int[2]; + // Initialized in TaskbarController constructor. + private TaskbarController.TaskbarViewCallbacks mControllerCallbacks; + // Initialized in init(). private int mHotseatStartIndex; private int mHotseatEndIndex; @@ -58,8 +64,6 @@ public class TaskbarView extends LinearLayout { private int mRecentsStartIndex; private int mRecentsEndIndex; - private TaskbarController.TaskbarViewCallbacks mControllerCallbacks; - // Delegate touches to the closest view if within mIconTouchSize. private boolean mDelegateTargeted; private View mDelegateView; @@ -90,7 +94,7 @@ public class TaskbarView extends LinearLayout { mTouchSlop = ViewConfiguration.get(context).getScaledTouchSlop(); } - protected void setCallbacks(TaskbarController.TaskbarViewCallbacks taskbarViewCallbacks) { + protected void construct(TaskbarController.TaskbarViewCallbacks taskbarViewCallbacks) { mControllerCallbacks = taskbarViewCallbacks; } @@ -130,17 +134,37 @@ public class TaskbarView extends LinearLayout { // Replace any Hotseat views with the appropriate type if it's not already that type. final int expectedLayoutResId; + boolean isFolder = false; + boolean needsReinflate = false; if (hotseatItemInfo != null && hotseatItemInfo.isPredictedItem()) { expectedLayoutResId = R.layout.taskbar_predicted_app_icon; + } else if (hotseatItemInfo instanceof FolderInfo) { + expectedLayoutResId = R.layout.folder_icon; + isFolder = true; + // Unlike for BubbleTextView, we can't reapply a new FolderInfo after inflation, so + // if the info changes we need to reinflate. This should only happen if a new folder + // is dragged to the position that another folder previously existed. + needsReinflate = hotseatView != null && hotseatView.getTag() != hotseatItemInfo; } else { expectedLayoutResId = R.layout.taskbar_app_icon; } - if (hotseatView == null || hotseatView.getSourceLayoutResId() != expectedLayoutResId) { + if (hotseatView == null || hotseatView.getSourceLayoutResId() != expectedLayoutResId + || needsReinflate) { removeView(hotseatView); - BubbleTextView btv = (BubbleTextView) inflate(expectedLayoutResId); - LayoutParams lp = new LayoutParams(btv.getIconSize(), btv.getIconSize()); + TaskbarActivityContext activityContext = + ActivityContext.lookupContext(getContext()); + if (isFolder) { + FolderInfo folderInfo = (FolderInfo) hotseatItemInfo; + FolderIcon folderIcon = FolderIcon.inflateFolderAndIcon(expectedLayoutResId, + activityContext, this, folderInfo); + folderIcon.setTextVisible(false); + hotseatView = folderIcon; + } else { + hotseatView = inflate(expectedLayoutResId); + } + int iconSize = activityContext.getDeviceProfile().iconSizePx; + LayoutParams lp = new LayoutParams(iconSize, iconSize); lp.setMargins(mItemMarginLeftRight, 0, mItemMarginLeftRight, 0); - hotseatView = btv; addView(hotseatView, hotseatIndex, lp); } @@ -153,6 +177,11 @@ public class TaskbarView extends LinearLayout { hotseatView.setOnClickListener(mControllerCallbacks.getItemOnClickListener()); hotseatView.setOnLongClickListener( mControllerCallbacks.getItemOnLongClickListener()); + } else if (isFolder) { + hotseatView.setVisibility(VISIBLE); + hotseatView.setOnClickListener(mControllerCallbacks.getItemOnClickListener()); + hotseatView.setOnLongClickListener( + mControllerCallbacks.getItemOnLongClickListener()); } else { hotseatView.setVisibility(GONE); hotseatView.setOnClickListener(null); @@ -345,6 +374,7 @@ public class TaskbarView extends LinearLayout { switch (event.getAction()) { case DragEvent.ACTION_DRAG_STARTED: mIsDraggingItem = true; + AbstractFloatingView.closeAllOpenViews(ActivityContext.lookupContext(getContext())); return true; case DragEvent.ACTION_DRAG_ENDED: mIsDraggingItem = false; @@ -358,6 +388,7 @@ public class TaskbarView extends LinearLayout { } private View inflate(@LayoutRes int layoutResId) { - return LayoutInflater.from(getContext()).inflate(layoutResId, this, false); + TaskbarActivityContext taskbarActivityContext = ActivityContext.lookupContext(getContext()); + return taskbarActivityContext.getLayoutInflater().inflate(layoutResId, this, false); } } diff --git a/src/com/android/launcher3/BubbleTextView.java b/src/com/android/launcher3/BubbleTextView.java index edc7e9bb8a..e5a4335dcd 100644 --- a/src/com/android/launcher3/BubbleTextView.java +++ b/src/com/android/launcher3/BubbleTextView.java @@ -505,7 +505,7 @@ public class BubbleTextView extends TextView implements ItemInfoUpdateReceiver, * @param canvas The canvas to draw to. */ protected void drawDotIfNecessary(Canvas canvas) { - if (mDisplay == DISPLAY_TASKBAR) { + if (mActivity instanceof Launcher && ((Launcher) mActivity).isViewInTaskbar(this)) { // TODO: support notification dots in Taskbar return; } diff --git a/src/com/android/launcher3/DeviceProfile.java b/src/com/android/launcher3/DeviceProfile.java index 19915b7026..83a7d77601 100644 --- a/src/com/android/launcher3/DeviceProfile.java +++ b/src/com/android/launcher3/DeviceProfile.java @@ -375,7 +375,7 @@ public class DeviceProfile { * iconTextSizePx, iconDrawablePaddingPx, cellWidth/Height, allApps* variants, * hotseat sizes, workspaceSpringLoadedShrinkFactor, folderIconSizePx, and folderIconOffsetYPx. */ - private void updateIconSize(float scale, Resources res) { + public void updateIconSize(float scale, Resources res) { // Workspace final boolean isVerticalLayout = isVerticalBarLayout(); float invIconSizeDp = isVerticalLayout ? inv.landscapeIconSize : inv.iconSize; diff --git a/src/com/android/launcher3/folder/Folder.java b/src/com/android/launcher3/folder/Folder.java index 9efbc7dcab..504b29e75a 100644 --- a/src/com/android/launcher3/folder/Folder.java +++ b/src/com/android/launcher3/folder/Folder.java @@ -1616,6 +1616,11 @@ public class Folder extends AbstractFloatingView implements ClipPathView, DragSo return false; } + @Override + public boolean canInterceptEventsInSystemGestureRegion() { + return true; + } + /** * Alternative to using {@link #getClipToOutline()} as it only works with derivatives of * rounded rect. diff --git a/src/com/android/launcher3/folder/FolderIcon.java b/src/com/android/launcher3/folder/FolderIcon.java index 65a4fba63c..75d8f2296c 100644 --- a/src/com/android/launcher3/folder/FolderIcon.java +++ b/src/com/android/launcher3/folder/FolderIcon.java @@ -754,6 +754,9 @@ public class FolderIcon extends FrameLayout implements FolderListener, IconLabel } public void clearLeaveBehindIfExists() { + if (!(getLayoutParams() instanceof CellLayout.LayoutParams)) { + return; + } ((CellLayout.LayoutParams) getLayoutParams()).canReorder = true; if (isInHotseat()) { CellLayout cl = (CellLayout) getParent().getParent(); @@ -762,6 +765,9 @@ public class FolderIcon extends FrameLayout implements FolderListener, IconLabel } public void drawLeaveBehindIfExists() { + if (!(getLayoutParams() instanceof CellLayout.LayoutParams)) { + return; + } CellLayout.LayoutParams lp = (CellLayout.LayoutParams) getLayoutParams(); // While the folder is open, the position of the icon cannot change. lp.canReorder = false; diff --git a/src/com/android/launcher3/views/BaseDragLayer.java b/src/com/android/launcher3/views/BaseDragLayer.java index 15f7730fd7..1939d15776 100644 --- a/src/com/android/launcher3/views/BaseDragLayer.java +++ b/src/com/android/launcher3/views/BaseDragLayer.java @@ -206,15 +206,19 @@ public abstract class BaseDragLayer protected boolean findActiveController(MotionEvent ev) { mActiveController = null; - if ((mTouchDispatchState & (TOUCH_DISPATCHING_FROM_VIEW_GESTURE_REGION - | TOUCH_DISPATCHING_FROM_PROXY)) == 0) { - // Only look for controllers if we are not dispatching from gesture area and proxy is - // not active + if (canFindActiveController()) { mActiveController = findControllerToHandleTouch(ev); } return mActiveController != null; } + protected boolean canFindActiveController() { + // Only look for controllers if we are not dispatching from gesture area and proxy is + // not active + return (mTouchDispatchState & (TOUCH_DISPATCHING_FROM_VIEW_GESTURE_REGION + | TOUCH_DISPATCHING_FROM_PROXY)) == 0; + } + @Override public boolean onRequestSendAccessibilityEvent(View child, AccessibilityEvent event) { // Shortcuts can appear above folder -- GitLab From bb4de85c253baf31ff120e006941fc1cfe410047 Mon Sep 17 00:00:00 2001 From: Tony Wickham Date: Wed, 10 Feb 2021 12:35:28 -0800 Subject: [PATCH 0527/1664] Draw folder leave-behind when opening folder in taskbar Previously there was only support for CellLayout to draw the leave-behind. Added FolderIconParent interface so any parent can draw the leave-behind, and implement that by TaskbarView. Test: Open folder in Taskber, ensure leave-behind draws Bug: 171917176 Change-Id: I08d1d939a34f971f893994fe05cac972d165ef53 --- .../launcher3/taskbar/TaskbarView.java | 32 ++++++++++++++++- .../launcher3/ShortcutAndWidgetContainer.java | 23 +++++++++++- .../android/launcher3/folder/FolderIcon.java | 35 ++++++++++--------- 3 files changed, 72 insertions(+), 18 deletions(-) diff --git a/quickstep/src/com/android/launcher3/taskbar/TaskbarView.java b/quickstep/src/com/android/launcher3/taskbar/TaskbarView.java index df77f8778e..7a13b89d25 100644 --- a/quickstep/src/com/android/launcher3/taskbar/TaskbarView.java +++ b/quickstep/src/com/android/launcher3/taskbar/TaskbarView.java @@ -17,6 +17,7 @@ package com.android.launcher3.taskbar; import android.content.Context; import android.content.res.Resources; +import android.graphics.Canvas; import android.graphics.RectF; import android.graphics.drawable.ColorDrawable; import android.graphics.drawable.Drawable; @@ -44,7 +45,7 @@ import com.android.systemui.shared.recents.model.Task; /** * Hosts the Taskbar content such as Hotseat and Recent Apps. Drawn on top of other apps. */ -public class TaskbarView extends LinearLayout { +public class TaskbarView extends LinearLayout implements FolderIcon.FolderIconParent { private final ColorDrawable mBackgroundDrawable; private final int mItemMarginLeftRight; @@ -69,6 +70,8 @@ public class TaskbarView extends LinearLayout { private View mDelegateView; private boolean mIsDraggingItem; + // Only non-null when the corresponding Folder is open. + private @Nullable FolderIcon mLeaveBehindFolderIcon; public TaskbarView(@NonNull Context context) { this(context, null); @@ -387,6 +390,33 @@ public class TaskbarView extends LinearLayout { return mIsDraggingItem; } + // FolderIconParent implemented methods. + + @Override + public void drawFolderLeaveBehindForIcon(FolderIcon child) { + mLeaveBehindFolderIcon = child; + invalidate(); + } + + @Override + public void clearFolderLeaveBehind(FolderIcon child) { + mLeaveBehindFolderIcon = null; + invalidate(); + } + + // End FolderIconParent implemented methods. + + @Override + protected void onDraw(Canvas canvas) { + super.onDraw(canvas); + if (mLeaveBehindFolderIcon != null) { + canvas.save(); + canvas.translate(mLeaveBehindFolderIcon.getLeft(), mLeaveBehindFolderIcon.getTop()); + mLeaveBehindFolderIcon.getFolderBackground().drawLeaveBehind(canvas); + canvas.restore(); + } + } + private View inflate(@LayoutRes int layoutResId) { TaskbarActivityContext taskbarActivityContext = ActivityContext.lookupContext(getContext()); return taskbarActivityContext.getLayoutInflater().inflate(layoutResId, this, false); diff --git a/src/com/android/launcher3/ShortcutAndWidgetContainer.java b/src/com/android/launcher3/ShortcutAndWidgetContainer.java index 1c5081cfca..eab8272431 100644 --- a/src/com/android/launcher3/ShortcutAndWidgetContainer.java +++ b/src/com/android/launcher3/ShortcutAndWidgetContainer.java @@ -26,10 +26,11 @@ import android.view.View; import android.view.ViewGroup; import com.android.launcher3.CellLayout.ContainerType; +import com.android.launcher3.folder.FolderIcon; import com.android.launcher3.views.ActivityContext; import com.android.launcher3.widget.LauncherAppWidgetHostView; -public class ShortcutAndWidgetContainer extends ViewGroup { +public class ShortcutAndWidgetContainer extends ViewGroup implements FolderIcon.FolderIconParent { static final String TAG = "ShortcutAndWidgetContainer"; // These are temporary variables to prevent having to allocate a new object just to @@ -228,4 +229,24 @@ public class ShortcutAndWidgetContainer extends ViewGroup { child.cancelLongPress(); } } + + @Override + public void drawFolderLeaveBehindForIcon(FolderIcon child) { + CellLayout.LayoutParams lp = (CellLayout.LayoutParams) child.getLayoutParams(); + // While the folder is open, the position of the icon cannot change. + lp.canReorder = false; + if (mContainerType == CellLayout.HOTSEAT) { + CellLayout cl = (CellLayout) getParent(); + cl.setFolderLeaveBehindCell(lp.cellX, lp.cellY); + } + } + + @Override + public void clearFolderLeaveBehind(FolderIcon child) { + ((CellLayout.LayoutParams) child.getLayoutParams()).canReorder = true; + if (mContainerType == CellLayout.HOTSEAT) { + CellLayout cl = (CellLayout) getParent(); + cl.clearFolderLeaveBehind(); + } + } } diff --git a/src/com/android/launcher3/folder/FolderIcon.java b/src/com/android/launcher3/folder/FolderIcon.java index 75d8f2296c..6b02021ed3 100644 --- a/src/com/android/launcher3/folder/FolderIcon.java +++ b/src/com/android/launcher3/folder/FolderIcon.java @@ -754,26 +754,14 @@ public class FolderIcon extends FrameLayout implements FolderListener, IconLabel } public void clearLeaveBehindIfExists() { - if (!(getLayoutParams() instanceof CellLayout.LayoutParams)) { - return; - } - ((CellLayout.LayoutParams) getLayoutParams()).canReorder = true; - if (isInHotseat()) { - CellLayout cl = (CellLayout) getParent().getParent(); - cl.clearFolderLeaveBehind(); + if (getParent() instanceof FolderIconParent) { + ((FolderIconParent) getParent()).clearFolderLeaveBehind(this); } } public void drawLeaveBehindIfExists() { - if (!(getLayoutParams() instanceof CellLayout.LayoutParams)) { - return; - } - CellLayout.LayoutParams lp = (CellLayout.LayoutParams) getLayoutParams(); - // While the folder is open, the position of the icon cannot change. - lp.canReorder = false; - if (isInHotseat()) { - CellLayout cl = (CellLayout) getParent().getParent(); - cl.setFolderLeaveBehindCell(lp.cellX, lp.cellY); + if (getParent() instanceof FolderIconParent) { + ((FolderIconParent) getParent()).drawFolderLeaveBehindForIcon(this); } } @@ -842,4 +830,19 @@ public class FolderIcon extends FrameLayout implements FolderListener, IconLabel MAX_NUM_ITEMS_IN_PREVIEW); } } + + /** + * Interface that provides callbacks to a parent ViewGroup that hosts this FolderIcon. + */ + public interface FolderIconParent { + /** + * Tells the FolderIconParent to draw a "leave-behind" when the Folder is open and leaving a + * gap where the FolderIcon would be when the Folder is closed. + */ + void drawFolderLeaveBehindForIcon(FolderIcon child); + /** + * Tells the FolderIconParent to stop drawing the "leave-behind" as the Folder is closed. + */ + void clearFolderLeaveBehind(FolderIcon child); + } } -- GitLab From a9740ecc8f190af6829446d3c76f43f4188087f1 Mon Sep 17 00:00:00 2001 From: vadimt Date: Fri, 12 Feb 2021 10:08:25 -0800 Subject: [PATCH 0528/1664] Switching overlays more correctly Test: presubmit Bug: 180114721 Change-Id: I0d8d0c70ef34ec941e28704d06f9df5f17782155 --- .../src/com/android/quickstep/NavigationModeSwitchRule.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/quickstep/tests/src/com/android/quickstep/NavigationModeSwitchRule.java b/quickstep/tests/src/com/android/quickstep/NavigationModeSwitchRule.java index 0f6671d18c..35383d2831 100644 --- a/quickstep/tests/src/com/android/quickstep/NavigationModeSwitchRule.java +++ b/quickstep/tests/src/com/android/quickstep/NavigationModeSwitchRule.java @@ -168,7 +168,7 @@ public class NavigationModeSwitchRule implements TestRule { Log.d(TAG, "setActiveOverlay: " + overlayPackage + "..."); UiDevice.getInstance(getInstrumentation()).executeShellCommand( - "cmd overlay enable-exclusive " + overlayPackage); + "cmd overlay enable-exclusive --category " + overlayPackage); if (currentSysUiNavigationMode() != expectedMode) { final CountDownLatch latch = new CountDownLatch(1); -- GitLab From 2cdc6bae3942aedee975981009b11f5d9e95c049 Mon Sep 17 00:00:00 2001 From: Lucas Dupin Date: Tue, 16 Feb 2021 17:47:33 -0800 Subject: [PATCH 0529/1664] Conform to new naming Resources were renamed from main/accent to primary/secondary. Test: make Bug: 171916625 Change-Id: Ic71cf716eba9f57d27869dffa501219c41c999ec --- res/values-v31/colors.xml | 22 +++++++++++----------- 1 file changed, 11 insertions(+), 11 deletions(-) diff --git a/res/values-v31/colors.xml b/res/values-v31/colors.xml index b8600a60ed..6baf39e034 100644 --- a/res/values-v31/colors.xml +++ b/res/values-v31/colors.xml @@ -17,17 +17,17 @@ */ --> - @android:color/system_main_50 - @android:color/system_main_100 - @android:color/system_main_300 - @android:color/system_main_800 - @android:color/system_main_900 - @android:color/system_main_700 + @android:color/system_primary_50 + @android:color/system_primary_100 + @android:color/system_primary_300 + @android:color/system_primary_800 + @android:color/system_primary_900 + @android:color/system_primary_700 - @android:color/system_main_50 - @android:color/system_main_900 + @android:color/system_primary_50 + @android:color/system_primary_900 - @android:color/system_main_50 - @android:color/system_main_200 - @android:color/system_main_400 + @android:color/system_primary_50 + @android:color/system_primary_200 + @android:color/system_primary_400 \ No newline at end of file -- GitLab From e1a8440d78ad2437c5b69eb4df02f0a91cc4ff02 Mon Sep 17 00:00:00 2001 From: thiruram Date: Fri, 12 Feb 2021 16:02:39 -0800 Subject: [PATCH 0530/1664] Add @Nullable annotation to AllAppsStore.getApp. Test: Manual Bug: 180120291 Change-Id: I3f29d478e39b46e307f33f2f354720bc2915b550 --- src/com/android/launcher3/allapps/AllAppsStore.java | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/src/com/android/launcher3/allapps/AllAppsStore.java b/src/com/android/launcher3/allapps/AllAppsStore.java index 769cb5e31b..355ccadeba 100644 --- a/src/com/android/launcher3/allapps/AllAppsStore.java +++ b/src/com/android/launcher3/allapps/AllAppsStore.java @@ -21,6 +21,8 @@ import static com.android.launcher3.model.data.AppInfo.EMPTY_ARRAY; import android.view.View; import android.view.ViewGroup; +import androidx.annotation.Nullable; + import com.android.launcher3.BubbleTextView; import com.android.launcher3.model.data.AppInfo; import com.android.launcher3.model.data.ItemInfo; @@ -78,6 +80,11 @@ public class AllAppsStore { return (mModelFlags & mask) != 0; } + /** + * Returns {@link AppInfo} if any apps matches with provided {@link ComponentKey}, otherwise + * null. + */ + @Nullable public AppInfo getApp(ComponentKey key) { mTempInfo.componentName = key.componentName; mTempInfo.user = key.user; -- GitLab From a34af18b0efd6bfc6b1aa0f0c3b63d6751b282fc Mon Sep 17 00:00:00 2001 From: Vinit Nayak Date: Thu, 11 Feb 2021 15:48:54 -0800 Subject: [PATCH 0531/1664] Update System settings content observer to cache values. Existing ContentObserver will cache values and overwrite them when the value itself is modified or caller force updates. Bug: 149571513 Test: Wrote unit tests, mostly for caching logic. Not meant to test ContentObserver contract for registering an observer. Change-Id: I12835f6c2be27ce17f65a55c51c4ef85c63b4487 --- Android.bp | 1 - .../RecentsAnimationDeviceState.java | 49 ++--- .../logging/SettingsChangeLogger.java | 13 +- .../quickstep/util/RecentsOrientedState.java | 26 +-- .../launcher3/util/SettingsCacheTest.java | 115 +++++++++++ .../android/launcher3/LauncherAppState.java | 21 +- .../notification/NotificationListener.java | 19 +- .../settings/NotificationDotsPreference.java | 4 +- .../launcher3/settings/SettingsActivity.java | 35 ++-- .../util/SecureSettingsObserver.java | 104 ---------- .../android/launcher3/util/SettingsCache.java | 179 ++++++++++++++++++ tests/Android.mk | 1 - 12 files changed, 380 insertions(+), 187 deletions(-) create mode 100644 robolectric_tests/src/com/android/launcher3/util/SettingsCacheTest.java delete mode 100644 src/com/android/launcher3/util/SecureSettingsObserver.java create mode 100644 src/com/android/launcher3/util/SettingsCache.java diff --git a/Android.bp b/Android.bp index a720658f9f..9d675a4b3e 100644 --- a/Android.bp +++ b/Android.bp @@ -24,7 +24,6 @@ android_library { ], srcs: [ "tests/tapl/**/*.java", - "src/com/android/launcher3/util/SecureSettingsObserver.java", "src/com/android/launcher3/ResourceUtils.java", "src/com/android/launcher3/testing/TestProtocol.java", ], diff --git a/quickstep/src/com/android/quickstep/RecentsAnimationDeviceState.java b/quickstep/src/com/android/quickstep/RecentsAnimationDeviceState.java index 4301377808..f99b7e6af4 100644 --- a/quickstep/src/com/android/quickstep/RecentsAnimationDeviceState.java +++ b/quickstep/src/com/android/quickstep/RecentsAnimationDeviceState.java @@ -17,14 +17,16 @@ package com.android.quickstep; import static android.content.Intent.ACTION_USER_UNLOCKED; +import static com.android.launcher3.util.SettingsCache.ONE_HANDED_ENABLED; +import static com.android.launcher3.util.SettingsCache.ONE_HANDED_SWIPE_BOTTOM_TO_NOTIFICATION_ENABLED; import static com.android.launcher3.util.DisplayController.DisplayHolder.CHANGE_ALL; import static com.android.launcher3.util.DisplayController.DisplayHolder.CHANGE_FRAME_DELAY; import static com.android.quickstep.SysUINavigationMode.Mode.NO_BUTTON; import static com.android.quickstep.SysUINavigationMode.Mode.THREE_BUTTONS; import static com.android.systemui.shared.system.QuickStepContract.SYSUI_STATE_A11Y_BUTTON_CLICKABLE; import static com.android.systemui.shared.system.QuickStepContract.SYSUI_STATE_A11Y_BUTTON_LONG_CLICKABLE; -import static com.android.systemui.shared.system.QuickStepContract.SYSUI_STATE_ASSIST_GESTURE_CONSTRAINED; import static com.android.systemui.shared.system.QuickStepContract.SYSUI_STATE_ALLOW_GESTURE_IGNORING_BAR_VISIBILITY; +import static com.android.systemui.shared.system.QuickStepContract.SYSUI_STATE_ASSIST_GESTURE_CONSTRAINED; import static com.android.systemui.shared.system.QuickStepContract.SYSUI_STATE_BUBBLES_EXPANDED; import static com.android.systemui.shared.system.QuickStepContract.SYSUI_STATE_GLOBAL_ACTIONS_SHOWING; import static com.android.systemui.shared.system.QuickStepContract.SYSUI_STATE_HOME_DISABLED; @@ -44,6 +46,7 @@ import android.content.Intent; import android.content.IntentFilter; import android.content.res.Resources; import android.graphics.Region; +import android.net.Uri; import android.os.Process; import android.os.SystemProperties; import android.os.UserManager; @@ -57,11 +60,11 @@ import androidx.annotation.BinderThread; import com.android.launcher3.R; import com.android.launcher3.Utilities; +import com.android.launcher3.util.SettingsCache; import com.android.launcher3.util.DisplayController; import com.android.launcher3.util.DisplayController.DisplayHolder; import com.android.launcher3.util.DisplayController.DisplayInfoChangeListener; import com.android.launcher3.util.DisplayController.Info; -import com.android.launcher3.util.SecureSettingsObserver; import com.android.quickstep.SysUINavigationMode.NavigationModeChangeListener; import com.android.quickstep.SysUINavigationMode.OneHandedModeChangeListener; import com.android.quickstep.util.NavBarPosition; @@ -177,33 +180,33 @@ public class RecentsAnimationDeviceState implements } } + SettingsCache settingsCache = SettingsCache.INSTANCE.get(mContext); if (mIsOneHandedModeSupported) { - SecureSettingsObserver oneHandedEnabledObserver = - SecureSettingsObserver.newOneHandedSettingsObserver( - mContext, enabled -> mIsOneHandedModeEnabled = enabled); - oneHandedEnabledObserver.register(); - oneHandedEnabledObserver.dispatchOnChange(); - runOnDestroy(oneHandedEnabledObserver::unregister); + Uri oneHandedUri = Settings.Secure.getUriFor(ONE_HANDED_ENABLED); + SettingsCache.OnChangeListener onChangeListener = + enabled -> mIsOneHandedModeEnabled = enabled; + settingsCache.register(oneHandedUri, onChangeListener); + settingsCache.dispatchOnChange(oneHandedUri); + runOnDestroy(() -> settingsCache.unregister(oneHandedUri, onChangeListener)); } else { mIsOneHandedModeEnabled = false; } - SecureSettingsObserver swipeBottomEnabledObserver = - SecureSettingsObserver.newSwipeToNotificationSettingsObserver( - mContext, enabled -> mIsSwipeToNotificationEnabled = enabled); - swipeBottomEnabledObserver.register(); - swipeBottomEnabledObserver.dispatchOnChange(); - runOnDestroy(swipeBottomEnabledObserver::unregister); - - SecureSettingsObserver userSetupObserver = new SecureSettingsObserver( - context.getContentResolver(), - e -> mIsUserSetupComplete = e, - Settings.Secure.USER_SETUP_COMPLETE, - 0); - mIsUserSetupComplete = userSetupObserver.getValue(); + + Uri swipeBottomNotificationUri = + Settings.Secure.getUriFor(ONE_HANDED_SWIPE_BOTTOM_TO_NOTIFICATION_ENABLED); + SettingsCache.OnChangeListener onChangeListener = + enabled -> mIsSwipeToNotificationEnabled = enabled; + settingsCache.register(swipeBottomNotificationUri, onChangeListener); + settingsCache.dispatchOnChange(swipeBottomNotificationUri); + runOnDestroy(() -> settingsCache.unregister(swipeBottomNotificationUri, onChangeListener)); + + Uri setupCompleteUri = Settings.Secure.getUriFor(Settings.Secure.USER_SETUP_COMPLETE); + mIsUserSetupComplete = settingsCache.getValue(setupCompleteUri, 0); if (!mIsUserSetupComplete) { - userSetupObserver.register(); - runOnDestroy(userSetupObserver::unregister); + SettingsCache.OnChangeListener userSetupChangeListener = e -> mIsUserSetupComplete = e; + settingsCache.register(setupCompleteUri, userSetupChangeListener); + runOnDestroy(() -> settingsCache.unregister(setupCompleteUri, userSetupChangeListener)); } } diff --git a/quickstep/src/com/android/quickstep/logging/SettingsChangeLogger.java b/quickstep/src/com/android/quickstep/logging/SettingsChangeLogger.java index 3157865f78..f336bf5260 100644 --- a/quickstep/src/com/android/quickstep/logging/SettingsChangeLogger.java +++ b/quickstep/src/com/android/quickstep/logging/SettingsChangeLogger.java @@ -28,7 +28,7 @@ import static com.android.launcher3.logging.StatsLogManager.LauncherEvent.LAUNCH import static com.android.launcher3.logging.StatsLogManager.LauncherEvent.LAUNCHER_NOTIFICATION_DOT_DISABLED; import static com.android.launcher3.logging.StatsLogManager.LauncherEvent.LAUNCHER_NOTIFICATION_DOT_ENABLED; import static com.android.launcher3.model.QuickstepModelDelegate.LAST_PREDICTION_ENABLED_STATE; -import static com.android.launcher3.util.SecureSettingsObserver.newNotificationSettingsObserver; +import static com.android.launcher3.util.SettingsCache.NOTIFICATION_BADGING_URI; import android.content.Context; import android.content.SharedPreferences; @@ -43,7 +43,7 @@ import com.android.launcher3.R; import com.android.launcher3.logging.InstanceIdSequence; import com.android.launcher3.logging.StatsLogManager; import com.android.launcher3.logging.StatsLogManager.StatsLogger; -import com.android.launcher3.util.SecureSettingsObserver; +import com.android.launcher3.util.SettingsCache; import com.android.quickstep.SysUINavigationMode; import com.android.quickstep.SysUINavigationMode.Mode; import com.android.quickstep.SysUINavigationMode.NavigationModeChangeListener; @@ -77,11 +77,10 @@ public class SettingsChangeLogger implements getPrefs(context).registerOnSharedPreferenceChangeListener(this); getDevicePrefs(context).registerOnSharedPreferenceChangeListener(this); - SecureSettingsObserver dotsObserver = - newNotificationSettingsObserver(context, this::onNotificationDotsChanged); - mNotificationDotsEnabled = dotsObserver.getValue(); - dispatchUserEvent(); - + SettingsCache mSettingsCache = SettingsCache.INSTANCE.get(context); + mSettingsCache.register(NOTIFICATION_BADGING_URI, + this::onNotificationDotsChanged); + mSettingsCache.dispatchOnChange(NOTIFICATION_BADGING_URI); } private static ArrayMap loadPrefKeys(Context context) { diff --git a/quickstep/src/com/android/quickstep/util/RecentsOrientedState.java b/quickstep/src/com/android/quickstep/util/RecentsOrientedState.java index a3ee9122a7..215f05ae4c 100644 --- a/quickstep/src/com/android/quickstep/util/RecentsOrientedState.java +++ b/quickstep/src/com/android/quickstep/util/RecentsOrientedState.java @@ -23,22 +23,18 @@ import static android.view.Surface.ROTATION_180; import static android.view.Surface.ROTATION_270; import static android.view.Surface.ROTATION_90; -import static com.android.launcher3.Utilities.newContentObserver; +import static com.android.launcher3.util.SettingsCache.ROTATION_SETTING_URI; import static com.android.launcher3.states.RotationHelper.ALLOW_ROTATION_PREFERENCE_KEY; import static com.android.launcher3.util.Executors.UI_HELPER_EXECUTOR; import static java.lang.annotation.RetentionPolicy.SOURCE; -import android.content.ContentResolver; import android.content.Context; import android.content.SharedPreferences; import android.content.res.Resources; -import android.database.ContentObserver; import android.graphics.Matrix; import android.graphics.PointF; import android.graphics.Rect; -import android.os.Handler; -import android.provider.Settings; import android.util.Log; import android.view.MotionEvent; import android.view.OrientationEventListener; @@ -51,6 +47,7 @@ import com.android.launcher3.DeviceProfile; import com.android.launcher3.InvariantDeviceProfile; import com.android.launcher3.R; import com.android.launcher3.Utilities; +import com.android.launcher3.util.SettingsCache; import com.android.launcher3.testing.TestProtocol; import com.android.launcher3.touch.PagedOrientationHandler; import com.android.launcher3.util.WindowBounds; @@ -72,9 +69,6 @@ public final class RecentsOrientedState implements SharedPreferences.OnSharedPre private static final String TAG = "RecentsOrientedState"; private static final boolean DEBUG = false; - private ContentObserver mSystemAutoRotateObserver = - newContentObserver(new Handler(), t -> updateAutoRotateSetting()); - @Retention(SOURCE) @IntDef({ROTATION_0, ROTATION_90, ROTATION_180, ROTATION_270}) public @interface SurfaceRotation {} @@ -118,9 +112,11 @@ public final class RecentsOrientedState implements SharedPreferences.OnSharedPre | FLAG_SWIPE_UP_NOT_RUNNING; private final Context mContext; - private final ContentResolver mContentResolver; private final SharedPreferences mSharedPrefs; private final OrientationEventListener mOrientationListener; + private final SettingsCache mSettingsCache; + private final SettingsCache.OnChangeListener mRotationChangeListener = + isEnabled -> updateAutoRotateSetting(); private final Matrix mTmpMatrix = new Matrix(); @@ -138,7 +134,6 @@ public final class RecentsOrientedState implements SharedPreferences.OnSharedPre public RecentsOrientedState(Context context, BaseActivityInterface sizeStrategy, IntConsumer rotationChangeListener) { mContext = context; - mContentResolver = context.getContentResolver(); mSharedPrefs = Utilities.getPrefs(context); mOrientationListener = new OrientationEventListener(context) { @Override @@ -162,6 +157,7 @@ public final class RecentsOrientedState implements SharedPreferences.OnSharedPre mFlags |= FLAG_MULTIPLE_ORIENTATION_SUPPORTED_BY_DENSITY; } mFlags |= FLAG_SWIPE_UP_NOT_RUNNING; + mSettingsCache = SettingsCache.INSTANCE.get(mContext); initFlags(); } @@ -271,8 +267,8 @@ public final class RecentsOrientedState implements SharedPreferences.OnSharedPre } private void updateAutoRotateSetting() { - setFlag(FLAG_SYSTEM_ROTATION_ALLOWED, Settings.System.getInt(mContentResolver, - Settings.System.ACCELEROMETER_ROTATION, 1) == 1); + setFlag(FLAG_SYSTEM_ROTATION_ALLOWED, + mSettingsCache.getValue(ROTATION_SETTING_URI, 1)); } private void updateHomeRotationSetting() { @@ -295,9 +291,7 @@ public final class RecentsOrientedState implements SharedPreferences.OnSharedPre public void initListeners() { if (isMultipleOrientationSupportedByDevice()) { mSharedPrefs.registerOnSharedPreferenceChangeListener(this); - mContentResolver.registerContentObserver( - Settings.System.getUriFor(Settings.System.ACCELEROMETER_ROTATION), - false, mSystemAutoRotateObserver); + mSettingsCache.register(ROTATION_SETTING_URI, mRotationChangeListener); } initFlags(); } @@ -308,7 +302,7 @@ public final class RecentsOrientedState implements SharedPreferences.OnSharedPre public void destroyListeners() { if (isMultipleOrientationSupportedByDevice()) { mSharedPrefs.unregisterOnSharedPreferenceChangeListener(this); - mContentResolver.unregisterContentObserver(mSystemAutoRotateObserver); + mSettingsCache.unregister(ROTATION_SETTING_URI, mRotationChangeListener); } setRotationWatcherEnabled(false); } diff --git a/robolectric_tests/src/com/android/launcher3/util/SettingsCacheTest.java b/robolectric_tests/src/com/android/launcher3/util/SettingsCacheTest.java new file mode 100644 index 0000000000..fbf4c63e65 --- /dev/null +++ b/robolectric_tests/src/com/android/launcher3/util/SettingsCacheTest.java @@ -0,0 +1,115 @@ +/* + * Copyright (C) 2021 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; + +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertTrue; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.times; +import static org.mockito.Mockito.verify; + +import android.content.Context; +import android.net.Uri; + +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.robolectric.RobolectricTestRunner; +import org.robolectric.RuntimeEnvironment; + +import java.util.Collections; + +@RunWith(RobolectricTestRunner.class) +public class SettingsCacheTest { + + public static final Uri KEY_SYSTEM_URI_TEST1 = Uri.parse("content://settings/system/test1"); + public static final Uri KEY_SYSTEM_URI_TEST2 = Uri.parse("content://settings/system/test2");; + + private SettingsCache.OnChangeListener mChangeListener; + private SettingsCache mSettingsCache; + + @Before + public void setup() { + mChangeListener = mock(SettingsCache.OnChangeListener.class); + Context targetContext = RuntimeEnvironment.application; + mSettingsCache = SettingsCache.INSTANCE.get(targetContext); + mSettingsCache.register(KEY_SYSTEM_URI_TEST1, mChangeListener); + } + + @Test + public void listenerCalledOnChange() { + mSettingsCache.onChange(true, KEY_SYSTEM_URI_TEST1); + verify(mChangeListener, times(1)).onSettingsChanged(true); + } + + @Test + public void getValueRespectsDefaultValue() { + // Case of key not found + boolean val = mSettingsCache.getValue(KEY_SYSTEM_URI_TEST1, 0); + assertFalse(val); + } + + @Test + public void getValueHitsCache() { + mSettingsCache.setKeyCache(Collections.singletonMap(KEY_SYSTEM_URI_TEST1, true)); + boolean val = mSettingsCache.getValue(KEY_SYSTEM_URI_TEST1, 0); + assertTrue(val); + } + + @Test + public void getValueUpdatedCache() { + // First ensure there's nothing in cache + boolean val = mSettingsCache.getValue(KEY_SYSTEM_URI_TEST1, 0); + assertFalse(val); + + mSettingsCache.setKeyCache(Collections.singletonMap(KEY_SYSTEM_URI_TEST1, true)); + val = mSettingsCache.getValue(KEY_SYSTEM_URI_TEST1, 0); + assertTrue(val); + } + + @Test + public void multipleListenersSingleKey() { + SettingsCache.OnChangeListener secondListener = mock(SettingsCache.OnChangeListener.class); + mSettingsCache.register(KEY_SYSTEM_URI_TEST1, secondListener); + + mSettingsCache.onChange(true, KEY_SYSTEM_URI_TEST1); + verify(mChangeListener, times(1)).onSettingsChanged(true); + verify(secondListener, times(1)).onSettingsChanged(true); + } + + @Test + public void singleListenerMultipleKeys() { + SettingsCache.OnChangeListener secondListener = mock(SettingsCache.OnChangeListener.class); + mSettingsCache.register(KEY_SYSTEM_URI_TEST2, secondListener); + + mSettingsCache.onChange(true, KEY_SYSTEM_URI_TEST1); + mSettingsCache.onChange(true, KEY_SYSTEM_URI_TEST2); + verify(mChangeListener, times(1)).onSettingsChanged(true); + verify(secondListener, times(1)).onSettingsChanged(true); + } + + @Test + public void sameListenerMultipleKeys() { + SettingsCache.OnChangeListener secondListener = mock(SettingsCache.OnChangeListener.class); + mSettingsCache.register(KEY_SYSTEM_URI_TEST2, mChangeListener); + + mSettingsCache.onChange(true, KEY_SYSTEM_URI_TEST1); + mSettingsCache.onChange(true, KEY_SYSTEM_URI_TEST2); + verify(mChangeListener, times(2)).onSettingsChanged(true); + verify(secondListener, times(0)).onSettingsChanged(true); + } +} diff --git a/src/com/android/launcher3/LauncherAppState.java b/src/com/android/launcher3/LauncherAppState.java index a4181c53c9..57d76005a6 100644 --- a/src/com/android/launcher3/LauncherAppState.java +++ b/src/com/android/launcher3/LauncherAppState.java @@ -17,8 +17,8 @@ package com.android.launcher3; import static com.android.launcher3.InvariantDeviceProfile.CHANGE_FLAG_ICON_PARAMS; +import static com.android.launcher3.util.SettingsCache.NOTIFICATION_BADGING_URI; import static com.android.launcher3.util.Executors.MODEL_EXECUTOR; -import static com.android.launcher3.util.SecureSettingsObserver.newNotificationSettingsObserver; import android.content.ComponentName; import android.content.Context; @@ -37,10 +37,10 @@ import com.android.launcher3.notification.NotificationListener; import com.android.launcher3.pm.InstallSessionHelper; import com.android.launcher3.pm.InstallSessionTracker; import com.android.launcher3.pm.UserCache; +import com.android.launcher3.util.SettingsCache; import com.android.launcher3.util.MainThreadInitializedObject; import com.android.launcher3.util.Preconditions; import com.android.launcher3.util.SafeCloseable; -import com.android.launcher3.util.SecureSettingsObserver; import com.android.launcher3.util.SimpleBroadcastReceiver; import com.android.launcher3.widget.custom.CustomWidgetManager; @@ -57,8 +57,9 @@ public class LauncherAppState { private final IconCache mIconCache; private final WidgetPreviewLoader mWidgetCache; private final InvariantDeviceProfile mInvariantDeviceProfile; + private SettingsCache.OnChangeListener mNotificationSettingsChangedListener; - private SecureSettingsObserver mNotificationDotsObserver; + private SettingsCache mSettingsCache; private InstallSessionTracker mInstallSessionTracker; private SimpleBroadcastReceiver mModelChangeReceiver; private SafeCloseable mCalendarChangeTracker; @@ -108,10 +109,11 @@ public class LauncherAppState { .registerInstallTracker(mModel); // Register an observer to rebind the notification listener when dots are re-enabled. - mNotificationDotsObserver = - newNotificationSettingsObserver(mContext, this::onNotificationSettingsChanged); - mNotificationDotsObserver.register(); - mNotificationDotsObserver.dispatchOnChange(); + mSettingsCache = SettingsCache.INSTANCE.get(mContext); + mNotificationSettingsChangedListener = this::onNotificationSettingsChanged; + mSettingsCache.register(NOTIFICATION_BADGING_URI, + mNotificationSettingsChangedListener); + mSettingsCache.dispatchOnChange(NOTIFICATION_BADGING_URI); } public LauncherAppState(Context context, @Nullable String iconCacheFileName) { @@ -166,8 +168,9 @@ public class LauncherAppState { } CustomWidgetManager.INSTANCE.get(mContext).setWidgetRefreshCallback(null); - if (mNotificationDotsObserver != null) { - mNotificationDotsObserver.unregister(); + if (mSettingsCache != null) { + mSettingsCache.unregister(NOTIFICATION_BADGING_URI, + mNotificationSettingsChangedListener); } } diff --git a/src/com/android/launcher3/notification/NotificationListener.java b/src/com/android/launcher3/notification/NotificationListener.java index 059ad18971..2905dc322a 100644 --- a/src/com/android/launcher3/notification/NotificationListener.java +++ b/src/com/android/launcher3/notification/NotificationListener.java @@ -16,9 +16,9 @@ package com.android.launcher3.notification; +import static com.android.launcher3.util.SettingsCache.NOTIFICATION_BADGING_URI; import static com.android.launcher3.util.Executors.MAIN_EXECUTOR; import static com.android.launcher3.util.Executors.MODEL_EXECUTOR; -import static com.android.launcher3.util.SecureSettingsObserver.newNotificationSettingsObserver; import android.annotation.TargetApi; import android.app.Notification; @@ -37,8 +37,8 @@ import androidx.annotation.AnyThread; import androidx.annotation.Nullable; import androidx.annotation.WorkerThread; +import com.android.launcher3.util.SettingsCache; import com.android.launcher3.util.PackageUserKey; -import com.android.launcher3.util.SecureSettingsObserver; import java.util.ArrayList; import java.util.Arrays; @@ -81,7 +81,8 @@ public class NotificationListener extends NotificationListenerService { /** The last notification key that was dismissed from launcher UI */ private String mLastKeyDismissedByLauncher; - private SecureSettingsObserver mNotificationDotsObserver; + private SettingsCache mSettingsCache; + private SettingsCache.OnChangeListener mNotificationSettingsChangedListener; public NotificationListener() { mWorkerHandler = new Handler(MODEL_EXECUTOR.getLooper(), this::handleWorkerMessage); @@ -207,10 +208,12 @@ public class NotificationListener extends NotificationListenerService { super.onListenerConnected(); sIsConnected = true; - mNotificationDotsObserver = - newNotificationSettingsObserver(this, this::onNotificationSettingsChanged); - mNotificationDotsObserver.register(); - mNotificationDotsObserver.dispatchOnChange(); + // Register an observer to rebind the notification listener when dots are re-enabled. + mSettingsCache = SettingsCache.INSTANCE.get(this); + mNotificationSettingsChangedListener = this::onNotificationSettingsChanged; + mSettingsCache.register(NOTIFICATION_BADGING_URI, + mNotificationSettingsChangedListener); + mSettingsCache.dispatchOnChange(NOTIFICATION_BADGING_URI); onNotificationFullRefresh(); } @@ -229,7 +232,7 @@ public class NotificationListener extends NotificationListenerService { public void onListenerDisconnected() { super.onListenerDisconnected(); sIsConnected = false; - mNotificationDotsObserver.unregister(); + mSettingsCache.unregister(NOTIFICATION_BADGING_URI, mNotificationSettingsChangedListener); onNotificationFullRefresh(); } diff --git a/src/com/android/launcher3/settings/NotificationDotsPreference.java b/src/com/android/launcher3/settings/NotificationDotsPreference.java index a91303a529..a35416915f 100644 --- a/src/com/android/launcher3/settings/NotificationDotsPreference.java +++ b/src/com/android/launcher3/settings/NotificationDotsPreference.java @@ -35,14 +35,14 @@ import androidx.preference.PreferenceViewHolder; import com.android.launcher3.R; import com.android.launcher3.notification.NotificationListener; -import com.android.launcher3.util.SecureSettingsObserver; +import com.android.launcher3.util.SettingsCache; /** * A {@link Preference} for indicating notification dots status. * Also has utility methods for updating UI based on dots status changes. */ public class NotificationDotsPreference extends Preference - implements SecureSettingsObserver.OnChangeListener { + implements SettingsCache.OnChangeListener { private boolean mWidgetFrameVisible = false; diff --git a/src/com/android/launcher3/settings/SettingsActivity.java b/src/com/android/launcher3/settings/SettingsActivity.java index 922425fca1..ac8dac5f7e 100644 --- a/src/com/android/launcher3/settings/SettingsActivity.java +++ b/src/com/android/launcher3/settings/SettingsActivity.java @@ -18,13 +18,13 @@ package com.android.launcher3.settings; import static androidx.core.view.accessibility.AccessibilityNodeInfoCompat.ACTION_ACCESSIBILITY_FOCUS; +import static com.android.launcher3.util.SettingsCache.NOTIFICATION_BADGING_URI; +import static com.android.launcher3.util.SettingsCache.NOTIFICATION_ENABLED_LISTENERS; import static com.android.launcher3.states.RotationHelper.ALLOW_ROTATION_PREFERENCE_KEY; import static com.android.launcher3.states.RotationHelper.getAllowRotationDefaultValue; -import static com.android.launcher3.util.SecureSettingsObserver.newNotificationSettingsObserver; import android.content.SharedPreferences; import android.os.Bundle; -import android.provider.Settings; import android.text.TextUtils; import androidx.annotation.NonNull; @@ -45,8 +45,8 @@ import com.android.launcher3.R; import com.android.launcher3.Utilities; import com.android.launcher3.config.FeatureFlags; import com.android.launcher3.model.WidgetsModel; +import com.android.launcher3.util.SettingsCache; import com.android.launcher3.uioverrides.plugins.PluginManagerWrapper; -import com.android.launcher3.util.SecureSettingsObserver; /** * Settings activity for Launcher. Currently implements the following setting: Allow rotation @@ -59,8 +59,6 @@ public class SettingsActivity extends FragmentActivity private static final String FLAGS_PREFERENCE_KEY = "flag_toggler"; private static final String NOTIFICATION_DOTS_PREFERENCE_KEY = "pref_icon_badging"; - /** Hidden field Settings.Secure.ENABLED_NOTIFICATION_LISTENERS */ - private static final String NOTIFICATION_ENABLED_LISTENERS = "enabled_notification_listeners"; public static final String EXTRA_FRAGMENT_ARG_KEY = ":settings:fragment_args_key"; public static final String EXTRA_SHOW_FRAGMENT_ARGS = ":settings:show_fragment_args"; @@ -126,10 +124,11 @@ public class SettingsActivity extends FragmentActivity */ public static class LauncherSettingsFragment extends PreferenceFragmentCompat { - private SecureSettingsObserver mNotificationDotsObserver; + private SettingsCache mSettingsCache; private String mHighLightKey; private boolean mPreferenceHighlighted = false; + private NotificationDotsPreference mNotificationSettingsChangedListener; @Override public void onCreatePreferences(Bundle savedInstanceState, String rootKey) { @@ -177,14 +176,16 @@ public class SettingsActivity extends FragmentActivity } // Listen to system notification dot settings while this UI is active. - mNotificationDotsObserver = newNotificationSettingsObserver( - getActivity(), (NotificationDotsPreference) preference); - mNotificationDotsObserver.register(); + mSettingsCache = SettingsCache.INSTANCE.get(getActivity()); + mNotificationSettingsChangedListener = + ((NotificationDotsPreference) preference); + mSettingsCache.register(NOTIFICATION_BADGING_URI, + (NotificationDotsPreference) mNotificationSettingsChangedListener); // Also listen if notification permission changes - mNotificationDotsObserver.getResolver().registerContentObserver( - Settings.Secure.getUriFor(NOTIFICATION_ENABLED_LISTENERS), false, - mNotificationDotsObserver); - mNotificationDotsObserver.dispatchOnChange(); + mSettingsCache.register(NOTIFICATION_ENABLED_LISTENERS, + mNotificationSettingsChangedListener); + mSettingsCache.dispatchOnChange(NOTIFICATION_BADGING_URI); + mSettingsCache.dispatchOnChange(NOTIFICATION_ENABLED_LISTENERS); return true; case ALLOW_ROTATION_PREFERENCE_KEY: @@ -251,9 +252,11 @@ public class SettingsActivity extends FragmentActivity @Override public void onDestroy() { - if (mNotificationDotsObserver != null) { - mNotificationDotsObserver.unregister(); - mNotificationDotsObserver = null; + if (mSettingsCache != null) { + mSettingsCache.unregister(NOTIFICATION_BADGING_URI, + mNotificationSettingsChangedListener); + mSettingsCache.unregister(NOTIFICATION_ENABLED_LISTENERS, + mNotificationSettingsChangedListener); } super.onDestroy(); } diff --git a/src/com/android/launcher3/util/SecureSettingsObserver.java b/src/com/android/launcher3/util/SecureSettingsObserver.java deleted file mode 100644 index 9fe72ad80a..0000000000 --- a/src/com/android/launcher3/util/SecureSettingsObserver.java +++ /dev/null @@ -1,104 +0,0 @@ -/* - * Copyright (C) 2017 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; - -import android.content.ContentResolver; -import android.content.Context; -import android.database.ContentObserver; -import android.os.Handler; -import android.provider.Settings; - -/** - * Utility class to listen for secure settings changes - */ -public class SecureSettingsObserver extends ContentObserver { - - /** Hidden field Settings.Secure.NOTIFICATION_BADGING */ - public static final String NOTIFICATION_BADGING = "notification_badging"; - /** Hidden field Settings.Secure.ONE_HANDED_MODE_ENABLED */ - public static final String ONE_HANDED_ENABLED = "one_handed_mode_enabled"; - /** Hidden field Settings.Secure.SWIPE_BOTTOM_TO_NOTIFICATION_ENABLED */ - public static final String ONE_HANDED_SWIPE_BOTTOM_TO_NOTIFICATION_ENABLED = - "swipe_bottom_to_notification_enabled"; - - private final ContentResolver mResolver; - private final String mKeySetting; - private final int mDefaultValue; - private final OnChangeListener mOnChangeListener; - - public SecureSettingsObserver(ContentResolver resolver, OnChangeListener listener, - String keySetting, int defaultValue) { - super(new Handler()); - - mResolver = resolver; - mOnChangeListener = listener; - mKeySetting = keySetting; - mDefaultValue = defaultValue; - } - - @Override - public void onChange(boolean selfChange) { - mOnChangeListener.onSettingsChanged(getValue()); - } - - public boolean getValue() { - return Settings.Secure.getInt(mResolver, mKeySetting, mDefaultValue) == 1; - } - - public void register() { - mResolver.registerContentObserver(Settings.Secure.getUriFor(mKeySetting), false, this); - } - - public ContentResolver getResolver() { - return mResolver; - } - - public void dispatchOnChange() { - onChange(true); - } - - public void unregister() { - mResolver.unregisterContentObserver(this); - } - - public interface OnChangeListener { - void onSettingsChanged(boolean isEnabled); - } - - public static SecureSettingsObserver newNotificationSettingsObserver(Context context, - OnChangeListener listener) { - return new SecureSettingsObserver( - context.getContentResolver(), listener, NOTIFICATION_BADGING, 1); - } - - public static SecureSettingsObserver newOneHandedSettingsObserver(Context context, - OnChangeListener listener) { - return new SecureSettingsObserver( - context.getContentResolver(), listener, ONE_HANDED_ENABLED, 0); - } - - /** - * Constructs settings observer for {@link #ONE_HANDED_SWIPE_BOTTOM_TO_NOTIFICATION_ENABLED} - * preference. - */ - public static SecureSettingsObserver newSwipeToNotificationSettingsObserver(Context context, - OnChangeListener listener) { - return new SecureSettingsObserver( - context.getContentResolver(), listener, - ONE_HANDED_SWIPE_BOTTOM_TO_NOTIFICATION_ENABLED, 1); - } -} diff --git a/src/com/android/launcher3/util/SettingsCache.java b/src/com/android/launcher3/util/SettingsCache.java new file mode 100644 index 0000000000..22b4d38891 --- /dev/null +++ b/src/com/android/launcher3/util/SettingsCache.java @@ -0,0 +1,179 @@ +/* + * Copyright (C) 2021 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; + +import static android.provider.Settings.System.ACCELEROMETER_ROTATION; + +import android.content.ContentResolver; +import android.content.Context; +import android.database.ContentObserver; +import android.net.Uri; +import android.os.Handler; +import android.provider.Settings; + +import androidx.annotation.VisibleForTesting; + +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.concurrent.ConcurrentHashMap; +import java.util.concurrent.CopyOnWriteArrayList; + +/** + * ContentObserver over Settings keys that also has a caching layer. + * Consumers can register for callbacks via {@link #register(Uri, OnChangeListener)} and + * {@link #unregister(Uri, OnChangeListener)} methods. + * + * This can be used as a normal cache without any listeners as well via the + * {@link #getValue(Uri, int)} and {@link #dispatchOnChange(Uri)} to update (and subsequently call + * get) + * + * The cache will be invalidated/updated through the normal + * {@link ContentObserver#onChange(boolean)} calls + * or can be force updated by calling {@link #dispatchOnChange(Uri)}. + * + * Cache will also be updated if a key queried is missing (even if it has no listeners registered). + */ +public class SettingsCache extends ContentObserver { + + /** Hidden field Settings.Secure.NOTIFICATION_BADGING */ + public static final Uri NOTIFICATION_BADGING_URI = + Settings.Secure.getUriFor("notification_badging"); + /** Hidden field Settings.Secure.ONE_HANDED_MODE_ENABLED */ + public static final String ONE_HANDED_ENABLED = "one_handed_mode_enabled"; + /** Hidden field Settings.Secure.SWIPE_BOTTOM_TO_NOTIFICATION_ENABLED */ + public static final String ONE_HANDED_SWIPE_BOTTOM_TO_NOTIFICATION_ENABLED = + "swipe_bottom_to_notification_enabled"; + /** Hidden field Settings.Secure.ENABLED_NOTIFICATION_LISTENERS */ + public static final Uri NOTIFICATION_ENABLED_LISTENERS = + Settings.Secure.getUriFor("enabled_notification_listeners"); + public static final Uri ROTATION_SETTING_URI = + Settings.System.getUriFor(ACCELEROMETER_ROTATION); + + private static final String SYSTEM_URI_PREFIX = Settings.System.CONTENT_URI.toString(); + + /** + * Caches the last seen value for registered keys. + */ + private Map mKeyCache = new ConcurrentHashMap<>(); + private final Map> mListenerMap = new HashMap<>(); + protected final ContentResolver mResolver; + + + /** + * Singleton instance + */ + public static MainThreadInitializedObject INSTANCE = + new MainThreadInitializedObject<>(SettingsCache::new); + + private SettingsCache(Context context) { + super(new Handler()); + mResolver = context.getContentResolver(); + } + + @Override + public void onChange(boolean selfChange, Uri uri) { + // We use default of 1, but if we're getting an onChange call, can assume a non-default + // value will exist + boolean newVal = updateValue(uri, 1 /* Effectively Unused */); + if (!mListenerMap.containsKey(uri)) { + return; + } + + for (OnChangeListener listener : mListenerMap.get(uri)) { + listener.onSettingsChanged(newVal); + } + } + + /** + * Returns the value for this classes key from the cache. If not in cache, will call + * {@link #updateValue(Uri, int)} to fetch. + */ + public boolean getValue(Uri keySetting, int defaultValue) { + if (mKeyCache.containsKey(keySetting)) { + return mKeyCache.get(keySetting); + } else { + return updateValue(keySetting, defaultValue); + } + } + + /** + * Does not de-dupe if you add same listeners for the same key multiple times. + * Unregister once complete using {@link #unregister(Uri, OnChangeListener)} + */ + public void register(Uri uri, OnChangeListener changeListener) { + if (mListenerMap.containsKey(uri)) { + mListenerMap.get(uri).add(changeListener); + } else { + CopyOnWriteArrayList l = new CopyOnWriteArrayList<>(); + l.add(changeListener); + mListenerMap.put(uri, l); + mResolver.registerContentObserver(uri, false, this); + } + } + + private boolean updateValue(Uri keyUri, int defaultValue) { + String key = keyUri.getLastPathSegment(); + boolean newVal; + if (keyUri.toString().startsWith(SYSTEM_URI_PREFIX)) { + newVal = Settings.System.getInt(mResolver, key, defaultValue) == 1; + } else { // SETTING_SECURE + newVal = Settings.Secure.getInt(mResolver, key, defaultValue) == 1; + } + + mKeyCache.put(keyUri, newVal); + return newVal; + } + + /** + * Force update a change for a given URI and have all listeners for that URI receive callbacks + * even if the value is unchanged. + */ + public void dispatchOnChange(Uri uri) { + onChange(true, uri); + } + + /** + * Call to stop receiving updates on the given {@param listener}. + * This Uri/Listener pair must correspond to the same pair called with for + * {@link #register(Uri, OnChangeListener)} + */ + public void unregister(Uri uri, OnChangeListener listener) { + List listenersToRemoveFrom = mListenerMap.get(uri); + if (!listenersToRemoveFrom.contains(listener)) { + return; + } + + listenersToRemoveFrom.remove(listener); + if (listenersToRemoveFrom.isEmpty()) { + mListenerMap.remove(uri); + } + } + + /** + * Don't use this. Ever. + * @param keyCache Cache to replace {@link #mKeyCache} + */ + @VisibleForTesting + void setKeyCache(Map keyCache) { + mKeyCache = keyCache; + } + + public interface OnChangeListener { + void onSettingsChanged(boolean isEnabled); + } +} diff --git a/tests/Android.mk b/tests/Android.mk index 3d9077d4bd..43d51fca01 100644 --- a/tests/Android.mk +++ b/tests/Android.mk @@ -32,7 +32,6 @@ else LOCAL_SRC_FILES := $(call all-java-files-under, tapl) \ ../src/com/android/launcher3/ResourceUtils.java \ - ../src/com/android/launcher3/util/SecureSettingsObserver.java \ ../src/com/android/launcher3/testing/TestProtocol.java endif -- GitLab From 3a06568d8bcfb8a28eb0c5f631bf1b9c79084956 Mon Sep 17 00:00:00 2001 From: Alex Chau Date: Thu, 11 Feb 2021 13:54:27 +0000 Subject: [PATCH 0532/1664] Use PersistedTaskSnapshotData to calculate TaskView size - Disabled variable width for running task, as PersistedTaskSnapshotData is not always up to date for current task after rotating the device Bug: 174464656 Test: Test tablet overview with different comination of portrait/landscape wide/narrow thumbnails in portrait/landscape Change-Id: Ifa01e72020ccaf422e3975b092462de85fa19efd --- .../android/quickstep/RecentTasksList.java | 5 ++-- .../android/quickstep/views/RecentsView.java | 11 +++++--- .../quickstep/views/TaskThumbnailView.java | 28 ------------------- .../com/android/quickstep/views/TaskView.java | 13 ++++++--- 4 files changed, 18 insertions(+), 39 deletions(-) diff --git a/quickstep/src/com/android/quickstep/RecentTasksList.java b/quickstep/src/com/android/quickstep/RecentTasksList.java index 70b4f20c2b..1c5dc4c829 100644 --- a/quickstep/src/com/android/quickstep/RecentTasksList.java +++ b/quickstep/src/com/android/quickstep/RecentTasksList.java @@ -191,6 +191,7 @@ public class RecentTasksList extends TaskStackChangeListener { } else { task = new Task(taskKey); } + task.setLastSnapshotData(rawTask); allTasks.add(task); } @@ -200,9 +201,7 @@ public class RecentTasksList extends TaskStackChangeListener { private ArrayList copyOf(ArrayList tasks) { ArrayList newTasks = new ArrayList<>(); for (int i = 0; i < tasks.size(); i++) { - Task t = tasks.get(i); - newTasks.add(new Task(t.key, t.colorPrimary, t.colorBackground, t.isDockable, - t.isLocked, t.taskDescription, t.topActivity)); + newTasks.add(new Task(tasks.get(i))); } return newTasks; } diff --git a/quickstep/src/com/android/quickstep/views/RecentsView.java b/quickstep/src/com/android/quickstep/views/RecentsView.java index 5d492acf84..deb1388e96 100644 --- a/quickstep/src/com/android/quickstep/views/RecentsView.java +++ b/quickstep/src/com/android/quickstep/views/RecentsView.java @@ -646,9 +646,9 @@ public abstract class RecentsView extends PagedView public TaskView getTaskView(int taskId) { for (int i = 0; i < getTaskViewCount(); i++) { - TaskView tv = getTaskViewAt(i); - if (tv.getTask() != null && tv.getTask().key != null && tv.getTask().key.id == taskId) { - return tv; + TaskView taskView = getTaskViewAt(i); + if (taskView.hasTaskId(taskId)) { + return taskView; } } return null; @@ -808,6 +808,7 @@ public abstract class RecentsView extends PagedView final Task task = tasks.get(i); final TaskView taskView = (TaskView) getChildAt(pageIndex); taskView.bind(task, mOrientationState); + taskView.updateTaskSize(!taskView.hasTaskId(mRunningTaskId)); } if (mNextPage == INVALID_PAGE) { @@ -942,7 +943,8 @@ public abstract class RecentsView extends PagedView // Force TaskView to update size from thumbnail final int taskCount = getTaskViewCount(); for (int i = 0; i < taskCount; i++) { - getTaskViewAt(i).updateTaskSize(); + TaskView taskView = getTaskViewAt(i); + taskView.updateTaskSize(!taskView.hasTaskId(mRunningTaskId)); } } @@ -1252,6 +1254,7 @@ public abstract class RecentsView extends PagedView // gesture and the task list is loaded and applied mTmpRunningTask = Task.from(new TaskKey(runningTaskInfo), runningTaskInfo, false); taskView.bind(mTmpRunningTask, mOrientationState); + taskView.updateTaskSize(false); // Measure and layout immediately so that the scroll values is updated instantly // as the user might be quick-switching diff --git a/quickstep/src/com/android/quickstep/views/TaskThumbnailView.java b/quickstep/src/com/android/quickstep/views/TaskThumbnailView.java index 3bd883ddc4..f2f4bc1238 100644 --- a/quickstep/src/com/android/quickstep/views/TaskThumbnailView.java +++ b/quickstep/src/com/android/quickstep/views/TaskThumbnailView.java @@ -111,9 +111,6 @@ public class TaskThumbnailView extends View implements PluginListener Date: Wed, 17 Feb 2021 11:40:07 +0000 Subject: [PATCH 0533/1664] Extract common codes for personal / work profile tabs These codes can be reused in the FullWidgetsSheet which we will be adding tabs for personal / work profile. Test: Set up work profile and then switch person / work profile tabs in the AllAppsContainerView. Bug: 179797520 Change-Id: Ib7eb1190e1384a664cbe3e34411c9362f1f6db03 --- res/layout/all_apps.xml | 30 +----- res/layout/personal_work_tabs.xml | 47 ++++++++++ res/layout/secondary_launcher.xml | 4 +- .../allapps/AllAppsContainerView.java | 18 ++-- .../launcher3/allapps/AllAppsPagedView.java | 60 ++---------- .../allapps/LauncherAllAppsContainerView.java | 9 +- .../workprofile/PersonalWorkPagedView.java | 92 +++++++++++++++++++ .../PersonalWorkSlidingTabStrip.java | 25 +++-- 8 files changed, 178 insertions(+), 107 deletions(-) create mode 100644 res/layout/personal_work_tabs.xml create mode 100644 src/com/android/launcher3/workprofile/PersonalWorkPagedView.java rename src/com/android/launcher3/{allapps => workprofile}/PersonalWorkSlidingTabStrip.java (85%) diff --git a/res/layout/all_apps.xml b/res/layout/all_apps.xml index 0041c9a7c8..8ed16c7493 100644 --- a/res/layout/all_apps.xml +++ b/res/layout/all_apps.xml @@ -40,35 +40,7 @@ - - -