diff options
Diffstat (limited to 'libs')
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", } |