summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--apct-tests/perftests/core/src/android/os/TracePerfTest.java4
-rw-r--r--boot/boot-image-profile-extra.txt8
-rw-r--r--core/api/current.txt4
-rw-r--r--core/api/module-lib-current.txt10
-rw-r--r--core/api/test-current.txt1
-rw-r--r--core/java/android/app/ActivityManagerInternal.java5
-rw-r--r--core/java/android/app/IActivityTaskManager.aidl1
-rw-r--r--core/java/android/app/Instrumentation.java13
-rw-r--r--core/java/android/app/Notification.java120
-rw-r--r--core/java/android/app/admin/DevicePolicyManager.java17
-rw-r--r--core/java/android/app/supervision/flags.aconfig8
-rw-r--r--core/java/android/content/ContentResolver.java6
-rw-r--r--core/java/android/content/res/Resources.java2
-rw-r--r--core/java/android/content/res/ResourcesImpl.java2
-rw-r--r--core/java/android/content/res/TypedArray.java1
-rw-r--r--core/java/android/graphics/fonts/FontFamilyUpdateRequest.java1
-rw-r--r--core/java/android/graphics/fonts/FontFileUpdateRequest.java1
-rw-r--r--core/java/android/hardware/contexthub/HubEndpoint.java11
-rw-r--r--core/java/android/hardware/contexthub/HubEndpointSession.java1
-rw-r--r--core/java/android/hardware/display/DisplayManager.java74
-rw-r--r--core/java/android/hardware/display/DisplayManagerGlobal.java103
-rw-r--r--core/java/android/inputmethodservice/InputMethodService.java2
-rw-r--r--core/java/android/os/BundleMerger.java48
-rw-r--r--core/java/android/os/PerfettoTrace.java20
-rw-r--r--core/java/android/os/PerfettoTrackEventExtra.java599
-rw-r--r--core/java/android/os/vibrator/flags.aconfig10
-rw-r--r--core/java/android/permission/flags.aconfig9
-rw-r--r--core/java/android/provider/Settings.java10
-rw-r--r--core/java/android/text/AlteredCharSequence.java1
-rw-r--r--core/java/android/text/AndroidBidi.java1
-rw-r--r--core/java/android/text/AndroidCharacter.java1
-rw-r--r--core/java/android/text/Annotation.java1
-rw-r--r--core/java/android/text/AutoGrowArray.java1
-rw-r--r--core/java/android/text/AutoText.java1
-rw-r--r--core/java/android/text/BidiFormatter.java1
-rw-r--r--core/java/android/text/BoringLayout.java1
-rw-r--r--core/java/android/text/CharSequenceCharacterIterator.java1
-rw-r--r--core/java/android/text/ClipboardManager.java1
-rw-r--r--core/java/android/text/DynamicLayout.java1
-rw-r--r--core/java/android/text/Editable.java1
-rw-r--r--core/java/android/text/Emoji.java1
-rw-r--r--core/java/android/text/EmojiConsistency.java1
-rw-r--r--core/java/android/text/FontConfig.java1
-rw-r--r--core/java/android/text/GetChars.java1
-rw-r--r--core/java/android/text/GraphemeClusterSegmentFinder.java1
-rw-r--r--core/java/android/text/GraphicsOperations.java1
-rw-r--r--core/java/android/text/Highlights.java1
-rw-r--r--core/java/android/text/Html.java41
-rw-r--r--core/java/android/text/Hyphenator.java1
-rw-r--r--core/java/android/text/InputFilter.java1
-rw-r--r--core/java/android/text/InputType.java1
-rw-r--r--core/java/android/text/Layout.java1
-rw-r--r--core/java/android/text/LoginFilter.java1
-rw-r--r--core/java/android/text/MeasuredParagraph.java1
-rw-r--r--core/java/android/text/NoCopySpan.java1
-rw-r--r--core/java/android/text/PackedIntVector.java1
-rw-r--r--core/java/android/text/PackedObjectVector.java1
-rw-r--r--core/java/android/text/ParcelableSpan.java1
-rw-r--r--core/java/android/text/PrecomputedText.java1
-rw-r--r--core/java/android/text/SegmentFinder.java1
-rw-r--r--core/java/android/text/Selection.java1
-rw-r--r--core/java/android/text/SpanColors.java1
-rw-r--r--core/java/android/text/SpanSet.java1
-rw-r--r--core/java/android/text/SpanWatcher.java1
-rw-r--r--core/java/android/text/Spannable.java1
-rw-r--r--core/java/android/text/SpannableString.java1
-rw-r--r--core/java/android/text/SpannableStringBuilder.java1
-rw-r--r--core/java/android/text/SpannableStringInternal.java1
-rw-r--r--core/java/android/text/Spanned.java1
-rw-r--r--core/java/android/text/SpannedString.java1
-rw-r--r--core/java/android/text/StaticLayout.java1
-rw-r--r--core/java/android/text/TextDirectionHeuristic.java1
-rw-r--r--core/java/android/text/TextDirectionHeuristics.java1
-rw-r--r--core/java/android/text/TextLine.java1
-rw-r--r--core/java/android/text/TextPaint.java1
-rw-r--r--core/java/android/text/TextShaper.java1
-rw-r--r--core/java/android/text/TextUtils.java44
-rw-r--r--core/java/android/text/TextWatcher.java1
-rw-r--r--core/java/android/text/WordSegmentFinder.java1
-rw-r--r--core/java/android/text/format/DateFormat.java1
-rw-r--r--core/java/android/text/format/DateIntervalFormat.java1
-rw-r--r--core/java/android/text/format/DateTimeFormat.java1
-rw-r--r--core/java/android/text/format/DateUtils.java1
-rw-r--r--core/java/android/text/format/DateUtilsBridge.java1
-rw-r--r--core/java/android/text/format/Formatter.java1
-rw-r--r--core/java/android/text/format/RelativeDateTimeFormatter.java1
-rw-r--r--core/java/android/text/format/Time.java1
-rw-r--r--core/java/android/text/format/TimeFormatter.java1
-rw-r--r--core/java/android/text/format/TimeMigrationUtils.java1
-rw-r--r--core/java/android/text/method/AllCapsTransformationMethod.java1
-rw-r--r--core/java/android/text/method/ArrowKeyMovementMethod.java1
-rw-r--r--core/java/android/text/method/BaseKeyListener.java1
-rw-r--r--core/java/android/text/method/BaseMovementMethod.java1
-rw-r--r--core/java/android/text/method/CharacterPickerDialog.java1
-rw-r--r--core/java/android/text/method/DateKeyListener.java1
-rw-r--r--core/java/android/text/method/DateTimeKeyListener.java1
-rw-r--r--core/java/android/text/method/DialerKeyListener.java1
-rw-r--r--core/java/android/text/method/DigitsKeyListener.java1
-rw-r--r--core/java/android/text/method/HideReturnsTransformationMethod.java1
-rw-r--r--core/java/android/text/method/InsertModeTransformationMethod.java1
-rw-r--r--core/java/android/text/method/KeyListener.java1
-rw-r--r--core/java/android/text/method/LinkMovementMethod.java1
-rw-r--r--core/java/android/text/method/MetaKeyKeyListener.java1
-rw-r--r--core/java/android/text/method/MovementMethod.java1
-rw-r--r--core/java/android/text/method/MultiTapKeyListener.java1
-rw-r--r--core/java/android/text/method/NumberKeyListener.java1
-rw-r--r--core/java/android/text/method/OffsetMapping.java1
-rw-r--r--core/java/android/text/method/PasswordTransformationMethod.java1
-rw-r--r--core/java/android/text/method/QwertyKeyListener.java1
-rw-r--r--core/java/android/text/method/ReplacementTransformationMethod.java1
-rw-r--r--core/java/android/text/method/ScrollingMovementMethod.java1
-rw-r--r--core/java/android/text/method/SingleLineTransformationMethod.java1
-rw-r--r--core/java/android/text/method/TextKeyListener.java1
-rw-r--r--core/java/android/text/method/TimeKeyListener.java1
-rw-r--r--core/java/android/text/method/Touch.java1
-rw-r--r--core/java/android/text/method/TransformationMethod.java1
-rw-r--r--core/java/android/text/method/TransformationMethod2.java1
-rw-r--r--core/java/android/text/method/TranslationTransformationMethod.java1
-rw-r--r--core/java/android/text/method/WordIterator.java1
-rw-r--r--core/java/android/text/style/AbsoluteSizeSpan.java1
-rw-r--r--core/java/android/text/style/AccessibilityClickableSpan.java1
-rw-r--r--core/java/android/text/style/AccessibilityReplacementSpan.java1
-rw-r--r--core/java/android/text/style/AccessibilityURLSpan.java1
-rw-r--r--core/java/android/text/style/AlignmentSpan.java1
-rw-r--r--core/java/android/text/style/BackgroundColorSpan.java1
-rw-r--r--core/java/android/text/style/BulletSpan.java1
-rw-r--r--core/java/android/text/style/CharacterStyle.java1
-rw-r--r--core/java/android/text/style/ClickableSpan.java1
-rw-r--r--core/java/android/text/style/ForegroundColorSpan.java1
-rw-r--r--core/java/android/text/style/IconMarginSpan.java1
-rw-r--r--core/java/android/text/style/LeadingMarginSpan.java1
-rw-r--r--core/java/android/text/style/LineBackgroundSpan.java1
-rw-r--r--core/java/android/text/style/LineBreakConfigSpan.java1
-rw-r--r--core/java/android/text/style/LineHeightSpan.java1
-rw-r--r--core/java/android/text/style/LocaleSpan.java1
-rw-r--r--core/java/android/text/style/MaskFilterSpan.java1
-rw-r--r--core/java/android/text/style/MetricAffectingSpan.java1
-rw-r--r--core/java/android/text/style/NoWritingToolsSpan.java1
-rw-r--r--core/java/android/text/style/ParagraphStyle.java1
-rw-r--r--core/java/android/text/style/QuoteSpan.java1
-rw-r--r--core/java/android/text/style/RasterizerSpan.java1
-rw-r--r--core/java/android/text/style/RelativeSizeSpan.java1
-rw-r--r--core/java/android/text/style/ReplacementSpan.java1
-rw-r--r--core/java/android/text/style/ScaleXSpan.java1
-rw-r--r--core/java/android/text/style/SpanUtils.java1
-rw-r--r--core/java/android/text/style/SpellCheckSpan.java1
-rw-r--r--core/java/android/text/style/StrikethroughSpan.java1
-rw-r--r--core/java/android/text/style/StyleSpan.java1
-rw-r--r--core/java/android/text/style/SubscriptSpan.java1
-rw-r--r--core/java/android/text/style/SuggestionRangeSpan.java1
-rw-r--r--core/java/android/text/style/SuggestionSpan.java1
-rw-r--r--core/java/android/text/style/SuperscriptSpan.java1
-rw-r--r--core/java/android/text/style/TabStopSpan.java1
-rw-r--r--core/java/android/text/style/TextAppearanceSpan.java1
-rw-r--r--core/java/android/text/style/TtsSpan.java1
-rw-r--r--core/java/android/text/style/TypefaceSpan.java1
-rw-r--r--core/java/android/text/style/URLSpan.java1
-rw-r--r--core/java/android/text/style/UnderlineSpan.java1
-rw-r--r--core/java/android/text/style/UpdateAppearance.java1
-rw-r--r--core/java/android/text/style/UpdateLayout.java1
-rw-r--r--core/java/android/text/style/WrapTogetherSpan.java1
-rw-r--r--core/java/android/text/util/Rfc822Token.java1
-rw-r--r--core/java/android/text/util/Rfc822Tokenizer.java1
-rw-r--r--core/java/android/view/Display.java7
-rw-r--r--core/java/android/view/InsetsController.java6
-rw-r--r--core/java/android/view/SurfaceControl.java75
-rw-r--r--core/java/android/view/SurfaceView.java15
-rw-r--r--core/java/android/view/WindowInsets.java3
-rw-r--r--core/java/android/view/flags/view_flags.aconfig8
-rw-r--r--core/java/android/window/DesktopModeFlags.java8
-rw-r--r--core/java/android/window/TransitionFilter.java6
-rw-r--r--core/java/android/window/TransitionInfo.java38
-rw-r--r--core/java/android/window/flags/lse_desktop_experience.aconfig31
-rw-r--r--core/java/android/window/flags/windowing_sdk.aconfig10
-rw-r--r--core/java/com/android/internal/app/MediaRouteControllerContentManager.java132
-rw-r--r--core/java/com/android/internal/app/MediaRouteControllerDialog.java119
-rw-r--r--core/java/com/android/internal/jank/FrameTracker.java16
-rw-r--r--core/java/com/android/internal/os/LongArrayMultiStateCounter.java9
-rw-r--r--core/java/com/android/internal/os/LongArrayMultiStateCounter_ravenwood.java20
-rw-r--r--core/java/com/android/internal/pm/pkg/component/AconfigFlags.java7
-rw-r--r--core/java/com/android/internal/widget/ConversationLayout.java54
-rw-r--r--core/java/com/android/internal/widget/MessagingGroup.java8
-rw-r--r--core/java/com/android/internal/widget/MessagingLayout.java25
-rw-r--r--core/jni/Android.bp7
-rw-r--r--core/jni/android_app_PropertyInvalidatedCache.cpp6
-rw-r--r--core/jni/android_app_PropertyInvalidatedCache.h54
-rw-r--r--core/jni/android_graphics_BLASTBufferQueue.cpp3
-rw-r--r--core/jni/android_media_ImageReader.cpp75
-rw-r--r--core/jni/android_media_PublicFormatUtils.cpp9
-rw-r--r--core/jni/android_media_Utils.cpp12
-rw-r--r--core/jni/android_view_SurfaceControl.cpp26
-rw-r--r--core/jni/com_android_internal_os_LongArrayMultiStateCounter.cpp25
-rw-r--r--core/jni/jni_wrappers.h2
-rw-r--r--core/jni/platform/host/HostRuntime.cpp6
-rw-r--r--core/res/AndroidManifest.xml12
-rw-r--r--core/res/res/layout/notification_2025_conversation_header.xml189
-rw-r--r--core/res/res/layout/notification_2025_template_collapsed_call.xml188
-rw-r--r--core/res/res/layout/notification_2025_template_collapsed_conversation.xml216
-rw-r--r--core/res/res/layout/notification_2025_template_conversation.xml159
-rw-r--r--core/res/res/layout/notification_2025_template_expanded_call.xml86
-rw-r--r--core/res/res/layout/notification_2025_template_expanded_conversation.xml75
-rw-r--r--core/res/res/layout/notification_2025_template_expanded_messaging.xml6
-rw-r--r--core/res/res/layout/notification_2025_top_line_views.xml38
-rw-r--r--core/res/res/values/config_telephony.xml7
-rw-r--r--core/res/res/values/symbols.xml4
-rw-r--r--core/tests/coretests/Android.bp27
-rw-r--r--core/tests/coretests/src/android/content/res/FontScaleConverterTest.kt5
-rw-r--r--core/tests/coretests/src/android/graphics/BitmapFactoryTest.java3
-rw-r--r--core/tests/coretests/src/android/graphics/BitmapTest.java3
-rw-r--r--core/tests/coretests/src/android/graphics/PaintTest.java3
-rw-r--r--core/tests/coretests/src/android/graphics/TypefaceSystemFallbackTest.java10
-rw-r--r--core/tests/coretests/src/android/graphics/TypefaceTest.java2
-rw-r--r--core/tests/coretests/src/android/hardware/display/DisplayManagerGlobalTest.java116
-rw-r--r--core/tests/coretests/src/android/os/BundleMergerTest.java41
-rw-r--r--core/tests/coretests/src/android/os/PerfettoTraceTest.java4
-rw-r--r--core/tests/coretests/src/android/text/AndroidCharacterTest.java2
-rw-r--r--core/tests/coretests/src/android/text/SpanColorsTest.java2
-rw-r--r--core/tests/coretests/src/android/text/SpannableTest.java10
-rw-r--r--core/tests/coretests/src/android/text/StaticLayoutTest.java2
-rw-r--r--core/tests/coretests/src/android/text/TextUtilsTest.java4
-rw-r--r--core/tests/coretests/src/android/text/format/DateFormatTest.java5
-rw-r--r--core/tests/coretests/src/android/text/format/DateIntervalFormatTest.java2
-rw-r--r--core/tests/coretests/src/android/text/format/DateUtilsTest.java2
-rw-r--r--core/tests/coretests/src/android/text/format/TimeMigrationUtilsTest.java2
-rw-r--r--core/tests/coretests/src/android/text/format/TimeTest.java2
-rw-r--r--core/tests/coretests/src/android/text/method/BackspaceTest.java2
-rw-r--r--core/tests/coretests/src/android/text/method/EditorState.java3
-rw-r--r--core/tests/coretests/src/android/text/method/ForwardDeleteTest.java2
-rw-r--r--core/tests/coretests/src/android/text/method/InsertModeTransformationMethodTest.java2
-rw-r--r--core/tests/coretests/src/android/text/util/LinkifyTest.java2
-rw-r--r--core/tests/coretests/src/android/view/InsetsSourceConsumerTest.java4
-rw-r--r--core/tests/coretests/src/com/android/internal/os/KernelSingleUidTimeReaderTest.java2
-rw-r--r--core/tests/coretests/src/com/android/internal/os/LongArrayMultiStateCounterTest.java2
-rw-r--r--graphics/java/android/graphics/AvoidXfermode.java1
-rw-r--r--graphics/java/android/graphics/BLASTBufferQueue.java1
-rw-r--r--graphics/java/android/graphics/BaseCanvas.java1
-rw-r--r--graphics/java/android/graphics/BaseRecordingCanvas.java1
-rw-r--r--graphics/java/android/graphics/Bitmap.java1
-rw-r--r--graphics/java/android/graphics/BitmapFactory.java1
-rw-r--r--graphics/java/android/graphics/BitmapRegionDecoder.java1
-rw-r--r--graphics/java/android/graphics/BitmapShader.java1
-rw-r--r--graphics/java/android/graphics/BlendMode.java1
-rw-r--r--graphics/java/android/graphics/BlendModeColorFilter.java1
-rw-r--r--graphics/java/android/graphics/BlurMaskFilter.java1
-rw-r--r--graphics/java/android/graphics/Camera.java1
-rw-r--r--graphics/java/android/graphics/Canvas.java1
-rw-r--r--graphics/java/android/graphics/CanvasProperty.java1
-rw-r--r--graphics/java/android/graphics/ColorFilter.java1
-rw-r--r--graphics/java/android/graphics/ColorMatrixColorFilter.java1
-rw-r--r--graphics/java/android/graphics/Compatibility.java1
-rw-r--r--graphics/java/android/graphics/ComposePathEffect.java1
-rw-r--r--graphics/java/android/graphics/ComposeShader.java1
-rw-r--r--graphics/java/android/graphics/CornerPathEffect.java1
-rw-r--r--graphics/java/android/graphics/DashPathEffect.java1
-rw-r--r--graphics/java/android/graphics/DiscretePathEffect.java1
-rw-r--r--graphics/java/android/graphics/DrawFilter.java1
-rw-r--r--graphics/java/android/graphics/EmbossMaskFilter.java1
-rw-r--r--graphics/java/android/graphics/FontFamily.java1
-rw-r--r--graphics/java/android/graphics/FontListParser.java1
-rw-r--r--graphics/java/android/graphics/ForceDarkType.java1
-rw-r--r--graphics/java/android/graphics/FrameInfo.java1
-rw-r--r--graphics/java/android/graphics/Gainmap.java1
-rw-r--r--graphics/java/android/graphics/GraphicBuffer.java1
-rw-r--r--graphics/java/android/graphics/GraphicsProtos.java1
-rw-r--r--graphics/java/android/graphics/GraphicsStatsService.java1
-rw-r--r--graphics/java/android/graphics/HardwareBufferRenderer.java1
-rw-r--r--graphics/java/android/graphics/HardwareRenderer.java1
-rw-r--r--graphics/java/android/graphics/HardwareRendererObserver.java1
-rw-r--r--graphics/java/android/graphics/ImageDecoder.java5
-rw-r--r--graphics/java/android/graphics/ImageFormat.java1
-rw-r--r--graphics/java/android/graphics/LayerRasterizer.java1
-rw-r--r--graphics/java/android/graphics/LeakyTypefaceStorage.java1
-rw-r--r--graphics/java/android/graphics/LightingColorFilter.java1
-rw-r--r--graphics/java/android/graphics/LinearGradient.java1
-rw-r--r--graphics/java/android/graphics/MaskFilter.java1
-rw-r--r--graphics/java/android/graphics/Mesh.java1
-rw-r--r--graphics/java/android/graphics/MeshSpecification.java1
-rw-r--r--graphics/java/android/graphics/Movie.java1
-rw-r--r--graphics/java/android/graphics/NinePatch.java1
-rw-r--r--graphics/java/android/graphics/Paint.java1
-rw-r--r--graphics/java/android/graphics/PaintFlagsDrawFilter.java1
-rw-r--r--graphics/java/android/graphics/PathDashPathEffect.java1
-rw-r--r--graphics/java/android/graphics/PathEffect.java1
-rw-r--r--graphics/java/android/graphics/PathIterator.java16
-rw-r--r--graphics/java/android/graphics/PathMeasure.java1
-rw-r--r--graphics/java/android/graphics/Picture.java1
-rw-r--r--graphics/java/android/graphics/PixelXorXfermode.java1
-rw-r--r--graphics/java/android/graphics/PorterDuff.java1
-rw-r--r--graphics/java/android/graphics/PorterDuffColorFilter.java1
-rw-r--r--graphics/java/android/graphics/PorterDuffXfermode.java1
-rw-r--r--graphics/java/android/graphics/PostProcessor.java1
-rw-r--r--graphics/java/android/graphics/RadialGradient.java1
-rw-r--r--graphics/java/android/graphics/Rasterizer.java1
-rw-r--r--graphics/java/android/graphics/RecordingCanvas.java1
-rw-r--r--graphics/java/android/graphics/Region.java1
-rw-r--r--graphics/java/android/graphics/RegionIterator.java1
-rw-r--r--graphics/java/android/graphics/RenderEffect.java1
-rw-r--r--graphics/java/android/graphics/RenderNode.java1
-rw-r--r--graphics/java/android/graphics/RuntimeColorFilter.java1
-rw-r--r--graphics/java/android/graphics/RuntimeShader.java1
-rw-r--r--graphics/java/android/graphics/RuntimeXfermode.java1
-rw-r--r--graphics/java/android/graphics/Shader.java1
-rw-r--r--graphics/java/android/graphics/SumPathEffect.java1
-rw-r--r--graphics/java/android/graphics/SurfaceTexture.java1
-rw-r--r--graphics/java/android/graphics/SweepGradient.java1
-rw-r--r--graphics/java/android/graphics/TableMaskFilter.java1
-rw-r--r--graphics/java/android/graphics/TemporaryBuffer.java1
-rw-r--r--graphics/java/android/graphics/TextureLayer.java1
-rw-r--r--graphics/java/android/graphics/Typeface.java45
-rw-r--r--graphics/java/android/graphics/Xfermode.java1
-rw-r--r--graphics/java/android/graphics/YuvImage.java1
-rw-r--r--graphics/java/android/graphics/fonts/Font.java11
-rw-r--r--graphics/java/android/graphics/fonts/FontCustomizationParser.java1
-rw-r--r--graphics/java/android/graphics/fonts/FontFamily.java1
-rw-r--r--graphics/java/android/graphics/fonts/FontFileUtil.java1
-rw-r--r--graphics/java/android/graphics/fonts/FontStyle.java1
-rw-r--r--graphics/java/android/graphics/fonts/FontVariationAxis.java1
-rw-r--r--graphics/java/android/graphics/fonts/SystemFonts.java31
-rw-r--r--graphics/java/android/graphics/text/GraphemeBreak.java1
-rw-r--r--graphics/java/android/graphics/text/LineBreakConfig.java10
-rw-r--r--graphics/java/android/graphics/text/LineBreaker.java1
-rw-r--r--graphics/java/android/graphics/text/MeasuredText.java1
-rw-r--r--graphics/java/android/graphics/text/PositionedGlyphs.java1
-rw-r--r--graphics/java/android/graphics/text/TextRunShaper.java1
-rw-r--r--libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/SplitPresenter.java3
-rw-r--r--libs/WindowManager/Shell/aconfig/multitasking.aconfig10
-rw-r--r--libs/WindowManager/Shell/multivalentTests/AndroidManifest.xml1
-rw-r--r--libs/WindowManager/Shell/multivalentTests/src/com/android/wm/shell/bubbles/BubbleTaskViewListenerTest.kt161
-rw-r--r--libs/WindowManager/Shell/res/values/dimen.xml3
-rw-r--r--libs/WindowManager/Shell/shared/res/values/dimen.xml3
-rw-r--r--libs/WindowManager/Shell/shared/src/com/android/wm/shell/shared/animation/Interpolators.java6
-rw-r--r--libs/WindowManager/Shell/shared/src/com/android/wm/shell/shared/animation/MinimizeAnimator.kt50
-rw-r--r--libs/WindowManager/Shell/shared/src/com/android/wm/shell/shared/desktopmode/DesktopModeCompatPolicy.kt37
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/activityembedding/ActivityEmbeddingAnimationRunner.java14
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/activityembedding/ActivityEmbeddingAnimationSpec.java44
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/activityembedding/ActivityEmbeddingController.java4
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/Bubble.java2
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleExpandedView.java65
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleTaskViewListener.java23
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleTransitions.java3
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/common/DisplayImeController.java18
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/common/MultiDisplayDragMoveIndicatorController.kt124
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/common/MultiDisplayDragMoveIndicatorSurface.kt151
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/common/pip/PipBoundsAlgorithm.java9
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/dagger/WMShellConcurrencyModule.java29
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/dagger/WMShellModule.java79
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/CloseDesktopTaskTransitionHandler.kt5
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopDisplayEventHandler.kt81
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopDisplayModeController.kt121
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopMinimizationTransitionHandler.kt36
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopRepository.kt61
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopTasksController.kt148
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopTasksLimiter.kt25
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DisplayDeskState.aidl2
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/IDesktopTaskListener.aidl8
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/multidesks/DesksOrganizer.kt10
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/multidesks/RootTaskDesksOrganizer.kt285
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/docs/README.md1
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/docs/transitions.md118
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/freeform/FreeformTaskTransitionHandler.java48
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/pip2/phone/PipController.java4
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/pip2/phone/PipTransition.java7
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/recents/RecentsTransitionHandler.java117
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/transition/DefaultMixedHandler.java4
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/transition/DefaultTransitionHandler.java16
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/transition/HomeTransitionObserver.java78
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/transition/TransitionAnimationHelper.java18
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/DesktopModeWindowDecorViewModel.java21
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/DesktopModeWindowDecoration.java3
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/MultiDisplayVeiledResizeTaskPositioner.kt37
-rw-r--r--libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/activityembedding/ActivityEmbeddingAnimationRunnerTests.java53
-rw-r--r--libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/activityembedding/ActivityEmbeddingControllerTests.java34
-rw-r--r--libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/common/MultiDisplayDragMoveIndicatorControllerTest.kt168
-rw-r--r--libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopDisplayEventHandlerTest.kt170
-rw-r--r--libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopDisplayModeControllerTest.kt233
-rw-r--r--libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopMinimizationTransitionHandlerTest.kt8
-rw-r--r--libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopRepositoryTest.kt2
-rw-r--r--libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopTasksControllerTest.kt154
-rw-r--r--libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopTasksLimiterTest.kt82
-rw-r--r--libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/multidesks/RootTaskDesksOrganizerTest.kt280
-rw-r--r--libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/recents/RecentsTransitionHandlerTest.java87
-rw-r--r--libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/shared/animation/MinimizeAnimatorTest.kt118
-rw-r--r--libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/shared/desktopmode/DesktopModeCompatPolicyTest.kt47
-rw-r--r--libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/transition/HomeTransitionObserverTest.java84
-rw-r--r--libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/DesktopModeWindowDecorViewModelTestsBase.kt5
-rw-r--r--libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/MultiDisplayVeiledResizeTaskPositionerTest.kt9
-rw-r--r--libs/hostgraphics/include/gui/BufferItemConsumer.h29
-rw-r--r--libs/hostgraphics/include/gui/Surface.h4
-rw-r--r--libs/hostgraphics/include/ui/Fence.h4
-rw-r--r--libs/hwui/Android.bp12
-rw-r--r--libs/hwui/framework-graphics-ravenwood-policies.txt1
-rw-r--r--libs/hwui/jni/Bitmap.cpp12
-rw-r--r--libs/hwui/jni/Region.cpp6
-rw-r--r--libs/hwui/jni/ScopedParcel.cpp4
-rw-r--r--libs/hwui/jni/ScopedParcel.h4
-rw-r--r--libs/hwui/jni/graphics_jni_helpers.h1
-rw-r--r--media/java/android/media/AudioDeviceVolumeManager.java57
-rw-r--r--native/android/surface_control.cpp28
-rw-r--r--packages/CtsShim/build/Android.bp30
-rw-r--r--packages/CtsShim/build/shim/AndroidManifest.xml8
-rw-r--r--packages/CtsShim/build/shim/AndroidManifestUpgrade.xml30
-rw-r--r--packages/SettingsLib/BannerMessagePreference/Android.bp1
-rw-r--r--packages/SettingsLib/BannerMessagePreference/res/drawable-v35/settingslib_resolved_banner_avd.xml157
-rw-r--r--packages/SettingsLib/BannerMessagePreference/res/layout-v35/settingslib_expressive_banner_message.xml137
-rw-r--r--packages/SettingsLib/BannerMessagePreference/res/values-v35/styles_expressive.xml8
-rw-r--r--packages/SettingsLib/BannerMessagePreference/src/com/android/settingslib/widget/BannerMessagePreference.java222
-rw-r--r--packages/SettingsLib/BannerMessagePreference/src/com/android/settingslib/widget/BannerMessageView.java16
-rw-r--r--packages/SettingsLib/BannerMessagePreference/src/com/android/settingslib/widget/ResolutionAnimator.kt196
-rw-r--r--packages/SettingsLib/ButtonPreference/Android.bp1
-rw-r--r--packages/SettingsLib/SettingsTheme/res/values-v35/themes.xml2
-rw-r--r--packages/SettingsLib/SettingsTheme/src/com/android/settingslib/widget/SettingsPreferenceGroupAdapter.kt6
-rw-r--r--packages/SettingsLib/src/com/android/settingslib/bluetooth/BluetoothUtils.java51
-rw-r--r--packages/SettingsLib/src/com/android/settingslib/bluetooth/LocalBluetoothLeBroadcast.java21
-rw-r--r--packages/SettingsLib/tests/robotests/src/com/android/settingslib/bluetooth/AmbientVolumeControllerTest.java6
-rw-r--r--packages/SettingsLib/tests/robotests/src/com/android/settingslib/bluetooth/BluetoothUtilsTest.java84
-rw-r--r--packages/SettingsLib/tests/robotests/src/com/android/settingslib/bluetooth/CachedBluetoothDeviceManagerTest.java6
-rw-r--r--packages/SettingsProvider/test/src/android/provider/SettingsBackupTest.java2
-rw-r--r--packages/SystemUI/animation/src/com/android/systemui/animation/FontInterpolator.kt93
-rw-r--r--packages/SystemUI/animation/src/com/android/systemui/animation/FontVariationUtils.kt32
-rw-r--r--packages/SystemUI/animation/src/com/android/systemui/animation/GSFAxes.kt98
-rw-r--r--packages/SystemUI/compose/features/src/com/android/systemui/notifications/ui/composable/NotificationsShadeOverlay.kt1
-rw-r--r--packages/SystemUI/compose/features/src/com/android/systemui/shade/ui/composable/OverlayShade.kt10
-rw-r--r--packages/SystemUI/compose/features/src/com/android/systemui/shade/ui/composable/ShadeHeader.kt44
-rw-r--r--packages/SystemUI/compose/features/src/com/android/systemui/shade/ui/composable/VariableDayDate.kt10
-rw-r--r--packages/SystemUI/customization/src/com/android/systemui/shared/clocks/FlexClockController.kt30
-rw-r--r--packages/SystemUI/customization/src/com/android/systemui/shared/clocks/FlexClockFaceController.kt2
-rw-r--r--packages/SystemUI/customization/src/com/android/systemui/shared/clocks/FontUtils.kt43
-rw-r--r--packages/SystemUI/customization/src/com/android/systemui/shared/clocks/view/SimpleDigitalClockTextView.kt31
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/keyguard/EmergencyButtonControllerTest.kt6
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/systemui/animation/FontVariationUtilsTest.kt5
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/systemui/media/controls/ui/binder/SeekBarObserverTest.kt35
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/chips/call/domain/interactor/CallChipInteractorTest.kt25
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/chips/call/ui/viewmodel/CallChipViewModelTest.kt160
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/chips/ui/viewmodel/OngoingActivityChipsViewModelTest.kt75
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/data/repository/StatusBarModeRepositoryImplTest.kt48
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/NotificationTransitionAnimatorControllerTest.kt6
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/collection/BundleEntryTest.kt33
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/collection/NotificationEntryTest.java (renamed from packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/NotificationEntryTest.java)73
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/collection/coordinator/OriginalUnseenKeyguardCoordinatorTest.kt14
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/collection/render/NodeSpecBuilderTest.kt3
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/collection/render/RenderStageManagerTest.kt4
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/row/NotificationGutsManagerTest.kt11
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/row/NotificationMenuRowTest.java16
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/row/wrapper/NotificationConversationTemplateViewWrapperTest.kt1
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/stack/MagneticNotificationRowManagerImplTest.kt63
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/stack/StackScrollAlgorithmTest.kt14
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/stack/ui/viewmodel/SharedNotificationContainerViewModelTest.kt52
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/pipeline/mobile/domain/interactor/MobileIconsInteractorTest.kt69
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/pipeline/mobile/ui/viewmodel/StackedMobileIconViewModelTest.kt145
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/pipeline/shared/ui/viewmodel/FakeHomeStatusBarViewModel.kt2
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/pipeline/shared/ui/viewmodel/HomeStatusBarViewModelImplTest.kt54
-rw-r--r--packages/SystemUI/plugin/src/com/android/systemui/plugins/qs/QS.java2
-rw-r--r--packages/SystemUI/plugin/src/com/android/systemui/plugins/statusbar/NotificationMenuRowPlugin.java5
-rw-r--r--packages/SystemUI/res/layout/bindable_status_bar_compose_icon.xml1
-rw-r--r--packages/SystemUI/res/values/dimens.xml7
-rw-r--r--packages/SystemUI/res/values/strings.xml4
-rw-r--r--packages/SystemUI/shared/Android.bp1
-rw-r--r--packages/SystemUI/shared/res/values/bools.xml5
-rw-r--r--packages/SystemUI/shared/src/com/android/systemui/shared/recents/utilities/Utilities.java9
-rw-r--r--packages/SystemUI/shared/src/com/android/systemui/shared/system/ActivityManagerWrapper.java26
-rw-r--r--packages/SystemUI/src/com/android/keyguard/EmergencyButtonController.java2
-rw-r--r--packages/SystemUI/src/com/android/systemui/accessibility/hearingaid/AmbientVolumeLayout.java34
-rw-r--r--packages/SystemUI/src/com/android/systemui/accessibility/hearingaid/HearingDevicesDialogDelegate.java1
-rw-r--r--packages/SystemUI/src/com/android/systemui/accessibility/hearingaid/HearingDevicesUiEvent.kt12
-rw-r--r--packages/SystemUI/src/com/android/systemui/communal/ui/viewmodel/CommunalViewModel.kt10
-rw-r--r--packages/SystemUI/src/com/android/systemui/keyguard/dagger/KeyguardModule.java3
-rw-r--r--packages/SystemUI/src/com/android/systemui/media/controls/ui/binder/MediaControlViewBinder.kt19
-rw-r--r--packages/SystemUI/src/com/android/systemui/media/controls/ui/binder/SeekBarObserver.kt63
-rw-r--r--packages/SystemUI/src/com/android/systemui/media/controls/ui/controller/MediaControlPanel.java16
-rw-r--r--packages/SystemUI/src/com/android/systemui/media/controls/ui/controller/MediaViewController.kt1
-rw-r--r--packages/SystemUI/src/com/android/systemui/media/controls/ui/viewmodel/MediaControlViewModel.kt8
-rw-r--r--packages/SystemUI/src/com/android/systemui/qs/QSEvents.kt105
-rw-r--r--packages/SystemUI/src/com/android/systemui/qs/composefragment/QSFragmentCompose.kt53
-rw-r--r--packages/SystemUI/src/com/android/systemui/qs/composefragment/viewmodel/QSFragmentComposeViewModel.kt3
-rw-r--r--packages/SystemUI/src/com/android/systemui/qs/panels/domain/interactor/IconTilesInteractor.kt14
-rw-r--r--packages/SystemUI/src/com/android/systemui/shade/QuickSettingsControllerImpl.java15
-rw-r--r--packages/SystemUI/src/com/android/systemui/shade/ui/viewmodel/ShadeHeaderViewModel.kt26
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/BlurUtils.kt2
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/NotificationGroupingUtil.java5
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/NotificationLockscreenUserManagerImpl.java44
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/NotificationShadeDepthController.kt39
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/chips/call/domain/interactor/CallChipInteractor.kt26
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/core/NewStatusBarIcons.kt4
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationTransitionAnimatorController.kt5
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationUtils.java3
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationUtils.kt4
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/BundleEntry.java76
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/EntryAdapter.java43
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/GroupEntry.java1
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/ListAttachState.kt4
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/ListDumper.java6
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/ListEntry.java37
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/NotificationEntry.java61
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/PipelineEntry.java66
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/ShadeListBuilder.java147
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/SuppressedAttachState.kt4
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/BundleCoordinator.kt10
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/ColorizedFgsCoordinator.java5
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/ConversationCoordinator.kt18
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/DataStoreCoordinator.kt6
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/DismissibilityCoordinator.kt6
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/GroupCountCoordinator.kt4
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/GroupWhenCoordinator.kt4
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/GutsCoordinator.kt4
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/HeadsUpCoordinator.kt24
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/LockScreenMinimalismCoordinator.kt10
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/OriginalUnseenKeyguardCoordinator.kt3
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/PreparationCoordinator.java19
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/RankingCoordinator.java12
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/RowAlertTimeCoordinator.kt4
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/RowAppearanceCoordinator.kt4
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/SensitiveContentCoordinator.kt16
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/ShadeEventCoordinator.kt4
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/StackCoordinator.kt6
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/VisualStabilityCoordinator.java15
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/inflation/NotifUiAdjustmentProvider.kt2
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/listbuilder/OnAfterRenderListListener.java4
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/listbuilder/OnBeforeFinalizeFilterListener.java4
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/listbuilder/OnBeforeRenderListListener.java4
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/listbuilder/OnBeforeSortListener.java4
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/listbuilder/OnBeforeTransformGroupsListener.java4
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/listbuilder/ShadeListBuilderHelper.kt4
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/listbuilder/ShadeListBuilderLogger.kt22
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/listbuilder/pluggable/NotifComparator.java6
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/listbuilder/pluggable/NotifSectioner.java8
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/listbuilder/pluggable/NotifStabilityManager.kt6
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/notifcollection/NotifCollectionLogger.kt11
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/provider/HighPriorityProvider.java27
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/provider/SectionStyleProvider.kt6
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/render/GroupExpansionManagerImpl.java4
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/render/GroupMembershipManager.java3
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/render/GroupMembershipManagerImpl.java20
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/render/NodeSpecBuilder.kt6
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/render/NotifViewBarn.kt10
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/render/NotifViewRenderer.kt4
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/render/RenderStageManager.kt12
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/render/ShadeViewManager.kt4
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/domain/interactor/RenderNotificationListInteractor.kt14
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/interruption/KeyguardNotificationVisibilityProvider.kt4
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/people/PeopleNotificationIdentifier.kt5
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/promoted/AODPromotedNotification.kt6
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRow.java159
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRowController.java6
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationGutsManager.java9
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationMenuRow.java4
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/row/wrapper/NotificationCallTemplateViewWrapper.kt39
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/row/wrapper/NotificationConversationTemplateViewWrapper.kt31
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayout.java2
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/StackScrollAlgorithm.java14
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/ui/viewmodel/SharedNotificationContainerViewModel.kt47
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/domain/interactor/MobileIconsInteractor.kt24
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/ui/viewmodel/MobileIconsViewModel.kt2
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/ui/viewmodel/StackedMobileIconViewModel.kt12
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/pipeline/shared/ui/binder/HomeStatusBarViewBinder.kt104
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/pipeline/shared/ui/composable/StackedMobileIcon.kt93
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/pipeline/shared/ui/viewmodel/HomeStatusBarViewModel.kt12
-rw-r--r--packages/SystemUI/src/com/android/systemui/user/domain/interactor/UserLockedInteractor.kt12
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/animation/FontInterpolatorTest.kt6
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/ShadeListBuilderTest.java70
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRowTest.java2
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationContentViewTest.kt7
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationGutsManagerWithScenesTest.kt5
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutTest.java2
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/unfold/UnfoldLatencyTrackerTest.kt3
-rw-r--r--packages/SystemUI/tests/utils/src/com/android/systemui/qs/panels/domain/interactor/IconTilesInteractorKosmos.kt2
-rw-r--r--packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/notification/collection/NotificationEntryBuilder.java6
-rw-r--r--packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/phone/ongoingcall/shared/model/OngoingCallTestHelper.kt3
-rw-r--r--packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/pipeline/mobile/domain/interactor/FakeMobileIconsInteractor.kt2
-rw-r--r--packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/pipeline/mobile/ui/viewmodel/StackedMobileIconViewModelKosmos.kt22
-rw-r--r--packages/SystemUI/tests/utils/src/com/android/systemui/user/domain/interactor/UserLockedInteractorKosmos.kt5
-rw-r--r--packages/Vcn/framework-b/Android.bp3
-rw-r--r--ravenwood/Android.bp10
-rw-r--r--ravenwood/Framework.bp4
-rw-r--r--ravenwood/junit-impl-src/android/platform/test/ravenwood/RavenwoodContext.java10
-rw-r--r--ravenwood/junit-impl-src/android/platform/test/ravenwood/RavenwoodNativeLoader.java39
-rw-r--r--ravenwood/junit-impl-src/android/platform/test/ravenwood/RavenwoodRuntimeEnvironmentController.java13
-rw-r--r--ravenwood/runtime-helper-src/libcore-fake/com/android/ravenwood/RavenwoodJdkPatch.java19
-rw-r--r--ravenwood/runtime-helper-src/libcore-fake/com/android/ravenwood/RavenwoodRuntimeNative.java2
-rw-r--r--ravenwood/runtime-helper-src/libcore-fake/dalvik/system/CloseGuard.java198
-rw-r--r--ravenwood/runtime-helper-src/libcore-fake/libcore/io/IoBridge.java63
-rw-r--r--ravenwood/runtime-helper-src/libcore-fake/libcore/util/NativeAllocationRegistry.java63
-rw-r--r--ravenwood/runtime-jni/ravenwood_runtime.cpp6
-rw-r--r--ravenwood/texts/ravenwood-annotation-allowed-classes.txt231
-rw-r--r--ravenwood/texts/ravenwood-build.prop6
-rw-r--r--ravenwood/texts/ravenwood-common-policies.txt4
-rw-r--r--ravenwood/texts/ravenwood-framework-policies.txt19
-rw-r--r--services/accessibility/accessibility.aconfig10
-rw-r--r--services/accessibility/java/com/android/server/accessibility/AccessibilityInputFilter.java4
-rw-r--r--services/accessibility/java/com/android/server/accessibility/autoclick/AutoclickController.java20
-rw-r--r--services/accessibility/java/com/android/server/accessibility/autoclick/AutoclickIndicatorView.java1
-rw-r--r--services/accessibility/java/com/android/server/accessibility/autoclick/AutoclickTypePanel.java149
-rw-r--r--services/accessibility/java/com/android/server/accessibility/magnification/FullScreenMagnificationGestureHandler.java31
-rw-r--r--services/accessibility/java/com/android/server/accessibility/magnification/MagnificationGestureHandler.java3
-rw-r--r--services/appwidget/java/com/android/server/appwidget/AppWidgetServiceImpl.java6
-rw-r--r--services/core/java/com/android/server/StorageManagerService.java2
-rw-r--r--services/core/java/com/android/server/am/ActivityManagerService.java11
-rw-r--r--services/core/java/com/android/server/am/OomAdjuster.java26
-rw-r--r--services/core/java/com/android/server/am/ProcessList.java17
-rw-r--r--services/core/java/com/android/server/am/ProcessStateRecord.java3
-rw-r--r--services/core/java/com/android/server/am/UserController.java2
-rw-r--r--services/core/java/com/android/server/am/UserSwitchingDialog.java6
-rw-r--r--services/core/java/com/android/server/am/flags.aconfig7
-rw-r--r--services/core/java/com/android/server/audio/AudioService.java42
-rw-r--r--services/core/java/com/android/server/biometrics/BiometricService.java20
-rw-r--r--services/core/java/com/android/server/content/ContentService.java9
-rw-r--r--services/core/java/com/android/server/display/LogicalDisplayMapper.java8
-rw-r--r--services/core/java/com/android/server/display/feature/DisplayManagerFlags.java14
-rw-r--r--services/core/java/com/android/server/display/feature/display_flags.aconfig17
-rw-r--r--services/core/java/com/android/server/location/contexthub/ContextHubEndpointBroker.java27
-rw-r--r--services/core/java/com/android/server/location/contexthub/ContextHubEndpointManager.java3
-rw-r--r--services/core/java/com/android/server/location/contexthub/ContextHubService.java4
-rw-r--r--services/core/java/com/android/server/location/injector/SystemEmergencyHelper.java32
-rw-r--r--services/core/java/com/android/server/notification/PreferencesHelper.java25
-rw-r--r--services/core/java/com/android/server/pm/AppDataHelper.java6
-rw-r--r--services/core/java/com/android/server/pm/BackgroundInstallControlService.java3
-rw-r--r--services/core/java/com/android/server/pm/DeletePackageHelper.java4
-rw-r--r--services/core/java/com/android/server/pm/PackageFreezer.java6
-rw-r--r--services/core/java/com/android/server/pm/PackageManagerService.java21
-rw-r--r--services/core/java/com/android/server/pm/PreferredActivityHelper.java4
-rw-r--r--services/core/java/com/android/server/pm/RemovePackageHelper.java14
-rw-r--r--services/core/java/com/android/server/pm/Settings.java3
-rw-r--r--services/core/java/com/android/server/pm/permission/PermissionManagerService.java3
-rw-r--r--services/core/java/com/android/server/pm/permission/PermissionManagerServiceImpl.java3
-rw-r--r--services/core/java/com/android/server/pm/permission/PermissionManagerServiceInterface.java3
-rw-r--r--services/core/java/com/android/server/pm/permission/PermissionManagerServiceInternal.java3
-rw-r--r--services/core/java/com/android/server/power/stats/BatteryStatsImpl.java14
-rw-r--r--services/core/java/com/android/server/power/stats/processor/AmbientDisplayPowerStatsProcessor.java10
-rw-r--r--services/core/java/com/android/server/power/stats/processor/CpuPowerStatsProcessor.java44
-rw-r--r--services/core/java/com/android/server/power/stats/processor/MobileRadioPowerStatsProcessor.java4
-rw-r--r--services/core/java/com/android/server/power/stats/processor/MultiStateStats.java29
-rw-r--r--services/core/java/com/android/server/power/stats/processor/PhoneCallPowerStatsProcessor.java15
-rw-r--r--services/core/java/com/android/server/power/stats/processor/PowerComponentAggregatedPowerStats.java35
-rw-r--r--services/core/java/com/android/server/power/stats/processor/SensorPowerStatsProcessor.java10
-rw-r--r--services/core/java/com/android/server/storage/ImmutableVolumeInfo.java5
-rw-r--r--services/core/java/com/android/server/storage/StorageSessionController.java37
-rw-r--r--services/core/java/com/android/server/storage/WatchedVolumeInfo.java5
-rw-r--r--services/core/java/com/android/server/vibrator/AbstractVibratorStep.java2
-rw-r--r--services/core/java/com/android/server/vibrator/ComposePrimitivesVibratorStep.java3
-rw-r--r--services/core/java/com/android/server/vibrator/ComposePwleV2VibratorStep.java3
-rw-r--r--services/core/java/com/android/server/vibrator/ComposePwleVibratorStep.java3
-rw-r--r--services/core/java/com/android/server/vibrator/ExternalVibrationSession.java2
-rw-r--r--services/core/java/com/android/server/vibrator/PerformPrebakedVibratorStep.java3
-rw-r--r--services/core/java/com/android/server/vibrator/PerformVendorEffectVibratorStep.java3
-rw-r--r--services/core/java/com/android/server/vibrator/SetAmplitudeVibratorStep.java8
-rw-r--r--services/core/java/com/android/server/vibrator/SingleVibrationSession.java4
-rw-r--r--services/core/java/com/android/server/vibrator/VendorVibrationSession.java4
-rw-r--r--services/core/java/com/android/server/vibrator/VibrationSession.java2
-rw-r--r--services/core/java/com/android/server/vibrator/VibrationStepConductor.java35
-rw-r--r--services/core/java/com/android/server/vibrator/VibratorController.java65
-rw-r--r--services/core/java/com/android/server/vibrator/VibratorManagerService.java12
-rw-r--r--services/core/java/com/android/server/wm/ActivityRecord.java87
-rw-r--r--services/core/java/com/android/server/wm/ActivityTaskManagerService.java20
-rw-r--r--services/core/java/com/android/server/wm/AppCompatAspectRatioPolicy.java6
-rw-r--r--services/core/java/com/android/server/wm/DisplayArea.java9
-rw-r--r--services/core/java/com/android/server/wm/DisplayContent.java115
-rw-r--r--services/core/java/com/android/server/wm/DisplayPolicy.java1
-rw-r--r--services/core/java/com/android/server/wm/KeyguardController.java2
-rw-r--r--services/core/java/com/android/server/wm/RootWindowContainer.java4
-rw-r--r--services/core/java/com/android/server/wm/Task.java38
-rw-r--r--services/core/java/com/android/server/wm/TaskFragment.java36
-rw-r--r--services/core/java/com/android/server/wm/Transition.java76
-rw-r--r--services/core/java/com/android/server/wm/TrustedPresentationListenerController.java12
-rw-r--r--services/core/java/com/android/server/wm/WallpaperWindowToken.java3
-rw-r--r--services/core/java/com/android/server/wm/WindowContainer.java300
-rw-r--r--services/core/java/com/android/server/wm/WindowProcessController.java28
-rw-r--r--services/core/java/com/android/server/wm/WindowState.java11
-rw-r--r--services/core/jni/Android.bp1
-rw-r--r--services/core/jni/com_android_server_vibrator_VibratorController.cpp56
-rw-r--r--services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyEngine.java9
-rw-r--r--services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java9
-rw-r--r--services/tests/InputMethodSystemServerTests/src/com/android/inputmethodservice/InputMethodServiceTest.java66
-rw-r--r--services/tests/displayservicetests/src/com/android/server/display/DisplayManagerServiceTest.java3
-rw-r--r--services/tests/mockingservicestests/src/com/android/server/am/MockingOomAdjusterTests.java102
-rw-r--r--services/tests/mockingservicestests/src/com/android/server/wallpaper/WallpaperManagerServiceTests.java2
-rw-r--r--services/tests/powerstatstests/src/com/android/server/power/stats/processor/AggregatedPowerStatsTest.java4
-rw-r--r--services/tests/powerstatstests/src/com/android/server/power/stats/processor/AmbientDisplayPowerStatsProcessorTest.java2
-rw-r--r--services/tests/powerstatstests/src/com/android/server/power/stats/processor/BasePowerStatsProcessorTest.java16
-rw-r--r--services/tests/powerstatstests/src/com/android/server/power/stats/processor/BinaryStatePowerStatsProcessorTest.java21
-rw-r--r--services/tests/powerstatstests/src/com/android/server/power/stats/processor/BluetoothPowerStatsProcessorTest.java4
-rw-r--r--services/tests/powerstatstests/src/com/android/server/power/stats/processor/CameraPowerStatsTest.java11
-rw-r--r--services/tests/powerstatstests/src/com/android/server/power/stats/processor/CustomEnergyConsumerPowerStatsTest.java2
-rw-r--r--services/tests/powerstatstests/src/com/android/server/power/stats/processor/GnssPowerStatsTest.java31
-rw-r--r--services/tests/powerstatstests/src/com/android/server/power/stats/processor/MobileRadioPowerStatsProcessorTest.java2
-rw-r--r--services/tests/powerstatstests/src/com/android/server/power/stats/processor/MultiStateStatsTest.java6
-rw-r--r--services/tests/powerstatstests/src/com/android/server/power/stats/processor/PhoneCallPowerStatsProcessorTest.java2
-rw-r--r--services/tests/powerstatstests/src/com/android/server/power/stats/processor/ScreenPowerStatsProcessorTest.java2
-rw-r--r--services/tests/powerstatstests/src/com/android/server/power/stats/processor/SensorPowerStatsProcessorTest.java2
-rw-r--r--services/tests/powerstatstests/src/com/android/server/power/stats/processor/WakelockPowerStatsProcessorTest.java2
-rw-r--r--services/tests/powerstatstests/src/com/android/server/power/stats/processor/WifiPowerStatsProcessorTest.java4
-rw-r--r--services/tests/servicestests/Android.bp3
-rw-r--r--services/tests/servicestests/src/com/android/server/accessibility/autoclick/AutoclickControllerTest.java41
-rw-r--r--services/tests/servicestests/src/com/android/server/accessibility/autoclick/AutoclickTypePanelTest.java121
-rw-r--r--services/tests/servicestests/src/com/android/server/accessibility/magnification/FullScreenMagnificationGestureHandlerTest.java50
-rw-r--r--services/tests/servicestests/src/com/android/server/accessibility/magnification/MagnificationGestureHandlerTest.java37
-rw-r--r--services/tests/servicestests/src/com/android/server/devicepolicy/DpmMockContext.java5
-rw-r--r--services/tests/servicestests/src/com/android/server/pm/ShortcutManagerTest1.java22
-rw-r--r--services/tests/uiservicestests/src/com/android/server/notification/PreferencesHelperTest.java21
-rw-r--r--services/tests/vibrator/src/com/android/server/vibrator/DeviceAdapterTest.java10
-rw-r--r--services/tests/vibrator/src/com/android/server/vibrator/VibrationThreadTest.java293
-rw-r--r--services/tests/vibrator/src/com/android/server/vibrator/VibratorControllerTest.java61
-rw-r--r--services/tests/vibrator/utils/com/android/server/vibrator/FakeVibratorControllerProvider.java29
-rw-r--r--services/tests/wmtests/src/com/android/server/wm/ActivityRecordTests.java17
-rw-r--r--services/tests/wmtests/src/com/android/server/wm/DisplayContentTests.java152
-rw-r--r--services/tests/wmtests/src/com/android/server/wm/PresentationControllerTests.java71
-rw-r--r--services/tests/wmtests/src/com/android/server/wm/SystemServicesTestRule.java2
-rw-r--r--services/tests/wmtests/src/com/android/server/wm/TransitionTests.java44
-rw-r--r--services/tests/wmtests/src/com/android/server/wm/WindowContainerTests.java5
-rw-r--r--services/tests/wmtests/src/com/android/server/wm/WindowTestsBase.java7
-rw-r--r--services/voiceinteraction/java/com/android/server/soundtrigger/PhoneCallStateHandler.java35
-rw-r--r--tests/FlickerTests/FlickerService/src/com/android/server/wm/flicker/service/transitions/flicker/LaunchTaskPortrait.kt45
-rw-r--r--tests/FlickerTests/FlickerService/src/com/android/server/wm/flicker/service/transitions/scenarios/LaunchTask.kt130
-rw-r--r--tests/Input/Android.bp3
-rw-r--r--tests/testables/src/android/testing/TestableLooper.java19
-rw-r--r--tests/utils/testutils/java/android/os/test/TestLooper.java44
-rw-r--r--tools/aapt2/link/FlaggedResources_test.cpp14
715 files changed, 10984 insertions, 4789 deletions
diff --git a/apct-tests/perftests/core/src/android/os/TracePerfTest.java b/apct-tests/perftests/core/src/android/os/TracePerfTest.java
index 0b941c9d83c0..d9051240d399 100644
--- a/apct-tests/perftests/core/src/android/os/TracePerfTest.java
+++ b/apct-tests/perftests/core/src/android/os/TracePerfTest.java
@@ -127,14 +127,14 @@ public class TracePerfTest {
public void testInstantPerfettoWithArgs() {
PerfettoTrace.instant(FOO_CATEGORY, "testInstantP")
.addArg("foo", "bar")
- .addFlow(1)
+ .setFlow(1)
.emit();
BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
while (state.keepRunning()) {
PerfettoTrace.instant(FOO_CATEGORY, "testInstantP")
.addArg("foo", "bar")
- .addFlow(1)
+ .setFlow(1)
.emit();
}
}
diff --git a/boot/boot-image-profile-extra.txt b/boot/boot-image-profile-extra.txt
index ced0d176f174..ce99bfed1ce3 100644
--- a/boot/boot-image-profile-extra.txt
+++ b/boot/boot-image-profile-extra.txt
@@ -45,11 +45,15 @@ HSPLandroid/os/MessageQueue$OnFileDescriptorEventListener;->*
HSPLandroid/os/MessageQueue$StackNodeType;->*
HSPLandroid/os/MessageQueue$StateNode;->*
HSPLandroid/os/MessageQueue$TimedParkStateNode;->*
+
+# For now, compile all methods in PerfettoTrace and PerfettoTrackEventExtra.
+# Similar to the existing Trace APIs, these new APIs can impact the performance
+# of many subsystems including MessageQueue. This also keeps benchmark
+# comparisons between both APIs fair.
HSPLandroid/os/PerfettoTrace$Category;->*
HSPLandroid/os/PerfettoTrace;->*
HSPLandroid/os/PerfettoTrackEventExtra;->*
-HSPLandroid/os/PerfettoTrackEventExtra$BuilderImpl;->*
-HSPLandroid/os/PerfettoTrackEventExtra$NoOpBuilder;->*
+HSPLandroid/os/PerfettoTrackEventExtra$Builder;->*
HSPLandroid/os/PerfettoTrackEventExtra$ArgBool;->*
HSPLandroid/os/PerfettoTrackEventExtra$ArgInt64;->*
HSPLandroid/os/PerfettoTrackEventExtra$ArgDouble;->*
diff --git a/core/api/current.txt b/core/api/current.txt
index 9ebb5068bf19..4862236a35e3 100644
--- a/core/api/current.txt
+++ b/core/api/current.txt
@@ -53802,8 +53802,8 @@ package android.view {
method @FlaggedApi("com.android.graphics.hwui.flags.limited_hdr") public void setDesiredHdrHeadroom(@FloatRange(from=0.0f, to=10000.0) float);
method public void setSecure(boolean);
method public void setSurfaceLifecycle(int);
- method public void setZOrderMediaOverlay(boolean);
- method public void setZOrderOnTop(boolean);
+ method @Deprecated @FlaggedApi("android.view.flags.deprecate_surface_view_z_order_apis") public void setZOrderMediaOverlay(boolean);
+ method @Deprecated @FlaggedApi("android.view.flags.deprecate_surface_view_z_order_apis") public void setZOrderOnTop(boolean);
field public static final int SURFACE_LIFECYCLE_DEFAULT = 0; // 0x0
field public static final int SURFACE_LIFECYCLE_FOLLOWS_ATTACHMENT = 2; // 0x2
field public static final int SURFACE_LIFECYCLE_FOLLOWS_VISIBILITY = 1; // 0x1
diff --git a/core/api/module-lib-current.txt b/core/api/module-lib-current.txt
index 40069aa00106..526a213a6003 100644
--- a/core/api/module-lib-current.txt
+++ b/core/api/module-lib-current.txt
@@ -165,6 +165,16 @@ package android.hardware.usb {
package android.media {
+ public class AudioDeviceVolumeManager {
+ method @FlaggedApi("android.media.audio.unify_absolute_volume_management") @RequiresPermission(anyOf={android.Manifest.permission.MODIFY_AUDIO_ROUTING, android.Manifest.permission.MODIFY_AUDIO_SETTINGS_PRIVILEGED, android.Manifest.permission.BLUETOOTH_PRIVILEGED, android.Manifest.permission.BLUETOOTH_STACK}) public void setDeviceAbsoluteMultiVolumeBehavior(@NonNull android.media.AudioDeviceAttributes, @NonNull java.util.List<android.media.VolumeInfo>, @NonNull java.util.concurrent.Executor, @NonNull android.media.AudioDeviceVolumeManager.OnAudioDeviceVolumeChangedListener);
+ method @FlaggedApi("android.media.audio.unify_absolute_volume_management") @RequiresPermission(anyOf={android.Manifest.permission.MODIFY_AUDIO_ROUTING, android.Manifest.permission.MODIFY_AUDIO_SETTINGS_PRIVILEGED, android.Manifest.permission.BLUETOOTH_PRIVILEGED, android.Manifest.permission.BLUETOOTH_STACK}) public void setDeviceAbsoluteVolumeBehavior(@NonNull android.media.AudioDeviceAttributes, @NonNull android.media.VolumeInfo, @NonNull java.util.concurrent.Executor, @NonNull android.media.AudioDeviceVolumeManager.OnAudioDeviceVolumeChangedListener);
+ }
+
+ @FlaggedApi("android.media.audio.unify_absolute_volume_management") public static interface AudioDeviceVolumeManager.OnAudioDeviceVolumeChangedListener {
+ method public void onAudioDeviceVolumeAdjusted(@NonNull android.media.AudioDeviceAttributes, @NonNull android.media.VolumeInfo, int, int);
+ method public void onAudioDeviceVolumeChanged(@NonNull android.media.AudioDeviceAttributes, @NonNull android.media.VolumeInfo);
+ }
+
public class AudioManager {
method public void adjustStreamVolumeForUid(int, int, int, @NonNull String, int, int, int);
method public void adjustSuggestedStreamVolumeForUid(int, int, int, @NonNull String, int, int, int);
diff --git a/core/api/test-current.txt b/core/api/test-current.txt
index 89b377314887..4222c7c64672 100644
--- a/core/api/test-current.txt
+++ b/core/api/test-current.txt
@@ -1737,6 +1737,7 @@ package android.hardware.display {
method @NonNull public int[] getUserDisabledHdrTypes();
method public boolean isMinimalPostProcessingRequested(int);
method @RequiresPermission(android.Manifest.permission.ACCESS_SURFACE_FLINGER) public void overrideHdrTypes(int, @NonNull int[]);
+ method @FlaggedApi("com.android.server.display.feature.flags.delay_implicit_rr_registration_until_rr_accessed") public void resetImplicitRefreshRateCallbackStatus();
method @RequiresPermission(android.Manifest.permission.WRITE_SECURE_SETTINGS) public void setAreUserDisabledHdrTypesAllowed(boolean);
method @RequiresPermission(android.Manifest.permission.MODIFY_USER_PREFERRED_DISPLAY_MODE) public void setGlobalUserPreferredDisplayMode(@NonNull android.view.Display.Mode);
method @RequiresPermission(android.Manifest.permission.MODIFY_HDR_CONVERSION_MODE) public void setHdrConversionMode(@NonNull android.hardware.display.HdrConversionMode);
diff --git a/core/java/android/app/ActivityManagerInternal.java b/core/java/android/app/ActivityManagerInternal.java
index 6151b8e2ef0a..a12c0674998e 100644
--- a/core/java/android/app/ActivityManagerInternal.java
+++ b/core/java/android/app/ActivityManagerInternal.java
@@ -23,6 +23,7 @@ import android.annotation.NonNull;
import android.annotation.Nullable;
import android.annotation.PermissionMethod;
import android.annotation.PermissionName;
+import android.annotation.SpecialUsers.CanBeALL;
import android.annotation.UserIdInt;
import android.app.ActivityManager.ProcessCapability;
import android.app.ActivityManager.RestrictionLevel;
@@ -1365,8 +1366,8 @@ public abstract class ActivityManagerInternal {
* watchdog reset.
* @hide
*/
- public abstract void killApplicationSync(String pkgName, int appId, int userId,
- String reason, int exitInfoReason);
+ public abstract void killApplicationSync(String pkgName, int appId,
+ @CanBeALL @UserIdInt int userId, String reason, int exitInfoReason);
/**
* Queries the offset data for a given method on a process.
diff --git a/core/java/android/app/IActivityTaskManager.aidl b/core/java/android/app/IActivityTaskManager.aidl
index 4b1afa517122..01b2953362b5 100644
--- a/core/java/android/app/IActivityTaskManager.aidl
+++ b/core/java/android/app/IActivityTaskManager.aidl
@@ -146,6 +146,7 @@ interface IActivityTaskManager {
int getFrontActivityScreenCompatMode();
void setFrontActivityScreenCompatMode(int mode);
void setFocusedTask(int taskId);
+ boolean setTaskIsPerceptible(int taskId, boolean isPerceptible);
boolean removeTask(int taskId);
void removeAllVisibleRecentTasks();
List<ActivityManager.RunningTaskInfo> getTasks(int maxNum, boolean filterOnlyVisibleRecents,
diff --git a/core/java/android/app/Instrumentation.java b/core/java/android/app/Instrumentation.java
index b611acf79bc3..eb9feb95bf3d 100644
--- a/core/java/android/app/Instrumentation.java
+++ b/core/java/android/app/Instrumentation.java
@@ -50,6 +50,7 @@ import android.os.UserHandle;
import android.os.UserManager;
import android.ravenwood.annotation.RavenwoodKeep;
import android.ravenwood.annotation.RavenwoodKeepPartialClass;
+import android.ravenwood.annotation.RavenwoodKeepWholeClass;
import android.ravenwood.annotation.RavenwoodReplace;
import android.util.AndroidRuntimeException;
import android.util.Log;
@@ -460,6 +461,7 @@ public class Instrumentation {
*
* @param runner The code to run on the main thread.
*/
+ @RavenwoodReplace(blockedBy = ActivityThread.class)
public void runOnMainSync(Runnable runner) {
validateNotAppThread();
SyncRunnable sr = new SyncRunnable(runner);
@@ -467,6 +469,13 @@ public class Instrumentation {
sr.waitForComplete();
}
+ private void runOnMainSync$ravenwood(Runnable runner) {
+ validateNotAppThread();
+ SyncRunnable sr = new SyncRunnable(runner);
+ mInstrContext.getMainExecutor().execute(sr);
+ sr.waitForComplete();
+ }
+
boolean isSdkSandboxAllowedToStartActivities() {
return Process.isSdkSandbox()
&& mThread != null
@@ -2442,7 +2451,8 @@ public class Instrumentation {
}
}
- private final void validateNotAppThread() {
+ @RavenwoodKeep
+ private void validateNotAppThread() {
if (Looper.myLooper() == Looper.getMainLooper()) {
throw new RuntimeException(
"This method can not be called from the main application thread");
@@ -2586,6 +2596,7 @@ public class Instrumentation {
}
}
+ @RavenwoodKeepWholeClass
private static final class SyncRunnable implements Runnable {
private final Runnable mTarget;
private boolean mComplete;
diff --git a/core/java/android/app/Notification.java b/core/java/android/app/Notification.java
index 5dca1c70a2e6..719e4389d92d 100644
--- a/core/java/android/app/Notification.java
+++ b/core/java/android/app/Notification.java
@@ -18,6 +18,7 @@ package android.app;
import static android.annotation.Dimension.DP;
import static android.app.Flags.evenlyDividedCallStyleActionLayout;
+import static android.app.Flags.notificationsRedesignTemplates;
import static android.app.admin.DevicePolicyResources.Drawables.Source.NOTIFICATION;
import static android.app.admin.DevicePolicyResources.Drawables.Style.SOLID_COLORED;
import static android.app.admin.DevicePolicyResources.Drawables.WORK_PROFILE_ICON;
@@ -818,7 +819,8 @@ public class Notification implements Parcelable
R.layout.notification_2025_template_expanded_base,
R.layout.notification_2025_template_heads_up_base,
R.layout.notification_2025_template_header,
- R.layout.notification_2025_template_conversation,
+ R.layout.notification_2025_template_collapsed_conversation,
+ R.layout.notification_2025_template_expanded_conversation,
R.layout.notification_2025_template_collapsed_call,
R.layout.notification_2025_template_expanded_call,
R.layout.notification_2025_template_collapsed_messaging,
@@ -5963,7 +5965,10 @@ public class Notification implements Parcelable
|| resId == getCompactHeadsUpBaseLayoutResource()
|| resId == getMessagingCompactHeadsUpLayoutResource()
|| resId == getCollapsedMessagingLayoutResource()
- || resId == getCollapsedMediaLayoutResource());
+ || resId == getCollapsedMediaLayoutResource()
+ || resId == getCollapsedConversationLayoutResource()
+ || (notificationsRedesignTemplates()
+ && resId == getCollapsedCallLayoutResource()));
RemoteViews contentView = new BuilderRemoteViews(mContext.getApplicationInfo(), resId);
resetStandardTemplate(contentView);
@@ -6001,8 +6006,7 @@ public class Notification implements Parcelable
// Update margins to leave space for the top line (but not for headerless views like
// HUNS, which use a different layout that already accounts for that). Templates that
// have content that will be displayed under the small icon also use a different margin.
- if (Flags.notificationsRedesignTemplates()
- && !p.mHeaderless && !p.mSkipTopLineAlignment) {
+ if (Flags.notificationsRedesignTemplates() && !p.mHeaderless) {
int margin = getContentMarginTop(mContext,
R.dimen.notification_2025_content_margin_top);
contentView.setViewLayoutMargin(R.id.notification_main_column,
@@ -7673,12 +7677,18 @@ public class Notification implements Parcelable
}
}
+ // Note: In the 2025 redesign, we use two separate layouts for the collapsed and expanded
+ // version of conversations. See below.
private int getConversationLayoutResource() {
- if (Flags.notificationsRedesignTemplates()) {
- return R.layout.notification_2025_template_conversation;
- } else {
- return R.layout.notification_template_material_conversation;
- }
+ return R.layout.notification_template_material_conversation;
+ }
+
+ private int getCollapsedConversationLayoutResource() {
+ return R.layout.notification_2025_template_collapsed_conversation;
+ }
+
+ private int getExpandedConversationLayoutResource() {
+ return R.layout.notification_2025_template_expanded_conversation;
}
private int getCollapsedCallLayoutResource() {
@@ -9462,7 +9472,7 @@ public class Notification implements Parcelable
boolean hideRightIcons = viewType != StandardTemplateParams.VIEW_TYPE_NORMAL;
boolean isConversationLayout = mConversationType != CONVERSATION_TYPE_LEGACY;
boolean isImportantConversation = mConversationType == CONVERSATION_TYPE_IMPORTANT;
- boolean isHeaderless = !isConversationLayout && isCollapsed;
+ boolean isLegacyHeaderless = !isConversationLayout && isCollapsed;
//TODO (b/217799515): ensure mConversationTitle always returns the correct
// conversationTitle, probably set mConversationTitle = conversationTitle after this
@@ -9483,7 +9493,8 @@ public class Notification implements Parcelable
} else {
isOneToOne = !isGroupConversation();
}
- if (isHeaderless && isOneToOne && TextUtils.isEmpty(conversationTitle)) {
+ if ((isLegacyHeaderless || notificationsRedesignTemplates())
+ && isOneToOne && TextUtils.isEmpty(conversationTitle)) {
conversationTitle = getOtherPersonName();
}
@@ -9493,22 +9504,24 @@ public class Notification implements Parcelable
.viewType(viewType)
.highlightExpander(isConversationLayout)
.hideProgress(true)
- .title(isHeaderless ? conversationTitle : null)
.text(null)
.hideLeftIcon(isOneToOne)
- .hideRightIcon(hideRightIcons || isOneToOne)
- .headerTextSecondary(isHeaderless ? null : conversationTitle)
- .skipTopLineAlignment(true);
+ .hideRightIcon(hideRightIcons || isOneToOne);
+ if (notificationsRedesignTemplates()) {
+ p.title(conversationTitle)
+ .hideAppName(isCollapsed);
+ } else {
+ p.title(isLegacyHeaderless ? conversationTitle : null)
+ .headerTextSecondary(isLegacyHeaderless ? null : conversationTitle);
+ }
RemoteViews contentView = mBuilder.applyStandardTemplateWithActions(
- isConversationLayout
- ? mBuilder.getConversationLayoutResource()
- : isCollapsed
- ? mBuilder.getCollapsedMessagingLayoutResource()
- : mBuilder.getExpandedMessagingLayoutResource(),
+ getMessagingLayoutResource(isConversationLayout, isCollapsed),
p,
bindResult);
- if (isConversationLayout) {
+ if (isConversationLayout && !notificationsRedesignTemplates()) {
+ // Redesign note: This view is replaced by the `title`, which is handled normally.
mBuilder.setTextViewColorPrimary(contentView, R.id.conversation_text, p);
+ // Redesign note: This special divider is no longer needed.
mBuilder.setTextViewColorSecondary(contentView, R.id.app_name_divider, p);
}
@@ -9538,7 +9551,18 @@ public class Notification implements Parcelable
contentView.setBoolean(R.id.status_bar_latest_event_content,
"setIsImportantConversation", isImportantConversation);
}
- if (isHeaderless) {
+ if (notificationsRedesignTemplates() && !isCollapsed) {
+ // Align the title to the app/small icon in the expanded form. In other layouts,
+ // this margin is added directly to the notification_main_column parent, but for
+ // messages we don't want the margin to be applied to the actual messaging
+ // content since it can contain icons that are displayed below the app icon.
+ Resources res = mBuilder.mContext.getResources();
+ int marginStart = res.getDimensionPixelSize(
+ R.dimen.notification_2025_content_margin_start);
+ contentView.setViewLayoutMargin(R.id.title,
+ RemoteViews.MARGIN_START, marginStart, TypedValue.COMPLEX_UNIT_PX);
+ }
+ if (isLegacyHeaderless) {
// Collapsed legacy messaging style has a 1-line limit.
contentView.setInt(R.id.notification_messaging, "setMaxDisplayedLines", 1);
}
@@ -9549,6 +9573,33 @@ public class Notification implements Parcelable
return contentView;
}
+ private int getMessagingLayoutResource(boolean isConversationLayout, boolean isCollapsed) {
+ if (notificationsRedesignTemplates()) {
+ // Note: We eventually would like to use the same layouts for both conversations and
+ // regular messaging notifications.
+ if (isConversationLayout) {
+ if (isCollapsed) {
+ return mBuilder.getCollapsedConversationLayoutResource();
+ } else {
+ return mBuilder.getExpandedConversationLayoutResource();
+ }
+ } else {
+ if (isCollapsed) {
+ return mBuilder.getCollapsedMessagingLayoutResource();
+ } else {
+ return mBuilder.getExpandedMessagingLayoutResource();
+ }
+ }
+
+ } else {
+ return isConversationLayout
+ ? mBuilder.getConversationLayoutResource()
+ : isCollapsed
+ ? mBuilder.getCollapsedMessagingLayoutResource()
+ : mBuilder.getExpandedMessagingLayoutResource();
+ }
+ }
+
private CharSequence getKey(Person person) {
return person == null ? null
: person.getKey() == null ? person.getName() : person.getKey();
@@ -10986,6 +11037,7 @@ public class Notification implements Parcelable
private RemoteViews makeCallLayout(int viewType) {
final boolean isCollapsed = viewType == StandardTemplateParams.VIEW_TYPE_NORMAL;
+ final boolean isHeadsUp = viewType == StandardTemplateParams.VIEW_TYPE_HEADS_UP;
Bundle extras = mBuilder.mN.extras;
CharSequence title = mPerson != null ? mPerson.getName() : null;
CharSequence text = mBuilder.processLegacyText(extras.getCharSequence(EXTRA_TEXT));
@@ -11001,22 +11053,31 @@ public class Notification implements Parcelable
.hideLeftIcon(true)
.hideRightIcon(true)
.hideAppName(isCollapsed)
- .titleViewId(R.id.conversation_text)
.title(title)
- .text(text)
- .summaryText(mBuilder.processLegacyText(mVerificationText));
+ .text(text);
+ if (!notificationsRedesignTemplates()) {
+ // We're using the normal title in the redesign, not a special text.
+ p.titleViewId(R.id.conversation_text)
+ // The verification text is now part of the top line views, so this is no
+ // longer necessary.
+ .summaryText(mBuilder.processLegacyText(mVerificationText));
+ }
mBuilder.mActions = getActionsListWithSystemActions();
final RemoteViews contentView;
if (isCollapsed) {
contentView = mBuilder.applyStandardTemplate(
mBuilder.getCollapsedCallLayoutResource(), p, null /* result */);
+ } else if (notificationsRedesignTemplates() && isHeadsUp) {
+ contentView = mBuilder.applyStandardTemplateWithActions(
+ mBuilder.getCollapsedCallLayoutResource(), p, null /* result */);
} else {
contentView = mBuilder.applyStandardTemplateWithActions(
mBuilder.getExpandedCallLayoutResource(), p, null /* result */);
}
// Bind some extra conversation-specific header fields.
- if (!p.mHideAppName) {
+ if (!notificationsRedesignTemplates() && !p.mHideAppName) {
+ // Redesign note: This special divider is no longer needed.
mBuilder.setTextViewColorSecondary(contentView, R.id.app_name_divider, p);
contentView.setViewVisibility(R.id.app_name_divider, View.VISIBLE);
}
@@ -14676,7 +14737,6 @@ public class Notification implements Parcelable
Icon mPromotedPicture;
boolean mCallStyleActions;
boolean mAllowTextWithProgress;
- boolean mSkipTopLineAlignment;
int mTitleViewId;
int mTextViewId;
@Nullable CharSequence mTitle;
@@ -14702,7 +14762,6 @@ public class Notification implements Parcelable
mPromotedPicture = null;
mCallStyleActions = false;
mAllowTextWithProgress = false;
- mSkipTopLineAlignment = false;
mTitleViewId = R.id.title;
mTextViewId = R.id.text;
mTitle = null;
@@ -14769,11 +14828,6 @@ public class Notification implements Parcelable
return this;
}
- public StandardTemplateParams skipTopLineAlignment(boolean skipTopLineAlignment) {
- mSkipTopLineAlignment = skipTopLineAlignment;
- return this;
- }
-
final StandardTemplateParams hideSnoozeButton(boolean hideSnoozeButton) {
this.mHideSnoozeButton = hideSnoozeButton;
return this;
diff --git a/core/java/android/app/admin/DevicePolicyManager.java b/core/java/android/app/admin/DevicePolicyManager.java
index 08719fc549f8..5359ba44a3d2 100644
--- a/core/java/android/app/admin/DevicePolicyManager.java
+++ b/core/java/android/app/admin/DevicePolicyManager.java
@@ -14200,6 +14200,9 @@ public class DevicePolicyManager {
* <li>Manifest.permission.ACTIVITY_RECOGNITION</li>
* <li>Manifest.permission.BODY_SENSORS</li>
* </ul>
+ * On devices running {@link android.os.Build.VERSION_CODES#BAKLAVA}, the
+ * {@link android.health.connect.HealthPermissions} are also included in the
+ * restricted list.
* <p>
* A profile owner may not grant these permissions (i.e. call this method with any of the
* permissions listed above and {@code grantState} of {@code #PERMISSION_GRANT_STATE_GRANTED}),
@@ -17644,9 +17647,17 @@ public class DevicePolicyManager {
android.Manifest.permission.MANAGE_PROFILE_AND_DEVICE_OWNERS
})
public boolean isFinancedDevice() {
- return isDeviceManaged()
- && getDeviceOwnerType(getDeviceOwnerComponentOnAnyUser())
- == DEVICE_OWNER_TYPE_FINANCED;
+ try {
+ return isDeviceManaged()
+ && getDeviceOwnerType(getDeviceOwnerComponentOnAnyUser())
+ == DEVICE_OWNER_TYPE_FINANCED;
+ } catch (IllegalStateException e) {
+ // getDeviceOwnerType() will throw IllegalStateException if the device does not have a
+ // DO. This can happen under a race condition when the DO is removed after
+ // isDeviceManaged() (so it still returns true) but before getDeviceOwnerType().
+ // In this case, the device should not be considered a financed device.
+ return false;
+ }
}
// TODO(b/315298076): revert ag/25574027 and update the doc
diff --git a/core/java/android/app/supervision/flags.aconfig b/core/java/android/app/supervision/flags.aconfig
index 94de03877fd7..709418393479 100644
--- a/core/java/android/app/supervision/flags.aconfig
+++ b/core/java/android/app/supervision/flags.aconfig
@@ -72,3 +72,11 @@ flag {
description: "Flag that enables system APIs in Supervision Manager"
bug: "382034839"
}
+
+flag {
+ name: "enable_web_content_filters_screen"
+ is_exported: true
+ namespace: "supervision"
+ description: "Flag that enables the web content filters screen with Supervision settings entry point"
+ bug: "395134536"
+}
diff --git a/core/java/android/content/ContentResolver.java b/core/java/android/content/ContentResolver.java
index efcaa0ea6f07..a753cbf956c6 100644
--- a/core/java/android/content/ContentResolver.java
+++ b/core/java/android/content/ContentResolver.java
@@ -23,6 +23,8 @@ import android.annotation.IntDef;
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.annotation.RequiresPermission;
+import android.annotation.SpecialUsers.CanBeALL;
+import android.annotation.SpecialUsers.CanBeCURRENT;
import android.annotation.SuppressLint;
import android.annotation.SystemApi;
import android.annotation.TestApi;
@@ -2709,7 +2711,7 @@ public abstract class ContentResolver implements ContentInterface {
public final void registerContentObserverAsUser(@NonNull Uri uri,
boolean notifyForDescendants,
@NonNull ContentObserver observer,
- @NonNull UserHandle userHandle) {
+ @NonNull @CanBeALL @CanBeCURRENT UserHandle userHandle) {
Objects.requireNonNull(uri, "uri");
Objects.requireNonNull(observer, "observer");
Objects.requireNonNull(userHandle, "userHandle");
@@ -2723,7 +2725,7 @@ public abstract class ContentResolver implements ContentInterface {
/** @hide - designated user version */
@UnsupportedAppUsage
public final void registerContentObserver(Uri uri, boolean notifyForDescendants,
- ContentObserver observer, @UserIdInt int userHandle) {
+ ContentObserver observer, @CanBeALL @CanBeCURRENT @UserIdInt int userHandle) {
try {
getContentService().registerContentObserver(uri, notifyForDescendants,
observer.getContentObserver(), userHandle, mTargetSdkVersion);
diff --git a/core/java/android/content/res/Resources.java b/core/java/android/content/res/Resources.java
index 05596318aef5..2658efab0e44 100644
--- a/core/java/android/content/res/Resources.java
+++ b/core/java/android/content/res/Resources.java
@@ -482,7 +482,6 @@ public class Resources {
*
* @return Typeface The Typeface data associated with the resource.
*/
- @RavenwoodThrow(blockedBy = Typeface.class)
@NonNull public Typeface getFont(@FontRes int id) throws NotFoundException {
final TypedValue value = obtainTempTypedValue();
try {
@@ -507,7 +506,6 @@ public class Resources {
/**
* @hide
*/
- @RavenwoodThrow(blockedBy = Typeface.class)
public void preloadFonts(@ArrayRes int id) {
final TypedArray array = obtainTypedArray(id);
try {
diff --git a/core/java/android/content/res/ResourcesImpl.java b/core/java/android/content/res/ResourcesImpl.java
index 3727a3468c1f..8c76fd70afd9 100644
--- a/core/java/android/content/res/ResourcesImpl.java
+++ b/core/java/android/content/res/ResourcesImpl.java
@@ -529,6 +529,7 @@ public class ResourcesImpl {
for (int i = 0; i < locales.size(); i++) {
selectedLocales[i] = adjustLanguageTag(locales.get(i).toLanguageTag());
}
+ defaultLocale = adjustLanguageTag(lc.getDefaultLocale().toLanguageTag());
} else {
selectedLocales = new String[]{
adjustLanguageTag(locales.get(0).toLanguageTag())};
@@ -1067,7 +1068,6 @@ public class ResourcesImpl {
* Loads a font from XML or resources stream.
*/
@Nullable
- @RavenwoodThrow(blockedBy = Typeface.class)
public Typeface loadFont(Resources wrapper, TypedValue value, int id) {
if (value.string == null) {
throw new NotFoundException("Resource \"" + getResourceName(id) + "\" ("
diff --git a/core/java/android/content/res/TypedArray.java b/core/java/android/content/res/TypedArray.java
index 79185a10e156..ee7d008cf314 100644
--- a/core/java/android/content/res/TypedArray.java
+++ b/core/java/android/content/res/TypedArray.java
@@ -1043,7 +1043,6 @@ public class TypedArray implements AutoCloseable {
* not a font resource.
*/
@Nullable
- @RavenwoodThrow(blockedBy = Typeface.class)
public Typeface getFont(@StyleableRes int index) {
if (mRecycled) {
throw new RuntimeException("Cannot make calls to a recycled instance!");
diff --git a/core/java/android/graphics/fonts/FontFamilyUpdateRequest.java b/core/java/android/graphics/fonts/FontFamilyUpdateRequest.java
index bfbcfd828114..1bf01f4beb1b 100644
--- a/core/java/android/graphics/fonts/FontFamilyUpdateRequest.java
+++ b/core/java/android/graphics/fonts/FontFamilyUpdateRequest.java
@@ -68,6 +68,7 @@ import java.util.Objects;
* @hide
*/
@SystemApi
+@android.ravenwood.annotation.RavenwoodKeepWholeClass
public final class FontFamilyUpdateRequest {
/**
diff --git a/core/java/android/graphics/fonts/FontFileUpdateRequest.java b/core/java/android/graphics/fonts/FontFileUpdateRequest.java
index cf1dca965216..1f2be6fa5050 100644
--- a/core/java/android/graphics/fonts/FontFileUpdateRequest.java
+++ b/core/java/android/graphics/fonts/FontFileUpdateRequest.java
@@ -28,6 +28,7 @@ import java.util.Objects;
* @hide
*/
@SystemApi
+@android.ravenwood.annotation.RavenwoodKeepWholeClass
public final class FontFileUpdateRequest {
private final ParcelFileDescriptor mParcelFileDescriptor;
diff --git a/core/java/android/hardware/contexthub/HubEndpoint.java b/core/java/android/hardware/contexthub/HubEndpoint.java
index b7edef619a67..47ba51d98323 100644
--- a/core/java/android/hardware/contexthub/HubEndpoint.java
+++ b/core/java/android/hardware/contexthub/HubEndpoint.java
@@ -126,7 +126,16 @@ public class HubEndpoint {
if (sessionExists) {
Log.w(
TAG,
- "onSessionOpenComplete: session already exists, id=" + sessionId);
+ "onSessionOpenRequest: session already exists, id=" + sessionId);
+ }
+
+ if (mLifecycleCallback == null) {
+ Log.w(
+ TAG,
+ "onSessionOpenRequest: "
+ + "failed to open session, no lifecycle callback attached",
+ new Exception());
+ rejectSession(sessionId);
}
if (!sessionExists && mLifecycleCallback != null) {
diff --git a/core/java/android/hardware/contexthub/HubEndpointSession.java b/core/java/android/hardware/contexthub/HubEndpointSession.java
index ca59be8fcc65..c5e2d7a1d9f9 100644
--- a/core/java/android/hardware/contexthub/HubEndpointSession.java
+++ b/core/java/android/hardware/contexthub/HubEndpointSession.java
@@ -88,7 +88,6 @@ public class HubEndpointSession implements AutoCloseable {
: ContextHubTransaction.TYPE_HUB_MESSAGE_DEFAULT);
if (!isResponseRequired) {
// If the message doesn't require acknowledgement, respond with success immediately
- // TODO(b/379162322): Improve handling of synchronous failures.
mHubEndpoint.sendMessage(this, message, null);
ret.setResponse(
new ContextHubTransaction.Response<>(
diff --git a/core/java/android/hardware/display/DisplayManager.java b/core/java/android/hardware/display/DisplayManager.java
index 7850e377ec4d..92a56fc575e3 100644
--- a/core/java/android/hardware/display/DisplayManager.java
+++ b/core/java/android/hardware/display/DisplayManager.java
@@ -851,6 +851,12 @@ public final class DisplayManager {
* Registers a display listener to receive notifications about when
* displays are added, removed or changed.
*
+ * Because of the high frequency at which the refresh rate can change, clients will be
+ * registered for refresh rate change callbacks only when they request for refresh rate
+ * data({@link Display#getRefreshRate()}. Or alternately, you can consider using
+ * {@link #registerDisplayListener(Executor, long, DisplayListener)} and explicitly subscribe to
+ * {@link #EVENT_TYPE_DISPLAY_REFRESH_RATE} event
+ *
* We encourage to use {@link #registerDisplayListener(Executor, long, DisplayListener)}
* instead to subscribe for explicit events of interest
*
@@ -863,8 +869,8 @@ public final class DisplayManager {
public void registerDisplayListener(DisplayListener listener, Handler handler) {
registerDisplayListener(listener, handler, EVENT_TYPE_DISPLAY_ADDED
| EVENT_TYPE_DISPLAY_CHANGED
- | EVENT_TYPE_DISPLAY_REFRESH_RATE
- | EVENT_TYPE_DISPLAY_REMOVED);
+ | EVENT_TYPE_DISPLAY_REMOVED, 0,
+ ActivityThread.currentPackageName(), /* isEventFilterExplicit */ false);
}
/**
@@ -882,9 +888,8 @@ public final class DisplayManager {
*/
public void registerDisplayListener(@NonNull DisplayListener listener,
@Nullable Handler handler, @EventType long eventFilter) {
- mGlobal.registerDisplayListener(listener, handler,
- mGlobal.mapFiltersToInternalEventFlag(eventFilter, 0),
- ActivityThread.currentPackageName());
+ registerDisplayListener(listener, handler, eventFilter, 0,
+ ActivityThread.currentPackageName(), /* isEventFilterExplicit */ true);
}
/**
@@ -901,9 +906,8 @@ public final class DisplayManager {
@FlaggedApi(FLAG_DISPLAY_LISTENER_PERFORMANCE_IMPROVEMENTS)
public void registerDisplayListener(@NonNull Executor executor, @EventType long eventFilter,
@NonNull DisplayListener listener) {
- mGlobal.registerDisplayListener(listener, executor,
- mGlobal.mapFiltersToInternalEventFlag(eventFilter, 0),
- ActivityThread.currentPackageName());
+ registerDisplayListener(listener, executor, eventFilter, 0,
+ ActivityThread.currentPackageName(), /* isEventFilterExplicit */ true);
}
/**
@@ -924,9 +928,39 @@ public final class DisplayManager {
public void registerDisplayListener(@NonNull DisplayListener listener,
@Nullable Handler handler, @EventType long eventFilter,
@PrivateEventType long privateEventFilter) {
+ registerDisplayListener(listener, handler, eventFilter, privateEventFilter,
+ ActivityThread.currentPackageName(), /* isEventFilterExplicit */ true);
+ }
+
+ /**
+ * Registers a display listener to receive notifications about given display event types.
+ *
+ * @param listener The listener to register.
+ * @param handler The handler on which the listener should be invoked, or null
+ * if the listener should be invoked on the calling thread's looper.
+ * @param eventFilter A bitmask of the event types for which this listener is subscribed.
+ * @param privateEventFilter A bitmask of the private event types for which this listener
+ * is subscribed.
+ * @param isEventFilterExplicit Indicates if the client explicitly supplied the display events
+ * to be subscribed to.
+ *
+ */
+ private void registerDisplayListener(@NonNull DisplayListener listener,
+ @Nullable Handler handler, @EventType long eventFilter,
+ @PrivateEventType long privateEventFilter, String packageName,
+ boolean isEventFilterExplicit) {
mGlobal.registerDisplayListener(listener, handler,
mGlobal.mapFiltersToInternalEventFlag(eventFilter, privateEventFilter),
- ActivityThread.currentPackageName());
+ packageName, /* isEventFilterExplicit */ isEventFilterExplicit);
+ }
+
+ private void registerDisplayListener(@NonNull DisplayListener listener,
+ Executor executor, @EventType long eventFilter,
+ @PrivateEventType long privateEventFilter, String packageName,
+ boolean isEventFilterExplicit) {
+ mGlobal.registerDisplayListener(listener, executor,
+ mGlobal.mapFiltersToInternalEventFlag(eventFilter, privateEventFilter),
+ packageName, /* isEventFilterExplicit */ isEventFilterExplicit);
}
/**
@@ -1146,6 +1180,28 @@ public final class DisplayManager {
}
/**
+ * Resets the behavior that automatically registers clients for refresh rate change callbacks
+ * when they register via {@link #registerDisplayListener(DisplayListener, Handler)}
+ *
+ * <p>By default, clients are not registered for refresh rate change callbacks via
+ * {@link #registerDisplayListener(DisplayListener, Handler)}. However, calling
+ * {@link Display#getRefreshRate()} triggers automatic registration for existing and future
+ * {@link DisplayListener} instances. This method reverts this behavior, preventing new
+ * clients from being automatically registered for refresh rate change callbacks. Note that the
+ * existing ones will continue to stay registered
+ *
+ * <p>In essence, this method returns the system to its initial state, where explicit calls to
+ * {{@link Display#getRefreshRate()} are required to receive refresh rate change notifications.
+ *
+ * @hide
+ */
+ @FlaggedApi(Flags.FLAG_DELAY_IMPLICIT_RR_REGISTRATION_UNTIL_RR_ACCESSED)
+ @TestApi
+ public void resetImplicitRefreshRateCallbackStatus() {
+ mGlobal.resetImplicitRefreshRateCallbackStatus();
+ }
+
+ /**
* Overrides HDR modes for a display device.
*
* @hide
diff --git a/core/java/android/hardware/display/DisplayManagerGlobal.java b/core/java/android/hardware/display/DisplayManagerGlobal.java
index a7d610e54e2c..c4af87116eed 100644
--- a/core/java/android/hardware/display/DisplayManagerGlobal.java
+++ b/core/java/android/hardware/display/DisplayManagerGlobal.java
@@ -187,6 +187,9 @@ public final class DisplayManagerGlobal {
private final Binder mToken = new Binder();
+ // Guarded by mLock
+ private boolean mShouldImplicitlyRegisterRrChanges = false;
+
@VisibleForTesting
public DisplayManagerGlobal(IDisplayManager dm) {
mDm = dm;
@@ -390,27 +393,49 @@ public final class DisplayManagerGlobal {
* the handler for the main thread.
* If that is still null, a runtime exception will be thrown.
* @param packageName of the calling package.
+ * @param isEventFilterExplicit Indicates if the client explicitly supplied the display events
+ * to be subscribed to.
*/
public void registerDisplayListener(@NonNull DisplayListener listener,
@Nullable Handler handler, @InternalEventFlag long internalEventFlagsMask,
- String packageName) {
+ String packageName, boolean isEventFilterExplicit) {
Looper looper = getLooperForHandler(handler);
Handler springBoard = new Handler(looper);
registerDisplayListener(listener, new HandlerExecutor(springBoard), internalEventFlagsMask,
- packageName);
+ packageName, isEventFilterExplicit);
}
/**
* Register a listener for display-related changes.
*
* @param listener The listener that will be called when display changes occur.
+ * @param handler Handler for the thread that will be receiving the callbacks. May be null.
+ * If null, listener will use the handler for the current thread, and if still null,
+ * the handler for the main thread.
+ * If that is still null, a runtime exception will be thrown.
+ * @param internalEventFlagsMask Mask of events to be listened to.
+ * @param packageName of the calling package.
+ */
+ public void registerDisplayListener(@NonNull DisplayListener listener,
+ @Nullable Handler handler, @InternalEventFlag long internalEventFlagsMask,
+ String packageName) {
+ registerDisplayListener(listener, handler, internalEventFlagsMask, packageName, true);
+ }
+
+
+ /**
+ * Register a listener for display-related changes.
+ *
+ * @param listener The listener that will be called when display changes occur.
* @param executor Executor for the thread that will be receiving the callbacks. Cannot be null.
* @param internalEventFlagsMask Mask of events to be listened to.
* @param packageName of the calling package.
+ * @param isEventFilterExplicit Indicates if the explicit events to be subscribed to
+ * were supplied or not
*/
public void registerDisplayListener(@NonNull DisplayListener listener,
@NonNull Executor executor, @InternalEventFlag long internalEventFlagsMask,
- String packageName) {
+ String packageName, boolean isEventFilterExplicit) {
if (listener == null) {
throw new IllegalArgumentException("listener must not be null");
}
@@ -429,7 +454,7 @@ public final class DisplayManagerGlobal {
int index = findDisplayListenerLocked(listener);
if (index < 0) {
mDisplayListeners.add(new DisplayListenerDelegate(listener, executor,
- internalEventFlagsMask, packageName));
+ internalEventFlagsMask, packageName, isEventFilterExplicit));
registerCallbackIfNeededLocked();
} else {
mDisplayListeners.get(index).setEventsMask(internalEventFlagsMask);
@@ -439,6 +464,22 @@ public final class DisplayManagerGlobal {
}
}
+
+ /**
+ * Registers all the clients to INTERNAL_EVENT_FLAG_DISPLAY_REFRESH_RATE events if qualified
+ */
+ public void registerForRefreshRateChanges() {
+ if (!Flags.delayImplicitRrRegistrationUntilRrAccessed()) {
+ return;
+ }
+ synchronized (mLock) {
+ if (!mShouldImplicitlyRegisterRrChanges) {
+ mShouldImplicitlyRegisterRrChanges = true;
+ updateCallbackIfNeededLocked();
+ }
+ }
+ }
+
public void unregisterDisplayListener(DisplayListener listener) {
if (listener == null) {
throw new IllegalArgumentException("listener must not be null");
@@ -521,8 +562,14 @@ public final class DisplayManagerGlobal {
long mask = 0;
final int numListeners = mDisplayListeners.size();
for (int i = 0; i < numListeners; i++) {
- mask |= mDisplayListeners.get(i).mInternalEventFlagsMask;
+ DisplayListenerDelegate displayListenerDelegate = mDisplayListeners.get(i);
+ if (!Flags.delayImplicitRrRegistrationUntilRrAccessed()
+ || mShouldImplicitlyRegisterRrChanges) {
+ displayListenerDelegate.implicitlyRegisterForRRChanges();
+ }
+ mask |= displayListenerDelegate.mInternalEventFlagsMask;
}
+
if (mDispatchNativeCallbacks) {
mask |= INTERNAL_EVENT_FLAG_DISPLAY_ADDED
| INTERNAL_EVENT_FLAG_DISPLAY_BASIC_CHANGED
@@ -802,6 +849,18 @@ public final class DisplayManagerGlobal {
}
/**
+ * Resets the implicit registration of refresh rate change callbacks
+ *
+ */
+ public void resetImplicitRefreshRateCallbackStatus() {
+ if (Flags.delayImplicitRrRegistrationUntilRrAccessed()) {
+ synchronized (mLock) {
+ mShouldImplicitlyRegisterRrChanges = false;
+ }
+ }
+ }
+
+ /**
* Overrides HDR modes for a display device.
*
*/
@@ -1439,21 +1498,27 @@ public final class DisplayManagerGlobal {
}
}
- private static final class DisplayListenerDelegate {
+ @VisibleForTesting
+ static final class DisplayListenerDelegate {
public final DisplayListener mListener;
public volatile long mInternalEventFlagsMask;
+ // Indicates if the client explicitly supplied the display events to be subscribed to.
+ private final boolean mIsEventFilterExplicit;
+
private final DisplayInfo mDisplayInfo = new DisplayInfo();
private final Executor mExecutor;
private AtomicLong mGenerationId = new AtomicLong(1);
private final String mPackageName;
DisplayListenerDelegate(DisplayListener listener, @NonNull Executor executor,
- @InternalEventFlag long internalEventFlag, String packageName) {
+ @InternalEventFlag long internalEventFlag, String packageName,
+ boolean isEventFilterExplicit) {
mExecutor = executor;
mListener = listener;
mInternalEventFlagsMask = internalEventFlag;
mPackageName = packageName;
+ mIsEventFilterExplicit = isEventFilterExplicit;
}
void sendDisplayEvent(int displayId, @DisplayEvent int event, @Nullable DisplayInfo info,
@@ -1470,6 +1535,11 @@ public final class DisplayManagerGlobal {
});
}
+ @VisibleForTesting
+ boolean isEventFilterExplicit() {
+ return mIsEventFilterExplicit;
+ }
+
void clearEvents() {
mGenerationId.incrementAndGet();
}
@@ -1478,6 +1548,17 @@ public final class DisplayManagerGlobal {
mInternalEventFlagsMask = newInternalEventFlagsMask;
}
+ private void implicitlyRegisterForRRChanges() {
+ // For backward compatibility, if the user didn't supply the explicit events while
+ // subscribing, register them to refresh rate change events if they subscribed to
+ // display changed events
+ if ((mInternalEventFlagsMask & INTERNAL_EVENT_FLAG_DISPLAY_BASIC_CHANGED) != 0
+ && !mIsEventFilterExplicit) {
+ setEventsMask(mInternalEventFlagsMask
+ | INTERNAL_EVENT_FLAG_DISPLAY_REFRESH_RATE);
+ }
+ }
+
private void handleDisplayEventInner(int displayId, @DisplayEvent int event,
@Nullable DisplayInfo info, boolean forceUpdate) {
if (extraLogging()) {
@@ -1677,6 +1758,9 @@ public final class DisplayManagerGlobal {
public void registerNativeChoreographerForRefreshRateCallbacks() {
synchronized (mLock) {
mDispatchNativeCallbacks = true;
+ if (Flags.delayImplicitRrRegistrationUntilRrAccessed()) {
+ mShouldImplicitlyRegisterRrChanges = true;
+ }
registerCallbackIfNeededLocked();
updateCallbackIfNeededLocked();
DisplayInfo display = getDisplayInfoLocked(Display.DEFAULT_DISPLAY);
@@ -1806,4 +1890,9 @@ public final class DisplayManagerGlobal {
return baseEventMask;
}
+
+ @VisibleForTesting
+ CopyOnWriteArrayList<DisplayListenerDelegate> getDisplayListeners() {
+ return mDisplayListeners;
+ }
}
diff --git a/core/java/android/inputmethodservice/InputMethodService.java b/core/java/android/inputmethodservice/InputMethodService.java
index a528ba4b16bf..7b47efd47008 100644
--- a/core/java/android/inputmethodservice/InputMethodService.java
+++ b/core/java/android/inputmethodservice/InputMethodService.java
@@ -990,6 +990,8 @@ public class InputMethodService extends AbstractInputMethodService {
}
Trace.traceEnd(TRACE_TAG_WINDOW_MANAGER);
if (android.view.inputmethod.Flags.refactorInsetsController()) {
+ // After the IME window was hidden, we can remove its surface
+ scheduleImeSurfaceRemoval();
// The hide request first finishes the animation and then proceeds to the server
// side, finally reaching here, marking this the end state.
ImeTracker.forLogging().onHidden(statsToken);
diff --git a/core/java/android/os/BundleMerger.java b/core/java/android/os/BundleMerger.java
index dc243a5e9c08..fd5a18c22a37 100644
--- a/core/java/android/os/BundleMerger.java
+++ b/core/java/android/os/BundleMerger.java
@@ -119,11 +119,23 @@ public class BundleMerger implements Parcelable {
public static final int STRATEGY_ARRAY_APPEND = 50;
/**
+ * Merge strategy that combines two conflicting array values by creating a new array
+ * containing all unique elements from both arrays.
+ */
+ public static final int STRATEGY_ARRAY_UNION = 55;
+
+ /**
* Merge strategy that combines two conflicting {@link ArrayList} values by
* appending the last {@link ArrayList} after the first {@link ArrayList}.
*/
public static final int STRATEGY_ARRAY_LIST_APPEND = 60;
+ /**
+ * Merge strategy that combines two conflicting {@link String} values by
+ * appending the last {@link String} after the first {@link String}.
+ */
+ public static final int STRATEGY_STRING_APPEND = 70;
+
@IntDef(flag = false, prefix = { "STRATEGY_" }, value = {
STRATEGY_REJECT,
STRATEGY_FIRST,
@@ -136,7 +148,9 @@ public class BundleMerger implements Parcelable {
STRATEGY_BOOLEAN_AND,
STRATEGY_BOOLEAN_OR,
STRATEGY_ARRAY_APPEND,
+ STRATEGY_ARRAY_UNION,
STRATEGY_ARRAY_LIST_APPEND,
+ STRATEGY_STRING_APPEND,
})
@Retention(RetentionPolicy.SOURCE)
public @interface Strategy {}
@@ -298,8 +312,12 @@ public class BundleMerger implements Parcelable {
return booleanOr(first, last);
case STRATEGY_ARRAY_APPEND:
return arrayAppend(first, last);
+ case STRATEGY_ARRAY_UNION:
+ return arrayUnion(first, last);
case STRATEGY_ARRAY_LIST_APPEND:
return arrayListAppend(first, last);
+ case STRATEGY_STRING_APPEND:
+ return stringAppend(first, last);
default:
throw new UnsupportedOperationException();
}
@@ -361,6 +379,29 @@ public class BundleMerger implements Parcelable {
return res;
}
+ private static @NonNull Object arrayUnion(@NonNull Object first, @NonNull Object last) {
+ if (!first.getClass().isArray()) {
+ throw new IllegalArgumentException("Unable to union " + first.getClass());
+ }
+ final int firstLength = Array.getLength(first);
+ final int lastLength = Array.getLength(last);
+ final ArrayList<Object> list = new ArrayList<>(firstLength + lastLength);
+ final ArraySet<Object> set = new ArraySet<>();
+ for (int i = 0; i < firstLength; i++) {
+ set.add(Array.get(first, i));
+ }
+ for (int i = 0; i < lastLength; i++) {
+ set.add(Array.get(last, i));
+ }
+ final Class<?> clazz = first.getClass().getComponentType();
+ final int setSize = set.size();
+ final Object res = Array.newInstance(clazz, setSize);
+ for (int i = 0; i < setSize; i++) {
+ Array.set(res, i, set.valueAt(i));
+ }
+ return res;
+ }
+
@SuppressWarnings("unchecked")
private static @NonNull Object arrayListAppend(@NonNull Object first, @NonNull Object last) {
if (!(first instanceof ArrayList)) {
@@ -374,6 +415,13 @@ public class BundleMerger implements Parcelable {
return res;
}
+ private static @NonNull Object stringAppend(@NonNull Object first, @NonNull Object last) {
+ if (!(first instanceof String)) {
+ throw new IllegalArgumentException("Unable to append " + first.getClass());
+ }
+ return ((String) first) + ((String) last);
+ }
+
public static final @android.annotation.NonNull Parcelable.Creator<BundleMerger> CREATOR =
new Parcelable.Creator<BundleMerger>() {
@Override
diff --git a/core/java/android/os/PerfettoTrace.java b/core/java/android/os/PerfettoTrace.java
index 741d542ecb3b..932836f8a050 100644
--- a/core/java/android/os/PerfettoTrace.java
+++ b/core/java/android/os/PerfettoTrace.java
@@ -232,10 +232,6 @@ public final class PerfettoTrace {
* @param eventName The event name to appear in the trace.
*/
public static PerfettoTrackEventExtra.Builder instant(Category category, String eventName) {
- if (!category.isEnabled()) {
- return PerfettoTrackEventExtra.noOpBuilder();
- }
-
return PerfettoTrackEventExtra.builder().init(PERFETTO_TE_TYPE_INSTANT, category)
.setEventName(eventName);
}
@@ -247,10 +243,6 @@ public final class PerfettoTrace {
* @param eventName The event name to appear in the trace.
*/
public static PerfettoTrackEventExtra.Builder begin(Category category, String eventName) {
- if (!category.isEnabled()) {
- return PerfettoTrackEventExtra.noOpBuilder();
- }
-
return PerfettoTrackEventExtra.builder().init(PERFETTO_TE_TYPE_SLICE_BEGIN, category)
.setEventName(eventName);
}
@@ -261,10 +253,6 @@ public final class PerfettoTrace {
* @param category The perfetto category.
*/
public static PerfettoTrackEventExtra.Builder end(Category category) {
- if (!category.isEnabled()) {
- return PerfettoTrackEventExtra.noOpBuilder();
- }
-
return PerfettoTrackEventExtra.builder().init(PERFETTO_TE_TYPE_SLICE_END, category);
}
@@ -275,10 +263,6 @@ public final class PerfettoTrace {
* @param value The value of the counter.
*/
public static PerfettoTrackEventExtra.Builder counter(Category category, long value) {
- if (!category.isEnabled()) {
- return PerfettoTrackEventExtra.noOpBuilder();
- }
-
return PerfettoTrackEventExtra.builder().init(PERFETTO_TE_TYPE_COUNTER, category)
.setCounter(value);
}
@@ -302,10 +286,6 @@ public final class PerfettoTrace {
* @param value The value of the counter.
*/
public static PerfettoTrackEventExtra.Builder counter(Category category, double value) {
- if (!category.isEnabled()) {
- return PerfettoTrackEventExtra.noOpBuilder();
- }
-
return PerfettoTrackEventExtra.builder().init(PERFETTO_TE_TYPE_COUNTER, category)
.setCounter(value);
}
diff --git a/core/java/android/os/PerfettoTrackEventExtra.java b/core/java/android/os/PerfettoTrackEventExtra.java
index 68442293c3a3..f4b5dfe76f88 100644
--- a/core/java/android/os/PerfettoTrackEventExtra.java
+++ b/core/java/android/os/PerfettoTrackEventExtra.java
@@ -35,8 +35,8 @@ import java.util.function.Supplier;
*/
@android.ravenwood.annotation.RavenwoodKeepWholeClass
public final class PerfettoTrackEventExtra {
+ private static final boolean DEBUG = false;
private static final int DEFAULT_EXTRA_CACHE_SIZE = 5;
- private static final Builder NO_OP_BUILDER = new NoOpBuilder();
private static final ThreadLocal<PerfettoTrackEventExtra> sTrackEventExtra =
new ThreadLocal<PerfettoTrackEventExtra>() {
@Override
@@ -46,7 +46,7 @@ public final class PerfettoTrackEventExtra {
};
private static final AtomicLong sNamedTrackId = new AtomicLong();
private static final Supplier<Flow> sFlowSupplier = Flow::new;
- private static final Supplier<BuilderImpl> sBuilderSupplier = BuilderImpl::new;
+ private static final Supplier<Builder> sBuilderSupplier = Builder::new;
private static final Supplier<FieldInt64> sFieldInt64Supplier = FieldInt64::new;
private static final Supplier<FieldDouble> sFieldDoubleSupplier = FieldDouble::new;
private static final Supplier<FieldString> sFieldStringSupplier = FieldString::new;
@@ -56,6 +56,8 @@ public final class PerfettoTrackEventExtra {
private CounterInt64 mCounterInt64;
private CounterDouble mCounterDouble;
private Proto mProto;
+ private Flow mFlow;
+ private Flow mTerminatingFlow;
/**
* Represents a native pointer to a Perfetto C SDK struct. E.g. PerfettoTeHlExtra.
@@ -135,245 +137,10 @@ public final class PerfettoTrackEventExtra {
}
}
- public interface Builder {
- /**
- * Emits the track event.
- */
- void emit();
-
- /**
- * Initialize the builder for a new trace event.
- */
- Builder init(int traceType, PerfettoTrace.Category category);
-
- /**
- * Sets the event name for the track event.
- */
- Builder setEventName(String eventName);
-
- /**
- * Adds a debug arg with key {@code name} and value {@code val}.
- */
- Builder addArg(String name, long val);
-
- /**
- * Adds a debug arg with key {@code name} and value {@code val}.
- */
- Builder addArg(String name, boolean val);
-
- /**
- * Adds a debug arg with key {@code name} and value {@code val}.
- */
- Builder addArg(String name, double val);
-
- /**
- * Adds a debug arg with key {@code name} and value {@code val}.
- */
- Builder addArg(String name, String val);
-
- /**
- * Adds a flow with {@code id}.
- */
- Builder addFlow(int id);
-
- /**
- * Adds a terminating flow with {@code id}.
- */
- Builder addTerminatingFlow(int id);
-
- /**
- * Adds the events to a named track instead of the thread track where the
- * event occurred.
- */
- Builder usingNamedTrack(long parentUuid, String name);
-
- /**
- * Adds the events to a process scoped named track instead of the thread track where the
- * event occurred.
- */
- Builder usingProcessNamedTrack(String name);
-
- /**
- * Adds the events to a thread scoped named track instead of the thread track where the
- * event occurred.
- */
- Builder usingThreadNamedTrack(long tid, String name);
-
- /**
- * Adds the events to a counter track instead. This is required for
- * setting counter values.
- */
- Builder usingCounterTrack(long parentUuid, String name);
-
- /**
- * Adds the events to a process scoped counter track instead. This is required for
- * setting counter values.
- */
- Builder usingProcessCounterTrack(String name);
-
- /**
- * Adds the events to a thread scoped counter track instead. This is required for
- * setting counter values.
- */
- Builder usingThreadCounterTrack(long tid, String name);
-
- /**
- * Sets a long counter value on the event.
- *
- */
- Builder setCounter(long val);
-
- /**
- * Sets a double counter value on the event.
- *
- */
- Builder setCounter(double val);
-
- /**
- * Adds a proto field with field id {@code id} and value {@code val}.
- */
- Builder addField(long id, long val);
-
- /**
- * Adds a proto field with field id {@code id} and value {@code val}.
- */
- Builder addField(long id, double val);
-
- /**
- * Adds a proto field with field id {@code id} and value {@code val}.
- */
- Builder addField(long id, String val);
-
- /**
- * Begins a proto field with field
- * Fields can be added from this point and there must be a corresponding
- * {@link endProto}.
- *
- * The proto field is a singleton and all proto fields get added inside the
- * one {@link beginProto} and {@link endProto} within the {@link Builder}.
- */
- Builder beginProto();
-
- /**
- * Ends a proto field.
- */
- Builder endProto();
-
- /**
- * Begins a nested proto field with field id {@code id}.
- * Fields can be added from this point and there must be a corresponding
- * {@link endNested}.
- */
- Builder beginNested(long id);
-
- /**
- * Ends a nested proto field.
- */
- Builder endNested();
- }
-
- @android.ravenwood.annotation.RavenwoodKeepWholeClass
- public static final class NoOpBuilder implements Builder {
- @Override
- public void emit() {}
- @Override
- public Builder init(int traceType, PerfettoTrace.Category category) {
- return this;
- }
- @Override
- public Builder setEventName(String eventName) {
- return this;
- }
- @Override
- public Builder addArg(String name, long val) {
- return this;
- }
- @Override
- public Builder addArg(String name, boolean val) {
- return this;
- }
- @Override
- public Builder addArg(String name, double val) {
- return this;
- }
- @Override
- public Builder addArg(String name, String val) {
- return this;
- }
- @Override
- public Builder addFlow(int id) {
- return this;
- }
- @Override
- public Builder addTerminatingFlow(int id) {
- return this;
- }
- @Override
- public Builder usingNamedTrack(long parentUuid, String name) {
- return this;
- }
- @Override
- public Builder usingProcessNamedTrack(String name) {
- return this;
- }
- @Override
- public Builder usingThreadNamedTrack(long tid, String name) {
- return this;
- }
- @Override
- public Builder usingCounterTrack(long parentUuid, String name) {
- return this;
- }
- @Override
- public Builder usingProcessCounterTrack(String name) {
- return this;
- }
- @Override
- public Builder usingThreadCounterTrack(long tid, String name) {
- return this;
- }
- @Override
- public Builder setCounter(long val) {
- return this;
- }
- @Override
- public Builder setCounter(double val) {
- return this;
- }
- @Override
- public Builder addField(long id, long val) {
- return this;
- }
- @Override
- public Builder addField(long id, double val) {
- return this;
- }
- @Override
- public Builder addField(long id, String val) {
- return this;
- }
- @Override
- public Builder beginProto() {
- return this;
- }
- @Override
- public Builder endProto() {
- return this;
- }
- @Override
- public Builder beginNested(long id) {
- return this;
- }
- @Override
- public Builder endNested() {
- return this;
- }
- }
-
/**
* Builder for Perfetto track event extras.
*/
- public static final class BuilderImpl implements Builder {
+ public static final class Builder {
// For performance reasons, we hold a reference to mExtra as a holder for
// perfetto pointers being added. This way, we avoid an additional list to hold
// the pointers in Java and we can pass them down directly to native code.
@@ -386,10 +153,13 @@ public final class PerfettoTrackEventExtra {
private Builder mParent;
private FieldContainer mCurrentContainer;
+ private boolean mIsCategoryEnabled;
private final CounterInt64 mCounterInt64;
private final CounterDouble mCounterDouble;
private final Proto mProto;
+ private final Flow mFlow;
+ private final Flow mTerminatingFlow;
private final RingBuffer<NamedTrack> mNamedTrackCache;
private final RingBuffer<CounterTrack> mCounterTrackCache;
@@ -403,9 +173,9 @@ public final class PerfettoTrackEventExtra {
private final Pool<FieldString> mFieldStringCache;
private final Pool<FieldNested> mFieldNestedCache;
private final Pool<Flow> mFlowCache;
- private final Pool<BuilderImpl> mBuilderCache;
+ private final Pool<Builder> mBuilderCache;
- private BuilderImpl() {
+ private Builder() {
mExtra = sTrackEventExtra.get();
mNamedTrackCache = mExtra.mNamedTrackCache;
mCounterTrackCache = mExtra.mCounterTrackCache;
@@ -423,20 +193,32 @@ public final class PerfettoTrackEventExtra {
mCounterInt64 = mExtra.getCounterInt64();
mCounterDouble = mExtra.getCounterDouble();
mProto = mExtra.getProto();
+ mFlow = mExtra.getFlow();
+ mTerminatingFlow = mExtra.getTerminatingFlow();
}
- @Override
+ /**
+ * Emits the track event.
+ */
public void emit() {
- checkParent();
- mIsBuilt = true;
+ if (!mIsCategoryEnabled) {
+ return;
+ }
+ if (DEBUG) {
+ checkParent();
+ }
+ mIsBuilt = true;
native_emit(mTraceType, mCategory.getPtr(), mEventName, mExtra.getPtr());
- // Reset after emitting to free any the extras used to trace the event.
- mExtra.reset();
}
- @Override
+ /**
+ * Initialize the builder for a new trace event.
+ */
public Builder init(int traceType, PerfettoTrace.Category category) {
+ if (!category.isEnabled()) {
+ return this;
+ }
mTraceType = traceType;
mCategory = category;
mEventName = "";
@@ -449,18 +231,27 @@ public final class PerfettoTrackEventExtra {
mExtra.reset();
// Reset after on init in case the thread created builders without calling emit
- return initInternal(this, null);
+ return initInternal(this, null, true);
}
- @Override
+ /**
+ * Sets the event name for the track event.
+ */
public Builder setEventName(String eventName) {
mEventName = eventName;
return this;
}
- @Override
+ /**
+ * Adds a debug arg with key {@code name} and value {@code val}.
+ */
public Builder addArg(String name, long val) {
- checkParent();
+ if (!mIsCategoryEnabled) {
+ return this;
+ }
+ if (DEBUG) {
+ checkParent();
+ }
ArgInt64 arg = mArgInt64Cache.get(name.hashCode());
if (arg == null || !arg.getName().equals(name)) {
arg = new ArgInt64(name);
@@ -471,9 +262,16 @@ public final class PerfettoTrackEventExtra {
return this;
}
- @Override
+ /**
+ * Adds a debug arg with key {@code name} and value {@code val}.
+ */
public Builder addArg(String name, boolean val) {
- checkParent();
+ if (!mIsCategoryEnabled) {
+ return this;
+ }
+ if (DEBUG) {
+ checkParent();
+ }
ArgBool arg = mArgBoolCache.get(name.hashCode());
if (arg == null || !arg.getName().equals(name)) {
arg = new ArgBool(name);
@@ -484,9 +282,16 @@ public final class PerfettoTrackEventExtra {
return this;
}
- @Override
+ /**
+ * Adds a debug arg with key {@code name} and value {@code val}.
+ */
public Builder addArg(String name, double val) {
- checkParent();
+ if (!mIsCategoryEnabled) {
+ return this;
+ }
+ if (DEBUG) {
+ checkParent();
+ }
ArgDouble arg = mArgDoubleCache.get(name.hashCode());
if (arg == null || !arg.getName().equals(name)) {
arg = new ArgDouble(name);
@@ -497,9 +302,16 @@ public final class PerfettoTrackEventExtra {
return this;
}
- @Override
+ /**
+ * Adds a debug arg with key {@code name} and value {@code val}.
+ */
public Builder addArg(String name, String val) {
- checkParent();
+ if (!mIsCategoryEnabled) {
+ return this;
+ }
+ if (DEBUG) {
+ checkParent();
+ }
ArgString arg = mArgStringCache.get(name.hashCode());
if (arg == null || !arg.getName().equals(name)) {
arg = new ArgString(name);
@@ -510,27 +322,79 @@ public final class PerfettoTrackEventExtra {
return this;
}
- @Override
+ /**
+ * Adds a flow with {@code id}.
+ */
public Builder addFlow(int id) {
- checkParent();
+ if (!mIsCategoryEnabled) {
+ return this;
+ }
+ if (DEBUG) {
+ checkParent();
+ }
Flow flow = mFlowCache.get(sFlowSupplier);
flow.setProcessFlow(id);
mExtra.addPerfettoPointer(flow);
return this;
}
- @Override
+ /**
+ * Adds a terminating flow with {@code id}.
+ */
public Builder addTerminatingFlow(int id) {
- checkParent();
+ if (!mIsCategoryEnabled) {
+ return this;
+ }
+ if (DEBUG) {
+ checkParent();
+ }
Flow flow = mFlowCache.get(sFlowSupplier);
flow.setProcessTerminatingFlow(id);
mExtra.addPerfettoPointer(flow);
return this;
}
- @Override
+ /**
+ * Adds a flow with {@code id}.
+ */
+ public Builder setFlow(int id) {
+ if (!mIsCategoryEnabled) {
+ return this;
+ }
+ if (DEBUG) {
+ checkParent();
+ }
+ mFlow.setProcessFlow(id);
+ mExtra.addPerfettoPointer(mFlow);
+ return this;
+ }
+
+ /**
+ * Adds a terminating flow with {@code id}.
+ */
+ public Builder setTerminatingFlow(int id) {
+ if (!mIsCategoryEnabled) {
+ return this;
+ }
+ if (DEBUG) {
+ checkParent();
+ }
+ mTerminatingFlow.setProcessTerminatingFlow(id);
+ mExtra.addPerfettoPointer(mTerminatingFlow);
+ return this;
+ }
+
+ /**
+ * Adds the events to a named track instead of the thread track where the
+ * event occurred.
+ */
public Builder usingNamedTrack(long parentUuid, String name) {
- checkParent();
+ if (!mIsCategoryEnabled) {
+ return this;
+ }
+ if (DEBUG) {
+ checkParent();
+ }
NamedTrack track = mNamedTrackCache.get(name.hashCode());
if (track == null || !track.getName().equals(name)) {
@@ -541,19 +405,39 @@ public final class PerfettoTrackEventExtra {
return this;
}
- @Override
+ /**
+ * Adds the events to a process scoped named track instead of the thread track where the
+ * event occurred.
+ */
public Builder usingProcessNamedTrack(String name) {
+ if (!mIsCategoryEnabled) {
+ return this;
+ }
return usingNamedTrack(PerfettoTrace.getProcessTrackUuid(), name);
}
- @Override
+ /**
+ * Adds the events to a thread scoped named track instead of the thread track where the
+ * event occurred.
+ */
public Builder usingThreadNamedTrack(long tid, String name) {
+ if (!mIsCategoryEnabled) {
+ return this;
+ }
return usingNamedTrack(PerfettoTrace.getThreadTrackUuid(tid), name);
}
- @Override
+ /**
+ * Adds the events to a counter track instead. This is required for
+ * setting counter values.
+ */
public Builder usingCounterTrack(long parentUuid, String name) {
- checkParent();
+ if (!mIsCategoryEnabled) {
+ return this;
+ }
+ if (DEBUG) {
+ checkParent();
+ }
CounterTrack track = mCounterTrackCache.get(name.hashCode());
if (track == null || !track.getName().equals(name)) {
@@ -564,95 +448,178 @@ public final class PerfettoTrackEventExtra {
return this;
}
- @Override
+ /**
+ * Adds the events to a process scoped counter track instead. This is required for
+ * setting counter values.
+ */
public Builder usingProcessCounterTrack(String name) {
+ if (!mIsCategoryEnabled) {
+ return this;
+ }
return usingCounterTrack(PerfettoTrace.getProcessTrackUuid(), name);
}
- @Override
+ /**
+ * Adds the events to a thread scoped counter track instead. This is required for
+ * setting counter values.
+ */
public Builder usingThreadCounterTrack(long tid, String name) {
+ if (!mIsCategoryEnabled) {
+ return this;
+ }
return usingCounterTrack(PerfettoTrace.getThreadTrackUuid(tid), name);
}
- @Override
+ /**
+ * Sets a long counter value on the event.
+ *
+ */
public Builder setCounter(long val) {
- checkParent();
+ if (!mIsCategoryEnabled) {
+ return this;
+ }
+ if (DEBUG) {
+ checkParent();
+ }
mCounterInt64.setValue(val);
mExtra.addPerfettoPointer(mCounterInt64);
return this;
}
- @Override
+ /**
+ * Sets a double counter value on the event.
+ *
+ */
public Builder setCounter(double val) {
- checkParent();
+ if (!mIsCategoryEnabled) {
+ return this;
+ }
+ if (DEBUG) {
+ checkParent();
+ }
mCounterDouble.setValue(val);
mExtra.addPerfettoPointer(mCounterDouble);
return this;
}
- @Override
+ /**
+ * Adds a proto field with field id {@code id} and value {@code val}.
+ */
public Builder addField(long id, long val) {
- checkContainer();
+ if (!mIsCategoryEnabled) {
+ return this;
+ }
+ if (DEBUG) {
+ checkContainer();
+ }
FieldInt64 field = mFieldInt64Cache.get(sFieldInt64Supplier);
field.setValue(id, val);
mExtra.addPerfettoPointer(mCurrentContainer, field);
return this;
}
- @Override
+ /**
+ * Adds a proto field with field id {@code id} and value {@code val}.
+ */
public Builder addField(long id, double val) {
- checkContainer();
+ if (!mIsCategoryEnabled) {
+ return this;
+ }
+ if (DEBUG) {
+ checkContainer();
+ }
FieldDouble field = mFieldDoubleCache.get(sFieldDoubleSupplier);
field.setValue(id, val);
mExtra.addPerfettoPointer(mCurrentContainer, field);
return this;
}
- @Override
+ /**
+ * Adds a proto field with field id {@code id} and value {@code val}.
+ */
public Builder addField(long id, String val) {
- checkContainer();
+ if (!mIsCategoryEnabled) {
+ return this;
+ }
+ if (DEBUG) {
+ checkContainer();
+ }
FieldString field = mFieldStringCache.get(sFieldStringSupplier);
field.setValue(id, val);
mExtra.addPerfettoPointer(mCurrentContainer, field);
return this;
}
- @Override
+ /**
+ * Begins a proto field.
+ * Fields can be added from this point and there must be a corresponding
+ * {@link endProto}.
+ *
+ * The proto field is a singleton and all proto fields get added inside the
+ * one {@link beginProto} and {@link endProto} within the {@link Builder}.
+ */
public Builder beginProto() {
- checkParent();
+ if (!mIsCategoryEnabled) {
+ return this;
+ }
+ if (DEBUG) {
+ checkParent();
+ }
mProto.clearFields();
mExtra.addPerfettoPointer(mProto);
- return mBuilderCache.get(sBuilderSupplier).initInternal(this, mProto);
+ return mBuilderCache.get(sBuilderSupplier).initInternal(this, mProto, true);
}
- @Override
+ /**
+ * Ends a proto field.
+ */
public Builder endProto() {
+ if (!mIsCategoryEnabled) {
+ return this;
+ }
if (mParent == null || mCurrentContainer == null) {
throw new IllegalStateException("No proto to end");
}
return mParent;
}
- @Override
+ /**
+ * Begins a nested proto field with field id {@code id}.
+ * Fields can be added from this point and there must be a corresponding
+ * {@link endNested}.
+ */
public Builder beginNested(long id) {
- checkContainer();
+ if (!mIsCategoryEnabled) {
+ return this;
+ }
+ if (DEBUG) {
+ checkContainer();
+ }
FieldNested field = mFieldNestedCache.get(sFieldNestedSupplier);
field.setId(id);
mExtra.addPerfettoPointer(mCurrentContainer, field);
- return mBuilderCache.get(sBuilderSupplier).initInternal(this, field);
+ return mBuilderCache.get(sBuilderSupplier).initInternal(this, field, true);
}
- @Override
+ /**
+ * Ends a nested proto field.
+ */
public Builder endNested() {
+ if (!mIsCategoryEnabled) {
+ return this;
+ }
if (mParent == null || mCurrentContainer == null) {
throw new IllegalStateException("No nested field to end");
}
return mParent;
}
- private Builder initInternal(Builder parent, FieldContainer container) {
+
+ private Builder initInternal(Builder parent, FieldContainer field,
+ boolean isCategoryEnabled) {
mParent = parent;
- mCurrentContainer = container;
+ mCurrentContainer = field;
+ mIsCategoryEnabled = isCategoryEnabled;
mIsBuilt = false;
return this;
@@ -685,14 +652,8 @@ public final class PerfettoTrackEventExtra {
* Start a {@link Builder} to build a {@link PerfettoTrackEventExtra}.
*/
public static Builder builder() {
- return sTrackEventExtra.get().mBuilderCache.get(sBuilderSupplier).initInternal(null, null);
- }
-
- /**
- * Returns a no-op {@link Builder}. Useful if a category is disabled.
- */
- public static Builder noOpBuilder() {
- return NO_OP_BUILDER;
+ return sTrackEventExtra.get().mBuilderCache.get(sBuilderSupplier).initInternal(null, null,
+ false);
}
private final RingBuffer<NamedTrack> mNamedTrackCache =
@@ -710,7 +671,7 @@ public final class PerfettoTrackEventExtra {
private final Pool<FieldString> mFieldStringCache = new Pool(DEFAULT_EXTRA_CACHE_SIZE);
private final Pool<FieldNested> mFieldNestedCache = new Pool(DEFAULT_EXTRA_CACHE_SIZE);
private final Pool<Flow> mFlowCache = new Pool(DEFAULT_EXTRA_CACHE_SIZE);
- private final Pool<BuilderImpl> mBuilderCache = new Pool(DEFAULT_EXTRA_CACHE_SIZE);
+ private final Pool<Builder> mBuilderCache = new Pool(DEFAULT_EXTRA_CACHE_SIZE);
private static final NativeAllocationRegistry sRegistry =
NativeAllocationRegistry.createMalloced(
@@ -757,6 +718,7 @@ public final class PerfettoTrackEventExtra {
mPendingPointers.clear();
}
+ @android.ravenwood.annotation.RavenwoodReplace
private CounterInt64 getCounterInt64() {
if (mCounterInt64 == null) {
mCounterInt64 = new CounterInt64();
@@ -764,6 +726,7 @@ public final class PerfettoTrackEventExtra {
return mCounterInt64;
}
+ @android.ravenwood.annotation.RavenwoodReplace
private CounterDouble getCounterDouble() {
if (mCounterDouble == null) {
mCounterDouble = new CounterDouble();
@@ -771,6 +734,7 @@ public final class PerfettoTrackEventExtra {
return mCounterDouble;
}
+ @android.ravenwood.annotation.RavenwoodReplace
private Proto getProto() {
if (mProto == null) {
mProto = new Proto();
@@ -778,6 +742,22 @@ public final class PerfettoTrackEventExtra {
return mProto;
}
+ @android.ravenwood.annotation.RavenwoodReplace
+ private Flow getFlow() {
+ if (mFlow == null) {
+ mFlow = new Flow();
+ }
+ return mFlow;
+ }
+
+ @android.ravenwood.annotation.RavenwoodReplace
+ private Flow getTerminatingFlow() {
+ if (mTerminatingFlow == null) {
+ mTerminatingFlow = new Flow();
+ }
+ return mTerminatingFlow;
+ }
+
private static final class Flow implements PerfettoPointer {
private static final NativeAllocationRegistry sRegistry =
NativeAllocationRegistry.createMalloced(
@@ -1337,4 +1317,29 @@ public final class PerfettoTrackEventExtra {
// Tracing currently completely disabled under Ravenwood
return 0;
}
+
+ private CounterInt64 getCounterInt64$ravenwood() {
+ // Tracing currently completely disabled under Ravenwood
+ return null;
+ }
+
+ private CounterDouble getCounterDouble$ravenwood() {
+ // Tracing currently completely disabled under Ravenwood
+ return null;
+ }
+
+ private Proto getProto$ravenwood() {
+ // Tracing currently completely disabled under Ravenwood
+ return null;
+ }
+
+ private Flow getFlow$ravenwood() {
+ // Tracing currently completely disabled under Ravenwood
+ return null;
+ }
+
+ private Flow getTerminatingFlow$ravenwood() {
+ // Tracing currently completely disabled under Ravenwood
+ return null;
+ }
}
diff --git a/core/java/android/os/vibrator/flags.aconfig b/core/java/android/os/vibrator/flags.aconfig
index 7ceb948945fd..0615578935a1 100644
--- a/core/java/android/os/vibrator/flags.aconfig
+++ b/core/java/android/os/vibrator/flags.aconfig
@@ -145,3 +145,13 @@ flag {
purpose: PURPOSE_FEATURE
}
}
+
+flag {
+ namespace: "haptics"
+ name: "fix_vibration_thread_callback_handling"
+ description: "Fix how the VibrationThread handles late callbacks from the vibrator HAL"
+ bug: "395005081"
+ metadata {
+ purpose: PURPOSE_BUGFIX
+ }
+}
diff --git a/core/java/android/permission/flags.aconfig b/core/java/android/permission/flags.aconfig
index d469a2f985fa..ca24c0c6c376 100644
--- a/core/java/android/permission/flags.aconfig
+++ b/core/java/android/permission/flags.aconfig
@@ -514,3 +514,12 @@ flag {
description: "Force AttributionSource.myAttributionSource() to return a default device id"
bug: "343121936"
}
+
+flag {
+ name: "grant_read_blocked_numbers_to_system_ui_intelligence"
+ is_exported: true
+ is_fixed_read_only: true
+ namespace: "permissions"
+ description: "This flag is used to add role protection to READ_BLOCKED_NUMBERS for SYSTEM_UI_INTELLIGENCE"
+ bug: "354758615"
+}
diff --git a/core/java/android/provider/Settings.java b/core/java/android/provider/Settings.java
index 4ebfe53cab58..538283e10738 100644
--- a/core/java/android/provider/Settings.java
+++ b/core/java/android/provider/Settings.java
@@ -13017,18 +13017,16 @@ public final class Settings {
* false/0.
* @hide
*/
- @Readable
public static final String REDACT_OTP_NOTIFICATION_WHILE_CONNECTED_TO_WIFI =
"redact_otp_on_wifi";
/**
- * Toggle for whether to immediately redact OTP notifications, or require the device to be
- * locked for 10 minutes. Defaults to false/0
+ * Time (in milliseconds) that the device should need to be locked, in order for an OTP
+ * notification to be redacted. Default is 10 minutes (600,000 ms)
* @hide
*/
- @Readable
- public static final String REDACT_OTP_NOTIFICATION_IMMEDIATELY =
- "remove_otp_redaction_delay";
+ public static final String OTP_NOTIFICATION_REDACTION_LOCK_TIME =
+ "otp_redaction_lock_time";
/**
* These entries are considered common between the personal and the managed profile,
diff --git a/core/java/android/text/AlteredCharSequence.java b/core/java/android/text/AlteredCharSequence.java
index 971a47dba6e8..a05c690a9e30 100644
--- a/core/java/android/text/AlteredCharSequence.java
+++ b/core/java/android/text/AlteredCharSequence.java
@@ -24,6 +24,7 @@ package android.text;
* @deprecated The functionality this class offers is easily implemented outside the framework.
*/
@Deprecated
+@android.ravenwood.annotation.RavenwoodKeepWholeClass
public class AlteredCharSequence
implements CharSequence, GetChars
{
diff --git a/core/java/android/text/AndroidBidi.java b/core/java/android/text/AndroidBidi.java
index 31da79995172..fcdd50abc02a 100644
--- a/core/java/android/text/AndroidBidi.java
+++ b/core/java/android/text/AndroidBidi.java
@@ -28,6 +28,7 @@ import com.android.internal.annotations.VisibleForTesting;
* @hide
*/
@VisibleForTesting(visibility = VisibleForTesting.Visibility.PACKAGE)
+@android.ravenwood.annotation.RavenwoodKeepWholeClass
public class AndroidBidi {
/**
diff --git a/core/java/android/text/AndroidCharacter.java b/core/java/android/text/AndroidCharacter.java
index c5f1a01f5927..37c4fdbef915 100644
--- a/core/java/android/text/AndroidCharacter.java
+++ b/core/java/android/text/AndroidCharacter.java
@@ -22,6 +22,7 @@ package android.text;
* @deprecated Use various methods from {@link android.icu.lang.UCharacter}, instead.
*/
@Deprecated
+@android.ravenwood.annotation.RavenwoodKeepWholeClass
public class AndroidCharacter
{
public static final int EAST_ASIAN_WIDTH_NEUTRAL = 0;
diff --git a/core/java/android/text/Annotation.java b/core/java/android/text/Annotation.java
index bb5d3ea7da4b..ac3e5a964a33 100644
--- a/core/java/android/text/Annotation.java
+++ b/core/java/android/text/Annotation.java
@@ -23,6 +23,7 @@ import android.os.Parcel;
* TextView save/restore cycles and can be used to keep application-specific
* data that needs to be maintained for regions of text.
*/
+@android.ravenwood.annotation.RavenwoodKeepWholeClass
public class Annotation implements ParcelableSpan {
private final String mKey;
private final String mValue;
diff --git a/core/java/android/text/AutoGrowArray.java b/core/java/android/text/AutoGrowArray.java
index e428377a0a31..06c74c3370c1 100644
--- a/core/java/android/text/AutoGrowArray.java
+++ b/core/java/android/text/AutoGrowArray.java
@@ -30,6 +30,7 @@ import libcore.util.EmptyArray;
*
* @hide
*/
+@android.ravenwood.annotation.RavenwoodKeepWholeClass
public final class AutoGrowArray {
private static final int MIN_CAPACITY_INCREMENT = 12;
private static final int MAX_CAPACITY_TO_BE_KEPT = 10000;
diff --git a/core/java/android/text/AutoText.java b/core/java/android/text/AutoText.java
index c5339a42cbd1..d7b0547e6c4c 100644
--- a/core/java/android/text/AutoText.java
+++ b/core/java/android/text/AutoText.java
@@ -31,6 +31,7 @@ import java.util.Locale;
/**
* This class accesses a dictionary of corrections to frequent misspellings.
*/
+@android.ravenwood.annotation.RavenwoodKeepWholeClass
public class AutoText {
// struct trie {
// char c;
diff --git a/core/java/android/text/BidiFormatter.java b/core/java/android/text/BidiFormatter.java
index dfa172df72ea..6d4103cee7a6 100644
--- a/core/java/android/text/BidiFormatter.java
+++ b/core/java/android/text/BidiFormatter.java
@@ -82,6 +82,7 @@ import java.util.Locale;
* first-strong estimation algorithm. It can also be configured to use a custom directionality
* estimation object.
*/
+@android.ravenwood.annotation.RavenwoodKeepWholeClass
public final class BidiFormatter {
/**
diff --git a/core/java/android/text/BoringLayout.java b/core/java/android/text/BoringLayout.java
index 4fdcecc4f782..2b410e6284bf 100644
--- a/core/java/android/text/BoringLayout.java
+++ b/core/java/android/text/BoringLayout.java
@@ -45,6 +45,7 @@ import com.android.text.flags.Flags;
* {@link android.graphics.Canvas#drawText(java.lang.CharSequence, int, int, float, float, android.graphics.Paint)
* Canvas.drawText()} directly.</p>
*/
+@android.ravenwood.annotation.RavenwoodKeepWholeClass
public class BoringLayout extends Layout implements TextUtils.EllipsizeCallback {
/**
diff --git a/core/java/android/text/CharSequenceCharacterIterator.java b/core/java/android/text/CharSequenceCharacterIterator.java
index 9b07d29bd9dd..1599be8237d4 100644
--- a/core/java/android/text/CharSequenceCharacterIterator.java
+++ b/core/java/android/text/CharSequenceCharacterIterator.java
@@ -24,6 +24,7 @@ import java.text.CharacterIterator;
* An implementation of {@link java.text.CharacterIterator} that iterates over a given CharSequence.
* {@hide}
*/
+@android.ravenwood.annotation.RavenwoodKeepWholeClass
public class CharSequenceCharacterIterator implements CharacterIterator {
private final int mBeginIndex, mEndIndex;
private int mIndex;
diff --git a/core/java/android/text/ClipboardManager.java b/core/java/android/text/ClipboardManager.java
index d0309100b0f2..41990f0fc8dd 100644
--- a/core/java/android/text/ClipboardManager.java
+++ b/core/java/android/text/ClipboardManager.java
@@ -21,6 +21,7 @@ package android.text;
* {@link android.content.ClipboardManager} for the modern API.
*/
@Deprecated
+@android.ravenwood.annotation.RavenwoodKeepWholeClass
public abstract class ClipboardManager {
/**
* Returns the text on the clipboard. It will eventually be possible
diff --git a/core/java/android/text/DynamicLayout.java b/core/java/android/text/DynamicLayout.java
index 6b1aef710e50..3b66ce0167c4 100644
--- a/core/java/android/text/DynamicLayout.java
+++ b/core/java/android/text/DynamicLayout.java
@@ -53,6 +53,7 @@ import java.lang.ref.WeakReference;
* {@link android.graphics.Canvas#drawText(java.lang.CharSequence, int, int, float, float, android.graphics.Paint)
* Canvas.drawText()} directly.</p>
*/
+@android.ravenwood.annotation.RavenwoodKeepWholeClass
public class DynamicLayout extends Layout {
private static final int PRIORITY = 128;
private static final int BLOCK_MINIMUM_CHARACTER_LENGTH = 400;
diff --git a/core/java/android/text/Editable.java b/core/java/android/text/Editable.java
index a942f6ce2879..53d819f52a5c 100644
--- a/core/java/android/text/Editable.java
+++ b/core/java/android/text/Editable.java
@@ -22,6 +22,7 @@ package android.text;
* to immutable text like Strings). If you make a {@link DynamicLayout}
* of an Editable, the layout will be reflowed as the text is changed.
*/
+@android.ravenwood.annotation.RavenwoodKeepWholeClass
public interface Editable
extends CharSequence, GetChars, Spannable, Appendable
{
diff --git a/core/java/android/text/Emoji.java b/core/java/android/text/Emoji.java
index cf0e3c26daac..28c37c00d66e 100644
--- a/core/java/android/text/Emoji.java
+++ b/core/java/android/text/Emoji.java
@@ -23,6 +23,7 @@ import android.icu.lang.UProperty;
* An utility class for Emoji.
* @hide
*/
+@android.ravenwood.annotation.RavenwoodKeepWholeClass
public class Emoji {
public static int COMBINING_ENCLOSING_KEYCAP = 0x20E3;
diff --git a/core/java/android/text/EmojiConsistency.java b/core/java/android/text/EmojiConsistency.java
index dfaa217c0cca..9823305ec72a 100644
--- a/core/java/android/text/EmojiConsistency.java
+++ b/core/java/android/text/EmojiConsistency.java
@@ -48,6 +48,7 @@ import java.util.Set;
* </ol>
* </p>
*/
+@android.ravenwood.annotation.RavenwoodKeepWholeClass
public final class EmojiConsistency {
/* Cannot construct */
private EmojiConsistency() { }
diff --git a/core/java/android/text/FontConfig.java b/core/java/android/text/FontConfig.java
index 783f3b7aa64b..5a4d3a867032 100644
--- a/core/java/android/text/FontConfig.java
+++ b/core/java/android/text/FontConfig.java
@@ -55,6 +55,7 @@ import java.util.Objects;
*/
@SystemApi
@TestApi
+@android.ravenwood.annotation.RavenwoodKeepWholeClass
public final class FontConfig implements Parcelable {
private final @NonNull List<FontFamily> mFamilies;
private final @NonNull List<Alias> mAliases;
diff --git a/core/java/android/text/GetChars.java b/core/java/android/text/GetChars.java
index 348a911a442f..229f5437e76b 100644
--- a/core/java/android/text/GetChars.java
+++ b/core/java/android/text/GetChars.java
@@ -21,6 +21,7 @@ package android.text;
* getChars() method like the one in String that is faster than
* calling charAt() multiple times.
*/
+@android.ravenwood.annotation.RavenwoodKeepWholeClass
public interface GetChars
extends CharSequence
{
diff --git a/core/java/android/text/GraphemeClusterSegmentFinder.java b/core/java/android/text/GraphemeClusterSegmentFinder.java
index 0f6fdaf23c65..996223dd1a84 100644
--- a/core/java/android/text/GraphemeClusterSegmentFinder.java
+++ b/core/java/android/text/GraphemeClusterSegmentFinder.java
@@ -31,6 +31,7 @@ import android.graphics.text.GraphemeBreak;
* @see <a href="https://unicode.org/reports/tr29/#Grapheme_Cluster_Boundaries">Unicode Text
* Segmentation - Grapheme Cluster Boundaries</a>
*/
+@android.ravenwood.annotation.RavenwoodKeepWholeClass
public class GraphemeClusterSegmentFinder extends SegmentFinder {
private static AutoGrowArray.FloatArray sTempAdvances = null;
private final boolean[] mIsGraphemeBreak;
diff --git a/core/java/android/text/GraphicsOperations.java b/core/java/android/text/GraphicsOperations.java
index 6c1544644eab..f7fe805e53a4 100644
--- a/core/java/android/text/GraphicsOperations.java
+++ b/core/java/android/text/GraphicsOperations.java
@@ -26,6 +26,7 @@ import android.graphics.Paint;
*
* @hide
*/
+@android.ravenwood.annotation.RavenwoodKeepWholeClass
public interface GraphicsOperations extends CharSequence {
/**
* Just like {@link Canvas#drawText}.
diff --git a/core/java/android/text/Highlights.java b/core/java/android/text/Highlights.java
index 693dbcf84e84..217a38b6edd5 100644
--- a/core/java/android/text/Highlights.java
+++ b/core/java/android/text/Highlights.java
@@ -30,6 +30,7 @@ import java.util.Objects;
/**
* A class that represents of the highlight of the text.
*/
+@android.ravenwood.annotation.RavenwoodKeepWholeClass
public class Highlights {
private final List<Pair<Paint, int[]>> mHighlights;
diff --git a/core/java/android/text/Html.java b/core/java/android/text/Html.java
index a42eece57eec..d412071bf3b9 100644
--- a/core/java/android/text/Html.java
+++ b/core/java/android/text/Html.java
@@ -17,13 +17,13 @@
package android.text;
import android.app.ActivityThread;
-import android.app.Application;
import android.compat.annotation.UnsupportedAppUsage;
import android.content.res.Resources;
import android.graphics.Color;
import android.graphics.Typeface;
import android.graphics.drawable.Drawable;
import android.os.Build;
+import android.ravenwood.annotation.RavenwoodReplace;
import android.text.style.AbsoluteSizeSpan;
import android.text.style.AlignmentSpan;
import android.text.style.BackgroundColorSpan;
@@ -65,6 +65,7 @@ import java.util.regex.Pattern;
* This class processes HTML strings into displayable styled text.
* Not all HTML tags are supported.
*/
+@android.ravenwood.annotation.RavenwoodKeepWholeClass
public class Html {
/**
* Retrieves images for HTML &lt;img&gt; tags.
@@ -506,6 +507,15 @@ public class Html {
out.append("</p>\n");
}
+ @RavenwoodReplace(blockedBy = ActivityThread.class)
+ private static float getDisplayMetricsDensity() {
+ return ActivityThread.currentApplication().getResources().getDisplayMetrics().density;
+ }
+
+ private static float getDisplayMetricsDensity$ravenwood() {
+ return Resources.getSystem().getDisplayMetrics().density;
+ }
+
private static void withinParagraph(StringBuilder out, Spanned text, int start, int end) {
int next;
for (int i = start; i < end; i = next) {
@@ -559,8 +569,7 @@ public class Html {
AbsoluteSizeSpan s = ((AbsoluteSizeSpan) style[j]);
float sizeDip = s.getSize();
if (!s.getDip()) {
- Application application = ActivityThread.currentApplication();
- sizeDip /= application.getResources().getDisplayMetrics().density;
+ sizeDip /= getDisplayMetricsDensity();
}
// px in CSS is the equivalance of dip in Android
@@ -669,6 +678,7 @@ public class Html {
}
}
+@android.ravenwood.annotation.RavenwoodKeepWholeClass
class HtmlToSpannedConverter implements ContentHandler {
private static final float[] HEADING_SIZES = {
@@ -843,6 +853,16 @@ class HtmlToSpannedConverter implements ContentHandler {
}
}
+ @RavenwoodReplace(blockedBy = ActivityThread.class)
+ private static int getFontWeightAdjustment() {
+ return ActivityThread.currentApplication().getResources()
+ .getConfiguration().fontWeightAdjustment;
+ }
+
+ private static int getFontWeightAdjustment$ravenwood() {
+ return Resources.getSystem().getConfiguration().fontWeightAdjustment;
+ }
+
private void handleEndTag(String tag) {
if (tag.equalsIgnoreCase("br")) {
handleBr(mSpannableStringBuilder);
@@ -858,17 +878,11 @@ class HtmlToSpannedConverter implements ContentHandler {
} else if (tag.equalsIgnoreCase("span")) {
endCssStyle(mSpannableStringBuilder);
} else if (tag.equalsIgnoreCase("strong")) {
- Application application = ActivityThread.currentApplication();
- int fontWeightAdjustment =
- application.getResources().getConfiguration().fontWeightAdjustment;
end(mSpannableStringBuilder, Bold.class, new StyleSpan(Typeface.BOLD,
- fontWeightAdjustment));
+ getFontWeightAdjustment()));
} else if (tag.equalsIgnoreCase("b")) {
- Application application = ActivityThread.currentApplication();
- int fontWeightAdjustment =
- application.getResources().getConfiguration().fontWeightAdjustment;
end(mSpannableStringBuilder, Bold.class, new StyleSpan(Typeface.BOLD,
- fontWeightAdjustment));
+ getFontWeightAdjustment()));
} else if (tag.equalsIgnoreCase("em")) {
end(mSpannableStringBuilder, Italic.class, new StyleSpan(Typeface.ITALIC));
} else if (tag.equalsIgnoreCase("cite")) {
@@ -1036,11 +1050,8 @@ class HtmlToSpannedConverter implements ContentHandler {
// Their ranges should not include the newlines at the end
Heading h = getLast(text, Heading.class);
if (h != null) {
- Application application = ActivityThread.currentApplication();
- int fontWeightAdjustment =
- application.getResources().getConfiguration().fontWeightAdjustment;
setSpanFromMark(text, h, new RelativeSizeSpan(HEADING_SIZES[h.mLevel]),
- new StyleSpan(Typeface.BOLD, fontWeightAdjustment));
+ new StyleSpan(Typeface.BOLD, getFontWeightAdjustment()));
}
endBlockElement(text);
diff --git a/core/java/android/text/Hyphenator.java b/core/java/android/text/Hyphenator.java
index 6f0628ad38e6..7f9a8a1c0806 100644
--- a/core/java/android/text/Hyphenator.java
+++ b/core/java/android/text/Hyphenator.java
@@ -21,6 +21,7 @@ package android.text;
*
* @hide
*/
+@android.ravenwood.annotation.RavenwoodKeepWholeClass
public class Hyphenator {
private Hyphenator() {}
diff --git a/core/java/android/text/InputFilter.java b/core/java/android/text/InputFilter.java
index 96e7bd0fef4c..ed5de03e5528 100644
--- a/core/java/android/text/InputFilter.java
+++ b/core/java/android/text/InputFilter.java
@@ -27,6 +27,7 @@ import java.util.Locale;
* InputFilters can be attached to {@link Editable}s to constrain the
* changes that can be made to them.
*/
+@android.ravenwood.annotation.RavenwoodKeepWholeClass
public interface InputFilter
{
/**
diff --git a/core/java/android/text/InputType.java b/core/java/android/text/InputType.java
index 4ebecb7494fa..03c9c023d9ce 100644
--- a/core/java/android/text/InputType.java
+++ b/core/java/android/text/InputType.java
@@ -44,6 +44,7 @@ import java.util.List;
* TYPE_DATETIME_VARIATION_TIME
* </dl>
*/
+@android.ravenwood.annotation.RavenwoodKeepWholeClass
public interface InputType {
/**
* Mask of bits that determine the overall class
diff --git a/core/java/android/text/Layout.java b/core/java/android/text/Layout.java
index b273a7f7c271..44c3f9a8244e 100644
--- a/core/java/android/text/Layout.java
+++ b/core/java/android/text/Layout.java
@@ -69,6 +69,7 @@ import java.util.Locale;
* which will be updated as the text changes.
* For text that will not change, use a {@link StaticLayout}.
*/
+@android.ravenwood.annotation.RavenwoodKeepWholeClass
public abstract class Layout {
// These should match the constants in framework/base/libs/hwui/hwui/DrawTextFunctor.h
diff --git a/core/java/android/text/LoginFilter.java b/core/java/android/text/LoginFilter.java
index 0e4eec4488ee..94f196f7ef6b 100644
--- a/core/java/android/text/LoginFilter.java
+++ b/core/java/android/text/LoginFilter.java
@@ -23,6 +23,7 @@ package android.text;
* handle non-BMP characters.
*/
@Deprecated
+@android.ravenwood.annotation.RavenwoodKeepWholeClass
public abstract class LoginFilter implements InputFilter {
private boolean mAppendInvalid; // whether to append or ignore invalid characters
/**
diff --git a/core/java/android/text/MeasuredParagraph.java b/core/java/android/text/MeasuredParagraph.java
index 31a226341907..b2e44598a548 100644
--- a/core/java/android/text/MeasuredParagraph.java
+++ b/core/java/android/text/MeasuredParagraph.java
@@ -68,6 +68,7 @@ import java.util.Arrays;
* @hide
*/
@TestApi
+@android.ravenwood.annotation.RavenwoodKeepWholeClass
public class MeasuredParagraph {
private static final char OBJECT_REPLACEMENT_CHARACTER = '\uFFFC';
diff --git a/core/java/android/text/NoCopySpan.java b/core/java/android/text/NoCopySpan.java
index e754d765e14c..4cd3d04dc4e6 100644
--- a/core/java/android/text/NoCopySpan.java
+++ b/core/java/android/text/NoCopySpan.java
@@ -21,6 +21,7 @@ package android.text;
* into a new Spanned when performing a slice or copy operation on the original
* Spanned it was placed in.
*/
+@android.ravenwood.annotation.RavenwoodKeepWholeClass
public interface NoCopySpan {
/**
* Convenience equivalent for when you would just want a new Object() for
diff --git a/core/java/android/text/PackedIntVector.java b/core/java/android/text/PackedIntVector.java
index 3e5bf5677853..11dd0c38182b 100644
--- a/core/java/android/text/PackedIntVector.java
+++ b/core/java/android/text/PackedIntVector.java
@@ -29,6 +29,7 @@ import com.android.internal.util.GrowingArrayUtils;
* @hide
*/
@VisibleForTesting(visibility = VisibleForTesting.Visibility.PACKAGE)
+@android.ravenwood.annotation.RavenwoodKeepWholeClass
public class PackedIntVector {
private final int mColumns;
private int mRows;
diff --git a/core/java/android/text/PackedObjectVector.java b/core/java/android/text/PackedObjectVector.java
index b777e16a153d..beb5ea4ee28c 100644
--- a/core/java/android/text/PackedObjectVector.java
+++ b/core/java/android/text/PackedObjectVector.java
@@ -21,6 +21,7 @@ import com.android.internal.util.GrowingArrayUtils;
import libcore.util.EmptyArray;
+@android.ravenwood.annotation.RavenwoodKeepWholeClass
class PackedObjectVector<E>
{
private int mColumns;
diff --git a/core/java/android/text/ParcelableSpan.java b/core/java/android/text/ParcelableSpan.java
index d7c1a4bc26e8..a9a4893d3692 100644
--- a/core/java/android/text/ParcelableSpan.java
+++ b/core/java/android/text/ParcelableSpan.java
@@ -24,6 +24,7 @@ import android.os.Parcelable;
* This can only be used by code in the framework; it is not intended for
* applications to implement their own Parcelable spans.
*/
+@android.ravenwood.annotation.RavenwoodKeepWholeClass
public interface ParcelableSpan extends Parcelable {
/**
* Return a special type identifier for this span class.
diff --git a/core/java/android/text/PrecomputedText.java b/core/java/android/text/PrecomputedText.java
index 5f6a9bd068c9..71cacd9b199a 100644
--- a/core/java/android/text/PrecomputedText.java
+++ b/core/java/android/text/PrecomputedText.java
@@ -75,6 +75,7 @@ import java.util.Objects;
* Note that any {@link android.text.NoCopySpan} attached to the original text won't be passed to
* PrecomputedText.
*/
+@android.ravenwood.annotation.RavenwoodKeepWholeClass
public class PrecomputedText implements Spannable {
private static final char LINE_FEED = '\n';
diff --git a/core/java/android/text/SegmentFinder.java b/core/java/android/text/SegmentFinder.java
index 047d07a2e3e0..b7ab0e62753a 100644
--- a/core/java/android/text/SegmentFinder.java
+++ b/core/java/android/text/SegmentFinder.java
@@ -39,6 +39,7 @@ import java.util.Objects;
*
* @see Layout#getRangeForRect(RectF, SegmentFinder, Layout.TextInclusionStrategy)
*/
+@android.ravenwood.annotation.RavenwoodKeepWholeClass
public abstract class SegmentFinder {
/**
* Return value of previousStartBoundary(int), previousEndBoundary(int), nextStartBoundary(int),
diff --git a/core/java/android/text/Selection.java b/core/java/android/text/Selection.java
index 711578c1482f..674b47372482 100644
--- a/core/java/android/text/Selection.java
+++ b/core/java/android/text/Selection.java
@@ -27,6 +27,7 @@ import java.text.BreakIterator;
* Utility class for manipulating cursors and selections in CharSequences.
* A cursor is a selection where the start and end are at the same offset.
*/
+@android.ravenwood.annotation.RavenwoodKeepWholeClass
public class Selection {
private Selection() { /* cannot be instantiated */ }
diff --git a/core/java/android/text/SpanColors.java b/core/java/android/text/SpanColors.java
index fcd242b62700..3b6a0418dcb0 100644
--- a/core/java/android/text/SpanColors.java
+++ b/core/java/android/text/SpanColors.java
@@ -27,6 +27,7 @@ import android.text.style.CharacterStyle;
*
* @hide
*/
+@android.ravenwood.annotation.RavenwoodKeepWholeClass
public class SpanColors {
public static final @ColorInt int NO_COLOR_FOUND = Color.TRANSPARENT;
diff --git a/core/java/android/text/SpanSet.java b/core/java/android/text/SpanSet.java
index d464278c714c..4ad8106eb459 100644
--- a/core/java/android/text/SpanSet.java
+++ b/core/java/android/text/SpanSet.java
@@ -31,6 +31,7 @@ import java.util.Arrays;
* Note that empty spans are ignored by this class.
* @hide
*/
+@android.ravenwood.annotation.RavenwoodKeepWholeClass
public class SpanSet<E> {
private final Class<? extends E> classType;
diff --git a/core/java/android/text/SpanWatcher.java b/core/java/android/text/SpanWatcher.java
index 01e82c815ac8..31d63206a144 100644
--- a/core/java/android/text/SpanWatcher.java
+++ b/core/java/android/text/SpanWatcher.java
@@ -21,6 +21,7 @@ package android.text;
* will be called to notify it that other markup objects have been
* added, changed, or removed.
*/
+@android.ravenwood.annotation.RavenwoodKeepWholeClass
public interface SpanWatcher extends NoCopySpan {
/**
* This method is called to notify you that the specified object
diff --git a/core/java/android/text/Spannable.java b/core/java/android/text/Spannable.java
index 8315b2aa52c6..fac5131c035a 100644
--- a/core/java/android/text/Spannable.java
+++ b/core/java/android/text/Spannable.java
@@ -21,6 +21,7 @@ package android.text;
* attached and detached. Not all Spannable classes have mutable text;
* see {@link Editable} for that.
*/
+@android.ravenwood.annotation.RavenwoodKeepWholeClass
public interface Spannable
extends Spanned
{
diff --git a/core/java/android/text/SpannableString.java b/core/java/android/text/SpannableString.java
index afb5df809bc0..ee04a86a808f 100644
--- a/core/java/android/text/SpannableString.java
+++ b/core/java/android/text/SpannableString.java
@@ -21,6 +21,7 @@ package android.text;
* markup objects can be attached and detached.
* For mutable text, see {@link SpannableStringBuilder}.
*/
+@android.ravenwood.annotation.RavenwoodKeepWholeClass
public class SpannableString
extends SpannableStringInternal
implements CharSequence, GetChars, Spannable
diff --git a/core/java/android/text/SpannableStringBuilder.java b/core/java/android/text/SpannableStringBuilder.java
index 0e61eff86c2b..f8d7283a94b3 100644
--- a/core/java/android/text/SpannableStringBuilder.java
+++ b/core/java/android/text/SpannableStringBuilder.java
@@ -35,6 +35,7 @@ import java.util.IdentityHashMap;
/**
* This is the class for text whose content and markup can both be changed.
*/
+@android.ravenwood.annotation.RavenwoodKeepWholeClass
public class SpannableStringBuilder implements CharSequence, GetChars, Spannable, Editable,
Appendable, GraphicsOperations {
private final static String TAG = "SpannableStringBuilder";
diff --git a/core/java/android/text/SpannableStringInternal.java b/core/java/android/text/SpannableStringInternal.java
index f2ab1bb31fbc..90d83d59eb65 100644
--- a/core/java/android/text/SpannableStringInternal.java
+++ b/core/java/android/text/SpannableStringInternal.java
@@ -27,6 +27,7 @@ import libcore.util.EmptyArray;
import java.lang.reflect.Array;
+@android.ravenwood.annotation.RavenwoodKeepWholeClass
/* package */ abstract class SpannableStringInternal
{
/* package */ SpannableStringInternal(CharSequence source,
diff --git a/core/java/android/text/Spanned.java b/core/java/android/text/Spanned.java
index a0d54c26c6d9..6706ffd245c1 100644
--- a/core/java/android/text/Spanned.java
+++ b/core/java/android/text/Spanned.java
@@ -22,6 +22,7 @@ package android.text;
* see {@link Spannable} for mutable markup and {@link Editable} for
* mutable text.
*/
+@android.ravenwood.annotation.RavenwoodKeepWholeClass
public interface Spanned
extends CharSequence
{
diff --git a/core/java/android/text/SpannedString.java b/core/java/android/text/SpannedString.java
index acee3c5f1a41..a3f1ee2e3f20 100644
--- a/core/java/android/text/SpannedString.java
+++ b/core/java/android/text/SpannedString.java
@@ -22,6 +22,7 @@ package android.text;
* For mutable markup, see {@link SpannableString}; for mutable text,
* see {@link SpannableStringBuilder}.
*/
+@android.ravenwood.annotation.RavenwoodKeepWholeClass
public final class SpannedString
extends SpannableStringInternal
implements CharSequence, GetChars, Spanned
diff --git a/core/java/android/text/StaticLayout.java b/core/java/android/text/StaticLayout.java
index a5d52957c40e..8193cd2beb80 100644
--- a/core/java/android/text/StaticLayout.java
+++ b/core/java/android/text/StaticLayout.java
@@ -55,6 +55,7 @@ import java.util.Arrays;
* float, float, android.graphics.Paint)
* Canvas.drawText()} directly.</p>
*/
+@android.ravenwood.annotation.RavenwoodKeepWholeClass
public class StaticLayout extends Layout {
/*
* The break iteration is done in native code. The protocol for using the native code is as
diff --git a/core/java/android/text/TextDirectionHeuristic.java b/core/java/android/text/TextDirectionHeuristic.java
index 8a4ba42bcc91..66cea853cda9 100644
--- a/core/java/android/text/TextDirectionHeuristic.java
+++ b/core/java/android/text/TextDirectionHeuristic.java
@@ -19,6 +19,7 @@ package android.text;
/**
* Interface for objects that use a heuristic for guessing at the paragraph direction by examining text.
*/
+@android.ravenwood.annotation.RavenwoodKeepWholeClass
public interface TextDirectionHeuristic {
/**
* Guess if a chars array is in the RTL direction or not.
diff --git a/core/java/android/text/TextDirectionHeuristics.java b/core/java/android/text/TextDirectionHeuristics.java
index 85260f4af2c8..3af8fb7f489d 100644
--- a/core/java/android/text/TextDirectionHeuristics.java
+++ b/core/java/android/text/TextDirectionHeuristics.java
@@ -32,6 +32,7 @@ import java.nio.CharBuffer;
* class.
*
*/
+@android.ravenwood.annotation.RavenwoodKeepWholeClass
public class TextDirectionHeuristics {
/**
diff --git a/core/java/android/text/TextLine.java b/core/java/android/text/TextLine.java
index 3015791ee0a9..091eb6027002 100644
--- a/core/java/android/text/TextLine.java
+++ b/core/java/android/text/TextLine.java
@@ -53,6 +53,7 @@ import java.util.ArrayList;
* @hide
*/
@VisibleForTesting(visibility = VisibleForTesting.Visibility.PACKAGE)
+@android.ravenwood.annotation.RavenwoodKeepWholeClass
public class TextLine {
private static final boolean DEBUG = false;
diff --git a/core/java/android/text/TextPaint.java b/core/java/android/text/TextPaint.java
index 73825b13cb6b..ff063f2ac2f4 100644
--- a/core/java/android/text/TextPaint.java
+++ b/core/java/android/text/TextPaint.java
@@ -25,6 +25,7 @@ import android.graphics.Paint;
* TextPaint is an extension of Paint that leaves room for some extra
* data used during text measuring and drawing.
*/
+@android.ravenwood.annotation.RavenwoodKeepWholeClass
public class TextPaint extends Paint {
// Special value 0 means no background paint
diff --git a/core/java/android/text/TextShaper.java b/core/java/android/text/TextShaper.java
index 6da0b63dbc1f..6d1740184763 100644
--- a/core/java/android/text/TextShaper.java
+++ b/core/java/android/text/TextShaper.java
@@ -169,6 +169,7 @@ import android.graphics.text.TextRunShaper;
* @see TextShaper#shapeText(CharSequence, int, int, TextDirectionHeuristic, TextPaint,
* GlyphsConsumer)
*/
+@android.ravenwood.annotation.RavenwoodKeepWholeClass
public class TextShaper {
private TextShaper() {}
diff --git a/core/java/android/text/TextUtils.java b/core/java/android/text/TextUtils.java
index 6dc82c40ddc5..042966b81b9a 100644
--- a/core/java/android/text/TextUtils.java
+++ b/core/java/android/text/TextUtils.java
@@ -34,6 +34,7 @@ import android.icu.text.Edits;
import android.icu.util.ULocale;
import android.os.Parcel;
import android.os.Parcelable;
+import android.ravenwood.annotation.RavenwoodKeepWholeClass;
import android.sysprop.DisplayProperties;
import android.text.style.AbsoluteSizeSpan;
import android.text.style.AccessibilityClickableSpan;
@@ -85,8 +86,7 @@ import java.util.List;
import java.util.Locale;
import java.util.regex.Pattern;
-@android.ravenwood.annotation.RavenwoodKeepStaticInitializer
-@android.ravenwood.annotation.RavenwoodKeepPartialClass
+@RavenwoodKeepWholeClass
public class TextUtils {
private static final String TAG = "TextUtils";
@@ -147,7 +147,6 @@ public class TextUtils {
private TextUtils() { /* cannot be instantiated */ }
- @android.ravenwood.annotation.RavenwoodKeep
public static void getChars(CharSequence s, int start, int end,
char[] dest, int destoff) {
Class<? extends CharSequence> c = s.getClass();
@@ -166,12 +165,10 @@ public class TextUtils {
}
}
- @android.ravenwood.annotation.RavenwoodKeep
public static int indexOf(CharSequence s, char ch) {
return indexOf(s, ch, 0);
}
- @android.ravenwood.annotation.RavenwoodKeep
public static int indexOf(CharSequence s, char ch, int start) {
Class<? extends CharSequence> c = s.getClass();
@@ -181,7 +178,6 @@ public class TextUtils {
return indexOf(s, ch, start, s.length());
}
- @android.ravenwood.annotation.RavenwoodKeep
public static int indexOf(CharSequence s, char ch, int start, int end) {
Class<? extends CharSequence> c = s.getClass();
@@ -219,12 +215,10 @@ public class TextUtils {
return -1;
}
- @android.ravenwood.annotation.RavenwoodKeep
public static int lastIndexOf(CharSequence s, char ch) {
return lastIndexOf(s, ch, s.length() - 1);
}
- @android.ravenwood.annotation.RavenwoodKeep
public static int lastIndexOf(CharSequence s, char ch, int last) {
Class<? extends CharSequence> c = s.getClass();
@@ -234,7 +228,6 @@ public class TextUtils {
return lastIndexOf(s, ch, 0, last);
}
- @android.ravenwood.annotation.RavenwoodKeep
public static int lastIndexOf(CharSequence s, char ch,
int start, int last) {
if (last < 0)
@@ -280,17 +273,14 @@ public class TextUtils {
return -1;
}
- @android.ravenwood.annotation.RavenwoodKeep
public static int indexOf(CharSequence s, CharSequence needle) {
return indexOf(s, needle, 0, s.length());
}
- @android.ravenwood.annotation.RavenwoodKeep
public static int indexOf(CharSequence s, CharSequence needle, int start) {
return indexOf(s, needle, start, s.length());
}
- @android.ravenwood.annotation.RavenwoodKeep
public static int indexOf(CharSequence s, CharSequence needle,
int start, int end) {
int nlen = needle.length();
@@ -318,7 +308,6 @@ public class TextUtils {
return -1;
}
- @android.ravenwood.annotation.RavenwoodKeep
public static boolean regionMatches(CharSequence one, int toffset,
CharSequence two, int ooffset,
int len) {
@@ -351,7 +340,6 @@ public class TextUtils {
* in that it does not preserve any style runs in the source sequence,
* allowing a more efficient implementation.
*/
- @android.ravenwood.annotation.RavenwoodKeep
public static String substring(CharSequence source, int start, int end) {
if (source instanceof String)
return ((String) source).substring(start, end);
@@ -424,7 +412,6 @@ public class TextUtils {
* calling object.toString(). If tokens is null, a NullPointerException will be thrown. If
* tokens is an empty array, an empty string will be returned.
*/
- @android.ravenwood.annotation.RavenwoodKeep
public static String join(@NonNull CharSequence delimiter, @NonNull Object[] tokens) {
final int length = tokens.length;
if (length == 0) {
@@ -448,7 +435,6 @@ public class TextUtils {
* calling object.toString(). If tokens is null, a NullPointerException will be thrown. If
* tokens is empty, an empty string will be returned.
*/
- @android.ravenwood.annotation.RavenwoodKeep
public static String join(@NonNull CharSequence delimiter, @NonNull Iterable tokens) {
final Iterator<?> it = tokens.iterator();
if (!it.hasNext()) {
@@ -481,7 +467,6 @@ public class TextUtils {
*
* @throws NullPointerException if expression or text is null
*/
- @android.ravenwood.annotation.RavenwoodKeep
public static String[] split(String text, String expression) {
if (text.length() == 0) {
return EmptyArray.STRING;
@@ -507,7 +492,6 @@ public class TextUtils {
*
* @throws NullPointerException if expression or text is null
*/
- @android.ravenwood.annotation.RavenwoodKeep
public static String[] split(String text, Pattern pattern) {
if (text.length() == 0) {
return EmptyArray.STRING;
@@ -545,7 +529,6 @@ public class TextUtils {
* be returned for the empty string after that delimeter. That is, splitting <tt>"a,b,"</tt> on
* comma will return <tt>"a", "b"</tt>, not <tt>"a", "b", ""</tt>.
*/
- @android.ravenwood.annotation.RavenwoodKeepWholeClass
public static class SimpleStringSplitter implements StringSplitter, Iterator<String> {
private String mString;
private char mDelimiter;
@@ -609,31 +592,26 @@ public class TextUtils {
* @param str the string to be examined
* @return true if str is null or zero length
*/
- @android.ravenwood.annotation.RavenwoodKeep
public static boolean isEmpty(@Nullable CharSequence str) {
return str == null || str.length() == 0;
}
/** {@hide} */
- @android.ravenwood.annotation.RavenwoodKeep
public static String nullIfEmpty(@Nullable String str) {
return isEmpty(str) ? null : str;
}
/** {@hide} */
- @android.ravenwood.annotation.RavenwoodKeep
public static String emptyIfNull(@Nullable String str) {
return str == null ? "" : str;
}
/** {@hide} */
- @android.ravenwood.annotation.RavenwoodKeep
public static String firstNotEmpty(@Nullable String a, @NonNull String b) {
return !isEmpty(a) ? a : Preconditions.checkStringNotEmpty(b);
}
/** {@hide} */
- @android.ravenwood.annotation.RavenwoodKeep
public static int length(@Nullable String s) {
return s != null ? s.length() : 0;
}
@@ -642,7 +620,6 @@ public class TextUtils {
* @return interned string if it's null.
* @hide
*/
- @android.ravenwood.annotation.RavenwoodKeep
public static String safeIntern(String s) {
return (s != null) ? s.intern() : null;
}
@@ -652,7 +629,6 @@ public class TextUtils {
* spaces and ASCII control characters were trimmed from the start and end,
* as by {@link String#trim}.
*/
- @android.ravenwood.annotation.RavenwoodKeep
public static int getTrimmedLength(CharSequence s) {
int len = s.length();
@@ -677,7 +653,6 @@ public class TextUtils {
* @param b second CharSequence to check
* @return true if a and b are equal
*/
- @android.ravenwood.annotation.RavenwoodKeep
public static boolean equals(@Nullable CharSequence a, @Nullable CharSequence b) {
if (a == b) return true;
int length;
@@ -1713,7 +1688,6 @@ public class TextUtils {
return true;
}
- @android.ravenwood.annotation.RavenwoodKeep
/* package */ static char[] obtain(int len) {
char[] buf;
@@ -1728,7 +1702,6 @@ public class TextUtils {
return buf;
}
- @android.ravenwood.annotation.RavenwoodKeep
/* package */ static void recycle(char[] temp) {
if (temp.length > 1000)
return;
@@ -1743,7 +1716,6 @@ public class TextUtils {
* @param s the string to be encoded
* @return the encoded string
*/
- @android.ravenwood.annotation.RavenwoodKeep
public static String htmlEncode(String s) {
StringBuilder sb = new StringBuilder();
char c;
@@ -1830,7 +1802,6 @@ public class TextUtils {
/**
* Returns whether the given CharSequence contains any printable characters.
*/
- @android.ravenwood.annotation.RavenwoodKeep
public static boolean isGraphic(CharSequence str) {
final int len = str.length();
for (int cp, i=0; i<len; i+=Character.charCount(cp)) {
@@ -1857,7 +1828,6 @@ public class TextUtils {
* @deprecated Use {@link #isGraphic(CharSequence)} instead.
*/
@Deprecated
- @android.ravenwood.annotation.RavenwoodKeep
public static boolean isGraphic(char c) {
int gc = Character.getType(c);
return gc != Character.CONTROL
@@ -1872,7 +1842,6 @@ public class TextUtils {
/**
* Returns whether the given CharSequence contains only digits.
*/
- @android.ravenwood.annotation.RavenwoodKeep
public static boolean isDigitsOnly(CharSequence str) {
final int len = str.length();
for (int cp, i = 0; i < len; i += Character.charCount(cp)) {
@@ -1887,7 +1856,6 @@ public class TextUtils {
/**
* @hide
*/
- @android.ravenwood.annotation.RavenwoodKeep
public static boolean isPrintableAscii(final char c) {
final int asciiFirst = 0x20;
final int asciiLast = 0x7E; // included
@@ -1898,7 +1866,6 @@ public class TextUtils {
* @hide
*/
@UnsupportedAppUsage
- @android.ravenwood.annotation.RavenwoodKeep
public static boolean isPrintableAsciiOnly(final CharSequence str) {
final int len = str.length();
for (int i = 0; i < len; i++) {
@@ -1950,7 +1917,6 @@ public class TextUtils {
* {@link #CAP_MODE_CHARACTERS}, {@link #CAP_MODE_WORDS}, and
* {@link #CAP_MODE_SENTENCES}.
*/
- @android.ravenwood.annotation.RavenwoodKeep
public static int getCapsMode(CharSequence cs, int off, int reqModes) {
if (off < 0) {
return 0;
@@ -2162,7 +2128,6 @@ public class TextUtils {
*
* Be careful: this code will need to be updated when vertical scripts will be supported
*/
- @android.ravenwood.annotation.RavenwoodKeep
public static int getLayoutDirectionFromLocale(Locale locale) {
return ((locale != null && !locale.equals(Locale.ROOT)
&& ULocale.forLocale(locale).isRightToLeft())
@@ -2197,7 +2162,6 @@ public class TextUtils {
* match the supported grammar described above.
* @hide
*/
- @android.ravenwood.annotation.RavenwoodKeep
public static @NonNull String formatSimple(@NonNull String format, Object... args) {
final StringBuilder sb = new StringBuilder(format);
int j = 0;
@@ -2387,7 +2351,6 @@ public class TextUtils {
}
/** @hide */
- @android.ravenwood.annotation.RavenwoodKeep
public static boolean isNewline(int codePoint) {
int type = Character.getType(codePoint);
return type == Character.PARAGRAPH_SEPARATOR || type == Character.LINE_SEPARATOR
@@ -2395,19 +2358,16 @@ public class TextUtils {
}
/** @hide */
- @android.ravenwood.annotation.RavenwoodKeep
public static boolean isWhitespace(int codePoint) {
return Character.isWhitespace(codePoint) || codePoint == NBSP_CODE_POINT;
}
/** @hide */
- @android.ravenwood.annotation.RavenwoodKeep
public static boolean isWhitespaceExceptNewline(int codePoint) {
return isWhitespace(codePoint) && !isNewline(codePoint);
}
/** @hide */
- @android.ravenwood.annotation.RavenwoodKeep
public static boolean isPunctuation(int codePoint) {
int type = Character.getType(codePoint);
return type == Character.CONNECTOR_PUNCTUATION
diff --git a/core/java/android/text/TextWatcher.java b/core/java/android/text/TextWatcher.java
index a0aef690a1b8..5963ca7d5083 100644
--- a/core/java/android/text/TextWatcher.java
+++ b/core/java/android/text/TextWatcher.java
@@ -20,6 +20,7 @@ package android.text;
* When an object of this type is attached to an Editable, its methods will
* be called when the text is changed.
*/
+@android.ravenwood.annotation.RavenwoodKeepWholeClass
public interface TextWatcher extends NoCopySpan {
/**
* This method is called to notify you that, within <code>s</code>,
diff --git a/core/java/android/text/WordSegmentFinder.java b/core/java/android/text/WordSegmentFinder.java
index b0a70eae902a..b8702d72f29c 100644
--- a/core/java/android/text/WordSegmentFinder.java
+++ b/core/java/android/text/WordSegmentFinder.java
@@ -33,6 +33,7 @@ import android.text.method.WordIterator;
* @see <a href="https://unicode.org/reports/tr29/#Word_Boundaries">Unicode Text Segmentation - Word
* Boundaries</a>
*/
+@android.ravenwood.annotation.RavenwoodKeepWholeClass
public class WordSegmentFinder extends SegmentFinder {
private final CharSequence mText;
private final WordIterator mWordIterator;
diff --git a/core/java/android/text/format/DateFormat.java b/core/java/android/text/format/DateFormat.java
index e6dad27d595b..ef60d3058c5c 100644
--- a/core/java/android/text/format/DateFormat.java
+++ b/core/java/android/text/format/DateFormat.java
@@ -63,6 +63,7 @@ import java.util.TimeZone;
* Note that the non-{@code format} methods in this class are implemented by
* {@code SimpleDateFormat}.
*/
+@android.ravenwood.annotation.RavenwoodKeepWholeClass
public class DateFormat {
/**
* @deprecated Use a literal {@code '} instead.
diff --git a/core/java/android/text/format/DateIntervalFormat.java b/core/java/android/text/format/DateIntervalFormat.java
index 8dea3228eb0c..5ec9561b2315 100644
--- a/core/java/android/text/format/DateIntervalFormat.java
+++ b/core/java/android/text/format/DateIntervalFormat.java
@@ -37,6 +37,7 @@ import java.util.TimeZone;
* @hide
*/
@VisibleForTesting(visibility = PACKAGE)
+@android.ravenwood.annotation.RavenwoodKeepWholeClass
public final class DateIntervalFormat {
private static final LruCache<String, android.icu.text.DateIntervalFormat> CACHED_FORMATTERS =
diff --git a/core/java/android/text/format/DateTimeFormat.java b/core/java/android/text/format/DateTimeFormat.java
index 064d7172c44f..c8dd61d0d75c 100644
--- a/core/java/android/text/format/DateTimeFormat.java
+++ b/core/java/android/text/format/DateTimeFormat.java
@@ -29,6 +29,7 @@ import android.util.LruCache;
*
* @hide
*/
+@android.ravenwood.annotation.RavenwoodKeepWholeClass
class DateTimeFormat {
private static final FormatterCache CACHED_FORMATTERS = new FormatterCache();
diff --git a/core/java/android/text/format/DateUtils.java b/core/java/android/text/format/DateUtils.java
index 518a5498d6ed..12ad76454870 100644
--- a/core/java/android/text/format/DateUtils.java
+++ b/core/java/android/text/format/DateUtils.java
@@ -44,6 +44,7 @@ import java.util.TimeZone;
* This class contains various date-related utilities for creating text for things like
* elapsed time and date ranges, strings for days of the week and months, and AM/PM text etc.
*/
+@android.ravenwood.annotation.RavenwoodKeepWholeClass
public class DateUtils
{
private static final Object sLock = new Object();
diff --git a/core/java/android/text/format/DateUtilsBridge.java b/core/java/android/text/format/DateUtilsBridge.java
index 92ec9cf6d736..752a8c0ef40a 100644
--- a/core/java/android/text/format/DateUtilsBridge.java
+++ b/core/java/android/text/format/DateUtilsBridge.java
@@ -46,6 +46,7 @@ import com.android.internal.annotations.VisibleForTesting;
* @hide
*/
@VisibleForTesting(visibility = PACKAGE)
+@android.ravenwood.annotation.RavenwoodKeepWholeClass
public final class DateUtilsBridge {
/**
diff --git a/core/java/android/text/format/Formatter.java b/core/java/android/text/format/Formatter.java
index 7653bdb7b2d8..e7783dcb2630 100644
--- a/core/java/android/text/format/Formatter.java
+++ b/core/java/android/text/format/Formatter.java
@@ -41,6 +41,7 @@ import java.util.Locale;
* Utility class to aid in formatting common values that are not covered
* by the {@link java.util.Formatter} class in {@link java.util}
*/
+@android.ravenwood.annotation.RavenwoodKeepWholeClass
public final class Formatter {
/** {@hide} */
diff --git a/core/java/android/text/format/RelativeDateTimeFormatter.java b/core/java/android/text/format/RelativeDateTimeFormatter.java
index 9096469699c1..6b940f8cb380 100644
--- a/core/java/android/text/format/RelativeDateTimeFormatter.java
+++ b/core/java/android/text/format/RelativeDateTimeFormatter.java
@@ -42,6 +42,7 @@ import java.util.Locale;
* @hide
*/
@VisibleForTesting(visibility = PACKAGE)
+@android.ravenwood.annotation.RavenwoodKeepWholeClass
public final class RelativeDateTimeFormatter {
public static final long SECOND_IN_MILLIS = 1000;
diff --git a/core/java/android/text/format/Time.java b/core/java/android/text/format/Time.java
index bac7c6cf87d3..1beb57389ef5 100644
--- a/core/java/android/text/format/Time.java
+++ b/core/java/android/text/format/Time.java
@@ -53,6 +53,7 @@ import java.util.TimeZone;
* @deprecated Use {@link java.util.GregorianCalendar} instead.
*/
@Deprecated
+@android.ravenwood.annotation.RavenwoodKeepWholeClass
public class Time {
private static final String Y_M_D_T_H_M_S_000 = "%Y-%m-%dT%H:%M:%S.000";
private static final String Y_M_D_T_H_M_S_000_Z = "%Y-%m-%dT%H:%M:%S.000Z";
diff --git a/core/java/android/text/format/TimeFormatter.java b/core/java/android/text/format/TimeFormatter.java
index e42ad6334649..dd703d85624f 100644
--- a/core/java/android/text/format/TimeFormatter.java
+++ b/core/java/android/text/format/TimeFormatter.java
@@ -40,6 +40,7 @@ import java.util.TimeZone;
*
* <p>This class is not thread safe.
*/
+@android.ravenwood.annotation.RavenwoodKeepWholeClass
class TimeFormatter {
// An arbitrary value outside the range representable by a char.
private static final int FORCE_LOWER_CASE = -1;
diff --git a/core/java/android/text/format/TimeMigrationUtils.java b/core/java/android/text/format/TimeMigrationUtils.java
index 17bac8d67b26..b2f5024c73b7 100644
--- a/core/java/android/text/format/TimeMigrationUtils.java
+++ b/core/java/android/text/format/TimeMigrationUtils.java
@@ -22,6 +22,7 @@ package android.text.format;
*
* @hide
*/
+@android.ravenwood.annotation.RavenwoodKeepWholeClass
public class TimeMigrationUtils {
private TimeMigrationUtils() {}
diff --git a/core/java/android/text/method/AllCapsTransformationMethod.java b/core/java/android/text/method/AllCapsTransformationMethod.java
index 305b056aee72..70dcc52691cd 100644
--- a/core/java/android/text/method/AllCapsTransformationMethod.java
+++ b/core/java/android/text/method/AllCapsTransformationMethod.java
@@ -33,6 +33,7 @@ import java.util.Locale;
*
* @hide
*/
+@android.ravenwood.annotation.RavenwoodKeepWholeClass
public class AllCapsTransformationMethod implements TransformationMethod2 {
private static final String TAG = "AllCapsTransformationMethod";
diff --git a/core/java/android/text/method/ArrowKeyMovementMethod.java b/core/java/android/text/method/ArrowKeyMovementMethod.java
index 37474e5645b0..609922073f53 100644
--- a/core/java/android/text/method/ArrowKeyMovementMethod.java
+++ b/core/java/android/text/method/ArrowKeyMovementMethod.java
@@ -30,6 +30,7 @@ import android.widget.TextView;
* A movement method that provides cursor movement and selection.
* Supports displaying the context menu on DPad Center.
*/
+@android.ravenwood.annotation.RavenwoodKeepWholeClass
public class ArrowKeyMovementMethod extends BaseMovementMethod implements MovementMethod {
private static boolean isSelecting(Spannable buffer) {
return ((MetaKeyKeyListener.getMetaState(buffer, MetaKeyKeyListener.META_SHIFT_ON) == 1) ||
diff --git a/core/java/android/text/method/BaseKeyListener.java b/core/java/android/text/method/BaseKeyListener.java
index e427908541e5..5ebfd99c6f6e 100644
--- a/core/java/android/text/method/BaseKeyListener.java
+++ b/core/java/android/text/method/BaseKeyListener.java
@@ -47,6 +47,7 @@ import java.text.BreakIterator;
* with hardware keyboards. Software input methods have no obligation to trigger
* the methods in this class.
*/
+@android.ravenwood.annotation.RavenwoodKeepWholeClass
public abstract class BaseKeyListener extends MetaKeyKeyListener
implements KeyListener {
/* package */ static final Object OLD_SEL_START = new NoCopySpan.Concrete();
diff --git a/core/java/android/text/method/BaseMovementMethod.java b/core/java/android/text/method/BaseMovementMethod.java
index 7a4b3a095ae9..0c2e52e04c1f 100644
--- a/core/java/android/text/method/BaseMovementMethod.java
+++ b/core/java/android/text/method/BaseMovementMethod.java
@@ -27,6 +27,7 @@ import android.widget.TextView;
/**
* Base classes for movement methods.
*/
+@android.ravenwood.annotation.RavenwoodKeepWholeClass
public class BaseMovementMethod implements MovementMethod {
@Override
public boolean canSelectArbitrarily() {
diff --git a/core/java/android/text/method/CharacterPickerDialog.java b/core/java/android/text/method/CharacterPickerDialog.java
index 7d838e06cd68..f084d03cf6dd 100644
--- a/core/java/android/text/method/CharacterPickerDialog.java
+++ b/core/java/android/text/method/CharacterPickerDialog.java
@@ -38,6 +38,7 @@ import com.android.internal.R;
/**
* Dialog for choosing accented characters related to a base character.
*/
+@android.ravenwood.annotation.RavenwoodKeepWholeClass
public class CharacterPickerDialog extends Dialog
implements OnItemClickListener, OnClickListener {
private View mView;
diff --git a/core/java/android/text/method/DateKeyListener.java b/core/java/android/text/method/DateKeyListener.java
index 0accbf6c74e5..acf182263272 100644
--- a/core/java/android/text/method/DateKeyListener.java
+++ b/core/java/android/text/method/DateKeyListener.java
@@ -35,6 +35,7 @@ import java.util.Locale;
* with hardware keyboards. Software input methods have no obligation to trigger
* the methods in this class.
*/
+@android.ravenwood.annotation.RavenwoodKeepWholeClass
public class DateKeyListener extends NumberKeyListener
{
public int getInputType() {
diff --git a/core/java/android/text/method/DateTimeKeyListener.java b/core/java/android/text/method/DateTimeKeyListener.java
index 1593db5de641..a46ae45433b9 100644
--- a/core/java/android/text/method/DateTimeKeyListener.java
+++ b/core/java/android/text/method/DateTimeKeyListener.java
@@ -35,6 +35,7 @@ import java.util.Locale;
* with hardware keyboards. Software input methods have no obligation to trigger
* the methods in this class.
*/
+@android.ravenwood.annotation.RavenwoodKeepWholeClass
public class DateTimeKeyListener extends NumberKeyListener
{
public int getInputType() {
diff --git a/core/java/android/text/method/DialerKeyListener.java b/core/java/android/text/method/DialerKeyListener.java
index 17abed6c363a..9eea51a07593 100644
--- a/core/java/android/text/method/DialerKeyListener.java
+++ b/core/java/android/text/method/DialerKeyListener.java
@@ -28,6 +28,7 @@ import android.view.KeyEvent;
* with hardware keyboards. Software input methods have no obligation to trigger
* the methods in this class.
*/
+@android.ravenwood.annotation.RavenwoodKeepWholeClass
public class DialerKeyListener extends NumberKeyListener
{
@Override
diff --git a/core/java/android/text/method/DigitsKeyListener.java b/core/java/android/text/method/DigitsKeyListener.java
index d9f2dcf2e896..c97d4afef4fa 100644
--- a/core/java/android/text/method/DigitsKeyListener.java
+++ b/core/java/android/text/method/DigitsKeyListener.java
@@ -40,6 +40,7 @@ import java.util.Locale;
* with hardware keyboards. Software input methods have no obligation to trigger
* the methods in this class.
*/
+@android.ravenwood.annotation.RavenwoodKeepWholeClass
public class DigitsKeyListener extends NumberKeyListener
{
private char[] mAccepted;
diff --git a/core/java/android/text/method/HideReturnsTransformationMethod.java b/core/java/android/text/method/HideReturnsTransformationMethod.java
index 40ce8714cf38..8b93b3558f86 100644
--- a/core/java/android/text/method/HideReturnsTransformationMethod.java
+++ b/core/java/android/text/method/HideReturnsTransformationMethod.java
@@ -24,6 +24,7 @@ import android.os.Build;
* to be hidden by displaying them as zero-width non-breaking space
* characters (\uFEFF).
*/
+@android.ravenwood.annotation.RavenwoodKeepWholeClass
public class HideReturnsTransformationMethod
extends ReplacementTransformationMethod {
private static char[] ORIGINAL = new char[] { '\r' };
diff --git a/core/java/android/text/method/InsertModeTransformationMethod.java b/core/java/android/text/method/InsertModeTransformationMethod.java
index 6c6576f8888e..ace2d256f5b0 100644
--- a/core/java/android/text/method/InsertModeTransformationMethod.java
+++ b/core/java/android/text/method/InsertModeTransformationMethod.java
@@ -58,6 +58,7 @@ import java.lang.reflect.Array;
* the new transformed text: "hello abc\n\n world", and the highlight range will be [5, 11).
* @hide
*/
+@android.ravenwood.annotation.RavenwoodKeepWholeClass
public class InsertModeTransformationMethod implements TransformationMethod, TextWatcher {
/** The start offset of the highlight range in the original text, inclusive. */
private int mStart;
diff --git a/core/java/android/text/method/KeyListener.java b/core/java/android/text/method/KeyListener.java
index ce7054c40d01..447c4d9432ed 100644
--- a/core/java/android/text/method/KeyListener.java
+++ b/core/java/android/text/method/KeyListener.java
@@ -34,6 +34,7 @@ import android.view.View;
* targetting Jelly Bean or later, and will only deliver it for some
* key presses to applications targetting Ice Cream Sandwich or earlier.
*/
+@android.ravenwood.annotation.RavenwoodKeepWholeClass
public interface KeyListener {
/**
* Return the type of text that this key listener is manipulating,
diff --git a/core/java/android/text/method/LinkMovementMethod.java b/core/java/android/text/method/LinkMovementMethod.java
index 9f4a0aea7207..484bc1ae1a85 100644
--- a/core/java/android/text/method/LinkMovementMethod.java
+++ b/core/java/android/text/method/LinkMovementMethod.java
@@ -33,6 +33,7 @@ import android.widget.TextView;
* A movement method that traverses links in the text buffer and scrolls if necessary.
* Supports clicking on links with DPad Center or Enter.
*/
+@android.ravenwood.annotation.RavenwoodKeepWholeClass
public class LinkMovementMethod extends ScrollingMovementMethod {
private static final int CLICK = 1;
private static final int UP = 2;
diff --git a/core/java/android/text/method/MetaKeyKeyListener.java b/core/java/android/text/method/MetaKeyKeyListener.java
index d1d7c968411f..7c9c2f132793 100644
--- a/core/java/android/text/method/MetaKeyKeyListener.java
+++ b/core/java/android/text/method/MetaKeyKeyListener.java
@@ -71,6 +71,7 @@ import android.view.View;
* }
* </code>
*/
+@android.ravenwood.annotation.RavenwoodKeepWholeClass
public abstract class MetaKeyKeyListener {
/**
* Flag that indicates that the SHIFT key is on.
diff --git a/core/java/android/text/method/MovementMethod.java b/core/java/android/text/method/MovementMethod.java
index f6fe575a9265..5ea439d50561 100644
--- a/core/java/android/text/method/MovementMethod.java
+++ b/core/java/android/text/method/MovementMethod.java
@@ -32,6 +32,7 @@ import android.widget.TextView;
* directly by applications.
* </p>
*/
+@android.ravenwood.annotation.RavenwoodKeepWholeClass
public interface MovementMethod {
public void initialize(TextView widget, Spannable text);
public boolean onKeyDown(TextView widget, Spannable text, int keyCode, KeyEvent event);
diff --git a/core/java/android/text/method/MultiTapKeyListener.java b/core/java/android/text/method/MultiTapKeyListener.java
index 5770482b3feb..022853abf450 100644
--- a/core/java/android/text/method/MultiTapKeyListener.java
+++ b/core/java/android/text/method/MultiTapKeyListener.java
@@ -36,6 +36,7 @@ import android.view.View;
* with hardware keyboards. Software input methods have no obligation to trigger
* the methods in this class.
*/
+@android.ravenwood.annotation.RavenwoodKeepWholeClass
public class MultiTapKeyListener extends BaseKeyListener
implements SpanWatcher {
private static MultiTapKeyListener[] sInstance =
diff --git a/core/java/android/text/method/NumberKeyListener.java b/core/java/android/text/method/NumberKeyListener.java
index 2b038dd11348..e32ccd48c7e3 100644
--- a/core/java/android/text/method/NumberKeyListener.java
+++ b/core/java/android/text/method/NumberKeyListener.java
@@ -39,6 +39,7 @@ import java.util.Locale;
* with hardware keyboards. Software input methods have no obligation to trigger
* the methods in this class.
*/
+@android.ravenwood.annotation.RavenwoodKeepWholeClass
public abstract class NumberKeyListener extends BaseKeyListener
implements InputFilter
{
diff --git a/core/java/android/text/method/OffsetMapping.java b/core/java/android/text/method/OffsetMapping.java
index fcf3de6784fb..99613d3ae300 100644
--- a/core/java/android/text/method/OffsetMapping.java
+++ b/core/java/android/text/method/OffsetMapping.java
@@ -27,6 +27,7 @@ import java.lang.annotation.RetentionPolicy;
* {@link TransformationMethod} that alters the text length.
* @hide
*/
+@android.ravenwood.annotation.RavenwoodKeepWholeClass
public interface OffsetMapping {
/**
* The mapping strategy for a character offset.
diff --git a/core/java/android/text/method/PasswordTransformationMethod.java b/core/java/android/text/method/PasswordTransformationMethod.java
index 53553be6e5a8..4a61d9aa1bd0 100644
--- a/core/java/android/text/method/PasswordTransformationMethod.java
+++ b/core/java/android/text/method/PasswordTransformationMethod.java
@@ -33,6 +33,7 @@ import android.view.View;
import java.lang.ref.WeakReference;
+@android.ravenwood.annotation.RavenwoodKeepWholeClass
public class PasswordTransformationMethod
implements TransformationMethod, TextWatcher
{
diff --git a/core/java/android/text/method/QwertyKeyListener.java b/core/java/android/text/method/QwertyKeyListener.java
index c43864d0f215..27c58ea3a56e 100644
--- a/core/java/android/text/method/QwertyKeyListener.java
+++ b/core/java/android/text/method/QwertyKeyListener.java
@@ -37,6 +37,7 @@ import android.view.View;
* with hardware keyboards. Software input methods have no obligation to trigger
* the methods in this class.
*/
+@android.ravenwood.annotation.RavenwoodKeepWholeClass
public class QwertyKeyListener extends BaseKeyListener {
private static QwertyKeyListener[] sInstance =
new QwertyKeyListener[Capitalize.values().length * 2];
diff --git a/core/java/android/text/method/ReplacementTransformationMethod.java b/core/java/android/text/method/ReplacementTransformationMethod.java
index d6f879aa4353..05899d79b02e 100644
--- a/core/java/android/text/method/ReplacementTransformationMethod.java
+++ b/core/java/android/text/method/ReplacementTransformationMethod.java
@@ -30,6 +30,7 @@ import android.view.View;
* array to be replaced by the corresponding characters in the
* {@link #getReplacement} array.
*/
+@android.ravenwood.annotation.RavenwoodKeepWholeClass
public abstract class ReplacementTransformationMethod
implements TransformationMethod
{
diff --git a/core/java/android/text/method/ScrollingMovementMethod.java b/core/java/android/text/method/ScrollingMovementMethod.java
index 4f422cbf51cb..2e0eda96b7f6 100644
--- a/core/java/android/text/method/ScrollingMovementMethod.java
+++ b/core/java/android/text/method/ScrollingMovementMethod.java
@@ -25,6 +25,7 @@ import android.widget.TextView;
/**
* A movement method that interprets movement keys by scrolling the text buffer.
*/
+@android.ravenwood.annotation.RavenwoodKeepWholeClass
public class ScrollingMovementMethod extends BaseMovementMethod implements MovementMethod {
@Override
protected boolean left(TextView widget, Spannable buffer) {
diff --git a/core/java/android/text/method/SingleLineTransformationMethod.java b/core/java/android/text/method/SingleLineTransformationMethod.java
index 818526a7d795..d6eff86920f8 100644
--- a/core/java/android/text/method/SingleLineTransformationMethod.java
+++ b/core/java/android/text/method/SingleLineTransformationMethod.java
@@ -21,6 +21,7 @@ package android.text.method;
* displayed as spaces instead of causing line breaks, and causes
* carriage return characters (\r) to have no appearance.
*/
+@android.ravenwood.annotation.RavenwoodKeepWholeClass
public class SingleLineTransformationMethod
extends ReplacementTransformationMethod {
private static char[] ORIGINAL = new char[] { '\n', '\r' };
diff --git a/core/java/android/text/method/TextKeyListener.java b/core/java/android/text/method/TextKeyListener.java
index 2eb917b6fd57..1b0ae61c62a4 100644
--- a/core/java/android/text/method/TextKeyListener.java
+++ b/core/java/android/text/method/TextKeyListener.java
@@ -43,6 +43,7 @@ import java.lang.ref.WeakReference;
* with hardware keyboards. Software input methods have no obligation to trigger
* the methods in this class.
*/
+@android.ravenwood.annotation.RavenwoodKeepWholeClass
public class TextKeyListener extends BaseKeyListener implements SpanWatcher {
private static TextKeyListener[] sInstance =
new TextKeyListener[Capitalize.values().length * 2];
diff --git a/core/java/android/text/method/TimeKeyListener.java b/core/java/android/text/method/TimeKeyListener.java
index f11f40099d9c..337611c79e3e 100644
--- a/core/java/android/text/method/TimeKeyListener.java
+++ b/core/java/android/text/method/TimeKeyListener.java
@@ -35,6 +35,7 @@ import java.util.Locale;
* with hardware keyboards. Software input methods have no obligation to trigger
* the methods in this class.
*/
+@android.ravenwood.annotation.RavenwoodKeepWholeClass
public class TimeKeyListener extends NumberKeyListener
{
public int getInputType() {
diff --git a/core/java/android/text/method/Touch.java b/core/java/android/text/method/Touch.java
index 44811cb3ef8b..85aadba5da2b 100644
--- a/core/java/android/text/method/Touch.java
+++ b/core/java/android/text/method/Touch.java
@@ -25,6 +25,7 @@ import android.view.MotionEvent;
import android.view.ViewConfiguration;
import android.widget.TextView;
+@android.ravenwood.annotation.RavenwoodKeepWholeClass
public class Touch {
private Touch() { }
diff --git a/core/java/android/text/method/TransformationMethod.java b/core/java/android/text/method/TransformationMethod.java
index 8f3b334abbbd..5246baa39d14 100644
--- a/core/java/android/text/method/TransformationMethod.java
+++ b/core/java/android/text/method/TransformationMethod.java
@@ -24,6 +24,7 @@ import android.view.View;
* characters of passwords with dots, or keeping the newline characters
* from causing line breaks in single-line text fields.
*/
+@android.ravenwood.annotation.RavenwoodKeepWholeClass
public interface TransformationMethod
{
/**
diff --git a/core/java/android/text/method/TransformationMethod2.java b/core/java/android/text/method/TransformationMethod2.java
index 8d5ec246640e..6e0feb419f00 100644
--- a/core/java/android/text/method/TransformationMethod2.java
+++ b/core/java/android/text/method/TransformationMethod2.java
@@ -23,6 +23,7 @@ import android.compat.annotation.UnsupportedAppUsage;
*
* @hide
*/
+@android.ravenwood.annotation.RavenwoodKeepWholeClass
public interface TransformationMethod2 extends TransformationMethod {
/**
* Relax the contract of TransformationMethod to allow length changes,
diff --git a/core/java/android/text/method/TranslationTransformationMethod.java b/core/java/android/text/method/TranslationTransformationMethod.java
index 43d186ee9d21..8f43d3dd1f6f 100644
--- a/core/java/android/text/method/TranslationTransformationMethod.java
+++ b/core/java/android/text/method/TranslationTransformationMethod.java
@@ -33,6 +33,7 @@ import java.util.regex.Pattern;
*
* @hide
*/
+@android.ravenwood.annotation.RavenwoodKeepWholeClass
public class TranslationTransformationMethod implements TransformationMethod2 {
private static final String TAG = "TranslationTransformationMethod";
diff --git a/core/java/android/text/method/WordIterator.java b/core/java/android/text/method/WordIterator.java
index 2956f8461388..d57fa9b55312 100644
--- a/core/java/android/text/method/WordIterator.java
+++ b/core/java/android/text/method/WordIterator.java
@@ -37,6 +37,7 @@ import java.util.Locale;
* Also provides methods to determine word boundaries.
* {@hide}
*/
+@android.ravenwood.annotation.RavenwoodKeepWholeClass
public class WordIterator implements Selection.PositionIterator {
// Size of the window for the word iterator, should be greater than the longest word's length
private static final int WINDOW_WIDTH = 50;
diff --git a/core/java/android/text/style/AbsoluteSizeSpan.java b/core/java/android/text/style/AbsoluteSizeSpan.java
index 6d4f05a84a03..1bc5d71fcd37 100644
--- a/core/java/android/text/style/AbsoluteSizeSpan.java
+++ b/core/java/android/text/style/AbsoluteSizeSpan.java
@@ -32,6 +32,7 @@ import android.text.TextUtils;
* <img src="{@docRoot}reference/android/images/text/style/absolutesizespan.png" />
* <figcaption>Text with text size updated.</figcaption>
*/
+@android.ravenwood.annotation.RavenwoodKeepWholeClass
public class AbsoluteSizeSpan extends MetricAffectingSpan implements ParcelableSpan {
private final int mSize;
diff --git a/core/java/android/text/style/AccessibilityClickableSpan.java b/core/java/android/text/style/AccessibilityClickableSpan.java
index ee8d156f9aac..5741f2ae2864 100644
--- a/core/java/android/text/style/AccessibilityClickableSpan.java
+++ b/core/java/android/text/style/AccessibilityClickableSpan.java
@@ -43,6 +43,7 @@ import com.android.internal.R;
*
* @hide
*/
+@android.ravenwood.annotation.RavenwoodKeepWholeClass
public class AccessibilityClickableSpan extends ClickableSpan
implements ParcelableSpan {
// The id of the span this one replaces
diff --git a/core/java/android/text/style/AccessibilityReplacementSpan.java b/core/java/android/text/style/AccessibilityReplacementSpan.java
index e4fc14790b53..af3a324668d0 100644
--- a/core/java/android/text/style/AccessibilityReplacementSpan.java
+++ b/core/java/android/text/style/AccessibilityReplacementSpan.java
@@ -31,6 +31,7 @@ import android.view.accessibility.AccessibilityNodeInfo;
*
* @hide
*/
+@android.ravenwood.annotation.RavenwoodKeepWholeClass
public class AccessibilityReplacementSpan extends ReplacementSpan
implements ParcelableSpan {
diff --git a/core/java/android/text/style/AccessibilityURLSpan.java b/core/java/android/text/style/AccessibilityURLSpan.java
index e280bdf8b339..1fb76e776b56 100644
--- a/core/java/android/text/style/AccessibilityURLSpan.java
+++ b/core/java/android/text/style/AccessibilityURLSpan.java
@@ -27,6 +27,7 @@ import android.view.accessibility.AccessibilityNodeInfo;
* @hide
*/
@SuppressWarnings("ParcelableCreator")
+@android.ravenwood.annotation.RavenwoodKeepWholeClass
public class AccessibilityURLSpan extends URLSpan implements Parcelable {
final AccessibilityClickableSpan mAccessibilityClickableSpan;
diff --git a/core/java/android/text/style/AlignmentSpan.java b/core/java/android/text/style/AlignmentSpan.java
index 31db78a51c75..53cbd63a32c7 100644
--- a/core/java/android/text/style/AlignmentSpan.java
+++ b/core/java/android/text/style/AlignmentSpan.java
@@ -25,6 +25,7 @@ import android.text.TextUtils;
/**
* Span that allows defining the alignment of text at the paragraph level.
*/
+@android.ravenwood.annotation.RavenwoodKeepWholeClass
public interface AlignmentSpan extends ParagraphStyle {
/**
diff --git a/core/java/android/text/style/BackgroundColorSpan.java b/core/java/android/text/style/BackgroundColorSpan.java
index 13647d92a897..bb04d0ff6178 100644
--- a/core/java/android/text/style/BackgroundColorSpan.java
+++ b/core/java/android/text/style/BackgroundColorSpan.java
@@ -34,6 +34,7 @@ import android.text.TextUtils;
* <img src="{@docRoot}reference/android/images/text/style/backgroundcolorspan.png" />
* <figcaption>Set a background color for the text.</figcaption>
*/
+@android.ravenwood.annotation.RavenwoodKeepWholeClass
public class BackgroundColorSpan extends CharacterStyle
implements UpdateAppearance, ParcelableSpan {
diff --git a/core/java/android/text/style/BulletSpan.java b/core/java/android/text/style/BulletSpan.java
index f70e6c56b5c9..24ae6e25fa59 100644
--- a/core/java/android/text/style/BulletSpan.java
+++ b/core/java/android/text/style/BulletSpan.java
@@ -63,6 +63,7 @@ import android.text.TextUtils;
* <img src="{@docRoot}reference/android/images/text/style/custombulletspan.png" />
* <figcaption>Customized BulletSpan.</figcaption>
*/
+@android.ravenwood.annotation.RavenwoodKeepWholeClass
public class BulletSpan implements LeadingMarginSpan, ParcelableSpan {
// Bullet is slightly bigger to avoid aliasing artifacts on mdpi devices.
private static final int STANDARD_BULLET_RADIUS = 4;
diff --git a/core/java/android/text/style/CharacterStyle.java b/core/java/android/text/style/CharacterStyle.java
index 5b95f1a7816a..2ea05e6ecde0 100644
--- a/core/java/android/text/style/CharacterStyle.java
+++ b/core/java/android/text/style/CharacterStyle.java
@@ -23,6 +23,7 @@ import android.text.TextPaint;
* class. Most extend its subclass {@link MetricAffectingSpan}, but simple
* ones may just implement {@link UpdateAppearance}.
*/
+@android.ravenwood.annotation.RavenwoodKeepWholeClass
public abstract class CharacterStyle {
public abstract void updateDrawState(TextPaint tp);
diff --git a/core/java/android/text/style/ClickableSpan.java b/core/java/android/text/style/ClickableSpan.java
index 238da55526b0..9e35d75c8833 100644
--- a/core/java/android/text/style/ClickableSpan.java
+++ b/core/java/android/text/style/ClickableSpan.java
@@ -36,6 +36,7 @@ import android.view.View;
* <img src="{@docRoot}reference/android/images/text/style/clickablespan.png" />
* <figcaption>Text with <code>ClickableSpan</code>.</figcaption>
*/
+@android.ravenwood.annotation.RavenwoodKeepWholeClass
public abstract class ClickableSpan extends CharacterStyle implements UpdateAppearance {
private static int sIdCounter = 0;
diff --git a/core/java/android/text/style/ForegroundColorSpan.java b/core/java/android/text/style/ForegroundColorSpan.java
index 5c9742622549..337c49fddf16 100644
--- a/core/java/android/text/style/ForegroundColorSpan.java
+++ b/core/java/android/text/style/ForegroundColorSpan.java
@@ -34,6 +34,7 @@ import android.text.TextUtils;
* <img src="{@docRoot}reference/android/images/text/style/foregroundcolorspan.png" />
* <figcaption>Set a text color.</figcaption>
*/
+@android.ravenwood.annotation.RavenwoodKeepWholeClass
public class ForegroundColorSpan extends CharacterStyle
implements UpdateAppearance, ParcelableSpan {
diff --git a/core/java/android/text/style/IconMarginSpan.java b/core/java/android/text/style/IconMarginSpan.java
index a6c513971ffb..cc946e98ece9 100644
--- a/core/java/android/text/style/IconMarginSpan.java
+++ b/core/java/android/text/style/IconMarginSpan.java
@@ -44,6 +44,7 @@ import android.text.Spanned;
* @see DrawableMarginSpan for working with a {@link android.graphics.drawable.Drawable} instead of
* a {@link Bitmap}.
*/
+@android.ravenwood.annotation.RavenwoodKeepWholeClass
public class IconMarginSpan implements LeadingMarginSpan, LineHeightSpan {
@NonNull
diff --git a/core/java/android/text/style/LeadingMarginSpan.java b/core/java/android/text/style/LeadingMarginSpan.java
index 5bd2d60bb34f..60c45784f680 100644
--- a/core/java/android/text/style/LeadingMarginSpan.java
+++ b/core/java/android/text/style/LeadingMarginSpan.java
@@ -32,6 +32,7 @@ import android.text.TextUtils;
* LeadingMarginSpans should be attached from the first character to the last
* character of a single paragraph.
*/
+@android.ravenwood.annotation.RavenwoodKeepWholeClass
public interface LeadingMarginSpan
extends ParagraphStyle
{
diff --git a/core/java/android/text/style/LineBackgroundSpan.java b/core/java/android/text/style/LineBackgroundSpan.java
index 7cb91477738e..c2d38ce92290 100644
--- a/core/java/android/text/style/LineBackgroundSpan.java
+++ b/core/java/android/text/style/LineBackgroundSpan.java
@@ -28,6 +28,7 @@ import android.text.TextUtils;
/**
* Used to change the background of lines where the span is attached to.
*/
+@android.ravenwood.annotation.RavenwoodKeepWholeClass
public interface LineBackgroundSpan extends ParagraphStyle
{
/**
diff --git a/core/java/android/text/style/LineBreakConfigSpan.java b/core/java/android/text/style/LineBreakConfigSpan.java
index eeb638389271..1af1eed86e1f 100644
--- a/core/java/android/text/style/LineBreakConfigSpan.java
+++ b/core/java/android/text/style/LineBreakConfigSpan.java
@@ -31,6 +31,7 @@ import java.util.Objects;
* LineBreakSpan for changing line break style of the specific region of the text.
*/
@FlaggedApi(FLAG_NO_BREAK_NO_HYPHENATION_SPAN)
+@android.ravenwood.annotation.RavenwoodKeepWholeClass
public final class LineBreakConfigSpan implements ParcelableSpan {
private final LineBreakConfig mLineBreakConfig;
diff --git a/core/java/android/text/style/LineHeightSpan.java b/core/java/android/text/style/LineHeightSpan.java
index ae565d1c3317..71e8932c4aba 100644
--- a/core/java/android/text/style/LineHeightSpan.java
+++ b/core/java/android/text/style/LineHeightSpan.java
@@ -30,6 +30,7 @@ import com.android.internal.util.Preconditions;
/**
* The classes that affect the line height of paragraph should implement this interface.
*/
+@android.ravenwood.annotation.RavenwoodKeepWholeClass
public interface LineHeightSpan extends ParagraphStyle, WrapTogetherSpan {
/**
* Classes that implement this should define how the height is being calculated.
diff --git a/core/java/android/text/style/LocaleSpan.java b/core/java/android/text/style/LocaleSpan.java
index 489ceeaa5542..be5525a2f41a 100644
--- a/core/java/android/text/style/LocaleSpan.java
+++ b/core/java/android/text/style/LocaleSpan.java
@@ -32,6 +32,7 @@ import java.util.Locale;
/**
* Changes the {@link Locale} of the text to which the span is attached.
*/
+@android.ravenwood.annotation.RavenwoodKeepWholeClass
public class LocaleSpan extends MetricAffectingSpan implements ParcelableSpan {
@NonNull
private final LocaleList mLocales;
diff --git a/core/java/android/text/style/MaskFilterSpan.java b/core/java/android/text/style/MaskFilterSpan.java
index 587d1b4497dc..44db012953b3 100644
--- a/core/java/android/text/style/MaskFilterSpan.java
+++ b/core/java/android/text/style/MaskFilterSpan.java
@@ -30,6 +30,7 @@ import android.text.TextPaint;
* <img src="{@docRoot}reference/android/images/text/style/maskfilterspan.png" />
* <figcaption>Text blurred with the <code>MaskFilterSpan</code>.</figcaption>
*/
+@android.ravenwood.annotation.RavenwoodKeepWholeClass
public class MaskFilterSpan extends CharacterStyle implements UpdateAppearance {
private MaskFilter mFilter;
diff --git a/core/java/android/text/style/MetricAffectingSpan.java b/core/java/android/text/style/MetricAffectingSpan.java
index 61b7947af638..f30fdc15ae39 100644
--- a/core/java/android/text/style/MetricAffectingSpan.java
+++ b/core/java/android/text/style/MetricAffectingSpan.java
@@ -23,6 +23,7 @@ import android.text.TextPaint;
* The classes that affect character-level text formatting in a way that
* changes the width or height of characters extend this class.
*/
+@android.ravenwood.annotation.RavenwoodKeepWholeClass
public abstract class MetricAffectingSpan
extends CharacterStyle
implements UpdateLayout {
diff --git a/core/java/android/text/style/NoWritingToolsSpan.java b/core/java/android/text/style/NoWritingToolsSpan.java
index 90f85aa69faa..c7dfcfa6dc0b 100644
--- a/core/java/android/text/style/NoWritingToolsSpan.java
+++ b/core/java/android/text/style/NoWritingToolsSpan.java
@@ -32,6 +32,7 @@ import android.text.TextUtils;
* tools should only rewrite the user input text, and not modify the quoted text.
*/
@FlaggedApi(FLAG_WRITING_TOOLS)
+@android.ravenwood.annotation.RavenwoodKeepWholeClass
public final class NoWritingToolsSpan implements ParcelableSpan {
/**
diff --git a/core/java/android/text/style/ParagraphStyle.java b/core/java/android/text/style/ParagraphStyle.java
index 423156eca3de..27c1e261b116 100644
--- a/core/java/android/text/style/ParagraphStyle.java
+++ b/core/java/android/text/style/ParagraphStyle.java
@@ -20,6 +20,7 @@ package android.text.style;
* The classes that affect paragraph-level text formatting implement
* this interface.
*/
+@android.ravenwood.annotation.RavenwoodKeepWholeClass
public interface ParagraphStyle
{
diff --git a/core/java/android/text/style/QuoteSpan.java b/core/java/android/text/style/QuoteSpan.java
index 393ede653cb1..99c95749205a 100644
--- a/core/java/android/text/style/QuoteSpan.java
+++ b/core/java/android/text/style/QuoteSpan.java
@@ -57,6 +57,7 @@ import android.text.TextUtils;
* <img src="{@docRoot}reference/android/images/text/style/customquotespan.png" />
* <figcaption>Customized <code>QuoteSpan</code>.</figcaption>
*/
+@android.ravenwood.annotation.RavenwoodKeepWholeClass
public class QuoteSpan implements LeadingMarginSpan, ParcelableSpan {
/**
* Default stripe width in pixels.
diff --git a/core/java/android/text/style/RasterizerSpan.java b/core/java/android/text/style/RasterizerSpan.java
index f0be50ab065c..cf8599c4f1b1 100644
--- a/core/java/android/text/style/RasterizerSpan.java
+++ b/core/java/android/text/style/RasterizerSpan.java
@@ -22,6 +22,7 @@ import android.text.TextPaint;
/**
* @removed Rasterizer is not supported for hw-accerlerated and PDF rendering
*/
+@android.ravenwood.annotation.RavenwoodKeepWholeClass
public class RasterizerSpan extends CharacterStyle implements UpdateAppearance {
private Rasterizer mRasterizer;
diff --git a/core/java/android/text/style/RelativeSizeSpan.java b/core/java/android/text/style/RelativeSizeSpan.java
index 5c91b201d28c..38d5d38ae704 100644
--- a/core/java/android/text/style/RelativeSizeSpan.java
+++ b/core/java/android/text/style/RelativeSizeSpan.java
@@ -34,6 +34,7 @@ import android.text.TextUtils;
* <img src="{@docRoot}reference/android/images/text/style/relativesizespan.png" />
* <figcaption>Text increased by 50% with <code>RelativeSizeSpan</code>.</figcaption>
*/
+@android.ravenwood.annotation.RavenwoodKeepWholeClass
public class RelativeSizeSpan extends MetricAffectingSpan implements ParcelableSpan {
private final float mProportion;
diff --git a/core/java/android/text/style/ReplacementSpan.java b/core/java/android/text/style/ReplacementSpan.java
index 9430fd3a26c0..a6fe1fe72937 100644
--- a/core/java/android/text/style/ReplacementSpan.java
+++ b/core/java/android/text/style/ReplacementSpan.java
@@ -23,6 +23,7 @@ import android.graphics.Canvas;
import android.graphics.Paint;
import android.text.TextPaint;
+@android.ravenwood.annotation.RavenwoodKeepWholeClass
public abstract class ReplacementSpan extends MetricAffectingSpan {
private CharSequence mContentDescription = null;
diff --git a/core/java/android/text/style/ScaleXSpan.java b/core/java/android/text/style/ScaleXSpan.java
index d022b071b4d7..009973ee5306 100644
--- a/core/java/android/text/style/ScaleXSpan.java
+++ b/core/java/android/text/style/ScaleXSpan.java
@@ -36,6 +36,7 @@ import android.text.TextUtils;
* <img src="{@docRoot}reference/android/images/text/style/scalexspan.png" />
* <figcaption>Text scaled by 100% with <code>ScaleXSpan</code>.</figcaption>
*/
+@android.ravenwood.annotation.RavenwoodKeepWholeClass
public class ScaleXSpan extends MetricAffectingSpan implements ParcelableSpan {
private final float mProportion;
diff --git a/core/java/android/text/style/SpanUtils.java b/core/java/android/text/style/SpanUtils.java
index 6b4bd1a76358..21a96cdfe3b1 100644
--- a/core/java/android/text/style/SpanUtils.java
+++ b/core/java/android/text/style/SpanUtils.java
@@ -30,6 +30,7 @@ import java.util.List;
/**
* @hide
*/
+@android.ravenwood.annotation.RavenwoodKeepWholeClass
public class SpanUtils {
private SpanUtils() {} // Do not instantiate
diff --git a/core/java/android/text/style/SpellCheckSpan.java b/core/java/android/text/style/SpellCheckSpan.java
index e8ec3c6fb55c..39cd279d3d18 100644
--- a/core/java/android/text/style/SpellCheckSpan.java
+++ b/core/java/android/text/style/SpellCheckSpan.java
@@ -28,6 +28,7 @@ import android.text.TextUtils;
*
* @hide
*/
+@android.ravenwood.annotation.RavenwoodKeepWholeClass
public class SpellCheckSpan implements ParcelableSpan {
private boolean mSpellCheckInProgress;
diff --git a/core/java/android/text/style/StrikethroughSpan.java b/core/java/android/text/style/StrikethroughSpan.java
index 65ee34717232..3654870ee088 100644
--- a/core/java/android/text/style/StrikethroughSpan.java
+++ b/core/java/android/text/style/StrikethroughSpan.java
@@ -32,6 +32,7 @@ import android.text.TextUtils;
* <img src="{@docRoot}reference/android/images/text/style/strikethroughspan.png" />
* <figcaption>Strikethrough text.</figcaption>
*/
+@android.ravenwood.annotation.RavenwoodKeepWholeClass
public class StrikethroughSpan extends CharacterStyle
implements UpdateAppearance, ParcelableSpan {
diff --git a/core/java/android/text/style/StyleSpan.java b/core/java/android/text/style/StyleSpan.java
index 378682b9c890..c01e13443ad1 100644
--- a/core/java/android/text/style/StyleSpan.java
+++ b/core/java/android/text/style/StyleSpan.java
@@ -44,6 +44,7 @@ import android.text.TextUtils;
* <img src="{@docRoot}reference/android/images/text/style/stylespan.png" />
* <figcaption>Text styled bold and italic with the <code>StyleSpan</code>.</figcaption>
*/
+@android.ravenwood.annotation.RavenwoodKeepWholeClass
public class StyleSpan extends MetricAffectingSpan implements ParcelableSpan {
private final int mStyle;
diff --git a/core/java/android/text/style/SubscriptSpan.java b/core/java/android/text/style/SubscriptSpan.java
index 729a9ad73e75..54c765d902a4 100644
--- a/core/java/android/text/style/SubscriptSpan.java
+++ b/core/java/android/text/style/SubscriptSpan.java
@@ -37,6 +37,7 @@ import android.text.TextUtils;
* Note: Since the span affects the position of the text, if the text is on the last line of a
* TextView, it may appear cut.
*/
+@android.ravenwood.annotation.RavenwoodKeepWholeClass
public class SubscriptSpan extends MetricAffectingSpan implements ParcelableSpan {
/**
diff --git a/core/java/android/text/style/SuggestionRangeSpan.java b/core/java/android/text/style/SuggestionRangeSpan.java
index 1eee99aaac62..640fae4d1a3a 100644
--- a/core/java/android/text/style/SuggestionRangeSpan.java
+++ b/core/java/android/text/style/SuggestionRangeSpan.java
@@ -27,6 +27,7 @@ import android.text.TextUtils;
* A SuggestionRangeSpan is used to show which part of an EditText is affected by a suggestion
* popup window.
*/
+@android.ravenwood.annotation.RavenwoodKeepWholeClass
public final class SuggestionRangeSpan extends CharacterStyle implements ParcelableSpan {
private int mBackgroundColor;
diff --git a/core/java/android/text/style/SuggestionSpan.java b/core/java/android/text/style/SuggestionSpan.java
index 0cf96f617f4a..d819062428f9 100644
--- a/core/java/android/text/style/SuggestionSpan.java
+++ b/core/java/android/text/style/SuggestionSpan.java
@@ -48,6 +48,7 @@ import java.util.Locale;
*
* @see TextView#isSuggestionsEnabled()
*/
+@android.ravenwood.annotation.RavenwoodKeepWholeClass
public class SuggestionSpan extends CharacterStyle implements ParcelableSpan {
private static final String TAG = "SuggestionSpan";
diff --git a/core/java/android/text/style/SuperscriptSpan.java b/core/java/android/text/style/SuperscriptSpan.java
index 561022352ffd..d3b339c02e92 100644
--- a/core/java/android/text/style/SuperscriptSpan.java
+++ b/core/java/android/text/style/SuperscriptSpan.java
@@ -35,6 +35,7 @@ import android.text.TextUtils;
* TextView, it may appear cut. This can be avoided by decreasing the text size with an {@link
* AbsoluteSizeSpan}
*/
+@android.ravenwood.annotation.RavenwoodKeepWholeClass
public class SuperscriptSpan extends MetricAffectingSpan implements ParcelableSpan {
/**
* Creates a {@link SuperscriptSpan}.
diff --git a/core/java/android/text/style/TabStopSpan.java b/core/java/android/text/style/TabStopSpan.java
index 812847594ad8..e6733a2aac91 100644
--- a/core/java/android/text/style/TabStopSpan.java
+++ b/core/java/android/text/style/TabStopSpan.java
@@ -24,6 +24,7 @@ import android.annotation.Px;
* the leading margin of the line. <code>TabStopSpan</code> will only affect the first tab
* encountered on the first line of the text.
*/
+@android.ravenwood.annotation.RavenwoodKeepWholeClass
public interface TabStopSpan extends ParagraphStyle {
/**
diff --git a/core/java/android/text/style/TextAppearanceSpan.java b/core/java/android/text/style/TextAppearanceSpan.java
index 245a9dbc9f6c..7ede3499dc4d 100644
--- a/core/java/android/text/style/TextAppearanceSpan.java
+++ b/core/java/android/text/style/TextAppearanceSpan.java
@@ -58,6 +58,7 @@ import android.text.TextUtils;
* @attr ref android.R.styleable#TextAppearance_fontVariationSettings
*
*/
+@android.ravenwood.annotation.RavenwoodKeepWholeClass
public class TextAppearanceSpan extends MetricAffectingSpan implements ParcelableSpan {
private final String mFamilyName;
private final int mStyle;
diff --git a/core/java/android/text/style/TtsSpan.java b/core/java/android/text/style/TtsSpan.java
index e0d4ec1ca826..6d776d14fb00 100644
--- a/core/java/android/text/style/TtsSpan.java
+++ b/core/java/android/text/style/TtsSpan.java
@@ -42,6 +42,7 @@ import java.util.Locale;
* The inner classes are there for convenience and provide builders for each
* TtsSpan type.
*/
+@android.ravenwood.annotation.RavenwoodKeepWholeClass
public class TtsSpan implements ParcelableSpan {
private final String mType;
private final PersistableBundle mArgs;
diff --git a/core/java/android/text/style/TypefaceSpan.java b/core/java/android/text/style/TypefaceSpan.java
index bdfc772c0328..86f7f7638629 100644
--- a/core/java/android/text/style/TypefaceSpan.java
+++ b/core/java/android/text/style/TypefaceSpan.java
@@ -50,6 +50,7 @@ import android.text.TextUtils;
* <figcaption>Text with <code>TypefaceSpan</code>s constructed based on a font from resource and
* from a font family.</figcaption>
*/
+@android.ravenwood.annotation.RavenwoodKeepWholeClass
public class TypefaceSpan extends MetricAffectingSpan implements ParcelableSpan {
@Nullable
diff --git a/core/java/android/text/style/URLSpan.java b/core/java/android/text/style/URLSpan.java
index 9969d29a857d..f06627d0cbe1 100644
--- a/core/java/android/text/style/URLSpan.java
+++ b/core/java/android/text/style/URLSpan.java
@@ -41,6 +41,7 @@ import android.view.View;
* <img src="{@docRoot}reference/android/images/text/style/urlspan.png" />
* <figcaption>Text with <code>URLSpan</code>.</figcaption>
*/
+@android.ravenwood.annotation.RavenwoodKeepWholeClass
public class URLSpan extends ClickableSpan implements ParcelableSpan {
private final String mURL;
diff --git a/core/java/android/text/style/UnderlineSpan.java b/core/java/android/text/style/UnderlineSpan.java
index 075e70b7fbf5..b3bb142d1dc8 100644
--- a/core/java/android/text/style/UnderlineSpan.java
+++ b/core/java/android/text/style/UnderlineSpan.java
@@ -32,6 +32,7 @@ import android.text.TextUtils;
* <img src="{@docRoot}reference/android/images/text/style/underlinespan.png" />
* <figcaption>Underlined text.</figcaption>
*/
+@android.ravenwood.annotation.RavenwoodKeepWholeClass
public class UnderlineSpan extends CharacterStyle
implements UpdateAppearance, ParcelableSpan {
diff --git a/core/java/android/text/style/UpdateAppearance.java b/core/java/android/text/style/UpdateAppearance.java
index 7112347fcfbf..7b0a6d372122 100644
--- a/core/java/android/text/style/UpdateAppearance.java
+++ b/core/java/android/text/style/UpdateAppearance.java
@@ -22,5 +22,6 @@ package android.text.style;
* that if the class also impacts size or other metrics, it should instead
* implement {@link UpdateLayout}.
*/
+@android.ravenwood.annotation.RavenwoodKeepWholeClass
public interface UpdateAppearance {
}
diff --git a/core/java/android/text/style/UpdateLayout.java b/core/java/android/text/style/UpdateLayout.java
index 591075ecb972..5af4141cc8c2 100644
--- a/core/java/android/text/style/UpdateLayout.java
+++ b/core/java/android/text/style/UpdateLayout.java
@@ -22,4 +22,5 @@ package android.text.style;
* this interface. This interface also includes {@link UpdateAppearance}
* since such a change implicitly also impacts the appearance.
*/
+@android.ravenwood.annotation.RavenwoodKeepWholeClass
public interface UpdateLayout extends UpdateAppearance { }
diff --git a/core/java/android/text/style/WrapTogetherSpan.java b/core/java/android/text/style/WrapTogetherSpan.java
index 11721a8c3253..cf74c1bae3b7 100644
--- a/core/java/android/text/style/WrapTogetherSpan.java
+++ b/core/java/android/text/style/WrapTogetherSpan.java
@@ -16,6 +16,7 @@
package android.text.style;
+@android.ravenwood.annotation.RavenwoodKeepWholeClass
public interface WrapTogetherSpan
extends ParagraphStyle
{
diff --git a/core/java/android/text/util/Rfc822Token.java b/core/java/android/text/util/Rfc822Token.java
index 2f207db9d494..d6e987b2952e 100644
--- a/core/java/android/text/util/Rfc822Token.java
+++ b/core/java/android/text/util/Rfc822Token.java
@@ -22,6 +22,7 @@ import android.annotation.Nullable;
* This class stores an RFC 822-like name, address, and comment,
* and provides methods to convert them to quoted strings.
*/
+@android.ravenwood.annotation.RavenwoodKeepWholeClass
public class Rfc822Token {
@Nullable
private String mName, mAddress, mComment;
diff --git a/core/java/android/text/util/Rfc822Tokenizer.java b/core/java/android/text/util/Rfc822Tokenizer.java
index 68334e4d927c..8a9252ac9506 100644
--- a/core/java/android/text/util/Rfc822Tokenizer.java
+++ b/core/java/android/text/util/Rfc822Tokenizer.java
@@ -27,6 +27,7 @@ import java.util.Collection;
* a string of addresses (such as might be typed into such a field)
* into a series of Rfc822Tokens.
*/
+@android.ravenwood.annotation.RavenwoodKeepWholeClass
public class Rfc822Tokenizer implements MultiAutoCompleteTextView.Tokenizer {
/**
diff --git a/core/java/android/view/Display.java b/core/java/android/view/Display.java
index 231aa6816908..3f45e29f2d43 100644
--- a/core/java/android/view/Display.java
+++ b/core/java/android/view/Display.java
@@ -108,6 +108,7 @@ public final class Display {
private final String mOwnerPackageName;
private final Resources mResources;
private DisplayAdjustments mDisplayAdjustments;
+ private boolean mRefreshRateChangesRegistered;
@UnsupportedAppUsage
private DisplayInfo mDisplayInfo; // never null
@@ -1217,6 +1218,10 @@ public final class Display {
*/
public float getRefreshRate() {
synchronized (mLock) {
+ if (!mRefreshRateChangesRegistered) {
+ DisplayManagerGlobal.getInstance().registerForRefreshRateChanges();
+ mRefreshRateChangesRegistered = true;
+ }
updateDisplayInfoLocked();
return mDisplayInfo.getRefreshRate();
}
@@ -1601,7 +1606,7 @@ public final class Display {
.INTERNAL_EVENT_FLAG_DISPLAY_BASIC_CHANGED
| DisplayManagerGlobal
.INTERNAL_EVENT_FLAG_DISPLAY_HDR_SDR_RATIO_CHANGED,
- ActivityThread.currentPackageName());
+ ActivityThread.currentPackageName(), /* isEventFilterImplicit */ true);
}
}
diff --git a/core/java/android/view/InsetsController.java b/core/java/android/view/InsetsController.java
index c174fbe0bbcd..3659e785f590 100644
--- a/core/java/android/view/InsetsController.java
+++ b/core/java/android/view/InsetsController.java
@@ -1299,7 +1299,11 @@ public class InsetsController implements WindowInsetsController, InsetsAnimation
// Handle the pending show request for other insets types since the IME insets
// has being requested hidden.
handlePendingControlRequest(statsToken);
- getImeSourceConsumer().removeSurface();
+ if (!Flags.refactorInsetsController()) {
+ // the surface can't be removed until the end of the animation. This is handled by
+ // IMMS after the window was requested to be hidden.
+ getImeSourceConsumer().removeSurface();
+ }
}
applyAnimation(typesReady, false /* show */, fromIme, false /* skipsCallbacks */,
statsToken);
diff --git a/core/java/android/view/SurfaceControl.java b/core/java/android/view/SurfaceControl.java
index 0903d2293d67..c4347f05f4a3 100644
--- a/core/java/android/view/SurfaceControl.java
+++ b/core/java/android/view/SurfaceControl.java
@@ -3080,6 +3080,21 @@ public final class SurfaceControl implements Parcelable {
}
/**
+ * Changes the default ApplyToken.
+ *
+ * ApplyToken is used to determine the order in which Transactions are applied.
+ * Transactions applied with the same ApplyToken will be applied in the order
+ * they were queued in SurfaceFlinger. Transactions are sent via binder so the
+ * caller should be aware of the order in which binder calls are executed in
+ * SurfaceFlinger. This along with the ApplyToken will determine the order
+ * in which Transactions are applied. Transactions with different apply tokens
+ * will be applied in arbitrary order regardless of when they were queued in
+ * SurfaceFlinger.
+ *
+ * Caller must keep track of the previous ApplyToken if they want to restore it.
+ *
+ * Note each buffer producer should have its own ApplyToken in order to ensure
+ * that Transactions are not delayed by Transactions from other buffer producers.
*
* @hide
*/
@@ -3088,6 +3103,7 @@ public final class SurfaceControl implements Parcelable {
}
/**
+ * Returns the default ApplyToken.
*
* @hide
*/
@@ -3096,8 +3112,10 @@ public final class SurfaceControl implements Parcelable {
}
/**
- * Apply the transaction, clearing it's state, and making it usable
+ * Apply the transaction, clearing its state, and making it usable
* as a new transaction.
+ *
+ * This method will also increment the transaction ID for debugging purposes.
*/
public void apply() {
apply(/*sync*/ false);
@@ -3116,7 +3134,7 @@ public final class SurfaceControl implements Parcelable {
/**
- * Clear the transaction object, without applying it.
+ * Clear the transaction object, without applying it. The transction ID is preserved.
*
* @hide
*/
@@ -3375,6 +3393,9 @@ public final class SurfaceControl implements Parcelable {
* If two siblings share the same Z order the ordering is undefined. Surfaces
* with a negative Z will be placed below the parent surface.
*
+ * Calling setLayer after setRelativeLayer will reset the relative layer
+ * in the same transaction.
+ *
* @param sc The SurfaceControl to set the Z order on
* @param z The Z-order
* @return This Transaction.
@@ -3392,6 +3413,22 @@ public final class SurfaceControl implements Parcelable {
}
/**
+ * Set the Z-order for a given SurfaceControl, relative to the specified SurfaceControl.
+ * The SurfaceControl with a negative z will be placed below the relativeTo
+ * SurfaceControl and the SurfaceControl with a positive z will be placed above the
+ * relativeTo SurfaceControl.
+ *
+ * Calling setLayer will reset the relative layer. Calling setRelativeLayer after setLayer
+ * will override the setLayer call.
+ *
+ * If a layer is set to be relative to a layer that is destroyed, the layer will be
+ * offscreen until setLayer is called or setRelativeLayer is called with a valid
+ * SurfaceControl.
+ *
+ * @param sc The SurfaceControl to set the Z order on
+ * @param relativeTo The SurfaceControl to set the Z order relative to
+ * @param z The Z-order
+ * @return This Transaction.
* @hide
*/
public Transaction setRelativeLayer(SurfaceControl sc, SurfaceControl relativeTo, int z) {
@@ -3405,6 +3442,9 @@ public final class SurfaceControl implements Parcelable {
}
/**
+ * The hint from the buffer producer as to what portion of the layer is
+ * transparent.
+ *
* @hide
*/
public Transaction setTransparentRegionHint(SurfaceControl sc, Region transparentRegion) {
@@ -3438,6 +3478,10 @@ public final class SurfaceControl implements Parcelable {
}
/**
+ * Sets the input channel for a given SurfaceControl. The position and order of the
+ * SurfaceControl in conjunction with the touchable region in the InputWindowHandle
+ * determines the hit region.
+ *
* @hide
*/
public Transaction setInputWindowInfo(SurfaceControl sc, InputWindowHandle handle) {
@@ -3549,6 +3593,8 @@ public final class SurfaceControl implements Parcelable {
* surface. If no crop is specified and the surface has no buffer, the surface bounds is
* only constrained by the size of its parent bounds.
*
+ * To unset the crop, pass in an invalid Rect (0, 0, -1, -1)
+ *
* @param sc SurfaceControl to set crop of.
* @param crop Bounds of the crop to apply.
* @hide
@@ -3578,6 +3624,8 @@ public final class SurfaceControl implements Parcelable {
* surface. If no crop is specified and the surface has no buffer, the surface bounds is
* only constrained by the size of its parent bounds.
*
+ * To unset the crop, pass in an invalid Rect (0, 0, -1, -1)
+ *
* @param sc SurfaceControl to set crop of.
* @param crop Bounds of the crop to apply.
* @return this This transaction for chaining
@@ -3625,6 +3673,8 @@ public final class SurfaceControl implements Parcelable {
* surface. If no crop is specified and the surface has no buffer, the surface bounds is
* only constrained by the size of its parent bounds.
*
+ * To unset the crop, pass in an invalid Rect (0, 0, -1, -1)
+ *
* @param sc SurfaceControl to set crop of.
* @param crop Bounds of the crop to apply.
* @return this This transaction for chaining
@@ -3643,7 +3693,12 @@ public final class SurfaceControl implements Parcelable {
}
/**
- * Sets the corner radius of a {@link SurfaceControl}.
+ * Sets the corner radius of a {@link SurfaceControl}. This corner radius is applied to the
+ * SurfaceControl and its children. The API expects a crop to be set on the SurfaceControl
+ * to ensure that the corner radius is applied to the correct region. If the crop does not
+ * intersect with the SurfaceControl's visible content, the corner radius will not be
+ * applied.
+ *
* @param sc SurfaceControl
* @param cornerRadius Corner radius in pixels.
* @return Itself.
@@ -3753,6 +3808,9 @@ public final class SurfaceControl implements Parcelable {
}
/**
+ * Associates a layer with a display. The layer will be drawn on the display with the
+ * specified layer stack. If the layer is not a root layer, this call has no effect.
+ *
* @hide
*/
@UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.O)
@@ -3791,6 +3849,7 @@ public final class SurfaceControl implements Parcelable {
/**
* Fills the surface with the specified color.
+ *
* @param color A float array with three values to represent r, g, b in range [0..1]. An
* invalid color will remove the color fill.
* @hide
@@ -3809,8 +3868,9 @@ public final class SurfaceControl implements Parcelable {
/**
* Removes color fill.
- * @hide
- */
+ *
+ * @hide
+ */
public Transaction unsetColor(SurfaceControl sc) {
checkPreconditions(sc);
if (SurfaceControlRegistry.sCallStackDebuggingEnabled) {
@@ -3898,6 +3958,8 @@ public final class SurfaceControl implements Parcelable {
}
/**
+ * Sets the surface to render contents of the display to.
+ *
* @hide
*/
public Transaction setDisplaySurface(IBinder displayToken, Surface surface) {
@@ -3916,6 +3978,9 @@ public final class SurfaceControl implements Parcelable {
}
/**
+ * Sets the layer stack of the display.
+ *
+ * All layers with the same layer stack will be drawn on this display.
* @hide
*/
public Transaction setDisplayLayerStack(IBinder displayToken, int layerStack) {
diff --git a/core/java/android/view/SurfaceView.java b/core/java/android/view/SurfaceView.java
index dd32947c69e4..b98f4db5dce6 100644
--- a/core/java/android/view/SurfaceView.java
+++ b/core/java/android/view/SurfaceView.java
@@ -16,6 +16,7 @@
package android.view;
+import static android.view.flags.Flags.FLAG_DEPRECATE_SURFACE_VIEW_Z_ORDER_APIS;
import static android.view.flags.Flags.FLAG_SURFACE_VIEW_GET_SURFACE_PACKAGE;
import static android.view.flags.Flags.FLAG_SURFACE_VIEW_SET_COMPOSITION_ORDER;
import static android.view.WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON;
@@ -812,7 +813,12 @@ public class SurfaceView extends View implements ViewRootImpl.SurfaceChangedCall
* window is attached to the window manager.
*
* <p>Calling this overrides any previous call to {@link #setZOrderOnTop}.
+ *
+ * @deprecated Use {@link #setCompositionOrder(int)} instead. It provides more
+ * control over the Z ordering behavior.
*/
+ @Deprecated
+ @FlaggedApi(FLAG_DEPRECATE_SURFACE_VIEW_Z_ORDER_APIS)
public void setZOrderMediaOverlay(boolean isMediaOverlay) {
mRequestedSubLayer = isMediaOverlay
? APPLICATION_MEDIA_OVERLAY_SUBLAYER : APPLICATION_MEDIA_SUBLAYER;
@@ -834,7 +840,12 @@ public class SurfaceView extends View implements ViewRootImpl.SurfaceChangedCall
* <p>Calling this overrides any previous call to {@link #setZOrderMediaOverlay}.
*
* @param onTop Whether to show the surface on top of this view's window.
+ *
+ * @deprecated Use {@link #setCompositionOrder(int)} instead. It provides more
+ * control over the Z ordering behavior.
*/
+ @Deprecated
+ @FlaggedApi(FLAG_DEPRECATE_SURFACE_VIEW_Z_ORDER_APIS)
public void setZOrderOnTop(boolean onTop) {
// In R and above we allow dynamic layer changes.
final boolean allowDynamicChange = getContext().getApplicationInfo().targetSdkVersion
@@ -866,7 +877,11 @@ public class SurfaceView extends View implements ViewRootImpl.SurfaceChangedCall
* @return Whether the Z ordering changed.
*
* @hide
+ *
+ * @deprecated Use {@link #setCompositionOrder(int)} instead. It provides more control
+ * over the Z ordering behavior.
*/
+ @Deprecated
public boolean setZOrderedOnTop(boolean onTop, boolean allowDynamicChange) {
final int subLayer;
if (onTop) {
diff --git a/core/java/android/view/WindowInsets.java b/core/java/android/view/WindowInsets.java
index e3ea6b229d64..a952967f4daf 100644
--- a/core/java/android/view/WindowInsets.java
+++ b/core/java/android/view/WindowInsets.java
@@ -1290,7 +1290,8 @@ public final class WindowInsets {
int newTop = Math.max(0, insets.top - top);
int newRight = Math.max(0, insets.right - right);
int newBottom = Math.max(0, insets.bottom - bottom);
- if (newLeft == left && newTop == top && newRight == right && newBottom == bottom) {
+ if (newLeft == insets.left && newTop == insets.top
+ && newRight == insets.right && newBottom == insets.bottom) {
return insets;
}
return Insets.of(newLeft, newTop, newRight, newBottom);
diff --git a/core/java/android/view/flags/view_flags.aconfig b/core/java/android/view/flags/view_flags.aconfig
index f6fdec94c332..d06f885638b6 100644
--- a/core/java/android/view/flags/view_flags.aconfig
+++ b/core/java/android/view/flags/view_flags.aconfig
@@ -129,6 +129,14 @@ flag {
}
flag {
+ name: "deprecate_surface_view_z_order_apis"
+ namespace: "window_surfaces"
+ description: "Deprecate SurfaceView z order control APIs."
+ bug: "341021569"
+ is_fixed_read_only: true
+}
+
+flag {
name: "use_refactored_round_scrollbar"
namespace: "wear_frameworks"
description: "Use refactored round scrollbar."
diff --git a/core/java/android/window/DesktopModeFlags.java b/core/java/android/window/DesktopModeFlags.java
index b44620f12bbb..4aeedbb72903 100644
--- a/core/java/android/window/DesktopModeFlags.java
+++ b/core/java/android/window/DesktopModeFlags.java
@@ -102,9 +102,15 @@ public enum DesktopModeFlags {
ENABLE_HOLD_TO_DRAG_APP_HANDLE(Flags::enableHoldToDragAppHandle, true),
ENABLE_MINIMIZE_BUTTON(Flags::enableMinimizeButton, true),
ENABLE_MODALS_FULLSCREEN_WITH_PERMISSIONS(Flags::enableModalsFullscreenWithPermission, false),
+ ENABLE_OPAQUE_BACKGROUND_FOR_TRANSPARENT_WINDOWS(
+ Flags::enableOpaqueBackgroundForTransparentWindows, false),
+ ENABLE_QUICKSWITCH_DESKTOP_SPLIT_BUGFIX(Flags::enableQuickswitchDesktopSplitBugfix, true),
ENABLE_RESIZING_METRICS(Flags::enableResizingMetrics, true),
ENABLE_RESTORE_TO_PREVIOUS_SIZE_FROM_DESKTOP_IMMERSIVE(
Flags::enableRestoreToPreviousSizeFromDesktopImmersive, true),
+ ENABLE_START_LAUNCH_TRANSITION_FROM_TASKBAR_BUGFIX(
+ Flags::enableStartLaunchTransitionFromTaskbarBugfix, true),
+ ENABLE_TASKBAR_OVERFLOW(Flags::enableTaskbarOverflow, false),
ENABLE_TASK_RESIZING_KEYBOARD_SHORTCUTS(Flags::enableTaskResizingKeyboardShortcuts, true),
ENABLE_TASK_STACK_OBSERVER_IN_SHELL(Flags::enableTaskStackObserverInShell, true),
ENABLE_THEMED_APP_HEADERS(Flags::enableThemedAppHeaders, true),
@@ -117,6 +123,8 @@ public enum DesktopModeFlags {
ENABLE_WINDOWING_TRANSITION_HANDLERS_OBSERVERS(
Flags::enableWindowingTransitionHandlersObservers, false),
EXCLUDE_CAPTION_FROM_APP_BOUNDS(Flags::excludeCaptionFromAppBounds, false),
+ IGNORE_ASPECT_RATIO_RESTRICTIONS_FOR_RESIZEABLE_FREEFORM_ACTIVITIES(
+ Flags::ignoreAspectRatioRestrictionsForResizeableFreeformActivities, true),
INCLUDE_TOP_TRANSPARENT_FULLSCREEN_TASK_IN_DESKTOP_HEURISTIC(
Flags::includeTopTransparentFullscreenTaskInDesktopHeuristic, true)
// go/keep-sorted end
diff --git a/core/java/android/window/TransitionFilter.java b/core/java/android/window/TransitionFilter.java
index 61fc6226f822..d8d20119a602 100644
--- a/core/java/android/window/TransitionFilter.java
+++ b/core/java/android/window/TransitionFilter.java
@@ -31,8 +31,6 @@ import android.os.Parcel;
import android.os.Parcelable;
import android.view.WindowManager;
-import com.android.window.flags.Flags;
-
/**
* A parcelable filter that can be used for rerouting transitions to a remote. This is a local
* representation so that the transition system doesn't need to make blocking queries over
@@ -261,9 +259,7 @@ public final class TransitionFilter implements Parcelable {
// only applies to activity/task
&& (change.getTaskInfo() != null
|| change.getActivityComponent() != null)) {
- final TransitionInfo.AnimationOptions opts =
- Flags.moveAnimationOptionsToChange() ? change.getAnimationOptions()
- : info.getAnimationOptions();
+ final TransitionInfo.AnimationOptions opts = change.getAnimationOptions();
if (opts != null) {
boolean canActuallyOverride = change.getTaskInfo() == null
|| opts.getOverrideTaskTransition();
diff --git a/core/java/android/window/TransitionInfo.java b/core/java/android/window/TransitionInfo.java
index 4f34aa36a204..32175f122d61 100644
--- a/core/java/android/window/TransitionInfo.java
+++ b/core/java/android/window/TransitionInfo.java
@@ -57,8 +57,6 @@ import android.view.Surface;
import android.view.SurfaceControl;
import android.view.WindowManager;
-import com.android.window.flags.Flags;
-
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.util.ArrayList;
@@ -220,10 +218,6 @@ public final class TransitionInfo implements Parcelable {
private final ArrayList<Change> mChanges = new ArrayList<>();
private final ArrayList<Root> mRoots = new ArrayList<>();
- // TODO(b/327332488): Clean-up usages after the flag is fully enabled.
- @Deprecated
- private AnimationOptions mOptions;
-
/** This is only a BEST-EFFORT id used for log correlation. DO NOT USE for any real work! */
private int mDebugId = -1;
@@ -238,7 +232,6 @@ public final class TransitionInfo implements Parcelable {
mFlags = in.readInt();
in.readTypedList(mChanges, Change.CREATOR);
in.readTypedList(mRoots, Root.CREATOR);
- mOptions = in.readTypedObject(AnimationOptions.CREATOR);
mDebugId = in.readInt();
mTrack = in.readInt();
}
@@ -250,7 +243,6 @@ public final class TransitionInfo implements Parcelable {
dest.writeInt(mFlags);
dest.writeTypedList(mChanges);
dest.writeTypedList(mRoots, flags);
- dest.writeTypedObject(mOptions, flags);
dest.writeInt(mDebugId);
dest.writeInt(mTrack);
}
@@ -286,18 +278,6 @@ public final class TransitionInfo implements Parcelable {
mRoots.add(other);
}
- /**
- * @deprecated Set {@link AnimationOptions} to change. This method is only used if
- * {@link Flags#FLAG_MOVE_ANIMATION_OPTIONS_TO_CHANGE} is disabled.
- */
- @Deprecated
- public void setAnimationOptions(@Nullable AnimationOptions options) {
- if (Flags.moveAnimationOptionsToChange()) {
- return;
- }
- mOptions = options;
- }
-
public @TransitionType int getType() {
return mType;
}
@@ -360,16 +340,6 @@ public final class TransitionInfo implements Parcelable {
}
/**
- * @deprecated Use {@link Change#getAnimationOptions()} instead. This method is called only
- * if {@link Flags#FLAG_MOVE_ANIMATION_OPTIONS_TO_CHANGE} is disabled.
- */
- @Deprecated
- @Nullable
- public AnimationOptions getAnimationOptions() {
- return mOptions;
- }
-
- /**
* @return the list of {@link Change}s in this transition. The list is sorted top-to-bottom
* in Z (meaning index 0 is the top-most container).
*/
@@ -455,9 +425,6 @@ public final class TransitionInfo implements Parcelable {
StringBuilder sb = new StringBuilder();
sb.append("{id=").append(mDebugId).append(" t=").append(transitTypeToString(mType))
.append(" f=0x").append(Integer.toHexString(mFlags)).append(" trk=").append(mTrack);
- if (mOptions != null) {
- sb.append(" opt=").append(mOptions);
- }
sb.append(" r=[");
for (int i = 0; i < mRoots.size(); ++i) {
if (i > 0) {
@@ -656,8 +623,6 @@ public final class TransitionInfo implements Parcelable {
for (int i = 0; i < mRoots.size(); ++i) {
out.mRoots.add(mRoots.get(i).localRemoteCopy());
}
- // Doesn't have any native stuff, so no need for actual copy
- out.mOptions = mOptions;
return out;
}
@@ -860,9 +825,6 @@ public final class TransitionInfo implements Parcelable {
* Sets {@link AnimationOptions} to override animation.
*/
public void setAnimationOptions(@Nullable AnimationOptions options) {
- if (!Flags.moveAnimationOptionsToChange()) {
- return;
- }
mAnimationOptions = options;
}
diff --git a/core/java/android/window/flags/lse_desktop_experience.aconfig b/core/java/android/window/flags/lse_desktop_experience.aconfig
index 3e3b8e100d6d..f77a36676b3d 100644
--- a/core/java/android/window/flags/lse_desktop_experience.aconfig
+++ b/core/java/android/window/flags/lse_desktop_experience.aconfig
@@ -681,6 +681,16 @@ flag {
}
flag {
+ name: "enable_opaque_background_for_transparent_windows"
+ namespace: "lse_desktop_experience"
+ description: "Whether an opaque background should be forcefully set for windows with only transparent background."
+ bug: "397219542"
+ metadata {
+ purpose: PURPOSE_BUGFIX
+ }
+}
+
+flag {
name: "enable_desktop_mode_through_dev_option"
namespace: "lse_desktop_experience"
description: "Enables support for desktop mode through developer options for devices eligible for desktop mode."
@@ -705,6 +715,13 @@ flag {
}
flag {
+ name: "enable_activity_embedding_support_for_connected_displays"
+ namespace: "lse_desktop_experience"
+ description: "Enables activity embedding support for connected displays, including enabling AE optimization for Settings."
+ bug: "369438353"
+}
+
+flag {
name: "enable_full_screen_window_on_removing_split_screen_stage_bugfix"
namespace: "lse_desktop_experience"
description: "Enables clearing the windowing mode of a freeform window when removing the task from the split screen stage."
@@ -764,3 +781,17 @@ flag {
purpose: PURPOSE_BUGFIX
}
}
+
+flag {
+ name: "enable_taskbar_overflow"
+ namespace: "lse_desktop_experience"
+ description: "Show recent apps in the taskbar overflow."
+ bug: "368119679"
+}
+
+flag {
+ name: "enable_projected_display_desktop_mode"
+ namespace: "lse_desktop_experience"
+ description: "Enable Desktop Mode on Projected Mode devices but constrained to the external display."
+ bug: "384568161"
+} \ No newline at end of file
diff --git a/core/java/android/window/flags/windowing_sdk.aconfig b/core/java/android/window/flags/windowing_sdk.aconfig
index 54d0eeffc9bb..6e45d3dc659f 100644
--- a/core/java/android/window/flags/windowing_sdk.aconfig
+++ b/core/java/android/window/flags/windowing_sdk.aconfig
@@ -62,16 +62,6 @@ flag {
flag {
namespace: "windowing_sdk"
- name: "move_animation_options_to_change"
- description: "Move AnimationOptions from TransitionInfo to each Change"
- bug: "327332488"
- metadata {
- purpose: PURPOSE_BUGFIX
- }
-}
-
-flag {
- namespace: "windowing_sdk"
name: "rear_display_disable_force_desktop_system_decorations"
description: "Block system decorations from being added to a rear display when desktop mode is forced"
bug: "346103150"
diff --git a/core/java/com/android/internal/app/MediaRouteControllerContentManager.java b/core/java/com/android/internal/app/MediaRouteControllerContentManager.java
index dc4caa3d35d7..3a8b94f222ba 100644
--- a/core/java/com/android/internal/app/MediaRouteControllerContentManager.java
+++ b/core/java/com/android/internal/app/MediaRouteControllerContentManager.java
@@ -17,7 +17,12 @@
package com.android.internal.app;
import android.content.Context;
+import android.content.res.TypedArray;
+import android.graphics.drawable.AnimationDrawable;
+import android.graphics.drawable.Drawable;
+import android.graphics.drawable.StateListDrawable;
import android.media.MediaRouter;
+import android.util.TypedValue;
import android.view.View;
import android.widget.LinearLayout;
import android.widget.SeekBar;
@@ -35,9 +40,14 @@ public class MediaRouteControllerContentManager {
*/
public interface Delegate {
/**
- * Updates the title of the cast device
+ * Updates the title of the media route device
*/
- void setCastDeviceTitle(CharSequence title);
+ void setMediaRouteDeviceTitle(CharSequence title);
+
+ /**
+ * Updates the icon of the media route device
+ */
+ void setMediaRouteDeviceIcon(Drawable icon);
/**
* Dismiss the UI to transition to a different workflow.
@@ -45,6 +55,7 @@ public class MediaRouteControllerContentManager {
void dismissView();
}
+ private final Context mContext;
private final Delegate mDelegate;
// Time to wait before updating the volume when the user lets go of the seek bar
@@ -53,15 +64,25 @@ public class MediaRouteControllerContentManager {
private static final int VOLUME_UPDATE_DELAY_MILLIS = 250;
private final MediaRouter mRouter;
+ private final MediaRouteControllerContentManager.MediaRouterCallback mCallback;
private final MediaRouter.RouteInfo mRoute;
+ private Drawable mMediaRouteButtonDrawable;
+ private final int[] mMediaRouteConnectingState = { R.attr.state_checked, R.attr.state_enabled };
+ private final int[] mMediaRouteOnState = { R.attr.state_activated, R.attr.state_enabled };
+ private Drawable mCurrentIconDrawable;
+
+ private boolean mAttachedToWindow;
+
private LinearLayout mVolumeLayout;
private SeekBar mVolumeSlider;
private boolean mVolumeSliderTouched;
public MediaRouteControllerContentManager(Context context, Delegate delegate) {
+ mContext = context;
mDelegate = delegate;
mRouter = context.getSystemService(MediaRouter.class);
+ mCallback = new MediaRouteControllerContentManager.MediaRouterCallback();
mRoute = mRouter.getSelectedRoute();
}
@@ -70,7 +91,7 @@ public class MediaRouteControllerContentManager {
* given container view.
*/
public void bindViews(View containerView) {
- mDelegate.setCastDeviceTitle(mRoute.getName());
+ mDelegate.setMediaRouteDeviceTitle(mRoute.getName());
mVolumeLayout = containerView.findViewById(R.id.media_route_volume_layout);
mVolumeSlider = containerView.findViewById(R.id.media_route_volume_slider);
mVolumeSlider.setOnSeekBarChangeListener(new SeekBar.OnSeekBarChangeListener() {
@@ -108,20 +129,58 @@ public class MediaRouteControllerContentManager {
}
}
});
+
+ mMediaRouteButtonDrawable = obtainMediaRouteButtonDrawable();
+ }
+
+ /**
+ * Called when this UI is attached to a window..
+ */
+ public void onAttachedToWindow() {
+ mAttachedToWindow = true;
+ mRouter.addCallback(0, mCallback, MediaRouter.CALLBACK_FLAG_UNFILTERED_EVENTS);
+ update();
+ }
+
+ /**
+ * Called when this UI is detached from a window..
+ */
+ public void onDetachedFromWindow() {
+ mRouter.removeCallback(mCallback);
+ mAttachedToWindow = false;
}
/**
* Updates all the views to reflect new states.
*/
public void update() {
- mDelegate.setCastDeviceTitle(mRoute.getName());
+ if (!mRoute.isSelected() || mRoute.isDefault()) {
+ mDelegate.dismissView();
+ }
+
+ mDelegate.setMediaRouteDeviceTitle(mRoute.getName());
updateVolume();
+
+ Drawable icon = getIconDrawable();
+ if (icon != mCurrentIconDrawable) {
+ mCurrentIconDrawable = icon;
+ if (icon instanceof AnimationDrawable animDrawable) {
+ if (!mAttachedToWindow && !mRoute.isConnecting()) {
+ // When the route is already connected before the view is attached, show the
+ // last frame of the connected animation immediately.
+ if (animDrawable.isRunning()) {
+ animDrawable.stop();
+ }
+ icon = animDrawable.getFrame(animDrawable.getNumberOfFrames() - 1);
+ } else if (!animDrawable.isRunning()) {
+ animDrawable.start();
+ }
+ }
+ mDelegate.setMediaRouteDeviceIcon(icon);
+ }
}
- /**
- * Updates the volume layout and slider.
- */
- public void updateVolume() {
+ private void updateVolume() {
if (!mVolumeSliderTouched) {
if (isVolumeControlAvailable()) {
mVolumeLayout.setVisibility(View.VISIBLE);
@@ -150,4 +209,61 @@ public class MediaRouteControllerContentManager {
private boolean isVolumeControlAvailable() {
return mRoute.getVolumeHandling() == MediaRouter.RouteInfo.PLAYBACK_VOLUME_VARIABLE;
}
+
+ private Drawable obtainMediaRouteButtonDrawable() {
+ TypedValue value = new TypedValue();
+ if (!mContext.getTheme().resolveAttribute(R.attr.mediaRouteButtonStyle, value, true)) {
+ return null;
+ }
+ int[] drawableAttrs = new int[] { R.attr.externalRouteEnabledDrawable };
+ TypedArray a = mContext.obtainStyledAttributes(value.data, drawableAttrs);
+ Drawable drawable = a.getDrawable(0);
+ a.recycle();
+ return drawable;
+ }
+
+ private Drawable getIconDrawable() {
+ if (!(mMediaRouteButtonDrawable instanceof StateListDrawable)) {
+ return mMediaRouteButtonDrawable;
+ } else if (mRoute.isConnecting()) {
+ StateListDrawable stateListDrawable = (StateListDrawable) mMediaRouteButtonDrawable;
+ stateListDrawable.setState(mMediaRouteConnectingState);
+ return stateListDrawable.getCurrent();
+ } else {
+ StateListDrawable stateListDrawable = (StateListDrawable) mMediaRouteButtonDrawable;
+ stateListDrawable.setState(mMediaRouteOnState);
+ return stateListDrawable.getCurrent();
+ }
+ }
+
+ private final class MediaRouterCallback extends MediaRouter.SimpleCallback {
+ @Override
+ public void onRouteUnselected(MediaRouter router, int type, MediaRouter.RouteInfo info) {
+ update();
+ }
+
+ @Override
+ public void onRouteChanged(MediaRouter router, MediaRouter.RouteInfo route) {
+ update();
+ }
+
+ @Override
+ public void onRouteVolumeChanged(MediaRouter router, MediaRouter.RouteInfo route) {
+ if (route == mRoute) {
+ updateVolume();
+ }
+ }
+
+ @Override
+ public void onRouteGrouped(MediaRouter router, MediaRouter.RouteInfo info,
+ MediaRouter.RouteGroup group, int index) {
+ update();
+ }
+
+ @Override
+ public void onRouteUngrouped(MediaRouter router, MediaRouter.RouteInfo info,
+ MediaRouter.RouteGroup group) {
+ update();
+ }
+ }
}
diff --git a/core/java/com/android/internal/app/MediaRouteControllerDialog.java b/core/java/com/android/internal/app/MediaRouteControllerDialog.java
index 45a4a13667a4..5899963f4550 100644
--- a/core/java/com/android/internal/app/MediaRouteControllerDialog.java
+++ b/core/java/com/android/internal/app/MediaRouteControllerDialog.java
@@ -21,15 +21,9 @@ import android.app.MediaRouteActionProvider;
import android.app.MediaRouteButton;
import android.content.Context;
import android.content.res.Resources;
-import android.content.res.TypedArray;
-import android.graphics.drawable.AnimationDrawable;
import android.graphics.drawable.Drawable;
-import android.graphics.drawable.StateListDrawable;
import android.media.MediaRouter;
-import android.media.MediaRouter.RouteGroup;
-import android.media.MediaRouter.RouteInfo;
import android.os.Bundle;
-import android.util.TypedValue;
import android.view.KeyEvent;
import android.view.View;
@@ -48,19 +42,11 @@ import com.android.internal.R;
*/
public class MediaRouteControllerDialog extends AlertDialog implements
MediaRouteControllerContentManager.Delegate {
- // TODO(b/360050020): Eventually these 3 variables should be in the content manager instead of
+ // TODO(b/360050020): Eventually these 2 variables should be in the content manager instead of
// here. So these should be removed when the migration is completed.
private final MediaRouter mRouter;
- private final MediaRouterCallback mCallback;
private final MediaRouter.RouteInfo mRoute;
- private Drawable mMediaRouteButtonDrawable;
- private int[] mMediaRouteConnectingState = { R.attr.state_checked, R.attr.state_enabled };
- private int[] mMediaRouteOnState = { R.attr.state_activated, R.attr.state_enabled };
- private Drawable mCurrentIconDrawable;
-
- private boolean mAttachedToWindow;
-
private final MediaRouteControllerContentManager mContentManager;
public MediaRouteControllerDialog(Context context, int theme) {
@@ -68,7 +54,6 @@ public class MediaRouteControllerDialog extends AlertDialog implements
mContentManager = new MediaRouteControllerContentManager(context, this);
mRouter = (MediaRouter) context.getSystemService(Context.MEDIA_ROUTER_SERVICE);
- mCallback = new MediaRouterCallback();
mRoute = mRouter.getSelectedRoute();
}
@@ -87,24 +72,18 @@ public class MediaRouteControllerDialog extends AlertDialog implements
customPanelView.setMinimumHeight(0);
}
- mMediaRouteButtonDrawable = obtainMediaRouteButtonDrawable();
- update();
+ mContentManager.update();
}
@Override
public void onAttachedToWindow() {
super.onAttachedToWindow();
- mAttachedToWindow = true;
-
- mRouter.addCallback(0, mCallback, MediaRouter.CALLBACK_FLAG_UNFILTERED_EVENTS);
- update();
+ mContentManager.onAttachedToWindow();
}
@Override
public void onDetachedFromWindow() {
- mRouter.removeCallback(mCallback);
- mAttachedToWindow = false;
-
+ mContentManager.onDetachedFromWindow();
super.onDetachedFromWindow();
}
@@ -128,95 +107,17 @@ public class MediaRouteControllerDialog extends AlertDialog implements
}
@Override
- public void setCastDeviceTitle(CharSequence title) {
+ public void setMediaRouteDeviceTitle(CharSequence title) {
setTitle(title);
}
@Override
- public void dismissView() {
- dismiss();
- }
-
- private void update() {
- if (!mRoute.isSelected() || mRoute.isDefault()) {
- dismissView();
- }
-
- mContentManager.update();
-
- Drawable icon = getIconDrawable();
- if (icon != mCurrentIconDrawable) {
- mCurrentIconDrawable = icon;
- if (icon instanceof AnimationDrawable animDrawable) {
- if (!mAttachedToWindow && !mRoute.isConnecting()) {
- // When the route is already connected before the view is attached, show the
- // last frame of the connected animation immediately.
- if (animDrawable.isRunning()) {
- animDrawable.stop();
- }
- icon = animDrawable.getFrame(animDrawable.getNumberOfFrames() - 1);
- } else if (!animDrawable.isRunning()) {
- animDrawable.start();
- }
- }
- setIcon(icon);
- }
- }
-
- private Drawable obtainMediaRouteButtonDrawable() {
- Context context = getContext();
- TypedValue value = new TypedValue();
- if (!context.getTheme().resolveAttribute(R.attr.mediaRouteButtonStyle, value, true)) {
- return null;
- }
- int[] drawableAttrs = new int[] { R.attr.externalRouteEnabledDrawable };
- TypedArray a = context.obtainStyledAttributes(value.data, drawableAttrs);
- Drawable drawable = a.getDrawable(0);
- a.recycle();
- return drawable;
- }
-
- private Drawable getIconDrawable() {
- if (!(mMediaRouteButtonDrawable instanceof StateListDrawable)) {
- return mMediaRouteButtonDrawable;
- } else if (mRoute.isConnecting()) {
- StateListDrawable stateListDrawable = (StateListDrawable) mMediaRouteButtonDrawable;
- stateListDrawable.setState(mMediaRouteConnectingState);
- return stateListDrawable.getCurrent();
- } else {
- StateListDrawable stateListDrawable = (StateListDrawable) mMediaRouteButtonDrawable;
- stateListDrawable.setState(mMediaRouteOnState);
- return stateListDrawable.getCurrent();
- }
+ public void setMediaRouteDeviceIcon(Drawable icon) {
+ setIcon(icon);
}
- private final class MediaRouterCallback extends MediaRouter.SimpleCallback {
- @Override
- public void onRouteUnselected(MediaRouter router, int type, RouteInfo info) {
- update();
- }
-
- @Override
- public void onRouteChanged(MediaRouter router, MediaRouter.RouteInfo route) {
- update();
- }
-
- @Override
- public void onRouteVolumeChanged(MediaRouter router, MediaRouter.RouteInfo route) {
- if (route == mRoute) {
- mContentManager.updateVolume();
- }
- }
-
- @Override
- public void onRouteGrouped(MediaRouter router, RouteInfo info, RouteGroup group,
- int index) {
- update();
- }
-
- @Override
- public void onRouteUngrouped(MediaRouter router, RouteInfo info, RouteGroup group) {
- update();
- }
+ @Override
+ public void dismissView() {
+ dismiss();
}
}
diff --git a/core/java/com/android/internal/jank/FrameTracker.java b/core/java/com/android/internal/jank/FrameTracker.java
index 454323b60333..a0f45b082a36 100644
--- a/core/java/com/android/internal/jank/FrameTracker.java
+++ b/core/java/com/android/internal/jank/FrameTracker.java
@@ -257,15 +257,11 @@ public class FrameTracker implements HardwareRendererObserver.OnFrameMetricsAvai
@Override
public void surfaceDestroyed() {
-
- // Wait a while to give the system a chance for the remaining
- // frames to arrive, then force finish the session.
- mHandler.postDelayed(() -> {
+ mHandler.post(() -> {
if (!mMetricsFinalized) {
end(REASON_END_SURFACE_DESTROYED);
- finish();
}
- }, 50);
+ });
}
};
// This callback has a reference to FrameTracker,
@@ -367,9 +363,9 @@ public class FrameTracker implements HardwareRendererObserver.OnFrameMetricsAvai
// Send a flush jank data transaction.
if (mSurfaceControl != null && mSurfaceControl.isValid()) {
SurfaceControl.Transaction.sendSurfaceFlushJankData(mSurfaceControl);
- if (mJankDataListenerRegistration != null) {
- mJankDataListenerRegistration.flush();
- }
+ }
+ if (mJankDataListenerRegistration != null) {
+ mJankDataListenerRegistration.flush();
}
long delay;
@@ -650,6 +646,8 @@ public class FrameTracker implements HardwareRendererObserver.OnFrameMetricsAvai
Log.w(TAG, "Missing SF jank callback for vsyncId: " + info.frameVsyncId
+ ", CUJ=" + name);
}
+ } else if (Flags.useSfFrameDuration() && info.surfaceControlCallbackFired) {
+ maxFrameTimeNanos = Math.max(info.totalDurationNanos, maxFrameTimeNanos);
}
}
maxSuccessiveMissedFramesCount = Math.max(
diff --git a/core/java/com/android/internal/os/LongArrayMultiStateCounter.java b/core/java/com/android/internal/os/LongArrayMultiStateCounter.java
index fe616e085488..5e9c87a5154a 100644
--- a/core/java/com/android/internal/os/LongArrayMultiStateCounter.java
+++ b/core/java/com/android/internal/os/LongArrayMultiStateCounter.java
@@ -16,6 +16,7 @@
package com.android.internal.os;
+import android.annotation.CheckResult;
import android.annotation.Nullable;
import android.os.Parcel;
import android.os.Parcelable;
@@ -196,7 +197,8 @@ public final class LongArrayMultiStateCounter implements Parcelable {
/**
* Populates the array with the accumulated counts for the specified state.
*/
- public void getCounts(long[] counts, int state) {
+ @CheckResult
+ public boolean getCounts(long[] counts, int state) {
if (state < 0 || state >= mStateCount) {
throw new IllegalArgumentException(
"State: " + state + ", outside the range: [0-" + mStateCount + "]");
@@ -205,7 +207,7 @@ public final class LongArrayMultiStateCounter implements Parcelable {
throw new IllegalArgumentException(
"Invalid array length: " + counts.length + ", expected: " + mLength);
}
- native_getCounts(mNativeObject, counts, state);
+ return native_getCounts(mNativeObject, counts, state);
}
@Override
@@ -282,7 +284,8 @@ public final class LongArrayMultiStateCounter implements Parcelable {
@FastNative
@RavenwoodRedirect
- private static native void native_getCounts(long nativeObject, long[] counts, int state);
+ @CheckResult
+ private static native boolean native_getCounts(long nativeObject, long[] counts, int state);
@FastNative
@RavenwoodRedirect
diff --git a/core/java/com/android/internal/os/LongArrayMultiStateCounter_ravenwood.java b/core/java/com/android/internal/os/LongArrayMultiStateCounter_ravenwood.java
index 4f5f37d0640e..403e8c1be8fa 100644
--- a/core/java/com/android/internal/os/LongArrayMultiStateCounter_ravenwood.java
+++ b/core/java/com/android/internal/os/LongArrayMultiStateCounter_ravenwood.java
@@ -168,8 +168,20 @@ class LongArrayMultiStateCounter_ravenwood {
}
}
- public void getValues(long[] values, int state) {
- System.arraycopy(mStates[state].mCounter, 0, values, 0, mArrayLength);
+ public boolean getValues(long[] values, int state) {
+ long[] counts = mStates[state].mCounter;
+ boolean allZeros = true;
+ for (int i = 0; i < counts.length; i++) {
+ if (counts[i] != 0) {
+ allZeros = false;
+ break;
+ }
+ }
+ if (allZeros) {
+ return false;
+ }
+ System.arraycopy(counts, 0, values, 0, mArrayLength);
+ return true;
}
public void reset() {
@@ -316,8 +328,8 @@ class LongArrayMultiStateCounter_ravenwood {
getInstance(instanceId).addCounts(counts);
}
- public static void native_getCounts(long instanceId, long[] counts, int state) {
- getInstance(instanceId).getValues(counts, state);
+ public static boolean native_getCounts(long instanceId, long[] counts, int state) {
+ return getInstance(instanceId).getValues(counts, state);
}
public static void native_reset(long instanceId) {
diff --git a/core/java/com/android/internal/pm/pkg/component/AconfigFlags.java b/core/java/com/android/internal/pm/pkg/component/AconfigFlags.java
index b57acf3d97fd..b19967a47001 100644
--- a/core/java/com/android/internal/pm/pkg/component/AconfigFlags.java
+++ b/core/java/com/android/internal/pm/pkg/component/AconfigFlags.java
@@ -261,7 +261,12 @@ public class AconfigFlags {
// Default value is false for when the flag is not found.
// Note: Unlike with the old storage, with AconfigPackage, we don't have a way to
// know if the flag is not found or if it's found but the value is false.
- value = aconfigPackage.getBooleanFlagValue(flagName, false);
+ try {
+ value = aconfigPackage.getBooleanFlagValue(flagName, false);
+ } catch (Exception e) {
+ Slog.e(LOG_TAG, "Failed to read Aconfig flag value for " + flagPackageAndName, e);
+ return null;
+ }
}
if (DEBUG) {
Slog.v(LOG_TAG, "Aconfig flag value for " + flagPackageAndName + " = " + value);
diff --git a/core/java/com/android/internal/widget/ConversationLayout.java b/core/java/com/android/internal/widget/ConversationLayout.java
index 641ecc9b675a..ce46da12aa76 100644
--- a/core/java/com/android/internal/widget/ConversationLayout.java
+++ b/core/java/com/android/internal/widget/ConversationLayout.java
@@ -250,7 +250,8 @@ public class ConversationLayout extends FrameLayout
mPeopleHelper.animateViewForceHidden(mImportanceRingView, forceHidden);
mPeopleHelper.animateViewForceHidden(mIcon, forceHidden);
});
- mConversationText = findViewById(R.id.conversation_text);
+ mConversationText = findViewById(notificationsRedesignTemplates()
+ ? R.id.title : R.id.conversation_text);
mExpandButtonContainer = findViewById(R.id.expand_button_container);
mExpandButtonContainerA11yContainer =
findViewById(R.id.expand_button_a11y_container);
@@ -716,17 +717,10 @@ public class ConversationLayout extends FrameLayout
}
private void updateImageMessages() {
- View newMessage = null;
- if (mIsCollapsed && !mGroups.isEmpty()) {
-
- // When collapsed, we're displaying the image message in a dedicated container
- // on the right of the layout instead of inline. Let's add the isolated image there
- MessagingGroup messagingGroup = mGroups.getLast();
- MessagingImageMessage isolatedMessage = messagingGroup.getIsolatedMessage();
- if (isolatedMessage != null) {
- newMessage = isolatedMessage.getView();
- }
+ if (mImageMessageContainer == null) {
+ return;
}
+ View newMessage = getNewImageMessage();
// Remove all messages that don't belong into the image layout
View previousMessage = mImageMessageContainer.getChildAt(0);
if (previousMessage != newMessage) {
@@ -738,6 +732,20 @@ public class ConversationLayout extends FrameLayout
mImageMessageContainer.setVisibility(newMessage != null ? VISIBLE : GONE);
}
+ @Nullable
+ private View getNewImageMessage() {
+ if (mIsCollapsed && !mGroups.isEmpty()) {
+ // When collapsed, we're displaying the image message in a dedicated container
+ // on the right of the layout instead of inline. Let's add the isolated image there
+ MessagingGroup messagingGroup = mGroups.getLast();
+ MessagingImageMessage isolatedMessage = messagingGroup.getIsolatedMessage();
+ if (isolatedMessage != null) {
+ return isolatedMessage.getView();
+ }
+ }
+ return null;
+ }
+
public void bindFacePile(ImageView bottomBackground, ImageView bottomView, ImageView topView) {
applyNotificationBackgroundColor(bottomBackground);
// Let's find the two last conversations:
@@ -841,6 +849,10 @@ public class ConversationLayout extends FrameLayout
}
private void updateAppName() {
+ if (notificationsRedesignTemplates()) {
+ return;
+ }
+
mAppName.setVisibility(mIsCollapsed ? GONE : VISIBLE);
}
@@ -1533,6 +1545,10 @@ public class ConversationLayout extends FrameLayout
}
private void updateExpandButton() {
+ if (notificationsRedesignTemplates()) {
+ return;
+ }
+
int buttonGravity;
ViewGroup newContainer;
if (mIsCollapsed) {
@@ -1565,6 +1581,10 @@ public class ConversationLayout extends FrameLayout
}
private void updateContentEndPaddings() {
+ if (notificationsRedesignTemplates()) {
+ return;
+ }
+
// Let's make sure the conversation header can't run into the expand button when we're
// collapsed and update the paddings of the content
int headerPaddingEnd;
@@ -1593,6 +1613,10 @@ public class ConversationLayout extends FrameLayout
}
private void onAppNameVisibilityChanged() {
+ if (notificationsRedesignTemplates()) {
+ return;
+ }
+
boolean appNameGone = mAppName.getVisibility() == GONE;
if (appNameGone != mAppNameGone) {
mAppNameGone = appNameGone;
@@ -1601,10 +1625,18 @@ public class ConversationLayout extends FrameLayout
}
private void updateAppNameDividerVisibility() {
+ if (notificationsRedesignTemplates()) {
+ return;
+ }
+
mAppNameDivider.setVisibility(mAppNameGone ? GONE : VISIBLE);
}
public void updateExpandability(boolean expandable, @Nullable OnClickListener onClickListener) {
+ if (notificationsRedesignTemplates()) {
+ return;
+ }
+
mExpandable = expandable;
if (expandable) {
mExpandButtonContainer.setVisibility(VISIBLE);
diff --git a/core/java/com/android/internal/widget/MessagingGroup.java b/core/java/com/android/internal/widget/MessagingGroup.java
index 31d9770f6ac4..b9a603cc5696 100644
--- a/core/java/com/android/internal/widget/MessagingGroup.java
+++ b/core/java/com/android/internal/widget/MessagingGroup.java
@@ -449,12 +449,8 @@ public class MessagingGroup extends NotificationOptimizedLinearLayout implements
}
private void updateIconVisibility() {
- if (Flags.notificationsRedesignTemplates() && !mIsInConversation) {
- // We don't show any icon (other than the app icon) in the collapsed form. For
- // conversations, keeping this container helps with aligning the message to the icon
- // when collapsed, but the old messaging style already has this alignment built into
- // the template like all other layouts. Conversations are special because we use the
- // same base layout for both the collapsed and expanded views.
+ if (Flags.notificationsRedesignTemplates()) {
+ // We don't show any icon (other than the app or person icon) in the collapsed form.
mMessagingIconContainer.setVisibility(mSingleLine ? GONE : VISIBLE);
}
}
diff --git a/core/java/com/android/internal/widget/MessagingLayout.java b/core/java/com/android/internal/widget/MessagingLayout.java
index 64b44df8c982..eb22e7c8cdc0 100644
--- a/core/java/com/android/internal/widget/MessagingLayout.java
+++ b/core/java/com/android/internal/widget/MessagingLayout.java
@@ -337,19 +337,10 @@ public class MessagingLayout extends FrameLayout
}
private void updateImageMessages() {
- View newMessage = null;
if (mImageMessageContainer == null) {
return;
}
- if (mIsCollapsed && !mGroups.isEmpty()) {
- // When collapsed, we're displaying the image message in a dedicated container
- // on the right of the layout instead of inline. Let's add the isolated image there
- MessagingGroup messagingGroup = mGroups.getLast();
- MessagingImageMessage isolatedMessage = messagingGroup.getIsolatedMessage();
- if (isolatedMessage != null) {
- newMessage = isolatedMessage.getView();
- }
- }
+ View newMessage = getNewImageMessage();
// Remove all messages that don't belong into the image layout
View previousMessage = mImageMessageContainer.getChildAt(0);
if (previousMessage != newMessage) {
@@ -368,6 +359,20 @@ public class MessagingLayout extends FrameLayout
}
}
+ @Nullable
+ private View getNewImageMessage() {
+ if (mIsCollapsed && !mGroups.isEmpty()) {
+ // When collapsed, we're displaying the image message in a dedicated container
+ // on the right of the layout instead of inline. Let's add the isolated image there
+ MessagingGroup messagingGroup = mGroups.getLast();
+ MessagingImageMessage isolatedMessage = messagingGroup.getIsolatedMessage();
+ if (isolatedMessage != null) {
+ return isolatedMessage.getView();
+ }
+ }
+ return null;
+ }
+
private void removeGroups(ArrayList<MessagingGroup> oldGroups) {
int size = oldGroups.size();
for (int i = 0; i < size; i++) {
diff --git a/core/jni/Android.bp b/core/jni/Android.bp
index 06702e2fa4bf..92a841f9c290 100644
--- a/core/jni/Android.bp
+++ b/core/jni/Android.bp
@@ -76,6 +76,9 @@ cc_library_shared_for_libandroid_runtime {
srcs: [
"android_animation_PropertyValuesHolder.cpp",
"android_content_res_ApkAssets.cpp",
+ "android_media_ImageReader.cpp",
+ "android_media_PublicFormatUtils.cpp",
+ "android_media_Utils.cpp",
"android_os_SystemClock.cpp",
"android_os_SystemProperties.cpp",
"android_text_AndroidCharacter.cpp",
@@ -134,10 +137,7 @@ cc_library_shared_for_libandroid_runtime {
"android_app_ActivityThread.cpp",
"android_app_NativeActivity.cpp",
"android_app_admin_SecurityLog.cpp",
- "android_media_ImageReader.cpp",
"android_media_ImageWriter.cpp",
- "android_media_PublicFormatUtils.cpp",
- "android_media_Utils.cpp",
"android_opengl_EGL14.cpp",
"android_opengl_EGL15.cpp",
"android_opengl_EGLExt.cpp",
@@ -489,6 +489,7 @@ cc_library_shared_for_libandroid_runtime {
"libsqlite",
"libgui_window_info_static",
"libbinder",
+ "libbinder_ndk",
"libhidlbase", // libhwbinder is in here
],
version_script: "platform/linux/libandroid_runtime_export.txt",
diff --git a/core/jni/android_app_PropertyInvalidatedCache.cpp b/core/jni/android_app_PropertyInvalidatedCache.cpp
index 12585d5f8137..6267522f114f 100644
--- a/core/jni/android_app_PropertyInvalidatedCache.cpp
+++ b/core/jni/android_app_PropertyInvalidatedCache.cpp
@@ -35,7 +35,7 @@ int NonceStore::getMaxNonce() const {
return kMaxNonce;
}
-size_t NonceStore::getMaxByte() const {
+int32_t NonceStore::getMaxByte() const {
return kMaxByte;
}
@@ -68,13 +68,13 @@ int32_t NonceStore::getHash() const {
}
// Copy the byte block to the target and return the current hash.
-int32_t NonceStore::getByteBlock(block_t* block, size_t len) const {
+int32_t NonceStore::getByteBlock(block_t* block, int32_t len) const {
memcpy(block, (void*) byteBlock(), std::min(kMaxByte, len));
return mByteHash;
}
// Set the byte block and the hash.
-void NonceStore::setByteBlock(int hash, const block_t* block, size_t len) {
+void NonceStore::setByteBlock(int hash, const block_t* block, int32_t len) {
memcpy((void*) byteBlock(), block, len = std::min(kMaxByte, len));
mByteHash = hash;
}
diff --git a/core/jni/android_app_PropertyInvalidatedCache.h b/core/jni/android_app_PropertyInvalidatedCache.h
index 54a4ac65fce2..1d75182356c6 100644
--- a/core/jni/android_app_PropertyInvalidatedCache.h
+++ b/core/jni/android_app_PropertyInvalidatedCache.h
@@ -27,8 +27,12 @@ namespace android::app::PropertyInvalidatedCache {
* location. Fields with a variable location are found via offsets. The offsets make this
* object position-independent, which is required because it is in shared memory and would be
* mapped into different virtual addresses for different processes.
+ *
+ * This structure is shared between 64-bit and 32-bit processes. Therefore it is imperative
+ * that the structure not use any datatypes that are architecture-dependent (like size_t).
+ * Additionally, care must be taken to avoid unexpected padding in the structure.
*/
-class NonceStore {
+class alignas(8) NonceStore {
protected:
// A convenient typedef. The jbyteArray element type is jbyte, which the compiler treats as
// signed char.
@@ -43,23 +47,27 @@ class NonceStore {
// The value of an unset field.
static constexpr int UNSET = 0;
- // The size of the nonce array.
+ // The size of the nonce array. This and the following sizes are int32_t to
+ // be ABI independent.
const int32_t kMaxNonce;
// The size of the byte array.
- const size_t kMaxByte;
+ const int32_t kMaxByte;
// The offset to the nonce array.
- const size_t mNonceOffset;
+ const int32_t mNonceOffset;
// The offset to the byte array.
- const size_t mByteOffset;
+ const int32_t mByteOffset;
// The byte block hash. This is fixed and at a known offset, so leave it in the base class.
volatile std::atomic<int32_t> mByteHash;
+ // A 4-byte padd that makes the size of this structure a multiple of 8 bytes.
+ const int32_t _pad = 0;
+
// The constructor is protected! It only makes sense when called from a subclass.
- NonceStore(int kMaxNonce, size_t kMaxByte, volatile nonce_t* nonce, volatile block_t* block) :
+ NonceStore(int kMaxNonce, int kMaxByte, volatile nonce_t* nonce, volatile block_t* block) :
kMaxNonce(kMaxNonce),
kMaxByte(kMaxByte),
mNonceOffset(offset(this, const_cast<nonce_t*>(nonce))),
@@ -70,7 +78,7 @@ class NonceStore {
// These provide run-time access to the sizing parameters.
int getMaxNonce() const;
- size_t getMaxByte() const;
+ int getMaxByte() const;
// Fetch a nonce, returning UNSET if the index is out of range. This method specifically
// does not throw or generate an error if the index is out of range; this allows the method
@@ -86,10 +94,10 @@ class NonceStore {
int32_t getHash() const;
// Copy the byte block to the target and return the current hash.
- int32_t getByteBlock(block_t* block, size_t len) const;
+ int32_t getByteBlock(block_t* block, int32_t len) const;
// Set the byte block and the hash.
- void setByteBlock(int hash, const block_t* block, size_t len);
+ void setByteBlock(int hash, const block_t* block, int32_t len);
private:
@@ -113,6 +121,12 @@ class NonceStore {
}
};
+// Assert that the size of the object is fixed, independent of the CPU architecture. There are
+// four int32_t fields and one atomic<int32_t>, which sums to 20 bytes total. This assertion
+// uses a constant instead of computing the size of the objects in the compiler, to avoid
+// different answers on different architectures.
+static_assert(sizeof(NonceStore) == 24);
+
/**
* A cache nonce block contains an array of std::atomic<int64_t> and an array of bytes. The
* byte array has an associated hash. This class provides methods to read and write the fields
@@ -126,20 +140,22 @@ class NonceStore {
* The template is parameterized by the number of nonces it supports and the number of bytes in
* the string block.
*/
-template<int maxNonce, size_t maxByte> class CacheNonce : public NonceStore {
+template<int MAX_NONCE, int MAX_BYTE> class CacheNonce : public NonceStore {
// The array of nonces
- volatile nonce_t mNonce[maxNonce];
+ volatile nonce_t mNonce[MAX_NONCE];
// The byte array. This is not atomic but it is guarded by the mByteHash.
- volatile block_t mByteBlock[maxByte];
+ volatile block_t mByteBlock[MAX_BYTE];
public:
+ // Export the parameters for use in compiler assertions.
+ static constexpr int kMaxNonceCount = MAX_NONCE;
+ static constexpr int kMaxByteCount = MAX_BYTE;
+
// Construct and initialize the memory.
- CacheNonce() :
- NonceStore(maxNonce, maxByte, &mNonce[0], &mByteBlock[0])
- {
- for (int i = 0; i < maxNonce; i++) {
+ CacheNonce() : NonceStore(MAX_NONCE, MAX_BYTE, &mNonce[0], &mByteBlock[0]) {
+ for (int i = 0; i < MAX_NONCE; i++) {
mNonce[i] = UNSET;
}
mByteHash = UNSET;
@@ -155,4 +171,10 @@ template<int maxNonce, size_t maxByte> class CacheNonce : public NonceStore {
typedef CacheNonce</* max nonce */ 128, /* byte block size */ 8192> SystemCacheNonce;
// LINT.ThenChange(/core/tests/coretests/src/android/app/PropertyInvalidatedCacheTests.java:system_nonce_config)
+// Verify that there is no padding in the final class.
+static_assert(sizeof(SystemCacheNonce) ==
+ sizeof(NonceStore)
+ + SystemCacheNonce::kMaxNonceCount*8
+ + SystemCacheNonce::kMaxByteCount);
+
} // namespace android.app.PropertyInvalidatedCache
diff --git a/core/jni/android_graphics_BLASTBufferQueue.cpp b/core/jni/android_graphics_BLASTBufferQueue.cpp
index a52678359423..1a7490e61873 100644
--- a/core/jni/android_graphics_BLASTBufferQueue.cpp
+++ b/core/jni/android_graphics_BLASTBufferQueue.cpp
@@ -26,6 +26,7 @@
#include <nativehelper/JNIHelp.h>
#include <utils/Log.h>
#include <utils/RefBase.h>
+#include <utils/StrongPointer.h>
#include "core_jni_helpers.h"
@@ -124,7 +125,7 @@ private:
static jlong nativeCreate(JNIEnv* env, jclass clazz, jstring jName,
jboolean updateDestinationFrame) {
ScopedUtfChars name(env, jName);
- sp<BLASTBufferQueue> queue = new BLASTBufferQueue(name.c_str(), updateDestinationFrame);
+ sp<BLASTBufferQueue> queue = sp<BLASTBufferQueue>::make(name.c_str(), updateDestinationFrame);
queue->incStrong((void*)nativeCreate);
return reinterpret_cast<jlong>(queue.get());
}
diff --git a/core/jni/android_media_ImageReader.cpp b/core/jni/android_media_ImageReader.cpp
index 20b9c571317e..a34cb391c2fd 100644
--- a/core/jni/android_media_ImageReader.cpp
+++ b/core/jni/android_media_ImageReader.cpp
@@ -25,13 +25,22 @@
#include <android_runtime/android_view_Surface.h>
#include <com_android_graphics_libgui_flags.h>
#include <cutils/atomic.h>
+#ifdef __ANDROID__
#include <grallocusage/GrallocUsageConversion.h>
+#else
+#define GRALLOC_USAGE_PROTECTED 0
+#define GRALLOC_USAGE_SW_READ_OFTEN 0
+#define GRALLOC_USAGE_SW_WRITE_OFTEN 0
+#endif
#include <gui/BufferItemConsumer.h>
#include <gui/Surface.h>
#include <inttypes.h>
#include <jni.h>
+#include <jni_wrappers.h>
#include <nativehelper/JNIHelp.h>
+#ifdef __ANDROID__
#include <private/android/AHardwareBufferHelpers.h>
+#endif
#include <stdint.h>
#include <ui/Rect.h>
#include <utils/List.h>
@@ -393,8 +402,12 @@ static void ImageReader_init(JNIEnv* env, jobject thiz, jobject weakThiz, jint w
String8 consumerName = String8::format("ImageReader-%dx%df%xm%d-%d-%d",
width, height, nativeHalFormat, maxImages, getpid(),
createProcessUniqueId());
+#ifdef __ANDROID__
uint64_t consumerUsage =
android_hardware_HardwareBuffer_convertToGrallocUsageBits(ndkUsage);
+#else
+ uint64_t consumerUsage = 0;
+#endif
#if COM_ANDROID_GRAPHICS_LIBGUI_FLAGS(WB_CONSUMER_BASE_OWNS_BQ)
sp<BufferItemConsumer> bufferConsumer = new BufferItemConsumer(consumerUsage, maxImages,
@@ -773,6 +786,7 @@ static bool Image_getLockedImageInfo(JNIEnv* env, LockedImage* buffer, int idx,
return true;
}
+#ifdef __ANDROID__
static void ImageReader_unlockGraphicBuffer(JNIEnv* env, jobject /*thiz*/,
jobject buffer) {
sp<GraphicBuffer> graphicBuffer =
@@ -856,6 +870,7 @@ static jobjectArray ImageReader_createImagePlanes(JNIEnv* env, jobject /*thiz*/,
return imagePlanes;
}
+#endif
static jobjectArray Image_createSurfacePlanes(JNIEnv* env, jobject thiz,
int numPlanes, int halReaderFormat, uint64_t ndkReaderUsage)
@@ -964,6 +979,7 @@ static jint Image_getFormat(JNIEnv* env, jobject thiz, jint readerFormat)
}
}
+#ifdef __ANDROID__
static jobject Image_getHardwareBuffer(JNIEnv* env, jobject thiz) {
BufferItem* buffer = Image_getBufferItem(env, thiz);
if (buffer == nullptr) {
@@ -976,45 +992,48 @@ static jobject Image_getHardwareBuffer(JNIEnv* env, jobject thiz) {
// to link against libandroid.so
return android_hardware_HardwareBuffer_createFromAHardwareBuffer(env, b);
}
+#endif
} // extern "C"
// ----------------------------------------------------------------------------
-static const JNINativeMethod gImageReaderMethods[] = {
- {"nativeClassInit", "()V", (void*)ImageReader_classInit },
- {"nativeInit", "(Ljava/lang/Object;IIIJII)V", (void*)ImageReader_init },
- {"nativeClose", "()V", (void*)ImageReader_close },
- {"nativeReleaseImage", "(Landroid/media/Image;)V", (void*)ImageReader_imageRelease },
- {"nativeImageSetup", "(Landroid/media/Image;)I", (void*)ImageReader_imageSetup },
- {"nativeGetSurface", "()Landroid/view/Surface;", (void*)ImageReader_getSurface },
- {"nativeDetachImage", "(Landroid/media/Image;Z)I", (void*)ImageReader_detachImage },
- {"nativeCreateImagePlanes",
- "(ILandroid/graphics/GraphicBuffer;IIIIII)[Landroid/media/ImageReader$ImagePlane;",
- (void*)ImageReader_createImagePlanes },
- {"nativeUnlockGraphicBuffer",
- "(Landroid/graphics/GraphicBuffer;)V", (void*)ImageReader_unlockGraphicBuffer },
- {"nativeDiscardFreeBuffers", "()V", (void*)ImageReader_discardFreeBuffers }
-};
+static const JNINativeMethod gImageReaderMethods[] =
+ {{"nativeClassInit", "()V", (void*)ImageReader_classInit},
+ {"nativeInit", "(Ljava/lang/Object;IIIJII)V", (void*)ImageReader_init},
+ {"nativeClose", "()V", (void*)ImageReader_close},
+ {"nativeReleaseImage", "(Landroid/media/Image;)V", (void*)ImageReader_imageRelease},
+ {"nativeImageSetup", "(Landroid/media/Image;)I", (void*)ImageReader_imageSetup},
+ {"nativeGetSurface", "()Landroid/view/Surface;", (void*)ImageReader_getSurface},
+ {"nativeDetachImage", "(Landroid/media/Image;Z)I", (void*)ImageReader_detachImage},
+#ifdef __ANDROID__
+ {"nativeCreateImagePlanes",
+ "(ILandroid/graphics/GraphicBuffer;IIIIII)[Landroid/media/ImageReader$ImagePlane;",
+ (void*)ImageReader_createImagePlanes},
+ {"nativeUnlockGraphicBuffer", "(Landroid/graphics/GraphicBuffer;)V",
+ (void*)ImageReader_unlockGraphicBuffer},
+#endif
+ {"nativeDiscardFreeBuffers", "()V", (void*)ImageReader_discardFreeBuffers}};
static const JNINativeMethod gImageMethods[] = {
- {"nativeCreatePlanes", "(IIJ)[Landroid/media/ImageReader$SurfaceImage$SurfacePlane;",
- (void*)Image_createSurfacePlanes },
- {"nativeGetWidth", "()I", (void*)Image_getWidth },
- {"nativeGetHeight", "()I", (void*)Image_getHeight },
- {"nativeGetFormat", "(I)I", (void*)Image_getFormat },
- {"nativeGetFenceFd", "()I", (void*)Image_getFenceFd },
- {"nativeGetHardwareBuffer", "()Landroid/hardware/HardwareBuffer;",
- (void*)Image_getHardwareBuffer },
+ {"nativeCreatePlanes", "(IIJ)[Landroid/media/ImageReader$SurfaceImage$SurfacePlane;",
+ (void*)Image_createSurfacePlanes},
+ {"nativeGetWidth", "()I", (void*)Image_getWidth},
+ {"nativeGetHeight", "()I", (void*)Image_getHeight},
+ {"nativeGetFormat", "(I)I", (void*)Image_getFormat},
+ {"nativeGetFenceFd", "()I", (void*)Image_getFenceFd},
+#ifdef __ANDROID__
+ {"nativeGetHardwareBuffer", "()Landroid/hardware/HardwareBuffer;",
+ (void*)Image_getHardwareBuffer},
+#endif
};
int register_android_media_ImageReader(JNIEnv *env) {
+ int ret1 = RegisterMethodsOrDie(env, "android/media/ImageReader", gImageReaderMethods,
+ NELEM(gImageReaderMethods));
- int ret1 = AndroidRuntime::registerNativeMethods(env,
- "android/media/ImageReader", gImageReaderMethods, NELEM(gImageReaderMethods));
-
- int ret2 = AndroidRuntime::registerNativeMethods(env,
- "android/media/ImageReader$SurfaceImage", gImageMethods, NELEM(gImageMethods));
+ int ret2 = RegisterMethodsOrDie(env, "android/media/ImageReader$SurfaceImage", gImageMethods,
+ NELEM(gImageMethods));
return (ret1 || ret2);
}
diff --git a/core/jni/android_media_PublicFormatUtils.cpp b/core/jni/android_media_PublicFormatUtils.cpp
index 04494ad00a65..bcdf654d5a8c 100644
--- a/core/jni/android_media_PublicFormatUtils.cpp
+++ b/core/jni/android_media_PublicFormatUtils.cpp
@@ -16,10 +16,10 @@
#define LOG_TAG "PublicFormatUtils_JNI"
-#include <utils/misc.h>
-#include <ui/PublicFormat.h>
-#include <android_runtime/AndroidRuntime.h>
#include <jni.h>
+#include <jni_wrappers.h>
+#include <ui/PublicFormat.h>
+#include <utils/misc.h>
using namespace android;
@@ -53,7 +53,6 @@ static const JNINativeMethod gMethods[] = {
};
int register_android_media_PublicFormatUtils(JNIEnv *env) {
- return AndroidRuntime::registerNativeMethods(env,
- "android/media/PublicFormatUtils", gMethods, NELEM(gMethods));
+ return RegisterMethodsOrDie(env, "android/media/PublicFormatUtils", gMethods, NELEM(gMethods));
}
diff --git a/core/jni/android_media_Utils.cpp b/core/jni/android_media_Utils.cpp
index e8f8644a4503..e48414113cf0 100644
--- a/core/jni/android_media_Utils.cpp
+++ b/core/jni/android_media_Utils.cpp
@@ -19,10 +19,12 @@
#include "android_media_Utils.h"
+#ifdef __ANDROID__ // Layoutlib does not support hardware
#include <aidl/android/hardware/graphics/common/PixelFormat.h>
#include <aidl/android/hardware/graphics/common/PlaneLayoutComponentType.h>
#include <ui/GraphicBufferMapper.h>
#include <ui/GraphicTypes.h>
+#endif
#include <utils/Log.h>
#define ALIGN(x, mask) ( ((x) + (mask) - 1) & ~((mask) - 1) )
@@ -34,7 +36,13 @@ namespace android {
// -----------Utility functions used by ImageReader/Writer JNI-----------------
+#ifdef __ANDROID__
using AidlPixelFormat = aidl::android::hardware::graphics::common::PixelFormat;
+#else
+namespace AidlPixelFormat {
+const int32_t YCBCR_P210 = 60;
+}
+#endif
enum {
IMAGE_MAX_NUM_PLANES = 3,
@@ -517,6 +525,7 @@ status_t getLockedImageInfo(LockedImage* buffer, int idx,
return OK;
}
+#ifdef __ANDROID__
static status_t extractP010Gralloc4PlaneLayout(
sp<GraphicBuffer> buffer, void *pData, int format, LockedImage *outputImage) {
using aidl::android::hardware::graphics::common::PlaneLayoutComponent;
@@ -663,6 +672,7 @@ static status_t extractP210Gralloc4PlaneLayout(sp<GraphicBuffer> buffer, void *p
outputImage->chromaStep = 4;
return OK;
}
+#endif
status_t lockImageFromBuffer(sp<GraphicBuffer> buffer, uint32_t inUsage,
const Rect& rect, int fenceFd, LockedImage* outputImage) {
@@ -701,6 +711,7 @@ status_t lockImageFromBuffer(sp<GraphicBuffer> buffer, uint32_t inUsage,
ALOGE("Lock buffer failed!");
return res;
}
+#ifdef __ANDROID__
if (isPossibly10BitYUV(format)) {
if (format == HAL_PIXEL_FORMAT_YCBCR_P010 &&
OK == extractP010Gralloc4PlaneLayout(buffer, pData, format, outputImage)) {
@@ -713,6 +724,7 @@ status_t lockImageFromBuffer(sp<GraphicBuffer> buffer, uint32_t inUsage,
return OK;
}
}
+#endif
}
outputImage->data = reinterpret_cast<uint8_t*>(pData);
diff --git a/core/jni/android_view_SurfaceControl.cpp b/core/jni/android_view_SurfaceControl.cpp
index b99b0ef7f24e..a8e51a7c1fe8 100644
--- a/core/jni/android_view_SurfaceControl.cpp
+++ b/core/jni/android_view_SurfaceControl.cpp
@@ -765,28 +765,28 @@ static void nativeSetLuts(JNIEnv* env, jclass clazz, jlong transactionObj, jlong
std::vector<int32_t> dimensions;
std::vector<int32_t> sizes;
std::vector<int32_t> samplingKeys;
- int32_t fd = -1;
+ base::unique_fd fd;
if (jdimensionArray) {
jsize numLuts = env->GetArrayLength(jdimensionArray);
- ScopedIntArrayRW joffsets(env, joffsetArray);
+ ScopedIntArrayRO joffsets(env, joffsetArray);
if (joffsets.get() == nullptr) {
- jniThrowRuntimeException(env, "Failed to get ScopedIntArrayRW from joffsetArray");
+ jniThrowRuntimeException(env, "Failed to get ScopedIntArrayRO from joffsetArray");
return;
}
- ScopedIntArrayRW jdimensions(env, jdimensionArray);
+ ScopedIntArrayRO jdimensions(env, jdimensionArray);
if (jdimensions.get() == nullptr) {
- jniThrowRuntimeException(env, "Failed to get ScopedIntArrayRW from jdimensionArray");
+ jniThrowRuntimeException(env, "Failed to get ScopedIntArrayRO from jdimensionArray");
return;
}
- ScopedIntArrayRW jsizes(env, jsizeArray);
+ ScopedIntArrayRO jsizes(env, jsizeArray);
if (jsizes.get() == nullptr) {
- jniThrowRuntimeException(env, "Failed to get ScopedIntArrayRW from jsizeArray");
+ jniThrowRuntimeException(env, "Failed to get ScopedIntArrayRO from jsizeArray");
return;
}
- ScopedIntArrayRW jsamplingKeys(env, jsamplingKeyArray);
+ ScopedIntArrayRO jsamplingKeys(env, jsamplingKeyArray);
if (jsamplingKeys.get() == nullptr) {
- jniThrowRuntimeException(env, "Failed to get ScopedIntArrayRW from jsamplingKeyArray");
+ jniThrowRuntimeException(env, "Failed to get ScopedIntArrayRO from jsamplingKeyArray");
return;
}
@@ -796,15 +796,15 @@ static void nativeSetLuts(JNIEnv* env, jclass clazz, jlong transactionObj, jlong
sizes = std::vector<int32_t>(jsizes.get(), jsizes.get() + numLuts);
samplingKeys = std::vector<int32_t>(jsamplingKeys.get(), jsamplingKeys.get() + numLuts);
- ScopedFloatArrayRW jbuffers(env, jbufferArray);
+ ScopedFloatArrayRO jbuffers(env, jbufferArray);
if (jbuffers.get() == nullptr) {
- jniThrowRuntimeException(env, "Failed to get ScopedFloatArrayRW from jbufferArray");
+ jniThrowRuntimeException(env, "Failed to get ScopedFloatArrayRO from jbufferArray");
return;
}
// create the shared memory and copy jbuffers
size_t bufferSize = jbuffers.size() * sizeof(float);
- fd = ashmem_create_region("lut_shared_mem", bufferSize);
+ fd.reset(ashmem_create_region("lut_shared_mem", bufferSize));
if (fd < 0) {
jniThrowRuntimeException(env, "ashmem_create_region() failed");
return;
@@ -820,7 +820,7 @@ static void nativeSetLuts(JNIEnv* env, jclass clazz, jlong transactionObj, jlong
}
}
- transaction->setLuts(ctrl, base::unique_fd(fd), offsets, dimensions, sizes, samplingKeys);
+ transaction->setLuts(ctrl, std::move(fd), offsets, dimensions, sizes, samplingKeys);
}
static void nativeSetPictureProfileId(JNIEnv* env, jclass clazz, jlong transactionObj,
diff --git a/core/jni/com_android_internal_os_LongArrayMultiStateCounter.cpp b/core/jni/com_android_internal_os_LongArrayMultiStateCounter.cpp
index 8f36ecb8b01a..7d94ef85f51a 100644
--- a/core/jni/com_android_internal_os_LongArrayMultiStateCounter.cpp
+++ b/core/jni/com_android_internal_os_LongArrayMultiStateCounter.cpp
@@ -116,17 +116,26 @@ static void native_reset(jlong nativePtr) {
counter->reset();
}
-static void native_getCounts(JNIEnv *env, jclass, jlong nativePtr, jlongArray values, jint state) {
+static bool native_getCounts(JNIEnv *env, jclass, jlong nativePtr, jlongArray values, jint state) {
auto *counter = reinterpret_cast<LongArrayMultiStateCounter *>(nativePtr);
- ScopedLongArrayRW scopedArray(env, values);
auto *data = counter->getCount(state).data();
- auto size = env->GetArrayLength(values);
- auto *outData = scopedArray.get();
if (data == nullptr) {
- memset(outData, 0, size * sizeof(uint64_t));
- } else {
- memcpy(outData, data, size * sizeof(uint64_t));
+ return false;
+ }
+ auto size = env->GetArrayLength(values);
+ bool allZeros = true;
+ for (int i = 0; i < size; i++) {
+ if (data[i]) {
+ allZeros = false;
+ break;
+ }
}
+ if (allZeros) {
+ return false;
+ }
+ ScopedLongArrayRW scopedArray(env, values);
+ memcpy(scopedArray.get(), data, size * sizeof(uint64_t));
+ return true;
}
static jobject native_toString(JNIEnv *env, jclass, jlong nativePtr) {
@@ -255,7 +264,7 @@ static const JNINativeMethod g_LongArrayMultiStateCounter_methods[] = {
// @CriticalNative
{"native_reset", "(J)V", (void *)native_reset},
// @FastNative
- {"native_getCounts", "(J[JI)V", (void *)native_getCounts},
+ {"native_getCounts", "(J[JI)Z", (void *)native_getCounts},
// @FastNative
{"native_toString", "(J)Ljava/lang/String;", (void *)native_toString},
// @FastNative
diff --git a/core/jni/jni_wrappers.h b/core/jni/jni_wrappers.h
index e3e17eed54d5..1f44994b54f2 100644
--- a/core/jni/jni_wrappers.h
+++ b/core/jni/jni_wrappers.h
@@ -22,6 +22,8 @@
#include <log/log.h>
#include <nativehelper/JNIHelp.h>
+#include <string>
+
namespace android {
static inline jclass FindClassOrDie(JNIEnv* env, const char* class_name) {
diff --git a/core/jni/platform/host/HostRuntime.cpp b/core/jni/platform/host/HostRuntime.cpp
index 1a0328338980..746740b0248b 100644
--- a/core/jni/platform/host/HostRuntime.cpp
+++ b/core/jni/platform/host/HostRuntime.cpp
@@ -48,6 +48,8 @@ using namespace std;
* (see AndroidRuntime.cpp).
*/
+extern int register_android_media_ImageReader(JNIEnv* env);
+extern int register_android_media_PublicFormatUtils(JNIEnv* env);
extern int register_android_os_Binder(JNIEnv* env);
extern int register_libcore_util_NativeAllocationRegistry(JNIEnv* env);
@@ -126,6 +128,10 @@ static const std::unordered_map<std::string, RegJNIRec> gRegJNIMap = {
{"android.database.sqlite.SQLiteDebug", REG_JNI(register_android_database_SQLiteDebug)},
{"android.database.sqlite.SQLiteRawStatement",
REG_JNI(register_android_database_SQLiteRawStatement)},
+#endif
+ {"android.media.ImageReader", REG_JNI(register_android_media_ImageReader)},
+ {"android.media.PublicFormatUtils", REG_JNI(register_android_media_PublicFormatUtils)},
+#ifdef __linux__
{"android.os.Binder", REG_JNI(register_android_os_Binder)},
{"android.os.FileObserver", REG_JNI(register_android_os_FileObserver)},
{"android.os.MessageQueue", REG_JNI(register_android_os_MessageQueue)},
diff --git a/core/res/AndroidManifest.xml b/core/res/AndroidManifest.xml
index 78526ad4a06b..ee6899cf866b 100644
--- a/core/res/AndroidManifest.xml
+++ b/core/res/AndroidManifest.xml
@@ -7759,7 +7759,17 @@
@FlaggedApi("com.android.server.telecom.flags.telecom_resolve_hidden_dependencies")
@hide -->
<permission android:name="android.permission.READ_BLOCKED_NUMBERS"
- android:protectionLevel="signature" />
+ android:protectionLevel="signature"
+ android:featureFlag="!android.permission.flags.grant_read_blocked_numbers_to_system_ui_intelligence" />
+
+ <!-- Allows the holder to read blocked numbers. See
+ {@link android.provider.BlockedNumberContract}.
+ @SystemApi
+ @FlaggedApi("com.android.server.telecom.flags.telecom_resolve_hidden_dependencies")
+ @hide -->
+ <permission android:name="android.permission.READ_BLOCKED_NUMBERS"
+ android:protectionLevel="signature|role"
+ android:featureFlag="android.permission.flags.grant_read_blocked_numbers_to_system_ui_intelligence" />
<!-- Allows the holder to write blocked numbers. See
{@link android.provider.BlockedNumberContract}.
diff --git a/core/res/res/layout/notification_2025_conversation_header.xml b/core/res/res/layout/notification_2025_conversation_header.xml
index 1bde17358825..68096f8cc50e 100644
--- a/core/res/res/layout/notification_2025_conversation_header.xml
+++ b/core/res/res/layout/notification_2025_conversation_header.xml
@@ -1,6 +1,6 @@
<?xml version="1.0" encoding="utf-8"?>
<!--
- ~ Copyright (C) 2024 The Android Open Source Project
+ ~ Copyright (C) 2025 The Android Open Source Project
~
~ Licensed under the Apache License, Version 2.0 (the "License");
~ you may not use this file except in compliance with the License.
@@ -15,157 +15,74 @@
~ limitations under the License
-->
-<com.android.internal.widget.ConversationHeaderLinearLayout
+<!-- extends RelativeLayout -->
+<NotificationHeaderView
xmlns:android="http://schemas.android.com/apk/res/android"
- android:id="@+id/conversation_header"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
+ android:id="@+id/notification_header"
+ android:layout_width="match_parent"
+ android:layout_height="@dimen/notification_2025_header_height"
+ android:clipChildren="false"
+ android:gravity="center_vertical"
android:orientation="horizontal"
- android:paddingTop="@dimen/notification_2025_margin"
+ android:theme="@style/Theme.DeviceDefault.Notification"
+ android:importantForAccessibility="no"
>
- <TextView
- android:id="@+id/conversation_text"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:textAppearance="@style/TextAppearance.DeviceDefault.Notification.Title"
- android:textSize="16sp"
- android:singleLine="true"
- android:layout_weight="1"
- />
-
- <TextView
- android:id="@+id/app_name_divider"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:textAppearance="@style/TextAppearance.DeviceDefault.Notification.Info"
- android:layout_marginStart="@dimen/notification_conversation_header_separating_margin"
- android:text="@string/notification_header_divider_symbol"
- android:singleLine="true"
+ <ImageView
+ android:id="@+id/left_icon"
+ android:layout_width="@dimen/notification_2025_left_icon_size"
+ android:layout_height="@dimen/notification_2025_left_icon_size"
+ android:layout_alignParentStart="true"
+ android:layout_margin="@dimen/notification_2025_margin"
+ android:background="@drawable/notification_large_icon_outline"
+ android:clipToOutline="true"
+ android:importantForAccessibility="no"
+ android:scaleType="centerCrop"
android:visibility="gone"
/>
- <!-- App Name -->
- <com.android.internal.widget.ObservableTextView
- android:id="@+id/app_name_text"
- android:textAppearance="@style/TextAppearance.DeviceDefault.Notification.Info"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:layout_marginStart="@dimen/notification_conversation_header_separating_margin"
- android:singleLine="true"
- android:visibility="gone"
- />
+ <include layout="@layout/notification_2025_conversation_icon_container" />
- <TextView
- android:id="@+id/time_divider"
+ <!-- extends ViewGroup -->
+ <NotificationTopLineView
+ xmlns:android="http://schemas.android.com/apk/res/android"
+ android:id="@+id/notification_top_line"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
- android:textAppearance="@style/TextAppearance.DeviceDefault.Notification.Info"
- android:layout_marginStart="@dimen/notification_conversation_header_separating_margin"
- android:text="@string/notification_header_divider_symbol"
- android:singleLine="true"
- android:visibility="gone"
- />
+ android:layout_alignParentStart="true"
+ android:layout_toStartOf="@id/expand_button"
+ android:layout_alignWithParentIfMissing="true"
+ android:layout_marginVertical="@dimen/notification_2025_margin"
+ android:clipChildren="false"
+ android:gravity="center_vertical"
+ android:paddingStart="@dimen/notification_2025_content_margin_start"
+ android:theme="@style/Theme.DeviceDefault.Notification"
+ >
- <DateTimeView
- android:id="@+id/time"
- android:textAppearance="@style/TextAppearance.DeviceDefault.Notification.Time"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:layout_marginStart="@dimen/notification_conversation_header_separating_margin"
- android:showRelative="true"
- android:singleLine="true"
- android:visibility="gone"
- />
+ <include layout="@layout/notification_2025_top_line_views" />
- <ViewStub
- android:id="@+id/chronometer"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:layout_marginStart="@dimen/notification_conversation_header_separating_margin"
- android:layout="@layout/notification_template_part_chronometer"
- android:visibility="gone"
- />
+ </NotificationTopLineView>
- <TextView
- android:id="@+id/verification_divider"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:textAppearance="@style/TextAppearance.DeviceDefault.Notification.Info"
- android:layout_marginStart="@dimen/notification_conversation_header_separating_margin"
- android:text="@string/notification_header_divider_symbol"
- android:singleLine="true"
- android:visibility="gone"
- />
-
- <ImageView
- android:id="@+id/verification_icon"
- android:layout_width="@dimen/notification_verification_icon_size"
- android:layout_height="@dimen/notification_verification_icon_size"
- android:layout_marginStart="@dimen/notification_conversation_header_separating_margin"
- android:baseline="10dp"
- android:scaleType="fitCenter"
- android:src="@drawable/ic_notifications_alerted"
- android:visibility="gone"
+ <FrameLayout
+ android:id="@+id/alternate_expand_target"
+ android:layout_width="@dimen/notification_2025_content_margin_start"
+ android:layout_height="match_parent"
+ android:layout_alignParentStart="true"
+ android:importantForAccessibility="no"
+ android:focusable="false"
/>
- <TextView
- android:id="@+id/verification_text"
- android:textAppearance="@style/TextAppearance.DeviceDefault.Notification.Info"
+ <include layout="@layout/notification_2025_expand_button"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
- android:layout_marginStart="@dimen/notification_conversation_header_separating_margin"
- android:layout_weight="100"
- android:showRelative="true"
- android:singleLine="true"
- android:visibility="gone"
- />
+ android:layout_gravity="top|end"
+ android:layout_alignParentEnd="true" />
- <ImageButton
- android:id="@+id/feedback"
- android:layout_width="@dimen/notification_feedback_size"
- android:layout_height="@dimen/notification_feedback_size"
- android:layout_marginStart="@dimen/notification_header_separating_margin"
- android:background="?android:selectableItemBackgroundBorderless"
- android:contentDescription="@string/notification_feedback_indicator"
- android:baseline="13dp"
- android:scaleType="fitCenter"
- android:src="@drawable/ic_feedback_indicator"
- android:visibility="gone"
- />
+ <include layout="@layout/notification_close_button"
+ android:id="@+id/close_button"
+ android:layout_width="@dimen/notification_close_button_size"
+ android:layout_height="@dimen/notification_close_button_size"
+ android:layout_alignParentTop="true"
+ android:layout_alignParentEnd="true" />
- <ImageView
- android:id="@+id/phishing_alert"
- android:layout_width="@dimen/notification_2025_badge_size"
- android:layout_height="@dimen/notification_2025_badge_size"
- android:layout_marginStart="@dimen/notification_2025_badge_margin"
- android:baseline="@dimen/notification_2025_badge_baseline"
- android:scaleType="fitCenter"
- android:src="@drawable/ic_dialog_alert_material"
- android:visibility="gone"
- android:contentDescription="@string/notification_phishing_alert_content_description"
- />
-
- <ImageView
- android:id="@+id/profile_badge"
- android:layout_width="@dimen/notification_2025_badge_size"
- android:layout_height="@dimen/notification_2025_badge_size"
- android:layout_marginStart="@dimen/notification_2025_badge_margin"
- android:baseline="@dimen/notification_2025_badge_baseline"
- android:scaleType="fitCenter"
- android:visibility="gone"
- android:contentDescription="@string/notification_work_profile_content_description"
- />
-
- <ImageView
- android:id="@+id/alerted_icon"
- android:layout_width="@dimen/notification_2025_badge_size"
- android:layout_height="@dimen/notification_2025_badge_size"
- android:layout_marginStart="@dimen/notification_2025_badge_margin"
- android:baseline="@dimen/notification_2025_badge_baseline"
- android:contentDescription="@string/notification_alerted_content_description"
- android:scaleType="fitCenter"
- android:src="@drawable/ic_notifications_alerted"
- android:visibility="gone"
- />
-</com.android.internal.widget.ConversationHeaderLinearLayout>
+</NotificationHeaderView>
diff --git a/core/res/res/layout/notification_2025_template_collapsed_call.xml b/core/res/res/layout/notification_2025_template_collapsed_call.xml
index 6f3c15adb082..ee691e4d6894 100644
--- a/core/res/res/layout/notification_2025_template_collapsed_call.xml
+++ b/core/res/res/layout/notification_2025_template_collapsed_call.xml
@@ -1,5 +1,5 @@
<?xml version="1.0" encoding="utf-8"?><!--
- ~ Copyright (C) 2024 The Android Open Source Project
+ ~ Copyright (C) 2025 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.
@@ -25,55 +25,177 @@
android:theme="@style/Theme.DeviceDefault.Notification"
>
- <!-- CallLayout shares visual appearance with ConversationLayout, so shares layouts -->
- <include layout="@layout/notification_2025_conversation_icon_container" />
-
<LinearLayout
- xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="wrap_content"
- android:minHeight="@dimen/notification_2025_min_height"
- android:orientation="horizontal"
+ android:clipChildren="false"
+ android:orientation="vertical"
>
- <LinearLayout
- android:id="@+id/notification_main_column"
+ <com.android.internal.widget.NotificationMaxHeightFrameLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
- android:layout_weight="1"
- android:layout_marginStart="@dimen/notification_2025_content_margin_start"
- android:orientation="vertical"
- android:paddingBottom="@dimen/notification_2025_margin"
+ android:minHeight="@dimen/notification_2025_min_height"
+ android:clipChildren="false"
>
- <include
- layout="@layout/notification_2025_conversation_header"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
+ <ImageView
+ android:id="@+id/left_icon"
+ android:layout_width="@dimen/notification_2025_left_icon_size"
+ android:layout_height="@dimen/notification_2025_left_icon_size"
+ android:layout_alignParentStart="true"
+ android:layout_margin="@dimen/notification_2025_margin"
+ android:background="@drawable/notification_large_icon_outline"
+ android:clipToOutline="true"
+ android:importantForAccessibility="no"
+ android:scaleType="centerCrop"
+ android:visibility="gone"
/>
- <include layout="@layout/notification_template_text"
- android:layout_height="wrap_content"
- android:minHeight="@dimen/notification_text_height"
+ <!-- CallLayout shares visual appearance with ConversationLayout, so shares layouts -->
+ <include layout="@layout/notification_2025_conversation_icon_container" />
+
+ <FrameLayout
+ android:id="@+id/alternate_expand_target"
+ android:layout_width="@dimen/notification_2025_content_margin_start"
+ android:layout_height="match_parent"
+ android:layout_gravity="start"
+ android:importantForAccessibility="no"
+ android:focusable="false"
/>
- </LinearLayout>
+ <LinearLayout
+ android:id="@+id/notification_headerless_view_row"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ android:layout_marginStart="@dimen/notification_2025_content_margin_start"
+ android:orientation="horizontal"
+ android:clipChildren="false"
+ >
- <FrameLayout
- android:layout_width="wrap_content"
- android:layout_height="match_parent"
- android:minWidth="@dimen/notification_content_margin_end"
- >
+ <LinearLayout
+ android:id="@+id/notification_headerless_view_column"
+ android:layout_width="0px"
+ android:layout_height="wrap_content"
+ android:layout_gravity="center_vertical"
+ android:layout_weight="1"
+ android:layout_marginBottom="@dimen/notification_2025_margin"
+ android:layout_marginTop="@dimen/notification_2025_margin"
+ android:clipChildren="false"
+ android:orientation="vertical"
+ >
- <include
- layout="@layout/notification_2025_expand_button"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:layout_gravity="top|end"
- />
+ <NotificationTopLineView
+ android:id="@+id/notification_top_line"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:minHeight="@dimen/notification_headerless_line_height"
+ android:clipChildren="false"
+ android:theme="@style/Theme.DeviceDefault.Notification"
+ >
+
+ <!--
+ NOTE: The notification_2025_top_line_views layout contains the app_name_text.
+ In order to include the title view at the beginning, the Notification.Builder
+ has logic to hide that view whenever this title view is to be visible.
+ -->
+
+ <TextView
+ android:id="@+id/title"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_marginEnd="@dimen/notification_header_separating_margin"
+ android:ellipsize="end"
+ android:fadingEdge="horizontal"
+ android:singleLine="true"
+ android:textAlignment="viewStart"
+ android:textAppearance="@style/TextAppearance.DeviceDefault.Notification.Title"
+ />
+
+ <include layout="@layout/notification_2025_top_line_views" />
+
+ </NotificationTopLineView>
- </FrameLayout>
+ <LinearLayout
+ android:id="@+id/notification_main_column"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:orientation="vertical"
+ android:clipChildren="false"
+ >
+ <com.android.internal.widget.NotificationVanishingFrameLayout
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:minHeight="@dimen/notification_headerless_line_height"
+ >
+ <!-- This is the simplest way to keep this text vertically centered without
+ gravity="center_vertical" which causes jumpiness in expansion animations. -->
+ <include
+ layout="@layout/notification_2025_text"
+ android:layout_width="match_parent"
+ android:layout_height="@dimen/notification_text_height"
+ android:layout_gravity="center_vertical"
+ android:layout_marginTop="0dp"
+ />
+ </com.android.internal.widget.NotificationVanishingFrameLayout>
+ </LinearLayout>
+ </LinearLayout>
+
+ <ImageView
+ android:id="@+id/right_icon"
+ android:layout_width="@dimen/notification_right_icon_size"
+ android:layout_height="@dimen/notification_right_icon_size"
+ android:layout_gravity="center_vertical|end"
+ android:layout_marginTop="@dimen/notification_right_icon_headerless_margin"
+ android:layout_marginBottom="@dimen/notification_right_icon_headerless_margin"
+ android:layout_marginStart="@dimen/notification_right_icon_content_margin"
+ android:background="@drawable/notification_large_icon_outline"
+ android:clipToOutline="true"
+ android:importantForAccessibility="no"
+ android:scaleType="centerCrop"
+ />
+
+ <FrameLayout
+ android:id="@+id/expand_button_touch_container"
+ android:layout_width="wrap_content"
+ android:layout_height="match_parent"
+ android:minWidth="@dimen/notification_content_margin_end"
+ >
+
+ <include layout="@layout/notification_2025_expand_button"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_gravity="top|end"
+ />
+
+ </FrameLayout>
+
+ </LinearLayout>
+
+ <include layout="@layout/notification_close_button"
+ android:id="@+id/close_button"
+ android:layout_width="@dimen/notification_close_button_size"
+ android:layout_height="@dimen/notification_close_button_size"
+ android:layout_gravity="top|end" />
+
+ </com.android.internal.widget.NotificationMaxHeightFrameLayout>
+
+ <LinearLayout
+ android:id="@+id/notification_action_list_margin_target"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:layout_marginTop="-20dp"
+ android:clipChildren="false"
+ android:orientation="vertical">
+ <include layout="@layout/notification_template_smart_reply_container"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:layout_marginTop="@dimen/notification_content_margin"
+ android:layout_marginStart="@dimen/notification_2025_content_margin_start"
+ android:layout_marginEnd="@dimen/notification_content_margin_end" />
+ <include layout="@layout/notification_material_action_list" />
+ </LinearLayout>
</LinearLayout>
</com.android.internal.widget.CallLayout>
diff --git a/core/res/res/layout/notification_2025_template_collapsed_conversation.xml b/core/res/res/layout/notification_2025_template_collapsed_conversation.xml
new file mode 100644
index 000000000000..f80411103501
--- /dev/null
+++ b/core/res/res/layout/notification_2025_template_collapsed_conversation.xml
@@ -0,0 +1,216 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ ~ Copyright (C) 2025 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
+ -->
+
+<!-- extends FrameLayout -->
+<com.android.internal.widget.ConversationLayout
+ xmlns:android="http://schemas.android.com/apk/res/android"
+ android:id="@+id/status_bar_latest_event_content"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:clipChildren="false"
+ android:tag="conversation"
+ >
+
+ <LinearLayout
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:clipChildren="false"
+ android:orientation="vertical"
+ >
+
+
+ <com.android.internal.widget.NotificationMaxHeightFrameLayout
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:minHeight="@dimen/notification_2025_min_height"
+ android:clipChildren="false"
+ >
+
+ <ImageView
+ android:id="@+id/left_icon"
+ android:layout_width="@dimen/notification_2025_left_icon_size"
+ android:layout_height="@dimen/notification_2025_left_icon_size"
+ android:layout_alignParentStart="true"
+ android:layout_margin="@dimen/notification_2025_margin"
+ android:background="@drawable/notification_large_icon_outline"
+ android:clipToOutline="true"
+ android:importantForAccessibility="no"
+ android:scaleType="centerCrop"
+ android:visibility="gone"
+ />
+
+ <include layout="@layout/notification_2025_conversation_icon_container" />
+
+ <FrameLayout
+ android:id="@+id/alternate_expand_target"
+ android:layout_width="@dimen/notification_2025_content_margin_start"
+ android:layout_height="match_parent"
+ android:layout_gravity="start"
+ android:importantForAccessibility="no"
+ android:focusable="false"
+ />
+
+ <!--
+ NOTE: to make the expansion animation of id/notification_messaging happen vertically,
+ its X positioning must be the left edge of the notification, so instead of putting the
+ layout_marginStart on the id/notification_headerless_view_row, we put it on
+ id/notification_top_line, making the layout here just a bit different from the base.
+ -->
+ <LinearLayout
+ android:id="@+id/notification_headerless_view_row"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ android:orientation="horizontal"
+ android:clipChildren="false"
+ >
+
+ <!--
+ NOTE: because messaging will always have 2 lines, this LinearLayout should NOT
+ have the id/notification_headerless_view_column, as that is used for modifying
+ vertical margins to accommodate the single-line state that base supports
+ -->
+ <LinearLayout
+ android:layout_width="0px"
+ android:layout_height="wrap_content"
+ android:layout_gravity="center_vertical"
+ android:layout_weight="1"
+ android:layout_marginBottom="@dimen/notification_2025_margin"
+ android:layout_marginTop="@dimen/notification_2025_margin"
+ android:layout_marginStart="@dimen/notification_2025_content_margin_start"
+ android:clipChildren="false"
+ android:orientation="vertical"
+ >
+
+ <NotificationTopLineView
+ android:id="@+id/notification_top_line"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:minHeight="@dimen/notification_headerless_line_height"
+ android:clipChildren="false"
+ android:theme="@style/Theme.DeviceDefault.Notification"
+ >
+
+ <!--
+ NOTE: The notification_2025_top_line_views layout contains the app_name_text.
+ In order to include the title view at the beginning, the Notification.Builder
+ has logic to hide that view whenever this title view is to be visible.
+ -->
+
+ <TextView
+ android:id="@+id/title"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_marginEnd="@dimen/notification_header_separating_margin"
+ android:ellipsize="end"
+ android:fadingEdge="horizontal"
+ android:singleLine="true"
+ android:textAlignment="viewStart"
+ android:textAppearance="@style/TextAppearance.DeviceDefault.Notification.Title"
+ />
+
+ <include layout="@layout/notification_2025_top_line_views" />
+
+ </NotificationTopLineView>
+
+ <LinearLayout
+ android:id="@+id/notification_main_column"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:orientation="vertical"
+ android:clipChildren="false"
+ >
+ <com.android.internal.widget.MessagingLinearLayout
+ android:id="@+id/notification_messaging"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:clipChildren="false"
+ android:spacing="@dimen/notification_messaging_spacing" />
+ </LinearLayout>
+
+ </LinearLayout>
+
+ <!-- Images -->
+ <com.android.internal.widget.MessagingLinearLayout
+ android:id="@+id/conversation_image_message_container"
+ android:layout_width="@dimen/notification_right_icon_size"
+ android:layout_height="@dimen/notification_right_icon_size"
+ android:layout_gravity="center_vertical|end"
+ android:layout_marginTop="@dimen/notification_right_icon_headerless_margin"
+ android:layout_marginBottom="@dimen/notification_right_icon_headerless_margin"
+ android:layout_marginStart="@dimen/notification_right_icon_content_margin"
+ android:forceHasOverlappingRendering="false"
+ android:spacing="0dp"
+ android:clipChildren="false"
+ android:visibility="gone"
+ />
+
+ <ImageView
+ android:id="@+id/right_icon"
+ android:layout_width="@dimen/notification_right_icon_size"
+ android:layout_height="@dimen/notification_right_icon_size"
+ android:layout_gravity="center_vertical|end"
+ android:layout_marginTop="@dimen/notification_right_icon_headerless_margin"
+ android:layout_marginBottom="@dimen/notification_right_icon_headerless_margin"
+ android:layout_marginStart="@dimen/notification_right_icon_content_margin"
+ android:background="@drawable/notification_large_icon_outline"
+ android:clipToOutline="true"
+ android:importantForAccessibility="no"
+ android:scaleType="centerCrop"
+ />
+
+ <FrameLayout
+ android:id="@+id/expand_button_touch_container"
+ android:layout_width="wrap_content"
+ android:layout_height="match_parent"
+ android:minWidth="@dimen/notification_content_margin_end"
+ >
+
+ <include layout="@layout/notification_2025_expand_button"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_gravity="top|end"
+ />
+
+ </FrameLayout>
+
+ </LinearLayout>
+
+ <include layout="@layout/notification_close_button"
+ android:id="@+id/close_button"
+ android:layout_width="@dimen/notification_close_button_size"
+ android:layout_height="@dimen/notification_close_button_size"
+ android:layout_gravity="top|end" />
+
+ </com.android.internal.widget.NotificationMaxHeightFrameLayout>
+
+ <LinearLayout
+ android:id="@+id/notification_action_list_margin_target"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:layout_marginTop="-20dp"
+ android:clipChildren="false"
+ android:orientation="vertical">
+ <include layout="@layout/notification_template_smart_reply_container"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:layout_marginTop="@dimen/notification_content_margin"
+ android:layout_marginStart="@dimen/notification_2025_content_margin_start"
+ android:layout_marginEnd="@dimen/notification_content_margin_end" />
+ <include layout="@layout/notification_material_action_list" />
+ </LinearLayout>
+</LinearLayout>
+</com.android.internal.widget.ConversationLayout>
diff --git a/core/res/res/layout/notification_2025_template_conversation.xml b/core/res/res/layout/notification_2025_template_conversation.xml
deleted file mode 100644
index 24b6ad09d3f7..000000000000
--- a/core/res/res/layout/notification_2025_template_conversation.xml
+++ /dev/null
@@ -1,159 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
- ~ Copyright (C) 2024 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
- -->
-
-<!-- extends FrameLayout -->
-<com.android.internal.widget.ConversationLayout
- xmlns:android="http://schemas.android.com/apk/res/android"
- android:id="@+id/status_bar_latest_event_content"
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:clipChildren="false"
- android:tag="conversation"
- android:theme="@style/Theme.DeviceDefault.Notification"
- >
-
- <include layout="@layout/notification_2025_conversation_icon_container" />
-
- <!-- Wraps entire "expandable" notification -->
- <com.android.internal.widget.RemeasuringLinearLayout
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:layout_gravity="top"
- android:clipToPadding="false"
- android:clipChildren="false"
- android:orientation="vertical"
- >
- <!-- LinearLayout for Expand Button-->
- <com.android.internal.widget.RemeasuringLinearLayout
- android:id="@+id/expand_button_and_content_container"
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:layout_weight="1"
- android:gravity="start|top"
- android:orientation="horizontal"
- android:clipChildren="false"
- android:clipToPadding="false">
- <!--TODO: move this into a separate layout and share logic with the header to bring back app opps etc-->
- <com.android.internal.widget.RemeasuringLinearLayout
- android:id="@+id/notification_action_list_margin_target"
- android:layout_width="0dp"
- android:orientation="vertical"
- android:layout_height="wrap_content"
- android:layout_weight="1">
-
- <!-- Header -->
-
- <!-- Use layout_marginStart instead of paddingStart to work around strange
- measurement behavior on lower display densities. -->
- <include
- layout="@layout/notification_2025_conversation_header"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:layout_marginBottom="2dp"
- android:layout_marginStart="@dimen/notification_2025_content_margin_start"
- />
-
- <!-- Messages -->
- <com.android.internal.widget.MessagingLinearLayout
- android:id="@+id/notification_messaging"
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:minHeight="@dimen/notification_text_size"
- android:spacing="@dimen/notification_messaging_spacing"
- android:clipToPadding="false"
- android:clipChildren="false"
- />
- </com.android.internal.widget.RemeasuringLinearLayout>
-
- <!-- This is where the expand button container will be placed when collapsed-->
- </com.android.internal.widget.RemeasuringLinearLayout>
-
- <include layout="@layout/notification_template_smart_reply_container"
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:layout_marginTop="@dimen/notification_content_margin"
- android:layout_marginStart="@dimen/notification_2025_content_margin_start"
- android:layout_marginEnd="@dimen/notification_content_margin_end" />
- <include layout="@layout/notification_material_action_list" />
- </com.android.internal.widget.RemeasuringLinearLayout>
-
- <!--expand_button_a11y_container ensures talkback focus order is correct when view is expanded.
- The -1px of marginTop and 1px of paddingTop make sure expand_button_a11y_container is prior to
- its sibling view in accessibility focus order.
- {see android.view.ViewGroup.addChildrenForAccessibility()}
- expand_button_container will be moved under expand_button_and_content_container when collapsed,
- this dynamic movement ensures message can flow under expand button when expanded-->
- <FrameLayout
- android:id="@+id/expand_button_a11y_container"
- android:layout_width="match_parent"
- android:layout_height="match_parent"
- android:layout_gravity="end|top"
- android:clipChildren="false"
- android:clipToPadding="false"
- android:layout_marginTop="-1px"
- android:paddingTop="1px"
- >
- <!--expand_button_container is dynamically placed between here and at the end of the
- layout. It starts here since only FrameLayout layout params have gravity-->
- <LinearLayout
- android:id="@+id/expand_button_container"
- android:layout_width="wrap_content"
- android:layout_height="match_parent"
- android:layout_gravity="end|top"
- android:clipChildren="false"
- android:clipToPadding="false"
- android:orientation="vertical">
- <include layout="@layout/notification_close_button"
- android:layout_width="@dimen/notification_close_button_size"
- android:layout_height="@dimen/notification_close_button_size"
- android:layout_gravity="end"
- android:layout_marginEnd="20dp"
- />
- <!--expand_button_touch_container makes sure that we can nicely center the expand
- content in the collapsed layout while the parent makes sure that we're never laid out
- bigger than the messaging content.-->
- <LinearLayout
- android:id="@+id/expand_button_touch_container"
- android:layout_width="wrap_content"
- android:layout_height="@dimen/conversation_expand_button_height"
- android:orientation="horizontal"
- android:layout_gravity="end|top"
- android:paddingEnd="0dp"
- android:clipToPadding="false"
- android:clipChildren="false"
- >
- <!-- Images -->
- <com.android.internal.widget.MessagingLinearLayout
- android:id="@+id/conversation_image_message_container"
- android:forceHasOverlappingRendering="false"
- android:layout_width="40dp"
- android:layout_height="40dp"
- android:layout_marginStart="@dimen/conversation_image_start_margin"
- android:spacing="0dp"
- android:layout_gravity="center"
- android:clipToPadding="false"
- android:clipChildren="false"
- />
- <include layout="@layout/notification_2025_expand_button"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:layout_gravity="top|end"
- />
- </LinearLayout>
- </LinearLayout>
- </FrameLayout>
-</com.android.internal.widget.ConversationLayout>
diff --git a/core/res/res/layout/notification_2025_template_expanded_call.xml b/core/res/res/layout/notification_2025_template_expanded_call.xml
index 0be61253c917..bbc29664d594 100644
--- a/core/res/res/layout/notification_2025_template_expanded_call.xml
+++ b/core/res/res/layout/notification_2025_template_expanded_call.xml
@@ -1,6 +1,6 @@
<?xml version="1.0" encoding="utf-8"?>
<!--
- ~ Copyright (C) 2024 The Android Open Source Project
+ ~ Copyright (C) 2025 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.
@@ -27,83 +27,47 @@
>
<!-- CallLayout shares visual appearance with ConversationLayout, so shares layouts -->
- <include layout="@layout/notification_2025_conversation_icon_container" />
+ <include layout="@layout/notification_2025_conversation_header"/>
- <LinearLayout
+ <com.android.internal.widget.RemeasuringLinearLayout
android:id="@+id/notification_action_list_margin_target"
android:layout_width="match_parent"
android:layout_height="wrap_content"
- android:layout_marginBottom="@dimen/notification_content_margin"
- android:orientation="vertical"
- >
-
- <LinearLayout
+ android:layout_gravity="top"
+ android:clipChildren="false"
+ android:orientation="vertical">
+
+ <!-- Note: the top margin is being set in code based on the estimated space needed for
+ the header text. -->
+ <com.android.internal.widget.RemeasuringLinearLayout
+ android:id="@+id/notification_main_column"
android:layout_width="match_parent"
android:layout_height="wrap_content"
+ android:layout_gravity="top"
android:layout_weight="1"
- android:orientation="horizontal"
+ android:layout_marginStart="@dimen/notification_2025_content_margin_start"
+ android:layout_marginEnd="@dimen/notification_content_margin_end"
+ android:orientation="vertical"
+ android:clipChildren="false"
>
- <LinearLayout
- android:id="@+id/notification_main_column"
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:layout_marginStart="@dimen/notification_2025_content_margin_start"
- android:layout_weight="1"
- android:orientation="vertical"
- android:minHeight="68dp"
- >
-
- <include
- layout="@layout/notification_2025_conversation_header"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- />
-
- <include layout="@layout/notification_template_text_multiline" />
-
- <include
- android:layout_width="match_parent"
- android:layout_height="@dimen/notification_progress_bar_height"
- android:layout_marginTop="@dimen/notification_progress_margin_top"
- layout="@layout/notification_template_progress"
- />
- </LinearLayout>
-
- <FrameLayout
- android:layout_width="wrap_content"
- android:layout_height="match_parent"
- android:minWidth="@dimen/notification_content_margin_end"
- >
-
- <include
- layout="@layout/notification_expand_button"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- />
+ <include layout="@layout/notification_template_part_line1"/>
- </FrameLayout>
+ <include layout="@layout/notification_template_text_multiline" />
- </LinearLayout>
+ </com.android.internal.widget.RemeasuringLinearLayout>
- <ViewStub
- android:layout="@layout/notification_material_reply_text"
- android:id="@+id/notification_material_reply_container"
+ <include layout="@layout/notification_template_smart_reply_container"
android:layout_width="match_parent"
android:layout_height="wrap_content"
- />
-
- <include
- layout="@layout/notification_template_smart_reply_container"
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:layout_marginStart="@dimen/notification_2025_content_margin_start"
- android:layout_marginEnd="@dimen/notification_content_margin_end"
android:layout_marginTop="@dimen/notification_content_margin"
- />
+ android:layout_marginStart="@dimen/notification_2025_content_margin_start"
+ android:layout_marginEnd="@dimen/notification_content_margin_end" />
<include layout="@layout/notification_material_action_list" />
- </LinearLayout>
+ </com.android.internal.widget.RemeasuringLinearLayout>
+
+ <include layout="@layout/notification_template_right_icon" />
</com.android.internal.widget.CallLayout>
diff --git a/core/res/res/layout/notification_2025_template_expanded_conversation.xml b/core/res/res/layout/notification_2025_template_expanded_conversation.xml
new file mode 100644
index 000000000000..d7e8bb3b6da2
--- /dev/null
+++ b/core/res/res/layout/notification_2025_template_expanded_conversation.xml
@@ -0,0 +1,75 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ ~ Copyright (C) 2025 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
+ -->
+
+<!-- extends FrameLayout -->
+<com.android.internal.widget.ConversationLayout
+ xmlns:android="http://schemas.android.com/apk/res/android"
+ android:id="@+id/status_bar_latest_event_content"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:clipToPadding="false"
+ android:clipChildren="false"
+ android:tag="conversation"
+ >
+
+ <include layout="@layout/notification_2025_conversation_header"/>
+
+ <com.android.internal.widget.RemeasuringLinearLayout
+ android:id="@+id/notification_action_list_margin_target"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:layout_gravity="top"
+ android:clipChildren="false"
+ android:orientation="vertical">
+
+ <!-- Note: the top margin is being set in code based on the estimated space needed for
+ the header text. -->
+ <com.android.internal.widget.RemeasuringLinearLayout
+ android:id="@+id/notification_main_column"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:layout_gravity="top"
+ android:layout_weight="1"
+ android:layout_marginEnd="@dimen/notification_content_margin_end"
+ android:orientation="vertical"
+ android:clipChildren="false"
+ >
+
+ <include layout="@layout/notification_template_part_line1"/>
+
+ <com.android.internal.widget.MessagingLinearLayout
+ android:id="@+id/notification_messaging"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:clipChildren="false"
+ android:spacing="@dimen/notification_messaging_spacing" />
+ </com.android.internal.widget.RemeasuringLinearLayout>
+
+ <include layout="@layout/notification_template_smart_reply_container"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:layout_marginTop="@dimen/notification_content_margin"
+ android:layout_marginStart="@dimen/notification_2025_content_margin_start"
+ android:layout_marginEnd="@dimen/notification_content_margin_end" />
+
+ <include layout="@layout/notification_material_action_list" />
+
+ </com.android.internal.widget.RemeasuringLinearLayout>
+
+ <include layout="@layout/notification_template_right_icon" />
+
+</com.android.internal.widget.ConversationLayout>
diff --git a/core/res/res/layout/notification_2025_template_expanded_messaging.xml b/core/res/res/layout/notification_2025_template_expanded_messaging.xml
index 177706c6d58d..20abfee6a4b6 100644
--- a/core/res/res/layout/notification_2025_template_expanded_messaging.xml
+++ b/core/res/res/layout/notification_2025_template_expanded_messaging.xml
@@ -36,17 +36,21 @@
android:clipChildren="false"
android:orientation="vertical">
+ <!-- Note: the top margin is being set in code based on the estimated space needed for
+ the header text. -->
<com.android.internal.widget.RemeasuringLinearLayout
android:id="@+id/notification_main_column"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_gravity="top"
android:layout_weight="1"
- android:layout_marginTop="@dimen/notification_2025_header_height"
android:layout_marginEnd="@dimen/notification_content_margin_end"
android:orientation="vertical"
android:clipChildren="false"
>
+
+ <include layout="@layout/notification_template_part_line1"/>
+
<com.android.internal.widget.MessagingLinearLayout
android:id="@+id/notification_messaging"
android:layout_width="match_parent"
diff --git a/core/res/res/layout/notification_2025_top_line_views.xml b/core/res/res/layout/notification_2025_top_line_views.xml
index 74873463391e..7431c421ed3d 100644
--- a/core/res/res/layout/notification_2025_top_line_views.xml
+++ b/core/res/res/layout/notification_2025_top_line_views.xml
@@ -20,7 +20,7 @@
<merge
xmlns:android="http://schemas.android.com/apk/res/android">
- <TextView
+ <com.android.internal.widget.ObservableTextView
android:id="@+id/app_name_text"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
@@ -108,6 +108,42 @@
android:visibility="gone"
/>
+ <TextView
+ android:id="@+id/verification_divider"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:textAppearance="@style/TextAppearance.DeviceDefault.Notification.Info"
+ android:layout_marginStart="@dimen/notification_header_separating_margin"
+ android:layout_marginEnd="@dimen/notification_header_separating_margin"
+ android:text="@string/notification_header_divider_symbol"
+ android:singleLine="true"
+ android:visibility="gone"
+ />
+
+ <ImageView
+ android:id="@+id/verification_icon"
+ android:layout_width="@dimen/notification_2025_badge_size"
+ android:layout_height="@dimen/notification_2025_badge_size"
+ android:layout_marginStart="@dimen/notification_header_separating_margin"
+ android:layout_marginEnd="@dimen/notification_header_separating_margin"
+ android:baseline="@dimen/notification_2025_badge_baseline"
+ android:scaleType="fitCenter"
+ android:visibility="gone"
+ />
+
+ <TextView
+ android:id="@+id/verification_text"
+ android:textAppearance="@style/TextAppearance.DeviceDefault.Notification.Info"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_marginStart="@dimen/notification_header_separating_margin"
+ android:layout_marginEnd="@dimen/notification_header_separating_margin"
+ android:layout_weight="100"
+ android:showRelative="true"
+ android:singleLine="true"
+ android:visibility="gone"
+ />
+
<ImageButton
android:id="@+id/feedback"
android:layout_width="@dimen/notification_feedback_size"
diff --git a/core/res/res/values/config_telephony.xml b/core/res/res/values/config_telephony.xml
index f2ec56c69374..59ed25a1865f 100644
--- a/core/res/res/values/config_telephony.xml
+++ b/core/res/res/values/config_telephony.xml
@@ -64,6 +64,13 @@
<integer name="auto_data_switch_performance_stability_time_threshold_millis">120000</integer>
<java-symbol type="integer" name="auto_data_switch_performance_stability_time_threshold_millis" />
+ <!-- Define the bar for switching data back to the default SIM when both SIMs are out of service
+ in milliseconds. A value of 0 means an immediate switch, otherwise for a negative value,
+ the threshold defined by auto_data_switch_availability_stability_time_threshold_millis
+ will be used instead. -->
+ <integer name="auto_data_switch_availability_switchback_stability_time_threshold_millis">150000</integer>
+ <java-symbol type="integer" name="auto_data_switch_availability_switchback_stability_time_threshold_millis" />
+
<!-- Define the maximum retry times when a validation for switching failed.-->
<integer name="auto_data_switch_validation_max_retry">7</integer>
<java-symbol type="integer" name="auto_data_switch_validation_max_retry" />
diff --git a/core/res/res/values/symbols.xml b/core/res/res/values/symbols.xml
index 26f0ab3f28e1..b013ffd41ecb 100644
--- a/core/res/res/values/symbols.xml
+++ b/core/res/res/values/symbols.xml
@@ -3264,6 +3264,7 @@
<java-symbol type="dimen" name="notification_content_margin" />
<java-symbol type="dimen" name="notification_2025_margin" />
<java-symbol type="dimen" name="notification_2025_content_margin_top" />
+ <java-symbol type="dimen" name="notification_2025_content_margin_start" />
<java-symbol type="dimen" name="notification_2025_expand_button_horizontal_icon_padding" />
<java-symbol type="dimen" name="notification_2025_expand_button_reduced_end_padding" />
<java-symbol type="dimen" name="notification_progress_margin_horizontal" />
@@ -4705,7 +4706,8 @@
<java-symbol type="dimen" name="conversation_icon_container_top_padding" />
<java-symbol type="dimen" name="conversation_icon_container_top_padding_small_avatar" />
<java-symbol type="layout" name="notification_template_material_conversation" />
- <java-symbol type="layout" name="notification_2025_template_conversation" />
+ <java-symbol type="layout" name="notification_2025_template_collapsed_conversation" />
+ <java-symbol type="layout" name="notification_2025_template_expanded_conversation" />
<java-symbol type="dimen" name="button_padding_horizontal_material" />
<java-symbol type="dimen" name="button_inset_horizontal_material" />
<java-symbol type="layout" name="conversation_face_pile_layout" />
diff --git a/core/tests/coretests/Android.bp b/core/tests/coretests/Android.bp
index 4c49ff849d49..05fb5735972e 100644
--- a/core/tests/coretests/Android.bp
+++ b/core/tests/coretests/Android.bp
@@ -249,8 +249,14 @@ android_library {
android_ravenwood_test {
name: "FrameworksCoreTestsRavenwood",
libs: [
- "android.test.base.stubs.system",
- "android.test.runner.stubs.system",
+ "android.test.base.stubs",
+ "android.test.mock.stubs",
+ "android.test.runner.stubs",
+ "android.view.flags-aconfig-java",
+ "ext",
+ "framework",
+ "framework-res",
+ "org.apache.http.legacy.stubs",
],
static_libs: [
"androidx.annotation_annotation",
@@ -264,6 +270,7 @@ android_ravenwood_test {
"flag-junit",
"flag-junit",
"perfetto_trace_java_protos",
+ "platform-compat-test-rules",
"platform-test-annotations",
"testng",
],
@@ -278,8 +285,12 @@ android_ravenwood_test {
"src/android/content/res/*.java",
"src/android/content/res/*.kt",
"src/android/database/CursorWindowTest.java",
+ "src/android/graphics/*.java",
+ "src/android/graphics/*.kt",
"src/android/os/**/*.java",
"src/android/telephony/PinResultTest.java",
+ "src/android/text/**/*.java",
+ "src/android/text/**/*.kt",
"src/android/util/**/*.java",
"src/android/view/DisplayAdjustmentsTests.java",
"src/android/view/DisplayInfoTest.java",
@@ -288,20 +299,21 @@ android_ravenwood_test {
"src/com/android/internal/os/**/*.java",
"src/com/android/internal/power/EnergyConsumerStatsTest.java",
"src/com/android/internal/ravenwood/**/*.java",
-
- // Pull in R.java from FrameworksCoreTests-resonly, not from FrameworksCoreTests,
- // to avoid having a dependency to FrameworksCoreTests.
- // This way, when updating source files and running this test, we don't need to
- // rebuild the entire FrameworksCoreTests, which would be slow.
"src/com/android/internal/util/**/*.java",
":FrameworksCoreTestDoubles-sources",
":FrameworksCoreTests-aidl",
":FrameworksCoreTests-helpers",
+
+ // Pull in R.java from FrameworksCoreTests-resonly, not from FrameworksCoreTests,
+ // to avoid having a dependency to FrameworksCoreTests.
+ // This way, when updating source files and running this test, we don't need to
+ // rebuild the entire FrameworksCoreTests, which would be slow.
":FrameworksCoreTests-resonly{.aapt.srcjar}",
],
exclude_srcs: [
"src/android/content/res/FontScaleConverterActivityTest.java",
+ "src/android/graphics/GraphicsPerformanceTests.java",
],
resource_apk: "FrameworksCoreTests-resonly",
aidl: {
@@ -313,6 +325,7 @@ android_ravenwood_test {
"res/xml/power_profile_test_cpu_legacy.xml",
"res/xml/power_profile_test_modem.xml",
],
+ sdk_version: "core_platform",
auto_gen_config: true,
team: "trendy_team_ravenwood",
}
diff --git a/core/tests/coretests/src/android/content/res/FontScaleConverterTest.kt b/core/tests/coretests/src/android/content/res/FontScaleConverterTest.kt
index 0e5d92688123..2c614424a9a5 100644
--- a/core/tests/coretests/src/android/content/res/FontScaleConverterTest.kt
+++ b/core/tests/coretests/src/android/content/res/FontScaleConverterTest.kt
@@ -17,10 +17,8 @@
package android.content.res
import android.platform.test.annotations.Presubmit
-import android.platform.test.ravenwood.RavenwoodRule
import androidx.test.ext.junit.runners.AndroidJUnit4
import com.google.common.truth.Truth.assertWithMessage
-import org.junit.Rule
import org.junit.Test
import org.junit.runner.RunWith
@@ -28,9 +26,6 @@ import org.junit.runner.RunWith
@RunWith(AndroidJUnit4::class)
class FontScaleConverterTest {
- @get:Rule
- val ravenwoodRule: RavenwoodRule = RavenwoodRule.Builder().build()
-
@Test
fun straightInterpolation() {
val table = createTable(8f to 8f, 10f to 10f, 20f to 20f)
diff --git a/core/tests/coretests/src/android/graphics/BitmapFactoryTest.java b/core/tests/coretests/src/android/graphics/BitmapFactoryTest.java
index 84bdbe03df13..263307ee3df7 100644
--- a/core/tests/coretests/src/android/graphics/BitmapFactoryTest.java
+++ b/core/tests/coretests/src/android/graphics/BitmapFactoryTest.java
@@ -20,7 +20,9 @@ import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertTrue;
+import android.os.MemoryFile;
import android.os.ParcelFileDescriptor;
+import android.platform.test.annotations.DisabledOnRavenwood;
import androidx.test.ext.junit.runners.AndroidJUnit4;
import androidx.test.filters.SmallTest;
@@ -37,6 +39,7 @@ public class BitmapFactoryTest {
// tests that we can decode bitmaps from MemoryFiles
@SmallTest
@Test
+ @DisabledOnRavenwood(blockedBy = MemoryFile.class)
public void testBitmapParcelFileDescriptor() throws Exception {
Bitmap bitmap1 = Bitmap.createBitmap(
new int[] { Color.BLUE }, 1, 1, Bitmap.Config.RGB_565);
diff --git a/core/tests/coretests/src/android/graphics/BitmapTest.java b/core/tests/coretests/src/android/graphics/BitmapTest.java
index 0126d367eb20..61c3d7813e88 100644
--- a/core/tests/coretests/src/android/graphics/BitmapTest.java
+++ b/core/tests/coretests/src/android/graphics/BitmapTest.java
@@ -22,6 +22,7 @@ import static org.junit.Assert.assertTrue;
import static org.junit.Assert.fail;
import android.hardware.HardwareBuffer;
+import android.platform.test.annotations.DisabledOnRavenwood;
import androidx.test.ext.junit.runners.AndroidJUnit4;
import androidx.test.filters.SmallTest;
@@ -252,6 +253,7 @@ public class BitmapTest {
| GraphicBuffer.USAGE_SW_WRITE_OFTEN;
@Test
+ @DisabledOnRavenwood(blockedBy = HardwareBuffer.class)
public void testWrapHardwareBufferWithSrgbColorSpace() {
GraphicBuffer buffer = GraphicBuffer.create(10, 10, PixelFormat.RGBA_8888, GRAPHICS_USAGE);
Canvas canvas = buffer.lockCanvas();
@@ -265,6 +267,7 @@ public class BitmapTest {
}
@Test
+ @DisabledOnRavenwood(blockedBy = HardwareBuffer.class)
public void testWrapHardwareBufferWithDisplayP3ColorSpace() {
GraphicBuffer buffer = GraphicBuffer.create(10, 10, PixelFormat.RGBA_8888, GRAPHICS_USAGE);
Canvas canvas = buffer.lockCanvas();
diff --git a/core/tests/coretests/src/android/graphics/PaintTest.java b/core/tests/coretests/src/android/graphics/PaintTest.java
index 56760d77e28b..deb5157bb339 100644
--- a/core/tests/coretests/src/android/graphics/PaintTest.java
+++ b/core/tests/coretests/src/android/graphics/PaintTest.java
@@ -24,6 +24,7 @@ import static org.junit.Assert.assertNotEquals;
import static org.junit.Assert.assertTrue;
import static org.junit.Assert.fail;
+import android.platform.test.annotations.DisabledOnRavenwood;
import android.platform.test.annotations.RequiresFlagsEnabled;
import android.platform.test.flag.junit.CheckFlagsRule;
import android.platform.test.flag.junit.DeviceFlagsValueProvider;
@@ -97,6 +98,7 @@ public class PaintTest {
@SmallTest
@Test
+ @DisabledOnRavenwood(bug = 391381043)
public void testHintingWidth() {
final Typeface fontTypeface = Typeface.createFromAsset(
InstrumentationRegistry.getInstrumentation().getContext().getAssets(), FONT_PATH);
@@ -143,6 +145,7 @@ public class PaintTest {
}
@Test
+ @DisabledOnRavenwood(bug = 391381043)
public void testHasGlyph_variationSelectors() {
final Typeface fontTypeface = Typeface.createFromAsset(
InstrumentationRegistry.getInstrumentation().getContext().getAssets(),
diff --git a/core/tests/coretests/src/android/graphics/TypefaceSystemFallbackTest.java b/core/tests/coretests/src/android/graphics/TypefaceSystemFallbackTest.java
index 2b6eda8f0988..dc3376e09b15 100644
--- a/core/tests/coretests/src/android/graphics/TypefaceSystemFallbackTest.java
+++ b/core/tests/coretests/src/android/graphics/TypefaceSystemFallbackTest.java
@@ -35,9 +35,9 @@ import android.platform.test.flag.junit.DeviceFlagsValueProvider;
import android.text.FontConfig;
import android.util.ArrayMap;
-import androidx.test.InstrumentationRegistry;
import androidx.test.ext.junit.runners.AndroidJUnit4;
import androidx.test.filters.SmallTest;
+import androidx.test.platform.app.InstrumentationRegistry;
import com.android.text.flags.Flags;
@@ -63,9 +63,6 @@ import java.util.Map;
@SmallTest
@RunWith(AndroidJUnit4.class)
public class TypefaceSystemFallbackTest {
- private static final String SYSTEM_FONT_DIR = "/system/fonts/";
- private static final String SYSTEM_FONTS_XML = "/system/etc/fonts.xml";
-
private static final String[] TEST_FONT_FILES = {
"a3em.ttf", // Supports "a","b","c". The width of "a" is 3em, others are 1em.
"b3em.ttf", // Supports "a","b","c". The width of "b" is 3em, others are 1em.
@@ -118,8 +115,6 @@ public class TypefaceSystemFallbackTest {
@Before
public void setUp() {
- final AssetManager am =
- InstrumentationRegistry.getInstrumentation().getContext().getAssets();
for (final String fontFile : TEST_FONT_FILES) {
final String sourceInAsset = "fonts/" + fontFile;
copyAssetToFile(sourceInAsset, new File(TEST_FONT_DIR, fontFile));
@@ -216,7 +211,8 @@ public class TypefaceSystemFallbackTest {
FontConfig fontConfig;
try {
fontConfig = FontListParser.parse(
- SYSTEM_FONTS_XML, SYSTEM_FONT_DIR, null, TEST_OEM_DIR, null, 0, 0);
+ SystemFonts.LEGACY_FONTS_XML, SystemFonts.SYSTEM_FONT_DIR,
+ null, TEST_OEM_DIR, null, 0, 0);
} catch (IOException | XmlPullParserException e) {
throw new RuntimeException(e);
}
diff --git a/core/tests/coretests/src/android/graphics/TypefaceTest.java b/core/tests/coretests/src/android/graphics/TypefaceTest.java
index 80efa511d163..0c8b5ab5f3f9 100644
--- a/core/tests/coretests/src/android/graphics/TypefaceTest.java
+++ b/core/tests/coretests/src/android/graphics/TypefaceTest.java
@@ -26,6 +26,7 @@ import android.content.res.Resources;
import android.graphics.fonts.FontFamily;
import android.graphics.fonts.SystemFonts;
import android.os.SharedMemory;
+import android.platform.test.annotations.DisabledOnRavenwood;
import android.text.FontConfig;
import android.util.ArrayMap;
@@ -196,6 +197,7 @@ public class TypefaceTest {
@SmallTest
@Test
+ @DisabledOnRavenwood(blockedBy = SharedMemory.class)
public void testSerialize() throws Exception {
FontConfig fontConfig = SystemFonts.getSystemPreinstalledFontConfig();
Map<String, FontFamily[]> fallbackMap = SystemFonts.buildSystemFallback(fontConfig);
diff --git a/core/tests/coretests/src/android/hardware/display/DisplayManagerGlobalTest.java b/core/tests/coretests/src/android/hardware/display/DisplayManagerGlobalTest.java
index 9383807ec761..8ef105f79988 100644
--- a/core/tests/coretests/src/android/hardware/display/DisplayManagerGlobalTest.java
+++ b/core/tests/coretests/src/android/hardware/display/DisplayManagerGlobalTest.java
@@ -56,6 +56,7 @@ import org.mockito.Mock;
import org.mockito.Mockito;
import org.mockito.MockitoAnnotations;
+import java.util.concurrent.CopyOnWriteArrayList;
import java.util.concurrent.Executor;
import java.util.function.Consumer;
@@ -116,7 +117,8 @@ public class DisplayManagerGlobalTest {
@Test
public void testDisplayListenerIsCalled_WhenDisplayEventOccurs() throws RemoteException {
mDisplayManagerGlobal.registerDisplayListener(mDisplayListener, mHandler,
- ALL_DISPLAY_EVENTS, /* packageName= */ null);
+ ALL_DISPLAY_EVENTS, /* packageName= */ null,
+ /* isEventFilterExplicit */ true);
Mockito.verify(mDisplayManager)
.registerCallbackWithEventMask(mCallbackCaptor.capture(), anyLong());
IDisplayManagerCallback callback = mCallbackCaptor.getValue();
@@ -151,7 +153,7 @@ public class DisplayManagerGlobalTest {
mDisplayManagerGlobal.registerDisplayListener(mDisplayListener, mHandler,
INTERNAL_EVENT_FLAG_DISPLAY_REFRESH_RATE
| INTERNAL_EVENT_FLAG_DISPLAY_STATE,
- null);
+ null, /* isEventFilterExplicit */ true);
Mockito.verify(mDisplayManager)
.registerCallbackWithEventMask(mCallbackCaptor.capture(), anyLong());
IDisplayManagerCallback callback = mCallbackCaptor.getValue();
@@ -172,11 +174,80 @@ public class DisplayManagerGlobalTest {
}
@Test
+ @RequiresFlagsEnabled(Flags.FLAG_DELAY_IMPLICIT_RR_REGISTRATION_UNTIL_RR_ACCESSED)
+ public void test_refreshRateRegistration_implicitRRCallbacksEnabled()
+ throws RemoteException {
+ // Subscription without supplied events doesn't subscribe to RR events
+ mDisplayManagerGlobal.registerDisplayListener(mDisplayListener, mHandler,
+ ALL_DISPLAY_EVENTS, /* packageName= */ null,
+ /* isEventFilterExplicit */ false);
+ Mockito.verify(mDisplayManager)
+ .registerCallbackWithEventMask(mCallbackCaptor.capture(), eq(ALL_DISPLAY_EVENTS));
+
+ // After registering to refresh rate changes, subscription without supplied events subscribe
+ // to RR events
+ mDisplayManagerGlobal.registerForRefreshRateChanges();
+ mDisplayManagerGlobal.registerDisplayListener(mDisplayListener, mHandler,
+ ALL_DISPLAY_EVENTS, /* packageName= */ null,
+ /* isEventFilterExplicit */ false);
+ Mockito.verify(mDisplayManager)
+ .registerCallbackWithEventMask(mCallbackCaptor.capture(), eq(ALL_DISPLAY_EVENTS
+ | INTERNAL_EVENT_FLAG_DISPLAY_REFRESH_RATE));
+
+ // Assert all the existing listeners are also subscribed to RR events
+ CopyOnWriteArrayList<DisplayManagerGlobal.DisplayListenerDelegate> delegates =
+ mDisplayManagerGlobal.getDisplayListeners();
+ for (DisplayManagerGlobal.DisplayListenerDelegate delegate: delegates) {
+ assertEquals(ALL_DISPLAY_EVENTS | INTERNAL_EVENT_FLAG_DISPLAY_REFRESH_RATE,
+ delegate.mInternalEventFlagsMask);
+ }
+
+ // Subscription to RR when events are supplied doesn't happen
+ mDisplayManagerGlobal.registerDisplayListener(mDisplayListener, mHandler,
+ ALL_DISPLAY_EVENTS, /* packageName= */ null,
+ /* isEventFilterExplicit */ true);
+ Mockito.verify(mDisplayManager)
+ .registerCallbackWithEventMask(mCallbackCaptor.capture(), eq(ALL_DISPLAY_EVENTS));
+
+ // Assert one listeners are not subscribed to RR events
+ delegates = mDisplayManagerGlobal.getDisplayListeners();
+ int subscribedListenersCount = 0;
+ int nonSubscribedListenersCount = 0;
+ for (DisplayManagerGlobal.DisplayListenerDelegate delegate: delegates) {
+
+ if (delegate.isEventFilterExplicit()) {
+ assertEquals(ALL_DISPLAY_EVENTS, delegate.mInternalEventFlagsMask);
+ nonSubscribedListenersCount++;
+ } else {
+ assertEquals(ALL_DISPLAY_EVENTS | INTERNAL_EVENT_FLAG_DISPLAY_REFRESH_RATE,
+ delegate.mInternalEventFlagsMask);
+ subscribedListenersCount++;
+ }
+ }
+
+ assertEquals(2, subscribedListenersCount);
+ assertEquals(1, nonSubscribedListenersCount);
+ }
+
+ @Test
+ @RequiresFlagsDisabled(Flags.FLAG_DELAY_IMPLICIT_RR_REGISTRATION_UNTIL_RR_ACCESSED)
+ public void test_refreshRateRegistration_implicitRRCallbacksDisabled()
+ throws RemoteException {
+ mDisplayManagerGlobal.registerDisplayListener(mDisplayListener, mHandler,
+ ALL_DISPLAY_EVENTS, /* packageName= */ null,
+ /* isEventFilterExplicit */ false);
+ Mockito.verify(mDisplayManager)
+ .registerCallbackWithEventMask(mCallbackCaptor.capture(), eq(ALL_DISPLAY_EVENTS
+ | INTERNAL_EVENT_FLAG_DISPLAY_REFRESH_RATE));
+ }
+
+ @Test
public void testDisplayListenerIsNotCalled_WhenClientIsNotSubscribed() throws RemoteException {
// First we subscribe to all events in order to test that the subsequent calls to
// registerDisplayListener will update the event mask.
mDisplayManagerGlobal.registerDisplayListener(mDisplayListener, mHandler,
- ALL_DISPLAY_EVENTS, /* packageName= */ null);
+ ALL_DISPLAY_EVENTS, /* packageName= */ null,
+ /* isEventFilterExplicit */ true);
Mockito.verify(mDisplayManager)
.registerCallbackWithEventMask(mCallbackCaptor.capture(), anyLong());
IDisplayManagerCallback callback = mCallbackCaptor.getValue();
@@ -184,21 +255,24 @@ public class DisplayManagerGlobalTest {
int displayId = 1;
mDisplayManagerGlobal.registerDisplayListener(mDisplayListener, mHandler,
ALL_DISPLAY_EVENTS
- & ~DisplayManagerGlobal.INTERNAL_EVENT_FLAG_DISPLAY_ADDED, null);
+ & ~DisplayManagerGlobal.INTERNAL_EVENT_FLAG_DISPLAY_ADDED, null,
+ /* isEventFilterExplicit */ true);
callback.onDisplayEvent(displayId, DisplayManagerGlobal.EVENT_DISPLAY_ADDED);
waitForHandler();
Mockito.verifyZeroInteractions(mDisplayListener);
mDisplayManagerGlobal.registerDisplayListener(mDisplayListener, mHandler,
ALL_DISPLAY_EVENTS
- & ~DISPLAY_CHANGE_EVENTS, null);
+ & ~DISPLAY_CHANGE_EVENTS, null,
+ /* isEventFilterExplicit */ true);
callback.onDisplayEvent(displayId, DisplayManagerGlobal.EVENT_DISPLAY_BASIC_CHANGED);
waitForHandler();
Mockito.verifyZeroInteractions(mDisplayListener);
mDisplayManagerGlobal.registerDisplayListener(mDisplayListener, mHandler,
ALL_DISPLAY_EVENTS
- & ~DisplayManagerGlobal.INTERNAL_EVENT_FLAG_DISPLAY_REMOVED, null);
+ & ~DisplayManagerGlobal.INTERNAL_EVENT_FLAG_DISPLAY_REMOVED, null,
+ /* isEventFilterExplicit */ true);
callback.onDisplayEvent(displayId, DisplayManagerGlobal.EVENT_DISPLAY_REMOVED);
waitForHandler();
Mockito.verifyZeroInteractions(mDisplayListener);
@@ -218,11 +292,34 @@ public class DisplayManagerGlobalTest {
}
@Test
+ @RequiresFlagsEnabled(Flags.FLAG_DELAY_IMPLICIT_RR_REGISTRATION_UNTIL_RR_ACCESSED)
+ public void test_registerNativeRefreshRateCallbacks_enablesRRImplicitRegistrations()
+ throws RemoteException {
+ // Registering the display listener without supplied events doesn't subscribe to RR events
+ mDisplayManagerGlobal.registerDisplayListener(mDisplayListener, mHandler,
+ ALL_DISPLAY_EVENTS, /* packageName= */ null,
+ /* isEventFilterExplicit */ false);
+ Mockito.verify(mDisplayManager)
+ .registerCallbackWithEventMask(mCallbackCaptor.capture(), eq(ALL_DISPLAY_EVENTS));
+
+ // Native subscription for refresh rates is done
+ mDisplayManagerGlobal.registerNativeChoreographerForRefreshRateCallbacks();
+
+ // Registering the display listener without supplied events subscribe to RR events
+ mDisplayManagerGlobal.registerDisplayListener(mDisplayListener, mHandler,
+ ALL_DISPLAY_EVENTS, /* packageName= */ null,
+ /* isEventFilterExplicit */ false);
+ Mockito.verify(mDisplayManager)
+ .registerCallbackWithEventMask(mCallbackCaptor.capture(), eq(ALL_DISPLAY_EVENTS
+ | INTERNAL_EVENT_FLAG_DISPLAY_REFRESH_RATE));
+ }
+
+ @Test
public void testDisplayManagerGlobalRegistersWithDisplayManager_WhenThereAreListeners()
throws RemoteException {
mDisplayManagerGlobal.registerDisplayListener(mDisplayListener, mHandler,
DisplayManagerGlobal.INTERNAL_EVENT_FLAG_DISPLAY_BRIGHTNESS_CHANGED,
- null);
+ null, /* isEventFilterExplicit */ true);
InOrder inOrder = Mockito.inOrder(mDisplayManager);
inOrder.verify(mDisplayManager)
@@ -260,9 +357,10 @@ public class DisplayManagerGlobalTest {
mDisplayManagerGlobal.registerDisplayListener(mDisplayListener, mHandler,
DisplayManagerGlobal.INTERNAL_EVENT_FLAG_DISPLAY_ADDED
| DisplayManagerGlobal.INTERNAL_EVENT_FLAG_DISPLAY_REMOVED,
- null /* packageName */);
+ null /* packageName */, /* isEventFilterExplicit */ true);
mDisplayManagerGlobal.registerDisplayListener(mDisplayListener2, mHandler,
- DISPLAY_CHANGE_EVENTS, null /* packageName */);
+ DISPLAY_CHANGE_EVENTS, null /* packageName */,
+ /* isEventFilterExplicit */ true);
mDisplayManagerGlobal.handleDisplayChangeFromWindowManager(321);
waitForHandler();
diff --git a/core/tests/coretests/src/android/os/BundleMergerTest.java b/core/tests/coretests/src/android/os/BundleMergerTest.java
index 43ed821e647d..53799935e118 100644
--- a/core/tests/coretests/src/android/os/BundleMergerTest.java
+++ b/core/tests/coretests/src/android/os/BundleMergerTest.java
@@ -18,6 +18,7 @@ package android.os;
import static android.os.BundleMerger.STRATEGY_ARRAY_APPEND;
import static android.os.BundleMerger.STRATEGY_ARRAY_LIST_APPEND;
+import static android.os.BundleMerger.STRATEGY_ARRAY_UNION;
import static android.os.BundleMerger.STRATEGY_BOOLEAN_AND;
import static android.os.BundleMerger.STRATEGY_BOOLEAN_OR;
import static android.os.BundleMerger.STRATEGY_COMPARABLE_MAX;
@@ -28,6 +29,7 @@ import static android.os.BundleMerger.STRATEGY_NUMBER_ADD;
import static android.os.BundleMerger.STRATEGY_NUMBER_INCREMENT_FIRST;
import static android.os.BundleMerger.STRATEGY_NUMBER_INCREMENT_FIRST_AND_ADD;
import static android.os.BundleMerger.STRATEGY_REJECT;
+import static android.os.BundleMerger.STRATEGY_STRING_APPEND;
import static android.os.BundleMerger.merge;
import static org.junit.Assert.assertArrayEquals;
@@ -204,6 +206,33 @@ public class BundleMergerTest {
}
@Test
+ public void testStrategyArrayUnion() throws Exception {
+ assertArrayEquals(new int[] {},
+ (int[]) merge(STRATEGY_ARRAY_UNION, new int[] {}, new int[] {}));
+ assertArrayEquals(new int[] {10},
+ (int[]) merge(STRATEGY_ARRAY_UNION, new int[] {10}, new int[] {}));
+ assertArrayEquals(new int[] {20},
+ (int[]) merge(STRATEGY_ARRAY_UNION, new int[] {}, new int[] {20}));
+ assertArrayEquals(new int[] {10, 20},
+ (int[]) merge(STRATEGY_ARRAY_UNION, new int[] {10}, new int[] {20}));
+ assertArrayEquals(new int[] {10, 20, 30, 40},
+ (int[]) merge(STRATEGY_ARRAY_UNION, new int[] {10, 30}, new int[] {20, 40}));
+ assertArrayEquals(new int[] {10, 20, 30},
+ (int[]) merge(STRATEGY_ARRAY_UNION, new int[] {10, 30}, new int[] {10, 20}));
+ assertArrayEquals(new int[] {10, 20},
+ (int[]) merge(STRATEGY_ARRAY_UNION, new int[] {10, 20}, new int[] {20}));
+ assertArrayEquals(new String[] {"a", "b"},
+ (String[]) merge(STRATEGY_ARRAY_UNION, new String[] {"a"}, new String[] {"b"}));
+ assertArrayEquals(new String[] {"a", "b", "c"},
+ (String[]) merge(STRATEGY_ARRAY_UNION, new String[] {"a", "b"},
+ new String[] {"b", "c"}));
+
+ assertThrows(Exception.class, () -> {
+ merge(STRATEGY_ARRAY_UNION, 10, 20);
+ });
+ }
+
+ @Test
public void testStrategyArrayListAppend() throws Exception {
assertEquals(arrayListOf(),
merge(STRATEGY_ARRAY_LIST_APPEND, arrayListOf(), arrayListOf()));
@@ -224,6 +253,18 @@ public class BundleMergerTest {
}
@Test
+ public void testStrategyStringAppend() throws Exception {
+ assertEquals("ab", merge(STRATEGY_STRING_APPEND, "a", "b"));
+ assertEquals("abc", merge(STRATEGY_STRING_APPEND, "a", "bc"));
+ assertEquals("abc", merge(STRATEGY_STRING_APPEND, "ab", "c"));
+ assertEquals("a,b,c,", merge(STRATEGY_STRING_APPEND, "a,", "b,c,"));
+
+ assertThrows(Exception.class, () -> {
+ merge(STRATEGY_STRING_APPEND, 10, 20);
+ });
+ }
+
+ @Test
public void testSetDefaultMergeStrategy() throws Exception {
final BundleMerger merger = new BundleMerger();
merger.setDefaultMergeStrategy(STRATEGY_FIRST);
diff --git a/core/tests/coretests/src/android/os/PerfettoTraceTest.java b/core/tests/coretests/src/android/os/PerfettoTraceTest.java
index fb743d2b42ad..69150150d6f9 100644
--- a/core/tests/coretests/src/android/os/PerfettoTraceTest.java
+++ b/core/tests/coretests/src/android/os/PerfettoTraceTest.java
@@ -108,8 +108,8 @@ public class PerfettoTraceTest {
PerfettoTrace.Session session = new PerfettoTrace.Session(true, traceConfig.toByteArray());
PerfettoTrace.instant(FOO_CATEGORY, "event")
- .addFlow(2)
- .addTerminatingFlow(3)
+ .setFlow(2)
+ .setTerminatingFlow(3)
.addArg("long_val", 10000000000L)
.addArg("bool_val", true)
.addArg("double_val", 3.14)
diff --git a/core/tests/coretests/src/android/text/AndroidCharacterTest.java b/core/tests/coretests/src/android/text/AndroidCharacterTest.java
index 1c5986a838fc..819a5fb8fd40 100644
--- a/core/tests/coretests/src/android/text/AndroidCharacterTest.java
+++ b/core/tests/coretests/src/android/text/AndroidCharacterTest.java
@@ -18,6 +18,7 @@ package android.text;
import static org.junit.Assert.assertArrayEquals;
+import android.platform.test.annotations.DisabledOnRavenwood;
import android.platform.test.annotations.Presubmit;
import androidx.test.filters.SmallTest;
@@ -26,6 +27,7 @@ import org.junit.Test;
@Presubmit
@SmallTest
+@DisabledOnRavenwood(reason = "No need to make j.l.Character match behavior of AndroidCharacter")
public class AndroidCharacterTest {
@Test
diff --git a/core/tests/coretests/src/android/text/SpanColorsTest.java b/core/tests/coretests/src/android/text/SpanColorsTest.java
index d2cb8c160d21..4cdbd0886310 100644
--- a/core/tests/coretests/src/android/text/SpanColorsTest.java
+++ b/core/tests/coretests/src/android/text/SpanColorsTest.java
@@ -20,6 +20,7 @@ import static com.google.common.truth.Truth.assertThat;
import android.graphics.Color;
import android.graphics.drawable.ShapeDrawable;
+import android.platform.test.annotations.DisabledOnRavenwood;
import android.platform.test.annotations.Presubmit;
import android.text.style.ForegroundColorSpan;
import android.text.style.ImageSpan;
@@ -35,6 +36,7 @@ import org.junit.runner.RunWith;
@Presubmit
@SmallTest
@RunWith(AndroidJUnit4.class)
+@DisabledOnRavenwood(blockedBy = ShapeDrawable.class)
public class SpanColorsTest {
private final TextPaint mWorkPaint = new TextPaint();
private SpanColors mSpanColors;
diff --git a/core/tests/coretests/src/android/text/SpannableTest.java b/core/tests/coretests/src/android/text/SpannableTest.java
index a3e6a7812324..710d1e2a3314 100644
--- a/core/tests/coretests/src/android/text/SpannableTest.java
+++ b/core/tests/coretests/src/android/text/SpannableTest.java
@@ -16,10 +16,10 @@
package android.text;
+import static org.junit.Assert.assertArrayEquals;
import static org.junit.Assert.assertEquals;
import android.platform.test.annotations.Presubmit;
-import android.test.MoreAsserts;
import androidx.test.ext.junit.runners.AndroidJUnit4;
import androidx.test.filters.SmallTest;
@@ -50,13 +50,13 @@ public abstract class SpannableTest {
// but other spans are not, unless the query region is empty, in
// in which case any abutting spans are returned.
spans = spannable.getSpans(0, 1, Object.class);
- MoreAsserts.assertEquals(new Object[]{emptySpan}, spans);
+ assertArrayEquals(new Object[]{emptySpan}, spans);
spans = spannable.getSpans(0, 2, Object.class);
- MoreAsserts.assertEquals(new Object[]{emptySpan, unemptySpan}, spans);
+ assertArrayEquals(new Object[]{emptySpan, unemptySpan}, spans);
spans = spannable.getSpans(1, 2, Object.class);
- MoreAsserts.assertEquals(new Object[]{emptySpan, unemptySpan}, spans);
+ assertArrayEquals(new Object[]{emptySpan, unemptySpan}, spans);
spans = spannable.getSpans(2, 2, Object.class);
- MoreAsserts.assertEquals(new Object[]{unemptySpan}, spans);
+ assertArrayEquals(new Object[]{unemptySpan}, spans);
}
@Test
diff --git a/core/tests/coretests/src/android/text/StaticLayoutTest.java b/core/tests/coretests/src/android/text/StaticLayoutTest.java
index 3541900dcacf..55f38b2fc2bd 100644
--- a/core/tests/coretests/src/android/text/StaticLayoutTest.java
+++ b/core/tests/coretests/src/android/text/StaticLayoutTest.java
@@ -25,6 +25,7 @@ import android.graphics.Canvas;
import android.graphics.Paint;
import android.graphics.Paint.FontMetricsInt;
import android.os.LocaleList;
+import android.platform.test.annotations.DisabledOnRavenwood;
import android.platform.test.annotations.Presubmit;
import android.text.Layout.Alignment;
import android.text.method.EditorState;
@@ -726,6 +727,7 @@ public class StaticLayoutTest {
}
@Test
+ @DisabledOnRavenwood(bug = 391342883)
public void testLocaleSpanAffectsHyphenation() {
TextPaint paint = new TextPaint();
paint.setTextLocale(Locale.US);
diff --git a/core/tests/coretests/src/android/text/TextUtilsTest.java b/core/tests/coretests/src/android/text/TextUtilsTest.java
index f552265cc507..e38c8800169a 100644
--- a/core/tests/coretests/src/android/text/TextUtilsTest.java
+++ b/core/tests/coretests/src/android/text/TextUtilsTest.java
@@ -18,6 +18,7 @@ package android.text;
import static android.text.TextUtils.formatSimple;
+import static org.junit.Assert.assertArrayEquals;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertNotNull;
@@ -28,7 +29,6 @@ import static org.junit.Assert.fail;
import android.os.Parcel;
import android.platform.test.annotations.Presubmit;
-import android.test.MoreAsserts;
import android.text.style.StyleSpan;
import android.text.util.Rfc822Token;
import android.text.util.Rfc822Tokenizer;
@@ -237,7 +237,7 @@ public class TextUtilsTest {
for (String s : splitter) {
strings.add(s);
}
- MoreAsserts.assertEquals(expectedStrings, strings.toArray(new String[]{}));
+ assertArrayEquals(expectedStrings, strings.toArray(new String[]{}));
}
@Test
diff --git a/core/tests/coretests/src/android/text/format/DateFormatTest.java b/core/tests/coretests/src/android/text/format/DateFormatTest.java
index 59af6dd20478..c16393c2643d 100644
--- a/core/tests/coretests/src/android/text/format/DateFormatTest.java
+++ b/core/tests/coretests/src/android/text/format/DateFormatTest.java
@@ -74,8 +74,9 @@ public class DateFormatTest {
DateFormatSymbols dfs = DateFormat.getIcuDateFormatSymbols(Locale.US);
assertEquals("AM", dfs.getAmPmStrings()[0]);
assertEquals("PM", dfs.getAmPmStrings()[1]);
- assertEquals("a", dfs.getAmpmNarrowStrings()[0]);
- assertEquals("p", dfs.getAmpmNarrowStrings()[1]);
+ // getAmpmNarrowStrings() is a @CorePlatformApi that we should stop using in framework
+ // assertEquals("a", dfs.getAmpmNarrowStrings()[0]);
+ // assertEquals("p", dfs.getAmpmNarrowStrings()[1]);
}
@Test
diff --git a/core/tests/coretests/src/android/text/format/DateIntervalFormatTest.java b/core/tests/coretests/src/android/text/format/DateIntervalFormatTest.java
index a07d399218e3..e54273479b80 100644
--- a/core/tests/coretests/src/android/text/format/DateIntervalFormatTest.java
+++ b/core/tests/coretests/src/android/text/format/DateIntervalFormatTest.java
@@ -40,6 +40,7 @@ import static org.junit.Assert.assertTrue;
import android.icu.util.Calendar;
import android.icu.util.TimeZone;
import android.icu.util.ULocale;
+import android.platform.test.annotations.DisabledOnRavenwood;
import android.platform.test.annotations.Presubmit;
import androidx.test.ext.junit.runners.AndroidJUnit4;
@@ -686,6 +687,7 @@ public class DateIntervalFormatTest {
}
@Test
+ @DisabledOnRavenwood(bug = 391381043)
public void testIsLibcoreVFlagEnabled() {
// This flag has been fully ramped. It should never be false.
assertTrue(DateIntervalFormat.isLibcoreVFlagEnabled());
diff --git a/core/tests/coretests/src/android/text/format/DateUtilsTest.java b/core/tests/coretests/src/android/text/format/DateUtilsTest.java
index 47be893eb3e9..a853d4a0c051 100644
--- a/core/tests/coretests/src/android/text/format/DateUtilsTest.java
+++ b/core/tests/coretests/src/android/text/format/DateUtilsTest.java
@@ -21,6 +21,7 @@ import static org.junit.Assert.assertEquals;
import android.content.res.Configuration;
import android.content.res.Resources;
import android.os.LocaleList;
+import android.platform.test.annotations.DisabledOnRavenwood;
import android.platform.test.annotations.Presubmit;
import androidx.test.ext.junit.runners.AndroidJUnit4;
@@ -116,6 +117,7 @@ public class DateUtilsTest {
}
@Test
+ @DisabledOnRavenwood(reason = "DateFormat.set24HourTimePref is not available on host JVM")
public void testFormatSameDayTime() {
// This test assumes a default DateFormat.is24Hour setting.
DateFormat.set24HourTimePref(null);
diff --git a/core/tests/coretests/src/android/text/format/TimeMigrationUtilsTest.java b/core/tests/coretests/src/android/text/format/TimeMigrationUtilsTest.java
index c8cb5f38b185..49f3373d0659 100644
--- a/core/tests/coretests/src/android/text/format/TimeMigrationUtilsTest.java
+++ b/core/tests/coretests/src/android/text/format/TimeMigrationUtilsTest.java
@@ -18,6 +18,7 @@ package android.text.format;
import static org.junit.Assert.assertEquals;
+import android.platform.test.annotations.DisabledOnRavenwood;
import android.platform.test.annotations.Presubmit;
import androidx.test.ext.junit.runners.AndroidJUnit4;
@@ -72,6 +73,7 @@ public class TimeMigrationUtilsTest {
* Compares TimeMigrationUtils.formatSimpleDateTime() with the code it is replacing.
*/
@Test
+ @DisabledOnRavenwood(blockedBy = Time.class)
public void formatMillisAsDateTime_matchesOldBehavior() {
// A selection of interesting locales.
Locale[] locales = new Locale[] {
diff --git a/core/tests/coretests/src/android/text/format/TimeTest.java b/core/tests/coretests/src/android/text/format/TimeTest.java
index 6138ea1926dd..29c58998a635 100644
--- a/core/tests/coretests/src/android/text/format/TimeTest.java
+++ b/core/tests/coretests/src/android/text/format/TimeTest.java
@@ -20,6 +20,7 @@ import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertTrue;
import static org.junit.Assert.fail;
+import android.platform.test.annotations.DisabledOnRavenwood;
import android.platform.test.annotations.Presubmit;
import android.util.Log;
import android.util.TimeFormatException;
@@ -34,6 +35,7 @@ import org.junit.runner.RunWith;
@Presubmit
@SmallTest
@RunWith(AndroidJUnit4.class)
+@DisabledOnRavenwood(blockedBy = Time.class)
public class TimeTest {
@Test
diff --git a/core/tests/coretests/src/android/text/method/BackspaceTest.java b/core/tests/coretests/src/android/text/method/BackspaceTest.java
index a7ff244507cb..646e8f92fbb3 100644
--- a/core/tests/coretests/src/android/text/method/BackspaceTest.java
+++ b/core/tests/coretests/src/android/text/method/BackspaceTest.java
@@ -16,6 +16,7 @@
package android.text.method;
+import android.platform.test.annotations.DisabledOnRavenwood;
import android.platform.test.annotations.Presubmit;
import android.text.InputType;
import android.util.KeyUtils;
@@ -41,6 +42,7 @@ import org.junit.runner.RunWith;
@Presubmit
@SmallTest
@RunWith(AndroidJUnit4.class)
+@DisabledOnRavenwood(blockedBy = EditText.class)
public class BackspaceTest {
private EditText mTextView;
diff --git a/core/tests/coretests/src/android/text/method/EditorState.java b/core/tests/coretests/src/android/text/method/EditorState.java
index 4eff7a49ac7b..633fa112c016 100644
--- a/core/tests/coretests/src/android/text/method/EditorState.java
+++ b/core/tests/coretests/src/android/text/method/EditorState.java
@@ -16,7 +16,7 @@
package android.text.method;
-import static org.mockito.Matchers.any;
+import static org.mockito.ArgumentMatchers.any;
import static org.mockito.Mockito.doNothing;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.when;
@@ -181,4 +181,3 @@ public class EditorState {
Assert.assertEquals(expected.mSelectionEnd, mSelectionEnd);
}
}
-
diff --git a/core/tests/coretests/src/android/text/method/ForwardDeleteTest.java b/core/tests/coretests/src/android/text/method/ForwardDeleteTest.java
index 1e4024d92f97..8044fd7a3432 100644
--- a/core/tests/coretests/src/android/text/method/ForwardDeleteTest.java
+++ b/core/tests/coretests/src/android/text/method/ForwardDeleteTest.java
@@ -16,6 +16,7 @@
package android.text.method;
+import android.platform.test.annotations.DisabledOnRavenwood;
import android.platform.test.annotations.Presubmit;
import android.text.InputType;
import android.util.KeyUtils;
@@ -40,6 +41,7 @@ import org.junit.runner.RunWith;
@Presubmit
@SmallTest
@RunWith(AndroidJUnit4.class)
+@DisabledOnRavenwood(blockedBy = EditText.class)
public class ForwardDeleteTest {
private EditText mTextView;
diff --git a/core/tests/coretests/src/android/text/method/InsertModeTransformationMethodTest.java b/core/tests/coretests/src/android/text/method/InsertModeTransformationMethodTest.java
index e2c19024a840..37ad204ad64c 100644
--- a/core/tests/coretests/src/android/text/method/InsertModeTransformationMethodTest.java
+++ b/core/tests/coretests/src/android/text/method/InsertModeTransformationMethodTest.java
@@ -19,6 +19,7 @@ package android.text.method;
import static com.google.common.truth.Truth.assertThat;
import android.content.Context;
+import android.platform.test.annotations.DisabledOnRavenwood;
import android.platform.test.annotations.Presubmit;
import android.platform.test.annotations.RequiresFlagsDisabled;
import android.platform.test.annotations.RequiresFlagsEnabled;
@@ -44,6 +45,7 @@ import org.junit.runner.RunWith;
@Presubmit
@SmallTest
@RunWith(AndroidJUnit4.class)
+@DisabledOnRavenwood(blockedBy = View.class)
public class InsertModeTransformationMethodTest {
private static View sView;
private static final String TEXT = "abc def";
diff --git a/core/tests/coretests/src/android/text/util/LinkifyTest.java b/core/tests/coretests/src/android/text/util/LinkifyTest.java
index 52f3b2e0534f..98bdb0b53df6 100644
--- a/core/tests/coretests/src/android/text/util/LinkifyTest.java
+++ b/core/tests/coretests/src/android/text/util/LinkifyTest.java
@@ -23,6 +23,7 @@ import static org.junit.Assert.assertTrue;
import android.content.Context;
import android.content.res.Configuration;
import android.os.LocaleList;
+import android.platform.test.annotations.DisabledOnRavenwood;
import android.text.Spannable;
import android.text.SpannableString;
import android.text.method.LinkMovementMethod;
@@ -46,6 +47,7 @@ import java.util.Locale;
*/
@SmallTest
@RunWith(AndroidJUnit4.class)
+@DisabledOnRavenwood(blockedBy = Linkify.class)
public class LinkifyTest {
private static final LocaleList LOCALE_LIST_US = new LocaleList(Locale.US);
diff --git a/core/tests/coretests/src/android/view/InsetsSourceConsumerTest.java b/core/tests/coretests/src/android/view/InsetsSourceConsumerTest.java
index e6361e10cfa7..6adceb96d977 100644
--- a/core/tests/coretests/src/android/view/InsetsSourceConsumerTest.java
+++ b/core/tests/coretests/src/android/view/InsetsSourceConsumerTest.java
@@ -230,7 +230,9 @@ public class InsetsSourceConsumerTest {
new InsetsSourceControl(ID_STATUS_BAR, statusBars(), mLeash,
false /* initialVisible */, new Point(), Insets.NONE),
new int[1], hideTypes, new int[1], new int[1]);
- assertTrue(mRemoveSurfaceCalled);
+ if (!android.view.inputmethod.Flags.refactorInsetsController()) {
+ assertTrue(mRemoveSurfaceCalled);
+ }
assertEquals(0, hideTypes[0]);
});
diff --git a/core/tests/coretests/src/com/android/internal/os/KernelSingleUidTimeReaderTest.java b/core/tests/coretests/src/com/android/internal/os/KernelSingleUidTimeReaderTest.java
index 3239598eccdc..0ba2d851feb9 100644
--- a/core/tests/coretests/src/com/android/internal/os/KernelSingleUidTimeReaderTest.java
+++ b/core/tests/coretests/src/com/android/internal/os/KernelSingleUidTimeReaderTest.java
@@ -23,6 +23,7 @@ import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertTrue;
+import android.annotation.SuppressLint;
import android.platform.test.annotations.IgnoreUnderRavenwood;
import android.platform.test.ravenwood.RavenwoodRule;
import android.util.SparseArray;
@@ -287,6 +288,7 @@ public class KernelSingleUidTimeReaderTest {
0, lastUidCpuTimes.size());
}
+ @SuppressLint("CheckResult")
@Test
public void testAddDeltaFromBpf() {
LongArrayMultiStateCounter counter = new LongArrayMultiStateCounter(2, 5);
diff --git a/core/tests/coretests/src/com/android/internal/os/LongArrayMultiStateCounterTest.java b/core/tests/coretests/src/com/android/internal/os/LongArrayMultiStateCounterTest.java
index 7e5d0a4c2e42..959e121aae9e 100644
--- a/core/tests/coretests/src/com/android/internal/os/LongArrayMultiStateCounterTest.java
+++ b/core/tests/coretests/src/com/android/internal/os/LongArrayMultiStateCounterTest.java
@@ -20,6 +20,7 @@ import static com.google.common.truth.Truth.assertThat;
import static org.junit.Assert.assertThrows;
+import android.annotation.SuppressLint;
import android.os.BadParcelableException;
import android.os.Parcel;
import android.platform.test.ravenwood.RavenwoodRule;
@@ -176,6 +177,7 @@ public class LongArrayMultiStateCounterTest {
assertCounts(newCounter, 0, new long[]{116, 232, 364, 528});
}
+ @SuppressLint("CheckResult")
private void assertCounts(LongArrayMultiStateCounter counter, int state, long[] expected) {
long[] counts = new long[expected.length];
counter.getCounts(counts, state);
diff --git a/graphics/java/android/graphics/AvoidXfermode.java b/graphics/java/android/graphics/AvoidXfermode.java
index 683c15702427..5296ee848872 100644
--- a/graphics/java/android/graphics/AvoidXfermode.java
+++ b/graphics/java/android/graphics/AvoidXfermode.java
@@ -23,6 +23,7 @@ package android.graphics;
* @removed
*/
@Deprecated
+@android.ravenwood.annotation.RavenwoodKeepWholeClass
public class AvoidXfermode extends Xfermode {
// these need to match the enum in AvoidXfermode.h on the native side
diff --git a/graphics/java/android/graphics/BLASTBufferQueue.java b/graphics/java/android/graphics/BLASTBufferQueue.java
index 9b9be244cf90..9f605342e378 100644
--- a/graphics/java/android/graphics/BLASTBufferQueue.java
+++ b/graphics/java/android/graphics/BLASTBufferQueue.java
@@ -26,6 +26,7 @@ import java.util.function.Consumer;
/**
* @hide
*/
+@android.ravenwood.annotation.RavenwoodKeepWholeClass
public final class BLASTBufferQueue {
// Note: This field is accessed by native code.
public long mNativeObject; // BLASTBufferQueue*
diff --git a/graphics/java/android/graphics/BaseCanvas.java b/graphics/java/android/graphics/BaseCanvas.java
index a2a0f4936888..0ca58cc07213 100644
--- a/graphics/java/android/graphics/BaseCanvas.java
+++ b/graphics/java/android/graphics/BaseCanvas.java
@@ -48,6 +48,7 @@ import java.util.Objects;
*
* @hide
*/
+@android.ravenwood.annotation.RavenwoodKeepWholeClass
public abstract class BaseCanvas {
/**
* Should only be assigned in constructors (or setBitmap if software canvas),
diff --git a/graphics/java/android/graphics/BaseRecordingCanvas.java b/graphics/java/android/graphics/BaseRecordingCanvas.java
index 5b1fa7b15e6d..0511bd15dd13 100644
--- a/graphics/java/android/graphics/BaseRecordingCanvas.java
+++ b/graphics/java/android/graphics/BaseRecordingCanvas.java
@@ -44,6 +44,7 @@ import java.util.Objects;
*
* @hide
*/
+@android.ravenwood.annotation.RavenwoodKeepWholeClass
public class BaseRecordingCanvas extends Canvas {
public BaseRecordingCanvas(long nativeCanvas) {
diff --git a/graphics/java/android/graphics/Bitmap.java b/graphics/java/android/graphics/Bitmap.java
index 0c4ea79dd5be..cd5a54c2fd3f 100644
--- a/graphics/java/android/graphics/Bitmap.java
+++ b/graphics/java/android/graphics/Bitmap.java
@@ -51,6 +51,7 @@ import java.nio.ShortBuffer;
import java.util.ArrayList;
import java.util.WeakHashMap;
+@android.ravenwood.annotation.RavenwoodKeepWholeClass
public final class Bitmap implements Parcelable {
private static final String TAG = "Bitmap";
diff --git a/graphics/java/android/graphics/BitmapFactory.java b/graphics/java/android/graphics/BitmapFactory.java
index 1c2014183bb7..a5535c8d8485 100644
--- a/graphics/java/android/graphics/BitmapFactory.java
+++ b/graphics/java/android/graphics/BitmapFactory.java
@@ -41,6 +41,7 @@ import java.io.InputStream;
* Creates Bitmap objects from various sources, including files, streams,
* and byte-arrays.
*/
+@android.ravenwood.annotation.RavenwoodKeepWholeClass
public class BitmapFactory {
private static final int DECODE_BUFFER_SIZE = 16 * 1024;
diff --git a/graphics/java/android/graphics/BitmapRegionDecoder.java b/graphics/java/android/graphics/BitmapRegionDecoder.java
index 29112af9516b..9b3f7158a3e5 100644
--- a/graphics/java/android/graphics/BitmapRegionDecoder.java
+++ b/graphics/java/android/graphics/BitmapRegionDecoder.java
@@ -37,6 +37,7 @@ import java.io.InputStream;
* to get a decoded Bitmap of the specified region.
*
*/
+@android.ravenwood.annotation.RavenwoodKeepWholeClass
public final class BitmapRegionDecoder {
private long mNativeBitmapRegionDecoder;
private boolean mRecycled;
diff --git a/graphics/java/android/graphics/BitmapShader.java b/graphics/java/android/graphics/BitmapShader.java
index dcfff62459ab..ac3543a403cc 100644
--- a/graphics/java/android/graphics/BitmapShader.java
+++ b/graphics/java/android/graphics/BitmapShader.java
@@ -31,6 +31,7 @@ import java.lang.annotation.RetentionPolicy;
* Shader used to draw a bitmap as a texture. The bitmap can be repeated or
* mirrored by setting the tiling mode.
*/
+@android.ravenwood.annotation.RavenwoodKeepWholeClass
public class BitmapShader extends Shader {
/**
* Prevent garbage collection.
diff --git a/graphics/java/android/graphics/BlendMode.java b/graphics/java/android/graphics/BlendMode.java
index c6ae680d01bf..c07af4e23b6f 100644
--- a/graphics/java/android/graphics/BlendMode.java
+++ b/graphics/java/android/graphics/BlendMode.java
@@ -19,6 +19,7 @@ package android.graphics;
import android.annotation.NonNull;
import android.annotation.Nullable;
+@android.ravenwood.annotation.RavenwoodKeepWholeClass
public enum BlendMode {
/**
diff --git a/graphics/java/android/graphics/BlendModeColorFilter.java b/graphics/java/android/graphics/BlendModeColorFilter.java
index d4e23732bdc3..d5dd0d3c5330 100644
--- a/graphics/java/android/graphics/BlendModeColorFilter.java
+++ b/graphics/java/android/graphics/BlendModeColorFilter.java
@@ -23,6 +23,7 @@ import android.annotation.NonNull;
* A color filter that can be used to tint the source pixels using a single
* color and a specific {@link BlendMode}.
*/
+@android.ravenwood.annotation.RavenwoodKeepWholeClass
public final class BlendModeColorFilter extends ColorFilter {
@ColorInt final int mColor;
diff --git a/graphics/java/android/graphics/BlurMaskFilter.java b/graphics/java/android/graphics/BlurMaskFilter.java
index f3064f872041..22ed524e8ec0 100644
--- a/graphics/java/android/graphics/BlurMaskFilter.java
+++ b/graphics/java/android/graphics/BlurMaskFilter.java
@@ -22,6 +22,7 @@ package android.graphics;
* inside, or straddles, the original mask's border, is controlled by the
* Blur enum.
*/
+@android.ravenwood.annotation.RavenwoodKeepWholeClass
public class BlurMaskFilter extends MaskFilter {
public enum Blur {
diff --git a/graphics/java/android/graphics/Camera.java b/graphics/java/android/graphics/Camera.java
index 46640d7222ca..27b695c8c77f 100644
--- a/graphics/java/android/graphics/Camera.java
+++ b/graphics/java/android/graphics/Camera.java
@@ -21,6 +21,7 @@ package android.graphics;
* generate a matrix that can be applied, for instance, on a
* {@link Canvas}.
*/
+@android.ravenwood.annotation.RavenwoodKeepWholeClass
public class Camera {
/**
* Creates a new camera, with empty transformations.
diff --git a/graphics/java/android/graphics/Canvas.java b/graphics/java/android/graphics/Canvas.java
index 28c2ca36fd2e..9137150b104c 100644
--- a/graphics/java/android/graphics/Canvas.java
+++ b/graphics/java/android/graphics/Canvas.java
@@ -54,6 +54,7 @@ import java.lang.annotation.RetentionPolicy;
* <a href="{@docRoot}guide/topics/graphics/2d-graphics.html">
* Canvas and Drawables</a> developer guide.</p></div>
*/
+@android.ravenwood.annotation.RavenwoodKeepWholeClass
public class Canvas extends BaseCanvas {
private static int sCompatibilityVersion = 0;
private static boolean sCompatibilityRestore = false;
diff --git a/graphics/java/android/graphics/CanvasProperty.java b/graphics/java/android/graphics/CanvasProperty.java
index e949584b0659..755161d387cc 100644
--- a/graphics/java/android/graphics/CanvasProperty.java
+++ b/graphics/java/android/graphics/CanvasProperty.java
@@ -25,6 +25,7 @@ import com.android.internal.util.VirtualRefBasePtr;
* TODO: Make public?
* @hide
*/
+@android.ravenwood.annotation.RavenwoodKeepWholeClass
public final class CanvasProperty<T> {
private VirtualRefBasePtr mProperty;
diff --git a/graphics/java/android/graphics/ColorFilter.java b/graphics/java/android/graphics/ColorFilter.java
index 7050325997b6..918f26dfe640 100644
--- a/graphics/java/android/graphics/ColorFilter.java
+++ b/graphics/java/android/graphics/ColorFilter.java
@@ -23,6 +23,7 @@ import libcore.util.NativeAllocationRegistry;
* each pixel drawn with that paint. This is an abstract class that should
* never be used directly.
*/
+@android.ravenwood.annotation.RavenwoodKeepWholeClass
public class ColorFilter {
private static class NoImagePreloadHolder {
diff --git a/graphics/java/android/graphics/ColorMatrixColorFilter.java b/graphics/java/android/graphics/ColorMatrixColorFilter.java
index bfdf3187c575..cb78a8384994 100644
--- a/graphics/java/android/graphics/ColorMatrixColorFilter.java
+++ b/graphics/java/android/graphics/ColorMatrixColorFilter.java
@@ -27,6 +27,7 @@ import android.os.Build;
*
* @see ColorMatrix
*/
+@android.ravenwood.annotation.RavenwoodKeepWholeClass
public class ColorMatrixColorFilter extends ColorFilter {
@UnsupportedAppUsage
private final ColorMatrix mMatrix = new ColorMatrix();
diff --git a/graphics/java/android/graphics/Compatibility.java b/graphics/java/android/graphics/Compatibility.java
index 747fbf111b4b..f89a4f7810c1 100644
--- a/graphics/java/android/graphics/Compatibility.java
+++ b/graphics/java/android/graphics/Compatibility.java
@@ -21,6 +21,7 @@ package android.graphics;
* specified by the app.
* @hide
*/
+@android.ravenwood.annotation.RavenwoodKeepWholeClass
public final class Compatibility {
private Compatibility() {}
diff --git a/graphics/java/android/graphics/ComposePathEffect.java b/graphics/java/android/graphics/ComposePathEffect.java
index 7d59ecea948e..b380d2e78d4c 100644
--- a/graphics/java/android/graphics/ComposePathEffect.java
+++ b/graphics/java/android/graphics/ComposePathEffect.java
@@ -16,6 +16,7 @@
package android.graphics;
+@android.ravenwood.annotation.RavenwoodKeepWholeClass
public class ComposePathEffect extends PathEffect {
/**
diff --git a/graphics/java/android/graphics/ComposeShader.java b/graphics/java/android/graphics/ComposeShader.java
index e7145686247e..57a11d232ac5 100644
--- a/graphics/java/android/graphics/ComposeShader.java
+++ b/graphics/java/android/graphics/ComposeShader.java
@@ -22,6 +22,7 @@ import android.annotation.NonNull;
/** A subclass of shader that returns the composition of two other shaders, combined by
an {@link android.graphics.Xfermode} subclass.
*/
+@android.ravenwood.annotation.RavenwoodKeepWholeClass
public class ComposeShader extends Shader {
Shader mShaderA;
diff --git a/graphics/java/android/graphics/CornerPathEffect.java b/graphics/java/android/graphics/CornerPathEffect.java
index 8f4d7d9b1c49..37f0d2979e5e 100644
--- a/graphics/java/android/graphics/CornerPathEffect.java
+++ b/graphics/java/android/graphics/CornerPathEffect.java
@@ -16,6 +16,7 @@
package android.graphics;
+@android.ravenwood.annotation.RavenwoodKeepWholeClass
public class CornerPathEffect extends PathEffect {
/**
diff --git a/graphics/java/android/graphics/DashPathEffect.java b/graphics/java/android/graphics/DashPathEffect.java
index ef3ebe8089ca..ff3c376be29d 100644
--- a/graphics/java/android/graphics/DashPathEffect.java
+++ b/graphics/java/android/graphics/DashPathEffect.java
@@ -16,6 +16,7 @@
package android.graphics;
+@android.ravenwood.annotation.RavenwoodKeepWholeClass
public class DashPathEffect extends PathEffect {
/**
diff --git a/graphics/java/android/graphics/DiscretePathEffect.java b/graphics/java/android/graphics/DiscretePathEffect.java
index 3b3c9c9be6c2..77f984589896 100644
--- a/graphics/java/android/graphics/DiscretePathEffect.java
+++ b/graphics/java/android/graphics/DiscretePathEffect.java
@@ -16,6 +16,7 @@
package android.graphics;
+@android.ravenwood.annotation.RavenwoodKeepWholeClass
public class DiscretePathEffect extends PathEffect {
/**
diff --git a/graphics/java/android/graphics/DrawFilter.java b/graphics/java/android/graphics/DrawFilter.java
index c7fdcb22c71d..505a830d725b 100644
--- a/graphics/java/android/graphics/DrawFilter.java
+++ b/graphics/java/android/graphics/DrawFilter.java
@@ -22,6 +22,7 @@ package android.graphics;
* can disable/enable antialiasing, or change the color for everything this is
* drawn.
*/
+@android.ravenwood.annotation.RavenwoodKeepWholeClass
public class DrawFilter {
/**
diff --git a/graphics/java/android/graphics/EmbossMaskFilter.java b/graphics/java/android/graphics/EmbossMaskFilter.java
index 003678ae5a3c..f0a661f57291 100644
--- a/graphics/java/android/graphics/EmbossMaskFilter.java
+++ b/graphics/java/android/graphics/EmbossMaskFilter.java
@@ -16,6 +16,7 @@
package android.graphics;
+@android.ravenwood.annotation.RavenwoodKeepWholeClass
public class EmbossMaskFilter extends MaskFilter {
/**
* Create an emboss maskfilter
diff --git a/graphics/java/android/graphics/FontFamily.java b/graphics/java/android/graphics/FontFamily.java
index 88f0e8ef8a94..09022ee35e64 100644
--- a/graphics/java/android/graphics/FontFamily.java
+++ b/graphics/java/android/graphics/FontFamily.java
@@ -41,6 +41,7 @@ import java.nio.channels.FileChannel;
* @deprecated Use {@link android.graphics.fonts.FontFamily} instead.
*/
@Deprecated
+@android.ravenwood.annotation.RavenwoodKeepWholeClass
public class FontFamily {
private static String TAG = "FontFamily";
diff --git a/graphics/java/android/graphics/FontListParser.java b/graphics/java/android/graphics/FontListParser.java
index 13c4a94cb9b6..8d0f12866bcf 100644
--- a/graphics/java/android/graphics/FontListParser.java
+++ b/graphics/java/android/graphics/FontListParser.java
@@ -48,6 +48,7 @@ import java.util.regex.Pattern;
* Parser for font config files.
* @hide
*/
+@android.ravenwood.annotation.RavenwoodKeepWholeClass
public class FontListParser {
private static final String TAG = "FontListParser";
diff --git a/graphics/java/android/graphics/ForceDarkType.java b/graphics/java/android/graphics/ForceDarkType.java
index 396b03703bb9..d21aef30a45c 100644
--- a/graphics/java/android/graphics/ForceDarkType.java
+++ b/graphics/java/android/graphics/ForceDarkType.java
@@ -29,6 +29,7 @@ import java.lang.annotation.RetentionPolicy;
*
* @hide
*/
+@android.ravenwood.annotation.RavenwoodKeepWholeClass
public class ForceDarkType {
/**
* Force dark disabled: normal, default operation.
diff --git a/graphics/java/android/graphics/FrameInfo.java b/graphics/java/android/graphics/FrameInfo.java
index 3b8f46630344..520213892d01 100644
--- a/graphics/java/android/graphics/FrameInfo.java
+++ b/graphics/java/android/graphics/FrameInfo.java
@@ -38,6 +38,7 @@ import java.lang.annotation.RetentionPolicy;
*
* @hide
*/
+@android.ravenwood.annotation.RavenwoodKeepWholeClass
public final class FrameInfo {
public long[] frameInfo = new long[FRAME_INFO_SIZE];
diff --git a/graphics/java/android/graphics/Gainmap.java b/graphics/java/android/graphics/Gainmap.java
index 63ca3b8313ce..7fc13db85659 100644
--- a/graphics/java/android/graphics/Gainmap.java
+++ b/graphics/java/android/graphics/Gainmap.java
@@ -86,6 +86,7 @@ import java.lang.annotation.RetentionPolicy;
* for these functions cancels out and does not affect the result, so other bases may be used
* if preferred.
*/
+@android.ravenwood.annotation.RavenwoodKeepWholeClass
public final class Gainmap implements Parcelable {
/** @hide */
diff --git a/graphics/java/android/graphics/GraphicBuffer.java b/graphics/java/android/graphics/GraphicBuffer.java
index 6705b25ab0ec..4982851c65de 100644
--- a/graphics/java/android/graphics/GraphicBuffer.java
+++ b/graphics/java/android/graphics/GraphicBuffer.java
@@ -28,6 +28,7 @@ import android.os.Parcelable;
* @hide
*/
@SuppressWarnings("UnusedDeclaration")
+@android.ravenwood.annotation.RavenwoodKeepWholeClass
public class GraphicBuffer implements Parcelable {
// Note: keep usage flags in sync with GraphicBuffer.h and gralloc.h
public static final int USAGE_SW_READ_NEVER = 0x0;
diff --git a/graphics/java/android/graphics/GraphicsProtos.java b/graphics/java/android/graphics/GraphicsProtos.java
index 6bc41d39ff98..fa7eaf946845 100644
--- a/graphics/java/android/graphics/GraphicsProtos.java
+++ b/graphics/java/android/graphics/GraphicsProtos.java
@@ -24,6 +24,7 @@ import android.util.proto.ProtoOutputStream;
*
* @hide
*/
+@android.ravenwood.annotation.RavenwoodKeepWholeClass
public final class GraphicsProtos {
/** GraphicsProtos can never be an instance */
private GraphicsProtos() {}
diff --git a/graphics/java/android/graphics/GraphicsStatsService.java b/graphics/java/android/graphics/GraphicsStatsService.java
index 7a012bcde799..d0b9998e18c8 100644
--- a/graphics/java/android/graphics/GraphicsStatsService.java
+++ b/graphics/java/android/graphics/GraphicsStatsService.java
@@ -74,6 +74,7 @@ import java.util.TimeZone;
* for the process to use.
*
* @hide */
+@android.ravenwood.annotation.RavenwoodKeepWholeClass
public class GraphicsStatsService extends IGraphicsStats.Stub {
public static final String GRAPHICS_STATS_SERVICE = "graphicsstats";
diff --git a/graphics/java/android/graphics/HardwareBufferRenderer.java b/graphics/java/android/graphics/HardwareBufferRenderer.java
index e04f13c9b922..81798709b7c6 100644
--- a/graphics/java/android/graphics/HardwareBufferRenderer.java
+++ b/graphics/java/android/graphics/HardwareBufferRenderer.java
@@ -55,6 +55,7 @@ import java.util.function.Consumer;
* HardwareBufferRenderer will never clear contents before each draw invocation so previous contents
* in the {@link HardwareBuffer} target will be preserved across renders.
*/
+@android.ravenwood.annotation.RavenwoodKeepWholeClass
public class HardwareBufferRenderer implements AutoCloseable {
private static final ColorSpace DEFAULT_COLORSPACE = ColorSpace.get(Named.SRGB);
diff --git a/graphics/java/android/graphics/HardwareRenderer.java b/graphics/java/android/graphics/HardwareRenderer.java
index ef6b72871415..3444f84c20af 100644
--- a/graphics/java/android/graphics/HardwareRenderer.java
+++ b/graphics/java/android/graphics/HardwareRenderer.java
@@ -79,6 +79,7 @@ import sun.misc.Cleaner;
* Failure to do so will cause the render thread to stall on that surface, blocking all
* HardwareRenderer instances.</p>
*/
+@android.ravenwood.annotation.RavenwoodKeepWholeClass
public class HardwareRenderer {
private static final String LOG_TAG = "HardwareRenderer";
diff --git a/graphics/java/android/graphics/HardwareRendererObserver.java b/graphics/java/android/graphics/HardwareRendererObserver.java
index d5a6a2fe158a..99263780f407 100644
--- a/graphics/java/android/graphics/HardwareRendererObserver.java
+++ b/graphics/java/android/graphics/HardwareRendererObserver.java
@@ -28,6 +28,7 @@ import java.lang.ref.WeakReference;
*
* @hide
*/
+@android.ravenwood.annotation.RavenwoodKeepWholeClass
public final class HardwareRendererObserver {
private final long[] mFrameMetrics;
private final Handler mHandler;
diff --git a/graphics/java/android/graphics/ImageDecoder.java b/graphics/java/android/graphics/ImageDecoder.java
index 639517996724..419929a39007 100644
--- a/graphics/java/android/graphics/ImageDecoder.java
+++ b/graphics/java/android/graphics/ImageDecoder.java
@@ -44,6 +44,7 @@ import android.media.MediaFormat;
import android.net.Uri;
import android.os.Build;
import android.os.Trace;
+import android.ravenwood.annotation.RavenwoodIgnore;
import android.system.ErrnoException;
import android.system.Os;
import android.util.DisplayMetrics;
@@ -173,6 +174,7 @@ import java.util.concurrent.atomic.AtomicBoolean;
* });
* </pre>
*/
+@android.ravenwood.annotation.RavenwoodKeepWholeClass
public final class ImageDecoder implements AutoCloseable {
/**
* Source of encoded image data.
@@ -1987,6 +1989,7 @@ public final class ImageDecoder implements AutoCloseable {
* Check if HEVC decoder is supported by the device.
*/
@SuppressWarnings("AndroidFrameworkCompatChange")
+ @RavenwoodIgnore(blockedBy = MediaCodecList.class)
private static boolean isHevcDecoderSupported() {
synchronized (sIsHevcDecoderSupportedLock) {
if (sIsHevcDecoderSupportedInitialized) {
@@ -2010,6 +2013,7 @@ public final class ImageDecoder implements AutoCloseable {
* Checks if the device supports decoding 10-bit AV1.
*/
@SuppressWarnings("AndroidFrameworkCompatChange") // This is not an app-visible API.
+ @RavenwoodIgnore(blockedBy = MediaCodecList.class)
private static boolean isP010SupportedForAV1() {
synchronized (sIsP010SupportedLock) {
if (sIsP010SupportedFlagsInitialized) {
@@ -2025,6 +2029,7 @@ public final class ImageDecoder implements AutoCloseable {
* This method is called by JNI.
*/
@SuppressWarnings("unused")
+ @RavenwoodIgnore(blockedBy = MediaCodecList.class)
private static boolean isP010SupportedForHEVC() {
synchronized (sIsP010SupportedLock) {
if (sIsP010SupportedFlagsInitialized) {
diff --git a/graphics/java/android/graphics/ImageFormat.java b/graphics/java/android/graphics/ImageFormat.java
index b4899f975f43..4c9f5ac6ba92 100644
--- a/graphics/java/android/graphics/ImageFormat.java
+++ b/graphics/java/android/graphics/ImageFormat.java
@@ -24,6 +24,7 @@ import com.android.internal.camera.flags.Flags;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
+@android.ravenwood.annotation.RavenwoodKeepWholeClass
public class ImageFormat {
/** @hide */
@Retention(RetentionPolicy.SOURCE)
diff --git a/graphics/java/android/graphics/LayerRasterizer.java b/graphics/java/android/graphics/LayerRasterizer.java
index 25155ab284fb..1a44248d01b0 100644
--- a/graphics/java/android/graphics/LayerRasterizer.java
+++ b/graphics/java/android/graphics/LayerRasterizer.java
@@ -20,6 +20,7 @@ package android.graphics;
* @removed feature is not supported by hw-accerlerated or PDF backends
*/
@Deprecated
+@android.ravenwood.annotation.RavenwoodKeepWholeClass
public class LayerRasterizer extends Rasterizer {
public LayerRasterizer() { }
diff --git a/graphics/java/android/graphics/LeakyTypefaceStorage.java b/graphics/java/android/graphics/LeakyTypefaceStorage.java
index 618e60d442d7..25a843696cc5 100644
--- a/graphics/java/android/graphics/LeakyTypefaceStorage.java
+++ b/graphics/java/android/graphics/LeakyTypefaceStorage.java
@@ -32,6 +32,7 @@ import java.util.ArrayList;
*
* @hide
*/
+@android.ravenwood.annotation.RavenwoodKeepWholeClass
public class LeakyTypefaceStorage {
private static final Object sLock = new Object();
diff --git a/graphics/java/android/graphics/LightingColorFilter.java b/graphics/java/android/graphics/LightingColorFilter.java
index fe73a1a70b9c..1afdc7706396 100644
--- a/graphics/java/android/graphics/LightingColorFilter.java
+++ b/graphics/java/android/graphics/LightingColorFilter.java
@@ -40,6 +40,7 @@ import android.os.Build;
* </pre>
* The result is pinned to the <code>[0..255]</code> range for each channel.
*/
+@android.ravenwood.annotation.RavenwoodKeepWholeClass
public class LightingColorFilter extends ColorFilter {
@ColorInt
private int mMul;
diff --git a/graphics/java/android/graphics/LinearGradient.java b/graphics/java/android/graphics/LinearGradient.java
index 087937144b97..c6566c9b27fe 100644
--- a/graphics/java/android/graphics/LinearGradient.java
+++ b/graphics/java/android/graphics/LinearGradient.java
@@ -24,6 +24,7 @@ import android.compat.annotation.UnsupportedAppUsage;
import android.os.Build;
+@android.ravenwood.annotation.RavenwoodKeepWholeClass
public class LinearGradient extends Shader {
@UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
private float mX0;
diff --git a/graphics/java/android/graphics/MaskFilter.java b/graphics/java/android/graphics/MaskFilter.java
index d4743155729e..b490650e4d0c 100644
--- a/graphics/java/android/graphics/MaskFilter.java
+++ b/graphics/java/android/graphics/MaskFilter.java
@@ -21,6 +21,7 @@ package android.graphics;
* an alpha-channel mask before drawing it. A subclass of MaskFilter may be
* installed into a Paint. Blur and emboss are implemented as subclasses of MaskFilter.
*/
+@android.ravenwood.annotation.RavenwoodKeepWholeClass
public class MaskFilter {
protected void finalize() throws Throwable {
diff --git a/graphics/java/android/graphics/Mesh.java b/graphics/java/android/graphics/Mesh.java
index 6be8332e784b..f4d841b161fa 100644
--- a/graphics/java/android/graphics/Mesh.java
+++ b/graphics/java/android/graphics/Mesh.java
@@ -37,6 +37,7 @@ import java.nio.ShortBuffer;
* for the mesh. Once generated, a mesh object can be drawn through
* {@link Canvas#drawMesh(Mesh, BlendMode, Paint)}
*/
+@android.ravenwood.annotation.RavenwoodKeepWholeClass
public class Mesh {
private long mNativeMeshWrapper;
private boolean mIsIndexed;
diff --git a/graphics/java/android/graphics/MeshSpecification.java b/graphics/java/android/graphics/MeshSpecification.java
index b1aae7f37c31..9c7e948dfc1b 100644
--- a/graphics/java/android/graphics/MeshSpecification.java
+++ b/graphics/java/android/graphics/MeshSpecification.java
@@ -72,6 +72,7 @@ import java.lang.annotation.RetentionPolicy;
* These should be kept in mind when generating a mesh specification, as exceeding them will
* lead to errors.
*/
+@android.ravenwood.annotation.RavenwoodKeepWholeClass
public class MeshSpecification {
long mNativeMeshSpec;
diff --git a/graphics/java/android/graphics/Movie.java b/graphics/java/android/graphics/Movie.java
index 9c9535d16aab..cefe391f2d2c 100644
--- a/graphics/java/android/graphics/Movie.java
+++ b/graphics/java/android/graphics/Movie.java
@@ -27,6 +27,7 @@ import java.io.InputStream;
* @deprecated Prefer {@link android.graphics.drawable.AnimatedImageDrawable}.
*/
@Deprecated
+@android.ravenwood.annotation.RavenwoodKeepWholeClass
public class Movie {
@UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
private long mNativeMovie;
diff --git a/graphics/java/android/graphics/NinePatch.java b/graphics/java/android/graphics/NinePatch.java
index 382269f74366..00df23fe76ba 100644
--- a/graphics/java/android/graphics/NinePatch.java
+++ b/graphics/java/android/graphics/NinePatch.java
@@ -32,6 +32,7 @@ import android.compat.annotation.UnsupportedAppUsage;
* using a WYSIWYG graphics editor.
* </p>
*/
+@android.ravenwood.annotation.RavenwoodKeepWholeClass
public class NinePatch {
/**
* Struct of inset information attached to a 9 patch bitmap.
diff --git a/graphics/java/android/graphics/Paint.java b/graphics/java/android/graphics/Paint.java
index 3d4dccf095f5..a0ca0988e03c 100644
--- a/graphics/java/android/graphics/Paint.java
+++ b/graphics/java/android/graphics/Paint.java
@@ -66,6 +66,7 @@ import java.util.Objects;
* The Paint class holds the style and color information about how to draw
* geometries, text and bitmaps.
*/
+@android.ravenwood.annotation.RavenwoodKeepWholeClass
public class Paint {
private static final String TAG = "Paint";
diff --git a/graphics/java/android/graphics/PaintFlagsDrawFilter.java b/graphics/java/android/graphics/PaintFlagsDrawFilter.java
index 232661113b5a..f4c49b11ea96 100644
--- a/graphics/java/android/graphics/PaintFlagsDrawFilter.java
+++ b/graphics/java/android/graphics/PaintFlagsDrawFilter.java
@@ -16,6 +16,7 @@
package android.graphics;
+@android.ravenwood.annotation.RavenwoodKeepWholeClass
public class PaintFlagsDrawFilter extends DrawFilter {
/**
* Subclass of DrawFilter that affects every paint by first clearing
diff --git a/graphics/java/android/graphics/PathDashPathEffect.java b/graphics/java/android/graphics/PathDashPathEffect.java
index 2b6a6edcc266..dc92e6caabcd 100644
--- a/graphics/java/android/graphics/PathDashPathEffect.java
+++ b/graphics/java/android/graphics/PathDashPathEffect.java
@@ -16,6 +16,7 @@
package android.graphics;
+@android.ravenwood.annotation.RavenwoodKeepWholeClass
public class PathDashPathEffect extends PathEffect {
public enum Style {
diff --git a/graphics/java/android/graphics/PathEffect.java b/graphics/java/android/graphics/PathEffect.java
index 3292501e6324..9bb71935cfd0 100644
--- a/graphics/java/android/graphics/PathEffect.java
+++ b/graphics/java/android/graphics/PathEffect.java
@@ -21,6 +21,7 @@ package android.graphics;
* the geometry of a drawing primitive before it is transformed by the
* canvas' matrix and drawn.
*/
+@android.ravenwood.annotation.RavenwoodKeepWholeClass
public class PathEffect {
protected void finalize() throws Throwable {
diff --git a/graphics/java/android/graphics/PathIterator.java b/graphics/java/android/graphics/PathIterator.java
index d7caabf9f91b..1ed70d02d140 100644
--- a/graphics/java/android/graphics/PathIterator.java
+++ b/graphics/java/android/graphics/PathIterator.java
@@ -34,6 +34,7 @@ import java.util.Iterator;
* <code>PathIterator</code> can be used to query a given {@link Path} object, to discover its
* operations and point values.
*/
+@android.ravenwood.annotation.RavenwoodKeepWholeClass
public class PathIterator implements Iterator<PathIterator.Segment> {
private final float[] mPointsArray;
@@ -47,9 +48,11 @@ public class PathIterator implements Iterator<PathIterator.Segment> {
private static final boolean IS_DALVIK = "dalvik".equalsIgnoreCase(
System.getProperty("java.vm.name"));
- private static final NativeAllocationRegistry sRegistry =
- NativeAllocationRegistry.createMalloced(
- PathIterator.class.getClassLoader(), nGetFinalizer());
+ private static class NoImagePreloadHolder {
+ private static final NativeAllocationRegistry sRegistry =
+ NativeAllocationRegistry.createMalloced(
+ PathIterator.class.getClassLoader(), nGetFinalizer());
+ }
/**
* The <code>Verb</code> indicates the operation for a given segment of a path. These
@@ -69,6 +72,11 @@ public class PathIterator implements Iterator<PathIterator.Segment> {
public static final int VERB_CLOSE = 5;
public static final int VERB_DONE = 6;
+
+ static {
+ // Keep <cinit> exist in bytecode
+ }
+
/**
* Returns a {@link PathIterator} object for this path, which can be used to query the
* data (operations and points) in the path. Iterators can only be used on Path objects
@@ -90,7 +98,7 @@ public class PathIterator implements Iterator<PathIterator.Segment> {
mPointsArray = new float[POINT_ARRAY_SIZE];
mPointsAddress = 0;
}
- sRegistry.registerNativeAllocation(this, mNativeIterator);
+ NoImagePreloadHolder.sRegistry.registerNativeAllocation(this, mNativeIterator);
}
/**
diff --git a/graphics/java/android/graphics/PathMeasure.java b/graphics/java/android/graphics/PathMeasure.java
index 2c6cfa5c2e3d..4d123db41ee7 100644
--- a/graphics/java/android/graphics/PathMeasure.java
+++ b/graphics/java/android/graphics/PathMeasure.java
@@ -16,6 +16,7 @@
package android.graphics;
+@android.ravenwood.annotation.RavenwoodKeepWholeClass
public class PathMeasure {
private Path mPath;
diff --git a/graphics/java/android/graphics/Picture.java b/graphics/java/android/graphics/Picture.java
index ee4165b8da05..54eb2bc659bc 100644
--- a/graphics/java/android/graphics/Picture.java
+++ b/graphics/java/android/graphics/Picture.java
@@ -33,6 +33,7 @@ import java.io.OutputStream;
* <p class="note"><strong>Note:</strong> Prior to API level 23 a picture cannot
* be replayed on a hardware accelerated canvas.</p>
*/
+@android.ravenwood.annotation.RavenwoodKeepWholeClass
public class Picture {
private PictureCanvas mRecordingCanvas;
// TODO: Figure out if this was a false-positive
diff --git a/graphics/java/android/graphics/PixelXorXfermode.java b/graphics/java/android/graphics/PixelXorXfermode.java
index 27884e07ecfb..64278525bb1f 100644
--- a/graphics/java/android/graphics/PixelXorXfermode.java
+++ b/graphics/java/android/graphics/PixelXorXfermode.java
@@ -20,6 +20,7 @@ package android.graphics;
* @removed
*/
@Deprecated
+@android.ravenwood.annotation.RavenwoodKeepWholeClass
public class PixelXorXfermode extends Xfermode {
public PixelXorXfermode(int opColor) {
diff --git a/graphics/java/android/graphics/PorterDuff.java b/graphics/java/android/graphics/PorterDuff.java
index eb940e2f9017..730a804e17c3 100644
--- a/graphics/java/android/graphics/PorterDuff.java
+++ b/graphics/java/android/graphics/PorterDuff.java
@@ -26,6 +26,7 @@ import android.compat.annotation.UnsupportedAppUsage;
*
* Consider using {@link BlendMode} instead as it provides a wider variety of tinting options
*/
+@android.ravenwood.annotation.RavenwoodKeepWholeClass
public class PorterDuff {
/**
* {@usesMathJax}
diff --git a/graphics/java/android/graphics/PorterDuffColorFilter.java b/graphics/java/android/graphics/PorterDuffColorFilter.java
index 0700f217ecf0..777ef6ce906b 100644
--- a/graphics/java/android/graphics/PorterDuffColorFilter.java
+++ b/graphics/java/android/graphics/PorterDuffColorFilter.java
@@ -25,6 +25,7 @@ import android.os.Build;
* A color filter that can be used to tint the source pixels using a single
* color and a specific {@link PorterDuff Porter-Duff composite mode}.
*/
+@android.ravenwood.annotation.RavenwoodKeepWholeClass
public class PorterDuffColorFilter extends ColorFilter {
@ColorInt
private int mColor;
diff --git a/graphics/java/android/graphics/PorterDuffXfermode.java b/graphics/java/android/graphics/PorterDuffXfermode.java
index 83d0507a5074..e10d7370d6f9 100644
--- a/graphics/java/android/graphics/PorterDuffXfermode.java
+++ b/graphics/java/android/graphics/PorterDuffXfermode.java
@@ -23,6 +23,7 @@ package android.graphics;
* information on the available alpha compositing and blending modes.</p>
*
*/
+@android.ravenwood.annotation.RavenwoodKeepWholeClass
public class PorterDuffXfermode extends Xfermode {
/**
* Create an xfermode that uses the specified porter-duff mode.
diff --git a/graphics/java/android/graphics/PostProcessor.java b/graphics/java/android/graphics/PostProcessor.java
index 6fed39b9975d..066214ac6cd6 100644
--- a/graphics/java/android/graphics/PostProcessor.java
+++ b/graphics/java/android/graphics/PostProcessor.java
@@ -37,6 +37,7 @@ import android.graphics.drawable.Drawable;
*
* <p>Supplied to ImageDecoder via {@link ImageDecoder#setPostProcessor setPostProcessor}.</p>
*/
+@android.ravenwood.annotation.RavenwoodKeepWholeClass
public interface PostProcessor {
/**
* Do any processing after (for example) decoding.
diff --git a/graphics/java/android/graphics/RadialGradient.java b/graphics/java/android/graphics/RadialGradient.java
index e582e66e1627..06e92eae9c82 100644
--- a/graphics/java/android/graphics/RadialGradient.java
+++ b/graphics/java/android/graphics/RadialGradient.java
@@ -24,6 +24,7 @@ import android.annotation.Nullable;
import android.compat.annotation.UnsupportedAppUsage;
import android.os.Build;
+@android.ravenwood.annotation.RavenwoodKeepWholeClass
public class RadialGradient extends Shader {
@UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
private float mX;
diff --git a/graphics/java/android/graphics/Rasterizer.java b/graphics/java/android/graphics/Rasterizer.java
index 575095426563..5e67da50a40d 100644
--- a/graphics/java/android/graphics/Rasterizer.java
+++ b/graphics/java/android/graphics/Rasterizer.java
@@ -24,6 +24,7 @@ package android.graphics;
/**
* @removed feature is not supported by hw-accerlerated or PDF backends
*/
+@android.ravenwood.annotation.RavenwoodKeepWholeClass
public class Rasterizer {
protected void finalize() throws Throwable { }
diff --git a/graphics/java/android/graphics/RecordingCanvas.java b/graphics/java/android/graphics/RecordingCanvas.java
index cc5b3b94e0fa..a56f461e511a 100644
--- a/graphics/java/android/graphics/RecordingCanvas.java
+++ b/graphics/java/android/graphics/RecordingCanvas.java
@@ -33,6 +33,7 @@ import dalvik.annotation.optimization.CriticalNative;
* {@link RenderNode#endRecording()} is called. It must not be retained beyond that as it is
* internally reused.
*/
+@android.ravenwood.annotation.RavenwoodKeepWholeClass
public final class RecordingCanvas extends BaseRecordingCanvas {
// The recording canvas pool should be large enough to handle a deeply nested
// view hierarchy because display lists are generated recursively.
diff --git a/graphics/java/android/graphics/Region.java b/graphics/java/android/graphics/Region.java
index 29708738d2db..e2215d4bf300 100644
--- a/graphics/java/android/graphics/Region.java
+++ b/graphics/java/android/graphics/Region.java
@@ -23,6 +23,7 @@ import android.os.Parcel;
import android.os.Parcelable;
import android.util.Pools.SynchronizedPool;
+@android.ravenwood.annotation.RavenwoodKeepWholeClass
public class Region implements Parcelable {
private static final int MAX_POOL_SIZE = 10;
diff --git a/graphics/java/android/graphics/RegionIterator.java b/graphics/java/android/graphics/RegionIterator.java
index 443b23c1b5fc..5d74487e5a8e 100644
--- a/graphics/java/android/graphics/RegionIterator.java
+++ b/graphics/java/android/graphics/RegionIterator.java
@@ -16,6 +16,7 @@
package android.graphics;
+@android.ravenwood.annotation.RavenwoodKeepWholeClass
public class RegionIterator {
/**
diff --git a/graphics/java/android/graphics/RenderEffect.java b/graphics/java/android/graphics/RenderEffect.java
index b8a46856601e..06bfb82ef630 100644
--- a/graphics/java/android/graphics/RenderEffect.java
+++ b/graphics/java/android/graphics/RenderEffect.java
@@ -30,6 +30,7 @@ import libcore.util.NativeAllocationRegistry;
* Additionally a {@link RenderEffect} can be applied to a View's backing RenderNode through
* {@link android.view.View#setRenderEffect(RenderEffect)}
*/
+@android.ravenwood.annotation.RavenwoodKeepWholeClass
public final class RenderEffect {
private static class RenderEffectHolder {
diff --git a/graphics/java/android/graphics/RenderNode.java b/graphics/java/android/graphics/RenderNode.java
index 03a8b306f99d..fa41876187cf 100644
--- a/graphics/java/android/graphics/RenderNode.java
+++ b/graphics/java/android/graphics/RenderNode.java
@@ -192,6 +192,7 @@ import java.lang.ref.WeakReference;
* top-level content is desired, and finally calling {@link Surface#unlockCanvasAndPost(Canvas)}.
* </p>
*/
+@android.ravenwood.annotation.RavenwoodKeepWholeClass
public final class RenderNode {
// Use a Holder to allow static initialization in the boot image.
diff --git a/graphics/java/android/graphics/RuntimeColorFilter.java b/graphics/java/android/graphics/RuntimeColorFilter.java
index a64acfe767a9..06aecc3f2c49 100644
--- a/graphics/java/android/graphics/RuntimeColorFilter.java
+++ b/graphics/java/android/graphics/RuntimeColorFilter.java
@@ -37,6 +37,7 @@ import com.android.graphics.hwui.flags.Flags;
* </pre>
*/
@FlaggedApi(Flags.FLAG_RUNTIME_COLOR_FILTERS_BLENDERS)
+@android.ravenwood.annotation.RavenwoodKeepWholeClass
public class RuntimeColorFilter extends ColorFilter {
private String mAgsl;
diff --git a/graphics/java/android/graphics/RuntimeShader.java b/graphics/java/android/graphics/RuntimeShader.java
index db2376e008f5..6464f72490c4 100644
--- a/graphics/java/android/graphics/RuntimeShader.java
+++ b/graphics/java/android/graphics/RuntimeShader.java
@@ -248,6 +248,7 @@ import libcore.util.NativeAllocationRegistry;
* the bitmap), remember that the coordinates are local to the canvas.</p>
*
*/
+@android.ravenwood.annotation.RavenwoodKeepWholeClass
public class RuntimeShader extends Shader {
private static class NoImagePreloadHolder {
diff --git a/graphics/java/android/graphics/RuntimeXfermode.java b/graphics/java/android/graphics/RuntimeXfermode.java
index c8a0b1a11339..1e20bd352244 100644
--- a/graphics/java/android/graphics/RuntimeXfermode.java
+++ b/graphics/java/android/graphics/RuntimeXfermode.java
@@ -39,6 +39,7 @@ import libcore.util.NativeAllocationRegistry;
* </pre>
*/
@FlaggedApi(Flags.FLAG_RUNTIME_COLOR_FILTERS_BLENDERS)
+@android.ravenwood.annotation.RavenwoodKeepWholeClass
public class RuntimeXfermode extends Xfermode {
private static class NoImagePreloadHolder {
diff --git a/graphics/java/android/graphics/Shader.java b/graphics/java/android/graphics/Shader.java
index 4d6beadc0fdd..369fab45bf69 100644
--- a/graphics/java/android/graphics/Shader.java
+++ b/graphics/java/android/graphics/Shader.java
@@ -29,6 +29,7 @@ import libcore.util.NativeAllocationRegistry;
* paint.setShader(shader). After that any object (other than a bitmap) that is
* drawn with that paint will get its color(s) from the shader.
*/
+@android.ravenwood.annotation.RavenwoodKeepWholeClass
public class Shader {
private static class NoImagePreloadHolder {
diff --git a/graphics/java/android/graphics/SumPathEffect.java b/graphics/java/android/graphics/SumPathEffect.java
index 8fedc317c428..3543e101fb38 100644
--- a/graphics/java/android/graphics/SumPathEffect.java
+++ b/graphics/java/android/graphics/SumPathEffect.java
@@ -16,6 +16,7 @@
package android.graphics;
+@android.ravenwood.annotation.RavenwoodKeepWholeClass
public class SumPathEffect extends PathEffect {
/**
diff --git a/graphics/java/android/graphics/SurfaceTexture.java b/graphics/java/android/graphics/SurfaceTexture.java
index 5caedba364be..df384ead58fb 100644
--- a/graphics/java/android/graphics/SurfaceTexture.java
+++ b/graphics/java/android/graphics/SurfaceTexture.java
@@ -78,6 +78,7 @@ import java.lang.ref.WeakReference;
* frame-available callback is called on an arbitrary thread, so unless special care is taken {@link
* #updateTexImage} should not be called directly from the callback.
*/
+@android.ravenwood.annotation.RavenwoodKeepWholeClass
public class SurfaceTexture {
private final Looper mCreatorLooper;
@UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
diff --git a/graphics/java/android/graphics/SweepGradient.java b/graphics/java/android/graphics/SweepGradient.java
index 3a29395b1717..94219259a69d 100644
--- a/graphics/java/android/graphics/SweepGradient.java
+++ b/graphics/java/android/graphics/SweepGradient.java
@@ -23,6 +23,7 @@ import android.annotation.Nullable;
import android.compat.annotation.UnsupportedAppUsage;
import android.os.Build;
+@android.ravenwood.annotation.RavenwoodKeepWholeClass
public class SweepGradient extends Shader {
@UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
private float mCx;
diff --git a/graphics/java/android/graphics/TableMaskFilter.java b/graphics/java/android/graphics/TableMaskFilter.java
index 204f9705852a..ca7627c40031 100644
--- a/graphics/java/android/graphics/TableMaskFilter.java
+++ b/graphics/java/android/graphics/TableMaskFilter.java
@@ -21,6 +21,7 @@ import android.compat.annotation.UnsupportedAppUsage;
/**
* @hide
*/
+@android.ravenwood.annotation.RavenwoodKeepWholeClass
public class TableMaskFilter extends MaskFilter {
public TableMaskFilter(byte[] table) {
diff --git a/graphics/java/android/graphics/TemporaryBuffer.java b/graphics/java/android/graphics/TemporaryBuffer.java
index ef3f7f704e0d..681c48ea9f71 100644
--- a/graphics/java/android/graphics/TemporaryBuffer.java
+++ b/graphics/java/android/graphics/TemporaryBuffer.java
@@ -23,6 +23,7 @@ import com.android.internal.util.ArrayUtils;
/**
* @hide
*/
+@android.ravenwood.annotation.RavenwoodKeepWholeClass
public class TemporaryBuffer {
@UnsupportedAppUsage
public static char[] obtain(int len) {
diff --git a/graphics/java/android/graphics/TextureLayer.java b/graphics/java/android/graphics/TextureLayer.java
index ac1bd6902062..981b78a7b5b8 100644
--- a/graphics/java/android/graphics/TextureLayer.java
+++ b/graphics/java/android/graphics/TextureLayer.java
@@ -29,6 +29,7 @@ import com.android.internal.util.VirtualRefBasePtr;
*
* @hide TODO: Make this a SystemApi for b/155905258
*/
+@android.ravenwood.annotation.RavenwoodKeepWholeClass
public final class TextureLayer implements AutoCloseable {
private HardwareRenderer mRenderer;
private VirtualRefBasePtr mFinalizer;
diff --git a/graphics/java/android/graphics/Typeface.java b/graphics/java/android/graphics/Typeface.java
index 874b847c709c..d1aca34c7b8d 100644
--- a/graphics/java/android/graphics/Typeface.java
+++ b/graphics/java/android/graphics/Typeface.java
@@ -42,6 +42,7 @@ import android.os.SystemProperties;
import android.os.Trace;
import android.provider.FontRequest;
import android.provider.FontsContract;
+import android.ravenwood.annotation.RavenwoodReplace;
import android.system.ErrnoException;
import android.system.OsConstants;
import android.text.FontConfig;
@@ -86,6 +87,7 @@ import java.util.Objects;
* textSize, textSkewX, textScaleX to specify
* how text appears when drawn (and measured).
*/
+@android.ravenwood.annotation.RavenwoodKeepWholeClass
public class Typeface {
private static String TAG = "Typeface";
@@ -93,9 +95,11 @@ public class Typeface {
/** @hide */
public static final boolean ENABLE_LAZY_TYPEFACE_INITIALIZATION = true;
- private static final NativeAllocationRegistry sRegistry =
- NativeAllocationRegistry.createMalloced(
- Typeface.class.getClassLoader(), nativeGetReleaseFunc());
+ private static class NoImagePreloadHolder {
+ static final NativeAllocationRegistry sRegistry =
+ NativeAllocationRegistry.createMalloced(
+ Typeface.class.getClassLoader(), nativeGetReleaseFunc());
+ }
/** The default NORMAL typeface object */
public static final Typeface DEFAULT = null;
@@ -1284,7 +1288,7 @@ public class Typeface {
}
native_instance = ni;
- mCleaner = sRegistry.registerNativeAllocation(this, native_instance);
+ mCleaner = NoImagePreloadHolder.sRegistry.registerNativeAllocation(this, native_instance);
mStyle = nativeGetStyle(ni);
mWeight = nativeGetWeight(ni);
mIsVariationInstance = nativeIsVariationInstance(ni);
@@ -1560,9 +1564,23 @@ public class Typeface {
}
static {
+ staticInitializer();
+ }
+
+ @RavenwoodReplace(reason = "Prevent circular reference on host side JVM", bug = 337329128)
+ private static void staticInitializer() {
+ init();
+ }
+
+ private static void staticInitializer$ravenwood() {
+ /* no-op */
+ }
+
+ /** @hide */
+ public static void init() {
// Preload Roboto-Regular.ttf in Zygote for improving app launch performance.
- preloadFontFile("/system/fonts/Roboto-Regular.ttf");
- preloadFontFile("/system/fonts/RobotoStatic-Regular.ttf");
+ preloadFontFile(SystemFonts.SYSTEM_FONT_DIR + "Roboto-Regular.ttf");
+ preloadFontFile(SystemFonts.SYSTEM_FONT_DIR + "RobotoStatic-Regular.ttf");
String locale = SystemProperties.get("persist.sys.locale", "en-US");
String script = ULocale.addLikelySubtags(ULocale.forLanguageTag(locale)).getScript();
@@ -1642,6 +1660,21 @@ public class Typeface {
setSystemFontMap(typefaceMap);
}
+ /**
+ * {@link #loadPreinstalledSystemFontMap()} does not actually initialize the native
+ * system font APIs. Add a new method to actually load the font files without going
+ * through SharedMemory.
+ *
+ * @hide
+ */
+ public static void loadNativeSystemFonts() {
+ synchronized (SYSTEM_FONT_MAP_LOCK) {
+ for (var type : sSystemFontMap.values()) {
+ nativeAddFontCollections(type.native_instance);
+ }
+ }
+ }
+
static {
if (!ENABLE_LAZY_TYPEFACE_INITIALIZATION) {
loadPreinstalledSystemFontMap();
diff --git a/graphics/java/android/graphics/Xfermode.java b/graphics/java/android/graphics/Xfermode.java
index fb689e4cb9c2..eda9e3c1055d 100644
--- a/graphics/java/android/graphics/Xfermode.java
+++ b/graphics/java/android/graphics/Xfermode.java
@@ -28,4 +28,5 @@ package android.graphics;
* specified in the Modes enum. When an Xfermode is assigned to a Paint, then
* objects drawn with that paint have the xfermode applied.
*/
+@android.ravenwood.annotation.RavenwoodKeepWholeClass
public class Xfermode {}
diff --git a/graphics/java/android/graphics/YuvImage.java b/graphics/java/android/graphics/YuvImage.java
index b0c7f202f23a..2b7f40493e8d 100644
--- a/graphics/java/android/graphics/YuvImage.java
+++ b/graphics/java/android/graphics/YuvImage.java
@@ -32,6 +32,7 @@ import java.io.OutputStream;
* To compress a rectangle region in the YUV data, users have to specify the
* region by left, top, width and height.
*/
+@android.ravenwood.annotation.RavenwoodKeepWholeClass
public class YuvImage {
/**
diff --git a/graphics/java/android/graphics/fonts/Font.java b/graphics/java/android/graphics/fonts/Font.java
index 2893177aafc5..8be340005543 100644
--- a/graphics/java/android/graphics/fonts/Font.java
+++ b/graphics/java/android/graphics/fonts/Font.java
@@ -44,6 +44,7 @@ import java.io.IOException;
import java.io.InputStream;
import java.nio.ByteBuffer;
import java.nio.ByteOrder;
+import java.nio.channels.Channels;
import java.nio.channels.FileChannel;
import java.util.Arrays;
import java.util.Collections;
@@ -54,6 +55,7 @@ import java.util.Set;
/**
* A font class can be used for creating FontFamily.
*/
+@android.ravenwood.annotation.RavenwoodKeepWholeClass
public final class Font {
private static final String TAG = "Font";
@@ -293,7 +295,14 @@ public final class Font {
int capacity = assetStream.available();
ByteBuffer buffer = ByteBuffer.allocateDirect(capacity);
buffer.order(ByteOrder.nativeOrder());
- assetStream.read(buffer.array(), buffer.arrayOffset(), assetStream.available());
+ if (buffer.hasArray()) {
+ assetStream.read(buffer.array(), buffer.arrayOffset(), assetStream.available());
+ } else {
+ // Direct buffer does not have a backing array on Ravenwood,
+ // wrap it with a channel and read from it
+ var ch = Channels.newChannel(assetStream);
+ ch.read(buffer.duplicate());
+ }
if (assetStream.read() != -1) {
throw new IOException("Unable to access full contents of " + path);
diff --git a/graphics/java/android/graphics/fonts/FontCustomizationParser.java b/graphics/java/android/graphics/fonts/FontCustomizationParser.java
index b7bf0553bcc6..732a5f3bfce4 100644
--- a/graphics/java/android/graphics/fonts/FontCustomizationParser.java
+++ b/graphics/java/android/graphics/fonts/FontCustomizationParser.java
@@ -43,6 +43,7 @@ import java.util.Map;
*
* @hide
*/
+@android.ravenwood.annotation.RavenwoodKeepWholeClass
public class FontCustomizationParser {
private static final String TAG = "FontCustomizationParser";
diff --git a/graphics/java/android/graphics/fonts/FontFamily.java b/graphics/java/android/graphics/fonts/FontFamily.java
index 5a7b0bbca399..0ab46398c924 100644
--- a/graphics/java/android/graphics/fonts/FontFamily.java
+++ b/graphics/java/android/graphics/fonts/FontFamily.java
@@ -66,6 +66,7 @@ import java.util.Set;
* </p>
*
*/
+@android.ravenwood.annotation.RavenwoodKeepWholeClass
public final class FontFamily {
private static final String TAG = "FontFamily";
diff --git a/graphics/java/android/graphics/fonts/FontFileUtil.java b/graphics/java/android/graphics/fonts/FontFileUtil.java
index abcafb666576..305ab3b39995 100644
--- a/graphics/java/android/graphics/fonts/FontFileUtil.java
+++ b/graphics/java/android/graphics/fonts/FontFileUtil.java
@@ -32,6 +32,7 @@ import java.util.Set;
* Provides a utility for font file operations.
* @hide
*/
+@android.ravenwood.annotation.RavenwoodKeepWholeClass
public class FontFileUtil {
private FontFileUtil() {} // Do not instantiate
diff --git a/graphics/java/android/graphics/fonts/FontStyle.java b/graphics/java/android/graphics/fonts/FontStyle.java
index 48969aa71059..b3ddbed645ff 100644
--- a/graphics/java/android/graphics/fonts/FontStyle.java
+++ b/graphics/java/android/graphics/fonts/FontStyle.java
@@ -44,6 +44,7 @@ import java.util.Objects;
* </p>
*
*/
+@android.ravenwood.annotation.RavenwoodKeepWholeClass
public final class FontStyle {
private static final String TAG = "FontStyle";
diff --git a/graphics/java/android/graphics/fonts/FontVariationAxis.java b/graphics/java/android/graphics/fonts/FontVariationAxis.java
index 30a248bb3e0e..1d715940d628 100644
--- a/graphics/java/android/graphics/fonts/FontVariationAxis.java
+++ b/graphics/java/android/graphics/fonts/FontVariationAxis.java
@@ -31,6 +31,7 @@ import java.util.regex.Pattern;
/**
* Class that holds information about single font variation axis.
*/
+@android.ravenwood.annotation.RavenwoodKeepWholeClass
public final class FontVariationAxis {
@UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P, trackingBug = 115609023)
private final int mTag;
diff --git a/graphics/java/android/graphics/fonts/SystemFonts.java b/graphics/java/android/graphics/fonts/SystemFonts.java
index 0e25c346064c..599c42659ece 100644
--- a/graphics/java/android/graphics/fonts/SystemFonts.java
+++ b/graphics/java/android/graphics/fonts/SystemFonts.java
@@ -25,6 +25,7 @@ import android.annotation.Nullable;
import android.graphics.FontListParser;
import android.graphics.Typeface;
import android.os.LocaleList;
+import android.ravenwood.annotation.RavenwoodReplace;
import android.text.FontConfig;
import android.util.ArrayMap;
import android.util.Log;
@@ -32,6 +33,7 @@ import android.util.SparseIntArray;
import com.android.internal.annotations.GuardedBy;
import com.android.internal.annotations.VisibleForTesting;
+import com.android.internal.ravenwood.RavenwoodEnvironment;
import org.xmlpull.v1.XmlPullParserException;
@@ -50,23 +52,46 @@ import java.util.Set;
/**
* Provides the system font configurations.
*/
+@android.ravenwood.annotation.RavenwoodKeepWholeClass
public final class SystemFonts {
private static final String TAG = "SystemFonts";
- private static final String FONTS_XML = "/system/etc/font_fallback.xml";
- private static final String LEGACY_FONTS_XML = "/system/etc/fonts.xml";
+ private static final String FONTS_XML = getFontsXmlDir() + "font_fallback.xml";
+ /** @hide */
+ public static final String LEGACY_FONTS_XML = getFontsXmlDir() + "fonts.xml";
/** @hide */
- public static final String SYSTEM_FONT_DIR = "/system/fonts/";
+ public static final String SYSTEM_FONT_DIR = getSystemFontDir();
private static final String OEM_XML = "/product/etc/fonts_customization.xml";
/** @hide */
public static final String OEM_FONT_DIR = "/product/fonts/";
+ private static final String DEVICE_FONTS_XML_DIR = "/system/etc/";
+ private static final String DEVICE_FONT_DIR = "/system/fonts/";
+
private SystemFonts() {} // Do not instansiate.
private static final Object LOCK = new Object();
private static @GuardedBy("sLock") Set<Font> sAvailableFonts;
+ @RavenwoodReplace
+ private static String getFontsXmlDir() {
+ return DEVICE_FONTS_XML_DIR;
+ }
+
+ private static String getFontsXmlDir$ravenwood() {
+ return RavenwoodEnvironment.getInstance().getRavenwoodRuntimePath() + "fonts/";
+ }
+
+ @RavenwoodReplace
+ private static String getSystemFontDir() {
+ return DEVICE_FONT_DIR;
+ }
+
+ private static String getSystemFontDir$ravenwood() {
+ return RavenwoodEnvironment.getInstance().getRavenwoodRuntimePath() + "fonts/";
+ }
+
/**
* Returns all available font files in the system.
*
diff --git a/graphics/java/android/graphics/text/GraphemeBreak.java b/graphics/java/android/graphics/text/GraphemeBreak.java
index f82b2fd659cc..0bc1e3b0cae3 100644
--- a/graphics/java/android/graphics/text/GraphemeBreak.java
+++ b/graphics/java/android/graphics/text/GraphemeBreak.java
@@ -17,6 +17,7 @@
package android.graphics.text;
/** @hide */
+@android.ravenwood.annotation.RavenwoodKeepWholeClass
public class GraphemeBreak {
private GraphemeBreak() { }
diff --git a/graphics/java/android/graphics/text/LineBreakConfig.java b/graphics/java/android/graphics/text/LineBreakConfig.java
index 5a1086cef407..fa3cfbdd4e97 100644
--- a/graphics/java/android/graphics/text/LineBreakConfig.java
+++ b/graphics/java/android/graphics/text/LineBreakConfig.java
@@ -24,12 +24,13 @@ import android.annotation.IntDef;
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.annotation.SuppressLint;
-import android.app.ActivityThread;
import android.os.Build;
import android.os.LocaleList;
import android.os.Parcel;
import android.os.Parcelable;
+import dalvik.system.VMRuntime;
+
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.util.Objects;
@@ -41,6 +42,7 @@ import java.util.Objects;
* <a href="https://www.w3.org/TR/css-text-3/#line-break-property" class="external">
* line-break property</a> for more information.
*/
+@android.ravenwood.annotation.RavenwoodKeepWholeClass
public final class LineBreakConfig implements Parcelable {
/**
* No hyphenation preference is specified.
@@ -484,8 +486,7 @@ public final class LineBreakConfig implements Parcelable {
* @hide
*/
public static @LineBreakStyle int getResolvedLineBreakStyle(@Nullable LineBreakConfig config) {
- final int targetSdkVersion = ActivityThread.currentApplication().getApplicationInfo()
- .targetSdkVersion;
+ final int targetSdkVersion = VMRuntime.getRuntime().getTargetSdkVersion();
final int defaultStyle;
final int vicVersion = Build.VERSION_CODES.VANILLA_ICE_CREAM;
if (targetSdkVersion >= vicVersion) {
@@ -519,8 +520,7 @@ public final class LineBreakConfig implements Parcelable {
*/
public static @LineBreakWordStyle int getResolvedLineBreakWordStyle(
@Nullable LineBreakConfig config) {
- final int targetSdkVersion = ActivityThread.currentApplication().getApplicationInfo()
- .targetSdkVersion;
+ final int targetSdkVersion = VMRuntime.getRuntime().getTargetSdkVersion();
final int defaultWordStyle;
final int vicVersion = Build.VERSION_CODES.VANILLA_ICE_CREAM;
if (targetSdkVersion >= vicVersion) {
diff --git a/graphics/java/android/graphics/text/LineBreaker.java b/graphics/java/android/graphics/text/LineBreaker.java
index 94de066c9182..29135b837352 100644
--- a/graphics/java/android/graphics/text/LineBreaker.java
+++ b/graphics/java/android/graphics/text/LineBreaker.java
@@ -91,6 +91,7 @@ import java.lang.annotation.RetentionPolicy;
* </pre>
* </p>
*/
+@android.ravenwood.annotation.RavenwoodKeepWholeClass
public class LineBreaker {
/** @hide */
@IntDef(prefix = { "BREAK_STRATEGY_" }, value = {
diff --git a/graphics/java/android/graphics/text/MeasuredText.java b/graphics/java/android/graphics/text/MeasuredText.java
index 884268a4b85c..f58d5311fa30 100644
--- a/graphics/java/android/graphics/text/MeasuredText.java
+++ b/graphics/java/android/graphics/text/MeasuredText.java
@@ -56,6 +56,7 @@ import java.util.Objects;
* </pre>
* </p>
*/
+@android.ravenwood.annotation.RavenwoodKeepWholeClass
public class MeasuredText {
private static final String TAG = "MeasuredText";
diff --git a/graphics/java/android/graphics/text/PositionedGlyphs.java b/graphics/java/android/graphics/text/PositionedGlyphs.java
index 43216ba6e087..31125ffa7bd4 100644
--- a/graphics/java/android/graphics/text/PositionedGlyphs.java
+++ b/graphics/java/android/graphics/text/PositionedGlyphs.java
@@ -46,6 +46,7 @@ import java.util.Objects;
* @see TextRunShaper#shapeTextRun(char[], int, int, int, int, float, float, boolean, Paint)
* @see TextRunShaper#shapeTextRun(CharSequence, int, int, int, int, float, float, boolean, Paint)
*/
+@android.ravenwood.annotation.RavenwoodKeepWholeClass
public final class PositionedGlyphs {
private static class NoImagePreloadHolder {
private static final NativeAllocationRegistry REGISTRY =
diff --git a/graphics/java/android/graphics/text/TextRunShaper.java b/graphics/java/android/graphics/text/TextRunShaper.java
index 19ea04a48046..f1e3d67e9925 100644
--- a/graphics/java/android/graphics/text/TextRunShaper.java
+++ b/graphics/java/android/graphics/text/TextRunShaper.java
@@ -40,6 +40,7 @@ import com.android.internal.util.Preconditions;
* @see android.text.TextShaper#shapeText(CharSequence, int, int, TextDirectionHeuristic, TextPaint,
* TextShaper.GlyphsConsumer)
*/
+@android.ravenwood.annotation.RavenwoodKeepWholeClass
public class TextRunShaper {
private TextRunShaper() {} // Do not instantiate
diff --git a/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/SplitPresenter.java b/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/SplitPresenter.java
index eb59d6efdeff..7ab9e2e65b76 100644
--- a/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/SplitPresenter.java
+++ b/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/SplitPresenter.java
@@ -705,8 +705,7 @@ class SplitPresenter extends JetpackTaskFragmentOrganizer {
}
private static boolean isOverlayTransitionSupported() {
- return Flags.moveAnimationOptionsToChange()
- && Flags.activityEmbeddingOverlayPresentationFlag();
+ return Flags.activityEmbeddingOverlayPresentationFlag();
}
@NonNull
diff --git a/libs/WindowManager/Shell/aconfig/multitasking.aconfig b/libs/WindowManager/Shell/aconfig/multitasking.aconfig
index a08f88a5b937..1e72d64397d7 100644
--- a/libs/WindowManager/Shell/aconfig/multitasking.aconfig
+++ b/libs/WindowManager/Shell/aconfig/multitasking.aconfig
@@ -184,3 +184,13 @@ flag {
description: "Try out bubble bar on phones"
bug: "394869612"
}
+
+flag {
+ name: "enable_bubble_task_view_listener"
+ namespace: "multitasking"
+ description: "Use the same taskview listener for bubble bar and floating"
+ bug: "272102927"
+ metadata {
+ purpose: PURPOSE_BUGFIX
+ }
+}
diff --git a/libs/WindowManager/Shell/multivalentTests/AndroidManifest.xml b/libs/WindowManager/Shell/multivalentTests/AndroidManifest.xml
index 95cd1c72a2af..800ea7446b6e 100644
--- a/libs/WindowManager/Shell/multivalentTests/AndroidManifest.xml
+++ b/libs/WindowManager/Shell/multivalentTests/AndroidManifest.xml
@@ -2,6 +2,7 @@
package="com.android.wm.shell.multivalenttests">
<uses-permission android:name="android.permission.MANAGE_ACTIVITY_TASKS"/>
+ <uses-permission android:name="android.permission.GET_INTENT_SENDER_INTENT"/>
<application android:debuggable="true" android:supportsRtl="true" >
<uses-library android:name="android.test.runner" />
diff --git a/libs/WindowManager/Shell/multivalentTests/src/com/android/wm/shell/bubbles/BubbleTaskViewListenerTest.kt b/libs/WindowManager/Shell/multivalentTests/src/com/android/wm/shell/bubbles/BubbleTaskViewListenerTest.kt
index 9ebc3d78b3a7..3aefcd5ec6c0 100644
--- a/libs/WindowManager/Shell/multivalentTests/src/com/android/wm/shell/bubbles/BubbleTaskViewListenerTest.kt
+++ b/libs/WindowManager/Shell/multivalentTests/src/com/android/wm/shell/bubbles/BubbleTaskViewListenerTest.kt
@@ -16,6 +16,7 @@
package com.android.wm.shell.bubbles
+import android.app.ActivityOptions
import android.app.Notification
import android.app.PendingIntent
import android.content.ComponentName
@@ -24,6 +25,8 @@ import android.content.Intent
import android.content.pm.ShortcutInfo
import android.graphics.drawable.Icon
import android.os.UserHandle
+import android.platform.test.annotations.EnableFlags
+import android.platform.test.flag.junit.SetFlagsRule
import android.service.notification.NotificationListenerService.Ranking
import android.service.notification.StatusBarNotification
import android.view.View
@@ -33,6 +36,7 @@ import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.SmallTest
import androidx.test.platform.app.InstrumentationRegistry.getInstrumentation
import com.android.internal.protolog.ProtoLog
+import com.android.wm.shell.Flags.FLAG_ENABLE_BUBBLE_ANYTHING
import com.android.wm.shell.R
import com.android.wm.shell.bubbles.Bubbles.BubbleMetadataFlagListener
import com.android.wm.shell.common.TestShellExecutor
@@ -41,6 +45,7 @@ import com.android.wm.shell.taskview.TaskViewController
import com.android.wm.shell.taskview.TaskViewTaskController
import com.google.common.truth.Truth.assertThat
import org.junit.Before
+import org.junit.Rule
import org.junit.Test
import org.junit.runner.RunWith
import org.mockito.Mockito
@@ -48,6 +53,7 @@ import org.mockito.Mockito.never
import org.mockito.Mockito.reset
import org.mockito.kotlin.any
import org.mockito.kotlin.anyOrNull
+import org.mockito.kotlin.argumentCaptor
import org.mockito.kotlin.doReturn
import org.mockito.kotlin.eq
import org.mockito.kotlin.mock
@@ -61,6 +67,9 @@ import org.mockito.kotlin.whenever
@RunWith(AndroidJUnit4::class)
class BubbleTaskViewListenerTest {
+ @get:Rule
+ val setFlagsRule = SetFlagsRule()
+
private val context = ApplicationProvider.getApplicationContext<Context>()
private var taskViewController = mock<TaskViewController>()
@@ -155,9 +164,22 @@ class BubbleTaskViewListenerTest {
}
getInstrumentation().waitForIdleSync()
- // ..so it's pending intent-based, and launches that
+ // ..so it's pending intent-based, so the pending intent should be active
assertThat(b.isPendingIntentActive).isTrue()
- verify(taskViewController).startActivity(any(), eq(pendingIntent), any(), any(), any())
+
+ val intentCaptor = argumentCaptor<Intent>()
+ val optionsCaptor = argumentCaptor<ActivityOptions>()
+
+ verify(taskViewController).startActivity(any(),
+ eq(pendingIntent),
+ intentCaptor.capture(),
+ optionsCaptor.capture(),
+ any())
+ val intentFlags = intentCaptor.lastValue.flags
+ assertThat((intentFlags and Intent.FLAG_ACTIVITY_NEW_DOCUMENT) != 0).isTrue()
+ assertThat((intentFlags and Intent.FLAG_ACTIVITY_MULTIPLE_TASK) != 0).isTrue()
+ assertThat(optionsCaptor.lastValue.launchedFromBubble).isTrue()
+ assertThat(optionsCaptor.lastValue.taskAlwaysOnTop).isTrue()
}
@Test
@@ -178,12 +200,52 @@ class BubbleTaskViewListenerTest {
}
getInstrumentation().waitForIdleSync()
- assertThat(b.isPendingIntentActive).isFalse()
- verify(taskViewController).startShortcutActivity(any(), eq(shortcutInfo), any(), any())
+ val optionsCaptor = argumentCaptor<ActivityOptions>()
+
+ assertThat(b.isPendingIntentActive).isFalse() // not triggered for shortcut chats
+ verify(taskViewController).startShortcutActivity(any(),
+ eq(shortcutInfo),
+ optionsCaptor.capture(),
+ any())
+ assertThat(optionsCaptor.lastValue.launchedFromBubble).isTrue()
+ assertThat(optionsCaptor.lastValue.isApplyActivityFlagsForBubbles).isTrue()
+ assertThat(optionsCaptor.lastValue.taskAlwaysOnTop).isTrue()
}
+ @EnableFlags(FLAG_ENABLE_BUBBLE_ANYTHING)
@Test
- fun onInitialized_appBubble() {
+ fun onInitialized_shortcutBubble() {
+ val shortcutInfo = ShortcutInfo.Builder(context)
+ .setId("mockShortcutId")
+ .build()
+
+ val b = createShortcutBubble(shortcutInfo)
+ bubbleTaskViewListener.setBubble(b)
+
+ assertThat(b.isChat).isFalse()
+ assertThat(b.isShortcut).isTrue()
+ assertThat(b.shortcutInfo).isNotNull()
+
+ getInstrumentation().runOnMainSync {
+ bubbleTaskViewListener.onInitialized()
+ }
+ getInstrumentation().waitForIdleSync()
+
+ val optionsCaptor = argumentCaptor<ActivityOptions>()
+
+ assertThat(b.isPendingIntentActive).isFalse() // chat only triggers setting it active
+ verify(taskViewController).startShortcutActivity(any(),
+ eq(shortcutInfo),
+ optionsCaptor.capture(),
+ any())
+ assertThat(optionsCaptor.lastValue.launchedFromBubble).isFalse() // chat only
+ assertThat(optionsCaptor.lastValue.isApplyActivityFlagsForBubbles).isFalse() // chat only
+ assertThat(optionsCaptor.lastValue.isApplyMultipleTaskFlagForShortcut).isTrue()
+ assertThat(optionsCaptor.lastValue.taskAlwaysOnTop).isTrue()
+ }
+
+ @Test
+ fun onInitialized_appBubble_intent() {
val b = createAppBubble()
bubbleTaskViewListener.setBubble(b)
@@ -194,11 +256,83 @@ class BubbleTaskViewListenerTest {
}
getInstrumentation().waitForIdleSync()
- assertThat(b.isPendingIntentActive).isFalse()
- verify(taskViewController).startActivity(any(), any(), anyOrNull(), any(), any())
+ val intentCaptor = argumentCaptor<Intent>()
+ val optionsCaptor = argumentCaptor<ActivityOptions>()
+
+ assertThat(b.isPendingIntentActive).isFalse() // chat only triggers setting it active
+ verify(taskViewController).startActivity(any(),
+ any(),
+ intentCaptor.capture(),
+ optionsCaptor.capture(),
+ any())
+
+ assertThat((intentCaptor.lastValue.flags
+ and Intent.FLAG_ACTIVITY_MULTIPLE_TASK) != 0).isTrue()
+ assertThat(optionsCaptor.lastValue.launchedFromBubble).isFalse() // chat only
+ assertThat(optionsCaptor.lastValue.isApplyActivityFlagsForBubbles).isFalse() // chat only
+ assertThat(optionsCaptor.lastValue.taskAlwaysOnTop).isTrue()
}
@Test
+ fun onInitialized_appBubble_pendingIntent() {
+ val b = createAppBubble(usePendingIntent = true)
+ bubbleTaskViewListener.setBubble(b)
+
+ assertThat(b.isApp).isTrue()
+
+ getInstrumentation().runOnMainSync {
+ bubbleTaskViewListener.onInitialized()
+ }
+ getInstrumentation().waitForIdleSync()
+
+ val intentCaptor = argumentCaptor<Intent>()
+ val optionsCaptor = argumentCaptor<ActivityOptions>()
+
+ assertThat(b.isPendingIntentActive).isFalse() // chat only triggers setting it active
+ verify(taskViewController).startActivity(any(),
+ any(),
+ intentCaptor.capture(),
+ optionsCaptor.capture(),
+ any())
+
+ assertThat((intentCaptor.lastValue.flags
+ and Intent.FLAG_ACTIVITY_MULTIPLE_TASK) != 0).isTrue()
+ assertThat(optionsCaptor.lastValue.launchedFromBubble).isFalse() // chat only
+ assertThat(optionsCaptor.lastValue.isApplyActivityFlagsForBubbles).isFalse() // chat only
+ assertThat(optionsCaptor.lastValue.taskAlwaysOnTop).isTrue()
+ }
+
+ @Test
+ fun onInitialized_noteBubble() {
+ val b = createNoteBubble()
+ bubbleTaskViewListener.setBubble(b)
+
+ assertThat(b.isNote).isTrue()
+
+ getInstrumentation().runOnMainSync {
+ bubbleTaskViewListener.onInitialized()
+ }
+ getInstrumentation().waitForIdleSync()
+
+ val intentCaptor = argumentCaptor<Intent>()
+ val optionsCaptor = argumentCaptor<ActivityOptions>()
+
+ assertThat(b.isPendingIntentActive).isFalse() // chat only triggers setting it active
+ verify(taskViewController).startActivity(any(),
+ any(),
+ intentCaptor.capture(),
+ optionsCaptor.capture(),
+ any())
+
+ assertThat((intentCaptor.lastValue.flags
+ and Intent.FLAG_ACTIVITY_MULTIPLE_TASK) != 0).isTrue()
+ assertThat(optionsCaptor.lastValue.launchedFromBubble).isFalse() // chat only
+ assertThat(optionsCaptor.lastValue.isApplyActivityFlagsForBubbles).isFalse() // chat only
+ assertThat(optionsCaptor.lastValue.taskAlwaysOnTop).isTrue()
+ }
+
+
+ @Test
fun onInitialized_preparingTransition() {
val b = createAppBubble()
bubbleTaskViewListener.setBubble(b)
@@ -416,13 +550,24 @@ class BubbleTaskViewListenerTest {
assertThat(isNew).isTrue()
}
- private fun createAppBubble(): Bubble {
+ private fun createAppBubble(usePendingIntent: Boolean = false): Bubble {
val target = Intent(context, TestActivity::class.java)
target.setPackage(context.packageName)
+ if (usePendingIntent) {
+ // Robolectric doesn't seem to play nice with PendingIntents, have to mock it.
+ val pendingIntent = mock<PendingIntent>()
+ whenever(pendingIntent.intent).thenReturn(target)
+ return Bubble.createAppBubble(pendingIntent, mock<UserHandle>(),
+ mainExecutor, bgExecutor)
+ }
return Bubble.createAppBubble(target, mock<UserHandle>(), mock<Icon>(),
mainExecutor, bgExecutor)
}
+ private fun createShortcutBubble(shortcutInfo: ShortcutInfo): Bubble {
+ return Bubble.createShortcutBubble(shortcutInfo, mainExecutor, bgExecutor)
+ }
+
private fun createNoteBubble(): Bubble {
val target = Intent(context, TestActivity::class.java)
target.setPackage(context.packageName)
diff --git a/libs/WindowManager/Shell/res/values/dimen.xml b/libs/WindowManager/Shell/res/values/dimen.xml
index f5f3f0fe52eb..a0c68ad44379 100644
--- a/libs/WindowManager/Shell/res/values/dimen.xml
+++ b/libs/WindowManager/Shell/res/values/dimen.xml
@@ -670,7 +670,4 @@
<dimen name="desktop_windowing_education_promo_height">352dp</dimen>
<!-- The corner radius of the desktop windowing education promo. -->
<dimen name="desktop_windowing_education_promo_corner_radius">28dp</dimen>
-
- <!-- The corner radius of freeform tasks in desktop windowing. -->
- <dimen name="desktop_windowing_freeform_rounded_corner_radius">16dp</dimen>
</resources>
diff --git a/libs/WindowManager/Shell/shared/res/values/dimen.xml b/libs/WindowManager/Shell/shared/res/values/dimen.xml
index 11a6f32d7454..23c9caf2046c 100644
--- a/libs/WindowManager/Shell/shared/res/values/dimen.xml
+++ b/libs/WindowManager/Shell/shared/res/values/dimen.xml
@@ -46,4 +46,7 @@
<dimen name="drop_target_expanded_view_height">578</dimen>
<dimen name="drop_target_expanded_view_padding_bottom">108</dimen>
<dimen name="drop_target_expanded_view_padding_horizontal">24</dimen>
+
+ <!-- The corner radius of freeform tasks in desktop windowing. -->
+ <dimen name="desktop_windowing_freeform_rounded_corner_radius">16dp</dimen>
</resources> \ No newline at end of file
diff --git a/libs/WindowManager/Shell/shared/src/com/android/wm/shell/shared/animation/Interpolators.java b/libs/WindowManager/Shell/shared/src/com/android/wm/shell/shared/animation/Interpolators.java
index e92c1eb81e89..43dd9b73b352 100644
--- a/libs/WindowManager/Shell/shared/src/com/android/wm/shell/shared/animation/Interpolators.java
+++ b/libs/WindowManager/Shell/shared/src/com/android/wm/shell/shared/animation/Interpolators.java
@@ -74,6 +74,12 @@ public class Interpolators {
0.05f, 0.7f, 0.1f, 1f);
/**
+ * The standard accelerating interpolator that should be used on every regular movement of
+ * content that is disappearing e.g. when moving off screen.
+ */
+ public static final Interpolator STANDARD_ACCELERATE = new PathInterpolator(0.3f, 0f, 1f, 1f);
+
+ /**
* The standard decelerating interpolator that should be used on every regular movement of
* content that is appearing e.g. when coming from off screen.
*/
diff --git a/libs/WindowManager/Shell/shared/src/com/android/wm/shell/shared/animation/MinimizeAnimator.kt b/libs/WindowManager/Shell/shared/src/com/android/wm/shell/shared/animation/MinimizeAnimator.kt
index 0586e265eced..4ecace0292cf 100644
--- a/libs/WindowManager/Shell/shared/src/com/android/wm/shell/shared/animation/MinimizeAnimator.kt
+++ b/libs/WindowManager/Shell/shared/src/com/android/wm/shell/shared/animation/MinimizeAnimator.kt
@@ -19,52 +19,76 @@ package com.android.wm.shell.shared.animation
import android.animation.Animator
import android.animation.AnimatorSet
import android.animation.ValueAnimator
-import android.util.DisplayMetrics
+import android.content.Context
+import android.os.Handler
+import android.view.Choreographer
import android.view.SurfaceControl.Transaction
-import android.view.animation.LinearInterpolator
-import android.view.animation.PathInterpolator
import android.window.TransitionInfo.Change
+import com.android.internal.jank.Cuj.CUJ_DESKTOP_MODE_MINIMIZE_WINDOW
+import com.android.internal.jank.InteractionJankMonitor
/** Creates minimization animation */
object MinimizeAnimator {
private const val MINIMIZE_ANIM_ALPHA_DURATION_MS = 100L
- private val STANDARD_ACCELERATE = PathInterpolator(0.3f, 0f, 1f, 1f)
-
private val minimizeBoundsAnimationDef =
WindowAnimator.BoundsAnimationParams(
durationMs = 200,
endOffsetYDp = 12f,
endScale = 0.97f,
- interpolator = STANDARD_ACCELERATE,
+ interpolator = Interpolators.STANDARD_ACCELERATE,
)
+ /**
+ * Creates a minimize animator for given task [Change].
+ *
+ * @param onAnimFinish finish-callback for the animation, note that this is called on the same
+ * thread as the animation itself.
+ * @param animationHandler the Handler that the animation is running on.
+ */
@JvmStatic
fun create(
- displayMetrics: DisplayMetrics,
+ context: Context,
change: Change,
transaction: Transaction,
onAnimFinish: (Animator) -> Unit,
+ interactionJankMonitor: InteractionJankMonitor,
+ animationHandler: Handler,
): Animator {
val boundsAnimator = WindowAnimator.createBoundsAnimator(
- displayMetrics,
+ context.resources.displayMetrics,
minimizeBoundsAnimationDef,
change,
transaction,
)
val alphaAnimator = ValueAnimator.ofFloat(1f, 0f).apply {
duration = MINIMIZE_ANIM_ALPHA_DURATION_MS
- interpolator = LinearInterpolator()
+ interpolator = Interpolators.LINEAR
addUpdateListener { animation ->
- transaction.setAlpha(change.leash, animation.animatedValue as Float).apply()
+ transaction
+ .setAlpha(change.leash, animation.animatedValue as Float)
+ .setFrameTimeline(Choreographer.getInstance().vsyncId)
+ .apply()
}
}
val listener = object : Animator.AnimatorListener {
- override fun onAnimationEnd(animator: Animator) = onAnimFinish(animator)
- override fun onAnimationCancel(animator: Animator) = Unit
+ override fun onAnimationStart(animator: Animator) {
+ interactionJankMonitor.begin(
+ change.leash,
+ context,
+ animationHandler,
+ CUJ_DESKTOP_MODE_MINIMIZE_WINDOW,
+ )
+ }
+ override fun onAnimationCancel(animator: Animator) {
+ interactionJankMonitor.cancel(CUJ_DESKTOP_MODE_MINIMIZE_WINDOW)
+ }
override fun onAnimationRepeat(animator: Animator) = Unit
- override fun onAnimationStart(animator: Animator) = Unit
+ override fun onAnimationEnd(animator: Animator) {
+ interactionJankMonitor.end(CUJ_DESKTOP_MODE_MINIMIZE_WINDOW)
+ onAnimFinish(animator)
+ }
}
return AnimatorSet().apply {
playTogether(boundsAnimator, alphaAnimator)
diff --git a/libs/WindowManager/Shell/shared/src/com/android/wm/shell/shared/desktopmode/DesktopModeCompatPolicy.kt b/libs/WindowManager/Shell/shared/src/com/android/wm/shell/shared/desktopmode/DesktopModeCompatPolicy.kt
index 14338a49ee2f..0e4a6b9fb083 100644
--- a/libs/WindowManager/Shell/shared/src/com/android/wm/shell/shared/desktopmode/DesktopModeCompatPolicy.kt
+++ b/libs/WindowManager/Shell/shared/src/com/android/wm/shell/shared/desktopmode/DesktopModeCompatPolicy.kt
@@ -16,12 +16,14 @@
package com.android.wm.shell.shared.desktopmode
+import android.Manifest.permission.SYSTEM_ALERT_WINDOW
import android.app.TaskInfo
import android.content.Context
import android.content.pm.ActivityInfo
import android.content.pm.ActivityInfo.INSETS_DECOUPLED_CONFIGURATION_ENFORCED
import android.content.pm.ActivityInfo.OVERRIDE_ENABLE_INSETS_DECOUPLED_CONFIGURATION
import android.content.pm.ActivityInfo.OVERRIDE_EXCLUDE_CAPTION_INSETS_FROM_APP_BOUNDS
+import android.content.pm.PackageManager
import android.window.DesktopModeFlags
import com.android.internal.R
@@ -32,8 +34,10 @@ import com.android.internal.R
class DesktopModeCompatPolicy(private val context: Context) {
private val systemUiPackage: String = context.resources.getString(R.string.config_systemUi)
+ private val pkgManager: PackageManager
+ get() = context.getPackageManager()
private val defaultHomePackage: String?
- get() = context.getPackageManager().getHomeActivities(ArrayList())?.packageName
+ get() = pkgManager.getHomeActivities(ArrayList())?.packageName
/**
* If the top activity should be exempt from desktop windowing and forced back to fullscreen.
@@ -47,11 +51,12 @@ class DesktopModeCompatPolicy(private val context: Context) {
fun isTopActivityExemptFromDesktopWindowing(packageName: String?,
numActivities: Int, isTopActivityNoDisplay: Boolean, isActivityStackTransparent: Boolean) =
- DesktopModeFlags.ENABLE_DESKTOP_WINDOWING_MODALS_POLICY.isTrue
- && ((isSystemUiTask(packageName)
- || isPartOfDefaultHomePackageOrNoHomeAvailable(packageName)
- || isTransparentTask(isActivityStackTransparent, numActivities))
- && !isTopActivityNoDisplay)
+ DesktopModeFlags.ENABLE_DESKTOP_WINDOWING_MODALS_POLICY.isTrue &&
+ ((isSystemUiTask(packageName) ||
+ isPartOfDefaultHomePackageOrNoHomeAvailable(packageName) ||
+ (isTransparentTask(isActivityStackTransparent, numActivities) &&
+ hasFullscreenTransparentPermission(packageName))) &&
+ !isTopActivityNoDisplay)
/**
* Whether the caption insets should be excluded from configuration for system to handle.
@@ -83,6 +88,26 @@ class DesktopModeCompatPolicy(private val context: Context) {
private fun isSystemUiTask(packageName: String?) = packageName == systemUiPackage
+ // Checks if the app for the given package has the SYSTEM_ALERT_WINDOW permission.
+ private fun hasFullscreenTransparentPermission(packageName: String?): Boolean {
+ if (DesktopModeFlags.ENABLE_MODALS_FULLSCREEN_WITH_PERMISSIONS.isTrue) {
+ if (packageName == null) {
+ return false
+ }
+ return try {
+ val packageInfo = pkgManager.getPackageInfo(
+ packageName,
+ PackageManager.GET_PERMISSIONS
+ )
+ packageInfo?.requestedPermissions?.contains(SYSTEM_ALERT_WINDOW) == true
+ } catch (e: PackageManager.NameNotFoundException) {
+ false // Package not found
+ }
+ }
+ // If the flag is disabled we make this condition neutral.
+ return true
+ }
+
/**
* Returns true if the tasks base activity is part of the default home package, or there is
* currently no default home package available.
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/activityembedding/ActivityEmbeddingAnimationRunner.java b/libs/WindowManager/Shell/src/com/android/wm/shell/activityembedding/ActivityEmbeddingAnimationRunner.java
index f269b3831aab..78f5154c0ecb 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/activityembedding/ActivityEmbeddingAnimationRunner.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/activityembedding/ActivityEmbeddingAnimationRunner.java
@@ -364,7 +364,7 @@ class ActivityEmbeddingAnimationRunner {
@NonNull SurfaceControl.Transaction finishTransaction,
@NonNull List<ActivityEmbeddingAnimationAdapter> adapters) {
for (ActivityEmbeddingAnimationAdapter adapter : adapters) {
- final int backgroundColor = getTransitionBackgroundColorIfSet(info, adapter.mChange,
+ final int backgroundColor = getTransitionBackgroundColorIfSet(adapter.mChange,
adapter.mAnimation, 0 /* defaultColor */);
if (backgroundColor != 0) {
// We only need to show one color.
@@ -436,8 +436,8 @@ class ActivityEmbeddingAnimationRunner {
final TransitionInfo.AnimationOptions options = boundsAnimationChange
.getAnimationOptions();
if (options != null) {
- final Animation overrideAnimation = mAnimationSpec.loadCustomAnimationFromOptions(
- options, TRANSIT_CHANGE);
+ final Animation overrideAnimation =
+ mAnimationSpec.loadCustomAnimation(options, TRANSIT_CHANGE);
if (overrideAnimation != null) {
overrideShowBackdrop = overrideAnimation.getShowBackdrop();
}
@@ -447,7 +447,7 @@ class ActivityEmbeddingAnimationRunner {
// There are two animations in the array. The first one is for the start leash
// (snapshot), and the second one is for the end leash (TaskFragment).
final Animation[] animations =
- mAnimationSpec.createChangeBoundsChangeAnimations(info, change, parentBounds);
+ mAnimationSpec.createChangeBoundsChangeAnimations(change, parentBounds);
// Jump cut if either animation has zero for duration.
for (Animation animation : animations) {
if (shouldUseJumpCutForAnimation(animation)) {
@@ -500,12 +500,10 @@ class ActivityEmbeddingAnimationRunner {
// window without bounds change.
animation = ActivityEmbeddingAnimationSpec.createNoopAnimation(change);
} else if (TransitionUtil.isClosingType(change.getMode())) {
- animation =
- mAnimationSpec.createChangeBoundsCloseAnimation(info, change, parentBounds);
+ animation = mAnimationSpec.createChangeBoundsCloseAnimation(change, parentBounds);
shouldShowBackgroundColor = false;
} else {
- animation =
- mAnimationSpec.createChangeBoundsOpenAnimation(info, change, parentBounds);
+ animation = mAnimationSpec.createChangeBoundsOpenAnimation(change, parentBounds);
shouldShowBackgroundColor = false;
}
if (shouldUseJumpCutForAnimation(animation)) {
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/activityembedding/ActivityEmbeddingAnimationSpec.java b/libs/WindowManager/Shell/src/com/android/wm/shell/activityembedding/ActivityEmbeddingAnimationSpec.java
index 77799e99607b..2b9eda40cdbb 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/activityembedding/ActivityEmbeddingAnimationSpec.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/activityembedding/ActivityEmbeddingAnimationSpec.java
@@ -42,7 +42,6 @@ import android.view.animation.TranslateAnimation;
import android.window.TransitionInfo;
import com.android.internal.policy.TransitionAnimation;
-import com.android.window.flags.Flags;
import com.android.wm.shell.shared.TransitionUtil;
/** Animation spec for ActivityEmbedding transition. */
@@ -94,9 +93,10 @@ class ActivityEmbeddingAnimationSpec {
/** Animation for window that is opening in a change transition. */
@NonNull
- Animation createChangeBoundsOpenAnimation(@NonNull TransitionInfo info,
- @NonNull TransitionInfo.Change change, @NonNull Rect parentBounds) {
- final Animation customAnimation = loadCustomAnimation(info, change, TRANSIT_CHANGE);
+ Animation createChangeBoundsOpenAnimation(@NonNull TransitionInfo.Change change,
+ @NonNull Rect parentBounds) {
+ final Animation customAnimation =
+ loadCustomAnimation(change.getAnimationOptions(), TRANSIT_CHANGE);
if (customAnimation != null) {
return customAnimation;
}
@@ -126,9 +126,10 @@ class ActivityEmbeddingAnimationSpec {
/** Animation for window that is closing in a change transition. */
@NonNull
- Animation createChangeBoundsCloseAnimation(@NonNull TransitionInfo info,
- @NonNull TransitionInfo.Change change, @NonNull Rect parentBounds) {
- final Animation customAnimation = loadCustomAnimation(info, change, TRANSIT_CHANGE);
+ Animation createChangeBoundsCloseAnimation(@NonNull TransitionInfo.Change change,
+ @NonNull Rect parentBounds) {
+ final Animation customAnimation =
+ loadCustomAnimation(change.getAnimationOptions(), TRANSIT_CHANGE);
if (customAnimation != null) {
return customAnimation;
}
@@ -162,12 +163,13 @@ class ActivityEmbeddingAnimationSpec {
* the second one is for the end leash.
*/
@NonNull
- Animation[] createChangeBoundsChangeAnimations(@NonNull TransitionInfo info,
- @NonNull TransitionInfo.Change change, @NonNull Rect parentBounds) {
+ Animation[] createChangeBoundsChangeAnimations(@NonNull TransitionInfo.Change change,
+ @NonNull Rect parentBounds) {
// TODO(b/293658614): Support more complicated animations that may need more than a noop
// animation as the start leash.
final Animation noopAnimation = createNoopAnimation(change);
- final Animation customAnimation = loadCustomAnimation(info, change, TRANSIT_CHANGE);
+ final Animation customAnimation =
+ loadCustomAnimation(change.getAnimationOptions(), TRANSIT_CHANGE);
if (customAnimation != null) {
return new Animation[]{noopAnimation, customAnimation};
}
@@ -221,7 +223,8 @@ class ActivityEmbeddingAnimationSpec {
Animation loadOpenAnimation(@NonNull TransitionInfo info,
@NonNull TransitionInfo.Change change, @NonNull Rect wholeAnimationBounds) {
final boolean isEnter = TransitionUtil.isOpeningType(change.getMode());
- final Animation customAnimation = loadCustomAnimation(info, change, change.getMode());
+ final Animation customAnimation =
+ loadCustomAnimation(change.getAnimationOptions(), change.getMode());
final Animation animation;
if (customAnimation != null) {
animation = customAnimation;
@@ -248,7 +251,8 @@ class ActivityEmbeddingAnimationSpec {
Animation loadCloseAnimation(@NonNull TransitionInfo info,
@NonNull TransitionInfo.Change change, @NonNull Rect wholeAnimationBounds) {
final boolean isEnter = TransitionUtil.isOpeningType(change.getMode());
- final Animation customAnimation = loadCustomAnimation(info, change, change.getMode());
+ final Animation customAnimation =
+ loadCustomAnimation(change.getAnimationOptions(), change.getMode());
final Animation animation;
if (customAnimation != null) {
animation = customAnimation;
@@ -280,20 +284,8 @@ class ActivityEmbeddingAnimationSpec {
}
@Nullable
- private Animation loadCustomAnimation(@NonNull TransitionInfo info,
- @NonNull TransitionInfo.Change change, @WindowManager.TransitionType int mode) {
- final TransitionInfo.AnimationOptions options;
- if (Flags.moveAnimationOptionsToChange()) {
- options = change.getAnimationOptions();
- } else {
- options = info.getAnimationOptions();
- }
- return loadCustomAnimationFromOptions(options, mode);
- }
-
- @Nullable
- Animation loadCustomAnimationFromOptions(@Nullable TransitionInfo.AnimationOptions options,
- @WindowManager.TransitionType int mode) {
+ Animation loadCustomAnimation(@Nullable TransitionInfo.AnimationOptions options,
+ @WindowManager.TransitionType int mode) {
if (options == null || options.getType() != ANIM_CUSTOM) {
return null;
}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/activityembedding/ActivityEmbeddingController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/activityembedding/ActivityEmbeddingController.java
index 55ed5fa4b56f..3a95333309ac 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/activityembedding/ActivityEmbeddingController.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/activityembedding/ActivityEmbeddingController.java
@@ -40,7 +40,6 @@ import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import com.android.internal.annotations.VisibleForTesting;
-import com.android.window.flags.Flags;
import com.android.wm.shell.shared.TransitionUtil;
import com.android.wm.shell.sysui.ShellInit;
import com.android.wm.shell.transition.Transitions;
@@ -123,9 +122,6 @@ public class ActivityEmbeddingController implements Transitions.TransitionHandle
}
private boolean shouldAnimateAnimationOptions(@NonNull TransitionInfo info) {
- if (!Flags.moveAnimationOptionsToChange()) {
- return shouldAnimateAnimationOptions(info.getAnimationOptions());
- }
for (TransitionInfo.Change change : info.getChanges()) {
if (!shouldAnimateAnimationOptions(change.getAnimationOptions())) {
// If any of override animation is not supported, don't animate the transition.
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/Bubble.java b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/Bubble.java
index d9489287ff42..313d151aeab7 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/Bubble.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/Bubble.java
@@ -364,7 +364,7 @@ public class Bubble implements BubbleViewProvider {
@ShellMainThread Executor mainExecutor, @ShellBackgroundThread Executor bgExecutor) {
return new Bubble(intent,
user,
- /* key= */ getAppBubbleKeyForApp(ComponentUtils.getPackageName(intent), user),
+ /* key= */ getAppBubbleKeyForApp(intent.getIntent().getPackage(), user),
mainExecutor, bgExecutor);
}
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 3f607a9c52ef..2c2451cab999 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
@@ -197,6 +197,8 @@ public class BubbleExpandedView extends LinearLayout {
*/
private final FrameLayout mExpandedViewContainer = new FrameLayout(getContext());
+ private TaskView.Listener mCurrentTaskViewListener;
+
private final TaskView.Listener mTaskViewListener = new TaskView.Listener() {
private boolean mInitialized = false;
private boolean mDestroyed = false;
@@ -235,18 +237,24 @@ public class BubbleExpandedView extends LinearLayout {
Context context =
mContext.createContextAsUser(
mBubble.getUser(), Context.CONTEXT_RESTRICTED);
+ Intent fillInIntent = new Intent();
+ fillInIntent.addFlags(FLAG_ACTIVITY_MULTIPLE_TASK);
PendingIntent pi = PendingIntent.getActivity(
context,
/* requestCode= */ 0,
- mBubble.getIntent().addFlags(FLAG_ACTIVITY_MULTIPLE_TASK),
- PendingIntent.FLAG_IMMUTABLE | PendingIntent.FLAG_UPDATE_CURRENT,
+ mBubble.getIntent(),
+ // Needs to be mutable for the fillInIntent
+ PendingIntent.FLAG_MUTABLE | PendingIntent.FLAG_UPDATE_CURRENT,
/* options= */ null);
- mTaskView.startActivity(pi, /* fillInIntent= */ null, options,
- launchBounds);
+ mTaskView.startActivity(pi, fillInIntent, options, launchBounds);
} else if (!mIsOverflow && isShortcutBubble) {
ProtoLog.v(WM_SHELL_BUBBLES, "startingShortcutBubble=%s", getBubbleKey());
- options.setLaunchedFromBubble(true);
- options.setApplyActivityFlagsForBubbles(true);
+ if (mBubble.isChat()) {
+ options.setLaunchedFromBubble(true);
+ options.setApplyActivityFlagsForBubbles(true);
+ } else {
+ options.setApplyMultipleTaskFlagForShortcut(true);
+ }
mTaskView.startShortcutActivity(mBubble.getShortcutInfo(),
options, launchBounds);
} else {
@@ -453,7 +461,34 @@ public class BubbleExpandedView extends LinearLayout {
mTaskView = bubbleTaskView.getTaskView();
// reset the insets that might left after TaskView is shown in BubbleBarExpandedView
mTaskView.setCaptionInsets(null);
- bubbleTaskView.setDelegateListener(mTaskViewListener);
+ if (Flags.enableBubbleTaskViewListener()) {
+ mCurrentTaskViewListener = new BubbleTaskViewListener(mContext, bubbleTaskView,
+ /* viewParent= */ this, expandedViewManager,
+ new BubbleTaskViewListener.Callback() {
+ @Override
+ public void onTaskCreated() {
+ setContentVisibility(true);
+ }
+
+ @Override
+ public void onContentVisibilityChanged(boolean visible) {
+ setContentVisibility(visible);
+ }
+
+ @Override
+ public void onBackPressed() {
+ mStackView.onBackPressed();
+ }
+
+ @Override
+ public void onTaskRemovalStarted() {
+ // nothing to do / handled in listener.
+ }
+ });
+ } else {
+ mCurrentTaskViewListener = mTaskViewListener;
+ bubbleTaskView.setDelegateListener(mCurrentTaskViewListener);
+ }
// set a fixed width so it is not recalculated as part of a rotation. the width will be
// updated manually after the rotation.
@@ -464,9 +499,12 @@ public class BubbleExpandedView extends LinearLayout {
}
mExpandedViewContainer.addView(mTaskView, lp);
bringChildToFront(mTaskView);
- if (bubbleTaskView.isCreated()) {
- mTaskViewListener.onTaskCreated(
- bubbleTaskView.getTaskId(), bubbleTaskView.getComponentName());
+
+ if (!Flags.enableBubbleTaskViewListener()) {
+ if (bubbleTaskView.isCreated()) {
+ mCurrentTaskViewListener.onTaskCreated(
+ bubbleTaskView.getTaskId(), bubbleTaskView.getComponentName());
+ }
}
}
}
@@ -897,7 +935,12 @@ public class BubbleExpandedView extends LinearLayout {
Log.w(TAG, "Stack is null for bubble: " + bubble);
return;
}
- boolean isNew = mBubble == null || didBackingContentChange(bubble);
+ boolean isNew;
+ if (mCurrentTaskViewListener instanceof BubbleTaskViewListener) {
+ isNew = ((BubbleTaskViewListener) mCurrentTaskViewListener).setBubble(bubble);
+ } else {
+ isNew = mBubble == null || didBackingContentChange(bubble);
+ }
boolean isUpdate = bubble != null && mBubble != null
&& bubble.getKey().equals(mBubble.getKey());
ProtoLog.d(WM_SHELL_BUBBLES, "BubbleExpandedView - update bubble=%s; isNew=%b; isUpdate=%b",
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleTaskViewListener.java b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleTaskViewListener.java
index a38debb702dc..63d713495177 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleTaskViewListener.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleTaskViewListener.java
@@ -129,27 +129,28 @@ public class BubbleTaskViewListener implements TaskView.Listener {
Context context =
mContext.createContextAsUser(
mBubble.getUser(), Context.CONTEXT_RESTRICTED);
- Intent fillInIntent = null;
+ Intent fillInIntent = new Intent();
+ fillInIntent.addFlags(FLAG_ACTIVITY_MULTIPLE_TASK);
// First try get pending intent from the bubble
PendingIntent pi = mBubble.getPendingIntent();
if (pi == null) {
- // If null - create new one
+ // If null - create new one based on the bubble intent
pi = PendingIntent.getActivity(
context,
/* requestCode= */ 0,
- mBubble.getIntent()
- .addFlags(FLAG_ACTIVITY_MULTIPLE_TASK),
- PendingIntent.FLAG_IMMUTABLE
- | PendingIntent.FLAG_UPDATE_CURRENT,
+ mBubble.getIntent(),
+ // Needs to be mutable for the fillInIntent
+ PendingIntent.FLAG_MUTABLE | PendingIntent.FLAG_UPDATE_CURRENT,
/* options= */ null);
- } else {
- fillInIntent = new Intent(pi.getIntent());
- fillInIntent.addFlags(FLAG_ACTIVITY_MULTIPLE_TASK);
}
mTaskView.startActivity(pi, fillInIntent, options, launchBounds);
} else if (isShortcutBubble) {
- options.setLaunchedFromBubble(true);
- options.setApplyActivityFlagsForBubbles(true);
+ if (mBubble.isChat()) {
+ options.setLaunchedFromBubble(true);
+ options.setApplyActivityFlagsForBubbles(true);
+ } else {
+ options.setApplyMultipleTaskFlagForShortcut(true);
+ }
mTaskView.startShortcutActivity(mBubble.getShortcutInfo(),
options, launchBounds);
} else {
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleTransitions.java b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleTransitions.java
index a676f41baafe..338ffe76e6ea 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleTransitions.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleTransitions.java
@@ -21,6 +21,7 @@ import static android.app.WindowConfiguration.WINDOWING_MODE_MULTI_WINDOW;
import static android.app.WindowConfiguration.WINDOWING_MODE_UNDEFINED;
import static android.view.View.INVISIBLE;
import static android.view.WindowManager.TRANSIT_CHANGE;
+import static android.view.WindowManager.TRANSIT_TO_FRONT;
import static com.android.wm.shell.transition.Transitions.TRANSIT_CONVERT_TO_BUBBLE;
@@ -325,7 +326,7 @@ public class BubbleTransitions {
for (int i = 0; i < info.getChanges().size(); ++i) {
final TransitionInfo.Change chg = info.getChanges().get(i);
if (chg.getTaskInfo() == null) continue;
- if (chg.getMode() != TRANSIT_CHANGE) continue;
+ if (chg.getMode() != TRANSIT_CHANGE && chg.getMode() != TRANSIT_TO_FRONT) continue;
if (!mTaskInfo.token.equals(chg.getTaskInfo().token)) continue;
mStartBounds.set(chg.getStartAbsBounds());
// Converting a task into taskview, so treat as "new"
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/common/DisplayImeController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/common/DisplayImeController.java
index 8377a35a9e7d..df82091ef002 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/common/DisplayImeController.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/common/DisplayImeController.java
@@ -227,7 +227,13 @@ public class DisplayImeController implements DisplayController.OnDisplaysChanged
/** Hides the IME for Bubbles when the device is locked. */
public void hideImeForBubblesWhenLocked(int displayId) {
PerDisplay pd = mImePerDisplay.get(displayId);
- pd.setImeInputTargetRequestedVisibility(false, pd.getImeSourceControl().getImeStatsToken());
+ InsetsSourceControl imeSourceControl = pd.getImeSourceControl();
+ if (imeSourceControl != null) {
+ ImeTracker.Token imeStatsToken = imeSourceControl.getImeStatsToken();
+ if (imeStatsToken != null) {
+ pd.setImeInputTargetRequestedVisibility(false, imeStatsToken);
+ }
+ }
}
/** An implementation of {@link IDisplayWindowInsetsController} for a given display id. */
@@ -324,8 +330,10 @@ public class DisplayImeController implements DisplayController.OnDisplaysChanged
}
applyVisibilityToLeash(imeSourceControl);
}
- if (!mImeShowing) {
- removeImeSurface(mDisplayId);
+ if (!android.view.inputmethod.Flags.refactorInsetsController()) {
+ if (!mImeShowing) {
+ removeImeSurface(mDisplayId);
+ }
}
}
} else {
@@ -663,7 +671,9 @@ public class DisplayImeController implements DisplayController.OnDisplaysChanged
ImeTracker.forLogging().onProgress(mStatsToken,
ImeTracker.PHASE_WM_ANIMATION_RUNNING);
t.hide(animatingLeash);
- removeImeSurface(mDisplayId);
+ if (!android.view.inputmethod.Flags.refactorInsetsController()) {
+ removeImeSurface(mDisplayId);
+ }
if (android.view.inputmethod.Flags.refactorInsetsController()) {
setVisibleDirectly(false /* visible */, statsToken);
}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/common/MultiDisplayDragMoveIndicatorController.kt b/libs/WindowManager/Shell/src/com/android/wm/shell/common/MultiDisplayDragMoveIndicatorController.kt
new file mode 100644
index 000000000000..7a5bc1383ccf
--- /dev/null
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/common/MultiDisplayDragMoveIndicatorController.kt
@@ -0,0 +1,124 @@
+/*
+ * Copyright (C) 2025 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.common
+
+import android.app.ActivityManager.RunningTaskInfo
+import android.graphics.RectF
+import android.view.SurfaceControl
+import com.android.wm.shell.RootTaskDisplayAreaOrganizer
+import com.android.wm.shell.shared.annotations.ShellDesktopThread
+
+/**
+ * Controller to manage the indicators that show users the current position of the dragged window on
+ * the new display when performing drag move across displays.
+ */
+class MultiDisplayDragMoveIndicatorController(
+ private val displayController: DisplayController,
+ private val rootTaskDisplayAreaOrganizer: RootTaskDisplayAreaOrganizer,
+ private val indicatorSurfaceFactory: MultiDisplayDragMoveIndicatorSurface.Factory,
+ @ShellDesktopThread private val desktopExecutor: ShellExecutor,
+) {
+ @ShellDesktopThread
+ private val dragIndicators =
+ mutableMapOf<Int, MutableMap<Int, MultiDisplayDragMoveIndicatorSurface>>()
+
+ /**
+ * Called during drag move, which started at [startDisplayId]. Updates the position and
+ * visibility of the drag move indicators for the [taskInfo] based on [boundsDp] on the
+ * destination displays ([displayIds]) as the dragged window moves. [transactionSupplier]
+ * provides a [SurfaceControl.Transaction] for applying changes to the indicator surfaces.
+ *
+ * It is executed on the [desktopExecutor] to prevent blocking the main thread and avoid jank,
+ * as creating and manipulating surfaces can be expensive.
+ */
+ fun onDragMove(
+ boundsDp: RectF,
+ startDisplayId: Int,
+ taskInfo: RunningTaskInfo,
+ displayIds: Set<Int>,
+ transactionSupplier: () -> SurfaceControl.Transaction,
+ ) {
+ desktopExecutor.execute {
+ for (displayId in displayIds) {
+ if (displayId == startDisplayId) {
+ // No need to render indicators on the original display where the drag started.
+ continue
+ }
+ val displayLayout = displayController.getDisplayLayout(displayId) ?: continue
+ val shouldBeVisible =
+ RectF.intersects(RectF(boundsDp), displayLayout.globalBoundsDp())
+ if (
+ dragIndicators[taskInfo.taskId]?.containsKey(displayId) != true &&
+ !shouldBeVisible
+ ) {
+ // Skip this display if:
+ // - It doesn't have an existing indicator that needs to be updated, AND
+ // - The latest dragged window bounds don't intersect with this display.
+ continue
+ }
+
+ val boundsPx =
+ MultiDisplayDragMoveBoundsCalculator.convertGlobalDpToLocalPxForRect(
+ boundsDp,
+ displayLayout,
+ )
+
+ // Get or create the inner map for the current task.
+ val dragIndicatorsForTask =
+ dragIndicators.getOrPut(taskInfo.taskId) { mutableMapOf() }
+ dragIndicatorsForTask[displayId]?.also { existingIndicator ->
+ val transaction = transactionSupplier()
+ existingIndicator.relayout(boundsPx, transaction, shouldBeVisible)
+ transaction.apply()
+ } ?: run {
+ val newIndicator =
+ indicatorSurfaceFactory.create(
+ taskInfo,
+ displayController.getDisplay(displayId),
+ )
+ newIndicator.show(
+ transactionSupplier(),
+ taskInfo,
+ rootTaskDisplayAreaOrganizer,
+ displayId,
+ boundsPx,
+ )
+ dragIndicatorsForTask[displayId] = newIndicator
+ }
+ }
+ }
+ }
+
+ /**
+ * Called when the drag ends. Disposes of the drag move indicator surfaces associated with the
+ * given [taskId]. [transactionSupplier] provides a [SurfaceControl.Transaction] for applying
+ * changes to the indicator surfaces.
+ *
+ * It is executed on the [desktopExecutor] to ensure that any pending `onDragMove` operations
+ * have completed before disposing of the surfaces.
+ */
+ fun onDragEnd(taskId: Int, transactionSupplier: () -> SurfaceControl.Transaction) {
+ desktopExecutor.execute {
+ dragIndicators.remove(taskId)?.values?.takeIf { it.isNotEmpty() }?.let { indicators ->
+ val transaction = transactionSupplier()
+ indicators.forEach { indicator ->
+ indicator.disposeSurface(transaction)
+ }
+ transaction.apply()
+ }
+ }
+ }
+}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/common/MultiDisplayDragMoveIndicatorSurface.kt b/libs/WindowManager/Shell/src/com/android/wm/shell/common/MultiDisplayDragMoveIndicatorSurface.kt
new file mode 100644
index 000000000000..d05d3b0903d7
--- /dev/null
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/common/MultiDisplayDragMoveIndicatorSurface.kt
@@ -0,0 +1,151 @@
+/*
+ * Copyright (C) 2025 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.common
+
+import android.app.ActivityManager.RunningTaskInfo
+import android.content.Context
+import android.graphics.Color
+import android.graphics.Rect
+import android.os.Trace
+import android.view.Display
+import android.view.SurfaceControl
+import androidx.compose.material3.dynamicDarkColorScheme
+import androidx.compose.material3.dynamicLightColorScheme
+import androidx.compose.ui.graphics.toArgb
+import com.android.wm.shell.RootTaskDisplayAreaOrganizer
+import com.android.wm.shell.windowdecor.common.DecorThemeUtil
+import com.android.wm.shell.windowdecor.common.Theme
+
+/**
+ * Represents the indicator surface that visualizes the current position of a dragged window during
+ * a multi-display drag operation.
+ *
+ * This class manages the creation, display, and manipulation of the [SurfaceControl]s that act as a
+ * visual indicator, providing feedback to the user about the dragged window's location.
+ */
+class MultiDisplayDragMoveIndicatorSurface(
+ context: Context,
+ taskInfo: RunningTaskInfo,
+ display: Display,
+ surfaceControlBuilderFactory: Factory.SurfaceControlBuilderFactory,
+) {
+ private var isVisible = false
+
+ // A container surface to host the veil background
+ private var veilSurface: SurfaceControl? = null
+
+ private val decorThemeUtil = DecorThemeUtil(context)
+ private val lightColors = dynamicLightColorScheme(context)
+ private val darkColors = dynamicDarkColorScheme(context)
+
+ init {
+ Trace.beginSection("DragIndicatorSurface#init")
+
+ val displayId = display.displayId
+ veilSurface =
+ surfaceControlBuilderFactory
+ .create("Drag indicator veil of Task=${taskInfo.taskId} Display=$displayId")
+ .setColorLayer()
+ .setCallsite("DragIndicatorSurface#init")
+ .setHidden(true)
+ .build()
+
+ // TODO: b/383069173 - Add icon for the surface.
+
+ Trace.endSection()
+ }
+
+ /**
+ * Disposes the indicator surface using the provided [transaction].
+ */
+ fun disposeSurface(transaction: SurfaceControl.Transaction) {
+ veilSurface?.let { veil -> transaction.remove(veil) }
+ veilSurface = null
+ }
+
+ /**
+ * Shows the indicator surface at [bounds] on the specified display ([displayId]),
+ * visualizing the drag of the [taskInfo]. The indicator surface is shown using [transaction],
+ * and the [rootTaskDisplayAreaOrganizer] is used to reparent the surfaces.
+ */
+ fun show(
+ transaction: SurfaceControl.Transaction,
+ taskInfo: RunningTaskInfo,
+ rootTaskDisplayAreaOrganizer: RootTaskDisplayAreaOrganizer,
+ displayId: Int,
+ bounds: Rect,
+ ) {
+ val backgroundColor =
+ when (decorThemeUtil.getAppTheme(taskInfo)) {
+ Theme.LIGHT -> lightColors.surfaceContainer
+ Theme.DARK -> darkColors.surfaceContainer
+ }
+ val veil = veilSurface ?: return
+ isVisible = true
+
+ rootTaskDisplayAreaOrganizer.reparentToDisplayArea(displayId, veil, transaction)
+ relayout(bounds, transaction, shouldBeVisible = true)
+ transaction.show(veil).setColor(veil, Color.valueOf(backgroundColor.toArgb()).components)
+ transaction.apply()
+ }
+
+ /**
+ * Repositions and resizes the indicator surface based on [bounds] using [transaction]. The
+ * [shouldBeVisible] flag indicates whether the indicator is within the display after relayout.
+ */
+ fun relayout(bounds: Rect, transaction: SurfaceControl.Transaction, shouldBeVisible: Boolean) {
+ if (!isVisible && !shouldBeVisible) {
+ // No need to relayout if the surface is already invisible and should not be visible.
+ return
+ }
+ isVisible = shouldBeVisible
+ val veil = veilSurface ?: return
+ transaction.setCrop(veil, bounds)
+ }
+
+ /**
+ * Factory for creating [MultiDisplayDragMoveIndicatorSurface] instances with the [context].
+ */
+ class Factory(private val context: Context) {
+ private val surfaceControlBuilderFactory: SurfaceControlBuilderFactory =
+ object : SurfaceControlBuilderFactory {}
+
+ /**
+ * Creates a new [MultiDisplayDragMoveIndicatorSurface] instance to visualize the drag
+ * operation of the [taskInfo] on the given [display].
+ */
+ fun create(
+ taskInfo: RunningTaskInfo,
+ display: Display,
+ ) = MultiDisplayDragMoveIndicatorSurface(
+ context,
+ taskInfo,
+ display,
+ surfaceControlBuilderFactory,
+ )
+
+ /**
+ * Interface for creating [SurfaceControl.Builder] instances.
+ *
+ * This provides an abstraction over [SurfaceControl.Builder] creation for testing purposes.
+ */
+ interface SurfaceControlBuilderFactory {
+ fun create(name: String): SurfaceControl.Builder {
+ return SurfaceControl.Builder().setName(name)
+ }
+ }
+ }
+}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/common/pip/PipBoundsAlgorithm.java b/libs/WindowManager/Shell/src/com/android/wm/shell/common/pip/PipBoundsAlgorithm.java
index 8e026f04ac31..04e8d8dee520 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/common/pip/PipBoundsAlgorithm.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/common/pip/PipBoundsAlgorithm.java
@@ -24,6 +24,7 @@ import android.content.pm.ActivityInfo;
import android.content.res.Resources;
import android.graphics.Rect;
import android.util.DisplayMetrics;
+import android.util.Rational;
import android.util.Size;
import android.view.Gravity;
@@ -41,9 +42,6 @@ public class PipBoundsAlgorithm {
private static final String TAG = PipBoundsAlgorithm.class.getSimpleName();
private static final float INVALID_SNAP_FRACTION = -1f;
- // The same value (with the same name) is used in Launcher.
- private static final float PIP_ASPECT_RATIO_MISMATCH_THRESHOLD = 0.01f;
-
@NonNull private final PipBoundsState mPipBoundsState;
@NonNull protected final PipDisplayLayoutState mPipDisplayLayoutState;
@NonNull protected final SizeSpecSource mSizeSpecSource;
@@ -223,9 +221,8 @@ public class PipBoundsAlgorithm {
+ " than destination(%s)", sourceRectHint, destinationBounds);
return false;
}
- final float reportedRatio = destinationBounds.width() / (float) destinationBounds.height();
- final float inferredRatio = sourceRectHint.width() / (float) sourceRectHint.height();
- if (Math.abs(reportedRatio - inferredRatio) > PIP_ASPECT_RATIO_MISMATCH_THRESHOLD) {
+ if (!PictureInPictureParams.isSameAspectRatio(sourceRectHint,
+ new Rational(destinationBounds.width(), destinationBounds.height()))) {
ProtoLog.d(ShellProtoLogGroup.WM_SHELL_PICTURE_IN_PICTURE,
"isSourceRectHintValidForEnterPip=false, hint(%s) does not match"
+ " destination(%s) aspect ratio", sourceRectHint, destinationBounds);
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/WMShellConcurrencyModule.java b/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/WMShellConcurrencyModule.java
index ee3e39e71558..e9dc6132f5f4 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/WMShellConcurrencyModule.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/WMShellConcurrencyModule.java
@@ -162,22 +162,31 @@ public abstract class WMShellConcurrencyModule {
}
}
- /**
- * Provide a Shell animation-thread Executor.
- */
+ /** Provide a Shell animation-thread Handler. */
@WMSingleton
@Provides
@ShellAnimationThread
- public static ShellExecutor provideShellAnimationExecutor() {
- HandlerThread shellAnimationThread = new HandlerThread("wmshell.anim",
- THREAD_PRIORITY_DISPLAY);
- shellAnimationThread.start();
+ public static Handler provideShellAnimationHandler() {
+ HandlerThread animThread = new HandlerThread("wmshell.anim", THREAD_PRIORITY_DISPLAY);
+ animThread.start();
if (Build.IS_DEBUGGABLE) {
- shellAnimationThread.getLooper().setTraceTag(Trace.TRACE_TAG_WINDOW_MANAGER);
- shellAnimationThread.getLooper().setSlowLogThresholdMs(MSGQ_SLOW_DISPATCH_THRESHOLD_MS,
+ animThread.getLooper().setTraceTag(Trace.TRACE_TAG_WINDOW_MANAGER);
+ animThread.getLooper().setSlowLogThresholdMs(MSGQ_SLOW_DISPATCH_THRESHOLD_MS,
MSGQ_SLOW_DELIVERY_THRESHOLD_MS);
}
- return new HandlerExecutor(Handler.createAsync(shellAnimationThread.getLooper()));
+ return Handler.createAsync(animThread.getLooper());
+ }
+
+ /**
+ * Provide a Shell animation-thread Executor.
+ */
+ @WMSingleton
+ @Provides
+ @ShellAnimationThread
+ public static ShellExecutor provideShellAnimationExecutor(
+ @ShellAnimationThread Handler animHandler
+ ) {
+ return new HandlerExecutor(animHandler);
}
/**
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/WMShellModule.java b/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/WMShellModule.java
index 2fd8c27d5970..5d5e4d3ec758 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/WMShellModule.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/WMShellModule.java
@@ -68,6 +68,8 @@ import com.android.wm.shell.common.DisplayInsetsController;
import com.android.wm.shell.common.DisplayLayout;
import com.android.wm.shell.common.FloatingContentCoordinator;
import com.android.wm.shell.common.LaunchAdjacentController;
+import com.android.wm.shell.common.MultiDisplayDragMoveIndicatorController;
+import com.android.wm.shell.common.MultiDisplayDragMoveIndicatorSurface;
import com.android.wm.shell.common.MultiInstanceHelper;
import com.android.wm.shell.common.ShellExecutor;
import com.android.wm.shell.common.SyncTransactionQueue;
@@ -82,6 +84,7 @@ import com.android.wm.shell.desktopmode.CloseDesktopTaskTransitionHandler;
import com.android.wm.shell.desktopmode.DefaultDragToDesktopTransitionHandler;
import com.android.wm.shell.desktopmode.DesktopActivityOrientationChangeHandler;
import com.android.wm.shell.desktopmode.DesktopDisplayEventHandler;
+import com.android.wm.shell.desktopmode.DesktopDisplayModeController;
import com.android.wm.shell.desktopmode.DesktopImmersiveController;
import com.android.wm.shell.desktopmode.DesktopMinimizationTransitionHandler;
import com.android.wm.shell.desktopmode.DesktopMixedTransitionHandler;
@@ -428,9 +431,10 @@ public abstract class WMShellModule {
Transitions transitions,
DisplayController displayController,
@ShellMainThread ShellExecutor mainExecutor,
- @ShellAnimationThread ShellExecutor animExecutor) {
+ @ShellAnimationThread ShellExecutor animExecutor,
+ @ShellAnimationThread Handler animHandler) {
return new FreeformTaskTransitionHandler(
- transitions, displayController, mainExecutor, animExecutor);
+ transitions, displayController, mainExecutor, animExecutor, animHandler);
}
@WMSingleton
@@ -989,7 +993,8 @@ public abstract class WMShellModule {
WindowDecorTaskResourceLoader taskResourceLoader,
RecentsTransitionHandler recentsTransitionHandler,
DesktopModeCompatPolicy desktopModeCompatPolicy,
- DesktopTilingDecorViewModel desktopTilingDecorViewModel
+ DesktopTilingDecorViewModel desktopTilingDecorViewModel,
+ MultiDisplayDragMoveIndicatorController multiDisplayDragMoveIndicatorController
) {
if (!DesktopModeStatus.canEnterDesktopModeOrShowAppHandle(context)) {
return Optional.empty();
@@ -1006,7 +1011,30 @@ public abstract class WMShellModule {
windowDecorCaptionHandleRepository, activityOrientationChangeHandler,
focusTransitionObserver, desktopModeEventLogger, desktopModeUiEventLogger,
taskResourceLoader, recentsTransitionHandler, desktopModeCompatPolicy,
- desktopTilingDecorViewModel));
+ desktopTilingDecorViewModel,
+ multiDisplayDragMoveIndicatorController));
+ }
+
+ @WMSingleton
+ @Provides
+ static MultiDisplayDragMoveIndicatorController
+ providesMultiDisplayDragMoveIndicatorController(
+ DisplayController displayController,
+ RootTaskDisplayAreaOrganizer rootTaskDisplayAreaOrganizer,
+ MultiDisplayDragMoveIndicatorSurface.Factory
+ multiDisplayDragMoveIndicatorSurfaceFactory,
+ @ShellDesktopThread ShellExecutor desktopExecutor
+ ) {
+ return new MultiDisplayDragMoveIndicatorController(
+ displayController, rootTaskDisplayAreaOrganizer,
+ multiDisplayDragMoveIndicatorSurfaceFactory, desktopExecutor);
+ }
+
+ @WMSingleton
+ @Provides
+ static MultiDisplayDragMoveIndicatorSurface.Factory
+ providesMultiDisplayDragMoveIndicatorSurfaceFactory(Context context) {
+ return new MultiDisplayDragMoveIndicatorSurface.Factory(context);
}
@WMSingleton
@@ -1074,8 +1102,9 @@ public abstract class WMShellModule {
Context context,
@ShellMainThread ShellExecutor mainExecutor,
@ShellAnimationThread ShellExecutor animExecutor,
- @ShellMainThread Handler handler) {
- return new CloseDesktopTaskTransitionHandler(context, mainExecutor, animExecutor, handler);
+ @ShellAnimationThread Handler animHandler) {
+ return new CloseDesktopTaskTransitionHandler(context, mainExecutor, animExecutor,
+ animHandler);
}
@WMSingleton
@@ -1083,9 +1112,10 @@ public abstract class WMShellModule {
static DesktopMinimizationTransitionHandler provideDesktopMinimizationTransitionHandler(
@ShellMainThread ShellExecutor mainExecutor,
@ShellAnimationThread ShellExecutor animExecutor,
- DisplayController displayController) {
+ DisplayController displayController,
+ @ShellAnimationThread Handler mainHandler) {
return new DesktopMinimizationTransitionHandler(mainExecutor, animExecutor,
- displayController);
+ displayController, mainHandler);
}
@WMSingleton
@@ -1230,13 +1260,10 @@ public abstract class WMShellModule {
static Optional<DesktopDisplayEventHandler> provideDesktopDisplayEventHandler(
Context context,
ShellInit shellInit,
- Transitions transitions,
DisplayController displayController,
- RootTaskDisplayAreaOrganizer rootTaskDisplayAreaOrganizer,
- IWindowManager windowManager,
Optional<DesktopUserRepositories> desktopUserRepositories,
Optional<DesktopTasksController> desktopTasksController,
- ShellTaskOrganizer shellTaskOrganizer
+ Optional<DesktopDisplayModeController> desktopDisplayModeController
) {
if (!DesktopModeStatus.canEnterDesktopMode(context)) {
return Optional.empty();
@@ -1245,13 +1272,10 @@ public abstract class WMShellModule {
new DesktopDisplayEventHandler(
context,
shellInit,
- transitions,
displayController,
- rootTaskDisplayAreaOrganizer,
- windowManager,
desktopUserRepositories.get(),
desktopTasksController.get(),
- shellTaskOrganizer));
+ desktopDisplayModeController.get()));
}
@WMSingleton
@@ -1381,6 +1405,29 @@ public abstract class WMShellModule {
return new DesktopModeUiEventLogger(uiEventLogger, packageManager);
}
+ @WMSingleton
+ @Provides
+ static Optional<DesktopDisplayModeController> provideDesktopDisplayModeController(
+ Context context,
+ Transitions transitions,
+ RootTaskDisplayAreaOrganizer rootTaskDisplayAreaOrganizer,
+ IWindowManager windowManager,
+ ShellTaskOrganizer shellTaskOrganizer,
+ DesktopWallpaperActivityTokenProvider desktopWallpaperActivityTokenProvider
+ ) {
+ if (!DesktopModeStatus.canEnterDesktopMode(context)) {
+ return Optional.empty();
+ }
+ return Optional.of(
+ new DesktopDisplayModeController(
+ context,
+ transitions,
+ rootTaskDisplayAreaOrganizer,
+ windowManager,
+ shellTaskOrganizer,
+ desktopWallpaperActivityTokenProvider));
+ }
+
//
// App zoom out
//
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/CloseDesktopTaskTransitionHandler.kt b/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/CloseDesktopTaskTransitionHandler.kt
index 1ce093e02a4a..b22a46edbf3a 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/CloseDesktopTaskTransitionHandler.kt
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/CloseDesktopTaskTransitionHandler.kt
@@ -37,7 +37,6 @@ import com.android.app.animation.Interpolators
import com.android.internal.jank.Cuj.CUJ_DESKTOP_MODE_CLOSE_TASK
import com.android.internal.jank.InteractionJankMonitor
import com.android.wm.shell.common.ShellExecutor
-import com.android.wm.shell.shared.annotations.ShellMainThread
import com.android.wm.shell.transition.Transitions
import java.util.function.Supplier
@@ -49,7 +48,7 @@ constructor(
private val mainExecutor: ShellExecutor,
private val animExecutor: ShellExecutor,
private val transactionSupplier: Supplier<Transaction> = Supplier { Transaction() },
- @ShellMainThread private val handler: Handler,
+ private val animHandler: Handler,
) : Transitions.TransitionHandler {
private val runningAnimations = mutableMapOf<IBinder, List<Animator>>()
@@ -95,7 +94,7 @@ constructor(
interactionJankMonitor.begin(
lastChangeLeash,
context,
- handler,
+ animHandler,
CUJ_DESKTOP_MODE_CLOSE_TASK,
)
}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopDisplayEventHandler.kt b/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopDisplayEventHandler.kt
index c38558d7bde9..946e7952dd50 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopDisplayEventHandler.kt
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopDisplayEventHandler.kt
@@ -16,41 +16,25 @@
package com.android.wm.shell.desktopmode
-import android.app.WindowConfiguration.ACTIVITY_TYPE_STANDARD
-import android.app.WindowConfiguration.WINDOWING_MODE_FREEFORM
-import android.app.WindowConfiguration.WINDOWING_MODE_UNDEFINED
-import android.app.WindowConfiguration.windowingModeToString
import android.content.Context
-import android.provider.Settings
-import android.provider.Settings.Global.DEVELOPMENT_FORCE_DESKTOP_MODE_ON_EXTERNAL_DISPLAYS
import android.view.Display.DEFAULT_DISPLAY
-import android.view.IWindowManager
-import android.view.WindowManager.TRANSIT_CHANGE
import android.window.DesktopExperienceFlags
-import android.window.WindowContainerTransaction
import com.android.internal.protolog.ProtoLog
-import com.android.window.flags.Flags
-import com.android.wm.shell.RootTaskDisplayAreaOrganizer
-import com.android.wm.shell.ShellTaskOrganizer
import com.android.wm.shell.common.DisplayController
import com.android.wm.shell.common.DisplayController.OnDisplaysChangedListener
import com.android.wm.shell.desktopmode.multidesks.OnDeskRemovedListener
import com.android.wm.shell.protolog.ShellProtoLogGroup.WM_SHELL_DESKTOP_MODE
import com.android.wm.shell.shared.desktopmode.DesktopModeStatus
import com.android.wm.shell.sysui.ShellInit
-import com.android.wm.shell.transition.Transitions
/** Handles display events in desktop mode */
class DesktopDisplayEventHandler(
private val context: Context,
shellInit: ShellInit,
- private val transitions: Transitions,
private val displayController: DisplayController,
- private val rootTaskDisplayAreaOrganizer: RootTaskDisplayAreaOrganizer,
- private val windowManager: IWindowManager,
private val desktopUserRepositories: DesktopUserRepositories,
private val desktopTasksController: DesktopTasksController,
- private val shellTaskOrganizer: ShellTaskOrganizer,
+ private val desktopDisplayModeController: DesktopDisplayModeController,
) : OnDisplaysChangedListener, OnDeskRemovedListener {
private val desktopRepository: DesktopRepository
@@ -70,7 +54,7 @@ class DesktopDisplayEventHandler(
override fun onDisplayAdded(displayId: Int) {
if (displayId != DEFAULT_DISPLAY) {
- refreshDisplayWindowingMode()
+ desktopDisplayModeController.refreshDisplayWindowingMode()
}
if (!supportsDesks(displayId)) {
@@ -88,7 +72,7 @@ class DesktopDisplayEventHandler(
override fun onDisplayRemoved(displayId: Int) {
if (displayId != DEFAULT_DISPLAY) {
- refreshDisplayWindowingMode()
+ desktopDisplayModeController.refreshDisplayWindowingMode()
}
// TODO: b/362720497 - move desks in closing display to the remaining desk.
@@ -102,65 +86,6 @@ class DesktopDisplayEventHandler(
}
}
- private fun refreshDisplayWindowingMode() {
- if (!Flags.enableDisplayWindowingModeSwitching()) return
- // TODO: b/375319538 - Replace the check with a DisplayManager API once it's available.
- val isExtendedDisplayEnabled =
- 0 !=
- Settings.Global.getInt(
- context.contentResolver,
- DEVELOPMENT_FORCE_DESKTOP_MODE_ON_EXTERNAL_DISPLAYS,
- 0,
- )
- if (!isExtendedDisplayEnabled) {
- // No action needed in mirror or projected mode.
- return
- }
-
- val hasNonDefaultDisplay =
- rootTaskDisplayAreaOrganizer.getDisplayIds().any { displayId ->
- displayId != DEFAULT_DISPLAY
- }
- val targetDisplayWindowingMode =
- if (hasNonDefaultDisplay) {
- WINDOWING_MODE_FREEFORM
- } else {
- // Use the default display windowing mode when no non-default display.
- windowManager.getWindowingMode(DEFAULT_DISPLAY)
- }
- val tdaInfo = rootTaskDisplayAreaOrganizer.getDisplayAreaInfo(DEFAULT_DISPLAY)
- requireNotNull(tdaInfo) { "DisplayAreaInfo of DEFAULT_DISPLAY must be non-null." }
- val currentDisplayWindowingMode = tdaInfo.configuration.windowConfiguration.windowingMode
- if (currentDisplayWindowingMode == targetDisplayWindowingMode) {
- // Already in the target mode.
- return
- }
-
- logV(
- "As an external display is connected, changing default display's windowing mode from" +
- " ${windowingModeToString(currentDisplayWindowingMode)}" +
- " to ${windowingModeToString(targetDisplayWindowingMode)}"
- )
-
- val wct = WindowContainerTransaction()
- wct.setWindowingMode(tdaInfo.token, targetDisplayWindowingMode)
- shellTaskOrganizer
- .getRunningTasks(DEFAULT_DISPLAY)
- .filter { it.activityType == ACTIVITY_TYPE_STANDARD }
- .forEach {
- // TODO: b/391965153 - Reconsider the logic under multi-desk window hierarchy
- when (it.windowingMode) {
- currentDisplayWindowingMode -> {
- wct.setWindowingMode(it.token, currentDisplayWindowingMode)
- }
- targetDisplayWindowingMode -> {
- wct.setWindowingMode(it.token, WINDOWING_MODE_UNDEFINED)
- }
- }
- }
- transitions.startTransition(TRANSIT_CHANGE, wct, /* handler= */ null)
- }
-
// TODO: b/362720497 - connected/projected display considerations.
private fun supportsDesks(displayId: Int): Boolean =
DesktopModeStatus.canEnterDesktopMode(context)
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopDisplayModeController.kt b/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopDisplayModeController.kt
new file mode 100644
index 000000000000..c9a63ff818f5
--- /dev/null
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopDisplayModeController.kt
@@ -0,0 +1,121 @@
+/*
+ * Copyright (C) 2025 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.desktopmode
+
+import android.app.WindowConfiguration.ACTIVITY_TYPE_STANDARD
+import android.app.WindowConfiguration.WINDOWING_MODE_FREEFORM
+import android.app.WindowConfiguration.WINDOWING_MODE_FULLSCREEN
+import android.app.WindowConfiguration.WINDOWING_MODE_UNDEFINED
+import android.app.WindowConfiguration.windowingModeToString
+import android.content.Context
+import android.provider.Settings
+import android.provider.Settings.Global.DEVELOPMENT_FORCE_DESKTOP_MODE_ON_EXTERNAL_DISPLAYS
+import android.view.Display.DEFAULT_DISPLAY
+import android.view.IWindowManager
+import android.view.WindowManager.TRANSIT_CHANGE
+import android.window.WindowContainerTransaction
+import com.android.internal.protolog.ProtoLog
+import com.android.window.flags.Flags
+import com.android.wm.shell.RootTaskDisplayAreaOrganizer
+import com.android.wm.shell.ShellTaskOrganizer
+import com.android.wm.shell.desktopmode.desktopwallpaperactivity.DesktopWallpaperActivityTokenProvider
+import com.android.wm.shell.protolog.ShellProtoLogGroup.WM_SHELL_DESKTOP_MODE
+import com.android.wm.shell.transition.Transitions
+
+/** Controls the display windowing mode in desktop mode */
+class DesktopDisplayModeController(
+ private val context: Context,
+ private val transitions: Transitions,
+ private val rootTaskDisplayAreaOrganizer: RootTaskDisplayAreaOrganizer,
+ private val windowManager: IWindowManager,
+ private val shellTaskOrganizer: ShellTaskOrganizer,
+ private val desktopWallpaperActivityTokenProvider: DesktopWallpaperActivityTokenProvider,
+) {
+
+ fun refreshDisplayWindowingMode() {
+ if (!Flags.enableDisplayWindowingModeSwitching()) return
+ // TODO: b/375319538 - Replace the check with a DisplayManager API once it's available.
+ val isExtendedDisplayEnabled =
+ 0 !=
+ Settings.Global.getInt(
+ context.contentResolver,
+ DEVELOPMENT_FORCE_DESKTOP_MODE_ON_EXTERNAL_DISPLAYS,
+ 0,
+ )
+ if (!isExtendedDisplayEnabled) {
+ // No action needed in mirror or projected mode.
+ return
+ }
+
+ val hasNonDefaultDisplay =
+ rootTaskDisplayAreaOrganizer.getDisplayIds().any { displayId ->
+ displayId != DEFAULT_DISPLAY
+ }
+ val targetDisplayWindowingMode =
+ if (hasNonDefaultDisplay) {
+ WINDOWING_MODE_FREEFORM
+ } else {
+ // Use the default display windowing mode when no non-default display.
+ windowManager.getWindowingMode(DEFAULT_DISPLAY)
+ }
+ val tdaInfo = rootTaskDisplayAreaOrganizer.getDisplayAreaInfo(DEFAULT_DISPLAY)
+ requireNotNull(tdaInfo) { "DisplayAreaInfo of DEFAULT_DISPLAY must be non-null." }
+ val currentDisplayWindowingMode = tdaInfo.configuration.windowConfiguration.windowingMode
+ if (currentDisplayWindowingMode == targetDisplayWindowingMode) {
+ // Already in the target mode.
+ return
+ }
+
+ logV(
+ "As an external display is connected, changing default display's windowing mode from" +
+ " ${windowingModeToString(currentDisplayWindowingMode)}" +
+ " to ${windowingModeToString(targetDisplayWindowingMode)}"
+ )
+
+ val wct = WindowContainerTransaction()
+ wct.setWindowingMode(tdaInfo.token, targetDisplayWindowingMode)
+ shellTaskOrganizer
+ .getRunningTasks(DEFAULT_DISPLAY)
+ .filter { it.activityType == ACTIVITY_TYPE_STANDARD }
+ .forEach {
+ // TODO: b/391965153 - Reconsider the logic under multi-desk window hierarchy
+ when (it.windowingMode) {
+ currentDisplayWindowingMode -> {
+ wct.setWindowingMode(it.token, currentDisplayWindowingMode)
+ }
+ targetDisplayWindowingMode -> {
+ wct.setWindowingMode(it.token, WINDOWING_MODE_UNDEFINED)
+ }
+ }
+ }
+ // The override windowing mode of DesktopWallpaper can be UNDEFINED on fullscreen-display
+ // right after the first launch while its resolved windowing mode is FULLSCREEN. We here
+ // it has the FULLSCREEN override windowing mode.
+ desktopWallpaperActivityTokenProvider.getToken(DEFAULT_DISPLAY)?.let { token ->
+ wct.setWindowingMode(token, WINDOWING_MODE_FULLSCREEN)
+ }
+ transitions.startTransition(TRANSIT_CHANGE, wct, /* handler= */ null)
+ }
+
+ private fun logV(msg: String, vararg arguments: Any?) {
+ ProtoLog.v(WM_SHELL_DESKTOP_MODE, "%s: $msg", TAG, *arguments)
+ }
+
+ companion object {
+ private const val TAG = "DesktopDisplayModeController"
+ }
+}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopMinimizationTransitionHandler.kt b/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopMinimizationTransitionHandler.kt
index 728638d9bbd3..7074e8bc9cce 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopMinimizationTransitionHandler.kt
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopMinimizationTransitionHandler.kt
@@ -18,14 +18,17 @@ package com.android.wm.shell.desktopmode
import android.animation.Animator
import android.app.WindowConfiguration.WINDOWING_MODE_FREEFORM
+import android.os.Handler
import android.os.IBinder
-import android.util.DisplayMetrics
import android.view.SurfaceControl.Transaction
import android.window.TransitionInfo
import android.window.TransitionRequestInfo
import android.window.WindowContainerTransaction
+import com.android.internal.jank.InteractionJankMonitor
+import com.android.internal.protolog.ProtoLog
import com.android.wm.shell.common.DisplayController
import com.android.wm.shell.common.ShellExecutor
+import com.android.wm.shell.protolog.ShellProtoLogGroup.WM_SHELL_DESKTOP_MODE
import com.android.wm.shell.shared.TransitionUtil
import com.android.wm.shell.shared.animation.MinimizeAnimator.create
import com.android.wm.shell.transition.Transitions
@@ -41,6 +44,7 @@ class DesktopMinimizationTransitionHandler(
private val mainExecutor: ShellExecutor,
private val animExecutor: ShellExecutor,
private val displayController: DisplayController,
+ private val animHandler: Handler,
) : Transitions.TransitionHandler {
/** Shouldn't handle anything */
@@ -90,10 +94,30 @@ class DesktopMinimizationTransitionHandler(
val t = Transaction()
val sc = change.leash
finishTransaction.hide(sc)
- val displayMetrics: DisplayMetrics? =
- change.taskInfo?.let {
- displayController.getDisplayContext(it.displayId)?.getResources()?.displayMetrics
- }
- return displayMetrics?.let { create(it, change, t, onAnimFinish) }
+ val displayContext =
+ change.taskInfo?.let { displayController.getDisplayContext(it.displayId) }
+ if (displayContext == null) {
+ logW(
+ "displayContext is null for taskId=${change.taskInfo?.taskId}, " +
+ "displayId=${change.taskInfo?.displayId}"
+ )
+ return null
+ }
+ return create(
+ displayContext,
+ change,
+ t,
+ onAnimFinish,
+ InteractionJankMonitor.getInstance(),
+ animHandler,
+ )
+ }
+
+ private companion object {
+ private fun logW(msg: String, vararg arguments: Any?) {
+ ProtoLog.w(WM_SHELL_DESKTOP_MODE, "%s: $msg", TAG, *arguments)
+ }
+
+ const val TAG = "DesktopMinimizationTransitionHandler"
}
}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopRepository.kt b/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopRepository.kt
index 031925b2997a..7c6cf4a8b37f 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopRepository.kt
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopRepository.kt
@@ -332,29 +332,22 @@ class DesktopRepository(
return false
}
- /**
- * Adds given task to the closing task list for [displayId]'s active desk.
- *
- * TODO: b/389960283 - add explicit [deskId] argument.
- */
- fun addClosingTask(displayId: Int, taskId: Int) {
- val activeDesk =
- desktopData.getActiveDesk(displayId)
- ?: error("Expected active desk in display: $displayId")
- if (activeDesk.closingTasks.add(taskId)) {
- logD(
- "Added closing task=%d displayId=%d deskId=%d",
- taskId,
- displayId,
- activeDesk.deskId,
- )
+ /** Adds given task to the closing task list of its desk. */
+ fun addClosingTask(displayId: Int, deskId: Int?, taskId: Int) {
+ val desk =
+ deskId?.let { desktopData.getDesk(it) }
+ ?: checkNotNull(desktopData.getActiveDesk(displayId)) {
+ "Expected active desk in display: $displayId"
+ }
+ if (desk.closingTasks.add(taskId)) {
+ logD("Added closing task=%d displayId=%d deskId=%d", taskId, displayId, desk.deskId)
} else {
// If the task hasn't been removed from closing list after it disappeared.
logW(
"Task with taskId=%d displayId=%d deskId=%d is already closing",
taskId,
displayId,
- activeDesk.deskId,
+ desk.deskId,
)
}
}
@@ -392,7 +385,8 @@ class DesktopRepository(
* Checks if a task is the only visible, non-closing, non-minimized task on the active desk of
* the given display, or any display's active desk if [displayId] is [INVALID_DISPLAY].
*
- * TODO: b/389960283 - add explicit [deskId] argument.
+ * TODO: b/389960283 - consider forcing callers to use [isOnlyVisibleNonClosingTaskInDesk] with
+ * an explicit desk id instead of using this function and defaulting to the active one.
*/
fun isOnlyVisibleNonClosingTask(taskId: Int, displayId: Int = INVALID_DISPLAY): Boolean {
val activeDesks =
@@ -402,14 +396,27 @@ class DesktopRepository(
desktopData.getAllActiveDesks()
}
return activeDesks.any { desk ->
- desk.visibleTasks
- .subtract(desk.closingTasks)
- .subtract(desk.minimizedTasks)
- .singleOrNull() == taskId
+ isOnlyVisibleNonClosingTaskInDesk(
+ taskId = taskId,
+ deskId = desk.deskId,
+ displayId = desk.displayId,
+ )
}
}
/**
+ * Checks if a task is the only visible, non-closing, non-minimized task on the given desk of
+ * the given display.
+ */
+ fun isOnlyVisibleNonClosingTaskInDesk(taskId: Int, deskId: Int, displayId: Int): Boolean {
+ val desk = desktopData.getDesk(deskId) ?: return false
+ return desk.visibleTasks
+ .subtract(desk.closingTasks)
+ .subtract(desk.minimizedTasks)
+ .singleOrNull() == taskId
+ }
+
+ /**
* Returns the active tasks in the given display's active desk.
*
* TODO: b/389960283 - migrate callers to [getActiveTaskIdsInDesk].
@@ -686,6 +693,11 @@ class DesktopRepository(
* TODO: b/389960283 - add explicit [deskId] argument.
*/
fun setTopTransparentFullscreenTaskId(displayId: Int, taskId: Int) {
+ logD(
+ "Top transparent fullscreen task set for display: taskId=%d, displayId=%d",
+ taskId,
+ displayId,
+ )
desktopData.getActiveDesk(displayId)?.topTransparentFullscreenTaskId = taskId
}
@@ -703,6 +715,11 @@ class DesktopRepository(
* TODO: b/389960283 - add explicit [deskId] argument.
*/
fun clearTopTransparentFullscreenTaskId(displayId: Int) {
+ logD(
+ "Top transparent fullscreen task cleared for display: taskId=%d, displayId=%d",
+ desktopData.getActiveDesk(displayId)?.topTransparentFullscreenTaskId,
+ displayId,
+ )
desktopData.getActiveDesk(displayId)?.topTransparentFullscreenTaskId = null
}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopTasksController.kt b/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopTasksController.kt
index 93058db0c171..ca870d2b6988 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopTasksController.kt
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopTasksController.kt
@@ -117,6 +117,7 @@ import com.android.wm.shell.recents.RecentsTransitionHandler
import com.android.wm.shell.recents.RecentsTransitionStateListener
import com.android.wm.shell.recents.RecentsTransitionStateListener.RecentsTransitionState
import com.android.wm.shell.recents.RecentsTransitionStateListener.TRANSITION_STATE_NOT_RUNNING
+import com.android.wm.shell.shared.R as SharedR
import com.android.wm.shell.shared.TransitionUtil
import com.android.wm.shell.shared.annotations.ExternalThread
import com.android.wm.shell.shared.annotations.ShellDesktopThread
@@ -801,6 +802,9 @@ class DesktopTasksController(
): ((IBinder) -> Unit) {
val taskId = taskInfo.taskId
val deskId = taskRepository.getDeskIdForTask(taskInfo.taskId)
+ if (deskId == null && DesktopExperienceFlags.ENABLE_MULTIPLE_DESKTOPS_BACKEND.isTrue) {
+ error("Did not find desk for task: $taskId")
+ }
snapEventHandler.removeTaskIfTiled(displayId, taskId)
val shouldExitDesktop =
willExitDesktop(
@@ -818,7 +822,7 @@ class DesktopTasksController(
shouldEndUpAtHome = true,
)
- taskRepository.addClosingTask(displayId, taskId)
+ taskRepository.addClosingTask(displayId = displayId, deskId = deskId, taskId = taskId)
taskbarDesktopTaskListener?.onTaskbarCornerRoundingUpdate(
doesAnyTaskRequireTaskbarRounding(displayId, taskId)
)
@@ -870,6 +874,10 @@ class DesktopTasksController(
private fun minimizeTaskInner(taskInfo: RunningTaskInfo, minimizeReason: MinimizeReason) {
val taskId = taskInfo.taskId
val deskId = taskRepository.getDeskIdForTask(taskInfo.taskId)
+ if (deskId == null && DesktopExperienceFlags.ENABLE_MULTIPLE_DESKTOPS_BACKEND.isTrue) {
+ logW("minimizeTaskInner: desk not found for task: ${taskInfo.taskId}")
+ return
+ }
val displayId = taskInfo.displayId
val wct = WindowContainerTransaction()
@@ -890,10 +898,26 @@ class DesktopTasksController(
taskInfo = taskInfo,
reason = DesktopImmersiveController.ExitReason.MINIMIZED,
)
-
- wct.reorder(taskInfo.token, false)
- val isLastTask = taskRepository.isOnlyVisibleNonClosingTask(taskId, displayId)
- val transition: IBinder =
+ if (DesktopExperienceFlags.ENABLE_MULTIPLE_DESKTOPS_BACKEND.isTrue) {
+ desksOrganizer.minimizeTask(
+ wct = wct,
+ deskId = checkNotNull(deskId) { "Expected non-null deskId" },
+ task = taskInfo,
+ )
+ } else {
+ wct.reorder(taskInfo.token, /* onTop= */ false)
+ }
+ val isLastTask =
+ if (DesktopExperienceFlags.ENABLE_MULTIPLE_DESKTOPS_BACKEND.isTrue) {
+ taskRepository.isOnlyVisibleNonClosingTaskInDesk(
+ taskId = taskId,
+ deskId = checkNotNull(deskId) { "Expected non-null deskId" },
+ displayId = displayId,
+ )
+ } else {
+ taskRepository.isOnlyVisibleNonClosingTask(taskId = taskId, displayId = displayId)
+ }
+ val transition =
freeformTaskTransitionStarter.startMinimizedModeTransition(wct, taskId, isLastTask)
desktopTasksLimiter.ifPresent {
it.addPendingMinimizeChange(
@@ -1055,7 +1079,8 @@ class DesktopTasksController(
)
}
- private fun startLaunchTransition(
+ @VisibleForTesting
+ fun startLaunchTransition(
transitionType: Int,
wct: WindowContainerTransaction,
launchingTaskId: Int?,
@@ -1063,34 +1088,52 @@ class DesktopTasksController(
displayId: Int = DEFAULT_DISPLAY,
unminimizeReason: UnminimizeReason = UnminimizeReason.UNKNOWN,
): IBinder {
+ // TODO: b/397619806 - Consolidate sharable logic with [handleFreeformTaskLaunch].
+ var launchTransaction = wct
val taskIdToMinimize =
addAndGetMinimizeChanges(
displayId,
- wct,
+ launchTransaction,
newTaskId = launchingTaskId,
launchingNewIntent = launchingTaskId == null,
)
val exitImmersiveResult =
desktopImmersiveController.exitImmersiveIfApplicable(
- wct = wct,
+ wct = launchTransaction,
displayId = displayId,
excludeTaskId = launchingTaskId,
reason = DesktopImmersiveController.ExitReason.TASK_LAUNCH,
)
+ var deskIdToActivate: Int? = null
+ if (
+ DesktopExperienceFlags.ENABLE_DISPLAY_WINDOWING_MODE_SWITCHING.isTrue &&
+ !isDesktopModeShowing(displayId)
+ ) {
+ deskIdToActivate =
+ checkNotNull(
+ launchingTaskId?.let { taskRepository.getDeskIdForTask(it) }
+ ?: getDefaultDeskId(displayId)
+ )
+ val activateDeskWct = WindowContainerTransaction()
+ addDeskActivationChanges(deskIdToActivate, activateDeskWct)
+ // Desk activation must be handled before app launch-related transactions.
+ activateDeskWct.merge(launchTransaction, /* transfer= */ true)
+ launchTransaction = activateDeskWct
+ }
val t =
if (remoteTransition == null) {
desktopMixedTransitionHandler.startLaunchTransition(
transitionType = transitionType,
- wct = wct,
+ wct = launchTransaction,
taskId = launchingTaskId,
minimizingTaskId = taskIdToMinimize,
exitingImmersiveTask = exitImmersiveResult.asExit()?.exitingTask,
)
} else if (taskIdToMinimize == null) {
val remoteTransitionHandler = OneShotRemoteHandler(mainExecutor, remoteTransition)
- transitions.startTransition(transitionType, wct, remoteTransitionHandler).also {
- remoteTransitionHandler.setTransition(it)
- }
+ transitions
+ .startTransition(transitionType, launchTransaction, remoteTransitionHandler)
+ .also { remoteTransitionHandler.setTransition(it) }
} else {
val remoteTransitionHandler =
DesktopWindowLimitRemoteHandler(
@@ -1099,9 +1142,9 @@ class DesktopTasksController(
remoteTransition,
taskIdToMinimize,
)
- transitions.startTransition(transitionType, wct, remoteTransitionHandler).also {
- remoteTransitionHandler.setTransition(it)
- }
+ transitions
+ .startTransition(transitionType, launchTransaction, remoteTransitionHandler)
+ .also { remoteTransitionHandler.setTransition(it) }
}
if (taskIdToMinimize != null) {
addPendingMinimizeTransition(t, taskIdToMinimize, MinimizeReason.TASK_LIMIT)
@@ -1109,6 +1152,24 @@ class DesktopTasksController(
if (launchingTaskId != null && taskRepository.isMinimizedTask(launchingTaskId)) {
addPendingUnminimizeTransition(t, displayId, launchingTaskId, unminimizeReason)
}
+ if (
+ DesktopExperienceFlags.ENABLE_MULTIPLE_DESKTOPS_BACKEND.isTrue &&
+ deskIdToActivate != null
+ ) {
+ if (DesktopExperienceFlags.ENABLE_DISPLAY_WINDOWING_MODE_SWITCHING.isTrue) {
+ desksTransitionObserver.addPendingTransition(
+ DeskTransition.ActivateDesk(
+ token = t,
+ displayId = displayId,
+ deskId = deskIdToActivate,
+ )
+ )
+ }
+
+ desktopModeEnterExitTransitionListener?.onEnterDesktopModeTransitionStarted(
+ FREEFORM_ANIMATION_DURATION
+ )
+ }
exitImmersiveResult.asExit()?.runOnTransitionStart?.invoke(t)
return t
}
@@ -1231,9 +1292,9 @@ class DesktopTasksController(
// home.
if (Flags.enablePerDisplayDesktopWallpaperActivity()) {
performDesktopExitCleanupIfNeeded(
- task.taskId,
- task.displayId,
- wct,
+ taskId = task.taskId,
+ displayId = task.displayId,
+ wct = wct,
forceToFullscreen = false,
// TODO: b/371096166 - Temporary turing home relaunch off to prevent home stealing
// display focus. Remove shouldEndUpAtHome = false when home focus handling
@@ -1800,6 +1861,7 @@ class DesktopTasksController(
private fun performDesktopExitCleanupIfNeeded(
taskId: Int,
+ deskId: Int? = null,
displayId: Int,
wct: WindowContainerTransaction,
forceToFullscreen: Boolean,
@@ -1813,13 +1875,14 @@ class DesktopTasksController(
// |RunOnTransitStart| when the transition is started.
return performDesktopExitCleanUp(
wct = wct,
- deskId = null,
+ deskId = deskId,
displayId = displayId,
willExitDesktop = true,
shouldEndUpAtHome = shouldEndUpAtHome,
)
}
+ /** TODO: b/394268248 - update [deskId] to be non-null. */
private fun performDesktopExitCleanUp(
wct: WindowContainerTransaction,
deskId: Int?,
@@ -2015,7 +2078,9 @@ class DesktopTasksController(
}
val cornerRadius =
context.resources
- .getDimensionPixelSize(R.dimen.desktop_windowing_freeform_rounded_corner_radius)
+ .getDimensionPixelSize(
+ SharedR.dimen.desktop_windowing_freeform_rounded_corner_radius
+ )
.toFloat()
info.changes
.filter { it.taskInfo?.windowingMode == WINDOWING_MODE_FREEFORM }
@@ -2370,17 +2435,28 @@ class DesktopTasksController(
): WindowContainerTransaction? {
logV("handleTaskClosing")
if (!isDesktopModeShowing(task.displayId)) return null
+ val deskId = taskRepository.getDeskIdForTask(task.taskId)
+ if (deskId == null && DesktopExperienceFlags.ENABLE_MULTIPLE_DESKTOPS_BACKEND.isTrue) {
+ return null
+ }
val wct = WindowContainerTransaction()
- performDesktopExitCleanupIfNeeded(
- task.taskId,
- task.displayId,
- wct,
- forceToFullscreen = false,
- )
+ val deactivationRunnable =
+ performDesktopExitCleanupIfNeeded(
+ taskId = task.taskId,
+ deskId = deskId,
+ displayId = task.displayId,
+ wct = wct,
+ forceToFullscreen = false,
+ )
+ deactivationRunnable?.invoke(transition)
if (!DesktopModeFlags.ENABLE_DESKTOP_WINDOWING_BACK_NAVIGATION.isTrue()) {
- taskRepository.addClosingTask(task.displayId, task.taskId)
+ taskRepository.addClosingTask(
+ displayId = task.displayId,
+ deskId = deskId,
+ taskId = task.taskId,
+ )
snapEventHandler.removeTaskIfTiled(task.displayId, task.taskId)
}
@@ -2584,9 +2660,9 @@ class DesktopTasksController(
wct.setDensityDpi(taskInfo.token, getDefaultDensityDpi())
performDesktopExitCleanupIfNeeded(
- taskInfo.taskId,
- taskInfo.displayId,
- wct,
+ taskId = taskInfo.taskId,
+ displayId = taskInfo.displayId,
+ wct = wct,
forceToFullscreen = false,
shouldEndUpAtHome = false,
)
@@ -2665,10 +2741,9 @@ class DesktopTasksController(
activateDesk(deskId, remoteTransition)
}
- /** Activates the given desk. */
- fun activateDesk(deskId: Int, remoteTransition: RemoteTransition? = null) {
+ /** Activates the given desk but without starting a transition. */
+ fun addDeskActivationChanges(deskId: Int, wct: WindowContainerTransaction) {
val displayId = taskRepository.getDisplayForDesk(deskId)
- val wct = WindowContainerTransaction()
if (DesktopExperienceFlags.ENABLE_MULTIPLE_DESKTOPS_BACKEND.isTrue) {
prepareForDeskActivation(displayId, wct)
desksOrganizer.activateDesk(wct, deskId)
@@ -2681,6 +2756,13 @@ class DesktopTasksController(
} else {
bringDesktopAppsToFront(displayId, wct)
}
+ }
+
+ /** Activates the given desk. */
+ fun activateDesk(deskId: Int, remoteTransition: RemoteTransition? = null) {
+ val displayId = taskRepository.getDisplayForDesk(deskId)
+ val wct = WindowContainerTransaction()
+ addDeskActivationChanges(deskId, wct)
val transitionType = transitionType(remoteTransition)
val handler =
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopTasksLimiter.kt b/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopTasksLimiter.kt
index 81b136dd1569..f9ab359e952d 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopTasksLimiter.kt
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopTasksLimiter.kt
@@ -26,7 +26,6 @@ import android.window.DesktopModeFlags
import android.window.TransitionInfo
import android.window.WindowContainerTransaction
import androidx.annotation.VisibleForTesting
-import com.android.internal.jank.Cuj.CUJ_DESKTOP_MODE_MINIMIZE_WINDOW
import com.android.internal.jank.InteractionJankMonitor
import com.android.internal.protolog.ProtoLog
import com.android.wm.shell.ShellTaskOrganizer
@@ -176,28 +175,13 @@ class DesktopTasksLimiter(
return taskChange.mode == TRANSIT_TO_BACK
}
- override fun onTransitionStarting(transition: IBinder) {
- val mActiveTaskDetails = activeTransitionTokensAndTasks[transition]
- val info = mActiveTaskDetails?.transitionInfo ?: return
- val minimizeChange = getMinimizeChange(info, mActiveTaskDetails.taskId) ?: return
- // Begin minimize window CUJ instrumentation.
- interactionJankMonitor.begin(
- minimizeChange.leash,
- context,
- handler,
- CUJ_DESKTOP_MODE_MINIMIZE_WINDOW,
- )
- }
-
private fun getMinimizeChange(info: TransitionInfo, taskId: Int): TransitionInfo.Change? =
info.changes.find { change ->
change.taskInfo?.taskId == taskId && change.mode == TRANSIT_TO_BACK
}
override fun onTransitionMerged(merged: IBinder, playing: IBinder) {
- if (activeTransitionTokensAndTasks.remove(merged) != null) {
- interactionJankMonitor.end(CUJ_DESKTOP_MODE_MINIMIZE_WINDOW)
- }
+ activeTransitionTokensAndTasks.remove(merged)
pendingTransitionTokensAndTasks.remove(merged)?.let { taskToTransfer ->
pendingTransitionTokensAndTasks[playing] = taskToTransfer
}
@@ -209,13 +193,6 @@ class DesktopTasksLimiter(
}
override fun onTransitionFinished(transition: IBinder, aborted: Boolean) {
- if (activeTransitionTokensAndTasks.remove(transition) != null) {
- if (aborted) {
- interactionJankMonitor.cancel(CUJ_DESKTOP_MODE_MINIMIZE_WINDOW)
- } else {
- interactionJankMonitor.end(CUJ_DESKTOP_MODE_MINIMIZE_WINDOW)
- }
- }
pendingTransitionTokensAndTasks.remove(transition)
activeUnminimizeTransitionTokensAndTasks.remove(transition)
pendingUnminimizeTransitionTokensAndTasks.remove(transition)
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DisplayDeskState.aidl b/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DisplayDeskState.aidl
index 59add47fc79d..5f45192569e3 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DisplayDeskState.aidl
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DisplayDeskState.aidl
@@ -18,13 +18,11 @@ package com.android.wm.shell.desktopmode;
/**
* Defines the state of desks on a display whose ID is `displayId`, which is:
- * - `canCreateDesks`: whether it's possible to create new desks on this display.
* - `activeDeskId`: the currently active desk Id, or `-1` if none is active.
* - `deskId`: the list of desk Ids of the available desks on this display.
*/
parcelable DisplayDeskState {
int displayId;
- boolean canCreateDesk;
int activeDeskId;
int[] deskIds;
}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/IDesktopTaskListener.aidl b/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/IDesktopTaskListener.aidl
index 7ed1581cdfdb..cefbd8947feb 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/IDesktopTaskListener.aidl
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/IDesktopTaskListener.aidl
@@ -28,7 +28,7 @@ oneway interface IDesktopTaskListener {
* Called once when the listener first gets connected to initialize it with the current state of
* desks in Shell.
*/
- void onListenerConnected(in DisplayDeskState[] displayDeskStates);
+ void onListenerConnected(in DisplayDeskState[] displayDeskStates, boolean canCreateDesks);
/** Desktop tasks visibility has changed. Visible if at least 1 task is visible. */
void onTasksVisibilityChanged(int displayId, int visibleTasksCount);
@@ -49,10 +49,10 @@ oneway interface IDesktopTaskListener {
void onExitDesktopModeTransitionStarted(int transitionDuration);
/**
- * Called when the conditions that allow the creation of a new desk on the display whose ID is
- * `displayId` changes to `canCreateDesks`. It's also called when a new display is added.
+ * Called when the conditions that allow the creation of a new desk changes. This is a global
+ * state for the entire device.
*/
- void onCanCreateDesksChanged(int displayId, boolean canCreateDesks);
+ void onCanCreateDesksChanged(boolean canCreateDesks);
/** Called when a desk whose ID is `deskId` is added to the display whose ID is `displayId`. */
void onDeskAdded(int displayId, int deskId);
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/multidesks/DesksOrganizer.kt b/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/multidesks/DesksOrganizer.kt
index 0f2f3711a9a3..fc359d7d67b6 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/multidesks/DesksOrganizer.kt
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/multidesks/DesksOrganizer.kt
@@ -40,9 +40,19 @@ interface DesksOrganizer {
task: ActivityManager.RunningTaskInfo,
)
+ /** Minimizes the given task of the given deskId. */
+ fun minimizeTask(
+ wct: WindowContainerTransaction,
+ deskId: Int,
+ task: ActivityManager.RunningTaskInfo,
+ )
+
/** Whether the change is for the given desk id. */
fun isDeskChange(change: TransitionInfo.Change, deskId: Int): Boolean
+ /** Whether the change is for a known desk. */
+ fun isDeskChange(change: TransitionInfo.Change): Boolean
+
/**
* Returns the desk id in which the task in the given change is located at the end of a
* transition, if any.
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/multidesks/RootTaskDesksOrganizer.kt b/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/multidesks/RootTaskDesksOrganizer.kt
index 339932cabd2c..f576258ebdaa 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/multidesks/RootTaskDesksOrganizer.kt
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/multidesks/RootTaskDesksOrganizer.kt
@@ -15,7 +15,9 @@
*/
package com.android.wm.shell.desktopmode.multidesks
+import android.annotation.SuppressLint
import android.app.ActivityManager.RunningTaskInfo
+import android.app.ActivityTaskManager.INVALID_TASK_ID
import android.app.WindowConfiguration.ACTIVITY_TYPE_STANDARD
import android.app.WindowConfiguration.ACTIVITY_TYPE_UNDEFINED
import android.app.WindowConfiguration.WINDOWING_MODE_FREEFORM
@@ -25,6 +27,7 @@ import android.view.SurfaceControl
import android.view.WindowManager.TRANSIT_TO_FRONT
import android.window.DesktopExperienceFlags
import android.window.TransitionInfo
+import android.window.WindowContainerToken
import android.window.WindowContainerTransaction
import androidx.core.util.forEach
import com.android.internal.annotations.VisibleForTesting
@@ -43,8 +46,12 @@ class RootTaskDesksOrganizer(
private val shellTaskOrganizer: ShellTaskOrganizer,
) : DesksOrganizer, ShellTaskOrganizer.TaskListener {
- private val deskCreateRequests = mutableListOf<CreateRequest>()
- @VisibleForTesting val roots = SparseArray<DeskRoot>()
+ private val createDeskRootRequests = mutableListOf<CreateDeskRequest>()
+ @VisibleForTesting val deskRootsByDeskId = SparseArray<DeskRoot>()
+ private val createDeskMinimizationRootRequests =
+ mutableListOf<CreateDeskMinimizationRootRequest>()
+ @VisibleForTesting
+ val deskMinimizationRootsByDeskId: MutableMap<Int, DeskMinimizationRoot> = mutableMapOf()
init {
if (DesktopExperienceFlags.ENABLE_MULTIPLE_DESKTOPS_BACKEND.isTrue) {
@@ -57,7 +64,7 @@ class RootTaskDesksOrganizer(
override fun createDesk(displayId: Int, callback: OnCreateCallback) {
logV("createDesk in display: %d", displayId)
- deskCreateRequests += CreateRequest(displayId, callback)
+ createDeskRootRequests += CreateDeskRequest(displayId, callback)
shellTaskOrganizer.createRootTask(
displayId,
WINDOWING_MODE_FREEFORM,
@@ -68,14 +75,14 @@ class RootTaskDesksOrganizer(
override fun removeDesk(wct: WindowContainerTransaction, deskId: Int) {
logV("removeDesk %d", deskId)
- val desk = checkNotNull(roots[deskId]) { "Root not found for desk: $deskId" }
- wct.removeRootTask(desk.taskInfo.token)
+ deskRootsByDeskId[deskId]?.let { root -> wct.removeRootTask(root.token) }
+ deskMinimizationRootsByDeskId[deskId]?.let { root -> wct.removeRootTask(root.token) }
}
override fun activateDesk(wct: WindowContainerTransaction, deskId: Int) {
logV("activateDesk %d", deskId)
- val root = checkNotNull(roots[deskId]) { "Root not found for desk: $deskId" }
- wct.reorder(root.taskInfo.token, /* onTop= */ true)
+ val root = checkNotNull(deskRootsByDeskId[deskId]) { "Root not found for desk: $deskId" }
+ wct.reorder(root.token, /* onTop= */ true)
wct.setLaunchRoot(
/* container= */ root.taskInfo.token,
/* windowingModes= */ intArrayOf(WINDOWING_MODE_FREEFORM, WINDOWING_MODE_UNDEFINED),
@@ -85,7 +92,7 @@ class RootTaskDesksOrganizer(
override fun deactivateDesk(wct: WindowContainerTransaction, deskId: Int) {
logV("deactivateDesk %d", deskId)
- val root = checkNotNull(roots[deskId]) { "Root not found for desk: $deskId" }
+ val root = checkNotNull(deskRootsByDeskId[deskId]) { "Root not found for desk: $deskId" }
wct.setLaunchRoot(
/* container= */ root.taskInfo.token,
/* windowingModes= */ null,
@@ -98,16 +105,58 @@ class RootTaskDesksOrganizer(
deskId: Int,
task: RunningTaskInfo,
) {
- val root = roots[deskId] ?: error("Root not found for desk: $deskId")
+ val root = deskRootsByDeskId[deskId] ?: error("Root not found for desk: $deskId")
wct.setWindowingMode(task.token, WINDOWING_MODE_UNDEFINED)
wct.reparent(task.token, root.taskInfo.token, /* onTop= */ true)
}
+ override fun minimizeTask(wct: WindowContainerTransaction, deskId: Int, task: RunningTaskInfo) {
+ val deskRoot =
+ checkNotNull(deskRootsByDeskId[deskId]) { "Root not found for desk: $deskId" }
+ val minimizationRoot =
+ checkNotNull(deskMinimizationRootsByDeskId[deskId]) {
+ "Minimization root not found for desk: $deskId"
+ }
+ val taskId = task.taskId
+ if (taskId in minimizationRoot.children) {
+ logV("Task #$taskId is already minimized in desk #$deskId")
+ return
+ }
+ if (taskId !in deskRoot.children) {
+ logE("Attempted to minimize task=${task.taskId} in desk=$deskId but it was not a child")
+ return
+ }
+ wct.reparent(task.token, minimizationRoot.token, /* onTop= */ true)
+ }
+
override fun isDeskChange(change: TransitionInfo.Change, deskId: Int): Boolean =
- roots.contains(deskId) && change.taskInfo?.taskId == deskId
+ (isDeskRootChange(change) && change.taskId == deskId) ||
+ (getDeskMinimizationRootInChange(change)?.deskId == deskId)
+
+ override fun isDeskChange(change: TransitionInfo.Change): Boolean =
+ isDeskRootChange(change) || getDeskMinimizationRootInChange(change) != null
+
+ private fun isDeskRootChange(change: TransitionInfo.Change): Boolean =
+ change.taskId in deskRootsByDeskId
- override fun getDeskAtEnd(change: TransitionInfo.Change): Int? =
- change.taskInfo?.parentTaskId?.takeIf { it in roots }
+ private fun getDeskMinimizationRootInChange(
+ change: TransitionInfo.Change
+ ): DeskMinimizationRoot? =
+ deskMinimizationRootsByDeskId.values.find { it.rootId == change.taskId }
+
+ private val TransitionInfo.Change.taskId: Int
+ get() = taskInfo?.taskId ?: INVALID_TASK_ID
+
+ override fun getDeskAtEnd(change: TransitionInfo.Change): Int? {
+ val parentTaskId = change.taskInfo?.parentTaskId ?: return null
+ if (parentTaskId in deskRootsByDeskId) {
+ return parentTaskId
+ }
+ val deskMinimizationRoot =
+ deskMinimizationRootsByDeskId.values.find { root -> root.rootId == parentTaskId }
+ ?: return null
+ return deskMinimizationRoot.deskId
+ }
override fun isDeskActiveAtEnd(change: TransitionInfo.Change, deskId: Int): Boolean =
change.taskInfo?.taskId == deskId &&
@@ -115,51 +164,176 @@ class RootTaskDesksOrganizer(
change.mode == TRANSIT_TO_FRONT
override fun onTaskAppeared(taskInfo: RunningTaskInfo, leash: SurfaceControl) {
- if (taskInfo.parentTaskId in roots) {
+ // Check whether this task is appearing inside a desk.
+ if (taskInfo.parentTaskId in deskRootsByDeskId) {
val deskId = taskInfo.parentTaskId
val taskId = taskInfo.taskId
logV("Task #$taskId appeared in desk #$deskId")
addChildToDesk(taskId = taskId, deskId = deskId)
return
}
- val deskId = taskInfo.taskId
- check(deskId !in roots) { "A root already exists for desk: $deskId" }
- val request =
- checkNotNull(deskCreateRequests.firstOrNull { it.displayId == taskInfo.displayId }) {
- "Task ${taskInfo.taskId} appeared without pending create request"
- }
- logV("Desk #$deskId appeared")
- roots[deskId] = DeskRoot(deskId, taskInfo, leash)
- deskCreateRequests.remove(request)
- request.onCreateCallback.onCreated(deskId)
+ // Check whether this task is appearing in a minimization root.
+ val minimizationRoot =
+ deskMinimizationRootsByDeskId.values.singleOrNull { it.rootId == taskInfo.parentTaskId }
+ if (minimizationRoot != null) {
+ val deskId = minimizationRoot.deskId
+ val taskId = taskInfo.taskId
+ logV("Task #$taskId was minimized in desk #$deskId ")
+ addChildToMinimizationRoot(taskId = taskId, deskId = deskId)
+ return
+ }
+ // The appearing task is a root (either a desk or a minimization root), it should not exist
+ // already.
+ check(taskInfo.taskId !in deskRootsByDeskId) {
+ "A root already exists for desk: ${taskInfo.taskId}"
+ }
+ check(deskMinimizationRootsByDeskId.values.none { it.rootId == taskInfo.taskId }) {
+ "A minimization root already exists with rootId: ${taskInfo.taskId}"
+ }
+
+ val appearingInDisplayId = taskInfo.displayId
+ // Check if there's any pending desk creation requests under this display.
+ val deskRequest =
+ createDeskRootRequests.firstOrNull { it.displayId == appearingInDisplayId }
+ if (deskRequest != null) {
+ // Appearing root matches desk request.
+ val deskId = taskInfo.taskId
+ logV("Desk #$deskId appeared")
+ deskRootsByDeskId[deskId] = DeskRoot(deskId, taskInfo, leash)
+ createDeskRootRequests.remove(deskRequest)
+ deskRequest.onCreateCallback.onCreated(deskId)
+ createDeskMinimizationRoot(displayId = appearingInDisplayId, deskId = deskId)
+ return
+ }
+ // Check if there's any pending minimization container creation requests under this display.
+ val deskMinimizationRootRequest =
+ createDeskMinimizationRootRequests.first { it.displayId == appearingInDisplayId }
+ val deskId = deskMinimizationRootRequest.deskId
+ logV("Minimization container for desk #$deskId appeared with id=${taskInfo.taskId}")
+ val deskMinimizationRoot = DeskMinimizationRoot(deskId, taskInfo, leash)
+ deskMinimizationRootsByDeskId[deskId] = deskMinimizationRoot
+ createDeskMinimizationRootRequests.remove(deskMinimizationRootRequest)
+ hideMinimizationRoot(deskMinimizationRoot)
}
override fun onTaskInfoChanged(taskInfo: RunningTaskInfo) {
- if (roots.contains(taskInfo.taskId)) {
+ if (deskRootsByDeskId.contains(taskInfo.taskId)) {
val deskId = taskInfo.taskId
- roots[deskId] = roots[deskId].copy(taskInfo = taskInfo)
+ deskRootsByDeskId[deskId] = deskRootsByDeskId[deskId].copy(taskInfo = taskInfo)
+ logV("Desk #$deskId's task info changed")
+ return
}
+ val minimizationRoot =
+ deskMinimizationRootsByDeskId.values.find { root -> root.rootId == taskInfo.taskId }
+ if (minimizationRoot != null) {
+ deskMinimizationRootsByDeskId.remove(minimizationRoot.deskId)
+ deskMinimizationRootsByDeskId[minimizationRoot.deskId] =
+ minimizationRoot.copy(taskInfo = taskInfo)
+ logV("Minimization root for desk#${minimizationRoot.deskId} task info changed")
+ return
+ }
+
+ val parentTaskId = taskInfo.parentTaskId
+ if (parentTaskId in deskRootsByDeskId) {
+ val deskId = taskInfo.parentTaskId
+ val taskId = taskInfo.taskId
+ logV("onTaskInfoChanged: Task #$taskId appeared in desk #$deskId")
+ addChildToDesk(taskId = taskId, deskId = deskId)
+ return
+ }
+ // Check whether this task is appearing in a minimization root.
+ val parentMinimizationRoot =
+ deskMinimizationRootsByDeskId.values.singleOrNull { it.rootId == parentTaskId }
+ if (parentMinimizationRoot != null) {
+ val deskId = parentMinimizationRoot.deskId
+ val taskId = taskInfo.taskId
+ logV("onTaskInfoChanged: Task #$taskId was minimized in desk #$deskId ")
+ addChildToMinimizationRoot(taskId = taskId, deskId = deskId)
+ return
+ }
+ logE("onTaskInfoChanged: unknown task: ${taskInfo.taskId}")
}
override fun onTaskVanished(taskInfo: RunningTaskInfo) {
- if (roots.contains(taskInfo.taskId)) {
+ if (deskRootsByDeskId.contains(taskInfo.taskId)) {
val deskId = taskInfo.taskId
- val deskRoot = roots[deskId]
+ val deskRoot = deskRootsByDeskId[deskId]
// Use the last saved taskInfo to obtain the displayId. Using the local one here will
// return -1 since the task is not unassociated with a display.
val displayId = deskRoot.taskInfo.displayId
logV("Desk #$deskId vanished from display #$displayId")
- roots.remove(deskId)
+ deskRootsByDeskId.remove(deskId)
+ return
+ }
+ val deskMinimizationRoot =
+ deskMinimizationRootsByDeskId.values.singleOrNull { it.rootId == taskInfo.taskId }
+ if (deskMinimizationRoot != null) {
+ logV("Minimization root for desk ${deskMinimizationRoot.deskId} vanished")
+ deskMinimizationRootsByDeskId.remove(deskMinimizationRoot.deskId)
return
}
+
+ // Check whether the vanishing task was a child of any desk.
// At this point, [parentTaskId] may be unset even if this is a task vanishing from a desk,
// so search through each root to remove this if it's a child.
- roots.forEach { deskId, deskRoot ->
+ deskRootsByDeskId.forEach { deskId, deskRoot ->
if (deskRoot.children.remove(taskInfo.taskId)) {
logV("Task #${taskInfo.taskId} vanished from desk #$deskId")
return
}
}
+ // Check whether the vanishing task was a child of the minimized root and remove it.
+ deskMinimizationRootsByDeskId.values.forEach { root ->
+ val taskId = taskInfo.taskId
+ if (root.children.remove(taskId)) {
+ logV("Task #$taskId vanished from minimization root of desk #${root.deskId}")
+ return
+ }
+ }
+ }
+
+ private fun createDeskMinimizationRoot(displayId: Int, deskId: Int) {
+ createDeskMinimizationRootRequests +=
+ CreateDeskMinimizationRootRequest(displayId = displayId, deskId = deskId)
+ shellTaskOrganizer.createRootTask(
+ displayId,
+ WINDOWING_MODE_FREEFORM,
+ /* listener = */ this,
+ /* removeWithTaskOrganizer = */ true,
+ )
+ }
+
+ @SuppressLint("MissingPermission")
+ private fun hideMinimizationRoot(root: DeskMinimizationRoot) {
+ shellTaskOrganizer.applyTransaction(
+ WindowContainerTransaction().apply { setHidden(root.token, /* hidden= */ true) }
+ )
+ }
+
+ private fun addChildToDesk(taskId: Int, deskId: Int) {
+ deskRootsByDeskId.forEach { _, deskRoot ->
+ if (deskRoot.deskId == deskId) {
+ deskRoot.children.add(taskId)
+ } else {
+ deskRoot.children.remove(taskId)
+ }
+ }
+ // A task cannot be in both a desk root and a minimization root at the same time, so make
+ // sure to remove them if needed.
+ deskMinimizationRootsByDeskId.values.forEach { root -> root.children.remove(taskId) }
+ }
+
+ private fun addChildToMinimizationRoot(taskId: Int, deskId: Int) {
+ deskMinimizationRootsByDeskId.forEach { _, minimizationRoot ->
+ if (minimizationRoot.deskId == deskId) {
+ minimizationRoot.children += taskId
+ } else {
+ minimizationRoot.children -= taskId
+ }
+ }
+ // A task cannot be in both a desk root and a minimization root at the same time, so make
+ // sure to remove them if needed.
+ deskRootsByDeskId.forEach { _, deskRoot -> deskRoot.children -= taskId }
}
@VisibleForTesting
@@ -168,34 +342,55 @@ class RootTaskDesksOrganizer(
val taskInfo: RunningTaskInfo,
val leash: SurfaceControl,
val children: MutableSet<Int> = mutableSetOf(),
+ ) {
+ val token: WindowContainerToken = taskInfo.token
+ }
+
+ @VisibleForTesting
+ data class DeskMinimizationRoot(
+ val deskId: Int,
+ val taskInfo: RunningTaskInfo,
+ val leash: SurfaceControl,
+ val children: MutableSet<Int> = mutableSetOf(),
+ ) {
+ val rootId: Int
+ get() = taskInfo.taskId
+
+ val token: WindowContainerToken = taskInfo.token
+ }
+
+ private data class CreateDeskRequest(
+ val displayId: Int,
+ val onCreateCallback: OnCreateCallback,
)
+ private data class CreateDeskMinimizationRootRequest(val displayId: Int, val deskId: Int)
+
+ private fun logV(msg: String, vararg arguments: Any?) {
+ ProtoLog.v(WM_SHELL_DESKTOP_MODE, "%s: $msg", TAG, *arguments)
+ }
+
+ private fun logE(msg: String, vararg arguments: Any?) {
+ ProtoLog.e(WM_SHELL_DESKTOP_MODE, "%s: $msg", TAG, *arguments)
+ }
+
override fun dump(pw: PrintWriter, prefix: String) {
val innerPrefix = "$prefix "
pw.println("$prefix$TAG")
pw.println("${innerPrefix}Desk Roots:")
- roots.forEach { deskId, root ->
+ deskRootsByDeskId.forEach { deskId, root ->
+ val minimizationRoot = deskMinimizationRootsByDeskId[deskId]
pw.println("$innerPrefix #$deskId visible=${root.taskInfo.isVisible}")
+ pw.println("$innerPrefix displayId=${root.taskInfo.displayId}")
pw.println("$innerPrefix children=${root.children}")
- }
- }
-
- private fun addChildToDesk(taskId: Int, deskId: Int) {
- roots.forEach { _, deskRoot ->
- if (deskRoot.deskId == deskId) {
- deskRoot.children.add(taskId)
- } else {
- deskRoot.children.remove(taskId)
+ pw.println("$innerPrefix minimization root:")
+ pw.println("$innerPrefix rootId=${minimizationRoot?.rootId}")
+ if (minimizationRoot != null) {
+ pw.println("$innerPrefix children=${minimizationRoot.children}")
}
}
}
- private data class CreateRequest(val displayId: Int, val onCreateCallback: OnCreateCallback)
-
- private fun logV(msg: String, vararg arguments: Any?) {
- ProtoLog.v(WM_SHELL_DESKTOP_MODE, "%s: $msg", TAG, *arguments)
- }
-
companion object {
private const val TAG = "RootTaskDesksOrganizer"
}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/docs/README.md b/libs/WindowManager/Shell/src/com/android/wm/shell/docs/README.md
index 3fad28ad232f..a98ae5566394 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/docs/README.md
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/docs/README.md
@@ -9,6 +9,7 @@ particular order):
4) [Threading model in the Shell](threading.md)
5) [Making changes in the Shell](changes.md)
6) [Extending the Shell for Products/OEMs](extending.md)
+6) [Shell transitions](transitions.md)
7) [Debugging in the Shell](debugging.md)
8) [Testing in the Shell](testing.md)
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/docs/transitions.md b/libs/WindowManager/Shell/src/com/android/wm/shell/docs/transitions.md
new file mode 100644
index 000000000000..dc23bb0c77d7
--- /dev/null
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/docs/transitions.md
@@ -0,0 +1,118 @@
+# Shell transitions
+[Back to home](README.md)
+
+---
+
+## General
+
+General guides for using Shell Transitions can be found here:
+- [Shell transitions animation guide](http://go/shell-transit-anim)
+- [Hitchhiker's guide to transitions](http://go/transitions-book)
+
+## Transient-launch transitions
+<span style="color:orange">Use with care!</span>
+
+Transient-launch transitions are a way to handle non-atomic (ie. gestural) transitions by allowing
+WM Core to put participating activities into a transiently visible or hidden state for the duration
+of the animation and adding the ability to cancel the transition.
+
+For example, if you are launching an activity normally, WM Core will be updated
+at the start of the animation which includes pausing the previous activity and resuming the next
+activity (and subsequently the transition will reconcile that state via an animation).
+
+If you are transiently launching an activity though, WM Core will ensure that both the leaving
+activity and the incoming activity will be RESUMED for the duration of the transition duration. In
+addition, WM Core will track the position of the transient-launch activity in the window hierarchy
+prior to the launch, and allow Shell to restore it to that position if the transitions needs to be
+canceled.
+
+Starting a transient-launch transition can be done via the activity options (since the activity may
+not have been started yet):
+```kotlin
+val opts = ActivityOptions.makeBasic().setTransientLaunch()
+val wct = WindowContainerTransaction()
+wct.sendPendingIntent(pendingIntent, new Intent(), opts.toBundle())
+transitions.startTransition(TRANSIT_OPEN, wct, ...)
+```
+
+And restoring the transient order via a WCT:
+```kotlin
+val wct = WindowContainerTransaction()
+wct.restoreTransientOrder(transientLaunchContainerToken)
+transitions.startTransition(TRANSIT_RESTORE, wct, ...)
+```
+
+### <span style="color:orange">Considerations</span>
+
+Usage of transient-launch transitions should be done with consideration, there are a few gotchas
+that might result in subtle and hard-to-reproduce issues.
+
+#### Understanding the flow
+When starting a transient-launch transition, there are several possible outcomes:
+1) The transition finishes as normal: The user is committing the transition to the state requested
+ at the start of the transition. In such cases, you can simply finish the transition and the
+ states of the transiently shown/hidden activities will be updated to match the original state
+ that a non-transient transition would have (ie. closing activities will be stopped).
+
+2) The transition is interrupted: A change in the system results in the window hierarchy changing
+ in a way which may or may not affect the transient-launch activity. eg. We transiently-launch
+ home from app A, but then app B launches. In this case, WM attempts to create a new transition
+ reflecting the window hierarchy changes (ie. if B occludes Home in the above example, then the
+ transition will have Home TO_BACK, and B TO_FRONT).
+
+ At this point, the transition handler can choose to merge the incoming transition or not (to
+ queue it after this current transition). Take note of the next section for concerns re. bookend
+ transitions.
+
+3) The transition is canceled: The user is canceling the transition to the previous state. In such
+ cases, you need to store the `WindowContainerToken` for the task associated with the
+ transient-launch activity, and restore the transient order via the `WindowContainerTransaction`
+ API above. In some cases, if anything has been reordered since (ie. due to other merged
+ transitions), then you may also want to use `WindowContainerTransaction#reorder()` to place all
+ the relevant containers to their original order (provided via the change-order in the initial
+ launch transition).
+
+#### Finishing the transient-launch transition
+
+When restoring the transient order in the 3rd flow above, it is recommended to do it in a new
+transition and <span style="color:orange">**not**</span> via the WindowContainerTransaction in
+`TransitionFinishCallback#onTransitionFinished()` provided when starting the transition.
+
+Changes to the window hierarchy via the finish transaction are not applied in sync with other
+transitions that are collecting and aplying, and are also not observable in Shell in any way.
+Starting a new transition instead ensures both. (The finish transaction can still be used if there
+are non-transition affecting properties (ie. container properties) that need to be updated as a part
+of finishing the transient-launch transition).
+
+So the general idea is when restoring is:
+
+1) Start transient-launch transition START_T
+2) ...
+3) All done, start bookend transition END_T
+4) Handler receives END_T, merges it and then finishes START_T
+
+In practice it's not quite that simple, due to the ordering of transitions and extra care must be
+taken when using a new transition to prevent deadlocking when merging transitions.
+
+When a new transition arrives while a transient-launch transition is playing, the handler can
+choose to handle/merge the transition into the ongoing one, or skip merging to queue it up to be
+played after. In the above flow, we can see how this might result in a deadlock:
+
+Queueing END during merge:
+1) Start transient-launch transition START_T
+2) ...
+3) Incoming transition OTHER_T, choose to cancel START_T -> start bookend transition END_T, but don't merge OTHER_T
+3) Waiting for END_T... <span style="color:red">Deadlock!</span>
+
+Interrupt while pending END:
+1) Start transient-launch transition START_T
+2) ...
+3) All done, start bookend transition END_T
+3) Incoming transition OTHER_T occurs before END_T, but don't merge OTHER_T
+3) Waiting for END_T... <span style="color:red">Deadlock!</span>
+
+This means that when using transient-launch transitions with a bookend transition
+<span style="color:orange">requires</span> you to handle any incoming transitions if the bookend is
+ever queued (or already posted) after it. You can do so by preempting the bookend transition
+(finishing the transient-launch transition), or handling the merge of the new transition (so it
+doesn't queue). \ No newline at end of file
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/freeform/FreeformTaskTransitionHandler.java b/libs/WindowManager/Shell/src/com/android/wm/shell/freeform/FreeformTaskTransitionHandler.java
index b60fb5e7bfdd..16e411e1fc07 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/freeform/FreeformTaskTransitionHandler.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/freeform/FreeformTaskTransitionHandler.java
@@ -25,10 +25,12 @@ import android.animation.AnimatorListenerAdapter;
import android.animation.ValueAnimator;
import android.app.ActivityManager;
import android.app.WindowConfiguration;
+import android.content.Context;
import android.graphics.Rect;
+import android.os.Handler;
import android.os.IBinder;
import android.util.ArrayMap;
-import android.util.DisplayMetrics;
+import android.util.Log;
import android.view.SurfaceControl;
import android.view.WindowManager;
import android.window.TransitionInfo;
@@ -38,6 +40,7 @@ import android.window.WindowContainerTransaction;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
+import com.android.internal.jank.InteractionJankMonitor;
import com.android.wm.shell.common.DisplayController;
import com.android.wm.shell.common.ShellExecutor;
import com.android.wm.shell.shared.animation.MinimizeAnimator;
@@ -52,11 +55,13 @@ import java.util.List;
*/
public class FreeformTaskTransitionHandler
implements Transitions.TransitionHandler, FreeformTaskTransitionStarter {
+ private static final String TAG = "FreeformTaskTransitionHandler";
private static final int CLOSE_ANIM_DURATION = 400;
private final Transitions mTransitions;
private final DisplayController mDisplayController;
private final ShellExecutor mMainExecutor;
private final ShellExecutor mAnimExecutor;
+ private final Handler mAnimHandler;
private final List<IBinder> mPendingTransitionTokens = new ArrayList<>();
@@ -66,11 +71,13 @@ public class FreeformTaskTransitionHandler
Transitions transitions,
DisplayController displayController,
ShellExecutor mainExecutor,
- ShellExecutor animExecutor) {
+ ShellExecutor animExecutor,
+ Handler animHandler) {
mTransitions = transitions;
mDisplayController = displayController;
mMainExecutor = mainExecutor;
mAnimExecutor = animExecutor;
+ mAnimHandler = animHandler;
}
@Override
@@ -123,13 +130,11 @@ public class FreeformTaskTransitionHandler
@NonNull Transitions.TransitionFinishCallback finishCallback) {
boolean transitionHandled = false;
final ArrayList<Animator> animations = new ArrayList<>();
- final Runnable onAnimFinish = () -> {
+ final Runnable onAnimFinish = () -> mMainExecutor.execute(() -> {
if (!animations.isEmpty()) return;
- mMainExecutor.execute(() -> {
- mAnimations.remove(transition);
- finishCallback.onTransitionFinished(null /* wct */);
- });
- };
+ mAnimations.remove(transition);
+ finishCallback.onTransitionFinished(null /* wct */);
+ });
for (TransitionInfo.Change change : info.getChanges()) {
if ((change.getFlags() & TransitionInfo.FLAG_IS_WALLPAPER) != 0) {
continue;
@@ -234,18 +239,25 @@ public class FreeformTaskTransitionHandler
SurfaceControl.Transaction t = new SurfaceControl.Transaction();
SurfaceControl sc = change.getLeash();
finishT.hide(sc);
- final DisplayMetrics displayMetrics =
- mDisplayController
- .getDisplayContext(taskInfo.displayId).getResources().getDisplayMetrics();
+ final Context displayContext =
+ mDisplayController.getDisplayContext(taskInfo.displayId);
+ if (displayContext == null) {
+ Log.w(TAG, "No displayContext for displayId=" + taskInfo.displayId);
+ return false;
+ }
final Animator animator = MinimizeAnimator.create(
- displayMetrics,
+ displayContext,
change,
t,
(anim) -> {
- animations.remove(anim);
- onAnimFinish.run();
+ mMainExecutor.execute(() -> {
+ animations.remove(anim);
+ onAnimFinish.run();
+ });
return null;
- });
+ },
+ InteractionJankMonitor.getInstance(),
+ mAnimHandler);
animations.add(animator);
return true;
}
@@ -277,8 +289,10 @@ public class FreeformTaskTransitionHandler
new AnimatorListenerAdapter() {
@Override
public void onAnimationEnd(Animator animation) {
- animations.remove(animator);
- onAnimFinish.run();
+ mMainExecutor.execute(() -> {
+ animations.remove(animator);
+ onAnimFinish.run();
+ });
}
});
animations.add(animator);
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip2/phone/PipController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip2/phone/PipController.java
index 99c9302edb75..1ce24f76ada5 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip2/phone/PipController.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip2/phone/PipController.java
@@ -404,6 +404,10 @@ public class PipController implements ConfigurationChangeListener,
mPipBoundsState.setBoundsStateForEntry(componentName, activityInfo, pictureInPictureParams,
mPipBoundsAlgorithm);
+
+ // Update the size spec in case aspect ratio is invariant, but display has changed
+ // since the last PiP session, or this is the first PiP session altogether.
+ mPipBoundsState.updateMinMaxSize(mPipBoundsState.getAspectRatio());
return mPipBoundsAlgorithm.getEntryDestinationBounds();
}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip2/phone/PipTransition.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip2/phone/PipTransition.java
index 035c93db7ee4..97b3e5a2da87 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip2/phone/PipTransition.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip2/phone/PipTransition.java
@@ -738,6 +738,13 @@ public class PipTransition extends PipTransitionController implements
}
}
+ if (!mPipTransitionState.isInSwipePipToHomeTransition()) {
+ // Update the size spec in case aspect ratio is invariant, but display has changed
+ // since the last PiP session, or this is the first PiP session altogether.
+ // Skip the update if in swipe PiP to home, as this has already been done.
+ mPipBoundsState.updateMinMaxSize(mPipBoundsState.getAspectRatio());
+ }
+
// calculate the entry bounds and notify core to move task to pinned with final bounds
final Rect entryBounds = mPipBoundsAlgorithm.getEntryDestinationBounds();
mPipBoundsState.setBounds(entryBounds);
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/recents/RecentsTransitionHandler.java b/libs/WindowManager/Shell/src/com/android/wm/shell/recents/RecentsTransitionHandler.java
index 847a0383e7d0..3e03e001c49b 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/recents/RecentsTransitionHandler.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/recents/RecentsTransitionHandler.java
@@ -47,6 +47,7 @@ import android.app.ActivityManager;
import android.app.ActivityTaskManager;
import android.app.IApplicationThread;
import android.app.PendingIntent;
+import android.app.WindowConfiguration;
import android.content.Context;
import android.content.Intent;
import android.graphics.Color;
@@ -75,11 +76,11 @@ import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.os.IResultReceiver;
import com.android.internal.protolog.ProtoLog;
import com.android.wm.shell.Flags;
-import com.android.wm.shell.R;
import com.android.wm.shell.ShellTaskOrganizer;
import com.android.wm.shell.common.ShellExecutor;
import com.android.wm.shell.common.pip.PipUtils;
import com.android.wm.shell.protolog.ShellProtoLogGroup;
+import com.android.wm.shell.shared.R;
import com.android.wm.shell.shared.TransitionUtil;
import com.android.wm.shell.sysui.ShellInit;
import com.android.wm.shell.transition.HomeTransitionObserver;
@@ -320,7 +321,7 @@ public class RecentsTransitionHandler implements Transitions.TransitionHandler,
"RecentsTransitionHandler.mergeAnimation: no controller found");
return;
}
- controller.merge(info, startT, finishT, mergeTarget, finishCallback);
+ controller.merge(info, startT, finishT, finishCallback);
}
@Override
@@ -408,7 +409,8 @@ public class RecentsTransitionHandler implements Transitions.TransitionHandler,
// next called.
private Pair<int[], TaskSnapshot[]> mPendingPauseSnapshotsForCancel;
- // Used to track a pending finish transition
+ // Used to track a pending finish transition, this is only non-null if
+ // enableRecentsBookendTransition() is enabled
private IBinder mPendingFinishTransition;
private IResultReceiver mPendingRunnerFinishCb;
@@ -917,7 +919,7 @@ public class RecentsTransitionHandler implements Transitions.TransitionHandler,
*/
@SuppressLint("NewApi")
void merge(TransitionInfo info, SurfaceControl.Transaction startT,
- SurfaceControl.Transaction finishT, IBinder mergeTarget,
+ SurfaceControl.Transaction finishT,
Transitions.TransitionFinishCallback finishCallback) {
if (mFinishCB == null) {
ProtoLog.v(ShellProtoLogGroup.WM_SHELL_RECENTS_TRANSITION,
@@ -927,16 +929,25 @@ public class RecentsTransitionHandler implements Transitions.TransitionHandler,
return;
}
- if (Flags.enableRecentsBookendTransition()
- && info.getType() == TRANSIT_END_RECENTS_TRANSITION
- && mergeTarget == mTransition) {
- // This is a pending finish, so merge the end transition to trigger completing the
- // cleanup of the recents transition
- ProtoLog.v(ShellProtoLogGroup.WM_SHELL_RECENTS_TRANSITION,
- "[%d] RecentsController.merge: TRANSIT_END_RECENTS_TRANSITION",
- mInstanceId);
- finishCallback.onTransitionFinished(null /* wct */);
- return;
+ if (Flags.enableRecentsBookendTransition()) {
+ if (info.getType() == TRANSIT_END_RECENTS_TRANSITION) {
+ // This is a pending finish, so merge the end transition to trigger completing
+ // the cleanup of the recents transition
+ ProtoLog.v(ShellProtoLogGroup.WM_SHELL_RECENTS_TRANSITION,
+ "[%d] RecentsController.merge: TRANSIT_END_RECENTS_TRANSITION",
+ mInstanceId);
+ consumeMerge(info, startT, finishT, finishCallback);
+ return;
+ } else if (mPendingFinishTransition != null) {
+ // This transition is interrupting a pending finish that was already sent, so
+ // pre-empt the pending finish transition since the state has already changed
+ // in the core
+ ProtoLog.v(ShellProtoLogGroup.WM_SHELL_RECENTS_TRANSITION,
+ "[%d] RecentsController.merge: Awaiting TRANSIT_END_RECENTS_TRANSITION",
+ mInstanceId);
+ onFinishInner(null /* wct */);
+ return;
+ }
}
if (info.getType() == TRANSIT_SLEEP) {
@@ -1210,16 +1221,12 @@ public class RecentsTransitionHandler implements Transitions.TransitionHandler,
}
return;
}
+
// At this point, we are accepting the merge.
- startT.apply();
- // Since we're accepting the merge, update the finish transaction so that changes via
- // that transaction will be applied on top of those of the merged transitions
- mFinishTransaction = finishT;
+ consumeMerge(info, startT, finishT, finishCallback);
+
+ // Notify Launcher of the new opening tasks if necessary
boolean passTransitionInfo = ENABLE_DESKTOP_RECENTS_TRANSITIONS_CORNERS_BUGFIX.isTrue();
- if (!passTransitionInfo) {
- // not using the incoming anim-only surfaces
- info.releaseAnimSurfaces();
- }
if (appearedTargets != null) {
try {
ProtoLog.v(ShellProtoLogGroup.WM_SHELL_RECENTS_TRANSITION,
@@ -1229,6 +1236,27 @@ public class RecentsTransitionHandler implements Transitions.TransitionHandler,
Slog.e(TAG, "Error sending appeared tasks to recents animation", e);
}
}
+ }
+
+ /**
+ * Consumes the merge of the other given transition.
+ */
+ private void consumeMerge(TransitionInfo info, SurfaceControl.Transaction startT,
+ SurfaceControl.Transaction finishT,
+ Transitions.TransitionFinishCallback finishCallback) {
+ ProtoLog.v(ShellProtoLogGroup.WM_SHELL_RECENTS_TRANSITION,
+ "[%d] RecentsController.merge: consuming merge",
+ mInstanceId);
+
+ startT.apply();
+ // Since we're accepting the merge, update the finish transaction so that changes via
+ // that transaction will be applied on top of those of the merged transitions
+ mFinishTransaction = finishT;
+ boolean passTransitionInfo = ENABLE_DESKTOP_RECENTS_TRANSITIONS_CORNERS_BUGFIX.isTrue();
+ if (!passTransitionInfo) {
+ // not using the incoming anim-only surfaces
+ info.releaseAnimSurfaces();
+ }
finishCallback.onTransitionFinished(null /* wct */);
}
@@ -1346,9 +1374,16 @@ public class RecentsTransitionHandler implements Transitions.TransitionHandler,
final SurfaceControl.Transaction t = mFinishTransaction;
final WindowContainerTransaction wct = new WindowContainerTransaction();
+ // The following code must set this if it is changing anything in core that might affect
+ // transitions as a part of finishing the recents transition
+ boolean requiresBookendTransition = false;
+
if (mKeyguardLocked && mRecentsTask != null) {
if (toHome) wct.reorder(mRecentsTask, true /* toTop */);
else wct.restoreTransientOrder(mRecentsTask);
+ // We are manipulating the window hierarchy, which should only be done with the
+ // bookend transition
+ requiresBookendTransition = true;
}
if (returningToApp) {
ProtoLog.v(ShellProtoLogGroup.WM_SHELL_RECENTS_TRANSITION, " returning to app");
@@ -1365,6 +1400,9 @@ public class RecentsTransitionHandler implements Transitions.TransitionHandler,
if (!mKeyguardLocked && mRecentsTask != null) {
wct.restoreTransientOrder(mRecentsTask);
}
+ // We are manipulating the window hierarchy, which should only be done with the
+ // bookend transition
+ requiresBookendTransition = true;
} else if (toHome && mOpeningSeparateHome && mPausingTasks != null) {
ProtoLog.v(ShellProtoLogGroup.WM_SHELL_RECENTS_TRANSITION, " 3p launching home");
// Special situation where 3p launcher was changed during recents (this happens
@@ -1384,6 +1422,9 @@ public class RecentsTransitionHandler implements Transitions.TransitionHandler,
if (!mKeyguardLocked && mRecentsTask != null) {
wct.restoreTransientOrder(mRecentsTask);
}
+ // We are manipulating the window hierarchy, which should only be done with the
+ // bookend transition
+ requiresBookendTransition = true;
} else {
if (mPausingSeparateHome) {
if (mOpeningTasks.isEmpty()) {
@@ -1484,13 +1525,21 @@ public class RecentsTransitionHandler implements Transitions.TransitionHandler,
if (Flags.enableRecentsBookendTransition()) {
if (!wct.isEmpty()) {
- ProtoLog.v(ShellProtoLogGroup.WM_SHELL_RECENTS_TRANSITION,
- "[%d] RecentsController.finishInner: "
- + "Queuing TRANSIT_END_RECENTS_TRANSITION", mInstanceId);
- mPendingRunnerFinishCb = runnerFinishCb;
- mPendingFinishTransition = mTransitions.startTransition(
- TRANSIT_END_RECENTS_TRANSITION, wct,
- new PendingFinishTransitionHandler());
+ if (requiresBookendTransition) {
+ ProtoLog.v(ShellProtoLogGroup.WM_SHELL_RECENTS_TRANSITION,
+ "[%d] RecentsController.finishInner: "
+ + "Queuing TRANSIT_END_RECENTS_TRANSITION", mInstanceId);
+ mPendingRunnerFinishCb = runnerFinishCb;
+ mPendingFinishTransition = mTransitions.startTransition(
+ TRANSIT_END_RECENTS_TRANSITION, wct,
+ new PendingFinishTransitionHandler());
+ } else {
+ ProtoLog.v(ShellProtoLogGroup.WM_SHELL_RECENTS_TRANSITION,
+ "[%d] RecentsController.finishInner: Non-transition affecting wct",
+ mInstanceId);
+ mPendingRunnerFinishCb = runnerFinishCb;
+ onFinishInner(wct);
+ }
} else {
// If there's no work to do, just go ahead and clean up
mPendingRunnerFinishCb = runnerFinishCb;
@@ -1631,6 +1680,9 @@ public class RecentsTransitionHandler implements Transitions.TransitionHandler,
@NonNull SurfaceControl.Transaction startTransaction,
@NonNull SurfaceControl.Transaction finishTransaction,
@NonNull Transitions.TransitionFinishCallback finishCallback) {
+ ProtoLog.v(ShellProtoLogGroup.WM_SHELL_RECENTS_TRANSITION,
+ "[%d] PendingFinishTransitionHandler.startAnimation: "
+ + "Started pending finish transition", mInstanceId);
return false;
}
@@ -1644,10 +1696,15 @@ public class RecentsTransitionHandler implements Transitions.TransitionHandler,
@Override
public void onTransitionConsumed(@NonNull IBinder transition, boolean aborted,
@Nullable SurfaceControl.Transaction finishTransaction) {
+ if (mPendingFinishTransition == null) {
+ // The cleanup was pre-empted by an earlier transition, nothing there is nothing
+ // to do here
+ return;
+ }
// Once we have merged (or not if the WCT didn't result in any changes), then we can
// run the pending finish logic
ProtoLog.v(ShellProtoLogGroup.WM_SHELL_RECENTS_TRANSITION,
- "[%d] RecentsController.onTransitionConsumed: "
+ "[%d] PendingFinishTransitionHandler.onTransitionConsumed: "
+ "Consumed pending finish transition", mInstanceId);
onFinishInner(null /* wct */);
}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/transition/DefaultMixedHandler.java b/libs/WindowManager/Shell/src/com/android/wm/shell/transition/DefaultMixedHandler.java
index 743bd052995e..347dcff86529 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/transition/DefaultMixedHandler.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/transition/DefaultMixedHandler.java
@@ -40,7 +40,6 @@ import android.window.WindowContainerToken;
import android.window.WindowContainerTransaction;
import com.android.internal.protolog.ProtoLog;
-import com.android.window.flags.Flags;
import com.android.wm.shell.ShellTaskOrganizer;
import com.android.wm.shell.activityembedding.ActivityEmbeddingController;
import com.android.wm.shell.common.ComponentUtils;
@@ -439,9 +438,6 @@ public class DefaultMixedHandler implements MixedTransitionHandler,
for (int i = 0; i < info.getRootCount(); ++i) {
out.addRoot(info.getRoot(i));
}
- if (!Flags.moveAnimationOptionsToChange()) {
- out.setAnimationOptions(info.getAnimationOptions());
- }
return out;
}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/transition/DefaultTransitionHandler.java b/libs/WindowManager/Shell/src/com/android/wm/shell/transition/DefaultTransitionHandler.java
index 01428e60582e..e9c6adec75d7 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/transition/DefaultTransitionHandler.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/transition/DefaultTransitionHandler.java
@@ -539,7 +539,7 @@ public class DefaultTransitionHandler implements Transitions.TransitionHandler {
cornerRadius = 0;
}
- backgroundColorForTransition = getTransitionBackgroundColorIfSet(info, change, a,
+ backgroundColorForTransition = getTransitionBackgroundColorIfSet(change, a,
backgroundColorForTransition);
if (!isTask && a.getExtensionEdges() != 0x0) {
@@ -606,12 +606,7 @@ public class DefaultTransitionHandler implements Transitions.TransitionHandler {
mTransactionPool, mMainExecutor, animRelOffset, cornerRadius,
clipRect);
- final TransitionInfo.AnimationOptions options;
- if (Flags.moveAnimationOptionsToChange()) {
- options = change.getAnimationOptions();
- } else {
- options = info.getAnimationOptions();
- }
+ final TransitionInfo.AnimationOptions options = change.getAnimationOptions();
if (options != null) {
attachThumbnail(animations, onAnimFinish, change, options, cornerRadius);
}
@@ -834,12 +829,7 @@ public class DefaultTransitionHandler implements Transitions.TransitionHandler {
final boolean isOpeningType = TransitionUtil.isOpeningType(type);
final boolean enter = TransitionUtil.isOpeningType(changeMode);
final boolean isTask = change.getTaskInfo() != null;
- final TransitionInfo.AnimationOptions options;
- if (Flags.moveAnimationOptionsToChange()) {
- options = change.getAnimationOptions();
- } else {
- options = info.getAnimationOptions();
- }
+ final TransitionInfo.AnimationOptions options = change.getAnimationOptions();
final int overrideType = options != null ? options.getType() : ANIM_NONE;
final int userId = options != null ? options.getUserId() : UserHandle.USER_CURRENT;
final Rect endBounds = TransitionUtil.isClosingType(changeMode)
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/transition/HomeTransitionObserver.java b/libs/WindowManager/Shell/src/com/android/wm/shell/transition/HomeTransitionObserver.java
index 1917996d48fb..938885cc1684 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/transition/HomeTransitionObserver.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/transition/HomeTransitionObserver.java
@@ -21,6 +21,7 @@ import static android.view.Display.DEFAULT_DISPLAY;
import static android.window.TransitionInfo.FLAG_BACK_GESTURE_ANIMATED;
import static com.android.wm.shell.desktopmode.DesktopModeTransitionTypes.TRANSIT_DESKTOP_MODE_START_DRAG_TO_DESKTOP;
+import static com.android.wm.shell.transition.Transitions.TRANSIT_CONVERT_TO_BUBBLE;
import static com.android.wm.shell.transition.Transitions.TransitionObserver;
import android.annotation.NonNull;
@@ -35,6 +36,7 @@ import com.android.wm.shell.common.ShellExecutor;
import com.android.wm.shell.common.SingleInstanceRemoteListener;
import com.android.wm.shell.shared.IHomeTransitionListener;
import com.android.wm.shell.shared.TransitionUtil;
+import com.android.wm.shell.shared.bubbles.BubbleAnythingFlagHelper;
/**
* The {@link TransitionObserver} that observes for transitions involving the home
@@ -48,6 +50,8 @@ public class HomeTransitionObserver implements TransitionObserver,
private @NonNull final Context mContext;
private @NonNull final ShellExecutor mMainExecutor;
+ private Boolean mPendingHomeVisibilityUpdate;
+
public HomeTransitionObserver(@NonNull Context context,
@NonNull ShellExecutor mainExecutor) {
mContext = context;
@@ -59,28 +63,78 @@ public class HomeTransitionObserver implements TransitionObserver,
@NonNull TransitionInfo info,
@NonNull SurfaceControl.Transaction startTransaction,
@NonNull SurfaceControl.Transaction finishTransaction) {
+ if (BubbleAnythingFlagHelper.enableBubbleToFullscreen()) {
+ handleTransitionReadyWithBubbleAnything(info);
+ } else {
+ handleTransitionReady(info);
+ }
+ }
+
+ private void handleTransitionReady(@NonNull TransitionInfo info) {
for (TransitionInfo.Change change : info.getChanges()) {
final ActivityManager.RunningTaskInfo taskInfo = change.getTaskInfo();
- if (info.getType() == TRANSIT_DESKTOP_MODE_START_DRAG_TO_DESKTOP
- || taskInfo == null
+ if (taskInfo == null
+ || info.getType() == TRANSIT_DESKTOP_MODE_START_DRAG_TO_DESKTOP
|| taskInfo.displayId != DEFAULT_DISPLAY
|| taskInfo.taskId == -1
|| !taskInfo.isRunning) {
continue;
}
+ Boolean homeVisibilityUpdate = getHomeVisibilityUpdate(info, change, taskInfo);
+ if (homeVisibilityUpdate != null) {
+ notifyHomeVisibilityChanged(homeVisibilityUpdate);
+ }
+ }
+ }
+
+ private void handleTransitionReadyWithBubbleAnything(@NonNull TransitionInfo info) {
+ Boolean homeVisibilityUpdate = null;
+ for (TransitionInfo.Change change : info.getChanges()) {
+ final ActivityManager.RunningTaskInfo taskInfo = change.getTaskInfo();
+ if (taskInfo == null
+ || taskInfo.displayId != DEFAULT_DISPLAY
+ || taskInfo.taskId == -1
+ || !taskInfo.isRunning) {
+ continue;
+ }
+
+ Boolean update = getHomeVisibilityUpdate(info, change, taskInfo);
+ if (update != null) {
+ homeVisibilityUpdate = update;
+ }
+ }
+
+ if (info.getType() == TRANSIT_DESKTOP_MODE_START_DRAG_TO_DESKTOP) {
+ // Do not apply at the start of desktop drag as that updates launcher UI visibility.
+ // Store the value and apply with a next transition if needed.
+ mPendingHomeVisibilityUpdate = homeVisibilityUpdate;
+ return;
+ }
+
+ if (info.getType() == TRANSIT_CONVERT_TO_BUBBLE && homeVisibilityUpdate == null) {
+ // We are converting to bubble and we did not get a change to home visibility in this
+ // transition. Apply the value from start of drag.
+ homeVisibilityUpdate = mPendingHomeVisibilityUpdate;
+ }
+ if (homeVisibilityUpdate != null) {
+ mPendingHomeVisibilityUpdate = null;
+ notifyHomeVisibilityChanged(homeVisibilityUpdate);
+ }
+ }
- final int mode = change.getMode();
- final boolean isBackGesture = change.hasFlags(FLAG_BACK_GESTURE_ANIMATED);
- if (taskInfo.getActivityType() == ACTIVITY_TYPE_HOME) {
- final boolean gestureToHomeTransition = isBackGesture
- && TransitionUtil.isClosingType(info.getType());
- if (gestureToHomeTransition || TransitionUtil.isClosingMode(mode)
- || (!isBackGesture && TransitionUtil.isOpeningMode(mode))) {
- notifyHomeVisibilityChanged(gestureToHomeTransition
- || TransitionUtil.isOpeningType(mode));
- }
+ private Boolean getHomeVisibilityUpdate(TransitionInfo info,
+ TransitionInfo.Change change, ActivityManager.RunningTaskInfo taskInfo) {
+ final int mode = change.getMode();
+ final boolean isBackGesture = change.hasFlags(FLAG_BACK_GESTURE_ANIMATED);
+ if (taskInfo.getActivityType() == ACTIVITY_TYPE_HOME) {
+ final boolean gestureToHomeTransition = isBackGesture
+ && TransitionUtil.isClosingType(info.getType());
+ if (gestureToHomeTransition || TransitionUtil.isClosingMode(mode)
+ || (!isBackGesture && TransitionUtil.isOpeningMode(mode))) {
+ return gestureToHomeTransition || TransitionUtil.isOpeningType(mode);
}
}
+ return null;
}
@Override
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/transition/TransitionAnimationHelper.java b/libs/WindowManager/Shell/src/com/android/wm/shell/transition/TransitionAnimationHelper.java
index 4feb4753096e..7984bcedc4e5 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/transition/TransitionAnimationHelper.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/transition/TransitionAnimationHelper.java
@@ -58,7 +58,6 @@ import android.window.TransitionInfo;
import com.android.internal.R;
import com.android.internal.policy.TransitionAnimation;
import com.android.internal.protolog.ProtoLog;
-import com.android.window.flags.Flags;
import com.android.wm.shell.protolog.ShellProtoLogGroup;
import com.android.wm.shell.shared.TransitionUtil;
@@ -78,12 +77,7 @@ public class TransitionAnimationHelper {
final boolean isFreeform = isTask && change.getTaskInfo().isFreeform();
final boolean isCoveredByOpaqueFullscreenChange =
isCoveredByOpaqueFullscreenChange(info, change);
- final TransitionInfo.AnimationOptions options;
- if (Flags.moveAnimationOptionsToChange()) {
- options = change.getAnimationOptions();
- } else {
- options = info.getAnimationOptions();
- }
+ final TransitionInfo.AnimationOptions options = change.getAnimationOptions();
final int overrideType = options != null ? options.getType() : ANIM_NONE;
int animAttr = 0;
boolean translucent = false;
@@ -279,16 +273,10 @@ public class TransitionAnimationHelper {
* the given transition animation.
*/
@ColorInt
- public static int getTransitionBackgroundColorIfSet(@NonNull TransitionInfo info,
- @NonNull TransitionInfo.Change change, @NonNull Animation a,
- @ColorInt int defaultColor) {
+ public static int getTransitionBackgroundColorIfSet(@NonNull TransitionInfo.Change change,
+ @NonNull Animation a, @ColorInt int defaultColor) {
if (!a.getShowBackdrop()) {
return defaultColor;
- }
- if (!Flags.moveAnimationOptionsToChange() && info.getAnimationOptions() != null
- && info.getAnimationOptions().getBackgroundColor() != 0) {
- // If available use the background color provided through AnimationOptions
- return info.getAnimationOptions().getBackgroundColor();
} else if (a.getBackdropColor() != 0) {
// Otherwise fallback on the background color provided through the animation
// definition.
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/DesktopModeWindowDecorViewModel.java b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/DesktopModeWindowDecorViewModel.java
index 5a6ea214e561..cf139a008164 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/DesktopModeWindowDecorViewModel.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/DesktopModeWindowDecorViewModel.java
@@ -103,6 +103,7 @@ import com.android.wm.shell.common.DisplayChangeController;
import com.android.wm.shell.common.DisplayController;
import com.android.wm.shell.common.DisplayInsetsController;
import com.android.wm.shell.common.DisplayLayout;
+import com.android.wm.shell.common.MultiDisplayDragMoveIndicatorController;
import com.android.wm.shell.common.MultiInstanceHelper;
import com.android.wm.shell.common.ShellExecutor;
import com.android.wm.shell.common.SyncTransactionQueue;
@@ -258,6 +259,7 @@ public class DesktopModeWindowDecorViewModel implements WindowDecorViewModel,
private final RecentsTransitionHandler mRecentsTransitionHandler;
private final DesktopModeCompatPolicy mDesktopModeCompatPolicy;
private final DesktopTilingDecorViewModel mDesktopTilingDecorViewModel;
+ private final MultiDisplayDragMoveIndicatorController mMultiDisplayDragMoveIndicatorController;
public DesktopModeWindowDecorViewModel(
Context context,
@@ -296,7 +298,8 @@ public class DesktopModeWindowDecorViewModel implements WindowDecorViewModel,
WindowDecorTaskResourceLoader taskResourceLoader,
RecentsTransitionHandler recentsTransitionHandler,
DesktopModeCompatPolicy desktopModeCompatPolicy,
- DesktopTilingDecorViewModel desktopTilingDecorViewModel) {
+ DesktopTilingDecorViewModel desktopTilingDecorViewModel,
+ MultiDisplayDragMoveIndicatorController multiDisplayDragMoveIndicatorController) {
this(
context,
shellExecutor,
@@ -340,7 +343,8 @@ public class DesktopModeWindowDecorViewModel implements WindowDecorViewModel,
taskResourceLoader,
recentsTransitionHandler,
desktopModeCompatPolicy,
- desktopTilingDecorViewModel);
+ desktopTilingDecorViewModel,
+ multiDisplayDragMoveIndicatorController);
}
@VisibleForTesting
@@ -387,7 +391,8 @@ public class DesktopModeWindowDecorViewModel implements WindowDecorViewModel,
WindowDecorTaskResourceLoader taskResourceLoader,
RecentsTransitionHandler recentsTransitionHandler,
DesktopModeCompatPolicy desktopModeCompatPolicy,
- DesktopTilingDecorViewModel desktopTilingDecorViewModel) {
+ DesktopTilingDecorViewModel desktopTilingDecorViewModel,
+ MultiDisplayDragMoveIndicatorController multiDisplayDragMoveIndicatorController) {
mContext = context;
mMainExecutor = shellExecutor;
mMainHandler = mainHandler;
@@ -460,6 +465,7 @@ public class DesktopModeWindowDecorViewModel implements WindowDecorViewModel,
mDesktopModeCompatPolicy = desktopModeCompatPolicy;
mDesktopTilingDecorViewModel = desktopTilingDecorViewModel;
mDesktopTasksController.setSnapEventHandler(this);
+ mMultiDisplayDragMoveIndicatorController = multiDisplayDragMoveIndicatorController;
shellInit.addInitCallback(this::onInit, this);
}
@@ -1759,7 +1765,8 @@ public class DesktopModeWindowDecorViewModel implements WindowDecorViewModel,
mTransitions,
mInteractionJankMonitor,
mTransactionFactory,
- mMainHandler);
+ mMainHandler,
+ mMultiDisplayDragMoveIndicatorController);
windowDecoration.setTaskDragResizer(taskPositioner);
final DesktopModeTouchEventListener touchEventListener =
@@ -2056,7 +2063,8 @@ public class DesktopModeWindowDecorViewModel implements WindowDecorViewModel,
Transitions transitions,
InteractionJankMonitor interactionJankMonitor,
Supplier<SurfaceControl.Transaction> transactionFactory,
- Handler handler) {
+ Handler handler,
+ MultiDisplayDragMoveIndicatorController multiDisplayDragMoveIndicatorController) {
final TaskPositioner taskPositioner = DesktopModeStatus.isVeiledResizeEnabled()
// TODO(b/383632995): Update when the flag is launched.
? (Flags.enableConnectedDisplaysWindowDrag()
@@ -2067,7 +2075,8 @@ public class DesktopModeWindowDecorViewModel implements WindowDecorViewModel,
dragEventListener,
transitions,
interactionJankMonitor,
- handler)
+ handler,
+ multiDisplayDragMoveIndicatorController)
: new VeiledResizeTaskPositioner(
taskOrganizer,
windowDecoration,
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/DesktopModeWindowDecoration.java b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/DesktopModeWindowDecoration.java
index dca376f7df0e..6165dbf686fd 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/DesktopModeWindowDecoration.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/DesktopModeWindowDecoration.java
@@ -1069,7 +1069,8 @@ public class DesktopModeWindowDecoration extends WindowDecoration<WindowDecorLin
private static int getCornerRadius(@NonNull Context context, int layoutResId) {
if (layoutResId == R.layout.desktop_mode_app_header) {
return loadDimensionPixelSize(context.getResources(),
- R.dimen.desktop_windowing_freeform_rounded_corner_radius);
+ com.android.wm.shell.shared.R.dimen
+ .desktop_windowing_freeform_rounded_corner_radius);
}
return INVALID_CORNER_RADIUS;
}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/MultiDisplayVeiledResizeTaskPositioner.kt b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/MultiDisplayVeiledResizeTaskPositioner.kt
index bb20292a51d4..c6cb62d153ac 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/MultiDisplayVeiledResizeTaskPositioner.kt
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/MultiDisplayVeiledResizeTaskPositioner.kt
@@ -17,6 +17,7 @@ package com.android.wm.shell.windowdecor
import android.graphics.PointF
import android.graphics.Rect
+import android.hardware.display.DisplayTopology
import android.os.Handler
import android.os.IBinder
import android.os.Looper
@@ -32,10 +33,10 @@ import com.android.internal.jank.InteractionJankMonitor
import com.android.wm.shell.ShellTaskOrganizer
import com.android.wm.shell.common.DisplayController
import com.android.wm.shell.common.MultiDisplayDragMoveBoundsCalculator
+import com.android.wm.shell.common.MultiDisplayDragMoveIndicatorController
import com.android.wm.shell.shared.annotations.ShellMainThread
import com.android.wm.shell.transition.Transitions
import java.util.concurrent.TimeUnit
-import java.util.function.Supplier
/**
* A task positioner that also takes into account resizing a
@@ -49,11 +50,12 @@ class MultiDisplayVeiledResizeTaskPositioner(
private val desktopWindowDecoration: DesktopModeWindowDecoration,
private val displayController: DisplayController,
dragEventListener: DragPositioningCallbackUtility.DragEventListener,
- private val transactionSupplier: Supplier<SurfaceControl.Transaction>,
+ private val transactionSupplier: () -> SurfaceControl.Transaction,
private val transitions: Transitions,
private val interactionJankMonitor: InteractionJankMonitor,
@ShellMainThread private val handler: Handler,
-) : TaskPositioner, Transitions.TransitionHandler {
+ private val multiDisplayDragMoveIndicatorController: MultiDisplayDragMoveIndicatorController,
+) : TaskPositioner, Transitions.TransitionHandler, DisplayController.OnDisplaysChangedListener {
private val dragEventListeners =
mutableListOf<DragPositioningCallbackUtility.DragEventListener>()
private val stableBounds = Rect()
@@ -71,6 +73,7 @@ class MultiDisplayVeiledResizeTaskPositioner(
private var isResizingOrAnimatingResize = false
@Surface.Rotation private var rotation = 0
private var startDisplayId = 0
+ private val displayIds = mutableSetOf<Int>()
constructor(
taskOrganizer: ShellTaskOrganizer,
@@ -80,19 +83,22 @@ class MultiDisplayVeiledResizeTaskPositioner(
transitions: Transitions,
interactionJankMonitor: InteractionJankMonitor,
@ShellMainThread handler: Handler,
+ multiDisplayDragMoveIndicatorController: MultiDisplayDragMoveIndicatorController,
) : this(
taskOrganizer,
windowDecoration,
displayController,
dragEventListener,
- Supplier<SurfaceControl.Transaction> { SurfaceControl.Transaction() },
+ { SurfaceControl.Transaction() },
transitions,
interactionJankMonitor,
handler,
+ multiDisplayDragMoveIndicatorController,
)
init {
dragEventListeners.add(dragEventListener)
+ displayController.addDisplayWindowListener(this)
}
override fun onDragPositioningStart(ctrlType: Int, displayId: Int, x: Float, y: Float): Rect {
@@ -164,7 +170,7 @@ class MultiDisplayVeiledResizeTaskPositioner(
createLongTimeoutJankConfigBuilder(Cuj.CUJ_DESKTOP_MODE_DRAG_WINDOW)
)
- val t = transactionSupplier.get()
+ val t = transactionSupplier()
val startDisplayLayout = displayController.getDisplayLayout(startDisplayId)
val currentDisplayLayout = displayController.getDisplayLayout(displayId)
@@ -196,7 +202,13 @@ class MultiDisplayVeiledResizeTaskPositioner(
)
)
- // TODO(b/383069173): Render drag indicator(s)
+ multiDisplayDragMoveIndicatorController.onDragMove(
+ boundsDp,
+ startDisplayId,
+ desktopWindowDecoration.mTaskInfo,
+ displayIds,
+ transactionSupplier,
+ )
t.setPosition(
desktopWindowDecoration.leash,
@@ -267,7 +279,10 @@ class MultiDisplayVeiledResizeTaskPositioner(
)
)
- // TODO(b/383069173): Clear drag indicator(s)
+ multiDisplayDragMoveIndicatorController.onDragEnd(
+ desktopWindowDecoration.mTaskInfo.taskId,
+ transactionSupplier,
+ )
}
interactionJankMonitor.end(Cuj.CUJ_DESKTOP_MODE_DRAG_WINDOW)
@@ -348,6 +363,14 @@ class MultiDisplayVeiledResizeTaskPositioner(
dragEventListeners.remove(dragEventListener)
}
+ override fun onTopologyChanged(topology: DisplayTopology) {
+ // TODO: b/383069173 - Cancel window drag when topology changes happen during drag.
+
+ displayIds.clear()
+ val displayBounds = topology.getAbsoluteBounds()
+ displayIds.addAll(List(displayBounds.size()) { displayBounds.keyAt(it) })
+ }
+
companion object {
// Timeout used for resize and drag CUJs, this is longer than the default timeout to avoid
// timing out in the middle of a resize or drag action.
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/activityembedding/ActivityEmbeddingAnimationRunnerTests.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/activityembedding/ActivityEmbeddingAnimationRunnerTests.java
index 94dc774a6737..d4d8d93abf7d 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/activityembedding/ActivityEmbeddingAnimationRunnerTests.java
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/activityembedding/ActivityEmbeddingAnimationRunnerTests.java
@@ -39,7 +39,6 @@ import android.animation.Animator;
import android.annotation.NonNull;
import android.graphics.Point;
import android.graphics.Rect;
-import android.platform.test.annotations.DisableFlags;
import android.platform.test.annotations.EnableFlags;
import android.view.animation.AlphaAnimation;
import android.view.animation.Animation;
@@ -77,7 +76,6 @@ public class ActivityEmbeddingAnimationRunnerTests extends ActivityEmbeddingAnim
doNothing().when(mController).onAnimationFinished(any());
}
- @EnableFlags(Flags.FLAG_MOVE_ANIMATION_OPTIONS_TO_CHANGE)
@Test
public void testStartAnimation() {
final TransitionInfo info = new TransitionInfoBuilder(TRANSIT_OPEN, 0)
@@ -103,7 +101,6 @@ public class ActivityEmbeddingAnimationRunnerTests extends ActivityEmbeddingAnim
verify(mController).onAnimationFinished(mTransition);
}
- @EnableFlags(Flags.FLAG_MOVE_ANIMATION_OPTIONS_TO_CHANGE)
@Test
public void testChangesBehindStartingWindow() {
final TransitionInfo info = new TransitionInfoBuilder(TRANSIT_OPEN, 0)
@@ -118,7 +115,6 @@ public class ActivityEmbeddingAnimationRunnerTests extends ActivityEmbeddingAnim
assertEquals(0, animator.getDuration());
}
- @EnableFlags(Flags.FLAG_MOVE_ANIMATION_OPTIONS_TO_CHANGE)
@Test
public void testTransitionTypeDragResize() {
final TransitionInfo info = new TransitionInfoBuilder(TRANSIT_TASK_FRAGMENT_DRAG_RESIZE, 0)
@@ -133,25 +129,6 @@ public class ActivityEmbeddingAnimationRunnerTests extends ActivityEmbeddingAnim
assertEquals(0, animator.getDuration());
}
- @DisableFlags(Flags.FLAG_MOVE_ANIMATION_OPTIONS_TO_CHANGE)
- @Test
- public void testInvalidCustomAnimation_disableAnimationOptionsPerChange() {
- final TransitionInfo info = new TransitionInfoBuilder(TRANSIT_OPEN, 0)
- .addChange(createChange(FLAG_IN_TASK_WITH_EMBEDDED_ACTIVITY, TRANSIT_OPEN))
- .build();
- info.setAnimationOptions(TransitionInfo.AnimationOptions
- .makeCustomAnimOptions("packageName", 0 /* enterResId */, 0 /* exitResId */,
- 0 /* backgroundColor */, false /* overrideTaskTransition */));
- final Animator animator = mAnimRunner.createAnimator(
- info, mStartTransaction, mFinishTransaction,
- () -> mFinishCallback.onTransitionFinished(null /* wct */),
- new ArrayList<>());
-
- // An invalid custom animation is equivalent to jump-cut.
- assertEquals(0, animator.getDuration());
- }
-
- @EnableFlags(Flags.FLAG_MOVE_ANIMATION_OPTIONS_TO_CHANGE)
@Test
public void testInvalidCustomAnimation_enableAnimationOptionsPerChange() {
final TransitionInfo info = new TransitionInfoBuilder(TRANSIT_OPEN, 0)
@@ -169,36 +146,6 @@ public class ActivityEmbeddingAnimationRunnerTests extends ActivityEmbeddingAnim
assertEquals(0, animator.getDuration());
}
- @DisableFlags(Flags.FLAG_ACTIVITY_EMBEDDING_OVERLAY_PRESENTATION_FLAG)
- @Test
- public void testCalculateParentBounds_flagDisabled() {
- final Rect parentBounds = new Rect(0, 0, 2000, 2000);
- final Rect primaryBounds = new Rect();
- final Rect secondaryBounds = new Rect();
- parentBounds.splitVertically(primaryBounds, secondaryBounds);
-
- final TransitionInfo.Change change = createChange(0 /* flags */);
- change.setStartAbsBounds(secondaryBounds);
-
- final TransitionInfo.Change boundsAnimationChange = createChange(0 /* flags */);
- boundsAnimationChange.setStartAbsBounds(primaryBounds);
- boundsAnimationChange.setEndAbsBounds(primaryBounds);
- final Rect actualParentBounds = new Rect();
-
- calculateParentBounds(change, boundsAnimationChange, actualParentBounds);
-
- assertEquals(parentBounds, actualParentBounds);
-
- actualParentBounds.setEmpty();
-
- boundsAnimationChange.setStartAbsBounds(secondaryBounds);
- boundsAnimationChange.setEndAbsBounds(primaryBounds);
-
- calculateParentBounds(boundsAnimationChange, boundsAnimationChange, actualParentBounds);
-
- assertEquals(parentBounds, actualParentBounds);
- }
-
// TODO(b/243518738): Rewrite with TestParameter
@EnableFlags(Flags.FLAG_ACTIVITY_EMBEDDING_OVERLAY_PRESENTATION_FLAG)
@Test
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/activityembedding/ActivityEmbeddingControllerTests.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/activityembedding/ActivityEmbeddingControllerTests.java
index 9f29ef71930a..53a13d0d4ffd 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/activityembedding/ActivityEmbeddingControllerTests.java
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/activityembedding/ActivityEmbeddingControllerTests.java
@@ -32,8 +32,6 @@ import static org.mockito.Mockito.verifyNoMoreInteractions;
import android.animation.Animator;
import android.animation.ValueAnimator;
import android.graphics.Rect;
-import android.platform.test.annotations.DisableFlags;
-import android.platform.test.annotations.EnableFlags;
import android.view.SurfaceControl;
import android.window.TransitionInfo;
@@ -41,7 +39,6 @@ import androidx.test.annotation.UiThreadTest;
import androidx.test.ext.junit.runners.AndroidJUnit4;
import androidx.test.filters.SmallTest;
-import com.android.window.flags.Flags;
import com.android.wm.shell.transition.TransitionInfoBuilder;
import org.junit.Before;
@@ -69,13 +66,11 @@ public class ActivityEmbeddingControllerTests extends ActivityEmbeddingAnimation
any());
}
- @EnableFlags(Flags.FLAG_MOVE_ANIMATION_OPTIONS_TO_CHANGE)
@Test
public void testInstantiate() {
verify(mShellInit).addInitCallback(any(), any());
}
- @EnableFlags(Flags.FLAG_MOVE_ANIMATION_OPTIONS_TO_CHANGE)
@Test
public void testOnInit() {
mController.onInit();
@@ -83,7 +78,6 @@ public class ActivityEmbeddingControllerTests extends ActivityEmbeddingAnimation
verify(mTransitions).addHandler(mController);
}
- @EnableFlags(Flags.FLAG_MOVE_ANIMATION_OPTIONS_TO_CHANGE)
@Test
public void testSetAnimScaleSetting() {
mController.setAnimScaleSetting(1.0f);
@@ -92,7 +86,6 @@ public class ActivityEmbeddingControllerTests extends ActivityEmbeddingAnimation
verify(mAnimSpec).setAnimScaleSetting(1.0f);
}
- @EnableFlags(Flags.FLAG_MOVE_ANIMATION_OPTIONS_TO_CHANGE)
@Test
public void testStartAnimation_containsNonActivityEmbeddingChange() {
final TransitionInfo.Change nonEmbeddedOpen = createChange(0 /* flags */);
@@ -129,7 +122,6 @@ public class ActivityEmbeddingControllerTests extends ActivityEmbeddingAnimation
assertFalse(info2.getChanges().contains(nonEmbeddedClose));
}
- @EnableFlags(Flags.FLAG_MOVE_ANIMATION_OPTIONS_TO_CHANGE)
@Test
public void testStartAnimation_containsOnlyFillTaskActivityEmbeddingChange() {
final TransitionInfo info = new TransitionInfoBuilder(TRANSIT_OPEN, 0)
@@ -146,7 +138,6 @@ public class ActivityEmbeddingControllerTests extends ActivityEmbeddingAnimation
verifyNoMoreInteractions(mFinishCallback);
}
- @EnableFlags(Flags.FLAG_MOVE_ANIMATION_OPTIONS_TO_CHANGE)
@Test
public void testStartAnimation_containsActivityEmbeddingSplitChange() {
// Change that occupies only part of the Task.
@@ -164,7 +155,6 @@ public class ActivityEmbeddingControllerTests extends ActivityEmbeddingAnimation
verifyNoMoreInteractions(mFinishTransaction);
}
- @EnableFlags(Flags.FLAG_MOVE_ANIMATION_OPTIONS_TO_CHANGE)
@Test
public void testStartAnimation_containsChangeEnterActivityEmbeddingSplit() {
// Change that is entering ActivityEmbedding split.
@@ -181,7 +171,6 @@ public class ActivityEmbeddingControllerTests extends ActivityEmbeddingAnimation
verifyNoMoreInteractions(mFinishTransaction);
}
- @EnableFlags(Flags.FLAG_MOVE_ANIMATION_OPTIONS_TO_CHANGE)
@Test
public void testStartAnimation_containsChangeExitActivityEmbeddingSplit() {
// Change that is exiting ActivityEmbedding split.
@@ -198,27 +187,6 @@ public class ActivityEmbeddingControllerTests extends ActivityEmbeddingAnimation
verifyNoMoreInteractions(mFinishTransaction);
}
- @DisableFlags(Flags.FLAG_MOVE_ANIMATION_OPTIONS_TO_CHANGE)
- @Test
- public void testShouldAnimate_containsAnimationOptions_disableAnimOptionsPerChange() {
- final TransitionInfo info = new TransitionInfoBuilder(TRANSIT_CLOSE, 0)
- .addChange(createEmbeddedChange(EMBEDDED_RIGHT_BOUNDS, TASK_BOUNDS, TASK_BOUNDS))
- .build();
-
- info.setAnimationOptions(TransitionInfo.AnimationOptions
- .makeCustomAnimOptions("packageName", 0 /* enterResId */, 0 /* exitResId */,
- 0 /* backgroundColor */, false /* overrideTaskTransition */));
- assertTrue(mController.shouldAnimate(info));
-
- info.setAnimationOptions(TransitionInfo.AnimationOptions
- .makeSceneTransitionAnimOptions());
- assertFalse(mController.shouldAnimate(info));
-
- info.setAnimationOptions(TransitionInfo.AnimationOptions.makeCrossProfileAnimOptions());
- assertFalse(mController.shouldAnimate(info));
- }
-
- @EnableFlags(Flags.FLAG_MOVE_ANIMATION_OPTIONS_TO_CHANGE)
@Test
public void testShouldAnimate_containsAnimationOptions_enableAnimOptionsPerChange() {
final TransitionInfo info = new TransitionInfoBuilder(TRANSIT_CLOSE, 0)
@@ -239,7 +207,6 @@ public class ActivityEmbeddingControllerTests extends ActivityEmbeddingAnimation
assertFalse(mController.shouldAnimate(info));
}
- @EnableFlags(Flags.FLAG_MOVE_ANIMATION_OPTIONS_TO_CHANGE)
@UiThreadTest
@Test
public void testMergeAnimation() {
@@ -278,7 +245,6 @@ public class ActivityEmbeddingControllerTests extends ActivityEmbeddingAnimation
verify(mFinishCallback).onTransitionFinished(any());
}
- @EnableFlags(Flags.FLAG_MOVE_ANIMATION_OPTIONS_TO_CHANGE)
@Test
public void testOnAnimationFinished() {
// Should not call finish when there is no transition.
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/common/MultiDisplayDragMoveIndicatorControllerTest.kt b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/common/MultiDisplayDragMoveIndicatorControllerTest.kt
new file mode 100644
index 000000000000..abd238847519
--- /dev/null
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/common/MultiDisplayDragMoveIndicatorControllerTest.kt
@@ -0,0 +1,168 @@
+/*
+ * Copyright (C) 2025 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.common
+
+import android.app.ActivityManager.RunningTaskInfo
+import android.content.res.Configuration
+import android.graphics.Rect
+import android.graphics.RectF
+import android.testing.TestableResources
+import android.view.Display
+import android.view.SurfaceControl
+import androidx.test.ext.junit.runners.AndroidJUnit4
+import androidx.test.filters.SmallTest
+import com.android.wm.shell.RootTaskDisplayAreaOrganizer
+import com.android.wm.shell.ShellTestCase
+import com.android.wm.shell.TestShellExecutor
+import java.util.function.Supplier
+import org.junit.Before
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.mockito.Mockito.never
+import org.mockito.Mockito.verify
+import org.mockito.kotlin.any
+import org.mockito.kotlin.eq
+import org.mockito.kotlin.mock
+import org.mockito.kotlin.times
+import org.mockito.kotlin.whenever
+
+/**
+ * Tests for [MultiDisplayDragMoveIndicatorController].
+ *
+ * Build/Install/Run: atest WMShellUnitTests:MultiDisplayDragMoveIndicatorControllerTest
+ */
+@SmallTest
+@RunWith(AndroidJUnit4::class)
+class MultiDisplayDragMoveIndicatorControllerTest : ShellTestCase() {
+ private val displayController = mock<DisplayController>()
+ private val rootTaskDisplayAreaOrganizer = mock<RootTaskDisplayAreaOrganizer>()
+ private val indicatorSurfaceFactory = mock<MultiDisplayDragMoveIndicatorSurface.Factory>()
+ private val indicatorSurface0 = mock<MultiDisplayDragMoveIndicatorSurface>()
+ private val indicatorSurface1 = mock<MultiDisplayDragMoveIndicatorSurface>()
+ private val transaction = mock<SurfaceControl.Transaction>()
+ private val transactionSupplier = mock<Supplier<SurfaceControl.Transaction>>()
+ private val taskInfo = mock<RunningTaskInfo>()
+ private val display0 = mock<Display>()
+ private val display1 = mock<Display>()
+
+ private lateinit var resources: TestableResources
+ private val executor = TestShellExecutor()
+
+ private lateinit var controller: MultiDisplayDragMoveIndicatorController
+
+ @Before
+ fun setUp() {
+ resources = mContext.getOrCreateTestableResources()
+ val resourceConfiguration = Configuration()
+ resourceConfiguration.uiMode = 0
+ resources.overrideConfiguration(resourceConfiguration)
+
+ controller =
+ MultiDisplayDragMoveIndicatorController(
+ displayController,
+ rootTaskDisplayAreaOrganizer,
+ indicatorSurfaceFactory,
+ executor,
+ )
+
+ val spyDisplayLayout0 =
+ MultiDisplayTestUtil.createSpyDisplayLayout(
+ MultiDisplayTestUtil.DISPLAY_GLOBAL_BOUNDS_0,
+ MultiDisplayTestUtil.DISPLAY_DPI_0,
+ resources.resources,
+ )
+ val spyDisplayLayout1 =
+ MultiDisplayTestUtil.createSpyDisplayLayout(
+ MultiDisplayTestUtil.DISPLAY_GLOBAL_BOUNDS_1,
+ MultiDisplayTestUtil.DISPLAY_DPI_1,
+ resources.resources,
+ )
+
+ taskInfo.taskId = TASK_ID
+ whenever(displayController.getDisplayLayout(0)).thenReturn(spyDisplayLayout0)
+ whenever(displayController.getDisplayLayout(1)).thenReturn(spyDisplayLayout1)
+ whenever(displayController.getDisplay(0)).thenReturn(display0)
+ whenever(displayController.getDisplay(1)).thenReturn(display1)
+ whenever(indicatorSurfaceFactory.create(taskInfo, display0)).thenReturn(indicatorSurface0)
+ whenever(indicatorSurfaceFactory.create(taskInfo, display1)).thenReturn(indicatorSurface1)
+ whenever(transactionSupplier.get()).thenReturn(transaction)
+ }
+
+ @Test
+ fun onDrag_boundsNotIntersectWithDisplay_noIndicator() {
+ controller.onDragMove(
+ RectF(2000f, 2000f, 2100f, 2200f), // not intersect with any display
+ startDisplayId = 0,
+ taskInfo,
+ displayIds = setOf(0, 1),
+ ) { transaction }
+ executor.flushAll()
+
+ verify(indicatorSurfaceFactory, never()).create(any(), any())
+ }
+
+ @Test
+ fun onDrag_boundsIntersectWithStartDisplay_noIndicator() {
+ controller.onDragMove(
+ RectF(100f, 100f, 200f, 200f), // intersect with display 0
+ startDisplayId = 0,
+ taskInfo,
+ displayIds = setOf(0, 1),
+ ) { transaction }
+ executor.flushAll()
+
+ verify(indicatorSurfaceFactory, never()).create(any(), any())
+ }
+
+ @Test
+ fun onDrag_boundsIntersectWithNonStartDisplay_showAndDisposeIndicator() {
+ controller.onDragMove(
+ RectF(100f, -100f, 200f, 200f), // intersect with display 0 and 1
+ startDisplayId = 0,
+ taskInfo,
+ displayIds = setOf(0, 1),
+ ) { transaction }
+ executor.flushAll()
+
+ verify(indicatorSurfaceFactory, times(1)).create(taskInfo, display1)
+ verify(indicatorSurface1, times(1))
+ .show(transaction, taskInfo, rootTaskDisplayAreaOrganizer, 1, Rect(0, 1800, 200, 2400))
+
+ controller.onDragMove(
+ RectF(2000f, 2000f, 2100f, 2200f), // not intersect with display 1
+ startDisplayId = 0,
+ taskInfo,
+ displayIds = setOf(0, 1)
+ ) { transaction }
+ while (executor.callbacks.isNotEmpty()) {
+ executor.flushAll()
+ }
+
+ verify(indicatorSurface1, times(1))
+ .relayout(any(), eq(transaction), shouldBeVisible = eq(false))
+
+ controller.onDragEnd(TASK_ID, { transaction })
+ while (executor.callbacks.isNotEmpty()) {
+ executor.flushAll()
+ }
+
+ verify(indicatorSurface1, times(1)).disposeSurface(transaction)
+ }
+
+ companion object {
+ private const val TASK_ID = 10
+ }
+}
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopDisplayEventHandlerTest.kt b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopDisplayEventHandlerTest.kt
index 0d5741fccbcc..8ad54f5a0bb4 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopDisplayEventHandlerTest.kt
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopDisplayEventHandlerTest.kt
@@ -16,51 +16,28 @@
package com.android.wm.shell.desktopmode
-import android.app.ActivityManager.RunningTaskInfo
-import android.app.WindowConfiguration.WINDOWING_MODE_FREEFORM
-import android.app.WindowConfiguration.WINDOWING_MODE_FULLSCREEN
-import android.app.WindowConfiguration.WINDOWING_MODE_UNDEFINED
-import android.content.ContentResolver
-import android.os.Binder
import android.platform.test.annotations.EnableFlags
-import android.provider.Settings
-import android.provider.Settings.Global.DEVELOPMENT_FORCE_DESKTOP_MODE_ON_EXTERNAL_DISPLAYS
import android.testing.AndroidTestingRunner
import android.view.Display.DEFAULT_DISPLAY
-import android.view.IWindowManager
-import android.view.WindowManager.TRANSIT_CHANGE
-import android.window.DisplayAreaInfo
-import android.window.WindowContainerTransaction
import androidx.test.filters.SmallTest
import com.android.dx.mockito.inline.extended.ExtendedMockito.mockitoSession
import com.android.dx.mockito.inline.extended.ExtendedMockito.never
import com.android.dx.mockito.inline.extended.StaticMockitoSession
import com.android.window.flags.Flags
-import com.android.wm.shell.MockToken
-import com.android.wm.shell.RootTaskDisplayAreaOrganizer
-import com.android.wm.shell.ShellTaskOrganizer
import com.android.wm.shell.ShellTestCase
-import com.android.wm.shell.TestRunningTaskInfoBuilder
import com.android.wm.shell.common.DisplayController
import com.android.wm.shell.common.DisplayController.OnDisplaysChangedListener
import com.android.wm.shell.common.ShellExecutor
import com.android.wm.shell.shared.desktopmode.DesktopModeStatus
import com.android.wm.shell.sysui.ShellInit
-import com.android.wm.shell.transition.Transitions
-import com.google.common.truth.Truth.assertThat
import org.junit.After
import org.junit.Before
import org.junit.Test
import org.junit.runner.RunWith
-import org.mockito.ArgumentMatchers.isNull
import org.mockito.Mock
-import org.mockito.Mockito.anyInt
import org.mockito.Mockito.spy
-import org.mockito.Mockito.times
import org.mockito.Mockito.verify
-import org.mockito.kotlin.any
import org.mockito.kotlin.argumentCaptor
-import org.mockito.kotlin.eq
import org.mockito.kotlin.whenever
import org.mockito.quality.Strictness
@@ -73,27 +50,18 @@ import org.mockito.quality.Strictness
@RunWith(AndroidTestingRunner::class)
class DesktopDisplayEventHandlerTest : ShellTestCase() {
@Mock lateinit var testExecutor: ShellExecutor
- @Mock lateinit var transitions: Transitions
@Mock lateinit var displayController: DisplayController
- @Mock lateinit var rootTaskDisplayAreaOrganizer: RootTaskDisplayAreaOrganizer
- @Mock private lateinit var mockWindowManager: IWindowManager
@Mock private lateinit var mockDesktopUserRepositories: DesktopUserRepositories
@Mock private lateinit var mockDesktopRepository: DesktopRepository
@Mock private lateinit var mockDesktopTasksController: DesktopTasksController
- @Mock private lateinit var shellTaskOrganizer: ShellTaskOrganizer
+ @Mock private lateinit var desktopDisplayModeController: DesktopDisplayModeController
private lateinit var mockitoSession: StaticMockitoSession
private lateinit var shellInit: ShellInit
private lateinit var handler: DesktopDisplayEventHandler
private val onDisplaysChangedListenerCaptor = argumentCaptor<OnDisplaysChangedListener>()
- private val runningTasks = mutableListOf<RunningTaskInfo>()
private val externalDisplayId = 100
- private val freeformTask =
- TestRunningTaskInfoBuilder().setWindowingMode(WINDOWING_MODE_FREEFORM).build()
- private val fullscreenTask =
- TestRunningTaskInfoBuilder().setWindowingMode(WINDOWING_MODE_FULLSCREEN).build()
- private val defaultTDA = DisplayAreaInfo(MockToken().token(), DEFAULT_DISPLAY, 0)
@Before
fun setUp() {
@@ -105,24 +73,15 @@ class DesktopDisplayEventHandlerTest : ShellTestCase() {
shellInit = spy(ShellInit(testExecutor))
whenever(mockDesktopUserRepositories.current).thenReturn(mockDesktopRepository)
- whenever(transitions.startTransition(anyInt(), any(), isNull())).thenAnswer { Binder() }
- whenever(rootTaskDisplayAreaOrganizer.getDisplayAreaInfo(DEFAULT_DISPLAY))
- .thenReturn(defaultTDA)
handler =
DesktopDisplayEventHandler(
context,
shellInit,
- transitions,
displayController,
- rootTaskDisplayAreaOrganizer,
- mockWindowManager,
mockDesktopUserRepositories,
mockDesktopTasksController,
- shellTaskOrganizer,
+ desktopDisplayModeController,
)
- whenever(shellTaskOrganizer.getRunningTasks(anyInt())).thenAnswer { runningTasks }
- runningTasks.add(freeformTask)
- runningTasks.add(fullscreenTask)
shellInit.init()
verify(displayController)
.addDisplayWindowListener(onDisplaysChangedListenerCaptor.capture())
@@ -133,65 +92,6 @@ class DesktopDisplayEventHandlerTest : ShellTestCase() {
mockitoSession.finishMocking()
}
- private fun testDisplayWindowingModeSwitch(
- defaultWindowingMode: Int,
- extendedDisplayEnabled: Boolean,
- expectTransition: Boolean,
- ) {
- defaultTDA.configuration.windowConfiguration.windowingMode = defaultWindowingMode
- whenever(mockWindowManager.getWindowingMode(anyInt())).thenAnswer { defaultWindowingMode }
- val settingsSession =
- ExtendedDisplaySettingsSession(
- context.contentResolver,
- if (extendedDisplayEnabled) 1 else 0,
- )
-
- settingsSession.use {
- connectExternalDisplay()
- defaultTDA.configuration.windowConfiguration.windowingMode = WINDOWING_MODE_FREEFORM
- disconnectExternalDisplay()
-
- if (expectTransition) {
- val arg = argumentCaptor<WindowContainerTransaction>()
- verify(transitions, times(2))
- .startTransition(eq(TRANSIT_CHANGE), arg.capture(), isNull())
- assertThat(arg.firstValue.changes[defaultTDA.token.asBinder()]?.windowingMode)
- .isEqualTo(WINDOWING_MODE_FREEFORM)
- assertThat(arg.secondValue.changes[defaultTDA.token.asBinder()]?.windowingMode)
- .isEqualTo(defaultWindowingMode)
- } else {
- verify(transitions, never()).startTransition(eq(TRANSIT_CHANGE), any(), isNull())
- }
- }
- }
-
- @Test
- fun displayWindowingModeSwitchOnDisplayConnected_extendedDisplayDisabled() {
- testDisplayWindowingModeSwitch(
- defaultWindowingMode = WINDOWING_MODE_FULLSCREEN,
- extendedDisplayEnabled = false,
- expectTransition = false,
- )
- }
-
- @Test
- fun displayWindowingModeSwitchOnDisplayConnected_fullscreenDisplay() {
- testDisplayWindowingModeSwitch(
- defaultWindowingMode = WINDOWING_MODE_FULLSCREEN,
- extendedDisplayEnabled = true,
- expectTransition = true,
- )
- }
-
- @Test
- fun displayWindowingModeSwitchOnDisplayConnected_freeformDisplay() {
- testDisplayWindowingModeSwitch(
- defaultWindowingMode = WINDOWING_MODE_FREEFORM,
- extendedDisplayEnabled = true,
- expectTransition = false,
- )
- }
-
@Test
fun testDisplayAdded_supportsDesks_createsDesk() {
whenever(DesktopModeStatus.canEnterDesktopMode(context)).thenReturn(true)
@@ -231,70 +131,14 @@ class DesktopDisplayEventHandlerTest : ShellTestCase() {
}
@Test
- fun displayWindowingModeSwitch_existingTasksOnConnected() {
- defaultTDA.configuration.windowConfiguration.windowingMode = WINDOWING_MODE_FULLSCREEN
- whenever(mockWindowManager.getWindowingMode(anyInt())).thenAnswer {
- WINDOWING_MODE_FULLSCREEN
- }
-
- ExtendedDisplaySettingsSession(context.contentResolver, 1).use {
- connectExternalDisplay()
-
- val arg = argumentCaptor<WindowContainerTransaction>()
- verify(transitions, times(1))
- .startTransition(eq(TRANSIT_CHANGE), arg.capture(), isNull())
- assertThat(arg.firstValue.changes[freeformTask.token.asBinder()]?.windowingMode)
- .isEqualTo(WINDOWING_MODE_UNDEFINED)
- assertThat(arg.firstValue.changes[fullscreenTask.token.asBinder()]?.windowingMode)
- .isEqualTo(WINDOWING_MODE_FULLSCREEN)
- }
- }
-
- @Test
- fun displayWindowingModeSwitch_existingTasksOnDisconnected() {
- defaultTDA.configuration.windowConfiguration.windowingMode = WINDOWING_MODE_FREEFORM
- whenever(mockWindowManager.getWindowingMode(anyInt())).thenAnswer {
- WINDOWING_MODE_FULLSCREEN
- }
-
- ExtendedDisplaySettingsSession(context.contentResolver, 1).use {
- disconnectExternalDisplay()
-
- val arg = argumentCaptor<WindowContainerTransaction>()
- verify(transitions, times(1))
- .startTransition(eq(TRANSIT_CHANGE), arg.capture(), isNull())
- assertThat(arg.firstValue.changes[freeformTask.token.asBinder()]?.windowingMode)
- .isEqualTo(WINDOWING_MODE_FREEFORM)
- assertThat(arg.firstValue.changes[fullscreenTask.token.asBinder()]?.windowingMode)
- .isEqualTo(WINDOWING_MODE_UNDEFINED)
- }
- }
-
- private fun connectExternalDisplay() {
- whenever(rootTaskDisplayAreaOrganizer.getDisplayIds())
- .thenReturn(intArrayOf(DEFAULT_DISPLAY, externalDisplayId))
+ fun testConnectExternalDisplay() {
onDisplaysChangedListenerCaptor.lastValue.onDisplayAdded(externalDisplayId)
+ verify(desktopDisplayModeController).refreshDisplayWindowingMode()
}
- private fun disconnectExternalDisplay() {
- whenever(rootTaskDisplayAreaOrganizer.getDisplayIds())
- .thenReturn(intArrayOf(DEFAULT_DISPLAY))
+ @Test
+ fun testDisconnectExternalDisplay() {
onDisplaysChangedListenerCaptor.lastValue.onDisplayRemoved(externalDisplayId)
- }
-
- private class ExtendedDisplaySettingsSession(
- private val contentResolver: ContentResolver,
- private val overrideValue: Int,
- ) : AutoCloseable {
- private val settingName = DEVELOPMENT_FORCE_DESKTOP_MODE_ON_EXTERNAL_DISPLAYS
- private val initialValue = Settings.Global.getInt(contentResolver, settingName, 0)
-
- init {
- Settings.Global.putInt(contentResolver, settingName, overrideValue)
- }
-
- override fun close() {
- Settings.Global.putInt(contentResolver, settingName, initialValue)
- }
+ verify(desktopDisplayModeController).refreshDisplayWindowingMode()
}
}
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopDisplayModeControllerTest.kt b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopDisplayModeControllerTest.kt
new file mode 100644
index 000000000000..0ff7230f6e0c
--- /dev/null
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopDisplayModeControllerTest.kt
@@ -0,0 +1,233 @@
+/*
+ * Copyright (C) 2025 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.desktopmode
+
+import android.app.ActivityManager.RunningTaskInfo
+import android.app.WindowConfiguration.WINDOWING_MODE_FREEFORM
+import android.app.WindowConfiguration.WINDOWING_MODE_FULLSCREEN
+import android.app.WindowConfiguration.WINDOWING_MODE_UNDEFINED
+import android.content.ContentResolver
+import android.os.Binder
+import android.provider.Settings
+import android.provider.Settings.Global.DEVELOPMENT_FORCE_DESKTOP_MODE_ON_EXTERNAL_DISPLAYS
+import android.testing.AndroidTestingRunner
+import android.view.Display.DEFAULT_DISPLAY
+import android.view.IWindowManager
+import android.view.WindowManager.TRANSIT_CHANGE
+import android.window.DisplayAreaInfo
+import android.window.WindowContainerTransaction
+import androidx.test.filters.SmallTest
+import com.android.dx.mockito.inline.extended.ExtendedMockito.never
+import com.android.wm.shell.MockToken
+import com.android.wm.shell.RootTaskDisplayAreaOrganizer
+import com.android.wm.shell.ShellTaskOrganizer
+import com.android.wm.shell.ShellTestCase
+import com.android.wm.shell.TestRunningTaskInfoBuilder
+import com.android.wm.shell.desktopmode.desktopwallpaperactivity.DesktopWallpaperActivityTokenProvider
+import com.android.wm.shell.transition.Transitions
+import com.google.common.truth.Truth.assertThat
+import org.junit.Before
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.mockito.ArgumentMatchers.isNull
+import org.mockito.Mockito.anyInt
+import org.mockito.Mockito.times
+import org.mockito.Mockito.verify
+import org.mockito.kotlin.any
+import org.mockito.kotlin.argumentCaptor
+import org.mockito.kotlin.eq
+import org.mockito.kotlin.mock
+import org.mockito.kotlin.whenever
+
+/**
+ * Test class for [DesktopDisplayModeController]
+ *
+ * Usage: atest WMShellUnitTests:DesktopDisplayModeControllerTest
+ */
+@SmallTest
+@RunWith(AndroidTestingRunner::class)
+class DesktopDisplayModeControllerTest : ShellTestCase() {
+ private val transitions = mock<Transitions>()
+ private val rootTaskDisplayAreaOrganizer = mock<RootTaskDisplayAreaOrganizer>()
+ private val mockWindowManager = mock<IWindowManager>()
+ private val shellTaskOrganizer = mock<ShellTaskOrganizer>()
+ private val desktopWallpaperActivityTokenProvider =
+ mock<DesktopWallpaperActivityTokenProvider>()
+
+ private lateinit var controller: DesktopDisplayModeController
+
+ private val runningTasks = mutableListOf<RunningTaskInfo>()
+ private val freeformTask =
+ TestRunningTaskInfoBuilder().setWindowingMode(WINDOWING_MODE_FREEFORM).build()
+ private val fullscreenTask =
+ TestRunningTaskInfoBuilder().setWindowingMode(WINDOWING_MODE_FULLSCREEN).build()
+ private val defaultTDA = DisplayAreaInfo(MockToken().token(), DEFAULT_DISPLAY, 0)
+ private val wallpaperToken = MockToken().token()
+
+ @Before
+ fun setUp() {
+ whenever(transitions.startTransition(anyInt(), any(), isNull())).thenReturn(Binder())
+ whenever(rootTaskDisplayAreaOrganizer.getDisplayAreaInfo(DEFAULT_DISPLAY))
+ .thenReturn(defaultTDA)
+ controller =
+ DesktopDisplayModeController(
+ context,
+ transitions,
+ rootTaskDisplayAreaOrganizer,
+ mockWindowManager,
+ shellTaskOrganizer,
+ desktopWallpaperActivityTokenProvider,
+ )
+ runningTasks.add(freeformTask)
+ runningTasks.add(fullscreenTask)
+ whenever(shellTaskOrganizer.getRunningTasks(anyInt())).thenReturn(ArrayList(runningTasks))
+ whenever(desktopWallpaperActivityTokenProvider.getToken()).thenReturn(wallpaperToken)
+ }
+
+ private fun testDisplayWindowingModeSwitch(
+ defaultWindowingMode: Int,
+ extendedDisplayEnabled: Boolean,
+ expectTransition: Boolean,
+ ) {
+ defaultTDA.configuration.windowConfiguration.windowingMode = defaultWindowingMode
+ whenever(mockWindowManager.getWindowingMode(anyInt())).thenReturn(defaultWindowingMode)
+ val settingsSession =
+ ExtendedDisplaySettingsSession(
+ context.contentResolver,
+ if (extendedDisplayEnabled) 1 else 0,
+ )
+
+ settingsSession.use {
+ connectExternalDisplay()
+ defaultTDA.configuration.windowConfiguration.windowingMode = WINDOWING_MODE_FREEFORM
+ disconnectExternalDisplay()
+
+ if (expectTransition) {
+ val arg = argumentCaptor<WindowContainerTransaction>()
+ verify(transitions, times(2))
+ .startTransition(eq(TRANSIT_CHANGE), arg.capture(), isNull())
+ assertThat(arg.firstValue.changes[defaultTDA.token.asBinder()]?.windowingMode)
+ .isEqualTo(WINDOWING_MODE_FREEFORM)
+ assertThat(arg.firstValue.changes[wallpaperToken.asBinder()]?.windowingMode)
+ .isEqualTo(WINDOWING_MODE_FULLSCREEN)
+ assertThat(arg.secondValue.changes[defaultTDA.token.asBinder()]?.windowingMode)
+ .isEqualTo(defaultWindowingMode)
+ assertThat(arg.secondValue.changes[wallpaperToken.asBinder()]?.windowingMode)
+ .isEqualTo(WINDOWING_MODE_FULLSCREEN)
+ } else {
+ verify(transitions, never()).startTransition(eq(TRANSIT_CHANGE), any(), isNull())
+ }
+ }
+ }
+
+ @Test
+ fun displayWindowingModeSwitchOnDisplayConnected_extendedDisplayDisabled() {
+ testDisplayWindowingModeSwitch(
+ defaultWindowingMode = WINDOWING_MODE_FULLSCREEN,
+ extendedDisplayEnabled = false,
+ expectTransition = false,
+ )
+ }
+
+ @Test
+ fun displayWindowingModeSwitchOnDisplayConnected_fullscreenDisplay() {
+ testDisplayWindowingModeSwitch(
+ defaultWindowingMode = WINDOWING_MODE_FULLSCREEN,
+ extendedDisplayEnabled = true,
+ expectTransition = true,
+ )
+ }
+
+ @Test
+ fun displayWindowingModeSwitchOnDisplayConnected_freeformDisplay() {
+ testDisplayWindowingModeSwitch(
+ defaultWindowingMode = WINDOWING_MODE_FREEFORM,
+ extendedDisplayEnabled = true,
+ expectTransition = false,
+ )
+ }
+
+ @Test
+ fun displayWindowingModeSwitch_existingTasksOnConnected() {
+ defaultTDA.configuration.windowConfiguration.windowingMode = WINDOWING_MODE_FULLSCREEN
+ whenever(mockWindowManager.getWindowingMode(anyInt())).thenReturn(WINDOWING_MODE_FULLSCREEN)
+
+ ExtendedDisplaySettingsSession(context.contentResolver, 1).use {
+ connectExternalDisplay()
+
+ val arg = argumentCaptor<WindowContainerTransaction>()
+ verify(transitions, times(1))
+ .startTransition(eq(TRANSIT_CHANGE), arg.capture(), isNull())
+ assertThat(arg.firstValue.changes[freeformTask.token.asBinder()]?.windowingMode)
+ .isEqualTo(WINDOWING_MODE_UNDEFINED)
+ assertThat(arg.firstValue.changes[fullscreenTask.token.asBinder()]?.windowingMode)
+ .isEqualTo(WINDOWING_MODE_FULLSCREEN)
+ }
+ }
+
+ @Test
+ fun displayWindowingModeSwitch_existingTasksOnDisconnected() {
+ defaultTDA.configuration.windowConfiguration.windowingMode = WINDOWING_MODE_FREEFORM
+ whenever(mockWindowManager.getWindowingMode(anyInt())).thenAnswer {
+ WINDOWING_MODE_FULLSCREEN
+ }
+
+ ExtendedDisplaySettingsSession(context.contentResolver, 1).use {
+ disconnectExternalDisplay()
+
+ val arg = argumentCaptor<WindowContainerTransaction>()
+ verify(transitions, times(1))
+ .startTransition(eq(TRANSIT_CHANGE), arg.capture(), isNull())
+ assertThat(arg.firstValue.changes[freeformTask.token.asBinder()]?.windowingMode)
+ .isEqualTo(WINDOWING_MODE_FREEFORM)
+ assertThat(arg.firstValue.changes[fullscreenTask.token.asBinder()]?.windowingMode)
+ .isEqualTo(WINDOWING_MODE_UNDEFINED)
+ }
+ }
+
+ private fun connectExternalDisplay() {
+ whenever(rootTaskDisplayAreaOrganizer.getDisplayIds())
+ .thenReturn(intArrayOf(DEFAULT_DISPLAY, EXTERNAL_DISPLAY_ID))
+ controller.refreshDisplayWindowingMode()
+ }
+
+ private fun disconnectExternalDisplay() {
+ whenever(rootTaskDisplayAreaOrganizer.getDisplayIds())
+ .thenReturn(intArrayOf(DEFAULT_DISPLAY))
+ controller.refreshDisplayWindowingMode()
+ }
+
+ private class ExtendedDisplaySettingsSession(
+ private val contentResolver: ContentResolver,
+ private val overrideValue: Int,
+ ) : AutoCloseable {
+ private val settingName = DEVELOPMENT_FORCE_DESKTOP_MODE_ON_EXTERNAL_DISPLAYS
+ private val initialValue = Settings.Global.getInt(contentResolver, settingName, 0)
+
+ init {
+ Settings.Global.putInt(contentResolver, settingName, overrideValue)
+ }
+
+ override fun close() {
+ Settings.Global.putInt(contentResolver, settingName, initialValue)
+ }
+ }
+
+ private companion object {
+ const val EXTERNAL_DISPLAY_ID = 100
+ }
+}
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopMinimizationTransitionHandlerTest.kt b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopMinimizationTransitionHandlerTest.kt
index 4c3325d4d1de..0d1c57221fb9 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopMinimizationTransitionHandlerTest.kt
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopMinimizationTransitionHandlerTest.kt
@@ -21,6 +21,7 @@ import android.app.WindowConfiguration.ACTIVITY_TYPE_STANDARD
import android.app.WindowConfiguration.WINDOWING_MODE_FREEFORM
import android.app.WindowConfiguration.WINDOWING_MODE_FULLSCREEN
import android.app.WindowConfiguration.WindowingMode
+import android.os.Handler
import android.testing.AndroidTestingRunner
import android.testing.TestableLooper.RunWithLooper
import android.view.SurfaceControl
@@ -56,7 +57,12 @@ class DesktopMinimizationTransitionHandlerTest : ShellTestCase() {
@Before
fun setUp() {
handler =
- DesktopMinimizationTransitionHandler(testExecutor, testExecutor, displayController)
+ DesktopMinimizationTransitionHandler(
+ testExecutor,
+ testExecutor,
+ displayController,
+ mock<Handler>(),
+ )
whenever(displayController.getDisplayContext(any())).thenReturn(mContext)
}
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopRepositoryTest.kt b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopRepositoryTest.kt
index ed9b97d264f7..9bff287e314a 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopRepositoryTest.kt
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopRepositoryTest.kt
@@ -333,7 +333,7 @@ class DesktopRepositoryTest(flags: FlagsParameterization) : ShellTestCase() {
@Test
fun isOnlyVisibleNonClosingTask_singleVisibleClosingTask() {
repo.updateTask(DEFAULT_DISPLAY, taskId = 1, isVisible = true)
- repo.addClosingTask(DEFAULT_DISPLAY, 1)
+ repo.addClosingTask(displayId = DEFAULT_DISPLAY, deskId = 0, taskId = 1)
// A visible task that's closing
assertThat(repo.isVisibleTask(1)).isTrue()
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopTasksControllerTest.kt b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopTasksControllerTest.kt
index fcd92ac2678a..785fb3e875b8 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopTasksControllerTest.kt
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopTasksControllerTest.kt
@@ -2477,8 +2477,7 @@ class DesktopTasksControllerTest(flags: FlagsParameterization) : ShellTestCase()
controller.moveTaskToFront(task.taskId, unminimizeReason = UnminimizeReason.UNKNOWN)
val wct = getLatestDesktopMixedTaskWct(type = TRANSIT_OPEN)
- assertThat(wct.hierarchyOps).hasSize(1)
- wct.assertLaunchTaskAt(0, task.taskId, WINDOWING_MODE_FREEFORM)
+ wct.assertLaunchTask(task.taskId, WINDOWING_MODE_FREEFORM)
}
@Test
@@ -2827,7 +2826,7 @@ class DesktopTasksControllerTest(flags: FlagsParameterization) : ShellTestCase()
fun onDesktopWindowClose_singleActiveTask_isClosing() {
val task = setUpFreeformTask()
- taskRepository.addClosingTask(DEFAULT_DISPLAY, task.taskId)
+ taskRepository.addClosingTask(displayId = DEFAULT_DISPLAY, deskId = 0, taskId = task.taskId)
val wct = WindowContainerTransaction()
controller.onDesktopWindowClose(wct, displayId = DEFAULT_DISPLAY, task)
@@ -2864,7 +2863,11 @@ class DesktopTasksControllerTest(flags: FlagsParameterization) : ShellTestCase()
val task1 = setUpFreeformTask()
val task2 = setUpFreeformTask()
- taskRepository.addClosingTask(DEFAULT_DISPLAY, task2.taskId)
+ taskRepository.addClosingTask(
+ displayId = DEFAULT_DISPLAY,
+ deskId = 0,
+ taskId = task2.taskId,
+ )
val wct = WindowContainerTransaction()
controller.onDesktopWindowClose(wct, displayId = DEFAULT_DISPLAY, task1)
@@ -3225,6 +3228,30 @@ class DesktopTasksControllerTest(flags: FlagsParameterization) : ShellTestCase()
}
@Test
+ @EnableFlags(Flags.FLAG_ENABLE_MULTIPLE_DESKTOPS_BACKEND)
+ fun onDesktopWindowMinimize_minimizesTask() {
+ val task = setUpFreeformTask()
+ val transition = Binder()
+ val runOnTransit = RunOnStartTransitionCallback()
+ whenever(
+ freeformTaskTransitionStarter.startMinimizedModeTransition(
+ any(),
+ anyInt(),
+ anyBoolean(),
+ )
+ )
+ .thenReturn(transition)
+ whenever(mMockDesktopImmersiveController.exitImmersiveIfApplicable(any(), eq(task), any()))
+ .thenReturn(
+ ExitResult.Exit(exitingTask = task.taskId, runOnTransitionStart = runOnTransit)
+ )
+
+ controller.minimizeTask(task, MinimizeReason.MINIMIZE_BUTTON)
+
+ verify(desksOrganizer).minimizeTask(any(), /* deskId= */ eq(0), eq(task))
+ }
+
+ @Test
fun onDesktopWindowMinimize_triesToStopTiling() {
val task = setUpFreeformTask(displayId = DEFAULT_DISPLAY)
val transition = Binder()
@@ -3972,7 +3999,11 @@ class DesktopTasksControllerTest(flags: FlagsParameterization) : ShellTestCase()
val task1 = setUpFreeformTask(displayId = DEFAULT_DISPLAY)
val task2 = setUpFreeformTask(displayId = DEFAULT_DISPLAY)
- taskRepository.addClosingTask(displayId = DEFAULT_DISPLAY, taskId = task2.taskId)
+ taskRepository.addClosingTask(
+ displayId = DEFAULT_DISPLAY,
+ deskId = 0,
+ taskId = task2.taskId,
+ )
val result =
controller.handleRequest(Binder(), createTransition(task1, type = TRANSIT_TO_BACK))
@@ -4083,6 +4114,36 @@ class DesktopTasksControllerTest(flags: FlagsParameterization) : ShellTestCase()
}
@Test
+ @EnableFlags(
+ Flags.FLAG_ENABLE_DESKTOP_WINDOWING_WALLPAPER_ACTIVITY,
+ Flags.FLAG_ENABLE_DESKTOP_WALLPAPER_ACTIVITY_FOR_SYSTEM_USER,
+ Flags.FLAG_ENABLE_MULTIPLE_DESKTOPS_BACKEND,
+ )
+ fun handleRequest_closeTransition_onlyDesktopTask_deactivatesDesk() {
+ val task = setUpFreeformTask()
+
+ controller.handleRequest(Binder(), createTransition(task, type = TRANSIT_CLOSE))
+
+ verify(desksOrganizer).deactivateDesk(any(), /* deskId= */ eq(0))
+ }
+
+ @Test
+ @EnableFlags(
+ Flags.FLAG_ENABLE_DESKTOP_WINDOWING_WALLPAPER_ACTIVITY,
+ Flags.FLAG_ENABLE_DESKTOP_WALLPAPER_ACTIVITY_FOR_SYSTEM_USER,
+ Flags.FLAG_ENABLE_MULTIPLE_DESKTOPS_BACKEND,
+ )
+ fun handleRequest_closeTransition_onlyDesktopTask_addsDeactivatesDeskTransition() {
+ val transition = Binder()
+ val task = setUpFreeformTask()
+
+ controller.handleRequest(transition, createTransition(task, type = TRANSIT_CLOSE))
+
+ verify(desksTransitionsObserver)
+ .addPendingTransition(DeskTransition.DeactivateDesk(token = transition, deskId = 0))
+ }
+
+ @Test
@DisableFlags(Flags.FLAG_ENABLE_DESKTOP_WINDOWING_WALLPAPER_ACTIVITY)
fun handleRequest_closeTransition_multipleTasks_noWallpaper_doesNotHandle() {
val task1 = setUpFreeformTask()
@@ -4115,7 +4176,11 @@ class DesktopTasksControllerTest(flags: FlagsParameterization) : ShellTestCase()
val task1 = setUpFreeformTask(displayId = DEFAULT_DISPLAY)
val task2 = setUpFreeformTask(displayId = DEFAULT_DISPLAY)
- taskRepository.addClosingTask(displayId = DEFAULT_DISPLAY, taskId = task2.taskId)
+ taskRepository.addClosingTask(
+ displayId = DEFAULT_DISPLAY,
+ deskId = 0,
+ taskId = task2.taskId,
+ )
val result =
controller.handleRequest(Binder(), createTransition(task1, type = TRANSIT_CLOSE))
@@ -6240,6 +6305,61 @@ class DesktopTasksControllerTest(flags: FlagsParameterization) : ShellTestCase()
assertThat(taskRepository.getNumberOfDesks(DEFAULT_DISPLAY)).isEqualTo(currentDeskCount + 1)
}
+ @Test
+ @EnableFlags(
+ Flags.FLAG_ENABLE_DESKTOP_WINDOWING_WALLPAPER_ACTIVITY,
+ Flags.FLAG_ENABLE_DESKTOP_WALLPAPER_ACTIVITY_FOR_SYSTEM_USER,
+ )
+ fun startLaunchTransition_desktopNotShowing_movesWallpaperToFront() {
+ val launchingTask = createFreeformTask()
+ val wct = WindowContainerTransaction()
+ wct.reorder(launchingTask.token, /* onTop= */ true)
+ whenever(
+ desktopMixedTransitionHandler.startLaunchTransition(
+ eq(TRANSIT_OPEN),
+ any(),
+ anyOrNull(),
+ anyOrNull(),
+ anyOrNull(),
+ )
+ )
+ .thenReturn(Binder())
+
+ controller.startLaunchTransition(TRANSIT_OPEN, wct, launchingTaskId = null)
+
+ val latestWct = getLatestDesktopMixedTaskWct(type = TRANSIT_OPEN)
+ val launchingTaskReorderIndex = latestWct.indexOfReorder(launchingTask, toTop = true)
+ val wallpaperReorderIndex = latestWct.indexOfReorder(wallpaperToken, toTop = true)
+ assertThat(launchingTaskReorderIndex).isNotEqualTo(-1)
+ assertThat(wallpaperReorderIndex).isNotEqualTo(-1)
+ assertThat(launchingTaskReorderIndex).isGreaterThan(wallpaperReorderIndex)
+ }
+
+ @Test
+ @EnableFlags(
+ Flags.FLAG_ENABLE_DESKTOP_WINDOWING_WALLPAPER_ACTIVITY,
+ Flags.FLAG_ENABLE_DESKTOP_WALLPAPER_ACTIVITY_FOR_SYSTEM_USER,
+ )
+ fun startLaunchTransition_desktopShowing_doesNotReorderWallpaper() {
+ val wct = WindowContainerTransaction()
+ whenever(
+ desktopMixedTransitionHandler.startLaunchTransition(
+ eq(TRANSIT_OPEN),
+ any(),
+ anyOrNull(),
+ anyOrNull(),
+ anyOrNull(),
+ )
+ )
+ .thenReturn(Binder())
+
+ setUpFreeformTask()
+ controller.startLaunchTransition(TRANSIT_OPEN, wct, launchingTaskId = null)
+
+ val latestWct = getLatestDesktopMixedTaskWct(type = TRANSIT_OPEN)
+ assertNull(latestWct.hierarchyOps.find { op -> op.container == wallpaperToken.asBinder() })
+ }
+
private class RunOnStartTransitionCallback : ((IBinder) -> Unit) {
var invocations = 0
private set
@@ -6626,13 +6746,20 @@ private fun WindowContainerTransaction.assertWithoutHop(
}
private fun WindowContainerTransaction.indexOfReorder(
- task: RunningTaskInfo,
+ token: WindowContainerToken,
toTop: Boolean? = null,
): Int {
- val hop = hierarchyOps.singleOrNull(ReorderPredicate(task.token, toTop)) ?: return -1
+ val hop = hierarchyOps.singleOrNull(ReorderPredicate(token, toTop)) ?: return -1
return hierarchyOps.indexOf(hop)
}
+private fun WindowContainerTransaction.indexOfReorder(
+ task: RunningTaskInfo,
+ toTop: Boolean? = null,
+): Int {
+ return indexOfReorder(task.token, toTop)
+}
+
private class ReorderPredicate(val token: WindowContainerToken, val toTop: Boolean? = null) :
((WindowContainerTransaction.HierarchyOp) -> Boolean) {
override fun invoke(hop: WindowContainerTransaction.HierarchyOp): Boolean =
@@ -6750,6 +6877,17 @@ private fun WindowContainerTransaction.assertPendingIntentAt(index: Int, intent:
assertThat(op.pendingIntent?.intent?.categories).isEqualTo(intent.categories)
}
+private fun WindowContainerTransaction.assertLaunchTask(taskId: Int, windowingMode: Int) {
+ val keyLaunchWindowingMode = "android.activity.windowingMode"
+
+ assertHop { hop ->
+ hop.type == HIERARCHY_OP_TYPE_LAUNCH_TASK &&
+ hop.launchOptions?.getInt(LAUNCH_KEY_TASK_ID) == taskId &&
+ hop.launchOptions?.getInt(keyLaunchWindowingMode, WINDOWING_MODE_UNDEFINED) ==
+ windowingMode
+ }
+}
+
private fun WindowContainerTransaction.assertLaunchTaskAt(
index: Int,
taskId: Int,
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopTasksLimiterTest.kt b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopTasksLimiterTest.kt
index d33209dbc30e..62e3c544e390 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopTasksLimiterTest.kt
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopTasksLimiterTest.kt
@@ -37,7 +37,6 @@ import androidx.test.filters.SmallTest
import com.android.dx.mockito.inline.extended.ExtendedMockito
import com.android.dx.mockito.inline.extended.ExtendedMockito.doReturn
import com.android.dx.mockito.inline.extended.StaticMockitoSession
-import com.android.internal.jank.Cuj.CUJ_DESKTOP_MODE_MINIMIZE_WINDOW
import com.android.internal.jank.InteractionJankMonitor
import com.android.window.flags.Flags.FLAG_ENABLE_DESKTOP_WINDOWING_BACK_NAVIGATION
import com.android.wm.shell.ShellTaskOrganizer
@@ -72,8 +71,6 @@ import org.mockito.Mockito.any
import org.mockito.Mockito.mock
import org.mockito.Mockito.spy
import org.mockito.Mockito.`when`
-import org.mockito.kotlin.eq
-import org.mockito.kotlin.verify
import org.mockito.quality.Strictness
/**
@@ -521,85 +518,6 @@ class DesktopTasksLimiterTest : ShellTestCase() {
}
@Test
- fun minimizeTransitionReadyAndFinished_logsJankInstrumentationBeginAndEnd() {
- desktopTaskRepo.addDesk(displayId = DEFAULT_DISPLAY, deskId = 0)
- desktopTaskRepo.setActiveDesk(displayId = DEFAULT_DISPLAY, deskId = 0)
- (1..<MAX_TASK_LIMIT).forEach { _ -> setUpFreeformTask() }
- val transition = Binder()
- val task = setUpFreeformTask()
- addPendingMinimizeChange(transition, taskId = task.taskId)
-
- callOnTransitionReady(
- transition,
- TransitionInfoBuilder(TRANSIT_OPEN).addChange(TRANSIT_TO_BACK, task).build(),
- )
-
- desktopTasksLimiter.getTransitionObserver().onTransitionStarting(transition)
-
- verify(interactionJankMonitor)
- .begin(any(), eq(mContext), eq(handler), eq(CUJ_DESKTOP_MODE_MINIMIZE_WINDOW))
-
- desktopTasksLimiter
- .getTransitionObserver()
- .onTransitionFinished(transition, /* aborted= */ false)
-
- verify(interactionJankMonitor).end(eq(CUJ_DESKTOP_MODE_MINIMIZE_WINDOW))
- }
-
- @Test
- fun minimizeTransitionReadyAndAborted_logsJankInstrumentationBeginAndCancel() {
- desktopTaskRepo.addDesk(displayId = DEFAULT_DISPLAY, deskId = 0)
- desktopTaskRepo.setActiveDesk(displayId = DEFAULT_DISPLAY, deskId = 0)
- (1..<MAX_TASK_LIMIT).forEach { _ -> setUpFreeformTask() }
- val transition = Binder()
- val task = setUpFreeformTask()
- addPendingMinimizeChange(transition, taskId = task.taskId)
-
- callOnTransitionReady(
- transition,
- TransitionInfoBuilder(TRANSIT_OPEN).addChange(TRANSIT_TO_BACK, task).build(),
- )
-
- desktopTasksLimiter.getTransitionObserver().onTransitionStarting(transition)
-
- verify(interactionJankMonitor)
- .begin(any(), eq(mContext), eq(handler), eq(CUJ_DESKTOP_MODE_MINIMIZE_WINDOW))
-
- desktopTasksLimiter
- .getTransitionObserver()
- .onTransitionFinished(transition, /* aborted= */ true)
-
- verify(interactionJankMonitor).cancel(eq(CUJ_DESKTOP_MODE_MINIMIZE_WINDOW))
- }
-
- @Test
- fun minimizeTransitionReadyAndMerged_logsJankInstrumentationBeginAndEnd() {
- desktopTaskRepo.addDesk(displayId = DEFAULT_DISPLAY, deskId = 0)
- desktopTaskRepo.setActiveDesk(displayId = DEFAULT_DISPLAY, deskId = 0)
- (1..<MAX_TASK_LIMIT).forEach { _ -> setUpFreeformTask() }
- val mergedTransition = Binder()
- val newTransition = Binder()
- val task = setUpFreeformTask()
- addPendingMinimizeChange(mergedTransition, taskId = task.taskId)
-
- callOnTransitionReady(
- mergedTransition,
- TransitionInfoBuilder(TRANSIT_OPEN).addChange(TRANSIT_TO_BACK, task).build(),
- )
-
- desktopTasksLimiter.getTransitionObserver().onTransitionStarting(mergedTransition)
-
- verify(interactionJankMonitor)
- .begin(any(), eq(mContext), eq(handler), eq(CUJ_DESKTOP_MODE_MINIMIZE_WINDOW))
-
- desktopTasksLimiter
- .getTransitionObserver()
- .onTransitionMerged(mergedTransition, newTransition)
-
- verify(interactionJankMonitor).end(eq(CUJ_DESKTOP_MODE_MINIMIZE_WINDOW))
- }
-
- @Test
fun getMinimizingTask_noPendingTransition_returnsNull() {
val transition = Binder()
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/multidesks/RootTaskDesksOrganizerTest.kt b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/multidesks/RootTaskDesksOrganizerTest.kt
index 8b10ca1a2a70..96b85ad2729e 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/multidesks/RootTaskDesksOrganizerTest.kt
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/multidesks/RootTaskDesksOrganizerTest.kt
@@ -22,6 +22,7 @@ import android.view.SurfaceControl
import android.view.WindowManager.TRANSIT_TO_FRONT
import android.window.TransitionInfo
import android.window.WindowContainerTransaction
+import android.window.WindowContainerTransaction.Change
import android.window.WindowContainerTransaction.HierarchyOp
import android.window.WindowContainerTransaction.HierarchyOp.HIERARCHY_OP_TYPE_SET_LAUNCH_ROOT
import androidx.test.filters.SmallTest
@@ -29,15 +30,19 @@ import com.android.wm.shell.ShellTaskOrganizer
import com.android.wm.shell.ShellTestCase
import com.android.wm.shell.TestShellExecutor
import com.android.wm.shell.desktopmode.DesktopTestHelpers.createFreeformTask
+import com.android.wm.shell.desktopmode.multidesks.RootTaskDesksOrganizer.DeskMinimizationRoot
import com.android.wm.shell.desktopmode.multidesks.RootTaskDesksOrganizer.DeskRoot
import com.android.wm.shell.sysui.ShellCommandHandler
import com.android.wm.shell.sysui.ShellInit
import com.google.common.truth.Truth.assertThat
+import kotlin.test.assertNotNull
import org.junit.Assert.assertEquals
import org.junit.Assert.assertThrows
import org.junit.Before
import org.junit.Test
import org.junit.runner.RunWith
+import org.mockito.Mockito.verify
+import org.mockito.kotlin.argThat
import org.mockito.kotlin.mock
/**
@@ -75,6 +80,43 @@ class RootTaskDesksOrganizerTest : ShellTestCase() {
}
@Test
+ fun testCreateDesk_createsMinimizationRoot() {
+ val callback = FakeOnCreateCallback()
+ organizer.createDesk(Display.DEFAULT_DISPLAY, callback)
+ val freeformRoot = createFreeformTask().apply { parentTaskId = -1 }
+ organizer.onTaskAppeared(freeformRoot, SurfaceControl())
+
+ val minimizationRootTask = createFreeformTask().apply { parentTaskId = -1 }
+ organizer.onTaskAppeared(minimizationRootTask, SurfaceControl())
+
+ val minimizationRoot = organizer.deskMinimizationRootsByDeskId[freeformRoot.taskId]
+ assertNotNull(minimizationRoot)
+ assertThat(minimizationRoot.deskId).isEqualTo(freeformRoot.taskId)
+ assertThat(minimizationRoot.rootId).isEqualTo(minimizationRootTask.taskId)
+ }
+
+ @Test
+ fun testCreateMinimizationRoot_marksHidden() {
+ organizer.createDesk(Display.DEFAULT_DISPLAY, FakeOnCreateCallback())
+ val freeformRoot = createFreeformTask().apply { parentTaskId = -1 }
+ organizer.onTaskAppeared(freeformRoot, SurfaceControl())
+
+ val minimizationRootTask = createFreeformTask().apply { parentTaskId = -1 }
+ organizer.onTaskAppeared(minimizationRootTask, SurfaceControl())
+
+ verify(mockShellTaskOrganizer)
+ .applyTransaction(
+ argThat { wct ->
+ wct.changes.any { change ->
+ change.key == minimizationRootTask.token.asBinder() &&
+ (change.value.changeMask and Change.CHANGE_HIDDEN != 0) &&
+ change.value.hidden
+ }
+ }
+ )
+ }
+
+ @Test
fun testOnTaskAppeared_withoutRequest_throws() {
val freeformRoot = createFreeformTask().apply { parentTaskId = -1 }
@@ -105,57 +147,122 @@ class RootTaskDesksOrganizerTest : ShellTestCase() {
}
@Test
+ fun testOnTaskAppeared_duplicateMinimizedRoot_throws() {
+ organizer.createDesk(Display.DEFAULT_DISPLAY, FakeOnCreateCallback())
+ val freeformRoot = createFreeformTask().apply { parentTaskId = -1 }
+ val minimizationRootTask = createFreeformTask().apply { parentTaskId = -1 }
+ organizer.onTaskAppeared(freeformRoot, SurfaceControl())
+ organizer.onTaskAppeared(minimizationRootTask, SurfaceControl())
+
+ assertThrows(Exception::class.java) {
+ organizer.onTaskAppeared(minimizationRootTask, SurfaceControl())
+ }
+ }
+
+ @Test
fun testOnTaskVanished_removesRoot() {
val desk = createDesk()
- organizer.onTaskVanished(desk.taskInfo)
+ organizer.onTaskVanished(desk.deskRoot.taskInfo)
+
+ assertThat(organizer.deskRootsByDeskId.contains(desk.deskRoot.deskId)).isFalse()
+ }
+
+ @Test
+ fun testOnTaskVanished_removesMinimizedRoot() {
+ val desk = createDesk()
+
+ organizer.onTaskVanished(desk.deskRoot.taskInfo)
+ organizer.onTaskVanished(desk.minimizationRoot.taskInfo)
- assertThat(organizer.roots.contains(desk.deskId)).isFalse()
+ assertThat(organizer.deskMinimizationRootsByDeskId.contains(desk.deskRoot.deskId)).isFalse()
}
@Test
fun testDesktopWindowAppearsInDesk() {
val desk = createDesk()
- val child = createFreeformTask().apply { parentTaskId = desk.deskId }
+ val child = createFreeformTask().apply { parentTaskId = desk.deskRoot.deskId }
organizer.onTaskAppeared(child, SurfaceControl())
- assertThat(desk.children).contains(child.taskId)
+ assertThat(desk.deskRoot.children).contains(child.taskId)
+ }
+
+ @Test
+ fun testDesktopWindowAppearsInDeskMinimizationRoot() {
+ val desk = createDesk()
+ val child = createFreeformTask().apply { parentTaskId = desk.minimizationRoot.rootId }
+
+ organizer.onTaskAppeared(child, SurfaceControl())
+
+ assertThat(desk.minimizationRoot.children).contains(child.taskId)
+ }
+
+ @Test
+ fun testDesktopWindowMovesToMinimizationRoot() {
+ val desk = createDesk()
+ val child = createFreeformTask().apply { parentTaskId = desk.deskRoot.deskId }
+ organizer.onTaskAppeared(child, SurfaceControl())
+
+ child.parentTaskId = desk.minimizationRoot.rootId
+ organizer.onTaskInfoChanged(child)
+
+ assertThat(desk.deskRoot.children).doesNotContain(child.taskId)
+ assertThat(desk.minimizationRoot.children).contains(child.taskId)
}
@Test
fun testDesktopWindowDisappearsFromDesk() {
val desk = createDesk()
- val child = createFreeformTask().apply { parentTaskId = desk.deskId }
+ val child = createFreeformTask().apply { parentTaskId = desk.deskRoot.deskId }
organizer.onTaskAppeared(child, SurfaceControl())
organizer.onTaskVanished(child)
- assertThat(desk.children).doesNotContain(child.taskId)
+ assertThat(desk.deskRoot.children).doesNotContain(child.taskId)
}
@Test
- fun testRemoveDesk() {
+ fun testDesktopWindowDisappearsFromDeskMinimizationRoot() {
+ val desk = createDesk()
+ val child = createFreeformTask().apply { parentTaskId = desk.minimizationRoot.rootId }
+
+ organizer.onTaskAppeared(child, SurfaceControl())
+ organizer.onTaskVanished(child)
+
+ assertThat(desk.minimizationRoot.children).doesNotContain(child.taskId)
+ }
+
+ @Test
+ fun testRemoveDesk_removesDeskRoot() {
val desk = createDesk()
val wct = WindowContainerTransaction()
- organizer.removeDesk(wct, desk.deskId)
+ organizer.removeDesk(wct, desk.deskRoot.deskId)
assertThat(
wct.hierarchyOps.any { hop ->
hop.type == HierarchyOp.HIERARCHY_OP_TYPE_REMOVE_ROOT_TASK &&
- hop.container == desk.taskInfo.token.asBinder()
+ hop.container == desk.deskRoot.token.asBinder()
}
)
.isTrue()
}
@Test
- fun testRemoveDesk_didNotExist_throws() {
- val freeformRoot = createFreeformTask().apply { parentTaskId = -1 }
+ fun testRemoveDesk_removesMinimizationRoot() {
+ val desk = createDesk()
val wct = WindowContainerTransaction()
- assertThrows(Exception::class.java) { organizer.removeDesk(wct, freeformRoot.taskId) }
+ organizer.removeDesk(wct, desk.deskRoot.deskId)
+
+ assertThat(
+ wct.hierarchyOps.any { hop ->
+ hop.type == HierarchyOp.HIERARCHY_OP_TYPE_REMOVE_ROOT_TASK &&
+ hop.container == desk.minimizationRoot.token.asBinder()
+ }
+ )
+ .isTrue()
}
@Test
@@ -163,20 +270,20 @@ class RootTaskDesksOrganizerTest : ShellTestCase() {
val desk = createDesk()
val wct = WindowContainerTransaction()
- organizer.activateDesk(wct, desk.deskId)
+ organizer.activateDesk(wct, desk.deskRoot.deskId)
assertThat(
wct.hierarchyOps.any { hop ->
hop.type == HierarchyOp.HIERARCHY_OP_TYPE_REORDER &&
hop.toTop &&
- hop.container == desk.taskInfo.token.asBinder()
+ hop.container == desk.deskRoot.taskInfo.token.asBinder()
}
)
.isTrue()
assertThat(
wct.hierarchyOps.any { hop ->
hop.type == HierarchyOp.HIERARCHY_OP_TYPE_SET_LAUNCH_ROOT &&
- hop.container == desk.taskInfo.token.asBinder()
+ hop.container == desk.deskRoot.taskInfo.token.asBinder()
}
)
.isTrue()
@@ -196,14 +303,14 @@ class RootTaskDesksOrganizerTest : ShellTestCase() {
val desktopTask = createFreeformTask().apply { parentTaskId = -1 }
val wct = WindowContainerTransaction()
- organizer.moveTaskToDesk(wct, desk.deskId, desktopTask)
+ organizer.moveTaskToDesk(wct, desk.deskRoot.deskId, desktopTask)
assertThat(
wct.hierarchyOps.any { hop ->
hop.isReparent &&
hop.toTop &&
hop.container == desktopTask.token.asBinder() &&
- hop.newParent == desk.taskInfo.token.asBinder()
+ hop.newParent == desk.deskRoot.taskInfo.token.asBinder()
}
)
.isTrue()
@@ -231,13 +338,26 @@ class RootTaskDesksOrganizerTest : ShellTestCase() {
fun testGetDeskAtEnd() {
val desk = createDesk()
- val task = createFreeformTask().apply { parentTaskId = desk.deskId }
+ val task = createFreeformTask().apply { parentTaskId = desk.deskRoot.deskId }
+ val endDesk =
+ organizer.getDeskAtEnd(
+ TransitionInfo.Change(task.token, SurfaceControl()).apply { taskInfo = task }
+ )
+
+ assertThat(endDesk).isEqualTo(desk.deskRoot.deskId)
+ }
+
+ @Test
+ fun testGetDeskAtEnd_inMinimizationRoot() {
+ val desk = createDesk()
+
+ val task = createFreeformTask().apply { parentTaskId = desk.minimizationRoot.rootId }
val endDesk =
organizer.getDeskAtEnd(
TransitionInfo.Change(task.token, SurfaceControl()).apply { taskInfo = task }
)
- assertThat(endDesk).isEqualTo(desk.deskId)
+ assertThat(endDesk).isEqualTo(desk.deskRoot.deskId)
}
@Test
@@ -264,14 +384,14 @@ class RootTaskDesksOrganizerTest : ShellTestCase() {
fun deactivateDesk_clearsLaunchRoot() {
val wct = WindowContainerTransaction()
val desk = createDesk()
- organizer.activateDesk(wct, desk.deskId)
+ organizer.activateDesk(wct, desk.deskRoot.deskId)
- organizer.deactivateDesk(wct, desk.deskId)
+ organizer.deactivateDesk(wct, desk.deskRoot.deskId)
assertThat(
wct.hierarchyOps.any { hop ->
hop.type == HIERARCHY_OP_TYPE_SET_LAUNCH_ROOT &&
- hop.container == desk.taskInfo.token.asBinder() &&
+ hop.container == desk.deskRoot.taskInfo.token.asBinder() &&
hop.windowingModes == null &&
hop.activityTypes == null
}
@@ -280,25 +400,129 @@ class RootTaskDesksOrganizerTest : ShellTestCase() {
}
@Test
- fun isDeskChange() {
+ fun isDeskChange_forDeskId() {
val desk = createDesk()
assertThat(
organizer.isDeskChange(
- TransitionInfo.Change(desk.taskInfo.token, desk.leash).apply {
- taskInfo = desk.taskInfo
+ TransitionInfo.Change(desk.deskRoot.taskInfo.token, desk.deskRoot.leash).apply {
+ taskInfo = desk.deskRoot.taskInfo
},
- desk.deskId,
+ desk.deskRoot.deskId,
+ )
+ )
+ .isTrue()
+ }
+
+ @Test
+ fun isDeskChange_forDeskId_inMinimizationRoot() {
+ val desk = createDesk()
+
+ assertThat(
+ organizer.isDeskChange(
+ change =
+ TransitionInfo.Change(
+ desk.minimizationRoot.token,
+ desk.minimizationRoot.leash,
+ )
+ .apply { taskInfo = desk.minimizationRoot.taskInfo },
+ deskId = desk.deskRoot.deskId,
+ )
+ )
+ .isTrue()
+ }
+
+ @Test
+ fun isDeskChange_anyDesk() {
+ val desk = createDesk()
+
+ assertThat(
+ organizer.isDeskChange(
+ change =
+ TransitionInfo.Change(desk.deskRoot.taskInfo.token, desk.deskRoot.leash)
+ .apply { taskInfo = desk.deskRoot.taskInfo }
+ )
+ )
+ .isTrue()
+ }
+
+ @Test
+ fun isDeskChange_anyDesk_inMinimizationRoot() {
+ val desk = createDesk()
+
+ assertThat(
+ organizer.isDeskChange(
+ change =
+ TransitionInfo.Change(
+ desk.minimizationRoot.taskInfo.token,
+ desk.minimizationRoot.leash,
+ )
+ .apply { taskInfo = desk.minimizationRoot.taskInfo }
)
)
.isTrue()
}
- private fun createDesk(): DeskRoot {
+ @Test
+ fun minimizeTask() {
+ val desk = createDesk()
+ val task = createFreeformTask().apply { parentTaskId = desk.deskRoot.deskId }
+ val wct = WindowContainerTransaction()
+ organizer.moveTaskToDesk(wct, desk.deskRoot.deskId, task)
+ organizer.onTaskAppeared(task, SurfaceControl())
+
+ organizer.minimizeTask(wct, deskId = desk.deskRoot.deskId, task)
+
+ assertThat(
+ wct.hierarchyOps.any { hop ->
+ hop.isReparent &&
+ hop.container == task.token.asBinder() &&
+ hop.newParent == desk.minimizationRoot.token.asBinder()
+ }
+ )
+ .isTrue()
+ }
+
+ @Test
+ fun minimizeTask_alreadyMinimized_noOp() {
+ val desk = createDesk()
+ val task = createFreeformTask().apply { parentTaskId = desk.minimizationRoot.rootId }
+ val wct = WindowContainerTransaction()
+ organizer.onTaskAppeared(task, SurfaceControl())
+
+ organizer.minimizeTask(wct, deskId = desk.deskRoot.deskId, task)
+
+ assertThat(wct.isEmpty).isTrue()
+ }
+
+ @Test
+ fun minimizeTask_inDifferentDesk_noOp() {
+ val desk = createDesk()
+ val otherDesk = createDesk()
+ val task = createFreeformTask().apply { parentTaskId = otherDesk.deskRoot.deskId }
+ val wct = WindowContainerTransaction()
+ organizer.onTaskAppeared(task, SurfaceControl())
+
+ organizer.minimizeTask(wct, deskId = desk.deskRoot.deskId, task)
+
+ assertThat(wct.isEmpty).isTrue()
+ }
+
+ private data class DeskRoots(
+ val deskRoot: DeskRoot,
+ val minimizationRoot: DeskMinimizationRoot,
+ )
+
+ private fun createDesk(): DeskRoots {
organizer.createDesk(Display.DEFAULT_DISPLAY, FakeOnCreateCallback())
val freeformRoot = createFreeformTask().apply { parentTaskId = -1 }
organizer.onTaskAppeared(freeformRoot, SurfaceControl())
- return organizer.roots[freeformRoot.taskId]
+ val minimizationRoot = createFreeformTask().apply { parentTaskId = -1 }
+ organizer.onTaskAppeared(minimizationRoot, SurfaceControl())
+ return DeskRoots(
+ organizer.deskRootsByDeskId[freeformRoot.taskId],
+ checkNotNull(organizer.deskMinimizationRootsByDeskId[freeformRoot.taskId]),
+ )
}
private class FakeOnCreateCallback : DesksOrganizer.OnCreateCallback {
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/recents/RecentsTransitionHandlerTest.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/recents/RecentsTransitionHandlerTest.java
index fd5e567f69ed..93dd3456f3f2 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/recents/RecentsTransitionHandlerTest.java
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/recents/RecentsTransitionHandlerTest.java
@@ -17,16 +17,20 @@
package com.android.wm.shell.recents;
import static android.app.WindowConfiguration.ACTIVITY_TYPE_HOME;
+import static android.app.WindowConfiguration.ACTIVITY_TYPE_STANDARD;
import static android.app.WindowConfiguration.WINDOWING_MODE_FREEFORM;
import static android.view.WindowManager.TRANSIT_CLOSE;
import static android.view.WindowManager.TRANSIT_OPEN;
+import static android.view.WindowManager.TRANSIT_SLEEP;
import static android.view.WindowManager.TRANSIT_TO_FRONT;
import static com.android.dx.mockito.inline.extended.ExtendedMockito.mockitoSession;
import static com.android.window.flags.Flags.FLAG_ENABLE_DESKTOP_RECENTS_TRANSITIONS_CORNERS_BUGFIX;
+import static com.android.wm.shell.Flags.FLAG_ENABLE_RECENTS_BOOKEND_TRANSITION;
import static com.android.wm.shell.recents.RecentsTransitionStateListener.TRANSITION_STATE_ANIMATING;
import static com.android.wm.shell.recents.RecentsTransitionStateListener.TRANSITION_STATE_NOT_RUNNING;
import static com.android.wm.shell.recents.RecentsTransitionStateListener.TRANSITION_STATE_REQUESTED;
+import static com.android.wm.shell.transition.Transitions.TRANSIT_END_RECENTS_TRANSITION;
import static com.android.wm.shell.transition.Transitions.TRANSIT_START_RECENTS_TRANSITION;
import static com.google.common.truth.Truth.assertThat;
@@ -64,7 +68,6 @@ import androidx.test.runner.AndroidJUnit4;
import com.android.dx.mockito.inline.extended.ExtendedMockito;
import com.android.dx.mockito.inline.extended.StaticMockitoSession;
import com.android.internal.os.IResultReceiver;
-import com.android.wm.shell.R;
import com.android.wm.shell.ShellTaskOrganizer;
import com.android.wm.shell.ShellTestCase;
import com.android.wm.shell.TestRunningTaskInfoBuilder;
@@ -73,6 +76,7 @@ import com.android.wm.shell.common.DisplayInsetsController;
import com.android.wm.shell.common.TaskStackListenerImpl;
import com.android.wm.shell.desktopmode.DesktopRepository;
import com.android.wm.shell.desktopmode.DesktopUserRepositories;
+import com.android.wm.shell.shared.R;
import com.android.wm.shell.shared.desktopmode.DesktopModeStatus;
import com.android.wm.shell.sysui.ShellCommandHandler;
import com.android.wm.shell.sysui.ShellController;
@@ -308,8 +312,7 @@ public class RecentsTransitionHandlerTest extends ShellTestCase {
mRecentsTransitionHandler.findController(transition).merge(
mergeTransitionInfo,
new StubTransaction(),
- finishT,
- transition,
+ new StubTransaction(),
mock(Transitions.TransitionFinishCallback.class));
mMainExecutor.flushAll();
@@ -318,6 +321,69 @@ public class RecentsTransitionHandlerTest extends ShellTestCase {
}
@Test
+ @EnableFlags(FLAG_ENABLE_RECENTS_BOOKEND_TRANSITION)
+ public void testMerge_consumeBookendTransition() throws Exception {
+ // Start and finish the transition
+ final IRecentsAnimationRunner animationRunner = mock(IRecentsAnimationRunner.class);
+ final IBinder transition = startRecentsTransition(/* synthetic= */ false, animationRunner);
+ mRecentsTransitionHandler.startAnimation(
+ transition, createTransitionInfo(), new StubTransaction(), new StubTransaction(),
+ mock(Transitions.TransitionFinishCallback.class));
+ mRecentsTransitionHandler.findController(transition).finish(/* toHome= */ false,
+ false /* sendUserLeaveHint */, mock(IResultReceiver.class));
+ mMainExecutor.flushAll();
+
+ // Merge the bookend transition
+ TransitionInfo mergeTransitionInfo =
+ new TransitionInfoBuilder(TRANSIT_END_RECENTS_TRANSITION)
+ .addChange(TRANSIT_OPEN, new TestRunningTaskInfoBuilder().build())
+ .build();
+ SurfaceControl.Transaction finishT = mock(SurfaceControl.Transaction.class);
+ Transitions.TransitionFinishCallback finishCallback
+ = mock(Transitions.TransitionFinishCallback.class);
+ mRecentsTransitionHandler.findController(transition).merge(
+ mergeTransitionInfo,
+ new StubTransaction(),
+ finishT,
+ finishCallback);
+ mMainExecutor.flushAll();
+
+ // Verify that we've merged
+ verify(finishCallback).onTransitionFinished(any());
+ }
+
+ @Test
+ @EnableFlags(FLAG_ENABLE_RECENTS_BOOKEND_TRANSITION)
+ public void testMerge_pendingBookendTransition_mergesTransition() throws Exception {
+ // Start and finish the transition
+ final IRecentsAnimationRunner animationRunner = mock(IRecentsAnimationRunner.class);
+ final IBinder transition = startRecentsTransition(/* synthetic= */ false, animationRunner);
+ mRecentsTransitionHandler.startAnimation(
+ transition, createTransitionInfo(), new StubTransaction(), new StubTransaction(),
+ mock(Transitions.TransitionFinishCallback.class));
+ mRecentsTransitionHandler.findController(transition).finish(/* toHome= */ false,
+ false /* sendUserLeaveHint */, mock(IResultReceiver.class));
+ mMainExecutor.flushAll();
+
+ // Merge a new transition while we have a pending finish
+ TransitionInfo mergeTransitionInfo = new TransitionInfoBuilder(TRANSIT_OPEN)
+ .addChange(TRANSIT_OPEN, new TestRunningTaskInfoBuilder().build())
+ .build();
+ SurfaceControl.Transaction finishT = mock(SurfaceControl.Transaction.class);
+ Transitions.TransitionFinishCallback finishCallback
+ = mock(Transitions.TransitionFinishCallback.class);
+ mRecentsTransitionHandler.findController(transition).merge(
+ mergeTransitionInfo,
+ new StubTransaction(),
+ finishT,
+ finishCallback);
+ mMainExecutor.flushAll();
+
+ // Verify that we've cleaned up the original transition
+ assertNull(mRecentsTransitionHandler.findController(transition));
+ }
+
+ @Test
@EnableFlags(FLAG_ENABLE_DESKTOP_RECENTS_TRANSITIONS_CORNERS_BUGFIX)
public void testMergeAndFinish_openingFreeformTasks_setsCornerRadius() {
ActivityManager.RunningTaskInfo freeformTask =
@@ -336,7 +402,6 @@ public class RecentsTransitionHandlerTest extends ShellTestCase {
mergeTransitionInfo,
new StubTransaction(),
finishT,
- transition,
mock(Transitions.TransitionFinishCallback.class));
mRecentsTransitionHandler.findController(transition).finish(/* toHome= */ false,
false /* sendUserLeaveHint */, mock(IResultReceiver.class));
@@ -385,15 +450,23 @@ public class RecentsTransitionHandlerTest extends ShellTestCase {
}
private TransitionInfo createTransitionInfo() {
- final ActivityManager.RunningTaskInfo task = new TestRunningTaskInfoBuilder()
+ final ActivityManager.RunningTaskInfo homeTask = new TestRunningTaskInfoBuilder()
.setTopActivityType(ACTIVITY_TYPE_HOME)
.build();
+ final ActivityManager.RunningTaskInfo appTask = new TestRunningTaskInfoBuilder()
+ .setTopActivityType(ACTIVITY_TYPE_STANDARD)
+ .build();
final TransitionInfo.Change homeChange = new TransitionInfo.Change(
- task.token, new SurfaceControl());
+ homeTask.token, new SurfaceControl());
homeChange.setMode(TRANSIT_TO_FRONT);
- homeChange.setTaskInfo(task);
+ homeChange.setTaskInfo(homeTask);
+ final TransitionInfo.Change appChange = new TransitionInfo.Change(
+ appTask.token, new SurfaceControl());
+ appChange.setMode(TRANSIT_TO_FRONT);
+ appChange.setTaskInfo(appTask);
return new TransitionInfoBuilder(TRANSIT_START_RECENTS_TRANSITION)
.addChange(homeChange)
+ .addChange(appChange)
.build();
}
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/shared/animation/MinimizeAnimatorTest.kt b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/shared/animation/MinimizeAnimatorTest.kt
new file mode 100644
index 000000000000..ba609d4322fc
--- /dev/null
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/shared/animation/MinimizeAnimatorTest.kt
@@ -0,0 +1,118 @@
+/*
+ * Copyright (C) 2025 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.shared.animation
+
+import android.animation.Animator
+import android.animation.AnimatorSet
+import android.animation.ValueAnimator
+import android.content.Context
+import android.content.res.Resources
+import android.os.Handler
+import android.util.DisplayMetrics
+import android.view.SurfaceControl
+import android.view.SurfaceControl.Transaction
+import android.window.TransitionInfo
+import android.window.WindowContainerToken
+import androidx.test.ext.junit.runners.AndroidJUnit4
+import androidx.test.filters.SmallTest
+import androidx.test.internal.runner.junit4.statement.UiThreadStatement.runOnUiThread
+import com.android.internal.jank.Cuj.CUJ_DESKTOP_MODE_MINIMIZE_WINDOW
+import com.android.internal.jank.InteractionJankMonitor
+import com.google.common.truth.Truth.assertThat
+import org.junit.Before
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.mockito.ArgumentMatchers.any
+import org.mockito.ArgumentMatchers.anyFloat
+import org.mockito.ArgumentMatchers.anyLong
+import org.mockito.kotlin.mock
+import org.mockito.kotlin.never
+import org.mockito.kotlin.verify
+import org.mockito.kotlin.whenever
+
+@SmallTest
+@RunWith(AndroidJUnit4::class)
+class MinimizeAnimatorTest {
+ private val context = mock<Context>()
+ private val resources = mock<Resources>()
+ private val transaction = mock<Transaction>()
+ private val leash = mock<SurfaceControl>()
+ private val interactionJankMonitor = mock<InteractionJankMonitor>()
+ private val animationHandler = mock<Handler>()
+
+ private val displayMetrics = DisplayMetrics().apply { density = 1f }
+
+ @Before
+ fun setup() {
+ whenever(context.resources).thenReturn(resources)
+ whenever(resources.displayMetrics).thenReturn(displayMetrics)
+ whenever(transaction.setAlpha(any(), anyFloat())).thenReturn(transaction)
+ whenever(transaction.setPosition(any(), anyFloat(), anyFloat())).thenReturn(transaction)
+ whenever(transaction.setScale(any(), anyFloat(), anyFloat())).thenReturn(transaction)
+ whenever(transaction.setFrameTimeline(anyLong())).thenReturn(transaction)
+ }
+
+ @Test
+ fun create_returnsBoundsAndAlphaAnimators() {
+ val change = TransitionInfo.Change(mock<WindowContainerToken>(), leash)
+
+ val animator = createAnimator(change)
+
+ assertThat(animator).isInstanceOf(AnimatorSet::class.java)
+ val animatorSet = animator as AnimatorSet
+ assertThat(animatorSet.childAnimations).hasSize(2)
+ assertIsBoundsAnimator(animatorSet.childAnimations[0])
+ assertIsAlphaAnimator(animatorSet.childAnimations[1])
+ }
+
+ @Test
+ fun create_doesNotlogJankInstrumentation() = runOnUiThread {
+ val change = TransitionInfo.Change(mock<WindowContainerToken>(), leash)
+
+ createAnimator(change)
+
+ verify(interactionJankMonitor, never()).begin(
+ leash, context, animationHandler, CUJ_DESKTOP_MODE_MINIMIZE_WINDOW)
+ }
+
+ @Test
+ fun onAnimationStart_logsJankInstrumentation() = runOnUiThread {
+ val change = TransitionInfo.Change(mock<WindowContainerToken>(), leash)
+
+ createAnimator(change).start()
+
+ verify(interactionJankMonitor).begin(
+ leash, context, animationHandler, CUJ_DESKTOP_MODE_MINIMIZE_WINDOW)
+ }
+
+ private fun createAnimator(change: TransitionInfo.Change): Animator =
+ MinimizeAnimator.create(context, change, transaction, {}, interactionJankMonitor,
+ animationHandler)
+
+ private fun assertIsBoundsAnimator(animator: Animator) {
+ assertThat(animator).isInstanceOf(ValueAnimator::class.java)
+ assertThat(animator.duration).isEqualTo(200)
+ assertThat(animator.interpolator).isEqualTo(Interpolators.STANDARD_ACCELERATE)
+ }
+
+ private fun assertIsAlphaAnimator(animator: Animator) {
+ assertThat(animator).isInstanceOf(ValueAnimator::class.java)
+ assertThat(animator.duration).isEqualTo(100)
+ assertThat(animator.interpolator).isEqualTo(Interpolators.LINEAR)
+ }
+}
+
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/shared/desktopmode/DesktopModeCompatPolicyTest.kt b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/shared/desktopmode/DesktopModeCompatPolicyTest.kt
index f69bf34ea3f7..88c6e499b869 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/shared/desktopmode/DesktopModeCompatPolicyTest.kt
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/shared/desktopmode/DesktopModeCompatPolicyTest.kt
@@ -16,13 +16,16 @@
package com.android.wm.shell.shared.desktopmode
+import android.Manifest.permission.SYSTEM_ALERT_WINDOW
import android.app.TaskInfo
import android.compat.testing.PlatformCompatChangeRule
import android.content.ComponentName
import android.content.pm.ActivityInfo
import android.content.pm.ApplicationInfo
+import android.content.pm.PackageInfo
import android.content.pm.PackageManager
import android.os.Process
+import android.platform.test.annotations.DisableFlags
import android.platform.test.annotations.EnableFlags
import android.testing.AndroidTestingRunner
import androidx.test.filters.SmallTest
@@ -39,7 +42,9 @@ import org.junit.Before
import org.junit.Rule
import org.junit.Test
import org.junit.runner.RunWith
+import org.mockito.ArgumentMatchers.anyString
import org.mockito.kotlin.any
+import org.mockito.kotlin.eq
import org.mockito.kotlin.mock
import org.mockito.kotlin.whenever
@@ -55,6 +60,7 @@ class DesktopModeCompatPolicyTest : ShellTestCase() {
private lateinit var desktopModeCompatPolicy: DesktopModeCompatPolicy
private val packageManager: PackageManager = mock()
private val homeActivities = ComponentName(HOME_LAUNCHER_PACKAGE_NAME, /* class */ "")
+ private val baseActivityTest = ComponentName("com.test.dummypackage", "TestClass")
@Before
fun setUp() {
@@ -64,6 +70,7 @@ class DesktopModeCompatPolicyTest : ShellTestCase() {
}
@Test
+ @DisableFlags(Flags.FLAG_ENABLE_MODALS_FULLSCREEN_WITH_PERMISSION)
fun testIsTopActivityExemptFromDesktopWindowing_onlyTransparentActivitiesInStack() {
assertTrue(desktopModeCompatPolicy.isTopActivityExemptFromDesktopWindowing(
createFreeformTask(/* displayId */ 0)
@@ -71,10 +78,39 @@ class DesktopModeCompatPolicyTest : ShellTestCase() {
isActivityStackTransparent = true
isTopActivityNoDisplay = false
numActivities = 1
+ baseActivity = baseActivityTest
}))
}
@Test
+ @EnableFlags(Flags.FLAG_ENABLE_MODALS_FULLSCREEN_WITH_PERMISSION)
+ fun testIsTopActivityExemptWithPermission_onlyTransparentActivitiesInStack() {
+ allowOverlayPermission(arrayOf(SYSTEM_ALERT_WINDOW))
+ assertTrue(desktopModeCompatPolicy.isTopActivityExemptFromDesktopWindowing(
+ createFreeformTask(/* displayId */ 0)
+ .apply {
+ isActivityStackTransparent = true
+ isTopActivityNoDisplay = false
+ numActivities = 1
+ baseActivity = baseActivityTest
+ }))
+ }
+
+ @Test
+ @EnableFlags(Flags.FLAG_ENABLE_MODALS_FULLSCREEN_WITH_PERMISSION)
+ fun testIsTopActivityExemptWithNoPermission_onlyTransparentActivitiesInStack() {
+ allowOverlayPermission(arrayOf())
+ assertFalse(desktopModeCompatPolicy.isTopActivityExemptFromDesktopWindowing(
+ createFreeformTask(/* displayId */ 0)
+ .apply {
+ isActivityStackTransparent = true
+ isTopActivityNoDisplay = false
+ numActivities = 1
+ baseActivity = baseActivityTest
+ }))
+ }
+
+ @Test
fun testIsTopActivityExemptFromDesktopWindowing_noActivitiesInStack() {
assertFalse(desktopModeCompatPolicy.isTopActivityExemptFromDesktopWindowing(
createFreeformTask(/* displayId */ 0)
@@ -219,4 +255,15 @@ class DesktopModeCompatPolicyTest : ShellTestCase() {
}
}
}
+
+ fun allowOverlayPermission(permissions: Array<String>) {
+ val packageInfo = mock<PackageInfo>()
+ packageInfo.requestedPermissions = permissions
+ whenever(
+ packageManager.getPackageInfo(
+ anyString(),
+ eq(PackageManager.GET_PERMISSIONS)
+ )
+ ).thenReturn(packageInfo)
+ }
}
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/transition/HomeTransitionObserverTest.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/transition/HomeTransitionObserverTest.java
index 6f28e656d060..3099b0f5cf66 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/transition/HomeTransitionObserverTest.java
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/transition/HomeTransitionObserverTest.java
@@ -17,36 +17,44 @@
package com.android.wm.shell.transition;
import static android.app.WindowConfiguration.ACTIVITY_TYPE_HOME;
+import static android.app.WindowConfiguration.ACTIVITY_TYPE_STANDARD;
import static android.app.WindowConfiguration.ACTIVITY_TYPE_UNDEFINED;
import static android.view.WindowManager.TRANSIT_CHANGE;
import static android.view.WindowManager.TRANSIT_OPEN;
import static android.view.WindowManager.TRANSIT_PREPARE_BACK_NAVIGATION;
import static android.view.WindowManager.TRANSIT_TO_BACK;
+import static android.view.WindowManager.TRANSIT_TO_FRONT;
import static android.window.TransitionInfo.FLAG_BACK_GESTURE_ANIMATED;
import static com.android.dx.mockito.inline.extended.ExtendedMockito.verify;
import static com.android.wm.shell.desktopmode.DesktopModeTransitionTypes.TRANSIT_DESKTOP_MODE_START_DRAG_TO_DESKTOP;
+import static com.android.wm.shell.transition.Transitions.TRANSIT_CONVERT_TO_BUBBLE;
import static org.mockito.ArgumentMatchers.anyBoolean;
import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.never;
import static org.mockito.Mockito.times;
import static org.mockito.Mockito.when;
import android.app.ActivityManager;
import android.app.WindowConfiguration.ActivityType;
import android.content.Context;
+import android.os.Binder;
import android.os.Handler;
import android.os.IBinder;
import android.os.Looper;
import android.os.RemoteException;
+import android.platform.test.annotations.EnableFlags;
import android.view.SurfaceControl;
import android.window.TransitionInfo;
import android.window.TransitionInfo.TransitionMode;
+import android.window.WindowContainerToken;
import androidx.test.ext.junit.runners.AndroidJUnit4;
import androidx.test.filters.SmallTest;
import androidx.test.platform.app.InstrumentationRegistry;
+import com.android.wm.shell.Flags;
import com.android.wm.shell.ShellTaskOrganizer;
import com.android.wm.shell.ShellTestCase;
import com.android.wm.shell.TestShellExecutor;
@@ -188,6 +196,72 @@ public class HomeTransitionObserverTest extends ShellTestCase {
}
@Test
+ @EnableFlags({Flags.FLAG_ENABLE_BUBBLE_TO_FULLSCREEN, Flags.FLAG_ENABLE_CREATE_ANY_BUBBLE})
+ public void testDragTaskToBubbleOverHome_notifiesHomeIsVisible() throws RemoteException {
+ ActivityManager.RunningTaskInfo homeTask = createTaskInfo(1, ACTIVITY_TYPE_HOME);
+ ActivityManager.RunningTaskInfo bubbleTask = createTaskInfo(2, ACTIVITY_TYPE_STANDARD);
+
+ TransitionInfo startDragTransition =
+ new TransitionInfoBuilder(TRANSIT_DESKTOP_MODE_START_DRAG_TO_DESKTOP)
+ .addChange(TRANSIT_TO_FRONT, homeTask)
+ .addChange(TRANSIT_TO_BACK, bubbleTask)
+ .build();
+
+ // Start drag to desktop which brings home to front
+ mHomeTransitionObserver.onTransitionReady(new Binder(), startDragTransition,
+ MockTransactionPool.create(), MockTransactionPool.create());
+ // Does not notify home visibility yet
+ verify(mListener, never()).onHomeVisibilityChanged(anyBoolean());
+
+ TransitionInfo convertToBubbleTransition =
+ new TransitionInfoBuilder(TRANSIT_CONVERT_TO_BUBBLE)
+ .addChange(TRANSIT_TO_FRONT, bubbleTask)
+ .build();
+
+ // Convert to bubble. Transition does not include changes for home task
+ mHomeTransitionObserver.onTransitionReady(new Binder(), convertToBubbleTransition,
+ MockTransactionPool.create(), MockTransactionPool.create());
+
+ // Notifies home visibility change that was pending from the start of drag
+ verify(mListener).onHomeVisibilityChanged(true);
+ }
+
+ @Test
+ @EnableFlags({Flags.FLAG_ENABLE_BUBBLE_TO_FULLSCREEN, Flags.FLAG_ENABLE_CREATE_ANY_BUBBLE})
+ public void testDragTaskToBubbleOverOtherTask_notifiesHomeIsNotVisible()
+ throws RemoteException {
+ ActivityManager.RunningTaskInfo homeTask = createTaskInfo(1, ACTIVITY_TYPE_HOME);
+ ActivityManager.RunningTaskInfo bubbleTask = createTaskInfo(2, ACTIVITY_TYPE_STANDARD);
+ ActivityManager.RunningTaskInfo otherTask = createTaskInfo(3, ACTIVITY_TYPE_STANDARD);
+
+ TransitionInfo startDragTransition =
+ new TransitionInfoBuilder(TRANSIT_DESKTOP_MODE_START_DRAG_TO_DESKTOP)
+ .addChange(TRANSIT_TO_FRONT, homeTask)
+ .addChange(TRANSIT_TO_BACK, bubbleTask)
+ .build();
+
+ // Start drag to desktop which brings home to front
+ mHomeTransitionObserver.onTransitionReady(new Binder(), startDragTransition,
+ MockTransactionPool.create(), MockTransactionPool.create());
+ // Does not notify home visibility yet
+ verify(mListener, never()).onHomeVisibilityChanged(anyBoolean());
+
+ TransitionInfo convertToBubbleTransition =
+ new TransitionInfoBuilder(TRANSIT_CONVERT_TO_BUBBLE)
+ .addChange(TRANSIT_TO_FRONT, bubbleTask)
+ .addChange(TRANSIT_TO_FRONT, otherTask)
+ .addChange(TRANSIT_TO_BACK, homeTask)
+ .build();
+
+ // Convert to bubble. Transition includes home task to back which updates home visibility
+ mHomeTransitionObserver.onTransitionReady(new Binder(), convertToBubbleTransition,
+ MockTransactionPool.create(), MockTransactionPool.create());
+
+ // Notifies home visibility change due to home moving to back in the second transition
+ verify(mListener).onHomeVisibilityChanged(false);
+ }
+
+ @Test
public void testHomeActivityWithBackGestureNotifiesHomeIsVisibleAfterClose()
throws RemoteException {
TransitionInfo info = mock(TransitionInfo.class);
@@ -227,4 +301,14 @@ public class HomeTransitionObserverTest extends ShellTestCase {
when(change.getMode()).thenReturn(mode);
taskInfo.isRunning = isRunning;
}
+
+ private static ActivityManager.RunningTaskInfo createTaskInfo(int taskId, int activityType) {
+ ActivityManager.RunningTaskInfo taskInfo = new ActivityManager.RunningTaskInfo();
+ taskInfo.taskId = taskId;
+ taskInfo.topActivityType = activityType;
+ taskInfo.configuration.windowConfiguration.setActivityType(activityType);
+ taskInfo.token = mock(WindowContainerToken.class);
+ taskInfo.isRunning = true;
+ return taskInfo;
+ }
}
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/DesktopModeWindowDecorViewModelTestsBase.kt b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/DesktopModeWindowDecorViewModelTestsBase.kt
index 8cccdb2b6120..81dfaed56b6f 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/DesktopModeWindowDecorViewModelTestsBase.kt
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/DesktopModeWindowDecorViewModelTestsBase.kt
@@ -52,6 +52,7 @@ import com.android.wm.shell.common.DisplayChangeController
import com.android.wm.shell.common.DisplayController
import com.android.wm.shell.common.DisplayInsetsController
import com.android.wm.shell.common.DisplayLayout
+import com.android.wm.shell.common.MultiDisplayDragMoveIndicatorController
import com.android.wm.shell.common.MultiInstanceHelper
import com.android.wm.shell.common.SyncTransactionQueue
import com.android.wm.shell.desktopmode.DesktopActivityOrientationChangeHandler
@@ -138,6 +139,8 @@ open class DesktopModeWindowDecorViewModelTestsBase : ShellTestCase() {
protected val mockFreeformTaskTransitionStarter = mock<FreeformTaskTransitionStarter>()
protected val mockActivityOrientationChangeHandler =
mock<DesktopActivityOrientationChangeHandler>()
+ protected val mockMultiDisplayDragMoveIndicatorController =
+ mock<MultiDisplayDragMoveIndicatorController>()
protected val mockInputManager = mock<InputManager>()
private val mockTaskPositionerFactory =
mock<DesktopModeWindowDecorViewModel.TaskPositionerFactory>()
@@ -229,6 +232,7 @@ open class DesktopModeWindowDecorViewModelTestsBase : ShellTestCase() {
mockRecentsTransitionHandler,
desktopModeCompatPolicy,
mockTilingWindowDecoration,
+ mockMultiDisplayDragMoveIndicatorController,
)
desktopModeWindowDecorViewModel.setSplitScreenController(mockSplitScreenController)
whenever(mockDisplayController.getDisplayLayout(any())).thenReturn(mockDisplayLayout)
@@ -243,6 +247,7 @@ open class DesktopModeWindowDecorViewModelTestsBase : ShellTestCase() {
any(),
any(),
any(),
+ any(),
any()
)
)
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/MultiDisplayVeiledResizeTaskPositionerTest.kt b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/MultiDisplayVeiledResizeTaskPositionerTest.kt
index 937938df82c8..a6b077037b86 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/MultiDisplayVeiledResizeTaskPositionerTest.kt
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/MultiDisplayVeiledResizeTaskPositionerTest.kt
@@ -41,6 +41,7 @@ import com.android.wm.shell.ShellTaskOrganizer
import com.android.wm.shell.ShellTestCase
import com.android.wm.shell.common.DisplayController
import com.android.wm.shell.common.DisplayLayout
+import com.android.wm.shell.common.MultiDisplayDragMoveIndicatorController
import com.android.wm.shell.common.MultiDisplayTestUtil
import com.android.wm.shell.transition.Transitions
import com.android.wm.shell.transition.Transitions.TransitionFinishCallback
@@ -62,8 +63,8 @@ import org.mockito.Mockito.mock
import org.mockito.Mockito.never
import org.mockito.Mockito.times
import org.mockito.Mockito.verify
-import org.mockito.Mockito.`when` as whenever
import org.mockito.Mockito.`when`
+import org.mockito.Mockito.`when` as whenever
import org.mockito.MockitoAnnotations
/**
@@ -93,7 +94,8 @@ class MultiDisplayVeiledResizeTaskPositionerTest : ShellTestCase() {
@Mock private lateinit var mockTransitions: Transitions
@Mock private lateinit var mockInteractionJankMonitor: InteractionJankMonitor
@Mock private lateinit var mockSurfaceControl: SurfaceControl
-
+ @Mock private lateinit var mockMultiDisplayDragMoveIndicatorController:
+ MultiDisplayDragMoveIndicatorController
private lateinit var resources: TestableResources
private lateinit var spyDisplayLayout0: DisplayLayout
private lateinit var spyDisplayLayout1: DisplayLayout
@@ -170,10 +172,11 @@ class MultiDisplayVeiledResizeTaskPositionerTest : ShellTestCase() {
mockDesktopWindowDecoration,
mockDisplayController,
mockDragEventListener,
- mockTransactionFactory,
+ { mockTransaction },
mockTransitions,
mockInteractionJankMonitor,
mainHandler,
+ mockMultiDisplayDragMoveIndicatorController,
)
}
diff --git a/libs/hostgraphics/include/gui/BufferItemConsumer.h b/libs/hostgraphics/include/gui/BufferItemConsumer.h
index c25941151800..5c96c82e061c 100644
--- a/libs/hostgraphics/include/gui/BufferItemConsumer.h
+++ b/libs/hostgraphics/include/gui/BufferItemConsumer.h
@@ -17,6 +17,8 @@
#ifndef ANDROID_GUI_BUFFERITEMCONSUMER_H
#define ANDROID_GUI_BUFFERITEMCONSUMER_H
+#include <com_android_graphics_libgui_flags.h>
+#include <gui/BufferQueue.h>
#include <gui/ConsumerBase.h>
#include <gui/IGraphicBufferConsumer.h>
#include <utils/RefBase.h>
@@ -26,9 +28,22 @@ namespace android {
class BufferItemConsumer : public ConsumerBase {
public:
BufferItemConsumer(const sp<IGraphicBufferConsumer>& consumer, uint64_t consumerUsage,
- int bufferCount, bool controlledByApp)
+ int bufferCount = -1, bool controlledByApp = false)
: mConsumer(consumer) {}
+#if COM_ANDROID_GRAPHICS_LIBGUI_FLAGS(WB_CONSUMER_BASE_OWNS_BQ)
+ BufferItemConsumer(uint64_t consumerUsage, int bufferCount = -1,
+ bool controlledByApp = false, bool isConsumerSurfaceFlinger = false) {
+ sp<IGraphicBufferProducer> producer;
+ BufferQueue::createBufferQueue(&producer, &mConsumer);
+ mSurface = sp<Surface>::make(producer, controlledByApp);
+ }
+
+ status_t setConsumerIsProtected(bool isProtected) {
+ return OK;
+ }
+#endif // COM_ANDROID_GRAPHICS_LIBGUI_FLAGS(WB_CONSUMER_BASE_OWNS_BQ)
+
status_t acquireBuffer(BufferItem* item, nsecs_t presentWhen, bool waitForFence = true) {
return mConsumer->acquireBuffer(item, presentWhen, 0);
}
@@ -71,8 +86,20 @@ public:
return OK;
}
+#if COM_ANDROID_GRAPHICS_LIBGUI_FLAGS(WB_CONSUMER_BASE_OWNS_BQ)
+// Returns a Surface that can be used as the producer for this consumer.
+ sp<Surface> getSurface() const {
+ return mSurface;
+ }
+#endif // COM_ANDROID_GRAPHICS_LIBGUI_FLAGS(WB_CONSUMER_BASE_OWNS_BQ)
+
private:
sp<IGraphicBufferConsumer> mConsumer;
+#if COM_ANDROID_GRAPHICS_LIBGUI_FLAGS(WB_CONSUMER_BASE_OWNS_BQ)
+ // This Surface wraps the IGraphicBufferConsumer created for this
+ // ConsumerBase.
+ sp<Surface> mSurface;
+#endif // COM_ANDROID_GRAPHICS_LIBGUI_FLAGS(WB_CONSUMER_BASE_OWNS_BQ)
};
} // namespace android
diff --git a/libs/hostgraphics/include/gui/Surface.h b/libs/hostgraphics/include/gui/Surface.h
index 2774f89cb54c..e268ce6ca769 100644
--- a/libs/hostgraphics/include/gui/Surface.h
+++ b/libs/hostgraphics/include/gui/Surface.h
@@ -34,6 +34,10 @@ public:
ANativeWindow::query = hook_query;
}
+ sp<IGraphicBufferProducer> getIGraphicBufferProducer() const {
+ return mBufferProducer;
+ }
+
static bool isValid(const sp<Surface>& surface) {
return surface != nullptr;
}
diff --git a/libs/hostgraphics/include/ui/Fence.h b/libs/hostgraphics/include/ui/Fence.h
index 187c3116f61c..3364b8aed605 100644
--- a/libs/hostgraphics/include/ui/Fence.h
+++ b/libs/hostgraphics/include/ui/Fence.h
@@ -60,6 +60,10 @@ public:
return 0;
}
+ int get() const {
+ return 0;
+ }
+
inline Status getStatus() {
// The sync_wait call underlying wait() has been measured to be
// significantly faster than the sync_fence_info call underlying
diff --git a/libs/hwui/Android.bp b/libs/hwui/Android.bp
index bb2a53bc04d6..38ac8ab7135e 100644
--- a/libs/hwui/Android.bp
+++ b/libs/hwui/Android.bp
@@ -233,6 +233,14 @@ java_sdk_library {
}
filegroup {
+ name: "framework-graphics-ravenwood-policies",
+ srcs: [
+ "framework-graphics-ravenwood-policies.txt",
+ ],
+ visibility: ["//frameworks/base/ravenwood"],
+}
+
+filegroup {
name: "framework-graphics-srcs",
srcs: [
"apex/java/**/*.java",
@@ -461,6 +469,10 @@ cc_defaults {
},
linux: {
srcs: ["platform/linux/utils/SharedLib.cpp"],
+ shared_libs: [
+ "libbinder",
+ "libbinder_ndk",
+ ],
},
darwin: {
srcs: ["platform/darwin/utils/SharedLib.cpp"],
diff --git a/libs/hwui/framework-graphics-ravenwood-policies.txt b/libs/hwui/framework-graphics-ravenwood-policies.txt
new file mode 100644
index 000000000000..7296225ccfe8
--- /dev/null
+++ b/libs/hwui/framework-graphics-ravenwood-policies.txt
@@ -0,0 +1 @@
+class android.graphics.ColorMatrix keepclass
diff --git a/libs/hwui/jni/Bitmap.cpp b/libs/hwui/jni/Bitmap.cpp
index cfde0b28c0d5..27d4ac7cef4b 100644
--- a/libs/hwui/jni/Bitmap.cpp
+++ b/libs/hwui/jni/Bitmap.cpp
@@ -613,7 +613,7 @@ static void Bitmap_setHasMipMap(JNIEnv* env, jobject, jlong bitmapHandle,
///////////////////////////////////////////////////////////////////////////////
// TODO: Move somewhere else
-#ifdef __ANDROID__ // Layoutlib does not support parcel
+#ifdef __linux__ // Only Linux support parcel
#define ON_ERROR_RETURN(X) \
if ((error = (X)) != STATUS_OK) return error
@@ -717,7 +717,7 @@ static binder_status_t writeBlob(AParcel* parcel, uint64_t bitmapId, const SkBit
#undef ON_ERROR_RETURN
-#endif // __ANDROID__ // Layoutlib does not support parcel
+#endif // __linux__ // Only Linux support parcel
// This is the maximum possible size because the SkColorSpace must be
// representable (and therefore serializable) using a matrix and numerical
@@ -733,7 +733,7 @@ static bool validateImageInfo(const SkImageInfo& info, int32_t rowBytes) {
}
static jobject Bitmap_createFromParcel(JNIEnv* env, jobject, jobject parcel) {
-#ifdef __ANDROID__ // Layoutlib does not support parcel
+#ifdef __linux__ // Only Linux support parcel
if (parcel == NULL) {
jniThrowNullPointerException(env, "parcel cannot be null");
return NULL;
@@ -836,14 +836,14 @@ static jobject Bitmap_createFromParcel(JNIEnv* env, jobject, jobject parcel) {
return createBitmap(env, nativeBitmap.release(), getPremulBitmapCreateFlags(isMutable), nullptr,
nullptr, density, sourceId);
#else
- jniThrowRuntimeException(env, "Cannot use parcels outside of Android");
+ jniThrowRuntimeException(env, "Cannot use parcels outside of Linux");
return NULL;
#endif
}
static jboolean Bitmap_writeToParcel(JNIEnv* env, jobject, jlong bitmapHandle, jint density,
jobject parcel) {
-#ifdef __ANDROID__ // Layoutlib does not support parcel
+#ifdef __linux__ // Only Linux support parcel
if (parcel == NULL) {
ALOGD("------- writeToParcel null parcel\n");
return JNI_FALSE;
@@ -901,7 +901,7 @@ static jboolean Bitmap_writeToParcel(JNIEnv* env, jobject, jlong bitmapHandle, j
}
return JNI_TRUE;
#else
- doThrowRE(env, "Cannot use parcels outside of Android");
+ doThrowRE(env, "Cannot use parcels outside of Linux");
return JNI_FALSE;
#endif
}
diff --git a/libs/hwui/jni/Region.cpp b/libs/hwui/jni/Region.cpp
index 1e064b820591..76986eeb079d 100644
--- a/libs/hwui/jni/Region.cpp
+++ b/libs/hwui/jni/Region.cpp
@@ -18,7 +18,7 @@
#include "SkPath.h"
#include "GraphicsJNI.h"
-#ifdef __ANDROID__ // Layoutlib does not support parcel
+#ifdef __linux__ // Only Linux support parcel
#include <android/binder_parcel.h>
#include <android/binder_parcel_jni.h>
#include <android/binder_parcel_utils.h>
@@ -202,7 +202,7 @@ static jstring Region_toString(JNIEnv* env, jobject clazz, jlong regionHandle) {
static jlong Region_createFromParcel(JNIEnv* env, jobject clazz, jobject parcel)
{
-#ifdef __ANDROID__ // Layoutlib does not support parcel
+#ifdef __linux__ // Only Linux support parcel
if (parcel == nullptr) {
return 0;
}
@@ -230,7 +230,7 @@ static jlong Region_createFromParcel(JNIEnv* env, jobject clazz, jobject parcel)
static jboolean Region_writeToParcel(JNIEnv* env, jobject clazz, jlong regionHandle, jobject parcel)
{
-#ifdef __ANDROID__ // Layoutlib does not support parcel
+#ifdef __linux__ // Only Linux support parcel
const SkRegion* region = reinterpret_cast<SkRegion*>(regionHandle);
if (parcel == nullptr) {
return JNI_FALSE;
diff --git a/libs/hwui/jni/ScopedParcel.cpp b/libs/hwui/jni/ScopedParcel.cpp
index 95e4e01d8df8..52cd988344b0 100644
--- a/libs/hwui/jni/ScopedParcel.cpp
+++ b/libs/hwui/jni/ScopedParcel.cpp
@@ -15,7 +15,7 @@
*/
#include "ScopedParcel.h"
-#ifdef __ANDROID__ // Layoutlib does not support parcel
+#ifdef __linux__ // Only Linux support parcel
using namespace android;
@@ -92,4 +92,4 @@ void ScopedParcel::writeData(const std::optional<sk_sp<SkData>>& optData) {
AParcel_writeByteArray(mParcel, nullptr, -1);
}
}
-#endif // __ANDROID__ // Layoutlib does not support parcel
+#endif // __linux__ // Only Linux support parcel
diff --git a/libs/hwui/jni/ScopedParcel.h b/libs/hwui/jni/ScopedParcel.h
index f2f138fda43c..f2b793a354d7 100644
--- a/libs/hwui/jni/ScopedParcel.h
+++ b/libs/hwui/jni/ScopedParcel.h
@@ -15,7 +15,7 @@
*/
#include "SkData.h"
-#ifdef __ANDROID__ // Layoutlib does not support parcel
+#ifdef __linux__ // Only Linux support parcel
#include <android-base/unique_fd.h>
#include <android/binder_parcel.h>
#include <android/binder_parcel_jni.h>
@@ -64,4 +64,4 @@ enum class BlobType : int32_t {
ASHMEM,
};
-#endif // __ANDROID__ // Layoutlib does not support parcel \ No newline at end of file
+#endif // __linux__ // Only Linux support parcel
diff --git a/libs/hwui/jni/graphics_jni_helpers.h b/libs/hwui/jni/graphics_jni_helpers.h
index 91db134af18f..ff26ec1771bd 100644
--- a/libs/hwui/jni/graphics_jni_helpers.h
+++ b/libs/hwui/jni/graphics_jni_helpers.h
@@ -21,6 +21,7 @@
#include <nativehelper/JNIPlatformHelp.h>
#include <nativehelper/scoped_local_ref.h>
#include <nativehelper/scoped_utf_chars.h>
+#include <nativehelper/scoped_primitive_array.h>
#include <string>
// Host targets (layoutlib) do not differentiate between regular and critical native methods,
diff --git a/media/java/android/media/AudioDeviceVolumeManager.java b/media/java/android/media/AudioDeviceVolumeManager.java
index 892a8612d74a..56d3df3b2555 100644
--- a/media/java/android/media/AudioDeviceVolumeManager.java
+++ b/media/java/android/media/AudioDeviceVolumeManager.java
@@ -16,6 +16,9 @@
package android.media;
+import static android.annotation.SystemApi.Client.MODULE_LIBRARIES;
+import static android.media.audio.Flags.FLAG_UNIFY_ABSOLUTE_VOLUME_MANAGEMENT;
+
import static com.android.media.flags.Flags.FLAG_ENABLE_AUDIO_INPUT_DEVICE_ROUTING_AND_VOLUME_CONTROL;
import android.Manifest;
@@ -91,6 +94,8 @@ public class AudioDeviceVolumeManager {
* @see #setDeviceAbsoluteVolumeBehavior(AudioDeviceAttributes, VolumeInfo, boolean, Executor,
* OnAudioDeviceVolumeChangedListener)
*/
+ @SystemApi(client = MODULE_LIBRARIES)
+ @FlaggedApi(FLAG_UNIFY_ABSOLUTE_VOLUME_MANAGEMENT)
public interface OnAudioDeviceVolumeChangedListener {
/**
* Called the device for the given audio device has changed.
@@ -203,6 +208,30 @@ public class AudioDeviceVolumeManager {
* volume updates to apply on that device
* @param device the audio device set to absolute volume mode
* @param volume the type of volume this device responds to
+ * @param executor the Executor used for receiving volume updates through the listener
+ * @param vclistener the callback for volume updates
+ */
+ @SystemApi(client = MODULE_LIBRARIES)
+ @RequiresPermission(anyOf = { android.Manifest.permission.MODIFY_AUDIO_ROUTING,
+ android.Manifest.permission.MODIFY_AUDIO_SETTINGS_PRIVILEGED,
+ android.Manifest.permission.BLUETOOTH_PRIVILEGED,
+ android.Manifest.permission.BLUETOOTH_STACK})
+ @FlaggedApi(FLAG_UNIFY_ABSOLUTE_VOLUME_MANAGEMENT)
+ public void setDeviceAbsoluteVolumeBehavior(
+ @NonNull AudioDeviceAttributes device,
+ @NonNull VolumeInfo volume,
+ @NonNull @CallbackExecutor Executor executor,
+ @NonNull OnAudioDeviceVolumeChangedListener vclistener) {
+ setDeviceAbsoluteVolumeBehavior(device, volume, /*handlesVolumeAdjustment=*/false, executor,
+ vclistener);
+ }
+
+ /**
+ * @hide
+ * Configures a device to use absolute volume model, and registers a listener for receiving
+ * volume updates to apply on that device
+ * @param device the audio device set to absolute volume mode
+ * @param volume the type of volume this device responds to
* @param handlesVolumeAdjustment whether the controller handles volume adjustments separately
* from volume changes. If true, adjustments from {@link AudioManager#adjustStreamVolume}
* will be sent via {@link OnAudioDeviceVolumeChangedListener#onAudioDeviceVolumeAdjusted}.
@@ -210,7 +239,7 @@ public class AudioDeviceVolumeManager {
* @param vclistener the callback for volume updates
*/
@RequiresPermission(anyOf = { android.Manifest.permission.MODIFY_AUDIO_ROUTING,
- android.Manifest.permission.BLUETOOTH_PRIVILEGED })
+ android.Manifest.permission.BLUETOOTH_PRIVILEGED})
public void setDeviceAbsoluteVolumeBehavior(
@NonNull AudioDeviceAttributes device,
@NonNull VolumeInfo volume,
@@ -229,6 +258,30 @@ public class AudioDeviceVolumeManager {
* registers a listener for receiving volume updates to apply on that device
* @param device the audio device set to absolute multi-volume mode
* @param volumes the list of volumes the given device responds to
+ * @param executor the Executor used for receiving volume updates through the listener
+ * @param vclistener the callback for volume updates
+ */
+ @SystemApi(client = MODULE_LIBRARIES)
+ @RequiresPermission(anyOf = { android.Manifest.permission.MODIFY_AUDIO_ROUTING,
+ android.Manifest.permission.MODIFY_AUDIO_SETTINGS_PRIVILEGED,
+ android.Manifest.permission.BLUETOOTH_PRIVILEGED,
+ android.Manifest.permission.BLUETOOTH_STACK})
+ @FlaggedApi(FLAG_UNIFY_ABSOLUTE_VOLUME_MANAGEMENT)
+ public void setDeviceAbsoluteMultiVolumeBehavior(
+ @NonNull AudioDeviceAttributes device,
+ @NonNull List<VolumeInfo> volumes,
+ @NonNull @CallbackExecutor Executor executor,
+ @NonNull OnAudioDeviceVolumeChangedListener vclistener) {
+ setDeviceAbsoluteMultiVolumeBehavior(device, volumes, /*handlesVolumeAdjustment=*/false,
+ executor, vclistener);
+ }
+
+ /**
+ * @hide
+ * Configures a device to use absolute volume model applied to different volume types, and
+ * registers a listener for receiving volume updates to apply on that device
+ * @param device the audio device set to absolute multi-volume mode
+ * @param volumes the list of volumes the given device responds to
* @param handlesVolumeAdjustment whether the controller handles volume adjustments separately
* from volume changes. If true, adjustments from {@link AudioManager#adjustStreamVolume}
* will be sent via {@link OnAudioDeviceVolumeChangedListener#onAudioDeviceVolumeAdjusted}.
@@ -236,7 +289,7 @@ public class AudioDeviceVolumeManager {
* @param vclistener the callback for volume updates
*/
@RequiresPermission(anyOf = { android.Manifest.permission.MODIFY_AUDIO_ROUTING,
- android.Manifest.permission.BLUETOOTH_PRIVILEGED })
+ android.Manifest.permission.BLUETOOTH_PRIVILEGED})
public void setDeviceAbsoluteMultiVolumeBehavior(
@NonNull AudioDeviceAttributes device,
@NonNull List<VolumeInfo> volumes,
diff --git a/native/android/surface_control.cpp b/native/android/surface_control.cpp
index 4fe0b80f3951..275972495206 100644
--- a/native/android/surface_control.cpp
+++ b/native/android/surface_control.cpp
@@ -715,47 +715,45 @@ void ASurfaceTransaction_setLuts(ASurfaceTransaction* aSurfaceTransaction,
sp<SurfaceControl> surfaceControl = ASurfaceControl_to_SurfaceControl(aSurfaceControl);
Transaction* transaction = ASurfaceTransaction_to_Transaction(aSurfaceTransaction);
- int fd = -1;
+ base::unique_fd fd;
std::vector<int32_t> offsets;
std::vector<int32_t> dimensions;
std::vector<int32_t> sizes;
std::vector<int32_t> samplingKeys;
if (luts) {
- std::vector<float> buffer(luts->totalBufferSize);
int32_t count = luts->offsets.size();
offsets = luts->offsets;
dimensions.reserve(count);
sizes.reserve(count);
samplingKeys.reserve(count);
- for (int32_t i = 0; i < count; i++) {
- dimensions.emplace_back(luts->entries[i]->properties.dimension);
- sizes.emplace_back(luts->entries[i]->properties.size);
- samplingKeys.emplace_back(luts->entries[i]->properties.samplingKey);
- std::copy(luts->entries[i]->buffer.data.begin(), luts->entries[i]->buffer.data.end(),
- buffer.begin() + offsets[i]);
- }
// mmap
- fd = ashmem_create_region("lut_shared_mem", luts->totalBufferSize * sizeof(float));
+ fd.reset(ashmem_create_region("lut_shared_mem", luts->totalBufferSize * sizeof(float)));
if (fd < 0) {
LOG_ALWAYS_FATAL("setLuts, ashmem_create_region() failed");
return;
}
- void* ptr = mmap(nullptr, luts->totalBufferSize * sizeof(float), PROT_READ | PROT_WRITE,
- MAP_SHARED, fd, 0);
+ float* ptr = (float*)mmap(nullptr, luts->totalBufferSize * sizeof(float),
+ PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0);
if (ptr == MAP_FAILED) {
LOG_ALWAYS_FATAL("setLuts, Failed to map the shared memory");
return;
}
- memcpy(ptr, buffer.data(), luts->totalBufferSize * sizeof(float));
+ for (int32_t i = 0; i < count; i++) {
+ dimensions.emplace_back(luts->entries[i]->properties.dimension);
+ sizes.emplace_back(luts->entries[i]->properties.size);
+ samplingKeys.emplace_back(luts->entries[i]->properties.samplingKey);
+ std::copy(luts->entries[i]->buffer.data.begin(), luts->entries[i]->buffer.data.end(),
+ ptr + offsets[i]);
+ }
+
munmap(ptr, luts->totalBufferSize * sizeof(float));
}
- transaction->setLuts(surfaceControl, base::unique_fd(fd), offsets, dimensions, sizes,
- samplingKeys);
+ transaction->setLuts(surfaceControl, std::move(fd), offsets, dimensions, sizes, samplingKeys);
}
void ASurfaceTransaction_setColor(ASurfaceTransaction* aSurfaceTransaction,
diff --git a/packages/CtsShim/build/Android.bp b/packages/CtsShim/build/Android.bp
index bd892637a5eb..853d1ad0dc94 100644
--- a/packages/CtsShim/build/Android.bp
+++ b/packages/CtsShim/build/Android.bp
@@ -152,34 +152,6 @@ android_app {
}
//##########################################################
-// Variant: System app upgrade
-
-android_app {
- name: "CtsShimUpgrade",
-
- sdk_version: "current",
- optimize: {
- enabled: false,
- },
- dex_preopt: {
- enabled: false,
- },
-
- manifest: "shim/AndroidManifestUpgrade.xml",
- min_sdk_version: "24",
-}
-
-java_genrule {
- name: "generate_shim_manifest",
- srcs: [
- "shim/AndroidManifest.xml",
- ":CtsShimUpgrade",
- ],
- out: ["AndroidManifest.xml"],
- cmd: "sed -e s/__HASH__/`sha512sum -b $(location :CtsShimUpgrade) | cut -d' ' -f1`/ $(location shim/AndroidManifest.xml) > $(out)",
-}
-
-//##########################################################
// Variant: System app
android_app {
@@ -193,7 +165,7 @@ android_app {
enabled: false,
},
- manifest: ":generate_shim_manifest",
+ manifest: "shim/AndroidManifest.xml",
apex_available: [
"//apex_available:platform",
"com.android.apex.cts.shim.v1",
diff --git a/packages/CtsShim/build/shim/AndroidManifest.xml b/packages/CtsShim/build/shim/AndroidManifest.xml
index 3b8276b66bac..1ffe56c0c76a 100644
--- a/packages/CtsShim/build/shim/AndroidManifest.xml
+++ b/packages/CtsShim/build/shim/AndroidManifest.xml
@@ -17,15 +17,13 @@
<!-- Manifest for the system CTS shim -->
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
- package="com.android.cts.ctsshim"
- android:sharedUserId="com.android.cts.ctsshim" >
+ package="com.android.cts.ctsshim" >
- <uses-sdk
- android:minSdkVersion="24"
+ <uses-sdk android:minSdkVersion="24"
android:targetSdkVersion="28" />
<restrict-update
- android:hash="__HASH__" />
+ android:hash="__CAN_NOT_BE_UPDATED__" />
<application
android:hasCode="false"
diff --git a/packages/CtsShim/build/shim/AndroidManifestUpgrade.xml b/packages/CtsShim/build/shim/AndroidManifestUpgrade.xml
deleted file mode 100644
index 7f3644a18ad6..000000000000
--- a/packages/CtsShim/build/shim/AndroidManifestUpgrade.xml
+++ /dev/null
@@ -1,30 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!-- Copyright (C) 2016 The Android Open Source Project
-
- Licensed under the Apache License, Version 2.0 (the "License");
- you may not use this file except in compliance with the License.
- You may obtain a copy of the License at
-
- http://www.apache.org/licenses/LICENSE-2.0
-
- Unless required by applicable law or agreed to in writing, software
- distributed under the License is distributed on an "AS IS" BASIS,
- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- See the License for the specific language governing permissions and
- limitations under the License.
--->
-
-<!-- Manifest for the system CTS shim -->
-<manifest xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:tools="http://schemas.android.com/tools"
- package="com.android.cts.ctsshim" >
-
- <uses-sdk
- android:minSdkVersion="24"
- android:targetSdkVersion="28" />
-
- <application
- android:hasCode="false"
- tools:ignore="AllowBackup,MissingApplicationIcon" />
-</manifest>
-
diff --git a/packages/SettingsLib/BannerMessagePreference/Android.bp b/packages/SettingsLib/BannerMessagePreference/Android.bp
index 182daeb45d7c..203a3bf64910 100644
--- a/packages/SettingsLib/BannerMessagePreference/Android.bp
+++ b/packages/SettingsLib/BannerMessagePreference/Android.bp
@@ -31,5 +31,6 @@ android_library {
apex_available: [
"//apex_available:platform",
"com.android.healthfitness",
+ "com.android.permission",
],
}
diff --git a/packages/SettingsLib/BannerMessagePreference/res/drawable-v35/settingslib_resolved_banner_avd.xml b/packages/SettingsLib/BannerMessagePreference/res/drawable-v35/settingslib_resolved_banner_avd.xml
new file mode 100644
index 000000000000..c999de7d99ea
--- /dev/null
+++ b/packages/SettingsLib/BannerMessagePreference/res/drawable-v35/settingslib_resolved_banner_avd.xml
@@ -0,0 +1,157 @@
+<!--
+ ~ Copyright (C) 2025 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.
+ -->
+<animated-vector xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:aapt="http://schemas.android.com/aapt">
+ <aapt:attr name="android:drawable">
+ <vector
+ android:width="112dp"
+ android:height="112dp"
+ android:viewportHeight="112"
+ android:viewportWidth="112">
+ <group android:name="_R_G">
+ <group
+ android:name="_R_G_L_1_G"
+ android:translateX="56.5"
+ android:translateY="56.625">
+ <path
+ android:name="_R_G_L_1_G_D_0_P_0"
+ android:fillAlpha="0"
+ android:fillColor="?android:attr/textColorPrimary"
+ android:fillType="nonZero"
+ android:pathData=" M-14.75 -0.59 C-14.75,-0.59 -15.32,-1.16 -15.32,-1.16 C-15.32,-1.16 -14.67,-0.73 -14.67,-0.73 C-14.67,-0.73 -14.39,-0.44 -14.39,-0.44 C-14.39,-0.44 -17.32,2.5 -17.32,2.5 C-17.32,2.5 -17.59,2.23 -17.59,2.23 C-17.59,2.23 -14.75,-0.59 -14.75,-0.59c "
+ android:trimPathEnd="1"
+ android:trimPathOffset="0"
+ android:trimPathStart="0" />
+ </group>
+ <group
+ android:name="_R_G_L_0_G"
+ android:rotation="-90"
+ android:translateX="56"
+ android:translateY="56">
+ <path
+ android:name="_R_G_L_0_G_D_0_P_0"
+ android:pathData=" M53.5 0 C53.5,-29.53 29.53,-53.5 0,-53.5 C0,-53.5 0,-53.5 0,-53.5 C-29.53,-53.5 -53.5,-29.53 -53.5,0 C-53.5,0 -53.5,0 -53.5,0 C-53.5,29.53 -29.53,53.5 0,53.5 C0,53.5 0,53.5 0,53.5 C29.53,53.5 53.5,29.53 53.5,0 C53.5,0 53.5,0 53.5,0c "
+ android:strokeAlpha="1"
+ android:strokeColor="?android:attr/textColorTertiary"
+ android:strokeLineCap="round"
+ android:strokeLineJoin="round"
+ android:strokeWidth="5"
+ android:trimPathEnd="0"
+ android:trimPathOffset="0"
+ android:trimPathStart="0" />
+ </group>
+ </group>
+ <group android:name="time_group" />
+ </vector>
+ </aapt:attr>
+ <target android:name="_R_G_L_1_G_D_0_P_0">
+ <aapt:attr name="android:animation">
+ <set android:ordering="together">
+ <objectAnimator
+ android:duration="400"
+ android:propertyName="fillAlpha"
+ android:startOffset="0"
+ android:valueFrom="0"
+ android:valueTo="0"
+ android:valueType="floatType">
+ <aapt:attr name="android:interpolator">
+ <pathInterpolator android:pathData="M 0.0,0.0 c0.167,0.167 0.833,0.833 1.0,1.0" />
+ </aapt:attr>
+ </objectAnimator>
+ <objectAnimator
+ android:duration="17"
+ android:propertyName="fillAlpha"
+ android:startOffset="400"
+ android:valueFrom="0"
+ android:valueTo="1"
+ android:valueType="floatType">
+ <aapt:attr name="android:interpolator">
+ <pathInterpolator android:pathData="M 0.0,0.0 c0.167,0.167 0.833,0.833 1.0,1.0" />
+ </aapt:attr>
+ </objectAnimator>
+ </set>
+ </aapt:attr>
+ </target>
+ <target android:name="_R_G_L_1_G_D_0_P_0">
+ <aapt:attr name="android:animation">
+ <set android:ordering="together">
+ <objectAnimator
+ android:duration="400"
+ android:propertyName="pathData"
+ android:startOffset="0"
+ android:valueFrom="M-14.75 -0.59 C-14.75,-0.59 -15.32,-1.16 -15.32,-1.16 C-15.32,-1.16 -14.67,-0.73 -14.67,-0.73 C-14.67,-0.73 -14.39,-0.44 -14.39,-0.44 C-14.39,-0.44 -17.32,2.5 -17.32,2.5 C-17.32,2.5 -17.59,2.23 -17.59,2.23 C-17.59,2.23 -14.75,-0.59 -14.75,-0.59c "
+ android:valueTo="M-14.75 -0.59 C-14.75,-0.59 -15.32,-1.16 -15.32,-1.16 C-15.32,-1.16 -14.67,-0.73 -14.67,-0.73 C-14.67,-0.73 -14.39,-0.44 -14.39,-0.44 C-14.39,-0.44 -17.32,2.5 -17.32,2.5 C-17.32,2.5 -17.59,2.23 -17.59,2.23 C-17.59,2.23 -14.75,-0.59 -14.75,-0.59c "
+ android:valueType="pathType">
+ <aapt:attr name="android:interpolator">
+ <pathInterpolator android:pathData="M 0.0,0.0 c0.167,0.167 0.833,0.833 1.0,1.0" />
+ </aapt:attr>
+ </objectAnimator>
+ <objectAnimator
+ android:duration="83"
+ android:propertyName="pathData"
+ android:startOffset="400"
+ android:valueFrom="M-14.75 -0.59 C-14.75,-0.59 -15.32,-1.16 -15.32,-1.16 C-15.32,-1.16 -14.67,-0.73 -14.67,-0.73 C-14.67,-0.73 -14.39,-0.44 -14.39,-0.44 C-14.39,-0.44 -17.32,2.5 -17.32,2.5 C-17.32,2.5 -17.59,2.23 -17.59,2.23 C-17.59,2.23 -14.75,-0.59 -14.75,-0.59c "
+ android:valueTo="M-14.75 -0.59 C-14.75,-0.59 -6.41,7.75 -6.41,7.75 C-6.41,7.75 -6.3,7.65 -6.3,7.65 C-6.3,7.65 -3.48,10.47 -3.48,10.47 C-3.48,10.47 -6.41,13.41 -6.41,13.41 C-6.41,13.41 -17.59,2.23 -17.59,2.23 C-17.59,2.23 -14.75,-0.59 -14.75,-0.59c "
+ android:valueType="pathType">
+ <aapt:attr name="android:interpolator">
+ <pathInterpolator android:pathData="M 0.0,0.0 c0.167,0.167 0.833,0.833 1.0,1.0" />
+ </aapt:attr>
+ </objectAnimator>
+ <objectAnimator
+ android:duration="167"
+ android:propertyName="pathData"
+ android:startOffset="483"
+ android:valueFrom="M-14.75 -0.59 C-14.75,-0.59 -6.41,7.75 -6.41,7.75 C-6.41,7.75 -6.3,7.65 -6.3,7.65 C-6.3,7.65 -3.48,10.47 -3.48,10.47 C-3.48,10.47 -6.41,13.41 -6.41,13.41 C-6.41,13.41 -17.59,2.23 -17.59,2.23 C-17.59,2.23 -14.75,-0.59 -14.75,-0.59c "
+ android:valueTo="M-14.75 -0.59 C-14.75,-0.59 -6.41,7.75 -6.41,7.75 C-6.41,7.75 14.77,-13.41 14.77,-13.41 C14.77,-13.41 17.59,-10.59 17.59,-10.59 C17.59,-10.59 -6.41,13.41 -6.41,13.41 C-6.41,13.41 -17.59,2.23 -17.59,2.23 C-17.59,2.23 -14.75,-0.59 -14.75,-0.59c "
+ android:valueType="pathType">
+ <aapt:attr name="android:interpolator">
+ <pathInterpolator android:pathData="M 0.0,0.0 c0.167,0.167 0.667,1 1.0,1.0" />
+ </aapt:attr>
+ </objectAnimator>
+ </set>
+ </aapt:attr>
+ </target>
+ <target android:name="_R_G_L_0_G_D_0_P_0">
+ <aapt:attr name="android:animation">
+ <set android:ordering="together">
+ <objectAnimator
+ android:duration="500"
+ android:propertyName="trimPathEnd"
+ android:startOffset="0"
+ android:valueFrom="0"
+ android:valueTo="1"
+ android:valueType="floatType">
+ <aapt:attr name="android:interpolator">
+ <pathInterpolator android:pathData="M 0.0,0.0 c0.2,0 0,1 1.0,1.0" />
+ </aapt:attr>
+ </objectAnimator>
+ </set>
+ </aapt:attr>
+ </target>
+ <target android:name="time_group">
+ <aapt:attr name="android:animation">
+ <set android:ordering="together">
+ <objectAnimator
+ android:duration="667"
+ android:propertyName="translateX"
+ android:startOffset="0"
+ android:valueFrom="0"
+ android:valueTo="1"
+ android:valueType="floatType" />
+ </set>
+ </aapt:attr>
+ </target>
+</animated-vector> \ No newline at end of file
diff --git a/packages/SettingsLib/BannerMessagePreference/res/layout-v35/settingslib_expressive_banner_message.xml b/packages/SettingsLib/BannerMessagePreference/res/layout-v35/settingslib_expressive_banner_message.xml
index b10ef6e77ec7..c448a2d434f8 100644
--- a/packages/SettingsLib/BannerMessagePreference/res/layout-v35/settingslib_expressive_banner_message.xml
+++ b/packages/SettingsLib/BannerMessagePreference/res/layout-v35/settingslib_expressive_banner_message.xml
@@ -28,81 +28,104 @@
android:orientation="vertical"
style="@style/Banner.Preference.SettingsLib.Expressive">
- <RelativeLayout
- android:id="@+id/top_row"
+ <FrameLayout
android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:orientation="horizontal">
+ android:layout_height="wrap_content">
<LinearLayout
+ android:id="@+id/banner_content"
android:layout_width="match_parent"
android:layout_height="wrap_content"
- android:layout_alignParentStart="true"
- android:layout_marginEnd="@dimen/settingslib_expressive_space_medium4"
android:orientation="vertical">
- <TextView
- android:id="@+id/banner_header"
+
+ <RelativeLayout
+ android:id="@+id/top_row"
android:layout_width="match_parent"
android:layout_height="wrap_content"
- style="@style/Banner.Header.SettingsLib.Expressive"/>
+ android:orientation="horizontal">
- <LinearLayout
- android:layout_width="wrap_content"
- android:layout_height="wrap_content">
-
- <ImageView
- android:id="@+id/banner_icon"
- android:layout_width="@dimen/settingslib_expressive_space_small3"
- android:layout_height="@dimen/settingslib_expressive_space_small3"
- android:layout_gravity="center_vertical"
- android:importantForAccessibility="no"
- android:scaleType="fitCenter" />
-
- <TextView
- android:id="@+id/banner_title"
+ <LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
- style="@style/Banner.Title.SettingsLib.Expressive" />
- </LinearLayout>
+ android:layout_alignParentStart="true"
+ android:layout_marginEnd="@dimen/settingslib_expressive_space_medium4"
+ android:orientation="vertical">
+ <TextView
+ android:id="@+id/banner_header"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ style="@style/Banner.Header.SettingsLib.Expressive"/>
+
+ <LinearLayout
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content">
+
+ <ImageView
+ android:id="@+id/banner_icon"
+ android:layout_width="@dimen/settingslib_expressive_space_small3"
+ android:layout_height="@dimen/settingslib_expressive_space_small3"
+ android:layout_gravity="center_vertical"
+ android:layout_marginEnd="@dimen/settingslib_expressive_space_extrasmall4"
+ android:importantForAccessibility="no"
+ android:scaleType="fitCenter" />
+
+ <TextView
+ android:id="@+id/banner_title"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ style="@style/Banner.Title.SettingsLib.Expressive" />
+ </LinearLayout>
+
+ <TextView
+ android:id="@+id/banner_subtitle"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ style="@style/Banner.Subtitle.SettingsLib.Expressive"/>
+ </LinearLayout>
+
+ <ImageButton
+ android:id="@+id/banner_dismiss_btn"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:contentDescription="@string/accessibility_banner_message_dismiss"
+ style="@style/Banner.Dismiss.SettingsLib.Expressive" />
+ </RelativeLayout>
<TextView
- android:id="@+id/banner_subtitle"
+ android:id="@+id/banner_summary"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ style="@style/Banner.Summary.SettingsLib.Expressive"/>
+
+ <LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
- style="@style/Banner.Subtitle.SettingsLib.Expressive"/>
+ android:id="@+id/banner_buttons_frame"
+ android:paddingTop="@dimen/settingslib_expressive_space_extrasmall6"
+ android:orientation="horizontal">
+
+ <com.google.android.material.button.MaterialButton
+ android:id="@+id/banner_negative_btn"
+ android:layout_weight="1"
+ style="@style/Banner.NegativeButton.SettingsLib.Expressive"/>
+ <Space
+ android:id="@+id/banner_button_space"
+ android:layout_width="@dimen/settingslib_expressive_space_extrasmall4"
+ android:layout_height="@dimen/settingslib_expressive_space_small1"/>
+ <com.google.android.material.button.MaterialButton
+ android:id="@+id/banner_positive_btn"
+ android:layout_weight="1"
+ style="@style/Banner.PositiveButton.SettingsLib.Expressive"/>
+ </LinearLayout>
</LinearLayout>
- <ImageButton
- android:id="@+id/banner_dismiss_btn"
+ <TextView
+ android:id="@+id/resolved_banner_text"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
- style="@style/Banner.Dismiss.SettingsLib.Expressive" />
- </RelativeLayout>
-
- <TextView
- android:id="@+id/banner_summary"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- style="@style/Banner.Summary.SettingsLib.Expressive"/>
-
- <LinearLayout
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:id="@+id/banner_buttons_frame"
- android:paddingTop="@dimen/settingslib_expressive_space_extrasmall6"
- android:orientation="horizontal">
-
- <com.google.android.material.button.MaterialButton
- android:id="@+id/banner_negative_btn"
- android:layout_weight="1"
- style="@style/Banner.NegativeButton.SettingsLib.Expressive"/>
- <Space
- android:layout_width="@dimen/settingslib_expressive_space_extrasmall4"
- android:layout_height="@dimen/settingslib_expressive_space_small1"/>
- <com.google.android.material.button.MaterialButton
- android:id="@+id/banner_positive_btn"
- android:layout_weight="1"
- style="@style/Banner.PositiveButton.SettingsLib.Expressive"/>
- </LinearLayout>
+ android:drawableTop="@drawable/settingslib_resolved_banner_avd"
+ android:visibility="gone"
+ style="@style/Banner.ResolvedText.SettingsLib.Expressive"/>
+ </FrameLayout>
</com.android.settingslib.widget.BannerMessageView>
</LinearLayout> \ No newline at end of file
diff --git a/packages/SettingsLib/BannerMessagePreference/res/values-v35/styles_expressive.xml b/packages/SettingsLib/BannerMessagePreference/res/values-v35/styles_expressive.xml
index b864311bf9b7..09e07ccef683 100644
--- a/packages/SettingsLib/BannerMessagePreference/res/values-v35/styles_expressive.xml
+++ b/packages/SettingsLib/BannerMessagePreference/res/values-v35/styles_expressive.xml
@@ -33,7 +33,6 @@
<style name="Banner.Title.SettingsLib.Expressive"
parent="">
<item name="android:layout_gravity">start</item>
- <item name="android:layout_marginLeft">@dimen/settingslib_expressive_space_extrasmall4</item>
<item name="android:textAlignment">viewStart</item>
<item name="android:textAppearance">@style/TextAppearance.SettingsLib.TitleLarge.Emphasized</item>
<item name="android:textColor">?android:attr/textColorPrimary</item>
@@ -73,4 +72,11 @@
parent="@style/SettingsLibButtonStyle.Expressive.Outline.Extra">
<item name="materialSizeOverlay">@style/SizeOverlay.Material3Expressive.Button.Small</item>
</style>
+
+ <style name="Banner.ResolvedText.SettingsLib.Expressive" parent="">
+ <item name="android:layout_gravity">center</item>
+ <item name="android:drawablePadding">@dimen/settingslib_expressive_space_small1</item>
+ <item name="android:textAppearance">@style/TextAppearance.SettingsLib.BodyMedium</item>
+ <item name="android:textColor">?android:attr/textColorPrimary</item>
+ </style>
</resources>
diff --git a/packages/SettingsLib/BannerMessagePreference/src/com/android/settingslib/widget/BannerMessagePreference.java b/packages/SettingsLib/BannerMessagePreference/src/com/android/settingslib/widget/BannerMessagePreference.java
index c82829d6ccea..c90a76a39510 100644
--- a/packages/SettingsLib/BannerMessagePreference/src/com/android/settingslib/widget/BannerMessagePreference.java
+++ b/packages/SettingsLib/BannerMessagePreference/src/com/android/settingslib/widget/BannerMessagePreference.java
@@ -35,6 +35,8 @@ import android.widget.TextView;
import androidx.annotation.ColorInt;
import androidx.annotation.ColorRes;
+import androidx.annotation.NonNull;
+import androidx.annotation.Nullable;
import androidx.annotation.RequiresApi;
import androidx.annotation.StringRes;
import androidx.preference.Preference;
@@ -43,6 +45,9 @@ import androidx.preference.PreferenceViewHolder;
import com.android.settingslib.widget.preference.banner.R;
import com.google.android.material.button.MaterialButton;
+
+import java.util.Objects;
+
/**
* Banner message is a banner displaying important information (permission request, page error etc),
* and provide actions for user to address. It requires a user action to be dismissed.
@@ -67,13 +72,15 @@ public class BannerMessagePreference extends Preference implements GroupSectionD
R.color.banner_accent_attention_normal,
R.color.settingslib_banner_button_background_normal);
- // Corresponds to the enum valye of R.attr.attentionLevel
+ // Corresponds to the enum value of R.attr.attentionLevel
private final int mAttrValue;
@ColorRes private final int mBackgroundColorResId;
@ColorRes private final int mAccentColorResId;
@ColorRes private final int mButtonBackgroundColorResId;
- AttentionLevel(int attrValue, @ColorRes int backgroundColorResId,
+ AttentionLevel(
+ int attrValue,
+ @ColorRes int backgroundColorResId,
@ColorRes int accentColorResId,
@ColorRes int buttonBackgroundColorResId) {
mAttrValue = attrValue;
@@ -115,33 +122,38 @@ public class BannerMessagePreference extends Preference implements GroupSectionD
new BannerMessagePreference.DismissButtonInfo();
// Default attention level is High.
- private AttentionLevel mAttentionLevel = AttentionLevel.HIGH;
- private CharSequence mSubtitle;
- private CharSequence mHeader;
+ @NonNull private AttentionLevel mAttentionLevel = AttentionLevel.HIGH;
+ @Nullable private CharSequence mSubtitle;
+ @Nullable private CharSequence mHeader;
private int mButtonOrientation;
+ @Nullable private ResolutionAnimator.Data mResolutionData;
- public BannerMessagePreference(Context context) {
+ public BannerMessagePreference(@NonNull Context context) {
super(context);
init(context, null /* attrs */);
}
- public BannerMessagePreference(Context context, AttributeSet attrs) {
+ public BannerMessagePreference(@NonNull Context context, @Nullable AttributeSet attrs) {
super(context, attrs);
init(context, attrs);
}
- public BannerMessagePreference(Context context, AttributeSet attrs, int defStyleAttr) {
+ public BannerMessagePreference(
+ @NonNull Context context, @Nullable AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
init(context, attrs);
}
- public BannerMessagePreference(Context context, AttributeSet attrs, int defStyleAttr,
+ public BannerMessagePreference(
+ @NonNull Context context,
+ @Nullable AttributeSet attrs,
+ int defStyleAttr,
int defStyleRes) {
super(context, attrs, defStyleAttr, defStyleRes);
init(context, attrs);
}
- private void init(Context context, AttributeSet attrs) {
+ private void init(@NonNull Context context, @Nullable AttributeSet attrs) {
setSelectable(false);
int resId = SettingsThemeHelper.isExpressiveTheme(context)
? R.layout.settingslib_expressive_banner_message
@@ -166,7 +178,7 @@ public class BannerMessagePreference extends Preference implements GroupSectionD
}
@Override
- public void onBindViewHolder(PreferenceViewHolder holder) {
+ public void onBindViewHolder(@NonNull PreferenceViewHolder holder) {
super.onBindViewHolder(holder);
final Context context = getContext();
@@ -193,14 +205,20 @@ public class BannerMessagePreference extends Preference implements GroupSectionD
final ImageView iconView = (ImageView) holder.findViewById(R.id.banner_icon);
if (iconView != null) {
Drawable icon = getIcon();
- iconView.setImageDrawable(
- icon == null
- ? getContext().getDrawable(R.drawable.ic_warning)
- : icon);
- if (mAttentionLevel != AttentionLevel.NORMAL
- && !SettingsThemeHelper.isExpressiveTheme(context)) {
- iconView.setColorFilter(
- new PorterDuffColorFilter(accentColor, PorterDuff.Mode.SRC_IN));
+
+ if (icon == null && SettingsThemeHelper.isExpressiveTheme(context)) {
+ iconView.setVisibility(View.GONE);
+ } else {
+ iconView.setVisibility(View.VISIBLE);
+ iconView.setImageDrawable(
+ icon == null
+ ? getContext().getDrawable(R.drawable.ic_warning)
+ : icon);
+ if (mAttentionLevel != AttentionLevel.NORMAL
+ && !SettingsThemeHelper.isExpressiveTheme(context)) {
+ iconView.setColorFilter(
+ new PorterDuffColorFilter(accentColor, PorterDuff.Mode.SRC_IN));
+ }
}
}
@@ -233,8 +251,10 @@ public class BannerMessagePreference extends Preference implements GroupSectionD
mDismissButtonInfo.setUpButton();
final TextView subtitleView = (TextView) holder.findViewById(R.id.banner_subtitle);
- subtitleView.setText(mSubtitle);
- subtitleView.setVisibility(mSubtitle == null ? View.GONE : View.VISIBLE);
+ if (subtitleView != null) {
+ subtitleView.setText(mSubtitle);
+ subtitleView.setVisibility(mSubtitle == null ? View.GONE : View.VISIBLE);
+ }
TextView headerView = (TextView) holder.findViewById(R.id.banner_header);
if (headerView != null) {
@@ -268,11 +288,25 @@ public class BannerMessagePreference extends Preference implements GroupSectionD
linearLayout.setOrientation(mButtonOrientation);
}
}
+
+ View buttonSpace = holder.findViewById(R.id.banner_button_space);
+ if (buttonSpace != null) {
+ if (mPositiveButtonInfo.shouldBeVisible() && mNegativeButtonInfo.shouldBeVisible()) {
+ buttonSpace.setVisibility(View.VISIBLE);
+ } else {
+ buttonSpace.setVisibility(View.GONE);
+ }
+ }
+
+ if (mResolutionData != null) {
+ new ResolutionAnimator(mResolutionData, holder).startResolutionAnimation();
+ }
}
/**
- * Set the visibility state of positive button.
+ * Sets the visibility state of the positive button.
*/
+ @NonNull
public BannerMessagePreference setPositiveButtonVisible(boolean isVisible) {
if (isVisible != mPositiveButtonInfo.mIsVisible) {
mPositiveButtonInfo.mIsVisible = isVisible;
@@ -282,8 +316,9 @@ public class BannerMessagePreference extends Preference implements GroupSectionD
}
/**
- * Set the visibility state of negative button.
+ * Sets the visibility state of the negative button.
*/
+ @NonNull
public BannerMessagePreference setNegativeButtonVisible(boolean isVisible) {
if (isVisible != mNegativeButtonInfo.mIsVisible) {
mNegativeButtonInfo.mIsVisible = isVisible;
@@ -293,9 +328,10 @@ public class BannerMessagePreference extends Preference implements GroupSectionD
}
/**
- * Set the visibility state of dismiss button.
+ * Sets the visibility state of the dismiss button.
*/
@RequiresApi(Build.VERSION_CODES.S)
+ @NonNull
public BannerMessagePreference setDismissButtonVisible(boolean isVisible) {
if (isVisible != mDismissButtonInfo.mIsVisible) {
mDismissButtonInfo.mIsVisible = isVisible;
@@ -305,10 +341,35 @@ public class BannerMessagePreference extends Preference implements GroupSectionD
}
/**
- * Register a callback to be invoked when positive button is clicked.
+ * Sets the enabled state of the positive button.
+ */
+ @NonNull
+ public BannerMessagePreference setPositiveButtonEnabled(boolean isEnabled) {
+ if (isEnabled != mPositiveButtonInfo.mIsEnabled) {
+ mPositiveButtonInfo.mIsEnabled = isEnabled;
+ notifyChanged();
+ }
+ return this;
+ }
+
+ /**
+ * Sets the enabled state of the negative button.
+ */
+ @NonNull
+ public BannerMessagePreference setNegativeButtonEnabled(boolean isEnabled) {
+ if (isEnabled != mNegativeButtonInfo.mIsEnabled) {
+ mNegativeButtonInfo.mIsEnabled = isEnabled;
+ notifyChanged();
+ }
+ return this;
+ }
+
+ /**
+ * Registers a callback to be invoked when positive button is clicked.
*/
+ @NonNull
public BannerMessagePreference setPositiveButtonOnClickListener(
- View.OnClickListener listener) {
+ @Nullable View.OnClickListener listener) {
if (listener != mPositiveButtonInfo.mListener) {
mPositiveButtonInfo.mListener = listener;
notifyChanged();
@@ -317,10 +378,11 @@ public class BannerMessagePreference extends Preference implements GroupSectionD
}
/**
- * Register a callback to be invoked when negative button is clicked.
+ * Registers a callback to be invoked when negative button is clicked.
*/
+ @NonNull
public BannerMessagePreference setNegativeButtonOnClickListener(
- View.OnClickListener listener) {
+ @Nullable View.OnClickListener listener) {
if (listener != mNegativeButtonInfo.mListener) {
mNegativeButtonInfo.mListener = listener;
notifyChanged();
@@ -329,11 +391,12 @@ public class BannerMessagePreference extends Preference implements GroupSectionD
}
/**
- * Register a callback to be invoked when the dismiss button is clicked.
+ * Registers a callback to be invoked when the dismiss button is clicked.
*/
@RequiresApi(Build.VERSION_CODES.S)
+ @NonNull
public BannerMessagePreference setDismissButtonOnClickListener(
- View.OnClickListener listener) {
+ @Nullable View.OnClickListener listener) {
if (listener != mDismissButtonInfo.mListener) {
mDismissButtonInfo.mListener = listener;
notifyChanged();
@@ -344,6 +407,7 @@ public class BannerMessagePreference extends Preference implements GroupSectionD
/**
* Sets the text to be displayed in positive button.
*/
+ @NonNull
public BannerMessagePreference setPositiveButtonText(@StringRes int textResId) {
return setPositiveButtonText(getContext().getString(textResId));
}
@@ -351,7 +415,9 @@ public class BannerMessagePreference extends Preference implements GroupSectionD
/**
* Sets the text to be displayed in positive button.
*/
- public BannerMessagePreference setPositiveButtonText(CharSequence positiveButtonText) {
+ @NonNull
+ public BannerMessagePreference setPositiveButtonText(
+ @Nullable CharSequence positiveButtonText) {
if (!TextUtils.equals(positiveButtonText, mPositiveButtonInfo.mText)) {
mPositiveButtonInfo.mText = positiveButtonText;
notifyChanged();
@@ -362,6 +428,7 @@ public class BannerMessagePreference extends Preference implements GroupSectionD
/**
* Sets the text to be displayed in negative button.
*/
+ @NonNull
public BannerMessagePreference setNegativeButtonText(@StringRes int textResId) {
return setNegativeButtonText(getContext().getString(textResId));
}
@@ -369,7 +436,9 @@ public class BannerMessagePreference extends Preference implements GroupSectionD
/**
* Sets the text to be displayed in negative button.
*/
- public BannerMessagePreference setNegativeButtonText(CharSequence negativeButtonText) {
+ @NonNull
+ public BannerMessagePreference setNegativeButtonText(
+ @Nullable CharSequence negativeButtonText) {
if (!TextUtils.equals(negativeButtonText, mNegativeButtonInfo.mText)) {
mNegativeButtonInfo.mText = negativeButtonText;
notifyChanged();
@@ -380,8 +449,12 @@ public class BannerMessagePreference extends Preference implements GroupSectionD
/**
* Sets button orientation.
*/
- @RequiresApi(Build.VERSION_CODES.VANILLA_ICE_CREAM)
+ @NonNull
public BannerMessagePreference setButtonOrientation(int orientation) {
+ if (!SettingsThemeHelper.isExpressiveTheme(getContext())) {
+ return this;
+ }
+
if (mButtonOrientation != orientation) {
mButtonOrientation = orientation;
notifyChanged();
@@ -393,6 +466,7 @@ public class BannerMessagePreference extends Preference implements GroupSectionD
* Sets the subtitle.
*/
@RequiresApi(Build.VERSION_CODES.S)
+ @NonNull
public BannerMessagePreference setSubtitle(@StringRes int textResId) {
return setSubtitle(getContext().getString(textResId));
}
@@ -401,7 +475,8 @@ public class BannerMessagePreference extends Preference implements GroupSectionD
* Sets the subtitle.
*/
@RequiresApi(Build.VERSION_CODES.S)
- public BannerMessagePreference setSubtitle(CharSequence subtitle) {
+ @NonNull
+ public BannerMessagePreference setSubtitle(@Nullable CharSequence subtitle) {
if (!TextUtils.equals(subtitle, mSubtitle)) {
mSubtitle = subtitle;
notifyChanged();
@@ -412,7 +487,7 @@ public class BannerMessagePreference extends Preference implements GroupSectionD
/**
* Sets the header.
*/
- @RequiresApi(Build.VERSION_CODES.VANILLA_ICE_CREAM)
+ @NonNull
public BannerMessagePreference setHeader(@StringRes int textResId) {
return setHeader(getContext().getString(textResId));
}
@@ -420,8 +495,12 @@ public class BannerMessagePreference extends Preference implements GroupSectionD
/**
* Sets the header.
*/
- @RequiresApi(Build.VERSION_CODES.VANILLA_ICE_CREAM)
- public BannerMessagePreference setHeader(CharSequence header) {
+ @NonNull
+ public BannerMessagePreference setHeader(@Nullable CharSequence header) {
+ if (!SettingsThemeHelper.isExpressiveTheme(getContext())) {
+ return this;
+ }
+
if (!TextUtils.equals(header, mHeader)) {
mHeader = header;
notifyChanged();
@@ -430,32 +509,75 @@ public class BannerMessagePreference extends Preference implements GroupSectionD
}
/**
- * Sets the attention level. This will update the color theme of the preference.
+ * Plays a resolution animation, showing the given message.
*/
- public BannerMessagePreference setAttentionLevel(AttentionLevel attentionLevel) {
- if (attentionLevel == mAttentionLevel) {
+ @NonNull
+ public BannerMessagePreference showResolutionAnimation(
+ @NonNull CharSequence resolutionMessage,
+ @NonNull ResolutionCompletedCallback onCompletionCallback) {
+ if (!SettingsThemeHelper.isExpressiveTheme(getContext())) {
return this;
}
+ ResolutionAnimator.Data resolutionData =
+ new ResolutionAnimator.Data(resolutionMessage, onCompletionCallback);
+ if (!Objects.equals(mResolutionData, resolutionData)) {
+ mResolutionData = resolutionData;
+ notifyChanged();
+ }
+ return this;
+ }
- if (attentionLevel != null) {
- mAttentionLevel = attentionLevel;
+ /**
+ * Removes the resolution animation from this preference.
+ *
+ * <p>Should be called after the resolution animation completes if this preference will be
+ * reused. Otherwise the resolution animation will be played everytime this preference is
+ * displayed.
+ */
+ @NonNull
+ public BannerMessagePreference clearResolutionAnimation() {
+ if (!SettingsThemeHelper.isExpressiveTheme(getContext())) {
+ return this;
+ }
+ if (mResolutionData != null) {
+ mResolutionData = null;
notifyChanged();
}
return this;
}
+ /**
+ * Sets the attention level. This will update the color theme of the preference.
+ */
+ @NonNull
+ public BannerMessagePreference setAttentionLevel(@NonNull AttentionLevel attentionLevel) {
+ if (attentionLevel == mAttentionLevel) {
+ return this;
+ }
+
+ mAttentionLevel = attentionLevel;
+ notifyChanged();
+ return this;
+ }
+
static class ButtonInfo {
- private Button mButton;
- private CharSequence mText;
- private View.OnClickListener mListener;
+ @Nullable private Button mButton;
+ @Nullable private CharSequence mText;
+ @Nullable private View.OnClickListener mListener;
private boolean mIsVisible = true;
+ private boolean mIsEnabled = true;
@ColorInt private int mColor;
@ColorInt private int mBackgroundColor;
- private ColorStateList mStrokeColor;
+ @Nullable private ColorStateList mStrokeColor;
void setUpButton() {
+ if (mButton == null) {
+ return;
+ }
+
mButton.setText(mText);
mButton.setOnClickListener(mListener);
+ mButton.setEnabled(mIsEnabled);
MaterialButton btn = null;
if (mButton instanceof MaterialButton) {
@@ -492,11 +614,15 @@ public class BannerMessagePreference extends Preference implements GroupSectionD
}
static class DismissButtonInfo {
- private ImageButton mButton;
- private View.OnClickListener mListener;
+ @Nullable private ImageButton mButton;
+ @Nullable private View.OnClickListener mListener;
private boolean mIsVisible = true;
void setUpButton() {
+ if (mButton == null) {
+ return;
+ }
+
mButton.setOnClickListener(mListener);
if (shouldBeVisible()) {
mButton.setVisibility(View.VISIBLE);
diff --git a/packages/SettingsLib/BannerMessagePreference/src/com/android/settingslib/widget/BannerMessageView.java b/packages/SettingsLib/BannerMessagePreference/src/com/android/settingslib/widget/BannerMessageView.java
index ff4e79ddaaa1..eabb6341c3dd 100644
--- a/packages/SettingsLib/BannerMessagePreference/src/com/android/settingslib/widget/BannerMessageView.java
+++ b/packages/SettingsLib/BannerMessagePreference/src/com/android/settingslib/widget/BannerMessageView.java
@@ -23,6 +23,7 @@ import android.view.TouchDelegate;
import android.view.View;
import android.widget.LinearLayout;
+import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import com.android.settingslib.widget.preference.banner.R;
@@ -34,22 +35,25 @@ import com.android.settingslib.widget.preference.banner.R;
* {@link BannerMessagePreference} to a {@code PreferenceScreen}.
*/
public class BannerMessageView extends LinearLayout {
- private Rect mTouchTargetForDismissButton;
+ @Nullable private Rect mTouchTargetForDismissButton;
- public BannerMessageView(Context context) {
+ public BannerMessageView(@NonNull Context context) {
super(context);
}
- public BannerMessageView(Context context,
- @Nullable AttributeSet attrs) {
+ public BannerMessageView(@NonNull Context context, @Nullable AttributeSet attrs) {
super(context, attrs);
}
- public BannerMessageView(Context context, @Nullable AttributeSet attrs, int defStyleAttr) {
+ public BannerMessageView(
+ @NonNull Context context, @Nullable AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
}
- public BannerMessageView(Context context, AttributeSet attrs, int defStyleAttr,
+ public BannerMessageView(
+ @NonNull Context context,
+ @Nullable AttributeSet attrs,
+ int defStyleAttr,
int defStyleRes) {
super(context, attrs, defStyleAttr, defStyleRes);
}
diff --git a/packages/SettingsLib/BannerMessagePreference/src/com/android/settingslib/widget/ResolutionAnimator.kt b/packages/SettingsLib/BannerMessagePreference/src/com/android/settingslib/widget/ResolutionAnimator.kt
new file mode 100644
index 000000000000..fbf910a42423
--- /dev/null
+++ b/packages/SettingsLib/BannerMessagePreference/src/com/android/settingslib/widget/ResolutionAnimator.kt
@@ -0,0 +1,196 @@
+/*
+ * Copyright (C) 2025 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.settingslib.widget
+
+import android.graphics.drawable.Animatable2
+import android.graphics.drawable.AnimatedVectorDrawable
+import android.graphics.drawable.Drawable
+import android.os.Build
+import android.provider.DeviceConfig
+import android.transition.Fade
+import android.transition.Transition
+import android.transition.TransitionListenerAdapter
+import android.transition.TransitionManager
+import android.transition.TransitionSet
+import android.view.View
+import android.view.ViewGroup
+import android.view.animation.LinearInterpolator
+import android.widget.TextView
+import androidx.preference.PreferenceViewHolder
+import com.android.settingslib.widget.preference.banner.R
+import java.time.Duration
+
+/** Callback to communicate when a banner message resolution animation is completed. */
+fun interface ResolutionCompletedCallback {
+ fun onCompleted()
+}
+
+internal class ResolutionAnimator(
+ private val data: Data,
+ private val preferenceViewHolder: PreferenceViewHolder,
+) {
+
+ data class Data(
+ val resolutionMessage: CharSequence,
+ val resolutionCompletedCallback: ResolutionCompletedCallback,
+ )
+
+ private val defaultBannerContent: View?
+ get() = preferenceViewHolder.findView(R.id.banner_content)
+ private val resolvedTextView: TextView?
+ get() = preferenceViewHolder.findView(R.id.resolved_banner_text)
+
+ fun startResolutionAnimation() {
+ resolvedTextView?.text = data.resolutionMessage
+ resolvedTextView?.resolutionDrawable?.reset()
+
+ val transitionSet =
+ TransitionSet()
+ .setOrdering(TransitionSet.ORDERING_SEQUENTIAL)
+ .setInterpolator(linearInterpolator)
+ .addTransition(hideIssueContentTransition)
+ .addTransition(
+ showResolvedContentTransition
+ .clone()
+ .addListener(
+ object : TransitionListenerAdapter() {
+ override fun onTransitionEnd(transition: Transition) {
+ super.onTransitionEnd(transition)
+ startIssueResolvedAnimation()
+ }
+ }
+ )
+ )
+
+ preferenceViewHolder.itemView.post {
+ TransitionManager.beginDelayedTransition(
+ preferenceViewHolder.itemView as ViewGroup,
+ transitionSet,
+ )
+
+ defaultBannerContent?.visibility = View.INVISIBLE
+ resolvedTextView?.visibility = View.VISIBLE
+ }
+
+ preferenceViewHolder.itemView.addOnAttachStateChangeListener(
+ object : View.OnAttachStateChangeListener {
+ override fun onViewAttachedToWindow(v: View) {}
+
+ override fun onViewDetachedFromWindow(v: View) {
+ v.removeOnAttachStateChangeListener(this)
+ cancelAnimationsAndFinish()
+ }
+ }
+ )
+ }
+
+ private fun startIssueResolvedAnimation() {
+ val animatedDrawable = resolvedTextView?.resolutionDrawable
+
+ if (animatedDrawable == null) {
+ hideResolvedUiAndFinish()
+ return
+ }
+
+ animatedDrawable.apply {
+ clearAnimationCallbacks()
+ registerAnimationCallback(
+ object : Animatable2.AnimationCallback() {
+ override fun onAnimationEnd(drawable: Drawable) {
+ super.onAnimationEnd(drawable)
+ hideResolvedUiAndFinish()
+ }
+ }
+ )
+ start()
+ }
+ }
+
+ private fun hideResolvedUiAndFinish() {
+ val hideTransition =
+ hideResolvedContentTransition
+ .clone()
+ .setInterpolator(linearInterpolator)
+ .addListener(
+ object : TransitionListenerAdapter() {
+ override fun onTransitionEnd(transition: Transition) {
+ super.onTransitionEnd(transition)
+ data.resolutionCompletedCallback.onCompleted()
+ }
+ }
+ )
+ TransitionManager.beginDelayedTransition(
+ preferenceViewHolder.itemView as ViewGroup,
+ hideTransition,
+ )
+ resolvedTextView?.visibility = View.GONE
+ }
+
+ private fun cancelAnimationsAndFinish() {
+ TransitionManager.endTransitions(preferenceViewHolder.itemView as ViewGroup)
+
+ resolvedTextView?.visibility = View.GONE
+
+ val animatedDrawable = resolvedTextView?.resolutionDrawable
+ animatedDrawable?.clearAnimationCallbacks()
+ animatedDrawable?.stop()
+
+ data.resolutionCompletedCallback.onCompleted()
+ }
+
+ private companion object {
+ private val linearInterpolator = LinearInterpolator()
+
+ private val HIDE_ISSUE_CONTENT_TRANSITION_DURATION = Duration.ofMillis(333)
+ private val hideIssueContentTransition =
+ Fade(Fade.OUT).setDuration(HIDE_ISSUE_CONTENT_TRANSITION_DURATION.toMillis())
+
+ private val SHOW_RESOLVED_CONTENT_TRANSITION_DELAY = Duration.ofMillis(133)
+ private val SHOW_RESOLVED_CONTENT_TRANSITION_DURATION = Duration.ofMillis(250)
+ private val showResolvedContentTransition =
+ Fade(Fade.IN)
+ .setStartDelay(SHOW_RESOLVED_CONTENT_TRANSITION_DELAY.toMillis())
+ .setDuration(SHOW_RESOLVED_CONTENT_TRANSITION_DURATION.toMillis())
+
+ private val hideResolvedContentTransitionDelay
+ get() =
+ if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) {
+ Duration.ofMillis(
+ DeviceConfig.getLong(
+ "settings_ui",
+ "banner_message_pref_hide_resolved_content_delay_millis",
+ 400,
+ )
+ )
+ } else {
+ Duration.ofMillis(400)
+ }
+
+ private val HIDE_RESOLVED_UI_TRANSITION_DURATION = Duration.ofMillis(167)
+ private val hideResolvedContentTransition
+ get() =
+ Fade(Fade.OUT)
+ .setStartDelay(hideResolvedContentTransitionDelay.toMillis())
+ .setDuration(HIDE_RESOLVED_UI_TRANSITION_DURATION.toMillis())
+
+ inline fun <reified T : View> PreferenceViewHolder.findView(id: Int): T? =
+ findViewById(id) as? T
+
+ val TextView.resolutionDrawable: AnimatedVectorDrawable?
+ get() = compoundDrawables.find { it != null } as? AnimatedVectorDrawable
+ }
+}
diff --git a/packages/SettingsLib/ButtonPreference/Android.bp b/packages/SettingsLib/ButtonPreference/Android.bp
index a377f312ffbf..c8375a992d30 100644
--- a/packages/SettingsLib/ButtonPreference/Android.bp
+++ b/packages/SettingsLib/ButtonPreference/Android.bp
@@ -30,5 +30,6 @@ android_library {
apex_available: [
"//apex_available:platform",
"com.android.healthfitness",
+ "com.android.permission",
],
}
diff --git a/packages/SettingsLib/SettingsTheme/res/values-v35/themes.xml b/packages/SettingsLib/SettingsTheme/res/values-v35/themes.xml
index dc5c9b297181..b6e80c784f10 100644
--- a/packages/SettingsLib/SettingsTheme/res/values-v35/themes.xml
+++ b/packages/SettingsLib/SettingsTheme/res/values-v35/themes.xml
@@ -18,7 +18,7 @@
<resources>
<style name="Theme.SettingsBase_v35" parent="Theme.SettingsBase_v33" >
<item name="android:colorAccent">@color/settingslib_materialColorPrimary</item>
- <item name="android:colorBackground">@color/settingslib_materialColorSurfaceContainerLowest</item>
+ <item name="android:colorBackground">@color/settingslib_materialColorSurfaceContainer</item>
<item name="android:textColorPrimary">@color/settingslib_materialColorOnSurface</item>
<item name="android:textColorSecondary">@color/settingslib_text_color_secondary</item>
<item name="android:textColorTertiary">@color/settingslib_materialColorOutline</item>
diff --git a/packages/SettingsLib/SettingsTheme/src/com/android/settingslib/widget/SettingsPreferenceGroupAdapter.kt b/packages/SettingsLib/SettingsTheme/src/com/android/settingslib/widget/SettingsPreferenceGroupAdapter.kt
index 6a0632073de9..a04fce7eeb86 100644
--- a/packages/SettingsLib/SettingsTheme/src/com/android/settingslib/widget/SettingsPreferenceGroupAdapter.kt
+++ b/packages/SettingsLib/SettingsTheme/src/com/android/settingslib/widget/SettingsPreferenceGroupAdapter.kt
@@ -47,7 +47,7 @@ open class SettingsPreferenceGroupAdapter(preferenceGroup: PreferenceGroup) :
private val mHandler = Handler(Looper.getMainLooper())
- private val syncRunnable = Runnable { updatePreferences() }
+ private val syncRunnable = Runnable { updatePreferencesList() }
init {
val context = preferenceGroup.context
@@ -64,7 +64,7 @@ open class SettingsPreferenceGroupAdapter(preferenceGroup: PreferenceGroup) :
true, /* resolveRefs */
)
mLegacyBackgroundRes = outValue.resourceId
- updatePreferences()
+ updatePreferencesList()
}
@SuppressLint("RestrictedApi")
@@ -82,7 +82,7 @@ open class SettingsPreferenceGroupAdapter(preferenceGroup: PreferenceGroup) :
updateBackground(holder, position)
}
- private fun updatePreferences() {
+ private fun updatePreferencesList() {
val oldList = ArrayList(mRoundCornerMappingList)
mRoundCornerMappingList = ArrayList()
mappingPreferenceGroup(mRoundCornerMappingList, mPreferenceGroup)
diff --git a/packages/SettingsLib/src/com/android/settingslib/bluetooth/BluetoothUtils.java b/packages/SettingsLib/src/com/android/settingslib/bluetooth/BluetoothUtils.java
index 522a436b0732..335f4a8293a0 100644
--- a/packages/SettingsLib/src/com/android/settingslib/bluetooth/BluetoothUtils.java
+++ b/packages/SettingsLib/src/com/android/settingslib/bluetooth/BluetoothUtils.java
@@ -23,6 +23,7 @@ import android.graphics.Bitmap;
import android.graphics.Canvas;
import android.graphics.drawable.BitmapDrawable;
import android.graphics.drawable.Drawable;
+import android.hardware.input.InputManager;
import android.media.AudioDeviceAttributes;
import android.media.AudioDeviceInfo;
import android.media.AudioManager;
@@ -34,6 +35,7 @@ import android.sysprop.BluetoothProperties;
import android.text.TextUtils;
import android.util.Log;
import android.util.Pair;
+import android.view.InputDevice;
import androidx.annotation.DrawableRes;
import androidx.annotation.NonNull;
@@ -1193,4 +1195,53 @@ public class BluetoothUtils {
}
device.setMetadata(METADATA_FAST_PAIR_CUSTOMIZED_FIELDS, fastPairCustomizedMeta.getBytes());
}
+
+ /**
+ * Returns the {@link InputDevice} of the given bluetooth address if the device is a input
+ * device.
+ *
+ * @param address The address of the bluetooth device
+ * @return The {@link InputDevice} of the given address if applicable
+ */
+ @Nullable
+ public static InputDevice getInputDevice(Context context, String address) {
+ InputManager im = context.getSystemService(InputManager.class);
+
+ if (im != null) {
+ for (int deviceId : im.getInputDeviceIds()) {
+ String btAddress = im.getInputDeviceBluetoothAddress(deviceId);
+
+ if (btAddress != null && btAddress.equals(address)) {
+ return im.getInputDevice(deviceId);
+ }
+ }
+ }
+ return null;
+ }
+
+ /**
+ * Identifies whether a device is a stylus using the associated {@link InputDevice} or
+ * {@link CachedBluetoothDevice}.
+ * InputDevices are only available when the device is USI or Bluetooth-connected, whereas
+ * CachedBluetoothDevices are available for Bluetooth devices when connected or paired,
+ * so to handle all cases, both are needed.
+ *
+ * @param inputDevice The associated input device of the stylus
+ * @param cachedBluetoothDevice The associated bluetooth device of the stylus
+ */
+ public static boolean isDeviceStylus(@Nullable InputDevice inputDevice,
+ @Nullable CachedBluetoothDevice cachedBluetoothDevice) {
+ if (inputDevice != null && inputDevice.supportsSource(InputDevice.SOURCE_STYLUS)) {
+ return true;
+ }
+
+ if (cachedBluetoothDevice != null) {
+ BluetoothDevice bluetoothDevice = cachedBluetoothDevice.getDevice();
+ String deviceType = BluetoothUtils.getStringMetaData(bluetoothDevice,
+ BluetoothDevice.METADATA_DEVICE_TYPE);
+ return TextUtils.equals(deviceType, BluetoothDevice.DEVICE_TYPE_STYLUS);
+ }
+
+ return false;
+ }
}
diff --git a/packages/SettingsLib/src/com/android/settingslib/bluetooth/LocalBluetoothLeBroadcast.java b/packages/SettingsLib/src/com/android/settingslib/bluetooth/LocalBluetoothLeBroadcast.java
index 3017d79836e8..367e38ed779d 100644
--- a/packages/SettingsLib/src/com/android/settingslib/bluetooth/LocalBluetoothLeBroadcast.java
+++ b/packages/SettingsLib/src/com/android/settingslib/bluetooth/LocalBluetoothLeBroadcast.java
@@ -63,6 +63,7 @@ import com.google.common.collect.ImmutableList;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.nio.charset.StandardCharsets;
+import java.security.SecureRandom;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
@@ -70,7 +71,6 @@ import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Objects;
-import java.util.UUID;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.Executor;
import java.util.concurrent.Executors;
@@ -108,6 +108,10 @@ public class LocalBluetoothLeBroadcast implements LocalBluetoothProfile {
private static final String SYSUI_PKG = "com.android.systemui";
private static final String TAG = "LocalBluetoothLeBroadcast";
private static final boolean DEBUG = BluetoothUtils.D;
+ private static final String VALID_PASSWORD_CHARACTERS =
+ "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789!@#$%^&*()-_=+[]{}|;:,"
+ + ".<>?/";
+ private static final int PASSWORD_LENGTH = 16;
static final String NAME = "LE_AUDIO_BROADCAST";
private static final String UNDERLINE = "_";
@@ -1088,11 +1092,16 @@ public class LocalBluetoothLeBroadcast implements LocalBluetoothProfile {
mBroadcastId = UNKNOWN_VALUE_PLACEHOLDER;
}
- private String generateRandomPassword() {
- String randomUUID = UUID.randomUUID().toString();
- // first 16 chars from xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx
- return randomUUID.substring(0, 8) + randomUUID.substring(9, 13) + randomUUID.substring(14,
- 18);
+ private static String generateRandomPassword() {
+ SecureRandom random = new SecureRandom();
+ StringBuilder stringBuilder = new StringBuilder(PASSWORD_LENGTH);
+
+ for (int i = 0; i < PASSWORD_LENGTH; i++) {
+ int randomIndex = random.nextInt(VALID_PASSWORD_CHARACTERS.length());
+ stringBuilder.append(VALID_PASSWORD_CHARACTERS.charAt(randomIndex));
+ }
+
+ return stringBuilder.toString();
}
private void registerContentObserver() {
diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/bluetooth/AmbientVolumeControllerTest.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/bluetooth/AmbientVolumeControllerTest.java
index abc1d226972b..e4381ae0a663 100644
--- a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/bluetooth/AmbientVolumeControllerTest.java
+++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/bluetooth/AmbientVolumeControllerTest.java
@@ -26,6 +26,7 @@ import static org.mockito.Mockito.when;
import android.bluetooth.AudioInputControl;
import android.bluetooth.BluetoothDevice;
+import android.bluetooth.BluetoothProfile;
import android.content.Context;
import androidx.test.core.app.ApplicationProvider;
@@ -67,6 +68,9 @@ public class AmbientVolumeControllerTest {
@Before
public void setUp() {
when(mProfileManager.getVolumeControlProfile()).thenReturn(mVolumeControlProfile);
+ when(mVolumeControlProfile.isProfileReady()).thenReturn(true);
+ when(mVolumeControlProfile.getConnectionStatus(mDevice)).thenReturn(
+ BluetoothProfile.STATE_CONNECTED);
when(mDevice.getAddress()).thenReturn(TEST_ADDRESS);
when(mDevice.isConnected()).thenReturn(true);
mVolumeController = new AmbientVolumeController(mProfileManager, mCallback);
@@ -74,8 +78,6 @@ public class AmbientVolumeControllerTest {
@Test
public void onServiceConnected_notifyCallback() {
- when(mVolumeControlProfile.isProfileReady()).thenReturn(true);
-
mVolumeController.onServiceConnected();
verify(mCallback).onVolumeControlServiceConnected();
diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/bluetooth/BluetoothUtilsTest.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/bluetooth/BluetoothUtilsTest.java
index ebe6128e5582..0325c0ec7915 100644
--- a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/bluetooth/BluetoothUtilsTest.java
+++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/bluetooth/BluetoothUtilsTest.java
@@ -15,7 +15,9 @@
*/
package com.android.settingslib.bluetooth;
+import static com.android.settingslib.bluetooth.BluetoothUtils.getInputDevice;
import static com.android.settingslib.bluetooth.BluetoothUtils.isAvailableAudioSharingMediaBluetoothDevice;
+import static com.android.settingslib.bluetooth.BluetoothUtils.isDeviceStylus;
import static com.android.settingslib.bluetooth.LocalBluetoothLeBroadcast.UNKNOWN_VALUE_PLACEHOLDER;
import static com.android.settingslib.flags.Flags.FLAG_ENABLE_DETERMINING_ADVANCED_DETAILS_HEADER_WITH_METADATA;
@@ -42,6 +44,7 @@ import android.content.Context;
import android.content.pm.ApplicationInfo;
import android.content.pm.PackageManager;
import android.graphics.drawable.Drawable;
+import android.hardware.input.InputManager;
import android.media.AudioDeviceAttributes;
import android.media.AudioDeviceInfo;
import android.media.AudioManager;
@@ -51,6 +54,7 @@ import android.platform.test.annotations.EnableFlags;
import android.platform.test.flag.junit.SetFlagsRule;
import android.provider.Settings;
import android.util.Pair;
+import android.view.InputDevice;
import com.android.internal.R;
import com.android.settingslib.flags.Flags;
@@ -97,14 +101,18 @@ public class BluetoothUtilsTest {
@Mock private LocalBluetoothLeBroadcastAssistant mAssistant;
@Mock private CachedBluetoothDeviceManager mDeviceManager;
@Mock private BluetoothLeBroadcastReceiveState mLeBroadcastReceiveState;
+ @Mock
+ private InputManager mInputManager;
private Context mContext;
private ShadowBluetoothAdapter mShadowBluetoothAdapter;
+ private final InputDevice mInputDevice = mock(InputDevice.class);
private static final String STRING_METADATA = "string_metadata";
private static final String LE_AUDIO_SHARING_METADATA = "le_audio_sharing";
private static final String BOOL_METADATA = "true";
private static final String INT_METADATA = "25";
private static final int METADATA_FAST_PAIR_CUSTOMIZED_FIELDS = 25;
+ private static final int TEST_DEVICE_ID = 123;
private static final String KEY_HEARABLE_CONTROL_SLICE = "HEARABLE_CONTROL_SLICE_WITH_WIDTH";
private static final String CONTROL_METADATA =
"<HEARABLE_CONTROL_SLICE_WITH_WIDTH>"
@@ -115,6 +123,7 @@ public class BluetoothUtilsTest {
private static final String FAKE_TEMP_BOND_METADATA = "<TEMP_BOND_TYPE>fake</TEMP_BOND_TYPE>";
private static final String TEST_EXCLUSIVE_MANAGER_PACKAGE = "com.test.manager";
private static final String TEST_EXCLUSIVE_MANAGER_COMPONENT = "com.test.manager/.component";
+ private static final String TEST_ADDRESS = "11:22:33:44:55:66";
private static final int TEST_BROADCAST_ID = 25;
@Rule public final SetFlagsRule mSetFlagsRule = new SetFlagsRule();
@@ -134,6 +143,10 @@ public class BluetoothUtilsTest {
when(mA2dpProfile.getProfileId()).thenReturn(BluetoothProfile.A2DP);
when(mLeAudioProfile.getProfileId()).thenReturn(BluetoothProfile.LE_AUDIO);
when(mHearingAid.getProfileId()).thenReturn(BluetoothProfile.HEARING_AID);
+ when(mContext.getSystemService(InputManager.class)).thenReturn(mInputManager);
+ when(mInputManager.getInputDeviceIds()).thenReturn(new int[]{TEST_DEVICE_ID});
+ when(mInputManager.getInputDeviceBluetoothAddress(TEST_DEVICE_ID)).thenReturn(TEST_ADDRESS);
+ when(mInputManager.getInputDevice(TEST_DEVICE_ID)).thenReturn(mInputDevice);
}
@Test
@@ -1097,9 +1110,8 @@ public class BluetoothUtilsTest {
@Test
public void getAudioDeviceAttributesForSpatialAudio_bleHeadset() {
- String address = "11:22:33:44:55:66";
when(mCachedBluetoothDevice.getDevice()).thenReturn(mBluetoothDevice);
- when(mCachedBluetoothDevice.getAddress()).thenReturn(address);
+ when(mCachedBluetoothDevice.getAddress()).thenReturn(TEST_ADDRESS);
when(mCachedBluetoothDevice.getProfiles()).thenReturn(List.of(mLeAudioProfile));
when(mLeAudioProfile.isEnabled(mBluetoothDevice)).thenReturn(true);
@@ -1112,14 +1124,13 @@ public class BluetoothUtilsTest {
new AudioDeviceAttributes(
AudioDeviceAttributes.ROLE_OUTPUT,
AudioDeviceInfo.TYPE_BLE_HEADSET,
- address));
+ TEST_ADDRESS));
}
@Test
public void getAudioDeviceAttributesForSpatialAudio_bleSpeaker() {
- String address = "11:22:33:44:55:66";
when(mCachedBluetoothDevice.getDevice()).thenReturn(mBluetoothDevice);
- when(mCachedBluetoothDevice.getAddress()).thenReturn(address);
+ when(mCachedBluetoothDevice.getAddress()).thenReturn(TEST_ADDRESS);
when(mCachedBluetoothDevice.getProfiles()).thenReturn(List.of(mLeAudioProfile));
when(mLeAudioProfile.isEnabled(mBluetoothDevice)).thenReturn(true);
@@ -1132,14 +1143,14 @@ public class BluetoothUtilsTest {
new AudioDeviceAttributes(
AudioDeviceAttributes.ROLE_OUTPUT,
AudioDeviceInfo.TYPE_BLE_SPEAKER,
- address));
+ TEST_ADDRESS));
}
@Test
public void getAudioDeviceAttributesForSpatialAudio_a2dp() {
- String address = "11:22:33:44:55:66";
+
when(mCachedBluetoothDevice.getDevice()).thenReturn(mBluetoothDevice);
- when(mCachedBluetoothDevice.getAddress()).thenReturn(address);
+ when(mCachedBluetoothDevice.getAddress()).thenReturn(TEST_ADDRESS);
when(mCachedBluetoothDevice.getProfiles()).thenReturn(List.of(mA2dpProfile));
when(mA2dpProfile.isEnabled(mBluetoothDevice)).thenReturn(true);
@@ -1152,14 +1163,13 @@ public class BluetoothUtilsTest {
new AudioDeviceAttributes(
AudioDeviceAttributes.ROLE_OUTPUT,
AudioDeviceInfo.TYPE_BLUETOOTH_A2DP,
- address));
+ TEST_ADDRESS));
}
@Test
public void getAudioDeviceAttributesForSpatialAudio_hearingAid() {
- String address = "11:22:33:44:55:66";
when(mCachedBluetoothDevice.getDevice()).thenReturn(mBluetoothDevice);
- when(mCachedBluetoothDevice.getAddress()).thenReturn(address);
+ when(mCachedBluetoothDevice.getAddress()).thenReturn(TEST_ADDRESS);
when(mCachedBluetoothDevice.getProfiles()).thenReturn(List.of(mHearingAid));
when(mHearingAid.isEnabled(mBluetoothDevice)).thenReturn(true);
@@ -1172,7 +1182,7 @@ public class BluetoothUtilsTest {
new AudioDeviceAttributes(
AudioDeviceAttributes.ROLE_OUTPUT,
AudioDeviceInfo.TYPE_HEARING_AID,
- address));
+ TEST_ADDRESS));
}
@Test
@@ -1375,4 +1385,54 @@ public class BluetoothUtilsTest {
verify(mBluetoothDevice).setMetadata(METADATA_FAST_PAIR_CUSTOMIZED_FIELDS,
TEMP_BOND_METADATA.getBytes());
}
+
+ @Test
+ public void getInputDevice_addressNotMatched_returnsNull() {
+ assertThat(getInputDevice(mContext, "123")).isNull();
+ }
+
+ @Test
+ public void getInputDevice_isInputDevice_returnsInputDevice() {
+ assertThat(getInputDevice(mContext, TEST_ADDRESS)).isEqualTo(mInputDevice);
+ }
+
+ @Test
+ public void isDeviceStylus_noDevices_false() {
+ assertThat(isDeviceStylus(null, null)).isFalse();
+ }
+
+ @Test
+ public void isDeviceStylus_nonStylusInputDevice_false() {
+ InputDevice inputDevice = new InputDevice.Builder()
+ .setSources(InputDevice.SOURCE_DPAD)
+ .build();
+
+ assertThat(isDeviceStylus(inputDevice, null)).isFalse();
+ }
+
+ @Test
+ public void isDeviceStylus_stylusInputDevice_true() {
+ InputDevice inputDevice = new InputDevice.Builder()
+ .setSources(InputDevice.SOURCE_STYLUS)
+ .build();
+
+ assertThat(isDeviceStylus(inputDevice, null)).isTrue();
+ }
+
+ @Test
+ public void isDeviceStylus_nonStylusBluetoothDevice_false() {
+ when(mBluetoothDevice.getMetadata(BluetoothDevice.METADATA_DEVICE_TYPE)).thenReturn(
+ BluetoothDevice.DEVICE_TYPE_WATCH.getBytes());
+
+ assertThat(isDeviceStylus(null, mCachedBluetoothDevice)).isFalse();
+ }
+
+ @Test
+ public void isDeviceStylus_stylusBluetoothDevice_true() {
+ when(mBluetoothDevice.getMetadata(BluetoothDevice.METADATA_DEVICE_TYPE)).thenReturn(
+ BluetoothDevice.DEVICE_TYPE_STYLUS.getBytes());
+ when(mCachedBluetoothDevice.getDevice()).thenReturn(mBluetoothDevice);
+
+ assertThat(isDeviceStylus(null, mCachedBluetoothDevice)).isTrue();
+ }
}
diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/bluetooth/CachedBluetoothDeviceManagerTest.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/bluetooth/CachedBluetoothDeviceManagerTest.java
index 69e99c616910..94199df4b9f8 100644
--- a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/bluetooth/CachedBluetoothDeviceManagerTest.java
+++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/bluetooth/CachedBluetoothDeviceManagerTest.java
@@ -662,10 +662,10 @@ public class CachedBluetoothDeviceManagerTest {
@Test
@RequiresFlagsEnabled(
com.android.settingslib.flags.Flags.FLAG_HEARING_DEVICE_SET_CONNECTION_STATUS_REPORT)
- public void onDeviceUnpaired_hearingDevice_callReportConnectionStatus() {
+ public void onDeviceUnpaired_containsHearingAidInfo_callReportConnectionStatus() {
when(mDevice1.getBondState()).thenReturn(BluetoothDevice.BOND_BONDED);
- when(mCachedDevice1.getProfiles()).thenReturn(
- ImmutableList.of(mHapClientProfile, mHearingAidProfile));
+ mCachedDevice1.setHearingAidInfo(
+ new HearingAidInfo.Builder().setHiSyncId(HISYNCID1).build());
mCachedDeviceManager.onDeviceUnpaired(mCachedDevice1);
diff --git a/packages/SettingsProvider/test/src/android/provider/SettingsBackupTest.java b/packages/SettingsProvider/test/src/android/provider/SettingsBackupTest.java
index 85617bad1a91..70c042cb8eba 100644
--- a/packages/SettingsProvider/test/src/android/provider/SettingsBackupTest.java
+++ b/packages/SettingsProvider/test/src/android/provider/SettingsBackupTest.java
@@ -811,7 +811,7 @@ public class SettingsBackupTest {
Settings.Secure.V_TO_U_RESTORE_ALLOWLIST,
Settings.Secure.V_TO_U_RESTORE_DENYLIST,
Settings.Secure.REDACT_OTP_NOTIFICATION_WHILE_CONNECTED_TO_WIFI,
- Settings.Secure.REDACT_OTP_NOTIFICATION_IMMEDIATELY);
+ Settings.Secure.OTP_NOTIFICATION_REDACTION_LOCK_TIME);
@Test
public void systemSettingsBackedUpOrDenied() {
diff --git a/packages/SystemUI/animation/src/com/android/systemui/animation/FontInterpolator.kt b/packages/SystemUI/animation/src/com/android/systemui/animation/FontInterpolator.kt
index f8bcb81dc073..bc75b1dad40c 100644
--- a/packages/SystemUI/animation/src/com/android/systemui/animation/FontInterpolator.kt
+++ b/packages/SystemUI/animation/src/com/android/systemui/animation/FontInterpolator.kt
@@ -22,19 +22,8 @@ import android.util.Log
import android.util.LruCache
import android.util.MathUtils
import androidx.annotation.VisibleForTesting
-import java.lang.Float.max
-import java.lang.Float.min
import kotlin.math.roundToInt
-private const val TAG_WGHT = "wght"
-private const val TAG_ITAL = "ital"
-
-private const val FONT_WEIGHT_DEFAULT_VALUE = 400f
-private const val FONT_ITALIC_MAX = 1f
-private const val FONT_ITALIC_MIN = 0f
-private const val FONT_ITALIC_ANIMATION_STEP = 0.1f
-private const val FONT_ITALIC_DEFAULT_VALUE = 0f
-
/** Caches for font interpolation */
interface FontCache {
val animationFrameCount: Int
@@ -91,11 +80,8 @@ class FontCacheImpl(override val animationFrameCount: Int = DEFAULT_FONT_CACHE_M
class FontInterpolator(val fontCache: FontCache = FontCacheImpl()) {
/** Linear interpolate the font variation settings. */
fun lerp(start: Font, end: Font, progress: Float, linearProgress: Float): Font {
- if (progress == 0f) {
- return start
- } else if (progress == 1f) {
- return end
- }
+ if (progress <= 0f) return start
+ if (progress >= 1f) return end
val startAxes = start.axes ?: EMPTY_AXES
val endAxes = end.axes ?: EMPTY_AXES
@@ -110,7 +96,7 @@ class FontInterpolator(val fontCache: FontCache = FontCacheImpl()) {
InterpKey(start, end, (linearProgress * fontCache.animationFrameCount).roundToInt())
fontCache.get(iKey)?.let {
if (DEBUG) {
- Log.d(LOG_TAG, "[$progress] Interp. cache hit for $iKey")
+ Log.d(LOG_TAG, "[$progress, $linearProgress] Interp. cache hit for $iKey")
}
return it
}
@@ -121,37 +107,16 @@ class FontInterpolator(val fontCache: FontCache = FontCacheImpl()) {
// and also pre-fill the missing axes value with default value from 'fvar' table.
val newAxes =
lerp(startAxes, endAxes) { tag, startValue, endValue ->
- when (tag) {
- TAG_WGHT ->
- MathUtils.lerp(
- startValue ?: FONT_WEIGHT_DEFAULT_VALUE,
- endValue ?: FONT_WEIGHT_DEFAULT_VALUE,
- progress,
- )
- TAG_ITAL ->
- adjustItalic(
- MathUtils.lerp(
- startValue ?: FONT_ITALIC_DEFAULT_VALUE,
- endValue ?: FONT_ITALIC_DEFAULT_VALUE,
- progress,
- )
- )
- else -> {
- require(startValue != null && endValue != null) {
- "Unable to interpolate due to unknown default axes value : $tag"
- }
- MathUtils.lerp(startValue, endValue, progress)
- }
- }
+ MathUtils.lerp(startValue, endValue, progress)
}
// Check if we already make font for this axes. This is typically happens if the animation
- // happens backward.
+ // happens backward and is being linearly interpolated.
val vKey = VarFontKey(start, newAxes)
fontCache.get(vKey)?.let {
fontCache.put(iKey, it)
if (DEBUG) {
- Log.d(LOG_TAG, "[$progress] Axis cache hit for $vKey")
+ Log.d(LOG_TAG, "[$progress, $linearProgress] Axis cache hit for $vKey")
}
return it
}
@@ -164,14 +129,14 @@ class FontInterpolator(val fontCache: FontCache = FontCacheImpl()) {
fontCache.put(vKey, newFont)
// Cache misses are likely to create memory leaks, so this is logged at error level.
- Log.e(LOG_TAG, "[$progress] Cache MISS for $iKey / $vKey")
+ Log.e(LOG_TAG, "[$progress, $linearProgress] Cache MISS for $iKey / $vKey")
return newFont
}
private fun lerp(
start: Array<FontVariationAxis>,
end: Array<FontVariationAxis>,
- filter: (tag: String, left: Float?, right: Float?) -> Float,
+ filter: (tag: String, left: Float, right: Float) -> Float,
): List<FontVariationAxis> {
// Safe to modify result of Font#getAxes since it returns cloned object.
start.sortBy { axis -> axis.tag }
@@ -191,39 +156,37 @@ class FontInterpolator(val fontCache: FontCache = FontCacheImpl()) {
else -> tagA.compareTo(tagB)
}
- val axis =
+ val tag =
+ when {
+ comp == 0 -> tagA!!
+ comp < 0 -> tagA!!
+ else -> tagB!!
+ }
+
+ val axisDefinition = GSFAxes.getAxis(tag)
+ require(comp == 0 || axisDefinition != null) {
+ "Unable to interpolate due to unknown default axes value: $tag"
+ }
+
+ val axisValue =
when {
- comp == 0 -> {
- val v = filter(tagA!!, start[i++].styleValue, end[j++].styleValue)
- FontVariationAxis(tagA, v)
- }
- comp < 0 -> {
- val v = filter(tagA!!, start[i++].styleValue, null)
- FontVariationAxis(tagA, v)
- }
- else -> { // comp > 0
- val v = filter(tagB!!, null, end[j++].styleValue)
- FontVariationAxis(tagB, v)
- }
+ comp == 0 -> filter(tag, start[i++].styleValue, end[j++].styleValue)
+ comp < 0 -> filter(tag, start[i++].styleValue, axisDefinition!!.defaultValue)
+ else -> filter(tag, axisDefinition!!.defaultValue, end[j++].styleValue)
}
- result.add(axis)
+ // Round axis value to valid intermediate steps. This improves the cache hit rate.
+ val step = axisDefinition?.animationStep ?: DEFAULT_ANIMATION_STEP
+ result.add(FontVariationAxis(tag, (axisValue / step).roundToInt() * step))
}
return result
}
- // For the performance reasons, we animate italic with FONT_ITALIC_ANIMATION_STEP. This helps
- // Cache hit ratio in the Skia glyph cache.
- private fun adjustItalic(value: Float) =
- coerceInWithStep(value, FONT_ITALIC_MIN, FONT_ITALIC_MAX, FONT_ITALIC_ANIMATION_STEP)
-
- private fun coerceInWithStep(v: Float, min: Float, max: Float, step: Float) =
- (v.coerceIn(min, max) / step).toInt() * step
-
companion object {
private const val LOG_TAG = "FontInterpolator"
private val DEBUG = Log.isLoggable(LOG_TAG, Log.DEBUG)
private val EMPTY_AXES = arrayOf<FontVariationAxis>()
+ private const val DEFAULT_ANIMATION_STEP = 1f
// Returns true if given two font instance can be interpolated.
fun canInterpolate(start: Font, end: Font) =
diff --git a/packages/SystemUI/animation/src/com/android/systemui/animation/FontVariationUtils.kt b/packages/SystemUI/animation/src/com/android/systemui/animation/FontVariationUtils.kt
index 9545bda80b2d..9a746870c6ff 100644
--- a/packages/SystemUI/animation/src/com/android/systemui/animation/FontVariationUtils.kt
+++ b/packages/SystemUI/animation/src/com/android/systemui/animation/FontVariationUtils.kt
@@ -1,12 +1,20 @@
-package com.android.systemui.animation
+/*
+ * Copyright (C) 2024 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.
+ */
-object GSFAxes {
- const val WEIGHT = "wght"
- const val WIDTH = "wdth"
- const val SLANT = "slnt"
- const val ROUND = "ROND"
- const val OPTICAL_SIZE = "opsz"
-}
+package com.android.systemui.animation
class FontVariationUtils {
private var mWeight = -1
@@ -46,20 +54,20 @@ class FontVariationUtils {
}
var resultString = ""
if (mWeight >= 0) {
- resultString += "'${GSFAxes.WEIGHT}' $mWeight"
+ resultString += "'${GSFAxes.WEIGHT.tag}' $mWeight"
}
if (mWidth >= 0) {
resultString +=
- (if (resultString.isBlank()) "" else ", ") + "'${GSFAxes.WIDTH}' $mWidth"
+ (if (resultString.isBlank()) "" else ", ") + "'${GSFAxes.WIDTH.tag}' $mWidth"
}
if (mOpticalSize >= 0) {
resultString +=
(if (resultString.isBlank()) "" else ", ") +
- "'${GSFAxes.OPTICAL_SIZE}' $mOpticalSize"
+ "'${GSFAxes.OPTICAL_SIZE.tag}' $mOpticalSize"
}
if (mRoundness >= 0) {
resultString +=
- (if (resultString.isBlank()) "" else ", ") + "'${GSFAxes.ROUND}' $mRoundness"
+ (if (resultString.isBlank()) "" else ", ") + "'${GSFAxes.ROUND.tag}' $mRoundness"
}
return if (isUpdated) resultString else ""
}
diff --git a/packages/SystemUI/animation/src/com/android/systemui/animation/GSFAxes.kt b/packages/SystemUI/animation/src/com/android/systemui/animation/GSFAxes.kt
new file mode 100644
index 000000000000..f4e03613169a
--- /dev/null
+++ b/packages/SystemUI/animation/src/com/android/systemui/animation/GSFAxes.kt
@@ -0,0 +1,98 @@
+/*
+ * Copyright (C) 2025 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.animation
+
+data class AxisDefinition(
+ val tag: String,
+ val minValue: Float,
+ val defaultValue: Float,
+ val maxValue: Float,
+ val animationStep: Float,
+)
+
+object GSFAxes {
+ val WEIGHT =
+ AxisDefinition(
+ tag = "wght",
+ minValue = 1f,
+ defaultValue = 400f,
+ maxValue = 1000f,
+ animationStep = 10f,
+ )
+
+ val WIDTH =
+ AxisDefinition(
+ tag = "wdth",
+ minValue = 25f,
+ defaultValue = 100f,
+ maxValue = 151f,
+ animationStep = 1f,
+ )
+
+ val SLANT =
+ AxisDefinition(
+ tag = "slnt",
+ minValue = 0f,
+ defaultValue = 0f,
+ maxValue = -10f,
+ animationStep = 0.1f,
+ )
+
+ val ROUND =
+ AxisDefinition(
+ tag = "ROND",
+ minValue = 0f,
+ defaultValue = 0f,
+ maxValue = 100f,
+ animationStep = 1f,
+ )
+
+ val GRADE =
+ AxisDefinition(
+ tag = "GRAD",
+ minValue = 0f,
+ defaultValue = 0f,
+ maxValue = 100f,
+ animationStep = 1f,
+ )
+
+ val OPTICAL_SIZE =
+ AxisDefinition(
+ tag = "opsz",
+ minValue = 6f,
+ defaultValue = 18f,
+ maxValue = 144f,
+ animationStep = 1f,
+ )
+
+ // Not a GSF Axis, but present for FontInterpolator compatibility
+ val ITALIC =
+ AxisDefinition(
+ tag = "ITAL",
+ minValue = 0f,
+ defaultValue = 0f,
+ maxValue = 1f,
+ animationStep = 0.1f,
+ )
+
+ private val AXIS_MAP =
+ listOf(WEIGHT, WIDTH, SLANT, ROUND, GRADE, OPTICAL_SIZE, ITALIC)
+ .map { def -> def.tag.toLowerCase() to def }
+ .toMap()
+
+ fun getAxis(axis: String): AxisDefinition? = AXIS_MAP[axis.toLowerCase()]
+}
diff --git a/packages/SystemUI/compose/features/src/com/android/systemui/notifications/ui/composable/NotificationsShadeOverlay.kt b/packages/SystemUI/compose/features/src/com/android/systemui/notifications/ui/composable/NotificationsShadeOverlay.kt
index 297995becfb2..7cd6c6b47f2a 100644
--- a/packages/SystemUI/compose/features/src/com/android/systemui/notifications/ui/composable/NotificationsShadeOverlay.kt
+++ b/packages/SystemUI/compose/features/src/com/android/systemui/notifications/ui/composable/NotificationsShadeOverlay.kt
@@ -124,7 +124,6 @@ constructor(
SmallClock(
burnInParams = burnIn.parameters,
onTopChanged = burnIn.onSmallClockTopChanged,
- modifier = Modifier.fillMaxWidth(),
)
}
}
diff --git a/packages/SystemUI/compose/features/src/com/android/systemui/shade/ui/composable/OverlayShade.kt b/packages/SystemUI/compose/features/src/com/android/systemui/shade/ui/composable/OverlayShade.kt
index cdb1e2e53b09..7e7b6297406e 100644
--- a/packages/SystemUI/compose/features/src/com/android/systemui/shade/ui/composable/OverlayShade.kt
+++ b/packages/SystemUI/compose/features/src/com/android/systemui/shade/ui/composable/OverlayShade.kt
@@ -54,6 +54,7 @@ import com.android.compose.animation.scene.ElementKey
import com.android.compose.animation.scene.LowestZIndexContentPicker
import com.android.compose.windowsizeclass.LocalWindowSizeClass
import com.android.systemui.res.R
+import androidx.compose.ui.unit.Dp
/** Renders a lightweight shade UI container, as an overlay. */
@Composable
@@ -202,10 +203,15 @@ object OverlayShade {
}
object Dimensions {
- val PanelCornerRadius = 46.dp
+ val PanelCornerRadius: Dp
+ @Composable
+ @ReadOnlyComposable get() =
+ dimensionResource(R.dimen.overlay_shade_panel_shape_radius)
}
object Shapes {
- val RoundedCornerPanel = RoundedCornerShape(Dimensions.PanelCornerRadius)
+ val RoundedCornerPanel: RoundedCornerShape
+ @Composable
+ @ReadOnlyComposable get() = RoundedCornerShape(Dimensions.PanelCornerRadius)
}
}
diff --git a/packages/SystemUI/compose/features/src/com/android/systemui/shade/ui/composable/ShadeHeader.kt b/packages/SystemUI/compose/features/src/com/android/systemui/shade/ui/composable/ShadeHeader.kt
index 5e12ee1b18fa..db9035b1635b 100644
--- a/packages/SystemUI/compose/features/src/com/android/systemui/shade/ui/composable/ShadeHeader.kt
+++ b/packages/SystemUI/compose/features/src/com/android/systemui/shade/ui/composable/ShadeHeader.kt
@@ -65,6 +65,7 @@ import com.android.compose.animation.scene.ValueKey
import com.android.compose.animation.scene.animateElementFloatAsState
import com.android.compose.animation.scene.content.state.TransitionState
import com.android.compose.modifiers.thenIf
+import com.android.compose.theme.colorAttr
import com.android.settingslib.Utils
import com.android.systemui.battery.BatteryMeterView
import com.android.systemui.battery.BatteryMeterViewController
@@ -75,8 +76,6 @@ import com.android.systemui.compose.modifiers.sysuiResTag
import com.android.systemui.privacy.OngoingPrivacyChip
import com.android.systemui.res.R
import com.android.systemui.scene.shared.model.Scenes
-import com.android.systemui.shade.ui.composable.ShadeHeader.Colors.chipBackground
-import com.android.systemui.shade.ui.composable.ShadeHeader.Colors.chipHighlighted
import com.android.systemui.shade.ui.composable.ShadeHeader.Colors.onScrimDim
import com.android.systemui.shade.ui.composable.ShadeHeader.Dimensions.ChipPaddingHorizontal
import com.android.systemui.shade.ui.composable.ShadeHeader.Dimensions.ChipPaddingVertical
@@ -107,7 +106,7 @@ object ShadeHeader {
object Dimensions {
val CollapsedHeight = 48.dp
val ExpandedHeight = 120.dp
- val ChipPaddingHorizontal = 8.dp
+ val ChipPaddingHorizontal = 6.dp
val ChipPaddingVertical = 4.dp
}
@@ -117,12 +116,6 @@ object ShadeHeader {
val ColorScheme.onScrimDim: Color
get() = Color.DarkGray
-
- val ColorScheme.chipBackground: Color
- get() = Color.DarkGray
-
- val ColorScheme.chipHighlighted: Color
- get() = Color.LightGray
}
object TestTags {
@@ -165,7 +158,7 @@ fun ContentScope.CollapsedShadeHeader(
VariableDayDate(
longerDateText = viewModel.longerDateText,
shorterDateText = viewModel.shorterDateText,
- chipHighlight = viewModel.notificationsChipHighlight,
+ textColor = colorAttr(android.R.attr.textColorPrimary),
modifier = Modifier.element(ShadeHeader.Elements.CollapsedContentStart),
)
}
@@ -265,7 +258,7 @@ fun ContentScope.ExpandedShadeHeader(
VariableDayDate(
longerDateText = viewModel.longerDateText,
shorterDateText = viewModel.shorterDateText,
- chipHighlight = viewModel.notificationsChipHighlight,
+ textColor = colorAttr(android.R.attr.textColorPrimary),
modifier = Modifier.widthIn(max = 90.dp),
)
Spacer(modifier = Modifier.weight(1f))
@@ -310,6 +303,7 @@ fun ContentScope.OverlayShadeHeader(
verticalAlignment = Alignment.CenterVertically,
modifier = Modifier.padding(horizontal = horizontalPadding),
) {
+ val chipHighlight = viewModel.notificationsChipHighlight
if (isShadeLayoutWide) {
Clock(
scale = 1f,
@@ -319,13 +313,13 @@ fun ContentScope.OverlayShadeHeader(
Spacer(modifier = Modifier.width(5.dp))
}
NotificationsChip(
- chipHighlight = viewModel.notificationsChipHighlight,
onClick = viewModel::onNotificationIconChipClicked,
+ backgroundColor = chipHighlight.backgroundColor(MaterialTheme.colorScheme),
) {
VariableDayDate(
longerDateText = viewModel.longerDateText,
shorterDateText = viewModel.shorterDateText,
- chipHighlight = viewModel.notificationsChipHighlight,
+ textColor = chipHighlight.foregroundColor(MaterialTheme.colorScheme),
)
}
}
@@ -338,14 +332,13 @@ fun ContentScope.OverlayShadeHeader(
) {
val chipHighlight = viewModel.quickSettingsChipHighlight
SystemIconChip(
- chipHighlight = chipHighlight,
+ backgroundColor = chipHighlight.backgroundColor(MaterialTheme.colorScheme),
onClick = viewModel::onSystemIconChipClicked,
) {
StatusIcons(
viewModel = viewModel,
useExpandedFormat = false,
modifier = Modifier.padding(end = 6.dp).weight(1f, fill = false),
- chipHighlight = chipHighlight,
)
BatteryIcon(
createBatteryMeterViewController =
@@ -515,6 +508,7 @@ private fun BatteryIcon(
batteryIcon.setPercentShowMode(
if (useExpandedFormat) BatteryMeterView.MODE_ESTIMATE else BatteryMeterView.MODE_ON
)
+ // TODO(b/397223606): Get the actual spec for this.
if (chipHighlight is HeaderChipHighlight.Strong) {
batteryIcon.updateColors(primaryColor, inverseColor, inverseColor)
} else if (chipHighlight is HeaderChipHighlight.Weak) {
@@ -553,7 +547,6 @@ private fun ContentScope.StatusIcons(
viewModel: ShadeHeaderViewModel,
useExpandedFormat: Boolean,
modifier: Modifier = Modifier,
- chipHighlight: HeaderChipHighlight = HeaderChipHighlight.None,
) {
val localContext = LocalContext.current
val themedContext =
@@ -581,6 +574,8 @@ private fun ContentScope.StatusIcons(
viewModel.createTintedIconManager(iconContainer, StatusBarLocation.QS)
}
+ val chipHighlight = viewModel.quickSettingsChipHighlight
+
AndroidView(
factory = { context ->
iconManager.setTint(primaryColor, inverseColor)
@@ -617,6 +612,7 @@ private fun ContentScope.StatusIcons(
iconContainer.removeIgnoredSlot(locationSlot)
}
+ // TODO(b/397223606): Get the actual spec for this.
if (chipHighlight is HeaderChipHighlight.Strong) {
iconManager.setTint(inverseColor, primaryColor)
} else if (chipHighlight is HeaderChipHighlight.Weak) {
@@ -629,16 +625,12 @@ private fun ContentScope.StatusIcons(
@Composable
private fun NotificationsChip(
- chipHighlight: HeaderChipHighlight,
onClick: () -> Unit,
modifier: Modifier = Modifier,
+ backgroundColor: Color = Color.Unspecified,
content: @Composable BoxScope.() -> Unit,
) {
val interactionSource = remember { MutableInteractionSource() }
- val chipBackground =
- with(MaterialTheme.colorScheme) {
- if (chipHighlight is HeaderChipHighlight.Strong) chipHighlighted else chipBackground
- }
Box(
modifier =
modifier
@@ -647,7 +639,7 @@ private fun NotificationsChip(
indication = null,
onClick = onClick,
)
- .background(chipBackground, RoundedCornerShape(25.dp))
+ .background(backgroundColor, RoundedCornerShape(25.dp))
.padding(horizontal = ChipPaddingHorizontal, vertical = ChipPaddingVertical)
) {
content()
@@ -657,7 +649,7 @@ private fun NotificationsChip(
@Composable
private fun SystemIconChip(
modifier: Modifier = Modifier,
- chipHighlight: HeaderChipHighlight = HeaderChipHighlight.None,
+ backgroundColor: Color = Color.Unspecified,
onClick: (() -> Unit)? = null,
content: @Composable RowScope.() -> Unit,
) {
@@ -667,16 +659,12 @@ private fun SystemIconChip(
with(MaterialTheme.colorScheme) {
Modifier.background(onScrimDim, RoundedCornerShape(CollapsedHeight / 4))
}
- val backgroundColor =
- with(MaterialTheme.colorScheme) {
- if (chipHighlight is HeaderChipHighlight.Strong) chipHighlighted else chipBackground
- }
Row(
verticalAlignment = Alignment.CenterVertically,
modifier =
modifier
- .thenIf(chipHighlight !is HeaderChipHighlight.None) {
+ .thenIf(backgroundColor != Color.Unspecified) {
Modifier.background(backgroundColor, RoundedCornerShape(25.dp))
.padding(horizontal = ChipPaddingHorizontal, vertical = ChipPaddingVertical)
}
diff --git a/packages/SystemUI/compose/features/src/com/android/systemui/shade/ui/composable/VariableDayDate.kt b/packages/SystemUI/compose/features/src/com/android/systemui/shade/ui/composable/VariableDayDate.kt
index 64aada52626b..8fbd0519cbdf 100644
--- a/packages/SystemUI/compose/features/src/com/android/systemui/shade/ui/composable/VariableDayDate.kt
+++ b/packages/SystemUI/compose/features/src/com/android/systemui/shade/ui/composable/VariableDayDate.kt
@@ -4,22 +4,16 @@ import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.Text
import androidx.compose.runtime.Composable
import androidx.compose.ui.Modifier
+import androidx.compose.ui.graphics.Color
import androidx.compose.ui.layout.Layout
-import com.android.compose.theme.colorAttr
-import com.android.systemui.shade.ui.viewmodel.ShadeHeaderViewModel.HeaderChipHighlight
@Composable
fun VariableDayDate(
longerDateText: String,
shorterDateText: String,
- chipHighlight: HeaderChipHighlight,
+ textColor: Color,
modifier: Modifier = Modifier,
) {
- val textColor =
- if (chipHighlight is HeaderChipHighlight.Strong)
- colorAttr(android.R.attr.textColorPrimaryInverse)
- else colorAttr(android.R.attr.textColorPrimary)
-
Layout(
contents =
listOf(
diff --git a/packages/SystemUI/customization/src/com/android/systemui/shared/clocks/FlexClockController.kt b/packages/SystemUI/customization/src/com/android/systemui/shared/clocks/FlexClockController.kt
index 004d1aa1fe93..ac1c5a8dfaf3 100644
--- a/packages/SystemUI/customization/src/com/android/systemui/shared/clocks/FlexClockController.kt
+++ b/packages/SystemUI/customization/src/com/android/systemui/shared/clocks/FlexClockController.kt
@@ -130,39 +130,25 @@ class FlexClockController(private val clockCtx: ClockContext) : ClockController
private val FONT_AXES =
listOf(
- ClockFontAxis(
- key = GSFAxes.WEIGHT,
+ GSFAxes.WEIGHT.toClockAxis(
type = AxisType.Float,
- minValue = 25f,
currentValue = 400f,
- maxValue = 1000f,
name = "Weight",
description = "Glyph Weight",
),
- ClockFontAxis(
- key = GSFAxes.WIDTH,
+ GSFAxes.WIDTH.toClockAxis(
type = AxisType.Float,
- minValue = 25f,
currentValue = 85f,
- maxValue = 151f,
name = "Width",
description = "Glyph Width",
),
- ClockFontAxis(
- key = GSFAxes.ROUND,
+ GSFAxes.ROUND.toClockAxis(
type = AxisType.Boolean,
- minValue = 0f,
- currentValue = 0f,
- maxValue = 100f,
name = "Round",
description = "Glyph Roundness",
),
- ClockFontAxis(
- key = GSFAxes.SLANT,
+ GSFAxes.SLANT.toClockAxis(
type = AxisType.Boolean,
- minValue = 0f,
- currentValue = 0f,
- maxValue = -10f,
name = "Slant",
description = "Glyph Slant",
),
@@ -170,10 +156,10 @@ class FlexClockController(private val clockCtx: ClockContext) : ClockController
private val LEGACY_FLEX_SETTINGS =
listOf(
- ClockFontAxisSetting(GSFAxes.WEIGHT, 600f),
- ClockFontAxisSetting(GSFAxes.WIDTH, 100f),
- ClockFontAxisSetting(GSFAxes.ROUND, 100f),
- ClockFontAxisSetting(GSFAxes.SLANT, 0f),
+ GSFAxes.WEIGHT.toClockAxisSetting(600f),
+ GSFAxes.WIDTH.toClockAxisSetting(100f),
+ GSFAxes.ROUND.toClockAxisSetting(100f),
+ GSFAxes.SLANT.toClockAxisSetting(0f),
)
}
}
diff --git a/packages/SystemUI/customization/src/com/android/systemui/shared/clocks/FlexClockFaceController.kt b/packages/SystemUI/customization/src/com/android/systemui/shared/clocks/FlexClockFaceController.kt
index b2dbd6552955..b4c2f5de290f 100644
--- a/packages/SystemUI/customization/src/com/android/systemui/shared/clocks/FlexClockFaceController.kt
+++ b/packages/SystemUI/customization/src/com/android/systemui/shared/clocks/FlexClockFaceController.kt
@@ -132,7 +132,7 @@ class FlexClockFaceController(clockCtx: ClockContext, private val isLargeClock:
if (!isLargeClock) {
axes =
axes.map { axis ->
- if (axis.key == GSFAxes.WIDTH && axis.value > SMALL_CLOCK_MAX_WDTH) {
+ if (axis.key == GSFAxes.WIDTH.tag && axis.value > SMALL_CLOCK_MAX_WDTH) {
axis.copy(value = SMALL_CLOCK_MAX_WDTH)
} else {
axis
diff --git a/packages/SystemUI/customization/src/com/android/systemui/shared/clocks/FontUtils.kt b/packages/SystemUI/customization/src/com/android/systemui/shared/clocks/FontUtils.kt
new file mode 100644
index 000000000000..212b1e29d1b8
--- /dev/null
+++ b/packages/SystemUI/customization/src/com/android/systemui/shared/clocks/FontUtils.kt
@@ -0,0 +1,43 @@
+/*
+ * Copyright (C) 2025 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.shared.clocks
+
+import com.android.systemui.animation.AxisDefinition
+import com.android.systemui.plugins.clocks.AxisType
+import com.android.systemui.plugins.clocks.ClockFontAxis
+import com.android.systemui.plugins.clocks.ClockFontAxisSetting
+
+fun AxisDefinition.toClockAxis(
+ type: AxisType,
+ currentValue: Float? = null,
+ name: String,
+ description: String,
+): ClockFontAxis {
+ return ClockFontAxis(
+ key = this.tag,
+ type = type,
+ maxValue = this.maxValue,
+ minValue = this.minValue,
+ currentValue = currentValue ?: this.defaultValue,
+ name = name,
+ description = description,
+ )
+}
+
+fun AxisDefinition.toClockAxisSetting(value: Float? = null): ClockFontAxisSetting {
+ return ClockFontAxisSetting(this.tag, value ?: this.defaultValue)
+}
diff --git a/packages/SystemUI/customization/src/com/android/systemui/shared/clocks/view/SimpleDigitalClockTextView.kt b/packages/SystemUI/customization/src/com/android/systemui/shared/clocks/view/SimpleDigitalClockTextView.kt
index 54be4d81ea06..b7ce20ec32d1 100644
--- a/packages/SystemUI/customization/src/com/android/systemui/shared/clocks/view/SimpleDigitalClockTextView.kt
+++ b/packages/SystemUI/customization/src/com/android/systemui/shared/clocks/view/SimpleDigitalClockTextView.kt
@@ -49,6 +49,7 @@ import com.android.systemui.shared.clocks.DigitTranslateAnimator
import com.android.systemui.shared.clocks.DimensionParser
import com.android.systemui.shared.clocks.FLEX_CLOCK_ID
import com.android.systemui.shared.clocks.FontTextStyle
+import com.android.systemui.shared.clocks.toClockAxisSetting
import java.lang.Thread
import kotlin.math.max
import kotlin.math.min
@@ -585,25 +586,25 @@ open class SimpleDigitalClockTextView(
val FIDGET_INTERPOLATOR = PathInterpolator(0.26873f, 0f, 0.45042f, 1f)
val FIDGET_DISTS =
mapOf(
- GSFAxes.WEIGHT to Pair(200f, 500f),
- GSFAxes.WIDTH to Pair(30f, 75f),
- GSFAxes.ROUND to Pair(0f, 50f),
- GSFAxes.SLANT to Pair(0f, -5f),
+ GSFAxes.WEIGHT.tag to Pair(200f, 500f),
+ GSFAxes.WIDTH.tag to Pair(30f, 75f),
+ GSFAxes.ROUND.tag to Pair(0f, 50f),
+ GSFAxes.SLANT.tag to Pair(0f, -5f),
)
val AOD_COLOR = Color.WHITE
- val LS_WEIGHT_AXIS = ClockFontAxisSetting(GSFAxes.WEIGHT, 400f)
- val AOD_WEIGHT_AXIS = ClockFontAxisSetting(GSFAxes.WEIGHT, 200f)
- val WIDTH_AXIS = ClockFontAxisSetting(GSFAxes.WIDTH, 85f)
- val ROUND_AXIS = ClockFontAxisSetting(GSFAxes.ROUND, 0f)
- val SLANT_AXIS = ClockFontAxisSetting(GSFAxes.SLANT, 0f)
+ val LS_WEIGHT_AXIS = GSFAxes.WEIGHT.toClockAxisSetting(400f)
+ val AOD_WEIGHT_AXIS = GSFAxes.WEIGHT.toClockAxisSetting(200f)
+ val WIDTH_AXIS = GSFAxes.WIDTH.toClockAxisSetting(85f)
+ val ROUND_AXIS = GSFAxes.ROUND.toClockAxisSetting(0f)
+ val SLANT_AXIS = GSFAxes.SLANT.toClockAxisSetting(0f)
// Axes for Legacy version of the Flex Clock
- val FLEX_LS_WEIGHT_AXIS = ClockFontAxisSetting(GSFAxes.WEIGHT, 600f)
- val FLEX_AOD_LARGE_WEIGHT_AXIS = ClockFontAxisSetting(GSFAxes.WEIGHT, 74f)
- val FLEX_AOD_SMALL_WEIGHT_AXIS = ClockFontAxisSetting(GSFAxes.WEIGHT, 133f)
- val FLEX_LS_WIDTH_AXIS = ClockFontAxisSetting(GSFAxes.WIDTH, 100f)
- val FLEX_AOD_WIDTH_AXIS = ClockFontAxisSetting(GSFAxes.WIDTH, 43f)
- val FLEX_ROUND_AXIS = ClockFontAxisSetting(GSFAxes.ROUND, 100f)
+ val FLEX_LS_WEIGHT_AXIS = GSFAxes.WEIGHT.toClockAxisSetting(600f)
+ val FLEX_AOD_LARGE_WEIGHT_AXIS = GSFAxes.WEIGHT.toClockAxisSetting(74f)
+ val FLEX_AOD_SMALL_WEIGHT_AXIS = GSFAxes.WEIGHT.toClockAxisSetting(133f)
+ val FLEX_LS_WIDTH_AXIS = GSFAxes.WIDTH.toClockAxisSetting(100f)
+ val FLEX_AOD_WIDTH_AXIS = GSFAxes.WIDTH.toClockAxisSetting(43f)
+ val FLEX_ROUND_AXIS = GSFAxes.ROUND.toClockAxisSetting(100f)
}
}
diff --git a/packages/SystemUI/multivalentTests/src/com/android/keyguard/EmergencyButtonControllerTest.kt b/packages/SystemUI/multivalentTests/src/com/android/keyguard/EmergencyButtonControllerTest.kt
index c42e25b20e0d..d046ad114fa5 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/keyguard/EmergencyButtonControllerTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/keyguard/EmergencyButtonControllerTest.kt
@@ -103,7 +103,7 @@ class EmergencyButtonControllerTest : SysuiTestCase() {
fun testUpdateEmergencyButton() {
Mockito.`when`(telecomManager.isInCall).thenReturn(true)
Mockito.`when`(lockPatternUtils.isSecure(anyInt())).thenReturn(true)
- Mockito.`when`(packageManager.hasSystemFeature(PackageManager.FEATURE_TELEPHONY))
+ Mockito.`when`(packageManager.hasSystemFeature(PackageManager.FEATURE_TELEPHONY_CALLING))
.thenReturn(true)
underTest.updateEmergencyCallButton()
backgroundExecutor.runAllReady()
@@ -112,7 +112,7 @@ class EmergencyButtonControllerTest : SysuiTestCase() {
/* isInCall= */ any(),
/* hasTelephonyRadio= */ any(),
/* simLocked= */ any(),
- /* isSecure= */ any()
+ /* isSecure= */ any(),
)
mainExecutor.runAllReady()
verify(emergencyButton)
@@ -120,7 +120,7 @@ class EmergencyButtonControllerTest : SysuiTestCase() {
/* isInCall= */ eq(true),
/* hasTelephonyRadio= */ eq(true),
/* simLocked= */ any(),
- /* isSecure= */ eq(true)
+ /* isSecure= */ eq(true),
)
}
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/animation/FontVariationUtilsTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/animation/FontVariationUtilsTest.kt
index f44769d522eb..8d3640d8d809 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/animation/FontVariationUtilsTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/animation/FontVariationUtilsTest.kt
@@ -21,7 +21,7 @@ class FontVariationUtilsTest : SysuiTestCase() {
roundness = 100,
)
Assert.assertEquals(
- "'${GSFAxes.WEIGHT}' 100, '${GSFAxes.WIDTH}' 100, '${GSFAxes.ROUND}' 100",
+ "'${GSFAxes.WEIGHT.tag}' 100, '${GSFAxes.WIDTH.tag}' 100, '${GSFAxes.ROUND.tag}' 100",
initFvar,
)
val updatedFvar =
@@ -32,7 +32,8 @@ class FontVariationUtilsTest : SysuiTestCase() {
roundness = 100,
)
Assert.assertEquals(
- "'${GSFAxes.WEIGHT}' 200, '${GSFAxes.WIDTH}' 100, '${GSFAxes.OPTICAL_SIZE}' 0, '${GSFAxes.ROUND}' 100",
+ "'${GSFAxes.WEIGHT.tag}' 200, '${GSFAxes.WIDTH.tag}' 100," +
+ " '${GSFAxes.OPTICAL_SIZE.tag}' 0, '${GSFAxes.ROUND.tag}' 100",
updatedFvar,
)
}
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/media/controls/ui/binder/SeekBarObserverTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/media/controls/ui/binder/SeekBarObserverTest.kt
index 4e14fec8408f..943ada9346e7 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/media/controls/ui/binder/SeekBarObserverTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/media/controls/ui/binder/SeekBarObserverTest.kt
@@ -18,6 +18,9 @@ package com.android.systemui.media.controls.ui.binder
import android.animation.Animator
import android.animation.ObjectAnimator
+import android.icu.text.MeasureFormat
+import android.icu.util.Measure
+import android.icu.util.MeasureUnit
import android.testing.TestableLooper
import android.view.View
import android.widget.SeekBar
@@ -30,6 +33,7 @@ import com.android.systemui.media.controls.ui.view.MediaViewHolder
import com.android.systemui.media.controls.ui.viewmodel.SeekBarViewModel
import com.android.systemui.res.R
import com.google.common.truth.Truth.assertThat
+import java.util.Locale
import org.junit.Before
import org.junit.Rule
import org.junit.Test
@@ -61,11 +65,11 @@ class SeekBarObserverTest : SysuiTestCase() {
fun setUp() {
context.orCreateTestableResources.addOverride(
R.dimen.qs_media_enabled_seekbar_height,
- enabledHeight
+ enabledHeight,
)
context.orCreateTestableResources.addOverride(
R.dimen.qs_media_disabled_seekbar_height,
- disabledHeight
+ disabledHeight,
)
seekBarView = SeekBar(context)
@@ -110,14 +114,31 @@ class SeekBarObserverTest : SysuiTestCase() {
@Test
fun seekBarProgress() {
+ val elapsedTime = 3000
+ val duration = (1.5 * 60 * 60 * 1000).toInt()
// WHEN part of the track has been played
- val data = SeekBarViewModel.Progress(true, true, true, false, 3000, 120000, true)
+ val data = SeekBarViewModel.Progress(true, true, true, false, elapsedTime, duration, true)
observer.onChanged(data)
// THEN seek bar shows the progress
- assertThat(seekBarView.progress).isEqualTo(3000)
- assertThat(seekBarView.max).isEqualTo(120000)
-
- val desc = context.getString(R.string.controls_media_seekbar_description, "00:03", "02:00")
+ assertThat(seekBarView.progress).isEqualTo(elapsedTime)
+ assertThat(seekBarView.max).isEqualTo(duration)
+
+ val expectedProgress =
+ MeasureFormat.getInstance(Locale.getDefault(), MeasureFormat.FormatWidth.WIDE)
+ .formatMeasures(Measure(3, MeasureUnit.SECOND))
+ val expectedDuration =
+ MeasureFormat.getInstance(Locale.getDefault(), MeasureFormat.FormatWidth.WIDE)
+ .formatMeasures(
+ Measure(1, MeasureUnit.HOUR),
+ Measure(30, MeasureUnit.MINUTE),
+ Measure(0, MeasureUnit.SECOND),
+ )
+ val desc =
+ context.getString(
+ R.string.controls_media_seekbar_description,
+ expectedProgress,
+ expectedDuration,
+ )
assertThat(seekBarView.contentDescription).isEqualTo(desc)
}
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/chips/call/domain/interactor/CallChipInteractorTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/chips/call/domain/interactor/CallChipInteractorTest.kt
index e38ea30daf0e..9fd189f35c32 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/chips/call/domain/interactor/CallChipInteractorTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/chips/call/domain/interactor/CallChipInteractorTest.kt
@@ -20,11 +20,14 @@ import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.SmallTest
import com.android.systemui.SysuiTestCase
import com.android.systemui.coroutines.collectLastValue
-import com.android.systemui.kosmos.Kosmos
-import com.android.systemui.kosmos.testScope
+import com.android.systemui.kosmos.collectLastValue
+import com.android.systemui.kosmos.runTest
+import com.android.systemui.kosmos.useUnconfinedTestDispatcher
import com.android.systemui.statusbar.phone.ongoingcall.data.repository.ongoingCallRepository
import com.android.systemui.statusbar.phone.ongoingcall.shared.model.OngoingCallModel
-import com.android.systemui.statusbar.phone.ongoingcall.shared.model.inCallModel
+import com.android.systemui.statusbar.phone.ongoingcall.shared.model.OngoingCallTestHelper.addOngoingCallState
+import com.android.systemui.statusbar.phone.ongoingcall.shared.model.OngoingCallTestHelper.removeOngoingCallState
+import com.android.systemui.testKosmos
import com.google.common.truth.Truth.assertThat
import kotlin.test.Test
import kotlinx.coroutines.test.runTest
@@ -33,22 +36,20 @@ import org.junit.runner.RunWith
@SmallTest
@RunWith(AndroidJUnit4::class)
class CallChipInteractorTest : SysuiTestCase() {
- val kosmos = Kosmos()
+ val kosmos = testKosmos().useUnconfinedTestDispatcher()
val repo = kosmos.ongoingCallRepository
val underTest = kosmos.callChipInteractor
@Test
- fun ongoingCallState_matchesRepo() =
- kosmos.testScope.runTest {
+ fun ongoingCallState_matchesState() =
+ kosmos.runTest {
val latest by collectLastValue(underTest.ongoingCallState)
- val inCall = inCallModel(startTimeMs = 1000)
- repo.setOngoingCallState(inCall)
- assertThat(latest).isEqualTo(inCall)
+ addOngoingCallState(key = "testKey")
+ assertThat(latest).isInstanceOf(OngoingCallModel.InCall::class.java)
- val noCall = OngoingCallModel.NoCall
- repo.setOngoingCallState(noCall)
- assertThat(latest).isEqualTo(noCall)
+ removeOngoingCallState(key = "testKey")
+ assertThat(latest).isEqualTo(OngoingCallModel.NoCall)
}
}
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/chips/call/ui/viewmodel/CallChipViewModelTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/chips/call/ui/viewmodel/CallChipViewModelTest.kt
index e044d1db92a9..fda4ab005446 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/chips/call/ui/viewmodel/CallChipViewModelTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/chips/call/ui/viewmodel/CallChipViewModelTest.kt
@@ -27,7 +27,9 @@ import com.android.systemui.animation.Expandable
import com.android.systemui.common.shared.model.ContentDescription.Companion.loadContentDescription
import com.android.systemui.common.shared.model.Icon
import com.android.systemui.coroutines.collectLastValue
-import com.android.systemui.kosmos.testScope
+import com.android.systemui.kosmos.collectLastValue
+import com.android.systemui.kosmos.runTest
+import com.android.systemui.kosmos.useUnconfinedTestDispatcher
import com.android.systemui.plugins.activityStarter
import com.android.systemui.res.R
import com.android.systemui.statusbar.StatusBarIconView
@@ -35,17 +37,11 @@ import com.android.systemui.statusbar.chips.ui.model.ColorsModel
import com.android.systemui.statusbar.chips.ui.model.OngoingActivityChipModel
import com.android.systemui.statusbar.chips.ui.view.ChipBackgroundContainer
import com.android.systemui.statusbar.core.StatusBarConnectedDisplays
-import com.android.systemui.statusbar.notification.data.model.activeNotificationModel
-import com.android.systemui.statusbar.notification.data.repository.ActiveNotificationListRepository
-import com.android.systemui.statusbar.notification.data.repository.ActiveNotificationsStore
-import com.android.systemui.statusbar.notification.data.repository.activeNotificationListRepository
import com.android.systemui.statusbar.notification.promoted.shared.model.PromotedNotificationContentModel
-import com.android.systemui.statusbar.notification.shared.CallType
import com.android.systemui.statusbar.phone.ongoingcall.DisableChipsModernization
import com.android.systemui.statusbar.phone.ongoingcall.EnableChipsModernization
-import com.android.systemui.statusbar.phone.ongoingcall.data.repository.ongoingCallRepository
-import com.android.systemui.statusbar.phone.ongoingcall.shared.model.OngoingCallModel
-import com.android.systemui.statusbar.phone.ongoingcall.shared.model.inCallModel
+import com.android.systemui.statusbar.phone.ongoingcall.shared.model.OngoingCallTestHelper.addOngoingCallState
+import com.android.systemui.statusbar.phone.ongoingcall.shared.model.OngoingCallTestHelper.removeOngoingCallState
import com.android.systemui.testKosmos
import com.android.systemui.util.time.fakeSystemClock
import com.google.common.truth.Truth.assertThat
@@ -60,10 +56,7 @@ import org.mockito.kotlin.whenever
@SmallTest
@RunWith(AndroidJUnit4::class)
class CallChipViewModelTest : SysuiTestCase() {
- private val kosmos = testKosmos()
- private val notificationListRepository = kosmos.activeNotificationListRepository
- private val testScope = kosmos.testScope
- private val repo = kosmos.ongoingCallRepository
+ private val kosmos = testKosmos().useUnconfinedTestDispatcher()
private val chipBackgroundView = mock<ChipBackgroundContainer>()
private val chipView =
@@ -82,53 +75,53 @@ class CallChipViewModelTest : SysuiTestCase() {
@Test
fun chip_noCall_isHidden() =
- testScope.runTest {
+ kosmos.runTest {
val latest by collectLastValue(underTest.chip)
- repo.setOngoingCallState(OngoingCallModel.NoCall)
+ removeOngoingCallState("testKey")
assertThat(latest).isInstanceOf(OngoingActivityChipModel.Inactive::class.java)
}
@Test
fun chip_inCall_zeroStartTime_isShownAsIconOnly() =
- testScope.runTest {
+ kosmos.runTest {
val latest by collectLastValue(underTest.chip)
- repo.setOngoingCallState(inCallModel(startTimeMs = 0))
+ addOngoingCallState(startTimeMs = 0)
assertThat(latest).isInstanceOf(OngoingActivityChipModel.Active.IconOnly::class.java)
}
@Test
fun chip_inCall_negativeStartTime_isShownAsIconOnly() =
- testScope.runTest {
+ kosmos.runTest {
val latest by collectLastValue(underTest.chip)
- repo.setOngoingCallState(inCallModel(startTimeMs = -2))
+ addOngoingCallState(startTimeMs = -2)
assertThat(latest).isInstanceOf(OngoingActivityChipModel.Active.IconOnly::class.java)
}
@Test
fun chip_inCall_positiveStartTime_isShownAsTimer() =
- testScope.runTest {
+ kosmos.runTest {
val latest by collectLastValue(underTest.chip)
- repo.setOngoingCallState(inCallModel(startTimeMs = 345))
+ addOngoingCallState(startTimeMs = 345)
assertThat(latest).isInstanceOf(OngoingActivityChipModel.Active.Timer::class.java)
}
@Test
fun chip_inCall_startTimeConvertedToElapsedRealtime() =
- testScope.runTest {
+ kosmos.runTest {
val latest by collectLastValue(underTest.chip)
kosmos.fakeSystemClock.setCurrentTimeMillis(3000)
kosmos.fakeSystemClock.setElapsedRealtime(400_000)
- repo.setOngoingCallState(inCallModel(startTimeMs = 1000))
+ addOngoingCallState(startTimeMs = 1000)
// The OngoingCallModel start time is relative to currentTimeMillis, so this call
// started 2000ms ago (1000 - 3000). The OngoingActivityChipModel start time needs to be
@@ -141,13 +134,11 @@ class CallChipViewModelTest : SysuiTestCase() {
@Test
@EnableFlags(StatusBarConnectedDisplays.FLAG_NAME)
fun chip_positiveStartTime_connectedDisplaysFlagOn_iconIsNotifIcon() =
- testScope.runTest {
+ kosmos.runTest {
val latest by collectLastValue(underTest.chip)
val notifKey = "testNotifKey"
- repo.setOngoingCallState(
- inCallModel(startTimeMs = 1000, notificationIcon = null, notificationKey = notifKey)
- )
+ addOngoingCallState(startTimeMs = 1000, statusBarChipIconView = null, key = notifKey)
assertThat((latest as OngoingActivityChipModel.Active).icon)
.isInstanceOf(
@@ -163,16 +154,14 @@ class CallChipViewModelTest : SysuiTestCase() {
@Test
@DisableFlags(StatusBarConnectedDisplays.FLAG_NAME)
fun chip_zeroStartTime_cdFlagOff_iconIsNotifIcon_withContentDescription() =
- testScope.runTest {
+ kosmos.runTest {
val latest by collectLastValue(underTest.chip)
val notifIcon = createStatusBarIconViewOrNull()
- repo.setOngoingCallState(
- inCallModel(
- startTimeMs = 0,
- notificationIcon = notifIcon,
- appName = "Fake app name",
- )
+ addOngoingCallState(
+ startTimeMs = 0,
+ statusBarChipIconView = notifIcon,
+ appName = "Fake app name",
)
assertThat((latest as OngoingActivityChipModel.Active).icon)
@@ -190,16 +179,13 @@ class CallChipViewModelTest : SysuiTestCase() {
@Test
@EnableFlags(StatusBarConnectedDisplays.FLAG_NAME)
fun chip_zeroStartTime_cdFlagOn_iconIsNotifKeyIcon_withContentDescription() =
- testScope.runTest {
+ kosmos.runTest {
val latest by collectLastValue(underTest.chip)
- repo.setOngoingCallState(
- inCallModel(
- startTimeMs = 0,
- notificationIcon = createStatusBarIconViewOrNull(),
- notificationKey = "notifKey",
- appName = "Fake app name",
- )
+ addOngoingCallState(
+ key = "notifKey",
+ statusBarChipIconView = createStatusBarIconViewOrNull(),
+ appName = "Fake app name",
)
assertThat((latest as OngoingActivityChipModel.Active).icon)
@@ -219,10 +205,10 @@ class CallChipViewModelTest : SysuiTestCase() {
@Test
@DisableFlags(StatusBarConnectedDisplays.FLAG_NAME)
fun chip_notifIconFlagOn_butNullNotifIcon_cdFlagOff_iconIsPhone() =
- testScope.runTest {
+ kosmos.runTest {
val latest by collectLastValue(underTest.chip)
- repo.setOngoingCallState(inCallModel(startTimeMs = 1000, notificationIcon = null))
+ addOngoingCallState(statusBarChipIconView = null)
assertThat((latest as OngoingActivityChipModel.Active).icon)
.isInstanceOf(OngoingActivityChipModel.ChipIcon.SingleColorIcon::class.java)
@@ -237,16 +223,13 @@ class CallChipViewModelTest : SysuiTestCase() {
@Test
@EnableFlags(StatusBarConnectedDisplays.FLAG_NAME)
fun chip_notifIconFlagOn_butNullNotifIcon_cdFlagOn_iconIsNotifKeyIcon_withContentDescription() =
- testScope.runTest {
+ kosmos.runTest {
val latest by collectLastValue(underTest.chip)
- repo.setOngoingCallState(
- inCallModel(
- startTimeMs = 1000,
- notificationIcon = null,
- notificationKey = "notifKey",
- appName = "Fake app name",
- )
+ addOngoingCallState(
+ key = "notifKey",
+ statusBarChipIconView = null,
+ appName = "Fake app name",
)
assertThat((latest as OngoingActivityChipModel.Active).icon)
@@ -265,10 +248,10 @@ class CallChipViewModelTest : SysuiTestCase() {
@Test
fun chip_positiveStartTime_colorsAreAccentThemed() =
- testScope.runTest {
+ kosmos.runTest {
val latest by collectLastValue(underTest.chip)
- repo.setOngoingCallState(inCallModel(startTimeMs = 1000, promotedContent = null))
+ addOngoingCallState(startTimeMs = 1000, promotedContent = null)
assertThat((latest as OngoingActivityChipModel.Active).colors)
.isEqualTo(ColorsModel.AccentThemed)
@@ -276,10 +259,10 @@ class CallChipViewModelTest : SysuiTestCase() {
@Test
fun chip_zeroStartTime_colorsAreAccentThemed() =
- testScope.runTest {
+ kosmos.runTest {
val latest by collectLastValue(underTest.chip)
- repo.setOngoingCallState(inCallModel(startTimeMs = 0, promotedContent = null))
+ addOngoingCallState(startTimeMs = 0, promotedContent = null)
assertThat((latest as OngoingActivityChipModel.Active).colors)
.isEqualTo(ColorsModel.AccentThemed)
@@ -287,19 +270,19 @@ class CallChipViewModelTest : SysuiTestCase() {
@Test
fun chip_resetsCorrectly() =
- testScope.runTest {
+ kosmos.runTest {
val latest by collectLastValue(underTest.chip)
kosmos.fakeSystemClock.setCurrentTimeMillis(3000)
kosmos.fakeSystemClock.setElapsedRealtime(400_000)
// Start a call
- repo.setOngoingCallState(inCallModel(startTimeMs = 1000))
+ addOngoingCallState(key = "testKey", startTimeMs = 1000)
assertThat(latest).isInstanceOf(OngoingActivityChipModel.Active::class.java)
assertThat((latest as OngoingActivityChipModel.Active.Timer).startTimeMs)
.isEqualTo(398_000)
// End the call
- repo.setOngoingCallState(OngoingCallModel.NoCall)
+ removeOngoingCallState(key = "testKey")
assertThat(latest).isInstanceOf(OngoingActivityChipModel.Inactive::class.java)
// Let 100_000ms elapse
@@ -307,7 +290,7 @@ class CallChipViewModelTest : SysuiTestCase() {
kosmos.fakeSystemClock.setElapsedRealtime(500_000)
// Start a new call, which started 1000ms ago
- repo.setOngoingCallState(inCallModel(startTimeMs = 102_000))
+ addOngoingCallState(key = "testKey", startTimeMs = 102_000)
assertThat(latest).isInstanceOf(OngoingActivityChipModel.Active::class.java)
assertThat((latest as OngoingActivityChipModel.Active.Timer).startTimeMs)
.isEqualTo(499_000)
@@ -316,10 +299,10 @@ class CallChipViewModelTest : SysuiTestCase() {
@Test
@DisableChipsModernization
fun chip_inCall_nullIntent_nullClickListener() =
- testScope.runTest {
+ kosmos.runTest {
val latest by collectLastValue(underTest.chip)
- repo.setOngoingCallState(inCallModel(startTimeMs = 1000, intent = null))
+ addOngoingCallState(contentIntent = null)
assertThat((latest as OngoingActivityChipModel.Active).onClickListenerLegacy).isNull()
}
@@ -327,11 +310,11 @@ class CallChipViewModelTest : SysuiTestCase() {
@Test
@DisableChipsModernization
fun chip_inCall_positiveStartTime_validIntent_clickListenerLaunchesIntent() =
- testScope.runTest {
+ kosmos.runTest {
val latest by collectLastValue(underTest.chip)
val pendingIntent = mock<PendingIntent>()
- repo.setOngoingCallState(inCallModel(startTimeMs = 1000, intent = pendingIntent))
+ addOngoingCallState(startTimeMs = 1000, contentIntent = pendingIntent)
val clickListener = (latest as OngoingActivityChipModel.Active).onClickListenerLegacy
assertThat(clickListener).isNotNull()
@@ -345,11 +328,11 @@ class CallChipViewModelTest : SysuiTestCase() {
@Test
@DisableChipsModernization
fun chip_inCall_zeroStartTime_validIntent_clickListenerLaunchesIntent() =
- testScope.runTest {
+ kosmos.runTest {
val latest by collectLastValue(underTest.chip)
val pendingIntent = mock<PendingIntent>()
- repo.setOngoingCallState(inCallModel(startTimeMs = 0, intent = pendingIntent))
+ addOngoingCallState(startTimeMs = 0, contentIntent = pendingIntent)
val clickListener = (latest as OngoingActivityChipModel.Active).onClickListenerLegacy
assertThat(clickListener).isNotNull()
@@ -364,14 +347,10 @@ class CallChipViewModelTest : SysuiTestCase() {
@Test
@EnableChipsModernization
fun chip_inCall_nullIntent_noneClickBehavior() =
- testScope.runTest {
+ kosmos.runTest {
val latest by collectLastValue(underTest.chip)
- postOngoingCallNotification(
- repository = notificationListRepository,
- startTimeMs = 1000L,
- intent = null,
- )
+ addOngoingCallState(startTimeMs = 1000, contentIntent = null)
assertThat((latest as OngoingActivityChipModel.Active).clickBehavior)
.isInstanceOf(OngoingActivityChipModel.ClickBehavior.None::class.java)
@@ -380,15 +359,11 @@ class CallChipViewModelTest : SysuiTestCase() {
@Test
@EnableChipsModernization
fun chip_inCall_positiveStartTime_validIntent_clickBehaviorLaunchesIntent() =
- testScope.runTest {
+ kosmos.runTest {
val latest by collectLastValue(underTest.chip)
val pendingIntent = mock<PendingIntent>()
- postOngoingCallNotification(
- repository = notificationListRepository,
- startTimeMs = 1000L,
- intent = pendingIntent,
- )
+ addOngoingCallState(startTimeMs = 1000, contentIntent = pendingIntent)
val clickBehavior = (latest as OngoingActivityChipModel.Active).clickBehavior
assertThat(clickBehavior)
@@ -405,15 +380,11 @@ class CallChipViewModelTest : SysuiTestCase() {
@Test
@EnableChipsModernization
fun chip_inCall_zeroStartTime_validIntent_clickBehaviorLaunchesIntent() =
- testScope.runTest {
+ kosmos.runTest {
val latest by collectLastValue(underTest.chip)
val pendingIntent = mock<PendingIntent>()
- postOngoingCallNotification(
- repository = notificationListRepository,
- startTimeMs = 0L,
- intent = pendingIntent,
- )
+ addOngoingCallState(startTimeMs = 0, contentIntent = pendingIntent)
val clickBehavior = (latest as OngoingActivityChipModel.Active).clickBehavior
assertThat(clickBehavior)
@@ -435,27 +406,6 @@ class CallChipViewModelTest : SysuiTestCase() {
mock<StatusBarIconView>()
}
- fun postOngoingCallNotification(
- repository: ActiveNotificationListRepository,
- startTimeMs: Long,
- intent: PendingIntent?,
- ) {
- repository.activeNotifications.value =
- ActiveNotificationsStore.Builder()
- .apply {
- addIndividualNotif(
- activeNotificationModel(
- key = "notif1",
- whenTime = startTimeMs,
- callType = CallType.Ongoing,
- statusBarChipIcon = null,
- contentIntent = intent,
- )
- )
- }
- .build()
- }
-
private val PROMOTED_CONTENT_WITH_COLOR =
PromotedNotificationContentModel.Builder("notif")
.apply {
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/chips/ui/viewmodel/OngoingActivityChipsViewModelTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/chips/ui/viewmodel/OngoingActivityChipsViewModelTest.kt
index 626dcd5b0864..719924c865fd 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/chips/ui/viewmodel/OngoingActivityChipsViewModelTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/chips/ui/viewmodel/OngoingActivityChipsViewModelTest.kt
@@ -22,15 +22,18 @@ import android.content.pm.PackageManager
import android.graphics.Bitmap
import android.graphics.drawable.BitmapDrawable
import android.platform.test.annotations.DisableFlags
+import android.platform.test.annotations.EnableFlags
import android.view.View
import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.SmallTest
import com.android.systemui.SysuiTestCase
import com.android.systemui.animation.Expandable
import com.android.systemui.common.shared.model.Icon
-import com.android.systemui.coroutines.collectLastValue
import com.android.systemui.kosmos.Kosmos
+import com.android.systemui.kosmos.collectLastValue
+import com.android.systemui.kosmos.runTest
import com.android.systemui.kosmos.testScope
+import com.android.systemui.kosmos.useUnconfinedTestDispatcher
import com.android.systemui.mediaprojection.data.model.MediaProjectionState
import com.android.systemui.mediaprojection.data.repository.fakeMediaProjectionRepository
import com.android.systemui.mediaprojection.taskswitcher.FakeActivityTaskManager.Companion.createTask
@@ -48,16 +51,13 @@ import com.android.systemui.statusbar.core.StatusBarConnectedDisplays
import com.android.systemui.statusbar.phone.SystemUIDialog
import com.android.systemui.statusbar.phone.mockSystemUIDialogFactory
import com.android.systemui.statusbar.phone.ongoingcall.StatusBarChipsModernization
-import com.android.systemui.statusbar.phone.ongoingcall.data.repository.ongoingCallRepository
-import com.android.systemui.statusbar.phone.ongoingcall.shared.model.OngoingCallModel
-import com.android.systemui.statusbar.phone.ongoingcall.shared.model.inCallModel
+import com.android.systemui.statusbar.phone.ongoingcall.shared.model.OngoingCallTestHelper.addOngoingCallState
+import com.android.systemui.statusbar.phone.ongoingcall.shared.model.OngoingCallTestHelper.removeOngoingCallState
import com.android.systemui.testKosmos
import com.android.systemui.util.time.fakeSystemClock
import com.google.common.truth.Truth.assertThat
import kotlinx.coroutines.flow.launchIn
import kotlinx.coroutines.flow.onEach
-import kotlinx.coroutines.test.runCurrent
-import kotlinx.coroutines.test.runTest
import org.junit.Before
import org.junit.Test
import org.junit.runner.RunWith
@@ -72,15 +72,14 @@ import org.mockito.kotlin.whenever
/** Tests for [OngoingActivityChipsViewModel] when the [StatusBarNotifChips] flag is disabled. */
@SmallTest
@RunWith(AndroidJUnit4::class)
+@EnableFlags(StatusBarChipsModernization.FLAG_NAME)
@DisableFlags(StatusBarNotifChips.FLAG_NAME)
class OngoingActivityChipsViewModelTest : SysuiTestCase() {
- private val kosmos = testKosmos()
- private val testScope = kosmos.testScope
+ private val kosmos = testKosmos().useUnconfinedTestDispatcher()
private val systemClock = kosmos.fakeSystemClock
private val screenRecordState = kosmos.screenRecordRepository.screenRecordState
private val mediaProjectionState = kosmos.fakeMediaProjectionRepository.mediaProjectionState
- private val callRepo = kosmos.ongoingCallRepository
private val mockSystemUIDialog = mock<SystemUIDialog>()
private val chipBackgroundView = mock<ChipBackgroundContainer>()
@@ -96,7 +95,7 @@ class OngoingActivityChipsViewModelTest : SysuiTestCase() {
private val mockExpandable: Expandable =
mock<Expandable>().apply { whenever(dialogTransitionController(any())).thenReturn(mock()) }
- private val underTest = kosmos.ongoingActivityChipsViewModel
+ private val Kosmos.underTest by Kosmos.Fixture { ongoingActivityChipsViewModel }
@Before
fun setUp() {
@@ -111,10 +110,10 @@ class OngoingActivityChipsViewModelTest : SysuiTestCase() {
@Test
fun primaryChip_allHidden_hidden() =
- testScope.runTest {
+ kosmos.runTest {
screenRecordState.value = ScreenRecordModel.DoingNothing
mediaProjectionState.value = MediaProjectionState.NotProjecting
- callRepo.setOngoingCallState(OngoingCallModel.NoCall)
+ removeOngoingCallState("testKey")
val latest by collectLastValue(underTest.primaryChip)
@@ -123,10 +122,10 @@ class OngoingActivityChipsViewModelTest : SysuiTestCase() {
@Test
fun primaryChip_screenRecordShow_restHidden_screenRecordShown() =
- testScope.runTest {
+ kosmos.runTest {
screenRecordState.value = ScreenRecordModel.Recording
mediaProjectionState.value = MediaProjectionState.NotProjecting
- callRepo.setOngoingCallState(OngoingCallModel.NoCall)
+ removeOngoingCallState("testKey")
val latest by collectLastValue(underTest.primaryChip)
@@ -135,10 +134,10 @@ class OngoingActivityChipsViewModelTest : SysuiTestCase() {
@Test
fun primaryChip_screenRecordShowAndCallShow_screenRecordShown() =
- testScope.runTest {
+ kosmos.runTest {
screenRecordState.value = ScreenRecordModel.Recording
- callRepo.setOngoingCallState(inCallModel(startTimeMs = 34))
+ addOngoingCallState()
val latest by collectLastValue(underTest.primaryChip)
@@ -147,11 +146,11 @@ class OngoingActivityChipsViewModelTest : SysuiTestCase() {
@Test
fun primaryChip_screenRecordShowAndShareToAppShow_screenRecordShown() =
- testScope.runTest {
+ kosmos.runTest {
screenRecordState.value = ScreenRecordModel.Recording
mediaProjectionState.value =
MediaProjectionState.Projecting.EntireScreen(NORMAL_PACKAGE)
- callRepo.setOngoingCallState(OngoingCallModel.NoCall)
+ removeOngoingCallState("testKey")
val latest by collectLastValue(underTest.primaryChip)
@@ -160,11 +159,11 @@ class OngoingActivityChipsViewModelTest : SysuiTestCase() {
@Test
fun primaryChip_shareToAppShowAndCallShow_shareToAppShown() =
- testScope.runTest {
+ kosmos.runTest {
screenRecordState.value = ScreenRecordModel.DoingNothing
mediaProjectionState.value =
MediaProjectionState.Projecting.EntireScreen(NORMAL_PACKAGE)
- callRepo.setOngoingCallState(inCallModel(startTimeMs = 34))
+ addOngoingCallState()
val latest by collectLastValue(underTest.primaryChip)
@@ -173,15 +172,13 @@ class OngoingActivityChipsViewModelTest : SysuiTestCase() {
@Test
fun primaryChip_screenRecordAndShareToAppAndCastToOtherHideAndCallShown_callShown() =
- testScope.runTest {
+ kosmos.runTest {
val notificationKey = "call"
screenRecordState.value = ScreenRecordModel.DoingNothing
// MediaProjection covers both share-to-app and cast-to-other-device
mediaProjectionState.value = MediaProjectionState.NotProjecting
- callRepo.setOngoingCallState(
- inCallModel(startTimeMs = 34, notificationKey = notificationKey)
- )
+ addOngoingCallState(key = notificationKey)
val latest by collectLastValue(underTest.primaryChip)
@@ -190,12 +187,10 @@ class OngoingActivityChipsViewModelTest : SysuiTestCase() {
@Test
fun primaryChip_higherPriorityChipAdded_lowerPriorityChipReplaced() =
- testScope.runTest {
+ kosmos.runTest {
// Start with just the lowest priority chip shown
val callNotificationKey = "call"
- callRepo.setOngoingCallState(
- inCallModel(startTimeMs = 34, notificationKey = callNotificationKey)
- )
+ addOngoingCallState(key = callNotificationKey)
// And everything else hidden
mediaProjectionState.value = MediaProjectionState.NotProjecting
screenRecordState.value = ScreenRecordModel.DoingNothing
@@ -224,15 +219,13 @@ class OngoingActivityChipsViewModelTest : SysuiTestCase() {
@Test
fun primaryChip_highestPriorityChipRemoved_showsNextPriorityChip() =
- testScope.runTest {
+ kosmos.runTest {
// WHEN all chips are active
screenRecordState.value = ScreenRecordModel.Recording
mediaProjectionState.value =
MediaProjectionState.Projecting.EntireScreen(NORMAL_PACKAGE)
val callNotificationKey = "call"
- callRepo.setOngoingCallState(
- inCallModel(startTimeMs = 34, notificationKey = callNotificationKey)
- )
+ addOngoingCallState(key = callNotificationKey)
val latest by collectLastValue(underTest.primaryChip)
@@ -255,17 +248,15 @@ class OngoingActivityChipsViewModelTest : SysuiTestCase() {
/** Regression test for b/347726238. */
@Test
fun primaryChip_timerDoesNotResetAfterSubscribersRestart() =
- testScope.runTest {
+ kosmos.runTest {
var latest: OngoingActivityChipModel? = null
- val job1 = underTest.primaryChip.onEach { latest = it }.launchIn(this)
+ val job1 = underTest.primaryChip.onEach { latest = it }.launchIn(kosmos.testScope)
// Start a chip with a timer
systemClock.setElapsedRealtime(1234)
screenRecordState.value = ScreenRecordModel.Recording
- runCurrent()
-
assertThat((latest as OngoingActivityChipModel.Active.Timer).startTimeMs)
.isEqualTo(1234)
@@ -276,9 +267,7 @@ class OngoingActivityChipsViewModelTest : SysuiTestCase() {
systemClock.setElapsedRealtime(5678)
// WHEN we re-subscribe to the chip flow
- val job2 = underTest.primaryChip.onEach { latest = it }.launchIn(this)
-
- runCurrent()
+ val job2 = underTest.primaryChip.onEach { latest = it }.launchIn(kosmos.testScope)
// THEN the old start time is still used
assertThat((latest as OngoingActivityChipModel.Active.Timer).startTimeMs)
@@ -289,14 +278,14 @@ class OngoingActivityChipsViewModelTest : SysuiTestCase() {
@Test
fun primaryChip_screenRecordStoppedViaDialog_chipHiddenWithoutAnimation() =
- testScope.runTest {
+ kosmos.runTest {
screenRecordState.value = ScreenRecordModel.Recording
mediaProjectionState.value =
MediaProjectionState.Projecting.EntireScreen(
NORMAL_PACKAGE,
hostDeviceName = "Recording Display",
)
- callRepo.setOngoingCallState(OngoingCallModel.NoCall)
+ removeOngoingCallState("testKey")
val latest by collectLastValue(underTest.primaryChip)
@@ -319,11 +308,11 @@ class OngoingActivityChipsViewModelTest : SysuiTestCase() {
@Test
fun primaryChip_projectionStoppedViaDialog_chipHiddenWithoutAnimation() =
- testScope.runTest {
+ kosmos.runTest {
mediaProjectionState.value =
MediaProjectionState.Projecting.EntireScreen(NORMAL_PACKAGE)
screenRecordState.value = ScreenRecordModel.DoingNothing
- callRepo.setOngoingCallState(OngoingCallModel.NoCall)
+ removeOngoingCallState("testKey")
val latest by collectLastValue(underTest.primaryChip)
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/data/repository/StatusBarModeRepositoryImplTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/data/repository/StatusBarModeRepositoryImplTest.kt
index ab475c5edb76..2f6bedb42e45 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/data/repository/StatusBarModeRepositoryImplTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/data/repository/StatusBarModeRepositoryImplTest.kt
@@ -37,6 +37,8 @@ import com.android.systemui.statusbar.layout.LetterboxAppearance
import com.android.systemui.statusbar.layout.LetterboxAppearanceCalculator
import com.android.systemui.statusbar.layout.StatusBarBoundsProvider
import com.android.systemui.statusbar.phone.fragment.dagger.HomeStatusBarComponent
+import com.android.systemui.statusbar.phone.ongoingcall.DisableChipsModernization
+import com.android.systemui.statusbar.phone.ongoingcall.EnableChipsModernization
import com.android.systemui.statusbar.phone.ongoingcall.data.repository.ongoingCallRepository
import com.android.systemui.statusbar.phone.ongoingcall.shared.model.OngoingCallModel
import com.android.systemui.statusbar.phone.ongoingcall.shared.model.inCallModel
@@ -387,6 +389,7 @@ class StatusBarModeRepositoryImplTest : SysuiTestCase() {
}
@Test
+ @DisableChipsModernization
fun statusBarMode_ongoingCallAndFullscreen_semiTransparent() =
testScope.runTest {
val latest by collectLastValue(underTest.statusBarAppearance)
@@ -398,6 +401,19 @@ class StatusBarModeRepositoryImplTest : SysuiTestCase() {
}
@Test
+ @EnableChipsModernization
+ fun statusBarMode_ongoingProcessRequiresStatusBarVisible_andFullscreen_semiTransparent() =
+ testScope.runTest {
+ val latest by collectLastValue(underTest.statusBarAppearance)
+
+ underTest.setOngoingProcessRequiresStatusBarVisible(true)
+ onSystemBarAttributesChanged(requestedVisibleTypes = WindowInsets.Type.navigationBars())
+
+ assertThat(latest!!.mode).isEqualTo(StatusBarMode.SEMI_TRANSPARENT)
+ }
+
+ @Test
+ @DisableChipsModernization
fun statusBarMode_ongoingCallButNotFullscreen_matchesAppearance() =
testScope.runTest {
val latest by collectLastValue(underTest.statusBarAppearance)
@@ -413,6 +429,23 @@ class StatusBarModeRepositoryImplTest : SysuiTestCase() {
}
@Test
+ @EnableChipsModernization
+ fun statusBarMode_ongoingProcessRequiresStatusBarVisible_butNotFullscreen_matchesAppearance() =
+ testScope.runTest {
+ val latest by collectLastValue(underTest.statusBarAppearance)
+
+ underTest.setOngoingProcessRequiresStatusBarVisible(true)
+
+ onSystemBarAttributesChanged(
+ requestedVisibleTypes = WindowInsets.Type.statusBars(),
+ appearance = APPEARANCE_OPAQUE_STATUS_BARS,
+ )
+
+ assertThat(latest!!.mode).isEqualTo(StatusBarMode.OPAQUE)
+ }
+
+ @Test
+ @DisableChipsModernization
fun statusBarMode_fullscreenButNotOngoingCall_matchesAppearance() =
testScope.runTest {
val latest by collectLastValue(underTest.statusBarAppearance)
@@ -427,6 +460,21 @@ class StatusBarModeRepositoryImplTest : SysuiTestCase() {
}
@Test
+ @EnableChipsModernization
+ fun statusBarMode_fullscreen_butNotOngoingProcessRequiresStatusBarVisible_matchesAppearance() =
+ testScope.runTest {
+ val latest by collectLastValue(underTest.statusBarAppearance)
+
+ underTest.setOngoingProcessRequiresStatusBarVisible(false)
+ onSystemBarAttributesChanged(
+ requestedVisibleTypes = WindowInsets.Type.navigationBars(),
+ appearance = APPEARANCE_OPAQUE_STATUS_BARS,
+ )
+
+ assertThat(latest!!.mode).isEqualTo(StatusBarMode.OPAQUE)
+ }
+
+ @Test
fun statusBarMode_transientShown_semiTransparent() =
testScope.runTest {
val latest by collectLastValue(underTest.statusBarAppearance)
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/NotificationTransitionAnimatorControllerTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/NotificationTransitionAnimatorControllerTest.kt
index a1772e3f62ed..510167d10db4 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/NotificationTransitionAnimatorControllerTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/NotificationTransitionAnimatorControllerTest.kt
@@ -10,6 +10,7 @@ import com.android.systemui.coroutines.collectLastValue
import com.android.systemui.jank.interactionJankMonitor
import com.android.systemui.kosmos.testScope
import com.android.systemui.res.R
+import com.android.systemui.statusbar.notification.collection.GroupEntry
import com.android.systemui.statusbar.notification.collection.GroupEntryBuilder
import com.android.systemui.statusbar.notification.collection.NotificationEntryBuilder
import com.android.systemui.statusbar.notification.data.repository.NotificationLaunchAnimationRepository
@@ -165,7 +166,10 @@ class NotificationTransitionAnimatorControllerTest : SysuiTestCase() {
.setSummary(summary)
.addChild(notification.entry)
.build()
- assertSame(summary, notification.entry.parent?.summary)
+
+ val parentSummary = if (notification.entry.parent is GroupEntry)
+ (notification.entry.parent as GroupEntry).summary else null
+ assertSame(summary, parentSummary)
`when`(headsUpManager.isHeadsUpEntry(notificationKey)).thenReturn(false)
`when`(headsUpManager.isHeadsUpEntry(summary.key)).thenReturn(true)
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/collection/BundleEntryTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/collection/BundleEntryTest.kt
index 426af264da07..83e26c4220b1 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/collection/BundleEntryTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/collection/BundleEntryTest.kt
@@ -16,6 +16,8 @@
package com.android.systemui.statusbar.notification.collection
+import android.app.Notification
+import android.graphics.Color
import android.platform.test.annotations.EnableFlags
import android.platform.test.flag.junit.SetFlagsRule
import android.testing.TestableLooper.RunWithLooper
@@ -72,4 +74,35 @@ class BundleEntryTest : SysuiTestCase() {
fun getKey_adapter() {
assertThat(underTest.entryAdapter.key).isEqualTo("key")
}
+
+ @Test
+ @EnableFlags(NotificationBundleUi.FLAG_NAME)
+ fun isClearable_adapter() {
+ assertThat(underTest.entryAdapter.isClearable).isTrue()
+ }
+
+ @Test
+ @EnableFlags(NotificationBundleUi.FLAG_NAME)
+ fun getSummarization_adapter() {
+ assertThat(underTest.entryAdapter.summarization).isNull()
+ }
+
+ @Test
+ @EnableFlags(NotificationBundleUi.FLAG_NAME)
+ fun getContrastedColor_adapter() {
+ assertThat(underTest.entryAdapter.getContrastedColor(context, false, Color.WHITE))
+ .isEqualTo(Notification.COLOR_DEFAULT)
+ }
+
+ @Test
+ @EnableFlags(NotificationBundleUi.FLAG_NAME)
+ fun canPeek_adapter() {
+ assertThat(underTest.entryAdapter.canPeek()).isFalse()
+ }
+
+ @Test
+ @EnableFlags(NotificationBundleUi.FLAG_NAME)
+ fun getWhen_adapter() {
+ assertThat(underTest.entryAdapter.`when`).isEqualTo(0)
+ }
} \ No newline at end of file
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/NotificationEntryTest.java b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/collection/NotificationEntryTest.java
index 19d1224a9bf3..1f5c6722f38e 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/NotificationEntryTest.java
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/collection/NotificationEntryTest.java
@@ -163,9 +163,10 @@ public class NotificationEntryTest extends SysuiTestCase {
@Test
public void testIsExemptFromDndVisualSuppression_media() {
+ MediaSession session = new MediaSession(mContext, "test");
Notification.Builder n = new Notification.Builder(mContext, "")
.setStyle(new Notification.MediaStyle()
- .setMediaSession(mock(MediaSession.Token.class)))
+ .setMediaSession(session.getSessionToken()))
.setSmallIcon(R.drawable.ic_person)
.setContentTitle("Title")
.setContentText("Text");
@@ -593,6 +594,76 @@ public class NotificationEntryTest extends SysuiTestCase {
assertThat(entry.getEntryAdapter().getGroupRoot()).isEqualTo(parent.getEntryAdapter());
}
+ @Test
+ @EnableFlags(NotificationBundleUi.FLAG_NAME)
+ public void isClearable_adapter() {
+ ExpandableNotificationRow row = mock(ExpandableNotificationRow.class);
+ Notification notification = new Notification.Builder(mContext, "")
+ .setSmallIcon(R.drawable.ic_person)
+ .build();
+
+ NotificationEntry entry = new NotificationEntryBuilder()
+ .setPkg(TEST_PACKAGE_NAME)
+ .setOpPkg(TEST_PACKAGE_NAME)
+ .setUid(TEST_UID)
+ .setChannel(mChannel)
+ .setId(mId++)
+ .setNotification(notification)
+ .setUser(new UserHandle(ActivityManager.getCurrentUser()))
+ .build();
+ entry.setRow(row);
+
+ assertThat(entry.getEntryAdapter().isClearable()).isEqualTo(entry.isClearable());
+ }
+
+ @Test
+ @EnableFlags(NotificationBundleUi.FLAG_NAME)
+ public void getSummarization_adapter() {
+ ExpandableNotificationRow row = mock(ExpandableNotificationRow.class);
+ Notification notification = new Notification.Builder(mContext, "")
+ .setSmallIcon(R.drawable.ic_person)
+ .build();
+
+ NotificationEntry entry = new NotificationEntryBuilder()
+ .setPkg(TEST_PACKAGE_NAME)
+ .setOpPkg(TEST_PACKAGE_NAME)
+ .setUid(TEST_UID)
+ .setChannel(mChannel)
+ .setId(mId++)
+ .setNotification(notification)
+ .setUser(new UserHandle(ActivityManager.getCurrentUser()))
+ .build();
+ Ranking ranking = new RankingBuilder(entry.getRanking())
+ .setSummarization("hello")
+ .build();
+ entry.setRanking(ranking);
+ entry.setRow(row);
+
+ assertThat(entry.getEntryAdapter().getSummarization()).isEqualTo("hello");
+ }
+
+ @Test
+ @EnableFlags(NotificationBundleUi.FLAG_NAME)
+ public void getIcons_adapter() {
+ ExpandableNotificationRow row = mock(ExpandableNotificationRow.class);
+ Notification notification = new Notification.Builder(mContext, "")
+ .setSmallIcon(R.drawable.ic_person)
+ .build();
+
+ NotificationEntry entry = new NotificationEntryBuilder()
+ .setPkg(TEST_PACKAGE_NAME)
+ .setOpPkg(TEST_PACKAGE_NAME)
+ .setUid(TEST_UID)
+ .setChannel(mChannel)
+ .setId(mId++)
+ .setNotification(notification)
+ .setUser(new UserHandle(ActivityManager.getCurrentUser()))
+ .build();
+ entry.setRow(row);
+
+ assertThat(entry.getEntryAdapter().getIcons()).isEqualTo(entry.getIcons());
+ }
+
private Notification.Action createContextualAction(String title) {
return new Notification.Action.Builder(
Icon.createWithResource(getContext(), android.R.drawable.sym_def_app_icon),
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/collection/coordinator/OriginalUnseenKeyguardCoordinatorTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/collection/coordinator/OriginalUnseenKeyguardCoordinatorTest.kt
index 7781df1ad91f..43cb9575b609 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/collection/coordinator/OriginalUnseenKeyguardCoordinatorTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/collection/coordinator/OriginalUnseenKeyguardCoordinatorTest.kt
@@ -16,8 +16,11 @@
package com.android.systemui.statusbar.notification.collection.coordinator
import android.app.Notification
+import android.app.Notification.MediaStyle
+import android.media.session.MediaSession
import android.platform.test.flag.junit.FlagsParameterization
import android.provider.Settings
+import android.service.notification.StatusBarNotification
import androidx.test.filters.SmallTest
import com.android.systemui.SysuiTestCase
import com.android.systemui.dump.dumpManager
@@ -36,6 +39,7 @@ import com.android.systemui.scene.data.repository.Idle
import com.android.systemui.scene.data.repository.setTransition
import com.android.systemui.scene.domain.interactor.sceneInteractor
import com.android.systemui.scene.shared.model.Scenes
+import com.android.systemui.statusbar.SbnBuilder
import com.android.systemui.statusbar.notification.collection.GroupEntryBuilder
import com.android.systemui.statusbar.notification.collection.NotifPipeline
import com.android.systemui.statusbar.notification.collection.NotificationEntryBuilder
@@ -217,6 +221,16 @@ class OriginalUnseenKeyguardCoordinatorTest(flags: FlagsParameterization) : Sysu
mock<ExpandableNotificationRow>().apply {
whenever(isMediaRow).thenReturn(true)
}
+ sbn = SbnBuilder().setNotification(
+ Notification.Builder(context, "channel").setStyle(
+ MediaStyle().setMediaSession(
+ MediaSession(
+ context,
+ "tag"
+ ).sessionToken
+ )
+ ).build()
+ ).build()
}
collectionListener.onEntryAdded(fakeEntry)
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/collection/render/NodeSpecBuilderTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/collection/render/NodeSpecBuilderTest.kt
index 99bd4fc549a8..d9c91771043b 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/collection/render/NodeSpecBuilderTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/collection/render/NodeSpecBuilderTest.kt
@@ -26,6 +26,7 @@ import com.android.systemui.statusbar.notification.collection.GroupEntryBuilder
import com.android.systemui.statusbar.notification.collection.ListEntry
import com.android.systemui.statusbar.notification.collection.NotificationEntry
import com.android.systemui.statusbar.notification.collection.NotificationEntryBuilder
+import com.android.systemui.statusbar.notification.collection.PipelineEntry
import com.android.systemui.statusbar.notification.collection.getAttachState
import com.android.systemui.statusbar.notification.collection.listbuilder.NotifSection
import com.android.systemui.statusbar.notification.collection.listbuilder.pluggable.NotifSectioner
@@ -409,7 +410,7 @@ private fun buildSection(
): NotifSection {
return NotifSection(object : NotifSectioner("Section $index (bucket=$bucket)", bucket) {
- override fun isInSection(entry: ListEntry?): Boolean {
+ override fun isInSection(entry: PipelineEntry?): Boolean {
throw NotImplementedError("This should never be called")
}
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/collection/render/RenderStageManagerTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/collection/render/RenderStageManagerTest.kt
index e6fbc725af04..4a05804f663a 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/collection/render/RenderStageManagerTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/collection/render/RenderStageManagerTest.kt
@@ -24,6 +24,7 @@ import com.android.systemui.statusbar.notification.collection.GroupEntryBuilder
import com.android.systemui.statusbar.notification.collection.ListEntry
import com.android.systemui.statusbar.notification.collection.NotificationEntry
import com.android.systemui.statusbar.notification.collection.NotificationEntryBuilder
+import com.android.systemui.statusbar.notification.collection.PipelineEntry
import com.android.systemui.statusbar.notification.collection.ShadeListBuilder
import com.android.systemui.statusbar.notification.collection.listbuilder.OnAfterRenderEntryListener
import com.android.systemui.statusbar.notification.collection.listbuilder.OnAfterRenderGroupListener
@@ -40,6 +41,7 @@ import org.mockito.kotlin.spy
import org.mockito.kotlin.times
import org.mockito.kotlin.verify
import org.mockito.kotlin.verifyNoMoreInteractions
+import java.nio.channels.Pipe
@SmallTest
@RunWith(AndroidJUnit4::class)
@@ -198,7 +200,7 @@ class RenderStageManagerTest : SysuiTestCase() {
)
private class FakeNotifViewRenderer : NotifViewRenderer {
- override fun onRenderList(notifList: List<ListEntry>) {}
+ override fun onRenderList(notifList: List<PipelineEntry>) {}
override fun getGroupController(group: GroupEntry): NotifGroupController = mock()
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/row/NotificationGutsManagerTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/row/NotificationGutsManagerTest.kt
index 4c1f4f17e00c..1b8d64d5483c 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/row/NotificationGutsManagerTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/row/NotificationGutsManagerTest.kt
@@ -80,9 +80,6 @@ import com.android.systemui.statusbar.policy.DeviceProvisionedController
import com.android.systemui.testKosmos
import com.android.systemui.util.kotlin.JavaAdapter
import com.android.systemui.wmshell.BubblesManager
-import java.util.Optional
-import kotlin.test.assertNotNull
-import kotlin.test.fail
import kotlinx.coroutines.test.runCurrent
import kotlinx.coroutines.test.runTest
import org.junit.Assert.assertEquals
@@ -110,6 +107,9 @@ import org.mockito.kotlin.verify
import org.mockito.kotlin.whenever
import platform.test.runner.parameterized.ParameterizedAndroidJunit4
import platform.test.runner.parameterized.Parameters
+import java.util.Optional
+import kotlin.test.assertNotNull
+import kotlin.test.fail
/** Tests for [NotificationGutsManager]. */
@SmallTest
@@ -509,7 +509,6 @@ class NotificationGutsManagerTest(flags: FlagsParameterization) : SysuiTestCase(
.setImportance(NotificationManager.IMPORTANCE_HIGH)
.build()
- whenever(row.isNonblockable).thenReturn(false)
whenever(highPriorityProvider.isHighPriority(entry)).thenReturn(true)
val statusBarNotification = entry.sbn
gutsManager.initializeNotificationInfo(row, notificationInfoView)
@@ -546,7 +545,6 @@ class NotificationGutsManagerTest(flags: FlagsParameterization) : SysuiTestCase(
NotificationEntryHelper.modifyRanking(row.entry)
.setUserSentiment(NotificationListenerService.Ranking.USER_SENTIMENT_NEGATIVE)
.build()
- whenever(row.isNonblockable).thenReturn(false)
val statusBarNotification = row.entry.sbn
val entry = row.entry
@@ -586,7 +584,6 @@ class NotificationGutsManagerTest(flags: FlagsParameterization) : SysuiTestCase(
NotificationEntryHelper.modifyRanking(row.entry)
.setUserSentiment(NotificationListenerService.Ranking.USER_SENTIMENT_NEGATIVE)
.build()
- whenever(row.isNonblockable).thenReturn(false)
val statusBarNotification = row.entry.sbn
val entry = row.entry
@@ -641,7 +638,7 @@ class NotificationGutsManagerTest(flags: FlagsParameterization) : SysuiTestCase(
): NotificationMenuRowPlugin.MenuItem {
val menuRow: NotificationMenuRowPlugin =
NotificationMenuRow(mContext, peopleNotificationIdentifier)
- menuRow.createMenu(row, row.entry.sbn)
+ menuRow.createMenu(row)
val menuItem = menuRow.getLongpressMenuItem(mContext)
assertNotNull(menuItem)
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/row/NotificationMenuRowTest.java b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/row/NotificationMenuRowTest.java
index 027e899e20df..9fdfca14a5b2 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/row/NotificationMenuRowTest.java
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/row/NotificationMenuRowTest.java
@@ -72,7 +72,7 @@ public class NotificationMenuRowTest extends LeakCheckedTest {
public void testAttachDetach() {
NotificationMenuRowPlugin row =
new NotificationMenuRow(mContext, mPeopleNotificationIdentifier);
- row.createMenu(mRow, null);
+ row.createMenu(mRow);
ViewUtils.attachView(row.getMenuView());
TestableLooper.get(this).processAllMessages();
ViewUtils.detachView(row.getMenuView());
@@ -83,9 +83,9 @@ public class NotificationMenuRowTest extends LeakCheckedTest {
public void testRecreateMenu() {
NotificationMenuRowPlugin row =
new NotificationMenuRow(mContext, mPeopleNotificationIdentifier);
- row.createMenu(mRow, null);
+ row.createMenu(mRow);
assertTrue(row.getMenuView() != null);
- row.createMenu(mRow, null);
+ row.createMenu(mRow);
assertTrue(row.getMenuView() != null);
}
@@ -103,7 +103,7 @@ public class NotificationMenuRowTest extends LeakCheckedTest {
Settings.Global.putInt(mContext.getContentResolver(), SHOW_NEW_NOTIF_DISMISS, 0);
NotificationMenuRow row = new NotificationMenuRow(mContext, mPeopleNotificationIdentifier);
- row.createMenu(mRow, null);
+ row.createMenu(mRow);
ViewGroup container = (ViewGroup) row.getMenuView();
// noti blocking
@@ -116,7 +116,7 @@ public class NotificationMenuRowTest extends LeakCheckedTest {
Settings.Global.putInt(mContext.getContentResolver(), SHOW_NEW_NOTIF_DISMISS, 0);
NotificationMenuRow row = new NotificationMenuRow(mContext, mPeopleNotificationIdentifier);
- row.createMenu(mRow, null);
+ row.createMenu(mRow);
ViewGroup container = (ViewGroup) row.getMenuView();
// just for noti blocking
@@ -129,7 +129,7 @@ public class NotificationMenuRowTest extends LeakCheckedTest {
Settings.Global.putInt(mContext.getContentResolver(), SHOW_NEW_NOTIF_DISMISS, 0);
NotificationMenuRow row = new NotificationMenuRow(mContext, mPeopleNotificationIdentifier);
- row.createMenu(mRow, null);
+ row.createMenu(mRow);
ViewGroup container = (ViewGroup) row.getMenuView();
// one for snooze and one for noti blocking
@@ -142,7 +142,7 @@ public class NotificationMenuRowTest extends LeakCheckedTest {
Settings.Global.putInt(mContext.getContentResolver(), SHOW_NEW_NOTIF_DISMISS, 1);
NotificationMenuRow row = new NotificationMenuRow(mContext, mPeopleNotificationIdentifier);
- row.createMenu(mRow, null);
+ row.createMenu(mRow);
ViewGroup container = (ViewGroup) row.getMenuView();
// Clear menu
@@ -417,7 +417,7 @@ public class NotificationMenuRowTest extends LeakCheckedTest {
public void testOnTouchMove() {
NotificationMenuRow row = Mockito.spy(
new NotificationMenuRow(mContext, mPeopleNotificationIdentifier));
- row.createMenu(mRow, null);
+ row.createMenu(mRow);
doReturn(50f).when(row).getDismissThreshold();
doReturn(true).when(row).canBeDismissed();
doReturn(mView).when(row).getMenuView();
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/row/wrapper/NotificationConversationTemplateViewWrapperTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/row/wrapper/NotificationConversationTemplateViewWrapperTest.kt
index 9a6a6997b96f..081f52c4ff3b 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/row/wrapper/NotificationConversationTemplateViewWrapperTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/row/wrapper/NotificationConversationTemplateViewWrapperTest.kt
@@ -135,6 +135,7 @@ class NotificationConversationTemplateViewWrapperTest : SysuiTestCase() {
.thenReturn(mock())
whenever(requireViewById<View>(R.id.app_name_text)).thenReturn(mock())
whenever(requireViewById<View>(R.id.conversation_text)).thenReturn(mock())
+ whenever(requireViewById<View>(R.id.title)).thenReturn(mock())
}
return mockView
}
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/stack/MagneticNotificationRowManagerImplTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/stack/MagneticNotificationRowManagerImplTest.kt
index e5cb0fbc9e4b..885e71e7a7fe 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/stack/MagneticNotificationRowManagerImplTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/stack/MagneticNotificationRowManagerImplTest.kt
@@ -32,6 +32,7 @@ import com.android.systemui.testKosmos
import com.google.android.msdl.data.model.MSDLToken
import com.google.common.truth.Truth.assertThat
import kotlinx.coroutines.test.runTest
+import org.junit.After
import org.junit.Before
import org.junit.Test
import org.junit.runner.RunWith
@@ -49,7 +50,7 @@ class MagneticNotificationRowManagerImplTest : SysuiTestCase() {
private val sectionsManager = mock<NotificationSectionsManager>()
private val msdlPlayer = kosmos.fakeMSDLPlayer
private var canRowBeDismissed = true
- private var magneticAnimationsCancelled = false
+ private var magneticAnimationsCancelled = MutableList(childrenNumber) { false }
private val underTest = kosmos.magneticNotificationRowManagerImpl
@@ -64,8 +65,10 @@ class MagneticNotificationRowManagerImplTest : SysuiTestCase() {
NotificationTestHelper(mContext, mDependency, kosmos.testableLooper, featureFlags)
children = notificationTestHelper.createGroup(childrenNumber).childrenContainer
swipedRow = children.attachedChildren[childrenNumber / 2]
- configureMagneticRowListener(swipedRow)
- magneticAnimationsCancelled = false
+ children.attachedChildren.forEachIndexed { index, row ->
+ row.magneticRowListener = row.magneticRowListener.asTestableListener(index)
+ }
+ magneticAnimationsCancelled.replaceAll { false }
}
@Test
@@ -259,14 +262,14 @@ class MagneticNotificationRowManagerImplTest : SysuiTestCase() {
underTest.onMagneticInteractionEnd(swipedRow, velocity = null)
// THEN magnetic animations are cancelled
- assertThat(magneticAnimationsCancelled).isTrue()
+ assertThat(magneticAnimationsCancelled[childrenNumber / 2]).isTrue()
}
@Test
fun onMagneticInteractionEnd_forMagneticNeighbor_cancelsMagneticAnimations() =
kosmos.testScope.runTest {
- val neighborRow = children.attachedChildren[childrenNumber / 2 - 1]
- configureMagneticRowListener(neighborRow)
+ val neighborIndex = childrenNumber / 2 - 1
+ val neighborRow = children.attachedChildren[neighborIndex]
// GIVEN that targets are set
setTargets()
@@ -275,9 +278,15 @@ class MagneticNotificationRowManagerImplTest : SysuiTestCase() {
underTest.onMagneticInteractionEnd(neighborRow, null)
// THEN magnetic animations are cancelled
- assertThat(magneticAnimationsCancelled).isTrue()
+ assertThat(magneticAnimationsCancelled[neighborIndex]).isTrue()
}
+ @After
+ fun tearDown() {
+ // We reset the manager so that all MagneticRowListener can cancel all animations
+ underTest.reset()
+ }
+
private fun setDetachedState() {
val threshold = 100f
underTest.setSwipeThresholdPx(threshold)
@@ -302,27 +311,33 @@ class MagneticNotificationRowManagerImplTest : SysuiTestCase() {
originalTranslation *
MagneticNotificationRowManagerImpl.MAGNETIC_REDUCTION
- private fun configureMagneticRowListener(row: ExpandableNotificationRow) {
- val listener =
- object : MagneticRowListener {
- override fun setMagneticTranslation(translation: Float) {
- row.translation = translation
- }
+ private fun MagneticRowListener.asTestableListener(rowIndex: Int): MagneticRowListener {
+ val delegate = this
+ return object : MagneticRowListener {
+ override fun setMagneticTranslation(translation: Float) {
+ delegate.setMagneticTranslation(translation)
+ }
- override fun triggerMagneticForce(
- endTranslation: Float,
- springForce: SpringForce,
- startVelocity: Float,
- ) {}
+ override fun triggerMagneticForce(
+ endTranslation: Float,
+ springForce: SpringForce,
+ startVelocity: Float,
+ ) {
+ delegate.triggerMagneticForce(endTranslation, springForce, startVelocity)
+ }
- override fun cancelMagneticAnimations() {
- magneticAnimationsCancelled = true
- }
+ override fun cancelMagneticAnimations() {
+ magneticAnimationsCancelled[rowIndex] = true
+ delegate.cancelMagneticAnimations()
+ }
- override fun cancelTranslationAnimations() {}
+ override fun cancelTranslationAnimations() {
+ delegate.cancelTranslationAnimations()
+ }
- override fun canRowBeDismissed(): Boolean = canRowBeDismissed
+ override fun canRowBeDismissed(): Boolean {
+ return canRowBeDismissed
}
- row.magneticRowListener = listener
+ }
}
}
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/stack/StackScrollAlgorithmTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/stack/StackScrollAlgorithmTest.kt
index 01ba4df3a314..7603eecd27ff 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/stack/StackScrollAlgorithmTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/stack/StackScrollAlgorithmTest.kt
@@ -146,6 +146,20 @@ class StackScrollAlgorithmTest(flags: FlagsParameterization) : SysuiTestCase() {
}
@Test
+ @EnableSceneContainer
+ fun resetViewStates_defaultHun_hasShadow() {
+ val headsUpTop = 200f
+ ambientState.headsUpTop = headsUpTop
+
+ whenever(notificationRow.isPinned).thenReturn(true)
+ whenever(notificationRow.isHeadsUp).thenReturn(true)
+
+ stackScrollAlgorithm.resetViewStates(ambientState, 0)
+
+ assertThat(notificationRow.viewState.zTranslation).isGreaterThan(baseZ)
+ }
+
+ @Test
@DisableSceneContainer
fun resetViewStates_defaultHunWhenShadeIsOpening_yTranslationIsInset() {
whenever(notificationRow.isPinned).thenReturn(true)
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/stack/ui/viewmodel/SharedNotificationContainerViewModelTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/stack/ui/viewmodel/SharedNotificationContainerViewModelTest.kt
index 21297e3e64b7..c630a1c1e006 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/stack/ui/viewmodel/SharedNotificationContainerViewModelTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/stack/ui/viewmodel/SharedNotificationContainerViewModelTest.kt
@@ -17,14 +17,15 @@
package com.android.systemui.statusbar.notification.stack.ui.viewmodel
+import android.platform.test.annotations.EnableFlags
import android.platform.test.flag.junit.FlagsParameterization
import androidx.test.filters.SmallTest
-import com.android.compose.animation.scene.ObservableTransitionState
+import com.android.systemui.Flags.FLAG_GLANCEABLE_HUB_V2
import com.android.systemui.SysuiTestCase
import com.android.systemui.bouncer.data.repository.keyguardBouncerRepository
import com.android.systemui.common.shared.model.NotificationContainerBounds
import com.android.systemui.common.ui.data.repository.fakeConfigurationRepository
-import com.android.systemui.communal.data.repository.communalSceneRepository
+import com.android.systemui.communal.domain.interactor.communalSceneInteractor
import com.android.systemui.communal.shared.model.CommunalScenes
import com.android.systemui.coroutines.collectLastValue
import com.android.systemui.coroutines.collectValues
@@ -54,11 +55,14 @@ import com.android.systemui.keyguard.ui.viewmodel.AodBurnInViewModel
import com.android.systemui.keyguard.ui.viewmodel.ViewStateAccessor
import com.android.systemui.keyguard.ui.viewmodel.aodBurnInViewModel
import com.android.systemui.keyguard.ui.viewmodel.keyguardRootViewModel
+import com.android.systemui.kosmos.collectLastValue
+import com.android.systemui.kosmos.runTest
import com.android.systemui.kosmos.testScope
import com.android.systemui.res.R
import com.android.systemui.scene.data.repository.Idle
import com.android.systemui.scene.data.repository.Transition
import com.android.systemui.scene.data.repository.setTransition
+import com.android.systemui.scene.shared.flag.SceneContainerFlag
import com.android.systemui.scene.shared.model.Scenes
import com.android.systemui.shade.domain.interactor.enableDualShade
import com.android.systemui.shade.domain.interactor.enableSingleShade
@@ -125,7 +129,6 @@ class SharedNotificationContainerViewModelTest(flags: FlagsParameterization) : S
kosmos.sharedNotificationContainerInteractor
}
private val largeScreenHeaderHelper by lazy { kosmos.mockLargeScreenHeaderHelper }
- private val communalSceneRepository by lazy { kosmos.communalSceneRepository }
lateinit var underTest: SharedNotificationContainerViewModel
@@ -591,6 +594,25 @@ class SharedNotificationContainerViewModelTest(flags: FlagsParameterization) : S
}
@Test
+ @DisableSceneContainer
+ @EnableFlags(FLAG_GLANCEABLE_HUB_V2)
+ fun isOnLockscreenFalseWhenCommunalShowing() =
+ kosmos.runTest {
+ val isOnLockscreen by collectLastValue(underTest.isOnLockscreen)
+
+ setTransition(
+ sceneTransition = Idle(Scenes.Bouncer),
+ stateTransition = TransitionStep(from = LOCKSCREEN, to = PRIMARY_BOUNCER),
+ )
+ assertThat(isOnLockscreen).isTrue()
+
+ testScope.showCommunalScene()
+
+ // If bouncer is showing over the hub, it should not be considered on lockscreen
+ assertThat(isOnLockscreen).isFalse()
+ }
+
+ @Test
fun isOnLockscreenWithoutShade() =
testScope.runTest {
val isOnLockscreenWithoutShade by collectLastValue(underTest.isOnLockscreenWithoutShade)
@@ -1472,20 +1494,24 @@ class SharedNotificationContainerViewModelTest(flags: FlagsParameterization) : S
}
private fun TestScope.showCommunalScene() {
- val transitionState =
- MutableStateFlow<ObservableTransitionState>(
- ObservableTransitionState.Idle(CommunalScenes.Communal)
- )
- communalSceneRepository.setTransitionState(transitionState)
+ val targetScene =
+ if (SceneContainerFlag.isEnabled) {
+ Scenes.Communal
+ } else {
+ CommunalScenes.Communal
+ }
+ kosmos.communalSceneInteractor.changeScene(targetScene, "test")
runCurrent()
}
private fun TestScope.hideCommunalScene() {
- val transitionState =
- MutableStateFlow<ObservableTransitionState>(
- ObservableTransitionState.Idle(CommunalScenes.Blank)
- )
- communalSceneRepository.setTransitionState(transitionState)
+ val targetScene =
+ if (SceneContainerFlag.isEnabled) {
+ Scenes.Lockscreen
+ } else {
+ CommunalScenes.Blank
+ }
+ kosmos.communalSceneInteractor.changeScene(targetScene, "test")
runCurrent()
}
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/pipeline/mobile/domain/interactor/MobileIconsInteractorTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/pipeline/mobile/domain/interactor/MobileIconsInteractorTest.kt
index d17abd7da05c..6066a3870dfe 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/pipeline/mobile/domain/interactor/MobileIconsInteractorTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/pipeline/mobile/domain/interactor/MobileIconsInteractorTest.kt
@@ -17,6 +17,7 @@
package com.android.systemui.statusbar.pipeline.mobile.domain.interactor
import android.os.ParcelUuid
+import android.platform.test.annotations.EnableFlags
import android.telephony.SubscriptionManager.INVALID_SUBSCRIPTION_ID
import android.telephony.SubscriptionManager.PROFILE_CLASS_PROVISIONING
import android.telephony.SubscriptionManager.PROFILE_CLASS_UNSET
@@ -33,9 +34,12 @@ import com.android.systemui.kosmos.collectLastValue
import com.android.systemui.kosmos.runCurrent
import com.android.systemui.kosmos.runTest
import com.android.systemui.kosmos.testScope
+import com.android.systemui.statusbar.core.NewStatusBarIcons
+import com.android.systemui.statusbar.core.StatusBarRootModernization
import com.android.systemui.statusbar.pipeline.mobile.data.model.SubscriptionModel
import com.android.systemui.statusbar.pipeline.mobile.data.repository.FakeMobileConnectionRepository
import com.android.systemui.statusbar.pipeline.mobile.data.repository.fake
+import com.android.systemui.statusbar.pipeline.mobile.data.repository.fakeMobileConnectionsRepository
import com.android.systemui.statusbar.pipeline.mobile.data.repository.mobileConnectionsRepository
import com.android.systemui.statusbar.pipeline.mobile.data.repository.mobileConnectionsRepositoryLogbufferName
import com.android.systemui.statusbar.pipeline.shared.data.model.ConnectivitySlot
@@ -897,6 +901,71 @@ class MobileIconsInteractorTest : SysuiTestCase() {
assertThat(latest).isEqualTo(2)
}
+ @Test
+ @EnableFlags(NewStatusBarIcons.FLAG_NAME, StatusBarRootModernization.FLAG_NAME)
+ fun isStackable_tracksNumberOfSubscriptions() =
+ kosmos.runTest {
+ val latest by collectLastValue(underTest.isStackable)
+
+ connectionsRepository.setSubscriptions(listOf(SUB_1))
+ assertThat(latest).isFalse()
+
+ connectionsRepository.setSubscriptions(listOf(SUB_1, SUB_2))
+ assertThat(latest).isTrue()
+
+ connectionsRepository.setSubscriptions(listOf(SUB_1, SUB_2, SUB_3_OPP))
+ assertThat(latest).isFalse()
+ }
+
+ @Test
+ @EnableFlags(NewStatusBarIcons.FLAG_NAME, StatusBarRootModernization.FLAG_NAME)
+ fun isStackable_checksForTerrestrialConnections() =
+ kosmos.runTest {
+ val exclusivelyNonTerrestrialSub =
+ SubscriptionModel(
+ isExclusivelyNonTerrestrial = true,
+ subscriptionId = 5,
+ carrierName = "Carrier 5",
+ profileClass = PROFILE_CLASS_UNSET,
+ )
+
+ val latest by collectLastValue(underTest.isStackable)
+
+ connectionsRepository.setSubscriptions(listOf(SUB_1, SUB_2))
+ assertThat(latest).isTrue()
+
+ connectionsRepository.setSubscriptions(listOf(SUB_1, exclusivelyNonTerrestrialSub))
+ assertThat(latest).isFalse()
+ }
+
+ @Test
+ @EnableFlags(NewStatusBarIcons.FLAG_NAME, StatusBarRootModernization.FLAG_NAME)
+ fun isStackable_checksForNumberOfBars() =
+ kosmos.runTest {
+ val latest by collectLastValue(underTest.isStackable)
+
+ // Number of levels is the same for both
+ connectionsRepository.setSubscriptions(listOf(SUB_1, SUB_2))
+ setNumberOfLevelsForSubId(SUB_1_ID, 5)
+ setNumberOfLevelsForSubId(SUB_2_ID, 5)
+
+ assertThat(latest).isTrue()
+
+ // Change the number of levels to be different than SUB_2
+ setNumberOfLevelsForSubId(SUB_1_ID, 6)
+
+ assertThat(latest).isFalse()
+ }
+
+ private fun setNumberOfLevelsForSubId(subId: Int, numberOfLevels: Int) {
+ with(kosmos) {
+ (fakeMobileConnectionsRepository.getRepoForSubId(subId)
+ as FakeMobileConnectionRepository)
+ .numberOfLevels
+ .value = numberOfLevels
+ }
+ }
+
/**
* Convenience method for creating a pair of subscriptions to test the filteredSubscriptions
* flow.
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/pipeline/mobile/ui/viewmodel/StackedMobileIconViewModelTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/pipeline/mobile/ui/viewmodel/StackedMobileIconViewModelTest.kt
new file mode 100644
index 000000000000..20bdebd069f7
--- /dev/null
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/pipeline/mobile/ui/viewmodel/StackedMobileIconViewModelTest.kt
@@ -0,0 +1,145 @@
+/*
+ * Copyright (C) 2025 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.statusbar.pipeline.mobile.ui.viewmodel
+
+import android.platform.test.annotations.EnableFlags
+import android.telephony.SubscriptionManager.PROFILE_CLASS_UNSET
+import androidx.test.ext.junit.runners.AndroidJUnit4
+import androidx.test.filters.SmallTest
+import com.android.systemui.SysuiTestCase
+import com.android.systemui.common.shared.model.Icon
+import com.android.systemui.flags.Flags
+import com.android.systemui.flags.fakeFeatureFlagsClassic
+import com.android.systemui.kosmos.Kosmos
+import com.android.systemui.kosmos.Kosmos.Fixture
+import com.android.systemui.kosmos.runTest
+import com.android.systemui.kosmos.testScope
+import com.android.systemui.kosmos.useUnconfinedTestDispatcher
+import com.android.systemui.lifecycle.activateIn
+import com.android.systemui.statusbar.core.NewStatusBarIcons
+import com.android.systemui.statusbar.core.StatusBarRootModernization
+import com.android.systemui.statusbar.pipeline.mobile.data.model.SubscriptionModel
+import com.android.systemui.statusbar.pipeline.mobile.domain.interactor.fakeMobileIconsInteractor
+import com.android.systemui.statusbar.pipeline.mobile.domain.model.SignalIconModel
+import com.android.systemui.testKosmos
+import com.google.common.truth.Truth.assertThat
+import org.junit.Before
+import org.junit.Test
+import org.junit.runner.RunWith
+
+@SmallTest
+@RunWith(AndroidJUnit4::class)
+class StackedMobileIconViewModelTest : SysuiTestCase() {
+ private val kosmos = testKosmos().useUnconfinedTestDispatcher()
+ private val testScope = kosmos.testScope
+
+ private val Kosmos.underTest: StackedMobileIconViewModel by Fixture {
+ stackedMobileIconViewModel
+ }
+
+ @Before
+ fun setUp() {
+ kosmos.fakeFeatureFlagsClassic.set(Flags.NEW_NETWORK_SLICE_UI, false)
+ kosmos.underTest.activateIn(testScope)
+ }
+
+ @Test
+ @EnableFlags(NewStatusBarIcons.FLAG_NAME, StatusBarRootModernization.FLAG_NAME)
+ fun dualSim_filtersOutNonDualConnections() =
+ kosmos.runTest {
+ fakeMobileIconsInteractor.filteredSubscriptions.value = listOf()
+ assertThat(underTest.dualSim).isNull()
+
+ fakeMobileIconsInteractor.filteredSubscriptions.value = listOf(SUB_1)
+ assertThat(underTest.dualSim).isNull()
+
+ fakeMobileIconsInteractor.filteredSubscriptions.value = listOf(SUB_1, SUB_2, SUB_3)
+ assertThat(underTest.dualSim).isNull()
+
+ fakeMobileIconsInteractor.filteredSubscriptions.value = listOf(SUB_1, SUB_2)
+ assertThat(underTest.dualSim).isNotNull()
+ }
+
+ @Test
+ @EnableFlags(NewStatusBarIcons.FLAG_NAME, StatusBarRootModernization.FLAG_NAME)
+ fun dualSim_filtersOutNonCellularIcons() =
+ kosmos.runTest {
+ fakeMobileIconsInteractor.filteredSubscriptions.value = listOf(SUB_1)
+ assertThat(underTest.dualSim).isNull()
+
+ fakeMobileIconsInteractor
+ .getInteractorForSubId(SUB_1.subscriptionId)!!
+ .signalLevelIcon
+ .value =
+ SignalIconModel.Satellite(
+ level = 0,
+ icon = Icon.Resource(res = 0, contentDescription = null),
+ )
+ fakeMobileIconsInteractor.filteredSubscriptions.value = listOf(SUB_1, SUB_2)
+ assertThat(underTest.dualSim).isNull()
+ }
+
+ @Test
+ @EnableFlags(NewStatusBarIcons.FLAG_NAME, StatusBarRootModernization.FLAG_NAME)
+ fun dualSim_tracksActiveSubId() =
+ kosmos.runTest {
+ // Active sub id is null, order is unchanged
+ fakeMobileIconsInteractor.filteredSubscriptions.value = listOf(SUB_1, SUB_2)
+ setIconLevel(SUB_1.subscriptionId, 1)
+ setIconLevel(SUB_2.subscriptionId, 2)
+
+ assertThat(underTest.dualSim!!.primary.level).isEqualTo(1)
+ assertThat(underTest.dualSim!!.secondary.level).isEqualTo(2)
+
+ // Active sub is 2, order is swapped
+ fakeMobileIconsInteractor.activeMobileDataSubscriptionId.value = SUB_2.subscriptionId
+
+ assertThat(underTest.dualSim!!.primary.level).isEqualTo(2)
+ assertThat(underTest.dualSim!!.secondary.level).isEqualTo(1)
+ }
+
+ private fun setIconLevel(subId: Int, level: Int) {
+ with(kosmos.fakeMobileIconsInteractor.getInteractorForSubId(subId)!!) {
+ signalLevelIcon.value =
+ (signalLevelIcon.value as SignalIconModel.Cellular).copy(level = level)
+ }
+ }
+
+ companion object {
+ private val SUB_1 =
+ SubscriptionModel(
+ subscriptionId = 1,
+ isOpportunistic = false,
+ carrierName = "Carrier 1",
+ profileClass = PROFILE_CLASS_UNSET,
+ )
+ private val SUB_2 =
+ SubscriptionModel(
+ subscriptionId = 2,
+ isOpportunistic = false,
+ carrierName = "Carrier 2",
+ profileClass = PROFILE_CLASS_UNSET,
+ )
+ private val SUB_3 =
+ SubscriptionModel(
+ subscriptionId = 3,
+ isOpportunistic = false,
+ carrierName = "Carrier 3",
+ profileClass = PROFILE_CLASS_UNSET,
+ )
+ }
+}
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/pipeline/shared/ui/viewmodel/FakeHomeStatusBarViewModel.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/pipeline/shared/ui/viewmodel/FakeHomeStatusBarViewModel.kt
index eb961bd5f4ae..f91e3a612862 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/pipeline/shared/ui/viewmodel/FakeHomeStatusBarViewModel.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/pipeline/shared/ui/viewmodel/FakeHomeStatusBarViewModel.kt
@@ -64,6 +64,8 @@ class FakeHomeStatusBarViewModel(
override val isHomeStatusBarAllowedByScene = MutableStateFlow(false)
+ override val canShowOngoingActivityChips: Flow<Boolean> = MutableStateFlow(false)
+
override val batteryViewModelFactory: BatteryViewModel.Factory =
object : BatteryViewModel.Factory {
override fun create(): BatteryViewModel = mock(BatteryViewModel::class.java)
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/pipeline/shared/ui/viewmodel/HomeStatusBarViewModelImplTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/pipeline/shared/ui/viewmodel/HomeStatusBarViewModelImplTest.kt
index 51484baf1b7b..8a796fc33608 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/pipeline/shared/ui/viewmodel/HomeStatusBarViewModelImplTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/pipeline/shared/ui/viewmodel/HomeStatusBarViewModelImplTest.kt
@@ -678,6 +678,60 @@ class HomeStatusBarViewModelImplTest : SysuiTestCase() {
}
@Test
+ fun canShowOngoingActivityChips_statusBarHidden_noSecureCamera_noHun_false() =
+ kosmos.runTest {
+ val latest by collectLastValue(underTest.canShowOngoingActivityChips)
+
+ // home status bar not allowed
+ kosmos.sceneContainerRepository.snapToScene(Scenes.Lockscreen)
+ kosmos.keyguardOcclusionRepository.setShowWhenLockedActivityInfo(false, taskInfo = null)
+
+ assertThat(latest).isFalse()
+ }
+
+ @Test
+ fun canShowOngoingActivityChips_statusBarNotHidden_noSecureCamera_noHun_true() =
+ kosmos.runTest {
+ val latest by collectLastValue(underTest.canShowOngoingActivityChips)
+
+ transitionKeyguardToGone()
+
+ assertThat(latest).isTrue()
+ }
+
+ @Test
+ fun canShowOngoingActivityChips_statusBarNotHidden_secureCamera_noHun_false() =
+ kosmos.runTest {
+ val latest by collectLastValue(underTest.canShowOngoingActivityChips)
+
+ fakeKeyguardTransitionRepository.sendTransitionSteps(
+ from = KeyguardState.LOCKSCREEN,
+ to = KeyguardState.OCCLUDED,
+ testScope = testScope,
+ )
+ kosmos.keyguardInteractor.onCameraLaunchDetected(CAMERA_LAUNCH_SOURCE_POWER_DOUBLE_TAP)
+
+ assertThat(latest).isFalse()
+ }
+
+ @Test
+ fun canShowOngoingActivityChips_statusBarNotHidden_noSecureCamera_hun_false() =
+ kosmos.runTest {
+ val latest by collectLastValue(underTest.canShowOngoingActivityChips)
+
+ transitionKeyguardToGone()
+
+ headsUpNotificationRepository.setNotifications(
+ UnconfinedFakeHeadsUpRowRepository(
+ key = "key",
+ pinnedStatus = MutableStateFlow(PinnedStatus.PinnedByUser),
+ )
+ )
+
+ assertThat(latest).isFalse()
+ }
+
+ @Test
fun isClockVisible_allowedByDisableFlags_visible() =
kosmos.runTest {
val latest by collectLastValue(underTest.isClockVisible)
diff --git a/packages/SystemUI/plugin/src/com/android/systemui/plugins/qs/QS.java b/packages/SystemUI/plugin/src/com/android/systemui/plugins/qs/QS.java
index d3218ad8c9fb..a0bf7f00b11c 100644
--- a/packages/SystemUI/plugin/src/com/android/systemui/plugins/qs/QS.java
+++ b/packages/SystemUI/plugin/src/com/android/systemui/plugins/qs/QS.java
@@ -203,6 +203,8 @@ public interface QS extends FragmentBase {
*/
void setIsNotificationPanelFullWidth(boolean isFullWidth);
+ default void setQSExpandingOrCollapsing(boolean isQSExpandingOrCollapsing) {}
+
/**
* Callback for when QSPanel container is scrolled
*/
diff --git a/packages/SystemUI/plugin/src/com/android/systemui/plugins/statusbar/NotificationMenuRowPlugin.java b/packages/SystemUI/plugin/src/com/android/systemui/plugins/statusbar/NotificationMenuRowPlugin.java
index 94fdbae83253..9b961d2535ae 100644
--- a/packages/SystemUI/plugin/src/com/android/systemui/plugins/statusbar/NotificationMenuRowPlugin.java
+++ b/packages/SystemUI/plugin/src/com/android/systemui/plugins/statusbar/NotificationMenuRowPlugin.java
@@ -122,7 +122,7 @@ public interface NotificationMenuRowPlugin extends Plugin {
public void setAppName(String appName);
- public void createMenu(ViewGroup parent, StatusBarNotification sbn);
+ public void createMenu(ViewGroup parent);
public void resetMenu();
@@ -215,9 +215,8 @@ public interface NotificationMenuRowPlugin extends Plugin {
/**
* Callback used to signal the menu that its parent notification has been updated.
- * @param sbn
*/
- public void onNotificationUpdated(StatusBarNotification sbn);
+ public void onNotificationUpdated();
/**
* Callback used to signal the menu that a user is moving the parent notification.
diff --git a/packages/SystemUI/res/layout/bindable_status_bar_compose_icon.xml b/packages/SystemUI/res/layout/bindable_status_bar_compose_icon.xml
index fa9318bc151c..6b55ac2e0398 100644
--- a/packages/SystemUI/res/layout/bindable_status_bar_compose_icon.xml
+++ b/packages/SystemUI/res/layout/bindable_status_bar_compose_icon.xml
@@ -27,7 +27,6 @@
android:layout_height="@dimen/status_bar_bindable_icon_size"
android:layout_width="wrap_content"
android:layout_gravity="center_vertical"
- android:padding="4sp"
/>
</com.android.systemui.statusbar.pipeline.shared.ui.view.SingleBindableStatusBarComposeIconView>
diff --git a/packages/SystemUI/res/values/dimens.xml b/packages/SystemUI/res/values/dimens.xml
index 17a89b3a0394..640e1fa79530 100644
--- a/packages/SystemUI/res/values/dimens.xml
+++ b/packages/SystemUI/res/values/dimens.xml
@@ -491,6 +491,9 @@
<!-- some constraints use a negative margin. must be aligned with overlay_border_width, above;
overlay_border_width_neg = overlay_border_width * -1 -->
<dimen name="overlay_border_width_neg">-4dp</dimen>
+ <dimen name="overlay_shade_panel_shape_radius">
+ @dimen/aux_spacing_overlay_panel_shape_radius
+ </dimen>
<dimen name="clipboard_preview_size">@dimen/overlay_x_scale</dimen>
<dimen name="clipboard_overlay_min_font">10sp</dimen>
@@ -2215,4 +2218,8 @@
<dimen name="rear_display_progress_width">231dp</dimen>
<!-- Rear display mode end -->
+ <!-- Spacing attributes to overwrite -->
+ <dimen name="aux_spacing_overlay_panel_shape_radius">46dp</dimen>
+ <!-- Spacing attributes to overwrite end -->
+
</resources>
diff --git a/packages/SystemUI/res/values/strings.xml b/packages/SystemUI/res/values/strings.xml
index 3fc46ed6c9d1..359bd2bcb37c 100644
--- a/packages/SystemUI/res/values/strings.xml
+++ b/packages/SystemUI/res/values/strings.xml
@@ -3170,8 +3170,8 @@
<string name="controls_media_settings_button">Settings</string>
<!-- Description for media control's playing media item, including information for the media's title, the artist, and source app [CHAR LIMIT=NONE]-->
<string name="controls_media_playing_item_description"><xliff:g id="song_name" example="Daily mix">%1$s</xliff:g> by <xliff:g id="artist_name" example="Various artists">%2$s</xliff:g> is playing from <xliff:g id="app_label" example="Spotify">%3$s</xliff:g></string>
- <!-- Content description for media cotnrols progress bar [CHAR_LIMIT=NONE] -->
- <string name="controls_media_seekbar_description"><xliff:g id="elapsed_time" example="1:30">%1$s</xliff:g> of <xliff:g id="total_time" example="3:00">%2$s</xliff:g></string>
+ <!-- Content description for media controls progress bar [CHAR_LIMIT=NONE] -->
+ <string name="controls_media_seekbar_description"><xliff:g id="elapsed_time" example="1 hour 2 minutes 30 seconds">%1$s</xliff:g> of <xliff:g id="total_time" example="4 hours 5 seconds">%2$s</xliff:g></string>
<!-- Placeholder title to inform user that an app has posted media controls [CHAR_LIMIT=NONE] -->
<string name="controls_media_empty_title"><xliff:g id="app_name" example="Foo Music App">%1$s</xliff:g> is running</string>
diff --git a/packages/SystemUI/shared/Android.bp b/packages/SystemUI/shared/Android.bp
index 0f1da509468a..ae3a76e2d2ca 100644
--- a/packages/SystemUI/shared/Android.bp
+++ b/packages/SystemUI/shared/Android.bp
@@ -71,6 +71,7 @@ android_library {
"//frameworks/libs/systemui:com_android_systemui_shared_flags_lib",
"//frameworks/libs/systemui:msdl",
"//frameworks/libs/systemui:view_capture",
+ "am_flags_lib",
],
resource_dirs: [
"res",
diff --git a/packages/SystemUI/shared/res/values/bools.xml b/packages/SystemUI/shared/res/values/bools.xml
index f22dac4b9fb4..98e5cea0e78f 100644
--- a/packages/SystemUI/shared/res/values/bools.xml
+++ b/packages/SystemUI/shared/res/values/bools.xml
@@ -22,4 +22,7 @@
<resources>
<!-- Whether to add padding at the bottom of the complication clock -->
<bool name="dream_overlay_complication_clock_bottom_padding">false</bool>
-</resources> \ No newline at end of file
+
+ <!-- Whether to mark tasks that are present in the UI as perceptible tasks. -->
+ <bool name="config_usePerceptibleTasks">false</bool>
+</resources>
diff --git a/packages/SystemUI/shared/src/com/android/systemui/shared/recents/utilities/Utilities.java b/packages/SystemUI/shared/src/com/android/systemui/shared/recents/utilities/Utilities.java
index 9ebb15f43307..c82243934b8b 100644
--- a/packages/SystemUI/shared/src/com/android/systemui/shared/recents/utilities/Utilities.java
+++ b/packages/SystemUI/shared/src/com/android/systemui/shared/recents/utilities/Utilities.java
@@ -19,6 +19,7 @@ package com.android.systemui.shared.recents.utilities;
import static android.app.StatusBarManager.NAVBAR_BACK_DISMISS_IME;
import static android.app.StatusBarManager.NAVBAR_IME_SWITCHER_BUTTON_VISIBLE;
import static android.app.StatusBarManager.NAVBAR_IME_VISIBLE;
+import static android.app.WindowConfiguration.WINDOWING_MODE_FREEFORM;
import android.annotation.TargetApi;
import android.app.StatusBarManager.NavbarFlags;
@@ -35,6 +36,8 @@ import android.util.DisplayMetrics;
import android.view.Surface;
import android.view.WindowManager;
+import com.android.systemui.shared.recents.model.Task;
+
/* Common code */
public class Utilities {
@@ -165,4 +168,10 @@ public class Utilities {
float densityRatio = (float) densityDpi / DisplayMetrics.DENSITY_DEFAULT;
return (size / densityRatio);
}
+
+ /** Whether a task is in freeform mode. */
+ public static boolean isFreeformTask(Task task) {
+ return task != null && task.getKey() != null
+ && task.getKey().windowingMode == WINDOWING_MODE_FREEFORM;
+ }
}
diff --git a/packages/SystemUI/shared/src/com/android/systemui/shared/system/ActivityManagerWrapper.java b/packages/SystemUI/shared/src/com/android/systemui/shared/system/ActivityManagerWrapper.java
index ed9ba7aa455b..487d1ce2514e 100644
--- a/packages/SystemUI/shared/src/com/android/systemui/shared/system/ActivityManagerWrapper.java
+++ b/packages/SystemUI/shared/src/com/android/systemui/shared/system/ActivityManagerWrapper.java
@@ -46,6 +46,8 @@ import android.view.Display;
import android.window.TaskSnapshot;
import com.android.internal.app.IVoiceInteractionManagerService;
+import com.android.server.am.Flags;
+import com.android.systemui.shared.R;
import com.android.systemui.shared.recents.model.Task;
import com.android.systemui.shared.recents.model.ThumbnailData;
@@ -227,6 +229,17 @@ public class ActivityManagerWrapper {
}
/**
+ * Sets whether or not the specified task is perceptible.
+ */
+ public boolean setTaskIsPerceptible(int taskId, boolean isPerceptible) {
+ try {
+ return getService().setTaskIsPerceptible(taskId, isPerceptible);
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
+ }
+
+ /**
* Removes a task by id.
*/
public void removeTask(final int taskId) {
@@ -311,10 +324,23 @@ public class ActivityManagerWrapper {
}
/**
+ * Returns true if tasks with a presence in the UI should be marked as perceptible tasks.
+ */
+ public static boolean usePerceptibleTasks(Context context) {
+ return Flags.perceptibleTasks()
+ && context.getResources().getBoolean(R.bool.config_usePerceptibleTasks);
+ }
+
+ /**
* Returns true if the running task represents the home task
*/
public static boolean isHomeTask(RunningTaskInfo info) {
return info.configuration.windowConfiguration.getActivityType()
== WindowConfiguration.ACTIVITY_TYPE_HOME;
}
+
+ public boolean isRunningInTestHarness() {
+ return ActivityManager.isRunningInTestHarness()
+ || ActivityManager.isRunningInUserTestHarness();
+ }
}
diff --git a/packages/SystemUI/src/com/android/keyguard/EmergencyButtonController.java b/packages/SystemUI/src/com/android/keyguard/EmergencyButtonController.java
index 5e36539ecbec..a7bb11ea0442 100644
--- a/packages/SystemUI/src/com/android/keyguard/EmergencyButtonController.java
+++ b/packages/SystemUI/src/com/android/keyguard/EmergencyButtonController.java
@@ -156,7 +156,7 @@ public class EmergencyButtonController extends ViewController<EmergencyButton> {
mMainExecutor.execute(() -> mView.updateEmergencyCallButton(
/* isInCall= */ isInCall,
/* hasTelephonyRadio= */ getContext().getPackageManager()
- .hasSystemFeature(PackageManager.FEATURE_TELEPHONY),
+ .hasSystemFeature(PackageManager.FEATURE_TELEPHONY_CALLING),
/* simLocked= */ mKeyguardUpdateMonitor.isSimPinVoiceSecure(),
/* isSecure= */ isSecure));
});
diff --git a/packages/SystemUI/src/com/android/systemui/accessibility/hearingaid/AmbientVolumeLayout.java b/packages/SystemUI/src/com/android/systemui/accessibility/hearingaid/AmbientVolumeLayout.java
index 7c141c1b561e..5247acc94e03 100644
--- a/packages/SystemUI/src/com/android/systemui/accessibility/hearingaid/AmbientVolumeLayout.java
+++ b/packages/SystemUI/src/com/android/systemui/accessibility/hearingaid/AmbientVolumeLayout.java
@@ -58,11 +58,22 @@ public class AmbientVolumeLayout extends LinearLayout implements AmbientVolumeUi
private final BiMap<Integer, AmbientVolumeSlider> mSideToSliderMap = HashBiMap.create();
private int mVolumeLevel = AMBIENT_VOLUME_LEVEL_DEFAULT;
+ private HearingDevicesUiEventLogger mUiEventLogger;
+ private int mLaunchSourceId;
+
private final AmbientVolumeSlider.OnChangeListener mSliderOnChangeListener =
(slider, value) -> {
- if (mListener != null) {
- final int side = mSideToSliderMap.inverse().get(slider);
- mListener.onSliderValueChange(side, value);
+ final Integer side = mSideToSliderMap.inverse().get(slider);
+ if (side != null) {
+ if (mUiEventLogger != null) {
+ HearingDevicesUiEvent uiEvent = side == SIDE_UNIFIED
+ ? HearingDevicesUiEvent.HEARING_DEVICES_AMBIENT_CHANGE_UNIFIED
+ : HearingDevicesUiEvent.HEARING_DEVICES_AMBIENT_CHANGE_SEPARATED;
+ mUiEventLogger.log(uiEvent, mLaunchSourceId);
+ }
+ if (mListener != null) {
+ mListener.onSliderValueChange(side, value);
+ }
}
};
@@ -94,6 +105,12 @@ public class AmbientVolumeLayout extends LinearLayout implements AmbientVolumeUi
return;
}
setMuted(!mMuted);
+ if (mUiEventLogger != null) {
+ HearingDevicesUiEvent uiEvent = mMuted
+ ? HearingDevicesUiEvent.HEARING_DEVICES_AMBIENT_MUTE
+ : HearingDevicesUiEvent.HEARING_DEVICES_AMBIENT_UNMUTE;
+ mUiEventLogger.log(uiEvent, mLaunchSourceId);
+ }
if (mListener != null) {
mListener.onAmbientVolumeIconClick();
}
@@ -103,6 +120,12 @@ public class AmbientVolumeLayout extends LinearLayout implements AmbientVolumeUi
mExpandIcon = requireViewById(R.id.ambient_expand_icon);
mExpandIcon.setOnClickListener(v -> {
setExpanded(!mExpanded);
+ if (mUiEventLogger != null) {
+ HearingDevicesUiEvent uiEvent = mExpanded
+ ? HearingDevicesUiEvent.HEARING_DEVICES_AMBIENT_EXPAND_CONTROLS
+ : HearingDevicesUiEvent.HEARING_DEVICES_AMBIENT_COLLAPSE_CONTROLS;
+ mUiEventLogger.log(uiEvent, mLaunchSourceId);
+ }
if (mListener != null) {
mListener.onExpandIconClick();
}
@@ -243,6 +266,11 @@ public class AmbientVolumeLayout extends LinearLayout implements AmbientVolumeUi
updateVolumeLevel();
}
+ void setUiEventLogger(HearingDevicesUiEventLogger uiEventLogger, int launchSourceId) {
+ mUiEventLogger = uiEventLogger;
+ mLaunchSourceId = launchSourceId;
+ }
+
private void updateVolumeLevel() {
int leftLevel, rightLevel;
if (mExpanded) {
diff --git a/packages/SystemUI/src/com/android/systemui/accessibility/hearingaid/HearingDevicesDialogDelegate.java b/packages/SystemUI/src/com/android/systemui/accessibility/hearingaid/HearingDevicesDialogDelegate.java
index 22ecb0af8c18..786d27af3994 100644
--- a/packages/SystemUI/src/com/android/systemui/accessibility/hearingaid/HearingDevicesDialogDelegate.java
+++ b/packages/SystemUI/src/com/android/systemui/accessibility/hearingaid/HearingDevicesDialogDelegate.java
@@ -382,6 +382,7 @@ public class HearingDevicesDialogDelegate implements SystemUIDialog.Delegate,
private void setupAmbientControls(CachedBluetoothDevice activeHearingDevice) {
final AmbientVolumeLayout ambientLayout = mDialog.requireViewById(R.id.ambient_layout);
+ ambientLayout.setUiEventLogger(mUiEventLogger, mLaunchSourceId);
mAmbientController = new AmbientVolumeUiController(
mDialog.getContext(), mLocalBluetoothManager, ambientLayout);
mAmbientController.setShowUiWhenLocalDataExist(false);
diff --git a/packages/SystemUI/src/com/android/systemui/accessibility/hearingaid/HearingDevicesUiEvent.kt b/packages/SystemUI/src/com/android/systemui/accessibility/hearingaid/HearingDevicesUiEvent.kt
index 9e77b02be495..fe1d5040c6f5 100644
--- a/packages/SystemUI/src/com/android/systemui/accessibility/hearingaid/HearingDevicesUiEvent.kt
+++ b/packages/SystemUI/src/com/android/systemui/accessibility/hearingaid/HearingDevicesUiEvent.kt
@@ -29,7 +29,17 @@ enum class HearingDevicesUiEvent(private val id: Int) : UiEventLogger.UiEventEnu
@UiEvent(doc = "Click on the device gear to enter device detail page")
HEARING_DEVICES_GEAR_CLICK(1853),
@UiEvent(doc = "Select a preset from preset spinner") HEARING_DEVICES_PRESET_SELECT(1854),
- @UiEvent(doc = "Click on related tool") HEARING_DEVICES_RELATED_TOOL_CLICK(1856);
+ @UiEvent(doc = "Click on related tool") HEARING_DEVICES_RELATED_TOOL_CLICK(1856),
+ @UiEvent(doc = "Change the ambient volume with unified control")
+ HEARING_DEVICES_AMBIENT_CHANGE_UNIFIED(2149),
+ @UiEvent(doc = "Change the ambient volume with separated control")
+ HEARING_DEVICES_AMBIENT_CHANGE_SEPARATED(2150),
+ @UiEvent(doc = "Mute the ambient volume") HEARING_DEVICES_AMBIENT_MUTE(2151),
+ @UiEvent(doc = "Unmute the ambient volume") HEARING_DEVICES_AMBIENT_UNMUTE(2152),
+ @UiEvent(doc = "Expand the ambient volume controls")
+ HEARING_DEVICES_AMBIENT_EXPAND_CONTROLS(2153),
+ @UiEvent(doc = "Collapse the ambient volume controls")
+ HEARING_DEVICES_AMBIENT_COLLAPSE_CONTROLS(2154);
override fun getId(): Int = this.id
}
diff --git a/packages/SystemUI/src/com/android/systemui/communal/ui/viewmodel/CommunalViewModel.kt b/packages/SystemUI/src/com/android/systemui/communal/ui/viewmodel/CommunalViewModel.kt
index cce1ae1a2947..6473b1c30586 100644
--- a/packages/SystemUI/src/com/android/systemui/communal/ui/viewmodel/CommunalViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/communal/ui/viewmodel/CommunalViewModel.kt
@@ -237,7 +237,15 @@ constructor(
with(mediaHost) {
expansion = MediaHostState.EXPANDED
expandedMatchesParentHeight = true
- showsOnlyActiveMedia = false
+ if (v2FlagEnabled()) {
+ // Only show active media to match lock screen, not resumable media, which can
+ // persist
+ // for up to 2 days.
+ showsOnlyActiveMedia = true
+ } else {
+ // Maintain old behavior on tablet until V2 flag rolls out.
+ showsOnlyActiveMedia = false
+ }
falsingProtectionNeeded = false
disablePagination = true
init(MediaHierarchyManager.LOCATION_COMMUNAL_HUB)
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/dagger/KeyguardModule.java b/packages/SystemUI/src/com/android/systemui/keyguard/dagger/KeyguardModule.java
index 97ad2d7d36ff..6b1248b6983e 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/dagger/KeyguardModule.java
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/dagger/KeyguardModule.java
@@ -247,14 +247,13 @@ public interface KeyguardModule {
@Provides
@SysUISingleton
static BlurConfig provideBlurConfig(@Main Resources resources) {
- int minBlurRadius = resources.getDimensionPixelSize(R.dimen.min_window_blur_radius);
int maxBlurRadius =
Flags.notificationShadeBlur() || Flags.bouncerUiRevamp()
|| Flags.glanceableHubBlurredBackground()
? resources.getDimensionPixelSize(R.dimen.max_shade_window_blur_radius)
: resources.getDimensionPixelSize(R.dimen.max_window_blur_radius);
- return new BlurConfig(minBlurRadius, maxBlurRadius);
+ return new BlurConfig(0.0f, maxBlurRadius);
}
/** */
diff --git a/packages/SystemUI/src/com/android/systemui/media/controls/ui/binder/MediaControlViewBinder.kt b/packages/SystemUI/src/com/android/systemui/media/controls/ui/binder/MediaControlViewBinder.kt
index 6d796d96ea71..3f538203aee9 100644
--- a/packages/SystemUI/src/com/android/systemui/media/controls/ui/binder/MediaControlViewBinder.kt
+++ b/packages/SystemUI/src/com/android/systemui/media/controls/ui/binder/MediaControlViewBinder.kt
@@ -49,9 +49,10 @@ import com.android.systemui.media.controls.ui.util.MediaArtworkHelper
import com.android.systemui.media.controls.ui.view.MediaViewHolder
import com.android.systemui.media.controls.ui.viewmodel.MediaActionViewModel
import com.android.systemui.media.controls.ui.viewmodel.MediaControlViewModel
-import com.android.systemui.media.controls.ui.viewmodel.MediaControlViewModel.Companion.MEDIA_PLAYER_SCRIM_CENTER_ALPHA
import com.android.systemui.media.controls.ui.viewmodel.MediaControlViewModel.Companion.MEDIA_PLAYER_SCRIM_END_ALPHA
+import com.android.systemui.media.controls.ui.viewmodel.MediaControlViewModel.Companion.MEDIA_PLAYER_SCRIM_END_ALPHA_LEGACY
import com.android.systemui.media.controls.ui.viewmodel.MediaControlViewModel.Companion.MEDIA_PLAYER_SCRIM_START_ALPHA
+import com.android.systemui.media.controls.ui.viewmodel.MediaControlViewModel.Companion.MEDIA_PLAYER_SCRIM_START_ALPHA_LEGACY
import com.android.systemui.media.controls.ui.viewmodel.MediaControlViewModel.Companion.SEMANTIC_ACTIONS_ALL
import com.android.systemui.media.controls.ui.viewmodel.MediaControlViewModel.Companion.SEMANTIC_ACTIONS_COMPACT
import com.android.systemui.media.controls.ui.viewmodel.MediaOutputSwitcherViewModel
@@ -537,18 +538,24 @@ object MediaControlViewBinder {
height: Int,
): LayerDrawable {
val albumArt = MediaArtworkHelper.getScaledBackground(context, artworkIcon, width, height)
- val alpha =
+ val startAlpha =
if (Flags.mediaControlsA11yColors()) {
- MEDIA_PLAYER_SCRIM_CENTER_ALPHA
- } else {
MEDIA_PLAYER_SCRIM_START_ALPHA
+ } else {
+ MEDIA_PLAYER_SCRIM_START_ALPHA_LEGACY
+ }
+ val endAlpha =
+ if (Flags.mediaControlsA11yColors()) {
+ MEDIA_PLAYER_SCRIM_END_ALPHA
+ } else {
+ MEDIA_PLAYER_SCRIM_END_ALPHA_LEGACY
}
return MediaArtworkHelper.setUpGradientColorOnDrawable(
albumArt,
context.getDrawable(R.drawable.qs_media_scrim)?.mutate() as GradientDrawable,
mutableColorScheme,
- alpha,
- MEDIA_PLAYER_SCRIM_END_ALPHA,
+ startAlpha,
+ endAlpha,
)
}
diff --git a/packages/SystemUI/src/com/android/systemui/media/controls/ui/binder/SeekBarObserver.kt b/packages/SystemUI/src/com/android/systemui/media/controls/ui/binder/SeekBarObserver.kt
index 34f7c4dcaec0..c9716be52408 100644
--- a/packages/SystemUI/src/com/android/systemui/media/controls/ui/binder/SeekBarObserver.kt
+++ b/packages/SystemUI/src/com/android/systemui/media/controls/ui/binder/SeekBarObserver.kt
@@ -18,6 +18,9 @@ package com.android.systemui.media.controls.ui.binder
import android.animation.Animator
import android.animation.ObjectAnimator
+import android.icu.text.MeasureFormat
+import android.icu.util.Measure
+import android.icu.util.MeasureUnit
import android.text.format.DateUtils
import androidx.annotation.UiThread
import androidx.lifecycle.Observer
@@ -28,8 +31,11 @@ import com.android.systemui.media.controls.ui.drawable.SquigglyProgress
import com.android.systemui.media.controls.ui.view.MediaViewHolder
import com.android.systemui.media.controls.ui.viewmodel.SeekBarViewModel
import com.android.systemui.res.R
+import java.util.Locale
private const val TAG = "SeekBarObserver"
+private const val MIN_IN_SEC = 60
+private const val HOUR_IN_SEC = MIN_IN_SEC * 60
/**
* Observer for changes from SeekBarViewModel.
@@ -127,10 +133,9 @@ open class SeekBarObserver(private val holder: MediaViewHolder) :
}
holder.seekBar.setMax(data.duration)
- val totalTimeString =
- DateUtils.formatElapsedTime(data.duration / DateUtils.SECOND_IN_MILLIS)
+ val totalTimeDescription = formatTimeContentDescription(data.duration)
if (data.scrubbing) {
- holder.scrubbingTotalTimeView.text = totalTimeString
+ holder.scrubbingTotalTimeView.text = formatTimeLabel(data.duration)
}
data.elapsedTime?.let {
@@ -148,20 +153,62 @@ open class SeekBarObserver(private val holder: MediaViewHolder) :
}
}
- val elapsedTimeString = DateUtils.formatElapsedTime(it / DateUtils.SECOND_IN_MILLIS)
+ val elapsedTimeDescription = formatTimeContentDescription(it)
if (data.scrubbing) {
- holder.scrubbingElapsedTimeView.text = elapsedTimeString
+ holder.scrubbingElapsedTimeView.text = formatTimeLabel(it)
}
holder.seekBar.contentDescription =
holder.seekBar.context.getString(
R.string.controls_media_seekbar_description,
- elapsedTimeString,
- totalTimeString
+ elapsedTimeDescription,
+ totalTimeDescription,
)
}
}
+ /** Returns a time string suitable for display, e.g. "12:34" */
+ private fun formatTimeLabel(milliseconds: Int): CharSequence {
+ return DateUtils.formatElapsedTime(milliseconds / DateUtils.SECOND_IN_MILLIS)
+ }
+
+ /**
+ * Returns a time string suitable for content description, e.g. "12 minutes 34 seconds"
+ *
+ * Follows same logic as Chronometer#formatDuration
+ */
+ private fun formatTimeContentDescription(milliseconds: Int): CharSequence {
+ var seconds = milliseconds / DateUtils.SECOND_IN_MILLIS
+
+ val hours =
+ if (seconds >= HOUR_IN_SEC) {
+ seconds / HOUR_IN_SEC
+ } else {
+ 0
+ }
+ seconds -= hours * HOUR_IN_SEC
+
+ val minutes =
+ if (seconds >= MIN_IN_SEC) {
+ seconds / MIN_IN_SEC
+ } else {
+ 0
+ }
+ seconds -= minutes * MIN_IN_SEC
+
+ val measures = arrayListOf<Measure>()
+ if (hours > 0) {
+ measures.add(Measure(hours, MeasureUnit.HOUR))
+ }
+ if (minutes > 0) {
+ measures.add(Measure(minutes, MeasureUnit.MINUTE))
+ }
+ measures.add(Measure(seconds, MeasureUnit.SECOND))
+
+ return MeasureFormat.getInstance(Locale.getDefault(), MeasureFormat.FormatWidth.WIDE)
+ .formatMeasures(*measures.toTypedArray())
+ }
+
@VisibleForTesting
open fun buildResetAnimator(targetTime: Int): Animator {
val animator =
@@ -169,7 +216,7 @@ open class SeekBarObserver(private val holder: MediaViewHolder) :
holder.seekBar,
"progress",
holder.seekBar.progress,
- targetTime + RESET_ANIMATION_DURATION_MS
+ targetTime + RESET_ANIMATION_DURATION_MS,
)
animator.setAutoCancel(true)
animator.duration = RESET_ANIMATION_DURATION_MS.toLong()
diff --git a/packages/SystemUI/src/com/android/systemui/media/controls/ui/controller/MediaControlPanel.java b/packages/SystemUI/src/com/android/systemui/media/controls/ui/controller/MediaControlPanel.java
index 39c08daf53d6..694a4c7e493d 100644
--- a/packages/SystemUI/src/com/android/systemui/media/controls/ui/controller/MediaControlPanel.java
+++ b/packages/SystemUI/src/com/android/systemui/media/controls/ui/controller/MediaControlPanel.java
@@ -23,7 +23,10 @@ import static com.android.systemui.Flags.communalHub;
import static com.android.systemui.Flags.mediaLockscreenLaunchAnimation;
import static com.android.systemui.media.controls.domain.pipeline.MediaActionsKt.getNotificationActions;
import static com.android.systemui.media.controls.shared.model.SmartspaceMediaDataKt.NUM_REQUIRED_RECOMMENDATIONS;
-import static com.android.systemui.media.controls.ui.viewmodel.MediaControlViewModel.MEDIA_PLAYER_SCRIM_CENTER_ALPHA;
+import static com.android.systemui.media.controls.ui.viewmodel.MediaControlViewModel.MEDIA_PLAYER_SCRIM_END_ALPHA;
+import static com.android.systemui.media.controls.ui.viewmodel.MediaControlViewModel.MEDIA_PLAYER_SCRIM_END_ALPHA_LEGACY;
+import static com.android.systemui.media.controls.ui.viewmodel.MediaControlViewModel.MEDIA_PLAYER_SCRIM_START_ALPHA;
+import static com.android.systemui.media.controls.ui.viewmodel.MediaControlViewModel.MEDIA_PLAYER_SCRIM_START_ALPHA_LEGACY;
import android.animation.Animator;
import android.animation.AnimatorInflater;
@@ -176,9 +179,7 @@ public class MediaControlPanel {
protected static final int SMARTSPACE_CARD_DISMISS_EVENT = 761;
private static final float REC_MEDIA_COVER_SCALE_FACTOR = 1.25f;
- private static final float MEDIA_SCRIM_START_ALPHA = 0.25f;
private static final float MEDIA_REC_SCRIM_START_ALPHA = 0.15f;
- private static final float MEDIA_PLAYER_SCRIM_END_ALPHA = 1.0f;
private static final float MEDIA_REC_SCRIM_END_ALPHA = 1.0f;
private static final Intent SETTINGS_INTENT = new Intent(ACTION_MEDIA_CONTROLS_SETTINGS);
@@ -1093,11 +1094,12 @@ public class MediaControlPanel {
Drawable albumArt = getScaledBackground(artworkIcon, width, height);
GradientDrawable gradient = (GradientDrawable) mContext.getDrawable(
R.drawable.qs_media_scrim).mutate();
- float startAlpha = (Flags.mediaControlsA11yColors())
- ? MEDIA_PLAYER_SCRIM_CENTER_ALPHA
- : MEDIA_SCRIM_START_ALPHA;
+ if (Flags.mediaControlsA11yColors()) {
+ return setupGradientColorOnDrawable(albumArt, gradient, mutableColorScheme,
+ MEDIA_PLAYER_SCRIM_START_ALPHA, MEDIA_PLAYER_SCRIM_END_ALPHA);
+ }
return setupGradientColorOnDrawable(albumArt, gradient, mutableColorScheme,
- startAlpha, MEDIA_PLAYER_SCRIM_END_ALPHA);
+ MEDIA_PLAYER_SCRIM_START_ALPHA_LEGACY, MEDIA_PLAYER_SCRIM_END_ALPHA_LEGACY);
}
@VisibleForTesting
diff --git a/packages/SystemUI/src/com/android/systemui/media/controls/ui/controller/MediaViewController.kt b/packages/SystemUI/src/com/android/systemui/media/controls/ui/controller/MediaViewController.kt
index 198155b3f297..b687dce20b06 100644
--- a/packages/SystemUI/src/com/android/systemui/media/controls/ui/controller/MediaViewController.kt
+++ b/packages/SystemUI/src/com/android/systemui/media/controls/ui/controller/MediaViewController.kt
@@ -1137,6 +1137,7 @@ constructor(
) {
gutsViewHolder.gutsText.setTypeface(menuTF)
gutsViewHolder.dismissText.setTypeface(menuTF)
+ gutsViewHolder.cancelText.setTypeface(menuTF)
titleText.setTypeface(titleTF)
artistText.setTypeface(artistTF)
seamlessText.setTypeface(menuTF)
diff --git a/packages/SystemUI/src/com/android/systemui/media/controls/ui/viewmodel/MediaControlViewModel.kt b/packages/SystemUI/src/com/android/systemui/media/controls/ui/viewmodel/MediaControlViewModel.kt
index 9153e17393d2..bcda485272c2 100644
--- a/packages/SystemUI/src/com/android/systemui/media/controls/ui/viewmodel/MediaControlViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/media/controls/ui/viewmodel/MediaControlViewModel.kt
@@ -419,8 +419,10 @@ class MediaControlViewModel(
const val TURBULENCE_NOISE_PLAY_MS_DURATION = 7500L
@Deprecated("Remove with media_controls_a11y_colors flag")
- const val MEDIA_PLAYER_SCRIM_START_ALPHA = 0.25f
- const val MEDIA_PLAYER_SCRIM_CENTER_ALPHA = 0.75f
- const val MEDIA_PLAYER_SCRIM_END_ALPHA = 1.0f
+ const val MEDIA_PLAYER_SCRIM_START_ALPHA_LEGACY = 0.25f
+ @Deprecated("Remove with media_controls_a11y_colors flag")
+ const val MEDIA_PLAYER_SCRIM_END_ALPHA_LEGACY = 1.0f
+ const val MEDIA_PLAYER_SCRIM_START_ALPHA = 0.65f
+ const val MEDIA_PLAYER_SCRIM_END_ALPHA = 0.75f
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/qs/QSEvents.kt b/packages/SystemUI/src/com/android/systemui/qs/QSEvents.kt
index 24bb16a11fe7..3a81102699f7 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/QSEvents.kt
+++ b/packages/SystemUI/src/com/android/systemui/qs/QSEvents.kt
@@ -27,9 +27,7 @@ object QSEvents {
private set
fun setLoggerForTesting(): UiEventLoggerFake {
- return UiEventLoggerFake().also {
- qsUiEventsLogger = it
- }
+ return UiEventLoggerFake().also { qsUiEventsLogger = it }
}
fun resetLogger() {
@@ -40,32 +38,28 @@ object QSEvents {
enum class QSEvent(private val _id: Int) : UiEventLogger.UiEventEnum {
@UiEvent(doc = "Tile clicked. It has an instance id and a spec (or packageName)")
QS_ACTION_CLICK(387),
-
- @UiEvent(doc = "Tile secondary button clicked. " +
- "It has an instance id and a spec (or packageName)")
+ @UiEvent(
+ doc =
+ "Tile secondary button clicked. " + "It has an instance id and a spec (or packageName)"
+ )
QS_ACTION_SECONDARY_CLICK(388),
-
@UiEvent(doc = "Tile long clicked. It has an instance id and a spec (or packageName)")
QS_ACTION_LONG_PRESS(389),
-
- @UiEvent(doc = "Quick Settings panel expanded")
- QS_PANEL_EXPANDED(390),
-
- @UiEvent(doc = "Quick Settings panel collapsed")
- QS_PANEL_COLLAPSED(391),
-
- @UiEvent(doc = "Tile visible in Quick Settings panel. The tile may be in a different page. " +
- "It has an instance id and a spec (or packageName)")
+ @UiEvent(doc = "Quick Settings panel expanded") QS_PANEL_EXPANDED(390),
+ @UiEvent(doc = "Quick Settings panel collapsed") QS_PANEL_COLLAPSED(391),
+ @UiEvent(
+ doc =
+ "Tile visible in Quick Settings panel. The tile may be in a different page. " +
+ "It has an instance id and a spec (or packageName)"
+ )
QS_TILE_VISIBLE(392),
-
- @UiEvent(doc = "Quick Quick Settings panel expanded")
- QQS_PANEL_EXPANDED(393),
-
- @UiEvent(doc = "Quick Quick Settings panel collapsed")
- QQS_PANEL_COLLAPSED(394),
-
- @UiEvent(doc = "Tile visible in Quick Quick Settings panel. " +
- "It has an instance id and a spec (or packageName)")
+ @UiEvent(doc = "Quick Quick Settings panel expanded") QQS_PANEL_EXPANDED(393),
+ @UiEvent(doc = "Quick Quick Settings panel collapsed") QQS_PANEL_COLLAPSED(394),
+ @UiEvent(
+ doc =
+ "Tile visible in Quick Quick Settings panel. " +
+ "It has an instance id and a spec (or packageName)"
+ )
QQS_TILE_VISIBLE(395);
override fun getId() = _id
@@ -73,47 +67,32 @@ enum class QSEvent(private val _id: Int) : UiEventLogger.UiEventEnum {
enum class QSEditEvent(private val _id: Int) : UiEventLogger.UiEventEnum {
- @UiEvent(doc = "Tile removed from current tiles")
- QS_EDIT_REMOVE(210),
-
- @UiEvent(doc = "Tile added to current tiles")
- QS_EDIT_ADD(211),
-
- @UiEvent(doc = "Tile moved")
- QS_EDIT_MOVE(212),
-
- @UiEvent(doc = "QS customizer open")
- QS_EDIT_OPEN(213),
-
- @UiEvent(doc = "QS customizer closed")
- QS_EDIT_CLOSED(214),
-
- @UiEvent(doc = "QS tiles reset")
- QS_EDIT_RESET(215);
+ @UiEvent(doc = "Tile removed from current tiles") QS_EDIT_REMOVE(210),
+ @UiEvent(doc = "Tile added to current tiles") QS_EDIT_ADD(211),
+ @UiEvent(doc = "Tile moved") QS_EDIT_MOVE(212),
+ @UiEvent(doc = "QS customizer open") QS_EDIT_OPEN(213),
+ @UiEvent(doc = "QS customizer closed") QS_EDIT_CLOSED(214),
+ @UiEvent(doc = "QS tiles reset") QS_EDIT_RESET(215),
+ @UiEvent(doc = "QS edit mode resize tile to large") QS_EDIT_RESIZE_LARGE(2122),
+ @UiEvent(doc = "QS edit mode resize tile to small") QS_EDIT_RESIZE_SMALL(2123);
override fun getId() = _id
}
/**
- * Events from the QS DND tile dialog. {@see QSZenModeDialogMetricsLogger}
- * Other names for DND (Do Not Disturb) include "Zen" and "Priority mode".
+ * Events from the QS DND tile dialog. {@see QSZenModeDialogMetricsLogger} Other names for DND (Do
+ * Not Disturb) include "Zen" and "Priority mode".
*/
enum class QSDndEvent(private val _id: Int) : UiEventLogger.UiEventEnum {
- @UiEvent(doc = "User selected an option on the DND dialog")
- QS_DND_CONDITION_SELECT(420),
-
+ @UiEvent(doc = "User selected an option on the DND dialog") QS_DND_CONDITION_SELECT(420),
@UiEvent(doc = "User increased countdown duration of DND from the DND dialog")
QS_DND_TIME_UP(422),
-
@UiEvent(doc = "User decreased countdown duration of DND from the DND dialog")
QS_DND_TIME_DOWN(423),
-
@UiEvent(doc = "User enabled DND from the QS DND dialog to last until manually turned off")
QS_DND_DIALOG_ENABLE_FOREVER(946),
-
@UiEvent(doc = "User enabled DND from the QS DND dialog to last until the next alarm goes off")
QS_DND_DIALOG_ENABLE_UNTIL_ALARM(947),
-
@UiEvent(doc = "User enabled DND from the QS DND dialog to last until countdown is done")
QS_DND_DIALOG_ENABLE_UNTIL_COUNTDOWN(948);
@@ -121,29 +100,17 @@ enum class QSDndEvent(private val _id: Int) : UiEventLogger.UiEventEnum {
}
enum class QSUserSwitcherEvent(private val _id: Int) : UiEventLogger.UiEventEnum {
- @UiEvent(doc = "The current user has been switched in the detail panel")
- QS_USER_SWITCH(424),
-
- @UiEvent(doc = "User switcher QS dialog open")
- QS_USER_DETAIL_OPEN(425),
-
- @UiEvent(doc = "User switcher QS dialog closed")
- QS_USER_DETAIL_CLOSE(426),
-
- @UiEvent(doc = "User switcher QS dialog more settings pressed")
- QS_USER_MORE_SETTINGS(427),
-
- @UiEvent(doc = "The user has added a guest in the detail panel")
- QS_USER_GUEST_ADD(754),
-
+ @UiEvent(doc = "The current user has been switched in the detail panel") QS_USER_SWITCH(424),
+ @UiEvent(doc = "User switcher QS dialog open") QS_USER_DETAIL_OPEN(425),
+ @UiEvent(doc = "User switcher QS dialog closed") QS_USER_DETAIL_CLOSE(426),
+ @UiEvent(doc = "User switcher QS dialog more settings pressed") QS_USER_MORE_SETTINGS(427),
+ @UiEvent(doc = "The user has added a guest in the detail panel") QS_USER_GUEST_ADD(754),
@UiEvent(doc = "The user selected 'Start over' after switching to the existing Guest user")
QS_USER_GUEST_WIPE(755),
-
@UiEvent(doc = "The user selected 'Yes, continue' after switching to the existing Guest user")
QS_USER_GUEST_CONTINUE(756),
-
@UiEvent(doc = "The user has pressed 'Remove guest' in the detail panel")
QS_USER_GUEST_REMOVE(757);
override fun getId() = _id
-} \ No newline at end of file
+}
diff --git a/packages/SystemUI/src/com/android/systemui/qs/composefragment/QSFragmentCompose.kt b/packages/SystemUI/src/com/android/systemui/qs/composefragment/QSFragmentCompose.kt
index 6ad8bae05d7a..5930a24e01a0 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/composefragment/QSFragmentCompose.kt
+++ b/packages/SystemUI/src/com/android/systemui/qs/composefragment/QSFragmentCompose.kt
@@ -306,6 +306,7 @@ constructor(
sceneState,
viewModel.containerViewModel.editModeViewModel.isEditing,
snapshotFlow { viewModel.expansionState }.map { it.progress },
+ snapshotFlow { viewModel.isQSExpandingOrCollapsing },
)
}
@@ -537,6 +538,10 @@ constructor(
return qqsVisible.value
}
+ override fun setQSExpandingOrCollapsing(isQSExpandingOrCollapsing: Boolean) {
+ viewModel.isQSExpandingOrCollapsing = isQSExpandingOrCollapsing
+ }
+
private fun setListenerCollections() {
lifecycleScope.launch {
lifecycle.repeatOnLifecycle(Lifecycle.State.STARTED) {
@@ -877,6 +882,7 @@ private suspend fun synchronizeQsState(
state: MutableSceneTransitionLayoutState,
editMode: Flow<Boolean>,
expansion: Flow<Float>,
+ isQSExpandingOrCollapsing: Flow<Boolean>,
) {
coroutineScope {
val animationScope = this
@@ -888,31 +894,46 @@ private suspend fun synchronizeQsState(
currentTransition = null
}
- editMode.combine(expansion, ::Pair).collectLatest { (editMode, progress) ->
+ var lastValidProgress = 0f
+ combine(editMode, expansion, isQSExpandingOrCollapsing, ::Triple).collectLatest {
+ (editMode, progress, isQSExpandingOrCollapsing) ->
if (editMode && state.currentScene != SceneKeys.EditMode) {
state.setTargetScene(SceneKeys.EditMode, animationScope)?.second?.join()
} else if (!editMode && state.currentScene == SceneKeys.EditMode) {
state.setTargetScene(SceneKeys.QuickSettings, animationScope)?.second?.join()
}
+
if (!editMode) {
- when (progress) {
- 0f -> snapTo(QuickQuickSettings)
- 1f -> snapTo(QuickSettings)
- else -> {
- val transition = currentTransition
- if (transition != null) {
- transition.progress = progress
- return@collectLatest
- }
+ if (!isQSExpandingOrCollapsing) {
+ if (progress == 0f) {
+ snapTo(QuickQuickSettings)
+ return@collectLatest
+ }
- val newTransition =
- ExpansionTransition(progress).also { currentTransition = it }
- state.startTransitionImmediately(
- animationScope = animationScope,
- transition = newTransition,
- )
+ if (progress == 1f) {
+ snapTo(QuickSettings)
+ return@collectLatest
}
}
+
+ var progress = progress
+ if (progress >= 0f || progress <= 1f) {
+ lastValidProgress = progress
+ } else {
+ progress = lastValidProgress
+ }
+
+ val transition = currentTransition
+ if (transition != null) {
+ transition.progress = progress
+ return@collectLatest
+ }
+
+ val newTransition = ExpansionTransition(progress).also { currentTransition = it }
+ state.startTransitionImmediately(
+ animationScope = animationScope,
+ transition = newTransition,
+ )
}
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/qs/composefragment/viewmodel/QSFragmentComposeViewModel.kt b/packages/SystemUI/src/com/android/systemui/qs/composefragment/viewmodel/QSFragmentComposeViewModel.kt
index ff84479ebcad..b829bbce2f18 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/composefragment/viewmodel/QSFragmentComposeViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/qs/composefragment/viewmodel/QSFragmentComposeViewModel.kt
@@ -306,6 +306,8 @@ constructor(
val animateTilesExpansion: Boolean
get() = inFirstPage && !mediaSuddenlyAppearingInLandscape
+ var isQSExpandingOrCollapsing by mutableStateOf(false)
+
private val inFirstPage: Boolean
get() = inFirstPageViewModel.inFirstPage
@@ -539,6 +541,7 @@ constructor(
println("proposedTranslation", proposedTranslation)
println("expansionState", expansionState)
println("forceQS", forceQs)
+ println("isShadeExpandingOrCollapsing", isQSExpandingOrCollapsing)
printSection("Derived values") {
println("headerTranslation", headerTranslation)
println("translationScaleY", translationScaleY)
diff --git a/packages/SystemUI/src/com/android/systemui/qs/panels/domain/interactor/IconTilesInteractor.kt b/packages/SystemUI/src/com/android/systemui/qs/panels/domain/interactor/IconTilesInteractor.kt
index 482cd4014acf..3f279b0f7a74 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/panels/domain/interactor/IconTilesInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/qs/panels/domain/interactor/IconTilesInteractor.kt
@@ -16,15 +16,18 @@
package com.android.systemui.qs.panels.domain.interactor
+import com.android.internal.logging.UiEventLogger
import com.android.systemui.dagger.SysUISingleton
import com.android.systemui.dagger.qualifiers.Application
import com.android.systemui.log.LogBuffer
import com.android.systemui.log.core.LogLevel
+import com.android.systemui.qs.QSEditEvent
import com.android.systemui.qs.panels.data.repository.DefaultLargeTilesRepository
import com.android.systemui.qs.panels.data.repository.LargeTileSpanRepository
import com.android.systemui.qs.panels.shared.model.PanelsLog
import com.android.systemui.qs.pipeline.domain.interactor.CurrentTilesInteractor
import com.android.systemui.qs.pipeline.shared.TileSpec
+import com.android.systemui.qs.pipeline.shared.metricSpec
import javax.inject.Inject
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.flow.SharingStarted
@@ -40,6 +43,7 @@ constructor(
private val repo: DefaultLargeTilesRepository,
private val currentTilesInteractor: CurrentTilesInteractor,
private val preferencesInteractor: QSPreferencesInteractor,
+ private val uiEventLogger: UiEventLogger,
largeTilesSpanRepo: LargeTileSpanRepository,
@PanelsLog private val logBuffer: LogBuffer,
@Application private val applicationScope: CoroutineScope,
@@ -70,8 +74,18 @@ constructor(
val isIcon = !largeTilesSpecs.value.contains(spec)
if (toIcon && !isIcon) {
preferencesInteractor.setLargeTilesSpecs(largeTilesSpecs.value - spec)
+ uiEventLogger.log(
+ /* event= */ QSEditEvent.QS_EDIT_RESIZE_SMALL,
+ /* uid= */ 0,
+ /* packageName= */ spec.metricSpec,
+ )
} else if (!toIcon && isIcon) {
preferencesInteractor.setLargeTilesSpecs(largeTilesSpecs.value + spec)
+ uiEventLogger.log(
+ /* event= */ QSEditEvent.QS_EDIT_RESIZE_LARGE,
+ /* uid= */ 0,
+ /* packageName= */ spec.metricSpec,
+ )
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/shade/QuickSettingsControllerImpl.java b/packages/SystemUI/src/com/android/systemui/shade/QuickSettingsControllerImpl.java
index d05837261b89..34b3324f81da 100644
--- a/packages/SystemUI/src/com/android/systemui/shade/QuickSettingsControllerImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/shade/QuickSettingsControllerImpl.java
@@ -105,15 +105,14 @@ import com.android.systemui.util.kotlin.JavaAdapter;
import dalvik.annotation.optimization.NeverCompile;
-import dagger.Lazy;
-
-import kotlin.Unit;
-
import java.io.PrintWriter;
import javax.inject.Inject;
import javax.inject.Provider;
+import dagger.Lazy;
+import kotlin.Unit;
+
/** Handles QuickSettings touch handling, expansion and animation state. */
@SysUISingleton
public class QuickSettingsControllerImpl implements QuickSettingsController, Dumpable {
@@ -2366,8 +2365,16 @@ public class QuickSettingsControllerImpl implements QuickSettingsController, Dum
return;
}
if (startTracing) {
+ if (mQs != null) {
+ mQs.setQSExpandingOrCollapsing(true);
+ }
+
monitor.begin(mPanelView, Cuj.CUJ_NOTIFICATION_SHADE_QS_EXPAND_COLLAPSE);
} else {
+ if (mQs != null) {
+ mQs.setQSExpandingOrCollapsing(false);
+ }
+
if (wasCancelled) {
monitor.cancel(Cuj.CUJ_NOTIFICATION_SHADE_QS_EXPAND_COLLAPSE);
} else {
diff --git a/packages/SystemUI/src/com/android/systemui/shade/ui/viewmodel/ShadeHeaderViewModel.kt b/packages/SystemUI/src/com/android/systemui/shade/ui/viewmodel/ShadeHeaderViewModel.kt
index 4adc1a5ae746..20b44d73e097 100644
--- a/packages/SystemUI/src/com/android/systemui/shade/ui/viewmodel/ShadeHeaderViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/shade/ui/viewmodel/ShadeHeaderViewModel.kt
@@ -22,7 +22,9 @@ import android.icu.text.DateFormat
import android.icu.text.DisplayContext
import android.provider.Settings
import android.view.ViewGroup
+import androidx.compose.material3.ColorScheme
import androidx.compose.runtime.getValue
+import androidx.compose.ui.graphics.Color
import com.android.app.tracing.coroutines.launchTraced as launch
import com.android.systemui.battery.BatteryMeterViewController
import com.android.systemui.lifecycle.ExclusiveActivatable
@@ -244,11 +246,29 @@ constructor(
/** Represents the background highlight of a header icons chip. */
sealed interface HeaderChipHighlight {
- data object None : HeaderChipHighlight
- data object Weak : HeaderChipHighlight
+ fun backgroundColor(colorScheme: ColorScheme): Color
- data object Strong : HeaderChipHighlight
+ fun foregroundColor(colorScheme: ColorScheme): Color
+
+ data object None : HeaderChipHighlight {
+ override fun backgroundColor(colorScheme: ColorScheme): Color = Color.Unspecified
+
+ override fun foregroundColor(colorScheme: ColorScheme): Color = colorScheme.primary
+ }
+
+ data object Weak : HeaderChipHighlight {
+ override fun backgroundColor(colorScheme: ColorScheme): Color =
+ colorScheme.primary.copy(alpha = 0.1f)
+
+ override fun foregroundColor(colorScheme: ColorScheme): Color = colorScheme.primary
+ }
+
+ data object Strong : HeaderChipHighlight {
+ override fun backgroundColor(colorScheme: ColorScheme): Color = colorScheme.secondary
+
+ override fun foregroundColor(colorScheme: ColorScheme): Color = colorScheme.onSecondary
+ }
}
private fun getFormatFromPattern(pattern: String?): DateFormat {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/BlurUtils.kt b/packages/SystemUI/src/com/android/systemui/statusbar/BlurUtils.kt
index f30043eece62..f45971b57b65 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/BlurUtils.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/BlurUtils.kt
@@ -45,7 +45,7 @@ open class BlurUtils @Inject constructor(
private val crossWindowBlurListeners: CrossWindowBlurListeners,
dumpManager: DumpManager
) : Dumpable {
- val minBlurRadius = blurConfig.minBlurRadiusPx
+ val minBlurRadius = resources.getDimensionPixelSize(R.dimen.min_window_blur_radius).toFloat();
val maxBlurRadius = if (Flags.notificationShadeBlur()) {
blurConfig.maxBlurRadiusPx
} else {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationGroupingUtil.java b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationGroupingUtil.java
index c1b8d9d123b9..6ebe02469f5c 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationGroupingUtil.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationGroupingUtil.java
@@ -16,6 +16,8 @@
package com.android.systemui.statusbar;
+import static android.app.Flags.notificationsRedesignTemplates;
+
import android.app.Flags;
import android.app.Notification;
import android.graphics.drawable.Drawable;
@@ -427,7 +429,8 @@ public class NotificationGroupingUtil {
@Override
public void apply(View parent, View view, boolean apply, boolean reset) {
- if (reset && parent instanceof ConversationLayout) {
+ if (!notificationsRedesignTemplates()
+ && reset && parent instanceof ConversationLayout) {
ConversationLayout layout = (ConversationLayout) parent;
apply = layout.shouldHideAppName();
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationLockscreenUserManagerImpl.java b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationLockscreenUserManagerImpl.java
index f06565f1b6d2..32da6fff6bcc 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationLockscreenUserManagerImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationLockscreenUserManagerImpl.java
@@ -24,7 +24,7 @@ import static android.app.admin.DevicePolicyManager.KEYGUARD_DISABLE_UNREDACTED_
import static android.os.Flags.allowPrivateProfile;
import static android.os.UserHandle.USER_ALL;
import static android.os.UserHandle.USER_NULL;
-import static android.provider.Settings.Secure.REDACT_OTP_NOTIFICATION_IMMEDIATELY;
+import static android.provider.Settings.Secure.OTP_NOTIFICATION_REDACTION_LOCK_TIME;
import static android.provider.Settings.Secure.LOCK_SCREEN_ALLOW_PRIVATE_NOTIFICATIONS;
import static android.provider.Settings.Secure.LOCK_SCREEN_SHOW_NOTIFICATIONS;
import static android.provider.Settings.Secure.REDACT_OTP_NOTIFICATION_WHILE_CONNECTED_TO_WIFI;
@@ -124,10 +124,10 @@ public class NotificationLockscreenUserManagerImpl implements
private static final Uri REDACT_OTP_ON_WIFI =
Settings.Secure.getUriFor(REDACT_OTP_NOTIFICATION_WHILE_CONNECTED_TO_WIFI);
- private static final Uri REDACT_OTP_IMMEDIATELY =
- Settings.Secure.getUriFor(REDACT_OTP_NOTIFICATION_IMMEDIATELY);
+ private static final Uri OTP_REDACTION_LOCK_TIME =
+ Settings.Secure.getUriFor(OTP_NOTIFICATION_REDACTION_LOCK_TIME);
- private static final long LOCK_TIME_FOR_SENSITIVE_REDACTION_MS =
+ private static final long DEFAULT_LOCK_TIME_FOR_SENSITIVE_REDACTION_MS =
TimeUnit.MINUTES.toMillis(10);
private final Lazy<NotificationVisibilityProvider> mVisibilityProviderLazy;
private final Lazy<CommonNotifCollection> mCommonNotifCollectionLazy;
@@ -316,7 +316,8 @@ public class NotificationLockscreenUserManagerImpl implements
protected final AtomicBoolean mConnectedToWifi = new AtomicBoolean(false);
protected final AtomicBoolean mRedactOtpOnWifi = new AtomicBoolean(true);
- protected final AtomicBoolean mRedactOtpImmediately = new AtomicBoolean(false);
+ protected final AtomicLong mOtpRedactionRequiredLockTimeMs =
+ new AtomicLong(DEFAULT_LOCK_TIME_FOR_SENSITIVE_REDACTION_MS);
protected int mCurrentUserId = 0;
@@ -375,7 +376,7 @@ public class NotificationLockscreenUserManagerImpl implements
mLockScreenUris.add(SHOW_LOCKSCREEN);
mLockScreenUris.add(SHOW_PRIVATE_LOCKSCREEN);
mLockScreenUris.add(REDACT_OTP_ON_WIFI);
- mLockScreenUris.add(REDACT_OTP_IMMEDIATELY);
+ mLockScreenUris.add(OTP_REDACTION_LOCK_TIME);
dumpManager.registerDumpable(this);
@@ -447,8 +448,8 @@ public class NotificationLockscreenUserManagerImpl implements
changed |= updateUserShowPrivateSettings(user.getIdentifier());
} else if (REDACT_OTP_ON_WIFI.equals(uri)) {
changed |= updateRedactOtpOnWifiSetting();
- } else if (REDACT_OTP_IMMEDIATELY.equals(uri)) {
- changed |= updateRedactOtpImmediatelySetting();
+ } else if (OTP_REDACTION_LOCK_TIME.equals(uri)) {
+ changed |= updateOtpLockTimeSetting();
}
}
@@ -487,7 +488,7 @@ public class NotificationLockscreenUserManagerImpl implements
mLockscreenSettingsObserver
);
mSecureSettings.registerContentObserverAsync(
- REDACT_OTP_IMMEDIATELY,
+ OTP_REDACTION_LOCK_TIME,
mLockscreenSettingsObserver
);
@@ -638,13 +639,13 @@ public class NotificationLockscreenUserManagerImpl implements
}
@WorkerThread
- private boolean updateRedactOtpImmediatelySetting() {
- boolean originalValue = mRedactOtpImmediately.get();
- boolean newValue = mSecureSettings.getIntForUser(
- REDACT_OTP_NOTIFICATION_IMMEDIATELY,
- 0,
- Process.myUserHandle().getIdentifier()) != 0;
- mRedactOtpImmediately.set(newValue);
+ private boolean updateOtpLockTimeSetting() {
+ long originalValue = mOtpRedactionRequiredLockTimeMs.get();
+ long newValue = mSecureSettings.getLongForUser(
+ OTP_NOTIFICATION_REDACTION_LOCK_TIME,
+ DEFAULT_LOCK_TIME_FOR_SENSITIVE_REDACTION_MS,
+ Process.myUserHandle().getIdentifier());
+ mOtpRedactionRequiredLockTimeMs.set(newValue);
return originalValue != newValue;
}
@@ -832,14 +833,9 @@ public class NotificationLockscreenUserManagerImpl implements
return false;
}
- long latestTimeForRedaction;
- if (mRedactOtpImmediately.get()) {
- latestTimeForRedaction = mLastLockTime.get();
- } else {
- // If the lock screen was not already locked for LOCK_TIME_FOR_SENSITIVE_REDACTION_MS
- // when this notification arrived, do not redact
- latestTimeForRedaction = mLastLockTime.get() + LOCK_TIME_FOR_SENSITIVE_REDACTION_MS;
- }
+ // If the lock screen was not already locked for at least mOtpRedactionRequiredLockTimeMs
+ // when this notification arrived, do not redact
+ long latestTimeForRedaction = mLastLockTime.get() + mOtpRedactionRequiredLockTimeMs.get();
if (ent.getSbn().getPostTime() < latestTimeForRedaction) {
return false;
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationShadeDepthController.kt b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationShadeDepthController.kt
index 6aa2fe29e768..3db004848d22 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationShadeDepthController.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationShadeDepthController.kt
@@ -110,6 +110,10 @@ constructor(
private var prevTimestamp: Long = -1
private var prevShadeDirection = 0
private var prevShadeVelocity = 0f
+ // tracks whether app launch transition is in progress. This involves two independent factors
+ // that control blur, shade expansion and app launch animation from outside sysui.
+ // They can complete out of order, this flag will be reset by the animation that finishes later.
+ private var appLaunchTransitionIsInProgress = false
// Only for dumpsys
private var lastAppliedBlur = 0
@@ -158,6 +162,18 @@ constructor(
if (field == value) {
return
}
+ // Set this to true now, this will be reset when the next shade expansion finishes or
+ // when the app launch finishes, whichever happens later.
+ if (value) {
+ appLaunchTransitionIsInProgress = true
+ } else {
+ // App was launching and now it has finished launching
+ if (shadeExpansion == 0.0f) {
+ // this means shade expansion finished before app launch was done.
+ // reset the flag here
+ appLaunchTransitionIsInProgress = false
+ }
+ }
field = value
scheduleUpdate()
@@ -172,6 +188,12 @@ constructor(
shadeAnimation.animateTo(0)
shadeAnimation.finishIfRunning()
}
+ @Deprecated(
+ message =
+ "This might get reset to false before shade expansion is fully done, " +
+ "consider using areBlursDisabledForAppLaunch"
+ )
+ get() = field
private var zoomOutCalculatedFromShadeRadius: Float = 0.0f
@@ -183,6 +205,11 @@ constructor(
scheduleUpdate()
}
+ private val areBlursDisabledForAppLaunch: Boolean
+ get() =
+ blursDisabledForAppLaunch ||
+ (Flags.bouncerUiRevamp() && appLaunchTransitionIsInProgress)
+
/** Force stop blur effect when necessary. */
private var scrimsVisible: Boolean = false
set(value) {
@@ -221,7 +248,7 @@ constructor(
combinedBlur = max(combinedBlur, blurUtils.blurRadiusOfRatio(transitionToFullShadeProgress))
var shadeRadius = max(combinedBlur, wakeAndUnlockBlurRadius)
- if (blursDisabledForAppLaunch || blursDisabledForUnlock) {
+ if (areBlursDisabledForAppLaunch || blursDisabledForUnlock) {
shadeRadius = 0f
}
@@ -259,7 +286,7 @@ constructor(
private val shouldBlurBeOpaque: Boolean
get() =
if (Flags.notificationShadeBlur()) false
- else scrimsVisible && !blursDisabledForAppLaunch
+ else scrimsVisible && !areBlursDisabledForAppLaunch
/** Callback that updates the window blur value and is called only once per frame. */
@VisibleForTesting
@@ -442,6 +469,13 @@ constructor(
val shadeDirection = sign(diff).toInt()
val shadeVelocity =
MathUtils.constrain(VELOCITY_SCALE * diff / deltaTime, MIN_VELOCITY, MAX_VELOCITY)
+ if (expansion == 0.0f && appLaunchTransitionIsInProgress && !blursDisabledForAppLaunch) {
+ // Shade expansion finished but the app launch is already done, then this should mark
+ // the transition as done.
+ Log.d(TAG, "appLaunchTransitionIsInProgress is now false from shade expansion event")
+ appLaunchTransitionIsInProgress = false
+ }
+
updateShadeAnimationBlur(expansion, tracking, shadeVelocity, shadeDirection)
prevShadeDirection = shadeDirection
@@ -553,6 +587,7 @@ constructor(
it.println("brightnessMirrorRadius: ${brightnessMirrorSpring.radius}")
it.println("wakeAndUnlockBlur: $wakeAndUnlockBlurRadius")
it.println("blursDisabledForAppLaunch: $blursDisabledForAppLaunch")
+ it.println("appLaunchTransitionIsInProgress: $appLaunchTransitionIsInProgress")
it.println("qsPanelExpansion: $qsPanelExpansion")
it.println("transitionToFullShadeProgress: $transitionToFullShadeProgress")
it.println("lastAppliedBlur: $lastAppliedBlur")
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/chips/call/domain/interactor/CallChipInteractor.kt b/packages/SystemUI/src/com/android/systemui/statusbar/chips/call/domain/interactor/CallChipInteractor.kt
index 2eae3eb4fc19..7548f6ff5bd2 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/chips/call/domain/interactor/CallChipInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/chips/call/domain/interactor/CallChipInteractor.kt
@@ -22,9 +22,9 @@ import com.android.systemui.log.LogBuffer
import com.android.systemui.log.core.LogLevel
import com.android.systemui.statusbar.chips.StatusBarChipLogTags.pad
import com.android.systemui.statusbar.chips.StatusBarChipsLog
+import com.android.systemui.statusbar.phone.ongoingcall.StatusBarChipsModernization
import com.android.systemui.statusbar.phone.ongoingcall.data.repository.OngoingCallRepository
import com.android.systemui.statusbar.phone.ongoingcall.domain.interactor.OngoingCallInteractor
-import com.android.systemui.statusbar.phone.ongoingcall.StatusBarChipsModernization
import com.android.systemui.statusbar.phone.ongoingcall.shared.model.OngoingCallModel
import javax.inject.Inject
import kotlinx.coroutines.CoroutineScope
@@ -44,25 +44,17 @@ constructor(
@StatusBarChipsLog private val logger: LogBuffer,
) {
val ongoingCallState: StateFlow<OngoingCallModel> =
- (if (StatusBarChipsModernization.isEnabled)
- ongoingCallInteractor.ongoingCallState
- else
- repository.ongoingCallState)
+ (if (StatusBarChipsModernization.isEnabled) {
+ ongoingCallInteractor.ongoingCallState
+ } else {
+ repository.ongoingCallState
+ })
.onEach {
- logger.log(
- TAG,
- LogLevel.INFO,
- { str1 = it::class.simpleName },
- { "State: $str1" }
- )
+ logger.log(TAG, LogLevel.INFO, { str1 = it::class.simpleName }, { "State: $str1" })
}
- .stateIn(
- scope,
- SharingStarted.Lazily,
- OngoingCallModel.NoCall
- )
+ .stateIn(scope, SharingStarted.Lazily, OngoingCallModel.NoCall)
companion object {
private val TAG = "OngoingCall".pad()
}
-} \ No newline at end of file
+}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/core/NewStatusBarIcons.kt b/packages/SystemUI/src/com/android/systemui/statusbar/core/NewStatusBarIcons.kt
index e3be95373698..402881d438dc 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/core/NewStatusBarIcons.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/core/NewStatusBarIcons.kt
@@ -29,10 +29,10 @@ object NewStatusBarIcons {
val token: FlagToken
get() = FlagToken(FLAG_NAME, isEnabled)
- /** Is the refactor enabled */
+ /** Is the refactor enabled. Dependency on [StatusBarRootModernization] */
@JvmStatic
inline val isEnabled
- get() = Flags.newStatusBarIcons()
+ get() = Flags.newStatusBarIcons() && StatusBarRootModernization.isEnabled
/**
* Called to ensure code is only run when the flag is enabled. This protects users from the
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationTransitionAnimatorController.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationTransitionAnimatorController.kt
index 383227d2b3aa..ab40582afc10 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationTransitionAnimatorController.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationTransitionAnimatorController.kt
@@ -21,6 +21,7 @@ import android.view.ViewGroup
import com.android.internal.jank.InteractionJankMonitor
import com.android.systemui.animation.ActivityTransitionAnimator
import com.android.systemui.animation.TransitionAnimator
+import com.android.systemui.statusbar.notification.collection.GroupEntry
import com.android.systemui.statusbar.notification.domain.interactor.NotificationLaunchAnimationInteractor
import com.android.systemui.statusbar.notification.headsup.HeadsUpManager
import com.android.systemui.statusbar.notification.headsup.HeadsUpUtil
@@ -157,8 +158,8 @@ class NotificationTransitionAnimatorController(
private val headsUpNotificationRow: ExpandableNotificationRow?
get() {
- val summaryEntry = notificationEntry.parent?.summary
-
+ val pipelineParent = notificationEntry.parent
+ val summaryEntry = (pipelineParent as? GroupEntry)?.summary
return when {
headsUpManager.isHeadsUpEntry(notificationKey) -> notification
summaryEntry == null -> null
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationUtils.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationUtils.java
index c6775d6dc051..31bcf2bc819b 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationUtils.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationUtils.java
@@ -26,6 +26,7 @@ import com.android.internal.util.ContrastColorUtil;
import com.android.systemui.res.R;
import com.android.systemui.statusbar.notification.collection.EntryAdapter;
import com.android.systemui.statusbar.notification.collection.ListEntry;
+import com.android.systemui.statusbar.notification.collection.PipelineEntry;
import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow;
import com.android.systemui.util.Compile;
@@ -80,7 +81,7 @@ public class NotificationUtils {
private static final boolean INCLUDE_HASH_CODE_IN_LIST_ENTRY_LOG_KEY = false;
/** Get the notification key, reformatted for logging, for the (optional) entry */
- public static String logKey(ListEntry entry) {
+ public static String logKey(PipelineEntry entry) {
if (entry == null) {
return "null";
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationUtils.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationUtils.kt
index 432bac49fa9b..2c29e30f9660 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationUtils.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationUtils.kt
@@ -17,10 +17,10 @@
package com.android.systemui.statusbar.notification
import android.service.notification.StatusBarNotification
-import com.android.systemui.statusbar.notification.collection.ListEntry
+import com.android.systemui.statusbar.notification.collection.PipelineEntry
/** Get the notification key, reformatted for logging, for the (optional) entry */
-val ListEntry?.logKey: String?
+val PipelineEntry?.logKey: String?
get() = this?.let { NotificationUtils.logKey(it) }
/** Get the notification key, reformatted for logging, for the (optional) sbn */
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/BundleEntry.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/BundleEntry.java
index 0e3f103c152e..35a28288c631 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/BundleEntry.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/BundleEntry.java
@@ -21,29 +21,62 @@ import static android.app.NotificationChannel.PROMOTIONS_ID;
import static android.app.NotificationChannel.RECS_ID;
import static android.app.NotificationChannel.SOCIAL_MEDIA_ID;
+import android.app.Notification;
+import android.content.Context;
+import android.os.Build;
+
import androidx.annotation.Nullable;
import androidx.annotation.VisibleForTesting;
+import com.android.systemui.statusbar.notification.icon.IconPack;
+import com.android.systemui.statusbar.notification.collection.listbuilder.NotifSection;
import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow;
import java.util.List;
/**
- * Abstract class to represent notification section bundled by AI.
+ * Class to represent notifications bundled by classification.
*/
public class BundleEntry extends PipelineEntry {
- private final String mKey;
private final BundleEntryAdapter mEntryAdapter;
// TODO (b/389839319): implement the row
private ExpandableNotificationRow mRow;
public BundleEntry(String key) {
- mKey = key;
+ super(key);
mEntryAdapter = new BundleEntryAdapter();
}
+ @Nullable
+ @Override
+ public NotificationEntry getRepresentativeEntry() {
+ return null;
+ }
+
+ @Nullable
+ @Override
+ public NotifSection getSection() {
+ return null;
+ }
+
+ @Override
+ public int getSectionIndex() {
+ return 0;
+ }
+
+ @Nullable
+ @Override
+ public PipelineEntry getParent() {
+ return null;
+ }
+
+ @Override
+ public boolean wasAttachedInPreviousPass() {
+ return false;
+ }
+
@VisibleForTesting
public BundleEntryAdapter getEntryAdapter() {
return mEntryAdapter;
@@ -79,6 +112,43 @@ public class BundleEntry extends PipelineEntry {
public EntryAdapter getGroupRoot() {
return this;
}
+
+ @Override
+ public boolean isClearable() {
+ // TODO(b/394483200): check whether all of the children are clearable, when implemented
+ return true;
+ }
+
+ @Override
+ public int getTargetSdk() {
+ return Build.VERSION_CODES.CUR_DEVELOPMENT;
+ }
+
+ @Override
+ public String getSummarization() {
+ return null;
+ }
+
+ @Override
+ public int getContrastedColor(Context context, boolean isLowPriority, int backgroundColor) {
+ return Notification.COLOR_DEFAULT;
+ }
+
+ @Override
+ public boolean canPeek() {
+ return false;
+ }
+
+ @Override
+ public long getWhen() {
+ return 0;
+ }
+
+ @Override
+ public IconPack getIcons() {
+ // TODO(b/396446620): implement bundle icons
+ return null;
+ }
}
public static final List<BundleEntry> ROOT_BUNDLES = List.of(
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/EntryAdapter.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/EntryAdapter.java
index 4df81c97e21e..6431cacf2107 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/EntryAdapter.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/EntryAdapter.java
@@ -16,9 +16,12 @@
package com.android.systemui.statusbar.notification.collection;
+import android.content.Context;
+
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
+import com.android.systemui.statusbar.notification.icon.IconPack;
import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow;
/**
@@ -59,9 +62,49 @@ public interface EntryAdapter {
EntryAdapter getGroupRoot();
/**
+ * @return whether the row can be removed with the 'Clear All' action
+ */
+ boolean isClearable();
+
+ /**
* Returns whether the entry is attached to the current shade list
*/
default boolean isAttached() {
return getParent() != null;
}
+
+ /**
+ * Returns the target sdk of the package that owns this entry.
+ */
+ int getTargetSdk();
+
+ /**
+ * Returns the summarization for this entry, if there is one
+ */
+ @Nullable String getSummarization();
+
+ /**
+ * Performs any steps needed to set or reset data before an inflation or reinflation.
+ */
+ default void prepareForInflation() {}
+
+ /**
+ * Gets a color that would have sufficient contrast on the given background color.
+ */
+ int getContrastedColor(Context context, boolean isLowPriority, int backgroundColor);
+
+ /**
+ * Whether this entry can peek on screen as a heads up view
+ */
+ boolean canPeek();
+
+ /**
+ * Returns the visible 'time', in milliseconds, of the entry
+ */
+ long getWhen();
+
+ /**
+ * Retrieves the pack of icons associated with this entry
+ */
+ IconPack getIcons();
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/GroupEntry.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/GroupEntry.java
index 918843cedd2e..8726e83d7ddf 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/GroupEntry.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/GroupEntry.java
@@ -75,6 +75,7 @@ public class GroupEntry extends ListEntry {
return mChildren;
}
+ // TODO(b/394483200) Change ROOT_ENTRY to PipelineEntry
public static final GroupEntry ROOT_ENTRY = new GroupEntry("<root>", 0);
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/ListAttachState.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/ListAttachState.kt
index b5fce4163bb0..4a1b9568c714 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/ListAttachState.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/ListAttachState.kt
@@ -21,14 +21,14 @@ import com.android.systemui.statusbar.notification.collection.listbuilder.plugga
import com.android.systemui.statusbar.notification.collection.listbuilder.pluggable.NotifPromoter
/**
- * Stores the state that [ShadeListBuilder] assigns to this [ListEntry]
+ * Stores the state that [ShadeListBuilder] assigns to this [PipelineEntry]
*/
data class ListAttachState private constructor(
/**
* Null if not attached to the current shade list. If top-level, then the shade list root. If
* part of a group, then that group's GroupEntry.
*/
- var parent: GroupEntry?,
+ var parent: PipelineEntry?,
/**
* The section that this ListEntry was sorted into. If the child of the group, this will be the
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/ListDumper.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/ListDumper.java
index f6a572ec6ce6..60b75b1fd8c8 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/ListDumper.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/ListDumper.java
@@ -39,14 +39,14 @@ public class ListDumper {
* entry to be in its current state (ie: filter, lifeExtender)
*/
public static String dumpTree(
- List<ListEntry> entries,
+ List<PipelineEntry> entries,
NotificationInteractionTracker interactionTracker,
boolean includeRecordKeeping,
String indent) {
StringBuilder sb = new StringBuilder();
final String childEntryIndent = indent + INDENT;
for (int topEntryIndex = 0; topEntryIndex < entries.size(); topEntryIndex++) {
- ListEntry entry = entries.get(topEntryIndex);
+ PipelineEntry entry = entries.get(topEntryIndex);
dumpEntry(entry,
Integer.toString(topEntryIndex),
indent,
@@ -106,7 +106,7 @@ public class ListDumper {
}
private static void dumpEntry(
- ListEntry entry,
+ PipelineEntry entry,
String index,
String indent,
StringBuilder sb,
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/ListEntry.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/ListEntry.java
index c8e3be4e57b4..697d0a06cf9d 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/ListEntry.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/ListEntry.java
@@ -21,28 +21,18 @@ import android.annotation.UptimeMillisLong;
import androidx.annotation.Nullable;
-import com.android.systemui.statusbar.notification.collection.listbuilder.NotifSection;
-
/**
* Abstract superclass for top-level entries, i.e. things that can appear in the final notification
* list shown to users. In practice, this means either GroupEntries or NotificationEntries.
*/
public abstract class ListEntry extends PipelineEntry {
- private final String mKey;
private final long mCreationTime;
- private final ListAttachState mPreviousAttachState = ListAttachState.create();
- private final ListAttachState mAttachState = ListAttachState.create();
-
protected ListEntry(String key, long creationTime) {
- mKey = key;
+ super(key);
mCreationTime = creationTime;
}
- public String getKey() {
- return mKey;
- }
-
/**
* The SystemClock.uptimeMillis() when this object was created. In general, this means the
* moment when NotificationManager notifies our listener about the existence of this entry.
@@ -64,34 +54,22 @@ public abstract class ListEntry extends PipelineEntry {
*/
public abstract @Nullable NotificationEntry getRepresentativeEntry();
- @Nullable public GroupEntry getParent() {
+ @Nullable public PipelineEntry getParent() {
return mAttachState.getParent();
}
- void setParent(@Nullable GroupEntry parent) {
+ void setParent(@Nullable PipelineEntry parent) {
mAttachState.setParent(parent);
}
- @Nullable public GroupEntry getPreviousParent() {
+ @Nullable public PipelineEntry getPreviousParent() {
return mPreviousAttachState.getParent();
}
- @Nullable public NotifSection getSection() {
- return mAttachState.getSection();
- }
-
public int getSectionIndex() {
return mAttachState.getSection() != null ? mAttachState.getSection().getIndex() : -1;
}
- ListAttachState getAttachState() {
- return mAttachState;
- }
-
- ListAttachState getPreviousAttachState() {
- return mPreviousAttachState;
- }
-
/**
* Stores the current attach state into {@link #getPreviousAttachState()}} and then starts a
* fresh attach state (all entries will be null/default-initialized).
@@ -100,11 +78,4 @@ public abstract class ListEntry extends PipelineEntry {
mPreviousAttachState.clone(mAttachState);
mAttachState.reset();
}
-
- /**
- * True if this entry was attached in the last pass, else false.
- */
- public boolean wasAttachedInPreviousPass() {
- return getPreviousAttachState().getParent() != null;
- }
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/NotificationEntry.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/NotificationEntry.java
index 90f9525c7683..e5b72d459069 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/NotificationEntry.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/NotificationEntry.java
@@ -78,6 +78,7 @@ import com.android.systemui.statusbar.notification.row.NotificationGuts;
import com.android.systemui.statusbar.notification.row.shared.HeadsUpStatusBarModel;
import com.android.systemui.statusbar.notification.row.shared.NotificationContentModel;
import com.android.systemui.statusbar.notification.row.shared.NotificationRowContentBinderRefactor;
+import com.android.systemui.statusbar.notification.shared.NotificationBundleUi;
import com.android.systemui.statusbar.notification.stack.PriorityBucket;
import com.android.systemui.util.ListenerSet;
@@ -304,11 +305,54 @@ public final class NotificationEntry extends ListEntry {
if (isTopLevelEntry() || getParent() == null) {
return null;
}
- if (NotificationEntry.this.getParent().getSummary() != null) {
- return NotificationEntry.this.getParent().getSummary().mEntryAdapter;
+ if (NotificationEntry.this.getParent() instanceof GroupEntry parentGroupEntry) {
+ if (parentGroupEntry.getSummary() != null) {
+ return parentGroupEntry.getSummary().mEntryAdapter;
+ }
}
return null;
}
+
+ @Override
+ public boolean isClearable() {
+ return NotificationEntry.this.isClearable();
+ }
+
+ @Override
+ public int getTargetSdk() {
+ return NotificationEntry.this.targetSdk;
+ }
+
+ @Override
+ public String getSummarization() {
+ return getRanking().getSummarization();
+ }
+
+ @Override
+ public void prepareForInflation() {
+ getSbn().clearPackageContext();
+ }
+
+ @Override
+ public int getContrastedColor(Context context, boolean isLowPriority, int backgroundColor) {
+ return NotificationEntry.this.getContrastedColor(
+ context, isLowPriority, backgroundColor);
+ }
+
+ @Override
+ public boolean canPeek() {
+ return isStickyAndNotDemoted();
+ }
+
+ @Override
+ public long getWhen() {
+ return getSbn().getNotification().getWhen();
+ }
+
+ @Override
+ public IconPack getIcons() {
+ return NotificationEntry.this.getIcons();
+ }
}
public EntryAdapter getEntryAdapter() {
@@ -546,7 +590,7 @@ public final class NotificationEntry extends ListEntry {
* Get the children that are actually attached to this notification's row.
*
* TODO: Seems like most callers here should probably be using
- * {@link GroupMembershipManager#getChildren(ListEntry)}
+ * {@link GroupMembershipManager#getChildren(PipelineEntry)}
*/
public @Nullable List<NotificationEntry> getAttachedNotifChildren() {
if (row == null) {
@@ -580,6 +624,7 @@ public final class NotificationEntry extends ListEntry {
}
public boolean hasFinishedInitialization() {
+ NotificationBundleUi.assertInLegacyMode();
return initializationTime != -1
&& SystemClock.elapsedRealtime() > initializationTime + INITIALIZATION_DELAY;
}
@@ -663,10 +708,12 @@ public final class NotificationEntry extends ListEntry {
}
public void resetInitializationTime() {
+ NotificationBundleUi.assertInLegacyMode();
initializationTime = -1;
}
public void setInitializationTime(long time) {
+ NotificationBundleUi.assertInLegacyMode();
if (initializationTime == -1) {
initializationTime = time;
}
@@ -683,9 +730,13 @@ public final class NotificationEntry extends ListEntry {
* @return {@code true} if we are a media notification
*/
public boolean isMediaNotification() {
- if (row == null) return false;
+ if (NotificationBundleUi.isEnabled()) {
+ return getSbn().getNotification().isMediaNotification();
+ } else {
+ if (row == null) return false;
- return row.isMediaRow();
+ return row.isMediaRow();
+ }
}
public boolean containsCustomViews() {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/PipelineEntry.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/PipelineEntry.java
index c5a479180329..78652ccda1d1 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/PipelineEntry.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/PipelineEntry.java
@@ -16,8 +16,74 @@
package com.android.systemui.statusbar.notification.collection;
+import androidx.annotation.NonNull;
+import androidx.annotation.Nullable;
+
+import com.android.systemui.statusbar.notification.collection.listbuilder.NotifSection;
+
/**
* Class to represent a notification, group, or bundle in the pipeline.
*/
public abstract class PipelineEntry {
+
+ final String mKey;
+ final ListAttachState mAttachState = ListAttachState.create();
+ final ListAttachState mPreviousAttachState = ListAttachState.create();
+
+ public PipelineEntry(String key) {
+ this.mKey = key;
+ }
+
+ /**
+ * Key of the representative entry.
+ */
+ public @NonNull String getKey() {
+ return mKey;
+ }
+
+ /**
+ * @return The representative NotificationEntry:
+ * for NotificationEntry, return itself
+ * for GroupEntry, return the summary NotificationEntry, or null if it does not exist
+ * for BundleEntry, return null
+ */
+ public abstract @Nullable NotificationEntry getRepresentativeEntry();
+
+ /**
+ * @return NotifSection that ShadeListBuilder assigned to this PipelineEntry.
+ */
+ @Nullable public NotifSection getSection() {
+ return mAttachState.getSection();
+ }
+
+ /**
+ * @return True if this entry was attached in the last pass, else false.
+ */
+ public boolean wasAttachedInPreviousPass() {
+ return getPreviousAttachState().getParent() != null;
+ }
+
+ /**
+ * @return Index of section assigned to this entry.
+ */
+ public abstract int getSectionIndex();
+
+ /**
+ * @return Parent PipelineEntry
+ */
+ public abstract @Nullable PipelineEntry getParent();
+
+ /**
+ * @return Current state that ShadeListBuilder assigned to this PipelineEntry.
+ */
+ final ListAttachState getAttachState() {
+ return mAttachState;
+ }
+
+ /**
+ * @return Previous state that ShadeListBuilder assigned to this PipelineEntry.
+ */
+ final ListAttachState getPreviousAttachState() {
+ return mPreviousAttachState;
+ }
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/ShadeListBuilder.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/ShadeListBuilder.java
index 9c1d0735a65b..bb84ab8f421a 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/ShadeListBuilder.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/ShadeListBuilder.java
@@ -67,6 +67,7 @@ import com.android.systemui.statusbar.notification.collection.listbuilder.plugga
import com.android.systemui.statusbar.notification.collection.listbuilder.pluggable.NotifStabilityManager;
import com.android.systemui.statusbar.notification.collection.listbuilder.pluggable.Pluggable;
import com.android.systemui.statusbar.notification.collection.notifcollection.CollectionReadyForBuildListener;
+import com.android.systemui.statusbar.notification.shared.NotificationBundleUi;
import com.android.systemui.statusbar.notification.stack.NotificationPriorityBucketKt;
import com.android.systemui.util.Assert;
import com.android.systemui.util.NamedListenerSet;
@@ -99,15 +100,15 @@ public class ShadeListBuilder implements Dumpable, PipelineDumpable {
private final DumpManager mDumpManager;
// used exclusivly by ShadeListBuilder#notifySectionEntriesUpdated
// TODO replace temp with collection pool for readability
- private final ArrayList<ListEntry> mTempSectionMembers = new ArrayList<>();
+ private final ArrayList<PipelineEntry> mTempSectionMembers = new ArrayList<>();
private NotifPipelineFlags mFlags;
private final boolean mAlwaysLogList;
- private List<ListEntry> mNotifList = new ArrayList<>();
- private List<ListEntry> mNewNotifList = new ArrayList<>();
+ private List<PipelineEntry> mNotifList = new ArrayList<>();
+ private List<PipelineEntry> mNewNotifList = new ArrayList<>();
private final SemiStableSort mSemiStableSort = new SemiStableSort();
- private final StableOrder<ListEntry> mStableOrder = this::getStableOrderRank;
+ private final StableOrder<PipelineEntry> mStableOrder = this::getStableOrderRank;
private final PipelineState mPipelineState = new PipelineState();
private final Map<String, GroupEntry> mGroups = new ArrayMap<>();
private Collection<NotificationEntry> mAllEntries = Collections.emptyList();
@@ -132,8 +133,8 @@ public class ShadeListBuilder implements Dumpable, PipelineDumpable {
mOnBeforeRenderListListeners = new NamedListenerSet<>();
@Nullable private OnRenderListListener mOnRenderListListener;
- private List<ListEntry> mReadOnlyNotifList = Collections.unmodifiableList(mNotifList);
- private List<ListEntry> mReadOnlyNewNotifList = Collections.unmodifiableList(mNewNotifList);
+ private List<PipelineEntry> mReadOnlyNotifList = Collections.unmodifiableList(mNotifList);
+ private List<PipelineEntry> mReadOnlyNewNotifList = Collections.unmodifiableList(mNewNotifList);
private final NotifPipelineChoreographer mChoreographer;
private int mConsecutiveReentrantRebuilds = 0;
@@ -307,7 +308,7 @@ public class ShadeListBuilder implements Dumpable, PipelineDumpable {
}
}
- List<ListEntry> getShadeList() {
+ List<PipelineEntry> getShadeList() {
Assert.isMainThread();
// NOTE: Accessing this method when the pipeline is running is generally going to provide
// incorrect results, and indicates a poorly behaved component of the pipeline.
@@ -492,7 +493,7 @@ public class ShadeListBuilder implements Dumpable, PipelineDumpable {
Trace.beginSection("ShadeListBuilder.notifySectionEntriesUpdated");
mTempSectionMembers.clear();
for (NotifSection section : mNotifSections) {
- for (ListEntry entry : mNotifList) {
+ for (PipelineEntry entry : mNotifList) {
if (section == entry.getSection()) {
mTempSectionMembers.add(entry);
}
@@ -513,11 +514,11 @@ public class ShadeListBuilder implements Dumpable, PipelineDumpable {
*/
private void applyNewNotifList() {
mNotifList.clear();
- List<ListEntry> emptyList = mNotifList;
+ List<PipelineEntry> emptyList = mNotifList;
mNotifList = mNewNotifList;
mNewNotifList = emptyList;
- List<ListEntry> readOnlyNotifList = mReadOnlyNotifList;
+ List<PipelineEntry> readOnlyNotifList = mReadOnlyNotifList;
mReadOnlyNotifList = mReadOnlyNewNotifList;
mReadOnlyNewNotifList = readOnlyNotifList;
}
@@ -537,12 +538,12 @@ public class ShadeListBuilder implements Dumpable, PipelineDumpable {
}
private void filterNotifs(
- Collection<? extends ListEntry> entries,
- List<ListEntry> out,
+ Collection<? extends PipelineEntry> entries,
+ List<PipelineEntry> out,
List<NotifFilter> filters) {
Trace.beginSection("ShadeListBuilder.filterNotifs");
final long now = mSystemClock.uptimeMillis();
- for (ListEntry entry : entries) {
+ for (PipelineEntry entry : entries) {
if (entry instanceof GroupEntry) {
final GroupEntry groupEntry = (GroupEntry) entry;
@@ -575,11 +576,11 @@ public class ShadeListBuilder implements Dumpable, PipelineDumpable {
Trace.endSection();
}
- private void groupNotifs(List<ListEntry> entries, List<ListEntry> out) {
+ private void groupNotifs(List<PipelineEntry> entries, List<PipelineEntry> out) {
Trace.beginSection("ShadeListBuilder.groupNotifs");
- for (ListEntry listEntry : entries) {
+ for (PipelineEntry PipelineEntry : entries) {
// since grouping hasn't happened yet, all notifs are NotificationEntries
- NotificationEntry entry = (NotificationEntry) listEntry;
+ NotificationEntry entry = (NotificationEntry) PipelineEntry;
if (entry.getSbn().isGroup()) {
final String topLevelKey = entry.getSbn().getGroupKey();
@@ -630,14 +631,14 @@ public class ShadeListBuilder implements Dumpable, PipelineDumpable {
Trace.endSection();
}
- private void stabilizeGroupingNotifs(List<ListEntry> topLevelList) {
+ private void stabilizeGroupingNotifs(List<PipelineEntry> topLevelList) {
if (getStabilityManager().isEveryChangeAllowed()) {
return;
}
Trace.beginSection("ShadeListBuilder.stabilizeGroupingNotifs");
for (int i = 0; i < topLevelList.size(); i++) {
- final ListEntry tle = topLevelList.get(i);
+ final PipelineEntry tle = topLevelList.get(i);
if (tle instanceof GroupEntry) {
// maybe put children back into their old group (including moving back to top-level)
GroupEntry groupEntry = (GroupEntry) tle;
@@ -666,13 +667,13 @@ public class ShadeListBuilder implements Dumpable, PipelineDumpable {
/**
* Returns true if the group change was suppressed, else false
*/
- private boolean maybeSuppressGroupChange(NotificationEntry entry, List<ListEntry> out) {
- final GroupEntry prevParent = entry.getPreviousAttachState().getParent();
+ private boolean maybeSuppressGroupChange(NotificationEntry entry, List<PipelineEntry> out) {
+ final PipelineEntry prevParent = entry.getPreviousAttachState().getParent();
if (prevParent == null) {
// New entries are always allowed.
return false;
}
- final GroupEntry assignedParent = entry.getParent();
+ final PipelineEntry assignedParent = entry.getParent();
if (prevParent == assignedParent) {
// Nothing to change.
return false;
@@ -690,21 +691,22 @@ public class ShadeListBuilder implements Dumpable, PipelineDumpable {
entry.setParent(prevParent);
if (prevParent == ROOT_ENTRY) {
out.add(entry);
- } else {
- prevParent.addChild(entry);
+ } else if (prevParent instanceof GroupEntry) {
+ ((GroupEntry) prevParent).addChild(entry);
if (!mGroups.containsKey(prevParent.getKey())) {
- mGroups.put(prevParent.getKey(), prevParent);
+ mGroups.put(prevParent.getKey(), (GroupEntry) prevParent);
}
}
+ // TODO(b/394483200): Revisit group stability for BundleEntry
return true;
}
return false;
}
- private void promoteNotifs(List<ListEntry> list) {
+ private void promoteNotifs(List<PipelineEntry> list) {
Trace.beginSection("ShadeListBuilder.promoteNotifs");
for (int i = 0; i < list.size(); i++) {
- final ListEntry tle = list.get(i);
+ final PipelineEntry tle = list.get(i);
if (tle instanceof GroupEntry) {
final GroupEntry group = (GroupEntry) tle;
@@ -724,7 +726,7 @@ public class ShadeListBuilder implements Dumpable, PipelineDumpable {
Trace.endSection();
}
- private void pruneIncompleteGroups(List<ListEntry> shadeList) {
+ private void pruneIncompleteGroups(List<PipelineEntry> shadeList) {
Trace.beginSection("ShadeListBuilder.pruneIncompleteGroups");
// Any group which lost a child on this run to stability is exempt from being pruned or
// having its summary promoted, regardless of how many children it has
@@ -741,7 +743,7 @@ public class ShadeListBuilder implements Dumpable, PipelineDumpable {
// Iterate backwards, so that we can remove elements without affecting indices of
// yet-to-be-accessed entries.
for (int i = shadeList.size() - 1; i >= 0; i--) {
- final ListEntry tle = shadeList.get(i);
+ final PipelineEntry tle = shadeList.get(i);
if (tle instanceof GroupEntry) {
final GroupEntry group = (GroupEntry) tle;
@@ -792,7 +794,7 @@ public class ShadeListBuilder implements Dumpable, PipelineDumpable {
Trace.endSection();
}
- private void pruneGroupAtIndexAndPromoteSummary(List<ListEntry> shadeList,
+ private void pruneGroupAtIndexAndPromoteSummary(List<PipelineEntry> shadeList,
GroupEntry group, int index) {
// Validate that the group has no children
checkArgument(group.getChildren().isEmpty(), "group should have no children");
@@ -800,7 +802,7 @@ public class ShadeListBuilder implements Dumpable, PipelineDumpable {
NotificationEntry summary = group.getSummary();
summary.setParent(ROOT_ENTRY);
// The list may be sorted; replace the group with the summary, in its place
- ListEntry oldEntry = shadeList.set(index, summary);
+ PipelineEntry oldEntry = shadeList.set(index, summary);
// Validate that the replaced entry was the group entry
checkState(oldEntry == group);
@@ -811,10 +813,10 @@ public class ShadeListBuilder implements Dumpable, PipelineDumpable {
"SUMMARY with no children @ " + mPipelineState.getStateName());
}
- private void pruneGroupAtIndexAndPromoteAnyChildren(List<ListEntry> shadeList,
+ private void pruneGroupAtIndexAndPromoteAnyChildren(List<PipelineEntry> shadeList,
GroupEntry group, int index) {
// REMOVE the GroupEntry at this index
- ListEntry oldEntry = shadeList.remove(index);
+ PipelineEntry oldEntry = shadeList.remove(index);
// Validate that the replaced entry was the group entry
checkState(oldEntry == group);
@@ -867,14 +869,14 @@ public class ShadeListBuilder implements Dumpable, PipelineDumpable {
* top level (ungrouped) notifications.
*/
@NonNull
- private Set<String> getGroupsWithChildrenLostToStability(List<ListEntry> shadeList) {
+ private Set<String> getGroupsWithChildrenLostToStability(List<PipelineEntry> shadeList) {
if (getStabilityManager().isEveryChangeAllowed()) {
return Collections.emptySet();
}
ArraySet<String> groupsWithChildrenLostToStability = new ArraySet<>();
for (int i = 0; i < shadeList.size(); i++) {
- final ListEntry tle = shadeList.get(i);
- final GroupEntry suppressedParent =
+ final PipelineEntry tle = shadeList.get(i);
+ final PipelineEntry suppressedParent =
tle.getAttachState().getSuppressedChanges().getParent();
if (suppressedParent != null) {
// This top-level-entry was supposed to be attached to this group,
@@ -891,9 +893,10 @@ public class ShadeListBuilder implements Dumpable, PipelineDumpable {
*
* These groups will be exempt from appearing without any children.
*/
- private void addGroupsWithChildrenLostToPromotion(List<ListEntry> shadeList, Set<String> out) {
+ private void addGroupsWithChildrenLostToPromotion(List<PipelineEntry> shadeList,
+ Set<String> out) {
for (int i = 0; i < shadeList.size(); i++) {
- final ListEntry tle = shadeList.get(i);
+ final PipelineEntry tle = shadeList.get(i);
if (tle.getAttachState().getPromoter() != null) {
// This top-level-entry was part of a group, but was promoted out of it.
final String groupKey = tle.getRepresentativeEntry().getSbn().getGroupKey();
@@ -909,7 +912,7 @@ public class ShadeListBuilder implements Dumpable, PipelineDumpable {
* These groups will be exempt from appearing without any children.
*/
private void addGroupsWithChildrenLostToFiltering(Set<String> out) {
- for (ListEntry tle : mAllEntries) {
+ for (PipelineEntry tle : mAllEntries) {
StatusBarNotification sbn = tle.getRepresentativeEntry().getSbn();
if (sbn.isGroup()
&& !sbn.getNotification().isGroupSummary()
@@ -920,14 +923,14 @@ public class ShadeListBuilder implements Dumpable, PipelineDumpable {
}
/**
- * If a ListEntry was added to the shade list and then later removed (e.g. because it was a
+ * If a PipelineEntry was added to the shade list and then later removed (e.g. because it was a
* group that was broken up), this method will erase any bookkeeping traces of that addition
* and/or check that they were already erased.
*
* Before calling this method, the entry must already have been removed from its parent. If
* it's a group, its summary must be null and its children must be empty.
*/
- private void annulAddition(ListEntry entry, List<ListEntry> shadeList) {
+ private void annulAddition(PipelineEntry entry, List<PipelineEntry> shadeList) {
// This function does very little, but if any of its assumptions are violated (and it has a
// lot of them), it will put the system into an inconsistent state. So we check all of them
@@ -955,9 +958,11 @@ public class ShadeListBuilder implements Dumpable, PipelineDumpable {
throw new IllegalStateException(
"Cannot nullify group " + ge.getKey() + ": still has children");
}
- } else if (entry instanceof NotificationEntry) {
- if (entry == entry.getParent().getSummary()
- || entry.getParent().getChildren().contains(entry)) {
+ } else if (entry instanceof NotificationEntry
+ && entry.getParent() instanceof GroupEntry) {
+ GroupEntry parentGroupEntry = (GroupEntry) entry.getParent();
+ if (entry == parentGroupEntry.getSummary()
+ || parentGroupEntry.getChildren().contains(entry)) {
throw new IllegalStateException("Cannot nullify addition of child "
+ entry.getKey() + ": it's still attached to its parent.");
}
@@ -972,14 +977,14 @@ public class ShadeListBuilder implements Dumpable, PipelineDumpable {
* This can happen if the entry is removed from a group that was broken up or if the entry was
* filtered out during any of the filtering steps.
*/
- private void annulAddition(ListEntry entry) {
+ private void annulAddition(PipelineEntry entry) {
entry.getAttachState().detach();
}
private void assignSections() {
Trace.beginSection("ShadeListBuilder.assignSections");
// Assign sections to top-level elements and their children
- for (ListEntry entry : mNotifList) {
+ for (PipelineEntry entry : mNotifList) {
NotifSection section = applySections(entry);
if (entry instanceof GroupEntry) {
GroupEntry parent = (GroupEntry) entry;
@@ -1000,7 +1005,7 @@ public class ShadeListBuilder implements Dumpable, PipelineDumpable {
private void sortWithSemiStableSort() {
// Sort each group's children
boolean allSorted = true;
- for (ListEntry entry : mNotifList) {
+ for (PipelineEntry entry : mNotifList) {
if (entry instanceof GroupEntry) {
GroupEntry parent = (GroupEntry) entry;
allSorted &= sortGroupChildren(parent.getRawChildren());
@@ -1009,7 +1014,7 @@ public class ShadeListBuilder implements Dumpable, PipelineDumpable {
// Sort each section within the top level list
mNotifList.sort(mTopLevelComparator);
if (!getStabilityManager().isEveryChangeAllowed()) {
- for (List<ListEntry> subList : getSectionSubLists(mNotifList)) {
+ for (List<PipelineEntry> subList : getSectionSubLists(mNotifList)) {
allSorted &= mSemiStableSort.stabilizeTo(subList, mStableOrder, mNewNotifList);
}
applyNewNotifList();
@@ -1021,7 +1026,7 @@ public class ShadeListBuilder implements Dumpable, PipelineDumpable {
}
}
- private Iterable<List<ListEntry>> getSectionSubLists(List<ListEntry> entries) {
+ private Iterable<List<PipelineEntry>> getSectionSubLists(List<PipelineEntry> entries) {
return ShadeListBuilderHelper.INSTANCE.getSectionSubLists(entries);
}
@@ -1056,12 +1061,12 @@ public class ShadeListBuilder implements Dumpable, PipelineDumpable {
/**
* Assign the index of each notification relative to the total order
*/
- private void assignIndexes(List<ListEntry> notifList) {
+ private void assignIndexes(List<PipelineEntry> notifList) {
if (notifList.size() == 0) return;
NotifSection currentSection = requireNonNull(notifList.get(0).getSection());
int sectionMemberIndex = 0;
for (int i = 0; i < notifList.size(); i++) {
- final ListEntry entry = notifList.get(i);
+ final PipelineEntry entry = notifList.get(i);
NotifSection section = requireNonNull(entry.getSection());
if (section.getIndex() != currentSection.getIndex()) {
sectionMemberIndex = 0;
@@ -1098,7 +1103,7 @@ public class ShadeListBuilder implements Dumpable, PipelineDumpable {
Trace.endSection();
}
- private void logAttachStateChanges(ListEntry entry) {
+ private void logAttachStateChanges(PipelineEntry entry) {
final ListAttachState curr = entry.getAttachState();
final ListAttachState prev = entry.getPreviousAttachState();
@@ -1114,8 +1119,8 @@ public class ShadeListBuilder implements Dumpable, PipelineDumpable {
mLogger.logParentChanged(mIterationCount, prev.getParent(), curr.getParent());
}
- GroupEntry currSuppressedParent = curr.getSuppressedChanges().getParent();
- GroupEntry prevSuppressedParent = prev.getSuppressedChanges().getParent();
+ PipelineEntry currSuppressedParent = curr.getSuppressedChanges().getParent();
+ PipelineEntry prevSuppressedParent = prev.getSuppressedChanges().getParent();
if (currSuppressedParent != null && (prevSuppressedParent == null
|| !prevSuppressedParent.getKey().equals(currSuppressedParent.getKey()))) {
mLogger.logParentChangeSuppressedStarted(
@@ -1209,7 +1214,7 @@ public class ShadeListBuilder implements Dumpable, PipelineDumpable {
@Nullable
private NotifComparator getSectionComparator(
- @NonNull ListEntry o1, @NonNull ListEntry o2) {
+ @NonNull PipelineEntry o1, @NonNull PipelineEntry o2) {
final NotifSection section = o1.getSection();
if (section != o2.getSection()) {
throw new RuntimeException("Entry ordering should only be done within sections");
@@ -1220,7 +1225,7 @@ public class ShadeListBuilder implements Dumpable, PipelineDumpable {
return null;
}
- private final Comparator<ListEntry> mTopLevelComparator = (o1, o2) -> {
+ private final Comparator<PipelineEntry> mTopLevelComparator = (o1, o2) -> {
int cmp = Integer.compare(
o1.getSectionIndex(),
o2.getSectionIndex());
@@ -1263,7 +1268,7 @@ public class ShadeListBuilder implements Dumpable, PipelineDumpable {
};
@Nullable
- private Integer getStableOrderRank(ListEntry entry) {
+ private Integer getStableOrderRank(PipelineEntry entry) {
if (getStabilityManager().isEntryReorderingAllowed(entry)) {
// let the stability manager constrain or allow reordering
return null;
@@ -1282,7 +1287,13 @@ public class ShadeListBuilder implements Dumpable, PipelineDumpable {
entry.getAttachState().setExcludingFilter(filter);
if (filter != null) {
// notification is removed from the list, so we reset its initialization time
- entry.resetInitializationTime();
+ if (NotificationBundleUi.isEnabled()) {
+ if (entry.getRow() != null) {
+ entry.getRow().resetInitializationTime();
+ }
+ } else {
+ entry.resetInitializationTime();
+ }
}
return filter != null;
}
@@ -1316,7 +1327,7 @@ public class ShadeListBuilder implements Dumpable, PipelineDumpable {
return null;
}
- private NotifSection applySections(ListEntry entry) {
+ private NotifSection applySections(PipelineEntry entry) {
final NotifSection newSection = findSection(entry);
final ListAttachState prevAttachState = entry.getPreviousAttachState();
@@ -1339,7 +1350,7 @@ public class ShadeListBuilder implements Dumpable, PipelineDumpable {
return finalSection;
}
- private void setEntrySection(ListEntry entry, NotifSection finalSection) {
+ private void setEntrySection(PipelineEntry entry, NotifSection finalSection) {
entry.getAttachState().setSection(finalSection);
NotificationEntry representativeEntry = entry.getRepresentativeEntry();
if (representativeEntry != null) {
@@ -1351,7 +1362,7 @@ public class ShadeListBuilder implements Dumpable, PipelineDumpable {
}
@NonNull
- private NotifSection findSection(ListEntry entry) {
+ private NotifSection findSection(PipelineEntry entry) {
for (int i = 0; i < mNotifSections.size(); i++) {
NotifSection section = mNotifSections.get(i);
if (section.getSectioner().isInSection(entry)) {
@@ -1421,10 +1432,10 @@ public class ShadeListBuilder implements Dumpable, PipelineDumpable {
mChoreographer.schedule();
}
- private static int countChildren(List<ListEntry> entries) {
+ private static int countChildren(List<PipelineEntry> entries) {
int count = 0;
for (int i = 0; i < entries.size(); i++) {
- final ListEntry entry = entries.get(i);
+ final PipelineEntry entry = entries.get(i);
if (entry instanceof GroupEntry) {
count += ((GroupEntry) entry).getChildren().size();
}
@@ -1432,7 +1443,7 @@ public class ShadeListBuilder implements Dumpable, PipelineDumpable {
return count;
}
- private void dispatchOnBeforeTransformGroups(List<ListEntry> entries) {
+ private void dispatchOnBeforeTransformGroups(List<PipelineEntry> entries) {
Trace.beginSection("ShadeListBuilder.dispatchOnBeforeTransformGroups");
mOnBeforeTransformGroupsListeners.forEachTraced(listener -> {
listener.onBeforeTransformGroups(entries);
@@ -1440,7 +1451,7 @@ public class ShadeListBuilder implements Dumpable, PipelineDumpable {
Trace.endSection();
}
- private void dispatchOnBeforeSort(List<ListEntry> entries) {
+ private void dispatchOnBeforeSort(List<PipelineEntry> entries) {
Trace.beginSection("ShadeListBuilder.dispatchOnBeforeSort");
mOnBeforeSortListeners.forEachTraced(listener -> {
listener.onBeforeSort(entries);
@@ -1448,7 +1459,7 @@ public class ShadeListBuilder implements Dumpable, PipelineDumpable {
Trace.endSection();
}
- private void dispatchOnBeforeFinalizeFilter(List<ListEntry> entries) {
+ private void dispatchOnBeforeFinalizeFilter(List<PipelineEntry> entries) {
Trace.beginSection("ShadeListBuilder.dispatchOnBeforeFinalizeFilter");
mOnBeforeFinalizeFilterListeners.forEachTraced(listener -> {
listener.onBeforeFinalizeFilter(entries);
@@ -1456,7 +1467,7 @@ public class ShadeListBuilder implements Dumpable, PipelineDumpable {
Trace.endSection();
}
- private void dispatchOnBeforeRenderList(List<ListEntry> entries) {
+ private void dispatchOnBeforeRenderList(List<PipelineEntry> entries) {
Trace.beginSection("ShadeListBuilder.dispatchOnBeforeRenderList");
mOnBeforeRenderListListeners.forEachTraced(listener -> {
listener.onBeforeRenderList(entries);
@@ -1501,13 +1512,13 @@ public class ShadeListBuilder implements Dumpable, PipelineDumpable {
* @param entries A read-only view into the current notif list. Note that this list is
* backed by the live list and will change in response to new pipeline runs.
*/
- void onRenderList(@NonNull List<ListEntry> entries);
+ void onRenderList(@NonNull List<PipelineEntry> entries);
}
private static final NotifSectioner DEFAULT_SECTIONER = new NotifSectioner("UnknownSection",
NotificationPriorityBucketKt.BUCKET_UNKNOWN) {
@Override
- public boolean isInSection(ListEntry entry) {
+ public boolean isInSection(PipelineEntry entry) {
return true;
}
};
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/SuppressedAttachState.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/SuppressedAttachState.kt
index 584563b6c388..c9429547a518 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/SuppressedAttachState.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/SuppressedAttachState.kt
@@ -19,7 +19,7 @@ package com.android.systemui.statusbar.notification.collection
import com.android.systemui.statusbar.notification.collection.listbuilder.NotifSection
/**
- * Stores the suppressed state that [ShadeListBuilder] assigned to this [ListEntry] before the
+ * Stores the suppressed state that [ShadeListBuilder] assigned to this [PipelineEntry] before the
* VisualStabilityManager suppressed group and section changes.
*/
data class SuppressedAttachState private constructor(
@@ -35,7 +35,7 @@ data class SuppressedAttachState private constructor(
* - Root if suppressing group change to top-level
* - GroupEntry if suppressing group change to a different group
*/
- var parent: GroupEntry?,
+ var parent: PipelineEntry?,
/**
* Whether the ListEntry would have been pruned had its group change not been suppressed.
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/BundleCoordinator.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/BundleCoordinator.kt
index 244c5946c21e..e6d5f4120a20 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/BundleCoordinator.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/BundleCoordinator.kt
@@ -20,7 +20,7 @@ import android.app.NotificationChannel.NEWS_ID
import android.app.NotificationChannel.PROMOTIONS_ID
import android.app.NotificationChannel.RECS_ID
import android.app.NotificationChannel.SOCIAL_MEDIA_ID
-import com.android.systemui.statusbar.notification.collection.ListEntry
+import com.android.systemui.statusbar.notification.collection.PipelineEntry
import com.android.systemui.statusbar.notification.collection.NotifPipeline
import com.android.systemui.statusbar.notification.collection.coordinator.dagger.CoordinatorScope
import com.android.systemui.statusbar.notification.collection.listbuilder.pluggable.NotifSectioner
@@ -48,7 +48,7 @@ class BundleCoordinator @Inject constructor(
val newsSectioner =
object : NotifSectioner("News", BUCKET_NEWS) {
- override fun isInSection(entry: ListEntry): Boolean {
+ override fun isInSection(entry: PipelineEntry): Boolean {
return entry.representativeEntry?.channel?.id == NEWS_ID
}
@@ -59,7 +59,7 @@ class BundleCoordinator @Inject constructor(
val socialSectioner =
object : NotifSectioner("Social", BUCKET_SOCIAL) {
- override fun isInSection(entry: ListEntry): Boolean {
+ override fun isInSection(entry: PipelineEntry): Boolean {
return entry.representativeEntry?.channel?.id == SOCIAL_MEDIA_ID
}
@@ -70,7 +70,7 @@ class BundleCoordinator @Inject constructor(
val recsSectioner =
object : NotifSectioner("Recommendations", BUCKET_RECS) {
- override fun isInSection(entry: ListEntry): Boolean {
+ override fun isInSection(entry: PipelineEntry): Boolean {
return entry.representativeEntry?.channel?.id == RECS_ID
}
@@ -81,7 +81,7 @@ class BundleCoordinator @Inject constructor(
val promoSectioner =
object : NotifSectioner("Promotions", BUCKET_PROMO) {
- override fun isInSection(entry: ListEntry): Boolean {
+ override fun isInSection(entry: PipelineEntry): Boolean {
return entry.representativeEntry?.channel?.id == PROMOTIONS_ID
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/ColorizedFgsCoordinator.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/ColorizedFgsCoordinator.java
index 9df4bf4af4e8..afba85b49c30 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/ColorizedFgsCoordinator.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/ColorizedFgsCoordinator.java
@@ -25,6 +25,7 @@ import androidx.annotation.Nullable;
import com.android.systemui.dagger.qualifiers.Application;
import com.android.systemui.statusbar.notification.collection.ListEntry;
+import com.android.systemui.statusbar.notification.collection.PipelineEntry;
import com.android.systemui.statusbar.notification.collection.NotifPipeline;
import com.android.systemui.statusbar.notification.collection.NotificationEntry;
import com.android.systemui.statusbar.notification.collection.coordinator.dagger.CoordinatorScope;
@@ -96,7 +97,7 @@ public class ColorizedFgsCoordinator implements Coordinator {
private final NotifSectioner mNotifSectioner = new NotifSectioner("ColorizedSectioner",
NotificationPriorityBucketKt.BUCKET_FOREGROUND_SERVICE) {
@Override
- public boolean isInSection(ListEntry entry) {
+ public boolean isInSection(PipelineEntry entry) {
NotificationEntry notificationEntry = entry.getRepresentativeEntry();
if (notificationEntry != null) {
return isRichOngoing(notificationEntry);
@@ -117,7 +118,7 @@ public class ColorizedFgsCoordinator implements Coordinator {
private final NotifComparator mOngoingComparator = new NotifComparator(
"OngoingComparator") {
@Override
- public int compare(@NonNull ListEntry o1, @NonNull ListEntry o2) {
+ public int compare(@NonNull PipelineEntry o1, @NonNull PipelineEntry o2) {
return Integer.compare(
getSortKey(o1.getRepresentativeEntry()),
getSortKey(o2.getRepresentativeEntry())
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/ConversationCoordinator.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/ConversationCoordinator.kt
index af2c1979ff77..248b5286803f 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/ConversationCoordinator.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/ConversationCoordinator.kt
@@ -16,7 +16,8 @@
package com.android.systemui.statusbar.notification.collection.coordinator
-import com.android.systemui.statusbar.notification.collection.ListEntry
+import com.android.systemui.statusbar.notification.collection.GroupEntry
+import com.android.systemui.statusbar.notification.collection.PipelineEntry
import com.android.systemui.statusbar.notification.collection.NotifPipeline
import com.android.systemui.statusbar.notification.collection.NotificationEntry
import com.android.systemui.statusbar.notification.collection.SortBySectionTimeFlag
@@ -61,6 +62,7 @@ class ConversationCoordinator @Inject constructor(
originalGroup == null -> null
originalGroup == promoted.parent -> null
originalGroup.parent == null -> null
+ originalGroup !is GroupEntry -> summary.key
originalGroup.summary != summary -> null
originalGroup.children.any { it.channel == summary.channel } -> null
else -> summary.key
@@ -74,7 +76,7 @@ class ConversationCoordinator @Inject constructor(
override fun shouldPromoteToTopLevel(entry: NotificationEntry): Boolean {
val shouldPromote = entry.channel?.isImportantConversation == true
if (shouldPromote) {
- val summary = entry.parent?.summary
+ val summary = (entry.parent as? GroupEntry)?.summary
if (summary != null && entry.channel == summary.channel) {
promotedEntriesToSummaryOfSameChannel[entry] = summary
}
@@ -85,14 +87,14 @@ class ConversationCoordinator @Inject constructor(
val priorityPeopleSectioner =
object : NotifSectioner("Priority People", BUCKET_PRIORITY_PEOPLE) {
- override fun isInSection(entry: ListEntry): Boolean {
+ override fun isInSection(entry: PipelineEntry): Boolean {
return getPeopleType(entry) == TYPE_IMPORTANT_PERSON
}
}
// TODO(b/330193582): Rename to just "People"
val peopleAlertingSectioner = object : NotifSectioner("People(alerting)", BUCKET_PEOPLE) {
- override fun isInSection(entry: ListEntry): Boolean {
+ override fun isInSection(entry: PipelineEntry): Boolean {
if (SortBySectionTimeFlag.isEnabled) {
return highPriorityProvider.isHighPriorityConversation(entry)
|| isConversation(entry)
@@ -111,7 +113,7 @@ class ConversationCoordinator @Inject constructor(
val peopleSilentSectioner = object : NotifSectioner("People(silent)", BUCKET_PEOPLE) {
// Because the peopleAlertingSectioner is above this one, it will claim all conversations that are alerting.
// All remaining conversations must be silent.
- override fun isInSection(entry: ListEntry): Boolean {
+ override fun isInSection(entry: PipelineEntry): Boolean {
SortBySectionTimeFlag.assertInLegacyMode()
return isConversation(entry)
}
@@ -132,17 +134,17 @@ class ConversationCoordinator @Inject constructor(
pipeline.addOnBeforeRenderListListener(onBeforeRenderListListener)
}
- private fun isConversation(entry: ListEntry): Boolean =
+ private fun isConversation(entry: PipelineEntry): Boolean =
getPeopleType(entry) != TYPE_NON_PERSON
@PeopleNotificationType
- private fun getPeopleType(entry: ListEntry): Int =
+ private fun getPeopleType(entry: PipelineEntry): Int =
entry.representativeEntry?.let {
peopleNotificationIdentifier.getPeopleNotificationType(it)
} ?: TYPE_NON_PERSON
private val notifComparator: NotifComparator = object : NotifComparator("People") {
- override fun compare(entry1: ListEntry, entry2: ListEntry): Int {
+ override fun compare(entry1: PipelineEntry, entry2: PipelineEntry): Int {
val type1 = getPeopleType(entry1)
val type2 = getPeopleType(entry2)
return type2.compareTo(type1)
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/DataStoreCoordinator.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/DataStoreCoordinator.kt
index 034a4fd2af72..e4ec76c68e6f 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/DataStoreCoordinator.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/DataStoreCoordinator.kt
@@ -17,7 +17,7 @@
package com.android.systemui.statusbar.notification.collection.coordinator
import com.android.systemui.statusbar.notification.collection.GroupEntry
-import com.android.systemui.statusbar.notification.collection.ListEntry
+import com.android.systemui.statusbar.notification.collection.PipelineEntry
import com.android.systemui.statusbar.notification.collection.NotifLiveDataStoreImpl
import com.android.systemui.statusbar.notification.collection.NotifPipeline
import com.android.systemui.statusbar.notification.collection.NotificationEntry
@@ -43,12 +43,12 @@ internal constructor(private val notifLiveDataStoreImpl: NotifLiveDataStoreImpl)
d.dump("notifLiveDataStoreImpl", notifLiveDataStoreImpl)
}
- private fun onAfterRenderList(entries: List<ListEntry>) {
+ private fun onAfterRenderList(entries: List<PipelineEntry>) {
val flatEntryList = flattenedEntryList(entries)
notifLiveDataStoreImpl.setActiveNotifList(flatEntryList)
}
- private fun flattenedEntryList(entries: List<ListEntry>) =
+ private fun flattenedEntryList(entries: List<PipelineEntry>) =
mutableListOf<NotificationEntry>().also { list ->
entries.forEach { entry ->
when (entry) {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/DismissibilityCoordinator.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/DismissibilityCoordinator.kt
index 0a9dddc1c75e..6bed0cf054af 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/DismissibilityCoordinator.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/DismissibilityCoordinator.kt
@@ -17,7 +17,7 @@
package com.android.systemui.statusbar.notification.collection.coordinator
import com.android.systemui.statusbar.notification.collection.GroupEntry
-import com.android.systemui.statusbar.notification.collection.ListEntry
+import com.android.systemui.statusbar.notification.collection.PipelineEntry
import com.android.systemui.statusbar.notification.collection.NotifPipeline
import com.android.systemui.statusbar.notification.collection.coordinator.dagger.CoordinatorScope
import com.android.systemui.statusbar.notification.collection.provider.NotificationDismissibilityProviderImpl
@@ -37,7 +37,7 @@ constructor(
pipeline.addOnBeforeRenderListListener(::onBeforeRenderListListener)
}
- private fun onBeforeRenderListListener(entries: List<ListEntry>) {
+ private fun onBeforeRenderListListener(entries: List<PipelineEntry>) {
val isLocked = !keyguardStateController.isUnlocked
val nonDismissableEntryKeys = mutableSetOf<String>()
markNonDismissibleEntries(nonDismissableEntryKeys, entries, isLocked)
@@ -54,7 +54,7 @@ constructor(
*/
private fun markNonDismissibleEntries(
markedKeys: MutableSet<String>,
- entries: List<ListEntry>,
+ entries: List<PipelineEntry>,
isLocked: Boolean
): Boolean {
var anyNonDismissableEntries = false
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/GroupCountCoordinator.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/GroupCountCoordinator.kt
index 02bf3b3be537..2f0701f96f28 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/GroupCountCoordinator.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/GroupCountCoordinator.kt
@@ -18,7 +18,7 @@ package com.android.systemui.statusbar.notification.collection.coordinator
import android.util.ArrayMap
import com.android.systemui.statusbar.notification.collection.GroupEntry
-import com.android.systemui.statusbar.notification.collection.ListEntry
+import com.android.systemui.statusbar.notification.collection.PipelineEntry
import com.android.systemui.statusbar.notification.collection.NotifPipeline
import com.android.systemui.statusbar.notification.collection.coordinator.dagger.CoordinatorScope
import com.android.systemui.statusbar.notification.collection.render.NotifGroupController
@@ -34,7 +34,7 @@ class GroupCountCoordinator @Inject constructor() : Coordinator {
pipeline.addOnAfterRenderGroupListener(::onAfterRenderGroup)
}
- private fun onBeforeFinalizeFilter(entries: List<ListEntry>) {
+ private fun onBeforeFinalizeFilter(entries: List<PipelineEntry>) {
// save untruncated child counts to our internal map
untruncatedChildCounts.clear()
entries.asSequence().filterIsInstance<GroupEntry>().forEach { groupEntry ->
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/GroupWhenCoordinator.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/GroupWhenCoordinator.kt
index f253100b3661..d0411b56d92d 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/GroupWhenCoordinator.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/GroupWhenCoordinator.kt
@@ -18,7 +18,7 @@ package com.android.systemui.statusbar.notification.collection.coordinator
import android.util.ArrayMap
import com.android.systemui.dagger.qualifiers.Main
import com.android.systemui.statusbar.notification.collection.GroupEntry
-import com.android.systemui.statusbar.notification.collection.ListEntry
+import com.android.systemui.statusbar.notification.collection.PipelineEntry
import com.android.systemui.statusbar.notification.collection.NotifPipeline
import com.android.systemui.statusbar.notification.collection.coordinator.dagger.CoordinatorScope
import com.android.systemui.statusbar.notification.collection.listbuilder.pluggable.Invalidator
@@ -52,7 +52,7 @@ constructor(
pipeline.addPreRenderInvalidator(invalidator)
}
- private fun onBeforeFinalizeFilterListener(entries: List<ListEntry>) {
+ private fun onBeforeFinalizeFilterListener(entries: List<PipelineEntry>) {
cancelListInvalidation()
notificationGroupTimes.clear()
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/GutsCoordinator.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/GutsCoordinator.kt
index b200136b1b43..9045aac6ea6f 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/GutsCoordinator.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/GutsCoordinator.kt
@@ -18,7 +18,7 @@ package com.android.systemui.statusbar.notification.collection.coordinator
import android.util.ArraySet
import com.android.systemui.Dumpable
import com.android.systemui.dump.DumpManager
-import com.android.systemui.statusbar.notification.collection.ListEntry
+import com.android.systemui.statusbar.notification.collection.PipelineEntry
import com.android.systemui.statusbar.notification.collection.NotifPipeline
import com.android.systemui.statusbar.notification.collection.NotificationEntry
import com.android.systemui.statusbar.notification.collection.coordinator.dagger.CoordinatorScope
@@ -113,7 +113,7 @@ class GutsCoordinator @Inject constructor(
}
}
- private fun isCurrentlyShowingGuts(entry: ListEntry) =
+ private fun isCurrentlyShowingGuts(entry: PipelineEntry) =
notifsWithOpenGuts.contains(entry.key)
private fun closeGutsAndEndLifetimeExtension(entry: NotificationEntry) {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/HeadsUpCoordinator.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/HeadsUpCoordinator.kt
index eb5a3703bcfb..611e0ef77fac 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/HeadsUpCoordinator.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/HeadsUpCoordinator.kt
@@ -27,7 +27,7 @@ import com.android.systemui.statusbar.chips.notification.domain.interactor.Statu
import com.android.systemui.statusbar.chips.notification.shared.StatusBarNotifChips
import com.android.systemui.statusbar.notification.NotifPipelineFlags
import com.android.systemui.statusbar.notification.collection.GroupEntry
-import com.android.systemui.statusbar.notification.collection.ListEntry
+import com.android.systemui.statusbar.notification.collection.PipelineEntry
import com.android.systemui.statusbar.notification.collection.NotifCollection
import com.android.systemui.statusbar.notification.collection.NotifPipeline
import com.android.systemui.statusbar.notification.collection.NotificationEntry
@@ -103,7 +103,7 @@ constructor(
mNotifPipeline = pipeline
mHeadsUpManager.addListener(mOnHeadsUpChangedListener)
pipeline.addCollectionListener(mNotifCollectionListener)
- pipeline.addOnBeforeTransformGroupsListener(::onBeforeTransformGroups)
+ pipeline.addOnBeforeTransformGroupsListener { onBeforeTransformGroups() }
pipeline.addOnBeforeFinalizeFilterListener(::onBeforeFinalizeFilter)
pipeline.addPromoter(mNotifPromoter)
pipeline.addNotificationLifetimeExtender(mLifetimeExtender)
@@ -170,7 +170,7 @@ constructor(
* Once the pipeline starts running, we can look through posted entries and quickly process any
* that don't have groups, and thus will never gave a group heads up edge case.
*/
- fun onBeforeTransformGroups(list: List<ListEntry>) {
+ fun onBeforeTransformGroups() {
mNow = mSystemClock.currentTimeMillis()
if (mPostedEntries.isEmpty()) {
return
@@ -191,7 +191,7 @@ constructor(
* we know that stability and [NotifPromoter]s have been applied, so we can use the location of
* notifications in this list to determine what kind of group heads up behavior should happen.
*/
- fun onBeforeFinalizeFilter(list: List<ListEntry>) =
+ fun onBeforeFinalizeFilter(list: List<PipelineEntry>) =
mHeadsUpManager.modifyHuns { hunMutator ->
// Nothing to do if there are no other adds/updates
if (mPostedEntries.isEmpty()) {
@@ -410,7 +410,7 @@ constructor(
)
.firstOrNull()
- private fun getGroupLocationsByKey(list: List<ListEntry>): Map<String, GroupLocation> =
+ private fun getGroupLocationsByKey(list: List<PipelineEntry>): Map<String, GroupLocation> =
mutableMapOf<String, GroupLocation>().also { map ->
list.forEach { topLevelEntry ->
when (topLevelEntry) {
@@ -833,13 +833,13 @@ constructor(
val sectioner =
object : NotifSectioner("HeadsUp", BUCKET_HEADS_UP) {
- override fun isInSection(entry: ListEntry): Boolean =
+ override fun isInSection(entry: PipelineEntry): Boolean =
// TODO: This check won't notice if a child of the group is going to HUN...
isGoingToShowHunNoRetract(entry)
override fun getComparator(): NotifComparator {
return object : NotifComparator("HeadsUp") {
- override fun compare(o1: ListEntry, o2: ListEntry): Int =
+ override fun compare(o1: PipelineEntry, o2: PipelineEntry): Int =
mHeadsUpManager.compare(o1.representativeEntry, o2.representativeEntry)
}
}
@@ -867,7 +867,7 @@ constructor(
private fun isSticky(entry: NotificationEntry) = mHeadsUpManager.isSticky(entry.key)
- private fun isEntryBinding(entry: ListEntry): Boolean {
+ private fun isEntryBinding(entry: PipelineEntry): Boolean {
val bindingUntil = mEntriesBindingUntil[entry.key]
return bindingUntil != null && bindingUntil >= mNow
}
@@ -875,12 +875,12 @@ constructor(
/**
* Whether the notification is already heads up or binding so that it can imminently heads up
*/
- private fun isAttemptingToShowHun(entry: ListEntry) =
+ private fun isAttemptingToShowHun(entry: PipelineEntry) =
mHeadsUpManager.isHeadsUpEntry(entry.key) ||
isEntryBinding(entry) ||
isHeadsUpAnimatingAway(entry)
- private fun isHeadsUpAnimatingAway(entry: ListEntry): Boolean {
+ private fun isHeadsUpAnimatingAway(entry: PipelineEntry): Boolean {
if (!GroupHunAnimationFix.isEnabled) return false
return entry.representativeEntry?.row?.isHeadsUpAnimatingAway ?: false
}
@@ -891,7 +891,7 @@ constructor(
* returns `true` even if the update would (in isolation of its group) cause the heads up to be
* retracted. This is important for not retracting transferred group heads ups.
*/
- private fun isGoingToShowHunNoRetract(entry: ListEntry) =
+ private fun isGoingToShowHunNoRetract(entry: PipelineEntry) =
mPostedEntries[entry.key]?.calculateShouldBeHeadsUpNoRetract ?: isAttemptingToShowHun(entry)
/**
@@ -900,7 +900,7 @@ constructor(
* strict because any update which would revoke the heads up supersedes the current heads
* up/binding state.
*/
- private fun isGoingToShowHunStrict(entry: ListEntry) =
+ private fun isGoingToShowHunStrict(entry: PipelineEntry) =
mPostedEntries[entry.key]?.calculateShouldBeHeadsUpStrict ?: isAttemptingToShowHun(entry)
private fun endNotifLifetimeExtensionIfExtended(entry: NotificationEntry) {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/LockScreenMinimalismCoordinator.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/LockScreenMinimalismCoordinator.kt
index e2328497d9ea..56deb18df9ab 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/LockScreenMinimalismCoordinator.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/LockScreenMinimalismCoordinator.kt
@@ -27,7 +27,7 @@ import com.android.systemui.plugins.statusbar.StatusBarStateController
import com.android.systemui.shade.domain.interactor.ShadeInteractor
import com.android.systemui.statusbar.StatusBarState
import com.android.systemui.statusbar.notification.collection.GroupEntry
-import com.android.systemui.statusbar.notification.collection.ListEntry
+import com.android.systemui.statusbar.notification.collection.PipelineEntry
import com.android.systemui.statusbar.notification.collection.NotifPipeline
import com.android.systemui.statusbar.notification.collection.NotificationEntry
import com.android.systemui.statusbar.notification.collection.coordinator.dagger.CoordinatorScope
@@ -176,7 +176,7 @@ constructor(
}
}
- private fun pickOutTopUnseenNotifs(list: List<ListEntry>) {
+ private fun pickOutTopUnseenNotifs(list: List<PipelineEntry>) {
if (NotificationMinimalism.isUnexpectedlyInLegacyMode()) return
if (!minimalismEnabled) return
// Only ever elevate a top unseen notification on keyguard, not even locked shade
@@ -224,7 +224,7 @@ constructor(
val topOngoingSectioner =
object : NotifSectioner("TopOngoing", BUCKET_TOP_ONGOING) {
- override fun isInSection(entry: ListEntry): Boolean {
+ override fun isInSection(entry: PipelineEntry): Boolean {
if (NotificationMinimalism.isUnexpectedlyInLegacyMode()) return false
if (!minimalismEnabled) return false
return entry.anyEntry { notificationEntry ->
@@ -235,7 +235,7 @@ constructor(
val topUnseenSectioner =
object : NotifSectioner("TopUnseen", BUCKET_TOP_UNSEEN) {
- override fun isInSection(entry: ListEntry): Boolean {
+ override fun isInSection(entry: PipelineEntry): Boolean {
if (NotificationMinimalism.isUnexpectedlyInLegacyMode()) return false
if (!minimalismEnabled) return false
return entry.anyEntry { notificationEntry ->
@@ -244,7 +244,7 @@ constructor(
}
}
- private fun ListEntry.anyEntry(predicate: (NotificationEntry?) -> Boolean) =
+ private fun PipelineEntry.anyEntry(predicate: (NotificationEntry?) -> Boolean) =
when {
predicate(representativeEntry) -> true
this !is GroupEntry -> false
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/OriginalUnseenKeyguardCoordinator.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/OriginalUnseenKeyguardCoordinator.kt
index de6f2576ff19..660ee401b369 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/OriginalUnseenKeyguardCoordinator.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/OriginalUnseenKeyguardCoordinator.kt
@@ -30,6 +30,7 @@ import com.android.systemui.scene.domain.interactor.SceneInteractor
import com.android.systemui.scene.shared.flag.SceneContainerFlag
import com.android.systemui.scene.shared.model.Scenes
import com.android.systemui.statusbar.expansionChanges
+import com.android.systemui.statusbar.notification.collection.GroupEntry
import com.android.systemui.statusbar.notification.collection.NotifPipeline
import com.android.systemui.statusbar.notification.collection.NotificationEntry
import com.android.systemui.statusbar.notification.collection.coordinator.dagger.CoordinatorScope
@@ -313,7 +314,7 @@ constructor(
unseenNotifications.contains(entry) -> false
// Don't apply the filter to (non-promoted) group summaries
// - summary will be pruned if necessary, depending on if children are filtered
- entry.parent?.summary == entry -> false
+ (entry.parent as? GroupEntry)?.summary == entry -> false
// Check that the entry satisfies certain characteristics that would bypass the
// filter
shouldIgnoreUnseenCheck(entry) -> false
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/PreparationCoordinator.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/PreparationCoordinator.java
index 2ecce1f02ef6..20c6736b74c8 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/PreparationCoordinator.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/PreparationCoordinator.java
@@ -35,7 +35,7 @@ import androidx.annotation.Nullable;
import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.statusbar.IStatusBarService;
import com.android.systemui.statusbar.notification.collection.GroupEntry;
-import com.android.systemui.statusbar.notification.collection.ListEntry;
+import com.android.systemui.statusbar.notification.collection.PipelineEntry;
import com.android.systemui.statusbar.notification.collection.NotifPipeline;
import com.android.systemui.statusbar.notification.collection.NotificationEntry;
import com.android.systemui.statusbar.notification.collection.ShadeListBuilder;
@@ -232,9 +232,10 @@ public class PreparationCoordinator implements Coordinator {
*/
@Override
public boolean shouldFilterOut(NotificationEntry entry, long now) {
- final GroupEntry parent = requireNonNull(entry.getParent());
- Boolean isMemberOfDelayedGroup = mIsDelayedGroupCache.get(parent);
- if (isMemberOfDelayedGroup == null) {
+ final PipelineEntry pipelineEntryParent = requireNonNull(entry.getParent());
+ Boolean isMemberOfDelayedGroup = mIsDelayedGroupCache.get(pipelineEntryParent);
+ if (isMemberOfDelayedGroup == null && pipelineEntryParent instanceof GroupEntry) {
+ GroupEntry parent = (GroupEntry) pipelineEntryParent;
isMemberOfDelayedGroup = shouldWaitForGroupToInflate(parent, now);
mIsDelayedGroupCache.put(parent, isMemberOfDelayedGroup);
}
@@ -279,7 +280,7 @@ public class PreparationCoordinator implements Coordinator {
}
};
- private void purgeCaches(Collection<ListEntry> entries) {
+ private void purgeCaches(Collection<PipelineEntry> entries) {
Set<String> wantedPackages = getPackages(entries);
mAppIconProvider.purgeCache(wantedPackages);
mNotificationIconStyleProvider.purgeCache(wantedPackages);
@@ -288,9 +289,9 @@ public class PreparationCoordinator implements Coordinator {
/**
* Get all app packages present in {@param entries}.
*/
- private static @NonNull Set<String> getPackages(Collection<ListEntry> entries) {
+ private static @NonNull Set<String> getPackages(Collection<PipelineEntry> entries) {
Set<String> packages = new HashSet<>();
- for (ListEntry entry : entries) {
+ for (PipelineEntry entry : entries) {
NotificationEntry notificationEntry = entry.getRepresentativeEntry();
if (notificationEntry == null) {
Log.wtf(TAG, "notification entry " + entry.getKey()
@@ -302,9 +303,9 @@ public class PreparationCoordinator implements Coordinator {
return packages;
}
- private void inflateAllRequiredViews(List<ListEntry> entries) {
+ private void inflateAllRequiredViews(List<PipelineEntry> entries) {
for (int i = 0, size = entries.size(); i < size; i++) {
- ListEntry entry = entries.get(i);
+ PipelineEntry entry = entries.get(i);
if (entry instanceof GroupEntry) {
GroupEntry groupEntry = (GroupEntry) entry;
inflateRequiredGroupViews(groupEntry);
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/RankingCoordinator.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/RankingCoordinator.java
index 69b069d792e3..26bc5b14d357 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/RankingCoordinator.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/RankingCoordinator.java
@@ -20,7 +20,7 @@ import android.annotation.NonNull;
import android.annotation.Nullable;
import com.android.systemui.plugins.statusbar.StatusBarStateController;
-import com.android.systemui.statusbar.notification.collection.ListEntry;
+import com.android.systemui.statusbar.notification.collection.PipelineEntry;
import com.android.systemui.statusbar.notification.collection.NotifPipeline;
import com.android.systemui.statusbar.notification.collection.NotificationEntry;
import com.android.systemui.statusbar.notification.collection.coordinator.dagger.CoordinatorScope;
@@ -97,7 +97,7 @@ public class RankingCoordinator implements Coordinator {
private final NotifSectioner mAlertingNotifSectioner = new NotifSectioner("Alerting",
NotificationPriorityBucketKt.BUCKET_ALERTING) {
@Override
- public boolean isInSection(ListEntry entry) {
+ public boolean isInSection(PipelineEntry entry) {
return mHighPriorityProvider.isHighPriority(entry);
}
@@ -115,7 +115,7 @@ public class RankingCoordinator implements Coordinator {
private final NotifSectioner mSilentNotifSectioner = new NotifSectioner("Silent",
NotificationPriorityBucketKt.BUCKET_SILENT) {
@Override
- public boolean isInSection(ListEntry entry) {
+ public boolean isInSection(PipelineEntry entry) {
return !mHighPriorityProvider.isHighPriority(entry)
&& !entry.getRepresentativeEntry().isAmbient();
}
@@ -128,7 +128,7 @@ public class RankingCoordinator implements Coordinator {
@Nullable
@Override
- public void onEntriesUpdated(@NonNull List<ListEntry> entries) {
+ public void onEntriesUpdated(@NonNull List<PipelineEntry> entries) {
mHasSilentEntries = false;
for (int i = 0; i < entries.size(); i++) {
if (entries.get(i).getRepresentativeEntry().getSbn().isClearable()) {
@@ -144,7 +144,7 @@ public class RankingCoordinator implements Coordinator {
private final NotifSectioner mMinimizedNotifSectioner = new NotifSectioner("Minimized",
NotificationPriorityBucketKt.BUCKET_SILENT) {
@Override
- public boolean isInSection(ListEntry entry) {
+ public boolean isInSection(PipelineEntry entry) {
return !mHighPriorityProvider.isHighPriority(entry)
&& entry.getRepresentativeEntry().isAmbient();
}
@@ -157,7 +157,7 @@ public class RankingCoordinator implements Coordinator {
@Nullable
@Override
- public void onEntriesUpdated(@NonNull List<ListEntry> entries) {
+ public void onEntriesUpdated(@NonNull List<PipelineEntry> entries) {
mHasMinimizedEntries = false;
for (int i = 0; i < entries.size(); i++) {
if (entries.get(i).getRepresentativeEntry().getSbn().isClearable()) {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/RowAlertTimeCoordinator.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/RowAlertTimeCoordinator.kt
index 4a7b7ca51ba2..930e52a8fd52 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/RowAlertTimeCoordinator.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/RowAlertTimeCoordinator.kt
@@ -17,7 +17,7 @@ package com.android.systemui.statusbar.notification.collection.coordinator
import android.util.ArrayMap
import com.android.systemui.statusbar.notification.collection.GroupEntry
-import com.android.systemui.statusbar.notification.collection.ListEntry
+import com.android.systemui.statusbar.notification.collection.PipelineEntry
import com.android.systemui.statusbar.notification.collection.NotifPipeline
import com.android.systemui.statusbar.notification.collection.NotificationEntry
import com.android.systemui.statusbar.notification.collection.coordinator.dagger.CoordinatorScope
@@ -39,7 +39,7 @@ class RowAlertTimeCoordinator @Inject constructor() : Coordinator {
pipeline.addOnAfterRenderEntryListener(::onAfterRenderEntry)
}
- private fun onBeforeFinalizeFilterListener(entries: List<ListEntry>) {
+ private fun onBeforeFinalizeFilterListener(entries: List<PipelineEntry>) {
latestAlertTimeBySummary.clear()
entries.asSequence().filterIsInstance<GroupEntry>().forEach { groupEntry ->
val summary = checkNotNull(groupEntry.summary)
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/RowAppearanceCoordinator.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/RowAppearanceCoordinator.kt
index df8e56eb4102..a987c544bb50 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/RowAppearanceCoordinator.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/RowAppearanceCoordinator.kt
@@ -20,7 +20,7 @@ import android.content.Context
import com.android.systemui.res.R
import com.android.systemui.shade.ShadeDisplayAware
import com.android.systemui.statusbar.notification.AssistantFeedbackController
-import com.android.systemui.statusbar.notification.collection.ListEntry
+import com.android.systemui.statusbar.notification.collection.PipelineEntry
import com.android.systemui.statusbar.notification.collection.NotifPipeline
import com.android.systemui.statusbar.notification.collection.NotificationEntry
import com.android.systemui.statusbar.notification.collection.coordinator.dagger.CoordinatorScope
@@ -63,7 +63,7 @@ internal constructor(
pipeline.addOnAfterRenderEntryListener(::onAfterRenderEntry)
}
- private fun onBeforeRenderList(list: List<ListEntry>) {
+ private fun onBeforeRenderList(list: List<PipelineEntry>) {
entryToExpand =
list.firstOrNull()?.representativeEntry?.takeIf { entry ->
!mSectionStyleProvider.isMinimizedSection(entry.section!!)
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/SensitiveContentCoordinator.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/SensitiveContentCoordinator.kt
index 90916d1c77d8..db24e7d0604d 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/SensitiveContentCoordinator.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/SensitiveContentCoordinator.kt
@@ -32,7 +32,7 @@ import com.android.systemui.statusbar.NotificationLockscreenUserManager.REDACTIO
import com.android.systemui.statusbar.StatusBarState
import com.android.systemui.statusbar.notification.DynamicPrivacyController
import com.android.systemui.statusbar.notification.collection.GroupEntry
-import com.android.systemui.statusbar.notification.collection.ListEntry
+import com.android.systemui.statusbar.notification.collection.PipelineEntry
import com.android.systemui.statusbar.notification.collection.NotifPipeline
import com.android.systemui.statusbar.notification.collection.NotificationEntry
import com.android.systemui.statusbar.notification.collection.coordinator.dagger.CoordinatorScope
@@ -155,7 +155,7 @@ constructor(
}
}
- override fun onBeforeRenderList(entries: List<ListEntry>) {
+ override fun onBeforeRenderList(entries: List<PipelineEntry>) {
if (
isKeyguardGoingAway ||
statusBarStateController.state == StatusBarState.KEYGUARD &&
@@ -220,13 +220,15 @@ constructor(
}
}
-private fun extractAllRepresentativeEntries(entries: List<ListEntry>): Sequence<NotificationEntry> =
+private fun extractAllRepresentativeEntries(entries: List<PipelineEntry>): Sequence<NotificationEntry> =
entries.asSequence().flatMap(::extractAllRepresentativeEntries)
-private fun extractAllRepresentativeEntries(listEntry: ListEntry): Sequence<NotificationEntry> =
+private fun extractAllRepresentativeEntries(
+ pipelineEntry: PipelineEntry,
+): Sequence<NotificationEntry> =
sequence {
- listEntry.representativeEntry?.let { yield(it) }
- if (listEntry is GroupEntry) {
- yieldAll(extractAllRepresentativeEntries(listEntry.children))
+ pipelineEntry.representativeEntry?.let { yield(it) }
+ if (pipelineEntry is GroupEntry) {
+ yieldAll(extractAllRepresentativeEntries(pipelineEntry.children))
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/ShadeEventCoordinator.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/ShadeEventCoordinator.kt
index 1c66db783a34..29a8eb0c25d9 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/ShadeEventCoordinator.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/ShadeEventCoordinator.kt
@@ -19,7 +19,7 @@ package com.android.systemui.statusbar.notification.collection.coordinator
import android.service.notification.NotificationListenerService
import com.android.systemui.dagger.SysUISingleton
import com.android.systemui.dagger.qualifiers.Main
-import com.android.systemui.statusbar.notification.collection.ListEntry
+import com.android.systemui.statusbar.notification.collection.PipelineEntry
import com.android.systemui.statusbar.notification.collection.NotifPipeline
import com.android.systemui.statusbar.notification.collection.NotificationEntry
import com.android.systemui.statusbar.notification.collection.notifcollection.NotifCollectionListener
@@ -67,7 +67,7 @@ class ShadeEventCoordinator @Inject internal constructor(
mShadeEmptiedCallback = callback
}
- private fun onBeforeRenderList(entries: List<ListEntry>) {
+ private fun onBeforeRenderList(entries: List<PipelineEntry>) {
if (mEntryRemoved && entries.isEmpty()) {
mLogger.logShadeEmptied()
// TODO(b/206023518): This was bad. Do not copy this.
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/StackCoordinator.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/StackCoordinator.kt
index 1cb2366a16fe..53a73f4ced63 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/StackCoordinator.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/StackCoordinator.kt
@@ -19,7 +19,7 @@ package com.android.systemui.statusbar.notification.collection.coordinator
import com.android.app.tracing.traceSection
import com.android.server.notification.Flags.screenshareNotificationHiding
import com.android.systemui.Flags.screenshareNotificationHidingBugFix
-import com.android.systemui.statusbar.notification.collection.ListEntry
+import com.android.systemui.statusbar.notification.collection.PipelineEntry
import com.android.systemui.statusbar.notification.collection.NotifPipeline
import com.android.systemui.statusbar.notification.collection.coordinator.dagger.CoordinatorScope
import com.android.systemui.statusbar.notification.collection.render.GroupExpansionManagerImpl
@@ -50,14 +50,14 @@ internal constructor(
groupExpansionManagerImpl.attach(pipeline)
}
- private fun onAfterRenderList(entries: List<ListEntry>) =
+ private fun onAfterRenderList(entries: List<PipelineEntry>) =
traceSection("StackCoordinator.onAfterRenderList") {
val notifStats = calculateNotifStats(entries)
activeNotificationsInteractor.setNotifStats(notifStats)
renderListInteractor.setRenderedList(entries)
}
- private fun calculateNotifStats(entries: List<ListEntry>): NotifStats {
+ private fun calculateNotifStats(entries: List<PipelineEntry>): NotifStats {
var hasNonClearableAlertingNotifs = false
var hasClearableAlertingNotifs = false
var hasNonClearableSilentNotifs = false
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/VisualStabilityCoordinator.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/VisualStabilityCoordinator.java
index 49d5029bbc70..3e5655a9e925 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/VisualStabilityCoordinator.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/VisualStabilityCoordinator.java
@@ -39,7 +39,7 @@ import com.android.systemui.shade.domain.interactor.ShadeAnimationInteractor;
import com.android.systemui.shade.domain.interactor.ShadeInteractor;
import com.android.systemui.statusbar.notification.VisibilityLocationProvider;
import com.android.systemui.statusbar.notification.collection.GroupEntry;
-import com.android.systemui.statusbar.notification.collection.ListEntry;
+import com.android.systemui.statusbar.notification.collection.PipelineEntry;
import com.android.systemui.statusbar.notification.collection.NotifPipeline;
import com.android.systemui.statusbar.notification.collection.NotificationEntry;
import com.android.systemui.statusbar.notification.collection.listbuilder.OnBeforeRenderListListener;
@@ -253,21 +253,20 @@ public class VisualStabilityCoordinator implements Coordinator, Dumpable {
return false;
}
- final GroupEntry parent = entry.getParent();
+ final PipelineEntry parent = entry.getParent();
if (parent == null) {
return false;
}
-
return isHeadsUpGroup(parent);
}
- private boolean isHeadsUpGroup(GroupEntry groupEntry) {
- if (StabilizeHeadsUpGroup.isUnexpectedlyInLegacyMode()) {
+ private boolean isHeadsUpGroup(PipelineEntry pipelineEntry) {
+ if (!(pipelineEntry instanceof GroupEntry groupEntry)) {
return false;
}
- if (groupEntry == null) {
+ if (StabilizeHeadsUpGroup.isUnexpectedlyInLegacyMode()) {
return false;
}
@@ -370,7 +369,7 @@ public class VisualStabilityCoordinator implements Coordinator, Dumpable {
}
@Override
- public boolean isEntryReorderingAllowed(@NonNull ListEntry entry) {
+ public boolean isEntryReorderingAllowed(@NonNull PipelineEntry entry) {
if (StabilizeHeadsUpGroup.isEnabled()) {
if (isEveryChangeAllowed()) {
return true;
@@ -403,7 +402,7 @@ public class VisualStabilityCoordinator implements Coordinator, Dumpable {
private final OnBeforeRenderListListener mOnBeforeRenderListListener =
new OnBeforeRenderListListener() {
@Override
- public void onBeforeRenderList(List<ListEntry> entries) {
+ public void onBeforeRenderList(List<PipelineEntry> entries) {
if (StabilizeHeadsUpGroup.isUnexpectedlyInLegacyMode()) {
return;
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/inflation/NotifUiAdjustmentProvider.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/inflation/NotifUiAdjustmentProvider.kt
index 465bc288cbc1..adcc3ec02f0c 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/inflation/NotifUiAdjustmentProvider.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/inflation/NotifUiAdjustmentProvider.kt
@@ -124,7 +124,7 @@ constructor(
val parent = entry.parent ?: error("Entry must have a parent to determine if minimized")
val isMinimizedSection = sectionStyleProvider.isMinimizedSection(section)
val isTopLevelEntry = parent == GroupEntry.ROOT_ENTRY
- val isGroupSummary = parent.summary == entry
+ val isGroupSummary = (parent as? GroupEntry)?.summary == entry
return isMinimizedSection && (isTopLevelEntry || isGroupSummary)
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/listbuilder/OnAfterRenderListListener.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/listbuilder/OnAfterRenderListListener.java
index ac450c03b850..e3ab81c84bfc 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/listbuilder/OnAfterRenderListListener.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/listbuilder/OnAfterRenderListListener.java
@@ -18,7 +18,7 @@ package com.android.systemui.statusbar.notification.collection.listbuilder;
import androidx.annotation.NonNull;
-import com.android.systemui.statusbar.notification.collection.ListEntry;
+import com.android.systemui.statusbar.notification.collection.PipelineEntry;
import com.android.systemui.statusbar.notification.collection.NotifPipeline;
import java.util.List;
@@ -31,5 +31,5 @@ public interface OnAfterRenderListListener {
* @param entries The current list of top-level entries. Note that this is a live view into the
* current list and will change whenever the pipeline is rerun.
*/
- void onAfterRenderList(@NonNull List<ListEntry> entries);
+ void onAfterRenderList(@NonNull List<PipelineEntry> entries);
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/listbuilder/OnBeforeFinalizeFilterListener.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/listbuilder/OnBeforeFinalizeFilterListener.java
index 086661ea219b..3c4f4225388c 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/listbuilder/OnBeforeFinalizeFilterListener.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/listbuilder/OnBeforeFinalizeFilterListener.java
@@ -16,7 +16,7 @@
package com.android.systemui.statusbar.notification.collection.listbuilder;
-import com.android.systemui.statusbar.notification.collection.ListEntry;
+import com.android.systemui.statusbar.notification.collection.PipelineEntry;
import com.android.systemui.statusbar.notification.collection.NotifPipeline;
import java.util.List;
@@ -30,5 +30,5 @@ public interface OnBeforeFinalizeFilterListener {
* @param entries The current list of top-level entries. Note that this is a live view into the
* current list and will change whenever the pipeline is rerun.
*/
- void onBeforeFinalizeFilter(List<ListEntry> entries);
+ void onBeforeFinalizeFilter(List<PipelineEntry> entries);
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/listbuilder/OnBeforeRenderListListener.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/listbuilder/OnBeforeRenderListListener.java
index 44a27a4b546a..6ceb7d5653f0 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/listbuilder/OnBeforeRenderListListener.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/listbuilder/OnBeforeRenderListListener.java
@@ -16,7 +16,7 @@
package com.android.systemui.statusbar.notification.collection.listbuilder;
-import com.android.systemui.statusbar.notification.collection.ListEntry;
+import com.android.systemui.statusbar.notification.collection.PipelineEntry;
import com.android.systemui.statusbar.notification.collection.NotifPipeline;
import java.util.List;
@@ -30,5 +30,5 @@ public interface OnBeforeRenderListListener {
* @param entries The current list of top-level entries. Note that this is a live view into the
* current list and will change whenever the pipeline is rerun.
*/
- void onBeforeRenderList(List<ListEntry> entries);
+ void onBeforeRenderList(List<PipelineEntry> entries);
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/listbuilder/OnBeforeSortListener.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/listbuilder/OnBeforeSortListener.java
index 56cfe5cb3716..858cec1e80b5 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/listbuilder/OnBeforeSortListener.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/listbuilder/OnBeforeSortListener.java
@@ -16,7 +16,7 @@
package com.android.systemui.statusbar.notification.collection.listbuilder;
-import com.android.systemui.statusbar.notification.collection.ListEntry;
+import com.android.systemui.statusbar.notification.collection.PipelineEntry;
import com.android.systemui.statusbar.notification.collection.NotifPipeline;
import java.util.List;
@@ -30,5 +30,5 @@ public interface OnBeforeSortListener {
* @param entries The current list of top-level entries. Note that this is a live view into the
* current list and will change whenever the pipeline is rerun.
*/
- void onBeforeSort(List<ListEntry> entries);
+ void onBeforeSort(List<PipelineEntry> entries);
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/listbuilder/OnBeforeTransformGroupsListener.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/listbuilder/OnBeforeTransformGroupsListener.java
index 0dc4df0da066..eb525c7c4505 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/listbuilder/OnBeforeTransformGroupsListener.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/listbuilder/OnBeforeTransformGroupsListener.java
@@ -16,7 +16,7 @@
package com.android.systemui.statusbar.notification.collection.listbuilder;
-import com.android.systemui.statusbar.notification.collection.ListEntry;
+import com.android.systemui.statusbar.notification.collection.PipelineEntry;
import com.android.systemui.statusbar.notification.collection.NotifPipeline;
import com.android.systemui.statusbar.notification.collection.listbuilder.pluggable.NotifPromoter;
@@ -35,5 +35,5 @@ public interface OnBeforeTransformGroupsListener {
* a live view into the current notif list and will change as the list moves through
* the pipeline.
*/
- void onBeforeTransformGroups(List<ListEntry> list);
+ void onBeforeTransformGroups(List<PipelineEntry> list);
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/listbuilder/ShadeListBuilderHelper.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/listbuilder/ShadeListBuilderHelper.kt
index d8f75f61c05a..0d8a64abd13d 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/listbuilder/ShadeListBuilderHelper.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/listbuilder/ShadeListBuilderHelper.kt
@@ -16,10 +16,10 @@
package com.android.systemui.statusbar.notification.collection.listbuilder
-import com.android.systemui.statusbar.notification.collection.ListEntry
+import com.android.systemui.statusbar.notification.collection.PipelineEntry
object ShadeListBuilderHelper {
- fun getSectionSubLists(entries: List<ListEntry>): Iterable<List<ListEntry>> =
+ fun getSectionSubLists(entries: List<PipelineEntry>): Iterable<List<PipelineEntry>> =
getContiguousSubLists(entries, minLength = 1) { it.sectionIndex }
inline fun <T : Any, K : Any> getContiguousSubLists(
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/listbuilder/ShadeListBuilderLogger.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/listbuilder/ShadeListBuilderLogger.kt
index a8409d0c6fa0..8a9548f0b9d5 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/listbuilder/ShadeListBuilderLogger.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/listbuilder/ShadeListBuilderLogger.kt
@@ -23,7 +23,7 @@ import com.android.systemui.log.core.LogLevel.INFO
import com.android.systemui.log.core.LogLevel.WARNING
import com.android.systemui.statusbar.notification.NotifPipelineFlags
import com.android.systemui.statusbar.notification.collection.GroupEntry
-import com.android.systemui.statusbar.notification.collection.ListEntry
+import com.android.systemui.statusbar.notification.collection.PipelineEntry
import com.android.systemui.statusbar.notification.collection.NotificationEntry
import com.android.systemui.statusbar.notification.collection.listbuilder.PipelineState.StateName
import com.android.systemui.statusbar.notification.collection.listbuilder.PipelineState.getStateName
@@ -152,9 +152,9 @@ class ShadeListBuilderLogger @Inject constructor(
fun logEntryAttachStateChanged(
buildId: Int,
- entry: ListEntry,
- prevParent: GroupEntry?,
- newParent: GroupEntry?
+ entry: PipelineEntry,
+ prevParent: PipelineEntry?,
+ newParent: PipelineEntry?
) {
buffer.log(TAG, INFO, {
long1 = buildId.toLong()
@@ -177,7 +177,7 @@ class ShadeListBuilderLogger @Inject constructor(
})
}
- fun logParentChanged(buildId: Int, prevParent: GroupEntry?, newParent: GroupEntry?) {
+ fun logParentChanged(buildId: Int, prevParent: PipelineEntry?, newParent: PipelineEntry?) {
buffer.log(TAG, INFO, {
long1 = buildId.toLong()
str1 = prevParent?.logKey
@@ -195,8 +195,8 @@ class ShadeListBuilderLogger @Inject constructor(
fun logParentChangeSuppressedStarted(
buildId: Int,
- suppressedParent: GroupEntry?,
- keepingParent: GroupEntry?
+ suppressedParent: PipelineEntry?,
+ keepingParent: PipelineEntry?
) {
buffer.log(TAG, INFO, {
long1 = buildId.toLong()
@@ -209,8 +209,8 @@ class ShadeListBuilderLogger @Inject constructor(
fun logParentChangeSuppressedStopped(
buildId: Int,
- previouslySuppressedParent: GroupEntry?,
- previouslyKeptParent: GroupEntry?
+ previouslySuppressedParent: PipelineEntry?,
+ previouslyKeptParent: PipelineEntry?
) {
buffer.log(TAG, INFO, {
long1 = buildId.toLong()
@@ -224,7 +224,7 @@ class ShadeListBuilderLogger @Inject constructor(
fun logGroupPruningSuppressed(
buildId: Int,
- keepingParent: GroupEntry?
+ keepingParent: PipelineEntry?
) {
buffer.log(TAG, INFO, {
long1 = buildId.toLong()
@@ -310,7 +310,7 @@ class ShadeListBuilderLogger @Inject constructor(
val logRankInFinalList = Compile.IS_DEBUG && notifPipelineFlags.isDevLoggingEnabled()
- fun logFinalList(entries: List<ListEntry>) {
+ fun logFinalList(entries: List<PipelineEntry>) {
if (entries.isEmpty()) {
buffer.log(TAG, DEBUG, {}, { "(empty list)" })
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/listbuilder/pluggable/NotifComparator.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/listbuilder/pluggable/NotifComparator.java
index f7bbd281ec51..f7c74c217cdf 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/listbuilder/pluggable/NotifComparator.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/listbuilder/pluggable/NotifComparator.java
@@ -18,7 +18,7 @@ package com.android.systemui.statusbar.notification.collection.listbuilder.plugg
import androidx.annotation.NonNull;
-import com.android.systemui.statusbar.notification.collection.ListEntry;
+import com.android.systemui.statusbar.notification.collection.PipelineEntry;
import com.android.systemui.statusbar.notification.collection.NotifPipeline;
import java.util.Comparator;
@@ -29,7 +29,7 @@ import java.util.List;
*/
public abstract class NotifComparator
extends Pluggable<NotifComparator>
- implements Comparator<ListEntry> {
+ implements Comparator<PipelineEntry> {
protected NotifComparator(String name) {
super(name);
@@ -41,5 +41,5 @@ public abstract class NotifComparator
* @return a negative integer, zero, or a positive integer as the first argument is less than
* equal to, or greater than the second (same as standard Comparator<> interface).
*/
- public abstract int compare(@NonNull ListEntry o1, @NonNull ListEntry o2);
+ public abstract int compare(@NonNull PipelineEntry o1, @NonNull PipelineEntry o2);
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/listbuilder/pluggable/NotifSectioner.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/listbuilder/pluggable/NotifSectioner.java
index 8c52c53ea6b2..4a856a55f185 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/listbuilder/pluggable/NotifSectioner.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/listbuilder/pluggable/NotifSectioner.java
@@ -18,7 +18,7 @@ package com.android.systemui.statusbar.notification.collection.listbuilder.plugg
import android.annotation.Nullable;
-import com.android.systemui.statusbar.notification.collection.ListEntry;
+import com.android.systemui.statusbar.notification.collection.PipelineEntry;
import com.android.systemui.statusbar.notification.collection.ShadeListBuilder;
import com.android.systemui.statusbar.notification.collection.render.NodeController;
import com.android.systemui.statusbar.notification.collection.render.NodeSpec;
@@ -52,11 +52,11 @@ public abstract class NotifSectioner extends Pluggable<NotifSectioner> {
* However, this doesn't necessarily mean that your section will get called on each top-level
* notification. The first section to return true determines the section of the notification.
*/
- public abstract boolean isInSection(ListEntry entry);
+ public abstract boolean isInSection(PipelineEntry entry);
/**
* Returns an optional {@link NotifComparator} to sort entries only in this section.
- * {@link ListEntry} instances passed to this comparator are guaranteed to have this section,
+ * {@link PipelineEntry} instances passed to this comparator are guaranteed to have this section,
* and this ordering will take precedence over any global comparators supplied to {@link
* com.android.systemui.statusbar.notification.collection.NotifPipeline#setComparators(List)}.
*
@@ -80,5 +80,5 @@ public abstract class NotifSectioner extends Pluggable<NotifSectioner> {
* Notify of children of this section being updated
* @param entries of this section that are borrowed (must clone to store)
*/
- public void onEntriesUpdated(List<ListEntry> entries) {}
+ public void onEntriesUpdated(List<PipelineEntry> entries) {}
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/listbuilder/pluggable/NotifStabilityManager.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/listbuilder/pluggable/NotifStabilityManager.kt
index 58bbca822164..e1e2bcff3e31 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/listbuilder/pluggable/NotifStabilityManager.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/listbuilder/pluggable/NotifStabilityManager.kt
@@ -16,7 +16,7 @@
package com.android.systemui.statusbar.notification.collection.listbuilder.pluggable
import com.android.systemui.statusbar.notification.collection.GroupEntry
-import com.android.systemui.statusbar.notification.collection.ListEntry
+import com.android.systemui.statusbar.notification.collection.PipelineEntry
import com.android.systemui.statusbar.notification.collection.NotificationEntry
/**
@@ -75,7 +75,7 @@ abstract class NotifStabilityManager protected constructor(name: String) :
* being suppressed. However, if an order change is suppressed, that will be reported to ths
* implementation by calling [onEntryReorderSuppressed] after ordering is complete.
*/
- abstract fun isEntryReorderingAllowed(entry: ListEntry): Boolean
+ abstract fun isEntryReorderingAllowed(entry: PipelineEntry): Boolean
/**
* Called by the pipeline to determine if every call to the other stability methods would
@@ -100,7 +100,7 @@ object DefaultNotifStabilityManager : NotifStabilityManager("DefaultNotifStabili
override fun isGroupChangeAllowed(entry: NotificationEntry): Boolean = true
override fun isGroupPruneAllowed(entry: GroupEntry): Boolean = true
override fun isSectionChangeAllowed(entry: NotificationEntry): Boolean = true
- override fun isEntryReorderingAllowed(entry: ListEntry): Boolean = true
+ override fun isEntryReorderingAllowed(entry: PipelineEntry): Boolean = true
override fun isEveryChangeAllowed(): Boolean = true
override fun onEntryReorderSuppressed() {}
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/notifcollection/NotifCollectionLogger.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/notifcollection/NotifCollectionLogger.kt
index 014ac549591e..ae2c70a284e9 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/notifcollection/NotifCollectionLogger.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/notifcollection/NotifCollectionLogger.kt
@@ -27,6 +27,7 @@ import com.android.systemui.log.core.LogLevel.ERROR
import com.android.systemui.log.core.LogLevel.INFO
import com.android.systemui.log.core.LogLevel.WARNING
import com.android.systemui.log.core.LogLevel.WTF
+import com.android.systemui.statusbar.notification.collection.GroupEntry
import com.android.systemui.statusbar.notification.collection.NotifCollection
import com.android.systemui.statusbar.notification.collection.NotifCollection.CancellationReason
import com.android.systemui.statusbar.notification.collection.NotifCollection.FutureDismissal
@@ -421,6 +422,14 @@ class NotifCollectionLogger @Inject constructor(
})
}
+ private fun getParentLogKey(childEntry: NotificationEntry): String {
+ return if (childEntry.parent is GroupEntry) {
+ (childEntry.parent as? GroupEntry)?.summary?.logKey ?: "(null)"
+ } else {
+ "(null)"
+ }
+ }
+
fun logDismissAlreadyParentDismissedNotif(
childEntry: NotificationEntry,
childIndex: Int,
@@ -430,7 +439,7 @@ class NotifCollectionLogger @Inject constructor(
str1 = childEntry.logKey
int1 = childIndex
int2 = childCount
- str2 = childEntry.parent?.summary?.logKey ?: "(null)"
+ str2 = getParentLogKey(childEntry)
}, {
"DISMISS Already Parent-Dismissed $str1 ($int1/$int2) with summary $str2"
})
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/provider/HighPriorityProvider.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/provider/HighPriorityProvider.java
index 2e3ab926ad57..051bd3a99549 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/provider/HighPriorityProvider.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/provider/HighPriorityProvider.java
@@ -23,7 +23,7 @@ import android.app.NotificationManager;
import com.android.systemui.dagger.SysUISingleton;
import com.android.systemui.statusbar.notification.collection.GroupEntry;
-import com.android.systemui.statusbar.notification.collection.ListEntry;
+import com.android.systemui.statusbar.notification.collection.PipelineEntry;
import com.android.systemui.statusbar.notification.collection.NotificationEntry;
import com.android.systemui.statusbar.notification.collection.render.GroupMembershipManager;
import com.android.systemui.statusbar.notification.people.PeopleNotificationIdentifier;
@@ -53,7 +53,7 @@ public class HighPriorityProvider {
}
/**
- * @return true if the ListEntry is high priority, else false
+ * @return true if the PipelineEntry is high priority, else false
*
* A NotificationEntry is considered high priority if it:
* - has importance greater than or equal to IMPORTANCE_DEFAULT
@@ -67,12 +67,12 @@ public class HighPriorityProvider {
* A GroupEntry is considered high priority if its representativeEntry (summary) or any of its
* children are high priority.
*/
- public boolean isHighPriority(@Nullable ListEntry entry) {
+ public boolean isHighPriority(@Nullable PipelineEntry entry) {
return isHighPriority(entry, /* allowImplicit = */ true);
}
/**
- * @return true if the ListEntry is explicitly high priority, else false
+ * @return true if the PipelineEntry is explicitly high priority, else false
*
* A NotificationEntry is considered explicitly high priority if has importance greater than or
* equal to IMPORTANCE_DEFAULT.
@@ -80,11 +80,11 @@ public class HighPriorityProvider {
* A GroupEntry is considered explicitly high priority if its representativeEntry (summary) or
* any of its children are explicitly high priority.
*/
- public boolean isExplicitlyHighPriority(@Nullable ListEntry entry) {
+ public boolean isExplicitlyHighPriority(@Nullable PipelineEntry entry) {
return isHighPriority(entry, /* allowImplicit= */ false);
}
- private boolean isHighPriority(@Nullable ListEntry entry, boolean allowImplicit) {
+ private boolean isHighPriority(@Nullable PipelineEntry entry, boolean allowImplicit) {
if (entry == null) {
return false;
}
@@ -100,9 +100,9 @@ public class HighPriorityProvider {
}
/**
- * @return true if the ListEntry is high priority conversation, else false
+ * @return true if the PipelineEntry is high priority conversation, else false
*/
- public boolean isHighPriorityConversation(@NonNull ListEntry entry) {
+ public boolean isHighPriorityConversation(@NonNull PipelineEntry entry) {
final NotificationEntry notifEntry = entry.getRepresentativeEntry();
if (notifEntry == null) {
return false;
@@ -119,7 +119,7 @@ public class HighPriorityProvider {
return isNotificationEntryWithAtLeastOneImportantChild(entry);
}
- private boolean isNotificationEntryWithAtLeastOneImportantChild(@NonNull ListEntry entry) {
+ private boolean isNotificationEntryWithAtLeastOneImportantChild(@NonNull PipelineEntry entry) {
if (!(entry instanceof GroupEntry)) {
return false;
}
@@ -134,7 +134,7 @@ public class HighPriorityProvider {
* Returns whether the given ListEntry has a high priority child or is in a group with a child
* that's high priority
*/
- private boolean hasHighPriorityChild(ListEntry entry, boolean allowImplicit) {
+ private boolean hasHighPriorityChild(PipelineEntry entry, boolean allowImplicit) {
if (NotificationBundleUi.isEnabled()) {
GroupEntry representativeGroupEntry = null;
if (entry instanceof GroupEntry) {
@@ -142,9 +142,10 @@ public class HighPriorityProvider {
} else if (entry instanceof NotificationEntry){
final NotificationEntry notificationEntry = entry.getRepresentativeEntry();
if (notificationEntry.getParent() != null
- && notificationEntry.getParent().getSummary() != null
- && notificationEntry.getParent().getSummary() == notificationEntry) {
- representativeGroupEntry = notificationEntry.getParent();
+ && notificationEntry.getParent() instanceof GroupEntry parent
+ && parent.getSummary() != null
+ && parent.getSummary() == notificationEntry) {
+ representativeGroupEntry = parent;
}
}
return representativeGroupEntry != null &&
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/provider/SectionStyleProvider.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/provider/SectionStyleProvider.kt
index ea9f29521459..f418bb639f49 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/provider/SectionStyleProvider.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/provider/SectionStyleProvider.kt
@@ -17,7 +17,7 @@
package com.android.systemui.statusbar.notification.collection.provider
import com.android.systemui.dagger.SysUISingleton
-import com.android.systemui.statusbar.notification.collection.ListEntry
+import com.android.systemui.statusbar.notification.collection.PipelineEntry
import com.android.systemui.statusbar.notification.collection.SortBySectionTimeFlag
import com.android.systemui.statusbar.notification.collection.listbuilder.NotifSection
import com.android.systemui.statusbar.notification.collection.listbuilder.pluggable.NotifSectioner
@@ -53,7 +53,7 @@ class SectionStyleProvider @Inject constructor(
* Determine if the given entry is minimized.
*/
@JvmOverloads
- fun isMinimized(entry: ListEntry, ifNotInSection: Boolean = true): Boolean {
+ fun isMinimized(entry: PipelineEntry, ifNotInSection: Boolean = true): Boolean {
val section = entry.section ?: return ifNotInSection
return isMinimizedSection(section)
}
@@ -77,7 +77,7 @@ class SectionStyleProvider @Inject constructor(
* Determine if the given entry is silent.
*/
@JvmOverloads
- fun isSilent(entry: ListEntry, ifNotInSection: Boolean = true): Boolean {
+ fun isSilent(entry: PipelineEntry, ifNotInSection: Boolean = true): Boolean {
val section = entry.section ?: return ifNotInSection
if (SortBySectionTimeFlag.isEnabled) {
if (entry.section?.bucket == BUCKET_PEOPLE) {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/render/GroupExpansionManagerImpl.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/render/GroupExpansionManagerImpl.java
index 16b98e20498a..b179a694dad7 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/render/GroupExpansionManagerImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/render/GroupExpansionManagerImpl.java
@@ -25,7 +25,7 @@ import com.android.systemui.dagger.SysUISingleton;
import com.android.systemui.dump.DumpManager;
import com.android.systemui.statusbar.notification.collection.EntryAdapter;
import com.android.systemui.statusbar.notification.collection.GroupEntry;
-import com.android.systemui.statusbar.notification.collection.ListEntry;
+import com.android.systemui.statusbar.notification.collection.PipelineEntry;
import com.android.systemui.statusbar.notification.collection.NotifPipeline;
import com.android.systemui.statusbar.notification.collection.NotificationEntry;
import com.android.systemui.statusbar.notification.collection.listbuilder.OnBeforeRenderListListener;
@@ -81,7 +81,7 @@ public class GroupExpansionManagerImpl implements GroupExpansionManager, Dumpabl
}
final Set<NotificationEntry> renderingSummaries = new HashSet<>();
- for (ListEntry entry : entries) {
+ for (PipelineEntry entry : entries) {
if (entry instanceof GroupEntry) {
renderingSummaries.add(entry.getRepresentativeEntry());
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/render/GroupMembershipManager.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/render/GroupMembershipManager.java
index 69267e5d9e55..3edbfafd7d33 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/render/GroupMembershipManager.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/render/GroupMembershipManager.java
@@ -22,6 +22,7 @@ import android.annotation.Nullable;
import com.android.systemui.statusbar.notification.collection.EntryAdapter;
import com.android.systemui.statusbar.notification.collection.ListEntry;
import com.android.systemui.statusbar.notification.collection.NotificationEntry;
+import com.android.systemui.statusbar.notification.collection.PipelineEntry;
import java.util.List;
@@ -78,5 +79,5 @@ public interface GroupMembershipManager {
* @return list of the children
*/
@Nullable
- List<NotificationEntry> getChildren(@NonNull ListEntry summary);
+ List<NotificationEntry> getChildren(@NonNull PipelineEntry summary);
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/render/GroupMembershipManagerImpl.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/render/GroupMembershipManagerImpl.java
index 80a9f8adf8f3..a1a23e3a0b44 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/render/GroupMembershipManagerImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/render/GroupMembershipManagerImpl.java
@@ -24,7 +24,7 @@ import androidx.annotation.Nullable;
import com.android.systemui.dagger.SysUISingleton;
import com.android.systemui.statusbar.notification.collection.EntryAdapter;
import com.android.systemui.statusbar.notification.collection.GroupEntry;
-import com.android.systemui.statusbar.notification.collection.ListEntry;
+import com.android.systemui.statusbar.notification.collection.PipelineEntry;
import com.android.systemui.statusbar.notification.collection.NotificationEntry;
import com.android.systemui.statusbar.notification.shared.NotificationBundleUi;
@@ -48,8 +48,13 @@ public class GroupMembershipManagerImpl implements GroupMembershipManager {
// The entry is not attached, so it doesn't count.
return false;
}
+ PipelineEntry pipelineEntry = entry.getParent();
+ if (!(pipelineEntry instanceof GroupEntry groupEntry)) {
+ return false;
+ }
+
// If entry is a summary, its parent is a GroupEntry with summary = entry.
- return entry.getParent().getSummary() == entry;
+ return groupEntry.getSummary() == entry;
}
@Override
@@ -65,7 +70,10 @@ public class GroupMembershipManagerImpl implements GroupMembershipManager {
if (isTopLevelEntry(entry) || entry.getParent() == null) {
return null;
}
- return entry.getParent().getSummary();
+ if (entry.getParent() instanceof GroupEntry parent) {
+ return parent.getSummary();
+ }
+ return null;
}
@Nullable
@@ -91,8 +99,9 @@ public class GroupMembershipManagerImpl implements GroupMembershipManager {
@Nullable
@Override
- public List<NotificationEntry> getChildren(@NonNull ListEntry entry) {
+ public List<NotificationEntry> getChildren(@NonNull PipelineEntry entry) {
NotificationBundleUi.assertInLegacyMode();
+
if (entry instanceof GroupEntry) {
return ((GroupEntry) entry).getChildren();
}
@@ -100,8 +109,7 @@ public class GroupMembershipManagerImpl implements GroupMembershipManager {
NotificationEntry representativeEntry = entry.getRepresentativeEntry();
if (representativeEntry != null && isGroupSummary(representativeEntry)) {
// maybe we were actually passed the summary
- GroupEntry parent = representativeEntry.getParent();
- if (parent != null) {
+ if (representativeEntry.getParent() instanceof GroupEntry parent) {
return parent.getChildren();
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/render/NodeSpecBuilder.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/render/NodeSpecBuilder.kt
index 9fc4e4036b31..e17319484cce 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/render/NodeSpecBuilder.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/render/NodeSpecBuilder.kt
@@ -18,12 +18,12 @@ package com.android.systemui.statusbar.notification.collection.render
import com.android.systemui.statusbar.notification.NotificationSectionsFeatureManager
import com.android.systemui.statusbar.notification.collection.GroupEntry
-import com.android.systemui.statusbar.notification.collection.ListEntry
import com.android.systemui.statusbar.notification.collection.NotificationEntry
import com.android.systemui.statusbar.notification.collection.listbuilder.NotifSection
import com.android.systemui.statusbar.notification.collection.provider.SectionHeaderVisibilityProvider
import com.android.systemui.util.Compile
import com.android.app.tracing.traceSection
+import com.android.systemui.statusbar.notification.collection.PipelineEntry
/**
* Converts a notif list (the output of the ShadeListBuilder) into a NodeSpec, an abstract
@@ -45,7 +45,7 @@ class NodeSpecBuilder(
fun buildNodeSpec(
rootController: NodeController,
- notifList: List<ListEntry>
+ notifList: List<PipelineEntry>
): NodeSpec = traceSection("NodeSpecBuilder.buildNodeSpec") {
val root = NodeSpecImpl(null, rootController)
@@ -100,7 +100,7 @@ class NodeSpecBuilder(
return@traceSection root
}
- private fun buildNotifNode(parent: NodeSpec, entry: ListEntry): NodeSpec = when (entry) {
+ private fun buildNotifNode(parent: NodeSpec, entry: PipelineEntry): NodeSpec = when (entry) {
is NotificationEntry -> NodeSpecImpl(parent, viewBarn.requireNodeController(entry))
is GroupEntry ->
NodeSpecImpl(parent, viewBarn.requireNodeController(checkNotNull(entry.summary)))
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/render/NotifViewBarn.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/render/NotifViewBarn.kt
index fd91d5a2c082..56662f105e4a 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/render/NotifViewBarn.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/render/NotifViewBarn.kt
@@ -18,18 +18,18 @@ package com.android.systemui.statusbar.notification.collection.render
import android.view.textclassifier.Log
import com.android.systemui.dagger.SysUISingleton
-import com.android.systemui.statusbar.notification.collection.ListEntry
+import com.android.systemui.statusbar.notification.collection.PipelineEntry
import com.android.systemui.statusbar.notification.collection.NotificationEntry
import javax.inject.Inject
/**
- * The ViewBarn is just a map from [ListEntry] to an instance of a [NodeController].
+ * The ViewBarn is just a map from [PipelineEntry] to an instance of a [NodeController].
*/
@SysUISingleton
class NotifViewBarn @Inject constructor() {
private val rowMap = mutableMapOf<String, NotifViewController>()
- fun requireNodeController(entry: ListEntry): NodeController {
+ fun requireNodeController(entry: PipelineEntry): NodeController {
if (DEBUG) {
Log.d(TAG, "requireNodeController: ${entry.key}")
}
@@ -50,14 +50,14 @@ class NotifViewBarn @Inject constructor() {
return rowMap[entry.key] ?: error("No view has been registered for entry: ${entry.key}")
}
- fun registerViewForEntry(entry: ListEntry, controller: NotifViewController) {
+ fun registerViewForEntry(entry: PipelineEntry, controller: NotifViewController) {
if (DEBUG) {
Log.d(TAG, "registerViewForEntry: ${entry.key}")
}
rowMap[entry.key] = controller
}
- fun removeViewForEntry(entry: ListEntry) {
+ fun removeViewForEntry(entry: PipelineEntry) {
if (DEBUG) {
Log.d(TAG, "removeViewForEntry: ${entry.key}")
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/render/NotifViewRenderer.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/render/NotifViewRenderer.kt
index 8284022c7270..396d47e7096c 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/render/NotifViewRenderer.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/render/NotifViewRenderer.kt
@@ -17,7 +17,7 @@
package com.android.systemui.statusbar.notification.collection.render
import com.android.systemui.statusbar.notification.collection.GroupEntry
-import com.android.systemui.statusbar.notification.collection.ListEntry
+import com.android.systemui.statusbar.notification.collection.PipelineEntry
import com.android.systemui.statusbar.notification.collection.NotificationEntry
/**
@@ -34,7 +34,7 @@ interface NotifViewRenderer {
* also ensure that future calls to [getStackController], [getGroupController], and
* [getRowController] will provide valid results.
*/
- fun onRenderList(notifList: List<ListEntry>)
+ fun onRenderList(notifList: List<PipelineEntry>)
/**
* Provides an interface for the pipeline to update individual groups. This will be called at
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/render/RenderStageManager.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/render/RenderStageManager.kt
index 21e68376031c..93e844de79d9 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/render/RenderStageManager.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/render/RenderStageManager.kt
@@ -19,7 +19,7 @@ package com.android.systemui.statusbar.notification.collection.render
import com.android.app.tracing.traceSection
import com.android.systemui.dagger.SysUISingleton
import com.android.systemui.statusbar.notification.collection.GroupEntry
-import com.android.systemui.statusbar.notification.collection.ListEntry
+import com.android.systemui.statusbar.notification.collection.PipelineEntry
import com.android.systemui.statusbar.notification.collection.NotificationEntry
import com.android.systemui.statusbar.notification.collection.PipelineDumpable
import com.android.systemui.statusbar.notification.collection.PipelineDumper
@@ -46,7 +46,7 @@ class RenderStageManager @Inject constructor() : PipelineDumpable {
listBuilder.setOnRenderListListener(::onRenderList)
}
- private fun onRenderList(notifList: List<ListEntry>) {
+ private fun onRenderList(notifList: List<PipelineEntry>) {
traceSection("RenderStageManager.onRenderList") {
val viewRenderer = viewRenderer ?: return
viewRenderer.onRenderList(notifList)
@@ -85,7 +85,7 @@ class RenderStageManager @Inject constructor() : PipelineDumpable {
dump("onAfterRenderEntryListeners", onAfterRenderEntryListeners)
}
- private fun dispatchOnAfterRenderList(entries: List<ListEntry>) {
+ private fun dispatchOnAfterRenderList(entries: List<PipelineEntry>) {
traceSection("RenderStageManager.dispatchOnAfterRenderList") {
onAfterRenderListListeners.forEach { listener -> listener.onAfterRenderList(entries) }
}
@@ -93,7 +93,7 @@ class RenderStageManager @Inject constructor() : PipelineDumpable {
private fun dispatchOnAfterRenderGroups(
viewRenderer: NotifViewRenderer,
- entries: List<ListEntry>,
+ entries: List<PipelineEntry>,
) {
traceSection("RenderStageManager.dispatchOnAfterRenderGroups") {
if (onAfterRenderGroupListeners.isEmpty()) {
@@ -110,7 +110,7 @@ class RenderStageManager @Inject constructor() : PipelineDumpable {
private fun dispatchOnAfterRenderEntries(
viewRenderer: NotifViewRenderer,
- entries: List<ListEntry>,
+ entries: List<PipelineEntry>,
) {
traceSection("RenderStageManager.dispatchOnAfterRenderEntries") {
if (onAfterRenderEntryListeners.isEmpty()) {
@@ -129,7 +129,7 @@ class RenderStageManager @Inject constructor() : PipelineDumpable {
* Performs a forward, depth-first traversal of the list where the group's summary immediately
* precedes the group's children.
*/
- private inline fun List<ListEntry>.forEachNotificationEntry(
+ private inline fun List<PipelineEntry>.forEachNotificationEntry(
action: (NotificationEntry) -> Unit
) {
forEach { entry ->
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/render/ShadeViewManager.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/render/ShadeViewManager.kt
index 72316bf14c9a..9532d6d89cf7 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/render/ShadeViewManager.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/render/ShadeViewManager.kt
@@ -22,7 +22,7 @@ import com.android.app.tracing.traceSection
import com.android.systemui.shade.ShadeDisplayAware
import com.android.systemui.statusbar.notification.NotificationSectionsFeatureManager
import com.android.systemui.statusbar.notification.collection.GroupEntry
-import com.android.systemui.statusbar.notification.collection.ListEntry
+import com.android.systemui.statusbar.notification.collection.PipelineEntry
import com.android.systemui.statusbar.notification.collection.NotificationEntry
import com.android.systemui.statusbar.notification.collection.PipelineDumpable
import com.android.systemui.statusbar.notification.collection.PipelineDumper
@@ -76,7 +76,7 @@ constructor(
private val viewRenderer =
object : NotifViewRenderer {
- override fun onRenderList(notifList: List<ListEntry>) {
+ override fun onRenderList(notifList: List<PipelineEntry>) {
traceSection("ShadeViewManager.onRenderList") {
viewDiffer.applySpec(specBuilder.buildNodeSpec(rootController, notifList))
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/domain/interactor/RenderNotificationListInteractor.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/domain/interactor/RenderNotificationListInteractor.kt
index bde3c4d8c632..adc049e7cdf1 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/domain/interactor/RenderNotificationListInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/domain/interactor/RenderNotificationListInteractor.kt
@@ -29,7 +29,7 @@ import com.android.app.tracing.traceSection
import com.android.systemui.dagger.qualifiers.Main
import com.android.systemui.statusbar.StatusBarIconView
import com.android.systemui.statusbar.notification.collection.GroupEntry
-import com.android.systemui.statusbar.notification.collection.ListEntry
+import com.android.systemui.statusbar.notification.collection.PipelineEntry
import com.android.systemui.statusbar.notification.collection.NotificationEntry
import com.android.systemui.statusbar.notification.collection.provider.SectionStyleProvider
import com.android.systemui.statusbar.notification.data.repository.ActiveNotificationListRepository
@@ -57,11 +57,11 @@ constructor(
/**
* Sets the current list of rendered notification entries as displayed in the notification list.
*/
- fun setRenderedList(entries: List<ListEntry>) {
+ fun setRenderedList(entries: List<PipelineEntry>) {
traceSection("RenderNotificationListInteractor.setRenderedList") {
repository.activeNotifications.update { existingModels ->
buildActiveNotificationsStore(existingModels, sectionStyleProvider, context) {
- entries.forEach(::addListEntry)
+ entries.forEach(::addPipelineEntry)
setRankingsMap(entries)
}
}
@@ -89,11 +89,11 @@ private class ActiveNotificationsStoreBuilder(
fun build(): ActiveNotificationsStore = builder.build()
/**
- * Convert a [ListEntry] into [ActiveNotificationEntryModel]s, and add them to the
+ * Convert a [PipelineEntry] into [ActiveNotificationEntryModel]s, and add them to the
* [ActiveNotificationsStore]. Special care is taken to avoid re-allocating models if the result
* would be identical to an existing model (at the expense of additional computations).
*/
- fun addListEntry(entry: ListEntry) {
+ fun addPipelineEntry(entry: PipelineEntry) {
when (entry) {
is GroupEntry -> {
entry.summary?.let { summary ->
@@ -116,11 +116,11 @@ private class ActiveNotificationsStoreBuilder(
}
}
- fun setRankingsMap(entries: List<ListEntry>) {
+ fun setRankingsMap(entries: List<PipelineEntry>) {
builder.setRankingsMap(flatMapToRankingsMap(entries))
}
- fun flatMapToRankingsMap(entries: List<ListEntry>): Map<String, Int> {
+ fun flatMapToRankingsMap(entries: List<PipelineEntry>): Map<String, Int> {
val result = ArrayMap<String, Int>()
for (entry in entries) {
if (entry is NotificationEntry) {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/interruption/KeyguardNotificationVisibilityProvider.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/interruption/KeyguardNotificationVisibilityProvider.kt
index c7548aae7131..5096894911c8 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/interruption/KeyguardNotificationVisibilityProvider.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/interruption/KeyguardNotificationVisibilityProvider.kt
@@ -19,7 +19,7 @@ import com.android.systemui.settings.UserTracker
import com.android.systemui.statusbar.NotificationLockscreenUserManager
import com.android.systemui.statusbar.StatusBarState
import com.android.systemui.statusbar.SysuiStatusBarStateController
-import com.android.systemui.statusbar.notification.collection.ListEntry
+import com.android.systemui.statusbar.notification.collection.PipelineEntry
import com.android.systemui.statusbar.notification.collection.NotificationEntry
import com.android.systemui.statusbar.notification.collection.provider.HighPriorityProvider
import com.android.systemui.statusbar.policy.KeyguardStateController
@@ -198,7 +198,7 @@ constructor(
else -> false
}
- private fun shouldHideIfEntrySilent(entry: ListEntry): Boolean =
+ private fun shouldHideIfEntrySilent(entry: PipelineEntry): Boolean =
when {
// Show if explicitly high priority (not hidden)
highPriorityProvider.isExplicitlyHighPriority(entry) -> false
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/people/PeopleNotificationIdentifier.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/people/PeopleNotificationIdentifier.kt
index f755dbb48e1d..002230e4e516 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/people/PeopleNotificationIdentifier.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/people/PeopleNotificationIdentifier.kt
@@ -20,6 +20,7 @@ import android.annotation.IntDef
import android.service.notification.NotificationListenerService.Ranking
import android.service.notification.StatusBarNotification
import com.android.systemui.dagger.SysUISingleton
+import com.android.systemui.statusbar.notification.collection.GroupEntry
import com.android.systemui.statusbar.notification.collection.NotificationEntry
import com.android.systemui.statusbar.notification.collection.render.GroupMembershipManager
import com.android.systemui.statusbar.notification.people.PeopleNotificationIdentifier.Companion.PeopleNotificationType
@@ -117,8 +118,8 @@ class PeopleNotificationIdentifierImpl @Inject constructor(
if (!entry.sbn.notification.isGroupSummary) {
return TYPE_NON_PERSON;
}
-
- return getPeopleTypeForChildList(entry.parent?.children)
+ val parent = entry.parent as? GroupEntry ?: return TYPE_NON_PERSON
+ return getPeopleTypeForChildList(parent.children)
} else {
if (!groupManager.isGroupSummary(entry)) {
return TYPE_NON_PERSON
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/promoted/AODPromotedNotification.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/promoted/AODPromotedNotification.kt
index 9aa5a2e32ada..7959e99f3189 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/promoted/AODPromotedNotification.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/promoted/AODPromotedNotification.kt
@@ -17,6 +17,7 @@
package com.android.systemui.statusbar.notification.promoted
import android.app.Flags
+import android.app.Flags.notificationsRedesignTemplates
import android.app.Notification
import android.graphics.PorterDuff
import android.view.LayoutInflater
@@ -166,7 +167,10 @@ private class AODPromotedNotificationViewUpdater(root: View) {
private val closeButton: View? = root.findViewById(R.id.close_button)
private val conversationIconBadge: View? = root.findViewById(R.id.conversation_icon_badge)
private val conversationIcon: CachingIconView? = root.findViewById(R.id.conversation_icon)
- private val conversationText: TextView? = root.findViewById(R.id.conversation_text)
+ private val conversationText: TextView? =
+ root.findViewById(
+ if (notificationsRedesignTemplates()) R.id.title else R.id.conversation_text
+ )
private val expandButton: NotificationExpandButton? = root.findViewById(R.id.expand_button)
private val headerText: TextView? = root.findViewById(R.id.header_text)
private val headerTextDivider: View? = root.findViewById(R.id.header_text_divider)
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRow.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRow.java
index 64cd6174a585..d2800d7c1b71 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRow.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRow.java
@@ -46,6 +46,7 @@ import android.graphics.drawable.AnimationDrawable;
import android.graphics.drawable.Drawable;
import android.os.Build;
import android.os.Bundle;
+import android.os.SystemClock;
import android.os.SystemProperties;
import android.os.Trace;
import android.os.UserHandle;
@@ -117,7 +118,6 @@ import com.android.systemui.statusbar.notification.headsup.PinnedStatus;
import com.android.systemui.statusbar.notification.logging.NotificationCounters;
import com.android.systemui.statusbar.notification.people.PeopleNotificationIdentifier;
import com.android.systemui.statusbar.notification.promoted.PromotedNotificationUiForceExpanded;
-import com.android.systemui.statusbar.notification.row.NotificationRowContentBinder.InflationFlag;
import com.android.systemui.statusbar.notification.row.shared.AsyncGroupHeaderViewInflation;
import com.android.systemui.statusbar.notification.row.shared.LockscreenOtpRedaction;
import com.android.systemui.statusbar.notification.row.wrapper.NotificationCompactMessagingTemplateViewWrapper;
@@ -169,6 +169,7 @@ public class ExpandableNotificationRow extends ActivatableNotificationView
private static final long RECENTLY_ALERTED_THRESHOLD_MS = TimeUnit.SECONDS.toMillis(30);
private static final SourceType BASE_VALUE = SourceType.from("BaseValue");
private static final SourceType FROM_PARENT = SourceType.from("FromParent(ENR)");
+ private static final long INITIALIZATION_DELAY = 400;
// We don't correctly track dark mode until the content views are inflated, so always update
// the background on first content update just in case it happens to be during a theme change.
@@ -267,7 +268,6 @@ public class ExpandableNotificationRow extends ActivatableNotificationView
private NotificationContentView mPublicLayout;
private NotificationContentView mPrivateLayout;
private NotificationContentView[] mLayouts;
- private int mNotificationColor;
private ExpandableNotificationRowLogger mLogger;
private String mLoggingKey;
private NotificationGuts mGuts;
@@ -352,6 +352,10 @@ public class ExpandableNotificationRow extends ActivatableNotificationView
*/
private boolean mSaveSpaceOnLockscreen;
+ // indicates when this view was first attached to a window
+ // this value will reset when the view is completely removed from the shade (ie: filtered out)
+ private long initializationTime = -1;
+
/**
* It is added for unit testing purpose.
* Please do not use it for other purposes.
@@ -625,26 +629,11 @@ public class ExpandableNotificationRow extends ActivatableNotificationView
}
/**
- * Marks a content view as freeable, setting it so that future inflations do not reinflate
- * and ensuring that the view is freed when it is safe to remove.
- *
- * @param inflationFlag flag corresponding to the content view to be freed
- * @deprecated By default, {@link NotificationRowContentBinder#unbindContent} will tell the
- * view hierarchy to only free when the view is safe to remove so this method is no longer
- * needed. Will remove when all uses are gone.
- */
- @Deprecated
- public void freeContentViewWhenSafe(@InflationFlag int inflationFlag) {
- RowContentBindParams params = mRowContentBindStage.getStageParams(mEntry);
- params.markContentViewsFreeable(inflationFlag);
- mRowContentBindStage.requestRebind(mEntry, null /* callback */);
- }
-
- /**
* Returns whether this row is considered non-blockable (i.e. it's a non-blockable system notif
* or is in an allowList).
*/
public boolean getIsNonblockable() {
+ NotificationBundleUi.assertInLegacyMode();
if (mEntry == null) {
return true;
}
@@ -666,9 +655,8 @@ public class ExpandableNotificationRow extends ActivatableNotificationView
l.onNotificationUpdated(mEntry);
}
mShowingPublicInitialized = false;
- updateNotificationColor();
if (mMenuRow != null) {
- mMenuRow.onNotificationUpdated(mEntry.getSbn());
+ mMenuRow.onNotificationUpdated();
mMenuRow.setAppName(mAppName);
}
if (mIsSummaryWithChildren) {
@@ -727,15 +715,6 @@ public class ExpandableNotificationRow extends ActivatableNotificationView
}
/**
- * Called when the notification's ranking was changed (but nothing else changed).
- */
- public void onNotificationRankingUpdated() {
- if (mMenuRow != null) {
- mMenuRow.onNotificationUpdated(mEntry.getSbn());
- }
- }
-
- /**
* Call when bubble state has changed and the button on the notification should be updated.
*/
public void updateBubbleButton() {
@@ -746,7 +725,7 @@ public class ExpandableNotificationRow extends ActivatableNotificationView
@VisibleForTesting
void updateShelfIconColor() {
- StatusBarIconView expandedIcon = mEntry.getIcons().getShelfIcon();
+ StatusBarIconView expandedIcon = getShelfIcon();
boolean isPreL = Boolean.TRUE.equals(expandedIcon.getTag(R.id.icon_is_pre_L));
boolean colorize = !isPreL || NotificationUtils.isGrayscale(expandedIcon,
ContrastColorUtil.getInstance(mContext));
@@ -767,8 +746,13 @@ public class ExpandableNotificationRow extends ActivatableNotificationView
if (color != Notification.COLOR_INVALID) {
return color;
} else {
- return mEntry.getContrastedColor(mContext, mIsMinimized && !isExpanded(),
- getBackgroundColorWithoutTint());
+ if (NotificationBundleUi.isEnabled()) {
+ return mEntryAdapter.getContrastedColor(mContext, mIsMinimized && !isExpanded(),
+ getBackgroundColorWithoutTint());
+ } else {
+ return mEntry.getContrastedColor(mContext, mIsMinimized && !isExpanded(),
+ getBackgroundColorWithoutTint());
+ }
}
}
@@ -870,15 +854,29 @@ public class ExpandableNotificationRow extends ActivatableNotificationView
boolean customView = contractedView != null
&& contractedView.getId()
!= com.android.internal.R.id.status_bar_latest_event_content;
- boolean beforeN = mEntry.targetSdk < Build.VERSION_CODES.N;
- boolean beforeP = mEntry.targetSdk < Build.VERSION_CODES.P;
- boolean beforeS = mEntry.targetSdk < Build.VERSION_CODES.S;
+ int targetSdk = Build.VERSION_CODES.CUR_DEVELOPMENT;
+ if (NotificationBundleUi.isEnabled()) {
+ targetSdk = mEntryAdapter.getTargetSdk();
+ } else {
+ targetSdk = mEntry.targetSdk;
+ }
+
+ boolean beforeN = targetSdk < Build.VERSION_CODES.N;
+ boolean beforeP = targetSdk < Build.VERSION_CODES.P;
+ boolean beforeS = targetSdk < Build.VERSION_CODES.S;
int smallHeight;
boolean isCallLayout = contractedView instanceof CallLayout;
boolean isMessagingLayout = contractedView instanceof MessagingLayout
|| contractedView instanceof ConversationLayout;
+ String summarization = null;
+ if (NotificationBundleUi.isEnabled()) {
+ summarization = mEntryAdapter.getSummarization();
+ } else {
+ summarization = mEntry.getRanking().getSummarization();
+ }
+
if (customView && beforeS && !mIsSummaryWithChildren) {
if (beforeN) {
smallHeight = mMaxSmallHeightBeforeN;
@@ -891,7 +889,7 @@ public class ExpandableNotificationRow extends ActivatableNotificationView
smallHeight = maxExpandedHeight;
} else if (NmSummarizationUiFlag.isEnabled()
&& isMessagingLayout
- && !TextUtils.isEmpty(mEntry.getRanking().getSummarization())) {
+ && !TextUtils.isEmpty(summarization)) {
smallHeight = mMaxSmallHeightWithSummarization;
} else {
smallHeight = mMaxSmallHeight;
@@ -1524,7 +1522,22 @@ public class ExpandableNotificationRow extends ActivatableNotificationView
@Override
public boolean hasFinishedInitialization() {
- return getEntry().hasFinishedInitialization();
+ if (NotificationBundleUi.isEnabled()) {
+ return initializationTime != -1
+ && SystemClock.elapsedRealtime() > initializationTime + INITIALIZATION_DELAY;
+ } else {
+ return getEntry().hasFinishedInitialization();
+ }
+ }
+
+ public void resetInitializationTime() {
+ initializationTime = -1;
+ }
+
+ public void setInitializationTime(long time) {
+ if (initializationTime == -1) {
+ initializationTime = time;
+ }
}
/**
@@ -1539,7 +1552,7 @@ public class ExpandableNotificationRow extends ActivatableNotificationView
return null;
}
if (mMenuRow.getMenuView() == null) {
- mMenuRow.createMenu(this, mEntry.getSbn());
+ mMenuRow.createMenu(this);
mMenuRow.setAppName(mAppName);
FrameLayout.LayoutParams lp = new LayoutParams(LayoutParams.MATCH_PARENT,
LayoutParams.MATCH_PARENT);
@@ -1581,7 +1594,7 @@ public class ExpandableNotificationRow extends ActivatableNotificationView
if (oldMenu != null) {
int menuIndex = indexOfChild(oldMenu);
removeView(oldMenu);
- mMenuRow.createMenu(ExpandableNotificationRow.this, mEntry.getSbn());
+ mMenuRow.createMenu(ExpandableNotificationRow.this);
mMenuRow.setAppName(mAppName);
addView(mMenuRow.getMenuView(), menuIndex);
}
@@ -1589,12 +1602,17 @@ public class ExpandableNotificationRow extends ActivatableNotificationView
l.reinflate();
l.reInflateViews();
}
- mEntry.getSbn().clearPackageContext();
+ if (NotificationBundleUi.isEnabled()) {
+ mEntryAdapter.prepareForInflation();
+ } else {
+ mEntry.getSbn().clearPackageContext();
+ }
// TODO: Move content inflation logic out of this call
RowContentBindParams params = mRowContentBindStage.getStageParams(mEntry);
params.setNeedsReinflation(true);
- var rebindEndCallback = mRebindingTracker.trackRebinding(mEntry.getKey());
+ var rebindEndCallback = mRebindingTracker.trackRebinding(NotificationBundleUi.isEnabled()
+ ? mEntryAdapter.getKey() : mEntry.getKey());
mRowContentBindStage.requestRebind(mEntry, (e) -> rebindEndCallback.onFinished());
Trace.endSection();
}
@@ -1658,20 +1676,6 @@ public class ExpandableNotificationRow extends ActivatableNotificationView
mPrivateLayout.setSingleLineWidthIndention(indention);
}
- public int getNotificationColor() {
- return mNotificationColor;
- }
-
- public void updateNotificationColor() {
- Configuration currentConfig = getResources().getConfiguration();
- boolean nightMode = (currentConfig.uiMode & Configuration.UI_MODE_NIGHT_MASK)
- == Configuration.UI_MODE_NIGHT_YES;
-
- mNotificationColor = ContrastColorUtil.resolveContrastColor(mContext,
- mEntry.getSbn().getNotification().color,
- getBackgroundColorWithoutTint(), nightMode);
- }
-
public HybridNotificationView getSingleLineView() {
return mPrivateLayout.getSingleLineView();
}
@@ -2178,7 +2182,7 @@ public class ExpandableNotificationRow extends ActivatableNotificationView
R.dimen.notification_min_height);
}
mMaxSmallHeightWithSummarization = NotificationUtils.getFontScaledHeight(mContext,
- com.android.internal.R.dimen.notification_collapsed_height_with_summarization);
+ com.android.internal.R.dimen.notification_min_height);
mMaxExpandedHeight = NotificationUtils.getFontScaledHeight(mContext,
R.dimen.notification_max_height);
mMaxExpandedHeightForPromotedOngoing = NotificationUtils.getFontScaledHeight(mContext,
@@ -2260,6 +2264,11 @@ public class ExpandableNotificationRow extends ActivatableNotificationView
mEntry = entry;
}
+ @VisibleForTesting
+ protected void setEntryAdapter(EntryAdapter entry) {
+ mEntryAdapter = entry;
+ }
+
private final Runnable mExpireRecentlyAlertedFlag = () -> applyAudiblyAlertedRecently(false);
private void applyAudiblyAlertedRecently(boolean audiblyAlertedRecently) {
@@ -2497,7 +2506,7 @@ public class ExpandableNotificationRow extends ActivatableNotificationView
mTranslateableViews.get(i).setTranslationX(0);
}
invalidateOutline();
- getEntry().getIcons().getShelfIcon().setScrollX(0);
+ getShelfIcon().setScrollX(0);
}
if (mMenuRow != null) {
@@ -2616,7 +2625,7 @@ public class ExpandableNotificationRow extends ActivatableNotificationView
// In order to keep the shelf in sync with this swiping, we're simply translating
// it's icon by the same amount. The translation is already being used for the normal
// positioning, so we can use the scrollX instead.
- getEntry().getIcons().getShelfIcon().setScrollX((int) -translationX);
+ getShelfIcon().setScrollX((int) -translationX);
}
if (mMenuRow != null && mMenuRow.getMenuView() != null) {
@@ -2843,7 +2852,11 @@ public class ExpandableNotificationRow extends ActivatableNotificationView
@Override
public @NonNull StatusBarIconView getShelfIcon() {
- return getEntry().getIcons().getShelfIcon();
+ if (NotificationBundleUi.isEnabled()) {
+ return getEntryAdapter().getIcons().getShelfIcon();
+ } else {
+ return mEntry.getIcons().getShelfIcon();
+ }
}
@Override
@@ -3072,8 +3085,11 @@ public class ExpandableNotificationRow extends ActivatableNotificationView
* except for legacy use cases.
*/
public boolean canShowHeadsUp() {
+ boolean canEntryHun = NotificationBundleUi.isEnabled()
+ ? mEntryAdapter.canPeek()
+ : mEntry.isStickyAndNotDemoted();
if (mOnKeyguard && !isDozing() && !isBypassEnabled() &&
- (!mEntry.isStickyAndNotDemoted()
+ (!canEntryHun
|| (!mIgnoreLockscreenConstraints && mSaveSpaceOnLockscreen))) {
return false;
}
@@ -3112,7 +3128,11 @@ public class ExpandableNotificationRow extends ActivatableNotificationView
}
if (!mIsSummaryWithChildren && wasSummary) {
// Reset the 'when' once the row stops being a summary
- mPublicLayout.setNotificationWhen(mEntry.getSbn().getNotification().getWhen());
+ if (NotificationBundleUi.isEnabled()) {
+ mPublicLayout.setNotificationWhen(mEntryAdapter.getWhen());
+ } else {
+ mPublicLayout.setNotificationWhen(mEntry.getSbn().getNotification().getWhen());
+ }
}
getShowingLayout().updateBackgroundColor(false /* animate */);
mPrivateLayout.updateExpandButtons(isExpandable());
@@ -3381,7 +3401,7 @@ public class ExpandableNotificationRow extends ActivatableNotificationView
*/
@Override
public boolean canExpandableViewBeDismissed() {
- if (areGutsExposed() || !mEntry.hasFinishedInitialization()) {
+ if (areGutsExposed() || !hasFinishedInitialization()) {
return false;
}
return canViewBeDismissed();
@@ -3405,7 +3425,12 @@ public class ExpandableNotificationRow extends ActivatableNotificationView
* clearability see {@link NotificationEntry#isClearable()}.
*/
public boolean canViewBeCleared() {
- return mEntry.isClearable() && (!shouldShowPublic() || !mSensitiveHiddenInGeneral);
+ if (NotificationBundleUi.isEnabled()) {
+ return mEntryAdapter.isClearable()
+ && (!shouldShowPublic() || !mSensitiveHiddenInGeneral);
+ } else {
+ return mEntry.isClearable() && (!shouldShowPublic() || !mSensitiveHiddenInGeneral);
+ }
}
private boolean shouldShowPublic() {
@@ -4082,6 +4107,7 @@ public class ExpandableNotificationRow extends ActivatableNotificationView
}
public boolean isMediaRow() {
+ NotificationBundleUi.assertInLegacyMode();
return mEntry.getSbn().getNotification().isMediaNotification();
}
@@ -4204,7 +4230,11 @@ public class ExpandableNotificationRow extends ActivatableNotificationView
public void dump(PrintWriter pwOriginal, String[] args) {
IndentingPrintWriter pw = DumpUtilsKt.asIndenting(pwOriginal);
// Skip super call; dump viewState ourselves
- pw.println("Notification: " + mEntry.getKey());
+ if (NotificationBundleUi.isEnabled()) {
+ pw.println("Notification: " + mEntryAdapter.getKey());
+ } else {
+ pw.println("Notification: " + mEntry.getKey());
+ }
DumpUtilsKt.withIncreasedIndent(pw, () -> {
pw.println(this);
pw.print("visibility: " + getVisibility());
@@ -4235,6 +4265,7 @@ public class ExpandableNotificationRow extends ActivatableNotificationView
pw.print(", isPinned: " + isPinned());
pw.print(", expandedWhenPinned: " + mExpandedWhenPinned);
pw.print(", isMinimized: " + mIsMinimized);
+ pw.print(", isAboveShelf: " + isAboveShelf());
pw.println();
if (NotificationContentView.INCLUDE_HEIGHTS_TO_DUMP) {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRowController.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRowController.java
index b43a387a5edb..07711b6e0eb8 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRowController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRowController.java
@@ -374,7 +374,11 @@ public class ExpandableNotificationRowController implements NotifViewController
mView.addOnAttachStateChangeListener(new View.OnAttachStateChangeListener() {
@Override
public void onViewAttachedToWindow(View v) {
- mView.getEntry().setInitializationTime(mClock.elapsedRealtime());
+ if (NotificationBundleUi.isEnabled()) {
+ mView.setInitializationTime(mClock.elapsedRealtime());
+ } else {
+ mView.getEntry().setInitializationTime(mClock.elapsedRealtime());
+ }
mPluginManager.addPluginListener(mView,
NotificationMenuRowPlugin.class, false /* Allow multiple */);
if (!SceneContainerFlag.isEnabled()) {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationGutsManager.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationGutsManager.java
index 6e638f5de209..9a75253295d5 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationGutsManager.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationGutsManager.java
@@ -73,6 +73,7 @@ import com.android.systemui.statusbar.notification.collection.render.NotifGutsVi
import com.android.systemui.statusbar.notification.headsup.HeadsUpManager;
import com.android.systemui.statusbar.notification.row.icon.AppIconProvider;
import com.android.systemui.statusbar.notification.row.icon.NotificationIconStyleProvider;
+import com.android.systemui.statusbar.notification.shared.NotificationBundleUi;
import com.android.systemui.statusbar.notification.stack.NotificationListContainer;
import com.android.systemui.statusbar.phone.CentralSurfaces;
import com.android.systemui.statusbar.policy.DeviceProvisionedController;
@@ -436,7 +437,9 @@ public class NotificationGutsManager implements NotifGutsViewManager, CoreStarta
onNasFeedbackClick,
mUiEventLogger,
mDeviceProvisionedController.isDeviceProvisioned(),
- row.getIsNonblockable(),
+ NotificationBundleUi.isEnabled()
+ ? !row.getEntry().isBlockable()
+ : row.getIsNonblockable(),
mHighPriorityProvider.isHighPriority(row.getEntry()),
mAssistantFeedbackController,
mMetricsLogger,
@@ -480,7 +483,9 @@ public class NotificationGutsManager implements NotifGutsViewManager, CoreStarta
row.getEntry(),
onSettingsClick,
mDeviceProvisionedController.isDeviceProvisioned(),
- row.getIsNonblockable());
+ NotificationBundleUi.isEnabled()
+ ? !row.getEntry().isBlockable()
+ : row.getIsNonblockable());
}
/**
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationMenuRow.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationMenuRow.java
index b96b224a7d2e..ab382df13d10 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationMenuRow.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationMenuRow.java
@@ -186,7 +186,7 @@ public class NotificationMenuRow implements NotificationMenuRowPlugin, View.OnCl
}
@Override
- public void createMenu(ViewGroup parent, StatusBarNotification sbn) {
+ public void createMenu(ViewGroup parent) {
mParent = (ExpandableNotificationRow) parent;
createMenuViews(true /* resetState */);
}
@@ -227,7 +227,7 @@ public class NotificationMenuRow implements NotificationMenuRowPlugin, View.OnCl
}
@Override
- public void onNotificationUpdated(StatusBarNotification sbn) {
+ public void onNotificationUpdated() {
if (mMenuContainer == null) {
// Menu hasn't been created yet, no need to do anything.
return;
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/wrapper/NotificationCallTemplateViewWrapper.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/wrapper/NotificationCallTemplateViewWrapper.kt
index 2d946945597e..e887e25b74a2 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/wrapper/NotificationCallTemplateViewWrapper.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/wrapper/NotificationCallTemplateViewWrapper.kt
@@ -16,6 +16,7 @@
package com.android.systemui.statusbar.notification.row.wrapper
+import android.app.Flags.notificationsRedesignTemplates
import android.content.Context
import android.view.View
import com.android.internal.widget.CachingIconView
@@ -25,17 +26,13 @@ import com.android.systemui.statusbar.notification.NotificationFadeAware
import com.android.systemui.statusbar.notification.NotificationUtils
import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow
-/**
- * Wraps a notification containing a call template
- */
-class NotificationCallTemplateViewWrapper constructor(
- ctx: Context,
- view: View,
- row: ExpandableNotificationRow
-) : NotificationTemplateViewWrapper(ctx, view, row) {
+/** Wraps a notification containing a call template */
+class NotificationCallTemplateViewWrapper
+constructor(ctx: Context, view: View, row: ExpandableNotificationRow) :
+ NotificationTemplateViewWrapper(ctx, view, row) {
private val minHeightWithActions: Int =
- NotificationUtils.getFontScaledHeight(ctx, R.dimen.notification_max_height)
+ NotificationUtils.getFontScaledHeight(ctx, R.dimen.notification_max_height)
private val callLayout: CallLayout = view as CallLayout
private lateinit var conversationIconContainer: View
@@ -48,13 +45,17 @@ class NotificationCallTemplateViewWrapper constructor(
private fun resolveViews() {
with(callLayout) {
conversationIconContainer =
- requireViewById(com.android.internal.R.id.conversation_icon_container)
+ requireViewById(com.android.internal.R.id.conversation_icon_container)
conversationIconView = requireViewById(com.android.internal.R.id.conversation_icon)
conversationBadgeBg =
- requireViewById(com.android.internal.R.id.conversation_icon_badge_bg)
+ requireViewById(com.android.internal.R.id.conversation_icon_badge_bg)
expandBtn = requireViewById(com.android.internal.R.id.expand_button)
appName = requireViewById(com.android.internal.R.id.app_name_text)
- conversationTitleView = requireViewById(com.android.internal.R.id.conversation_text)
+ conversationTitleView =
+ requireViewById(
+ if (notificationsRedesignTemplates()) com.android.internal.R.id.title
+ else com.android.internal.R.id.conversation_text
+ )
}
}
@@ -68,20 +69,12 @@ class NotificationCallTemplateViewWrapper constructor(
override fun updateTransformedTypes() {
// This also clears the existing types
super.updateTransformedTypes()
- addTransformedViews(
- appName,
- conversationTitleView
- )
- addViewsTransformingToSimilar(
- conversationIconView,
- conversationBadgeBg,
- expandBtn
- )
+ addTransformedViews(appName, conversationTitleView)
+ addViewsTransformingToSimilar(conversationIconView, conversationBadgeBg, expandBtn)
}
override fun disallowSingleClick(x: Float, y: Float): Boolean {
- val isOnExpandButton = expandBtn.visibility == View.VISIBLE &&
- isOnView(expandBtn, x, y)
+ val isOnExpandButton = expandBtn.visibility == View.VISIBLE && isOnView(expandBtn, x, y)
return isOnExpandButton || super.disallowSingleClick(x, y)
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/wrapper/NotificationConversationTemplateViewWrapper.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/wrapper/NotificationConversationTemplateViewWrapper.kt
index 9d13ab53ec71..6a96fba8f28d 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/wrapper/NotificationConversationTemplateViewWrapper.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/wrapper/NotificationConversationTemplateViewWrapper.kt
@@ -17,10 +17,12 @@
package com.android.systemui.statusbar.notification.row.wrapper
import android.app.Flags
+import android.app.Flags.notificationsRedesignTemplates
import android.content.Context
import android.graphics.drawable.AnimatedImageDrawable
import android.view.View
import android.view.ViewGroup
+import androidx.core.view.isVisible
import com.android.internal.widget.CachingIconView
import com.android.internal.widget.ConversationLayout
import com.android.internal.widget.MessagingGroup
@@ -53,8 +55,8 @@ class NotificationConversationTemplateViewWrapper(
private lateinit var badgeIconView: NotificationRowIconView
private lateinit var conversationBadgeBg: View
private lateinit var expandBtn: View
- private lateinit var expandBtnContainer: View
- private lateinit var imageMessageContainer: ViewGroup
+ private var expandBtnContainer: View? = null
+ private var imageMessageContainer: ViewGroup? = null
private lateinit var messageContainers: ArrayList<MessagingGroup>
private lateinit var messagingLinearLayout: MessagingLinearLayout
private lateinit var conversationTitleView: View
@@ -78,10 +80,14 @@ class NotificationConversationTemplateViewWrapper(
conversationBadgeBg =
requireViewById(com.android.internal.R.id.conversation_icon_badge_bg)
expandBtn = requireViewById(com.android.internal.R.id.expand_button)
- expandBtnContainer = requireViewById(com.android.internal.R.id.expand_button_container)
+ expandBtnContainer = findViewById(com.android.internal.R.id.expand_button_container)
importanceRing = requireViewById(com.android.internal.R.id.conversation_icon_badge_ring)
appName = requireViewById(com.android.internal.R.id.app_name_text)
- conversationTitleView = requireViewById(com.android.internal.R.id.conversation_text)
+ conversationTitleView =
+ requireViewById(
+ if (notificationsRedesignTemplates()) com.android.internal.R.id.title
+ else com.android.internal.R.id.conversation_text
+ )
facePileTop = findViewById(com.android.internal.R.id.conversation_face_pile_top)
facePileBottom = findViewById(com.android.internal.R.id.conversation_face_pile_bottom)
facePileBottomBg =
@@ -133,11 +139,21 @@ class NotificationConversationTemplateViewWrapper(
expandable: Boolean,
onClickListener: View.OnClickListener,
requestLayout: Boolean,
- ) = conversationLayout.updateExpandability(expandable, onClickListener)
+ ) {
+ if (notificationsRedesignTemplates()) {
+ super.updateExpandability(expandable, onClickListener, requestLayout)
+ } else {
+ conversationLayout.updateExpandability(expandable, onClickListener)
+ }
+ }
override fun disallowSingleClick(x: Float, y: Float): Boolean {
val isOnExpandButton =
- expandBtnContainer.visibility == View.VISIBLE && isOnView(expandBtnContainer, x, y)
+ if (notificationsRedesignTemplates()) {
+ expandBtn.isVisible && isOnView(expandBtn, x, y)
+ } else {
+ expandBtnContainer?.visibility == View.VISIBLE && isOnView(expandBtnContainer, x, y)
+ }
return isOnExpandButton || super.disallowSingleClick(x, y)
}
@@ -158,7 +174,8 @@ class NotificationConversationTemplateViewWrapper(
// and the top level image message container.
val containers =
messageContainers.asSequence().map { it.messageContainer } +
- sequenceOf(imageMessageContainer)
+ if (notificationsRedesignTemplates()) emptySequence()
+ else sequenceOf(imageMessageContainer!!)
val drawables =
containers
.flatMap { it.children }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayout.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayout.java
index 3d60e03d7ca4..3ff18efeae53 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayout.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayout.java
@@ -6686,7 +6686,7 @@ public class NotificationStackScrollLayout
static boolean canChildBeCleared(View v) {
if (v instanceof ExpandableNotificationRow row) {
- if (row.areGutsExposed() || !row.getEntry().hasFinishedInitialization()) {
+ if (row.areGutsExposed() || !row.hasFinishedInitialization()) {
return false;
}
return row.canViewBeCleared();
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/StackScrollAlgorithm.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/StackScrollAlgorithm.java
index 06b989aaab57..08692bea2292 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/StackScrollAlgorithm.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/StackScrollAlgorithm.java
@@ -1227,13 +1227,17 @@ public class StackScrollAlgorithm {
float baseZ = ambientState.getBaseZHeight();
if (SceneContainerFlag.isEnabled()) {
- // SceneContainer flags off this logic, and just sets the baseZ because:
+ // SceneContainer simplifies this logic, because:
// - there are no overlapping HUNs anymore, no need for multiplying their shadows
// - shadows for HUNs overlapping with the stack are now set from updateHeadsUpStates
- // - shadows for HUNs overlapping with the shelf are NOT set anymore, because it only
- // happens on AOD/Pulsing, where they're displayed on a black background so a shadow
- // wouldn't be visible.
- childViewState.setZTranslation(baseZ);
+ if (child.isPinned() || ambientState.getTrackedHeadsUpRow() == child) {
+ // set a default elevation on the HUN, which would be overridden
+ // from updateHeadsUpStates if it is displayed in the shade
+ childViewState.setZTranslation(baseZ + mPinnedZTranslationExtra);
+ } else {
+ // set baseZ for every notification
+ childViewState.setZTranslation(baseZ);
+ }
} else {
if (child.mustStayOnScreen() && !childViewState.headsUpIsVisible
&& !ambientState.isDozingAndNotPulsing(child)
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/ui/viewmodel/SharedNotificationContainerViewModel.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/ui/viewmodel/SharedNotificationContainerViewModel.kt
index 5c81c8e22bfc..34b65603542c 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/ui/viewmodel/SharedNotificationContainerViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/ui/viewmodel/SharedNotificationContainerViewModel.kt
@@ -20,6 +20,7 @@ package com.android.systemui.statusbar.notification.stack.ui.viewmodel
import android.content.Context
import androidx.annotation.VisibleForTesting
import com.android.app.tracing.coroutines.flow.flowName
+import com.android.systemui.Flags.glanceableHubV2
import com.android.systemui.common.shared.model.NotificationContainerBounds
import com.android.systemui.common.ui.domain.interactor.ConfigurationInteractor
import com.android.systemui.communal.domain.interactor.CommunalSceneInteractor
@@ -87,7 +88,9 @@ import com.android.systemui.statusbar.notification.domain.interactor.HeadsUpNoti
import com.android.systemui.statusbar.notification.stack.domain.interactor.NotificationStackAppearanceInteractor
import com.android.systemui.statusbar.notification.stack.domain.interactor.SharedNotificationContainerInteractor
import com.android.systemui.unfold.domain.interactor.UnfoldTransitionInteractor
+import com.android.systemui.util.kotlin.BooleanFlowOperators.allOf
import com.android.systemui.util.kotlin.BooleanFlowOperators.anyOf
+import com.android.systemui.util.kotlin.BooleanFlowOperators.not
import com.android.systemui.util.kotlin.FlowDumperImpl
import com.android.systemui.util.kotlin.Utils.Companion.sample as sampleCombine
import com.android.systemui.util.kotlin.sample
@@ -299,20 +302,40 @@ constructor(
.distinctUntilChanged()
.dumpWhileCollecting("configurationBasedDimensions")
+ private val isOnAnyBouncer: Flow<Boolean> =
+ anyOf(
+ keyguardTransitionInteractor.transitionValue(ALTERNATE_BOUNCER).map { it > 0f },
+ keyguardTransitionInteractor
+ .transitionValue(
+ scene = Scenes.Bouncer,
+ stateWithoutSceneContainer = PRIMARY_BOUNCER,
+ )
+ .map { it > 0f },
+ )
+
/** If the user is visually on one of the unoccluded lockscreen states. */
val isOnLockscreen: Flow<Boolean> =
- anyOf(
- keyguardTransitionInteractor.transitionValue(AOD).map { it > 0f },
- keyguardTransitionInteractor.transitionValue(DOZING).map { it > 0f },
- keyguardTransitionInteractor.transitionValue(ALTERNATE_BOUNCER).map { it > 0f },
- keyguardTransitionInteractor
- .transitionValue(
- scene = Scenes.Bouncer,
- stateWithoutSceneContainer = PRIMARY_BOUNCER,
- )
- .map { it > 0f },
- keyguardTransitionInteractor.transitionValue(LOCKSCREEN).map { it > 0f },
- )
+ if (glanceableHubV2()) {
+ anyOf(
+ keyguardTransitionInteractor.transitionValue(AOD).map { it > 0f },
+ keyguardTransitionInteractor.transitionValue(DOZING).map { it > 0f },
+ keyguardTransitionInteractor.transitionValue(LOCKSCREEN).map { it > 0f },
+ allOf(
+ // Exclude bouncer showing over communal hub, as this should not be
+ // considered
+ // "lockscreen"
+ not(communalSceneInteractor.isCommunalVisible),
+ isOnAnyBouncer,
+ ),
+ )
+ } else {
+ anyOf(
+ keyguardTransitionInteractor.transitionValue(AOD).map { it > 0f },
+ keyguardTransitionInteractor.transitionValue(DOZING).map { it > 0f },
+ keyguardTransitionInteractor.transitionValue(LOCKSCREEN).map { it > 0f },
+ isOnAnyBouncer,
+ )
+ }
.flowName("isOnLockscreen")
.stateIn(
scope = applicationScope,
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/domain/interactor/MobileIconsInteractor.kt b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/domain/interactor/MobileIconsInteractor.kt
index c52536d2b312..10821dffd394 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/domain/interactor/MobileIconsInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/domain/interactor/MobileIconsInteractor.kt
@@ -21,7 +21,6 @@ import android.telephony.CarrierConfigManager
import android.telephony.SubscriptionManager
import android.telephony.SubscriptionManager.PROFILE_CLASS_PROVISIONING
import com.android.settingslib.SignalIcon.MobileIconGroup
-import com.android.settingslib.flags.Flags
import com.android.settingslib.mobile.TelephonyIcons
import com.android.systemui.dagger.SysUISingleton
import com.android.systemui.dagger.qualifiers.Background
@@ -29,11 +28,13 @@ import com.android.systemui.flags.FeatureFlagsClassic
import com.android.systemui.flags.Flags.FILTER_PROVISIONING_NETWORK_SUBSCRIPTIONS
import com.android.systemui.log.table.TableLogBuffer
import com.android.systemui.log.table.logDiffsForTable
+import com.android.systemui.statusbar.core.NewStatusBarIcons
import com.android.systemui.statusbar.core.StatusBarRootModernization
import com.android.systemui.statusbar.pipeline.dagger.MobileSummaryLog
import com.android.systemui.statusbar.pipeline.mobile.data.model.SubscriptionModel
import com.android.systemui.statusbar.pipeline.mobile.data.repository.MobileConnectionRepository
import com.android.systemui.statusbar.pipeline.mobile.data.repository.MobileConnectionsRepository
+import com.android.systemui.statusbar.pipeline.mobile.domain.model.SignalIconModel
import com.android.systemui.statusbar.pipeline.shared.data.model.ConnectivitySlot
import com.android.systemui.statusbar.pipeline.shared.data.repository.ConnectivityRepository
import com.android.systemui.statusbar.policy.data.repository.UserSetupRepository
@@ -85,6 +86,12 @@ interface MobileIconsInteractor {
/** Whether the mobile icons can be stacked vertically. */
val isStackable: StateFlow<Boolean>
+ /**
+ * Observable for the subscriptionId of the current mobile data connection. Null if we don't
+ * have a valid subscription id
+ */
+ val activeMobileDataSubscriptionId: StateFlow<Int?>
+
/** True if the active mobile data subscription has data enabled */
val activeDataConnectionHasDataEnabled: StateFlow<Boolean>
@@ -168,6 +175,9 @@ constructor(
)
.stateIn(scope, SharingStarted.WhileSubscribed(), false)
+ override val activeMobileDataSubscriptionId: StateFlow<Int?> =
+ mobileConnectionsRepo.activeMobileDataSubscriptionId
+
override val activeDataConnectionHasDataEnabled: StateFlow<Boolean> =
mobileConnectionsRepo.activeMobileDataRepository
.flatMapLatest { it?.dataEnabled ?: flowOf(false) }
@@ -298,10 +308,16 @@ constructor(
.stateIn(scope, SharingStarted.WhileSubscribed(), emptyList())
override val isStackable =
- if (Flags.newStatusBarIcons() && StatusBarRootModernization.isEnabled) {
+ if (NewStatusBarIcons.isEnabled && StatusBarRootModernization.isEnabled) {
icons.flatMapLatest { icons ->
- combine(icons.map { it.isNonTerrestrial }) {
- it.size == 2 && it.none { isNonTerrestrial -> isNonTerrestrial }
+ combine(icons.map { it.signalLevelIcon }) { signalLevelIcons ->
+ // These are only stackable if:
+ // - They are cellular
+ // - There's exactly two
+ // - They have the same number of levels
+ signalLevelIcons.filterIsInstance<SignalIconModel.Cellular>().let {
+ it.size == 2 && it[0].numberOfLevels == it[1].numberOfLevels
+ }
}
}
} else {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/ui/viewmodel/MobileIconsViewModel.kt b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/ui/viewmodel/MobileIconsViewModel.kt
index 6176a3e9e281..288e49eac5a8 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/ui/viewmodel/MobileIconsViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/ui/viewmodel/MobileIconsViewModel.kt
@@ -63,6 +63,8 @@ constructor(
@VisibleForTesting
val reuseCache = ConcurrentHashMap<Int, Pair<MobileIconViewModel, CoroutineScope>>()
+ val activeMobileDataSubscriptionId: StateFlow<Int?> = interactor.activeMobileDataSubscriptionId
+
val subscriptionIdsFlow: StateFlow<List<Int>> =
interactor.filteredSubscriptions
.mapLatest { subscriptions ->
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/ui/viewmodel/StackedMobileIconViewModel.kt b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/ui/viewmodel/StackedMobileIconViewModel.kt
index a2c2a3cd1507..2c85a5150575 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/ui/viewmodel/StackedMobileIconViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/ui/viewmodel/StackedMobileIconViewModel.kt
@@ -25,7 +25,7 @@ import com.android.systemui.statusbar.pipeline.mobile.domain.model.SignalIconMod
import dagger.assisted.AssistedFactory
import dagger.assisted.AssistedInject
import kotlinx.coroutines.ExperimentalCoroutinesApi
-import kotlinx.coroutines.flow.StateFlow
+import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.combine
import kotlinx.coroutines.flow.flatMapLatest
import kotlinx.coroutines.flow.flowOf
@@ -43,8 +43,14 @@ constructor(mobileIconsViewModel: MobileIconsViewModel) : ExclusiveActivatable()
initialValue = false,
)
- private val iconViewModelFlow: StateFlow<List<MobileIconViewModelCommon>> =
- mobileIconsViewModel.mobileSubViewModels
+ private val iconViewModelFlow: Flow<List<MobileIconViewModelCommon>> =
+ combine(
+ mobileIconsViewModel.mobileSubViewModels,
+ mobileIconsViewModel.activeMobileDataSubscriptionId,
+ ) { viewModels, activeSubId ->
+ // Sort to get the active subscription first, if it's set
+ viewModels.sortedByDescending { it.subscriptionId == activeSubId }
+ }
val dualSim: DualSim? by
hydrator.hydratedStateOf(
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/shared/ui/binder/HomeStatusBarViewBinder.kt b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/shared/ui/binder/HomeStatusBarViewBinder.kt
index cd320a12d577..d7348892356d 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/shared/ui/binder/HomeStatusBarViewBinder.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/shared/ui/binder/HomeStatusBarViewBinder.kt
@@ -31,6 +31,8 @@ import com.android.systemui.scene.shared.flag.SceneContainerFlag
import com.android.systemui.statusbar.chips.mediaprojection.domain.model.MediaProjectionStopDialogModel
import com.android.systemui.statusbar.chips.notification.shared.StatusBarNotifChips
import com.android.systemui.statusbar.chips.ui.binder.OngoingActivityChipBinder
+import com.android.systemui.statusbar.chips.ui.binder.OngoingActivityChipViewBinding
+import com.android.systemui.statusbar.chips.ui.model.MultipleOngoingActivityChipsModelLegacy
import com.android.systemui.statusbar.chips.ui.model.OngoingActivityChipModel
import com.android.systemui.statusbar.core.StatusBarConnectedDisplays
import com.android.systemui.statusbar.core.StatusBarRootModernization
@@ -149,6 +151,7 @@ constructor(
if (!StatusBarNotifChips.isEnabled && !StatusBarChipsModernization.isEnabled) {
val primaryChipViewBinding =
OngoingActivityChipBinder.createBinding(primaryChipView)
+
launch {
viewModel.primaryOngoingActivityChip.collect { primaryChipModel ->
OngoingActivityChipBinder.bind(
@@ -156,18 +159,14 @@ constructor(
primaryChipViewBinding,
iconViewStore,
)
- if (StatusBarRootModernization.isEnabled) {
- when (primaryChipModel) {
- is OngoingActivityChipModel.Active ->
- primaryChipViewBinding.rootView.show(
- shouldAnimateChange = true
- )
- is OngoingActivityChipModel.Inactive ->
- primaryChipViewBinding.rootView.hide(
- state = View.GONE,
- shouldAnimateChange = primaryChipModel.shouldAnimate,
- )
+ if (StatusBarRootModernization.isEnabled) {
+ launch {
+ bindLegacyPrimaryOngoingActivityChipWithVisibility(
+ viewModel,
+ primaryChipModel,
+ primaryChipViewBinding,
+ )
}
} else {
when (primaryChipModel) {
@@ -213,12 +212,14 @@ constructor(
)
if (StatusBarRootModernization.isEnabled) {
- primaryChipViewBinding.rootView.adjustVisibility(
- chips.primary.toVisibilityModel()
- )
- secondaryChipViewBinding.rootView.adjustVisibility(
- chips.secondary.toVisibilityModel()
- )
+ launch {
+ bindOngoingActivityChipsWithVisibility(
+ viewModel,
+ chips,
+ primaryChipViewBinding,
+ secondaryChipViewBinding,
+ )
+ }
} else {
listener?.onOngoingActivityStatusChanged(
hasPrimaryOngoingActivity =
@@ -312,6 +313,52 @@ constructor(
}
}
+ /** Bind the (legacy) single primary ongoing activity chip with the status bar visibility */
+ private suspend fun bindLegacyPrimaryOngoingActivityChipWithVisibility(
+ viewModel: HomeStatusBarViewModel,
+ primaryChipModel: OngoingActivityChipModel,
+ primaryChipViewBinding: OngoingActivityChipViewBinding,
+ ) {
+ viewModel.canShowOngoingActivityChips.collectLatest { visible ->
+ if (!visible) {
+ primaryChipViewBinding.rootView.hide(shouldAnimateChange = false)
+ } else {
+ when (primaryChipModel) {
+ is OngoingActivityChipModel.Active -> {
+ primaryChipViewBinding.rootView.show(shouldAnimateChange = true)
+ }
+
+ is OngoingActivityChipModel.Inactive -> {
+ primaryChipViewBinding.rootView.hide(
+ state = View.GONE,
+ shouldAnimateChange = primaryChipModel.shouldAnimate,
+ )
+ }
+ }
+ }
+ }
+ }
+
+ /** Bind the primary/secondary chips along with the home status bar's visibility */
+ private suspend fun bindOngoingActivityChipsWithVisibility(
+ viewModel: HomeStatusBarViewModel,
+ chips: MultipleOngoingActivityChipsModelLegacy,
+ primaryChipViewBinding: OngoingActivityChipViewBinding,
+ secondaryChipViewBinding: OngoingActivityChipViewBinding,
+ ) {
+ viewModel.canShowOngoingActivityChips.collectLatest { canShow ->
+ if (!canShow) {
+ primaryChipViewBinding.rootView.hide(shouldAnimateChange = false)
+ secondaryChipViewBinding.rootView.hide(shouldAnimateChange = false)
+ } else {
+ primaryChipViewBinding.rootView.adjustVisibility(chips.primary.toVisibilityModel())
+ secondaryChipViewBinding.rootView.adjustVisibility(
+ chips.secondary.toVisibilityModel()
+ )
+ }
+ }
+ }
+
private fun SystemEventAnimationState.isAnimatingChip() =
when (this) {
AnimatingIn,
@@ -374,43 +421,42 @@ constructor(
if (visibility == View.INVISIBLE || visibility == View.GONE) {
return
}
+ alpha = 0f
visibility = state
}
// See CollapsedStatusBarFragment#hide.
private fun View.hide(state: Int = View.INVISIBLE, shouldAnimateChange: Boolean) {
+ animate().cancel()
if (visibility == View.INVISIBLE || visibility == View.GONE) {
return
}
- val v = this
- v.animate().cancel()
if (!shouldAnimateChange) {
- v.alpha = 0f
- v.visibility = state
+ alpha = 0f
+ visibility = state
return
}
- v.animate()
+ animate()
.alpha(0f)
.setDuration(CollapsedStatusBarFragment.FADE_OUT_DURATION.toLong())
.setStartDelay(0)
.setInterpolator(Interpolators.ALPHA_OUT)
- .withEndAction { v.visibility = state }
+ .withEndAction { visibility = state }
}
// See CollapsedStatusBarFragment#show.
private fun View.show(shouldAnimateChange: Boolean) {
- if (visibility == View.VISIBLE) {
+ animate().cancel()
+ if (visibility == View.VISIBLE && alpha >= 1f) {
return
}
- val v = this
- v.animate().cancel()
- v.visibility = View.VISIBLE
+ visibility = View.VISIBLE
if (!shouldAnimateChange) {
- v.alpha = 1f
+ alpha = 1f
return
}
- v.animate()
+ animate()
.alpha(1f)
.setDuration(CollapsedStatusBarFragment.FADE_IN_DURATION.toLong())
.setInterpolator(Interpolators.ALPHA_IN)
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/shared/ui/composable/StackedMobileIcon.kt b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/shared/ui/composable/StackedMobileIcon.kt
index 465a43fbfb9e..a51da0e4cc73 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/shared/ui/composable/StackedMobileIcon.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/shared/ui/composable/StackedMobileIcon.kt
@@ -17,9 +17,12 @@
package com.android.systemui.statusbar.pipeline.shared.ui.composable
import androidx.compose.foundation.Canvas
+import androidx.compose.foundation.layout.Arrangement.spacedBy
import androidx.compose.foundation.layout.Row
+import androidx.compose.foundation.layout.fillMaxHeight
+import androidx.compose.foundation.layout.height
import androidx.compose.foundation.layout.padding
-import androidx.compose.foundation.layout.size
+import androidx.compose.foundation.layout.width
import androidx.compose.material3.LocalContentColor
import androidx.compose.runtime.Composable
import androidx.compose.ui.Alignment
@@ -27,23 +30,28 @@ import androidx.compose.ui.Modifier
import androidx.compose.ui.geometry.CornerRadius
import androidx.compose.ui.geometry.Offset
import androidx.compose.ui.geometry.Size
+import androidx.compose.ui.graphics.BlendMode
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.graphics.drawscope.DrawScope
import androidx.compose.ui.platform.LocalDensity
import androidx.compose.ui.unit.TextUnit
-import androidx.compose.ui.unit.dp
import androidx.compose.ui.unit.sp
-import com.android.compose.modifiers.height
-import com.android.compose.modifiers.width
import com.android.systemui.common.ui.compose.Icon
import com.android.systemui.statusbar.pipeline.mobile.ui.viewmodel.StackedMobileIconViewModel
import com.android.systemui.statusbar.pipeline.shared.ui.composable.StackedMobileIconDimensions.BarBaseHeightFiveBarsSp
import com.android.systemui.statusbar.pipeline.shared.ui.composable.StackedMobileIconDimensions.BarBaseHeightFourBarsSp
import com.android.systemui.statusbar.pipeline.shared.ui.composable.StackedMobileIconDimensions.BarsLevelIncrementSp
import com.android.systemui.statusbar.pipeline.shared.ui.composable.StackedMobileIconDimensions.BarsVerticalPaddingSp
+import com.android.systemui.statusbar.pipeline.shared.ui.composable.StackedMobileIconDimensions.ExclamationCutoutRadiusSp
+import com.android.systemui.statusbar.pipeline.shared.ui.composable.StackedMobileIconDimensions.ExclamationDiameterSp
+import com.android.systemui.statusbar.pipeline.shared.ui.composable.StackedMobileIconDimensions.ExclamationHeightSp
+import com.android.systemui.statusbar.pipeline.shared.ui.composable.StackedMobileIconDimensions.ExclamationHorizontalOffset
+import com.android.systemui.statusbar.pipeline.shared.ui.composable.StackedMobileIconDimensions.ExclamationVerticalSpacing
import com.android.systemui.statusbar.pipeline.shared.ui.composable.StackedMobileIconDimensions.HorizontalPaddingFiveBarsSp
import com.android.systemui.statusbar.pipeline.shared.ui.composable.StackedMobileIconDimensions.HorizontalPaddingFourBarsSp
import com.android.systemui.statusbar.pipeline.shared.ui.composable.StackedMobileIconDimensions.IconHeightSp
+import com.android.systemui.statusbar.pipeline.shared.ui.composable.StackedMobileIconDimensions.IconPaddingSp
+import com.android.systemui.statusbar.pipeline.shared.ui.composable.StackedMobileIconDimensions.IconSpacingSp
import com.android.systemui.statusbar.pipeline.shared.ui.composable.StackedMobileIconDimensions.IconWidthFiveBarsSp
import com.android.systemui.statusbar.pipeline.shared.ui.composable.StackedMobileIconDimensions.IconWidthFourBarsSp
import com.android.systemui.statusbar.pipeline.shared.ui.composable.StackedMobileIconDimensions.SecondaryBarHeightSp
@@ -58,15 +66,16 @@ fun StackedMobileIcon(viewModel: StackedMobileIconViewModel, modifier: Modifier
val dualSim = viewModel.dualSim ?: return
val contentColor = LocalContentColor.current
-
- Row(verticalAlignment = Alignment.CenterVertically, modifier = modifier) {
+ val padding = with(LocalDensity.current) { IconPaddingSp.toDp() }
+ val horizontalArrangement = with(LocalDensity.current) { spacedBy(IconSpacingSp.toDp()) }
+
+ Row(
+ horizontalArrangement = horizontalArrangement,
+ verticalAlignment = Alignment.CenterVertically,
+ modifier = modifier.padding(horizontal = padding),
+ ) {
viewModel.networkTypeIcon?.let {
- Icon(
- it,
- tint = contentColor,
- modifier =
- Modifier.height { IconHeightSp.roundToPx() }.padding(start = 1.dp, end = 2.dp),
- )
+ Icon(it, tint = contentColor, modifier = Modifier.fillMaxHeight())
}
StackedMobileIcon(dualSim, contentColor)
@@ -79,23 +88,23 @@ private fun StackedMobileIcon(
color: Color,
modifier: Modifier = Modifier,
) {
- val maxNumberOfLevels =
- max(viewModel.primary.numberOfLevels, viewModel.secondary.numberOfLevels)
- val dimensions = if (maxNumberOfLevels == 6) FiveBarsDimensions else FourBarsDimensions
+ // Removing 1 to get the real number of bars
+ val numberOfBars = max(viewModel.primary.numberOfLevels, viewModel.secondary.numberOfLevels) - 1
+ val dimensions = if (numberOfBars == 5) FiveBarsDimensions else FourBarsDimensions
val iconSize =
with(LocalDensity.current) { dimensions.totalWidth.toDp() to IconHeightSp.toDp() }
- Canvas(modifier.size(width = iconSize.first, height = iconSize.second)) {
+ Canvas(modifier.width(iconSize.first).height(iconSize.second)) {
val verticalPaddingPx = BarsVerticalPaddingSp.roundToPx()
val horizontalPaddingPx = dimensions.barsHorizontalPadding.roundToPx()
- val totalPaddingWidthPx = horizontalPaddingPx * (maxNumberOfLevels - 1)
+ val totalPaddingWidthPx = horizontalPaddingPx * (numberOfBars - 1)
- val barWidthPx = (size.width - totalPaddingWidthPx) / maxNumberOfLevels
+ val barWidthPx = (size.width - totalPaddingWidthPx) / numberOfBars
val dotHeightPx = SecondaryBarHeightSp.toPx()
val baseBarHeightPx = dimensions.barBaseHeight.toPx()
var xOffsetPx = 0f
- for (bar in 1..maxNumberOfLevels) {
+ for (bar in 1..numberOfBars) {
// Bottom dots representing secondary sim
val dotYOffsetPx = size.height - dotHeightPx
if (bar <= viewModel.secondary.numberOfLevels) {
@@ -123,6 +132,10 @@ private fun StackedMobileIcon(
xOffsetPx += barWidthPx + horizontalPaddingPx
}
+
+ if (viewModel.primary.showExclamationMark) {
+ drawExclamationCutout(color)
+ }
}
}
@@ -143,6 +156,39 @@ private fun DrawScope.drawMobileIconBar(
)
}
+private fun DrawScope.drawExclamationCutout(color: Color) {
+ // Exclamation mark is bottom aligned with the canvas
+ val exclamationDiameterPx = ExclamationDiameterSp.toPx()
+ val exclamationRadiusPx = ExclamationDiameterSp.toPx() / 2
+ val exclamationTotalHeight =
+ ExclamationHeightSp.toPx() + ExclamationVerticalSpacing.toPx() + exclamationDiameterPx
+ val exclamationDotCenter =
+ Offset(size.width - ExclamationHorizontalOffset.toPx(), size.height - exclamationRadiusPx)
+ val exclamationMarkTopLeft =
+ Offset(exclamationDotCenter.x - exclamationRadiusPx, size.height - exclamationTotalHeight)
+ val exclamationCornerRadius = CornerRadius(exclamationRadiusPx)
+ val cutoutCenter = Offset(exclamationDotCenter.x, size.height - (exclamationTotalHeight / 2))
+
+ // Transparent cutout
+ drawCircle(
+ color = Color.Transparent,
+ radius = ExclamationCutoutRadiusSp.toPx(),
+ center = cutoutCenter,
+ blendMode = BlendMode.SrcIn,
+ )
+
+ // Top bar for the exclamation mark
+ drawRoundRect(
+ color = color,
+ topLeft = exclamationMarkTopLeft,
+ size = Size(exclamationDiameterPx, ExclamationHeightSp.toPx()),
+ cornerRadius = exclamationCornerRadius,
+ )
+
+ // Bottom circle for the exclamation mark
+ drawCircle(color = color, center = exclamationDotCenter, radius = exclamationRadiusPx)
+}
+
private abstract class BarsDependentDimensions(
val totalWidth: TextUnit,
val barsHorizontalPadding: TextUnit,
@@ -166,10 +212,19 @@ private object FiveBarsDimensions :
private object StackedMobileIconDimensions {
// Common dimensions
val IconHeightSp = 12.sp
+ val IconPaddingSp = 4.sp
+ val IconSpacingSp = 2.sp
val BarsVerticalPaddingSp = 1.5.sp
val BarsLevelIncrementSp = 1.sp
val SecondaryBarHeightSp = 3.sp
+ // Exclamation cutout dimensions
+ val ExclamationCutoutRadiusSp = 5.sp
+ val ExclamationDiameterSp = 1.5.sp
+ val ExclamationHeightSp = 4.5.sp
+ val ExclamationVerticalSpacing = 1.sp
+ val ExclamationHorizontalOffset = 1.sp
+
// Dimensions dependant on the number of total bars
val IconWidthFiveBarsSp = 18.5.sp
val IconWidthFourBarsSp = 16.sp
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/shared/ui/viewmodel/HomeStatusBarViewModel.kt b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/shared/ui/viewmodel/HomeStatusBarViewModel.kt
index 9ae2cb203d91..807e90567eb7 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/shared/ui/viewmodel/HomeStatusBarViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/shared/ui/viewmodel/HomeStatusBarViewModel.kt
@@ -149,6 +149,9 @@ interface HomeStatusBarViewModel : Activatable {
*/
val isHomeStatusBarAllowedByScene: StateFlow<Boolean>
+ /** True if the home status bar is showing, and there is no HUN happening */
+ val canShowOngoingActivityChips: Flow<Boolean>
+
/** True if the operator name view is not hidden due to HUN or other visibility state */
val shouldShowOperatorNameView: Flow<Boolean>
val isClockVisible: Flow<VisibilityModel>
@@ -412,6 +415,15 @@ constructor(
)
.flowOn(bgDispatcher)
+ override val canShowOngoingActivityChips: Flow<Boolean> =
+ combine(
+ isHomeStatusBarAllowed,
+ keyguardInteractor.isSecureCameraActive,
+ headsUpNotificationInteractor.statusBarHeadsUpStatus,
+ ) { isHomeStatusBarAllowed, isSecureCameraActive, headsUpState ->
+ isHomeStatusBarAllowed && !isSecureCameraActive && !headsUpState.isPinned
+ }
+
override val isClockVisible: Flow<VisibilityModel> =
combine(
shouldHomeStatusBarBeVisible,
diff --git a/packages/SystemUI/src/com/android/systemui/user/domain/interactor/UserLockedInteractor.kt b/packages/SystemUI/src/com/android/systemui/user/domain/interactor/UserLockedInteractor.kt
index ef29a387e06f..3bd8af690763 100644
--- a/packages/SystemUI/src/com/android/systemui/user/domain/interactor/UserLockedInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/user/domain/interactor/UserLockedInteractor.kt
@@ -18,12 +18,20 @@ package com.android.systemui.user.domain.interactor
import android.os.UserHandle
import com.android.systemui.dagger.SysUISingleton
+import com.android.systemui.dagger.qualifiers.Background
import com.android.systemui.user.data.repository.UserRepository
import javax.inject.Inject
+import kotlinx.coroutines.CoroutineDispatcher
import kotlinx.coroutines.flow.Flow
+import kotlinx.coroutines.flow.flowOn
@SysUISingleton
-class UserLockedInteractor @Inject constructor(val userRepository: UserRepository) {
+class UserLockedInteractor
+@Inject
+constructor(
+ @Background val backgroundDispatcher: CoroutineDispatcher,
+ val userRepository: UserRepository,
+) {
fun isUserUnlocked(userHandle: UserHandle?): Flow<Boolean> =
- userRepository.isUserUnlocked(userHandle)
+ userRepository.isUserUnlocked(userHandle).flowOn(backgroundDispatcher)
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/animation/FontInterpolatorTest.kt b/packages/SystemUI/tests/src/com/android/systemui/animation/FontInterpolatorTest.kt
index 2e634390679a..c2a495d13c02 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/animation/FontInterpolatorTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/animation/FontInterpolatorTest.kt
@@ -64,6 +64,12 @@ class FontInterpolatorTest : SysuiTestCase() {
"'wght' 500, 'ital' 0.5, 'GRAD' 450",
interp.lerp(startFont, endFont, 0.5f, 0.5f),
)
+
+ // Ensure axes rounded correctly to nearest step
+ assertSameAxes(
+ "'wght' 490, 'ital' 0.5, 'GRAD' 446",
+ interp.lerp(startFont, endFont, 0.492f, 0.492f),
+ )
}
@Test
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/ShadeListBuilderTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/ShadeListBuilderTest.java
index 4ff09d3bc6af..81213caaa5f4 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/ShadeListBuilderTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/ShadeListBuilderTest.java
@@ -45,6 +45,9 @@ import static java.util.Arrays.asList;
import static java.util.Collections.singletonList;
import android.os.SystemClock;
+import android.platform.test.annotations.DisableFlags;
+import android.platform.test.annotations.EnableFlags;
+import android.platform.test.flag.junit.SetFlagsRule;
import android.testing.AndroidTestingRunner;
import android.testing.TestableLooper;
import android.util.ArrayMap;
@@ -74,10 +77,14 @@ import com.android.systemui.statusbar.notification.collection.listbuilder.plugga
import com.android.systemui.statusbar.notification.collection.listbuilder.pluggable.NotifStabilityManager;
import com.android.systemui.statusbar.notification.collection.listbuilder.pluggable.Pluggable;
import com.android.systemui.statusbar.notification.collection.notifcollection.CollectionReadyForBuildListener;
+import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow;
+import com.android.systemui.statusbar.notification.row.NotificationTestHelper;
+import com.android.systemui.statusbar.notification.shared.NotificationBundleUi;
import com.android.systemui.util.time.FakeSystemClock;
import org.junit.Assert;
import org.junit.Before;
+import org.junit.Rule;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.ArgumentCaptor;
@@ -123,10 +130,13 @@ public class ShadeListBuilderTest extends SysuiTestCase {
private CollectionReadyForBuildListener mReadyForBuildListener;
private List<NotificationEntryBuilder> mPendingSet = new ArrayList<>();
private List<NotificationEntry> mEntrySet = new ArrayList<>();
- private List<ListEntry> mBuiltList = new ArrayList<>();
+ private List<PipelineEntry> mBuiltList = new ArrayList<>();
private TestableStabilityManager mStabilityManager;
private TestableNotifFilter mFinalizeFilter;
+ @Rule
+ public final SetFlagsRule mSetFlagsRule = new SetFlagsRule();
+
private Map<String, Integer> mNextIdMap = new ArrayMap<>();
private int mNextRank = 0;
@@ -561,6 +571,7 @@ public class ShadeListBuilderTest extends SysuiTestCase {
}
@Test
+ @DisableFlags(NotificationBundleUi.FLAG_NAME)
public void testFilter_resetsInitalizationTime() {
// GIVEN a NotifFilter that filters out a specific package
NotifFilter filter1 = spy(new PackageFilter(PACKAGE_1));
@@ -584,6 +595,31 @@ public class ShadeListBuilderTest extends SysuiTestCase {
}
@Test
+ @EnableFlags(NotificationBundleUi.FLAG_NAME)
+ public void testFilter_resetsInitializationTime_onRow() throws Exception {
+ // GIVEN a NotifFilter that filters out a specific package
+ NotifFilter filter1 = spy(new PackageFilter(PACKAGE_1));
+ mListBuilder.addFinalizeFilter(filter1);
+
+ // GIVEN a notification that was initialized 1 second ago that will be filtered out
+ final NotificationEntry entry = new NotificationEntryBuilder()
+ .setPkg(PACKAGE_1)
+ .setId(nextId(PACKAGE_1))
+ .setRank(nextRank())
+ .build();
+ entry.setRow(new NotificationTestHelper(mContext, mDependency).createRow());
+ entry.getRow().setInitializationTime(SystemClock.elapsedRealtime() - 1000);
+ assertTrue(entry.getRow().hasFinishedInitialization());
+
+ // WHEN the pipeline is kicked off
+ mReadyForBuildListener.onBuildList(singletonList(entry), "test");
+ mPipelineChoreographer.runIfScheduled();
+
+ // THEN the entry's initialization time is reset
+ assertFalse(entry.getRow().hasFinishedInitialization());
+ }
+
+ @Test
public void testNotifFiltersCanBePreempted() {
// GIVEN two notif filters
NotifFilter filter1 = spy(new PackageFilter(PACKAGE_2));
@@ -687,26 +723,26 @@ public class ShadeListBuilderTest extends SysuiTestCase {
@Test
public void testNotifSectionsChildrenUpdated() {
- ArrayList<ListEntry> pkg1Entries = new ArrayList<>();
- ArrayList<ListEntry> pkg2Entries = new ArrayList<>();
- ArrayList<ListEntry> pkg3Entries = new ArrayList<>();
+ ArrayList<PipelineEntry> pkg1Entries = new ArrayList<>();
+ ArrayList<PipelineEntry> pkg2Entries = new ArrayList<>();
+ ArrayList<PipelineEntry> pkg3Entries = new ArrayList<>();
final NotifSectioner pkg1Sectioner = spy(new PackageSectioner(PACKAGE_1) {
@Override
- public void onEntriesUpdated(List<ListEntry> entries) {
+ public void onEntriesUpdated(List<PipelineEntry> entries) {
super.onEntriesUpdated(entries);
pkg1Entries.addAll(entries);
}
});
final NotifSectioner pkg2Sectioner = spy(new PackageSectioner(PACKAGE_2) {
@Override
- public void onEntriesUpdated(List<ListEntry> entries) {
+ public void onEntriesUpdated(List<PipelineEntry> entries) {
super.onEntriesUpdated(entries);
pkg2Entries.addAll(entries);
}
});
final NotifSectioner pkg3Sectioner = spy(new PackageSectioner(PACKAGE_3) {
@Override
- public void onEntriesUpdated(List<ListEntry> entries) {
+ public void onEntriesUpdated(List<PipelineEntry> entries) {
super.onEntriesUpdated(entries);
pkg3Entries.addAll(entries);
}
@@ -2442,7 +2478,7 @@ public class ShadeListBuilderTest extends SysuiTestCase {
mBuiltList.size());
for (int i = 0; i < expectedEntries.length; i++) {
- ListEntry outEntry = mBuiltList.get(i);
+ PipelineEntry outEntry = mBuiltList.get(i);
ExpectedEntry expectedEntry = expectedEntries[i];
if (expectedEntry instanceof ExpectedNotif) {
@@ -2617,7 +2653,7 @@ public class ShadeListBuilderTest extends SysuiTestCase {
}
@Override
- public int compare(@NonNull ListEntry o1, @NonNull ListEntry o2) {
+ public int compare(@NonNull PipelineEntry o1, @NonNull PipelineEntry o2) {
boolean contains1 = mPreferredPackages.contains(
o1.getRepresentativeEntry().getSbn().getPackageName());
boolean contains2 = mPreferredPackages.contains(
@@ -2655,37 +2691,37 @@ public class ShadeListBuilderTest extends SysuiTestCase {
}
@Override
- public boolean isInSection(ListEntry entry) {
+ public boolean isInSection(PipelineEntry entry) {
return mPackages.contains(entry.getRepresentativeEntry().getSbn().getPackageName());
}
}
private static class RecordingOnBeforeTransformGroupsListener
implements OnBeforeTransformGroupsListener {
- List<ListEntry> mEntriesReceived;
+ List<PipelineEntry> mEntriesReceived;
@Override
- public void onBeforeTransformGroups(List<ListEntry> list) {
+ public void onBeforeTransformGroups(List<PipelineEntry> list) {
mEntriesReceived = new ArrayList<>(list);
}
}
private static class RecordingOnBeforeSortListener
implements OnBeforeSortListener {
- List<ListEntry> mEntriesReceived;
+ List<PipelineEntry> mEntriesReceived;
@Override
- public void onBeforeSort(List<ListEntry> list) {
+ public void onBeforeSort(List<PipelineEntry> list) {
mEntriesReceived = new ArrayList<>(list);
}
}
private static class RecordingOnBeforeRenderListener
implements OnBeforeRenderListListener {
- List<ListEntry> mEntriesReceived;
+ List<PipelineEntry> mEntriesReceived;
@Override
- public void onBeforeRenderList(List<ListEntry> list) {
+ public void onBeforeRenderList(List<PipelineEntry> list) {
mEntriesReceived = new ArrayList<>(list);
}
}
@@ -2764,7 +2800,7 @@ public class ShadeListBuilderTest extends SysuiTestCase {
}
@Override
- public boolean isEntryReorderingAllowed(@NonNull ListEntry entry) {
+ public boolean isEntryReorderingAllowed(@NonNull PipelineEntry entry) {
return mAllowEntryReodering;
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRowTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRowTest.java
index 1b5353127f25..24d8d1cc8fae 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRowTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRowTest.java
@@ -75,6 +75,7 @@ import com.android.systemui.statusbar.notification.headsup.PinnedStatus;
import com.android.systemui.statusbar.notification.promoted.PromotedNotificationUiForceExpanded;
import com.android.systemui.statusbar.notification.row.ExpandableView.OnHeightChangedListener;
import com.android.systemui.statusbar.notification.row.wrapper.NotificationViewWrapper;
+import com.android.systemui.statusbar.notification.shared.NotificationBundleUi;
import com.android.systemui.statusbar.notification.shared.NotificationContentAlphaOptimization;
import com.android.systemui.statusbar.notification.stack.NotificationChildrenContainer;
import com.android.systemui.statusbar.phone.KeyguardBypassController;
@@ -589,6 +590,7 @@ public class ExpandableNotificationRowTest extends SysuiTestCase {
}
@Test
+ @DisableFlags(NotificationBundleUi.FLAG_NAME)
public void testGetIsNonblockable() throws Exception {
ExpandableNotificationRow row =
mNotificationTestHelper.createRow(mNotificationTestHelper.createNotification());
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationContentViewTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationContentViewTest.kt
index 47238fedee4d..c874bc6056c6 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationContentViewTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationContentViewTest.kt
@@ -36,6 +36,7 @@ import com.android.internal.widget.NotificationActionListLayout
import com.android.internal.widget.NotificationExpandButton
import com.android.systemui.SysuiTestCase
import com.android.systemui.statusbar.notification.FeedbackIcon
+import com.android.systemui.statusbar.notification.collection.EntryAdapter
import com.android.systemui.statusbar.notification.collection.NotificationEntry
import com.android.systemui.statusbar.notification.people.PeopleNotificationIdentifier
import com.android.systemui.statusbar.notification.shared.NotificationBundleUi
@@ -82,6 +83,7 @@ class NotificationContentViewTest : SysuiTestCase() {
fakeParent =
spy(FrameLayout(mContext, /* attrs= */ null).also { it.visibility = View.GONE })
val mockEntry = createMockNotificationEntry()
+ val mockEntryAdapter = createMockNotificationEntryAdapter()
row =
spy(
when (NotificationBundleUi.isEnabled) {
@@ -92,6 +94,7 @@ class NotificationContentViewTest : SysuiTestCase() {
UserHandle.CURRENT
).apply {
entry = mockEntry
+ entryAdapter = mockEntryAdapter
}
}
false -> {
@@ -611,6 +614,7 @@ class NotificationContentViewTest : SysuiTestCase() {
whenever(this.entry).thenReturn(notificationEntry)
whenever(this.context).thenReturn(mContext)
whenever(this.bubbleClickListener).thenReturn(View.OnClickListener {})
+ whenever(this.entryAdapter).thenReturn(createMockNotificationEntryAdapter())
}
private fun createMockNotificationEntry() =
@@ -624,6 +628,9 @@ class NotificationContentViewTest : SysuiTestCase() {
whenever(sbnMock.user).thenReturn(userMock)
}
+ private fun createMockNotificationEntryAdapter() =
+ mock<EntryAdapter>()
+
private fun createLinearLayoutWithBottomMargin(bottomMargin: Int): LinearLayout {
val outerLayout = LinearLayout(mContext)
val innerLayout = LinearLayout(mContext)
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationGutsManagerWithScenesTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationGutsManagerWithScenesTest.kt
index 3d4c90140adb..99b99ee1860b 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationGutsManagerWithScenesTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationGutsManagerWithScenesTest.kt
@@ -427,7 +427,6 @@ class NotificationGutsManagerWithScenesTest : SysuiTestCase() {
.setUserSentiment(Ranking.USER_SENTIMENT_NEGATIVE)
.setImportance(NotificationManager.IMPORTANCE_HIGH)
.build()
- whenever(row.getIsNonblockable()).thenReturn(false)
whenever(highPriorityProvider.isHighPriority(entry)).thenReturn(true)
val statusBarNotification = entry.sbn
gutsManager.initializeNotificationInfo(row, notificationInfoView)
@@ -463,7 +462,6 @@ class NotificationGutsManagerWithScenesTest : SysuiTestCase() {
NotificationEntryHelper.modifyRanking(row.entry)
.setUserSentiment(Ranking.USER_SENTIMENT_NEGATIVE)
.build()
- whenever(row.getIsNonblockable()).thenReturn(false)
val statusBarNotification = row.entry.sbn
val entry = row.entry
gutsManager.initializeNotificationInfo(row, notificationInfoView)
@@ -499,7 +497,6 @@ class NotificationGutsManagerWithScenesTest : SysuiTestCase() {
NotificationEntryHelper.modifyRanking(row.entry)
.setUserSentiment(Ranking.USER_SENTIMENT_NEGATIVE)
.build()
- whenever(row.getIsNonblockable()).thenReturn(false)
val statusBarNotification = row.entry.sbn
val entry = row.entry
gutsManager.initializeNotificationInfo(row, notificationInfoView)
@@ -566,7 +563,7 @@ class NotificationGutsManagerWithScenesTest : SysuiTestCase() {
): NotificationMenuRowPlugin.MenuItem {
val menuRow: NotificationMenuRowPlugin =
NotificationMenuRow(mContext, peopleNotificationIdentifier)
- menuRow.createMenu(row, row!!.entry.sbn)
+ menuRow.createMenu(row)
val menuItem = menuRow.getLongpressMenuItem(mContext)
Assert.assertNotNull(menuItem)
return menuItem
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutTest.java
index 6ac20d40f2dc..955de273c426 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutTest.java
@@ -675,7 +675,7 @@ public class NotificationStackScrollLayoutTest extends SysuiTestCase {
@Test
public void testClearNotifications_clearAllInProgress() {
ExpandableNotificationRow row = createClearableRow();
- when(row.getEntry().hasFinishedInitialization()).thenReturn(true);
+ when(row.hasFinishedInitialization()).thenReturn(true);
doReturn(true).when(mStackScroller).isVisible(row);
mStackScroller.addContainerView(row);
diff --git a/packages/SystemUI/tests/src/com/android/systemui/unfold/UnfoldLatencyTrackerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/unfold/UnfoldLatencyTrackerTest.kt
index a1122c3cbcd2..a553b176c34a 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/unfold/UnfoldLatencyTrackerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/unfold/UnfoldLatencyTrackerTest.kt
@@ -18,10 +18,12 @@ package com.android.systemui.unfold
import android.hardware.devicestate.DeviceStateManager
import android.hardware.devicestate.DeviceStateManager.FoldStateListener
+import android.platform.test.annotations.DisableFlags
import android.provider.Settings
import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.SmallTest
import com.android.internal.util.LatencyTracker
+import com.android.systemui.Flags
import com.android.systemui.SysuiTestCase
import com.android.systemui.foldedDeviceStateList
import com.android.systemui.halfFoldedDeviceState
@@ -46,6 +48,7 @@ import org.mockito.kotlin.whenever
@RunWith(AndroidJUnit4::class)
@SmallTest
+@DisableFlags(Flags.FLAG_UNFOLD_LATENCY_TRACKING_FIX)
class UnfoldLatencyTrackerTest : SysuiTestCase() {
@Mock lateinit var latencyTracker: LatencyTracker
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/qs/panels/domain/interactor/IconTilesInteractorKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/qs/panels/domain/interactor/IconTilesInteractorKosmos.kt
index 8d4db8b74061..8a6f68c5da68 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/qs/panels/domain/interactor/IconTilesInteractorKosmos.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/qs/panels/domain/interactor/IconTilesInteractorKosmos.kt
@@ -16,6 +16,7 @@
package com.android.systemui.qs.panels.domain.interactor
+import com.android.internal.logging.uiEventLoggerFake
import com.android.systemui.kosmos.Kosmos
import com.android.systemui.kosmos.applicationCoroutineScope
import com.android.systemui.log.core.FakeLogBuffer
@@ -29,6 +30,7 @@ val Kosmos.iconTilesInteractor by
defaultLargeTilesRepository,
currentTilesInteractor,
qsPreferencesInteractor,
+ uiEventLoggerFake,
largeTileSpanRepository,
FakeLogBuffer.Factory.create(),
applicationCoroutineScope,
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/notification/collection/NotificationEntryBuilder.java b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/notification/collection/NotificationEntryBuilder.java
index 562ac0c15a0b..804ec9f29926 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/notification/collection/NotificationEntryBuilder.java
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/notification/collection/NotificationEntryBuilder.java
@@ -56,7 +56,7 @@ public class NotificationEntryBuilder {
private StatusBarNotification mSbn = null;
/* ListEntry properties */
- private GroupEntry mParent;
+ private PipelineEntry mParent;
private NotifSection mNotifSection;
/* If set, use this creation time instead of mClock.uptimeMillis */
@@ -91,7 +91,7 @@ public class NotificationEntryBuilder {
}
/** Update an the parent on an existing entry */
- public static void setNewParent(NotificationEntry entry, GroupEntry parent) {
+ public static void setNewParent(NotificationEntry entry, PipelineEntry parent) {
entry.setParent(parent);
}
@@ -135,7 +135,7 @@ public class NotificationEntryBuilder {
/**
* Sets the parent.
*/
- public NotificationEntryBuilder setParent(@Nullable GroupEntry parent) {
+ public NotificationEntryBuilder setParent(@Nullable PipelineEntry parent) {
mParent = parent;
return this;
}
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/phone/ongoingcall/shared/model/OngoingCallTestHelper.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/phone/ongoingcall/shared/model/OngoingCallTestHelper.kt
index d09d010cba2e..8ff7c7d01fb3 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/phone/ongoingcall/shared/model/OngoingCallTestHelper.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/phone/ongoingcall/shared/model/OngoingCallTestHelper.kt
@@ -76,6 +76,7 @@ object OngoingCallTestHelper {
promotedContent: PromotedNotificationContentModel? = null,
contentIntent: PendingIntent? = null,
uid: Int = DEFAULT_UID,
+ appName: String = "Fake name",
) {
if (StatusBarChipsModernization.isEnabled) {
activeNotificationListRepository.addNotif(
@@ -87,6 +88,7 @@ object OngoingCallTestHelper {
contentIntent = contentIntent,
promotedContent = promotedContent,
uid = uid,
+ appName = appName,
)
)
} else {
@@ -96,6 +98,7 @@ object OngoingCallTestHelper {
notificationIcon = statusBarChipIconView,
intent = contentIntent,
notificationKey = key,
+ appName = appName,
promotedContent = promotedContent,
)
)
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/pipeline/mobile/domain/interactor/FakeMobileIconsInteractor.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/pipeline/mobile/domain/interactor/FakeMobileIconsInteractor.kt
index 9b6f205fba72..8fa82cad5c32 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/pipeline/mobile/domain/interactor/FakeMobileIconsInteractor.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/pipeline/mobile/domain/interactor/FakeMobileIconsInteractor.kt
@@ -58,6 +58,8 @@ class FakeMobileIconsInteractor(
override val defaultDataSubId: MutableStateFlow<Int?> = MutableStateFlow(DEFAULT_DATA_SUB_ID)
+ override val activeMobileDataSubscriptionId: MutableStateFlow<Int?> = MutableStateFlow(null)
+
private val _activeDataConnectionHasDataEnabled = MutableStateFlow(false)
override val activeDataConnectionHasDataEnabled = _activeDataConnectionHasDataEnabled
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/pipeline/mobile/ui/viewmodel/StackedMobileIconViewModelKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/pipeline/mobile/ui/viewmodel/StackedMobileIconViewModelKosmos.kt
new file mode 100644
index 000000000000..880ba5eee5d2
--- /dev/null
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/pipeline/mobile/ui/viewmodel/StackedMobileIconViewModelKosmos.kt
@@ -0,0 +1,22 @@
+/*
+ * Copyright (C) 2025 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.statusbar.pipeline.mobile.ui.viewmodel
+
+import com.android.systemui.kosmos.Kosmos
+
+val Kosmos.stackedMobileIconViewModel: StackedMobileIconViewModel by
+ Kosmos.Fixture { StackedMobileIconViewModel(mobileIconsViewModel) }
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/user/domain/interactor/UserLockedInteractorKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/user/domain/interactor/UserLockedInteractorKosmos.kt
index fd955089cdc7..933c351679a4 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/user/domain/interactor/UserLockedInteractorKosmos.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/user/domain/interactor/UserLockedInteractorKosmos.kt
@@ -17,7 +17,10 @@
package com.android.systemui.user.domain.interactor
import com.android.systemui.kosmos.Kosmos
+import com.android.systemui.kosmos.testDispatcher
import com.android.systemui.user.data.repository.userRepository
val Kosmos.userLockedInteractor by
- Kosmos.Fixture { UserLockedInteractor(userRepository = userRepository) }
+ Kosmos.Fixture {
+ UserLockedInteractor(backgroundDispatcher = testDispatcher, userRepository = userRepository)
+ }
diff --git a/packages/Vcn/framework-b/Android.bp b/packages/Vcn/framework-b/Android.bp
index edb22c0e7aa0..c53123359872 100644
--- a/packages/Vcn/framework-b/Android.bp
+++ b/packages/Vcn/framework-b/Android.bp
@@ -77,8 +77,7 @@ framework_connectivity_b_defaults_soong_config {
],
soong_config_variables: {
is_vcn_in_mainline: {
- //TODO: b/380155299 Make it Baklava when aidl tool can understand it
- min_sdk_version: "current",
+ min_sdk_version: "36",
static_libs: ["android.net.vcn.flags-aconfig-java"],
apex_available: ["com.android.tethering"],
diff --git a/ravenwood/Android.bp b/ravenwood/Android.bp
index 65550f2b4273..ccbc46fdb03b 100644
--- a/ravenwood/Android.bp
+++ b/ravenwood/Android.bp
@@ -121,6 +121,7 @@ java_library {
name: "ravenwood-helper-framework-runtime",
srcs: [
"runtime-helper-src/framework/**/*.java",
+ ":framework-graphics-srcs",
],
static_libs: [
"ravenwood-runtime-common",
@@ -278,6 +279,7 @@ cc_library_host_shared {
cc_library_host_shared {
name: "libravenwood_runtime",
defaults: ["ravenwood_jni_defaults"],
+ header_libs: ["libicuuc_headers"],
srcs: [
"runtime-jni/ravenwood_runtime.cpp",
"runtime-jni/ravenwood_os_constants.cpp",
@@ -372,6 +374,13 @@ platform_compat_config {
visibility: ["//visibility:private"],
}
+java_library {
+ name: "ext-ravenwood",
+ installable: false,
+ static_libs: ["ext"],
+ visibility: ["//visibility:private"],
+}
+
filegroup {
name: "ravenwood-data",
device_common_srcs: [
@@ -637,6 +646,7 @@ android_ravenwood_libgroup {
libs: [
"100-framework-minus-apex.ravenwood",
"200-kxml2-android",
+ "ext-ravenwood",
"ravenwood-runtime-common-ravenwood",
diff --git a/ravenwood/Framework.bp b/ravenwood/Framework.bp
index 71496b0d5766..e36677189e02 100644
--- a/ravenwood/Framework.bp
+++ b/ravenwood/Framework.bp
@@ -419,11 +419,13 @@ java_genrule {
"--out-impl-jar $(location ravenwood.jar) " +
"--in-jar $(location :framework-graphics.impl{.jar}) " +
- "--policy-override-file $(location :ravenwood-common-policies) ",
+ "--policy-override-file $(location :ravenwood-common-policies) " +
+ "--policy-override-file $(location :framework-graphics-ravenwood-policies) ",
srcs: [
":framework-graphics.impl{.jar}",
":ravenwood-common-policies",
+ ":framework-graphics-ravenwood-policies",
":ravenwood-standard-options",
],
out: [
diff --git a/ravenwood/junit-impl-src/android/platform/test/ravenwood/RavenwoodContext.java b/ravenwood/junit-impl-src/android/platform/test/ravenwood/RavenwoodContext.java
index a3326337d485..9e6b12f60add 100644
--- a/ravenwood/junit-impl-src/android/platform/test/ravenwood/RavenwoodContext.java
+++ b/ravenwood/junit-impl-src/android/platform/test/ravenwood/RavenwoodContext.java
@@ -230,6 +230,16 @@ public class RavenwoodContext extends RavenwoodBaseContext {
return mAppContext;
}
+ @Override
+ public boolean isRestricted() {
+ return false;
+ }
+
+ @Override
+ public boolean canLoadUnsafeResources() {
+ return true;
+ }
+
/**
* Wrap the given {@link Supplier} to become memoized.
*
diff --git a/ravenwood/junit-impl-src/android/platform/test/ravenwood/RavenwoodNativeLoader.java b/ravenwood/junit-impl-src/android/platform/test/ravenwood/RavenwoodNativeLoader.java
index a208d6dce2ce..7e935d0451ae 100644
--- a/ravenwood/junit-impl-src/android/platform/test/ravenwood/RavenwoodNativeLoader.java
+++ b/ravenwood/junit-impl-src/android/platform/test/ravenwood/RavenwoodNativeLoader.java
@@ -48,6 +48,7 @@ public final class RavenwoodNativeLoader {
android.content.res.AssetManager.class,
android.content.res.StringBlock.class,
android.content.res.XmlBlock.class,
+ android.text.AndroidCharacter.class,
};
/**
@@ -61,15 +62,49 @@ public final class RavenwoodNativeLoader {
android.graphics.Path.class,
android.graphics.Color.class,
android.graphics.ColorSpace.class,
+ android.graphics.Bitmap.class,
+ android.graphics.BitmapFactory.class,
+ android.graphics.BitmapRegionDecoder.class,
+ android.graphics.Camera.class,
+ android.graphics.Canvas.class,
+ android.graphics.CanvasProperty.class,
+ android.graphics.ColorFilter.class,
+ android.graphics.DrawFilter.class,
+ android.graphics.FontFamily.class,
+ android.graphics.Gainmap.class,
+ android.graphics.ImageDecoder.class,
+ android.graphics.MaskFilter.class,
+ android.graphics.NinePatch.class,
+ android.graphics.Paint.class,
+ android.graphics.PathEffect.class,
+ android.graphics.PathIterator.class,
+ android.graphics.PathMeasure.class,
+ android.graphics.Picture.class,
+ android.graphics.RecordingCanvas.class,
+ android.graphics.Region.class,
+ android.graphics.RenderNode.class,
+ android.graphics.Shader.class,
+ android.graphics.RenderEffect.class,
+ android.graphics.Typeface.class,
+ android.graphics.YuvImage.class,
+ android.graphics.fonts.Font.class,
+ android.graphics.fonts.FontFamily.class,
+ android.graphics.text.LineBreaker.class,
+ android.graphics.text.MeasuredText.class,
+ android.graphics.text.TextRunShaper.class,
+ android.graphics.text.GraphemeBreak.class,
+ android.util.PathParser.class,
};
/**
* Extra strings needed to pass to register_android_graphics_classes().
*
- * `android.graphics.Graphics` is not actually a class, so we just hardcode it here.
+ * Several entries are not actually a class, so we just hardcode them here.
*/
public final static String[] GRAPHICS_EXTRA_INIT_PARAMS = new String[] {
- "android.graphics.Graphics"
+ "android.graphics.Graphics",
+ "android.graphics.ByteBufferStreamAdaptor",
+ "android.graphics.CreateJavaOutputStreamAdaptor"
};
private RavenwoodNativeLoader() {
diff --git a/ravenwood/junit-impl-src/android/platform/test/ravenwood/RavenwoodRuntimeEnvironmentController.java b/ravenwood/junit-impl-src/android/platform/test/ravenwood/RavenwoodRuntimeEnvironmentController.java
index ae88bb234e9d..f205d238c693 100644
--- a/ravenwood/junit-impl-src/android/platform/test/ravenwood/RavenwoodRuntimeEnvironmentController.java
+++ b/ravenwood/junit-impl-src/android/platform/test/ravenwood/RavenwoodRuntimeEnvironmentController.java
@@ -43,6 +43,7 @@ import android.app.UiAutomation;
import android.content.Context;
import android.content.pm.ApplicationInfo;
import android.content.res.Resources;
+import android.graphics.Typeface;
import android.icu.util.ULocale;
import android.os.Binder;
import android.os.Build;
@@ -246,6 +247,13 @@ public class RavenwoodRuntimeEnvironmentController {
// Do the basic set up for the android sysprops.
RavenwoodSystemProperties.initialize();
+ // Set ICU data file
+ String icuData = RavenwoodCommonUtils.getRavenwoodRuntimePath()
+ + "ravenwood-data/"
+ + RavenwoodRuntimeNative.getIcuDataName()
+ + ".dat";
+ RavenwoodRuntimeNative.setSystemProperty("ro.icu.data.path", icuData);
+
// Enable all log levels for native logging, until we'll have a way to change the native
// side log level at runtime.
// Do this after loading RAVENWOOD_NATIVE_RUNTIME_NAME (which backs Os.setenv()),
@@ -268,6 +276,11 @@ public class RavenwoodRuntimeEnvironmentController {
Objects.requireNonNull(Build.TYPE);
Objects.requireNonNull(Build.VERSION.SDK);
+ // Fonts can only be initialized once
+ Typeface.init();
+ Typeface.loadPreinstalledSystemFontMap();
+ Typeface.loadNativeSystemFonts();
+
System.setProperty(RAVENWOOD_VERSION_JAVA_SYSPROP, "1");
// This will let AndroidJUnit4 use the original runner.
System.setProperty("android.junit.runner",
diff --git a/ravenwood/runtime-helper-src/libcore-fake/com/android/ravenwood/RavenwoodJdkPatch.java b/ravenwood/runtime-helper-src/libcore-fake/com/android/ravenwood/RavenwoodJdkPatch.java
index 96aed4b3401d..d5a96ddc3a98 100644
--- a/ravenwood/runtime-helper-src/libcore-fake/com/android/ravenwood/RavenwoodJdkPatch.java
+++ b/ravenwood/runtime-helper-src/libcore-fake/com/android/ravenwood/RavenwoodJdkPatch.java
@@ -20,6 +20,7 @@ import com.android.ravenwood.common.JvmWorkaround;
import java.io.FileDescriptor;
import java.util.LinkedHashMap;
import java.util.Map;
+import java.util.regex.Pattern;
/**
* Class to host APIs that exist in libcore, but not in standard JRE.
@@ -46,4 +47,22 @@ public class RavenwoodJdkPatch {
final var it = map.entrySet().iterator();
return it.hasNext() ? it.next() : null;
}
+
+ /**
+ * Implements Pattern.compile(String)
+ *
+ * ART always assumes UNICODE_CHARACTER_CLASS is set.
+ */
+ public static Pattern compilePattern(String regex) {
+ return Pattern.compile(regex, Pattern.UNICODE_CHARACTER_CLASS);
+ }
+
+ /**
+ * Implements Pattern.compile(String, int)
+ *
+ * ART always assumes UNICODE_CHARACTER_CLASS is set.
+ */
+ public static Pattern compilePattern(String regex, int flag) {
+ return Pattern.compile(regex, flag | Pattern.UNICODE_CHARACTER_CLASS);
+ }
}
diff --git a/ravenwood/runtime-helper-src/libcore-fake/com/android/ravenwood/RavenwoodRuntimeNative.java b/ravenwood/runtime-helper-src/libcore-fake/com/android/ravenwood/RavenwoodRuntimeNative.java
index acbcdf1926db..0d82a8691881 100644
--- a/ravenwood/runtime-helper-src/libcore-fake/com/android/ravenwood/RavenwoodRuntimeNative.java
+++ b/ravenwood/runtime-helper-src/libcore-fake/com/android/ravenwood/RavenwoodRuntimeNative.java
@@ -66,6 +66,8 @@ public class RavenwoodRuntimeNative {
public static native int gettid();
+ public static native String getIcuDataName();
+
public static long lseek(FileDescriptor fd, long offset, int whence) throws ErrnoException {
return nLseek(JvmWorkaround.getInstance().getFdInt(fd), offset, whence);
}
diff --git a/ravenwood/runtime-helper-src/libcore-fake/dalvik/system/CloseGuard.java b/ravenwood/runtime-helper-src/libcore-fake/dalvik/system/CloseGuard.java
new file mode 100644
index 000000000000..82bab64f22f3
--- /dev/null
+++ b/ravenwood/runtime-helper-src/libcore-fake/dalvik/system/CloseGuard.java
@@ -0,0 +1,198 @@
+/*
+ * Copyright (C) 2010 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 dalvik.system;
+
+/**
+ * A no-op copy of libcore/dalvik/src/main/java/dalvik/system/CloseGuard.java
+ */
+public final class CloseGuard {
+
+ /**
+ * Returns a CloseGuard instance. {@code #open(String)} can be used to set
+ * up the instance to warn on failure to close.
+ *
+ * @return {@link CloseGuard} instance.
+ *
+ * @hide
+ */
+ public static CloseGuard get() {
+ return new CloseGuard();
+ }
+
+ /**
+ * Enables/disables stack capture and tracking. A call stack is captured
+ * during open(), and open/close events are reported to the Tracker, only
+ * if enabled is true. If a stack trace was captured, the {@link
+ * #getReporter() reporter} is informed of unclosed resources; otherwise a
+ * one-line warning is logged.
+ *
+ * @param enabled whether stack capture and tracking is enabled.
+ *
+ * @hide
+ */
+ public static void setEnabled(boolean enabled) {
+ }
+
+ /**
+ * True if CloseGuard stack capture and tracking are enabled.
+ *
+ * @hide
+ */
+ public static boolean isEnabled() {
+ return false;
+ }
+
+ /**
+ * Used to replace default Reporter used to warn of CloseGuard
+ * violations when stack tracking is enabled. Must be non-null.
+ *
+ * @param rep replacement for default Reporter.
+ *
+ * @hide
+ */
+ public static void setReporter(Reporter rep) {
+ if (rep == null) {
+ throw new NullPointerException("reporter == null");
+ }
+ }
+
+ /**
+ * Returns non-null CloseGuard.Reporter.
+ *
+ * @return CloseGuard's Reporter.
+ *
+ * @hide
+ */
+ public static Reporter getReporter() {
+ return null;
+ }
+
+ /**
+ * Sets the {@link Tracker} that is notified when resources are allocated and released.
+ * The Tracker is invoked only if CloseGuard {@link #isEnabled()} held when {@link #open()}
+ * was called. A null argument disables tracking.
+ *
+ * <p>This is only intended for use by {@code dalvik.system.CloseGuardSupport} class and so
+ * MUST NOT be used for any other purposes.
+ *
+ * @hide
+ */
+ public static void setTracker(Tracker tracker) {
+ }
+
+ /**
+ * Returns {@link #setTracker(Tracker) last Tracker that was set}, or null to indicate
+ * there is none.
+ *
+ * <p>This is only intended for use by {@code dalvik.system.CloseGuardSupport} class and so
+ * MUST NOT be used for any other purposes.
+ *
+ * @hide
+ */
+ public static Tracker getTracker() {
+ return null;
+ }
+
+ private CloseGuard() {}
+
+ /**
+ * {@code open} initializes the instance with a warning that the caller
+ * should have explicitly called the {@code closer} method instead of
+ * relying on finalization.
+ *
+ * @param closer non-null name of explicit termination method. Printed by warnIfOpen.
+ * @throws NullPointerException if closer is null.
+ *
+ * @hide
+ */
+ public void open(String closer) {
+ openWithCallSite(closer, null /* callsite */);
+ }
+
+ /**
+ * Like {@link #open(String)}, but with explicit callsite string being passed in for better
+ * performance.
+ * <p>
+ * This only has better performance than {@link #open(String)} if {@link #isEnabled()} returns {@code true}, which
+ * usually shouldn't happen on release builds.
+ *
+ * @param closer Non-null name of explicit termination method. Printed by warnIfOpen.
+ * @param callsite Non-null string uniquely identifying the callsite.
+ *
+ * @hide
+ */
+ public void openWithCallSite(String closer, String callsite) {
+ }
+
+ // We keep either an allocation stack containing the closer String or, when
+ // in disabled state, just the closer String.
+ // We keep them in a single field only to minimize overhead.
+ private Object /* String or Throwable */ closerNameOrAllocationInfo;
+
+ /**
+ * Marks this CloseGuard instance as closed to avoid warnings on
+ * finalization.
+ *
+ * @hide
+ */
+ public void close() {
+ }
+
+ /**
+ * Logs a warning if the caller did not properly cleanup by calling an
+ * explicit close method before finalization. If CloseGuard was enabled
+ * when the CloseGuard was created, passes the stacktrace associated with
+ * the allocation to the current reporter. If it was not enabled, it just
+ * directly logs a brief message.
+ *
+ * @hide
+ */
+ public void warnIfOpen() {
+ }
+
+
+ /**
+ * Interface to allow customization of tracking behaviour.
+ *
+ * <p>This is only intended for use by {@code dalvik.system.CloseGuardSupport} class and so
+ * MUST NOT be used for any other purposes.
+ *
+ * @hide
+ */
+ public interface Tracker {
+ void open(Throwable allocationSite);
+ void close(Throwable allocationSite);
+ }
+
+ /**
+ * Interface to allow customization of reporting behavior.
+ * @hide
+ */
+ public interface Reporter {
+ /**
+ *
+ * @hide
+ */
+ void report(String message, Throwable allocationSite);
+
+ /**
+ *
+ * @hide
+ */
+ default void report(String message) {}
+ }
+}
diff --git a/ravenwood/runtime-helper-src/libcore-fake/libcore/io/IoBridge.java b/ravenwood/runtime-helper-src/libcore-fake/libcore/io/IoBridge.java
new file mode 100644
index 000000000000..2a1ee2542982
--- /dev/null
+++ b/ravenwood/runtime-helper-src/libcore-fake/libcore/io/IoBridge.java
@@ -0,0 +1,63 @@
+/*
+ * Copyright (C) 2025 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 libcore.io;
+
+import android.system.ErrnoException;
+import android.system.Os;
+import android.system.OsConstants;
+
+import java.io.FileDescriptor;
+import java.io.FileNotFoundException;
+import java.io.IOException;
+
+public class IoBridge {
+
+ public static void closeAndSignalBlockedThreads(FileDescriptor fd) throws IOException {
+ if (fd == null) {
+ return;
+ }
+ try {
+ Os.close(fd);
+ } catch (ErrnoException errnoException) {
+ throw errnoException.rethrowAsIOException();
+ }
+ }
+
+ public static FileDescriptor open(String path, int flags) throws FileNotFoundException {
+ FileDescriptor fd = null;
+ try {
+ fd = Os.open(path, flags, 0666);
+ // Posix open(2) fails with EISDIR only if you ask for write permission.
+ // Java disallows reading directories too.f
+ if (OsConstants.S_ISDIR(Os.fstat(fd).st_mode)) {
+ throw new ErrnoException("open", OsConstants.EISDIR);
+ }
+ return fd;
+ } catch (ErrnoException errnoException) {
+ try {
+ if (fd != null) {
+ closeAndSignalBlockedThreads(fd);
+ }
+ } catch (IOException ignored) {
+ }
+ FileNotFoundException ex = new FileNotFoundException(path + ": "
+ + errnoException.getMessage());
+ ex.initCause(errnoException);
+ throw ex;
+ }
+ }
+}
diff --git a/ravenwood/runtime-helper-src/libcore-fake/libcore/util/NativeAllocationRegistry.java b/ravenwood/runtime-helper-src/libcore-fake/libcore/util/NativeAllocationRegistry.java
index ad86135de32e..cf1a5138cbc6 100644
--- a/ravenwood/runtime-helper-src/libcore-fake/libcore/util/NativeAllocationRegistry.java
+++ b/ravenwood/runtime-helper-src/libcore-fake/libcore/util/NativeAllocationRegistry.java
@@ -35,6 +35,11 @@ public class NativeAllocationRegistry {
return new NativeAllocationRegistry(classLoader, freeFunction, size);
}
+ public static NativeAllocationRegistry createNonmalloced(
+ Class clazz, long freeFunction, long size) {
+ return new NativeAllocationRegistry(clazz.getClassLoader(), freeFunction, size);
+ }
+
public static NativeAllocationRegistry createMalloced(
ClassLoader classLoader, long freeFunction, long size) {
return new NativeAllocationRegistry(classLoader, freeFunction, size);
@@ -45,6 +50,11 @@ public class NativeAllocationRegistry {
return new NativeAllocationRegistry(classLoader, freeFunction, 0);
}
+ public static NativeAllocationRegistry createMalloced(
+ Class clazz, long freeFunction, long size) {
+ return new NativeAllocationRegistry(clazz.getClassLoader(), freeFunction, size);
+ }
+
public NativeAllocationRegistry(ClassLoader classLoader, long freeFunction, long size) {
if (size < 0) {
throw new IllegalArgumentException("Invalid native allocation size: " + size);
@@ -52,6 +62,37 @@ public class NativeAllocationRegistry {
mFreeFunction = freeFunction;
}
+ private class CleanerThunk implements Runnable {
+ private long nativePtr;
+
+ public CleanerThunk() {
+ nativePtr = 0;
+ }
+
+ public void setNativePtr(long ptr) {
+ nativePtr = ptr;
+ }
+
+ @Override
+ public void run() {
+ if (nativePtr != 0) {
+ applyFreeFunction(mFreeFunction, nativePtr);
+ }
+ }
+ }
+
+ private static class CleanableRunner implements Runnable {
+ private final Cleaner.Cleanable mCleanable;
+
+ public CleanableRunner(Cleaner.Cleanable cleanable) {
+ mCleanable = cleanable;
+ }
+
+ public void run() {
+ mCleanable.clean();
+ }
+ }
+
public Runnable registerNativeAllocation(Object referent, long nativePtr) {
if (referent == null) {
throw new IllegalArgumentException("referent is null");
@@ -60,13 +101,25 @@ public class NativeAllocationRegistry {
throw new IllegalArgumentException("nativePtr is null");
}
- final Runnable releaser = () -> {
- RavenwoodRuntimeNative.applyFreeFunction(mFreeFunction, nativePtr);
- };
- sCleaner.register(referent, releaser);
+ final CleanerThunk thunk;
+ final CleanableRunner result;
+ try {
+ thunk = new CleanerThunk();
+ final var cleanable = sCleaner.register(referent, thunk);
+ result = new CleanableRunner(cleanable);
+ } catch (VirtualMachineError vme /* probably OutOfMemoryError */) {
+ applyFreeFunction(mFreeFunction, nativePtr);
+ throw vme;
+ }
+ // Enable the cleaner only after we can no longer throw anything, including OOME.
+ thunk.setNativePtr(nativePtr);
// Ensure that cleaner doesn't get invoked before we enable it.
Reference.reachabilityFence(referent);
- return releaser;
+ return result;
+ }
+
+ public static void applyFreeFunction(long freeFunction, long nativePtr) {
+ RavenwoodRuntimeNative.applyFreeFunction(freeFunction, nativePtr);
}
}
diff --git a/ravenwood/runtime-jni/ravenwood_runtime.cpp b/ravenwood/runtime-jni/ravenwood_runtime.cpp
index 8d8ed7119e84..01ebdc953539 100644
--- a/ravenwood/runtime-jni/ravenwood_runtime.cpp
+++ b/ravenwood/runtime-jni/ravenwood_runtime.cpp
@@ -20,6 +20,7 @@
#include <sys/syscall.h>
#include <unistd.h>
#include <utils/misc.h>
+#include <unicode/utypes.h>
#include <string>
@@ -183,6 +184,10 @@ static jint Linux_gettid(JNIEnv* env, jobject) {
return syscall(__NR_gettid);
}
+static jstring getIcuDataName(JNIEnv* env, jclass clazz) {
+ return env->NewStringUTF(U_ICUDATA_NAME);
+}
+
// ---- Registration ----
extern void register_android_system_OsConstants(JNIEnv* env);
@@ -201,6 +206,7 @@ static const JNINativeMethod sMethods[] =
{ "setenv", "(Ljava/lang/String;Ljava/lang/String;Z)V", (void*)Linux_setenv },
{ "getpid", "()I", (void*)Linux_getpid },
{ "gettid", "()I", (void*)Linux_gettid },
+ { "getIcuDataName", "()Ljava/lang/String;", (void*)getIcuDataName },
};
extern "C" jint JNI_OnLoad(JavaVM* vm, void* /* reserved */) {
diff --git a/ravenwood/texts/ravenwood-annotation-allowed-classes.txt b/ravenwood/texts/ravenwood-annotation-allowed-classes.txt
index e202d0ecfa23..7462cc2f384a 100644
--- a/ravenwood/texts/ravenwood-annotation-allowed-classes.txt
+++ b/ravenwood/texts/ravenwood-annotation-allowed-classes.txt
@@ -259,6 +259,8 @@ android.database.sqlite.SQLiteClosable
android.database.sqlite.SQLiteException
android.text.TextUtils
+android.text.Html
+android.text.HtmlToSpannedConverter
android.accounts.Account
@@ -278,6 +280,10 @@ android.graphics.PointF
android.graphics.Rect
android.graphics.RectF
+android.graphics.fonts.SystemFonts
+
+android.graphics.text.LineBreakConfig
+
android.content.ContentProvider
android.app.ActivityManager
@@ -383,3 +389,228 @@ android.app.compat.*
com.android.server.compat.*
com.android.internal.compat.*
android.app.AppCompatCallbacks
+android.graphics.AvoidXfermode
+android.graphics.BLASTBufferQueue
+android.graphics.BaseCanvas
+android.graphics.BaseRecordingCanvas
+android.graphics.Bitmap
+android.graphics.BitmapFactory
+android.graphics.BitmapRegionDecoder
+android.graphics.BitmapShader
+android.graphics.BlendMode
+android.graphics.BlendModeColorFilter
+android.graphics.BlurMaskFilter
+android.graphics.Camera
+android.graphics.Canvas
+android.graphics.CanvasProperty
+android.graphics.ColorFilter
+android.graphics.ColorMatrix
+android.graphics.ColorMatrixColorFilter
+android.graphics.Compatibility
+android.graphics.ComposePathEffect
+android.graphics.ComposeShader
+android.graphics.CornerPathEffect
+android.graphics.DashPathEffect
+android.graphics.DiscretePathEffect
+android.graphics.DrawFilter
+android.graphics.EmbossMaskFilter
+android.graphics.FontFamily
+android.graphics.FontListParser
+android.graphics.ForceDarkType
+android.graphics.FrameInfo
+android.graphics.Gainmap
+android.graphics.GraphicBuffer
+android.graphics.GraphicsProtos
+android.graphics.GraphicsStatsService
+android.graphics.HardwareBufferRenderer
+android.graphics.HardwareRenderer
+android.graphics.HardwareRendererObserver
+android.graphics.ImageDecoder
+android.graphics.ImageFormat
+android.graphics.LayerRasterizer
+android.graphics.LeakyTypefaceStorage
+android.graphics.LightingColorFilter
+android.graphics.LinearGradient
+android.graphics.MaskFilter
+android.graphics.Mesh
+android.graphics.MeshSpecification
+android.graphics.Movie
+android.graphics.NinePatch
+android.graphics.Paint
+android.graphics.PaintFlagsDrawFilter
+android.graphics.PathDashPathEffect
+android.graphics.PathEffect
+android.graphics.PathIterator
+android.graphics.PathMeasure
+android.graphics.Picture
+android.graphics.PixelXorXfermode
+android.graphics.PorterDuff
+android.graphics.PorterDuffColorFilter
+android.graphics.PorterDuffXfermode
+android.graphics.PostProcessor
+android.graphics.RadialGradient
+android.graphics.Rasterizer
+android.graphics.RecordingCanvas
+android.graphics.Region
+android.graphics.RegionIterator
+android.graphics.RenderEffect
+android.graphics.RenderNode
+android.graphics.RuntimeColorFilter
+android.graphics.RuntimeShader
+android.graphics.RuntimeXfermode
+android.graphics.Shader
+android.graphics.SumPathEffect
+android.graphics.SurfaceTexture
+android.graphics.SweepGradient
+android.graphics.TableMaskFilter
+android.graphics.TemporaryBuffer
+android.graphics.TextureLayer
+android.graphics.Typeface
+android.graphics.Xfermode
+android.graphics.YuvImage
+android.graphics.fonts.Font
+android.graphics.fonts.FontCustomizationParser
+android.graphics.fonts.FontFamily
+android.graphics.fonts.FontFamilyUpdateRequest
+android.graphics.fonts.FontFileUpdateRequest
+android.graphics.fonts.FontFileUtil
+android.graphics.fonts.FontStyle
+android.graphics.fonts.FontVariationAxis
+android.graphics.text.GraphemeBreak
+android.graphics.text.LineBreaker
+android.graphics.text.MeasuredText
+android.graphics.text.PositionedGlyphs
+android.graphics.text.TextRunShaper
+android.text.AlteredCharSequence
+android.text.AndroidBidi
+android.text.AndroidCharacter
+android.text.Annotation
+android.text.AutoGrowArray
+android.text.AutoText
+android.text.BidiFormatter
+android.text.BoringLayout
+android.text.CharSequenceCharacterIterator
+android.text.ClipboardManager
+android.text.DynamicLayout
+android.text.Editable
+android.text.Emoji
+android.text.EmojiConsistency
+android.text.FontConfig
+android.text.GetChars
+android.text.GraphemeClusterSegmentFinder
+android.text.GraphicsOperations
+android.text.Highlights
+android.text.Hyphenator
+android.text.InputFilter
+android.text.InputType
+android.text.Layout
+android.text.LoginFilter
+android.text.MeasuredParagraph
+android.text.NoCopySpan
+android.text.PackedIntVector
+android.text.PackedObjectVector
+android.text.ParcelableSpan
+android.text.PrecomputedText
+android.text.SegmentFinder
+android.text.Selection
+android.text.SpanColors
+android.text.SpanSet
+android.text.SpanWatcher
+android.text.Spannable
+android.text.SpannableString
+android.text.SpannableStringBuilder
+android.text.SpannableStringInternal
+android.text.Spanned
+android.text.SpannedString
+android.text.StaticLayout
+android.text.TextDirectionHeuristic
+android.text.TextDirectionHeuristics
+android.text.TextLine
+android.text.TextPaint
+android.text.TextShaper
+android.text.TextWatcher
+android.text.WordSegmentFinder
+android.text.format.DateFormat
+android.text.format.DateIntervalFormat
+android.text.format.DateTimeFormat
+android.text.format.DateUtils
+android.text.format.DateUtilsBridge
+android.text.format.Formatter
+android.text.format.RelativeDateTimeFormatter
+android.text.format.Time
+android.text.format.TimeFormatter
+android.text.format.TimeMigrationUtils
+android.text.method.AllCapsTransformationMethod
+android.text.method.ArrowKeyMovementMethod
+android.text.method.BaseKeyListener
+android.text.method.BaseMovementMethod
+android.text.method.CharacterPickerDialog
+android.text.method.DateKeyListener
+android.text.method.DateTimeKeyListener
+android.text.method.DialerKeyListener
+android.text.method.DigitsKeyListener
+android.text.method.HideReturnsTransformationMethod
+android.text.method.InsertModeTransformationMethod
+android.text.method.KeyListener
+android.text.method.LinkMovementMethod
+android.text.method.MetaKeyKeyListener
+android.text.method.MovementMethod
+android.text.method.MultiTapKeyListener
+android.text.method.NumberKeyListener
+android.text.method.OffsetMapping
+android.text.method.PasswordTransformationMethod
+android.text.method.QwertyKeyListener
+android.text.method.ReplacementTransformationMethod
+android.text.method.ScrollingMovementMethod
+android.text.method.SingleLineTransformationMethod
+android.text.method.TextKeyListener
+android.text.method.TimeKeyListener
+android.text.method.Touch
+android.text.method.TransformationMethod
+android.text.method.TransformationMethod2
+android.text.method.TranslationTransformationMethod
+android.text.method.WordIterator
+android.text.style.AbsoluteSizeSpan
+android.text.style.AccessibilityClickableSpan
+android.text.style.AccessibilityReplacementSpan
+android.text.style.AccessibilityURLSpan
+android.text.style.AlignmentSpan
+android.text.style.BackgroundColorSpan
+android.text.style.BulletSpan
+android.text.style.CharacterStyle
+android.text.style.ClickableSpan
+android.text.style.ForegroundColorSpan
+android.text.style.IconMarginSpan
+android.text.style.LeadingMarginSpan
+android.text.style.LineBackgroundSpan
+android.text.style.LineBreakConfigSpan
+android.text.style.LineHeightSpan
+android.text.style.LocaleSpan
+android.text.style.MaskFilterSpan
+android.text.style.MetricAffectingSpan
+android.text.style.NoWritingToolsSpan
+android.text.style.ParagraphStyle
+android.text.style.QuoteSpan
+android.text.style.RasterizerSpan
+android.text.style.RelativeSizeSpan
+android.text.style.ReplacementSpan
+android.text.style.ScaleXSpan
+android.text.style.SpanUtils
+android.text.style.SpellCheckSpan
+android.text.style.StrikethroughSpan
+android.text.style.StyleSpan
+android.text.style.SubscriptSpan
+android.text.style.SuggestionRangeSpan
+android.text.style.SuggestionSpan
+android.text.style.SuperscriptSpan
+android.text.style.TabStopSpan
+android.text.style.TextAppearanceSpan
+android.text.style.TtsSpan
+android.text.style.TypefaceSpan
+android.text.style.URLSpan
+android.text.style.UnderlineSpan
+android.text.style.UpdateAppearance
+android.text.style.UpdateLayout
+android.text.style.WrapTogetherSpan
+android.text.util.Rfc822Token
+android.text.util.Rfc822Tokenizer
diff --git a/ravenwood/texts/ravenwood-build.prop b/ravenwood/texts/ravenwood-build.prop
index 7cc4454a6e56..37c50f11f73f 100644
--- a/ravenwood/texts/ravenwood-build.prop
+++ b/ravenwood/texts/ravenwood-build.prop
@@ -8,7 +8,11 @@ ro.soc.manufacturer=Android
ro.soc.model=Ravenwood
ro.debuggable=1
-# The ones starting with "ro.product" or "ro.bild" will be copied to all "partitions" too.
+# For the graphics stack
+ro.hwui.max_texture_allocation_size=104857600
+persist.sys.locale=en-US
+
+# The ones starting with "ro.product" or "ro.build" will be copied to all "partitions" too.
# See RavenwoodSystemProperties.
ro.product.brand=Android
ro.product.device=Ravenwood
diff --git a/ravenwood/texts/ravenwood-common-policies.txt b/ravenwood/texts/ravenwood-common-policies.txt
index fd4ea6cf40c2..f0f4b8580f7d 100644
--- a/ravenwood/texts/ravenwood-common-policies.txt
+++ b/ravenwood/texts/ravenwood-common-policies.txt
@@ -21,3 +21,7 @@ class java.io.FileDescriptor # no-pta
method setInt$ @com.android.ravenwood.RavenwoodJdkPatch.setInt$
class java.util.LinkedHashMap # no-pta
method eldest @com.android.ravenwood.RavenwoodJdkPatch.eldest
+
+# Always set flag UNICODE_CHARACTER_CLASS when compiling regex
+class java.util.regex.Pattern keep
+ method compile @com.android.ravenwood.RavenwoodJdkPatch.compilePattern
diff --git a/ravenwood/texts/ravenwood-framework-policies.txt b/ravenwood/texts/ravenwood-framework-policies.txt
index fff9e6ad41d5..0695316543ae 100644
--- a/ravenwood/texts/ravenwood-framework-policies.txt
+++ b/ravenwood/texts/ravenwood-framework-policies.txt
@@ -63,3 +63,22 @@ class android.text.ClipboardManager keep # no-pta
# Just enough to allow ResourcesManager to run
class android.hardware.display.DisplayManagerGlobal keep # no-pta
method getInstance ()Landroid/hardware/display/DisplayManagerGlobal; ignore # no-pta
+
+# Bare minimum to support running ImageDecoderTest
+class android.graphics.drawable.Drawable$ConstantState keepclass # no-pta
+class android.graphics.drawable.BitmapDrawable$BitmapState keepclass # no-pta
+class android.graphics.drawable.BitmapDrawable keep # no-pta
+ method <init> (Landroid/content/res/Resources;Landroid/graphics/Bitmap;)V keep
+ method init * keep
+ method updateLocalState * keep
+ method computeBitmapSize * keep
+ method getIntrinsicWidth * keep
+ method getIntrinsicHeight * keep
+ method getBitmap * keep
+class android.graphics.drawable.Drawable keep # no-pta
+ method <init> ()V keep
+ method resolveDensity * keep
+ method updateBlendModeFilter * ignore
+
+class android.os.StrictMode keep # no-pta
+ method noteSlowCall (Ljava/lang/String;)V ignore
diff --git a/services/accessibility/accessibility.aconfig b/services/accessibility/accessibility.aconfig
index 5e1fe8a60973..b52b3dabd47d 100644
--- a/services/accessibility/accessibility.aconfig
+++ b/services/accessibility/accessibility.aconfig
@@ -145,16 +145,6 @@ flag {
}
flag {
- name: "enable_magnification_follows_mouse_bugfix"
- namespace: "accessibility"
- description: "Whether to enable mouse following for fullscreen magnification"
- bug: "354696546"
- metadata {
- purpose: PURPOSE_BUGFIX
- }
-}
-
-flag {
name: "enable_magnification_follows_mouse_with_pointer_motion_filter"
namespace: "accessibility"
description: "Whether to enable mouse following using pointer motion filter"
diff --git a/services/accessibility/java/com/android/server/accessibility/AccessibilityInputFilter.java b/services/accessibility/java/com/android/server/accessibility/AccessibilityInputFilter.java
index 9b5f22afb81d..4e41808626b4 100644
--- a/services/accessibility/java/com/android/server/accessibility/AccessibilityInputFilter.java
+++ b/services/accessibility/java/com/android/server/accessibility/AccessibilityInputFilter.java
@@ -53,7 +53,6 @@ import com.android.server.accessibility.magnification.FullScreenMagnificationGes
import com.android.server.accessibility.magnification.FullScreenMagnificationVibrationHelper;
import com.android.server.accessibility.magnification.MagnificationGestureHandler;
import com.android.server.accessibility.magnification.MagnificationKeyHandler;
-import com.android.server.accessibility.magnification.MouseEventHandler;
import com.android.server.accessibility.magnification.WindowMagnificationGestureHandler;
import com.android.server.accessibility.magnification.WindowMagnificationPromptController;
import com.android.server.policy.WindowManagerPolicy;
@@ -899,8 +898,7 @@ class AccessibilityInputFilter extends InputFilter implements EventStreamTransfo
triggerable,
new WindowMagnificationPromptController(displayContext, mUserId),
displayId,
- fullScreenMagnificationVibrationHelper,
- new MouseEventHandler(controller));
+ fullScreenMagnificationVibrationHelper);
}
return magnificationGestureHandler;
}
diff --git a/services/accessibility/java/com/android/server/accessibility/autoclick/AutoclickController.java b/services/accessibility/java/com/android/server/accessibility/autoclick/AutoclickController.java
index 4fa0d506f09e..aa82df493f84 100644
--- a/services/accessibility/java/com/android/server/accessibility/autoclick/AutoclickController.java
+++ b/services/accessibility/java/com/android/server/accessibility/autoclick/AutoclickController.java
@@ -101,8 +101,15 @@ public class AutoclickController extends BaseEventStreamTransformation {
}
@Override
- public void toggleAutoclickPause() {
- // TODO(b/388872274): allows users to pause the autoclick.
+ public void toggleAutoclickPause(boolean paused) {
+ if (paused) {
+ if (mClickScheduler != null) {
+ mClickScheduler.cancel();
+ }
+ if (mAutoclickIndicatorScheduler != null) {
+ mAutoclickIndicatorScheduler.cancel();
+ }
+ }
}
};
@@ -133,7 +140,9 @@ public class AutoclickController extends BaseEventStreamTransformation {
mAutoclickIndicatorScheduler);
}
- handleMouseMotion(event, policyFlags);
+ if (!isPaused()) {
+ handleMouseMotion(event, policyFlags);
+ }
} else if (mClickScheduler != null) {
mClickScheduler.cancel();
}
@@ -216,6 +225,11 @@ public class AutoclickController extends BaseEventStreamTransformation {
}
}
+ private boolean isPaused() {
+ // TODO (b/397460424): Unpause when hovering over panel.
+ return Flags.enableAutoclickIndicator() && mAutoclickTypePanel.isPaused();
+ }
+
/**
* Observes autoclick setting values, and updates ClickScheduler delay and indicator size
* whenever the setting value changes.
diff --git a/services/accessibility/java/com/android/server/accessibility/autoclick/AutoclickIndicatorView.java b/services/accessibility/java/com/android/server/accessibility/autoclick/AutoclickIndicatorView.java
index 01f359fc5753..6beb47acb8b1 100644
--- a/services/accessibility/java/com/android/server/accessibility/autoclick/AutoclickIndicatorView.java
+++ b/services/accessibility/java/com/android/server/accessibility/autoclick/AutoclickIndicatorView.java
@@ -123,6 +123,7 @@ public class AutoclickIndicatorView extends View {
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
// Get the screen dimensions.
+ // TODO(b/397944891): Handle device rotation case.
DisplayMetrics displayMetrics = getResources().getDisplayMetrics();
int screenWidth = displayMetrics.widthPixels;
int screenHeight = displayMetrics.heightPixels;
diff --git a/services/accessibility/java/com/android/server/accessibility/autoclick/AutoclickTypePanel.java b/services/accessibility/java/com/android/server/accessibility/autoclick/AutoclickTypePanel.java
index 23c5cc4111f6..342675a65360 100644
--- a/services/accessibility/java/com/android/server/accessibility/autoclick/AutoclickTypePanel.java
+++ b/services/accessibility/java/com/android/server/accessibility/autoclick/AutoclickTypePanel.java
@@ -25,6 +25,7 @@ import android.graphics.drawable.Drawable;
import android.graphics.drawable.GradientDrawable;
import android.view.Gravity;
import android.view.LayoutInflater;
+import android.view.MotionEvent;
import android.view.View;
import android.view.WindowInsets;
import android.view.WindowManager;
@@ -51,6 +52,16 @@ public class AutoclickTypePanel {
public static final int CORNER_TOP_LEFT = 2;
public static final int CORNER_TOP_RIGHT = 3;
+ // Distance between panel and screen edge.
+ // TODO(b/396402941): Finalize edge margin.
+ private static final int PANEL_EDGE_MARGIN = 15;
+
+ // Touch point when drag starts, it can be anywhere inside the panel.
+ private float mTouchStartX, mTouchStartY;
+ // Initial panel position in screen coordinates.
+ private int mPanelStartX, mPanelStartY;
+ private boolean mIsDragging = false;
+
// Types of click the AutoclickTypePanel supports.
@IntDef({
AUTOCLICK_TYPE_LEFT_CLICK,
@@ -79,11 +90,20 @@ public class AutoclickTypePanel {
// An interface exposed to {@link AutoclickController) to handle different actions on the panel,
// including changing autoclick type, pausing/resuming autoclick.
public interface ClickPanelControllerInterface {
- // Allows users to change a different autoclick type.
+ /**
+ * Allows users to change a different autoclick type.
+ *
+ * @param clickType The new autoclick type to use. Should be one of the values defined in
+ * {@link AutoclickType}.
+ */
void handleAutoclickTypeChange(@AutoclickType int clickType);
- // Allows users to pause/resume the autoclick.
- void toggleAutoclickPause();
+ /**
+ * Allows users to pause or resume autoclick.
+ *
+ * @param paused {@code true} to pause autoclick, {@code false} to resume.
+ */
+ void toggleAutoclickPause(boolean paused);
}
private final Context mContext;
@@ -92,6 +112,8 @@ public class AutoclickTypePanel {
private final WindowManager mWindowManager;
+ private WindowManager.LayoutParams mParams;
+
private final ClickPanelControllerInterface mClickPanelController;
// Whether the panel is expanded or not.
@@ -124,6 +146,7 @@ public class AutoclickTypePanel {
mContext = context;
mWindowManager = windowManager;
mClickPanelController = clickPanelController;
+ mParams = getDefaultLayoutParams();
mPauseButtonDrawable = mContext.getDrawable(
R.drawable.accessibility_autoclick_pause);
@@ -145,6 +168,91 @@ public class AutoclickTypePanel {
mPositionButton = mContentView.findViewById(R.id.accessibility_autoclick_position_layout);
initializeButtonState();
+
+ // Set up touch event handling for the panel to allow the user to drag and reposition the
+ // panel by touching and moving it.
+ mContentView.setOnTouchListener(this::onPanelTouch);
+ }
+
+ /**
+ * Handles touch events on the panel, enabling the user to drag and reposition it.
+ * This function supports the draggable panel feature, allowing users to move the panel
+ * to different screen locations for better usability and customization.
+ */
+ private boolean onPanelTouch(View v, MotionEvent event) {
+ // TODO(b/397681794): Make sure this works on multiple screens.
+ switch (event.getAction()) {
+ case MotionEvent.ACTION_DOWN:
+ // Store initial touch positions.
+ mTouchStartX = event.getRawX();
+ mTouchStartY = event.getRawY();
+
+ // Store initial panel position relative to screen's top-left corner.
+ // getLocationOnScreen provides coordinates relative to the top-left corner of the
+ // screen's display. We are using this coordinate system to consistently track the
+ // panel's position during drag operations.
+ int[] location = new int[2];
+ v.getLocationOnScreen(location);
+ mPanelStartX = location[0];
+ mPanelStartY = location[1];
+ return true;
+ case MotionEvent.ACTION_MOVE:
+ mIsDragging = true;
+
+ // Set panel gravity to TOP|LEFT to match getLocationOnScreen's coordinate system
+ mParams.gravity = Gravity.LEFT | Gravity.TOP;
+
+ if (mIsDragging) {
+ // Calculate touch distance moved from start position.
+ float deltaX = event.getRawX() - mTouchStartX;
+ float deltaY = event.getRawY() - mTouchStartY;
+
+ // Update panel position, based on Top-Left absolute positioning.
+ mParams.x = mPanelStartX + (int) deltaX;
+ mParams.y = mPanelStartY + (int) deltaY;
+ mWindowManager.updateViewLayout(mContentView, mParams);
+ }
+ return true;
+ case MotionEvent.ACTION_UP:
+ case MotionEvent.ACTION_CANCEL:
+ if (mIsDragging) {
+ // When drag ends, snap panel to nearest edge.
+ snapToNearestEdge(mParams);
+ }
+ mIsDragging = false;
+ return true;
+ }
+ return false;
+ }
+
+ private void snapToNearestEdge(WindowManager.LayoutParams params) {
+ // Get screen width to determine which side to snap to.
+ // TODO(b/397944891): Handle device rotation case.
+ int screenWidth = mContext.getResources().getDisplayMetrics().widthPixels;
+ int yPosition = params.y;
+
+ // Determine which half of the screen the panel is on.
+ boolean isOnLeftHalf = params.x < screenWidth / 2;
+
+ if (isOnLeftHalf) {
+ // Snap to left edge. Set params.gravity to make sure x, y offsets from correct anchor.
+ params.gravity = Gravity.START | Gravity.TOP;
+ // Set the current corner to be bottom-left to ensure that the subsequent reposition
+ // action rotates the panel clockwise from bottom-left towards top-left.
+ mCurrentCornerIndex = 1;
+ } else {
+ // Snap to right edge. Set params.gravity to make sure x, y offsets from correct anchor.
+ params.gravity = Gravity.END | Gravity.TOP;
+ // Set the current corner to be top-right to ensure that the subsequent reposition
+ // action rotates the panel clockwise from top-right towards bottom-right.
+ mCurrentCornerIndex = 3;
+ }
+
+ // Apply final position: set params.x to be edge margin, params.y to maintain vertical
+ // position.
+ params.x = PANEL_EDGE_MARGIN;
+ params.y = yPosition;
+ mWindowManager.updateViewLayout(mContentView, params);
}
private void initializeButtonState() {
@@ -200,7 +308,7 @@ public class AutoclickTypePanel {
}
public void show() {
- mWindowManager.addView(mContentView, getLayoutParams());
+ mWindowManager.addView(mContentView, mParams);
}
public void hide() {
@@ -211,6 +319,10 @@ public class AutoclickTypePanel {
mWindowManager.removeView(mContentView);
}
+ public boolean isPaused() {
+ return mPaused;
+ }
+
/** Toggles the panel expanded or collapsed state. */
private void togglePanelExpansion(@AutoclickType int clickType) {
final LinearLayout button = getButtonFromClickType(clickType);
@@ -234,6 +346,7 @@ public class AutoclickTypePanel {
private void togglePause() {
mPaused = !mPaused;
+ mClickPanelController.toggleAutoclickPause(mPaused);
ImageButton imageButton = (ImageButton) mPauseButton.getChildAt(/* index= */ 0);
if (mPaused) {
@@ -277,9 +390,8 @@ public class AutoclickTypePanel {
@Corner int nextCornerIndex = (mCurrentCornerIndex + 1) % CORNER_ROTATION_ORDER.length;
mCurrentCornerIndex = nextCornerIndex;
- // getLayoutParams() will update the panel position based on current corner.
- WindowManager.LayoutParams params = getLayoutParams();
- mWindowManager.updateViewLayout(mContentView, params);
+ setPanelPositionForCorner(mParams, mCurrentCornerIndex);
+ mWindowManager.updateViewLayout(mContentView, mParams);
}
private void setPanelPositionForCorner(WindowManager.LayoutParams params, @Corner int corner) {
@@ -289,22 +401,22 @@ public class AutoclickTypePanel {
switch (corner) {
case CORNER_BOTTOM_RIGHT:
params.gravity = Gravity.END | Gravity.BOTTOM;
- params.x = 15;
+ params.x = PANEL_EDGE_MARGIN;
params.y = 90;
break;
case CORNER_BOTTOM_LEFT:
params.gravity = Gravity.START | Gravity.BOTTOM;
- params.x = 15;
+ params.x = PANEL_EDGE_MARGIN;
params.y = 90;
break;
case CORNER_TOP_LEFT:
params.gravity = Gravity.START | Gravity.TOP;
- params.x = 15;
+ params.x = PANEL_EDGE_MARGIN;
params.y = 30;
break;
case CORNER_TOP_RIGHT:
params.gravity = Gravity.END | Gravity.TOP;
- params.x = 15;
+ params.x = PANEL_EDGE_MARGIN;
params.y = 30;
break;
default:
@@ -329,13 +441,22 @@ public class AutoclickTypePanel {
return mCurrentCornerIndex;
}
+ @VisibleForTesting
+ WindowManager.LayoutParams getLayoutParamsForTesting() {
+ return mParams;
+ }
+
+ @VisibleForTesting
+ boolean getIsDraggingForTesting() {
+ return mIsDragging;
+ }
+
/**
* Retrieves the layout params for AutoclickIndicatorView, used when it's added to the Window
* Manager.
*/
- @VisibleForTesting
@NonNull
- WindowManager.LayoutParams getLayoutParams() {
+ private WindowManager.LayoutParams getDefaultLayoutParams() {
final WindowManager.LayoutParams layoutParams = new WindowManager.LayoutParams();
layoutParams.type = WindowManager.LayoutParams.TYPE_APPLICATION_OVERLAY;
layoutParams.flags = WindowManager.LayoutParams.FLAG_NOT_TOUCH_MODAL;
@@ -348,7 +469,7 @@ public class AutoclickTypePanel {
mContext.getString(R.string.accessibility_autoclick_type_settings_panel_title);
layoutParams.width = WindowManager.LayoutParams.WRAP_CONTENT;
layoutParams.height = WindowManager.LayoutParams.WRAP_CONTENT;
- setPanelPositionForCorner(layoutParams, mCurrentCornerIndex);
+ setPanelPositionForCorner(layoutParams, CORNER_BOTTOM_RIGHT);
return layoutParams;
}
}
diff --git a/services/accessibility/java/com/android/server/accessibility/magnification/FullScreenMagnificationGestureHandler.java b/services/accessibility/java/com/android/server/accessibility/magnification/FullScreenMagnificationGestureHandler.java
index d11ae0a6ad97..e0dd8b601a3d 100644
--- a/services/accessibility/java/com/android/server/accessibility/magnification/FullScreenMagnificationGestureHandler.java
+++ b/services/accessibility/java/com/android/server/accessibility/magnification/FullScreenMagnificationGestureHandler.java
@@ -182,7 +182,7 @@ public class FullScreenMagnificationGestureHandler extends MagnificationGestureH
private final int mMinimumVelocity;
private final int mMaximumVelocity;
- private MouseEventHandler mMouseEventHandler;
+ private final MouseEventHandler mMouseEventHandler;
public FullScreenMagnificationGestureHandler(
@UiContext Context context,
@@ -194,8 +194,7 @@ public class FullScreenMagnificationGestureHandler extends MagnificationGestureH
boolean detectShortcutTrigger,
@NonNull WindowMagnificationPromptController promptController,
int displayId,
- FullScreenMagnificationVibrationHelper fullScreenMagnificationVibrationHelper,
- MouseEventHandler mouseEventHandler) {
+ FullScreenMagnificationVibrationHelper fullScreenMagnificationVibrationHelper) {
this(
context,
fullScreenMagnificationController,
@@ -210,8 +209,7 @@ public class FullScreenMagnificationGestureHandler extends MagnificationGestureH
/* magnificationLogger= */ null,
ViewConfiguration.get(context),
new OneFingerPanningSettingsProvider(
- context, Flags.enableMagnificationOneFingerPanningGesture()),
- mouseEventHandler);
+ context, Flags.enableMagnificationOneFingerPanningGesture()));
}
/** Constructor for tests. */
@@ -229,8 +227,7 @@ public class FullScreenMagnificationGestureHandler extends MagnificationGestureH
FullScreenMagnificationVibrationHelper fullScreenMagnificationVibrationHelper,
MagnificationLogger magnificationLogger,
ViewConfiguration viewConfiguration,
- OneFingerPanningSettingsProvider oneFingerPanningSettingsProvider,
- MouseEventHandler mouseEventHandler) {
+ OneFingerPanningSettingsProvider oneFingerPanningSettingsProvider) {
super(displayId, detectSingleFingerTripleTap, detectTwoFingerTripleTap,
detectShortcutTrigger, trace, callback);
if (DEBUG_ALL) {
@@ -316,7 +313,7 @@ public class FullScreenMagnificationGestureHandler extends MagnificationGestureH
mOverscrollEdgeSlop = context.getResources().getDimensionPixelSize(
R.dimen.accessibility_fullscreen_magnification_gesture_edge_slop);
mIsWatch = context.getPackageManager().hasSystemFeature(PackageManager.FEATURE_WATCH);
- mMouseEventHandler = mouseEventHandler;
+ mMouseEventHandler = new MouseEventHandler(mFullScreenMagnificationController);
if (mDetectShortcutTrigger) {
mScreenStateReceiver = new ScreenStateReceiver(context, this);
@@ -340,15 +337,14 @@ public class FullScreenMagnificationGestureHandler extends MagnificationGestureH
@Override
void handleMouseOrStylusEvent(MotionEvent event, MotionEvent rawEvent, int policyFlags) {
- if (Flags.enableMagnificationFollowsMouseBugfix()) {
- if (mFullScreenMagnificationController.isActivated(mDisplayId)) {
- // TODO(b/354696546): Allow mouse/stylus to activate whichever display they are
- // over, rather than only interacting with the current display.
-
- // Send through the mouse/stylus event handler.
- mMouseEventHandler.onEvent(event, mDisplayId);
- }
+ if (!mFullScreenMagnificationController.isActivated(mDisplayId)) {
+ return;
}
+ // TODO(b/354696546): Allow mouse/stylus to activate whichever display they are
+ // over, rather than only interacting with the current display.
+
+ // Send through the mouse/stylus event handler.
+ mMouseEventHandler.onEvent(event, mDisplayId);
}
private void handleTouchEventWith(
@@ -1170,8 +1166,7 @@ public class FullScreenMagnificationGestureHandler extends MagnificationGestureH
protected void cacheDelayedMotionEvent(MotionEvent event, MotionEvent rawEvent,
int policyFlags) {
- if (Flags.enableMagnificationFollowsMouseBugfix()
- && !event.isFromSource(SOURCE_TOUCHSCREEN)) {
+ if (!event.isFromSource(SOURCE_TOUCHSCREEN)) {
// Only touch events need to be cached and sent later.
return;
}
diff --git a/services/accessibility/java/com/android/server/accessibility/magnification/MagnificationGestureHandler.java b/services/accessibility/java/com/android/server/accessibility/magnification/MagnificationGestureHandler.java
index fa86ba39bb1a..6b39c98887bd 100644
--- a/services/accessibility/java/com/android/server/accessibility/magnification/MagnificationGestureHandler.java
+++ b/services/accessibility/java/com/android/server/accessibility/magnification/MagnificationGestureHandler.java
@@ -146,8 +146,7 @@ public abstract class MagnificationGestureHandler extends BaseEventStreamTransfo
} break;
case SOURCE_MOUSE:
case SOURCE_STYLUS: {
- if (magnificationShortcutExists()
- && Flags.enableMagnificationFollowsMouseBugfix()) {
+ if (magnificationShortcutExists()) {
handleMouseOrStylusEvent(event, rawEvent, policyFlags);
}
}
diff --git a/services/appwidget/java/com/android/server/appwidget/AppWidgetServiceImpl.java b/services/appwidget/java/com/android/server/appwidget/AppWidgetServiceImpl.java
index 74a87ed92f52..4441db78e4b5 100644
--- a/services/appwidget/java/com/android/server/appwidget/AppWidgetServiceImpl.java
+++ b/services/appwidget/java/com/android/server/appwidget/AppWidgetServiceImpl.java
@@ -5232,7 +5232,7 @@ class AppWidgetServiceImpl extends IAppWidgetService.Stub implements WidgetBacku
}
}
return singleCategoryKeyedEntries;
- } catch (IOException e) {
+ } catch (Exception e) {
Slog.e(TAG, "Failed to load generated previews for " + provider, e);
return new SparseArray<>();
}
@@ -5261,7 +5261,7 @@ class AppWidgetServiceImpl extends IAppWidgetService.Stub implements WidgetBacku
try {
provider.info.generatedPreviewCategories = readGeneratedPreviewCategoriesFromProto(
input);
- } catch (IOException e) {
+ } catch (Exception e) {
Slog.e(TAG, "Failed to read generated previews from file for " + provider, e);
previewsFile.delete();
provider.info.generatedPreviewCategories = 0;
@@ -5314,7 +5314,7 @@ class AppWidgetServiceImpl extends IAppWidgetService.Stub implements WidgetBacku
scheduleNotifyGroupHostsForProvidersChangedLocked(provider.getUserId());
}
}
- } catch (IOException e) {
+ } catch (Exception e) {
if (file != null && stream != null) {
file.failWrite(stream);
}
diff --git a/services/core/java/com/android/server/StorageManagerService.java b/services/core/java/com/android/server/StorageManagerService.java
index d2a5734f323f..b6fe0ad37078 100644
--- a/services/core/java/com/android/server/StorageManagerService.java
+++ b/services/core/java/com/android/server/StorageManagerService.java
@@ -49,7 +49,6 @@ import static com.android.internal.util.XmlUtils.writeStringAttribute;
import static org.xmlpull.v1.XmlPullParser.END_DOCUMENT;
import static org.xmlpull.v1.XmlPullParser.START_TAG;
-import android.annotation.EnforcePermission;
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.annotation.UserIdInt;
@@ -2443,6 +2442,7 @@ class StorageManagerService extends IStorageManager.Stub
} catch (Installer.InstallerException e) {
Slog.e(TAG, "Failed unmount mirror data", e);
}
+ extendWatchdogTimeout("#unmount might be slow");
mVold.unmount(vol.getId());
mStorageSessionController.onVolumeUnmount(vol.getImmutableVolumeInfo());
} catch (Exception e) {
diff --git a/services/core/java/com/android/server/am/ActivityManagerService.java b/services/core/java/com/android/server/am/ActivityManagerService.java
index 07a4d52f56ec..8b701f0e2069 100644
--- a/services/core/java/com/android/server/am/ActivityManagerService.java
+++ b/services/core/java/com/android/server/am/ActivityManagerService.java
@@ -198,6 +198,7 @@ import android.annotation.Nullable;
import android.annotation.PermissionMethod;
import android.annotation.PermissionName;
import android.annotation.RequiresPermission;
+import android.annotation.SpecialUsers.CanBeALL;
import android.annotation.UserIdInt;
import android.app.Activity;
import android.app.ActivityClient;
@@ -3921,8 +3922,8 @@ public class ActivityManagerService extends IActivityManager.Stub
* The pkg name and app id have to be specified.
*/
@Override
- public void killApplication(String pkg, int appId, int userId, String reason,
- int exitInfoReason) {
+ public void killApplication(String pkg, int appId, @CanBeALL @UserIdInt int userId,
+ String reason, int exitInfoReason) {
if (pkg == null) {
return;
}
@@ -4307,7 +4308,7 @@ public class ActivityManagerService extends IActivityManager.Stub
final boolean forceStopPackageLocked(String packageName, int appId,
boolean callerWillRestart, boolean purgeCache, boolean doit,
boolean evenPersistent, boolean uninstalling, boolean packageStateStopped,
- int userId, String reasonString, int reason) {
+ @CanBeALL @UserIdInt int userId, String reasonString, int reason) {
return forceStopPackageInternalLocked(packageName, appId, callerWillRestart, purgeCache,
doit, evenPersistent, uninstalling, packageStateStopped, userId, reasonString,
reason, ProcessList.INVALID_ADJ);
@@ -4317,7 +4318,7 @@ public class ActivityManagerService extends IActivityManager.Stub
private boolean forceStopPackageInternalLocked(String packageName, int appId,
boolean callerWillRestart, boolean purgeCache, boolean doit,
boolean evenPersistent, boolean uninstalling, boolean packageStateStopped,
- int userId, String reasonString, int reason, int minOomAdj) {
+ @CanBeALL @UserIdInt int userId, String reasonString, int reason, int minOomAdj) {
int i;
if (userId == UserHandle.USER_ALL && packageName == null) {
@@ -18093,7 +18094,7 @@ public class ActivityManagerService extends IActivityManager.Stub
}
@Override
- public void killApplicationSync(String pkgName, int appId, int userId,
+ public void killApplicationSync(String pkgName, int appId, @CanBeALL @UserIdInt int userId,
String reason, int exitInfoReason) {
if (pkgName == null) {
return;
diff --git a/services/core/java/com/android/server/am/OomAdjuster.java b/services/core/java/com/android/server/am/OomAdjuster.java
index 61c5501a7b5a..13d367a95942 100644
--- a/services/core/java/com/android/server/am/OomAdjuster.java
+++ b/services/core/java/com/android/server/am/OomAdjuster.java
@@ -446,6 +446,8 @@ public class OomAdjuster {
private static final int CACHING_UI_SERVICE_CLIENT_ADJ_THRESHOLD =
Flags.raiseBoundUiServiceThreshold() ? SERVICE_ADJ : PERCEPTIBLE_APP_ADJ;
+ static final long PERCEPTIBLE_TASK_TIMEOUT_MILLIS = 5 * 60 * 1000;
+
@VisibleForTesting
public static class Injector {
boolean isChangeEnabled(@CachedCompatChangeId int cachedCompatChangeId,
@@ -1847,7 +1849,7 @@ public class OomAdjuster {
mHasVisibleActivities = false;
}
- void onOtherActivity() {
+ void onOtherActivity(long perceptibleTaskStoppedTimeMillis) {
if (procState > PROCESS_STATE_CACHED_ACTIVITY) {
procState = PROCESS_STATE_CACHED_ACTIVITY;
mAdjType = "cch-act";
@@ -1856,6 +1858,28 @@ public class OomAdjuster {
"Raise procstate to cached activity: " + app);
}
}
+ if (Flags.perceptibleTasks() && adj > PERCEPTIBLE_MEDIUM_APP_ADJ) {
+ if (perceptibleTaskStoppedTimeMillis >= 0) {
+ final long now = mInjector.getUptimeMillis();
+ if (now - perceptibleTaskStoppedTimeMillis < PERCEPTIBLE_TASK_TIMEOUT_MILLIS) {
+ adj = PERCEPTIBLE_MEDIUM_APP_ADJ;
+ mAdjType = "perceptible-act";
+ if (procState > PROCESS_STATE_IMPORTANT_BACKGROUND) {
+ procState = PROCESS_STATE_IMPORTANT_BACKGROUND;
+ }
+
+ maybeSetProcessFollowUpUpdateLocked(app,
+ perceptibleTaskStoppedTimeMillis + PERCEPTIBLE_TASK_TIMEOUT_MILLIS,
+ now);
+ } else if (adj > PREVIOUS_APP_ADJ) {
+ adj = PREVIOUS_APP_ADJ;
+ mAdjType = "stale-perceptible-act";
+ if (procState > PROCESS_STATE_LAST_ACTIVITY) {
+ procState = PROCESS_STATE_LAST_ACTIVITY;
+ }
+ }
+ }
+ }
mHasVisibleActivities = false;
}
}
diff --git a/services/core/java/com/android/server/am/ProcessList.java b/services/core/java/com/android/server/am/ProcessList.java
index b8babe69d5a7..a61368c4bc36 100644
--- a/services/core/java/com/android/server/am/ProcessList.java
+++ b/services/core/java/com/android/server/am/ProcessList.java
@@ -67,6 +67,8 @@ import static com.android.server.wm.WindowProcessController.STOPPED_STATE_FORCE_
import android.Manifest;
import android.annotation.NonNull;
import android.annotation.Nullable;
+import android.annotation.SpecialUsers.CanBeALL;
+import android.annotation.UserIdInt;
import android.app.ActivityManager;
import android.app.ActivityManager.ProcessCapability;
import android.app.ActivityThread;
@@ -2961,8 +2963,8 @@ public final class ProcessList {
}
@GuardedBy({"mService", "mProcLock"})
- boolean killPackageProcessesLSP(String packageName, int appId, int userId, int minOomAdj,
- int reasonCode, int subReason, String reason) {
+ boolean killPackageProcessesLSP(String packageName, int appId, @CanBeALL @UserIdInt int userId,
+ int minOomAdj, int reasonCode, int subReason, String reason) {
return killPackageProcessesLSP(packageName, appId, userId, minOomAdj,
false /* callerWillRestart */, true /* allowRestart */, true /* doit */,
false /* evenPersistent */, false /* setRemoved */, false /* uninstalling */,
@@ -2970,7 +2972,8 @@ public final class ProcessList {
}
@GuardedBy("mService")
- void killAppZygotesLocked(String packageName, int appId, int userId, boolean force) {
+ void killAppZygotesLocked(String packageName, int appId, @CanBeALL @UserIdInt int userId,
+ boolean force) {
// See if there are any app zygotes running for this packageName / UID combination,
// and kill it if so.
final ArrayList<AppZygote> zygotesToKill = new ArrayList<>();
@@ -3050,9 +3053,9 @@ public final class ProcessList {
@GuardedBy({"mService", "mProcLock"})
boolean killPackageProcessesLSP(String packageName, int appId,
- int userId, int minOomAdj, boolean callerWillRestart, boolean allowRestart,
- boolean doit, boolean evenPersistent, boolean setRemoved, boolean uninstalling,
- int reasonCode, int subReason, String reason) {
+ @CanBeALL @UserIdInt int userId, int minOomAdj, boolean callerWillRestart,
+ boolean allowRestart, boolean doit, boolean evenPersistent, boolean setRemoved,
+ boolean uninstalling, int reasonCode, int subReason, String reason) {
final PackageManagerInternal pm = mService.getPackageManagerInternal();
final ArrayList<Pair<ProcessRecord, Boolean>> procs = new ArrayList<>();
@@ -5220,7 +5223,7 @@ public final class ProcessList {
}
@GuardedBy("mService")
- void sendPackageBroadcastLocked(int cmd, String[] packages, int userId) {
+ void sendPackageBroadcastLocked(int cmd, String[] packages, @CanBeALL @UserIdInt int userId) {
boolean foundProcess = false;
for (int i = mLruProcesses.size() - 1; i >= 0; i--) {
ProcessRecord r = mLruProcesses.get(i);
diff --git a/services/core/java/com/android/server/am/ProcessStateRecord.java b/services/core/java/com/android/server/am/ProcessStateRecord.java
index b0f808b39053..25175e6bee5f 100644
--- a/services/core/java/com/android/server/am/ProcessStateRecord.java
+++ b/services/core/java/com/android/server/am/ProcessStateRecord.java
@@ -1120,7 +1120,8 @@ final class ProcessStateRecord {
} else if ((flags & ACTIVITY_STATE_FLAG_IS_STOPPING) != 0) {
callback.onStoppingActivity((flags & ACTIVITY_STATE_FLAG_IS_STOPPING_FINISHING) != 0);
} else {
- callback.onOtherActivity();
+ final long ts = mApp.getWindowProcessController().getPerceptibleTaskStoppedTimeMillis();
+ callback.onOtherActivity(ts);
}
mCachedAdj = callback.adj;
diff --git a/services/core/java/com/android/server/am/UserController.java b/services/core/java/com/android/server/am/UserController.java
index e0fbaf43ea43..18f3500b2d56 100644
--- a/services/core/java/com/android/server/am/UserController.java
+++ b/services/core/java/com/android/server/am/UserController.java
@@ -4059,7 +4059,7 @@ class UserController implements Handler.Callback {
synchronized (mUserSwitchingDialogLock) {
dismissUserSwitchingDialog(null);
mUserSwitchingDialog = new UserSwitchingDialog(mService.mContext, fromUser, toUser,
- switchingFromSystemUserMessage, switchingToSystemUserMessage);
+ mHandler, switchingFromSystemUserMessage, switchingToSystemUserMessage);
mUserSwitchingDialog.show(onShown);
}
}
diff --git a/services/core/java/com/android/server/am/UserSwitchingDialog.java b/services/core/java/com/android/server/am/UserSwitchingDialog.java
index d1fcb9d1ca37..223e0b79ec0b 100644
--- a/services/core/java/com/android/server/am/UserSwitchingDialog.java
+++ b/services/core/java/com/android/server/am/UserSwitchingDialog.java
@@ -34,7 +34,6 @@ import android.graphics.drawable.Animatable2;
import android.graphics.drawable.AnimatedVectorDrawable;
import android.graphics.drawable.Drawable;
import android.os.Handler;
-import android.os.Looper;
import android.os.SystemProperties;
import android.os.Trace;
import android.os.UserHandle;
@@ -72,7 +71,7 @@ class UserSwitchingDialog extends Dialog {
// Time to wait for the onAnimationEnd() callbacks before moving on
private static final int ANIMATION_TIMEOUT_MS = 1000;
- private final Handler mHandler = new Handler(Looper.myLooper());
+ private final Handler mHandler;
protected final UserInfo mOldUser;
protected final UserInfo mNewUser;
@@ -81,13 +80,14 @@ class UserSwitchingDialog extends Dialog {
protected final Context mContext;
private final int mTraceCookie;
- UserSwitchingDialog(Context context, UserInfo oldUser, UserInfo newUser,
+ UserSwitchingDialog(Context context, UserInfo oldUser, UserInfo newUser, Handler handler,
String switchingFromSystemUserMessage, String switchingToSystemUserMessage) {
super(context, R.style.Theme_Material_NoActionBar_Fullscreen);
mContext = context;
mOldUser = oldUser;
mNewUser = newUser;
+ mHandler = handler;
mSwitchingFromSystemUserMessage = switchingFromSystemUserMessage;
mSwitchingToSystemUserMessage = switchingToSystemUserMessage;
mDisableAnimations = SystemProperties.getBoolean(
diff --git a/services/core/java/com/android/server/am/flags.aconfig b/services/core/java/com/android/server/am/flags.aconfig
index 27c384a22fb6..c8fedf3d1765 100644
--- a/services/core/java/com/android/server/am/flags.aconfig
+++ b/services/core/java/com/android/server/am/flags.aconfig
@@ -293,6 +293,13 @@ flag {
}
flag {
+ name: "perceptible_tasks"
+ namespace: "system_performance"
+ description: "Boost the oom_score_adj of activities in perceptible tasks"
+ bug: "370890207"
+}
+
+flag {
name: "expedite_activity_launch_on_cold_start"
namespace: "system_performance"
description: "Notify ActivityTaskManager of cold starts early to fix app launch behavior."
diff --git a/services/core/java/com/android/server/audio/AudioService.java b/services/core/java/com/android/server/audio/AudioService.java
index d800503c658e..b85967fa2bac 100644
--- a/services/core/java/com/android/server/audio/AudioService.java
+++ b/services/core/java/com/android/server/audio/AudioService.java
@@ -954,7 +954,8 @@ public class AudioService extends IAudioService.Stub
/**
* Stores information about a device using absolute volume behavior.
*/
- private static final class AbsoluteVolumeDeviceInfo {
+ private static final class AbsoluteVolumeDeviceInfo implements IBinder.DeathRecipient {
+ private final AudioService mParent;
private final AudioDeviceAttributes mDevice;
private final List<VolumeInfo> mVolumeInfos;
private final IAudioDeviceVolumeDispatcher mCallback;
@@ -962,16 +963,26 @@ public class AudioService extends IAudioService.Stub
private @AudioManager.AbsoluteDeviceVolumeBehavior int mDeviceVolumeBehavior;
private AbsoluteVolumeDeviceInfo(
+ AudioService parent,
AudioDeviceAttributes device,
List<VolumeInfo> volumeInfos,
IAudioDeviceVolumeDispatcher callback,
boolean handlesVolumeAdjustment,
@AudioManager.AbsoluteDeviceVolumeBehavior int behavior) {
+ this.mParent = parent;
this.mDevice = device;
this.mVolumeInfos = volumeInfos;
this.mCallback = callback;
this.mHandlesVolumeAdjustment = handlesVolumeAdjustment;
this.mDeviceVolumeBehavior = behavior;
+
+ try {
+ this.mCallback.asBinder().linkToDeath(this, 0);
+ } catch (RemoteException | NullPointerException e) {
+ // NPE can be raised when mocking the callback object
+ Slog.w(TAG, "Exception: " + e
+ + "\nCannot listen to callback binder death for device " + mDevice);
+ }
}
/**
@@ -991,6 +1002,25 @@ public class AudioService extends IAudioService.Stub
}
return null;
}
+
+ @Override
+ public void binderDied() {
+ if (mParent.removeAudioSystemDeviceOutFromAbsVolumeDevices(mDevice.getInternalType())
+ != null) {
+ mParent.dispatchDeviceVolumeBehavior(mDevice,
+ AudioManager.DEVICE_VOLUME_BEHAVIOR_VARIABLE);
+ }
+ }
+
+ public void unlinkToDeath() {
+ try {
+ mCallback.asBinder().unlinkToDeath(this, 0);
+ } catch (NullPointerException e) {
+ // NPE can be raised when mocking the callback object
+ Slog.w(TAG, "Exception: " + e
+ + "\nCannot unlink to death, null binder object for device " + mDevice);
+ }
+ }
}
// Devices for the which use the "absolute volume" concept (framework sends audio signal
@@ -8142,16 +8172,16 @@ public class AudioService extends IAudioService.Stub
int deviceOut = device.getInternalType();
if (register) {
- AbsoluteVolumeDeviceInfo info = new AbsoluteVolumeDeviceInfo(
+ AbsoluteVolumeDeviceInfo info = new AbsoluteVolumeDeviceInfo(this,
device, volumes, cb, handlesVolumeAdjustment, deviceVolumeBehavior);
final AbsoluteVolumeDeviceInfo oldInfo = getAbsoluteVolumeDeviceInfo(deviceOut);
+ addAudioSystemDeviceOutToAbsVolumeDevices(deviceOut, info);
boolean volumeBehaviorChanged = (oldInfo == null)
|| (oldInfo.mDeviceVolumeBehavior != deviceVolumeBehavior);
if (volumeBehaviorChanged) {
removeAudioSystemDeviceOutFromFullVolumeDevices(deviceOut);
removeAudioSystemDeviceOutFromFixedVolumeDevices(deviceOut);
- addAudioSystemDeviceOutToAbsVolumeDevices(deviceOut, info);
dispatchDeviceVolumeBehavior(device, deviceVolumeBehavior);
}
@@ -8177,8 +8207,10 @@ public class AudioService extends IAudioService.Stub
}
}
} else {
- boolean wasAbsVol = removeAudioSystemDeviceOutFromAbsVolumeDevices(deviceOut) != null;
- if (wasAbsVol) {
+ AbsoluteVolumeDeviceInfo deviceInfo = removeAudioSystemDeviceOutFromAbsVolumeDevices(
+ deviceOut);
+ if (deviceInfo != null) {
+ deviceInfo.unlinkToDeath();
dispatchDeviceVolumeBehavior(device, AudioManager.DEVICE_VOLUME_BEHAVIOR_VARIABLE);
}
}
diff --git a/services/core/java/com/android/server/biometrics/BiometricService.java b/services/core/java/com/android/server/biometrics/BiometricService.java
index a5058dd51a33..cf5fa9699ca9 100644
--- a/services/core/java/com/android/server/biometrics/BiometricService.java
+++ b/services/core/java/com/android/server/biometrics/BiometricService.java
@@ -540,9 +540,23 @@ public class BiometricService extends SystemService {
DEFAULT_MANDATORY_BIOMETRICS_STATUS)
&& mMandatoryBiometricsRequirementsSatisfied.getOrDefault(userId,
DEFAULT_MANDATORY_BIOMETRICS_REQUIREMENTS_SATISFIED_STATUS)
- && getEnabledForApps(userId, TYPE_ANY_BIOMETRIC)
- && (mFingerprintEnrolledForUser.getOrDefault(userId, false /* default */)
- || mFaceEnrolledForUser.getOrDefault(userId, false /* default */));
+ && getBiometricStatusForIdentityCheck(userId);
+ }
+
+ private boolean getBiometricStatusForIdentityCheck(int userId) {
+ if (com.android.settings.flags.Flags.biometricsOnboardingEducation()) {
+ if (mFingerprintEnrolledForUser.getOrDefault(userId, false /* default */)
+ && getEnabledForApps(userId, TYPE_FINGERPRINT)) {
+ return true;
+ } else {
+ return mFaceEnrolledForUser.getOrDefault(userId, false /* default */)
+ && getEnabledForApps(userId, TYPE_FACE);
+ }
+ } else {
+ return (mFingerprintEnrolledForUser.getOrDefault(userId, false /* default */)
+ || mFaceEnrolledForUser.getOrDefault(userId, false /* default */))
+ && getEnabledForApps(userId, TYPE_ANY_BIOMETRIC);
+ }
}
void notifyEnabledOnKeyguardCallbacks(int userId, int modality) {
diff --git a/services/core/java/com/android/server/content/ContentService.java b/services/core/java/com/android/server/content/ContentService.java
index c393e921d957..79af6ed9d60b 100644
--- a/services/core/java/com/android/server/content/ContentService.java
+++ b/services/core/java/com/android/server/content/ContentService.java
@@ -24,6 +24,8 @@ import android.accounts.AccountManagerInternal;
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.annotation.RequiresPermission;
+import android.annotation.SpecialUsers.CanBeALL;
+import android.annotation.SpecialUsers.CanBeCURRENT;
import android.annotation.UserIdInt;
import android.app.ActivityManager;
import android.app.ActivityManager.RestrictionLevel;
@@ -361,7 +363,8 @@ public final class ContentService extends IContentService.Stub {
*/
@Override
public void registerContentObserver(Uri uri, boolean notifyForDescendants,
- IContentObserver observer, int userHandle, int targetSdkVersion) {
+ IContentObserver observer, @CanBeALL @CanBeCURRENT @UserIdInt int userHandle,
+ int targetSdkVersion) {
if (observer == null || uri == null) {
throw new IllegalArgumentException("You must pass a valid uri and observer");
}
@@ -1398,8 +1401,8 @@ public final class ContentService extends IContentService.Stub {
}
}
- private int handleIncomingUser(Uri uri, int pid, int uid, int modeFlags, boolean allowNonFull,
- int userId) {
+ private @CanBeALL @UserIdInt int handleIncomingUser(Uri uri, int pid, int uid, int modeFlags,
+ boolean allowNonFull, @CanBeALL @CanBeCURRENT @UserIdInt int userId) {
if (userId == UserHandle.USER_CURRENT) {
userId = ActivityManager.getCurrentUser();
}
diff --git a/services/core/java/com/android/server/display/LogicalDisplayMapper.java b/services/core/java/com/android/server/display/LogicalDisplayMapper.java
index 872f33484951..f4daf8761e9b 100644
--- a/services/core/java/com/android/server/display/LogicalDisplayMapper.java
+++ b/services/core/java/com/android/server/display/LogicalDisplayMapper.java
@@ -890,10 +890,12 @@ class LogicalDisplayMapper implements DisplayDeviceRepository.Listener {
// still need to let WindowManager know so it can update its own internal state for
// things like display cutouts.
display.getNonOverrideDisplayInfoLocked(mTempDisplayInfo);
- if (!mTempNonOverrideDisplayInfo.equals(mTempDisplayInfo)) {
- logicalDisplayEventMask |= LOGICAL_DISPLAY_EVENT_BASIC_CHANGED
- | LOGICAL_DISPLAY_EVENT_REFRESH_RATE_CHANGED;
+ if (!mTempNonOverrideDisplayInfo.equals(mTempDisplayInfo,
+ /* compareOnlyBasicChanges */ true)) {
+ logicalDisplayEventMask |= LOGICAL_DISPLAY_EVENT_BASIC_CHANGED;
}
+ logicalDisplayEventMask
+ |= updateAndGetMaskForDisplayPropertyChanges(mTempNonOverrideDisplayInfo);
}
mLogicalDisplaysToUpdate.put(displayId, logicalDisplayEventMask);
mUpdatedLogicalDisplays.put(displayId, UPDATE_STATE_UPDATED);
diff --git a/services/core/java/com/android/server/display/feature/DisplayManagerFlags.java b/services/core/java/com/android/server/display/feature/DisplayManagerFlags.java
index c3057ded66eb..7cc178d5ff6c 100644
--- a/services/core/java/com/android/server/display/feature/DisplayManagerFlags.java
+++ b/services/core/java/com/android/server/display/feature/DisplayManagerFlags.java
@@ -280,6 +280,11 @@ public class DisplayManagerFlags {
Flags::committedStateSeparateEvent
);
+ private final FlagState mDelayImplicitRrRegistrationUntilRrAccessed = new FlagState(
+ Flags.FLAG_DELAY_IMPLICIT_RR_REGISTRATION_UNTIL_RR_ACCESSED,
+ Flags::delayImplicitRrRegistrationUntilRrAccessed
+ );
+
/**
* @return {@code true} if 'port' is allowed in display layout configuration file.
*/
@@ -586,7 +591,6 @@ public class DisplayManagerFlags {
return mFramerateOverrideTriggersRrCallbacks.isEnabled();
}
-
/**
* @return {@code true} if the flag for sending refresh rate events only for the apps in
* foreground is enabled
@@ -604,6 +608,13 @@ public class DisplayManagerFlags {
}
/**
+ * @return {@code true} if the flag for only explicit subscription for RR changes is enabled
+ */
+ public boolean isDelayImplicitRrRegistrationUntilRrAccessedEnabled() {
+ return mDelayImplicitRrRegistrationUntilRrAccessed.isEnabled();
+ }
+
+ /**
* dumps all flagstates
* @param pw printWriter
*/
@@ -660,6 +671,7 @@ public class DisplayManagerFlags {
pw.println(" " + mFramerateOverrideTriggersRrCallbacks);
pw.println(" " + mRefreshRateEventForForegroundApps);
pw.println(" " + mCommittedStateSeparateEvent);
+ pw.println(" " + mDelayImplicitRrRegistrationUntilRrAccessed);
}
private static class FlagState {
diff --git a/services/core/java/com/android/server/display/feature/display_flags.aconfig b/services/core/java/com/android/server/display/feature/display_flags.aconfig
index f5451307afb7..a0064a9f5d1d 100644
--- a/services/core/java/com/android/server/display/feature/display_flags.aconfig
+++ b/services/core/java/com/android/server/display/feature/display_flags.aconfig
@@ -474,9 +474,9 @@ flag {
description: "Feature flag to trigger the RR callbacks when framerate overridding happens."
bug: "390113266"
is_fixed_read_only: true
- metadata {
- purpose: PURPOSE_BUGFIX
- }
+ metadata {
+ purpose: PURPOSE_BUGFIX
+ }
}
flag {
@@ -508,3 +508,14 @@ flag {
purpose: PURPOSE_BUGFIX
}
}
+
+flag {
+ name: "delay_implicit_rr_registration_until_rr_accessed"
+ namespace: "display_manager"
+ description: "Feature flag for clients to subscribe to RR changes by either explicitly subscribing for refresh rate changes or request for refresh rate data"
+ bug: "391828526"
+ is_fixed_read_only: true
+ metadata {
+ purpose: PURPOSE_BUGFIX
+ }
+}
diff --git a/services/core/java/com/android/server/location/contexthub/ContextHubEndpointBroker.java b/services/core/java/com/android/server/location/contexthub/ContextHubEndpointBroker.java
index 1a29150cd40c..940bcb4c6ba1 100644
--- a/services/core/java/com/android/server/location/contexthub/ContextHubEndpointBroker.java
+++ b/services/core/java/com/android/server/location/contexthub/ContextHubEndpointBroker.java
@@ -16,6 +16,7 @@
package com.android.server.location.contexthub;
+import android.annotation.NonNull;
import android.app.AppOpsManager;
import android.content.Context;
import android.hardware.contexthub.EndpointInfo;
@@ -64,7 +65,7 @@ public class ContextHubEndpointBroker extends IContextHubEndpoint.Stub
* Internal interface used to invoke client callbacks.
*/
interface CallbackConsumer {
- void accept(IContextHubEndpointCallback callback) throws RemoteException;
+ void accept(@NonNull IContextHubEndpointCallback callback) throws RemoteException;
}
/** The context of the service. */
@@ -86,7 +87,7 @@ public class ContextHubEndpointBroker extends IContextHubEndpoint.Stub
private final EndpointInfo mHalEndpointInfo;
/** The remote callback interface for this endpoint. */
- private final IContextHubEndpointCallback mContextHubEndpointCallback;
+ @NonNull private final IContextHubEndpointCallback mContextHubEndpointCallback;
/** True if this endpoint is registered with the service/HAL. */
@GuardedBy("mRegistrationLock")
@@ -158,7 +159,7 @@ public class ContextHubEndpointBroker extends IContextHubEndpoint.Stub
IEndpointCommunication hubInterface,
ContextHubEndpointManager endpointManager,
EndpointInfo halEndpointInfo,
- IContextHubEndpointCallback callback,
+ @NonNull IContextHubEndpointCallback callback,
String packageName,
String attributionTag,
ContextHubTransactionManager transactionManager) {
@@ -419,9 +420,7 @@ public class ContextHubEndpointBroker extends IContextHubEndpoint.Stub
}
/* package */ void attachDeathRecipient() throws RemoteException {
- if (mContextHubEndpointCallback != null) {
- mContextHubEndpointCallback.asBinder().linkToDeath(this, 0 /* flags */);
- }
+ mContextHubEndpointCallback.asBinder().linkToDeath(this, 0 /* flags */);
}
/* package */ void onEndpointSessionOpenRequest(
@@ -664,15 +663,13 @@ public class ContextHubEndpointBroker extends IContextHubEndpoint.Stub
* @return false if the callback threw a RemoteException
*/
private boolean invokeCallback(CallbackConsumer consumer) {
- if (mContextHubEndpointCallback != null) {
- acquireWakeLock();
- try {
- consumer.accept(mContextHubEndpointCallback);
- } catch (RemoteException e) {
- Log.e(TAG, "RemoteException while calling endpoint callback", e);
- releaseWakeLock();
- return false;
- }
+ acquireWakeLock();
+ try {
+ consumer.accept(mContextHubEndpointCallback);
+ } catch (RemoteException e) {
+ Log.e(TAG, "RemoteException while calling endpoint callback", e);
+ releaseWakeLock();
+ return false;
}
return true;
}
diff --git a/services/core/java/com/android/server/location/contexthub/ContextHubEndpointManager.java b/services/core/java/com/android/server/location/contexthub/ContextHubEndpointManager.java
index 30bb8f3fa188..8ab581e1fb7a 100644
--- a/services/core/java/com/android/server/location/contexthub/ContextHubEndpointManager.java
+++ b/services/core/java/com/android/server/location/contexthub/ContextHubEndpointManager.java
@@ -17,6 +17,7 @@
package com.android.server.location.contexthub;
import android.annotation.IntDef;
+import android.annotation.NonNull;
import android.content.Context;
import android.hardware.contexthub.ContextHubInfo;
import android.hardware.contexthub.EndpointInfo;
@@ -240,7 +241,7 @@ import java.util.function.Consumer;
*/
/* package */ IContextHubEndpoint registerEndpoint(
HubEndpointInfo pendingEndpointInfo,
- IContextHubEndpointCallback callback,
+ @NonNull IContextHubEndpointCallback callback,
String packageName,
String attributionTag)
throws RemoteException {
diff --git a/services/core/java/com/android/server/location/contexthub/ContextHubService.java b/services/core/java/com/android/server/location/contexthub/ContextHubService.java
index bf7351cb11db..2c0c55bd8df4 100644
--- a/services/core/java/com/android/server/location/contexthub/ContextHubService.java
+++ b/services/core/java/com/android/server/location/contexthub/ContextHubService.java
@@ -792,6 +792,10 @@ public class ContextHubService extends IContextHubService.Stub {
Log.e(TAG, "Endpoint manager failed to initialize");
throw new UnsupportedOperationException("Endpoint registration is not supported");
}
+ if (callback == null) {
+ Log.e(TAG, "Endpoint callback is invalid");
+ throw new IllegalArgumentException("registerEndpoint must have a non-null callback");
+ }
return mEndpointManager.registerEndpoint(
pendingHubEndpointInfo, callback, packageName, attributionTag);
}
diff --git a/services/core/java/com/android/server/location/injector/SystemEmergencyHelper.java b/services/core/java/com/android/server/location/injector/SystemEmergencyHelper.java
index 177eefb2ef2a..3f75b11befc2 100644
--- a/services/core/java/com/android/server/location/injector/SystemEmergencyHelper.java
+++ b/services/core/java/com/android/server/location/injector/SystemEmergencyHelper.java
@@ -29,7 +29,6 @@ import android.telephony.TelephonyManager;
import android.util.Log;
import com.android.internal.telephony.TelephonyIntents;
-import com.android.internal.telephony.flags.Flags;
import com.android.server.FgThread;
import java.util.Objects;
@@ -107,26 +106,19 @@ public class SystemEmergencyHelper extends EmergencyHelper {
boolean isInExtensionTime = mEmergencyCallEndRealtimeMs != Long.MIN_VALUE
&& (SystemClock.elapsedRealtime() - mEmergencyCallEndRealtimeMs) < extensionTimeMs;
- if (!Flags.enforceTelephonyFeatureMapping()) {
- return mIsInEmergencyCall
- || isInExtensionTime
- || mTelephonyManager.getEmergencyCallbackMode()
- || mTelephonyManager.isInEmergencySmsMode();
- } else {
- boolean emergencyCallbackMode = false;
- boolean emergencySmsMode = false;
- PackageManager pm = mContext.getPackageManager();
- if (pm.hasSystemFeature(PackageManager.FEATURE_TELEPHONY_CALLING)) {
- emergencyCallbackMode = mTelephonyManager.getEmergencyCallbackMode();
- }
- if (pm.hasSystemFeature(PackageManager.FEATURE_TELEPHONY_MESSAGING)) {
- emergencySmsMode = mTelephonyManager.isInEmergencySmsMode();
- }
- return mIsInEmergencyCall
- || isInExtensionTime
- || emergencyCallbackMode
- || emergencySmsMode;
+ boolean emergencyCallbackMode = false;
+ boolean emergencySmsMode = false;
+ PackageManager pm = mContext.getPackageManager();
+ if (pm.hasSystemFeature(PackageManager.FEATURE_TELEPHONY_CALLING)) {
+ emergencyCallbackMode = mTelephonyManager.getEmergencyCallbackMode();
+ }
+ if (pm.hasSystemFeature(PackageManager.FEATURE_TELEPHONY_MESSAGING)) {
+ emergencySmsMode = mTelephonyManager.isInEmergencySmsMode();
}
+ return mIsInEmergencyCall
+ || isInExtensionTime
+ || emergencyCallbackMode
+ || emergencySmsMode;
}
private class EmergencyCallTelephonyCallback extends TelephonyCallback implements
diff --git a/services/core/java/com/android/server/notification/PreferencesHelper.java b/services/core/java/com/android/server/notification/PreferencesHelper.java
index 0fc182f3f1bb..fff812c038e7 100644
--- a/services/core/java/com/android/server/notification/PreferencesHelper.java
+++ b/services/core/java/com/android/server/notification/PreferencesHelper.java
@@ -42,6 +42,7 @@ import static com.android.internal.util.FrameworkStatsLog.PACKAGE_NOTIFICATION_P
import static com.android.internal.util.FrameworkStatsLog.PACKAGE_NOTIFICATION_PREFERENCES__FSI_STATE__DENIED;
import static com.android.internal.util.FrameworkStatsLog.PACKAGE_NOTIFICATION_PREFERENCES__FSI_STATE__GRANTED;
import static com.android.internal.util.FrameworkStatsLog.PACKAGE_NOTIFICATION_PREFERENCES__FSI_STATE__NOT_REQUESTED;
+import static com.android.server.notification.PreferencesHelper.LockableAppFields.USER_LOCKED_BUBBLE;
import static com.android.server.notification.PreferencesHelper.LockableAppFields.USER_LOCKED_PROMOTABLE;
import android.annotation.FlaggedApi;
@@ -286,7 +287,7 @@ public class PreferencesHelper implements RankingConfig {
if (!TAG_RANKING.equals(tag)) return;
final int xmlVersion = parser.getAttributeInt(null, ATT_VERSION, -1);
- boolean upgradeForBubbles = xmlVersion == XML_VERSION_BUBBLES_UPGRADE;
+ boolean upgradeForBubbles = xmlVersion >= XML_VERSION_BUBBLES_UPGRADE;
boolean migrateToPermission = (xmlVersion < XML_VERSION_NOTIF_PERMISSION);
if (mShowReviewPermissionsNotification
&& (xmlVersion < XML_VERSION_REVIEW_PERMISSIONS_NOTIFICATION)) {
@@ -337,15 +338,19 @@ public class PreferencesHelper implements RankingConfig {
}
boolean skipWarningLogged = false;
boolean skipGroupWarningLogged = false;
- boolean hasSAWPermission = false;
- if (upgradeForBubbles && uid != UNKNOWN_UID) {
- hasSAWPermission = mAppOps.noteOpNoThrow(
- OP_SYSTEM_ALERT_WINDOW, uid, name, null,
- "check-notif-bubble") == AppOpsManager.MODE_ALLOWED;
- }
- int bubblePref = hasSAWPermission
- ? BUBBLE_PREFERENCE_ALL
- : parser.getAttributeInt(null, ATT_ALLOW_BUBBLE, DEFAULT_BUBBLE_PREFERENCE);
+ int bubblePref = parser.getAttributeInt(null, ATT_ALLOW_BUBBLE,
+ DEFAULT_BUBBLE_PREFERENCE);
+ boolean bubbleLocked = (parser.getAttributeInt(null,
+ ATT_APP_USER_LOCKED_FIELDS, DEFAULT_LOCKED_APP_FIELDS) & USER_LOCKED_BUBBLE)
+ != 0;
+ if (!bubbleLocked
+ && upgradeForBubbles
+ && uid != UNKNOWN_UID
+ && mAppOps.noteOpNoThrow(OP_SYSTEM_ALERT_WINDOW, uid, name, null,
+ "check-notif-bubble") == AppOpsManager.MODE_ALLOWED) {
+ // User hasn't changed bubble pref & the app has SAW, so allow all bubbles.
+ bubblePref = BUBBLE_PREFERENCE_ALL;
+ }
int appImportance = parser.getAttributeInt(null, ATT_IMPORTANCE, DEFAULT_IMPORTANCE);
// when data is loaded from disk it's loaded as USER_ALL, but restored data that
diff --git a/services/core/java/com/android/server/pm/AppDataHelper.java b/services/core/java/com/android/server/pm/AppDataHelper.java
index 29f8243cfe60..ae415196f15e 100644
--- a/services/core/java/com/android/server/pm/AppDataHelper.java
+++ b/services/core/java/com/android/server/pm/AppDataHelper.java
@@ -24,6 +24,7 @@ import static com.android.server.pm.PackageManagerServiceUtils.logCriticalInfo;
import android.annotation.NonNull;
import android.annotation.Nullable;
+import android.annotation.SpecialUsers.CanBeALL;
import android.annotation.UserIdInt;
import android.content.pm.PackageManager;
import android.os.CreateAppDataArgs;
@@ -548,7 +549,7 @@ public class AppDataHelper {
return prepareAppDataFuture;
}
- void clearAppDataLIF(AndroidPackage pkg, int userId, int flags) {
+ void clearAppDataLIF(AndroidPackage pkg, @CanBeALL @UserIdInt int userId, int flags) {
if (pkg == null) {
return;
}
@@ -559,7 +560,8 @@ public class AppDataHelper {
}
}
- void clearAppDataLeafLIF(String packageName, String volumeUuid, int userId, int flags) {
+ void clearAppDataLeafLIF(String packageName, String volumeUuid, @CanBeALL @UserIdInt int userId,
+ int flags) {
final Computer snapshot = mPm.snapshotComputer();
final PackageStateInternal packageStateInternal =
snapshot.getPackageStateInternal(packageName);
diff --git a/services/core/java/com/android/server/pm/BackgroundInstallControlService.java b/services/core/java/com/android/server/pm/BackgroundInstallControlService.java
index c3af578de369..463989adc98f 100644
--- a/services/core/java/com/android/server/pm/BackgroundInstallControlService.java
+++ b/services/core/java/com/android/server/pm/BackgroundInstallControlService.java
@@ -332,7 +332,8 @@ public class BackgroundInstallControlService extends SystemService {
userId)
!= PERMISSION_GRANTED) {
if(Build.IS_DEBUGGABLE) {
- Slog.d(TAG, "handlePackageAdd " + packageName + ": installer doesn't "
+ Slog.d(TAG, "handlePackageAdd " + packageName + ": installer ("
+ + installerPackageName + ") doesn't "
+ "have INSTALL_PACKAGES permission, skipping");
}
return;
diff --git a/services/core/java/com/android/server/pm/DeletePackageHelper.java b/services/core/java/com/android/server/pm/DeletePackageHelper.java
index 90adb6683496..6bec34ef7063 100644
--- a/services/core/java/com/android/server/pm/DeletePackageHelper.java
+++ b/services/core/java/com/android/server/pm/DeletePackageHelper.java
@@ -35,6 +35,8 @@ import static com.android.server.pm.PackageManagerService.TAG;
import android.Manifest;
import android.annotation.NonNull;
import android.annotation.Nullable;
+import android.annotation.SpecialUsers.CanBeALL;
+import android.annotation.UserIdInt;
import android.app.ApplicationExitInfo;
import android.app.ApplicationPackageManager;
import android.content.Intent;
@@ -562,7 +564,7 @@ final class DeletePackageHelper {
}
@GuardedBy("mPm.mInstallLock")
- private void deleteInstalledPackageLIF(PackageSetting ps, int userId,
+ private void deleteInstalledPackageLIF(PackageSetting ps, @CanBeALL @UserIdInt int userId,
boolean deleteCodeAndResources, int flags, @NonNull int[] allUserHandles,
@NonNull PackageRemovedInfo outInfo, boolean writeSettings) {
synchronized (mPm.mLock) {
diff --git a/services/core/java/com/android/server/pm/PackageFreezer.java b/services/core/java/com/android/server/pm/PackageFreezer.java
index 11f2059c4267..d66eb814ef66 100644
--- a/services/core/java/com/android/server/pm/PackageFreezer.java
+++ b/services/core/java/com/android/server/pm/PackageFreezer.java
@@ -18,6 +18,8 @@ package com.android.server.pm;
import android.annotation.NonNull;
import android.annotation.Nullable;
+import android.annotation.SpecialUsers.CanBeALL;
+import android.annotation.UserIdInt;
import android.content.pm.Flags;
import android.content.pm.PackageManager;
@@ -60,12 +62,12 @@ final class PackageFreezer implements AutoCloseable {
}
}
- PackageFreezer(String packageName, int userId, String killReason,
+ PackageFreezer(String packageName, @CanBeALL @UserIdInt int userId, String killReason,
PackageManagerService pm, int exitInfoReason, @Nullable InstallRequest request) {
this(packageName, userId, killReason, pm, exitInfoReason, request, false);
}
- PackageFreezer(String packageName, int userId, String killReason,
+ PackageFreezer(String packageName, @CanBeALL @UserIdInt int userId, String killReason,
PackageManagerService pm, int exitInfoReason, @Nullable InstallRequest request,
boolean waitAppKilled) {
mPm = pm;
diff --git a/services/core/java/com/android/server/pm/PackageManagerService.java b/services/core/java/com/android/server/pm/PackageManagerService.java
index 2464a291b4dd..91a1c9c12cb8 100644
--- a/services/core/java/com/android/server/pm/PackageManagerService.java
+++ b/services/core/java/com/android/server/pm/PackageManagerService.java
@@ -50,6 +50,7 @@ import android.annotation.AppIdInt;
import android.annotation.IntDef;
import android.annotation.NonNull;
import android.annotation.Nullable;
+import android.annotation.SpecialUsers.CanBeALL;
import android.annotation.StringRes;
import android.annotation.UserIdInt;
import android.annotation.WorkerThread;
@@ -1590,7 +1591,7 @@ public class PackageManagerService implements PackageSender, TestUtilityService
scheduleWritePackageRestrictions(userId);
}
- void scheduleWritePackageRestrictions(int userId) {
+ void scheduleWritePackageRestrictions(@CanBeALL @UserIdInt int userId) {
invalidatePackageInfoCache();
if (userId == UserHandle.USER_ALL) {
synchronized (mDirtyUsers) {
@@ -3074,7 +3075,7 @@ public class PackageManagerService implements PackageSender, TestUtilityService
}
@NonNull
- int[] resolveUserIds(int userId) {
+ int[] resolveUserIds(@CanBeALL @UserIdInt int userId) {
return (userId == UserHandle.USER_ALL) ? mUserManager.getUserIds() : new int[] { userId };
}
@@ -3112,7 +3113,7 @@ public class PackageManagerService implements PackageSender, TestUtilityService
}
void killApplication(String pkgName, @AppIdInt int appId,
- @UserIdInt int userId, String reason, int exitInfoReason) {
+ @CanBeALL @UserIdInt int userId, String reason, int exitInfoReason) {
// Request the ActivityManager to kill the process(only for existing packages)
// so that we do not end up in a confused state while the user is still using the older
// version of the application while the new one gets installed.
@@ -3131,7 +3132,7 @@ public class PackageManagerService implements PackageSender, TestUtilityService
}
void killApplicationSync(String pkgName, @AppIdInt int appId,
- @UserIdInt int userId, String reason, int exitInfoReason) {
+ @CanBeALL @UserIdInt int userId, String reason, int exitInfoReason) {
ActivityManagerInternal mAmi = LocalServices.getService(ActivityManagerInternal.class);
if (Thread.holdsLock(mLock) || mAmi == null) {
// holds PM's lock, go back killApplication to avoid it run into watchdog reset.
@@ -3385,7 +3386,7 @@ public class PackageManagerService implements PackageSender, TestUtilityService
}
// TODO(b/261957226): centralise this logic in DPM
- boolean isPackageDeviceAdmin(String packageName, int userId) {
+ boolean isPackageDeviceAdmin(String packageName, @CanBeALL @UserIdInt int userId) {
final IDevicePolicyManager dpm = getDevicePolicyManager();
final DevicePolicyManagerInternal dpmi =
mInjector.getLocalService(DevicePolicyManagerInternal.class);
@@ -3555,7 +3556,7 @@ public class PackageManagerService implements PackageSender, TestUtilityService
/** This method takes a specific user id as well as UserHandle.USER_ALL. */
@GuardedBy("mLock")
void clearPackagePreferredActivitiesLPw(String packageName,
- @NonNull SparseBooleanArray outUserChanged, int userId) {
+ @NonNull SparseBooleanArray outUserChanged, @CanBeALL @UserIdInt int userId) {
mSettings.clearPackagePreferredActivities(packageName, outUserChanged, userId);
}
@@ -4388,14 +4389,14 @@ public class PackageManagerService implements PackageSender, TestUtilityService
}
}
- public PackageFreezer freezePackage(String packageName, int userId, String killReason,
- int exitInfoReason, InstallRequest request) {
+ public PackageFreezer freezePackage(String packageName, @CanBeALL @UserIdInt int userId,
+ String killReason, int exitInfoReason, InstallRequest request) {
return freezePackage(packageName, userId, killReason, exitInfoReason, request,
/* waitAppKilled= */ false);
}
- private PackageFreezer freezePackage(String packageName, int userId, String killReason,
- int exitInfoReason, InstallRequest request, boolean waitAppKilled) {
+ private PackageFreezer freezePackage(String packageName, @CanBeALL @UserIdInt int userId,
+ String killReason, int exitInfoReason, InstallRequest request, boolean waitAppKilled) {
return new PackageFreezer(packageName, userId, killReason, this, exitInfoReason, request,
waitAppKilled);
}
diff --git a/services/core/java/com/android/server/pm/PreferredActivityHelper.java b/services/core/java/com/android/server/pm/PreferredActivityHelper.java
index 41d2aeb9b168..fa56596bf62d 100644
--- a/services/core/java/com/android/server/pm/PreferredActivityHelper.java
+++ b/services/core/java/com/android/server/pm/PreferredActivityHelper.java
@@ -25,6 +25,7 @@ import static com.android.server.pm.PackageManagerService.DEBUG_PREFERRED;
import static com.android.server.pm.PackageManagerService.TAG;
import android.annotation.NonNull;
+import android.annotation.SpecialUsers.CanBeALL;
import android.annotation.UserIdInt;
import android.content.ComponentName;
import android.content.Intent;
@@ -115,7 +116,8 @@ final class PreferredActivityHelper {
}
/** This method takes a specific user id as well as UserHandle.USER_ALL. */
- public void clearPackagePreferredActivities(String packageName, int userId) {
+ public void clearPackagePreferredActivities(String packageName,
+ @CanBeALL @UserIdInt int userId) {
final SparseBooleanArray changedUsers = new SparseBooleanArray();
synchronized (mPm.mLock) {
mPm.clearPackagePreferredActivitiesLPw(packageName, changedUsers, userId);
diff --git a/services/core/java/com/android/server/pm/RemovePackageHelper.java b/services/core/java/com/android/server/pm/RemovePackageHelper.java
index f01a74e8d60d..22b4ec7b51b6 100644
--- a/services/core/java/com/android/server/pm/RemovePackageHelper.java
+++ b/services/core/java/com/android/server/pm/RemovePackageHelper.java
@@ -30,6 +30,8 @@ import static com.android.server.pm.PackageManagerService.TAG;
import android.annotation.NonNull;
import android.annotation.Nullable;
+import android.annotation.SpecialUsers.CanBeALL;
+import android.annotation.UserIdInt;
import android.content.pm.PackageManager;
import android.content.pm.parsing.ApkLiteParseUtils;
import android.content.pm.parsing.PackageLite;
@@ -256,7 +258,8 @@ final class RemovePackageHelper {
* Make sure this flag is set for partially installed apps. If not it's meaningless to
* delete a partially installed application.
*/
- public void clearPackageStateForUserLIF(PackageSetting ps, int userId, int flags) {
+ public void clearPackageStateForUserLIF(PackageSetting ps, @CanBeALL @UserIdInt int userId,
+ int flags) {
final String packageName = ps.getPackageName();
// Step 1: always destroy app profiles except when explicitly preserved
if ((flags & Installer.FLAG_CLEAR_APP_DATA_KEEP_ART_PROFILES) == 0) {
@@ -370,13 +373,13 @@ final class RemovePackageHelper {
* This method deletes the package from internal data structures such as mPackages / mSettings.
*
* @param targetUserId indicates the target user of the deletion. It equals to
- * {@link UserHandle.USER_ALL} if the deletion was initiated for all users,
+ * {@link UserHandle#USER_ALL} if the deletion was initiated for all users,
* otherwise it equals to the specific user id that the deletion was meant
* for.
*/
@GuardedBy("mPm.mInstallLock")
- public void removePackageDataLIF(final PackageSetting deletedPs, int targetUserId,
- @NonNull int[] allUserHandles,
+ public void removePackageDataLIF(final PackageSetting deletedPs,
+ @CanBeALL @UserIdInt int targetUserId, @NonNull int[] allUserHandles,
@NonNull PackageRemovedInfo outInfo, int flags, boolean writeSettings) {
String packageName = deletedPs.getPackageName();
if (DEBUG_REMOVE) Slog.d(TAG, "removePackageDataLI: " + deletedPs);
@@ -482,7 +485,8 @@ final class RemovePackageHelper {
}
}
- private static boolean shouldDeletePackageSetting(PackageSetting deletedPs, int userId,
+ private static boolean shouldDeletePackageSetting(PackageSetting deletedPs,
+ @CanBeALL @UserIdInt int userId,
int[] allUserHandles, int flags) {
if ((flags & PackageManager.DELETE_KEEP_DATA) != 0) {
return false;
diff --git a/services/core/java/com/android/server/pm/Settings.java b/services/core/java/com/android/server/pm/Settings.java
index 485a28070bc5..92257f1ee2dd 100644
--- a/services/core/java/com/android/server/pm/Settings.java
+++ b/services/core/java/com/android/server/pm/Settings.java
@@ -35,6 +35,7 @@ import static com.android.server.pm.SharedUidMigration.BEST_EFFORT;
import android.annotation.NonNull;
import android.annotation.Nullable;
+import android.annotation.SpecialUsers.CanBeALL;
import android.annotation.UserIdInt;
import android.app.compat.ChangeIdStateCache;
import android.content.ComponentName;
@@ -6639,7 +6640,7 @@ public final class Settings implements Watchable, Snappable, ResilientAtomicFile
/** This method takes a specific user id as well as UserHandle.USER_ALL. */
void clearPackagePreferredActivities(String packageName,
- @NonNull SparseBooleanArray outUserChanged, int userId) {
+ @NonNull SparseBooleanArray outUserChanged, @CanBeALL @UserIdInt int userId) {
boolean changed = false;
ArrayList<PreferredActivity> removed = null;
for (int i = 0; i < mPreferredActivities.size(); i++) {
diff --git a/services/core/java/com/android/server/pm/permission/PermissionManagerService.java b/services/core/java/com/android/server/pm/permission/PermissionManagerService.java
index 5c5a9c1b6c05..ac19ea12c6a4 100644
--- a/services/core/java/com/android/server/pm/permission/PermissionManagerService.java
+++ b/services/core/java/com/android/server/pm/permission/PermissionManagerService.java
@@ -37,6 +37,7 @@ import android.Manifest;
import android.annotation.AppIdInt;
import android.annotation.NonNull;
import android.annotation.Nullable;
+import android.annotation.SpecialUsers.CanBeALL;
import android.annotation.UserIdInt;
import android.app.ActivityManager;
import android.app.AppOpsManager;
@@ -765,7 +766,7 @@ public class PermissionManagerService extends IPermissionManager.Stub {
@Override
public void onPackageUninstalled(@NonNull String packageName, int appId,
@NonNull PackageState packageState, @Nullable AndroidPackage pkg,
- @NonNull List<AndroidPackage> sharedUserPkgs, @UserIdInt int userId) {
+ @NonNull List<AndroidPackage> sharedUserPkgs, @CanBeALL @UserIdInt int userId) {
if (userId != UserHandle.USER_ALL) {
final int[] userIds = getAllUserIds();
if (!ArrayUtils.contains(userIds, userId)) {
diff --git a/services/core/java/com/android/server/pm/permission/PermissionManagerServiceImpl.java b/services/core/java/com/android/server/pm/permission/PermissionManagerServiceImpl.java
index e51ec04e60fe..33d57d5cc2b9 100644
--- a/services/core/java/com/android/server/pm/permission/PermissionManagerServiceImpl.java
+++ b/services/core/java/com/android/server/pm/permission/PermissionManagerServiceImpl.java
@@ -65,6 +65,7 @@ import android.annotation.AppIdInt;
import android.annotation.IntDef;
import android.annotation.NonNull;
import android.annotation.Nullable;
+import android.annotation.SpecialUsers.CanBeALL;
import android.annotation.UserIdInt;
import android.app.ActivityManager;
import android.app.AppOpsManager;
@@ -5284,7 +5285,7 @@ public class PermissionManagerServiceImpl implements PermissionManagerServiceInt
@Override
public void onPackageUninstalled(@NonNull String packageName, int appId,
@NonNull PackageState packageState, @Nullable AndroidPackage pkg,
- @NonNull List<AndroidPackage> sharedUserPkgs, @UserIdInt int userId) {
+ @NonNull List<AndroidPackage> sharedUserPkgs, @CanBeALL @UserIdInt int userId) {
Objects.requireNonNull(packageState, "packageState");
Objects.requireNonNull(packageName, "packageName");
Objects.requireNonNull(sharedUserPkgs, "sharedUserPkgs");
diff --git a/services/core/java/com/android/server/pm/permission/PermissionManagerServiceInterface.java b/services/core/java/com/android/server/pm/permission/PermissionManagerServiceInterface.java
index 3d295f773805..f2491d949e6b 100644
--- a/services/core/java/com/android/server/pm/permission/PermissionManagerServiceInterface.java
+++ b/services/core/java/com/android/server/pm/permission/PermissionManagerServiceInterface.java
@@ -19,6 +19,7 @@ package com.android.server.pm.permission;
import android.annotation.AppIdInt;
import android.annotation.NonNull;
import android.annotation.Nullable;
+import android.annotation.SpecialUsers.CanBeALL;
import android.annotation.UserIdInt;
import android.content.Context;
import android.content.pm.PackageManager;
@@ -662,5 +663,5 @@ public interface PermissionManagerServiceInterface extends PermissionManagerInte
*/
void onPackageUninstalled(@NonNull String packageName, int appId,
@NonNull PackageState packageState, @Nullable AndroidPackage pkg,
- @NonNull List<AndroidPackage> sharedUserPkgs, @UserIdInt int userId);
+ @NonNull List<AndroidPackage> sharedUserPkgs, @CanBeALL @UserIdInt int userId);
}
diff --git a/services/core/java/com/android/server/pm/permission/PermissionManagerServiceInternal.java b/services/core/java/com/android/server/pm/permission/PermissionManagerServiceInternal.java
index a5c12840a645..ad765c8a0d54 100644
--- a/services/core/java/com/android/server/pm/permission/PermissionManagerServiceInternal.java
+++ b/services/core/java/com/android/server/pm/permission/PermissionManagerServiceInternal.java
@@ -18,6 +18,7 @@ package com.android.server.pm.permission;
import android.annotation.NonNull;
import android.annotation.Nullable;
+import android.annotation.SpecialUsers.CanBeALL;
import android.annotation.UserIdInt;
import android.app.AppOpsManager;
import android.content.pm.PackageInstaller.SessionParams;
@@ -325,7 +326,7 @@ public interface PermissionManagerServiceInternal extends PermissionManagerInter
//@SystemApi(client = SystemApi.Client.SYSTEM_SERVER)
void onPackageUninstalled(@NonNull String packageName, int appId,
@Nullable PackageState packageState, @Nullable AndroidPackage pkg,
- @NonNull List<AndroidPackage> sharedUserPkgs, @UserIdInt int userId);
+ @NonNull List<AndroidPackage> sharedUserPkgs, @CanBeALL @UserIdInt int userId);
/**
* The permission-related parameters passed in for package installation.
diff --git a/services/core/java/com/android/server/power/stats/BatteryStatsImpl.java b/services/core/java/com/android/server/power/stats/BatteryStatsImpl.java
index 1d62087428d8..2cf6b7efcb48 100644
--- a/services/core/java/com/android/server/power/stats/BatteryStatsImpl.java
+++ b/services/core/java/com/android/server/power/stats/BatteryStatsImpl.java
@@ -2911,19 +2911,7 @@ public class BatteryStatsImpl extends BatteryStats {
return false;
}
- mCounter.getCounts(counts, procState);
-
- // Return counts only if at least one of the elements is non-zero.
- for (int i = counts.length - 1; i >= 0; --i) {
- if (counts[i] != 0) {
- return true;
- }
- }
- return false;
- }
-
- public void logState(Printer pw, String prefix) {
- pw.println(prefix + "mCounter=" + mCounter);
+ return mCounter.getCounts(counts, procState);
}
/**
diff --git a/services/core/java/com/android/server/power/stats/processor/AmbientDisplayPowerStatsProcessor.java b/services/core/java/com/android/server/power/stats/processor/AmbientDisplayPowerStatsProcessor.java
index 5f93bdf07f47..a550f2d820a9 100644
--- a/services/core/java/com/android/server/power/stats/processor/AmbientDisplayPowerStatsProcessor.java
+++ b/services/core/java/com/android/server/power/stats/processor/AmbientDisplayPowerStatsProcessor.java
@@ -68,11 +68,15 @@ class AmbientDisplayPowerStatsProcessor extends PowerStatsProcessor {
// processor. All that remains to be done is copy the estimates over.
MultiStateStats.States.forEachTrackedStateCombination(deviceStateConfig,
states -> {
- screenStats.getDeviceStats(mTmpScreenStats, states);
+ if (!screenStats.getDeviceStats(mTmpScreenStats, states)) {
+ return;
+ }
double power =
mScreenPowerStatsLayout.getScreenDozePowerEstimate(mTmpScreenStats);
- mStatsLayout.setDevicePowerEstimate(mTmpDeviceStats, power);
- stats.setDeviceStats(states, mTmpDeviceStats);
+ if (power != 0) {
+ mStatsLayout.setDevicePowerEstimate(mTmpDeviceStats, power);
+ stats.setDeviceStats(states, mTmpDeviceStats);
+ }
});
}
}
diff --git a/services/core/java/com/android/server/power/stats/processor/CpuPowerStatsProcessor.java b/services/core/java/com/android/server/power/stats/processor/CpuPowerStatsProcessor.java
index 6ce2cf738192..98280beced9f 100644
--- a/services/core/java/com/android/server/power/stats/processor/CpuPowerStatsProcessor.java
+++ b/services/core/java/com/android/server/power/stats/processor/CpuPowerStatsProcessor.java
@@ -119,6 +119,8 @@ class CpuPowerStatsProcessor extends PowerStatsProcessor {
mTmpUidStatsArray = new long[descriptor.uidStatsArrayLength];
mWakelockDescriptor = null;
+
+ initEnergyConsumerToPowerBracketMaps();
}
/**
@@ -157,9 +159,6 @@ class CpuPowerStatsProcessor extends PowerStatsProcessor {
if (mPlan == null) {
mPlan = new PowerEstimationPlan(stats.getConfig());
- if (mStatsLayout.getEnergyConsumerCount() != 0) {
- initEnergyConsumerToPowerBracketMaps();
- }
}
Intermediates intermediates = new Intermediates();
@@ -255,6 +254,10 @@ class CpuPowerStatsProcessor extends PowerStatsProcessor {
*/
private void initEnergyConsumerToPowerBracketMaps() {
int energyConsumerCount = mStatsLayout.getEnergyConsumerCount();
+ if (energyConsumerCount == 0) {
+ return;
+ }
+
int powerBracketCount = mStatsLayout.getCpuPowerBracketCount();
mEnergyConsumerToCombinedEnergyConsumerMap = new int[energyConsumerCount];
@@ -404,7 +407,10 @@ class CpuPowerStatsProcessor extends PowerStatsProcessor {
deviceStatsIntermediates.timeByBracket = new long[powerBracketCount];
deviceStatsIntermediates.powerByBracket = new double[powerBracketCount];
- stats.getDeviceStats(mTmpDeviceStatsArray, deviceStateEstimation.stateValues);
+ if (!stats.getDeviceStats(mTmpDeviceStatsArray, deviceStateEstimation.stateValues)) {
+ continue;
+ }
+
for (int step = 0; step < cpuScalingStepCount; step++) {
if (intermediates.timeByScalingStep[step] == 0) {
continue;
@@ -429,16 +435,19 @@ class CpuPowerStatsProcessor extends PowerStatsProcessor {
}
if (wakelockStats != null) {
- wakelockStats.getDeviceStats(mTmpWakelockDeviceStats,
- deviceStateEstimation.stateValues);
- double wakelockPowerEstimate = mWakelockPowerStatsLayout.getDevicePowerEstimate(
- mTmpWakelockDeviceStats);
- power = Math.max(0, power - wakelockPowerEstimate);
+ if (wakelockStats.getDeviceStats(mTmpWakelockDeviceStats,
+ deviceStateEstimation.stateValues)) {
+ double wakelockPowerEstimate = mWakelockPowerStatsLayout.getDevicePowerEstimate(
+ mTmpWakelockDeviceStats);
+ power = Math.max(0, power - wakelockPowerEstimate);
+ }
}
- deviceStatsIntermediates.power = power;
- mStatsLayout.setDevicePowerEstimate(mTmpDeviceStatsArray, power);
- stats.setDeviceStats(deviceStateEstimation.stateValues, mTmpDeviceStatsArray);
+ if (power != 0) {
+ deviceStatsIntermediates.power = power;
+ mStatsLayout.setDevicePowerEstimate(mTmpDeviceStatsArray, power);
+ stats.setDeviceStats(deviceStateEstimation.stateValues, mTmpDeviceStatsArray);
+ }
}
}
@@ -538,11 +547,12 @@ class CpuPowerStatsProcessor extends PowerStatsProcessor {
}
if (wakelockStats != null) {
- wakelockStats.getUidStats(mTmpWakelockUidStats, uid,
- proportionalEstimate.stateValues);
- double wakelockPowerEstimate = mWakelockPowerStatsLayout.getUidPowerEstimate(
- mTmpWakelockUidStats);
- power = Math.max(0, power - wakelockPowerEstimate);
+ if (wakelockStats.getUidStats(mTmpWakelockUidStats, uid,
+ proportionalEstimate.stateValues)) {
+ double wakelockPowerEstimate = mWakelockPowerStatsLayout.getUidPowerEstimate(
+ mTmpWakelockUidStats);
+ power = Math.max(0, power - wakelockPowerEstimate);
+ }
}
if (power != 0) {
diff --git a/services/core/java/com/android/server/power/stats/processor/MobileRadioPowerStatsProcessor.java b/services/core/java/com/android/server/power/stats/processor/MobileRadioPowerStatsProcessor.java
index a544daad82f1..5883505326ad 100644
--- a/services/core/java/com/android/server/power/stats/processor/MobileRadioPowerStatsProcessor.java
+++ b/services/core/java/com/android/server/power/stats/processor/MobileRadioPowerStatsProcessor.java
@@ -259,7 +259,9 @@ class MobileRadioPowerStatsProcessor extends PowerStatsProcessor {
stats.forEachStateStatsKey(key -> {
RxTxPowerEstimators estimators = mRxTxPowerEstimators.get(key);
- stats.getStateStats(mTmpStateStatsArray, key, deviceStates);
+ if (!stats.getStateStats(mTmpStateStatsArray, key, deviceStates)) {
+ return;
+ }
long rxTime = mStatsLayout.getStateRxTime(mTmpStateStatsArray);
intermediates.rxPower += estimators.mRxPowerEstimator.calculatePower(rxTime);
for (int txLevel = 0; txLevel < ModemActivityInfo.getNumTxPowerLevels(); txLevel++) {
diff --git a/services/core/java/com/android/server/power/stats/processor/MultiStateStats.java b/services/core/java/com/android/server/power/stats/processor/MultiStateStats.java
index 69325757c79d..0038943d7cf8 100644
--- a/services/core/java/com/android/server/power/stats/processor/MultiStateStats.java
+++ b/services/core/java/com/android/server/power/stats/processor/MultiStateStats.java
@@ -16,6 +16,7 @@
package com.android.server.power.stats.processor;
+import android.annotation.CheckResult;
import android.annotation.Nullable;
import android.util.Slog;
@@ -342,10 +343,12 @@ class MultiStateStats {
}
/**
- * Returns accumulated stats for the specified composite state.
+ * Returns accumulated stats for the specified composite state or false if the results are
+ * all zeros.
*/
- void getStats(long[] outValues, int[] states) {
- mCounter.getCounts(outValues, mFactory.getSerialState(states));
+ @CheckResult
+ boolean getStats(long[] outValues, int[] states) {
+ return mCounter.getCounts(outValues, mFactory.getSerialState(states));
}
/**
@@ -389,15 +392,7 @@ class MultiStateStats {
private void writeXmlForStates(TypedXmlSerializer serializer, int[] states, long[] values)
throws IOException {
- mCounter.getCounts(values, mFactory.getSerialState(states));
- boolean nonZero = false;
- for (long value : values) {
- if (value != 0) {
- nonZero = true;
- break;
- }
- }
- if (!nonZero) {
+ if (!mCounter.getCounts(values, mFactory.getSerialState(states))) {
return;
}
@@ -470,15 +465,7 @@ class MultiStateStats {
StringBuilder sb = new StringBuilder();
long[] values = new long[mCounter.getArrayLength()];
States.forEachTrackedStateCombination(mFactory.mStates, states -> {
- mCounter.getCounts(values, mFactory.getSerialState(states));
- boolean nonZero = false;
- for (long value : values) {
- if (value != 0) {
- nonZero = true;
- break;
- }
- }
- if (!nonZero) {
+ if (!mCounter.getCounts(values, mFactory.getSerialState(states))) {
return;
}
diff --git a/services/core/java/com/android/server/power/stats/processor/PhoneCallPowerStatsProcessor.java b/services/core/java/com/android/server/power/stats/processor/PhoneCallPowerStatsProcessor.java
index 3957ae0862dc..ad628e4632e1 100644
--- a/services/core/java/com/android/server/power/stats/processor/PhoneCallPowerStatsProcessor.java
+++ b/services/core/java/com/android/server/power/stats/processor/PhoneCallPowerStatsProcessor.java
@@ -69,12 +69,15 @@ class PhoneCallPowerStatsProcessor extends PowerStatsProcessor {
// processor. All that remains to be done is copy the estimates over.
MultiStateStats.States.forEachTrackedStateCombination(deviceStateConfig,
states -> {
- mobileRadioStats.getDeviceStats(mTmpMobileRadioDeviceStats, states);
- double callPowerEstimate =
- mMobileRadioStatsLayout.getDeviceCallPowerEstimate(
- mTmpMobileRadioDeviceStats);
- mStatsLayout.setDevicePowerEstimate(mTmpDeviceStats, callPowerEstimate);
- stats.setDeviceStats(states, mTmpDeviceStats);
+ if (!mobileRadioStats.getDeviceStats(mTmpMobileRadioDeviceStats, states)) {
+ return;
+ }
+ double callPowerEstimate = mMobileRadioStatsLayout.getDeviceCallPowerEstimate(
+ mTmpMobileRadioDeviceStats);
+ if (callPowerEstimate != 0) {
+ mStatsLayout.setDevicePowerEstimate(mTmpDeviceStats, callPowerEstimate);
+ stats.setDeviceStats(states, mTmpDeviceStats);
+ }
});
}
}
diff --git a/services/core/java/com/android/server/power/stats/processor/PowerComponentAggregatedPowerStats.java b/services/core/java/com/android/server/power/stats/processor/PowerComponentAggregatedPowerStats.java
index f9b9da9171cb..d285a5987710 100644
--- a/services/core/java/com/android/server/power/stats/processor/PowerComponentAggregatedPowerStats.java
+++ b/services/core/java/com/android/server/power/stats/processor/PowerComponentAggregatedPowerStats.java
@@ -16,6 +16,7 @@
package com.android.server.power.stats.processor;
+import android.annotation.CheckResult;
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.os.BatteryStats;
@@ -312,6 +313,11 @@ class PowerComponentAggregatedPowerStats {
return uids;
}
+ /**
+ * Populates outValues with the stats for the specified states. If the stats are all 0,
+ * returns false, leaving outValues unchanged.
+ */
+ @CheckResult
boolean getDeviceStats(long[] outValues, int[] deviceStates) {
if (deviceStates.length != mDeviceStateConfig.length) {
throw new IllegalArgumentException(
@@ -319,12 +325,16 @@ class PowerComponentAggregatedPowerStats {
+ " expected: " + mDeviceStateConfig.length);
}
if (mDeviceStats != null) {
- mDeviceStats.getStats(outValues, deviceStates);
- return true;
+ return mDeviceStats.getStats(outValues, deviceStates);
}
return false;
}
+ /**
+ * Populates outValues with the stats for the specified key and device states. If the stats
+ * are all 0, returns false, leaving outValues unchanged.
+ */
+ @CheckResult
boolean getStateStats(long[] outValues, int key, int[] deviceStates) {
if (deviceStates.length != mDeviceStateConfig.length) {
throw new IllegalArgumentException(
@@ -333,8 +343,7 @@ class PowerComponentAggregatedPowerStats {
}
MultiStateStats stateStats = mStateStats.get(key);
if (stateStats != null) {
- stateStats.getStats(outValues, deviceStates);
- return true;
+ return stateStats.getStats(outValues, deviceStates);
}
return false;
}
@@ -345,6 +354,11 @@ class PowerComponentAggregatedPowerStats {
}
}
+ /**
+ * Populates outValues with the stats for the specified UID and UID states. If the stats are
+ * all 0, returns false, leaving outValues unchanged.
+ */
+ @CheckResult
boolean getUidStats(long[] outValues, int uid, int[] uidStates) {
if (uidStates.length != mUidStateConfig.length) {
throw new IllegalArgumentException(
@@ -353,8 +367,7 @@ class PowerComponentAggregatedPowerStats {
}
UidStats uidStats = mUidStats.get(uid);
if (uidStats != null && uidStats.stats != null) {
- uidStats.stats.getStats(outValues, uidStates);
- return true;
+ return uidStats.stats.getStats(outValues, uidStates);
}
return false;
}
@@ -578,15 +591,7 @@ class PowerComponentAggregatedPowerStats {
long[] values = new long[stats.getDimensionCount()];
MultiStateStats.States[] stateInfo = stats.getStates();
MultiStateStats.States.forEachTrackedStateCombination(stateInfo, states -> {
- stats.getStats(values, states);
- boolean nonZero = false;
- for (long value : values) {
- if (value != 0) {
- nonZero = true;
- break;
- }
- }
- if (!nonZero) {
+ if (!stats.getStats(values, states)) {
return;
}
diff --git a/services/core/java/com/android/server/power/stats/processor/SensorPowerStatsProcessor.java b/services/core/java/com/android/server/power/stats/processor/SensorPowerStatsProcessor.java
index ba728d36d561..284e6a9fce89 100644
--- a/services/core/java/com/android/server/power/stats/processor/SensorPowerStatsProcessor.java
+++ b/services/core/java/com/android/server/power/stats/processor/SensorPowerStatsProcessor.java
@@ -298,12 +298,16 @@ class SensorPowerStatsProcessor extends PowerStatsProcessor {
continue;
}
- if (!stats.getDeviceStats(mTmpDeviceStatsArray, estimation.stateValues)) {
+ double power = ((Intermediates) estimation.intermediates).power;
+ if (power == 0) {
continue;
}
- mStatsLayout.setDevicePowerEstimate(mTmpDeviceStatsArray,
- ((Intermediates) estimation.intermediates).power);
+ if (!stats.getDeviceStats(mTmpDeviceStatsArray, estimation.stateValues)) {
+ Arrays.fill(mTmpDeviceStatsArray, 0);
+ }
+
+ mStatsLayout.setDevicePowerEstimate(mTmpDeviceStatsArray, power);
stats.setDeviceStats(estimation.stateValues, mTmpDeviceStatsArray);
}
}
diff --git a/services/core/java/com/android/server/storage/ImmutableVolumeInfo.java b/services/core/java/com/android/server/storage/ImmutableVolumeInfo.java
index 9d60a576d9bc..0f8212e23c20 100644
--- a/services/core/java/com/android/server/storage/ImmutableVolumeInfo.java
+++ b/services/core/java/com/android/server/storage/ImmutableVolumeInfo.java
@@ -136,4 +136,9 @@ public final class ImmutableVolumeInfo {
public boolean isVisibleForWrite(int userId) {
return mVolumeInfo.isVisibleForWrite(userId);
}
+
+ @Override
+ public String toString() {
+ return mVolumeInfo.toString();
+ }
}
diff --git a/services/core/java/com/android/server/storage/StorageSessionController.java b/services/core/java/com/android/server/storage/StorageSessionController.java
index 342b864c6473..281aeb68f224 100644
--- a/services/core/java/com/android/server/storage/StorageSessionController.java
+++ b/services/core/java/com/android/server/storage/StorageSessionController.java
@@ -156,14 +156,15 @@ public final class StorageSessionController {
StorageUserConnection connection = null;
synchronized (mLock) {
connection = mConnections.get(connectionUserId);
- if (connection != null) {
- Slog.i(TAG, "Notifying volume state changed for session with id: " + sessionId);
- connection.notifyVolumeStateChanged(sessionId,
- vol.buildStorageVolume(mContext, vol.getMountUserId(), false));
- } else {
- Slog.w(TAG, "No available storage user connection for userId : "
- + connectionUserId);
- }
+ }
+
+ if (connection != null) {
+ Slog.i(TAG, "Notifying volume state changed for session with id: " + sessionId);
+ connection.notifyVolumeStateChanged(sessionId,
+ vol.buildStorageVolume(mContext, vol.getMountUserId(), false));
+ } else {
+ Slog.w(TAG, "No available storage user connection for userId : "
+ + connectionUserId);
}
}
@@ -225,16 +226,18 @@ public final class StorageSessionController {
String sessionId = vol.getId();
int userId = getConnectionUserIdForVolume(vol);
+ StorageUserConnection connection = null;
synchronized (mLock) {
- StorageUserConnection connection = mConnections.get(userId);
- if (connection != null) {
- Slog.i(TAG, "Removed session for vol with id: " + sessionId);
- connection.removeSession(sessionId);
- return connection;
- } else {
- Slog.w(TAG, "Session already removed for vol with id: " + sessionId);
- return null;
- }
+ connection = mConnections.get(userId);
+ }
+
+ if (connection != null) {
+ Slog.i(TAG, "Removed session for vol with id: " + sessionId);
+ connection.removeSession(sessionId);
+ return connection;
+ } else {
+ Slog.w(TAG, "Session already removed for vol with id: " + sessionId);
+ return null;
}
}
diff --git a/services/core/java/com/android/server/storage/WatchedVolumeInfo.java b/services/core/java/com/android/server/storage/WatchedVolumeInfo.java
index 4124cfb4f092..94e52cd1033a 100644
--- a/services/core/java/com/android/server/storage/WatchedVolumeInfo.java
+++ b/services/core/java/com/android/server/storage/WatchedVolumeInfo.java
@@ -203,4 +203,9 @@ public class WatchedVolumeInfo extends WatchableImpl {
public boolean isVisibleForWrite(int userId) {
return mVolumeInfo.isVisibleForWrite(userId);
}
+
+ @Override
+ public String toString() {
+ return mVolumeInfo.toString();
+ }
} \ No newline at end of file
diff --git a/services/core/java/com/android/server/vibrator/AbstractVibratorStep.java b/services/core/java/com/android/server/vibrator/AbstractVibratorStep.java
index 07d9ad16aca5..da6478bd3395 100644
--- a/services/core/java/com/android/server/vibrator/AbstractVibratorStep.java
+++ b/services/core/java/com/android/server/vibrator/AbstractVibratorStep.java
@@ -124,6 +124,8 @@ abstract class AbstractVibratorStep extends Step {
Slog.d(VibrationThread.TAG,
"Turning off vibrator " + getVibratorId());
}
+ // Make sure we ignore any pending callback from old vibration commands.
+ conductor.nextVibratorCallbackStepId(getVibratorId());
controller.off();
getVibration().stats.reportVibratorOff();
mPendingVibratorOffDeadline = 0;
diff --git a/services/core/java/com/android/server/vibrator/ComposePrimitivesVibratorStep.java b/services/core/java/com/android/server/vibrator/ComposePrimitivesVibratorStep.java
index e495af59a2f9..b3eead109999 100644
--- a/services/core/java/com/android/server/vibrator/ComposePrimitivesVibratorStep.java
+++ b/services/core/java/com/android/server/vibrator/ComposePrimitivesVibratorStep.java
@@ -73,7 +73,8 @@ final class ComposePrimitivesVibratorStep extends AbstractComposedVibratorStep {
PrimitiveSegment[] primitivesArray =
primitives.toArray(new PrimitiveSegment[primitives.size()]);
- long vibratorOnResult = controller.on(primitivesArray, getVibration().id);
+ int stepId = conductor.nextVibratorCallbackStepId(getVibratorId());
+ long vibratorOnResult = controller.on(primitivesArray, getVibration().id, stepId);
handleVibratorOnResult(vibratorOnResult);
getVibration().stats.reportComposePrimitives(vibratorOnResult, primitivesArray);
diff --git a/services/core/java/com/android/server/vibrator/ComposePwleV2VibratorStep.java b/services/core/java/com/android/server/vibrator/ComposePwleV2VibratorStep.java
index bb8e6eed5707..7b41457b5016 100644
--- a/services/core/java/com/android/server/vibrator/ComposePwleV2VibratorStep.java
+++ b/services/core/java/com/android/server/vibrator/ComposePwleV2VibratorStep.java
@@ -72,7 +72,8 @@ final class ComposePwleV2VibratorStep extends AbstractComposedVibratorStep {
+ controller.getVibratorInfo().getId());
}
PwlePoint[] pwlesArray = pwles.toArray(new PwlePoint[pwles.size()]);
- long vibratorOnResult = controller.on(pwlesArray, getVibration().id);
+ int stepId = conductor.nextVibratorCallbackStepId(getVibratorId());
+ long vibratorOnResult = controller.on(pwlesArray, getVibration().id, stepId);
handleVibratorOnResult(vibratorOnResult);
getVibration().stats.reportComposePwle(vibratorOnResult, pwlesArray);
diff --git a/services/core/java/com/android/server/vibrator/ComposePwleVibratorStep.java b/services/core/java/com/android/server/vibrator/ComposePwleVibratorStep.java
index e8952fafaf77..d003251ef307 100644
--- a/services/core/java/com/android/server/vibrator/ComposePwleVibratorStep.java
+++ b/services/core/java/com/android/server/vibrator/ComposePwleVibratorStep.java
@@ -71,7 +71,8 @@ final class ComposePwleVibratorStep extends AbstractComposedVibratorStep {
+ controller.getVibratorInfo().getId());
}
RampSegment[] pwlesArray = pwles.toArray(new RampSegment[pwles.size()]);
- long vibratorOnResult = controller.on(pwlesArray, getVibration().id);
+ int stepId = conductor.nextVibratorCallbackStepId(getVibratorId());
+ long vibratorOnResult = controller.on(pwlesArray, getVibration().id, stepId);
handleVibratorOnResult(vibratorOnResult);
getVibration().stats.reportComposePwle(vibratorOnResult, pwlesArray);
diff --git a/services/core/java/com/android/server/vibrator/ExternalVibrationSession.java b/services/core/java/com/android/server/vibrator/ExternalVibrationSession.java
index a92ac679b0f4..b2cc1f60ca1d 100644
--- a/services/core/java/com/android/server/vibrator/ExternalVibrationSession.java
+++ b/services/core/java/com/android/server/vibrator/ExternalVibrationSession.java
@@ -148,7 +148,7 @@ final class ExternalVibrationSession extends Vibration
}
@Override
- public void notifyVibratorCallback(int vibratorId, long vibrationId) {
+ public void notifyVibratorCallback(int vibratorId, long vibrationId, long stepId) {
// ignored, external control does not expect callbacks from the vibrator
}
diff --git a/services/core/java/com/android/server/vibrator/PerformPrebakedVibratorStep.java b/services/core/java/com/android/server/vibrator/PerformPrebakedVibratorStep.java
index 4b23216258af..88bb181781bf 100644
--- a/services/core/java/com/android/server/vibrator/PerformPrebakedVibratorStep.java
+++ b/services/core/java/com/android/server/vibrator/PerformPrebakedVibratorStep.java
@@ -64,7 +64,8 @@ final class PerformPrebakedVibratorStep extends AbstractComposedVibratorStep {
}
VibrationEffect fallback = getVibration().getFallback(prebaked.getEffectId());
- long vibratorOnResult = controller.on(prebaked, getVibration().id);
+ int stepId = conductor.nextVibratorCallbackStepId(getVibratorId());
+ long vibratorOnResult = controller.on(prebaked, getVibration().id, stepId);
handleVibratorOnResult(vibratorOnResult);
getVibration().stats.reportPerformEffect(vibratorOnResult, prebaked);
diff --git a/services/core/java/com/android/server/vibrator/PerformVendorEffectVibratorStep.java b/services/core/java/com/android/server/vibrator/PerformVendorEffectVibratorStep.java
index 407f3d996798..d4bcc36aee18 100644
--- a/services/core/java/com/android/server/vibrator/PerformVendorEffectVibratorStep.java
+++ b/services/core/java/com/android/server/vibrator/PerformVendorEffectVibratorStep.java
@@ -49,7 +49,8 @@ final class PerformVendorEffectVibratorStep extends AbstractVibratorStep {
public List<Step> play() {
Trace.traceBegin(Trace.TRACE_TAG_VIBRATOR, "PerformVendorEffectVibratorStep");
try {
- long vibratorOnResult = controller.on(effect, getVibration().id);
+ int stepId = conductor.nextVibratorCallbackStepId(getVibratorId());
+ long vibratorOnResult = controller.on(effect, getVibration().id, stepId);
vibratorOnResult = Math.min(vibratorOnResult, VENDOR_EFFECT_MAX_DURATION_MS);
handleVibratorOnResult(vibratorOnResult);
getVibration().stats.reportPerformVendorEffect(vibratorOnResult);
diff --git a/services/core/java/com/android/server/vibrator/SetAmplitudeVibratorStep.java b/services/core/java/com/android/server/vibrator/SetAmplitudeVibratorStep.java
index 8478e7743183..26b9595e60cd 100644
--- a/services/core/java/com/android/server/vibrator/SetAmplitudeVibratorStep.java
+++ b/services/core/java/com/android/server/vibrator/SetAmplitudeVibratorStep.java
@@ -20,6 +20,7 @@ import android.annotation.NonNull;
import android.os.SystemClock;
import android.os.Trace;
import android.os.VibrationEffect;
+import android.os.vibrator.Flags;
import android.os.vibrator.StepSegment;
import android.os.vibrator.VibrationEffectSegment;
import android.util.Slog;
@@ -49,6 +50,10 @@ final class SetAmplitudeVibratorStep extends AbstractComposedVibratorStep {
@Override
public boolean acceptVibratorCompleteCallback(int vibratorId) {
+ if (Flags.fixVibrationThreadCallbackHandling()) {
+ // TODO: remove this method once flag removed.
+ return super.acceptVibratorCompleteCallback(vibratorId);
+ }
// Ensure the super method is called and will reset the off timeout and boolean flag.
// This is true if the vibrator was ON and this callback has the same vibratorId.
if (!super.acceptVibratorCompleteCallback(vibratorId)) {
@@ -161,7 +166,8 @@ final class SetAmplitudeVibratorStep extends AbstractComposedVibratorStep {
"Turning on vibrator " + controller.getVibratorInfo().getId() + " for "
+ duration + "ms");
}
- long vibratorOnResult = controller.on(duration, getVibration().id);
+ int stepId = conductor.nextVibratorCallbackStepId(getVibratorId());
+ long vibratorOnResult = controller.on(duration, getVibration().id, stepId);
handleVibratorOnResult(vibratorOnResult);
getVibration().stats.reportVibratorOn(vibratorOnResult);
return vibratorOnResult;
diff --git a/services/core/java/com/android/server/vibrator/SingleVibrationSession.java b/services/core/java/com/android/server/vibrator/SingleVibrationSession.java
index 628221b09d77..309eb8c3b099 100644
--- a/services/core/java/com/android/server/vibrator/SingleVibrationSession.java
+++ b/services/core/java/com/android/server/vibrator/SingleVibrationSession.java
@@ -137,13 +137,13 @@ final class SingleVibrationSession implements VibrationSession, IBinder.DeathRec
}
@Override
- public void notifyVibratorCallback(int vibratorId, long vibrationId) {
+ public void notifyVibratorCallback(int vibratorId, long vibrationId, long stepId) {
if (vibrationId != mVibration.id) {
return;
}
synchronized (mLock) {
if (mConductor != null) {
- mConductor.notifyVibratorComplete(vibratorId);
+ mConductor.notifyVibratorComplete(vibratorId, stepId);
}
}
}
diff --git a/services/core/java/com/android/server/vibrator/VendorVibrationSession.java b/services/core/java/com/android/server/vibrator/VendorVibrationSession.java
index 64b52b175252..bda3d442956b 100644
--- a/services/core/java/com/android/server/vibrator/VendorVibrationSession.java
+++ b/services/core/java/com/android/server/vibrator/VendorVibrationSession.java
@@ -218,8 +218,8 @@ final class VendorVibrationSession extends IVibrationSession.Stub
}
@Override
- public void notifyVibratorCallback(int vibratorId, long vibrationId) {
- Slog.d(TAG, "Vibration callback received for vibration " + vibrationId
+ public void notifyVibratorCallback(int vibratorId, long vibrationId, long stepId) {
+ Slog.d(TAG, "Vibration callback received for vibration " + vibrationId + " step " + stepId
+ " on vibrator " + vibratorId + ", ignoring...");
}
diff --git a/services/core/java/com/android/server/vibrator/VibrationSession.java b/services/core/java/com/android/server/vibrator/VibrationSession.java
index ae95a70e2a4f..23715e392580 100644
--- a/services/core/java/com/android/server/vibrator/VibrationSession.java
+++ b/services/core/java/com/android/server/vibrator/VibrationSession.java
@@ -106,7 +106,7 @@ interface VibrationSession {
* complete (e.g. on(), perform(), compose()). This does not mean the vibration is complete,
* since its playback might have one or more interactions with the vibrator hardware.
*/
- void notifyVibratorCallback(int vibratorId, long vibrationId);
+ void notifyVibratorCallback(int vibratorId, long vibrationId, long stepId);
/**
* Notify all synced vibrators have completed the last synchronized command during the playback
diff --git a/services/core/java/com/android/server/vibrator/VibrationStepConductor.java b/services/core/java/com/android/server/vibrator/VibrationStepConductor.java
index 1e20debe156d..36e13224a476 100644
--- a/services/core/java/com/android/server/vibrator/VibrationStepConductor.java
+++ b/services/core/java/com/android/server/vibrator/VibrationStepConductor.java
@@ -30,6 +30,7 @@ import android.os.vibrator.VibrationEffectSegment;
import android.util.IntArray;
import android.util.Slog;
import android.util.SparseArray;
+import android.util.SparseIntArray;
import com.android.internal.annotations.GuardedBy;
import com.android.server.vibrator.VibrationSession.Status;
@@ -93,6 +94,8 @@ final class VibrationStepConductor {
private final Object mLock = new Object();
@GuardedBy("mLock")
private final IntArray mSignalVibratorsComplete;
+ @GuardedBy("mLock")
+ private final SparseIntArray mSignalVibratorStepIds;
@Nullable
@GuardedBy("mLock")
private Vibration.EndInfo mSignalCancel = null;
@@ -121,6 +124,8 @@ final class VibrationStepConductor {
this.vibratorManagerHooks = vibratorManagerHooks;
this.mSignalVibratorsComplete =
new IntArray(mDeviceAdapter.getAvailableVibratorIds().length);
+ this.mSignalVibratorStepIds =
+ new SparseIntArray(mDeviceAdapter.getAvailableVibratorIds().length);
}
@Nullable
@@ -418,7 +423,7 @@ final class VibrationStepConductor {
* <p>This is a lightweight method intended to be called directly via native callbacks.
* The state update is recorded for processing on the main execution thread (VibrationThread).
*/
- public void notifyVibratorComplete(int vibratorId) {
+ public void notifyVibratorComplete(int vibratorId, long stepId) {
// HAL callbacks may be triggered directly within HAL calls, so these notifications
// could be on the VibrationThread as it calls the HAL, or some other executor later.
// Therefore no thread assertion is made here.
@@ -428,6 +433,14 @@ final class VibrationStepConductor {
}
synchronized (mLock) {
+ if (Flags.fixVibrationThreadCallbackHandling()
+ && mSignalVibratorStepIds.get(vibratorId) != stepId) {
+ if (DEBUG) {
+ Slog.d(TAG, "Vibrator " + vibratorId + " callback for step=" + stepId
+ + " ignored, current step=" + mSignalVibratorStepIds.get(vibratorId));
+ }
+ return;
+ }
mSignalVibratorsComplete.add(vibratorId);
mLock.notify();
}
@@ -645,6 +658,26 @@ final class VibrationStepConductor {
}
}
+ /**
+ * Updates and returns the next step id value to be used in vibrator commands.
+ *
+ * <p>This new step id will be kept by this conductor to filter out old callbacks that might be
+ * triggered too late by the HAL, preventing them from affecting the ongoing vibration playback.
+ */
+ public int nextVibratorCallbackStepId(int vibratorId) {
+ if (!Flags.fixVibrationThreadCallbackHandling()) {
+ return 0;
+ }
+ if (Build.IS_DEBUGGABLE) {
+ expectIsVibrationThread(true);
+ }
+ synchronized (mLock) {
+ int stepId = mSignalVibratorStepIds.get(vibratorId) + 1;
+ mSignalVibratorStepIds.put(vibratorId, stepId);
+ return stepId;
+ }
+ }
+
private static CombinedVibration.Sequential toSequential(CombinedVibration effect) {
if (effect instanceof CombinedVibration.Sequential) {
return (CombinedVibration.Sequential) effect;
diff --git a/services/core/java/com/android/server/vibrator/VibratorController.java b/services/core/java/com/android/server/vibrator/VibratorController.java
index acb31ceb4027..ab13b0e88d04 100644
--- a/services/core/java/com/android/server/vibrator/VibratorController.java
+++ b/services/core/java/com/android/server/vibrator/VibratorController.java
@@ -72,7 +72,7 @@ final class VibratorController {
public interface OnVibrationCompleteListener {
/** Callback triggered when an active vibration command is complete. */
- void onComplete(int vibratorId, long vibrationId);
+ void onComplete(int vibratorId, long vibrationId, long stepId);
}
/** Representation of the vibrator state based on the interactions through this controller. */
@@ -285,11 +285,11 @@ final class VibratorController {
* @return The positive duration of the vibration started, if successful, zero if the vibrator
* do not support the input or a negative number if the operation failed.
*/
- public long on(long milliseconds, long vibrationId) {
+ public long on(long milliseconds, long vibrationId, long stepId) {
Trace.traceBegin(TRACE_TAG_VIBRATOR, "VibratorController#on");
try {
synchronized (mLock) {
- long duration = mNativeWrapper.on(milliseconds, vibrationId);
+ long duration = mNativeWrapper.on(milliseconds, vibrationId, stepId);
if (duration > 0) {
mCurrentAmplitude = -1;
updateStateAndNotifyListenersLocked(VibratorState.VIBRATING);
@@ -310,7 +310,7 @@ final class VibratorController {
* @return The positive duration of the vibration started, if successful, zero if the vibrator
* do not support the input or a negative number if the operation failed.
*/
- public long on(VibrationEffect.VendorEffect vendorEffect, long vibrationId) {
+ public long on(VibrationEffect.VendorEffect vendorEffect, long vibrationId, long stepId) {
Trace.traceBegin(TRACE_TAG_VIBRATOR, "VibratorController#on (vendor)");
synchronized (mLock) {
Parcel vendorData = Parcel.obtain();
@@ -319,7 +319,7 @@ final class VibratorController {
vendorData.setDataPosition(0);
long duration = mNativeWrapper.performVendorEffect(vendorData,
vendorEffect.getEffectStrength(), vendorEffect.getScale(),
- vendorEffect.getAdaptiveScale(), vibrationId);
+ vendorEffect.getAdaptiveScale(), vibrationId, stepId);
if (duration > 0) {
mCurrentAmplitude = -1;
updateStateAndNotifyListenersLocked(VibratorState.VIBRATING);
@@ -341,12 +341,12 @@ final class VibratorController {
* @return The positive duration of the vibration started, if successful, zero if the vibrator
* do not support the input or a negative number if the operation failed.
*/
- public long on(PrebakedSegment prebaked, long vibrationId) {
+ public long on(PrebakedSegment prebaked, long vibrationId, long stepId) {
Trace.traceBegin(TRACE_TAG_VIBRATOR, "VibratorController#on (Prebaked)");
try {
synchronized (mLock) {
long duration = mNativeWrapper.perform(prebaked.getEffectId(),
- prebaked.getEffectStrength(), vibrationId);
+ prebaked.getEffectStrength(), vibrationId, stepId);
if (duration > 0) {
mCurrentAmplitude = -1;
updateStateAndNotifyListenersLocked(VibratorState.VIBRATING);
@@ -367,14 +367,14 @@ final class VibratorController {
* @return The positive duration of the vibration started, if successful, zero if the vibrator
* do not support the input or a negative number if the operation failed.
*/
- public long on(PrimitiveSegment[] primitives, long vibrationId) {
+ public long on(PrimitiveSegment[] primitives, long vibrationId, long stepId) {
Trace.traceBegin(TRACE_TAG_VIBRATOR, "VibratorController#on (Primitive)");
try {
if (!mVibratorInfo.hasCapability(IVibrator.CAP_COMPOSE_EFFECTS)) {
return 0;
}
synchronized (mLock) {
- long duration = mNativeWrapper.compose(primitives, vibrationId);
+ long duration = mNativeWrapper.compose(primitives, vibrationId, stepId);
if (duration > 0) {
mCurrentAmplitude = -1;
updateStateAndNotifyListenersLocked(VibratorState.VIBRATING);
@@ -394,7 +394,7 @@ final class VibratorController {
*
* @return The duration of the effect playing, or 0 if unsupported.
*/
- public long on(RampSegment[] primitives, long vibrationId) {
+ public long on(RampSegment[] primitives, long vibrationId, long stepId) {
Trace.traceBegin(TRACE_TAG_VIBRATOR, "VibratorController#on (PWLE)");
try {
if (!mVibratorInfo.hasCapability(IVibrator.CAP_COMPOSE_PWLE_EFFECTS)) {
@@ -402,7 +402,8 @@ final class VibratorController {
}
synchronized (mLock) {
int braking = mVibratorInfo.getDefaultBraking();
- long duration = mNativeWrapper.composePwle(primitives, braking, vibrationId);
+ long duration = mNativeWrapper.composePwle(
+ primitives, braking, vibrationId, stepId);
if (duration > 0) {
mCurrentAmplitude = -1;
updateStateAndNotifyListenersLocked(VibratorState.VIBRATING);
@@ -422,14 +423,14 @@ final class VibratorController {
*
* @return The duration of the effect playing, or 0 if unsupported.
*/
- public long on(PwlePoint[] pwlePoints, long vibrationId) {
+ public long on(PwlePoint[] pwlePoints, long vibrationId, long stepId) {
Trace.traceBegin(TRACE_TAG_VIBRATOR, "VibratorController#on (PWLE v2)");
try {
if (!mVibratorInfo.hasCapability(IVibrator.CAP_COMPOSE_PWLE_EFFECTS_V2)) {
return 0;
}
synchronized (mLock) {
- long duration = mNativeWrapper.composePwleV2(pwlePoints, vibrationId);
+ long duration = mNativeWrapper.composePwleV2(pwlePoints, vibrationId, stepId);
if (duration > 0) {
mCurrentAmplitude = -1;
updateStateAndNotifyListenersLocked(VibratorState.VIBRATING);
@@ -544,26 +545,27 @@ final class VibratorController {
private static native boolean isAvailable(long nativePtr);
- private static native long on(long nativePtr, long milliseconds, long vibrationId);
+ private static native long on(long nativePtr, long milliseconds, long vibrationId,
+ long stepId);
private static native void off(long nativePtr);
private static native void setAmplitude(long nativePtr, float amplitude);
private static native long performEffect(long nativePtr, long effect, long strength,
- long vibrationId);
+ long vibrationId, long stepId);
private static native long performVendorEffect(long nativePtr, Parcel vendorData,
- long strength, float scale, float adaptiveScale, long vibrationId);
+ long strength, float scale, float adaptiveScale, long vibrationId, long stepId);
private static native long performComposedEffect(long nativePtr, PrimitiveSegment[] effect,
- long vibrationId);
+ long vibrationId, long stepId);
private static native long performPwleEffect(long nativePtr, RampSegment[] effect,
- int braking, long vibrationId);
+ int braking, long vibrationId, long stepId);
private static native long performPwleV2Effect(long nativePtr, PwlePoint[] effect,
- long vibrationId);
+ long vibrationId, long stepId);
private static native void setExternalControl(long nativePtr, boolean enabled);
@@ -595,8 +597,8 @@ final class VibratorController {
}
/** Turns vibrator on for given time. */
- public long on(long milliseconds, long vibrationId) {
- return on(mNativePtr, milliseconds, vibrationId);
+ public long on(long milliseconds, long vibrationId, long stepId) {
+ return on(mNativePtr, milliseconds, vibrationId, stepId);
}
/** Turns vibrator off. */
@@ -610,30 +612,31 @@ final class VibratorController {
}
/** Turns vibrator on to perform one of the supported effects. */
- public long perform(long effect, long strength, long vibrationId) {
- return performEffect(mNativePtr, effect, strength, vibrationId);
+ public long perform(long effect, long strength, long vibrationId, long stepId) {
+ return performEffect(mNativePtr, effect, strength, vibrationId, stepId);
}
/** Turns vibrator on to perform a vendor-specific effect. */
public long performVendorEffect(Parcel vendorData, long strength, float scale,
- float adaptiveScale, long vibrationId) {
+ float adaptiveScale, long vibrationId, long stepId) {
return performVendorEffect(mNativePtr, vendorData, strength, scale, adaptiveScale,
- vibrationId);
+ vibrationId, stepId);
}
/** Turns vibrator on to perform effect composed of give primitives effect. */
- public long compose(PrimitiveSegment[] primitives, long vibrationId) {
- return performComposedEffect(mNativePtr, primitives, vibrationId);
+ public long compose(PrimitiveSegment[] primitives, long vibrationId, long stepId) {
+ return performComposedEffect(mNativePtr, primitives, vibrationId, stepId);
}
/** Turns vibrator on to perform PWLE effect composed of given primitives. */
- public long composePwle(RampSegment[] primitives, int braking, long vibrationId) {
- return performPwleEffect(mNativePtr, primitives, braking, vibrationId);
+ public long composePwle(RampSegment[] primitives, int braking, long vibrationId,
+ long stepId) {
+ return performPwleEffect(mNativePtr, primitives, braking, vibrationId, stepId);
}
/** Turns vibrator on to perform PWLE effect composed of given points. */
- public long composePwleV2(PwlePoint[] pwlePoints, long vibrationId) {
- return performPwleV2Effect(mNativePtr, pwlePoints, vibrationId);
+ public long composePwleV2(PwlePoint[] pwlePoints, long vibrationId, long stepId) {
+ return performPwleV2Effect(mNativePtr, pwlePoints, vibrationId, stepId);
}
/** Enabled the device vibrator to be controlled by another service. */
diff --git a/services/core/java/com/android/server/vibrator/VibratorManagerService.java b/services/core/java/com/android/server/vibrator/VibratorManagerService.java
index 75b1b202bcfd..3f5fc338ee3b 100644
--- a/services/core/java/com/android/server/vibrator/VibratorManagerService.java
+++ b/services/core/java/com/android/server/vibrator/VibratorManagerService.java
@@ -1293,14 +1293,14 @@ public class VibratorManagerService extends IVibratorManagerService.Stub {
}
}
- private void onVibrationComplete(int vibratorId, long vibrationId) {
+ private void onVibrationComplete(int vibratorId, long vibrationId, long stepId) {
synchronized (mLock) {
if (mCurrentSession != null) {
if (DEBUG) {
- Slog.d(TAG, "Vibration " + vibrationId + " on vibrator " + vibratorId
- + " complete, notifying thread");
+ Slog.d(TAG, "Vibration " + vibrationId + " step " + stepId
+ + " on vibrator " + vibratorId + " complete, notifying thread");
}
- mCurrentSession.notifyVibratorCallback(vibratorId, vibrationId);
+ mCurrentSession.notifyVibratorCallback(vibratorId, vibrationId, stepId);
}
}
}
@@ -2100,10 +2100,10 @@ public class VibratorManagerService extends IVibratorManagerService.Stub {
}
@Override
- public void onComplete(int vibratorId, long vibrationId) {
+ public void onComplete(int vibratorId, long vibrationId, long stepId) {
VibratorManagerService service = mServiceRef.get();
if (service != null) {
- service.onVibrationComplete(vibratorId, vibrationId);
+ service.onVibrationComplete(vibratorId, vibrationId, stepId);
}
}
}
diff --git a/services/core/java/com/android/server/wm/ActivityRecord.java b/services/core/java/com/android/server/wm/ActivityRecord.java
index 9692b69b1256..7b6d408fbe2c 100644
--- a/services/core/java/com/android/server/wm/ActivityRecord.java
+++ b/services/core/java/com/android/server/wm/ActivityRecord.java
@@ -109,7 +109,6 @@ import static android.view.WindowManager.LayoutParams.TYPE_APPLICATION_STARTING;
import static android.view.WindowManager.LayoutParams.TYPE_BASE_APPLICATION;
import static android.view.WindowManager.PROPERTY_ACTIVITY_EMBEDDING_SPLITS_ENABLED;
import static android.view.WindowManager.PROPERTY_ALLOW_UNTRUSTED_ACTIVITY_EMBEDDING_STATE_SHARING;
-import static android.view.WindowManager.TRANSIT_CLOSE;
import static android.view.WindowManager.TRANSIT_OLD_UNSET;
import static android.view.WindowManager.TRANSIT_RELAUNCH;
import static android.view.WindowManager.hasWindowExtensionsEnabled;
@@ -321,9 +320,7 @@ import android.util.MergedConfiguration;
import android.util.Slog;
import android.util.TimeUtils;
import android.util.proto.ProtoOutputStream;
-import android.view.AppTransitionAnimationSpec;
import android.view.DisplayInfo;
-import android.view.IAppTransitionAnimationSpecsFuture;
import android.view.InputApplicationHandle;
import android.view.RemoteAnimationAdapter;
import android.view.RemoteAnimationDefinition;
@@ -493,6 +490,7 @@ final class ActivityRecord extends WindowToken {
private long createTime = System.currentTimeMillis();
long lastVisibleTime; // last time this activity became visible
long pauseTime; // last time we started pausing the activity
+ long mStoppedTime; // last time we completely stopped the activity
long launchTickTime; // base time for launch tick messages
long topResumedStateLossTime; // last time we reported top resumed state loss to an activity
// Last configuration reported to the activity in the client process.
@@ -2348,7 +2346,8 @@ final class ActivityRecord extends WindowToken {
// The snapshot of home is only used once because it won't be updated while screen
// is on (see {@link TaskSnapshotController#screenTurningOff}).
mWmService.mTaskSnapshotController.removeSnapshotCache(task.mTaskId);
- if ((mDisplayContent.mAppTransition.getTransitFlags()
+ final Transition transition = mTransitionController.getCollectingTransition();
+ if (transition != null && (transition.getFlags()
& WindowManager.TRANSIT_FLAG_KEYGUARD_GOING_AWAY_NO_ANIMATION) == 0) {
// Only use snapshot of home as starting window when unlocking directly.
return false;
@@ -3637,7 +3636,6 @@ final class ActivityRecord extends WindowToken {
if (DEBUG_VISIBILITY || DEBUG_TRANSITION) {
Slog.v(TAG_TRANSITION, "Prepare close transition: finishing " + this);
}
- mDisplayContent.prepareAppTransition(TRANSIT_CLOSE);
// When finishing the activity preemptively take the snapshot before the app window
// is marked as hidden and any configuration changes take place
@@ -3739,7 +3737,6 @@ final class ActivityRecord extends WindowToken {
private void prepareActivityHideTransitionAnimation() {
final DisplayContent dc = mDisplayContent;
- dc.prepareAppTransition(TRANSIT_CLOSE);
setVisibility(false);
dc.executeAppTransition();
}
@@ -4391,13 +4388,6 @@ final class ActivityRecord extends WindowToken {
removeStartingWindow();
}
- // If app transition animation was running for this activity, then we need to ensure that
- // the app transition notifies that animations have completed in
- // DisplayContent.handleAnimatingStoppedAndTransition(), so add to that list now
- if (isAnimating(TRANSITION | PARENTS, ANIMATION_TYPE_APP_TRANSITION)) {
- getDisplayContent().mNoAnimationNotifyOnTransitionFinished.add(token);
- }
-
if (delayed && !isEmpty()) {
// set the token aside because it has an active animation to be finished
ProtoLog.v(WM_DEBUG_ADD_REMOVE,
@@ -5069,8 +5059,6 @@ final class ActivityRecord extends WindowToken {
void applyOptionsAnimation() {
if (DEBUG_TRANSITION) Slog.i(TAG, "Applying options for " + this);
if (mPendingRemoteAnimation != null) {
- mDisplayContent.mAppTransition.overridePendingAppTransitionRemote(
- mPendingRemoteAnimation);
mTransitionController.setStatusBarTransitionDelay(
mPendingRemoteAnimation.getStatusBarTransitionDelay());
} else {
@@ -5100,14 +5088,6 @@ final class ActivityRecord extends WindowToken {
IRemoteCallback finishCallback = null;
switch (animationType) {
case ANIM_CUSTOM:
- displayContent.mAppTransition.overridePendingAppTransition(
- pendingOptions.getPackageName(),
- pendingOptions.getCustomEnterResId(),
- pendingOptions.getCustomExitResId(),
- pendingOptions.getCustomBackgroundColor(),
- pendingOptions.getAnimationStartedListener(),
- pendingOptions.getAnimationFinishedListener(),
- pendingOptions.getOverrideTaskTransition());
options = AnimationOptions.makeCustomAnimOptions(pendingOptions.getPackageName(),
pendingOptions.getCustomEnterResId(), pendingOptions.getCustomExitResId(),
pendingOptions.getCustomBackgroundColor(),
@@ -5116,9 +5096,6 @@ final class ActivityRecord extends WindowToken {
finishCallback = pendingOptions.getAnimationFinishedListener();
break;
case ANIM_CLIP_REVEAL:
- displayContent.mAppTransition.overridePendingAppTransitionClipReveal(
- pendingOptions.getStartX(), pendingOptions.getStartY(),
- pendingOptions.getWidth(), pendingOptions.getHeight());
options = AnimationOptions.makeClipRevealAnimOptions(
pendingOptions.getStartX(), pendingOptions.getStartY(),
pendingOptions.getWidth(), pendingOptions.getHeight());
@@ -5130,9 +5107,6 @@ final class ActivityRecord extends WindowToken {
}
break;
case ANIM_SCALE_UP:
- displayContent.mAppTransition.overridePendingAppTransitionScaleUp(
- pendingOptions.getStartX(), pendingOptions.getStartY(),
- pendingOptions.getWidth(), pendingOptions.getHeight());
options = AnimationOptions.makeScaleUpAnimOptions(
pendingOptions.getStartX(), pendingOptions.getStartY(),
pendingOptions.getWidth(), pendingOptions.getHeight(),
@@ -5148,10 +5122,6 @@ final class ActivityRecord extends WindowToken {
case ANIM_THUMBNAIL_SCALE_DOWN:
final boolean scaleUp = (animationType == ANIM_THUMBNAIL_SCALE_UP);
final HardwareBuffer buffer = pendingOptions.getThumbnail();
- displayContent.mAppTransition.overridePendingAppTransitionThumb(buffer,
- pendingOptions.getStartX(), pendingOptions.getStartY(),
- pendingOptions.getAnimationStartedListener(),
- scaleUp);
options = AnimationOptions.makeThumbnailAnimOptions(buffer,
pendingOptions.getStartX(), pendingOptions.getStartY(), scaleUp);
startCallback = pendingOptions.getAnimationStartedListener();
@@ -5164,36 +5134,9 @@ final class ActivityRecord extends WindowToken {
break;
case ANIM_THUMBNAIL_ASPECT_SCALE_UP:
case ANIM_THUMBNAIL_ASPECT_SCALE_DOWN:
- final AppTransitionAnimationSpec[] specs = pendingOptions.getAnimSpecs();
- final IAppTransitionAnimationSpecsFuture specsFuture =
- pendingOptions.getSpecsFuture();
- if (specsFuture != null) {
- displayContent.mAppTransition.overridePendingAppTransitionMultiThumbFuture(
- specsFuture, pendingOptions.getAnimationStartedListener(),
- animationType == ANIM_THUMBNAIL_ASPECT_SCALE_UP);
- } else if (animationType == ANIM_THUMBNAIL_ASPECT_SCALE_DOWN
- && specs != null) {
- displayContent.mAppTransition.overridePendingAppTransitionMultiThumb(
- specs, pendingOptions.getAnimationStartedListener(),
- pendingOptions.getAnimationFinishedListener(), false);
- } else {
- displayContent.mAppTransition.overridePendingAppTransitionAspectScaledThumb(
- pendingOptions.getThumbnail(),
- pendingOptions.getStartX(), pendingOptions.getStartY(),
- pendingOptions.getWidth(), pendingOptions.getHeight(),
- pendingOptions.getAnimationStartedListener(),
- (animationType == ANIM_THUMBNAIL_ASPECT_SCALE_UP));
- if (intent.getSourceBounds() == null) {
- intent.setSourceBounds(new Rect(pendingOptions.getStartX(),
- pendingOptions.getStartY(),
- pendingOptions.getStartX() + pendingOptions.getWidth(),
- pendingOptions.getStartY() + pendingOptions.getHeight()));
- }
- }
+ // TODO(b/397847511): remove the related types from ActivityOptions.
break;
case ANIM_OPEN_CROSS_PROFILE_APPS:
- displayContent.mAppTransition
- .overridePendingAppTransitionStartCrossProfileApps();
options = AnimationOptions.makeCrossProfileAnimOptions();
break;
case ANIM_NONE:
@@ -5450,8 +5393,6 @@ final class ActivityRecord extends WindowToken {
}
private void setVisibility(boolean visible, boolean deferHidingClient) {
- final AppTransition appTransition = getDisplayContent().mAppTransition;
-
// Don't set visibility to false if we were already not visible. This prevents WM from
// adding the app to the closing app list which doesn't make sense for something that is
// already not visible. However, set visibility to true even if we are already visible.
@@ -5471,8 +5412,8 @@ final class ActivityRecord extends WindowToken {
}
ProtoLog.v(WM_DEBUG_APP_TRANSITIONS,
- "setAppVisibility(%s, visible=%b): %s visible=%b mVisibleRequested=%b Callers=%s",
- token, visible, appTransition, isVisible(), mVisibleRequested,
+ "setAppVisibility(%s, visible=%b): visible=%b mVisibleRequested=%b Callers=%s",
+ token, visible, isVisible(), mVisibleRequested,
Debug.getCallers(6));
// Before setting mVisibleRequested so we can track changes.
@@ -5569,15 +5510,6 @@ final class ActivityRecord extends WindowToken {
updateReportedVisibilityLocked();
}
- @Override
- boolean applyAnimation(LayoutParams lp, @TransitionOldType int transit, boolean enter,
- boolean isVoiceInteraction, @Nullable ArrayList<WindowContainer> sources) {
- if ((mTransitionChangeFlags & FLAG_STARTING_WINDOW_TRANSFER_RECIPIENT) != 0) {
- return false;
- }
- return super.applyAnimation(lp, transit, enter, isVoiceInteraction, sources);
- }
-
/**
* Update visibility to this {@link ActivityRecord}.
*
@@ -6447,6 +6379,7 @@ final class ActivityRecord extends WindowToken {
Slog.w(TAG, "Exception thrown during pause", e);
// Just in case, assume it to be stopped.
mAppStopped = true;
+ mStoppedTime = SystemClock.uptimeMillis();
ProtoLog.v(WM_DEBUG_STATES, "Stop failed; moving to STOPPED: %s", this);
setState(STOPPED, "stopIfPossible");
}
@@ -6480,6 +6413,7 @@ final class ActivityRecord extends WindowToken {
if (isStopping) {
ProtoLog.v(WM_DEBUG_STATES, "Moving to STOPPED: %s (stop complete)", this);
+ mStoppedTime = SystemClock.uptimeMillis();
setState(STOPPED, "activityStopped");
}
@@ -6639,9 +6573,7 @@ final class ActivityRecord extends WindowToken {
// starting window is drawn, the transition can start earlier. Exclude finishing and bubble
// because it may be a trampoline.
if (app == null && !finishing && !mLaunchedFromBubble
- && mVisibleRequested && !mDisplayContent.mAppTransition.isReady()
- && !mDisplayContent.mAppTransition.isRunning()
- && mDisplayContent.isNextTransitionForward()) {
+ && mVisibleRequested && mDisplayContent.isNextTransitionForward()) {
// The pending transition state will be cleared after the transition is started, so
// save the state for launching the client later (used by LaunchActivityItem).
mStartingData.mIsTransitionForward = true;
@@ -7523,7 +7455,6 @@ final class ActivityRecord extends WindowToken {
}
}
- getDisplayContent().mAppTransition.notifyAppTransitionFinishedLocked(token);
scheduleAnimation();
// Schedule to handle the stopping and finishing activities which the animation is done
diff --git a/services/core/java/com/android/server/wm/ActivityTaskManagerService.java b/services/core/java/com/android/server/wm/ActivityTaskManagerService.java
index 37783781a901..6f83822ee97a 100644
--- a/services/core/java/com/android/server/wm/ActivityTaskManagerService.java
+++ b/services/core/java/com/android/server/wm/ActivityTaskManagerService.java
@@ -2130,6 +2130,26 @@ public class ActivityTaskManagerService extends IActivityTaskManager.Stub {
}
@Override
+ public boolean setTaskIsPerceptible(int taskId, boolean isPerceptible) {
+ enforceTaskPermission("setTaskIsPerceptible()");
+ final long ident = Binder.clearCallingIdentity();
+ try {
+ synchronized (mGlobalLock) {
+ final Task task = mRootWindowContainer.anyTaskForId(taskId,
+ MATCH_ATTACHED_TASK_ONLY);
+ if (task == null) {
+ Slog.w(TAG, "setTaskIsPerceptible: No task to set with id=" + taskId);
+ return false;
+ }
+ task.mIsPerceptible = isPerceptible;
+ }
+ return true;
+ } finally {
+ Binder.restoreCallingIdentity(ident);
+ }
+ }
+
+ @Override
public boolean removeTask(int taskId) {
mAmInternal.enforceCallingPermission(REMOVE_TASKS, "removeTask()");
synchronized (mGlobalLock) {
diff --git a/services/core/java/com/android/server/wm/AppCompatAspectRatioPolicy.java b/services/core/java/com/android/server/wm/AppCompatAspectRatioPolicy.java
index 15c0789d777e..3535a96d9c45 100644
--- a/services/core/java/com/android/server/wm/AppCompatAspectRatioPolicy.java
+++ b/services/core/java/com/android/server/wm/AppCompatAspectRatioPolicy.java
@@ -36,8 +36,7 @@ import android.app.WindowConfiguration;
import android.content.pm.ActivityInfo;
import android.content.res.Configuration;
import android.graphics.Rect;
-
-import com.android.window.flags.Flags;
+import android.window.DesktopModeFlags;
/**
* Encapsulate app compat policy logic related to aspect ratio.
@@ -298,7 +297,8 @@ class AppCompatAspectRatioPolicy {
// Camera compat mode is an exception to this, where the activity is letterboxed
// to an aspect ratio commonly found on phones, e.g. 16:9, to avoid issues like
// stretching of the camera preview.
- || (Flags.ignoreAspectRatioRestrictionsForResizeableFreeformActivities()
+ || (DesktopModeFlags
+ .IGNORE_ASPECT_RATIO_RESTRICTIONS_FOR_RESIZEABLE_FREEFORM_ACTIVITIES.isTrue()
&& task.getWindowingMode() == WINDOWING_MODE_FREEFORM
&& !mActivityRecord.shouldCreateAppCompatDisplayInsets()
&& !AppCompatCameraPolicy.shouldCameraCompatControlAspectRatio(
diff --git a/services/core/java/com/android/server/wm/DisplayArea.java b/services/core/java/com/android/server/wm/DisplayArea.java
index b932ef362aca..6718ae435cd9 100644
--- a/services/core/java/com/android/server/wm/DisplayArea.java
+++ b/services/core/java/com/android/server/wm/DisplayArea.java
@@ -748,14 +748,9 @@ public class DisplayArea<T extends WindowContainer> extends WindowContainer<T> {
&& policy.okToAnimate(true /* ignoreScreenOn */)) {
return false;
}
- // Consider unoccluding only when all unknown visibilities have been
- // resolved, as otherwise we just may be starting another occluding activity.
- final boolean isUnoccluding =
- mDisplayContent.mAppTransition.isUnoccluding()
- && mDisplayContent.mUnknownAppVisibilityController.allResolved();
- // If keyguard is showing, or we're unoccluding, force the keyguard's orientation,
+ // Use keyguard's orientation if it is showing and not occluded
// even if SystemUI hasn't updated the attrs yet.
- if (policy.isKeyguardShowingAndNotOccluded() || isUnoccluding) {
+ if (policy.isKeyguardShowingAndNotOccluded()) {
return true;
}
}
diff --git a/services/core/java/com/android/server/wm/DisplayContent.java b/services/core/java/com/android/server/wm/DisplayContent.java
index e30c24d87d20..214a3a15bb44 100644
--- a/services/core/java/com/android/server/wm/DisplayContent.java
+++ b/services/core/java/com/android/server/wm/DisplayContent.java
@@ -88,7 +88,6 @@ import static android.view.inputmethod.ImeTracker.DEBUG_IME_VISIBILITY;
import static android.window.DisplayAreaOrganizer.FEATURE_IME;
import static android.window.DisplayAreaOrganizer.FEATURE_ROOT;
-import static com.android.internal.protolog.WmProtoLogGroups.WM_DEBUG_APP_TRANSITIONS;
import static com.android.internal.protolog.WmProtoLogGroups.WM_DEBUG_BOOT;
import static com.android.internal.protolog.WmProtoLogGroups.WM_DEBUG_CONTENT_RECORDING;
import static com.android.internal.protolog.WmProtoLogGroups.WM_DEBUG_FOCUS;
@@ -369,12 +368,6 @@ class DisplayContent extends RootDisplayArea implements WindowManagerPolicy.Disp
private MetricsLogger mMetricsLogger;
- /**
- * List of clients without a transtiton animation that we notify once we are done
- * transitioning since they won't be notified through the app window animator.
- */
- final List<IBinder> mNoAnimationNotifyOnTransitionFinished = new ArrayList<>();
-
// Mapping from a token IBinder to a WindowToken object on this display.
private final HashMap<IBinder, WindowToken> mTokenMap = new HashMap();
@@ -829,28 +822,36 @@ class DisplayContent extends RootDisplayArea implements WindowManagerPolicy.Disp
*/
private final ToBooleanFunction<WindowState> mFindFocusedWindow = w -> {
final ActivityRecord focusedApp = mFocusedApp;
+ final boolean canReceiveKeys = w.canReceiveKeys();
ProtoLog.v(WM_DEBUG_FOCUS, "Looking for focus: %s, flags=%d, canReceive=%b, reason=%s",
- w, w.mAttrs.flags, w.canReceiveKeys(),
+ w, w.mAttrs.flags, canReceiveKeys,
w.canReceiveKeysReason(false /* fromUserTouch */));
- if (!w.canReceiveKeys()) {
+ if (!canReceiveKeys) {
return false;
}
- // When switching the app task, we keep the IME window visibility for better
- // transitioning experiences.
- // However, in case IME created a child window or the IME selection dialog without
- // dismissing during the task switching to keep the window focus because IME window has
- // higher window hierarchy, we don't give it focus if the next IME layering target
- // doesn't request IME visible.
- if (w.mIsImWindow && w.isChildWindow() && (mImeLayeringTarget == null
- || !mImeLayeringTarget.isRequestedVisible(ime()))) {
- return false;
- }
- if (w.mAttrs.type == TYPE_INPUT_METHOD_DIALOG && mImeLayeringTarget != null
- && !(mImeLayeringTarget.isRequestedVisible(ime())
- && mImeLayeringTarget.isVisibleRequested())) {
- return false;
+ // IME windows remain visibleRequested while switching apps to maintain a smooth animation.
+ // This persists until the new app is focused, so they can be visibleRequested despite not
+ // being visible to the user (i.e. occluded). These rank higher in the window hierarchy than
+ // app windows, so they will always be considered first. To avoid having the focus stuck,
+ // an IME window (child or not) cannot be focused if the IME parent is not visible. However,
+ // child windows also require the IME to be visible in the current app.
+ if (w.mIsImWindow) {
+ final boolean imeParentVisible = mInputMethodSurfaceParentWindow != null
+ && mInputMethodSurfaceParentWindow.isVisibleRequested();
+ if (!imeParentVisible) {
+ ProtoLog.v(WM_DEBUG_FOCUS, "findFocusedWindow: IME window not focusable as"
+ + " IME parent is not visible");
+ return false;
+ }
+
+ if (w.isChildWindow()
+ && !getInsetsStateController().getImeSourceProvider().isImeShowing()) {
+ ProtoLog.v(WM_DEBUG_FOCUS, "findFocusedWindow: IME child window not focusable as"
+ + " IME is not visible");
+ return false;
+ }
}
final ActivityRecord activity = w.mActivityRecord;
@@ -1171,8 +1172,6 @@ class DisplayContent extends RootDisplayArea implements WindowManagerPolicy.Disp
mFixedRotationTransitionListener = new FixedRotationTransitionListener(mDisplayId);
mAppTransition = new AppTransition(mWmService.mContext, mWmService, this);
- mAppTransition.registerListenerLocked(mWmService.mActivityManagerAppTransitionNotifier);
- mAppTransition.registerListenerLocked(mFixedRotationTransitionListener);
mTransitionController.registerLegacyListener(mFixedRotationTransitionListener);
mUnknownAppVisibilityController = new UnknownAppVisibilityController(mWmService, this);
mRemoteDisplayChangeController = new RemoteDisplayChangeController(this);
@@ -2858,13 +2857,6 @@ class DisplayContent extends RootDisplayArea implements WindowManagerPolicy.Disp
return isVisible() && !mRemoved && !mRemoving;
}
- @Override
- void onAppTransitionDone() {
- super.onAppTransitionDone();
- mWmService.mWindowsChanged = true;
- onTransitionFinished();
- }
-
void onTransitionFinished() {
// If the transition finished callback cannot match the token for some reason, make sure the
// rotated state is cleared if it is already invisible.
@@ -3385,9 +3377,7 @@ class DisplayContent extends RootDisplayArea implements WindowManagerPolicy.Disp
mDeferredRemoval = false;
try {
mUnknownAppVisibilityController.clear();
- mAppTransition.removeAppTransitionTimeoutCallbacks();
mTransitionController.unregisterLegacyListener(mFixedRotationTransitionListener);
- handleAnimatingStoppedAndTransition();
mDeviceStateController.unregisterDeviceStateCallback(mDeviceStateConsumer);
super.removeImmediately();
if (DEBUG_DISPLAY) Slog.v(TAG_WM, "Removing display=" + this);
@@ -3570,11 +3560,7 @@ class DisplayContent extends RootDisplayArea implements WindowManagerPolicy.Disp
mDisplayRotation.dumpDebug(proto, DISPLAY_ROTATION);
mDisplayFrames.dumpDebug(proto, DISPLAY_FRAMES);
proto.write(MIN_SIZE_OF_RESIZEABLE_TASK_DP, mMinSizeOfResizeableTaskDp);
- if (mTransitionController.isShellTransitionsEnabled()) {
- mTransitionController.dumpDebugLegacy(proto, APP_TRANSITION);
- } else {
- mAppTransition.dumpDebug(proto, APP_TRANSITION);
- }
+ mTransitionController.dumpDebugLegacy(proto, APP_TRANSITION);
if (mFocusedApp != null) {
mFocusedApp.writeNameToProto(proto, FOCUSED_APP);
}
@@ -5634,61 +5620,20 @@ class DisplayContent extends RootDisplayArea implements WindowManagerPolicy.Disp
* @see AppTransition#prepareAppTransition
*/
void requestTransitionAndLegacyPrepare(@WindowManager.TransitionType int transit,
- @WindowManager.TransitionFlags int flags) {
- prepareAppTransition(transit, flags);
- mTransitionController.requestTransitionIfNeeded(transit, flags, null /* trigger */, this);
+ @WindowManager.TransitionFlags int flags, @Nullable WindowContainer trigger) {
+ mTransitionController.requestTransitionIfNeeded(transit, flags, trigger, this);
}
void executeAppTransition() {
mTransitionController.setReady(this);
- if (mAppTransition.isTransitionSet()) {
- ProtoLog.w(WM_DEBUG_APP_TRANSITIONS,
- "Execute app transition: %s, displayId: %d Callers=%s",
- mAppTransition, mDisplayId, Debug.getCallers(5));
- mAppTransition.setReady();
- mWmService.mWindowPlacerLocked.requestTraversal();
- }
- }
-
- /**
- * Update pendingLayoutChanges after app transition has finished.
- */
- void handleAnimatingStoppedAndTransition() {
- int changes = 0;
-
- mAppTransition.setIdle();
-
- for (int i = mNoAnimationNotifyOnTransitionFinished.size() - 1; i >= 0; i--) {
- final IBinder token = mNoAnimationNotifyOnTransitionFinished.get(i);
- mAppTransition.notifyAppTransitionFinishedLocked(token);
- }
- mNoAnimationNotifyOnTransitionFinished.clear();
-
- mWallpaperController.hideDeferredWallpapersIfNeededLegacy();
-
- onAppTransitionDone();
-
- changes |= FINISH_LAYOUT_REDO_LAYOUT;
- ProtoLog.v(WM_DEBUG_WALLPAPER, "Wallpaper layer changed: assigning layers + relayout");
- computeImeTarget(true /* updateImeTarget */);
- mWallpaperMayChange = true;
- // Since the window list has been rebuilt, focus might have to be recomputed since the
- // actual order of windows might have changed again.
- mWmService.mFocusMayChange = true;
-
- pendingLayoutChanges |= changes;
}
/** Check if pending app transition is for activity / task launch. */
boolean isNextTransitionForward() {
// TODO(b/191375840): decouple "forwardness" from transition system.
- if (mTransitionController.isShellTransitionsEnabled()) {
- @WindowManager.TransitionType int type =
- mTransitionController.getCollectingTransitionType();
- return type == TRANSIT_OPEN || type == TRANSIT_TO_FRONT;
- }
- return mAppTransition.containsTransitRequest(TRANSIT_OPEN)
- || mAppTransition.containsTransitRequest(TRANSIT_TO_FRONT);
+ final @WindowManager.TransitionType int type =
+ mTransitionController.getCollectingTransitionType();
+ return type == TRANSIT_OPEN || type == TRANSIT_TO_FRONT;
}
/**
diff --git a/services/core/java/com/android/server/wm/DisplayPolicy.java b/services/core/java/com/android/server/wm/DisplayPolicy.java
index fbe850198c50..7aa2101f516c 100644
--- a/services/core/java/com/android/server/wm/DisplayPolicy.java
+++ b/services/core/java/com/android/server/wm/DisplayPolicy.java
@@ -631,7 +631,6 @@ public class DisplayPolicy {
mHandler.post(mAppTransitionFinished);
}
};
- displayContent.mAppTransition.registerListenerLocked(mAppTransitionListener);
displayContent.mTransitionController.registerLegacyListener(mAppTransitionListener);
// TODO: Make it can take screenshot on external display
diff --git a/services/core/java/com/android/server/wm/KeyguardController.java b/services/core/java/com/android/server/wm/KeyguardController.java
index 6091b8334438..6d73739e5046 100644
--- a/services/core/java/com/android/server/wm/KeyguardController.java
+++ b/services/core/java/com/android/server/wm/KeyguardController.java
@@ -250,7 +250,7 @@ class KeyguardController {
// to the locked state before holding the sleep token again
if (!ENABLE_NEW_KEYGUARD_SHELL_TRANSITIONS) {
dc.requestTransitionAndLegacyPrepare(
- TRANSIT_TO_FRONT, TRANSIT_FLAG_KEYGUARD_APPEARING);
+ TRANSIT_TO_FRONT, TRANSIT_FLAG_KEYGUARD_APPEARING, /* trigger= */ null);
}
dc.mWallpaperController.adjustWallpaperWindows();
dc.executeAppTransition();
diff --git a/services/core/java/com/android/server/wm/RootWindowContainer.java b/services/core/java/com/android/server/wm/RootWindowContainer.java
index 40f16c187f20..f309372ab6a2 100644
--- a/services/core/java/com/android/server/wm/RootWindowContainer.java
+++ b/services/core/java/com/android/server/wm/RootWindowContainer.java
@@ -2103,10 +2103,6 @@ class RootWindowContainer extends WindowContainer<DisplayContent>
removeRootTasksInWindowingModes(WINDOWING_MODE_PINNED);
}
- // Set a transition to ensure that we don't immediately try and update the visibility
- // of the activity entering PIP
- r.getDisplayContent().prepareAppTransition(TRANSIT_NONE);
-
transitionController.collect(task);
// Defer the windowing mode change until after the transition to prevent the activity
diff --git a/services/core/java/com/android/server/wm/Task.java b/services/core/java/com/android/server/wm/Task.java
index 6b3499a5d68c..6cd1336122be 100644
--- a/services/core/java/com/android/server/wm/Task.java
+++ b/services/core/java/com/android/server/wm/Task.java
@@ -54,7 +54,6 @@ import static android.view.WindowManager.LayoutParams.TYPE_BASE_APPLICATION;
import static android.view.WindowManager.PROPERTY_COMPAT_ALLOW_RESIZEABLE_ACTIVITY_OVERRIDES;
import static android.view.WindowManager.TRANSIT_CLOSE;
import static android.view.WindowManager.TRANSIT_FLAG_APP_CRASHED;
-import static android.view.WindowManager.TRANSIT_NONE;
import static android.view.WindowManager.TRANSIT_OPEN;
import static android.view.WindowManager.TRANSIT_TO_BACK;
import static android.view.WindowManager.TRANSIT_TO_FRONT;
@@ -504,6 +503,17 @@ class Task extends TaskFragment {
int mOffsetYForInsets;
/**
+ * When set to true, the task will be kept at a PERCEPTIBLE_APP_ADJ, and downgraded
+ * to PREVIOUS_APP_ADJ if not in foreground for a period of time.
+ * One example use case is for desktop form factors, where it is important keep tasks in the
+ * perceptible state (rather than cached where it may be frozen) when a user moves it to the
+ * foreground.
+ * On startup, restored Tasks will not be perceptible, until user actually interacts with it
+ * (i.e. brings it to the foreground)
+ */
+ boolean mIsPerceptible = false;
+
+ /**
* Whether the compatibility overrides that change the resizability of the app should be allowed
* for the specific app.
*/
@@ -1647,8 +1657,7 @@ class Task extends TaskFragment {
// Prevent the transition from being executed too early if the top activity is
// resumed but the mVisibleRequested of any other activity is true, the transition
// should wait until next activity resumed.
- if (r.isState(RESUMED) || (r.isVisible()
- && !mDisplayContent.mAppTransition.containsTransitRequest(TRANSIT_CLOSE))) {
+ if (r.isState(RESUMED) || r.isVisible()) {
r.finishIfPossible(reason, false /* oomAdj */);
} else {
r.destroyIfPossible(reason);
@@ -2324,11 +2333,6 @@ class Task extends TaskFragment {
mLastSurfaceSize.set(width, height);
}
- @VisibleForTesting
- boolean isInChangeTransition() {
- return AppTransition.isChangeTransitOld(mTransit);
- }
-
@Override
void writeIdentifierToProto(ProtoOutputStream proto, long fieldId) {
final long token = proto.start(fieldId);
@@ -3854,6 +3858,7 @@ class Task extends TaskFragment {
pw.print(ActivityInfo.resizeModeToString(mResizeMode));
pw.print(" mSupportsPictureInPicture="); pw.print(mSupportsPictureInPicture);
pw.print(" isResizeable="); pw.println(isResizeable());
+ pw.print(" isPerceptible="); pw.println(mIsPerceptible);
pw.print(prefix); pw.print("lastActiveTime="); pw.print(lastActiveTime);
pw.println(" (inactive for " + (getInactiveDuration() / 1000) + "s)");
pw.print(prefix); pw.println(" isTrimmable=" + mIsTrimmableFromRecents);
@@ -5293,11 +5298,9 @@ class Task extends TaskFragment {
// Place a new activity at top of root task, so it is next to interact with the user.
if ((r.intent.getFlags() & Intent.FLAG_ACTIVITY_NO_ANIMATION) != 0) {
- mDisplayContent.prepareAppTransition(TRANSIT_NONE);
mTaskSupervisor.mNoAnimActivities.add(r);
mTransitionController.setNoAnimation(r);
} else {
- mDisplayContent.prepareAppTransition(TRANSIT_OPEN);
mTaskSupervisor.mNoAnimActivities.remove(r);
}
if (newTask && !r.mLaunchTaskBehind) {
@@ -5477,7 +5480,8 @@ class Task extends TaskFragment {
Slog.w(TAG, " Force finishing activity "
+ r.intent.getComponent().flattenToShortString());
Task finishedTask = r.getTask();
- mDisplayContent.requestTransitionAndLegacyPrepare(TRANSIT_CLOSE, TRANSIT_FLAG_APP_CRASHED);
+ mDisplayContent.requestTransitionAndLegacyPrepare(TRANSIT_CLOSE, TRANSIT_FLAG_APP_CRASHED,
+ finishedTask);
r.finishIfPossible(reason, false /* oomAdj */);
// Also terminate any activities below it that aren't yet stopped, to avoid a situation
@@ -5695,7 +5699,6 @@ class Task extends TaskFragment {
ActivityOptions.abort(options);
}
}
- mDisplayContent.prepareAppTransition(transit);
}
final void moveTaskToFront(Task tr, boolean noAnimation, ActivityOptions options,
@@ -5747,12 +5750,9 @@ class Task extends TaskFragment {
if (DEBUG_TRANSITION) Slog.v(TAG_TRANSITION, "Prepare to front transition: task=" + tr);
if (noAnimation) {
- mDisplayContent.prepareAppTransition(TRANSIT_NONE);
mTaskSupervisor.mNoAnimActivities.add(top);
- if (mTransitionController.isShellTransitionsEnabled()) {
- mTransitionController.collect(top);
- mTransitionController.setNoAnimation(top);
- }
+ mTransitionController.collect(top);
+ mTransitionController.setNoAnimation(top);
ActivityOptions.abort(options);
} else {
updateTransitLocked(TRANSIT_TO_FRONT, options);
@@ -5862,10 +5862,6 @@ class Task extends TaskFragment {
moveTaskToBackInner(tr, transition);
});
} else {
- // Skip the transition for pinned task.
- if (!inPinnedWindowingMode()) {
- mDisplayContent.prepareAppTransition(TRANSIT_TO_BACK);
- }
moveTaskToBackInner(tr, null /* transition */);
}
return true;
diff --git a/services/core/java/com/android/server/wm/TaskFragment.java b/services/core/java/com/android/server/wm/TaskFragment.java
index bbda849262b2..80095b395cc4 100644
--- a/services/core/java/com/android/server/wm/TaskFragment.java
+++ b/services/core/java/com/android/server/wm/TaskFragment.java
@@ -44,10 +44,6 @@ import static android.view.Display.INVALID_DISPLAY;
import static android.view.Surface.ROTATION_270;
import static android.view.Surface.ROTATION_90;
import static android.view.WindowManager.LayoutParams.FLAG_DIM_BEHIND;
-import static android.view.WindowManager.TRANSIT_CLOSE;
-import static android.view.WindowManager.TRANSIT_FLAG_OPEN_BEHIND;
-import static android.view.WindowManager.TRANSIT_NONE;
-import static android.view.WindowManager.TRANSIT_OPEN;
import static com.android.internal.protolog.WmProtoLogGroups.WM_DEBUG_STATES;
import static com.android.server.wm.ActivityRecord.State.PAUSED;
@@ -56,7 +52,6 @@ import static com.android.server.wm.ActivityRecord.State.RESUMED;
import static com.android.server.wm.ActivityRecord.State.STOPPING;
import static com.android.server.wm.ActivityTaskManagerDebugConfig.DEBUG_RESULTS;
import static com.android.server.wm.ActivityTaskManagerDebugConfig.DEBUG_SWITCH;
-import static com.android.server.wm.ActivityTaskManagerDebugConfig.DEBUG_TRANSITION;
import static com.android.server.wm.ActivityTaskManagerDebugConfig.POSTFIX_RESULTS;
import static com.android.server.wm.ActivityTaskManagerDebugConfig.POSTFIX_SWITCH;
import static com.android.server.wm.ActivityTaskManagerDebugConfig.POSTFIX_TRANSITION;
@@ -1682,36 +1677,15 @@ class TaskFragment extends WindowContainer<WindowContainer> {
final DisplayContent dc = taskDisplayArea.mDisplayContent;
if (prev != null) {
if (prev.finishing) {
- if (DEBUG_TRANSITION) {
- Slog.v(TAG_TRANSITION, "Prepare close transition: prev=" + prev);
- }
if (mTaskSupervisor.mNoAnimActivities.contains(prev)) {
anim = false;
- dc.prepareAppTransition(TRANSIT_NONE);
- } else {
- dc.prepareAppTransition(TRANSIT_CLOSE);
}
prev.setVisibility(false);
- } else {
- if (DEBUG_TRANSITION) {
- Slog.v(TAG_TRANSITION, "Prepare open transition: prev=" + prev);
- }
- if (mTaskSupervisor.mNoAnimActivities.contains(next)) {
- anim = false;
- dc.prepareAppTransition(TRANSIT_NONE);
- } else {
- dc.prepareAppTransition(TRANSIT_OPEN,
- next.mLaunchTaskBehind ? TRANSIT_FLAG_OPEN_BEHIND : 0);
- }
- }
- } else {
- if (DEBUG_TRANSITION) Slog.v(TAG_TRANSITION, "Prepare open transition: no previous");
- if (mTaskSupervisor.mNoAnimActivities.contains(next)) {
+ } else if (mTaskSupervisor.mNoAnimActivities.contains(next)) {
anim = false;
- dc.prepareAppTransition(TRANSIT_NONE);
- } else {
- dc.prepareAppTransition(TRANSIT_OPEN);
}
+ } else if (mTaskSupervisor.mNoAnimActivities.contains(next)) {
+ anim = false;
}
if (anim) {
@@ -1915,10 +1889,12 @@ class TaskFragment extends WindowContainer<WindowContainer> {
// Even if the transient activity is occluded, defer pausing (addToStopping will still
// be called) it until the transient transition is done. So the current resuming
// activity won't need to wait for additional pause complete.
+ ProtoLog.d(WM_DEBUG_STATES, "startPausing: Skipping pause for transient "
+ + "resumed activity=%s", mResumedActivity);
return false;
}
- ProtoLog.d(WM_DEBUG_STATES, "startPausing: taskFrag =%s " + "mResumedActivity=%s", this,
+ ProtoLog.d(WM_DEBUG_STATES, "startPausing: taskFrag=%s mResumedActivity=%s", this,
mResumedActivity);
if (mPausingActivity != null) {
diff --git a/services/core/java/com/android/server/wm/Transition.java b/services/core/java/com/android/server/wm/Transition.java
index 5217a759c6ae..fd7d96a646ae 100644
--- a/services/core/java/com/android/server/wm/Transition.java
+++ b/services/core/java/com/android/server/wm/Transition.java
@@ -1640,6 +1640,12 @@ class Transition implements BLASTSyncEngine.TransactionReadyListener {
mSyncEngine.setSyncMethod(syncId, BLASTSyncEngine.METHOD_BLAST);
}
mSyncEngine.addToSyncSet(syncId, target);
+ } else {
+ // If there is an existing sync group for the commit-at-end activity,
+ // enforce BLAST sync method for its windows, before resuming config dispatch.
+ target.forAllWindows(windowState -> {
+ windowState.mSyncMethodOverride = BLASTSyncEngine.METHOD_BLAST;
+ }, true /* traverseTopToBottom */);
}
// Reset surface state here (since it was skipped in buildFinishTransaction). Since
// we are resuming config to the "current" state, we have to calculate the matching
@@ -2026,23 +2032,18 @@ class Transition implements BLASTSyncEngine.TransactionReadyListener {
if (mOverrideOptions == null) {
return;
}
-
- if (!Flags.moveAnimationOptionsToChange()) {
- info.setAnimationOptions(mOverrideOptions);
- } else {
- final List<TransitionInfo.Change> changes = info.getChanges();
- for (int i = changes.size() - 1; i >= 0; --i) {
- final WindowContainer<?> container = mTargets.get(i).mContainer;
- if (container.asActivityRecord() != null
- || shouldApplyAnimOptionsToTask(container.asTask())) {
- changes.get(i).setAnimationOptions(mOverrideOptions);
- // TODO(b/295805497): Extract mBackgroundColor from AnimationOptions.
- changes.get(i).setBackgroundColor(mOverrideOptions.getBackgroundColor());
- } else if (shouldApplyAnimOptionsToEmbeddedTf(container.asTaskFragment())) {
- // We only override AnimationOptions because backgroundColor should be from
- // TaskFragmentAnimationParams.
- changes.get(i).setAnimationOptions(mOverrideOptions);
- }
+ final List<TransitionInfo.Change> changes = info.getChanges();
+ for (int i = changes.size() - 1; i >= 0; --i) {
+ final WindowContainer<?> container = mTargets.get(i).mContainer;
+ if (container.asActivityRecord() != null
+ || shouldApplyAnimOptionsToTask(container.asTask())) {
+ changes.get(i).setAnimationOptions(mOverrideOptions);
+ // TODO(b/295805497): Extract mBackgroundColor from AnimationOptions.
+ changes.get(i).setBackgroundColor(mOverrideOptions.getBackgroundColor());
+ } else if (shouldApplyAnimOptionsToEmbeddedTf(container.asTaskFragment())) {
+ // We only override AnimationOptions because backgroundColor should be from
+ // TaskFragmentAnimationParams.
+ changes.get(i).setAnimationOptions(mOverrideOptions);
}
}
updateActivityTargetForCrossProfileAnimation(info);
@@ -2933,9 +2934,6 @@ class Transition implements BLASTSyncEngine.TransactionReadyListener {
final AnimationOptions animOptionsForActivityTransition =
calculateAnimationOptionsForActivityTransition(type, sortedTargets);
- if (!Flags.moveAnimationOptionsToChange() && animOptionsForActivityTransition != null) {
- out.setAnimationOptions(animOptionsForActivityTransition);
- }
final ArraySet<WindowContainer> occludedAtEndContainers = new ArraySet<>();
// Convert all the resolved ChangeInfos into TransactionInfo.Change objects in order.
@@ -3059,28 +3057,26 @@ class Transition implements BLASTSyncEngine.TransactionReadyListener {
}
AnimationOptions animOptions = null;
- if (Flags.moveAnimationOptionsToChange()) {
- if (activityRecord != null && animOptionsForActivityTransition != null) {
- animOptions = animOptionsForActivityTransition;
- } else if (Flags.activityEmbeddingOverlayPresentationFlag()
- && isEmbeddedTaskFragment) {
- final TaskFragmentAnimationParams params = taskFragment.getAnimationParams();
- if (params.hasOverrideAnimation()) {
- // Only set AnimationOptions if there's any animation override.
- // We use separated field for backgroundColor, and
- // AnimationOptions#backgroundColor will be removed in long term.
- animOptions = AnimationOptions.makeCustomAnimOptions(
- taskFragment.getTask().getBasePackageName(),
- params.getOpenAnimationResId(), params.getChangeAnimationResId(),
- params.getCloseAnimationResId(), 0 /* backgroundColor */,
- false /* overrideTaskTransition */);
- animOptions.setUserId(taskFragment.getTask().mUserId);
- }
- }
- if (animOptions != null) {
- change.setAnimationOptions(animOptions);
+ if (activityRecord != null && animOptionsForActivityTransition != null) {
+ animOptions = animOptionsForActivityTransition;
+ } else if (Flags.activityEmbeddingOverlayPresentationFlag()
+ && isEmbeddedTaskFragment) {
+ final TaskFragmentAnimationParams params = taskFragment.getAnimationParams();
+ if (params.hasOverrideAnimation()) {
+ // Only set AnimationOptions if there's any animation override.
+ // We use separated field for backgroundColor, and
+ // AnimationOptions#backgroundColor will be removed in long term.
+ animOptions = AnimationOptions.makeCustomAnimOptions(
+ taskFragment.getTask().getBasePackageName(),
+ params.getOpenAnimationResId(), params.getChangeAnimationResId(),
+ params.getCloseAnimationResId(), 0 /* backgroundColor */,
+ false /* overrideTaskTransition */);
+ animOptions.setUserId(taskFragment.getTask().mUserId);
}
}
+ if (animOptions != null) {
+ change.setAnimationOptions(animOptions);
+ }
if (activityRecord != null) {
change.setActivityComponent(activityRecord.mActivityComponent);
diff --git a/services/core/java/com/android/server/wm/TrustedPresentationListenerController.java b/services/core/java/com/android/server/wm/TrustedPresentationListenerController.java
index 242883612124..e612d8ec0ce6 100644
--- a/services/core/java/com/android/server/wm/TrustedPresentationListenerController.java
+++ b/services/core/java/com/android/server/wm/TrustedPresentationListenerController.java
@@ -20,6 +20,7 @@ import static android.graphics.Matrix.MSCALE_X;
import static android.graphics.Matrix.MSCALE_Y;
import static android.graphics.Matrix.MSKEW_X;
import static android.graphics.Matrix.MSKEW_Y;
+import static android.view.Display.INVALID_DISPLAY;
import static com.android.internal.protolog.WmProtoLogGroups.WM_DEBUG_TPL;
@@ -35,6 +36,7 @@ import android.util.ArrayMap;
import android.util.IntArray;
import android.util.Pair;
import android.util.Size;
+import android.util.SparseArray;
import android.view.InputWindowHandle;
import android.window.ITrustedPresentationListener;
import android.window.TrustedPresentationThresholds;
@@ -251,7 +253,7 @@ public class TrustedPresentationListenerController {
Rect tmpLogicalDisplaySize = new Rect();
Matrix tmpInverseMatrix = new Matrix();
float[] tmpMatrix = new float[9];
- Region coveredRegionsAbove = new Region();
+ SparseArray<Region> coveredRegionsAboveByDisplay = new SparseArray<>();
long currTimeMs = System.currentTimeMillis();
ProtoLog.v(WM_DEBUG_TPL, "Checking %d windows", mLastWindowHandles.first.length);
@@ -262,7 +264,7 @@ public class TrustedPresentationListenerController {
ProtoLog.v(WM_DEBUG_TPL, "Skipping %s", windowHandle.name);
continue;
}
- var displayFound = false;
+ int displayId = INVALID_DISPLAY;
tmpRectF.set(windowHandle.frame);
for (var displayHandle : mLastWindowHandles.second) {
if (displayHandle.mDisplayId == windowHandle.displayId) {
@@ -273,17 +275,18 @@ public class TrustedPresentationListenerController {
tmpLogicalDisplaySize.set(0, 0, displayHandle.mLogicalSize.getWidth(),
displayHandle.mLogicalSize.getHeight());
tmpRect.intersect(tmpLogicalDisplaySize);
- displayFound = true;
+ displayId = displayHandle.mDisplayId;
break;
}
}
- if (!displayFound) {
+ if (displayId == INVALID_DISPLAY) {
ProtoLog.v(WM_DEBUG_TPL, "Skipping %s, no associated display %d", windowHandle.name,
windowHandle.displayId);
continue;
}
+ Region coveredRegionsAbove = coveredRegionsAboveByDisplay.get(displayId, new Region());
var listeners = mRegisteredListeners.get(windowHandle.getWindowToken());
if (listeners != null) {
Region region = new Region();
@@ -304,6 +307,7 @@ public class TrustedPresentationListenerController {
}
coveredRegionsAbove.op(tmpRect, Region.Op.UNION);
+ coveredRegionsAboveByDisplay.put(displayId, coveredRegionsAbove);
ProtoLog.v(WM_DEBUG_TPL, "coveredRegionsAbove updated with %s frame:%s region:%s",
windowHandle.name, tmpRect.toShortString(), coveredRegionsAbove);
}
diff --git a/services/core/java/com/android/server/wm/WallpaperWindowToken.java b/services/core/java/com/android/server/wm/WallpaperWindowToken.java
index 7c88abcec7ec..9506ffeb2792 100644
--- a/services/core/java/com/android/server/wm/WallpaperWindowToken.java
+++ b/services/core/java/com/android/server/wm/WallpaperWindowToken.java
@@ -217,8 +217,7 @@ class WallpaperWindowToken extends WindowToken {
}
// If in a transition, defer commits for activities that are going invisible
- if (!visible && (mTransitionController.inTransition()
- || getDisplayContent().mAppTransition.isRunning())) {
+ if (!visible && mTransitionController.inTransition()) {
return;
}
diff --git a/services/core/java/com/android/server/wm/WindowContainer.java b/services/core/java/com/android/server/wm/WindowContainer.java
index 45202a29ba97..d0d2067ac4bc 100644
--- a/services/core/java/com/android/server/wm/WindowContainer.java
+++ b/services/core/java/com/android/server/wm/WindowContainer.java
@@ -16,9 +16,6 @@
package com.android.server.wm;
-import static android.app.WindowConfiguration.WINDOWING_MODE_FREEFORM;
-import static android.app.WindowConfiguration.WINDOWING_MODE_FULLSCREEN;
-import static android.app.WindowConfiguration.WINDOWING_MODE_MULTI_WINDOW;
import static android.app.WindowConfiguration.WINDOWING_MODE_PINNED;
import static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_BEHIND;
import static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_UNSET;
@@ -29,24 +26,15 @@ import static android.content.pm.ActivityInfo.reverseOrientation;
import static android.content.res.Configuration.ORIENTATION_LANDSCAPE;
import static android.content.res.Configuration.ORIENTATION_PORTRAIT;
import static android.content.res.Configuration.ORIENTATION_UNDEFINED;
-import static android.os.Trace.TRACE_TAG_WINDOW_MANAGER;
import static android.os.UserHandle.USER_NULL;
import static android.view.SurfaceControl.Transaction;
import static android.view.WindowInsets.Type.InsetsType;
import static android.view.WindowManager.LayoutParams.INVALID_WINDOW_TYPE;
-import static android.window.TaskFragmentAnimationParams.DEFAULT_ANIMATION_BACKGROUND_COLOR;
import static android.window.DesktopModeFlags.ENABLE_CAPTION_COMPAT_INSET_FORCE_CONSUMPTION;
import static com.android.internal.protolog.WmProtoLogGroups.WM_DEBUG_ANIM;
-import static com.android.internal.protolog.WmProtoLogGroups.WM_DEBUG_APP_TRANSITIONS;
-import static com.android.internal.protolog.WmProtoLogGroups.WM_DEBUG_APP_TRANSITIONS_ANIM;
import static com.android.internal.protolog.WmProtoLogGroups.WM_DEBUG_ORIENTATION;
import static com.android.internal.protolog.WmProtoLogGroups.WM_DEBUG_SYNC_ENGINE;
-import static com.android.server.policy.WindowManagerPolicy.FINISH_LAYOUT_REDO_WALLPAPER;
-import static com.android.server.wm.AppTransition.MAX_APP_TRANSITION_DURATION;
-import static com.android.server.wm.AppTransition.isActivityTransitOld;
-import static com.android.server.wm.AppTransition.isTaskFragmentTransitOld;
-import static com.android.server.wm.DisplayContent.IME_TARGET_LAYERING;
import static com.android.server.wm.IdentifierProto.HASH_CODE;
import static com.android.server.wm.IdentifierProto.TITLE;
import static com.android.server.wm.IdentifierProto.USER_ID;
@@ -65,7 +53,6 @@ import static com.android.server.wm.WindowContainerProto.VISIBLE;
import static com.android.server.wm.WindowManagerDebugConfig.DEBUG;
import static com.android.server.wm.WindowManagerDebugConfig.TAG_WITH_CLASS_NAME;
import static com.android.server.wm.WindowManagerDebugConfig.TAG_WM;
-import static com.android.server.wm.WindowStateAnimator.ROOT_TASK_CLIP_AFTER_ANIM;
import android.annotation.CallSuper;
import android.annotation.ColorInt;
@@ -81,10 +68,8 @@ import android.graphics.Rect;
import android.os.Debug;
import android.os.IBinder;
import android.os.RemoteException;
-import android.os.Trace;
import android.util.ArrayMap;
import android.util.ArraySet;
-import android.util.Pair;
import android.util.Pools;
import android.util.RotationUtils;
import android.util.Slog;
@@ -102,14 +87,11 @@ import android.view.SurfaceControl.Builder;
import android.view.SurfaceControlViewHost;
import android.view.WindowManager;
import android.view.WindowManager.TransitionOldType;
-import android.view.animation.Animation;
import android.window.IWindowContainerToken;
import android.window.WindowContainerToken;
import com.android.internal.annotations.VisibleForTesting;
-import com.android.internal.graphics.ColorUtils;
import com.android.internal.protolog.ProtoLog;
-import com.android.internal.protolog.common.LogLevel;
import com.android.internal.util.ToBooleanFunction;
import com.android.server.wm.SurfaceAnimator.Animatable;
import com.android.server.wm.SurfaceAnimator.AnimationType;
@@ -1315,31 +1297,12 @@ class WindowContainer<E extends WindowContainer> extends ConfigurationContainer<
}
/**
- * Returns true if the container or one of its children as some content it can display or wants
- * to display (e.g. app views or saved surface).
- *
- * NOTE: While this method will return true if the there is some content to display, it doesn't
- * mean the container is visible. Use {@link #isVisible()} to determine if the container is
- * visible.
- */
- boolean hasContentToDisplay() {
- for (int i = mChildren.size() - 1; i >= 0; --i) {
- final WindowContainer wc = mChildren.get(i);
- if (wc.hasContentToDisplay()) {
- return true;
- }
- }
- return false;
- }
-
- /**
* Returns true if the container or one of its children is considered visible from the
* WindowManager perspective which usually means valid surface and some other internal state
* are true.
*
* NOTE: While this method will return true if the surface is visible, it doesn't mean the
- * client has actually displayed any content. Use {@link #hasContentToDisplay()} to determine if
- * the container has any content to display.
+ * client has actually displayed any content.
*/
boolean isVisible() {
// TODO: Will this be more correct if it checks the visibility of its parents?
@@ -1480,13 +1443,6 @@ class WindowContainer<E extends WindowContainer> extends ConfigurationContainer<
}
}
- void onAppTransitionDone() {
- for (int i = mChildren.size() - 1; i >= 0; --i) {
- final WindowContainer wc = mChildren.get(i);
- wc.onAppTransitionDone();
- }
- }
-
/**
* Called when this container or one of its descendants changed its requested orientation, and
* wants this container to handle it or pass it to its parent.
@@ -3039,264 +2995,10 @@ class WindowContainer<E extends WindowContainer> extends ConfigurationContainer<
getRelativePosition(outPosition);
}
- /**
- * Applies the app transition animation according the given the layout properties in the
- * window hierarchy.
- *
- * @param lp The layout parameters of the window.
- * @param transit The app transition type indicates what kind of transition to be applied.
- * @param enter Whether the app transition is entering transition or not.
- * @param isVoiceInteraction Whether the container is participating in voice interaction or not.
- * @param sources {@link ActivityRecord}s which causes this app transition animation.
- *
- * @return {@code true} when the container applied the app transition, {@code false} if the
- * app transition is disabled or skipped.
- *
- * @see #getAnimationAdapter
- */
- boolean applyAnimation(WindowManager.LayoutParams lp, @TransitionOldType int transit,
- boolean enter, boolean isVoiceInteraction,
- @Nullable ArrayList<WindowContainer> sources) {
- if (mWmService.mDisableTransitionAnimation) {
- ProtoLog.v(WM_DEBUG_APP_TRANSITIONS_ANIM,
- "applyAnimation: transition animation is disabled or skipped. "
- + "container=%s", this);
- cancelAnimation();
- return false;
- }
-
- // Only apply an animation if the display isn't frozen. If it is frozen, there is no reason
- // to animate and it can cause strange artifacts when we unfreeze the display if some
- // different animation is running.
- try {
- Trace.traceBegin(TRACE_TAG_WINDOW_MANAGER, "WC#applyAnimation");
- if (okToAnimate()) {
- ProtoLog.v(WM_DEBUG_APP_TRANSITIONS_ANIM,
- "applyAnimation: transit=%s, enter=%b, wc=%s",
- AppTransition.appTransitionOldToString(transit), enter, this);
- applyAnimationUnchecked(lp, enter, transit, isVoiceInteraction, sources);
- } else {
- cancelAnimation();
- }
- } finally {
- Trace.traceEnd(TRACE_TAG_WINDOW_MANAGER);
- }
-
- return isAnimating();
- }
-
- /**
- * Gets the {@link AnimationAdapter} according the given window layout properties in the window
- * hierarchy.
- *
- * @return The return value will always contain two elements, one for normal animations and the
- * other for thumbnail animation, both can be {@code null}.
- *
- * @See com.android.server.wm.RemoteAnimationController.RemoteAnimationRecord
- * @See LocalAnimationAdapter
- */
- Pair<AnimationAdapter, AnimationAdapter> getAnimationAdapter(WindowManager.LayoutParams lp,
- @TransitionOldType int transit, boolean enter, boolean isVoiceInteraction) {
- final Pair<AnimationAdapter, AnimationAdapter> resultAdapters;
- final int appRootTaskClipMode = getDisplayContent().mAppTransition.getAppRootTaskClipMode();
-
- // Separate position and size for use in animators.
- final Rect screenBounds = getAnimationBounds(appRootTaskClipMode);
- mTmpRect.set(screenBounds);
- getAnimationPosition(mTmpPoint);
- mTmpRect.offsetTo(0, 0);
-
- final boolean isChanging = AppTransition.isChangeTransitOld(transit);
-
- if (isChanging) {
- final float durationScale = mWmService.getTransitionAnimationScaleLocked();
- final DisplayInfo displayInfo = getDisplayContent().getDisplayInfo();
- mTmpRect.offsetTo(mTmpPoint.x, mTmpPoint.y);
-
- final AnimationAdapter adapter = new LocalAnimationAdapter(
- new WindowChangeAnimationSpec(null /* startBounds */, mTmpRect,
- displayInfo, durationScale, true /* isAppAnimation */,
- false /* isThumbnail */),
- getSurfaceAnimationRunner());
-
- final AnimationAdapter thumbnailAdapter = null;
- resultAdapters = new Pair<>(adapter, thumbnailAdapter);
- mTransit = transit;
- mTransitFlags = getDisplayContent().mAppTransition.getTransitFlags();
- } else {
- mNeedsAnimationBoundsLayer = (appRootTaskClipMode == ROOT_TASK_CLIP_AFTER_ANIM);
- final Animation a = loadAnimation(lp, transit, enter, isVoiceInteraction);
-
- if (a != null) {
- // Only apply corner radius to animation if we're not in multi window mode.
- // We don't want rounded corners when in pip or split screen.
- final float windowCornerRadius = !inMultiWindowMode()
- ? getDisplayContent().getWindowCornerRadius()
- : 0;
- if (asActivityRecord() != null
- && asActivityRecord().isNeedsLetterboxedAnimation()) {
- asActivityRecord().getLetterboxInnerBounds(mTmpRect);
- }
- AnimationAdapter adapter = new LocalAnimationAdapter(
- new WindowAnimationSpec(a, mTmpPoint, mTmpRect,
- getDisplayContent().mAppTransition.canSkipFirstFrame(),
- appRootTaskClipMode, true /* isAppAnimation */, windowCornerRadius),
- getSurfaceAnimationRunner());
-
- resultAdapters = new Pair<>(adapter, null);
- mNeedsZBoost = a.getZAdjustment() == Animation.ZORDER_TOP
- || AppTransition.isClosingTransitOld(transit);
- mTransit = transit;
- mTransitFlags = getDisplayContent().mAppTransition.getTransitFlags();
- } else {
- resultAdapters = new Pair<>(null, null);
- }
- }
- return resultAdapters;
- }
-
- protected void applyAnimationUnchecked(WindowManager.LayoutParams lp, boolean enter,
- @TransitionOldType int transit, boolean isVoiceInteraction,
- @Nullable ArrayList<WindowContainer> sources) {
- final Task task = asTask();
- if (task != null && !enter && !task.isActivityTypeHomeOrRecents()) {
- final InsetsControlTarget imeTarget = mDisplayContent.getImeTarget(IME_TARGET_LAYERING);
- final boolean isImeLayeringTarget = imeTarget != null && imeTarget.getWindow() != null
- && imeTarget.getWindow().getTask() == task;
- // Attach and show the IME screenshot when the task is the IME target and performing
- // task closing transition to the next task.
- if (isImeLayeringTarget && AppTransition.isTaskCloseTransitOld(transit)) {
- mDisplayContent.showImeScreenshot();
- }
- }
- final Pair<AnimationAdapter, AnimationAdapter> adapters = getAnimationAdapter(lp,
- transit, enter, isVoiceInteraction);
- AnimationAdapter adapter = adapters.first;
- AnimationAdapter thumbnailAdapter = adapters.second;
- if (adapter != null) {
- if (sources != null) {
- mSurfaceAnimationSources.addAll(sources);
- }
-
- AnimationRunnerBuilder animationRunnerBuilder = new AnimationRunnerBuilder();
-
- // Check if the animation requests to show background color for Activity and embedded
- // TaskFragment.
- final ActivityRecord activityRecord = asActivityRecord();
- final TaskFragment taskFragment = asTaskFragment();
- if (adapter.getShowBackground()
- // Check if it is Activity transition.
- && ((activityRecord != null && isActivityTransitOld(transit))
- // Check if it is embedded TaskFragment transition.
- || (taskFragment != null && taskFragment.isEmbedded()
- && isTaskFragmentTransitOld(transit)))) {
- final @ColorInt int backgroundColorForTransition;
- if (adapter.getBackgroundColor() != 0) {
- // If available use the background color provided through getBackgroundColor
- // which if set originates from a call to overridePendingAppTransition.
- backgroundColorForTransition = adapter.getBackgroundColor();
- } else {
- final TaskFragment organizedTf = activityRecord != null
- ? activityRecord.getOrganizedTaskFragment()
- : taskFragment.getOrganizedTaskFragment();
- if (organizedTf != null && organizedTf.getAnimationParams()
- .getAnimationBackgroundColor() != DEFAULT_ANIMATION_BACKGROUND_COLOR) {
- // This window is embedded and has an animation background color set on the
- // TaskFragment. Pass this color with this window, so the handler can use it
- // as the animation background color if needed,
- backgroundColorForTransition = organizedTf.getAnimationParams()
- .getAnimationBackgroundColor();
- } else {
- // Otherwise default to the window's background color if provided through
- // the theme as the background color for the animation - the top most window
- // with a valid background color and showBackground set takes precedence.
- final Task parentTask = activityRecord != null
- ? activityRecord.getTask()
- : taskFragment.getTask();
- backgroundColorForTransition = parentTask.getTaskDescription()
- .getBackgroundColor();
- }
- }
- // Set to opaque for animation background to prevent it from exposing the blank
- // background or content below.
- animationRunnerBuilder.setTaskBackgroundColor(ColorUtils.setAlphaComponent(
- backgroundColorForTransition, 255));
- }
-
- animationRunnerBuilder.build()
- .startAnimation(getPendingTransaction(), adapter, !isVisible(),
- ANIMATION_TYPE_APP_TRANSITION, thumbnailAdapter);
-
- if (adapter.getShowWallpaper()) {
- getDisplayContent().pendingLayoutChanges |= FINISH_LAYOUT_REDO_WALLPAPER;
- }
- }
- }
-
final SurfaceAnimationRunner getSurfaceAnimationRunner() {
return mWmService.mSurfaceAnimationRunner;
}
- private Animation loadAnimation(WindowManager.LayoutParams lp, int transit, boolean enter,
- boolean isVoiceInteraction) {
- if ((isOrganized()
- // TODO(b/161711458): Clean-up when moved to shell.
- && getWindowingMode() != WINDOWING_MODE_FULLSCREEN
- && getWindowingMode() != WINDOWING_MODE_FREEFORM
- && getWindowingMode() != WINDOWING_MODE_MULTI_WINDOW)) {
- return null;
- }
-
- final DisplayContent displayContent = getDisplayContent();
- final DisplayInfo displayInfo = displayContent.getDisplayInfo();
- final int width = displayInfo.appWidth;
- final int height = displayInfo.appHeight;
- ProtoLog.v(WM_DEBUG_APP_TRANSITIONS_ANIM, "applyAnimation: container=%s", this);
-
- // Determine the visible rect to calculate the thumbnail clip with
- // getAnimationFrames.
- final Rect frame = new Rect(0, 0, width, height);
- final Rect displayFrame = new Rect(0, 0,
- displayInfo.logicalWidth, displayInfo.logicalHeight);
- final Rect insets = new Rect();
- final Rect stableInsets = new Rect();
- final Rect surfaceInsets = new Rect();
- getAnimationFrames(frame, insets, stableInsets, surfaceInsets);
-
- if (mLaunchTaskBehind) {
- // Differentiate the two animations. This one which is briefly on the screen
- // gets the !enter animation, and the other one which remains on the
- // screen gets the enter animation. Both appear in the mOpeningApps set.
- enter = false;
- }
- ProtoLog.d(WM_DEBUG_APP_TRANSITIONS,
- "Loading animation for app transition. transit=%s enter=%b frame=%s insets=%s "
- + "surfaceInsets=%s",
- AppTransition.appTransitionOldToString(transit), enter, frame, insets,
- surfaceInsets);
- final Configuration displayConfig = displayContent.getConfiguration();
- final Animation a = getDisplayContent().mAppTransition.loadAnimation(lp, transit, enter,
- displayConfig.uiMode, displayConfig.orientation, frame, displayFrame, insets,
- surfaceInsets, stableInsets, isVoiceInteraction, inFreeformWindowingMode(), this);
- if (a != null) {
- if (a != null) {
- // Setup the maximum app transition duration to prevent malicious app may set a long
- // animation duration or infinite repeat counts for the app transition through
- // ActivityOption#makeCustomAnimation or WindowManager#overridePendingTransition.
- a.restrictDuration(MAX_APP_TRANSITION_DURATION);
- }
- if (ProtoLog.isEnabled(WM_DEBUG_ANIM, LogLevel.DEBUG)) {
- ProtoLog.i(WM_DEBUG_ANIM, "Loaded animation %s for %s, duration: %d, stack=%s",
- a, this, ((a != null) ? a.getDuration() : 0), Debug.getCallers(20));
- }
- final int containingWidth = frame.width();
- final int containingHeight = frame.height();
- a.initialize(containingWidth, containingHeight, width, height);
- a.scaleCurrentDuration(mWmService.getTransitionAnimationScaleLocked());
- }
- return a;
- }
-
boolean canCreateRemoteAnimationTarget() {
return false;
}
diff --git a/services/core/java/com/android/server/wm/WindowProcessController.java b/services/core/java/com/android/server/wm/WindowProcessController.java
index 30dde543b9d4..270de0197a4e 100644
--- a/services/core/java/com/android/server/wm/WindowProcessController.java
+++ b/services/core/java/com/android/server/wm/WindowProcessController.java
@@ -37,6 +37,7 @@ import static com.android.server.wm.ActivityRecord.State.PAUSING;
import static com.android.server.wm.ActivityRecord.State.RESUMED;
import static com.android.server.wm.ActivityRecord.State.STARTED;
import static com.android.server.wm.ActivityRecord.State.STOPPING;
+import static com.android.server.wm.ActivityRecord.State.STOPPED;
import static com.android.server.wm.ActivityTaskManagerDebugConfig.DEBUG_RELEASE;
import static com.android.server.wm.ActivityTaskManagerDebugConfig.POSTFIX_CONFIGURATION;
import static com.android.server.wm.ActivityTaskManagerDebugConfig.POSTFIX_RELEASE;
@@ -344,6 +345,12 @@ public class WindowProcessController extends ConfigurationContainer<Configuratio
*/
private volatile int mActivityStateFlags = ACTIVITY_STATE_FLAG_MASK_MIN_TASK_LAYER;
+ /**
+ * The most recent timestamp of when one of this process's stopped activities in a
+ * perceptible task became stopped. Written by window manager and read by activity manager.
+ */
+ private volatile long mPerceptibleTaskStoppedTimeMillis = Long.MIN_VALUE;
+
public WindowProcessController(@NonNull ActivityTaskManagerService atm,
@NonNull ApplicationInfo info, String name, int uid, int userId, Object owner,
@NonNull WindowProcessListener listener) {
@@ -475,8 +482,9 @@ public class WindowProcessController extends ConfigurationContainer<Configuratio
r.detachFromProcess();
if (r.isVisibleRequested()) {
hasVisibleActivity = true;
+ Task finishingTask = r.getTask();
r.mDisplayContent.requestTransitionAndLegacyPrepare(TRANSIT_CLOSE,
- TRANSIT_FLAG_APP_CRASHED);
+ TRANSIT_FLAG_APP_CRASHED, finishingTask);
}
r.destroyIfPossible("handleAppCrashed");
}
@@ -1228,6 +1236,17 @@ public class WindowProcessController extends ConfigurationContainer<Configuratio
return mActivityStateFlags;
}
+ /**
+ * Returns the most recent timestamp when one of this process's stopped activities in a
+ * perceptible task became stopped. It should only be called if {@link #hasActivities}
+ * returns {@code true} and {@link #getActivityStateFlags} does not have any of
+ * the ACTIVITY_STATE_FLAG_IS_(VISIBLE|PAUSING_OR_PAUSED|STOPPING) bit set.
+ */
+ @HotPath(caller = HotPath.OOM_ADJUSTMENT)
+ public long getPerceptibleTaskStoppedTimeMillis() {
+ return mPerceptibleTaskStoppedTimeMillis;
+ }
+
void computeProcessActivityState() {
// Since there could be more than one activities in a process record, we don't need to
// compute the OomAdj with each of them, just need to find out the activity with the
@@ -1239,6 +1258,7 @@ public class WindowProcessController extends ConfigurationContainer<Configuratio
int minTaskLayer = Integer.MAX_VALUE;
int stateFlags = 0;
int nonOccludedRatio = 0;
+ long perceptibleTaskStoppedTimeMillis = Long.MIN_VALUE;
final boolean wasResumed = hasResumedActivity();
final boolean wasAnyVisible = (mActivityStateFlags
& (ACTIVITY_STATE_FLAG_IS_VISIBLE | ACTIVITY_STATE_FLAG_IS_WINDOW_VISIBLE)) != 0;
@@ -1287,6 +1307,11 @@ public class WindowProcessController extends ConfigurationContainer<Configuratio
bestInvisibleState = STOPPING;
// Not "finishing" if any of activity isn't finishing.
allStoppingFinishing &= r.finishing;
+ } else if (bestInvisibleState == DESTROYED && r.isState(STOPPED)) {
+ if (task.mIsPerceptible) {
+ perceptibleTaskStoppedTimeMillis =
+ Long.max(r.mStoppedTime, perceptibleTaskStoppedTimeMillis);
+ }
}
}
}
@@ -1324,6 +1349,7 @@ public class WindowProcessController extends ConfigurationContainer<Configuratio
}
}
mActivityStateFlags = stateFlags;
+ mPerceptibleTaskStoppedTimeMillis = perceptibleTaskStoppedTimeMillis;
final boolean anyVisible = (stateFlags
& (ACTIVITY_STATE_FLAG_IS_VISIBLE | ACTIVITY_STATE_FLAG_IS_WINDOW_VISIBLE)) != 0;
diff --git a/services/core/java/com/android/server/wm/WindowState.java b/services/core/java/com/android/server/wm/WindowState.java
index 92ad2cec364b..bfedc90497ae 100644
--- a/services/core/java/com/android/server/wm/WindowState.java
+++ b/services/core/java/com/android/server/wm/WindowState.java
@@ -1698,17 +1698,6 @@ class WindowState extends WindowContainer<WindowState> implements WindowManagerP
|| mActivityRecord.isStartingWindowDisplayed());
}
- @Override
- boolean hasContentToDisplay() {
- if (!isDrawn() && (mViewVisibility == View.VISIBLE
- || (isAnimating(TRANSITION | PARENTS)
- && !getDisplayContent().mAppTransition.isTransitionSet()))) {
- return true;
- }
-
- return super.hasContentToDisplay();
- }
-
private boolean isVisibleByPolicyOrInsets() {
return isVisibleByPolicy()
// If we don't have a provider, this window isn't used as a window generating
diff --git a/services/core/jni/Android.bp b/services/core/jni/Android.bp
index d26539c377a9..95776088aad8 100644
--- a/services/core/jni/Android.bp
+++ b/services/core/jni/Android.bp
@@ -204,6 +204,7 @@ cc_defaults {
"android.frameworks.sensorservice-V1-ndk",
"android.frameworks.stats@1.0",
"android.frameworks.stats-V2-ndk",
+ "android.os.vibrator.flags-aconfig-cc",
"android.system.suspend.control-V1-cpp",
"android.system.suspend.control.internal-cpp",
"android.system.suspend-V1-ndk",
diff --git a/services/core/jni/com_android_server_vibrator_VibratorController.cpp b/services/core/jni/com_android_server_vibrator_VibratorController.cpp
index abd4cd25cf68..534dbb1f6cf1 100644
--- a/services/core/jni/com_android_server_vibrator_VibratorController.cpp
+++ b/services/core/jni/com_android_server_vibrator_VibratorController.cpp
@@ -21,6 +21,7 @@
#include <android/binder_parcel_jni.h>
#include <android/hardware/vibrator/1.3/IVibrator.h>
#include <android/persistable_bundle_aidl.h>
+#include <android_os_vibrator.h>
#include <nativehelper/JNIHelp.h>
#include <utils/Log.h>
#include <utils/misc.h>
@@ -143,21 +144,23 @@ public:
return mHal->doWithRetry(fn, functionName);
}
- std::function<void()> createCallback(jlong vibrationId) {
+ std::function<void()> createCallback(jlong vibrationId, jlong stepId) {
auto callbackId = ++mCallbackId;
- return [vibrationId, callbackId, this]() {
+ return [vibrationId, stepId, callbackId, this]() {
auto currentCallbackId = mCallbackId.load();
- if (currentCallbackId != callbackId) {
- // This callback is from an older HAL call that is no longer relevant to the service
+ if (!android_os_vibrator_fix_vibration_thread_callback_handling() &&
+ currentCallbackId != callbackId) {
+ // This callback is from an older HAL call that is no longer relevant
return;
}
auto jniEnv = GetOrAttachJNIEnvironment(sJvm);
- jniEnv->CallVoidMethod(mCallbackListener, sMethodIdOnComplete, mVibratorId,
- vibrationId);
+ jniEnv->CallVoidMethod(mCallbackListener, sMethodIdOnComplete, mVibratorId, vibrationId,
+ stepId);
};
}
void disableOldCallbacks() {
+ // TODO remove this once android_os_vibrator_fix_vibration_thread_callback_handling removed
mCallbackId++;
}
@@ -165,6 +168,7 @@ private:
const std::shared_ptr<vibrator::HalController> mHal;
const int32_t mVibratorId;
const jobject mCallbackListener;
+ // TODO remove this once android_os_vibrator_fix_vibration_thread_callback_handling removed
std::atomic<int64_t> mCallbackId;
};
@@ -273,13 +277,13 @@ static jboolean vibratorIsAvailable(JNIEnv* env, jclass /* clazz */, jlong ptr)
}
static jlong vibratorOn(JNIEnv* env, jclass /* clazz */, jlong ptr, jlong timeoutMs,
- jlong vibrationId) {
+ jlong vibrationId, jlong stepId) {
VibratorControllerWrapper* wrapper = reinterpret_cast<VibratorControllerWrapper*>(ptr);
if (wrapper == nullptr) {
ALOGE("vibratorOn failed because native wrapper was not initialized");
return -1;
}
- auto callback = wrapper->createCallback(vibrationId);
+ auto callback = wrapper->createCallback(vibrationId, stepId);
auto onFn = [timeoutMs, &callback](vibrator::HalWrapper* hal) {
return hal->on(std::chrono::milliseconds(timeoutMs), callback);
};
@@ -324,7 +328,7 @@ static void vibratorSetExternalControl(JNIEnv* env, jclass /* clazz */, jlong pt
}
static jlong vibratorPerformEffect(JNIEnv* env, jclass /* clazz */, jlong ptr, jlong effect,
- jlong strength, jlong vibrationId) {
+ jlong strength, jlong vibrationId, jlong stepId) {
VibratorControllerWrapper* wrapper = reinterpret_cast<VibratorControllerWrapper*>(ptr);
if (wrapper == nullptr) {
ALOGE("vibratorPerformEffect failed because native wrapper was not initialized");
@@ -332,7 +336,7 @@ static jlong vibratorPerformEffect(JNIEnv* env, jclass /* clazz */, jlong ptr, j
}
Aidl::Effect effectType = static_cast<Aidl::Effect>(effect);
Aidl::EffectStrength effectStrength = static_cast<Aidl::EffectStrength>(strength);
- auto callback = wrapper->createCallback(vibrationId);
+ auto callback = wrapper->createCallback(vibrationId, stepId);
auto performEffectFn = [effectType, effectStrength, &callback](vibrator::HalWrapper* hal) {
return hal->performEffect(effectType, effectStrength, callback);
};
@@ -342,7 +346,7 @@ static jlong vibratorPerformEffect(JNIEnv* env, jclass /* clazz */, jlong ptr, j
static jlong vibratorPerformVendorEffect(JNIEnv* env, jclass /* clazz */, jlong ptr,
jobject vendorData, jlong strength, jfloat scale,
- jfloat adaptiveScale, jlong vibrationId) {
+ jfloat adaptiveScale, jlong vibrationId, jlong stepId) {
VibratorControllerWrapper* wrapper = reinterpret_cast<VibratorControllerWrapper*>(ptr);
if (wrapper == nullptr) {
ALOGE("vibratorPerformVendorEffect failed because native wrapper was not initialized");
@@ -350,7 +354,7 @@ static jlong vibratorPerformVendorEffect(JNIEnv* env, jclass /* clazz */, jlong
}
Aidl::VendorEffect effect =
vendorEffectFromJavaParcel(env, vendorData, strength, scale, adaptiveScale);
- auto callback = wrapper->createCallback(vibrationId);
+ auto callback = wrapper->createCallback(vibrationId, stepId);
auto performVendorEffectFn = [&effect, &callback](vibrator::HalWrapper* hal) {
return hal->performVendorEffect(effect, callback);
};
@@ -359,7 +363,8 @@ static jlong vibratorPerformVendorEffect(JNIEnv* env, jclass /* clazz */, jlong
}
static jlong vibratorPerformComposedEffect(JNIEnv* env, jclass /* clazz */, jlong ptr,
- jobjectArray composition, jlong vibrationId) {
+ jobjectArray composition, jlong vibrationId,
+ jlong stepId) {
VibratorControllerWrapper* wrapper = reinterpret_cast<VibratorControllerWrapper*>(ptr);
if (wrapper == nullptr) {
ALOGE("vibratorPerformComposedEffect failed because native wrapper was not initialized");
@@ -371,7 +376,7 @@ static jlong vibratorPerformComposedEffect(JNIEnv* env, jclass /* clazz */, jlon
jobject element = env->GetObjectArrayElement(composition, i);
effects.push_back(effectFromJavaPrimitive(env, element));
}
- auto callback = wrapper->createCallback(vibrationId);
+ auto callback = wrapper->createCallback(vibrationId, stepId);
auto performComposedEffectFn = [&effects, &callback](vibrator::HalWrapper* hal) {
return hal->performComposedEffect(effects, callback);
};
@@ -381,7 +386,8 @@ static jlong vibratorPerformComposedEffect(JNIEnv* env, jclass /* clazz */, jlon
}
static jlong vibratorPerformPwleEffect(JNIEnv* env, jclass /* clazz */, jlong ptr,
- jobjectArray waveform, jint brakingId, jlong vibrationId) {
+ jobjectArray waveform, jint brakingId, jlong vibrationId,
+ jlong stepId) {
VibratorControllerWrapper* wrapper = reinterpret_cast<VibratorControllerWrapper*>(ptr);
if (wrapper == nullptr) {
ALOGE("vibratorPerformPwleEffect failed because native wrapper was not initialized");
@@ -406,7 +412,7 @@ static jlong vibratorPerformPwleEffect(JNIEnv* env, jclass /* clazz */, jlong pt
}
}
- auto callback = wrapper->createCallback(vibrationId);
+ auto callback = wrapper->createCallback(vibrationId, stepId);
auto performPwleEffectFn = [&primitives, &callback](vibrator::HalWrapper* hal) {
return hal->performPwleEffect(primitives, callback);
};
@@ -415,7 +421,7 @@ static jlong vibratorPerformPwleEffect(JNIEnv* env, jclass /* clazz */, jlong pt
}
static jlong vibratorPerformPwleV2Effect(JNIEnv* env, jclass /* clazz */, jlong ptr,
- jobjectArray waveform, jlong vibrationId) {
+ jobjectArray waveform, jlong vibrationId, jlong stepId) {
VibratorControllerWrapper* wrapper = reinterpret_cast<VibratorControllerWrapper*>(ptr);
if (wrapper == nullptr) {
ALOGE("vibratorPerformPwleV2Effect failed because native wrapper was not initialized");
@@ -431,7 +437,7 @@ static jlong vibratorPerformPwleV2Effect(JNIEnv* env, jclass /* clazz */, jlong
}
composite.pwlePrimitives = primitives;
- auto callback = wrapper->createCallback(vibrationId);
+ auto callback = wrapper->createCallback(vibrationId, stepId);
auto composePwleV2Fn = [&composite, &callback](vibrator::HalWrapper* hal) {
return hal->composePwleV2(composite, callback);
};
@@ -610,16 +616,16 @@ static const JNINativeMethod method_table[] = {
(void*)vibratorNativeInit},
{"getNativeFinalizer", "()J", (void*)vibratorGetNativeFinalizer},
{"isAvailable", "(J)Z", (void*)vibratorIsAvailable},
- {"on", "(JJJ)J", (void*)vibratorOn},
+ {"on", "(JJJJ)J", (void*)vibratorOn},
{"off", "(J)V", (void*)vibratorOff},
{"setAmplitude", "(JF)V", (void*)vibratorSetAmplitude},
- {"performEffect", "(JJJJ)J", (void*)vibratorPerformEffect},
- {"performVendorEffect", "(JLandroid/os/Parcel;JFFJ)J", (void*)vibratorPerformVendorEffect},
- {"performComposedEffect", "(J[Landroid/os/vibrator/PrimitiveSegment;J)J",
+ {"performEffect", "(JJJJJ)J", (void*)vibratorPerformEffect},
+ {"performVendorEffect", "(JLandroid/os/Parcel;JFFJJ)J", (void*)vibratorPerformVendorEffect},
+ {"performComposedEffect", "(J[Landroid/os/vibrator/PrimitiveSegment;JJ)J",
(void*)vibratorPerformComposedEffect},
- {"performPwleEffect", "(J[Landroid/os/vibrator/RampSegment;IJ)J",
+ {"performPwleEffect", "(J[Landroid/os/vibrator/RampSegment;IJJ)J",
(void*)vibratorPerformPwleEffect},
- {"performPwleV2Effect", "(J[Landroid/os/vibrator/PwlePoint;J)J",
+ {"performPwleV2Effect", "(J[Landroid/os/vibrator/PwlePoint;JJ)J",
(void*)vibratorPerformPwleV2Effect},
{"setExternalControl", "(JZ)V", (void*)vibratorSetExternalControl},
{"alwaysOnEnable", "(JJJJ)V", (void*)vibratorAlwaysOnEnable},
@@ -632,7 +638,7 @@ int register_android_server_vibrator_VibratorController(JavaVM* jvm, JNIEnv* env
auto listenerClassName =
"com/android/server/vibrator/VibratorController$OnVibrationCompleteListener";
jclass listenerClass = FindClassOrDie(env, listenerClassName);
- sMethodIdOnComplete = GetMethodIDOrDie(env, listenerClass, "onComplete", "(IJ)V");
+ sMethodIdOnComplete = GetMethodIDOrDie(env, listenerClass, "onComplete", "(IJJ)V");
jclass primitiveClass = FindClassOrDie(env, "android/os/vibrator/PrimitiveSegment");
sPrimitiveClassInfo.id = GetFieldIDOrDie(env, primitiveClass, "mPrimitiveId", "I");
diff --git a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyEngine.java b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyEngine.java
index 5b7e7f17c454..e3b9fdb16d59 100644
--- a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyEngine.java
+++ b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyEngine.java
@@ -1454,11 +1454,12 @@ final class DevicePolicyEngine {
}
for (int i = 0; i < mLocalPolicies.size(); i++) {
- Set<PolicyKey> localPolicies = new HashSet<>(
- mLocalPolicies.get(mLocalPolicies.keyAt(i)).keySet());
+ // New users are potentially added to mLocalPolicies during the loop body
+ // (see b/374511959).
+ int userId = mLocalPolicies.keyAt(i);
+ Set<PolicyKey> localPolicies = new HashSet<>(mLocalPolicies.get(userId).keySet());
for (PolicyKey policy : localPolicies) {
- PolicyState<?> policyState = mLocalPolicies.get(
- mLocalPolicies.keyAt(i)).get(policy);
+ PolicyState<?> policyState = mLocalPolicies.get(userId).get(policy);
if (policyState.getPoliciesSetByAdmins().containsKey(admin)) {
removeLocalPolicy(
policyState.getPolicyDefinition(), admin, mLocalPolicies.keyAt(i));
diff --git a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
index 191c21e661d0..aee32a0473a3 100644
--- a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
+++ b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
@@ -423,6 +423,7 @@ import android.database.ContentObserver;
import android.database.Cursor;
import android.graphics.Bitmap;
import android.hardware.usb.UsbManager;
+import android.health.connect.HealthConnectManager;
import android.location.Location;
import android.location.LocationManager;
import android.media.AudioManager;
@@ -2149,6 +2150,14 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub {
.hasSystemFeature(PackageManager.FEATURE_AUTOMOTIVE);
mBackgroundHandler = BackgroundThread.getHandler();
+ // Add the health permission to the list of restricted permissions.
+ if (android.permission.flags.Flags.replaceBodySensorPermissionEnabled()) {
+ Set<String> healthPermissions = HealthConnectManager.getHealthPermissions(mContext);
+ for (String permission : healthPermissions) {
+ SENSOR_PERMISSIONS.add(permission);
+ }
+ }
+
// Needed when mHasFeature == false, because it controls the certificate warning text.
mCertificateMonitor = new CertificateMonitor(this, mInjector, mBackgroundHandler);
diff --git a/services/tests/InputMethodSystemServerTests/src/com/android/inputmethodservice/InputMethodServiceTest.java b/services/tests/InputMethodSystemServerTests/src/com/android/inputmethodservice/InputMethodServiceTest.java
index ae5e85163e9a..856466675a28 100644
--- a/services/tests/InputMethodSystemServerTests/src/com/android/inputmethodservice/InputMethodServiceTest.java
+++ b/services/tests/InputMethodSystemServerTests/src/com/android/inputmethodservice/InputMethodServiceTest.java
@@ -40,10 +40,14 @@ import android.content.res.Configuration;
import android.graphics.Insets;
import android.os.Build;
import android.os.RemoteException;
+import android.platform.test.annotations.RequiresFlagsEnabled;
import android.platform.test.flag.junit.DeviceFlagsValueProvider;
import android.provider.Settings;
import android.server.wm.WindowManagerStateHelper;
import android.util.Log;
+import android.view.View;
+import android.view.WindowInsets;
+import android.view.WindowInsetsAnimation;
import android.view.WindowManagerGlobal;
import android.view.inputmethod.EditorInfo;
import android.view.inputmethod.Flags;
@@ -72,6 +76,7 @@ import org.junit.Test;
import org.junit.rules.TestName;
import org.junit.runner.RunWith;
+import java.util.List;
import java.util.Objects;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.TimeUnit;
@@ -139,10 +144,9 @@ public class InputMethodServiceTest {
if (!mOriginalVerboseImeTrackerLoggingEnabled) {
setVerboseImeTrackerLogging(true);
}
+ mUiDevice.setOrientationNatural();
prepareIme();
prepareActivity();
- mUiDevice.freezeRotation();
- mUiDevice.setOrientationNatural();
// Waits for input binding ready.
eventually(() -> {
mInputMethodService = InputMethodServiceWrapper.getInstance();
@@ -169,6 +173,9 @@ public class InputMethodServiceTest {
@After
public void tearDown() throws Exception {
+ if (!mUiDevice.isNaturalOrientation()) {
+ mUiDevice.setOrientationNatural();
+ }
mUiDevice.unfreezeRotation();
if (!mOriginalVerboseImeTrackerLoggingEnabled) {
setVerboseImeTrackerLogging(false);
@@ -245,6 +252,61 @@ public class InputMethodServiceTest {
}
/**
+ * This checks that the surface is removed after the window was hidden in
+ * InputMethodService#hideSoftInput
+ */
+ @Test
+ @RequiresFlagsEnabled(Flags.FLAG_REFACTOR_INSETS_CONTROLLER)
+ public void testSurfaceRemovedAfterHideSoftInput() {
+ setShowImeWithHardKeyboard(true /* enabled */);
+
+ // Triggers to show IME via public API.
+ verifyInputViewStatusOnMainSync(() -> mActivity.showImeWithWindowInsetsController(),
+ EVENT_SHOW, true /* eventExpected */, true /* shown */, "IME is shown");
+ assertWithMessage("IME is shown").that(mInputMethodService.isInputViewShown()).isTrue();
+
+ final var window = mInputMethodService.getWindow().getWindow();
+ assertWithMessage("IME window exists").that(window).isNotNull();
+ assertWithMessage("IME window showing").that(
+ window.getDecorView().getVisibility()).isEqualTo(View.VISIBLE);
+
+ mActivity.getWindow().getDecorView().setWindowInsetsAnimationCallback(
+ new WindowInsetsAnimation.Callback(
+ WindowInsetsAnimation.Callback.DISPATCH_MODE_CONTINUE_ON_SUBTREE) {
+ @NonNull
+ @Override
+ public WindowInsetsAnimation.Bounds onStart(
+ @NonNull WindowInsetsAnimation animation,
+ @NonNull WindowInsetsAnimation.Bounds bounds) {
+ return super.onStart(animation, bounds);
+ }
+
+ @NonNull
+ @Override
+ public WindowInsets onProgress(@NonNull WindowInsets insets,
+ @NonNull List<WindowInsetsAnimation> runningAnimations) {
+ assertWithMessage("IME surface not removed during the animation").that(
+ window.getDecorView().getVisibility()).isEqualTo(View.VISIBLE);
+ return insets;
+ }
+
+ @Override
+ public void onEnd(@NonNull WindowInsetsAnimation animation) {
+ assertWithMessage(
+ "IME surface not removed before the end of the animation").that(
+ window.getDecorView().getVisibility()).isEqualTo(View.VISIBLE);
+ super.onEnd(animation);
+ }
+ });
+
+ // Triggers to hide IME via public API.
+ verifyInputViewStatusOnMainSync(() -> mActivity.hideImeWithWindowInsetsController(),
+ EVENT_HIDE, true /* eventExpected */, false /* shown */, "IME is not shown");
+ eventually(() -> assertWithMessage("IME window not showing").that(
+ window.getDecorView().getVisibility()).isEqualTo(View.GONE));
+ }
+
+ /**
* This checks the result of calling IMS#requestShowSelf and IMS#requestHideSelf.
*/
@Test
diff --git a/services/tests/displayservicetests/src/com/android/server/display/DisplayManagerServiceTest.java b/services/tests/displayservicetests/src/com/android/server/display/DisplayManagerServiceTest.java
index e0e44252a8f3..c151732cec66 100644
--- a/services/tests/displayservicetests/src/com/android/server/display/DisplayManagerServiceTest.java
+++ b/services/tests/displayservicetests/src/com/android/server/display/DisplayManagerServiceTest.java
@@ -984,8 +984,7 @@ public class DisplayManagerServiceTest {
Handler handler = displayManager.getDisplayHandler();
waitForIdleHandler(handler);
- assertThat(callback.receivedEvents()).containsExactly(EVENT_DISPLAY_BASIC_CHANGED,
- EVENT_DISPLAY_REFRESH_RATE_CHANGED);
+ assertThat(callback.receivedEvents()).containsExactly(EVENT_DISPLAY_BASIC_CHANGED);
}
/**
diff --git a/services/tests/mockingservicestests/src/com/android/server/am/MockingOomAdjusterTests.java b/services/tests/mockingservicestests/src/com/android/server/am/MockingOomAdjusterTests.java
index 1ef758cf192e..340115a7d465 100644
--- a/services/tests/mockingservicestests/src/com/android/server/am/MockingOomAdjusterTests.java
+++ b/services/tests/mockingservicestests/src/com/android/server/am/MockingOomAdjusterTests.java
@@ -3337,6 +3337,108 @@ public class MockingOomAdjusterTests {
followUpTimeCaptor.capture());
}
+ /**
+ * For Perceptible Tasks adjustment, this solely unit-tests OomAdjuster -> onOtherActivity()
+ */
+ @SuppressWarnings("GuardedBy")
+ @Test
+ @EnableFlags(Flags.FLAG_PERCEPTIBLE_TASKS)
+ public void testPerceptibleAdjustment() {
+ ProcessRecord app = spy(makeDefaultProcessRecord(MOCKAPP_PID, MOCKAPP_UID,
+ MOCKAPP_PROCESSNAME, MOCKAPP_PACKAGENAME, true));
+
+ long now = mInjector.getUptimeMillis();
+
+ // GIVEN: perceptible adjustment is NOT enabled (perceptible stop time is not set)
+ // EXPECT: zero adjustment
+ // TLDR: App is not set as a perceptible task and hence no oom_adj boosting.
+ mService.mOomAdjuster.mTmpComputeOomAdjWindowCallback.initialize(app, CACHED_APP_MIN_ADJ,
+ false, false, PROCESS_STATE_CACHED_ACTIVITY,
+ SCHED_GROUP_DEFAULT, 0, 0, PROCESS_STATE_IMPORTANT_FOREGROUND);
+ mService.mOomAdjuster.mTmpComputeOomAdjWindowCallback.onOtherActivity(-1);
+ assertEquals(CACHED_APP_MIN_ADJ, mService.mOomAdjuster.mTmpComputeOomAdjWindowCallback.adj);
+
+ // GIVEN: perceptible adjustment is enabled (perceptible stop time is set) and
+ // elapsed time < PERCEPTIBLE_TASK_TIMEOUT
+ // EXPECT: adjustment to PERCEPTIBLE_MEDIUM_APP_ADJ
+ // TLDR: App is a perceptible task (e.g. opened from launcher) and has oom_adj boosting.
+ mService.mOomAdjuster.mTmpComputeOomAdjWindowCallback.initialize(app, CACHED_APP_MIN_ADJ,
+ false, false, PROCESS_STATE_CACHED_ACTIVITY,
+ SCHED_GROUP_DEFAULT, 0, 0, PROCESS_STATE_IMPORTANT_FOREGROUND);
+ mInjector.reset();
+ mService.mOomAdjuster.mTmpComputeOomAdjWindowCallback.onOtherActivity(now);
+ assertEquals(PERCEPTIBLE_MEDIUM_APP_ADJ,
+ mService.mOomAdjuster.mTmpComputeOomAdjWindowCallback.adj);
+
+ // GIVEN: perceptible adjustment is enabled (perceptible stop time is set) and
+ // elapsed time > PERCEPTIBLE_TASK_TIMEOUT
+ // EXPECT: adjustment to PREVIOUS_APP_ADJ
+ // TLDR: App is a perceptible task (e.g. opened from launcher) and has oom_adj boosting, but
+ // time has elapsed and has dropped to a lower boosting of PREVIOUS_APP_ADJ
+ mService.mOomAdjuster.mTmpComputeOomAdjWindowCallback.initialize(app, CACHED_APP_MIN_ADJ,
+ false, false, PROCESS_STATE_CACHED_ACTIVITY,
+ SCHED_GROUP_DEFAULT, 0, 0, PROCESS_STATE_IMPORTANT_FOREGROUND);
+ mInjector.jumpUptimeAheadTo(OomAdjuster.PERCEPTIBLE_TASK_TIMEOUT_MILLIS + 1000);
+ mService.mOomAdjuster.mTmpComputeOomAdjWindowCallback.onOtherActivity(0);
+ assertEquals(PREVIOUS_APP_ADJ, mService.mOomAdjuster.mTmpComputeOomAdjWindowCallback.adj);
+ }
+
+ /**
+ * For Perceptible Tasks adjustment, this tests overall adjustment flow.
+ */
+ @SuppressWarnings("GuardedBy")
+ @Test
+ @EnableFlags(Flags.FLAG_PERCEPTIBLE_TASKS)
+ public void testUpdateOomAdjPerceptible() {
+ ProcessRecord app = spy(makeDefaultProcessRecord(MOCKAPP_PID, MOCKAPP_UID,
+ MOCKAPP_PROCESSNAME, MOCKAPP_PACKAGENAME, true));
+ WindowProcessController wpc = app.getWindowProcessController();
+
+ // Set uptime to be at least the timeout time + buffer, so that we don't end up with
+ // negative stopTime in our test input
+ mInjector.jumpUptimeAheadTo(OomAdjuster.PERCEPTIBLE_TASK_TIMEOUT_MILLIS + 60L * 1000L);
+ long now = mInjector.getUptimeMillis();
+ doReturn(true).when(wpc).hasActivities();
+
+ // GIVEN: perceptible adjustment is is enabled
+ // EXPECT: perceptible-act adjustment
+ doReturn(WindowProcessController.ACTIVITY_STATE_FLAG_IS_STOPPING_FINISHING)
+ .when(wpc).getActivityStateFlags();
+ doReturn(now).when(wpc).getPerceptibleTaskStoppedTimeMillis();
+ updateOomAdj(app);
+ assertProcStates(app, PROCESS_STATE_IMPORTANT_BACKGROUND, PERCEPTIBLE_MEDIUM_APP_ADJ,
+ SCHED_GROUP_BACKGROUND, "perceptible-act");
+
+ // GIVEN: perceptible adjustment is is enabled and timeout has been reached
+ // EXPECT: stale-perceptible-act adjustment
+ doReturn(WindowProcessController.ACTIVITY_STATE_FLAG_IS_STOPPING_FINISHING)
+ .when(wpc).getActivityStateFlags();
+
+ doReturn(now - OomAdjuster.PERCEPTIBLE_TASK_TIMEOUT_MILLIS).when(
+ wpc).getPerceptibleTaskStoppedTimeMillis();
+ updateOomAdj(app);
+ assertProcStates(app, PROCESS_STATE_LAST_ACTIVITY, PREVIOUS_APP_ADJ,
+ SCHED_GROUP_BACKGROUND, "stale-perceptible-act");
+
+ // GIVEN: perceptible adjustment is is disabled
+ // EXPECT: no perceptible adjustment
+ doReturn(WindowProcessController.ACTIVITY_STATE_FLAG_IS_STOPPING_FINISHING)
+ .when(wpc).getActivityStateFlags();
+ doReturn(Long.MIN_VALUE).when(wpc).getPerceptibleTaskStoppedTimeMillis();
+ updateOomAdj(app);
+ assertProcStates(app, PROCESS_STATE_CACHED_ACTIVITY, CACHED_APP_MIN_ADJ,
+ SCHED_GROUP_BACKGROUND, "cch-act");
+
+ // GIVEN: perceptible app is in foreground
+ // EXPECT: no perceptible adjustment
+ doReturn(WindowProcessController.ACTIVITY_STATE_FLAG_IS_VISIBLE)
+ .when(wpc).getActivityStateFlags();
+ doReturn(now).when(wpc).getPerceptibleTaskStoppedTimeMillis();
+ updateOomAdj(app);
+ assertProcStates(app, PROCESS_STATE_TOP, VISIBLE_APP_ADJ,
+ SCHED_GROUP_DEFAULT, "vis-activity");
+ }
+
@SuppressWarnings("GuardedBy")
@Test
public void testUpdateOomAdj_DoAll_Multiple_Provider_Retention() {
diff --git a/services/tests/mockingservicestests/src/com/android/server/wallpaper/WallpaperManagerServiceTests.java b/services/tests/mockingservicestests/src/com/android/server/wallpaper/WallpaperManagerServiceTests.java
index bc04fd94c719..49f2e9d70287 100644
--- a/services/tests/mockingservicestests/src/com/android/server/wallpaper/WallpaperManagerServiceTests.java
+++ b/services/tests/mockingservicestests/src/com/android/server/wallpaper/WallpaperManagerServiceTests.java
@@ -258,6 +258,8 @@ public class WallpaperManagerServiceTests {
spyOn(mIpm);
spyOn(mResources);
doReturn(true).when(mResources).getBoolean(eq(R.bool.config_isDesktopModeSupported));
+ doReturn(true).when(mResources).getBoolean(
+ eq(R.bool.config_canInternalDisplayHostDesktops));
mService = new TestWallpaperManagerService(sContext);
spyOn(mService);
mService.systemReady();
diff --git a/services/tests/powerstatstests/src/com/android/server/power/stats/processor/AggregatedPowerStatsTest.java b/services/tests/powerstatstests/src/com/android/server/power/stats/processor/AggregatedPowerStatsTest.java
index 0e73329dcfe5..978fd1af2636 100644
--- a/services/tests/powerstatstests/src/com/android/server/power/stats/processor/AggregatedPowerStatsTest.java
+++ b/services/tests/powerstatstests/src/com/android/server/power/stats/processor/AggregatedPowerStatsTest.java
@@ -18,6 +18,7 @@ package com.android.server.power.stats.processor;
import static com.google.common.truth.Truth.assertThat;
+import android.annotation.SuppressLint;
import android.os.BatteryConsumer;
import android.os.PersistableBundle;
import android.util.SparseArray;
@@ -280,6 +281,7 @@ public class AggregatedPowerStatsTest {
.isEqualTo(new long[]{250, 300});
}
+ @SuppressLint("CheckResult")
private static long[] getDeviceStats(
AggregatedPowerStats stats, int powerComponentId,
int... states) {
@@ -290,6 +292,7 @@ public class AggregatedPowerStatsTest {
return out;
}
+ @SuppressLint("CheckResult")
private static long[] getStateStats(
AggregatedPowerStats stats, int key, int... states) {
PowerComponentAggregatedPowerStats powerComponentStats =
@@ -299,6 +302,7 @@ public class AggregatedPowerStatsTest {
return out;
}
+ @SuppressLint("CheckResult")
private static long[] getUidDeviceStats(
AggregatedPowerStats stats, int powerComponentId,
int uid, int... states) {
diff --git a/services/tests/powerstatstests/src/com/android/server/power/stats/processor/AmbientDisplayPowerStatsProcessorTest.java b/services/tests/powerstatstests/src/com/android/server/power/stats/processor/AmbientDisplayPowerStatsProcessorTest.java
index 21e615f8c740..58e9d1e26f2c 100644
--- a/services/tests/powerstatstests/src/com/android/server/power/stats/processor/AmbientDisplayPowerStatsProcessorTest.java
+++ b/services/tests/powerstatstests/src/com/android/server/power/stats/processor/AmbientDisplayPowerStatsProcessorTest.java
@@ -27,6 +27,7 @@ import static com.google.common.truth.Truth.assertThat;
import static org.mockito.Mockito.when;
+import android.annotation.SuppressLint;
import android.hardware.power.stats.EnergyConsumerType;
import android.os.BatteryConsumer;
import android.os.Handler;
@@ -166,6 +167,7 @@ public class AmbientDisplayPowerStatsProcessorTest {
return stats.getPowerComponentStats(BatteryConsumer.POWER_COMPONENT_AMBIENT_DISPLAY);
}
+ @SuppressLint("CheckResult")
private void assertPowerEstimate(
PowerComponentAggregatedPowerStats aggregatedStats,
int powerState, int screenState, double expectedPowerEstimate) {
diff --git a/services/tests/powerstatstests/src/com/android/server/power/stats/processor/BasePowerStatsProcessorTest.java b/services/tests/powerstatstests/src/com/android/server/power/stats/processor/BasePowerStatsProcessorTest.java
index cca60339acf7..58784d7b4a25 100644
--- a/services/tests/powerstatstests/src/com/android/server/power/stats/processor/BasePowerStatsProcessorTest.java
+++ b/services/tests/powerstatstests/src/com/android/server/power/stats/processor/BasePowerStatsProcessorTest.java
@@ -34,6 +34,7 @@ import static com.google.common.truth.Truth.assertThat;
import static org.mockito.Mockito.mock;
+import android.annotation.SuppressLint;
import android.os.BatteryConsumer;
import android.os.BatteryUsageStats;
import android.os.Process;
@@ -68,6 +69,7 @@ public class BasePowerStatsProcessorTest {
.setProcessorSupplier(() -> new BasePowerStatsProcessor(() -> 4000));
}
+ @SuppressLint("CheckResult")
@Test
public void processPowerStats() {
AggregatedPowerStats aggregatedPowerStats = prepareAggregatedPowerStats(true);
@@ -95,9 +97,11 @@ public class BasePowerStatsProcessorTest {
stats.getUidStats(uidStats, APP_UID1,
states(POWER_STATE_BATTERY, SCREEN_STATE_OTHER, PROCESS_STATE_FOREGROUND_SERVICE));
assertThat(statsLayout.getUidUsageDuration(uidStats)).isEqualTo(5000);
- stats.getUidStats(uidStats, APP_UID1,
+ boolean nonZero = stats.getUidStats(uidStats, APP_UID1,
states(POWER_STATE_BATTERY, SCREEN_STATE_OTHER, PROCESS_STATE_UNSPECIFIED));
- assertThat(statsLayout.getUidUsageDuration(uidStats)).isEqualTo(0);
+ if (nonZero) {
+ assertThat(statsLayout.getUidUsageDuration(uidStats)).isEqualTo(0);
+ }
stats.getUidStats(uidStats, APP_UID2,
states(POWER_STATE_BATTERY, SCREEN_STATE_ON, PROCESS_STATE_CACHED));
@@ -105,11 +109,14 @@ public class BasePowerStatsProcessorTest {
stats.getUidStats(uidStats, APP_UID2,
states(POWER_STATE_BATTERY, SCREEN_STATE_OTHER, PROCESS_STATE_CACHED));
assertThat(statsLayout.getUidUsageDuration(uidStats)).isEqualTo(8500);
- stats.getUidStats(uidStats, APP_UID2,
+ nonZero = stats.getUidStats(uidStats, APP_UID2,
states(POWER_STATE_BATTERY, SCREEN_STATE_OTHER, PROCESS_STATE_UNSPECIFIED));
- assertThat(statsLayout.getUidUsageDuration(uidStats)).isEqualTo(0);
+ if (nonZero) {
+ assertThat(statsLayout.getUidUsageDuration(uidStats)).isEqualTo(0);
+ }
}
+ @SuppressLint("CheckResult")
@Test
public void fuelgaugeAvailable() {
AggregatedPowerStats aggregatedPowerStats = prepareAggregatedPowerStats(true);
@@ -138,6 +145,7 @@ public class BasePowerStatsProcessorTest {
assertThat(dischargeDuration).isWithin(5).of(6000);
}
+ @SuppressLint("CheckResult")
@Test
public void fuelgaugeUnavailable() {
AggregatedPowerStats aggregatedPowerStats = prepareAggregatedPowerStats(false);
diff --git a/services/tests/powerstatstests/src/com/android/server/power/stats/processor/BinaryStatePowerStatsProcessorTest.java b/services/tests/powerstatstests/src/com/android/server/power/stats/processor/BinaryStatePowerStatsProcessorTest.java
index 2ff7dde3255f..e6e7f6e105b7 100644
--- a/services/tests/powerstatstests/src/com/android/server/power/stats/processor/BinaryStatePowerStatsProcessorTest.java
+++ b/services/tests/powerstatstests/src/com/android/server/power/stats/processor/BinaryStatePowerStatsProcessorTest.java
@@ -30,6 +30,7 @@ import static com.android.server.power.stats.processor.AggregatedPowerStatsConfi
import static com.google.common.truth.Truth.assertThat;
+import android.annotation.SuppressLint;
import android.os.BatteryConsumer;
import android.os.BatteryStats;
import android.os.PersistableBundle;
@@ -74,6 +75,7 @@ public class BinaryStatePowerStatsProcessorTest {
}
}
+ @SuppressLint("CheckResult")
@Test
public void powerProfileModel() {
BinaryStatePowerStatsLayout statsLayout = new BinaryStatePowerStatsLayout();
@@ -138,12 +140,14 @@ public class BinaryStatePowerStatsProcessorTest {
assertThat(statsLayout.getUidPowerEstimate(uidStats))
.isWithin(PRECISION).of(expectedPower2);
- stats.getUidStats(uidStats, APP_UID2,
- states(POWER_STATE_OTHER, SCREEN_STATE_ON, PROCESS_STATE_CACHED));
- assertThat(statsLayout.getUidPowerEstimate(uidStats))
- .isWithin(PRECISION).of(0);
+ if (stats.getUidStats(uidStats, APP_UID2,
+ states(POWER_STATE_OTHER, SCREEN_STATE_ON, PROCESS_STATE_CACHED))) {
+ assertThat(statsLayout.getUidPowerEstimate(uidStats))
+ .isWithin(PRECISION).of(0);
+ }
}
+ @SuppressLint("CheckResult")
@Test
public void energyConsumerModel() {
BinaryStatePowerStatsLayout
@@ -232,10 +236,11 @@ public class BinaryStatePowerStatsProcessorTest {
assertThat(statsLayout.getUidPowerEstimate(uidStats))
.isWithin(PRECISION).of(expectedPower2);
- stats.getUidStats(uidStats, APP_UID2,
- states(POWER_STATE_OTHER, SCREEN_STATE_ON, PROCESS_STATE_CACHED));
- assertThat(statsLayout.getUidPowerEstimate(uidStats))
- .isWithin(PRECISION).of(0);
+ if (stats.getUidStats(uidStats, APP_UID2,
+ states(POWER_STATE_OTHER, SCREEN_STATE_ON, PROCESS_STATE_CACHED))) {
+ assertThat(statsLayout.getUidPowerEstimate(uidStats))
+ .isWithin(PRECISION).of(0);
+ }
}
diff --git a/services/tests/powerstatstests/src/com/android/server/power/stats/processor/BluetoothPowerStatsProcessorTest.java b/services/tests/powerstatstests/src/com/android/server/power/stats/processor/BluetoothPowerStatsProcessorTest.java
index 60131861ce89..6d7119dc1f0e 100644
--- a/services/tests/powerstatstests/src/com/android/server/power/stats/processor/BluetoothPowerStatsProcessorTest.java
+++ b/services/tests/powerstatstests/src/com/android/server/power/stats/processor/BluetoothPowerStatsProcessorTest.java
@@ -33,6 +33,7 @@ import static com.google.common.truth.Truth.assertThat;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.when;
+import android.annotation.SuppressLint;
import android.bluetooth.BluetoothActivityEnergyInfo;
import android.bluetooth.BluetoothAdapter;
import android.bluetooth.UidTraffic;
@@ -161,6 +162,7 @@ public class BluetoothPowerStatsProcessorTest {
when(mPackageManager.hasSystemFeature(PackageManager.FEATURE_BLUETOOTH)).thenReturn(true);
}
+ @SuppressLint("CheckResult")
@Test
public void powerProfileModel_mostlyDataTransfer() {
// No power monitoring hardware
@@ -262,6 +264,7 @@ public class BluetoothPowerStatsProcessorTest {
.isWithin(PRECISION).of(expectedPower2 * 0.75);
}
+ @SuppressLint("CheckResult")
@Test
public void powerProfileModel_mostlyScan() {
// No power monitoring hardware
@@ -361,6 +364,7 @@ public class BluetoothPowerStatsProcessorTest {
.isWithin(PRECISION).of(expectedPower2 * 0.75);
}
+ @SuppressLint("CheckResult")
@Test
public void consumedEnergyModel() {
when(mConsumedEnergyRetriever.getVoltageMv()).thenReturn(VOLTAGE_MV);
diff --git a/services/tests/powerstatstests/src/com/android/server/power/stats/processor/CameraPowerStatsTest.java b/services/tests/powerstatstests/src/com/android/server/power/stats/processor/CameraPowerStatsTest.java
index 23642de466a1..a95963242d8f 100644
--- a/services/tests/powerstatstests/src/com/android/server/power/stats/processor/CameraPowerStatsTest.java
+++ b/services/tests/powerstatstests/src/com/android/server/power/stats/processor/CameraPowerStatsTest.java
@@ -33,6 +33,7 @@ import static com.google.common.truth.Truth.assertThat;
import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.Mockito.when;
+import android.annotation.SuppressLint;
import android.hardware.power.stats.EnergyConsumerResult;
import android.hardware.power.stats.EnergyConsumerType;
import android.os.BatteryConsumer;
@@ -117,6 +118,7 @@ public class CameraPowerStatsTest {
mMonotonicClock = new MonotonicClock(0, mStatsRule.getMockClock());
}
+ @SuppressLint("CheckResult")
@Test
public void energyConsumerModel() {
when(mConsumedEnergyRetriever.getVoltageMv()).thenReturn(VOLTAGE_MV);
@@ -211,10 +213,11 @@ public class CameraPowerStatsTest {
assertThat(statsLayout.getUidPowerEstimate(uidStats))
.isWithin(PRECISION).of(expectedPower2);
- stats.getUidStats(uidStats, APP_UID2,
- states(POWER_STATE_OTHER, SCREEN_STATE_ON, PROCESS_STATE_CACHED));
- assertThat(statsLayout.getUidPowerEstimate(uidStats))
- .isWithin(PRECISION).of(0);
+ if (stats.getUidStats(uidStats, APP_UID2,
+ states(POWER_STATE_OTHER, SCREEN_STATE_ON, PROCESS_STATE_CACHED))) {
+ assertThat(statsLayout.getUidPowerEstimate(uidStats))
+ .isWithin(PRECISION).of(0);
+ }
}
private BatteryStats.HistoryItem buildHistoryItem(int timestamp, boolean stateOn,
diff --git a/services/tests/powerstatstests/src/com/android/server/power/stats/processor/CustomEnergyConsumerPowerStatsTest.java b/services/tests/powerstatstests/src/com/android/server/power/stats/processor/CustomEnergyConsumerPowerStatsTest.java
index 42baba765276..a421675f1896 100644
--- a/services/tests/powerstatstests/src/com/android/server/power/stats/processor/CustomEnergyConsumerPowerStatsTest.java
+++ b/services/tests/powerstatstests/src/com/android/server/power/stats/processor/CustomEnergyConsumerPowerStatsTest.java
@@ -33,6 +33,7 @@ import static com.google.common.truth.Truth.assertThat;
import static org.mockito.Mockito.when;
+import android.annotation.SuppressLint;
import android.hardware.power.stats.EnergyConsumerAttribution;
import android.hardware.power.stats.EnergyConsumerResult;
import android.hardware.power.stats.EnergyConsumerType;
@@ -154,6 +155,7 @@ public class CustomEnergyConsumerPowerStatsTest {
.isEqualTo(6000);
}
+ @SuppressLint("CheckResult")
@Test
public void processStats() throws Exception {
AggregatedPowerStats aggregatedPowerStats = createAggregatedPowerStats();
diff --git a/services/tests/powerstatstests/src/com/android/server/power/stats/processor/GnssPowerStatsTest.java b/services/tests/powerstatstests/src/com/android/server/power/stats/processor/GnssPowerStatsTest.java
index c63267ec6ae7..b4f21133f621 100644
--- a/services/tests/powerstatstests/src/com/android/server/power/stats/processor/GnssPowerStatsTest.java
+++ b/services/tests/powerstatstests/src/com/android/server/power/stats/processor/GnssPowerStatsTest.java
@@ -33,6 +33,7 @@ import static com.google.common.truth.Truth.assertThat;
import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.Mockito.when;
+import android.annotation.SuppressLint;
import android.hardware.power.stats.EnergyConsumerResult;
import android.hardware.power.stats.EnergyConsumerType;
import android.location.GnssSignalQuality;
@@ -122,6 +123,7 @@ public class GnssPowerStatsTest {
mHistoryItem.clear();
}
+ @SuppressLint("CheckResult")
@Test
public void powerProfileModel() {
// ODPM unsupported
@@ -206,12 +208,14 @@ public class GnssPowerStatsTest {
assertThat(statsLayout.getUidPowerEstimate(uidStats))
.isWithin(PRECISION).of(0.51111);
- stats.getUidStats(uidStats, APP_UID2,
- states(POWER_STATE_OTHER, SCREEN_STATE_ON, PROCESS_STATE_CACHED));
- assertThat(statsLayout.getUidPowerEstimate(uidStats))
- .isWithin(PRECISION).of(0);
+ if (stats.getUidStats(uidStats, APP_UID2,
+ states(POWER_STATE_OTHER, SCREEN_STATE_ON, PROCESS_STATE_CACHED))) {
+ assertThat(statsLayout.getUidPowerEstimate(uidStats))
+ .isWithin(PRECISION).of(0);
+ }
}
+ @SuppressLint("CheckResult")
@Test
public void initialStateGnssOn() {
// ODPM unsupported
@@ -285,12 +289,14 @@ public class GnssPowerStatsTest {
assertThat(statsLayout.getUidPowerEstimate(uidStats))
.isWithin(PRECISION).of(0.51111);
- stats.getUidStats(uidStats, APP_UID2,
- states(POWER_STATE_OTHER, SCREEN_STATE_ON, PROCESS_STATE_CACHED));
- assertThat(statsLayout.getUidPowerEstimate(uidStats))
- .isWithin(PRECISION).of(0);
+ if (stats.getUidStats(uidStats, APP_UID2,
+ states(POWER_STATE_OTHER, SCREEN_STATE_ON, PROCESS_STATE_CACHED))) {
+ assertThat(statsLayout.getUidPowerEstimate(uidStats))
+ .isWithin(PRECISION).of(0);
+ }
}
+ @SuppressLint("CheckResult")
@Test
public void energyConsumerModel() {
when(mConsumedEnergyRetriever.getVoltageMv()).thenReturn(VOLTAGE_MV);
@@ -386,10 +392,11 @@ public class GnssPowerStatsTest {
assertThat(statsLayout.getUidPowerEstimate(uidStats))
.isWithin(PRECISION).of(expectedPower2);
- stats.getUidStats(uidStats, APP_UID2,
- states(POWER_STATE_OTHER, SCREEN_STATE_ON, PROCESS_STATE_CACHED));
- assertThat(statsLayout.getUidPowerEstimate(uidStats))
- .isWithin(PRECISION).of(0);
+ if (stats.getUidStats(uidStats, APP_UID2,
+ states(POWER_STATE_OTHER, SCREEN_STATE_ON, PROCESS_STATE_CACHED))) {
+ assertThat(statsLayout.getUidPowerEstimate(uidStats))
+ .isWithin(PRECISION).of(0);
+ }
}
private BatteryStats.HistoryItem buildHistoryItemInitialStateGpsOn(long timestamp) {
diff --git a/services/tests/powerstatstests/src/com/android/server/power/stats/processor/MobileRadioPowerStatsProcessorTest.java b/services/tests/powerstatstests/src/com/android/server/power/stats/processor/MobileRadioPowerStatsProcessorTest.java
index 6acd36885b1e..3dc401769e7d 100644
--- a/services/tests/powerstatstests/src/com/android/server/power/stats/processor/MobileRadioPowerStatsProcessorTest.java
+++ b/services/tests/powerstatstests/src/com/android/server/power/stats/processor/MobileRadioPowerStatsProcessorTest.java
@@ -39,6 +39,7 @@ import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.when;
import android.annotation.Nullable;
+import android.annotation.SuppressLint;
import android.content.Context;
import android.content.pm.PackageManager;
import android.hardware.power.stats.EnergyConsumerResult;
@@ -170,6 +171,7 @@ public class MobileRadioPowerStatsProcessorTest {
.thenAnswer(invocation -> invocation.getArgument(0));
}
+ @SuppressLint("CheckResult")
@Test
public void powerProfileModel() {
// No power monitoring hardware
diff --git a/services/tests/powerstatstests/src/com/android/server/power/stats/processor/MultiStateStatsTest.java b/services/tests/powerstatstests/src/com/android/server/power/stats/processor/MultiStateStatsTest.java
index 3b614bdb38ed..9abb06f338e3 100644
--- a/services/tests/powerstatstests/src/com/android/server/power/stats/processor/MultiStateStatsTest.java
+++ b/services/tests/powerstatstests/src/com/android/server/power/stats/processor/MultiStateStatsTest.java
@@ -21,6 +21,7 @@ import static com.google.common.truth.Truth.assertWithMessage;
import static org.junit.Assert.assertThrows;
+import android.annotation.SuppressLint;
import android.os.BatteryConsumer;
import androidx.test.filters.SmallTest;
@@ -124,6 +125,7 @@ public class MultiStateStatsTest {
assertThat(e.getMessage()).contains("40");
}
+ @SuppressLint("CheckResult")
@Test
public void multiStateStats_aggregation() {
MultiStateStats.Factory factory = makeFactory(true, true, false);
@@ -159,9 +161,9 @@ public class MultiStateStatsTest {
// (400 - 100) * 0 + (600 - 400) * 0.5
assertThat(stats).isEqualTo(new long[]{100, 100});
- multiStateStats.getStats(stats, new int[]{1, BatteryConsumer.PROCESS_STATE_BACKGROUND, 0});
// Never been in this composite state
- assertThat(stats).isEqualTo(new long[]{0, 0});
+ assertThat(multiStateStats.getStats(stats,
+ new int[]{1, BatteryConsumer.PROCESS_STATE_BACKGROUND, 0})).isFalse();
}
@Test
diff --git a/services/tests/powerstatstests/src/com/android/server/power/stats/processor/PhoneCallPowerStatsProcessorTest.java b/services/tests/powerstatstests/src/com/android/server/power/stats/processor/PhoneCallPowerStatsProcessorTest.java
index a20274fb5ded..2f742d74d8e6 100644
--- a/services/tests/powerstatstests/src/com/android/server/power/stats/processor/PhoneCallPowerStatsProcessorTest.java
+++ b/services/tests/powerstatstests/src/com/android/server/power/stats/processor/PhoneCallPowerStatsProcessorTest.java
@@ -29,6 +29,7 @@ import static org.mockito.ArgumentMatchers.anyInt;
import static org.mockito.Mockito.doAnswer;
import static org.mockito.Mockito.when;
+import android.annotation.SuppressLint;
import android.content.Context;
import android.content.pm.PackageManager;
import android.hardware.power.stats.EnergyConsumerType;
@@ -159,6 +160,7 @@ public class PhoneCallPowerStatsProcessorTest {
mStatsRule.setTestPowerProfile("power_profile_test_legacy_modem");
}
+ @SuppressLint("CheckResult")
@Test
public void copyEstimatesFromMobileRadioPowerStats() {
AggregatedPowerStatsConfig config = new AggregatedPowerStatsConfig();
diff --git a/services/tests/powerstatstests/src/com/android/server/power/stats/processor/ScreenPowerStatsProcessorTest.java b/services/tests/powerstatstests/src/com/android/server/power/stats/processor/ScreenPowerStatsProcessorTest.java
index 185216583f1b..31456a1574d0 100644
--- a/services/tests/powerstatstests/src/com/android/server/power/stats/processor/ScreenPowerStatsProcessorTest.java
+++ b/services/tests/powerstatstests/src/com/android/server/power/stats/processor/ScreenPowerStatsProcessorTest.java
@@ -29,6 +29,7 @@ import static org.mockito.ArgumentMatchers.any;
import static org.mockito.Mockito.doAnswer;
import static org.mockito.Mockito.when;
+import android.annotation.SuppressLint;
import android.hardware.power.stats.EnergyConsumerResult;
import android.hardware.power.stats.EnergyConsumerType;
import android.os.BatteryConsumer;
@@ -278,6 +279,7 @@ public class ScreenPowerStatsProcessorTest {
.of(expectedDozePowerEstimate);
}
+ @SuppressLint("CheckResult")
private void assertUidPowerEstimate(
PowerComponentAggregatedPowerStats aggregatedStats, int uid,
int powerState, int screenState, double expectedScreenPowerEstimate) {
diff --git a/services/tests/powerstatstests/src/com/android/server/power/stats/processor/SensorPowerStatsProcessorTest.java b/services/tests/powerstatstests/src/com/android/server/power/stats/processor/SensorPowerStatsProcessorTest.java
index d97260455bdd..c2f01d1fa65c 100644
--- a/services/tests/powerstatstests/src/com/android/server/power/stats/processor/SensorPowerStatsProcessorTest.java
+++ b/services/tests/powerstatstests/src/com/android/server/power/stats/processor/SensorPowerStatsProcessorTest.java
@@ -33,6 +33,7 @@ import static com.google.common.truth.Truth.assertThat;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.when;
+import android.annotation.SuppressLint;
import android.hardware.Sensor;
import android.hardware.SensorManager;
import android.hardware.input.InputSensorInfo;
@@ -91,6 +92,7 @@ public class SensorPowerStatsProcessorTest {
List.of(sensor1, sensor2, sensor3));
}
+ @SuppressLint("CheckResult")
@Test
public void testPowerEstimation() {
PowerComponentAggregatedPowerStats stats = createAggregatedPowerStats(
diff --git a/services/tests/powerstatstests/src/com/android/server/power/stats/processor/WakelockPowerStatsProcessorTest.java b/services/tests/powerstatstests/src/com/android/server/power/stats/processor/WakelockPowerStatsProcessorTest.java
index 8257d714a5d5..5ac7216194a4 100644
--- a/services/tests/powerstatstests/src/com/android/server/power/stats/processor/WakelockPowerStatsProcessorTest.java
+++ b/services/tests/powerstatstests/src/com/android/server/power/stats/processor/WakelockPowerStatsProcessorTest.java
@@ -31,6 +31,7 @@ import static com.google.common.truth.Truth.assertThat;
import static org.mockito.Mockito.mock;
+import android.annotation.SuppressLint;
import android.os.BatteryConsumer;
import android.os.PersistableBundle;
import android.os.Process;
@@ -123,6 +124,7 @@ public class WakelockPowerStatsProcessorTest {
return history;
}
+ @SuppressLint("CheckResult")
private void assertAggregatedPowerStats(AggregatedPowerStats aggregatedPowerStats) {
PowerComponentAggregatedPowerStats stats =
aggregatedPowerStats.getPowerComponentStats(POWER_COMPONENT_WAKELOCK);
diff --git a/services/tests/powerstatstests/src/com/android/server/power/stats/processor/WifiPowerStatsProcessorTest.java b/services/tests/powerstatstests/src/com/android/server/power/stats/processor/WifiPowerStatsProcessorTest.java
index bd92a84fc815..e36056a98c85 100644
--- a/services/tests/powerstatstests/src/com/android/server/power/stats/processor/WifiPowerStatsProcessorTest.java
+++ b/services/tests/powerstatstests/src/com/android/server/power/stats/processor/WifiPowerStatsProcessorTest.java
@@ -39,6 +39,7 @@ import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.when;
import android.annotation.Nullable;
+import android.annotation.SuppressLint;
import android.content.Context;
import android.content.pm.PackageManager;
import android.hardware.power.stats.EnergyConsumerResult;
@@ -198,6 +199,7 @@ public class WifiPowerStatsProcessorTest {
mBatteryStats = mStatsRule.getBatteryStats();
}
+ @SuppressLint("CheckResult")
@Test
public void powerProfileModel_powerController() {
when(mWifiManager.isEnhancedPowerReportingSupported()).thenReturn(true);
@@ -310,6 +312,7 @@ public class WifiPowerStatsProcessorTest {
.isWithin(PRECISION).of(expectedPower2 * 0.75);
}
+ @SuppressLint("CheckResult")
@Test
public void consumedEnergyModel_powerController() {
when(mWifiManager.isEnhancedPowerReportingSupported()).thenReturn(true);
@@ -424,6 +427,7 @@ public class WifiPowerStatsProcessorTest {
.isWithin(PRECISION).of(expectedPower2 * 0.75);
}
+ @SuppressLint("CheckResult")
@Test
public void powerProfileModel_noPowerController() {
when(mWifiManager.isEnhancedPowerReportingSupported()).thenReturn(false);
diff --git a/services/tests/servicestests/Android.bp b/services/tests/servicestests/Android.bp
index d702cae248a9..009ce88cfd6c 100644
--- a/services/tests/servicestests/Android.bp
+++ b/services/tests/servicestests/Android.bp
@@ -33,6 +33,9 @@ android_test {
"test-apps/DisplayManagerTestApp/src/**/*.java",
],
+ kotlincflags: [
+ "-Werror",
+ ],
static_libs: [
"a11ychecker",
"aatf",
diff --git a/services/tests/servicestests/src/com/android/server/accessibility/autoclick/AutoclickControllerTest.java b/services/tests/servicestests/src/com/android/server/accessibility/autoclick/AutoclickControllerTest.java
index 7f60caaa569b..0745c68fd337 100644
--- a/services/tests/servicestests/src/com/android/server/accessibility/autoclick/AutoclickControllerTest.java
+++ b/services/tests/servicestests/src/com/android/server/accessibility/autoclick/AutoclickControllerTest.java
@@ -25,6 +25,7 @@ import static com.google.common.truth.Truth.assertThat;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
import android.content.Context;
import android.platform.test.annotations.DisableFlags;
@@ -400,6 +401,46 @@ public class AutoclickControllerTest {
.isNotEqualTo(initialScheduledTime);
}
+ @Test
+ @EnableFlags(com.android.server.accessibility.Flags.FLAG_ENABLE_AUTOCLICK_INDICATOR)
+ public void pauseButton_flagOn_clickNotTriggeredWhenPaused() {
+ injectFakeMouseActionHoverMoveEvent();
+
+ // Pause autoclick.
+ AutoclickTypePanel mockAutoclickTypePanel = mock(AutoclickTypePanel.class);
+ when(mockAutoclickTypePanel.isPaused()).thenReturn(true);
+ mController.mAutoclickTypePanel = mockAutoclickTypePanel;
+
+ // Send hover move event.
+ MotionEvent hoverMove = MotionEvent.obtain(
+ /* downTime= */ 0,
+ /* eventTime= */ 100,
+ /* action= */ MotionEvent.ACTION_HOVER_MOVE,
+ /* x= */ 30f,
+ /* y= */ 0f,
+ /* metaState= */ 0);
+ hoverMove.setSource(InputDevice.SOURCE_MOUSE);
+ mController.onMotionEvent(hoverMove, hoverMove, /* policyFlags= */ 0);
+
+ // Verify there is not a pending click.
+ assertThat(mController.mClickScheduler.getIsActiveForTesting()).isFalse();
+ assertThat(mController.mClickScheduler.getScheduledClickTimeForTesting()).isEqualTo(-1);
+
+ // Resume autoclick.
+ when(mockAutoclickTypePanel.isPaused()).thenReturn(false);
+
+ // Send initial move event again. Because this is the first recorded move, a click won't be
+ // scheduled.
+ injectFakeMouseActionHoverMoveEvent();
+ assertThat(mController.mClickScheduler.getIsActiveForTesting()).isFalse();
+ assertThat(mController.mClickScheduler.getScheduledClickTimeForTesting()).isEqualTo(-1);
+
+ // Send move again to trigger click and verify there is now a pending click.
+ mController.onMotionEvent(hoverMove, hoverMove, /* policyFlags= */ 0);
+ assertThat(mController.mClickScheduler.getIsActiveForTesting()).isTrue();
+ assertThat(mController.mClickScheduler.getScheduledClickTimeForTesting()).isNotEqualTo(-1);
+ }
+
private void injectFakeMouseActionHoverMoveEvent() {
MotionEvent event = getFakeMotionHoverMoveEvent();
event.setSource(InputDevice.SOURCE_MOUSE);
diff --git a/services/tests/servicestests/src/com/android/server/accessibility/autoclick/AutoclickTypePanelTest.java b/services/tests/servicestests/src/com/android/server/accessibility/autoclick/AutoclickTypePanelTest.java
index f3016f4b17f9..c60c4b60d081 100644
--- a/services/tests/servicestests/src/com/android/server/accessibility/autoclick/AutoclickTypePanelTest.java
+++ b/services/tests/servicestests/src/com/android/server/accessibility/autoclick/AutoclickTypePanelTest.java
@@ -21,6 +21,9 @@ import static androidx.test.platform.app.InstrumentationRegistry.getInstrumentat
import static com.android.server.accessibility.autoclick.AutoclickTypePanel.AUTOCLICK_TYPE_LEFT_CLICK;
import static com.android.server.accessibility.autoclick.AutoclickTypePanel.AUTOCLICK_TYPE_SCROLL;
import static com.android.server.accessibility.autoclick.AutoclickTypePanel.AutoclickType;
+import static com.android.server.accessibility.autoclick.AutoclickTypePanel.CORNER_BOTTOM_LEFT;
+import static com.android.server.accessibility.autoclick.AutoclickTypePanel.CORNER_BOTTOM_RIGHT;
+import static com.android.server.accessibility.autoclick.AutoclickTypePanel.CORNER_TOP_RIGHT;
import static com.android.server.accessibility.autoclick.AutoclickTypePanel.ClickPanelControllerInterface;
import static com.google.common.truth.Truth.assertThat;
@@ -31,6 +34,7 @@ import android.testing.AndroidTestingRunner;
import android.testing.TestableContext;
import android.testing.TestableLooper;
import android.view.Gravity;
+import android.view.MotionEvent;
import android.view.View;
import android.view.WindowManager;
import android.widget.LinearLayout;
@@ -68,6 +72,7 @@ public class AutoclickTypePanelTest {
private LinearLayout mPositionButton;
private @AutoclickType int mActiveClickType = AUTOCLICK_TYPE_LEFT_CLICK;
+ private boolean mPaused;
private final ClickPanelControllerInterface clickPanelController =
new ClickPanelControllerInterface() {
@@ -77,7 +82,9 @@ public class AutoclickTypePanelTest {
}
@Override
- public void toggleAutoclickPause() {}
+ public void toggleAutoclickPause(boolean paused) {
+ mPaused = paused;
+ }
};
@Before
@@ -199,6 +206,116 @@ public class AutoclickTypePanelTest {
}
}
+ @Test
+ public void pauseButton_onClick() {
+ mPauseButton.callOnClick();
+ assertThat(mPaused).isTrue();
+ assertThat(mAutoclickTypePanel.isPaused()).isTrue();
+
+ mPauseButton.callOnClick();
+ assertThat(mPaused).isFalse();
+ assertThat(mAutoclickTypePanel.isPaused()).isFalse();
+ }
+
+ @Test
+ public void onTouch_dragMove_updatesPosition() {
+ View contentView = mAutoclickTypePanel.getContentViewForTesting();
+ WindowManager.LayoutParams params = mAutoclickTypePanel.getLayoutParamsForTesting();
+ int[] panelLocation = new int[2];
+ contentView.getLocationOnScreen(panelLocation);
+
+ // Define movement delta for both x and y directions.
+ int delta = 15;
+
+ // Dispatch initial down event.
+ float touchX = panelLocation[0] + 10;
+ float touchY = panelLocation[1] + 10;
+ MotionEvent downEvent = MotionEvent.obtain(
+ 0, 0,
+ MotionEvent.ACTION_DOWN, touchX, touchY, 0);
+ contentView.dispatchTouchEvent(downEvent);
+
+ // Create move event with delta, move from (x, y) to (x + delta, y + delta)
+ MotionEvent moveEvent = MotionEvent.obtain(
+ 0, 0,
+ MotionEvent.ACTION_MOVE, touchX + delta, touchY + delta, 0);
+ contentView.dispatchTouchEvent(moveEvent);
+
+ // Verify position update.
+ assertThat(mAutoclickTypePanel.getIsDraggingForTesting()).isTrue();
+ assertThat(params.gravity).isEqualTo(Gravity.LEFT | Gravity.TOP);
+ assertThat(params.x).isEqualTo(panelLocation[0] + delta);
+ assertThat(params.y).isEqualTo(panelLocation[1] + delta);
+ }
+
+ @Test
+ public void dragAndEndAtRight_snapsToRightSide() {
+ View contentView = mAutoclickTypePanel.getContentViewForTesting();
+ WindowManager.LayoutParams params = mAutoclickTypePanel.getLayoutParamsForTesting();
+ int[] panelLocation = new int[2];
+ contentView.getLocationOnScreen(panelLocation);
+
+ int screenWidth = mTestableContext.getResources().getDisplayMetrics().widthPixels;
+
+ // Verify initial corner is bottom-right.
+ assertThat(mAutoclickTypePanel.getCurrentCornerIndexForTesting())
+ .isEqualTo(CORNER_BOTTOM_RIGHT);
+
+ dispatchDragSequence(contentView,
+ /* startX =*/ panelLocation[0] + 10, /* startY =*/ panelLocation[1] + 10,
+ /* endX =*/ (float) (screenWidth * 3) / 4, /* endY =*/ panelLocation[1] + 10);
+
+ // Verify snapping to the right.
+ assertThat(params.gravity).isEqualTo(Gravity.END | Gravity.TOP);
+ assertThat(mAutoclickTypePanel.getCurrentCornerIndexForTesting())
+ .isEqualTo(CORNER_TOP_RIGHT);
+ }
+
+ @Test
+ public void dragAndEndAtLeft_snapsToLeftSide() {
+ View contentView = mAutoclickTypePanel.getContentViewForTesting();
+ WindowManager.LayoutParams params = mAutoclickTypePanel.getLayoutParamsForTesting();
+ int[] panelLocation = new int[2];
+ contentView.getLocationOnScreen(panelLocation);
+
+ int screenWidth = mTestableContext.getResources().getDisplayMetrics().widthPixels;
+
+ // Verify initial corner is bottom-right.
+ assertThat(mAutoclickTypePanel.getCurrentCornerIndexForTesting())
+ .isEqualTo(CORNER_BOTTOM_RIGHT);
+
+ dispatchDragSequence(contentView,
+ /* startX =*/ panelLocation[0] + 10, /* startY =*/ panelLocation[1] + 10,
+ /* endX =*/ (float) screenWidth / 4, /* endY =*/ panelLocation[1] + 10);
+
+ // Verify snapping to the left.
+ assertThat(params.gravity).isEqualTo(Gravity.START | Gravity.TOP);
+ assertThat(mAutoclickTypePanel.getCurrentCornerIndexForTesting())
+ .isEqualTo(CORNER_BOTTOM_LEFT);
+ }
+
+ // Helper method to handle drag event sequences
+ private void dispatchDragSequence(View view, float startX, float startY, float endX,
+ float endY) {
+ // Down event
+ MotionEvent downEvent = MotionEvent.obtain(0, 0, MotionEvent.ACTION_DOWN, startX, startY,
+ 0);
+ view.dispatchTouchEvent(downEvent);
+
+ // Move event
+ MotionEvent moveEvent = MotionEvent.obtain(0, 0, MotionEvent.ACTION_MOVE, endX, endY, 0);
+ view.dispatchTouchEvent(moveEvent);
+
+ // Up event
+ MotionEvent upEvent = MotionEvent.obtain(0, 0, MotionEvent.ACTION_UP, endX, endY, 0);
+ view.dispatchTouchEvent(upEvent);
+
+ // Clean up
+ downEvent.recycle();
+ moveEvent.recycle();
+ upEvent.recycle();
+ }
+
private void verifyButtonHasSelectedStyle(@NonNull LinearLayout button) {
GradientDrawable gradientDrawable = (GradientDrawable) button.getBackground();
assertThat(gradientDrawable.getColor().getDefaultColor())
@@ -206,7 +323,7 @@ public class AutoclickTypePanelTest {
}
private void verifyPanelPosition(int[] expectedPosition) {
- WindowManager.LayoutParams params = mAutoclickTypePanel.getLayoutParams();
+ WindowManager.LayoutParams params = mAutoclickTypePanel.getLayoutParamsForTesting();
assertThat(mAutoclickTypePanel.getCurrentCornerIndexForTesting()).isEqualTo(
expectedPosition[0]);
assertThat(params.gravity).isEqualTo(expectedPosition[1]);
diff --git a/services/tests/servicestests/src/com/android/server/accessibility/magnification/FullScreenMagnificationGestureHandlerTest.java b/services/tests/servicestests/src/com/android/server/accessibility/magnification/FullScreenMagnificationGestureHandlerTest.java
index 9f5dd93054c3..5c126d1f5d3f 100644
--- a/services/tests/servicestests/src/com/android/server/accessibility/magnification/FullScreenMagnificationGestureHandlerTest.java
+++ b/services/tests/servicestests/src/com/android/server/accessibility/magnification/FullScreenMagnificationGestureHandlerTest.java
@@ -311,8 +311,7 @@ public class FullScreenMagnificationGestureHandlerTest {
mMockFullScreenMagnificationVibrationHelper,
mMockMagnificationLogger,
ViewConfiguration.get(mContext),
- mMockOneFingerPanningSettingsProvider,
- new MouseEventHandler(mFullScreenMagnificationController));
+ mMockOneFingerPanningSettingsProvider);
// OverscrollHandler is only supported on watches.
// @See config_enable_a11y_fullscreen_magnification_overscroll_handler
if (isWatch()) {
@@ -482,8 +481,8 @@ public class FullScreenMagnificationGestureHandlerTest {
@Test
@RequiresFlagsDisabled(Flags.FLAG_ENABLE_MAGNIFICATION_MULTIPLE_FINGER_MULTIPLE_TAP_GESTURE)
public void testDisablingTripleTap_removesInputLag() {
- mMgh = newInstance(/* detectSingleFingerTripleTap */ false,
- /* detectTwoFingerTripleTap */ true, /* detectShortcut */ true);
+ mMgh = newInstance(/* detectSingleFingerTripleTap= */ false,
+ /* detectTwoFingerTripleTap= */ true, /* detectShortcutTrigger= */ true);
goFromStateIdleTo(STATE_IDLE);
allowEventDelegation();
tap();
@@ -494,8 +493,8 @@ public class FullScreenMagnificationGestureHandlerTest {
@Test
@RequiresFlagsEnabled(Flags.FLAG_ENABLE_MAGNIFICATION_MULTIPLE_FINGER_MULTIPLE_TAP_GESTURE)
public void testDisablingSingleFingerTripleTapAndTwoFingerTripleTap_removesInputLag() {
- mMgh = newInstance(/* detectSingleFingerTripleTap */ false,
- /* detectTwoFingerTripleTap */ false, /* detectShortcut */ true);
+ mMgh = newInstance(/* detectSingleFingerTripleTap= */ false,
+ /* detectTwoFingerTripleTap= */ false, /* detectShortcutTrigger= */ true);
goFromStateIdleTo(STATE_IDLE);
allowEventDelegation();
tap();
@@ -1420,12 +1419,6 @@ public class FullScreenMagnificationGestureHandlerTest {
}
@Test
- @RequiresFlagsDisabled(Flags.FLAG_ENABLE_MAGNIFICATION_FOLLOWS_MOUSE_BUGFIX)
- public void testMouseMoveEventsDoNotMoveMagnifierViewport() {
- runMoveEventsDoNotMoveMagnifierViewport(InputDevice.SOURCE_MOUSE);
- }
-
- @Test
public void testStylusMoveEventsDoNotMoveMagnifierViewport() {
runMoveEventsDoNotMoveMagnifierViewport(InputDevice.SOURCE_STYLUS);
}
@@ -1441,7 +1434,7 @@ public class FullScreenMagnificationGestureHandlerTest {
(INITIAL_MAGNIFICATION_BOUNDS.top + INITIAL_MAGNIFICATION_BOUNDS.height()) / 2.0f;
float scale = 5.6f; // value is unimportant but unique among tests to increase coverage.
mFullScreenMagnificationController.setScaleAndCenter(
- DISPLAY_0, centerX, centerY, scale, true, /* animate= */ false, 1);
+ DISPLAY_0, scale, centerX, centerY, true, /* animate= */ false, 1);
centerX = mFullScreenMagnificationController.getCenterX(DISPLAY_0);
centerY = mFullScreenMagnificationController.getCenterY(DISPLAY_0);
@@ -1474,55 +1467,36 @@ public class FullScreenMagnificationGestureHandlerTest {
}
@Test
- @RequiresFlagsDisabled(Flags.FLAG_ENABLE_MAGNIFICATION_FOLLOWS_MOUSE_BUGFIX)
- public void testMouseHoverMoveEventsDoNotMoveMagnifierViewport() {
- runHoverMoveEventsDoNotMoveMagnifierViewport(InputDevice.SOURCE_MOUSE);
- }
-
- @Test
- @RequiresFlagsDisabled(Flags.FLAG_ENABLE_MAGNIFICATION_FOLLOWS_MOUSE_BUGFIX)
- public void testStylusHoverMoveEventsDoNotMoveMagnifierViewport() {
- runHoverMoveEventsDoNotMoveMagnifierViewport(InputDevice.SOURCE_STYLUS);
- }
-
- @Test
- @RequiresFlagsEnabled(Flags.FLAG_ENABLE_MAGNIFICATION_FOLLOWS_MOUSE_BUGFIX)
public void testMouseHoverMoveEventsMoveMagnifierViewport() {
runHoverMovesViewportTest(InputDevice.SOURCE_MOUSE);
}
@Test
- @RequiresFlagsEnabled(Flags.FLAG_ENABLE_MAGNIFICATION_FOLLOWS_MOUSE_BUGFIX)
public void testStylusHoverMoveEventsMoveMagnifierViewport() {
runHoverMovesViewportTest(InputDevice.SOURCE_STYLUS);
}
@Test
- @RequiresFlagsEnabled(Flags.FLAG_ENABLE_MAGNIFICATION_FOLLOWS_MOUSE_BUGFIX)
public void testMouseDownEventsDoNotMoveMagnifierViewport() {
runDownDoesNotMoveViewportTest(InputDevice.SOURCE_MOUSE);
}
@Test
- @RequiresFlagsEnabled(Flags.FLAG_ENABLE_MAGNIFICATION_FOLLOWS_MOUSE_BUGFIX)
public void testStylusDownEventsDoNotMoveMagnifierViewport() {
runDownDoesNotMoveViewportTest(InputDevice.SOURCE_STYLUS);
}
@Test
- @RequiresFlagsEnabled(Flags.FLAG_ENABLE_MAGNIFICATION_FOLLOWS_MOUSE_BUGFIX)
public void testMouseUpEventsDoNotMoveMagnifierViewport() {
runUpDoesNotMoveViewportTest(InputDevice.SOURCE_MOUSE);
}
@Test
- @RequiresFlagsEnabled(Flags.FLAG_ENABLE_MAGNIFICATION_FOLLOWS_MOUSE_BUGFIX)
public void testStylusUpEventsDoNotMoveMagnifierViewport() {
runUpDoesNotMoveViewportTest(InputDevice.SOURCE_STYLUS);
}
@Test
- @RequiresFlagsEnabled(Flags.FLAG_ENABLE_MAGNIFICATION_FOLLOWS_MOUSE_BUGFIX)
public void testMouseMoveEventsMoveMagnifierViewport() {
final EventCaptor eventCaptor = new EventCaptor();
mMgh.setNext(eventCaptor);
@@ -1533,7 +1507,7 @@ public class FullScreenMagnificationGestureHandlerTest {
(INITIAL_MAGNIFICATION_BOUNDS.top + INITIAL_MAGNIFICATION_BOUNDS.height()) / 2.0f;
float scale = 6.2f; // value is unimportant but unique among tests to increase coverage.
mFullScreenMagnificationController.setScaleAndCenter(
- DISPLAY_0, centerX, centerY, scale, true, /* animate= */ false, 1);
+ DISPLAY_0, scale, centerX, centerY, true, /* animate= */ false, 1);
MotionEvent event = mouseEvent(centerX, centerY, ACTION_HOVER_MOVE);
send(event, InputDevice.SOURCE_MOUSE);
fastForward(20);
@@ -1574,7 +1548,7 @@ public class FullScreenMagnificationGestureHandlerTest {
(INITIAL_MAGNIFICATION_BOUNDS.top + INITIAL_MAGNIFICATION_BOUNDS.height()) / 2.0f;
float scale = 4.0f; // value is unimportant but unique among tests to increase coverage.
mFullScreenMagnificationController.setScaleAndCenter(
- DISPLAY_0, centerX, centerY, scale, true, /* animate= */ false, 1);
+ DISPLAY_0, scale, centerX, centerY, true, /* animate= */ false, 1);
// HOVER_MOVE should change magnifier viewport.
MotionEvent event = motionEvent(centerX + 20, centerY, ACTION_HOVER_MOVE);
@@ -1618,7 +1592,7 @@ public class FullScreenMagnificationGestureHandlerTest {
(INITIAL_MAGNIFICATION_BOUNDS.top + INITIAL_MAGNIFICATION_BOUNDS.height()) / 2.0f;
float scale = 5.3f; // value is unimportant but unique among tests to increase coverage.
mFullScreenMagnificationController.setScaleAndCenter(
- DISPLAY_0, centerX, centerY, scale, true, /* animate= */ false, 1);
+ DISPLAY_0, scale, centerX, centerY, true, /* animate= */ false, 1);
MotionEvent event = motionEvent(centerX, centerY, ACTION_HOVER_MOVE);
send(event, source);
fastForward(20);
@@ -1652,7 +1626,7 @@ public class FullScreenMagnificationGestureHandlerTest {
(INITIAL_MAGNIFICATION_BOUNDS.top + INITIAL_MAGNIFICATION_BOUNDS.height()) / 2.0f;
float scale = 2.7f; // value is unimportant but unique among tests to increase coverage.
mFullScreenMagnificationController.setScaleAndCenter(
- DISPLAY_0, centerX, centerY, scale, true, /* animate= */ false, 1);
+ DISPLAY_0, scale, centerX, centerY, true, /* animate= */ false, 1);
MotionEvent event = motionEvent(centerX, centerY, ACTION_HOVER_MOVE);
send(event, source);
fastForward(20);
@@ -1688,7 +1662,7 @@ public class FullScreenMagnificationGestureHandlerTest {
(INITIAL_MAGNIFICATION_BOUNDS.top + INITIAL_MAGNIFICATION_BOUNDS.height()) / 2.0f;
float scale = 3.8f; // value is unimportant but unique among tests to increase coverage.
mFullScreenMagnificationController.setScaleAndCenter(
- DISPLAY_0, centerX, centerY, scale, true, /* animate= */ false, 1);
+ DISPLAY_0, scale, centerX, centerY, true, /* animate= */ false, 1);
centerX = mFullScreenMagnificationController.getCenterX(DISPLAY_0);
centerY = mFullScreenMagnificationController.getCenterY(DISPLAY_0);
@@ -1725,7 +1699,7 @@ public class FullScreenMagnificationGestureHandlerTest {
(INITIAL_MAGNIFICATION_BOUNDS.top + INITIAL_MAGNIFICATION_BOUNDS.height()) / 2.0f;
float scale = 4.0f; // value is unimportant but unique among tests to increase coverage.
mFullScreenMagnificationController.setScaleAndCenter(
- DISPLAY_0, centerX, centerY, scale, true, /* animate= */ false, 1);
+ DISPLAY_0, scale, centerX, centerY, true, /* animate= */ false, 1);
centerX = mFullScreenMagnificationController.getCenterX(DISPLAY_0);
centerY = mFullScreenMagnificationController.getCenterY(DISPLAY_0);
diff --git a/services/tests/servicestests/src/com/android/server/accessibility/magnification/MagnificationGestureHandlerTest.java b/services/tests/servicestests/src/com/android/server/accessibility/magnification/MagnificationGestureHandlerTest.java
index 45c157d1c1a0..203e6554b412 100644
--- a/services/tests/servicestests/src/com/android/server/accessibility/magnification/MagnificationGestureHandlerTest.java
+++ b/services/tests/servicestests/src/com/android/server/accessibility/magnification/MagnificationGestureHandlerTest.java
@@ -21,15 +21,11 @@ import static android.view.MotionEvent.ACTION_DOWN;
import static android.view.MotionEvent.ACTION_HOVER_MOVE;
import static android.view.MotionEvent.ACTION_UP;
-import static junit.framework.Assert.assertFalse;
-
import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.Mockito.verify;
import static org.testng.AssertJUnit.assertTrue;
import android.annotation.NonNull;
-import android.platform.test.annotations.RequiresFlagsDisabled;
-import android.platform.test.annotations.RequiresFlagsEnabled;
import android.platform.test.flag.junit.CheckFlagsRule;
import android.platform.test.flag.junit.DeviceFlagsValueProvider;
import android.provider.Settings;
@@ -39,7 +35,6 @@ import android.view.MotionEvent;
import androidx.test.runner.AndroidJUnit4;
import com.android.server.accessibility.AccessibilityTraceManager;
-import com.android.server.accessibility.Flags;
import org.junit.Before;
import org.junit.Rule;
@@ -93,7 +88,6 @@ public class MagnificationGestureHandlerTest {
}
@Test
- @RequiresFlagsEnabled(Flags.FLAG_ENABLE_MAGNIFICATION_FOLLOWS_MOUSE_BUGFIX)
public void onMotionEvent_isFromMouse_handleMouseOrStylusEvent() {
final MotionEvent mouseEvent = MotionEvent.obtain(0, 0, ACTION_HOVER_MOVE, 0, 0, 0);
mouseEvent.setSource(InputDevice.SOURCE_MOUSE);
@@ -108,7 +102,6 @@ public class MagnificationGestureHandlerTest {
}
@Test
- @RequiresFlagsEnabled(Flags.FLAG_ENABLE_MAGNIFICATION_FOLLOWS_MOUSE_BUGFIX)
public void onMotionEvent_isFromStylus_handleMouseOrStylusEvent() {
final MotionEvent stylusEvent = MotionEvent.obtain(0, 0, ACTION_HOVER_MOVE, 0, 0, 0);
stylusEvent.setSource(InputDevice.SOURCE_STYLUS);
@@ -123,36 +116,6 @@ public class MagnificationGestureHandlerTest {
}
@Test
- @RequiresFlagsDisabled(Flags.FLAG_ENABLE_MAGNIFICATION_FOLLOWS_MOUSE_BUGFIX)
- public void onMotionEvent_isFromMouse_handleMouseOrStylusEventNotCalled() {
- final MotionEvent mouseEvent = MotionEvent.obtain(0, 0, ACTION_HOVER_MOVE, 0, 0, 0);
- mouseEvent.setSource(InputDevice.SOURCE_MOUSE);
-
- mMgh.onMotionEvent(mouseEvent, mouseEvent, /* policyFlags= */ 0);
-
- try {
- assertFalse(mMgh.mIsHandleMouseOrStylusEventCalled);
- } finally {
- mouseEvent.recycle();
- }
- }
-
- @Test
- @RequiresFlagsDisabled(Flags.FLAG_ENABLE_MAGNIFICATION_FOLLOWS_MOUSE_BUGFIX)
- public void onMotionEvent_isFromStylus_handleMouseOrStylusEventNotCalled() {
- final MotionEvent stylusEvent = MotionEvent.obtain(0, 0, ACTION_HOVER_MOVE, 0, 0, 0);
- stylusEvent.setSource(InputDevice.SOURCE_STYLUS);
-
- mMgh.onMotionEvent(stylusEvent, stylusEvent, /* policyFlags= */ 0);
-
- try {
- assertFalse(mMgh.mIsHandleMouseOrStylusEventCalled);
- } finally {
- stylusEvent.recycle();
- }
- }
-
- @Test
public void onMotionEvent_downEvent_handleInteractionStart() {
final MotionEvent downEvent = MotionEvent.obtain(0, 0, ACTION_DOWN, 0, 0, 0);
downEvent.setSource(InputDevice.SOURCE_TOUCHSCREEN);
diff --git a/services/tests/servicestests/src/com/android/server/devicepolicy/DpmMockContext.java b/services/tests/servicestests/src/com/android/server/devicepolicy/DpmMockContext.java
index e5fac7ac5e0c..00b0c558b4e3 100644
--- a/services/tests/servicestests/src/com/android/server/devicepolicy/DpmMockContext.java
+++ b/services/tests/servicestests/src/com/android/server/devicepolicy/DpmMockContext.java
@@ -263,6 +263,11 @@ public class DpmMockContext extends MockContext {
}
@Override
+ public Context getApplicationContext() {
+ return this;
+ }
+
+ @Override
public PackageManager getPackageManager() {
return mMockSystemServices.packageManager;
}
diff --git a/services/tests/servicestests/src/com/android/server/pm/ShortcutManagerTest1.java b/services/tests/servicestests/src/com/android/server/pm/ShortcutManagerTest1.java
index 58f762204c27..ad1537e270e8 100644
--- a/services/tests/servicestests/src/com/android/server/pm/ShortcutManagerTest1.java
+++ b/services/tests/servicestests/src/com/android/server/pm/ShortcutManagerTest1.java
@@ -261,7 +261,7 @@ public class ShortcutManagerTest1 extends BaseShortcutManagerTest {
// === Test for app side APIs ===
/** Test for {@link android.content.pm.ShortcutManager#getMaxShortcutCountForActivity()} */
- public void testGetMaxDynamicShortcutCount() {
+ public void disabled_testGetMaxDynamicShortcutCount() {
assertEquals(MAX_SHORTCUTS, mManager.getMaxShortcutCountForActivity());
}
@@ -793,7 +793,7 @@ public class ShortcutManagerTest1 extends BaseShortcutManagerTest {
assertEquals(2, mManager.getRemainingCallCount());
}
- public void testDeleteAllDynamicShortcuts() {
+ public void disabled_testDeleteAllDynamicShortcuts() {
final ShortcutInfo si1 = makeShortcut("shortcut1");
final ShortcutInfo si2 = makeShortcut("shortcut2");
final ShortcutInfo si3 = makeShortcut("shortcut3");
@@ -1036,7 +1036,7 @@ public class ShortcutManagerTest1 extends BaseShortcutManagerTest {
*/
}
- public void testCleanupDanglingBitmaps() throws Exception {
+ public void disabled_testCleanupDanglingBitmaps() throws Exception {
assertBitmapDirectories(USER_10, EMPTY_STRINGS);
assertBitmapDirectories(USER_11, EMPTY_STRINGS);
@@ -1702,7 +1702,7 @@ public class ShortcutManagerTest1 extends BaseShortcutManagerTest {
"s2");
}
- public void testCachedShortcuts_accessShortcutsPermission() {
+ public void disabled_testCachedShortcuts_accessShortcutsPermission() {
runWithCaller(CALLING_PACKAGE_1, USER_10, () -> {
assertTrue(mManager.setDynamicShortcuts(list(makeShortcut("s1"),
makeLongLivedShortcut("s2"), makeLongLivedShortcut("s3"),
@@ -1744,7 +1744,7 @@ public class ShortcutManagerTest1 extends BaseShortcutManagerTest {
assertShortcutIds(mManager.getShortcuts(ShortcutManager.FLAG_MATCH_CACHED), "s3");
}
- public void testCachedShortcuts_canPassShortcutLimit() {
+ public void disabled_testCachedShortcuts_canPassShortcutLimit() {
// Change the max number of shortcuts.
mService.updateConfigurationLocked(ConfigConstants.KEY_MAX_SHORTCUTS + "=4");
@@ -2362,7 +2362,7 @@ public class ShortcutManagerTest1 extends BaseShortcutManagerTest {
* This is similar to the above test, except it used "disable" instead of "remove". It also
* does "enable".
*/
- public void testDisableAndEnableShortcuts() {
+ public void disabled_testDisableAndEnableShortcuts() {
runWithCaller(CALLING_PACKAGE_1, USER_10, () -> {
final ShortcutInfo s1_1 = makeShortcutWithTimestamp("s1", 1000);
final ShortcutInfo s1_2 = makeShortcutWithTimestamp("s2", 2000);
@@ -2487,7 +2487,7 @@ public class ShortcutManagerTest1 extends BaseShortcutManagerTest {
});
}
- public void testDisableShortcuts_thenRepublish() {
+ public void disabled_testDisableShortcuts_thenRepublish() {
runWithCaller(CALLING_PACKAGE_1, USER_10, () -> {
assertTrue(mManager.setDynamicShortcuts(list(
makeShortcut("s1"), makeShortcut("s2"), makeShortcut("s3"))));
@@ -4230,7 +4230,7 @@ public class ShortcutManagerTest1 extends BaseShortcutManagerTest {
// TODO Check all other fields
}
- public void testCleanupPackage() {
+ public void disabled_testCleanupPackage() {
runWithCaller(CALLING_PACKAGE_1, USER_10, () -> {
assertTrue(mManager.setDynamicShortcuts(list(
makeShortcut("s0_1"))));
@@ -4507,7 +4507,7 @@ public class ShortcutManagerTest1 extends BaseShortcutManagerTest {
mService.saveDirtyInfo();
}
- public void testCleanupPackage_republishManifests() {
+ public void disabled_testCleanupPackage_republishManifests() {
addManifestShortcutResource(
new ComponentName(CALLING_PACKAGE_1, ShortcutActivity.class.getName()),
R.xml.shortcut_2);
@@ -4847,7 +4847,7 @@ public class ShortcutManagerTest1 extends BaseShortcutManagerTest {
assertEquals(expected, spi.canRestoreTo(mService, pi, true));
}
- public void testCanRestoreTo() {
+ public void disabled_testCanRestoreTo() {
addPackage(CALLING_PACKAGE_1, CALLING_UID_1, 10, "sig1");
addPackage(CALLING_PACKAGE_2, CALLING_UID_2, 10, "sig1", "sig2");
addPackage(CALLING_PACKAGE_3, CALLING_UID_3, 10, "sig1");
@@ -5785,7 +5785,7 @@ public class ShortcutManagerTest1 extends BaseShortcutManagerTest {
checkBackupAndRestore_success(/*firstRestore=*/ true);
}
- public void testBackupAndRestore_restoreToSuperSetSignatures() {
+ public void disabled_testBackupAndRestore_restoreToSuperSetSignatures() {
prepareForBackupTest();
// Change package signatures.
diff --git a/services/tests/uiservicestests/src/com/android/server/notification/PreferencesHelperTest.java b/services/tests/uiservicestests/src/com/android/server/notification/PreferencesHelperTest.java
index 5dea44d6ebf4..67e85ff2445d 100644
--- a/services/tests/uiservicestests/src/com/android/server/notification/PreferencesHelperTest.java
+++ b/services/tests/uiservicestests/src/com/android/server/notification/PreferencesHelperTest.java
@@ -4495,6 +4495,27 @@ public class PreferencesHelperTest extends UiServiceTestCase {
}
@Test
+ public void testBubblePreference_sameVersionWithSAWPermission() throws Exception {
+ when(mAppOpsManager.noteOpNoThrow(eq(OP_SYSTEM_ALERT_WINDOW), anyInt(),
+ anyString(), eq(null), anyString())).thenReturn(MODE_ALLOWED);
+
+ final String xml = "<ranking version=\"4\">\n"
+ + "<package name=\"" + PKG_O + "\" uid=\"" + UID_O + "\">\n"
+ + "<channel id=\"someId\" name=\"hi\""
+ + " importance=\"3\"/>"
+ + "</package>"
+ + "</ranking>";
+ TypedXmlPullParser parser = Xml.newFastPullParser();
+ parser.setInput(new BufferedInputStream(new ByteArrayInputStream(xml.getBytes())),
+ null);
+ parser.nextTag();
+ mHelper.readXml(parser, false, UserHandle.USER_ALL);
+
+ assertEquals(BUBBLE_PREFERENCE_ALL, mHelper.getBubblePreference(PKG_O, UID_O));
+ assertEquals(0, mHelper.getAppLockedFields(PKG_O, UID_O));
+ }
+
+ @Test
public void testBubblePreference_upgradeWithSAWThenUserOverride() throws Exception {
when(mAppOpsManager.noteOpNoThrow(eq(OP_SYSTEM_ALERT_WINDOW), anyInt(),
anyString(), eq(null), anyString())).thenReturn(MODE_ALLOWED);
diff --git a/services/tests/vibrator/src/com/android/server/vibrator/DeviceAdapterTest.java b/services/tests/vibrator/src/com/android/server/vibrator/DeviceAdapterTest.java
index d5548a4f375e..f091a65cfe46 100644
--- a/services/tests/vibrator/src/com/android/server/vibrator/DeviceAdapterTest.java
+++ b/services/tests/vibrator/src/com/android/server/vibrator/DeviceAdapterTest.java
@@ -497,19 +497,19 @@ public class DeviceAdapterTest {
private VibratorController createEmptyVibratorController(int vibratorId) {
return new FakeVibratorControllerProvider(mTestLooper.getLooper())
- .newVibratorController(vibratorId, (id, vibrationId) -> {});
+ .newVibratorController(vibratorId, (id, vibrationId, stepId) -> {});
}
private VibratorController createBasicVibratorController(int vibratorId) {
FakeVibratorControllerProvider provider = createVibratorProviderWithEffects(
IVibrator.CAP_COMPOSE_EFFECTS);
- return provider.newVibratorController(vibratorId, (id, vibrationId) -> {});
+ return provider.newVibratorController(vibratorId, (id, vibrationId, stepId) -> {});
}
private VibratorController createPwleWithoutFrequenciesVibratorController(int vibratorId) {
FakeVibratorControllerProvider provider = createVibratorProviderWithEffects(
IVibrator.CAP_COMPOSE_EFFECTS, IVibrator.CAP_COMPOSE_PWLE_EFFECTS);
- return provider.newVibratorController(vibratorId, (id, vibrationId) -> {});
+ return provider.newVibratorController(vibratorId, (id, vibrationId, stepId) -> {});
}
private VibratorController createPwleVibratorController(int vibratorId) {
@@ -519,7 +519,7 @@ public class DeviceAdapterTest {
provider.setMinFrequency(TEST_MIN_FREQUENCY);
provider.setFrequencyResolution(TEST_FREQUENCY_RESOLUTION);
provider.setMaxAmplitudes(TEST_AMPLITUDE_MAP);
- return provider.newVibratorController(vibratorId, (id, vibrationId) -> {});
+ return provider.newVibratorController(vibratorId, (id, vibrationId, stepId) -> {});
}
private VibratorController createPwleV2VibratorController(int vibratorId) {
@@ -538,7 +538,7 @@ public class DeviceAdapterTest {
provider.setMinEnvelopeEffectControlPointDurationMillis(
TEST_MIN_ENVELOPE_EFFECT_CONTROL_POINT_DURATION_MILLIS);
- return provider.newVibratorController(vibratorId, (id, vibrationId) -> {});
+ return provider.newVibratorController(vibratorId, (id, vibrationId, stepId) -> {});
}
private FakeVibratorControllerProvider createVibratorProviderWithEffects(int... capabilities) {
diff --git a/services/tests/vibrator/src/com/android/server/vibrator/VibrationThreadTest.java b/services/tests/vibrator/src/com/android/server/vibrator/VibrationThreadTest.java
index 5f2af0a085c3..42279e40fa33 100644
--- a/services/tests/vibrator/src/com/android/server/vibrator/VibrationThreadTest.java
+++ b/services/tests/vibrator/src/com/android/server/vibrator/VibrationThreadTest.java
@@ -17,6 +17,9 @@
package com.android.server.vibrator;
import static android.os.VibrationAttributes.USAGE_RINGTONE;
+import static android.os.VibrationEffect.Composition.PRIMITIVE_CLICK;
+import static android.os.VibrationEffect.Composition.PRIMITIVE_SPIN;
+import static android.os.VibrationEffect.Composition.PRIMITIVE_TICK;
import static android.os.VibrationEffect.VibrationParameter.targetAmplitude;
import static android.os.VibrationEffect.VibrationParameter.targetFrequency;
@@ -66,7 +69,6 @@ import android.os.vibrator.VibrationConfig;
import android.os.vibrator.VibrationEffectSegment;
import android.platform.test.annotations.DisableFlags;
import android.platform.test.annotations.EnableFlags;
-import android.platform.test.annotations.RequiresFlagsEnabled;
import android.platform.test.flag.junit.CheckFlagsRule;
import android.platform.test.flag.junit.DeviceFlagsValueProvider;
import android.platform.test.flag.junit.SetFlagsRule;
@@ -153,9 +155,10 @@ public class VibrationThreadTest {
when(mPackageManagerInternalMock.getSystemUiServiceComponent())
.thenReturn(new ComponentName("", ""));
doAnswer(answer -> {
- mVibrationConductor.notifyVibratorComplete(answer.getArgument(0));
+ mVibrationConductor.notifyVibratorComplete(
+ answer.getArgument(0), answer.getArgument(2));
return null;
- }).when(mControllerCallbacks).onComplete(anyInt(), anyLong());
+ }).when(mControllerCallbacks).onComplete(anyInt(), anyLong(), anyLong());
LocalServices.removeServiceForTest(PackageManagerInternal.class);
LocalServices.addService(PackageManagerInternal.class, mPackageManagerInternalMock);
@@ -190,7 +193,7 @@ public class VibrationThreadTest {
HalVibration vibration = startThreadAndDispatcher(effect);
waitForCompletion();
- verify(mControllerCallbacks, never()).onComplete(anyInt(), eq(vibration.id));
+ verify(mControllerCallbacks, never()).onComplete(anyInt(), eq(vibration.id), anyLong());
verifyCallbacksTriggered(vibration, Status.IGNORED_UNSUPPORTED);
}
@@ -203,7 +206,7 @@ public class VibrationThreadTest {
HalVibration vibration = startThreadAndDispatcher(effect);
waitForCompletion();
- verify(mControllerCallbacks, never()).onComplete(anyInt(), eq(vibration.id));
+ verify(mControllerCallbacks, never()).onComplete(anyInt(), eq(vibration.id), anyLong());
verifyCallbacksTriggered(vibration, Status.IGNORED_UNSUPPORTED);
}
@@ -217,7 +220,7 @@ public class VibrationThreadTest {
verify(mManagerHooks).noteVibratorOn(eq(UID), eq(10L));
verify(mManagerHooks).noteVibratorOff(eq(UID));
- verify(mControllerCallbacks).onComplete(eq(VIBRATOR_ID), eq(vibration.id));
+ verify(mControllerCallbacks).onComplete(eq(VIBRATOR_ID), eq(vibration.id), anyLong());
verifyCallbacksTriggered(vibration, Status.FINISHED);
assertFalse(mControllers.get(VIBRATOR_ID).isVibrating());
@@ -234,7 +237,7 @@ public class VibrationThreadTest {
verify(mManagerHooks).noteVibratorOn(eq(UID), eq(10L));
verify(mManagerHooks).noteVibratorOff(eq(UID));
- verify(mControllerCallbacks).onComplete(eq(VIBRATOR_ID), eq(vibration.id));
+ verify(mControllerCallbacks).onComplete(eq(VIBRATOR_ID), eq(vibration.id), anyLong());
verifyCallbacksTriggered(vibration, Status.FINISHED);
assertFalse(mControllers.get(VIBRATOR_ID).isVibrating());
@@ -254,7 +257,7 @@ public class VibrationThreadTest {
verify(mManagerHooks).noteVibratorOn(eq(UID), eq(15L));
verify(mManagerHooks).noteVibratorOff(eq(UID));
- verify(mControllerCallbacks).onComplete(eq(VIBRATOR_ID), eq(vibration.id));
+ verify(mControllerCallbacks).onComplete(eq(VIBRATOR_ID), eq(vibration.id), anyLong());
verifyCallbacksTriggered(vibration, Status.FINISHED);
assertFalse(mControllers.get(VIBRATOR_ID).isVibrating());
@@ -265,7 +268,7 @@ public class VibrationThreadTest {
}
@Test
- @RequiresFlagsEnabled(Flags.FLAG_ADAPTIVE_HAPTICS_ENABLED)
+ @EnableFlags(Flags.FLAG_ADAPTIVE_HAPTICS_ENABLED)
public void vibrate_singleWaveformWithAdaptiveHapticsScaling_scalesAmplitudesProperly() {
// No user settings scale.
setUserSetting(Settings.System.RING_VIBRATION_INTENSITY,
@@ -291,7 +294,7 @@ public class VibrationThreadTest {
}
@Test
- @RequiresFlagsEnabled(Flags.FLAG_ADAPTIVE_HAPTICS_ENABLED)
+ @EnableFlags(Flags.FLAG_ADAPTIVE_HAPTICS_ENABLED)
public void vibrate_withVibrationParamsRequestStalling_timeoutRequestAndApplyNoScaling() {
// No user settings scale.
setUserSetting(Settings.System.RING_VIBRATION_INTENSITY,
@@ -412,6 +415,7 @@ public class VibrationThreadTest {
.isEqualTo(expectedOneShots(200L, 50L));
}
+ @EnableFlags(Flags.FLAG_FIX_VIBRATION_THREAD_CALLBACK_HANDLING)
@LargeTest
@Test
public void vibrate_singleVibratorRepeatingPatternWithZeroDurationSteps_repeatsEffectCorrectly()
@@ -443,6 +447,30 @@ public class VibrationThreadTest {
.isEqualTo(expectedOneShots(200L, 150L, 300L, 150L, 300L));
}
+ @EnableFlags(Flags.FLAG_FIX_VIBRATION_THREAD_CALLBACK_HANDLING)
+ @Test
+ public void vibrate_singleVibratorPatternWithCallbackDelay_oldCallbacksIgnored() {
+ FakeVibratorControllerProvider fakeVibrator = mVibratorProviders.get(VIBRATOR_ID);
+ fakeVibrator.setCompletionCallbackDelay(100); // 100ms delay to notify service.
+ fakeVibrator.setCapabilities(IVibrator.CAP_AMPLITUDE_CONTROL);
+
+ VibrationEffect effect = VibrationEffect.createWaveform(
+ /* timings= */ new long[]{0, 200, 50, 400}, /* repeat= */ -1);
+ HalVibration vibration = startThreadAndDispatcher(effect);
+ waitForCompletion(800 + TEST_TIMEOUT_MILLIS); // 200 + 50 + 400 + 100ms delay
+
+ verifyCallbacksTriggered(vibration, Status.FINISHED);
+ assertThat(mControllers.get(VIBRATOR_ID).isVibrating()).isFalse();
+
+ verify(mControllerCallbacks).onComplete(eq(VIBRATOR_ID), eq(vibration.id), eq(1L));
+ // Step id = 2 skipped by the 50ms OFF step after the 200ms ON step.
+ verify(mControllerCallbacks).onComplete(eq(VIBRATOR_ID), eq(vibration.id), eq(3L));
+
+ // First callback ignored, did not cause the vibrator to turn back on during the 400ms step.
+ assertThat(fakeVibrator.getEffectSegments(vibration.id))
+ .isEqualTo(expectedOneShots(200L, 400L));
+ }
+
@Test
public void vibrate_singleVibratorRepeatingPwle_generatesLargestPwles() throws Exception {
FakeVibratorControllerProvider fakeVibrator = mVibratorProviders.get(VIBRATOR_ID);
@@ -479,12 +507,12 @@ public class VibrationThreadTest {
throws Exception {
FakeVibratorControllerProvider fakeVibrator = mVibratorProviders.get(VIBRATOR_ID);
fakeVibrator.setCapabilities(IVibrator.CAP_COMPOSE_EFFECTS);
- fakeVibrator.setSupportedPrimitives(VibrationEffect.Composition.PRIMITIVE_CLICK);
+ fakeVibrator.setSupportedPrimitives(PRIMITIVE_CLICK);
fakeVibrator.setCompositionSizeMax(10);
VibrationEffect effect = VibrationEffect.startComposition()
// Very long delay so thread will be cancelled after first PWLE is triggered.
- .addPrimitive(VibrationEffect.Composition.PRIMITIVE_CLICK, 1f, 100)
+ .addPrimitive(PRIMITIVE_CLICK, 1f, 100)
.compose();
VibrationEffect repeatingEffect = VibrationEffect.startComposition()
.repeatEffectIndefinitely(effect)
@@ -561,13 +589,12 @@ public class VibrationThreadTest {
public void vibrate_singleVibratorPredefinedCancel_cancelsVibrationImmediately()
throws Exception {
mVibratorProviders.get(VIBRATOR_ID).setCapabilities(IVibrator.CAP_COMPOSE_EFFECTS);
- mVibratorProviders.get(VIBRATOR_ID).setSupportedPrimitives(
- VibrationEffect.Composition.PRIMITIVE_CLICK);
+ mVibratorProviders.get(VIBRATOR_ID).setSupportedPrimitives(PRIMITIVE_CLICK);
VibrationEffect effect = VibrationEffect.startComposition()
- .addPrimitive(VibrationEffect.Composition.PRIMITIVE_CLICK, 1f, 100)
- .addPrimitive(VibrationEffect.Composition.PRIMITIVE_CLICK, 1f, 100)
- .addPrimitive(VibrationEffect.Composition.PRIMITIVE_CLICK, 1f, 100)
+ .addPrimitive(PRIMITIVE_CLICK, 1f, 100)
+ .addPrimitive(PRIMITIVE_CLICK, 1f, 100)
+ .addPrimitive(PRIMITIVE_CLICK, 1f, 100)
.compose();
HalVibration vibration = startThreadAndDispatcher(effect);
@@ -591,7 +618,7 @@ public class VibrationThreadTest {
}
@Test
- @RequiresFlagsEnabled(android.os.vibrator.Flags.FLAG_VENDOR_VIBRATION_EFFECTS)
+ @EnableFlags(Flags.FLAG_VENDOR_VIBRATION_EFFECTS)
public void vibrate_singleVibratorVendorEffectCancel_cancelsVibrationImmediately()
throws Exception {
mVibratorProviders.get(VIBRATOR_ID).setCapabilities(IVibrator.CAP_PERFORM_VENDOR_EFFECTS);
@@ -657,7 +684,7 @@ public class VibrationThreadTest {
verify(mManagerHooks).noteVibratorOn(eq(UID), eq(20L));
verify(mManagerHooks).noteVibratorOff(eq(UID));
- verify(mControllerCallbacks).onComplete(eq(VIBRATOR_ID), eq(vibration.id));
+ verify(mControllerCallbacks).onComplete(eq(VIBRATOR_ID), eq(vibration.id), anyLong());
verifyCallbacksTriggered(vibration, Status.FINISHED);
assertFalse(mControllers.get(VIBRATOR_ID).isVibrating());
@@ -678,7 +705,7 @@ public class VibrationThreadTest {
verify(mManagerHooks).noteVibratorOn(eq(UID), eq(10L));
verify(mManagerHooks).noteVibratorOff(eq(UID));
- verify(mControllerCallbacks).onComplete(eq(VIBRATOR_ID), eq(vibration.id));
+ verify(mControllerCallbacks).onComplete(eq(VIBRATOR_ID), eq(vibration.id), anyLong());
verifyCallbacksTriggered(vibration, Status.FINISHED);
assertFalse(mControllers.get(VIBRATOR_ID).isVibrating());
@@ -695,13 +722,14 @@ public class VibrationThreadTest {
verify(mManagerHooks).noteVibratorOn(eq(UID), eq(0L));
verify(mManagerHooks, never()).noteVibratorOff(eq(UID));
- verify(mControllerCallbacks, never()).onComplete(eq(VIBRATOR_ID), eq(vibration.id));
+ verify(mControllerCallbacks, never())
+ .onComplete(eq(VIBRATOR_ID), eq(vibration.id), anyLong());
verifyCallbacksTriggered(vibration, Status.IGNORED_UNSUPPORTED);
assertTrue(mVibratorProviders.get(VIBRATOR_ID).getEffectSegments(vibration.id).isEmpty());
}
@Test
- @RequiresFlagsEnabled(android.os.vibrator.Flags.FLAG_VENDOR_VIBRATION_EFFECTS)
+ @EnableFlags(Flags.FLAG_VENDOR_VIBRATION_EFFECTS)
public void vibrate_singleVibratorVendorEffect_runsVibration() {
mVibratorProviders.get(1).setCapabilities(IVibrator.CAP_PERFORM_VENDOR_EFFECTS);
@@ -712,7 +740,7 @@ public class VibrationThreadTest {
verify(mManagerHooks).noteVibratorOn(eq(UID),
eq(PerformVendorEffectVibratorStep.VENDOR_EFFECT_MAX_DURATION_MS));
verify(mManagerHooks).noteVibratorOff(eq(UID));
- verify(mControllerCallbacks).onComplete(eq(VIBRATOR_ID), eq(vibration.id));
+ verify(mControllerCallbacks).onComplete(eq(VIBRATOR_ID), eq(vibration.id), anyLong());
verifyCallbacksTriggered(vibration, Status.FINISHED);
assertThat(mControllers.get(VIBRATOR_ID).isVibrating()).isFalse();
@@ -725,24 +753,23 @@ public class VibrationThreadTest {
public void vibrate_singleVibratorComposed_runsVibration() {
FakeVibratorControllerProvider fakeVibrator = mVibratorProviders.get(VIBRATOR_ID);
fakeVibrator.setCapabilities(IVibrator.CAP_COMPOSE_EFFECTS);
- fakeVibrator.setSupportedPrimitives(VibrationEffect.Composition.PRIMITIVE_CLICK,
- VibrationEffect.Composition.PRIMITIVE_TICK);
+ fakeVibrator.setSupportedPrimitives(PRIMITIVE_CLICK, PRIMITIVE_TICK);
VibrationEffect effect = VibrationEffect.startComposition()
- .addPrimitive(VibrationEffect.Composition.PRIMITIVE_CLICK, 1f)
- .addPrimitive(VibrationEffect.Composition.PRIMITIVE_TICK, 0.5f)
+ .addPrimitive(PRIMITIVE_CLICK, 1f)
+ .addPrimitive(PRIMITIVE_TICK, 0.5f)
.compose();
HalVibration vibration = startThreadAndDispatcher(effect);
waitForCompletion();
verify(mManagerHooks).noteVibratorOn(eq(UID), eq(40L));
verify(mManagerHooks).noteVibratorOff(eq(UID));
- verify(mControllerCallbacks).onComplete(eq(VIBRATOR_ID), eq(vibration.id));
+ verify(mControllerCallbacks).onComplete(eq(VIBRATOR_ID), eq(vibration.id), anyLong());
verifyCallbacksTriggered(vibration, Status.FINISHED);
assertFalse(mControllers.get(VIBRATOR_ID).isVibrating());
assertEquals(Arrays.asList(
- expectedPrimitive(VibrationEffect.Composition.PRIMITIVE_CLICK, 1, 0),
- expectedPrimitive(VibrationEffect.Composition.PRIMITIVE_TICK, 0.5f, 0)),
+ expectedPrimitive(PRIMITIVE_CLICK, 1, 0),
+ expectedPrimitive(PRIMITIVE_TICK, 0.5f, 0)),
fakeVibrator.getEffectSegments(vibration.id));
}
@@ -750,14 +777,15 @@ public class VibrationThreadTest {
@DisableFlags(Flags.FLAG_PRIMITIVE_COMPOSITION_ABSOLUTE_DELAY)
public void vibrate_singleVibratorComposedAndNoCapability_triggersHalAndReturnsUnsupported() {
VibrationEffect effect = VibrationEffect.startComposition()
- .addPrimitive(VibrationEffect.Composition.PRIMITIVE_CLICK, 1f)
+ .addPrimitive(PRIMITIVE_CLICK, 1f)
.compose();
HalVibration vibration = startThreadAndDispatcher(effect);
waitForCompletion();
verify(mManagerHooks).noteVibratorOn(eq(UID), eq(0L));
verify(mManagerHooks, never()).noteVibratorOff(eq(UID));
- verify(mControllerCallbacks, never()).onComplete(eq(VIBRATOR_ID), eq(vibration.id));
+ verify(mControllerCallbacks, never())
+ .onComplete(eq(VIBRATOR_ID), eq(vibration.id), anyLong());
verifyCallbacksTriggered(vibration, Status.IGNORED_UNSUPPORTED);
assertTrue(mVibratorProviders.get(VIBRATOR_ID).getEffectSegments(vibration.id).isEmpty());
}
@@ -766,14 +794,15 @@ public class VibrationThreadTest {
@EnableFlags(Flags.FLAG_PRIMITIVE_COMPOSITION_ABSOLUTE_DELAY)
public void vibrate_singleVibratorComposedAndNoCapability_ignoresVibration() {
VibrationEffect effect = VibrationEffect.startComposition()
- .addPrimitive(VibrationEffect.Composition.PRIMITIVE_CLICK, 1f)
+ .addPrimitive(PRIMITIVE_CLICK, 1f)
.compose();
HalVibration vibration = startThreadAndDispatcher(effect);
waitForCompletion();
verify(mManagerHooks, never()).noteVibratorOn(eq(UID), anyLong());
verify(mManagerHooks, never()).noteVibratorOff(eq(UID));
- verify(mControllerCallbacks, never()).onComplete(eq(VIBRATOR_ID), eq(vibration.id));
+ verify(mControllerCallbacks, never())
+ .onComplete(eq(VIBRATOR_ID), eq(vibration.id), anyLong());
verifyCallbacksTriggered(vibration, Status.IGNORED_UNSUPPORTED);
assertTrue(mVibratorProviders.get(VIBRATOR_ID).getEffectSegments(vibration.id).isEmpty());
}
@@ -782,34 +811,30 @@ public class VibrationThreadTest {
public void vibrate_singleVibratorLargeComposition_splitsVibratorComposeCalls() {
FakeVibratorControllerProvider fakeVibrator = mVibratorProviders.get(VIBRATOR_ID);
fakeVibrator.setCapabilities(IVibrator.CAP_COMPOSE_EFFECTS);
- fakeVibrator.setSupportedPrimitives(
- VibrationEffect.Composition.PRIMITIVE_CLICK,
- VibrationEffect.Composition.PRIMITIVE_TICK,
- VibrationEffect.Composition.PRIMITIVE_SPIN);
+ fakeVibrator.setSupportedPrimitives(PRIMITIVE_CLICK, PRIMITIVE_TICK, PRIMITIVE_SPIN);
fakeVibrator.setCompositionSizeMax(2);
VibrationEffect effect = VibrationEffect.startComposition()
- .addPrimitive(VibrationEffect.Composition.PRIMITIVE_CLICK, 1f)
- .addPrimitive(VibrationEffect.Composition.PRIMITIVE_TICK, 0.5f)
- .addPrimitive(VibrationEffect.Composition.PRIMITIVE_SPIN, 0.8f)
+ .addPrimitive(PRIMITIVE_CLICK, 1f)
+ .addPrimitive(PRIMITIVE_TICK, 0.5f)
+ .addPrimitive(PRIMITIVE_SPIN, 0.8f)
.compose();
HalVibration vibration = startThreadAndDispatcher(effect);
waitForCompletion();
verifyCallbacksTriggered(vibration, Status.FINISHED);
// Vibrator compose called twice.
- verify(mControllerCallbacks, times(2)).onComplete(eq(VIBRATOR_ID), eq(vibration.id));
+ verify(mControllerCallbacks, times(2))
+ .onComplete(eq(VIBRATOR_ID), eq(vibration.id), anyLong());
assertEquals(3, fakeVibrator.getEffectSegments(vibration.id).size());
}
@Test
- @DisableFlags(android.os.vibrator.Flags.FLAG_NORMALIZED_PWLE_EFFECTS)
+ @DisableFlags(Flags.FLAG_NORMALIZED_PWLE_EFFECTS)
public void vibrate_singleVibratorComposedEffects_runsDifferentVibrations() {
FakeVibratorControllerProvider fakeVibrator = mVibratorProviders.get(VIBRATOR_ID);
fakeVibrator.setSupportedEffects(VibrationEffect.EFFECT_CLICK);
- fakeVibrator.setSupportedPrimitives(
- VibrationEffect.Composition.PRIMITIVE_CLICK,
- VibrationEffect.Composition.PRIMITIVE_TICK);
+ fakeVibrator.setSupportedPrimitives(PRIMITIVE_CLICK, PRIMITIVE_TICK);
fakeVibrator.setCapabilities(IVibrator.CAP_COMPOSE_EFFECTS,
IVibrator.CAP_COMPOSE_PWLE_EFFECTS, IVibrator.CAP_AMPLITUDE_CONTROL);
fakeVibrator.setMinFrequency(100);
@@ -820,8 +845,8 @@ public class VibrationThreadTest {
VibrationEffect effect = VibrationEffect.startComposition()
.addEffect(VibrationEffect.createOneShot(10, 100))
- .addPrimitive(VibrationEffect.Composition.PRIMITIVE_CLICK, 1f)
- .addPrimitive(VibrationEffect.Composition.PRIMITIVE_TICK, 0.5f)
+ .addPrimitive(PRIMITIVE_CLICK, 1f)
+ .addPrimitive(PRIMITIVE_TICK, 0.5f)
.addEffect(VibrationEffect.get(VibrationEffect.EFFECT_CLICK))
.addEffect(VibrationEffect.startWaveform()
.addTransition(Duration.ofMillis(10),
@@ -836,13 +861,14 @@ public class VibrationThreadTest {
// Use first duration the vibrator is turned on since we cannot estimate the clicks.
verify(mManagerHooks).noteVibratorOn(eq(UID), eq(10L));
verify(mManagerHooks).noteVibratorOff(eq(UID));
- verify(mControllerCallbacks, times(5)).onComplete(eq(VIBRATOR_ID), eq(vibration.id));
+ verify(mControllerCallbacks, times(5))
+ .onComplete(eq(VIBRATOR_ID), eq(vibration.id), anyLong());
verifyCallbacksTriggered(vibration, Status.FINISHED);
assertFalse(mControllers.get(VIBRATOR_ID).isVibrating());
assertEquals(Arrays.asList(
expectedOneShot(10),
- expectedPrimitive(VibrationEffect.Composition.PRIMITIVE_CLICK, 1, 0),
- expectedPrimitive(VibrationEffect.Composition.PRIMITIVE_TICK, 0.5f, 0),
+ expectedPrimitive(PRIMITIVE_CLICK, 1, 0),
+ expectedPrimitive(PRIMITIVE_TICK, 0.5f, 0),
expectedPrebaked(VibrationEffect.EFFECT_CLICK),
expectedRamp(/* startAmplitude= */ 0, /* endAmplitude= */ 0.5f,
/* startFrequencyHz= */ 150, /* endFrequencyHz= */ 100, /* duration= */ 10),
@@ -857,17 +883,15 @@ public class VibrationThreadTest {
public void vibrate_singleVibratorComposedWithFallback_replacedInTheMiddleOfComposition() {
FakeVibratorControllerProvider fakeVibrator = mVibratorProviders.get(VIBRATOR_ID);
fakeVibrator.setSupportedEffects(VibrationEffect.EFFECT_CLICK);
- fakeVibrator.setSupportedPrimitives(
- VibrationEffect.Composition.PRIMITIVE_CLICK,
- VibrationEffect.Composition.PRIMITIVE_TICK);
+ fakeVibrator.setSupportedPrimitives(PRIMITIVE_CLICK, PRIMITIVE_TICK);
fakeVibrator.setCapabilities(IVibrator.CAP_COMPOSE_EFFECTS);
VibrationEffect fallback = VibrationEffect.createOneShot(10, 100);
VibrationEffect effect = VibrationEffect.startComposition()
.addEffect(VibrationEffect.get(VibrationEffect.EFFECT_CLICK))
- .addPrimitive(VibrationEffect.Composition.PRIMITIVE_CLICK, 1f)
+ .addPrimitive(PRIMITIVE_CLICK, 1f)
.addEffect(VibrationEffect.get(VibrationEffect.EFFECT_TICK))
- .addPrimitive(VibrationEffect.Composition.PRIMITIVE_TICK, 0.5f)
+ .addPrimitive(PRIMITIVE_TICK, 0.5f)
.compose();
HalVibration vibration = createVibration(CombinedVibration.createParallel(effect));
vibration.fillFallbacks(unused -> fallback);
@@ -877,7 +901,8 @@ public class VibrationThreadTest {
// Use first duration the vibrator is turned on since we cannot estimate the clicks.
verify(mManagerHooks).noteVibratorOn(eq(UID), anyLong());
verify(mManagerHooks).noteVibratorOff(eq(UID));
- verify(mControllerCallbacks, times(4)).onComplete(eq(VIBRATOR_ID), eq(vibration.id));
+ verify(mControllerCallbacks, times(4))
+ .onComplete(eq(VIBRATOR_ID), eq(vibration.id), anyLong());
verifyCallbacksTriggered(vibration, Status.FINISHED);
assertFalse(mControllers.get(VIBRATOR_ID).isVibrating());
@@ -894,7 +919,7 @@ public class VibrationThreadTest {
}
@Test
- @EnableFlags(android.os.vibrator.Flags.FLAG_NORMALIZED_PWLE_EFFECTS)
+ @EnableFlags(Flags.FLAG_NORMALIZED_PWLE_EFFECTS)
public void vibrate_singleVibratorPwle_runsComposePwleV2() {
FakeVibratorControllerProvider fakeVibrator = mVibratorProviders.get(VIBRATOR_ID);
fakeVibrator.setCapabilities(IVibrator.CAP_COMPOSE_PWLE_EFFECTS_V2);
@@ -915,7 +940,7 @@ public class VibrationThreadTest {
verify(mManagerHooks).noteVibratorOn(eq(UID), eq(100L));
verify(mManagerHooks).noteVibratorOff(eq(UID));
- verify(mControllerCallbacks).onComplete(eq(VIBRATOR_ID), eq(vibration.id));
+ verify(mControllerCallbacks).onComplete(eq(VIBRATOR_ID), eq(vibration.id), anyLong());
verifyCallbacksTriggered(vibration, Status.FINISHED);
assertFalse(mControllers.get(VIBRATOR_ID).isVibrating());
assertEquals(Arrays.asList(
@@ -929,7 +954,7 @@ public class VibrationThreadTest {
}
@Test
- @EnableFlags(android.os.vibrator.Flags.FLAG_NORMALIZED_PWLE_EFFECTS)
+ @EnableFlags(Flags.FLAG_NORMALIZED_PWLE_EFFECTS)
public void vibrate_singleVibratorBasicPwle_runsComposePwleV2() {
FakeVibratorControllerProvider fakeVibrator = mVibratorProviders.get(VIBRATOR_ID);
fakeVibrator.setCapabilities(IVibrator.CAP_COMPOSE_PWLE_EFFECTS_V2);
@@ -951,7 +976,7 @@ public class VibrationThreadTest {
verify(mManagerHooks).noteVibratorOn(eq(UID), eq(220L));
verify(mManagerHooks).noteVibratorOff(eq(UID));
- verify(mControllerCallbacks).onComplete(eq(VIBRATOR_ID), eq(vibration.id));
+ verify(mControllerCallbacks).onComplete(eq(VIBRATOR_ID), eq(vibration.id), anyLong());
verifyCallbacksTriggered(vibration, Status.FINISHED);
assertFalse(mControllers.get(VIBRATOR_ID).isVibrating());
assertEquals(Arrays.asList(
@@ -964,7 +989,7 @@ public class VibrationThreadTest {
}
@Test
- @EnableFlags(android.os.vibrator.Flags.FLAG_NORMALIZED_PWLE_EFFECTS)
+ @EnableFlags(Flags.FLAG_NORMALIZED_PWLE_EFFECTS)
public void vibrate_singleVibratorPwle_withInitialFrequency_runsComposePwleV2() {
FakeVibratorControllerProvider fakeVibrator = mVibratorProviders.get(VIBRATOR_ID);
fakeVibrator.setCapabilities(IVibrator.CAP_COMPOSE_PWLE_EFFECTS_V2);
@@ -987,7 +1012,7 @@ public class VibrationThreadTest {
verify(mManagerHooks).noteVibratorOn(eq(UID), eq(100L));
verify(mManagerHooks).noteVibratorOff(eq(UID));
- verify(mControllerCallbacks).onComplete(eq(VIBRATOR_ID), eq(vibration.id));
+ verify(mControllerCallbacks).onComplete(eq(VIBRATOR_ID), eq(vibration.id), anyLong());
verifyCallbacksTriggered(vibration, Status.FINISHED);
assertFalse(mControllers.get(VIBRATOR_ID).isVibrating());
assertEquals(Arrays.asList(
@@ -1001,7 +1026,7 @@ public class VibrationThreadTest {
}
@Test
- @EnableFlags(android.os.vibrator.Flags.FLAG_NORMALIZED_PWLE_EFFECTS)
+ @EnableFlags(Flags.FLAG_NORMALIZED_PWLE_EFFECTS)
public void vibrate_singleVibratorPwle_TooManyControlPoints_splitsAndRunsComposePwleV2() {
FakeVibratorControllerProvider fakeVibrator = mVibratorProviders.get(VIBRATOR_ID);
fakeVibrator.setCapabilities(IVibrator.CAP_COMPOSE_PWLE_EFFECTS_V2);
@@ -1027,7 +1052,8 @@ public class VibrationThreadTest {
verifyCallbacksTriggered(vibration, Status.FINISHED);
// Vibrator compose called 3 times with 2 segments instead of 2 times with 3 segments.
// Using best split points instead of max-packing PWLEs.
- verify(mControllerCallbacks, times(3)).onComplete(eq(VIBRATOR_ID), eq(vibration.id));
+ verify(mControllerCallbacks, times(3))
+ .onComplete(eq(VIBRATOR_ID), eq(vibration.id), anyLong());
assertFalse(mControllers.get(VIBRATOR_ID).isVibrating());
assertEquals(Arrays.asList(
@@ -1043,7 +1069,7 @@ public class VibrationThreadTest {
}
@Test
- @DisableFlags(android.os.vibrator.Flags.FLAG_NORMALIZED_PWLE_EFFECTS)
+ @DisableFlags(Flags.FLAG_NORMALIZED_PWLE_EFFECTS)
public void vibrate_singleVibratorPwle_runsComposePwle() {
FakeVibratorControllerProvider fakeVibrator = mVibratorProviders.get(VIBRATOR_ID);
fakeVibrator.setCapabilities(IVibrator.CAP_COMPOSE_PWLE_EFFECTS);
@@ -1066,7 +1092,7 @@ public class VibrationThreadTest {
verify(mManagerHooks).noteVibratorOn(eq(UID), eq(100L));
verify(mManagerHooks).noteVibratorOff(eq(UID));
- verify(mControllerCallbacks).onComplete(eq(VIBRATOR_ID), eq(vibration.id));
+ verify(mControllerCallbacks).onComplete(eq(VIBRATOR_ID), eq(vibration.id), anyLong());
verifyCallbacksTriggered(vibration, Status.FINISHED);
assertFalse(mControllers.get(VIBRATOR_ID).isVibrating());
assertEquals(Arrays.asList(
@@ -1109,7 +1135,8 @@ public class VibrationThreadTest {
// Vibrator compose called 3 times with 2 segments instead of 2 times with 3 segments.
// Using best split points instead of max-packing PWLEs.
- verify(mControllerCallbacks, times(3)).onComplete(eq(VIBRATOR_ID), eq(vibration.id));
+ verify(mControllerCallbacks, times(3))
+ .onComplete(eq(VIBRATOR_ID), eq(vibration.id), anyLong());
assertEquals(6, fakeVibrator.getEffectSegments(vibration.id).size());
}
@@ -1160,8 +1187,8 @@ public class VibrationThreadTest {
verify(mManagerHooks).noteVibratorOn(eq(UID), eq(20L));
verify(mManagerHooks).noteVibratorOff(eq(UID));
- verify(mControllerCallbacks).onComplete(eq(VIBRATOR_ID), eq(vibration.id));
- verify(mControllerCallbacks, never()).onComplete(eq(2), eq(vibration.id));
+ verify(mControllerCallbacks).onComplete(eq(VIBRATOR_ID), eq(vibration.id), anyLong());
+ verify(mControllerCallbacks, never()).onComplete(eq(2), eq(vibration.id), anyLong());
verifyCallbacksTriggered(vibration, Status.FINISHED);
assertFalse(mControllers.get(VIBRATOR_ID).isVibrating());
@@ -1183,9 +1210,9 @@ public class VibrationThreadTest {
verify(mManagerHooks).noteVibratorOn(eq(UID), eq(20L));
verify(mManagerHooks).noteVibratorOff(eq(UID));
- verify(mControllerCallbacks).onComplete(eq(1), eq(vibration.id));
- verify(mControllerCallbacks).onComplete(eq(2), eq(vibration.id));
- verify(mControllerCallbacks).onComplete(eq(3), eq(vibration.id));
+ verify(mControllerCallbacks).onComplete(eq(1), eq(vibration.id), anyLong());
+ verify(mControllerCallbacks).onComplete(eq(2), eq(vibration.id), anyLong());
+ verify(mControllerCallbacks).onComplete(eq(3), eq(vibration.id), anyLong());
verifyCallbacksTriggered(vibration, Status.FINISHED);
assertFalse(mControllers.get(1).isVibrating());
assertFalse(mControllers.get(2).isVibrating());
@@ -1207,11 +1234,10 @@ public class VibrationThreadTest {
mVibratorProviders.get(2).setCapabilities(IVibrator.CAP_AMPLITUDE_CONTROL);
mVibratorProviders.get(3).setCapabilities(IVibrator.CAP_AMPLITUDE_CONTROL);
mVibratorProviders.get(4).setCapabilities(IVibrator.CAP_COMPOSE_EFFECTS);
- mVibratorProviders.get(4).setSupportedPrimitives(
- VibrationEffect.Composition.PRIMITIVE_CLICK);
+ mVibratorProviders.get(4).setSupportedPrimitives(PRIMITIVE_CLICK);
VibrationEffect composed = VibrationEffect.startComposition()
- .addPrimitive(VibrationEffect.Composition.PRIMITIVE_CLICK)
+ .addPrimitive(PRIMITIVE_CLICK)
.compose();
CombinedVibration effect = CombinedVibration.startParallel()
.addVibrator(1, VibrationEffect.get(VibrationEffect.EFFECT_CLICK))
@@ -1225,10 +1251,10 @@ public class VibrationThreadTest {
verify(mManagerHooks).noteVibratorOn(eq(UID), eq(20L));
verify(mManagerHooks).noteVibratorOff(eq(UID));
- verify(mControllerCallbacks).onComplete(eq(1), eq(vibration.id));
- verify(mControllerCallbacks).onComplete(eq(2), eq(vibration.id));
- verify(mControllerCallbacks).onComplete(eq(3), eq(vibration.id));
- verify(mControllerCallbacks).onComplete(eq(4), eq(vibration.id));
+ verify(mControllerCallbacks).onComplete(eq(1), eq(vibration.id), anyLong());
+ verify(mControllerCallbacks).onComplete(eq(2), eq(vibration.id), anyLong());
+ verify(mControllerCallbacks).onComplete(eq(3), eq(vibration.id), anyLong());
+ verify(mControllerCallbacks).onComplete(eq(4), eq(vibration.id), anyLong());
verifyCallbacksTriggered(vibration, Status.FINISHED);
assertFalse(mControllers.get(1).isVibrating());
assertFalse(mControllers.get(2).isVibrating());
@@ -1243,8 +1269,7 @@ public class VibrationThreadTest {
assertEquals(Arrays.asList(expectedOneShot(20)),
mVibratorProviders.get(3).getEffectSegments(vibration.id));
assertEquals(expectedAmplitudes(1, 2), mVibratorProviders.get(3).getAmplitudes());
- assertEquals(Arrays.asList(
- expectedPrimitive(VibrationEffect.Composition.PRIMITIVE_CLICK, 1, 0)),
+ assertEquals(Arrays.asList(expectedPrimitive(PRIMITIVE_CLICK, 1, 0)),
mVibratorProviders.get(4).getEffectSegments(vibration.id));
}
@@ -1253,12 +1278,11 @@ public class VibrationThreadTest {
mockVibrators(1, 2, 3);
mVibratorProviders.get(1).setCapabilities(IVibrator.CAP_AMPLITUDE_CONTROL);
mVibratorProviders.get(2).setCapabilities(IVibrator.CAP_COMPOSE_EFFECTS);
- mVibratorProviders.get(2).setSupportedPrimitives(
- VibrationEffect.Composition.PRIMITIVE_CLICK);
+ mVibratorProviders.get(2).setSupportedPrimitives(PRIMITIVE_CLICK);
mVibratorProviders.get(3).setSupportedEffects(VibrationEffect.EFFECT_CLICK);
VibrationEffect composed = VibrationEffect.startComposition()
- .addPrimitive(VibrationEffect.Composition.PRIMITIVE_CLICK)
+ .addPrimitive(PRIMITIVE_CLICK)
.compose();
CombinedVibration effect = CombinedVibration.startSequential()
.addNext(3, VibrationEffect.get(VibrationEffect.EFFECT_CLICK), /* delay= */ 50)
@@ -1268,10 +1292,10 @@ public class VibrationThreadTest {
HalVibration vibration = startThreadAndDispatcher(effect);
waitForCompletion();
- InOrder controllerVerifier = inOrder(mControllerCallbacks);
- controllerVerifier.verify(mControllerCallbacks).onComplete(eq(3), eq(vibration.id));
- controllerVerifier.verify(mControllerCallbacks).onComplete(eq(1), eq(vibration.id));
- controllerVerifier.verify(mControllerCallbacks).onComplete(eq(2), eq(vibration.id));
+ InOrder verifier = inOrder(mControllerCallbacks);
+ verifier.verify(mControllerCallbacks).onComplete(eq(3), eq(vibration.id), anyLong());
+ verifier.verify(mControllerCallbacks).onComplete(eq(1), eq(vibration.id), anyLong());
+ verifier.verify(mControllerCallbacks).onComplete(eq(2), eq(vibration.id), anyLong());
InOrder batteryVerifier = inOrder(mManagerHooks);
batteryVerifier.verify(mManagerHooks).noteVibratorOn(eq(UID), eq(20L));
@@ -1289,8 +1313,7 @@ public class VibrationThreadTest {
assertEquals(Arrays.asList(expectedOneShot(10)),
mVibratorProviders.get(1).getEffectSegments(vibration.id));
assertEquals(expectedAmplitudes(100), mVibratorProviders.get(1).getAmplitudes());
- assertEquals(Arrays.asList(
- expectedPrimitive(VibrationEffect.Composition.PRIMITIVE_CLICK, 1, 0)),
+ assertEquals(Arrays.asList(expectedPrimitive(PRIMITIVE_CLICK, 1, 0)),
mVibratorProviders.get(2).getEffectSegments(vibration.id));
assertEquals(Arrays.asList(expectedPrebaked(VibrationEffect.EFFECT_CLICK)),
mVibratorProviders.get(3).getEffectSegments(vibration.id));
@@ -1301,15 +1324,13 @@ public class VibrationThreadTest {
int[] vibratorIds = new int[]{1, 2};
mockVibrators(vibratorIds);
mVibratorProviders.get(1).setCapabilities(IVibrator.CAP_COMPOSE_EFFECTS);
- mVibratorProviders.get(1).setSupportedPrimitives(
- VibrationEffect.Composition.PRIMITIVE_CLICK);
+ mVibratorProviders.get(1).setSupportedPrimitives(PRIMITIVE_CLICK);
mVibratorProviders.get(2).setCapabilities(IVibrator.CAP_COMPOSE_EFFECTS);
- mVibratorProviders.get(2).setSupportedPrimitives(
- VibrationEffect.Composition.PRIMITIVE_CLICK);
+ mVibratorProviders.get(2).setSupportedPrimitives(PRIMITIVE_CLICK);
when(mManagerHooks.prepareSyncedVibration(anyLong(), eq(vibratorIds))).thenReturn(true);
VibrationEffect composed = VibrationEffect.startComposition()
- .addPrimitive(VibrationEffect.Composition.PRIMITIVE_CLICK, 1, 100)
+ .addPrimitive(PRIMITIVE_CLICK, 1, 100)
.compose();
CombinedVibration effect = CombinedVibration.createParallel(composed);
// We create the HalVibration here to obtain the vibration id and use it to mock the
@@ -1331,8 +1352,7 @@ public class VibrationThreadTest {
verify(mManagerHooks, never()).cancelSyncedVibration();
verifyCallbacksTriggered(vibration, Status.FINISHED);
- VibrationEffectSegment expected = expectedPrimitive(
- VibrationEffect.Composition.PRIMITIVE_CLICK, 1, 100);
+ VibrationEffectSegment expected = expectedPrimitive(PRIMITIVE_CLICK, 1, 100);
assertEquals(Arrays.asList(expected),
mVibratorProviders.get(1).getEffectSegments(vibration.id));
assertEquals(Arrays.asList(expected),
@@ -1345,12 +1365,11 @@ public class VibrationThreadTest {
mockVibrators(vibratorIds);
mVibratorProviders.get(1).setSupportedEffects(VibrationEffect.EFFECT_CLICK);
mVibratorProviders.get(4).setCapabilities(IVibrator.CAP_COMPOSE_EFFECTS);
- mVibratorProviders.get(4).setSupportedPrimitives(
- VibrationEffect.Composition.PRIMITIVE_CLICK);
+ mVibratorProviders.get(4).setSupportedPrimitives(PRIMITIVE_CLICK);
when(mManagerHooks.prepareSyncedVibration(anyLong(), any())).thenReturn(true);
VibrationEffect composed = VibrationEffect.startComposition()
- .addPrimitive(VibrationEffect.Composition.PRIMITIVE_CLICK)
+ .addPrimitive(PRIMITIVE_CLICK)
.compose();
CombinedVibration effect = CombinedVibration.startParallel()
.addVibrator(1, VibrationEffect.get(VibrationEffect.EFFECT_CLICK))
@@ -1463,9 +1482,9 @@ public class VibrationThreadTest {
verify(mManagerHooks).noteVibratorOn(eq(UID), eq(80L));
verify(mManagerHooks).noteVibratorOff(eq(UID));
- verify(mControllerCallbacks).onComplete(eq(1), eq(vibration.id));
- verify(mControllerCallbacks).onComplete(eq(2), eq(vibration.id));
- verify(mControllerCallbacks).onComplete(eq(3), eq(vibration.id));
+ verify(mControllerCallbacks).onComplete(eq(1), eq(vibration.id), anyLong());
+ verify(mControllerCallbacks).onComplete(eq(2), eq(vibration.id), anyLong());
+ verify(mControllerCallbacks).onComplete(eq(3), eq(vibration.id), anyLong());
verifyCallbacksTriggered(vibration, Status.FINISHED);
assertFalse(mControllers.get(1).isVibrating());
assertFalse(mControllers.get(2).isVibrating());
@@ -1505,7 +1524,7 @@ public class VibrationThreadTest {
waitForCompletion(rampDownDuration + TEST_TIMEOUT_MILLIS);
long completionTime = SystemClock.elapsedRealtime();
- verify(mControllerCallbacks).onComplete(VIBRATOR_ID, vibration.id);
+ verify(mControllerCallbacks).onComplete(eq(VIBRATOR_ID), eq(vibration.id), anyLong());
// Vibration ends after duration, thread completed after ramp down
assertThat(vibrationEndTime - startTime).isAtLeast(expectedDuration);
assertThat(vibrationEndTime - startTime).isLessThan(expectedDuration + rampDownDuration);
@@ -1534,7 +1553,7 @@ public class VibrationThreadTest {
waitForCompletion(TEST_TIMEOUT_MILLIS);
- verify(mControllerCallbacks).onComplete(VIBRATOR_ID, vibration.id);
+ verify(mControllerCallbacks).onComplete(eq(VIBRATOR_ID), eq(vibration.id), anyLong());
assertThat(vibrationEndTime - startTime).isAtLeast(expectedDuration + callbackDelay);
}
@@ -1563,7 +1582,8 @@ public class VibrationThreadTest {
waitForCompletion(callbackDelay + TEST_TIMEOUT_MILLIS);
long completionTime = SystemClock.elapsedRealtime();
- verify(mControllerCallbacks, never()).onComplete(VIBRATOR_ID, vibration.id);
+ verify(mControllerCallbacks, never())
+ .onComplete(eq(VIBRATOR_ID), eq(vibration.id), anyLong());
// Vibration ends and thread completes after timeout, before the HAL callback
assertThat(vibrationEndTime - startTime).isAtLeast(expectedDuration + callbackTimeout);
assertThat(vibrationEndTime - startTime).isLessThan(expectedDuration + callbackDelay);
@@ -1639,15 +1659,14 @@ public class VibrationThreadTest {
mockVibrators(1, 2);
mVibratorProviders.get(1).setSupportedEffects(VibrationEffect.EFFECT_CLICK);
mVibratorProviders.get(2).setCapabilities(IVibrator.CAP_COMPOSE_EFFECTS);
- mVibratorProviders.get(2).setSupportedPrimitives(
- VibrationEffect.Composition.PRIMITIVE_CLICK);
+ mVibratorProviders.get(2).setSupportedPrimitives(PRIMITIVE_CLICK);
CombinedVibration effect = CombinedVibration.startParallel()
.addVibrator(1, VibrationEffect.get(VibrationEffect.EFFECT_CLICK))
.addVibrator(2, VibrationEffect.startComposition()
- .addPrimitive(VibrationEffect.Composition.PRIMITIVE_CLICK, 1f, 100)
- .addPrimitive(VibrationEffect.Composition.PRIMITIVE_CLICK, 1f, 100)
- .addPrimitive(VibrationEffect.Composition.PRIMITIVE_CLICK, 1f, 100)
+ .addPrimitive(PRIMITIVE_CLICK, 1f, 100)
+ .addPrimitive(PRIMITIVE_CLICK, 1f, 100)
+ .addPrimitive(PRIMITIVE_CLICK, 1f, 100)
.compose())
.combine();
HalVibration vibration = startThreadAndDispatcher(effect);
@@ -1672,7 +1691,7 @@ public class VibrationThreadTest {
}
@Test
- @RequiresFlagsEnabled(android.os.vibrator.Flags.FLAG_VENDOR_VIBRATION_EFFECTS)
+ @EnableFlags(Flags.FLAG_VENDOR_VIBRATION_EFFECTS)
public void vibrate_multipleVendorEffectCancel_cancelsVibrationImmediately() throws Exception {
mockVibrators(1, 2);
mVibratorProviders.get(1).setCapabilities(IVibrator.CAP_PERFORM_VENDOR_EFFECTS);
@@ -1767,7 +1786,7 @@ public class VibrationThreadTest {
HalVibration vibration = startThreadAndDispatcher(effect);
waitForCompletion();
- verify(mControllerCallbacks).onComplete(eq(VIBRATOR_ID), eq(vibration.id));
+ verify(mControllerCallbacks).onComplete(eq(VIBRATOR_ID), eq(vibration.id), anyLong());
verifyCallbacksTriggered(vibration, Status.FINISHED);
// Duration extended for 5 + 5 + 5 + 15.
@@ -1847,7 +1866,7 @@ public class VibrationThreadTest {
HalVibration vibration = startThreadAndDispatcher(effect);
waitForCompletion();
- verify(mControllerCallbacks).onComplete(eq(VIBRATOR_ID), eq(vibration.id));
+ verify(mControllerCallbacks).onComplete(eq(VIBRATOR_ID), eq(vibration.id), anyLong());
verifyCallbacksTriggered(vibration, Status.FINISHED);
assertEquals(Arrays.asList(expectedPrebaked(VibrationEffect.EFFECT_CLICK)),
@@ -1856,7 +1875,7 @@ public class VibrationThreadTest {
}
@Test
- @RequiresFlagsEnabled(android.os.vibrator.Flags.FLAG_VENDOR_VIBRATION_EFFECTS)
+ @EnableFlags(Flags.FLAG_VENDOR_VIBRATION_EFFECTS)
public void vibrate_vendorEffectWithRampDown_doesNotAddRampDown() {
when(mVibrationConfigMock.getRampDownDurationMs()).thenReturn(15);
mVibratorProviders.get(VIBRATOR_ID).setCapabilities(IVibrator.CAP_PERFORM_VENDOR_EFFECTS);
@@ -1865,7 +1884,7 @@ public class VibrationThreadTest {
HalVibration vibration = startThreadAndDispatcher(effect);
waitForCompletion();
- verify(mControllerCallbacks).onComplete(eq(VIBRATOR_ID), eq(vibration.id));
+ verify(mControllerCallbacks).onComplete(eq(VIBRATOR_ID), eq(vibration.id), anyLong());
verifyCallbacksTriggered(vibration, Status.FINISHED);
assertThat(mVibratorProviders.get(VIBRATOR_ID).getVendorEffects(vibration.id))
@@ -1879,26 +1898,24 @@ public class VibrationThreadTest {
when(mVibrationConfigMock.getRampDownDurationMs()).thenReturn(15);
mVibratorProviders.get(VIBRATOR_ID).setCapabilities(IVibrator.CAP_AMPLITUDE_CONTROL,
IVibrator.CAP_COMPOSE_EFFECTS);
- mVibratorProviders.get(VIBRATOR_ID).setSupportedPrimitives(
- VibrationEffect.Composition.PRIMITIVE_CLICK);
+ mVibratorProviders.get(VIBRATOR_ID).setSupportedPrimitives(PRIMITIVE_CLICK);
VibrationEffect effect = VibrationEffect.startComposition()
- .addPrimitive(VibrationEffect.Composition.PRIMITIVE_CLICK)
+ .addPrimitive(PRIMITIVE_CLICK)
.compose();
HalVibration vibration = startThreadAndDispatcher(effect);
waitForCompletion();
- verify(mControllerCallbacks).onComplete(eq(VIBRATOR_ID), eq(vibration.id));
+ verify(mControllerCallbacks).onComplete(eq(VIBRATOR_ID), eq(vibration.id), anyLong());
verifyCallbacksTriggered(vibration, Status.FINISHED);
- assertEquals(
- Arrays.asList(expectedPrimitive(VibrationEffect.Composition.PRIMITIVE_CLICK, 1, 0)),
+ assertEquals(Arrays.asList(expectedPrimitive(PRIMITIVE_CLICK, 1, 0)),
mVibratorProviders.get(VIBRATOR_ID).getEffectSegments(vibration.id));
assertTrue(mVibratorProviders.get(VIBRATOR_ID).getAmplitudes().isEmpty());
}
@Test
- @DisableFlags(android.os.vibrator.Flags.FLAG_NORMALIZED_PWLE_EFFECTS)
+ @DisableFlags(Flags.FLAG_NORMALIZED_PWLE_EFFECTS)
public void vibrate_pwleWithRampDown_doesNotAddRampDown() {
when(mVibrationConfigMock.getRampDownDurationMs()).thenReturn(15);
FakeVibratorControllerProvider fakeVibrator = mVibratorProviders.get(VIBRATOR_ID);
@@ -1916,7 +1933,7 @@ public class VibrationThreadTest {
HalVibration vibration = startThreadAndDispatcher(effect);
waitForCompletion();
- verify(mControllerCallbacks).onComplete(eq(VIBRATOR_ID), eq(vibration.id));
+ verify(mControllerCallbacks).onComplete(eq(VIBRATOR_ID), eq(vibration.id), anyLong());
verifyCallbacksTriggered(vibration, Status.FINISHED);
assertEquals(Arrays.asList(expectedRamp(0, 1, 150, 150, 1)),
@@ -1928,8 +1945,7 @@ public class VibrationThreadTest {
public void vibrate_multipleVibrations_withCancel() throws Exception {
mVibratorProviders.get(VIBRATOR_ID).setSupportedEffects(
VibrationEffect.EFFECT_CLICK, VibrationEffect.EFFECT_TICK);
- mVibratorProviders.get(VIBRATOR_ID).setSupportedPrimitives(
- VibrationEffect.Composition.PRIMITIVE_CLICK);
+ mVibratorProviders.get(VIBRATOR_ID).setSupportedPrimitives(PRIMITIVE_CLICK);
mVibratorProviders.get(VIBRATOR_ID).setCapabilities(IVibrator.CAP_AMPLITUDE_CONTROL,
IVibrator.CAP_COMPOSE_EFFECTS);
@@ -1940,7 +1956,7 @@ public class VibrationThreadTest {
.repeatEffectIndefinitely(VibrationEffect.get(VibrationEffect.EFFECT_TICK))
.compose();
VibrationEffect effect3 = VibrationEffect.startComposition()
- .addPrimitive(VibrationEffect.Composition.PRIMITIVE_CLICK)
+ .addPrimitive(PRIMITIVE_CLICK)
.compose();
VibrationEffect effect4 = VibrationEffect.createOneShot(8000, 100);
VibrationEffect effect5 = VibrationEffect.get(VibrationEffect.EFFECT_CLICK);
@@ -1974,14 +1990,15 @@ public class VibrationThreadTest {
assertFalse(mControllers.get(VIBRATOR_ID).isVibrating());
// Effect1
- verify(mControllerCallbacks).onComplete(VIBRATOR_ID, vibration1.id);
+ verify(mControllerCallbacks).onComplete(eq(VIBRATOR_ID), eq(vibration1.id), anyLong());
verifyCallbacksTriggered(vibration1, Status.FINISHED);
assertEquals(Arrays.asList(expectedPrebaked(VibrationEffect.EFFECT_CLICK)),
fakeVibrator.getEffectSegments(vibration1.id));
// Effect2: repeating, cancelled.
- verify(mControllerCallbacks, atLeast(2)).onComplete(VIBRATOR_ID, vibration2.id);
+ verify(mControllerCallbacks, atLeast(2))
+ .onComplete(eq(VIBRATOR_ID), eq(vibration2.id), anyLong());
verifyCallbacksTriggered(vibration2, Status.CANCELLED_BY_USER);
// The exact count of segments might vary, so just check that there's more than 2 and
@@ -1994,10 +2011,9 @@ public class VibrationThreadTest {
}
// Effect3
- verify(mControllerCallbacks).onComplete(eq(VIBRATOR_ID), eq(vibration3.id));
+ verify(mControllerCallbacks).onComplete(eq(VIBRATOR_ID), eq(vibration3.id), anyLong());
verifyCallbacksTriggered(vibration3, Status.FINISHED);
- assertEquals(Arrays.asList(
- expectedPrimitive(VibrationEffect.Composition.PRIMITIVE_CLICK, 1, 0)),
+ assertEquals(Arrays.asList(expectedPrimitive(PRIMITIVE_CLICK, 1, 0)),
fakeVibrator.getEffectSegments(vibration3.id));
// Effect4: cancelled quickly.
@@ -2014,8 +2030,7 @@ public class VibrationThreadTest {
mockVibrators(1, 2, 3);
mVibratorProviders.get(1).setCapabilities(IVibrator.CAP_AMPLITUDE_CONTROL);
mVibratorProviders.get(2).setCapabilities(IVibrator.CAP_COMPOSE_EFFECTS);
- mVibratorProviders.get(2).setSupportedPrimitives(
- VibrationEffect.Composition.PRIMITIVE_CLICK);
+ mVibratorProviders.get(2).setSupportedPrimitives(PRIMITIVE_CLICK);
mVibratorProviders.get(3).setSupportedEffects(VibrationEffect.EFFECT_CLICK);
CombinedVibration effect = CombinedVibration.startSequential()
@@ -2029,8 +2044,7 @@ public class VibrationThreadTest {
/* delay= */ TEST_TIMEOUT_MILLIS)
.addNext(2,
VibrationEffect.startComposition()
- .addPrimitive(VibrationEffect.Composition.PRIMITIVE_CLICK, 1,
- /* delay= */ TEST_TIMEOUT_MILLIS)
+ .addPrimitive(PRIMITIVE_CLICK, 1, /* delay= */ TEST_TIMEOUT_MILLIS)
.compose(),
/* delay= */ TEST_TIMEOUT_MILLIS)
.combine();
@@ -2051,8 +2065,7 @@ public class VibrationThreadTest {
assertEquals(Arrays.asList(expectedOneShot(TEST_TIMEOUT_MILLIS)),
mVibratorProviders.get(1).getEffectSegments(vibration.id));
assertEquals(expectedAmplitudes(255), mVibratorProviders.get(1).getAmplitudes());
- assertEquals(Arrays.asList(expectedPrimitive(
- VibrationEffect.Composition.PRIMITIVE_CLICK, 1, TEST_TIMEOUT_MILLIS)),
+ assertEquals(Arrays.asList(expectedPrimitive(PRIMITIVE_CLICK, 1, TEST_TIMEOUT_MILLIS)),
mVibratorProviders.get(2).getEffectSegments(vibration.id));
assertEquals(Arrays.asList(expectedPrebaked(VibrationEffect.EFFECT_CLICK)),
mVibratorProviders.get(3).getEffectSegments(vibration.id));
@@ -2205,7 +2218,7 @@ public class VibrationThreadTest {
private void verifyCallbacksTriggered(HalVibration vibration, Status expectedStatus) {
assertThat(vibration.getStatus()).isEqualTo(expectedStatus);
- verify(mManagerHooks).onVibrationThreadReleased(vibration.id);
+ verify(mManagerHooks).onVibrationThreadReleased(eq(vibration.id));
}
private static final class TestLooperAutoDispatcher extends Thread {
diff --git a/services/tests/vibrator/src/com/android/server/vibrator/VibratorControllerTest.java b/services/tests/vibrator/src/com/android/server/vibrator/VibratorControllerTest.java
index 0978f48491cc..4df13deaed7d 100644
--- a/services/tests/vibrator/src/com/android/server/vibrator/VibratorControllerTest.java
+++ b/services/tests/vibrator/src/com/android/server/vibrator/VibratorControllerTest.java
@@ -203,9 +203,10 @@ public class VibratorControllerTest {
@Test
public void setAmplitude_vibratorVibrating_setsAmplitude() {
- when(mNativeWrapperMock.on(anyLong(), anyLong())).thenAnswer(args -> args.getArgument(0));
+ when(mNativeWrapperMock.on(anyLong(), anyLong(), anyLong()))
+ .thenAnswer(args -> args.getArgument(0));
VibratorController controller = createController();
- controller.on(100, /* vibrationId= */ 1);
+ controller.on(100, 1, 1);
assertTrue(controller.isVibrating());
assertEquals(-1, controller.getCurrentAmplitude(), /* delta= */ 0);
@@ -215,81 +216,84 @@ public class VibratorControllerTest {
@Test
public void on_withDuration_turnsVibratorOn() {
- when(mNativeWrapperMock.on(anyLong(), anyLong())).thenAnswer(args -> args.getArgument(0));
+ when(mNativeWrapperMock.on(anyLong(), anyLong(), anyLong()))
+ .thenAnswer(args -> args.getArgument(0));
VibratorController controller = createController();
- controller.on(100, 10);
+ controller.on(100, 10, 20);
assertTrue(controller.isVibrating());
- verify(mNativeWrapperMock).on(eq(100L), eq(10L));
+ verify(mNativeWrapperMock).on(eq(100L), eq(10L), eq(20L));
}
@Test
public void on_withPrebaked_performsEffect() {
- when(mNativeWrapperMock.perform(anyLong(), anyLong(), anyLong())).thenReturn(10L);
+ when(mNativeWrapperMock.perform(anyLong(), anyLong(), anyLong(), anyLong()))
+ .thenReturn(10L);
VibratorController controller = createController();
PrebakedSegment prebaked = createPrebaked(VibrationEffect.EFFECT_CLICK,
VibrationEffect.EFFECT_STRENGTH_MEDIUM);
- assertEquals(10L, controller.on(prebaked, 11));
+ assertEquals(10L, controller.on(prebaked, 11, 23));
assertTrue(controller.isVibrating());
verify(mNativeWrapperMock).perform(eq((long) VibrationEffect.EFFECT_CLICK),
- eq((long) VibrationEffect.EFFECT_STRENGTH_MEDIUM), eq(11L));
+ eq((long) VibrationEffect.EFFECT_STRENGTH_MEDIUM), eq(11L), eq(23L));
}
@Test
public void on_withComposed_performsEffect() {
mockVibratorCapabilities(IVibrator.CAP_COMPOSE_EFFECTS);
- when(mNativeWrapperMock.compose(any(), anyLong())).thenReturn(15L);
+ when(mNativeWrapperMock.compose(any(), anyLong(), anyLong())).thenReturn(15L);
VibratorController controller = createController();
PrimitiveSegment[] primitives = new PrimitiveSegment[]{
new PrimitiveSegment(VibrationEffect.Composition.PRIMITIVE_CLICK, 0.5f, 10)
};
- assertEquals(15L, controller.on(primitives, 12));
+ assertEquals(15L, controller.on(primitives, 12, 34));
assertTrue(controller.isVibrating());
- verify(mNativeWrapperMock).compose(eq(primitives), eq(12L));
+ verify(mNativeWrapperMock).compose(eq(primitives), eq(12L), eq(34L));
}
@Test
public void on_withComposedPwle_performsEffect() {
mockVibratorCapabilities(IVibrator.CAP_COMPOSE_PWLE_EFFECTS);
- when(mNativeWrapperMock.composePwle(any(), anyInt(), anyLong())).thenReturn(15L);
+ when(mNativeWrapperMock.composePwle(any(), anyInt(), anyLong(), anyLong())).thenReturn(15L);
VibratorController controller = createController();
RampSegment[] primitives = new RampSegment[]{
new RampSegment(/* startAmplitude= */ 0, /* endAmplitude= */ 1,
/* startFrequencyHz= */ 100, /* endFrequencyHz= */ 200, /* duration= */ 10)
};
- assertEquals(15L, controller.on(primitives, 12));
+ assertEquals(15L, controller.on(primitives, 12, 45));
assertTrue(controller.isVibrating());
- verify(mNativeWrapperMock).composePwle(eq(primitives), eq(Braking.NONE), eq(12L));
+ verify(mNativeWrapperMock).composePwle(eq(primitives), eq(Braking.NONE), eq(12L), eq(45L));
}
@Test
public void on_withComposedPwleV2_performsEffect() {
mockVibratorCapabilities(IVibrator.CAP_COMPOSE_PWLE_EFFECTS_V2);
- when(mNativeWrapperMock.composePwleV2(any(), anyLong())).thenReturn(15L);
+ when(mNativeWrapperMock.composePwleV2(any(), anyLong(), anyLong())).thenReturn(15L);
VibratorController controller = createController();
PwlePoint[] primitives = new PwlePoint[]{
new PwlePoint(/*amplitude=*/ 0, /*frequencyHz=*/ 100, /*timeMillis=*/ 0),
new PwlePoint(/*amplitude=*/ 1, /*frequencyHz=*/ 200, /*timeMillis=*/ 10)
};
- assertEquals(15L, controller.on(primitives, 12));
+ assertEquals(15L, controller.on(primitives, 12, 53));
assertTrue(controller.isVibrating());
- verify(mNativeWrapperMock).composePwleV2(eq(primitives), eq(12L));
+ verify(mNativeWrapperMock).composePwleV2(eq(primitives), eq(12L), eq(53L));
}
@Test
public void off_turnsOffVibrator() {
- when(mNativeWrapperMock.on(anyLong(), anyLong())).thenAnswer(args -> args.getArgument(0));
+ when(mNativeWrapperMock.on(anyLong(), anyLong(), anyLong()))
+ .thenAnswer(args -> args.getArgument(0));
VibratorController controller = createController();
- controller.on(100, 1);
+ controller.on(100, 1, 1);
assertTrue(controller.isVibrating());
controller.off();
@@ -301,10 +305,11 @@ public class VibratorControllerTest {
@Test
public void reset_turnsOffVibratorAndDisablesExternalControl() {
mockVibratorCapabilities(IVibrator.CAP_EXTERNAL_CONTROL);
- when(mNativeWrapperMock.on(anyLong(), anyLong())).thenAnswer(args -> args.getArgument(0));
+ when(mNativeWrapperMock.on(anyLong(), anyLong(), anyLong()))
+ .thenAnswer(args -> args.getArgument(0));
VibratorController controller = createController();
- controller.on(100, 1);
+ controller.on(100, 1, 1);
assertTrue(controller.isVibrating());
controller.reset();
@@ -315,12 +320,13 @@ public class VibratorControllerTest {
@Test
public void registerVibratorStateListener_callbacksAreTriggered() throws Exception {
- when(mNativeWrapperMock.on(anyLong(), anyLong())).thenAnswer(args -> args.getArgument(0));
+ when(mNativeWrapperMock.on(anyLong(), anyLong(), anyLong()))
+ .thenAnswer(args -> args.getArgument(0));
VibratorController controller = createController();
controller.registerVibratorStateListener(mVibratorStateListenerMock);
- controller.on(10, 1);
- controller.on(100, 2);
+ controller.on(10, 1, 1);
+ controller.on(100, 2, 1);
controller.off();
controller.off();
@@ -334,19 +340,20 @@ public class VibratorControllerTest {
@Test
public void unregisterVibratorStateListener_callbackNotTriggeredAfter() throws Exception {
- when(mNativeWrapperMock.on(anyLong(), anyLong())).thenAnswer(args -> args.getArgument(0));
+ when(mNativeWrapperMock.on(anyLong(), anyLong(), anyLong()))
+ .thenAnswer(args -> args.getArgument(0));
VibratorController controller = createController();
controller.registerVibratorStateListener(mVibratorStateListenerMock);
verify(mVibratorStateListenerMock).onVibrating(false);
- controller.on(10, 1);
+ controller.on(10, 1, 1);
verify(mVibratorStateListenerMock).onVibrating(true);
controller.unregisterVibratorStateListener(mVibratorStateListenerMock);
Mockito.clearInvocations(mVibratorStateListenerMock);
- controller.on(10, 1);
+ controller.on(10, 1, 1);
verifyNoMoreInteractions(mVibratorStateListenerMock);
}
diff --git a/services/tests/vibrator/utils/com/android/server/vibrator/FakeVibratorControllerProvider.java b/services/tests/vibrator/utils/com/android/server/vibrator/FakeVibratorControllerProvider.java
index 3f3476716831..bd806b7f7f37 100644
--- a/services/tests/vibrator/utils/com/android/server/vibrator/FakeVibratorControllerProvider.java
+++ b/services/tests/vibrator/utils/com/android/server/vibrator/FakeVibratorControllerProvider.java
@@ -118,11 +118,11 @@ public final class FakeVibratorControllerProvider {
}
@Override
- public long on(long milliseconds, long vibrationId) {
+ public long on(long milliseconds, long vibrationId, long stepId) {
recordEffectSegment(vibrationId, new StepSegment(VibrationEffect.DEFAULT_AMPLITUDE,
/* frequencyHz= */ 0, (int) milliseconds));
applyLatency(mOnLatency);
- scheduleListener(milliseconds, vibrationId);
+ scheduleListener(milliseconds, vibrationId, stepId);
return milliseconds;
}
@@ -139,7 +139,7 @@ public final class FakeVibratorControllerProvider {
}
@Override
- public long perform(long effect, long strength, long vibrationId) {
+ public long perform(long effect, long strength, long vibrationId, long stepId) {
if (mSupportedEffects == null
|| Arrays.binarySearch(mSupportedEffects, (int) effect) < 0) {
return 0;
@@ -147,13 +147,13 @@ public final class FakeVibratorControllerProvider {
recordEffectSegment(vibrationId,
new PrebakedSegment((int) effect, false, (int) strength));
applyLatency(mOnLatency);
- scheduleListener(EFFECT_DURATION, vibrationId);
+ scheduleListener(EFFECT_DURATION, vibrationId, stepId);
return EFFECT_DURATION;
}
@Override
public long performVendorEffect(Parcel vendorData, long strength, float scale,
- float adaptiveScale, long vibrationId) {
+ float adaptiveScale, long vibrationId, long stepId) {
if ((mCapabilities & IVibrator.CAP_PERFORM_VENDOR_EFFECTS) == 0) {
return 0;
}
@@ -161,13 +161,13 @@ public final class FakeVibratorControllerProvider {
recordVendorEffect(vibrationId,
new VibrationEffect.VendorEffect(bundle, (int) strength, scale, adaptiveScale));
applyLatency(mOnLatency);
- scheduleListener(mVendorEffectDuration, vibrationId);
+ scheduleListener(mVendorEffectDuration, vibrationId, stepId);
// HAL has unknown duration for vendor effects.
return Long.MAX_VALUE;
}
@Override
- public long compose(PrimitiveSegment[] primitives, long vibrationId) {
+ public long compose(PrimitiveSegment[] primitives, long vibrationId, long stepId) {
if (mSupportedPrimitives == null) {
return 0;
}
@@ -182,12 +182,13 @@ public final class FakeVibratorControllerProvider {
recordEffectSegment(vibrationId, primitive);
}
applyLatency(mOnLatency);
- scheduleListener(duration, vibrationId);
+ scheduleListener(duration, vibrationId, stepId);
return duration;
}
@Override
- public long composePwle(RampSegment[] primitives, int braking, long vibrationId) {
+ public long composePwle(RampSegment[] primitives, int braking, long vibrationId,
+ long stepId) {
long duration = 0;
for (RampSegment primitive : primitives) {
duration += primitive.getDuration();
@@ -195,19 +196,19 @@ public final class FakeVibratorControllerProvider {
}
recordBraking(vibrationId, braking);
applyLatency(mOnLatency);
- scheduleListener(duration, vibrationId);
+ scheduleListener(duration, vibrationId, stepId);
return duration;
}
@Override
- public long composePwleV2(PwlePoint[] pwlePoints, long vibrationId) {
+ public long composePwleV2(PwlePoint[] pwlePoints, long vibrationId, long stepId) {
long duration = 0;
for (PwlePoint pwlePoint: pwlePoints) {
duration += pwlePoint.getTimeMillis();
recordEffectPwlePoint(vibrationId, pwlePoint);
}
applyLatency(mOnLatency);
- scheduleListener(duration, vibrationId);
+ scheduleListener(duration, vibrationId, stepId);
return duration;
}
@@ -263,8 +264,8 @@ public final class FakeVibratorControllerProvider {
}
}
- private void scheduleListener(long vibrationDuration, long vibrationId) {
- mHandler.postDelayed(() -> listener.onComplete(vibratorId, vibrationId),
+ private void scheduleListener(long vibrationDuration, long vibrationId, long stepId) {
+ mHandler.postDelayed(() -> listener.onComplete(vibratorId, vibrationId, stepId),
vibrationDuration + mCompletionCallbackDelay);
}
}
diff --git a/services/tests/wmtests/src/com/android/server/wm/ActivityRecordTests.java b/services/tests/wmtests/src/com/android/server/wm/ActivityRecordTests.java
index d53ba1d24d8c..301a7544227f 100644
--- a/services/tests/wmtests/src/com/android/server/wm/ActivityRecordTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/ActivityRecordTests.java
@@ -56,8 +56,6 @@ import static android.view.WindowManager.LayoutParams.FLAG_SHOW_WHEN_LOCKED;
import static android.view.WindowManager.LayoutParams.TYPE_APPLICATION;
import static android.view.WindowManager.LayoutParams.TYPE_APPLICATION_STARTING;
import static android.view.WindowManager.LayoutParams.TYPE_BASE_APPLICATION;
-import static android.view.WindowManager.TRANSIT_CLOSE;
-import static android.view.WindowManager.TRANSIT_OLD_ACTIVITY_OPEN;
import static android.view.WindowManager.TRANSIT_PIP;
import static android.window.StartingWindowInfo.TYPE_PARAMETER_LEGACY_SPLASH_SCREEN;
@@ -921,12 +919,6 @@ public class ActivityRecordTests extends WindowTestsBase {
// animation and AR#takeSceneTransitionInfo also clear the AR#mPendingOptions
assertNull(activity.takeSceneTransitionInfo());
assertNull(activity.getOptions());
-
- final AppTransition appTransition = activity.mDisplayContent.mAppTransition;
- spyOn(appTransition);
- activity.applyOptionsAnimation();
-
- verify(appTransition).overridePendingAppTransitionRemote(any());
}
@Test
@@ -1190,7 +1182,6 @@ public class ActivityRecordTests extends WindowTestsBase {
FINISH_RESULT_REQUESTED, activity.finishIfPossible("test", false /* oomAdj */));
assertEquals(PAUSING, activity.getState());
verify(activity).setVisibility(eq(false));
- verify(activity.mDisplayContent).prepareAppTransition(eq(TRANSIT_CLOSE));
}
/**
@@ -1237,7 +1228,6 @@ public class ActivityRecordTests extends WindowTestsBase {
activity.finishIfPossible("test", false /* oomAdj */);
verify(activity).setVisibility(eq(false));
- verify(activity.mDisplayContent).prepareAppTransition(eq(TRANSIT_CLOSE));
verify(activity.mDisplayContent, never()).executeAppTransition();
}
@@ -1254,7 +1244,6 @@ public class ActivityRecordTests extends WindowTestsBase {
activity.finishIfPossible("test", false /* oomAdj */);
verify(activity, atLeast(1)).setVisibility(eq(false));
- verify(activity.mDisplayContent).prepareAppTransition(eq(TRANSIT_CLOSE));
verify(activity.mDisplayContent).executeAppTransition();
}
@@ -1275,7 +1264,6 @@ public class ActivityRecordTests extends WindowTestsBase {
activity.finishIfPossible("test", false /* oomAdj */);
- verify(activity.mDisplayContent, never()).prepareAppTransition(eq(TRANSIT_CLOSE));
assertFalse(activity.inTransition());
// finishIfPossible -> completeFinishing -> addToFinishingAndWaitForIdle
@@ -2657,10 +2645,6 @@ public class ActivityRecordTests extends WindowTestsBase {
@Presubmit
public void testGetOrientation() {
mDisplayContent.setIgnoreOrientationRequest(false);
- // ActivityBuilder will resume top activities and cause the activity been added into
- // opening apps list. Since this test is focus on the effect of visible on getting
- // orientation, we skip app transition to avoid interference.
- doNothing().when(mDisplayContent).prepareAppTransition(anyInt());
final ActivityRecord activity = new ActivityBuilder(mAtm).setCreateTask(true).build();
activity.setVisible(true);
@@ -2925,7 +2909,6 @@ public class ActivityRecordTests extends WindowTestsBase {
sources.add(activity2);
doReturn(true).when(activity2).okToAnimate();
doReturn(true).when(activity2).isAnimating();
- assertTrue(activity2.applyAnimation(null, TRANSIT_OLD_ACTIVITY_OPEN, true, false, sources));
}
@Test
public void testTrackingStartingWindowThroughTrampoline() {
diff --git a/services/tests/wmtests/src/com/android/server/wm/DisplayContentTests.java b/services/tests/wmtests/src/com/android/server/wm/DisplayContentTests.java
index d5b9751b0f51..a99bc4966c03 100644
--- a/services/tests/wmtests/src/com/android/server/wm/DisplayContentTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/DisplayContentTests.java
@@ -37,7 +37,6 @@ import static android.view.Surface.ROTATION_0;
import static android.view.Surface.ROTATION_180;
import static android.view.Surface.ROTATION_270;
import static android.view.Surface.ROTATION_90;
-import static android.view.WindowInsets.Type.ime;
import static android.view.WindowInsets.Type.navigationBars;
import static android.view.WindowInsets.Type.statusBars;
import static android.view.WindowInsetsController.BEHAVIOR_SHOW_TRANSIENT_BARS_BY_SWIPE;
@@ -56,6 +55,7 @@ import static android.view.WindowManager.LayoutParams.TYPE_APPLICATION_ATTACHED_
import static android.view.WindowManager.LayoutParams.TYPE_APPLICATION_OVERLAY;
import static android.view.WindowManager.LayoutParams.TYPE_APPLICATION_STARTING;
import static android.view.WindowManager.LayoutParams.TYPE_BASE_APPLICATION;
+import static android.view.WindowManager.LayoutParams.TYPE_INPUT_METHOD;
import static android.view.WindowManager.LayoutParams.TYPE_INPUT_METHOD_DIALOG;
import static android.view.WindowManager.LayoutParams.TYPE_NAVIGATION_BAR;
import static android.view.WindowManager.LayoutParams.TYPE_NOTIFICATION_SHADE;
@@ -63,9 +63,6 @@ import static android.view.WindowManager.LayoutParams.TYPE_SCREENSHOT;
import static android.view.WindowManager.LayoutParams.TYPE_STATUS_BAR;
import static android.view.WindowManager.LayoutParams.TYPE_VOICE_INTERACTION;
import static android.view.WindowManager.LayoutParams.TYPE_WALLPAPER;
-import static android.view.WindowManager.TRANSIT_CLOSE;
-import static android.view.WindowManager.TRANSIT_OLD_TASK_CLOSE;
-import static android.view.WindowManager.TRANSIT_OLD_TRANSLUCENT_ACTIVITY_CLOSE;
import static android.window.DisplayAreaOrganizer.FEATURE_WINDOWED_MAGNIFICATION;
import static com.android.dx.mockito.inline.extended.ExtendedMockito.any;
@@ -1742,8 +1739,6 @@ public class DisplayContentTests extends WindowTestsBase {
public void testFixedRotationWithPip() {
final DisplayContent displayContent = mDefaultDisplay;
displayContent.setIgnoreOrientationRequest(false);
- // Unblock the condition in PinnedTaskController#continueOrientationChangeIfNeeded.
- doNothing().when(displayContent).prepareAppTransition(anyInt());
// Make resume-top really update the activity state.
setBooted(mAtm);
clearInvocations(mWm);
@@ -1826,7 +1821,7 @@ public class DisplayContentTests extends WindowTestsBase {
.setTask(nonTopVisible.getTask()).setVisible(false)
.setActivityTheme(android.R.style.Theme_Translucent).build();
final TestTransitionPlayer player = registerTestTransitionPlayer();
- mDisplayContent.requestTransitionAndLegacyPrepare(WindowManager.TRANSIT_OPEN, 0);
+ mDisplayContent.requestTransitionAndLegacyPrepare(WindowManager.TRANSIT_OPEN, 0, null);
translucentTop.setVisibility(true);
mDisplayContent.updateOrientation();
assertEquals("Non-top visible activity must be portrait",
@@ -2373,33 +2368,6 @@ public class DisplayContentTests extends WindowTestsBase {
@SetupWindows(addWindows = W_INPUT_METHOD)
@Test
- public void testShowImeScreenshot() {
- final Task rootTask = createTask(mDisplayContent);
- final Task task = createTaskInRootTask(rootTask, 0 /* userId */);
- final ActivityRecord activity = createActivityRecord(mDisplayContent, task);
- final WindowState win = newWindowBuilder("win", TYPE_BASE_APPLICATION).setWindowToken(
- activity).build();
- task.getDisplayContent().prepareAppTransition(TRANSIT_CLOSE);
- doReturn(true).when(task).okToAnimate();
- ArrayList<WindowContainer> sources = new ArrayList<>();
- sources.add(activity);
-
- mDisplayContent.setImeLayeringTarget(win);
- spyOn(mDisplayContent);
-
- // Expecting the IME screenshot only be attached when performing task closing transition.
- task.applyAnimation(null, TRANSIT_OLD_TASK_CLOSE, false /* enter */,
- false /* isVoiceInteraction */, sources);
- verify(mDisplayContent).showImeScreenshot();
-
- clearInvocations(mDisplayContent);
- activity.applyAnimation(null, TRANSIT_OLD_TRANSLUCENT_ACTIVITY_CLOSE, false /* enter */,
- false /* isVoiceInteraction */, sources);
- verify(mDisplayContent, never()).showImeScreenshot();
- }
-
- @SetupWindows(addWindows = W_INPUT_METHOD)
- @Test
public void testShowImeScreenshot_removeCurSnapshotBeforeCreateNext() {
final Task rootTask = createTask(mDisplayContent);
final Task task = createTaskInRootTask(rootTask, 0 /* userId */);
@@ -2427,32 +2395,6 @@ public class DisplayContentTests extends WindowTestsBase {
@UseTestDisplay(addWindows = {W_INPUT_METHOD})
@Test
- public void testRemoveImeScreenshot_whenTargetSurfaceWasInvisible() {
- final Task rootTask = createTask(mDisplayContent);
- final Task task = createTaskInRootTask(rootTask, 0 /* userId */);
- final ActivityRecord activity = createActivityRecord(mDisplayContent, task);
- final WindowState win = newWindowBuilder("win", TYPE_BASE_APPLICATION).setWindowToken(
- activity).build();
- win.onSurfaceShownChanged(true);
- makeWindowVisible(win, mDisplayContent.mInputMethodWindow);
- task.getDisplayContent().prepareAppTransition(TRANSIT_CLOSE);
- doReturn(true).when(task).okToAnimate();
- ArrayList<WindowContainer> sources = new ArrayList<>();
- sources.add(activity);
-
- mDisplayContent.setImeLayeringTarget(win);
- mDisplayContent.setImeInputTarget(win);
- mDisplayContent.getInsetsStateController().getImeSourceProvider().setImeShowing(true);
- task.applyAnimation(null, TRANSIT_OLD_TASK_CLOSE, false /* enter */,
- false /* isVoiceInteraction */, sources);
- assertNotNull(mDisplayContent.mImeScreenshot);
-
- win.onSurfaceShownChanged(false);
- assertNull(mDisplayContent.mImeScreenshot);
- }
-
- @UseTestDisplay(addWindows = {W_INPUT_METHOD})
- @Test
public void testRemoveImeScreenshot_whenWindowRemoveImmediately() {
final Task rootTask = createTask(mDisplayContent);
final Task task = createTaskInRootTask(rootTask, 0 /* userId */);
@@ -2751,55 +2693,77 @@ public class DisplayContentTests extends WindowTestsBase {
@SetupWindows(addWindows = W_INPUT_METHOD)
@Test
- public void testImeChildWindowFocusWhenImeLayeringTargetChanges() {
- final WindowState imeChildWindow = newWindowBuilder("imeChildWindow",
+ public void testImeChildWindowFocusWhenImeParentWindowChanges() {
+ final var imeChildWindow = newWindowBuilder("imeChildWindow",
TYPE_APPLICATION_ATTACHED_DIALOG).setParent(mImeWindow).build();
- makeWindowVisibleAndDrawn(imeChildWindow, mImeWindow);
- assertTrue(imeChildWindow.canReceiveKeys());
- mDisplayContent.setInputMethodWindowLocked(mImeWindow);
-
- // Verify imeChildWindow can be focused window if the next IME target requests IME visible.
- final WindowState imeAppTarget = newWindowBuilder("imeAppTarget",
- TYPE_BASE_APPLICATION).setDisplay(mDisplayContent).build();
- mDisplayContent.setImeLayeringTarget(imeAppTarget);
- spyOn(imeAppTarget);
- doReturn(true).when(imeAppTarget).isRequestedVisible(ime());
- assertEquals(imeChildWindow, mDisplayContent.findFocusedWindow());
-
- // Verify imeChildWindow doesn't be focused window if the next IME target does not
- // request IME visible.
- final WindowState nextImeAppTarget = newWindowBuilder("nextImeAppTarget",
- TYPE_BASE_APPLICATION).setDisplay(mDisplayContent).build();
- mDisplayContent.setImeLayeringTarget(nextImeAppTarget);
- assertNotEquals(imeChildWindow, mDisplayContent.findFocusedWindow());
+ doTestImeWindowFocusWhenImeParentWindowChanged(imeChildWindow);
}
@SetupWindows(addWindows = W_INPUT_METHOD)
@Test
- public void testImeMenuDialogFocusWhenImeLayeringTargetChanges() {
- final WindowState imeMenuDialog = newWindowBuilder("imeMenuDialog",
+ public void testImeDialogWindowFocusWhenImeParentWindowChanges() {
+ final var imeDialogWindow = newWindowBuilder("imeMenuDialog",
TYPE_INPUT_METHOD_DIALOG).build();
- makeWindowVisibleAndDrawn(imeMenuDialog, mImeWindow);
- assertTrue(imeMenuDialog.canReceiveKeys());
+ doTestImeWindowFocusWhenImeParentWindowChanged(imeDialogWindow);
+ }
+
+ @SetupWindows(addWindows = W_INPUT_METHOD)
+ @Test
+ public void testImeWindowFocusWhenImeParentWindowChanges() {
+ // Verify focusable, non-child IME windows.
+ final var otherImeWindow = newWindowBuilder("otherImeWindow",
+ TYPE_INPUT_METHOD).build();
+ doTestImeWindowFocusWhenImeParentWindowChanged(otherImeWindow);
+ }
+
+ private void doTestImeWindowFocusWhenImeParentWindowChanged(@NonNull WindowState window) {
+ makeWindowVisibleAndDrawn(window, mImeWindow);
+ assertTrue("Window canReceiveKeys", window.canReceiveKeys());
mDisplayContent.setInputMethodWindowLocked(mImeWindow);
- // Verify imeMenuDialog can be focused window if the next IME target requests IME visible.
+ // Verify window can be focused if the IME parent is visible and the IME is visible.
final WindowState imeAppTarget = newWindowBuilder("imeAppTarget",
TYPE_BASE_APPLICATION).setDisplay(mDisplayContent).build();
mDisplayContent.setImeLayeringTarget(imeAppTarget);
- imeAppTarget.setRequestedVisibleTypes(ime());
- assertEquals(imeMenuDialog, mDisplayContent.findFocusedWindow());
-
- // Verify imeMenuDialog doesn't be focused window if the next IME target is closing.
+ mDisplayContent.updateImeInputAndControlTarget(imeAppTarget);
+ final var imeProvider = mDisplayContent.getInsetsStateController().getImeSourceProvider();
+ imeProvider.setImeShowing(true);
+ final var imeParentWindow = mDisplayContent.getImeParentWindow();
+ assertNotNull("IME parent window is not null", imeParentWindow);
+ assertTrue("IME parent window is visible", imeParentWindow.isVisibleRequested());
+ assertTrue("IME is visible", imeProvider.isImeShowing());
+ assertEquals("Window is the focused one", window, mDisplayContent.findFocusedWindow());
+
+ // Verify window can't be focused if the IME parent is not visible.
final WindowState nextImeAppTarget = newWindowBuilder("nextImeAppTarget",
TYPE_BASE_APPLICATION).setDisplay(mDisplayContent).build();
makeWindowVisibleAndDrawn(nextImeAppTarget);
- // Even if the app still requests IME, the ime dialog should not gain focus if the target
- // app is invisible.
- nextImeAppTarget.setRequestedVisibleTypes(ime());
- nextImeAppTarget.mActivityRecord.setVisibility(false);
+ // Change layering target but keep input target (and thus imeParent) the same.
mDisplayContent.setImeLayeringTarget(nextImeAppTarget);
- assertNotEquals(imeMenuDialog, mDisplayContent.findFocusedWindow());
+ // IME parent window is not visible, occluded by new layering target.
+ imeParentWindow.setVisibleRequested(false);
+ assertEquals("IME parent window did not change", imeParentWindow,
+ mDisplayContent.getImeParentWindow());
+ assertFalse("IME parent window is not visible", imeParentWindow.isVisibleRequested());
+ assertTrue("IME is visible", imeProvider.isImeShowing());
+ assertNotEquals("Window is not the focused one when imeParent is not visible", window,
+ mDisplayContent.findFocusedWindow());
+
+ // Verify window can be focused if the IME is not visible.
+ mDisplayContent.updateImeInputAndControlTarget(nextImeAppTarget);
+ imeProvider.setImeShowing(false);
+ final var nextImeParentWindow = mDisplayContent.getImeParentWindow();
+ assertNotNull("Next IME parent window is not null", nextImeParentWindow);
+ assertNotEquals("IME parent window changed", imeParentWindow, nextImeParentWindow);
+ assertTrue("Next IME parent window is visible", nextImeParentWindow.isVisibleRequested());
+ assertFalse("IME is not visible", imeProvider.isImeShowing());
+ if (window.isChildWindow()) {
+ assertNotEquals("Child window is not the focused on when the IME is not visible",
+ window, mDisplayContent.findFocusedWindow());
+ } else {
+ assertEquals("Window is the focused one when the IME is not visible",
+ window, mDisplayContent.findFocusedWindow());
+ }
}
@Test
diff --git a/services/tests/wmtests/src/com/android/server/wm/PresentationControllerTests.java b/services/tests/wmtests/src/com/android/server/wm/PresentationControllerTests.java
index 7c8a8835c3b8..2d4101e40615 100644
--- a/services/tests/wmtests/src/com/android/server/wm/PresentationControllerTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/PresentationControllerTests.java
@@ -31,7 +31,7 @@ import static org.mockito.ArgumentMatchers.eq;
import android.annotation.NonNull;
import android.graphics.Rect;
import android.os.UserHandle;
-import android.os.UserManager;
+import android.platform.test.annotations.DisableFlags;
import android.platform.test.annotations.EnableFlags;
import android.platform.test.annotations.Presubmit;
import android.view.DisplayInfo;
@@ -68,32 +68,14 @@ public class PresentationControllerTests extends WindowTestsBase {
@EnableFlags(FLAG_ENABLE_PRESENTATION_FOR_CONNECTED_DISPLAYS)
@Test
public void testPresentationShowAndHide() {
- final DisplayInfo displayInfo = new DisplayInfo();
- displayInfo.copyFrom(mDisplayInfo);
- displayInfo.flags = FLAG_PRESENTATION;
- final DisplayContent dc = createNewDisplay(displayInfo);
- final int displayId = dc.getDisplayId();
- doReturn(dc).when(mWm.mRoot).getDisplayContentOrCreate(displayId);
+ final DisplayContent dc = createPresentationDisplay();
final ActivityRecord activity = createActivityRecord(createTask(dc));
assertTrue(activity.isVisible());
- doReturn(true).when(() -> UserManager.isVisibleBackgroundUsersEnabled());
- final int uid = 100000; // uid for non-system user
- final Session session = createTestSession(mAtm, 1234 /* pid */, uid);
- final int userId = UserHandle.getUserId(uid);
- doReturn(false).when(mWm.mUmInternal).isUserVisible(eq(userId), eq(displayId));
- final WindowManager.LayoutParams params = new WindowManager.LayoutParams(
- WindowManager.LayoutParams.TYPE_PRESENTATION);
- final IWindow clientWindow = new TestIWindow();
- // Show a Presentation window, which requests the activity to be stopped.
- final int result = mWm.addWindow(session, clientWindow, params, View.VISIBLE, displayId,
- userId, WindowInsets.Type.defaultVisible(), null, new InsetsState(),
- new InsetsSourceControl.Array(), new Rect(), new float[1]);
- assertTrue(result >= WindowManagerGlobal.ADD_OKAY);
+ // Add a presentation window, which requests the activity to stop.
+ final WindowState window = addPresentationWindow(100000, dc.mDisplayId);
assertFalse(activity.isVisibleRequested());
assertTrue(activity.isVisible());
- final WindowState window = mWm.windowForClientLocked(session, clientWindow, false);
- window.mHasSurface = true;
final Transition addTransition = window.mTransitionController.getCollectingTransition();
assertEquals(TRANSIT_OPEN, addTransition.mType);
assertTrue(addTransition.isInTransition(window));
@@ -117,6 +99,51 @@ public class PresentationControllerTests extends WindowTestsBase {
assertTrue(activity.isVisible());
}
+ @DisableFlags(FLAG_ENABLE_PRESENTATION_FOR_CONNECTED_DISPLAYS)
+ @Test
+ public void testPresentationShowAndHide_flagDisabled() {
+ final DisplayContent dc = createPresentationDisplay();
+ final ActivityRecord activity = createActivityRecord(createTask(dc));
+ assertTrue(activity.isVisible());
+
+ final WindowState window = addPresentationWindow(100000, dc.mDisplayId);
+ assertFalse(window.mTransitionController.isCollecting());
+ assertTrue(activity.isVisibleRequested());
+ assertTrue(activity.isVisible());
+
+ window.removeIfPossible();
+ assertFalse(window.mTransitionController.isCollecting());
+ assertTrue(activity.isVisibleRequested());
+ assertTrue(activity.isVisible());
+ assertFalse(window.isAttached());
+ }
+
+ private WindowState addPresentationWindow(int uid, int displayId) {
+ final Session session = createTestSession(mAtm, 1234 /* pid */, uid);
+ final int userId = UserHandle.getUserId(uid);
+ doReturn(true).when(mWm.mUmInternal).isUserVisible(eq(userId), eq(displayId));
+ final WindowManager.LayoutParams params = new WindowManager.LayoutParams(
+ WindowManager.LayoutParams.TYPE_PRESENTATION);
+ final IWindow clientWindow = new TestIWindow();
+ final int res = mWm.addWindow(session, clientWindow, params, View.VISIBLE, displayId,
+ userId, WindowInsets.Type.defaultVisible(), null, new InsetsState(),
+ new InsetsSourceControl.Array(), new Rect(), new float[1]);
+ assertTrue(res >= WindowManagerGlobal.ADD_OKAY);
+ final WindowState window = mWm.windowForClientLocked(session, clientWindow, false);
+ window.mHasSurface = true;
+ return window;
+ }
+
+ private DisplayContent createPresentationDisplay() {
+ final DisplayInfo displayInfo = new DisplayInfo();
+ displayInfo.copyFrom(mDisplayInfo);
+ displayInfo.flags = FLAG_PRESENTATION;
+ final DisplayContent dc = createNewDisplay(displayInfo);
+ final int displayId = dc.getDisplayId();
+ doReturn(dc).when(mWm.mRoot).getDisplayContentOrCreate(displayId);
+ return dc;
+ }
+
private void completeTransition(@NonNull Transition transition, boolean abortSync) {
final ActionChain chain = ActionChain.testFinish(transition);
if (abortSync) {
diff --git a/services/tests/wmtests/src/com/android/server/wm/SystemServicesTestRule.java b/services/tests/wmtests/src/com/android/server/wm/SystemServicesTestRule.java
index 9406779c929d..3776b03695d5 100644
--- a/services/tests/wmtests/src/com/android/server/wm/SystemServicesTestRule.java
+++ b/services/tests/wmtests/src/com/android/server/wm/SystemServicesTestRule.java
@@ -264,7 +264,7 @@ public class SystemServicesTestRule implements TestRule {
final DisplayManagerGlobal dmg = DisplayManagerGlobal.getInstance();
spyOn(dmg);
doNothing().when(dmg).registerDisplayListener(
- any(), any(Executor.class), anyLong(), anyString());
+ any(), any(Executor.class), anyLong(), anyString(), anyBoolean());
doNothing().when(dmg).registerTopologyListener(any(Executor.class), any(), anyString());
}
diff --git a/services/tests/wmtests/src/com/android/server/wm/TransitionTests.java b/services/tests/wmtests/src/com/android/server/wm/TransitionTests.java
index 2ee34d3a4b36..c0cb09f9a7d7 100644
--- a/services/tests/wmtests/src/com/android/server/wm/TransitionTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/TransitionTests.java
@@ -82,8 +82,6 @@ import android.graphics.Color;
import android.graphics.Point;
import android.graphics.Rect;
import android.os.IBinder;
-import android.platform.test.annotations.DisableFlags;
-import android.platform.test.annotations.EnableFlags;
import android.platform.test.annotations.Presubmit;
import android.util.ArrayMap;
import android.util.ArraySet;
@@ -104,7 +102,6 @@ import androidx.annotation.NonNull;
import androidx.test.filters.SmallTest;
import com.android.internal.graphics.ColorUtils;
-import com.android.window.flags.Flags;
import org.junit.Test;
import org.junit.runner.RunWith;
@@ -683,7 +680,7 @@ public class TransitionTests extends WindowTestsBase {
app.onStartingWindowDrawn();
// The task appeared event should be deferred until transition ready.
assertFalse(task.taskAppearedReady());
- testPlayer.onTransactionReady(app.getSyncTransaction());
+ testPlayer.onTransactionReady();
assertTrue(task.taskAppearedReady());
assertTrue(playerProc.isRunningRemoteTransition());
assertTrue(controller.mRemotePlayer.reportRunning(delegateProc.getThread()));
@@ -1162,7 +1159,8 @@ public class TransitionTests extends WindowTestsBase {
screenDecor.updateSurfacePosition(mMockT);
assertEquals(prevPos, screenDecor.mLastSurfacePosition);
- final SurfaceControl.Transaction startTransaction = mock(SurfaceControl.Transaction.class);
+ final SurfaceControl.Transaction startTransaction = mTransaction;
+ clearInvocations(startTransaction);
final SurfaceControl.TransactionCommittedListener transactionCommittedListener =
onRotationTransactionReady(player, startTransaction);
@@ -1213,7 +1211,8 @@ public class TransitionTests extends WindowTestsBase {
assertFalse(statusBar.mToken.inTransition());
assertTrue(app.getTask().inTransition());
- final SurfaceControl.Transaction startTransaction = mock(SurfaceControl.Transaction.class);
+ final SurfaceControl.Transaction startTransaction = mTransaction;
+ clearInvocations(startTransaction);
final SurfaceControl leash = statusBar.mToken.getAnimationLeash();
doReturn(true).when(leash).isValid();
final SurfaceControl.TransactionCommittedListener transactionCommittedListener =
@@ -1287,7 +1286,8 @@ public class TransitionTests extends WindowTestsBase {
// Avoid DeviceStateController disturbing the test by triggering another rotation change.
doReturn(false).when(mDisplayContent).updateRotationUnchecked();
- onRotationTransactionReady(player, mWm.mTransactionFactory.get()).onTransactionCommitted();
+ clearInvocations(mTransaction);
+ onRotationTransactionReady(player, mTransaction).onTransactionCommitted();
assertEquals(ROTATION_ANIMATION_SEAMLESS, player.mLastReady.getChange(
mDisplayContent.mRemoteToken.toWindowContainerToken()).getRotationAnimation());
spyOn(navBarInsetsProvider);
@@ -1350,7 +1350,7 @@ public class TransitionTests extends WindowTestsBase {
mDisplayContent.setFixedRotationLaunchingAppUnchecked(home);
doReturn(true).when(home).hasFixedRotationTransform(any());
player.startTransition();
- player.onTransactionReady(mDisplayContent.getSyncTransaction());
+ player.onTransactionReady();
final DisplayRotation displayRotation = mDisplayContent.getDisplayRotation();
final RemoteDisplayChangeController displayChangeController = mDisplayContent
@@ -2009,21 +2009,6 @@ public class TransitionTests extends WindowTestsBase {
assertEquals(expectedBackgroundColor, info.getChanges().get(1).getBackgroundColor());
}
- @DisableFlags(Flags.FLAG_MOVE_ANIMATION_OPTIONS_TO_CHANGE)
- @Test
- public void testOverrideAnimationOptionsToInfoIfNecessary_disableAnimOptionsPerChange() {
- ActivityRecord r = initializeOverrideAnimationOptionsTest();
- TransitionInfo.AnimationOptions options = TransitionInfo.AnimationOptions
- .makeCommonAnimOptions("testPackage");
- mTransition.setOverrideAnimation(options, r, null /* startCallback */,
- null /* finishCallback */);
-
- mTransition.overrideAnimationOptionsToInfoIfNecessary(mInfo);
-
- assertEquals(options, mInfo.getAnimationOptions());
- }
-
- @EnableFlags(Flags.FLAG_MOVE_ANIMATION_OPTIONS_TO_CHANGE)
@Test
public void testOverrideAnimationOptionsToInfoIfNecessary_fromStyleAnimOptions() {
ActivityRecord r = initializeOverrideAnimationOptionsTest();
@@ -2049,7 +2034,6 @@ public class TransitionTests extends WindowTestsBase {
options, activityChange.getAnimationOptions());
}
- @EnableFlags(Flags.FLAG_MOVE_ANIMATION_OPTIONS_TO_CHANGE)
@Test
public void testOverrideAnimationOptionsToInfoIfNecessary_sceneAnimOptions() {
ActivityRecord r = initializeOverrideAnimationOptionsTest();
@@ -2075,7 +2059,6 @@ public class TransitionTests extends WindowTestsBase {
options, activityChange.getAnimationOptions());
}
- @EnableFlags(Flags.FLAG_MOVE_ANIMATION_OPTIONS_TO_CHANGE)
@Test
public void testOverrideAnimationOptionsToInfoIfNecessary_crossProfileAnimOptions() {
ActivityRecord r = initializeOverrideAnimationOptionsTest();
@@ -2103,7 +2086,6 @@ public class TransitionTests extends WindowTestsBase {
assertTrue(activityChange.hasFlags(FLAG_CROSS_PROFILE_OWNER_THUMBNAIL));
}
- @EnableFlags(Flags.FLAG_MOVE_ANIMATION_OPTIONS_TO_CHANGE)
@Test
public void testOverrideAnimationOptionsToInfoIfNecessary_customAnimOptions() {
ActivityRecord r = initializeOverrideAnimationOptionsTest();
@@ -2136,7 +2118,6 @@ public class TransitionTests extends WindowTestsBase {
options.getBackgroundColor(), activityChange.getBackgroundColor());
}
- @EnableFlags(Flags.FLAG_MOVE_ANIMATION_OPTIONS_TO_CHANGE)
@Test
public void testOverrideAnimationOptionsToInfoIfNecessary_haveTaskFragmentAnimParams() {
ActivityRecord r = initializeOverrideAnimationOptionsTest();
@@ -2185,7 +2166,6 @@ public class TransitionTests extends WindowTestsBase {
options.getBackgroundColor(), activityChange.getBackgroundColor());
}
- @EnableFlags(Flags.FLAG_MOVE_ANIMATION_OPTIONS_TO_CHANGE)
@Test
public void testOverrideAnimationOptionsToInfoIfNecessary_customAnimOptionsWithTaskOverride() {
ActivityRecord r = initializeOverrideAnimationOptionsTest();
@@ -2415,7 +2395,6 @@ public class TransitionTests extends WindowTestsBase {
}
@Test
- @EnableFlags(Flags.FLAG_ENABLE_DISPLAY_FOCUS_IN_SHELL_TRANSITIONS)
public void testMoveDisplayToTop() {
// Set up two displays, each of which has a task.
DisplayContent otherDisplay = createNewDisplay();
@@ -3071,8 +3050,11 @@ public class TransitionTests extends WindowTestsBase {
TestTransitionPlayer player, SurfaceControl.Transaction startTransaction) {
final ArgumentCaptor<SurfaceControl.TransactionCommittedListener> listenerCaptor =
ArgumentCaptor.forClass(SurfaceControl.TransactionCommittedListener.class);
- player.onTransactionReady(startTransaction);
- verify(startTransaction).addTransactionCommittedListener(any(), listenerCaptor.capture());
+ player.onTransactionReady();
+ // The startTransaction is from mWm.mTransactionFactory.get() in SyncGroup#finishNow.
+ // 2 times are from SyncGroup#finishNow and AsyncRotationController#setupStartTransaction.
+ verify(startTransaction, times(2)).addTransactionCommittedListener(
+ any(), listenerCaptor.capture());
return listenerCaptor.getValue();
}
}
diff --git a/services/tests/wmtests/src/com/android/server/wm/WindowContainerTests.java b/services/tests/wmtests/src/com/android/server/wm/WindowContainerTests.java
index cee98fb1b34c..4f310de1e48b 100644
--- a/services/tests/wmtests/src/com/android/server/wm/WindowContainerTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/WindowContainerTests.java
@@ -30,8 +30,6 @@ import static android.view.WindowInsets.Type.statusBars;
import static android.view.WindowInsets.Type.systemOverlays;
import static android.view.WindowManager.LayoutParams.TYPE_APPLICATION_OVERLAY;
import static android.view.WindowManager.LayoutParams.TYPE_BASE_APPLICATION;
-import static android.view.WindowManager.TRANSIT_CLOSE;
-import static android.view.WindowManager.TRANSIT_OLD_TASK_CLOSE;
import static android.window.DisplayAreaOrganizer.FEATURE_DEFAULT_TASK_CONTAINER;
import static com.android.dx.mockito.inline.extended.ExtendedMockito.any;
@@ -1121,7 +1119,6 @@ public class WindowContainerTests extends WindowTestsBase {
final ActivityRecord activity = createActivityRecord(mDisplayContent, task);
final WindowState win = newWindowBuilder("win", TYPE_BASE_APPLICATION).setWindowToken(
activity).build();
- task.getDisplayContent().prepareAppTransition(TRANSIT_CLOSE);
spyOn(win);
doReturn(true).when(task).okToAnimate();
ArrayList<WindowContainer> sources = new ArrayList<>();
@@ -1130,8 +1127,6 @@ public class WindowContainerTests extends WindowTestsBase {
// Simulate the task applying the exit transition, verify the main window of the task
// will be set the frozen insets state before the animation starts
activity.setVisibility(false);
- task.applyAnimation(null, TRANSIT_OLD_TASK_CLOSE, false /* enter */,
- false /* isVoiceInteraction */, sources);
verify(win).freezeInsetsState();
// Simulate the task transition finished.
diff --git a/services/tests/wmtests/src/com/android/server/wm/WindowTestsBase.java b/services/tests/wmtests/src/com/android/server/wm/WindowTestsBase.java
index 97c6ac6854c3..c6416850c464 100644
--- a/services/tests/wmtests/src/com/android/server/wm/WindowTestsBase.java
+++ b/services/tests/wmtests/src/com/android/server/wm/WindowTestsBase.java
@@ -2159,13 +2159,14 @@ public class WindowTestsBase extends SystemServiceTestsBase {
mOrganizer.startTransition(mLastTransit.getToken(), null);
}
- void onTransactionReady(SurfaceControl.Transaction t) {
- mLastTransit.onTransactionReady(mLastTransit.getSyncId(), t);
+ void onTransactionReady() {
+ // SyncGroup#finishNow -> Transition#onTransactionReady.
+ mController.mSyncEngine.abort(mLastTransit.getSyncId());
}
void start() {
startTransition();
- onTransactionReady(mock(SurfaceControl.Transaction.class));
+ onTransactionReady();
}
public void finish() {
diff --git a/services/voiceinteraction/java/com/android/server/soundtrigger/PhoneCallStateHandler.java b/services/voiceinteraction/java/com/android/server/soundtrigger/PhoneCallStateHandler.java
index 49ad46131b0d..df43ed973fe7 100644
--- a/services/voiceinteraction/java/com/android/server/soundtrigger/PhoneCallStateHandler.java
+++ b/services/voiceinteraction/java/com/android/server/soundtrigger/PhoneCallStateHandler.java
@@ -24,7 +24,6 @@ import android.telephony.TelephonyManager;
import android.util.Slog;
import com.android.internal.annotations.GuardedBy;
-import com.android.internal.telephony.flags.Flags;
import java.util.ArrayList;
import java.util.List;
@@ -119,28 +118,18 @@ public class PhoneCallStateHandler {
private boolean checkCallStatus() {
List<SubscriptionInfo> infoList = mSubscriptionManager.getActiveSubscriptionInfoList();
if (infoList == null) return false;
- if (!Flags.enforceTelephonyFeatureMapping()) {
- return infoList.stream()
- .filter(s -> (s.getSubscriptionId()
- != SubscriptionManager.INVALID_SUBSCRIPTION_ID))
- .anyMatch(s -> isCallOngoingFromState(
- mTelephonyManager
- .createForSubscriptionId(s.getSubscriptionId())
- .getCallStateForSubscription()));
- } else {
- return infoList.stream()
- .filter(s -> (s.getSubscriptionId()
- != SubscriptionManager.INVALID_SUBSCRIPTION_ID))
- .anyMatch(s -> {
- try {
- return isCallOngoingFromState(mTelephonyManager
- .createForSubscriptionId(s.getSubscriptionId())
- .getCallStateForSubscription());
- } catch (UnsupportedOperationException e) {
- return false;
- }
- });
- }
+ return infoList.stream()
+ .filter(s -> (s.getSubscriptionId()
+ != SubscriptionManager.INVALID_SUBSCRIPTION_ID))
+ .anyMatch(s -> {
+ try {
+ return isCallOngoingFromState(mTelephonyManager
+ .createForSubscriptionId(s.getSubscriptionId())
+ .getCallStateForSubscription());
+ } catch (UnsupportedOperationException e) {
+ return false;
+ }
+ });
}
private void updateTelephonyListeners() {
diff --git a/tests/FlickerTests/FlickerService/src/com/android/server/wm/flicker/service/transitions/flicker/LaunchTaskPortrait.kt b/tests/FlickerTests/FlickerService/src/com/android/server/wm/flicker/service/transitions/flicker/LaunchTaskPortrait.kt
new file mode 100644
index 000000000000..c82ce8a4cb1d
--- /dev/null
+++ b/tests/FlickerTests/FlickerService/src/com/android/server/wm/flicker/service/transitions/flicker/LaunchTaskPortrait.kt
@@ -0,0 +1,45 @@
+/*
+ * Copyright (C) 2025 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.server.wm.flicker.service.transitions.flicker
+
+import android.tools.Rotation
+import android.tools.flicker.FlickerConfig
+import android.tools.flicker.annotation.ExpectedScenarios
+import android.tools.flicker.annotation.FlickerConfigProvider
+import android.tools.flicker.config.FlickerConfig
+import android.tools.flicker.config.FlickerServiceConfig
+import android.tools.flicker.junit.FlickerServiceJUnit4ClassRunner
+import com.android.server.wm.flicker.service.transitions.scenarios.LaunchTask
+import org.junit.Test
+import org.junit.runner.RunWith
+
+@RunWith(FlickerServiceJUnit4ClassRunner::class)
+class LaunchTaskPortrait : LaunchTask(Rotation.ROTATION_0) {
+ @ExpectedScenarios(["TASK_TRANSITION_SCENARIO", "OPEN_NEW_TASK_APP_SCENARIO"])
+ @Test
+ override fun openNewTask() = super.openNewTask()
+
+ companion object {
+ @JvmStatic
+ @FlickerConfigProvider
+ fun flickerConfigProvider(): FlickerConfig =
+ FlickerConfig()
+ .use(FlickerServiceConfig.DEFAULT)
+ .use(TASK_TRANSITION_SCENARIO)
+ .use(OPEN_NEW_TASK_APP_SCENARIO)
+ }
+} \ No newline at end of file
diff --git a/tests/FlickerTests/FlickerService/src/com/android/server/wm/flicker/service/transitions/scenarios/LaunchTask.kt b/tests/FlickerTests/FlickerService/src/com/android/server/wm/flicker/service/transitions/scenarios/LaunchTask.kt
new file mode 100644
index 000000000000..147477d728be
--- /dev/null
+++ b/tests/FlickerTests/FlickerService/src/com/android/server/wm/flicker/service/transitions/scenarios/LaunchTask.kt
@@ -0,0 +1,130 @@
+/*
+ * Copyright (C) 2025 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.server.wm.flicker.service.transitions.scenarios
+
+import android.app.Instrumentation
+import android.tools.Rotation
+import android.tools.flicker.AssertionInvocationGroup
+import android.tools.flicker.assertors.assertions.AppWindowCoversFullScreenAtStart
+import android.tools.flicker.assertors.assertions.AppWindowOnTopAtEnd
+import android.tools.flicker.assertors.assertions.AppWindowOnTopAtStart
+import android.tools.flicker.assertors.assertions.BackgroundShowsInTransition
+import android.tools.flicker.assertors.assertions.LayerBecomesInvisible
+import android.tools.flicker.assertors.assertions.LayerBecomesVisible
+import android.tools.flicker.assertors.assertions.LayerIsNeverVisible
+import android.tools.flicker.assertors.assertions.AppWindowIsNeverVisible
+import android.tools.flicker.config.AssertionTemplates
+import android.tools.flicker.config.FlickerConfigEntry
+import android.tools.flicker.config.ScenarioId
+import android.tools.flicker.config.appclose.Components.CLOSING_APPS
+import android.tools.flicker.config.appclose.Components.CLOSING_CHANGES
+import android.tools.flicker.config.applaunch.Components.OPENING_CHANGES
+import android.tools.flicker.config.common.Components.LAUNCHER
+import android.tools.flicker.config.common.Components.WALLPAPER
+import android.tools.flicker.extractors.TaggedCujTransitionMatcher
+import android.tools.flicker.extractors.TaggedScenarioExtractorBuilder
+import android.tools.flicker.rules.ChangeDisplayOrientationRule
+import android.tools.traces.events.CujType
+import android.tools.traces.parsers.WindowManagerStateHelper
+import androidx.test.platform.app.InstrumentationRegistry
+import androidx.test.uiautomator.UiDevice
+import com.android.launcher3.tapl.LauncherInstrumentation
+import com.android.server.wm.flicker.helpers.NewTasksAppHelper
+import org.junit.After
+import org.junit.Before
+import org.junit.Test
+
+
+/**
+ * This tests performs a transition between tasks
+ */
+abstract class LaunchTask(val rotation: Rotation = Rotation.ROTATION_0) {
+ protected val instrumentation: Instrumentation = InstrumentationRegistry.getInstrumentation()
+ private val wmHelper = WindowManagerStateHelper(instrumentation)
+ private val device = UiDevice.getInstance(instrumentation)
+ private val tapl = LauncherInstrumentation()
+
+ private val launchNewTaskApp = NewTasksAppHelper(instrumentation)
+
+ @Before
+ fun setup() {
+ tapl.setEnableRotation(true)
+ ChangeDisplayOrientationRule.setRotation(rotation)
+ tapl.setExpectedRotation(rotation.value)
+ launchNewTaskApp.launchViaIntent(wmHelper)
+ }
+
+ @Test
+ open fun openNewTask() {
+ launchNewTaskApp.openNewTask(device, wmHelper)
+ }
+
+ @After
+ fun tearDown() {
+ launchNewTaskApp.exit(wmHelper)
+ }
+
+ companion object {
+ /**
+ * General task transition scenario that can be reused for any trace
+ */
+ val TASK_TRANSITION_SCENARIO =
+ FlickerConfigEntry(
+ scenarioId = ScenarioId("TASK_TRANSITION_SCENARIO"),
+ extractor = TaggedScenarioExtractorBuilder()
+ .setTargetTag(CujType.CUJ_DEFAULT_TASK_TO_TASK_ANIMATION)
+ .setTransitionMatcher(
+ TaggedCujTransitionMatcher(associatedTransitionRequired = true)
+ )
+ .build(),
+ assertions = listOf(
+ // Opening changes replace the closing ones
+ LayerBecomesInvisible(CLOSING_CHANGES),
+ AppWindowOnTopAtStart(CLOSING_CHANGES),
+ LayerBecomesVisible(OPENING_CHANGES),
+ AppWindowOnTopAtEnd(OPENING_CHANGES),
+
+ // There is a background color and it's covering the transition area
+ BackgroundShowsInTransition(CLOSING_CHANGES)
+ ).associateBy({ it }, { AssertionInvocationGroup.BLOCKING })
+ )
+
+ /**
+ * Scenario that is making assertions that are valid for the new task app but that do not
+ * apply to other task transitions in general
+ */
+ val OPEN_NEW_TASK_APP_SCENARIO =
+ FlickerConfigEntry(
+ scenarioId = ScenarioId("OPEN_NEW_TASK_APP_SCENARIO"),
+ extractor = TaggedScenarioExtractorBuilder()
+ .setTargetTag(CujType.CUJ_DEFAULT_TASK_TO_TASK_ANIMATION)
+ .setTransitionMatcher(
+ TaggedCujTransitionMatcher(associatedTransitionRequired = true)
+ )
+ .build(),
+ assertions = AssertionTemplates.COMMON_ASSERTIONS +
+ listOf(
+ // Wallpaper and launcher never visible
+ LayerIsNeverVisible(WALLPAPER, mustExist = true),
+ LayerIsNeverVisible(LAUNCHER, mustExist = true),
+ AppWindowIsNeverVisible(LAUNCHER, mustExist = true),
+ // App window covers the display at start
+ AppWindowCoversFullScreenAtStart(CLOSING_APPS)
+ ).associateBy({ it }, { AssertionInvocationGroup.BLOCKING })
+ )
+ }
+} \ No newline at end of file
diff --git a/tests/Input/Android.bp b/tests/Input/Android.bp
index 1f0bd61b5c3f..168141bf6e7d 100644
--- a/tests/Input/Android.bp
+++ b/tests/Input/Android.bp
@@ -19,6 +19,9 @@ android_test {
"src/**/*.kt",
],
asset_dirs: ["assets"],
+ kotlincflags: [
+ "-Werror",
+ ],
platform_apis: true,
certificate: "platform",
static_libs: [
diff --git a/tests/testables/src/android/testing/TestableLooper.java b/tests/testables/src/android/testing/TestableLooper.java
index 7a19add1d1b9..8cd89ce89e83 100644
--- a/tests/testables/src/android/testing/TestableLooper.java
+++ b/tests/testables/src/android/testing/TestableLooper.java
@@ -34,6 +34,7 @@ import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
import java.lang.reflect.Field;
+import java.lang.reflect.Method;
import java.util.ArrayDeque;
import java.util.Map;
import java.util.Objects;
@@ -73,15 +74,21 @@ public class TestableLooper {
/**
* Baklava introduces new {@link TestLooperManager} APIs that we can use instead of reflection.
*/
- private static boolean newTestabilityApisSupported() {
- return android.os.Flags.messageQueueTestability();
+ private static boolean isAtLeastBaklava() {
+ Method[] methods = TestLooperManager.class.getMethods();
+ for (Method method : methods) {
+ if (method.getName().equals("peekWhen")) {
+ return true;
+ }
+ }
+ return false;
// TODO(shayba): delete the above, uncomment the below.
// SDK_INT has not yet ramped to Baklava in all 25Q2 builds.
// return Build.VERSION.SDK_INT >= Build.VERSION_CODES.BAKLAVA;
}
static {
- if (newTestabilityApisSupported()) {
+ if (isAtLeastBaklava()) {
MESSAGE_QUEUE_MESSAGES_FIELD = null;
MESSAGE_NEXT_FIELD = null;
MESSAGE_WHEN_FIELD = null;
@@ -241,14 +248,14 @@ public class TestableLooper {
}
public void moveTimeForward(long milliSeconds) {
- if (newTestabilityApisSupported()) {
- moveTimeForwardModern(milliSeconds);
+ if (isAtLeastBaklava()) {
+ moveTimeForwardBaklava(milliSeconds);
} else {
moveTimeForwardLegacy(milliSeconds);
}
}
- private void moveTimeForwardModern(long milliSeconds) {
+ private void moveTimeForwardBaklava(long milliSeconds) {
// Drain all Messages from the queue.
Queue<Message> messages = new ArrayDeque<>();
while (true) {
diff --git a/tests/utils/testutils/java/android/os/test/TestLooper.java b/tests/utils/testutils/java/android/os/test/TestLooper.java
index c7a36dd3f9d8..4d379e45a81a 100644
--- a/tests/utils/testutils/java/android/os/test/TestLooper.java
+++ b/tests/utils/testutils/java/android/os/test/TestLooper.java
@@ -65,13 +65,19 @@ public class TestLooper {
private AutoDispatchThread mAutoDispatchThread;
/**
- * Modern introduces new {@link TestLooperManager} APIs that we can use instead of reflection.
+ * Baklava introduces new {@link TestLooperManager} APIs that we can use instead of reflection.
*/
- private static boolean newTestabilityApisSupported() {
- return android.os.Flags.messageQueueTestability();
+ private static boolean isAtLeastBaklava() {
+ Method[] methods = TestLooperManager.class.getMethods();
+ for (Method method : methods) {
+ if (method.getName().equals("peekWhen")) {
+ return true;
+ }
+ }
+ return false;
// TODO(shayba): delete the above, uncomment the below.
- // SDK_INT has not yet ramped to Modern in all 25Q2 builds.
- // return Build.VERSION.SDK_INT >= Build.VERSION_CODES.Modern;
+ // SDK_INT has not yet ramped to Baklava in all 25Q2 builds.
+ // return Build.VERSION.SDK_INT >= Build.VERSION_CODES.BAKLAVA;
}
static {
@@ -81,7 +87,7 @@ public class TestLooper {
THREAD_LOCAL_LOOPER_FIELD = Looper.class.getDeclaredField("sThreadLocal");
THREAD_LOCAL_LOOPER_FIELD.setAccessible(true);
- if (newTestabilityApisSupported()) {
+ if (isAtLeastBaklava()) {
MESSAGE_QUEUE_MESSAGES_FIELD = null;
MESSAGE_NEXT_FIELD = null;
MESSAGE_WHEN_FIELD = null;
@@ -130,7 +136,7 @@ public class TestLooper {
throw new RuntimeException("Reflection error constructing or accessing looper", e);
}
- if (newTestabilityApisSupported()) {
+ if (isAtLeastBaklava()) {
mTestLooperManager =
InstrumentationRegistry.getInstrumentation().acquireLooperManager(mLooper);
} else {
@@ -159,14 +165,14 @@ public class TestLooper {
}
public void moveTimeForward(long milliSeconds) {
- if (newTestabilityApisSupported()) {
- moveTimeForwardModern(milliSeconds);
+ if (isAtLeastBaklava()) {
+ moveTimeForwardBaklava(milliSeconds);
} else {
moveTimeForwardLegacy(milliSeconds);
}
}
- private void moveTimeForwardModern(long milliSeconds) {
+ private void moveTimeForwardBaklava(long milliSeconds) {
// Drain all Messages from the queue.
Queue<Message> messages = new ArrayDeque<>();
while (true) {
@@ -259,14 +265,14 @@ public class TestLooper {
* @return true if there are pending messages in the message queue
*/
public boolean isIdle() {
- if (newTestabilityApisSupported()) {
- return isIdleModern();
+ if (isAtLeastBaklava()) {
+ return isIdleBaklava();
} else {
return isIdleLegacy();
}
}
- private boolean isIdleModern() {
+ private boolean isIdleBaklava() {
Long when = mTestLooperManager.peekWhen();
return when != null && currentTime() >= when;
}
@@ -280,14 +286,14 @@ public class TestLooper {
* @return the next message in the Looper's message queue or null if there is none
*/
public Message nextMessage() {
- if (newTestabilityApisSupported()) {
- return nextMessageModern();
+ if (isAtLeastBaklava()) {
+ return nextMessageBaklava();
} else {
return nextMessageLegacy();
}
}
- private Message nextMessageModern() {
+ private Message nextMessageBaklava() {
if (isIdle()) {
return mTestLooperManager.poll();
} else {
@@ -308,14 +314,14 @@ public class TestLooper {
* Asserts that there is a message in the queue
*/
public void dispatchNext() {
- if (newTestabilityApisSupported()) {
- dispatchNextModern();
+ if (isAtLeastBaklava()) {
+ dispatchNextBaklava();
} else {
dispatchNextLegacy();
}
}
- private void dispatchNextModern() {
+ private void dispatchNextBaklava() {
assertTrue(isIdle());
Message msg = mTestLooperManager.poll();
if (msg == null) {
diff --git a/tools/aapt2/link/FlaggedResources_test.cpp b/tools/aapt2/link/FlaggedResources_test.cpp
index adf711ecfcbb..7bea96c26990 100644
--- a/tools/aapt2/link/FlaggedResources_test.cpp
+++ b/tools/aapt2/link/FlaggedResources_test.cpp
@@ -169,4 +169,18 @@ TEST_F(FlaggedResourcesTest, EnabledXmlELementAttributeRemoved) {
ASSERT_TRUE(output.contains("test.package.readWriteFlag"));
}
+TEST_F(FlaggedResourcesTest, ReadWriteFlagInPathFails) {
+ test::TestDiagnosticsImpl diag;
+ const std::string compiled_files_dir = GetTestPath("compiled");
+ ASSERT_FALSE(CompileFile(GetTestPath("res/values/flag(!test.package.rwFlag)/bools.xml"),
+ R"(<resources>
+ <bool name="bool1">false</bool>
+ </resources>)",
+ compiled_files_dir, &diag,
+ {"--feature-flags", "test.package.rwFlag=false"}));
+
+ ASSERT_TRUE(diag.GetLog().contains(
+ "Only read only flags may be used with resources: test.package.rwFlag"));
+}
+
} // namespace aapt