summaryrefslogtreecommitdiff
path: root/libs
diff options
context:
space:
mode:
Diffstat (limited to 'libs')
-rw-r--r--libs/WindowManager/Shell/res/drawable/bubble_dismiss_circle.xml28
-rw-r--r--libs/WindowManager/Shell/res/drawable/bubble_dismiss_icon.xml26
-rw-r--r--libs/WindowManager/Shell/res/drawable/dismiss_circle_background.xml7
-rw-r--r--libs/WindowManager/Shell/res/drawable/pip_ic_close_white.xml2
-rw-r--r--libs/WindowManager/Shell/res/layout/bubble_dismiss_target.xml49
-rw-r--r--libs/WindowManager/Shell/res/layout/bubble_overflow_view.xml4
-rw-r--r--libs/WindowManager/Shell/res/values-as/strings.xml6
-rw-r--r--libs/WindowManager/Shell/res/values-bn/strings.xml6
-rw-r--r--libs/WindowManager/Shell/res/values-gu/strings.xml6
-rw-r--r--libs/WindowManager/Shell/res/values-mr/strings.xml6
-rw-r--r--libs/WindowManager/Shell/res/values-pt-rBR/strings.xml4
-rw-r--r--libs/WindowManager/Shell/res/values-pt/strings.xml4
-rw-r--r--libs/WindowManager/Shell/res/values/dimen.xml24
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/ShellTaskOrganizer.java13
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BadgedImageView.java21
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleController.java1
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleData.java30
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleExpandedView.java7
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleFlyoutView.java4
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleIconFactory.java5
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleOverflow.kt22
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubblePositioner.java58
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleStackView.java222
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/DismissView.kt2
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/StackEducationView.kt2
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/animation/ExpandedAnimationController.java17
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/animation/StackAnimationController.java35
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipTaskOrganizer.java4
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipDismissTargetHandler.java36
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipMotionHelper.java15
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipTouchHandler.java4
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/startingsurface/SplashscreenContentDrawer.java32
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/startingsurface/StartingSurfaceDrawer.java7
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/startingsurface/TaskSnapshotWindow.java61
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/tasksurfacehelper/TaskSurfaceHelper.java26
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/tasksurfacehelper/TaskSurfaceHelperController.java60
-rw-r--r--libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/bubbles/BubbleDataTest.java63
-rw-r--r--libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/bubbles/BubbleFlyoutViewTest.java1
-rw-r--r--libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/bubbles/TestableBubblePositioner.java13
-rw-r--r--libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/bubbles/animation/StackAnimationControllerTest.java6
-rw-r--r--libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/tasksurfacehelper/TaskSurfaceHelperControllerTest.java58
-rwxr-xr-xlibs/androidfw/ApkAssets.cpp4
-rw-r--r--libs/androidfw/Asset.cpp7
-rw-r--r--libs/androidfw/AssetsProvider.cpp16
-rw-r--r--libs/androidfw/TEST_MAPPING2
-rw-r--r--libs/androidfw/include/androidfw/Asset.h6
-rw-r--r--libs/androidfw/include/androidfw/AssetsProvider.h9
-rw-r--r--libs/androidfw/include/androidfw/LoadedArsc.h4
-rw-r--r--libs/hwui/AutoBackendTextureRelease.cpp22
-rw-r--r--libs/hwui/AutoBackendTextureRelease.h2
-rw-r--r--libs/hwui/DeferredLayerUpdater.cpp50
-rw-r--r--libs/hwui/DeferredLayerUpdater.h17
-rw-r--r--libs/hwui/jni/fonts/Font.cpp20
-rw-r--r--libs/hwui/renderthread/CanvasContext.cpp1
-rw-r--r--libs/hwui/renderthread/CanvasContext.h2
-rw-r--r--libs/usb/Android.bp1
56 files changed, 774 insertions, 386 deletions
diff --git a/libs/WindowManager/Shell/res/drawable/bubble_dismiss_circle.xml b/libs/WindowManager/Shell/res/drawable/bubble_dismiss_circle.xml
deleted file mode 100644
index 2104be48d1d9..000000000000
--- a/libs/WindowManager/Shell/res/drawable/bubble_dismiss_circle.xml
+++ /dev/null
@@ -1,28 +0,0 @@
-<!--
- ~ Copyright (C) 2020 The Android Open Source Project
- ~
- ~ Licensed under the Apache License, Version 2.0 (the "License");
- ~ you may not use this file except in compliance with the License.
- ~ You may obtain a copy of the License at
- ~
- ~ http://www.apache.org/licenses/LICENSE-2.0
- ~
- ~ Unless required by applicable law or agreed to in writing, software
- ~ distributed under the License is distributed on an "AS IS" BASIS,
- ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- ~ See the License for the specific language governing permissions and
- ~ limitations under the License.
- -->
-<!--
- The transparent circle outline that encircles the bubbles when they're in the dismiss target.
--->
-<shape
- xmlns:android="http://schemas.android.com/apk/res/android"
- android:shape="oval">
-
- <stroke
- android:width="1dp"
- android:color="#66FFFFFF" />
-
- <solid android:color="#B3000000" />
-</shape> \ No newline at end of file
diff --git a/libs/WindowManager/Shell/res/drawable/bubble_dismiss_icon.xml b/libs/WindowManager/Shell/res/drawable/bubble_dismiss_icon.xml
deleted file mode 100644
index ff8feded11ab..000000000000
--- a/libs/WindowManager/Shell/res/drawable/bubble_dismiss_icon.xml
+++ /dev/null
@@ -1,26 +0,0 @@
-<!--
- ~ Copyright (C) 2020 The Android Open Source Project
- ~
- ~ Licensed under the Apache License, Version 2.0 (the "License");
- ~ you may not use this file except in compliance with the License.
- ~ You may obtain a copy of the License at
- ~
- ~ http://www.apache.org/licenses/LICENSE-2.0
- ~
- ~ Unless required by applicable law or agreed to in writing, software
- ~ distributed under the License is distributed on an "AS IS" BASIS,
- ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- ~ See the License for the specific language governing permissions and
- ~ limitations under the License.
- -->
-<!-- The 'X' bubble dismiss icon. This is just ic_close with a stroke. -->
-<vector xmlns:android="http://schemas.android.com/apk/res/android"
- android:width="24.0dp"
- android:height="24.0dp"
- android:viewportWidth="24.0"
- android:viewportHeight="24.0">
- <path
- android:pathData="M19.000000,6.400000l-1.400000,-1.400000 -5.600000,5.600000 -5.600000,-5.600000 -1.400000,1.400000 5.600000,5.600000 -5.600000,5.600000 1.400000,1.400000 5.600000,-5.600000 5.600000,5.600000 1.400000,-1.400000 -5.600000,-5.600000z"
- android:fillColor="#FFFFFFFF"
- android:strokeColor="#FF000000"/>
-</vector>
diff --git a/libs/WindowManager/Shell/res/drawable/dismiss_circle_background.xml b/libs/WindowManager/Shell/res/drawable/dismiss_circle_background.xml
index 7809c8398c2d..f7fda362d76c 100644
--- a/libs/WindowManager/Shell/res/drawable/dismiss_circle_background.xml
+++ b/libs/WindowManager/Shell/res/drawable/dismiss_circle_background.xml
@@ -20,9 +20,8 @@
android:shape="oval">
<stroke
- android:width="1dp"
- android:color="#AAFFFFFF" />
-
- <solid android:color="#77000000" />
+ android:width="2dp"
+ android:color="@android:color/system_accent1_600" />
+ <solid android:color="@android:color/system_accent1_600" />
</shape> \ No newline at end of file
diff --git a/libs/WindowManager/Shell/res/drawable/pip_ic_close_white.xml b/libs/WindowManager/Shell/res/drawable/pip_ic_close_white.xml
index 60456267afef..62285e62fbf5 100644
--- a/libs/WindowManager/Shell/res/drawable/pip_ic_close_white.xml
+++ b/libs/WindowManager/Shell/res/drawable/pip_ic_close_white.xml
@@ -21,5 +21,5 @@
android:viewportHeight="24.0">
<path
android:pathData="M19.000000,6.400000l-1.400000,-1.400000 -5.600000,5.600000 -5.600000,-5.600000 -1.400000,1.400000 5.600000,5.600000 -5.600000,5.600000 1.400000,1.400000 5.600000,-5.600000 5.600000,5.600000 1.400000,-1.400000 -5.600000,-5.600000z"
- android:fillColor="#FFFFFFFF"/>
+ android:fillColor="@android:color/system_neutral1_50"/>
</vector>
diff --git a/libs/WindowManager/Shell/res/layout/bubble_dismiss_target.xml b/libs/WindowManager/Shell/res/layout/bubble_dismiss_target.xml
deleted file mode 100644
index f5cd727a6d03..000000000000
--- a/libs/WindowManager/Shell/res/layout/bubble_dismiss_target.xml
+++ /dev/null
@@ -1,49 +0,0 @@
-<!--
- ~ Copyright (C) 2019 The Android Open Source Project
- ~
- ~ Licensed under the Apache License, Version 2.0 (the "License");
- ~ you may not use this file except in compliance with the License.
- ~ You may obtain a copy of the License at
- ~
- ~ http://www.apache.org/licenses/LICENSE-2.0
- ~
- ~ Unless required by applicable law or agreed to in writing, software
- ~ distributed under the License is distributed on an "AS IS" BASIS,
- ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- ~ See the License for the specific language governing permissions and
- ~ limitations under the License
- -->
-<!-- Bubble dismiss target consisting of an X icon and the text 'Dismiss'. -->
-<FrameLayout
- xmlns:android="http://schemas.android.com/apk/res/android"
- android:layout_width="wrap_content"
- android:layout_height="@dimen/floating_dismiss_gradient_height"
- android:layout_gravity="bottom|center_horizontal">
-
- <FrameLayout
- android:id="@+id/bubble_dismiss_circle"
- android:layout_width="@dimen/bubble_dismiss_encircle_size"
- android:layout_height="@dimen/bubble_dismiss_encircle_size"
- android:layout_gravity="center"
- android:background="@drawable/bubble_dismiss_circle" />
-
- <LinearLayout
- android:id="@+id/bubble_dismiss_icon_container"
- android:layout_width="wrap_content"
- android:layout_height="match_parent"
- android:gravity="center"
- android:paddingBottom="@dimen/bubble_dismiss_target_padding_y"
- android:paddingTop="@dimen/bubble_dismiss_target_padding_y"
- android:paddingLeft="@dimen/bubble_dismiss_target_padding_x"
- android:paddingRight="@dimen/bubble_dismiss_target_padding_x"
- android:clipChildren="false"
- android:clipToPadding="false"
- android:orientation="horizontal">
-
- <ImageView
- android:id="@+id/bubble_dismiss_close_icon"
- android:layout_width="24dp"
- android:layout_height="24dp"
- android:src="@drawable/bubble_dismiss_icon" />
- </LinearLayout>
-</FrameLayout> \ No newline at end of file
diff --git a/libs/WindowManager/Shell/res/layout/bubble_overflow_view.xml b/libs/WindowManager/Shell/res/layout/bubble_overflow_view.xml
index d07107c8f0a0..544b731bb550 100644
--- a/libs/WindowManager/Shell/res/layout/bubble_overflow_view.xml
+++ b/libs/WindowManager/Shell/res/layout/bubble_overflow_view.xml
@@ -25,8 +25,8 @@
xmlns:android="http://schemas.android.com/apk/res/android"
android:id="@+id/bubble_view"
android:layout_gravity="center"
- android:layout_width="@dimen/individual_bubble_size"
- android:layout_height="@dimen/individual_bubble_size"/>
+ android:layout_width="@dimen/bubble_size"
+ android:layout_height="@dimen/bubble_size"/>
<TextView
android:id="@+id/bubble_view_name"
diff --git a/libs/WindowManager/Shell/res/values-as/strings.xml b/libs/WindowManager/Shell/res/values-as/strings.xml
index 22388bef0355..1ace3cdacb46 100644
--- a/libs/WindowManager/Shell/res/values-as/strings.xml
+++ b/libs/WindowManager/Shell/res/values-as/strings.xml
@@ -28,10 +28,8 @@
<string name="pip_skip_to_next" msgid="8403429188794867653">"পৰৱৰ্তী মিডিয়ালৈ যাওক"</string>
<string name="pip_skip_to_prev" msgid="7172158111196394092">"আগৰটো মিডিয়ালৈ যাওক"</string>
<string name="accessibility_action_pip_resize" msgid="4623966104749543182">"আকাৰ সলনি কৰক"</string>
- <!-- no translation found for accessibility_action_pip_stash (4060775037619702641) -->
- <skip />
- <!-- no translation found for accessibility_action_pip_unstash (7467499339610437646) -->
- <skip />
+ <string name="accessibility_action_pip_stash" msgid="4060775037619702641">"লুকুৱাওক"</string>
+ <string name="accessibility_action_pip_unstash" msgid="7467499339610437646">"দেখুৱাওক"</string>
<string name="dock_forced_resizable" msgid="1749750436092293116">"এপ্‌টোৱে বিভাজিত স্ক্ৰীনৰ সৈতে কাম নকৰিব পাৰে।"</string>
<string name="dock_non_resizeble_failed_to_dock_text" msgid="7408396418008948957">"এপটোৱে বিভাজিত স্ক্ৰীণ সমৰ্থন নকৰে।"</string>
<string name="forced_resizable_secondary_display" msgid="1768046938673582671">"গৌণ ডিছপ্লেত এপে সঠিকভাৱে কাম নকৰিব পাৰে।"</string>
diff --git a/libs/WindowManager/Shell/res/values-bn/strings.xml b/libs/WindowManager/Shell/res/values-bn/strings.xml
index 2895951b0c2f..b35e17919cc2 100644
--- a/libs/WindowManager/Shell/res/values-bn/strings.xml
+++ b/libs/WindowManager/Shell/res/values-bn/strings.xml
@@ -28,10 +28,8 @@
<string name="pip_skip_to_next" msgid="8403429188794867653">"এগিয়ে যাওয়ার জন্য এড়িয়ে যান"</string>
<string name="pip_skip_to_prev" msgid="7172158111196394092">"পিছনে যাওয়ার জন্য এড়িয়ে যান"</string>
<string name="accessibility_action_pip_resize" msgid="4623966104749543182">"রিসাইজ করুন"</string>
- <!-- no translation found for accessibility_action_pip_stash (4060775037619702641) -->
- <skip />
- <!-- no translation found for accessibility_action_pip_unstash (7467499339610437646) -->
- <skip />
+ <string name="accessibility_action_pip_stash" msgid="4060775037619702641">"স্ট্যাস করুন"</string>
+ <string name="accessibility_action_pip_unstash" msgid="7467499339610437646">"আনস্ট্যাস করুন"</string>
<string name="dock_forced_resizable" msgid="1749750436092293116">"অ্যাপটি স্প্লিট স্ক্রিনে কাজ নাও করতে পারে।"</string>
<string name="dock_non_resizeble_failed_to_dock_text" msgid="7408396418008948957">"অ্যাপ্লিকেশান বিভক্ত-স্ক্রিন সমর্থন করে না৷"</string>
<string name="forced_resizable_secondary_display" msgid="1768046938673582671">"সেকেন্ডারি ডিসপ্লেতে অ্যাপটি কাজ নাও করতে পারে।"</string>
diff --git a/libs/WindowManager/Shell/res/values-gu/strings.xml b/libs/WindowManager/Shell/res/values-gu/strings.xml
index ce5c6c635b50..e0654bd6bfef 100644
--- a/libs/WindowManager/Shell/res/values-gu/strings.xml
+++ b/libs/WindowManager/Shell/res/values-gu/strings.xml
@@ -28,10 +28,8 @@
<string name="pip_skip_to_next" msgid="8403429188794867653">"આગલા પર જાઓ"</string>
<string name="pip_skip_to_prev" msgid="7172158111196394092">"પહેલાંના પર જાઓ"</string>
<string name="accessibility_action_pip_resize" msgid="4623966104749543182">"કદ બદલો"</string>
- <!-- no translation found for accessibility_action_pip_stash (4060775037619702641) -->
- <skip />
- <!-- no translation found for accessibility_action_pip_unstash (7467499339610437646) -->
- <skip />
+ <string name="accessibility_action_pip_stash" msgid="4060775037619702641">"છુપાવો"</string>
+ <string name="accessibility_action_pip_unstash" msgid="7467499339610437646">"બતાવો"</string>
<string name="dock_forced_resizable" msgid="1749750436092293116">"વિભાજિત-સ્ક્રીન સાથે ઍપ કદાચ કામ ન કરે."</string>
<string name="dock_non_resizeble_failed_to_dock_text" msgid="7408396418008948957">"ઍપ્લિકેશન સ્ક્રીન-વિભાજનનું સમર્થન કરતી નથી."</string>
<string name="forced_resizable_secondary_display" msgid="1768046938673582671">"ઍપ્લિકેશન ગૌણ ડિસ્પ્લે પર કદાચ કામ ન કરે."</string>
diff --git a/libs/WindowManager/Shell/res/values-mr/strings.xml b/libs/WindowManager/Shell/res/values-mr/strings.xml
index 4376d21111ff..a4b7be4d52c7 100644
--- a/libs/WindowManager/Shell/res/values-mr/strings.xml
+++ b/libs/WindowManager/Shell/res/values-mr/strings.xml
@@ -28,10 +28,8 @@
<string name="pip_skip_to_next" msgid="8403429188794867653">"डावलून पुढे जा"</string>
<string name="pip_skip_to_prev" msgid="7172158111196394092">"डावलून मागे जा"</string>
<string name="accessibility_action_pip_resize" msgid="4623966104749543182">"आकार बदला"</string>
- <!-- no translation found for accessibility_action_pip_stash (4060775037619702641) -->
- <skip />
- <!-- no translation found for accessibility_action_pip_unstash (7467499339610437646) -->
- <skip />
+ <string name="accessibility_action_pip_stash" msgid="4060775037619702641">"स्टॅश करा"</string>
+ <string name="accessibility_action_pip_unstash" msgid="7467499339610437646">"अनस्टॅश करा"</string>
<string name="dock_forced_resizable" msgid="1749750436092293116">"अ‍ॅप कदाचित स्प्लिट स्क्रीनसह काम करू शकत नाही."</string>
<string name="dock_non_resizeble_failed_to_dock_text" msgid="7408396418008948957">"अ‍ॅप स्क्रीन-विभाजनास समर्थन देत नाही."</string>
<string name="forced_resizable_secondary_display" msgid="1768046938673582671">"दुसऱ्या डिस्प्लेवर अ‍ॅप कदाचित चालणार नाही."</string>
diff --git a/libs/WindowManager/Shell/res/values-pt-rBR/strings.xml b/libs/WindowManager/Shell/res/values-pt-rBR/strings.xml
index 616e67e88eda..9c97ffe2d7e6 100644
--- a/libs/WindowManager/Shell/res/values-pt-rBR/strings.xml
+++ b/libs/WindowManager/Shell/res/values-pt-rBR/strings.xml
@@ -28,8 +28,8 @@
<string name="pip_skip_to_next" msgid="8403429188794867653">"Pular para a próxima"</string>
<string name="pip_skip_to_prev" msgid="7172158111196394092">"Pular para a anterior"</string>
<string name="accessibility_action_pip_resize" msgid="4623966104749543182">"Redimensionar"</string>
- <string name="accessibility_action_pip_stash" msgid="4060775037619702641">"Adicionar ao Depósito"</string>
- <string name="accessibility_action_pip_unstash" msgid="7467499339610437646">"Remover do Depósito"</string>
+ <string name="accessibility_action_pip_stash" msgid="4060775037619702641">"Ocultar"</string>
+ <string name="accessibility_action_pip_unstash" msgid="7467499339610437646">"Exibir"</string>
<string name="dock_forced_resizable" msgid="1749750436092293116">"É possível que o app não funcione com a tela dividida."</string>
<string name="dock_non_resizeble_failed_to_dock_text" msgid="7408396418008948957">"O app não é compatível com a divisão de tela."</string>
<string name="forced_resizable_secondary_display" msgid="1768046938673582671">"É possível que o app não funcione em uma tela secundária."</string>
diff --git a/libs/WindowManager/Shell/res/values-pt/strings.xml b/libs/WindowManager/Shell/res/values-pt/strings.xml
index 616e67e88eda..9c97ffe2d7e6 100644
--- a/libs/WindowManager/Shell/res/values-pt/strings.xml
+++ b/libs/WindowManager/Shell/res/values-pt/strings.xml
@@ -28,8 +28,8 @@
<string name="pip_skip_to_next" msgid="8403429188794867653">"Pular para a próxima"</string>
<string name="pip_skip_to_prev" msgid="7172158111196394092">"Pular para a anterior"</string>
<string name="accessibility_action_pip_resize" msgid="4623966104749543182">"Redimensionar"</string>
- <string name="accessibility_action_pip_stash" msgid="4060775037619702641">"Adicionar ao Depósito"</string>
- <string name="accessibility_action_pip_unstash" msgid="7467499339610437646">"Remover do Depósito"</string>
+ <string name="accessibility_action_pip_stash" msgid="4060775037619702641">"Ocultar"</string>
+ <string name="accessibility_action_pip_unstash" msgid="7467499339610437646">"Exibir"</string>
<string name="dock_forced_resizable" msgid="1749750436092293116">"É possível que o app não funcione com a tela dividida."</string>
<string name="dock_non_resizeble_failed_to_dock_text" msgid="7408396418008948957">"O app não é compatível com a divisão de tela."</string>
<string name="forced_resizable_secondary_display" msgid="1768046938673582671">"É possível que o app não funcione em uma tela secundária."</string>
diff --git a/libs/WindowManager/Shell/res/values/dimen.xml b/libs/WindowManager/Shell/res/values/dimen.xml
index e32d3251f788..dddf2c12ea2d 100644
--- a/libs/WindowManager/Shell/res/values/dimen.xml
+++ b/libs/WindowManager/Shell/res/values/dimen.xml
@@ -15,7 +15,8 @@
limitations under the License.
-->
<resources>
- <dimen name="dismiss_circle_size">52dp</dimen>
+ <dimen name="dismiss_circle_size">96dp</dimen>
+ <dimen name="dismiss_circle_small">60dp</dimen>
<!-- The height of the gradient indicating the dismiss edge when moving a PIP. -->
<dimen name="floating_dismiss_gradient_height">250dp</dimen>
@@ -101,12 +102,12 @@
<dimen name="bubble_flyout_avatar_message_space">6dp</dimen>
<!-- Padding between status bar and bubbles when displayed in expanded state -->
<dimen name="bubble_padding_top">16dp</dimen>
- <!-- Max amount of space between bubbles when expanded. -->
- <dimen name="bubble_max_spacing">8dp</dimen>
- <!-- Size of individual bubbles. -->
- <dimen name="individual_bubble_size">60dp</dimen>
- <!-- Size of bubble bitmap. -->
- <dimen name="bubble_bitmap_size">52dp</dimen>
+ <!-- Space between bubbles when expanded. -->
+ <dimen name="bubble_spacing">3dp</dimen>
+ <!-- Size of the bubble. -->
+ <dimen name="bubble_size">60dp</dimen>
+ <!-- Size of the badge shown on the bubble. -->
+ <dimen name="bubble_badge_size">24dp</dimen>
<!-- Extra padding added to the touchable rect for bubbles so they are easier to grab. -->
<dimen name="bubble_touch_padding">12dp</dimen>
<!-- Size of the circle around the bubbles when they're in the dismiss target. -->
@@ -120,6 +121,8 @@
<dimen name="bubble_expanded_default_height">180dp</dimen>
<!-- On large screens the width of the expanded view is restricted to this size. -->
<dimen name="bubble_expanded_view_tablet_width">412dp</dimen>
+ <!-- Inset to apply to the icon in the overflow button. -->
+ <dimen name="bubble_overflow_icon_inset">30dp</dimen>
<!-- Default (and minimum) height of bubble overflow -->
<dimen name="bubble_overflow_height">480dp</dimen>
<!-- Bubble overflow padding when there are no bubbles -->
@@ -154,7 +157,7 @@
<!-- Interior padding of the message bubble -->
<dimen name="bubble_message_padding">4dp</dimen>
<!-- Offset between bubbles in their stacked position. -->
- <dimen name="bubble_stack_offset">10dp</dimen>
+ <dimen name="bubble_stack_offset">12dp</dimen>
<!-- Offset between stack y and animation y for bubble swap. -->
<dimen name="bubble_swap_animation_offset">15dp</dimen>
<!-- How far offscreen the bubble stack rests. There's some padding around the bubble so
@@ -178,11 +181,6 @@
individual_bubble_size + some padding. -->
<dimen name="bubble_stack_user_education_side_inset">72dp</dimen>
- <!-- The width/height of the icon view on staring surface. -->
- <dimen name="starting_surface_icon_size">160dp</dimen>
- <!-- The default width/height of the icon on the spec of adaptive icon drawable. -->
- <dimen name="default_icon_size">108dp</dimen>
-
<!-- The width/height of the size compat restart button. -->
<dimen name="size_compat_button_size">48dp</dimen>
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/ShellTaskOrganizer.java b/libs/WindowManager/Shell/src/com/android/wm/shell/ShellTaskOrganizer.java
index 94d13eab4299..a88be3198502 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/ShellTaskOrganizer.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/ShellTaskOrganizer.java
@@ -399,6 +399,19 @@ public class ShellTaskOrganizer extends TaskOrganizer {
}
}
+ /** Helper to set int metadata on the Surface corresponding to the task id. */
+ public void setSurfaceMetadata(int taskId, int key, int value) {
+ synchronized (mLock) {
+ final TaskAppearedInfo info = mTasks.get(taskId);
+ if (info == null || info.getLeash() == null) {
+ return;
+ }
+ SurfaceControl.Transaction t = new SurfaceControl.Transaction();
+ t.setMetadata(info.getLeash(), key, value);
+ t.apply();
+ }
+ }
+
private boolean updateTaskListenerIfNeeded(RunningTaskInfo taskInfo, SurfaceControl leash,
TaskListener oldListener, TaskListener newListener) {
if (oldListener == newListener) return false;
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BadgedImageView.java b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BadgedImageView.java
index 55dfc12966a9..d92e2ccc77bd 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BadgedImageView.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BadgedImageView.java
@@ -23,15 +23,19 @@ import android.annotation.Nullable;
import android.content.Context;
import android.graphics.Bitmap;
import android.graphics.Canvas;
+import android.graphics.Outline;
import android.graphics.Paint;
import android.graphics.PaintFlagsDrawFilter;
import android.graphics.Path;
import android.graphics.Rect;
import android.util.AttributeSet;
import android.util.PathParser;
+import android.view.View;
+import android.view.ViewOutlineProvider;
import android.widget.ImageView;
import com.android.launcher3.icons.DotRenderer;
+import com.android.launcher3.icons.IconNormalizer;
import com.android.wm.shell.animation.Interpolators;
import java.util.EnumSet;
@@ -104,6 +108,19 @@ public class BadgedImageView extends ImageView {
setFocusable(true);
setClickable(true);
+ setOutlineProvider(new ViewOutlineProvider() {
+ @Override
+ public void getOutline(View view, Outline outline) {
+ BadgedImageView.this.getOutline(outline);
+ }
+ });
+ }
+
+ private void getOutline(Outline outline) {
+ final int bubbleSize = mPositioner.getBubbleSize();
+ final int normalizedSize = IconNormalizer.getNormalizedCircleSize(bubbleSize);
+ final int inset = (bubbleSize - normalizedSize) / 2;
+ outline.setOval(inset, inset, inset + normalizedSize, inset + normalizedSize);
}
public void initialize(BubblePositioner positioner) {
@@ -111,7 +128,7 @@ public class BadgedImageView extends ImageView {
Path iconPath = PathParser.createPathFromPathData(
getResources().getString(com.android.internal.R.string.config_icon_mask));
- mDotRenderer = new DotRenderer(mPositioner.getBubbleBitmapSize(),
+ mDotRenderer = new DotRenderer(mPositioner.getBubbleSize(),
iconPath, DEFAULT_PATH_SIZE);
}
@@ -192,7 +209,7 @@ public class BadgedImageView extends ImageView {
* @param iconPath The new icon path to use when calculating dot position.
*/
void drawDot(Path iconPath) {
- mDotRenderer = new DotRenderer(mPositioner.getBubbleBitmapSize(),
+ mDotRenderer = new DotRenderer(mPositioner.getBubbleSize(),
iconPath, DEFAULT_PATH_SIZE);
invalidate();
}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleController.java
index 0dd11fb7c5e9..8c8a56a773f7 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleController.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleController.java
@@ -722,6 +722,7 @@ public class BubbleController {
|| !newConfig.windowConfiguration.getBounds().equals(mScreenBounds)) {
mDensityDpi = newConfig.densityDpi;
mScreenBounds.set(newConfig.windowConfiguration.getBounds());
+ mBubbleData.onMaxBubblesChanged();
mBubbleIconFactory = new BubbleIconFactory(mContext);
mStackView.onDisplaySizeChanged();
}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleData.java b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleData.java
index 6f526ecf2b62..69a741c674db 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleData.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleData.java
@@ -141,9 +141,11 @@ public class BubbleData {
private final BubbleOverflow mOverflow;
private boolean mShowingOverflow;
private boolean mExpanded;
- private final int mMaxBubbles;
+ private int mMaxBubbles;
private int mMaxOverflowBubbles;
+ private boolean mNeedsTrimming;
+
// State tracked during an operation -- keeps track of what listener events to dispatch.
private Update mStateChange;
@@ -180,7 +182,7 @@ public class BubbleData {
mOverflowBubbles = new ArrayList<>();
mPendingBubbles = new HashMap<>();
mStateChange = new Update(mBubbles, mOverflowBubbles);
- mMaxBubbles = mContext.getResources().getInteger(R.integer.bubbles_max_rendered);
+ mMaxBubbles = mPositioner.getMaxBubbles();
mMaxOverflowBubbles = mContext.getResources().getInteger(R.integer.bubbles_max_overflow);
}
@@ -194,6 +196,16 @@ public class BubbleData {
mCancelledListener = listener;
}
+ public void onMaxBubblesChanged() {
+ mMaxBubbles = mPositioner.getMaxBubbles();
+ if (!mExpanded) {
+ trim();
+ dispatchPendingChanges();
+ } else {
+ mNeedsTrimming = true;
+ }
+ }
+
public boolean hasBubbles() {
return !mBubbles.isEmpty();
}
@@ -455,13 +467,19 @@ public class BubbleData {
private void trim() {
if (mBubbles.size() > mMaxBubbles) {
+ int numtoRemove = mBubbles.size() - mMaxBubbles;
+ ArrayList<Bubble> toRemove = new ArrayList<>();
mBubbles.stream()
// sort oldest first (ascending lastActivity)
.sorted(Comparator.comparingLong(Bubble::getLastActivity))
// skip the selected bubble
.filter((b) -> !b.equals(mSelectedBubble))
- .findFirst()
- .ifPresent((b) -> doRemove(b.getKey(), Bubbles.DISMISS_AGED));
+ .forEachOrdered((b) -> {
+ if (toRemove.size() < numtoRemove) {
+ toRemove.add(b);
+ }
+ });
+ toRemove.forEach((b) -> doRemove(b.getKey(), Bubbles.DISMISS_AGED));
}
}
@@ -770,6 +788,10 @@ public class BubbleData {
}
}
}
+ if (mNeedsTrimming) {
+ mNeedsTrimming = false;
+ trim();
+ }
mExpanded = shouldExpand;
mStateChange.expanded = shouldExpand;
mStateChange.expandedChanged = true;
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleExpandedView.java b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleExpandedView.java
index 158babe25652..b674c68b2490 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleExpandedView.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleExpandedView.java
@@ -60,6 +60,7 @@ import android.widget.LinearLayout;
import androidx.annotation.Nullable;
import com.android.internal.policy.ScreenDecorationsUtils;
+import com.android.launcher3.icons.IconNormalizer;
import com.android.wm.shell.R;
import com.android.wm.shell.TaskView;
import com.android.wm.shell.common.AlphaOptimizedButton;
@@ -726,9 +727,13 @@ public class BubbleExpandedView extends LinearLayout {
setPadding((int) paddingLeft, (int) paddingTop, (int) paddingRight, 0);
final float expandedViewY = mPositioner.getExpandedViewY();
+ // TODO: I don't understand why it works but it does - why normalized in portrait
+ // & not in landscape? Am I missing ~2dp in the portrait expandedViewY calculation?
+ final float normalizedSize = IconNormalizer.getNormalizedCircleSize(
+ mPositioner.getBubbleSize());
final float bubbleCenter = showVertically
? bubblePosition + (mPositioner.getBubbleSize() / 2f) - expandedViewY
- : bubblePosition + (mPositioner.getBubbleBitmapSize() / 2f) - mPointerWidth;
+ : bubblePosition + (normalizedSize / 2f) - mPointerWidth;
// Post because we need the width of the view
post(() -> {
float pointerY;
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleFlyoutView.java b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleFlyoutView.java
index 79f77d78a257..2fc696cda758 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleFlyoutView.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleFlyoutView.java
@@ -72,7 +72,6 @@ public class BubbleFlyoutView extends FrameLayout {
private final int mFlyoutSpaceFromBubble;
private final int mPointerSize;
private int mBubbleSize;
- private int mBubbleBitmapSize;
private final int mFlyoutElevation;
private final int mBubbleElevation;
@@ -337,10 +336,9 @@ public class BubbleFlyoutView extends FrameLayout {
boolean hideDot,
BubblePositioner positioner) {
- mBubbleBitmapSize = positioner.getBubbleBitmapSize();
mBubbleSize = positioner.getBubbleSize();
- mOriginalDotSize = SIZE_PERCENTAGE * mBubbleBitmapSize;
+ mOriginalDotSize = SIZE_PERCENTAGE * mBubbleSize;
mNewDotRadius = (DOT_SCALE * mOriginalDotSize) / 2f;
mNewDotSize = mNewDotRadius * 2f;
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleIconFactory.java b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleIconFactory.java
index b83feeedbf21..b0e029fdc681 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleIconFactory.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleIconFactory.java
@@ -48,9 +48,8 @@ public class BubbleIconFactory extends BaseIconFactory {
public BubbleIconFactory(Context context) {
super(context, context.getResources().getConfiguration().densityDpi,
- context.getResources().getDimensionPixelSize(R.dimen.individual_bubble_size));
- mBadgeSize = mContext.getResources().getDimensionPixelSize(
- com.android.launcher3.icons.R.dimen.profile_badge_size);
+ context.getResources().getDimensionPixelSize(R.dimen.bubble_size));
+ mBadgeSize = mContext.getResources().getDimensionPixelSize(R.dimen.bubble_badge_size);
}
/**
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleOverflow.kt b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleOverflow.kt
index b7a30ea94f87..705a12a5e65b 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleOverflow.kt
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleOverflow.kt
@@ -30,12 +30,6 @@ import android.view.LayoutInflater
import android.widget.FrameLayout
import com.android.wm.shell.R
-/**
- * The icon in the bubble overflow is scaled down, this is the percent of the normal bubble bitmap
- * size to use.
- */
-const val ICON_BITMAP_SIZE_PERCENT = 0.46f
-
class BubbleOverflow(
private val context: Context,
private val positioner: BubblePositioner
@@ -44,10 +38,9 @@ class BubbleOverflow(
private lateinit var bitmap: Bitmap
private lateinit var dotPath: Path
- private var bitmapSize = 0
- private var iconBitmapSize = 0
private var dotColor = 0
private var showDot = false
+ private var overflowIconInset = 0
private val inflater: LayoutInflater = LayoutInflater.from(context)
private var expandedView: BubbleExpandedView?
@@ -55,8 +48,6 @@ class BubbleOverflow(
init {
updateResources()
- bitmapSize = positioner.bubbleBitmapSize
- iconBitmapSize = (bitmapSize * ICON_BITMAP_SIZE_PERCENT).toInt()
expandedView = null
overflowBtn = null
}
@@ -80,10 +71,10 @@ class BubbleOverflow(
}
fun updateResources() {
- bitmapSize = positioner.bubbleBitmapSize
- iconBitmapSize = (bitmapSize * ICON_BITMAP_SIZE_PERCENT).toInt()
- val bubbleSize = positioner.bubbleSize
- overflowBtn?.layoutParams = FrameLayout.LayoutParams(bubbleSize, bubbleSize)
+ overflowIconInset = context.resources.getDimensionPixelSize(
+ R.dimen.bubble_overflow_icon_inset)
+ overflowBtn?.layoutParams = FrameLayout.LayoutParams(positioner.bubbleSize,
+ positioner.bubbleSize)
expandedView?.updateDimensions()
}
@@ -103,8 +94,7 @@ class BubbleOverflow(
val iconFactory = BubbleIconFactory(context)
// Update bitmap
- val fg = InsetDrawable(overflowBtn?.drawable,
- bitmapSize - iconBitmapSize /* inset */)
+ val fg = InsetDrawable(overflowBtn?.drawable, overflowIconInset)
bitmap = iconFactory.createBadgedIconBitmap(AdaptiveIconDrawable(
ColorDrawable(colorAccent), fg),
null /* user */, true /* shrinkNonAdaptiveIcons */).icon
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubblePositioner.java b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubblePositioner.java
index 65a0ddab64fc..c600f56ba0c5 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubblePositioner.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubblePositioner.java
@@ -56,20 +56,20 @@ public class BubblePositioner {
public static final int TASKBAR_POSITION_LEFT = 1;
public static final int TASKBAR_POSITION_BOTTOM = 2;
- /**
- * The bitmap in the bubble is slightly smaller than the overall size of the bubble.
- * This is the percentage to scale the image down based on the overall bubble size.
- */
- private static final float BUBBLE_BITMAP_SIZE_PERCENT = 0.86f;
+ /** When the bubbles are collapsed in a stack only some of them are shown, this is how many. **/
+ public static final int NUM_VISIBLE_WHEN_RESTING = 2;
private Context mContext;
private WindowManager mWindowManager;
private Rect mPositionRect;
private @Surface.Rotation int mRotation = Surface.ROTATION_0;
private Insets mInsets;
+ private int mDefaultMaxBubbles;
+ private int mMaxBubbles;
private int mBubbleSize;
- private int mBubbleBitmapSize;
+ private int mBubbleBadgeSize;
+ private int mSpacingBetweenBubbles;
private int mExpandedViewLargeScreenWidth;
private int mExpandedViewPadding;
private int mPointerMargin;
@@ -150,20 +150,48 @@ public class BubblePositioner {
mPositionRect.bottom -= mInsets.bottom;
Resources res = mContext.getResources();
- mBubbleSize = res.getDimensionPixelSize(R.dimen.individual_bubble_size);
- mBubbleBitmapSize = res.getDimensionPixelSize(R.dimen.bubble_bitmap_size);
+ mBubbleSize = res.getDimensionPixelSize(R.dimen.bubble_size);
+ mBubbleBadgeSize = res.getDimensionPixelSize(R.dimen.bubble_badge_size);
+ mSpacingBetweenBubbles = res.getDimensionPixelSize(R.dimen.bubble_spacing);
+ mDefaultMaxBubbles = res.getInteger(R.integer.bubbles_max_rendered);
+
mExpandedViewLargeScreenWidth = res.getDimensionPixelSize(
R.dimen.bubble_expanded_view_tablet_width);
mExpandedViewPadding = res.getDimensionPixelSize(R.dimen.bubble_expanded_view_padding);
mPointerWidth = res.getDimensionPixelSize(R.dimen.bubble_pointer_width);
mPointerHeight = res.getDimensionPixelSize(R.dimen.bubble_pointer_height);
mPointerMargin = res.getDimensionPixelSize(R.dimen.bubble_pointer_margin);
+
+ mMaxBubbles = calculateMaxBubbles();
+
if (mShowingInTaskbar) {
adjustForTaskbar();
}
}
/**
+ * @return the maximum number of bubbles that can fit on the screen when expanded. If the
+ * screen size / screen density is too small to support the default maximum number, then
+ * the number will be adjust to something lower to ensure everything is presented nicely.
+ */
+ private int calculateMaxBubbles() {
+ // Use the shortest edge.
+ // In portrait the bubbles should align with the expanded view so subtract its padding.
+ // We always show the overflow so subtract one bubble size.
+ int padding = showBubblesVertically() ? 0 : (mExpandedViewPadding * 2);
+ int availableSpace = Math.min(mPositionRect.width(), mPositionRect.height())
+ - padding
+ - mBubbleSize;
+ // Each of the bubbles have spacing because the overflow is at the end.
+ int howManyFit = availableSpace / (mBubbleSize + mSpacingBetweenBubbles);
+ if (howManyFit < mDefaultMaxBubbles) {
+ // Not enough space for the default.
+ return howManyFit;
+ }
+ return mDefaultMaxBubbles;
+ }
+
+ /**
* Taskbar insets appear as navigationBar insets, however, unlike navigationBar this should
* not inset bubbles UI as bubbles floats above the taskbar. This adjust the available space
* and insets to account for the taskbar.
@@ -225,20 +253,16 @@ public class BubblePositioner {
return isLandscape() || mShowingInTaskbar || mIsLargeScreen;
}
- /** Size of the bubble account for badge & dot. */
+ /** Size of the bubble. */
public int getBubbleSize() {
- int bsize = (mShowingInTaskbar && mTaskbarIconSize > 0)
+ return (mShowingInTaskbar && mTaskbarIconSize > 0)
? mTaskbarIconSize
: mBubbleSize;
- return bsize;
}
- /** Size of the bitmap within the bubble */
- public int getBubbleBitmapSize() {
- float size = (mShowingInTaskbar && mTaskbarIconSize > 0)
- ? (mTaskbarIconSize * BUBBLE_BITMAP_SIZE_PERCENT)
- : mBubbleBitmapSize;
- return (int) size;
+ /** The maximum number of bubbles that can be displayed comfortably on screen. */
+ public int getMaxBubbles() {
+ return mMaxBubbles;
}
/**
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleStackView.java b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleStackView.java
index 219e8379e6c7..d821c6ff7cd1 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleStackView.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleStackView.java
@@ -22,6 +22,7 @@ import static android.view.ViewGroup.LayoutParams.WRAP_CONTENT;
import static com.android.wm.shell.bubbles.BubbleDebugConfig.DEBUG_BUBBLE_STACK_VIEW;
import static com.android.wm.shell.bubbles.BubbleDebugConfig.TAG_BUBBLES;
import static com.android.wm.shell.bubbles.BubbleDebugConfig.TAG_WITH_CLASS_NAME;
+import static com.android.wm.shell.bubbles.BubblePositioner.NUM_VISIBLE_WHEN_RESTING;
import android.animation.Animator;
import android.animation.AnimatorListenerAdapter;
@@ -33,10 +34,7 @@ import android.content.Intent;
import android.content.res.Resources;
import android.content.res.TypedArray;
import android.graphics.Color;
-import android.graphics.ColorMatrix;
-import android.graphics.ColorMatrixColorFilter;
import android.graphics.Outline;
-import android.graphics.Paint;
import android.graphics.PointF;
import android.graphics.Rect;
import android.graphics.RectF;
@@ -191,8 +189,7 @@ public class BubbleStackView extends FrameLayout
private final BubbleController mBubbleController;
private final BubbleData mBubbleData;
- private final ValueAnimator mDesaturateAndDarkenAnimator;
- private final Paint mDesaturateAndDarkenPaint = new Paint();
+ private final ValueAnimator mDismissBubbleAnimator;
private PhysicsAnimationLayout mBubbleContainer;
private StackAnimationController mStackAnimationController;
@@ -242,7 +239,6 @@ public class BubbleStackView extends FrameLayout
@Nullable private RelativeStackPosition mRelativeStackPositionBeforeRotation;
- private int mMaxBubbles;
private int mBubbleSize;
private int mBubbleElevation;
private int mBubbleTouchPadding;
@@ -330,8 +326,8 @@ public class BubbleStackView extends FrameLayout
private boolean mIsExpansionAnimating = false;
private boolean mIsBubbleSwitchAnimating = false;
- /** The view to desaturate/darken when magneted to the dismiss target. */
- @Nullable private View mDesaturateAndDarkenTargetView;
+ /** The view to shrink and apply alpha to when magneted to the dismiss target. */
+ @Nullable private View mViewBeingDismissed;
private Rect mTempRect = new Rect();
@@ -415,8 +411,7 @@ public class BubbleStackView extends FrameLayout
if (mExpandedAnimationController.getDraggedOutBubble() == null) {
return;
}
-
- animateDesaturateAndDarken(
+ animateDismissBubble(
mExpandedAnimationController.getDraggedOutBubble(), true);
}
@@ -426,8 +421,7 @@ public class BubbleStackView extends FrameLayout
if (mExpandedAnimationController.getDraggedOutBubble() == null) {
return;
}
-
- animateDesaturateAndDarken(
+ animateDismissBubble(
mExpandedAnimationController.getDraggedOutBubble(), false);
if (wasFlungOut) {
@@ -459,14 +453,13 @@ public class BubbleStackView extends FrameLayout
@Override
public void onStuckToTarget(
@NonNull MagnetizedObject.MagneticTarget target) {
- animateDesaturateAndDarken(mBubbleContainer, true);
+ animateDismissBubble(mBubbleContainer, true);
}
@Override
public void onUnstuckFromTarget(@NonNull MagnetizedObject.MagneticTarget target,
float velX, float velY, boolean wasFlungOut) {
- animateDesaturateAndDarken(mBubbleContainer, false);
-
+ animateDismissBubble(mBubbleContainer, false);
if (wasFlungOut) {
mStackAnimationController.flingStackThenSpringToEdge(
mStackAnimationController.getStackPosition().x, velX, velY);
@@ -481,11 +474,10 @@ public class BubbleStackView extends FrameLayout
mStackAnimationController.animateStackDismissal(
mDismissView.getHeight() /* translationYBy */,
() -> {
- resetDesaturationAndDarken();
+ resetDismissAnimator();
dismissMagnetizedObject();
}
);
-
mDismissView.hide();
}
};
@@ -632,6 +624,7 @@ public class BubbleStackView extends FrameLayout
// First, see if the magnetized object consumes the event - if so, we shouldn't move the
// bubble since it's stuck to the target.
if (!passEventToMagnetizedObject(ev)) {
+ updateBubbleShadows(true /* showForAllBubbles */);
if (mBubbleData.isExpanded() || mPositioner.showingInTaskbar()) {
mExpandedAnimationController.dragBubbleOut(
v, viewInitialX + dx, viewInitialY + dy);
@@ -671,7 +664,7 @@ public class BubbleStackView extends FrameLayout
mStackAnimationController.flingStackThenSpringToEdge(
viewInitialX + dx, velX, velY) <= 0;
final boolean updateForCollapsedStack = oldOnLeft != mStackOnLeftOrWillBe;
- updateBadgesAndZOrder(updateForCollapsedStack);
+ updateBadges(updateForCollapsedStack);
logBubbleEvent(null /* no bubble associated with bubble stack move */,
FrameworkStatsLog.BUBBLE_UICHANGED__ACTION__STACK_MOVED);
}
@@ -762,8 +755,7 @@ public class BubbleStackView extends FrameLayout
mBubbleData = data;
Resources res = getResources();
- mMaxBubbles = res.getInteger(R.integer.bubbles_max_rendered);
- mBubbleSize = res.getDimensionPixelSize(R.dimen.individual_bubble_size);
+ mBubbleSize = res.getDimensionPixelSize(R.dimen.bubble_size);
mBubbleElevation = res.getDimensionPixelSize(R.dimen.bubble_elevation);
mBubbleTouchPadding = res.getDimensionPixelSize(R.dimen.bubble_touch_padding);
mImeOffset = res.getDimensionPixelSize(R.dimen.pip_ime_offset);
@@ -783,9 +775,9 @@ public class BubbleStackView extends FrameLayout
mBubbleController.onAllBubblesAnimatedOut();
}
};
-
mStackAnimationController = new StackAnimationController(
- floatingContentCoordinator, this::getBubbleCount, onBubbleAnimatedOut, mPositioner);
+ floatingContentCoordinator, this::getBubbleCount, onBubbleAnimatedOut,
+ this::animateShadows /* onStackAnimationFinished */, mPositioner);
mExpandedAnimationController = new ExpandedAnimationController(
mPositioner, mExpandedViewPadding, onBubbleAnimatedOut);
@@ -836,17 +828,7 @@ public class BubbleStackView extends FrameLayout
.setDampingRatio(SpringForce.DAMPING_RATIO_LOW_BOUNCY));
mFlyoutTransitionSpring.addEndListener(mAfterFlyoutTransitionSpring);
- mDismissView = new DismissView(context);
- addView(mDismissView);
-
- final ContentResolver contentResolver = getContext().getContentResolver();
- final int dismissRadius = Settings.Secure.getInt(
- contentResolver, "bubble_dismiss_radius", mBubbleSize * 2 /* default */);
-
- // Save the MagneticTarget instance for the newly set up view - we'll add this to the
- // MagnetizedObjects.
- mMagneticTarget = new MagnetizedObject.MagneticTarget(
- mDismissView.getCircle(), dismissRadius);
+ setUpDismissView();
setClipChildren(false);
setFocusable(true);
@@ -891,6 +873,7 @@ public class BubbleStackView extends FrameLayout
mRelativeStackPositionBeforeRotation = null;
}
+ setUpDismissView();
if (mIsExpanded) {
// Re-draw bubble row and pointer for new orientation.
beforeExpandedViewAnimation();
@@ -905,30 +888,23 @@ public class BubbleStackView extends FrameLayout
}
removeOnLayoutChangeListener(mOrientationChangedListener);
};
-
- final ColorMatrix animatedMatrix = new ColorMatrix();
- final ColorMatrix darkenMatrix = new ColorMatrix();
-
- mDesaturateAndDarkenAnimator = ValueAnimator.ofFloat(1f, 0f);
- mDesaturateAndDarkenAnimator.addUpdateListener(animation -> {
+ final float maxDismissSize = getResources().getDimensionPixelSize(
+ R.dimen.dismiss_circle_size);
+ final float minDismissSize = getResources().getDimensionPixelSize(
+ R.dimen.dismiss_circle_small);
+ final float sizePercent = minDismissSize / maxDismissSize;
+ mDismissBubbleAnimator = ValueAnimator.ofFloat(1f, 0f);
+ mDismissBubbleAnimator.addUpdateListener(animation -> {
final float animatedValue = (float) animation.getAnimatedValue();
- animatedMatrix.setSaturation(animatedValue);
-
- final float animatedDarkenValue = (1f - animatedValue) * DARKEN_PERCENT;
- darkenMatrix.setScale(
- 1f - animatedDarkenValue /* red */,
- 1f - animatedDarkenValue /* green */,
- 1f - animatedDarkenValue /* blue */,
- 1f /* alpha */);
-
- // Concat the matrices so that the animatedMatrix both desaturates and darkens.
- animatedMatrix.postConcat(darkenMatrix);
-
- // Update the paint and apply it to the bubble container.
- mDesaturateAndDarkenPaint.setColorFilter(new ColorMatrixColorFilter(animatedMatrix));
-
- if (mDesaturateAndDarkenTargetView != null) {
- mDesaturateAndDarkenTargetView.setLayerPaint(mDesaturateAndDarkenPaint);
+ if (mDismissView != null) {
+ mDismissView.setPivotX((mDismissView.getRight() - mDismissView.getLeft()) / 2f);
+ mDismissView.setPivotY((mDismissView.getBottom() - mDismissView.getTop()) / 2f);
+ final float scaleValue = Math.max(animatedValue, sizePercent);
+ mDismissView.getCircle().setScaleX(scaleValue);
+ mDismissView.getCircle().setScaleY(scaleValue);
+ }
+ if (mViewBeingDismissed != null) {
+ mViewBeingDismissed.setAlpha(Math.max(animatedValue, 0.7f));
}
});
@@ -938,6 +914,8 @@ public class BubbleStackView extends FrameLayout
if (ev.getAction() == MotionEvent.ACTION_DOWN) {
if (mShowingManage) {
showManageMenu(false /* show */);
+ } else if (mStackEduView != null && mStackEduView.getVisibility() == VISIBLE) {
+ mStackEduView.hide(false);
} else if (mBubbleData.isExpanded()) {
mBubbleData.setExpanded(false);
}
@@ -1046,6 +1024,23 @@ public class BubbleStackView extends FrameLayout
}
};
+ private void setUpDismissView() {
+ if (mDismissView != null) {
+ removeView(mDismissView);
+ }
+ mDismissView = new DismissView(getContext());
+ addView(mDismissView);
+
+ final ContentResolver contentResolver = getContext().getContentResolver();
+ final int dismissRadius = Settings.Secure.getInt(
+ contentResolver, "bubble_dismiss_radius", mBubbleSize * 2 /* default */);
+
+ // Save the MagneticTarget instance for the newly set up view - we'll add this to the
+ // MagnetizedObjects.
+ mMagneticTarget = new MagnetizedObject.MagneticTarget(
+ mDismissView.getCircle(), dismissRadius);
+ }
+
// TODO: Create ManageMenuView and move setup / animations there
private void setUpManageMenu() {
if (mManageMenu != null) {
@@ -1152,6 +1147,7 @@ public class BubbleStackView extends FrameLayout
mStackEduView = new StackEducationView(mContext);
addView(mStackEduView);
}
+ mBubbleContainer.bringToFront();
return mStackEduView.show(mPositioner.getDefaultStartPosition());
}
@@ -1214,6 +1210,7 @@ public class BubbleStackView extends FrameLayout
public void onThemeChanged() {
setUpFlyout();
setUpManageMenu();
+ setUpDismissView();
updateOverflow();
updateUserEdu();
updateExpandedViewTheme();
@@ -1253,6 +1250,7 @@ public class BubbleStackView extends FrameLayout
updateOverflow();
setUpManageMenu();
setUpFlyout();
+ setUpDismissView();
mBubbleSize = mPositioner.getBubbleSize();
for (Bubble b : mBubbleData.getBubbles()) {
if (b.getIconView() == null) {
@@ -1505,13 +1503,12 @@ public class BubbleStackView extends FrameLayout
// Set the dot position to the opposite of the side the stack is resting on, since the stack
// resting slightly off-screen would result in the dot also being off-screen.
bubble.getIconView().setDotBadgeOnLeft(!mStackOnLeftOrWillBe /* onLeft */);
-
bubble.getIconView().setOnClickListener(mBubbleClickListener);
bubble.getIconView().setOnTouchListener(mBubbleTouchListener);
-
mBubbleContainer.addView(bubble.getIconView(), 0,
new FrameLayout.LayoutParams(mPositioner.getBubbleSize(),
mPositioner.getBubbleSize()));
+ updateBubbleShadows(false /* showForAllBubbles */);
animateInFlyoutForBubble(bubble);
requestUpdate();
logBubbleEvent(bubble, FrameworkStatsLog.BUBBLE_UICHANGED__ACTION__POSTED);
@@ -1566,7 +1563,8 @@ public class BubbleStackView extends FrameLayout
};
if (mIsExpanded || isExpansionAnimating()) {
reorder.run();
- updateBadgesAndZOrder(false /* setBadgeForCollapsedStack */);
+ updateBadges(false /* setBadgeForCollapsedStack */);
+ updateZOrder();
} else if (!isExpansionAnimating()) {
List<View> bubbleViews = bubbles.stream()
.map(b -> b.getIconView()).collect(Collectors.toList());
@@ -1803,7 +1801,8 @@ public class BubbleStackView extends FrameLayout
}
beforeExpandedViewAnimation();
- updateBadgesAndZOrder(false /* setBadgeForCollapsedStack */);
+ updateZOrder();
+ updateBadges(false /* setBadgeForCollapsedStack */);
mBubbleContainer.setActiveController(mExpandedAnimationController);
updateOverflowVisibility();
updatePointerPosition();
@@ -2014,7 +2013,8 @@ public class BubbleStackView extends FrameLayout
mExpandedBubble));
}
updateOverflowVisibility();
- updateBadgesAndZOrder(true /* setBadgeForCollapsedStack */);
+ updateZOrder();
+ updateBadges(true /* setBadgeForCollapsedStack */);
afterExpandedViewAnimation();
if (previouslySelected != null) {
previouslySelected.setTaskViewVisibility(false);
@@ -2259,42 +2259,46 @@ public class BubbleStackView extends FrameLayout
}
}
- /** Prepares and starts the desaturate/darken animation on the bubble stack. */
- private void animateDesaturateAndDarken(View targetView, boolean desaturateAndDarken) {
- mDesaturateAndDarkenTargetView = targetView;
+ /** Prepares and starts the dismiss animation on the bubble stack. */
+ private void animateDismissBubble(View targetView, boolean applyAlpha) {
+ mViewBeingDismissed = targetView;
- if (mDesaturateAndDarkenTargetView == null) {
+ if (mViewBeingDismissed == null) {
return;
}
-
- if (desaturateAndDarken) {
- // Use the animated paint for the bubbles.
- mDesaturateAndDarkenTargetView.setLayerType(
- View.LAYER_TYPE_HARDWARE, mDesaturateAndDarkenPaint);
- mDesaturateAndDarkenAnimator.removeAllListeners();
- mDesaturateAndDarkenAnimator.start();
+ if (applyAlpha) {
+ mDismissBubbleAnimator.removeAllListeners();
+ mDismissBubbleAnimator.start();
} else {
- mDesaturateAndDarkenAnimator.removeAllListeners();
- mDesaturateAndDarkenAnimator.addListener(new AnimatorListenerAdapter() {
+ mDismissBubbleAnimator.removeAllListeners();
+ mDismissBubbleAnimator.addListener(new AnimatorListenerAdapter() {
@Override
public void onAnimationEnd(Animator animation) {
super.onAnimationEnd(animation);
- // Stop using the animated paint.
- resetDesaturationAndDarken();
+ resetDismissAnimator();
+ }
+
+ @Override
+ public void onAnimationCancel(Animator animation) {
+ super.onAnimationCancel(animation);
+ resetDismissAnimator();
}
});
- mDesaturateAndDarkenAnimator.reverse();
+ mDismissBubbleAnimator.reverse();
}
}
- private void resetDesaturationAndDarken() {
-
- mDesaturateAndDarkenAnimator.removeAllListeners();
- mDesaturateAndDarkenAnimator.cancel();
+ private void resetDismissAnimator() {
+ mDismissBubbleAnimator.removeAllListeners();
+ mDismissBubbleAnimator.cancel();
- if (mDesaturateAndDarkenTargetView != null) {
- mDesaturateAndDarkenTargetView.setLayerType(View.LAYER_TYPE_NONE, null);
- mDesaturateAndDarkenTargetView = null;
+ if (mViewBeingDismissed != null) {
+ mViewBeingDismissed.setAlpha(1f);
+ mViewBeingDismissed = null;
+ }
+ if (mDismissView != null) {
+ mDismissView.getCircle().setScaleX(1f);
+ mDismissView.getCircle().setScaleY(1f);
}
}
@@ -2707,14 +2711,56 @@ public class BubbleStackView extends FrameLayout
}
/**
- * Sets the appropriate Z-order, badge, and dot position for each bubble in the stack.
- * Animate dot and badge changes.
+ * Updates whether each of the bubbles should show shadows. When collapsed & resting, only the
+ * visible bubbles (top 2) will show a shadow. When the stack is being dragged, everything
+ * shows a shadow. When an individual bubble is dragged out, it should show a shadow.
*/
- private void updateBadgesAndZOrder(boolean setBadgeForCollapsedStack) {
+ private void updateBubbleShadows(boolean showForAllBubbles) {
+ int bubbleCount = getBubbleCount();
+ for (int i = 0; i < bubbleCount; i++) {
+ final float z = (mPositioner.getMaxBubbles() * mBubbleElevation) - i;
+ BadgedImageView bv = (BadgedImageView) mBubbleContainer.getChildAt(i);
+ boolean isDraggedOut = mMagnetizedObject != null
+ && mMagnetizedObject.getUnderlyingObject().equals(bv);
+ if (showForAllBubbles || isDraggedOut) {
+ bv.setZ(z);
+ } else {
+ final float tz = i < NUM_VISIBLE_WHEN_RESTING ? z : 0f;
+ bv.setZ(tz);
+ }
+ }
+ }
+
+ /**
+ * When the bubbles are flung and then rest, the shadows stack up for the bubbles hidden
+ * beneath the top two bubbles, to avoid this we animate the Z translations once the stack
+ * is resting so that they fade away nicely.
+ */
+ private void animateShadows() {
+ int bubbleCount = getBubbleCount();
+ for (int i = 0; i < bubbleCount; i++) {
+ BadgedImageView bv = (BadgedImageView) mBubbleContainer.getChildAt(i);
+ boolean fullShadow = i < NUM_VISIBLE_WHEN_RESTING;
+ if (!fullShadow) {
+ bv.animate().translationZ(0).start();
+ }
+ }
+ }
+
+ private void updateZOrder() {
+ int bubbleCount = getBubbleCount();
+ for (int i = 0; i < bubbleCount; i++) {
+ BadgedImageView bv = (BadgedImageView) mBubbleContainer.getChildAt(i);
+ bv.setZ(i < NUM_VISIBLE_WHEN_RESTING
+ ? (mPositioner.getMaxBubbles() * mBubbleElevation) - i
+ : 0f);
+ }
+ }
+
+ private void updateBadges(boolean setBadgeForCollapsedStack) {
int bubbleCount = getBubbleCount();
for (int i = 0; i < bubbleCount; i++) {
BadgedImageView bv = (BadgedImageView) mBubbleContainer.getChildAt(i);
- bv.setZ((mMaxBubbles * mBubbleElevation) - i);
if (mIsExpanded) {
// If we're not displaying vertically, we always show the badge on the left.
boolean onLeft = mPositioner.showBubblesVertically() && !mStackOnLeftOrWillBe;
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/DismissView.kt b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/DismissView.kt
index 04b5ad6dddf9..0a1cd2246339 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/DismissView.kt
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/DismissView.kt
@@ -67,8 +67,6 @@ class DismissView(context: Context) : FrameLayout(context) {
fun show() {
if (isShowing) return
isShowing = true
- bringToFront()
- setZ(Short.MAX_VALUE - 1f)
setVisibility(View.VISIBLE)
(getBackground() as TransitionDrawable).startTransition(DISMISS_SCRIM_FADE_MS)
animator.cancel()
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/StackEducationView.kt b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/StackEducationView.kt
index 04c4dfb9b08d..0a2cfc4089ed 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/StackEducationView.kt
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/StackEducationView.kt
@@ -99,7 +99,7 @@ class StackEducationView constructor(context: Context) : LinearLayout(context) {
post {
with(view) {
val bubbleSize = context.resources.getDimensionPixelSize(
- R.dimen.individual_bubble_size)
+ R.dimen.bubble_size)
translationY = stackPosition.y + bubbleSize / 2 - getHeight() / 2
}
animate()
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/animation/ExpandedAnimationController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/animation/ExpandedAnimationController.java
index 7f55403ba33c..df2b440c19df 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/animation/ExpandedAnimationController.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/animation/ExpandedAnimationController.java
@@ -16,6 +16,8 @@
package com.android.wm.shell.bubbles.animation;
+import static com.android.wm.shell.bubbles.BubblePositioner.NUM_VISIBLE_WHEN_RESTING;
+
import android.content.res.Resources;
import android.graphics.Path;
import android.graphics.PointF;
@@ -209,16 +211,8 @@ public class ExpandedAnimationController
mBubblePaddingTop = res.getDimensionPixelSize(R.dimen.bubble_padding_top);
mStackOffsetPx = res.getDimensionPixelSize(R.dimen.bubble_stack_offset);
mBubbleSizePx = mPositioner.getBubbleSize();
- mBubblesMaxRendered = res.getInteger(R.integer.bubbles_max_rendered);
- mBubblesMaxSpace = res.getDimensionPixelSize(R.dimen.bubble_max_spacing);
- final float availableSpace = mPositioner.isLandscape()
- ? mPositioner.getAvailableRect().height()
- : mPositioner.getAvailableRect().width();
- final float spaceForMaxBubbles = (mExpandedViewPadding * 2)
- + (mBubblesMaxRendered + 1) * mBubbleSizePx;
- float spaceBetweenBubbles =
- (availableSpace - spaceForMaxBubbles) / mBubblesMaxRendered;
- mSpaceBetweenBubbles = Math.min(spaceBetweenBubbles, mBubblesMaxSpace);
+ mBubblesMaxRendered = mPositioner.getMaxBubbles();
+ mSpaceBetweenBubbles = res.getDimensionPixelSize(R.dimen.bubble_spacing);
}
/**
@@ -289,7 +283,8 @@ public class ExpandedAnimationController
path.lineTo(stackedX, expandedY);
// Then, draw a line down to the stack position.
- path.lineTo(stackedX, mCollapsePoint.y + Math.min(index, 1) * mStackOffsetPx);
+ path.lineTo(stackedX, mCollapsePoint.y
+ + Math.min(index, NUM_VISIBLE_WHEN_RESTING - 1) * mStackOffsetPx);
}
// The lead bubble should be the bubble with the longest distance to travel when we're
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/animation/StackAnimationController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/animation/StackAnimationController.java
index 6209051d38ef..8043d2b8b2f9 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/animation/StackAnimationController.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/animation/StackAnimationController.java
@@ -16,6 +16,8 @@
package com.android.wm.shell.bubbles.animation;
+import static com.android.wm.shell.bubbles.BubblePositioner.NUM_VISIBLE_WHEN_RESTING;
+
import android.content.ContentResolver;
import android.content.res.Resources;
import android.graphics.PointF;
@@ -182,9 +184,7 @@ public class StackAnimationController extends
private int mMaxBubbles;
/** Default bubble elevation. */
private int mElevation;
- /** Diameter of the bubble icon. */
- private int mBubbleBitmapSize;
- /** Width of the bubble (icon and padding). */
+ /** Diameter of the bubble. */
private int mBubbleSize;
/**
* The amount of space to add between the bubbles and certain UI elements, such as the top of
@@ -257,14 +257,21 @@ public class StackAnimationController extends
*/
private Runnable mOnBubbleAnimatedOutAction;
+ /**
+ * Callback to run whenever the stack is finished being flung somewhere.
+ */
+ private Runnable mOnStackAnimationFinished;
+
public StackAnimationController(
FloatingContentCoordinator floatingContentCoordinator,
IntSupplier bubbleCountSupplier,
Runnable onBubbleAnimatedOutAction,
+ Runnable onStackAnimationFinished,
BubblePositioner positioner) {
mFloatingContentCoordinator = floatingContentCoordinator;
mBubbleCountSupplier = bubbleCountSupplier;
mOnBubbleAnimatedOutAction = onBubbleAnimatedOutAction;
+ mOnStackAnimationFinished = onStackAnimationFinished;
mPositioner = positioner;
}
@@ -302,7 +309,7 @@ public class StackAnimationController extends
return true; // Default to left, which is where it starts by default.
}
- float stackCenter = mStackPosition.x + mBubbleBitmapSize / 2;
+ float stackCenter = mStackPosition.x + mBubbleSize / 2;
float screenCenter = mLayout.getWidth() / 2;
return stackCenter < screenCenter;
}
@@ -346,7 +353,7 @@ public class StackAnimationController extends
* @return The X value that the stack will end up at after the fling/spring.
*/
public float flingStackThenSpringToEdge(float x, float velX, float velY) {
- final boolean stackOnLeftSide = x - mBubbleBitmapSize / 2 < mLayout.getWidth() / 2;
+ final boolean stackOnLeftSide = x - mBubbleSize / 2 < mLayout.getWidth() / 2;
final boolean stackShouldFlingLeft = stackOnLeftSide
? velX < ESCAPE_VELOCITY
@@ -674,6 +681,10 @@ public class StackAnimationController extends
mPositioner.setRestingPosition(mStackPosition);
}
+ if (mOnStackAnimationFinished != null) {
+ mOnStackAnimationFinished.run();
+ }
+
if (after != null) {
for (Runnable callback : after) {
callback.run();
@@ -718,7 +729,7 @@ public class StackAnimationController extends
} else {
// We only show the first two bubbles in the stack & the rest hide behind them
// so they don't need an offset.
- return index > 1 ? 0f : mStackOffset;
+ return index > (NUM_VISIBLE_WHEN_RESTING - 1) ? 0f : mStackOffset;
}
} else {
return 0f;
@@ -827,7 +838,8 @@ public class StackAnimationController extends
private void moveToFinalIndex(View view, int newIndex,
Runnable finishReorder) {
final ViewPropertyAnimator animator = view.animate()
- .translationY(getStackPosition().y + Math.min(newIndex, 1) * mStackOffset)
+ .translationY(getStackPosition().y
+ + Math.min(newIndex, NUM_VISIBLE_WHEN_RESTING - 1) * mStackOffset)
.setDuration(BUBBLE_SWAP_DURATION)
.withEndAction(() -> {
view.setTag(R.id.reorder_animator_tag, null);
@@ -836,8 +848,9 @@ public class StackAnimationController extends
view.setTag(R.id.reorder_animator_tag, animator);
}
+ // TODO: do we need this & BubbleStackView#updateBadgesAndZOrder?
private void updateBadgesAndZOrder(View v, int index) {
- v.setZ((mMaxBubbles * mElevation) - index);
+ v.setZ(index < NUM_VISIBLE_WHEN_RESTING ? (mMaxBubbles * mElevation) - index : 0f);
BadgedImageView bv = (BadgedImageView) v;
if (index == 0) {
bv.showDotAndBadge(!isStackOnLeftSide());
@@ -857,7 +870,6 @@ public class StackAnimationController extends
mMaxBubbles = res.getInteger(R.integer.bubbles_max_rendered);
mElevation = res.getDimensionPixelSize(R.dimen.bubble_elevation);
mBubbleSize = mPositioner.getBubbleSize();
- mBubbleBitmapSize = mPositioner.getBubbleBitmapSize();
mBubblePaddingTop = res.getDimensionPixelSize(R.dimen.bubble_padding_top);
mBubbleOffscreen = res.getDimensionPixelSize(R.dimen.bubble_stack_offscreen);
}
@@ -942,8 +954,9 @@ public class StackAnimationController extends
final float yOffset = getOffsetForChainedPropertyAnimation(
DynamicAnimation.TRANSLATION_Y, 0);
for (int i = 0; i < mLayout.getChildCount(); i++) {
- mLayout.getChildAt(i).setTranslationX(pos.x + (Math.min(i, 1) * xOffset));
- mLayout.getChildAt(i).setTranslationY(pos.y + (Math.min(i, 1) * yOffset));
+ float index = Math.min(i, NUM_VISIBLE_WHEN_RESTING - 1);
+ mLayout.getChildAt(i).setTranslationX(pos.x + (index * xOffset));
+ mLayout.getChildAt(i).setTranslationY(pos.y + (index * yOffset));
}
}
}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipTaskOrganizer.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipTaskOrganizer.java
index 00494611420d..e1b198c4217a 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipTaskOrganizer.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipTaskOrganizer.java
@@ -379,6 +379,10 @@ public class PipTaskOrganizer implements ShellTaskOrganizer.TaskListener,
}
}
+ public SurfaceControl getSurfaceControl() {
+ return mLeash;
+ }
+
private void setBoundsStateForEntry(ComponentName componentName, PictureInPictureParams params,
ActivityInfo activityInfo) {
mPipBoundsState.setBoundsStateForEntry(componentName,
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipDismissTargetHandler.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipDismissTargetHandler.java
index c26b686f91fb..1da9577fe49a 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipDismissTargetHandler.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipDismissTargetHandler.java
@@ -26,8 +26,10 @@ import android.graphics.Rect;
import android.graphics.drawable.TransitionDrawable;
import android.view.Gravity;
import android.view.MotionEvent;
+import android.view.SurfaceControl;
import android.view.View;
import android.view.ViewGroup;
+import android.view.ViewTreeObserver;
import android.view.WindowManager;
import android.widget.FrameLayout;
@@ -47,7 +49,7 @@ import kotlin.Unit;
/**
* Handler of all Magnetized Object related code for PiP.
*/
-public class PipDismissTargetHandler {
+public class PipDismissTargetHandler implements ViewTreeObserver.OnPreDrawListener {
/* The multiplier to apply scale the target size by when applying the magnetic field radius */
private static final float MAGNETIC_FIELD_RADIUS_MULTIPLIER = 1.25f;
@@ -92,6 +94,9 @@ public class PipDismissTargetHandler {
private int mDismissAreaHeight;
private float mMagneticFieldRadiusPercent = 1f;
+ private SurfaceControl mTaskLeash;
+ private boolean mHasDismissTargetSurface;
+
private final Context mContext;
private final PipMotionHelper mMotionHelper;
private final PipUiEventLogger mPipUiEventLogger;
@@ -167,6 +172,14 @@ public class PipDismissTargetHandler {
mMagneticTargetAnimator = PhysicsAnimator.getInstance(mTargetView);
}
+ @Override
+ public boolean onPreDraw() {
+ mTargetViewContainer.getViewTreeObserver().removeOnPreDrawListener(this);
+ mHasDismissTargetSurface = true;
+ updateDismissTargetLayer();
+ return true;
+ }
+
/**
* Potentially start consuming future motion events if PiP is currently near the magnetized
* object.
@@ -207,12 +220,31 @@ public class PipDismissTargetHandler {
* MAGNETIC_FIELD_RADIUS_MULTIPLIER));
}
+ public void setTaskLeash(SurfaceControl taskLeash) {
+ mTaskLeash = taskLeash;
+ }
+
+ private void updateDismissTargetLayer() {
+ if (!mHasDismissTargetSurface || mTaskLeash == null) {
+ // No dismiss target surface, can just return
+ return;
+ }
+
+ // Put the dismiss target behind the task
+ SurfaceControl.Transaction t = new SurfaceControl.Transaction();
+ t.setRelativeLayer(mTargetViewContainer.getViewRootImpl().getSurfaceControl(),
+ mTaskLeash, -1);
+ t.apply();
+ }
+
/** Adds the magnetic target view to the WindowManager so it's ready to be animated in. */
public void createOrUpdateDismissTarget() {
if (!mTargetViewContainer.isAttachedToWindow()) {
mMagneticTargetAnimator.cancel();
mTargetViewContainer.setVisibility(View.INVISIBLE);
+ mTargetViewContainer.getViewTreeObserver().removeOnPreDrawListener(this);
+ mHasDismissTargetSurface = false;
try {
mWindowManager.addView(mTargetViewContainer, getDismissTargetLayoutParams());
@@ -259,9 +291,9 @@ public class PipDismissTargetHandler {
createOrUpdateDismissTarget();
if (mTargetViewContainer.getVisibility() != View.VISIBLE) {
-
mTargetView.setTranslationY(mTargetViewContainer.getHeight());
mTargetViewContainer.setVisibility(View.VISIBLE);
+ mTargetViewContainer.getViewTreeObserver().addOnPreDrawListener(this);
// Cancel in case we were in the middle of animating it out.
mMagneticTargetAnimator.cancel();
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipMotionHelper.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipMotionHelper.java
index 15e7f07a1f6c..604ebc08f42e 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipMotionHelper.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipMotionHelper.java
@@ -40,6 +40,7 @@ import android.view.Choreographer;
import androidx.dynamicanimation.animation.AnimationHandler;
import androidx.dynamicanimation.animation.AnimationHandler.FrameCallbackScheduler;
+import com.android.wm.shell.R;
import com.android.wm.shell.animation.FloatProperties;
import com.android.wm.shell.animation.PhysicsAnimator;
import com.android.wm.shell.common.FloatingContentCoordinator;
@@ -71,6 +72,8 @@ public class PipMotionHelper implements PipAppOpsListener.Callback,
/** Friction to use for PIP when it moves via physics fling animations. */
private static final float DEFAULT_FRICTION = 1.9f;
+ /** How much of the dismiss circle size to use when scaling down PIP. **/
+ private static final float DISMISS_CIRCLE_PERCENT = 0.85f;
private final Context mContext;
private final PipTaskOrganizer mPipTaskOrganizer;
@@ -296,9 +299,17 @@ public class PipMotionHelper implements PipAppOpsListener.Callback,
boolean flung, Function0<Unit> after) {
final PointF targetCenter = target.getCenterOnScreen();
- final float desiredWidth = getBounds().width() / 2;
- final float desiredHeight = getBounds().height() / 2;
+ // PIP should fit in the circle
+ final float dismissCircleSize = mContext.getResources().getDimensionPixelSize(
+ R.dimen.dismiss_circle_size);
+ final float width = getBounds().width();
+ final float height = getBounds().height();
+ final float ratio = width / height;
+
+ // Width should be a little smaller than the circle size.
+ final float desiredWidth = dismissCircleSize * DISMISS_CIRCLE_PERCENT;
+ final float desiredHeight = desiredWidth / ratio;
final float destinationX = targetCenter.x - (desiredWidth / 2f);
final float destinationY = targetCenter.y - (desiredHeight / 2f);
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipTouchHandler.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipTouchHandler.java
index f0ea4653c245..b1086c575f49 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipTouchHandler.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipTouchHandler.java
@@ -75,6 +75,7 @@ public class PipTouchHandler {
private final @NonNull PipBoundsState mPipBoundsState;
private final PipUiEventLogger mPipUiEventLogger;
private final PipDismissTargetHandler mPipDismissTargetHandler;
+ private final PipTaskOrganizer mPipTaskOrganizer;
private final ShellExecutor mMainExecutor;
private PipResizeGestureHandler mPipResizeGestureHandler;
@@ -173,6 +174,7 @@ public class PipTouchHandler {
mAccessibilityManager = context.getSystemService(AccessibilityManager.class);
mPipBoundsAlgorithm = pipBoundsAlgorithm;
mPipBoundsState = pipBoundsState;
+ mPipTaskOrganizer = pipTaskOrganizer;
mMenuController = menuController;
mPipUiEventLogger = pipUiEventLogger;
mFloatingContentCoordinator = floatingContentCoordinator;
@@ -799,6 +801,7 @@ public class PipTouchHandler {
mMovementWithinDismiss = touchState.getDownTouchPosition().y
>= mPipBoundsState.getMovementBounds().bottom;
mMotionHelper.setSpringingToTouch(false);
+ mPipDismissTargetHandler.setTaskLeash(mPipTaskOrganizer.getSurfaceControl());
// If the menu is still visible then just poke the menu
// so that it will timeout after the user stops touching it
@@ -847,6 +850,7 @@ public class PipTouchHandler {
@Override
public boolean onUp(PipTouchState touchState) {
mPipDismissTargetHandler.hideDismissTargetMaybe();
+ mPipDismissTargetHandler.setTaskLeash(null);
if (!touchState.isUserInteracting()) {
return false;
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/startingsurface/SplashscreenContentDrawer.java b/libs/WindowManager/Shell/src/com/android/wm/shell/startingsurface/SplashscreenContentDrawer.java
index 093c272193a3..1d37a128da0c 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/startingsurface/SplashscreenContentDrawer.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/startingsurface/SplashscreenContentDrawer.java
@@ -124,9 +124,9 @@ public class SplashscreenContentDrawer {
private void updateDensity() {
mIconSize = mContext.getResources().getDimensionPixelSize(
- com.android.wm.shell.R.dimen.starting_surface_icon_size);
+ com.android.internal.R.dimen.starting_surface_icon_size);
mDefaultIconSize = mContext.getResources().getDimensionPixelSize(
- com.android.wm.shell.R.dimen.default_icon_size);
+ com.android.internal.R.dimen.starting_surface_default_icon_size);
mBrandingImageWidth = mContext.getResources().getDimensionPixelSize(
com.android.wm.shell.R.dimen.starting_surface_brand_image_width);
mBrandingImageHeight = mContext.getResources().getDimensionPixelSize(
@@ -135,7 +135,7 @@ public class SplashscreenContentDrawer {
com.android.wm.shell.R.dimen.starting_surface_exit_animation_window_shift_length);
}
- private int getSystemBGColor() {
+ private static int getSystemBGColor() {
final Context systemContext = ActivityThread.currentApplication();
if (systemContext == null) {
Slog.e(TAG, "System context does not exist!");
@@ -145,17 +145,18 @@ public class SplashscreenContentDrawer {
return res.getColor(com.android.wm.shell.R.color.splash_window_background_default);
}
- private Drawable createDefaultBackgroundDrawable() {
+ private static Drawable createDefaultBackgroundDrawable() {
return new ColorDrawable(getSystemBGColor());
}
- private @ColorInt int peekWindowBGColor(Context context) {
+ /** Extract the window background color from {@code attrs}. */
+ public static int peekWindowBGColor(Context context, SplashScreenWindowAttrs attrs) {
Trace.traceBegin(TRACE_TAG_WINDOW_MANAGER, "peekWindowBGColor");
final Drawable themeBGDrawable;
- if (mTmpAttrs.mWindowBgColor != 0) {
- themeBGDrawable = new ColorDrawable(mTmpAttrs.mWindowBgColor);
- } else if (mTmpAttrs.mWindowBgResId != 0) {
- themeBGDrawable = context.getDrawable(mTmpAttrs.mWindowBgResId);
+ if (attrs.mWindowBgColor != 0) {
+ themeBGDrawable = new ColorDrawable(attrs.mWindowBgColor);
+ } else if (attrs.mWindowBgResId != 0) {
+ themeBGDrawable = context.getDrawable(attrs.mWindowBgResId);
} else {
themeBGDrawable = createDefaultBackgroundDrawable();
Slog.w(TAG, "Window background does not exist, using " + themeBGDrawable);
@@ -165,7 +166,7 @@ public class SplashscreenContentDrawer {
return estimatedWindowBGColor;
}
- private int estimateWindowBGColor(Drawable themeBGDrawable) {
+ private static int estimateWindowBGColor(Drawable themeBGDrawable) {
final DrawableColorTester themeBGTester =
new DrawableColorTester(themeBGDrawable, true /* filterTransparent */);
if (themeBGTester.nonTransparentRatio() == 0) {
@@ -183,7 +184,7 @@ public class SplashscreenContentDrawer {
getWindowAttrs(context, mTmpAttrs);
final StartingWindowViewBuilder builder = new StartingWindowViewBuilder();
- final int themeBGColor = peekWindowBGColor(context);
+ final int themeBGColor = peekWindowBGColor(context, this.mTmpAttrs);
// TODO (b/173975965) Tracking the performance on improved splash screen.
return builder
.setContext(context)
@@ -193,7 +194,11 @@ public class SplashscreenContentDrawer {
.build();
}
- private static void getWindowAttrs(Context context, SplashScreenWindowAttrs attrs) {
+ /**
+ * Get the {@link SplashScreenWindowAttrs} from {@code context} and fill them into
+ * {@code attrs}.
+ */
+ public static void getWindowAttrs(Context context, SplashScreenWindowAttrs attrs) {
final TypedArray typedArray = context.obtainStyledAttributes(
com.android.internal.R.styleable.Window);
attrs.mWindowBgResId = typedArray.getResourceId(R.styleable.Window_windowBackground, 0);
@@ -216,7 +221,8 @@ public class SplashscreenContentDrawer {
}
}
- private static class SplashScreenWindowAttrs {
+ /** The configuration of the splash screen window. */
+ public static class SplashScreenWindowAttrs {
private int mWindowBgResId = 0;
private int mWindowBgColor = Color.TRANSPARENT;
private Drawable mReplaceIcon = null;
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/startingsurface/StartingSurfaceDrawer.java b/libs/WindowManager/Shell/src/com/android/wm/shell/startingsurface/StartingSurfaceDrawer.java
index 1a03ebd4caae..ff91d827b280 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/startingsurface/StartingSurfaceDrawer.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/startingsurface/StartingSurfaceDrawer.java
@@ -29,6 +29,7 @@ import android.content.pm.PackageManager;
import android.content.res.Configuration;
import android.content.res.Resources;
import android.content.res.TypedArray;
+import android.graphics.PixelFormat;
import android.graphics.Rect;
import android.hardware.display.DisplayManager;
import android.os.IBinder;
@@ -215,6 +216,7 @@ public class StartingSurfaceDrawer {
WindowManager.LayoutParams.TYPE_APPLICATION_STARTING);
params.setFitInsetsSides(0);
params.setFitInsetsTypes(0);
+ params.format = PixelFormat.TRANSLUCENT;
int windowFlags = WindowManager.LayoutParams.FLAG_HARDWARE_ACCELERATED
| WindowManager.LayoutParams.FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS
| WindowManager.LayoutParams.FLAG_LAYOUT_IN_SCREEN
@@ -347,8 +349,13 @@ public class StartingSurfaceDrawer {
void makeTaskSnapshotWindow(StartingWindowInfo startingWindowInfo, IBinder appToken,
TaskSnapshot snapshot) {
final int taskId = startingWindowInfo.taskInfo.taskId;
+ // Remove any existing starting window for this task before adding.
+ removeWindowNoAnimate(taskId);
final TaskSnapshotWindow surface = TaskSnapshotWindow.create(startingWindowInfo, appToken,
snapshot, mSplashScreenExecutor, () -> removeWindowNoAnimate(taskId));
+ if (surface == null) {
+ return;
+ }
final StartingWindowRecord tView = new StartingWindowRecord(appToken,
null/* decorView */, surface);
mStartingWindowRecords.put(taskId, tView);
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/startingsurface/TaskSnapshotWindow.java b/libs/WindowManager/Shell/src/com/android/wm/shell/startingsurface/TaskSnapshotWindow.java
index 8147b4775963..acf7f3367d71 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/startingsurface/TaskSnapshotWindow.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/startingsurface/TaskSnapshotWindow.java
@@ -45,6 +45,7 @@ import static com.android.internal.policy.DecorView.STATUS_BAR_COLOR_VIEW_ATTRIB
import static com.android.internal.policy.DecorView.getNavigationBarRect;
import android.annotation.BinderThread;
+import android.annotation.NonNull;
import android.annotation.Nullable;
import android.app.ActivityManager;
import android.app.ActivityManager.TaskDescription;
@@ -141,7 +142,8 @@ public class TaskSnapshotWindow {
private final float[] mTmpFloat9 = new float[9];
static TaskSnapshotWindow create(StartingWindowInfo info, IBinder appToken,
- TaskSnapshot snapshot, ShellExecutor mainExecutor, Runnable clearWindowHandler) {
+ TaskSnapshot snapshot, ShellExecutor splashScreenExecutor,
+ @NonNull Runnable clearWindowHandler) {
final ActivityManager.RunningTaskInfo runningTaskInfo = info.taskInfo;
final int taskId = runningTaskInfo.taskId;
if (DEBUG) {
@@ -208,39 +210,38 @@ public class TaskSnapshotWindow {
final TaskSnapshotWindow snapshotSurface = new TaskSnapshotWindow(
surfaceControl, snapshot, layoutParams.getTitle(), taskDescription, appearance,
windowFlags, windowPrivateFlags, taskBounds, orientation,
- topWindowInsetsState, clearWindowHandler, mainExecutor);
+ topWindowInsetsState, clearWindowHandler, splashScreenExecutor);
final Window window = snapshotSurface.mWindow;
final InsetsState mTmpInsetsState = new InsetsState();
final InputChannel tmpInputChannel = new InputChannel();
- mainExecutor.execute(() -> {
- try {
- Trace.traceBegin(TRACE_TAG_WINDOW_MANAGER, "TaskSnapshot#addToDisplay");
- final int res = session.addToDisplay(window, layoutParams, View.GONE, displayId,
- mTmpInsetsState, tmpInputChannel, mTmpInsetsState, mTempControls);
- Trace.traceEnd(TRACE_TAG_WINDOW_MANAGER);
- if (res < 0) {
- Slog.w(TAG, "Failed to add snapshot starting window res=" + res);
- return;
- }
- } catch (RemoteException e) {
- snapshotSurface.clearWindowSynced();
- }
- window.setOuter(snapshotSurface);
- try {
- Trace.traceBegin(TRACE_TAG_WINDOW_MANAGER, "TaskSnapshot#relayout");
- session.relayout(window, layoutParams, -1, -1, View.VISIBLE, 0, -1,
- tmpFrames, tmpMergedConfiguration, surfaceControl, mTmpInsetsState,
- mTempControls, TMP_SURFACE_SIZE);
- Trace.traceEnd(TRACE_TAG_WINDOW_MANAGER);
- } catch (RemoteException e) {
- snapshotSurface.clearWindowSynced();
+
+ try {
+ Trace.traceBegin(TRACE_TAG_WINDOW_MANAGER, "TaskSnapshot#addToDisplay");
+ final int res = session.addToDisplay(window, layoutParams, View.GONE, displayId,
+ mTmpInsetsState, tmpInputChannel, mTmpInsetsState, mTempControls);
+ Trace.traceEnd(TRACE_TAG_WINDOW_MANAGER);
+ if (res < 0) {
+ Slog.w(TAG, "Failed to add snapshot starting window res=" + res);
+ return null;
}
+ } catch (RemoteException e) {
+ snapshotSurface.clearWindowSynced();
+ }
+ window.setOuter(snapshotSurface);
+ try {
+ Trace.traceBegin(TRACE_TAG_WINDOW_MANAGER, "TaskSnapshot#relayout");
+ session.relayout(window, layoutParams, -1, -1, View.VISIBLE, 0, -1,
+ tmpFrames, tmpMergedConfiguration, surfaceControl, mTmpInsetsState,
+ mTempControls, TMP_SURFACE_SIZE);
+ Trace.traceEnd(TRACE_TAG_WINDOW_MANAGER);
+ } catch (RemoteException e) {
+ snapshotSurface.clearWindowSynced();
+ }
- final Rect systemBarInsets = getSystemBarInsets(tmpFrames.frame, topWindowInsetsState);
- snapshotSurface.setFrames(tmpFrames.frame, systemBarInsets);
- snapshotSurface.drawSnapshot();
- });
+ final Rect systemBarInsets = getSystemBarInsets(tmpFrames.frame, topWindowInsetsState);
+ snapshotSurface.setFrames(tmpFrames.frame, systemBarInsets);
+ snapshotSurface.drawSnapshot();
return snapshotSurface;
}
@@ -469,9 +470,7 @@ public class TaskSnapshotWindow {
* Clear window from drawer, must be post on main executor.
*/
private void clearWindowSynced() {
- if (mClearWindowHandler != null) {
- mClearWindowHandler.run();
- }
+ mSplashScreenExecutor.executeDelayed(mClearWindowHandler, 0);
}
private void reportDrawn() {
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/tasksurfacehelper/TaskSurfaceHelper.java b/libs/WindowManager/Shell/src/com/android/wm/shell/tasksurfacehelper/TaskSurfaceHelper.java
new file mode 100644
index 000000000000..fbbd09faee9b
--- /dev/null
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/tasksurfacehelper/TaskSurfaceHelper.java
@@ -0,0 +1,26 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.wm.shell.tasksurfacehelper;
+
+/**
+ * Interface to communicate with a Task's SurfaceControl.
+ */
+public interface TaskSurfaceHelper {
+
+ /** Sets the METADATA_GAME_MODE for the layer corresponding to the task **/
+ default void setGameModeForTask(int taskId, int gameMode) {}
+}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/tasksurfacehelper/TaskSurfaceHelperController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/tasksurfacehelper/TaskSurfaceHelperController.java
new file mode 100644
index 000000000000..b459b9fcd0b2
--- /dev/null
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/tasksurfacehelper/TaskSurfaceHelperController.java
@@ -0,0 +1,60 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.wm.shell.tasksurfacehelper;
+
+import android.view.SurfaceControl;
+
+import com.android.wm.shell.ShellTaskOrganizer;
+import com.android.wm.shell.common.ShellExecutor;
+
+/**
+ * Intermediary controller that communicates with {@link ShellTaskOrganizer} to send commands
+ * to SurfaceControl.
+ */
+public class TaskSurfaceHelperController {
+
+ private final ShellTaskOrganizer mTaskOrganizer;
+ private final ShellExecutor mMainExecutor;
+ private final TaskSurfaceHelperImpl mImpl = new TaskSurfaceHelperImpl();
+
+ public TaskSurfaceHelperController(ShellTaskOrganizer taskOrganizer,
+ ShellExecutor mainExecutor) {
+ mTaskOrganizer = taskOrganizer;
+ mMainExecutor = mainExecutor;
+ }
+
+ public TaskSurfaceHelper asTaskSurfaceHelper() {
+ return mImpl;
+ }
+
+ /**
+ * Sends a Transaction to set the game mode metadata on the
+ * corresponding SurfaceControl
+ */
+ public void setGameModeForTask(int taskId, int gameMode) {
+ mTaskOrganizer.setSurfaceMetadata(taskId, SurfaceControl.METADATA_GAME_MODE, gameMode);
+ }
+
+ private class TaskSurfaceHelperImpl implements TaskSurfaceHelper {
+ @Override
+ public void setGameModeForTask(int taskId, int gameMode) {
+ mMainExecutor.execute(() -> {
+ TaskSurfaceHelperController.this.setGameModeForTask(taskId, gameMode);
+ });
+ }
+ }
+}
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/bubbles/BubbleDataTest.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/bubbles/BubbleDataTest.java
index 2bb7204c7941..3e3195fe8dc5 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/bubbles/BubbleDataTest.java
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/bubbles/BubbleDataTest.java
@@ -19,9 +19,12 @@ package com.android.wm.shell.bubbles;
import static com.google.common.truth.Truth.assertThat;
import static com.google.common.truth.Truth.assertWithMessage;
+import static junit.framework.Assert.assertNotNull;
import static junit.framework.TestCase.assertEquals;
+import static org.mockito.ArgumentMatchers.any;
import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.never;
import static org.mockito.Mockito.reset;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.verifyZeroInteractions;
@@ -90,6 +93,7 @@ public class BubbleDataTest extends ShellTestCase {
private Bubble mBubbleDismissed;
private BubbleData mBubbleData;
+ private TestableBubblePositioner mPositioner;
@Mock
private TimeSource mTimeSource;
@@ -150,9 +154,9 @@ public class BubbleDataTest extends ShellTestCase {
mMainExecutor);
mBubbleC1 = new Bubble(mEntryC1, mSuppressionListener, mPendingIntentCanceledListener,
mMainExecutor);
- TestableBubblePositioner positioner = new TestableBubblePositioner(mContext,
+ mPositioner = new TestableBubblePositioner(mContext,
mock(WindowManager.class));
- mBubbleData = new BubbleData(getContext(), mBubbleLogger, positioner,
+ mBubbleData = new BubbleData(getContext(), mBubbleLogger, mPositioner,
mMainExecutor);
// Used by BubbleData to set lastAccessedTime
@@ -810,6 +814,61 @@ public class BubbleDataTest extends ShellTestCase {
assertEquals(1, mBubbleData.getOverflowBubbles().size());
}
+ @Test
+ public void test_onMaxBubblesChanged_notExpanded() {
+ mBubbleData.setListener(mListener);
+ mPositioner.setMaxBubbles(5);
+ sendUpdatedEntryAtTime(mEntryA1, 1000);
+ sendUpdatedEntryAtTime(mEntryA2, 2000);
+ sendUpdatedEntryAtTime(mEntryA3, 3000);
+ sendUpdatedEntryAtTime(mEntryB1, 4000);
+ sendUpdatedEntryAtTime(mEntryB2, 5000);
+ mBubbleData.setExpanded(false);
+ reset(mListener);
+
+ mPositioner.setMaxBubbles(3);
+ mBubbleData.onMaxBubblesChanged();
+ verifyUpdateReceived();
+
+ BubbleData.Update update = mUpdateCaptor.getValue();
+ assertThat(update.removedBubbles.get(0)).isEqualTo(
+ Pair.create(mBubbleA1, Bubbles.DISMISS_AGED));
+ assertThat(update.removedBubbles.get(1)).isEqualTo(
+ Pair.create(mBubbleA2, Bubbles.DISMISS_AGED));
+
+ assertNotNull(mBubbleData.getOverflowBubbleWithKey(mBubbleA1.getKey()));
+ assertNotNull(mBubbleData.getOverflowBubbleWithKey(mBubbleA2.getKey()));
+ }
+
+ @Test
+ public void test_onMaxBubblesChanged_expanded() {
+ mBubbleData.setListener(mListener);
+ mPositioner.setMaxBubbles(5);
+ sendUpdatedEntryAtTime(mEntryA1, 1000);
+ sendUpdatedEntryAtTime(mEntryA2, 2000);
+ sendUpdatedEntryAtTime(mEntryA3, 3000);
+ sendUpdatedEntryAtTime(mEntryB1, 4000);
+ sendUpdatedEntryAtTime(mEntryB2, 5000);
+ mBubbleData.setExpanded(true);
+ reset(mListener);
+
+ mPositioner.setMaxBubbles(3);
+ mBubbleData.onMaxBubblesChanged();
+ verify(mListener, never()).applyUpdate(any());
+
+ mBubbleData.setExpanded(false);
+ verifyUpdateReceived();
+
+ BubbleData.Update update = mUpdateCaptor.getValue();
+ assertThat(update.removedBubbles.get(0)).isEqualTo(
+ Pair.create(mBubbleA1, Bubbles.DISMISS_AGED));
+ assertThat(update.removedBubbles.get(1)).isEqualTo(
+ Pair.create(mBubbleA2, Bubbles.DISMISS_AGED));
+
+ assertNotNull(mBubbleData.getOverflowBubbleWithKey(mBubbleA1.getKey()));
+ assertNotNull(mBubbleData.getOverflowBubbleWithKey(mBubbleA2.getKey()));
+ }
+
private void verifyUpdateReceived() {
verify(mListener).applyUpdate(mUpdateCaptor.capture());
reset(mListener);
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/bubbles/BubbleFlyoutViewTest.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/bubbles/BubbleFlyoutViewTest.java
index 69d5244e5ac2..6644eaf28a62 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/bubbles/BubbleFlyoutViewTest.java
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/bubbles/BubbleFlyoutViewTest.java
@@ -57,7 +57,6 @@ public class BubbleFlyoutViewTest extends ShellTestCase {
public void setUp() throws Exception {
MockitoAnnotations.initMocks(this);
- when(mPositioner.getBubbleBitmapSize()).thenReturn(40);
when(mPositioner.getBubbleSize()).thenReturn(60);
mFlyoutMessage = new Bubble.FlyoutMessage();
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/bubbles/TestableBubblePositioner.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/bubbles/TestableBubblePositioner.java
index 96bc5335a32c..9027ca442e2c 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/bubbles/TestableBubblePositioner.java
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/bubbles/TestableBubblePositioner.java
@@ -22,7 +22,10 @@ import android.graphics.Insets;
import android.graphics.Rect;
import android.view.WindowManager;
+import com.android.wm.shell.R;
+
public class TestableBubblePositioner extends BubblePositioner {
+ private int mMaxBubbles;
public TestableBubblePositioner(Context context,
WindowManager windowManager) {
@@ -31,5 +34,15 @@ public class TestableBubblePositioner extends BubblePositioner {
updateInternal(Configuration.ORIENTATION_PORTRAIT,
Insets.of(0, 0, 0, 0),
new Rect(0, 0, 500, 1000));
+ mMaxBubbles = context.getResources().getInteger(R.integer.bubbles_max_rendered);
+ }
+
+ public void setMaxBubbles(int max) {
+ mMaxBubbles = max;
+ }
+
+ @Override
+ public int getMaxBubbles() {
+ return mMaxBubbles;
}
}
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/bubbles/animation/StackAnimationControllerTest.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/bubbles/animation/StackAnimationControllerTest.java
index f36dcbe7bf4b..31fafcaab7cc 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/bubbles/animation/StackAnimationControllerTest.java
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/bubbles/animation/StackAnimationControllerTest.java
@@ -69,7 +69,7 @@ public class StackAnimationControllerTest extends PhysicsAnimationLayoutTestCase
public int getAsInt() {
return mLayout.getChildCount();
}
- }, mock(Runnable.class)));
+ }, mock(Runnable.class), mock(Runnable.class)));
mLayout.setActiveController(mStackController);
addOneMoreThanBubbleLimitBubbles();
mStackOffset = mLayout.getResources().getDimensionPixelSize(R.dimen.bubble_stack_offset);
@@ -307,10 +307,12 @@ public class StackAnimationControllerTest extends PhysicsAnimationLayoutTestCase
TestableStackController(
FloatingContentCoordinator floatingContentCoordinator,
IntSupplier bubbleCountSupplier,
- Runnable onBubbleAnimatedOutAction) {
+ Runnable onBubbleAnimatedOutAction,
+ Runnable onStackAnimationFinished) {
super(floatingContentCoordinator,
bubbleCountSupplier,
onBubbleAnimatedOutAction,
+ onStackAnimationFinished,
new TestableBubblePositioner(mContext, mock(WindowManager.class)));
}
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/tasksurfacehelper/TaskSurfaceHelperControllerTest.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/tasksurfacehelper/TaskSurfaceHelperControllerTest.java
new file mode 100644
index 000000000000..d6142753b48a
--- /dev/null
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/tasksurfacehelper/TaskSurfaceHelperControllerTest.java
@@ -0,0 +1,58 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.wm.shell.tasksurfacehelper;
+
+import static org.mockito.Mockito.verify;
+
+import android.platform.test.annotations.Presubmit;
+import android.testing.AndroidTestingRunner;
+import android.view.SurfaceControl;
+
+import androidx.test.filters.SmallTest;
+
+import com.android.wm.shell.ShellTaskOrganizer;
+import com.android.wm.shell.common.ShellExecutor;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
+
+@Presubmit
+@RunWith(AndroidTestingRunner.class)
+@SmallTest
+public class TaskSurfaceHelperControllerTest {
+ private TaskSurfaceHelperController mTaskSurfaceHelperController;
+ @Mock
+ private ShellTaskOrganizer mMockTaskOrganizer;
+ @Mock
+ private ShellExecutor mMockShellExecutor;
+
+ @Before
+ public void setUp() throws Exception {
+ MockitoAnnotations.initMocks(this);
+ mTaskSurfaceHelperController = new TaskSurfaceHelperController(
+ mMockTaskOrganizer, mMockShellExecutor);
+ }
+
+ @Test
+ public void testSetGameModeForTask() {
+ mTaskSurfaceHelperController.setGameModeForTask(/*taskId*/1, /*gameMode*/3);
+ verify(mMockTaskOrganizer).setSurfaceMetadata(1, SurfaceControl.METADATA_GAME_MODE, 3);
+ }
+}
diff --git a/libs/androidfw/ApkAssets.cpp b/libs/androidfw/ApkAssets.cpp
index 26d836328b54..2beb33abe782 100755
--- a/libs/androidfw/ApkAssets.cpp
+++ b/libs/androidfw/ApkAssets.cpp
@@ -40,7 +40,7 @@ ApkAssets::ApkAssets(std::unique_ptr<Asset> resources_asset,
loaded_idmap_(std::move(loaded_idmap)) {}
std::unique_ptr<ApkAssets> ApkAssets::Load(const std::string& path, package_property_t flags) {
- return Load(ZipAssetsProvider::Create(path), flags);
+ return Load(ZipAssetsProvider::Create(path, flags), flags);
}
std::unique_ptr<ApkAssets> ApkAssets::LoadFromFd(base::unique_fd fd,
@@ -91,7 +91,7 @@ std::unique_ptr<ApkAssets> ApkAssets::LoadOverlay(const std::string& idmap_path,
overlay_assets = EmptyAssetsProvider::Create(overlay_path);
} else {
// The overlay should be an APK.
- overlay_assets = ZipAssetsProvider::Create(overlay_path);
+ overlay_assets = ZipAssetsProvider::Create(overlay_path, flags);
}
if (overlay_assets == nullptr) {
return {};
diff --git a/libs/androidfw/Asset.cpp b/libs/androidfw/Asset.cpp
index 4fbe4a3efbdd..43a70c176a83 100644
--- a/libs/androidfw/Asset.cpp
+++ b/libs/androidfw/Asset.cpp
@@ -594,7 +594,12 @@ void _FileAsset::close(void)
*/
const void* _FileAsset::getBuffer(bool aligned)
{
- return getIncFsBuffer(aligned).unsafe_ptr();
+ auto buffer = getIncFsBuffer(aligned);
+ if (mBuf != NULL)
+ return mBuf;
+ if (!buffer.convert<uint8_t>().verify(mLength))
+ return NULL;
+ return buffer.unsafe_ptr();
}
incfs::map_ptr<void> _FileAsset::getIncFsBuffer(bool aligned)
diff --git a/libs/androidfw/AssetsProvider.cpp b/libs/androidfw/AssetsProvider.cpp
index 6c7a25307247..bce34d37c90b 100644
--- a/libs/androidfw/AssetsProvider.cpp
+++ b/libs/androidfw/AssetsProvider.cpp
@@ -85,12 +85,14 @@ const std::string& ZipAssetsProvider::PathOrDebugName::GetDebugName() const {
}
ZipAssetsProvider::ZipAssetsProvider(ZipArchiveHandle handle, PathOrDebugName&& path,
- time_t last_mod_time)
+ package_property_t flags, time_t last_mod_time)
: zip_handle_(handle, ::CloseArchive),
name_(std::forward<PathOrDebugName>(path)),
+ flags_(flags),
last_mod_time_(last_mod_time) {}
-std::unique_ptr<ZipAssetsProvider> ZipAssetsProvider::Create(std::string path) {
+std::unique_ptr<ZipAssetsProvider> ZipAssetsProvider::Create(std::string path,
+ package_property_t flags) {
ZipArchiveHandle handle;
if (int32_t result = OpenArchive(path.c_str(), &handle); result != 0) {
LOG(ERROR) << "Failed to open APK '" << path << "': " << ::ErrorCodeString(result);
@@ -109,11 +111,12 @@ std::unique_ptr<ZipAssetsProvider> ZipAssetsProvider::Create(std::string path) {
return std::unique_ptr<ZipAssetsProvider>(
new ZipAssetsProvider(handle, PathOrDebugName{std::move(path),
- true /* is_path */}, sb.st_mtime));
+ true /* is_path */}, flags, sb.st_mtime));
}
std::unique_ptr<ZipAssetsProvider> ZipAssetsProvider::Create(base::unique_fd fd,
std::string friendly_name,
+ package_property_t flags,
off64_t offset,
off64_t len) {
ZipArchiveHandle handle;
@@ -140,7 +143,7 @@ std::unique_ptr<ZipAssetsProvider> ZipAssetsProvider::Create(base::unique_fd fd,
return std::unique_ptr<ZipAssetsProvider>(
new ZipAssetsProvider(handle, PathOrDebugName{std::move(friendly_name),
- false /* is_path */}, sb.st_mtime));
+ false /* is_path */}, flags, sb.st_mtime));
}
std::unique_ptr<Asset> ZipAssetsProvider::OpenInternal(const std::string& path,
@@ -161,10 +164,11 @@ std::unique_ptr<Asset> ZipAssetsProvider::OpenInternal(const std::string& path,
const int fd = GetFileDescriptor(zip_handle_.get());
const off64_t fd_offset = GetFileDescriptorOffset(zip_handle_.get());
+ const bool incremental_hardening = (flags_ & PROPERTY_DISABLE_INCREMENTAL_HARDENING) == 0U;
incfs::IncFsFileMap asset_map;
if (entry.method == kCompressDeflated) {
if (!asset_map.Create(fd, entry.offset + fd_offset, entry.compressed_length,
- name_.GetDebugName().c_str())) {
+ name_.GetDebugName().c_str(), incremental_hardening)) {
LOG(ERROR) << "Failed to mmap file '" << path << "' in APK '" << name_.GetDebugName()
<< "'";
return {};
@@ -181,7 +185,7 @@ std::unique_ptr<Asset> ZipAssetsProvider::OpenInternal(const std::string& path,
}
if (!asset_map.Create(fd, entry.offset + fd_offset, entry.uncompressed_length,
- name_.GetDebugName().c_str())) {
+ name_.GetDebugName().c_str(), incremental_hardening)) {
LOG(ERROR) << "Failed to mmap file '" << path << "' in APK '" << name_.GetDebugName() << "'";
return {};
}
diff --git a/libs/androidfw/TEST_MAPPING b/libs/androidfw/TEST_MAPPING
index 766714cd7694..9ebc9969a730 100644
--- a/libs/androidfw/TEST_MAPPING
+++ b/libs/androidfw/TEST_MAPPING
@@ -4,4 +4,4 @@
"name": "CtsResourcesLoaderTests"
}
]
-} \ No newline at end of file
+}
diff --git a/libs/androidfw/include/androidfw/Asset.h b/libs/androidfw/include/androidfw/Asset.h
index 40c91a6fcbf5..19febcdee77e 100644
--- a/libs/androidfw/include/androidfw/Asset.h
+++ b/libs/androidfw/include/androidfw/Asset.h
@@ -91,7 +91,8 @@ public:
* Get a pointer to a buffer with the entire contents of the file.
* If `aligned` is true, the buffer data will be aligned to a 4-byte boundary.
*
- * Use this function if the asset can never reside on IncFs.
+ * If the buffer contents reside on IncFs, the entire buffer will be scanned to ensure the
+ * presence of the data before returning a raw pointer to the buffer.
*/
virtual const void* getBuffer(bool aligned) = 0;
@@ -99,7 +100,8 @@ public:
* Get a incfs::map_ptr<void> to a buffer with the entire contents of the file.
* If `aligned` is true, the buffer data will be aligned to a 4-byte boundary.
*
- * Use this function if the asset can potentially reside on IncFs.
+ * Use this function if the asset can potentially reside on IncFs to avoid the scanning of the
+ * buffer contents done in Asset::getBuffer.
*/
virtual incfs::map_ptr<void> getIncFsBuffer(bool aligned) = 0;
diff --git a/libs/androidfw/include/androidfw/AssetsProvider.h b/libs/androidfw/include/androidfw/AssetsProvider.h
index ec51c65053bf..966ec74c1786 100644
--- a/libs/androidfw/include/androidfw/AssetsProvider.h
+++ b/libs/androidfw/include/androidfw/AssetsProvider.h
@@ -80,9 +80,12 @@ struct AssetsProvider {
// Supplies assets from a zip archive.
struct ZipAssetsProvider : public AssetsProvider {
- static std::unique_ptr<ZipAssetsProvider> Create(std::string path);
+ static std::unique_ptr<ZipAssetsProvider> Create(std::string path,
+ package_property_t flags);
+
static std::unique_ptr<ZipAssetsProvider> Create(base::unique_fd fd,
std::string friendly_name,
+ package_property_t flags,
off64_t offset = 0,
off64_t len = kUnknownLength);
@@ -101,7 +104,8 @@ struct ZipAssetsProvider : public AssetsProvider {
private:
struct PathOrDebugName;
- ZipAssetsProvider(ZipArchive* handle, PathOrDebugName&& path, time_t last_mod_time);
+ ZipAssetsProvider(ZipArchive* handle, PathOrDebugName&& path, package_property_t flags,
+ time_t last_mod_time);
struct PathOrDebugName {
PathOrDebugName(std::string&& value, bool is_path);
@@ -119,6 +123,7 @@ struct ZipAssetsProvider : public AssetsProvider {
std::unique_ptr<ZipArchive, void (*)(ZipArchive*)> zip_handle_;
PathOrDebugName name_;
+ package_property_t flags_;
time_t last_mod_time_;
};
diff --git a/libs/androidfw/include/androidfw/LoadedArsc.h b/libs/androidfw/include/androidfw/LoadedArsc.h
index 9bbdede56293..b3d6a4dcb955 100644
--- a/libs/androidfw/include/androidfw/LoadedArsc.h
+++ b/libs/androidfw/include/androidfw/LoadedArsc.h
@@ -92,6 +92,10 @@ enum : package_property_t {
// The package is a RRO.
PROPERTY_OVERLAY = 1U << 3U,
+
+ // The apk assets is owned by the application running in this process and incremental crash
+ // protections for this APK must be disabled.
+ PROPERTY_DISABLE_INCREMENTAL_HARDENING = 1U << 4U,
};
struct OverlayableInfo {
diff --git a/libs/hwui/AutoBackendTextureRelease.cpp b/libs/hwui/AutoBackendTextureRelease.cpp
index 33264d5d5c86..ef5eacbdb4ad 100644
--- a/libs/hwui/AutoBackendTextureRelease.cpp
+++ b/libs/hwui/AutoBackendTextureRelease.cpp
@@ -89,5 +89,27 @@ void AutoBackendTextureRelease::newBufferContent(GrDirectContext* context) {
}
}
+void AutoBackendTextureRelease::releaseQueueOwnership(GrDirectContext* context) {
+ if (!context) {
+ return;
+ }
+
+ LOG_ALWAYS_FATAL_IF(Properties::getRenderPipelineType() != RenderPipelineType::SkiaVulkan);
+ if (mBackendTexture.isValid()) {
+ // Passing in VK_IMAGE_LAYOUT_UNDEFINED means we keep the old layout.
+ GrBackendSurfaceMutableState newState(VK_IMAGE_LAYOUT_UNDEFINED,
+ VK_QUEUE_FAMILY_FOREIGN_EXT);
+
+ // The unref for this ref happens in the releaseProc passed into setBackendTextureState. The
+ // releaseProc callback will be made when the work to set the new state has finished on the
+ // gpu.
+ ref();
+ // Note that we don't have an explicit call to set the backend texture back onto the
+ // graphics queue when we use the VkImage again. Internally, Skia will notice that the image
+ // is not on the graphics queue and will do the transition automatically.
+ context->setBackendTextureState(mBackendTexture, newState, nullptr, releaseProc, this);
+ }
+}
+
} /* namespace uirenderer */
} /* namespace android */
diff --git a/libs/hwui/AutoBackendTextureRelease.h b/libs/hwui/AutoBackendTextureRelease.h
index 06f51fcd1105..c9bb767a3185 100644
--- a/libs/hwui/AutoBackendTextureRelease.h
+++ b/libs/hwui/AutoBackendTextureRelease.h
@@ -49,6 +49,8 @@ public:
void newBufferContent(GrDirectContext* context);
+ void releaseQueueOwnership(GrDirectContext* context);
+
private:
// The only way to invoke dtor is with unref, when mUsageCount is 0.
~AutoBackendTextureRelease() {}
diff --git a/libs/hwui/DeferredLayerUpdater.cpp b/libs/hwui/DeferredLayerUpdater.cpp
index 6589dbd50cf7..8d112d1c64bf 100644
--- a/libs/hwui/DeferredLayerUpdater.cpp
+++ b/libs/hwui/DeferredLayerUpdater.cpp
@@ -76,6 +76,9 @@ void DeferredLayerUpdater::destroyLayer() {
mLayer = nullptr;
+ for (auto& [index, slot] : mImageSlots) {
+ slot.clear(mRenderState.getRenderThread().getGrContext());
+ }
mImageSlots.clear();
}
@@ -89,31 +92,39 @@ void DeferredLayerUpdater::setPaint(const SkPaint* paint) {
}
}
-static status_t createReleaseFence(bool useFenceSync, EGLSyncKHR* eglFence, EGLDisplay* display,
- int* releaseFence, void* handle) {
+status_t DeferredLayerUpdater::createReleaseFence(bool useFenceSync, EGLSyncKHR* eglFence,
+ EGLDisplay* display, int* releaseFence,
+ void* handle) {
*display = EGL_NO_DISPLAY;
- RenderState* renderState = (RenderState*)handle;
+ DeferredLayerUpdater* dlu = (DeferredLayerUpdater*)handle;
+ RenderState& renderState = dlu->mRenderState;
status_t err;
if (Properties::getRenderPipelineType() == RenderPipelineType::SkiaGL) {
- EglManager& eglManager = renderState->getRenderThread().eglManager();
+ EglManager& eglManager = renderState.getRenderThread().eglManager();
*display = eglManager.eglDisplay();
err = eglManager.createReleaseFence(useFenceSync, eglFence, releaseFence);
} else {
- err = renderState->getRenderThread().vulkanManager().createReleaseFence(
- releaseFence, renderState->getRenderThread().getGrContext());
+ int previousSlot = dlu->mCurrentSlot;
+ if (previousSlot != -1) {
+ dlu->mImageSlots[previousSlot].releaseQueueOwnership(
+ renderState.getRenderThread().getGrContext());
+ }
+ err = renderState.getRenderThread().vulkanManager().createReleaseFence(
+ releaseFence, renderState.getRenderThread().getGrContext());
}
return err;
}
-static status_t fenceWait(int fence, void* handle) {
+status_t DeferredLayerUpdater::fenceWait(int fence, void* handle) {
// Wait on the producer fence for the buffer to be ready.
status_t err;
- RenderState* renderState = (RenderState*)handle;
+ DeferredLayerUpdater* dlu = (DeferredLayerUpdater*)handle;
+ RenderState& renderState = dlu->mRenderState;
if (Properties::getRenderPipelineType() == RenderPipelineType::SkiaGL) {
- err = renderState->getRenderThread().eglManager().fenceWait(fence);
+ err = renderState.getRenderThread().eglManager().fenceWait(fence);
} else {
- err = renderState->getRenderThread().vulkanManager().fenceWait(
- fence, renderState->getRenderThread().getGrContext());
+ err = renderState.getRenderThread().vulkanManager().fenceWait(
+ fence, renderState.getRenderThread().getGrContext());
}
return err;
}
@@ -143,9 +154,10 @@ void DeferredLayerUpdater::apply() {
// cannot tell which mode it is in.
AHardwareBuffer* hardwareBuffer = ASurfaceTexture_dequeueBuffer(
mSurfaceTexture.get(), &slot, &dataspace, transformMatrix, &newContent,
- createReleaseFence, fenceWait, &mRenderState);
+ createReleaseFence, fenceWait, this);
if (hardwareBuffer) {
+ mCurrentSlot = slot;
sk_sp<SkImage> layerImage = mImageSlots[slot].createIfNeeded(
hardwareBuffer, dataspace, newContent,
mRenderState.getRenderThread().getGrContext());
@@ -193,7 +205,7 @@ sk_sp<SkImage> DeferredLayerUpdater::ImageSlot::createIfNeeded(AHardwareBuffer*
if (!mTextureRelease || !mTextureRelease->getImage().get() || dataspace != mDataspace ||
forceCreate || mBuffer != buffer) {
if (buffer != mBuffer) {
- clear();
+ clear(context);
}
if (!buffer) {
@@ -213,8 +225,11 @@ sk_sp<SkImage> DeferredLayerUpdater::ImageSlot::createIfNeeded(AHardwareBuffer*
return mTextureRelease ? mTextureRelease->getImage() : nullptr;
}
-void DeferredLayerUpdater::ImageSlot::clear() {
+void DeferredLayerUpdater::ImageSlot::clear(GrDirectContext* context) {
if (mTextureRelease) {
+ if (Properties::getRenderPipelineType() == RenderPipelineType::SkiaVulkan) {
+ this->releaseQueueOwnership(context);
+ }
// The following unref counteracts the initial mUsageCount of 1, set by default initializer.
mTextureRelease->unref(true);
mTextureRelease = nullptr;
@@ -223,5 +238,12 @@ void DeferredLayerUpdater::ImageSlot::clear() {
mBuffer = nullptr;
}
+void DeferredLayerUpdater::ImageSlot::releaseQueueOwnership(GrDirectContext* context) {
+ LOG_ALWAYS_FATAL_IF(Properties::getRenderPipelineType() != RenderPipelineType::SkiaVulkan);
+ if (mTextureRelease) {
+ mTextureRelease->releaseQueueOwnership(context);
+ }
+}
+
} /* namespace uirenderer */
} /* namespace android */
diff --git a/libs/hwui/DeferredLayerUpdater.h b/libs/hwui/DeferredLayerUpdater.h
index 6731e9c428d6..8f79c4ec97b8 100644
--- a/libs/hwui/DeferredLayerUpdater.h
+++ b/libs/hwui/DeferredLayerUpdater.h
@@ -20,9 +20,12 @@
#include <SkImage.h>
#include <SkMatrix.h>
#include <android/hardware_buffer.h>
-#include <cutils/compiler.h>
#include <android/surface_texture.h>
+#include <cutils/compiler.h>
+#include <utils/Errors.h>
+#include <EGL/egl.h>
+#include <EGL/eglext.h>
#include <map>
#include <memory>
@@ -103,13 +106,16 @@ private:
*/
class ImageSlot {
public:
- ~ImageSlot() { clear(); }
+ ~ImageSlot() {}
sk_sp<SkImage> createIfNeeded(AHardwareBuffer* buffer, android_dataspace dataspace,
bool forceCreate, GrDirectContext* context);
+ void releaseQueueOwnership(GrDirectContext* context);
+
+ void clear(GrDirectContext* context);
+
private:
- void clear();
// the dataspace associated with the current image
android_dataspace mDataspace = HAL_DATASPACE_UNKNOWN;
@@ -123,6 +129,10 @@ private:
AutoBackendTextureRelease* mTextureRelease = nullptr;
};
+ static status_t createReleaseFence(bool useFenceSync, EGLSyncKHR* eglFence, EGLDisplay* display,
+ int* releaseFence, void* handle);
+ static status_t fenceWait(int fence, void* handle);
+
/**
* DeferredLayerUpdater stores the SkImages that have been allocated by the BufferQueue
* for each buffer slot.
@@ -142,6 +152,7 @@ private:
SkMatrix* mTransform;
bool mGLContextAttached;
bool mUpdateTexImage;
+ int mCurrentSlot = -1;
Layer* mLayer;
};
diff --git a/libs/hwui/jni/fonts/Font.cpp b/libs/hwui/jni/fonts/Font.cpp
index 5a972f56ea87..bd3b7c93466c 100644
--- a/libs/hwui/jni/fonts/Font.cpp
+++ b/libs/hwui/jni/fonts/Font.cpp
@@ -35,6 +35,7 @@
#include <minikin/FontFamily.h>
#include <minikin/FontFileParser.h>
#include <minikin/LocaleList.h>
+#include <minikin/SystemFonts.h>
#include <ui/FatVector.h>
#include <memory>
@@ -282,6 +283,22 @@ static jint Font_getSourceId(CRITICAL_JNI_PARAMS_COMMA jlong fontPtr) {
return font->font->typeface()->GetSourceId();
}
+static jlongArray Font_getAvailableFontSet(JNIEnv* env, jobject) {
+ std::vector<jlong> refArray;
+ minikin::SystemFonts::getFontSet(
+ [&refArray](const std::vector<std::shared_ptr<minikin::Font>>& fontSet) {
+ refArray.reserve(fontSet.size());
+ for (const auto& font : fontSet) {
+ std::shared_ptr<minikin::Font> fontRef = font;
+ refArray.push_back(
+ reinterpret_cast<jlong>(new FontWrapper(std::move(fontRef))));
+ }
+ });
+ jlongArray r = env->NewLongArray(refArray.size());
+ env->SetLongArrayRegion(r, 0, refArray.size(), refArray.data());
+ return r;
+}
+
// Fast Native
static jlong FontFileUtil_getFontRevision(JNIEnv* env, jobject, jobject buffer, jint index) {
NPE_CHECK_RETURN_ZERO(env, buffer);
@@ -373,6 +390,9 @@ static const JNINativeMethod gFontMethods[] = {
{"nGetAxisCount", "(J)I", (void*)Font_getAxisCount},
{"nGetAxisInfo", "(JI)J", (void*)Font_getAxisInfo},
{"nGetSourceId", "(J)I", (void*)Font_getSourceId},
+
+ // System font accessors
+ {"nGetAvailableFontSet", "()[J", (void*)Font_getAvailableFontSet},
};
static const JNINativeMethod gFontFileUtilMethods[] = {
diff --git a/libs/hwui/renderthread/CanvasContext.cpp b/libs/hwui/renderthread/CanvasContext.cpp
index d3173056065c..a0d93e928876 100644
--- a/libs/hwui/renderthread/CanvasContext.cpp
+++ b/libs/hwui/renderthread/CanvasContext.cpp
@@ -680,6 +680,7 @@ void CanvasContext::onSurfaceStatsAvailable(void* context, ASurfaceControl* cont
frameInfo->set(FrameInfoIndex::FrameCompleted) = std::max(gpuCompleteTime,
frameInfo->get(FrameInfoIndex::SwapBuffersCompleted));
frameInfo->set(FrameInfoIndex::GpuCompleted) = gpuCompleteTime;
+ std::lock_guard(instance->mFrameMetricsReporterMutex);
instance->mJankTracker.finishFrame(*frameInfo, instance->mFrameMetricsReporter);
}
}
diff --git a/libs/hwui/renderthread/CanvasContext.h b/libs/hwui/renderthread/CanvasContext.h
index 4f8e4ca7f23a..6f90e81e51b0 100644
--- a/libs/hwui/renderthread/CanvasContext.h
+++ b/libs/hwui/renderthread/CanvasContext.h
@@ -170,6 +170,7 @@ public:
if (mFrameMetricsReporter.get() != nullptr) {
mFrameMetricsReporter->removeObserver(observer);
if (!mFrameMetricsReporter->hasObservers()) {
+ std::lock_guard lock(mFrameMetricsReporterMutex);
mFrameMetricsReporter.reset(nullptr);
}
}
@@ -295,6 +296,7 @@ private:
JankTracker mJankTracker;
FrameInfoVisualizer mProfiler;
std::unique_ptr<FrameMetricsReporter> mFrameMetricsReporter;
+ std::mutex mFrameMetricsReporterMutex;
std::set<RenderNode*> mPrefetchedLayers;
diff --git a/libs/usb/Android.bp b/libs/usb/Android.bp
index edc847487dcb..e685a73e4249 100644
--- a/libs/usb/Android.bp
+++ b/libs/usb/Android.bp
@@ -28,4 +28,5 @@ java_sdk_library {
name: "com.android.future.usb.accessory",
srcs: ["src/**/*.java"],
api_packages: ["com.android.future.usb"],
+ dist_group: "android",
}