Merging from ub-launcher3-master @ build 6248758 am: 3210c06687

Change-Id: I2b510554913b8a5444620b711547d962b5184002
This commit is contained in:
Automerger Merge Worker
2020-02-29 03:40:56 +00:00
96 changed files with 2736 additions and 808 deletions
+11
View File
@@ -47,3 +47,14 @@ java_library_static {
},
static_libs: ["libprotobuf-java-lite"],
}
java_library {
name: "LauncherPluginLib",
static_libs: ["PluginCoreLib"],
srcs: ["src_plugins/**/*.java"],
sdk_version: "current",
min_sdk_version: "28",
}
-18
View File
@@ -16,24 +16,6 @@
LOCAL_PATH := $(call my-dir)
#
# Build rule for plugin lib (needed to write a plugin).
#
include $(CLEAR_VARS)
LOCAL_USE_AAPT2 := true
LOCAL_AAPT2_ONLY := true
LOCAL_MODULE_TAGS := optional
LOCAL_STATIC_JAVA_LIBRARIES:= PluginCoreLib
LOCAL_SRC_FILES := \
$(call all-java-files-under, src_plugins)
LOCAL_SDK_VERSION := current
LOCAL_MIN_SDK_VERSION := 28
LOCAL_MODULE := LauncherPluginLib
include $(BUILD_STATIC_JAVA_LIBRARY)
#
# Build rule for Launcher3 dependencies lib.
#
+37
View File
@@ -0,0 +1,37 @@
148867106
139828243
133381284
145297320
149969889
label,
144052839
148099851
148099851
139750033
148896221
149993849
149935239
149967272
148900990
142753423
149870691
149870691
149870691
149422395
145253300
149927292
144854916
139828243
149867607
144052839
148542211
149200572
149197172
149199058
149215103
149198955
145595763
149481723
139750033
148542211
145647019
+30
View File
@@ -0,0 +1,30 @@
133381284
139750033
139828243
142753423
144052839
144854916
145253300
145297320
145595763
145647019
148099851
148542211
148867106
148896221
148900990
149197172
149198955
149199058
149200572
149215103
149422395
149481723
149867607
149870691
149927292
149935239
149967272
149969889
149993849
label,
+29
View File
@@ -0,0 +1,29 @@
133381284 erosky P1 ASSIGNED [R] Move split-screen presentation/layout to system-ui. ----
139750033 sunnygoyal P2 ASSIGNED Overview misfired ----
139828243 xuqiu P2 ASSIGNED [Overview Actions] Add Overview actions ----
142753423 sfufa P3 ACCEPTED Prototype predictive hotseat ----
144052839 tracyzhou P2 ACCEPTED Improving Launcher preview in ThemePicker ----
144854916 winsonc P2 ASSIGNED Add winscope logging for systemui/launcher ----
145253300 awickham P1 FIXED [a11y]Talkback doesn't focus on pop up window when long press Smart space. ----
145297320 zakcohen P2 FIXED Overview layout with anchor chips ----
145595763 sfufa P4 FIXED Wrong Tab is highlighted after creating Work profile ----
145647019 twickham P1 FIXED [a11y] Use Talkback page navigation to switch apps in Overview, Talkback couldn't move focus across apps one by one. ----
148099851 peskal P1 FIXED Make reduced scale snapshots toggle on/off base on config_reducedTaskSnapshotScale=0 ----
148542211 awickham P2 ASSIGNED Sandbox for gesture nav tutorial ----
148867106 vadimt P2 ASSIGNED [Flaky test] AddConfigWidgetTest.testConfigCancelled ----
148896221 hyunyoungs P3 ASSIGNED Migrate to soong ----
148900990 hyunyoungs P2 FIXED Hard to know if there's a text field for naming folders without a cursor ----
149197172 sfufa P1 FIXED [Regression] New work profile OOBE doesn't adopt theme color ----
149198955 sfufa P2 FIXED Work tab flash seems superfluous with new OOBE ----
149199058 sfufa P1 FIXED Work profile OOBE not dismissed after locking/unlocking screen ----
149200572 sfufa P1 FIXED [Regression] Work profile toggle is obscured by system navigation bar ----
149215103 sfufa P1 FIXED OOBE still displayed after removing work profile ----
149422395 vadimt P2 ASSIGNED Test should fail with a clear diags if it can't install a required package ----
149481723 sfufa P2 FIXED Launcher OOBE for work profile and COPE devices improvements ----
149867607 sfufa P0 FIXED [Failing test] WorkTabTest.workTabExists ----
149870691 twickham P2 ASSIGNED Cleanup AppWindowAnimationHelper and TransformParams ----
149927292 sfufa P0 FIXED [Flaky test] Apps view did not bind WorkTabTest.toggleWorks ----
149935239 hyunyoungs P1 FIXED Update the suggestFolderName when items are added and deleted from folders ----
149967272 hyunyoungs P1 FIXED Tap on Edit Name interaction drops the first recommendation ----
149969889 sfufa P2 FIXED [Crash] java.lang.NullPointerException: Attempt to invoke virtual method 'android.content.ComponentName ----
149993849 thiruram P1 FIXED Pixel launcher crashes when tapping on empty shortcut folder name ----
+965
View File
@@ -0,0 +1,965 @@
COMMAND>> git log 6a8d59d9649cde5626f57935dfeba27cb866583f..d8995a6fdaf363c5cbef2c7209b4b199c7e083af(B
commit d8995a6fdaf363c5cbef2c7209b4b199c7e083af
Merge: 3be0443d3 3dd64ca3a
Author: Vadim Tryshev <vadimt@google.com>
Date: Fri Feb 28 22:29:47 2020 +0000
Merge "Revert "TAPL: Verifying activity start/stop events"" into ub-launcher3-master
commit 3dd64ca3a8a43c4b0fcb28a761d9379c0de0e1de
Author: Vadim Tryshev <vadimt@google.com>
Date: Fri Feb 28 22:28:16 2020 +0000
Revert "TAPL: Verifying activity start/stop events"
This reverts commit 1186700c9a9d282824bad4b9cc566974c287a49d.
Reason for revert: Fails in the lab
Change-Id: I6715a058c76d96752b904331968b46a07ce93f85
commit 3be0443d377ee78af88bbe5d9551cfeefd280e06
Merge: 23ceafe3d f450b2500
Author: Vadim Tryshev <vadimt@google.com>
Date: Fri Feb 28 21:34:25 2020 +0000
Merge "Revert "Enabling flaky tests per our new flaky test policy"" into ub-launcher3-master
commit f450b2500c6854f59ad9b8ccc373199c631e681b
Author: Vadim Tryshev <vadimt@google.com>
Date: Fri Feb 28 21:33:16 2020 +0000
Revert "Enabling flaky tests per our new flaky test policy"
This reverts commit 983145c5944ae8c44166a1bccce94f077e3e9106.
Reason for revert: Too flaky
Change-Id: Ifabb638d6ce0a539518bc0b6b667a9783b6ca29d
commit 23ceafe3d5d1766f458c5cfe3234e92ea35db85a
Merge: 64a932ed2 dc24231e4
Author: Zak Cohen <zakcohen@google.com>
Date: Fri Feb 28 21:21:36 2020 +0000
Merge "TAPL tests - make all apps in overview checks less fragile." into ub-launcher3-master
commit dc24231e4faff53750a06e64a16d596add296dbe
Author: Zak Cohen <zakcohen@google.com>
Date: Fri Feb 28 11:59:33 2020 -0800
TAPL tests - make all apps in overview checks less fragile.
Tests that check for all apps in overview mode were only checking for
actions being enabled. This isn't sufficient logic, two button mode also
uses all apps.
Centralize the check for all apps.
Test: local tapl tests
Change-Id: If1bf98019e6f1aea8f7967883aba6780743e9d6b
commit 64a932ed2a7e166257c107676f35eb2e3c3c4c7c
Merge: 1fabbf5a2 1186700c9
Author: Vadim Tryshev <vadimt@google.com>
Date: Fri Feb 28 19:22:32 2020 +0000
Merge "TAPL: Verifying activity start/stop events" into ub-launcher3-master
commit 1fabbf5a23f2bb0aa83a2b9ceb7b255274e9ff03
Merge: 7ff297a81 983145c59
Author: Vadim Tryshev <vadimt@google.com>
Date: Fri Feb 28 18:29:23 2020 +0000
Merge "Enabling flaky tests per our new flaky test policy" into ub-launcher3-master
commit 983145c5944ae8c44166a1bccce94f077e3e9106
Author: vadimt <vadimt@google.com>
Date: Thu Feb 27 17:58:59 2020 -0800
Enabling flaky tests per our new flaky test policy
I'll check whether the tests are still flaky and assign to the test
owner if they are.
Bug: 148867106
Change-Id: Ic850abc862b0f266b100aa6bfff4e89b3802e50d
commit 7ff297a81faee4815a1feba6d64e6dd464bb7f0d
Author: Becky Qiu <xuqiu@google.com>
Date: Fri Feb 28 01:38:44 2020 +0000
Revert "[Overview Actions] Disable swiping for TaskView if it's in select mode."
This reverts commit 0499fb2060af0b7d065d77b22c81f4f77ee24eed.
Reason for revert: This might break the post submit test. See b/150401176
Change-Id: Iafc420fff72de8e83ce5e325fc1e5b1a1efe7350
commit 2fe6fb967163e4c91f90a1680b2004ec45e0730e
Merge: eecb258cc 3d64ffd4a
Author: Vadim Tryshev <vadimt@google.com>
Date: Thu Feb 27 22:28:59 2020 +0000
Merge "Fixing activity leak via alarm in SecondaryDropTarget" into ub-launcher3-master
commit 3d64ffd4a53e73c6fb4c04d6f144878ce4710d65
Author: vadimt <vadimt@google.com>
Date: Thu Feb 27 11:28:47 2020 -0800
Fixing activity leak via alarm in SecondaryDropTarget
Change-Id: I12c099ee1bb55d9db81e14d27d2eb09fbbc83e0f
commit eecb258cc7c81ce04feb903b15267257d480c8bc
Merge: 0e0b17636 9a4df4dba
Author: Zak Cohen <zakcohen@google.com>
Date: Thu Feb 27 19:48:51 2020 +0000
Merge changes from topic "enable" into ub-launcher3-master
* changes:
Overview Actions - Enable by default.
TAPL test update - don't run all apps in overview tests with actions.
commit 0e0b1763612778dc94d3c29e4d32018c854dbcc7
Merge: 3ba680055 da12b805c
Author: Vadim Tryshev <vadimt@google.com>
Date: Thu Feb 27 19:45:39 2020 +0000
Merge "Not showing "mismatched events" and other diags after Launcher crashes" into ub-launcher3-master
commit 3ba6800552f699f6937e7ff6d24ff66c20d6c2cc
Merge: 9f62bc10a 1779a3829
Author: TreeHugger Robot <treehugger-gerrit@google.com>
Date: Thu Feb 27 19:19:14 2020 +0000
Merge "Using StrictMode to detect activity leaks" into ub-launcher3-master
commit 1186700c9a9d282824bad4b9cc566974c287a49d
Author: vadimt <vadimt@google.com>
Date: Fri Feb 14 14:42:35 2020 -0800
TAPL: Verifying activity start/stop events
It also turned out that Pilfer event seems to come in a
non-deterministic order relative to the events from the Main and TIS
sequences. So I moved it to its own sequence.
Change-Id: I5851aafb6d04398c5727712eaf8561916a30c4c5
commit 9f62bc10afe375032453d5361707acd6c9df439b
Merge: b5565fb62 dbc758305
Author: Vadim Tryshev <vadimt@google.com>
Date: Thu Feb 27 19:08:29 2020 +0000
Merge "Remove unnecessary PilferPointer events and enable testPressHomeOnAllAppsContextMenu" into ub-launcher3-master
commit b5565fb62fb0256395c4a2fd9abf07c83451f26b
Merge: 39c610192 0499fb206
Author: Becky Qiu <xuqiu@google.com>
Date: Thu Feb 27 18:48:09 2020 +0000
Merge "[Overview Actions] Disable swiping for TaskView if it's in select mode." into ub-launcher3-master
commit 39c610192a8350adba64fdf6869f9b560163cb80
Merge: de9823243 6f9637662
Author: TreeHugger Robot <treehugger-gerrit@google.com>
Date: Thu Feb 27 01:14:52 2020 +0000
Merge "Catching OOM exceptions in auto-investigator" into ub-launcher3-master
commit de9823243a73662c211a5d405511d6bcf4f1f0a6
Merge: 833cd4320 f2a15f1be
Author: Zak Cohen <zakcohen@google.com>
Date: Wed Feb 26 21:25:36 2020 +0000
Merge "Make RectFSpringAnim resilient to negative height starting rect." into ub-launcher3-master
commit da12b805cc1c02b03048430840d4772399bcb733
Author: vadimt <vadimt@google.com>
Date: Wed Feb 26 12:17:47 2020 -0800
Not showing "mismatched events" and other diags after Launcher crashes
Change-Id: Iee5f8813c3610314d486589bc361466a05da0ecb
commit f2a15f1be54735ef41fe3a1dfea7f196a2e93b04
Author: Zak Cohen <zakcohen@google.com>
Date: Wed Feb 26 10:53:53 2020 -0800
Make RectFSpringAnim resilient to negative height starting rect.
Test: ran tapl tests, failed before, passed after
Change-Id: I10b82dd348b5c12550e60b145c76dad542aa56e8
commit 833cd43206cefdc2d653d2ab42cb5b6c9317b278
Merge: 5991eb833 65ed9c3d9
Author: TreeHugger Robot <treehugger-gerrit@google.com>
Date: Wed Feb 26 17:47:59 2020 +0000
Merge "Adding a failure investigator" into ub-launcher3-master
commit 5991eb83377e84011d90cc40eb3ebe7617292a49
Merge: b066af18a 5bb63c916
Author: TreeHugger Robot <treehugger-gerrit@google.com>
Date: Wed Feb 26 17:42:40 2020 +0000
Merge changes from topic "new-task-snapshots-ub-launcher3-master" into ub-launcher3-master
* changes:
Toggle loading state based on config_lowResTaskSnapshotScale
Add config to enable snapshot preloading
commit b066af18a51a7ab6ac167c3b9c5532c6aed1f294
Merge: 0c5f58afb e6981a606
Author: TreeHugger Robot <treehugger-gerrit@google.com>
Date: Wed Feb 26 07:59:20 2020 +0000
Merge "Update mEndRect when closing ArrowPopup" into ub-launcher3-master
commit dbc7583054405ab0f87a9394c56ecbf4241d8159
Author: vadimt <vadimt@google.com>
Date: Wed Feb 19 19:17:28 2020 -0800
Remove unnecessary PilferPointer events and enable testPressHomeOnAllAppsContextMenu
Change-Id: I04199f46024b05f05d04ba2be7a797842bb48527
commit 0499fb2060af0b7d065d77b22c81f4f77ee24eed
Author: Becky Qiu <xuqiu@google.com>
Date: Tue Feb 25 15:12:55 2020 -0800
[Overview Actions] Disable swiping for TaskView if it's in select mode.
This including swiping up to dismiss, down to launch app, left and right to switch apps.
Test: local
Bug: 139828243
Change-Id: Ib588c8725fb765097d03a5def719abdffd603708
commit 0c5f58afb716d22fa3adb3a1962483f4430ae63c
Merge: f17241918 e7e91fd60
Author: Tracy Zhou <tracyzhou@google.com>
Date: Wed Feb 26 01:01:24 2020 +0000
Merge "Connect split-screen things to systemui divider" into ub-launcher3-master
commit f1724191896786e5e957489b34ef5692d43aed46
Merge: 37f435d62 bb18f4c9c
Author: TreeHugger Robot <treehugger-gerrit@google.com>
Date: Wed Feb 26 00:50:47 2020 +0000
Merge "Fixing log message" into ub-launcher3-master
commit 37f435d62462c31c99c32d9222f539b104338a06
Merge: 43a161579 20cc7a3b5
Author: TreeHugger Robot <treehugger-gerrit@google.com>
Date: Wed Feb 26 00:35:40 2020 +0000
Merge "Update proguard flags." into ub-launcher3-master
commit e7e91fd60e5f78e63dd1b92fe21cb38a9f0cc494
Author: Evan Rosky <erosky@google.com>
Date: Wed Nov 20 17:00:06 2019 -0800
Connect split-screen things to systemui divider
Since divider stuff lives in sysui instead of framework
Bug: 133381284
Test: Manual, open 2 apps in split and drag-up to show
recents.
Change-Id: If6740b7ee4829bf4cac6e829e81943f16a41f977
(cherry picked from commit 3ef159becdbda1041781920eb816d041bd88da44)
commit bb18f4c9c6acfeef5666493ae904020b55414fd9
Author: Sunny Goyal <sunnygoyal@google.com>
Date: Tue Feb 25 15:27:06 2020 -0800
Fixing log message
Change-Id: I0668ea273a54ea741b1b871a50a8738c0ec4788b
commit 9a4df4dba1512f4aa67da5c7f9d69a1ad22f3805
Author: Zak Cohen <zakcohen@google.com>
Date: Wed Feb 19 14:52:10 2020 -0800
Overview Actions - Enable by default.
Enable the Overview Actions feature flag by default.
Test: local
Bug: 145297320
Change-Id: I6f4a2b6e90704b78d1ff1f77bbfea2d8789169ff
commit 43a161579600c2f7cbce8d96e2474f97983f7d15
Author: Zak Cohen <zakcohen@google.com>
Date: Tue Feb 25 14:29:37 2020 -0800
Hide apps view in transitions if none of its parts are visible.
Check where the all apps view has any pieces visible, if not hide it.
Test: local
Change-Id: I1d9b957b6c10dfbe4beaa56c4fee920844699a6c
commit fe935dfbc198192780f9c3bdd94fdfb1b849d33b
Author: Zak Cohen <zakcohen@google.com>
Date: Thu Feb 20 15:08:10 2020 -0800
TAPL test update - don't run all apps in overview tests with actions.
Overview actions removes the all apps from overview. Don't run the tests
that depend on these.
Ultimately need to add more tests for the actions.
Test: tapl local
Change-Id: I2471d10af7bc03a40a94f99aa16354b85bdb3ad7
commit 1779a38290933a2b72df87795b5c8f1758da2ece
Author: vadimt <vadimt@google.com>
Date: Tue Feb 25 14:41:01 2020 -0800
Using StrictMode to detect activity leaks
Change-Id: I615f641897d57be0cd31be944796c6931ef9ab00
commit 6f96376620b34b68c5896d25a315fc0f8b72ac80
Author: vadimt <vadimt@google.com>
Date: Tue Feb 25 11:05:39 2020 -0800
Catching OOM exceptions in auto-investigator
In the development environment, the device may stay for long time
without reboot, and the logcat size may turn to be long enough to
overflow the memory.
Suppressing OOM exceptions.
Change-Id: I562e2c03312e88f669d9cb660af6e8849dc0c8ef
commit d71c3b185de62a50b730bbf389d48ead9ccc6315
Merge: 86430dd2d 93c4de741
Author: TreeHugger Robot <treehugger-gerrit@google.com>
Date: Tue Feb 25 18:23:19 2020 +0000
Merge "Setting interpolator beforehand, instead of overriding it after the pending animaiton is created" into ub-launcher3-master
commit 86430dd2d25ba69220f20e5cc2f5379cadcef27c
Merge: 6382b39ff b79614cc9
Author: TreeHugger Robot <treehugger-gerrit@google.com>
Date: Tue Feb 25 18:20:39 2020 +0000
Merge "Performing a check for a crashed Launcher process for each UI test" into ub-launcher3-master
commit 65ed9c3d9bb6c0086613b3e6512bdb0a1f673936
Author: vadimt <vadimt@google.com>
Date: Mon Feb 24 19:10:50 2020 -0800
Adding a failure investigator
Change-Id: Ie1c9820ecb7373a1f4428a24867c51332a84e65c
commit b79614cc93f7a54dead7c19e6e225cc4a832ba53
Author: vadimt <vadimt@google.com>
Date: Mon Feb 24 18:24:05 2020 -0800
Performing a check for a crashed Launcher process for each UI test
commit 6382b39ff4605fbe30d8edf9ba25d32a7c04d7e5
Author: Tony Wickham <twickham@google.com>
Date: Mon Feb 24 17:06:17 2020 -0800
Add null check to sendCustomAccessibilityEvent()
ag/10273975 caused a regression where launcher crashes if RecentsView is
empty.
Change-Id: I1607627be0a9748fd552adc757296d7b6bbfd1e2
commit e6981a606abac6fe70cbb9fdefd16a25fb2f2f58
Author: Tony Wickham <twickham@google.com>
Date: Mon Feb 24 17:33:20 2020 -0800
Update mEndRect when closing ArrowPopup
Generally mEndRect is still correct from when we set it in
animateOpen(), but there are cases where it is no longer valid and
should be recomputed in animateClose(). For example, if you dismiss
notifications, the height of the popup changes between opening and
closing it.
Test: Long press an icon with a notification dot, dismiss all
notifications from the popup and then close the popup. The shadow
follows the outline of the popup, instead of extending well below it.
Change-Id: Ic5d6ad2807ea1ec8aff5e3d6ce8abaa11ce5d33c
commit 93c4de7413f40a3660fdecb3a19fe5271e93d360
Author: Sunny Goyal <sunnygoyal@google.com>
Date: Mon Feb 24 16:39:36 2020 -0800
Setting interpolator beforehand, instead of overriding it after the pending
animaiton is created
Change-Id: I7caec2eeb86084591fa627518d064b7ebb895d65
commit 20cc7a3b5b2ef2e49b85d2cd51320c4c0183e244
Author: Amos Bianchi <amosbianchi@google.com>
Date: Mon Feb 24 15:35:49 2020 -0800
Update proguard flags.
Fully R8 optimized variants may crash with:
java.lang.AbstractMethodError exception.
Test: build, manual test on device.
Change-Id: I18a7b011ddd3536e3d33b991954a52e5fc5c7d56
commit 436573401765cdc97d8cf672ace3ccbea16badeb
Merge: 6b7fbd22d be13d109b
Author: Tracy Zhou <tracyzhou@google.com>
Date: Mon Feb 24 20:01:58 2020 +0000
Merge "Render user's actual workspace in ThemePicker preview (Part 3)" into ub-launcher3-master
commit 6b7fbd22dda79a31e75da18da4b0bf05cd25a85a
Merge: d2c56525b 27d3c595c
Author: Andy Wickham <awickham@google.com>
Date: Mon Feb 24 18:49:12 2020 +0000
Merge "Makes all ArrowPopups AccessibilityTargets." into ub-launcher3-master
commit d2c56525bb18c5e971aced5f60857c2f70d502d6
Merge: 7fa947cb3 a173193af
Author: Sunny Goyal <sunnygoyal@google.com>
Date: Mon Feb 24 18:30:13 2020 +0000
Merge "Using LeastSquaresVelocityTrackerStrategy for calculating velocity when detecting motion pause events. This is the same logic used by the platform implementation VelocityTracker" into ub-launcher3-master
commit 7fa947cb316945f40cab5cf7b5ed20e8b82dc219
Merge: bc5056391 5a93eb3ba
Author: TreeHugger Robot <treehugger-gerrit@google.com>
Date: Mon Feb 24 18:24:22 2020 +0000
Merge "Fix NPE on app launch log" into ub-launcher3-master
commit 5a93eb3bafbe38bccd34c59c4843c318135f7975
Author: Samuel Fufa <sfufa@google.com>
Date: Fri Feb 21 18:51:36 2020 -0800
Fix NPE on app launch log
Bug: 149969889
Change-Id: I60fd283ac082fab1ebf6ea90568e054525c414cc
commit bc50563915e92d3eec594c45af365f327d277996
Merge: 218f5276a a241df66d
Author: Sreyas Rangaraju <sreyasr@google.com>
Date: Sat Feb 22 01:11:47 2020 +0000
Merge "OverviewActions: Creating parent view to house RecentsView and Overview Actions." into ub-launcher3-master
commit 218f5276a3b8e7ac3a5d3dc95c30087149e52e5b
Merge: 3310b76a2 15d79a9b8
Author: TreeHugger Robot <treehugger-gerrit@google.com>
Date: Sat Feb 22 00:55:55 2020 +0000
Merge "Fixes incorrect FromFolderLabelState in clearcut log when updating folder label." into ub-launcher3-master
commit 3310b76a27ef0f7bdb332e82faea498e868c5f9a
Merge: 6b029c30b a754374c3
Author: Tony Wickham <twickham@google.com>
Date: Sat Feb 22 00:34:09 2020 +0000
Merge "Remove mSourceWindowClipInsetsForLiveTile" into ub-launcher3-master
commit 15d79a9b85cb58319e51ffa364ab38e804da307f
Author: thiruram <thiruram@google.com>
Date: Fri Feb 21 14:31:02 2020 -0800
Fixes incorrect FromFolderLabelState in clearcut log when updating folder label.
Bug description: When folder label is updated from a suggested label to a custom label,
EditFolderLabelEvent log expected to have FromFolderLabelState = FROM_SUGGESTED.
Currently FromFolderLabelState is logged as FROM_CUSTOM in such scenarios.
Root cause: Folder label state is decided based on whether IME has entered into compose mode while editing.
This works fine for initial folder creation and also updated to non-empty label(to both custom or suggested).
But when folder is edited from suggested to empty label, its internal state changes from SUGGESTED to CUSTOM as expected.
Later when empty label is updated to suggested again, its internal state remains custom forever.
Fix: Instead of setting folder's internal state based on IME's compose mode, comparing actual folder label with suggestions
and set it to SUGGESTED if any suggestion matches else CUSTOM.
Change-Id: Ieea572ee93fd5567e3128c9bbcea3b670f0f01d9
commit a241df66df203e1fcb5133cb93f0968591003b25
Author: Sreyas <sreyasr@google.com>
Date: Wed Feb 5 16:58:34 2020 -0800
OverviewActions: Creating parent view to house RecentsView and Overview Actions.
Creating RecentsViewHolder to be contain LauncherRecentsView in OverviewPanel so Overview Actions View can be created only once.
Change-Id: I111f88903d2ff80275cc2e07b761577260073c17
commit be13d109b7d422e89a994554d2c4be30a86338b2
Author: Tracy Zhou <tracyzhou@google.com>
Date: Sun Jan 12 01:07:59 2020 -0800
Render user's actual workspace in ThemePicker preview (Part 3)
go/grid-migration-preview
With this change, we can see actual grid migration in wallpaper preview.
The approach here: we use a tmp table (favorites_preview) here specifically for this preview (to write off the migration results), and load from this tmp table workspace items if migration is necessary and successful. Otherwise, we load from the current workspace.
UPDATED: this change should be completely compatible with the new multi-db grid migration algorithm. Here is why
1. In LauncherPreviewRender#renderScreenShot, I added a check to decide which grid migration preview method we should call. Once v2 preview method is implemented, it should be integrated with other parts of this change perfectly (the reason will be mentioned below).
2. While we have multiple DBs, mOpenHelper in LauncherProvider always points to the current db we are using. Queries using CONTENT_URI is routed to whatever DB mOpenHelper points to, so it works perfectly to directly operate on CONTENT_URI even when we use multi-db underneath the hood.
3. With 1 and 2 mentioned, I believe in order for this preview change to support multi-db, we only need to implement the V2 grid migration algorithm. Because most of what we are doing in this change is wrapped in GridSizeMigrationTask, it's perfectly safeguarded.
Bug: 144052839
Change-Id: Ie6d6048d77326f96546c8a180a7cd8f15b47e4c4
commit 5bb63c916e757ca90a3ad56ccb6a8e340bb4fcab
Author: Peter Kalauskas <peskal@google.com>
Date: Fri Jan 24 18:51:13 2020 -0800
Toggle loading state based on config_lowResTaskSnapshotScale
Change high res loading state implementation to toggle on/off based on
config_lowResTaskSnapshotScale!=0 instead of ro.config.low_ram=true
Test: TaskSnapshotCacheTest
Test: TaskSnapshotControllerTest
Test: TaskSnapshotPersisterLoaderTest
Test: TaskSnapshotSurfaceTest
Bug: 148099851
Change-Id: I316e553a8a13fe38f1224c38f7aa717a0f628f57
commit 6b029c30b1e36435f4fd6c65bbe6d9f6f6b4d0c0
Merge: 977940502 8605be316
Author: Hyunyoung Song <hyunyoungs@google.com>
Date: Fri Feb 21 23:36:29 2020 +0000
Merge "Convert LauncherPluginLib to soong" into ub-launcher3-master
commit 03c8f296cd9ed6a95c4de07213c0ba0fda277023
Author: Peter Kalauskas <peskal@google.com>
Date: Tue Feb 18 16:20:27 2020 -0800
Add config to enable snapshot preloading
Enable preloading if config_enableTaskSnapshotPreloading instead of
assuming it should be off for all low ram devices. This allows it to be
configured via an overlay.
Test: TaskSnapshotCacheTest
Test: TaskSnapshotControllerTest
Test: TaskSnapshotPersisterLoaderTest
Test: TaskSnapshotSurfaceTest
Bug: 148099851
Change-Id: I9e3320dcff5b710e110cc7199c41c5a004991592
commit a173193afa082647db3cd44ce34403773aacad6a
Author: Sunny Goyal <sunnygoyal@google.com>
Date: Thu Feb 20 12:03:39 2020 -0800
Using LeastSquaresVelocityTrackerStrategy for calculating velocity
when detecting motion pause events. This is the same logic used by
the platform implementation VelocityTracker
Bug: 139750033
Change-Id: I87aabd46e58a5caa1395be3a63fd38d0ee75e355
commit 8605be3162fab552a0b950a2c4016986abe62148
Author: Hyunyoung Song <hyunyoungs@google.com>
Date: Fri Feb 21 14:52:25 2020 -0800
Convert LauncherPluginLib to soong
Bug: 148896221
Change-Id: I0c09ba2994cf2a8e230aebc51eb52a8acadf8686
commit 9779405023996bbe6bc2f9a050d66f4dbd9d5385
Merge: 621c1a8be 4e3eaa5a7
Author: TreeHugger Robot <treehugger-gerrit@google.com>
Date: Fri Feb 21 20:36:35 2020 +0000
Merge "[Overview Actions] Add ControlType for overview action buttons." into ub-launcher3-master
commit 621c1a8be7a0ee1ffff4af7a3f1e8c0e5a3c10ee
Merge: b47a08ad1 6d3e5465e
Author: Hyunyoung Song <hyunyoungs@google.com>
Date: Fri Feb 21 20:08:20 2020 +0000
Merge "Update the suggestFolderName when items are added and deleted from folders" into ub-launcher3-master
commit b47a08ad1eb7ce8b7b6ef7eb00b02f45ba6f88d6
Merge: fb5d542f5 888b516a2
Author: TreeHugger Robot <treehugger-gerrit@google.com>
Date: Fri Feb 21 20:05:38 2020 +0000
Merge "Fixes NPE when creating folder with null suggestedFolderNames." into ub-launcher3-master
commit 888b516a22ebb0a33be0656ede18606c037e9db3
Author: thiruram <thiruram@google.com>
Date: Fri Feb 21 10:42:23 2020 -0800
Fixes NPE when creating folder with null suggestedFolderNames.
Bug: 149993849
Change-Id: Ia2758b76aef1812dab225c8a95e7141fcdeefab4
commit 6d3e5465e23ef48bdd7a572c985ada779cbb9f42
Author: Hyunyoung Song <hyunyoungs@google.com>
Date: Wed Feb 19 23:32:04 2020 -0800
Update the suggestFolderName when items are added and deleted from folders
Bug: 149935239
Bug: 149967272
Bug: 148900990
Change-Id: I0ed27236ad22579a1f3dcfd35a32252c5b1f6691
commit fb5d542f51cb2a4532bb006bd36cf5f71a9e68f3
Merge: 1562d104b 46b4f5816
Author: TreeHugger Robot <treehugger-gerrit@google.com>
Date: Fri Feb 21 17:33:27 2020 +0000
Merge "Increase FileLog days" into ub-launcher3-master
commit 46b4f581610d322287473e8070a10f649c6985d6
Author: Samuel Fufa <sfufa@google.com>
Date: Wed Feb 19 15:56:12 2020 -0800
Increase FileLog days
Bug: 142753423
Change-Id: I43ccfe540cb0899a4a4ab6722b6670e830457f60
commit 1562d104b86fa4f231dbe823d96c61d27fb65057
Merge: f1030cf56 2f41808e9
Author: Vadim Tryshev <vadimt@google.com>
Date: Fri Feb 21 00:27:46 2020 +0000
Merge "Updating logcat reading logic" into ub-launcher3-master
commit f1030cf5642d8d21b1d1c47d3b3518fa7a388b64
Merge: aeaad97eb d1d342b1c
Author: Tony Wickham <twickham@google.com>
Date: Thu Feb 20 22:53:10 2020 +0000
Merge changes Ie48f4b66,Ia48f4453 into ub-launcher3-master
* changes:
Always update clip rect before applying surface params
Clean up AppWindowAnimationHelper and TransformParams
commit a754374c30195ecac5f54b3243f5a23e8dfaa8d2
Author: Tony Wickham <twickham@google.com>
Date: Wed Feb 19 13:30:34 2020 -0800
Remove mSourceWindowClipInsetsForLiveTile
It seems that the only time it is used, it is the same as
mSourceWindowClipInsets anyway.
Bug: 149870691
Change-Id: I3b1a5a47a0d49cdd6eb8a3271ca24da6c17109cb
commit d1d342b1c72c1dd500208098d82be18f4b23cf62
Author: Tony Wickham <twickham@google.com>
Date: Wed Feb 19 16:04:25 2020 -0800
Always update clip rect before applying surface params
Previously, we were only updating the clip rect when params.currentRect
== null, meaning the clip would be stale once the caller started
providing its own rect (e.g. when swiping to home).
Also fix some visual jumps when swiping home, all caused by running the
transform progress from 0 to 1 instead of starting at whatever the
progress was before ending the gesture, e.g.:
- When swiping to home without animating into an icon, the corner radius
was set back to the window corner radius.
- Before this change, the clip didn't update throughout the animation,
making the window slightly bigger than the floating icon view; after
this change, the clip jumped to show the insets again before clipping
back down during the home animation.
Bug: 149870691
Change-Id: Ie48f4b665a5bf3cbef76bdf7f043febe99fb84a0
commit badab61ace41602c78dd59732a1b954c7c0cf4aa
Author: Tony Wickham <twickham@google.com>
Date: Wed Feb 19 12:59:37 2020 -0800
Clean up AppWindowAnimationHelper and TransformParams
AppWindowAnimationHelper and TransformParams were being very tightly
intertwined, to the point that you really had to understand subtle
nuances of both in order to get a desired behavior. This makes making
changes really difficult, because there are lots of "traps" to know
about and navigate. To help alleviate this burden, cleaned up some of
these traps and give AWAH and TP distinct roles:
- A caller who needs to animate an app window needs both AWAH and TP.
TP defines specific parameters of how the app window should be
controlled, and AWAH simply reads from TP in order to provide the
desired behavior.
- Only the caller should write to TP; AWAH should no longer change
anything in a TP that is passed to it. For instance, instead of
repurposing TP.currentRect, AWAH now has its own mCurrentRect to
update based on passed parameters.
- TP is a very basic class that just holds various values that callers
can set one at a time. The order should not matter (setting one value
will never set another one), and defaults make sense.
- Commented the setter methods in TP so developers shouldn't have to
follow the code to see how they are used.
Bug: 149870691
Change-Id: Ia48f4453c29549271a3fc6538128a1a77439e015
commit 2f41808e9b7e361341c70c6978301f90bbacfc03
Author: vadimt <vadimt@google.com>
Date: Wed Feb 19 14:19:17 2020 -0800
Updating logcat reading logic
Start a thread to read logcat synchronusly instead
of back-tracking at the end of the test
Also:
* Reusing the same logcat process for all checks. This eliminates
reading the log from its start for every gesture. We dont kill that
process at the end, and dont stop the thread. Ive verified that
“am instrument” doesnt hang, and the logcat process gets automatically
killed after the test process exits.
* Not using mStarted latch, as there is no need to wait until the reader
reaches the start mark.
Bug: 149422395
Change-Id: Ide4ed19ad8d099c41918f38c2b073b8b2e143b69
commit 27d3c595cfd3c462b93364c357253c2f4b686f37
Author: Andy Wickham <awickham@google.com>
Date: Thu Feb 20 20:32:07 2020 +0000
Makes all ArrowPopups AccessibilityTargets.
This currently includes the menu that pops up when
long pressing an app icon in Launcher or Search, or
long pressing Smartspace. Previously, only the menu
for Launcher apps grabbed accessibility focus after
the open animation completed, and now all three do.
Fixes: 145253300
Change-Id: I147b45d38a04ab9a55eee9b5bd5551b4c7185fcf
commit aeaad97eb7ac5610491c35d2bc7bd15758d22966
Author: Samuel Fufa <sfufa@google.com>
Date: Thu Feb 20 11:21:26 2020 -0800
Increase timeout for WorkTabTest.toggleWorks test
Bug: 149927292
Change-Id: I6dcd0bfa2fe584be9e46f653992f9c748e795614
commit d446f984576fa7fbd5ed0555bdcb6a42440f12a4
Merge: 7be160170 f3b7246bf
Author: Winson Chung <winsonc@google.com>
Date: Thu Feb 20 06:37:46 2020 +0000
Merge "Adding new tracing call from SysUI" into ub-launcher3-master
commit 7be160170d1f1e81f0eea4305a8d01031a8633be
Merge: 91f97cb2d 10a285dc2
Author: Tracy Zhou <tracyzhou@google.com>
Date: Thu Feb 20 05:53:45 2020 +0000
Merge "Render user's actual workspace in ThemePicker preview (Part 5)" into ub-launcher3-master
commit f3b7246bf273b69a1a7390b4fbb7794ed57168ee
Author: Winson Chung <winsonc@google.com>
Date: Wed Nov 20 12:27:06 2019 -0800
Adding new tracing call from SysUI
Bug: 144854916
Test: This change is only to ensure the changes build
Change-Id: I62b247b45454861328b6062ed571c854a374aa34
commit 91f97cb2d1e5be3a17b040e34732b10df14c975f
Merge: 6e82910a4 d1c077972
Author: TreeHugger Robot <treehugger-gerrit@google.com>
Date: Thu Feb 20 01:49:30 2020 +0000
Merge "Increase timeout for flaky work test" into ub-launcher3-master
commit 4e3eaa5a71d127f30927e07cc42ddfa7afcb4887
Author: Becky Qiu <xuqiu@google.com>
Date: Wed Feb 19 17:37:22 2020 -0800
[Overview Actions] Add ControlType for overview action buttons.
Design: go/logging_for_overview_actions
Test:local, logging result: https://paste.googleplex.com/6489148393259008
Bug:139828243
Change-Id: I61fa66f8ec6edae43f6242599f8d4ea8e28d735d
commit 6e82910a45539afab26f60d60261c1b56c3885d7
Merge: 9fb7d28b6 619daaf82
Author: TreeHugger Robot <treehugger-gerrit@google.com>
Date: Thu Feb 20 01:16:42 2020 +0000
Merge "Give current TaskView accessibility focus" into ub-launcher3-master
commit d1c07797269afc77608809b72088f89fad71acf9
Author: Samuel Fufa <sfufa@google.com>
Date: Wed Feb 19 17:07:00 2020 -0800
Increase timeout for flaky work test
Bug:149867607
Change-Id: Ic746a4857a47fc29372d19419ddfa6eaebd06f87
commit 10a285dc24b75cb7469f284c945ea348ea305139
Author: Tracy Zhou <tracyzhou@google.com>
Date: Tue Feb 18 11:52:53 2020 -0800
Render user's actual workspace in ThemePicker preview (Part 5)
This change takes care of rendering widgets using widget provider's layout info.
Test: manual
Bug: 144052839
Change-Id: I7002d8bf653513cdd317736d550a47f61f0ee474
commit 9fb7d28b638d70be07337ce2c73038bdcfaf8b5d
Author: Andy Wickham <awickham@google.com>
Date: Thu Feb 20 00:07:06 2020 +0000
Removes exported="true" for RecentsActivity.
Change-Id: Iea9ad04886c3221cefa709c14b6501b2af83af06
commit 2eac20e3789bbd3d47d07a291d85fec9a7c53772
Merge: b3c991f7d 63b7f3633
Author: Andy Wickham <awickham@google.com>
Date: Wed Feb 19 23:51:20 2020 +0000
Merge "Renames BackGestureTutorialActivity to GestureSandboxActivity." into ub-launcher3-master
commit b3c991f7d344966a1892efd1a4077ad9b6d91161
Merge: 1e33123a0 9ac4b580a
Author: TreeHugger Robot <treehugger-gerrit@google.com>
Date: Wed Feb 19 21:45:38 2020 +0000
Merge "null check in showLableSuggestion()." into ub-launcher3-master
commit 63b7f36338c5ccce05b97d8fa16e740142360d4a
Author: Andy Wickham <awickham@google.com>
Date: Tue Feb 18 23:19:42 2020 +0000
Renames BackGestureTutorialActivity to GestureSandboxActivity.
Bug: 148542211
Change-Id: Iaac537380b02f1c52f967748c30fe49ac36bb91e
commit 1e33123a0ba651ccd9f020d333e6787948344bd5
Merge: 6eebaab5d 3b0c1f3ac
Author: Andy Wickham <awickham@google.com>
Date: Wed Feb 19 21:11:20 2020 +0000
Merge "Allows multiple gesture blocking activities to be specified." into ub-launcher3-master
commit 9ac4b580a1a5caabab15cb5b1eafa584da5f9504
Author: jayaprakashs <jayaprakashs@google.com>
Date: Wed Feb 19 12:38:05 2020 -0800
null check in showLableSuggestion().
It throws NPE when the data is missing for all the packages and all the suggestions are null.
Change-Id: I4aa36969e1aee00bcfc577a839d6da01d4e67a75
commit 6eebaab5d6ba9094715652f4b1107e43e5cb6035
Merge: aeb185e74 c3bd4d0e7
Author: TreeHugger Robot <treehugger-gerrit@google.com>
Date: Wed Feb 19 20:10:59 2020 +0000
Merge "Updating Pause detection to use motion events directly" into ub-launcher3-master
commit aeb185e744f8d1edac11dad6bd254bd0b741d9fb
Merge: b8d1e3526 ccebfbe27
Author: Samuel Fufa <sfufa@google.com>
Date: Wed Feb 19 19:53:38 2020 +0000
Merge "Clean up work profile" into ub-launcher3-master
commit ccebfbe2733e6570d3af0622262a1eb2e5633478
Author: Samuel Fufa <sfufa@google.com>
Date: Mon Feb 10 09:26:20 2020 -0800
Clean up work profile
This includes
- Dismiss work edu on launcher state change
- Remove work tab flash on first setup
- Make edu bottom sheet adopt theme color
- Fix Work toggle bottom inset
Bug: 149200572
Bug: 149197172
Bug: 149199058
Bug: 149215103
Bug: 149198955
Bug: 145595763
Bug:149481723
Test: Manual
Change-Id: I39a30782b80fd3a66bede55754fa30a940d2caee
commit c3bd4d0e75e150a44169551eba27db48bc66a462
Author: Sunny Goyal <sunnygoyal@google.com>
Date: Tue Feb 18 17:32:57 2020 -0800
Updating Pause detection to use motion events directly
Bug: 139750033
Change-Id: Ib261b203fec941f3c5303eafc1d435efdf3dbcaf
commit 3b0c1f3acaea80c332da2b49b35a58f1d5a1f53c
Author: Andy Wickham <awickham@google.com>
Date: Fri Feb 14 00:31:54 2020 +0000
Allows multiple gesture blocking activities to be specified.
Bug: 148542211
Change-Id: Ie299359eb2df60f5f08d156dc6882fa133fab27c
commit 619daaf82a65cbd1190a676b8582f1a7eb1cb774
Author: Tony Wickham <twickham@google.com>
Date: Fri Feb 7 12:29:06 2020 -0800
Give current TaskView accessibility focus
Send TYPE_VIEW_FOCUSED in the following places:
- When page transition ends on a task
- When finishing state transition to overview
- When un-hiding the current running task view
Bug: 145647019
Change-Id: I7bb357ea60e1dea79daf2ad50efa51071e064da8
@@ -16,6 +16,7 @@
package com.android.launcher3.model;
import android.content.ComponentName;
import android.content.Context;
import android.os.UserHandle;
@@ -68,4 +69,9 @@ public class WidgetsModel {
public void onPackageIconsUpdated(Set<String> packageNames, UserHandle user,
LauncherAppState app) {
}
public WidgetItem getWidgetProviderInfoByProviderName(
ComponentName providerName) {
return null;
}
}
+10 -1
View File
@@ -50,4 +50,13 @@
-dontwarn android.graphics.**
# Ignore warnings for hidden utility classes referenced from the shared lib
-dontwarn com.android.internal.util.**
-dontwarn com.android.internal.util.**
################ Do not optimize recents lib #############
-keep class com.android.systemui.** {
*;
}
-keep class com.android.quickstep.** {
*;
}
+4
View File
@@ -151,6 +151,10 @@ enum ControlType {
DISMISS_PREDICTION = 21;
HYBRID_HOTSEAT_ACCEPTED = 22;
HYBRID_HOTSEAT_CANCELED = 23;
OVERVIEW_ACTIONS_SHARE_BUTTON = 24;
OVERVIEW_ACTIONS_SCREENSHOT_BUTTON = 25;
OVERVIEW_ACTIONS_SELECT_BUTTON = 26;
SELECT_MODE_CLOSE_BUTTON = 27;
}
enum TipType {
+2 -5
View File
@@ -47,10 +47,7 @@
</intent-filter>
</service>
<!-- STOPSHIP: Change exported to false once all the integration is complete.
It is set to true so that the activity can be started from command line -->
<activity android:name="com.android.quickstep.RecentsActivity"
android:exported="true"
android:excludeFromRecents="true"
android:launchMode="singleTask"
android:clearTaskOnLaunch="true"
@@ -93,12 +90,12 @@
android:directBootAware="true" />
<activity
android:name="com.android.quickstep.interaction.BackGestureTutorialActivity"
android:name="com.android.quickstep.interaction.GestureSandboxActivity"
android:autoRemoveFromRecents="true"
android:excludeFromRecents="true"
android:screenOrientation="portrait">
<intent-filter>
<action android:name="com.android.quickstep.action.BACK_GESTURE_TUTORIAL" />
<action android:name="com.android.quickstep.action.GESTURE_SANDBOX" />
<category android:name="android.intent.category.DEFAULT" />
</intent-filter>
</activity>
@@ -15,5 +15,5 @@
-->
<vector android:height="24dp" android:viewportHeight="24"
android:viewportWidth="24" android:width="24dp" xmlns:android="http://schemas.android.com/apk/res/android">
<path android:fillColor="@color/bottom_panel_background" android:pathData="M19 9l1.25-2.75L23 5l-2.75-1.25L19 1l-1.25 2.75L15 5l2.75 1.25L19 9zm-7.5.5L9 4 6.5 9.5 1 12l5.5 2.5L9 20l2.5-5.5L17 12l-5.5-2.5zM19 15l-1.25 2.75L15 19l2.75 1.25L19 23l1.25-2.75L23 19l-2.75-1.25L19 15z"/>
<path android:fillColor="?android:attr/colorAccent" android:pathData="M19 9l1.25-2.75L23 5l-2.75-1.25L19 1l-1.25 2.75L15 5l2.75 1.25L19 9zm-7.5.5L9 4 6.5 9.5 1 12l5.5 2.5L9 20l2.5-5.5L17 12l-5.5-2.5zM19 15l-1.25 2.75L15 19l2.75 1.25L19 23l1.25-2.75L23 19l-2.75-1.25L19 15z"/>
</vector>
@@ -14,12 +14,17 @@
See the License for the specific language governing permissions and
limitations under the License.
-->
<com.android.quickstep.views.LauncherRecentsView
<com.android.launcher3.InsettableFrameLayout
xmlns:android="http://schemas.android.com/apk/res/android"
android:theme="@style/HomeScreenElementTheme"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:clipChildren="false"
android:clipToPadding="false"
android:accessibilityPaneTitle="@string/accessibility_recent_apps"
android:visibility="invisible" />
android:layout_width="wrap_content"
android:layout_height="wrap_content">
<com.android.quickstep.views.LauncherRecentsView
android:id="@+id/overview_panel_recents"
android:theme="@style/HomeScreenElementTheme"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:clipChildren="false"
android:clipToPadding="false"
android:accessibilityPaneTitle="@string/accessibility_recent_apps"
android:visibility="invisible" />
</com.android.launcher3.InsettableFrameLayout>
@@ -24,13 +24,13 @@
<View
android:layout_width="match_parent"
android:layout_height="32dp"
android:backgroundTint="@color/bottom_panel_background"
android:backgroundTint="?android:attr/colorAccent"
android:background="@drawable/bottom_sheet_top_border" />
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:background="@color/bottom_panel_background"
android:background="?android:attr/colorAccent"
android:orientation="vertical">
<TextView
@@ -34,6 +34,7 @@ import com.android.launcher3.R;
import com.android.launcher3.WorkspaceItemInfo;
import com.android.launcher3.uioverrides.QuickstepLauncher;
import com.android.launcher3.util.ActivityTracker;
import com.android.launcher3.util.Themes;
import java.util.List;
@@ -109,7 +110,7 @@ public class HotseatEduController {
NOTIFICATION_CHANNEL_ID)
.setContentTitle(name)
.setOngoing(true)
.setColor(mLauncher.getColor(R.color.bottom_panel_background))
.setColor(Themes.getColorAccent(mLauncher))
.setContentIntent(PendingIntent.getActivity(mLauncher, 0, intent,
PendingIntent.FLAG_CANCEL_CURRENT))
.setSmallIcon(R.drawable.hotseat_edu_notification_icon)
@@ -191,7 +191,7 @@ public class HotseatEduDialog extends AbstractSlideInView implements Insettable
|| predictions.size() < mLauncher.getDeviceProfile().inv.numHotseatIcons) {
return;
}
mLauncher.getDragLayer().addView(this);
attachToContainer();
logOnBoardingSeen();
animateOpen();
for (int i = 0; i < mLauncher.getDeviceProfile().inv.numHotseatIcons; i++) {
@@ -37,6 +37,7 @@ import static com.android.quickstep.SysUINavigationMode.removeShelfFromOverview;
import android.graphics.Rect;
import android.view.View;
import android.view.accessibility.AccessibilityEvent;
import android.view.animation.Interpolator;
import com.android.launcher3.AbstractFloatingView;
@@ -47,6 +48,7 @@ import com.android.launcher3.R;
import com.android.launcher3.Workspace;
import com.android.launcher3.allapps.DiscoveryBounce;
import com.android.launcher3.anim.AnimatorSetBuilder;
import com.android.launcher3.compat.AccessibilityManagerCompat;
import com.android.launcher3.userevent.nano.LauncherLogProto.Action;
import com.android.launcher3.userevent.nano.LauncherLogProto.ContainerType;
import com.android.quickstep.SysUINavigationMode;
@@ -143,6 +145,10 @@ public class OverviewState extends LauncherState {
public void onStateTransitionEnd(Launcher launcher) {
launcher.getRotationHelper().setCurrentStateRequest(REQUEST_ROTATE);
DiscoveryBounce.showForOverviewIfNeeded(launcher);
RecentsView recentsView = launcher.getOverviewPanel();
AccessibilityManagerCompat.sendCustomAccessibilityEvent(
recentsView.getPageAt(recentsView.getCurrentPage()),
AccessibilityEvent.TYPE_VIEW_FOCUSED, null);
}
@Override
@@ -172,7 +172,7 @@ public class FlingAndHoldTouchController extends PortraitStatesTouchController {
mMotionPauseDetector.setDisallowPause(!handlingOverviewAnim()
|| upDisplacement < mMotionPauseMinDisplacement
|| upDisplacement > mMotionPauseMaxDisplacement);
mMotionPauseDetector.addPosition(displacement, event.getEventTime());
mMotionPauseDetector.addPosition(event);
return super.onDrag(displacement, event);
}
@@ -317,7 +317,7 @@ public class NoButtonQuickSwitchTouchController implements TouchController,
// home screen elements will appear in the shelf on motion pause.
mMotionPauseDetector.setDisallowPause(mIsHomeScreenVisible
|| -displacement.y < mMotionPauseMinDisplacement);
mMotionPauseDetector.addPosition(displacement.y, ev.getEventTime());
mMotionPauseDetector.addPosition(ev);
if (mIsHomeScreenVisible) {
// Cancel the shelf anim so it doesn't clobber mNonOverviewAnim.
@@ -19,6 +19,7 @@ package com.android.launcher3.uioverrides.touchcontrollers;
import static com.android.launcher3.uioverrides.touchcontrollers.PortraitStatesTouchController.isTouchOverHotseat;
import android.view.MotionEvent;
import android.view.animation.Interpolator;
import com.android.launcher3.Launcher;
import com.android.launcher3.util.PendingAnimation;
@@ -74,12 +75,12 @@ public final class PortraitOverviewStateTouchHelper {
* @param duration how long the animation should be
* @return the animation
*/
PendingAnimation createSwipeDownToTaskAppAnimation(long duration) {
PendingAnimation createSwipeDownToTaskAppAnimation(long duration, Interpolator interpolator) {
mRecentsView.setCurrentPage(mRecentsView.getPageNearestToCenterOfScreen());
TaskView taskView = mRecentsView.getCurrentPageTaskView();
if (taskView == null) {
throw new IllegalStateException("There is no task view to animate to.");
}
return mRecentsView.createTaskLauncherAnimation(taskView, duration);
return mRecentsView.createTaskLaunchAnimation(taskView, duration, interpolator);
}
}
@@ -191,9 +191,8 @@ public abstract class TaskViewTouchController<T extends BaseDraggingActivity>
mEndDisplacement = -mTaskBeingDragged.getHeight();
} else {
mPendingAnimation = mRecentsView.createTaskLauncherAnimation(
mTaskBeingDragged, maxDuration);
mPendingAnimation.anim.setInterpolator(Interpolators.ZOOM_IN);
mPendingAnimation = mRecentsView.createTaskLaunchAnimation(
mTaskBeingDragged, maxDuration, Interpolators.ZOOM_IN);
mTempCords[1] = mTaskBeingDragged.getHeight();
dl.getDescendantCoordRelativeToSelf(mTaskBeingDragged, mTempCords);
@@ -203,8 +202,8 @@ public abstract class TaskViewTouchController<T extends BaseDraggingActivity>
if (mCurrentAnimation != null) {
mCurrentAnimation.setOnCancelRunnable(null);
}
mCurrentAnimation = AnimatorPlaybackController
.wrap(mPendingAnimation.anim, maxDuration, this::clearState);
mCurrentAnimation = AnimatorPlaybackController.wrap(
mPendingAnimation.anim, maxDuration, this::clearState);
onUserControlledAnimationCreated(mCurrentAnimation);
mCurrentAnimation.getTarget().addListener(this);
mCurrentAnimation.dispatchOnStart();
@@ -428,6 +428,10 @@ public abstract class BaseSwipeUpHandler<T extends BaseDraggingActivity, Q exten
// rounding at the end of the animation.
float startRadius = mAppWindowAnimationHelper.getCurrentCornerRadius();
float endRadius = startRect.width() / 6f;
float startTransformProgress = mTransformParams.getProgress();
float endTransformProgress = 1;
// 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.
final float windowAlphaThreshold = isFloatingIconView ? 1f - SHAPE_PROGRESS_DURATION : 1f;
@@ -437,8 +441,10 @@ public abstract class BaseSwipeUpHandler<T extends BaseDraggingActivity, Q exten
public void onUpdate(RectF currentRect, float progress) {
homeAnim.setPlayFraction(progress);
mTransformParams.setProgress(progress)
.setCurrentRectAndTargetAlpha(currentRect, getWindowAlpha(progress));
mTransformParams.setProgress(
Utilities.mapRange(progress, startTransformProgress, endTransformProgress))
.setCurrentRect(currentRect)
.setTargetAlpha(getWindowAlpha(progress));
if (isFloatingIconView) {
mTransformParams.setCornerRadius(endRadius * progress + startRadius
* (1f - progress));
@@ -3,8 +3,8 @@ package com.android.quickstep;
import android.app.Activity;
import android.content.Context;
import android.os.Bundle;
import android.util.Log;
import com.android.launcher3.config.FeatureFlags;
import com.android.launcher3.testing.TestInformationHandler;
import com.android.launcher3.testing.TestProtocol;
import com.android.launcher3.uioverrides.touchcontrollers.PortraitStatesTouchController;
@@ -64,6 +64,12 @@ public class QuickstepTestInformationHandler extends TestInformationHandler {
taskBaseIntentComponents);
return response;
}
case TestProtocol.REQUEST_OVERVIEW_ACTIONS_ENABLED: {
response.putBoolean(TestProtocol.TEST_INFO_RESPONSE_FIELD,
FeatureFlags.ENABLE_OVERVIEW_ACTIONS.get());
return response;
}
}
return super.call(method);
@@ -163,7 +163,7 @@ public final class TaskViewUtils {
if (ENABLE_QUICKSTEP_LIVE_TILE.get()) {
List<SurfaceParams> surfaceParamsList = new ArrayList<>();
// Append the surface transform params for the app that's being opened.
Collections.addAll(surfaceParamsList, inOutHelper.getSurfaceParams(params));
Collections.addAll(surfaceParamsList, inOutHelper.computeSurfaceParams(params));
AppWindowAnimationHelper liveTileAnimationHelper =
v.getRecentsView().getClipAnimationHelper();
@@ -173,14 +173,14 @@ public final class TaskViewUtils {
v.getRecentsView().getLiveTileParams(true /* mightNeedToRefill */);
if (liveTileParams != null) {
SurfaceParams[] liveTileSurfaceParams =
liveTileAnimationHelper.getSurfaceParams(liveTileParams);
liveTileAnimationHelper.computeSurfaceParams(liveTileParams);
if (liveTileSurfaceParams != null) {
Collections.addAll(surfaceParamsList, liveTileSurfaceParams);
}
}
}
// Apply surface transform using the surface params list.
AppWindowAnimationHelper.applySurfaceParams(params.syncTransactionApplier,
AppWindowAnimationHelper.applySurfaceParams(params.getSyncTransactionApplier(),
surfaceParamsList.toArray(new SurfaceParams[surfaceParamsList.size()]));
// Get the task bounds for the app that's being opened after surface transform
// update.
@@ -772,7 +772,7 @@ public class TouchInteractionService extends Service implements PluginListener<O
protected boolean shouldNotifyBackGesture() {
return mBackGestureNotificationCounter > 0 &&
mDeviceState.getGestureBlockedActivityPackage() != null;
!mDeviceState.getGestureBlockedActivityPackages().isEmpty();
}
@WorkerThread
@@ -781,8 +781,8 @@ public class TouchInteractionService extends Service implements PluginListener<O
mBackGestureNotificationCounter--;
Utilities.getDevicePrefs(this).edit()
.putInt(KEY_BACK_NOTIFICATION_COUNT, mBackGestureNotificationCounter).apply();
sendBroadcast(new Intent(NOTIFY_ACTION_BACK).setPackage(
mDeviceState.getGestureBlockedActivityPackage()));
mDeviceState.getGestureBlockedActivityPackages().forEach(blockedPackage ->
sendBroadcast(new Intent(NOTIFY_ACTION_BACK).setPackage(blockedPackage)));
}
}
@@ -31,8 +31,8 @@ import android.view.ViewConfiguration;
import com.android.launcher3.R;
import com.android.quickstep.InputConsumer;
import com.android.quickstep.RecentsAnimationDeviceState;
import com.android.quickstep.util.MotionPauseDetector;
import com.android.quickstep.SystemUiProxy;
import com.android.quickstep.util.MotionPauseDetector;
import com.android.systemui.shared.system.InputMonitorCompat;
/**
@@ -117,9 +117,7 @@ public class AccessibilityInputConsumer extends DelegateInputConsumer {
if (pointerIndex == -1) {
break;
}
mMotionPauseDetector.addPosition(ev.getY(pointerIndex) - mDownY,
ev.getEventTime());
mMotionPauseDetector.addPosition(ev, pointerIndex);
}
break;
}
@@ -59,6 +59,7 @@ import com.android.quickstep.TaskAnimationManager;
import com.android.quickstep.util.ActiveGestureLog;
import com.android.quickstep.util.CachedEventDispatcher;
import com.android.quickstep.util.MotionPauseDetector;
import com.android.quickstep.util.NavBarPosition;
import com.android.systemui.shared.system.ActivityManagerWrapper;
import com.android.systemui.shared.system.InputMonitorCompat;
@@ -77,6 +78,7 @@ public class OtherActivityInputConsumer extends ContextWrapper implements InputC
public static final float QUICKSTEP_TOUCH_SLOP_RATIO = 3;
private final RecentsAnimationDeviceState mDeviceState;
private final NavBarPosition mNavBarPosition;
private final TaskAnimationManager mTaskAnimationManager;
private final GestureState mGestureState;
private RecentsAnimationCallbacks mActiveCallbacks;
@@ -126,13 +128,16 @@ public class OtherActivityInputConsumer extends ContextWrapper implements InputC
Factory handlerFactory) {
super(base);
mDeviceState = deviceState;
mNavBarPosition = mDeviceState.getNavBarPosition();
mTaskAnimationManager = taskAnimationManager;
mGestureState = gestureState;
mMainThreadHandler = new Handler(Looper.getMainLooper());
mHandlerFactory = handlerFactory;
mActivityInterface = mGestureState.getActivityInterface();
mMotionPauseDetector = new MotionPauseDetector(base);
mMotionPauseDetector = new MotionPauseDetector(base, false,
mNavBarPosition.isLeftEdge() || mNavBarPosition.isRightEdge()
? MotionEvent.AXIS_X : MotionEvent.AXIS_Y);
mMotionPauseMinDisplacement = base.getResources().getDimension(
R.dimen.motion_pause_detector_min_displacement_from_app);
mOnCompleteCallback = onCompleteCallback;
@@ -172,7 +177,7 @@ public class OtherActivityInputConsumer extends ContextWrapper implements InputC
if (mPassedWindowMoveSlop && mInteractionHandler != null
&& !mRecentsViewDispatcher.hasConsumer()) {
mRecentsViewDispatcher.setConsumer(mInteractionHandler.getRecentsViewDispatcher(
mDeviceState.getNavBarPosition().getRotationMode()));
mNavBarPosition.getRotationMode()));
}
int edgeFlags = ev.getEdgeFlags();
ev.setEdgeFlags(edgeFlags | EDGE_NAV_BAR);
@@ -285,7 +290,7 @@ public class OtherActivityInputConsumer extends ContextWrapper implements InputC
if (mDeviceState.isFullyGesturalNavMode()) {
mMotionPauseDetector.setDisallowPause(upDist < mMotionPauseMinDisplacement
|| isLikelyToStartNewTask);
mMotionPauseDetector.addPosition(displacement, ev.getEventTime());
mMotionPauseDetector.addPosition(ev);
mInteractionHandler.setIsLikelyToStartNewTask(isLikelyToStartNewTask);
}
}
@@ -354,9 +359,9 @@ public class OtherActivityInputConsumer extends ContextWrapper implements InputC
ViewConfiguration.get(this).getScaledMaximumFlingVelocity());
float velocityX = mVelocityTracker.getXVelocity(mActivePointerId);
float velocityY = mVelocityTracker.getYVelocity(mActivePointerId);
float velocity = mDeviceState.getNavBarPosition().isRightEdge()
float velocity = mNavBarPosition.isRightEdge()
? velocityX
: mDeviceState.getNavBarPosition().isLeftEdge()
: mNavBarPosition.isLeftEdge()
? -velocityX
: velocityY;
@@ -410,9 +415,9 @@ public class OtherActivityInputConsumer extends ContextWrapper implements InputC
}
private float getDisplacement(MotionEvent ev) {
if (mDeviceState.getNavBarPosition().isRightEdge()) {
if (mNavBarPosition.isRightEdge()) {
return ev.getX() - mDownPos.x;
} else if (mDeviceState.getNavBarPosition().isLeftEdge()) {
} else if (mNavBarPosition.isLeftEdge()) {
return mDownPos.x - ev.getX();
} else {
return ev.getY() - mDownPos.y;
@@ -23,8 +23,8 @@ import com.android.launcher3.BaseDraggingActivity;
import com.android.launcher3.R;
import com.android.quickstep.GestureState;
import com.android.quickstep.InputConsumer;
import com.android.quickstep.util.MotionPauseDetector;
import com.android.quickstep.SystemUiProxy;
import com.android.quickstep.util.MotionPauseDetector;
/**
* An input consumer that detects swipe up and hold to exit screen pinning mode.
@@ -72,7 +72,7 @@ public class ScreenPinnedInputConsumer implements InputConsumer {
case MotionEvent.ACTION_MOVE:
float displacement = mTouchDownY - y;
mMotionPauseDetector.setDisallowPause(displacement < mMotionPauseMinDisplacement);
mMotionPauseDetector.addPosition(y, ev.getEventTime());
mMotionPauseDetector.addPosition(ev);
break;
case MotionEvent.ACTION_CANCEL:
case MotionEvent.ACTION_UP:
@@ -60,23 +60,23 @@ public class AppWindowAnimationHelper {
private final Rect mSourceStackBounds = new Rect();
// The insets of the source app
private final Rect mSourceInsets = new Rect();
// The source app bounds with the source insets applied, in the source app window coordinates
// The source app bounds with the source insets applied, in the device coordinates
private final RectF mSourceRect = new RectF();
// The bounds of the task view in launcher window coordinates
// The bounds of the task view in device coordinates
private final RectF mTargetRect = new RectF();
// The bounds of the app window (between mSourceRect and mTargetRect) in device coordinates
private final RectF mCurrentRect = new RectF();
// The insets to be used for clipping the app window, which can be larger than mSourceInsets
// if the aspect ratio of the target is smaller than the aspect ratio of the source rect. In
// app window coordinates.
private final RectF mSourceWindowClipInsets = new RectF();
// The insets to be used for clipping the app window. For live tile, we don't transform the clip
// relative to the target rect.
private final RectF mSourceWindowClipInsetsForLiveTile = new RectF();
// The clip rect in source app window coordinates. The app window surface will only be drawn
// within these bounds. This clip rect starts at the full mSourceStackBounds, and insets by
// mSourceWindowClipInsets as the transform progress goes to 1.
private final RectF mCurrentClipRectF = new RectF();
// The bounds of launcher (not including insets) in device coordinates
public final Rect mHomeStackBounds = new Rect();
// The clip rect in source app window coordinates
private final RectF mClipRectF = new RectF();
private final RectFEvaluator mRectFEvaluator = new RectFEvaluator();
private final Matrix mTmpMatrix = new Matrix();
private final Rect mTmpRect = new Rect();
@@ -146,7 +146,6 @@ public class AppWindowAnimationHelper {
Math.max(scaledTargetRect.top, 0),
Math.max(mSourceStackBounds.width() - scaledTargetRect.right, 0),
Math.max(mSourceStackBounds.height() - scaledTargetRect.bottom, 0));
mSourceWindowClipInsetsForLiveTile.set(mSourceWindowClipInsets);
mSourceRect.set(scaledTargetRect);
}
@@ -156,25 +155,29 @@ public class AppWindowAnimationHelper {
}
public RectF applyTransform(TransformParams params) {
SurfaceParams[] surfaceParams = getSurfaceParams(params);
SurfaceParams[] surfaceParams = computeSurfaceParams(params);
if (surfaceParams == null) {
return null;
}
applySurfaceParams(params.syncTransactionApplier, surfaceParams);
return params.currentRect;
applySurfaceParams(params.mSyncTransactionApplier, surfaceParams);
return mCurrentRect;
}
public SurfaceParams[] getSurfaceParams(TransformParams params) {
if (params.targetSet == null) {
/**
* Updates this AppWindowAnimationHelper's state based on the given TransformParams, and returns
* the SurfaceParams to apply via {@link SyncRtSurfaceTransactionApplierCompat#applyParams}.
*/
public SurfaceParams[] computeSurfaceParams(TransformParams params) {
if (params.mTargetSet == null) {
return null;
}
float progress = Utilities.boundToRange(params.progress, 0, 1);
float progress = Utilities.boundToRange(params.mProgress, 0, 1);
updateCurrentRect(params);
SurfaceParams[] surfaceParams = new SurfaceParams[params.targetSet.unfilteredApps.length];
for (int i = 0; i < params.targetSet.unfilteredApps.length; i++) {
RemoteAnimationTargetCompat app = params.targetSet.unfilteredApps[i];
SurfaceParams[] surfaceParams = new SurfaceParams[params.mTargetSet.unfilteredApps.length];
for (int i = 0; i < params.mTargetSet.unfilteredApps.length; i++) {
RemoteAnimationTargetCompat app = params.mTargetSet.unfilteredApps[i];
mTmpMatrix.setTranslate(app.position.x, app.position.y);
Rect crop = mTmpRect;
crop.set(app.sourceContainerBounds);
@@ -182,17 +185,17 @@ public class AppWindowAnimationHelper {
float alpha;
int layer = RemoteAnimationProvider.getLayer(app, mBoostModeTargetLayers);
float cornerRadius = 0f;
float scale = Math.max(params.currentRect.width(), mTargetRect.width()) / crop.width();
if (app.mode == params.targetSet.targetMode) {
alpha = mTaskAlphaCallback.getAlpha(app, params.targetAlpha);
float scale = Math.max(mCurrentRect.width(), mTargetRect.width()) / crop.width();
if (app.mode == params.mTargetSet.targetMode) {
alpha = mTaskAlphaCallback.getAlpha(app, params.mTargetAlpha);
if (app.activityType != RemoteAnimationTargetCompat.ACTIVITY_TYPE_HOME) {
mTmpMatrix.setRectToRect(mSourceRect, params.currentRect, ScaleToFit.FILL);
mTmpMatrix.setRectToRect(mSourceRect, mCurrentRect, ScaleToFit.FILL);
mTmpMatrix.postTranslate(app.position.x, app.position.y);
mClipRectF.roundOut(crop);
mCurrentClipRectF.roundOut(crop);
if (mSupportsRoundedCornersOnWindows) {
if (params.cornerRadius > -1) {
cornerRadius = params.cornerRadius;
scale = params.currentRect.width() / crop.width();
if (params.mCornerRadius > -1) {
cornerRadius = params.mCornerRadius;
scale = mCurrentRect.width() / crop.width();
} else {
float windowCornerRadius = mUseRoundedCornersOnWindows
? mWindowCornerRadius : 0;
@@ -206,14 +209,14 @@ public class AppWindowAnimationHelper {
&& app.isNotInRecents) {
alpha = 1 - Interpolators.DEACCEL_2_5.getInterpolation(progress);
}
} else if (params.targetSet.hasRecents) {
} else if (params.mTargetSet.hasRecents) {
// If home has a different target then recents, reverse anim the
// home target.
alpha = 1 - (progress * params.targetAlpha);
alpha = 1 - (progress * params.mTargetAlpha);
}
} else {
alpha = mBaseAlphaCallback.getAlpha(app, progress);
if (ENABLE_QUICKSTEP_LIVE_TILE.get() && params.launcherOnTop) {
if (ENABLE_QUICKSTEP_LIVE_TILE.get() && params.mLauncherOnTop) {
crop = null;
layer = Integer.MAX_VALUE;
}
@@ -228,31 +231,33 @@ public class AppWindowAnimationHelper {
}
public RectF updateCurrentRect(TransformParams params) {
float progress = params.progress;
if (params.currentRect == null) {
RectF currentRect;
if (params.mCurrentRect != null) {
mCurrentRect.set(params.mCurrentRect);
} else {
mTmpRectF.set(mTargetRect);
Utilities.scaleRectFAboutCenter(mTmpRectF, params.offsetScale);
currentRect = mRectFEvaluator.evaluate(progress, mSourceRect, mTmpRectF);
currentRect.offset(params.offsetX, 0);
// Don't clip past progress > 1.
progress = Math.min(1, progress);
final RectF sourceWindowClipInsets = params.forLiveTile
? mSourceWindowClipInsetsForLiveTile : mSourceWindowClipInsets;
mClipRectF.left = sourceWindowClipInsets.left * progress;
mClipRectF.top = sourceWindowClipInsets.top * progress;
mClipRectF.right =
mSourceStackBounds.width() - (sourceWindowClipInsets.right * progress);
mClipRectF.bottom =
mSourceStackBounds.height() - (sourceWindowClipInsets.bottom * progress);
params.setCurrentRectAndTargetAlpha(currentRect, 1);
Utilities.scaleRectFAboutCenter(mTmpRectF, params.mOffsetScale);
mCurrentRect.set(mRectFEvaluator.evaluate(params.mProgress, mSourceRect, mTmpRectF));
mCurrentRect.offset(params.mOffsetX, 0);
}
return params.currentRect;
updateClipRect(params);
return mCurrentRect;
}
private void updateClipRect(TransformParams params) {
// Don't clip past progress > 1.
float progress = Math.min(1, params.mProgress);
mCurrentClipRectF.left = mSourceWindowClipInsets.left * progress;
mCurrentClipRectF.top = mSourceWindowClipInsets.top * progress;
mCurrentClipRectF.right =
mSourceStackBounds.width() - (mSourceWindowClipInsets.right * progress);
mCurrentClipRectF.bottom =
mSourceStackBounds.height() - (mSourceWindowClipInsets.bottom * progress);
}
public RectF getCurrentRectWithInsets() {
mTmpMatrix.mapRect(mCurrentRectWithInsets, mClipRectF);
mTmpMatrix.mapRect(mCurrentRectWithInsets, mCurrentClipRectF);
return mCurrentRectWithInsets;
}
@@ -384,75 +389,152 @@ public class AppWindowAnimationHelper {
}
public static class TransformParams {
float progress;
public float offsetX;
public float offsetScale;
public @Nullable RectF currentRect;
float targetAlpha;
boolean forLiveTile;
float cornerRadius;
boolean launcherOnTop;
public RemoteAnimationTargets targetSet;
public SyncRtSurfaceTransactionApplierCompat syncTransactionApplier;
private float mProgress;
private float mOffsetX;
private float mOffsetScale;
private @Nullable RectF mCurrentRect;
private float mTargetAlpha;
private float mCornerRadius;
private boolean mLauncherOnTop;
private RemoteAnimationTargets mTargetSet;
private SyncRtSurfaceTransactionApplierCompat mSyncTransactionApplier;
public TransformParams() {
progress = 0;
offsetX = 0;
offsetScale = 1;
currentRect = null;
targetAlpha = 0;
forLiveTile = false;
cornerRadius = -1;
launcherOnTop = false;
mProgress = 0;
mOffsetX = 0;
mOffsetScale = 1;
mCurrentRect = null;
mTargetAlpha = 1;
mCornerRadius = -1;
mLauncherOnTop = false;
}
/**
* Sets the progress of the transformation, where 0 is the source and 1 is the target. We
* automatically adjust properties such as currentRect and cornerRadius based on this
* progress, unless they are manually overridden by setting them on this TransformParams.
*/
public TransformParams setProgress(float progress) {
this.progress = progress;
this.currentRect = null;
mProgress = progress;
return this;
}
/**
* Sets the corner radius of the transformed window, in pixels. If unspecified (-1), we
* simply interpolate between the window's corner radius to the task view's corner radius,
* based on {@link #mProgress}.
*/
public TransformParams setCornerRadius(float cornerRadius) {
this.cornerRadius = cornerRadius;
mCornerRadius = cornerRadius;
return this;
}
public TransformParams setCurrentRectAndTargetAlpha(RectF currentRect, float targetAlpha) {
this.currentRect = currentRect;
this.targetAlpha = targetAlpha;
/**
* Sets the current rect to show the transformed window, in device coordinates. This gives
* the caller manual control of where to show the window. If unspecified (null), we
* interpolate between {@link AppWindowAnimationHelper#mSourceRect} and
* {@link AppWindowAnimationHelper#mTargetRect}, based on {@link #mProgress}.
*/
public TransformParams setCurrentRect(RectF currentRect) {
mCurrentRect = currentRect;
return this;
}
/**
* Specifies the alpha of the transformed window. Default is 1.
*/
public TransformParams setTargetAlpha(float targetAlpha) {
mTargetAlpha = targetAlpha;
return this;
}
/**
* If {@link #mCurrentRect} is null (i.e. {@link #setCurrentRect(RectF)} hasn't overridden
* the default), then offset the current rect by this amount after computing the rect based
* on {@link #mProgress}.
*/
public TransformParams setOffsetX(float offsetX) {
this.offsetX = offsetX;
mOffsetX = offsetX;
return this;
}
/**
* If {@link #mCurrentRect} is null (i.e. {@link #setCurrentRect(RectF)} hasn't overridden
* the default), then scale the current rect by this amount after computing the rect based
* on {@link #mProgress}.
*/
public TransformParams setOffsetScale(float offsetScale) {
this.offsetScale = offsetScale;
return this;
}
public TransformParams setForLiveTile(boolean forLiveTile) {
this.forLiveTile = forLiveTile;
mOffsetScale = offsetScale;
return this;
}
/**
* If true, sets the crop = null and layer = Integer.MAX_VALUE for targets that don't match
* {@link #mTargetSet}.targetMode. (Currently only does this when live tiles are enabled.)
*/
public TransformParams setLauncherOnTop(boolean launcherOnTop) {
this.launcherOnTop = launcherOnTop;
mLauncherOnTop = launcherOnTop;
return this;
}
/**
* Specifies the set of RemoteAnimationTargetCompats that are included in the transformation
* that these TransformParams help compute. These TransformParams generally only apply to
* the targetSet.apps which match the targetSet.targetMode (e.g. the MODE_CLOSING app when
* swiping to home).
*/
public TransformParams setTargetSet(RemoteAnimationTargets targetSet) {
this.targetSet = targetSet;
mTargetSet = targetSet;
return this;
}
/**
* Sets the SyncRtSurfaceTransactionApplierCompat that will apply the SurfaceParams that
* are computed based on these TransformParams.
*/
public TransformParams setSyncTransactionApplier(
SyncRtSurfaceTransactionApplierCompat applier) {
this.syncTransactionApplier = applier;
mSyncTransactionApplier = applier;
return this;
}
// Pubic getters so outside packages can read the values.
public float getProgress() {
return mProgress;
}
public float getOffsetX() {
return mOffsetX;
}
public float getOffsetScale() {
return mOffsetScale;
}
@Nullable
public RectF getCurrentRect() {
return mCurrentRect;
}
public float getTargetAlpha() {
return mTargetAlpha;
}
public float getCornerRadius() {
return mCornerRadius;
}
public boolean isLauncherOnTop() {
return mLauncherOnTop;
}
public RemoteAnimationTargets getTargetSet() {
return mTargetSet;
}
public SyncRtSurfaceTransactionApplierCompat getSyncTransactionApplier() {
return mSyncTransactionApplier;
}
}
}
@@ -165,7 +165,7 @@ public class RectFSpringAnim {
mRectYAnim = new FlingSpringAnim(this, RECT_Y, startY, endY, startVelocityY,
mMinVisChange, minYValue, maxYValue, springVelocityFactor, onYEndListener);
float minVisibleChange = 1f / mStartRect.height();
float minVisibleChange = Math.abs(1f / mStartRect.height());
mRectScaleAnim = new SpringAnimation(this, RECT_SCALE_PROGRESS)
.setSpring(new SpringForce(1f)
.setDampingRatio(SpringForce.DAMPING_RATIO_LOW_BOUNCY)
@@ -200,8 +200,8 @@ public class LauncherRecentsView extends RecentsView<Launcher> implements StateL
if (ENABLE_QUICKSTEP_LIVE_TILE.get()) {
if (tv.isRunningTask()) {
mTransformParams.setProgress(1 - progress)
.setSyncTransactionApplier(mSyncTransactionApplier)
.setForLiveTile(true);
.setCurrentRect(null)
.setSyncTransactionApplier(mSyncTransactionApplier);
mAppWindowAnimationHelper.applyTransform(mTransformParams);
} else {
redrawLiveTile(true);
@@ -267,7 +267,8 @@ public class LauncherRecentsView extends RecentsView<Launcher> implements StateL
}
mTempRectF.set(mTempRect);
mTransformParams.setProgress(1f)
.setCurrentRectAndTargetAlpha(mTempRectF, taskView.getAlpha())
.setCurrentRect(mTempRectF)
.setTargetAlpha(taskView.getAlpha())
.setSyncTransactionApplier(mSyncTransactionApplier)
.setTargetSet(mRecentsAnimationTargets)
.setLauncherOnTop(true);
@@ -76,6 +76,7 @@ import android.view.ViewDebug;
import android.view.ViewGroup;
import android.view.accessibility.AccessibilityEvent;
import android.view.accessibility.AccessibilityNodeInfo;
import android.view.animation.Interpolator;
import android.widget.ListView;
import androidx.annotation.Nullable;
@@ -92,6 +93,7 @@ import com.android.launcher3.Utilities;
import com.android.launcher3.anim.AnimatorPlaybackController;
import com.android.launcher3.anim.PropertyListBuilder;
import com.android.launcher3.anim.SpringObjectAnimator;
import com.android.launcher3.compat.AccessibilityManagerCompat;
import com.android.launcher3.config.FeatureFlags;
import com.android.launcher3.graphics.RotationMode;
import com.android.launcher3.userevent.nano.LauncherLogProto;
@@ -980,6 +982,10 @@ public abstract class RecentsView<T extends BaseActivity> extends PagedView impl
TaskView runningTask = getRunningTaskView();
if (runningTask != null) {
runningTask.setStableAlpha(isHidden ? 0 : mContentAlpha);
if (!isHidden) {
AccessibilityManagerCompat.sendCustomAccessibilityEvent(runningTask,
AccessibilityEvent.TYPE_VIEW_FOCUSED, null);
}
}
}
@@ -1602,7 +1608,8 @@ public abstract class RecentsView<T extends BaseActivity> extends PagedView impl
return anim;
}
public PendingAnimation createTaskLauncherAnimation(TaskView tv, long duration) {
public PendingAnimation createTaskLaunchAnimation(
TaskView tv, long duration, Interpolator interpolator) {
if (FeatureFlags.IS_STUDIO_BUILD && mPendingAnimation != null) {
throw new IllegalStateException("Another pending animation is still running");
}
@@ -1615,7 +1622,6 @@ public abstract class RecentsView<T extends BaseActivity> extends PagedView impl
int targetSysUiFlags = tv.getThumbnail().getSysUiStatusNavFlags();
final boolean[] passedOverviewThreshold = new boolean[] {false};
ValueAnimator progressAnim = ValueAnimator.ofFloat(0, 1);
progressAnim.setInterpolator(LINEAR);
progressAnim.addUpdateListener(animator -> {
// Once we pass a certain threshold, update the sysui flags to match the target
// tasks' flags
@@ -1641,7 +1647,7 @@ public abstract class RecentsView<T extends BaseActivity> extends PagedView impl
appWindowAnimationHelper.prepareAnimation(mActivity.getDeviceProfile(), true /* isOpening */);
AnimatorSet anim = createAdjacentPageAnimForTaskLaunch(tv, appWindowAnimationHelper);
anim.play(progressAnim);
anim.setDuration(duration);
anim.setDuration(duration).setInterpolator(interpolator);
Consumer<Boolean> onTaskLaunchFinish = this::onTaskLaunched;
@@ -21,6 +21,7 @@ import static android.widget.Toast.LENGTH_SHORT;
import static com.android.launcher3.QuickstepAppTransitionManagerImpl.RECENTS_LAUNCH_DURATION;
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.quickstep.SysUINavigationMode.removeShelfFromOverview;
@@ -280,11 +281,10 @@ public class TaskView extends FrameLayout implements PageCallbacks, Reusable {
}
public AnimatorPlaybackController createLaunchAnimationForRunningTask() {
final PendingAnimation pendingAnimation =
getRecentsView().createTaskLauncherAnimation(this, RECENTS_LAUNCH_DURATION);
pendingAnimation.anim.setInterpolator(Interpolators.TOUCH_RESPONSE_INTERPOLATOR);
AnimatorPlaybackController currentAnimation = AnimatorPlaybackController
.wrap(pendingAnimation.anim, RECENTS_LAUNCH_DURATION, null);
final PendingAnimation pendingAnimation = getRecentsView().createTaskLaunchAnimation(
this, RECENTS_LAUNCH_DURATION, TOUCH_RESPONSE_INTERPOLATOR);
AnimatorPlaybackController currentAnimation = AnimatorPlaybackController.wrap(
pendingAnimation.anim, RECENTS_LAUNCH_DURATION);
currentAnimation.setEndAction(() -> {
pendingAnimation.finish(true, Touch.SWIPE);
launchTask(false);
+7 -5
View File
@@ -13,11 +13,13 @@
See the License for the specific language governing permissions and
limitations under the License.
-->
<resources>
<string name="task_overlay_factory_class" translatable="false"></string>
<resources xmlns:tools="http://schemas.android.com/tools">
<string name="task_overlay_factory_class" translatable="false"/>
<!-- Activity which blocks home gesture -->
<string name="gesture_blocking_activity" translatable="false"></string>
<!-- Activities which block home gesture -->
<string-array name="gesture_blocking_activities" tools:ignore="InconsistentArrays">
<item>com.android.launcher3/com.android.quickstep.interaction.GestureSandboxActivity</item>
</string-array>
<string name="stats_log_manager_class" translatable="false">com.android.quickstep.logging.StatsLogCompatManager</string>
@@ -32,5 +34,5 @@
<integer name="assistant_gesture_min_time_threshold">200</integer>
<integer name="assistant_gesture_corner_deg_threshold">20</integer>
<string name="wellbeing_provider_pkg" translatable="false"></string>
<string name="wellbeing_provider_pkg" translatable="false"/>
</resources>
@@ -201,7 +201,7 @@ public abstract class BaseQuickstepLauncher extends Launcher
if (FeatureFlags.ENABLE_OVERVIEW_ACTIONS.get() && removeShelfFromOverview(this)) {
// Overview is above all other launcher elements, including qsb, so move it to the top.
getOverviewPanel().bringToFront();
getOverviewPanelContainer().bringToFront();
}
}
@@ -53,6 +53,12 @@ public class AllAppsState extends LauncherState {
dispatchWindowStateChanged(launcher);
}
@Override
public void onStateDisabled(Launcher launcher) {
super.onStateDisabled(launcher);
AbstractFloatingView.closeAllOpenViews(launcher);
}
@Override
public String getDescription(Launcher launcher) {
AllAppsContainerView appsView = launcher.getAppsView();
@@ -228,15 +228,13 @@ public class PortraitStatesTouchController extends AbstractStateChangeTouchContr
// Reset the state manager, when changing the interaction mode
mLauncher.getStateManager().goToState(OVERVIEW, false /* animate */);
mPendingAnimation = mOverviewPortraitStateTouchHelper
.createSwipeDownToTaskAppAnimation(maxAccuracy);
mPendingAnimation.anim.setInterpolator(Interpolators.LINEAR);
.createSwipeDownToTaskAppAnimation(maxAccuracy, Interpolators.LINEAR);
Runnable onCancelRunnable = () -> {
cancelPendingAnim();
clearState();
};
mCurrentAnimation = AnimatorPlaybackController.wrap(mPendingAnimation.anim, maxAccuracy,
onCancelRunnable);
mCurrentAnimation = AnimatorPlaybackController.wrap(
mPendingAnimation.anim, maxAccuracy, onCancelRunnable);
mLauncher.getStateManager().setCurrentUserControlledAnimation(mCurrentAnimation);
totalShift = LayoutUtils.getShelfTrackingDistance(mLauncher,
mLauncher.getDeviceProfile());
@@ -66,6 +66,8 @@ import com.android.systemui.shared.system.SystemGestureExclusionListenerCompat;
import java.io.PrintWriter;
import java.util.ArrayList;
import java.util.List;
import java.util.stream.Collectors;
/**
* Manages the state of the system during a swipe up gesture.
@@ -107,7 +109,7 @@ public class RecentsAnimationDeviceState implements
private Region mExclusionRegion;
private SystemGestureExclusionListenerCompat mExclusionListener;
private ComponentName mGestureBlockedActivity;
private final List<ComponentName> mGestureBlockedActivities;
public RecentsAnimationDeviceState(Context context) {
final ContentResolver resolver = context.getContentResolver();
@@ -142,9 +144,19 @@ public class RecentsAnimationDeviceState implements
runOnDestroy(() -> mSysUiNavMode.removeModeChangeListener(this));
// Add any blocked activities
String blockingActivity = context.getString(R.string.gesture_blocking_activity);
if (!TextUtils.isEmpty(blockingActivity)) {
mGestureBlockedActivity = ComponentName.unflattenFromString(blockingActivity);
String[] blockingActivities;
try {
blockingActivities =
context.getResources().getStringArray(R.array.gesture_blocking_activities);
} catch (Resources.NotFoundException e) {
blockingActivities = new String[0];
}
mGestureBlockedActivities = new ArrayList<>(blockingActivities.length);
for (String blockingActivity : blockingActivities) {
if (!TextUtils.isEmpty(blockingActivity)) {
mGestureBlockedActivities.add(
ComponentName.unflattenFromString(blockingActivity));
}
}
}
@@ -272,17 +284,16 @@ public class RecentsAnimationDeviceState implements
* @return whether the given running task info matches the gesture-blocked activity.
*/
public boolean isGestureBlockedActivity(ActivityManager.RunningTaskInfo runningTaskInfo) {
return runningTaskInfo != null && mGestureBlockedActivity != null
&& mGestureBlockedActivity.equals(runningTaskInfo.topActivity);
return runningTaskInfo != null
&& mGestureBlockedActivities.contains(runningTaskInfo.topActivity);
}
/**
* @return the package of the gesture-blocked activity or {@code null} if there is none.
* @return the packages of gesture-blocked activities.
*/
public String getGestureBlockedActivityPackage() {
return (mGestureBlockedActivity != null)
? mGestureBlockedActivity.getPackageName()
: null;
public List<String> getGestureBlockedActivityPackages() {
return mGestureBlockedActivities.stream().map(ComponentName::getPackageName)
.collect(Collectors.toList());
}
/**
@@ -32,8 +32,8 @@ import com.android.quickstep.interaction.BackGestureTutorialFragment.TutorialTyp
import java.util.List;
import java.util.Optional;
/** Shows the Back gesture interactive tutorial in full screen mode. */
public class BackGestureTutorialActivity extends FragmentActivity {
/** Shows the gesture interactive sandbox in full screen mode. */
public class GestureSandboxActivity extends FragmentActivity {
Optional<BackGestureTutorialFragment> mFragment = Optional.empty();
@@ -15,6 +15,8 @@
*/
package com.android.quickstep.util;
import static com.android.launcher3.config.FeatureFlags.ENABLE_LSQ_VELOCITY_PROVIDER;
import android.content.Context;
import android.content.res.Resources;
import android.view.MotionEvent;
@@ -37,8 +39,7 @@ public class MotionPauseDetector {
private static final long FORCE_PAUSE_TIMEOUT = 300;
/**
* After {@link #makePauseHarderToTrigger()}, must
* move slowly for this long to trigger a pause.
* After {@link #mMakePauseHarderToTrigger}, must move slowly for this long to trigger a pause.
*/
private static final long HARDER_TRIGGER_TIMEOUT = 400;
@@ -49,13 +50,10 @@ public class MotionPauseDetector {
private final Alarm mForcePauseTimeout;
private final boolean mMakePauseHarderToTrigger;
private final Context mContext;
private final VelocityProvider mVelocityProvider;
private Long mPreviousTime = null;
private Float mPreviousPosition = null;
private Float mPreviousVelocity = null;
private Float mFirstPosition = null;
private OnMotionPauseListener mOnMotionPauseListener;
private boolean mIsPaused;
// Bias more for the first pause to make it feel extra responsive.
@@ -73,6 +71,13 @@ public class MotionPauseDetector {
* @param makePauseHarderToTrigger Used for gestures that require a more explicit pause.
*/
public MotionPauseDetector(Context context, boolean makePauseHarderToTrigger) {
this(context, makePauseHarderToTrigger, MotionEvent.AXIS_Y);
}
/**
* @param makePauseHarderToTrigger Used for gestures that require a more explicit pause.
*/
public MotionPauseDetector(Context context, boolean makePauseHarderToTrigger, int axis) {
mContext = context;
Resources res = context.getResources();
mSpeedVerySlow = res.getDimension(R.dimen.motion_pause_detector_speed_very_slow);
@@ -82,6 +87,8 @@ public class MotionPauseDetector {
mForcePauseTimeout = new Alarm();
mForcePauseTimeout.setOnAlarmListener(alarm -> updatePaused(true /* isPaused */));
mMakePauseHarderToTrigger = makePauseHarderToTrigger;
mVelocityProvider = ENABLE_LSQ_VELOCITY_PROVIDER.get()
? new LSqVelocityProvider(axis) : new LinearVelocityProvider(axis);
}
/**
@@ -101,28 +108,26 @@ public class MotionPauseDetector {
/**
* Computes velocity and acceleration to determine whether the motion is paused.
* @param position The x or y component of the motion being tracked.
*
* TODO: Use historical positions as well, e.g. {@link MotionEvent#getHistoricalY(int, int)}.
* @param ev The motion being tracked.
*/
public void addPosition(float position, long time) {
if (mFirstPosition == null) {
mFirstPosition = position;
}
public void addPosition(MotionEvent ev) {
addPosition(ev, 0);
}
/**
* Computes velocity and acceleration to determine whether the motion is paused.
* @param ev The motion being tracked.
* @param pointerIndex Index for the pointer being tracked in the motion event
*/
public void addPosition(MotionEvent ev, int pointerIndex) {
mForcePauseTimeout.setAlarm(mMakePauseHarderToTrigger
? HARDER_TRIGGER_TIMEOUT
: FORCE_PAUSE_TIMEOUT);
if (mPreviousTime != null && mPreviousPosition != null) {
long changeInTime = Math.max(1, time - mPreviousTime);
float changeInPosition = position - mPreviousPosition;
float velocity = changeInPosition / changeInTime;
if (mPreviousVelocity != null) {
checkMotionPaused(velocity, mPreviousVelocity, time);
}
mPreviousVelocity = velocity;
Float newVelocity = mVelocityProvider.addMotionEvent(ev, pointerIndex);
if (newVelocity != null && mPreviousVelocity != null) {
checkMotionPaused(newVelocity, mPreviousVelocity, ev.getEventTime());
}
mPreviousTime = time;
mPreviousPosition = position;
mPreviousVelocity = newVelocity;
}
private void checkMotionPaused(float velocity, float prevVelocity, long time) {
@@ -178,10 +183,8 @@ public class MotionPauseDetector {
}
public void clear() {
mPreviousTime = null;
mPreviousPosition = null;
mVelocityProvider.clear();
mPreviousVelocity = null;
mFirstPosition = null;
setOnMotionPauseListener(null);
mIsPaused = mHasEverBeenPaused = false;
mSlowStartTime = 0;
@@ -195,4 +198,188 @@ public class MotionPauseDetector {
public interface OnMotionPauseListener {
void onMotionPauseChanged(boolean isPaused);
}
/**
* Interface to abstract out velocity calculations
*/
protected interface VelocityProvider {
/**
* Adds a new motion events, and returns the velocity at this point, or null if
* the velocity is not available
*/
Float addMotionEvent(MotionEvent ev, int pointer);
/**
* Clears all stored motion event records
*/
void clear();
}
private static class LinearVelocityProvider implements VelocityProvider {
private Long mPreviousTime = null;
private Float mPreviousPosition = null;
private final int mAxis;
LinearVelocityProvider(int axis) {
mAxis = axis;
}
@Override
public Float addMotionEvent(MotionEvent ev, int pointer) {
long time = ev.getEventTime();
float position = ev.getAxisValue(mAxis, pointer);
Float velocity = null;
if (mPreviousTime != null && mPreviousPosition != null) {
long changeInTime = Math.max(1, time - mPreviousTime);
float changeInPosition = position - mPreviousPosition;
velocity = changeInPosition / changeInTime;
}
mPreviousTime = time;
mPreviousPosition = position;
return velocity;
}
@Override
public void clear() {
mPreviousTime = null;
mPreviousPosition = null;
}
}
/**
* Java implementation of {@link android.view.VelocityTracker} using the Least Square (deg 2)
* algorithm.
*/
private static class LSqVelocityProvider implements VelocityProvider {
// Maximum age of a motion event to be considered when calculating the velocity.
private static final long HORIZON_MS = 100;
// Number of samples to keep.
private static final int HISTORY_SIZE = 20;
// Position history are stored in a circular array
private final float[] mHistoricTimes = new float[HISTORY_SIZE];
private final float[] mHistoricPos = new float[HISTORY_SIZE];
private int mHistoryCount = 0;
private int mHistoryStart = 0;
private final int mAxis;
LSqVelocityProvider(int axis) {
mAxis = axis;
}
@Override
public void clear() {
mHistoryCount = mHistoryStart = 0;
}
private void addPositionAndTime(float eventTime, float eventPosition) {
mHistoricTimes[mHistoryStart] = eventTime;
mHistoricPos[mHistoryStart] = eventPosition;
mHistoryStart++;
if (mHistoryStart >= HISTORY_SIZE) {
mHistoryStart = 0;
}
mHistoryCount = Math.min(HISTORY_SIZE, mHistoryCount + 1);
}
@Override
public Float addMotionEvent(MotionEvent ev, int pointer) {
// Add all historic points
int historyCount = ev.getHistorySize();
for (int i = 0; i < historyCount; i++) {
addPositionAndTime(
ev.getHistoricalEventTime(i), ev.getHistoricalAxisValue(mAxis, pointer, i));
}
// Start index for the last position (about to be added)
int eventStartIndex = mHistoryStart;
addPositionAndTime(ev.getEventTime(), ev.getAxisValue(mAxis, pointer));
return solveUnweightedLeastSquaresDeg2(eventStartIndex);
}
/**
* Solves the instantaneous velocity.
* Based on solveUnweightedLeastSquaresDeg2 in VelocityTracker.cpp
*/
private Float solveUnweightedLeastSquaresDeg2(final int pointPos) {
final float eventTime = mHistoricTimes[pointPos];
float sxi = 0, sxiyi = 0, syi = 0, sxi2 = 0, sxi3 = 0, sxi2yi = 0, sxi4 = 0;
int count = 0;
for (int i = 0; i < mHistoryCount; i++) {
int index = pointPos - i;
if (index < 0) {
index += HISTORY_SIZE;
}
float time = mHistoricTimes[index];
float age = eventTime - time;
if (age > HORIZON_MS) {
break;
}
count++;
float xi = -age;
float yi = mHistoricPos[index];
float xi2 = xi * xi;
float xi3 = xi2 * xi;
float xi4 = xi3 * xi;
float xiyi = xi * yi;
float xi2yi = xi2 * yi;
sxi += xi;
sxi2 += xi2;
sxiyi += xiyi;
sxi2yi += xi2yi;
syi += yi;
sxi3 += xi3;
sxi4 += xi4;
}
if (count < 3) {
// Too few samples
if (count == 2) {
int endPos = pointPos - 1;
if (endPos < 0) {
endPos += HISTORY_SIZE;
}
float denominator = eventTime - mHistoricTimes[endPos];
if (denominator != 0) {
return (eventTime - mHistoricPos[endPos]) / denominator;
}
}
return null;
}
float Sxx = sxi2 - sxi * sxi / count;
float Sxy = sxiyi - sxi * syi / count;
float Sxx2 = sxi3 - sxi * sxi2 / count;
float Sx2y = sxi2yi - sxi2 * syi / count;
float Sx2x2 = sxi4 - sxi2 * sxi2 / count;
float denominator = Sxx * Sx2x2 - Sxx2 * Sxx2;
if (denominator == 0) {
// division by 0 when computing velocity
return null;
}
// Compute a
// float numerator = Sx2y*Sxx - Sxy*Sxx2;
// Compute b
float numerator = Sxy * Sx2x2 - Sx2y * Sxx2;
float b = numerator / denominator;
// Compute c
// float c = syi/count - b * sxi/count - a * sxi2/count;
return b;
}
}
}
@@ -35,6 +35,7 @@ import androidx.test.uiautomator.UiDevice;
import com.android.launcher3.tapl.LauncherInstrumentation;
import com.android.launcher3.tapl.TestHelpers;
import com.android.launcher3.ui.AbstractLauncherUiTest;
import com.android.launcher3.util.Wait;
import com.android.launcher3.util.rule.FailureWatcher;
import com.android.systemui.shared.system.QuickStepContract;
@@ -199,7 +200,7 @@ public class NavigationModeSwitchRule implements TestRule {
+ launcher.getNavigationModeMismatchError(),
() -> launcher.getNavigationModeMismatchError() == null,
60000 /* b/148422894 */, launcher);
AbstractLauncherUiTest.checkDetectedLeaks();
return true;
}
@@ -45,7 +45,6 @@ import com.android.launcher3.util.rule.TestStabilityRule;
import com.android.quickstep.NavigationModeSwitchRule.NavigationModeSwitch;
import com.android.quickstep.views.RecentsView;
import org.junit.After;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
@@ -53,21 +52,10 @@ import org.junit.runner.RunWith;
@LargeTest
@RunWith(AndroidJUnit4.class)
public class TaplTestsQuickstep extends AbstractQuickStepTest {
private int mLauncherPid;
@Before
public void setUp() throws Exception {
mLauncherPid = 0;
super.setUp();
TaplTestsLauncher3.initialize(this);
mLauncherPid = mLauncher.getPid();
}
@After
public void teardown() {
if (mLauncherPid != 0) {
assertEquals("Launcher crashed, pid mismatch:", mLauncherPid, mLauncher.getPid());
}
}
private void startTestApps() throws Exception {
@@ -93,6 +81,10 @@ public class TaplTestsQuickstep extends AbstractQuickStepTest {
@Test
public void testAllAppsFromOverview() throws Exception {
if (!mLauncher.hasAllAppsInOverview()) {
return;
}
// Test opening all apps from Overview.
assertNotNull("switchToAllApps() returned null",
mLauncher.getWorkspace().switchToOverview().switchToAllApps());
@@ -153,8 +145,8 @@ public class TaplTestsQuickstep extends AbstractQuickStepTest {
launcher -> assertEquals("Dismissing a task didn't remove 1 task from Overview",
numTasks - 1, getTaskCount(launcher)));
if (!TestHelpers.isInLauncherProcess() ||
getFromLauncher(launcher -> !launcher.getDeviceProfile().isLandscape)) {
if (mLauncher.hasAllAppsInOverview() && (!TestHelpers.isInLauncherProcess()
|| getFromLauncher(launcher -> !launcher.getDeviceProfile().isLandscape))) {
// Test switching to all apps and back.
final AllAppsFromOverview allApps = overview.switchToAllApps();
assertNotNull("overview.switchToAllApps() returned null (1)", allApps);
@@ -197,6 +189,10 @@ public class TaplTestsQuickstep extends AbstractQuickStepTest {
@Test
public void testAppIconLaunchFromAllAppsFromOverview() throws Exception {
if (!mLauncher.hasAllAppsInOverview()) {
return;
}
final AllApps allApps =
mLauncher.getWorkspace().switchToOverview().switchToAllApps();
assertTrue("Launcher internal state is not All Apps",
+2 -3
View File
@@ -44,9 +44,8 @@
layout="@layout/hotseat" />
<include
android:id="@+id/overview_panel"
layout="@layout/overview_panel"
android:visibility="gone" />
android:id="@+id/overview_panel_container"
layout="@layout/overview_panel"/>
<!-- Keep these behind the workspace so that they are not visible when
we go into AllApps -->
+4 -2
View File
@@ -14,7 +14,9 @@
See the License for the specific language governing permissions and
limitations under the License.
-->
<Space
<FrameLayout
xmlns:android="http://schemas.android.com/apk/res/android"
android:id="@+id/overview_panel_recents"
android:layout_width="0dp"
android:layout_height="0dp" />
android:layout_height="0dp"
android:visibility="gone" />
+2 -2
View File
@@ -15,9 +15,9 @@
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="?attr/allAppsScrimColor"
android:padding="8dp"
android:orientation="vertical"
android:background="?attr/allAppsScrimColor"
android:gravity="center">
<ImageView
@@ -38,7 +38,7 @@
android:layout_marginBottom="8dp"
android:text="@string/work_apps_paused_title"
android:textAlignment="center"
android:textSize="24sp" />
android:textSize="20sp" />
<TextView
android:layout_width="wrap_content"
+4 -5
View File
@@ -21,22 +21,22 @@
<View
android:layout_width="match_parent"
android:backgroundTint="@color/bottom_panel_background"
android:layout_height="32dp"
android:background="@drawable/bottom_sheet_top_border" />
android:background="@drawable/bottom_sheet_top_border"
android:backgroundTint="?android:attr/colorAccent" />
<LinearLayout
android:id="@+id/view_wrapper"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:background="@color/bottom_panel_background"
android:background="?android:attr/colorAccent"
android:orientation="vertical"
android:paddingLeft="@dimen/bottom_sheet_edu_padding"
android:paddingRight="@dimen/bottom_sheet_edu_padding">
<TextView
style="@style/TextHeadline"
android:id="@+id/content_text"
style="@style/TextHeadline"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="48dp"
@@ -57,5 +57,4 @@
android:textAlignment="center"
android:textColor="@android:color/white" />
</LinearLayout>
</com.android.launcher3.views.WorkEduView>
-1
View File
@@ -45,5 +45,4 @@
<color name="back_gesture_tutorial_primary_color">#1A73E8</color> <!-- Blue -->
<color name="bottom_panel_background">#f01A73E8</color>
</resources>
+1 -1
View File
@@ -330,7 +330,7 @@
<!--- User onboarding title for personal apps -->
<string name="work_profile_edu_personal_apps">Personal apps are private &amp; can\'t be seen by IT</string>
<!--- User onboarding title for work profile apps -->
<string name="work_profile_edu_work_apps">Work apps are badged and monitored by IT</string>
<string name="work_profile_edu_work_apps">Work apps are badged &amp; visible to IT</string>
<!-- Action label to proceed to the next work profile edu section-->
<string name="work_profile_edu_next">Next</string>
<!-- Action label to finish work profile edu-->
@@ -65,7 +65,7 @@ public class GridSizeMigrationTaskTest {
};
mIdp.numHotseatIcons = 3;
new GridSizeMigrationTask(mContext, mDb, mValidPackages, 5, 3)
new GridSizeMigrationTask(mContext, mDb, mValidPackages, false, 5, 3)
.migrateHotseat();
// First item is dropped as it has the least weight.
verifyHotseat(hotseatItems[1], hotseatItems[3], hotseatItems[4]);
@@ -82,7 +82,7 @@ public class GridSizeMigrationTaskTest {
};
mIdp.numHotseatIcons = 3;
new GridSizeMigrationTask(mContext, mDb, mValidPackages, 5, 3)
new GridSizeMigrationTask(mContext, mDb, mValidPackages, false, 5, 3)
.migrateHotseat();
// First item is dropped as it has the least weight.
verifyHotseat(hotseatItems[1], hotseatItems[3], hotseatItems[4]);
@@ -127,7 +127,7 @@ public class GridSizeMigrationTaskTest {
{ 5, 2, -1, 6},
}});
new GridSizeMigrationTask(mContext, mDb, mValidPackages,
new GridSizeMigrationTask(mContext, mDb, mValidPackages, false,
new Point(4, 4), new Point(3, 3)).migrateWorkspace();
// Column 2 and row 2 got removed.
@@ -147,7 +147,7 @@ public class GridSizeMigrationTaskTest {
{ 5, 2, -1, 6},
}});
new GridSizeMigrationTask(mContext, mDb, mValidPackages,
new GridSizeMigrationTask(mContext, mDb, mValidPackages, false,
new Point(4, 4), new Point(3, 3)).migrateWorkspace();
// Items in the second column get moved to new screen
@@ -172,7 +172,7 @@ public class GridSizeMigrationTaskTest {
{ 3, 1, -1, 4},
}});
new GridSizeMigrationTask(mContext, mDb, mValidPackages,
new GridSizeMigrationTask(mContext, mDb, mValidPackages, false,
new Point(4, 4), new Point(3, 3)).migrateWorkspace();
// Items in the second column of the first screen should get placed on the 3rd
@@ -204,7 +204,7 @@ public class GridSizeMigrationTaskTest {
{ 5, 2, -1, 6},
}});
new GridSizeMigrationTask(mContext, mDb, mValidPackages,
new GridSizeMigrationTask(mContext, mDb, mValidPackages, false,
new Point(4, 4), new Point(3, 3)).migrateWorkspace();
// Items in the second column of the first screen should get placed on a new screen.
@@ -235,7 +235,7 @@ public class GridSizeMigrationTaskTest {
{ 5, 2, 7, -1},
}}, 0);
new GridSizeMigrationTask(mContext, mDb, mValidPackages,
new GridSizeMigrationTask(mContext, mDb, mValidPackages, false,
new Point(4, 4), new Point(3, 4)).migrateWorkspace();
// Items in the second column of the first screen should get placed on a new screen.
@@ -262,7 +262,7 @@ public class GridSizeMigrationTaskTest {
{ 5, 6, 7, -1},
}}, 0);
new GridSizeMigrationTask(mContext, mDb, mValidPackages,
new GridSizeMigrationTask(mContext, mDb, mValidPackages, false,
new Point(4, 4), new Point(3, 3)).migrateWorkspace();
// Items in the second column of the first screen should get placed on a new screen.
@@ -282,7 +282,7 @@ public class GridSizeMigrationTaskTest {
* represent the workspace grid.
*/
private void verifyWorkspace(int[][][] ids) {
IntArray allScreens = getWorkspaceScreenIds(mDb);
IntArray allScreens = getWorkspaceScreenIds(mDb, LauncherSettings.Favorites.TABLE_NAME);
assertEquals(ids.length, allScreens.size());
int total = 0;
@@ -351,7 +351,7 @@ public class GridSizeMigrationTaskTest {
private final LinkedList<Point> mPoints;
public MultiStepMigrationTaskVerifier(int... points) {
super(null, null, null);
super(null, null, null, false);
mPoints = new LinkedList<>();
for (int i = 0; i < points.length; i += 2) {
@@ -52,6 +52,7 @@ import android.os.Process;
import com.android.launcher3.InvariantDeviceProfile;
import com.android.launcher3.ItemInfo;
import com.android.launcher3.LauncherAppState;
import com.android.launcher3.LauncherSettings;
import com.android.launcher3.WorkspaceItemInfo;
import com.android.launcher3.util.Executors;
import com.android.launcher3.util.LauncherRoboTestRunner;
@@ -91,7 +92,7 @@ public class LoaderCursorTest {
SCREEN, CELLX, CELLY, RESTORED, INTENT
});
mLoaderCursor = new LoaderCursor(mCursor, mApp);
mLoaderCursor = new LoaderCursor(mCursor, LauncherSettings.Favorites.CONTENT_URI, mApp);
mLoaderCursor.allUsers.put(0, Process.myUserHandle());
}
@@ -47,6 +47,7 @@ import androidx.annotation.Nullable;
import androidx.annotation.VisibleForTesting;
import com.android.launcher3.graphics.IconShape;
import com.android.launcher3.graphics.LauncherPreviewRenderer;
import com.android.launcher3.util.ConfigMonitor;
import com.android.launcher3.util.DefaultDisplay;
import com.android.launcher3.util.DefaultDisplay.Info;
@@ -156,6 +157,11 @@ public class InvariantDeviceProfile {
@TargetApi(23)
private InvariantDeviceProfile(Context context) {
if (context instanceof LauncherPreviewRenderer.PreviewContext) {
throw new IllegalArgumentException(
"PreviewContext is passed into this IDP constructor");
}
String gridName = Utilities.getPrefs(context).getBoolean(GRID_OPTIONS_PREFERENCE_KEY, false)
? Utilities.getPrefs(context).getString(KEY_IDP_GRID_NAME, null)
: null;
+8 -2
View File
@@ -80,6 +80,7 @@ import android.view.View;
import android.view.ViewGroup;
import android.view.accessibility.AccessibilityEvent;
import android.view.animation.OvershootInterpolator;
import android.widget.FrameLayout;
import android.widget.Toast;
import androidx.annotation.Nullable;
@@ -273,6 +274,7 @@ public class Launcher extends BaseDraggingActivity implements LauncherExterns,
// UI and state for the overview panel
private View mOverviewPanel;
private FrameLayout mOverviewPanelContainer;
@Thunk
boolean mWorkspaceLoading = true;
@@ -482,7 +484,6 @@ public class Launcher extends BaseDraggingActivity implements LauncherExterns,
@Override
public void onEnterAnimationComplete() {
super.onEnterAnimationComplete();
mAllAppsController.highlightWorkTabIfNecessary();
mRotationHelper.setCurrentTransitionRequest(REQUEST_NONE);
}
@@ -1161,7 +1162,8 @@ public class Launcher extends BaseDraggingActivity implements LauncherExterns,
mFocusHandler = mDragLayer.getFocusIndicatorHelper();
mWorkspace = mDragLayer.findViewById(R.id.workspace);
mWorkspace.initParentViews(mDragLayer);
mOverviewPanel = findViewById(R.id.overview_panel);
mOverviewPanel = findViewById(R.id.overview_panel_recents);
mOverviewPanelContainer = findViewById(R.id.overview_panel_container);
mHotseat = findViewById(R.id.hotseat);
mLauncherView.setSystemUiVisibility(View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN
@@ -1409,6 +1411,10 @@ public class Launcher extends BaseDraggingActivity implements LauncherExterns,
return (T) mOverviewPanel;
}
public FrameLayout getOverviewPanelContainer() {
return mOverviewPanelContainer;
}
public DropTargetBar getDropTargetBar() {
return mDropTargetBar;
}
+32 -18
View File
@@ -27,6 +27,8 @@ import android.content.pm.LauncherApps;
import android.os.Handler;
import android.util.Log;
import androidx.annotation.Nullable;
import com.android.launcher3.config.FeatureFlags;
import com.android.launcher3.icons.IconCache;
import com.android.launcher3.icons.IconProvider;
@@ -55,12 +57,12 @@ public class LauncherAppState {
private final IconCache mIconCache;
private final WidgetPreviewLoader mWidgetCache;
private final InvariantDeviceProfile mInvariantDeviceProfile;
private final SecureSettingsObserver mNotificationDotsObserver;
private final InstallSessionTracker mInstallSessionTracker;
private final SimpleBroadcastReceiver mModelChangeReceiver;
private final SafeCloseable mCalendarChangeTracker;
private final SafeCloseable mUserChangeListener;
private SecureSettingsObserver mNotificationDotsObserver;
private InstallSessionTracker mInstallSessionTracker;
private SimpleBroadcastReceiver mModelChangeReceiver;
private SafeCloseable mCalendarChangeTracker;
private SafeCloseable mUserChangeListener;
public static LauncherAppState getInstance(final Context context) {
return INSTANCE.get(context);
@@ -74,15 +76,8 @@ public class LauncherAppState {
return mContext;
}
private LauncherAppState(Context context) {
Log.v(Launcher.TAG, "LauncherAppState initiated");
Preconditions.assertUIThread();
mContext = context;
mInvariantDeviceProfile = InvariantDeviceProfile.INSTANCE.get(mContext);
mIconCache = new IconCache(mContext, mInvariantDeviceProfile);
mWidgetCache = new WidgetPreviewLoader(mContext, mIconCache);
mModel = new LauncherModel(this, mIconCache, AppFilter.newInstance(mContext));
public LauncherAppState(Context context) {
this(context, LauncherFiles.APP_ICONS_DB);
mModelChangeReceiver = new SimpleBroadcastReceiver(mModel::onBroadcastIntent);
@@ -123,6 +118,17 @@ public class LauncherAppState {
}
}
public LauncherAppState(Context context, @Nullable String iconCacheFileName) {
Log.v(Launcher.TAG, "LauncherAppState initiated");
Preconditions.assertUIThread();
mContext = context;
mInvariantDeviceProfile = InvariantDeviceProfile.INSTANCE.get(context);
mIconCache = new IconCache(mContext, mInvariantDeviceProfile, iconCacheFileName);
mWidgetCache = new WidgetPreviewLoader(mContext, mIconCache);
mModel = new LauncherModel(this, mIconCache, AppFilter.newInstance(mContext));
}
protected void onNotificationSettingsChanged(boolean areNotificationDotsEnabled) {
if (areNotificationDotsEnabled) {
NotificationListener.requestRebind(new ComponentName(
@@ -148,11 +154,19 @@ public class LauncherAppState {
* Call from Application.onTerminate(), which is not guaranteed to ever be called.
*/
public void onTerminate() {
mContext.unregisterReceiver(mModelChangeReceiver);
if (mModelChangeReceiver != null) {
mContext.unregisterReceiver(mModelChangeReceiver);
}
mContext.getSystemService(LauncherApps.class).unregisterCallback(mModel);
mInstallSessionTracker.unregister();
mCalendarChangeTracker.close();
mUserChangeListener.close();
if (mInstallSessionTracker != null) {
mInstallSessionTracker.unregister();
}
if (mCalendarChangeTracker != null) {
mCalendarChangeTracker.close();
}
if (mUserChangeListener != null) {
mUserChangeListener.close();
}
CustomWidgetManager.INSTANCE.get(mContext).setWidgetRefreshCallback(null);
if (mNotificationDotsObserver != null) {
+1 -1
View File
@@ -153,7 +153,7 @@ public class LauncherModel extends LauncherApps.Callback implements InstallSessi
public void onPackagesRemoved(UserHandle user, String... packages) {
int op = PackageUpdatedTask.OP_REMOVE;
FileLog.d(TAG, "package removed received " + String.join("," + packages));
FileLog.d(TAG, "package removed received " + TextUtils.join(",", packages));
enqueueModelUpdateTask(new PackageUpdatedTask(op, user, packages));
}
@@ -93,15 +93,26 @@ public class LauncherSettings {
public static final String TABLE_NAME = "favorites";
/**
* Backup table created when when the favorites table is modified during grid migration
* Backup table created when the favorites table is modified during grid migration
*/
public static final String BACKUP_TABLE_NAME = "favorites_bakup";
/**
* The content:// style URL for this table
* Temporary table used specifically for grid migrations during wallpaper preview
*/
public static final Uri CONTENT_URI = Uri.parse("content://" +
LauncherProvider.AUTHORITY + "/" + TABLE_NAME);
public static final String PREVIEW_TABLE_NAME = "favorites_preview";
/**
* The content:// style URL for "favorites" table
*/
public static final Uri CONTENT_URI = Uri.parse("content://"
+ LauncherProvider.AUTHORITY + "/" + TABLE_NAME);
/**
* The content:// style URL for "favorites_preview" table
*/
public static final Uri PREVIEW_CONTENT_URI = Uri.parse("content://"
+ LauncherProvider.AUTHORITY + "/" + PREVIEW_TABLE_NAME);
/**
* The content:// style URL for a given row, identified by its id.
@@ -111,8 +122,8 @@ public class LauncherSettings {
* @return The unique content URL for the specified row.
*/
public static Uri getContentUri(int id) {
return Uri.parse("content://" + LauncherProvider.AUTHORITY +
"/" + TABLE_NAME + "/" + id);
return Uri.parse("content://" + LauncherProvider.AUTHORITY
+ "/" + TABLE_NAME + "/" + id);
}
/**
@@ -74,6 +74,10 @@ public abstract class LauncherState {
public static final int VERTICAL_SWIPE_INDICATOR = 1 << 5;
public static final int RECENTS_CLEAR_ALL_BUTTON = 1 << 6;
/** Mask of all the items that are contained in the apps view. */
public static final int APPS_VIEW_ITEM_MASK =
HOTSEAT_SEARCH_BOX | ALL_APPS_HEADER | ALL_APPS_HEADER_EXTRA | ALL_APPS_CONTENT;
protected static final int FLAG_MULTI_PAGE = 1 << 0;
protected static final int FLAG_DISABLE_ACCESSIBILITY = 1 << 1;
protected static final int FLAG_DISABLE_RESTORE = 1 << 2;
+2
View File
@@ -374,6 +374,8 @@ public abstract class PagedView<T extends View & PageIndicator> extends ViewGrou
protected void onPageEndTransition() {
mWasInOverscroll = false;
AccessibilityManagerCompat.sendScrollFinishedEventToTest(getContext());
AccessibilityManagerCompat.sendCustomAccessibilityEvent(getPageAt(mCurrentPage),
AccessibilityEvent.TYPE_VIEW_FOCUSED, null);
}
protected int getUnboundedScrollX() {
@@ -56,6 +56,7 @@ public class SecondaryDropTarget extends ButtonDropTarget implements OnAlarmList
private final ArrayMap<UserHandle, Boolean> mUninstallDisabledCache = new ArrayMap<>(1);
private final Alarm mCacheExpireAlarm;
private boolean mHadPendingAlarm;
protected int mCurrentAccessibilityAction = -1;
public SecondaryDropTarget(Context context, AttributeSet attrs) {
@@ -69,6 +70,24 @@ public class SecondaryDropTarget extends ButtonDropTarget implements OnAlarmList
mCacheExpireAlarm.setOnAlarmListener(this);
}
@Override
protected void onAttachedToWindow() {
super.onAttachedToWindow();
if (mHadPendingAlarm) {
mCacheExpireAlarm.setAlarm(CACHE_EXPIRE_TIMEOUT);
mHadPendingAlarm = false;
}
}
@Override
protected void onDetachedFromWindow() {
super.onDetachedFromWindow();
if (mCacheExpireAlarm.alarmPending()) {
mCacheExpireAlarm.cancelAlarm();
mHadPendingAlarm = true;
}
}
@Override
protected void onFinishInflate() {
super.onFinishInflate();
@@ -103,6 +103,8 @@ public class AllAppsContainerView extends SpringRelativeLayout implements DragSo
private final MultiValueAlpha mMultiValueAlpha;
Rect mInsets = new Rect();
public AllAppsContainerView(Context context) {
this(context, null);
}
@@ -325,6 +327,7 @@ public class AllAppsContainerView extends SpringRelativeLayout implements DragSo
@Override
public void setInsets(Rect insets) {
mInsets.set(insets);
DeviceProfile grid = mLauncher.getDeviceProfile();
int leftRightPadding = grid.desiredWorkspaceLeftRightMarginPx
+ grid.cellLayoutPaddingLeftRightPx;
@@ -416,6 +419,7 @@ public class AllAppsContainerView extends SpringRelativeLayout implements DragSo
new ViewGroup.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT,
ViewGroup.LayoutParams.WRAP_CONTENT));
this.addView(mWorkFooterContainer);
mWorkFooterContainer.setInsets(mInsets);
mWorkFooterContainer.post(() -> mAH[AdapterHolder.WORK].applyPadding());
}
@@ -553,17 +557,6 @@ public class AllAppsContainerView extends SpringRelativeLayout implements DragSo
return mHeader != null && mHeader.getVisibility() == View.VISIBLE;
}
public void onScrollUpEnd() {
highlightWorkTabIfNecessary();
}
void highlightWorkTabIfNecessary() {
if (mUsingTabs) {
((PersonalWorkSlidingTabStrip) findViewById(R.id.tabs))
.highlightWorkTabIfNecessary();
}
}
/**
* Adds an update listener to {@param animator} that adds springs to the animation.
*/
@@ -648,9 +641,9 @@ public class AllAppsContainerView extends SpringRelativeLayout implements DragSo
R.layout.work_apps_paused, null);
recyclerView.post(() -> {
int width = recyclerView.getWidth();
int height = recyclerView.getHeight();
pausedOverlay.measure(makeMeasureSpec(width, EXACTLY),
makeMeasureSpec(height, EXACTLY));
int height = recyclerView.getHeight() - mWorkFooterContainer.getHeight();
pausedOverlay.measure(makeMeasureSpec(recyclerView.getWidth(), EXACTLY),
makeMeasureSpec(recyclerView.getHeight(), EXACTLY));
pausedOverlay.layout(0, 0, width, height);
applyPadding();
});
@@ -2,6 +2,7 @@ package com.android.launcher3.allapps;
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.AnimatorSetBuilder.ANIM_ALL_APPS_FADE;
@@ -203,6 +204,8 @@ public class AllAppsTransitionController implements StateHandler, OnDeviceProfil
boolean hasHeaderExtra = (visibleElements & ALL_APPS_HEADER_EXTRA) != 0;
boolean hasAllAppsContent = (visibleElements & ALL_APPS_CONTENT) != 0;
boolean hasAnyVisibleItem = (visibleElements & APPS_VIEW_ITEM_MASK) != 0;
Interpolator allAppsFade = builder.getInterpolator(ANIM_ALL_APPS_FADE, LINEAR);
Interpolator headerFade = builder.getInterpolator(ANIM_ALL_APPS_HEADER_FADE, allAppsFade);
setter.setViewAlpha(mAppsView.getContentView(), hasAllAppsContent ? 1 : 0, allAppsFade);
@@ -213,6 +216,8 @@ public class AllAppsTransitionController implements StateHandler, OnDeviceProfil
setter.setInt(mScrimView, ScrimView.DRAG_HANDLE_ALPHA,
(visibleElements & VERTICAL_SWIPE_INDICATOR) != 0 ? 255 : 0, allAppsFade);
setter.setViewAlpha(mAppsView, hasAnyVisibleItem ? 1 : 0, allAppsFade);
}
public AnimatorListenerAdapter getProgressAnimatorListener() {
@@ -248,18 +253,6 @@ public class AllAppsTransitionController implements StateHandler, OnDeviceProfil
private void onProgressAnimationEnd() {
if (Float.compare(mProgress, 1f) == 0) {
mAppsView.reset(false /* animate */);
} else if (isAllAppsExpanded()) {
mAppsView.onScrollUpEnd();
}
}
private boolean isAllAppsExpanded() {
return Float.compare(mProgress, 0f) == 0;
}
public void highlightWorkTabIfNecessary() {
if (isAllAppsExpanded()) {
mAppsView.highlightWorkTabIfNecessary();
}
}
}
@@ -35,7 +35,6 @@ import com.android.launcher3.LauncherState;
import com.android.launcher3.LauncherStateManager.StateListener;
import com.android.launcher3.R;
import com.android.launcher3.Utilities;
import com.android.launcher3.pm.UserCache;
/**
* Abstract base class of floating view responsible for showing discovery bounce animation
@@ -144,8 +143,7 @@ public class DiscoveryBounce extends AbstractFloatingView {
private static void showForHomeIfNeeded(Launcher launcher, boolean withDelay) {
if (!launcher.isInState(NORMAL)
|| (launcher.getSharedPrefs().getBoolean(HOME_BOUNCE_SEEN, false)
&& !shouldShowForWorkProfile(launcher))
|| launcher.getSharedPrefs().getBoolean(HOME_BOUNCE_SEEN, false)
|| AbstractFloatingView.getTopOpenView(launcher) != null
|| launcher.getSystemService(UserManager.class).isDemoUser()
|| Utilities.IS_RUNNING_IN_TEST_HARNESS) {
@@ -170,8 +168,7 @@ public class DiscoveryBounce extends AbstractFloatingView {
|| !launcher.hasBeenResumed()
|| launcher.isForceInvisible()
|| launcher.getDeviceProfile().isVerticalBarLayout()
|| (launcher.getSharedPrefs().getBoolean(SHELF_BOUNCE_SEEN, false)
&& !shouldShowForWorkProfile(launcher))
|| launcher.getSharedPrefs().getBoolean(SHELF_BOUNCE_SEEN, false)
|| launcher.getSystemService(UserManager.class).isDemoUser()
|| Utilities.IS_RUNNING_IN_TEST_HARNESS) {
return;
@@ -213,12 +210,6 @@ public class DiscoveryBounce extends AbstractFloatingView {
}
}
private static boolean shouldShowForWorkProfile(Launcher launcher) {
return !launcher.getSharedPrefs().getBoolean(
PersonalWorkSlidingTabStrip.KEY_SHOWED_PEEK_WORK_TAB, false)
&& UserCache.INSTANCE.get(launcher).hasWorkProfile();
}
private static void incrementShelfBounceCount(Launcher launcher) {
SharedPreferences sharedPrefs = launcher.getSharedPrefs();
int count = sharedPrefs.getInt(SHELF_BOUNCE_COUNT, 0);
@@ -63,6 +63,14 @@ public class LauncherAllAppsContainerView extends AllAppsContainerView {
.setScrollRangeDelta(mSearchUiManager.getScrollRangeDelta(insets));
}
@Override
public void setupHeader() {
super.setupHeader();
if (mWorkTabListener != null && !mUsingTabs) {
mLauncher.getStateManager().removeStateListener(mWorkTabListener);
}
}
@Override
public void onTabChanged(int pos) {
super.onTabChanged(pos);
@@ -16,7 +16,6 @@
package com.android.launcher3.allapps;
import android.content.Context;
import android.content.SharedPreferences;
import android.graphics.Canvas;
import android.graphics.Paint;
import android.util.AttributeSet;
@@ -39,11 +38,8 @@ public class PersonalWorkSlidingTabStrip extends LinearLayout implements PageInd
private static final int POSITION_PERSONAL = 0;
private static final int POSITION_WORK = 1;
public static final String KEY_SHOWED_PEEK_WORK_TAB = "showed_peek_work_tab";
private final Paint mSelectedIndicatorPaint;
private final Paint mDividerPaint;
private final SharedPreferences mSharedPreferences;
private int mSelectedIndicatorHeight;
private int mIndicatorLeft = -1;
@@ -72,7 +68,6 @@ public class PersonalWorkSlidingTabStrip extends LinearLayout implements PageInd
mDividerPaint.setStrokeWidth(
getResources().getDimensionPixelSize(R.dimen.all_apps_divider_height));
mSharedPreferences = Utilities.getPrefs(context);
mIsRtl = Utilities.isRtl(getResources());
}
@@ -128,25 +123,6 @@ public class PersonalWorkSlidingTabStrip extends LinearLayout implements PageInd
mIndicatorRight, getHeight(), mSelectedIndicatorPaint);
}
public void highlightWorkTabIfNecessary() {
if (mSharedPreferences.getBoolean(KEY_SHOWED_PEEK_WORK_TAB, false)) {
return;
}
if (mLastActivePage != POSITION_PERSONAL) {
return;
}
highlightWorkTab();
mSharedPreferences.edit().putBoolean(KEY_SHOWED_PEEK_WORK_TAB, true).apply();
}
private void highlightWorkTab() {
View v = getChildAt(POSITION_WORK);
v.post(() -> {
v.setPressed(true);
v.setPressed(false);
});
}
@Override
public void setScroll(int currentScroll, int totalScroll) {
float scrollOffset = ((float) currentScroll) / totalScroll;
@@ -18,11 +18,14 @@ package com.android.launcher3.compat;
import android.content.Context;
import android.os.Bundle;
import android.text.TextUtils;
import android.util.Log;
import android.view.View;
import android.view.accessibility.AccessibilityEvent;
import android.view.accessibility.AccessibilityManager;
import androidx.annotation.Nullable;
import com.android.launcher3.Utilities;
import com.android.launcher3.testing.TestProtocol;
@@ -37,11 +40,21 @@ public class AccessibilityManagerCompat {
return isAccessibilityEnabled(context);
}
public static void sendCustomAccessibilityEvent(View target, int type, String text) {
if (isObservedEventType(target.getContext(), type)) {
/**
*
* @param target The view the accessibility event is initialized on.
* If null, this method has no effect.
* @param type See TYPE_ constants defined in {@link AccessibilityEvent}.
* @param text Optional text to add to the event, which will be announced to the user.
*/
public static void sendCustomAccessibilityEvent(@Nullable View target, int type,
@Nullable String text) {
if (target != null && isObservedEventType(target.getContext(), type)) {
AccessibilityEvent event = AccessibilityEvent.obtain(type);
target.onInitializeAccessibilityEvent(event);
event.getText().add(text);
if (!TextUtils.isEmpty(text)) {
event.getText().add(text);
}
getManager(target.getContext()).sendAccessibilityEvent(event);
}
}
@@ -124,7 +124,7 @@ public final class FeatureFlags {
"Show launcher preview in grid picker");
public static final BooleanFlag ENABLE_OVERVIEW_ACTIONS = getDebugFlag(
"ENABLE_OVERVIEW_ACTIONS", false, "Show app actions instead of the shelf in Overview."
"ENABLE_OVERVIEW_ACTIONS", true, "Show app actions instead of the shelf in Overview."
+ " As part of this decoupling, also distinguish swipe up from nav bar vs above it.");
public static final BooleanFlag ENABLE_DATABASE_RESTORE = getDebugFlag(
@@ -135,6 +135,10 @@ public final class FeatureFlags {
"ENABLE_UNIVERSAL_SMARTSPACE", false,
"Replace Smartspace with a version rendered by System UI.");
public static final BooleanFlag ENABLE_LSQ_VELOCITY_PROVIDER = getDebugFlag(
"ENABLE_LSQ_VELOCITY_PROVIDER", false,
"Use Least Square algorithm for motion pause detection.");
public static void initialize(Context context) {
synchronized (sDebugFlags) {
for (DebugFlag flag : sDebugFlags) {
+64 -42
View File
@@ -33,6 +33,7 @@ import static com.android.launcher3.userevent.LauncherLogProto.Target.FromFolder
import static java.util.Arrays.asList;
import static java.util.Arrays.stream;
import static java.util.Optional.ofNullable;
import android.animation.Animator;
import android.animation.AnimatorListenerAdapter;
@@ -95,6 +96,7 @@ import com.android.launcher3.userevent.LauncherLogProto.ItemType;
import com.android.launcher3.userevent.LauncherLogProto.LauncherEvent;
import com.android.launcher3.userevent.LauncherLogProto.Target;
import com.android.launcher3.userevent.nano.LauncherLogProto;
import com.android.launcher3.util.Executors;
import com.android.launcher3.util.Thunk;
import com.android.launcher3.views.ClipPathView;
import com.android.launcher3.widget.PendingAddShortcutInfo;
@@ -322,12 +324,11 @@ public class Folder extends AbstractFloatingView implements ClipPathView, DragSo
post(() -> {
if (FeatureFlags.FOLDER_NAME_SUGGEST.get()) {
if (isEmpty(mFolderName.getText())) {
FolderNameInfo[] nameInfos =
(FolderNameInfo[]) mInfo.suggestedFolderNames.getParcelableArrayExtra(
FolderInfo.EXTRA_FOLDER_SUGGESTIONS);
if (nameInfos != null) {
showLabelSuggestion(nameInfos, false);
}
ofNullable(mInfo)
.map(info -> info.suggestedFolderNames)
.map(folderNames -> (FolderNameInfo[]) folderNames
.getParcelableArrayExtra(FolderInfo.EXTRA_FOLDER_SUGGESTIONS))
.ifPresent(nameInfos -> showLabelSuggestion(nameInfos, false));
}
}
mFolderName.setHint("");
@@ -345,7 +346,7 @@ public class Folder extends AbstractFloatingView implements ClipPathView, DragSo
}
mInfo.title = newTitle;
mInfo.setOption(FLAG_MANUAL_FOLDER_NAME, mFolderName.isEnteredCompose(),
mInfo.setOption(FLAG_MANUAL_FOLDER_NAME, getAcceptedSuggestionIndex() < 0,
mLauncher.getModelWriter());
mFolderIcon.onTitleChanged(newTitle);
mLauncher.getModelWriter().updateItemInDatabase(mInfo);
@@ -426,7 +427,7 @@ public class Folder extends AbstractFloatingView implements ClipPathView, DragSo
mInfo = info;
ArrayList<WorkspaceItemInfo> children = info.contents;
Collections.sort(children, ITEM_POS_COMPARATOR);
updateItemLocationsInDatabaseBatch();
updateItemLocationsInDatabaseBatch(true);
DragLayer.LayoutParams lp = (DragLayer.LayoutParams) getLayoutParams();
if (lp == null) {
@@ -436,19 +437,15 @@ public class Folder extends AbstractFloatingView implements ClipPathView, DragSo
}
mItemsInvalidated = true;
mInfo.addListener(this);
mPreviousLabel = mInfo.title.toString();
mIsPreviousLabelSuggested = !mInfo.hasOption(FLAG_MANUAL_FOLDER_NAME);
if (!isEmpty(mInfo.title)) {
mFolderName.setText(mInfo.title);
mPreviousLabel = mInfo.title.toString();
mIsPreviousLabelSuggested = !mInfo.hasOption(FLAG_MANUAL_FOLDER_NAME);
mFolderName.setHint(null);
} else {
mFolderName.setText("");
if (FeatureFlags.FOLDER_NAME_SUGGEST.get()) {
mFolderName.setHint("");
} else {
mFolderName.setHint(R.string.folder_hint_text);
}
mFolderName.setHint(R.string.folder_hint_text);
}
// In case any children didn't come across during loading, clean up the folder accordingly
mFolderIcon.post(() -> {
@@ -464,8 +461,6 @@ public class Folder extends AbstractFloatingView implements ClipPathView, DragSo
*/
public void showSuggestedTitle(FolderNameInfo[] nameInfos) {
if (FeatureFlags.FOLDER_NAME_SUGGEST.get()) {
mInfo.suggestedFolderNames = new Intent().putExtra(FolderInfo.EXTRA_FOLDER_SUGGESTIONS,
nameInfos);
if (isEmpty(mFolderName.getText().toString())
&& !mInfo.hasOption(FLAG_MANUAL_FOLDER_NAME)) {
showLabelSuggestion(nameInfos, true);
@@ -487,9 +482,9 @@ public class Folder extends AbstractFloatingView implements ClipPathView, DragSo
nameInfos[0].getLabel())
|| nameInfos.length > 1 && nameInfos[1] != null && !isEmpty(
nameInfos[1].getLabel());
CharSequence firstLabel = nameInfos[0].getLabel();
if (shouldOpen) {
CharSequence firstLabel = nameInfos[0] == null ? "" : nameInfos[0].getLabel();
if (!isEmpty(firstLabel)) {
mFolderName.setHint("");
mFolderName.setText(firstLabel);
@@ -985,7 +980,7 @@ public class Folder extends AbstractFloatingView implements ClipPathView, DragSo
// Reordering may have occured, and we need to save the new item locations. We do this once
// at the end to prevent unnecessary database operations.
updateItemLocationsInDatabaseBatch();
updateItemLocationsInDatabaseBatch(false);
// Use the item count to check for multi-page as the folder UI may not have
// been refreshed yet.
if (getItemCount() <= mContent.itemsPerPage()) {
@@ -995,7 +990,7 @@ public class Folder extends AbstractFloatingView implements ClipPathView, DragSo
}
}
private void updateItemLocationsInDatabaseBatch() {
private void updateItemLocationsInDatabaseBatch(boolean isBind) {
FolderGridOrganizer verifier = new FolderGridOrganizer(
mLauncher.getDeviceProfile().inv).setFolderInfo(mInfo);
@@ -1011,6 +1006,18 @@ public class Folder extends AbstractFloatingView implements ClipPathView, DragSo
if (!items.isEmpty()) {
mLauncher.getModelWriter().moveItemsInDatabase(items, mInfo.id, 0);
}
if (FeatureFlags.FOLDER_NAME_SUGGEST.get() && !isBind) {
Executors.MODEL_EXECUTOR.post(() -> {
FolderNameInfo[] nameInfos =
new FolderNameInfo[FolderNameProvider.SUGGEST_MAX];
FolderNameProvider fnp = FolderNameProvider.newInstance(getContext());
fnp.getSuggestedFolderName(
getContext(), mInfo.contents, nameInfos);
mInfo.suggestedFolderNames = new Intent().putExtra(
FolderInfo.EXTRA_FOLDER_SUGGESTIONS,
nameInfos);
});
}
}
public void notifyDrop() {
@@ -1315,7 +1322,7 @@ public class Folder extends AbstractFloatingView implements ClipPathView, DragSo
// We only need to update the locations if it doesn't get handled in
// #onDropCompleted.
if (d.dragSource != this) {
updateItemLocationsInDatabaseBatch();
updateItemLocationsInDatabaseBatch(false);
}
}
@@ -1356,7 +1363,7 @@ public class Folder extends AbstractFloatingView implements ClipPathView, DragSo
verifier.updateRankAndPos(item, rank);
mLauncher.getModelWriter().addOrMoveItemInDatabase(item, mInfo.id, 0, item.cellX,
item.cellY);
updateItemLocationsInDatabaseBatch();
updateItemLocationsInDatabaseBatch(false);
if (mContent.areViewsBound()) {
mContent.createAndAddViewForRank(item, rank);
@@ -1647,26 +1654,8 @@ public class Folder extends AbstractFloatingView implements ClipPathView, DragSo
checkNotNull(mFolderName.getText().toString(),
"Expected valid folder label, but found null");
Optional<String[]> suggestedLabels = Optional.ofNullable(
(FolderNameInfo[]) mInfo.suggestedFolderNames
.getParcelableArrayExtra(FolderInfo.EXTRA_FOLDER_SUGGESTIONS))
.map(folderNameInfoArray ->
stream(folderNameInfoArray)
.filter(Objects::nonNull)
.map(FolderNameInfo::getLabel)
.map(CharSequence::toString)
.toArray(String[]::new));
int accepted_suggestion_index = suggestedLabels
.map(folderNameInfoArray ->
IntStream.range(0, folderNameInfoArray.length)
.filter(index -> newLabel.equalsIgnoreCase(
folderNameInfoArray[index]))
.findFirst()
.orElse(-1)
).orElse(-1);
Optional<String[]> suggestedLabels = getSuggestedLabels();
int accepted_suggestion_index = getAcceptedSuggestionIndex();
boolean hasValidPrimary = suggestedLabels
.map(labels -> labels.length > 0 && !isEmpty(labels[0]))
.orElse(false);
@@ -1695,6 +1684,39 @@ public class Folder extends AbstractFloatingView implements ClipPathView, DragSo
: Target.ToFolderLabelState.valueOf("TO_CUSTOM" + suggestionsSuffix);
}
private Optional<String[]> getSuggestedLabels() {
return ofNullable(mInfo)
.map(info -> info.suggestedFolderNames)
.map(
folderNames ->
(FolderNameInfo[])
folderNames.getParcelableArrayExtra(FolderInfo.EXTRA_FOLDER_SUGGESTIONS))
.map(
folderNameInfoArray ->
stream(folderNameInfoArray)
.filter(Objects::nonNull)
.map(FolderNameInfo::getLabel)
.filter(Objects::nonNull)
.map(CharSequence::toString)
.toArray(String[]::new));
}
private int getAcceptedSuggestionIndex() {
String newLabel =
checkNotNull(mFolderName.getText().toString(),
"Expected valid folder label, but found null");
return getSuggestedLabels()
.map(suggestionsArray ->
IntStream.range(0, suggestionsArray.length)
.filter(index -> newLabel.equalsIgnoreCase(
suggestionsArray[index]))
.findFirst()
.orElse(-1)
).orElse(-1);
}
private Target.Builder newEditTextTargetBuilder() {
return Target.newBuilder().setType(Target.Type.ITEM).setItemType(ItemType.EDITTEXT);
@@ -102,13 +102,6 @@ public class FolderNameEditText extends ExtendedEditText {
mEnteredCompose = value;
}
protected boolean isEnteredCompose() {
if (DEBUG) {
Log.d(TAG, "isEnteredCompose " + mEnteredCompose);
}
return mEnteredCompose;
}
private class FolderNameEditTextInputConnection extends InputConnectionWrapper {
FolderNameEditTextInputConnection(InputConnection target, boolean mutable) {
@@ -80,4 +80,10 @@ public final class FolderNameInfo implements Parcelable {
public int describeContents() {
return 0;
}
@Override
@NonNull
public String toString() {
return String.format("%s:%.2f", mLabel, mScore);
}
}
@@ -20,13 +20,19 @@ import static android.view.View.MeasureSpec.makeMeasureSpec;
import static android.view.View.VISIBLE;
import static com.android.launcher3.config.FeatureFlags.ENABLE_LAUNCHER_PREVIEW_IN_GRID_PICKER;
import static com.android.launcher3.config.FeatureFlags.MULTI_DB_GRID_MIRATION_ALGO;
import static com.android.launcher3.model.GridSizeMigrationTask.needsToMigrate;
import static com.android.launcher3.model.ModelUtils.filterCurrentWorkspaceItems;
import static com.android.launcher3.model.ModelUtils.sortWorkspaceItemsSpatially;
import static com.android.launcher3.util.Executors.MODEL_EXECUTOR;
import android.annotation.TargetApi;
import android.app.Fragment;
import android.appwidget.AppWidgetHostView;
import android.content.Context;
import android.content.ContextWrapper;
import android.content.Intent;
import android.content.pm.ShortcutInfo;
import android.content.res.TypedArray;
import android.graphics.Bitmap;
import android.graphics.Canvas;
@@ -55,6 +61,7 @@ import com.android.launcher3.InsettableFrameLayout;
import com.android.launcher3.InvariantDeviceProfile;
import com.android.launcher3.ItemInfo;
import com.android.launcher3.LauncherAppState;
import com.android.launcher3.LauncherAppWidgetInfo;
import com.android.launcher3.LauncherModel;
import com.android.launcher3.LauncherSettings;
import com.android.launcher3.LauncherSettings.Favorites;
@@ -68,15 +75,31 @@ import com.android.launcher3.folder.FolderIcon;
import com.android.launcher3.icons.BaseIconFactory;
import com.android.launcher3.icons.BitmapInfo;
import com.android.launcher3.icons.BitmapRenderer;
import com.android.launcher3.icons.LauncherIcons;
import com.android.launcher3.model.AllAppsList;
import com.android.launcher3.model.BgDataModel;
import com.android.launcher3.model.BgDataModel.Callbacks;
import com.android.launcher3.model.GridSizeMigrationTask;
import com.android.launcher3.model.GridSizeMigrationTaskV2;
import com.android.launcher3.model.LoaderResults;
import com.android.launcher3.model.LoaderTask;
import com.android.launcher3.model.WidgetItem;
import com.android.launcher3.model.WidgetsModel;
import com.android.launcher3.pm.InstallSessionHelper;
import com.android.launcher3.pm.UserCache;
import com.android.launcher3.util.MainThreadInitializedObject;
import com.android.launcher3.views.ActivityContext;
import com.android.launcher3.views.BaseDragLayer;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.Callable;
import java.util.concurrent.ConcurrentLinkedQueue;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.Executor;
@@ -97,6 +120,81 @@ public class LauncherPreviewRenderer implements Callable<Bitmap> {
private static final String TAG = "LauncherPreviewRenderer";
/**
* Context used just for preview. It also provides a few objects (e.g. UserCache) just for
* preview purposes.
*/
public static class PreviewContext extends ContextWrapper {
private static final Set<MainThreadInitializedObject> WHITELIST = new HashSet<>(
Arrays.asList(UserCache.INSTANCE, InstallSessionHelper.INSTANCE,
LauncherAppState.INSTANCE, InvariantDeviceProfile.INSTANCE));
private final InvariantDeviceProfile mIdp;
private final Map<MainThreadInitializedObject, Object> mObjectMap = new HashMap<>();
private final ConcurrentLinkedQueue<LauncherIconsForPreview> mIconPool =
new ConcurrentLinkedQueue<>();
public PreviewContext(Context base, InvariantDeviceProfile idp) {
super(base);
mIdp = idp;
}
@Override
public Context getApplicationContext() {
return this;
}
/**
* Find a cached object from mObjectMap if we have already created one. If not, generate
* an object using the provider.
*/
public <T> T getObject(MainThreadInitializedObject<T> mainThreadInitializedObject,
MainThreadInitializedObject.ObjectProvider<T> provider) {
if (!WHITELIST.contains(mainThreadInitializedObject)) {
throw new IllegalStateException("Leaking unknown objects");
}
if (mainThreadInitializedObject == LauncherAppState.INSTANCE) {
throw new IllegalStateException(
"Should not use MainThreadInitializedObject to initialize this with "
+ "PreviewContext");
}
if (mainThreadInitializedObject == InvariantDeviceProfile.INSTANCE) {
return (T) mIdp;
}
if (mObjectMap.containsKey(mainThreadInitializedObject)) {
return (T) mObjectMap.get(mainThreadInitializedObject);
}
T t = provider.get(this);
mObjectMap.put(mainThreadInitializedObject, t);
return t;
}
public LauncherIcons newLauncherIcons(Context context, boolean shapeDetection) {
LauncherIconsForPreview launcherIconsForPreview = mIconPool.poll();
if (launcherIconsForPreview != null) {
return launcherIconsForPreview;
}
return new LauncherIconsForPreview(context, mIdp.fillResIconDpi, mIdp.iconBitmapSize,
-1 /* poolId */, shapeDetection);
}
private final class LauncherIconsForPreview extends LauncherIcons {
private LauncherIconsForPreview(Context context, int fillResIconDpi, int iconBitmapSize,
int poolId, boolean shapeDetection) {
super(context, fillResIconDpi, iconBitmapSize, poolId, shapeDetection);
}
@Override
public void recycle() {
// Clear any temporary state variables
clear();
mIconPool.offer(this);
}
}
}
private final Handler mUiHandler;
private final Context mContext;
private final InvariantDeviceProfile mIdp;
@@ -248,6 +346,16 @@ public class LauncherPreviewRenderer implements Callable<Bitmap> {
addInScreenFromBind(folderIcon, info);
}
private void inflateAndAddWidgets(LauncherAppWidgetInfo info, WidgetsModel widgetsModel) {
WidgetItem widgetItem = widgetsModel.getWidgetProviderInfoByProviderName(
info.providerName);
AppWidgetHostView view = new AppWidgetHostView(mContext);
view.setAppWidget(-1, widgetItem.widgetInfo);
view.updateAppWidget(null);
view.setTag(info);
addInScreenFromBind(view, info);
}
private void dispatchVisibilityAggregated(View view, boolean isVisible) {
// Similar to View.dispatchVisibilityAggregated implementation.
final boolean thisVisible = view.getVisibility() == VISIBLE;
@@ -268,15 +376,28 @@ public class LauncherPreviewRenderer implements Callable<Bitmap> {
private void renderScreenShot(Canvas canvas) {
if (ENABLE_LAUNCHER_PREVIEW_IN_GRID_PICKER.get()) {
final LauncherModel launcherModel = LauncherAppState.getInstance(
mContext).getModel();
final WorkspaceItemsInfoFetcher fetcher = new WorkspaceItemsInfoFetcher();
launcherModel.enqueueModelUpdateTask(fetcher);
ArrayList<ItemInfo> workspaceItems;
try {
workspaceItems = fetcher.mTask.get(5, TimeUnit.SECONDS);
} catch (InterruptedException | ExecutionException | TimeoutException e) {
Log.d(TAG, "Error fetching workspace items info", e);
boolean needsToMigrate = needsToMigrate(mContext, mIdp);
boolean success = false;
if (needsToMigrate) {
success = MULTI_DB_GRID_MIRATION_ALGO.get()
? GridSizeMigrationTaskV2.migrateGridIfNeeded(mContext, mIdp)
: GridSizeMigrationTask.migrateGridIfNeeded(mContext, mIdp);
}
WorkspaceFetcher fetcher;
if (needsToMigrate && success) {
LauncherAppState appForPreview = new LauncherAppState(
new PreviewContext(mContext, mIdp), null /* iconCacheFileName */);
fetcher = new WorkspaceItemsInfoFromPreviewFetcher(appForPreview);
MODEL_EXECUTOR.execute(fetcher);
} else {
fetcher = new WorkspaceItemsInfoFetcher();
LauncherAppState.getInstance(mContext).getModel().enqueueModelUpdateTask(
(LauncherModel.ModelUpdateTask) fetcher);
}
WorkspaceResult workspaceResult = fetcher.get();
if (workspaceResult == null) {
return;
}
@@ -284,9 +405,14 @@ public class LauncherPreviewRenderer implements Callable<Bitmap> {
// items
ArrayList<ItemInfo> currentWorkspaceItems = new ArrayList<>();
ArrayList<ItemInfo> otherWorkspaceItems = new ArrayList<>();
ArrayList<LauncherAppWidgetInfo> currentAppWidgets = new ArrayList<>();
ArrayList<LauncherAppWidgetInfo> otherAppWidgets = new ArrayList<>();
filterCurrentWorkspaceItems(0 /* currentScreenId */, workspaceItems,
currentWorkspaceItems, otherWorkspaceItems);
filterCurrentWorkspaceItems(0 /* currentScreenId */,
workspaceResult.mWorkspaceItems, currentWorkspaceItems,
otherWorkspaceItems);
filterCurrentWorkspaceItems(0 /* currentScreenId */, workspaceResult.mAppWidgets,
currentAppWidgets, otherAppWidgets);
sortWorkspaceItemsSpatially(mIdp, currentWorkspaceItems);
for (ItemInfo itemInfo : currentWorkspaceItems) {
@@ -303,6 +429,17 @@ public class LauncherPreviewRenderer implements Callable<Bitmap> {
break;
}
}
for (ItemInfo itemInfo : currentAppWidgets) {
switch (itemInfo.itemType) {
case Favorites.ITEM_TYPE_APPWIDGET:
case Favorites.ITEM_TYPE_CUSTOM_APPWIDGET:
inflateAndAddWidgets((LauncherAppWidgetInfo) itemInfo,
workspaceResult.mWidgetsModel);
break;
default:
break;
}
}
} else {
// Add hotseat icons
for (int i = 0; i < mIdp.numHotseatIcons; i++) {
@@ -349,10 +486,15 @@ public class LauncherPreviewRenderer implements Callable<Bitmap> {
}
}
private static class WorkspaceItemsInfoFetcher implements Callable<ArrayList<ItemInfo>>,
LauncherModel.ModelUpdateTask {
private static void measureView(View view, int width, int height) {
view.measure(makeMeasureSpec(width, EXACTLY), makeMeasureSpec(height, EXACTLY));
view.layout(0, 0, width, height);
}
private final FutureTask<ArrayList<ItemInfo>> mTask = new FutureTask<>(this);
private static class WorkspaceItemsInfoFetcher implements LauncherModel.ModelUpdateTask,
WorkspaceFetcher {
private final FutureTask<WorkspaceResult> mTask = new FutureTask<>(this);
private LauncherAppState mApp;
private LauncherModel mModel;
@@ -368,25 +510,81 @@ public class LauncherPreviewRenderer implements Callable<Bitmap> {
mAllAppsList = allAppsList;
}
@Override
public FutureTask<WorkspaceResult> getTask() {
return mTask;
}
@Override
public void run() {
mTask.run();
}
@Override
public ArrayList<ItemInfo> call() throws Exception {
public WorkspaceResult call() throws Exception {
if (!mModel.isModelLoaded()) {
Log.d(TAG, "Workspace not loaded, loading now");
mModel.startLoaderForResults(
new LoaderResults(mApp, mBgDataModel, mAllAppsList, new Callbacks[0]));
return new ArrayList<>();
return null;
}
return mBgDataModel.workspaceItems;
return new WorkspaceResult(mBgDataModel.workspaceItems, mBgDataModel.appWidgets,
mBgDataModel.widgetsModel);
}
}
private static void measureView(View view, int width, int height) {
view.measure(makeMeasureSpec(width, EXACTLY), makeMeasureSpec(height, EXACTLY));
view.layout(0, 0, width, height);
private static class WorkspaceItemsInfoFromPreviewFetcher extends LoaderTask implements
WorkspaceFetcher {
private final FutureTask<WorkspaceResult> mTask = new FutureTask<>(this);
WorkspaceItemsInfoFromPreviewFetcher(LauncherAppState app) {
super(app, null, new BgDataModel(), null);
}
@Override
public FutureTask<WorkspaceResult> getTask() {
return mTask;
}
@Override
public void run() {
mTask.run();
}
@Override
public WorkspaceResult call() throws Exception {
List<ShortcutInfo> allShortcuts = new ArrayList<>();
loadWorkspace(allShortcuts, LauncherSettings.Favorites.PREVIEW_CONTENT_URI);
return new WorkspaceResult(mBgDataModel.workspaceItems, mBgDataModel.appWidgets,
mBgDataModel.widgetsModel);
}
}
private interface WorkspaceFetcher extends Runnable, Callable<WorkspaceResult> {
FutureTask<WorkspaceResult> getTask();
default WorkspaceResult get() {
try {
return getTask().get(5, TimeUnit.SECONDS);
} catch (InterruptedException | ExecutionException | TimeoutException e) {
Log.d(TAG, "Error fetching workspace items info", e);
return null;
}
}
}
private static class WorkspaceResult {
private final ArrayList<ItemInfo> mWorkspaceItems;
private final ArrayList<LauncherAppWidgetInfo> mAppWidgets;
private final WidgetsModel mWidgetsModel;
private WorkspaceResult(ArrayList<ItemInfo> workspaceItems,
ArrayList<LauncherAppWidgetInfo> appWidgets, WidgetsModel widgetsModel) {
mWorkspaceItems = workspaceItems;
mAppWidgets = appWidgets;
mWidgetsModel = widgetsModel;
}
}
}
@@ -81,9 +81,13 @@ public class IconCache extends BaseIconCache {
private int mPendingIconRequestCount = 0;
public IconCache(Context context, InvariantDeviceProfile inv) {
super(context, LauncherFiles.APP_ICONS_DB, MODEL_EXECUTOR.getLooper(),
inv.fillResIconDpi, inv.iconBitmapSize, true /* inMemoryCache */);
public IconCache(Context context, InvariantDeviceProfile idp) {
this(context, idp, LauncherFiles.APP_ICONS_DB);
}
public IconCache(Context context, InvariantDeviceProfile idp, String dbFileName) {
super(context, dbFileName, MODEL_EXECUTOR.getLooper(),
idp.fillResIconDpi, idp.iconBitmapSize, true /* inMemoryCache */);
mComponentWithLabelCachingLogic = new ComponentCachingLogic(context, false);
mLauncherActivityInfoCachingLogic = LauncherActivityCachingLogic.newInstance(context);
mShortcutCachingLogic = new ShortcutCachingLogic();
@@ -19,8 +19,8 @@ package com.android.launcher3.icons;
import android.content.Context;
import com.android.launcher3.InvariantDeviceProfile;
import com.android.launcher3.LauncherAppState;
import com.android.launcher3.graphics.IconShape;
import com.android.launcher3.graphics.LauncherPreviewRenderer;
/**
* Wrapper class to provide access to {@link BaseIconFactory} and also to provide pool of this class
@@ -41,6 +41,11 @@ public class LauncherIcons extends BaseIconFactory implements AutoCloseable {
* avoid allocating new objects in many cases.
*/
public static LauncherIcons obtain(Context context, boolean shapeDetection) {
if (context instanceof LauncherPreviewRenderer.PreviewContext) {
return ((LauncherPreviewRenderer.PreviewContext) context).newLauncherIcons(context,
shapeDetection);
}
int poolId;
synchronized (sPoolSync) {
if (sPool != null) {
@@ -52,7 +57,7 @@ public class LauncherIcons extends BaseIconFactory implements AutoCloseable {
poolId = sPoolId;
}
InvariantDeviceProfile idp = LauncherAppState.getIDP(context);
InvariantDeviceProfile idp = InvariantDeviceProfile.INSTANCE.get(context);
return new LauncherIcons(context, idp.fillResIconDpi, idp.iconBitmapSize, poolId,
shapeDetection);
}
@@ -68,7 +73,7 @@ public class LauncherIcons extends BaseIconFactory implements AutoCloseable {
private LauncherIcons next;
private LauncherIcons(Context context, int fillResIconDpi, int iconBitmapSize, int poolId,
protected LauncherIcons(Context context, int fillResIconDpi, int iconBitmapSize, int poolId,
boolean shapeDetection) {
super(context, fillResIconDpi, iconBitmapSize, shapeDetection);
mPoolId = poolId;
@@ -10,6 +10,7 @@ import android.util.Pair;
import androidx.annotation.VisibleForTesting;
import com.android.launcher3.config.FeatureFlags;
import com.android.launcher3.util.IOUtils;
import java.io.BufferedReader;
@@ -42,7 +43,7 @@ public final class FileLog {
private static Handler sHandler = null;
private static File sLogsDirectory = null;
public static final int LOG_DAYS = 4;
public static final int LOG_DAYS = FeatureFlags.ENABLE_HYBRID_HOTSEAT.get() ? 10 : 4;
public static void setDir(File logsDir) {
if (ENABLED) {
@@ -147,7 +147,7 @@ public class UserEventDispatcher implements ResourceBasedOverride {
fillIntentInfo(event.srcTarget[0], intent, userHandle);
}
ItemInfo info = (ItemInfo) v.getTag();
if (Utilities.IS_DEBUG_DEVICE && FeatureFlags.ENABLE_HYBRID_HOTSEAT.get()) {
if (info != null && Utilities.IS_DEBUG_DEVICE && FeatureFlags.ENABLE_HYBRID_HOTSEAT.get()) {
FileLog.d(TAG, "appLaunch: packageName:" + info.getTargetComponent().getPackageName()
+ ",isWorkApp:" + (info.user != null && !Process.myUserHandle().equals(
userHandle)) + ",launchLocation:" + info.container);
@@ -100,12 +100,24 @@ public class GridBackupTable {
Process.myUserHandle()), 0);
return false;
}
return restoreIfBackupExists(Favorites.TABLE_NAME);
}
public boolean restoreToPreviewIfBackupExists() {
if (!tableExists(mDb, BACKUP_TABLE_NAME)) {
return false;
}
return restoreIfBackupExists(Favorites.PREVIEW_TABLE_NAME);
}
private boolean restoreIfBackupExists(String toTableName) {
if (loadDBProperties() != STATE_SANITIZED) {
return false;
}
long userSerial = UserCache.INSTANCE.get(mContext).getSerialNumberForUser(
Process.myUserHandle());
copyTable(mDb, BACKUP_TABLE_NAME, Favorites.TABLE_NAME, userSerial);
copyTable(mDb, BACKUP_TABLE_NAME, toTableName, userSerial);
Log.d(TAG, "Backup table found");
return true;
}
@@ -3,6 +3,7 @@ package com.android.launcher3.model;
import static com.android.launcher3.LauncherSettings.Settings.EXTRA_VALUE;
import static com.android.launcher3.Utilities.getPointString;
import static com.android.launcher3.Utilities.parsePoint;
import static com.android.launcher3.provider.LauncherDbUtils.copyTable;
import android.content.ComponentName;
import android.content.ContentValues;
@@ -14,6 +15,7 @@ import android.content.pm.PackageManager;
import android.database.Cursor;
import android.database.sqlite.SQLiteDatabase;
import android.graphics.Point;
import android.os.SystemClock;
import android.util.Log;
import android.util.SparseArray;
@@ -29,6 +31,7 @@ import com.android.launcher3.LauncherSettings.Settings;
import com.android.launcher3.Utilities;
import com.android.launcher3.Workspace;
import com.android.launcher3.config.FeatureFlags;
import com.android.launcher3.graphics.LauncherPreviewRenderer;
import com.android.launcher3.pm.InstallSessionHelper;
import com.android.launcher3.provider.LauncherDbUtils;
import com.android.launcher3.provider.LauncherDbUtils.SQLiteTransaction;
@@ -69,6 +72,7 @@ public class GridSizeMigrationTask {
private final SparseArray<ContentValues> mUpdateOperations = new SparseArray<>();
private final HashSet<String> mValidPackages;
private final String mTableName;
private final int mSrcX, mSrcY;
private final int mTrgX, mTrgY;
@@ -78,10 +82,12 @@ public class GridSizeMigrationTask {
private final int mDestHotseatSize;
protected GridSizeMigrationTask(Context context, SQLiteDatabase db,
HashSet<String> validPackages, Point sourceSize, Point targetSize) {
HashSet<String> validPackages, boolean usePreviewTable, Point sourceSize,
Point targetSize) {
mContext = context;
mDb = db;
mValidPackages = validPackages;
mTableName = usePreviewTable ? Favorites.PREVIEW_TABLE_NAME : Favorites.TABLE_NAME;
mSrcX = sourceSize.x;
mSrcY = sourceSize.y;
@@ -97,10 +103,12 @@ public class GridSizeMigrationTask {
}
protected GridSizeMigrationTask(Context context, SQLiteDatabase db,
HashSet<String> validPackages, int srcHotseatSize, int destHotseatSize) {
HashSet<String> validPackages, boolean usePreviewTable, int srcHotseatSize,
int destHotseatSize) {
mContext = context;
mDb = db;
mValidPackages = validPackages;
mTableName = usePreviewTable ? Favorites.PREVIEW_TABLE_NAME : Favorites.TABLE_NAME;
mSrcHotseatSize = srcHotseatSize;
@@ -120,7 +128,7 @@ public class GridSizeMigrationTask {
// Update items
int updateCount = mUpdateOperations.size();
for (int i = 0; i < updateCount; i++) {
mDb.update(Favorites.TABLE_NAME, mUpdateOperations.valueAt(i),
mDb.update(mTableName, mUpdateOperations.valueAt(i),
"_id=" + mUpdateOperations.keyAt(i), null);
}
@@ -128,8 +136,8 @@ public class GridSizeMigrationTask {
if (DEBUG) {
Log.d(TAG, "Removing items: " + mEntryToRemove.toConcatString());
}
mDb.delete(Favorites.TABLE_NAME, Utilities.createDbSelectionQuery(
Favorites._ID, mEntryToRemove), null);
mDb.delete(mTableName, Utilities.createDbSelectionQuery(Favorites._ID, mEntryToRemove),
null);
}
return updateCount > 0 || !mEntryToRemove.isEmpty();
@@ -182,8 +190,8 @@ public class GridSizeMigrationTask {
}
@VisibleForTesting
static IntArray getWorkspaceScreenIds(SQLiteDatabase db) {
return LauncherDbUtils.queryIntArray(db, Favorites.TABLE_NAME, Favorites.SCREEN,
static IntArray getWorkspaceScreenIds(SQLiteDatabase db, String tableName) {
return LauncherDbUtils.queryIntArray(db, tableName, Favorites.SCREEN,
Favorites.CONTAINER + " = " + Favorites.CONTAINER_DESKTOP,
Favorites.SCREEN, Favorites.SCREEN);
}
@@ -192,7 +200,7 @@ public class GridSizeMigrationTask {
* @return true if any DB change was made
*/
protected boolean migrateWorkspace() throws Exception {
IntArray allScreens = getWorkspaceScreenIds(mDb);
IntArray allScreens = getWorkspaceScreenIds(mDb, mTableName);
if (allScreens.isEmpty()) {
throw new Exception("Unable to get workspace screens");
}
@@ -244,12 +252,12 @@ public class GridSizeMigrationTask {
/**
* Migrate a particular screen id.
* Strategy:
* 1) For all possible combinations of row and column, pick the one which causes the least
* data loss: {@link #tryRemove(int, int, int, ArrayList, float[])}
* 2) Maintain a list of all lost items before this screen, and add any new item lost from
* this screen to that list as well.
* 3) If all those items from the above list can be placed on this screen, place them
* (otherwise they are placed on a new screen).
* 1) For all possible combinations of row and column, pick the one which causes the least
* data loss: {@link #tryRemove(int, int, int, ArrayList, float[])}
* 2) Maintain a list of all lost items before this screen, and add any new item lost from
* this screen to that list as well.
* 3) If all those items from the above list can be placed on this screen, place them
* (otherwise they are placed on a new screen).
*/
protected void migrateScreen(int screenId) {
// If we are migrating the first screen, do not touch the first row.
@@ -362,9 +370,9 @@ public class GridSizeMigrationTask {
/**
* Tries the remove the provided row and column.
*
* @param items all the items on the screen under operation
* @param items all the items on the screen under operation
* @param outLoss array of size 2. The first entry is filled with weight loss, and the second
* with the overall item movement.
* with the overall item movement.
*/
private ArrayList<DbEntry> tryRemove(int col, int row, int startY,
ArrayList<DbEntry> items, float[] outLoss) {
@@ -379,13 +387,13 @@ public class GridSizeMigrationTask {
for (DbEntry item : items) {
if ((item.cellX <= col && (item.spanX + item.cellX) > col)
|| (item.cellY <= row && (item.spanY + item.cellY) > row)) {
|| (item.cellY <= row && (item.spanY + item.cellY) > row)) {
removedItems.add(item);
if (item.cellX >= col) item.cellX --;
if (item.cellY >= row) item.cellY --;
if (item.cellX >= col) item.cellX--;
if (item.cellY >= row) item.cellY--;
} else {
if (item.cellX > col) item.cellX --;
if (item.cellY > row) item.cellY --;
if (item.cellX > col) item.cellX--;
if (item.cellY > row) item.cellY--;
finalItems.add(item);
occupied.markCells(item, true);
}
@@ -438,9 +446,9 @@ public class GridSizeMigrationTask {
/**
* Recursively finds a placement for the provided items.
*
* @param index the position in {@link #itemsToPlace} to start looking at.
* @param weightLoss total weight loss upto this point
* @param moveCost total move cost upto this point
* @param index the position in {@link #itemsToPlace} to start looking at.
* @param weightLoss total weight loss upto this point
* @param moveCost total move cost upto this point
* @param itemsPlaced all the items already placed upto this point
*/
public void find(int index, float weightLoss, float moveCost,
@@ -481,11 +489,11 @@ public class GridSizeMigrationTask {
float newMoveCost = moveCost;
if (x != myX) {
me.cellX = x;
newMoveCost ++;
newMoveCost++;
}
if (y != myY) {
me.cellY = y;
newMoveCost ++;
newMoveCost++;
}
if (ignoreMove) {
newMoveCost = moveCost;
@@ -500,35 +508,35 @@ public class GridSizeMigrationTask {
// Try resizing horizontally
if (myW > me.minSpanX && occupied.isRegionVacant(x, y, myW - 1, myH)) {
me.spanX --;
me.spanX--;
occupied.markCells(me, true);
// 1 extra move cost
find(index + 1, weightLoss, newMoveCost + 1, itemsIncludingMe);
occupied.markCells(me, false);
me.spanX ++;
me.spanX++;
}
// Try resizing vertically
if (myH > me.minSpanY && occupied.isRegionVacant(x, y, myW, myH - 1)) {
me.spanY --;
me.spanY--;
occupied.markCells(me, true);
// 1 extra move cost
find(index + 1, weightLoss, newMoveCost + 1, itemsIncludingMe);
occupied.markCells(me, false);
me.spanY ++;
me.spanY++;
}
// Try resizing horizontally & vertically
if (myH > me.minSpanY && myW > me.minSpanX &&
occupied.isRegionVacant(x, y, myW - 1, myH - 1)) {
me.spanX --;
me.spanY --;
me.spanX--;
me.spanY--;
occupied.markCells(me, true);
// 2 extra move cost
find(index + 1, weightLoss, newMoveCost + 2, itemsIncludingMe);
occupied.markCells(me, false);
me.spanX ++;
me.spanY ++;
me.spanX++;
me.spanY++;
}
me.cellX = myX;
me.cellY = myY;
@@ -565,11 +573,11 @@ public class GridSizeMigrationTask {
float newMoveCost = moveCost;
if (newX != myX) {
me.cellX = newX;
newMoveCost ++;
newMoveCost++;
}
if (newY != myY) {
me.cellY = newY;
newMoveCost ++;
newMoveCost++;
}
if (ignoreMove) {
newMoveCost = moveCost;
@@ -602,7 +610,7 @@ public class GridSizeMigrationTask {
}
private ArrayList<DbEntry> loadHotseatEntries() {
Cursor c = queryWorkspace(
Cursor c = queryWorkspace(
new String[]{
Favorites._ID, // 0
Favorites.ITEM_TYPE, // 1
@@ -787,7 +795,7 @@ public class GridSizeMigrationTask {
}
protected Cursor queryWorkspace(String[] columns, String where) {
return mDb.query(Favorites.TABLE_NAME, columns, where, null, null, null, null);
return mDb.query(mTableName, columns, where, null, null, null, null);
}
/**
@@ -879,24 +887,44 @@ public class GridSizeMigrationTask {
}
/**
* Migrates the workspace and hotseat in case their sizes changed.
* Check given a new IDP, if migration is necessary.
*/
public static boolean needsToMigrate(Context context, InvariantDeviceProfile idp) {
SharedPreferences prefs = Utilities.getPrefs(context);
String gridSizeString = getPointString(idp.numColumns, idp.numRows);
return !gridSizeString.equals(prefs.getString(KEY_MIGRATION_SRC_WORKSPACE_SIZE, ""))
|| idp.numHotseatIcons != prefs.getInt(KEY_MIGRATION_SRC_HOTSEAT_COUNT,
idp.numHotseatIcons);
}
/** See {@link #migrateGridIfNeeded(Context, InvariantDeviceProfile)} */
public static boolean migrateGridIfNeeded(Context context) {
if (context instanceof LauncherPreviewRenderer.PreviewContext) {
return true;
}
return migrateGridIfNeeded(context, null);
}
/**
* Run the migration algorithm if needed. For preview, we provide the intended idp because it
* has not been changed. If idp is null, we read it from the context, for actual grid migration.
*
* @return false if the migration failed.
*/
public static boolean migrateGridIfNeeded(Context context) {
SharedPreferences prefs = Utilities.getPrefs(context);
InvariantDeviceProfile idp = LauncherAppState.getIDP(context);
public static boolean migrateGridIfNeeded(Context context, InvariantDeviceProfile idp) {
boolean migrateForPreview = idp != null;
if (!migrateForPreview) {
idp = LauncherAppState.getIDP(context);
}
String gridSizeString = getPointString(idp.numColumns, idp.numRows);
if (gridSizeString.equals(prefs.getString(KEY_MIGRATION_SRC_WORKSPACE_SIZE, "")) &&
idp.numHotseatIcons == prefs.getInt(KEY_MIGRATION_SRC_HOTSEAT_COUNT,
idp.numHotseatIcons)) {
// Skip if workspace and hotseat sizes have not changed.
if (!needsToMigrate(context, idp)) {
return true;
}
long migrationStartTime = System.currentTimeMillis();
SharedPreferences prefs = Utilities.getPrefs(context);
String gridSizeString = getPointString(idp.numColumns, idp.numRows);
long migrationStartTime = SystemClock.elapsedRealtime();
try (SQLiteTransaction transaction = (SQLiteTransaction) Settings.call(
context.getContentResolver(), Settings.METHOD_NEW_TRANSACTION)
.getBinder(Settings.EXTRA_VALUE)) {
@@ -907,33 +935,39 @@ public class GridSizeMigrationTask {
KEY_MIGRATION_SRC_WORKSPACE_SIZE, gridSizeString));
boolean dbChanged = false;
if (migrateForPreview) {
copyTable(transaction.getDb(), Favorites.TABLE_NAME, Favorites.PREVIEW_TABLE_NAME,
context);
}
GridBackupTable backupTable = new GridBackupTable(context, transaction.getDb(),
srcHotseatCount, sourceSize.x, sourceSize.y);
if (backupTable.backupOrRestoreAsNeeded()) {
if (migrateForPreview ? backupTable.restoreToPreviewIfBackupExists()
: backupTable.backupOrRestoreAsNeeded()) {
dbChanged = true;
srcHotseatCount = backupTable.getRestoreHotseatAndGridSize(sourceSize);
}
HashSet<String> validPackages = getValidPackages(context);
// Hotseat
// Hotseat.
if (srcHotseatCount != idp.numHotseatIcons) {
// Migrate hotseat.
dbChanged = new GridSizeMigrationTask(context, transaction.getDb(),
validPackages, srcHotseatCount, idp.numHotseatIcons).migrateHotseat();
dbChanged = new GridSizeMigrationTask(context, transaction.getDb(), validPackages,
migrateForPreview, srcHotseatCount, idp.numHotseatIcons).migrateHotseat();
}
// Grid size
Point targetSize = new Point(idp.numColumns, idp.numRows);
if (new MultiStepMigrationTask(validPackages, context, transaction.getDb())
.migrate(sourceSize, targetSize)) {
if (new MultiStepMigrationTask(validPackages, context, transaction.getDb(),
migrateForPreview).migrate(sourceSize, targetSize)) {
dbChanged = true;
}
if (dbChanged) {
// Make sure we haven't removed everything.
final Cursor c = context.getContentResolver().query(
Favorites.CONTENT_URI, null, null, null, null);
migrateForPreview ? Favorites.PREVIEW_CONTENT_URI : Favorites.CONTENT_URI,
null, null, null, null);
boolean hasData = c.moveToNext();
c.close();
if (!hasData) {
@@ -942,21 +976,25 @@ public class GridSizeMigrationTask {
}
transaction.commit();
Settings.call(context.getContentResolver(), Settings.METHOD_REFRESH_BACKUP_TABLE);
if (!migrateForPreview) {
Settings.call(context.getContentResolver(), Settings.METHOD_REFRESH_BACKUP_TABLE);
}
return true;
} catch (Exception e) {
Log.e(TAG, "Error during grid migration", e);
Log.e(TAG, "Error during preview grid migration", e);
return false;
} finally {
Log.v(TAG, "Workspace migration completed in "
+ (System.currentTimeMillis() - migrationStartTime));
Log.v(TAG, "Preview workspace migration completed in "
+ (SystemClock.elapsedRealtime() - migrationStartTime));
// Save current configuration, so that the migration does not run again.
prefs.edit()
.putString(KEY_MIGRATION_SRC_WORKSPACE_SIZE, gridSizeString)
.putInt(KEY_MIGRATION_SRC_HOTSEAT_COUNT, idp.numHotseatIcons)
.apply();
if (!migrateForPreview) {
// Save current configuration, so that the migration does not run again.
prefs.edit()
.putString(KEY_MIGRATION_SRC_WORKSPACE_SIZE, gridSizeString)
.putInt(KEY_MIGRATION_SRC_HOTSEAT_COUNT, idp.numHotseatIcons)
.apply();
}
}
}
@@ -989,7 +1027,7 @@ public class GridSizeMigrationTask {
.getBinder(Settings.EXTRA_VALUE)) {
GridSizeMigrationTask task = new GridSizeMigrationTask(
context, transaction.getDb(), getValidPackages(context),
Integer.MAX_VALUE, Integer.MAX_VALUE);
false /* usePreviewTable */, Integer.MAX_VALUE, Integer.MAX_VALUE);
// Load all the valid entries
ArrayList<DbEntry> items = task.loadHotseatEntries();
@@ -1011,12 +1049,14 @@ public class GridSizeMigrationTask {
private final HashSet<String> mValidPackages;
private final Context mContext;
private final SQLiteDatabase mDb;
private final boolean mUsePreviewTable;
public MultiStepMigrationTask(HashSet<String> validPackages, Context context,
SQLiteDatabase db) {
SQLiteDatabase db, boolean usePreviewTable) {
mValidPackages = validPackages;
mContext = context;
mDb = db;
mUsePreviewTable = usePreviewTable;
}
public boolean migrate(Point sourceSize, Point targetSize) throws Exception {
@@ -1052,8 +1092,8 @@ public class GridSizeMigrationTask {
}
protected boolean runStepTask(Point sourceSize, Point nextSize) throws Exception {
return new GridSizeMigrationTask(mContext, mDb,
mValidPackages, sourceSize, nextSize).migrateWorkspace();
return new GridSizeMigrationTask(mContext, mDb, mValidPackages, mUsePreviewTable,
sourceSize, nextSize).migrateWorkspace();
}
}
}
@@ -18,6 +18,8 @@ package com.android.launcher3.model;
import android.content.Context;
import com.android.launcher3.InvariantDeviceProfile;
/**
* This class takes care of shrinking the workspace (by maximum of one row and one column), as a
* result of restoring from a larger device or device density change.
@@ -28,13 +30,20 @@ public class GridSizeMigrationTaskV2 {
}
/**
* Migrates the workspace and hotseat in case their sizes changed.
*
* @return false if the migration failed.
*/
/** See {@link #migrateGridIfNeeded(Context, InvariantDeviceProfile)} */
public static boolean migrateGridIfNeeded(Context context) {
// To be implemented.
return true;
}
/**
* Run the migration algorithm if needed. For preview, we provide the intended idp because it
* has not been changed. If idp is null, we read it from the context, for actual grid migration.
*
* @return false if the migration failed.
*/
public static boolean migrateGridIfNeeded(Context context, InvariantDeviceProfile idp) {
// To be implemented.
return true;
}
}
@@ -28,6 +28,7 @@ import android.content.pm.LauncherApps;
import android.content.pm.PackageManager;
import android.database.Cursor;
import android.database.CursorWrapper;
import android.net.Uri;
import android.os.UserHandle;
import android.provider.BaseColumns;
import android.text.TextUtils;
@@ -64,6 +65,7 @@ public class LoaderCursor extends CursorWrapper {
public final LongSparseArray<UserHandle> allUsers = new LongSparseArray<>();
private final Uri mContentUri;
private final Context mContext;
private final PackageManager mPM;
private final IconCache mIconCache;
@@ -96,8 +98,10 @@ public class LoaderCursor extends CursorWrapper {
public int itemType;
public int restoreFlag;
public LoaderCursor(Cursor c, LauncherAppState app) {
super(c);
public LoaderCursor(Cursor cursor, Uri contentUri, LauncherAppState app) {
super(cursor);
mContentUri = contentUri;
mContext = app.getContext();
mIconCache = app.getIconCache();
mIDP = app.getInvariantDeviceProfile();
@@ -312,9 +316,8 @@ public class LoaderCursor extends CursorWrapper {
public boolean commitDeleted() {
if (itemsToRemove.size() > 0) {
// Remove dead items
mContext.getContentResolver().delete(LauncherSettings.Favorites.CONTENT_URI,
Utilities.createDbSelectionQuery(
LauncherSettings.Favorites._ID, itemsToRemove), null);
mContext.getContentResolver().delete(mContentUri, Utilities.createDbSelectionQuery(
LauncherSettings.Favorites._ID, itemsToRemove), null);
return true;
}
return false;
@@ -339,7 +342,7 @@ public class LoaderCursor extends CursorWrapper {
// Update restored items that no longer require special handling
ContentValues values = new ContentValues();
values.put(LauncherSettings.Favorites.RESTORED, 0);
mContext.getContentResolver().update(LauncherSettings.Favorites.CONTENT_URI, values,
mContext.getContentResolver().update(mContentUri, values,
Utilities.createDbSelectionQuery(
LauncherSettings.Favorites._ID, restoredRows), null);
}
@@ -36,6 +36,7 @@ import android.content.pm.LauncherApps;
import android.content.pm.PackageInstaller;
import android.content.pm.PackageInstaller.SessionInfo;
import android.content.pm.ShortcutInfo;
import android.net.Uri;
import android.os.UserHandle;
import android.os.UserManager;
import android.text.TextUtils;
@@ -76,7 +77,6 @@ import com.android.launcher3.qsb.QsbContainerView;
import com.android.launcher3.shortcuts.ShortcutKey;
import com.android.launcher3.shortcuts.ShortcutRequest;
import com.android.launcher3.shortcuts.ShortcutRequest.QueryResult;
import com.android.launcher3.testing.TestProtocol;
import com.android.launcher3.util.ComponentKey;
import com.android.launcher3.util.IOUtils;
import com.android.launcher3.util.LooperIdleLock;
@@ -106,7 +106,7 @@ public class LoaderTask implements Runnable {
private final LauncherAppState mApp;
private final AllAppsList mBgAllAppsList;
private final BgDataModel mBgDataModel;
protected final BgDataModel mBgDataModel;
private FirstScreenBroadcast mFirstScreenBroadcast;
@@ -284,6 +284,10 @@ public class LoaderTask implements Runnable {
@VisibleForTesting
void loadWorkspace(List<ShortcutInfo> allDeepShortcuts) {
loadWorkspace(allDeepShortcuts, LauncherSettings.Favorites.CONTENT_URI);
}
protected void loadWorkspace(List<ShortcutInfo> allDeepShortcuts, Uri contentUri) {
final Context context = mApp.getContext();
final ContentResolver contentResolver = context.getContentResolver();
final PackageManagerHelper pmHelper = new PackageManagerHelper(context);
@@ -327,8 +331,8 @@ public class LoaderTask implements Runnable {
mFirstScreenBroadcast = new FirstScreenBroadcast(installingPkgs);
Map<ShortcutKey, ShortcutInfo> shortcutKeyToPinnedShortcuts = new HashMap<>();
final LoaderCursor c = new LoaderCursor(contentResolver.query(
LauncherSettings.Favorites.CONTENT_URI, null, null, null, null), mApp);
final LoaderCursor c = new LoaderCursor(
contentResolver.query(contentUri, null, null, null, null), contentUri, mApp);
Map<ComponentKey, AppWidgetProviderInfo> widgetProvidersMap = null;
@@ -19,6 +19,8 @@ package com.android.launcher3.model;
import com.android.launcher3.ItemInfoWithIcon;
import com.android.launcher3.LauncherSettings;
import java.util.Objects;
/**
* Represents a {@link Package} in the widget tray section.
*/
@@ -48,4 +50,17 @@ public class PackageItemInfo extends ItemInfoWithIcon {
public PackageItemInfo clone() {
return new PackageItemInfo(this);
}
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
PackageItemInfo that = (PackageItemInfo) o;
return Objects.equals(packageName, that.packageName);
}
@Override
public int hashCode() {
return Objects.hash(packageName);
}
}
@@ -32,6 +32,7 @@ import android.graphics.Paint;
import android.graphics.Rect;
import android.graphics.drawable.ShapeDrawable;
import android.util.AttributeSet;
import android.util.Pair;
import android.view.Gravity;
import android.view.LayoutInflater;
import android.view.View;
@@ -358,6 +359,11 @@ public abstract class ArrowPopup<T extends BaseDraggingActivity> extends Abstrac
}
}
@Override
protected Pair<View, String> getAccessibilityTarget() {
return Pair.create(this, "");
}
private void animateOpen() {
setVisibility(View.VISIBLE);
@@ -413,6 +419,8 @@ public abstract class ArrowPopup<T extends BaseDraggingActivity> extends Abstrac
}
if (getOutlineProvider() instanceof RevealOutlineAnimation) {
((RevealOutlineAnimation) getOutlineProvider()).getOutline(mEndRect);
} else {
mEndRect.set(0, 0, getMeasuredWidth(), getMeasuredHeight());
}
if (mOpenCloseAnimator != null) {
mOpenCloseAnimator.cancel();
@@ -37,7 +37,6 @@ import android.os.Build;
import android.os.Handler;
import android.os.Looper;
import android.util.AttributeSet;
import android.util.Pair;
import android.view.MotionEvent;
import android.view.View;
import android.view.ViewGroup;
@@ -329,11 +328,6 @@ public class PopupContainerWithArrow<T extends BaseDraggingActivity> extends Arr
R.string.shortcuts_menu_with_notifications_description);
}
@Override
protected Pair<View, String> getAccessibilityTarget() {
return Pair.create(this, "");
}
@Override
protected void getTargetObjectLocation(Rect outPos) {
getPopupContainer().getDescendantRectRelativeToSelf(mOriginalIcon, outPos);
@@ -22,10 +22,12 @@ import android.database.Cursor;
import android.database.DatabaseUtils;
import android.database.sqlite.SQLiteDatabase;
import android.os.Binder;
import android.os.Process;
import android.util.Log;
import com.android.launcher3.LauncherAppState;
import com.android.launcher3.LauncherSettings.Favorites;
import com.android.launcher3.pm.UserCache;
import com.android.launcher3.util.IntArray;
import java.util.Locale;
@@ -116,6 +118,15 @@ public class LauncherDbUtils {
db.execSQL("DROP TABLE IF EXISTS " + tableName);
}
/** Copy from table to the to table. */
public static void copyTable(SQLiteDatabase db, String from, String to, Context context) {
long userSerial = UserCache.INSTANCE.get(context).getSerialNumberForUser(
Process.myUserHandle());
dropTable(db, to);
Favorites.addTableToDb(db, userSerial, false, to);
db.execSQL("INSERT INTO " + to + " SELECT * FROM " + from);
}
/**
* Utility class to simplify managing sqlite transactions
*/
@@ -18,7 +18,6 @@ package com.android.launcher3.provider;
import android.content.ContentValues;
import android.content.Context;
import android.database.Cursor;
import android.database.sqlite.SQLiteDatabase;
import android.graphics.Point;
@@ -43,7 +42,7 @@ public class LossyScreenMigrationTask extends GridSizeMigrationTask {
protected LossyScreenMigrationTask(
Context context, InvariantDeviceProfile idp, SQLiteDatabase db) {
// Decrease the rows count by 1
super(context, db, getValidPackages(context),
super(context, db, getValidPackages(context), false /* usePreviewTable */,
new Point(idp.numColumns, idp.numRows + 1),
new Point(idp.numColumns, idp.numRows));
@@ -89,6 +89,8 @@ public final class TestProtocol {
public static final String REQUEST_ENABLE_DEBUG_TRACING = "enable-debug-tracing";
public static final String REQUEST_DISABLE_DEBUG_TRACING = "disable-debug-tracing";
public static final String REQUEST_OVERVIEW_ACTIONS_ENABLED = "overview-actions-enabled";
public static final String PERMANENT_DIAG_TAG = "TaplTarget";
public static final String NO_BACKGROUND_TO_OVERVIEW_TAG = "b/138251824";
@@ -22,6 +22,7 @@ import android.os.Looper;
import androidx.annotation.VisibleForTesting;
import com.android.launcher3.graphics.LauncherPreviewRenderer.PreviewContext;
import com.android.launcher3.util.ResourceBasedOverride.Overrides;
import java.util.concurrent.ExecutionException;
@@ -39,6 +40,10 @@ public class MainThreadInitializedObject<T> {
}
public T get(Context context) {
if (context instanceof PreviewContext) {
return ((PreviewContext) context).getObject(this, mProvider);
}
if (mValue == null) {
if (Looper.myLooper() == Looper.getMainLooper()) {
mValue = TraceHelper.whitelistIpcs("main.thread.object",
@@ -15,6 +15,8 @@
*/
package com.android.launcher3.views;
import static android.view.ViewGroup.LayoutParams.MATCH_PARENT;
import static com.android.launcher3.anim.Interpolators.scrollInterpolatorForVelocity;
import android.animation.Animator;
@@ -62,6 +64,7 @@ public abstract class AbstractSlideInView extends AbstractFloatingView
protected final ObjectAnimator mOpenCloseAnimator;
protected View mContent;
private final View mColorScrim;
protected Interpolator mScrollInterpolator;
// range [0, 1], 0=> completely open, 1=> completely closed
@@ -85,11 +88,30 @@ public abstract class AbstractSlideInView extends AbstractFloatingView
announceAccessibilityChanges();
}
});
int scrimColor = getScrimColor(mLauncher);
mColorScrim = scrimColor != -1 ? createColorScrim(mLauncher, scrimColor) : null;
}
protected void attachToContainer() {
if (mColorScrim != null) {
getPopupContainer().addView(mColorScrim);
}
getPopupContainer().addView(this);
}
/**
* Returns a scrim color for a sliding view. if returned value is -1, no scrim is added.
*/
protected int getScrimColor(Context context) {
return -1;
}
protected void setTranslationShift(float translationShift) {
mTranslationShift = translationShift;
mContent.setTranslationY(mTranslationShift * mContent.getHeight());
if (mColorScrim != null) {
mColorScrim.setAlpha(1 - mTranslationShift);
}
}
@Override
@@ -127,7 +149,8 @@ public abstract class AbstractSlideInView extends AbstractFloatingView
/* SingleAxisSwipeDetector.Listener */
@Override
public void onDragStart(boolean start) { }
public void onDragStart(boolean start) {
}
@Override
public boolean onDrag(float displacement) {
@@ -185,9 +208,25 @@ public abstract class AbstractSlideInView extends AbstractFloatingView
protected void onCloseComplete() {
mIsOpen = false;
getPopupContainer().removeView(this);
if (mColorScrim != null) {
getPopupContainer().removeView(mColorScrim);
}
}
protected BaseDragLayer getPopupContainer() {
return mLauncher.getDragLayer();
}
protected static View createColorScrim(Context context, int bgColor) {
View view = new View(context);
view.forceHasOverlappingRendering(false);
view.setBackgroundColor(bgColor);
BaseDragLayer.LayoutParams lp = new BaseDragLayer.LayoutParams(MATCH_PARENT, MATCH_PARENT);
lp.ignoreInsets = true;
view.setLayoutParams(lp);
return view;
}
}
@@ -29,7 +29,6 @@ import android.graphics.Rect;
import android.graphics.RectF;
import android.os.Build;
import android.util.AttributeSet;
import android.util.Log;
import android.util.Property;
import android.view.MotionEvent;
import android.view.View;
@@ -41,8 +40,8 @@ import android.widget.FrameLayout;
import com.android.launcher3.AbstractFloatingView;
import com.android.launcher3.InsettableFrameLayout;
import com.android.launcher3.Launcher;
import com.android.launcher3.Utilities;
import com.android.launcher3.testing.TestProtocol;
import com.android.launcher3.util.MultiValueAlpha;
import com.android.launcher3.util.MultiValueAlpha.AlphaProperty;
import com.android.launcher3.util.TouchController;
@@ -150,10 +149,20 @@ public abstract class BaseDragLayer<T extends Context & ActivityContext>
return findActiveController(ev);
}
private boolean isEventInLauncher(MotionEvent ev) {
final float x = ev.getX();
final float y = ev.getY();
return x >= mSystemGestureRegion.left && x < getWidth() - mSystemGestureRegion.right
&& y >= mSystemGestureRegion.top && y < getHeight() - mSystemGestureRegion.bottom;
}
private TouchController findControllerToHandleTouch(MotionEvent ev) {
AbstractFloatingView topView = AbstractFloatingView.getTopOpenView(mActivity);
if (topView != null && topView.onControllerInterceptTouchEvent(ev)) {
return topView;
if (isEventInLauncher(ev)) {
AbstractFloatingView topView = AbstractFloatingView.getTopOpenView(mActivity);
if (topView != null && topView.onControllerInterceptTouchEvent(ev)) {
return topView;
}
}
for (TouchController controller : mControllers) {
@@ -245,17 +254,12 @@ public abstract class BaseDragLayer<T extends Context & ActivityContext>
public boolean dispatchTouchEvent(MotionEvent ev) {
switch (ev.getAction()) {
case ACTION_DOWN: {
float x = ev.getX();
float y = ev.getY();
mTouchDispatchState |= TOUCH_DISPATCHING_VIEW;
if ((y < mSystemGestureRegion.top
|| x < mSystemGestureRegion.left
|| x > (getWidth() - mSystemGestureRegion.right)
|| y > (getHeight() - mSystemGestureRegion.bottom))) {
mTouchDispatchState |= TOUCH_DISPATCHING_GESTURE;
} else {
if (isEventInLauncher(ev)) {
mTouchDispatchState &= ~TOUCH_DISPATCHING_GESTURE;
} else {
mTouchDispatchState |= TOUCH_DISPATCHING_GESTURE;
}
break;
}
@@ -52,11 +52,15 @@ public class WorkEduView extends AbstractSlideInView implements Insettable {
private static final int WORK_EDU_PERSONAL_APPS = 1;
private static final int WORK_EDU_WORK_APPS = 2;
protected static final int FINAL_SCRIM_BG_COLOR = 0x88000000;
private Rect mInsets = new Rect();
private View mViewWrapper;
private Button mProceedButton;
private TextView mContentText;
private AllAppsPagedView mAllAppsPagedView;
private int mNextWorkEduStep = WORK_EDU_PERSONAL_APPS;
@@ -141,10 +145,15 @@ public class WorkEduView extends AbstractSlideInView implements Insettable {
}
private void show() {
mLauncher.getDragLayer().addView(this);
attachToContainer();
animateOpen();
}
@Override
protected int getScrimColor(Context context) {
return FINAL_SCRIM_BG_COLOR;
}
private void goToFirstPage() {
if (mAllAppsPagedView != null) {
mAllAppsPagedView.snapToPageImmediately(AllAppsContainerView.AdapterHolder.MAIN);
@@ -15,8 +15,6 @@
*/
package com.android.launcher3.widget;
import static android.view.ViewGroup.LayoutParams.MATCH_PARENT;
import static com.android.launcher3.icons.GraphicsUtils.setColorAlphaBound;
import static com.android.launcher3.logging.LoggerUtils.newContainerTarget;
@@ -42,7 +40,6 @@ import com.android.launcher3.userevent.nano.LauncherLogProto.Target;
import com.android.launcher3.util.SystemUiController;
import com.android.launcher3.util.Themes;
import com.android.launcher3.views.AbstractSlideInView;
import com.android.launcher3.views.BaseDragLayer;
/**
* Base class for various widgets popup
@@ -55,11 +52,14 @@ abstract class BaseWidgetSheet extends AbstractSlideInView
/* Touch handling related member variables. */
private Toast mWidgetInstructionToast;
protected final View mColorScrim;
public BaseWidgetSheet(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
mColorScrim = createColorScrim(context);
}
protected int getScrimColor(Context context) {
WallpaperColorInfo colors = WallpaperColorInfo.INSTANCE.get(context);
int alpha = context.getResources().getInteger(R.integer.extracted_color_gradient_alpha);
return setColorAlphaBound(colors.getSecondaryColor(), alpha);
}
@Override
@@ -98,16 +98,6 @@ abstract class BaseWidgetSheet extends AbstractSlideInView
return true;
}
protected void attachToContainer() {
getPopupContainer().addView(mColorScrim);
getPopupContainer().addView(this);
}
protected void setTranslationShift(float translationShift) {
super.setTranslationShift(translationShift);
mColorScrim.setAlpha(1 - mTranslationShift);
}
private boolean beginDraggingWidget(WidgetCell v) {
// Get the widget preview as the drag representation
WidgetImageView image = v.getWidgetView();
@@ -138,7 +128,6 @@ abstract class BaseWidgetSheet extends AbstractSlideInView
protected void onCloseComplete() {
super.onCloseComplete();
getPopupContainer().removeView(mColorScrim);
clearNavBarColor();
}
@@ -177,19 +166,4 @@ abstract class BaseWidgetSheet extends AbstractSlideInView
protected SystemUiController getSystemUiController() {
return mLauncher.getSystemUiController();
}
private static View createColorScrim(Context context) {
View view = new View(context);
view.forceHasOverlappingRendering(false);
WallpaperColorInfo colors = WallpaperColorInfo.INSTANCE.get(context);
int alpha = context.getResources().getInteger(R.integer.extracted_color_gradient_alpha);
view.setBackgroundColor(setColorAlphaBound(colors.getSecondaryColor(), alpha));
BaseDragLayer.LayoutParams lp = new BaseDragLayer.LayoutParams(MATCH_PARENT, MATCH_PARENT);
lp.ignoreInsets = true;
view.setLayoutParams(lp);
return view;
}
}
@@ -6,6 +6,7 @@ import static android.appwidget.AppWidgetProviderInfo.WIDGET_FEATURE_HIDE_FROM_P
import static com.android.launcher3.pm.ShortcutConfigActivityInfo.queryList;
import android.appwidget.AppWidgetProviderInfo;
import android.content.ComponentName;
import android.content.Context;
import android.content.pm.PackageManager;
import android.os.Process;
@@ -243,4 +244,16 @@ public class WidgetsModel {
}
}
}
public WidgetItem getWidgetProviderInfoByProviderName(
ComponentName providerName) {
ArrayList<WidgetItem> widgetsList = mWidgetsList.get(
new PackageItemInfo(providerName.getPackageName()));
for (WidgetItem item : widgetsList) {
if (item.componentName.equals(providerName)) {
return item;
}
}
return null;
}
}
@@ -22,10 +22,9 @@ import static com.android.launcher3.tapl.LauncherInstrumentation.ContainerType;
import static com.android.launcher3.ui.TaplTestsLauncher3.getAppPackageName;
import static com.android.launcher3.util.Executors.MAIN_EXECUTOR;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertTrue;
import static java.lang.System.exit;
import android.content.BroadcastReceiver;
import android.content.ComponentName;
import android.content.ContentResolver;
@@ -38,7 +37,7 @@ import android.content.pm.LauncherApps;
import android.content.pm.PackageManager;
import android.os.Process;
import android.os.RemoteException;
import android.util.Log;
import android.os.StrictMode;
import androidx.test.InstrumentationRegistry;
import androidx.test.uiautomator.By;
@@ -97,11 +96,50 @@ public abstract class AbstractLauncherUiTest {
public static final long DEFAULT_UI_TIMEOUT = 10000;
private static final String TAG = "AbstractLauncherUiTest";
private static String sDetectedActivityLeak;
private static boolean sActivityLeakReported;
protected LooperExecutor mMainThreadExecutor = MAIN_EXECUTOR;
protected final UiDevice mDevice = UiDevice.getInstance(getInstrumentation());
protected final LauncherInstrumentation mLauncher = new LauncherInstrumentation();
protected Context mTargetContext;
protected String mTargetPackage;
private int mLauncherPid;
static {
if (TestHelpers.isInLauncherProcess()) {
StrictMode.VmPolicy.Builder builder =
new StrictMode.VmPolicy.Builder()
.detectActivityLeaks()
.penaltyLog()
.penaltyListener(Runnable::run, violation -> {
// Runs in the main thread. We can't dumpheap in the main thread,
// so let's just mark the fact that the leak has happened.
if (sDetectedActivityLeak == null) {
sDetectedActivityLeak = violation.toString();
}
});
StrictMode.setVmPolicy(builder.build());
}
}
public static void checkDetectedLeaks() {
if (sDetectedActivityLeak != null && !sActivityLeakReported) {
sActivityLeakReported = true;
final UiDevice device = UiDevice.getInstance(getInstrumentation());
try {
device.executeShellCommand(
"am dumpheap "
+ device.getLauncherPackageName()
+ " "
+ getInstrumentation().getTargetContext().getFilesDir().getPath()
+ "/ActivityLeakHeapDump.hprof");
} catch (IOException e) {
throw new RuntimeException(e);
}
}
}
protected AbstractLauncherUiTest() {
mLauncher.enableCheckEventsForSuccessfulGestures();
@@ -121,6 +159,8 @@ public abstract class AbstractLauncherUiTest {
checkLauncherIntegrity(launcher, containerType)));
}
mLauncher.enableDebugTracing();
// Avoid double-reporting of Launcher crashes.
mLauncher.setOnLauncherCrashed(() -> mLauncherPid = 0);
}
protected final LauncherActivityRule mActivityMonitor = new LauncherActivityRule();
@@ -158,7 +198,7 @@ public abstract class AbstractLauncherUiTest {
return TestHelpers.isInLauncherProcess()
? RuleChain.outerRule(ShellCommandRule.setDefaultLauncher())
.around(inner) :
.around(inner) :
inner;
}
@@ -175,17 +215,23 @@ public abstract class AbstractLauncherUiTest {
@Before
public void setUp() throws Exception {
mLauncherPid = 0;
// Disable app tracker
AppLaunchTracker.INSTANCE.initializeForTesting(new AppLaunchTracker());
mTargetContext = InstrumentationRegistry.getTargetContext();
mTargetPackage = mTargetContext.getPackageName();
mLauncherPid = mLauncher.getPid();
}
@After
public void verifyLauncherState() {
// Limits UI tests affecting tests running after them.
mLauncher.waitForLauncherInitialized();
if (mLauncherPid != 0) {
assertEquals("Launcher crashed, pid mismatch:", mLauncherPid, mLauncher.getPid());
}
checkDetectedLeaks();
}
protected void clearLauncherData() throws IOException, InterruptedException {
@@ -196,6 +242,7 @@ public abstract class AbstractLauncherUiTest {
} else {
clearPackageData(mDevice.getLauncherPackageName());
mLauncher.enableDebugTracing();
mLauncherPid = mLauncher.getPid();
}
}
@@ -56,6 +56,7 @@ class PortraitLandscapeRunner implements TestRule {
private void evaluateInPortrait() throws Throwable {
mTest.mDevice.setOrientationNatural();
mTest.mLauncher.setExpectedRotation(Surface.ROTATION_0);
AbstractLauncherUiTest.checkDetectedLeaks();
base.evaluate();
mTest.getDevice().pressHome();
}
@@ -63,6 +64,7 @@ class PortraitLandscapeRunner implements TestRule {
private void evaluateInLandscape() throws Throwable {
mTest.mDevice.setOrientationLeft();
mTest.mLauncher.setExpectedRotation(Surface.ROTATION_90);
AbstractLauncherUiTest.checkDetectedLeaks();
base.evaluate();
mTest.getDevice().pressHome();
}
@@ -40,9 +40,7 @@ import com.android.launcher3.views.OptionsPopupView;
import com.android.launcher3.widget.WidgetsFullSheet;
import com.android.launcher3.widget.WidgetsRecyclerView;
import org.junit.After;
import org.junit.Before;
import org.junit.Ignore;
import org.junit.Test;
import org.junit.runner.RunWith;
@@ -51,21 +49,10 @@ import org.junit.runner.RunWith;
public class TaplTestsLauncher3 extends AbstractLauncherUiTest {
private static final String APP_NAME = "LauncherTestApp";
private int mLauncherPid;
@Before
public void setUp() throws Exception {
mLauncherPid = 0;
super.setUp();
initialize(this);
mLauncherPid = mLauncher.getPid();
}
@After
public void teardown() {
if (mLauncherPid != 0) {
assertEquals("Launcher crashed, pid mismatch:", mLauncherPid, mLauncher.getPid());
}
}
public static void initialize(AbstractLauncherUiTest test) throws Exception {
@@ -77,6 +64,7 @@ public class TaplTestsLauncher3 extends AbstractLauncherUiTest {
test.waitForResumed("Launcher internal state is still Background");
// Check that we switched to home.
test.mLauncher.getWorkspace();
AbstractLauncherUiTest.checkDetectedLeaks();
}
// Please don't add negative test cases for methods that fail only after a long wait.
@@ -122,7 +110,6 @@ public class TaplTestsLauncher3 extends AbstractLauncherUiTest {
}
@Test
@Ignore
public void testPressHomeOnAllAppsContextMenu() throws Exception {
final AllApps allApps = mLauncher.getWorkspace().switchToAllApps();
allApps.freeze();
@@ -73,11 +73,10 @@ public class WorkTabTest extends AbstractLauncherUiTest {
mDevice.pressHome();
waitForLauncherCondition("Launcher didn't start", Objects::nonNull);
executeOnLauncher(launcher -> launcher.getStateManager().goToState(ALL_APPS));
waitForLauncherCondition("Personal tab is missing",
launcher -> launcher.getAppsView().isPersonalTabVisible());
launcher -> launcher.getAppsView().isPersonalTabVisible(), 60000);
waitForLauncherCondition("Work tab is missing",
launcher -> launcher.getAppsView().isWorkTabVisible());
launcher -> launcher.getAppsView().isWorkTabVisible(), 60000);
}
@Test
@@ -89,7 +88,7 @@ public class WorkTabTest extends AbstractLauncherUiTest {
executeOnLauncher(launcher -> launcher.getStateManager().goToState(ALL_APPS));
waitForState("Launcher internal state didn't switch to All Apps", () -> ALL_APPS);
getOnceNotNull("Apps view did not bind",
launcher -> launcher.getAppsView().getWorkFooterContainer());
launcher -> launcher.getAppsView().getWorkFooterContainer(), 60000);
UserManager userManager = getFromLauncher(l -> l.getSystemService(UserManager.class));
assertEquals(2, userManager.getUserProfiles().size());
@@ -102,10 +102,15 @@ class FailureInvestigator {
logSinceBoot =
UiDevice.getInstance(getInstrumentation())
.executeShellCommand("logcat -d -t " + systemBootTime.replace(" ", ""));
} catch (IOException e) {
} catch (IOException | OutOfMemoryError e) {
return 0;
}
if (matches("android\\:\\:uirenderer\\:\\:renderthread\\:\\:EglManager\\:\\:swapBuffers",
logSinceBoot)) {
return 148529608;
}
for (ExceptionMatch exceptionMatch : EXCEPTION_MATCHES) {
if (matches(exceptionMatch.exceptionPattern, exception)) {
for (LogcatMatch logcatMatch : exceptionMatch.logcatMatches) {
@@ -69,22 +69,16 @@ import org.junit.Assert;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.lang.ref.WeakReference;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.Date;
import java.util.Deque;
import java.util.HashMap;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.concurrent.TimeoutException;
import java.util.function.Consumer;
import java.util.function.Function;
import java.util.function.Supplier;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import java.util.stream.Collectors;
@@ -97,16 +91,8 @@ public final class LauncherInstrumentation {
private static final String TAG = "Tapl";
private static final int ZERO_BUTTON_STEPS_FROM_BACKGROUND_TO_HOME = 20;
private static final int GESTURE_STEP_MS = 16;
private static final SimpleDateFormat DATE_TIME_FORMAT =
new SimpleDateFormat("yyyy-MM-dd HH:mm:ss.SSS");
private static long START_TIME = System.currentTimeMillis();
static final Pattern EVENT_LOG_ENTRY = Pattern.compile(
"(?<time>[0-9][0-9][0-9][0-9]-[0-9][0-9]-[0-9][0-9] "
+ "[0-9][0-9]:[0-9][0-9]:[0-9][0-9]\\.[0-9][0-9][0-9])"
+ ".*" + TestProtocol.TAPL_EVENTS_TAG + ": (?<sequence>[a-zA-Z]+) / "
+ "(?<event>.*)");
private static final Pattern EVENT_TOUCH_DOWN = getTouchEventPattern("ACTION_DOWN");
private static final Pattern EVENT_TOUCH_UP = getTouchEventPattern("ACTION_UP");
private static final Pattern EVENT_TOUCH_CANCEL = getTouchEventPattern("ACTION_CANCEL");
@@ -159,7 +145,7 @@ public final class LauncherInstrumentation {
private static final String WORKSPACE_RES_ID = "workspace";
private static final String APPS_RES_ID = "apps_view";
private static final String OVERVIEW_RES_ID = "overview_panel";
private static final String OVERVIEW_RES_ID = "overview_panel_recents";
private static final String WIDGETS_RES_ID = "widgets_list_view";
private static final String CONTEXT_MENU_RES_ID = "deep_shortcuts_container";
public static final int WAIT_TIME_MS = 10000;
@@ -176,12 +162,13 @@ public final class LauncherInstrumentation {
private Consumer<ContainerType> mOnSettledStateAction;
// Map from an event sequence name to an ordered list of expected events in that sequence.
// Not null when we are collecting expected events to compare with actual ones.
private Map<String, List<Pattern>> mExpectedEvents;
private static LogEventChecker sEventChecker;
// True if there is an gesture in progress that needs event verification.
private static boolean sCheckingEvents;
private Date mStartRecordingTime;
private boolean mCheckEventsForSuccessfulGestures = false;
private int mExpectedPid;
private Runnable mOnLauncherCrashed;
private static Pattern getTouchEventPattern(String prefix, String action) {
// The pattern includes sanity checks that we don't get a multi-touch events or other
@@ -264,6 +251,10 @@ public final class LauncherInstrumentation {
mCheckEventsForSuccessfulGestures = true;
}
public void setOnLauncherCrashed(Runnable onLauncherCrashed) {
mOnLauncherCrashed = onLauncherCrashed;
}
Context getContext() {
return mInstrumentation.getContext();
}
@@ -339,7 +330,7 @@ public final class LauncherInstrumentation {
}
}
private String getAnomalyMessage() {
private String getSystemAnomalyMessage() {
UiObject2 object = mDevice.findObject(By.res("android", "alertTitle"));
if (object != null) {
return "System alert popup is visible: " + object.getText();
@@ -358,15 +349,33 @@ public final class LauncherInstrumentation {
return null;
}
private String getAnomalyMessage() {
if (mExpectedPid != 0 && mExpectedPid != getPid()) {
mExpectedPid = 0;
if (mOnLauncherCrashed != null) mOnLauncherCrashed.run();
return "Launcher crashed";
}
final String systemAnomalyMessage = getSystemAnomalyMessage();
if (systemAnomalyMessage != null) {
return "http://go/tapl : Tests are broken by a non-Launcher system error: "
+ systemAnomalyMessage;
}
return null;
}
public void checkForAnomaly() {
final String anomalyMessage = getAnomalyMessage();
if (anomalyMessage != null) {
String message = "http://go/tapl : Tests are broken by a non-Launcher system error: "
+ anomalyMessage;
log("Hierarchy dump for: " + message);
if (sCheckingEvents) {
sCheckingEvents = false;
sEventChecker.finishNoWait();
}
log("Hierarchy dump for: " + anomalyMessage);
dumpViewHierarchy();
Assert.fail(formatSystemHealthMessage(message));
Assert.fail(formatSystemHealthMessage(anomalyMessage));
}
}
@@ -434,10 +443,12 @@ public final class LauncherInstrumentation {
log("Hierarchy dump for: " + message);
dumpViewHierarchy();
final String eventMismatch = getEventMismatchMessage(false);
if (eventMismatch != null) {
message = message + ", having produced " + eventMismatch;
if (sCheckingEvents) {
sCheckingEvents = false;
final String eventMismatch = sEventChecker.verify(0);
if (eventMismatch != null) {
message = message + ", having produced " + eventMismatch;
}
}
Assert.fail(formatSystemHealthMessage(message));
@@ -554,7 +565,7 @@ public final class LauncherInstrumentation {
return waitForLauncherObject(APPS_RES_ID);
}
case OVERVIEW: {
if (mDevice.isNaturalOrientation()) {
if (hasAllAppsInOverview()) {
waitForLauncherObject(APPS_RES_ID);
} else {
waitUntilGone(APPS_RES_ID);
@@ -662,14 +673,6 @@ public final class LauncherInstrumentation {
try (LauncherInstrumentation.Closable c = addContextLayer(action)) {
mDevice.waitForIdle();
if (getNavigationModel() == NavigationModel.TWO_BUTTON) {
if (hasLauncherObject(CONTEXT_MENU_RES_ID) ||
hasLauncherObject(WIDGETS_RES_ID)
&& !mDevice.isNaturalOrientation()) {
expectEvent(TestProtocol.SEQUENCE_MAIN, EVENT_PILFER_POINTERS);
}
}
if (getNavigationModel() == NavigationModel.TWO_BUTTON) {
expectEvent(TestProtocol.SEQUENCE_TIS, EVENT_TOUCH_DOWN_TIS);
expectEvent(TestProtocol.SEQUENCE_TIS, EVENT_TOUCH_UP_TIS);
@@ -1210,6 +1213,28 @@ public final class LauncherInstrumentation {
getTestInfo(TestProtocol.REQUEST_ENABLE_DEBUG_TRACING);
}
public boolean hasAllAppsInOverview() {
// Vertical bar layouts don't contain all apps
if (!mDevice.isNaturalOrientation()) {
return false;
}
// Portrait two button (quickstep) always has all apps.
if (getNavigationModel() == NavigationModel.TWO_BUTTON) {
return true;
}
// Overview actions hide all apps
if (overviewActionsEnabled()) {
return false;
}
// ...otherwise there should be all apps
return true;
}
private boolean overviewActionsEnabled() {
return getTestInfo(TestProtocol.REQUEST_OVERVIEW_ACTIONS_ENABLED).getBoolean(
TestProtocol.TEST_INFO_RESPONSE_FIELD);
}
public void disableDebugTracing() {
getTestInfo(TestProtocol.REQUEST_DISABLE_DEBUG_TRACING);
}
@@ -1245,204 +1270,37 @@ public final class LauncherInstrumentation {
return tasks;
}
// Returns actual events retrieved from logcat. The return value's key set is the set of all
// sequence names that actually had at least one event, and the values are lists of events in
// the given sequence, in the order they were recorded.
private Map<String, List<String>> getEvents() {
final Map<String, List<String>> events = new HashMap<>();
try {
// Logcat may skip events after the specified time. Querying for events starting 1 min
// earlier.
final Date startTime = new Date(mStartRecordingTime.getTime() - 60000);
final String logcatCommand = "logcat -d -v year --pid=" + getPid() + " -t "
+ DATE_TIME_FORMAT.format(startTime).replaceAll(" ", "")
+ " -s " + TestProtocol.TAPL_EVENTS_TAG;
log("Events query command: " + logcatCommand);
final String logcatEvents = mDevice.executeShellCommand(logcatCommand);
final Matcher matcher = EVENT_LOG_ENTRY.matcher(logcatEvents);
while (matcher.find()) {
// Skip events before recording start time.
if (DATE_TIME_FORMAT.parse(matcher.group("time"))
.compareTo(mStartRecordingTime) < 0) {
continue;
}
eventsListForSequence(matcher.group("sequence"), events).add(
matcher.group("event"));
}
return events;
} catch (IOException e) {
throw new RuntimeException(e);
} catch (ParseException e) {
throw new AssertionError(e);
}
}
// Returns an event list for a given sequence, adding it to the map as needed.
private static <T> List<T> eventsListForSequence(
String sequenceName, Map<String, List<T>> events) {
List<T> eventSequence = events.get(sequenceName);
if (eventSequence == null) {
eventSequence = new ArrayList<>();
events.put(sequenceName, eventSequence);
}
return eventSequence;
}
private void startRecordingEvents() {
Assert.assertTrue("Already recording events", mExpectedEvents == null);
mExpectedEvents = new HashMap<>();
mStartRecordingTime = new Date();
log("startRecordingEvents: " + DATE_TIME_FORMAT.format(mStartRecordingTime));
}
private void stopRecordingEvents() {
mExpectedEvents = null;
mStartRecordingTime = null;
}
public Closable eventsCheck() {
Assert.assertTrue("Nested event checking", !sCheckingEvents);
if ("com.android.launcher3".equals(getLauncherPackageName())) {
// Not checking specific Launcher3 event sequences.
return () -> {
};
}
// Entering events check block.
startRecordingEvents();
sCheckingEvents = true;
mExpectedPid = getPid();
if (sEventChecker == null) sEventChecker = new LogEventChecker();
sEventChecker.start();
return () -> {
// Leaving events check block.
if (mExpectedEvents == null) {
return; // There was a failure. Noo need to report another one.
}
checkForAnomaly();
if (!mCheckEventsForSuccessfulGestures) {
stopRecordingEvents();
return;
}
final String message = getEventMismatchMessage(true);
if (message != null) {
Assert.fail(formatSystemHealthMessage(
"http://go/tapl : successful gesture produced " + message));
if (sCheckingEvents) {
sCheckingEvents = false;
if (mCheckEventsForSuccessfulGestures) {
final String message = sEventChecker.verify(WAIT_TIME_MS);
if (message != null) {
Assert.fail(formatSystemHealthMessage(
"http://go/tapl : successful gesture produced " + message));
}
} else {
sEventChecker.finishNoWait();
}
}
};
}
void expectEvent(String sequence, Pattern expected) {
if (mExpectedEvents != null) {
eventsListForSequence(sequence, mExpectedEvents).add(expected);
}
}
// Returns non-null error message if the actual events in logcat don't match expected events.
// If we are not checking events, returns null.
private String getEventMismatchMessage(boolean waitForExpectedCount) {
if (mExpectedEvents == null) return null;
try {
Map<String, List<String>> actual = getEvents();
if (waitForExpectedCount) {
// Wait until Launcher generates the expected number of events.
final long endTime = SystemClock.uptimeMillis() + WAIT_TIME_MS;
while (SystemClock.uptimeMillis() < endTime
&& !receivedEnoughEvents(actual)) {
SystemClock.sleep(100);
actual = getEvents();
}
}
return getEventMismatchErrorMessage(actual);
} finally {
stopRecordingEvents();
}
}
// Returns whether there is a sufficient number of events in the logcat to match the expected
// events.
private boolean receivedEnoughEvents(Map<String, List<String>> actual) {
for (Map.Entry<String, List<Pattern>> expectedNamedSequence : mExpectedEvents.entrySet()) {
final List<String> actualEventSequence = actual.get(expectedNamedSequence.getKey());
if (actualEventSequence == null
|| actualEventSequence.size() < expectedNamedSequence.getValue().size()) {
return false;
}
}
return true;
}
// If the list of actual events matches the list of expected events, returns -1, otherwise
// the position of the mismatch.
private static int getMismatchPosition(List<Pattern> expected, List<String> actual) {
for (int i = 0; i < expected.size(); ++i) {
if (i >= actual.size()
|| !expected.get(i).matcher(actual.get(i)).find()) {
return i;
}
}
if (actual.size() > expected.size()) return expected.size();
return -1;
}
// Returns non-null error message if the actual events passed as a param don't match expected
// events.
private String getEventMismatchErrorMessage(Map<String, List<String>> actualEvents) {
final StringBuilder sb = new StringBuilder();
// Check that all expected even sequences match the actual data.
for (Map.Entry<String, List<Pattern>> expectedNamedSequence : mExpectedEvents.entrySet()) {
List<String> actualEventSequence = actualEvents.get(expectedNamedSequence.getKey());
if (actualEventSequence == null) actualEventSequence = new ArrayList<>();
final int mismatchPosition = getMismatchPosition(
expectedNamedSequence.getValue(), actualEventSequence);
if (mismatchPosition != -1) {
formatSequenceWithMismatch(
sb,
expectedNamedSequence.getKey(),
expectedNamedSequence.getValue(),
actualEventSequence,
mismatchPosition);
}
}
// Check for unexpected event sequences in the actual data.
for (Map.Entry<String, List<String>> actualNamedSequence : actualEvents.entrySet()) {
if (!mExpectedEvents.containsKey(actualNamedSequence.getKey())) {
formatSequenceWithMismatch(
sb,
actualNamedSequence.getKey(),
new ArrayList<>(),
actualNamedSequence.getValue(),
0);
}
}
return sb.length() != 0 ? "mismatching events: " + sb.toString() : null;
}
private static void formatSequenceWithMismatch(
StringBuilder sb,
String sequenceName,
List<Pattern> expected,
List<String> actualEvents,
int mismatchPosition) {
sb.append("\n>> Sequence " + sequenceName);
sb.append("\n Expected:");
formatEventListWithMismatch(sb, expected, mismatchPosition);
sb.append("\n Actual:");
formatEventListWithMismatch(sb, actualEvents, mismatchPosition);
}
private static void formatEventListWithMismatch(StringBuilder sb, List events, int position) {
for (int i = 0; i < events.size(); ++i) {
sb.append("\n | ");
sb.append(i == position ? "---> " : " ");
sb.append(events.get(i).toString());
}
if (position == events.size()) sb.append("\n | ---> (end)");
if (sCheckingEvents) sEventChecker.expectPattern(sequence, expected);
}
}
@@ -0,0 +1,233 @@
/*
* 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.tapl;
import static java.util.concurrent.TimeUnit.MILLISECONDS;
import android.util.Log;
import com.android.launcher3.testing.TestProtocol;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.UUID;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.Semaphore;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
/**
* Utility class to read log on a background thread.
*/
public class LogEventChecker {
private static final Pattern EVENT_LOG_ENTRY = Pattern.compile(
".*" + TestProtocol.TAPL_EVENTS_TAG + ": (?<sequence>[a-zA-Z]+) / (?<event>.*)");
private static final String START_PREFIX = "START_READER ";
private static final String FINISH_PREFIX = "FINISH_READER ";
private volatile CountDownLatch mFinished;
// Map from an event sequence name to an ordered list of expected events in that sequence.
private final ListMap<Pattern> mExpectedEvents = new ListMap<>();
private final ListMap<String> mEvents = new ListMap<>();
private final Semaphore mEventsCounter = new Semaphore(0);
private volatile String mStartCommand;
private volatile String mFinishCommand;
LogEventChecker() {
final Thread thread = new Thread(this::onRun, "log-reader-thread");
thread.setPriority(Thread.NORM_PRIORITY);
thread.start();
}
void start() {
if (mFinished != null) {
try {
mFinished.await();
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
mFinished = null;
}
mEvents.clear();
mExpectedEvents.clear();
mEventsCounter.drainPermits();
final String id = UUID.randomUUID().toString();
mStartCommand = START_PREFIX + id;
mFinishCommand = FINISH_PREFIX + id;
Log.d(TestProtocol.TAPL_EVENTS_TAG, mStartCommand);
}
private void onRun() {
try {
// Note that we use Runtime.exec to start the log reading process instead of running
// it via UIAutomation, so that we can directly access the "Process" object and
// ensure that the instrumentation is not stuck forever.
final String cmd = "logcat -s " + TestProtocol.TAPL_EVENTS_TAG;
try (BufferedReader reader = new BufferedReader(new InputStreamReader(
Runtime.getRuntime().exec(cmd).getInputStream()))) {
for (;;) {
// Skip everything before the next start command.
for (;;) {
final String event = reader.readLine();
if (event.contains(TestProtocol.TAPL_EVENTS_TAG)
&& event.contains(mStartCommand)) {
break;
}
}
// Store all actual events until the finish command.
for (;;) {
final String event = reader.readLine();
if (event.contains(TestProtocol.TAPL_EVENTS_TAG)) {
if (event.contains(mFinishCommand)) {
mFinished.countDown();
break;
} else {
final Matcher matcher = EVENT_LOG_ENTRY.matcher(event);
if (matcher.find()) {
mEvents.add(matcher.group("sequence"), matcher.group("event"));
mEventsCounter.release();
}
}
}
}
}
}
} catch (IOException e) {
throw new RuntimeException(e);
}
}
void expectPattern(String sequence, Pattern pattern) {
mExpectedEvents.add(sequence, pattern);
}
private void finishSync(long waitForExpectedCountMs) {
try {
// Wait until Launcher generates the expected number of events.
int expectedCount = mExpectedEvents.entrySet()
.stream().mapToInt(e -> e.getValue().size()).sum();
mEventsCounter.tryAcquire(expectedCount, waitForExpectedCountMs, MILLISECONDS);
finishNoWait();
mFinished.await();
mFinished = null;
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
}
void finishNoWait() {
mFinished = new CountDownLatch(1);
Log.d(TestProtocol.TAPL_EVENTS_TAG, mFinishCommand);
}
String verify(long waitForExpectedCountMs) {
finishSync(waitForExpectedCountMs);
final StringBuilder sb = new StringBuilder();
for (Map.Entry<String, List<Pattern>> expectedEvents : mExpectedEvents.entrySet()) {
String sequence = expectedEvents.getKey();
List<String> actual = new ArrayList<>(mEvents.getNonNull(sequence));
final int mismatchPosition = getMismatchPosition(expectedEvents.getValue(), actual);
if (mismatchPosition != -1) {
formatSequenceWithMismatch(
sb,
sequence,
expectedEvents.getValue(),
actual,
mismatchPosition);
}
}
// Check for unexpected event sequences in the actual data.
for (String actualNamedSequence : mEvents.keySet()) {
if (!mExpectedEvents.containsKey(actualNamedSequence)) {
formatSequenceWithMismatch(
sb,
actualNamedSequence,
new ArrayList<>(),
mEvents.get(actualNamedSequence),
0);
}
}
return sb.length() != 0 ? "mismatching events: " + sb.toString() : null;
}
// If the list of actual events matches the list of expected events, returns -1, otherwise
// the position of the mismatch.
private static int getMismatchPosition(List<Pattern> expected, List<String> actual) {
for (int i = 0; i < expected.size(); ++i) {
if (i >= actual.size()
|| !expected.get(i).matcher(actual.get(i)).find()) {
return i;
}
}
if (actual.size() > expected.size()) return expected.size();
return -1;
}
private static void formatSequenceWithMismatch(
StringBuilder sb,
String sequenceName,
List<Pattern> expected,
List<String> actualEvents,
int mismatchPosition) {
sb.append("\n>> Sequence " + sequenceName);
sb.append("\n Expected:");
formatEventListWithMismatch(sb, expected, mismatchPosition);
sb.append("\n Actual:");
formatEventListWithMismatch(sb, actualEvents, mismatchPosition);
}
private static void formatEventListWithMismatch(StringBuilder sb, List events, int position) {
for (int i = 0; i < events.size(); ++i) {
sb.append("\n | ");
sb.append(i == position ? "---> " : " ");
sb.append(events.get(i).toString());
}
if (position == events.size()) sb.append("\n | ---> (end)");
}
private static class ListMap<T> extends HashMap<String, List<T>> {
void add(String key, T value) {
getNonNull(key).add(value);
}
List<T> getNonNull(String key) {
List<T> list = get(key);
if (list == null) {
list = new ArrayList<>();
put(key, list);
}
return list;
}
}
}