Merge "Merging from ub-launcher3-master @ build 6327709" into rvc-dev
This commit is contained in:
committed by
Android (Google) Code Review
commit
ace493908c
-22
@@ -1,22 +0,0 @@
|
||||
150522230
|
||||
150260456
|
||||
139137636
|
||||
150122946
|
||||
150260456
|
||||
151166786
|
||||
139828243
|
||||
150876921
|
||||
150644839
|
||||
146593239
|
||||
143361609
|
||||
151025334
|
||||
150864182
|
||||
151050221
|
||||
150680980
|
||||
138964382
|
||||
150788630
|
||||
146593239
|
||||
149792636
|
||||
147305863
|
||||
148867106
|
||||
139137636
|
||||
@@ -1,19 +0,0 @@
|
||||
138964382
|
||||
139137636
|
||||
139828243
|
||||
143361609
|
||||
146593239
|
||||
147305863
|
||||
148867106
|
||||
149792636
|
||||
150122946
|
||||
150260456
|
||||
150522230
|
||||
150644839
|
||||
150680980
|
||||
150788630
|
||||
150864182
|
||||
150876921
|
||||
151025334
|
||||
151050221
|
||||
151166786
|
||||
@@ -1,48 +0,0 @@
|
||||
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 ----
|
||||
138964382 awickham P2 ASSIGNED Move all shared libs to a common location (plugin / iconloaderlib) ----
|
||||
139137636 vadimt P2 ASSIGNED Create memory tests for Launcher ----
|
||||
139828243 xuqiu P2 ASSIGNED [Overview Actions] Add Overview actions ----
|
||||
143361609 twickham P2 ASSIGNED Overview gesture in R ----
|
||||
146593239 awickham P2 FIXED Gesture navigation fail in Facebook messenger ----
|
||||
147305863 sfufa P2 FIXED DragNDrop userevent missing target hierarchy ----
|
||||
148867106 vadimt P2 FIXED [Flaky test] AddConfigWidgetTest.testConfigCancelled ----
|
||||
149792636 dupin P2 ASSIGNED [Android 11] Blur polish ----
|
||||
150122946 sfufa P4 FIXED Work toggle styling not correct ----
|
||||
150260456 peanutbutter P1 FIXED Add Fixed Rotation Transform to Home Settings ----
|
||||
150522230 hyunyoungs P1 ASSIGNED Pixel launcher keeps crashing ('java.lang.String android.content.ComponentName.getPackageName()) ----
|
||||
150644839 twickham P3 FIXED In landscape mode, long press on app shortcut UI issue observed. ----
|
||||
150680980 jonmiranda P2 FIXED Slide up to home screen animation is not smooth ----
|
||||
150788630 hyunyoungs P1 ASSIGNED Turn off FOLDER_NAME_SUGGEST flag on QQ builds earliest possible ----
|
||||
150864182 twickham P4 FIXED Crash when dumping while user is locked ----
|
||||
150876921 jonmiranda P2 FIXED Pages views seem to snap back to previous page ----
|
||||
151025334 hyunyoungs P1 VERIFIED DeviceConfig doesn't update immediately when Pheonotype pushes ----
|
||||
151050221 dupin P1 FIXED [NO LAST KMSG] [Short Uptime] [R]DUT will display the "Pixel is starting..." after rebooting and unlocking SIM pin code ----
|
||||
151166786 sunnygoyal P2 FIXED Swipe handler and activity interface mismatch ----
|
||||
-777
@@ -1,777 +0,0 @@
|
||||
[34mCOMMAND>> git log f3779f129f7326cb7acb57bf6aabd68aca5b6218..3aaf3967348ff55d2b8ac6d50e59ea01d9362af9(B[m
|
||||
commit 3aaf3967348ff55d2b8ac6d50e59ea01d9362af9
|
||||
Author: Vinit Nayak <peanutbutter@google.com>
|
||||
Date: Fri Mar 13 13:57:09 2020 -0700
|
||||
|
||||
Remove sensor manager from RecentsView
|
||||
|
||||
This disables the rotation animation in
|
||||
overview, but should hopefully fix tests.
|
||||
|
||||
fixes: 150260456
|
||||
Change-Id: I121cad155672c2e325cc0f83ce209be0d3806b1c
|
||||
|
||||
commit 9caed38e34671e6b07b95bea1e971dee03b010ce
|
||||
Merge: 6d8203ef4 f546e0599
|
||||
Author: Hyunyoung Song <hyunyoungs@google.com>
|
||||
Date: Fri Mar 13 16:41:17 2020 +0000
|
||||
|
||||
Merge "Null check every ComponentName call inside FolderNameProvider" into ub-launcher3-master
|
||||
|
||||
commit f546e0599e8127068e7d75ff787483453c781275
|
||||
Author: Hyunyoung Song <hyunyoungs@google.com>
|
||||
Date: Thu Mar 12 12:53:43 2020 -0700
|
||||
|
||||
Null check every ComponentName call inside FolderNameProvider
|
||||
|
||||
Bug: 150522230
|
||||
Change-Id: I50007a3a781234797e16d830935a8b8585ac242b
|
||||
|
||||
commit 6d8203ef4495f9550999f483f7c9822316b29971
|
||||
Merge: 3af717835 3388323bc
|
||||
Author: TreeHugger Robot <treehugger-gerrit@google.com>
|
||||
Date: Fri Mar 13 03:00:46 2020 +0000
|
||||
|
||||
Merge "Disable OrientationListener if vertical landscape not supported" into ub-launcher3-master
|
||||
|
||||
commit 3388323bc1b598dcee37d9522c14f968a1318f8e
|
||||
Author: Vinit Nayak <peanutbutter@google.com>
|
||||
Date: Thu Mar 12 17:18:51 2020 -0700
|
||||
|
||||
Disable OrientationListener if vertical landscape not supported
|
||||
|
||||
Even if multiple orientations are disabled but the flag
|
||||
is on, we'll be listneing and setting different layouts.
|
||||
Seeing in some tests that that callback was getting fired
|
||||
for some reason.
|
||||
|
||||
Fixes: 150260456
|
||||
Change-Id: I0a1c9f06cc4830d3dc8410a777d595851f1c35eb
|
||||
|
||||
commit 3af717835652536726e42d46d5875adf5ceb3cb7
|
||||
Author: thiruram <thiruram@google.com>
|
||||
Date: Thu Mar 12 16:16:24 2020 -0700
|
||||
|
||||
Fixes missing smart folder logging bug. Uses ProtoLite.toString method to log LauncherEvents.
|
||||
|
||||
Change-Id: I45dbf189e7bd47f8d4d7ba55180e59686bd6ecae
|
||||
|
||||
commit b6bc08ad5751e360a0e0407f7fc5cd708b7a28be
|
||||
Merge: 6aa63d9f8 984c01cbc
|
||||
Author: Tony Wickham <twickham@google.com>
|
||||
Date: Thu Mar 12 21:48:42 2020 +0000
|
||||
|
||||
Merge "Invert playNonAtomicComponent() as onlyPlayAtomicComponent()" into ub-launcher3-master
|
||||
|
||||
commit 6aa63d9f8ec9a20c431da2f0cd05be610ed0d152
|
||||
Merge: 4e82f5bc3 f0d96f83f
|
||||
Author: Vadim Tryshev <vadimt@google.com>
|
||||
Date: Thu Mar 12 21:01:30 2020 +0000
|
||||
|
||||
Merge "Fixing activity leak via accumulation of draw listeners" into ub-launcher3-master
|
||||
|
||||
commit f0d96f83f72e81ffc0f14db60c50c022839bb6e7
|
||||
Author: vadimt <vadimt@google.com>
|
||||
Date: Tue Mar 10 13:44:58 2020 -0700
|
||||
|
||||
Fixing activity leak via accumulation of draw listeners
|
||||
|
||||
Bug: 139137636
|
||||
Change-Id: I0a2f0849f886acaac31746ac7c9724c765692e88
|
||||
|
||||
commit 4e82f5bc364f66d9a6ac72071db0d18e5a7fd956
|
||||
Author: Sunny Goyal <sunnygoyal@google.com>
|
||||
Date: Thu Mar 12 12:29:33 2020 -0700
|
||||
|
||||
Fixing crash when swiping up using 3P Launcher
|
||||
|
||||
Change-Id: Ia181edc1a00136374b3f0d848beccf0c9acd7b5c
|
||||
|
||||
commit f85fcc792f74b3143670cef9e260ffb1b68a1f9f
|
||||
Merge: b9ec9319c f5a4deb12
|
||||
Author: TreeHugger Robot <treehugger-gerrit@google.com>
|
||||
Date: Thu Mar 12 18:12:59 2020 +0000
|
||||
|
||||
Merge "Hide work apps when work profile is paused" into ub-launcher3-master
|
||||
|
||||
commit f5a4deb120731f116aea6b2161560f4e81502260
|
||||
Author: Samuel Fufa <sfufa@google.com>
|
||||
Date: Wed Mar 4 16:24:06 2020 -0800
|
||||
|
||||
Hide work apps when work profile is paused
|
||||
|
||||
- hide overlay icon in landscape mode
|
||||
- don't show edu if user has already seen legacy work profile edu
|
||||
- make sure personal tab is highlighted when work profile is reinstalled
|
||||
- always go home after a work profile is added or removed
|
||||
- add tests for work edu flow
|
||||
|
||||
Bug: 150122946
|
||||
Test: Manual
|
||||
Change-Id: I8f80ac763acf03ca31a534464f4ddfd84528d329
|
||||
|
||||
commit b9ec9319c5534cf6ebd8df2ee1144e1ebb477c33
|
||||
Author: Vinit Nayak <peanutbutter@google.com>
|
||||
Date: Wed Mar 4 12:05:28 2020 -0800
|
||||
|
||||
Add fixed_rotation_transform to home settings
|
||||
|
||||
This sets the feature flag on launcher side
|
||||
and also updates the setting in Settings.Global
|
||||
Launcher DOES NOT listen to the Settings.Global
|
||||
change from adb anymore. This should take
|
||||
preference over setting it from command line.
|
||||
|
||||
Also fix a related swipe to home animation bug
|
||||
that happened w/ merge conflict.
|
||||
|
||||
Fixes: 150260456
|
||||
Test: Set and unset, visually verified behavior.
|
||||
Tested w/ autorotate on and off.
|
||||
Checked Settings.Global value correctly updated
|
||||
via "adb shell settings get global
|
||||
fixed_rotation_transform"
|
||||
TODO: Update tests to reflect this new
|
||||
default-on fixed rotation behavior.
|
||||
|
||||
Change-Id: Id95f006eb1e92a59e24b05567298fd21b1409b13
|
||||
|
||||
commit 984c01cbcda8ab31c24c6a27118d9c1934c23795
|
||||
Author: Tony Wickham <twickham@google.com>
|
||||
Date: Fri Mar 6 15:56:46 2020 -0800
|
||||
|
||||
Invert playNonAtomicComponent() as onlyPlayAtomicComponent()
|
||||
|
||||
This avoids the double negative we use in a few places, so should be clearer.
|
||||
Also added some comments to explain what the animComponents are used for.
|
||||
|
||||
Change-Id: Ibd25bd12efce6553b377bbd9c0651e4f4ac3e498
|
||||
|
||||
commit 31ff98e14491edda33a7ccd2be04795bdaad124c
|
||||
Merge: 4acdb3bcd 9e19866ed
|
||||
Author: TreeHugger Robot <treehugger-gerrit@google.com>
|
||||
Date: Thu Mar 12 00:26:57 2020 +0000
|
||||
|
||||
Merge "Only allow horizontal spring if page will change." into ub-launcher3-master
|
||||
|
||||
commit 4acdb3bcd7f69e0cd42b7fae16176cbe15b551aa
|
||||
Merge: 6a550f26a 003782f93
|
||||
Author: Sunny Goyal <sunnygoyal@google.com>
|
||||
Date: Thu Mar 12 00:20:15 2020 +0000
|
||||
|
||||
Merge "Removing some properties out of AnimationBuilder" into ub-launcher3-master
|
||||
|
||||
commit 6a550f26a79564575cee5dfe15338c64ffe00d2b
|
||||
Merge: 0abe81991 fa617d89c
|
||||
Author: TreeHugger Robot <treehugger-gerrit@google.com>
|
||||
Date: Thu Mar 12 00:15:44 2020 +0000
|
||||
|
||||
Merge "Using FallbackSwipeHandler in 2-button mode" into ub-launcher3-master
|
||||
|
||||
commit 003782f93c8b5096ebf6e64fbfa7e3483c11d685
|
||||
Author: Sunny Goyal <sunnygoyal@google.com>
|
||||
Date: Fri Mar 6 14:52:17 2020 -0800
|
||||
|
||||
Removing some properties out of AnimationBuilder
|
||||
|
||||
AnimationBuilder and PendingAnimation have similar logic. This will
|
||||
allow to unify the two classes
|
||||
|
||||
Change-Id: Id8c1d8a7020d132adbccdc6c80538ed6556cb75e
|
||||
|
||||
commit 0abe81991398556485d6605dbeb57b64e2414f92
|
||||
Merge: 9c47ddd2a 21167f01d
|
||||
Author: TreeHugger Robot <treehugger-gerrit@google.com>
|
||||
Date: Wed Mar 11 23:27:05 2020 +0000
|
||||
|
||||
Merge "[Overview Actions] Hide other tasks for select mode UI." into ub-launcher3-master
|
||||
|
||||
commit fa617d89cece2c08c03fa7506700bed93d060cf7
|
||||
Author: Sunny Goyal <sunnygoyal@google.com>
|
||||
Date: Wed Mar 11 16:20:25 2020 -0700
|
||||
|
||||
Using FallbackSwipeHandler in 2-button mode
|
||||
|
||||
Bug: 151166786
|
||||
Change-Id: Ia86af76c1779bafa4690e733a7e6764973c8ae0d
|
||||
|
||||
commit 21167f01d4877d5927e4cd473cf6fcf61acc54d8
|
||||
Author: Becky Qiu <xuqiu@google.com>
|
||||
Date: Thu Mar 5 11:26:34 2020 -0800
|
||||
|
||||
[Overview Actions] Hide other tasks for select mode UI.
|
||||
|
||||
Test:local
|
||||
Bug:139828243
|
||||
|
||||
Change-Id: Idc9c6a0e354b9df7f48f3ce93b560fdc4999fc3a
|
||||
|
||||
commit 9e19866ed86de5237ea02810e28fb56f3ab80616
|
||||
Author: Jon Miranda <jonmiranda@google.com>
|
||||
Date: Wed Mar 11 14:42:02 2020 -0700
|
||||
|
||||
Only allow horizontal spring if page will change.
|
||||
|
||||
Bug: 150876921
|
||||
Change-Id: I88db4c28ec3f8213c583e8a0dcd1cce2b1fee322
|
||||
|
||||
commit 9c47ddd2a60aacd5663e040cf1f90d65896544a4
|
||||
Merge: 8780065fd ddb08885f
|
||||
Author: Tony Wickham <twickham@google.com>
|
||||
Date: Wed Mar 11 21:10:04 2020 +0000
|
||||
|
||||
Merge "Try orienting popup the other way if offset pushes it out of bounds" into ub-launcher3-master
|
||||
|
||||
commit 8780065fdbe9e0b012f110d523447bcf4022a191
|
||||
Merge: 25960bcd8 2768a2468
|
||||
Author: Automerger Merge Worker <android-build-automerger-merge-worker@system.gserviceaccount.com>
|
||||
Date: Wed Mar 11 20:17:17 2020 +0000
|
||||
|
||||
[automerger skipped] Merge "[DO NOT MERGE] Adds fling gesture suppression to Launcher" into ub-launcher3-qt-qpr1-dev am: 631ed598ee -s ours am: 2768a24688 -s ours
|
||||
|
||||
am skip reason: subject contains skip directive
|
||||
|
||||
Change-Id: I36a95c05e2cef2bb615c67c30697c8a1b07b4c6f
|
||||
|
||||
commit 25960bcd88b97727c9892dfce0ad57761ef3a992
|
||||
Merge: 9c40c83d7 f3b22ffee
|
||||
Author: Automerger Merge Worker <android-build-automerger-merge-worker@system.gserviceaccount.com>
|
||||
Date: Wed Mar 11 20:17:10 2020 +0000
|
||||
|
||||
[automerger skipped] [DO NOT MERGE] Adds fling gesture suppression to Launcher am: f3b22ffee6 -s ours
|
||||
|
||||
am skip reason: subject contains skip directive
|
||||
|
||||
Change-Id: I38695bd05d74b8a526e61c2b55b2d2669e4e31a7
|
||||
|
||||
commit 2768a24688171fddd0ed4e8fa720d308736c2ca8
|
||||
Merge: f3b22ffee 631ed598e
|
||||
Author: Automerger Merge Worker <android-build-automerger-merge-worker@system.gserviceaccount.com>
|
||||
Date: Wed Mar 11 20:05:05 2020 +0000
|
||||
|
||||
[automerger skipped] Merge "[DO NOT MERGE] Adds fling gesture suppression to Launcher" into ub-launcher3-qt-qpr1-dev am: 631ed598ee -s ours
|
||||
|
||||
am skip reason: subject contains skip directive
|
||||
|
||||
Change-Id: I6a1906e1e7e302ca6886f3ecdeac5d374476020e
|
||||
|
||||
commit ddb08885f9d51db577aedc386cc3a90d054132b2
|
||||
Author: Tony Wickham <twickham@google.com>
|
||||
Date: Tue Mar 10 18:25:31 2020 -0700
|
||||
|
||||
Try orienting popup the other way if offset pushes it out of bounds
|
||||
|
||||
orientAboutObject() currently determines whether the popup should align
|
||||
its left side with the icon or the right side. However, after
|
||||
determining this, there is an offset to ensure the popup lines up with
|
||||
the icon as expected, which might push it out of bounds. In that case,
|
||||
we fallback to centering the popup. However, there might be plenty of
|
||||
room on the other side, so we should just align the other direction
|
||||
instead. Updated the logic to do that by first trying to align left
|
||||
(in LTR) or right (in RTL), then trying again with the other alignment
|
||||
if it doesn't fit after all x calculations are made.
|
||||
|
||||
Bug: 150644839
|
||||
Change-Id: I219dae331bf790e461d91394ffe025d40ec54c9b
|
||||
|
||||
commit f3b22ffee691dca3e8a5cc3c0a1fb1d19ce8a5ad
|
||||
Author: Govinda Wasserman <gwasserman@google.com>
|
||||
Date: Thu Mar 5 16:50:22 2020 -0500
|
||||
|
||||
[DO NOT MERGE] Adds fling gesture suppression to Launcher
|
||||
|
||||
Test: Tested locally
|
||||
BUG: 150688842
|
||||
Change-Id: Ifa96bd01363de47cf1d8cdce34d81d525c8c2c04
|
||||
(cherry picked from commit 9b90b1b0345ea57a6152919d318f4ce9cacd7556)
|
||||
|
||||
commit 631ed598ee115bdfa1b3249a87c1f266eb93d57d
|
||||
Merge: 9a32222ce 9b90b1b03
|
||||
Author: Govinda Wasserman <gwasserman@google.com>
|
||||
Date: Wed Mar 11 18:19:26 2020 +0000
|
||||
|
||||
Merge "[DO NOT MERGE] Adds fling gesture suppression to Launcher" into ub-launcher3-qt-qpr1-dev
|
||||
|
||||
commit 9b90b1b0345ea57a6152919d318f4ce9cacd7556
|
||||
Author: Govinda Wasserman <gwasserman@google.com>
|
||||
Date: Thu Mar 5 16:50:22 2020 -0500
|
||||
|
||||
[DO NOT MERGE] Adds fling gesture suppression to Launcher
|
||||
|
||||
Test: Tested locally
|
||||
BUG: 150688842
|
||||
Change-Id: Ifa96bd01363de47cf1d8cdce34d81d525c8c2c04
|
||||
|
||||
commit 9c40c83d70bbe4689e1bddc012aec6d7a04dc490
|
||||
Merge: b41aa64b8 86ace5452
|
||||
Author: Automerger Merge Worker <android-build-automerger-merge-worker@system.gserviceaccount.com>
|
||||
Date: Tue Mar 10 20:55:30 2020 +0000
|
||||
|
||||
[automerger skipped] Merge "Dismisses system overlays for Home intent." into ub-launcher3-qt-future-dev am: 86ace54523 -s ours
|
||||
|
||||
am skip reason: Change-Id Ib9c85de2f83f99d1ef53fb17fde5d0b3c514849a with SHA-1 65ced1b1d0 is in history
|
||||
|
||||
Change-Id: I70904c253ac6ad36820069f64d9338ee067d159b
|
||||
|
||||
commit b41aa64b8df2256f554c7ea4fa96c10464d1a7b6
|
||||
Merge: d7c844167 5e72945a8
|
||||
Author: Automerger Merge Worker <android-build-automerger-merge-worker@system.gserviceaccount.com>
|
||||
Date: Tue Mar 10 20:55:18 2020 +0000
|
||||
|
||||
[automerger skipped] Merge "Import translations. DO NOT MERGE" into ub-launcher3-rvc-dev am: 5e72945a85 -s ours
|
||||
|
||||
am skip reason: subject contains skip directive
|
||||
|
||||
Change-Id: Ib8b27aa6ad4e4f0ed4bdebf3bb7b7cac654fad25
|
||||
|
||||
commit d7c84416765d4883fa2d8bba595c058db042325f
|
||||
Merge: 0a9471546 3c1db273b
|
||||
Author: Hyunyoung Song <hyunyoungs@google.com>
|
||||
Date: Tue Mar 10 19:15:37 2020 +0000
|
||||
|
||||
Merge "DeviceFlag change is not detected when phenotype updates." into ub-launcher3-master
|
||||
|
||||
commit 5e72945a852ba56976835874a7c012726d2e00d6
|
||||
Merge: a066cb443 a159b77ef
|
||||
Author: TreeHugger Robot <treehugger-gerrit@google.com>
|
||||
Date: Tue Mar 10 17:45:51 2020 +0000
|
||||
|
||||
Merge "Import translations. DO NOT MERGE" into ub-launcher3-rvc-dev
|
||||
|
||||
commit 86ace54523dc354fadd65987e5ace43f89586e34
|
||||
Merge: 44e729895 b3b8aefe5
|
||||
Author: TreeHugger Robot <treehugger-gerrit@google.com>
|
||||
Date: Tue Mar 10 17:42:54 2020 +0000
|
||||
|
||||
Merge "Dismisses system overlays for Home intent." into ub-launcher3-qt-future-dev
|
||||
|
||||
commit a159b77ef149652daf56e4f413fd4dbc264b032b
|
||||
Author: Bill Yi <byi@google.com>
|
||||
Date: Tue Mar 10 09:56:26 2020 -0700
|
||||
|
||||
Import translations. DO NOT MERGE
|
||||
|
||||
Auto-generated-cl: translation import
|
||||
Change-Id: I4d6b82f9e793cf2649102e913c81c27f6ccc004a
|
||||
|
||||
commit 0a947154691988747aae405018c8ddb36be27a05
|
||||
Merge: f2508e783 44e729895
|
||||
Author: Automerger Merge Worker <android-build-automerger-merge-worker@system.gserviceaccount.com>
|
||||
Date: Tue Mar 10 16:21:57 2020 +0000
|
||||
|
||||
[automerger skipped] Merge "Import translations. DO NOT MERGE" into ub-launcher3-qt-future-dev am: 44e7298953 -s ours
|
||||
am skip reason: subject contains skip directive
|
||||
|
||||
Change-Id: I1b060550e9d0f839c96b1582712321ef530e353f
|
||||
|
||||
commit 44e729895391cd49acc938004c6044fc652ea9db
|
||||
Merge: defd3c0e6 83730697f
|
||||
Author: TreeHugger Robot <treehugger-gerrit@google.com>
|
||||
Date: Tue Mar 10 16:18:29 2020 +0000
|
||||
|
||||
Merge "Import translations. DO NOT MERGE" into ub-launcher3-qt-future-dev
|
||||
|
||||
commit f2508e783f624f423bb892a0060d84cbfc36052f
|
||||
Merge: e635a2689 defd3c0e6
|
||||
Author: Automerger Merge Worker <android-build-automerger-merge-worker@system.gserviceaccount.com>
|
||||
Date: Tue Mar 10 16:16:51 2020 +0000
|
||||
|
||||
[automerger skipped] Import translations. DO NOT MERGE am: 9a32222ce7 -s ours am: defd3c0e6f -s ours
|
||||
am skip reason: subject contains skip directive
|
||||
|
||||
Change-Id: I5eb69f5179f420f1ab678a025ab7bf28f93f595a
|
||||
|
||||
commit defd3c0e6fc964cbbb5801cf045bd6de9689a0c7
|
||||
Merge: 94c993a63 9a32222ce
|
||||
Author: Automerger Merge Worker <android-build-automerger-merge-worker@system.gserviceaccount.com>
|
||||
Date: Tue Mar 10 16:04:16 2020 +0000
|
||||
|
||||
[automerger skipped] Import translations. DO NOT MERGE am: 9a32222ce7 -s ours
|
||||
am skip reason: subject contains skip directive
|
||||
|
||||
Change-Id: I4fe5c187cc89f91199f3265f43d323556a992841
|
||||
|
||||
commit e635a2689d0b75065a7a8d5fae031382137303a6
|
||||
Merge: dbcc63ede a066cb443
|
||||
Author: Automerger Merge Worker <android-build-automerger-merge-worker@system.gserviceaccount.com>
|
||||
Date: Tue Mar 10 16:04:12 2020 +0000
|
||||
|
||||
[automerger skipped] Import translations. DO NOT MERGE am: a066cb4430 -s ours
|
||||
am skip reason: subject contains skip directive
|
||||
|
||||
Change-Id: I2cd255fd2f265718df096d05f3c8a67c78662c58
|
||||
|
||||
commit dbcc63edebe373d5f698e7d404eff83898a5b8b2
|
||||
Author: Sunny Goyal <sunnygoyal@google.com>
|
||||
Date: Fri Mar 6 15:35:55 2020 -0800
|
||||
|
||||
Removing some autoboxing during property animation
|
||||
|
||||
Change-Id: Ibd6f20c565a4d66dc6d606b3f0bbc96fec66fe56
|
||||
|
||||
commit add170098c5696948edbd7d7e3f220c801cfc9eb
|
||||
Merge: d01ee6d6a 4c9ee6354
|
||||
Author: Sunny Goyal <sunnygoyal@google.com>
|
||||
Date: Tue Mar 10 08:43:41 2020 +0000
|
||||
|
||||
Merge "Converting some anonymous classes to lambda calls" into ub-launcher3-master
|
||||
|
||||
commit b3b8aefe5751bf55ef32f61148f2a9b6c811db9e
|
||||
Author: Andy Wickham <awickham@google.com>
|
||||
Date: Tue Mar 10 01:36:02 2020 +0000
|
||||
|
||||
Dismisses system overlays for Home intent.
|
||||
|
||||
Test: Used Facebook chatheads (not system bubble).
|
||||
Before the change, Home gesture didn't work. After
|
||||
the change, it does work :)
|
||||
Bug: 146593239
|
||||
|
||||
Merged-In: Ib9c85de2f83f99d1ef53fb17fde5d0b3c514849a
|
||||
Change-Id: I19d91aaed19ccaec68478e364ce6b80049d49a98
|
||||
|
||||
commit a066cb4430989496e80770bbac04c68b2d515f2b
|
||||
Author: Bill Yi <byi@google.com>
|
||||
Date: Mon Mar 9 19:41:30 2020 -0700
|
||||
|
||||
Import translations. DO NOT MERGE
|
||||
|
||||
Auto-generated-cl: translation import
|
||||
Change-Id: I91780ce30d4eb9825d415e2825b9a94e2a4fade8
|
||||
|
||||
commit 83730697f8416f8124bb0a6593aba5334f38546e
|
||||
Author: Bill Yi <byi@google.com>
|
||||
Date: Mon Mar 9 19:37:07 2020 -0700
|
||||
|
||||
Import translations. DO NOT MERGE
|
||||
|
||||
Auto-generated-cl: translation import
|
||||
Change-Id: I833e37768c8b22a17cd5e36ac7b01ac024f3bbfc
|
||||
|
||||
commit 9a32222ce76b37911e42fdfa500d3e47623d459d
|
||||
Author: Bill Yi <byi@google.com>
|
||||
Date: Mon Mar 9 19:32:09 2020 -0700
|
||||
|
||||
Import translations. DO NOT MERGE
|
||||
|
||||
Auto-generated-cl: translation import
|
||||
Change-Id: I4cb33e7020ee7cb582982fecba72dfd7f2c70469
|
||||
|
||||
commit d01ee6d6a84ca0fcc2dbc4757cdc90e36da02692
|
||||
Merge: 7dfe1360e 94c993a63
|
||||
Author: Automerger Merge Worker <android-build-automerger-merge-worker@system.gserviceaccount.com>
|
||||
Date: Tue Mar 10 01:05:38 2020 +0000
|
||||
|
||||
[automerger skipped] Merge "[DO NOT MERGE] Fix some visual jumps when swiping home" into ub-launcher3-qt-future-dev am: 94c993a635 -s ours
|
||||
am skip reason: subject contains skip directive
|
||||
|
||||
Change-Id: I2b30725cd240cdc7ebfcb50eb2180aaf81ab4267
|
||||
|
||||
commit 94c993a635f00796b00c45b47b29496fe891839c
|
||||
Merge: e1664fcf1 8caa78790
|
||||
Author: Jonathan Miranda <jonmiranda@google.com>
|
||||
Date: Tue Mar 10 00:49:17 2020 +0000
|
||||
|
||||
Merge "[DO NOT MERGE] Fix some visual jumps when swiping home" into ub-launcher3-qt-future-dev
|
||||
|
||||
commit 7dfe1360edbe9ea64c0b8c591ffc525ba9a5a581
|
||||
Merge: 590914cc5 79a352169
|
||||
Author: TreeHugger Robot <treehugger-gerrit@google.com>
|
||||
Date: Tue Mar 10 00:46:11 2020 +0000
|
||||
|
||||
Merge "Show drag handle indictor in 2 zone model" into ub-launcher3-master
|
||||
|
||||
commit 79a352169f0197f5d0ea2be32cd8cf2f7dbef1ad
|
||||
Author: Tony Wickham <twickham@google.com>
|
||||
Date: Mon Mar 9 16:31:21 2020 -0700
|
||||
|
||||
Show drag handle indictor in 2 zone model
|
||||
|
||||
Note this is just the tiny arrow we show in accessibility mode, will
|
||||
probably need to get some updated visual treatment going forward.
|
||||
|
||||
Bug: 143361609
|
||||
Change-Id: I65975727f101984429aadc35a650826e36d9c9aa
|
||||
|
||||
commit 590914cc5ec219590f56cea52d975865187e7e0e
|
||||
Merge: e9801665a 65ced1b1d
|
||||
Author: TreeHugger Robot <treehugger-gerrit@google.com>
|
||||
Date: Mon Mar 9 22:00:20 2020 +0000
|
||||
|
||||
Merge "Dismisses system overlays for Home intent." into ub-launcher3-master
|
||||
|
||||
commit e9801665ac81ac0d4059991377a3a782f92dfe66
|
||||
Merge: b365cc438 e1664fcf1
|
||||
Author: Automerger Merge Worker <android-build-automerger-merge-worker@system.gserviceaccount.com>
|
||||
Date: Mon Mar 9 21:42:07 2020 +0000
|
||||
|
||||
[automerger skipped] [DO NOT MERGE] Initiailize APP_SEARCH_IMRPOVEMENT flag outside DEBUG builds. am: e1664fcf14 -s ours
|
||||
am skip reason: subject contains skip directive
|
||||
|
||||
Change-Id: I06a7e3ef6f5a0d6ed852b0b32238426ca3ba3809
|
||||
|
||||
commit 3c1db273bec12d4bb0573e20bc0f6ecc5b7e18b7
|
||||
Author: Hyunyoung Song <hyunyoungs@google.com>
|
||||
Date: Sat Mar 7 23:52:17 2020 -0800
|
||||
|
||||
DeviceFlag change is not detected when phenotype updates.
|
||||
|
||||
Bug: 151025334
|
||||
Test: adb shell device_config put launcher FOLDER_NAME_SUGGEST false
|
||||
|
||||
Change-Id: I5e478aebfea5847cf7cbe7c0cb6cb0c6f81481cb
|
||||
|
||||
commit b365cc43878e0cb4e6d4b998c10cd590adc4cb8d
|
||||
Author: Tony Wickham <twickham@google.com>
|
||||
Date: Mon Mar 9 13:20:04 2020 -0700
|
||||
|
||||
Fix crash when dumping before user unlocks
|
||||
|
||||
Bug: 150864182
|
||||
Bug: 151050221
|
||||
Change-Id: I29ba2ef66b4359a47f866d02306498537c45236e
|
||||
|
||||
commit e1664fcf1486d337ea998ded70d092590a0bfbd9
|
||||
Author: Alex Mang <alexmang@google.com>
|
||||
Date: Mon Mar 9 12:57:11 2020 -0700
|
||||
|
||||
[DO NOT MERGE] Initiailize APP_SEARCH_IMRPOVEMENT flag outside DEBUG builds.
|
||||
|
||||
This is addressing a bug where flags are only changed on debug build
|
||||
devices or initially when changes. When nexuslauncher restarts the flag
|
||||
is no longer retrieved.
|
||||
|
||||
Change-Id: Ieb6f460a271c918ee4e493b34692244f39cb3740
|
||||
|
||||
commit 8caa787906b49427afed77e7bd63b72d9bbbe8a7
|
||||
Author: Jon Miranda <jonmiranda@google.com>
|
||||
Date: Mon Mar 9 12:50:38 2020 -0700
|
||||
|
||||
[DO NOT MERGE] 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.
|
||||
|
||||
Partial backport of ag/Ie48f4b665a5bf3cbef76bdf7f043febe99fb84a0
|
||||
|
||||
Bug: 150680980
|
||||
Change-Id: Ida65097f0ef7d2e11d48b84ecdd353ef89078015
|
||||
|
||||
commit bf48cd480cd131c370760117681917dedd784c51
|
||||
Author: Andy Wickham <awickham@google.com>
|
||||
Date: Tue Mar 3 01:15:27 2020 +0000
|
||||
|
||||
Removes iconloaderlib from Launcher3.
|
||||
|
||||
(It's now in frameworks/libs/systemui)
|
||||
|
||||
Bug: 138964382
|
||||
Test: builds
|
||||
Change-Id: Ic60adfb2ebdcf1a72b440df26023b861fd6e62d5
|
||||
|
||||
commit a9bcd82554534d55e38ca039eb52c1dfacbdb70a
|
||||
Merge: e6df7da2a cfaa4889e
|
||||
Author: TreeHugger Robot <treehugger-gerrit@google.com>
|
||||
Date: Sat Mar 7 08:38:49 2020 +0000
|
||||
|
||||
Merge "Enabling springs for start dismiss animation" into ub-launcher3-master
|
||||
|
||||
commit e6df7da2a252c5d57114346d6bf6d6883b4a9f9a
|
||||
Merge: df8232c22 16eca5500
|
||||
Author: TreeHugger Robot <treehugger-gerrit@google.com>
|
||||
Date: Sat Mar 7 07:19:13 2020 +0000
|
||||
|
||||
Merge "Enabling event verification for Launcher3" into ub-launcher3-master
|
||||
|
||||
commit cfaa4889e65190b40ea988dd03421a01c9e06abc
|
||||
Author: Sunny Goyal <sunnygoyal@google.com>
|
||||
Date: Tue Feb 25 14:37:01 2020 -0800
|
||||
|
||||
Enabling springs for start dismiss animation
|
||||
|
||||
> Adding flag support for PendingAnimation which can be used
|
||||
to define custom behavior for various animations
|
||||
> Using SpringAnimationBuild for spring animation instead of
|
||||
SpringObjectanimator
|
||||
|
||||
Change-Id: I41ca34b0574981bb3fc7894639a321c12e6feac1
|
||||
|
||||
commit df8232c2242eeb2d8efc050a5e7afb88782ed9ca
|
||||
Merge: cfea0fb34 f538393e4
|
||||
Author: Automerger Merge Worker <android-build-automerger-merge-worker@system.gserviceaccount.com>
|
||||
Date: Sat Mar 7 06:50:59 2020 +0000
|
||||
|
||||
[automerger skipped] [DO NOT MERGE] Turn off FOLDER_NAME_SUGGEST feature flag am: f538393e42 -s ours
|
||||
am skip reason: subject contains skip directive
|
||||
|
||||
Change-Id: I1bbffc4f9582d106ce28ac07e6c6719c9f8f063d
|
||||
|
||||
commit f538393e42896e5488ca10fccc4cf9409d7295e2
|
||||
Author: Hyunyoung Song <hyunyoungs@google.com>
|
||||
Date: Fri Mar 6 12:53:42 2020 -0800
|
||||
|
||||
[DO NOT MERGE] Turn off FOLDER_NAME_SUGGEST feature flag
|
||||
Bug: 150788630
|
||||
|
||||
Change-Id: I740d6b6f3ee1a33a95debfafa29b3caea24a03c3
|
||||
|
||||
commit 65ced1b1d00bc6a6713b442162020df31d497f54
|
||||
Author: Andy Wickham <awickham@google.com>
|
||||
Date: Sat Mar 7 02:14:19 2020 +0000
|
||||
|
||||
Dismisses system overlays for Home intent.
|
||||
|
||||
Test: Used Facebook chatheads (not system bubble).
|
||||
Before the change, Home gesture didn't work. After
|
||||
the change, it does work :)
|
||||
Fixes: 146593239
|
||||
|
||||
Change-Id: Ib9c85de2f83f99d1ef53fb17fde5d0b3c514849a
|
||||
|
||||
commit cfea0fb348910bb245a03df0e178f0be59905dc7
|
||||
Merge: 312340504 e7dd35ef0
|
||||
Author: TreeHugger Robot <treehugger-gerrit@google.com>
|
||||
Date: Sat Mar 7 00:20:56 2020 +0000
|
||||
|
||||
Merge "OverviewActions: Renaming overview_actions_container and setActionsView" into ub-launcher3-master
|
||||
|
||||
commit 31234050473d511a61ce4dc257c15281c1c886b4
|
||||
Merge: f655f5cd1 27409e23d
|
||||
Author: Sunny Goyal <sunnygoyal@google.com>
|
||||
Date: Fri Mar 6 23:44:57 2020 +0000
|
||||
|
||||
Merge "Removing SecondaryDisplayLauncher library as it is directly included in Launcher" into ub-launcher3-master
|
||||
|
||||
commit f655f5cd16a9579afc11088e5545148261225b9f
|
||||
Merge: a579ddc9c 8687bc213
|
||||
Author: Winson Chung <winsonc@google.com>
|
||||
Date: Fri Mar 6 23:33:26 2020 +0000
|
||||
|
||||
Merge "Initial changes to support blur" into ub-launcher3-master
|
||||
|
||||
commit 4c9ee63540dcd2c039831edb0816a56458e30f8f
|
||||
Author: Sunny Goyal <sunnygoyal@google.com>
|
||||
Date: Fri Mar 6 15:16:22 2020 -0800
|
||||
|
||||
Converting some anonymous classes to lambda calls
|
||||
|
||||
Change-Id: I386046a4a515d84801a8bbd11cfa090ba7adfd71
|
||||
|
||||
commit 8687bc2131ce98ea0d058290dd21f03b5a429c82
|
||||
Author: Winson Chung <winsonc@google.com>
|
||||
Date: Thu Feb 27 23:34:24 2020 -0800
|
||||
|
||||
Initial changes to support blur
|
||||
|
||||
- Add a new controller to update the background blur on either the
|
||||
launcher or app surfaces based on state or transition
|
||||
|
||||
Bug: 149792636
|
||||
|
||||
Change-Id: I6103cd3d53a00c8025558dd49bb73137e2980014
|
||||
|
||||
commit a579ddc9c813f314ab3dfd4e80a9c0cf1c77ec61
|
||||
Author: Samuel Fufa <sfufa@google.com>
|
||||
Date: Thu Feb 27 16:59:19 2020 -0800
|
||||
|
||||
Refactor logging to capture Target hierarchy
|
||||
|
||||
Instead of creating a fixed number of targets, we now pass an ArrayList
|
||||
of targets to. Any class implementing
|
||||
LogContainerProviders#fillInLogContainerData can setup it's own target
|
||||
and add it to the ArrayList, It can also pass the ArrayList to other
|
||||
LogContainerProvider to capture full Target hierarchy.
|
||||
|
||||
Bug: 147305863
|
||||
Change-Id: I0063c692120fb9e1cff2d8902c5da972d0623418
|
||||
|
||||
commit e7dd35ef0dd653764e57665756e33ce75446e520
|
||||
Author: Sreyas <sreyasr@google.com>
|
||||
Date: Fri Mar 6 10:50:43 2020 -0800
|
||||
|
||||
OverviewActions: Renaming overview_actions_container and setActionsView
|
||||
|
||||
Change-Id: Ie444101f246e0d00980b47ce39f6e74dade23f73
|
||||
|
||||
commit 9099dfcfb75dff987c157a659de5883fe92b22c4
|
||||
Merge: e90adc47e 04b90c0fc
|
||||
Author: Vadim Tryshev <vadimt@google.com>
|
||||
Date: Fri Mar 6 18:36:05 2020 +0000
|
||||
|
||||
Merge "Test tweaks for the memory activity recreation test" into ub-launcher3-master
|
||||
|
||||
commit e90adc47ef114e129a14382f30d84910ef394348
|
||||
Author: Winson Chung <winsonc@google.com>
|
||||
Date: Fri Mar 6 00:01:49 2020 -0800
|
||||
|
||||
Fallback to predefined orientation handler if recents view isn't available
|
||||
|
||||
Change-Id: Iaed42fb9ef598d65e1cf2d166cc343f888352d47
|
||||
|
||||
commit d9da5a45fd1653c14e6fd54b15708a696c43c037
|
||||
Merge: f67ab6c64 3abc8511a
|
||||
Author: TreeHugger Robot <treehugger-gerrit@google.com>
|
||||
Date: Fri Mar 6 06:53:51 2020 +0000
|
||||
|
||||
Merge "Enabling widget config tests after fixing flakiness" into ub-launcher3-master
|
||||
|
||||
commit 16eca5500dab2f253f52920edaed477f5e43f413
|
||||
Author: vadimt <vadimt@google.com>
|
||||
Date: Thu Mar 5 19:02:18 2020 -0800
|
||||
|
||||
Enabling event verification for Launcher3
|
||||
|
||||
Will help investigating otherwise mysterious failures.
|
||||
|
||||
Change-Id: I805ed136baf6d86921fdb4782304fcdafbd3af5c
|
||||
|
||||
commit f67ab6c64d0691f7ff1ede3942179172122dac1e
|
||||
Merge: 93648b0a5 bb2bf277c
|
||||
Author: TreeHugger Robot <treehugger-gerrit@google.com>
|
||||
Date: Fri Mar 6 02:14:19 2020 +0000
|
||||
|
||||
Merge "Catching everything from dumpHprofData" into ub-launcher3-master
|
||||
|
||||
commit bb2bf277c0b22ecb1dc64d9971978d3c958dda44
|
||||
Author: vadimt <vadimt@google.com>
|
||||
Date: Thu Mar 5 17:22:18 2020 -0800
|
||||
|
||||
Catching everything from dumpHprofData
|
||||
|
||||
Change-Id: I79ced1d4bb3e6ea43ce6fa5bd07fe22b577006f9
|
||||
|
||||
commit 3abc8511a51afe983b481bdf6631535aa8b94f28
|
||||
Author: vadimt <vadimt@google.com>
|
||||
Date: Thu Mar 5 14:01:23 2020 -0800
|
||||
|
||||
Enabling widget config tests after fixing flakiness
|
||||
|
||||
Bug: 148867106
|
||||
Change-Id: I8bbd9ef9b1ca574f79f3f76869051495b59734ce
|
||||
|
||||
commit 27409e23d1ba7ca94031de8ff0603c53b86cfcdc
|
||||
Author: Sunny Goyal <sunnygoyal@google.com>
|
||||
Date: Wed Feb 26 16:08:20 2020 -0800
|
||||
|
||||
Removing SecondaryDisplayLauncher library as it is directly included in Launcher
|
||||
|
||||
Change-Id: I97a1fad07f2f6d34fc31c720fcc1e03d0f56477e
|
||||
|
||||
commit 04b90c0fcb316c830f7dc66475ff85c6760402f5
|
||||
Author: vadimt <vadimt@google.com>
|
||||
Date: Tue Oct 15 10:47:51 2019 -0700
|
||||
|
||||
Test tweaks for the memory activity recreation test
|
||||
|
||||
Speeding up switching navigation mode by switching from latch
|
||||
(which is not fired) to polling. I'll figure out later why the latch
|
||||
doesn't work.
|
||||
|
||||
Bug: 139137636
|
||||
Change-Id: I28a9b2b9a3882919fd2a3280b9804afe1b44a46e
|
||||
@@ -34,6 +34,7 @@
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_weight="1"
|
||||
android:gravity="center"
|
||||
android:layout_gravity="center_vertical"
|
||||
android:textColor="@android:color/white"
|
||||
android:textSize="16sp"/>
|
||||
|
||||
@@ -14,8 +14,5 @@
|
||||
limitations under the License.
|
||||
-->
|
||||
<resources>
|
||||
<integer name="app_background_blur_radius">150</integer>
|
||||
<integer name="allapps_background_blur_radius">90</integer>
|
||||
<integer name="overview_background_blur_radius">50</integer>
|
||||
<integer name="folder_background_blur_radius_adjustment">20</integer>
|
||||
<integer name="max_depth_blur_radius">150</integer>
|
||||
</resources>
|
||||
@@ -0,0 +1,151 @@
|
||||
/*
|
||||
* Copyright (C) 2008 The Android Open Source Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package com.android.launcher3;
|
||||
|
||||
import android.content.Context;
|
||||
import android.graphics.CornerPathEffect;
|
||||
import android.graphics.Paint;
|
||||
import android.graphics.drawable.ShapeDrawable;
|
||||
import android.os.Handler;
|
||||
import android.util.TypedValue;
|
||||
import android.view.Gravity;
|
||||
import android.view.MotionEvent;
|
||||
import android.view.View;
|
||||
import android.view.ViewGroup;
|
||||
import android.widget.LinearLayout;
|
||||
import android.widget.TextView;
|
||||
|
||||
import androidx.core.content.ContextCompat;
|
||||
|
||||
import com.android.launcher3.anim.Interpolators;
|
||||
import com.android.launcher3.dragndrop.DragLayer;
|
||||
import com.android.launcher3.graphics.TriangleShape;
|
||||
|
||||
/**
|
||||
* A base class for arrow tip view in launcher
|
||||
*/
|
||||
public class ArrowTipView extends AbstractFloatingView {
|
||||
|
||||
private static final long AUTO_CLOSE_TIMEOUT_MILLIS = 10 * 1000;
|
||||
private static final long SHOW_DELAY_MS = 200;
|
||||
private static final long SHOW_DURATION_MS = 300;
|
||||
private static final long HIDE_DURATION_MS = 100;
|
||||
|
||||
protected final Launcher mLauncher;
|
||||
private final Handler mHandler = new Handler();
|
||||
private Runnable mOnClosed;
|
||||
|
||||
public ArrowTipView(Context context) {
|
||||
super(context, null, 0);
|
||||
mLauncher = Launcher.getLauncher(context);
|
||||
init(context);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean onControllerInterceptTouchEvent(MotionEvent ev) {
|
||||
if (ev.getAction() == MotionEvent.ACTION_DOWN) {
|
||||
close(true);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void handleClose(boolean animate) {
|
||||
if (mIsOpen) {
|
||||
if (animate) {
|
||||
animate().alpha(0f)
|
||||
.withLayer()
|
||||
.setStartDelay(0)
|
||||
.setDuration(HIDE_DURATION_MS)
|
||||
.setInterpolator(Interpolators.ACCEL)
|
||||
.withEndAction(() -> mLauncher.getDragLayer().removeView(this))
|
||||
.start();
|
||||
} else {
|
||||
animate().cancel();
|
||||
mLauncher.getDragLayer().removeView(this);
|
||||
}
|
||||
if (mOnClosed != null) mOnClosed.run();
|
||||
mIsOpen = false;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void logActionCommand(int command) {
|
||||
}
|
||||
|
||||
@Override
|
||||
protected boolean isOfType(int type) {
|
||||
return (type & TYPE_ON_BOARD_POPUP) != 0;
|
||||
}
|
||||
|
||||
private void init(Context context) {
|
||||
inflate(context, R.layout.arrow_toast, this);
|
||||
setOrientation(LinearLayout.VERTICAL);
|
||||
View dismissButton = findViewById(R.id.dismiss);
|
||||
dismissButton.setOnClickListener(view -> {
|
||||
handleClose(true);
|
||||
});
|
||||
|
||||
View arrowView = findViewById(R.id.arrow);
|
||||
ViewGroup.LayoutParams arrowLp = arrowView.getLayoutParams();
|
||||
ShapeDrawable arrowDrawable = new ShapeDrawable(TriangleShape.create(
|
||||
arrowLp.width, arrowLp.height, false));
|
||||
Paint arrowPaint = arrowDrawable.getPaint();
|
||||
TypedValue typedValue = new TypedValue();
|
||||
context.getTheme().resolveAttribute(android.R.attr.colorAccent, typedValue, true);
|
||||
arrowPaint.setColor(ContextCompat.getColor(getContext(), typedValue.resourceId));
|
||||
// The corner path effect won't be reflected in the shadow, but shouldn't be noticeable.
|
||||
arrowPaint.setPathEffect(new CornerPathEffect(
|
||||
context.getResources().getDimension(R.dimen.arrow_toast_corner_radius)));
|
||||
arrowView.setBackground(arrowDrawable);
|
||||
|
||||
mIsOpen = true;
|
||||
|
||||
mHandler.postDelayed(() -> handleClose(true), AUTO_CLOSE_TIMEOUT_MILLIS);
|
||||
}
|
||||
|
||||
/**
|
||||
* Show Tip with specified string and Y location
|
||||
*/
|
||||
public ArrowTipView show(String text, int top) {
|
||||
((TextView) findViewById(R.id.text)).setText(text);
|
||||
mLauncher.getDragLayer().addView(this);
|
||||
|
||||
DragLayer.LayoutParams params = (DragLayer.LayoutParams) getLayoutParams();
|
||||
params.gravity = Gravity.CENTER_HORIZONTAL;
|
||||
params.leftMargin = mLauncher.getDeviceProfile().workspacePadding.left;
|
||||
params.rightMargin = mLauncher.getDeviceProfile().workspacePadding.right;
|
||||
post(() -> setY(top - getHeight()));
|
||||
setAlpha(0);
|
||||
animate()
|
||||
.alpha(1f)
|
||||
.withLayer()
|
||||
.setStartDelay(SHOW_DELAY_MS)
|
||||
.setDuration(SHOW_DURATION_MS)
|
||||
.setInterpolator(Interpolators.DEACCEL)
|
||||
.start();
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Register a callback fired when toast is hidden
|
||||
*/
|
||||
public ArrowTipView setOnClosedCallback(Runnable runnable) {
|
||||
mOnClosed = runnable;
|
||||
return this;
|
||||
}
|
||||
}
|
||||
+1
-1
@@ -91,7 +91,7 @@ public final class LauncherAppTransitionManagerImpl extends QuickstepAppTransiti
|
||||
AppWindowAnimationHelper helper =
|
||||
new AppWindowAnimationHelper(recentsView.getPagedViewOrientedState(), mLauncher);
|
||||
Animator recentsAnimator = getRecentsWindowAnimator(taskView, skipLauncherChanges,
|
||||
appTargets, wallpaperTargets, mLauncher.getBackgroundBlurController(), helper);
|
||||
appTargets, wallpaperTargets, mLauncher.getDepthController(), helper);
|
||||
anim.play(recentsAnimator.setDuration(RECENTS_LAUNCH_DURATION));
|
||||
|
||||
Animator childStateAnimation = null;
|
||||
|
||||
+13
-129
@@ -16,133 +16,30 @@
|
||||
|
||||
package com.android.launcher3.appprediction;
|
||||
|
||||
import static com.android.launcher3.AbstractFloatingView.TYPE_DISCOVERY_BOUNCE;
|
||||
import static com.android.launcher3.AbstractFloatingView.TYPE_ON_BOARD_POPUP;
|
||||
import static com.android.launcher3.LauncherState.ALL_APPS;
|
||||
import static com.android.quickstep.logging.UserEventDispatcherExtension.ALL_APPS_PREDICTION_TIPS;
|
||||
|
||||
import android.content.Context;
|
||||
import android.graphics.CornerPathEffect;
|
||||
import android.graphics.Paint;
|
||||
import android.graphics.drawable.ShapeDrawable;
|
||||
import android.os.Handler;
|
||||
import android.os.UserManager;
|
||||
import android.util.AttributeSet;
|
||||
import android.util.TypedValue;
|
||||
import android.view.Gravity;
|
||||
import android.view.MotionEvent;
|
||||
import android.view.View;
|
||||
import android.view.ViewGroup;
|
||||
import android.widget.LinearLayout;
|
||||
import android.widget.TextView;
|
||||
|
||||
import androidx.core.content.ContextCompat;
|
||||
|
||||
import com.android.launcher3.AbstractFloatingView;
|
||||
import com.android.launcher3.ArrowTipView;
|
||||
import com.android.launcher3.Launcher;
|
||||
import com.android.launcher3.LauncherState;
|
||||
import com.android.launcher3.LauncherStateManager;
|
||||
import com.android.launcher3.R;
|
||||
import com.android.launcher3.Utilities;
|
||||
import com.android.launcher3.allapps.FloatingHeaderView;
|
||||
import com.android.launcher3.anim.Interpolators;
|
||||
import com.android.launcher3.dragndrop.DragLayer;
|
||||
import com.android.launcher3.graphics.TriangleShape;
|
||||
import com.android.systemui.shared.system.LauncherEventUtil;
|
||||
|
||||
/**
|
||||
* All apps tip view aligned just above prediction apps, shown to users that enter all apps for the
|
||||
* ArrowTip helper aligned just above prediction apps, shown to users that enter all apps for the
|
||||
* first time.
|
||||
*/
|
||||
public class AllAppsTipView extends AbstractFloatingView {
|
||||
public class AllAppsTipView {
|
||||
|
||||
private static final String ALL_APPS_TIP_SEEN = "launcher.all_apps_tip_seen";
|
||||
private static final long AUTO_CLOSE_TIMEOUT_MILLIS = 10 * 1000;
|
||||
private static final long SHOW_DELAY_MS = 200;
|
||||
private static final long SHOW_DURATION_MS = 300;
|
||||
private static final long HIDE_DURATION_MS = 100;
|
||||
|
||||
private final Launcher mLauncher;
|
||||
private final Handler mHandler = new Handler();
|
||||
|
||||
private AllAppsTipView(Context context, AttributeSet attrs) {
|
||||
this(context, attrs, 0);
|
||||
}
|
||||
|
||||
private AllAppsTipView(Context context, AttributeSet attrs, int defStyleAttr) {
|
||||
super(context, attrs, defStyleAttr);
|
||||
setOrientation(LinearLayout.VERTICAL);
|
||||
|
||||
mLauncher = Launcher.getLauncher(context);
|
||||
|
||||
init(context);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean onControllerInterceptTouchEvent(MotionEvent ev) {
|
||||
if (ev.getAction() == MotionEvent.ACTION_DOWN) {
|
||||
close(true);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void handleClose(boolean animate) {
|
||||
if (mIsOpen) {
|
||||
if (animate) {
|
||||
animate().alpha(0f)
|
||||
.withLayer()
|
||||
.setStartDelay(0)
|
||||
.setDuration(HIDE_DURATION_MS)
|
||||
.setInterpolator(Interpolators.ACCEL)
|
||||
.withEndAction(() -> mLauncher.getDragLayer().removeView(this))
|
||||
.start();
|
||||
} else {
|
||||
animate().cancel();
|
||||
mLauncher.getDragLayer().removeView(this);
|
||||
}
|
||||
mLauncher.getSharedPrefs().edit().putBoolean(ALL_APPS_TIP_SEEN, true).apply();
|
||||
mIsOpen = false;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void logActionCommand(int command) {
|
||||
}
|
||||
|
||||
@Override
|
||||
protected boolean isOfType(int type) {
|
||||
return (type & TYPE_ON_BOARD_POPUP) != 0;
|
||||
}
|
||||
|
||||
private void init(Context context) {
|
||||
inflate(context, R.layout.arrow_toast, this);
|
||||
|
||||
TextView textView = findViewById(R.id.text);
|
||||
textView.setText(R.string.all_apps_prediction_tip);
|
||||
|
||||
View dismissButton = findViewById(R.id.dismiss);
|
||||
dismissButton.setOnClickListener(view -> {
|
||||
mLauncher.getUserEventDispatcher().logActionTip(
|
||||
LauncherEventUtil.DISMISS, ALL_APPS_PREDICTION_TIPS);
|
||||
handleClose(true);
|
||||
});
|
||||
|
||||
View arrowView = findViewById(R.id.arrow);
|
||||
ViewGroup.LayoutParams arrowLp = arrowView.getLayoutParams();
|
||||
ShapeDrawable arrowDrawable = new ShapeDrawable(TriangleShape.create(
|
||||
arrowLp.width, arrowLp.height, false));
|
||||
Paint arrowPaint = arrowDrawable.getPaint();
|
||||
TypedValue typedValue = new TypedValue();
|
||||
context.getTheme().resolveAttribute(android.R.attr.colorAccent, typedValue, true);
|
||||
arrowPaint.setColor(ContextCompat.getColor(getContext(), typedValue.resourceId));
|
||||
// The corner path effect won't be reflected in the shadow, but shouldn't be noticeable.
|
||||
arrowPaint.setPathEffect(new CornerPathEffect(
|
||||
context.getResources().getDimension(R.dimen.arrow_toast_corner_radius)));
|
||||
arrowView.setBackground(arrowDrawable);
|
||||
|
||||
mIsOpen = true;
|
||||
|
||||
mHandler.postDelayed(() -> handleClose(true), AUTO_CLOSE_TIMEOUT_MILLIS);
|
||||
}
|
||||
|
||||
private static boolean showAllAppsTipIfNecessary(Launcher launcher) {
|
||||
FloatingHeaderView floatingHeaderView = launcher.getAppsView().getFloatingHeaderView();
|
||||
@@ -156,28 +53,15 @@ public class AllAppsTipView extends AbstractFloatingView {
|
||||
return false;
|
||||
}
|
||||
|
||||
AllAppsTipView allAppsTipView = new AllAppsTipView(launcher.getAppsView().getContext(),
|
||||
null);
|
||||
launcher.getDragLayer().addView(allAppsTipView);
|
||||
int[] coords = new int[2];
|
||||
floatingHeaderView.findFixedRowByType(PredictionRowView.class).getLocationOnScreen(coords);
|
||||
ArrowTipView arrowTipView = new ArrowTipView(launcher).setOnClosedCallback(() -> {
|
||||
launcher.getSharedPrefs().edit().putBoolean(ALL_APPS_TIP_SEEN, true).apply();
|
||||
launcher.getUserEventDispatcher().logActionTip(LauncherEventUtil.DISMISS,
|
||||
ALL_APPS_PREDICTION_TIPS);
|
||||
});
|
||||
arrowTipView.show(launcher.getString(R.string.all_apps_prediction_tip), coords[1]);
|
||||
|
||||
DragLayer.LayoutParams params = (DragLayer.LayoutParams) allAppsTipView.getLayoutParams();
|
||||
params.gravity = Gravity.CENTER_HORIZONTAL;
|
||||
|
||||
int top = floatingHeaderView.findFixedRowByType(PredictionRowView.class).getTop();
|
||||
allAppsTipView.setY(top - launcher.getResources().getDimensionPixelSize(
|
||||
R.dimen.all_apps_tip_bottom_margin));
|
||||
|
||||
allAppsTipView.setAlpha(0);
|
||||
allAppsTipView.animate()
|
||||
.alpha(1f)
|
||||
.withLayer()
|
||||
.setStartDelay(SHOW_DELAY_MS)
|
||||
.setDuration(SHOW_DURATION_MS)
|
||||
.setInterpolator(Interpolators.DEACCEL)
|
||||
.start();
|
||||
|
||||
launcher.getUserEventDispatcher().logActionTip(
|
||||
LauncherEventUtil.VISIBLE, ALL_APPS_PREDICTION_TIPS);
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
+158
-22
@@ -23,23 +23,28 @@ import android.content.Intent;
|
||||
import android.content.res.Configuration;
|
||||
import android.os.Build;
|
||||
import android.view.View;
|
||||
import android.view.ViewGroup;
|
||||
|
||||
import androidx.core.app.NotificationCompat;
|
||||
|
||||
import com.android.launcher3.CellLayout;
|
||||
import com.android.launcher3.FolderInfo;
|
||||
import com.android.launcher3.Hotseat;
|
||||
import com.android.launcher3.InvariantDeviceProfile;
|
||||
import com.android.launcher3.ItemInfo;
|
||||
import com.android.launcher3.Launcher;
|
||||
import com.android.launcher3.LauncherSettings;
|
||||
import com.android.launcher3.R;
|
||||
import com.android.launcher3.Workspace;
|
||||
import com.android.launcher3.WorkspaceItemInfo;
|
||||
import com.android.launcher3.WorkspaceLayoutManager;
|
||||
import com.android.launcher3.config.FeatureFlags;
|
||||
import com.android.launcher3.uioverrides.QuickstepLauncher;
|
||||
import com.android.launcher3.util.ActivityTracker;
|
||||
import com.android.launcher3.util.GridOccupancy;
|
||||
import com.android.launcher3.util.IntArray;
|
||||
import com.android.launcher3.util.Themes;
|
||||
|
||||
import java.util.ArrayDeque;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
@@ -52,48 +57,179 @@ public class HotseatEduController {
|
||||
private static final int ONBOARDING_NOTIFICATION_ID = 7641;
|
||||
|
||||
private final Launcher mLauncher;
|
||||
private final NotificationManager mNotificationManager;
|
||||
private final Notification mNotification;
|
||||
private List<WorkspaceItemInfo> mPredictedApps;
|
||||
private HotseatEduDialog mActiveDialog;
|
||||
|
||||
private final NotificationManager mNotificationManager;
|
||||
private final Notification mNotification;
|
||||
private ArrayList<ItemInfo> mNewItems = new ArrayList<>();
|
||||
private IntArray mNewScreens = null;
|
||||
private Runnable mOnOnboardingComplete;
|
||||
|
||||
HotseatEduController(Launcher launcher) {
|
||||
HotseatEduController(Launcher launcher, Runnable runnable) {
|
||||
mLauncher = launcher;
|
||||
mOnOnboardingComplete = runnable;
|
||||
mNotificationManager = mLauncher.getSystemService(NotificationManager.class);
|
||||
createNotificationChannel();
|
||||
mNotification = createNotification();
|
||||
}
|
||||
|
||||
boolean migrate() {
|
||||
Workspace workspace = mLauncher.getWorkspace();
|
||||
CellLayout firstScreen = workspace.getScreenWithId(WorkspaceLayoutManager.FIRST_SCREEN_ID);
|
||||
int toPage = Workspace.FIRST_SCREEN_ID;
|
||||
int toRow = mLauncher.getDeviceProfile().inv.numRows - 1;
|
||||
if (FeatureFlags.HOTSEAT_MIGRATE_NEW_PAGE.get()) {
|
||||
toPage = workspace.getScreenIdForPageIndex(workspace.getPageCount());
|
||||
toRow = 0;
|
||||
} else if (!firstScreen.makeSpaceForHotseatMigration(true)) {
|
||||
return false;
|
||||
/**
|
||||
* Checks what type of migration should be used and migrates hotseat
|
||||
*/
|
||||
void migrate() {
|
||||
if (FeatureFlags.HOTSEAT_MIGRATE_TO_FOLDER.get()) {
|
||||
migrateToFolder();
|
||||
} else {
|
||||
migrateHotseatWhole();
|
||||
}
|
||||
ViewGroup hotseatVG = mLauncher.getHotseat().getShortcutsAndWidgets();
|
||||
for (int i = 0; i < hotseatVG.getChildCount(); i++) {
|
||||
View child = hotseatVG.getChildAt(i);
|
||||
}
|
||||
|
||||
/**
|
||||
* This migration places all non folder items in the hotseat into a folder and then moves
|
||||
* all folders in the hotseat to a workspace page that has enough empty spots.
|
||||
*
|
||||
* @return pageId that has accepted the items.
|
||||
*/
|
||||
private int migrateToFolder() {
|
||||
ArrayDeque<FolderInfo> folders = new ArrayDeque<>();
|
||||
|
||||
ArrayList<WorkspaceItemInfo> putIntoFolder = new ArrayList<>();
|
||||
|
||||
//separate folders and items that can get in folders
|
||||
for (int i = 0; i < mLauncher.getDeviceProfile().inv.numHotseatIcons; i++) {
|
||||
View view = mLauncher.getHotseat().getChildAt(i, 0);
|
||||
if (view == null) continue;
|
||||
ItemInfo info = (ItemInfo) view.getTag();
|
||||
if (info.itemType == LauncherSettings.Favorites.ITEM_TYPE_FOLDER) {
|
||||
folders.add((FolderInfo) info);
|
||||
} else if (info instanceof WorkspaceItemInfo) {
|
||||
putIntoFolder.add((WorkspaceItemInfo) info);
|
||||
}
|
||||
}
|
||||
|
||||
// create a temp folder and add non folder items to it
|
||||
if (!putIntoFolder.isEmpty()) {
|
||||
ItemInfo firstItem = putIntoFolder.get(0);
|
||||
FolderInfo folderInfo = new FolderInfo();
|
||||
folderInfo.title = "";
|
||||
mLauncher.getModelWriter().addItemToDatabase(folderInfo, firstItem.container,
|
||||
firstItem.screenId, firstItem.cellX, firstItem.cellY);
|
||||
folderInfo.contents.addAll(putIntoFolder);
|
||||
for (int i = 0; i < folderInfo.contents.size(); i++) {
|
||||
ItemInfo item = folderInfo.contents.get(i);
|
||||
item.rank = i;
|
||||
mLauncher.getModelWriter().moveItemInDatabase(item, folderInfo.id, 0,
|
||||
item.cellX, item.cellY);
|
||||
}
|
||||
folders.add(folderInfo);
|
||||
}
|
||||
mNewItems.addAll(folders);
|
||||
|
||||
return placeFoldersInWorkspace(folders);
|
||||
}
|
||||
|
||||
private int placeFoldersInWorkspace(ArrayDeque<FolderInfo> folders) {
|
||||
if (folders.isEmpty()) return 0;
|
||||
|
||||
Workspace workspace = mLauncher.getWorkspace();
|
||||
InvariantDeviceProfile idp = mLauncher.getDeviceProfile().inv;
|
||||
|
||||
GridOccupancy[] occupancyList = new GridOccupancy[workspace.getChildCount()];
|
||||
for (int i = 0; i < occupancyList.length; i++) {
|
||||
occupancyList[i] = ((CellLayout) workspace.getChildAt(i)).cloneGridOccupancy();
|
||||
}
|
||||
//scan every screen to find available spots to place folders
|
||||
int occupancyIndex = 0;
|
||||
int[] itemXY = new int[2];
|
||||
while (occupancyIndex < occupancyList.length && !folders.isEmpty()) {
|
||||
GridOccupancy occupancy = occupancyList[occupancyIndex];
|
||||
if (occupancy.findVacantCell(itemXY, 1, 1)) {
|
||||
FolderInfo info = folders.poll();
|
||||
mLauncher.getModelWriter().moveItemInDatabase(info,
|
||||
LauncherSettings.Favorites.CONTAINER_DESKTOP,
|
||||
workspace.getScreenIdForPageIndex(occupancyIndex), itemXY[0], itemXY[1]);
|
||||
occupancy.markCells(info, true);
|
||||
} else {
|
||||
occupancyIndex++;
|
||||
}
|
||||
}
|
||||
if (folders.isEmpty()) return workspace.getScreenIdForPageIndex(occupancyIndex);
|
||||
int screenId = LauncherSettings.Settings.call(mLauncher.getContentResolver(),
|
||||
LauncherSettings.Settings.METHOD_NEW_SCREEN_ID)
|
||||
.getInt(LauncherSettings.Settings.EXTRA_VALUE);
|
||||
// if all screens are full and we still have folders left, put those on a new page
|
||||
FolderInfo folderInfo;
|
||||
int col = 0;
|
||||
while ((folderInfo = folders.poll()) != null) {
|
||||
mLauncher.getModelWriter().moveItemInDatabase(folderInfo,
|
||||
LauncherSettings.Favorites.CONTAINER_DESKTOP, screenId, col++,
|
||||
idp.numRows - 1);
|
||||
}
|
||||
mNewScreens = IntArray.wrap(screenId);
|
||||
return workspace.getPageCount();
|
||||
}
|
||||
|
||||
/**
|
||||
* This migration option attempts to move the entire hotseat up to the first workspace that
|
||||
* has space to host items. If no such page is found, it moves items to a new page.
|
||||
*
|
||||
* @return pageId where items are migrated
|
||||
*/
|
||||
private int migrateHotseatWhole() {
|
||||
Workspace workspace = mLauncher.getWorkspace();
|
||||
Hotseat hotseatVG = mLauncher.getHotseat();
|
||||
|
||||
int pageId = -1;
|
||||
int toRow = 0;
|
||||
for (int i = 0; i < workspace.getPageCount(); i++) {
|
||||
CellLayout target = workspace.getScreenWithId(workspace.getScreenIdForPageIndex(i));
|
||||
if (target.makeSpaceForHotseatMigration(true)) {
|
||||
toRow = mLauncher.getDeviceProfile().inv.numRows - 1;
|
||||
pageId = i;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (pageId == -1) {
|
||||
pageId = LauncherSettings.Settings.call(mLauncher.getContentResolver(),
|
||||
LauncherSettings.Settings.METHOD_NEW_SCREEN_ID)
|
||||
.getInt(LauncherSettings.Settings.EXTRA_VALUE);
|
||||
}
|
||||
for (int i = 0; i < mLauncher.getDeviceProfile().inv.numHotseatIcons; i++) {
|
||||
View child = hotseatVG.getChildAt(i, 0);
|
||||
if (child == null || child.getTag() == null) continue;
|
||||
ItemInfo tag = (ItemInfo) child.getTag();
|
||||
mLauncher.getModelWriter().moveItemInDatabase(tag,
|
||||
LauncherSettings.Favorites.CONTAINER_DESKTOP, toPage, tag.screenId, toRow);
|
||||
LauncherSettings.Favorites.CONTAINER_DESKTOP, pageId, i, toRow);
|
||||
mNewItems.add(tag);
|
||||
}
|
||||
return true;
|
||||
return pageId;
|
||||
}
|
||||
|
||||
|
||||
void removeNotification() {
|
||||
mNotificationManager.cancel(ONBOARDING_NOTIFICATION_ID);
|
||||
}
|
||||
|
||||
void finishOnboarding() {
|
||||
mLauncher.getModel().rebindCallbacks();
|
||||
mLauncher.getHotseat().removeAllViewsInLayout();
|
||||
if (!mNewItems.isEmpty()) {
|
||||
int lastPage = mNewItems.get(mNewItems.size() - 1).screenId;
|
||||
ArrayList<ItemInfo> animated = new ArrayList<>();
|
||||
ArrayList<ItemInfo> nonAnimated = new ArrayList<>();
|
||||
|
||||
for (ItemInfo info : mNewItems) {
|
||||
if (info.screenId == lastPage) {
|
||||
animated.add(info);
|
||||
} else {
|
||||
nonAnimated.add(info);
|
||||
}
|
||||
}
|
||||
mLauncher.bindAppsAdded(mNewScreens, nonAnimated, animated);
|
||||
}
|
||||
mOnOnboardingComplete.run();
|
||||
destroy();
|
||||
mLauncher.getSharedPrefs().edit().putBoolean(KEY_HOTSEAT_EDU_SEEN, true).apply();
|
||||
removeNotification();
|
||||
}
|
||||
|
||||
void setPredictedApps(List<WorkspaceItemInfo> predictedApps) {
|
||||
|
||||
+19
-29
@@ -29,6 +29,7 @@ import android.widget.Button;
|
||||
import android.widget.TextView;
|
||||
import android.widget.Toast;
|
||||
|
||||
import com.android.launcher3.ArrowTipView;
|
||||
import com.android.launcher3.CellLayout;
|
||||
import com.android.launcher3.DeviceProfile;
|
||||
import com.android.launcher3.Insettable;
|
||||
@@ -53,20 +54,14 @@ public class HotseatEduDialog extends AbstractSlideInView implements Insettable
|
||||
private static final int DEFAULT_CLOSE_DURATION = 200;
|
||||
protected static final int FINAL_SCRIM_BG_COLOR = 0x88000000;
|
||||
|
||||
private static final int MIGRATE_SAME_PAGE = 0;
|
||||
private static final int MIGRATE_NEW_PAGE = 1;
|
||||
private static final int MIGRATE_NO_MIGRATE = 2;
|
||||
|
||||
// we use this value to keep track of migration logs as we experiment with different migrations
|
||||
private static final int MIGRATION_EXPERIMENT_IDENTIFIER = 1;
|
||||
|
||||
private final Rect mInsets = new Rect();
|
||||
private View mHotseatWrapper;
|
||||
private CellLayout mSampleHotseat;
|
||||
private TextView mEduHeading;
|
||||
private TextView mEduContent;
|
||||
private Button mDismissBtn;
|
||||
|
||||
private int mMigrationMode = MIGRATE_SAME_PAGE;
|
||||
|
||||
public void setHotseatEduController(HotseatEduController hotseatEduController) {
|
||||
mHotseatEduController = hotseatEduController;
|
||||
}
|
||||
@@ -89,8 +84,6 @@ public class HotseatEduDialog extends AbstractSlideInView implements Insettable
|
||||
super.onFinishInflate();
|
||||
mHotseatWrapper = findViewById(R.id.hotseat_wrapper);
|
||||
mSampleHotseat = findViewById(R.id.sample_prediction);
|
||||
mEduHeading = findViewById(R.id.hotseat_edu_heading);
|
||||
mEduContent = findViewById(R.id.hotseat_edu_content);
|
||||
|
||||
DeviceProfile grid = mLauncher.getDeviceProfile();
|
||||
Rect padding = grid.getHotseatLayoutPadding();
|
||||
@@ -105,25 +98,30 @@ public class HotseatEduDialog extends AbstractSlideInView implements Insettable
|
||||
mDismissBtn = findViewById(R.id.no_thanks);
|
||||
mDismissBtn.setOnClickListener(this::onDismiss);
|
||||
|
||||
// update ui to reflect which migration method is going to be used
|
||||
if (FeatureFlags.HOTSEAT_MIGRATE_TO_FOLDER.get()) {
|
||||
((TextView) findViewById(R.id.hotseat_edu_content)).setText(
|
||||
R.string.hotseat_edu_message_migrate_alt);
|
||||
}
|
||||
}
|
||||
|
||||
private void onAccept(View v) {
|
||||
if (mMigrationMode == MIGRATE_NO_MIGRATE || !mHotseatEduController.migrate()) {
|
||||
onDismiss(v);
|
||||
return;
|
||||
}
|
||||
mHotseatEduController.migrate();
|
||||
handleClose(true);
|
||||
mHotseatEduController.finishOnboarding();
|
||||
logUserAction(true);
|
||||
int toastStringRes = mMigrationMode == MIGRATE_SAME_PAGE
|
||||
//TODO: pass actual page index here.
|
||||
// Temporarily we're passing 1 for folder migration and 2 for page migration
|
||||
logUserAction(true, FeatureFlags.HOTSEAT_MIGRATE_TO_FOLDER.get() ? 1 : 2);
|
||||
int toastStringRes = !FeatureFlags.HOTSEAT_MIGRATE_TO_FOLDER.get()
|
||||
? R.string.hotseat_items_migrated : R.string.hotseat_items_migrated_alt;
|
||||
Toast.makeText(mLauncher, toastStringRes, Toast.LENGTH_LONG).show();
|
||||
}
|
||||
|
||||
private void onDismiss(View v) {
|
||||
Toast.makeText(getContext(), R.string.hotseat_no_migration, Toast.LENGTH_LONG).show();
|
||||
int top = mLauncher.getHotseat().getTop();
|
||||
new ArrowTipView(mLauncher).show(mLauncher.getString(R.string.hotseat_no_migration), top);
|
||||
mHotseatEduController.finishOnboarding();
|
||||
logUserAction(false);
|
||||
logUserAction(false, -1);
|
||||
handleClose(true);
|
||||
}
|
||||
|
||||
@@ -155,7 +153,7 @@ public class HotseatEduDialog extends AbstractSlideInView implements Insettable
|
||||
mLauncher.getDeviceProfile().hotseatBarSizePx + insets.bottom;
|
||||
}
|
||||
|
||||
private void logUserAction(boolean migrated) {
|
||||
private void logUserAction(boolean migrated, int pageIndex) {
|
||||
LauncherLogProto.Action action = new LauncherLogProto.Action();
|
||||
LauncherLogProto.Target target = new LauncherLogProto.Target();
|
||||
action.type = LauncherLogProto.Action.Type.TOUCH;
|
||||
@@ -164,8 +162,9 @@ public class HotseatEduDialog extends AbstractSlideInView implements Insettable
|
||||
target.tipType = LauncherLogProto.TipType.HYBRID_HOTSEAT;
|
||||
target.controlType = migrated ? LauncherLogProto.ControlType.HYBRID_HOTSEAT_ACCEPTED
|
||||
: HYBRID_HOTSEAT_CANCELED;
|
||||
target.rank = MIGRATION_EXPERIMENT_IDENTIFIER;
|
||||
// encoding migration type on pageIndex
|
||||
target.pageIndex = mMigrationMode;
|
||||
target.pageIndex = pageIndex;
|
||||
LauncherLogProto.LauncherEvent event = newLauncherEvent(action, target);
|
||||
UserEventDispatcher.newInstance(getContext()).dispatchUserEvent(event, null);
|
||||
}
|
||||
@@ -218,15 +217,6 @@ public class HotseatEduDialog extends AbstractSlideInView implements Insettable
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void attachToContainer() {
|
||||
super.attachToContainer();
|
||||
if (FeatureFlags.HOTSEAT_MIGRATE_NEW_PAGE.get()) {
|
||||
mEduContent.setText(R.string.hotseat_edu_message_migrate_alt);
|
||||
mMigrationMode = MIGRATE_NEW_PAGE;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Opens User education dialog with a list of suggested apps
|
||||
*/
|
||||
|
||||
+6
-8
@@ -94,7 +94,6 @@ public class HotseatPredictionController implements DragController.DragListener,
|
||||
private static final String BUNDLE_KEY_WORKSPACE = "workspace_apps";
|
||||
|
||||
private static final String PREDICTION_CLIENT = "hotseat";
|
||||
|
||||
private DropTarget.DragObject mDragObject;
|
||||
private int mHotSeatItemsCount;
|
||||
private int mPredictedSpotsCount = 0;
|
||||
@@ -115,6 +114,7 @@ public class HotseatPredictionController implements DragController.DragListener,
|
||||
|
||||
private List<PredictedAppIcon.PredictedIconOutlineDrawing> mOutlineDrawings = new ArrayList<>();
|
||||
|
||||
|
||||
private final View.OnLongClickListener mPredictionLongClickListener = v -> {
|
||||
if (!ItemLongClickListener.canStartDrag(mLauncher)) return false;
|
||||
if (mLauncher.getWorkspace().isSwitchingState()) return false;
|
||||
@@ -276,12 +276,10 @@ public class HotseatPredictionController implements DragController.DragListener,
|
||||
.build());
|
||||
mAppPredictor.registerPredictionUpdates(mLauncher.getMainExecutor(),
|
||||
this::setPredictedApps);
|
||||
setPauseUIUpdate(false);
|
||||
|
||||
if (!isReady()) {
|
||||
if (mHotseatEduController != null) {
|
||||
mHotseatEduController.destroy();
|
||||
}
|
||||
mHotseatEduController = new HotseatEduController(mLauncher);
|
||||
mHotseatEduController = new HotseatEduController(mLauncher, this::createPredictor);
|
||||
}
|
||||
mAppPredictor.requestPredictionUpdate();
|
||||
}
|
||||
@@ -327,7 +325,7 @@ public class HotseatPredictionController implements DragController.DragListener,
|
||||
mComponentKeyMappers.add(new ComponentKeyMapper(key, mDynamicItemCache));
|
||||
}
|
||||
predictionLog.append("]");
|
||||
FileLog.d(TAG, predictionLog.toString());
|
||||
if (false) FileLog.d(TAG, predictionLog.toString());
|
||||
updateDependencies();
|
||||
if (isReady()) {
|
||||
fillGapsWithPrediction();
|
||||
@@ -488,7 +486,6 @@ public class HotseatPredictionController implements DragController.DragListener,
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public void onDragEnd() {
|
||||
if (mDragObject == null) {
|
||||
@@ -564,7 +561,8 @@ public class HotseatPredictionController implements DragController.DragListener,
|
||||
}
|
||||
|
||||
@Override
|
||||
public void reapplyItemInfo(ItemInfoWithIcon info) {}
|
||||
public void reapplyItemInfo(ItemInfoWithIcon info) {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onDropCompleted(View target, DropTarget.DragObject d, boolean success) {
|
||||
|
||||
+2
-3
@@ -19,7 +19,6 @@ import android.content.Context;
|
||||
|
||||
import com.android.launcher3.AbstractFloatingView;
|
||||
import com.android.launcher3.Launcher;
|
||||
import com.android.launcher3.R;
|
||||
import com.android.launcher3.allapps.AllAppsTransitionController;
|
||||
import com.android.launcher3.userevent.nano.LauncherLogProto;
|
||||
import com.android.quickstep.util.LayoutUtils;
|
||||
@@ -107,7 +106,7 @@ public class BackgroundAppState extends OverviewState {
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getBackgroundBlurRadius(Context context) {
|
||||
return context.getResources().getInteger(R.integer.app_background_blur_radius);
|
||||
public float getDepth(Context context) {
|
||||
return 1f;
|
||||
}
|
||||
}
|
||||
|
||||
+2
-2
@@ -207,8 +207,8 @@ public class OverviewState extends LauncherState {
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getBackgroundBlurRadius(Context context) {
|
||||
return context.getResources().getInteger(R.integer.overview_background_blur_radius);
|
||||
public float getDepth(Context context) {
|
||||
return 1f;
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
+2
-2
@@ -120,8 +120,8 @@ public class FlingAndHoldTouchController extends PortraitStatesTouchController {
|
||||
mPeekAnim.start();
|
||||
VibratorWrapper.INSTANCE.get(mLauncher).vibrate(OVERVIEW_HAPTIC);
|
||||
|
||||
mLauncher.getDragLayer().getScrim().animateToSysuiMultiplier(isPaused ? 0 : 1,
|
||||
peekDuration, 0);
|
||||
mLauncher.getDragLayer().getScrim().createSysuiMultiplierAnim(isPaused ? 0 : 1)
|
||||
.setDuration(peekDuration).start();
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
+4
@@ -80,6 +80,9 @@ public abstract class TaskViewTouchController<T extends BaseDraggingActivity>
|
||||
}
|
||||
|
||||
private boolean canInterceptTouch() {
|
||||
if (mCurrentAnimation != null) {
|
||||
mCurrentAnimation.forceFinishIfCloseToEnd();
|
||||
}
|
||||
if (mCurrentAnimation != null) {
|
||||
// If we are already animating from a previous state, we can intercept.
|
||||
return true;
|
||||
@@ -126,6 +129,7 @@ public abstract class TaskViewTouchController<T extends BaseDraggingActivity>
|
||||
|
||||
for (int i = 0; i < mRecentsView.getTaskViewCount(); i++) {
|
||||
TaskView view = mRecentsView.getTaskViewAt(i);
|
||||
|
||||
if (mRecentsView.isTaskViewVisible(view) && mActivity.getDragLayer()
|
||||
.isEventOverView(view, ev)) {
|
||||
// Disable swiping up and down if the task overlay is modal.
|
||||
|
||||
+13
-13
@@ -19,7 +19,7 @@ import static com.android.launcher3.LauncherState.BACKGROUND_APP;
|
||||
import static com.android.launcher3.LauncherState.OVERVIEW;
|
||||
import static com.android.launcher3.anim.Interpolators.FAST_OUT_SLOW_IN;
|
||||
import static com.android.launcher3.anim.Interpolators.TOUCH_RESPONSE_INTERPOLATOR;
|
||||
import static com.android.launcher3.uioverrides.BackgroundBlurController.BACKGROUND_BLUR;
|
||||
import static com.android.launcher3.uioverrides.DepthController.DEPTH;
|
||||
import static com.android.systemui.shared.system.RemoteAnimationTargetCompat.MODE_CLOSING;
|
||||
import static com.android.systemui.shared.system.RemoteAnimationTargetCompat.MODE_OPENING;
|
||||
|
||||
@@ -34,7 +34,7 @@ import android.view.View;
|
||||
import com.android.launcher3.AbstractFloatingView;
|
||||
import com.android.launcher3.BaseDraggingActivity;
|
||||
import com.android.launcher3.anim.AnimationSuccessListener;
|
||||
import com.android.launcher3.uioverrides.BackgroundBlurController;
|
||||
import com.android.launcher3.uioverrides.DepthController;
|
||||
import com.android.quickstep.util.AppWindowAnimationHelper;
|
||||
import com.android.quickstep.util.AppWindowAnimationHelper.TransformParams;
|
||||
import com.android.quickstep.util.RemoteAnimationProvider;
|
||||
@@ -105,10 +105,10 @@ final class AppToOverviewAnimationProvider<T extends BaseDraggingActivity> exten
|
||||
mRecentsView.setRunningTaskIconScaledDown(true);
|
||||
}
|
||||
|
||||
BackgroundBlurController blurController = mActivityInterface.getBackgroundBlurController();
|
||||
if (blurController != null) {
|
||||
DepthController depthController = mActivityInterface.getDepthController();
|
||||
if (depthController != null) {
|
||||
// Update the surface to be the lowest closing app surface
|
||||
blurController.setSurfaceToLauncher(mRecentsView);
|
||||
depthController.setSurfaceToLauncher(mRecentsView);
|
||||
}
|
||||
|
||||
AnimatorSet anim = new AnimatorSet();
|
||||
@@ -124,7 +124,7 @@ final class AppToOverviewAnimationProvider<T extends BaseDraggingActivity> exten
|
||||
if (mActivity == null) {
|
||||
Log.e(TAG, "Animation created, before activity");
|
||||
anim.play(ValueAnimator.ofInt(0, 1).setDuration(RECENTS_LAUNCH_DURATION))
|
||||
.with(createBackgroundBlurAnimator(blurController));
|
||||
.with(createDepthAnimator(depthController));
|
||||
return anim;
|
||||
}
|
||||
|
||||
@@ -136,7 +136,7 @@ final class AppToOverviewAnimationProvider<T extends BaseDraggingActivity> exten
|
||||
if (runningTaskTarget == null) {
|
||||
Log.e(TAG, "No closing app");
|
||||
anim.play(ValueAnimator.ofInt(0, 1).setDuration(RECENTS_LAUNCH_DURATION))
|
||||
.with(createBackgroundBlurAnimator(blurController));
|
||||
.with(createDepthAnimator(depthController));
|
||||
return anim;
|
||||
}
|
||||
|
||||
@@ -184,7 +184,7 @@ final class AppToOverviewAnimationProvider<T extends BaseDraggingActivity> exten
|
||||
});
|
||||
}
|
||||
anim.play(valueAnimator)
|
||||
.with(createBackgroundBlurAnimator(blurController));
|
||||
.with(createDepthAnimator(depthController));
|
||||
return anim;
|
||||
}
|
||||
|
||||
@@ -197,14 +197,14 @@ final class AppToOverviewAnimationProvider<T extends BaseDraggingActivity> exten
|
||||
return RECENTS_LAUNCH_DURATION;
|
||||
}
|
||||
|
||||
private Animator createBackgroundBlurAnimator(BackgroundBlurController blurController) {
|
||||
if (blurController == null) {
|
||||
private Animator createDepthAnimator(DepthController depthController) {
|
||||
if (depthController == null) {
|
||||
// Dummy animation
|
||||
return ValueAnimator.ofInt(0);
|
||||
}
|
||||
return ObjectAnimator.ofInt(blurController, BACKGROUND_BLUR,
|
||||
BACKGROUND_APP.getBackgroundBlurRadius(mActivity),
|
||||
OVERVIEW.getBackgroundBlurRadius(mActivity))
|
||||
return ObjectAnimator.ofFloat(depthController, DEPTH,
|
||||
BACKGROUND_APP.getDepth(mActivity),
|
||||
OVERVIEW.getDepth(mActivity))
|
||||
.setDuration(RECENTS_LAUNCH_DURATION);
|
||||
}
|
||||
}
|
||||
|
||||
+12
-13
@@ -56,8 +56,8 @@ import com.android.launcher3.allapps.DiscoveryBounce;
|
||||
import com.android.launcher3.anim.AnimatorPlaybackController;
|
||||
import com.android.launcher3.appprediction.PredictionUiStateManager;
|
||||
import com.android.launcher3.touch.PagedOrientationHandler;
|
||||
import com.android.launcher3.uioverrides.BackgroundBlurController;
|
||||
import com.android.launcher3.uioverrides.BackgroundBlurController.ClampedBlurProperty;
|
||||
import com.android.launcher3.uioverrides.DepthController;
|
||||
import com.android.launcher3.uioverrides.DepthController.ClampedDepthProperty;
|
||||
import com.android.launcher3.userevent.nano.LauncherLogProto;
|
||||
import com.android.launcher3.views.FloatingIconView;
|
||||
import com.android.quickstep.SysUINavigationMode.Mode;
|
||||
@@ -330,18 +330,17 @@ public final class LauncherActivityInterface implements BaseActivityInterface<La
|
||||
endState.getVerticalProgress(activity)));
|
||||
}
|
||||
|
||||
// Animate the blur
|
||||
BackgroundBlurController blurController = getBackgroundBlurController();
|
||||
int fromBlurRadius = fromState.getBackgroundBlurRadius(activity);
|
||||
int toBlurRadius = endState.getBackgroundBlurRadius(activity);
|
||||
Animator backgroundBlur = ObjectAnimator.ofInt(blurController,
|
||||
new ClampedBlurProperty(toBlurRadius, fromBlurRadius),
|
||||
fromBlurRadius, toBlurRadius);
|
||||
anim.play(backgroundBlur);
|
||||
// Animate the blur and wallpaper zoom
|
||||
DepthController depthController = getDepthController();
|
||||
float fromDepthRatio = fromState.getDepth(activity);
|
||||
float toDepthRatio = endState.getDepth(activity);
|
||||
Animator depthAnimator = ObjectAnimator.ofFloat(depthController,
|
||||
new ClampedDepthProperty(fromDepthRatio, toDepthRatio),
|
||||
fromDepthRatio, toDepthRatio);
|
||||
anim.play(depthAnimator);
|
||||
|
||||
playScaleDownAnim(anim, activity, fromState, endState);
|
||||
|
||||
|
||||
anim.setDuration(transitionLength * 2);
|
||||
anim.setInterpolator(LINEAR);
|
||||
AnimatorPlaybackController controller =
|
||||
@@ -558,11 +557,11 @@ public final class LauncherActivityInterface implements BaseActivityInterface<La
|
||||
|
||||
@Nullable
|
||||
@Override
|
||||
public BackgroundBlurController getBackgroundBlurController() {
|
||||
public DepthController getDepthController() {
|
||||
BaseQuickstepLauncher launcher = getCreatedActivity();
|
||||
if (launcher == null) {
|
||||
return null;
|
||||
}
|
||||
return launcher.getBackgroundBlurController();
|
||||
return launcher.getDepthController();
|
||||
}
|
||||
}
|
||||
@@ -294,7 +294,7 @@ public class LauncherSwipeHandler<T extends BaseDraggingActivity>
|
||||
}
|
||||
|
||||
setupRecentsViewUi();
|
||||
mActivityInterface.getBackgroundBlurController().setSurfaceToLauncher(mRecentsView);
|
||||
mActivityInterface.getDepthController().setSurfaceToLauncher(mRecentsView);
|
||||
|
||||
if (mDeviceState.getNavMode() == TWO_BUTTONS) {
|
||||
// If the device is in two button mode, swiping up will show overview with predictions
|
||||
|
||||
@@ -186,7 +186,7 @@ public final class RecentsActivity extends BaseRecentsActivity {
|
||||
AppWindowAnimationHelper helper = new AppWindowAnimationHelper(
|
||||
mFallbackRecentsView.getPagedViewOrientedState(), this);
|
||||
Animator recentsAnimator = getRecentsWindowAnimator(taskView, !activityClosing, appTargets,
|
||||
wallpaperTargets, null /* backgroundBlurController */,
|
||||
wallpaperTargets, null /* depthController */,
|
||||
helper);
|
||||
target.play(recentsAnimator.setDuration(RECENTS_LAUNCH_DURATION));
|
||||
|
||||
|
||||
@@ -19,7 +19,7 @@ import static com.android.launcher3.LauncherState.BACKGROUND_APP;
|
||||
import static com.android.launcher3.anim.Interpolators.LINEAR;
|
||||
import static com.android.launcher3.anim.Interpolators.TOUCH_RESPONSE_INTERPOLATOR;
|
||||
import static com.android.launcher3.config.FeatureFlags.ENABLE_QUICKSTEP_LIVE_TILE;
|
||||
import static com.android.launcher3.uioverrides.BackgroundBlurController.BACKGROUND_BLUR;
|
||||
import static com.android.launcher3.uioverrides.DepthController.DEPTH;
|
||||
import static com.android.systemui.shared.system.RemoteAnimationTargetCompat.MODE_OPENING;
|
||||
|
||||
import android.animation.Animator;
|
||||
@@ -35,7 +35,7 @@ import com.android.launcher3.BaseActivity;
|
||||
import com.android.launcher3.BaseDraggingActivity;
|
||||
import com.android.launcher3.ItemInfo;
|
||||
import com.android.launcher3.Utilities;
|
||||
import com.android.launcher3.uioverrides.BackgroundBlurController;
|
||||
import com.android.launcher3.uioverrides.DepthController;
|
||||
import com.android.quickstep.util.AppWindowAnimationHelper;
|
||||
import com.android.quickstep.util.MultiValueUpdateListener;
|
||||
import com.android.quickstep.views.RecentsView;
|
||||
@@ -123,7 +123,7 @@ public final class TaskViewUtils {
|
||||
public static Animator getRecentsWindowAnimator(TaskView v, boolean skipViewChanges,
|
||||
RemoteAnimationTargetCompat[] appTargets,
|
||||
RemoteAnimationTargetCompat[] wallpaperTargets,
|
||||
BackgroundBlurController backgroundBlurController,
|
||||
DepthController depthController,
|
||||
final AppWindowAnimationHelper inOutHelper) {
|
||||
SyncRtSurfaceTransactionApplierCompat applier =
|
||||
new SyncRtSurfaceTransactionApplierCompat(v);
|
||||
@@ -215,9 +215,9 @@ public final class TaskViewUtils {
|
||||
}
|
||||
});
|
||||
|
||||
if (backgroundBlurController != null) {
|
||||
ObjectAnimator backgroundRadiusAnim = ObjectAnimator.ofInt(backgroundBlurController,
|
||||
BACKGROUND_BLUR, BACKGROUND_APP.getBackgroundBlurRadius(v.getContext()));
|
||||
if (depthController != null) {
|
||||
ObjectAnimator backgroundRadiusAnim = ObjectAnimator.ofFloat(depthController,
|
||||
DEPTH, BACKGROUND_APP.getDepth(v.getContext()));
|
||||
animatorSet.playTogether(appAnimator, backgroundRadiusAnim);
|
||||
} else {
|
||||
animatorSet.play(appAnimator);
|
||||
|
||||
+13
-3
@@ -15,7 +15,9 @@
|
||||
*/
|
||||
package com.android.quickstep;
|
||||
|
||||
import static android.view.MotionEvent.ACTION_CANCEL;
|
||||
import static android.view.MotionEvent.ACTION_DOWN;
|
||||
import static android.view.MotionEvent.ACTION_UP;
|
||||
|
||||
import static com.android.launcher3.config.FeatureFlags.ENABLE_QUICKSTEP_LIVE_TILE;
|
||||
import static com.android.launcher3.util.Executors.MAIN_EXECUTOR;
|
||||
@@ -435,7 +437,8 @@ public class TouchInteractionService extends Service implements PluginListener<O
|
||||
TraceHelper.FLAG_ALLOW_BINDER_TRACKING);
|
||||
mDeviceState.setOrientationTransformIfNeeded(event);
|
||||
|
||||
if (event.getAction() == ACTION_DOWN) {
|
||||
final int action = event.getAction();
|
||||
if (action == ACTION_DOWN) {
|
||||
GestureState newGestureState = new GestureState(mOverviewComponentObserver,
|
||||
ActiveGestureLog.INSTANCE.generateAndSetLogId());
|
||||
newGestureState.updateRunningTask(TraceHelper.whitelistIpcs("getRunningTask.0",
|
||||
@@ -468,6 +471,13 @@ public class TouchInteractionService extends Service implements PluginListener<O
|
||||
|
||||
ActiveGestureLog.INSTANCE.addLog("onMotionEvent", event.getActionMasked());
|
||||
mUncheckedConsumer.onMotionEvent(event);
|
||||
|
||||
if (action == ACTION_UP || action == ACTION_CANCEL) {
|
||||
if (mConsumer != null && !mConsumer.isConsumerDetachedFromGesture()) {
|
||||
onConsumerInactive(mConsumer);
|
||||
}
|
||||
}
|
||||
|
||||
TraceHelper.INSTANCE.endFlagsOverride(traceToken);
|
||||
}
|
||||
|
||||
@@ -661,8 +671,8 @@ public class TouchInteractionService extends Service implements PluginListener<O
|
||||
*/
|
||||
private void onConsumerInactive(InputConsumer caller) {
|
||||
if (mConsumer == caller) {
|
||||
mConsumer = mResetGestureInputConsumer;
|
||||
mUncheckedConsumer = mConsumer;
|
||||
mConsumer = mUncheckedConsumer = mResetGestureInputConsumer;
|
||||
mGestureState = new GestureState();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
+2
-1
@@ -81,7 +81,8 @@ public class RecentsRootView extends BaseDragLayer<RecentsActivity> {
|
||||
if (!insets.equals(mInsets)) {
|
||||
super.setInsets(insets);
|
||||
}
|
||||
setBackground(insets.top == 0 ? null
|
||||
setBackground(insets.top == 0 || !mAllowSysuiScrims
|
||||
? null
|
||||
: Themes.getAttrDrawable(getContext(), R.attr.workspaceStatusBarScrim));
|
||||
}
|
||||
|
||||
|
||||
+5
@@ -24,6 +24,11 @@ public abstract class DelegateInputConsumer implements InputConsumer {
|
||||
mState = STATE_INACTIVE;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isConsumerDetachedFromGesture() {
|
||||
return mDelegate.isConsumerDetachedFromGesture();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean allowInterceptByParent() {
|
||||
return mDelegate.allowInterceptByParent() && mState != STATE_ACTIVE;
|
||||
|
||||
+5
@@ -160,6 +160,11 @@ public class OtherActivityInputConsumer extends ContextWrapper implements InputC
|
||||
return TYPE_OTHER_ACTIVITY;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isConsumerDetachedFromGesture() {
|
||||
return true;
|
||||
}
|
||||
|
||||
private void forceCancelGesture(MotionEvent ev) {
|
||||
int action = ev.getAction();
|
||||
ev.setAction(ACTION_CANCEL);
|
||||
|
||||
+6
@@ -126,6 +126,8 @@ public class StaggeredWorkspaceAnim {
|
||||
addScrimAnimationForState(launcher, NORMAL, ALPHA_DURATION_MS);
|
||||
}
|
||||
|
||||
mAnimators.play(launcher.getDragLayer().getScrim().createSysuiMultiplierAnim(0f, 1f)
|
||||
.setDuration(ALPHA_DURATION_MS));
|
||||
mAnimators.addListener(new AnimatorListenerAdapter() {
|
||||
@Override
|
||||
public void onAnimationEnd(Animator animation) {
|
||||
@@ -153,6 +155,10 @@ public class StaggeredWorkspaceAnim {
|
||||
launcher.<RecentsView>getOverviewPanel().getScroller().forceFinished(true);
|
||||
}
|
||||
|
||||
public AnimatorSet getAnimators() {
|
||||
return mAnimators;
|
||||
}
|
||||
|
||||
/**
|
||||
* Starts the animation.
|
||||
*/
|
||||
|
||||
+4
-4
@@ -46,9 +46,9 @@ import com.android.launcher3.R;
|
||||
import com.android.launcher3.anim.Interpolators;
|
||||
import com.android.launcher3.appprediction.PredictionUiStateManager;
|
||||
import com.android.launcher3.appprediction.PredictionUiStateManager.Client;
|
||||
import com.android.launcher3.states.RotationHelper;
|
||||
import com.android.launcher3.uioverrides.BackgroundBlurController;
|
||||
import com.android.launcher3.config.FeatureFlags;
|
||||
import com.android.launcher3.states.RotationHelper;
|
||||
import com.android.launcher3.uioverrides.DepthController;
|
||||
import com.android.launcher3.uioverrides.plugins.PluginManagerWrapper;
|
||||
import com.android.launcher3.util.TraceHelper;
|
||||
import com.android.launcher3.views.ScrimView;
|
||||
@@ -409,7 +409,7 @@ public class LauncherRecentsView extends RecentsView<Launcher> implements StateL
|
||||
}
|
||||
|
||||
@Override
|
||||
protected BackgroundBlurController getBackgroundBlurController() {
|
||||
return mActivity.getBackgroundBlurController();
|
||||
protected DepthController getDepthController() {
|
||||
return mActivity.getDepthController();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -19,6 +19,7 @@ package com.android.quickstep.views;
|
||||
import static com.android.launcher3.BaseActivity.STATE_HANDLER_INVISIBILITY_FLAGS;
|
||||
import static com.android.launcher3.InvariantDeviceProfile.CHANGE_FLAG_ICON_PARAMS;
|
||||
import static com.android.launcher3.LauncherAnimUtils.SCALE_PROPERTY;
|
||||
import static com.android.launcher3.LauncherAnimUtils.VIEW_ALPHA;
|
||||
import static com.android.launcher3.LauncherState.BACKGROUND_APP;
|
||||
import static com.android.launcher3.Utilities.EDGE_NAV_BAR;
|
||||
import static com.android.launcher3.Utilities.squaredHypot;
|
||||
@@ -29,7 +30,7 @@ import static com.android.launcher3.anim.Interpolators.FAST_OUT_SLOW_IN;
|
||||
import static com.android.launcher3.anim.Interpolators.LINEAR;
|
||||
import static com.android.launcher3.config.FeatureFlags.ENABLE_OVERVIEW_ACTIONS;
|
||||
import static com.android.launcher3.config.FeatureFlags.ENABLE_QUICKSTEP_LIVE_TILE;
|
||||
import static com.android.launcher3.uioverrides.BackgroundBlurController.BACKGROUND_BLUR;
|
||||
import static com.android.launcher3.uioverrides.DepthController.DEPTH;
|
||||
import static com.android.launcher3.uioverrides.touchcontrollers.TaskViewTouchController.SUCCESS_TRANSITION_PROGRESS;
|
||||
import static com.android.launcher3.userevent.nano.LauncherLogProto.Action.Touch.TAP;
|
||||
import static com.android.launcher3.userevent.nano.LauncherLogProto.ControlType.CLEAR_ALL_BUTTON;
|
||||
@@ -68,7 +69,6 @@ import android.view.HapticFeedbackConstants;
|
||||
import android.view.KeyEvent;
|
||||
import android.view.LayoutInflater;
|
||||
import android.view.MotionEvent;
|
||||
import android.view.OrientationEventListener;
|
||||
import android.view.View;
|
||||
import android.view.ViewDebug;
|
||||
import android.view.ViewGroup;
|
||||
@@ -88,7 +88,6 @@ import com.android.launcher3.LauncherState;
|
||||
import com.android.launcher3.PagedView;
|
||||
import com.android.launcher3.R;
|
||||
import com.android.launcher3.Utilities;
|
||||
import com.android.launcher3.anim.AnimationSuccessListener;
|
||||
import com.android.launcher3.anim.AnimatorPlaybackController;
|
||||
import com.android.launcher3.anim.PendingAnimation;
|
||||
import com.android.launcher3.anim.PendingAnimation.EndState;
|
||||
@@ -96,11 +95,10 @@ import com.android.launcher3.anim.PropertyListBuilder;
|
||||
import com.android.launcher3.anim.SpringProperty;
|
||||
import com.android.launcher3.compat.AccessibilityManagerCompat;
|
||||
import com.android.launcher3.config.FeatureFlags;
|
||||
import com.android.launcher3.dragndrop.DragLayer;
|
||||
import com.android.launcher3.graphics.RotationMode;
|
||||
import com.android.launcher3.states.RotationHelper;
|
||||
import com.android.launcher3.touch.PagedOrientationHandler.CurveProperties;
|
||||
import com.android.launcher3.uioverrides.BackgroundBlurController;
|
||||
import com.android.launcher3.uioverrides.DepthController;
|
||||
import com.android.launcher3.userevent.nano.LauncherLogProto;
|
||||
import com.android.launcher3.userevent.nano.LauncherLogProto.Action.Direction;
|
||||
import com.android.launcher3.userevent.nano.LauncherLogProto.Action.Touch;
|
||||
@@ -720,6 +718,9 @@ public abstract class RecentsView<T extends BaseActivity> extends PagedView impl
|
||||
for (int i = 0; i < taskCount; i++) {
|
||||
getTaskViewAt(i).setFullscreenProgress(mFullscreenProgress);
|
||||
}
|
||||
if (mActionsView != null) {
|
||||
mActionsView.setVisibility(fullscreenProgress == 0 ? VISIBLE : INVISIBLE);
|
||||
}
|
||||
}
|
||||
|
||||
private void updateTaskStackListenerState() {
|
||||
@@ -1165,7 +1166,9 @@ public abstract class RecentsView<T extends BaseActivity> extends PagedView impl
|
||||
}
|
||||
|
||||
private void addDismissedTaskAnimations(View taskView, long duration, PendingAnimation anim) {
|
||||
anim.add(ObjectAnimator.ofFloat(taskView, ALPHA, 0).setDuration(duration), ACCEL_2);
|
||||
// Use setFloat instead of setViewAlpha as we want to keep the view visible even when it's
|
||||
// alpha is set to 0 so that it can be recycled in the view pool properly
|
||||
anim.setFloat(taskView, VIEW_ALPHA, 0, ACCEL_2);
|
||||
FloatProperty<View> secondaryViewTranslate =
|
||||
mOrientationHandler.getSecondaryViewTranslate();
|
||||
int secondaryTaskDimension = mOrientationHandler.getSecondaryDimension(taskView);
|
||||
@@ -1257,9 +1260,7 @@ public abstract class RecentsView<T extends BaseActivity> extends PagedView impl
|
||||
}
|
||||
|
||||
if (needsCurveUpdates) {
|
||||
ValueAnimator va = ValueAnimator.ofFloat(0, 1);
|
||||
va.addUpdateListener((a) -> updateCurveProperties());
|
||||
anim.add(va);
|
||||
anim.addOnFrameCallback(this::updateCurveProperties);
|
||||
}
|
||||
|
||||
// Add a tiny bit of translation Z, so that it draws on top of other views
|
||||
@@ -1279,6 +1280,7 @@ public abstract class RecentsView<T extends BaseActivity> extends PagedView impl
|
||||
}
|
||||
}
|
||||
|
||||
@SuppressWarnings("WrongCall")
|
||||
private void onEnd(EndState endState) {
|
||||
if (endState.isSuccess) {
|
||||
if (shouldRemoveTask) {
|
||||
@@ -1290,15 +1292,18 @@ public abstract class RecentsView<T extends BaseActivity> extends PagedView impl
|
||||
pageToSnapTo == (getTaskViewCount() - 1)) {
|
||||
pageToSnapTo -= 1;
|
||||
}
|
||||
removeView(taskView);
|
||||
removeViewInLayout(taskView);
|
||||
|
||||
if (getTaskViewCount() == 0) {
|
||||
removeView(mClearAllButton);
|
||||
removeViewInLayout(mClearAllButton);
|
||||
hideActionsView();
|
||||
startHome();
|
||||
} else {
|
||||
snapToPageImmediately(pageToSnapTo);
|
||||
}
|
||||
// Update the layout synchronously so that the position of next view is
|
||||
// immediately available.
|
||||
onLayout(false /* changed */, getLeft(), getTop(), getRight(), getBottom());
|
||||
}
|
||||
resetTaskVisuals();
|
||||
mPendingAnimation = null;
|
||||
@@ -1548,6 +1553,7 @@ public abstract class RecentsView<T extends BaseActivity> extends PagedView impl
|
||||
@Override
|
||||
protected void onLayout(boolean changed, int left, int top, int right, int bottom) {
|
||||
super.onLayout(changed, left, top, right, bottom);
|
||||
|
||||
updateEmptyStateUi(changed);
|
||||
|
||||
// Set the pivot points to match the task preview center
|
||||
@@ -1702,11 +1708,11 @@ public abstract class RecentsView<T extends BaseActivity> extends PagedView impl
|
||||
appWindowAnimationHelper.prepareAnimation(mActivity.getDeviceProfile(), true /* isOpening */);
|
||||
AnimatorSet anim = createAdjacentPageAnimForTaskLaunch(tv, appWindowAnimationHelper);
|
||||
|
||||
BackgroundBlurController blurController = getBackgroundBlurController();
|
||||
if (blurController != null) {
|
||||
ObjectAnimator backgroundBlur = ObjectAnimator.ofInt(blurController, BACKGROUND_BLUR,
|
||||
BACKGROUND_APP.getBackgroundBlurRadius(mActivity));
|
||||
anim.play(backgroundBlur);
|
||||
DepthController depthController = getDepthController();
|
||||
if (depthController != null) {
|
||||
ObjectAnimator depthAnimator = ObjectAnimator.ofFloat(depthController, DEPTH,
|
||||
BACKGROUND_APP.getDepth(mActivity));
|
||||
anim.play(depthAnimator);
|
||||
}
|
||||
anim.play(progressAnim);
|
||||
anim.setDuration(duration).setInterpolator(interpolator);
|
||||
@@ -2009,7 +2015,7 @@ public abstract class RecentsView<T extends BaseActivity> extends PagedView impl
|
||||
}
|
||||
|
||||
@Nullable
|
||||
protected BackgroundBlurController getBackgroundBlurController() {
|
||||
protected DepthController getDepthController() {
|
||||
return null;
|
||||
}
|
||||
|
||||
@@ -2029,7 +2035,6 @@ public abstract class RecentsView<T extends BaseActivity> extends PagedView impl
|
||||
void onEmptyMessageUpdated(boolean isEmpty);
|
||||
}
|
||||
|
||||
|
||||
private static class PinnedStackAnimationListener<T extends BaseActivity> extends
|
||||
IPinnedStackAnimationListener.Stub {
|
||||
private T mActivity;
|
||||
|
||||
@@ -247,8 +247,17 @@ public class TaskView extends FrameLayout implements PageCallbacks, Reusable {
|
||||
|
||||
/** Updates UI based on whether the task is modal. */
|
||||
public void updateUiForModalTask() {
|
||||
boolean isOverlayModal = isTaskOverlayModal();
|
||||
if (getRecentsView() != null) {
|
||||
getRecentsView().updateUiForModalTask(this, isTaskOverlayModal());
|
||||
getRecentsView().updateUiForModalTask(this, isOverlayModal);
|
||||
}
|
||||
// Hide footers when overlay is modal.
|
||||
if (isOverlayModal) {
|
||||
for (FooterWrapper footer : mFooters) {
|
||||
if (footer != null) {
|
||||
footer.animateHide();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -780,6 +789,22 @@ public class TaskView extends FrameLayout implements PageCallbacks, Reusable {
|
||||
animator.setDuration(100);
|
||||
animator.start();
|
||||
}
|
||||
|
||||
void animateHide() {
|
||||
ValueAnimator animator = ValueAnimator.ofFloat(0.0f, 1.0f);
|
||||
animator.addUpdateListener(anim -> {
|
||||
mFooterVerticalOffset = anim.getAnimatedFraction();
|
||||
updateFooterOffset();
|
||||
});
|
||||
animator.addListener(new AnimatorListenerAdapter() {
|
||||
@Override
|
||||
public void onAnimationEnd(Animator animation) {
|
||||
removeView(mView);
|
||||
}
|
||||
});
|
||||
animator.setDuration(100);
|
||||
animator.start();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
@@ -16,9 +16,7 @@
|
||||
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:layerType="software"
|
||||
android:background="@color/back_gesture_tutorial_background_color">
|
||||
<!--The layout is rendered on the software layer to avoid b/136158117-->
|
||||
|
||||
<ImageView
|
||||
android:id="@+id/back_gesture_tutorial_fragment_hand_coaching"
|
||||
|
||||
@@ -0,0 +1,18 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!-- 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.
|
||||
-->
|
||||
<resources>
|
||||
<color name="back_arrow_color_dark">#99000000</color>
|
||||
</resources>
|
||||
@@ -244,7 +244,7 @@ public abstract class BaseQuickstepLauncher extends Launcher
|
||||
return new StateHandler[] {
|
||||
getAllAppsController(),
|
||||
getWorkspace(),
|
||||
getBackgroundBlurController(),
|
||||
getDepthController(),
|
||||
new RecentsViewStateController(this),
|
||||
new BackButtonAlphaHandler(this)};
|
||||
}
|
||||
|
||||
@@ -16,6 +16,8 @@
|
||||
|
||||
package com.android.launcher3;
|
||||
|
||||
import static android.util.TypedValue.COMPLEX_UNIT_DIP;
|
||||
|
||||
import static com.android.launcher3.BaseActivity.INVISIBLE_ALL;
|
||||
import static com.android.launcher3.BaseActivity.INVISIBLE_BY_APP_TRANSITIONS;
|
||||
import static com.android.launcher3.BaseActivity.INVISIBLE_BY_PENDING_FLAGS;
|
||||
@@ -29,8 +31,9 @@ import static com.android.launcher3.anim.Interpolators.AGGRESSIVE_EASE;
|
||||
import static com.android.launcher3.anim.Interpolators.DEACCEL_1_7;
|
||||
import static com.android.launcher3.anim.Interpolators.EXAGGERATED_EASE;
|
||||
import static com.android.launcher3.anim.Interpolators.LINEAR;
|
||||
import static com.android.launcher3.config.FeatureFlags.KEYGUARD_ANIMATION;
|
||||
import static com.android.launcher3.dragndrop.DragLayer.ALPHA_INDEX_TRANSITIONS;
|
||||
import static com.android.launcher3.uioverrides.BackgroundBlurController.BACKGROUND_BLUR;
|
||||
import static com.android.launcher3.uioverrides.DepthController.DEPTH;
|
||||
import static com.android.launcher3.views.FloatingIconView.SHAPE_PROGRESS_DURATION;
|
||||
import static com.android.quickstep.TaskUtils.taskIsATargetWithMode;
|
||||
import static com.android.systemui.shared.system.QuickStepContract.getWindowCornerRadius;
|
||||
@@ -58,6 +61,7 @@ import android.os.CancellationSignal;
|
||||
import android.os.Handler;
|
||||
import android.os.Looper;
|
||||
import android.util.Pair;
|
||||
import android.util.TypedValue;
|
||||
import android.view.View;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
@@ -65,17 +69,18 @@ import androidx.annotation.Nullable;
|
||||
|
||||
import com.android.launcher3.DeviceProfile.OnDeviceProfileChangeListener;
|
||||
import com.android.launcher3.allapps.AllAppsTransitionController;
|
||||
import com.android.launcher3.anim.Interpolators;
|
||||
import com.android.launcher3.config.FeatureFlags;
|
||||
import com.android.launcher3.dragndrop.DragLayer;
|
||||
import com.android.launcher3.shortcuts.DeepShortcutView;
|
||||
import com.android.launcher3.uioverrides.BackgroundBlurController;
|
||||
import com.android.launcher3.uioverrides.DepthController;
|
||||
import com.android.launcher3.util.DynamicResource;
|
||||
import com.android.launcher3.util.MultiValueAlpha;
|
||||
import com.android.launcher3.util.MultiValueAlpha.AlphaProperty;
|
||||
import com.android.launcher3.views.FloatingIconView;
|
||||
import com.android.quickstep.RemoteAnimationTargets;
|
||||
import com.android.quickstep.util.MultiValueUpdateListener;
|
||||
import com.android.quickstep.util.RemoteAnimationProvider;
|
||||
import com.android.quickstep.util.StaggeredWorkspaceAnim;
|
||||
import com.android.systemui.shared.system.ActivityCompat;
|
||||
import com.android.systemui.shared.system.ActivityOptionsCompat;
|
||||
import com.android.systemui.shared.system.QuickStepContract;
|
||||
@@ -156,6 +161,7 @@ public abstract class QuickstepAppTransitionManagerImpl extends LauncherAppTrans
|
||||
// Strong refs to runners which are cleared when the launcher activity is destroyed
|
||||
private WrappedAnimationRunnerImpl mWallpaperOpenRunner;
|
||||
private WrappedAnimationRunnerImpl mAppLaunchRunner;
|
||||
private WrappedAnimationRunnerImpl mKeyguardGoingAwayRunner;
|
||||
|
||||
private final AnimatorListenerAdapter mForceInvisibleListener = new AnimatorListenerAdapter() {
|
||||
@Override
|
||||
@@ -380,18 +386,35 @@ public abstract class QuickstepAppTransitionManagerImpl extends LauncherAppTrans
|
||||
alpha.setInterpolator(LINEAR);
|
||||
launcherAnimator.play(alpha);
|
||||
|
||||
mDragLayer.setTranslationY(trans[0]);
|
||||
ObjectAnimator transY = ObjectAnimator.ofFloat(mDragLayer, View.TRANSLATION_Y, trans);
|
||||
transY.setInterpolator(AGGRESSIVE_EASE);
|
||||
transY.setDuration(CONTENT_TRANSLATION_DURATION);
|
||||
launcherAnimator.play(transY);
|
||||
Workspace workspace = mLauncher.getWorkspace();
|
||||
View currentPage = ((CellLayout) workspace.getChildAt(workspace.getCurrentPage()))
|
||||
.getShortcutsAndWidgets();
|
||||
View hotseat = mLauncher.getHotseat();
|
||||
View qsb = mLauncher.findViewById(R.id.search_container_all_apps);
|
||||
|
||||
currentPage.setLayerType(View.LAYER_TYPE_HARDWARE, null);
|
||||
hotseat.setLayerType(View.LAYER_TYPE_HARDWARE, null);
|
||||
qsb.setLayerType(View.LAYER_TYPE_HARDWARE, null);
|
||||
|
||||
launcherAnimator.play(ObjectAnimator.ofFloat(currentPage, View.TRANSLATION_Y, trans));
|
||||
launcherAnimator.play(ObjectAnimator.ofFloat(hotseat, View.TRANSLATION_Y, trans));
|
||||
launcherAnimator.play(ObjectAnimator.ofFloat(qsb, View.TRANSLATION_Y, trans));
|
||||
|
||||
mDragLayer.getScrim().hideSysUiScrim(true);
|
||||
// Pause page indicator animations as they lead to layer trashing.
|
||||
mLauncher.getWorkspace().getPageIndicator().pauseAnimations();
|
||||
mDragLayer.setLayerType(View.LAYER_TYPE_HARDWARE, null);
|
||||
|
||||
endListener = this::resetContentView;
|
||||
endListener = () -> {
|
||||
currentPage.setTranslationY(0);
|
||||
hotseat.setTranslationY(0);
|
||||
qsb.setTranslationY(0);
|
||||
|
||||
currentPage.setLayerType(View.LAYER_TYPE_NONE, null);
|
||||
hotseat.setLayerType(View.LAYER_TYPE_NONE, null);
|
||||
qsb.setLayerType(View.LAYER_TYPE_NONE, null);
|
||||
|
||||
mDragLayerAlpha.setValue(1f);
|
||||
mLauncher.getWorkspace().getPageIndicator().skipAnimationsToEnd();
|
||||
};
|
||||
}
|
||||
return new Pair<>(launcherAnimator, endListener);
|
||||
}
|
||||
@@ -589,17 +612,17 @@ public abstract class QuickstepAppTransitionManagerImpl extends LauncherAppTrans
|
||||
// When launching an app from overview that doesn't map to a task, we still want to just
|
||||
// blur the wallpaper instead of the launcher surface as well
|
||||
boolean allowBlurringLauncher = mLauncher.getStateManager().getState() != OVERVIEW;
|
||||
BackgroundBlurController blurController = mLauncher.getBackgroundBlurController();
|
||||
ObjectAnimator backgroundRadiusAnim = ObjectAnimator.ofInt(blurController, BACKGROUND_BLUR,
|
||||
BACKGROUND_APP.getBackgroundBlurRadius(mLauncher))
|
||||
DepthController depthController = mLauncher.getDepthController();
|
||||
ObjectAnimator backgroundRadiusAnim = ObjectAnimator.ofFloat(depthController, DEPTH,
|
||||
BACKGROUND_APP.getDepth(mLauncher))
|
||||
.setDuration(APP_LAUNCH_DURATION);
|
||||
if (allowBlurringLauncher) {
|
||||
blurController.setSurfaceToApp(RemoteAnimationProvider.findLowestOpaqueLayerTarget(
|
||||
depthController.setSurfaceToApp(RemoteAnimationProvider.findLowestOpaqueLayerTarget(
|
||||
appTargets, MODE_OPENING));
|
||||
backgroundRadiusAnim.addListener(new AnimatorListenerAdapter() {
|
||||
@Override
|
||||
public void onAnimationEnd(Animator animation) {
|
||||
blurController.setSurfaceToLauncher(mLauncher.getDragLayer());
|
||||
depthController.setSurfaceToLauncher(mLauncher.getDragLayer());
|
||||
}
|
||||
});
|
||||
}
|
||||
@@ -623,6 +646,17 @@ public abstract class QuickstepAppTransitionManagerImpl extends LauncherAppTrans
|
||||
new WrappedLauncherAnimationRunner<>(mWallpaperOpenRunner,
|
||||
false /* startAtFrontOfQueue */),
|
||||
CLOSING_TRANSITION_DURATION_MS, 0 /* statusBarTransitionDelay */));
|
||||
|
||||
if (KEYGUARD_ANIMATION.get()) {
|
||||
mKeyguardGoingAwayRunner = createWallpaperOpenRunner(true /* fromUnlock */);
|
||||
definition.addRemoteAnimation(
|
||||
WindowManagerWrapper.TRANSIT_KEYGUARD_GOING_AWAY_ON_WALLPAPER,
|
||||
new RemoteAnimationAdapterCompat(
|
||||
new WrappedLauncherAnimationRunner<>(mKeyguardGoingAwayRunner,
|
||||
true /* startAtFrontOfQueue */),
|
||||
CLOSING_TRANSITION_DURATION_MS, 0 /* statusBarTransitionDelay */));
|
||||
}
|
||||
|
||||
new ActivityCompat(mLauncher).registerRemoteAnimations(definition);
|
||||
}
|
||||
}
|
||||
@@ -639,6 +673,7 @@ public abstract class QuickstepAppTransitionManagerImpl extends LauncherAppTrans
|
||||
// definition so we don't have to wait for the system gc
|
||||
mWallpaperOpenRunner = null;
|
||||
mAppLaunchRunner = null;
|
||||
mKeyguardGoingAwayRunner = null;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -741,62 +776,6 @@ public abstract class QuickstepAppTransitionManagerImpl extends LauncherAppTrans
|
||||
return closingAnimator;
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates an animator that modifies Launcher as a result from
|
||||
* {@link #createWallpaperOpenRunner}.
|
||||
*/
|
||||
private void createLauncherResumeAnimation(AnimatorSet anim) {
|
||||
if (mLauncher.isInState(LauncherState.ALL_APPS)) {
|
||||
Pair<AnimatorSet, Runnable> contentAnimator =
|
||||
getLauncherContentAnimator(false /* isAppOpening */,
|
||||
new float[] {-mContentTransY, 0});
|
||||
contentAnimator.first.setStartDelay(LAUNCHER_RESUME_START_DELAY);
|
||||
anim.play(contentAnimator.first);
|
||||
anim.addListener(new AnimatorListenerAdapter() {
|
||||
@Override
|
||||
public void onAnimationEnd(Animator animation) {
|
||||
contentAnimator.second.run();
|
||||
}
|
||||
});
|
||||
} else {
|
||||
AnimatorSet workspaceAnimator = new AnimatorSet();
|
||||
|
||||
mDragLayer.setTranslationY(-mWorkspaceTransY);;
|
||||
workspaceAnimator.play(ObjectAnimator.ofFloat(mDragLayer, View.TRANSLATION_Y,
|
||||
-mWorkspaceTransY, 0));
|
||||
|
||||
mDragLayerAlpha.setValue(0);
|
||||
workspaceAnimator.play(ObjectAnimator.ofFloat(
|
||||
mDragLayerAlpha, MultiValueAlpha.VALUE, 0, 1f));
|
||||
|
||||
workspaceAnimator.setStartDelay(LAUNCHER_RESUME_START_DELAY);
|
||||
workspaceAnimator.setDuration(333);
|
||||
workspaceAnimator.setInterpolator(Interpolators.DEACCEL_1_7);
|
||||
|
||||
mDragLayer.getScrim().hideSysUiScrim(true);
|
||||
|
||||
// Pause page indicator animations as they lead to layer trashing.
|
||||
mLauncher.getWorkspace().getPageIndicator().pauseAnimations();
|
||||
mDragLayer.setLayerType(View.LAYER_TYPE_HARDWARE, null);
|
||||
|
||||
workspaceAnimator.addListener(new AnimatorListenerAdapter() {
|
||||
@Override
|
||||
public void onAnimationEnd(Animator animation) {
|
||||
resetContentView();
|
||||
}
|
||||
});
|
||||
anim.play(workspaceAnimator);
|
||||
}
|
||||
}
|
||||
|
||||
private void resetContentView() {
|
||||
mLauncher.getWorkspace().getPageIndicator().skipAnimationsToEnd();
|
||||
mDragLayerAlpha.setValue(1f);
|
||||
mDragLayer.setLayerType(View.LAYER_TYPE_NONE, null);
|
||||
mDragLayer.setTranslationY(0f);
|
||||
mDragLayer.getScrim().hideSysUiScrim(false);
|
||||
}
|
||||
|
||||
private boolean hasControlRemoteAppTransitionPermission() {
|
||||
return mLauncher.checkSelfPermission(CONTROL_REMOTE_APP_TRANSITION_PERMISSION)
|
||||
== PackageManager.PERMISSION_GRANTED;
|
||||
@@ -868,11 +847,12 @@ public abstract class QuickstepAppTransitionManagerImpl extends LauncherAppTrans
|
||||
|| mLauncher.isForceInvisible()) {
|
||||
// Only register the content animation for cancellation when state changes
|
||||
mLauncher.getStateManager().setCurrentAnimation(anim);
|
||||
if (mFromUnlock) {
|
||||
|
||||
if (mLauncher.isInState(LauncherState.ALL_APPS)) {
|
||||
Pair<AnimatorSet, Runnable> contentAnimator =
|
||||
getLauncherContentAnimator(false /* isAppOpening */,
|
||||
new float[] {mContentTransY, 0});
|
||||
contentAnimator.first.setStartDelay(0);
|
||||
new float[] {-mContentTransY, 0});
|
||||
contentAnimator.first.setStartDelay(LAUNCHER_RESUME_START_DELAY);
|
||||
anim.play(contentAnimator.first);
|
||||
anim.addListener(new AnimatorListenerAdapter() {
|
||||
@Override
|
||||
@@ -881,7 +861,12 @@ public abstract class QuickstepAppTransitionManagerImpl extends LauncherAppTrans
|
||||
}
|
||||
});
|
||||
} else {
|
||||
createLauncherResumeAnimation(anim);
|
||||
float velocityDpPerS = DynamicResource.provider(mLauncher)
|
||||
.getDimension(R.dimen.unlock_staggered_velocity_dp_per_s);
|
||||
float velocityPxPerS = TypedValue.applyDimension(COMPLEX_UNIT_DIP,
|
||||
velocityDpPerS, mLauncher.getResources().getDisplayMetrics());
|
||||
anim.play(new StaggeredWorkspaceAnim(mLauncher, velocityPxPerS, false)
|
||||
.getAnimators());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+54
-42
@@ -18,7 +18,8 @@ package com.android.launcher3.uioverrides;
|
||||
|
||||
import static com.android.launcher3.anim.Interpolators.LINEAR;
|
||||
|
||||
import android.util.IntProperty;
|
||||
import android.os.IBinder;
|
||||
import android.util.FloatProperty;
|
||||
import android.view.View;
|
||||
|
||||
import com.android.launcher3.Launcher;
|
||||
@@ -31,22 +32,23 @@ import com.android.launcher3.states.StateAnimationConfig;
|
||||
import com.android.systemui.shared.system.RemoteAnimationTargetCompat;
|
||||
import com.android.systemui.shared.system.SurfaceControlCompat;
|
||||
import com.android.systemui.shared.system.TransactionCompat;
|
||||
import com.android.systemui.shared.system.WallpaperManagerCompat;
|
||||
|
||||
/**
|
||||
* Controls the blur, for the Launcher surface only.
|
||||
* Controls blur and wallpaper zoom, for the Launcher surface only.
|
||||
*/
|
||||
public class BackgroundBlurController implements LauncherStateManager.StateHandler {
|
||||
public class DepthController implements LauncherStateManager.StateHandler {
|
||||
|
||||
public static final IntProperty<BackgroundBlurController> BACKGROUND_BLUR =
|
||||
new IntProperty<BackgroundBlurController>("backgroundBlur") {
|
||||
public static final FloatProperty<DepthController> DEPTH =
|
||||
new FloatProperty<DepthController>("depth") {
|
||||
@Override
|
||||
public void setValue(BackgroundBlurController blurController, int blurRadius) {
|
||||
blurController.setBackgroundBlurRadius(blurRadius);
|
||||
public void setValue(DepthController depthController, float depth) {
|
||||
depthController.setDepth(depth);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Integer get(BackgroundBlurController blurController) {
|
||||
return blurController.mBackgroundBlurRadius;
|
||||
public Float get(DepthController depthController) {
|
||||
return depthController.mDepth;
|
||||
}
|
||||
};
|
||||
|
||||
@@ -54,42 +56,50 @@ public class BackgroundBlurController implements LauncherStateManager.StateHandl
|
||||
* A property that updates the background blur within a given range of values (ie. even if the
|
||||
* animator goes beyond 0..1, the interpolated value will still be bounded).
|
||||
*/
|
||||
public static class ClampedBlurProperty extends IntProperty<BackgroundBlurController> {
|
||||
private final int mMinValue;
|
||||
private final int mMaxValue;
|
||||
public static class ClampedDepthProperty extends FloatProperty<DepthController> {
|
||||
private final float mMinValue;
|
||||
private final float mMaxValue;
|
||||
|
||||
public ClampedBlurProperty(int minValue, int maxValue) {
|
||||
super(("backgroundBlurClamped"));
|
||||
public ClampedDepthProperty(float minValue, float maxValue) {
|
||||
super("depthClamped");
|
||||
mMinValue = minValue;
|
||||
mMaxValue = maxValue;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setValue(BackgroundBlurController blurController, int blurRadius) {
|
||||
blurController.setBackgroundBlurRadius(Utilities.boundToRange(blurRadius,
|
||||
mMinValue, mMaxValue));
|
||||
public void setValue(DepthController depthController, float depth) {
|
||||
depthController.setDepth(Utilities.boundToRange(depth, mMinValue, mMaxValue));
|
||||
}
|
||||
|
||||
@Override
|
||||
public Integer get(BackgroundBlurController blurController) {
|
||||
return blurController.mBackgroundBlurRadius;
|
||||
public Float get(DepthController depthController) {
|
||||
return depthController.mDepth;
|
||||
}
|
||||
}
|
||||
|
||||
private final Launcher mLauncher;
|
||||
/**
|
||||
* Blur radius when completely zoomed out, in pixels.
|
||||
*/
|
||||
private int mMaxBlurRadius;
|
||||
private WallpaperManagerCompat mWallpaperManager;
|
||||
private SurfaceControlCompat mSurface;
|
||||
private int mBackgroundBlurRadius;
|
||||
/**
|
||||
* Ratio from 0 to 1, where 0 is fully zoomed out, and 1 is zoomed in.
|
||||
* @see android.service.wallpaper.WallpaperService.Engine#onZoomChanged(float)
|
||||
*/
|
||||
private float mDepth;
|
||||
|
||||
public BackgroundBlurController(Launcher l) {
|
||||
public DepthController(Launcher l) {
|
||||
mLauncher = l;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return the background blur adjustment for folders
|
||||
*/
|
||||
public int getFolderBackgroundBlurAdjustment() {
|
||||
return mLauncher.getResources().getInteger(
|
||||
R.integer.folder_background_blur_radius_adjustment);
|
||||
private void ensureDependencies() {
|
||||
if (mWallpaperManager != null) {
|
||||
return;
|
||||
}
|
||||
mMaxBlurRadius = mLauncher.getResources().getInteger(R.integer.max_depth_blur_radius);
|
||||
mWallpaperManager = new WallpaperManagerCompat(mLauncher);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -112,10 +122,10 @@ public class BackgroundBlurController implements LauncherStateManager.StateHandl
|
||||
if (mSurface != surface) {
|
||||
mSurface = surface;
|
||||
if (surface != null) {
|
||||
setBackgroundBlurRadius(mBackgroundBlurRadius);
|
||||
setDepth(mDepth);
|
||||
} else {
|
||||
// If there is no surface, then reset the blur radius
|
||||
setBackgroundBlurRadius(0);
|
||||
// If there is no surface, then reset the ratio
|
||||
setDepth(0f);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -126,9 +136,9 @@ public class BackgroundBlurController implements LauncherStateManager.StateHandl
|
||||
return;
|
||||
}
|
||||
|
||||
int toBackgroundBlurRadius = toState.getBackgroundBlurRadius(mLauncher);
|
||||
if (mBackgroundBlurRadius != toBackgroundBlurRadius) {
|
||||
setBackgroundBlurRadius(toBackgroundBlurRadius);
|
||||
float toDepth = toState.getDepth(mLauncher);
|
||||
if (Float.compare(mDepth, toDepth) != 0) {
|
||||
setDepth(toDepth);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -139,22 +149,24 @@ public class BackgroundBlurController implements LauncherStateManager.StateHandl
|
||||
return;
|
||||
}
|
||||
|
||||
int toBackgroundBlurRadius = toState.getBackgroundBlurRadius(mLauncher);
|
||||
if (mBackgroundBlurRadius != toBackgroundBlurRadius) {
|
||||
animation.setInt(this, BACKGROUND_BLUR, toBackgroundBlurRadius, LINEAR);
|
||||
float toDepth = toState.getDepth(mLauncher);
|
||||
if (Float.compare(mDepth, toDepth) != 0) {
|
||||
animation.setFloat(this, DEPTH, toDepth, LINEAR);
|
||||
}
|
||||
}
|
||||
|
||||
private void setBackgroundBlurRadius(int blurRadius) {
|
||||
// TODO: Do nothing if the shadows are not enabled
|
||||
// Always update the background blur as it will be reapplied when a surface is next
|
||||
// available
|
||||
mBackgroundBlurRadius = blurRadius;
|
||||
private void setDepth(float depth) {
|
||||
mDepth = depth;
|
||||
if (mSurface == null || !mSurface.isValid()) {
|
||||
return;
|
||||
}
|
||||
ensureDependencies();
|
||||
IBinder windowToken = mLauncher.getRootView().getWindowToken();
|
||||
if (windowToken != null) {
|
||||
mWallpaperManager.setWallpaperZoomOut(windowToken, mDepth);
|
||||
}
|
||||
new TransactionCompat()
|
||||
.setBackgroundBlurRadius(mSurface, blurRadius)
|
||||
.setBackgroundBlurRadius(mSurface, (int) (mDepth * mMaxBlurRadius))
|
||||
.apply();
|
||||
}
|
||||
}
|
||||
@@ -32,8 +32,11 @@ public class PreviewSurfaceRenderer {
|
||||
|
||||
/** Handle a received surface view request. */
|
||||
public static void render(Context context, Bundle bundle) {
|
||||
final String gridName = bundle.getString("name");
|
||||
String gridName = bundle.getString("name");
|
||||
bundle.remove("name");
|
||||
if (gridName == null) {
|
||||
gridName = InvariantDeviceProfile.getCurrentGridName(context);
|
||||
}
|
||||
final InvariantDeviceProfile idp = new InvariantDeviceProfile(context, gridName);
|
||||
|
||||
MAIN_EXECUTOR.execute(() -> {
|
||||
|
||||
@@ -22,7 +22,6 @@ import android.content.Context;
|
||||
import com.android.launcher3.AbstractFloatingView;
|
||||
import com.android.launcher3.Launcher;
|
||||
import com.android.launcher3.LauncherState;
|
||||
import com.android.launcher3.R;
|
||||
import com.android.launcher3.allapps.AllAppsContainerView;
|
||||
import com.android.launcher3.userevent.nano.LauncherLogProto.ContainerType;
|
||||
import com.android.quickstep.SysUINavigationMode;
|
||||
@@ -88,8 +87,8 @@ public class AllAppsState extends LauncherState {
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getBackgroundBlurRadius(Context context) {
|
||||
return context.getResources().getInteger(R.integer.allapps_background_blur_radius);
|
||||
public float getDepth(Context context) {
|
||||
return 1f;
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
@@ -33,7 +33,7 @@ import com.android.launcher3.BaseDraggingActivity;
|
||||
import com.android.launcher3.DeviceProfile;
|
||||
import com.android.launcher3.anim.AnimatorPlaybackController;
|
||||
import com.android.launcher3.touch.PagedOrientationHandler;
|
||||
import com.android.launcher3.uioverrides.BackgroundBlurController;
|
||||
import com.android.launcher3.uioverrides.DepthController;
|
||||
import com.android.quickstep.util.ActivityInitListener;
|
||||
import com.android.quickstep.util.ShelfPeekAnim;
|
||||
import com.android.systemui.shared.recents.model.ThumbnailData;
|
||||
@@ -81,7 +81,8 @@ public interface BaseActivityInterface<T extends BaseDraggingActivity> {
|
||||
@Nullable
|
||||
T getCreatedActivity();
|
||||
|
||||
default @Nullable BackgroundBlurController getBackgroundBlurController() {
|
||||
@Nullable
|
||||
default DepthController getDepthController() {
|
||||
return null;
|
||||
}
|
||||
|
||||
|
||||
@@ -59,6 +59,16 @@ public interface InputConsumer {
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns true if the lifecycle of this input consumer is detached from the normal gesture
|
||||
* down/up flow. If so, it is the responsibility of the input consumer to call back to
|
||||
* {@link TouchInteractionService#onConsumerInactive(InputConsumer)} after the consumer is
|
||||
* finished.
|
||||
*/
|
||||
default boolean isConsumerDetachedFromGesture() {
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Called by the event queue when the consumer is about to be switched to a new consumer.
|
||||
* Consumers should update the state accordingly here before the state is passed to the new
|
||||
|
||||
@@ -25,6 +25,7 @@ import android.widget.TextView;
|
||||
import com.android.launcher3.R;
|
||||
import com.android.quickstep.interaction.BackGestureTutorialFragment.TutorialStep;
|
||||
import com.android.quickstep.interaction.BackGestureTutorialFragment.TutorialType;
|
||||
import com.android.quickstep.interaction.EdgeBackGestureHandler.BackGestureResult;
|
||||
|
||||
import java.util.Optional;
|
||||
|
||||
@@ -79,21 +80,33 @@ abstract class BackGestureTutorialController {
|
||||
mHandCoachingAnimation.stop();
|
||||
}
|
||||
|
||||
void onGestureDetected() {
|
||||
hideHandCoachingAnimation();
|
||||
|
||||
if (mTutorialStep == TutorialStep.CONFIRM) {
|
||||
void onGestureAttempted(BackGestureResult result) {
|
||||
if (mTutorialStep == TutorialStep.CONFIRM
|
||||
&& (result == BackGestureResult.BACK_COMPLETED_FROM_LEFT
|
||||
|| result == BackGestureResult.BACK_COMPLETED_FROM_RIGHT)) {
|
||||
mFragment.closeTutorial();
|
||||
return;
|
||||
}
|
||||
|
||||
if (mTutorialTypeInfo.get().getTutorialType() == TutorialType.RIGHT_EDGE_BACK_NAVIGATION) {
|
||||
mFragment.changeController(TutorialStep.ENGAGED,
|
||||
TutorialType.LEFT_EDGE_BACK_NAVIGATION);
|
||||
if (!mTutorialTypeInfo.isPresent()) {
|
||||
return;
|
||||
}
|
||||
|
||||
mFragment.changeController(TutorialStep.CONFIRM);
|
||||
switch (mTutorialTypeInfo.get().getTutorialType()) {
|
||||
case RIGHT_EDGE_BACK_NAVIGATION:
|
||||
if (result == BackGestureResult.BACK_COMPLETED_FROM_RIGHT) {
|
||||
hideHandCoachingAnimation();
|
||||
mFragment.changeController(
|
||||
TutorialStep.ENGAGED, TutorialType.LEFT_EDGE_BACK_NAVIGATION);
|
||||
}
|
||||
break;
|
||||
case LEFT_EDGE_BACK_NAVIGATION:
|
||||
if (result == BackGestureResult.BACK_COMPLETED_FROM_LEFT) {
|
||||
hideHandCoachingAnimation();
|
||||
mFragment.changeController(TutorialStep.CONFIRM);
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
abstract Optional<Integer> getTitleStringId();
|
||||
|
||||
@@ -17,21 +17,26 @@ package com.android.quickstep.interaction;
|
||||
|
||||
import android.content.ActivityNotFoundException;
|
||||
import android.content.Intent;
|
||||
import android.graphics.Insets;
|
||||
import android.os.Bundle;
|
||||
import android.util.Log;
|
||||
import android.view.LayoutInflater;
|
||||
import android.view.View;
|
||||
import android.view.ViewGroup;
|
||||
import android.view.WindowInsets;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.fragment.app.Fragment;
|
||||
|
||||
import com.android.launcher3.R;
|
||||
import com.android.quickstep.interaction.EdgeBackGestureHandler.BackGestureAttemptCallback;
|
||||
import com.android.quickstep.interaction.EdgeBackGestureHandler.BackGestureResult;
|
||||
|
||||
import java.net.URISyntaxException;
|
||||
import java.util.Optional;
|
||||
|
||||
/** Shows the Back gesture interactive tutorial. */
|
||||
public class BackGestureTutorialFragment extends Fragment {
|
||||
public class BackGestureTutorialFragment extends Fragment implements BackGestureAttemptCallback {
|
||||
|
||||
private static final String LOG_TAG = "TutorialFragment";
|
||||
private static final String KEY_TUTORIAL_STEP = "tutorialStep";
|
||||
@@ -47,6 +52,7 @@ public class BackGestureTutorialFragment extends Fragment {
|
||||
private Optional<BackGestureTutorialController> mTutorialController = Optional.empty();
|
||||
private View mRootView;
|
||||
private BackGestureTutorialHandAnimation mHandCoachingAnimation;
|
||||
private EdgeBackGestureHandler mEdgeBackGestureHandler;
|
||||
|
||||
public static BackGestureTutorialFragment newInstance(
|
||||
TutorialStep tutorialStep, TutorialType tutorialType) {
|
||||
@@ -64,17 +70,25 @@ public class BackGestureTutorialFragment extends Fragment {
|
||||
Bundle args = savedInstanceState != null ? savedInstanceState : getArguments();
|
||||
mTutorialStep = (TutorialStep) args.getSerializable(KEY_TUTORIAL_STEP);
|
||||
mTutorialType = (TutorialType) args.getSerializable(KEY_TUTORIAL_TYPE);
|
||||
mEdgeBackGestureHandler = new EdgeBackGestureHandler(getContext());
|
||||
mEdgeBackGestureHandler.registerBackGestureAttemptCallback(this);
|
||||
}
|
||||
|
||||
@Override
|
||||
public View onCreateView(
|
||||
LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
|
||||
@NonNull LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
|
||||
super.onCreateView(inflater, container, savedInstanceState);
|
||||
|
||||
mRootView = inflater.inflate(R.layout.back_gesture_tutorial_fragment,
|
||||
container, /* attachToRoot= */ false);
|
||||
mRootView.findViewById(R.id.back_gesture_tutorial_fragment_close_button)
|
||||
.setOnClickListener(this::onCloseButtonClicked);
|
||||
mRootView.setOnApplyWindowInsetsListener((view, insets) -> {
|
||||
Insets systemInsets = insets.getInsets(WindowInsets.Type.systemBars());
|
||||
mEdgeBackGestureHandler.setInsets(systemInsets.left, systemInsets.right);
|
||||
return insets;
|
||||
});
|
||||
mRootView.setOnTouchListener(mEdgeBackGestureHandler);
|
||||
mHandCoachingAnimation = new BackGestureTutorialHandAnimation(getContext(), mRootView);
|
||||
|
||||
return mRootView;
|
||||
@@ -92,6 +106,14 @@ public class BackGestureTutorialFragment extends Fragment {
|
||||
mHandCoachingAnimation.stop();
|
||||
}
|
||||
|
||||
void onAttachedToWindow() {
|
||||
mEdgeBackGestureHandler.setIsEnabled(true);
|
||||
}
|
||||
|
||||
void onDetachedFromWindow() {
|
||||
mEdgeBackGestureHandler.setIsEnabled(false);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onSaveInstanceState(Bundle savedInstanceState) {
|
||||
savedInstanceState.putSerializable(KEY_TUTORIAL_STEP, mTutorialStep);
|
||||
@@ -125,10 +147,9 @@ public class BackGestureTutorialFragment extends Fragment {
|
||||
this.mTutorialType = tutorialType;
|
||||
}
|
||||
|
||||
void onBackPressed() {
|
||||
if (mTutorialController.isPresent()) {
|
||||
mTutorialController.get().onGestureDetected();
|
||||
}
|
||||
@Override
|
||||
public void onBackGestureAttempted(BackGestureResult result) {
|
||||
mTutorialController.ifPresent(controller -> controller.onGestureAttempted(result));
|
||||
}
|
||||
|
||||
void closeTutorial() {
|
||||
|
||||
@@ -0,0 +1,274 @@
|
||||
/*
|
||||
* Copyright (C) 2020 The Android Open Source Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package com.android.quickstep.interaction;
|
||||
|
||||
import android.content.Context;
|
||||
import android.content.res.Resources;
|
||||
import android.graphics.PixelFormat;
|
||||
import android.graphics.Point;
|
||||
import android.graphics.PointF;
|
||||
import android.hardware.display.DisplayManager;
|
||||
import android.hardware.display.DisplayManager.DisplayListener;
|
||||
import android.os.Handler;
|
||||
import android.os.Looper;
|
||||
import android.os.SystemProperties;
|
||||
import android.view.Display;
|
||||
import android.view.MotionEvent;
|
||||
import android.view.View;
|
||||
import android.view.View.OnTouchListener;
|
||||
import android.view.ViewConfiguration;
|
||||
import android.view.WindowManager;
|
||||
import android.view.WindowManager.LayoutParams;
|
||||
|
||||
import com.android.launcher3.ResourceUtils;
|
||||
|
||||
/**
|
||||
* Utility class to handle edge swipes for back gestures.
|
||||
*
|
||||
* Forked from platform/frameworks/base/packages/SystemUI/src/com/android/systemui/statusbar/phone/EdgeBackGestureHandler.java.
|
||||
*/
|
||||
public class EdgeBackGestureHandler implements DisplayListener, OnTouchListener {
|
||||
|
||||
private static final String TAG = "EdgeBackGestureHandler";
|
||||
private static final int MAX_LONG_PRESS_TIMEOUT = SystemProperties.getInt(
|
||||
"gestures.back_timeout", 250);
|
||||
|
||||
private final Context mContext;
|
||||
|
||||
private final Point mDisplaySize = new Point();
|
||||
private final int mDisplayId;
|
||||
|
||||
// The edge width where touch down is allowed
|
||||
private int mEdgeWidth;
|
||||
// The bottom gesture area height
|
||||
private int mBottomGestureHeight;
|
||||
// The slop to distinguish between horizontal and vertical motion
|
||||
private final float mTouchSlop;
|
||||
// Duration after which we consider the event as longpress.
|
||||
private final int mLongPressTimeout;
|
||||
|
||||
private final PointF mDownPoint = new PointF();
|
||||
private boolean mThresholdCrossed = false;
|
||||
private boolean mAllowGesture = false;
|
||||
private boolean mIsEnabled;
|
||||
private int mLeftInset;
|
||||
private int mRightInset;
|
||||
|
||||
private EdgeBackGesturePanel mEdgeBackPanel;
|
||||
private BackGestureAttemptCallback mGestureCallback;
|
||||
|
||||
private final EdgeBackGesturePanel.BackCallback mBackCallback =
|
||||
new EdgeBackGesturePanel.BackCallback() {
|
||||
@Override
|
||||
public void triggerBack() {
|
||||
if (mGestureCallback != null) {
|
||||
mGestureCallback.onBackGestureAttempted(mEdgeBackPanel.getIsLeftPanel()
|
||||
? BackGestureResult.BACK_COMPLETED_FROM_LEFT
|
||||
: BackGestureResult.BACK_COMPLETED_FROM_RIGHT);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void cancelBack() {
|
||||
if (mGestureCallback != null) {
|
||||
mGestureCallback.onBackGestureAttempted(mEdgeBackPanel.getIsLeftPanel()
|
||||
? BackGestureResult.BACK_CANCELLED_FROM_LEFT
|
||||
: BackGestureResult.BACK_CANCELLED_FROM_RIGHT);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
EdgeBackGestureHandler(Context context) {
|
||||
final Resources res = context.getResources();
|
||||
mContext = context;
|
||||
mDisplayId = context.getDisplay() == null
|
||||
? Display.DEFAULT_DISPLAY : context.getDisplay().getDisplayId();
|
||||
|
||||
mTouchSlop = ViewConfiguration.get(context).getScaledTouchSlop();
|
||||
mLongPressTimeout = Math.min(MAX_LONG_PRESS_TIMEOUT,
|
||||
ViewConfiguration.getLongPressTimeout());
|
||||
|
||||
mBottomGestureHeight =
|
||||
ResourceUtils.getNavbarSize(ResourceUtils.NAVBAR_BOTTOM_GESTURE_SIZE, res);
|
||||
mEdgeWidth = ResourceUtils.getNavbarSize("config_backGestureInset", res);
|
||||
}
|
||||
|
||||
void setIsEnabled(boolean isEnabled) {
|
||||
if (isEnabled == mIsEnabled) {
|
||||
return;
|
||||
}
|
||||
mIsEnabled = isEnabled;
|
||||
|
||||
if (mEdgeBackPanel != null) {
|
||||
mEdgeBackPanel.onDestroy();
|
||||
mEdgeBackPanel = null;
|
||||
}
|
||||
|
||||
if (!mIsEnabled) {
|
||||
mContext.getSystemService(DisplayManager.class).unregisterDisplayListener(this);
|
||||
} else {
|
||||
updateDisplaySize();
|
||||
mContext.getSystemService(DisplayManager.class).registerDisplayListener(this,
|
||||
new Handler(Looper.getMainLooper()));
|
||||
|
||||
// Add a nav bar panel window.
|
||||
mEdgeBackPanel = new EdgeBackGesturePanel(mContext);
|
||||
mEdgeBackPanel.setBackCallback(mBackCallback);
|
||||
mEdgeBackPanel.setLayoutParams(createLayoutParams());
|
||||
updateDisplaySize();
|
||||
}
|
||||
}
|
||||
|
||||
void registerBackGestureAttemptCallback(BackGestureAttemptCallback callback) {
|
||||
mGestureCallback = callback;
|
||||
}
|
||||
|
||||
private WindowManager.LayoutParams createLayoutParams() {
|
||||
Resources resources = mContext.getResources();
|
||||
WindowManager.LayoutParams layoutParams = new WindowManager.LayoutParams(
|
||||
ResourceUtils.getNavbarSize("navigation_edge_panel_width", resources),
|
||||
ResourceUtils.getNavbarSize("navigation_edge_panel_height", resources),
|
||||
LayoutParams.TYPE_APPLICATION_PANEL,
|
||||
WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE
|
||||
| WindowManager.LayoutParams.FLAG_NOT_TOUCH_MODAL
|
||||
| WindowManager.LayoutParams.FLAG_SPLIT_TOUCH
|
||||
| WindowManager.LayoutParams.FLAG_LAYOUT_IN_SCREEN,
|
||||
PixelFormat.TRANSLUCENT);
|
||||
layoutParams.setTitle(TAG + mDisplayId);
|
||||
layoutParams.windowAnimations = 0;
|
||||
layoutParams.setFitInsetsTypes(0 /* types */);
|
||||
return layoutParams;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean onTouch(View view, MotionEvent motionEvent) {
|
||||
if (mIsEnabled) {
|
||||
onMotionEvent(motionEvent);
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
private boolean isWithinTouchRegion(int x, int y) {
|
||||
// Disallow if too far from the edge
|
||||
if (x > mEdgeWidth + mLeftInset && x < (mDisplaySize.x - mEdgeWidth - mRightInset)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// Disallow if we are in the bottom gesture area
|
||||
if (y >= (mDisplaySize.y - mBottomGestureHeight)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
private void cancelGesture(MotionEvent ev) {
|
||||
// Send action cancel to reset all the touch events
|
||||
mAllowGesture = false;
|
||||
MotionEvent cancelEv = MotionEvent.obtain(ev);
|
||||
cancelEv.setAction(MotionEvent.ACTION_CANCEL);
|
||||
mEdgeBackPanel.onMotionEvent(cancelEv);
|
||||
cancelEv.recycle();
|
||||
}
|
||||
|
||||
private void onMotionEvent(MotionEvent ev) {
|
||||
int action = ev.getActionMasked();
|
||||
if (action == MotionEvent.ACTION_DOWN) {
|
||||
boolean isOnLeftEdge = ev.getX() <= mEdgeWidth + mLeftInset;
|
||||
mAllowGesture = isWithinTouchRegion((int) ev.getX(), (int) ev.getY());
|
||||
if (mAllowGesture) {
|
||||
mEdgeBackPanel.setIsLeftPanel(isOnLeftEdge);
|
||||
mEdgeBackPanel.onMotionEvent(ev);
|
||||
|
||||
mDownPoint.set(ev.getX(), ev.getY());
|
||||
mThresholdCrossed = false;
|
||||
}
|
||||
} else if (mAllowGesture) {
|
||||
if (!mThresholdCrossed) {
|
||||
if (action == MotionEvent.ACTION_POINTER_DOWN) {
|
||||
// We do not support multi touch for back gesture
|
||||
cancelGesture(ev);
|
||||
return;
|
||||
} else if (action == MotionEvent.ACTION_MOVE) {
|
||||
if ((ev.getEventTime() - ev.getDownTime()) > mLongPressTimeout) {
|
||||
cancelGesture(ev);
|
||||
return;
|
||||
}
|
||||
float dx = Math.abs(ev.getX() - mDownPoint.x);
|
||||
float dy = Math.abs(ev.getY() - mDownPoint.y);
|
||||
if (dy > dx && dy > mTouchSlop) {
|
||||
cancelGesture(ev);
|
||||
return;
|
||||
|
||||
} else if (dx > dy && dx > mTouchSlop) {
|
||||
mThresholdCrossed = true;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
// forward touch
|
||||
mEdgeBackPanel.onMotionEvent(ev);
|
||||
}
|
||||
|
||||
if (action == MotionEvent.ACTION_UP || action == MotionEvent.ACTION_CANCEL) {
|
||||
if (!mAllowGesture && mGestureCallback != null) {
|
||||
mGestureCallback.onBackGestureAttempted(BackGestureResult.BACK_NOT_STARTED);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onDisplayAdded(int displayId) { }
|
||||
|
||||
@Override
|
||||
public void onDisplayRemoved(int displayId) { }
|
||||
|
||||
@Override
|
||||
public void onDisplayChanged(int displayId) {
|
||||
if (displayId == mDisplayId) {
|
||||
updateDisplaySize();
|
||||
}
|
||||
}
|
||||
|
||||
private void updateDisplaySize() {
|
||||
mContext.getDisplay().getRealSize(mDisplaySize);
|
||||
if (mEdgeBackPanel != null) {
|
||||
mEdgeBackPanel.setDisplaySize(mDisplaySize);
|
||||
}
|
||||
}
|
||||
|
||||
void setInsets(int leftInset, int rightInset) {
|
||||
mLeftInset = leftInset;
|
||||
mRightInset = rightInset;
|
||||
}
|
||||
|
||||
enum BackGestureResult {
|
||||
UNKNOWN,
|
||||
BACK_COMPLETED_FROM_LEFT,
|
||||
BACK_COMPLETED_FROM_RIGHT,
|
||||
BACK_CANCELLED_FROM_LEFT,
|
||||
BACK_CANCELLED_FROM_RIGHT,
|
||||
BACK_NOT_STARTED,
|
||||
}
|
||||
|
||||
/** Callback to let the UI react to attempted back gestures. */
|
||||
interface BackGestureAttemptCallback {
|
||||
/** Called whenever any touch is completed. */
|
||||
void onBackGestureAttempted(BackGestureResult result);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,701 @@
|
||||
/*
|
||||
* Copyright (C) 2019 The Android Open Source Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package com.android.quickstep.interaction;
|
||||
|
||||
import android.animation.ValueAnimator;
|
||||
import android.annotation.SuppressLint;
|
||||
import android.content.Context;
|
||||
import android.content.res.Configuration;
|
||||
import android.content.res.Resources;
|
||||
import android.graphics.Canvas;
|
||||
import android.graphics.Paint;
|
||||
import android.graphics.Path;
|
||||
import android.graphics.Point;
|
||||
import android.os.SystemClock;
|
||||
import android.view.Gravity;
|
||||
import android.view.MotionEvent;
|
||||
import android.view.VelocityTracker;
|
||||
import android.view.View;
|
||||
import android.view.WindowManager;
|
||||
import android.view.animation.Interpolator;
|
||||
import android.view.animation.PathInterpolator;
|
||||
|
||||
import androidx.core.math.MathUtils;
|
||||
import androidx.dynamicanimation.animation.DynamicAnimation;
|
||||
import androidx.dynamicanimation.animation.FloatPropertyCompat;
|
||||
import androidx.dynamicanimation.animation.SpringAnimation;
|
||||
import androidx.dynamicanimation.animation.SpringForce;
|
||||
|
||||
import com.android.launcher3.R;
|
||||
import com.android.launcher3.ResourceUtils;
|
||||
import com.android.launcher3.anim.Interpolators;
|
||||
import com.android.launcher3.util.VibratorWrapper;
|
||||
|
||||
/** Forked from platform/frameworks/base/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarEdgePanel.java. */
|
||||
public class EdgeBackGesturePanel extends View {
|
||||
|
||||
private static final String LOG_TAG = "EdgeBackGesturePanel";
|
||||
|
||||
private static final long DISAPPEAR_FADE_ANIMATION_DURATION_MS = 80;
|
||||
private static final long DISAPPEAR_ARROW_ANIMATION_DURATION_MS = 100;
|
||||
|
||||
/**
|
||||
* The time required since the first vibration effect to automatically trigger a click
|
||||
*/
|
||||
private static final int GESTURE_DURATION_FOR_CLICK_MS = 400;
|
||||
|
||||
/**
|
||||
* The basic translation in dp where the arrow resides
|
||||
*/
|
||||
private static final int BASE_TRANSLATION_DP = 32;
|
||||
|
||||
/**
|
||||
* The length of the arrow leg measured from the center to the end
|
||||
*/
|
||||
private static final int ARROW_LENGTH_DP = 18;
|
||||
|
||||
/**
|
||||
* The angle measured from the xAxis, where the leg is when the arrow rests
|
||||
*/
|
||||
private static final int ARROW_ANGLE_WHEN_EXTENDED_DEGREES = 56;
|
||||
|
||||
/**
|
||||
* The angle that is added per 1000 px speed to the angle of the leg
|
||||
*/
|
||||
private static final int ARROW_ANGLE_ADDED_PER_1000_SPEED = 4;
|
||||
|
||||
/**
|
||||
* The maximum angle offset allowed due to speed
|
||||
*/
|
||||
private static final int ARROW_MAX_ANGLE_SPEED_OFFSET_DEGREES = 4;
|
||||
|
||||
/**
|
||||
* The thickness of the arrow. Adjusted to match the home handle (approximately)
|
||||
*/
|
||||
private static final float ARROW_THICKNESS_DP = 2.5f;
|
||||
|
||||
/**
|
||||
* The amount of rubber banding we do for the vertical translation
|
||||
*/
|
||||
private static final int RUBBER_BAND_AMOUNT = 15;
|
||||
|
||||
/**
|
||||
* The interpolator used to rubberband
|
||||
*/
|
||||
private static final Interpolator RUBBER_BAND_INTERPOLATOR =
|
||||
new PathInterpolator(1.0f / 5.0f, 1.0f, 1.0f, 1.0f);
|
||||
|
||||
/**
|
||||
* The amount of rubber banding we do for the translation before base translation
|
||||
*/
|
||||
private static final int RUBBER_BAND_AMOUNT_APPEAR = 4;
|
||||
|
||||
/**
|
||||
* The interpolator used to rubberband the appearing of the arrow.
|
||||
*/
|
||||
private static final Interpolator RUBBER_BAND_INTERPOLATOR_APPEAR =
|
||||
new PathInterpolator(1.0f / RUBBER_BAND_AMOUNT_APPEAR, 1.0f, 1.0f, 1.0f);
|
||||
|
||||
private final WindowManager mWindowManager;
|
||||
private BackCallback mBackCallback;
|
||||
|
||||
/**
|
||||
* The paint the arrow is drawn with
|
||||
*/
|
||||
private final Paint mPaint = new Paint();
|
||||
|
||||
private final float mDensity;
|
||||
private final float mBaseTranslation;
|
||||
private final float mArrowLength;
|
||||
private final float mArrowThickness;
|
||||
|
||||
/**
|
||||
* The minimum delta needed in movement for the arrow to change direction / stop triggering back
|
||||
*/
|
||||
private final float mMinDeltaForSwitch;
|
||||
// The closest to y = 0 that the arrow will be displayed.
|
||||
private int mMinArrowPosition;
|
||||
// The amount the arrow is shifted to avoid the finger.
|
||||
private int mFingerOffset;
|
||||
|
||||
private final float mSwipeThreshold;
|
||||
private final Path mArrowPath = new Path();
|
||||
private final Point mDisplaySize = new Point();
|
||||
|
||||
private final SpringAnimation mAngleAnimation;
|
||||
private final SpringAnimation mTranslationAnimation;
|
||||
private final SpringAnimation mVerticalTranslationAnimation;
|
||||
private final SpringForce mAngleAppearForce;
|
||||
private final SpringForce mAngleDisappearForce;
|
||||
private final ValueAnimator mArrowDisappearAnimation;
|
||||
private final SpringForce mRegularTranslationSpring;
|
||||
private final SpringForce mTriggerBackSpring;
|
||||
|
||||
private VelocityTracker mVelocityTracker;
|
||||
private int mArrowPaddingEnd;
|
||||
private WindowManager.LayoutParams mLayoutParams;
|
||||
|
||||
/**
|
||||
* True if the panel is currently on the left of the screen
|
||||
*/
|
||||
private boolean mIsLeftPanel;
|
||||
|
||||
private float mStartX;
|
||||
private float mStartY;
|
||||
private float mCurrentAngle;
|
||||
/**
|
||||
* The current translation of the arrow
|
||||
*/
|
||||
private float mCurrentTranslation;
|
||||
/**
|
||||
* Where the arrow will be in the resting position.
|
||||
*/
|
||||
private float mDesiredTranslation;
|
||||
|
||||
private boolean mDragSlopPassed;
|
||||
private boolean mArrowsPointLeft;
|
||||
private float mMaxTranslation;
|
||||
private boolean mTriggerBack;
|
||||
private float mPreviousTouchTranslation;
|
||||
private float mTotalTouchDelta;
|
||||
private float mVerticalTranslation;
|
||||
private float mDesiredVerticalTranslation;
|
||||
private float mDesiredAngle;
|
||||
private float mAngleOffset;
|
||||
private float mDisappearAmount;
|
||||
private long mVibrationTime;
|
||||
private int mScreenSize;
|
||||
|
||||
private final DynamicAnimation.OnAnimationEndListener mSetGoneEndListener =
|
||||
new DynamicAnimation.OnAnimationEndListener() {
|
||||
@Override
|
||||
public void onAnimationEnd(
|
||||
DynamicAnimation animation, boolean canceled, float value, float velocity) {
|
||||
animation.removeEndListener(this);
|
||||
if (!canceled) {
|
||||
setVisibility(GONE);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
private static final FloatPropertyCompat<EdgeBackGesturePanel> CURRENT_ANGLE =
|
||||
new FloatPropertyCompat<EdgeBackGesturePanel>("currentAngle") {
|
||||
@Override
|
||||
public void setValue(EdgeBackGesturePanel object, float value) {
|
||||
object.setCurrentAngle(value);
|
||||
}
|
||||
|
||||
@Override
|
||||
public float getValue(EdgeBackGesturePanel object) {
|
||||
return object.getCurrentAngle();
|
||||
}
|
||||
};
|
||||
|
||||
private static final FloatPropertyCompat<EdgeBackGesturePanel> CURRENT_TRANSLATION =
|
||||
new FloatPropertyCompat<EdgeBackGesturePanel>("currentTranslation") {
|
||||
@Override
|
||||
public void setValue(EdgeBackGesturePanel object, float value) {
|
||||
object.setCurrentTranslation(value);
|
||||
}
|
||||
|
||||
@Override
|
||||
public float getValue(EdgeBackGesturePanel object) {
|
||||
return object.getCurrentTranslation();
|
||||
}
|
||||
};
|
||||
|
||||
private static final FloatPropertyCompat<EdgeBackGesturePanel> CURRENT_VERTICAL_TRANSLATION =
|
||||
new FloatPropertyCompat<EdgeBackGesturePanel>("verticalTranslation") {
|
||||
|
||||
@Override
|
||||
public void setValue(EdgeBackGesturePanel object, float value) {
|
||||
object.setVerticalTranslation(value);
|
||||
}
|
||||
|
||||
@Override
|
||||
public float getValue(EdgeBackGesturePanel object) {
|
||||
return object.getVerticalTranslation();
|
||||
}
|
||||
};
|
||||
|
||||
public EdgeBackGesturePanel(Context context) {
|
||||
super(context);
|
||||
|
||||
mWindowManager = context.getSystemService(WindowManager.class);
|
||||
|
||||
mDensity = context.getResources().getDisplayMetrics().density;
|
||||
|
||||
mBaseTranslation = dp(BASE_TRANSLATION_DP);
|
||||
mArrowLength = dp(ARROW_LENGTH_DP);
|
||||
mArrowThickness = dp(ARROW_THICKNESS_DP);
|
||||
mMinDeltaForSwitch = dp(32);
|
||||
|
||||
mPaint.setStrokeWidth(mArrowThickness);
|
||||
mPaint.setStrokeCap(Paint.Cap.ROUND);
|
||||
mPaint.setAntiAlias(true);
|
||||
mPaint.setStyle(Paint.Style.STROKE);
|
||||
mPaint.setStrokeJoin(Paint.Join.ROUND);
|
||||
|
||||
mArrowDisappearAnimation = ValueAnimator.ofFloat(0.0f, 1.0f);
|
||||
mArrowDisappearAnimation.setDuration(DISAPPEAR_ARROW_ANIMATION_DURATION_MS);
|
||||
mArrowDisappearAnimation.setInterpolator(Interpolators.FAST_OUT_SLOW_IN);
|
||||
mArrowDisappearAnimation.addUpdateListener(animation -> {
|
||||
mDisappearAmount = (float) animation.getAnimatedValue();
|
||||
invalidate();
|
||||
});
|
||||
|
||||
mAngleAnimation =
|
||||
new SpringAnimation(this, CURRENT_ANGLE);
|
||||
mAngleAppearForce = new SpringForce()
|
||||
.setStiffness(500)
|
||||
.setDampingRatio(0.5f);
|
||||
mAngleDisappearForce = new SpringForce()
|
||||
.setStiffness(SpringForce.STIFFNESS_MEDIUM)
|
||||
.setDampingRatio(SpringForce.DAMPING_RATIO_MEDIUM_BOUNCY)
|
||||
.setFinalPosition(90);
|
||||
mAngleAnimation.setSpring(mAngleAppearForce).setMaxValue(90);
|
||||
|
||||
mTranslationAnimation =
|
||||
new SpringAnimation(this, CURRENT_TRANSLATION);
|
||||
mRegularTranslationSpring = new SpringForce()
|
||||
.setStiffness(SpringForce.STIFFNESS_MEDIUM)
|
||||
.setDampingRatio(SpringForce.DAMPING_RATIO_LOW_BOUNCY);
|
||||
mTriggerBackSpring = new SpringForce()
|
||||
.setStiffness(450)
|
||||
.setDampingRatio(SpringForce.DAMPING_RATIO_LOW_BOUNCY);
|
||||
mTranslationAnimation.setSpring(mRegularTranslationSpring);
|
||||
mVerticalTranslationAnimation =
|
||||
new SpringAnimation(this, CURRENT_VERTICAL_TRANSLATION);
|
||||
mVerticalTranslationAnimation.setSpring(
|
||||
new SpringForce()
|
||||
.setStiffness(SpringForce.STIFFNESS_MEDIUM)
|
||||
.setDampingRatio(SpringForce.DAMPING_RATIO_LOW_BOUNCY));
|
||||
mPaint.setColor(context.getColor(R.color.back_arrow_color_dark));
|
||||
loadDimens();
|
||||
updateArrowDirection();
|
||||
|
||||
mSwipeThreshold = ResourceUtils.getDimenByName(
|
||||
"navigation_edge_action_drag_threshold", context.getResources(), 16 /* defaultValue */);
|
||||
setVisibility(GONE);
|
||||
}
|
||||
|
||||
void onDestroy() {
|
||||
mWindowManager.removeView(this);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean hasOverlappingRendering() {
|
||||
return false;
|
||||
}
|
||||
|
||||
@SuppressLint("RtlHardcoded")
|
||||
void setIsLeftPanel(boolean isLeftPanel) {
|
||||
mIsLeftPanel = isLeftPanel;
|
||||
mLayoutParams.gravity = mIsLeftPanel
|
||||
? (Gravity.LEFT | Gravity.TOP)
|
||||
: (Gravity.RIGHT | Gravity.TOP);
|
||||
}
|
||||
|
||||
boolean getIsLeftPanel() {
|
||||
return mIsLeftPanel;
|
||||
}
|
||||
|
||||
void setDisplaySize(Point displaySize) {
|
||||
mDisplaySize.set(displaySize.x, displaySize.y);
|
||||
mScreenSize = Math.min(mDisplaySize.x, mDisplaySize.y);
|
||||
}
|
||||
|
||||
void setBackCallback(BackCallback callback) {
|
||||
mBackCallback = callback;
|
||||
}
|
||||
|
||||
void setLayoutParams(WindowManager.LayoutParams layoutParams) {
|
||||
mLayoutParams = layoutParams;
|
||||
mWindowManager.addView(this, mLayoutParams);
|
||||
}
|
||||
|
||||
private float getCurrentAngle() {
|
||||
return mCurrentAngle;
|
||||
}
|
||||
|
||||
private float getCurrentTranslation() {
|
||||
return mCurrentTranslation;
|
||||
}
|
||||
|
||||
void onMotionEvent(MotionEvent event) {
|
||||
if (mVelocityTracker == null) {
|
||||
mVelocityTracker = VelocityTracker.obtain();
|
||||
}
|
||||
mVelocityTracker.addMovement(event);
|
||||
switch (event.getActionMasked()) {
|
||||
case MotionEvent.ACTION_DOWN:
|
||||
mDragSlopPassed = false;
|
||||
resetOnDown();
|
||||
mStartX = event.getX();
|
||||
mStartY = event.getY();
|
||||
setVisibility(VISIBLE);
|
||||
updatePosition(event.getY());
|
||||
mWindowManager.updateViewLayout(this, mLayoutParams);
|
||||
break;
|
||||
case MotionEvent.ACTION_MOVE:
|
||||
handleMoveEvent(event);
|
||||
break;
|
||||
case MotionEvent.ACTION_UP:
|
||||
if (mTriggerBack) {
|
||||
triggerBack();
|
||||
} else {
|
||||
cancelBack();
|
||||
}
|
||||
mVelocityTracker.recycle();
|
||||
mVelocityTracker = null;
|
||||
break;
|
||||
case MotionEvent.ACTION_CANCEL:
|
||||
cancelBack();
|
||||
mVelocityTracker.recycle();
|
||||
mVelocityTracker = null;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onConfigurationChanged(Configuration newConfig) {
|
||||
super.onConfigurationChanged(newConfig);
|
||||
updateArrowDirection();
|
||||
loadDimens();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onDraw(Canvas canvas) {
|
||||
float pointerPosition = mCurrentTranslation - mArrowThickness / 2.0f;
|
||||
canvas.save();
|
||||
canvas.translate(
|
||||
mIsLeftPanel ? pointerPosition : getWidth() - pointerPosition,
|
||||
(getHeight() * 0.5f) + mVerticalTranslation);
|
||||
|
||||
// Let's calculate the position of the end based on the angle
|
||||
float x = (polarToCartX(mCurrentAngle) * mArrowLength);
|
||||
float y = (polarToCartY(mCurrentAngle) * mArrowLength);
|
||||
Path arrowPath = calculatePath(x, y);
|
||||
|
||||
canvas.drawPath(arrowPath, mPaint);
|
||||
canvas.restore();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onLayout(boolean changed, int left, int top, int right, int bottom) {
|
||||
super.onLayout(changed, left, top, right, bottom);
|
||||
mMaxTranslation = getWidth() - mArrowPaddingEnd;
|
||||
}
|
||||
|
||||
private void loadDimens() {
|
||||
Resources res = getResources();
|
||||
mArrowPaddingEnd = ResourceUtils.getDimenByName("navigation_edge_panel_padding", res, 8);
|
||||
mMinArrowPosition = ResourceUtils.getDimenByName("navigation_edge_arrow_min_y", res, 64);
|
||||
mFingerOffset = ResourceUtils.getDimenByName("navigation_edge_finger_offset", res, 48);
|
||||
}
|
||||
|
||||
private void updateArrowDirection() {
|
||||
// Both panels arrow point the same way
|
||||
mArrowsPointLeft = getLayoutDirection() == LAYOUT_DIRECTION_LTR;
|
||||
invalidate();
|
||||
}
|
||||
|
||||
private float getStaticArrowWidth() {
|
||||
return polarToCartX(ARROW_ANGLE_WHEN_EXTENDED_DEGREES) * mArrowLength;
|
||||
}
|
||||
|
||||
private float polarToCartX(float angleInDegrees) {
|
||||
return (float) Math.cos(Math.toRadians(angleInDegrees));
|
||||
}
|
||||
|
||||
private float polarToCartY(float angleInDegrees) {
|
||||
return (float) Math.sin(Math.toRadians(angleInDegrees));
|
||||
}
|
||||
|
||||
private Path calculatePath(float x, float y) {
|
||||
if (!mArrowsPointLeft) {
|
||||
x = -x;
|
||||
}
|
||||
float extent = lerp(1.0f, 0.75f, mDisappearAmount);
|
||||
x = x * extent;
|
||||
y = y * extent;
|
||||
mArrowPath.reset();
|
||||
mArrowPath.moveTo(x, y);
|
||||
mArrowPath.lineTo(0, 0);
|
||||
mArrowPath.lineTo(x, -y);
|
||||
return mArrowPath;
|
||||
}
|
||||
|
||||
private static float lerp(float start, float stop, float amount) {
|
||||
return start + (stop - start) * amount;
|
||||
}
|
||||
|
||||
private void triggerBack() {
|
||||
if (mBackCallback != null) {
|
||||
mBackCallback.triggerBack();
|
||||
}
|
||||
|
||||
if (mVelocityTracker == null) {
|
||||
mVelocityTracker = VelocityTracker.obtain();
|
||||
}
|
||||
mVelocityTracker.computeCurrentVelocity(1000);
|
||||
// Only do the extra translation if we're not already flinging
|
||||
boolean isSlow = Math.abs(mVelocityTracker.getXVelocity()) < 500;
|
||||
if (isSlow
|
||||
|| SystemClock.uptimeMillis() - mVibrationTime >= GESTURE_DURATION_FOR_CLICK_MS) {
|
||||
VibratorWrapper.INSTANCE.get(getContext()).vibrate(VibratorWrapper.EFFECT_CLICK);
|
||||
}
|
||||
|
||||
// Let's also snap the angle a bit
|
||||
if (mAngleOffset > -4) {
|
||||
mAngleOffset = Math.max(-8, mAngleOffset - 8);
|
||||
updateAngle(true /* animated */);
|
||||
}
|
||||
|
||||
// Finally, after the translation, animate back and disappear the arrow
|
||||
Runnable translationEnd = () -> {
|
||||
// let's snap it back
|
||||
mAngleOffset = Math.max(0, mAngleOffset + 8);
|
||||
updateAngle(true /* animated */);
|
||||
|
||||
mTranslationAnimation.setSpring(mTriggerBackSpring);
|
||||
// Translate the arrow back a bit to make for a nice transition
|
||||
setDesiredTranslation(mDesiredTranslation - dp(32), true /* animated */);
|
||||
animate().alpha(0f).setDuration(DISAPPEAR_FADE_ANIMATION_DURATION_MS)
|
||||
.withEndAction(() -> setVisibility(GONE));
|
||||
mArrowDisappearAnimation.start();
|
||||
};
|
||||
if (mTranslationAnimation.isRunning()) {
|
||||
mTranslationAnimation.addEndListener(new DynamicAnimation.OnAnimationEndListener() {
|
||||
@Override
|
||||
public void onAnimationEnd(DynamicAnimation animation, boolean canceled,
|
||||
float value,
|
||||
float velocity) {
|
||||
animation.removeEndListener(this);
|
||||
if (!canceled) {
|
||||
translationEnd.run();
|
||||
}
|
||||
}
|
||||
});
|
||||
} else {
|
||||
translationEnd.run();
|
||||
}
|
||||
}
|
||||
|
||||
private void cancelBack() {
|
||||
if (mBackCallback != null) {
|
||||
mBackCallback.cancelBack();
|
||||
}
|
||||
|
||||
if (mTranslationAnimation.isRunning()) {
|
||||
mTranslationAnimation.addEndListener(mSetGoneEndListener);
|
||||
} else {
|
||||
setVisibility(GONE);
|
||||
}
|
||||
}
|
||||
|
||||
private void resetOnDown() {
|
||||
animate().cancel();
|
||||
mAngleAnimation.cancel();
|
||||
mTranslationAnimation.cancel();
|
||||
mVerticalTranslationAnimation.cancel();
|
||||
mArrowDisappearAnimation.cancel();
|
||||
mAngleOffset = 0;
|
||||
mTranslationAnimation.setSpring(mRegularTranslationSpring);
|
||||
// Reset the arrow to the side
|
||||
setTriggerBack(false /* triggerBack */, false /* animated */);
|
||||
setDesiredTranslation(0, false /* animated */);
|
||||
setCurrentTranslation(0);
|
||||
updateAngle(false /* animate */);
|
||||
mPreviousTouchTranslation = 0;
|
||||
mTotalTouchDelta = 0;
|
||||
mVibrationTime = 0;
|
||||
setDesiredVerticalTransition(0, false /* animated */);
|
||||
}
|
||||
|
||||
private void handleMoveEvent(MotionEvent event) {
|
||||
float x = event.getX();
|
||||
float y = event.getY();
|
||||
float touchTranslation = Math.abs(x - mStartX);
|
||||
float yOffset = y - mStartY;
|
||||
float delta = touchTranslation - mPreviousTouchTranslation;
|
||||
if (Math.abs(delta) > 0) {
|
||||
if (Math.signum(delta) == Math.signum(mTotalTouchDelta)) {
|
||||
mTotalTouchDelta += delta;
|
||||
} else {
|
||||
mTotalTouchDelta = delta;
|
||||
}
|
||||
}
|
||||
mPreviousTouchTranslation = touchTranslation;
|
||||
|
||||
// Apply a haptic on drag slop passed
|
||||
if (!mDragSlopPassed && touchTranslation > mSwipeThreshold) {
|
||||
mDragSlopPassed = true;
|
||||
VibratorWrapper.INSTANCE.get(getContext()).vibrate(VibratorWrapper.EFFECT_CLICK);
|
||||
mVibrationTime = SystemClock.uptimeMillis();
|
||||
|
||||
// Let's show the arrow and animate it in!
|
||||
mDisappearAmount = 0.0f;
|
||||
setAlpha(1f);
|
||||
// And animate it go to back by default!
|
||||
setTriggerBack(true /* triggerBack */, true /* animated */);
|
||||
}
|
||||
|
||||
// Let's make sure we only go to the baseextend and apply rubberbanding afterwards
|
||||
if (touchTranslation > mBaseTranslation) {
|
||||
float diff = touchTranslation - mBaseTranslation;
|
||||
float progress = MathUtils.clamp(diff / (mScreenSize - mBaseTranslation), 0, 1);
|
||||
progress = RUBBER_BAND_INTERPOLATOR.getInterpolation(progress)
|
||||
* (mMaxTranslation - mBaseTranslation);
|
||||
touchTranslation = mBaseTranslation + progress;
|
||||
} else {
|
||||
float diff = mBaseTranslation - touchTranslation;
|
||||
float progress = MathUtils.clamp(diff / mBaseTranslation, 0, 1);
|
||||
progress = RUBBER_BAND_INTERPOLATOR_APPEAR.getInterpolation(progress)
|
||||
* (mBaseTranslation / RUBBER_BAND_AMOUNT_APPEAR);
|
||||
touchTranslation = mBaseTranslation - progress;
|
||||
}
|
||||
// By default we just assume the current direction is kept
|
||||
boolean triggerBack = mTriggerBack;
|
||||
|
||||
// First lets see if we had continuous motion in one direction for a while
|
||||
if (Math.abs(mTotalTouchDelta) > mMinDeltaForSwitch) {
|
||||
triggerBack = mTotalTouchDelta > 0;
|
||||
}
|
||||
|
||||
// Then, let's see if our velocity tells us to change direction
|
||||
mVelocityTracker.computeCurrentVelocity(1000);
|
||||
float xVelocity = mVelocityTracker.getXVelocity();
|
||||
float yVelocity = mVelocityTracker.getYVelocity();
|
||||
float velocity = (float) Math.hypot(xVelocity, yVelocity);
|
||||
mAngleOffset = Math.min(velocity / 1000 * ARROW_ANGLE_ADDED_PER_1000_SPEED,
|
||||
ARROW_MAX_ANGLE_SPEED_OFFSET_DEGREES) * Math.signum(xVelocity);
|
||||
if (mIsLeftPanel && mArrowsPointLeft || !mIsLeftPanel && !mArrowsPointLeft) {
|
||||
mAngleOffset *= -1;
|
||||
}
|
||||
|
||||
// Last if the direction in Y is bigger than X * 2 we also abort
|
||||
if (Math.abs(yOffset) > Math.abs(x - mStartX) * 2) {
|
||||
triggerBack = false;
|
||||
}
|
||||
setTriggerBack(triggerBack, true /* animated */);
|
||||
|
||||
if (!mTriggerBack) {
|
||||
touchTranslation = 0;
|
||||
} else if (mIsLeftPanel && mArrowsPointLeft
|
||||
|| (!mIsLeftPanel && !mArrowsPointLeft)) {
|
||||
// If we're on the left we should move less, because the arrow is facing the other
|
||||
// direction
|
||||
touchTranslation -= getStaticArrowWidth();
|
||||
}
|
||||
setDesiredTranslation(touchTranslation, true /* animated */);
|
||||
updateAngle(true /* animated */);
|
||||
|
||||
float maxYOffset = getHeight() / 2.0f - mArrowLength;
|
||||
float progress =
|
||||
MathUtils.clamp(Math.abs(yOffset) / (maxYOffset * RUBBER_BAND_AMOUNT), 0, 1);
|
||||
float verticalTranslation = RUBBER_BAND_INTERPOLATOR.getInterpolation(progress)
|
||||
* maxYOffset * Math.signum(yOffset);
|
||||
setDesiredVerticalTransition(verticalTranslation, true /* animated */);
|
||||
}
|
||||
|
||||
private void updatePosition(float touchY) {
|
||||
float position = touchY - mFingerOffset;
|
||||
position = Math.max(position, mMinArrowPosition);
|
||||
position -= mLayoutParams.height / 2.0f;
|
||||
mLayoutParams.y = MathUtils.clamp((int) position, 0, mDisplaySize.y);
|
||||
}
|
||||
|
||||
private void setDesiredVerticalTransition(float verticalTranslation, boolean animated) {
|
||||
if (mDesiredVerticalTranslation != verticalTranslation) {
|
||||
mDesiredVerticalTranslation = verticalTranslation;
|
||||
if (!animated) {
|
||||
setVerticalTranslation(verticalTranslation);
|
||||
} else {
|
||||
mVerticalTranslationAnimation.animateToFinalPosition(verticalTranslation);
|
||||
}
|
||||
invalidate();
|
||||
}
|
||||
}
|
||||
|
||||
private void setVerticalTranslation(float verticalTranslation) {
|
||||
mVerticalTranslation = verticalTranslation;
|
||||
invalidate();
|
||||
}
|
||||
|
||||
private float getVerticalTranslation() {
|
||||
return mVerticalTranslation;
|
||||
}
|
||||
|
||||
private void setDesiredTranslation(float desiredTranslation, boolean animated) {
|
||||
if (mDesiredTranslation != desiredTranslation) {
|
||||
mDesiredTranslation = desiredTranslation;
|
||||
if (!animated) {
|
||||
setCurrentTranslation(desiredTranslation);
|
||||
} else {
|
||||
mTranslationAnimation.animateToFinalPosition(desiredTranslation);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void setCurrentTranslation(float currentTranslation) {
|
||||
mCurrentTranslation = currentTranslation;
|
||||
invalidate();
|
||||
}
|
||||
|
||||
private void setTriggerBack(boolean triggerBack, boolean animated) {
|
||||
if (mTriggerBack != triggerBack) {
|
||||
mTriggerBack = triggerBack;
|
||||
mAngleAnimation.cancel();
|
||||
updateAngle(animated);
|
||||
// Whenever the trigger back state changes the existing translation animation should be
|
||||
// cancelled
|
||||
mTranslationAnimation.cancel();
|
||||
}
|
||||
}
|
||||
|
||||
private void updateAngle(boolean animated) {
|
||||
float newAngle = mTriggerBack ? ARROW_ANGLE_WHEN_EXTENDED_DEGREES + mAngleOffset : 90;
|
||||
if (newAngle != mDesiredAngle) {
|
||||
if (!animated) {
|
||||
setCurrentAngle(newAngle);
|
||||
} else {
|
||||
mAngleAnimation.setSpring(mTriggerBack ? mAngleAppearForce : mAngleDisappearForce);
|
||||
mAngleAnimation.animateToFinalPosition(newAngle);
|
||||
}
|
||||
mDesiredAngle = newAngle;
|
||||
}
|
||||
}
|
||||
|
||||
private void setCurrentAngle(float currentAngle) {
|
||||
mCurrentAngle = currentAngle;
|
||||
invalidate();
|
||||
}
|
||||
|
||||
private float dp(float dp) {
|
||||
return mDensity * dp;
|
||||
}
|
||||
|
||||
/** Callback to let the gesture handler react to the detected back gestures. */
|
||||
interface BackCallback {
|
||||
/** Indicates that a Back gesture was recognized. */
|
||||
void triggerBack();
|
||||
|
||||
/** Indicates that the gesture was cancelled. */
|
||||
void cancelBack();
|
||||
}
|
||||
}
|
||||
@@ -30,12 +30,11 @@ import com.android.quickstep.interaction.BackGestureTutorialFragment.TutorialSte
|
||||
import com.android.quickstep.interaction.BackGestureTutorialFragment.TutorialType;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Optional;
|
||||
|
||||
/** Shows the gesture interactive sandbox in full screen mode. */
|
||||
public class GestureSandboxActivity extends FragmentActivity {
|
||||
|
||||
Optional<BackGestureTutorialFragment> mFragment = Optional.empty();
|
||||
private BackGestureTutorialFragment mFragment;
|
||||
|
||||
@Override
|
||||
protected void onCreate(Bundle savedInstanceState) {
|
||||
@@ -43,10 +42,10 @@ public class GestureSandboxActivity extends FragmentActivity {
|
||||
requestWindowFeature(Window.FEATURE_NO_TITLE);
|
||||
setContentView(R.layout.back_gesture_tutorial_activity);
|
||||
|
||||
mFragment = Optional.of(BackGestureTutorialFragment.newInstance(TutorialStep.ENGAGED,
|
||||
TutorialType.RIGHT_EDGE_BACK_NAVIGATION));
|
||||
mFragment = BackGestureTutorialFragment.newInstance(
|
||||
TutorialStep.ENGAGED, TutorialType.RIGHT_EDGE_BACK_NAVIGATION);
|
||||
getSupportFragmentManager().beginTransaction()
|
||||
.add(R.id.back_gesture_tutorial_fragment_container, mFragment.get())
|
||||
.add(R.id.back_gesture_tutorial_fragment_container, mFragment)
|
||||
.commit();
|
||||
}
|
||||
|
||||
@@ -54,6 +53,13 @@ public class GestureSandboxActivity extends FragmentActivity {
|
||||
public void onAttachedToWindow() {
|
||||
super.onAttachedToWindow();
|
||||
disableSystemGestures();
|
||||
mFragment.onAttachedToWindow();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onDetachedFromWindow() {
|
||||
super.onDetachedFromWindow();
|
||||
mFragment.onDetachedFromWindow();
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -64,13 +70,6 @@ public class GestureSandboxActivity extends FragmentActivity {
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onBackPressed() {
|
||||
if (mFragment.isPresent()) {
|
||||
mFragment.get().onBackPressed();
|
||||
}
|
||||
}
|
||||
|
||||
private void hideSystemUI() {
|
||||
getWindow().getDecorView().setSystemUiVisibility(
|
||||
View.SYSTEM_UI_FLAG_IMMERSIVE_STICKY
|
||||
|
||||
@@ -13,39 +13,26 @@
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
-->
|
||||
<com.android.launcher3.views.WorkFooterContainer
|
||||
<com.android.launcher3.allapps.WorkModeSwitch
|
||||
xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:id="@+id/work_toggle_container"
|
||||
android:focusable="true"
|
||||
android:orientation="horizontal"
|
||||
style="@style/PrimaryMediumText"
|
||||
android:id="@+id/work_mode_toggle"
|
||||
android:drawableStart="@drawable/ic_corp"
|
||||
android:drawablePadding="16dp"
|
||||
android:drawableTint="?attr/workProfileOverlayTextColor"
|
||||
android:textColor="?attr/workProfileOverlayTextColor"
|
||||
android:layout_alignParentBottom="true"
|
||||
android:ellipsize="end"
|
||||
android:gravity="start"
|
||||
android:lines="1"
|
||||
android:showText="false"
|
||||
android:textSize="16sp"
|
||||
android:background="?attr/allAppsScrimColor"
|
||||
android:text="@string/work_profile_toggle_label"
|
||||
android:paddingBottom="@dimen/all_apps_work_profile_tab_footer_padding"
|
||||
android:paddingLeft="@dimen/all_apps_work_profile_tab_footer_padding"
|
||||
android:paddingRight="@dimen/all_apps_work_profile_tab_footer_padding"
|
||||
android:paddingTop="@dimen/all_apps_work_profile_tab_footer_padding">
|
||||
|
||||
<TextView
|
||||
style="@style/PrimaryMediumText"
|
||||
android:id="@+id/work_mode_label"
|
||||
android:layout_width="0dp"
|
||||
android:layout_weight="1"
|
||||
android:drawableStart="@drawable/ic_corp"
|
||||
android:drawablePadding="16dp"
|
||||
android:drawableTint="?attr/workProfileOverlayTextColor"
|
||||
android:textColor="?attr/workProfileOverlayTextColor"
|
||||
android:layout_height="wrap_content"
|
||||
android:ellipsize="end"
|
||||
android:gravity="center_vertical"
|
||||
android:lines="1"
|
||||
android:minHeight="24dp"
|
||||
android:paddingEnd="12dp"
|
||||
android:text="@string/work_profile_toggle_label"
|
||||
android:textSize="16sp"/>
|
||||
<com.android.launcher3.allapps.WorkModeSwitch
|
||||
android:id="@+id/work_mode_toggle"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"/>
|
||||
|
||||
</com.android.launcher3.views.WorkFooterContainer>
|
||||
android:paddingTop="@dimen/all_apps_work_profile_tab_footer_padding"
|
||||
/>
|
||||
@@ -142,6 +142,7 @@
|
||||
|
||||
<item name="staggered_damping_ratio" type="dimen" format="float">0.7</item>
|
||||
<item name="staggered_stiffness" type="dimen" format="float">150</item>
|
||||
<dimen name="unlock_staggered_velocity_dp_per_s">3dp</dimen>
|
||||
|
||||
<!-- Swipe up to home related -->
|
||||
<dimen name="swipe_up_fling_min_visible_change">18dp</dimen>
|
||||
@@ -170,8 +171,12 @@
|
||||
|
||||
<item>@dimen/staggered_damping_ratio</item>
|
||||
<item>@dimen/staggered_stiffness</item>
|
||||
<item>@dimen/unlock_staggered_velocity_dp_per_s</item>
|
||||
|
||||
<item>@dimen/swipe_up_fling_min_visible_change</item>
|
||||
<item>@dimen/swipe_up_y_overshoot</item>
|
||||
</array>
|
||||
|
||||
<string-array name="live_wallpapers_remove_sysui_scrims">
|
||||
</string-array>
|
||||
</resources>
|
||||
|
||||
@@ -180,7 +180,10 @@ public abstract class AbstractFloatingView extends LinearLayout implements Touch
|
||||
return null;
|
||||
}
|
||||
|
||||
protected static <T extends AbstractFloatingView> T getOpenView(
|
||||
/**
|
||||
* Returns a view matching FloatingViewType
|
||||
*/
|
||||
public static <T extends AbstractFloatingView> T getOpenView(
|
||||
ActivityContext activity, @FloatingViewType int type) {
|
||||
BaseDragLayer dragLayer = activity.getDragLayer();
|
||||
if (dragLayer == null) return null;
|
||||
|
||||
@@ -46,6 +46,7 @@ import android.widget.TextView;
|
||||
import com.android.launcher3.Launcher.OnResumeCallback;
|
||||
import com.android.launcher3.accessibility.LauncherAccessibilityDelegate;
|
||||
import com.android.launcher3.dot.DotInfo;
|
||||
import com.android.launcher3.dragndrop.DraggableView;
|
||||
import com.android.launcher3.folder.FolderIcon;
|
||||
import com.android.launcher3.graphics.IconPalette;
|
||||
import com.android.launcher3.graphics.IconShape;
|
||||
@@ -65,7 +66,7 @@ import java.text.NumberFormat;
|
||||
* too aggressive.
|
||||
*/
|
||||
public class BubbleTextView extends TextView implements ItemInfoUpdateReceiver, OnResumeCallback,
|
||||
IconLabelDotView {
|
||||
IconLabelDotView, DraggableView {
|
||||
|
||||
private static final int DISPLAY_WORKSPACE = 0;
|
||||
private static final int DISPLAY_ALL_APPS = 1;
|
||||
@@ -103,7 +104,7 @@ public class BubbleTextView extends TextView implements ItemInfoUpdateReceiver,
|
||||
|
||||
private final ActivityContext mActivity;
|
||||
private Drawable mIcon;
|
||||
private final boolean mCenterVertically;
|
||||
private boolean mCenterVertically;
|
||||
|
||||
private final int mDisplay;
|
||||
|
||||
@@ -701,4 +702,24 @@ public class BubbleTextView extends TextView implements ItemInfoUpdateReceiver,
|
||||
public int getIconSize() {
|
||||
return mIconSize;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getViewType() {
|
||||
return DRAGGABLE_ICON;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void getVisualDragBounds(Rect bounds) {
|
||||
DeviceProfile grid = mActivity.getDeviceProfile();
|
||||
BubbleTextView.getIconBounds(this, bounds, grid.iconSizePx);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void prepareDrawDragView() {
|
||||
if (getIcon() instanceof FastBitmapDrawable) {
|
||||
FastBitmapDrawable icon = (FastBitmapDrawable) getIcon();
|
||||
icon.setScale(1f);
|
||||
}
|
||||
setForceHideDot(true);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -155,7 +155,7 @@ public abstract class ButtonDropTarget extends TextView
|
||||
|
||||
@Override
|
||||
public final void onDragEnter(DragObject d) {
|
||||
if (!d.accessibleDrag && !mTextVisible) {
|
||||
if (!mAccessibleDrag && !mTextVisible) {
|
||||
// Show tooltip
|
||||
hideTooltip();
|
||||
|
||||
|
||||
@@ -53,11 +53,10 @@ import androidx.core.view.ViewCompat;
|
||||
|
||||
import com.android.launcher3.LauncherSettings.Favorites;
|
||||
import com.android.launcher3.accessibility.DragAndDropAccessibilityDelegate;
|
||||
import com.android.launcher3.accessibility.FolderAccessibilityHelper;
|
||||
import com.android.launcher3.accessibility.WorkspaceAccessibilityHelper;
|
||||
import com.android.launcher3.anim.Interpolators;
|
||||
import com.android.launcher3.anim.PropertyListBuilder;
|
||||
import com.android.launcher3.config.FeatureFlags;
|
||||
import com.android.launcher3.dragndrop.DraggableView;
|
||||
import com.android.launcher3.folder.PreviewBackground;
|
||||
import com.android.launcher3.graphics.DragPreviewProvider;
|
||||
import com.android.launcher3.graphics.RotationMode;
|
||||
@@ -79,9 +78,6 @@ import java.util.Comparator;
|
||||
import java.util.Stack;
|
||||
|
||||
public class CellLayout extends ViewGroup implements Transposable {
|
||||
public static final int WORKSPACE_ACCESSIBILITY_DRAG = 2;
|
||||
public static final int FOLDER_ACCESSIBILITY_DRAG = 1;
|
||||
|
||||
private static final String TAG = "CellLayout";
|
||||
private static final boolean LOGD = false;
|
||||
|
||||
@@ -105,6 +101,11 @@ public class CellLayout extends ViewGroup implements Transposable {
|
||||
@Thunk final int[] mTmpPoint = new int[2];
|
||||
@Thunk final int[] mTempLocation = new int[2];
|
||||
|
||||
// Used to visualize / debug the Grid of the CellLayout
|
||||
private static final boolean VISUALIZE_GRID = false;
|
||||
private Rect mVisualizeGridRect = new Rect();
|
||||
private Paint mVisualizeGridPaint = new Paint();
|
||||
|
||||
private GridOccupancy mOccupied;
|
||||
private GridOccupancy mTmpOccupied;
|
||||
|
||||
@@ -182,7 +183,6 @@ public class CellLayout extends ViewGroup implements Transposable {
|
||||
private static final Paint sPaint = new Paint();
|
||||
|
||||
// Related to accessible drag and drop
|
||||
private DragAndDropAccessibilityDelegate mTouchHelper;
|
||||
private boolean mUseTouchHelper = false;
|
||||
private RotationMode mRotationMode = RotationMode.NORMAL;
|
||||
|
||||
@@ -292,26 +292,20 @@ public class CellLayout extends ViewGroup implements Transposable {
|
||||
addView(mShortcutsAndWidgets);
|
||||
}
|
||||
|
||||
public void enableAccessibleDrag(boolean enable, int dragType) {
|
||||
mUseTouchHelper = enable;
|
||||
if (!enable) {
|
||||
ViewCompat.setAccessibilityDelegate(this, null);
|
||||
setImportantForAccessibility(IMPORTANT_FOR_ACCESSIBILITY_NO);
|
||||
getShortcutsAndWidgets().setImportantForAccessibility(IMPORTANT_FOR_ACCESSIBILITY_NO);
|
||||
setOnClickListener(null);
|
||||
} else {
|
||||
if (dragType == WORKSPACE_ACCESSIBILITY_DRAG &&
|
||||
!(mTouchHelper instanceof WorkspaceAccessibilityHelper)) {
|
||||
mTouchHelper = new WorkspaceAccessibilityHelper(this);
|
||||
} else if (dragType == FOLDER_ACCESSIBILITY_DRAG &&
|
||||
!(mTouchHelper instanceof FolderAccessibilityHelper)) {
|
||||
mTouchHelper = new FolderAccessibilityHelper(this);
|
||||
}
|
||||
ViewCompat.setAccessibilityDelegate(this, mTouchHelper);
|
||||
setImportantForAccessibility(IMPORTANT_FOR_ACCESSIBILITY_YES);
|
||||
getShortcutsAndWidgets().setImportantForAccessibility(IMPORTANT_FOR_ACCESSIBILITY_YES);
|
||||
setOnClickListener(mTouchHelper);
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets or clears a delegate used for accessible drag and drop
|
||||
*/
|
||||
public void setDragAndDropAccessibilityDelegate(DragAndDropAccessibilityDelegate delegate) {
|
||||
setOnClickListener(delegate);
|
||||
setOnHoverListener(delegate);
|
||||
ViewCompat.setAccessibilityDelegate(this, delegate);
|
||||
|
||||
mUseTouchHelper = delegate != null;
|
||||
int accessibilityFlag = mUseTouchHelper
|
||||
? IMPORTANT_FOR_ACCESSIBILITY_YES : IMPORTANT_FOR_ACCESSIBILITY_NO;
|
||||
setImportantForAccessibility(accessibilityFlag);
|
||||
getShortcutsAndWidgets().setImportantForAccessibility(accessibilityFlag);
|
||||
|
||||
// Invalidate the accessibility hierarchy
|
||||
if (getParent() != null) {
|
||||
@@ -338,15 +332,6 @@ public class CellLayout extends ViewGroup implements Transposable {
|
||||
super.setPadding(mTempRect.left, mTempRect.top, mTempRect.right, mTempRect.bottom);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean dispatchHoverEvent(MotionEvent event) {
|
||||
// Always attempt to dispatch hover events to accessibility first.
|
||||
if (mUseTouchHelper && mTouchHelper.dispatchHoverEvent(event)) {
|
||||
return true;
|
||||
}
|
||||
return super.dispatchHoverEvent(event);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean onInterceptTouchEvent(MotionEvent ev) {
|
||||
if (mUseTouchHelper ||
|
||||
@@ -483,6 +468,37 @@ public class CellLayout extends ViewGroup implements Transposable {
|
||||
mFolderLeaveBehind.drawLeaveBehind(canvas);
|
||||
canvas.restore();
|
||||
}
|
||||
|
||||
if (VISUALIZE_GRID) {
|
||||
visualizeGrid(canvas);
|
||||
}
|
||||
}
|
||||
|
||||
protected void visualizeGrid(Canvas canvas) {
|
||||
mVisualizeGridRect.set(0, 0, mCellWidth, mCellHeight);
|
||||
mVisualizeGridPaint.setStrokeWidth(4);
|
||||
|
||||
for (int i = 0; i < mCountX; i++) {
|
||||
for (int j = 0; j < mCountY; j++) {
|
||||
canvas.save();
|
||||
|
||||
int transX = i * mCellWidth;
|
||||
int transY = j * mCellHeight;
|
||||
|
||||
canvas.translate(getPaddingLeft() + transX, getPaddingTop() + transY);
|
||||
|
||||
mVisualizeGridPaint.setStyle(Paint.Style.FILL);
|
||||
mVisualizeGridPaint.setColor(Color.argb(80, 255, 100, 100));
|
||||
|
||||
canvas.drawRect(mVisualizeGridRect, mVisualizeGridPaint);
|
||||
|
||||
mVisualizeGridPaint.setStyle(Paint.Style.STROKE);
|
||||
mVisualizeGridPaint.setColor(Color.argb(255, 255, 100, 100));
|
||||
|
||||
canvas.drawRect(mVisualizeGridRect, mVisualizeGridPaint);
|
||||
canvas.restore();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -949,8 +965,8 @@ public class CellLayout extends ViewGroup implements Transposable {
|
||||
return false;
|
||||
}
|
||||
|
||||
void visualizeDropLocation(View v, DragPreviewProvider outlineProvider, int cellX, int cellY,
|
||||
int spanX, int spanY, boolean resize, DropTarget.DragObject dragObject) {
|
||||
void visualizeDropLocation(DraggableView v, DragPreviewProvider outlineProvider, int cellX, int
|
||||
cellY, int spanX, int spanY, boolean resize, DropTarget.DragObject dragObject) {
|
||||
final int oldDragCellX = mDragCell[0];
|
||||
final int oldDragCellY = mDragCell[1];
|
||||
|
||||
@@ -960,9 +976,6 @@ public class CellLayout extends ViewGroup implements Transposable {
|
||||
|
||||
Bitmap dragOutline = outlineProvider.generatedDragOutline;
|
||||
if (cellX != oldDragCellX || cellY != oldDragCellY) {
|
||||
Point dragOffset = dragObject.dragView.getDragVisualizeOffset();
|
||||
Rect dragRegion = dragObject.dragView.getDragRegion();
|
||||
|
||||
mDragCell[0] = cellX;
|
||||
mDragCell[1] = cellY;
|
||||
|
||||
@@ -971,50 +984,27 @@ public class CellLayout extends ViewGroup implements Transposable {
|
||||
mDragOutlineCurrent = (oldIndex + 1) % mDragOutlines.length;
|
||||
Rect r = mDragOutlines[mDragOutlineCurrent];
|
||||
|
||||
cellToRect(cellX, cellY, spanX, spanY, r);
|
||||
int left = r.left;
|
||||
int top = r.top;
|
||||
|
||||
int width = dragOutline.getWidth();
|
||||
int height = dragOutline.getHeight();
|
||||
|
||||
if (resize) {
|
||||
cellToRect(cellX, cellY, spanX, spanY, r);
|
||||
if (v instanceof LauncherAppWidgetHostView) {
|
||||
DeviceProfile profile = mActivity.getWallpaperDeviceProfile();
|
||||
Utilities.shrinkRect(r, profile.appWidgetScale.x, profile.appWidgetScale.y);
|
||||
}
|
||||
} else {
|
||||
// Find the top left corner of the rect the object will occupy
|
||||
final int[] topLeft = mTmpPoint;
|
||||
cellToPoint(cellX, cellY, topLeft);
|
||||
|
||||
int left = topLeft[0];
|
||||
int top = topLeft[1];
|
||||
|
||||
if (v != null && dragOffset == null) {
|
||||
// When drawing the drag outline, it did not account for margin offsets
|
||||
// added by the view's parent.
|
||||
MarginLayoutParams lp = (MarginLayoutParams) v.getLayoutParams();
|
||||
left += lp.leftMargin;
|
||||
top += lp.topMargin;
|
||||
|
||||
// Offsets due to the size difference between the View and the dragOutline.
|
||||
// There is a size difference to account for the outer blur, which may lie
|
||||
// outside the bounds of the view.
|
||||
top += ((mCellHeight * spanY) - dragOutline.getHeight()) / 2;
|
||||
// We center about the x axis
|
||||
left += ((mCellWidth * spanX) - dragOutline.getWidth()) / 2;
|
||||
} else {
|
||||
if (dragOffset != null && dragRegion != null) {
|
||||
// Center the drag region *horizontally* in the cell and apply a drag
|
||||
// outline offset
|
||||
left += dragOffset.x + ((mCellWidth * spanX) - dragRegion.width()) / 2;
|
||||
int cHeight = getShortcutsAndWidgets().getCellContentHeight();
|
||||
int cellPaddingY = (int) Math.max(0, ((mCellHeight - cHeight) / 2f));
|
||||
top += dragOffset.y + cellPaddingY;
|
||||
} else {
|
||||
// Center the drag outline in the cell
|
||||
left += ((mCellWidth * spanX) - dragOutline.getWidth()) / 2;
|
||||
top += ((mCellHeight * spanY) - dragOutline.getHeight()) / 2;
|
||||
}
|
||||
}
|
||||
r.set(left, top, left + dragOutline.getWidth(), top + dragOutline.getHeight());
|
||||
width = r.width();
|
||||
height = r.height();
|
||||
}
|
||||
|
||||
if (v != null && v.getViewType() == DraggableView.DRAGGABLE_ICON) {
|
||||
left += ((mCellWidth * spanX) - dragOutline.getWidth()) / 2;
|
||||
int cHeight = getShortcutsAndWidgets().getCellContentHeight();
|
||||
int cellPaddingY = (int) Math.max(0, ((mCellHeight - cHeight) / 2f));
|
||||
top += cellPaddingY;
|
||||
}
|
||||
|
||||
r.set(left, top, left + width, top + height);
|
||||
|
||||
Utilities.scaleRectAboutCenter(r, mChildScale);
|
||||
mDragOutlineAnims[mDragOutlineCurrent].setTag(dragOutline);
|
||||
mDragOutlineAnims[mDragOutlineCurrent].animateIn();
|
||||
@@ -1900,7 +1890,7 @@ public class CellLayout extends ViewGroup implements Transposable {
|
||||
|
||||
// This method starts or changes the reorder preview animations
|
||||
private void beginOrAdjustReorderPreviewAnimations(ItemConfiguration solution,
|
||||
View dragView, int delay, int mode) {
|
||||
View dragView, int mode) {
|
||||
int childCount = mShortcutsAndWidgets.getChildCount();
|
||||
for (int i = 0; i < childCount; i++) {
|
||||
View child = mShortcutsAndWidgets.getChildAt(i);
|
||||
@@ -1967,6 +1957,8 @@ public class CellLayout extends ViewGroup implements Transposable {
|
||||
|
||||
this.child = child;
|
||||
this.mode = mode;
|
||||
|
||||
// TODO issue!
|
||||
setInitialAnimationValues(false);
|
||||
finalScale = (mChildScale - (CHILD_DIVIDEND / child.getWidth())) * initScale;
|
||||
finalDeltaX = initDeltaX;
|
||||
@@ -2162,6 +2154,8 @@ public class CellLayout extends ViewGroup implements Transposable {
|
||||
*/
|
||||
private void getDirectionVectorForDrop(int dragViewCenterX, int dragViewCenterY, int spanX,
|
||||
int spanY, View dragView, int[] resultDirection) {
|
||||
|
||||
//TODO(adamcohen) b/151776141 use the items visual center for the direction vector
|
||||
int[] targetDestination = new int[2];
|
||||
|
||||
findNearestArea(dragViewCenterX, dragViewCenterY, spanX, spanY, targetDestination);
|
||||
@@ -2272,7 +2266,7 @@ public class CellLayout extends ViewGroup implements Transposable {
|
||||
setItemPlacementDirty(false);
|
||||
} else {
|
||||
beginOrAdjustReorderPreviewAnimations(swapSolution, dragView,
|
||||
REORDER_ANIMATION_DURATION, ReorderPreviewAnimation.MODE_PREVIEW);
|
||||
ReorderPreviewAnimation.MODE_PREVIEW);
|
||||
}
|
||||
mShortcutsAndWidgets.requestLayout();
|
||||
}
|
||||
@@ -2326,7 +2320,7 @@ public class CellLayout extends ViewGroup implements Transposable {
|
||||
|
||||
if (mode == MODE_SHOW_REORDER_HINT) {
|
||||
if (finalSolution != null) {
|
||||
beginOrAdjustReorderPreviewAnimations(finalSolution, dragView, 0,
|
||||
beginOrAdjustReorderPreviewAnimations(finalSolution, dragView,
|
||||
ReorderPreviewAnimation.MODE_HINT);
|
||||
result[0] = finalSolution.cellX;
|
||||
result[1] = finalSolution.cellY;
|
||||
@@ -2366,7 +2360,7 @@ public class CellLayout extends ViewGroup implements Transposable {
|
||||
setItemPlacementDirty(false);
|
||||
} else {
|
||||
beginOrAdjustReorderPreviewAnimations(finalSolution, dragView,
|
||||
REORDER_ANIMATION_DURATION, ReorderPreviewAnimation.MODE_PREVIEW);
|
||||
ReorderPreviewAnimation.MODE_PREVIEW);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
@@ -2787,7 +2781,6 @@ public class CellLayout extends ViewGroup implements Transposable {
|
||||
* Finds solution to accept hotseat migration to cell layout. commits solution if commitConfig
|
||||
*/
|
||||
public boolean makeSpaceForHotseatMigration(boolean commitConfig) {
|
||||
if (FeatureFlags.HOTSEAT_MIGRATE_NEW_PAGE.get()) return false;
|
||||
int[] cellPoint = new int[2];
|
||||
int[] directionVector = new int[]{0, -1};
|
||||
cellToPoint(0, mCountY, cellPoint);
|
||||
@@ -2797,12 +2790,23 @@ public class CellLayout extends ViewGroup implements Transposable {
|
||||
if (commitConfig) {
|
||||
copySolutionToTempState(configuration, null);
|
||||
commitTempPlacement();
|
||||
// undo marking cells occupied since there is actually nothing being placed yet.
|
||||
mOccupied.markCells(0, mCountY - 1, mCountX, 1, false);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* returns a copy of cell layout's grid occupancy
|
||||
*/
|
||||
public GridOccupancy cloneGridOccupancy() {
|
||||
GridOccupancy occupancy = new GridOccupancy(mCountX, mCountY);
|
||||
mOccupied.copyTo(occupancy);
|
||||
return occupancy;
|
||||
}
|
||||
|
||||
public boolean isRegionVacant(int x, int y, int spanX, int spanY) {
|
||||
return mOccupied.isRegionVacant(x, y, spanX, spanY);
|
||||
}
|
||||
|
||||
@@ -23,6 +23,7 @@ import com.android.launcher3.accessibility.DragViewStateAnnouncer;
|
||||
import com.android.launcher3.config.FeatureFlags;
|
||||
import com.android.launcher3.dragndrop.DragOptions;
|
||||
import com.android.launcher3.dragndrop.DragView;
|
||||
import com.android.launcher3.dragndrop.DraggableView;
|
||||
import com.android.launcher3.folder.FolderNameProvider;
|
||||
|
||||
/**
|
||||
@@ -59,9 +60,6 @@ public interface DropTarget {
|
||||
/** Where the drag originated */
|
||||
public DragSource dragSource = null;
|
||||
|
||||
/** The object is part of an accessible drag operation */
|
||||
public boolean accessibleDrag;
|
||||
|
||||
/** Indicates that the drag operation was cancelled */
|
||||
public boolean cancelled = false;
|
||||
|
||||
@@ -72,6 +70,10 @@ public interface DropTarget {
|
||||
|
||||
public FolderNameProvider folderNameProvider;
|
||||
|
||||
/** The source view (ie. icon, widget etc.) that is being dragged and which the
|
||||
* DragView represents. May be an actual View class or a virtual stand-in */
|
||||
public DraggableView originalView = null;
|
||||
|
||||
public DragObject(Context context) {
|
||||
if (FeatureFlags.FOLDER_NAME_SUGGEST.get()) {
|
||||
folderNameProvider = FolderNameProvider.newInstance(context);
|
||||
|
||||
@@ -79,7 +79,7 @@ public class FolderInfo extends ItemInfo {
|
||||
* Add an app or shortcut for a specified rank.
|
||||
*/
|
||||
public void add(WorkspaceItemInfo item, int rank, boolean animate) {
|
||||
rank = Utilities.boundToRange(rank, 0, contents.size() + 1);
|
||||
rank = Utilities.boundToRange(rank, 0, contents.size());
|
||||
contents.add(rank, item);
|
||||
for (int i = 0; i < mListeners.size(); i++) {
|
||||
mListeners.get(i).onAdd(item, rank);
|
||||
|
||||
@@ -162,9 +162,7 @@ public class InvariantDeviceProfile {
|
||||
"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;
|
||||
String gridName = getCurrentGridName(context);
|
||||
initGrid(context, gridName);
|
||||
mConfigMonitor = new ConfigMonitor(context,
|
||||
APPLY_CONFIG_AT_RUNTIME.get() ? this::onConfigChanged : this::killProcess);
|
||||
@@ -188,6 +186,12 @@ public class InvariantDeviceProfile {
|
||||
initGrid(context, null, new Info(display));
|
||||
}
|
||||
|
||||
public static String getCurrentGridName(Context context) {
|
||||
return Utilities.getPrefs(context).getBoolean(GRID_OPTIONS_PREFERENCE_KEY, false)
|
||||
? Utilities.getPrefs(context).getString(KEY_IDP_GRID_NAME, null)
|
||||
: null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieve system defined or RRO overriden icon shape.
|
||||
*/
|
||||
|
||||
@@ -124,7 +124,7 @@ import com.android.launcher3.testing.TestLogging;
|
||||
import com.android.launcher3.testing.TestProtocol;
|
||||
import com.android.launcher3.touch.AllAppsSwipeController;
|
||||
import com.android.launcher3.touch.ItemClickHandler;
|
||||
import com.android.launcher3.uioverrides.BackgroundBlurController;
|
||||
import com.android.launcher3.uioverrides.DepthController;
|
||||
import com.android.launcher3.uioverrides.plugins.PluginManagerWrapper;
|
||||
import com.android.launcher3.userevent.nano.LauncherLogProto.Action;
|
||||
import com.android.launcher3.userevent.nano.LauncherLogProto.ContainerType;
|
||||
@@ -326,19 +326,21 @@ public class Launcher extends BaseDraggingActivity implements LauncherExterns,
|
||||
private boolean mDeferOverlayCallbacks;
|
||||
private final Runnable mDeferredOverlayCallbacks = this::checkIfOverlayStillDeferred;
|
||||
|
||||
private BackgroundBlurController mBackgroundBlurController =
|
||||
new BackgroundBlurController(this);
|
||||
private DepthController mDepthController =
|
||||
new DepthController(this);
|
||||
|
||||
private final ViewTreeObserver.OnDrawListener mOnDrawListener =
|
||||
new ViewTreeObserver.OnDrawListener() {
|
||||
@Override
|
||||
public void onDraw() {
|
||||
getBackgroundBlurController().setSurfaceToLauncher(mDragLayer);
|
||||
getDepthController().setSurfaceToLauncher(mDragLayer);
|
||||
mDragLayer.post(() -> mDragLayer.getViewTreeObserver().removeOnDrawListener(
|
||||
this));
|
||||
}
|
||||
};
|
||||
|
||||
private long mLastTouchUpTime = -1;
|
||||
|
||||
@Override
|
||||
protected void onCreate(Bundle savedInstanceState) {
|
||||
Object traceToken = TraceHelper.INSTANCE.beginSection(ON_CREATE_EVT,
|
||||
@@ -954,7 +956,7 @@ public class Launcher extends BaseDraggingActivity implements LauncherExterns,
|
||||
|
||||
NotificationListener.removeNotificationsChangedListener();
|
||||
getStateManager().moveToRestState();
|
||||
getBackgroundBlurController().setSurfaceToLauncher(null);
|
||||
getDepthController().setSurfaceToLauncher(null);
|
||||
|
||||
// Workaround for b/78520668, explicitly trim memory once UI is hidden
|
||||
onTrimMemory(TRIM_MEMORY_UI_HIDDEN);
|
||||
@@ -1115,7 +1117,7 @@ public class Launcher extends BaseDraggingActivity implements LauncherExterns,
|
||||
|
||||
super.onPause();
|
||||
mDragController.cancelDrag();
|
||||
mDragController.resetLastGestureUpTime();
|
||||
mLastTouchUpTime = -1;
|
||||
mDropTargetBar.animateToVisibility(false);
|
||||
|
||||
if (!mDeferOverlayCallbacks) {
|
||||
@@ -1838,6 +1840,9 @@ public class Launcher extends BaseDraggingActivity implements LauncherExterns,
|
||||
|
||||
@Override
|
||||
public boolean dispatchTouchEvent(MotionEvent ev) {
|
||||
if (ev.getAction() == MotionEvent.ACTION_UP) {
|
||||
mLastTouchUpTime = System.currentTimeMillis();
|
||||
}
|
||||
TestLogging.recordMotionEvent(TestProtocol.SEQUENCE_MAIN, "Touch event", ev);
|
||||
return super.dispatchTouchEvent(ev);
|
||||
}
|
||||
@@ -2465,8 +2470,12 @@ public class Launcher extends BaseDraggingActivity implements LauncherExterns,
|
||||
}
|
||||
|
||||
private boolean canRunNewAppsAnimation() {
|
||||
long diff = System.currentTimeMillis() - mDragController.getLastGestureUpTime();
|
||||
return diff > (NEW_APPS_ANIMATION_INACTIVE_TIMEOUT_SECONDS * 1000);
|
||||
if (mDragController.isDragging()) {
|
||||
return false;
|
||||
} else {
|
||||
return (System.currentTimeMillis() - mLastTouchUpTime)
|
||||
> (NEW_APPS_ANIMATION_INACTIVE_TIMEOUT_SECONDS * 1000);
|
||||
}
|
||||
}
|
||||
|
||||
private ValueAnimator createNewAppBounceAnimation(View v, int i) {
|
||||
@@ -2708,7 +2717,7 @@ public class Launcher extends BaseDraggingActivity implements LauncherExterns,
|
||||
|
||||
protected StateHandler[] createStateHandlers() {
|
||||
return new StateHandler[] { getAllAppsController(), getWorkspace(),
|
||||
getBackgroundBlurController() };
|
||||
getDepthController() };
|
||||
}
|
||||
|
||||
public TouchController[] createTouchControllers() {
|
||||
@@ -2745,8 +2754,8 @@ public class Launcher extends BaseDraggingActivity implements LauncherExterns,
|
||||
return Stream.of(APP_INFO, WIDGETS, INSTALL);
|
||||
}
|
||||
|
||||
public BackgroundBlurController getBackgroundBlurController() {
|
||||
return mBackgroundBlurController;
|
||||
public DepthController getDepthController() {
|
||||
return mDepthController;
|
||||
}
|
||||
|
||||
public static Launcher getLauncher(Context context) {
|
||||
|
||||
@@ -271,11 +271,13 @@ public abstract class LauncherState {
|
||||
}
|
||||
|
||||
/**
|
||||
* The amount of blur to apply to the background of either the app or Launcher surface in this
|
||||
* state.
|
||||
* The amount of blur and wallpaper zoom to apply to the background of either the app
|
||||
* or Launcher surface in this state. Should be a number between 0 and 1, inclusive.
|
||||
*
|
||||
* 0 means completely zoomed in, without blurs. 1 is zoomed out, with blurs.
|
||||
*/
|
||||
public int getBackgroundBlurRadius(Context context) {
|
||||
return 0;
|
||||
public float getDepth(Context context) {
|
||||
return 0f;
|
||||
}
|
||||
|
||||
public String getDescription(Launcher launcher) {
|
||||
|
||||
@@ -145,38 +145,46 @@ public class ShortcutAndWidgetContainer extends ViewGroup {
|
||||
final View child = getChildAt(i);
|
||||
if (child.getVisibility() != GONE) {
|
||||
CellLayout.LayoutParams lp = (CellLayout.LayoutParams) child.getLayoutParams();
|
||||
|
||||
if (child instanceof LauncherAppWidgetHostView) {
|
||||
LauncherAppWidgetHostView lahv = (LauncherAppWidgetHostView) child;
|
||||
|
||||
// Scale and center the widget to fit within its cells.
|
||||
DeviceProfile profile = mActivity.getDeviceProfile();
|
||||
float scaleX = profile.appWidgetScale.x;
|
||||
float scaleY = profile.appWidgetScale.y;
|
||||
|
||||
lahv.setScaleToFit(Math.min(scaleX, scaleY));
|
||||
lahv.setTranslationForCentering(-(lp.width - (lp.width * scaleX)) / 2.0f,
|
||||
-(lp.height - (lp.height * scaleY)) / 2.0f);
|
||||
}
|
||||
|
||||
int childLeft = lp.x;
|
||||
int childTop = lp.y;
|
||||
child.layout(childLeft, childTop, childLeft + lp.width, childTop + lp.height);
|
||||
|
||||
if (lp.dropped) {
|
||||
lp.dropped = false;
|
||||
|
||||
final int[] cellXY = mTmpCellXY;
|
||||
getLocationOnScreen(cellXY);
|
||||
mWallpaperManager.sendWallpaperCommand(getWindowToken(),
|
||||
WallpaperManager.COMMAND_DROP,
|
||||
cellXY[0] + childLeft + lp.width / 2,
|
||||
cellXY[1] + childTop + lp.height / 2, 0, null);
|
||||
}
|
||||
layoutChild(child);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Core logic to layout a child for this ViewGroup.
|
||||
*/
|
||||
public void layoutChild(View child) {
|
||||
CellLayout.LayoutParams lp = (CellLayout.LayoutParams) child.getLayoutParams();
|
||||
if (child instanceof LauncherAppWidgetHostView) {
|
||||
LauncherAppWidgetHostView lahv = (LauncherAppWidgetHostView) child;
|
||||
|
||||
// Scale and center the widget to fit within its cells.
|
||||
DeviceProfile profile = mActivity.getDeviceProfile();
|
||||
float scaleX = profile.appWidgetScale.x;
|
||||
float scaleY = profile.appWidgetScale.y;
|
||||
|
||||
lahv.setScaleToFit(Math.min(scaleX, scaleY));
|
||||
lahv.setTranslationForCentering(-(lp.width - (lp.width * scaleX)) / 2.0f,
|
||||
-(lp.height - (lp.height * scaleY)) / 2.0f);
|
||||
}
|
||||
|
||||
int childLeft = lp.x;
|
||||
int childTop = lp.y;
|
||||
child.layout(childLeft, childTop, childLeft + lp.width, childTop + lp.height);
|
||||
|
||||
if (lp.dropped) {
|
||||
lp.dropped = false;
|
||||
|
||||
final int[] cellXY = mTmpCellXY;
|
||||
getLocationOnScreen(cellXY);
|
||||
mWallpaperManager.sendWallpaperCommand(getWindowToken(),
|
||||
WallpaperManager.COMMAND_DROP,
|
||||
cellXY[0] + childLeft + lp.width / 2,
|
||||
cellXY[1] + childTop + lp.height / 2, 0, null);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public boolean onInterceptTouchEvent(MotionEvent ev) {
|
||||
if (ev.getAction() == ACTION_DOWN && getAlpha() == 0) {
|
||||
|
||||
@@ -70,6 +70,7 @@ import com.android.launcher3.dragndrop.DragController;
|
||||
import com.android.launcher3.dragndrop.DragLayer;
|
||||
import com.android.launcher3.dragndrop.DragOptions;
|
||||
import com.android.launcher3.dragndrop.DragView;
|
||||
import com.android.launcher3.dragndrop.DraggableView;
|
||||
import com.android.launcher3.dragndrop.SpringLoadedDragController;
|
||||
import com.android.launcher3.folder.Folder;
|
||||
import com.android.launcher3.folder.FolderIcon;
|
||||
@@ -81,7 +82,6 @@ import com.android.launcher3.icons.BitmapRenderer;
|
||||
import com.android.launcher3.logging.UserEventDispatcher;
|
||||
import com.android.launcher3.pageindicators.WorkspacePageIndicator;
|
||||
import com.android.launcher3.popup.PopupContainerWithArrow;
|
||||
import com.android.launcher3.shortcuts.ShortcutDragPreviewProvider;
|
||||
import com.android.launcher3.states.StateAnimationConfig;
|
||||
import com.android.launcher3.touch.WorkspaceTouchListener;
|
||||
import com.android.launcher3.userevent.nano.LauncherLogProto.Action;
|
||||
@@ -192,10 +192,7 @@ public class Workspace extends PagedView<WorkspacePageIndicator>
|
||||
final WallpaperOffsetInterpolator mWallpaperOffset;
|
||||
private boolean mUnlockWallpaperFromDefaultPageOnLayout;
|
||||
|
||||
// Variables relating to the creation of user folders by hovering shortcuts over shortcuts
|
||||
private static final int FOLDER_CREATION_TIMEOUT = 0;
|
||||
public static final int REORDER_TIMEOUT = 650;
|
||||
private final Alarm mFolderCreationAlarm = new Alarm();
|
||||
private final Alarm mReorderAlarm = new Alarm();
|
||||
private PreviewBackground mFolderCreateBg;
|
||||
private FolderIcon mDragOverFolderIcon = null;
|
||||
@@ -567,11 +564,6 @@ public class Workspace extends PagedView<WorkspacePageIndicator>
|
||||
addView(newScreen, insertIndex);
|
||||
mStateTransitionAnimation.applyChildState(
|
||||
mLauncher.getStateManager().getState(), newScreen, insertIndex);
|
||||
|
||||
if (mLauncher.getAccessibilityDelegate().isInAccessibleDrag()) {
|
||||
newScreen.enableAccessibleDrag(true, CellLayout.WORKSPACE_ACCESSIBILITY_DRAG);
|
||||
}
|
||||
|
||||
return newScreen;
|
||||
}
|
||||
|
||||
@@ -818,11 +810,6 @@ public class Workspace extends PagedView<WorkspacePageIndicator>
|
||||
if (indexOfChild(cl) < currentPage) {
|
||||
pageShift++;
|
||||
}
|
||||
|
||||
if (isInAccessibleDrag) {
|
||||
cl.enableAccessibleDrag(false, CellLayout.WORKSPACE_ACCESSIBILITY_DRAG);
|
||||
}
|
||||
|
||||
removeView(cl);
|
||||
} else {
|
||||
// if this is the last screen, convert it to the empty screen
|
||||
@@ -1444,14 +1431,14 @@ public class Workspace extends PagedView<WorkspacePageIndicator>
|
||||
child.setVisibility(INVISIBLE);
|
||||
|
||||
if (options.isAccessibleDrag) {
|
||||
mDragController.addDragListener(new AccessibleDragListenerAdapter(
|
||||
this, CellLayout.WORKSPACE_ACCESSIBILITY_DRAG) {
|
||||
@Override
|
||||
protected void enableAccessibleDrag(boolean enable) {
|
||||
super.enableAccessibleDrag(enable);
|
||||
setEnableForLayout(mLauncher.getHotseat(),enable);
|
||||
}
|
||||
});
|
||||
mDragController.addDragListener(
|
||||
new AccessibleDragListenerAdapter(this, WorkspaceAccessibilityHelper::new) {
|
||||
@Override
|
||||
protected void enableAccessibleDrag(boolean enable) {
|
||||
super.enableAccessibleDrag(enable);
|
||||
setEnableForLayout(mLauncher.getHotseat(), enable);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
beginDragShared(child, this, options);
|
||||
@@ -1465,12 +1452,17 @@ public class Workspace extends PagedView<WorkspacePageIndicator>
|
||||
+ "View: " + child + " tag: " + child.getTag();
|
||||
throw new IllegalStateException(msg);
|
||||
}
|
||||
beginDragShared(child, source, (ItemInfo) dragObject,
|
||||
beginDragShared(child, null, source, (ItemInfo) dragObject,
|
||||
new DragPreviewProvider(child), options);
|
||||
}
|
||||
|
||||
public DragView beginDragShared(View child, DragSource source, ItemInfo dragObject,
|
||||
DragPreviewProvider previewProvider, DragOptions dragOptions) {
|
||||
/**
|
||||
* Core functionality for beginning a drag operation for an item that will be dropped within
|
||||
* the workspace
|
||||
*/
|
||||
public DragView beginDragShared(View child, DraggableView draggableView, DragSource source,
|
||||
ItemInfo dragObject, DragPreviewProvider previewProvider, DragOptions dragOptions) {
|
||||
|
||||
float iconScale = 1f;
|
||||
if (child instanceof BubbleTextView) {
|
||||
Drawable icon = ((BubbleTextView) child).getIcon();
|
||||
@@ -1479,41 +1471,36 @@ public class Workspace extends PagedView<WorkspacePageIndicator>
|
||||
}
|
||||
}
|
||||
|
||||
// Clear the pressed state if necessary
|
||||
child.clearFocus();
|
||||
child.setPressed(false);
|
||||
if (child instanceof BubbleTextView) {
|
||||
BubbleTextView icon = (BubbleTextView) child;
|
||||
icon.clearPressedBackground();
|
||||
}
|
||||
|
||||
mOutlineProvider = previewProvider;
|
||||
|
||||
// The drag bitmap follows the touch point around on the screen
|
||||
final Bitmap b = previewProvider.createDragBitmap();
|
||||
int halfPadding = previewProvider.previewPadding / 2;
|
||||
|
||||
float scale = previewProvider.getScaleAndPosition(b, mTempXY);
|
||||
int dragLayerX = mTempXY[0];
|
||||
int dragLayerY = mTempXY[1];
|
||||
|
||||
DeviceProfile grid = mLauncher.getDeviceProfile();
|
||||
Point dragVisualizeOffset = null;
|
||||
Rect dragRect = null;
|
||||
if (child instanceof BubbleTextView) {
|
||||
dragRect = new Rect();
|
||||
BubbleTextView.getIconBounds(child, dragRect, grid.iconSizePx);
|
||||
Rect dragRect = new Rect();
|
||||
|
||||
if (draggableView == null && child instanceof DraggableView) {
|
||||
draggableView = (DraggableView) child;
|
||||
}
|
||||
|
||||
if (draggableView != null) {
|
||||
draggableView.getVisualDragBounds(dragRect);
|
||||
dragLayerY += dragRect.top;
|
||||
// Note: The dragRect is used to calculate drag layer offsets, but the
|
||||
// dragVisualizeOffset in addition to the dragRect (the size) to position the outline.
|
||||
dragVisualizeOffset = new Point(- halfPadding, halfPadding);
|
||||
} else if (child instanceof FolderIcon) {
|
||||
int previewSize = grid.folderIconSizePx;
|
||||
dragVisualizeOffset = new Point(- halfPadding, halfPadding - child.getPaddingTop());
|
||||
dragRect = new Rect(0, child.getPaddingTop(), child.getWidth(), previewSize);
|
||||
} else if (previewProvider instanceof ShortcutDragPreviewProvider) {
|
||||
dragVisualizeOffset = new Point(- halfPadding, halfPadding);
|
||||
}
|
||||
|
||||
// Clear the pressed state if necessary
|
||||
if (child instanceof BubbleTextView) {
|
||||
BubbleTextView icon = (BubbleTextView) child;
|
||||
icon.clearPressedBackground();
|
||||
}
|
||||
|
||||
if (child.getParent() instanceof ShortcutAndWidgetContainer) {
|
||||
mDragSourceInternal = (ShortcutAndWidgetContainer) child.getParent();
|
||||
@@ -1524,13 +1511,13 @@ public class Workspace extends PagedView<WorkspacePageIndicator>
|
||||
.showForIcon((BubbleTextView) child);
|
||||
if (popupContainer != null) {
|
||||
dragOptions.preDragCondition = popupContainer.createPreDragCondition();
|
||||
|
||||
mLauncher.getUserEventDispatcher().resetElapsedContainerMillis("dragging started");
|
||||
}
|
||||
}
|
||||
|
||||
DragView dv = mDragController.startDrag(b, dragLayerX, dragLayerY, source,
|
||||
dragObject, dragVisualizeOffset, dragRect, scale * iconScale, scale, dragOptions);
|
||||
DragView dv = mDragController.startDrag(b, draggableView, dragLayerX, dragLayerY, source,
|
||||
dragObject, dragVisualizeOffset, dragRect, scale * iconScale,
|
||||
scale, dragOptions);
|
||||
dv.setIntrinsicIconScaleFactor(dragOptions.intrinsicIconScaleFactor);
|
||||
return dv;
|
||||
}
|
||||
@@ -1883,12 +1870,10 @@ public class Workspace extends PagedView<WorkspacePageIndicator>
|
||||
final LauncherAppWidgetHostView hostView = (LauncherAppWidgetHostView) cell;
|
||||
AppWidgetProviderInfo pInfo = hostView.getAppWidgetInfo();
|
||||
if (pInfo != null && pInfo.resizeMode != AppWidgetProviderInfo.RESIZE_NONE
|
||||
&& !d.accessibleDrag) {
|
||||
onCompleteRunnable = new Runnable() {
|
||||
public void run() {
|
||||
if (!isPageInTransition()) {
|
||||
AppWidgetResizeFrame.showForWidget(hostView, cellLayout);
|
||||
}
|
||||
&& !options.isAccessibleDrag) {
|
||||
onCompleteRunnable = () -> {
|
||||
if (!isPageInTransition()) {
|
||||
AppWidgetResizeFrame.showForWidget(hostView, cellLayout);
|
||||
}
|
||||
};
|
||||
}
|
||||
@@ -2088,8 +2073,6 @@ public class Workspace extends PagedView<WorkspacePageIndicator>
|
||||
if (mFolderCreateBg != null) {
|
||||
mFolderCreateBg.animateToRest();
|
||||
}
|
||||
mFolderCreationAlarm.setOnAlarmListener(null);
|
||||
mFolderCreationAlarm.cancelAlarm();
|
||||
}
|
||||
|
||||
private void cleanupAddToFolder() {
|
||||
@@ -2196,14 +2179,14 @@ public class Workspace extends PagedView<WorkspacePageIndicator>
|
||||
float targetCellDistance = mDragTargetLayout.getDistanceFromCell(
|
||||
mDragViewVisualCenter[0], mDragViewVisualCenter[1], mTargetCell);
|
||||
|
||||
manageFolderFeedback(mDragTargetLayout, mTargetCell, targetCellDistance, d);
|
||||
manageFolderFeedback(targetCellDistance, d);
|
||||
|
||||
boolean nearestDropOccupied = mDragTargetLayout.isNearestDropLocationOccupied((int)
|
||||
mDragViewVisualCenter[0], (int) mDragViewVisualCenter[1], item.spanX,
|
||||
item.spanY, child, mTargetCell);
|
||||
|
||||
if (!nearestDropOccupied) {
|
||||
mDragTargetLayout.visualizeDropLocation(child, mOutlineProvider,
|
||||
mDragTargetLayout.visualizeDropLocation(d.originalView, mOutlineProvider,
|
||||
mTargetCell[0], mTargetCell[1], item.spanX, item.spanY, false, d);
|
||||
} else if ((mDragMode == DRAG_MODE_NONE || mDragMode == DRAG_MODE_REORDER)
|
||||
&& !mReorderAlarm.alarmPending() && (mLastReorderX != reorderX ||
|
||||
@@ -2294,10 +2277,10 @@ public class Workspace extends PagedView<WorkspacePageIndicator>
|
||||
return null;
|
||||
}
|
||||
|
||||
private void manageFolderFeedback(CellLayout targetLayout,
|
||||
int[] targetCell, float distance, DragObject dragObject) {
|
||||
private void manageFolderFeedback(float distance, DragObject dragObject) {
|
||||
if (distance > mMaxDistanceForFolderCreation) {
|
||||
if (mDragMode != DRAG_MODE_NONE) {
|
||||
if ((mDragMode == DRAG_MODE_ADD_TO_FOLDER
|
||||
|| mDragMode == DRAG_MODE_CREATE_FOLDER)) {
|
||||
setDragMode(DRAG_MODE_NONE);
|
||||
}
|
||||
return;
|
||||
@@ -2306,18 +2289,18 @@ public class Workspace extends PagedView<WorkspacePageIndicator>
|
||||
final View dragOverView = mDragTargetLayout.getChildAt(mTargetCell[0], mTargetCell[1]);
|
||||
ItemInfo info = dragObject.dragInfo;
|
||||
boolean userFolderPending = willCreateUserFolder(info, dragOverView, false);
|
||||
if (mDragMode == DRAG_MODE_NONE && userFolderPending &&
|
||||
!mFolderCreationAlarm.alarmPending()) {
|
||||
if (mDragMode == DRAG_MODE_NONE && userFolderPending) {
|
||||
|
||||
FolderCreationAlarmListener listener = new
|
||||
FolderCreationAlarmListener(targetLayout, targetCell[0], targetCell[1]);
|
||||
mFolderCreateBg = new PreviewBackground();
|
||||
mFolderCreateBg.setup(mLauncher, mLauncher, null,
|
||||
dragOverView.getMeasuredWidth(), dragOverView.getPaddingTop());
|
||||
|
||||
if (!dragObject.accessibleDrag) {
|
||||
mFolderCreationAlarm.setOnAlarmListener(listener);
|
||||
mFolderCreationAlarm.setAlarm(FOLDER_CREATION_TIMEOUT);
|
||||
} else {
|
||||
listener.onAlarm(mFolderCreationAlarm);
|
||||
}
|
||||
// The full preview background should appear behind the icon
|
||||
mFolderCreateBg.isClipping = false;
|
||||
|
||||
mFolderCreateBg.animateToAccept(mDragTargetLayout, mTargetCell[0], mTargetCell[1]);
|
||||
mDragTargetLayout.clearDragOutlines();
|
||||
setDragMode(DRAG_MODE_CREATE_FOLDER);
|
||||
|
||||
if (dragObject.stateAnnouncer != null) {
|
||||
dragObject.stateAnnouncer.announce(WorkspaceAccessibilityHelper
|
||||
@@ -2330,8 +2313,8 @@ public class Workspace extends PagedView<WorkspacePageIndicator>
|
||||
if (willAddToFolder && mDragMode == DRAG_MODE_NONE) {
|
||||
mDragOverFolderIcon = ((FolderIcon) dragOverView);
|
||||
mDragOverFolderIcon.onDragEnter(info);
|
||||
if (targetLayout != null) {
|
||||
targetLayout.clearDragOutlines();
|
||||
if (mDragTargetLayout != null) {
|
||||
mDragTargetLayout.clearDragOutlines();
|
||||
}
|
||||
setDragMode(DRAG_MODE_ADD_TO_FOLDER);
|
||||
|
||||
@@ -2350,33 +2333,6 @@ public class Workspace extends PagedView<WorkspacePageIndicator>
|
||||
}
|
||||
}
|
||||
|
||||
class FolderCreationAlarmListener implements OnAlarmListener {
|
||||
final CellLayout layout;
|
||||
final int cellX;
|
||||
final int cellY;
|
||||
|
||||
final PreviewBackground bg = new PreviewBackground();
|
||||
|
||||
public FolderCreationAlarmListener(CellLayout layout, int cellX, int cellY) {
|
||||
this.layout = layout;
|
||||
this.cellX = cellX;
|
||||
this.cellY = cellY;
|
||||
|
||||
BubbleTextView cell = (BubbleTextView) layout.getChildAt(cellX, cellY);
|
||||
bg.setup(mLauncher, mLauncher, null, cell.getMeasuredWidth(), cell.getPaddingTop());
|
||||
|
||||
// The full preview background should appear behind the icon
|
||||
bg.isClipping = false;
|
||||
}
|
||||
|
||||
public void onAlarm(Alarm alarm) {
|
||||
mFolderCreateBg = bg;
|
||||
mFolderCreateBg.animateToAccept(layout, cellX, cellY);
|
||||
layout.clearDragOutlines();
|
||||
setDragMode(DRAG_MODE_CREATE_FOLDER);
|
||||
}
|
||||
}
|
||||
|
||||
class ReorderAlarmListener implements OnAlarmListener {
|
||||
final float[] dragViewCenter;
|
||||
final int minSpanX, minSpanY, spanX, spanY;
|
||||
@@ -2413,7 +2369,7 @@ public class Workspace extends PagedView<WorkspacePageIndicator>
|
||||
}
|
||||
|
||||
boolean resize = resultSpan[0] != spanX || resultSpan[1] != spanY;
|
||||
mDragTargetLayout.visualizeDropLocation(child, mOutlineProvider,
|
||||
mDragTargetLayout.visualizeDropLocation(dragObject.originalView, mOutlineProvider,
|
||||
mTargetCell[0], mTargetCell[1], resultSpan[0], resultSpan[1], resize, dragObject);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -16,7 +16,9 @@
|
||||
|
||||
package com.android.launcher3.accessibility;
|
||||
|
||||
import android.view.View;
|
||||
import android.view.ViewGroup;
|
||||
import android.view.ViewGroup.OnHierarchyChangeListener;
|
||||
|
||||
import com.android.launcher3.CellLayout;
|
||||
import com.android.launcher3.DropTarget.DragObject;
|
||||
@@ -24,36 +26,55 @@ import com.android.launcher3.Launcher;
|
||||
import com.android.launcher3.dragndrop.DragController.DragListener;
|
||||
import com.android.launcher3.dragndrop.DragOptions;
|
||||
|
||||
import java.util.function.Function;
|
||||
|
||||
/**
|
||||
* Utility listener to enable/disable accessibility drag flags for a ViewGroup
|
||||
* containing CellLayouts
|
||||
*/
|
||||
public class AccessibleDragListenerAdapter implements DragListener {
|
||||
public class AccessibleDragListenerAdapter implements DragListener, OnHierarchyChangeListener {
|
||||
|
||||
private final ViewGroup mViewGroup;
|
||||
private final int mDragType;
|
||||
private final Function<CellLayout, DragAndDropAccessibilityDelegate> mDelegateFactory;
|
||||
|
||||
/**
|
||||
* @param parent
|
||||
* @param dragType either {@link CellLayout#WORKSPACE_ACCESSIBILITY_DRAG} or
|
||||
* {@link CellLayout#FOLDER_ACCESSIBILITY_DRAG}
|
||||
* @param parent the viewgroup containing all the children
|
||||
* @param delegateFactory function to create no delegates
|
||||
*/
|
||||
public AccessibleDragListenerAdapter(ViewGroup parent, int dragType) {
|
||||
public AccessibleDragListenerAdapter(ViewGroup parent,
|
||||
Function<CellLayout, DragAndDropAccessibilityDelegate> delegateFactory) {
|
||||
mViewGroup = parent;
|
||||
mDragType = dragType;
|
||||
mDelegateFactory = delegateFactory;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onDragStart(DragObject dragObject, DragOptions options) {
|
||||
mViewGroup.setOnHierarchyChangeListener(this);
|
||||
enableAccessibleDrag(true);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onDragEnd() {
|
||||
mViewGroup.setOnHierarchyChangeListener(null);
|
||||
enableAccessibleDrag(false);
|
||||
Launcher.getLauncher(mViewGroup.getContext()).getDragController().removeDragListener(this);
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public void onChildViewAdded(View parent, View child) {
|
||||
if (parent == mViewGroup) {
|
||||
setEnableForLayout((CellLayout) child, true);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onChildViewRemoved(View parent, View child) {
|
||||
if (parent == mViewGroup) {
|
||||
setEnableForLayout((CellLayout) child, false);
|
||||
}
|
||||
}
|
||||
|
||||
protected void enableAccessibleDrag(boolean enable) {
|
||||
for (int i = 0; i < mViewGroup.getChildCount(); i++) {
|
||||
setEnableForLayout((CellLayout) mViewGroup.getChildAt(i), enable);
|
||||
@@ -61,6 +82,6 @@ public class AccessibleDragListenerAdapter implements DragListener {
|
||||
}
|
||||
|
||||
protected final void setEnableForLayout(CellLayout layout, boolean enable) {
|
||||
layout.enableAccessibleDrag(enable, mDragType);
|
||||
layout.setDragAndDropAccessibilityDelegate(enable ? mDelegateFactory.apply(layout) : null);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -19,24 +19,26 @@ package com.android.launcher3.accessibility;
|
||||
import android.content.Context;
|
||||
import android.graphics.Rect;
|
||||
import android.os.Bundle;
|
||||
import android.view.MotionEvent;
|
||||
import android.view.View;
|
||||
import android.view.View.OnClickListener;
|
||||
import android.view.View.OnHoverListener;
|
||||
import android.view.accessibility.AccessibilityEvent;
|
||||
|
||||
import androidx.core.view.accessibility.AccessibilityNodeInfoCompat;
|
||||
import androidx.customview.widget.ExploreByTouchHelper;
|
||||
|
||||
import com.android.launcher3.CellLayout;
|
||||
import com.android.launcher3.Launcher;
|
||||
import com.android.launcher3.R;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
import androidx.core.view.accessibility.AccessibilityNodeInfoCompat;
|
||||
import androidx.customview.widget.ExploreByTouchHelper;
|
||||
|
||||
/**
|
||||
* Helper class to make drag-and-drop in a {@link CellLayout} accessible.
|
||||
*/
|
||||
public abstract class DragAndDropAccessibilityDelegate extends ExploreByTouchHelper
|
||||
implements OnClickListener {
|
||||
implements OnClickListener, OnHoverListener {
|
||||
protected static final int INVALID_POSITION = -1;
|
||||
|
||||
private static final int[] sTempArray = new int[2];
|
||||
@@ -123,6 +125,11 @@ public abstract class DragAndDropAccessibilityDelegate extends ExploreByTouchHel
|
||||
node.setFocusable(true);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean onHover(View view, MotionEvent motionEvent) {
|
||||
return dispatchHoverEvent(motionEvent);
|
||||
}
|
||||
|
||||
protected abstract String getLocationDescriptionForIconDrop(int id);
|
||||
|
||||
protected abstract String getConfirmationForIconDrop(int id);
|
||||
|
||||
@@ -7,6 +7,7 @@ import static com.android.launcher3.LauncherState.NORMAL;
|
||||
import android.app.AlertDialog;
|
||||
import android.appwidget.AppWidgetProviderInfo;
|
||||
import android.content.DialogInterface;
|
||||
import android.graphics.Point;
|
||||
import android.graphics.Rect;
|
||||
import android.os.Bundle;
|
||||
import android.os.Handler;
|
||||
@@ -400,11 +401,11 @@ public class LauncherAccessibilityDelegate extends AccessibilityDelegate impleme
|
||||
|
||||
Rect pos = new Rect();
|
||||
mLauncher.getDragLayer().getDescendantRectRelativeToSelf(item, pos);
|
||||
mLauncher.getDragController().prepareAccessibleDrag(pos.centerX(), pos.centerY());
|
||||
mLauncher.getDragController().addDragListener(this);
|
||||
|
||||
DragOptions options = new DragOptions();
|
||||
options.isAccessibleDrag = true;
|
||||
options.simulatedDndStartPoint = new Point(pos.centerX(), pos.centerY());
|
||||
ItemLongClickListener.beginDrag(item, mLauncher, info, options);
|
||||
}
|
||||
|
||||
|
||||
@@ -38,6 +38,7 @@ import androidx.annotation.NonNull;
|
||||
import androidx.annotation.Nullable;
|
||||
import androidx.annotation.StringRes;
|
||||
import androidx.dynamicanimation.animation.DynamicAnimation;
|
||||
import androidx.recyclerview.widget.DefaultItemAnimator;
|
||||
import androidx.recyclerview.widget.LinearLayoutManager;
|
||||
import androidx.recyclerview.widget.RecyclerView;
|
||||
|
||||
@@ -62,7 +63,6 @@ import com.android.launcher3.util.MultiValueAlpha.AlphaProperty;
|
||||
import com.android.launcher3.util.Themes;
|
||||
import com.android.launcher3.views.RecyclerViewFastScroller;
|
||||
import com.android.launcher3.views.SpringRelativeLayout;
|
||||
import com.android.launcher3.views.WorkFooterContainer;
|
||||
|
||||
import java.util.ArrayList;
|
||||
|
||||
@@ -91,7 +91,7 @@ public class AllAppsContainerView extends SpringRelativeLayout implements DragSo
|
||||
private AllAppsPagedView mViewPager;
|
||||
|
||||
private FloatingHeaderView mHeader;
|
||||
private WorkFooterContainer mWorkFooterContainer;
|
||||
private WorkModeSwitch mWorkModeSwitch;
|
||||
|
||||
|
||||
private SpannableStringBuilder mSearchQueryBuilder = null;
|
||||
@@ -156,8 +156,8 @@ public class AllAppsContainerView extends SpringRelativeLayout implements DragSo
|
||||
return mMultiValueAlpha.getProperty(index);
|
||||
}
|
||||
|
||||
public WorkFooterContainer getWorkFooterContainer() {
|
||||
return mWorkFooterContainer;
|
||||
public WorkModeSwitch getWorkModeSwitch() {
|
||||
return mWorkModeSwitch;
|
||||
}
|
||||
|
||||
|
||||
@@ -195,7 +195,7 @@ public class AllAppsContainerView extends SpringRelativeLayout implements DragSo
|
||||
}
|
||||
|
||||
private void resetWorkProfile() {
|
||||
mWorkFooterContainer.refresh();
|
||||
mWorkModeSwitch.refresh();
|
||||
mAH[AdapterHolder.WORK].setupOverlay();
|
||||
mAH[AdapterHolder.WORK].applyPadding();
|
||||
}
|
||||
@@ -410,9 +410,9 @@ public class AllAppsContainerView extends SpringRelativeLayout implements DragSo
|
||||
} else {
|
||||
mAH[AdapterHolder.MAIN].setup(findViewById(R.id.apps_list_view), null);
|
||||
mAH[AdapterHolder.WORK].recyclerView = null;
|
||||
if (mWorkFooterContainer != null) {
|
||||
((ViewGroup) mWorkFooterContainer.getParent()).removeView(mWorkFooterContainer);
|
||||
mWorkFooterContainer = null;
|
||||
if (mWorkModeSwitch != null) {
|
||||
((ViewGroup) mWorkModeSwitch.getParent()).removeView(mWorkModeSwitch);
|
||||
mWorkModeSwitch = null;
|
||||
}
|
||||
}
|
||||
setupHeader();
|
||||
@@ -422,14 +422,11 @@ public class AllAppsContainerView extends SpringRelativeLayout implements DragSo
|
||||
}
|
||||
|
||||
private void setupWorkToggle() {
|
||||
mWorkFooterContainer = (WorkFooterContainer) mLauncher.getLayoutInflater().inflate(
|
||||
R.layout.work_tab_footer, findViewById(R.id.work_toggle_container));
|
||||
mWorkFooterContainer.setLayoutParams(
|
||||
new ViewGroup.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT,
|
||||
ViewGroup.LayoutParams.WRAP_CONTENT));
|
||||
this.addView(mWorkFooterContainer);
|
||||
mWorkFooterContainer.setInsets(mInsets);
|
||||
mWorkFooterContainer.post(() -> mAH[AdapterHolder.WORK].applyPadding());
|
||||
mWorkModeSwitch = (WorkModeSwitch) mLauncher.getLayoutInflater().inflate(
|
||||
R.layout.work_mode_switch, this, false);
|
||||
this.addView(mWorkModeSwitch);
|
||||
mWorkModeSwitch.setInsets(mInsets);
|
||||
mWorkModeSwitch.post(() -> mAH[AdapterHolder.WORK].applyPadding());
|
||||
}
|
||||
|
||||
private void replaceRVContainer(boolean showTabs) {
|
||||
@@ -469,8 +466,8 @@ public class AllAppsContainerView extends SpringRelativeLayout implements DragSo
|
||||
findViewById(R.id.tab_work)
|
||||
.setOnClickListener((View view) -> mViewPager.snapToPage(AdapterHolder.WORK));
|
||||
}
|
||||
if (mWorkFooterContainer != null) {
|
||||
mWorkFooterContainer.setWorkTabVisible(pos == AdapterHolder.WORK);
|
||||
if (mWorkModeSwitch != null) {
|
||||
mWorkModeSwitch.setWorkTabVisible(pos == AdapterHolder.WORK);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -614,6 +611,7 @@ public class AllAppsContainerView extends SpringRelativeLayout implements DragSo
|
||||
final Rect padding = new Rect();
|
||||
AllAppsRecyclerView recyclerView;
|
||||
boolean verticalFadingEdge;
|
||||
private View mOverlay;
|
||||
|
||||
boolean mWorkDisabled;
|
||||
|
||||
@@ -648,13 +646,22 @@ public class AllAppsContainerView extends SpringRelativeLayout implements DragSo
|
||||
if (!mIsWork || recyclerView == null) return;
|
||||
boolean workDisabled = UserCache.INSTANCE.get(mLauncher).isAnyProfileQuietModeEnabled();
|
||||
if (mWorkDisabled == workDisabled) return;
|
||||
recyclerView.setContentDescription(
|
||||
workDisabled ? mLauncher.getString(R.string.work_apps_paused_title) : null);
|
||||
View overlayView = getOverlayView();
|
||||
recyclerView.setItemAnimator(new DefaultItemAnimator());
|
||||
if (workDisabled) {
|
||||
overlayView.setAlpha(0);
|
||||
appsList.updateItemFilter((info, cn) -> false);
|
||||
recyclerView.addAutoSizedOverlay(
|
||||
mLauncher.getLayoutInflater().inflate(R.layout.work_apps_paused, null));
|
||||
recyclerView.addAutoSizedOverlay(overlayView);
|
||||
overlayView.animate().alpha(1).withEndAction(
|
||||
() -> recyclerView.setItemAnimator(null)).start();
|
||||
} else if (mInfoMatcher != null) {
|
||||
appsList.updateItemFilter(mInfoMatcher);
|
||||
recyclerView.clearAutoSizedOverlays();
|
||||
overlayView.animate().alpha(0).withEndAction(() -> {
|
||||
recyclerView.setItemAnimator(null);
|
||||
recyclerView.clearAutoSizedOverlays();
|
||||
}).start();
|
||||
}
|
||||
mWorkDisabled = workDisabled;
|
||||
}
|
||||
@@ -662,8 +669,7 @@ public class AllAppsContainerView extends SpringRelativeLayout implements DragSo
|
||||
void applyPadding() {
|
||||
if (recyclerView != null) {
|
||||
int bottomOffset =
|
||||
mWorkFooterContainer != null && mIsWork ? mWorkFooterContainer.getHeight()
|
||||
: 0;
|
||||
mWorkModeSwitch != null && mIsWork ? mWorkModeSwitch.getHeight() : 0;
|
||||
recyclerView.setPadding(padding.left, padding.top, padding.right,
|
||||
padding.bottom + bottomOffset);
|
||||
}
|
||||
@@ -674,5 +680,12 @@ public class AllAppsContainerView extends SpringRelativeLayout implements DragSo
|
||||
mAH[AdapterHolder.MAIN].recyclerView.setVerticalFadingEdgeEnabled(!mUsingTabs
|
||||
&& verticalFadingEdge);
|
||||
}
|
||||
|
||||
private View getOverlayView() {
|
||||
if (mOverlay == null) {
|
||||
mOverlay = mLauncher.getLayoutInflater().inflate(R.layout.work_apps_paused, null);
|
||||
}
|
||||
return mOverlay;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -18,6 +18,7 @@ package com.android.launcher3.allapps;
|
||||
import static com.android.launcher3.AppInfo.COMPONENT_KEY_COMPARATOR;
|
||||
import static com.android.launcher3.AppInfo.EMPTY_ARRAY;
|
||||
|
||||
import android.util.Log;
|
||||
import android.view.View;
|
||||
import android.view.ViewGroup;
|
||||
|
||||
@@ -55,6 +56,8 @@ public class AllAppsStore {
|
||||
private int mDeferUpdatesFlags = 0;
|
||||
private boolean mUpdatePending = false;
|
||||
|
||||
private boolean mListenerUpdateInProgress = false;
|
||||
|
||||
public AppInfo[] getApps() {
|
||||
return mApps;
|
||||
}
|
||||
@@ -99,10 +102,12 @@ public class AllAppsStore {
|
||||
mUpdatePending = true;
|
||||
return;
|
||||
}
|
||||
mListenerUpdateInProgress = true;
|
||||
int count = mUpdateListeners.size();
|
||||
for (int i = 0; i < count; i++) {
|
||||
mUpdateListeners.get(i).onAppsUpdated();
|
||||
}
|
||||
mListenerUpdateInProgress = false;
|
||||
}
|
||||
|
||||
public void addUpdateListener(OnUpdateListener listener) {
|
||||
@@ -110,6 +115,9 @@ public class AllAppsStore {
|
||||
}
|
||||
|
||||
public void removeUpdateListener(OnUpdateListener listener) {
|
||||
if (mListenerUpdateInProgress) {
|
||||
Log.e("AllAppsStore", "Trying to remove listener during update", new Exception());
|
||||
}
|
||||
mUpdateListeners.remove(listener);
|
||||
}
|
||||
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (C) 2018 The Android Open Source Project
|
||||
* 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.
|
||||
@@ -15,7 +15,13 @@
|
||||
*/
|
||||
package com.android.launcher3.allapps;
|
||||
|
||||
import static com.android.launcher3.util.PackageManagerHelper.hasShortcutsPermission;
|
||||
|
||||
import android.animation.ObjectAnimator;
|
||||
import android.animation.PropertyValuesHolder;
|
||||
import android.content.Context;
|
||||
import android.content.pm.PackageManager;
|
||||
import android.graphics.Rect;
|
||||
import android.os.AsyncTask;
|
||||
import android.os.Process;
|
||||
import android.os.UserHandle;
|
||||
@@ -24,28 +30,45 @@ import android.util.AttributeSet;
|
||||
import android.view.MotionEvent;
|
||||
import android.widget.Switch;
|
||||
|
||||
import com.android.launcher3.Insettable;
|
||||
import com.android.launcher3.Launcher;
|
||||
import com.android.launcher3.R;
|
||||
import com.android.launcher3.Utilities;
|
||||
import com.android.launcher3.pm.UserCache;
|
||||
|
||||
import java.lang.ref.WeakReference;
|
||||
|
||||
public class WorkModeSwitch extends Switch {
|
||||
/**
|
||||
* Work profile toggle switch shown at the bottom of AllApps work tab
|
||||
*/
|
||||
public class WorkModeSwitch extends Switch implements Insettable {
|
||||
|
||||
private Rect mInsets = new Rect();
|
||||
protected ObjectAnimator mOpenCloseAnimator;
|
||||
|
||||
|
||||
public WorkModeSwitch(Context context) {
|
||||
super(context);
|
||||
init();
|
||||
}
|
||||
|
||||
public WorkModeSwitch(Context context, AttributeSet attrs) {
|
||||
super(context, attrs);
|
||||
init();
|
||||
}
|
||||
|
||||
public WorkModeSwitch(Context context, AttributeSet attrs, int defStyleAttr) {
|
||||
super(context, attrs, defStyleAttr);
|
||||
init();
|
||||
}
|
||||
|
||||
private void init() {
|
||||
mOpenCloseAnimator = ObjectAnimator.ofPropertyValuesHolder(this);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setChecked(boolean checked) {
|
||||
// No-op, do not change the checked state until broadcast is received.
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -55,14 +78,23 @@ public class WorkModeSwitch extends Switch {
|
||||
|
||||
private void setCheckedInternal(boolean checked) {
|
||||
super.setChecked(checked);
|
||||
setCompoundDrawablesWithIntrinsicBounds(
|
||||
checked ? R.drawable.ic_corp : R.drawable.ic_corp_off, 0, 0, 0);
|
||||
}
|
||||
|
||||
public void refresh() {
|
||||
if (!shouldShowWorkSwitch()) return;
|
||||
UserCache userManager = UserCache.INSTANCE.get(getContext());
|
||||
setCheckedInternal(!userManager.isAnyProfileQuietModeEnabled());
|
||||
setEnabled(true);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onLayout(boolean changed, int l, int t, int r, int b) {
|
||||
super.onLayout(changed, l, t, r, b);
|
||||
this.setVisibility(shouldShowWorkSwitch() ? VISIBLE : GONE);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean onTouchEvent(MotionEvent ev) {
|
||||
return ev.getActionMasked() == MotionEvent.ACTION_MOVE || super.onTouchEvent(ev);
|
||||
@@ -72,6 +104,24 @@ public class WorkModeSwitch extends Switch {
|
||||
new SetQuietModeEnabledAsyncTask(enabled, new WeakReference<>(this)).execute();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setInsets(Rect insets) {
|
||||
int bottomInset = insets.bottom - mInsets.bottom;
|
||||
mInsets.set(insets);
|
||||
setPadding(getPaddingLeft(), getPaddingTop(), getPaddingRight(),
|
||||
getPaddingBottom() + bottomInset);
|
||||
}
|
||||
|
||||
/**
|
||||
* Animates in/out work profile toggle panel based on the tab user is on
|
||||
*/
|
||||
public void setWorkTabVisible(boolean workTabVisible) {
|
||||
if (!shouldShowWorkSwitch()) return;
|
||||
|
||||
mOpenCloseAnimator.setValues(PropertyValuesHolder.ofFloat(ALPHA, workTabVisible ? 1 : 0));
|
||||
mOpenCloseAnimator.start();
|
||||
}
|
||||
|
||||
private static final class SetQuietModeEnabledAsyncTask
|
||||
extends AsyncTask<Void, Void, Boolean> {
|
||||
|
||||
@@ -122,4 +172,11 @@ public class WorkModeSwitch extends Switch {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private boolean shouldShowWorkSwitch() {
|
||||
Launcher launcher = Launcher.getLauncher(getContext());
|
||||
return Utilities.ATLEAST_P && (hasShortcutsPermission(launcher)
|
||||
|| launcher.checkSelfPermission("android.permission.MODIFY_QUIET_MODE")
|
||||
== PackageManager.PERMISSION_GRANTED);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -77,6 +77,9 @@ public class AnimatorPlaybackController implements ValueAnimator.AnimatorUpdateL
|
||||
}
|
||||
};
|
||||
|
||||
// Progress factor after which an animation is considered almost completed.
|
||||
private static final float ANIMATION_COMPLETE_THRESHOLD = 0.95f;
|
||||
|
||||
private final ValueAnimator mAnimationPlayer;
|
||||
private final long mDuration;
|
||||
|
||||
@@ -209,6 +212,16 @@ public class AnimatorPlaybackController implements ValueAnimator.AnimatorUpdateL
|
||||
mAnimationPlayer.start();
|
||||
}
|
||||
|
||||
/**
|
||||
* Tries to finish the running animation if it is close to completion.
|
||||
*/
|
||||
public void forceFinishIfCloseToEnd() {
|
||||
if (mAnimationPlayer.isRunning()
|
||||
&& mAnimationPlayer.getAnimatedFraction() > ANIMATION_COMPLETE_THRESHOLD) {
|
||||
mAnimationPlayer.end();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Pauses the currently playing animation.
|
||||
*/
|
||||
|
||||
@@ -62,10 +62,6 @@ public class PendingAnimation implements PropertySetter {
|
||||
/**
|
||||
* Utility method to sent an interpolator on an animation and add it to the list
|
||||
*/
|
||||
public void add(Animator anim, TimeInterpolator interpolator) {
|
||||
add(anim, interpolator, SpringProperty.DEFAULT);
|
||||
}
|
||||
|
||||
public void add(Animator anim, TimeInterpolator interpolator, SpringProperty springProperty) {
|
||||
anim.setInterpolator(interpolator);
|
||||
add(anim, springProperty);
|
||||
|
||||
@@ -83,6 +83,9 @@ public final class FeatureFlags {
|
||||
public static final BooleanFlag UNSTABLE_SPRINGS = getDebugFlag(
|
||||
"UNSTABLE_SPRINGS", false, "Enable unstable springs for quickstep animations");
|
||||
|
||||
public static final BooleanFlag KEYGUARD_ANIMATION = getDebugFlag(
|
||||
"KEYGUARD_ANIMATION", false, "Enable animation for keyguard going away on wallpaper");
|
||||
|
||||
public static final BooleanFlag ADAPTIVE_ICON_WINDOW_ANIM = getDebugFlag(
|
||||
"ADAPTIVE_ICON_WINDOW_ANIM", true, "Use adaptive icons for window animations.");
|
||||
|
||||
@@ -117,12 +120,11 @@ public final class FeatureFlags {
|
||||
"ASSISTANT_GIVES_LAUNCHER_FOCUS", false,
|
||||
"Allow Launcher to handle nav bar gestures while Assistant is running over it");
|
||||
|
||||
public static final BooleanFlag ENABLE_HYBRID_HOTSEAT = getDebugFlag(
|
||||
public static final BooleanFlag ENABLE_HYBRID_HOTSEAT = new DeviceFlag(
|
||||
"ENABLE_HYBRID_HOTSEAT", false, "Fill gaps in hotseat with predicted apps");
|
||||
|
||||
public static final BooleanFlag HOTSEAT_MIGRATE_NEW_PAGE = getDebugFlag(
|
||||
"HOTSEAT_MIGRATE_NEW_PAGE", false,
|
||||
"Migrates hotseat to a new workspace page instead of same page");
|
||||
public static final BooleanFlag HOTSEAT_MIGRATE_TO_FOLDER = new DeviceFlag(
|
||||
"HOTSEAT_MIGRATE_TO_FOLDER", false, "Should move hotseat items into a folder");
|
||||
|
||||
public static final BooleanFlag ENABLE_DEEP_SHORTCUT_ICON_CACHE = getDebugFlag(
|
||||
"ENABLE_DEEP_SHORTCUT_ICON_CACHE", true, "R/W deep shortcut in IconCache");
|
||||
@@ -134,7 +136,7 @@ public final class FeatureFlags {
|
||||
"ENABLE_LAUNCHER_PREVIEW_IN_GRID_PICKER", true, "Show launcher preview in grid picker");
|
||||
|
||||
public static final BooleanFlag USE_SURFACE_VIEW_FOR_GRID_PREVIEW = getDebugFlag(
|
||||
"USE_SURFACE_VIEW_FOR_GRID_PREVIEW", false, "Use surface view for grid preview");
|
||||
"USE_SURFACE_VIEW_FOR_GRID_PREVIEW", true, "Use surface view for grid preview");
|
||||
|
||||
public static final BooleanFlag ENABLE_OVERVIEW_ACTIONS = getDebugFlag(
|
||||
"ENABLE_OVERVIEW_ACTIONS", true, "Show app actions instead of the shelf in Overview."
|
||||
|
||||
@@ -26,7 +26,6 @@ import android.graphics.Point;
|
||||
import android.graphics.Rect;
|
||||
import android.os.Handler;
|
||||
import android.os.Looper;
|
||||
import android.os.SystemClock;
|
||||
import android.util.Log;
|
||||
import android.view.DragEvent;
|
||||
import android.view.View;
|
||||
@@ -63,7 +62,6 @@ public abstract class BaseItemDragListener implements View.OnDragListener, DragS
|
||||
|
||||
protected Launcher mLauncher;
|
||||
private DragController mDragController;
|
||||
private long mDragStartTime;
|
||||
|
||||
public BaseItemDragListener(Rect previewRect, int previewBitmapWidth, int previewViewWidth) {
|
||||
mPreviewRect = previewRect;
|
||||
@@ -102,7 +100,7 @@ public abstract class BaseItemDragListener implements View.OnDragListener, DragS
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return mDragController.onDragEvent(mDragStartTime, event);
|
||||
return mDragController.onDragEvent(event);
|
||||
}
|
||||
|
||||
protected boolean onDragStart(DragEvent event) {
|
||||
@@ -118,7 +116,7 @@ public abstract class BaseItemDragListener implements View.OnDragListener, DragS
|
||||
|
||||
Point downPos = new Point((int) event.getX(), (int) event.getY());
|
||||
DragOptions options = new DragOptions();
|
||||
options.systemDndStartPoint = downPos;
|
||||
options.simulatedDndStartPoint = downPos;
|
||||
options.preDragCondition = preDragCondition;
|
||||
|
||||
// We use drag event position as the screenPos for the preview image. Since mPreviewRect
|
||||
@@ -128,7 +126,6 @@ public abstract class BaseItemDragListener implements View.OnDragListener, DragS
|
||||
// to source window.
|
||||
createDragHelper().startDrag(new Rect(mPreviewRect),
|
||||
mPreviewBitmapWidth, mPreviewViewWidth, downPos, this, options);
|
||||
mDragStartTime = SystemClock.uptimeMillis();
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
@@ -43,7 +43,6 @@ import com.android.launcher3.R;
|
||||
import com.android.launcher3.WorkspaceItemInfo;
|
||||
import com.android.launcher3.accessibility.DragViewStateAnnouncer;
|
||||
import com.android.launcher3.util.ItemInfoMatcher;
|
||||
import com.android.launcher3.util.Thunk;
|
||||
import com.android.launcher3.util.TouchController;
|
||||
import com.android.launcher3.util.UiThreadHelper;
|
||||
|
||||
@@ -61,8 +60,8 @@ public class DragController implements DragDriver.EventListener, TouchController
|
||||
*/
|
||||
private static final int DEEP_PRESS_DISTANCE_FACTOR = 3;
|
||||
|
||||
@Thunk Launcher mLauncher;
|
||||
private FlingToDeleteHelper mFlingToDeleteHelper;
|
||||
private final Launcher mLauncher;
|
||||
private final FlingToDeleteHelper mFlingToDeleteHelper;
|
||||
|
||||
// temporaries to avoid gc thrash
|
||||
private Rect mRectTemp = new Rect();
|
||||
@@ -77,11 +76,12 @@ public class DragController implements DragDriver.EventListener, TouchController
|
||||
/** Options controlling the drag behavior. */
|
||||
private DragOptions mOptions;
|
||||
|
||||
/** X coordinate of the down event. */
|
||||
private int mMotionDownX;
|
||||
/** Coordinate for motion down event */
|
||||
private final Point mMotionDown = new Point();
|
||||
/** Coordinate for last touch event **/
|
||||
private final Point mLastTouch = new Point();
|
||||
|
||||
/** Y coordinate of the down event. */
|
||||
private int mMotionDownY;
|
||||
private final Point mTmpPoint = new Point();
|
||||
|
||||
private DropTarget.DragObject mDragObject;
|
||||
|
||||
@@ -96,12 +96,9 @@ public class DragController implements DragDriver.EventListener, TouchController
|
||||
|
||||
private DropTarget mLastDropTarget;
|
||||
|
||||
private final int[] mLastTouch = new int[2];
|
||||
private long mLastTouchUpTime = -1;
|
||||
private int mLastTouchClassification;
|
||||
private int mDistanceSinceScroll = 0;
|
||||
|
||||
private int mTmpPoint[] = new int[2];
|
||||
private Rect mDragLayerRect = new Rect();
|
||||
|
||||
private boolean mIsInPreDrag;
|
||||
@@ -140,6 +137,8 @@ public class DragController implements DragDriver.EventListener, TouchController
|
||||
*
|
||||
* @param b The bitmap to display as the drag image. It will be re-scaled to the
|
||||
* enlarged size.
|
||||
* @param originalView The source view (ie. icon, widget etc.) that is being dragged
|
||||
* and which the DragView represents
|
||||
* @param dragLayerX The x position in the DragLayer of the left-top of the bitmap.
|
||||
* @param dragLayerY The y position in the DragLayer of the left-top of the bitmap.
|
||||
* @param source An object representing where the drag originated
|
||||
@@ -147,7 +146,7 @@ public class DragController implements DragDriver.EventListener, TouchController
|
||||
* @param dragRegion Coordinates within the bitmap b for the position of item being dragged.
|
||||
* Makes dragging feel more precise, e.g. you can clip out a transparent border
|
||||
*/
|
||||
public DragView startDrag(Bitmap b, int dragLayerX, int dragLayerY,
|
||||
public DragView startDrag(Bitmap b, DraggableView originalView, int dragLayerX, int dragLayerY,
|
||||
DragSource source, ItemInfo dragInfo, Point dragOffset, Rect dragRegion,
|
||||
float initialDragViewScale, float dragViewScaleOnDrop, DragOptions options) {
|
||||
if (PROFILE_DRAWING_DURING_DRAG) {
|
||||
@@ -159,13 +158,13 @@ public class DragController implements DragDriver.EventListener, TouchController
|
||||
AbstractFloatingView.closeOpenViews(mLauncher, false, TYPE_DISCOVERY_BOUNCE);
|
||||
|
||||
mOptions = options;
|
||||
if (mOptions.systemDndStartPoint != null) {
|
||||
mLastTouch[0] = mMotionDownX = mOptions.systemDndStartPoint.x;
|
||||
mLastTouch[1] = mMotionDownY = mOptions.systemDndStartPoint.y;
|
||||
if (mOptions.simulatedDndStartPoint != null) {
|
||||
mLastTouch.x = mMotionDown.x = mOptions.simulatedDndStartPoint.x;
|
||||
mLastTouch.y = mMotionDown.y = mOptions.simulatedDndStartPoint.y;
|
||||
}
|
||||
|
||||
final int registrationX = mMotionDownX - dragLayerX;
|
||||
final int registrationY = mMotionDownY - dragLayerY;
|
||||
final int registrationX = mMotionDown.x - dragLayerX;
|
||||
final int registrationY = mMotionDown.y - dragLayerY;
|
||||
|
||||
final int dragRegionLeft = dragRegion == null ? 0 : dragRegion.left;
|
||||
final int dragRegionTop = dragRegion == null ? 0 : dragRegion.top;
|
||||
@@ -173,6 +172,7 @@ public class DragController implements DragDriver.EventListener, TouchController
|
||||
mLastDropTarget = null;
|
||||
|
||||
mDragObject = new DropTarget.DragObject(mLauncher.getApplicationContext());
|
||||
mDragObject.originalView = originalView;
|
||||
|
||||
mIsInPreDrag = mOptions.preDragCondition != null
|
||||
&& !mOptions.preDragCondition.shouldStartDrag(0);
|
||||
@@ -184,17 +184,13 @@ public class DragController implements DragDriver.EventListener, TouchController
|
||||
registrationY, initialDragViewScale, dragViewScaleOnDrop, scaleDps);
|
||||
dragView.setItemInfo(dragInfo);
|
||||
mDragObject.dragComplete = false;
|
||||
if (mOptions.isAccessibleDrag) {
|
||||
// For an accessible drag, we assume the view is being dragged from the center.
|
||||
mDragObject.xOffset = b.getWidth() / 2;
|
||||
mDragObject.yOffset = b.getHeight() / 2;
|
||||
mDragObject.accessibleDrag = true;
|
||||
} else {
|
||||
mDragObject.xOffset = mMotionDownX - (dragLayerX + dragRegionLeft);
|
||||
mDragObject.yOffset = mMotionDownY - (dragLayerY + dragRegionTop);
|
||||
mDragObject.stateAnnouncer = DragViewStateAnnouncer.createFor(dragView);
|
||||
|
||||
mDragDriver = DragDriver.create(mLauncher, this, mDragObject, mOptions);
|
||||
mDragObject.xOffset = mMotionDown.x - (dragLayerX + dragRegionLeft);
|
||||
mDragObject.yOffset = mMotionDown.y - (dragLayerY + dragRegionTop);
|
||||
|
||||
mDragDriver = DragDriver.create(this, mOptions, mFlingToDeleteHelper::recordMotionEvent);
|
||||
if (!mOptions.isAccessibleDrag) {
|
||||
mDragObject.stateAnnouncer = DragViewStateAnnouncer.createFor(dragView);
|
||||
}
|
||||
|
||||
mDragObject.dragSource = source;
|
||||
@@ -210,7 +206,7 @@ public class DragController implements DragDriver.EventListener, TouchController
|
||||
}
|
||||
|
||||
mLauncher.getDragLayer().performHapticFeedback(HapticFeedbackConstants.LONG_PRESS);
|
||||
dragView.show(mLastTouch[0], mLastTouch[1]);
|
||||
dragView.show(mLastTouch.x, mLastTouch.y);
|
||||
mDistanceSinceScroll = 0;
|
||||
|
||||
if (!mIsInPreDrag) {
|
||||
@@ -219,7 +215,7 @@ public class DragController implements DragDriver.EventListener, TouchController
|
||||
mOptions.preDragCondition.onPreDragStart(mDragObject);
|
||||
}
|
||||
|
||||
handleMoveEvent(mLastTouch[0], mLastTouch[1]);
|
||||
handleMoveEvent(mLastTouch.x, mLastTouch.y);
|
||||
mLauncher.getUserEventDispatcher().resetActionDurationMillis();
|
||||
return dragView;
|
||||
}
|
||||
@@ -336,7 +332,7 @@ public class DragController implements DragDriver.EventListener, TouchController
|
||||
}
|
||||
}
|
||||
};
|
||||
mDragObject.dragView.animateTo(mMotionDownX, mMotionDownY, onCompleteRunnable, duration);
|
||||
mDragObject.dragView.animateTo(mMotionDown.x, mMotionDown.y, onCompleteRunnable, duration);
|
||||
}
|
||||
|
||||
private void callOnDragEnd() {
|
||||
@@ -365,30 +361,17 @@ public class DragController implements DragDriver.EventListener, TouchController
|
||||
/**
|
||||
* Clamps the position to the drag layer bounds.
|
||||
*/
|
||||
private int[] getClampedDragLayerPos(float x, float y) {
|
||||
private Point getClampedDragLayerPos(float x, float y) {
|
||||
mLauncher.getDragLayer().getLocalVisibleRect(mDragLayerRect);
|
||||
mTmpPoint[0] = (int) Math.max(mDragLayerRect.left, Math.min(x, mDragLayerRect.right - 1));
|
||||
mTmpPoint[1] = (int) Math.max(mDragLayerRect.top, Math.min(y, mDragLayerRect.bottom - 1));
|
||||
mTmpPoint.x = (int) Math.max(mDragLayerRect.left, Math.min(x, mDragLayerRect.right - 1));
|
||||
mTmpPoint.y = (int) Math.max(mDragLayerRect.top, Math.min(y, mDragLayerRect.bottom - 1));
|
||||
return mTmpPoint;
|
||||
}
|
||||
|
||||
public long getLastGestureUpTime() {
|
||||
if (mDragDriver != null) {
|
||||
return System.currentTimeMillis();
|
||||
} else {
|
||||
return mLastTouchUpTime;
|
||||
}
|
||||
}
|
||||
|
||||
public void resetLastGestureUpTime() {
|
||||
mLastTouchUpTime = -1;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onDriverDragMove(float x, float y) {
|
||||
final int[] dragLayerPos = getClampedDragLayerPos(x, y);
|
||||
|
||||
handleMoveEvent(dragLayerPos[0], dragLayerPos[1]);
|
||||
Point dragLayerPos = getClampedDragLayerPos(x, y);
|
||||
handleMoveEvent(dragLayerPos.x, dragLayerPos.y);
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -422,53 +405,38 @@ public class DragController implements DragDriver.EventListener, TouchController
|
||||
/**
|
||||
* Call this from a drag source view.
|
||||
*/
|
||||
@Override
|
||||
public boolean onControllerInterceptTouchEvent(MotionEvent ev) {
|
||||
if (mOptions != null && mOptions.isAccessibleDrag) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// Update the velocity tracker
|
||||
mFlingToDeleteHelper.recordMotionEvent(ev);
|
||||
Point dragLayerPos = getClampedDragLayerPos(ev.getX(), ev.getY());
|
||||
mLastTouch.set(dragLayerPos.x, dragLayerPos.y);
|
||||
if (ev.getAction() == MotionEvent.ACTION_DOWN) {
|
||||
// Remember location of down touch
|
||||
mMotionDown.set(dragLayerPos.x, dragLayerPos.y);
|
||||
}
|
||||
|
||||
final int action = ev.getAction();
|
||||
final int[] dragLayerPos = getClampedDragLayerPos(ev.getX(), ev.getY());
|
||||
final int dragLayerX = dragLayerPos[0];
|
||||
final int dragLayerY = dragLayerPos[1];
|
||||
mLastTouch[0] = dragLayerX;
|
||||
mLastTouch[1] = dragLayerY;
|
||||
if (ATLEAST_Q) {
|
||||
mLastTouchClassification = ev.getClassification();
|
||||
}
|
||||
|
||||
switch (action) {
|
||||
case MotionEvent.ACTION_DOWN:
|
||||
// Remember location of down touch
|
||||
mMotionDownX = dragLayerX;
|
||||
mMotionDownY = dragLayerY;
|
||||
break;
|
||||
case MotionEvent.ACTION_UP:
|
||||
mLastTouchUpTime = System.currentTimeMillis();
|
||||
break;
|
||||
}
|
||||
|
||||
return mDragDriver != null && mDragDriver.onInterceptTouchEvent(ev);
|
||||
}
|
||||
|
||||
/**
|
||||
* Call this from a drag source view.
|
||||
*/
|
||||
public boolean onDragEvent(long dragStartTime, DragEvent event) {
|
||||
mFlingToDeleteHelper.recordDragEvent(dragStartTime, event);
|
||||
return mDragDriver != null && mDragDriver.onDragEvent(event);
|
||||
@Override
|
||||
public boolean onControllerTouchEvent(MotionEvent ev) {
|
||||
return mDragDriver != null && mDragDriver.onTouchEvent(ev);
|
||||
}
|
||||
|
||||
/**
|
||||
* Call this from a drag view.
|
||||
* Call this from a drag source view.
|
||||
*/
|
||||
public void onDragViewAnimationEnd() {
|
||||
if (mDragDriver != null) {
|
||||
mDragDriver.onDragViewAnimationEnd();
|
||||
}
|
||||
public boolean onDragEvent(DragEvent event) {
|
||||
return mDragDriver != null && mDragDriver.onDragEvent(event);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -493,9 +461,8 @@ public class DragController implements DragDriver.EventListener, TouchController
|
||||
checkTouchMove(dropTarget);
|
||||
|
||||
// Check if we are hovering over the scroll areas
|
||||
mDistanceSinceScroll += Math.hypot(mLastTouch[0] - x, mLastTouch[1] - y);
|
||||
mLastTouch[0] = x;
|
||||
mLastTouch[1] = y;
|
||||
mDistanceSinceScroll += Math.hypot(mLastTouch.x - x, mLastTouch.y - y);
|
||||
mLastTouch.set(x, y);
|
||||
|
||||
int distanceDragged = mDistanceSinceScroll;
|
||||
if (ATLEAST_Q && mLastTouchClassification == MotionEvent.CLASSIFICATION_DEEP_PRESS) {
|
||||
@@ -513,7 +480,7 @@ public class DragController implements DragDriver.EventListener, TouchController
|
||||
|
||||
public void forceTouchMove() {
|
||||
int[] dummyCoordinates = mCoordinatesTemp;
|
||||
DropTarget dropTarget = findDropTarget(mLastTouch[0], mLastTouch[1], dummyCoordinates);
|
||||
DropTarget dropTarget = findDropTarget(mLastTouch.x, mLastTouch.y, dummyCoordinates);
|
||||
mDragObject.x = dummyCoordinates[0];
|
||||
mDragObject.y = dummyCoordinates[1];
|
||||
checkTouchMove(dropTarget);
|
||||
@@ -536,44 +503,6 @@ public class DragController implements DragDriver.EventListener, TouchController
|
||||
mLastDropTarget = dropTarget;
|
||||
}
|
||||
|
||||
/**
|
||||
* Call this from a drag source view.
|
||||
*/
|
||||
public boolean onControllerTouchEvent(MotionEvent ev) {
|
||||
if (mDragDriver == null || mOptions == null || mOptions.isAccessibleDrag) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// Update the velocity tracker
|
||||
mFlingToDeleteHelper.recordMotionEvent(ev);
|
||||
|
||||
final int action = ev.getAction();
|
||||
final int[] dragLayerPos = getClampedDragLayerPos(ev.getX(), ev.getY());
|
||||
final int dragLayerX = dragLayerPos[0];
|
||||
final int dragLayerY = dragLayerPos[1];
|
||||
|
||||
switch (action) {
|
||||
case MotionEvent.ACTION_DOWN:
|
||||
// Remember where the motion event started
|
||||
mMotionDownX = dragLayerX;
|
||||
mMotionDownY = dragLayerY;
|
||||
break;
|
||||
}
|
||||
|
||||
return mDragDriver.onTouchEvent(ev);
|
||||
}
|
||||
|
||||
/**
|
||||
* Since accessible drag and drop won't cause the same sequence of touch events, we manually
|
||||
* inject the appropriate state which would have been otherwise initiated via touch events.
|
||||
*/
|
||||
public void prepareAccessibleDrag(int x, int y) {
|
||||
mMotionDownX = x;
|
||||
mMotionDownY = y;
|
||||
mLastTouch[0] = x;
|
||||
mLastTouch[1] = y;
|
||||
}
|
||||
|
||||
/**
|
||||
* As above, since accessible drag and drop won't cause the same sequence of touch events,
|
||||
* we manually ensure appropriate drag and drop events get emulated for accessible drag.
|
||||
|
||||
@@ -16,19 +16,19 @@
|
||||
|
||||
package com.android.launcher3.dragndrop;
|
||||
|
||||
import android.content.Context;
|
||||
import android.util.Log;
|
||||
import android.os.SystemClock;
|
||||
import android.view.DragEvent;
|
||||
import android.view.MotionEvent;
|
||||
|
||||
import com.android.launcher3.DropTarget.DragObject;
|
||||
import com.android.launcher3.testing.TestProtocol;
|
||||
import java.util.function.Consumer;
|
||||
|
||||
/**
|
||||
* Base class for driving a drag/drop operation.
|
||||
*/
|
||||
public abstract class DragDriver {
|
||||
|
||||
protected final EventListener mEventListener;
|
||||
protected final Consumer<MotionEvent> mSecondaryEventConsumer;
|
||||
|
||||
public interface EventListener {
|
||||
void onDriverDragMove(float x, float y);
|
||||
@@ -37,131 +37,175 @@ public abstract class DragDriver {
|
||||
void onDriverDragCancel();
|
||||
}
|
||||
|
||||
public DragDriver(EventListener eventListener) {
|
||||
public DragDriver(EventListener eventListener, Consumer<MotionEvent> sec) {
|
||||
mEventListener = eventListener;
|
||||
mSecondaryEventConsumer = sec;
|
||||
}
|
||||
|
||||
/**
|
||||
* Handles ending of the DragView animation.
|
||||
* Called to handle system touch event
|
||||
*/
|
||||
public void onDragViewAnimationEnd() { }
|
||||
|
||||
public boolean onTouchEvent(MotionEvent ev) {
|
||||
final int action = ev.getAction();
|
||||
|
||||
switch (action) {
|
||||
case MotionEvent.ACTION_MOVE:
|
||||
mEventListener.onDriverDragMove(ev.getX(), ev.getY());
|
||||
break;
|
||||
case MotionEvent.ACTION_UP:
|
||||
mEventListener.onDriverDragMove(ev.getX(), ev.getY());
|
||||
mEventListener.onDriverDragEnd(ev.getX(), ev.getY());
|
||||
break;
|
||||
case MotionEvent.ACTION_CANCEL:
|
||||
mEventListener.onDriverDragCancel();
|
||||
break;
|
||||
}
|
||||
|
||||
return true;
|
||||
return false;
|
||||
}
|
||||
|
||||
public abstract boolean onDragEvent (DragEvent event);
|
||||
|
||||
|
||||
/**
|
||||
* Called to handle system touch intercept event
|
||||
*/
|
||||
public boolean onInterceptTouchEvent(MotionEvent ev) {
|
||||
final int action = ev.getAction();
|
||||
|
||||
switch (action) {
|
||||
case MotionEvent.ACTION_UP:
|
||||
mEventListener.onDriverDragEnd(ev.getX(), ev.getY());
|
||||
break;
|
||||
case MotionEvent.ACTION_CANCEL:
|
||||
mEventListener.onDriverDragCancel();
|
||||
break;
|
||||
}
|
||||
|
||||
return true;
|
||||
return false;
|
||||
}
|
||||
|
||||
public static DragDriver create(Context context, DragController dragController,
|
||||
DragObject dragObject, DragOptions options) {
|
||||
if (options.systemDndStartPoint != null) {
|
||||
return new SystemDragDriver(dragController, context, dragObject);
|
||||
/**
|
||||
* Called to handle system drag event
|
||||
*/
|
||||
public boolean onDragEvent(DragEvent event) {
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Created a driver for handing the actual events
|
||||
*/
|
||||
public static DragDriver create(DragController dragController, DragOptions options,
|
||||
Consumer<MotionEvent> sec) {
|
||||
if (options.simulatedDndStartPoint != null) {
|
||||
if (options.isAccessibleDrag) {
|
||||
return null;
|
||||
}
|
||||
return new SystemDragDriver(dragController, sec);
|
||||
} else {
|
||||
return new InternalDragDriver(dragController);
|
||||
return new InternalDragDriver(dragController, sec);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Class for driving a system (i.e. framework) drag/drop operation.
|
||||
*/
|
||||
static class SystemDragDriver extends DragDriver {
|
||||
|
||||
private final long mDragStartTime;
|
||||
float mLastX = 0;
|
||||
float mLastY = 0;
|
||||
|
||||
SystemDragDriver(DragController dragController, Consumer<MotionEvent> sec) {
|
||||
super(dragController, sec);
|
||||
mDragStartTime = SystemClock.uptimeMillis();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean onInterceptTouchEvent(MotionEvent ev) {
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* It creates a temporary {@link MotionEvent} object for secondary consumer
|
||||
*/
|
||||
private void simulateSecondaryMotionEvent(DragEvent event) {
|
||||
final int motionAction;
|
||||
switch (event.getAction()) {
|
||||
case DragEvent.ACTION_DRAG_STARTED:
|
||||
motionAction = MotionEvent.ACTION_DOWN;
|
||||
break;
|
||||
case DragEvent.ACTION_DRAG_LOCATION:
|
||||
motionAction = MotionEvent.ACTION_MOVE;
|
||||
break;
|
||||
case DragEvent.ACTION_DRAG_ENDED:
|
||||
motionAction = MotionEvent.ACTION_UP;
|
||||
break;
|
||||
default:
|
||||
return;
|
||||
}
|
||||
MotionEvent emulatedEvent = MotionEvent.obtain(mDragStartTime,
|
||||
SystemClock.uptimeMillis(), motionAction, event.getX(), event.getY(), 0);
|
||||
mSecondaryEventConsumer.accept(emulatedEvent);
|
||||
emulatedEvent.recycle();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean onDragEvent(DragEvent event) {
|
||||
simulateSecondaryMotionEvent(event);
|
||||
final int action = event.getAction();
|
||||
|
||||
switch (action) {
|
||||
case DragEvent.ACTION_DRAG_STARTED:
|
||||
mLastX = event.getX();
|
||||
mLastY = event.getY();
|
||||
return true;
|
||||
|
||||
case DragEvent.ACTION_DRAG_ENTERED:
|
||||
return true;
|
||||
|
||||
case DragEvent.ACTION_DRAG_LOCATION:
|
||||
mLastX = event.getX();
|
||||
mLastY = event.getY();
|
||||
mEventListener.onDriverDragMove(event.getX(), event.getY());
|
||||
return true;
|
||||
|
||||
case DragEvent.ACTION_DROP:
|
||||
mLastX = event.getX();
|
||||
mLastY = event.getY();
|
||||
mEventListener.onDriverDragMove(event.getX(), event.getY());
|
||||
mEventListener.onDriverDragEnd(mLastX, mLastY);
|
||||
return true;
|
||||
case DragEvent.ACTION_DRAG_EXITED:
|
||||
mEventListener.onDriverDragExitWindow();
|
||||
return true;
|
||||
|
||||
case DragEvent.ACTION_DRAG_ENDED:
|
||||
mEventListener.onDriverDragCancel();
|
||||
return true;
|
||||
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Class for driving an internal (i.e. not using framework) drag/drop operation.
|
||||
*/
|
||||
static class InternalDragDriver extends DragDriver {
|
||||
InternalDragDriver(DragController dragController, Consumer<MotionEvent> sec) {
|
||||
super(dragController, sec);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean onTouchEvent(MotionEvent ev) {
|
||||
mSecondaryEventConsumer.accept(ev);
|
||||
final int action = ev.getAction();
|
||||
|
||||
switch (action) {
|
||||
case MotionEvent.ACTION_MOVE:
|
||||
mEventListener.onDriverDragMove(ev.getX(), ev.getY());
|
||||
break;
|
||||
case MotionEvent.ACTION_UP:
|
||||
mEventListener.onDriverDragMove(ev.getX(), ev.getY());
|
||||
mEventListener.onDriverDragEnd(ev.getX(), ev.getY());
|
||||
break;
|
||||
case MotionEvent.ACTION_CANCEL:
|
||||
mEventListener.onDriverDragCancel();
|
||||
break;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
public boolean onInterceptTouchEvent(MotionEvent ev) {
|
||||
mSecondaryEventConsumer.accept(ev);
|
||||
final int action = ev.getAction();
|
||||
|
||||
switch (action) {
|
||||
case MotionEvent.ACTION_UP:
|
||||
mEventListener.onDriverDragEnd(ev.getX(), ev.getY());
|
||||
break;
|
||||
case MotionEvent.ACTION_CANCEL:
|
||||
mEventListener.onDriverDragCancel();
|
||||
break;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Class for driving a system (i.e. framework) drag/drop operation.
|
||||
*/
|
||||
class SystemDragDriver extends DragDriver {
|
||||
|
||||
float mLastX = 0;
|
||||
float mLastY = 0;
|
||||
|
||||
SystemDragDriver(DragController dragController, Context context, DragObject dragObject) {
|
||||
super(dragController);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean onTouchEvent(MotionEvent ev) {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean onInterceptTouchEvent(MotionEvent ev) {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean onDragEvent (DragEvent event) {
|
||||
final int action = event.getAction();
|
||||
|
||||
switch (action) {
|
||||
case DragEvent.ACTION_DRAG_STARTED:
|
||||
mLastX = event.getX();
|
||||
mLastY = event.getY();
|
||||
return true;
|
||||
|
||||
case DragEvent.ACTION_DRAG_ENTERED:
|
||||
return true;
|
||||
|
||||
case DragEvent.ACTION_DRAG_LOCATION:
|
||||
mLastX = event.getX();
|
||||
mLastY = event.getY();
|
||||
mEventListener.onDriverDragMove(event.getX(), event.getY());
|
||||
return true;
|
||||
|
||||
case DragEvent.ACTION_DROP:
|
||||
mLastX = event.getX();
|
||||
mLastY = event.getY();
|
||||
mEventListener.onDriverDragMove(event.getX(), event.getY());
|
||||
mEventListener.onDriverDragEnd(mLastX, mLastY);
|
||||
return true;
|
||||
case DragEvent.ACTION_DRAG_EXITED:
|
||||
mEventListener.onDriverDragExitWindow();
|
||||
return true;
|
||||
|
||||
case DragEvent.ACTION_DRAG_ENDED:
|
||||
mEventListener.onDriverDragCancel();
|
||||
return true;
|
||||
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Class for driving an internal (i.e. not using framework) drag/drop operation.
|
||||
*/
|
||||
class InternalDragDriver extends DragDriver {
|
||||
InternalDragDriver(DragController dragController) {
|
||||
super(dragController);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean onDragEvent (DragEvent event) { return false; }
|
||||
}
|
||||
|
||||
@@ -41,7 +41,6 @@ import android.view.accessibility.AccessibilityEvent;
|
||||
import android.view.accessibility.AccessibilityManager;
|
||||
import android.view.animation.Interpolator;
|
||||
import android.widget.FrameLayout;
|
||||
import android.widget.TextView;
|
||||
|
||||
import com.android.launcher3.AbstractFloatingView;
|
||||
import com.android.launcher3.CellLayout;
|
||||
@@ -52,7 +51,6 @@ import com.android.launcher3.ShortcutAndWidgetContainer;
|
||||
import com.android.launcher3.Workspace;
|
||||
import com.android.launcher3.anim.Interpolators;
|
||||
import com.android.launcher3.folder.Folder;
|
||||
import com.android.launcher3.folder.FolderIcon;
|
||||
import com.android.launcher3.graphics.OverviewScrim;
|
||||
import com.android.launcher3.graphics.RotationMode;
|
||||
import com.android.launcher3.graphics.WorkspaceAndHotseatScrim;
|
||||
@@ -90,6 +88,8 @@ public class DragLayer extends BaseDragLayer<Launcher> {
|
||||
private int mTopViewIndex;
|
||||
private int mChildCountOnLastUpdate = -1;
|
||||
|
||||
private Rect mTmpRect = new Rect();
|
||||
|
||||
// Related to adjacent page hints
|
||||
private final ViewGroupFocusHelper mFocusIndicatorHelper;
|
||||
private final WorkspaceAndHotseatScrim mWorkspaceScrim;
|
||||
@@ -254,59 +254,46 @@ public class DragLayer extends BaseDragLayer<Launcher> {
|
||||
|
||||
public void animateViewIntoPosition(DragView dragView, final View child, int duration,
|
||||
View anchorView) {
|
||||
|
||||
ShortcutAndWidgetContainer parentChildren = (ShortcutAndWidgetContainer) child.getParent();
|
||||
CellLayout.LayoutParams lp = (CellLayout.LayoutParams) child.getLayoutParams();
|
||||
parentChildren.measureChild(child);
|
||||
parentChildren.layoutChild(child);
|
||||
|
||||
Rect r = new Rect();
|
||||
getViewRectRelativeToSelf(dragView, r);
|
||||
getViewRectRelativeToSelf(dragView, mTmpRect);
|
||||
final int fromX = mTmpRect.left;
|
||||
final int fromY = mTmpRect.top;
|
||||
|
||||
float coord[] = new float[2];
|
||||
float childScale = child.getScaleX();
|
||||
|
||||
coord[0] = lp.x + (child.getMeasuredWidth() * (1 - childScale) / 2);
|
||||
coord[1] = lp.y + (child.getMeasuredHeight() * (1 - childScale) / 2);
|
||||
|
||||
// Since the child hasn't necessarily been laid out, we force the lp to be updated with
|
||||
// the correct coordinates (above) and use these to determine the final location
|
||||
float scale = getDescendantCoordRelativeToSelf((View) child.getParent(), coord);
|
||||
|
||||
// We need to account for the scale of the child itself, as the above only accounts for
|
||||
// for the scale in parents.
|
||||
scale *= childScale;
|
||||
int toX = Math.round(coord[0]);
|
||||
int toY = Math.round(coord[1]);
|
||||
float toScale = scale;
|
||||
if (child instanceof TextView) {
|
||||
TextView tv = (TextView) child;
|
||||
// Account for the source scale of the icon (ie. from AllApps to Workspace, in which
|
||||
// the workspace may have smaller icon bounds).
|
||||
toScale = scale / dragView.getIntrinsicIconScaleFactor();
|
||||
|
||||
// The child may be scaled (always about the center of the view) so to account for it,
|
||||
// we have to offset the position by the scaled size. Once we do that, we can center
|
||||
// the drag view about the scaled child view.
|
||||
// padding will remain constant (does not scale with size)
|
||||
toY += tv.getPaddingTop();
|
||||
toY -= dragView.getMeasuredHeight() * (1 - toScale) / 2;
|
||||
if (dragView.getDragVisualizeOffset() != null) {
|
||||
toY -= Math.round(toScale * dragView.getDragVisualizeOffset().y);
|
||||
}
|
||||
if (child instanceof DraggableView) {
|
||||
DraggableView d = (DraggableView) child;
|
||||
d.getVisualDragBounds(mTmpRect);
|
||||
|
||||
toX -= (dragView.getMeasuredWidth() - Math.round(scale * child.getMeasuredWidth())) / 2;
|
||||
} else if (child instanceof FolderIcon) {
|
||||
// Account for holographic blur padding on the drag view
|
||||
toY += Math.round(scale * (child.getPaddingTop() - dragView.getDragRegionTop()));
|
||||
toY -= scale * dragView.getBlurSizeOutline() / 2;
|
||||
toY -= (1 - scale) * dragView.getMeasuredHeight() / 2;
|
||||
// Center in the x coordinate about the target's drawable
|
||||
toX -= (dragView.getMeasuredWidth() - Math.round(scale * child.getMeasuredWidth())) / 2;
|
||||
} else {
|
||||
toY -= (Math.round(scale * (dragView.getHeight() - child.getMeasuredHeight()))) / 2;
|
||||
toX -= (Math.round(scale * (dragView.getMeasuredWidth()
|
||||
- child.getMeasuredWidth()))) / 2;
|
||||
// This accounts for the offset of the DragView created by scaling it about its
|
||||
// center as it animates into place.
|
||||
float scaleShiftX = dragView.getMeasuredWidth() * (1 - scale) / 2;
|
||||
float scaleShiftY = dragView.getMeasuredHeight() * (1 - scale) / 2;
|
||||
|
||||
toX += scale * (mTmpRect.left - dragView.getBlurSizeOutline() / 2) - scaleShiftX;
|
||||
toY += scale * (mTmpRect.top - dragView.getBlurSizeOutline() / 2) - scaleShiftY;
|
||||
}
|
||||
|
||||
final int fromX = r.left;
|
||||
final int fromY = r.top;
|
||||
child.setVisibility(INVISIBLE);
|
||||
Runnable onCompleteRunnable = () -> child.setVisibility(VISIBLE);
|
||||
animateViewIntoPosition(dragView, fromX, fromY, toX, toY, 1, 1, 1, toScale, toScale,
|
||||
@@ -559,7 +546,7 @@ public class DragLayer extends BaseDragLayer<Launcher> {
|
||||
@Override
|
||||
public void setInsets(Rect insets) {
|
||||
super.setInsets(insets);
|
||||
mWorkspaceScrim.onInsetsChanged(insets);
|
||||
mWorkspaceScrim.onInsetsChanged(insets, mAllowSysuiScrims);
|
||||
mOverviewScrim.onInsetsChanged(insets);
|
||||
}
|
||||
|
||||
|
||||
@@ -28,8 +28,11 @@ public class DragOptions {
|
||||
/** Whether or not an accessible drag operation is in progress. */
|
||||
public boolean isAccessibleDrag = false;
|
||||
|
||||
/** Specifies the start location for the system DnD, null when using internal DnD */
|
||||
public Point systemDndStartPoint = null;
|
||||
/**
|
||||
* Specifies the start location for a simulated DnD (like system drag or accessibility drag),
|
||||
* null when using internal DnD
|
||||
*/
|
||||
public Point simulatedDndStartPoint = null;
|
||||
|
||||
/** Determines when a pre-drag should transition to a drag. By default, this is immediate. */
|
||||
public PreDragCondition preDragCondition = null;
|
||||
|
||||
@@ -19,8 +19,6 @@ package com.android.launcher3.dragndrop;
|
||||
import static com.android.launcher3.Utilities.getBadge;
|
||||
import static com.android.launcher3.util.Executors.MODEL_EXECUTOR;
|
||||
|
||||
import android.animation.Animator;
|
||||
import android.animation.AnimatorListenerAdapter;
|
||||
import android.animation.FloatArrayEvaluator;
|
||||
import android.animation.ValueAnimator;
|
||||
import android.animation.ValueAnimator.AnimatorUpdateListener;
|
||||
@@ -146,15 +144,6 @@ public class DragView extends View implements LauncherStateManager.StateListener
|
||||
}
|
||||
});
|
||||
|
||||
mAnim.addListener(new AnimatorListenerAdapter() {
|
||||
@Override
|
||||
public void onAnimationEnd(Animator animation) {
|
||||
if (!mAnimationCancelled) {
|
||||
mDragController.onDragViewAnimationEnd();
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
mBitmap = bitmap;
|
||||
setDragRegion(new Rect(0, 0, bitmap.getWidth(), bitmap.getHeight()));
|
||||
|
||||
|
||||
@@ -0,0 +1,57 @@
|
||||
/*
|
||||
* 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.dragndrop;
|
||||
|
||||
import android.graphics.Rect;
|
||||
|
||||
/**
|
||||
* Interface defining methods required for drawing and previewing DragViews, drag previews, and
|
||||
* related animations
|
||||
*/
|
||||
public interface DraggableView {
|
||||
int DRAGGABLE_ICON = 0;
|
||||
int DRAGGABLE_WIDGET = 1;
|
||||
|
||||
/**
|
||||
* Static ctr for a simple instance which just returns the view type.
|
||||
*/
|
||||
static DraggableView ofType(int type) {
|
||||
return () -> type;
|
||||
}
|
||||
|
||||
/**
|
||||
* Certain handling of DragViews depend only on whether this is an Icon Type item or a Widget
|
||||
* Type item.
|
||||
*
|
||||
* @return DRAGGABLE_ICON or DRAGGABLE_WIDGET as appropriate
|
||||
*/
|
||||
int getViewType();
|
||||
|
||||
/**
|
||||
* Before rendering as a DragView bitmap, some views need a preparation step.
|
||||
*/
|
||||
default void prepareDrawDragView() { }
|
||||
|
||||
/**
|
||||
* If an actual View subclass, this method returns the rectangle (within the View's coordinates)
|
||||
* of the visual region that should get dragged. This is used to extract exactly that element
|
||||
* as well as to offset that element as appropriate for various animations
|
||||
*
|
||||
* @param bounds Visual bounds in the views coordinates will be written here.
|
||||
*/
|
||||
default void getVisualDragBounds(Rect bounds) { }
|
||||
}
|
||||
@@ -17,8 +17,6 @@
|
||||
package com.android.launcher3.dragndrop;
|
||||
|
||||
import android.graphics.PointF;
|
||||
import android.os.SystemClock;
|
||||
import android.view.DragEvent;
|
||||
import android.view.MotionEvent;
|
||||
import android.view.VelocityTracker;
|
||||
import android.view.ViewConfiguration;
|
||||
@@ -55,31 +53,6 @@ public class FlingToDeleteHelper {
|
||||
mVelocityTracker.addMovement(ev);
|
||||
}
|
||||
|
||||
/**
|
||||
* Same as {@link #recordMotionEvent}. It creates a temporary {@link MotionEvent} object
|
||||
* using {@param event} for tracking velocity.
|
||||
*/
|
||||
public void recordDragEvent(long dragStartTime, DragEvent event) {
|
||||
final int motionAction;
|
||||
switch (event.getAction()) {
|
||||
case DragEvent.ACTION_DRAG_STARTED:
|
||||
motionAction = MotionEvent.ACTION_DOWN;
|
||||
break;
|
||||
case DragEvent.ACTION_DRAG_LOCATION:
|
||||
motionAction = MotionEvent.ACTION_MOVE;
|
||||
break;
|
||||
case DragEvent.ACTION_DRAG_ENDED:
|
||||
motionAction = MotionEvent.ACTION_UP;
|
||||
break;
|
||||
default:
|
||||
return;
|
||||
}
|
||||
MotionEvent emulatedEvent = MotionEvent.obtain(dragStartTime, SystemClock.uptimeMillis(),
|
||||
motionAction, event.getX(), event.getY(), 0);
|
||||
recordMotionEvent(emulatedEvent);
|
||||
emulatedEvent.recycle();
|
||||
}
|
||||
|
||||
public void releaseVelocityTracker() {
|
||||
if (mVelocityTracker != null) {
|
||||
mVelocityTracker.recycle();
|
||||
|
||||
@@ -20,10 +20,10 @@ import static com.android.launcher3.util.Executors.MAIN_EXECUTOR;
|
||||
|
||||
import android.annotation.TargetApi;
|
||||
import android.graphics.Bitmap;
|
||||
import android.graphics.Canvas;
|
||||
import android.graphics.Matrix;
|
||||
import android.graphics.Path;
|
||||
import android.graphics.Point;
|
||||
import android.graphics.Rect;
|
||||
import android.graphics.drawable.AdaptiveIconDrawable;
|
||||
import android.graphics.drawable.ColorDrawable;
|
||||
import android.graphics.drawable.Drawable;
|
||||
@@ -33,7 +33,6 @@ import android.util.Log;
|
||||
import androidx.annotation.Nullable;
|
||||
|
||||
import com.android.launcher3.Launcher;
|
||||
import com.android.launcher3.R;
|
||||
import com.android.launcher3.folder.FolderIcon;
|
||||
import com.android.launcher3.folder.PreviewBackground;
|
||||
import com.android.launcher3.graphics.ShiftedBitmapDrawable;
|
||||
@@ -50,6 +49,7 @@ public class FolderAdaptiveIcon extends AdaptiveIconDrawable {
|
||||
private final Drawable mBadge;
|
||||
private final Path mMask;
|
||||
private final ConstantState mConstantState;
|
||||
private static final Rect sTmpRect = new Rect();
|
||||
|
||||
private FolderAdaptiveIcon(Drawable bg, Drawable fg, Drawable badge, Path mask) {
|
||||
super(bg, fg);
|
||||
@@ -72,23 +72,14 @@ public class FolderAdaptiveIcon extends AdaptiveIconDrawable {
|
||||
public static @Nullable FolderAdaptiveIcon createFolderAdaptiveIcon(
|
||||
Launcher launcher, int folderId, Point dragViewSize) {
|
||||
Preconditions.assertNonUiThread();
|
||||
int margin = launcher.getResources()
|
||||
.getDimensionPixelSize(R.dimen.blur_size_medium_outline);
|
||||
|
||||
// Allocate various bitmaps on the background thread, because why not!
|
||||
int width = dragViewSize.x - margin;
|
||||
int height = dragViewSize.y - margin;
|
||||
if (width <= 0 || height <= 0) {
|
||||
return null;
|
||||
}
|
||||
final Bitmap badge = Bitmap.createBitmap(width, height, Bitmap.Config.ARGB_8888);
|
||||
|
||||
// Create the actual drawable on the UI thread to avoid race conditions with
|
||||
// FolderIcon draw pass
|
||||
try {
|
||||
return MAIN_EXECUTOR.submit(() -> {
|
||||
FolderIcon icon = launcher.findFolderIcon(folderId);
|
||||
return icon == null ? null : createDrawableOnUiThread(icon, badge, dragViewSize);
|
||||
return icon == null ? null : createDrawableOnUiThread(icon, dragViewSize);
|
||||
|
||||
}).get();
|
||||
} catch (Exception e) {
|
||||
Log.e(TAG, "Unable to create folder icon", e);
|
||||
@@ -96,49 +87,50 @@ public class FolderAdaptiveIcon extends AdaptiveIconDrawable {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Initializes various bitmaps on the UI thread and returns the final drawable.
|
||||
*/
|
||||
private static FolderAdaptiveIcon createDrawableOnUiThread(FolderIcon icon,
|
||||
Bitmap badgeBitmap, Point dragViewSize) {
|
||||
Point dragViewSize) {
|
||||
Preconditions.assertUIThread();
|
||||
float margin = icon.getResources().getDimension(R.dimen.blur_size_medium_outline) / 2;
|
||||
|
||||
Canvas c = new Canvas();
|
||||
icon.getPreviewBounds(sTmpRect);
|
||||
|
||||
PreviewBackground bg = icon.getFolderBackground();
|
||||
|
||||
// Initialize badge
|
||||
c.setBitmap(badgeBitmap);
|
||||
bg.drawShadow(c);
|
||||
bg.drawBackgroundStroke(c);
|
||||
icon.drawDot(c);
|
||||
// assume square
|
||||
assert (dragViewSize.x == dragViewSize.y);
|
||||
final int previewSize = sTmpRect.width();
|
||||
|
||||
// Initialize preview
|
||||
final float sizeScaleFactor = 1 + 2 * AdaptiveIconDrawable.getExtraInsetFraction();
|
||||
final int previewWidth = (int) (dragViewSize.x * sizeScaleFactor);
|
||||
final int previewHeight = (int) (dragViewSize.y * sizeScaleFactor);
|
||||
final int margin = (dragViewSize.x - previewSize) / 2;
|
||||
final float previewShiftX = -sTmpRect.left + margin;
|
||||
final float previewShiftY = -sTmpRect.top + margin;
|
||||
|
||||
final float shiftFactor = AdaptiveIconDrawable.getExtraInsetFraction() / sizeScaleFactor;
|
||||
final float previewShiftX = shiftFactor * previewWidth;
|
||||
final float previewShiftY = shiftFactor * previewHeight;
|
||||
|
||||
Bitmap previewBitmap = BitmapRenderer.createHardwareBitmap(previewWidth, previewHeight,
|
||||
// Initialize badge, which consists of the outline stroke, shadow and dot; these
|
||||
// must be rendered above the foreground
|
||||
Bitmap badgeBmp = BitmapRenderer.createHardwareBitmap(dragViewSize.x, dragViewSize.y,
|
||||
(canvas) -> {
|
||||
int count = canvas.save();
|
||||
canvas.save();
|
||||
canvas.translate(previewShiftX, previewShiftY);
|
||||
icon.getPreviewItemManager().draw(canvas);
|
||||
canvas.restoreToCount(count);
|
||||
bg.drawShadow(canvas);
|
||||
bg.drawBackgroundStroke(canvas);
|
||||
icon.drawDot(canvas);
|
||||
canvas.restore();
|
||||
});
|
||||
|
||||
// Initialize mask
|
||||
Path mask = new Path();
|
||||
Matrix m = new Matrix();
|
||||
m.setTranslate(margin, margin);
|
||||
m.setTranslate(previewShiftX, previewShiftY);
|
||||
bg.getClipPath().transform(m, mask);
|
||||
|
||||
ShiftedBitmapDrawable badge = new ShiftedBitmapDrawable(badgeBitmap, margin, margin);
|
||||
ShiftedBitmapDrawable foreground = new ShiftedBitmapDrawable(previewBitmap,
|
||||
margin - previewShiftX, margin - previewShiftY);
|
||||
Bitmap previewBitmap = BitmapRenderer.createHardwareBitmap(dragViewSize.x, dragViewSize.y,
|
||||
(canvas) -> {
|
||||
canvas.save();
|
||||
canvas.translate(previewShiftX, previewShiftY);
|
||||
icon.getPreviewItemManager().draw(canvas);
|
||||
canvas.restore();
|
||||
});
|
||||
|
||||
ShiftedBitmapDrawable badge = new ShiftedBitmapDrawable(badgeBmp, 0, 0);
|
||||
ShiftedBitmapDrawable foreground = new ShiftedBitmapDrawable(previewBitmap, 0, 0);
|
||||
|
||||
return new FolderAdaptiveIcon(new ColorDrawable(bg.getBgColor()), foreground, badge, mask);
|
||||
}
|
||||
|
||||
@@ -81,10 +81,12 @@ import com.android.launcher3.OnAlarmListener;
|
||||
import com.android.launcher3.PagedView;
|
||||
import com.android.launcher3.R;
|
||||
import com.android.launcher3.ShortcutAndWidgetContainer;
|
||||
import com.android.launcher3.Utilities;
|
||||
import com.android.launcher3.Workspace;
|
||||
import com.android.launcher3.Workspace.ItemOperator;
|
||||
import com.android.launcher3.WorkspaceItemInfo;
|
||||
import com.android.launcher3.accessibility.AccessibleDragListenerAdapter;
|
||||
import com.android.launcher3.accessibility.FolderAccessibilityHelper;
|
||||
import com.android.launcher3.config.FeatureFlags;
|
||||
import com.android.launcher3.dragndrop.DragController;
|
||||
import com.android.launcher3.dragndrop.DragController.DragListener;
|
||||
@@ -271,16 +273,15 @@ public class Folder extends AbstractFloatingView implements ClipPathView, DragSo
|
||||
mDragController.addDragListener(this);
|
||||
if (options.isAccessibleDrag) {
|
||||
mDragController.addDragListener(new AccessibleDragListenerAdapter(
|
||||
mContent, CellLayout.FOLDER_ACCESSIBILITY_DRAG) {
|
||||
|
||||
@Override
|
||||
protected void enableAccessibleDrag(boolean enable) {
|
||||
super.enableAccessibleDrag(enable);
|
||||
mFooter.setImportantForAccessibility(enable
|
||||
? IMPORTANT_FOR_ACCESSIBILITY_NO_HIDE_DESCENDANTS
|
||||
: IMPORTANT_FOR_ACCESSIBILITY_AUTO);
|
||||
}
|
||||
});
|
||||
mContent, FolderAccessibilityHelper::new) {
|
||||
@Override
|
||||
protected void enableAccessibleDrag(boolean enable) {
|
||||
super.enableAccessibleDrag(enable);
|
||||
mFooter.setImportantForAccessibility(enable
|
||||
? IMPORTANT_FOR_ACCESSIBILITY_NO_HIDE_DESCENDANTS
|
||||
: IMPORTANT_FOR_ACCESSIBILITY_AUTO);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
mLauncher.getWorkspace().beginDragShared(v, this, options);
|
||||
@@ -324,13 +325,11 @@ public class Folder extends AbstractFloatingView implements ClipPathView, DragSo
|
||||
public void startEditingFolderName() {
|
||||
post(() -> {
|
||||
if (FeatureFlags.FOLDER_NAME_SUGGEST.get()) {
|
||||
if (isEmpty(mFolderName.getText())) {
|
||||
ofNullable(mInfo)
|
||||
.map(info -> info.suggestedFolderNames)
|
||||
.map(folderNames -> (FolderNameInfo[]) folderNames
|
||||
.getParcelableArrayExtra(FolderInfo.EXTRA_FOLDER_SUGGESTIONS))
|
||||
.ifPresent(nameInfos -> 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("");
|
||||
mIsEditingName = true;
|
||||
@@ -485,19 +484,24 @@ public class Folder extends AbstractFloatingView implements ClipPathView, DragSo
|
||||
nameInfos[1].getLabel());
|
||||
|
||||
if (shouldOpen) {
|
||||
CharSequence firstLabel = nameInfos[0] == null ? "" : nameInfos[0].getLabel();
|
||||
if (!isEmpty(firstLabel)) {
|
||||
mFolderName.setHint("");
|
||||
mFolderName.setText(firstLabel);
|
||||
// update the primary suggestion if the folder name is empty.
|
||||
if (isEmpty(mFolderName.getText())) {
|
||||
CharSequence firstLabel = nameInfos[0] == null ? "" : nameInfos[0].getLabel();
|
||||
if (!isEmpty(firstLabel)) {
|
||||
mFolderName.setHint("");
|
||||
mFolderName.setText(firstLabel);
|
||||
}
|
||||
}
|
||||
if (animate) {
|
||||
animateOpen(mInfo.contents, 0, true);
|
||||
}
|
||||
mFolderName.showKeyboard();
|
||||
mFolderName.displayCompletions(
|
||||
asList(nameInfos).subList(1, nameInfos.length).stream()
|
||||
asList(nameInfos).subList(0, nameInfos.length).stream()
|
||||
.filter(Objects::nonNull)
|
||||
.map(s -> s.getLabel().toString())
|
||||
.filter(s -> !s.isEmpty())
|
||||
.filter(s -> !s.equalsIgnoreCase(mFolderName.getText().toString()))
|
||||
.collect(Collectors.toList()));
|
||||
}
|
||||
}
|
||||
@@ -516,9 +520,6 @@ public class Folder extends AbstractFloatingView implements ClipPathView, DragSo
|
||||
}
|
||||
|
||||
private void startAnimation(final AnimatorSet a) {
|
||||
if (mCurrentAnimator != null && mCurrentAnimator.isRunning()) {
|
||||
mCurrentAnimator.cancel();
|
||||
}
|
||||
final Workspace workspace = mLauncher.getWorkspace();
|
||||
final CellLayout currentCellLayout =
|
||||
(CellLayout) workspace.getChildAt(workspace.getCurrentPage());
|
||||
@@ -635,6 +636,9 @@ public class Folder extends AbstractFloatingView implements ClipPathView, DragSo
|
||||
// dropping. One resulting issue is that replaceFolderWithFinalItem() can be called twice.
|
||||
mDeleteFolderOnDropCompleted = false;
|
||||
|
||||
if (mCurrentAnimator != null && mCurrentAnimator.isRunning()) {
|
||||
mCurrentAnimator.cancel();
|
||||
}
|
||||
AnimatorSet anim = new FolderAnimationManager(this, true /* isOpening */).getAnimator();
|
||||
anim.addListener(new AnimatorListenerAdapter() {
|
||||
@Override
|
||||
@@ -738,6 +742,9 @@ public class Folder extends AbstractFloatingView implements ClipPathView, DragSo
|
||||
}
|
||||
|
||||
private void animateClosed() {
|
||||
if (mCurrentAnimator != null && mCurrentAnimator.isRunning()) {
|
||||
mCurrentAnimator.cancel();
|
||||
}
|
||||
AnimatorSet a = new FolderAnimationManager(this, false /* isOpening */).getAnimator();
|
||||
a.addListener(new AnimatorListenerAdapter() {
|
||||
@Override
|
||||
@@ -956,6 +963,7 @@ public class Folder extends AbstractFloatingView implements ClipPathView, DragSo
|
||||
View icon = (mCurrentDragView != null && mCurrentDragView.getTag() == info)
|
||||
? mCurrentDragView : mContent.createNewView(info);
|
||||
ArrayList<View> views = getIconsInReadingOrder();
|
||||
info.rank = Utilities.boundToRange(info.rank, 0, views.size());
|
||||
views.add(info.rank, icon);
|
||||
mContent.arrangeChildren(views);
|
||||
mItemsInvalidated = true;
|
||||
|
||||
@@ -21,7 +21,6 @@ import static com.android.launcher3.LauncherAnimUtils.SCALE_PROPERTY;
|
||||
import static com.android.launcher3.folder.ClippedFolderIconLayoutRule.MAX_NUM_ITEMS_IN_PREVIEW;
|
||||
import static com.android.launcher3.graphics.IconShape.getShape;
|
||||
import static com.android.launcher3.icons.GraphicsUtils.setColorAlphaBound;
|
||||
import static com.android.launcher3.uioverrides.BackgroundBlurController.BACKGROUND_BLUR;
|
||||
|
||||
import android.animation.Animator;
|
||||
import android.animation.AnimatorListenerAdapter;
|
||||
@@ -47,7 +46,6 @@ import com.android.launcher3.ShortcutAndWidgetContainer;
|
||||
import com.android.launcher3.Utilities;
|
||||
import com.android.launcher3.anim.PropertyResetListener;
|
||||
import com.android.launcher3.dragndrop.DragLayer;
|
||||
import com.android.launcher3.uioverrides.BackgroundBlurController;
|
||||
import com.android.launcher3.util.Themes;
|
||||
|
||||
import java.util.List;
|
||||
@@ -222,14 +220,6 @@ public class FolderAnimationManager {
|
||||
Animator z = getAnimator(mFolder, View.TRANSLATION_Z, -mFolder.getElevation(), 0);
|
||||
play(a, z, mIsOpening ? midDuration : 0, midDuration);
|
||||
|
||||
BackgroundBlurController blurController = mLauncher.getBackgroundBlurController();
|
||||
int stateBackgroundBlur = mLauncher.getStateManager().getState()
|
||||
.getBackgroundBlurRadius(mLauncher);
|
||||
int folderBackgroundBlurAdjustment = blurController.getFolderBackgroundBlurAdjustment();
|
||||
play(a, ObjectAnimator.ofInt(blurController, BACKGROUND_BLUR, mIsOpening
|
||||
? stateBackgroundBlur + folderBackgroundBlurAdjustment
|
||||
: stateBackgroundBlur));
|
||||
|
||||
// Store clip variables
|
||||
CellLayout cellLayout = mContent.getCurrentCellLayout();
|
||||
boolean folderClipChildren = mFolder.getClipChildren();
|
||||
|
||||
@@ -63,6 +63,7 @@ import com.android.launcher3.dot.FolderDotInfo;
|
||||
import com.android.launcher3.dragndrop.BaseItemDragListener;
|
||||
import com.android.launcher3.dragndrop.DragLayer;
|
||||
import com.android.launcher3.dragndrop.DragView;
|
||||
import com.android.launcher3.dragndrop.DraggableView;
|
||||
import com.android.launcher3.icons.DotRenderer;
|
||||
import com.android.launcher3.touch.ItemClickHandler;
|
||||
import com.android.launcher3.util.Executors;
|
||||
@@ -78,7 +79,8 @@ import java.util.function.Predicate;
|
||||
/**
|
||||
* An icon that can appear on in the workspace representing an {@link Folder}.
|
||||
*/
|
||||
public class FolderIcon extends FrameLayout implements FolderListener, IconLabelDotView {
|
||||
public class FolderIcon extends FrameLayout implements FolderListener, IconLabelDotView,
|
||||
DraggableView {
|
||||
|
||||
@Thunk ActivityContext mActivity;
|
||||
@Thunk Folder mFolder;
|
||||
@@ -230,6 +232,16 @@ public class FolderIcon extends FrameLayout implements FolderListener, IconLabel
|
||||
mBackground.getBounds(outBounds);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getViewType() {
|
||||
return DRAGGABLE_ICON;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void getVisualDragBounds(Rect bounds) {
|
||||
getPreviewBounds(bounds);
|
||||
}
|
||||
|
||||
public float getBackgroundStrokeWidth() {
|
||||
return mBackground.getStrokeWidth();
|
||||
}
|
||||
@@ -525,6 +537,10 @@ public class FolderIcon extends FrameLayout implements FolderListener, IconLabel
|
||||
invalidate();
|
||||
}
|
||||
|
||||
public boolean getIconVisible() {
|
||||
return mBackgroundIsVisible;
|
||||
}
|
||||
|
||||
public PreviewBackground getFolderBackground() {
|
||||
return mBackground;
|
||||
}
|
||||
|
||||
@@ -30,14 +30,12 @@ import android.graphics.drawable.Drawable;
|
||||
import android.view.View;
|
||||
|
||||
import com.android.launcher3.BubbleTextView;
|
||||
import com.android.launcher3.FastBitmapDrawable;
|
||||
import com.android.launcher3.Launcher;
|
||||
import com.android.launcher3.R;
|
||||
import com.android.launcher3.config.FeatureFlags;
|
||||
import com.android.launcher3.folder.FolderIcon;
|
||||
import com.android.launcher3.dragndrop.DraggableView;
|
||||
import com.android.launcher3.icons.BitmapRenderer;
|
||||
import com.android.launcher3.widget.LauncherAppWidgetHostView;
|
||||
import com.android.launcher3.widget.PendingAppWidgetHostView;
|
||||
|
||||
import java.nio.ByteBuffer;
|
||||
|
||||
@@ -53,7 +51,7 @@ public class DragPreviewProvider {
|
||||
// The padding added to the drag view during the preview generation.
|
||||
public final int previewPadding;
|
||||
|
||||
protected final int blurSizeOutline;
|
||||
public final int blurSizeOutline;
|
||||
|
||||
private OutlineGeneratorCallback mOutlineGeneratorCallback;
|
||||
public Bitmap generatedDragOutline;
|
||||
@@ -66,56 +64,25 @@ public class DragPreviewProvider {
|
||||
mView = view;
|
||||
blurSizeOutline =
|
||||
context.getResources().getDimensionPixelSize(R.dimen.blur_size_medium_outline);
|
||||
|
||||
if (mView instanceof BubbleTextView) {
|
||||
Drawable d = ((BubbleTextView) mView).getIcon();
|
||||
Rect bounds = getDrawableBounds(d);
|
||||
previewPadding = blurSizeOutline - bounds.left - bounds.top;
|
||||
} else {
|
||||
previewPadding = blurSizeOutline;
|
||||
}
|
||||
previewPadding = blurSizeOutline;
|
||||
}
|
||||
|
||||
/**
|
||||
* Draws the {@link #mView} into the given {@param destCanvas}.
|
||||
*/
|
||||
protected void drawDragView(Canvas destCanvas, float scale) {
|
||||
destCanvas.save();
|
||||
int saveCount = destCanvas.save();
|
||||
destCanvas.scale(scale, scale);
|
||||
|
||||
if (mView instanceof BubbleTextView) {
|
||||
Drawable d = ((BubbleTextView) mView).getIcon();
|
||||
Rect bounds = getDrawableBounds(d);
|
||||
destCanvas.translate(blurSizeOutline / 2 - bounds.left,
|
||||
blurSizeOutline / 2 - bounds.top);
|
||||
if (d instanceof FastBitmapDrawable) {
|
||||
((FastBitmapDrawable) d).setScale(1);
|
||||
}
|
||||
d.draw(destCanvas);
|
||||
} else {
|
||||
final Rect clipRect = mTempRect;
|
||||
mView.getDrawingRect(clipRect);
|
||||
|
||||
boolean textVisible = false;
|
||||
if (mView instanceof FolderIcon) {
|
||||
// For FolderIcons the text can bleed into the icon area, and so we need to
|
||||
// hide the text completely (which can't be achieved by clipping).
|
||||
if (((FolderIcon) mView).getTextVisible()) {
|
||||
((FolderIcon) mView).setTextVisible(false);
|
||||
textVisible = true;
|
||||
}
|
||||
}
|
||||
destCanvas.translate(-mView.getScrollX() + blurSizeOutline / 2,
|
||||
-mView.getScrollY() + blurSizeOutline / 2);
|
||||
destCanvas.clipRect(clipRect);
|
||||
if (mView instanceof DraggableView) {
|
||||
DraggableView dv = (DraggableView) mView;
|
||||
dv.prepareDrawDragView();
|
||||
dv.getVisualDragBounds(mTempRect);
|
||||
destCanvas.translate(blurSizeOutline / 2 - mTempRect.left,
|
||||
blurSizeOutline / 2 - mTempRect.top);
|
||||
mView.draw(destCanvas);
|
||||
|
||||
// Restore text visibility of FolderIcon if necessary
|
||||
if (textVisible) {
|
||||
((FolderIcon) mView).setTextVisible(true);
|
||||
}
|
||||
}
|
||||
destCanvas.restore();
|
||||
destCanvas.restoreToCount(saveCount);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -123,28 +90,15 @@ public class DragPreviewProvider {
|
||||
* Responsibility for the bitmap is transferred to the caller.
|
||||
*/
|
||||
public Bitmap createDragBitmap() {
|
||||
int width = mView.getWidth();
|
||||
int height = mView.getHeight();
|
||||
|
||||
if (mView instanceof BubbleTextView) {
|
||||
Drawable d = ((BubbleTextView) mView).getIcon();
|
||||
Rect bounds = getDrawableBounds(d);
|
||||
width = bounds.width();
|
||||
height = bounds.height();
|
||||
} else if (mView instanceof LauncherAppWidgetHostView) {
|
||||
float scale = ((LauncherAppWidgetHostView) mView).getScaleToFit();
|
||||
width = (int) (mView.getWidth() * scale);
|
||||
height = (int) (mView.getHeight() * scale);
|
||||
|
||||
if (mView instanceof PendingAppWidgetHostView) {
|
||||
// Use hardware renderer as the icon for the pending app widget may be a hw bitmap
|
||||
return BitmapRenderer.createHardwareBitmap(width + blurSizeOutline,
|
||||
height + blurSizeOutline, (c) -> drawDragView(c, scale));
|
||||
} else {
|
||||
// Use software renderer for widgets as we know that they already work
|
||||
return BitmapRenderer.createSoftwareBitmap(width + blurSizeOutline,
|
||||
height + blurSizeOutline, (c) -> drawDragView(c, scale));
|
||||
}
|
||||
int width = 0;
|
||||
int height = 0;
|
||||
if (mView instanceof DraggableView) {
|
||||
((DraggableView) mView).getVisualDragBounds(mTempRect);
|
||||
width = mTempRect.width();
|
||||
height = mTempRect.height();
|
||||
} else {
|
||||
width = mView.getWidth();
|
||||
height = mView.getHeight();
|
||||
}
|
||||
|
||||
return BitmapRenderer.createHardwareBitmap(width + blurSizeOutline,
|
||||
|
||||
@@ -19,6 +19,7 @@ package com.android.launcher3.graphics;
|
||||
import static android.content.Intent.ACTION_SCREEN_OFF;
|
||||
import static android.content.Intent.ACTION_USER_PRESENT;
|
||||
|
||||
import static com.android.launcher3.config.FeatureFlags.KEYGUARD_ANIMATION;
|
||||
import static com.android.launcher3.icons.GraphicsUtils.setColorAlphaBound;
|
||||
|
||||
import android.animation.ObjectAnimator;
|
||||
@@ -39,12 +40,14 @@ import android.graphics.drawable.Drawable;
|
||||
import android.util.DisplayMetrics;
|
||||
import android.util.FloatProperty;
|
||||
import android.view.View;
|
||||
import android.view.WindowInsets;
|
||||
|
||||
import androidx.core.graphics.ColorUtils;
|
||||
|
||||
import com.android.launcher3.CellLayout;
|
||||
import com.android.launcher3.R;
|
||||
import com.android.launcher3.ResourceUtils;
|
||||
import com.android.launcher3.Utilities;
|
||||
import com.android.launcher3.Workspace;
|
||||
import com.android.launcher3.uioverrides.WallpaperColorInfo;
|
||||
import com.android.launcher3.util.Themes;
|
||||
@@ -81,6 +84,10 @@ public class WorkspaceAndHotseatScrim extends Scrim {
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Receiver used to get a signal that the user unlocked their device.
|
||||
* @see KEYGUARD_ANIMATION For proper signal.
|
||||
*/
|
||||
private final BroadcastReceiver mReceiver = new BroadcastReceiver() {
|
||||
@Override
|
||||
public void onReceive(Context context, Intent intent) {
|
||||
@@ -164,8 +171,10 @@ public class WorkspaceAndHotseatScrim extends Scrim {
|
||||
mSysUiAnimMultiplier = 0;
|
||||
reapplySysUiAlphaNoInvalidate();
|
||||
|
||||
animateToSysuiMultiplier(1, 600,
|
||||
mLauncher.getWindow().getTransitionBackgroundFadeDuration());
|
||||
ObjectAnimator oa = createSysuiMultiplierAnim(1);
|
||||
oa.setDuration(600);
|
||||
oa.setStartDelay(mLauncher.getWindow().getTransitionBackgroundFadeDuration());
|
||||
oa.start();
|
||||
mAnimateScrimOnNextDraw = false;
|
||||
}
|
||||
|
||||
@@ -178,26 +187,42 @@ public class WorkspaceAndHotseatScrim extends Scrim {
|
||||
}
|
||||
}
|
||||
|
||||
public void animateToSysuiMultiplier(float toMultiplier, long duration,
|
||||
long startDelay) {
|
||||
ObjectAnimator anim = ObjectAnimator.ofFloat(this, SYSUI_ANIM_MULTIPLIER, toMultiplier);
|
||||
/**
|
||||
* @return an ObjectAnimator that controls the fade in/out of the sys ui scrim.
|
||||
*/
|
||||
public ObjectAnimator createSysuiMultiplierAnim(float... values) {
|
||||
ObjectAnimator anim = ObjectAnimator.ofFloat(this, SYSUI_ANIM_MULTIPLIER, values);
|
||||
anim.setAutoCancel(true);
|
||||
anim.setDuration(duration);
|
||||
anim.setStartDelay(startDelay);
|
||||
anim.start();
|
||||
return anim;
|
||||
}
|
||||
|
||||
public void onInsetsChanged(Rect insets) {
|
||||
mDrawTopScrim = mTopScrim != null && insets.top > 0;
|
||||
mDrawBottomScrim = mBottomMask != null &&
|
||||
!mLauncher.getDeviceProfile().isVerticalBarLayout();
|
||||
/**
|
||||
* Determines whether to draw the top and/or bottom scrim based on new insets.
|
||||
*/
|
||||
public void onInsetsChanged(Rect insets, boolean allowSysuiScrims) {
|
||||
mDrawTopScrim = allowSysuiScrims
|
||||
&& mTopScrim != null
|
||||
&& insets.top > 0;
|
||||
mDrawBottomScrim = allowSysuiScrims
|
||||
&& mBottomMask != null
|
||||
&& !mLauncher.getDeviceProfile().isVerticalBarLayout()
|
||||
&& hasBottomNavButtons();
|
||||
}
|
||||
|
||||
private boolean hasBottomNavButtons() {
|
||||
if (Utilities.ATLEAST_Q && mLauncher.getRootView() != null
|
||||
&& mLauncher.getRootView().getRootWindowInsets() != null) {
|
||||
WindowInsets windowInsets = mLauncher.getRootView().getRootWindowInsets();
|
||||
return windowInsets.getTappableElementInsets().bottom > 0;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onViewAttachedToWindow(View view) {
|
||||
super.onViewAttachedToWindow(view);
|
||||
|
||||
if (mTopScrim != null) {
|
||||
if (!KEYGUARD_ANIMATION.get() && mTopScrim != null) {
|
||||
IntentFilter filter = new IntentFilter(ACTION_SCREEN_OFF);
|
||||
filter.addAction(ACTION_USER_PRESENT); // When the device wakes up + keyguard is gone
|
||||
mRoot.getContext().registerReceiver(mReceiver, filter);
|
||||
@@ -207,7 +232,7 @@ public class WorkspaceAndHotseatScrim extends Scrim {
|
||||
@Override
|
||||
public void onViewDetachedFromWindow(View view) {
|
||||
super.onViewDetachedFromWindow(view);
|
||||
if (mTopScrim != null) {
|
||||
if (!KEYGUARD_ANIMATION.get() && mTopScrim != null) {
|
||||
mRoot.getContext().unregisterReceiver(mReceiver);
|
||||
}
|
||||
}
|
||||
@@ -229,14 +254,6 @@ public class WorkspaceAndHotseatScrim extends Scrim {
|
||||
}
|
||||
}
|
||||
|
||||
public void hideSysUiScrim(boolean hideSysUiScrim) {
|
||||
mHideSysUiScrim = hideSysUiScrim || (mTopScrim == null);
|
||||
if (!hideSysUiScrim) {
|
||||
mAnimateScrimOnNextDraw = true;
|
||||
}
|
||||
invalidate();
|
||||
}
|
||||
|
||||
private void setSysUiProgress(float progress) {
|
||||
if (progress != mSysUiProgress) {
|
||||
mSysUiProgress = progress;
|
||||
|
||||
@@ -15,8 +15,6 @@
|
||||
*/
|
||||
package com.android.launcher3.logging;
|
||||
|
||||
import static com.android.launcher3.userevent.nano.LauncherLogProto.ContainerType.NAVBAR;
|
||||
|
||||
import android.util.ArrayMap;
|
||||
import android.util.SparseArray;
|
||||
import android.view.View;
|
||||
@@ -27,12 +25,9 @@ import com.android.launcher3.ItemInfo;
|
||||
import com.android.launcher3.LauncherSettings;
|
||||
import com.android.launcher3.userevent.nano.LauncherLogExtensions.TargetExtension;
|
||||
import com.android.launcher3.userevent.nano.LauncherLogProto.Action;
|
||||
import com.android.launcher3.userevent.nano.LauncherLogProto.ContainerType;
|
||||
import com.android.launcher3.userevent.nano.LauncherLogProto.ControlType;
|
||||
import com.android.launcher3.userevent.nano.LauncherLogProto.ItemType;
|
||||
import com.android.launcher3.userevent.nano.LauncherLogProto.LauncherEvent;
|
||||
import com.android.launcher3.userevent.nano.LauncherLogProto.Target;
|
||||
import com.android.launcher3.userevent.nano.LauncherLogProto.TipType;
|
||||
import com.android.launcher3.util.InstantAppResolver;
|
||||
|
||||
import java.lang.reflect.Field;
|
||||
@@ -70,93 +65,6 @@ public class LoggerUtils {
|
||||
return result != null ? result : UNKNOWN;
|
||||
}
|
||||
|
||||
public static String getActionStr(Action action) {
|
||||
String str = "";
|
||||
switch (action.type) {
|
||||
case Action.Type.TOUCH:
|
||||
str += getFieldName(action.touch, Action.Touch.class);
|
||||
if (action.touch == Action.Touch.SWIPE || action.touch == Action.Touch.FLING) {
|
||||
str += " direction=" + getFieldName(action.dir, Action.Direction.class);
|
||||
}
|
||||
break;
|
||||
case Action.Type.COMMAND:
|
||||
str += getFieldName(action.command, Action.Command.class);
|
||||
break;
|
||||
default: return getFieldName(action.type, Action.Type.class);
|
||||
}
|
||||
if (action.touch == Action.Touch.SWIPE || action.touch == Action.Touch.FLING ||
|
||||
(action.command == Action.Command.BACK && action.dir != Action.Direction.NONE)) {
|
||||
str += " direction=" + getFieldName(action.dir, Action.Direction.class);
|
||||
}
|
||||
return str;
|
||||
}
|
||||
|
||||
public static String getTargetStr(Target t) {
|
||||
if (t == null) {
|
||||
return "";
|
||||
}
|
||||
String str = "";
|
||||
switch (t.type) {
|
||||
case Target.Type.ITEM:
|
||||
str = getItemStr(t);
|
||||
break;
|
||||
case Target.Type.CONTROL:
|
||||
str = getFieldName(t.controlType, ControlType.class);
|
||||
break;
|
||||
case Target.Type.CONTAINER:
|
||||
str = getFieldName(t.containerType, ContainerType.class);
|
||||
if (t.containerType == ContainerType.WORKSPACE ||
|
||||
t.containerType == ContainerType.HOTSEAT ||
|
||||
t.containerType == NAVBAR) {
|
||||
str += " id=" + t.pageIndex;
|
||||
} else if (t.containerType == ContainerType.FOLDER) {
|
||||
str += " grid(" + t.gridX + "," + t.gridY + ")";
|
||||
}
|
||||
break;
|
||||
default:
|
||||
str += "UNKNOWN TARGET TYPE";
|
||||
}
|
||||
|
||||
if (t.spanX != 1 || t.spanY != 1) {
|
||||
str += " span(" + t.spanX + "," + t.spanY + ")";
|
||||
}
|
||||
|
||||
if (t.tipType != TipType.DEFAULT_NONE) {
|
||||
str += " " + getFieldName(t.tipType, TipType.class);
|
||||
}
|
||||
|
||||
return str;
|
||||
}
|
||||
|
||||
private static String getItemStr(Target t) {
|
||||
String typeStr = getFieldName(t.itemType, ItemType.class);
|
||||
if (t.packageNameHash != 0) {
|
||||
typeStr += ", packageHash=" + t.packageNameHash;
|
||||
}
|
||||
if (t.componentHash != 0) {
|
||||
typeStr += ", componentHash=" + t.componentHash;
|
||||
}
|
||||
if (t.intentHash != 0) {
|
||||
typeStr += ", intentHash=" + t.intentHash;
|
||||
}
|
||||
if (t.itemType == ItemType.FOLDER_ICON) {
|
||||
typeStr += ", grid(" + t.gridX + "," + t.gridY + ")";
|
||||
} else if ((t.packageNameHash != 0 || t.componentHash != 0 || t.intentHash != 0)
|
||||
&& t.itemType != ItemType.TASK) {
|
||||
typeStr +=
|
||||
", isWorkApp=" + t.isWorkApp + ", predictiveRank=" + t.predictedRank + ", grid("
|
||||
+ t.gridX + "," + t.gridY + "), span(" + t.spanX + "," + t.spanY
|
||||
+ "), pageIdx=" + t.pageIndex;
|
||||
}
|
||||
if (t.searchQueryLength != 0) {
|
||||
typeStr += ", searchQueryLength=" + t.searchQueryLength;
|
||||
}
|
||||
if (t.itemType == ItemType.TASK) {
|
||||
typeStr += ", pageIdx=" + t.pageIndex;
|
||||
}
|
||||
return typeStr;
|
||||
}
|
||||
|
||||
public static Target newItemTarget(int itemType) {
|
||||
Target t = newTarget(Target.Type.ITEM);
|
||||
t.itemType = itemType;
|
||||
|
||||
@@ -424,7 +424,8 @@ public class BgDataModel {
|
||||
// Now add the new shortcuts to the map.
|
||||
for (ShortcutInfo shortcut : shortcuts) {
|
||||
boolean shouldShowInContainer = shortcut.isEnabled()
|
||||
&& (shortcut.isDeclaredInManifest() || shortcut.isDynamic());
|
||||
&& (shortcut.isDeclaredInManifest() || shortcut.isDynamic())
|
||||
&& shortcut.getActivity() != null;
|
||||
if (shouldShowInContainer) {
|
||||
ComponentKey targetComponent
|
||||
= new ComponentKey(shortcut.getActivity(), shortcut.getUserHandle());
|
||||
|
||||
@@ -59,6 +59,7 @@ import com.android.launcher3.dot.DotInfo;
|
||||
import com.android.launcher3.dragndrop.DragController;
|
||||
import com.android.launcher3.dragndrop.DragOptions;
|
||||
import com.android.launcher3.dragndrop.DragView;
|
||||
import com.android.launcher3.dragndrop.DraggableView;
|
||||
import com.android.launcher3.notification.NotificationInfo;
|
||||
import com.android.launcher3.notification.NotificationItemView;
|
||||
import com.android.launcher3.notification.NotificationKeyData;
|
||||
@@ -662,7 +663,8 @@ public class PopupContainerWithArrow<T extends BaseDraggingActivity> extends Arr
|
||||
iconShift.x = mIconLastTouchPos.x - sv.getIconCenter().x;
|
||||
iconShift.y = mIconLastTouchPos.y - mLauncher.getDeviceProfile().iconSizePx;
|
||||
|
||||
DragView dv = mLauncher.getWorkspace().beginDragShared(sv.getIconView(),
|
||||
DraggableView draggableView = DraggableView.ofType(DraggableView.DRAGGABLE_ICON);
|
||||
DragView dv = mLauncher.getWorkspace().beginDragShared(sv.getIconView(), draggableView,
|
||||
mContainer, sv.getFinalInfo(),
|
||||
new ShortcutDragPreviewProvider(sv.getIconView(), iconShift),
|
||||
new DragOptions());
|
||||
|
||||
@@ -535,7 +535,7 @@ public abstract class AbstractStateChangeTouchController
|
||||
// case the user started interacting with it before the animation finished.
|
||||
mLauncher.getStateManager().goToState(targetState, false /* animated */);
|
||||
}
|
||||
mLauncher.getDragLayer().getScrim().animateToSysuiMultiplier(1, 0, 0);
|
||||
mLauncher.getDragLayer().getScrim().createSysuiMultiplierAnim(1f).setDuration(0).start();
|
||||
}
|
||||
|
||||
private void logReachedState(int logAction, LauncherState targetState) {
|
||||
|
||||
@@ -39,7 +39,7 @@ public class VibratorWrapper {
|
||||
public static final MainThreadInitializedObject<VibratorWrapper> INSTANCE =
|
||||
new MainThreadInitializedObject<>(VibratorWrapper::new);
|
||||
|
||||
private static final VibrationEffect EFFECT_CLICK =
|
||||
public static final VibrationEffect EFFECT_CLICK =
|
||||
createPredefined(VibrationEffect.EFFECT_CLICK);
|
||||
|
||||
/**
|
||||
|
||||
@@ -23,7 +23,11 @@ import static android.view.MotionEvent.ACTION_UP;
|
||||
import static com.android.launcher3.util.DefaultDisplay.getSingleFrameMs;
|
||||
|
||||
import android.annotation.TargetApi;
|
||||
import android.app.WallpaperInfo;
|
||||
import android.app.WallpaperManager;
|
||||
import android.content.ComponentName;
|
||||
import android.content.Context;
|
||||
import android.content.Intent;
|
||||
import android.graphics.Insets;
|
||||
import android.graphics.Rect;
|
||||
import android.graphics.RectF;
|
||||
@@ -38,11 +42,15 @@ import android.view.WindowInsets;
|
||||
import android.view.accessibility.AccessibilityEvent;
|
||||
import android.widget.FrameLayout;
|
||||
|
||||
import androidx.annotation.Nullable;
|
||||
|
||||
import com.android.launcher3.AbstractFloatingView;
|
||||
import com.android.launcher3.InsettableFrameLayout;
|
||||
import com.android.launcher3.R;
|
||||
import com.android.launcher3.Utilities;
|
||||
import com.android.launcher3.util.MultiValueAlpha;
|
||||
import com.android.launcher3.util.MultiValueAlpha.AlphaProperty;
|
||||
import com.android.launcher3.util.SimpleBroadcastReceiver;
|
||||
import com.android.launcher3.util.TouchController;
|
||||
|
||||
import java.io.PrintWriter;
|
||||
@@ -98,6 +106,10 @@ public abstract class BaseDragLayer<T extends Context & ActivityContext>
|
||||
|
||||
protected final T mActivity;
|
||||
private final MultiValueAlpha mMultiValueAlpha;
|
||||
private final WallpaperManager mWallpaperManager;
|
||||
private final SimpleBroadcastReceiver mWallpaperChangeReceiver =
|
||||
new SimpleBroadcastReceiver(this::onWallpaperChanged);
|
||||
private final String[] mWallpapersWithoutSysuiScrims;
|
||||
|
||||
// All the touch controllers for the view
|
||||
protected TouchController[] mControllers;
|
||||
@@ -108,10 +120,15 @@ public abstract class BaseDragLayer<T extends Context & ActivityContext>
|
||||
|
||||
private TouchCompleteListener mTouchCompleteListener;
|
||||
|
||||
protected boolean mAllowSysuiScrims = true;
|
||||
|
||||
public BaseDragLayer(Context context, AttributeSet attrs, int alphaChannelCount) {
|
||||
super(context, attrs);
|
||||
mActivity = (T) ActivityContext.lookupContext(context);
|
||||
mMultiValueAlpha = new MultiValueAlpha(this, alphaChannelCount);
|
||||
mWallpaperManager = context.getSystemService(WallpaperManager.class);
|
||||
mWallpapersWithoutSysuiScrims = getResources().getStringArray(
|
||||
R.array.live_wallpapers_remove_sysui_scrims);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -517,4 +534,46 @@ public abstract class BaseDragLayer<T extends Context & ActivityContext>
|
||||
}
|
||||
return super.dispatchApplyWindowInsets(insets);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onAttachedToWindow() {
|
||||
super.onAttachedToWindow();
|
||||
mWallpaperChangeReceiver.register(mActivity, Intent.ACTION_WALLPAPER_CHANGED);
|
||||
onWallpaperChanged(null);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onDetachedFromWindow() {
|
||||
super.onDetachedFromWindow();
|
||||
mActivity.unregisterReceiver(mWallpaperChangeReceiver);
|
||||
}
|
||||
|
||||
private void onWallpaperChanged(Intent unusedBroadcastIntent) {
|
||||
WallpaperInfo newWallpaperInfo = mWallpaperManager.getWallpaperInfo();
|
||||
boolean oldAllowSysuiScrims = mAllowSysuiScrims;
|
||||
mAllowSysuiScrims = computeAllowSysuiScrims(newWallpaperInfo);
|
||||
if (mAllowSysuiScrims != oldAllowSysuiScrims) {
|
||||
// Reapply insets so scrim can be removed or re-added if necessary.
|
||||
setInsets(mInsets);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Determines whether we can scrim the status bar and nav bar for the given wallpaper by
|
||||
* checking against a list of live wallpapers that we don't show the scrims on.
|
||||
*/
|
||||
private boolean computeAllowSysuiScrims(@Nullable WallpaperInfo newWallpaperInfo) {
|
||||
if (newWallpaperInfo == null) {
|
||||
// New wallpaper is static, not live. Thus, blacklist isn't applicable.
|
||||
return true;
|
||||
}
|
||||
ComponentName newWallpaper = newWallpaperInfo.getComponent();
|
||||
for (String wallpaperWithoutScrim : mWallpapersWithoutSysuiScrims) {
|
||||
if (newWallpaper.equals(ComponentName.unflattenFromString(wallpaperWithoutScrim))) {
|
||||
// New wallpaper is blacklisted from showing a scrim.
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,349 @@
|
||||
/*
|
||||
* Copyright (C) 2020 The Android Open Source Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package com.android.launcher3.views;
|
||||
|
||||
import static com.android.launcher3.Utilities.mapToRange;
|
||||
import static com.android.launcher3.anim.Interpolators.LINEAR;
|
||||
import static com.android.launcher3.views.FloatingIconView.SHAPE_PROGRESS_DURATION;
|
||||
|
||||
import android.animation.Animator;
|
||||
import android.animation.AnimatorListenerAdapter;
|
||||
import android.animation.ValueAnimator;
|
||||
import android.annotation.TargetApi;
|
||||
import android.content.Context;
|
||||
import android.graphics.Canvas;
|
||||
import android.graphics.Color;
|
||||
import android.graphics.Outline;
|
||||
import android.graphics.Path;
|
||||
import android.graphics.Rect;
|
||||
import android.graphics.RectF;
|
||||
import android.graphics.drawable.AdaptiveIconDrawable;
|
||||
import android.graphics.drawable.ColorDrawable;
|
||||
import android.graphics.drawable.Drawable;
|
||||
import android.os.Build;
|
||||
import android.util.AttributeSet;
|
||||
import android.view.View;
|
||||
import android.view.ViewOutlineProvider;
|
||||
|
||||
import androidx.annotation.Nullable;
|
||||
import androidx.dynamicanimation.animation.FloatPropertyCompat;
|
||||
import androidx.dynamicanimation.animation.SpringAnimation;
|
||||
import androidx.dynamicanimation.animation.SpringForce;
|
||||
|
||||
import com.android.launcher3.DeviceProfile;
|
||||
import com.android.launcher3.InsettableFrameLayout.LayoutParams;
|
||||
import com.android.launcher3.Launcher;
|
||||
import com.android.launcher3.R;
|
||||
import com.android.launcher3.Utilities;
|
||||
import com.android.launcher3.dragndrop.FolderAdaptiveIcon;
|
||||
import com.android.launcher3.graphics.IconShape;
|
||||
|
||||
/**
|
||||
* A view used to draw both layers of an {@link AdaptiveIconDrawable}.
|
||||
* Supports springing just the foreground layer.
|
||||
* Supports clipping the icon to/from its icon shape.
|
||||
*/
|
||||
@TargetApi(Build.VERSION_CODES.Q)
|
||||
public class ClipIconView extends View implements ClipPathView {
|
||||
|
||||
private static final Rect sTmpRect = new Rect();
|
||||
|
||||
// We spring the foreground drawable relative to the icon's movement in the DragLayer.
|
||||
// We then use these two factor values to scale the movement of the fg within this view.
|
||||
private static final int FG_TRANS_X_FACTOR = 60;
|
||||
private static final int FG_TRANS_Y_FACTOR = 75;
|
||||
|
||||
private static final FloatPropertyCompat<ClipIconView> mFgTransYProperty =
|
||||
new FloatPropertyCompat<ClipIconView>("ClipIconViewFgTransY") {
|
||||
@Override
|
||||
public float getValue(ClipIconView view) {
|
||||
return view.mFgTransY;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setValue(ClipIconView view, float transY) {
|
||||
view.mFgTransY = transY;
|
||||
view.invalidate();
|
||||
}
|
||||
};
|
||||
|
||||
private static final FloatPropertyCompat<ClipIconView> mFgTransXProperty =
|
||||
new FloatPropertyCompat<ClipIconView>("ClipIconViewFgTransX") {
|
||||
@Override
|
||||
public float getValue(ClipIconView view) {
|
||||
return view.mFgTransX;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setValue(ClipIconView view, float transX) {
|
||||
view.mFgTransX = transX;
|
||||
view.invalidate();
|
||||
}
|
||||
};
|
||||
|
||||
private final Launcher mLauncher;
|
||||
private final int mBlurSizeOutline;
|
||||
private final boolean mIsRtl;
|
||||
|
||||
private @Nullable Drawable mForeground;
|
||||
private @Nullable Drawable mBackground;
|
||||
|
||||
private boolean mIsVerticalBarLayout = false;
|
||||
private boolean mIsAdaptiveIcon = false;
|
||||
|
||||
private ValueAnimator mRevealAnimator;
|
||||
|
||||
private final Rect mStartRevealRect = new Rect();
|
||||
private final Rect mEndRevealRect = new Rect();
|
||||
private Path mClipPath;
|
||||
private float mTaskCornerRadius;
|
||||
|
||||
private final Rect mOutline = new Rect();
|
||||
private final Rect mFinalDrawableBounds = new Rect();
|
||||
|
||||
private final SpringAnimation mFgSpringY;
|
||||
private float mFgTransY;
|
||||
private final SpringAnimation mFgSpringX;
|
||||
private float mFgTransX;
|
||||
|
||||
public ClipIconView(Context context) {
|
||||
this(context, null);
|
||||
}
|
||||
|
||||
public ClipIconView(Context context, AttributeSet attrs) {
|
||||
this(context, attrs, 0);
|
||||
}
|
||||
|
||||
public ClipIconView(Context context, AttributeSet attrs, int defStyleAttr) {
|
||||
super(context, attrs, defStyleAttr);
|
||||
mLauncher = Launcher.getLauncher(context);
|
||||
mBlurSizeOutline = getResources().getDimensionPixelSize(
|
||||
R.dimen.blur_size_medium_outline);
|
||||
mIsRtl = Utilities.isRtl(getResources());
|
||||
|
||||
mFgSpringX = new SpringAnimation(this, mFgTransXProperty)
|
||||
.setSpring(new SpringForce()
|
||||
.setDampingRatio(SpringForce.DAMPING_RATIO_LOW_BOUNCY)
|
||||
.setStiffness(SpringForce.STIFFNESS_LOW));
|
||||
mFgSpringY = new SpringAnimation(this, mFgTransYProperty)
|
||||
.setSpring(new SpringForce()
|
||||
.setDampingRatio(SpringForce.DAMPING_RATIO_LOW_BOUNCY)
|
||||
.setStiffness(SpringForce.STIFFNESS_LOW));
|
||||
}
|
||||
|
||||
void update(RectF rect, float progress, float shapeProgressStart, float cornerRadius,
|
||||
boolean isOpening, float scale, float minSize, LayoutParams parentLp) {
|
||||
DeviceProfile dp = mLauncher.getDeviceProfile();
|
||||
float dX = mIsRtl
|
||||
? rect.left - (dp.widthPx - parentLp.getMarginStart() - parentLp.width)
|
||||
: rect.left - parentLp.getMarginStart();
|
||||
float dY = rect.top - parentLp.topMargin;
|
||||
|
||||
// shapeRevealProgress = 1 when progress = shapeProgressStart + SHAPE_PROGRESS_DURATION
|
||||
float toMax = isOpening ? 1 / SHAPE_PROGRESS_DURATION : 1f;
|
||||
float shapeRevealProgress = Utilities.boundToRange(mapToRange(
|
||||
Math.max(shapeProgressStart, progress), shapeProgressStart, 1f, 0, toMax,
|
||||
LINEAR), 0, 1);
|
||||
|
||||
if (mIsVerticalBarLayout) {
|
||||
mOutline.right = (int) (rect.width() / scale);
|
||||
} else {
|
||||
mOutline.bottom = (int) (rect.height() / scale);
|
||||
}
|
||||
|
||||
mTaskCornerRadius = cornerRadius / scale;
|
||||
if (mIsAdaptiveIcon) {
|
||||
if (!isOpening && progress >= shapeProgressStart) {
|
||||
if (mRevealAnimator == null) {
|
||||
mRevealAnimator = (ValueAnimator) IconShape.getShape().createRevealAnimator(
|
||||
this, mStartRevealRect, mOutline, mTaskCornerRadius, !isOpening);
|
||||
mRevealAnimator.addListener(new AnimatorListenerAdapter() {
|
||||
@Override
|
||||
public void onAnimationEnd(Animator animation) {
|
||||
mRevealAnimator = null;
|
||||
}
|
||||
});
|
||||
mRevealAnimator.start();
|
||||
// We pause here so we can set the current fraction ourselves.
|
||||
mRevealAnimator.pause();
|
||||
}
|
||||
mRevealAnimator.setCurrentFraction(shapeRevealProgress);
|
||||
}
|
||||
|
||||
float drawableScale = (mIsVerticalBarLayout ? mOutline.width() : mOutline.height())
|
||||
/ minSize;
|
||||
setBackgroundDrawableBounds(drawableScale);
|
||||
if (isOpening) {
|
||||
// Center align foreground
|
||||
int height = mFinalDrawableBounds.height();
|
||||
int width = mFinalDrawableBounds.width();
|
||||
int diffY = mIsVerticalBarLayout ? 0
|
||||
: (int) (((height * drawableScale) - height) / 2);
|
||||
int diffX = mIsVerticalBarLayout ? (int) (((width * drawableScale) - width) / 2)
|
||||
: 0;
|
||||
sTmpRect.set(mFinalDrawableBounds);
|
||||
sTmpRect.offset(diffX, diffY);
|
||||
mForeground.setBounds(sTmpRect);
|
||||
} else {
|
||||
// Spring the foreground relative to the icon's movement within the DragLayer.
|
||||
int diffX = (int) (dX / dp.availableWidthPx * FG_TRANS_X_FACTOR);
|
||||
int diffY = (int) (dY / dp.availableHeightPx * FG_TRANS_Y_FACTOR);
|
||||
|
||||
mFgSpringX.animateToFinalPosition(diffX);
|
||||
mFgSpringY.animateToFinalPosition(diffY);
|
||||
}
|
||||
}
|
||||
invalidate();
|
||||
invalidateOutline();
|
||||
}
|
||||
|
||||
private void setBackgroundDrawableBounds(float scale) {
|
||||
sTmpRect.set(mFinalDrawableBounds);
|
||||
Utilities.scaleRectAboutCenter(sTmpRect, scale);
|
||||
// Since the drawable is at the top of the view, we need to offset to keep it centered.
|
||||
if (mIsVerticalBarLayout) {
|
||||
sTmpRect.offsetTo((int) (mFinalDrawableBounds.left * scale), sTmpRect.top);
|
||||
} else {
|
||||
sTmpRect.offsetTo(sTmpRect.left, (int) (mFinalDrawableBounds.top * scale));
|
||||
}
|
||||
mBackground.setBounds(sTmpRect);
|
||||
}
|
||||
|
||||
protected void endReveal() {
|
||||
if (mRevealAnimator != null) {
|
||||
mRevealAnimator.end();
|
||||
}
|
||||
}
|
||||
|
||||
void setIcon(@Nullable Drawable drawable, int iconOffset, LayoutParams lp, boolean isOpening) {
|
||||
mIsAdaptiveIcon = drawable instanceof AdaptiveIconDrawable;
|
||||
if (mIsAdaptiveIcon) {
|
||||
boolean isFolderIcon = drawable instanceof FolderAdaptiveIcon;
|
||||
|
||||
AdaptiveIconDrawable adaptiveIcon = (AdaptiveIconDrawable) drawable;
|
||||
Drawable background = adaptiveIcon.getBackground();
|
||||
if (background == null) {
|
||||
background = new ColorDrawable(Color.TRANSPARENT);
|
||||
}
|
||||
mBackground = background;
|
||||
Drawable foreground = adaptiveIcon.getForeground();
|
||||
if (foreground == null) {
|
||||
foreground = new ColorDrawable(Color.TRANSPARENT);
|
||||
}
|
||||
mForeground = foreground;
|
||||
|
||||
final int originalHeight = lp.height;
|
||||
final int originalWidth = lp.width;
|
||||
|
||||
int blurMargin = mBlurSizeOutline / 2;
|
||||
mFinalDrawableBounds.set(0, 0, originalWidth, originalHeight);
|
||||
|
||||
if (!isFolderIcon) {
|
||||
mFinalDrawableBounds.inset(iconOffset - blurMargin, iconOffset - blurMargin);
|
||||
}
|
||||
mForeground.setBounds(mFinalDrawableBounds);
|
||||
mBackground.setBounds(mFinalDrawableBounds);
|
||||
|
||||
mStartRevealRect.set(0, 0, originalWidth, originalHeight);
|
||||
|
||||
if (!isFolderIcon) {
|
||||
Utilities.scaleRectAboutCenter(mStartRevealRect, IconShape.getNormalizationScale());
|
||||
}
|
||||
|
||||
float aspectRatio = mLauncher.getDeviceProfile().aspectRatio;
|
||||
if (mIsVerticalBarLayout) {
|
||||
lp.width = (int) Math.max(lp.width, lp.height * aspectRatio);
|
||||
} else {
|
||||
lp.height = (int) Math.max(lp.height, lp.width * aspectRatio);
|
||||
}
|
||||
|
||||
int left = mIsRtl
|
||||
? mLauncher.getDeviceProfile().widthPx - lp.getMarginStart() - lp.width
|
||||
: lp.leftMargin;
|
||||
layout(left, lp.topMargin, left + lp.width, lp.topMargin + lp.height);
|
||||
|
||||
float scale = Math.max((float) lp.height / originalHeight,
|
||||
(float) lp.width / originalWidth);
|
||||
float bgDrawableStartScale;
|
||||
if (isOpening) {
|
||||
bgDrawableStartScale = 1f;
|
||||
mOutline.set(0, 0, originalWidth, originalHeight);
|
||||
} else {
|
||||
bgDrawableStartScale = scale;
|
||||
mOutline.set(0, 0, lp.width, lp.height);
|
||||
}
|
||||
setBackgroundDrawableBounds(bgDrawableStartScale);
|
||||
mEndRevealRect.set(0, 0, lp.width, lp.height);
|
||||
setOutlineProvider(new ViewOutlineProvider() {
|
||||
@Override
|
||||
public void getOutline(View view, Outline outline) {
|
||||
outline.setRoundRect(mOutline, mTaskCornerRadius);
|
||||
}
|
||||
});
|
||||
setClipToOutline(true);
|
||||
} else {
|
||||
setBackground(drawable);
|
||||
setClipToOutline(false);
|
||||
}
|
||||
|
||||
invalidate();
|
||||
invalidateOutline();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setClipPath(Path clipPath) {
|
||||
mClipPath = clipPath;
|
||||
invalidate();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void draw(Canvas canvas) {
|
||||
int count = canvas.save();
|
||||
if (mClipPath != null) {
|
||||
canvas.clipPath(mClipPath);
|
||||
}
|
||||
super.draw(canvas);
|
||||
if (mBackground != null) {
|
||||
mBackground.draw(canvas);
|
||||
}
|
||||
if (mForeground != null) {
|
||||
int count2 = canvas.save();
|
||||
canvas.translate(mFgTransX, mFgTransY);
|
||||
mForeground.draw(canvas);
|
||||
canvas.restoreToCount(count2);
|
||||
}
|
||||
canvas.restoreToCount(count);
|
||||
}
|
||||
|
||||
void recycle() {
|
||||
setBackground(null);
|
||||
mIsAdaptiveIcon = false;
|
||||
mForeground = null;
|
||||
mBackground = null;
|
||||
mClipPath = null;
|
||||
mFinalDrawableBounds.setEmpty();
|
||||
if (mRevealAnimator != null) {
|
||||
mRevealAnimator.cancel();
|
||||
}
|
||||
mRevealAnimator = null;
|
||||
mTaskCornerRadius = 0;
|
||||
mOutline.setEmpty();
|
||||
mFgTransY = 0;
|
||||
mFgSpringX.cancel();
|
||||
mFgTransX = 0;
|
||||
mFgSpringY.cancel();
|
||||
}
|
||||
}
|
||||
@@ -18,8 +18,6 @@ package com.android.launcher3.views;
|
||||
import static com.android.launcher3.LauncherAnimUtils.DRAWABLE_ALPHA;
|
||||
import static com.android.launcher3.Utilities.getBadge;
|
||||
import static com.android.launcher3.Utilities.getFullDrawable;
|
||||
import static com.android.launcher3.Utilities.mapToRange;
|
||||
import static com.android.launcher3.anim.Interpolators.LINEAR;
|
||||
import static com.android.launcher3.config.FeatureFlags.ADAPTIVE_ICON_WINDOW_ANIM;
|
||||
import static com.android.launcher3.states.RotationHelper.REQUEST_LOCK;
|
||||
import static com.android.launcher3.util.Executors.MODEL_EXECUTOR;
|
||||
@@ -28,17 +26,12 @@ import android.animation.Animator;
|
||||
import android.animation.AnimatorListenerAdapter;
|
||||
import android.animation.AnimatorSet;
|
||||
import android.animation.ObjectAnimator;
|
||||
import android.animation.ValueAnimator;
|
||||
import android.annotation.TargetApi;
|
||||
import android.content.Context;
|
||||
import android.graphics.Canvas;
|
||||
import android.graphics.Color;
|
||||
import android.graphics.Outline;
|
||||
import android.graphics.Path;
|
||||
import android.graphics.Rect;
|
||||
import android.graphics.RectF;
|
||||
import android.graphics.drawable.AdaptiveIconDrawable;
|
||||
import android.graphics.drawable.ColorDrawable;
|
||||
import android.graphics.drawable.Drawable;
|
||||
import android.os.Build;
|
||||
import android.os.CancellationSignal;
|
||||
@@ -46,19 +39,17 @@ import android.util.AttributeSet;
|
||||
import android.util.Log;
|
||||
import android.view.View;
|
||||
import android.view.ViewGroup;
|
||||
import android.view.ViewOutlineProvider;
|
||||
import android.view.ViewTreeObserver.OnGlobalLayoutListener;
|
||||
import android.widget.FrameLayout;
|
||||
import android.widget.ImageView;
|
||||
|
||||
import androidx.annotation.Nullable;
|
||||
import androidx.annotation.UiThread;
|
||||
import androidx.annotation.WorkerThread;
|
||||
import androidx.dynamicanimation.animation.FloatPropertyCompat;
|
||||
import androidx.dynamicanimation.animation.SpringAnimation;
|
||||
import androidx.dynamicanimation.animation.SpringForce;
|
||||
|
||||
import com.android.launcher3.BubbleTextView;
|
||||
import com.android.launcher3.InsettableFrameLayout.LayoutParams;
|
||||
import com.android.launcher3.DeviceProfile;
|
||||
import com.android.launcher3.InsettableFrameLayout;
|
||||
import com.android.launcher3.ItemInfo;
|
||||
import com.android.launcher3.Launcher;
|
||||
import com.android.launcher3.R;
|
||||
@@ -66,8 +57,6 @@ import com.android.launcher3.Utilities;
|
||||
import com.android.launcher3.dragndrop.DragLayer;
|
||||
import com.android.launcher3.dragndrop.FolderAdaptiveIcon;
|
||||
import com.android.launcher3.folder.FolderIcon;
|
||||
import com.android.launcher3.graphics.IconShape;
|
||||
import com.android.launcher3.graphics.ShiftedBitmapDrawable;
|
||||
import com.android.launcher3.icons.LauncherIcons;
|
||||
import com.android.launcher3.popup.SystemShortcut;
|
||||
import com.android.launcher3.shortcuts.DeepShortcutView;
|
||||
@@ -76,8 +65,8 @@ import com.android.launcher3.shortcuts.DeepShortcutView;
|
||||
* A view that is created to look like another view with the purpose of creating fluid animations.
|
||||
*/
|
||||
@TargetApi(Build.VERSION_CODES.Q)
|
||||
public class FloatingIconView extends View implements
|
||||
Animator.AnimatorListener, ClipPathView, OnGlobalLayoutListener {
|
||||
public class FloatingIconView extends FrameLayout implements
|
||||
Animator.AnimatorListener, OnGlobalLayoutListener {
|
||||
|
||||
private static final String TAG = FloatingIconView.class.getSimpleName();
|
||||
|
||||
@@ -86,81 +75,34 @@ public class FloatingIconView extends View implements
|
||||
|
||||
public static final float SHAPE_PROGRESS_DURATION = 0.10f;
|
||||
private static final int FADE_DURATION_MS = 200;
|
||||
private static final Rect sTmpRect = new Rect();
|
||||
private static final RectF sTmpRectF = new RectF();
|
||||
private static final Object[] sTmpObjArray = new Object[1];
|
||||
|
||||
// We spring the foreground drawable relative to the icon's movement in the DragLayer.
|
||||
// We then use these two factor values to scale the movement of the fg within this view.
|
||||
private static final int FG_TRANS_X_FACTOR = 60;
|
||||
private static final int FG_TRANS_Y_FACTOR = 75;
|
||||
|
||||
private static final FloatPropertyCompat<FloatingIconView> mFgTransYProperty
|
||||
= new FloatPropertyCompat<FloatingIconView>("FloatingViewFgTransY") {
|
||||
@Override
|
||||
public float getValue(FloatingIconView view) {
|
||||
return view.mFgTransY;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setValue(FloatingIconView view, float transY) {
|
||||
view.mFgTransY = transY;
|
||||
view.invalidate();
|
||||
}
|
||||
};
|
||||
|
||||
private static final FloatPropertyCompat<FloatingIconView> mFgTransXProperty
|
||||
= new FloatPropertyCompat<FloatingIconView>("FloatingViewFgTransX") {
|
||||
@Override
|
||||
public float getValue(FloatingIconView view) {
|
||||
return view.mFgTransX;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setValue(FloatingIconView view, float transX) {
|
||||
view.mFgTransX = transX;
|
||||
view.invalidate();
|
||||
}
|
||||
};
|
||||
|
||||
private Runnable mEndRunnable;
|
||||
private CancellationSignal mLoadIconSignal;
|
||||
|
||||
private final Launcher mLauncher;
|
||||
private final int mBlurSizeOutline;
|
||||
private final boolean mIsRtl;
|
||||
|
||||
private boolean mIsVerticalBarLayout = false;
|
||||
private boolean mIsAdaptiveIcon = false;
|
||||
private boolean mIsOpening;
|
||||
|
||||
private IconLoadResult mIconLoadResult;
|
||||
|
||||
private ClipIconView mClipIconView;
|
||||
private @Nullable Drawable mBadge;
|
||||
private @Nullable Drawable mForeground;
|
||||
private @Nullable Drawable mBackground;
|
||||
|
||||
private float mRotation;
|
||||
private ValueAnimator mRevealAnimator;
|
||||
private final Rect mStartRevealRect = new Rect();
|
||||
private final Rect mEndRevealRect = new Rect();
|
||||
private Path mClipPath;
|
||||
private float mTaskCornerRadius;
|
||||
|
||||
private View mOriginalIcon;
|
||||
private RectF mPositionOut;
|
||||
private Runnable mOnTargetChangeRunnable;
|
||||
|
||||
private final Rect mOutline = new Rect();
|
||||
private final Rect mFinalDrawableBounds = new Rect();
|
||||
|
||||
private AnimatorSet mFadeAnimatorSet;
|
||||
private ListenerView mListenerView;
|
||||
|
||||
private final SpringAnimation mFgSpringY;
|
||||
private float mFgTransY;
|
||||
private final SpringAnimation mFgSpringX;
|
||||
private float mFgTransX;
|
||||
|
||||
public FloatingIconView(Context context) {
|
||||
this(context, null);
|
||||
}
|
||||
@@ -172,19 +114,11 @@ public class FloatingIconView extends View implements
|
||||
public FloatingIconView(Context context, AttributeSet attrs, int defStyleAttr) {
|
||||
super(context, attrs, defStyleAttr);
|
||||
mLauncher = Launcher.getLauncher(context);
|
||||
mBlurSizeOutline = getResources().getDimensionPixelSize(
|
||||
R.dimen.blur_size_medium_outline);
|
||||
mIsRtl = Utilities.isRtl(getResources());
|
||||
mListenerView = new ListenerView(context, attrs);
|
||||
|
||||
mFgSpringX = new SpringAnimation(this, mFgTransXProperty)
|
||||
.setSpring(new SpringForce()
|
||||
.setDampingRatio(SpringForce.DAMPING_RATIO_LOW_BOUNCY)
|
||||
.setStiffness(SpringForce.STIFFNESS_LOW));
|
||||
mFgSpringY = new SpringAnimation(this, mFgTransYProperty)
|
||||
.setSpring(new SpringForce()
|
||||
.setDampingRatio(SpringForce.DAMPING_RATIO_LOW_BOUNCY)
|
||||
.setStiffness(SpringForce.STIFFNESS_LOW));
|
||||
mClipIconView = new ClipIconView(context, attrs);
|
||||
addView(mClipIconView);
|
||||
setWillNotDraw(false);
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -213,10 +147,12 @@ public class FloatingIconView extends View implements
|
||||
float cornerRadius, boolean isOpening) {
|
||||
setAlpha(alpha);
|
||||
|
||||
LayoutParams lp = (LayoutParams) getLayoutParams();
|
||||
InsettableFrameLayout.LayoutParams lp =
|
||||
(InsettableFrameLayout.LayoutParams) getLayoutParams();
|
||||
|
||||
DeviceProfile dp = mLauncher.getDeviceProfile();
|
||||
float dX = mIsRtl
|
||||
? rect.left
|
||||
- (mLauncher.getDeviceProfile().widthPx - lp.getMarginStart() - lp.width)
|
||||
? rect.left - (dp.widthPx - lp.getMarginStart() - lp.width)
|
||||
: rect.left - lp.getMarginStart();
|
||||
float dY = rect.top - lp.topMargin;
|
||||
setTranslationX(dX);
|
||||
@@ -227,69 +163,15 @@ public class FloatingIconView extends View implements
|
||||
float scaleY = rect.height() / minSize;
|
||||
float scale = Math.max(1f, Math.min(scaleX, scaleY));
|
||||
|
||||
mClipIconView.update(rect, progress, shapeProgressStart, cornerRadius, isOpening, scale,
|
||||
minSize, lp);
|
||||
|
||||
setPivotX(0);
|
||||
setPivotY(0);
|
||||
setScaleX(scale);
|
||||
setScaleY(scale);
|
||||
|
||||
// shapeRevealProgress = 1 when progress = shapeProgressStart + SHAPE_PROGRESS_DURATION
|
||||
float toMax = isOpening ? 1 / SHAPE_PROGRESS_DURATION : 1f;
|
||||
float shapeRevealProgress = Utilities.boundToRange(mapToRange(
|
||||
Math.max(shapeProgressStart, progress), shapeProgressStart, 1f, 0, toMax,
|
||||
LINEAR), 0, 1);
|
||||
|
||||
if (mIsVerticalBarLayout) {
|
||||
mOutline.right = (int) (rect.width() / scale);
|
||||
} else {
|
||||
mOutline.bottom = (int) (rect.height() / scale);
|
||||
}
|
||||
|
||||
mTaskCornerRadius = cornerRadius / scale;
|
||||
if (mIsAdaptiveIcon) {
|
||||
if (!isOpening && progress >= shapeProgressStart) {
|
||||
if (mRevealAnimator == null) {
|
||||
mRevealAnimator = (ValueAnimator) IconShape.getShape().createRevealAnimator(
|
||||
this, mStartRevealRect, mOutline, mTaskCornerRadius, !isOpening);
|
||||
mRevealAnimator.addListener(new AnimatorListenerAdapter() {
|
||||
@Override
|
||||
public void onAnimationEnd(Animator animation) {
|
||||
mRevealAnimator = null;
|
||||
}
|
||||
});
|
||||
mRevealAnimator.start();
|
||||
// We pause here so we can set the current fraction ourselves.
|
||||
mRevealAnimator.pause();
|
||||
}
|
||||
mRevealAnimator.setCurrentFraction(shapeRevealProgress);
|
||||
}
|
||||
|
||||
float drawableScale = (mIsVerticalBarLayout ? mOutline.width() : mOutline.height())
|
||||
/ minSize;
|
||||
setBackgroundDrawableBounds(drawableScale);
|
||||
if (isOpening) {
|
||||
// Center align foreground
|
||||
int height = mFinalDrawableBounds.height();
|
||||
int width = mFinalDrawableBounds.width();
|
||||
int diffY = mIsVerticalBarLayout ? 0
|
||||
: (int) (((height * drawableScale) - height) / 2);
|
||||
int diffX = mIsVerticalBarLayout ? (int) (((width * drawableScale) - width) / 2)
|
||||
: 0;
|
||||
sTmpRect.set(mFinalDrawableBounds);
|
||||
sTmpRect.offset(diffX, diffY);
|
||||
mForeground.setBounds(sTmpRect);
|
||||
} else {
|
||||
// Spring the foreground relative to the icon's movement within the DragLayer.
|
||||
int diffX = (int) (dX / mLauncher.getDeviceProfile().availableWidthPx
|
||||
* FG_TRANS_X_FACTOR);
|
||||
int diffY = (int) (dY / mLauncher.getDeviceProfile().availableHeightPx
|
||||
* FG_TRANS_Y_FACTOR);
|
||||
|
||||
mFgSpringX.animateToFinalPosition(diffX);
|
||||
mFgSpringY.animateToFinalPosition(diffY);
|
||||
}
|
||||
}
|
||||
invalidate();
|
||||
invalidateOutline();
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -301,9 +183,7 @@ public class FloatingIconView extends View implements
|
||||
mEndRunnable.run();
|
||||
} else {
|
||||
// End runnable also ends the reveal animator, so we manually handle it here.
|
||||
if (mRevealAnimator != null) {
|
||||
mRevealAnimator.end();
|
||||
}
|
||||
mClipIconView.endReveal();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -315,23 +195,25 @@ public class FloatingIconView extends View implements
|
||||
*/
|
||||
private void matchPositionOf(Launcher launcher, View v, boolean isOpening, RectF positionOut) {
|
||||
float rotation = getLocationBoundsForView(launcher, v, isOpening, positionOut);
|
||||
final LayoutParams lp = new LayoutParams(
|
||||
final InsettableFrameLayout.LayoutParams lp = new InsettableFrameLayout.LayoutParams(
|
||||
Math.round(positionOut.width()),
|
||||
Math.round(positionOut.height()));
|
||||
updatePosition(rotation, positionOut, lp);
|
||||
setLayoutParams(lp);
|
||||
|
||||
mClipIconView.setLayoutParams(new FrameLayout.LayoutParams(lp.width, lp.height));
|
||||
}
|
||||
|
||||
private void updatePosition(float rotation, RectF position, LayoutParams lp) {
|
||||
private void updatePosition(float rotation, RectF pos, InsettableFrameLayout.LayoutParams lp) {
|
||||
mRotation = rotation;
|
||||
mPositionOut.set(position);
|
||||
mPositionOut.set(pos);
|
||||
lp.ignoreInsets = true;
|
||||
// Position the floating view exactly on top of the original
|
||||
lp.topMargin = Math.round(position.top);
|
||||
lp.topMargin = Math.round(pos.top);
|
||||
if (mIsRtl) {
|
||||
lp.setMarginStart(Math.round(mLauncher.getDeviceProfile().widthPx - position.right));
|
||||
lp.setMarginStart(Math.round(mLauncher.getDeviceProfile().widthPx - pos.right));
|
||||
} else {
|
||||
lp.setMarginStart(Math.round(position.left));
|
||||
lp.setMarginStart(Math.round(pos.left));
|
||||
}
|
||||
// Set the properties here already to make sure they are available when running the first
|
||||
// animation frame.
|
||||
@@ -412,9 +294,8 @@ public class FloatingIconView extends View implements
|
||||
drawable = originalView.getBackground();
|
||||
}
|
||||
} else {
|
||||
boolean isFolderIcon = originalView instanceof FolderIcon;
|
||||
int width = isFolderIcon ? originalView.getWidth() : (int) pos.width();
|
||||
int height = isFolderIcon ? originalView.getHeight() : (int) pos.height();
|
||||
int width = (int) pos.width();
|
||||
int height = (int) pos.height();
|
||||
if (supportsAdaptiveIcons) {
|
||||
drawable = getFullDrawable(l, info, width, height, sTmpObjArray);
|
||||
if (drawable instanceof AdaptiveIconDrawable) {
|
||||
@@ -451,110 +332,42 @@ public class FloatingIconView extends View implements
|
||||
/**
|
||||
* Sets the drawables of the {@param originalView} onto this view.
|
||||
*
|
||||
* @param originalView The View that the FloatingIconView will replace.
|
||||
* @param drawable The drawable of the original view.
|
||||
* @param badge The badge of the original view.
|
||||
* @param iconOffset The amount of offset needed to match this view with the original view.
|
||||
*/
|
||||
@UiThread
|
||||
private void setIcon(View originalView, @Nullable Drawable drawable, @Nullable Drawable badge,
|
||||
int iconOffset) {
|
||||
private void setIcon(@Nullable Drawable drawable, @Nullable Drawable badge, int iconOffset) {
|
||||
final InsettableFrameLayout.LayoutParams lp =
|
||||
(InsettableFrameLayout.LayoutParams) getLayoutParams();
|
||||
mBadge = badge;
|
||||
|
||||
mIsAdaptiveIcon = drawable instanceof AdaptiveIconDrawable;
|
||||
if (mIsAdaptiveIcon) {
|
||||
boolean isFolderIcon = drawable instanceof FolderAdaptiveIcon;
|
||||
|
||||
AdaptiveIconDrawable adaptiveIcon = (AdaptiveIconDrawable) drawable;
|
||||
Drawable background = adaptiveIcon.getBackground();
|
||||
if (background == null) {
|
||||
background = new ColorDrawable(Color.TRANSPARENT);
|
||||
}
|
||||
mBackground = background;
|
||||
Drawable foreground = adaptiveIcon.getForeground();
|
||||
if (foreground == null) {
|
||||
foreground = new ColorDrawable(Color.TRANSPARENT);
|
||||
}
|
||||
mForeground = foreground;
|
||||
|
||||
final LayoutParams lp = (LayoutParams) getLayoutParams();
|
||||
mClipIconView.setIcon(drawable, iconOffset, lp, mIsOpening);
|
||||
if (drawable instanceof AdaptiveIconDrawable) {
|
||||
final int originalHeight = lp.height;
|
||||
final int originalWidth = lp.width;
|
||||
|
||||
int blurMargin = mBlurSizeOutline / 2;
|
||||
mFinalDrawableBounds.set(0, 0, originalWidth, originalHeight);
|
||||
|
||||
if (!isFolderIcon) {
|
||||
mFinalDrawableBounds.inset(iconOffset - blurMargin, iconOffset - blurMargin);
|
||||
}
|
||||
mForeground.setBounds(mFinalDrawableBounds);
|
||||
mBackground.setBounds(mFinalDrawableBounds);
|
||||
|
||||
mStartRevealRect.set(0, 0, originalWidth, originalHeight);
|
||||
|
||||
if (mBadge != null) {
|
||||
mBadge.setBounds(mStartRevealRect);
|
||||
if (!mIsOpening && !isFolderIcon) {
|
||||
DRAWABLE_ALPHA.set(mBadge, 0);
|
||||
}
|
||||
}
|
||||
|
||||
if (isFolderIcon) {
|
||||
((FolderIcon) originalView).getPreviewBounds(sTmpRect);
|
||||
float bgStroke = ((FolderIcon) originalView).getBackgroundStrokeWidth();
|
||||
if (mForeground instanceof ShiftedBitmapDrawable) {
|
||||
ShiftedBitmapDrawable sbd = (ShiftedBitmapDrawable) mForeground;
|
||||
sbd.setShiftX(sbd.getShiftX() - sTmpRect.left - bgStroke);
|
||||
sbd.setShiftY(sbd.getShiftY() - sTmpRect.top - bgStroke);
|
||||
}
|
||||
if (mBadge instanceof ShiftedBitmapDrawable) {
|
||||
ShiftedBitmapDrawable sbd = (ShiftedBitmapDrawable) mBadge;
|
||||
sbd.setShiftX(sbd.getShiftX() - sTmpRect.left - bgStroke);
|
||||
sbd.setShiftY(sbd.getShiftY() - sTmpRect.top - bgStroke);
|
||||
}
|
||||
} else {
|
||||
Utilities.scaleRectAboutCenter(mStartRevealRect,
|
||||
IconShape.getNormalizationScale());
|
||||
}
|
||||
|
||||
float aspectRatio = mLauncher.getDeviceProfile().aspectRatio;
|
||||
if (mIsVerticalBarLayout) {
|
||||
lp.width = (int) Math.max(lp.width, lp.height * aspectRatio);
|
||||
} else {
|
||||
lp.height = (int) Math.max(lp.height, lp.width * aspectRatio);
|
||||
}
|
||||
setLayoutParams(lp);
|
||||
|
||||
int left = mIsRtl
|
||||
? mLauncher.getDeviceProfile().widthPx - lp.getMarginStart() - lp.width
|
||||
: lp.leftMargin;
|
||||
layout(left, lp.topMargin, left + lp.width, lp.topMargin + lp.height);
|
||||
final LayoutParams clipViewLp = (LayoutParams) mClipIconView.getLayoutParams();
|
||||
final int clipViewOgHeight = clipViewLp.height;
|
||||
final int clipViewOgWidth = clipViewLp.width;
|
||||
clipViewLp.width = lp.width;
|
||||
clipViewLp.height = lp.height;
|
||||
mClipIconView.setLayoutParams(clipViewLp);
|
||||
|
||||
float scale = Math.max((float) lp.height / originalHeight,
|
||||
(float) lp.width / originalWidth);
|
||||
float bgDrawableStartScale;
|
||||
if (mIsOpening) {
|
||||
bgDrawableStartScale = 1f;
|
||||
mOutline.set(0, 0, originalWidth, originalHeight);
|
||||
} else {
|
||||
bgDrawableStartScale = scale;
|
||||
mOutline.set(0, 0, lp.width, lp.height);
|
||||
if (mBadge != null) {
|
||||
mBadge.setBounds(0, 0, clipViewOgWidth, clipViewOgHeight);
|
||||
}
|
||||
setBackgroundDrawableBounds(bgDrawableStartScale);
|
||||
mEndRevealRect.set(0, 0, lp.width, lp.height);
|
||||
setOutlineProvider(new ViewOutlineProvider() {
|
||||
@Override
|
||||
public void getOutline(View view, Outline outline) {
|
||||
outline.setRoundRect(mOutline, mTaskCornerRadius);
|
||||
}
|
||||
});
|
||||
setClipToOutline(true);
|
||||
} else {
|
||||
setBackground(drawable);
|
||||
setClipToOutline(false);
|
||||
}
|
||||
|
||||
invalidate();
|
||||
invalidateOutline();
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -571,7 +384,7 @@ public class FloatingIconView extends View implements
|
||||
|
||||
synchronized (mIconLoadResult) {
|
||||
if (mIconLoadResult.isIconLoaded) {
|
||||
setIcon(originalView, mIconLoadResult.drawable, mIconLoadResult.badge,
|
||||
setIcon(mIconLoadResult.drawable, mIconLoadResult.badge,
|
||||
mIconLoadResult.iconOffset);
|
||||
hideOriginalView(originalView);
|
||||
} else {
|
||||
@@ -580,7 +393,7 @@ public class FloatingIconView extends View implements
|
||||
return;
|
||||
}
|
||||
|
||||
setIcon(originalView, mIconLoadResult.drawable, mIconLoadResult.badge,
|
||||
setIcon(mIconLoadResult.drawable, mIconLoadResult.badge,
|
||||
mIconLoadResult.iconOffset);
|
||||
|
||||
setVisibility(VISIBLE);
|
||||
@@ -600,23 +413,12 @@ public class FloatingIconView extends View implements
|
||||
}
|
||||
}
|
||||
|
||||
private void setBackgroundDrawableBounds(float scale) {
|
||||
sTmpRect.set(mFinalDrawableBounds);
|
||||
Utilities.scaleRectAboutCenter(sTmpRect, scale);
|
||||
// Since the drawable is at the top of the view, we need to offset to keep it centered.
|
||||
if (mIsVerticalBarLayout) {
|
||||
sTmpRect.offsetTo((int) (mFinalDrawableBounds.left * scale), sTmpRect.top);
|
||||
} else {
|
||||
sTmpRect.offsetTo(sTmpRect.left, (int) (mFinalDrawableBounds.top * scale));
|
||||
}
|
||||
mBackground.setBounds(sTmpRect);
|
||||
}
|
||||
|
||||
@WorkerThread
|
||||
@SuppressWarnings("WrongThread")
|
||||
private static int getOffsetForIconBounds(Launcher l, Drawable drawable, RectF position) {
|
||||
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.O ||
|
||||
!(drawable instanceof AdaptiveIconDrawable)) {
|
||||
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.O
|
||||
|| !(drawable instanceof AdaptiveIconDrawable)
|
||||
|| (drawable instanceof FolderAdaptiveIcon)) {
|
||||
return 0;
|
||||
}
|
||||
int blurSizeOutline =
|
||||
@@ -640,29 +442,11 @@ public class FloatingIconView extends View implements
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setClipPath(Path clipPath) {
|
||||
mClipPath = clipPath;
|
||||
invalidate();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void draw(Canvas canvas) {
|
||||
protected void dispatchDraw(Canvas canvas) {
|
||||
int count = canvas.save();
|
||||
canvas.rotate(mRotation,
|
||||
mFinalDrawableBounds.exactCenterX(), mFinalDrawableBounds.exactCenterY());
|
||||
if (mClipPath != null) {
|
||||
canvas.clipPath(mClipPath);
|
||||
}
|
||||
super.draw(canvas);
|
||||
if (mBackground != null) {
|
||||
mBackground.draw(canvas);
|
||||
}
|
||||
if (mForeground != null) {
|
||||
int count2 = canvas.save();
|
||||
canvas.translate(mFgTransX, mFgTransY);
|
||||
mForeground.draw(canvas);
|
||||
canvas.restoreToCount(count2);
|
||||
}
|
||||
super.dispatchDraw(canvas);
|
||||
if (mBadge != null) {
|
||||
mBadge.draw(canvas);
|
||||
}
|
||||
@@ -706,7 +490,8 @@ public class FloatingIconView extends View implements
|
||||
float rotation = getLocationBoundsForView(mLauncher, mOriginalIcon, mIsOpening,
|
||||
sTmpRectF);
|
||||
if (rotation != mRotation || !sTmpRectF.equals(mPositionOut)) {
|
||||
updatePosition(rotation, sTmpRectF, (LayoutParams) getLayoutParams());
|
||||
updatePosition(rotation, sTmpRectF,
|
||||
(InsettableFrameLayout.LayoutParams) getLayoutParams());
|
||||
if (mOnTargetChangeRunnable != null) {
|
||||
mOnTargetChangeRunnable.run();
|
||||
}
|
||||
@@ -822,12 +607,6 @@ public class FloatingIconView extends View implements
|
||||
}
|
||||
});
|
||||
|
||||
if (mBadge != null) {
|
||||
ObjectAnimator badgeFade = ObjectAnimator.ofInt(mBadge, DRAWABLE_ALPHA, 255);
|
||||
badgeFade.addUpdateListener(valueAnimator -> invalidate());
|
||||
fade.play(badgeFade);
|
||||
}
|
||||
|
||||
if (originalView instanceof IconLabelDotView) {
|
||||
IconLabelDotView view = (IconLabelDotView) originalView;
|
||||
fade.addListener(new AnimatorListenerAdapter() {
|
||||
@@ -869,21 +648,12 @@ public class FloatingIconView extends View implements
|
||||
setScaleX(1);
|
||||
setScaleY(1);
|
||||
setAlpha(1);
|
||||
setBackground(null);
|
||||
if (mLoadIconSignal != null) {
|
||||
mLoadIconSignal.cancel();
|
||||
}
|
||||
mLoadIconSignal = null;
|
||||
mEndRunnable = null;
|
||||
mIsAdaptiveIcon = false;
|
||||
mForeground = null;
|
||||
mBackground = null;
|
||||
mClipPath = null;
|
||||
mFinalDrawableBounds.setEmpty();
|
||||
if (mRevealAnimator != null) {
|
||||
mRevealAnimator.cancel();
|
||||
}
|
||||
mRevealAnimator = null;
|
||||
if (mFadeAnimatorSet != null) {
|
||||
mFadeAnimatorSet.cancel();
|
||||
}
|
||||
@@ -892,15 +662,10 @@ public class FloatingIconView extends View implements
|
||||
mListenerView.setListener(null);
|
||||
mOriginalIcon = null;
|
||||
mOnTargetChangeRunnable = null;
|
||||
mTaskCornerRadius = 0;
|
||||
mOutline.setEmpty();
|
||||
mFgTransY = 0;
|
||||
mFgSpringX.cancel();
|
||||
mFgTransX = 0;
|
||||
mFgSpringY.cancel();
|
||||
mBadge = null;
|
||||
sTmpObjArray[0] = null;
|
||||
mIconLoadResult = null;
|
||||
mClipIconView.recycle();
|
||||
}
|
||||
|
||||
private static class IconLoadResult {
|
||||
@@ -911,7 +676,7 @@ public class FloatingIconView extends View implements
|
||||
Runnable onIconLoaded;
|
||||
boolean isIconLoaded;
|
||||
|
||||
public IconLoadResult(ItemInfo itemInfo) {
|
||||
IconLoadResult(ItemInfo itemInfo) {
|
||||
this.itemInfo = itemInfo;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,133 +0,0 @@
|
||||
/*
|
||||
* Copyright (C) 2017 The Android Open Source Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package com.android.launcher3.views;
|
||||
|
||||
import static com.android.launcher3.util.PackageManagerHelper.hasShortcutsPermission;
|
||||
|
||||
import android.animation.ObjectAnimator;
|
||||
import android.animation.PropertyValuesHolder;
|
||||
import android.content.Context;
|
||||
import android.content.pm.PackageManager;
|
||||
import android.graphics.Rect;
|
||||
import android.util.AttributeSet;
|
||||
import android.view.View;
|
||||
import android.widget.LinearLayout;
|
||||
import android.widget.TextView;
|
||||
|
||||
import com.android.launcher3.Insettable;
|
||||
import com.android.launcher3.Launcher;
|
||||
import com.android.launcher3.R;
|
||||
import com.android.launcher3.Utilities;
|
||||
import com.android.launcher3.allapps.WorkModeSwitch;
|
||||
import com.android.launcher3.pm.UserCache;
|
||||
|
||||
/**
|
||||
* Container to show work footer in all-apps.
|
||||
*/
|
||||
public class WorkFooterContainer extends LinearLayout implements Insettable {
|
||||
private Rect mInsets = new Rect();
|
||||
|
||||
private WorkModeSwitch mWorkModeSwitch;
|
||||
private TextView mWorkModeLabel;
|
||||
|
||||
protected final ObjectAnimator mOpenCloseAnimator;
|
||||
|
||||
public WorkFooterContainer(Context context) {
|
||||
this(context, null, 0);
|
||||
}
|
||||
|
||||
public WorkFooterContainer(Context context, AttributeSet attrs) {
|
||||
this(context, attrs, 0);
|
||||
}
|
||||
|
||||
public WorkFooterContainer(Context context, AttributeSet attrs, int defStyleAttr) {
|
||||
super(context, attrs, defStyleAttr);
|
||||
mOpenCloseAnimator = ObjectAnimator.ofPropertyValuesHolder(this);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onLayout(boolean changed, int l, int t, int r, int b) {
|
||||
super.onLayout(changed, l, t, r, b);
|
||||
updateTranslation();
|
||||
this.setVisibility(shouldShowWorkFooter() ? VISIBLE : GONE);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onFinishInflate() {
|
||||
super.onFinishInflate();
|
||||
mWorkModeSwitch = findViewById(R.id.work_mode_toggle);
|
||||
mWorkModeLabel = findViewById(R.id.work_mode_label);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void offsetTopAndBottom(int offset) {
|
||||
super.offsetTopAndBottom(offset);
|
||||
updateTranslation();
|
||||
}
|
||||
|
||||
private void updateTranslation() {
|
||||
if (getParent() instanceof View) {
|
||||
View parent = (View) getParent();
|
||||
int availableBot = parent.getHeight() - parent.getPaddingBottom();
|
||||
setTranslationY(Math.max(0, availableBot - getBottom()));
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setInsets(Rect insets) {
|
||||
int bottomInset = insets.bottom - mInsets.bottom;
|
||||
mInsets.set(insets);
|
||||
setPadding(getPaddingLeft(), getPaddingTop(), getPaddingRight(),
|
||||
getPaddingBottom() + bottomInset);
|
||||
}
|
||||
|
||||
/**
|
||||
* Animates in/out work profile toggle panel based on the tab user is on
|
||||
*/
|
||||
public void setWorkTabVisible(boolean workTabVisible) {
|
||||
if (!shouldShowWorkFooter()) return;
|
||||
|
||||
mOpenCloseAnimator.setValues(PropertyValuesHolder.ofFloat(ALPHA, workTabVisible ? 1 : 0));
|
||||
mOpenCloseAnimator.start();
|
||||
}
|
||||
|
||||
/**
|
||||
* Refreshes views based on current work profile enabled status
|
||||
*/
|
||||
public void refresh() {
|
||||
if (!shouldShowWorkFooter()) return;
|
||||
boolean anyProfileQuietModeEnabled = UserCache.INSTANCE.get(
|
||||
getContext()).isAnyProfileQuietModeEnabled();
|
||||
|
||||
mWorkModeLabel.setCompoundDrawablesWithIntrinsicBounds(
|
||||
anyProfileQuietModeEnabled ? R.drawable.ic_corp_off : R.drawable.ic_corp, 0, 0, 0);
|
||||
mWorkModeSwitch.refresh();
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns work mode switch
|
||||
*/
|
||||
public WorkModeSwitch getWorkModeSwitch() {
|
||||
return mWorkModeSwitch;
|
||||
}
|
||||
|
||||
private boolean shouldShowWorkFooter() {
|
||||
Launcher launcher = Launcher.getLauncher(getContext());
|
||||
return Utilities.ATLEAST_P && (hasShortcutsPermission(launcher)
|
||||
|| launcher.checkSelfPermission("android.permission.MODIFY_QUIET_MODE")
|
||||
== PackageManager.PERMISSION_GRANTED);
|
||||
}
|
||||
}
|
||||
@@ -20,6 +20,7 @@ import android.appwidget.AppWidgetProviderInfo;
|
||||
import android.content.Context;
|
||||
import android.content.res.Configuration;
|
||||
import android.graphics.PointF;
|
||||
import android.graphics.Rect;
|
||||
import android.os.Handler;
|
||||
import android.os.SystemClock;
|
||||
import android.util.SparseBooleanArray;
|
||||
@@ -44,6 +45,7 @@ import com.android.launcher3.SimpleOnStylusPressListener;
|
||||
import com.android.launcher3.StylusEventHelper;
|
||||
import com.android.launcher3.Utilities;
|
||||
import com.android.launcher3.dragndrop.DragLayer;
|
||||
import com.android.launcher3.dragndrop.DraggableView;
|
||||
import com.android.launcher3.util.Executors;
|
||||
import com.android.launcher3.util.Themes;
|
||||
import com.android.launcher3.views.BaseDragLayer.TouchCompleteListener;
|
||||
@@ -52,7 +54,7 @@ import com.android.launcher3.views.BaseDragLayer.TouchCompleteListener;
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
public class LauncherAppWidgetHostView extends NavigableAppWidgetHostView
|
||||
implements TouchCompleteListener, View.OnLongClickListener {
|
||||
implements TouchCompleteListener, View.OnLongClickListener, DraggableView {
|
||||
|
||||
// Related to the auto-advancing of widgets
|
||||
private static final long ADVANCE_INTERVAL = 20000;
|
||||
@@ -412,4 +414,18 @@ public class LauncherAppWidgetHostView extends NavigableAppWidgetHostView
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getViewType() {
|
||||
return DRAGGABLE_WIDGET;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void getVisualDragBounds(Rect bounds) {
|
||||
int x = (int) (1 - getScaleToFit()) * getMeasuredWidth() / 2;
|
||||
int y = (int) (1 - getScaleToFit()) * getMeasuredWidth() / 2;
|
||||
int width = (int) getScaleToFit() * getMeasuredWidth();
|
||||
int height = (int) getScaleToFit() * getMeasuredHeight();
|
||||
bounds.set(x, y , x + width, y + height);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -24,12 +24,15 @@ import android.view.View;
|
||||
import android.view.ViewDebug;
|
||||
import android.view.ViewGroup;
|
||||
|
||||
import com.android.launcher3.dragndrop.DraggableView;
|
||||
|
||||
import java.util.ArrayList;
|
||||
|
||||
/**
|
||||
* Extension of AppWidgetHostView with support for controlled keyboard navigation.
|
||||
*/
|
||||
public abstract class NavigableAppWidgetHostView extends AppWidgetHostView {
|
||||
public abstract class NavigableAppWidgetHostView extends AppWidgetHostView
|
||||
implements DraggableView {
|
||||
|
||||
@ViewDebug.ExportedProperty(category = "launcher")
|
||||
private boolean mChildrenFocused;
|
||||
@@ -133,4 +136,14 @@ public abstract class NavigableAppWidgetHostView extends AppWidgetHostView {
|
||||
// The host view's background changes when selected, to indicate the focus is inside.
|
||||
setSelected(childIsFocused);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getViewType() {
|
||||
return DRAGGABLE_WIDGET;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void getVisualDragBounds(Rect bounds) {
|
||||
bounds.set(0, 0 , getMeasuredWidth(), getMeasuredHeight());
|
||||
}
|
||||
}
|
||||
|
||||
@@ -32,6 +32,7 @@ import com.android.launcher3.LauncherAppState;
|
||||
import com.android.launcher3.PendingAddItemInfo;
|
||||
import com.android.launcher3.R;
|
||||
import com.android.launcher3.dragndrop.DragOptions;
|
||||
import com.android.launcher3.dragndrop.DraggableView;
|
||||
import com.android.launcher3.dragndrop.LivePreviewWidgetCell;
|
||||
import com.android.launcher3.graphics.DragPreviewProvider;
|
||||
import com.android.launcher3.icons.LauncherIcons;
|
||||
@@ -79,6 +80,8 @@ public class PendingItemDragHelper extends DragPreviewProvider {
|
||||
|
||||
mEstimatedCellSize = launcher.getWorkspace().estimateItemSize(mAddInfo);
|
||||
|
||||
DraggableView draggableView;
|
||||
|
||||
if (mAddInfo instanceof PendingAddWidgetInfo) {
|
||||
PendingAddWidgetInfo createWidgetInfo = (PendingAddWidgetInfo) mAddInfo;
|
||||
|
||||
@@ -110,6 +113,7 @@ public class PendingItemDragHelper extends DragPreviewProvider {
|
||||
|
||||
dragOffset = null;
|
||||
dragRegion = null;
|
||||
draggableView = DraggableView.ofType(DraggableView.DRAGGABLE_WIDGET);
|
||||
} else {
|
||||
PendingAddShortcutInfo createShortcutInfo = (PendingAddShortcutInfo) mAddInfo;
|
||||
Drawable icon = createShortcutInfo.activityInfo.getFullResIcon(app.getIconCache());
|
||||
@@ -136,6 +140,7 @@ public class PendingItemDragHelper extends DragPreviewProvider {
|
||||
dragRegion.top = (mEstimatedCellSize[1]
|
||||
- iconSize - dp.iconTextSizePx - dp.iconDrawablePaddingPx) / 2;
|
||||
dragRegion.bottom = dragRegion.top + iconSize;
|
||||
draggableView = DraggableView.ofType(DraggableView.DRAGGABLE_ICON);
|
||||
}
|
||||
|
||||
// Since we are not going through the workspace for starting the drag, set drag related
|
||||
@@ -148,8 +153,8 @@ public class PendingItemDragHelper extends DragPreviewProvider {
|
||||
+ (int) ((scale * preview.getHeight() - preview.getHeight()) / 2);
|
||||
|
||||
// Start the drag
|
||||
launcher.getDragController().startDrag(preview, dragLayerX, dragLayerY, source, mAddInfo,
|
||||
dragOffset, dragRegion, scale, scale, options);
|
||||
launcher.getDragController().startDrag(preview, draggableView, dragLayerX, dragLayerY,
|
||||
source, mAddInfo, dragOffset, dragRegion, scale, scale, options);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
+9
-13
@@ -17,7 +17,7 @@
|
||||
package com.android.launcher3.uioverrides;
|
||||
|
||||
|
||||
import android.util.IntProperty;
|
||||
import android.util.FloatProperty;
|
||||
import android.view.View;
|
||||
|
||||
import com.android.launcher3.Launcher;
|
||||
@@ -27,26 +27,22 @@ import com.android.launcher3.anim.PendingAnimation;
|
||||
import com.android.launcher3.states.StateAnimationConfig;
|
||||
|
||||
/**
|
||||
* Controls the blur, for the Launcher surface only.
|
||||
* Controls blur and wallpaper zoom, for the Launcher surface only.
|
||||
*/
|
||||
public class BackgroundBlurController implements LauncherStateManager.StateHandler {
|
||||
public class DepthController implements LauncherStateManager.StateHandler {
|
||||
|
||||
public static final IntProperty<BackgroundBlurController> BACKGROUND_BLUR =
|
||||
new IntProperty<BackgroundBlurController>("backgroundBlur") {
|
||||
public static final FloatProperty<DepthController> DEPTH =
|
||||
new FloatProperty<DepthController>("depth") {
|
||||
@Override
|
||||
public void setValue(BackgroundBlurController blurController, int blurRadius) {}
|
||||
public void setValue(DepthController depthController, float depth) {}
|
||||
|
||||
@Override
|
||||
public Integer get(BackgroundBlurController blurController) {
|
||||
return 0;
|
||||
public Float get(DepthController depthController) {
|
||||
return 0f;
|
||||
}
|
||||
};
|
||||
|
||||
public BackgroundBlurController(Launcher l) {}
|
||||
|
||||
public int getFolderBackgroundBlurAdjustment() {
|
||||
return 0;
|
||||
}
|
||||
public DepthController(Launcher l) {}
|
||||
|
||||
public void setSurfaceToLauncher(View v) {}
|
||||
|
||||
@@ -18,7 +18,6 @@ package com.android.launcher3.ui;
|
||||
import static androidx.test.InstrumentationRegistry.getInstrumentation;
|
||||
|
||||
import static com.android.launcher3.WorkspaceLayoutManager.FIRST_SCREEN_ID;
|
||||
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;
|
||||
|
||||
@@ -57,6 +56,7 @@ import com.android.launcher3.LauncherStateManager;
|
||||
import com.android.launcher3.Utilities;
|
||||
import com.android.launcher3.model.AppLaunchTracker;
|
||||
import com.android.launcher3.tapl.LauncherInstrumentation;
|
||||
import com.android.launcher3.tapl.LauncherInstrumentation.ContainerType;
|
||||
import com.android.launcher3.tapl.TestHelpers;
|
||||
import com.android.launcher3.testcomponent.TestCommandReceiver;
|
||||
import com.android.launcher3.testing.TestProtocol;
|
||||
@@ -102,6 +102,7 @@ public abstract class AbstractLauncherUiTest {
|
||||
|
||||
private static String sDetectedActivityLeak;
|
||||
private static boolean sActivityLeakReported;
|
||||
private static final String SYSTEMUI_PACKAGE = "com.android.systemui";
|
||||
|
||||
protected LooperExecutor mMainThreadExecutor = MAIN_EXECUTOR;
|
||||
protected final UiDevice mDevice = UiDevice.getInstance(getInstrumentation());
|
||||
@@ -213,8 +214,26 @@ public abstract class AbstractLauncherUiTest {
|
||||
return mDevice;
|
||||
}
|
||||
|
||||
private boolean hasSystemUiObject(String resId) {
|
||||
return mDevice.hasObject(By.res(SYSTEMUI_PACKAGE, resId));
|
||||
}
|
||||
|
||||
@Before
|
||||
public void setUp() throws Exception {
|
||||
Log.d(TAG, "Before disabling battery defender");
|
||||
mDevice.executeShellCommand("setprop vendor.battery.defender.disable 1");
|
||||
Log.d(TAG, "Before enabling stay awake");
|
||||
mDevice.executeShellCommand("settings put global stay_on_while_plugged_in 3");
|
||||
for (int i = 0; i < 10 && hasSystemUiObject("keyguard_status_view"); ++i) {
|
||||
Log.d(TAG, "Before unlocking the phone");
|
||||
mDevice.executeShellCommand("input keyevent 82");
|
||||
mDevice.waitForIdle();
|
||||
}
|
||||
Assert.assertTrue("Keyguard still visible",
|
||||
mDevice.wait(
|
||||
Until.gone(By.res(SYSTEMUI_PACKAGE, "keyguard_status_view")), 60000));
|
||||
Log.d(TAG, "Keyguard is not visible");
|
||||
|
||||
final String launcherPackageName = mDevice.getLauncherPackageName();
|
||||
try {
|
||||
final Context context = InstrumentationRegistry.getContext();
|
||||
@@ -275,7 +294,8 @@ public abstract class AbstractLauncherUiTest {
|
||||
protected void resetLoaderState() {
|
||||
try {
|
||||
mMainThreadExecutor.execute(
|
||||
() -> LauncherAppState.getInstance(mTargetContext).getModel().forceReload());
|
||||
() -> LauncherAppState.getInstance(
|
||||
mTargetContext).getModel().forceReload());
|
||||
} catch (Throwable t) {
|
||||
throw new IllegalArgumentException(t);
|
||||
}
|
||||
@@ -289,7 +309,8 @@ public abstract class AbstractLauncherUiTest {
|
||||
ContentResolver resolver = mTargetContext.getContentResolver();
|
||||
int screenId = FIRST_SCREEN_ID;
|
||||
// Update the screen id counter for the provider.
|
||||
LauncherSettings.Settings.call(resolver, LauncherSettings.Settings.METHOD_NEW_SCREEN_ID);
|
||||
LauncherSettings.Settings.call(resolver,
|
||||
LauncherSettings.Settings.METHOD_NEW_SCREEN_ID);
|
||||
|
||||
if (screenId > FIRST_SCREEN_ID) {
|
||||
screenId = FIRST_SCREEN_ID;
|
||||
@@ -303,7 +324,8 @@ public abstract class AbstractLauncherUiTest {
|
||||
item.screenId = screenId;
|
||||
item.onAddToDatabase(writer);
|
||||
writer.put(LauncherSettings.Favorites._ID, item.id);
|
||||
resolver.insert(LauncherSettings.Favorites.CONTENT_URI, writer.getValues(mTargetContext));
|
||||
resolver.insert(LauncherSettings.Favorites.CONTENT_URI,
|
||||
writer.getValues(mTargetContext));
|
||||
resetLoaderState();
|
||||
|
||||
// Launch the home activity
|
||||
@@ -334,7 +356,8 @@ public abstract class AbstractLauncherUiTest {
|
||||
});
|
||||
}
|
||||
|
||||
// Cannot be used in TaplTests between a Tapl call injecting a gesture and a tapl call expecting
|
||||
// Cannot be used in TaplTests between a Tapl call injecting a gesture and a tapl call
|
||||
// expecting
|
||||
// the results of that gesture because the wait can hide flakeness.
|
||||
protected void waitForState(String message, Supplier<LauncherState> state) {
|
||||
waitForLauncherCondition(message,
|
||||
@@ -347,7 +370,8 @@ public abstract class AbstractLauncherUiTest {
|
||||
|
||||
// Cannot be used in TaplTests after injecting any gesture using Tapl because this can hide
|
||||
// flakiness.
|
||||
protected void waitForLauncherCondition(String message, Function<Launcher, Boolean> condition) {
|
||||
protected void waitForLauncherCondition(String
|
||||
message, Function<Launcher, Boolean> condition) {
|
||||
waitForLauncherCondition(message, condition, DEFAULT_ACTIVITY_TIMEOUT);
|
||||
}
|
||||
|
||||
@@ -423,7 +447,8 @@ public abstract class AbstractLauncherUiTest {
|
||||
|
||||
public Intent blockingGetExtraIntent() throws InterruptedException {
|
||||
Intent intent = blockingGetIntent();
|
||||
return intent == null ? null : (Intent) intent.getParcelableExtra(Intent.EXTRA_INTENT);
|
||||
return intent == null ? null : (Intent) intent.getParcelableExtra(
|
||||
Intent.EXTRA_INTENT);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -450,7 +475,8 @@ public abstract class AbstractLauncherUiTest {
|
||||
if (newTask) {
|
||||
intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_CLEAR_TASK);
|
||||
} else {
|
||||
intent.addFlags(Intent.FLAG_ACTIVITY_MULTIPLE_TASK | Intent.FLAG_ACTIVITY_NEW_DOCUMENT);
|
||||
intent.addFlags(
|
||||
Intent.FLAG_ACTIVITY_MULTIPLE_TASK | Intent.FLAG_ACTIVITY_NEW_DOCUMENT);
|
||||
}
|
||||
getInstrumentation().getTargetContext().startActivity(intent);
|
||||
assertTrue("App didn't start: " + selector,
|
||||
@@ -487,7 +513,8 @@ public abstract class AbstractLauncherUiTest {
|
||||
|
||||
protected boolean isInState(Supplier<LauncherState> state) {
|
||||
if (!TestHelpers.isInLauncherProcess()) return true;
|
||||
return getFromLauncher(launcher -> launcher.getStateManager().getState() == state.get());
|
||||
return getFromLauncher(
|
||||
launcher -> launcher.getStateManager().getState() == state.get());
|
||||
}
|
||||
|
||||
protected int getAllAppsScroll(Launcher launcher) {
|
||||
|
||||
@@ -16,6 +16,7 @@
|
||||
package com.android.launcher3.ui;
|
||||
|
||||
import static com.android.launcher3.LauncherState.ALL_APPS;
|
||||
import static com.android.launcher3.LauncherState.NORMAL;
|
||||
|
||||
import static org.junit.Assert.assertEquals;
|
||||
import static org.junit.Assert.assertTrue;
|
||||
@@ -31,9 +32,9 @@ import androidx.test.runner.AndroidJUnit4;
|
||||
import com.android.launcher3.R;
|
||||
import com.android.launcher3.allapps.AllAppsContainerView;
|
||||
import com.android.launcher3.allapps.AllAppsPagedView;
|
||||
import com.android.launcher3.allapps.WorkModeSwitch;
|
||||
import com.android.launcher3.dragndrop.DragLayer;
|
||||
import com.android.launcher3.views.WorkEduView;
|
||||
import com.android.launcher3.views.WorkFooterContainer;
|
||||
|
||||
import org.junit.After;
|
||||
import org.junit.Before;
|
||||
@@ -87,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(), 60000);
|
||||
launcher -> launcher.getAppsView().getWorkModeSwitch(), 60000);
|
||||
|
||||
UserManager userManager = getFromLauncher(l -> l.getSystemService(UserManager.class));
|
||||
assertEquals(2, userManager.getUserProfiles().size());
|
||||
@@ -102,10 +103,10 @@ public class WorkTabTest extends AbstractLauncherUiTest {
|
||||
|
||||
assertTrue(userManager.isQuietModeEnabled(workProfile));
|
||||
executeOnLauncher(launcher -> {
|
||||
WorkFooterContainer wf = launcher.getAppsView().getWorkFooterContainer();
|
||||
WorkModeSwitch wf = launcher.getAppsView().getWorkModeSwitch();
|
||||
((AllAppsPagedView) launcher.getAppsView().getContentView()).snapToPageImmediately(
|
||||
AllAppsContainerView.AdapterHolder.WORK);
|
||||
wf.getWorkModeSwitch().toggle();
|
||||
wf.toggle();
|
||||
});
|
||||
waitForLauncherCondition("Work toggle did not work",
|
||||
l -> l.getSystemService(UserManager.class).isQuietModeEnabled(workProfile));
|
||||
@@ -158,9 +159,11 @@ public class WorkTabTest extends AbstractLauncherUiTest {
|
||||
|
||||
// dismiss personal edu
|
||||
mDevice.pressHome();
|
||||
waitForState("Launcher did not go home", () -> NORMAL);
|
||||
|
||||
// open work tab
|
||||
executeOnLauncher(launcher -> launcher.getStateManager().goToState(ALL_APPS));
|
||||
waitForState("Launcher did not switch to all apps", () -> ALL_APPS);
|
||||
executeOnLauncher(launcher -> {
|
||||
AllAppsPagedView pagedView = (AllAppsPagedView) launcher.getAppsView().getContentView();
|
||||
pagedView.setCurrentPage(WORK_PAGE);
|
||||
|
||||
Reference in New Issue
Block a user