summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--core/api/current.txt11
-rw-r--r--core/api/system-current.txt17
-rw-r--r--core/java/android/app/AppOpsManager.java18
-rw-r--r--core/java/android/app/Notification.java24
-rw-r--r--core/java/android/app/NotificationManager.java11
-rw-r--r--core/java/android/app/PropertyInvalidatedCache.java484
-rw-r--r--core/java/android/app/SystemServiceRegistry.java5
-rw-r--r--core/java/android/app/appfunctions/AppFunctionManager.java33
-rw-r--r--core/java/android/app/performance.aconfig11
-rw-r--r--core/java/android/content/Intent.java12
-rw-r--r--core/java/android/content/pm/IPackageInstaller.aidl4
-rw-r--r--core/java/android/content/pm/flags.aconfig15
-rw-r--r--core/java/android/content/pm/parsing/ApkLite.java54
-rw-r--r--core/java/android/content/pm/parsing/ApkLiteParseUtils.java64
-rw-r--r--core/java/android/content/pm/parsing/PackageLite.java28
-rw-r--r--core/java/android/content/pm/verify/pkg/IVerificationSessionInterface.aidl9
-rw-r--r--core/java/android/content/pm/verify/pkg/VerificationSession.java21
-rw-r--r--core/java/android/hardware/camera2/params/OutputConfiguration.java121
-rw-r--r--core/java/android/hardware/display/AmbientDisplayConfiguration.java6
-rw-r--r--core/java/android/hardware/input/AidlKeyGestureEvent.aidl6
-rw-r--r--core/java/android/hardware/input/AppLaunchData.java176
-rw-r--r--core/java/android/hardware/input/KeyGestureEvent.java150
-rw-r--r--core/java/android/hardware/soundtrigger/SoundTrigger.java4
-rw-r--r--core/java/android/os/BatteryUsageStatsQuery.java5
-rw-r--r--core/java/android/os/Build.java12
-rw-r--r--core/java/android/provider/Settings.java6
-rw-r--r--core/java/android/security/forensic/ForensicEvent.java8
-rw-r--r--core/java/android/security/responsible_apis_flags.aconfig11
-rw-r--r--core/java/android/view/SurfaceControl.java49
-rw-r--r--core/java/android/view/accessibility/AccessibilityEvent.java12
-rw-r--r--core/java/android/view/accessibility/AccessibilityNodeInfo.java142
-rw-r--r--core/java/android/window/BackEvent.java5
-rw-r--r--core/java/android/window/flags/lse_desktop_experience.aconfig7
-rw-r--r--core/java/com/android/internal/jank/FrameTracker.java36
-rw-r--r--core/java/com/android/internal/os/ApplicationSharedMemory.java31
-rw-r--r--core/java/com/android/internal/os/flags.aconfig42
-rw-r--r--core/java/com/android/internal/widget/NotificationProgressBar.java352
-rw-r--r--core/java/com/android/internal/widget/NotificationProgressModel.java4
-rw-r--r--core/java/com/android/internal/widget/remotecompose/core/CompanionOperation.java6
-rw-r--r--core/java/com/android/internal/widget/remotecompose/core/CoreDocument.java278
-rw-r--r--core/java/com/android/internal/widget/remotecompose/core/DocumentedCompanion.java4
-rw-r--r--core/java/com/android/internal/widget/remotecompose/core/Operation.java12
-rw-r--r--core/java/com/android/internal/widget/remotecompose/core/Operations.java54
-rw-r--r--core/java/com/android/internal/widget/remotecompose/core/PaintContext.java152
-rw-r--r--core/java/com/android/internal/widget/remotecompose/core/PaintOperation.java13
-rw-r--r--core/java/com/android/internal/widget/remotecompose/core/Platform.java33
-rw-r--r--core/java/com/android/internal/widget/remotecompose/core/RemoteComposeBuffer.java988
-rw-r--r--core/java/com/android/internal/widget/remotecompose/core/RemoteComposeOperation.java4
-rw-r--r--core/java/com/android/internal/widget/remotecompose/core/RemoteComposeState.java173
-rw-r--r--core/java/com/android/internal/widget/remotecompose/core/RemoteContext.java146
-rw-r--r--core/java/com/android/internal/widget/remotecompose/core/TimeVariables.java9
-rw-r--r--core/java/com/android/internal/widget/remotecompose/core/VariableSupport.java11
-rw-r--r--core/java/com/android/internal/widget/remotecompose/core/WireBuffer.java14
-rw-r--r--core/java/com/android/internal/widget/remotecompose/core/documentation/DocumentationBuilder.java6
-rw-r--r--core/java/com/android/internal/widget/remotecompose/core/documentation/DocumentedOperation.java181
-rw-r--r--core/java/com/android/internal/widget/remotecompose/core/documentation/Operation.java34
-rw-r--r--core/java/com/android/internal/widget/remotecompose/core/documentation/OperationField.java29
-rw-r--r--core/java/com/android/internal/widget/remotecompose/core/documentation/StringPair.java2
-rw-r--r--core/java/com/android/internal/widget/remotecompose/core/operations/BitmapData.java35
-rw-r--r--core/java/com/android/internal/widget/remotecompose/core/operations/ClickArea.java87
-rw-r--r--core/java/com/android/internal/widget/remotecompose/core/operations/ClipPath.java21
-rw-r--r--core/java/com/android/internal/widget/remotecompose/core/operations/ClipRect.java57
-rw-r--r--core/java/com/android/internal/widget/remotecompose/core/operations/ColorConstant.java18
-rw-r--r--core/java/com/android/internal/widget/remotecompose/core/operations/ColorExpression.java75
-rw-r--r--core/java/com/android/internal/widget/remotecompose/core/operations/ComponentValue.java42
-rw-r--r--core/java/com/android/internal/widget/remotecompose/core/operations/DataListFloat.java18
-rw-r--r--core/java/com/android/internal/widget/remotecompose/core/operations/DataListIds.java52
-rw-r--r--core/java/com/android/internal/widget/remotecompose/core/operations/DataMapIds.java105
-rw-r--r--core/java/com/android/internal/widget/remotecompose/core/operations/DataMapLookup.java148
-rw-r--r--core/java/com/android/internal/widget/remotecompose/core/operations/DrawArc.java61
-rw-r--r--core/java/com/android/internal/widget/remotecompose/core/operations/DrawBase2.java25
-rw-r--r--core/java/com/android/internal/widget/remotecompose/core/operations/DrawBase3.java46
-rw-r--r--core/java/com/android/internal/widget/remotecompose/core/operations/DrawBase4.java62
-rw-r--r--core/java/com/android/internal/widget/remotecompose/core/operations/DrawBase6.java71
-rw-r--r--core/java/com/android/internal/widget/remotecompose/core/operations/DrawBitmap.java73
-rw-r--r--core/java/com/android/internal/widget/remotecompose/core/operations/DrawBitmapInt.java134
-rw-r--r--core/java/com/android/internal/widget/remotecompose/core/operations/DrawBitmapScaled.java319
-rw-r--r--core/java/com/android/internal/widget/remotecompose/core/operations/DrawCircle.java49
-rw-r--r--core/java/com/android/internal/widget/remotecompose/core/operations/DrawLine.java48
-rw-r--r--core/java/com/android/internal/widget/remotecompose/core/operations/DrawOval.java39
-rw-r--r--core/java/com/android/internal/widget/remotecompose/core/operations/DrawPath.java12
-rw-r--r--core/java/com/android/internal/widget/remotecompose/core/operations/DrawRect.java52
-rw-r--r--core/java/com/android/internal/widget/remotecompose/core/operations/DrawRoundRect.java63
-rw-r--r--core/java/com/android/internal/widget/remotecompose/core/operations/DrawSector.java96
-rw-r--r--core/java/com/android/internal/widget/remotecompose/core/operations/DrawText.java115
-rw-r--r--core/java/com/android/internal/widget/remotecompose/core/operations/DrawTextAnchored.java103
-rw-r--r--core/java/com/android/internal/widget/remotecompose/core/operations/DrawTextOnPath.java61
-rw-r--r--core/java/com/android/internal/widget/remotecompose/core/operations/DrawTweenPath.java83
-rw-r--r--core/java/com/android/internal/widget/remotecompose/core/operations/FloatConstant.java25
-rw-r--r--core/java/com/android/internal/widget/remotecompose/core/operations/FloatExpression.java81
-rw-r--r--core/java/com/android/internal/widget/remotecompose/core/operations/Header.java68
-rw-r--r--core/java/com/android/internal/widget/remotecompose/core/operations/IntegerExpression.java63
-rw-r--r--core/java/com/android/internal/widget/remotecompose/core/operations/MatrixRestore.java12
-rw-r--r--core/java/com/android/internal/widget/remotecompose/core/operations/MatrixRotate.java38
-rw-r--r--core/java/com/android/internal/widget/remotecompose/core/operations/MatrixSave.java5
-rw-r--r--core/java/com/android/internal/widget/remotecompose/core/operations/MatrixScale.java41
-rw-r--r--core/java/com/android/internal/widget/remotecompose/core/operations/MatrixSkew.java29
-rw-r--r--core/java/com/android/internal/widget/remotecompose/core/operations/MatrixTranslate.java27
-rw-r--r--core/java/com/android/internal/widget/remotecompose/core/operations/NamedVariable.java26
-rw-r--r--core/java/com/android/internal/widget/remotecompose/core/operations/PaintData.java15
-rw-r--r--core/java/com/android/internal/widget/remotecompose/core/operations/PathData.java48
-rw-r--r--core/java/com/android/internal/widget/remotecompose/core/operations/RootContentBehavior.java102
-rw-r--r--core/java/com/android/internal/widget/remotecompose/core/operations/RootContentDescription.java15
-rw-r--r--core/java/com/android/internal/widget/remotecompose/core/operations/ShaderData.java63
-rw-r--r--core/java/com/android/internal/widget/remotecompose/core/operations/TextData.java27
-rw-r--r--core/java/com/android/internal/widget/remotecompose/core/operations/TextFromFloat.java78
-rw-r--r--core/java/com/android/internal/widget/remotecompose/core/operations/TextLength.java93
-rw-r--r--core/java/com/android/internal/widget/remotecompose/core/operations/TextLookup.java127
-rw-r--r--core/java/com/android/internal/widget/remotecompose/core/operations/TextLookupInt.java120
-rw-r--r--core/java/com/android/internal/widget/remotecompose/core/operations/TextMeasure.java140
-rw-r--r--core/java/com/android/internal/widget/remotecompose/core/operations/TextMerge.java21
-rw-r--r--core/java/com/android/internal/widget/remotecompose/core/operations/Theme.java16
-rw-r--r--core/java/com/android/internal/widget/remotecompose/core/operations/Utils.java45
-rw-r--r--core/java/com/android/internal/widget/remotecompose/core/operations/layout/ActionOperation.java9
-rw-r--r--core/java/com/android/internal/widget/remotecompose/core/operations/layout/CanvasContent.java22
-rw-r--r--core/java/com/android/internal/widget/remotecompose/core/operations/layout/ClickModifierEnd.java6
-rw-r--r--core/java/com/android/internal/widget/remotecompose/core/operations/layout/ClickModifierOperation.java26
-rw-r--r--core/java/com/android/internal/widget/remotecompose/core/operations/layout/Component.java182
-rw-r--r--core/java/com/android/internal/widget/remotecompose/core/operations/layout/ComponentEnd.java6
-rw-r--r--core/java/com/android/internal/widget/remotecompose/core/operations/layout/ComponentStart.java27
-rw-r--r--core/java/com/android/internal/widget/remotecompose/core/operations/layout/ComponentStartOperation.java3
-rw-r--r--core/java/com/android/internal/widget/remotecompose/core/operations/layout/DecoratorComponent.java5
-rw-r--r--core/java/com/android/internal/widget/remotecompose/core/operations/layout/LayoutComponent.java27
-rw-r--r--core/java/com/android/internal/widget/remotecompose/core/operations/layout/LayoutComponentContent.java29
-rw-r--r--core/java/com/android/internal/widget/remotecompose/core/operations/layout/LoopEnd.java67
-rw-r--r--core/java/com/android/internal/widget/remotecompose/core/operations/layout/LoopOperation.java116
-rw-r--r--core/java/com/android/internal/widget/remotecompose/core/operations/layout/RootLayoutComponent.java74
-rw-r--r--core/java/com/android/internal/widget/remotecompose/core/operations/layout/animation/AnimateMeasure.java164
-rw-r--r--core/java/com/android/internal/widget/remotecompose/core/operations/layout/animation/AnimationSpec.java69
-rw-r--r--core/java/com/android/internal/widget/remotecompose/core/operations/layout/animation/ParticleAnimation.java29
-rw-r--r--core/java/com/android/internal/widget/remotecompose/core/operations/layout/managers/BoxLayout.java111
-rw-r--r--core/java/com/android/internal/widget/remotecompose/core/operations/layout/managers/CanvasLayout.java38
-rw-r--r--core/java/com/android/internal/widget/remotecompose/core/operations/layout/managers/ColumnLayout.java191
-rw-r--r--core/java/com/android/internal/widget/remotecompose/core/operations/layout/managers/LayoutManager.java81
-rw-r--r--core/java/com/android/internal/widget/remotecompose/core/operations/layout/managers/RowLayout.java190
-rw-r--r--core/java/com/android/internal/widget/remotecompose/core/operations/layout/managers/StateLayout.java565
-rw-r--r--core/java/com/android/internal/widget/remotecompose/core/operations/layout/managers/TextLayout.java149
-rw-r--r--core/java/com/android/internal/widget/remotecompose/core/operations/layout/measure/ComponentMeasure.java29
-rw-r--r--core/java/com/android/internal/widget/remotecompose/core/operations/layout/measure/Measurable.java24
-rw-r--r--core/java/com/android/internal/widget/remotecompose/core/operations/layout/measure/MeasurePass.java13
-rw-r--r--core/java/com/android/internal/widget/remotecompose/core/operations/layout/measure/Size.java5
-rw-r--r--core/java/com/android/internal/widget/remotecompose/core/operations/layout/modifiers/BackgroundModifierOperation.java68
-rw-r--r--core/java/com/android/internal/widget/remotecompose/core/operations/layout/modifiers/BorderModifierOperation.java120
-rw-r--r--core/java/com/android/internal/widget/remotecompose/core/operations/layout/modifiers/ClipRectModifierOperation.java18
-rw-r--r--core/java/com/android/internal/widget/remotecompose/core/operations/layout/modifiers/ComponentModifiers.java11
-rw-r--r--core/java/com/android/internal/widget/remotecompose/core/operations/layout/modifiers/ComponentVisibilityOperation.java33
-rw-r--r--core/java/com/android/internal/widget/remotecompose/core/operations/layout/modifiers/DecoratorModifierOperation.java8
-rw-r--r--core/java/com/android/internal/widget/remotecompose/core/operations/layout/modifiers/DimensionModifierOperation.java21
-rw-r--r--core/java/com/android/internal/widget/remotecompose/core/operations/layout/modifiers/HeightModifierOperation.java12
-rw-r--r--core/java/com/android/internal/widget/remotecompose/core/operations/layout/modifiers/HostActionOperation.java19
-rw-r--r--core/java/com/android/internal/widget/remotecompose/core/operations/layout/modifiers/HostNamedActionOperation.java19
-rw-r--r--core/java/com/android/internal/widget/remotecompose/core/operations/layout/modifiers/ModifierOperation.java4
-rw-r--r--core/java/com/android/internal/widget/remotecompose/core/operations/layout/modifiers/PaddingModifierOperation.java31
-rw-r--r--core/java/com/android/internal/widget/remotecompose/core/operations/layout/modifiers/RoundedClipRectModifierOperation.java79
-rw-r--r--core/java/com/android/internal/widget/remotecompose/core/operations/layout/modifiers/ShapeType.java4
-rw-r--r--core/java/com/android/internal/widget/remotecompose/core/operations/layout/modifiers/ValueIntegerChangeActionOperation.java30
-rw-r--r--core/java/com/android/internal/widget/remotecompose/core/operations/layout/modifiers/ValueIntegerExpressionChangeActionOperation.java96
-rw-r--r--core/java/com/android/internal/widget/remotecompose/core/operations/layout/modifiers/ValueStringChangeActionOperation.java32
-rw-r--r--core/java/com/android/internal/widget/remotecompose/core/operations/layout/modifiers/WidthModifierOperation.java13
-rw-r--r--core/java/com/android/internal/widget/remotecompose/core/operations/layout/utils/DebugLog.java13
-rw-r--r--core/java/com/android/internal/widget/remotecompose/core/operations/layout/utils/StringValueSupplier.java5
-rw-r--r--core/java/com/android/internal/widget/remotecompose/core/operations/paint/PaintBundle.java674
-rw-r--r--core/java/com/android/internal/widget/remotecompose/core/operations/paint/PaintChangeAdapter.java105
-rw-r--r--core/java/com/android/internal/widget/remotecompose/core/operations/paint/PaintChanges.java48
-rw-r--r--core/java/com/android/internal/widget/remotecompose/core/operations/paint/Painter.java142
-rw-r--r--core/java/com/android/internal/widget/remotecompose/core/operations/utilities/AnimatedFloatExpression.java165
-rw-r--r--core/java/com/android/internal/widget/remotecompose/core/operations/utilities/ArrayAccess.java13
-rw-r--r--core/java/com/android/internal/widget/remotecompose/core/operations/utilities/CollectionsAccess.java14
-rw-r--r--core/java/com/android/internal/widget/remotecompose/core/operations/utilities/ColorUtils.java22
-rw-r--r--core/java/com/android/internal/widget/remotecompose/core/operations/utilities/DataMap.java46
-rw-r--r--core/java/com/android/internal/widget/remotecompose/core/operations/utilities/ImageScaling.java226
-rw-r--r--core/java/com/android/internal/widget/remotecompose/core/operations/utilities/IntFloatMap.java7
-rw-r--r--core/java/com/android/internal/widget/remotecompose/core/operations/utilities/IntIntMap.java7
-rw-r--r--core/java/com/android/internal/widget/remotecompose/core/operations/utilities/IntMap.java61
-rw-r--r--core/java/com/android/internal/widget/remotecompose/core/operations/utilities/IntegerExpressionEvaluator.java124
-rw-r--r--core/java/com/android/internal/widget/remotecompose/core/operations/utilities/NanMap.java18
-rw-r--r--core/java/com/android/internal/widget/remotecompose/core/operations/utilities/StringSerializer.java11
-rw-r--r--core/java/com/android/internal/widget/remotecompose/core/operations/utilities/StringUtils.java25
-rw-r--r--core/java/com/android/internal/widget/remotecompose/core/operations/utilities/easing/BounceCurve.java5
-rw-r--r--core/java/com/android/internal/widget/remotecompose/core/operations/utilities/easing/CubicEasing.java29
-rw-r--r--core/java/com/android/internal/widget/remotecompose/core/operations/utilities/easing/Easing.java14
-rw-r--r--core/java/com/android/internal/widget/remotecompose/core/operations/utilities/easing/ElasticOutCurve.java18
-rw-r--r--core/java/com/android/internal/widget/remotecompose/core/operations/utilities/easing/FloatAnimation.java68
-rw-r--r--core/java/com/android/internal/widget/remotecompose/core/operations/utilities/easing/GeneralEasing.java28
-rw-r--r--core/java/com/android/internal/widget/remotecompose/core/operations/utilities/easing/MonotonicCurveFit.java58
-rw-r--r--core/java/com/android/internal/widget/remotecompose/core/operations/utilities/easing/StepCurve.java15
-rw-r--r--core/java/com/android/internal/widget/remotecompose/core/types/BooleanConstant.java34
-rw-r--r--core/java/com/android/internal/widget/remotecompose/core/types/IntegerConstant.java18
-rw-r--r--core/java/com/android/internal/widget/remotecompose/core/types/LongConstant.java31
-rw-r--r--core/java/com/android/internal/widget/remotecompose/player/RemoteComposeDocument.java23
-rw-r--r--core/java/com/android/internal/widget/remotecompose/player/RemoteComposePlayer.java91
-rw-r--r--core/java/com/android/internal/widget/remotecompose/player/platform/AndroidPaintContext.java537
-rw-r--r--core/java/com/android/internal/widget/remotecompose/player/platform/AndroidPlatformServices.java119
-rw-r--r--core/java/com/android/internal/widget/remotecompose/player/platform/AndroidRemoteContext.java46
-rw-r--r--core/java/com/android/internal/widget/remotecompose/player/platform/ClickAreaView.java8
-rw-r--r--core/java/com/android/internal/widget/remotecompose/player/platform/FloatsToPath.java69
-rw-r--r--core/java/com/android/internal/widget/remotecompose/player/platform/RemoteComposeCanvas.java41
-rw-r--r--core/jni/Android.bp1
-rw-r--r--core/jni/AndroidRuntime.cpp2
-rw-r--r--core/jni/android_app_PropertyInvalidatedCache.cpp119
-rw-r--r--core/jni/android_app_PropertyInvalidatedCache.h146
-rw-r--r--core/jni/android_hardware_Camera.cpp38
-rw-r--r--core/jni/android_view_SurfaceControl.cpp24
-rw-r--r--core/jni/com_android_internal_os_ApplicationSharedMemory.cpp24
-rw-r--r--core/res/AndroidManifest.xml21
-rw-r--r--core/res/res/color-watch-v36/btn_material_filled_background_color.xml3
-rw-r--r--core/res/res/color-watch-v36/btn_material_filled_content_color.xml (renamed from core/res/res/color-watch-v36/btn_material_filled_text_color.xml)3
-rw-r--r--core/res/res/color-watch-v36/btn_material_filled_tonal_background_color.xml3
-rw-r--r--core/res/res/color-watch-v36/btn_material_filled_tonal_content_color.xml (renamed from core/res/res/color-watch-v36/btn_material_filled_tonal_text_color.xml)3
-rw-r--r--core/res/res/drawable-watch-v36/dialog_alert_button_background_negative.xml25
-rw-r--r--core/res/res/drawable-watch-v36/dialog_alert_button_background_positive.xml30
-rw-r--r--core/res/res/drawable-watch-v36/dialog_alert_button_negative.xml22
-rw-r--r--core/res/res/drawable-watch-v36/dialog_alert_button_positive.xml22
-rw-r--r--core/res/res/drawable-watch-v36/ic_check.xml25
-rw-r--r--core/res/res/drawable-watch-v36/ic_close.xml25
-rw-r--r--core/res/res/layout-watch-v36/alert_dialog_material.xml123
-rw-r--r--core/res/res/values-watch-v36/colors.xml7
-rw-r--r--core/res/res/values-watch-v36/config.xml1
-rw-r--r--core/res/res/values-watch-v36/dimens_material.xml6
-rw-r--r--core/res/res/values-watch-v36/styles_material.xml28
-rw-r--r--core/res/res/values/attrs.xml8
-rw-r--r--core/res/res/values/styles_material.xml1
-rw-r--r--core/tests/BroadcastRadioTests/src/android/hardware/radio/RadioAlertUnitTest.java377
-rw-r--r--core/tests/coretests/Android.bp4
-rw-r--r--core/tests/coretests/AndroidTest.xml12
-rw-r--r--core/tests/coretests/src/android/app/PropertyInvalidatedCacheTests.java63
-rw-r--r--core/tests/coretests/src/android/content/pm/parsing/ApkLiteParseUtilsTest.java301
-rw-r--r--core/tests/coretests/src/android/content/pm/verify/VerificationSessionTest.java4
-rw-r--r--core/tests/coretests/src/android/view/accessibility/AccessibilityNodeInfoTest.java2
-rw-r--r--core/tests/coretests/src/com/android/internal/jank/FrameTrackerTest.java40
-rw-r--r--core/tests/coretests/src/com/android/internal/widget/NotificationProgressBarTest.java76
-rw-r--r--libs/WindowManager/Shell/Android.bp7
-rw-r--r--libs/WindowManager/Shell/multivalentTests/Android.bp2
-rw-r--r--libs/WindowManager/Shell/multivalentTests/src/com/android/wm/shell/bubbles/BubbleTaskViewTest.kt31
-rw-r--r--libs/WindowManager/Shell/multivalentTests/src/com/android/wm/shell/bubbles/BubbleViewInfoTaskTest.kt32
-rw-r--r--libs/WindowManager/Shell/res/layout/desktop_mode_window_decor_maximize_menu.xml42
-rw-r--r--libs/WindowManager/Shell/res/values/dimen.xml10
-rw-r--r--libs/WindowManager/Shell/res/values/strings.xml5
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/ShellTaskOrganizer.java16
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/back/BackAnimationController.java86
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleTaskView.kt22
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/bar/BubbleEducationViewController.kt27
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopRepository.kt23
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopTasksController.kt68
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopTasksLimiter.kt55
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/draganddrop/DragUtils.java30
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipResizeGestureHandler.java189
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipTouchHandler.java2
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/pip/tv/TvPipController.java1
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/taskview/OWNERS5
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/taskview/TaskViewTaskController.java10
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/taskview/TaskViewTransitions.java38
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/DesktopModeWindowDecorViewModel.java36
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/DesktopModeWindowDecoration.java87
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/DesktopStatusBarInputLayerSupplier.kt117
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/MaximizeMenu.kt237
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/additionalviewcontainer/AdditionalSystemViewContainer.kt92
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/viewholder/AppHandleViewHolder.kt109
-rw-r--r--libs/WindowManager/Shell/tests/unittest/Android.bp1
-rw-r--r--libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/ShellTaskOrganizerTests.java37
-rw-r--r--libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/back/BackAnimationControllerTest.java6
-rw-r--r--libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopTasksLimiterTest.kt16
-rw-r--r--libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip/phone/PipResizeGestureHandlerTest.java2
-rw-r--r--libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/taskview/TaskViewTransitionsTest.java23
-rw-r--r--libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/DesktopModeWindowDecorViewModelTests.kt40
-rw-r--r--libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/DesktopModeWindowDecorationTests.java131
-rw-r--r--lint-baseline.xml13
-rw-r--r--media/java/android/media/AudioDeviceInfo.java2
-rw-r--r--media/java/android/media/AudioManager.java34
-rw-r--r--media/java/android/media/audio/common/AidlConversion.java2
-rw-r--r--media/jni/android_media_ImageReader.cpp10
-rw-r--r--packages/SettingsLib/src/com/android/settingslib/bluetooth/devicesettings/data/repository/DeviceSettingServiceConnection.kt1
-rw-r--r--packages/SettingsLib/src/com/android/settingslib/license/LicenseHtmlLoaderCompat.java1
-rw-r--r--packages/SettingsProvider/src/android/provider/settings/backup/SecureSettings.java1
-rw-r--r--packages/SettingsProvider/src/android/provider/settings/validators/SecureSettingsValidators.java1
-rw-r--r--packages/SystemUI/Android.bp1
-rw-r--r--packages/SystemUI/aconfig/systemui.aconfig31
-rw-r--r--packages/SystemUI/animation/lib/Android.bp13
-rw-r--r--packages/SystemUI/animation/lib/OWNERS3
-rw-r--r--packages/SystemUI/animation/lib/src/com/android/systemui/animation/OriginRemoteTransition.java5
-rw-r--r--packages/SystemUI/animation/lib/src/com/android/systemui/animation/OriginTransitionSession.java6
-rw-r--r--packages/SystemUI/animation/lib/src/com/android/systemui/animation/SurfaceUIComponent.java10
-rw-r--r--packages/SystemUI/animation/lib/src/com/android/systemui/animation/Transactions.java1
-rw-r--r--packages/SystemUI/animation/lib/src/com/android/systemui/animation/UIComponent.java26
-rw-r--r--packages/SystemUI/animation/lib/src/com/android/systemui/animation/ViewUIComponent.java4
-rw-r--r--packages/SystemUI/animation/src/com/android/systemui/animation/back/FlingOnBackAnimationCallback.kt20
-rw-r--r--packages/SystemUI/common/Android.bp9
-rw-r--r--packages/SystemUI/common/AndroidManifest.xml21
-rw-r--r--packages/SystemUI/common/README.md8
-rw-r--r--packages/SystemUI/compose/features/src/com/android/systemui/notifications/ui/composable/NotificationScrimNestedScrollConnection.kt61
-rw-r--r--packages/SystemUI/compose/features/src/com/android/systemui/notifications/ui/composable/NotificationStackNestedScrollConnection.kt37
-rw-r--r--packages/SystemUI/compose/features/src/com/android/systemui/notifications/ui/composable/Notifications.kt10
-rw-r--r--packages/SystemUI/compose/features/src/com/android/systemui/qs/ui/composable/QuickSettings.kt8
-rw-r--r--packages/SystemUI/compose/features/src/com/android/systemui/shade/ui/composable/ShadeScene.kt15
-rw-r--r--packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/DraggableHandler.kt97
-rw-r--r--packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/NestedScrollToScene.kt8
-rw-r--r--packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/SwipeToScene.kt13
-rw-r--r--packages/SystemUI/compose/scene/src/com/android/compose/nestedscroll/LargeTopAppBarNestedScrollConnection.kt52
-rw-r--r--packages/SystemUI/compose/scene/src/com/android/compose/nestedscroll/PriorityNestedScrollConnection.kt364
-rw-r--r--packages/SystemUI/compose/scene/tests/src/com/android/compose/animation/scene/NestedScrollToSceneTest.kt34
-rw-r--r--packages/SystemUI/compose/scene/tests/src/com/android/compose/nestedscroll/PriorityNestedScrollConnectionTest.kt30
-rw-r--r--packages/SystemUI/customization/src/com/android/systemui/shared/clocks/DefaultClockProvider.kt19
-rw-r--r--packages/SystemUI/log/Android.bp6
-rw-r--r--packages/SystemUI/log/AndroidManifest.xml22
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/systemui/ambient/touch/BouncerFullscreenSwipeTouchHandlerTest.java67
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/systemui/ambient/touch/BouncerSwipeTouchHandlerTest.java68
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/systemui/ambient/touch/ShadeTouchHandlerTest.kt85
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/domain/interactor/KeyguardLockWhileAwakeInteractorTest.kt101
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/domain/interactor/KeyguardQuickAffordanceInteractorParameterizedTest.kt27
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/domain/interactor/KeyguardTransitionScenariosTest.kt8
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/systemui/qs/QSPanelControllerBaseTest.java42
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/irecording/IssueRecordingDataInteractorTest.kt13
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/irecording/IssueRecordingUserActionInteractorTest.kt10
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/systemui/recordissue/IssueRecordingServiceSessionTest.kt10
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/systemui/recordissue/IssueRecordingStateTest.kt84
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/systemui/shade/data/repository/ShadeRepositoryImplTest.kt2
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/stack/ui/viewmodel/SharedNotificationContainerViewModelTest.kt151
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/systemui/volume/dialog/sliders/domain/interactor/VolumeDialogSliderInteractorTest.kt90
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/systemui/wallpapers/data/repository/FakeWallpaperRepository.kt5
-rw-r--r--packages/SystemUI/plugin/src/com/android/systemui/plugins/clocks/ClockProviderPlugin.kt5
-rw-r--r--packages/SystemUI/plugin_core/AndroidManifest.xml24
-rw-r--r--packages/SystemUI/res/drawable/notif_footer_btn_history.xml10
-rw-r--r--packages/SystemUI/res/drawable/notif_footer_btn_settings.xml10
-rw-r--r--packages/SystemUI/res/drawable/volume_background_top.xml41
-rw-r--r--packages/SystemUI/res/drawable/volume_background_top_legacy.xml27
-rw-r--r--packages/SystemUI/res/drawable/volume_drawer_selection_bg.xml16
-rw-r--r--packages/SystemUI/res/drawable/volume_drawer_selection_bg_legacy.xml24
-rw-r--r--packages/SystemUI/res/drawable/volume_ringer_item_bg.xml22
-rw-r--r--packages/SystemUI/res/layout-land-television/volume_dialog.xml67
-rw-r--r--packages/SystemUI/res/layout-land/volume_dialog.xml67
-rw-r--r--packages/SystemUI/res/layout-land/volume_dialog_legacy.xml2
-rw-r--r--packages/SystemUI/res/layout/status_bar_notification_footer_redesign.xml82
-rw-r--r--packages/SystemUI/res/layout/volume_dialog.xml13
-rw-r--r--packages/SystemUI/res/layout/volume_dialog_legacy.xml2
-rw-r--r--packages/SystemUI/res/layout/volume_ringer_drawer.xml108
-rw-r--r--packages/SystemUI/res/layout/volume_ringer_drawer_legacy.xml135
-rw-r--r--packages/SystemUI/res/values/dimens.xml6
-rw-r--r--packages/SystemUI/res/values/strings.xml6
-rw-r--r--packages/SystemUI/res/values/styles.xml10
-rw-r--r--packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitor.java36
-rw-r--r--packages/SystemUI/src/com/android/systemui/ambient/touch/BouncerSwipeTouchHandler.kt48
-rw-r--r--packages/SystemUI/src/com/android/systemui/ambient/touch/ShadeTouchHandler.kt23
-rw-r--r--packages/SystemUI/src/com/android/systemui/ambient/touch/dagger/ShadeModule.java9
-rw-r--r--packages/SystemUI/src/com/android/systemui/communal/domain/interactor/CommunalSceneTransitionInteractor.kt6
-rw-r--r--packages/SystemUI/src/com/android/systemui/dreams/DreamOverlayService.java9
-rw-r--r--packages/SystemUI/src/com/android/systemui/dreams/dagger/DreamOverlayModule.java9
-rw-r--r--packages/SystemUI/src/com/android/systemui/dreams/touch/CommunalTouchHandler.java19
-rw-r--r--packages/SystemUI/src/com/android/systemui/keyguard/KeyguardService.java12
-rw-r--r--packages/SystemUI/src/com/android/systemui/keyguard/data/repository/KeyguardRepository.kt15
-rw-r--r--packages/SystemUI/src/com/android/systemui/keyguard/data/repository/KeyguardTransitionRepository.kt53
-rw-r--r--packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/FromGoneTransitionInteractor.kt43
-rw-r--r--packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/FromLockscreenTransitionInteractor.kt15
-rw-r--r--packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/InternalKeyguardTransitionInteractor.kt17
-rw-r--r--packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardDismissTransitionInteractor.kt11
-rw-r--r--packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardEnabledInteractor.kt19
-rw-r--r--packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardInteractor.kt4
-rw-r--r--packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardLockWhileAwakeInteractor.kt121
-rw-r--r--packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardOcclusionInteractor.kt8
-rw-r--r--packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardTransitionBootInteractor.kt2
-rw-r--r--packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/TransitionInteractor.kt19
-rw-r--r--packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/WindowManagerLockscreenVisibilityInteractor.kt16
-rw-r--r--packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/scenetransition/LockscreenSceneTransitionInteractor.kt17
-rw-r--r--packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/DozingToOccludedTransitionViewModel.kt11
-rw-r--r--packages/SystemUI/src/com/android/systemui/qs/QSPanel.java15
-rw-r--r--packages/SystemUI/src/com/android/systemui/qs/QSPanelControllerBase.java6
-rw-r--r--packages/SystemUI/src/com/android/systemui/qs/tiles/RecordIssueTile.kt10
-rw-r--r--packages/SystemUI/src/com/android/systemui/qs/tiles/impl/irecording/IssueRecordingDataInteractor.kt2
-rw-r--r--packages/SystemUI/src/com/android/systemui/recordissue/IssueRecordingServiceSession.kt8
-rw-r--r--packages/SystemUI/src/com/android/systemui/recordissue/IssueRecordingState.kt57
-rw-r--r--packages/SystemUI/src/com/android/systemui/scene/domain/startable/ScrimStartable.kt1
-rw-r--r--packages/SystemUI/src/com/android/systemui/shade/LargeScreenHeaderHelper.kt2
-rw-r--r--packages/SystemUI/src/com/android/systemui/shade/NotificationPanelUnfoldAnimationController.kt2
-rw-r--r--packages/SystemUI/src/com/android/systemui/shade/NotificationPanelViewController.java2
-rw-r--r--packages/SystemUI/src/com/android/systemui/shade/NotificationShadeWindowControllerImpl.java2
-rw-r--r--packages/SystemUI/src/com/android/systemui/shade/QsBatteryModeController.kt2
-rw-r--r--packages/SystemUI/src/com/android/systemui/shade/ShadeViewProviderModule.kt4
-rw-r--r--packages/SystemUI/src/com/android/systemui/shade/carrier/ShadeCarrierGroupController.java5
-rw-r--r--packages/SystemUI/src/com/android/systemui/shade/data/repository/ShadeRepository.kt2
-rw-r--r--packages/SystemUI/src/com/android/systemui/shade/domain/startable/ShadeStartable.kt5
-rw-r--r--packages/SystemUI/src/com/android/systemui/shade/transition/LargeScreenShadeInterpolatorImpl.kt2
-rw-r--r--packages/SystemUI/src/com/android/systemui/shade/ui/viewmodel/ShadeHeaderViewModel.kt3
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/ConversationNotifications.kt3
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/HeadsUpManagerPhone.java9
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/EntryWithDismissStats.kt46
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/NotifCollection.java54
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/RowAppearanceCoordinator.kt3
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/ViewConfigCoordinator.kt3
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/inflation/NotificationRowBinderImpl.java3
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/render/ShadeViewManager.kt3
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/footer/ui/view/FooterView.java29
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/footer/ui/viewbinder/FooterViewBinder.kt19
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/interruption/VisualInterruptionDecisionProviderImpl.kt3
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/row/BigPictureIconManager.kt3
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/row/icon/AppIconProvider.kt3
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/AmbientState.java2
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationSectionsManager.kt3
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayout.java168
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutController.java8
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/domain/interactor/SharedNotificationContainerInteractor.kt33
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/ui/viewbinder/NotificationListViewBinder.kt5
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/ui/viewbinder/SharedNotificationContainerBinder.kt51
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/ui/viewmodel/SharedNotificationContainerViewModel.kt199
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/phone/CentralSurfacesImpl.java6
-rw-r--r--packages/SystemUI/src/com/android/systemui/volume/dialog/domain/interactor/VolumeDialogCallbacksInteractor.kt4
-rw-r--r--packages/SystemUI/src/com/android/systemui/volume/dialog/domain/interactor/VolumeDialogVisibilityInteractor.kt12
-rw-r--r--packages/SystemUI/src/com/android/systemui/volume/dialog/ringer/ui/binder/VolumeDialogRingerViewBinder.kt48
-rw-r--r--packages/SystemUI/src/com/android/systemui/volume/dialog/sliders/domain/interactor/VolumeDialogSliderInteractor.kt20
-rw-r--r--packages/SystemUI/src/com/android/systemui/volume/dialog/sliders/ui/VolumeDialogSliderViewBinder.kt57
-rw-r--r--packages/SystemUI/src/com/android/systemui/volume/dialog/sliders/ui/VolumeDialogSlidersViewBinder.kt35
-rw-r--r--packages/SystemUI/src/com/android/systemui/volume/dialog/sliders/ui/viewmodel/VolumeDialogSliderViewModel.kt67
-rw-r--r--packages/SystemUI/src/com/android/systemui/volume/dialog/sliders/ui/viewmodel/VolumeDialogSlidersViewModel.kt19
-rw-r--r--packages/SystemUI/src/com/android/systemui/volume/dialog/ui/binder/VolumeDialogBinder.kt16
-rw-r--r--packages/SystemUI/src/com/android/systemui/wallpapers/data/repository/NoopWallpaperRepository.kt2
-rw-r--r--packages/SystemUI/src/com/android/systemui/wallpapers/data/repository/WallpaperRepository.kt14
-rw-r--r--packages/SystemUI/src/com/android/systemui/wallpapers/domain/interactor/WallpaperInteractor.kt4
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/qs/QSPanelTest.kt69
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/NotifCollectionTest.java65
-rw-r--r--packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/data/repository/FakeKeyguardRepository.kt8
-rw-r--r--packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/data/repository/FakeKeyguardTransitionRepository.kt23
-rw-r--r--packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/domain/interactor/FromGoneTransitionInteractorKosmos.kt6
-rw-r--r--packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/domain/interactor/KeyguardLockWhileAwakeInteractorKosmos.kt28
-rw-r--r--packages/SystemUI/tests/utils/src/com/android/systemui/plugins/FakeVolumeDialogController.kt36
-rw-r--r--packages/SystemUI/tests/utils/src/com/android/systemui/shade/domain/startable/ShadeStartableKosmos.kt2
-rw-r--r--packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/notification/stack/domain/interactor/SharedNotificationContainerInteractorKosmos.kt2
-rw-r--r--packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/notification/stack/ui/viewmodel/SharedNotificationContainerViewModelKosmos.kt6
-rw-r--r--packages/SystemUI/tests/utils/src/com/android/systemui/volume/dialog/sliders/domain/interactor/VolumeDialogSliderInteractorKosmos.kt31
-rw-r--r--packages/SystemUI/tests/utils/src/com/android/systemui/volume/dialog/sliders/domain/model/VolumeDialogSliderTypeKosmos.kt23
-rw-r--r--ravenwood/junit-impl-src/android/platform/test/ravenwood/RavenwoodAwareTestRunnerHook.java14
-rw-r--r--ravenwood/junit-impl-src/android/platform/test/ravenwood/RavenwoodTestStats.java4
-rwxr-xr-xravenwood/scripts/ravenwood-test-summary75
-rw-r--r--services/core/java/com/android/server/SystemConfig.java21
-rw-r--r--services/core/java/com/android/server/accounts/AccountManagerService.java23
-rw-r--r--services/core/java/com/android/server/am/ActivityManagerService.java16
-rw-r--r--services/core/java/com/android/server/am/OomAdjuster.java12
-rw-r--r--services/core/java/com/android/server/am/PendingIntentRecord.java6
-rw-r--r--services/core/java/com/android/server/am/PhantomProcessList.java42
-rw-r--r--services/core/java/com/android/server/audio/AudioService.java60
-rw-r--r--services/core/java/com/android/server/audio/AudioServiceEvents.java24
-rw-r--r--services/core/java/com/android/server/display/DisplayTopology.java250
-rw-r--r--services/core/java/com/android/server/display/DisplayTopologyCoordinator.java8
-rw-r--r--services/core/java/com/android/server/hdmi/HdmiCecLocalDevicePlayback.java51
-rw-r--r--services/core/java/com/android/server/hdmi/HdmiCecLocalDeviceTv.java7
-rw-r--r--services/core/java/com/android/server/hdmi/HdmiControlService.java2
-rw-r--r--services/core/java/com/android/server/hdmi/RequestActiveSourceAction.java17
-rw-r--r--services/core/java/com/android/server/input/InputManagerInternal.java7
-rw-r--r--services/core/java/com/android/server/input/InputManagerService.java15
-rw-r--r--services/core/java/com/android/server/input/KeyGestureController.java6
-rw-r--r--services/core/java/com/android/server/integrity/parser/RuleIndexRange.java3
-rw-r--r--services/core/java/com/android/server/integrity/parser/RuleIndexingController.java128
-rw-r--r--services/core/java/com/android/server/notification/NotificationManagerService.java28
-rw-r--r--services/core/java/com/android/server/notification/PreferencesHelper.java152
-rw-r--r--services/core/java/com/android/server/pm/DefaultCrossProfileIntentFiltersUtils.java4
-rw-r--r--services/core/java/com/android/server/pm/PackageInstallerService.java23
-rw-r--r--services/core/java/com/android/server/pm/PackageInstallerSession.java14
-rw-r--r--services/core/java/com/android/server/pm/PackageManagerService.java4
-rw-r--r--services/core/java/com/android/server/pm/PackageManagerShellCommand.java92
-rw-r--r--services/core/java/com/android/server/pm/verify/pkg/VerifierController.java251
-rw-r--r--services/core/java/com/android/server/policy/ModifierShortcutManager.java63
-rw-r--r--services/core/java/com/android/server/policy/PhoneWindowManager.java11
-rw-r--r--services/core/java/com/android/server/security/forensic/DataAggregator.java142
-rw-r--r--services/core/java/com/android/server/security/forensic/DataSource.java29
-rw-r--r--services/core/java/com/android/server/security/forensic/ForensicService.java31
-rw-r--r--services/core/java/com/android/server/wm/ActivityRecord.java2
-rw-r--r--services/core/java/com/android/server/wm/ActivityStartController.java29
-rw-r--r--services/core/java/com/android/server/wm/ActivityStarter.java16
-rw-r--r--services/core/java/com/android/server/wm/ActivityTaskManagerInternal.java13
-rw-r--r--services/core/java/com/android/server/wm/ActivityTaskManagerService.java26
-rw-r--r--services/core/java/com/android/server/wm/ActivityTaskSupervisor.java5
-rw-r--r--services/core/java/com/android/server/wm/AppTaskImpl.java5
-rw-r--r--services/core/java/com/android/server/wm/BackgroundActivityStartController.java21
-rw-r--r--services/core/java/com/android/server/wm/DragDropController.java2
-rw-r--r--services/core/java/com/android/server/wm/WindowContainer.java1
-rw-r--r--services/core/lint-baseline.xml673
-rw-r--r--services/profcollect/src/com/android/server/profcollect/ProfcollectForwardingService.java21
-rw-r--r--services/tests/PackageManagerServiceTests/server/src/com/android/server/pm/verify/pkg/VerifierControllerTest.java6
-rw-r--r--services/tests/displayservicetests/src/com/android/server/display/DisplayTopologyCoordinatorTest.kt10
-rw-r--r--services/tests/displayservicetests/src/com/android/server/display/DisplayTopologyTest.kt292
-rw-r--r--services/tests/mockingservicestests/src/com/android/server/am/MockingOomAdjusterTests.java2
-rw-r--r--services/tests/security/forensic/src/com/android/server/security/forensic/ForensicServiceTest.java103
-rw-r--r--services/tests/servicestests/src/com/android/server/hdmi/HdmiCecLocalDevicePlaybackTest.java279
-rw-r--r--services/tests/servicestests/src/com/android/server/hdmi/HdmiCecLocalDeviceTvTest.java19
-rw-r--r--services/tests/servicestests/src/com/android/server/integrity/parser/RuleIndexingControllerTest.java216
-rw-r--r--services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java26
-rw-r--r--services/tests/uiservicestests/src/com/android/server/notification/PreferencesHelperTest.java192
-rw-r--r--services/tests/wmtests/src/com/android/server/policy/ModifierShortcutTests.java48
-rw-r--r--services/tests/wmtests/src/com/android/server/wm/BackgroundActivityStartControllerExemptionTests.java73
-rw-r--r--services/tests/wmtests/src/com/android/server/wm/BackgroundActivityStartControllerLogTests.java5
-rw-r--r--services/tests/wmtests/src/com/android/server/wm/BackgroundActivityStartControllerTests.java54
-rw-r--r--services/tests/wmtests/src/com/android/server/wm/TransitionTests.java5
-rw-r--r--services/voiceinteraction/java/com/android/server/soundtrigger_middleware/SoundTriggerHw3Compat.java9
-rw-r--r--telecomm/java/android/telecom/Conference.java2
-rw-r--r--telecomm/java/android/telecom/Connection.java2
-rw-r--r--telephony/java/android/telephony/TelephonyManager.java12
493 files changed, 17765 insertions, 7130 deletions
diff --git a/core/api/current.txt b/core/api/current.txt
index aa6615b69f50..94481b6f22df 100644
--- a/core/api/current.txt
+++ b/core/api/current.txt
@@ -20277,6 +20277,7 @@ package android.hardware.camera2.params {
method public long getDynamicRangeProfile();
method public int getMaxSharedSurfaceCount();
method public int getMirrorMode();
+ method @FlaggedApi("com.android.internal.camera.flags.mirror_mode_shared_surfaces") public int getMirrorMode(@NonNull android.view.Surface);
method public long getStreamUseCase();
method @Nullable public android.view.Surface getSurface();
method public int getSurfaceGroupId();
@@ -20287,6 +20288,7 @@ package android.hardware.camera2.params {
method public void removeSurface(@NonNull android.view.Surface);
method public void setDynamicRangeProfile(long);
method public void setMirrorMode(int);
+ method @FlaggedApi("com.android.internal.camera.flags.mirror_mode_shared_surfaces") public void setMirrorMode(@NonNull android.view.Surface, int);
method public void setPhysicalCameraId(@Nullable String);
method public void setReadoutTimestampEnabled(boolean);
method public void setStreamUseCase(long);
@@ -32852,6 +32854,7 @@ package android.os {
public static class Build.VERSION_CODES {
ctor public Build.VERSION_CODES();
+ field @FlaggedApi("android.sdk.major_minor_versioning_scheme") public static final int BAKLAVA = 10000; // 0x2710
field public static final int BASE = 1; // 0x1
field public static final int BASE_1_1 = 2; // 0x2
field public static final int CUPCAKE = 3; // 0x3
@@ -32891,6 +32894,7 @@ package android.os {
}
@FlaggedApi("android.sdk.major_minor_versioning_scheme") public static class Build.VERSION_CODES_FULL {
+ field public static final int BAKLAVA = 1000000000; // 0x3b9aca00
field public static final int BASE = 100000; // 0x186a0
field public static final int BASE_1_1 = 200000; // 0x30d40
field public static final int CUPCAKE = 300000; // 0x493e0
@@ -55037,6 +55041,7 @@ package android.view.accessibility {
field public static final int CONTENT_CHANGE_TYPE_DRAG_STARTED = 128; // 0x80
field public static final int CONTENT_CHANGE_TYPE_ENABLED = 4096; // 0x1000
field public static final int CONTENT_CHANGE_TYPE_ERROR = 2048; // 0x800
+ field @FlaggedApi("android.view.accessibility.a11y_expansion_state_api") public static final int CONTENT_CHANGE_TYPE_EXPANDED = 16384; // 0x4000
field public static final int CONTENT_CHANGE_TYPE_PANE_APPEARED = 16; // 0x10
field public static final int CONTENT_CHANGE_TYPE_PANE_DISAPPEARED = 32; // 0x20
field public static final int CONTENT_CHANGE_TYPE_PANE_TITLE = 8; // 0x8
@@ -55186,6 +55191,7 @@ package android.view.accessibility {
method public CharSequence getContentDescription();
method public int getDrawingOrder();
method public CharSequence getError();
+ method @FlaggedApi("android.view.accessibility.a11y_expansion_state_api") public int getExpandedState();
method @Nullable public android.view.accessibility.AccessibilityNodeInfo.ExtraRenderingInfo getExtraRenderingInfo();
method public android.os.Bundle getExtras();
method public CharSequence getHintText();
@@ -55278,6 +55284,7 @@ package android.view.accessibility {
method public void setEditable(boolean);
method public void setEnabled(boolean);
method public void setError(CharSequence);
+ method @FlaggedApi("android.view.accessibility.a11y_expansion_state_api") public void setExpandedState(int);
method public void setFocusable(boolean);
method public void setFocused(boolean);
method @FlaggedApi("android.view.accessibility.granular_scrolling") public void setGranularScrollingSupported(boolean);
@@ -55364,6 +55371,10 @@ package android.view.accessibility {
field @FlaggedApi("android.view.accessibility.tri_state_checked") public static final int CHECKED_STATE_PARTIAL = 2; // 0x2
field @FlaggedApi("android.view.accessibility.tri_state_checked") public static final int CHECKED_STATE_TRUE = 1; // 0x1
field @NonNull public static final android.os.Parcelable.Creator<android.view.accessibility.AccessibilityNodeInfo> CREATOR;
+ field @FlaggedApi("android.view.accessibility.a11y_expansion_state_api") public static final int EXPANDED_STATE_COLLAPSED = 1; // 0x1
+ field @FlaggedApi("android.view.accessibility.a11y_expansion_state_api") public static final int EXPANDED_STATE_FULL = 3; // 0x3
+ field @FlaggedApi("android.view.accessibility.a11y_expansion_state_api") public static final int EXPANDED_STATE_PARTIAL = 2; // 0x2
+ field @FlaggedApi("android.view.accessibility.a11y_expansion_state_api") public static final int EXPANDED_STATE_UNDEFINED = 0; // 0x0
field public static final String EXTRA_DATA_RENDERING_INFO_KEY = "android.view.accessibility.extra.DATA_RENDERING_INFO_KEY";
field public static final String EXTRA_DATA_TEXT_CHARACTER_LOCATION_ARG_LENGTH = "android.view.accessibility.extra.DATA_TEXT_CHARACTER_LOCATION_ARG_LENGTH";
field public static final int EXTRA_DATA_TEXT_CHARACTER_LOCATION_ARG_MAX_LENGTH = 20000; // 0x4e20
diff --git a/core/api/system-current.txt b/core/api/system-current.txt
index 1fdb6981dcdd..d1a9e67d25ee 100644
--- a/core/api/system-current.txt
+++ b/core/api/system-current.txt
@@ -209,6 +209,8 @@ package android {
field public static final String MANAGE_FACTORY_RESET_PROTECTION = "android.permission.MANAGE_FACTORY_RESET_PROTECTION";
field public static final String MANAGE_GAME_ACTIVITY = "android.permission.MANAGE_GAME_ACTIVITY";
field public static final String MANAGE_GAME_MODE = "android.permission.MANAGE_GAME_MODE";
+ field @FlaggedApi("android.media.tv.flags.media_quality_fw") public static final String MANAGE_GLOBAL_PICTURE_QUALITY_SERVICE = "android.permission.MANAGE_GLOBAL_PICTURE_QUALITY_SERVICE";
+ field @FlaggedApi("android.media.tv.flags.media_quality_fw") public static final String MANAGE_GLOBAL_SOUND_QUALITY_SERVICE = "android.permission.MANAGE_GLOBAL_SOUND_QUALITY_SERVICE";
field public static final String MANAGE_HOTWORD_DETECTION = "android.permission.MANAGE_HOTWORD_DETECTION";
field public static final String MANAGE_IPSEC_TUNNELS = "android.permission.MANAGE_IPSEC_TUNNELS";
field public static final String MANAGE_LOW_POWER_STANDBY = "android.permission.MANAGE_LOW_POWER_STANDBY";
@@ -4637,7 +4639,7 @@ package android.content.pm.verify.pkg {
@FlaggedApi("android.content.pm.verification_service") public final class VerificationSession implements android.os.Parcelable {
method public int describeContents();
- method @RequiresPermission(android.Manifest.permission.VERIFICATION_AGENT) public long extendTimeRemaining(long);
+ method public long extendTimeRemaining(long);
method @NonNull public java.util.List<android.content.pm.SharedLibraryInfo> getDeclaredLibraries();
method @NonNull public android.os.PersistableBundle getExtensionParams();
method public int getId();
@@ -4645,12 +4647,12 @@ package android.content.pm.verify.pkg {
method @NonNull public String getPackageName();
method @NonNull public android.content.pm.SigningInfo getSigningInfo();
method @NonNull public android.net.Uri getStagedPackageUri();
- method @RequiresPermission(android.Manifest.permission.VERIFICATION_AGENT) public long getTimeoutTime();
+ method public long getTimeoutTime();
method public int getVerificationPolicy();
- method @RequiresPermission(android.Manifest.permission.VERIFICATION_AGENT) public void reportVerificationComplete(@NonNull android.content.pm.verify.pkg.VerificationStatus);
- method @RequiresPermission(android.Manifest.permission.VERIFICATION_AGENT) public void reportVerificationComplete(@NonNull android.content.pm.verify.pkg.VerificationStatus, @NonNull android.os.PersistableBundle);
- method @RequiresPermission(android.Manifest.permission.VERIFICATION_AGENT) public void reportVerificationIncomplete(int);
- method @RequiresPermission(android.Manifest.permission.VERIFICATION_AGENT) public boolean setVerificationPolicy(int);
+ method public void reportVerificationComplete(@NonNull android.content.pm.verify.pkg.VerificationStatus);
+ method public void reportVerificationComplete(@NonNull android.content.pm.verify.pkg.VerificationStatus, @NonNull android.os.PersistableBundle);
+ method public void reportVerificationIncomplete(int);
+ method public boolean setVerificationPolicy(int);
method public void writeToParcel(@NonNull android.os.Parcel, int);
field @NonNull public static final android.os.Parcelable.Creator<android.content.pm.verify.pkg.VerificationSession> CREATOR;
field public static final int VERIFICATION_INCOMPLETE_NETWORK_UNAVAILABLE = 1; // 0x1
@@ -7338,6 +7340,7 @@ package android.media {
method @RequiresPermission(android.Manifest.permission.MODIFY_AUDIO_ROUTING) public void setSupportedSystemUsages(@NonNull int[]);
method @RequiresPermission(anyOf={android.Manifest.permission.MODIFY_AUDIO_SETTINGS_PRIVILEGED, android.Manifest.permission.MODIFY_AUDIO_ROUTING}) public void setVolumeGroupVolumeIndex(int, int, int);
method @RequiresPermission(android.Manifest.permission.MODIFY_AUDIO_ROUTING) public void setVolumeIndexForAttributes(@NonNull android.media.AudioAttributes, int, int);
+ method @RequiresPermission(android.Manifest.permission.MODIFY_AUDIO_ROUTING) public void setWiredDeviceConnectionState(@NonNull android.media.AudioDeviceAttributes, int);
method @RequiresPermission(android.Manifest.permission.MODIFY_AUDIO_ROUTING) public boolean supportsBluetoothVariableLatency();
method @RequiresPermission(android.Manifest.permission.MODIFY_AUDIO_ROUTING) public void unregisterAudioPolicy(@NonNull android.media.audiopolicy.AudioPolicy);
method @RequiresPermission(android.Manifest.permission.MODIFY_AUDIO_ROUTING) public void unregisterAudioPolicyAsync(@NonNull android.media.audiopolicy.AudioPolicy);
@@ -7347,6 +7350,8 @@ package android.media {
field public static final int AUDIOFOCUS_FLAG_DELAY_OK = 1; // 0x1
field public static final int AUDIOFOCUS_FLAG_LOCK = 4; // 0x4
field public static final int AUDIOFOCUS_FLAG_PAUSES_ON_DUCKABLE_LOSS = 2; // 0x2
+ field @RequiresPermission(android.Manifest.permission.MODIFY_AUDIO_ROUTING) public static final int DEVICE_CONNECTION_STATE_CONNECTED = 1; // 0x1
+ field @RequiresPermission(android.Manifest.permission.MODIFY_AUDIO_ROUTING) public static final int DEVICE_CONNECTION_STATE_DISCONNECTED = 0; // 0x0
field public static final int DEVICE_VOLUME_BEHAVIOR_ABSOLUTE = 3; // 0x3
field public static final int DEVICE_VOLUME_BEHAVIOR_ABSOLUTE_ADJUST_ONLY = 5; // 0x5
field public static final int DEVICE_VOLUME_BEHAVIOR_ABSOLUTE_MULTI_MODE = 4; // 0x4
diff --git a/core/java/android/app/AppOpsManager.java b/core/java/android/app/AppOpsManager.java
index 2e3d22647a0f..56538d92baa0 100644
--- a/core/java/android/app/AppOpsManager.java
+++ b/core/java/android/app/AppOpsManager.java
@@ -8755,21 +8755,9 @@ public class AppOpsManager {
* Do a quick check for whether an application might be able to perform an operation.
* This is <em>not</em> a security check; you must use {@link #noteOp(String, int, String,
* String, String)} or {@link #startOp(String, int, String, String, String)} for your actual
- * security checks, which also ensure that the given uid and package name are consistent. This
- * function can just be used for a quick check to see if an operation has been disabled for the
- * application, as an early reject of some work. This does not modify the time stamp or other
- * data about the operation.
- *
- * <p>Important things this will not do (which you need to ultimate use
- * {@link #noteOp(String, int, String, String, String)} or
- * {@link #startOp(String, int, String, String, String)} to cover):</p>
- * <ul>
- * <li>Verifying the uid and package are consistent, so callers can't spoof
- * their identity.</li>
- * <li>Taking into account the current foreground/background state of the
- * app; apps whose mode varies by this state will always be reported
- * as {@link #MODE_ALLOWED}.</li>
- * </ul>
+ * security checks. This function can just be used for a quick check to see if an operation has
+ * been disabled for the application, as an early reject of some work. This does not modify the
+ * time stamp or other data about the operation.
*
* @param op The operation to check. One of the OPSTR_* constants.
* @param uid The user id of the application attempting to perform the operation.
diff --git a/core/java/android/app/Notification.java b/core/java/android/app/Notification.java
index 64aa705447aa..ca1662e6bfd0 100644
--- a/core/java/android/app/Notification.java
+++ b/core/java/android/app/Notification.java
@@ -11823,28 +11823,42 @@ public class Notification implements Parcelable
sanitizeProgressColor(indeterminateColor,
backgroundColor, defaultProgressColor));
} else {
-
// Ensure segment color contrasts.
final List<Segment> segments = new ArrayList<>();
+ int totalLength = 0;
for (Segment segment : mProgressSegments) {
- segments.add(sanitizeSegment(segment, backgroundColor,
- defaultProgressColor));
+ final int length = segment.getLength();
+ if (length <= 0) continue;
+
+ try {
+ totalLength += Math.addExact(totalLength, length);
+ segments.add(sanitizeSegment(segment, backgroundColor,
+ defaultProgressColor));
+ } catch (ArithmeticException e) {
+ totalLength = DEFAULT_PROGRESS_MAX;
+ segments.clear();
+ break;
+ }
}
// Create default segment when no segments are provided.
if (segments.isEmpty()) {
- segments.add(sanitizeSegment(new Segment(100), backgroundColor,
+ totalLength = DEFAULT_PROGRESS_MAX;
+ segments.add(sanitizeSegment(new Segment(totalLength), backgroundColor,
defaultProgressColor));
}
// Ensure point color contrasts.
final List<Point> points = new ArrayList<>();
for (Point point : mProgressPoints) {
+ final int position = point.getPosition();
+ if (position < 0 || position > totalLength) continue;
+
points.add(sanitizePoint(point, backgroundColor, defaultProgressColor));
}
model = new NotificationProgressModel(segments, points,
- mProgress, mIsStyledByProgress);
+ Math.clamp(mProgress, 0, totalLength), mIsStyledByProgress);
}
return model;
}
diff --git a/core/java/android/app/NotificationManager.java b/core/java/android/app/NotificationManager.java
index 06bf67c1e86f..f2a36e90c8ce 100644
--- a/core/java/android/app/NotificationManager.java
+++ b/core/java/android/app/NotificationManager.java
@@ -16,6 +16,8 @@
package android.app;
+import static android.service.notification.Flags.notificationClassification;
+
import android.annotation.CallbackExecutor;
import android.annotation.FlaggedApi;
import android.annotation.IntDef;
@@ -752,6 +754,11 @@ public class NotificationManager {
INotificationManager service = getService();
String pkg = mContext.getPackageName();
+ if (notificationClassification()
+ && NotificationChannel.SYSTEM_RESERVED_IDS.contains(notification.getChannelId())) {
+ return;
+ }
+
try {
if (localLOGV) Log.v(TAG, pkg + ": notify(" + id + ", " + notification + ")");
service.enqueueNotificationWithTag(pkg, mContext.getOpPackageName(), tag, id,
@@ -1131,6 +1138,10 @@ public class NotificationManager {
* had before it was deleted.
*/
public void deleteNotificationChannel(String channelId) {
+ if (notificationClassification()
+ && NotificationChannel.SYSTEM_RESERVED_IDS.contains(channelId)) {
+ return;
+ }
INotificationManager service = getService();
try {
service.deleteNotificationChannel(mContext.getPackageName(), channelId);
diff --git a/core/java/android/app/PropertyInvalidatedCache.java b/core/java/android/app/PropertyInvalidatedCache.java
index c93a6dd8969e..bc9e709420f1 100644
--- a/core/java/android/app/PropertyInvalidatedCache.java
+++ b/core/java/android/app/PropertyInvalidatedCache.java
@@ -30,16 +30,23 @@ import android.os.ParcelFileDescriptor;
import android.os.Process;
import android.os.SystemClock;
import android.os.SystemProperties;
+import android.util.ArrayMap;
import android.util.Log;
import com.android.internal.annotations.GuardedBy;
+import com.android.internal.annotations.VisibleForTesting;
+import com.android.internal.os.ApplicationSharedMemory;
import com.android.internal.os.BackgroundThread;
+import dalvik.annotation.optimization.CriticalNative;
+import dalvik.annotation.optimization.FastNative;
+
import java.io.ByteArrayOutputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.PrintWriter;
import java.util.ArrayList;
+import java.util.Arrays;
import java.util.HashSet;
import java.util.Iterator;
import java.util.LinkedHashMap;
@@ -203,19 +210,14 @@ public class PropertyInvalidatedCache<Query, Result> {
};
/**
- * Verify that the property name conforms to the standard. Log a warning if this is not true.
- * Note that this is done once in the cache constructor; it does not have to be very fast.
+ * Verify that the property name conforms to the standard and throw if this is not true. Note
+ * that this is done only once for a given property name; it does not have to be very fast.
*/
- private void validateCacheKey(String name) {
- if (Build.IS_USER) {
- // Do not bother checking keys in user builds. The keys will have been tested in
- // eng/userdebug builds already.
- return;
- }
+ private static void throwIfInvalidCacheKey(String name) {
for (int i = 0; i < sValidKeyPrefix.length; i++) {
if (name.startsWith(sValidKeyPrefix[i])) return;
}
- Log.w(TAG, "invalid cache name: " + name);
+ throw new IllegalArgumentException("invalid cache name: " + name);
}
/**
@@ -234,7 +236,8 @@ public class PropertyInvalidatedCache<Query, Result> {
* reserved values cause the cache to be skipped.
*/
// This is the initial value of all cache keys. It is changed when a cache is invalidated.
- private static final int NONCE_UNSET = 0;
+ @VisibleForTesting
+ static final int NONCE_UNSET = 0;
// This value is used in two ways. First, it is used internally to indicate that the cache is
// disabled for the current query. Secondly, it is used to globally disable the cache across
// the entire system. Once a cache is disabled, there is no way to enable it again. The
@@ -685,6 +688,77 @@ public class PropertyInvalidatedCache<Query, Result> {
}
/**
+ * Manage nonces that are stored in shared memory.
+ */
+ private static final class NonceSharedMem extends NonceHandler {
+ // The shared memory.
+ private volatile NonceStore mStore;
+
+ // The index of the nonce in shared memory.
+ private volatile int mHandle = NonceStore.INVALID_NONCE_INDEX;
+
+ // True if the string has been stored, ever.
+ private volatile boolean mRecorded = false;
+
+ // A short name that is saved in shared memory. This is the portion of the property name
+ // that follows the prefix.
+ private final String mShortName;
+
+ NonceSharedMem(@NonNull String name, @Nullable String prefix) {
+ super(name);
+ if ((prefix != null) && name.startsWith(prefix)) {
+ mShortName = name.substring(prefix.length());
+ } else {
+ mShortName = name;
+ }
+ }
+
+ // Fetch the nonce from shared memory. If the shared memory is not available, return
+ // UNSET. If the shared memory is available but the nonce name is not known (it may not
+ // have been invalidated by the server yet), return UNSET.
+ @Override
+ long getNonceInternal() {
+ if (mHandle == NonceStore.INVALID_NONCE_INDEX) {
+ if (mStore == null) {
+ mStore = NonceStore.getInstance();
+ if (mStore == null) {
+ return NONCE_UNSET;
+ }
+ }
+ mHandle = mStore.getHandleForName(mShortName);
+ if (mHandle == NonceStore.INVALID_NONCE_INDEX) {
+ return NONCE_UNSET;
+ }
+ }
+ return mStore.getNonce(mHandle);
+ }
+
+ // Set the nonce in shared mmory. If the shared memory is not available, throw an
+ // exception. Otherwise, if the nonce name has never been recorded, record it now and
+ // fetch the handle for the name. If the handle cannot be created, throw an exception.
+ @Override
+ void setNonceInternal(long value) {
+ if (mHandle == NonceStore.INVALID_NONCE_INDEX || !mRecorded) {
+ if (mStore == null) {
+ mStore = NonceStore.getInstance();
+ if (mStore == null) {
+ throw new IllegalStateException("setNonce: shared memory not ready");
+ }
+ }
+ // Always store the name before fetching the handle. storeName() is idempotent
+ // but does take a little time, so this code calls it just once.
+ mStore.storeName(mShortName);
+ mRecorded = true;
+ mHandle = mStore.getHandleForName(mShortName);
+ if (mHandle == NonceStore.INVALID_NONCE_INDEX) {
+ throw new IllegalStateException("setNonce: shared memory store failed");
+ }
+ }
+ mStore.setNonce(mHandle, value);
+ }
+ }
+
+ /**
* SystemProperties and shared storage are protected and cannot be written by random
* processes. So, for testing purposes, the NonceLocal handler stores the nonce locally. The
* NonceLocal uses the mTestNonce in the superclass, regardless of test mode.
@@ -712,6 +786,7 @@ public class PropertyInvalidatedCache<Query, Result> {
* Complete key prefixes.
*/
private static final String PREFIX_TEST = CACHE_KEY_PREFIX + "." + MODULE_TEST + ".";
+ private static final String PREFIX_SYSTEM = CACHE_KEY_PREFIX + "." + MODULE_SYSTEM + ".";
/**
* A static list of nonce handlers, indexed by name. NonceHandlers can be safely shared by
@@ -722,16 +797,32 @@ public class PropertyInvalidatedCache<Query, Result> {
private static final ConcurrentHashMap<String, NonceHandler> sHandlers
= new ConcurrentHashMap<>();
+ // True if shared memory is flag-enabled, false otherwise. Read the flags exactly once.
+ private static final boolean sSharedMemoryAvailable =
+ com.android.internal.os.Flags.applicationSharedMemoryEnabled()
+ && android.app.Flags.picUsesSharedMemory();
+
+ // Return true if this cache can use shared memory for its nonce. Shared memory may be used
+ // if the module is the system.
+ private static boolean sharedMemoryOkay(@NonNull String name) {
+ return sSharedMemoryAvailable && name.startsWith(PREFIX_SYSTEM);
+ }
+
/**
- * Return the proper nonce handler, based on the property name.
+ * Return the proper nonce handler, based on the property name. A handler is created if
+ * necessary. Before a handler is created, the name is checked, and an exception is thrown if
+ * the name is not valid.
*/
private static NonceHandler getNonceHandler(@NonNull String name) {
NonceHandler h = sHandlers.get(name);
if (h == null) {
synchronized (sGlobalLock) {
+ throwIfInvalidCacheKey(name);
h = sHandlers.get(name);
if (h == null) {
- if (name.startsWith(PREFIX_TEST)) {
+ if (sharedMemoryOkay(name)) {
+ h = new NonceSharedMem(name, PREFIX_SYSTEM);
+ } else if (name.startsWith(PREFIX_TEST)) {
h = new NonceLocal(name);
} else {
h = new NonceSysprop(name);
@@ -774,7 +865,6 @@ public class PropertyInvalidatedCache<Query, Result> {
public PropertyInvalidatedCache(int maxEntries, @NonNull String propertyName,
@NonNull String cacheName) {
mPropertyName = propertyName;
- validateCacheKey(mPropertyName);
mCacheName = cacheName;
mNonce = getNonceHandler(mPropertyName);
mMaxEntries = maxEntries;
@@ -799,7 +889,6 @@ public class PropertyInvalidatedCache<Query, Result> {
public PropertyInvalidatedCache(int maxEntries, @NonNull String module, @NonNull String api,
@NonNull String cacheName, @NonNull QueryHandler<Query, Result> computer) {
mPropertyName = createPropertyName(module, api);
- validateCacheKey(mPropertyName);
mCacheName = cacheName;
mNonce = getNonceHandler(mPropertyName);
mMaxEntries = maxEntries;
@@ -1620,6 +1709,14 @@ public class PropertyInvalidatedCache<Query, Result> {
// then only that cache is reported.
boolean detail = anyDetailed(args);
+ if (sSharedMemoryAvailable) {
+ pw.println(" SharedMemory: enabled");
+ NonceStore.getInstance().dump(pw, " ", detail);
+ } else {
+ pw.println(" SharedMemory: disabled");
+ }
+ pw.println();
+
ArrayList<PropertyInvalidatedCache> activeCaches = getActiveCaches();
for (int i = 0; i < activeCaches.size(); i++) {
PropertyInvalidatedCache currentCache = activeCaches.get(i);
@@ -1654,4 +1751,363 @@ public class PropertyInvalidatedCache<Query, Result> {
Log.e(TAG, "Failed to dump PropertyInvalidatedCache instances");
}
}
+
+ /**
+ * Nonces in shared memory are supported by a string block that acts as a table of contents
+ * for nonce names, and an array of nonce values. There are two key design principles with
+ * respect to nonce maps:
+ *
+ * 1. It is always okay if a nonce value cannot be determined. If the nonce is UNSET, the
+ * cache is bypassed, which is always functionally correct. Clients do not take extraordinary
+ * measures to be current with the nonce map. Clients must be current with the nonce itself;
+ * this is achieved through the shared memory.
+ *
+ * 2. Once a name is mapped to a nonce index, the mapping is fixed for the lifetime of the
+ * system. It is only necessary to distinguish between the unmapped and mapped states. Once
+ * a client has mapped a nonce, that mapping is known to be good for the lifetime of the
+ * system.
+ * @hide
+ */
+ @VisibleForTesting
+ public static class NonceStore {
+
+ // A lock for the store.
+ private final Object mLock = new Object();
+
+ // The native pointer. This is not owned by this class. It is owned by
+ // ApplicationSharedMemory, and it disappears when the owning instance is closed.
+ private final long mPtr;
+
+ // True if the memory is immutable.
+ private final boolean mMutable;
+
+ // The maximum length of a string in the string block. The maximum length must fit in a
+ // byte, but a smaller value has been chosen to limit memory use. Because strings are
+ // run-length encoded, a string consumes at most MAX_STRING_LENGTH+1 bytes in the string
+ // block.
+ private static final int MAX_STRING_LENGTH = 63;
+
+ // The raw byte block. Strings are stored as run-length encoded byte arrays. The first
+ // byte is the length of the following string. It is an axiom of the system that the
+ // string block is initially all zeros and that it is write-once memory: new strings are
+ // appended to existing strings, so there is never a need to revisit strings that have
+ // already been pulled from the string block.
+ @GuardedBy("mLock")
+ private final byte[] mStringBlock;
+
+ // The expected hash code of the string block. If the hash over the string block equals
+ // this value, then the string block is valid. Otherwise, the block is not valid and
+ // should be re-read. An invalid block generally means that a client has read the shared
+ // memory while the server was still writing it.
+ @GuardedBy("mLock")
+ private int mBlockHash = 0;
+
+ // The number of nonces that the native layer can hold. This is maintained for debug and
+ // logging.
+ private final int mMaxNonce;
+
+ /** @hide */
+ @VisibleForTesting
+ public NonceStore(long ptr, boolean mutable) {
+ mPtr = ptr;
+ mMutable = mutable;
+ mStringBlock = new byte[nativeGetMaxByte(ptr)];
+ mMaxNonce = nativeGetMaxNonce(ptr);
+ refreshStringBlockLocked();
+ }
+
+ // The static lock for singleton acquisition.
+ private static Object sLock = new Object();
+
+ // NonceStore is supposed to be a singleton.
+ private static NonceStore sInstance;
+
+ // Return the singleton instance.
+ static NonceStore getInstance() {
+ synchronized (sLock) {
+ if (sInstance == null) {
+ try {
+ ApplicationSharedMemory shmem = ApplicationSharedMemory.getInstance();
+ sInstance = (shmem == null)
+ ? null
+ : new NonceStore(shmem.getSystemNonceBlock(),
+ shmem.isMutable());
+ } catch (IllegalStateException e) {
+ // ApplicationSharedMemory.getInstance() throws if the shared memory is
+ // not yet mapped. Swallow the exception and leave sInstance null.
+ }
+ }
+ return sInstance;
+ }
+ }
+
+ // The index value of an unmapped name.
+ public static final int INVALID_NONCE_INDEX = -1;
+
+ // The highest string index extracted from the string block. -1 means no strings have
+ // been seen. This is used to skip strings that have already been processed, when the
+ // string block is updated.
+ @GuardedBy("mLock")
+ private int mHighestIndex = -1;
+
+ // The number bytes of the string block that has been used. This is a statistics.
+ @GuardedBy("mLock")
+ private int mStringBytes = 0;
+
+ // The number of partial reads on the string block. This is a statistic.
+ @GuardedBy("mLock")
+ private int mPartialReads = 0;
+
+ // The number of times the string block was updated. This is a statistic.
+ @GuardedBy("mLock")
+ private int mStringUpdated = 0;
+
+ // Map a string to a native index.
+ @GuardedBy("mLock")
+ private final ArrayMap<String, Integer> mStringHandle = new ArrayMap<>();
+
+ // Update the string map from the current string block. The string block is not modified
+ // and the block hash is not checked. The function skips past strings that have already
+ // been read, and then processes any new strings.
+ @GuardedBy("mLock")
+ private void updateStringMapLocked() {
+ int index = 0;
+ int offset = 0;
+ while (offset < mStringBlock.length && mStringBlock[offset] != 0) {
+ if (index > mHighestIndex) {
+ // Only record the string if it has not been seen yet.
+ final String s = new String(mStringBlock, offset+1, mStringBlock[offset]);
+ mStringHandle.put(s, index);
+ mHighestIndex = index;
+ }
+ offset += mStringBlock[offset] + 1;
+ index++;
+ }
+ mStringBytes = offset;
+ }
+
+ // Append a string to the string block and update the hash. This does not write the block
+ // to shared memory.
+ @GuardedBy("mLock")
+ private void appendStringToMapLocked(@NonNull String str) {
+ int offset = 0;
+ while (offset < mStringBlock.length && mStringBlock[offset] != 0) {
+ offset += mStringBlock[offset] + 1;
+ }
+ final byte[] strBytes = str.getBytes();
+
+ if (offset + strBytes.length >= mStringBlock.length) {
+ // Overflow. Do not add the string to the block; the string will remain undefined.
+ return;
+ }
+
+ mStringBlock[offset] = (byte) strBytes.length;
+ offset++;
+ for (int i = 0; i < strBytes.length; i++, offset++) {
+ mStringBlock[offset] = strBytes[i];
+ }
+ mBlockHash = Arrays.hashCode(mStringBlock);
+ }
+
+ // Possibly update the string block. If the native shared memory has a new block hash,
+ // then read the new string block values from shared memory, as well as the new hash.
+ @GuardedBy("mLock")
+ private void refreshStringBlockLocked() {
+ if (mBlockHash == nativeGetByteBlockHash(mPtr)) {
+ // The fastest way to know that the shared memory string block has not changed.
+ return;
+ }
+ final int hash = nativeGetByteBlock(mPtr, mBlockHash, mStringBlock);
+ if (hash != Arrays.hashCode(mStringBlock)) {
+ // This is a partial read: ignore it. The next time someone needs this string
+ // the memory will be read again and should succeed. Set the local hash to
+ // zero to ensure that the next read attempt will actually read from shared
+ // memory.
+ mBlockHash = 0;
+ mPartialReads++;
+ return;
+ }
+ // The hash has changed. Update the strings from the byte block.
+ mStringUpdated++;
+ mBlockHash = hash;
+ updateStringMapLocked();
+ }
+
+ // Throw an exception if the string cannot be stored in the string block.
+ private static void throwIfBadString(@NonNull String s) {
+ if (s.length() == 0) {
+ throw new IllegalArgumentException("cannot store an empty string");
+ }
+ if (s.length() > MAX_STRING_LENGTH) {
+ throw new IllegalArgumentException("cannot store a string longer than "
+ + MAX_STRING_LENGTH);
+ }
+ }
+
+ // Throw an exception if the nonce handle is invalid. The handle is bad if it is out of
+ // range of allocated handles. Note that NONCE_HANDLE_INVALID will throw: this is
+ // important for setNonce().
+ @GuardedBy("mLock")
+ private void throwIfBadHandle(int handle) {
+ if (handle < 0 || handle > mHighestIndex) {
+ throw new IllegalArgumentException("invalid nonce handle: " + handle);
+ }
+ }
+
+ // Throw if the memory is immutable (the process does not have write permission). The
+ // exception mimics the permission-denied exception thrown when a process writes to an
+ // unauthorized system property.
+ private void throwIfImmutable() {
+ if (!mMutable) {
+ throw new RuntimeException("write permission denied");
+ }
+ }
+
+ // Add a string to the local copy of the block and write the block to shared memory.
+ // Return the index of the new string. If the string has already been recorded, the
+ // shared memory is not updated but the index of the existing string is returned.
+ public int storeName(@NonNull String str) {
+ synchronized (mLock) {
+ Integer handle = mStringHandle.get(str);
+ if (handle == null) {
+ throwIfImmutable();
+ throwIfBadString(str);
+ appendStringToMapLocked(str);
+ nativeSetByteBlock(mPtr, mBlockHash, mStringBlock);
+ updateStringMapLocked();
+ handle = mStringHandle.get(str);
+ }
+ return handle;
+ }
+ }
+
+ // Retrieve the handle for a string. -1 is returned if the string is not found.
+ public int getHandleForName(@NonNull String str) {
+ synchronized (mLock) {
+ Integer handle = mStringHandle.get(str);
+ if (handle == null) {
+ refreshStringBlockLocked();
+ handle = mStringHandle.get(str);
+ }
+ return (handle != null) ? handle : INVALID_NONCE_INDEX;
+ }
+ }
+
+ // Thin wrapper around the native method.
+ public boolean setNonce(int handle, long value) {
+ synchronized (mLock) {
+ throwIfBadHandle(handle);
+ throwIfImmutable();
+ return nativeSetNonce(mPtr, handle, value);
+ }
+ }
+
+ public long getNonce(int handle) {
+ synchronized (mLock) {
+ throwIfBadHandle(handle);
+ return nativeGetNonce(mPtr, handle);
+ }
+ }
+
+ /**
+ * Dump the nonce statistics
+ */
+ public void dump(@NonNull PrintWriter pw, @NonNull String prefix, boolean detailed) {
+ synchronized (mLock) {
+ pw.println(formatSimple(
+ "%sStringsMapped: %d, BytesUsed: %d",
+ prefix, mHighestIndex, mStringBytes));
+ pw.println(formatSimple(
+ "%sPartialReads: %d, StringUpdates: %d",
+ prefix, mPartialReads, mStringUpdated));
+
+ if (detailed) {
+ for (String s: mStringHandle.keySet()) {
+ int h = mStringHandle.get(s);
+ pw.println(formatSimple(
+ "%sHandle:%d Name:%s", prefix, h, s));
+ }
+ }
+ }
+ }
+ }
+
+ /**
+ * Return the maximum number of nonces supported in the native layer.
+ *
+ * @param mPtr the pointer to the native shared memory.
+ * @return the number of nonces supported by the shared memory.
+ */
+ private static native int nativeGetMaxNonce(long mPtr);
+
+ /**
+ * Return the maximum number of string bytes supported in the native layer.
+ *
+ * @param mPtr the pointer to the native shared memory.
+ * @return the number of string bytes supported by the shared memory.
+ */
+ private static native int nativeGetMaxByte(long mPtr);
+
+ /**
+ * Write the byte block and set the hash into shared memory. The method is relatively
+ * forgiving, in that any non-null byte array will be stored without error. The number of
+ * bytes will the lesser of the length of the block parameter and the size of the native
+ * array. The native layer performs no checks on either byte block or the hash.
+ *
+ * @param mPtr the pointer to the native shared memory.
+ * @param hash a value to be stored in the native block hash.
+ * @param block the byte array to be store.
+ */
+ @FastNative
+ private static native void nativeSetByteBlock(long mPtr, int hash, @NonNull byte[] block);
+
+ /**
+ * Retrieve the string block into the array and return the hash value. If the incoming hash
+ * value is the same as the hash in shared memory, the native function returns immediately
+ * without touching the block parameter. Note that a zero hash value will always cause shared
+ * memory to be read. The number of bytes read is the lesser of the length of the block
+ * parameter and the size of the native array.
+ *
+ * @param mPtr the pointer to the native shared memory.
+ * @param hash a value to be compared against the hash in the native layer.
+ * @param block an array to receive the bytes from the native layer.
+ * @return the hash from the native layer.
+ */
+ @FastNative
+ private static native int nativeGetByteBlock(long mPtr, int hash, @NonNull byte[] block);
+
+ /**
+ * Retrieve just the byte block hash from the native layer. The function is CriticalNative
+ * and thus very fast.
+ *
+ * @param mPtr the pointer to the native shared memory.
+ * @return the current native hash value.
+ */
+ @CriticalNative
+ private static native int nativeGetByteBlockHash(long mPtr);
+
+ /**
+ * Set a nonce at the specified index. The index is checked against the size of the native
+ * nonce array and the function returns true if the index is valid, and false. The function
+ * is CriticalNative and thus very fast.
+ *
+ * @param mPtr the pointer to the native shared memory.
+ * @param index the index of the nonce to set.
+ * @param value the value to set for the nonce.
+ * @return true if the index is inside the nonce array and false otherwise.
+ */
+ @CriticalNative
+ private static native boolean nativeSetNonce(long mPtr, int index, long value);
+
+ /**
+ * Get the nonce from the specified index. The index is checked against the size of the
+ * native nonce array; the function returns the nonce value if the index is valid, and 0
+ * otherwise. The function is CriticalNative and thus very fast.
+ *
+ * @param mPtr the pointer to the native shared memory.
+ * @param index the index of the nonce to retrieve.
+ * @return the value of the specified nonce, of 0 if the index is out of bounds.
+ */
+ @CriticalNative
+ private static native long nativeGetNonce(long mPtr, int index);
}
diff --git a/core/java/android/app/SystemServiceRegistry.java b/core/java/android/app/SystemServiceRegistry.java
index bd26db55052b..c6b8f3baecf6 100644
--- a/core/java/android/app/SystemServiceRegistry.java
+++ b/core/java/android/app/SystemServiceRegistry.java
@@ -1750,10 +1750,13 @@ public final class SystemServiceRegistry {
@Override
public AdvancedProtectionManager createService(ContextImpl ctx)
throws ServiceNotFoundException {
- IBinder iBinder = ServiceManager.getServiceOrThrow(
+ IBinder iBinder = ServiceManager.getService(
Context.ADVANCED_PROTECTION_SERVICE);
IAdvancedProtectionService service =
IAdvancedProtectionService.Stub.asInterface(iBinder);
+ if (service == null) {
+ return null;
+ }
return new AdvancedProtectionManager(service);
}
});
diff --git a/core/java/android/app/appfunctions/AppFunctionManager.java b/core/java/android/app/appfunctions/AppFunctionManager.java
index a82c6ba7479c..5b478d09ecdc 100644
--- a/core/java/android/app/appfunctions/AppFunctionManager.java
+++ b/core/java/android/app/appfunctions/AppFunctionManager.java
@@ -42,10 +42,37 @@ import java.util.concurrent.Executor;
import java.util.function.Consumer;
/**
- * Provides app functions related functionalities.
+ * Provides access to app functions.
*
- * <p>App function is a specific piece of functionality that an app offers to the system. These
- * functionalities can be integrated into various system features.
+ * <p>An app function is a piece of functionality that apps expose to the system for cross-app
+ * orchestration.
+ *
+ * <p>**Developer Workflow:**
+ *
+ * <p>Most developers should interact with app functions through the AppFunctions SDK. This SDK
+ * library offers a more convenient and type-safe way to represent the inputs and outputs of an app
+ * function, using custom data classes called "AppFunction Schemas".
+ *
+ * <p>The suggested way to build an app function is to use the AppFunctions SDK. The SDK provides
+ * custom data classes (AppFunctions Schemas) and handles the conversion to the underlying {@link
+ * android.app.appsearch.GenericDocument}/{@link android.os.Bundle} format used in {@link
+ * ExecuteAppFunctionRequest} and {@link ExecuteAppFunctionResponse}.
+ *
+ * <p>**Discovering (Listing) App Functions:**
+ *
+ * <p>When there is a package change or the device starts up, the metadata of available functions is
+ * indexed on-device by {@link AppSearchManager}. AppSearch stores the indexed information as a
+ * {@code AppFunctionStaticMetadata} document. This allows other apps and the app itself to discover
+ * these functions using the AppSearch search APIs. Visibility to this metadata document is based on
+ * the packages that have visibility to the app providing the app functions.
+ *
+ * <p>**Executing App Functions:**
+ *
+ * <p>Requests to execute a function are built using the {@link ExecuteAppFunctionRequest} class.
+ * Callers need the {@code android.permission.EXECUTE_APP_FUNCTIONS} or {@code
+ * android.permission.EXECUTE_APP_FUNCTIONS_TRUSTED} permission to execute app functions from other
+ * apps. An app has automatic visibility to its own functions and doesn't need these permissions to
+ * call its own functions via {@code AppFunctionManager}.
*/
@FlaggedApi(FLAG_ENABLE_APP_FUNCTION_MANAGER)
@SystemService(Context.APP_FUNCTION_SERVICE)
diff --git a/core/java/android/app/performance.aconfig b/core/java/android/app/performance.aconfig
new file mode 100644
index 000000000000..7c6989e4f3e9
--- /dev/null
+++ b/core/java/android/app/performance.aconfig
@@ -0,0 +1,11 @@
+package: "android.app"
+container: "system"
+
+flag {
+ namespace: "system_performance"
+ name: "pic_uses_shared_memory"
+ is_exported: true
+ is_fixed_read_only: true
+ description: "PropertyInvalidatedCache uses shared memory for nonces."
+ bug: "366552454"
+}
diff --git a/core/java/android/content/Intent.java b/core/java/android/content/Intent.java
index e8cec70ef6a8..66ef004c5298 100644
--- a/core/java/android/content/Intent.java
+++ b/core/java/android/content/Intent.java
@@ -11672,6 +11672,7 @@ public class Intent implements Parcelable, Cloneable {
Log.w(TAG, "Failure filling in extras", e);
}
}
+ mCreatorTokenInfo = other.mCreatorTokenInfo;
if (mayHaveCopiedUris && mContentUserHint == UserHandle.USER_CURRENT
&& other.mContentUserHint != UserHandle.USER_CURRENT) {
mContentUserHint = other.mContentUserHint;
@@ -12225,6 +12226,13 @@ public class Intent implements Parcelable, Cloneable {
}
/** @hide */
+ public void removeCreatorToken() {
+ if (mCreatorTokenInfo != null) {
+ mCreatorTokenInfo.mCreatorToken = null;
+ }
+ }
+
+ /** @hide */
public @Nullable IBinder getCreatorToken() {
return mCreatorTokenInfo == null ? null : mCreatorTokenInfo.mCreatorToken;
}
@@ -12251,7 +12259,7 @@ public class Intent implements Parcelable, Cloneable {
public void collectExtraIntentKeys() {
if (!isPreventIntentRedirectEnabled()) return;
- if (mExtras != null && !mExtras.isParcelled() && !mExtras.isEmpty()) {
+ if (mExtras != null && !mExtras.isEmpty()) {
for (String key : mExtras.keySet()) {
if (mExtras.get(key) instanceof Intent) {
if (mCreatorTokenInfo == null) {
@@ -12833,6 +12841,8 @@ public class Intent implements Parcelable, Cloneable {
private boolean isImageCaptureIntent() {
return (MediaStore.ACTION_IMAGE_CAPTURE.equals(mAction)
|| MediaStore.ACTION_IMAGE_CAPTURE_SECURE.equals(mAction)
+ || MediaStore.ACTION_MOTION_PHOTO_CAPTURE.equals(mAction)
+ || MediaStore.ACTION_MOTION_PHOTO_CAPTURE_SECURE.equals(mAction)
|| MediaStore.ACTION_VIDEO_CAPTURE.equals(mAction));
}
diff --git a/core/java/android/content/pm/IPackageInstaller.aidl b/core/java/android/content/pm/IPackageInstaller.aidl
index c911326ccffd..ecea47944c72 100644
--- a/core/java/android/content/pm/IPackageInstaller.aidl
+++ b/core/java/android/content/pm/IPackageInstaller.aidl
@@ -94,9 +94,9 @@ interface IPackageInstaller {
@JavaPassthrough(annotation="@android.annotation.RequiresPermission(anyOf={android.Manifest.permission.INSTALL_PACKAGES,android.Manifest.permission.REQUEST_INSTALL_PACKAGES})")
void reportUnarchivalStatus(int unarchiveId, int status, long requiredStorageBytes, in PendingIntent userActionIntent, in UserHandle userHandle);
- @JavaPassthrough(annotation="@android.annotation.RequiresPermission(android.Manifest.permission.VERIFICATION_AGENT)")
+ @EnforcePermission("VERIFICATION_AGENT")
int getVerificationPolicy();
- @JavaPassthrough(annotation="@android.annotation.RequiresPermission(android.Manifest.permission.VERIFICATION_AGENT)")
+ @EnforcePermission("VERIFICATION_AGENT")
boolean setVerificationPolicy(int policy);
}
diff --git a/core/java/android/content/pm/flags.aconfig b/core/java/android/content/pm/flags.aconfig
index 5f439b1dcab9..6f70586881be 100644
--- a/core/java/android/content/pm/flags.aconfig
+++ b/core/java/android/content/pm/flags.aconfig
@@ -297,14 +297,6 @@ flag {
}
flag {
- name: "get_packages_from_launcher_apps"
- namespace: "package_manager_service"
- description: "Feature flag to provide the new methods within launcher apps class to get packages."
- bug: "363324203"
- is_fixed_read_only: true
-}
-
-flag {
name: "remove_cross_user_permission_hack"
namespace: "package_manager_service"
description: "Feature flag to remove hack code of using PackageManager.MATCH_ANY_USER flag without cross user permission."
@@ -328,6 +320,13 @@ flag {
}
flag {
+ name: "sdk_dependency_installer"
+ namespace: "package_manager_service"
+ description: "Feature flag to enable installation of missing sdk dependency of app"
+ bug: "370822870"
+}
+
+flag {
name: "include_feature_flags_in_package_cacher"
namespace: "package_manager_service"
description: "Include feature flag status when determining hits or misses in PackageCacher."
diff --git a/core/java/android/content/pm/parsing/ApkLite.java b/core/java/android/content/pm/parsing/ApkLite.java
index 19a13db15b05..4220590a6943 100644
--- a/core/java/android/content/pm/parsing/ApkLite.java
+++ b/core/java/android/content/pm/parsing/ApkLite.java
@@ -28,6 +28,7 @@ import android.content.pm.VerifierInfo;
import com.android.internal.util.CollectionUtils;
import com.android.internal.util.DataClass;
+import java.util.Collections;
import java.util.List;
import java.util.Set;
@@ -141,6 +142,21 @@ public class ApkLite {
private final boolean mIsSdkLibrary;
/**
+ * List of SDK names used by this apk.
+ */
+ private final @NonNull List<String> mUsesSdkLibraries;
+
+ /**
+ * List of SDK major versions used by this apk.
+ */
+ private final @Nullable long[] mUsesSdkLibrariesVersionsMajor;
+
+ /**
+ * List of SDK certificates used by this apk.
+ */
+ private final @Nullable String[][] mUsesSdkLibrariesCertDigests;
+
+ /**
* Indicates if this system app can be updated.
*/
private final boolean mUpdatableSystem;
@@ -167,7 +183,9 @@ public class ApkLite {
String requiredSystemPropertyName, String requiredSystemPropertyValue,
int minSdkVersion, int targetSdkVersion, int rollbackDataPolicy,
Set<String> requiredSplitTypes, Set<String> splitTypes,
- boolean hasDeviceAdminReceiver, boolean isSdkLibrary, boolean updatableSystem,
+ boolean hasDeviceAdminReceiver, boolean isSdkLibrary,
+ List<String> usesSdkLibraries, long[] usesSdkLibrariesVersionsMajor,
+ String[][] usesSdkLibrariesCertDigests, boolean updatableSystem,
String emergencyInstaller, List<SharedLibraryInfo> declaredLibraries) {
mPath = path;
mPackageName = packageName;
@@ -202,6 +220,9 @@ public class ApkLite {
mRollbackDataPolicy = rollbackDataPolicy;
mHasDeviceAdminReceiver = hasDeviceAdminReceiver;
mIsSdkLibrary = isSdkLibrary;
+ mUsesSdkLibraries = usesSdkLibraries;
+ mUsesSdkLibrariesVersionsMajor = usesSdkLibrariesVersionsMajor;
+ mUsesSdkLibrariesCertDigests = usesSdkLibrariesCertDigests;
mUpdatableSystem = updatableSystem;
mEmergencyInstaller = emergencyInstaller;
mArchivedPackage = null;
@@ -242,6 +263,9 @@ public class ApkLite {
mRollbackDataPolicy = 0;
mHasDeviceAdminReceiver = false;
mIsSdkLibrary = false;
+ mUsesSdkLibraries = Collections.emptyList();
+ mUsesSdkLibrariesVersionsMajor = null;
+ mUsesSdkLibrariesCertDigests = null;
mUpdatableSystem = true;
mEmergencyInstaller = null;
mArchivedPackage = archivedPackage;
@@ -555,6 +579,30 @@ public class ApkLite {
}
/**
+ * List of SDK names used by this apk.
+ */
+ @DataClass.Generated.Member
+ public @NonNull List<String> getUsesSdkLibraries() {
+ return mUsesSdkLibraries;
+ }
+
+ /**
+ * List of SDK major versions used by this apk.
+ */
+ @DataClass.Generated.Member
+ public @Nullable long[] getUsesSdkLibrariesVersionsMajor() {
+ return mUsesSdkLibrariesVersionsMajor;
+ }
+
+ /**
+ * List of SDK certificates used by this apk.
+ */
+ @DataClass.Generated.Member
+ public @Nullable String[][] getUsesSdkLibrariesCertDigests() {
+ return mUsesSdkLibrariesCertDigests;
+ }
+
+ /**
* Indicates if this system app can be updated.
*/
@DataClass.Generated.Member
@@ -584,10 +632,10 @@ public class ApkLite {
}
@DataClass.Generated(
- time = 1728333566322L,
+ time = 1729247366948L,
codegenVersion = "1.0.23",
sourceFile = "frameworks/base/core/java/android/content/pm/parsing/ApkLite.java",
- inputSignatures = "private final @android.annotation.NonNull java.lang.String mPackageName\nprivate final @android.annotation.NonNull java.lang.String mPath\nprivate final @android.annotation.Nullable java.lang.String mSplitName\nprivate final @android.annotation.Nullable java.lang.String mUsesSplitName\nprivate final @android.annotation.Nullable java.lang.String mConfigForSplit\nprivate final @android.annotation.Nullable java.util.Set<java.lang.String> mRequiredSplitTypes\nprivate final @android.annotation.Nullable java.util.Set<java.lang.String> mSplitTypes\nprivate final int mVersionCodeMajor\nprivate final int mVersionCode\nprivate final int mRevisionCode\nprivate final int mInstallLocation\nprivate final int mMinSdkVersion\nprivate final int mTargetSdkVersion\nprivate final @android.annotation.NonNull android.content.pm.VerifierInfo[] mVerifiers\nprivate final @android.annotation.NonNull android.content.pm.SigningDetails mSigningDetails\nprivate final boolean mFeatureSplit\nprivate final boolean mIsolatedSplits\nprivate final boolean mSplitRequired\nprivate final boolean mCoreApp\nprivate final boolean mDebuggable\nprivate final boolean mProfileableByShell\nprivate final boolean mMultiArch\nprivate final boolean mUse32bitAbi\nprivate final boolean mExtractNativeLibs\nprivate final boolean mUseEmbeddedDex\nprivate final @android.annotation.Nullable java.lang.String mTargetPackageName\nprivate final boolean mOverlayIsStatic\nprivate final int mOverlayPriority\nprivate final @android.annotation.Nullable java.lang.String mRequiredSystemPropertyName\nprivate final @android.annotation.Nullable java.lang.String mRequiredSystemPropertyValue\nprivate final int mRollbackDataPolicy\nprivate final boolean mHasDeviceAdminReceiver\nprivate final boolean mIsSdkLibrary\nprivate final boolean mUpdatableSystem\nprivate final @android.annotation.Nullable java.lang.String mEmergencyInstaller\nprivate final @android.annotation.NonNull java.util.List<android.content.pm.SharedLibraryInfo> mDeclaredLibraries\nprivate final @android.annotation.Nullable android.content.pm.ArchivedPackageParcel mArchivedPackage\npublic long getLongVersionCode()\nprivate boolean hasAnyRequiredSplitTypes()\nclass ApkLite extends java.lang.Object implements []\n@com.android.internal.util.DataClass(genConstructor=false, genConstDefs=false)")
+ inputSignatures = "private final @android.annotation.NonNull java.lang.String mPackageName\nprivate final @android.annotation.NonNull java.lang.String mPath\nprivate final @android.annotation.Nullable java.lang.String mSplitName\nprivate final @android.annotation.Nullable java.lang.String mUsesSplitName\nprivate final @android.annotation.Nullable java.lang.String mConfigForSplit\nprivate final @android.annotation.Nullable java.util.Set<java.lang.String> mRequiredSplitTypes\nprivate final @android.annotation.Nullable java.util.Set<java.lang.String> mSplitTypes\nprivate final int mVersionCodeMajor\nprivate final int mVersionCode\nprivate final int mRevisionCode\nprivate final int mInstallLocation\nprivate final int mMinSdkVersion\nprivate final int mTargetSdkVersion\nprivate final @android.annotation.NonNull android.content.pm.VerifierInfo[] mVerifiers\nprivate final @android.annotation.NonNull android.content.pm.SigningDetails mSigningDetails\nprivate final boolean mFeatureSplit\nprivate final boolean mIsolatedSplits\nprivate final boolean mSplitRequired\nprivate final boolean mCoreApp\nprivate final boolean mDebuggable\nprivate final boolean mProfileableByShell\nprivate final boolean mMultiArch\nprivate final boolean mUse32bitAbi\nprivate final boolean mExtractNativeLibs\nprivate final boolean mUseEmbeddedDex\nprivate final @android.annotation.Nullable java.lang.String mTargetPackageName\nprivate final boolean mOverlayIsStatic\nprivate final int mOverlayPriority\nprivate final @android.annotation.Nullable java.lang.String mRequiredSystemPropertyName\nprivate final @android.annotation.Nullable java.lang.String mRequiredSystemPropertyValue\nprivate final int mRollbackDataPolicy\nprivate final boolean mHasDeviceAdminReceiver\nprivate final boolean mIsSdkLibrary\nprivate final @android.annotation.NonNull java.util.List<java.lang.String> mUsesSdkLibraries\nprivate final @android.annotation.Nullable long[] mUsesSdkLibrariesVersionsMajor\nprivate final @android.annotation.Nullable java.lang.String[][] mUsesSdkLibrariesCertDigests\nprivate final boolean mUpdatableSystem\nprivate final @android.annotation.Nullable java.lang.String mEmergencyInstaller\nprivate final @android.annotation.NonNull java.util.List<android.content.pm.SharedLibraryInfo> mDeclaredLibraries\nprivate final @android.annotation.Nullable android.content.pm.ArchivedPackageParcel mArchivedPackage\npublic long getLongVersionCode()\nprivate boolean hasAnyRequiredSplitTypes()\nclass ApkLite extends java.lang.Object implements []\n@com.android.internal.util.DataClass(genConstructor=false, genConstDefs=false)")
@Deprecated
private void __metadata() {}
diff --git a/core/java/android/content/pm/parsing/ApkLiteParseUtils.java b/core/java/android/content/pm/parsing/ApkLiteParseUtils.java
index 1a7f628ae61c..50d875845b2f 100644
--- a/core/java/android/content/pm/parsing/ApkLiteParseUtils.java
+++ b/core/java/android/content/pm/parsing/ApkLiteParseUtils.java
@@ -32,6 +32,7 @@ import android.content.pm.parsing.result.ParseResult;
import android.content.res.ApkAssets;
import android.content.res.XmlResourceParser;
import android.os.Build;
+import android.os.SystemProperties;
import android.os.Trace;
import android.text.TextUtils;
import android.util.ArrayMap;
@@ -44,6 +45,7 @@ import com.android.internal.pm.pkg.component.flags.Flags;
import com.android.internal.util.ArrayUtils;
import libcore.io.IoUtils;
+import libcore.util.HexEncoding;
import org.xmlpull.v1.XmlPullParser;
import org.xmlpull.v1.XmlPullParserException;
@@ -88,6 +90,7 @@ public class ApkLiteParseUtils {
private static final String TAG_USES_SDK = "uses-sdk";
private static final String TAG_USES_SPLIT = "uses-split";
private static final String TAG_MANIFEST = "manifest";
+ private static final String TAG_USES_SDK_LIBRARY = "uses-sdk-library";
private static final String TAG_SDK_LIBRARY = "sdk-library";
private static final int SDK_VERSION = Build.VERSION.SDK_INT;
private static final String[] SDK_CODENAMES = Build.VERSION.ACTIVE_CODENAMES;
@@ -460,6 +463,9 @@ public class ApkLiteParseUtils {
boolean hasDeviceAdminReceiver = false;
boolean isSdkLibrary = false;
+ List<String> usesSdkLibraries = new ArrayList<>();
+ long[] usesSdkLibrariesVersionsMajor = new long[0];
+ String[][] usesSdkLibrariesCertDigests = new String[0][0];
List<SharedLibraryInfo> declaredLibraries = new ArrayList<>();
// Only search the tree when the tag is the direct child of <manifest> tag
@@ -523,6 +529,57 @@ public class ApkLiteParseUtils {
hasDeviceAdminReceiver |= isDeviceAdminReceiver(parser,
hasBindDeviceAdminPermission);
break;
+ case TAG_USES_SDK_LIBRARY:
+ String usesSdkLibName = parser.getAttributeValue(
+ ANDROID_RES_NAMESPACE, "name");
+ long usesSdkLibVersionMajor = parser.getAttributeIntValue(
+ ANDROID_RES_NAMESPACE, "versionMajor", -1);
+ String usesSdkCertDigest = parser.getAttributeValue(
+ ANDROID_RES_NAMESPACE, "certDigest");
+
+ if (usesSdkLibName == null || usesSdkLibName.isBlank()
+ || usesSdkLibVersionMajor < 0) {
+ return input.error(
+ PackageManager.INSTALL_PARSE_FAILED_MANIFEST_MALFORMED,
+ "Bad uses-sdk-library declaration name: "
+ + usesSdkLibName
+ + " version: " + usesSdkLibVersionMajor);
+ }
+
+ if (usesSdkLibraries.contains(usesSdkLibName)) {
+ return input.error(
+ PackageManager.INSTALL_PARSE_FAILED_MANIFEST_MALFORMED,
+ "Bad uses-sdk-library declaration. Depending on"
+ + " multiple versions of SDK library: "
+ + usesSdkLibName);
+ }
+
+ usesSdkLibraries.add(usesSdkLibName);
+ usesSdkLibrariesVersionsMajor = ArrayUtils.appendLong(
+ usesSdkLibrariesVersionsMajor, usesSdkLibVersionMajor,
+ /*allowDuplicates=*/ true);
+
+ // We allow ":" delimiters in the SHA declaration as this is the format
+ // emitted by the certtool making it easy for developers to copy/paste.
+ // TODO(372862145): Add test for this replacement
+ usesSdkCertDigest = usesSdkCertDigest.replace(":", "").toLowerCase();
+
+ if ("".equals(usesSdkCertDigest)) {
+ // Test-only uses-sdk-library empty certificate digest override.
+ usesSdkCertDigest = SystemProperties.get(
+ "debug.pm.uses_sdk_library_default_cert_digest", "");
+ // Validate the overridden digest.
+ try {
+ HexEncoding.decode(usesSdkCertDigest, false);
+ } catch (IllegalArgumentException e) {
+ usesSdkCertDigest = "";
+ }
+ }
+ // TODO(372862145): Add support for multiple signer
+ usesSdkLibrariesCertDigests = ArrayUtils.appendElement(String[].class,
+ usesSdkLibrariesCertDigests, new String[]{usesSdkCertDigest},
+ /*allowDuplicates=*/ true);
+ break;
case TAG_SDK_LIBRARY:
isSdkLibrary = true;
// Mirrors ParsingPackageUtils#parseSdkLibrary until lite and full
@@ -534,7 +591,7 @@ public class ApkLiteParseUtils {
if (sdkLibName == null || sdkLibVersionMajor < 0) {
return input.error(
PackageManager.INSTALL_PARSE_FAILED_MANIFEST_MALFORMED,
- "Bad uses-sdk-library declaration name: " + sdkLibName
+ "Bad sdk-library declaration name: " + sdkLibName
+ " version: " + sdkLibVersionMajor);
}
declaredLibraries.add(new SharedLibraryInfo(
@@ -694,8 +751,9 @@ public class ApkLiteParseUtils {
overlayIsStatic, overlayPriority, requiredSystemPropertyName,
requiredSystemPropertyValue, minSdkVersion, targetSdkVersion,
rollbackDataPolicy, requiredSplitTypes.first, requiredSplitTypes.second,
- hasDeviceAdminReceiver, isSdkLibrary, updatableSystem, emergencyInstaller,
- declaredLibraries));
+ hasDeviceAdminReceiver, isSdkLibrary, usesSdkLibraries,
+ usesSdkLibrariesVersionsMajor, usesSdkLibrariesCertDigests,
+ updatableSystem, emergencyInstaller, declaredLibraries));
}
private static boolean isDeviceAdminReceiver(
diff --git a/core/java/android/content/pm/parsing/PackageLite.java b/core/java/android/content/pm/parsing/PackageLite.java
index 9a2ee7fe4cc6..79c597327f5a 100644
--- a/core/java/android/content/pm/parsing/PackageLite.java
+++ b/core/java/android/content/pm/parsing/PackageLite.java
@@ -115,6 +115,12 @@ public class PackageLite {
*/
private final boolean mIsSdkLibrary;
+ private final @NonNull List<String> mUsesSdkLibraries;
+
+ private final @Nullable long[] mUsesSdkLibrariesVersionsMajor;
+
+ private final @Nullable String[][] mUsesSdkLibrariesCertDigests;
+
private final @NonNull List<SharedLibraryInfo> mDeclaredLibraries;
/**
@@ -149,6 +155,9 @@ public class PackageLite {
mSplitRequired = (baseApk.isSplitRequired() || hasAnyRequiredSplitTypes());
mProfileableByShell = baseApk.isProfileableByShell();
mIsSdkLibrary = baseApk.isIsSdkLibrary();
+ mUsesSdkLibraries = baseApk.getUsesSdkLibraries();
+ mUsesSdkLibrariesVersionsMajor = baseApk.getUsesSdkLibrariesVersionsMajor();
+ mUsesSdkLibrariesCertDigests = baseApk.getUsesSdkLibrariesCertDigests();
mSplitNames = splitNames;
mSplitTypes = splitTypes;
mIsFeatureSplits = isFeatureSplits;
@@ -438,6 +447,21 @@ public class PackageLite {
}
@DataClass.Generated.Member
+ public @NonNull List<String> getUsesSdkLibraries() {
+ return mUsesSdkLibraries;
+ }
+
+ @DataClass.Generated.Member
+ public @Nullable long[] getUsesSdkLibrariesVersionsMajor() {
+ return mUsesSdkLibrariesVersionsMajor;
+ }
+
+ @DataClass.Generated.Member
+ public @Nullable String[][] getUsesSdkLibrariesCertDigests() {
+ return mUsesSdkLibrariesCertDigests;
+ }
+
+ @DataClass.Generated.Member
public @NonNull List<SharedLibraryInfo> getDeclaredLibraries() {
return mDeclaredLibraries;
}
@@ -451,10 +475,10 @@ public class PackageLite {
}
@DataClass.Generated(
- time = 1728333569917L,
+ time = 1729248757933L,
codegenVersion = "1.0.23",
sourceFile = "frameworks/base/core/java/android/content/pm/parsing/PackageLite.java",
- inputSignatures = "private final @android.annotation.NonNull java.lang.String mPackageName\nprivate final @android.annotation.NonNull java.lang.String mPath\nprivate final @android.annotation.NonNull java.lang.String mBaseApkPath\nprivate final @android.annotation.Nullable java.lang.String[] mSplitApkPaths\nprivate final @android.annotation.Nullable java.lang.String[] mSplitNames\nprivate final @android.annotation.Nullable java.lang.String[] mUsesSplitNames\nprivate final @android.annotation.Nullable java.lang.String[] mConfigForSplit\nprivate final @android.annotation.Nullable java.util.Set<java.lang.String> mBaseRequiredSplitTypes\nprivate final @android.annotation.Nullable java.util.Set<java.lang.String>[] mRequiredSplitTypes\nprivate final @android.annotation.Nullable java.util.Set<java.lang.String>[] mSplitTypes\nprivate final int mVersionCodeMajor\nprivate final int mVersionCode\nprivate final int mTargetSdk\nprivate final int mBaseRevisionCode\nprivate final @android.annotation.Nullable int[] mSplitRevisionCodes\nprivate final int mInstallLocation\nprivate final @android.annotation.NonNull android.content.pm.VerifierInfo[] mVerifiers\nprivate final @android.annotation.NonNull android.content.pm.SigningDetails mSigningDetails\nprivate final @android.annotation.Nullable boolean[] mIsFeatureSplits\nprivate final boolean mIsolatedSplits\nprivate final boolean mSplitRequired\nprivate final boolean mCoreApp\nprivate final boolean mDebuggable\nprivate final boolean mMultiArch\nprivate final boolean mUse32bitAbi\nprivate final boolean mExtractNativeLibs\nprivate final boolean mProfileableByShell\nprivate final boolean mUseEmbeddedDex\nprivate final boolean mIsSdkLibrary\nprivate final @android.annotation.NonNull java.util.List<android.content.pm.SharedLibraryInfo> mDeclaredLibraries\nprivate final @android.annotation.Nullable android.content.pm.ArchivedPackageParcel mArchivedPackage\npublic java.util.List<java.lang.String> getAllApkPaths()\npublic long getLongVersionCode()\nprivate boolean hasAnyRequiredSplitTypes()\nclass PackageLite extends java.lang.Object implements []\n@com.android.internal.util.DataClass(genConstructor=false, genConstDefs=false)")
+ inputSignatures = "private final @android.annotation.NonNull java.lang.String mPackageName\nprivate final @android.annotation.NonNull java.lang.String mPath\nprivate final @android.annotation.NonNull java.lang.String mBaseApkPath\nprivate final @android.annotation.Nullable java.lang.String[] mSplitApkPaths\nprivate final @android.annotation.Nullable java.lang.String[] mSplitNames\nprivate final @android.annotation.Nullable java.lang.String[] mUsesSplitNames\nprivate final @android.annotation.Nullable java.lang.String[] mConfigForSplit\nprivate final @android.annotation.Nullable java.util.Set<java.lang.String> mBaseRequiredSplitTypes\nprivate final @android.annotation.Nullable java.util.Set<java.lang.String>[] mRequiredSplitTypes\nprivate final @android.annotation.Nullable java.util.Set<java.lang.String>[] mSplitTypes\nprivate final int mVersionCodeMajor\nprivate final int mVersionCode\nprivate final int mTargetSdk\nprivate final int mBaseRevisionCode\nprivate final @android.annotation.Nullable int[] mSplitRevisionCodes\nprivate final int mInstallLocation\nprivate final @android.annotation.NonNull android.content.pm.VerifierInfo[] mVerifiers\nprivate final @android.annotation.NonNull android.content.pm.SigningDetails mSigningDetails\nprivate final @android.annotation.Nullable boolean[] mIsFeatureSplits\nprivate final boolean mIsolatedSplits\nprivate final boolean mSplitRequired\nprivate final boolean mCoreApp\nprivate final boolean mDebuggable\nprivate final boolean mMultiArch\nprivate final boolean mUse32bitAbi\nprivate final boolean mExtractNativeLibs\nprivate final boolean mProfileableByShell\nprivate final boolean mUseEmbeddedDex\nprivate final boolean mIsSdkLibrary\nprivate final @android.annotation.NonNull java.util.List<java.lang.String> mUsesSdkLibraries\nprivate final @android.annotation.Nullable long[] mUsesSdkLibrariesVersionsMajor\nprivate final @android.annotation.Nullable java.lang.String[][] mUsesSdkLibrariesCertDigests\nprivate final @android.annotation.NonNull java.util.List<android.content.pm.SharedLibraryInfo> mDeclaredLibraries\nprivate final @android.annotation.Nullable android.content.pm.ArchivedPackageParcel mArchivedPackage\npublic java.util.List<java.lang.String> getAllApkPaths()\npublic long getLongVersionCode()\nprivate boolean hasAnyRequiredSplitTypes()\nclass PackageLite extends java.lang.Object implements []\n@com.android.internal.util.DataClass(genConstructor=false, genConstDefs=false)")
@Deprecated
private void __metadata() {}
diff --git a/core/java/android/content/pm/verify/pkg/IVerificationSessionInterface.aidl b/core/java/android/content/pm/verify/pkg/IVerificationSessionInterface.aidl
index 66caf2d0fec0..2ab745205193 100644
--- a/core/java/android/content/pm/verify/pkg/IVerificationSessionInterface.aidl
+++ b/core/java/android/content/pm/verify/pkg/IVerificationSessionInterface.aidl
@@ -24,16 +24,9 @@ import android.os.PersistableBundle;
* @hide
*/
interface IVerificationSessionInterface {
- @JavaPassthrough(annotation="@android.annotation.RequiresPermission(android.Manifest.permission.VERIFICATION_AGENT)")
long getTimeoutTime(int verificationId);
- @JavaPassthrough(annotation="@android.annotation.RequiresPermission(android.Manifest.permission.VERIFICATION_AGENT)")
long extendTimeRemaining(int verificationId, long additionalMs);
- @JavaPassthrough(annotation="@android.annotation.RequiresPermission(android.Manifest.permission.VERIFICATION_AGENT)")
boolean setVerificationPolicy(int verificationId, int policy);
- @JavaPassthrough(annotation="@android.annotation.RequiresPermission(android.Manifest.permission.VERIFICATION_AGENT)")
void reportVerificationIncomplete(int verificationId, int reason);
- @JavaPassthrough(annotation="@android.annotation.RequiresPermission(android.Manifest.permission.VERIFICATION_AGENT)")
- void reportVerificationComplete(int verificationId, in VerificationStatus status);
- @JavaPassthrough(annotation="@android.annotation.RequiresPermission(android.Manifest.permission.VERIFICATION_AGENT)")
- void reportVerificationCompleteWithExtensionResponse(int verificationId, in VerificationStatus status, in PersistableBundle response);
+ void reportVerificationComplete(int verificationId, in VerificationStatus status, in @nullable PersistableBundle extensionResponse);
} \ No newline at end of file
diff --git a/core/java/android/content/pm/verify/pkg/VerificationSession.java b/core/java/android/content/pm/verify/pkg/VerificationSession.java
index 4ade21198f37..97f78e0978fa 100644
--- a/core/java/android/content/pm/verify/pkg/VerificationSession.java
+++ b/core/java/android/content/pm/verify/pkg/VerificationSession.java
@@ -19,7 +19,6 @@ package android.content.pm.verify.pkg;
import android.annotation.FlaggedApi;
import android.annotation.IntDef;
import android.annotation.NonNull;
-import android.annotation.RequiresPermission;
import android.annotation.SystemApi;
import android.content.pm.Flags;
import android.content.pm.PackageInstaller;
@@ -166,8 +165,8 @@ public final class VerificationSession implements Parcelable {
/**
* Get the value of Clock.elapsedRealtime() at which time this verification
* will timeout as incomplete if no other verification response is provided.
+ * @throws SecurityException if the caller is not the current verifier bound by the system.
*/
- @RequiresPermission(android.Manifest.permission.VERIFICATION_AGENT)
public long getTimeoutTime() {
try {
return mSession.getTimeoutTime(mId);
@@ -190,8 +189,8 @@ public final class VerificationSession implements Parcelable {
/**
* Override the verification policy for this session.
* @return True if the override was successful, False otherwise.
+ * @throws SecurityException if the caller is not the current verifier bound by the system.
*/
- @RequiresPermission(android.Manifest.permission.VERIFICATION_AGENT)
public boolean setVerificationPolicy(@PackageInstaller.VerificationPolicy int policy) {
if (mVerificationPolicy == policy) {
// No effective policy change
@@ -215,8 +214,8 @@ public final class VerificationSession implements Parcelable {
* This may be called multiple times. If the request would bypass any max
* duration by the system, the method will return a lower value than the
* requested amount that indicates how much the time was extended.
+ * @throws SecurityException if the caller is not the current verifier bound by the system.
*/
- @RequiresPermission(android.Manifest.permission.VERIFICATION_AGENT)
public long extendTimeRemaining(long additionalMs) {
try {
return mSession.extendTimeRemaining(mId, additionalMs);
@@ -227,9 +226,9 @@ public final class VerificationSession implements Parcelable {
/**
* Report to the system that verification could not be completed along
- * with an approximate reason to pass on to the installer.
+ * with an approximate reason to pass on to the installer.]
+ * @throws SecurityException if the caller is not the current verifier bound by the system.
*/
- @RequiresPermission(android.Manifest.permission.VERIFICATION_AGENT)
public void reportVerificationIncomplete(@VerificationIncompleteReason int reason) {
try {
mSession.reportVerificationIncomplete(mId, reason);
@@ -242,11 +241,11 @@ public final class VerificationSession implements Parcelable {
* Report to the system that the verification has completed and the
* install process may act on that status to either block in the case
* of failure or continue to process the install in the case of success.
+ * @throws SecurityException if the caller is not the current verifier bound by the system.
*/
- @RequiresPermission(android.Manifest.permission.VERIFICATION_AGENT)
public void reportVerificationComplete(@NonNull VerificationStatus status) {
try {
- mSession.reportVerificationComplete(mId, status);
+ mSession.reportVerificationComplete(mId, status, /* extensionResponse= */ null);
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
}
@@ -256,12 +255,12 @@ public final class VerificationSession implements Parcelable {
* Same as {@link #reportVerificationComplete(VerificationStatus)}, but also provide
* a result to the extension params provided in the request, which will be passed to the
* installer in the installation result.
+ * @throws SecurityException if the caller is not the current verifier bound by the system.
*/
- @RequiresPermission(android.Manifest.permission.VERIFICATION_AGENT)
public void reportVerificationComplete(@NonNull VerificationStatus status,
- @NonNull PersistableBundle response) {
+ @NonNull PersistableBundle extensionResponse) {
try {
- mSession.reportVerificationCompleteWithExtensionResponse(mId, status, response);
+ mSession.reportVerificationComplete(mId, status, extensionResponse);
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
}
diff --git a/core/java/android/hardware/camera2/params/OutputConfiguration.java b/core/java/android/hardware/camera2/params/OutputConfiguration.java
index ebcc37113d1a..22dbf5bdde37 100644
--- a/core/java/android/hardware/camera2/params/OutputConfiguration.java
+++ b/core/java/android/hardware/camera2/params/OutputConfiguration.java
@@ -43,6 +43,7 @@ import android.hardware.camera2.utils.SurfaceUtils;
import android.media.ImageReader;
import android.os.Parcel;
import android.os.Parcelable;
+import android.util.IntArray;
import android.util.Log;
import android.util.Size;
import android.view.Surface;
@@ -596,6 +597,10 @@ public final class OutputConfiguration implements Parcelable {
mStreamUseCase = CameraMetadata.SCALER_AVAILABLE_STREAM_USE_CASES_DEFAULT;
mTimestampBase = TIMESTAMP_BASE_DEFAULT;
mMirrorMode = MIRROR_MODE_AUTO;
+ mMirrorModeForSurfaces = new IntArray();
+ if (Flags.mirrorModeSharedSurfaces()) {
+ mMirrorModeForSurfaces.add(mMirrorMode);
+ }
mReadoutTimestampEnabled = false;
mIsReadoutSensorTimestampBase = false;
mUsage = 0;
@@ -827,6 +832,7 @@ public final class OutputConfiguration implements Parcelable {
mSurfaceGroupId = SURFACE_GROUP_ID_NONE;
mSurfaces = new ArrayList<Surface>();
+ mMirrorModeForSurfaces = new IntArray();
mRotation = ROTATION_0;
mConfiguredSize = surfaceSize;
mConfiguredFormat = StreamConfigurationMap.imageFormatToInternal(ImageFormat.PRIVATE);
@@ -971,6 +977,9 @@ public final class OutputConfiguration implements Parcelable {
mDynamicRangeProfile = DynamicRangeProfiles.STANDARD;
mColorSpace = ColorSpaceProfiles.UNSPECIFIED;
mStreamUseCase = CameraMetadata.SCALER_AVAILABLE_STREAM_USE_CASES_DEFAULT;
+ mTimestampBase = TIMESTAMP_BASE_DEFAULT;
+ mMirrorMode = MIRROR_MODE_AUTO;
+ mMirrorModeForSurfaces = new IntArray();
mReadoutTimestampEnabled = false;
mIsReadoutSensorTimestampBase = false;
mUsage = usage;
@@ -1239,6 +1248,9 @@ public final class OutputConfiguration implements Parcelable {
}
mSurfaces.add(surface);
+ if (Flags.mirrorModeSharedSurfaces()) {
+ mMirrorModeForSurfaces.add(mMirrorMode);
+ }
}
/**
@@ -1266,9 +1278,16 @@ public final class OutputConfiguration implements Parcelable {
throw new IllegalArgumentException(
"Cannot remove surface associated with this output configuration");
}
- if (!mSurfaces.remove(surface)) {
+
+ int surfaceIndex = mSurfaces.indexOf(surface);
+ if (surfaceIndex == -1) {
throw new IllegalArgumentException("Surface is not part of this output configuration");
}
+
+ mSurfaces.remove(surfaceIndex);
+ if (Flags.mirrorModeSharedSurfaces()) {
+ mMirrorModeForSurfaces.remove(surfaceIndex);
+ }
}
/**
@@ -1405,6 +1424,11 @@ public final class OutputConfiguration implements Parcelable {
* ImageReader, MediaRecorder, or MediaCodec, the mirror mode has no effect. If mirroring is
* needed for such outputs, the application needs to mirror the image buffers itself before
* passing them onward.</p>
+ *
+ * <p>Starting from Android 16, this function sets the mirror modes for all of the output
+ * surfaces contained within this OutputConfiguration. To set the mirror mode for a particular
+ * output surface, the application can call {@link #setMirrorMode(Surface, int)}. Prior to
+ * Android 16, this function is only applicable if surface sharing is not enabled.</p>
*/
public void setMirrorMode(@MirrorMode int mirrorMode) {
// Verify that the value is in range
@@ -1413,6 +1437,9 @@ public final class OutputConfiguration implements Parcelable {
throw new IllegalArgumentException("Not a valid mirror mode " + mirrorMode);
}
mMirrorMode = mirrorMode;
+ for (int j = 0; j < mMirrorModeForSurfaces.size(); j++) {
+ mMirrorModeForSurfaces.set(j, mirrorMode);
+ }
}
/**
@@ -1428,6 +1455,72 @@ public final class OutputConfiguration implements Parcelable {
}
/**
+ * Set the mirroring mode for a surface belonging to this OutputConfiguration
+ *
+ * <p>This function is identical to {@link #setMirroMode(int)} if {@code surface} is
+ * the only surface belonging to this OutputConfiguration.</p>
+ *
+ * <p>If this OutputConfiguration contains a deferred surface, the application can either
+ * call {@link #setMirrorMode(int)}, or call this function after calling {@link #addSurface}.
+ * </p>
+ *
+ * <p>If this OutputConfiguration contains shared surfaces, the application can set
+ * different mirroring modes for different surfaces.</p>
+ *
+ * <p>For efficiency, the mirror effect is applied as a transform flag, so it is only effective
+ * in some outputs. It works automatically for SurfaceView and TextureView outputs. For manual
+ * use of SurfaceTexture, it is reflected in the value of
+ * {@link android.graphics.SurfaceTexture#getTransformMatrix}. For other end points, such as
+ * ImageReader, MediaRecorder, or MediaCodec, the mirror mode has no effect. If mirroring is
+ * needed for such outputs, the application needs to mirror the image buffers itself before
+ * passing them onward.</p>
+ *
+ * @throws IllegalArgumentException If the {@code surface} doesn't belong to this
+ * OutputConfiguration, or the {@code mirrorMode} value is
+ * not valid.
+ */
+ @FlaggedApi(Flags.FLAG_MIRROR_MODE_SHARED_SURFACES)
+ public void setMirrorMode(@NonNull Surface surface, @MirrorMode int mirrorMode) {
+ checkNotNull(surface, "Surface must not be null");
+ // Verify that the value is in range
+ if (mirrorMode < MIRROR_MODE_AUTO || mirrorMode > MIRROR_MODE_V) {
+ throw new IllegalArgumentException("Not a valid mirror mode " + mirrorMode);
+ }
+ int surfaceIndex = mSurfaces.indexOf(surface);
+ if (surfaceIndex == -1) {
+ throw new IllegalArgumentException("Surface not part of the OutputConfiguration");
+ }
+
+ mMirrorModeForSurfaces.set(surfaceIndex, mirrorMode);
+ }
+
+ /**
+ * Get the current mirroring mode for an output surface
+ *
+ * <p>If no {@link #setMirrorMode} is called first, this function returns
+ * {@link #MIRROR_MODE_AUTO}.</p>
+ *
+ * <p>If only {@link #setMirrorMode(int)} is called, the mirroring mode set by that
+ * function will be returned here as long as the {@code surface} belongs to this
+ * output configuration.</p>
+ *
+ * @throws IllegalArgumentException If the {@code surface} doesn't belong to this
+ * OutputConfiguration.
+ *
+ * @return The mirroring mode for the specified output surface
+ */
+ @FlaggedApi(Flags.FLAG_MIRROR_MODE_SHARED_SURFACES)
+ public @MirrorMode int getMirrorMode(@NonNull Surface surface) {
+ checkNotNull(surface, "Surface must not be null");
+
+ int surfaceIndex = mSurfaces.indexOf(surface);
+ if (surfaceIndex == -1) {
+ throw new IllegalArgumentException("Surface not part of the OutputConfiguration");
+ }
+ return mMirrorModeForSurfaces.get(surfaceIndex);
+ }
+
+ /**
* Use the camera sensor's readout time for the image timestamp.
*
* <p>The start of the camera sensor readout after exposure. For a rolling shutter camera
@@ -1491,6 +1584,7 @@ public final class OutputConfiguration implements Parcelable {
this.mStreamUseCase = other.mStreamUseCase;
this.mTimestampBase = other.mTimestampBase;
this.mMirrorMode = other.mMirrorMode;
+ this.mMirrorModeForSurfaces = other.mMirrorModeForSurfaces.clone();
this.mReadoutTimestampEnabled = other.mReadoutTimestampEnabled;
this.mUsage = other.mUsage;
}
@@ -1520,6 +1614,7 @@ public final class OutputConfiguration implements Parcelable {
int timestampBase = source.readInt();
int mirrorMode = source.readInt();
+ int[] mirrorModeForSurfaces = source.createIntArray();
boolean readoutTimestampEnabled = source.readInt() == 1;
int format = source.readInt();
int dataSpace = source.readInt();
@@ -1531,7 +1626,6 @@ public final class OutputConfiguration implements Parcelable {
mConfiguredSize = new Size(width, height);
mIsDeferredConfig = isDeferred;
mIsShared = isShared;
- mSurfaces = surfaces;
mUsage = 0;
if (mSurfaces.size() > 0) {
mSurfaceType = SURFACE_TYPE_UNKNOWN;
@@ -1560,6 +1654,7 @@ public final class OutputConfiguration implements Parcelable {
mStreamUseCase = streamUseCase;
mTimestampBase = timestampBase;
mMirrorMode = mirrorMode;
+ mMirrorModeForSurfaces = IntArray.wrap(mirrorModeForSurfaces);
mReadoutTimestampEnabled = readoutTimestampEnabled;
}
@@ -1706,6 +1801,7 @@ public final class OutputConfiguration implements Parcelable {
dest.writeLong(mStreamUseCase);
dest.writeInt(mTimestampBase);
dest.writeInt(mMirrorMode);
+ dest.writeIntArray(mMirrorModeForSurfaces.toArray());
dest.writeInt(mReadoutTimestampEnabled ? 1 : 0);
dest.writeInt(mConfiguredFormat);
dest.writeInt(mConfiguredDataspace);
@@ -1756,6 +1852,16 @@ public final class OutputConfiguration implements Parcelable {
return false;
}
}
+ if (Flags.mirrorModeSharedSurfaces()) {
+ if (mMirrorModeForSurfaces.size() != other.mMirrorModeForSurfaces.size()) {
+ return false;
+ }
+ for (int j = 0; j < mMirrorModeForSurfaces.size(); j++) {
+ if (mMirrorModeForSurfaces.get(j) != other.mMirrorModeForSurfaces.get(j)) {
+ return false;
+ }
+ }
+ }
int minLen = Math.min(mSurfaces.size(), other.mSurfaces.size());
for (int i = 0; i < minLen; i++) {
if (mSurfaces.get(i) != other.mSurfaces.get(i))
@@ -1799,8 +1905,9 @@ public final class OutputConfiguration implements Parcelable {
mPhysicalCameraId == null ? 0 : mPhysicalCameraId.hashCode(),
mIsMultiResolution ? 1 : 0, mSensorPixelModesUsed.hashCode(),
mDynamicRangeProfile, mColorSpace, mStreamUseCase,
- mTimestampBase, mMirrorMode, mReadoutTimestampEnabled ? 1 : 0,
- Long.hashCode(mUsage));
+ mTimestampBase, mMirrorMode,
+ HashCodeHelpers.hashCode(mMirrorModeForSurfaces.toArray()),
+ mReadoutTimestampEnabled ? 1 : 0, Long.hashCode(mUsage));
}
return HashCodeHelpers.hashCode(
@@ -1810,7 +1917,9 @@ public final class OutputConfiguration implements Parcelable {
mPhysicalCameraId == null ? 0 : mPhysicalCameraId.hashCode(),
mIsMultiResolution ? 1 : 0, mSensorPixelModesUsed.hashCode(),
mDynamicRangeProfile, mColorSpace, mStreamUseCase, mTimestampBase,
- mMirrorMode, mReadoutTimestampEnabled ? 1 : 0, Long.hashCode(mUsage));
+ mMirrorMode, HashCodeHelpers.hashCode(mMirrorModeForSurfaces.toArray()),
+ mReadoutTimestampEnabled ? 1 : 0,
+ Long.hashCode(mUsage));
}
private static final String TAG = "OutputConfiguration";
@@ -1852,6 +1961,8 @@ public final class OutputConfiguration implements Parcelable {
private int mTimestampBase;
// Mirroring mode
private int mMirrorMode;
+ // Per-surface mirror modes
+ private IntArray mMirrorModeForSurfaces;
// readout timestamp
private boolean mReadoutTimestampEnabled;
// Whether the timestamp base is set to READOUT_SENSOR
diff --git a/core/java/android/hardware/display/AmbientDisplayConfiguration.java b/core/java/android/hardware/display/AmbientDisplayConfiguration.java
index 59a602ca092d..7361d4f6b882 100644
--- a/core/java/android/hardware/display/AmbientDisplayConfiguration.java
+++ b/core/java/android/hardware/display/AmbientDisplayConfiguration.java
@@ -16,6 +16,8 @@
package android.hardware.display;
+import static android.provider.Settings.Secure.SCREEN_OFF_UNLOCK_UDFPS_ENABLED;
+
import android.annotation.TestApi;
import android.content.Context;
import android.hardware.biometrics.Flags;
@@ -151,8 +153,8 @@ public class AmbientDisplayConfiguration {
public boolean screenOffUdfpsEnabled(int user) {
return !TextUtils.isEmpty(udfpsLongPressSensorType())
&& ((mScreenOffUdfpsEnabledByDefault && Flags.screenOffUnlockUdfps())
- ? boolSettingDefaultOn("screen_off_udfps_enabled", user)
- : boolSettingDefaultOff("screen_off_udfps_enabled", user));
+ ? boolSettingDefaultOn(SCREEN_OFF_UNLOCK_UDFPS_ENABLED, user)
+ : boolSettingDefaultOff(SCREEN_OFF_UNLOCK_UDFPS_ENABLED, user));
}
/** @hide */
diff --git a/core/java/android/hardware/input/AidlKeyGestureEvent.aidl b/core/java/android/hardware/input/AidlKeyGestureEvent.aidl
index 7cf8795085e3..fc7151940bd7 100644
--- a/core/java/android/hardware/input/AidlKeyGestureEvent.aidl
+++ b/core/java/android/hardware/input/AidlKeyGestureEvent.aidl
@@ -26,4 +26,10 @@ parcelable AidlKeyGestureEvent {
int action;
int displayId;
int flags;
+
+ // App launch parameters: only set when gestureType = KEY_GESTURE_TYPE_LAUNCH_APPLICATION
+ String appLaunchCategory;
+ String appLaunchRole;
+ String appLaunchPackageName;
+ String appLaunchClassName;
}
diff --git a/core/java/android/hardware/input/AppLaunchData.java b/core/java/android/hardware/input/AppLaunchData.java
new file mode 100644
index 000000000000..43186f5db415
--- /dev/null
+++ b/core/java/android/hardware/input/AppLaunchData.java
@@ -0,0 +1,176 @@
+/*
+ * Copyright 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.
+ */
+
+package android.hardware.input;
+
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.text.TextUtils;
+
+import java.util.Objects;
+
+/**
+ * Provides data for launching an application.
+ *
+ * @hide
+ */
+public interface AppLaunchData {
+
+ /** Creates AppLaunchData for the provided category */
+ @NonNull
+ static AppLaunchData createLaunchDataForCategory(@NonNull String category) {
+ return new CategoryData(category);
+ }
+
+ /** Creates AppLaunchData for the provided role */
+ @NonNull
+ static AppLaunchData createLaunchDataForRole(@NonNull String role) {
+ return new RoleData(role);
+ }
+
+ /** Creates AppLaunchData for the target package name and class name */
+ @NonNull
+ static AppLaunchData createLaunchDataForComponent(@NonNull String packageName,
+ @NonNull String className) {
+ return new ComponentData(packageName, className);
+ }
+
+ @Nullable
+ static AppLaunchData createLaunchData(@Nullable String category, @Nullable String role,
+ @Nullable String packageName, @Nullable String className) {
+ if (!TextUtils.isEmpty(category)) {
+ return new CategoryData(category);
+ }
+ if (!TextUtils.isEmpty(role)) {
+ return new RoleData(role);
+ }
+ if (!TextUtils.isEmpty(packageName) && !TextUtils.isEmpty(className)) {
+ return new ComponentData(packageName, className);
+ }
+ return null;
+ }
+
+ /** Intent category based app launch data */
+ class CategoryData implements AppLaunchData {
+ @NonNull
+ private final String mCategory;
+ public CategoryData(@NonNull String category) {
+ mCategory = category;
+ }
+
+ @NonNull
+ public String getCategory() {
+ return mCategory;
+ }
+
+ @Override
+ public boolean equals(Object o) {
+ if (this == o) return true;
+ if (!(o instanceof CategoryData that)) return false;
+ return Objects.equals(mCategory, that.mCategory);
+ }
+
+ @Override
+ public int hashCode() {
+ return Objects.hash(mCategory);
+ }
+
+ @Override
+ public String toString() {
+ return "CategoryData{" +
+ "mCategory='" + mCategory + '\'' +
+ '}';
+ }
+ }
+
+ /** Role based app launch data */
+ class RoleData implements AppLaunchData {
+ @NonNull
+ private final String mRole;
+ public RoleData(@NonNull String role) {
+ mRole = role;
+ }
+
+ @NonNull
+ public String getRole() {
+ return mRole;
+ }
+
+ @Override
+ public boolean equals(Object o) {
+ if (this == o) return true;
+ if (!(o instanceof RoleData roleData)) return false;
+ return Objects.equals(mRole, roleData.mRole);
+ }
+
+ @Override
+ public int hashCode() {
+ return Objects.hash(mRole);
+ }
+
+ @Override
+ public String toString() {
+ return "RoleData{" +
+ "mRole='" + mRole + '\'' +
+ '}';
+ }
+ }
+
+ /** Target application launch data */
+ class ComponentData implements AppLaunchData {
+ @NonNull
+ private final String mPackageName;
+
+ @NonNull
+ private final String mClassName;
+
+ public ComponentData(@NonNull String packageName, @NonNull String className) {
+ mPackageName = packageName;
+ mClassName = className;
+ }
+
+ @NonNull
+ public String getPackageName() {
+ return mPackageName;
+ }
+
+ @NonNull
+ public String getClassName() {
+ return mClassName;
+ }
+
+ @Override
+ public boolean equals(Object o) {
+ if (this == o) return true;
+ if (!(o instanceof ComponentData that)) return false;
+ return Objects.equals(mPackageName, that.mPackageName) && Objects.equals(
+ mClassName, that.mClassName);
+ }
+
+ @Override
+ public int hashCode() {
+ return Objects.hash(mPackageName, mClassName);
+ }
+
+ @Override
+ public String toString() {
+ return "ComponentData{" +
+ "mPackageName='" + mPackageName + '\'' +
+ ", mClassName='" + mClassName + '\'' +
+ '}';
+ }
+ }
+}
diff --git a/core/java/android/hardware/input/KeyGestureEvent.java b/core/java/android/hardware/input/KeyGestureEvent.java
index 71d17ebc40e3..ee1a6aba2ab5 100644
--- a/core/java/android/hardware/input/KeyGestureEvent.java
+++ b/core/java/android/hardware/input/KeyGestureEvent.java
@@ -19,6 +19,8 @@ package android.hardware.input;
import android.annotation.IntDef;
import android.annotation.NonNull;
import android.annotation.Nullable;
+import android.app.role.RoleManager;
+import android.content.Intent;
import android.view.Display;
import android.view.KeyCharacterMap;
@@ -26,6 +28,7 @@ import com.android.internal.util.FrameworkStatsLog;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
+import java.util.Objects;
/**
* Provides information about the keyboard gesture event being triggered by an external keyboard.
@@ -37,6 +40,9 @@ public final class KeyGestureEvent {
@NonNull
private AidlKeyGestureEvent mKeyGestureEvent;
+ private static final int LOG_EVENT_UNSPECIFIED =
+ FrameworkStatsLog.KEYBOARD_SYSTEMS_EVENT_REPORTED__KEYBOARD_SYSTEM_EVENT__UNSPECIFIED;
+
public static final int KEY_GESTURE_TYPE_UNSPECIFIED = 0;
public static final int KEY_GESTURE_TYPE_HOME = 1;
public static final int KEY_GESTURE_TYPE_RECENT_APPS = 2;
@@ -76,6 +82,8 @@ public final class KeyGestureEvent {
public static final int KEY_GESTURE_TYPE_SLEEP = 36;
public static final int KEY_GESTURE_TYPE_WAKEUP = 37;
public static final int KEY_GESTURE_TYPE_MEDIA_KEY = 38;
+ // TODO(b/280423320): Remove "LAUNCH_DEFAULT_..." gestures and rely on launch intent to find
+ // the correct logging event.
public static final int KEY_GESTURE_TYPE_LAUNCH_DEFAULT_BROWSER = 39;
public static final int KEY_GESTURE_TYPE_LAUNCH_DEFAULT_EMAIL = 40;
public static final int KEY_GESTURE_TYPE_LAUNCH_DEFAULT_CONTACTS = 41;
@@ -88,7 +96,7 @@ public final class KeyGestureEvent {
public static final int KEY_GESTURE_TYPE_LAUNCH_DEFAULT_FILES = 48;
public static final int KEY_GESTURE_TYPE_LAUNCH_DEFAULT_WEATHER = 49;
public static final int KEY_GESTURE_TYPE_LAUNCH_DEFAULT_FITNESS = 50;
- public static final int KEY_GESTURE_TYPE_LAUNCH_APPLICATION_BY_PACKAGE_NAME = 51;
+ public static final int KEY_GESTURE_TYPE_LAUNCH_APPLICATION = 51;
public static final int KEY_GESTURE_TYPE_DESKTOP_MODE = 52;
public static final int KEY_GESTURE_TYPE_MULTI_WINDOW_NAVIGATION = 53;
public static final int KEY_GESTURE_TYPE_RECENT_APPS_SWITCHER = 54;
@@ -166,7 +174,7 @@ public final class KeyGestureEvent {
KEY_GESTURE_TYPE_LAUNCH_DEFAULT_FILES,
KEY_GESTURE_TYPE_LAUNCH_DEFAULT_WEATHER,
KEY_GESTURE_TYPE_LAUNCH_DEFAULT_FITNESS,
- KEY_GESTURE_TYPE_LAUNCH_APPLICATION_BY_PACKAGE_NAME,
+ KEY_GESTURE_TYPE_LAUNCH_APPLICATION,
KEY_GESTURE_TYPE_DESKTOP_MODE,
KEY_GESTURE_TYPE_MULTI_WINDOW_NAVIGATION,
KEY_GESTURE_TYPE_RECENT_APPS_SWITCHER,
@@ -210,6 +218,8 @@ public final class KeyGestureEvent {
private int mAction = KeyGestureEvent.ACTION_GESTURE_COMPLETE;
private int mDisplayId = Display.DEFAULT_DISPLAY;
private int mFlags = 0;
+ @Nullable
+ private AppLaunchData mAppLaunchData = null;
/**
* @see KeyGestureEvent#getDeviceId()
@@ -268,6 +278,14 @@ public final class KeyGestureEvent {
}
/**
+ * @see KeyGestureEvent#getAppLaunchData()
+ */
+ public Builder setAppLaunchData(@NonNull AppLaunchData appLaunchData) {
+ mAppLaunchData = appLaunchData;
+ return this;
+ }
+
+ /**
* Build {@link KeyGestureEvent}
*/
public KeyGestureEvent build() {
@@ -279,6 +297,21 @@ public final class KeyGestureEvent {
event.action = mAction;
event.displayId = mDisplayId;
event.flags = mFlags;
+ if (mAppLaunchData != null) {
+ if (mAppLaunchData instanceof AppLaunchData.CategoryData) {
+ event.appLaunchCategory =
+ ((AppLaunchData.CategoryData) mAppLaunchData).getCategory();
+ } else if (mAppLaunchData instanceof AppLaunchData.RoleData) {
+ event.appLaunchRole = ((AppLaunchData.RoleData) mAppLaunchData).getRole();
+ } else if (mAppLaunchData instanceof AppLaunchData.ComponentData) {
+ event.appLaunchPackageName =
+ ((AppLaunchData.ComponentData) mAppLaunchData).getPackageName();
+ event.appLaunchClassName =
+ ((AppLaunchData.ComponentData) mAppLaunchData).getClassName();
+ } else {
+ throw new IllegalArgumentException("AppLaunchData type is invalid!");
+ }
+ }
return new KeyGestureEvent(event);
}
}
@@ -315,6 +348,27 @@ public final class KeyGestureEvent {
return (mKeyGestureEvent.flags & FLAG_CANCELLED) != 0;
}
+ public int getLogEvent() {
+ if (getKeyGestureType() == KEY_GESTURE_TYPE_LAUNCH_APPLICATION) {
+ return getLogEventFromLaunchAppData(getAppLaunchData());
+ }
+ return keyGestureTypeToLogEvent(getKeyGestureType());
+ }
+
+ /**
+ * @return Launch app data associated with the event, only if key gesture type is
+ * {@code KEY_GESTURE_TYPE_LAUNCH_APPLICATION}
+ */
+ @Nullable
+ public AppLaunchData getAppLaunchData() {
+ if (mKeyGestureEvent.gestureType != KEY_GESTURE_TYPE_LAUNCH_APPLICATION) {
+ return null;
+ }
+ return AppLaunchData.createLaunchData(mKeyGestureEvent.appLaunchCategory,
+ mKeyGestureEvent.appLaunchRole, mKeyGestureEvent.appLaunchPackageName,
+ mKeyGestureEvent.appLaunchClassName);
+ }
+
@Override
public String toString() {
return "KeyGestureEvent { "
@@ -324,7 +378,8 @@ public final class KeyGestureEvent {
+ "keyGestureType = " + keyGestureTypeToString(mKeyGestureEvent.gestureType) + ", "
+ "action = " + mKeyGestureEvent.action + ", "
+ "displayId = " + mKeyGestureEvent.displayId + ", "
- + "flags = " + mKeyGestureEvent.flags
+ + "flags = " + mKeyGestureEvent.flags + ", "
+ + "appLaunchData = " + getAppLaunchData()
+ " }";
}
@@ -339,7 +394,11 @@ public final class KeyGestureEvent {
&& mKeyGestureEvent.gestureType == that.mKeyGestureEvent.gestureType
&& mKeyGestureEvent.action == that.mKeyGestureEvent.action
&& mKeyGestureEvent.displayId == that.mKeyGestureEvent.displayId
- && mKeyGestureEvent.flags == that.mKeyGestureEvent.flags;
+ && mKeyGestureEvent.flags == that.mKeyGestureEvent.flags
+ && Objects.equals(mKeyGestureEvent.appLaunchCategory, that.mKeyGestureEvent.appLaunchCategory)
+ && Objects.equals(mKeyGestureEvent.appLaunchRole, that.mKeyGestureEvent.appLaunchRole)
+ && Objects.equals(mKeyGestureEvent.appLaunchPackageName, that.mKeyGestureEvent.appLaunchPackageName)
+ && Objects.equals(mKeyGestureEvent.appLaunchClassName, that.mKeyGestureEvent.appLaunchClassName);
}
@Override
@@ -352,13 +411,21 @@ public final class KeyGestureEvent {
_hash = 31 * _hash + mKeyGestureEvent.action;
_hash = 31 * _hash + mKeyGestureEvent.displayId;
_hash = 31 * _hash + mKeyGestureEvent.flags;
+ _hash = 31 * _hash + (mKeyGestureEvent.appLaunchCategory != null
+ ? mKeyGestureEvent.appLaunchCategory.hashCode() : 0);
+ _hash = 31 * _hash + (mKeyGestureEvent.appLaunchRole != null
+ ? mKeyGestureEvent.appLaunchRole.hashCode() : 0);
+ _hash = 31 * _hash + (mKeyGestureEvent.appLaunchPackageName != null
+ ? mKeyGestureEvent.appLaunchPackageName.hashCode() : 0);
+ _hash = 31 * _hash + (mKeyGestureEvent.appLaunchClassName != null
+ ? mKeyGestureEvent.appLaunchClassName.hashCode() : 0);
return _hash;
}
/**
* Convert KeyGestureEvent type to corresponding log event got KeyboardSystemsEvent
*/
- public static int keyGestureTypeToLogEvent(@KeyGestureType int value) {
+ private static int keyGestureTypeToLogEvent(@KeyGestureType int value) {
switch (value) {
case KEY_GESTURE_TYPE_HOME:
return FrameworkStatsLog.KEYBOARD_SYSTEMS_EVENT_REPORTED__KEYBOARD_SYSTEM_EVENT__HOME;
@@ -460,14 +527,79 @@ public final class KeyGestureEvent {
return FrameworkStatsLog.KEYBOARD_SYSTEMS_EVENT_REPORTED__KEYBOARD_SYSTEM_EVENT__LAUNCH_DEFAULT_WEATHER;
case KEY_GESTURE_TYPE_LAUNCH_DEFAULT_FITNESS:
return FrameworkStatsLog.KEYBOARD_SYSTEMS_EVENT_REPORTED__KEYBOARD_SYSTEM_EVENT__LAUNCH_DEFAULT_FITNESS;
- case KEY_GESTURE_TYPE_LAUNCH_APPLICATION_BY_PACKAGE_NAME:
+ case KEY_GESTURE_TYPE_LAUNCH_APPLICATION:
return FrameworkStatsLog.KEYBOARD_SYSTEMS_EVENT_REPORTED__KEYBOARD_SYSTEM_EVENT__LAUNCH_APPLICATION_BY_PACKAGE_NAME;
case KEY_GESTURE_TYPE_DESKTOP_MODE:
return FrameworkStatsLog.KEYBOARD_SYSTEMS_EVENT_REPORTED__KEYBOARD_SYSTEM_EVENT__DESKTOP_MODE;
case KEY_GESTURE_TYPE_MULTI_WINDOW_NAVIGATION:
return FrameworkStatsLog.KEYBOARD_SYSTEMS_EVENT_REPORTED__KEYBOARD_SYSTEM_EVENT__MULTI_WINDOW_NAVIGATION;
default:
- return FrameworkStatsLog.KEYBOARD_SYSTEMS_EVENT_REPORTED__KEYBOARD_SYSTEM_EVENT__UNSPECIFIED;
+ return LOG_EVENT_UNSPECIFIED;
+ }
+ }
+
+ /**
+ * Find Log event type corresponding to app launch data.
+ * Returns {@code LOG_EVENT_UNSPECIFIED} if no matching event found
+ */
+ private static int getLogEventFromLaunchAppData(@Nullable AppLaunchData data) {
+ if (data == null) {
+ return LOG_EVENT_UNSPECIFIED;
+ }
+ if (data instanceof AppLaunchData.CategoryData) {
+ return getLogEventFromSelectorCategory(
+ ((AppLaunchData.CategoryData) data).getCategory());
+ } else if (data instanceof AppLaunchData.RoleData) {
+ return getLogEventFromRole(((AppLaunchData.RoleData) data).getRole());
+ } else if (data instanceof AppLaunchData.ComponentData) {
+ return FrameworkStatsLog.KEYBOARD_SYSTEMS_EVENT_REPORTED__KEYBOARD_SYSTEM_EVENT__LAUNCH_APPLICATION_BY_PACKAGE_NAME;
+ } else {
+ throw new IllegalArgumentException("AppLaunchData type is invalid!");
+ }
+ }
+
+ private static int getLogEventFromSelectorCategory(@NonNull String category) {
+ switch (category) {
+ case Intent.CATEGORY_APP_BROWSER:
+ return FrameworkStatsLog.KEYBOARD_SYSTEMS_EVENT_REPORTED__KEYBOARD_SYSTEM_EVENT__LAUNCH_DEFAULT_BROWSER;
+ case Intent.CATEGORY_APP_EMAIL:
+ return FrameworkStatsLog.KEYBOARD_SYSTEMS_EVENT_REPORTED__KEYBOARD_SYSTEM_EVENT__LAUNCH_DEFAULT_EMAIL;
+ case Intent.CATEGORY_APP_CONTACTS:
+ return FrameworkStatsLog.KEYBOARD_SYSTEMS_EVENT_REPORTED__KEYBOARD_SYSTEM_EVENT__LAUNCH_DEFAULT_CONTACTS;
+ case Intent.CATEGORY_APP_CALENDAR:
+ return FrameworkStatsLog.KEYBOARD_SYSTEMS_EVENT_REPORTED__KEYBOARD_SYSTEM_EVENT__LAUNCH_DEFAULT_CALENDAR;
+ case Intent.CATEGORY_APP_CALCULATOR:
+ return FrameworkStatsLog.KEYBOARD_SYSTEMS_EVENT_REPORTED__KEYBOARD_SYSTEM_EVENT__LAUNCH_DEFAULT_CALCULATOR;
+ case Intent.CATEGORY_APP_MUSIC:
+ return FrameworkStatsLog.KEYBOARD_SYSTEMS_EVENT_REPORTED__KEYBOARD_SYSTEM_EVENT__LAUNCH_DEFAULT_MUSIC;
+ case Intent.CATEGORY_APP_MAPS:
+ return FrameworkStatsLog.KEYBOARD_SYSTEMS_EVENT_REPORTED__KEYBOARD_SYSTEM_EVENT__LAUNCH_DEFAULT_MAPS;
+ case Intent.CATEGORY_APP_MESSAGING:
+ return FrameworkStatsLog.KEYBOARD_SYSTEMS_EVENT_REPORTED__KEYBOARD_SYSTEM_EVENT__LAUNCH_DEFAULT_MESSAGING;
+ case Intent.CATEGORY_APP_GALLERY:
+ return FrameworkStatsLog.KEYBOARD_SYSTEMS_EVENT_REPORTED__KEYBOARD_SYSTEM_EVENT__LAUNCH_DEFAULT_GALLERY;
+ case Intent.CATEGORY_APP_FILES:
+ return FrameworkStatsLog.KEYBOARD_SYSTEMS_EVENT_REPORTED__KEYBOARD_SYSTEM_EVENT__LAUNCH_DEFAULT_FILES;
+ case Intent.CATEGORY_APP_WEATHER:
+ return FrameworkStatsLog.KEYBOARD_SYSTEMS_EVENT_REPORTED__KEYBOARD_SYSTEM_EVENT__LAUNCH_DEFAULT_WEATHER;
+ case Intent.CATEGORY_APP_FITNESS:
+ return FrameworkStatsLog.KEYBOARD_SYSTEMS_EVENT_REPORTED__KEYBOARD_SYSTEM_EVENT__LAUNCH_DEFAULT_FITNESS;
+ default:
+ return LOG_EVENT_UNSPECIFIED;
+ }
+ }
+
+ /**
+ * Find Log event corresponding to the provide system role name.
+ * Returns {@code LOG_EVENT_UNSPECIFIED} if no matching event found.
+ */
+ private static int getLogEventFromRole(@NonNull String role) {
+ if (RoleManager.ROLE_BROWSER.equals(role)) {
+ return FrameworkStatsLog.KEYBOARD_SYSTEMS_EVENT_REPORTED__KEYBOARD_SYSTEM_EVENT__LAUNCH_DEFAULT_BROWSER;
+ } else if (RoleManager.ROLE_SMS.equals(role)) {
+ return FrameworkStatsLog.KEYBOARD_SYSTEMS_EVENT_REPORTED__KEYBOARD_SYSTEM_EVENT__LAUNCH_DEFAULT_MESSAGING;
+ } else {
+ return LOG_EVENT_UNSPECIFIED;
}
}
@@ -577,8 +709,8 @@ public final class KeyGestureEvent {
return "KEY_GESTURE_TYPE_LAUNCH_DEFAULT_WEATHER";
case KEY_GESTURE_TYPE_LAUNCH_DEFAULT_FITNESS:
return "KEY_GESTURE_TYPE_LAUNCH_DEFAULT_FITNESS";
- case KEY_GESTURE_TYPE_LAUNCH_APPLICATION_BY_PACKAGE_NAME:
- return "KEY_GESTURE_TYPE_LAUNCH_APPLICATION_BY_PACKAGE_NAME";
+ case KEY_GESTURE_TYPE_LAUNCH_APPLICATION:
+ return "KEY_GESTURE_TYPE_LAUNCH_APPLICATION";
case KEY_GESTURE_TYPE_DESKTOP_MODE:
return "KEY_GESTURE_TYPE_DESKTOP_MODE";
case KEY_GESTURE_TYPE_MULTI_WINDOW_NAVIGATION:
diff --git a/core/java/android/hardware/soundtrigger/SoundTrigger.java b/core/java/android/hardware/soundtrigger/SoundTrigger.java
index a1e7567faead..c6fd0ee3c80d 100644
--- a/core/java/android/hardware/soundtrigger/SoundTrigger.java
+++ b/core/java/android/hardware/soundtrigger/SoundTrigger.java
@@ -1627,7 +1627,7 @@ public class SoundTrigger {
boolean allowMultipleTriggers = in.readBoolean();
KeyphraseRecognitionExtra[] keyphrases =
in.createTypedArray(KeyphraseRecognitionExtra.CREATOR);
- byte[] data = in.readBlob();
+ byte[] data = in.createByteArray();
int audioCapabilities = in.readInt();
return new RecognitionConfig(captureRequested, allowMultipleTriggers, keyphrases, data,
audioCapabilities);
@@ -1638,7 +1638,7 @@ public class SoundTrigger {
dest.writeBoolean(mCaptureRequested);
dest.writeBoolean(mAllowMultipleTriggers);
dest.writeTypedArray(mKeyphrases, flags);
- dest.writeBlob(mData);
+ dest.writeByteArray(mData);
dest.writeInt(mAudioCapabilities);
}
diff --git a/core/java/android/os/BatteryUsageStatsQuery.java b/core/java/android/os/BatteryUsageStatsQuery.java
index e68c4ca5c070..6325b003a999 100644
--- a/core/java/android/os/BatteryUsageStatsQuery.java
+++ b/core/java/android/os/BatteryUsageStatsQuery.java
@@ -370,7 +370,10 @@ public final class BatteryUsageStatsQuery implements Parcelable {
* and most battery stats resets.
*/
public Builder accumulated() {
- mFlags |= FLAG_BATTERY_USAGE_STATS_ACCUMULATED;
+ mFlags |= FLAG_BATTERY_USAGE_STATS_ACCUMULATED
+ | FLAG_BATTERY_USAGE_STATS_INCLUDE_POWER_STATE
+ | FLAG_BATTERY_USAGE_STATS_INCLUDE_SCREEN_STATE
+ | FLAG_BATTERY_USAGE_STATS_INCLUDE_PROCESS_STATE_DATA;
return this;
}
diff --git a/core/java/android/os/Build.java b/core/java/android/os/Build.java
index a89483394611..13d7e3c2fbfd 100644
--- a/core/java/android/os/Build.java
+++ b/core/java/android/os/Build.java
@@ -1274,6 +1274,12 @@ public class Build {
* Vanilla Ice Cream.
*/
public static final int VANILLA_ICE_CREAM = 35;
+
+ /**
+ * Baklava.
+ */
+ @FlaggedApi(Flags.FLAG_MAJOR_MINOR_VERSIONING_SCHEME)
+ public static final int BAKLAVA = CUR_DEVELOPMENT;
}
/** @hide */
@@ -1313,6 +1319,7 @@ public class Build {
VERSION_CODES_FULL.TIRAMISU,
VERSION_CODES_FULL.UPSIDE_DOWN_CAKE,
VERSION_CODES_FULL.VANILLA_ICE_CREAM,
+ VERSION_CODES_FULL.BAKLAVA,
})
@Retention(RetentionPolicy.SOURCE)
public @interface SdkIntFull {}
@@ -1514,6 +1521,11 @@ public class Build {
*/
public static final int VANILLA_ICE_CREAM =
VERSION_CODES.VANILLA_ICE_CREAM * SDK_INT_MULTIPLIER;
+
+ /**
+ * The upcoming, not yet finalized, version of Android.
+ */
+ public static final int BAKLAVA = VERSION_CODES.BAKLAVA * SDK_INT_MULTIPLIER;
}
/**
diff --git a/core/java/android/provider/Settings.java b/core/java/android/provider/Settings.java
index 83c599e57de9..8efbc9c92675 100644
--- a/core/java/android/provider/Settings.java
+++ b/core/java/android/provider/Settings.java
@@ -11085,6 +11085,12 @@ public final class Settings {
public static final String SFPS_PERFORMANT_AUTH_ENABLED = "sfps_performant_auth_enabled_v2";
/**
+ * Whether or not the UDFPS device is enabling the screen off unlock settings.
+ * @hide
+ */
+ public static final String SCREEN_OFF_UNLOCK_UDFPS_ENABLED = "screen_off_udfps_enabled";
+
+ /**
* Whether or not debugging is enabled.
* @hide
*/
diff --git a/core/java/android/security/forensic/ForensicEvent.java b/core/java/android/security/forensic/ForensicEvent.java
index 9cbc5ecea962..90906edcc636 100644
--- a/core/java/android/security/forensic/ForensicEvent.java
+++ b/core/java/android/security/forensic/ForensicEvent.java
@@ -61,6 +61,14 @@ public final class ForensicEvent implements Parcelable {
in.readMap(mKeyValuePairs, getClass().getClassLoader(), String.class, String.class);
}
+ public String getType() {
+ return mType;
+ }
+
+ public Map<String, String> getKeyValuePairs() {
+ return mKeyValuePairs;
+ }
+
@Override
public void writeToParcel(@NonNull Parcel out, int flags) {
out.writeString(mType);
diff --git a/core/java/android/security/responsible_apis_flags.aconfig b/core/java/android/security/responsible_apis_flags.aconfig
index 9bb1039849e5..dec28c34ae5b 100644
--- a/core/java/android/security/responsible_apis_flags.aconfig
+++ b/core/java/android/security/responsible_apis_flags.aconfig
@@ -84,4 +84,13 @@ flag {
namespace: "responsible_apis"
description: "Prevent intent redirect attacks by aborting or throwing security exception"
bug: "361143368"
-} \ No newline at end of file
+}
+
+flag {
+ name: "enable_intent_matching_flags"
+ is_exported: true
+ namespace: "permissions"
+ is_fixed_read_only: true
+ description: "Applies intentMatchingFlags while matching intents to application components"
+ bug: "364354494"
+}
diff --git a/core/java/android/view/SurfaceControl.java b/core/java/android/view/SurfaceControl.java
index e6de478e3d3d..94f415b8680f 100644
--- a/core/java/android/view/SurfaceControl.java
+++ b/core/java/android/view/SurfaceControl.java
@@ -412,41 +412,28 @@ public final class SurfaceControl implements Parcelable {
*/
public static class JankData {
- /** @hide */
- @IntDef(flag = true, value = {JANK_NONE,
- DISPLAY_HAL,
- JANK_SURFACEFLINGER_DEADLINE_MISSED,
- JANK_SURFACEFLINGER_GPU_DEADLINE_MISSED,
- JANK_APP_DEADLINE_MISSED,
- PREDICTION_ERROR,
- SURFACE_FLINGER_SCHEDULING})
+ /**
+ * Needs to be kept in sync with android_view_SurfaceControl.cpp's
+ * JankDataListenerWrapper::onJankDataAvailable.
+ * @hide
+ */
+ @IntDef(flag = true, value = {
+ JANK_NONE,
+ JANK_COMPOSER,
+ JANK_APPLICATION,
+ JANK_OTHER,
+ })
@Retention(RetentionPolicy.SOURCE)
public @interface JankType {}
- // Needs to be kept in sync with frameworks/native/libs/gui/include/gui/JankInfo.h
-
// No Jank
- public static final int JANK_NONE = 0x0;
-
- // Jank not related to SurfaceFlinger or the App
- public static final int DISPLAY_HAL = 0x1;
- // SF took too long on the CPU
- public static final int JANK_SURFACEFLINGER_DEADLINE_MISSED = 0x2;
- // SF took too long on the GPU
- public static final int JANK_SURFACEFLINGER_GPU_DEADLINE_MISSED = 0x4;
- // Either App or GPU took too long on the frame
- public static final int JANK_APP_DEADLINE_MISSED = 0x8;
- // Vsync predictions have drifted beyond the threshold from the actual HWVsync
- public static final int PREDICTION_ERROR = 0x10;
- // Latching a buffer early might cause an early present of the frame
- public static final int SURFACE_FLINGER_SCHEDULING = 0x20;
- // A buffer is said to be stuffed if it was expected to be presented on a vsync but was
- // presented later because the previous buffer was presented in its expected vsync. This
- // usually happens if there is an unexpectedly long frame causing the rest of the buffers
- // to enter a stuffed state.
- public static final int BUFFER_STUFFING = 0x40;
- // Jank due to unknown reasons.
- public static final int UNKNOWN = 0x80;
+ public static final int JANK_NONE = 0;
+ // Jank caused by the composer missing a deadline
+ public static final int JANK_COMPOSER = 1 << 0;
+ // Jank caused by the application missing the composer's deadline
+ public static final int JANK_APPLICATION = 1 << 1;
+ // Jank due to other unknown reasons
+ public static final int JANK_OTHER = 1 << 2;
public JankData(long frameVsyncId, @JankType int jankType, long frameIntervalNs,
long scheduledAppFrameTimeNs, long actualAppFrameTimeNs) {
diff --git a/core/java/android/view/accessibility/AccessibilityEvent.java b/core/java/android/view/accessibility/AccessibilityEvent.java
index dfac244fcc0d..4e9d054dae07 100644
--- a/core/java/android/view/accessibility/AccessibilityEvent.java
+++ b/core/java/android/view/accessibility/AccessibilityEvent.java
@@ -798,6 +798,18 @@ public final class AccessibilityEvent extends AccessibilityRecord implements Par
@FlaggedApi(Flags.FLAG_TRI_STATE_CHECKED)
public static final int CONTENT_CHANGE_TYPE_CHECKED = 1 << 13;
+ /**
+ * Change type for {@link #TYPE_WINDOW_CONTENT_CHANGED} event: The source node changed its
+ * expanded state which is returned by {@link AccessibilityNodeInfo#getExpandedState()}. The
+ * view changing the node's expanded state should call {@link
+ * AccessibilityNodeInfo#setExpandedState(int)} and then send this event.
+ *
+ * @see AccessibilityNodeInfo#getExpandedState()
+ * @see AccessibilityNodeInfo#setExpandedState(int)
+ */
+ @FlaggedApi(Flags.FLAG_A11Y_EXPANSION_STATE_API)
+ public static final int CONTENT_CHANGE_TYPE_EXPANDED = 1 << 14;
+
// Speech state change types.
/** Change type for {@link #TYPE_SPEECH_STATE_CHANGE} event: another service is speaking. */
diff --git a/core/java/android/view/accessibility/AccessibilityNodeInfo.java b/core/java/android/view/accessibility/AccessibilityNodeInfo.java
index 60ccb77e1317..d3e7faa631d3 100644
--- a/core/java/android/view/accessibility/AccessibilityNodeInfo.java
+++ b/core/java/android/view/accessibility/AccessibilityNodeInfo.java
@@ -756,6 +756,56 @@ public class AccessibilityNodeInfo implements Parcelable {
public static final String ACTION_ARGUMENT_SCROLL_AMOUNT_FLOAT =
"android.view.accessibility.action.ARGUMENT_SCROLL_AMOUNT_FLOAT";
+ // Expanded state types.
+
+ /**
+ * Expanded state for a non-expandable element
+ *
+ * @see #getExpandedState()
+ * @see #setExpandedState(int)
+ */
+ @FlaggedApi(Flags.FLAG_A11Y_EXPANSION_STATE_API)
+ public static final int EXPANDED_STATE_UNDEFINED = 0;
+
+ /**
+ * Expanded state for a collapsed expandable element.
+ *
+ * @see #getExpandedState()
+ * @see #setExpandedState(int)
+ */
+ @FlaggedApi(Flags.FLAG_A11Y_EXPANSION_STATE_API)
+ public static final int EXPANDED_STATE_COLLAPSED = 1;
+
+ /**
+ * Expanded state for an expanded expandable element that can still be expanded further.
+ *
+ * @see #getExpandedState()
+ * @see #setExpandedState(int)
+ */
+ @FlaggedApi(Flags.FLAG_A11Y_EXPANSION_STATE_API)
+ public static final int EXPANDED_STATE_PARTIAL = 2;
+
+ /**
+ * Expanded state for a expanded expandable element that cannot be expanded further.
+ *
+ * @see #getExpandedState()
+ * @see #setExpandedState(int)
+ */
+ @FlaggedApi(Flags.FLAG_A11Y_EXPANSION_STATE_API)
+ public static final int EXPANDED_STATE_FULL = 3;
+
+ /** @hide */
+ @Retention(RetentionPolicy.SOURCE)
+ @IntDef(
+ prefix = "EXPANDED_STATE_",
+ value = {
+ EXPANDED_STATE_UNDEFINED,
+ EXPANDED_STATE_COLLAPSED,
+ EXPANDED_STATE_PARTIAL,
+ EXPANDED_STATE_FULL,
+ })
+ public @interface ExpandedState {}
+
// Focus types.
/**
@@ -1048,6 +1098,10 @@ public class AccessibilityNodeInfo implements Parcelable {
private int mMaxTextLength = -1;
private int mMovementGranularities;
+ // TODO(b/362782158) Initialize mExpandedState explicitly with
+ // the EXPANDED_STATE_UNDEFINED state when flagging is removed.
+ private int mExpandedState;
+
private int mTextSelectionStart = UNDEFINED_SELECTION_INDEX;
private int mTextSelectionEnd = UNDEFINED_SELECTION_INDEX;
private int mInputType = InputType.TYPE_NULL;
@@ -1931,6 +1985,47 @@ public class AccessibilityNodeInfo implements Parcelable {
}
/**
+ * Sets the expanded state of the node.
+ *
+ * <p><strong>Note:</strong> Cannot be called from an {@link
+ * android.accessibilityservice.AccessibilityService}. This class is made immutable before being
+ * delivered to an {@link android.accessibilityservice.AccessibilityService}.
+ *
+ * @param state new expanded state of this node.
+ * @throws IllegalArgumentException If state is not one of:
+ * <ul>
+ * <li>{@link #EXPANDED_STATE_UNDEFINED}
+ * <li>{@link #EXPANDED_STATE_COLLAPSED}
+ * <li>{@link #EXPANDED_STATE_PARTIAL}
+ * <li>{@link #EXPANDED_STATE_FULL}
+ * </ul>
+ *
+ * @throws IllegalStateException If called from an AccessibilityService
+ */
+ @FlaggedApi(Flags.FLAG_A11Y_EXPANSION_STATE_API)
+ public void setExpandedState(@ExpandedState int state) {
+ enforceValidExpandedState(state);
+ enforceNotSealed();
+ mExpandedState = state;
+ }
+
+ /**
+ * Gets the expanded state for this node.
+ *
+ * @return The expanded state, one of:
+ * <ul>
+ * <li>{@link #EXPANDED_STATE_UNDEFINED}
+ * <li>{@link #EXPANDED_STATE_COLLAPSED}
+ * <li>{@link #EXPANDED_STATE_FULL}
+ * <li>{@link #EXPANDED_STATE_PARTIAL}
+ * </ul>
+ */
+ @FlaggedApi(Flags.FLAG_A11Y_EXPANSION_STATE_API)
+ public @ExpandedState int getExpandedState() {
+ return mExpandedState;
+ }
+
+ /**
* Sets the minimum time duration between two content change events, which is used in throttling
* content change events in accessibility services.
*
@@ -4369,6 +4464,20 @@ public class AccessibilityNodeInfo implements Parcelable {
}
}
+ private void enforceValidExpandedState(int state) {
+ if (Flags.a11yExpansionStateApi()) {
+ switch (state) {
+ case EXPANDED_STATE_UNDEFINED:
+ case EXPANDED_STATE_COLLAPSED:
+ case EXPANDED_STATE_PARTIAL:
+ case EXPANDED_STATE_FULL:
+ return;
+ default:
+ throw new IllegalArgumentException("Unknown expanded state: " + state);
+ }
+ }
+ }
+
/**
* Enforces that this instance is not sealed.
*
@@ -4623,6 +4732,11 @@ public class AccessibilityNodeInfo implements Parcelable {
if (mChecked != DEFAULT.mChecked) {
nonDefaultFields |= bitAt(fieldIndex);
}
+ fieldIndex++;
+ if (mExpandedState != DEFAULT.mExpandedState) {
+ nonDefaultFields |= bitAt(fieldIndex);
+ }
+
int totalFields = fieldIndex;
parcel.writeLong(nonDefaultFields);
@@ -4794,6 +4908,9 @@ public class AccessibilityNodeInfo implements Parcelable {
if (isBitSet(nonDefaultFields, fieldIndex++)) {
parcel.writeInt(mChecked);
}
+ if (isBitSet(nonDefaultFields, fieldIndex++)) {
+ parcel.writeInt(mExpandedState);
+ }
if (DEBUG) {
fieldIndex--;
@@ -4883,6 +5000,7 @@ public class AccessibilityNodeInfo implements Parcelable {
mLeashedParent = other.mLeashedParent;
mLeashedParentNodeId = other.mLeashedParentNodeId;
mChecked = other.mChecked;
+ mExpandedState = other.mExpandedState;
}
private void initCopyInfos(AccessibilityNodeInfo other) {
@@ -5075,6 +5193,9 @@ public class AccessibilityNodeInfo implements Parcelable {
if (isBitSet(nonDefaultFields, fieldIndex++)) {
mChecked = parcel.readInt();
}
+ if (isBitSet(nonDefaultFields, fieldIndex++)) {
+ mExpandedState = parcel.readInt();
+ }
mSealed = sealed;
}
@@ -5249,6 +5370,26 @@ public class AccessibilityNodeInfo implements Parcelable {
}
}
+ private static String getExpandedStateSymbolicName(int state) {
+ if (Flags.a11yExpansionStateApi()) {
+ switch (state) {
+ case EXPANDED_STATE_UNDEFINED:
+ return "EXPANDED_STATE_UNDEFINED";
+ case EXPANDED_STATE_COLLAPSED:
+ return "EXPANDED_STATE_COLLAPSED";
+ case EXPANDED_STATE_PARTIAL:
+ return "EXPANDED_STATE_PARTIAL";
+ case EXPANDED_STATE_FULL:
+ return "EXPANDED_STATE_FULL";
+ default:
+ throw new IllegalArgumentException("Unknown expanded state: " + state);
+ }
+ } else {
+ // TODO(b/362782158) Remove when flag is removed.
+ return "";
+ }
+ }
+
private static boolean canPerformRequestOverConnection(int connectionId,
int windowId, long accessibilityNodeId) {
final boolean hasWindowId = windowId != AccessibilityWindowInfo.UNDEFINED_WINDOW_ID;
@@ -5346,6 +5487,7 @@ public class AccessibilityNodeInfo implements Parcelable {
builder.append("; containerTitle: ").append(mContainerTitle);
builder.append("; viewIdResName: ").append(mViewIdResourceName);
builder.append("; uniqueId: ").append(mUniqueId);
+ builder.append("; expandedState: ").append(getExpandedStateSymbolicName(mExpandedState));
builder.append("; checkable: ").append(isCheckable());
builder.append("; checked: ").append(isChecked());
diff --git a/core/java/android/window/BackEvent.java b/core/java/android/window/BackEvent.java
index 1b9235b21369..89b38d8048e5 100644
--- a/core/java/android/window/BackEvent.java
+++ b/core/java/android/window/BackEvent.java
@@ -23,7 +23,6 @@ import static com.android.window.flags.Flags.predictiveBackTimestampApi;
import android.annotation.FlaggedApi;
import android.annotation.FloatRange;
import android.annotation.IntDef;
-import android.util.TimeUtils;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
@@ -76,7 +75,7 @@ public final class BackEvent {
}
/**
- * Creates a new {@link BackEvent} instance with the current uptime as frame time.
+ * Creates a new {@link BackEvent} instance with a frame time of 0.
*
* @param touchX Absolute X location of the touch point of this event.
* @param touchY Absolute Y location of the touch point of this event.
@@ -88,7 +87,7 @@ public final class BackEvent {
mTouchY = touchY;
mProgress = progress;
mSwipeEdge = swipeEdge;
- mFrameTimeMillis = System.nanoTime() / TimeUtils.NANOS_PER_MS;
+ mFrameTimeMillis = 0;
}
/**
diff --git a/core/java/android/window/flags/lse_desktop_experience.aconfig b/core/java/android/window/flags/lse_desktop_experience.aconfig
index b805f5a2734b..c4a9e5722369 100644
--- a/core/java/android/window/flags/lse_desktop_experience.aconfig
+++ b/core/java/android/window/flags/lse_desktop_experience.aconfig
@@ -362,3 +362,10 @@ flag {
description: "Change the default display's windowing mode to freeform when display connected in extended mode."
bug: "374849026"
}
+
+flag {
+ name: "enable_desktop_windowing_pip"
+ namespace: "lse_desktop_experience"
+ description: "Enables PiP features in desktop mode."
+ bug: "350475854"
+}
diff --git a/core/java/com/android/internal/jank/FrameTracker.java b/core/java/com/android/internal/jank/FrameTracker.java
index 003393c337e7..0af4bea70e65 100644
--- a/core/java/com/android/internal/jank/FrameTracker.java
+++ b/core/java/com/android/internal/jank/FrameTracker.java
@@ -16,13 +16,9 @@
package com.android.internal.jank;
-import static android.view.SurfaceControl.JankData.DISPLAY_HAL;
-import static android.view.SurfaceControl.JankData.JANK_APP_DEADLINE_MISSED;
+import static android.view.SurfaceControl.JankData.JANK_APPLICATION;
+import static android.view.SurfaceControl.JankData.JANK_COMPOSER;
import static android.view.SurfaceControl.JankData.JANK_NONE;
-import static android.view.SurfaceControl.JankData.JANK_SURFACEFLINGER_DEADLINE_MISSED;
-import static android.view.SurfaceControl.JankData.JANK_SURFACEFLINGER_GPU_DEADLINE_MISSED;
-import static android.view.SurfaceControl.JankData.PREDICTION_ERROR;
-import static android.view.SurfaceControl.JankData.SURFACE_FLINGER_SCHEDULING;
import static com.android.internal.jank.DisplayRefreshRate.UNKNOWN_REFRESH_RATE;
import static com.android.internal.jank.DisplayRefreshRate.VARIABLE_REFRESH_RATE;
@@ -181,23 +177,11 @@ public class FrameTracker implements HardwareRendererObserver.OnFrameMetricsAvai
case JANK_NONE:
str.append("JANK_NONE");
break;
- case JANK_APP_DEADLINE_MISSED:
- str.append("JANK_APP_DEADLINE_MISSED");
+ case JANK_APPLICATION:
+ str.append("JANK_APPLICATION");
break;
- case JANK_SURFACEFLINGER_DEADLINE_MISSED:
- str.append("JANK_SURFACEFLINGER_DEADLINE_MISSED");
- break;
- case JANK_SURFACEFLINGER_GPU_DEADLINE_MISSED:
- str.append("JANK_SURFACEFLINGER_GPU_DEADLINE_MISSED");
- break;
- case DISPLAY_HAL:
- str.append("DISPLAY_HAL");
- break;
- case PREDICTION_ERROR:
- str.append("PREDICTION_ERROR");
- break;
- case SURFACE_FLINGER_SCHEDULING:
- str.append("SURFACE_FLINGER_SCHEDULING");
+ case JANK_COMPOSER:
+ str.append("JANK_COMPOSER");
break;
default:
str.append("UNKNOWN: ").append(jankType);
@@ -628,16 +612,12 @@ public class FrameTracker implements HardwareRendererObserver.OnFrameMetricsAvai
if (info.surfaceControlCallbackFired) {
totalFramesCount++;
boolean missedFrame = false;
- if ((info.jankType & JANK_APP_DEADLINE_MISSED) != 0) {
+ if ((info.jankType & JANK_APPLICATION) != 0) {
Log.w(TAG, "Missed App frame:" + info + ", CUJ=" + name);
missedAppFramesCount++;
missedFrame = true;
}
- if ((info.jankType & DISPLAY_HAL) != 0
- || (info.jankType & JANK_SURFACEFLINGER_DEADLINE_MISSED) != 0
- || (info.jankType & JANK_SURFACEFLINGER_GPU_DEADLINE_MISSED) != 0
- || (info.jankType & SURFACE_FLINGER_SCHEDULING) != 0
- || (info.jankType & PREDICTION_ERROR) != 0) {
+ if ((info.jankType & JANK_COMPOSER) != 0) {
Log.w(TAG, "Missed SF frame:" + info + ", CUJ=" + name);
missedSfFramesCount++;
missedFrame = true;
diff --git a/core/java/com/android/internal/os/ApplicationSharedMemory.java b/core/java/com/android/internal/os/ApplicationSharedMemory.java
index 84f713edcc1a..e6ea29e483f1 100644
--- a/core/java/com/android/internal/os/ApplicationSharedMemory.java
+++ b/core/java/com/android/internal/os/ApplicationSharedMemory.java
@@ -21,6 +21,7 @@ import android.util.Log;
import com.android.internal.annotations.VisibleForTesting;
import dalvik.annotation.optimization.CriticalNative;
+import dalvik.annotation.optimization.FastNative;
import libcore.io.IoUtils;
@@ -293,4 +294,34 @@ public class ApplicationSharedMemory implements AutoCloseable {
throw new IllegalStateException("Not mutable");
}
}
+
+ /**
+ * Return true if the memory has been mapped. This never throws.
+ */
+ public boolean isMapped() {
+ return mPtr != 0;
+ }
+
+ /**
+ * Return true if the memory is mapped and mutable. This never throws. Note that it returns
+ * false if the memory is not mapped.
+ */
+ public boolean isMutable() {
+ return isMapped() && mMutable;
+ }
+
+ /**
+ * Provide access to the nonce block needed by {@link PropertyInvalidatedCache}. This method
+ * returns 0 if the shared memory is not (yet) mapped.
+ */
+ public long getSystemNonceBlock() {
+ return isMapped() ? nativeGetSystemNonceBlock(mPtr) : 0;
+ }
+
+ /**
+ * Return a pointer to the system nonce cache in the shared memory region. The method is
+ * idempotent.
+ */
+ @FastNative
+ private static native long nativeGetSystemNonceBlock(long ptr);
}
diff --git a/core/java/com/android/internal/os/flags.aconfig b/core/java/com/android/internal/os/flags.aconfig
index 07df24843d9d..25a9fbc8476c 100644
--- a/core/java/com/android/internal/os/flags.aconfig
+++ b/core/java/com/android/internal/os/flags.aconfig
@@ -2,6 +2,48 @@ package: "com.android.internal.os"
container: "system"
flag {
+ namespace: "ravenwood"
+ name: "ravenwood_flag_rw_1"
+ description: "Ravenwood test RW flag 1"
+ bug: "311370221"
+ metadata {
+ purpose: PURPOSE_BUGFIX
+ }
+}
+
+flag {
+ namespace: "ravenwood"
+ name: "ravenwood_flag_rw_2"
+ description: "Ravenwood test RW flag 2"
+ bug: "311370221"
+ metadata {
+ purpose: PURPOSE_BUGFIX
+ }
+}
+
+flag {
+ namespace: "ravenwood"
+ name: "ravenwood_flag_ro_1"
+ description: "Ravenwood test RO flag 1"
+ is_fixed_read_only: true
+ bug: "311370221"
+ metadata {
+ purpose: PURPOSE_BUGFIX
+ }
+}
+
+flag {
+ namespace: "ravenwood"
+ name: "ravenwood_flag_ro_2"
+ description: "Ravenwood test RO flag 2"
+ is_fixed_read_only: true
+ bug: "311370221"
+ metadata {
+ purpose: PURPOSE_BUGFIX
+ }
+}
+
+flag {
name: "enable_apache_http_legacy_preload"
namespace: "system_performance"
description: "Enables zygote preload of non-BCP org.apache.http.legacy.jar library."
diff --git a/core/java/com/android/internal/widget/NotificationProgressBar.java b/core/java/com/android/internal/widget/NotificationProgressBar.java
index d3b1f972a955..f2b36c3b9981 100644
--- a/core/java/com/android/internal/widget/NotificationProgressBar.java
+++ b/core/java/com/android/internal/widget/NotificationProgressBar.java
@@ -21,6 +21,10 @@ import android.annotation.Nullable;
import android.app.Notification.ProgressStyle;
import android.content.Context;
import android.content.res.ColorStateList;
+import android.content.res.TypedArray;
+import android.graphics.Canvas;
+import android.graphics.Matrix;
+import android.graphics.Rect;
import android.graphics.drawable.Drawable;
import android.graphics.drawable.Icon;
import android.graphics.drawable.LayerDrawable;
@@ -52,7 +56,7 @@ import java.util.TreeSet;
* represent Notification ProgressStyle progress, such as for ridesharing and navigation.
*/
@RemoteViews.RemoteView
-public class NotificationProgressBar extends ProgressBar {
+public final class NotificationProgressBar extends ProgressBar {
private static final String TAG = "NotificationProgressBar";
private NotificationProgressModel mProgressModel;
@@ -61,7 +65,12 @@ public class NotificationProgressBar extends ProgressBar {
private List<Part> mProgressDrawableParts = null;
@Nullable
- private Drawable mProgressTrackerDrawable = null;
+ private Drawable mTracker = null;
+ private final int mTrackerHeight;
+ private int mTrackerWidth;
+ private int mTrackerPos;
+ private final Matrix mMatrix = new Matrix();
+ private Matrix mTrackerDrawMatrix = null;
public NotificationProgressBar(Context context) {
this(context, null);
@@ -78,28 +87,49 @@ public class NotificationProgressBar extends ProgressBar {
public NotificationProgressBar(Context context, AttributeSet attrs, int defStyleAttr,
int defStyleRes) {
super(context, attrs, defStyleAttr, defStyleRes);
+
+ final TypedArray a = context.obtainStyledAttributes(
+ attrs, R.styleable.NotificationProgressBar, defStyleAttr, defStyleRes);
+ saveAttributeDataForStyleable(context, R.styleable.NotificationProgressBar, attrs, a,
+ defStyleAttr,
+ defStyleRes);
+
+ // Supports setting the tracker in xml, but ProgressStyle notifications set/override it
+ // via {@code setProgressTrackerIcon}.
+ final Drawable tracker = a.getDrawable(R.styleable.NotificationProgressBar_tracker);
+ setTracker(tracker);
+
+ // If this is configured to be non-zero, will scale the tracker drawable and ensure its
+ // aspect ration is between 2:1 to 1:2.
+ mTrackerHeight = a.getDimensionPixelSize(R.styleable.NotificationProgressBar_trackerHeight,
+ 0);
}
/**
* Setter for the notification progress model.
*
* @see NotificationProgressModel#fromBundle
- * @see #setProgressModelAsync
*/
- @RemotableViewMethod(asyncImpl = "setProgressModelAsync")
+ @RemotableViewMethod
public void setProgressModel(@Nullable Bundle bundle) {
Preconditions.checkArgument(bundle != null,
"Bundle shouldn't be null");
mProgressModel = NotificationProgressModel.fromBundle(bundle);
+ final boolean isIndeterminate = mProgressModel.isIndeterminate();
+ setIndeterminate(isIndeterminate);
- if (mProgressModel.isIndeterminate()) {
+ if (isIndeterminate) {
final int indeterminateColor = mProgressModel.getIndeterminateColor();
setIndeterminateTintList(ColorStateList.valueOf(indeterminateColor));
} else {
+ final int progress = mProgressModel.getProgress();
+ final int progressMax = mProgressModel.getProgressMax();
mProgressDrawableParts = processAndConvertToDrawableParts(mProgressModel.getSegments(),
mProgressModel.getPoints(),
- mProgressModel.getProgress(), mProgressModel.isStyledByProgress());
+ progress,
+ progressMax,
+ mProgressModel.isStyledByProgress());
try {
final NotificationProgressDrawable drawable = getNotificationProgressDrawable();
@@ -107,6 +137,9 @@ public class NotificationProgressBar extends ProgressBar {
} catch (IllegalStateException ex) {
Log.e(TAG, "Can't set parts because can't get NotificationProgressDrawable", ex);
}
+
+ setMax(progressMax);
+ setProgress(progress);
}
}
@@ -137,6 +170,13 @@ public class NotificationProgressBar extends ProgressBar {
*/
@RemotableViewMethod(asyncImpl = "setProgressTrackerIconAsync")
public void setProgressTrackerIcon(@Nullable Icon icon) {
+ final Drawable progressTrackerDrawable;
+ if (icon != null) {
+ progressTrackerDrawable = icon.loadDrawable(getContext());
+ } else {
+ progressTrackerDrawable = null;
+ }
+ setTracker(progressTrackerDrawable);
}
/**
@@ -150,12 +190,295 @@ public class NotificationProgressBar extends ProgressBar {
progressTrackerDrawable = null;
}
return () -> {
- setProgressTrackerDrawable(progressTrackerDrawable);
+ setTracker(progressTrackerDrawable);
};
}
- private void setProgressTrackerDrawable(@Nullable Drawable drawable) {
- mProgressTrackerDrawable = drawable;
+ private void setTracker(@Nullable Drawable tracker) {
+ if (isIndeterminate() && tracker != null) {
+ return;
+ }
+
+ final boolean needUpdate = mTracker != null && tracker != mTracker;
+ if (needUpdate) {
+ mTracker.setCallback(null);
+ }
+
+ if (tracker != null) {
+ tracker.setCallback(this);
+ if (getMirrorForRtl()) {
+ tracker.setAutoMirrored(true);
+ }
+
+ if (canResolveLayoutDirection()) {
+ tracker.setLayoutDirection(getLayoutDirection());
+ }
+
+ // If we're updating get the new states
+ if (needUpdate && (tracker.getIntrinsicWidth() != mTracker.getIntrinsicWidth()
+ || tracker.getIntrinsicHeight() != mTracker.getIntrinsicHeight())) {
+ requestLayout();
+ }
+ }
+
+ mTracker = tracker;
+
+ configureTrackerBounds();
+
+ invalidate();
+
+ if (needUpdate) {
+ updateTrackerAndBarPos(getWidth(), getHeight());
+ if (tracker != null && tracker.isStateful()) {
+ // Note that if the states are different this won't work.
+ // For now, let's consider that an app bug.
+ tracker.setState(getDrawableState());
+ }
+ }
+ }
+
+ private void configureTrackerBounds() {
+ // Reset the tracker draw matrix to null
+ mTrackerDrawMatrix = null;
+
+ if (mTracker == null || mTrackerHeight <= 0) {
+ return;
+ }
+
+ final int dWidth = mTracker.getIntrinsicWidth();
+ final int dHeight = mTracker.getIntrinsicHeight();
+ if (dWidth <= 0 || dHeight <= 0) {
+ return;
+ }
+ final int maxDWidth = dHeight * 2;
+ final int maxDHeight = dWidth * 2;
+
+ mTrackerDrawMatrix = mMatrix;
+ float scale;
+ float dx = 0, dy = 0;
+
+ if (dWidth > maxDWidth) {
+ scale = (float) mTrackerHeight / (float) dHeight;
+ dx = (maxDWidth * scale - dWidth * scale) * 0.5f;
+ mTrackerWidth = (int) (maxDWidth * scale);
+ } else if (dHeight > maxDHeight) {
+ scale = (float) mTrackerHeight * 0.5f / (float) dWidth;
+ dy = (maxDHeight * scale - dHeight * scale) * 0.5f;
+ mTrackerWidth = mTrackerHeight / 2;
+ } else {
+ scale = (float) mTrackerHeight / (float) dHeight;
+ mTrackerWidth = (int) (dWidth * scale);
+ }
+
+ mTrackerDrawMatrix.setScale(scale, scale);
+ mTrackerDrawMatrix.postTranslate(Math.round(dx), Math.round(dy));
+ }
+
+ @Override
+ @RemotableViewMethod
+ public synchronized void setIndeterminate(boolean indeterminate) {
+ super.setIndeterminate(indeterminate);
+
+ if (isIndeterminate()) {
+ setTracker(null);
+ }
+ }
+
+ @Override
+ protected boolean verifyDrawable(@NonNull Drawable who) {
+ return who == mTracker || super.verifyDrawable(who);
+ }
+
+ @Override
+ public void jumpDrawablesToCurrentState() {
+ super.jumpDrawablesToCurrentState();
+
+ if (mTracker != null) {
+ mTracker.jumpToCurrentState();
+ }
+ }
+
+ @Override
+ protected void drawableStateChanged() {
+ super.drawableStateChanged();
+
+ final Drawable tracker = mTracker;
+ if (tracker != null && tracker.isStateful()
+ && tracker.setState(getDrawableState())) {
+ invalidateDrawable(tracker);
+ }
+ }
+
+ @Override
+ public void drawableHotspotChanged(float x, float y) {
+ super.drawableHotspotChanged(x, y);
+
+ if (mTracker != null) {
+ mTracker.setHotspot(x, y);
+ }
+ }
+
+ @Override
+ protected void onSizeChanged(int w, int h, int oldw, int oldh) {
+ super.onSizeChanged(w, h, oldw, oldh);
+
+ updateTrackerAndBarPos(w, h);
+ }
+
+ private void updateTrackerAndBarPos(int w, int h) {
+ final int paddedHeight = h - mPaddingTop - mPaddingBottom;
+ final Drawable bar = getCurrentDrawable();
+ final Drawable tracker = mTracker;
+
+ // The max height does not incorporate padding, whereas the height
+ // parameter does.
+ final int barHeight = Math.min(getMaxHeight(), paddedHeight);
+ final int trackerHeight = tracker == null ? 0
+ : ((mTrackerHeight == 0) ? tracker.getIntrinsicHeight() : mTrackerHeight);
+
+ // Apply offset to whichever item is taller.
+ final int barOffsetY;
+ final int trackerOffsetY;
+ if (trackerHeight > barHeight) {
+ final int offsetHeight = (paddedHeight - trackerHeight) / 2;
+ barOffsetY = offsetHeight + (trackerHeight - barHeight) / 2;
+ trackerOffsetY = offsetHeight;
+ } else {
+ final int offsetHeight = (paddedHeight - barHeight) / 2;
+ barOffsetY = offsetHeight;
+ trackerOffsetY = offsetHeight + (barHeight - trackerHeight) / 2;
+ }
+
+ if (bar != null) {
+ final int barWidth = w - mPaddingRight - mPaddingLeft;
+ bar.setBounds(0, barOffsetY, barWidth, barOffsetY + barHeight);
+ }
+
+ if (tracker != null) {
+ setTrackerPos(w, tracker, getScale(), trackerOffsetY);
+ }
+ }
+
+ private float getScale() {
+ int min = getMin();
+ int max = getMax();
+ int range = max - min;
+ return range > 0 ? (getProgress() - min) / (float) range : 0;
+ }
+
+ /**
+ * Updates the tracker drawable bounds.
+ *
+ * @param w Width of the view, including padding
+ * @param tracker Drawable used for the tracker
+ * @param scale Current progress between 0 and 1
+ * @param offsetY Vertical offset for centering. If set to
+ * {@link Integer#MIN_VALUE}, the current offset will be used.
+ */
+ private void setTrackerPos(int w, Drawable tracker, float scale, int offsetY) {
+ int available = w - mPaddingLeft - mPaddingRight;
+ final int trackerWidth = tracker.getIntrinsicWidth();
+ final int trackerHeight = tracker.getIntrinsicHeight();
+ available -= ((mTrackerHeight == 0) ? trackerWidth : mTrackerWidth);
+
+ final int trackerPos = (int) (scale * available + 0.5f);
+
+ final int top, bottom;
+ if (offsetY == Integer.MIN_VALUE) {
+ final Rect oldBounds = tracker.getBounds();
+ top = oldBounds.top;
+ bottom = oldBounds.bottom;
+ } else {
+ top = offsetY;
+ bottom = offsetY + trackerHeight;
+ }
+
+ mTrackerPos = (isLayoutRtl() && getMirrorForRtl()) ? available - trackerPos : trackerPos;
+ final int left = 0;
+ final int right = left + trackerWidth;
+
+ final Drawable background = getBackground();
+ if (background != null) {
+ final int bkgOffsetX = mPaddingLeft;
+ final int bkgOffsetY = mPaddingTop;
+ background.setHotspotBounds(left + bkgOffsetX, top + bkgOffsetY,
+ right + bkgOffsetX, bottom + bkgOffsetY);
+ }
+
+ // Canvas will be translated, so 0,0 is where we start drawing
+ tracker.setBounds(left, top, right, bottom);
+ }
+
+ @Override
+ public void onResolveDrawables(int layoutDirection) {
+ super.onResolveDrawables(layoutDirection);
+
+ if (mTracker != null) {
+ mTracker.setLayoutDirection(layoutDirection);
+ }
+ }
+
+ @Override
+ protected synchronized void onDraw(Canvas canvas) {
+ super.onDraw(canvas);
+ drawTracker(canvas);
+ }
+
+ /**
+ * Draw the tracker.
+ */
+ private void drawTracker(Canvas canvas) {
+ if (mTracker != null) {
+ final int saveCount = canvas.save();
+ // Translate the canvas origin to tracker position to make the draw matrix and the RtL
+ // transformations work.
+ canvas.translate(mPaddingLeft + mTrackerPos, mPaddingTop);
+ canvas.clipRect(0, 0, mTrackerWidth, mTrackerHeight);
+ if (mTrackerDrawMatrix != null) {
+ canvas.concat(mTrackerDrawMatrix);
+ }
+ mTracker.draw(canvas);
+ canvas.restoreToCount(saveCount);
+ }
+ }
+
+ @Override
+ protected synchronized void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
+ Drawable d = getCurrentDrawable();
+
+ int trackerHeight = mTracker == null ? 0 : mTracker.getIntrinsicHeight();
+ int dw = 0;
+ int dh = 0;
+ if (d != null) {
+ dw = Math.max(getMinWidth(), Math.min(getMaxWidth(), d.getIntrinsicWidth()));
+ dh = Math.max(getMinHeight(), Math.min(getMaxHeight(), d.getIntrinsicHeight()));
+ dh = Math.max(trackerHeight, dh);
+ }
+ dw += mPaddingLeft + mPaddingRight;
+ dh += mPaddingTop + mPaddingBottom;
+
+ setMeasuredDimension(resolveSizeAndState(dw, widthMeasureSpec, 0),
+ resolveSizeAndState(dh, heightMeasureSpec, 0));
+ }
+
+ @Override
+ public CharSequence getAccessibilityClassName() {
+ return NotificationProgressBar.class.getName();
+ }
+
+ @Override
+ public void onRtlPropertiesChanged(int layoutDirection) {
+ super.onRtlPropertiesChanged(layoutDirection);
+
+ final Drawable tracker = mTracker;
+ if (tracker != null) {
+ setTrackerPos(getWidth(), tracker, getScale(), Integer.MIN_VALUE);
+
+ // Since we draw translated, the drawable's bounds that it signals
+ // for invalidation won't be the actual bounds we want invalidated,
+ // so just invalidate this whole view.
+ invalidate();
+ }
}
/**
@@ -167,12 +490,18 @@ public class NotificationProgressBar extends ProgressBar {
List<ProgressStyle.Segment> segments,
List<ProgressStyle.Point> points,
int progress,
+ int progressMax,
boolean isStyledByProgress
) {
if (segments.isEmpty()) {
throw new IllegalArgumentException("List of segments shouldn't be empty");
}
+ final int totalLength = segments.stream().mapToInt(ProgressStyle.Segment::getLength).sum();
+ if (progressMax != totalLength) {
+ throw new IllegalArgumentException("Invalid progressMax : " + progressMax);
+ }
+
for (ProgressStyle.Segment segment : segments) {
final int length = segment.getLength();
if (length <= 0) {
@@ -180,14 +509,12 @@ public class NotificationProgressBar extends ProgressBar {
}
}
- final int progressMax = segments.stream().mapToInt(ProgressStyle.Segment::getLength).sum();
-
if (progress < 0 || progress > progressMax) {
throw new IllegalArgumentException("Invalid progress : " + progress);
}
for (ProgressStyle.Point point : points) {
final int pos = point.getPosition();
- if (pos <= 0 || pos >= progressMax) {
+ if (pos < 0 || pos > progressMax) {
throw new IllegalArgumentException("Invalid Point position : " + pos);
}
}
@@ -208,7 +535,6 @@ public class NotificationProgressBar extends ProgressBar {
isStyledByProgress);
}
-
// Any segment with a point on it gets split by the point.
// If isStyledByProgress is true, also split the segment with the progress value in its range.
private static Map<Integer, ProgressStyle.Segment> splitSegmentsByPointsAndProgress(
diff --git a/core/java/com/android/internal/widget/NotificationProgressModel.java b/core/java/com/android/internal/widget/NotificationProgressModel.java
index e51ea99ac4f5..e8cb37e8f19b 100644
--- a/core/java/com/android/internal/widget/NotificationProgressModel.java
+++ b/core/java/com/android/internal/widget/NotificationProgressModel.java
@@ -96,6 +96,10 @@ public final class NotificationProgressModel {
return mProgress;
}
+ public int getProgressMax() {
+ return mSegments.stream().mapToInt(Notification.ProgressStyle.Segment::getLength).sum();
+ }
+
public boolean isStyledByProgress() {
return mIsStyledByProgress;
}
diff --git a/core/java/com/android/internal/widget/remotecompose/core/CompanionOperation.java b/core/java/com/android/internal/widget/remotecompose/core/CompanionOperation.java
index deae9a571e12..244bb3d6380b 100644
--- a/core/java/com/android/internal/widget/remotecompose/core/CompanionOperation.java
+++ b/core/java/com/android/internal/widget/remotecompose/core/CompanionOperation.java
@@ -17,15 +17,13 @@ package com.android.internal.widget.remotecompose.core;
import java.util.List;
-/**
- * Interface for the companion operations
- */
+/** Interface for the companion operations */
public interface CompanionOperation {
/**
* Read, create and add instance to operations
+ *
* @param buffer data to read to create operation
* @param operations command is to be added
*/
void read(WireBuffer buffer, List<Operation> operations);
}
-
diff --git a/core/java/com/android/internal/widget/remotecompose/core/CoreDocument.java b/core/java/com/android/internal/widget/remotecompose/core/CoreDocument.java
index 18be4809998c..212df02b1bd3 100644
--- a/core/java/com/android/internal/widget/remotecompose/core/CoreDocument.java
+++ b/core/java/com/android/internal/widget/remotecompose/core/CoreDocument.java
@@ -16,6 +16,7 @@
package com.android.internal.widget.remotecompose.core;
import com.android.internal.widget.remotecompose.core.operations.ComponentValue;
+import com.android.internal.widget.remotecompose.core.operations.IntegerExpression;
import com.android.internal.widget.remotecompose.core.operations.NamedVariable;
import com.android.internal.widget.remotecompose.core.operations.RootContentBehavior;
import com.android.internal.widget.remotecompose.core.operations.Theme;
@@ -25,6 +26,8 @@ import com.android.internal.widget.remotecompose.core.operations.layout.Componen
import com.android.internal.widget.remotecompose.core.operations.layout.ComponentEnd;
import com.android.internal.widget.remotecompose.core.operations.layout.ComponentStartOperation;
import com.android.internal.widget.remotecompose.core.operations.layout.LayoutComponent;
+import com.android.internal.widget.remotecompose.core.operations.layout.LoopEnd;
+import com.android.internal.widget.remotecompose.core.operations.layout.LoopOperation;
import com.android.internal.widget.remotecompose.core.operations.layout.RootLayoutComponent;
import com.android.internal.widget.remotecompose.core.operations.layout.modifiers.ComponentModifiers;
import com.android.internal.widget.remotecompose.core.operations.layout.modifiers.ModifierOperation;
@@ -33,11 +36,12 @@ import com.android.internal.widget.remotecompose.core.operations.utilities.Strin
import java.util.ArrayList;
import java.util.HashMap;
import java.util.HashSet;
+import java.util.List;
import java.util.Set;
/**
- * Represents a platform independent RemoteCompose document,
- * containing RemoteCompose operations + state
+ * Represents a platform independent RemoteCompose document, containing RemoteCompose operations +
+ * state
*/
public class CoreDocument {
@@ -66,6 +70,8 @@ public class CoreDocument {
RemoteComposeBuffer mBuffer = new RemoteComposeBuffer(mRemoteComposeState);
+ private final HashMap<Long, IntegerExpression> mIntegerExpressions = new HashMap<>();
+
private int mLastId = 1; // last component id when inflating the file
public String getContentDescription() {
@@ -133,21 +139,14 @@ public class CoreDocument {
/**
* Sets the way the player handles the content
*
- * @param scroll set the horizontal behavior (NONE|SCROLL_HORIZONTAL|SCROLL_VERTICAL)
+ * @param scroll set the horizontal behavior (NONE|SCROLL_HORIZONTAL|SCROLL_VERTICAL)
* @param alignment set the alignment of the content (TOP|CENTER|BOTTOM|START|END)
- * @param sizing set the type of sizing for the content (NONE|SIZING_LAYOUT|SIZING_SCALE)
- * @param mode set the mode of sizing, either LAYOUT modes or SCALE modes
- * the LAYOUT modes are:
- * - LAYOUT_MATCH_PARENT
- * - LAYOUT_WRAP_CONTENT
- * or adding an horizontal mode and a vertical mode:
- * - LAYOUT_HORIZONTAL_MATCH_PARENT
- * - LAYOUT_HORIZONTAL_WRAP_CONTENT
- * - LAYOUT_HORIZONTAL_FIXED
- * - LAYOUT_VERTICAL_MATCH_PARENT
- * - LAYOUT_VERTICAL_WRAP_CONTENT
- * - LAYOUT_VERTICAL_FIXED
- * The LAYOUT_*_FIXED modes will use the intrinsic document size
+ * @param sizing set the type of sizing for the content (NONE|SIZING_LAYOUT|SIZING_SCALE)
+ * @param mode set the mode of sizing, either LAYOUT modes or SCALE modes the LAYOUT modes are:
+ * - LAYOUT_MATCH_PARENT - LAYOUT_WRAP_CONTENT or adding an horizontal mode and a vertical
+ * mode: - LAYOUT_HORIZONTAL_MATCH_PARENT - LAYOUT_HORIZONTAL_WRAP_CONTENT -
+ * LAYOUT_HORIZONTAL_FIXED - LAYOUT_VERTICAL_MATCH_PARENT - LAYOUT_VERTICAL_WRAP_CONTENT -
+ * LAYOUT_VERTICAL_FIXED The LAYOUT_*_FIXED modes will use the intrinsic document size
*/
public void setRootContentBehavior(int scroll, int alignment, int sizing, int mode) {
this.mContentScroll = scroll;
@@ -160,8 +159,8 @@ public class CoreDocument {
* Given dimensions w x h of where to paint the content, returns the corresponding scale factor
* according to the contentSizing information
*
- * @param w horizontal dimension of the rendering area
- * @param h vertical dimension of the rendering area
+ * @param w horizontal dimension of the rendering area
+ * @param h vertical dimension of the rendering area
* @param scaleOutput will contain the computed scale factor
*/
public void computeScale(float w, float h, float[] scaleOutput) {
@@ -169,50 +168,47 @@ public class CoreDocument {
float contentScaleY = 1f;
if (mContentSizing == RootContentBehavior.SIZING_SCALE) {
// we need to add canvas transforms ops here
+ float scaleX = 1f;
+ float scaleY = 1f;
+ float scale = 1f;
switch (mContentMode) {
- case RootContentBehavior.SCALE_INSIDE: {
- float scaleX = w / mWidth;
- float scaleY = h / mHeight;
- float scale = Math.min(1f, Math.min(scaleX, scaleY));
+ case RootContentBehavior.SCALE_INSIDE:
+ scaleX = w / mWidth;
+ scaleY = h / mHeight;
+ scale = Math.min(1f, Math.min(scaleX, scaleY));
contentScaleX = scale;
contentScaleY = scale;
- }
- break;
- case RootContentBehavior.SCALE_FIT: {
- float scaleX = w / mWidth;
- float scaleY = h / mHeight;
- float scale = Math.min(scaleX, scaleY);
+ break;
+ case RootContentBehavior.SCALE_FIT:
+ scaleX = w / mWidth;
+ scaleY = h / mHeight;
+ scale = Math.min(scaleX, scaleY);
contentScaleX = scale;
contentScaleY = scale;
- }
- break;
- case RootContentBehavior.SCALE_FILL_WIDTH: {
- float scale = w / mWidth;
+ break;
+ case RootContentBehavior.SCALE_FILL_WIDTH:
+ scale = w / mWidth;
contentScaleX = scale;
contentScaleY = scale;
- }
- break;
- case RootContentBehavior.SCALE_FILL_HEIGHT: {
- float scale = h / mHeight;
+ break;
+ case RootContentBehavior.SCALE_FILL_HEIGHT:
+ scale = h / mHeight;
contentScaleX = scale;
contentScaleY = scale;
- }
- break;
- case RootContentBehavior.SCALE_CROP: {
- float scaleX = w / mWidth;
- float scaleY = h / mHeight;
- float scale = Math.max(scaleX, scaleY);
+ break;
+ case RootContentBehavior.SCALE_CROP:
+ scaleX = w / mWidth;
+ scaleY = h / mHeight;
+ scale = Math.max(scaleX, scaleY);
contentScaleX = scale;
contentScaleY = scale;
- }
- break;
- case RootContentBehavior.SCALE_FILL_BOUNDS: {
- float scaleX = w / mWidth;
- float scaleY = h / mHeight;
+ break;
+ case RootContentBehavior.SCALE_FILL_BOUNDS:
+ scaleX = w / mWidth;
+ scaleY = h / mHeight;
contentScaleX = scaleX;
contentScaleY = scaleY;
- }
- break;
+ break;
default:
// nothing
}
@@ -225,14 +221,14 @@ public class CoreDocument {
* Given dimensions w x h of where to paint the content, returns the corresponding translation
* according to the contentAlignment information
*
- * @param w horizontal dimension of the rendering area
- * @param h vertical dimension of the rendering area
- * @param contentScaleX the horizontal scale we are going to use for the content
- * @param contentScaleY the vertical scale we are going to use for the content
+ * @param w horizontal dimension of the rendering area
+ * @param h vertical dimension of the rendering area
+ * @param contentScaleX the horizontal scale we are going to use for the content
+ * @param contentScaleY the vertical scale we are going to use for the content
* @param translateOutput will contain the computed translation
*/
- private void computeTranslate(float w, float h, float contentScaleX, float contentScaleY,
- float[] translateOutput) {
+ private void computeTranslate(
+ float w, float h, float contentScaleX, float contentScaleY, float[] translateOutput) {
int horizontalContentAlignment = mContentAlignment & 0xF0;
int verticalContentAlignment = mContentAlignment & 0xF;
float translateX = 0f;
@@ -241,34 +237,28 @@ public class CoreDocument {
float contentHeight = mHeight * contentScaleY;
switch (horizontalContentAlignment) {
- case RootContentBehavior.ALIGNMENT_START: {
+ case RootContentBehavior.ALIGNMENT_START:
// nothing
- }
- break;
- case RootContentBehavior.ALIGNMENT_HORIZONTAL_CENTER: {
+ break;
+ case RootContentBehavior.ALIGNMENT_HORIZONTAL_CENTER:
translateX = (w - contentWidth) / 2f;
- }
- break;
- case RootContentBehavior.ALIGNMENT_END: {
+ break;
+ case RootContentBehavior.ALIGNMENT_END:
translateX = w - contentWidth;
- }
- break;
+ break;
default:
// nothing (same as alignment_start)
}
switch (verticalContentAlignment) {
- case RootContentBehavior.ALIGNMENT_TOP: {
+ case RootContentBehavior.ALIGNMENT_TOP:
// nothing
- }
- break;
- case RootContentBehavior.ALIGNMENT_VERTICAL_CENTER: {
+ break;
+ case RootContentBehavior.ALIGNMENT_VERTICAL_CENTER:
translateY = (h - contentHeight) / 2f;
- }
- break;
- case RootContentBehavior.ALIGNMENT_BOTTOM: {
+ break;
+ case RootContentBehavior.ALIGNMENT_BOTTOM:
translateY = h - contentHeight;
- }
- break;
+ break;
default:
// nothing (same as alignment_top)
}
@@ -279,6 +269,7 @@ public class CoreDocument {
/**
* Returns the list of click areas
+ *
* @return list of click areas in document coordinates
*/
public Set<ClickAreaRepresentation> getClickAreas() {
@@ -287,15 +278,14 @@ public class CoreDocument {
/**
* Returns the root layout component
+ *
* @return returns the root component if it exists, null otherwise
*/
public RootLayoutComponent getRootLayoutComponent() {
return mRootLayoutComponent;
}
- /**
- * Invalidate the document for layout measures. This will trigger a layout remeasure pass.
- */
+ /** Invalidate the document for layout measures. This will trigger a layout remeasure pass. */
public void invalidateMeasure() {
if (mRootLayoutComponent != null) {
mRootLayoutComponent.invalidateMeasure();
@@ -304,6 +294,7 @@ public class CoreDocument {
/**
* Returns the component with the given id
+ *
* @param id component id
* @return the component if it exists, null otherwise
*/
@@ -332,8 +323,21 @@ public class CoreDocument {
}
/**
- * Callback interface for host actions
+ * Execute an integer expression with the given id and put its value on the targetId
+ *
+ * @param expressionId the id of the integer expression
+ * @param targetId the id of the value to update with the expression
+ * @param context the current context
*/
+ public void evaluateIntExpression(long expressionId, int targetId, RemoteContext context) {
+ IntegerExpression expression = mIntegerExpressions.get(expressionId);
+ if (expression != null) {
+ int v = expression.evaluate(context);
+ context.overrideInteger(targetId, v);
+ }
+ }
+
+ /** Callback interface for host actions */
public interface ActionCallback {
// TODO: add payload support
void onAction(String name);
@@ -343,6 +347,7 @@ public class CoreDocument {
/**
* Warn action listeners for the given named action
+ *
* @param name the action name
*/
public void runNamedAction(String name) {
@@ -360,9 +365,7 @@ public class CoreDocument {
mActionListeners.add(callback);
}
- /**
- * Clear existing callbacks for named host actions
- */
+ /** Clear existing callbacks for named host actions */
public void clearActionCallbacks() {
mActionListeners.clear();
}
@@ -395,13 +398,14 @@ public class CoreDocument {
float mBottom;
String mMetadata;
- public ClickAreaRepresentation(int id,
- String contentDescription,
- float left,
- float top,
- float right,
- float bottom,
- String metadata) {
+ public ClickAreaRepresentation(
+ int id,
+ String contentDescription,
+ float left,
+ float top,
+ float right,
+ float bottom,
+ String metadata) {
this.mId = id;
this.mContentDescription = contentDescription;
this.mLeft = left;
@@ -419,8 +423,7 @@ public class CoreDocument {
* @return x, y coordinate is within bounds
*/
public boolean contains(float x, float y) {
- return x >= mLeft && x < mRight
- && y >= mTop && y < mBottom;
+ return x >= mLeft && x < mRight && y >= mTop && y < mBottom;
}
public float getLeft() {
@@ -452,12 +455,16 @@ public class CoreDocument {
}
}
- /**
- * Load operations from the given buffer
- */
+ /** Load operations from the given buffer */
public void initFromBuffer(RemoteComposeBuffer buffer) {
mOperations = new ArrayList<Operation>();
buffer.inflateFromBuffer(mOperations);
+ for (Operation op : mOperations) {
+ if (op instanceof IntegerExpression) {
+ IntegerExpression expression = (IntegerExpression) op;
+ mIntegerExpressions.put((long) expression.mId, expression);
+ }
+ }
mOperations = inflateComponents(mOperations);
mBuffer = buffer;
for (Operation op : mOperations) {
@@ -473,6 +480,7 @@ public class CoreDocument {
/**
* Inflate a component tree
+ *
* @param operations flat list of operations
* @return nested list of operations / components
*/
@@ -482,6 +490,7 @@ public class CoreDocument {
ArrayList<Operation> finalOperationsList = new ArrayList<>();
ArrayList<Operation> ops = finalOperationsList;
ClickModifierOperation currentClickModifier = null;
+ LoopOperation currentLoop = null;
mLastId = -1;
for (Operation o : operations) {
@@ -509,11 +518,22 @@ public class CoreDocument {
} else if (o instanceof ClickModifierOperation) {
// TODO: refactor to add container <- component...
currentClickModifier = (ClickModifierOperation) o;
- ops = ((ClickModifierOperation) o).getList();
+ ops = currentClickModifier.getList();
} else if (o instanceof ClickModifierEnd) {
ops = currentComponent.getList();
ops.add(currentClickModifier);
currentClickModifier = null;
+ } else if (o instanceof LoopOperation) {
+ currentLoop = (LoopOperation) o;
+ ops = currentLoop.getList();
+ } else if (o instanceof LoopEnd) {
+ if (currentComponent != null) {
+ ops = currentComponent.getList();
+ ops.add(currentLoop);
+ } else {
+ ops = finalOperationsList;
+ }
+ currentLoop = null;
} else {
ops.add(o);
}
@@ -555,8 +575,8 @@ public class CoreDocument {
}
/**
- * Called when an initialization is needed, allowing the document to eg load
- * resources / cache them.
+ * Called when an initialization is needed, allowing the document to eg load resources / cache
+ * them.
*/
public void initializeContext(RemoteContext context) {
mRemoteComposeState.reset();
@@ -593,7 +613,7 @@ public class CoreDocument {
*
* @param majorVersion major version number, increased upon changes breaking the compatibility
* @param minorVersion minor version number, increased when adding new features
- * @param patch patch level, increased upon bugfixes
+ * @param patch patch level, increased upon bugfixes
*/
void setVersion(int majorVersion, int minorVersion, int patch) {
mVersion = new Version(majorVersion, minorVersion, patch);
@@ -605,22 +625,29 @@ public class CoreDocument {
/**
* Add a click area to the document, in root coordinates. We are not doing any specific sorting
- * through the declared areas on click detections, which means that the first one containing
- * the click coordinates will be the one reported; the order of addition of those click areas
- * is therefore meaningful.
+ * through the declared areas on click detections, which means that the first one containing the
+ * click coordinates will be the one reported; the order of addition of those click areas is
+ * therefore meaningful.
*
- * @param id the id of the area, which will be reported on click
+ * @param id the id of the area, which will be reported on click
* @param contentDescription the content description (used for accessibility)
- * @param left the left coordinate of the click area (in pixels)
- * @param top the top coordinate of the click area (in pixels)
- * @param right the right coordinate of the click area (in pixels)
- * @param bottom the bottom coordinate of the click area (in pixels)
- * @param metadata arbitrary metadata associated with the are, also reported on click
+ * @param left the left coordinate of the click area (in pixels)
+ * @param top the top coordinate of the click area (in pixels)
+ * @param right the right coordinate of the click area (in pixels)
+ * @param bottom the bottom coordinate of the click area (in pixels)
+ * @param metadata arbitrary metadata associated with the are, also reported on click
*/
- public void addClickArea(int id, String contentDescription,
- float left, float top, float right, float bottom, String metadata) {
- mClickAreas.add(new ClickAreaRepresentation(id,
- contentDescription, left, top, right, bottom, metadata));
+ public void addClickArea(
+ int id,
+ String contentDescription,
+ float left,
+ float top,
+ float right,
+ float bottom,
+ String metadata) {
+ mClickAreas.add(
+ new ClickAreaRepresentation(
+ id, contentDescription, left, top, right, bottom, metadata));
}
/**
@@ -672,9 +699,7 @@ public class CoreDocument {
}
}
- /**
- * Warn click listeners when a click area is activated
- */
+ /** Warn click listeners when a click area is activated */
private void warnClickListeners(ClickAreaRepresentation clickArea) {
for (ClickCallbacks listener : mClickListeners) {
listener.click(clickArea.mId, clickArea.mMetadata);
@@ -743,10 +768,9 @@ public class CoreDocument {
* Paint the document
*
* @param context the provided PaintContext
- * @param theme the theme we want to use for this document.
+ * @param theme the theme we want to use for this document.
*/
public void paint(RemoteContext context, int theme) {
- long time = System.nanoTime();
context.getPaintContext().clearNeedsRepaint();
context.mMode = RemoteContext.ContextMode.UNSET;
@@ -760,8 +784,12 @@ public class CoreDocument {
if (mContentSizing == RootContentBehavior.SIZING_SCALE) {
// we need to add canvas transforms ops here
computeScale(context.mWidth, context.mHeight, mScaleOutput);
- computeTranslate(context.mWidth, context.mHeight,
- mScaleOutput[0], mScaleOutput[1], mTranslateOutput);
+ computeTranslate(
+ context.mWidth,
+ context.mHeight,
+ mScaleOutput[0],
+ mScaleOutput[1],
+ mTranslateOutput);
context.mPaintContext.translate(mTranslateOutput[0], mTranslateOutput[1]);
context.mPaintContext.scale(mScaleOutput[0], mScaleOutput[1]);
}
@@ -795,9 +823,10 @@ public class CoreDocument {
// or the theme is equal as the one passed in argument to paint.
boolean apply = true;
if (theme != Theme.UNSPECIFIED) {
- apply = op instanceof Theme // always apply a theme setter
- || context.getTheme() == theme
- || context.getTheme() == Theme.UNSPECIFIED;
+ apply =
+ op instanceof Theme // always apply a theme setter
+ || context.getTheme() == theme
+ || context.getTheme() == Theme.UNSPECIFIED;
}
if (apply) {
op.apply(context);
@@ -808,7 +837,10 @@ public class CoreDocument {
mRepaintNext = 1;
}
context.mMode = RemoteContext.ContextMode.UNSET;
- // System.out.println(">> " + ( System.nanoTime() - time)*1E-6f+" ms");
+ // System.out.println(">> " + ( System.nanoTime() - time)*1E-6f+" ms");
+ if (DEBUG && mRootLayoutComponent != null) {
+ System.out.println(mRootLayoutComponent.displayHierarchy());
+ }
}
public String[] getStats() {
@@ -831,7 +863,6 @@ public class CoreDocument {
if (mOperation instanceof Component) {
Component com = (Component) mOperation;
count += addChildren(com, map, buffer);
-
}
}
@@ -893,5 +924,8 @@ public class CoreDocument {
}
}
}
-}
+ public List<Operation> getOperations() {
+ return mOperations;
+ }
+}
diff --git a/core/java/com/android/internal/widget/remotecompose/core/DocumentedCompanion.java b/core/java/com/android/internal/widget/remotecompose/core/DocumentedCompanion.java
index 661cf7cf4491..8a792a43f047 100644
--- a/core/java/com/android/internal/widget/remotecompose/core/DocumentedCompanion.java
+++ b/core/java/com/android/internal/widget/remotecompose/core/DocumentedCompanion.java
@@ -17,6 +17,4 @@ package com.android.internal.widget.remotecompose.core;
import com.android.internal.widget.remotecompose.core.documentation.DocumentedCompanionOperation;
-public interface DocumentedCompanion extends CompanionOperation , DocumentedCompanionOperation {
-
-}
+public interface DocumentedCompanion extends CompanionOperation, DocumentedCompanionOperation {}
diff --git a/core/java/com/android/internal/widget/remotecompose/core/Operation.java b/core/java/com/android/internal/widget/remotecompose/core/Operation.java
index 4a8b3d7a76d4..9f565a2915fb 100644
--- a/core/java/com/android/internal/widget/remotecompose/core/Operation.java
+++ b/core/java/com/android/internal/widget/remotecompose/core/Operation.java
@@ -15,14 +15,10 @@
*/
package com.android.internal.widget.remotecompose.core;
-/**
- * Base interface for RemoteCompose operations
- */
+/** Base interface for RemoteCompose operations */
public interface Operation {
- /**
- * add the operation to the buffer
- */
+ /** add the operation to the buffer */
void write(WireBuffer buffer);
/**
@@ -32,8 +28,6 @@ public interface Operation {
*/
void apply(RemoteContext context);
- /**
- * Debug utility to display an operation + indentation
- */
+ /** Debug utility to display an operation + indentation */
String deepToString(String indent);
}
diff --git a/core/java/com/android/internal/widget/remotecompose/core/Operations.java b/core/java/com/android/internal/widget/remotecompose/core/Operations.java
index 4a25b5eff3c9..acebe0761b79 100644
--- a/core/java/com/android/internal/widget/remotecompose/core/Operations.java
+++ b/core/java/com/android/internal/widget/remotecompose/core/Operations.java
@@ -25,15 +25,18 @@ import com.android.internal.widget.remotecompose.core.operations.ComponentValue;
import com.android.internal.widget.remotecompose.core.operations.DataListFloat;
import com.android.internal.widget.remotecompose.core.operations.DataListIds;
import com.android.internal.widget.remotecompose.core.operations.DataMapIds;
+import com.android.internal.widget.remotecompose.core.operations.DataMapLookup;
import com.android.internal.widget.remotecompose.core.operations.DrawArc;
import com.android.internal.widget.remotecompose.core.operations.DrawBitmap;
import com.android.internal.widget.remotecompose.core.operations.DrawBitmapInt;
+import com.android.internal.widget.remotecompose.core.operations.DrawBitmapScaled;
import com.android.internal.widget.remotecompose.core.operations.DrawCircle;
import com.android.internal.widget.remotecompose.core.operations.DrawLine;
import com.android.internal.widget.remotecompose.core.operations.DrawOval;
import com.android.internal.widget.remotecompose.core.operations.DrawPath;
import com.android.internal.widget.remotecompose.core.operations.DrawRect;
import com.android.internal.widget.remotecompose.core.operations.DrawRoundRect;
+import com.android.internal.widget.remotecompose.core.operations.DrawSector;
import com.android.internal.widget.remotecompose.core.operations.DrawText;
import com.android.internal.widget.remotecompose.core.operations.DrawTextAnchored;
import com.android.internal.widget.remotecompose.core.operations.DrawTextOnPath;
@@ -56,6 +59,10 @@ import com.android.internal.widget.remotecompose.core.operations.RootContentDesc
import com.android.internal.widget.remotecompose.core.operations.ShaderData;
import com.android.internal.widget.remotecompose.core.operations.TextData;
import com.android.internal.widget.remotecompose.core.operations.TextFromFloat;
+import com.android.internal.widget.remotecompose.core.operations.TextLength;
+import com.android.internal.widget.remotecompose.core.operations.TextLookup;
+import com.android.internal.widget.remotecompose.core.operations.TextLookupInt;
+import com.android.internal.widget.remotecompose.core.operations.TextMeasure;
import com.android.internal.widget.remotecompose.core.operations.TextMerge;
import com.android.internal.widget.remotecompose.core.operations.Theme;
import com.android.internal.widget.remotecompose.core.operations.layout.CanvasContent;
@@ -64,12 +71,15 @@ import com.android.internal.widget.remotecompose.core.operations.layout.ClickMod
import com.android.internal.widget.remotecompose.core.operations.layout.ComponentEnd;
import com.android.internal.widget.remotecompose.core.operations.layout.ComponentStart;
import com.android.internal.widget.remotecompose.core.operations.layout.LayoutComponentContent;
+import com.android.internal.widget.remotecompose.core.operations.layout.LoopEnd;
+import com.android.internal.widget.remotecompose.core.operations.layout.LoopOperation;
import com.android.internal.widget.remotecompose.core.operations.layout.RootLayoutComponent;
import com.android.internal.widget.remotecompose.core.operations.layout.animation.AnimationSpec;
import com.android.internal.widget.remotecompose.core.operations.layout.managers.BoxLayout;
import com.android.internal.widget.remotecompose.core.operations.layout.managers.CanvasLayout;
import com.android.internal.widget.remotecompose.core.operations.layout.managers.ColumnLayout;
import com.android.internal.widget.remotecompose.core.operations.layout.managers.RowLayout;
+import com.android.internal.widget.remotecompose.core.operations.layout.managers.StateLayout;
import com.android.internal.widget.remotecompose.core.operations.layout.managers.TextLayout;
import com.android.internal.widget.remotecompose.core.operations.layout.modifiers.BackgroundModifierOperation;
import com.android.internal.widget.remotecompose.core.operations.layout.modifiers.BorderModifierOperation;
@@ -81,6 +91,7 @@ import com.android.internal.widget.remotecompose.core.operations.layout.modifier
import com.android.internal.widget.remotecompose.core.operations.layout.modifiers.PaddingModifierOperation;
import com.android.internal.widget.remotecompose.core.operations.layout.modifiers.RoundedClipRectModifierOperation;
import com.android.internal.widget.remotecompose.core.operations.layout.modifiers.ValueIntegerChangeActionOperation;
+import com.android.internal.widget.remotecompose.core.operations.layout.modifiers.ValueIntegerExpressionChangeActionOperation;
import com.android.internal.widget.remotecompose.core.operations.layout.modifiers.ValueStringChangeActionOperation;
import com.android.internal.widget.remotecompose.core.operations.layout.modifiers.WidthModifierOperation;
import com.android.internal.widget.remotecompose.core.operations.utilities.IntMap;
@@ -88,9 +99,7 @@ import com.android.internal.widget.remotecompose.core.types.BooleanConstant;
import com.android.internal.widget.remotecompose.core.types.IntegerConstant;
import com.android.internal.widget.remotecompose.core.types.LongConstant;
-/**
- * List of operations supported in a RemoteCompose document
- */
+/** List of operations supported in a RemoteCompose document */
public class Operations {
////////////////////////////////////////
@@ -112,7 +121,7 @@ public class Operations {
public static final int DATA_SHADER = 45;
public static final int DATA_TEXT = 102;
- /////////////////////////////=====================
+ ///////////////////////////// =====================
public static final int CLIP_PATH = 38;
public static final int CLIP_RECT = 39;
public static final int PAINT_VALUES = 40;
@@ -121,7 +130,7 @@ public class Operations {
public static final int DRAW_CIRCLE = 46;
public static final int DRAW_LINE = 47;
public static final int DRAW_ROUND_RECT = 51;
- public static final int DRAW_ARC = 52;
+ public static final int DRAW_SECTOR = 52;
public static final int DRAW_TEXT_ON_PATH = 53;
public static final int DRAW_OVAL = 56;
public static final int DATA_PATH = 123;
@@ -149,8 +158,15 @@ public class Operations {
public static final int ID_LIST = 146;
public static final int FLOAT_LIST = 147;
public static final int DATA_LONG = 148;
+ public static final int DRAW_BITMAP_SCALED = 149;
+ public static final int TEXT_LOOKUP = 151;
+ public static final int DRAW_ARC = 152;
+ public static final int TEXT_LOOKUP_INT = 153;
+ public static final int DATA_MAP_LOOKUP = 154;
+ public static final int TEXT_MEASURE = 155;
+ public static final int TEXT_LENGTH = 156;
- /////////////////////////////////////////======================
+ ///////////////////////////////////////// ======================
////////////////////////////////////////
// Layout commands
@@ -163,10 +179,12 @@ public class Operations {
public static final int LAYOUT_COLUMN = 204;
public static final int LAYOUT_CANVAS = 205;
public static final int LAYOUT_CANVAS_CONTENT = 207;
-
public static final int LAYOUT_TEXT = 208;
+ public static final int LAYOUT_STATE = 217;
+
public static final int COMPONENT_START = 2;
public static final int COMPONENT_END = 3;
+
public static final int MODIFIER_WIDTH = 16;
public static final int MODIFIER_HEIGHT = 67;
public static final int MODIFIER_BACKGROUND = 55;
@@ -178,6 +196,8 @@ public class Operations {
public static final int MODIFIER_CLICK = 59;
public static final int MODIFIER_CLICK_END = 214;
+ public static final int LOOP_START = 215;
+ public static final int LOOP_END = 216;
public static final int MODIFIER_VISIBILITY = 211;
public static final int HOST_ACTION = 209;
@@ -185,6 +205,7 @@ public class Operations {
public static final int VALUE_INTEGER_CHANGE_ACTION = 212;
public static final int VALUE_STRING_CHANGE_ACTION = 213;
+ public static final int VALUE_INTEGER_EXPRESSION_CHANGE_ACTION = 218;
public static final int ANIMATION_SPEC = 14;
@@ -194,7 +215,7 @@ public class Operations {
static class UniqueIntMap<T> extends IntMap<T> {
@Override
- public T put(int key, T value) {
+ public T put(int key, T value) {
assert null == get(key) : "Opcode " + key + " already used in Operations !";
return super.put(key, value);
}
@@ -210,7 +231,7 @@ public class Operations {
map.put(ROOT_CONTENT_BEHAVIOR, RootContentBehavior::read);
map.put(ROOT_CONTENT_DESCRIPTION, RootContentDescription::read);
- map.put(DRAW_ARC, DrawArc::read);
+ map.put(DRAW_SECTOR, DrawSector::read);
map.put(DRAW_BITMAP, DrawBitmap::read);
map.put(DRAW_CIRCLE, DrawCircle::read);
map.put(DRAW_LINE, DrawLine::read);
@@ -247,6 +268,12 @@ public class Operations {
map.put(ID_LIST, DataListIds::read);
map.put(FLOAT_LIST, DataListFloat::read);
map.put(DATA_LONG, LongConstant::read);
+ map.put(DRAW_BITMAP_SCALED, DrawBitmapScaled::read);
+ map.put(TEXT_LOOKUP, TextLookup::read);
+ map.put(TEXT_LOOKUP_INT, TextLookupInt::read);
+
+ map.put(LOOP_START, LoopOperation::read);
+ map.put(LOOP_END, LoopEnd::read);
// Layout
@@ -267,6 +294,9 @@ public class Operations {
map.put(HOST_ACTION, HostActionOperation::read);
map.put(HOST_NAMED_ACTION, HostNamedActionOperation::read);
map.put(VALUE_INTEGER_CHANGE_ACTION, ValueIntegerChangeActionOperation::read);
+ map.put(
+ VALUE_INTEGER_EXPRESSION_CHANGE_ACTION,
+ ValueIntegerExpressionChangeActionOperation::read);
map.put(VALUE_STRING_CHANGE_ACTION, ValueStringChangeActionOperation::read);
map.put(LAYOUT_ROOT, RootLayoutComponent::read);
@@ -278,6 +308,12 @@ public class Operations {
map.put(LAYOUT_CANVAS_CONTENT, CanvasContent::read);
map.put(LAYOUT_TEXT, TextLayout::read);
+ map.put(LAYOUT_STATE, StateLayout::read);
+
map.put(COMPONENT_VALUE, ComponentValue::read);
+ map.put(DRAW_ARC, DrawArc::read);
+ map.put(DATA_MAP_LOOKUP, DataMapLookup::read);
+ map.put(TEXT_MEASURE, TextMeasure::read);
+ map.put(TEXT_LENGTH, TextLength::read);
}
}
diff --git a/core/java/com/android/internal/widget/remotecompose/core/PaintContext.java b/core/java/com/android/internal/widget/remotecompose/core/PaintContext.java
index 4770b122d172..13d6f783a586 100644
--- a/core/java/com/android/internal/widget/remotecompose/core/PaintContext.java
+++ b/core/java/com/android/internal/widget/remotecompose/core/PaintContext.java
@@ -17,15 +17,14 @@ package com.android.internal.widget.remotecompose.core;
import com.android.internal.widget.remotecompose.core.operations.paint.PaintBundle;
-/**
- * Specify an abstract paint context used by RemoteCompose commands to draw
- */
+/** Specify an abstract paint context used by RemoteCompose commands to draw */
public abstract class PaintContext {
public static final int TEXT_MEASURE_MONOSPACE_WIDTH = 0x01;
public static final int TEXT_MEASURE_FONT_HEIGHT = 0x02;
protected RemoteContext mContext;
private boolean mNeedsRepaint = false;
+
public RemoteContext getContext() {
return mContext;
}
@@ -46,43 +45,43 @@ public abstract class PaintContext {
this.mContext = context;
}
- /**
- * convenience function to call matrixSave()
- */
+ /** convenience function to call matrixSave() */
public void save() {
matrixSave();
}
- /**
- * convenience function to call matrixRestore()
- */
+ /** convenience function to call matrixRestore() */
public void restore() {
matrixRestore();
}
- /**
- * convenience function to call matrixSave()
- */
+ /** convenience function to call matrixSave() */
public void saveLayer(float x, float y, float width, float height) {
// TODO
matrixSave();
}
- public abstract void drawBitmap(int imageId,
- int srcLeft, int srcTop, int srcRight, int srcBottom,
- int dstLeft, int dstTop, int dstRight, int dstBottom,
- int cdId);
+ public abstract void drawBitmap(
+ int imageId,
+ int srcLeft,
+ int srcTop,
+ int srcRight,
+ int srcBottom,
+ int dstLeft,
+ int dstTop,
+ int dstRight,
+ int dstBottom,
+ int cdId);
public abstract void scale(float scaleX, float scaleY);
public abstract void translate(float translateX, float translateY);
- public abstract void drawArc(float left,
- float top,
- float right,
- float bottom,
- float startAngle,
- float sweepAngle);
+ public abstract void drawArc(
+ float left, float top, float right, float bottom, float startAngle, float sweepAngle);
+
+ public abstract void drawSector(
+ float left, float top, float right, float bottom, float startAngle, float sweepAngle);
public abstract void drawBitmap(int id, float left, float top, float right, float bottom);
@@ -96,43 +95,29 @@ public abstract class PaintContext {
public abstract void drawRect(float left, float top, float right, float bottom);
- /**
- * this caches the paint to a paint stack
- */
- public abstract void savePaint();
+ /** this caches the paint to a paint stack */
+ public abstract void savePaint();
- /**
- * This restores the paint form the paint stack
- */
- public abstract void restorePaint();
+ /** This restores the paint form the paint stack */
+ public abstract void restorePaint();
- public abstract void drawRoundRect(float left,
- float top,
- float right,
- float bottom,
- float radiusX,
- float radiusY);
+ public abstract void drawRoundRect(
+ float left, float top, float right, float bottom, float radiusX, float radiusY);
public abstract void drawTextOnPath(int textId, int pathId, float hOffset, float vOffset);
/**
- * Return the dimensions (left, top, right, bottom).
- * Relative to a drawTextRun x=0, y=0;
+ * Return the dimensions (left, top, right, bottom). Relative to a drawTextRun x=0, y=0;
*
* @param textId
* @param start
- * @param end if end is -1 it means the whole string
- * @param flags how to measure:
- * TEXT_MEASURE_MONOSPACE_WIDTH - measure as a monospace font
- * TEXT_MEASURE_FULL_HEIGHT - measure bounds of the given string using the
- * max ascend and descent of the font (not just of the measured text)
+ * @param end if end is -1 it means the whole string
+ * @param flags how to measure: TEXT_MEASURE_MONOSPACE_WIDTH - measure as a monospace font
+ * TEXT_MEASURE_FULL_HEIGHT - measure bounds of the given string using the max ascend and
+ * descent of the font (not just of the measured text)
* @param bounds the bounds (left, top, right, bottom)
*/
- public abstract void getTextBounds(int textId,
- int start,
- int end,
- int flags,
- float[]bounds);
+ public abstract void getTextBounds(int textId, int start, int end, int flags, float[] bounds);
/**
* Draw a text starting ast x,y
@@ -146,38 +131,38 @@ public abstract class PaintContext {
* @param y
* @param rtl
*/
- public abstract void drawTextRun(int textId,
- int start,
- int end,
- int contextStart,
- int contextEnd,
- float x,
- float y,
- boolean rtl);
+ public abstract void drawTextRun(
+ int textId,
+ int start,
+ int end,
+ int contextStart,
+ int contextEnd,
+ float x,
+ float y,
+ boolean rtl);
/**
* Draw an interpolation between two paths
+ *
* @param path1Id
* @param path2Id
- * @param tween 0.0 = is path1 1.0 is path2
+ * @param tween 0.0 = is path1 1.0 is path2
* @param start
* @param stop
*/
- public abstract void drawTweenPath(int path1Id,
- int path2Id,
- float tween,
- float start,
- float stop);
+ public abstract void drawTweenPath(
+ int path1Id, int path2Id, float tween, float start, float stop);
/**
* This applies changes to the current paint
+ *
* @param mPaintData the list of changes
*/
public abstract void applyPaint(PaintBundle mPaintData);
/**
- * Scale the rendering by scaleX and saleY (1.0 = no scale).
- * Scaling is done about centerX,centerY.
+ * Scale the rendering by scaleX and saleY (1.0 = no scale). Scaling is done about
+ * centerX,centerY.
*
* @param scaleX
* @param scaleY
@@ -188,6 +173,7 @@ public abstract class PaintContext {
/**
* Translate the rendering
+ *
* @param translateX
* @param translateY
*/
@@ -195,33 +181,30 @@ public abstract class PaintContext {
/**
* Skew the rendering
+ *
* @param skewX
* @param skewY
*/
public abstract void matrixSkew(float skewX, float skewY);
/**
- * Rotate the rendering.
- * Note rotates are cumulative.
+ * Rotate the rendering. Note rotates are cumulative.
+ *
* @param rotate angle to rotate
* @param pivotX x-coordinate about which to rotate
* @param pivotY y-coordinate about which to rotate
*/
public abstract void matrixRotate(float rotate, float pivotX, float pivotY);
- /**
- * Save the current state of the transform
- */
+ /** Save the current state of the transform */
public abstract void matrixSave();
- /**
- * Restore the previously saved state of the transform
- */
+ /** Restore the previously saved state of the transform */
public abstract void matrixRestore();
/**
- * Set the clip to a rectangle.
- * Drawing outside the current clip region will have no effect
+ * Set the clip to a rectangle. Drawing outside the current clip region will have no effect
+ *
* @param left
* @param top
* @param right
@@ -231,13 +214,15 @@ public abstract class PaintContext {
/**
* Clip based on a path.
+ *
* @param pathId
* @param regionOp
*/
public abstract void clipPath(int pathId, int regionOp);
/**
- * Clip based ona round rect
+ * Clip based ona round rect
+ *
* @param width
* @param height
* @param topStart
@@ -245,13 +230,15 @@ public abstract class PaintContext {
* @param bottomStart
* @param bottomEnd
*/
- public abstract void roundedClipRect(float width, float height,
- float topStart, float topEnd,
- float bottomStart, float bottomEnd);
-
- /**
- * Reset the paint
- */
+ public abstract void roundedClipRect(
+ float width,
+ float height,
+ float topStart,
+ float topEnd,
+ float bottomStart,
+ float bottomEnd);
+
+ /** Reset the paint */
public abstract void reset();
/**
@@ -285,4 +272,3 @@ public abstract class PaintContext {
mNeedsRepaint = true;
}
}
-
diff --git a/core/java/com/android/internal/widget/remotecompose/core/PaintOperation.java b/core/java/com/android/internal/widget/remotecompose/core/PaintOperation.java
index 4a1ccc9d3156..9b7b50f775b0 100644
--- a/core/java/com/android/internal/widget/remotecompose/core/PaintOperation.java
+++ b/core/java/com/android/internal/widget/remotecompose/core/PaintOperation.java
@@ -16,8 +16,8 @@
package com.android.internal.widget.remotecompose.core;
/**
- * PaintOperation interface, used for operations aimed at painting
- * (while any operation _can_ paint, this make it a little more explicit)
+ * PaintOperation interface, used for operations aimed at painting (while any operation _can_ paint,
+ * this make it a little more explicit)
*/
public abstract class PaintOperation implements Operation {
@@ -37,4 +37,13 @@ public abstract class PaintOperation implements Operation {
}
public abstract void paint(PaintContext context);
+
+ /**
+ * Will return true if the operation is similar enough to the current one, in the context of an
+ * animated transition.
+ */
+ public boolean suitableForTransition(Operation op) {
+ // by default expects the op to not be suitable
+ return false;
+ }
}
diff --git a/core/java/com/android/internal/widget/remotecompose/core/Platform.java b/core/java/com/android/internal/widget/remotecompose/core/Platform.java
index 903dab49cd06..6725e7e6ac2b 100644
--- a/core/java/com/android/internal/widget/remotecompose/core/Platform.java
+++ b/core/java/com/android/internal/widget/remotecompose/core/Platform.java
@@ -15,13 +15,36 @@
*/
package com.android.internal.widget.remotecompose.core;
-/**
- * Services that are needed to be provided by the platform during encoding.
- */
+/** Services that are needed to be provided by the platform during encoding. */
public interface Platform {
byte[] imageToByteArray(Object image);
+
int getImageWidth(Object image);
+
int getImageHeight(Object image);
- float[] pathToFloatArray(Object image);
-}
+ float[] pathToFloatArray(Object path);
+
+ Platform None =
+ new Platform() {
+ @Override
+ public byte[] imageToByteArray(Object image) {
+ throw new UnsupportedOperationException();
+ }
+
+ @Override
+ public int getImageWidth(Object image) {
+ throw new UnsupportedOperationException();
+ }
+
+ @Override
+ public int getImageHeight(Object image) {
+ throw new UnsupportedOperationException();
+ }
+
+ @Override
+ public float[] pathToFloatArray(Object path) {
+ throw new UnsupportedOperationException();
+ }
+ };
+}
diff --git a/core/java/com/android/internal/widget/remotecompose/core/RemoteComposeBuffer.java b/core/java/com/android/internal/widget/remotecompose/core/RemoteComposeBuffer.java
index 6b1828f0448f..5b5adc28a676 100644
--- a/core/java/com/android/internal/widget/remotecompose/core/RemoteComposeBuffer.java
+++ b/core/java/com/android/internal/widget/remotecompose/core/RemoteComposeBuffer.java
@@ -25,15 +25,18 @@ import com.android.internal.widget.remotecompose.core.operations.ComponentValue;
import com.android.internal.widget.remotecompose.core.operations.DataListFloat;
import com.android.internal.widget.remotecompose.core.operations.DataListIds;
import com.android.internal.widget.remotecompose.core.operations.DataMapIds;
+import com.android.internal.widget.remotecompose.core.operations.DataMapLookup;
import com.android.internal.widget.remotecompose.core.operations.DrawArc;
import com.android.internal.widget.remotecompose.core.operations.DrawBitmap;
import com.android.internal.widget.remotecompose.core.operations.DrawBitmapInt;
+import com.android.internal.widget.remotecompose.core.operations.DrawBitmapScaled;
import com.android.internal.widget.remotecompose.core.operations.DrawCircle;
import com.android.internal.widget.remotecompose.core.operations.DrawLine;
import com.android.internal.widget.remotecompose.core.operations.DrawOval;
import com.android.internal.widget.remotecompose.core.operations.DrawPath;
import com.android.internal.widget.remotecompose.core.operations.DrawRect;
import com.android.internal.widget.remotecompose.core.operations.DrawRoundRect;
+import com.android.internal.widget.remotecompose.core.operations.DrawSector;
import com.android.internal.widget.remotecompose.core.operations.DrawText;
import com.android.internal.widget.remotecompose.core.operations.DrawTextAnchored;
import com.android.internal.widget.remotecompose.core.operations.DrawTextOnPath;
@@ -55,6 +58,10 @@ import com.android.internal.widget.remotecompose.core.operations.RootContentBeha
import com.android.internal.widget.remotecompose.core.operations.RootContentDescription;
import com.android.internal.widget.remotecompose.core.operations.TextData;
import com.android.internal.widget.remotecompose.core.operations.TextFromFloat;
+import com.android.internal.widget.remotecompose.core.operations.TextLength;
+import com.android.internal.widget.remotecompose.core.operations.TextLookup;
+import com.android.internal.widget.remotecompose.core.operations.TextLookupInt;
+import com.android.internal.widget.remotecompose.core.operations.TextMeasure;
import com.android.internal.widget.remotecompose.core.operations.TextMerge;
import com.android.internal.widget.remotecompose.core.operations.Theme;
import com.android.internal.widget.remotecompose.core.operations.Utils;
@@ -62,11 +69,14 @@ import com.android.internal.widget.remotecompose.core.operations.layout.CanvasCo
import com.android.internal.widget.remotecompose.core.operations.layout.ComponentEnd;
import com.android.internal.widget.remotecompose.core.operations.layout.ComponentStart;
import com.android.internal.widget.remotecompose.core.operations.layout.LayoutComponentContent;
+import com.android.internal.widget.remotecompose.core.operations.layout.LoopEnd;
+import com.android.internal.widget.remotecompose.core.operations.layout.LoopOperation;
import com.android.internal.widget.remotecompose.core.operations.layout.RootLayoutComponent;
import com.android.internal.widget.remotecompose.core.operations.layout.managers.BoxLayout;
import com.android.internal.widget.remotecompose.core.operations.layout.managers.CanvasLayout;
import com.android.internal.widget.remotecompose.core.operations.layout.managers.ColumnLayout;
import com.android.internal.widget.remotecompose.core.operations.layout.managers.RowLayout;
+import com.android.internal.widget.remotecompose.core.operations.layout.managers.StateLayout;
import com.android.internal.widget.remotecompose.core.operations.layout.managers.TextLayout;
import com.android.internal.widget.remotecompose.core.operations.layout.modifiers.BackgroundModifierOperation;
import com.android.internal.widget.remotecompose.core.operations.layout.modifiers.BorderModifierOperation;
@@ -76,7 +86,9 @@ import com.android.internal.widget.remotecompose.core.operations.layout.modifier
import com.android.internal.widget.remotecompose.core.operations.paint.PaintBundle;
import com.android.internal.widget.remotecompose.core.operations.utilities.NanMap;
import com.android.internal.widget.remotecompose.core.operations.utilities.easing.FloatAnimation;
+import com.android.internal.widget.remotecompose.core.types.BooleanConstant;
import com.android.internal.widget.remotecompose.core.types.IntegerConstant;
+import com.android.internal.widget.remotecompose.core.types.LongConstant;
import java.io.File;
import java.io.FileInputStream;
@@ -86,9 +98,7 @@ import java.io.InputStream;
import java.util.ArrayList;
import java.util.Arrays;
-/**
- * Provides an abstract buffer to encode/decode RemoteCompose operations
- */
+/** Provides an abstract buffer to encode/decode RemoteCompose operations */
public class RemoteComposeBuffer {
public static final int EASING_CUBIC_STANDARD = FloatAnimation.CUBIC_STANDARD;
public static final int EASING_CUBIC_ACCELERATE = FloatAnimation.CUBIC_ACCELERATE;
@@ -156,13 +166,13 @@ public class RemoteComposeBuffer {
/**
* Insert a header
*
- * @param width the width of the document in pixels
- * @param height the height of the document in pixels
+ * @param width the width of the document in pixels
+ * @param height the height of the document in pixels
* @param contentDescription content description of the document
- * @param capabilities bitmask indicating needed capabilities (unused for now)
+ * @param capabilities bitmask indicating needed capabilities (unused for now)
*/
- public void header(int width, int height, String contentDescription,
- float density, long capabilities) {
+ public void header(
+ int width, int height, String contentDescription, float density, long capabilities) {
Header.apply(mBuffer, width, height, density, capabilities);
int contentDescriptionId = 0;
if (contentDescription != null) {
@@ -174,8 +184,8 @@ public class RemoteComposeBuffer {
/**
* Insert a header
*
- * @param width the width of the document in pixels
- * @param height the height of the document in pixels
+ * @param width the width of the document in pixels
+ * @param height the height of the document in pixels
* @param contentDescription content description of the document
*/
public void header(int width, int height, String contentDescription) {
@@ -185,23 +195,31 @@ public class RemoteComposeBuffer {
/**
* Insert a bitmap
*
- * @param image an opaque image that we'll add to the buffer
- * @param imageWidth the width of the image
+ * @param image an opaque image that we'll add to the buffer
+ * @param imageWidth the width of the image
* @param imageHeight the height of the image
- * @param srcLeft left coordinate of the source area
- * @param srcTop top coordinate of the source area
- * @param srcRight right coordinate of the source area
- * @param srcBottom bottom coordinate of the source area
- * @param dstLeft left coordinate of the destination area
- * @param dstTop top coordinate of the destination area
- * @param dstRight right coordinate of the destination area
- * @param dstBottom bottom coordinate of the destination area
- */
- public void drawBitmap(Object image,
- int imageWidth, int imageHeight,
- int srcLeft, int srcTop, int srcRight, int srcBottom,
- int dstLeft, int dstTop, int dstRight, int dstBottom,
- String contentDescription) {
+ * @param srcLeft left coordinate of the source area
+ * @param srcTop top coordinate of the source area
+ * @param srcRight right coordinate of the source area
+ * @param srcBottom bottom coordinate of the source area
+ * @param dstLeft left coordinate of the destination area
+ * @param dstTop top coordinate of the destination area
+ * @param dstRight right coordinate of the destination area
+ * @param dstBottom bottom coordinate of the destination area
+ */
+ public void drawBitmap(
+ Object image,
+ int imageWidth,
+ int imageHeight,
+ int srcLeft,
+ int srcTop,
+ int srcRight,
+ int srcBottom,
+ int dstLeft,
+ int dstTop,
+ int dstRight,
+ int dstBottom,
+ String contentDescription) {
int imageId = mRemoteComposeState.dataGetId(image);
if (imageId == -1) {
imageId = mRemoteComposeState.cacheData(image);
@@ -213,14 +231,39 @@ public class RemoteComposeBuffer {
contentDescriptionId = addText(contentDescription);
}
DrawBitmapInt.apply(
- mBuffer, imageId, srcLeft, srcTop, srcRight, srcBottom,
- dstLeft, dstTop, dstRight, dstBottom, contentDescriptionId
- );
+ mBuffer,
+ imageId,
+ srcLeft,
+ srcTop,
+ srcRight,
+ srcBottom,
+ dstLeft,
+ dstTop,
+ dstRight,
+ dstBottom,
+ contentDescriptionId);
}
/**
- * Adds a text string data to the stream and returns its id
- * Will be used to insert string with bitmaps etc.
+ * look up map and return the id of the object looked up
+ *
+ * @param mapId the map to access
+ * @param strId the string to lookup
+ * @return id containing the result of the lookup
+ */
+ public int mapLookup(int mapId, int strId) {
+ int hash = mapId + strId * 33;
+ int id = mRemoteComposeState.dataGetId(hash);
+ if (id == -1) {
+ id = mRemoteComposeState.cacheData(hash);
+ DataMapLookup.apply(mBuffer, id, mapId, strId);
+ }
+ return id;
+ }
+
+ /**
+ * Adds a text string data to the stream and returns its id Will be used to insert string with
+ * bitmaps etc.
*
* @param text the string to inject in the buffer
*/
@@ -236,13 +279,13 @@ public class RemoteComposeBuffer {
/**
* Add a click area to the document
*
- * @param id the id of the click area, reported in the click listener callback
+ * @param id the id of the click area, reported in the click listener callback
* @param contentDescription the content description of that click area (accessibility)
- * @param left left coordinate of the area bounds
- * @param top top coordinate of the area bounds
- * @param right right coordinate of the area bounds
- * @param bottom bottom coordinate of the area bounds
- * @param metadata associated metadata, user-provided
+ * @param left left coordinate of the area bounds
+ * @param top top coordinate of the area bounds
+ * @param right right coordinate of the area bounds
+ * @param bottom bottom coordinate of the area bounds
+ * @param metadata associated metadata, user-provided
*/
public void addClickArea(
int id,
@@ -251,8 +294,7 @@ public class RemoteComposeBuffer {
float top,
float right,
float bottom,
- String metadata
- ) {
+ String metadata) {
int contentDescriptionId = 0;
if (contentDescription != null) {
contentDescriptionId = addText(contentDescription);
@@ -261,77 +303,204 @@ public class RemoteComposeBuffer {
if (metadata != null) {
metadataId = addText(metadata);
}
- ClickArea.apply(mBuffer, id, contentDescriptionId,
- left, top, right, bottom, metadataId);
+ ClickArea.apply(mBuffer, id, contentDescriptionId, left, top, right, bottom, metadataId);
}
/**
* Sets the way the player handles the content
*
- * @param scroll set the horizontal behavior (NONE|SCROLL_HORIZONTAL|SCROLL_VERTICAL)
+ * @param scroll set the horizontal behavior (NONE|SCROLL_HORIZONTAL|SCROLL_VERTICAL)
* @param alignment set the alignment of the content (TOP|CENTER|BOTTOM|START|END)
- * @param sizing set the type of sizing for the content (NONE|SIZING_LAYOUT|SIZING_SCALE)
- * @param mode set the mode of sizing, either LAYOUT modes or SCALE modes
- * the LAYOUT modes are:
- * - LAYOUT_MATCH_PARENT
- * - LAYOUT_WRAP_CONTENT
- * or adding an horizontal mode and a vertical mode:
- * - LAYOUT_HORIZONTAL_MATCH_PARENT
- * - LAYOUT_HORIZONTAL_WRAP_CONTENT
- * - LAYOUT_HORIZONTAL_FIXED
- * - LAYOUT_VERTICAL_MATCH_PARENT
- * - LAYOUT_VERTICAL_WRAP_CONTENT
- * - LAYOUT_VERTICAL_FIXED
- * The LAYOUT_*_FIXED modes will use the intrinsic document size
+ * @param sizing set the type of sizing for the content (NONE|SIZING_LAYOUT|SIZING_SCALE)
+ * @param mode set the mode of sizing, either LAYOUT modes or SCALE modes the LAYOUT modes are:
+ * - LAYOUT_MATCH_PARENT - LAYOUT_WRAP_CONTENT or adding an horizontal mode and a vertical
+ * mode: - LAYOUT_HORIZONTAL_MATCH_PARENT - LAYOUT_HORIZONTAL_WRAP_CONTENT -
+ * LAYOUT_HORIZONTAL_FIXED - LAYOUT_VERTICAL_MATCH_PARENT - LAYOUT_VERTICAL_WRAP_CONTENT -
+ * LAYOUT_VERTICAL_FIXED The LAYOUT_*_FIXED modes will use the intrinsic document size
*/
public void setRootContentBehavior(int scroll, int alignment, int sizing, int mode) {
RootContentBehavior.apply(mBuffer, scroll, alignment, sizing, mode);
}
/**
- * add Drawing the specified arc, which will be scaled to fit inside the specified oval.
- * <br>
+ * add Drawing the specified arc, which will be scaled to fit inside the specified oval. <br>
* If the start angle is negative or >= 360, the start angle is treated as start angle modulo
- * 360.
- * <br>
+ * 360. <br>
* If the sweep angle is >= 360, then the oval is drawn completely. Note that this differs
* slightly from SkPath::arcTo, which treats the sweep angle modulo 360. If the sweep angle is
- * negative, the sweep angle is treated as sweep angle modulo 360
- * <br>
+ * negative, the sweep angle is treated as sweep angle modulo 360 <br>
* The arc is drawn clockwise. An angle of 0 degrees correspond to the geometric angle of 0
- * degrees (3 o'clock on a watch.)
- * <br>
+ * degrees (3 o'clock on a watch.) <br>
*
- * @param left left coordinate of oval used to define the shape and size of the arc
- * @param top top coordinate of oval used to define the shape and size of the arc
- * @param right right coordinate of oval used to define the shape and size of the arc
- * @param bottom bottom coordinate of oval used to define the shape and size of the arc
+ * @param left left coordinate of oval used to define the shape and size of the arc
+ * @param top top coordinate of oval used to define the shape and size of the arc
+ * @param right right coordinate of oval used to define the shape and size of the arc
+ * @param bottom bottom coordinate of oval used to define the shape and size of the arc
* @param startAngle Starting angle (in degrees) where the arc begins
* @param sweepAngle Sweep angle (in degrees) measured clockwise
*/
- public void addDrawArc(float left,
- float top,
- float right,
- float bottom,
- float startAngle,
- float sweepAngle) {
+ public void addDrawArc(
+ float left, float top, float right, float bottom, float startAngle, float sweepAngle) {
DrawArc.apply(mBuffer, left, top, right, bottom, startAngle, sweepAngle);
}
/**
- * @param image The bitmap to be drawn
- * @param left left coordinate of rectangle that the bitmap will be to fit into
- * @param top top coordinate of rectangle that the bitmap will be to fit into
- * @param right right coordinate of rectangle that the bitmap will be to fit into
- * @param bottom bottom coordinate of rectangle that the bitmap will be to fit into
+ * add Drawing the specified sector, which will be scaled to fit inside the specified oval. <br>
+ * If the start angle is negative or >= 360, the start angle is treated as start angle modulo
+ * 360. <br>
+ * If the sweep angle is >= 360, then the oval is drawn completely. Note that this differs
+ * slightly from SkPath::arcTo, which treats the sweep angle modulo 360. If the sweep angle is
+ * negative, the sweep angle is treated as sweep angle modulo 360 <br>
+ * The arc is drawn clockwise. An angle of 0 degrees correspond to the geometric angle of 0
+ * degrees (3 o'clock on a watch.) <br>
+ *
+ * @param left left coordinate of oval used to define the shape and size of the arc
+ * @param top top coordinate of oval used to define the shape and size of the arc
+ * @param right right coordinate of oval used to define the shape and size of the arc
+ * @param bottom bottom coordinate of oval used to define the shape and size of the arc
+ * @param startAngle Starting angle (in degrees) where the arc begins
+ * @param sweepAngle Sweep angle (in degrees) measured clockwise
+ */
+ public void addDrawSector(
+ float left, float top, float right, float bottom, float startAngle, float sweepAngle) {
+ DrawSector.apply(mBuffer, left, top, right, bottom, startAngle, sweepAngle);
+ }
+
+ /**
+ * @param image The bitmap to be drawn
+ * @param left left coordinate of rectangle that the bitmap will be to fit into
+ * @param top top coordinate of rectangle that the bitmap will be to fit into
+ * @param right right coordinate of rectangle that the bitmap will be to fit into
+ * @param bottom bottom coordinate of rectangle that the bitmap will be to fit into
+ * @param contentDescription content description of the image
+ */
+ public void addDrawBitmap(
+ Object image,
+ float left,
+ float top,
+ float right,
+ float bottom,
+ String contentDescription) {
+ int imageId = mRemoteComposeState.dataGetId(image);
+ if (imageId == -1) {
+ imageId = mRemoteComposeState.cacheData(image);
+ byte[] data = mPlatform.imageToByteArray(image);
+ int imageWidth = mPlatform.getImageWidth(image);
+ int imageHeight = mPlatform.getImageHeight(image);
+
+ BitmapData.apply(mBuffer, imageId, imageWidth, imageHeight, data);
+ }
+ int contentDescriptionId = 0;
+ if (contentDescription != null) {
+ contentDescriptionId = addText(contentDescription);
+ }
+ DrawBitmap.apply(mBuffer, imageId, left, top, right, bottom, contentDescriptionId);
+ }
+
+ /**
+ * @param imageId The Id bitmap to be drawn
+ * @param left left coordinate of rectangle that the bitmap will be to fit into
+ * @param top top coordinate of rectangle that the bitmap will be to fit into
+ * @param right right coordinate of rectangle that the bitmap will be to fit into
+ * @param bottom bottom coordinate of rectangle that the bitmap will be to fit into
* @param contentDescription content description of the image
*/
- public void addDrawBitmap(Object image,
- float left,
- float top,
- float right,
- float bottom,
- String contentDescription) {
+ public void addDrawBitmap(
+ int imageId,
+ float left,
+ float top,
+ float right,
+ float bottom,
+ String contentDescription) {
+ int contentDescriptionId = 0;
+ if (contentDescription != null) {
+ contentDescriptionId = addText(contentDescription);
+ }
+ DrawBitmap.apply(mBuffer, imageId, left, top, right, bottom, contentDescriptionId);
+ }
+
+ /**
+ * @param image The bitmap to be drawn
+ * @param srcLeft left coordinate in the source bitmap will be to extracted
+ * @param srcTop top coordinate in the source bitmap will be to extracted
+ * @param srcRight right coordinate in the source bitmap will be to extracted
+ * @param srcBottom bottom coordinate in the source bitmap will be to extracted
+ * @param dstLeft left coordinate of rectangle that the bitmap will be to fit into
+ * @param dstTop top coordinate of rectangle that the bitmap will be to fit into
+ * @param dstRight right coordinate of rectangle that the bitmap will be to fit into
+ * @param dstBottom bottom coordinate of rectangle that the bitmap will be to fit into
+ * @param scaleType The type of scaling to allow the image to fit.
+ * @param scaleFactor the scale factor when scale type is FIXED_SCALE (type = 7)
+ * @param contentDescription associate a string with image for accessibility
+ */
+ public void drawScaledBitmap(
+ Object image,
+ float srcLeft,
+ float srcTop,
+ float srcRight,
+ float srcBottom,
+ float dstLeft,
+ float dstTop,
+ float dstRight,
+ float dstBottom,
+ int scaleType,
+ float scaleFactor,
+ String contentDescription) {
+ int imageId = mRemoteComposeState.dataGetId(image);
+ if (imageId == -1) {
+ imageId = mRemoteComposeState.cacheData(image);
+ byte[] data = mPlatform.imageToByteArray(image);
+ int imageWidth = mPlatform.getImageWidth(image);
+ int imageHeight = mPlatform.getImageHeight(image);
+
+ BitmapData.apply(mBuffer, imageId, imageWidth, imageHeight, data);
+ }
+ int contentDescriptionId = 0;
+ if (contentDescription != null) {
+ contentDescriptionId = addText(contentDescription);
+ }
+ DrawBitmapScaled.apply(
+ mBuffer,
+ imageId,
+ srcLeft,
+ srcTop,
+ srcRight,
+ srcBottom,
+ dstLeft,
+ dstTop,
+ dstRight,
+ dstBottom,
+ scaleType,
+ scaleFactor,
+ contentDescriptionId);
+ }
+
+ /**
+ * Transmit bitmap so the you can use the id form. This is useful if
+ *
+ * @param image drawScaledBitmap
+ * @return id of the image useful with
+ */
+ public int addBitmap(Object image) {
+ int imageId = mRemoteComposeState.dataGetId(image);
+ if (imageId == -1) {
+ imageId = mRemoteComposeState.cacheData(image);
+ byte[] data = mPlatform.imageToByteArray(image);
+ int imageWidth = mPlatform.getImageWidth(image);
+ int imageHeight = mPlatform.getImageHeight(image);
+
+ BitmapData.apply(mBuffer, imageId, imageWidth, imageHeight, data);
+ }
+ return imageId;
+ }
+
+ /**
+ * Transmit bitmap so the you can use the id form. This is useful if
+ *
+ * @param image drawScaledBitmap
+ * @return id of the image useful with
+ */
+ public int addBitmap(Object image, String name) {
int imageId = mRemoteComposeState.dataGetId(image);
if (imageId == -1) {
imageId = mRemoteComposeState.cacheData(image);
@@ -340,14 +509,67 @@ public class RemoteComposeBuffer {
int imageHeight = mPlatform.getImageHeight(image);
BitmapData.apply(mBuffer, imageId, imageWidth, imageHeight, data);
+ setBitmapName(imageId, name);
}
+
+ return imageId;
+ }
+
+ /**
+ * This defines the name of the color given the id.
+ *
+ * @param id of the Bitmap
+ * @param name Name of the color
+ */
+ public void setBitmapName(int id, String name) {
+ NamedVariable.apply(mBuffer, id, NamedVariable.IMAGE_TYPE, name);
+ }
+
+ /**
+ * @param imageId The id of the bitmap to be drawn
+ * @param srcLeft left coordinate in the source bitmap will be to extracted
+ * @param srcTop top coordinate in the source bitmap will be to extracted
+ * @param srcRight right coordinate in the source bitmap will be to extracted
+ * @param srcBottom bottom coordinate in the source bitmap will be to extracted
+ * @param dstLeft left coordinate of rectangle that the bitmap will be to fit into
+ * @param dstTop top coordinate of rectangle that the bitmap will be to fit into
+ * @param dstRight right coordinate of rectangle that the bitmap will be to fit into
+ * @param dstBottom bottom coordinate of rectangle that the bitmap will be to fit into
+ * @param scaleType The type of scaling to allow the image to fit.
+ * @param scaleFactor the scale factor when scale type is FIXED_SCALE (type = 7)
+ * @param contentDescription associate a string with image for accessibility
+ */
+ public void drawScaledBitmap(
+ int imageId,
+ float srcLeft,
+ float srcTop,
+ float srcRight,
+ float srcBottom,
+ float dstLeft,
+ float dstTop,
+ float dstRight,
+ float dstBottom,
+ int scaleType,
+ float scaleFactor,
+ String contentDescription) {
int contentDescriptionId = 0;
if (contentDescription != null) {
contentDescriptionId = addText(contentDescription);
}
- DrawBitmap.apply(
- mBuffer, imageId, left, top, right, bottom, contentDescriptionId
- );
+ DrawBitmapScaled.apply(
+ mBuffer,
+ imageId,
+ srcLeft,
+ srcTop,
+ srcRight,
+ srcBottom,
+ dstLeft,
+ dstTop,
+ dstRight,
+ dstBottom,
+ scaleType,
+ scaleFactor,
+ contentDescriptionId);
}
/**
@@ -356,7 +578,7 @@ public class RemoteComposeBuffer {
*
* @param centerX The x-coordinate of the center of the circle to be drawn
* @param centerY The y-coordinate of the center of the circle to be drawn
- * @param radius The radius of the circle to be drawn
+ * @param radius The radius of the circle to be drawn
*/
public void addDrawCircle(float centerX, float centerY, float radius) {
DrawCircle.apply(mBuffer, centerX, centerY, radius);
@@ -378,9 +600,9 @@ public class RemoteComposeBuffer {
/**
* Draw the specified oval using the specified paint.
*
- * @param left left coordinate of oval
- * @param top top coordinate of oval
- * @param right right coordinate of oval
+ * @param left left coordinate of oval
+ * @param top top coordinate of oval
+ * @param right right coordinate of oval
* @param bottom bottom coordinate of oval
*/
public void addDrawOval(float left, float top, float right, float bottom) {
@@ -389,9 +611,9 @@ public class RemoteComposeBuffer {
/**
* Draw the specified path
- * <p>
- * Note: path objects are not immutable
- * modifying them and calling this will not change the drawing
+ *
+ * <p>Note: path objects are not immutable modifying them and calling this will not change the
+ * drawing
*
* @param path The path to be drawn
*/
@@ -415,9 +637,9 @@ public class RemoteComposeBuffer {
/**
* Draw the specified Rect
*
- * @param left left coordinate of rectangle to be drawn
- * @param top top coordinate of rectangle to be drawn
- * @param right right coordinate of rectangle to be drawn
+ * @param left left coordinate of rectangle to be drawn
+ * @param top top coordinate of rectangle to be drawn
+ * @param right right coordinate of rectangle to be drawn
* @param bottom bottom coordinate of rectangle to be drawn
*/
public void addDrawRect(float left, float top, float right, float bottom) {
@@ -427,23 +649,23 @@ public class RemoteComposeBuffer {
/**
* Draw the specified round-rect
*
- * @param left left coordinate of rectangle to be drawn
- * @param top left coordinate of rectangle to be drawn
- * @param right left coordinate of rectangle to be drawn
- * @param bottom left coordinate of rectangle to be drawn
+ * @param left left coordinate of rectangle to be drawn
+ * @param top left coordinate of rectangle to be drawn
+ * @param right left coordinate of rectangle to be drawn
+ * @param bottom left coordinate of rectangle to be drawn
* @param radiusX The x-radius of the oval used to round the corners
* @param radiusY The y-radius of the oval used to round the corners
*/
- public void addDrawRoundRect(float left, float top, float right, float bottom,
- float radiusX, float radiusY) {
+ public void addDrawRoundRect(
+ float left, float top, float right, float bottom, float radiusX, float radiusY) {
DrawRoundRect.apply(mBuffer, left, top, right, bottom, radiusX, radiusY);
}
/**
* Draw the text, with origin at (x,y) along the specified path.
*
- * @param text The text to be drawn
- * @param path The path the text should follow for its baseline
+ * @param text The text to be drawn
+ * @param path The path the text should follow for its baseline
* @param hOffset The distance along the path to add to the text's starting position
* @param vOffset The distance above(-) or below(+) the path to position the text
*/
@@ -457,91 +679,79 @@ public class RemoteComposeBuffer {
}
/**
- * Draw the text, with origin at (x,y). The origin is interpreted
- * based on the Align setting in the paint.
+ * Draw the text, with origin at (x,y). The origin is interpreted based on the Align setting in
+ * the paint.
*
- * @param text The text to be drawn
- * @param start The index of the first character in text to draw
- * @param end (end - 1) is the index of the last character in text to draw
+ * @param text The text to be drawn
+ * @param start The index of the first character in text to draw
+ * @param end (end - 1) is the index of the last character in text to draw
* @param contextStart
* @param contextEnd
- * @param x The x-coordinate of the origin of the text being drawn
- * @param y The y-coordinate of the baseline of the text being drawn
- * @param rtl Draw RTTL
- */
- public void addDrawTextRun(String text,
- int start,
- int end,
- int contextStart,
- int contextEnd,
- float x,
- float y,
- boolean rtl) {
+ * @param x The x-coordinate of the origin of the text being drawn
+ * @param y The y-coordinate of the baseline of the text being drawn
+ * @param rtl Draw RTTL
+ */
+ public void addDrawTextRun(
+ String text,
+ int start,
+ int end,
+ int contextStart,
+ int contextEnd,
+ float x,
+ float y,
+ boolean rtl) {
int textId = addText(text);
- DrawText.apply(
- mBuffer, textId, start, end,
- contextStart, contextEnd, x, y, rtl);
+ DrawText.apply(mBuffer, textId, start, end, contextStart, contextEnd, x, y, rtl);
}
/**
- * Draw the text, with origin at (x,y). The origin is interpreted
- * based on the Align setting in the paint.
+ * Draw the text, with origin at (x,y). The origin is interpreted based on the Align setting in
+ * the paint.
*
- * @param textId The text to be drawn
- * @param start The index of the first character in text to draw
- * @param end (end - 1) is the index of the last character in text to draw
+ * @param textId The text to be drawn
+ * @param start The index of the first character in text to draw
+ * @param end (end - 1) is the index of the last character in text to draw
* @param contextStart
* @param contextEnd
- * @param x The x-coordinate of the origin of the text being drawn
- * @param y The y-coordinate of the baseline of the text being drawn
- * @param rtl Draw RTTL
- */
- public void addDrawTextRun(int textId,
- int start,
- int end,
- int contextStart,
- int contextEnd,
- float x,
- float y,
- boolean rtl) {
- DrawText.apply(
- mBuffer, textId, start, end,
- contextStart, contextEnd, x, y, rtl);
- }
-
- /**
- * Draw a text on canvas at relative to position (x, y),
- * offset panX and panY.
- * <br>
- * The panning factors (panX, panY) mapped to the
- * resulting bounding box of the text, in such a way that a
- * panning factor of (0.0, 0.0) would center the text at (x, y)
+ * @param x The x-coordinate of the origin of the text being drawn
+ * @param y The y-coordinate of the baseline of the text being drawn
+ * @param rtl Draw RTTL
+ */
+ public void addDrawTextRun(
+ int textId,
+ int start,
+ int end,
+ int contextStart,
+ int contextEnd,
+ float x,
+ float y,
+ boolean rtl) {
+ DrawText.apply(mBuffer, textId, start, end, contextStart, contextEnd, x, y, rtl);
+ }
+
+ /**
+ * Draw a text on canvas at relative to position (x, y), offset panX and panY. <br>
+ * The panning factors (panX, panY) mapped to the resulting bounding box of the text, in such a
+ * way that a panning factor of (0.0, 0.0) would center the text at (x, y)
+ *
* <ul>
- * <li> Panning of -1.0, -1.0 - the text above & right of x,y.</li>
- * <li>Panning of 1.0, 1.0 - the text is below and to the left</li>
- * <li>Panning of 1.0, 0.0 - the test is centered & to the right of x,y</li>
+ * <li>Panning of -1.0, -1.0 - the text above & right of x,y.
+ * <li>Panning of 1.0, 1.0 - the text is below and to the left
+ * <li>Panning of 1.0, 0.0 - the test is centered & to the right of x,y
* </ul>
+ *
* Setting panY to NaN results in y being the baseline of the text.
*
- * @param text text to draw
- * @param x Coordinate of the Anchor
- * @param y Coordinate of the Anchor
- * @param panX justifies text -1.0=right, 0.0=center, 1.0=left
- * @param panY position text -1.0=above, 0.0=center, 1.0=below, Nan=baseline
+ * @param text text to draw
+ * @param x Coordinate of the Anchor
+ * @param y Coordinate of the Anchor
+ * @param panX justifies text -1.0=right, 0.0=center, 1.0=left
+ * @param panY position text -1.0=above, 0.0=center, 1.0=below, Nan=baseline
* @param flags 1 = RTL
*/
- public void drawTextAnchored(String text,
- float x,
- float y,
- float panX,
- float panY,
- int flags) {
+ public void drawTextAnchored(String text, float x, float y, float panX, float panY, int flags) {
int textId = addText(text);
- DrawTextAnchored.apply(
- mBuffer, textId,
- x, y,
- panX, panY,
- flags);
+ DrawTextAnchored.apply(mBuffer, textId, x, y, panX, panY, flags);
}
/**
@@ -556,6 +766,7 @@ public class RemoteComposeBuffer {
/**
* Merge two text (from id's) output one id
+ *
* @param id1 left id
* @param id2 right id
* @return new id that merges the two text
@@ -576,77 +787,68 @@ public class RemoteComposeBuffer {
/**
* Create a TextFromFloat command which creates text from a Float.
*
- * @param value The value to convert
+ * @param value The value to convert
* @param digitsBefore the digits before the decimal point
- * @param digitsAfter the digits after the decimal point
- * @param flags configure the behaviour using PAD_PRE_* and PAD_AFTER* flags
+ * @param digitsAfter the digits after the decimal point
+ * @param flags configure the behaviour using PAD_PRE_* and PAD_AFTER* flags
* @return id of the string that can be passed to drawTextAnchored
*/
- public int createTextFromFloat(float value, short digitsBefore,
- short digitsAfter, int flags) {
- String placeHolder = Utils.floatToString(value)
- + "(" + digitsBefore + "," + digitsAfter + "," + flags + ")";
+ public int createTextFromFloat(float value, short digitsBefore, short digitsAfter, int flags) {
+ String placeHolder =
+ Utils.floatToString(value)
+ + "("
+ + digitsBefore
+ + ","
+ + digitsAfter
+ + ","
+ + flags
+ + ")";
int id = mRemoteComposeState.dataGetId(placeHolder);
if (id == -1) {
id = mRemoteComposeState.cacheData(placeHolder);
// TextData.apply(mBuffer, id, text);
}
- TextFromFloat.apply(mBuffer, id, value, digitsBefore,
- digitsAfter, flags);
+ TextFromFloat.apply(mBuffer, id, value, digitsBefore, digitsAfter, flags);
return id;
}
/**
- * Draw a text on canvas at relative to position (x, y),
- * offset panX and panY.
- * <br>
- * The panning factors (panX, panY) mapped to the
- * resulting bounding box of the text, in such a way that a
- * panning factor of (0.0, 0.0) would center the text at (x, y)
+ * Draw a text on canvas at relative to position (x, y), offset panX and panY. <br>
+ * The panning factors (panX, panY) mapped to the resulting bounding box of the text, in such a
+ * way that a panning factor of (0.0, 0.0) would center the text at (x, y)
+ *
* <ul>
- * <li> Panning of -1.0, -1.0 - the text above & right of x,y.</li>
- * <li>Panning of 1.0, 1.0 - the text is below and to the left</li>
- * <li>Panning of 1.0, 0.0 - the test is centered & to the right of x,y</li>
+ * <li>Panning of -1.0, -1.0 - the text above & right of x,y.
+ * <li>Panning of 1.0, 1.0 - the text is below and to the left
+ * <li>Panning of 1.0, 0.0 - the test is centered & to the right of x,y
* </ul>
+ *
* Setting panY to NaN results in y being the baseline of the text.
*
* @param textId text to draw
- * @param x Coordinate of the Anchor
- * @param y Coordinate of the Anchor
- * @param panX justifies text -1.0=right, 0.0=center, 1.0=left
- * @param panY position text -1.0=above, 0.0=center, 1.0=below, Nan=baseline
- * @param flags 1 = RTL
+ * @param x Coordinate of the Anchor
+ * @param y Coordinate of the Anchor
+ * @param panX justifies text -1.0=right, 0.0=center, 1.0=left
+ * @param panY position text -1.0=above, 0.0=center, 1.0=below, Nan=baseline
+ * @param flags 1 = RTL
*/
- public void drawTextAnchored(int textId,
- float x,
- float y,
- float panX,
- float panY,
- int flags) {
+ public void drawTextAnchored(int textId, float x, float y, float panX, float panY, int flags) {
- DrawTextAnchored.apply(
- mBuffer, textId,
- x, y,
- panX, panY,
- flags);
+ DrawTextAnchored.apply(mBuffer, textId, x, y, panX, panY, flags);
}
/**
* draw an interpolation between two paths that have the same pattern
- * <p>
- * Warning paths objects are not immutable and this is not taken into consideration
+ *
+ * <p>Warning paths objects are not immutable and this is not taken into consideration
*
* @param path1 The path1 to be drawn between
* @param path2 The path2 to be drawn between
* @param tween The ratio of path1 and path2 to 0 = all path 1, 1 = all path2
* @param start The start of the subrange of paths to draw 0 = start form start 0.5 is half way
- * @param stop The end of the subrange of paths to draw 1 = end at the end 0.5 is end half way
+ * @param stop The end of the subrange of paths to draw 1 = end at the end 0.5 is end half way
*/
- public void addDrawTweenPath(Object path1,
- Object path2,
- float tween,
- float start,
- float stop) {
+ public void addDrawTweenPath(Object path1, Object path2, float tween, float start, float stop) {
int path1Id = mRemoteComposeState.dataGetId(path1);
if (path1Id == -1) { // never been seen before
path1Id = addPathData(path1);
@@ -663,18 +865,12 @@ public class RemoteComposeBuffer {
*
* @param path1Id The path1 to be drawn between
* @param path2Id The path2 to be drawn between
- * @param tween The ratio of path1 and path2 to 0 = all path 1, 1 = all path2
- * @param start The start of the subrange of paths to draw 0 = start form start .5 is 1/2 way
- * @param stop The end of the subrange of paths to draw 1 = end at the end .5 is end 1/2 way
+ * @param tween The ratio of path1 and path2 to 0 = all path 1, 1 = all path2
+ * @param start The start of the subrange of paths to draw 0 = start form start .5 is 1/2 way
+ * @param stop The end of the subrange of paths to draw 1 = end at the end .5 is end 1/2 way
*/
- public void addDrawTweenPath(int path1Id,
- int path2Id,
- float tween,
- float start,
- float stop) {
- DrawTweenPath.apply(
- mBuffer, path1Id, path2Id,
- tween, start, stop);
+ public void addDrawTweenPath(int path1Id, int path2Id, float tween, float start, float stop) {
+ DrawTweenPath.apply(mBuffer, path1Id, path2Id, tween, start, stop);
}
/**
@@ -692,11 +888,13 @@ public class RemoteComposeBuffer {
/**
* Adds a paint Bundle to the doc
+ *
* @param paint
*/
public void addPaint(PaintBundle paint) {
PaintData.apply(mBuffer, paint);
}
+
///////////////////////////////////////////////////////////////////////////////////////////////
public void inflateFromBuffer(ArrayList<Operation> operations) {
@@ -741,30 +939,29 @@ public class RemoteComposeBuffer {
return "v1.0";
}
- public static RemoteComposeBuffer fromFile(String path,
- RemoteComposeState remoteComposeState)
+ public static RemoteComposeBuffer fromFile(String path, RemoteComposeState remoteComposeState)
throws IOException {
RemoteComposeBuffer buffer = new RemoteComposeBuffer(remoteComposeState);
read(new File(path), buffer);
return buffer;
}
- public RemoteComposeBuffer fromFile(File file,
- RemoteComposeState remoteComposeState) throws IOException {
+ public RemoteComposeBuffer fromFile(File file, RemoteComposeState remoteComposeState)
+ throws IOException {
RemoteComposeBuffer buffer = new RemoteComposeBuffer(remoteComposeState);
read(file, buffer);
return buffer;
}
- public static RemoteComposeBuffer fromInputStream(InputStream inputStream,
- RemoteComposeState remoteComposeState) {
+ public static RemoteComposeBuffer fromInputStream(
+ InputStream inputStream, RemoteComposeState remoteComposeState) {
RemoteComposeBuffer buffer = new RemoteComposeBuffer(remoteComposeState);
read(inputStream, buffer);
return buffer;
}
- RemoteComposeBuffer copyFromOperations(ArrayList<Operation> operations,
- RemoteComposeBuffer buffer) {
+ RemoteComposeBuffer copyFromOperations(
+ ArrayList<Operation> operations, RemoteComposeBuffer buffer) {
for (Operation operation : operations) {
operation.write(buffer.mBuffer);
@@ -832,9 +1029,9 @@ public class RemoteComposeBuffer {
}
/**
- * This call balances a previous call to save(), and is used to remove all
- * modifications to the matrix/clip state since the last save call.
- * Do not call restore() more times than save() was called.
+ * This call balances a previous call to save(), and is used to remove all modifications to the
+ * matrix/clip state since the last save call. Do not call restore() more times than save() was
+ * called.
*/
public void addMatrixRestore() {
MatrixRestore.apply(mBuffer);
@@ -842,11 +1039,10 @@ public class RemoteComposeBuffer {
/**
* Add a saves the current matrix and clip onto a private stack.
- * <p>
- * Subsequent calls to translate,scale,rotate,skew,concat or clipRect,
- * clipPath will all operate as usual, but when the balancing call to
- * restore() is made, those calls will be forgotten, and the settings that
- * existed before the save() will be reinstated.
+ *
+ * <p>Subsequent calls to translate,scale,rotate,skew,concat or clipRect, clipPath will all
+ * operate as usual, but when the balancing call to restore() is made, those calls will be
+ * forgotten, and the settings that existed before the save() will be reinstated.
*/
public void addMatrixSave() {
MatrixSave.apply(mBuffer);
@@ -855,7 +1051,7 @@ public class RemoteComposeBuffer {
/**
* add a pre-concat the current matrix with the specified rotation.
*
- * @param angle The amount to rotate, in degrees
+ * @param angle The amount to rotate, in degrees
* @param centerX The x-coord for the pivot point (unchanged by the rotation)
* @param centerY The y-coord for the pivot point (unchanged by the rotation)
*/
@@ -886,8 +1082,8 @@ public class RemoteComposeBuffer {
/**
* Add a pre-concat of the current matrix with the specified scale.
*
- * @param scaleX The amount to scale in X
- * @param scaleY The amount to scale in Y
+ * @param scaleX The amount to scale in X
+ * @param scaleY The amount to scale in Y
* @param centerX The x-coord for the pivot point (unchanged by the scale)
* @param centerY The y-coord for the pivot point (unchanged by the scale)
*/
@@ -897,6 +1093,7 @@ public class RemoteComposeBuffer {
/**
* sets the clip based on clip id
+ *
* @param pathId 0 clears the clip
*/
public void addClipPath(int pathId) {
@@ -905,10 +1102,11 @@ public class RemoteComposeBuffer {
/**
* Sets the clip based on clip rec
- * @param left left coordinate of the clip rectangle
- * @param top top coordinate of the clip rectangle
- * @param right right coordinate of the clip rectangle
- * @param bottom bottom coordinate of the clip rectangle
+ *
+ * @param left left coordinate of the clip rectangle
+ * @param top top coordinate of the clip rectangle
+ * @param right right coordinate of the clip rectangle
+ * @param bottom bottom coordinate of the clip rectangle
*/
public void addClipRect(float left, float top, float right, float bottom) {
ClipRect.apply(mBuffer, left, top, right, bottom);
@@ -916,6 +1114,7 @@ public class RemoteComposeBuffer {
/**
* Add a float return a NaN number pointing to that float
+ *
* @param value the value of the float
* @return the nan id of float
*/
@@ -925,9 +1124,9 @@ public class RemoteComposeBuffer {
return Utils.asNan(id);
}
-
/**
* Add a Integer return an id number pointing to that float.
+ *
* @param value adds an integer and assigns it an id
* @return the id of the integer to be used
*/
@@ -938,7 +1137,32 @@ public class RemoteComposeBuffer {
}
/**
+ * Add a long constant return a id. They can be used as parameters to Custom Attributes.
+ *
+ * @param value the value of the long
+ * @return the id of the command representing long
+ */
+ public int addLong(long value) {
+ int id = mRemoteComposeState.cacheData(value);
+ LongConstant.apply(mBuffer, id, value);
+ return id;
+ }
+
+ /**
+ * Add a boolean constant return a id. They can be used as parameters to Custom Attributes.
+ *
+ * @param value the value of the boolean
+ * @return the id
+ */
+ public int addBoolean(boolean value) {
+ int id = mRemoteComposeState.cacheData(value);
+ BooleanConstant.apply(mBuffer, id, value);
+ return id;
+ }
+
+ /**
* Add a IntegerId as float ID.
+ *
* @param id id to be converted
* @return the id wrapped in a NaN
*/
@@ -948,6 +1172,7 @@ public class RemoteComposeBuffer {
/**
* Add a float that is a computation based on variables
+ *
* @param value A RPN style float operation i.e. "4, 3, ADD" outputs 7
* @return NaN id of the result of the calculation
*/
@@ -958,8 +1183,8 @@ public class RemoteComposeBuffer {
}
/**
- * Add a float that is a computation based on variables.
- * see packAnimation
+ * Add a float that is a computation based on variables. see packAnimation
+ *
* @param value A RPN style float operation i.e. "4, 3, ADD" outputs 7
* @param animation Array of floats that represents animation
* @return NaN id of the result of the calculation
@@ -970,12 +1195,37 @@ public class RemoteComposeBuffer {
return Utils.asNan(id);
}
+ /**
+ * measure the text and return a measure as a float
+ *
+ * @param textId id of the text
+ * @param mode the mode 0 is the width
+ * @return
+ */
+ public float textMeasure(int textId, int mode) {
+ int id = mRemoteComposeState.cacheData(textId + mode * 31);
+ TextMeasure.apply(mBuffer, id, textId, mode);
+ return Utils.asNan(id);
+ }
+
+ /**
+ * measure the text and return the length of the text as float
+ *
+ * @param textId id of the text
+ * @return id of a float that is the length
+ */
+ public float textLength(int textId) {
+ // The cache id is computed buy merging the two values together
+ // to create a relatively unique value
+ int id = mRemoteComposeState.cacheData(textId + (TextLength.id() << 16));
+ TextLength.apply(mBuffer, id, textId);
+ return Utils.asNan(id);
+ }
/**
* add a float array
*
* @param values
- *
* @return the id of the array, encoded as a float NaN
*/
public float addFloatArray(float[] values) {
@@ -986,23 +1236,23 @@ public class RemoteComposeBuffer {
/**
* This creates a list of individual floats
- * @param values array of floats to be individually stored
*
+ * @param values array of floats to be individually stored
* @return id of the list
*/
public float addFloatList(float[] values) {
- int []listId = new int[values.length];
+ int[] listId = new int[values.length];
for (int i = 0; i < listId.length; i++) {
listId[i] = mRemoteComposeState.cacheFloat(values[i]);
- FloatConstant.apply(mBuffer, listId[i], values[i]);
+ FloatConstant.apply(mBuffer, listId[i], values[i]);
}
return addList(listId);
}
/**
* This creates a list of individual floats
- * @param listId array id to be stored
*
+ * @param listId array id to be stored
* @return id of the list
*/
public float addList(int[] listId) {
@@ -1016,16 +1266,17 @@ public class RemoteComposeBuffer {
*
* @param keys
* @param values
- *
* @return the id of the map, encoded as a float NaN
*/
- public float addFloatMap(String[]keys, float[] values) {
- int []listId = new int[values.length];
+ public float addFloatMap(String[] keys, float[] values) {
+ int[] listId = new int[values.length];
+ byte[] type = new byte[values.length];
for (int i = 0; i < listId.length; i++) {
listId[i] = mRemoteComposeState.cacheFloat(values[i]);
- FloatConstant.apply(mBuffer, listId[i], values[i]);
+ FloatConstant.apply(mBuffer, listId[i], values[i]);
+ type[i] = DataMapIds.TYPE_FLOAT;
}
- return addMap(keys, listId);
+ return addMap(keys, type, listId);
}
/**
@@ -1033,17 +1284,49 @@ public class RemoteComposeBuffer {
*
* @param keys
* @param listId
- *
* @return the id of the map, encoded as a float NaN
*/
- public float addMap(String []keys, int[] listId) {
+ public int addMap(String[] keys, byte[] types, int[] listId) {
int id = mRemoteComposeState.cacheData(listId, NanMap.TYPE_ARRAY);
- DataMapIds.apply(mBuffer, id, keys, listId);
- return Utils.asNan(id);
+ DataMapIds.apply(mBuffer, id, keys, types, listId);
+ return id;
+ }
+
+ /**
+ * This provides access to text in RemoteList
+ *
+ * @param dataSet
+ * @param index index as a float variable
+ * @return
+ */
+ public int textLookup(float dataSet, float index) {
+ long hash =
+ ((long) Float.floatToRawIntBits(dataSet))
+ << (32 + Float.floatToRawIntBits(index)); // TODO: is this the correct ()s?
+ int id = mRemoteComposeState.cacheData(hash);
+ TextLookup.apply(mBuffer, id, Utils.idFromNan(dataSet), index);
+ return id;
+ }
+
+ /**
+ * This provides access to text in RemoteList
+ *
+ * @param dataSet
+ * @param index index as an int variable
+ * @return
+ */
+ public int textLookup(float dataSet, int index) {
+ long hash =
+ ((long) Float.floatToRawIntBits(dataSet))
+ << (32 + Float.floatToRawIntBits(index)); // TODO: is this the correct ()s?
+ int id = mRemoteComposeState.cacheData(hash);
+ TextLookupInt.apply(mBuffer, id, Utils.idFromNan(dataSet), index);
+ return id;
}
/**
* Add and integer expression
+ *
* @param mask defines which elements are operators or variables
* @param value array of values to calculate maximum 32
* @return the id as an integer
@@ -1051,11 +1334,12 @@ public class RemoteComposeBuffer {
public int addIntegerExpression(int mask, int[] value) {
int id = mRemoteComposeState.cacheData(value);
IntegerExpression.apply(mBuffer, id, mask, value);
- return id;
+ return id;
}
/**
* Add a simple color
+ *
* @param color the RGB color value
* @return id that represents that color
*/
@@ -1067,9 +1351,9 @@ public class RemoteComposeBuffer {
return id;
}
-
/**
* Add a color that represents the tween between two colors
+ *
* @param color1 the ARGB value of the first color
* @param color2 the ARGB value of the second color
* @param tween the interpolation bet
@@ -1084,8 +1368,8 @@ public class RemoteComposeBuffer {
}
/**
- * Add a color that represents the tween between two colors where color1
- * is the id of a color
+ * Add a color that represents the tween between two colors where color1 is the id of a color
+ *
* @param color1 id of color
* @param color2 rgb color value
* @param tween the tween between color1 and color2 (1 = color2)
@@ -1100,8 +1384,8 @@ public class RemoteComposeBuffer {
}
/**
- * Add a color that represents the tween between two colors where color2
- * is the id of a color
+ * Add a color that represents the tween between two colors where color2 is the id of a color
+ *
* @param color1 the ARGB value of the first color
* @param color2 id of the second color
* @param tween the tween between color1 and color2 (1 = color2)
@@ -1116,8 +1400,9 @@ public class RemoteComposeBuffer {
}
/**
- * Add a color that represents the tween between two colors where color1 &
- * color2 are the ids of colors
+ * Add a color that represents the tween between two colors where color1 & color2 are the ids of
+ * colors
+ *
* @param color1 id of the first color
* @param color2 id of the second color
* @param tween the tween between color1 and color2 (1 = color2)
@@ -1132,8 +1417,9 @@ public class RemoteComposeBuffer {
}
/**
- * Color calculated by Hue saturation and value.
- * (as floats they can be variables used to create color transitions)
+ * Color calculated by Hue saturation and value. (as floats they can be variables used to create
+ * color transitions)
+ *
* @param hue the Hue
* @param sat the saturation
* @param value the value
@@ -1148,8 +1434,9 @@ public class RemoteComposeBuffer {
}
/**
- * Color calculated by Alpha, Hue saturation and value.
- * (as floats they can be variables used to create color transitions)
+ * Color calculated by Alpha, Hue saturation and value. (as floats they can be variables used to
+ * create color transitions)
+ *
* @param alpha the Alpha
* @param hue the hue
* @param sat the saturation
@@ -1165,8 +1452,9 @@ public class RemoteComposeBuffer {
}
/**
- * create and animation based on description and return as an array of
- * floats. see addAnimatedFloat
+ * create and animation based on description and return as an array of floats. see
+ * addAnimatedFloat
+ *
* @param duration the duration of the aimation
* @param type the type of animation
* @param spec the parameters of the animation if any
@@ -1174,23 +1462,20 @@ public class RemoteComposeBuffer {
* @param wrap the wraps value so (e.g 360 so angles 355 would animate to 5)
* @return
*/
- public static float[] packAnimation(float duration,
- int type,
- float[] spec,
- float initialValue,
- float wrap) {
+ public static float[] packAnimation(
+ float duration, int type, float[] spec, float initialValue, float wrap) {
return FloatAnimation.packToFloatArray(duration, type, spec, initialValue, wrap);
}
/**
* This defines the name of the color given the id.
+ *
* @param id of the color
* @param name Name of the color
*/
public void setColorName(int id, String name) {
- NamedVariable.apply(mBuffer, id,
- NamedVariable.COLOR_TYPE, name);
+ NamedVariable.apply(mBuffer, id, NamedVariable.COLOR_TYPE, name);
}
/**
@@ -1200,16 +1485,14 @@ public class RemoteComposeBuffer {
* @param name name of the string
*/
public void setStringName(int id, String name) {
- NamedVariable.apply(mBuffer, id,
- NamedVariable.STRING_TYPE, name);
+ NamedVariable.apply(mBuffer, id, NamedVariable.STRING_TYPE, name);
}
/**
- * Returns a usable component id -- either the one passed in parameter if not -1
- * or a generated one.
+ * Returns a usable component id -- either the one passed in parameter if not -1 or a generated
+ * one.
*
* @param id the current component id (if -1, we'll generate a new one)
- *
* @return a usable component id
*/
private int getComponentId(int id) {
@@ -1225,63 +1508,64 @@ public class RemoteComposeBuffer {
/**
* Add a component start tag
+ *
* @param type type of component
* @param id component id
*/
public void addComponentStart(int type, int id) {
mLastComponentId = getComponentId(id);
- ComponentStart.apply(mBuffer,
- type, mLastComponentId, 0f, 0f);
+ ComponentStart.apply(mBuffer, type, mLastComponentId, 0f, 0f);
}
/**
* Add a component start tag
+ *
* @param type type of component
*/
public void addComponentStart(int type) {
addComponentStart(type, -1);
}
- /**
- * Add a component end tag
- */
+ /** Add a component end tag */
public void addComponentEnd() {
ComponentEnd.apply(mBuffer);
}
/**
* Add a background modifier of provided color
+ *
* @param color the color of the background
* @param shape the background shape -- SHAPE_RECTANGLE, SHAPE_CIRCLE
*/
public void addModifierBackground(int color, int shape) {
- float r = ((color >> 16) & 0xff) / 255.0f;
- float g = ((color >> 8) & 0xff) / 255.0f;
- float b = ((color) & 0xff) / 255.0f;
- float a = ((color >> 24) & 0xff) / 255.0f;
- BackgroundModifierOperation.apply(mBuffer, 0f, 0f, 0f, 0f,
- r, g, b, a, shape);
+ float r = (color >> 16 & 0xff) / 255.0f;
+ float g = (color >> 8 & 0xff) / 255.0f;
+ float b = (color & 0xff) / 255.0f;
+ float a = (color >> 24 & 0xff) / 255.0f;
+ BackgroundModifierOperation.apply(mBuffer, 0f, 0f, 0f, 0f, r, g, b, a, shape);
}
/**
* Add a border modifier
+ *
* @param borderWidth the border width
* @param borderRoundedCorner the rounded corner radius if the shape is ROUNDED_RECT
* @param color the color of the border
* @param shape the shape of the border
*/
- public void addModifierBorder(float borderWidth, float borderRoundedCorner,
- int color, int shape) {
- float r = ((color >> 16) & 0xff) / 255.0f;
- float g = ((color >> 8) & 0xff) / 255.0f;
- float b = ((color) & 0xff) / 255.0f;
- float a = ((color >> 24) & 0xff) / 255.0f;
- BorderModifierOperation.apply(mBuffer, 0f, 0f, 0f, 0f,
- borderWidth, borderRoundedCorner, r, g, b, a, shape);
+ public void addModifierBorder(
+ float borderWidth, float borderRoundedCorner, int color, int shape) {
+ float r = (color >> 16 & 0xff) / 255.0f;
+ float g = (color >> 8 & 0xff) / 255.0f;
+ float b = (color & 0xff) / 255.0f;
+ float a = (color >> 24 & 0xff) / 255.0f;
+ BorderModifierOperation.apply(
+ mBuffer, 0f, 0f, 0f, 0f, borderWidth, borderRoundedCorner, r, g, b, a, shape);
}
/**
* Add a padding modifier
+ *
* @param left left padding
* @param top top padding
* @param right right padding
@@ -1293,24 +1577,36 @@ public class RemoteComposeBuffer {
/**
* Sets the clip based on rounded clip rect
+ *
* @param topStart
* @param topEnd
* @param bottomStart
* @param bottomEnd
*/
- public void addRoundClipRectModifier(float topStart, float topEnd,
- float bottomStart, float bottomEnd) {
- RoundedClipRectModifierOperation.apply(mBuffer,
- topStart, topEnd, bottomStart, bottomEnd);
+ public void addRoundClipRectModifier(
+ float topStart, float topEnd, float bottomStart, float bottomEnd) {
+ RoundedClipRectModifierOperation.apply(mBuffer, topStart, topEnd, bottomStart, bottomEnd);
}
- /**
- * Add a clip rect modifier
- */
+ /** Add a clip rect modifier */
public void addClipRectModifier() {
ClipRectModifierOperation.apply(mBuffer);
}
+ public void addLoopStart(float count, float from, float step, int indexId) {
+ LoopOperation.apply(mBuffer, count, from, step, indexId);
+ }
+
+ public void addLoopEnd() {
+ LoopEnd.apply(mBuffer);
+ }
+
+ public void addStateLayout(
+ int componentId, int animationId, int horizontal, int vertical, int indexId) {
+ mLastComponentId = getComponentId(componentId);
+ StateLayout.apply(mBuffer, mLastComponentId, animationId, horizontal, vertical, indexId);
+ }
+
/**
* Add a box start tag
*
@@ -1319,11 +1615,9 @@ public class RemoteComposeBuffer {
* @param horizontal horizontal alignment
* @param vertical vertical alignment
*/
- public void addBoxStart(int componentId, int animationId,
- int horizontal, int vertical) {
+ public void addBoxStart(int componentId, int animationId, int horizontal, int vertical) {
mLastComponentId = getComponentId(componentId);
- BoxLayout.apply(mBuffer, mLastComponentId, animationId,
- horizontal, vertical);
+ BoxLayout.apply(mBuffer, mLastComponentId, animationId, horizontal, vertical);
}
/**
@@ -1335,11 +1629,10 @@ public class RemoteComposeBuffer {
* @param vertical vertical alignment
* @param spacedBy spacing between items
*/
- public void addRowStart(int componentId, int animationId,
- int horizontal, int vertical, float spacedBy) {
+ public void addRowStart(
+ int componentId, int animationId, int horizontal, int vertical, float spacedBy) {
mLastComponentId = getComponentId(componentId);
- RowLayout.apply(mBuffer, mLastComponentId, animationId,
- horizontal, vertical, spacedBy);
+ RowLayout.apply(mBuffer, mLastComponentId, animationId, horizontal, vertical, spacedBy);
}
/**
@@ -1351,15 +1644,15 @@ public class RemoteComposeBuffer {
* @param vertical vertical alignment
* @param spacedBy spacing between items
*/
- public void addColumnStart(int componentId, int animationId,
- int horizontal, int vertical, float spacedBy) {
+ public void addColumnStart(
+ int componentId, int animationId, int horizontal, int vertical, float spacedBy) {
mLastComponentId = getComponentId(componentId);
- ColumnLayout.apply(mBuffer, mLastComponentId, animationId,
- horizontal, vertical, spacedBy);
+ ColumnLayout.apply(mBuffer, mLastComponentId, animationId, horizontal, vertical, spacedBy);
}
/**
* Add a canvas start tag
+ *
* @param componentId component id
* @param animationId animation id
*/
@@ -1370,6 +1663,7 @@ public class RemoteComposeBuffer {
/**
* Add a canvas content start tag
+ *
* @param componentId component id
*/
public void addCanvasContentStart(int componentId) {
@@ -1377,17 +1671,13 @@ public class RemoteComposeBuffer {
CanvasContent.apply(mBuffer, mLastComponentId);
}
- /**
- * Add a root start tag
- */
+ /** Add a root start tag */
public void addRootStart() {
mLastComponentId = getComponentId(-1);
RootLayoutComponent.apply(mBuffer, mLastComponentId);
}
- /**
- * Add a content start tag
- */
+ /** Add a content start tag */
public void addContentStart() {
mLastComponentId = getComponentId(-1);
LayoutComponentContent.apply(mBuffer, mLastComponentId);
@@ -1395,6 +1685,7 @@ public class RemoteComposeBuffer {
/**
* Add a component width value
+ *
* @param id id of the value
*/
public void addComponentWidthValue(int id) {
@@ -1422,16 +1713,29 @@ public class RemoteComposeBuffer {
* @param fontWeight font weight (1 to 1000, normal is 400)
* @param fontFamily font family or null
*/
- public void addTextComponentStart(int componentId, int animationId,
- int textId, int color, float fontSize,
- int fontStyle, float fontWeight, String fontFamily) {
+ public void addTextComponentStart(
+ int componentId,
+ int animationId,
+ int textId,
+ int color,
+ float fontSize,
+ int fontStyle,
+ float fontWeight,
+ String fontFamily) {
mLastComponentId = getComponentId(componentId);
int fontFamilyId = -1;
if (fontFamily != null) {
fontFamilyId = addText(fontFamily);
}
- TextLayout.apply(mBuffer, mLastComponentId, animationId, textId, color, fontSize,
- fontStyle, fontWeight, fontFamilyId);
+ TextLayout.apply(
+ mBuffer,
+ mLastComponentId,
+ animationId,
+ textId,
+ color,
+ fontSize,
+ fontStyle,
+ fontWeight,
+ fontFamilyId);
}
}
-
diff --git a/core/java/com/android/internal/widget/remotecompose/core/RemoteComposeOperation.java b/core/java/com/android/internal/widget/remotecompose/core/RemoteComposeOperation.java
index c7ec33593286..e60695fc4a06 100644
--- a/core/java/com/android/internal/widget/remotecompose/core/RemoteComposeOperation.java
+++ b/core/java/com/android/internal/widget/remotecompose/core/RemoteComposeOperation.java
@@ -15,6 +15,4 @@
*/
package com.android.internal.widget.remotecompose.core;
-public interface RemoteComposeOperation extends Operation {
-
-}
+public interface RemoteComposeOperation extends Operation {}
diff --git a/core/java/com/android/internal/widget/remotecompose/core/RemoteComposeState.java b/core/java/com/android/internal/widget/remotecompose/core/RemoteComposeState.java
index 839522e29fdb..51445f2ff31d 100644
--- a/core/java/com/android/internal/widget/remotecompose/core/RemoteComposeState.java
+++ b/core/java/com/android/internal/widget/remotecompose/core/RemoteComposeState.java
@@ -15,30 +15,24 @@
*/
package com.android.internal.widget.remotecompose.core;
-import static com.android.internal.widget.remotecompose.core.RemoteContext.ID_CONTINUOUS_SEC;
-import static com.android.internal.widget.remotecompose.core.RemoteContext.ID_TIME_IN_MIN;
-import static com.android.internal.widget.remotecompose.core.RemoteContext.ID_TIME_IN_SEC;
-import static com.android.internal.widget.remotecompose.core.RemoteContext.ID_WINDOW_HEIGHT;
-import static com.android.internal.widget.remotecompose.core.RemoteContext.ID_WINDOW_WIDTH;
-import static com.android.internal.widget.remotecompose.core.operations.utilities.NanMap.START_ARRAY;
-import static com.android.internal.widget.remotecompose.core.operations.utilities.NanMap.START_VAR;
-
import com.android.internal.widget.remotecompose.core.operations.utilities.ArrayAccess;
import com.android.internal.widget.remotecompose.core.operations.utilities.CollectionsAccess;
+import com.android.internal.widget.remotecompose.core.operations.utilities.DataMap;
import com.android.internal.widget.remotecompose.core.operations.utilities.IntFloatMap;
import com.android.internal.widget.remotecompose.core.operations.utilities.IntIntMap;
import com.android.internal.widget.remotecompose.core.operations.utilities.IntMap;
+import com.android.internal.widget.remotecompose.core.operations.utilities.NanMap;
import java.util.ArrayList;
import java.util.HashMap;
/**
- * Represents runtime state for a RemoteCompose document
- * State includes things like the value of variables
+ * Represents runtime state for a RemoteCompose document State includes things like the value of
+ * variables
*/
public class RemoteComposeState implements CollectionsAccess {
public static final int START_ID = 42;
- private static final int MAX_FLOATS = 500;
+ // private static final int MAX_FLOATS = 500;
private static final int MAX_COLORS = 200;
private static final int MAX_DATA = 1000;
@@ -48,6 +42,8 @@ public class RemoteComposeState implements CollectionsAccess {
private final IntFloatMap mFloatMap = new IntFloatMap(); // efficient cache
private final IntIntMap mIntegerMap = new IntIntMap(); // efficient cache
private final IntIntMap mColorMap = new IntIntMap(); // efficient cache
+ private final IntMap<DataMap> mDataMapMap = new IntMap<>();
+ private final IntMap<Object> mObjectMap = new IntMap<>();
private final boolean[] mColorOverride = new boolean[MAX_COLORS];
private final IntMap<ArrayAccess> mCollectionMap = new IntMap<>();
@@ -56,13 +52,12 @@ public class RemoteComposeState implements CollectionsAccess {
private final boolean[] mIntegerOverride = new boolean[MAX_DATA];
private int mNextId = START_ID;
- private int[] mIdMaps = new int[]{START_ID, START_VAR, START_ARRAY};
+ private int[] mIdMaps = new int[] {START_ID, NanMap.START_VAR, NanMap.START_ARRAY};
private RemoteContext mRemoteContext = null;
-
/**
- * Get Object based on id. The system will cache things like bitmaps
- * Paths etc. They can be accessed with this command
+ * Get Object based on id. The system will cache things like bitmaps Paths etc. They can be
+ * accessed with this command
*
* @param id
* @return
@@ -81,9 +76,7 @@ public class RemoteComposeState implements CollectionsAccess {
return mIntDataMap.get(id) != null;
}
- /**
- * Return the id of an item from the cache.
- */
+ /** Return the id of an item from the cache. */
public int dataGetId(Object data) {
Integer res = mDataIntMap.get(data);
if (res == null) {
@@ -93,8 +86,8 @@ public class RemoteComposeState implements CollectionsAccess {
}
/**
- * Add an item to the cache. Generates an id for the item and adds it to the cache based on
- * that id.
+ * Add an item to the cache. Generates an id for the item and adds it to the cache based on that
+ * id.
*/
public int cacheData(Object item) {
int id = nextId();
@@ -104,8 +97,8 @@ public class RemoteComposeState implements CollectionsAccess {
}
/**
- * Add an item to the cache. Generates an id for the item and adds it to the cache based on
- * that id.
+ * Add an item to the cache. Generates an id for the item and adds it to the cache based on that
+ * id.
*/
public int cacheData(Object item, int type) {
int id = nextId(type);
@@ -114,23 +107,22 @@ public class RemoteComposeState implements CollectionsAccess {
return id;
}
- /**
- * Insert an item in the cache
- */
+ /** Insert an item in the cache */
public void cacheData(int id, Object item) {
mDataIntMap.put(item, id);
mIntDataMap.put(id, item);
}
- /**
- * Insert an item in the cache
- */
+ /** Insert an item in the cache */
public void updateData(int id, Object item) {
if (!mDataOverride[id]) {
- mDataIntMap.remove(mIntDataMap.get(id));
- mDataIntMap.put(item, id);
- mIntDataMap.put(id, item);
- updateListeners(id);
+ Object previous = mIntDataMap.get(id);
+ if (previous != item) {
+ mDataIntMap.remove(previous);
+ mDataIntMap.put(item, id);
+ mIntDataMap.put(id, item);
+ updateListeners(id);
+ }
}
}
@@ -141,16 +133,17 @@ public class RemoteComposeState implements CollectionsAccess {
* @param item the new value
*/
public void overrideData(int id, Object item) {
- mDataIntMap.remove(mIntDataMap.get(id));
- mDataIntMap.put(item, id);
- mIntDataMap.put(id, item);
- mDataOverride[id] = true;
- updateListeners(id);
+ Object previous = mIntDataMap.get(id);
+ if (previous != item) {
+ mDataIntMap.remove(previous);
+ mDataIntMap.put(item, id);
+ mIntDataMap.put(id, item);
+ mDataOverride[id] = true;
+ updateListeners(id);
+ }
}
- /**
- * Insert an item in the cache
- */
+ /** Insert an item in the cache */
public int cacheFloat(float item) {
int id = nextId();
mFloatMap.put(id, item);
@@ -158,25 +151,22 @@ public class RemoteComposeState implements CollectionsAccess {
return id;
}
- /**
- * Insert an item in the cache
- */
+ /** Insert an item in the cache */
public void cacheFloat(int id, float item) {
mFloatMap.put(id, item);
}
- /**
- * Insert an float item in the cache
- */
- public void updateFloat(int id, float item) {
- mFloatMap.put(id, item);
- mIntegerMap.put(id, (int) item);
- updateListeners(id);
+ /** Insert an float item in the cache */
+ public void updateFloat(int id, float value) {
+ float previous = mFloatMap.get(id);
+ if (previous != value) {
+ mFloatMap.put(id, value);
+ mIntegerMap.put(id, (int) value);
+ updateListeners(id);
+ }
}
- /**
- * Insert an item in the cache
- */
+ /** Insert an item in the cache */
public int cacheInteger(int item) {
int id = nextId();
mIntegerMap.put(id, item);
@@ -184,14 +174,15 @@ public class RemoteComposeState implements CollectionsAccess {
return id;
}
- /**
- * Insert an integer item in the cache
- */
- public void updateInteger(int id, int item) {
+ /** Insert an integer item in the cache */
+ public void updateInteger(int id, int value) {
if (!mIntegerOverride[id]) {
- mFloatMap.put(id, item);
- mIntegerMap.put(id, item);
- updateListeners(id);
+ int previous = mIntegerMap.get(id);
+ if (previous != value) {
+ mFloatMap.put(id, value);
+ mIntegerMap.put(id, value);
+ updateListeners(id);
+ }
}
}
@@ -202,10 +193,13 @@ public class RemoteComposeState implements CollectionsAccess {
* @param value the new value
*/
public void overrideInteger(int id, int value) {
- mIntegerMap.put(id, value);
- mFloatMap.put(id, value);
- mIntegerOverride[id] = true;
- updateListeners(id);
+ int previous = mIntegerMap.get(id);
+ if (previous != value) {
+ mIntegerMap.put(id, value);
+ mFloatMap.put(id, value);
+ mIntegerOverride[id] = true;
+ updateListeners(id);
+ }
}
/**
@@ -262,8 +256,7 @@ public class RemoteComposeState implements CollectionsAccess {
}
/**
- * Adds a colorOverride.
- * This is a list of ids and their colors optimized for playback;
+ * Adds a colorOverride. This is a list of ids and their colors optimized for playback;
*
* @param id
* @param color
@@ -273,9 +266,7 @@ public class RemoteComposeState implements CollectionsAccess {
mColorMap.put(id, color);
}
- /**
- * Clear the color Overrides
- */
+ /** Clear the color Overrides */
public void clearColorOverride() {
for (int i = 0; i < mColorOverride.length; i++) {
mColorOverride[i] = false;
@@ -310,16 +301,12 @@ public class RemoteComposeState implements CollectionsAccess {
return !mIntWrittenMap.get(id);
}
- /**
- * Method to mark that a value, represented by its id, has been written to the WireBuffer
- */
+ /** Method to mark that a value, represented by its id, has been written to the WireBuffer */
public void markWritten(int id) {
mIntWrittenMap.put(id, true);
}
- /**
- * Clear the record of the values that have been written to the WireBuffer.
- */
+ /** Clear the record of the values that have been written to the WireBuffer. */
public void reset() {
mIntWrittenMap.clear();
mDataIntMap.clear();
@@ -388,13 +375,13 @@ public class RemoteComposeState implements CollectionsAccess {
for (VariableSupport vs : mAllVarListeners) {
vs.updateVariables(context);
}
- if (mVarListeners.get(ID_CONTINUOUS_SEC) != null) {
+ if (mVarListeners.get(RemoteContext.ID_CONTINUOUS_SEC) != null) {
return 1;
}
- if (mVarListeners.get(ID_TIME_IN_SEC) != null) {
+ if (mVarListeners.get(RemoteContext.ID_TIME_IN_SEC) != null) {
return 1000;
}
- if (mVarListeners.get(ID_TIME_IN_MIN) != null) {
+ if (mVarListeners.get(RemoteContext.ID_TIME_IN_MIN) != null) {
return 1000 * 60;
}
return -1;
@@ -406,7 +393,7 @@ public class RemoteComposeState implements CollectionsAccess {
* @param width
*/
public void setWindowWidth(float width) {
- updateFloat(ID_WINDOW_WIDTH, width);
+ updateFloat(RemoteContext.ID_WINDOW_WIDTH, width);
}
/**
@@ -415,7 +402,7 @@ public class RemoteComposeState implements CollectionsAccess {
* @param height
*/
public void setWindowHeight(float height) {
- updateFloat(ID_WINDOW_HEIGHT, height);
+ updateFloat(RemoteContext.ID_WINDOW_HEIGHT, height);
}
public void addCollection(int id, ArrayAccess collection) {
@@ -426,17 +413,39 @@ public class RemoteComposeState implements CollectionsAccess {
public float getFloatValue(int id, int index) {
return mCollectionMap.get(id & 0xFFFFF).getFloatValue(index);
}
+
@Override
public float[] getFloats(int id) {
return mCollectionMap.get(id & 0xFFFFF).getFloats();
}
@Override
- public int getFloatsLength(int id) {
- return mCollectionMap.get(id & 0xFFFFF).getFloatsLength();
+ public int getId(int id, int index) {
+ return mCollectionMap.get(id & 0xFFFFF).getId(index);
+ }
+
+ public void putDataMap(int id, DataMap map) {
+ mDataMapMap.put(id, map);
+ }
+
+ public DataMap getDataMap(int id) {
+ return mDataMapMap.get(id);
+ }
+
+ @Override
+ public int getListLength(int id) {
+ return mCollectionMap.get(id & 0xFFFFF).getLength();
}
public void setContext(RemoteContext context) {
mRemoteContext = context;
}
+
+ public void updateObject(int id, Object value) {
+ mObjectMap.put(id, value);
+ }
+
+ public Object getObject(int id) {
+ return mObjectMap.get(id);
+ }
}
diff --git a/core/java/com/android/internal/widget/remotecompose/core/RemoteContext.java b/core/java/com/android/internal/widget/remotecompose/core/RemoteContext.java
index 0df0aa04ec9c..1066e7d9f617 100644
--- a/core/java/com/android/internal/widget/remotecompose/core/RemoteContext.java
+++ b/core/java/com/android/internal/widget/remotecompose/core/RemoteContext.java
@@ -22,6 +22,7 @@ import com.android.internal.widget.remotecompose.core.operations.Utils;
import com.android.internal.widget.remotecompose.core.operations.layout.Component;
import com.android.internal.widget.remotecompose.core.operations.utilities.ArrayAccess;
import com.android.internal.widget.remotecompose.core.operations.utilities.CollectionsAccess;
+import com.android.internal.widget.remotecompose.core.operations.utilities.DataMap;
import java.time.LocalDateTime;
import java.time.OffsetDateTime;
@@ -31,9 +32,9 @@ import java.time.ZoneOffset;
/**
* Specify an abstract context used to playback RemoteCompose documents
*
- * This allows us to intercept the different operations in a document and react to them.
+ * <p>This allows us to intercept the different operations in a document and react to them.
*
- * We also contain a PaintContext, so that any operation can draw as needed.
+ * <p>We also contain a PaintContext, so that any operation can draw as needed.
*/
public abstract class RemoteContext {
protected CoreDocument mDocument;
@@ -73,8 +74,7 @@ public abstract class RemoteContext {
}
/**
- * Load a path under an id.
- * Paths can be use in clip drawPath and drawTweenPath
+ * Load a path under an id. Paths can be use in clip drawPath and drawTweenPath
*
* @param instanceId
* @param floatPath
@@ -85,7 +85,7 @@ public abstract class RemoteContext {
* Associate a name with a give id.
*
* @param varName the name
- * @param varId the id (color,integer,float etc.)
+ * @param varId the id (color,integer,float etc.)
* @param varType thetype
*/
public abstract void loadVariableName(String varName, int varId, int varType);
@@ -93,7 +93,7 @@ public abstract class RemoteContext {
/**
* Save a color under a given id
*
- * @param id the id of the color
+ * @param id the id of the color
* @param color the color to set
*/
public abstract void loadColor(int id, int color);
@@ -118,35 +118,33 @@ public abstract class RemoteContext {
}
/**
- * Set the value of a named Color.
- * This overrides the color in the document
+ * Set the value of a named Color. This overrides the color in the document
*
* @param colorName the name of the color to override
- * @param color Override the default color
+ * @param color Override the default color
*/
public abstract void setNamedColorOverride(String colorName, int color);
/**
- * Set the value of a named String.
- * This overrides the string in the document
+ * Set the value of a named String. This overrides the string in the document
+ *
* @param stringName the name of the string to override
* @param value Override the default string
*/
public abstract void setNamedStringOverride(String stringName, String value);
-
/**
* Allows to clear a named String.
*
- * If an override exists, we revert back to the default value in the document.
+ * <p>If an override exists, we revert back to the default value in the document.
*
* @param stringName the name of the string to override
*/
public abstract void clearNamedStringOverride(String stringName);
/**
- * Set the value of a named Integer.
- * This overrides the integer in the document
+ * Set the value of a named Integer. This overrides the integer in the document
+ *
* @param integerName the name of the integer to override
* @param value Override the default integer
*/
@@ -155,34 +153,41 @@ public abstract class RemoteContext {
/**
* Allows to clear a named Integer.
*
- * If an override exists, we revert back to the default value in the document.
+ * <p>If an override exists, we revert back to the default value in the document.
*
* @param integerName the name of the integer to override
*/
public abstract void clearNamedIntegerOverride(String integerName);
-
/**
* Support Collections by registering this collection
*
- * @param id id of the collection
+ * @param id id of the collection
* @param collection the collection under this id
*/
public abstract void addCollection(int id, ArrayAccess collection);
+ public abstract void putDataMap(int id, DataMap map);
+
+ public abstract DataMap getDataMap(int id);
+
public abstract void runAction(int id, String metadata);
public abstract void runNamedAction(int textId);
+ public abstract void putObject(int mId, Object command);
+
+ public abstract Object getObject(int mId);
/**
* The context can be used in a few different mode, allowing operations to skip being executed:
- * - UNSET : all operations will get executed
- * - DATA : only operations dealing with DATA (eg loading a bitmap) should execute
- * - PAINT : only operations painting should execute
+ * - UNSET : all operations will get executed - DATA : only operations dealing with DATA (eg
+ * loading a bitmap) should execute - PAINT : only operations painting should execute
*/
public enum ContextMode {
- UNSET, DATA, PAINT
+ UNSET,
+ DATA,
+ PAINT
}
public int getTheme() {
@@ -229,9 +234,13 @@ public abstract class RemoteContext {
// Operations
///////////////////////////////////////////////////////////////////////////////////////////////
- public void header(int majorVersion, int minorVersion, int patchVersion,
- int width, int height, long capabilities
- ) {
+ public void header(
+ int majorVersion,
+ int minorVersion,
+ int patchVersion,
+ int width,
+ int height,
+ long capabilities) {
mRemoteComposeState.setWindowWidth(width);
mRemoteComposeState.setWindowHeight(height);
mDocument.setVersion(majorVersion, minorVersion, patchVersion);
@@ -243,21 +252,14 @@ public abstract class RemoteContext {
/**
* Sets the way the player handles the content
*
- * @param scroll set the horizontal behavior (NONE|SCROLL_HORIZONTAL|SCROLL_VERTICAL)
+ * @param scroll set the horizontal behavior (NONE|SCROLL_HORIZONTAL|SCROLL_VERTICAL)
* @param alignment set the alignment of the content (TOP|CENTER|BOTTOM|START|END)
- * @param sizing set the type of sizing for the content (NONE|SIZING_LAYOUT|SIZING_SCALE)
- * @param mode set the mode of sizing, either LAYOUT modes or SCALE modes
- * the LAYOUT modes are:
- * - LAYOUT_MATCH_PARENT
- * - LAYOUT_WRAP_CONTENT
- * or adding an horizontal mode and a vertical mode:
- * - LAYOUT_HORIZONTAL_MATCH_PARENT
- * - LAYOUT_HORIZONTAL_WRAP_CONTENT
- * - LAYOUT_HORIZONTAL_FIXED
- * - LAYOUT_VERTICAL_MATCH_PARENT
- * - LAYOUT_VERTICAL_WRAP_CONTENT
- * - LAYOUT_VERTICAL_FIXED
- * The LAYOUT_*_FIXED modes will use the intrinsic document size
+ * @param sizing set the type of sizing for the content (NONE|SIZING_LAYOUT|SIZING_SCALE)
+ * @param mode set the mode of sizing, either LAYOUT modes or SCALE modes the LAYOUT modes are:
+ * - LAYOUT_MATCH_PARENT - LAYOUT_WRAP_CONTENT or adding an horizontal mode and a vertical
+ * mode: - LAYOUT_HORIZONTAL_MATCH_PARENT - LAYOUT_HORIZONTAL_WRAP_CONTENT -
+ * LAYOUT_HORIZONTAL_FIXED - LAYOUT_VERTICAL_MATCH_PARENT - LAYOUT_VERTICAL_WRAP_CONTENT -
+ * LAYOUT_VERTICAL_FIXED The LAYOUT_*_FIXED modes will use the intrinsic document size
*/
public void setRootContentBehavior(int scroll, int alignment, int sizing, int mode) {
mDocument.setRootContentBehavior(scroll, alignment, sizing, mode);
@@ -281,16 +283,16 @@ public abstract class RemoteContext {
* Save a bitmap under an imageId
*
* @param imageId the id of the image
- * @param width the width of the image
- * @param height the height of the image
- * @param bitmap the bytes that represent the image
+ * @param width the width of the image
+ * @param height the height of the image
+ * @param bitmap the bytes that represent the image
*/
public abstract void loadBitmap(int imageId, int width, int height, byte[] bitmap);
/**
* Save a string under a given id
*
- * @param id the id of the string
+ * @param id the id of the string
* @param text the value to set
*/
public abstract void loadText(int id, String text);
@@ -306,7 +308,7 @@ public abstract class RemoteContext {
/**
* Load a float
*
- * @param id id of the float
+ * @param id id of the float
* @param value the value to set
*/
public abstract void loadFloat(int id, float value);
@@ -314,21 +316,19 @@ public abstract class RemoteContext {
/**
* Load a integer
*
- * @param id id of the integer
+ * @param id id of the integer
* @param value the value to set
*/
public abstract void loadInteger(int id, int value);
-
public abstract void overrideInteger(int id, int value);
public abstract void overrideText(int id, int valueId);
/**
- * Load an animated float associated with an id
- * Todo: Remove?
+ * Load an animated float associated with an id Todo: Remove?
*
- * @param id the id of the float
+ * @param id the id of the float
* @param animatedFloat The animated float
*/
public abstract void loadAnimatedFloat(int id, FloatExpression animatedFloat);
@@ -336,7 +336,7 @@ public abstract class RemoteContext {
/**
* Save a shader under and ID
*
- * @param id the id of the Shader
+ * @param id the id of the Shader
* @param value the shader
*/
public abstract void loadShader(int id, ShaderData value);
@@ -368,7 +368,7 @@ public abstract class RemoteContext {
/**
* called to notify system that a command is interested in a variable
*
- * @param id track when this id changes value
+ * @param id track when this id changes value
* @param variableSupport call back when value changes
*/
public abstract void listensTo(int id, VariableSupport variableSupport);
@@ -401,33 +401,25 @@ public abstract class RemoteContext {
public static final int ID_WEEK_DAY = 11;
public static final int ID_DAY_OF_MONTH = 12;
- /**
- * CONTINUOUS_SEC is seconds from midnight looping every hour 0-3600
- */
+ /** CONTINUOUS_SEC is seconds from midnight looping every hour 0-3600 */
public static final float FLOAT_CONTINUOUS_SEC = Utils.asNan(ID_CONTINUOUS_SEC);
- /**
- * seconds run from Midnight=0 quantized to seconds hour 0..3599
- */
+
+ /** seconds run from Midnight=0 quantized to seconds hour 0..3599 */
public static final float FLOAT_TIME_IN_SEC = Utils.asNan(ID_TIME_IN_SEC);
- /**
- * minutes run from Midnight=0 quantized to minutes 0..1439
- */
+
+ /** minutes run from Midnight=0 quantized to minutes 0..1439 */
public static final float FLOAT_TIME_IN_MIN = Utils.asNan(ID_TIME_IN_MIN);
- /**
- * hours run from Midnight=0 quantized to Hours 0-23
- */
+
+ /** hours run from Midnight=0 quantized to Hours 0-23 */
public static final float FLOAT_TIME_IN_HR = Utils.asNan(ID_TIME_IN_HR);
- /**
- * Moth of Year quantized to MONTHS 1-12. 1 = January
- */
+
+ /** Moth of Year quantized to MONTHS 1-12. 1 = January */
public static final float FLOAT_CALENDAR_MONTH = Utils.asNan(ID_CALENDAR_MONTH);
- /**
- * DAY OF THE WEEK 1-7. 1 = Monday
- */
+
+ /** DAY OF THE WEEK 1-7. 1 = Monday */
public static final float FLOAT_WEEK_DAY = Utils.asNan(ID_WEEK_DAY);
- /**
- * DAY OF THE MONTH 1-31
- */
+
+ /** DAY OF THE MONTH 1-31 */
public static final float FLOAT_DAY_OF_MONTH = Utils.asNan(ID_DAY_OF_MONTH);
public static final float FLOAT_WINDOW_WIDTH = Utils.asNan(ID_WINDOW_WIDTH);
@@ -446,7 +438,8 @@ public abstract class RemoteContext {
}
public static float getTime(float fl) {
- LocalDateTime dateTime = LocalDateTime.now();
+ LocalDateTime dateTime =
+ LocalDateTime.now(ZoneId.systemDefault()); // TODO, pass in a timezone explicitly?
// This define the time in the format
// seconds run from Midnight=0 quantized to seconds hour 0..3599
// minutes run from Midnight=0 quantized to minutes 0..1439
@@ -464,7 +457,6 @@ public abstract class RemoteContext {
float sec = currentSeconds + dateTime.getNano() * 1E-9f;
int day_week = dateTime.getDayOfWeek().getValue();
-
ZoneId zone = ZoneId.systemDefault();
OffsetDateTime offsetDateTime = dateTime.atZone(zone).toOffsetDateTime();
ZoneOffset offset = offsetDateTime.getOffset();
@@ -488,8 +480,6 @@ public abstract class RemoteContext {
return fl;
}
-
-
public abstract void addClickArea(
int id,
int contentDescription,
@@ -497,7 +487,5 @@ public abstract class RemoteContext {
float top,
float right,
float bottom,
- int metadataId
- );
+ int metadataId);
}
-
diff --git a/core/java/com/android/internal/widget/remotecompose/core/TimeVariables.java b/core/java/com/android/internal/widget/remotecompose/core/TimeVariables.java
index 04e04bbb9063..fa0cf3f455c4 100644
--- a/core/java/com/android/internal/widget/remotecompose/core/TimeVariables.java
+++ b/core/java/com/android/internal/widget/remotecompose/core/TimeVariables.java
@@ -20,9 +20,7 @@ import java.time.OffsetDateTime;
import java.time.ZoneId;
import java.time.ZoneOffset;
-/**
- * This generates the standard system variables for time.
- */
+/** This generates the standard system variables for time. */
public class TimeVariables {
/**
* This class populates all time variables in the system
@@ -30,7 +28,8 @@ public class TimeVariables {
* @param context
*/
public void updateTime(RemoteContext context) {
- LocalDateTime dateTime = LocalDateTime.now();
+ LocalDateTime dateTime =
+ LocalDateTime.now(ZoneId.systemDefault()); // TODO, pass in a timezone explicitly?
// This define the time in the format
// seconds run from Midnight=0 quantized to seconds hour 0..3599
// minutes run from Midnight=0 quantized to minutes 0..1439
@@ -47,7 +46,6 @@ public class TimeVariables {
float sec = currentSeconds + dateTime.getNano() * 1E-9f;
int day_week = dateTime.getDayOfWeek().getValue();
-
ZoneId zone = ZoneId.systemDefault();
OffsetDateTime offsetDateTime = dateTime.atZone(zone).toOffsetDateTime();
ZoneOffset offset = offsetDateTime.getOffset();
@@ -60,6 +58,5 @@ public class TimeVariables {
context.loadFloat(RemoteContext.ID_CALENDAR_MONTH, month);
context.loadFloat(RemoteContext.ID_DAY_OF_MONTH, month);
context.loadFloat(RemoteContext.ID_WEEK_DAY, day_week);
-
}
}
diff --git a/core/java/com/android/internal/widget/remotecompose/core/VariableSupport.java b/core/java/com/android/internal/widget/remotecompose/core/VariableSupport.java
index ee6d5798bdb2..51e58a1aeddd 100644
--- a/core/java/com/android/internal/widget/remotecompose/core/VariableSupport.java
+++ b/core/java/com/android/internal/widget/remotecompose/core/VariableSupport.java
@@ -16,20 +16,21 @@
package com.android.internal.widget.remotecompose.core;
/**
- * Interface for operators that interact with variables
- * Through this they register to listen to particular variables
- * and are notified when they change
+ * Interface for operators that interact with variables Through this they register to listen to
+ * particular variables and are notified when they change
*/
public interface VariableSupport {
/**
- * Call to allow an operator to register interest in variables.
- * Typically they call context.listensTo(id, this)
+ * Call to allow an operator to register interest in variables. Typically they call
+ * context.listensTo(id, this)
+ *
* @param context
*/
void registerListening(RemoteContext context);
/**
* Called to be notified that the variables you are interested have changed.
+ *
* @param context
*/
void updateVariables(RemoteContext context);
diff --git a/core/java/com/android/internal/widget/remotecompose/core/WireBuffer.java b/core/java/com/android/internal/widget/remotecompose/core/WireBuffer.java
index fc3202e2160d..c71b4901ca78 100644
--- a/core/java/com/android/internal/widget/remotecompose/core/WireBuffer.java
+++ b/core/java/com/android/internal/widget/remotecompose/core/WireBuffer.java
@@ -17,9 +17,7 @@ package com.android.internal.widget.remotecompose.core;
import java.util.Arrays;
-/**
- * The base communication buffer capable of encoding and decoding various types
- */
+/** The base communication buffer capable of encoding and decoding various types */
public class WireBuffer {
private static final int BUFFER_SIZE = 1024 * 1024 * 1;
int mMaxSize;
@@ -130,6 +128,7 @@ public class WireBuffer {
int v2 = (mBuffer[mIndex++] & 0xFF) << 0;
return v1 + v2;
}
+
public int peekInt() {
int tmp = mIndex;
int v1 = (mBuffer[tmp++] & 0xFF) << 24;
@@ -177,8 +176,8 @@ public class WireBuffer {
public byte[] readBuffer(int maxSize) {
int count = readInt();
if (count < 0 || count > maxSize) {
- throw new RuntimeException("attempt read a buff of invalid size 0 <= "
- + count + " > " + maxSize);
+ throw new RuntimeException(
+ "attempt read a buff of invalid size 0 <= " + count + " > " + maxSize);
}
byte[] b = Arrays.copyOfRange(mBuffer, mIndex, mIndex + count);
mIndex += count;
@@ -201,7 +200,7 @@ public class WireBuffer {
public void writeBoolean(boolean value) {
resize(1);
- mBuffer[mIndex++] = (byte) ((value) ? 1 : 0);
+ mBuffer[mIndex++] = (byte) (value ? 1 : 0);
mSize++;
}
@@ -256,7 +255,6 @@ public class WireBuffer {
writeInt(b.length);
for (int i = 0; i < b.length; i++) {
mBuffer[mIndex++] = b[i];
-
}
mSize += b.length;
}
@@ -265,6 +263,4 @@ public class WireBuffer {
byte[] buffer = content.getBytes();
writeBuffer(buffer);
}
-
}
-
diff --git a/core/java/com/android/internal/widget/remotecompose/core/documentation/DocumentationBuilder.java b/core/java/com/android/internal/widget/remotecompose/core/documentation/DocumentationBuilder.java
index ccbcdf6e615d..f6dfe2ebe171 100644
--- a/core/java/com/android/internal/widget/remotecompose/core/documentation/DocumentationBuilder.java
+++ b/core/java/com/android/internal/widget/remotecompose/core/documentation/DocumentationBuilder.java
@@ -17,6 +17,8 @@ package com.android.internal.widget.remotecompose.core.documentation;
public interface DocumentationBuilder {
void add(String value);
- Operation operation(String category, int id, String name);
- Operation wipOperation(String category, int id, String name);
+
+ DocumentedOperation operation(String category, int id, String name);
+
+ DocumentedOperation wipOperation(String category, int id, String name);
}
diff --git a/core/java/com/android/internal/widget/remotecompose/core/documentation/DocumentedOperation.java b/core/java/com/android/internal/widget/remotecompose/core/documentation/DocumentedOperation.java
new file mode 100644
index 000000000000..c33ae244b923
--- /dev/null
+++ b/core/java/com/android/internal/widget/remotecompose/core/documentation/DocumentedOperation.java
@@ -0,0 +1,181 @@
+/*
+ * 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.
+ */
+package com.android.internal.widget.remotecompose.core.documentation;
+
+import java.util.ArrayList;
+
+public class DocumentedOperation {
+ public static final int LAYOUT = 0;
+ public static final int INT = 0;
+ public static final int FLOAT = 1;
+ public static final int BOOLEAN = 2;
+ public static final int BUFFER = 4;
+ public static final int UTF8 = 5;
+ public static final int BYTE = 6;
+ public static final int VALUE = 7;
+ public static final int LONG = 8;
+ public static final int SHORT = 9;
+
+ public static final int FLOAT_ARRAY = 10;
+ public static final int INT_ARRAY = 11;
+
+ String mCategory;
+ int mId;
+ String mName;
+ String mDescription;
+
+ boolean mWIP;
+ String mTextExamples;
+
+ ArrayList<StringPair> mExamples = new ArrayList<>();
+ ArrayList<OperationField> mFields = new ArrayList<>();
+ String mVarSize = "";
+ int mExamplesWidth = 100;
+ int mExamplesHeight = 100;
+
+ public static String getType(int type) {
+ switch (type) {
+ case INT:
+ return "INT";
+ case FLOAT:
+ return "FLOAT";
+ case BOOLEAN:
+ return "BOOLEAN";
+ case BUFFER:
+ return "BUFFER";
+ case UTF8:
+ return "UTF8";
+ case BYTE:
+ return "BYTE";
+ case VALUE:
+ return "VALUE";
+ case LONG:
+ return "LONG";
+ case SHORT:
+ return "SHORT";
+ case FLOAT_ARRAY:
+ return "FLOAT[]";
+ case INT_ARRAY:
+ return "INT[]";
+ }
+ return "UNKNOWN";
+ }
+
+ public DocumentedOperation(String category, int id, String name, boolean wip) {
+ mCategory = category;
+ mId = id;
+ mName = name;
+ mWIP = wip;
+ }
+
+ public DocumentedOperation(String category, int id, String name) {
+ this(category, id, name, false);
+ }
+
+ public ArrayList<OperationField> getFields() {
+ return mFields;
+ }
+
+ public String getCategory() {
+ return mCategory;
+ }
+
+ public int getId() {
+ return mId;
+ }
+
+ public String getName() {
+ return mName;
+ }
+
+ public boolean isWIP() {
+ return mWIP;
+ }
+
+ public String getVarSize() {
+ return mVarSize;
+ }
+
+ public int getSizeFields() {
+ int size = 0;
+ mVarSize = "";
+ for (OperationField field : mFields) {
+ size += Math.max(0, field.getSize());
+ if (field.getSize() < 0) {
+ mVarSize += " + " + field.getVarSize() + " x 4";
+ }
+ }
+ return size;
+ }
+
+ public String getDescription() {
+ return mDescription;
+ }
+
+ public String getTextExamples() {
+ return mTextExamples;
+ }
+
+ public ArrayList<StringPair> getExamples() {
+ return mExamples;
+ }
+
+ public int getExamplesWidth() {
+ return mExamplesWidth;
+ }
+
+ public int getExamplesHeight() {
+ return mExamplesHeight;
+ }
+
+ public DocumentedOperation field(int type, String name, String description) {
+ mFields.add(new OperationField(type, name, description));
+ return this;
+ }
+
+ public DocumentedOperation field(int type, String name, String varSize, String description) {
+ mFields.add(new OperationField(type, name, varSize, description));
+ return this;
+ }
+
+ public DocumentedOperation possibleValues(String name, int value) {
+ if (!mFields.isEmpty()) {
+ mFields.get(mFields.size() - 1).possibleValue(name, "" + value);
+ }
+ return this;
+ }
+
+ public DocumentedOperation description(String description) {
+ mDescription = description;
+ return this;
+ }
+
+ public DocumentedOperation examples(String examples) {
+ mTextExamples = examples;
+ return this;
+ }
+
+ public DocumentedOperation exampleImage(String name, String imagePath) {
+ mExamples.add(new StringPair(name, imagePath));
+ return this;
+ }
+
+ public DocumentedOperation examplesDimension(int width, int height) {
+ mExamplesWidth = width;
+ mExamplesHeight = height;
+ return this;
+ }
+}
diff --git a/core/java/com/android/internal/widget/remotecompose/core/documentation/Operation.java b/core/java/com/android/internal/widget/remotecompose/core/documentation/Operation.java
index 9ccb2be9969a..f02a3852c92a 100644
--- a/core/java/com/android/internal/widget/remotecompose/core/documentation/Operation.java
+++ b/core/java/com/android/internal/widget/remotecompose/core/documentation/Operation.java
@@ -46,20 +46,30 @@ public class Operation {
int mExamplesWidth = 100;
int mExamplesHeight = 100;
-
public static String getType(int type) {
switch (type) {
- case (INT): return "INT";
- case (FLOAT): return "FLOAT";
- case (BOOLEAN): return "BOOLEAN";
- case (BUFFER): return "BUFFER";
- case (UTF8): return "UTF8";
- case (BYTE): return "BYTE";
- case (VALUE): return "VALUE";
- case (LONG): return "LONG";
- case (SHORT): return "SHORT";
- case (FLOAT_ARRAY): return "FLOAT[]";
- case (INT_ARRAY): return "INT[]";
+ case INT:
+ return "INT";
+ case FLOAT:
+ return "FLOAT";
+ case BOOLEAN:
+ return "BOOLEAN";
+ case BUFFER:
+ return "BUFFER";
+ case UTF8:
+ return "UTF8";
+ case BYTE:
+ return "BYTE";
+ case VALUE:
+ return "VALUE";
+ case LONG:
+ return "LONG";
+ case SHORT:
+ return "SHORT";
+ case FLOAT_ARRAY:
+ return "FLOAT[]";
+ case INT_ARRAY:
+ return "INT[]";
}
return "UNKNOWN";
}
diff --git a/core/java/com/android/internal/widget/remotecompose/core/documentation/OperationField.java b/core/java/com/android/internal/widget/remotecompose/core/documentation/OperationField.java
index 0dd3039f5f4d..c77048391405 100644
--- a/core/java/com/android/internal/widget/remotecompose/core/documentation/OperationField.java
+++ b/core/java/com/android/internal/widget/remotecompose/core/documentation/OperationField.java
@@ -41,20 +41,23 @@ public class OperationField {
public int getType() {
return mType;
}
+
public String getName() {
return mName;
}
+
public String getDescription() {
return mDescription;
}
+
public ArrayList<StringPair> getPossibleValues() {
return mPossibleValues;
}
-
public void possibleValue(String name, String value) {
mPossibleValues.add(new StringPair(name, value));
}
+
public boolean hasEnumeratedValues() {
return !mPossibleValues.isEmpty();
}
@@ -65,14 +68,22 @@ public class OperationField {
public int getSize() {
switch (mType) {
- case (Operation.BYTE) : return 1;
- case (Operation.INT) : return 4;
- case (Operation.FLOAT) : return 4;
- case (Operation.LONG) : return 8;
- case (Operation.SHORT) : return 2;
- case (Operation.INT_ARRAY): return -1;
- case (Operation.FLOAT_ARRAY): return -1;
- default : return 0;
+ case DocumentedOperation.BYTE:
+ return 1;
+ case DocumentedOperation.INT:
+ return 4;
+ case DocumentedOperation.FLOAT:
+ return 4;
+ case DocumentedOperation.LONG:
+ return 8;
+ case DocumentedOperation.SHORT:
+ return 2;
+ case DocumentedOperation.INT_ARRAY:
+ return -1;
+ case DocumentedOperation.FLOAT_ARRAY:
+ return -1;
+ default:
+ return 0;
}
}
}
diff --git a/core/java/com/android/internal/widget/remotecompose/core/documentation/StringPair.java b/core/java/com/android/internal/widget/remotecompose/core/documentation/StringPair.java
index 787bb54a5f0a..5b0cedbaf6af 100644
--- a/core/java/com/android/internal/widget/remotecompose/core/documentation/StringPair.java
+++ b/core/java/com/android/internal/widget/remotecompose/core/documentation/StringPair.java
@@ -14,6 +14,7 @@
* limitations under the License.
*/
package com.android.internal.widget.remotecompose.core.documentation;
+
public class StringPair {
String mName;
String mValue;
@@ -26,6 +27,7 @@ public class StringPair {
public String getName() {
return mName;
}
+
public String getValue() {
return mValue;
}
diff --git a/core/java/com/android/internal/widget/remotecompose/core/operations/BitmapData.java b/core/java/com/android/internal/widget/remotecompose/core/operations/BitmapData.java
index 58be641f608a..20ba8c313b47 100644
--- a/core/java/com/android/internal/widget/remotecompose/core/operations/BitmapData.java
+++ b/core/java/com/android/internal/widget/remotecompose/core/operations/BitmapData.java
@@ -15,8 +15,8 @@
*/
package com.android.internal.widget.remotecompose.core.operations;
-import static com.android.internal.widget.remotecompose.core.documentation.Operation.INT;
-import static com.android.internal.widget.remotecompose.core.documentation.Operation.INT_ARRAY;
+import static com.android.internal.widget.remotecompose.core.documentation.DocumentedOperation.INT;
+import static com.android.internal.widget.remotecompose.core.documentation.DocumentedOperation.INT_ARRAY;
import com.android.internal.widget.remotecompose.core.Operation;
import com.android.internal.widget.remotecompose.core.Operations;
@@ -24,14 +24,14 @@ import com.android.internal.widget.remotecompose.core.RemoteContext;
import com.android.internal.widget.remotecompose.core.SerializableToString;
import com.android.internal.widget.remotecompose.core.WireBuffer;
import com.android.internal.widget.remotecompose.core.documentation.DocumentationBuilder;
+import com.android.internal.widget.remotecompose.core.documentation.DocumentedOperation;
import com.android.internal.widget.remotecompose.core.operations.utilities.StringSerializer;
import java.util.List;
/**
- * Operation to deal with bitmap data
- * On getting an Image during a draw call the bitmap is compressed and saved
- * in playback the image is decompressed
+ * Operation to deal with bitmap data On getting an Image during a draw call the bitmap is
+ * compressed and saved in playback the image is decompressed
*/
public class BitmapData implements Operation, SerializableToString {
private static final int OP_CODE = Operations.DATA_BITMAP;
@@ -67,7 +67,6 @@ public class BitmapData implements Operation, SerializableToString {
return "BITMAP DATA " + mImageId;
}
-
public static String name() {
return CLASS_NAME;
}
@@ -84,7 +83,6 @@ public class BitmapData implements Operation, SerializableToString {
buffer.writeBuffer(bitmap);
}
-
public static void read(WireBuffer buffer, List<Operation> operations) {
int imageId = buffer.readInt();
int width = buffer.readInt();
@@ -99,19 +97,13 @@ public class BitmapData implements Operation, SerializableToString {
operations.add(new BitmapData(imageId, width, height, bitmap));
}
-
public static void documentation(DocumentationBuilder doc) {
- doc.operation("Data Operations",
- OP_CODE,
- CLASS_NAME)
+ doc.operation("Data Operations", OP_CODE, CLASS_NAME)
.description("Bitmap data")
- .field(INT, "id", "id of bitmap data")
- .field(INT, "width",
- "width of the image")
- .field(INT, "height",
- "height of the image")
- .field(INT_ARRAY, "values", "length",
- "Array of ints");
+ .field(DocumentedOperation.INT, "id", "id of bitmap data")
+ .field(INT, "width", "width of the image")
+ .field(INT, "height", "height of the image")
+ .field(INT_ARRAY, "values", "length", "Array of ints");
}
@Override
@@ -124,9 +116,10 @@ public class BitmapData implements Operation, SerializableToString {
return indent + toString();
}
+ @Override
public void serializeToString(int indent, StringSerializer serializer) {
- serializer.append(indent, CLASS_NAME
- + " id " + mImageId + " (" + mImageWidth + "x" + mImageHeight + ")");
+ serializer.append(
+ indent,
+ CLASS_NAME + " id " + mImageId + " (" + mImageWidth + "x" + mImageHeight + ")");
}
-
}
diff --git a/core/java/com/android/internal/widget/remotecompose/core/operations/ClickArea.java b/core/java/com/android/internal/widget/remotecompose/core/operations/ClickArea.java
index e72e24a0b85a..8b9e5a8d7625 100644
--- a/core/java/com/android/internal/widget/remotecompose/core/operations/ClickArea.java
+++ b/core/java/com/android/internal/widget/remotecompose/core/operations/ClickArea.java
@@ -15,20 +15,17 @@
*/
package com.android.internal.widget.remotecompose.core.operations;
-import static com.android.internal.widget.remotecompose.core.documentation.Operation.FLOAT;
-
import com.android.internal.widget.remotecompose.core.Operation;
import com.android.internal.widget.remotecompose.core.Operations;
import com.android.internal.widget.remotecompose.core.RemoteComposeOperation;
import com.android.internal.widget.remotecompose.core.RemoteContext;
import com.android.internal.widget.remotecompose.core.WireBuffer;
import com.android.internal.widget.remotecompose.core.documentation.DocumentationBuilder;
+import com.android.internal.widget.remotecompose.core.documentation.DocumentedOperation;
import java.util.List;
-/**
- * Add a click area to the document
- */
+/** Add a click area to the document */
public class ClickArea implements RemoteComposeOperation {
private static final int OP_CODE = Operations.CLICK_AREA;
private static final String CLASS_NAME = "ClickArea";
@@ -43,8 +40,8 @@ public class ClickArea implements RemoteComposeOperation {
/**
* Add a click area to the document
*
- * @param id the id of the click area, which will be reported in the listener
- * callback on the player
+ * @param id the id of the click area, which will be reported in the listener callback on the
+ * player
* @param contentDescription the content description (used for accessibility, as a textID)
* @param left left coordinate of the area bounds
* @param top top coordinate of the area bounds
@@ -52,10 +49,14 @@ public class ClickArea implements RemoteComposeOperation {
* @param bottom bottom coordinate of the area bounds
* @param metadata associated metadata, user-provided (as a textID, pointing to a string)
*/
- public ClickArea(int id, int contentDescription,
- float left, float top,
- float right, float bottom,
- int metadata) {
+ public ClickArea(
+ int id,
+ int contentDescription,
+ float left,
+ float top,
+ float right,
+ float bottom,
+ int metadata) {
this.mId = id;
this.mContentDescription = contentDescription;
this.mLeft = left;
@@ -72,10 +73,27 @@ public class ClickArea implements RemoteComposeOperation {
@Override
public String toString() {
- return "CLICK_AREA <" + mId + " <" + mContentDescription + "> "
- + "<" + mMetadata + ">+" + mLeft + " "
- + mTop + " " + mRight + " " + mBottom + "+"
- + " (" + (mRight - mLeft) + " x " + (mBottom - mTop) + " }";
+ return "CLICK_AREA <"
+ + mId
+ + " <"
+ + mContentDescription
+ + "> "
+ + "<"
+ + mMetadata
+ + ">+"
+ + mLeft
+ + " "
+ + mTop
+ + " "
+ + mRight
+ + " "
+ + mBottom
+ + "+"
+ + " ("
+ + (mRight - mLeft)
+ + " x "
+ + (mBottom - mTop)
+ + " }";
}
@Override
@@ -95,14 +113,19 @@ public class ClickArea implements RemoteComposeOperation {
return CLASS_NAME;
}
-
public static int id() {
return OP_CODE;
}
- public static void apply(WireBuffer buffer, int id, int contentDescription,
- float left, float top, float right, float bottom,
- int metadata) {
+ public static void apply(
+ WireBuffer buffer,
+ int id,
+ int contentDescription,
+ float left,
+ float top,
+ float right,
+ float bottom,
+ int metadata) {
buffer.start(OP_CODE);
buffer.writeInt(id);
buffer.writeInt(contentDescription);
@@ -113,7 +136,6 @@ public class ClickArea implements RemoteComposeOperation {
buffer.writeInt(metadata);
}
-
public static void read(WireBuffer buffer, List<Operation> operations) {
int id = buffer.readInt();
int contentDescription = buffer.readInt();
@@ -122,26 +144,21 @@ public class ClickArea implements RemoteComposeOperation {
float right = buffer.readFloat();
float bottom = buffer.readFloat();
int metadata = buffer.readInt();
- ClickArea clickArea = new ClickArea(id, contentDescription,
- left, top, right, bottom, metadata);
+ ClickArea clickArea =
+ new ClickArea(id, contentDescription, left, top, right, bottom, metadata);
operations.add(clickArea);
}
-
public static void documentation(DocumentationBuilder doc) {
- doc.operation("Canvas Operations",
- OP_CODE,
- CLASS_NAME)
+ doc.operation("Canvas Operations", OP_CODE, CLASS_NAME)
.description("Define a region you can click on")
- .field(FLOAT, "left",
- "The left side of the region")
- .field(FLOAT, "top",
- "The top of the region")
- .field(FLOAT, "right",
- "The right side of the region")
- .field(FLOAT, "bottom",
- "The bottom of the region")
- .field(FLOAT, "metadata",
+ .field(DocumentedOperation.FLOAT, "left", "The left side of the region")
+ .field(DocumentedOperation.FLOAT, "top", "The top of the region")
+ .field(DocumentedOperation.FLOAT, "right", "The right side of the region")
+ .field(DocumentedOperation.FLOAT, "bottom", "The bottom of the region")
+ .field(
+ DocumentedOperation.FLOAT,
+ "metadata",
"user defined string accessible in callback");
}
}
diff --git a/core/java/com/android/internal/widget/remotecompose/core/operations/ClipPath.java b/core/java/com/android/internal/widget/remotecompose/core/operations/ClipPath.java
index d77d53cca963..96b600acc971 100644
--- a/core/java/com/android/internal/widget/remotecompose/core/operations/ClipPath.java
+++ b/core/java/com/android/internal/widget/remotecompose/core/operations/ClipPath.java
@@ -15,21 +15,19 @@
*/
package com.android.internal.widget.remotecompose.core.operations;
-import static com.android.internal.widget.remotecompose.core.documentation.Operation.INT;
-
import com.android.internal.widget.remotecompose.core.Operation;
import com.android.internal.widget.remotecompose.core.Operations;
import com.android.internal.widget.remotecompose.core.PaintContext;
import com.android.internal.widget.remotecompose.core.PaintOperation;
import com.android.internal.widget.remotecompose.core.WireBuffer;
import com.android.internal.widget.remotecompose.core.documentation.DocumentationBuilder;
+import com.android.internal.widget.remotecompose.core.documentation.DocumentedOperation;
import java.util.List;
/**
- * Defines a path that clips a the subsequent drawing commands
- * Use MatrixSave and MatrixRestore commands to remove clip
- * TODO allow id 0 to mean null?
+ * Defines a path that clips a the subsequent drawing commands Use MatrixSave and MatrixRestore
+ * commands to remove clip TODO allow id 0 to mean null?
*/
public class ClipPath extends PaintOperation {
private static final int OP_CODE = Operations.CLIP_PATH;
@@ -68,7 +66,6 @@ public class ClipPath extends PaintOperation {
return "ClipPath " + mId + ";";
}
-
public static void read(WireBuffer buffer, List<Operation> operations) {
int pack = buffer.readInt();
int id = pack & 0xFFFFF;
@@ -77,12 +74,10 @@ public class ClipPath extends PaintOperation {
operations.add(op);
}
-
public static String name() {
return CLASS_NAME;
}
-
public static int id() {
return OP_CODE;
}
@@ -93,17 +88,13 @@ public class ClipPath extends PaintOperation {
}
public static void documentation(DocumentationBuilder doc) {
- doc.operation("Canvas Operations",
- OP_CODE,
- CLASS_NAME)
+ doc.operation("Canvas Operations", OP_CODE, CLASS_NAME)
.description("Intersect the current clip with the path")
- .field(INT, "id",
- "id of the path");
+ .field(DocumentedOperation.INT, "id", "id of the path");
}
-
@Override
public void paint(PaintContext context) {
context.clipPath(mId, mRegionOp);
}
-} \ No newline at end of file
+}
diff --git a/core/java/com/android/internal/widget/remotecompose/core/operations/ClipRect.java b/core/java/com/android/internal/widget/remotecompose/core/operations/ClipRect.java
index ec9b6fe483c9..b101bfb2d151 100644
--- a/core/java/com/android/internal/widget/remotecompose/core/operations/ClipRect.java
+++ b/core/java/com/android/internal/widget/remotecompose/core/operations/ClipRect.java
@@ -15,24 +15,20 @@
*/
package com.android.internal.widget.remotecompose.core.operations;
-import static com.android.internal.widget.remotecompose.core.documentation.Operation.FLOAT;
-
import com.android.internal.widget.remotecompose.core.Operation;
import com.android.internal.widget.remotecompose.core.Operations;
import com.android.internal.widget.remotecompose.core.PaintContext;
import com.android.internal.widget.remotecompose.core.WireBuffer;
import com.android.internal.widget.remotecompose.core.documentation.DocumentationBuilder;
+import com.android.internal.widget.remotecompose.core.documentation.DocumentedOperation;
import java.util.List;
-/**
- * Support clip with a rectangle
- */
+/** Support clip with a rectangle */
public class ClipRect extends DrawBase4 {
public static final int OP_CODE = Operations.CLIP_RECT;
public static final String CLASS_NAME = "ClipRect";
-
public static void read(WireBuffer buffer, List<Operation> operations) {
Maker m = ClipRect::new;
read(m, buffer, operations);
@@ -46,36 +42,33 @@ public class ClipRect extends DrawBase4 {
return CLASS_NAME;
}
-
- protected void write(WireBuffer buffer,
- float v1,
- float v2,
- float v3,
- float v4) {
+ @Override
+ protected void write(WireBuffer buffer, float v1, float v2, float v3, float v4) {
apply(buffer, v1, v2, v3, v4);
}
public static void documentation(DocumentationBuilder doc) {
- doc.operation("Expressions Operations",
- OP_CODE,
- CLASS_NAME)
+ doc.operation("Expressions Operations", OP_CODE, CLASS_NAME)
.description("Intersect the current clip with rectangle")
- .field(FLOAT, "left",
+ .field(
+ DocumentedOperation.FLOAT,
+ "left",
"The left side of the rectangle to intersect with the current clip")
- .field(FLOAT, "top",
+ .field(
+ DocumentedOperation.FLOAT,
+ "top",
"The top of the rectangle to intersect with the current clip")
- .field(FLOAT, "right",
+ .field(
+ DocumentedOperation.FLOAT,
+ "right",
"The right side of the rectangle to intersect with the current clip")
- .field(FLOAT, "bottom",
+ .field(
+ DocumentedOperation.FLOAT,
+ "bottom",
"The bottom of the rectangle to intersect with the current clip");
}
-
- public ClipRect(
- float left,
- float top,
- float right,
- float bottom) {
+ public ClipRect(float left, float top, float right, float bottom) {
super(left, top, right, bottom);
mName = CLASS_NAME;
}
@@ -89,16 +82,12 @@ public class ClipRect extends DrawBase4 {
* Writes out the clipRect to the buffer
*
* @param buffer buffer to write to
- * @param x1 start x of DrawOval
- * @param y1 start y of the DrawOval
- * @param x2 end x of the DrawOval
- * @param y2 end y of the DrawOval
+ * @param x1 start x of DrawOval
+ * @param y1 start y of the DrawOval
+ * @param x2 end x of the DrawOval
+ * @param y2 end y of the DrawOval
*/
- public static void apply(WireBuffer buffer,
- float x1,
- float y1,
- float x2,
- float y2) {
+ public static void apply(WireBuffer buffer, float x1, float y1, float x2, float y2) {
write(buffer, OP_CODE, x1, y1, x2, y2);
}
}
diff --git a/core/java/com/android/internal/widget/remotecompose/core/operations/ColorConstant.java b/core/java/com/android/internal/widget/remotecompose/core/operations/ColorConstant.java
index 2562e18b386c..19d80daf0c8f 100644
--- a/core/java/com/android/internal/widget/remotecompose/core/operations/ColorConstant.java
+++ b/core/java/com/android/internal/widget/remotecompose/core/operations/ColorConstant.java
@@ -15,20 +15,18 @@
*/
package com.android.internal.widget.remotecompose.core.operations;
-import static com.android.internal.widget.remotecompose.core.documentation.Operation.INT;
+import static com.android.internal.widget.remotecompose.core.documentation.DocumentedOperation.INT;
import com.android.internal.widget.remotecompose.core.Operation;
import com.android.internal.widget.remotecompose.core.Operations;
import com.android.internal.widget.remotecompose.core.RemoteContext;
import com.android.internal.widget.remotecompose.core.WireBuffer;
import com.android.internal.widget.remotecompose.core.documentation.DocumentationBuilder;
+import com.android.internal.widget.remotecompose.core.documentation.DocumentedOperation;
import java.util.List;
-/**
- * Operation that defines a simple Color based on ID
- * Mainly for colors in theming.
- */
+/** Operation that defines a simple Color based on ID Mainly for colors in theming. */
public class ColorConstant implements Operation {
private static final int OP_CODE = Operations.COLOR_CONSTANT;
private static final String CLASS_NAME = "ColorConstant";
@@ -78,14 +76,10 @@ public class ColorConstant implements Operation {
}
public static void documentation(DocumentationBuilder doc) {
- doc.operation("Expressions Operations",
- OP_CODE,
- CLASS_NAME)
+ doc.operation("Expressions Operations", OP_CODE, CLASS_NAME)
.description("Define a Color")
- .field(INT, "id",
- "Id of the color")
- .field(INT, "color",
- "32 bit ARGB color");
+ .field(DocumentedOperation.INT, "id", "Id of the color")
+ .field(INT, "color", "32 bit ARGB color");
}
@Override
diff --git a/core/java/com/android/internal/widget/remotecompose/core/operations/ColorExpression.java b/core/java/com/android/internal/widget/remotecompose/core/operations/ColorExpression.java
index 96d667465e8f..b6041eaeacdc 100644
--- a/core/java/com/android/internal/widget/remotecompose/core/operations/ColorExpression.java
+++ b/core/java/com/android/internal/widget/remotecompose/core/operations/ColorExpression.java
@@ -15,8 +15,8 @@
*/
package com.android.internal.widget.remotecompose.core.operations;
-import static com.android.internal.widget.remotecompose.core.documentation.Operation.FLOAT;
-import static com.android.internal.widget.remotecompose.core.documentation.Operation.INT;
+import static com.android.internal.widget.remotecompose.core.documentation.DocumentedOperation.FLOAT;
+import static com.android.internal.widget.remotecompose.core.documentation.DocumentedOperation.INT;
import com.android.internal.widget.remotecompose.core.Operation;
import com.android.internal.widget.remotecompose.core.Operations;
@@ -24,17 +24,13 @@ import com.android.internal.widget.remotecompose.core.RemoteContext;
import com.android.internal.widget.remotecompose.core.VariableSupport;
import com.android.internal.widget.remotecompose.core.WireBuffer;
import com.android.internal.widget.remotecompose.core.documentation.DocumentationBuilder;
+import com.android.internal.widget.remotecompose.core.documentation.DocumentedOperation;
import java.util.List;
/**
- * Operation to Colors
- * Color modes
- * mMode = 0 two colors and a tween
- * mMode = 1 color1 is a colorID.
- * mMode = 2 color2 is a colorID.
- * mMode = 3 color1 & color2 are ids
- * mMode = 4 H S V mode
+ * Operation to Colors Color modes mMode = 0 two colors and a tween mMode = 1 color1 is a colorID.
+ * mMode = 2 color2 is a colorID. mMode = 3 color1 & color2 are ids mMode = 4 H S V mode
*/
public class ColorExpression implements Operation, VariableSupport {
private static final int OP_CODE = Operations.COLOR_EXPRESSIONS;
@@ -45,7 +41,6 @@ public class ColorExpression implements Operation, VariableSupport {
public int mColor2;
public float mTween = 0.0f;
-
public float mHue = 0; // only in Mode 4
public float mSat = 0;
public float mValue = 0;
@@ -122,7 +117,6 @@ public class ColorExpression implements Operation, VariableSupport {
}
}
-
@Override
public void registerListening(RemoteContext context) {
if (mMode == 4) {
@@ -151,8 +145,8 @@ public class ColorExpression implements Operation, VariableSupport {
@Override
public void apply(RemoteContext context) {
if (mMode == 4) {
- context.loadColor(mId, (mAlpha << 24)
- | (0xFFFFFF & Utils.hsvToRgb(mOutHue, mOutSat, mOutValue)));
+ context.loadColor(
+ mId, (mAlpha << 24) | (0xFFFFFF & Utils.hsvToRgb(mOutHue, mOutSat, mOutValue)));
return;
}
if (mOutTween == 0.0) {
@@ -165,8 +159,7 @@ public class ColorExpression implements Operation, VariableSupport {
mOutColor2 = context.getColor(mColor2);
}
- context.loadColor(mId,
- Utils.interpolateColor(mOutColor1, mOutColor2, mOutTween));
+ context.loadColor(mId, Utils.interpolateColor(mOutColor1, mOutColor2, mOutTween));
}
}
@@ -179,23 +172,34 @@ public class ColorExpression implements Operation, VariableSupport {
@Override
public String toString() {
if (mMode == 4) {
- return "ColorExpression[" + mId + "] = hsv (" + Utils.floatToString(mHue)
- + ", " + Utils.floatToString(mSat)
- + ", " + Utils.floatToString(mValue) + ")";
+ return "ColorExpression["
+ + mId
+ + "] = hsv ("
+ + Utils.floatToString(mHue)
+ + ", "
+ + Utils.floatToString(mSat)
+ + ", "
+ + Utils.floatToString(mValue)
+ + ")";
}
String c1 = (mMode & 1) == 1 ? "[" + mColor1 + "]" : Utils.colorInt(mColor1);
String c2 = (mMode & 2) == 2 ? "[" + mColor2 + "]" : Utils.colorInt(mColor2);
- return "ColorExpression[" + mId + "] = tween(" + c1
- + ", " + c2 + ", "
- + Utils.floatToString(mTween) + ")";
+ return "ColorExpression["
+ + mId
+ + "] = tween("
+ + c1
+ + ", "
+ + c2
+ + ", "
+ + Utils.floatToString(mTween)
+ + ")";
}
public static String name() {
return CLASS_NAME;
}
-
public static int id() {
return OP_CODE;
}
@@ -204,22 +208,20 @@ public class ColorExpression implements Operation, VariableSupport {
* Call to write a ColorExpression object on the buffer
*
* @param buffer
- * @param id of the ColorExpression object
- * @param mode if colors are id or actual values
+ * @param id of the ColorExpression object
+ * @param mode if colors are id or actual values
* @param color1
* @param color2
* @param tween
*/
- public static void apply(WireBuffer buffer,
- int id, int mode,
- int color1, int color2, float tween) {
+ public static void apply(
+ WireBuffer buffer, int id, int mode, int color1, int color2, float tween) {
buffer.start(OP_CODE);
buffer.writeInt(id);
buffer.writeInt(mode);
buffer.writeInt(color1);
buffer.writeInt(color2);
buffer.writeFloat(tween);
-
}
public static void read(WireBuffer buffer, List<Operation> operations) {
@@ -233,29 +235,22 @@ public class ColorExpression implements Operation, VariableSupport {
}
public static void documentation(DocumentationBuilder doc) {
- doc.operation("Expressions Operations",
- OP_CODE,
- CLASS_NAME)
+ doc.operation("Expressions Operations", OP_CODE, CLASS_NAME)
.description("A Color defined by an expression")
- .field(INT, "id", "Id of the color")
+ .field(DocumentedOperation.INT, "id", "Id of the color")
.field(INT, "mode", "The use of the next 3 fields")
.possibleValues("COLOR_COLOR_INTERPOLATE", 0)
.possibleValues("COLOR_ID_INTERPOLATE", 1)
.possibleValues("ID_COLOR_INTERPOLATE", 2)
.possibleValues("ID_ID_INTERPOLATE", 3)
.possibleValues("HSV", 4)
- .field(INT, "color1",
- "32 bit ARGB color")
- .field(INT, "color2",
- "32 bit ARGB color")
- .field(FLOAT, "tween",
- "32 bit ARGB color");
-
+ .field(INT, "color1", "32 bit ARGB color")
+ .field(INT, "color2", "32 bit ARGB color")
+ .field(FLOAT, "tween", "32 bit ARGB color");
}
@Override
public String deepToString(String indent) {
return indent + toString();
}
-
}
diff --git a/core/java/com/android/internal/widget/remotecompose/core/operations/ComponentValue.java b/core/java/com/android/internal/widget/remotecompose/core/operations/ComponentValue.java
index 22fe673bc072..992972076839 100644
--- a/core/java/com/android/internal/widget/remotecompose/core/operations/ComponentValue.java
+++ b/core/java/com/android/internal/widget/remotecompose/core/operations/ComponentValue.java
@@ -15,7 +15,7 @@
*/
package com.android.internal.widget.remotecompose.core.operations;
-import static com.android.internal.widget.remotecompose.core.documentation.Operation.INT;
+import static com.android.internal.widget.remotecompose.core.documentation.DocumentedOperation.INT;
import com.android.internal.widget.remotecompose.core.Operation;
import com.android.internal.widget.remotecompose.core.Operations;
@@ -23,6 +23,7 @@ import com.android.internal.widget.remotecompose.core.RemoteContext;
import com.android.internal.widget.remotecompose.core.SerializableToString;
import com.android.internal.widget.remotecompose.core.WireBuffer;
import com.android.internal.widget.remotecompose.core.documentation.DocumentationBuilder;
+import com.android.internal.widget.remotecompose.core.documentation.DocumentedOperation;
import com.android.internal.widget.remotecompose.core.operations.utilities.StringSerializer;
import java.util.List;
@@ -82,15 +83,16 @@ public class ComponentValue implements Operation, SerializableToString {
}
public static void documentation(DocumentationBuilder doc) {
- doc.operation("Expressions Operations",
- OP_CODE,
- CLASS_NAME)
+ doc.operation("Expressions Operations", OP_CODE, CLASS_NAME)
.description("Encode a component-related value (eg its width, height etc.)")
- .field(INT, "TYPE",
+ .field(
+ DocumentedOperation.INT,
+ "TYPE",
"The type of value, either WIDTH(0) or HEIGHT(1)")
- .field(INT, "COMPONENT_ID",
- "The component id to reference")
- .field(INT, "VALUE_ID",
+ .field(INT, "COMPONENT_ID", "The component id to reference")
+ .field(
+ INT,
+ "VALUE_ID",
"The id of the RemoteFloat representing the described"
+ " component value, which can be used in expressions");
}
@@ -105,14 +107,11 @@ public class ComponentValue implements Operation, SerializableToString {
* Writes out the ComponentValue to the buffer
*
* @param buffer buffer to write to
- * @param type type of value (WIDTH or HEIGHT)
- * @param componentId component id to reference
- * @param valueId remote float used to represent the component value
+ * @param type type of value (WIDTH or HEIGHT)
+ * @param componentId component id to reference
+ * @param valueId remote float used to represent the component value
*/
- public static void apply(WireBuffer buffer,
- int type,
- int componentId,
- int valueId) {
+ public static void apply(WireBuffer buffer, int type, int componentId, int valueId) {
buffer.start(OP_CODE);
buffer.writeInt(type);
buffer.writeInt(componentId);
@@ -124,13 +123,20 @@ public class ComponentValue implements Operation, SerializableToString {
return null;
}
+ @Override
public void serializeToString(int indent, StringSerializer serializer) {
String type = "WIDTH";
if (mType == HEIGHT) {
type = "HEIGHT";
}
- serializer.append(indent, CLASS_NAME
- + " value " + mValueId + " set to "
- + type + " of Component " + mComponentID);
+ serializer.append(
+ indent,
+ CLASS_NAME
+ + " value "
+ + mValueId
+ + " set to "
+ + type
+ + " of Component "
+ + mComponentID);
}
}
diff --git a/core/java/com/android/internal/widget/remotecompose/core/operations/DataListFloat.java b/core/java/com/android/internal/widget/remotecompose/core/operations/DataListFloat.java
index edcb5fef7766..00758694c254 100644
--- a/core/java/com/android/internal/widget/remotecompose/core/operations/DataListFloat.java
+++ b/core/java/com/android/internal/widget/remotecompose/core/operations/DataListFloat.java
@@ -15,8 +15,8 @@
*/
package com.android.internal.widget.remotecompose.core.operations;
-import static com.android.internal.widget.remotecompose.core.documentation.Operation.FLOAT_ARRAY;
-import static com.android.internal.widget.remotecompose.core.documentation.Operation.INT;
+import static com.android.internal.widget.remotecompose.core.documentation.DocumentedOperation.FLOAT_ARRAY;
+import static com.android.internal.widget.remotecompose.core.documentation.DocumentedOperation.INT;
import com.android.internal.widget.remotecompose.core.Operation;
import com.android.internal.widget.remotecompose.core.Operations;
@@ -24,6 +24,7 @@ import com.android.internal.widget.remotecompose.core.RemoteContext;
import com.android.internal.widget.remotecompose.core.VariableSupport;
import com.android.internal.widget.remotecompose.core.WireBuffer;
import com.android.internal.widget.remotecompose.core.documentation.DocumentationBuilder;
+import com.android.internal.widget.remotecompose.core.documentation.DocumentedOperation;
import com.android.internal.widget.remotecompose.core.operations.utilities.ArrayAccess;
import java.util.Arrays;
@@ -63,7 +64,7 @@ public class DataListFloat implements VariableSupport, ArrayAccess, Operation {
@Override
public String toString() {
- return "DataListFloat[A_" + (mId & 0xFFFF) + "] " + Arrays.toString(mValues);
+ return "DataListFloat[" + Utils.idString(mId) + "] " + Arrays.toString(mValues);
}
public static void apply(WireBuffer buffer, int id, float[] values) {
@@ -90,14 +91,11 @@ public class DataListFloat implements VariableSupport, ArrayAccess, Operation {
}
public static void documentation(DocumentationBuilder doc) {
- doc.operation("Data Operations",
- OP_CODE,
- CLASS_NAME)
+ doc.operation("Data Operations", OP_CODE, CLASS_NAME)
.description("a list of Floats")
- .field(INT, "id", "id the array (2xxxxx)")
+ .field(DocumentedOperation.INT, "id", "id the array (2xxxxx)")
.field(INT, "length", "number of floats")
- .field(FLOAT_ARRAY, "values", "length",
- "array of floats");
+ .field(FLOAT_ARRAY, "values", "length", "array of floats");
}
@Override
@@ -121,7 +119,7 @@ public class DataListFloat implements VariableSupport, ArrayAccess, Operation {
}
@Override
- public int getFloatsLength() {
+ public int getLength() {
return mValues.length;
}
}
diff --git a/core/java/com/android/internal/widget/remotecompose/core/operations/DataListIds.java b/core/java/com/android/internal/widget/remotecompose/core/operations/DataListIds.java
index bde376e59d9c..c43dab4bbee0 100644
--- a/core/java/com/android/internal/widget/remotecompose/core/operations/DataListIds.java
+++ b/core/java/com/android/internal/widget/remotecompose/core/operations/DataListIds.java
@@ -15,8 +15,8 @@
*/
package com.android.internal.widget.remotecompose.core.operations;
-import static com.android.internal.widget.remotecompose.core.documentation.Operation.INT;
-import static com.android.internal.widget.remotecompose.core.documentation.Operation.INT_ARRAY;
+import static com.android.internal.widget.remotecompose.core.documentation.DocumentedOperation.INT;
+import static com.android.internal.widget.remotecompose.core.documentation.DocumentedOperation.INT_ARRAY;
import com.android.internal.widget.remotecompose.core.Operation;
import com.android.internal.widget.remotecompose.core.Operations;
@@ -24,39 +24,29 @@ import com.android.internal.widget.remotecompose.core.RemoteContext;
import com.android.internal.widget.remotecompose.core.VariableSupport;
import com.android.internal.widget.remotecompose.core.WireBuffer;
import com.android.internal.widget.remotecompose.core.documentation.DocumentationBuilder;
+import com.android.internal.widget.remotecompose.core.documentation.DocumentedOperation;
import com.android.internal.widget.remotecompose.core.operations.utilities.ArrayAccess;
import java.util.Arrays;
import java.util.List;
-public class DataListIds implements VariableSupport, ArrayAccess, Operation {
+public class DataListIds implements VariableSupport, ArrayAccess, Operation {
private static final int OP_CODE = Operations.ID_LIST;
private static final String CLASS_NAME = "IdListData";
int mId;
int[] mIds;
- float[] mValues;
private static final int MAX_LIST = 2000;
public DataListIds(int id, int[] ids) {
mId = id;
mIds = ids;
- mValues = new float[ids.length];
}
@Override
- public void updateVariables(RemoteContext context) {
- for (int i = 0; i < mIds.length; i++) {
- int id = mIds[i];
- mValues[i] = context.getFloat(id);
- }
- }
+ public void updateVariables(RemoteContext context) {}
@Override
- public void registerListening(RemoteContext context) {
- for (int mId : mIds) {
- context.listensTo(mId, this);
- }
- }
+ public void registerListening(RemoteContext context) {}
@Override
public void write(WireBuffer buffer) {
@@ -65,7 +55,7 @@ public class DataListIds implements VariableSupport, ArrayAccess, Operation {
@Override
public String toString() {
- return "map " + "\"" + Arrays.toString(mIds) + "\"";
+ return "map[" + Utils.idString(mId) + "] \"" + Arrays.toString(mIds) + "\"";
}
public static void apply(WireBuffer buffer, int id, int[] ids) {
@@ -92,15 +82,11 @@ public class DataListIds implements VariableSupport, ArrayAccess, Operation {
}
public static void documentation(DocumentationBuilder doc) {
- doc.operation("Data Operations",
- OP_CODE,
- CLASS_NAME)
+ doc.operation("Data Operations", OP_CODE, CLASS_NAME)
.description("a list of id's")
- .field(INT, "id", "id the array")
+ .field(DocumentedOperation.INT, "id", "id the array")
.field(INT, "length", "number of ids")
- .field(INT_ARRAY, "ids[n]", "length",
- "ids of other variables");
-
+ .field(INT_ARRAY, "ids[n]", "length", "ids of other variables");
}
@Override
@@ -115,16 +101,26 @@ public class DataListIds implements VariableSupport, ArrayAccess, Operation {
@Override
public float getFloatValue(int index) {
- return mValues[index];
+ return Float.NaN;
+ }
+
+ @Override
+ public int getId(int index) {
+ return mIds[index];
}
@Override
public float[] getFloats() {
- return mValues;
+ return null;
+ }
+
+ @Override
+ public int getLength() {
+ return mIds.length;
}
@Override
- public int getFloatsLength() {
- return mValues.length;
+ public int getIntValue(int index) {
+ return 0;
}
}
diff --git a/core/java/com/android/internal/widget/remotecompose/core/operations/DataMapIds.java b/core/java/com/android/internal/widget/remotecompose/core/operations/DataMapIds.java
index 53143dcf8211..75db29d2781e 100644
--- a/core/java/com/android/internal/widget/remotecompose/core/operations/DataMapIds.java
+++ b/core/java/com/android/internal/widget/remotecompose/core/operations/DataMapIds.java
@@ -15,78 +15,82 @@
*/
package com.android.internal.widget.remotecompose.core.operations;
-import static com.android.internal.widget.remotecompose.core.documentation.Operation.INT;
-import static com.android.internal.widget.remotecompose.core.documentation.Operation.UTF8;
+import static com.android.internal.widget.remotecompose.core.documentation.DocumentedOperation.INT;
+import static com.android.internal.widget.remotecompose.core.documentation.DocumentedOperation.UTF8;
import com.android.internal.widget.remotecompose.core.Operation;
import com.android.internal.widget.remotecompose.core.Operations;
import com.android.internal.widget.remotecompose.core.RemoteContext;
-import com.android.internal.widget.remotecompose.core.VariableSupport;
import com.android.internal.widget.remotecompose.core.WireBuffer;
import com.android.internal.widget.remotecompose.core.documentation.DocumentationBuilder;
-import com.android.internal.widget.remotecompose.core.operations.utilities.ArrayAccess;
+import com.android.internal.widget.remotecompose.core.operations.utilities.DataMap;
import java.util.List;
-public class DataMapIds implements VariableSupport, ArrayAccess, Operation {
+/** This is a map of strings to type & Id */
+public class DataMapIds implements Operation {
private static final int OP_CODE = Operations.ID_MAP;
- private static final String CLASS_NAME = "IdMapData";
+ private static final String CLASS_NAME = "DataMapIds";
int mId;
- String[] mNames;
- int[] mIds;
- float[] mValues;
- private static final int MAX_MAP = 2000;
-
- public DataMapIds(int id, String[] names, int[] ids) {
- mId = id;
- mNames = names;
- mIds = ids;
- mValues = new float[ids.length];
+ DataMap mDataMap;
- }
+ private static final int MAX_MAP = 2000;
- @Override
- public void updateVariables(RemoteContext context) {
- for (int i = 0; i < mIds.length; i++) {
- int id = mIds[i];
- mValues[i] = context.getFloat(id);
+ public static final byte TYPE_STRING = 0;
+ public static final byte TYPE_INT = 1;
+ public static final byte TYPE_FLOAT = 2;
+ public static final byte TYPE_LONG = 3;
+ public static final byte TYPE_BOOLEAN = 4;
+
+ private String typeString(byte type) {
+ switch (type) {
+ case TYPE_STRING:
+ return "String";
+ case TYPE_INT:
+ return "Int";
+ case TYPE_FLOAT:
+ return "Float";
+ case TYPE_LONG:
+ return "Long";
+ case TYPE_BOOLEAN:
+ return "Boolean";
}
+ return "?";
}
- @Override
- public void registerListening(RemoteContext context) {
- for (int mId : mIds) {
- context.listensTo(mId, this);
- }
+ public DataMapIds(int id, String[] names, byte[] types, int[] ids) {
+ mId = id;
+ mDataMap = new DataMap(names, types, ids);
}
@Override
public void write(WireBuffer buffer) {
- apply(buffer, mId, mNames, mIds);
+ apply(buffer, mId, mDataMap.mNames, mDataMap.mTypes, mDataMap.mIds);
}
@Override
public String toString() {
- StringBuilder builder = new StringBuilder("DataMapIds ");
- for (int i = 0; i < mNames.length; i++) {
+ StringBuilder builder = new StringBuilder("DataMapIds[" + Utils.idString(mId) + "] ");
+ for (int i = 0; i < mDataMap.mNames.length; i++) {
if (i != 0) {
builder.append(" ");
}
- builder.append(mNames[i]);
+ builder.append(typeString(mDataMap.mTypes[i]));
builder.append("[");
- builder.append(mIds[i]);
- builder.append("]");
-
+ builder.append(mDataMap.mNames[i]);
+ builder.append("]=");
+ builder.append(mDataMap.mIds[i]);
}
return builder.toString();
}
- public static void apply(WireBuffer buffer, int id, String[] names, int[] ids) {
+ public static void apply(WireBuffer buffer, int id, String[] names, byte[] type, int[] ids) {
buffer.start(OP_CODE);
buffer.writeInt(id);
buffer.writeInt(names.length);
for (int i = 0; i < names.length; i++) {
buffer.writeUTF8(names[i]);
+ buffer.writeByte(type == null ? 2 : type[i]);
buffer.writeInt(ids[i]);
}
}
@@ -99,25 +103,23 @@ public class DataMapIds implements VariableSupport, ArrayAccess, Operation {
}
String[] names = new String[len];
int[] ids = new int[len];
+ byte[] types = new byte[len];
for (int i = 0; i < names.length; i++) {
names[i] = buffer.readUTF8();
+ types[i] = (byte) buffer.readByte();
ids[i] = buffer.readInt();
}
- DataMapIds data = new DataMapIds(id, names, ids);
+ DataMapIds data = new DataMapIds(id, names, types, ids);
operations.add(data);
}
public static void documentation(DocumentationBuilder doc) {
- doc.operation("Data Operations",
- OP_CODE,
- CLASS_NAME)
+ doc.operation("Data Operations", OP_CODE, CLASS_NAME)
.description("Encode a collection of name id pairs")
.field(INT, "id", "id the array")
.field(INT, "length", "number of entries")
- .field(INT, "names[0]", "length",
- "path encoded as floats")
- .field(UTF8, "id[0]", "length",
- "path encoded as floats");
+ .field(INT, "names[0]", "length", "path encoded as floats")
+ .field(UTF8, "id[0]", "length", "path encoded as floats");
}
@Override
@@ -127,21 +129,6 @@ public class DataMapIds implements VariableSupport, ArrayAccess, Operation {
@Override
public void apply(RemoteContext context) {
- context.addCollection(mId, this);
- }
-
- @Override
- public float getFloatValue(int index) {
- return mValues[index];
- }
-
- @Override
- public float[] getFloats() {
- return mValues;
- }
-
- @Override
- public int getFloatsLength() {
- return mValues.length;
+ context.putDataMap(mId, mDataMap);
}
}
diff --git a/core/java/com/android/internal/widget/remotecompose/core/operations/DataMapLookup.java b/core/java/com/android/internal/widget/remotecompose/core/operations/DataMapLookup.java
new file mode 100644
index 000000000000..fb5e5d1734e6
--- /dev/null
+++ b/core/java/com/android/internal/widget/remotecompose/core/operations/DataMapLookup.java
@@ -0,0 +1,148 @@
+/*
+ * 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.
+ */
+package com.android.internal.widget.remotecompose.core.operations;
+
+import static com.android.internal.widget.remotecompose.core.documentation.DocumentedOperation.INT;
+
+import com.android.internal.widget.remotecompose.core.Operation;
+import com.android.internal.widget.remotecompose.core.Operations;
+import com.android.internal.widget.remotecompose.core.RemoteContext;
+import com.android.internal.widget.remotecompose.core.WireBuffer;
+import com.android.internal.widget.remotecompose.core.documentation.DocumentationBuilder;
+import com.android.internal.widget.remotecompose.core.operations.utilities.DataMap;
+import com.android.internal.widget.remotecompose.core.types.BooleanConstant;
+import com.android.internal.widget.remotecompose.core.types.LongConstant;
+
+import java.util.List;
+
+/** This can lookup in a map given a string writing the results to an id. */
+public class DataMapLookup implements Operation {
+ private static final int OP_CODE = Operations.DATA_MAP_LOOKUP;
+ private static final String CLASS_NAME = "DataMapLookup";
+ public int mId;
+ public int mDataMapId;
+ public int mStringId;
+
+ /**
+ * Create an access to a data map
+ *
+ * @param id of the output value
+ * @param dataMapId the id of the data map
+ * @param keyStringId the string to be looked up
+ */
+ public DataMapLookup(int id, int dataMapId, int keyStringId) {
+ this.mId = id;
+ this.mDataMapId = dataMapId;
+ this.mStringId = keyStringId;
+ }
+
+ @Override
+ public void write(WireBuffer buffer) {
+ apply(buffer, mId, mDataMapId, mStringId);
+ }
+
+ @Override
+ public String toString() {
+ return "DataMapLookup[" + mId + "] = " + Utils.idString(mDataMapId) + " " + mStringId;
+ }
+
+ /**
+ * The class name
+ *
+ * @return the name of the class
+ */
+ public static String name() {
+ return CLASS_NAME;
+ }
+
+ /**
+ * The opcode
+ *
+ * @return the opcode
+ */
+ public static int id() {
+ return OP_CODE;
+ }
+
+ /**
+ * Writes out the operation to the buffer
+ *
+ * @param buffer write command to this buffer
+ * @param id the id
+ * @param dataMapId the map to extract from
+ * @param keyStringId the map to extract from
+ */
+ public static void apply(WireBuffer buffer, int id, int dataMapId, int keyStringId) {
+ buffer.start(OP_CODE);
+ buffer.writeInt(id);
+ buffer.writeInt(dataMapId);
+ buffer.writeInt(keyStringId);
+ }
+
+ /**
+ * The read the buffer and create the command
+ *
+ * @param buffer buffer
+ * @param operations the created command is added to the list
+ */
+ public static void read(WireBuffer buffer, List<Operation> operations) {
+ int id = buffer.readInt();
+ int mapId = buffer.readInt();
+ int stringId = buffer.readInt();
+ operations.add(new DataMapLookup(id, mapId, stringId));
+ }
+
+ public static void documentation(DocumentationBuilder doc) {
+ doc.operation("Expressions Operations", OP_CODE, CLASS_NAME)
+ .description("A float and its associated id")
+ .field(INT, "id", "id of float")
+ .field(INT, "dataMapId", "32-bit float value")
+ .field(INT, "value", "32-bit float value");
+ }
+
+ @Override
+ public void apply(RemoteContext context) {
+ String str = context.getText(mStringId);
+ DataMap data = context.getDataMap(mDataMapId);
+ int pos = data.getPos(str);
+ byte type = data.getType(pos);
+ int dataId = data.getId(pos);
+ switch (type) {
+ case DataMapIds.TYPE_STRING:
+ context.loadText(mId, context.getText(dataId));
+ break;
+ case DataMapIds.TYPE_INT:
+ context.loadInteger(mId, context.getInteger(dataId));
+ break;
+ case DataMapIds.TYPE_FLOAT:
+ context.loadFloat(mId, context.getFloat(dataId));
+ break;
+ case DataMapIds.TYPE_LONG:
+ LongConstant lc = (LongConstant) context.getObject(dataId);
+ context.loadInteger(mId, (int) lc.getValue());
+ break;
+ case DataMapIds.TYPE_BOOLEAN:
+ BooleanConstant bc = (BooleanConstant) context.getObject(dataId);
+ context.loadInteger(mId, bc.getValue() ? 1 : 0);
+ break;
+ }
+ }
+
+ @Override
+ public String deepToString(String indent) {
+ return indent + toString();
+ }
+}
diff --git a/core/java/com/android/internal/widget/remotecompose/core/operations/DrawArc.java b/core/java/com/android/internal/widget/remotecompose/core/operations/DrawArc.java
index d37722917b27..e078307f3bd9 100644
--- a/core/java/com/android/internal/widget/remotecompose/core/operations/DrawArc.java
+++ b/core/java/com/android/internal/widget/remotecompose/core/operations/DrawArc.java
@@ -15,13 +15,12 @@
*/
package com.android.internal.widget.remotecompose.core.operations;
-import static com.android.internal.widget.remotecompose.core.documentation.Operation.FLOAT;
-
import com.android.internal.widget.remotecompose.core.Operation;
import com.android.internal.widget.remotecompose.core.Operations;
import com.android.internal.widget.remotecompose.core.PaintContext;
import com.android.internal.widget.remotecompose.core.WireBuffer;
import com.android.internal.widget.remotecompose.core.documentation.DocumentationBuilder;
+import com.android.internal.widget.remotecompose.core.documentation.DocumentedOperation;
import java.util.List;
@@ -40,6 +39,7 @@ public class DrawArc extends DrawBase6 {
/**
* Writes out the operation to the buffer
+ *
* @param buffer the buffer to write to
* @param v1 The left side of the Oval
* @param v2 The top of the Oval
@@ -48,13 +48,8 @@ public class DrawArc extends DrawBase6 {
* @param v5 Starting angle (in degrees) where the arc begins
* @param v6 Sweep angle (in degrees) measured clockwise
*/
- public static void apply(WireBuffer buffer,
- float v1,
- float v2,
- float v3,
- float v4,
- float v5,
- float v6) {
+ public static void apply(
+ WireBuffer buffer, float v1, float v2, float v3, float v4, float v5, float v6) {
buffer.start(OP_CODE);
buffer.writeFloat(v1);
buffer.writeFloat(v2);
@@ -64,47 +59,37 @@ public class DrawArc extends DrawBase6 {
buffer.writeFloat(v6);
}
- protected void write(WireBuffer buffer,
- float v1,
- float v2,
- float v3,
- float v4,
- float v5,
- float v6) {
+ @Override
+ protected void write(
+ WireBuffer buffer, float v1, float v2, float v3, float v4, float v5, float v6) {
apply(buffer, v1, v2, v3, v4, v5, v6);
}
public static void documentation(DocumentationBuilder doc) {
- doc.operation("Canvas Operations",
- OP_CODE,
- CLASS_NAME)
- .description("Draw the specified arc"
- + "which will be scaled to fit inside the specified oval")
- .field(FLOAT, "left",
- "The left side of the Oval")
- .field(FLOAT, "top",
- "The top of the Oval")
- .field(FLOAT, "right",
- "The right side of the Oval")
- .field(FLOAT, "bottom",
- "The bottom of the Oval")
- .field(FLOAT, "startAngle",
+ doc.operation("Canvas Operations", OP_CODE, CLASS_NAME)
+ .description(
+ "Draw the specified arc"
+ + "which will be scaled to fit inside the specified oval")
+ .field(DocumentedOperation.FLOAT, "left", "The left side of the Oval")
+ .field(DocumentedOperation.FLOAT, "top", "The top of the Oval")
+ .field(DocumentedOperation.FLOAT, "right", "The right side of the Oval")
+ .field(DocumentedOperation.FLOAT, "bottom", "The bottom of the Oval")
+ .field(
+ DocumentedOperation.FLOAT,
+ "startAngle",
"Starting angle (in degrees) where the arc begins")
- .field(FLOAT, "sweepAngle",
+ .field(
+ DocumentedOperation.FLOAT,
+ "sweepAngle",
"Sweep angle (in degrees) measured clockwise");
}
-
- public DrawArc(float v1,
- float v2,
- float v3,
- float v4,
- float v5,
- float v6) {
+ public DrawArc(float v1, float v2, float v3, float v4, float v5, float v6) {
super(v1, v2, v3, v4, v5, v6);
mName = "DrawArc";
}
+ @Override
public void paint(PaintContext context) {
context.drawArc(mV1, mV2, mV3, mV4, mV5, mV6);
}
diff --git a/core/java/com/android/internal/widget/remotecompose/core/operations/DrawBase2.java b/core/java/com/android/internal/widget/remotecompose/core/operations/DrawBase2.java
index 97eb76bfaaa9..c678cc4a36be 100644
--- a/core/java/com/android/internal/widget/remotecompose/core/operations/DrawBase2.java
+++ b/core/java/com/android/internal/widget/remotecompose/core/operations/DrawBase2.java
@@ -25,11 +25,8 @@ import com.android.internal.widget.remotecompose.core.WireBuffer;
import java.util.List;
-/**
- * Base class for commands that take 3 float
- */
-public abstract class DrawBase2 extends PaintOperation
- implements VariableSupport {
+/** Base class for commands that take 3 float */
+public abstract class DrawBase2 extends PaintOperation implements VariableSupport {
protected String mName = "DrawRectBase";
float mV1;
float mV2;
@@ -45,10 +42,8 @@ public abstract class DrawBase2 extends PaintOperation
@Override
public void updateVariables(RemoteContext context) {
- mV1 = (Float.isNaN(mValue1))
- ? context.getFloat(Utils.idFromNan(mValue1)) : mValue1;
- mV2 = (Float.isNaN(mValue2))
- ? context.getFloat(Utils.idFromNan(mValue2)) : mValue2;
+ mV1 = Float.isNaN(mValue1) ? context.getFloat(Utils.idFromNan(mValue1)) : mValue1;
+ mV2 = Float.isNaN(mValue2) ? context.getFloat(Utils.idFromNan(mValue2)) : mValue2;
}
@Override
@@ -61,7 +56,6 @@ public abstract class DrawBase2 extends PaintOperation
}
}
-
@Override
public void write(WireBuffer buffer) {
write(buffer, mV1, mV2);
@@ -70,8 +64,7 @@ public abstract class DrawBase2 extends PaintOperation
protected abstract void write(WireBuffer buffer, float v1, float v2);
protected interface Maker {
- DrawBase2 create(float v1,
- float v2);
+ DrawBase2 create(float v1, float v2);
}
@Override
@@ -79,7 +72,6 @@ public abstract class DrawBase2 extends PaintOperation
return mName + " " + floatToString(mV1) + " " + floatToString(mV2);
}
-
public static void read(Maker maker, WireBuffer buffer, List<Operation> operations) {
float v1 = buffer.readFloat();
float v2 = buffer.readFloat();
@@ -99,7 +91,6 @@ public abstract class DrawBase2 extends PaintOperation
return null;
}
-
/**
* Writes out the operation to the buffer
*
@@ -108,13 +99,9 @@ public abstract class DrawBase2 extends PaintOperation
* @param x1
* @param y1
*/
- protected static void write(WireBuffer buffer,
- int opCode,
- float x1,
- float y1) {
+ protected static void write(WireBuffer buffer, int opCode, float x1, float y1) {
buffer.start(opCode);
buffer.writeFloat(x1);
buffer.writeFloat(y1);
-
}
}
diff --git a/core/java/com/android/internal/widget/remotecompose/core/operations/DrawBase3.java b/core/java/com/android/internal/widget/remotecompose/core/operations/DrawBase3.java
index 2d1d3eb4b78c..e1108e906d8d 100644
--- a/core/java/com/android/internal/widget/remotecompose/core/operations/DrawBase3.java
+++ b/core/java/com/android/internal/widget/remotecompose/core/operations/DrawBase3.java
@@ -25,11 +25,8 @@ import com.android.internal.widget.remotecompose.core.WireBuffer;
import java.util.List;
-/**
- * Base class for commands that take 3 float
- */
-public abstract class DrawBase3 extends PaintOperation
- implements VariableSupport {
+/** Base class for commands that take 3 float */
+public abstract class DrawBase3 extends PaintOperation implements VariableSupport {
protected String mName = "DrawRectBase";
float mV1;
@@ -39,10 +36,7 @@ public abstract class DrawBase3 extends PaintOperation
float mValue2;
float mValue3;
- public DrawBase3(
- float v1,
- float v2,
- float v3) {
+ public DrawBase3(float v1, float v2, float v3) {
mValue1 = v1;
mValue2 = v2;
mValue3 = v3;
@@ -54,12 +48,9 @@ public abstract class DrawBase3 extends PaintOperation
@Override
public void updateVariables(RemoteContext context) {
- mV1 = (Utils.isVariable(mValue1))
- ? context.getFloat(Utils.idFromNan(mValue1)) : mValue1;
- mV2 = (Utils.isVariable(mValue2))
- ? context.getFloat(Utils.idFromNan(mValue2)) : mValue2;
- mV3 = (Utils.isVariable(mValue3))
- ? context.getFloat(Utils.idFromNan(mValue3)) : mValue3;
+ mV1 = Utils.isVariable(mValue1) ? context.getFloat(Utils.idFromNan(mValue1)) : mValue1;
+ mV2 = Utils.isVariable(mValue2) ? context.getFloat(Utils.idFromNan(mValue2)) : mValue2;
+ mV3 = Utils.isVariable(mValue3) ? context.getFloat(Utils.idFromNan(mValue3)) : mValue3;
}
@Override
@@ -80,21 +71,21 @@ public abstract class DrawBase3 extends PaintOperation
write(buffer, mV1, mV2, mV3);
}
- protected abstract void write(WireBuffer buffer,
- float v1,
- float v2,
- float v3);
+ protected abstract void write(WireBuffer buffer, float v1, float v2, float v3);
interface Maker {
- DrawBase3 create(float v1,
- float v2,
- float v3);
+ DrawBase3 create(float v1, float v2, float v3);
}
@Override
public String toString() {
- return mName + " " + floatToString(mV1) + " " + floatToString(mV2)
- + " " + floatToString(mV3);
+ return mName
+ + " "
+ + floatToString(mV1)
+ + " "
+ + floatToString(mV2)
+ + " "
+ + floatToString(mV3);
}
public static void read(Maker maker, WireBuffer buffer, List<Operation> operations) {
@@ -106,17 +97,14 @@ public abstract class DrawBase3 extends PaintOperation
}
/**
- * Construct and Operation from the 3 variables.
- * This must be overridden by subclasses
+ * Construct and Operation from the 3 variables. This must be overridden by subclasses
*
* @param x1
* @param y1
* @param x2
* @return
*/
- public Operation construct(float x1,
- float y1,
- float x2) {
+ public Operation construct(float x1, float y1, float x2) {
return null;
}
}
diff --git a/core/java/com/android/internal/widget/remotecompose/core/operations/DrawBase4.java b/core/java/com/android/internal/widget/remotecompose/core/operations/DrawBase4.java
index 943c5a44b5ac..09f0df985b5c 100644
--- a/core/java/com/android/internal/widget/remotecompose/core/operations/DrawBase4.java
+++ b/core/java/com/android/internal/widget/remotecompose/core/operations/DrawBase4.java
@@ -25,11 +25,8 @@ import com.android.internal.widget.remotecompose.core.WireBuffer;
import java.util.List;
-/**
- * Base class for draw commands that take 4 floats
- */
-public abstract class DrawBase4 extends PaintOperation
- implements VariableSupport {
+/** Base class for draw commands that take 4 floats */
+public abstract class DrawBase4 extends PaintOperation implements VariableSupport {
protected String mName = "DrawRectBase";
protected float mX1;
protected float mY1;
@@ -40,11 +37,7 @@ public abstract class DrawBase4 extends PaintOperation
float mX2Value;
float mY2Value;
- public DrawBase4(
- float x1,
- float y1,
- float x2,
- float y2) {
+ public DrawBase4(float x1, float y1, float x2, float y2) {
mX1Value = x1;
mY1Value = y1;
mX2Value = x2;
@@ -58,14 +51,10 @@ public abstract class DrawBase4 extends PaintOperation
@Override
public void updateVariables(RemoteContext context) {
- mX1 = (Float.isNaN(mX1Value))
- ? context.getFloat(Utils.idFromNan(mX1Value)) : mX1Value;
- mY1 = (Float.isNaN(mY1Value))
- ? context.getFloat(Utils.idFromNan(mY1Value)) : mY1Value;
- mX2 = (Float.isNaN(mX2Value))
- ? context.getFloat(Utils.idFromNan(mX2Value)) : mX2Value;
- mY2 = (Float.isNaN(mY2Value))
- ? context.getFloat(Utils.idFromNan(mY2Value)) : mY2Value;
+ mX1 = Float.isNaN(mX1Value) ? context.getFloat(Utils.idFromNan(mX1Value)) : mX1Value;
+ mY1 = Float.isNaN(mY1Value) ? context.getFloat(Utils.idFromNan(mY1Value)) : mY1Value;
+ mX2 = Float.isNaN(mX2Value) ? context.getFloat(Utils.idFromNan(mX2Value)) : mX2Value;
+ mY2 = Float.isNaN(mY2Value) ? context.getFloat(Utils.idFromNan(mY2Value)) : mY2Value;
}
@Override
@@ -89,23 +78,23 @@ public abstract class DrawBase4 extends PaintOperation
write(buffer, mX1, mY1, mX2, mY2);
}
- protected abstract void write(WireBuffer buffer,
- float v1,
- float v2,
- float v3,
- float v4);
+ protected abstract void write(WireBuffer buffer, float v1, float v2, float v3, float v4);
protected interface Maker {
- DrawBase4 create(float v1,
- float v2,
- float v3,
- float v4);
+ DrawBase4 create(float v1, float v2, float v3, float v4);
}
@Override
public String toString() {
- return mName + " " + floatToString(mX1Value, mX1) + " " + floatToString(mY1Value, mY1)
- + " " + floatToString(mX2Value, mX2) + " " + floatToString(mY2Value, mY2);
+ return mName
+ + " "
+ + floatToString(mX1Value, mX1)
+ + " "
+ + floatToString(mY1Value, mY1)
+ + " "
+ + floatToString(mX2Value, mX2)
+ + " "
+ + floatToString(mY2Value, mY2);
}
public static void read(Maker maker, WireBuffer buffer, List<Operation> operations) {
@@ -127,14 +116,10 @@ public abstract class DrawBase4 extends PaintOperation
* @param y2
* @return
*/
- public Operation construct(float x1,
- float y1,
- float x2,
- float y2) {
+ public Operation construct(float x1, float y1, float x2, float y2) {
return null;
}
-
/**
* Writes out the operation to the buffer
*
@@ -145,12 +130,8 @@ public abstract class DrawBase4 extends PaintOperation
* @param x2
* @param y2
*/
- protected static void write(WireBuffer buffer,
- int opCode,
- float x1,
- float y1,
- float x2,
- float y2) {
+ protected static void write(
+ WireBuffer buffer, int opCode, float x1, float y1, float x2, float y2) {
buffer.start(opCode);
buffer.writeFloat(x1);
buffer.writeFloat(y1);
@@ -158,4 +139,3 @@ public abstract class DrawBase4 extends PaintOperation
buffer.writeFloat(y2);
}
}
-
diff --git a/core/java/com/android/internal/widget/remotecompose/core/operations/DrawBase6.java b/core/java/com/android/internal/widget/remotecompose/core/operations/DrawBase6.java
index 767a36d03ace..e071d5f2096f 100644
--- a/core/java/com/android/internal/widget/remotecompose/core/operations/DrawBase6.java
+++ b/core/java/com/android/internal/widget/remotecompose/core/operations/DrawBase6.java
@@ -15,8 +15,6 @@
*/
package com.android.internal.widget.remotecompose.core.operations;
-import static com.android.internal.widget.remotecompose.core.operations.Utils.floatToString;
-
import com.android.internal.widget.remotecompose.core.Operation;
import com.android.internal.widget.remotecompose.core.PaintOperation;
import com.android.internal.widget.remotecompose.core.RemoteContext;
@@ -25,11 +23,8 @@ import com.android.internal.widget.remotecompose.core.WireBuffer;
import java.util.List;
-/**
- * Base class for draw commands the take 6 floats
- */
-public abstract class DrawBase6 extends PaintOperation
- implements VariableSupport {
+/** Base class for draw commands the take 6 floats */
+public abstract class DrawBase6 extends PaintOperation implements VariableSupport {
protected String mName = "DrawRectBase";
float mV1;
float mV2;
@@ -44,13 +39,7 @@ public abstract class DrawBase6 extends PaintOperation
float mValue5;
float mValue6;
- public DrawBase6(
- float v1,
- float v2,
- float v3,
- float v4,
- float v5,
- float v6) {
+ public DrawBase6(float v1, float v2, float v3, float v4, float v5, float v6) {
mValue1 = v1;
mValue2 = v2;
mValue3 = v3;
@@ -68,18 +57,12 @@ public abstract class DrawBase6 extends PaintOperation
@Override
public void updateVariables(RemoteContext context) {
- mV1 = (Float.isNaN(mValue1))
- ? context.getFloat(Utils.idFromNan(mValue1)) : mValue1;
- mV2 = (Float.isNaN(mValue2))
- ? context.getFloat(Utils.idFromNan(mValue2)) : mValue2;
- mV3 = (Float.isNaN(mValue3))
- ? context.getFloat(Utils.idFromNan(mValue3)) : mValue3;
- mV4 = (Float.isNaN(mValue4))
- ? context.getFloat(Utils.idFromNan(mValue4)) : mValue4;
- mV5 = (Float.isNaN(mValue5))
- ? context.getFloat(Utils.idFromNan(mValue5)) : mValue5;
- mV6 = (Float.isNaN(mValue6))
- ? context.getFloat(Utils.idFromNan(mValue6)) : mValue6;
+ mV1 = Float.isNaN(mValue1) ? context.getFloat(Utils.idFromNan(mValue1)) : mValue1;
+ mV2 = Float.isNaN(mValue2) ? context.getFloat(Utils.idFromNan(mValue2)) : mValue2;
+ mV3 = Float.isNaN(mValue3) ? context.getFloat(Utils.idFromNan(mValue3)) : mValue3;
+ mV4 = Float.isNaN(mValue4) ? context.getFloat(Utils.idFromNan(mValue4)) : mValue4;
+ mV5 = Float.isNaN(mValue5) ? context.getFloat(Utils.idFromNan(mValue5)) : mValue5;
+ mV6 = Float.isNaN(mValue6) ? context.getFloat(Utils.idFromNan(mValue6)) : mValue6;
}
@Override
@@ -109,27 +92,24 @@ public abstract class DrawBase6 extends PaintOperation
write(buffer, mV1, mV2, mV3, mV4, mV5, mV6);
}
- protected abstract void write(WireBuffer buffer,
- float v1,
- float v2,
- float v3,
- float v4,
- float v5,
- float v6);
+ protected abstract void write(
+ WireBuffer buffer, float v1, float v2, float v3, float v4, float v5, float v6);
@Override
public String toString() {
- return mName + " " + floatToString(mV1) + " " + floatToString(mV2)
- + " " + floatToString(mV3) + " " + floatToString(mV4);
+ return mName
+ + " "
+ + Utils.floatToString(mV1)
+ + " "
+ + Utils.floatToString(mV2)
+ + " "
+ + Utils.floatToString(mV3)
+ + " "
+ + Utils.floatToString(mV4);
}
interface Maker {
- DrawBase6 create(float v1,
- float v2,
- float v3,
- float v4,
- float v5,
- float v6);
+ DrawBase6 create(float v1, float v2, float v3, float v4, float v5, float v6);
}
public static void read(Maker build, WireBuffer buffer, List<Operation> operations) {
@@ -155,18 +135,11 @@ public abstract class DrawBase6 extends PaintOperation
* @param v6
* @return
*/
- public Operation construct(float v1,
- float v2,
- float v3,
- float v4,
- float v5,
- float v6) {
+ public Operation construct(float v1, float v2, float v3, float v4, float v5, float v6) {
return null;
}
-
public static String name() {
return "DrawBase6";
}
-
}
diff --git a/core/java/com/android/internal/widget/remotecompose/core/operations/DrawBitmap.java b/core/java/com/android/internal/widget/remotecompose/core/operations/DrawBitmap.java
index 2748f4c3c369..0b43fd24556a 100644
--- a/core/java/com/android/internal/widget/remotecompose/core/operations/DrawBitmap.java
+++ b/core/java/com/android/internal/widget/remotecompose/core/operations/DrawBitmap.java
@@ -15,8 +15,8 @@
*/
package com.android.internal.widget.remotecompose.core.operations;
-import static com.android.internal.widget.remotecompose.core.documentation.Operation.FLOAT;
-import static com.android.internal.widget.remotecompose.core.documentation.Operation.INT;
+import static com.android.internal.widget.remotecompose.core.documentation.DocumentedOperation.FLOAT;
+import static com.android.internal.widget.remotecompose.core.documentation.DocumentedOperation.INT;
import com.android.internal.widget.remotecompose.core.Operation;
import com.android.internal.widget.remotecompose.core.Operations;
@@ -44,12 +44,7 @@ public class DrawBitmap extends PaintOperation implements VariableSupport {
int mDescriptionId = 0;
public DrawBitmap(
- int imageId,
- float left,
- float top,
- float right,
- float bottom,
- int descriptionId) {
+ int imageId, float left, float top, float right, float bottom, int descriptionId) {
mLeft = left;
mTop = top;
mRight = right;
@@ -60,14 +55,10 @@ public class DrawBitmap extends PaintOperation implements VariableSupport {
@Override
public void updateVariables(RemoteContext context) {
- mOutputLeft = (Float.isNaN(mLeft))
- ? context.getFloat(Utils.idFromNan(mLeft)) : mLeft;
- mOutputTop = (Float.isNaN(mTop))
- ? context.getFloat(Utils.idFromNan(mTop)) : mTop;
- mOutputRight = (Float.isNaN(mRight))
- ? context.getFloat(Utils.idFromNan(mRight)) : mRight;
- mOutputBottom = (Float.isNaN(mBottom))
- ? context.getFloat(Utils.idFromNan(mBottom)) : mBottom;
+ mOutputLeft = Float.isNaN(mLeft) ? context.getFloat(Utils.idFromNan(mLeft)) : mLeft;
+ mOutputTop = Float.isNaN(mTop) ? context.getFloat(Utils.idFromNan(mTop)) : mTop;
+ mOutputRight = Float.isNaN(mRight) ? context.getFloat(Utils.idFromNan(mRight)) : mRight;
+ mOutputBottom = Float.isNaN(mBottom) ? context.getFloat(Utils.idFromNan(mBottom)) : mBottom;
}
@Override
@@ -93,8 +84,17 @@ public class DrawBitmap extends PaintOperation implements VariableSupport {
@Override
public String toString() {
- return "DrawBitmap (desc=" + mDescriptionId + ")" + mLeft + " " + mTop
- + " " + mRight + " " + mBottom + ";";
+ return "DrawBitmap (desc="
+ + mDescriptionId
+ + ")"
+ + mLeft
+ + " "
+ + mTop
+ + " "
+ + mRight
+ + " "
+ + mBottom
+ + ";";
}
public static void read(WireBuffer buffer, List<Operation> operations) {
@@ -117,13 +117,14 @@ public class DrawBitmap extends PaintOperation implements VariableSupport {
return OP_CODE;
}
- public static void apply(WireBuffer buffer,
- int id,
- float left,
- float top,
- float right,
- float bottom,
- int descriptionId) {
+ public static void apply(
+ WireBuffer buffer,
+ int id,
+ float left,
+ float top,
+ float right,
+ float bottom,
+ int descriptionId) {
buffer.start(Operations.DRAW_BITMAP);
buffer.writeInt(id);
buffer.writeFloat(left);
@@ -134,26 +135,18 @@ public class DrawBitmap extends PaintOperation implements VariableSupport {
}
public static void documentation(DocumentationBuilder doc) {
- doc.operation("Draw Operations",
- OP_CODE,
- CLASS_NAME)
+ doc.operation("Draw Operations", OP_CODE, CLASS_NAME)
.description("Draw a bitmap")
.field(INT, "id", "id of float")
- .field(FLOAT, "left",
- "The left side of the image")
- .field(FLOAT, "top",
- "The top of the image")
- .field(FLOAT, "right",
- "The right side of the image")
- .field(FLOAT, "bottom",
- "The bottom of the image")
+ .field(FLOAT, "left", "The left side of the image")
+ .field(FLOAT, "top", "The top of the image")
+ .field(FLOAT, "right", "The right side of the image")
+ .field(FLOAT, "bottom", "The bottom of the image")
.field(INT, "descriptionId", "id of string");
}
+ @Override
public void paint(PaintContext context) {
- context.drawBitmap(mId, mOutputLeft,
- mOutputTop,
- mOutputRight,
- mOutputBottom);
+ context.drawBitmap(mId, mOutputLeft, mOutputTop, mOutputRight, mOutputBottom);
}
}
diff --git a/core/java/com/android/internal/widget/remotecompose/core/operations/DrawBitmapInt.java b/core/java/com/android/internal/widget/remotecompose/core/operations/DrawBitmapInt.java
index 561d52720a67..fc7482759369 100644
--- a/core/java/com/android/internal/widget/remotecompose/core/operations/DrawBitmapInt.java
+++ b/core/java/com/android/internal/widget/remotecompose/core/operations/DrawBitmapInt.java
@@ -15,20 +15,17 @@
*/
package com.android.internal.widget.remotecompose.core.operations;
-import static com.android.internal.widget.remotecompose.core.documentation.Operation.INT;
-
import com.android.internal.widget.remotecompose.core.Operation;
import com.android.internal.widget.remotecompose.core.Operations;
import com.android.internal.widget.remotecompose.core.PaintContext;
import com.android.internal.widget.remotecompose.core.PaintOperation;
import com.android.internal.widget.remotecompose.core.WireBuffer;
import com.android.internal.widget.remotecompose.core.documentation.DocumentationBuilder;
+import com.android.internal.widget.remotecompose.core.documentation.DocumentedOperation;
import java.util.List;
-/**
- * Operation to draw a given cached bitmap
- */
+/** Operation to draw a given cached bitmap */
public class DrawBitmapInt extends PaintOperation {
private static final int OP_CODE = Operations.DRAW_BITMAP_INT;
private static final String CLASS_NAME = "DrawBitmapInt";
@@ -43,16 +40,17 @@ public class DrawBitmapInt extends PaintOperation {
int mDstBottom;
int mContentDescId = 0;
- public DrawBitmapInt(int imageId,
- int srcLeft,
- int srcTop,
- int srcRight,
- int srcBottom,
- int dstLeft,
- int dstTop,
- int dstRight,
- int dstBottom,
- int cdId) {
+ public DrawBitmapInt(
+ int imageId,
+ int srcLeft,
+ int srcTop,
+ int srcRight,
+ int srcBottom,
+ int dstLeft,
+ int dstTop,
+ int dstRight,
+ int dstBottom,
+ int cdId) {
this.mImageId = imageId;
this.mSrcLeft = srcLeft;
this.mSrcTop = srcTop;
@@ -67,18 +65,44 @@ public class DrawBitmapInt extends PaintOperation {
@Override
public void write(WireBuffer buffer) {
- apply(buffer, mImageId, mSrcLeft, mSrcTop, mSrcRight, mSrcBottom,
- mDstLeft, mDstTop, mDstRight, mDstBottom, mContentDescId);
+ apply(
+ buffer,
+ mImageId,
+ mSrcLeft,
+ mSrcTop,
+ mSrcRight,
+ mSrcBottom,
+ mDstLeft,
+ mDstTop,
+ mDstRight,
+ mDstBottom,
+ mContentDescId);
}
@Override
public String toString() {
- return "DRAW_BITMAP_INT " + mImageId + " on " + mSrcLeft + " " + mSrcTop
- + " " + mSrcRight + " " + mSrcBottom + " "
- + "- " + mDstLeft + " " + mDstTop + " " + mDstRight + " " + mDstBottom + ";";
+ return "DRAW_BITMAP_INT "
+ + mImageId
+ + " on "
+ + mSrcLeft
+ + " "
+ + mSrcTop
+ + " "
+ + mSrcRight
+ + " "
+ + mSrcBottom
+ + " "
+ + "- "
+ + mDstLeft
+ + " "
+ + mDstTop
+ + " "
+ + mDstRight
+ + " "
+ + mDstBottom
+ + ";";
}
-
public static String name() {
return CLASS_NAME;
}
@@ -87,10 +111,18 @@ public class DrawBitmapInt extends PaintOperation {
return OP_CODE;
}
- public static void apply(WireBuffer buffer, int imageId,
- int srcLeft, int srcTop, int srcRight, int srcBottom,
- int dstLeft, int dstTop, int dstRight, int dstBottom,
- int cdId) {
+ public static void apply(
+ WireBuffer buffer,
+ int imageId,
+ int srcLeft,
+ int srcTop,
+ int srcRight,
+ int srcBottom,
+ int dstLeft,
+ int dstTop,
+ int dstRight,
+ int dstBottom,
+ int cdId) {
buffer.start(Operations.DRAW_BITMAP_INT);
buffer.writeInt(imageId);
buffer.writeInt(srcLeft);
@@ -115,41 +147,41 @@ public class DrawBitmapInt extends PaintOperation {
int dstRight = buffer.readInt();
int dstBottom = buffer.readInt();
int cdId = buffer.readInt();
- DrawBitmapInt op = new DrawBitmapInt(imageId, sLeft, srcTop, srcRight, srcBottom,
- dstLeft, dstTop, dstRight, dstBottom, cdId);
+ DrawBitmapInt op =
+ new DrawBitmapInt(
+ imageId, sLeft, srcTop, srcRight, srcBottom, dstLeft, dstTop, dstRight,
+ dstBottom, cdId);
operations.add(op);
}
-
public static void documentation(DocumentationBuilder doc) {
- doc.operation("Draw Operations",
- OP_CODE,
- CLASS_NAME)
+ doc.operation("Draw Operations", OP_CODE, CLASS_NAME)
.description("Draw a bitmap using integer coordinates")
- .field(INT, "id", "id of bitmap")
- .field(INT, "srcLeft",
- "The left side of the image")
- .field(INT, "srcTop",
- "The top of the image")
- .field(INT, "srcRight",
- "The right side of the image")
- .field(INT, "srcBottom",
- "The bottom of the image")
- .field(INT, "dstLeft",
- "The left side of the image")
- .field(INT, "dstTop",
- "The top of the image")
- .field(INT, "dstRight",
- "The right side of the image")
- .field(INT, "dstBottom",
- "The bottom of the image")
- .field(INT, "cdId", "id of string");
+ .field(DocumentedOperation.INT, "id", "id of bitmap")
+ .field(DocumentedOperation.INT, "srcLeft", "The left side of the image")
+ .field(DocumentedOperation.INT, "srcTop", "The top of the image")
+ .field(DocumentedOperation.INT, "srcRight", "The right side of the image")
+ .field(DocumentedOperation.INT, "srcBottom", "The bottom of the image")
+ .field(DocumentedOperation.INT, "dstLeft", "The left side of the image")
+ .field(DocumentedOperation.INT, "dstTop", "The top of the image")
+ .field(DocumentedOperation.INT, "dstRight", "The right side of the image")
+ .field(DocumentedOperation.INT, "dstBottom", "The bottom of the image")
+ .field(DocumentedOperation.INT, "cdId", "id of string");
}
@Override
public void paint(PaintContext context) {
- context.drawBitmap(mImageId, mSrcLeft, mSrcTop, mSrcRight, mSrcBottom,
- mDstLeft, mDstTop, mDstRight, mDstBottom, mContentDescId);
+ context.drawBitmap(
+ mImageId,
+ mSrcLeft,
+ mSrcTop,
+ mSrcRight,
+ mSrcBottom,
+ mDstLeft,
+ mDstTop,
+ mDstRight,
+ mDstBottom,
+ mContentDescId);
}
}
diff --git a/core/java/com/android/internal/widget/remotecompose/core/operations/DrawBitmapScaled.java b/core/java/com/android/internal/widget/remotecompose/core/operations/DrawBitmapScaled.java
new file mode 100644
index 000000000000..22742c63dd2d
--- /dev/null
+++ b/core/java/com/android/internal/widget/remotecompose/core/operations/DrawBitmapScaled.java
@@ -0,0 +1,319 @@
+/*
+ * Copyright (C) 2023 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.internal.widget.remotecompose.core.operations;
+
+import com.android.internal.widget.remotecompose.core.Operation;
+import com.android.internal.widget.remotecompose.core.Operations;
+import com.android.internal.widget.remotecompose.core.PaintContext;
+import com.android.internal.widget.remotecompose.core.PaintOperation;
+import com.android.internal.widget.remotecompose.core.RemoteContext;
+import com.android.internal.widget.remotecompose.core.VariableSupport;
+import com.android.internal.widget.remotecompose.core.WireBuffer;
+import com.android.internal.widget.remotecompose.core.documentation.DocumentationBuilder;
+import com.android.internal.widget.remotecompose.core.documentation.DocumentedOperation;
+import com.android.internal.widget.remotecompose.core.operations.utilities.ImageScaling;
+
+import java.util.List;
+
+/** Operation to draw a given cached bitmap */
+public class DrawBitmapScaled extends PaintOperation implements VariableSupport {
+ private static final int OP_CODE = Operations.DRAW_BITMAP_SCALED;
+ private static final String CLASS_NAME = "DrawBitmapScaled";
+ int mImageId;
+ float mSrcLeft, mOutSrcLeft;
+ float mSrcTop, mOutSrcTop;
+ float mSrcRight, mOutSrcRight;
+ float mSrcBottom, mOutSrcBottom;
+ float mDstLeft, mOutDstLeft;
+ float mDstTop, mOutDstTop;
+ float mDstRight, mOutDstRight;
+ float mDstBottom, mOutDstBottom;
+ int mContentDescId;
+ float mScaleFactor, mOutScaleFactor;
+ int mScaleType;
+
+ ImageScaling mScaling = new ImageScaling();
+ public static final int SCALE_NONE = ImageScaling.SCALE_NONE;
+ public static final int SCALE_INSIDE = ImageScaling.SCALE_INSIDE;
+ public static final int SCALE_FILL_WIDTH = ImageScaling.SCALE_FILL_WIDTH;
+ public static final int SCALE_FILL_HEIGHT = ImageScaling.SCALE_FILL_HEIGHT;
+ public static final int SCALE_FIT = ImageScaling.SCALE_FIT;
+ public static final int SCALE_CROP = ImageScaling.SCALE_CROP;
+ public static final int SCALE_FILL_BOUNDS = ImageScaling.SCALE_FILL_BOUNDS;
+ public static final int SCALE_FIXED_SCALE = ImageScaling.SCALE_FIXED_SCALE;
+
+ public DrawBitmapScaled(
+ int imageId,
+ float srcLeft,
+ float srcTop,
+ float srcRight,
+ float srcBottom,
+ float dstLeft,
+ float dstTop,
+ float dstRight,
+ float dstBottom,
+ int type,
+ float scale,
+ int cdId) {
+ this.mImageId = imageId;
+ mOutSrcLeft = mSrcLeft = srcLeft;
+ mOutSrcTop = mSrcTop = srcTop;
+ mOutSrcRight = mSrcRight = srcRight;
+ mOutSrcBottom = mSrcBottom = srcBottom;
+ mOutDstLeft = mDstLeft = dstLeft;
+ mOutDstTop = mDstTop = dstTop;
+ mOutDstRight = mDstRight = dstRight;
+ mOutDstBottom = mDstBottom = dstBottom;
+ mScaleType = type;
+ mOutScaleFactor = mScaleFactor = scale;
+ this.mContentDescId = cdId;
+ }
+
+ @Override
+ public void updateVariables(RemoteContext context) {
+ mOutSrcLeft =
+ Float.isNaN(mSrcLeft) ? context.getFloat(Utils.idFromNan(mSrcLeft)) : mSrcLeft;
+ mOutSrcTop = Float.isNaN(mSrcTop) ? context.getFloat(Utils.idFromNan(mSrcTop)) : mSrcTop;
+ mOutSrcRight =
+ Float.isNaN(mSrcRight) ? context.getFloat(Utils.idFromNan(mSrcRight)) : mSrcRight;
+ mOutSrcBottom =
+ Float.isNaN(mSrcBottom)
+ ? context.getFloat(Utils.idFromNan(mSrcBottom))
+ : mSrcBottom;
+ mOutDstLeft =
+ Float.isNaN(mDstLeft) ? context.getFloat(Utils.idFromNan(mDstLeft)) : mDstLeft;
+ mOutDstTop = Float.isNaN(mDstTop) ? context.getFloat(Utils.idFromNan(mDstTop)) : mDstTop;
+ mOutDstRight =
+ Float.isNaN(mDstRight) ? context.getFloat(Utils.idFromNan(mDstRight)) : mDstRight;
+ mOutDstBottom =
+ Float.isNaN(mDstBottom)
+ ? context.getFloat(Utils.idFromNan(mDstBottom))
+ : mDstBottom;
+ mOutScaleFactor =
+ Float.isNaN(mScaleFactor)
+ ? context.getFloat(Utils.idFromNan(mScaleFactor))
+ : mScaleFactor;
+ }
+
+ @Override
+ public void registerListening(RemoteContext context) {
+ register(context, mSrcLeft);
+ register(context, mSrcTop);
+ register(context, mSrcRight);
+ register(context, mSrcBottom);
+ register(context, mDstLeft);
+ register(context, mDstTop);
+ register(context, mDstRight);
+ register(context, mDstBottom);
+ register(context, mScaleFactor);
+ }
+
+ private void register(RemoteContext context, float value) {
+ if (Float.isNaN(value)) {
+ context.listensTo(Utils.idFromNan(value), this);
+ }
+ }
+
+ static String str(float v) {
+ String s = " " + (int) v;
+ return s.substring(s.length() - 3);
+ }
+
+ void print(String str, float left, float top, float right, float bottom) {
+ String s = str;
+ s += str(left) + ", " + str(top) + ", " + str(right) + ", " + str(bottom) + ", ";
+ s += " [" + str(right - left) + " x " + str(bottom - top) + "]";
+ System.out.println(s);
+ }
+
+ @Override
+ public void write(WireBuffer buffer) {
+ apply(
+ buffer,
+ mImageId,
+ mSrcLeft,
+ mSrcTop,
+ mSrcRight,
+ mSrcBottom,
+ mDstLeft,
+ mDstTop,
+ mDstRight,
+ mDstBottom,
+ mScaleType,
+ mScaleFactor,
+ mContentDescId);
+ }
+
+ @Override
+ public String toString() {
+ return "DrawBitmapScaled "
+ + mImageId
+ + " ["
+ + Utils.floatToString(mSrcLeft, mOutSrcLeft)
+ + " "
+ + Utils.floatToString(mSrcTop, mOutSrcTop)
+ + " "
+ + Utils.floatToString(mSrcRight, mOutSrcRight)
+ + " "
+ + Utils.floatToString(mSrcBottom, mOutSrcBottom)
+ + "] "
+ + "- ["
+ + Utils.floatToString(mDstLeft, mOutDstLeft)
+ + " "
+ + Utils.floatToString(mDstTop, mOutDstTop)
+ + " "
+ + Utils.floatToString(mDstRight, mOutDstRight)
+ + " "
+ + Utils.floatToString(mDstBottom, mOutDstBottom)
+ + "] "
+ + " "
+ + mScaleType
+ + " "
+ + Utils.floatToString(mScaleFactor, mOutScaleFactor);
+ }
+
+ public static String name() {
+ return CLASS_NAME;
+ }
+
+ public static int id() {
+ return OP_CODE;
+ }
+
+ public static void apply(
+ WireBuffer buffer,
+ int imageId,
+ float srcLeft,
+ float srcTop,
+ float srcRight,
+ float srcBottom,
+ float dstLeft,
+ float dstTop,
+ float dstRight,
+ float dstBottom,
+ int scaleType,
+ float scaleFactor,
+ int cdId) {
+ buffer.start(OP_CODE);
+ buffer.writeInt(imageId);
+
+ buffer.writeFloat(srcLeft);
+ buffer.writeFloat(srcTop);
+ buffer.writeFloat(srcRight);
+ buffer.writeFloat(srcBottom);
+
+ buffer.writeFloat(dstLeft);
+ buffer.writeFloat(dstTop);
+ buffer.writeFloat(dstRight);
+ buffer.writeFloat(dstBottom);
+
+ buffer.writeInt(scaleType);
+ buffer.writeFloat(scaleFactor);
+ buffer.writeInt(cdId);
+ }
+
+ public static void read(WireBuffer buffer, List<Operation> operations) {
+ int imageId = buffer.readInt();
+
+ float sLeft = buffer.readFloat();
+ float srcTop = buffer.readFloat();
+ float srcRight = buffer.readFloat();
+ float srcBottom = buffer.readFloat();
+
+ float dstLeft = buffer.readFloat();
+ float dstTop = buffer.readFloat();
+ float dstRight = buffer.readFloat();
+ float dstBottom = buffer.readFloat();
+ int scaleType = buffer.readInt();
+ float scaleFactor = buffer.readFloat();
+ int cdId = buffer.readInt();
+ DrawBitmapScaled op =
+ new DrawBitmapScaled(
+ imageId,
+ sLeft,
+ srcTop,
+ srcRight,
+ srcBottom,
+ dstLeft,
+ dstTop,
+ dstRight,
+ dstBottom,
+ scaleType,
+ scaleFactor,
+ cdId);
+
+ operations.add(op);
+ }
+
+ public static void documentation(DocumentationBuilder doc) {
+ doc.operation("Draw Operations", OP_CODE, CLASS_NAME)
+ .description("Draw a bitmap using integer coordinates")
+ .field(DocumentedOperation.INT, "id", "id of bitmap")
+ .field(DocumentedOperation.FLOAT, "srcLeft", "The left side of the image")
+ .field(DocumentedOperation.FLOAT, "srcTop", "The top of the image")
+ .field(DocumentedOperation.FLOAT, "srcRight", "The right side of the image")
+ .field(DocumentedOperation.FLOAT, "srcBottom", "The bottom of the output")
+ .field(DocumentedOperation.FLOAT, "dstLeft", "The left side of the output")
+ .field(DocumentedOperation.FLOAT, "dstTop", "The top of the output")
+ .field(DocumentedOperation.FLOAT, "dstRight", "The right side of the output")
+ .field(DocumentedOperation.INT, "type", "type of auto scaling")
+ .field(DocumentedOperation.INT, "scaleFactor", "for allowed")
+ .field(DocumentedOperation.INT, "cdId", "id of string");
+ }
+
+ // private String typeToString(int type) {
+ // String[] typeString = {
+ // "none",
+ // "inside",
+ // "fill_width",
+ // "fill_height",
+ // "fit",
+ // "crop",
+ // "fill_bounds",
+ // "fixed_scale"
+ // };
+ // return typeString[type];
+ // }
+
+ @Override
+ public void paint(PaintContext context) {
+ mScaling.setup(
+ mOutSrcLeft,
+ mOutSrcTop,
+ mOutSrcRight,
+ mOutSrcBottom,
+ mOutDstLeft,
+ mOutDstTop,
+ mOutDstRight,
+ mOutDstBottom,
+ mScaleType,
+ mOutScaleFactor);
+ context.save();
+ context.clipRect(mOutDstLeft, mOutDstTop, mOutDstRight, mOutDstBottom);
+ context.drawBitmap(
+ mImageId,
+ (int) mOutSrcLeft,
+ (int) mOutSrcTop,
+ (int) mOutSrcRight,
+ (int) mOutSrcBottom,
+ (int) mScaling.mFinalDstLeft,
+ (int) mScaling.mFinalDstTop,
+ (int) mScaling.mFinalDstRight,
+ (int) mScaling.mFinalDstBottom,
+ mContentDescId);
+ context.restore();
+ }
+}
diff --git a/core/java/com/android/internal/widget/remotecompose/core/operations/DrawCircle.java b/core/java/com/android/internal/widget/remotecompose/core/operations/DrawCircle.java
index e39a19171cf0..04f095af29dc 100644
--- a/core/java/com/android/internal/widget/remotecompose/core/operations/DrawCircle.java
+++ b/core/java/com/android/internal/widget/remotecompose/core/operations/DrawCircle.java
@@ -1,12 +1,26 @@
+/*
+ * 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.
+ */
package com.android.internal.widget.remotecompose.core.operations;
-import static com.android.internal.widget.remotecompose.core.documentation.Operation.FLOAT;
-
import com.android.internal.widget.remotecompose.core.Operation;
import com.android.internal.widget.remotecompose.core.Operations;
import com.android.internal.widget.remotecompose.core.PaintContext;
import com.android.internal.widget.remotecompose.core.WireBuffer;
import com.android.internal.widget.remotecompose.core.documentation.DocumentationBuilder;
+import com.android.internal.widget.remotecompose.core.documentation.DocumentedOperation;
import java.util.List;
@@ -28,29 +42,25 @@ public class DrawCircle extends DrawBase3 {
}
public static void documentation(DocumentationBuilder doc) {
- doc.operation("Canvas Operations",
- OP_CODE,
- CLASS_NAME)
+ doc.operation("Canvas Operations", OP_CODE, CLASS_NAME)
.description("Draw a Circle")
- .field(FLOAT, "centerX",
+ .field(
+ DocumentedOperation.FLOAT,
+ "centerX",
"The x-coordinate of the center of the circle to be drawn")
- .field(FLOAT, "centerY",
+ .field(
+ DocumentedOperation.FLOAT,
+ "centerY",
"The y-coordinate of the center of the circle to be drawn")
- .field(FLOAT, "radius",
- "The radius of the circle to be drawn");
+ .field(DocumentedOperation.FLOAT, "radius", "The radius of the circle to be drawn");
}
- protected void write(WireBuffer buffer,
- float v1,
- float v2,
- float v3) {
+ @Override
+ protected void write(WireBuffer buffer, float v1, float v2, float v3) {
apply(buffer, v1, v2, v3);
}
- public DrawCircle(
- float left,
- float top,
- float right) {
+ public DrawCircle(float left, float top, float right) {
super(left, top, right);
mName = CLASS_NAME;
}
@@ -68,10 +78,7 @@ public class DrawCircle extends DrawBase3 {
* @param y1
* @param x2
*/
- public static void apply(WireBuffer buffer,
- float x1,
- float y1,
- float x2) {
+ public static void apply(WireBuffer buffer, float x1, float y1, float x2) {
buffer.start(OP_CODE);
buffer.writeFloat(x1);
buffer.writeFloat(y1);
diff --git a/core/java/com/android/internal/widget/remotecompose/core/operations/DrawLine.java b/core/java/com/android/internal/widget/remotecompose/core/operations/DrawLine.java
index a7276b56049f..dacbb0361761 100644
--- a/core/java/com/android/internal/widget/remotecompose/core/operations/DrawLine.java
+++ b/core/java/com/android/internal/widget/remotecompose/core/operations/DrawLine.java
@@ -15,14 +15,13 @@
*/
package com.android.internal.widget.remotecompose.core.operations;
-import static com.android.internal.widget.remotecompose.core.documentation.Operation.FLOAT;
-
import com.android.internal.widget.remotecompose.core.Operation;
import com.android.internal.widget.remotecompose.core.Operations;
import com.android.internal.widget.remotecompose.core.PaintContext;
import com.android.internal.widget.remotecompose.core.SerializableToString;
import com.android.internal.widget.remotecompose.core.WireBuffer;
import com.android.internal.widget.remotecompose.core.documentation.DocumentationBuilder;
+import com.android.internal.widget.remotecompose.core.documentation.DocumentedOperation;
import com.android.internal.widget.remotecompose.core.operations.utilities.StringSerializer;
import java.util.List;
@@ -36,7 +35,6 @@ public class DrawLine extends DrawBase4 implements SerializableToString {
read(m, buffer, operations);
}
-
public static int id() {
return OP_CODE;
}
@@ -46,33 +44,32 @@ public class DrawLine extends DrawBase4 implements SerializableToString {
}
public static void documentation(DocumentationBuilder doc) {
- doc.operation("Canvas Operations",
- OP_CODE,
- CLASS_NAME)
+ doc.operation("Canvas Operations", OP_CODE, CLASS_NAME)
.description("Draw a line segment")
- .field(FLOAT, "startX",
+ .field(
+ DocumentedOperation.FLOAT,
+ "startX",
"The x-coordinate of the start point of the line")
- .field(FLOAT, "startY",
+ .field(
+ DocumentedOperation.FLOAT,
+ "startY",
"The y-coordinate of the start point of the line")
- .field(FLOAT, "endX",
+ .field(
+ DocumentedOperation.FLOAT,
+ "endX",
"The x-coordinate of the end point of the line")
- .field(FLOAT, "endY",
+ .field(
+ DocumentedOperation.FLOAT,
+ "endY",
"The y-coordinate of the end point of the line");
}
- protected void write(WireBuffer buffer,
- float v1,
- float v2,
- float v3,
- float v4) {
+ @Override
+ protected void write(WireBuffer buffer, float v1, float v2, float v3, float v4) {
apply(buffer, v1, v2, v3, v4);
}
- public DrawLine(
- float left,
- float top,
- float right,
- float bottom) {
+ public DrawLine(float left, float top, float right, float bottom) {
super(left, top, right, bottom);
mName = "DrawLine";
}
@@ -91,14 +88,11 @@ public class DrawLine extends DrawBase4 implements SerializableToString {
* @param x2 end x of the line
* @param y2 end y of the line
*/
- public static void apply(WireBuffer buffer,
- float x1,
- float y1,
- float x2,
- float y2) {
+ public static void apply(WireBuffer buffer, float x1, float y1, float x2, float y2) {
write(buffer, OP_CODE, x1, y1, x2, y2);
}
+ @Override
public void serializeToString(int indent, StringSerializer serializer) {
String x1 = "" + mX1;
if (Float.isNaN(mX1Value)) {
@@ -116,8 +110,6 @@ public class DrawLine extends DrawBase4 implements SerializableToString {
if (Float.isNaN(mY2Value)) {
y2 = "[" + Utils.idFromNan(mY2Value) + " = " + mY2 + "]";
}
- serializer.append(indent, CLASS_NAME
- + "(" + x1 + ", " + y1 + ", " + x2 + ", " + y2 + ")"
- );
+ serializer.append(indent, CLASS_NAME + "(" + x1 + ", " + y1 + ", " + x2 + ", " + y2 + ")");
}
}
diff --git a/core/java/com/android/internal/widget/remotecompose/core/operations/DrawOval.java b/core/java/com/android/internal/widget/remotecompose/core/operations/DrawOval.java
index 01761efbb9ed..5d498e81c9d2 100644
--- a/core/java/com/android/internal/widget/remotecompose/core/operations/DrawOval.java
+++ b/core/java/com/android/internal/widget/remotecompose/core/operations/DrawOval.java
@@ -15,13 +15,12 @@
*/
package com.android.internal.widget.remotecompose.core.operations;
-import static com.android.internal.widget.remotecompose.core.documentation.Operation.FLOAT;
-
import com.android.internal.widget.remotecompose.core.Operation;
import com.android.internal.widget.remotecompose.core.Operations;
import com.android.internal.widget.remotecompose.core.PaintContext;
import com.android.internal.widget.remotecompose.core.WireBuffer;
import com.android.internal.widget.remotecompose.core.documentation.DocumentationBuilder;
+import com.android.internal.widget.remotecompose.core.documentation.DocumentedOperation;
import java.util.List;
@@ -43,25 +42,16 @@ public class DrawOval extends DrawBase4 {
}
public static void documentation(DocumentationBuilder doc) {
- doc.operation("Canvas Operations",
- OP_CODE,
- CLASS_NAME)
+ doc.operation("Canvas Operations", OP_CODE, CLASS_NAME)
.description("Draw the specified oval")
- .field(FLOAT, "left",
- "The left side of the oval")
- .field(FLOAT, "top",
- "The top of the oval")
- .field(FLOAT, "right",
- "The right side of the oval")
- .field(FLOAT, "bottom",
- "The bottom of the oval");
+ .field(DocumentedOperation.FLOAT, "left", "The left side of the oval")
+ .field(DocumentedOperation.FLOAT, "top", "The top of the oval")
+ .field(DocumentedOperation.FLOAT, "right", "The right side of the oval")
+ .field(DocumentedOperation.FLOAT, "bottom", "The bottom of the oval");
}
- protected void write(WireBuffer buffer,
- float v1,
- float v2,
- float v3,
- float v4) {
+ @Override
+ protected void write(WireBuffer buffer, float v1, float v2, float v3, float v4) {
apply(buffer, v1, v2, v3, v4);
}
@@ -70,11 +60,7 @@ public class DrawOval extends DrawBase4 {
apply(buffer, mX1, mY1, mX2, mY2);
}
- public DrawOval(
- float left,
- float top,
- float right,
- float bottom) {
+ public DrawOval(float left, float top, float right, float bottom) {
super(left, top, right, bottom);
mName = CLASS_NAME;
}
@@ -83,6 +69,7 @@ public class DrawOval extends DrawBase4 {
public void paint(PaintContext context) {
context.drawOval(mX1, mY1, mX2, mY2);
}
+
/**
* Writes out the DrawOval to the buffer
*
@@ -92,11 +79,7 @@ public class DrawOval extends DrawBase4 {
* @param x2 end x of the DrawOval
* @param y2 end y of the DrawOval
*/
- public static void apply(WireBuffer buffer,
- float x1,
- float y1,
- float x2,
- float y2) {
+ public static void apply(WireBuffer buffer, float x1, float y1, float x2, float y2) {
write(buffer, OP_CODE, x1, y1, x2, y2);
}
}
diff --git a/core/java/com/android/internal/widget/remotecompose/core/operations/DrawPath.java b/core/java/com/android/internal/widget/remotecompose/core/operations/DrawPath.java
index bc2c53a9a5ba..ccbf3d9e3091 100644
--- a/core/java/com/android/internal/widget/remotecompose/core/operations/DrawPath.java
+++ b/core/java/com/android/internal/widget/remotecompose/core/operations/DrawPath.java
@@ -15,14 +15,13 @@
*/
package com.android.internal.widget.remotecompose.core.operations;
-import static com.android.internal.widget.remotecompose.core.documentation.Operation.INT;
-
import com.android.internal.widget.remotecompose.core.Operation;
import com.android.internal.widget.remotecompose.core.Operations;
import com.android.internal.widget.remotecompose.core.PaintContext;
import com.android.internal.widget.remotecompose.core.PaintOperation;
import com.android.internal.widget.remotecompose.core.WireBuffer;
import com.android.internal.widget.remotecompose.core.documentation.DocumentationBuilder;
+import com.android.internal.widget.remotecompose.core.documentation.DocumentedOperation;
import java.util.List;
@@ -54,12 +53,10 @@ public class DrawPath extends PaintOperation {
operations.add(op);
}
-
public static String name() {
return CLASS_NAME;
}
-
public static int id() {
return Operations.DRAW_PATH;
}
@@ -70,14 +67,11 @@ public class DrawPath extends PaintOperation {
}
public static void documentation(DocumentationBuilder doc) {
- doc.operation("Draw Operations",
- OP_CODE,
- CLASS_NAME)
+ doc.operation("Draw Operations", OP_CODE, CLASS_NAME)
.description("Draw a bitmap using integer coordinates")
- .field(INT, "id", "id of path");
+ .field(DocumentedOperation.INT, "id", "id of path");
}
-
@Override
public void paint(PaintContext context) {
context.drawPath(mId, mStart, mEnd);
diff --git a/core/java/com/android/internal/widget/remotecompose/core/operations/DrawRect.java b/core/java/com/android/internal/widget/remotecompose/core/operations/DrawRect.java
index ad17fe743b76..644011b8a214 100644
--- a/core/java/com/android/internal/widget/remotecompose/core/operations/DrawRect.java
+++ b/core/java/com/android/internal/widget/remotecompose/core/operations/DrawRect.java
@@ -15,19 +15,16 @@
*/
package com.android.internal.widget.remotecompose.core.operations;
-import static com.android.internal.widget.remotecompose.core.documentation.Operation.FLOAT;
-
import com.android.internal.widget.remotecompose.core.Operation;
import com.android.internal.widget.remotecompose.core.Operations;
import com.android.internal.widget.remotecompose.core.PaintContext;
import com.android.internal.widget.remotecompose.core.WireBuffer;
import com.android.internal.widget.remotecompose.core.documentation.DocumentationBuilder;
+import com.android.internal.widget.remotecompose.core.documentation.DocumentedOperation;
import java.util.List;
-/**
- * Draw a Rectangle
- */
+/** Draw a Rectangle */
public class DrawRect extends DrawBase4 {
private static final int OP_CODE = Operations.DRAW_RECT;
private static final String CLASS_NAME = "DrawRect";
@@ -37,7 +34,6 @@ public class DrawRect extends DrawBase4 {
read(m, buffer, operations);
}
-
public static int id() {
return OP_CODE;
}
@@ -47,34 +43,20 @@ public class DrawRect extends DrawBase4 {
}
public static void documentation(DocumentationBuilder doc) {
- doc.operation("Canvas Operations",
- OP_CODE,
- CLASS_NAME)
+ doc.operation("Canvas Operations", OP_CODE, CLASS_NAME)
.description("Draw the specified rectangle")
- .field(FLOAT, "left",
- "The left side of the rectangle")
- .field(FLOAT, "top",
- "The top of the rectangle")
- .field(FLOAT, "right",
- "The right side of the rectangle")
- .field(FLOAT, "bottom",
- "The bottom of the rectangle");
+ .field(DocumentedOperation.FLOAT, "left", "The left side of the rectangle")
+ .field(DocumentedOperation.FLOAT, "top", "The top of the rectangle")
+ .field(DocumentedOperation.FLOAT, "right", "The right side of the rectangle")
+ .field(DocumentedOperation.FLOAT, "bottom", "The bottom of the rectangle");
}
-
- protected void write(WireBuffer buffer,
- float v1,
- float v2,
- float v3,
- float v4) {
+ @Override
+ protected void write(WireBuffer buffer, float v1, float v2, float v3, float v4) {
apply(buffer, v1, v2, v3, v4);
}
- public DrawRect(
- float left,
- float top,
- float right,
- float bottom) {
+ public DrawRect(float left, float top, float right, float bottom) {
super(left, top, right, bottom);
mName = CLASS_NAME;
}
@@ -88,16 +70,12 @@ public class DrawRect extends DrawBase4 {
* Writes out the DrawRect to the buffer
*
* @param buffer buffer to write to
- * @param x1 left x of rect
- * @param y1 top y of the rect
- * @param x2 right x of the rect
- * @param y2 bottom y of the rect
+ * @param x1 left x of rect
+ * @param y1 top y of the rect
+ * @param x2 right x of the rect
+ * @param y2 bottom y of the rect
*/
- public static void apply(WireBuffer buffer,
- float x1,
- float y1,
- float x2,
- float y2) {
+ public static void apply(WireBuffer buffer, float x1, float y1, float x2, float y2) {
write(buffer, OP_CODE, x1, y1, x2, y2);
}
}
diff --git a/core/java/com/android/internal/widget/remotecompose/core/operations/DrawRoundRect.java b/core/java/com/android/internal/widget/remotecompose/core/operations/DrawRoundRect.java
index 908e03a5191c..64a3b283505b 100644
--- a/core/java/com/android/internal/widget/remotecompose/core/operations/DrawRoundRect.java
+++ b/core/java/com/android/internal/widget/remotecompose/core/operations/DrawRoundRect.java
@@ -15,24 +15,20 @@
*/
package com.android.internal.widget.remotecompose.core.operations;
-import static com.android.internal.widget.remotecompose.core.documentation.Operation.FLOAT;
-
import com.android.internal.widget.remotecompose.core.Operation;
import com.android.internal.widget.remotecompose.core.Operations;
import com.android.internal.widget.remotecompose.core.PaintContext;
import com.android.internal.widget.remotecompose.core.WireBuffer;
import com.android.internal.widget.remotecompose.core.documentation.DocumentationBuilder;
+import com.android.internal.widget.remotecompose.core.documentation.DocumentedOperation;
import java.util.List;
-/**
- * Draw a rounded rectangle
- */
+/** Draw a rounded rectangle */
public class DrawRoundRect extends DrawBase6 {
private static final int OP_CODE = Operations.DRAW_ROUND_RECT;
private static final String CLASS_NAME = "DrawRoundRect";
-
public static void read(WireBuffer buffer, List<Operation> operations) {
Maker m = DrawRoundRect::new;
read(m, buffer, operations);
@@ -53,13 +49,8 @@ public class DrawRoundRect extends DrawBase6 {
* @param v5 The x-radius of the oval used to round the corners
* @param v6 The y-radius of the oval used to round the corners
*/
- public static void apply(WireBuffer buffer,
- float v1,
- float v2,
- float v3,
- float v4,
- float v5,
- float v6) {
+ public static void apply(
+ WireBuffer buffer, float v1, float v2, float v3, float v4, float v5, float v6) {
buffer.start(OP_CODE);
buffer.writeFloat(v1);
buffer.writeFloat(v2);
@@ -69,50 +60,36 @@ public class DrawRoundRect extends DrawBase6 {
buffer.writeFloat(v6);
}
- protected void write(WireBuffer buffer,
- float v1,
- float v2,
- float v3,
- float v4,
- float v5,
- float v6) {
+ @Override
+ protected void write(
+ WireBuffer buffer, float v1, float v2, float v3, float v4, float v5, float v6) {
apply(buffer, v1, v2, v3, v4, v5, v6);
}
public static void documentation(DocumentationBuilder doc) {
- doc.operation("Canvas Operations",
- OP_CODE,
- CLASS_NAME)
+ doc.operation("Canvas Operations", OP_CODE, CLASS_NAME)
.description("Draw the specified round-rect")
- .field(FLOAT, "left",
- "The left side of the rect")
- .field(FLOAT, "top",
- "The top of the rect")
- .field(FLOAT, "right",
- "The right side of the rect")
- .field(FLOAT, "bottom",
- "The bottom of the rect")
- .field(FLOAT, "rx",
+ .field(DocumentedOperation.FLOAT, "left", "The left side of the rect")
+ .field(DocumentedOperation.FLOAT, "top", "The top of the rect")
+ .field(DocumentedOperation.FLOAT, "right", "The right side of the rect")
+ .field(DocumentedOperation.FLOAT, "bottom", "The bottom of the rect")
+ .field(
+ DocumentedOperation.FLOAT,
+ "rx",
"The x-radius of the oval used to round the corners")
- .field(FLOAT, "sweepAngle",
+ .field(
+ DocumentedOperation.FLOAT,
+ "sweepAngle",
"The y-radius of the oval used to round the corners");
}
-
- public DrawRoundRect(float v1,
- float v2,
- float v3,
- float v4,
- float v5,
- float v6) {
+ public DrawRoundRect(float v1, float v2, float v3, float v4, float v5, float v6) {
super(v1, v2, v3, v4, v5, v6);
mName = CLASS_NAME;
}
@Override
public void paint(PaintContext context) {
- context.drawRoundRect(mV1, mV2, mV3, mV4, mV5, mV6
- );
+ context.drawRoundRect(mV1, mV2, mV3, mV4, mV5, mV6);
}
-
}
diff --git a/core/java/com/android/internal/widget/remotecompose/core/operations/DrawSector.java b/core/java/com/android/internal/widget/remotecompose/core/operations/DrawSector.java
new file mode 100644
index 000000000000..3cb191647c33
--- /dev/null
+++ b/core/java/com/android/internal/widget/remotecompose/core/operations/DrawSector.java
@@ -0,0 +1,96 @@
+/*
+ * 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.
+ */
+package com.android.internal.widget.remotecompose.core.operations;
+
+import com.android.internal.widget.remotecompose.core.Operation;
+import com.android.internal.widget.remotecompose.core.Operations;
+import com.android.internal.widget.remotecompose.core.PaintContext;
+import com.android.internal.widget.remotecompose.core.WireBuffer;
+import com.android.internal.widget.remotecompose.core.documentation.DocumentationBuilder;
+import com.android.internal.widget.remotecompose.core.documentation.DocumentedOperation;
+
+import java.util.List;
+
+public class DrawSector extends DrawBase6 {
+ public static final int OP_CODE = Operations.DRAW_SECTOR;
+ private static final String CLASS_NAME = "DrawSector";
+
+ public static void read(WireBuffer buffer, List<Operation> operations) {
+ Maker m = DrawSector::new;
+ read(m, buffer, operations);
+ }
+
+ public static int id() {
+ return OP_CODE;
+ }
+
+ /**
+ * Writes out the operation to the buffer
+ *
+ * @param buffer the buffer to write to
+ * @param v1 The left side of the Oval
+ * @param v2 The top of the Oval
+ * @param v3 The right side of the Oval
+ * @param v4 The bottom of the Oval
+ * @param v5 Starting angle (in degrees) where the arc begins
+ * @param v6 Sweep angle (in degrees) measured clockwise
+ */
+ public static void apply(
+ WireBuffer buffer, float v1, float v2, float v3, float v4, float v5, float v6) {
+ buffer.start(OP_CODE);
+ buffer.writeFloat(v1);
+ buffer.writeFloat(v2);
+ buffer.writeFloat(v3);
+ buffer.writeFloat(v4);
+ buffer.writeFloat(v5);
+ buffer.writeFloat(v6);
+ }
+
+ @Override
+ protected void write(
+ WireBuffer buffer, float v1, float v2, float v3, float v4, float v5, float v6) {
+ apply(buffer, v1, v2, v3, v4, v5, v6);
+ }
+
+ public static void documentation(DocumentationBuilder doc) {
+ doc.operation("Canvas Operations", OP_CODE, CLASS_NAME)
+ .description(
+ "Draw the specified sector (pie shape)"
+ + "which will be scaled to fit inside the specified oval")
+ .field(DocumentedOperation.FLOAT, "left", "The left side of the Oval")
+ .field(DocumentedOperation.FLOAT, "top", "The top of the Oval")
+ .field(DocumentedOperation.FLOAT, "right", "The right side of the Oval")
+ .field(DocumentedOperation.FLOAT, "bottom", "The bottom of the Oval")
+ .field(
+ DocumentedOperation.FLOAT,
+ "startAngle",
+ "Starting angle (in degrees) where the arc begins")
+ .field(
+ DocumentedOperation.FLOAT,
+ "sweepAngle",
+ "Sweep angle (in degrees) measured clockwise");
+ }
+
+ public DrawSector(float v1, float v2, float v3, float v4, float v5, float v6) {
+ super(v1, v2, v3, v4, v5, v6);
+ mName = "DrawSector";
+ }
+
+ @Override
+ public void paint(PaintContext context) {
+ context.drawSector(mV1, mV2, mV3, mV4, mV5, mV6);
+ }
+}
diff --git a/core/java/com/android/internal/widget/remotecompose/core/operations/DrawText.java b/core/java/com/android/internal/widget/remotecompose/core/operations/DrawText.java
index 60dddc2d610e..bcb7852e6615 100644
--- a/core/java/com/android/internal/widget/remotecompose/core/operations/DrawText.java
+++ b/core/java/com/android/internal/widget/remotecompose/core/operations/DrawText.java
@@ -15,9 +15,6 @@
*/
package com.android.internal.widget.remotecompose.core.operations;
-import static com.android.internal.widget.remotecompose.core.documentation.Operation.BOOLEAN;
-import static com.android.internal.widget.remotecompose.core.documentation.Operation.FLOAT;
-import static com.android.internal.widget.remotecompose.core.documentation.Operation.INT;
import static com.android.internal.widget.remotecompose.core.operations.Utils.floatToString;
import com.android.internal.widget.remotecompose.core.Operation;
@@ -28,12 +25,11 @@ import com.android.internal.widget.remotecompose.core.RemoteContext;
import com.android.internal.widget.remotecompose.core.VariableSupport;
import com.android.internal.widget.remotecompose.core.WireBuffer;
import com.android.internal.widget.remotecompose.core.documentation.DocumentationBuilder;
+import com.android.internal.widget.remotecompose.core.documentation.DocumentedOperation;
import java.util.List;
-/**
- * Draw Text
- */
+/** Draw Text */
public class DrawText extends PaintOperation implements VariableSupport {
private static final int OP_CODE = Operations.DRAW_TEXT_RUN;
private static final String CLASS_NAME = "DrawText";
@@ -48,14 +44,15 @@ public class DrawText extends PaintOperation implements VariableSupport {
float mOutY = 0f;
boolean mRtl = false;
- public DrawText(int textID,
- int start,
- int end,
- int contextStart,
- int contextEnd,
- float x,
- float y,
- boolean rtl) {
+ public DrawText(
+ int textID,
+ int start,
+ int end,
+ int contextStart,
+ int contextEnd,
+ float x,
+ float y,
+ boolean rtl) {
mTextID = textID;
mStart = start;
mEnd = end;
@@ -68,10 +65,8 @@ public class DrawText extends PaintOperation implements VariableSupport {
@Override
public void updateVariables(RemoteContext context) {
- mOutX = (Float.isNaN(mX))
- ? context.getFloat(Utils.idFromNan(mX)) : mX;
- mOutY = (Float.isNaN(mY))
- ? context.getFloat(Utils.idFromNan(mY)) : mY;
+ mOutX = Float.isNaN(mX) ? context.getFloat(Utils.idFromNan(mX)) : mX;
+ mOutY = Float.isNaN(mY) ? context.getFloat(Utils.idFromNan(mY)) : mY;
}
@Override
@@ -87,13 +82,20 @@ public class DrawText extends PaintOperation implements VariableSupport {
@Override
public void write(WireBuffer buffer) {
apply(buffer, mTextID, mStart, mEnd, mContextStart, mContextEnd, mX, mY, mRtl);
-
}
@Override
public String toString() {
- return "DrawTextRun [" + mTextID + "] " + mStart + ", " + mEnd + ", "
- + floatToString(mX, mOutX) + ", " + floatToString(mY, mOutY);
+ return "DrawTextRun ["
+ + mTextID
+ + "] "
+ + mStart
+ + ", "
+ + mEnd
+ + ", "
+ + floatToString(mX, mOutX)
+ + ", "
+ + floatToString(mY, mOutY);
}
public static void read(WireBuffer buffer, List<Operation> operations) {
@@ -114,7 +116,6 @@ public class DrawText extends PaintOperation implements VariableSupport {
return CLASS_NAME;
}
-
public static int id() {
return OP_CODE;
}
@@ -122,25 +123,26 @@ public class DrawText extends PaintOperation implements VariableSupport {
/**
* Writes out the operation to the buffer
*
- * @param buffer write the command to the buffer
- * @param textID id of the text
- * @param start Start position
- * @param end end position
+ * @param buffer write the command to the buffer
+ * @param textID id of the text
+ * @param start Start position
+ * @param end end position
* @param contextStart start of the context
- * @param contextEnd end of the context
- * @param x position of where to draw
- * @param y position of where to draw
- * @param rtl is it Right to Left text
+ * @param contextEnd end of the context
+ * @param x position of where to draw
+ * @param y position of where to draw
+ * @param rtl is it Right to Left text
*/
- public static void apply(WireBuffer buffer,
- int textID,
- int start,
- int end,
- int contextStart,
- int contextEnd,
- float x,
- float y,
- boolean rtl) {
+ public static void apply(
+ WireBuffer buffer,
+ int textID,
+ int start,
+ int end,
+ int contextStart,
+ int contextEnd,
+ float x,
+ float y,
+ boolean rtl) {
buffer.start(Operations.DRAW_TEXT_RUN);
buffer.writeInt(textID);
buffer.writeInt(start);
@@ -152,33 +154,30 @@ public class DrawText extends PaintOperation implements VariableSupport {
buffer.writeBoolean(rtl);
}
-
public static void documentation(DocumentationBuilder doc) {
- doc.operation("Draw Operations",
- id(),
- CLASS_NAME)
+ doc.operation("Draw Operations", id(), CLASS_NAME)
.description("Draw a run of text, all in a single direction")
- .field(INT, "textId", "id of bitmap")
- .field(INT, "start",
+ .field(DocumentedOperation.INT, "textId", "id of bitmap")
+ .field(
+ DocumentedOperation.INT,
+ "start",
"The start of the text to render. -1=end of string")
- .field(INT, "end",
- "The end of the text to render")
- .field(INT, "contextStart",
+ .field(DocumentedOperation.INT, "end", "The end of the text to render")
+ .field(
+ DocumentedOperation.INT,
+ "contextStart",
"the index of the start of the shaping context")
- .field(INT, "contextEnd",
+ .field(
+ DocumentedOperation.INT,
+ "contextEnd",
"the index of the end of the shaping context")
- .field(FLOAT, "x",
- "The x position at which to draw the text")
- .field(FLOAT, "y",
- "The y position at which to draw the text")
- .field(BOOLEAN, "RTL",
- "Whether the run is in RTL direction");
+ .field(DocumentedOperation.FLOAT, "x", "The x position at which to draw the text")
+ .field(DocumentedOperation.FLOAT, "y", "The y position at which to draw the text")
+ .field(DocumentedOperation.BOOLEAN, "RTL", "Whether the run is in RTL direction");
}
-
@Override
public void paint(PaintContext context) {
- context.drawTextRun(mTextID, mStart, mEnd, mContextStart,
- mContextEnd, mOutX, mOutY, mRtl);
+ context.drawTextRun(mTextID, mStart, mEnd, mContextStart, mContextEnd, mOutX, mOutY, mRtl);
}
}
diff --git a/core/java/com/android/internal/widget/remotecompose/core/operations/DrawTextAnchored.java b/core/java/com/android/internal/widget/remotecompose/core/operations/DrawTextAnchored.java
index 242bc2575cee..95a87667dfab 100644
--- a/core/java/com/android/internal/widget/remotecompose/core/operations/DrawTextAnchored.java
+++ b/core/java/com/android/internal/widget/remotecompose/core/operations/DrawTextAnchored.java
@@ -15,9 +15,6 @@
*/
package com.android.internal.widget.remotecompose.core.operations;
-import static com.android.internal.widget.remotecompose.core.documentation.Operation.FLOAT;
-import static com.android.internal.widget.remotecompose.core.documentation.Operation.INT;
-
import com.android.internal.widget.remotecompose.core.Operation;
import com.android.internal.widget.remotecompose.core.Operations;
import com.android.internal.widget.remotecompose.core.PaintContext;
@@ -26,12 +23,11 @@ import com.android.internal.widget.remotecompose.core.RemoteContext;
import com.android.internal.widget.remotecompose.core.VariableSupport;
import com.android.internal.widget.remotecompose.core.WireBuffer;
import com.android.internal.widget.remotecompose.core.documentation.DocumentationBuilder;
+import com.android.internal.widget.remotecompose.core.documentation.DocumentedOperation;
import java.util.List;
-/**
- * Draw Text in Anchored to a point
- */
+/** Draw Text in Anchored to a point */
public class DrawTextAnchored extends PaintOperation implements VariableSupport {
private static final int OP_CODE = Operations.DRAW_TEXT_ANCHOR;
private static final String CLASS_NAME = "DrawTextAnchored";
@@ -49,12 +45,7 @@ public class DrawTextAnchored extends PaintOperation implements VariableSupport
public static final int ANCHOR_TEXT_RTL = 1;
public static final int ANCHOR_MONOSPACE_MEASURE = 2;
- public DrawTextAnchored(int textID,
- float x,
- float y,
- float panX,
- float panY,
- int flags) {
+ public DrawTextAnchored(int textID, float x, float y, float panX, float panY, int flags) {
mTextID = textID;
mX = x;
mY = y;
@@ -67,14 +58,10 @@ public class DrawTextAnchored extends PaintOperation implements VariableSupport
@Override
public void updateVariables(RemoteContext context) {
- mOutX = (Float.isNaN(mX))
- ? context.getFloat(Utils.idFromNan(mX)) : mX;
- mOutY = (Float.isNaN(mY))
- ? context.getFloat(Utils.idFromNan(mY)) : mY;
- mOutPanX = (Float.isNaN(mPanX))
- ? context.getFloat(Utils.idFromNan(mPanX)) : mPanX;
- mOutPanY = (Float.isNaN(mPanY))
- ? context.getFloat(Utils.idFromNan(mPanY)) : mPanY;
+ mOutX = Float.isNaN(mX) ? context.getFloat(Utils.idFromNan(mX)) : mX;
+ mOutY = Float.isNaN(mY) ? context.getFloat(Utils.idFromNan(mY)) : mY;
+ mOutPanX = Float.isNaN(mPanX) ? context.getFloat(Utils.idFromNan(mPanX)) : mPanX;
+ mOutPanY = Float.isNaN(mPanY) ? context.getFloat(Utils.idFromNan(mPanY)) : mPanY;
}
@Override
@@ -95,18 +82,22 @@ public class DrawTextAnchored extends PaintOperation implements VariableSupport
@Override
public void write(WireBuffer buffer) {
- apply(buffer, mTextID, mX,
- mY,
- mPanX,
- mPanY,
- mFlags);
+ apply(buffer, mTextID, mX, mY, mPanX, mPanY, mFlags);
}
@Override
public String toString() {
- return "DrawTextAnchored [" + mTextID + "] " + floatToStr(mX) + ", "
- + floatToStr(mY) + ", "
- + floatToStr(mPanX) + ", " + floatToStr(mPanY) + ", "
+ return "DrawTextAnchored ["
+ + mTextID
+ + "] "
+ + floatToStr(mX)
+ + ", "
+ + floatToStr(mY)
+ + ", "
+ + floatToStr(mPanX)
+ + ", "
+ + floatToStr(mPanY)
+ + ", "
+ Integer.toBinaryString(mFlags);
}
@@ -125,10 +116,7 @@ public class DrawTextAnchored extends PaintOperation implements VariableSupport
float panY = buffer.readFloat();
int flags = buffer.readInt();
- DrawTextAnchored op = new DrawTextAnchored(textID,
- x, y,
- panX, panY,
- flags);
+ DrawTextAnchored op = new DrawTextAnchored(textID, x, y, panX, panY, flags);
operations.add(op);
}
@@ -152,13 +140,8 @@ public class DrawTextAnchored extends PaintOperation implements VariableSupport
* @param panY The pan from top(-1) to bottom(1) 0 being centered
* @param flags Change the behaviour
*/
- public static void apply(WireBuffer buffer,
- int textID,
- float x,
- float y,
- float panX,
- float panY,
- int flags) {
+ public static void apply(
+ WireBuffer buffer, int textID, float x, float y, float panX, float panY, int flags) {
buffer.start(OP_CODE);
buffer.writeInt(textID);
buffer.writeFloat(x);
@@ -169,25 +152,22 @@ public class DrawTextAnchored extends PaintOperation implements VariableSupport
}
public static void documentation(DocumentationBuilder doc) {
- doc.operation("Draw Operations",
- OP_CODE,
- CLASS_NAME)
+ doc.operation("Draw Operations", OP_CODE, CLASS_NAME)
.description("Draw text centered about an anchor point")
- .field(INT, "textId", "id of bitmap")
- .field(FLOAT, "x",
- "The x-position of the anchor point")
- .field(FLOAT, "y",
- "The y-position of the anchor point")
- .field(FLOAT, "panX",
+ .field(DocumentedOperation.INT, "textId", "id of bitmap")
+ .field(DocumentedOperation.FLOAT, "x", "The x-position of the anchor point")
+ .field(DocumentedOperation.FLOAT, "y", "The y-position of the anchor point")
+ .field(
+ DocumentedOperation.FLOAT,
+ "panX",
"The pan from left(-1) to right(1) 0 being centered")
- .field(FLOAT, "panY",
+ .field(
+ DocumentedOperation.FLOAT,
+ "panY",
"The pan from top(-1) to bottom(1) 0 being centered")
- .field(INT, "flags",
- "Change the behaviour");
-
+ .field(DocumentedOperation.INT, "flags", "Change the behaviour");
}
-
float[] mBounds = new float[4];
private float getHorizontalOffset() {
@@ -196,8 +176,7 @@ public class DrawTextAnchored extends PaintOperation implements VariableSupport
float textWidth = scale * (mBounds[2] - mBounds[0]);
float boxWidth = 0;
- return (boxWidth - textWidth) * (1 + mOutPanX) / 2.f
- - (scale * mBounds[0]);
+ return (boxWidth - textWidth) * (1 + mOutPanX) / 2.f - (scale * mBounds[0]);
}
private float getVerticalOffset() {
@@ -205,18 +184,18 @@ public class DrawTextAnchored extends PaintOperation implements VariableSupport
float scale = 1.0f;
float boxHeight = 0;
float textHeight = scale * (mBounds[3] - mBounds[1]);
- return (boxHeight - textHeight) * (1 - mOutPanY) / 2
- - (scale * mBounds[1]);
+ return (boxHeight - textHeight) * (1 - mOutPanY) / 2 - (scale * mBounds[1]);
}
@Override
public void paint(PaintContext context) {
- int flags = ((mFlags & ANCHOR_MONOSPACE_MEASURE) != 0)
- ? PaintContext.TEXT_MEASURE_MONOSPACE_WIDTH : 0;
+ int flags =
+ ((mFlags & ANCHOR_MONOSPACE_MEASURE) != 0)
+ ? PaintContext.TEXT_MEASURE_MONOSPACE_WIDTH
+ : 0;
context.getTextBounds(mTextID, 0, -1, flags, mBounds);
float x = mOutX + getHorizontalOffset();
- float y = (Float.isNaN(mOutPanY)) ? mOutY : mOutY + getVerticalOffset();
- context.drawTextRun(mTextID, 0, -1, 0, 1, x, y,
- (mFlags & ANCHOR_TEXT_RTL) == 1);
+ float y = Float.isNaN(mOutPanY) ? mOutY : mOutY + getVerticalOffset();
+ context.drawTextRun(mTextID, 0, -1, 0, 1, x, y, (mFlags & ANCHOR_TEXT_RTL) == 1);
}
}
diff --git a/core/java/com/android/internal/widget/remotecompose/core/operations/DrawTextOnPath.java b/core/java/com/android/internal/widget/remotecompose/core/operations/DrawTextOnPath.java
index d69362bd7a7d..aefd6f397ebf 100644
--- a/core/java/com/android/internal/widget/remotecompose/core/operations/DrawTextOnPath.java
+++ b/core/java/com/android/internal/widget/remotecompose/core/operations/DrawTextOnPath.java
@@ -15,10 +15,6 @@
*/
package com.android.internal.widget.remotecompose.core.operations;
-import static com.android.internal.widget.remotecompose.core.documentation.Operation.FLOAT;
-import static com.android.internal.widget.remotecompose.core.documentation.Operation.INT;
-import static com.android.internal.widget.remotecompose.core.operations.Utils.floatToString;
-
import com.android.internal.widget.remotecompose.core.Operation;
import com.android.internal.widget.remotecompose.core.Operations;
import com.android.internal.widget.remotecompose.core.PaintContext;
@@ -27,13 +23,12 @@ import com.android.internal.widget.remotecompose.core.RemoteContext;
import com.android.internal.widget.remotecompose.core.VariableSupport;
import com.android.internal.widget.remotecompose.core.WireBuffer;
import com.android.internal.widget.remotecompose.core.documentation.DocumentationBuilder;
+import com.android.internal.widget.remotecompose.core.documentation.DocumentedOperation;
import java.util.List;
-/**
- * Draw text along a path.
- */
-public class DrawTextOnPath extends PaintOperation implements VariableSupport {
+/** Draw text along a path. */
+public class DrawTextOnPath extends PaintOperation implements VariableSupport {
private static final int OP_CODE = Operations.DRAW_TEXT_ON_PATH;
private static final String CLASS_NAME = "DrawTextOnPath";
int mPathId;
@@ -46,18 +41,16 @@ public class DrawTextOnPath extends PaintOperation implements VariableSupport {
public DrawTextOnPath(int textId, int pathId, float hOffset, float vOffset) {
mPathId = pathId;
mTextId = textId;
- mOutHOffset = mHOffset = vOffset;
- mOutVOffset = mVOffset = hOffset;
+ mOutHOffset = mHOffset = hOffset;
+ mOutVOffset = mVOffset = vOffset;
}
-
@Override
public void updateVariables(RemoteContext context) {
- mOutHOffset = (Float.isNaN(mHOffset))
- ? context.getFloat(Utils.idFromNan(mHOffset)) : mHOffset;
- mOutVOffset = (Float.isNaN(mVOffset))
- ? context.getFloat(Utils.idFromNan(mVOffset)) : mVOffset;
-
+ mOutHOffset =
+ Float.isNaN(mHOffset) ? context.getFloat(Utils.idFromNan(mHOffset)) : mHOffset;
+ mOutVOffset =
+ Float.isNaN(mVOffset) ? context.getFloat(Utils.idFromNan(mVOffset)) : mVOffset;
}
@Override
@@ -77,16 +70,21 @@ public class DrawTextOnPath extends PaintOperation implements VariableSupport {
@Override
public String toString() {
- return "DrawTextOnPath [" + mTextId + "] [" + mPathId + "] "
- + floatToString(mHOffset, mOutHOffset) + ", "
- + floatToString(mVOffset, mOutVOffset);
+ return "DrawTextOnPath ["
+ + mTextId
+ + "] ["
+ + mPathId
+ + "] "
+ + Utils.floatToString(mHOffset, mOutHOffset)
+ + ", "
+ + Utils.floatToString(mVOffset, mOutVOffset);
}
public static void read(WireBuffer buffer, List<Operation> operations) {
int textId = buffer.readInt();
int pathId = buffer.readInt();
- float hOffset = buffer.readFloat();
float vOffset = buffer.readFloat();
+ float hOffset = buffer.readFloat();
DrawTextOnPath op = new DrawTextOnPath(textId, pathId, hOffset, vOffset);
operations.add(op);
}
@@ -95,33 +93,26 @@ public class DrawTextOnPath extends PaintOperation implements VariableSupport {
return "DrawTextOnPath";
}
-
public static int id() {
return Operations.DRAW_TEXT_ON_PATH;
}
- public static void apply(WireBuffer buffer, int textId, int pathId,
- float hOffset, float vOffset) {
+ public static void apply(
+ WireBuffer buffer, int textId, int pathId, float hOffset, float vOffset) {
buffer.start(OP_CODE);
buffer.writeInt(textId);
buffer.writeInt(pathId);
- buffer.writeFloat(hOffset);
buffer.writeFloat(vOffset);
+ buffer.writeFloat(hOffset);
}
public static void documentation(DocumentationBuilder doc) {
- doc.operation("Draw Operations",
- OP_CODE,
- CLASS_NAME)
+ doc.operation("Draw Operations", OP_CODE, CLASS_NAME)
.description("Draw text along path object")
- .field(INT, "textId",
- "id of the text")
- .field(INT, "pathId",
- "id of the path")
- .field(FLOAT, "xOffset",
- "x Shift of the text")
- .field(FLOAT, "yOffset",
- "y Shift of the text");
+ .field(DocumentedOperation.INT, "textId", "id of the text")
+ .field(DocumentedOperation.INT, "pathId", "id of the path")
+ .field(DocumentedOperation.FLOAT, "xOffset", "x Shift of the text")
+ .field(DocumentedOperation.FLOAT, "yOffset", "y Shift of the text");
}
@Override
diff --git a/core/java/com/android/internal/widget/remotecompose/core/operations/DrawTweenPath.java b/core/java/com/android/internal/widget/remotecompose/core/operations/DrawTweenPath.java
index 354726319c45..b6d45d95f2c0 100644
--- a/core/java/com/android/internal/widget/remotecompose/core/operations/DrawTweenPath.java
+++ b/core/java/com/android/internal/widget/remotecompose/core/operations/DrawTweenPath.java
@@ -15,8 +15,6 @@
*/
package com.android.internal.widget.remotecompose.core.operations;
-import static com.android.internal.widget.remotecompose.core.documentation.Operation.FLOAT;
-import static com.android.internal.widget.remotecompose.core.documentation.Operation.INT;
import static com.android.internal.widget.remotecompose.core.operations.Utils.floatToString;
import com.android.internal.widget.remotecompose.core.Operation;
@@ -27,6 +25,7 @@ import com.android.internal.widget.remotecompose.core.RemoteContext;
import com.android.internal.widget.remotecompose.core.VariableSupport;
import com.android.internal.widget.remotecompose.core.WireBuffer;
import com.android.internal.widget.remotecompose.core.documentation.DocumentationBuilder;
+import com.android.internal.widget.remotecompose.core.documentation.DocumentedOperation;
import java.util.List;
@@ -42,12 +41,7 @@ public class DrawTweenPath extends PaintOperation implements VariableSupport {
int mPath1Id;
int mPath2Id;
- public DrawTweenPath(
- int path1Id,
- int path2Id,
- float tween,
- float start,
- float stop) {
+ public DrawTweenPath(int path1Id, int path2Id, float tween, float start, float stop) {
mOutTween = mTween = tween;
mOutStart = mStart = start;
mOutStop = mStop = stop;
@@ -57,12 +51,9 @@ public class DrawTweenPath extends PaintOperation implements VariableSupport {
@Override
public void updateVariables(RemoteContext context) {
- mOutTween = (Float.isNaN(mTween))
- ? context.getFloat(Utils.idFromNan(mTween)) : mTween;
- mOutStart = (Float.isNaN(mStart))
- ? context.getFloat(Utils.idFromNan(mStart)) : mStart;
- mOutStop = (Float.isNaN(mStop))
- ? context.getFloat(Utils.idFromNan(mStop)) : mStop;
+ mOutTween = Float.isNaN(mTween) ? context.getFloat(Utils.idFromNan(mTween)) : mTween;
+ mOutStart = Float.isNaN(mStart) ? context.getFloat(Utils.idFromNan(mStart)) : mStart;
+ mOutStop = Float.isNaN(mStop) ? context.getFloat(Utils.idFromNan(mStop)) : mStop;
}
@Override
@@ -80,49 +71,44 @@ public class DrawTweenPath extends PaintOperation implements VariableSupport {
@Override
public void write(WireBuffer buffer) {
- apply(buffer, mPath1Id,
- mPath2Id,
- mTween,
- mStart,
- mStop);
+ apply(buffer, mPath1Id, mPath2Id, mTween, mStart, mStop);
}
@Override
public String toString() {
- return "DrawTweenPath " + mPath1Id + " " + mPath2Id
- + " " + floatToString(mTween, mOutTween) + " "
- + floatToString(mStart, mOutStart) + " "
- + "- " + floatToString(mStop, mOutStop);
+ return "DrawTweenPath "
+ + mPath1Id
+ + " "
+ + mPath2Id
+ + " "
+ + floatToString(mTween, mOutTween)
+ + " "
+ + floatToString(mStart, mOutStart)
+ + " "
+ + "- "
+ + floatToString(mStop, mOutStop);
}
-
public static void read(WireBuffer buffer, List<Operation> operations) {
int path1Id = buffer.readInt();
int path2Id = buffer.readInt();
float tween = buffer.readFloat();
float start = buffer.readFloat();
float stop = buffer.readFloat();
- DrawTweenPath op = new DrawTweenPath(path1Id, path2Id,
- tween, start, stop);
+ DrawTweenPath op = new DrawTweenPath(path1Id, path2Id, tween, start, stop);
operations.add(op);
}
-
public static String name() {
return "DrawTweenPath";
}
-
public static int id() {
return Operations.DRAW_TWEEN_PATH;
}
- public static void apply(WireBuffer buffer,
- int path1Id,
- int path2Id,
- float tween,
- float start,
- float stop) {
+ public static void apply(
+ WireBuffer buffer, int path1Id, int path2Id, float tween, float start, float stop) {
buffer.start(OP_CODE);
buffer.writeInt(path1Id);
buffer.writeInt(path2Id);
@@ -131,33 +117,18 @@ public class DrawTweenPath extends PaintOperation implements VariableSupport {
buffer.writeFloat(stop);
}
-
public static void documentation(DocumentationBuilder doc) {
- doc.operation("Draw Operations",
- OP_CODE,
- CLASS_NAME)
+ doc.operation("Draw Operations", OP_CODE, CLASS_NAME)
.description("Draw text along path object")
- .field(INT, "pathId1",
- "id of path 1")
- .field(INT, "pathId2",
- "id of path 2")
- .field(FLOAT, "tween",
- "interpolate between the two paths")
- .field(FLOAT, "start",
- "trim the start of the path")
- .field(FLOAT, "yOffset",
- "trim the end of the path");
-
+ .field(DocumentedOperation.INT, "pathId1", "id of path 1")
+ .field(DocumentedOperation.INT, "pathId2", "id of path 2")
+ .field(DocumentedOperation.FLOAT, "tween", "interpolate between the two paths")
+ .field(DocumentedOperation.FLOAT, "start", "trim the start of the path")
+ .field(DocumentedOperation.FLOAT, "yOffset", "trim the end of the path");
}
-
@Override
public void paint(PaintContext context) {
- context.drawTweenPath(mPath1Id,
- mPath2Id,
- mOutTween,
- mOutStart,
- mOutStop);
+ context.drawTweenPath(mPath1Id, mPath2Id, mOutTween, mOutStart, mOutStop);
}
-
}
diff --git a/core/java/com/android/internal/widget/remotecompose/core/operations/FloatConstant.java b/core/java/com/android/internal/widget/remotecompose/core/operations/FloatConstant.java
index 31b8ff6db58b..765e150e81af 100644
--- a/core/java/com/android/internal/widget/remotecompose/core/operations/FloatConstant.java
+++ b/core/java/com/android/internal/widget/remotecompose/core/operations/FloatConstant.java
@@ -15,21 +15,19 @@
*/
package com.android.internal.widget.remotecompose.core.operations;
-import static com.android.internal.widget.remotecompose.core.documentation.Operation.FLOAT;
-import static com.android.internal.widget.remotecompose.core.documentation.Operation.INT;
+import static com.android.internal.widget.remotecompose.core.documentation.DocumentedOperation.FLOAT;
import com.android.internal.widget.remotecompose.core.Operation;
import com.android.internal.widget.remotecompose.core.Operations;
import com.android.internal.widget.remotecompose.core.RemoteContext;
import com.android.internal.widget.remotecompose.core.WireBuffer;
import com.android.internal.widget.remotecompose.core.documentation.DocumentationBuilder;
+import com.android.internal.widget.remotecompose.core.documentation.DocumentedOperation;
import java.util.List;
-/**
- * Operation to deal with Text data
- */
-public class FloatConstant implements Operation {
+/** Operation to deal with Text data */
+public class FloatConstant implements com.android.internal.widget.remotecompose.core.Operation {
private static final int OP_CODE = Operations.DATA_FLOAT;
private static final String CLASS_NAME = "FloatConstant";
public int mTextId;
@@ -54,7 +52,6 @@ public class FloatConstant implements Operation {
return CLASS_NAME;
}
-
public static int id() {
return OP_CODE;
}
@@ -63,8 +60,8 @@ public class FloatConstant implements Operation {
* Writes out the operation to the buffer
*
* @param buffer write command to this buffer
- * @param id the id
- * @param value the value of the float
+ * @param id the id
+ * @param value the value of the float
*/
public static void apply(WireBuffer buffer, int id, float value) {
buffer.start(OP_CODE);
@@ -80,14 +77,10 @@ public class FloatConstant implements Operation {
}
public static void documentation(DocumentationBuilder doc) {
- doc.operation("Expressions Operations",
- OP_CODE,
- CLASS_NAME)
+ doc.operation("Expressions Operations", OP_CODE, CLASS_NAME)
.description("A float and its associated id")
- .field(INT, "id", "id of float")
- .field(FLOAT, "value",
- "32-bit float value");
-
+ .field(DocumentedOperation.INT, "id", "id of float")
+ .field(FLOAT, "value", "32-bit float value");
}
@Override
diff --git a/core/java/com/android/internal/widget/remotecompose/core/operations/FloatExpression.java b/core/java/com/android/internal/widget/remotecompose/core/operations/FloatExpression.java
index e3df1eb0da26..d71793364a33 100644
--- a/core/java/com/android/internal/widget/remotecompose/core/operations/FloatExpression.java
+++ b/core/java/com/android/internal/widget/remotecompose/core/operations/FloatExpression.java
@@ -15,10 +15,10 @@
*/
package com.android.internal.widget.remotecompose.core.operations;
-import static com.android.internal.widget.remotecompose.core.documentation.Operation.FLOAT;
-import static com.android.internal.widget.remotecompose.core.documentation.Operation.FLOAT_ARRAY;
-import static com.android.internal.widget.remotecompose.core.documentation.Operation.INT;
-import static com.android.internal.widget.remotecompose.core.documentation.Operation.SHORT;
+import static com.android.internal.widget.remotecompose.core.documentation.DocumentedOperation.FLOAT;
+import static com.android.internal.widget.remotecompose.core.documentation.DocumentedOperation.FLOAT_ARRAY;
+import static com.android.internal.widget.remotecompose.core.documentation.DocumentedOperation.INT;
+import static com.android.internal.widget.remotecompose.core.documentation.DocumentedOperation.SHORT;
import com.android.internal.widget.remotecompose.core.Operation;
import com.android.internal.widget.remotecompose.core.Operations;
@@ -26,19 +26,17 @@ import com.android.internal.widget.remotecompose.core.RemoteContext;
import com.android.internal.widget.remotecompose.core.VariableSupport;
import com.android.internal.widget.remotecompose.core.WireBuffer;
import com.android.internal.widget.remotecompose.core.documentation.DocumentationBuilder;
+import com.android.internal.widget.remotecompose.core.documentation.DocumentedOperation;
import com.android.internal.widget.remotecompose.core.operations.utilities.AnimatedFloatExpression;
import com.android.internal.widget.remotecompose.core.operations.utilities.NanMap;
import com.android.internal.widget.remotecompose.core.operations.utilities.easing.FloatAnimation;
-import java.util.Arrays;
import java.util.List;
/**
- * Operation to deal with AnimatedFloats
- * This is designed to be an optimized calculation for things like
- * injecting the width of the component int draw rect
- * As well as supporting generalized animation floats.
- * The floats represent a RPN style calculator
+ * Operation to deal with AnimatedFloats This is designed to be an optimized calculation for things
+ * like injecting the width of the component int draw rect As well as supporting generalized
+ * animation floats. The floats represent a RPN style calculator
*/
public class FloatExpression implements Operation, VariableSupport {
private static final int OP_CODE = Operations.ANIMATED_FLOAT;
@@ -49,6 +47,7 @@ public class FloatExpression implements Operation, VariableSupport {
public FloatAnimation mFloatAnimation;
public float[] mPreCalcValue;
private float mLastChange = Float.NaN;
+ private float mLastCalculatedValue = Float.NaN;
AnimatedFloatExpression mExp = new AnimatedFloatExpression();
public static final int MAX_EXPRESSION_SIZE = 32;
@@ -70,12 +69,12 @@ public class FloatExpression implements Operation, VariableSupport {
boolean value_changed = false;
for (int i = 0; i < mSrcValue.length; i++) {
float v = mSrcValue[i];
- if (Float.isNaN(v) && !AnimatedFloatExpression.isMathOperator(v)
+ if (Float.isNaN(v)
+ && !AnimatedFloatExpression.isMathOperator(v)
&& !NanMap.isDataVariable(v)) {
float newValue = context.getFloat(Utils.idFromNan(v));
if (mFloatAnimation != null) {
if (mPreCalcValue[i] != newValue) {
- mLastChange = context.getAnimationTime();
value_changed = true;
mPreCalcValue[i] = newValue;
}
@@ -86,8 +85,18 @@ public class FloatExpression implements Operation, VariableSupport {
mPreCalcValue[i] = mSrcValue[i];
}
}
+ float v = mLastCalculatedValue;
+ if (value_changed) { // inputs changed check if output changed
+ v = mExp.eval(mPreCalcValue, mPreCalcValue.length);
+ if (v != mLastCalculatedValue) {
+ mLastChange = context.getAnimationTime();
+ mLastCalculatedValue = v;
+ } else {
+ value_changed = false;
+ }
+ }
+
if (value_changed && mFloatAnimation != null) {
- float v = mExp.eval(Arrays.copyOf(mPreCalcValue, mPreCalcValue.length));
if (Float.isNaN(mFloatAnimation.getTargetValue())) {
mFloatAnimation.setInitialValue(v);
} else {
@@ -100,7 +109,8 @@ public class FloatExpression implements Operation, VariableSupport {
@Override
public void registerListening(RemoteContext context) {
for (float v : mSrcValue) {
- if (Float.isNaN(v) && !AnimatedFloatExpression.isMathOperator(v)
+ if (Float.isNaN(v)
+ && !AnimatedFloatExpression.isMathOperator(v)
&& !NanMap.isDataVariable(v)) {
context.listensTo(Utils.idFromNan(v), this);
}
@@ -118,8 +128,9 @@ public class FloatExpression implements Operation, VariableSupport {
float f = mFloatAnimation.get(t - mLastChange);
context.loadFloat(mId, f);
} else {
- context.loadFloat(mId, mExp.eval(context.getCollectionsAccess(),
- Arrays.copyOf(mPreCalcValue, mPreCalcValue.length)));
+ context.loadFloat(
+ mId,
+ mExp.eval(context.getCollectionsAccess(), mPreCalcValue, mPreCalcValue.length));
}
}
@@ -137,11 +148,17 @@ public class FloatExpression implements Operation, VariableSupport {
}
}
if (mPreCalcValue == null) {
- return "FloatExpression[" + mId + "] = ("
- + AnimatedFloatExpression.toString(mSrcValue, labels) + ")";
+ return "FloatExpression["
+ + mId
+ + "] = ("
+ + AnimatedFloatExpression.toString(mSrcValue, labels)
+ + ")";
}
- return "FloatExpression[" + mId + "] = ("
- + AnimatedFloatExpression.toString(mPreCalcValue, labels) + ")";
+ return "FloatExpression["
+ + mId
+ + "] = ("
+ + AnimatedFloatExpression.toString(mPreCalcValue, labels)
+ + ")";
}
public static String name() {
@@ -155,9 +172,9 @@ public class FloatExpression implements Operation, VariableSupport {
/**
* Writes out the operation to the buffer
*
- * @param buffer The buffer to write to
- * @param id the id of the resulting float
- * @param value the float expression array
+ * @param buffer The buffer to write to
+ * @param id the id of the resulting float
+ * @param value the float expression array
* @param animation the animation expression array
*/
public static void apply(WireBuffer buffer, int id, float[] value, float[] animation) {
@@ -178,7 +195,6 @@ public class FloatExpression implements Operation, VariableSupport {
buffer.writeFloat(v);
}
}
-
}
public static void read(WireBuffer buffer, List<Operation> operations) {
@@ -207,16 +223,20 @@ public class FloatExpression implements Operation, VariableSupport {
}
public static void documentation(DocumentationBuilder doc) {
- doc.operation("Expressions Operations",
- OP_CODE,
- CLASS_NAME)
+ doc.operation("Expressions Operations", OP_CODE, CLASS_NAME)
.description("A Float expression")
- .field(INT, "id", "The id of the Color")
+ .field(DocumentedOperation.INT, "id", "The id of the Color")
.field(SHORT, "expression_length", "expression length")
.field(SHORT, "animation_length", "animation description length")
- .field(FLOAT_ARRAY, "expression", "expression_length",
+ .field(
+ FLOAT_ARRAY,
+ "expression",
+ "expression_length",
"Sequence of Floats representing and expression")
- .field(FLOAT_ARRAY, "AnimationSpec", "animation_length",
+ .field(
+ FLOAT_ARRAY,
+ "AnimationSpec",
+ "animation_length",
"Sequence of Floats representing animation curve")
.field(FLOAT, "duration", "> time in sec")
.field(INT, "bits", "> WRAP|INITALVALUE | TYPE ")
@@ -229,5 +249,4 @@ public class FloatExpression implements Operation, VariableSupport {
public String deepToString(String indent) {
return indent + toString();
}
-
}
diff --git a/core/java/com/android/internal/widget/remotecompose/core/operations/Header.java b/core/java/com/android/internal/widget/remotecompose/core/operations/Header.java
index 099bce886b22..4f8516f5235d 100644
--- a/core/java/com/android/internal/widget/remotecompose/core/operations/Header.java
+++ b/core/java/com/android/internal/widget/remotecompose/core/operations/Header.java
@@ -15,8 +15,8 @@
*/
package com.android.internal.widget.remotecompose.core.operations;
-import static com.android.internal.widget.remotecompose.core.documentation.Operation.INT;
-import static com.android.internal.widget.remotecompose.core.documentation.Operation.LONG;
+import static com.android.internal.widget.remotecompose.core.documentation.DocumentedOperation.INT;
+import static com.android.internal.widget.remotecompose.core.documentation.DocumentedOperation.LONG;
import com.android.internal.widget.remotecompose.core.Operation;
import com.android.internal.widget.remotecompose.core.Operations;
@@ -29,9 +29,9 @@ import java.util.List;
/**
* Describe some basic information for a RemoteCompose document
- * <p>
- * It encodes the version of the document (following semantic versioning) as well
- * as the dimensions of the document in pixels.
+ *
+ * <p>It encodes the version of the document (following semantic versioning) as well as the
+ * dimensions of the document in pixels.
*/
public class Header implements RemoteComposeOperation {
private static final int OP_CODE = Operations.HEADER;
@@ -50,21 +50,26 @@ public class Header implements RemoteComposeOperation {
float mDensity;
long mCapabilities;
-
/**
- * It encodes the version of the document (following semantic versioning) as well
- * as the dimensions of the document in pixels.
+ * It encodes the version of the document (following semantic versioning) as well as the
+ * dimensions of the document in pixels.
*
* @param majorVersion the major version of the RemoteCompose document API
* @param minorVersion the minor version of the RemoteCompose document API
* @param patchVersion the patch version of the RemoteCompose document API
- * @param width the width of the RemoteCompose document
- * @param height the height of the RemoteCompose document
- * @param density the density at which the document was originally created
+ * @param width the width of the RemoteCompose document
+ * @param height the height of the RemoteCompose document
+ * @param density the density at which the document was originally created
* @param capabilities bitmask field storing needed capabilities (unused for now)
*/
- public Header(int majorVersion, int minorVersion, int patchVersion,
- int width, int height, float density, long capabilities) {
+ public Header(
+ int majorVersion,
+ int minorVersion,
+ int patchVersion,
+ int width,
+ int height,
+ float density,
+ long capabilities) {
this.mMajorVersion = majorVersion;
this.mMinorVersion = minorVersion;
this.mPatchVersion = patchVersion;
@@ -81,9 +86,19 @@ public class Header implements RemoteComposeOperation {
@Override
public String toString() {
- return "HEADER v" + mMajorVersion + "."
- + mMinorVersion + "." + mPatchVersion + ", "
- + mWidth + " x " + mHeight + " [" + mCapabilities + "]";
+ return "HEADER v"
+ + mMajorVersion
+ + "."
+ + mMinorVersion
+ + "."
+ + mPatchVersion
+ + ", "
+ + mWidth
+ + " x "
+ + mHeight
+ + " ["
+ + mCapabilities
+ + "]";
}
@Override
@@ -104,8 +119,8 @@ public class Header implements RemoteComposeOperation {
return OP_CODE;
}
- public static void apply(WireBuffer buffer, int width, int height,
- float density, long capabilities) {
+ public static void apply(
+ WireBuffer buffer, int width, int height, float density, long capabilities) {
buffer.start(OP_CODE);
buffer.writeInt(MAJOR_VERSION); // major version number of the protocol
buffer.writeInt(MINOR_VERSION); // minor version number of the protocol
@@ -125,15 +140,23 @@ public class Header implements RemoteComposeOperation {
// float density = buffer.readFloat();
float density = 1f;
long capabilities = buffer.readLong();
- Header header = new Header(majorVersion, minorVersion, patchVersion,
- width, height, density, capabilities);
+ Header header =
+ new Header(
+ majorVersion,
+ minorVersion,
+ patchVersion,
+ width,
+ height,
+ density,
+ capabilities);
operations.add(header);
}
public static void documentation(DocumentationBuilder doc) {
doc.operation("Protocol Operations", OP_CODE, CLASS_NAME)
- .description("Document metadata, containing the version,"
- + " original size & density, capabilities mask")
+ .description(
+ "Document metadata, containing the version,"
+ + " original size & density, capabilities mask")
.field(INT, "MAJOR_VERSION", "Major version")
.field(INT, "MINOR_VERSION", "Minor version")
.field(INT, "PATCH_VERSION", "Patch version")
@@ -142,5 +165,4 @@ public class Header implements RemoteComposeOperation {
// .field(FLOAT, "DENSITY", "Major version")
.field(LONG, "CAPABILITIES", "Major version");
}
-
}
diff --git a/core/java/com/android/internal/widget/remotecompose/core/operations/IntegerExpression.java b/core/java/com/android/internal/widget/remotecompose/core/operations/IntegerExpression.java
index 11730f358eaf..c9a850875011 100644
--- a/core/java/com/android/internal/widget/remotecompose/core/operations/IntegerExpression.java
+++ b/core/java/com/android/internal/widget/remotecompose/core/operations/IntegerExpression.java
@@ -15,8 +15,8 @@
*/
package com.android.internal.widget.remotecompose.core.operations;
-import static com.android.internal.widget.remotecompose.core.documentation.Operation.INT;
-import static com.android.internal.widget.remotecompose.core.documentation.Operation.INT_ARRAY;
+import static com.android.internal.widget.remotecompose.core.documentation.DocumentedOperation.INT;
+import static com.android.internal.widget.remotecompose.core.documentation.DocumentedOperation.INT_ARRAY;
import com.android.internal.widget.remotecompose.core.Operation;
import com.android.internal.widget.remotecompose.core.Operations;
@@ -24,17 +24,16 @@ import com.android.internal.widget.remotecompose.core.RemoteContext;
import com.android.internal.widget.remotecompose.core.VariableSupport;
import com.android.internal.widget.remotecompose.core.WireBuffer;
import com.android.internal.widget.remotecompose.core.documentation.DocumentationBuilder;
+import com.android.internal.widget.remotecompose.core.documentation.DocumentedOperation;
import com.android.internal.widget.remotecompose.core.operations.utilities.IntegerExpressionEvaluator;
import java.util.Arrays;
import java.util.List;
/**
- * Operation to deal with AnimatedFloats
- * This is designed to be an optimized calculation for things like
- * injecting the width of the component int draw rect
- * As well as supporting generalized animation floats.
- * The floats represent a RPN style calculator
+ * Operation to deal with AnimatedFloats This is designed to be an optimized calculation for things
+ * like injecting the width of the component int draw rect As well as supporting generalized
+ * animation floats. The floats represent a RPN style calculator
*/
public class IntegerExpression implements Operation, VariableSupport {
private static final int OP_CODE = Operations.INTEGER_EXPRESSION;
@@ -45,7 +44,7 @@ public class IntegerExpression implements Operation, VariableSupport {
public int[] mSrcValue;
public int[] mPreCalcValue;
private float mLastChange = Float.NaN;
- public static final int MAX_STRING_SIZE = 4000;
+ public static final int MAX_SIZE = 320;
IntegerExpressionEvaluator mExp = new IntegerExpressionEvaluator();
public IntegerExpression(int id, int mask, int[] value) {
@@ -70,7 +69,6 @@ public class IntegerExpression implements Operation, VariableSupport {
}
}
-
@Override
public void registerListening(RemoteContext context) {
for (int i = 0; i < mSrcValue.length; i++) {
@@ -91,6 +89,21 @@ public class IntegerExpression implements Operation, VariableSupport {
context.loadInteger(mId, v);
}
+ /**
+ * Evaluate the expression
+ *
+ * @param context current context
+ * @return the resulting value
+ */
+ public int evaluate(RemoteContext context) {
+ updateVariables(context);
+ float t = context.getAnimationTime();
+ if (Float.isNaN(mLastChange)) {
+ mLastChange = t;
+ }
+ return mExp.eval(mPreMask, Arrays.copyOf(mPreCalcValue, mPreCalcValue.length));
+ }
+
@Override
public void write(WireBuffer buffer) {
apply(buffer, mId, mMask, mSrcValue);
@@ -99,6 +112,9 @@ public class IntegerExpression implements Operation, VariableSupport {
@Override
public String toString() {
StringBuilder s = new StringBuilder();
+ if (mPreCalcValue == null) {
+ return "";
+ }
for (int i = 0; i < mPreCalcValue.length; i++) {
if (i != 0) {
s.append(" ");
@@ -128,9 +144,9 @@ public class IntegerExpression implements Operation, VariableSupport {
* Writes out the operation to the buffer
*
* @param buffer buffer to write to
- * @param id the id of the integer
- * @param mask the mask bits of ints & operators or variables
- * @param value array of integers to be evaluated
+ * @param id the id of the integer
+ * @param mask the mask bits of ints & operators or variables
+ * @param value array of integers to be evaluated
*/
public static void apply(WireBuffer buffer, int id, int mask, int[] value) {
buffer.start(OP_CODE);
@@ -146,7 +162,9 @@ public class IntegerExpression implements Operation, VariableSupport {
int id = buffer.readInt();
int mask = buffer.readInt();
int len = buffer.readInt();
-
+ if (len > MAX_SIZE) {
+ throw new RuntimeException("buffer corrupt integer expression " + len);
+ }
int[] values = new int[len];
for (int i = 0; i < values.length; i++) {
values[i] = buffer.readInt();
@@ -156,17 +174,12 @@ public class IntegerExpression implements Operation, VariableSupport {
}
public static void documentation(DocumentationBuilder doc) {
- doc.operation("Data Operations",
- OP_CODE,
- CLASS_NAME)
+ doc.operation("Data Operations", OP_CODE, CLASS_NAME)
.description("Expression that computes an integer")
- .field(INT, "id", "id of integer")
- .field(INT, "mask",
- "bits representing operators or other id's")
- .field(INT, "length",
- "length of array")
- .field(INT_ARRAY, "values", "length",
- "Array of ints");
+ .field(DocumentedOperation.INT, "id", "id of integer")
+ .field(INT, "mask", "bits representing operators or other id's")
+ .field(INT, "length", "length of array")
+ .field(INT_ARRAY, "values", "length", "Array of ints");
}
@Override
@@ -177,8 +190,8 @@ public class IntegerExpression implements Operation, VariableSupport {
/**
* given the "i" position in the mask is this an ID
*
- * @param mask 32 bit mask used for defining numbers vs other
- * @param i the bit in question
+ * @param mask 32 bit mask used for defining numbers vs other
+ * @param i the bit in question
* @param value the value
* @return true if this is an ID
*/
diff --git a/core/java/com/android/internal/widget/remotecompose/core/operations/MatrixRestore.java b/core/java/com/android/internal/widget/remotecompose/core/operations/MatrixRestore.java
index f3f9a5142a24..04f8a503adff 100644
--- a/core/java/com/android/internal/widget/remotecompose/core/operations/MatrixRestore.java
+++ b/core/java/com/android/internal/widget/remotecompose/core/operations/MatrixRestore.java
@@ -27,15 +27,14 @@ import java.util.List;
public class MatrixRestore extends PaintOperation {
private static final int OP_CODE = Operations.MATRIX_RESTORE;
private static final String CLASS_NAME = "MatrixRestore";
- public MatrixRestore() {
- }
+
+ public MatrixRestore() {}
@Override
public void write(WireBuffer buffer) {
apply(buffer);
}
-
public static void read(WireBuffer buffer, List<Operation> operations) {
MatrixRestore op = new MatrixRestore();
operations.add(op);
@@ -46,12 +45,10 @@ public class MatrixRestore extends PaintOperation {
return "MatrixRestore";
}
-
public static String name() {
return CLASS_NAME;
}
-
public static int id() {
return OP_CODE;
}
@@ -61,13 +58,10 @@ public class MatrixRestore extends PaintOperation {
}
public static void documentation(DocumentationBuilder doc) {
- doc.operation("Canvas Operations",
- OP_CODE,
- CLASS_NAME)
+ doc.operation("Canvas Operations", OP_CODE, CLASS_NAME)
.description("Restore the matrix and clip");
}
-
@Override
public void paint(PaintContext context) {
context.matrixRestore();
diff --git a/core/java/com/android/internal/widget/remotecompose/core/operations/MatrixRotate.java b/core/java/com/android/internal/widget/remotecompose/core/operations/MatrixRotate.java
index 9cc82fc05e69..df10f329630a 100644
--- a/core/java/com/android/internal/widget/remotecompose/core/operations/MatrixRotate.java
+++ b/core/java/com/android/internal/widget/remotecompose/core/operations/MatrixRotate.java
@@ -15,13 +15,12 @@
*/
package com.android.internal.widget.remotecompose.core.operations;
-import static com.android.internal.widget.remotecompose.core.documentation.Operation.FLOAT;
-
import com.android.internal.widget.remotecompose.core.Operation;
import com.android.internal.widget.remotecompose.core.Operations;
import com.android.internal.widget.remotecompose.core.PaintContext;
import com.android.internal.widget.remotecompose.core.WireBuffer;
import com.android.internal.widget.remotecompose.core.documentation.DocumentationBuilder;
+import com.android.internal.widget.remotecompose.core.documentation.DocumentedOperation;
import java.util.List;
@@ -30,12 +29,13 @@ public class MatrixRotate extends DrawBase3 {
private static final String CLASS_NAME = "MatrixRotate";
public static void read(WireBuffer buffer, List<Operation> operations) {
- Maker m = new Maker() {
- @Override
- public DrawBase3 create(float v1, float v2, float v3) {
- return new MatrixRotate(v1, v2, v3);
- }
- };
+ Maker m =
+ new Maker() {
+ @Override
+ public DrawBase3 create(float v1, float v2, float v3) {
+ return new MatrixRotate(v1, v2, v3);
+ }
+ };
read(m, buffer, operations);
}
@@ -48,20 +48,15 @@ public class MatrixRotate extends DrawBase3 {
}
public static void documentation(DocumentationBuilder doc) {
- doc.operation("Canvas Operations",
- OP_CODE,
- CLASS_NAME)
+ doc.operation("Canvas Operations", OP_CODE, CLASS_NAME)
.description("apply rotation to matrix")
- .field(FLOAT, "rotate", "Angle to rotate")
- .field(FLOAT, "pivotX", "X Pivot point")
- .field(FLOAT, "pivotY", "Y Pivot point");
+ .field(DocumentedOperation.FLOAT, "rotate", "Angle to rotate")
+ .field(DocumentedOperation.FLOAT, "pivotX", "X Pivot point")
+ .field(DocumentedOperation.FLOAT, "pivotY", "Y Pivot point");
}
-
- protected void write(WireBuffer buffer,
- float v1,
- float v2,
- float v3) {
+ @Override
+ protected void write(WireBuffer buffer, float v1, float v2, float v3) {
apply(buffer, v1, v2, v3);
}
@@ -83,10 +78,7 @@ public class MatrixRotate extends DrawBase3 {
* @param y1 X Pivot point
* @param x2 Y Pivot point
*/
- public static void apply(WireBuffer buffer,
- float x1,
- float y1,
- float x2) {
+ public static void apply(WireBuffer buffer, float x1, float y1, float x2) {
buffer.start(OP_CODE);
buffer.writeFloat(x1);
buffer.writeFloat(y1);
diff --git a/core/java/com/android/internal/widget/remotecompose/core/operations/MatrixSave.java b/core/java/com/android/internal/widget/remotecompose/core/operations/MatrixSave.java
index a47ed6b9f99a..67612c7fd2f4 100644
--- a/core/java/com/android/internal/widget/remotecompose/core/operations/MatrixSave.java
+++ b/core/java/com/android/internal/widget/remotecompose/core/operations/MatrixSave.java
@@ -47,7 +47,6 @@ public class MatrixSave extends PaintOperation {
return CLASS_NAME;
}
-
public static int id() {
return OP_CODE;
}
@@ -57,9 +56,7 @@ public class MatrixSave extends PaintOperation {
}
public static void documentation(DocumentationBuilder doc) {
- doc.operation("Canvas Operations",
- OP_CODE,
- CLASS_NAME)
+ doc.operation("Canvas Operations", OP_CODE, CLASS_NAME)
.description("Save the matrix and clip to a stack");
}
diff --git a/core/java/com/android/internal/widget/remotecompose/core/operations/MatrixScale.java b/core/java/com/android/internal/widget/remotecompose/core/operations/MatrixScale.java
index 769e798eb6b2..26c898acb67b 100644
--- a/core/java/com/android/internal/widget/remotecompose/core/operations/MatrixScale.java
+++ b/core/java/com/android/internal/widget/remotecompose/core/operations/MatrixScale.java
@@ -15,13 +15,12 @@
*/
package com.android.internal.widget.remotecompose.core.operations;
-import static com.android.internal.widget.remotecompose.core.documentation.Operation.FLOAT;
-
import com.android.internal.widget.remotecompose.core.Operation;
import com.android.internal.widget.remotecompose.core.Operations;
import com.android.internal.widget.remotecompose.core.PaintContext;
import com.android.internal.widget.remotecompose.core.WireBuffer;
import com.android.internal.widget.remotecompose.core.documentation.DocumentationBuilder;
+import com.android.internal.widget.remotecompose.core.documentation.DocumentedOperation;
import java.util.List;
@@ -42,28 +41,18 @@ public class MatrixScale extends DrawBase4 {
return CLASS_NAME;
}
-
- protected void write(WireBuffer buffer,
- float v1,
- float v2,
- float v3,
- float v4) {
+ @Override
+ protected void write(WireBuffer buffer, float v1, float v2, float v3, float v4) {
apply(buffer, v1, v2, v3, v4);
}
public static void documentation(DocumentationBuilder doc) {
- doc.operation("Canvas Operations",
- OP_CODE,
- CLASS_NAME)
+ doc.operation("Canvas Operations", OP_CODE, CLASS_NAME)
.description("Draw the specified Oval")
- .field(FLOAT, "scaleX",
- "The amount to scale in X")
- .field(FLOAT, "scaleY",
- "The amount to scale in Y")
- .field(FLOAT, "pivotX",
- "The x-coordinate for the pivot point")
- .field(FLOAT, "pivotY",
- "The y-coordinate for the pivot point");
+ .field(DocumentedOperation.FLOAT, "scaleX", "The amount to scale in X")
+ .field(DocumentedOperation.FLOAT, "scaleY", "The amount to scale in Y")
+ .field(DocumentedOperation.FLOAT, "pivotX", "The x-coordinate for the pivot point")
+ .field(DocumentedOperation.FLOAT, "pivotY", "The y-coordinate for the pivot point");
}
public MatrixScale(float scaleX, float scaleY, float centerX, float centerY) {
@@ -80,16 +69,12 @@ public class MatrixScale extends DrawBase4 {
* Writes out the DrawOval to the buffer
*
* @param buffer buffer to write to
- * @param x1 start x of DrawOval
- * @param y1 start y of the DrawOval
- * @param x2 end x of the DrawOval
- * @param y2 end y of the DrawOval
+ * @param x1 start x of DrawOval
+ * @param y1 start y of the DrawOval
+ * @param x2 end x of the DrawOval
+ * @param y2 end y of the DrawOval
*/
- public static void apply(WireBuffer buffer,
- float x1,
- float y1,
- float x2,
- float y2) {
+ public static void apply(WireBuffer buffer, float x1, float y1, float x2, float y2) {
write(buffer, OP_CODE, x1, y1, x2, y2);
}
}
diff --git a/core/java/com/android/internal/widget/remotecompose/core/operations/MatrixSkew.java b/core/java/com/android/internal/widget/remotecompose/core/operations/MatrixSkew.java
index 34f71b4d1a91..d64117871eaa 100644
--- a/core/java/com/android/internal/widget/remotecompose/core/operations/MatrixSkew.java
+++ b/core/java/com/android/internal/widget/remotecompose/core/operations/MatrixSkew.java
@@ -15,7 +15,7 @@
*/
package com.android.internal.widget.remotecompose.core.operations;
-import static com.android.internal.widget.remotecompose.core.documentation.Operation.FLOAT;
+import static com.android.internal.widget.remotecompose.core.documentation.DocumentedOperation.FLOAT;
import com.android.internal.widget.remotecompose.core.Operation;
import com.android.internal.widget.remotecompose.core.Operations;
@@ -29,7 +29,6 @@ public class MatrixSkew extends DrawBase2 {
public static final int OP_CODE = Operations.MATRIX_SKEW;
public static final String CLASS_NAME = "MatrixSkew";
-
public static void read(WireBuffer buffer, List<Operation> operations) {
Maker m = MatrixSkew::new;
read(m, buffer, operations);
@@ -43,25 +42,18 @@ public class MatrixSkew extends DrawBase2 {
return CLASS_NAME;
}
-
- protected void write(WireBuffer buffer,
- float v1,
- float v2) {
+ @Override
+ protected void write(WireBuffer buffer, float v1, float v2) {
apply(buffer, v1, v2);
}
public static void documentation(DocumentationBuilder doc) {
- doc.operation("Canvas Operations",
- OP_CODE,
- CLASS_NAME)
+ doc.operation("Canvas Operations", OP_CODE, CLASS_NAME)
.description("Current matrix with the specified skew.")
- .field(FLOAT, "skewX",
- "The amount to skew in X")
- .field(FLOAT, "skewY",
- "The amount to skew in Y");
+ .field(FLOAT, "skewX", "The amount to skew in X")
+ .field(FLOAT, "skewY", "The amount to skew in Y");
}
-
public MatrixSkew(float skewX, float skewY) {
super(skewX, skewY);
mName = CLASS_NAME;
@@ -76,13 +68,10 @@ public class MatrixSkew extends DrawBase2 {
* Writes out the DrawOval to the buffer
*
* @param buffer buffer to write to
- * @param x1 start x of DrawOval
- * @param y1 start y of the DrawOval
+ * @param x1 start x of DrawOval
+ * @param y1 start y of the DrawOval
*/
- public static void apply(WireBuffer buffer,
- float x1,
- float y1
- ) {
+ public static void apply(WireBuffer buffer, float x1, float y1) {
write(buffer, OP_CODE, x1, y1);
}
}
diff --git a/core/java/com/android/internal/widget/remotecompose/core/operations/MatrixTranslate.java b/core/java/com/android/internal/widget/remotecompose/core/operations/MatrixTranslate.java
index 8561343c0015..e008292f1107 100644
--- a/core/java/com/android/internal/widget/remotecompose/core/operations/MatrixTranslate.java
+++ b/core/java/com/android/internal/widget/remotecompose/core/operations/MatrixTranslate.java
@@ -15,13 +15,12 @@
*/
package com.android.internal.widget.remotecompose.core.operations;
-import static com.android.internal.widget.remotecompose.core.documentation.Operation.FLOAT;
-
import com.android.internal.widget.remotecompose.core.Operation;
import com.android.internal.widget.remotecompose.core.Operations;
import com.android.internal.widget.remotecompose.core.PaintContext;
import com.android.internal.widget.remotecompose.core.WireBuffer;
import com.android.internal.widget.remotecompose.core.documentation.DocumentationBuilder;
+import com.android.internal.widget.remotecompose.core.documentation.DocumentedOperation;
import java.util.List;
@@ -42,22 +41,16 @@ public class MatrixTranslate extends DrawBase2 {
return CLASS_NAME;
}
- protected void write(WireBuffer buffer,
- float v1,
- float v2) {
+ @Override
+ protected void write(WireBuffer buffer, float v1, float v2) {
apply(buffer, v1, v2);
}
public static void documentation(DocumentationBuilder doc) {
- doc.operation("Canvas Operations",
- OP_CODE,
- "MatrixTranslate")
+ doc.operation("Canvas Operations", OP_CODE, "MatrixTranslate")
.description("Preconcat the current matrix with the specified translation")
- .field(FLOAT, "dx",
- "The distance to translate in X")
- .field(FLOAT, "dy",
- "The distance to translate in Y");
-
+ .field(DocumentedOperation.FLOAT, "dx", "The distance to translate in X")
+ .field(DocumentedOperation.FLOAT, "dy", "The distance to translate in Y");
}
public MatrixTranslate(float translateX, float translateY) {
@@ -74,12 +67,10 @@ public class MatrixTranslate extends DrawBase2 {
* Writes out the DrawOval to the buffer
*
* @param buffer buffer to write to
- * @param x1 start x of DrawOval
- * @param y1 start y of the DrawOval
+ * @param x1 start x of DrawOval
+ * @param y1 start y of the DrawOval
*/
- public static void apply(WireBuffer buffer,
- float x1,
- float y1) {
+ public static void apply(WireBuffer buffer, float x1, float y1) {
write(buffer, OP_CODE, x1, y1);
}
}
diff --git a/core/java/com/android/internal/widget/remotecompose/core/operations/NamedVariable.java b/core/java/com/android/internal/widget/remotecompose/core/operations/NamedVariable.java
index 2cf83cd6caec..fa6e2712a0bb 100644
--- a/core/java/com/android/internal/widget/remotecompose/core/operations/NamedVariable.java
+++ b/core/java/com/android/internal/widget/remotecompose/core/operations/NamedVariable.java
@@ -15,20 +15,19 @@
*/
package com.android.internal.widget.remotecompose.core.operations;
-import static com.android.internal.widget.remotecompose.core.documentation.Operation.INT;
-import static com.android.internal.widget.remotecompose.core.documentation.Operation.UTF8;
+import static com.android.internal.widget.remotecompose.core.documentation.DocumentedOperation.INT;
+import static com.android.internal.widget.remotecompose.core.documentation.DocumentedOperation.UTF8;
import com.android.internal.widget.remotecompose.core.Operation;
import com.android.internal.widget.remotecompose.core.Operations;
import com.android.internal.widget.remotecompose.core.RemoteContext;
import com.android.internal.widget.remotecompose.core.WireBuffer;
import com.android.internal.widget.remotecompose.core.documentation.DocumentationBuilder;
+import com.android.internal.widget.remotecompose.core.documentation.DocumentedOperation;
import java.util.List;
-/**
- * Operation to deal with Text data
- */
+/** Operation to deal with Text data */
public class NamedVariable implements Operation {
private static final int OP_CODE = Operations.NAMED_VARIABLE;
private static final String CLASS_NAME = "NamedVariable";
@@ -39,6 +38,7 @@ public class NamedVariable implements Operation {
public static final int COLOR_TYPE = 2;
public static final int FLOAT_TYPE = 1;
public static final int STRING_TYPE = 0;
+ public static final int IMAGE_TYPE = 3;
public NamedVariable(int varId, int varType, String name) {
this.mVarId = varId;
@@ -53,8 +53,12 @@ public class NamedVariable implements Operation {
@Override
public String toString() {
- return "VariableName[" + mVarId + "] = \""
- + Utils.trimString(mVarName, 10) + "\" type=" + mVarType;
+ return "VariableName["
+ + mVarId
+ + "] = \""
+ + Utils.trimString(mVarName, 10)
+ + "\" type="
+ + mVarType;
}
public static String name() {
@@ -80,7 +84,7 @@ public class NamedVariable implements Operation {
buffer.writeUTF8(text);
}
- public static void read(WireBuffer buffer, List<Operation> operations) {
+ public static void read(WireBuffer buffer, List<Operation> operations) {
int varId = buffer.readInt();
int varType = buffer.readInt();
String text = buffer.readUTF8(MAX_STRING_SIZE);
@@ -88,11 +92,9 @@ public class NamedVariable implements Operation {
}
public static void documentation(DocumentationBuilder doc) {
- doc.operation("Data Operations",
- OP_CODE,
- CLASS_NAME)
+ doc.operation("Data Operations", OP_CODE, CLASS_NAME)
.description("Add a string name for an ID")
- .field(INT, "varId", "id to label")
+ .field(DocumentedOperation.INT, "varId", "id to label")
.field(INT, "varType", "The type of variable")
.field(UTF8, "name", "String");
}
diff --git a/core/java/com/android/internal/widget/remotecompose/core/operations/PaintData.java b/core/java/com/android/internal/widget/remotecompose/core/operations/PaintData.java
index ae7a89279320..095a0106b3d7 100644
--- a/core/java/com/android/internal/widget/remotecompose/core/operations/PaintData.java
+++ b/core/java/com/android/internal/widget/remotecompose/core/operations/PaintData.java
@@ -15,8 +15,8 @@
*/
package com.android.internal.widget.remotecompose.core.operations;
-import static com.android.internal.widget.remotecompose.core.documentation.Operation.INT;
-import static com.android.internal.widget.remotecompose.core.documentation.Operation.INT_ARRAY;
+import static com.android.internal.widget.remotecompose.core.documentation.DocumentedOperation.INT;
+import static com.android.internal.widget.remotecompose.core.documentation.DocumentedOperation.INT_ARRAY;
import com.android.internal.widget.remotecompose.core.Operation;
import com.android.internal.widget.remotecompose.core.Operations;
@@ -36,8 +36,7 @@ public class PaintData extends PaintOperation implements VariableSupport {
public PaintBundle mPaintData = new PaintBundle();
public static final int MAX_STRING_SIZE = 4000;
- public PaintData() {
- }
+ public PaintData() {}
@Override
public void updateVariables(RemoteContext context) {
@@ -59,7 +58,6 @@ public class PaintData extends PaintOperation implements VariableSupport {
return "PaintData " + "\"" + mPaintData + "\"";
}
-
public static String name() {
return CLASS_NAME;
}
@@ -80,13 +78,10 @@ public class PaintData extends PaintOperation implements VariableSupport {
}
public static void documentation(DocumentationBuilder doc) {
- doc.operation("Data Operations",
- OP_CODE,
- CLASS_NAME)
+ doc.operation("Data Operations", OP_CODE, CLASS_NAME)
.description("Encode a Paint ")
.field(INT, "length", "id string")
- .field(INT_ARRAY, "paint", "length",
- "path encoded as floats");
+ .field(INT_ARRAY, "paint", "length", "path encoded as floats");
}
@Override
diff --git a/core/java/com/android/internal/widget/remotecompose/core/operations/PathData.java b/core/java/com/android/internal/widget/remotecompose/core/operations/PathData.java
index 91352d914a99..13d5a49a584b 100644
--- a/core/java/com/android/internal/widget/remotecompose/core/operations/PathData.java
+++ b/core/java/com/android/internal/widget/remotecompose/core/operations/PathData.java
@@ -15,8 +15,8 @@
*/
package com.android.internal.widget.remotecompose.core.operations;
-import static com.android.internal.widget.remotecompose.core.documentation.Operation.FLOAT_ARRAY;
-import static com.android.internal.widget.remotecompose.core.documentation.Operation.INT;
+import static com.android.internal.widget.remotecompose.core.documentation.DocumentedOperation.FLOAT_ARRAY;
+import static com.android.internal.widget.remotecompose.core.documentation.DocumentedOperation.INT;
import com.android.internal.widget.remotecompose.core.Operation;
import com.android.internal.widget.remotecompose.core.Operations;
@@ -24,6 +24,7 @@ import com.android.internal.widget.remotecompose.core.RemoteContext;
import com.android.internal.widget.remotecompose.core.VariableSupport;
import com.android.internal.widget.remotecompose.core.WireBuffer;
import com.android.internal.widget.remotecompose.core.documentation.DocumentationBuilder;
+import com.android.internal.widget.remotecompose.core.documentation.DocumentedOperation;
import java.util.Arrays;
import java.util.List;
@@ -46,8 +47,7 @@ public class PathData implements Operation, VariableSupport {
for (int i = 0; i < mFloatPath.length; i++) {
float v = mFloatPath[i];
if (Utils.isVariable(v)) {
- mOutputPath[i] = (Float.isNaN(v))
- ? context.getFloat(Utils.idFromNan(v)) : v;
+ mOutputPath[i] = Float.isNaN(v) ? context.getFloat(Utils.idFromNan(v)) : v;
} else {
mOutputPath[i] = v;
}
@@ -79,30 +79,15 @@ public class PathData implements Operation, VariableSupport {
}
/**
- * public float[] getFloatPath(PaintContext context) {
- * float[] ret = mRetFloats; // Assume retFloats is declared elsewhere
- * if (ret == null) {
- * return mFloatPath; // Assume floatPath is declared elsewhere
- * }
- * float[] localRef = mRef; // Assume ref is of type Float[]
- * if (localRef == null) {
- * for (int i = 0; i < mFloatPath.length; i++) {
- * ret[i] = mFloatPath[i];
- * }
- * } else {
- * for (int i = 0; i < mFloatPath.length; i++) {
- * float lr = localRef[i];
- * if (Float.isNaN(lr)) {
- * ret[i] = Utils.getActualValue(lr);
- * } else {
- * ret[i] = mFloatPath[i];
- * }
- * }
- * }
- * return ret;
- * }
+ * public float[] getFloatPath(PaintContext context) { float[] ret = mRetFloats; // Assume
+ * retFloats is declared elsewhere if (ret == null) { return mFloatPath; // Assume floatPath is
+ * declared elsewhere } float[] localRef = mRef; // Assume ref is of type Float[] if (localRef
+ * == null) { for (int i = 0; i < mFloatPath.length; i++) { ret[i] = mFloatPath[i]; } } else {
+ * for (int i = 0; i < mFloatPath.length; i++) { float lr = localRef[i]; if (Float.isNaN(lr)) {
+ * ret[i] = Utils.getActualValue(lr); } else { ret[i] = mFloatPath[i]; } } } return ret; }
*/
public static final int MOVE = 10;
+
public static final int LINE = 11;
public static final int QUADRATIC = 12;
public static final int CONIC = 13;
@@ -145,17 +130,13 @@ public class PathData implements Operation, VariableSupport {
}
public static void documentation(DocumentationBuilder doc) {
- doc.operation("Data Operations",
- OP_CODE,
- CLASS_NAME)
+ doc.operation("Data Operations", OP_CODE, CLASS_NAME)
.description("Encode a Path ")
- .field(INT, "id", "id string")
+ .field(DocumentedOperation.INT, "id", "id string")
.field(INT, "length", "id string")
- .field(FLOAT_ARRAY, "pathData", "length",
- "path encoded as floats");
+ .field(FLOAT_ARRAY, "pathData", "length", "path encoded as floats");
}
-
public static String pathString(float[] path) {
if (path == null) {
return "null";
@@ -208,5 +189,4 @@ public class PathData implements Operation, VariableSupport {
public void apply(RemoteContext context) {
context.loadPathData(mInstanceId, mOutputPath);
}
-
}
diff --git a/core/java/com/android/internal/widget/remotecompose/core/operations/RootContentBehavior.java b/core/java/com/android/internal/widget/remotecompose/core/operations/RootContentBehavior.java
index 33f997fc7266..4a8f5324b74e 100644
--- a/core/java/com/android/internal/widget/remotecompose/core/operations/RootContentBehavior.java
+++ b/core/java/com/android/internal/widget/remotecompose/core/operations/RootContentBehavior.java
@@ -15,22 +15,21 @@
*/
package com.android.internal.widget.remotecompose.core.operations;
-import static com.android.internal.widget.remotecompose.core.documentation.Operation.INT;
-
import com.android.internal.widget.remotecompose.core.Operation;
import com.android.internal.widget.remotecompose.core.Operations;
import com.android.internal.widget.remotecompose.core.RemoteComposeOperation;
import com.android.internal.widget.remotecompose.core.RemoteContext;
import com.android.internal.widget.remotecompose.core.WireBuffer;
import com.android.internal.widget.remotecompose.core.documentation.DocumentationBuilder;
+import com.android.internal.widget.remotecompose.core.documentation.DocumentedOperation;
import java.util.List;
/**
* Describe some basic information for a RemoteCompose document
- * <p>
- * It encodes the version of the document (following semantic versioning) as well
- * as the dimensions of the document in pixels.
+ *
+ * <p>It encodes the version of the document (following semantic versioning) as well as the
+ * dimensions of the document in pixels.
*/
public class RootContentBehavior implements RemoteComposeOperation {
private static final int OP_CODE = Operations.ROOT_CONTENT_BEHAVIOR;
@@ -67,8 +66,8 @@ public class RootContentBehavior implements RemoteComposeOperation {
public static final int ALIGNMENT_START = 16;
public static final int ALIGNMENT_HORIZONTAL_CENTER = 32;
public static final int ALIGNMENT_END = 64;
- public static final int ALIGNMENT_CENTER = ALIGNMENT_HORIZONTAL_CENTER
- + ALIGNMENT_VERTICAL_CENTER;
+ public static final int ALIGNMENT_CENTER =
+ ALIGNMENT_HORIZONTAL_CENTER + ALIGNMENT_VERTICAL_CENTER;
///////////////////////////////////////////////////////////////////////////////////////////////
// Layout mode
@@ -98,21 +97,14 @@ public class RootContentBehavior implements RemoteComposeOperation {
/**
* Sets the way the player handles the content
*
- * @param scroll set the horizontal behavior (NONE|SCROLL_HORIZONTAL|SCROLL_VERTICAL)
+ * @param scroll set the horizontal behavior (NONE|SCROLL_HORIZONTAL|SCROLL_VERTICAL)
* @param alignment set the alignment of the content (TOP|CENTER|BOTTOM|START|END)
- * @param sizing set the type of sizing for the content (NONE|SIZING_LAYOUT|SIZING_SCALE)
- * @param mode set the mode of sizing, either LAYOUT modes or SCALE modes
- * the LAYOUT modes are:
- * - LAYOUT_MATCH_PARENT
- * - LAYOUT_WRAP_CONTENT
- * or adding an horizontal mode and a vertical mode:
- * - LAYOUT_HORIZONTAL_MATCH_PARENT
- * - LAYOUT_HORIZONTAL_WRAP_CONTENT
- * - LAYOUT_HORIZONTAL_FIXED
- * - LAYOUT_VERTICAL_MATCH_PARENT
- * - LAYOUT_VERTICAL_WRAP_CONTENT
- * - LAYOUT_VERTICAL_FIXED
- * The LAYOUT_*_FIXED modes will use the intrinsic document size
+ * @param sizing set the type of sizing for the content (NONE|SIZING_LAYOUT|SIZING_SCALE)
+ * @param mode set the mode of sizing, either LAYOUT modes or SCALE modes the LAYOUT modes are:
+ * - LAYOUT_MATCH_PARENT - LAYOUT_WRAP_CONTENT or adding an horizontal mode and a vertical
+ * mode: - LAYOUT_HORIZONTAL_MATCH_PARENT - LAYOUT_HORIZONTAL_WRAP_CONTENT -
+ * LAYOUT_HORIZONTAL_FIXED - LAYOUT_VERTICAL_MATCH_PARENT - LAYOUT_VERTICAL_WRAP_CONTENT -
+ * LAYOUT_VERTICAL_FIXED The LAYOUT_*_FIXED modes will use the intrinsic document size
*/
public RootContentBehavior(int scroll, int alignment, int sizing, int mode) {
switch (scroll) {
@@ -121,41 +113,43 @@ public class RootContentBehavior implements RemoteComposeOperation {
case SCROLL_VERTICAL:
mScroll = scroll;
break;
- default: {
+ default:
System.out.println(TAG + "incorrect scroll value " + scroll);
- }
}
if (alignment == ALIGNMENT_CENTER) {
mAlignment = alignment;
} else {
int horizontalContentAlignment = alignment & 0xF0;
int verticalContentAlignment = alignment & 0xF;
- boolean validHorizontalAlignment = horizontalContentAlignment == ALIGNMENT_START
- || horizontalContentAlignment == ALIGNMENT_HORIZONTAL_CENTER
- || horizontalContentAlignment == ALIGNMENT_END;
- boolean validVerticalAlignment = verticalContentAlignment == ALIGNMENT_TOP
- || verticalContentAlignment == ALIGNMENT_VERTICAL_CENTER
- || verticalContentAlignment == ALIGNMENT_BOTTOM;
+ boolean validHorizontalAlignment =
+ horizontalContentAlignment == ALIGNMENT_START
+ || horizontalContentAlignment == ALIGNMENT_HORIZONTAL_CENTER
+ || horizontalContentAlignment == ALIGNMENT_END;
+ boolean validVerticalAlignment =
+ verticalContentAlignment == ALIGNMENT_TOP
+ || verticalContentAlignment == ALIGNMENT_VERTICAL_CENTER
+ || verticalContentAlignment == ALIGNMENT_BOTTOM;
if (validHorizontalAlignment && validVerticalAlignment) {
mAlignment = alignment;
} else {
- System.out.println(TAG + "incorrect alignment "
- + " h: " + horizontalContentAlignment
- + " v: " + verticalContentAlignment);
+ System.out.println(
+ TAG
+ + "incorrect alignment "
+ + " h: "
+ + horizontalContentAlignment
+ + " v: "
+ + verticalContentAlignment);
}
}
switch (sizing) {
- case SIZING_LAYOUT: {
+ case SIZING_LAYOUT:
System.out.println(TAG + "sizing_layout is not yet supported");
- }
- break;
- case SIZING_SCALE: {
+ break;
+ case SIZING_SCALE:
mSizing = sizing;
- }
- break;
- default: {
+ break;
+ default:
System.out.println(TAG + "incorrect sizing value " + sizing);
- }
}
if (mSizing == SIZING_LAYOUT) {
if (mode != NONE) {
@@ -171,9 +165,8 @@ public class RootContentBehavior implements RemoteComposeOperation {
case SCALE_FILL_BOUNDS:
mMode = mode;
break;
- default: {
+ default:
System.out.println(TAG + "incorrect mode for scale sizing, mode: " + mode);
- }
}
}
}
@@ -185,8 +178,12 @@ public class RootContentBehavior implements RemoteComposeOperation {
@Override
public String toString() {
- return "ROOT_CONTENT_BEHAVIOR scroll: " + mScroll
- + " sizing: " + mSizing + " mode: " + mMode;
+ return "ROOT_CONTENT_BEHAVIOR scroll: "
+ + mScroll
+ + " sizing: "
+ + mSizing
+ + " mode: "
+ + mMode;
}
@Override
@@ -199,12 +196,10 @@ public class RootContentBehavior implements RemoteComposeOperation {
return toString();
}
-
public static String name() {
return CLASS_NAME;
}
-
public static int id() {
return OP_CODE;
}
@@ -217,7 +212,6 @@ public class RootContentBehavior implements RemoteComposeOperation {
buffer.writeInt(mode);
}
-
public static void read(WireBuffer buffer, List<Operation> operations) {
int scroll = buffer.readInt();
int alignment = buffer.readInt();
@@ -228,30 +222,27 @@ public class RootContentBehavior implements RemoteComposeOperation {
operations.add(rootContentBehavior);
}
-
public static void documentation(DocumentationBuilder doc) {
- doc.operation("Protocol Operations",
- OP_CODE,
- CLASS_NAME)
+ doc.operation("Protocol Operations", OP_CODE, CLASS_NAME)
.description("Describes the behaviour of the root")
- .field(INT, "scroll", "scroll")
+ .field(DocumentedOperation.INT, "scroll", "scroll")
.possibleValues("SCROLL_HORIZONTAL", SCROLL_HORIZONTAL)
.possibleValues("SCROLL_VERTICAL", SCROLL_VERTICAL)
- .field(INT, "alignment", "alignment")
+ .field(DocumentedOperation.INT, "alignment", "alignment")
.possibleValues("ALIGNMENT_TOP", ALIGNMENT_TOP)
.possibleValues("ALIGNMENT_VERTICAL_CENTER", ALIGNMENT_VERTICAL_CENTER)
.possibleValues("ALIGNMENT_BOTTOM", ALIGNMENT_BOTTOM)
.possibleValues("ALIGNMENT_START", ALIGNMENT_START)
.possibleValues("ALIGNMENT_START", ALIGNMENT_START)
.possibleValues("ALIGNMENT_END", ALIGNMENT_END)
- .field(INT, "sizing", "sizing")
+ .field(DocumentedOperation.INT, "sizing", "sizing")
.possibleValues("SCALE_INSIDE", SCALE_INSIDE)
.possibleValues("SCALE_FIT", SCALE_FIT)
.possibleValues("SCALE_FILL_WIDTH", SCALE_FILL_WIDTH)
.possibleValues("SCALE_FILL_HEIGHT", SCALE_FILL_HEIGHT)
.possibleValues("SCALE_CROP", SCALE_CROP)
.possibleValues("SCALE_FILL_BOUNDS", SCALE_FILL_BOUNDS)
- .field(INT, "mode", "mode")
+ .field(DocumentedOperation.INT, "mode", "mode")
.possibleValues("LAYOUT_HORIZONTAL_MATCH_PARENT", LAYOUT_HORIZONTAL_MATCH_PARENT)
.possibleValues("LAYOUT_HORIZONTAL_WRAP_CONTENT", LAYOUT_HORIZONTAL_WRAP_CONTENT)
.possibleValues("LAYOUT_HORIZONTAL_FIXED", LAYOUT_HORIZONTAL_FIXED)
@@ -260,6 +251,5 @@ public class RootContentBehavior implements RemoteComposeOperation {
.possibleValues("LAYOUT_VERTICAL_FIXED", LAYOUT_VERTICAL_FIXED)
.possibleValues("LAYOUT_MATCH_PARENT", LAYOUT_MATCH_PARENT)
.possibleValues("LAYOUT_WRAP_CONTENT", LAYOUT_WRAP_CONTENT);
-
}
}
diff --git a/core/java/com/android/internal/widget/remotecompose/core/operations/RootContentDescription.java b/core/java/com/android/internal/widget/remotecompose/core/operations/RootContentDescription.java
index e1533eefae53..bff902926fd3 100644
--- a/core/java/com/android/internal/widget/remotecompose/core/operations/RootContentDescription.java
+++ b/core/java/com/android/internal/widget/remotecompose/core/operations/RootContentDescription.java
@@ -15,20 +15,17 @@
*/
package com.android.internal.widget.remotecompose.core.operations;
-import static com.android.internal.widget.remotecompose.core.documentation.Operation.INT;
-
import com.android.internal.widget.remotecompose.core.Operation;
import com.android.internal.widget.remotecompose.core.Operations;
import com.android.internal.widget.remotecompose.core.RemoteComposeOperation;
import com.android.internal.widget.remotecompose.core.RemoteContext;
import com.android.internal.widget.remotecompose.core.WireBuffer;
import com.android.internal.widget.remotecompose.core.documentation.DocumentationBuilder;
+import com.android.internal.widget.remotecompose.core.documentation.DocumentedOperation;
import java.util.List;
-/**
- * Describe a content description for the document
- */
+/** Describe a content description for the document */
public class RootContentDescription implements RemoteComposeOperation {
private static final int OP_CODE = Operations.ROOT_CONTENT_DESCRIPTION;
private static final String CLASS_NAME = "RootContentDescription";
@@ -82,13 +79,9 @@ public class RootContentDescription implements RemoteComposeOperation {
operations.add(header);
}
-
public static void documentation(DocumentationBuilder doc) {
- doc.operation("Protocol Operations",
- OP_CODE,
- CLASS_NAME)
+ doc.operation("Protocol Operations", OP_CODE, CLASS_NAME)
.description("Content description of root")
- .field(INT, "id", "id of Int");
-
+ .field(DocumentedOperation.INT, "id", "id of Int");
}
}
diff --git a/core/java/com/android/internal/widget/remotecompose/core/operations/ShaderData.java b/core/java/com/android/internal/widget/remotecompose/core/operations/ShaderData.java
index c4dde6e0d358..7ec7879bf8b2 100644
--- a/core/java/com/android/internal/widget/remotecompose/core/operations/ShaderData.java
+++ b/core/java/com/android/internal/widget/remotecompose/core/operations/ShaderData.java
@@ -15,12 +15,12 @@
*/
package com.android.internal.widget.remotecompose.core.operations;
-import static com.android.internal.widget.remotecompose.core.documentation.Operation.BYTE;
-import static com.android.internal.widget.remotecompose.core.documentation.Operation.FLOAT_ARRAY;
-import static com.android.internal.widget.remotecompose.core.documentation.Operation.INT;
-import static com.android.internal.widget.remotecompose.core.documentation.Operation.INT_ARRAY;
-import static com.android.internal.widget.remotecompose.core.documentation.Operation.SHORT;
-import static com.android.internal.widget.remotecompose.core.documentation.Operation.UTF8;
+import static com.android.internal.widget.remotecompose.core.documentation.DocumentedOperation.BYTE;
+import static com.android.internal.widget.remotecompose.core.documentation.DocumentedOperation.FLOAT_ARRAY;
+import static com.android.internal.widget.remotecompose.core.documentation.DocumentedOperation.INT;
+import static com.android.internal.widget.remotecompose.core.documentation.DocumentedOperation.INT_ARRAY;
+import static com.android.internal.widget.remotecompose.core.documentation.DocumentedOperation.SHORT;
+import static com.android.internal.widget.remotecompose.core.documentation.DocumentedOperation.UTF8;
import com.android.internal.widget.remotecompose.core.Operation;
import com.android.internal.widget.remotecompose.core.Operations;
@@ -28,15 +28,15 @@ import com.android.internal.widget.remotecompose.core.RemoteContext;
import com.android.internal.widget.remotecompose.core.VariableSupport;
import com.android.internal.widget.remotecompose.core.WireBuffer;
import com.android.internal.widget.remotecompose.core.documentation.DocumentationBuilder;
+import com.android.internal.widget.remotecompose.core.documentation.DocumentedOperation;
import java.util.Arrays;
import java.util.HashMap;
import java.util.List;
/**
- * Operation to deal with bitmap data
- * On getting an Image during a draw call the bitmap is compressed and saved
- * in playback the image is decompressed
+ * Operation to deal with bitmap data On getting an Image during a draw call the bitmap is
+ * compressed and saved in playback the image is decompressed
*/
public class ShaderData implements Operation, VariableSupport {
private static final int OP_CODE = Operations.DATA_SHADER;
@@ -48,11 +48,12 @@ public class ShaderData implements Operation, VariableSupport {
HashMap<String, int[]> mUniformIntMap = null;
HashMap<String, Integer> mUniformBitmapMap = null;
- public ShaderData(int shaderID,
- int shaderTextId,
- HashMap<String, float[]> floatMap,
- HashMap<String, int[]> intMap,
- HashMap<String, Integer> bitmapMap) {
+ public ShaderData(
+ int shaderID,
+ int shaderTextId,
+ HashMap<String, float[]> floatMap,
+ HashMap<String, int[]> intMap,
+ HashMap<String, Integer> bitmapMap) {
mShaderID = shaderID;
mShaderTextId = shaderTextId;
if (floatMap != null) {
@@ -77,7 +78,6 @@ public class ShaderData implements Operation, VariableSupport {
mUniformBitmapMap.put(name, bitmapMap.get(name));
}
}
-
}
public int getShaderTextId() {
@@ -107,7 +107,7 @@ public class ShaderData implements Operation, VariableSupport {
/**
* get the name of all know uniform integers
*
- * @return Name of all integer uniforms
+ * @return Name of all integer uniforms
*/
public String[] getUniformIntegerNames() {
if (mUniformIntMap == null) return new String[0];
@@ -146,8 +146,13 @@ public class ShaderData implements Operation, VariableSupport {
@Override
public void write(WireBuffer buffer) {
- apply(buffer, mShaderID, mShaderTextId,
- mUniformFloatMap, mUniformIntMap, mUniformBitmapMap);
+ apply(
+ buffer,
+ mShaderID,
+ mShaderTextId,
+ mUniformFloatMap,
+ mUniformIntMap,
+ mUniformBitmapMap);
}
@Override
@@ -202,10 +207,13 @@ public class ShaderData implements Operation, VariableSupport {
* @param intMap the map of int uniforms
* @param bitmapMap the map of bitmap uniforms
*/
- public static void apply(WireBuffer buffer, int shaderID, int shaderTextId,
- HashMap<String, float[]> floatMap,
- HashMap<String, int[]> intMap,
- HashMap<String, Integer> bitmapMap) {
+ public static void apply(
+ WireBuffer buffer,
+ int shaderID,
+ int shaderTextId,
+ HashMap<String, float[]> floatMap,
+ HashMap<String, int[]> intMap,
+ HashMap<String, Integer> bitmapMap) {
buffer.start(OP_CODE);
buffer.writeInt(shaderID);
@@ -248,7 +256,6 @@ public class ShaderData implements Operation, VariableSupport {
}
}
-
public static void read(WireBuffer buffer, List<Operation> operations) {
int shaderID = buffer.readInt();
int shaderTextId = buffer.readInt();
@@ -298,16 +305,13 @@ public class ShaderData implements Operation, VariableSupport {
bitmapMap.put(name, val);
}
}
- operations.add(new ShaderData(shaderID, shaderTextId,
- floatMap, intMap, bitmapMap));
+ operations.add(new ShaderData(shaderID, shaderTextId, floatMap, intMap, bitmapMap));
}
public static void documentation(DocumentationBuilder doc) {
- doc.operation("Data Operations",
- OP_CODE,
- CLASS_NAME)
+ doc.operation("Data Operations", OP_CODE, CLASS_NAME)
.description("Shader")
- .field(INT, "shaderID", "id of shader")
+ .field(DocumentedOperation.INT, "shaderID", "id of shader")
.field(BYTE, " floatSize", "number of float uniforms")
.field(BYTE, " intSize", "number of int uniform")
.field(SHORT, " intSize", "number of int uniform")
@@ -319,7 +323,6 @@ public class ShaderData implements Operation, VariableSupport {
.field(INT_ARRAY, "VALUE", "int uniform (max 4)")
.field(UTF8, "bitmapName", "name of bitmap")
.field(INT, "VALUE", "id of bitmap");
-
}
@Override
diff --git a/core/java/com/android/internal/widget/remotecompose/core/operations/TextData.java b/core/java/com/android/internal/widget/remotecompose/core/operations/TextData.java
index b49cb7664495..638324950f88 100644
--- a/core/java/com/android/internal/widget/remotecompose/core/operations/TextData.java
+++ b/core/java/com/android/internal/widget/remotecompose/core/operations/TextData.java
@@ -15,8 +15,7 @@
*/
package com.android.internal.widget.remotecompose.core.operations;
-import static com.android.internal.widget.remotecompose.core.documentation.Operation.INT;
-import static com.android.internal.widget.remotecompose.core.documentation.Operation.UTF8;
+import static com.android.internal.widget.remotecompose.core.documentation.DocumentedOperation.UTF8;
import com.android.internal.widget.remotecompose.core.Operation;
import com.android.internal.widget.remotecompose.core.Operations;
@@ -24,13 +23,12 @@ import com.android.internal.widget.remotecompose.core.RemoteContext;
import com.android.internal.widget.remotecompose.core.SerializableToString;
import com.android.internal.widget.remotecompose.core.WireBuffer;
import com.android.internal.widget.remotecompose.core.documentation.DocumentationBuilder;
+import com.android.internal.widget.remotecompose.core.documentation.DocumentedOperation;
import com.android.internal.widget.remotecompose.core.operations.utilities.StringSerializer;
import java.util.List;
-/**
- * Operation to deal with Text data
- */
+/** Operation to deal with Text data */
public class TextData implements Operation, SerializableToString {
private static final int OP_CODE = Operations.DATA_TEXT;
private static final String CLASS_NAME = "TextData";
@@ -50,15 +48,13 @@ public class TextData implements Operation, SerializableToString {
@Override
public String toString() {
- return "TextData[" + mTextId + "] = \""
- + Utils.trimString(mText, 10) + "\"";
+ return "TextData[" + mTextId + "] = \"" + Utils.trimString(mText, 10) + "\"";
}
public static String name() {
return CLASS_NAME;
}
-
public static int id() {
return OP_CODE;
}
@@ -69,7 +65,6 @@ public class TextData implements Operation, SerializableToString {
buffer.writeUTF8(text);
}
-
public static void read(WireBuffer buffer, List<Operation> operations) {
int textId = buffer.readInt();
@@ -77,18 +72,13 @@ public class TextData implements Operation, SerializableToString {
operations.add(new TextData(textId, text));
}
-
public static void documentation(DocumentationBuilder doc) {
- doc.operation("Data Operations",
- OP_CODE,
- CLASS_NAME)
+ doc.operation("Data Operations", OP_CODE, CLASS_NAME)
.description("Encode a string ")
- .field(INT, "id", "id string")
- .field(UTF8, "text",
- "encode text as a string");
+ .field(DocumentedOperation.INT, "id", "id string")
+ .field(UTF8, "text", "encode text as a string");
}
-
@Override
public void apply(RemoteContext context) {
context.loadText(mTextId, mText);
@@ -101,8 +91,7 @@ public class TextData implements Operation, SerializableToString {
@Override
public void serializeToString(int indent, StringSerializer serializer) {
- serializer.append(indent, getSerializedName() + "<" + mTextId
- + "> = \"" + mText + "\"");
+ serializer.append(indent, getSerializedName() + "<" + mTextId + "> = \"" + mText + "\"");
}
private String getSerializedName() {
diff --git a/core/java/com/android/internal/widget/remotecompose/core/operations/TextFromFloat.java b/core/java/com/android/internal/widget/remotecompose/core/operations/TextFromFloat.java
index 8f235dcca28c..0d966d10384a 100644
--- a/core/java/com/android/internal/widget/remotecompose/core/operations/TextFromFloat.java
+++ b/core/java/com/android/internal/widget/remotecompose/core/operations/TextFromFloat.java
@@ -15,8 +15,8 @@
*/
package com.android.internal.widget.remotecompose.core.operations;
-import static com.android.internal.widget.remotecompose.core.documentation.Operation.INT;
-import static com.android.internal.widget.remotecompose.core.documentation.Operation.SHORT;
+import static com.android.internal.widget.remotecompose.core.documentation.DocumentedOperation.INT;
+import static com.android.internal.widget.remotecompose.core.documentation.DocumentedOperation.SHORT;
import com.android.internal.widget.remotecompose.core.Operation;
import com.android.internal.widget.remotecompose.core.Operations;
@@ -24,14 +24,15 @@ import com.android.internal.widget.remotecompose.core.RemoteContext;
import com.android.internal.widget.remotecompose.core.VariableSupport;
import com.android.internal.widget.remotecompose.core.WireBuffer;
import com.android.internal.widget.remotecompose.core.documentation.DocumentationBuilder;
+import com.android.internal.widget.remotecompose.core.documentation.DocumentedOperation;
import com.android.internal.widget.remotecompose.core.operations.utilities.StringUtils;
import java.util.List;
/**
- * Operation convert floats to text
- * This command is structured [command][textID][before,after][flags]
- * before and after define number of digits before and after the decimal point
+ * Operation convert floats to text This command is structured
+ * [command][textID][before,after][flags] before and after define number of digits before and after
+ * the decimal point
*/
public class TextFromFloat implements Operation, VariableSupport {
private static final int OP_CODE = Operations.TEXT_FROM_FLOAT;
@@ -49,12 +50,12 @@ public class TextFromFloat implements Operation, VariableSupport {
public static final int PAD_AFTER_SPACE = 0; // pad past point with space
public static final int PAD_AFTER_NONE = 1; // do not pad past last digit
public static final int PAD_AFTER_ZERO = 3; // pad with 0 past last digit
- public static final int PAD_PRE_SPACE = 0; // pad before number with spaces
- public static final int PAD_PRE_NONE = 4; // pad before number with 0s
- public static final int PAD_PRE_ZERO = 12; // do not pad before number
+ public static final int PAD_PRE_SPACE = 0; // pad before number with spaces
+ public static final int PAD_PRE_NONE = 4; // pad before number with 0s
+ public static final int PAD_PRE_ZERO = 12; // do not pad before number
- public TextFromFloat(int textId, float value, short digitsBefore,
- short digitsAfter, int flags) {
+ public TextFromFloat(
+ int textId, float value, short digitsBefore, short digitsAfter, int flags) {
this.mTextId = textId;
this.mValue = value;
this.mDigitsAfter = digitsAfter;
@@ -87,17 +88,23 @@ public class TextFromFloat implements Operation, VariableSupport {
@Override
public void write(WireBuffer buffer) {
- apply(buffer, mTextId, mValue, mDigitsAfter, mDigitsBefore, mFlags);
+ apply(buffer, mTextId, mValue, mDigitsBefore, mDigitsAfter, mFlags);
}
@Override
public String toString() {
- return "TextFromFloat[" + mTextId + "] = "
- + Utils.floatToString(mValue) + " " + mDigitsBefore
- + "." + mDigitsAfter + " " + mFlags;
+ return "TextFromFloat["
+ + mTextId
+ + "] = "
+ + Utils.floatToString(mValue)
+ + " "
+ + mDigitsBefore
+ + "."
+ + mDigitsAfter
+ + " "
+ + mFlags;
}
-
@Override
public void updateVariables(RemoteContext context) {
if (Float.isNaN(mValue)) {
@@ -123,22 +130,25 @@ public class TextFromFloat implements Operation, VariableSupport {
/**
* Writes out the operation to the buffer
*
- * @param buffer buffer to write to
- * @param textId the id of the output text
- * @param value the float value to be turned into strings
+ * @param buffer buffer to write to
+ * @param textId the id of the output text
+ * @param value the float value to be turned into strings
* @param digitsBefore the digits before the decimal point
- * @param digitsAfter the digits after the decimal point
- * @param flags flags that control if and how to fill the empty spots
+ * @param digitsAfter the digits after the decimal point
+ * @param flags flags that control if and how to fill the empty spots
*/
- public static void apply(WireBuffer buffer, int textId,
- float value, short digitsBefore,
- short digitsAfter, int flags) {
+ public static void apply(
+ WireBuffer buffer,
+ int textId,
+ float value,
+ short digitsBefore,
+ short digitsAfter,
+ int flags) {
buffer.start(OP_CODE);
buffer.writeInt(textId);
buffer.writeFloat(value);
buffer.writeInt((digitsBefore << 16) | digitsAfter);
buffer.writeInt(flags);
-
}
public static void read(WireBuffer buffer, List<Operation> operations) {
@@ -153,27 +163,19 @@ public class TextFromFloat implements Operation, VariableSupport {
}
public static void documentation(DocumentationBuilder doc) {
- doc.operation("Expressions Operations",
- OP_CODE,
- CLASS_NAME)
+ doc.operation("Expressions Operations", OP_CODE, CLASS_NAME)
.description("Draw text along path object")
- .field(INT, "textId",
- "id of the text generated")
- .field(INT, "value",
- "Value to add")
- .field(SHORT, "prePoint",
- "digits before the decimal point")
- .field(SHORT, "pstPoint",
- "digit after the decimal point")
+ .field(DocumentedOperation.INT, "textId", "id of the text generated")
+ .field(INT, "value", "Value to add")
+ .field(SHORT, "prePoint", "digits before the decimal point")
+ .field(SHORT, "pstPoint", "digit after the decimal point")
.field(INT, "flags", "options on padding");
}
-
@Override
public void apply(RemoteContext context) {
float v = mOutValue;
- String s = StringUtils.floatToString(v, mDigitsBefore,
- mDigitsAfter, mPre, mAfter);
+ String s = StringUtils.floatToString(v, mDigitsBefore, mDigitsAfter, mPre, mAfter);
context.loadText(mTextId, s);
}
diff --git a/core/java/com/android/internal/widget/remotecompose/core/operations/TextLength.java b/core/java/com/android/internal/widget/remotecompose/core/operations/TextLength.java
new file mode 100644
index 000000000000..e148fb9849fc
--- /dev/null
+++ b/core/java/com/android/internal/widget/remotecompose/core/operations/TextLength.java
@@ -0,0 +1,93 @@
+/*
+ * Copyright (C) 2023 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.internal.widget.remotecompose.core.operations;
+
+import static com.android.internal.widget.remotecompose.core.documentation.DocumentedOperation.INT;
+
+import com.android.internal.widget.remotecompose.core.Operation;
+import com.android.internal.widget.remotecompose.core.Operations;
+import com.android.internal.widget.remotecompose.core.RemoteContext;
+import com.android.internal.widget.remotecompose.core.WireBuffer;
+import com.android.internal.widget.remotecompose.core.documentation.DocumentationBuilder;
+
+import java.util.List;
+
+/** Operation to measure the length of the text */
+public class TextLength implements Operation {
+ private static final int OP_CODE = Operations.TEXT_LENGTH;
+ private static final String CLASS_NAME = "TextLength";
+ public int mLengthId;
+ public int mTextId;
+
+ public TextLength(int lengthId, int textId) {
+ this.mLengthId = lengthId;
+ this.mTextId = textId;
+ }
+
+ @Override
+ public void write(WireBuffer buffer) {
+ apply(buffer, mLengthId, mTextId);
+ }
+
+ @Override
+ public String toString() {
+ return CLASS_NAME + "[" + mLengthId + "] = " + mTextId;
+ }
+
+ public static String name() {
+ return CLASS_NAME;
+ }
+
+ public static int id() {
+ return OP_CODE;
+ }
+
+ /**
+ * Writes out the operation to the buffer
+ *
+ * @param buffer write command to this buffer
+ * @param lengthId the id to output
+ * @param textId the id of the text to measure
+ */
+ public static void apply(WireBuffer buffer, int lengthId, int textId) {
+ buffer.start(OP_CODE);
+ buffer.writeInt(lengthId);
+ buffer.writeInt(textId);
+ }
+
+ public static void read(WireBuffer buffer, List<Operation> operations) {
+ int lengthId = buffer.readInt();
+ int textId = buffer.readInt();
+ operations.add(new TextLength(lengthId, textId));
+ }
+
+ public static void documentation(DocumentationBuilder doc) {
+ doc.operation("Expressions Operations", OP_CODE, CLASS_NAME)
+ .description("get the length of the text and store in float table")
+ .field(INT, "id", "id of float length")
+ .field(INT, "value", "index of text");
+ }
+
+ @Override
+ public void apply(RemoteContext context) {
+ context.loadFloat(mLengthId, context.getText(mTextId).length());
+ }
+
+ @Override
+ public String deepToString(String indent) {
+ return indent + toString();
+ }
+}
diff --git a/core/java/com/android/internal/widget/remotecompose/core/operations/TextLookup.java b/core/java/com/android/internal/widget/remotecompose/core/operations/TextLookup.java
new file mode 100644
index 000000000000..b04d698fa36c
--- /dev/null
+++ b/core/java/com/android/internal/widget/remotecompose/core/operations/TextLookup.java
@@ -0,0 +1,127 @@
+/*
+ * 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.
+ */
+package com.android.internal.widget.remotecompose.core.operations;
+
+import static com.android.internal.widget.remotecompose.core.documentation.DocumentedOperation.FLOAT;
+import static com.android.internal.widget.remotecompose.core.documentation.DocumentedOperation.INT;
+
+import com.android.internal.widget.remotecompose.core.Operation;
+import com.android.internal.widget.remotecompose.core.Operations;
+import com.android.internal.widget.remotecompose.core.RemoteContext;
+import com.android.internal.widget.remotecompose.core.VariableSupport;
+import com.android.internal.widget.remotecompose.core.WireBuffer;
+import com.android.internal.widget.remotecompose.core.documentation.DocumentationBuilder;
+
+import java.util.List;
+
+/**
+ * Operation convert floats to text This command is structured
+ * [command][textID][before,after][flags] before and after define number of digits before and after
+ * the decimal point
+ */
+public class TextLookup implements Operation, VariableSupport {
+ private static final int OP_CODE = Operations.TEXT_LOOKUP;
+ private static final String CLASS_NAME = "TextFromFloat";
+ public int mTextId;
+ public int mDataSetId;
+ public float mOutIndex, mIndex;
+
+ public static final int MAX_STRING_SIZE = 4000;
+
+ public TextLookup(int textId, int dataSetId, float index) {
+ this.mTextId = textId;
+ this.mDataSetId = dataSetId;
+ this.mOutIndex = this.mIndex = index;
+ }
+
+ @Override
+ public void write(WireBuffer buffer) {
+ apply(buffer, mTextId, mDataSetId, mIndex);
+ }
+
+ @Override
+ public String toString() {
+ return "TextLookup["
+ + Utils.idString(mTextId)
+ + "] = "
+ + Utils.idString(mDataSetId)
+ + " "
+ + Utils.floatToString(mIndex);
+ }
+
+ @Override
+ public void updateVariables(RemoteContext context) {
+ if (Float.isNaN(mIndex)) {
+ mOutIndex = context.getFloat(Utils.idFromNan(mIndex));
+ }
+ }
+
+ @Override
+ public void registerListening(RemoteContext context) {
+ if (Float.isNaN(mIndex)) {
+ context.listensTo(Utils.idFromNan(mIndex), this);
+ }
+ }
+
+ public static String name() {
+ return CLASS_NAME;
+ }
+
+ public static int id() {
+ return OP_CODE;
+ }
+
+ /**
+ * Writes out the operation to the buffer
+ *
+ * @param buffer buffer to write to
+ * @param textId the id of the output text
+ * @param dataSet float pointer to the array/list to turn int a string
+ * @param index index of element to return
+ */
+ public static void apply(WireBuffer buffer, int textId, int dataSet, float index) {
+ buffer.start(OP_CODE);
+ buffer.writeInt(textId);
+ buffer.writeInt(dataSet);
+ buffer.writeFloat(index);
+ }
+
+ public static void read(WireBuffer buffer, List<Operation> operations) {
+ int textId = buffer.readInt();
+ int dataSetId = buffer.readInt();
+ float index = buffer.readFloat();
+ operations.add(new TextLookup(textId, dataSetId, index));
+ }
+
+ public static void documentation(DocumentationBuilder doc) {
+ doc.operation("Expressions Operations", OP_CODE, CLASS_NAME)
+ .description("Look an array and turn into a text object")
+ .field(INT, "textId", "id of the text generated")
+ .field(FLOAT, "dataSet", "float pointer to the array/list to turn int a string")
+ .field(FLOAT, "index", "index of element to return");
+ }
+
+ @Override
+ public void apply(RemoteContext context) {
+ int id = context.getCollectionsAccess().getId(mDataSetId, (int) mOutIndex);
+ context.loadText(mTextId, context.getText(id));
+ }
+
+ @Override
+ public String deepToString(String indent) {
+ return indent + toString();
+ }
+}
diff --git a/core/java/com/android/internal/widget/remotecompose/core/operations/TextLookupInt.java b/core/java/com/android/internal/widget/remotecompose/core/operations/TextLookupInt.java
new file mode 100644
index 000000000000..171bea249273
--- /dev/null
+++ b/core/java/com/android/internal/widget/remotecompose/core/operations/TextLookupInt.java
@@ -0,0 +1,120 @@
+/*
+ * 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.
+ */
+package com.android.internal.widget.remotecompose.core.operations;
+
+import static com.android.internal.widget.remotecompose.core.documentation.DocumentedOperation.INT;
+
+import com.android.internal.widget.remotecompose.core.Operation;
+import com.android.internal.widget.remotecompose.core.Operations;
+import com.android.internal.widget.remotecompose.core.RemoteContext;
+import com.android.internal.widget.remotecompose.core.VariableSupport;
+import com.android.internal.widget.remotecompose.core.WireBuffer;
+import com.android.internal.widget.remotecompose.core.documentation.DocumentationBuilder;
+import com.android.internal.widget.remotecompose.core.documentation.DocumentedOperation;
+
+import java.util.List;
+
+/** Operation convert int index of a list to text */
+public class TextLookupInt implements Operation, VariableSupport {
+ private static final int OP_CODE = Operations.TEXT_LOOKUP_INT;
+ private static final String CLASS_NAME = "TextFromINT";
+ public int mTextId;
+ public int mDataSetId;
+ public int mOutIndex;
+ public int mIndex;
+
+ public static final int MAX_STRING_SIZE = 4000;
+
+ public TextLookupInt(int textId, int dataSetId, int indexId) {
+ this.mTextId = textId;
+ this.mDataSetId = dataSetId;
+ this.mOutIndex = this.mIndex = indexId;
+ }
+
+ @Override
+ public void write(WireBuffer buffer) {
+ apply(buffer, mTextId, mDataSetId, mIndex);
+ }
+
+ @Override
+ public String toString() {
+ return "TextLookupInt["
+ + Utils.idString(mTextId)
+ + "] = "
+ + Utils.idString(mDataSetId)
+ + " "
+ + mIndex;
+ }
+
+ @Override
+ public void updateVariables(RemoteContext context) {
+ mOutIndex = context.getInteger(mIndex);
+ }
+
+ @Override
+ public void registerListening(RemoteContext context) {
+ context.listensTo(mIndex, this);
+ }
+
+ public static String name() {
+ return CLASS_NAME;
+ }
+
+ public static int id() {
+ return OP_CODE;
+ }
+
+ /**
+ * Writes out the operation to the buffer
+ *
+ * @param buffer buffer to write to
+ * @param textId the id of the output text
+ * @param dataSet float pointer to the array/list to turn int a string
+ * @param indexId index of element to return
+ */
+ public static void apply(WireBuffer buffer, int textId, int dataSet, int indexId) {
+ buffer.start(OP_CODE);
+ buffer.writeInt(textId);
+ buffer.writeInt(dataSet);
+ buffer.writeInt(indexId);
+ }
+
+ public static void read(WireBuffer buffer, List<Operation> operations) {
+ int textId = buffer.readInt();
+ int dataSetId = buffer.readInt();
+ int indexId = buffer.readInt();
+ operations.add(new TextLookupInt(textId, dataSetId, indexId));
+ }
+
+ public static void documentation(DocumentationBuilder doc) {
+ doc.operation("Expressions Operations", OP_CODE, CLASS_NAME)
+ .description("Look up an array and turn into a text object")
+ .field(DocumentedOperation.INT, "textId", "id of the text generated")
+ .field(INT, "dataSetId", "id to the array/list to turn int a string")
+ .field(INT, "index", "index of the element to return");
+ }
+
+ @Override
+ public void apply(RemoteContext context) {
+ int id = context.getCollectionsAccess().getId(mDataSetId, (int) mOutIndex);
+ context.loadText(mTextId, context.getText(id));
+ }
+
+ @Override
+ public String deepToString(String indent) {
+ return indent + toString();
+ }
+}
diff --git a/core/java/com/android/internal/widget/remotecompose/core/operations/TextMeasure.java b/core/java/com/android/internal/widget/remotecompose/core/operations/TextMeasure.java
new file mode 100644
index 000000000000..0281d6980e0f
--- /dev/null
+++ b/core/java/com/android/internal/widget/remotecompose/core/operations/TextMeasure.java
@@ -0,0 +1,140 @@
+/*
+ * 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.
+ */
+package com.android.internal.widget.remotecompose.core.operations;
+
+import static com.android.internal.widget.remotecompose.core.PaintContext.TEXT_MEASURE_FONT_HEIGHT;
+import static com.android.internal.widget.remotecompose.core.PaintContext.TEXT_MEASURE_MONOSPACE_WIDTH;
+import static com.android.internal.widget.remotecompose.core.documentation.DocumentedOperation.INT;
+
+import com.android.internal.widget.remotecompose.core.Operation;
+import com.android.internal.widget.remotecompose.core.Operations;
+import com.android.internal.widget.remotecompose.core.PaintContext;
+import com.android.internal.widget.remotecompose.core.PaintOperation;
+import com.android.internal.widget.remotecompose.core.WireBuffer;
+import com.android.internal.widget.remotecompose.core.documentation.DocumentationBuilder;
+
+import java.util.List;
+
+/** Operation to Measure Text data */
+public class TextMeasure extends PaintOperation {
+ private static final int OP_CODE = Operations.TEXT_MEASURE;
+ private static final String CLASS_NAME = "TextMeasure";
+ public int mId;
+ public int mTextId;
+ public int mType;
+
+ public static final int MEASURE_WIDTH = 0;
+ public static final int MEASURE_HEIGHT = 1;
+ public static final int MEASURE_LEFT = 2;
+ public static final int MEASURE_RIGHT = 3;
+ public static final int MEASURE_TOP = 4;
+ public static final int MEASURE_BOTTOM = 5;
+
+ /** a << 8 shifted {@link PaintContext#getTextBounds} */
+ public static final int MEASURE_MONOSPACE_FLAG = TEXT_MEASURE_MONOSPACE_WIDTH << 8;
+
+ public static final int MEASURE_MAX_HEIGHT_FLAG = TEXT_MEASURE_FONT_HEIGHT << 8;
+
+ public TextMeasure(int id, int textId, int type) {
+ this.mId = id;
+ this.mTextId = textId;
+ this.mType = type;
+ }
+
+ @Override
+ public void write(WireBuffer buffer) {
+ apply(buffer, mId, mTextId, mType);
+ }
+
+ @Override
+ public String toString() {
+ return "FloatConstant[" + mId + "] = " + mTextId + " " + mType;
+ }
+
+ public static String name() {
+ return CLASS_NAME;
+ }
+
+ public static int id() {
+ return OP_CODE;
+ }
+
+ /**
+ * Writes out the operation to the buffer
+ *
+ * @param buffer write command to this buffer
+ * @param id the id
+ * @param textId the id
+ * @param type the value of the float
+ */
+ public static void apply(WireBuffer buffer, int id, int textId, int type) {
+ buffer.start(OP_CODE);
+ buffer.writeInt(id);
+ buffer.writeInt(textId);
+ buffer.writeInt(type);
+ }
+
+ public static void read(WireBuffer buffer, List<Operation> operations) {
+ int id = buffer.readInt();
+ int textId = buffer.readInt();
+ int type = buffer.readInt();
+ operations.add(new TextMeasure(id, textId, type));
+ }
+
+ public static void documentation(DocumentationBuilder doc) {
+ doc.operation("Expressions Operations", OP_CODE, CLASS_NAME)
+ .description("A float and its associated id")
+ .field(INT, "id", "id of float result of the measure")
+ .field(INT, "textId", "id of text")
+ .field(INT, "type", "type: measure 0=width,1=height");
+ }
+
+ @Override
+ public String deepToString(String indent) {
+ return indent + toString();
+ }
+
+ float[] mBounds = new float[4];
+
+ @Override
+ public void paint(PaintContext context) {
+ int val = mType & 255;
+ int flags = mType >> 8;
+ context.getTextBounds(mTextId, 0, -1, flags, mBounds);
+ switch (val) {
+ case MEASURE_WIDTH:
+ context.getContext().loadFloat(mId, mBounds[2] - mBounds[0]);
+ break;
+ case MEASURE_HEIGHT:
+ context.getContext().loadFloat(mId, mBounds[3] - mBounds[1]);
+ break;
+ case MEASURE_LEFT:
+ context.getContext().loadFloat(mId, mBounds[0]);
+ break;
+ case MEASURE_TOP:
+ context.getContext().loadFloat(mId, mBounds[1]);
+ break;
+ case MEASURE_RIGHT:
+ context.getContext().loadFloat(mId, mBounds[2]);
+
+ break;
+ case MEASURE_BOTTOM:
+ context.getContext().loadFloat(mId, mBounds[3]);
+
+ break;
+ }
+ }
+}
diff --git a/core/java/com/android/internal/widget/remotecompose/core/operations/TextMerge.java b/core/java/com/android/internal/widget/remotecompose/core/operations/TextMerge.java
index dd7822308a94..78cc674a22e9 100644
--- a/core/java/com/android/internal/widget/remotecompose/core/operations/TextMerge.java
+++ b/core/java/com/android/internal/widget/remotecompose/core/operations/TextMerge.java
@@ -15,19 +15,18 @@
*/
package com.android.internal.widget.remotecompose.core.operations;
-import static com.android.internal.widget.remotecompose.core.documentation.Operation.INT;
+import static com.android.internal.widget.remotecompose.core.documentation.DocumentedOperation.INT;
import com.android.internal.widget.remotecompose.core.Operation;
import com.android.internal.widget.remotecompose.core.Operations;
import com.android.internal.widget.remotecompose.core.RemoteContext;
import com.android.internal.widget.remotecompose.core.WireBuffer;
import com.android.internal.widget.remotecompose.core.documentation.DocumentationBuilder;
+import com.android.internal.widget.remotecompose.core.documentation.DocumentedOperation;
import java.util.List;
-/**
- * Operation to deal with Text data
- */
+/** Operation to deal with Text data */
public class TextMerge implements Operation {
private static final int OP_CODE = Operations.TEXT_MERGE;
private static final String CLASS_NAME = "TextMerge";
@@ -51,7 +50,6 @@ public class TextMerge implements Operation {
return "TextMerge[" + mTextId + "] = [" + mSrcId1 + " ] + [ " + mSrcId2 + "]";
}
-
public static String name() {
return CLASS_NAME;
}
@@ -84,16 +82,11 @@ public class TextMerge implements Operation {
}
public static void documentation(DocumentationBuilder doc) {
- doc.operation("Data Operations",
- OP_CODE,
- CLASS_NAME)
+ doc.operation("Data Operations", OP_CODE, CLASS_NAME)
.description("Merge two string into one")
- .field(INT, "textId",
- "id of the text")
- .field(INT, "srcTextId1",
- "id of the path")
- .field(INT, "srcTextId1",
- "x Shift of the text");
+ .field(DocumentedOperation.INT, "textId", "id of the text")
+ .field(INT, "srcTextId1", "id of the path")
+ .field(INT, "srcTextId1", "x Shift of the text");
}
@Override
diff --git a/core/java/com/android/internal/widget/remotecompose/core/operations/Theme.java b/core/java/com/android/internal/widget/remotecompose/core/operations/Theme.java
index 52ae7fe5ab4b..845f25d0cd67 100644
--- a/core/java/com/android/internal/widget/remotecompose/core/operations/Theme.java
+++ b/core/java/com/android/internal/widget/remotecompose/core/operations/Theme.java
@@ -15,7 +15,7 @@
*/
package com.android.internal.widget.remotecompose.core.operations;
-import static com.android.internal.widget.remotecompose.core.documentation.Operation.INT;
+import static com.android.internal.widget.remotecompose.core.documentation.DocumentedOperation.INT;
import com.android.internal.widget.remotecompose.core.Operation;
import com.android.internal.widget.remotecompose.core.Operations;
@@ -27,10 +27,9 @@ import com.android.internal.widget.remotecompose.core.documentation.Documentatio
import java.util.List;
/**
- * Set a current theme, applied to the following operations in the document.
- * This can be used to "tag" the subsequent operations to a given theme. On playback,
- * we can then filter operations depending on the chosen theme.
- *
+ * Set a current theme, applied to the following operations in the document. This can be used to
+ * "tag" the subsequent operations to a given theme. On playback, we can then filter operations
+ * depending on the chosen theme.
*/
public class Theme implements RemoteComposeOperation {
private static final int OP_CODE = Operations.THEME;
@@ -43,10 +42,7 @@ public class Theme implements RemoteComposeOperation {
/**
* we can then filter operations depending on the chosen theme.
*
- * @param theme the theme we are interested in:
- * - Theme.UNSPECIFIED
- * - Theme.DARK
- * - Theme.LIGHT
+ * @param theme the theme we are interested in: - Theme.UNSPECIFIED - Theme.DARK - Theme.LIGHT
*/
public Theme(int theme) {
this.mTheme = theme;
@@ -76,7 +72,6 @@ public class Theme implements RemoteComposeOperation {
return CLASS_NAME;
}
-
public static int id() {
return OP_CODE;
}
@@ -86,7 +81,6 @@ public class Theme implements RemoteComposeOperation {
buffer.writeInt(theme);
}
-
public static void read(WireBuffer buffer, List<Operation> operations) {
int theme = buffer.readInt();
operations.add(new Theme(theme));
diff --git a/core/java/com/android/internal/widget/remotecompose/core/operations/Utils.java b/core/java/com/android/internal/widget/remotecompose/core/operations/Utils.java
index 6e858c7e0215..8ebb40cab806 100644
--- a/core/java/com/android/internal/widget/remotecompose/core/operations/Utils.java
+++ b/core/java/com/android/internal/widget/remotecompose/core/operations/Utils.java
@@ -15,9 +15,7 @@
*/
package com.android.internal.widget.remotecompose.core.operations;
-/**
- * Utilities to be used across all core operations
- */
+/** Utilities to be used across all core operations */
public class Utils {
public static float asNan(int v) {
return Float.intBitsToFloat(v | -0x800000);
@@ -28,8 +26,16 @@ public class Utils {
return b & 0x3FFFFF;
}
+ public static long idFromLong(long v) {
+ return v - 0x100000000L;
+ }
+
public static String idStringFromNan(float value) {
int b = Float.floatToRawIntBits(value) & 0x3FFFFF;
+ return idString(b);
+ }
+
+ public static String idString(int b) {
return (b > 0xFFFFF) ? "A_" + (b & 0xFFFFF) : "" + b;
}
@@ -38,8 +44,7 @@ public class Utils {
}
/**
- * trim a string to n characters if needing to trim
- * end in "..."
+ * trim a string to n characters if needing to trim end in "..."
*
* @param str
* @param n
@@ -54,6 +59,7 @@ public class Utils {
/**
* print the id and the value of a float
+ *
* @param idvalue
* @param value
* @return
@@ -70,6 +76,7 @@ public class Utils {
/**
* Convert float to string but render nan id in brackets [n]
+ *
* @param value
* @return
*/
@@ -85,17 +92,25 @@ public class Utils {
/**
* Debugging util to print a message and include the file/line it came from
+ *
* @param str
*/
public static void log(String str) {
StackTraceElement s = new Throwable().getStackTrace()[1];
- System.out.println("(" + s.getFileName()
- + ":" + s.getLineNumber() + "). "
- + s.getMethodName() + "() " + str);
+ System.out.println(
+ "("
+ + s.getFileName()
+ + ":"
+ + s.getLineNumber()
+ + "). "
+ + s.getMethodName()
+ + "() "
+ + str);
}
/**
* Debugging util to print the stack
+ *
* @param str
* @param n
*/
@@ -104,8 +119,8 @@ public class Utils {
for (int i = 1; i < n + 1; i++) {
StackTraceElement s = st[i];
String space = new String(new char[i]).replace('\0', ' ');
- System.out.println(space + "(" + s.getFileName()
- + ":" + s.getLineNumber() + ")." + str);
+ System.out.println(
+ space + "(" + s.getFileName() + ":" + s.getLineNumber() + ")." + str);
}
}
@@ -136,8 +151,8 @@ public class Utils {
}
/**
- * Interpolate two colors.
- * gamma corrected colors are interpolated in the form c1 * (1-t) + c2 * t
+ * Interpolate two colors. gamma corrected colors are interpolated in the form c1 * (1-t) + c2 *
+ * t
*
* @param c1
* @param c2
@@ -183,7 +198,6 @@ public class Utils {
int outb = clamp((int) ((float) Math.pow(f_b, 1.0 / 2.2) * 255.0f));
int outa = clamp((int) (f_a * 255.0f));
-
return (outa << 24 | outr << 16 | outg << 8 | outb);
}
@@ -205,9 +219,9 @@ public class Utils {
/**
* convert hue saturation and value to RGB
*
- * @param hue 0..1
+ * @param hue 0..1
* @param saturation 0..1 0=on the gray scale
- * @param value 0..1 0=black
+ * @param value 0..1 0=black
* @return
*/
public static int hsvToRgb(float hue, float saturation, float value) {
@@ -230,7 +244,6 @@ public class Utils {
return 0XFF000000 | (t << 16) + (p << 8) + v;
case 5:
return 0XFF000000 | (v << 16) + (p << 8) + q;
-
}
return 0;
}
diff --git a/core/java/com/android/internal/widget/remotecompose/core/operations/layout/ActionOperation.java b/core/java/com/android/internal/widget/remotecompose/core/operations/layout/ActionOperation.java
index 7588c794190d..bdc2a886ffd6 100644
--- a/core/java/com/android/internal/widget/remotecompose/core/operations/layout/ActionOperation.java
+++ b/core/java/com/android/internal/widget/remotecompose/core/operations/layout/ActionOperation.java
@@ -20,11 +20,10 @@ import com.android.internal.widget.remotecompose.core.Operation;
import com.android.internal.widget.remotecompose.core.RemoteContext;
import com.android.internal.widget.remotecompose.core.operations.utilities.StringSerializer;
-/**
- * Operations representing actions on the document
- */
+/** Operations representing actions on the document */
public interface ActionOperation extends Operation {
void serializeToString(int indent, StringSerializer serializer);
- void runAction(RemoteContext context, CoreDocument document,
- Component component, float x, float y);
+
+ void runAction(
+ RemoteContext context, CoreDocument document, Component component, float x, float y);
}
diff --git a/core/java/com/android/internal/widget/remotecompose/core/operations/layout/CanvasContent.java b/core/java/com/android/internal/widget/remotecompose/core/operations/layout/CanvasContent.java
index fd3501732e51..9d80d3cc40b0 100644
--- a/core/java/com/android/internal/widget/remotecompose/core/operations/layout/CanvasContent.java
+++ b/core/java/com/android/internal/widget/remotecompose/core/operations/layout/CanvasContent.java
@@ -15,7 +15,7 @@
*/
package com.android.internal.widget.remotecompose.core.operations.layout;
-import static com.android.internal.widget.remotecompose.core.documentation.Operation.INT;
+import static com.android.internal.widget.remotecompose.core.documentation.DocumentedOperation.INT;
import com.android.internal.widget.remotecompose.core.Operation;
import com.android.internal.widget.remotecompose.core.Operations;
@@ -24,13 +24,17 @@ import com.android.internal.widget.remotecompose.core.documentation.Documentatio
import java.util.List;
-/**
- * Represents the content of a CanvasLayout (i.e. contains the canvas commands)
- */
+/** Represents the content of a CanvasLayout (i.e. contains the canvas commands) */
public class CanvasContent extends Component implements ComponentStartOperation {
- public CanvasContent(int componentId, float x, float y,
- float width, float height, Component parent, int animationId) {
+ public CanvasContent(
+ int componentId,
+ float x,
+ float y,
+ float width,
+ float height,
+ Component parent,
+ int animationId) {
super(parent, componentId, animationId, x, y, width, height);
}
@@ -42,7 +46,8 @@ public class CanvasContent extends Component implements ComponentStartOperation
return Operations.LAYOUT_CANVAS_CONTENT;
}
- @Override protected String getSerializedName() {
+ @Override
+ protected String getSerializedName() {
return "CANVAS_CONTENT";
}
@@ -53,8 +58,7 @@ public class CanvasContent extends Component implements ComponentStartOperation
public static void read(WireBuffer buffer, List<Operation> operations) {
int componentId = buffer.readInt();
- operations.add(new CanvasContent(
- componentId, 0, 0, 0, 0, null, -1));
+ operations.add(new CanvasContent(componentId, 0, 0, 0, 0, null, -1));
}
public static void documentation(DocumentationBuilder doc) {
diff --git a/core/java/com/android/internal/widget/remotecompose/core/operations/layout/ClickModifierEnd.java b/core/java/com/android/internal/widget/remotecompose/core/operations/layout/ClickModifierEnd.java
index f7c6ce24df14..fe726ac78791 100644
--- a/core/java/com/android/internal/widget/remotecompose/core/operations/layout/ClickModifierEnd.java
+++ b/core/java/com/android/internal/widget/remotecompose/core/operations/layout/ClickModifierEnd.java
@@ -57,14 +57,14 @@ public class ClickModifierEnd implements Operation {
buffer.start(id());
}
-
public static void read(WireBuffer buffer, List<Operation> operations) {
operations.add(new ClickModifierEnd());
}
public static void documentation(DocumentationBuilder doc) {
doc.operation("Layout Operations", id(), name())
- .description("End tag for click modifiers. This operation marks the end"
- + "of a click modifier");
+ .description(
+ "End tag for click modifiers. This operation marks the end"
+ + "of a click modifier");
}
}
diff --git a/core/java/com/android/internal/widget/remotecompose/core/operations/layout/ClickModifierOperation.java b/core/java/com/android/internal/widget/remotecompose/core/operations/layout/ClickModifierOperation.java
index d75f70b674c5..d5ff07df54cd 100644
--- a/core/java/com/android/internal/widget/remotecompose/core/operations/layout/ClickModifierOperation.java
+++ b/core/java/com/android/internal/widget/remotecompose/core/operations/layout/ClickModifierOperation.java
@@ -35,14 +35,11 @@ import com.android.internal.widget.remotecompose.core.operations.utilities.easin
import java.util.ArrayList;
import java.util.List;
-/**
- * Represents a click modifier + actions
- */
+/** Represents a click modifier + actions */
public class ClickModifierOperation extends PaintOperation
implements ModifierOperation, DecoratorComponent {
private static final int OP_CODE = Operations.MODIFIER_CLICK;
-
long mAnimateRippleStart = 0;
float mAnimateRippleX = 0f;
float mAnimateRippleY = 0f;
@@ -60,6 +57,7 @@ public class ClickModifierOperation extends PaintOperation
mAnimateRippleX = x;
mAnimateRippleY = y;
}
+
public ArrayList<Operation> mList = new ArrayList<>();
public ArrayList<Operation> getList() {
@@ -107,14 +105,14 @@ public class ClickModifierOperation extends PaintOperation
context.savePaint();
mPaint.reset();
- FloatAnimation anim1 = new FloatAnimation(Easing.CUBIC_STANDARD, 1f,
- null, Float.NaN, Float.NaN);
+ FloatAnimation anim1 =
+ new FloatAnimation(Easing.CUBIC_STANDARD, 1f, null, Float.NaN, Float.NaN);
anim1.setInitialValue(0f);
anim1.setTargetValue(1f);
float tween = anim1.get(progress);
- FloatAnimation anim2 = new FloatAnimation(Easing.CUBIC_STANDARD, 0.5f,
- null, Float.NaN, Float.NaN);
+ FloatAnimation anim2 =
+ new FloatAnimation(Easing.CUBIC_STANDARD, 0.5f, null, Float.NaN, Float.NaN);
anim2.setInitialValue(0f);
anim2.setTargetValue(1f);
float tweenRadius = anim2.get(progress);
@@ -149,8 +147,11 @@ public class ClickModifierOperation extends PaintOperation
}
@Override
- public void onClick(RemoteContext context, CoreDocument document,
- Component component, float x, float y) {
+ public void onClick(
+ RemoteContext context, CoreDocument document, Component component, float x, float y) {
+ if (!component.isVisible()) {
+ return;
+ }
locationInWindow[0] = 0f;
locationInWindow[1] = 0f;
component.getLocationInWindow(locationInWindow);
@@ -176,7 +177,8 @@ public class ClickModifierOperation extends PaintOperation
public static void documentation(DocumentationBuilder doc) {
doc.operation("Layout Operations", OP_CODE, name())
- .description("Click modifier. This operation contains"
- + " a list of action executed on click");
+ .description(
+ "Click modifier. This operation contains"
+ + " a list of action executed on click");
}
}
diff --git a/core/java/com/android/internal/widget/remotecompose/core/operations/layout/Component.java b/core/java/com/android/internal/widget/remotecompose/core/operations/layout/Component.java
index fca0b1344a10..96dffca2042f 100644
--- a/core/java/com/android/internal/widget/remotecompose/core/operations/layout/Component.java
+++ b/core/java/com/android/internal/widget/remotecompose/core/operations/layout/Component.java
@@ -38,9 +38,7 @@ import com.android.internal.widget.remotecompose.core.operations.utilities.Strin
import java.util.ArrayList;
import java.util.HashSet;
-/**
- * Generic Component class
- */
+/** Generic Component class */
public class Component extends PaintOperation implements Measurable, SerializableToString {
private static final boolean DEBUG = false;
@@ -64,7 +62,6 @@ public class Component extends PaintOperation implements Measurable, Serializabl
PaintBundle mPaint = new PaintBundle();
protected HashSet<ComponentValue> mComponentValues = new HashSet<>();
-
public ArrayList<Operation> getList() {
return mList;
}
@@ -120,27 +117,27 @@ public class Component extends PaintOperation implements Measurable, Serializabl
*/
private void updateComponentValues(RemoteContext context) {
if (DEBUG) {
- System.out.println("UPDATE COMPONENT VALUES ("
- + mComponentValues.size()
- + ") FOR " + mComponentId);
+ System.out.println(
+ "UPDATE COMPONENT VALUES ("
+ + mComponentValues.size()
+ + ") FOR "
+ + mComponentId);
}
for (ComponentValue v : mComponentValues) {
switch (v.getType()) {
- case ComponentValue.WIDTH: {
+ case ComponentValue.WIDTH:
context.loadFloat(v.getValueId(), mWidth);
if (DEBUG) {
System.out.println("Updating WIDTH for " + mComponentId + " to " + mWidth);
}
- }
- break;
- case ComponentValue.HEIGHT: {
+ break;
+ case ComponentValue.HEIGHT:
context.loadFloat(v.getValueId(), mHeight);
if (DEBUG) {
- System.out.println("Updating HEIGHT for " + mComponentId
- + " to " + mHeight);
+ System.out.println(
+ "Updating HEIGHT for " + mComponentId + " to " + mHeight);
}
- }
- break;
+ break;
}
}
}
@@ -153,8 +150,14 @@ public class Component extends PaintOperation implements Measurable, Serializabl
mAnimationId = id;
}
- public Component(Component parent, int componentId, int animationId,
- float x, float y, float width, float height) {
+ public Component(
+ Component parent,
+ int componentId,
+ int animationId,
+ float x,
+ float y,
+ float width,
+ float height) {
this.mComponentId = componentId;
this.mX = x;
this.mY = y;
@@ -164,15 +167,20 @@ public class Component extends PaintOperation implements Measurable, Serializabl
this.mAnimationId = animationId;
}
- public Component(int componentId, float x, float y, float width, float height,
- Component parent) {
+ public Component(
+ int componentId, float x, float y, float width, float height, Component parent) {
this(parent, componentId, -1, x, y, width, height);
}
public Component(Component component) {
- this(component.mParent, component.mComponentId, component.mAnimationId,
- component.mX, component.mY, component.mWidth, component.mHeight
- );
+ this(
+ component.mParent,
+ component.mComponentId,
+ component.mAnimationId,
+ component.mX,
+ component.mY,
+ component.mWidth,
+ component.mHeight);
mList.addAll(component.mList);
finalizeCreation();
}
@@ -199,8 +207,8 @@ public class Component extends PaintOperation implements Measurable, Serializabl
}
/**
- * This traverses the component tree and make sure to
- * update variables referencing the component dimensions as needed.
+ * This traverses the component tree and make sure to update variables referencing the component
+ * dimensions as needed.
*
* @param context the current context
*/
@@ -223,9 +231,9 @@ public class Component extends PaintOperation implements Measurable, Serializabl
}
public enum Visibility {
+ GONE,
VISIBLE,
- INVISIBLE,
- GONE
+ INVISIBLE
}
public boolean isVisible() {
@@ -239,15 +247,43 @@ public class Component extends PaintOperation implements Measurable, Serializabl
}
public void setVisibility(Visibility visibility) {
- if (visibility != mVisibility) {
+ if (visibility != mVisibility || visibility != mScheduledVisibility) {
mScheduledVisibility = visibility;
invalidateMeasure();
}
}
@Override
- public void measure(PaintContext context, float minWidth, float maxWidth,
- float minHeight, float maxHeight, MeasurePass measure) {
+ public boolean suitableForTransition(Operation o) {
+ if (!(o instanceof Component)) {
+ return false;
+ }
+ if (mList.size() != ((Component) o).mList.size()) {
+ return false;
+ }
+ for (int i = 0; i < mList.size(); i++) {
+ Operation o1 = mList.get(i);
+ Operation o2 = ((Component) o).mList.get(i);
+ if (o1 instanceof Component && o2 instanceof Component) {
+ if (!((Component) o1).suitableForTransition(o2)) {
+ return false;
+ }
+ }
+ if (o1 instanceof PaintOperation && !((PaintOperation) o1).suitableForTransition(o2)) {
+ return false;
+ }
+ }
+ return true;
+ }
+
+ @Override
+ public void measure(
+ PaintContext context,
+ float minWidth,
+ float maxWidth,
+ float minHeight,
+ float maxHeight,
+ MeasurePass measure) {
ComponentMeasure m = measure.get(this);
m.setW(mWidth);
m.setH(mHeight);
@@ -256,19 +292,34 @@ public class Component extends PaintOperation implements Measurable, Serializabl
@Override
public void layout(RemoteContext context, MeasurePass measure) {
ComponentMeasure m = measure.get(this);
- if (!mFirstLayout && context.isAnimationEnabled()
+ if (!mFirstLayout
+ && context.isAnimationEnabled()
&& !(this instanceof LayoutComponentContent)) {
if (mAnimateMeasure == null) {
- ComponentMeasure origin = new ComponentMeasure(mComponentId,
- mX, mY, mWidth, mHeight, mVisibility);
- ComponentMeasure target = new ComponentMeasure(mComponentId,
- m.getX(), m.getY(), m.getW(), m.getH(), m.getVisibility());
- mAnimateMeasure = new AnimateMeasure(context.currentTime, this,
- origin, target,
- mAnimationSpec.getMotionDuration(), mAnimationSpec.getVisibilityDuration(),
- mAnimationSpec.getEnterAnimation(), mAnimationSpec.getExitAnimation(),
- mAnimationSpec.getMotionEasingType(),
- mAnimationSpec.getVisibilityEasingType());
+ ComponentMeasure origin =
+ new ComponentMeasure(mComponentId, mX, mY, mWidth, mHeight, mVisibility);
+ ComponentMeasure target =
+ new ComponentMeasure(
+ mComponentId,
+ m.getX(),
+ m.getY(),
+ m.getW(),
+ m.getH(),
+ m.getVisibility());
+ if (!target.same(origin)) {
+ mAnimateMeasure =
+ new AnimateMeasure(
+ context.currentTime,
+ this,
+ origin,
+ target,
+ mAnimationSpec.getMotionDuration(),
+ mAnimationSpec.getVisibilityDuration(),
+ mAnimationSpec.getEnterAnimation(),
+ mAnimationSpec.getExitAnimation(),
+ mAnimationSpec.getMotionEasingType(),
+ mAnimationSpec.getVisibilityEasingType());
+ }
} else {
mAnimateMeasure.updateTarget(m, context.currentTime);
}
@@ -323,19 +374,48 @@ public class Component extends PaintOperation implements Measurable, Serializabl
@Override
public String toString() {
- return "COMPONENT(<" + mComponentId + "> " + getClass().getSimpleName()
- + ") [" + mX + "," + mY + " - " + mWidth + " x " + mHeight + "] " + textContent()
- + " Visibility (" + mVisibility + ") ";
+ return "COMPONENT(<"
+ + mComponentId
+ + "> "
+ + getClass().getSimpleName()
+ + ") ["
+ + mX
+ + ","
+ + mY
+ + " - "
+ + mWidth
+ + " x "
+ + mHeight
+ + "] "
+ + textContent()
+ + " Visibility ("
+ + mVisibility
+ + ") ";
}
protected String getSerializedName() {
return "COMPONENT";
}
+ @Override
public void serializeToString(int indent, StringSerializer serializer) {
- serializer.append(indent, getSerializedName() + " [" + mComponentId
- + ":" + mAnimationId + "] = "
- + "[" + mX + ", " + mY + ", " + mWidth + ", " + mHeight + "] "
+ serializer.append(
+ indent,
+ getSerializedName()
+ + " ["
+ + mComponentId
+ + ":"
+ + mAnimationId
+ + "] = "
+ + "["
+ + mX
+ + ", "
+ + mY
+ + ", "
+ + mWidth
+ + ", "
+ + mHeight
+ + "] "
+ mVisibility
// + " [" + mNeedsMeasure + ", " + mNeedsRepaint + "]"
);
@@ -346,9 +426,7 @@ public class Component extends PaintOperation implements Measurable, Serializabl
// nothing
}
- /**
- * Returns the top-level RootLayoutComponent
- */
+ /** Returns the top-level RootLayoutComponent */
public RootLayoutComponent getRoot() throws Exception {
if (this instanceof RootLayoutComponent) {
return (RootLayoutComponent) this;
@@ -378,8 +456,8 @@ public class Component extends PaintOperation implements Measurable, Serializabl
}
/**
- * Mark itself as needing to be remeasured, and walk back up the tree
- * to mark each parents as well.
+ * Mark itself as needing to be remeasured, and walk back up the tree to mark each parents as
+ * well.
*/
public void invalidateMeasure() {
needsRepaint();
@@ -411,7 +489,7 @@ public class Component extends PaintOperation implements Measurable, Serializabl
public String textContent() {
StringBuilder builder = new StringBuilder();
- for (Operation op : mList) {
+ for (Operation ignored : mList) {
String letter = "";
// if (op instanceof DrawTextRun) {
// letter = "[" + ((DrawTextRun) op).text + "]";
diff --git a/core/java/com/android/internal/widget/remotecompose/core/operations/layout/ComponentEnd.java b/core/java/com/android/internal/widget/remotecompose/core/operations/layout/ComponentEnd.java
index 71decd74a40b..c83ee487a8ea 100644
--- a/core/java/com/android/internal/widget/remotecompose/core/operations/layout/ComponentEnd.java
+++ b/core/java/com/android/internal/widget/remotecompose/core/operations/layout/ComponentEnd.java
@@ -61,14 +61,14 @@ public class ComponentEnd implements Operation {
return 1 + 4 + 4 + 4;
}
-
public static void read(WireBuffer buffer, List<Operation> operations) {
operations.add(new ComponentEnd());
}
public static void documentation(DocumentationBuilder doc) {
doc.operation("Layout Operations", id(), name())
- .description("End tag for components / layouts. This operation marks the end"
- + "of a component");
+ .description(
+ "End tag for components / layouts. This operation marks the end"
+ + "of a component");
}
}
diff --git a/core/java/com/android/internal/widget/remotecompose/core/operations/layout/ComponentStart.java b/core/java/com/android/internal/widget/remotecompose/core/operations/layout/ComponentStart.java
index 32ef5ce8b278..72cc9b6d2613 100644
--- a/core/java/com/android/internal/widget/remotecompose/core/operations/layout/ComponentStart.java
+++ b/core/java/com/android/internal/widget/remotecompose/core/operations/layout/ComponentStart.java
@@ -15,8 +15,8 @@
*/
package com.android.internal.widget.remotecompose.core.operations.layout;
-import static com.android.internal.widget.remotecompose.core.documentation.Operation.FLOAT;
-import static com.android.internal.widget.remotecompose.core.documentation.Operation.INT;
+import static com.android.internal.widget.remotecompose.core.documentation.DocumentedOperation.FLOAT;
+import static com.android.internal.widget.remotecompose.core.documentation.DocumentedOperation.INT;
import com.android.internal.widget.remotecompose.core.Operation;
import com.android.internal.widget.remotecompose.core.Operations;
@@ -75,8 +75,19 @@ public class ComponentStart implements ComponentStartOperation {
@Override
public String toString() {
- return "COMPONENT_START (type " + mType + " " + typeDescription(mType)
- + ") - (" + mX + ", " + mY + " - " + mWidth + " x " + mHeight + ")";
+ return "COMPONENT_START (type "
+ + mType
+ + " "
+ + typeDescription(mType)
+ + ") - ("
+ + mX
+ + ", "
+ + mY
+ + " - "
+ + mWidth
+ + " x "
+ + mHeight
+ + ")";
}
@Override
@@ -149,8 +160,8 @@ public class ComponentStart implements ComponentStartOperation {
return Operations.COMPONENT_START;
}
- public static void apply(WireBuffer buffer, int type, int componentId,
- float width, float height) {
+ public static void apply(
+ WireBuffer buffer, int type, int componentId, float width, float height) {
buffer.start(Operations.COMPONENT_START);
buffer.writeInt(type);
buffer.writeInt(componentId);
@@ -172,8 +183,8 @@ public class ComponentStart implements ComponentStartOperation {
public static void documentation(DocumentationBuilder doc) {
doc.operation("Layout Operations", id(), name())
- .description("Basic component encapsulating draw commands."
- + "This is not resizable.")
+ .description(
+ "Basic component encapsulating draw commands." + "This is not resizable.")
.field(INT, "TYPE", "Type of components")
.field(INT, "COMPONENT_ID", "unique id for this component")
.field(FLOAT, "WIDTH", "width of the component")
diff --git a/core/java/com/android/internal/widget/remotecompose/core/operations/layout/ComponentStartOperation.java b/core/java/com/android/internal/widget/remotecompose/core/operations/layout/ComponentStartOperation.java
index 67964efe816f..abf2356a3e49 100644
--- a/core/java/com/android/internal/widget/remotecompose/core/operations/layout/ComponentStartOperation.java
+++ b/core/java/com/android/internal/widget/remotecompose/core/operations/layout/ComponentStartOperation.java
@@ -17,5 +17,4 @@ package com.android.internal.widget.remotecompose.core.operations.layout;
import com.android.internal.widget.remotecompose.core.Operation;
-public interface ComponentStartOperation extends Operation {
-}
+public interface ComponentStartOperation extends Operation {}
diff --git a/core/java/com/android/internal/widget/remotecompose/core/operations/layout/DecoratorComponent.java b/core/java/com/android/internal/widget/remotecompose/core/operations/layout/DecoratorComponent.java
index 71bf83913cfb..314650fcd597 100644
--- a/core/java/com/android/internal/widget/remotecompose/core/operations/layout/DecoratorComponent.java
+++ b/core/java/com/android/internal/widget/remotecompose/core/operations/layout/DecoratorComponent.java
@@ -24,6 +24,7 @@ import com.android.internal.widget.remotecompose.core.RemoteContext;
*/
public interface DecoratorComponent {
void layout(RemoteContext context, float width, float height);
- void onClick(RemoteContext context, CoreDocument document,
- Component component, float x, float y);
+
+ void onClick(
+ RemoteContext context, CoreDocument document, Component component, float x, float y);
}
diff --git a/core/java/com/android/internal/widget/remotecompose/core/operations/layout/LayoutComponent.java b/core/java/com/android/internal/widget/remotecompose/core/operations/layout/LayoutComponent.java
index f4c213159882..8172502d092f 100644
--- a/core/java/com/android/internal/widget/remotecompose/core/operations/layout/LayoutComponent.java
+++ b/core/java/com/android/internal/widget/remotecompose/core/operations/layout/LayoutComponent.java
@@ -32,9 +32,7 @@ import com.android.internal.widget.remotecompose.core.operations.layout.modifier
import java.util.ArrayList;
-/**
- * Component with modifiers and children
- */
+/** Component with modifiers and children */
public class LayoutComponent extends Component {
protected WidthModifierOperation mWidthModifier = null;
@@ -54,8 +52,14 @@ public class LayoutComponent extends Component {
protected ComponentModifiers mComponentModifiers = new ComponentModifiers();
protected ArrayList<Component> mChildrenComponents = new ArrayList<>();
- public LayoutComponent(Component parent, int componentId, int animationId,
- float x, float y, float width, float height) {
+ public LayoutComponent(
+ Component parent,
+ int componentId,
+ int animationId,
+ float x,
+ float y,
+ float width,
+ float height) {
super(parent, componentId, animationId, x, y, width, height);
}
@@ -91,7 +95,6 @@ public class LayoutComponent extends Component {
return mPaddingBottom;
}
-
public WidthModifierOperation getWidthModifier() {
return mWidthModifier;
}
@@ -237,10 +240,7 @@ public class LayoutComponent extends Component {
context.restore();
}
-
- /**
- * Traverse the modifiers to compute indicated dimension
- */
+ /** Traverse the modifiers to compute indicated dimension */
public float computeModifierDefinedWidth() {
float s = 0f;
float e = 0f;
@@ -283,9 +283,7 @@ public class LayoutComponent extends Component {
return s + e;
}
- /**
- * Traverse the modifiers to compute indicated dimension
- */
+ /** Traverse the modifiers to compute indicated dimension */
public float computeModifierDefinedHeight() {
float t = 0f;
float b = 0f;
@@ -328,4 +326,7 @@ public class LayoutComponent extends Component {
return t + b;
}
+ public ArrayList<Component> getChildrenComponents() {
+ return mChildrenComponents;
+ }
}
diff --git a/core/java/com/android/internal/widget/remotecompose/core/operations/layout/LayoutComponentContent.java b/core/java/com/android/internal/widget/remotecompose/core/operations/layout/LayoutComponentContent.java
index 5b3b54d3c423..66fd053c4b5e 100644
--- a/core/java/com/android/internal/widget/remotecompose/core/operations/layout/LayoutComponentContent.java
+++ b/core/java/com/android/internal/widget/remotecompose/core/operations/layout/LayoutComponentContent.java
@@ -15,7 +15,7 @@
*/
package com.android.internal.widget.remotecompose.core.operations.layout;
-import static com.android.internal.widget.remotecompose.core.documentation.Operation.INT;
+import static com.android.internal.widget.remotecompose.core.documentation.DocumentedOperation.INT;
import com.android.internal.widget.remotecompose.core.Operation;
import com.android.internal.widget.remotecompose.core.Operations;
@@ -24,13 +24,17 @@ import com.android.internal.widget.remotecompose.core.documentation.Documentatio
import java.util.List;
-/**
- * Represents the content of a LayoutComponent (i.e. the children components)
- */
+/** Represents the content of a LayoutComponent (i.e. the children components) */
public class LayoutComponentContent extends Component implements ComponentStartOperation {
- public LayoutComponentContent(int componentId, float x, float y,
- float width, float height, Component parent, int animationId) {
+ public LayoutComponentContent(
+ int componentId,
+ float x,
+ float y,
+ float width,
+ float height,
+ Component parent,
+ int animationId) {
super(parent, componentId, animationId, x, y, width, height);
}
@@ -42,7 +46,8 @@ public class LayoutComponentContent extends Component implements ComponentStartO
return Operations.LAYOUT_CONTENT;
}
- @Override protected String getSerializedName() {
+ @Override
+ protected String getSerializedName() {
return "CONTENT";
}
@@ -53,16 +58,16 @@ public class LayoutComponentContent extends Component implements ComponentStartO
public static void read(WireBuffer buffer, List<Operation> operations) {
int componentId = buffer.readInt();
- operations.add(new LayoutComponentContent(
- componentId, 0, 0, 0, 0, null, -1));
+ operations.add(new LayoutComponentContent(componentId, 0, 0, 0, 0, null, -1));
}
public static void documentation(DocumentationBuilder doc) {
doc.operation("Layout Operations", id(), name())
.field(INT, "COMPONENT_ID", "unique id for this component")
- .description("Container for components. BoxLayout, RowLayout and ColumnLayout "
- + "expects a LayoutComponentContent as a child, encapsulating the "
- + "components that needs to be laid out.");
+ .description(
+ "Container for components. BoxLayout, RowLayout and ColumnLayout "
+ + "expects a LayoutComponentContent as a child, encapsulating the "
+ + "components that needs to be laid out.");
}
@Override
diff --git a/core/java/com/android/internal/widget/remotecompose/core/operations/layout/LoopEnd.java b/core/java/com/android/internal/widget/remotecompose/core/operations/layout/LoopEnd.java
new file mode 100644
index 000000000000..3086d6aaa777
--- /dev/null
+++ b/core/java/com/android/internal/widget/remotecompose/core/operations/layout/LoopEnd.java
@@ -0,0 +1,67 @@
+/*
+ * 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.
+ */
+package com.android.internal.widget.remotecompose.core.operations.layout;
+
+import com.android.internal.widget.remotecompose.core.Operation;
+import com.android.internal.widget.remotecompose.core.Operations;
+import com.android.internal.widget.remotecompose.core.RemoteContext;
+import com.android.internal.widget.remotecompose.core.WireBuffer;
+import com.android.internal.widget.remotecompose.core.documentation.DocumentationBuilder;
+
+import java.util.List;
+
+public class LoopEnd implements Operation {
+
+ @Override
+ public void write(WireBuffer buffer) {
+ apply(buffer);
+ }
+
+ @Override
+ public String toString() {
+ return "LOOP_END";
+ }
+
+ @Override
+ public void apply(RemoteContext context) {
+ // nothing
+ }
+
+ @Override
+ public String deepToString(String indent) {
+ return (indent != null ? indent : "") + toString();
+ }
+
+ public static String name() {
+ return "LoopEnd";
+ }
+
+ public static int id() {
+ return Operations.LOOP_END;
+ }
+
+ public static void apply(WireBuffer buffer) {
+ buffer.start(id());
+ }
+
+ public static void read(WireBuffer buffer, List<Operation> operations) {
+ operations.add(new LoopEnd());
+ }
+
+ public static void documentation(DocumentationBuilder doc) {
+ doc.operation("Operations", id(), name()).description("End tag for loops");
+ }
+}
diff --git a/core/java/com/android/internal/widget/remotecompose/core/operations/layout/LoopOperation.java b/core/java/com/android/internal/widget/remotecompose/core/operations/layout/LoopOperation.java
new file mode 100644
index 000000000000..691000810c20
--- /dev/null
+++ b/core/java/com/android/internal/widget/remotecompose/core/operations/layout/LoopOperation.java
@@ -0,0 +1,116 @@
+/*
+ * 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.
+ */
+package com.android.internal.widget.remotecompose.core.operations.layout;
+
+import com.android.internal.widget.remotecompose.core.Operation;
+import com.android.internal.widget.remotecompose.core.Operations;
+import com.android.internal.widget.remotecompose.core.PaintContext;
+import com.android.internal.widget.remotecompose.core.PaintOperation;
+import com.android.internal.widget.remotecompose.core.VariableSupport;
+import com.android.internal.widget.remotecompose.core.WireBuffer;
+import com.android.internal.widget.remotecompose.core.documentation.DocumentationBuilder;
+
+import java.util.ArrayList;
+import java.util.List;
+
+/** Represents a loop of operations */
+public class LoopOperation extends PaintOperation {
+ private static final int OP_CODE = Operations.LOOP_START;
+
+ public ArrayList<Operation> mList = new ArrayList<>();
+
+ int mIndexVariableId;
+ float mUntil = 12;
+ float mFrom = 0;
+ float mStep = 1;
+
+ public LoopOperation(int count, int indexId) {
+ mUntil = count;
+ mIndexVariableId = indexId;
+ }
+
+ public LoopOperation(float count, float from, float step, int indexId) {
+ mUntil = count;
+ mFrom = from;
+ mStep = step;
+ mIndexVariableId = indexId;
+ }
+
+ public ArrayList<Operation> getList() {
+ return mList;
+ }
+
+ @Override
+ public void write(WireBuffer buffer) {
+ apply(buffer, mUntil, mFrom, mStep, mIndexVariableId);
+ }
+
+ @Override
+ public String toString() {
+ return "LoopOperation";
+ }
+
+ @Override
+ public String deepToString(String indent) {
+ return (indent != null ? indent : "") + toString();
+ }
+
+ @Override
+ public void paint(PaintContext context) {
+ if (mIndexVariableId == 0) {
+ for (float i = mFrom; i < mUntil; i += mStep) {
+ for (Operation op : mList) {
+ op.apply(context.getContext());
+ }
+ }
+ } else {
+ for (float i = mFrom; i < mUntil; i += mStep) {
+ context.getContext().loadFloat(mIndexVariableId, i);
+ for (Operation op : mList) {
+ if (op instanceof VariableSupport) {
+ ((VariableSupport) op).updateVariables(context.getContext());
+ }
+ op.apply(context.getContext());
+ }
+ }
+ }
+ }
+
+ public static String name() {
+ return "Loop";
+ }
+
+ public static void apply(WireBuffer buffer, float count, float from, float step, int indexId) {
+ buffer.start(OP_CODE);
+ buffer.writeFloat(count);
+ buffer.writeFloat(from);
+ buffer.writeFloat(step);
+ buffer.writeInt(indexId);
+ }
+
+ public static void read(WireBuffer buffer, List<Operation> operations) {
+ float count = buffer.readFloat();
+ float from = buffer.readFloat();
+ float step = buffer.readFloat();
+ int indexId = buffer.readInt();
+ operations.add(new LoopOperation(count, from, step, indexId));
+ }
+
+ public static void documentation(DocumentationBuilder doc) {
+ doc.operation("Operations", OP_CODE, name())
+ .description("Loop. This operation execute" + " a list of action in a loop");
+ }
+}
diff --git a/core/java/com/android/internal/widget/remotecompose/core/operations/layout/RootLayoutComponent.java b/core/java/com/android/internal/widget/remotecompose/core/operations/layout/RootLayoutComponent.java
index bf1a4963d4c4..680bb0b064d1 100644
--- a/core/java/com/android/internal/widget/remotecompose/core/operations/layout/RootLayoutComponent.java
+++ b/core/java/com/android/internal/widget/remotecompose/core/operations/layout/RootLayoutComponent.java
@@ -15,7 +15,7 @@
*/
package com.android.internal.widget.remotecompose.core.operations.layout;
-import static com.android.internal.widget.remotecompose.core.documentation.Operation.INT;
+import static com.android.internal.widget.remotecompose.core.documentation.DocumentedOperation.INT;
import com.android.internal.widget.remotecompose.core.Operation;
import com.android.internal.widget.remotecompose.core.Operations;
@@ -32,38 +32,66 @@ import com.android.internal.widget.remotecompose.core.operations.utilities.Strin
import java.util.List;
-/**
- * Represents the root layout component. Entry point to the component tree layout/paint.
- */
+/** Represents the root layout component. Entry point to the component tree layout/paint. */
public class RootLayoutComponent extends Component implements ComponentStartOperation {
int mCurrentId = -1;
- public RootLayoutComponent(int componentId, float x, float y,
- float width, float height, Component parent, int animationId) {
+ public RootLayoutComponent(
+ int componentId,
+ float x,
+ float y,
+ float width,
+ float height,
+ Component parent,
+ int animationId) {
super(parent, componentId, animationId, x, y, width, height);
}
- public RootLayoutComponent(int componentId, float x, float y,
- float width, float height, Component parent) {
+ public RootLayoutComponent(
+ int componentId, float x, float y, float width, float height, Component parent) {
super(parent, componentId, -1, x, y, width, height);
}
@Override
public String toString() {
- return "ROOT " + mComponentId + " (" + mX + ", " + mY + " - "
- + mWidth + " x " + mHeight + ") " + mVisibility;
+ return "ROOT "
+ + mComponentId
+ + " ("
+ + mX
+ + ", "
+ + mY
+ + " - "
+ + mWidth
+ + " x "
+ + mHeight
+ + ") "
+ + mVisibility;
}
@Override
public void serializeToString(int indent, StringSerializer serializer) {
- serializer.append(indent, "ROOT [" + mComponentId + ":" + mAnimationId
- + "] = [" + mX + ", " + mY + ", " + mWidth + ", " + mHeight + "] " + mVisibility);
+ serializer.append(
+ indent,
+ "ROOT ["
+ + mComponentId
+ + ":"
+ + mAnimationId
+ + "] = ["
+ + mX
+ + ", "
+ + mY
+ + ", "
+ + mWidth
+ + ", "
+ + mHeight
+ + "] "
+ + mVisibility);
}
/**
- * Traverse the hierarchy and assign generated ids to component without ids.
- * Most components would already have ids assigned during the document creation, but this
- * allow us to take care of any components added during the inflation.
+ * Traverse the hierarchy and assign generated ids to component without ids. Most components
+ * would already have ids assigned during the document creation, but this allow us to take care
+ * of any components added during the inflation.
*
* @param lastId the last known generated id
*/
@@ -84,9 +112,7 @@ public class RootLayoutComponent extends Component implements ComponentStartOper
}
}
- /**
- * This will measure then layout the tree of components
- */
+ /** This will measure then layout the tree of components */
public void layout(RemoteContext context) {
if (!mNeedsMeasure) {
return;
@@ -100,8 +126,7 @@ public class RootLayoutComponent extends Component implements ComponentStartOper
for (Operation op : mList) {
if (op instanceof Measurable) {
Measurable m = (Measurable) op;
- m.measure(context.getPaintContext(),
- 0f, mWidth, 0f, mHeight, measurePass);
+ m.measure(context.getPaintContext(), 0f, mWidth, 0f, mHeight, measurePass);
m.layout(context, measurePass);
}
}
@@ -161,15 +186,16 @@ public class RootLayoutComponent extends Component implements ComponentStartOper
public static void read(WireBuffer buffer, List<Operation> operations) {
int componentId = buffer.readInt();
- operations.add(new RootLayoutComponent(
- componentId, 0, 0, 0, 0, null, -1));
+ operations.add(new RootLayoutComponent(componentId, 0, 0, 0, 0, null, -1));
}
public static void documentation(DocumentationBuilder doc) {
doc.operation("Layout Operations", id(), name())
.field(INT, "COMPONENT_ID", "unique id for this component")
- .description("Root element for a document. Other components / layout managers "
- + "are children in the component tree starting from this Root component.");
+ .description(
+ "Root element for a document. Other components / layout managers are"
+ + " children in the component tree starting from"
+ + "this Root component.");
}
@Override
diff --git a/core/java/com/android/internal/widget/remotecompose/core/operations/layout/animation/AnimateMeasure.java b/core/java/com/android/internal/widget/remotecompose/core/operations/layout/animation/AnimateMeasure.java
index 1ada733e2cb2..e45058528859 100644
--- a/core/java/com/android/internal/widget/remotecompose/core/operations/layout/animation/AnimateMeasure.java
+++ b/core/java/com/android/internal/widget/remotecompose/core/operations/layout/animation/AnimateMeasure.java
@@ -28,7 +28,7 @@ import com.android.internal.widget.remotecompose.core.operations.utilities.easin
/**
* Basic interpolation manager between two ComponentMeasures
*
- * Handles position, size and visibility
+ * <p>Handles position, size and visibility
*/
public class AnimateMeasure {
long mStartTime = System.currentTimeMillis();
@@ -44,18 +44,24 @@ public class AnimateMeasure {
float mP = 0f;
float mVp = 0f;
- FloatAnimation mMotionEasing = new FloatAnimation(mMotionEasingType,
- mDuration / 1000f, null, 0f, Float.NaN);
- FloatAnimation mVisibilityEasing = new FloatAnimation(mVisibilityEasingType,
- mDurationVisibilityChange / 1000f,
- null, 0f, Float.NaN);
+ FloatAnimation mMotionEasing =
+ new FloatAnimation(mMotionEasingType, mDuration / 1000f, null, 0f, Float.NaN);
+ FloatAnimation mVisibilityEasing =
+ new FloatAnimation(
+ mVisibilityEasingType, mDurationVisibilityChange / 1000f, null, 0f, Float.NaN);
ParticleAnimation mParticleAnimation;
- public AnimateMeasure(long startTime, Component component, ComponentMeasure original,
- ComponentMeasure target, int duration, int durationVisibilityChange,
- AnimationSpec.ANIMATION enterAnimation,
- AnimationSpec.ANIMATION exitAnimation,
- int motionEasingType, int visibilityEasingType) {
+ public AnimateMeasure(
+ long startTime,
+ Component component,
+ ComponentMeasure original,
+ ComponentMeasure target,
+ int duration,
+ int durationVisibilityChange,
+ AnimationSpec.ANIMATION enterAnimation,
+ AnimationSpec.ANIMATION exitAnimation,
+ int motionEasingType,
+ int visibilityEasingType) {
this.mStartTime = startTime;
this.mComponent = component;
this.mOriginal = original;
@@ -64,18 +70,28 @@ public class AnimateMeasure {
this.mDurationVisibilityChange = durationVisibilityChange;
this.mEnterAnimation = enterAnimation;
this.mExitAnimation = exitAnimation;
+ this.mMotionEasingType = motionEasingType;
+ this.mVisibilityEasingType = visibilityEasingType;
+
+ float motionDuration = mDuration / 1000f;
+ float visibilityDuration = mDurationVisibilityChange / 1000f;
+
+ mMotionEasing = new FloatAnimation(mMotionEasingType, motionDuration, null, 0f, Float.NaN);
+ mVisibilityEasing =
+ new FloatAnimation(mVisibilityEasingType, visibilityDuration, null, 0f, Float.NaN);
mMotionEasing.setTargetValue(1f);
mVisibilityEasing.setTargetValue(1f);
+
component.mVisibility = target.getVisibility();
}
public void update(long currentTime) {
long elapsed = currentTime - mStartTime;
- mP = Math.min(elapsed / (float) mDuration, 1f);
- //mP = motionEasing.get(mP);
- mVp = Math.min(elapsed / (float) mDurationVisibilityChange, 1f);
- // mVp = mVisibilityEasing.get(mVp);
+ float motionProgress = elapsed / (float) mDuration;
+ float visibilityProgress = elapsed / (float) mDurationVisibilityChange;
+ mP = mMotionEasing.get(motionProgress);
+ mVp = mVisibilityEasing.get(visibilityProgress);
}
public PaintBundle paint = new PaintBundle();
@@ -117,8 +133,11 @@ public class AnimateMeasure {
paint.reset();
paint.setColor(0f, 0f, 0f, 1f - mVp);
context.applyPaint(paint);
- context.saveLayer(mComponent.getX(), mComponent.getY(),
- mComponent.getWidth(), mComponent.getHeight());
+ context.saveLayer(
+ mComponent.getX(),
+ mComponent.getY(),
+ mComponent.getWidth(),
+ mComponent.getHeight());
mComponent.paintingComponent(context);
context.restore();
context.restorePaint();
@@ -127,8 +146,11 @@ public class AnimateMeasure {
case SLIDE_LEFT:
context.save();
context.translate(-mVp * mComponent.getParent().getWidth(), 0f);
- context.saveLayer(mComponent.getX(), mComponent.getY(),
- mComponent.getWidth(), mComponent.getHeight());
+ context.saveLayer(
+ mComponent.getX(),
+ mComponent.getY(),
+ mComponent.getWidth(),
+ mComponent.getHeight());
mComponent.paintingComponent(context);
context.restore();
context.restore();
@@ -140,8 +162,11 @@ public class AnimateMeasure {
paint.setColor(0f, 0f, 0f, 1f);
context.applyPaint(paint);
context.translate(mVp * mComponent.getParent().getWidth(), 0f);
- context.saveLayer(mComponent.getX(), mComponent.getY(),
- mComponent.getWidth(), mComponent.getHeight());
+ context.saveLayer(
+ mComponent.getX(),
+ mComponent.getY(),
+ mComponent.getWidth(),
+ mComponent.getHeight());
mComponent.paintingComponent(context);
context.restore();
context.restorePaint();
@@ -149,20 +174,24 @@ public class AnimateMeasure {
break;
case SLIDE_TOP:
context.save();
- context.translate(0f,
- -mVp * mComponent.getParent().getHeight());
- context.saveLayer(mComponent.getX(), mComponent.getY(),
- mComponent.getWidth(), mComponent.getHeight());
+ context.translate(0f, -mVp * mComponent.getParent().getHeight());
+ context.saveLayer(
+ mComponent.getX(),
+ mComponent.getY(),
+ mComponent.getWidth(),
+ mComponent.getHeight());
mComponent.paintingComponent(context);
context.restore();
context.restore();
break;
case SLIDE_BOTTOM:
context.save();
- context.translate(0f,
- mVp * mComponent.getParent().getHeight());
- context.saveLayer(mComponent.getX(), mComponent.getY(),
- mComponent.getWidth(), mComponent.getHeight());
+ context.translate(0f, mVp * mComponent.getParent().getHeight());
+ context.saveLayer(
+ mComponent.getX(),
+ mComponent.getY(),
+ mComponent.getWidth(),
+ mComponent.getHeight());
mComponent.paintingComponent(context);
context.restore();
context.restore();
@@ -189,8 +218,11 @@ public class AnimateMeasure {
paint.reset();
paint.setColor(0f, 0f, 0f, mVp);
context.applyPaint(paint);
- context.saveLayer(mComponent.getX(), mComponent.getY(),
- mComponent.getWidth(), mComponent.getHeight());
+ context.saveLayer(
+ mComponent.getX(),
+ mComponent.getY(),
+ mComponent.getWidth(),
+ mComponent.getHeight());
mComponent.paintingComponent(context);
context.restore();
context.restorePaint();
@@ -202,8 +234,11 @@ public class AnimateMeasure {
paint.reset();
paint.setColor(0f, 0f, 0f, mVp);
context.applyPaint(paint);
- context.saveLayer(mComponent.getX(), mComponent.getY(),
- mComponent.getWidth(), mComponent.getHeight());
+ context.saveLayer(
+ mComponent.getX(),
+ mComponent.getY(),
+ mComponent.getWidth(),
+ mComponent.getHeight());
mComponent.paintingComponent(context);
context.restore();
context.restorePaint();
@@ -211,40 +246,48 @@ public class AnimateMeasure {
break;
case SLIDE_LEFT:
context.save();
- context.translate(
- (1f - mVp) * mComponent.getParent().getWidth(), 0f);
- context.saveLayer(mComponent.getX(), mComponent.getY(),
- mComponent.getWidth(), mComponent.getHeight());
+ context.translate((1f - mVp) * mComponent.getParent().getWidth(), 0f);
+ context.saveLayer(
+ mComponent.getX(),
+ mComponent.getY(),
+ mComponent.getWidth(),
+ mComponent.getHeight());
mComponent.paintingComponent(context);
context.restore();
context.restore();
break;
case SLIDE_RIGHT:
context.save();
- context.translate(
- -(1f - mVp) * mComponent.getParent().getWidth(), 0f);
- context.saveLayer(mComponent.getX(), mComponent.getY(),
- mComponent.getWidth(), mComponent.getHeight());
+ context.translate(-(1f - mVp) * mComponent.getParent().getWidth(), 0f);
+ context.saveLayer(
+ mComponent.getX(),
+ mComponent.getY(),
+ mComponent.getWidth(),
+ mComponent.getHeight());
mComponent.paintingComponent(context);
context.restore();
context.restore();
break;
case SLIDE_TOP:
context.save();
- context.translate(0f,
- (1f - mVp) * mComponent.getParent().getHeight());
- context.saveLayer(mComponent.getX(), mComponent.getY(),
- mComponent.getWidth(), mComponent.getHeight());
+ context.translate(0f, (1f - mVp) * mComponent.getParent().getHeight());
+ context.saveLayer(
+ mComponent.getX(),
+ mComponent.getY(),
+ mComponent.getWidth(),
+ mComponent.getHeight());
mComponent.paintingComponent(context);
context.restore();
context.restore();
break;
case SLIDE_BOTTOM:
context.save();
- context.translate(0f,
- -(1f - mVp) * mComponent.getParent().getHeight());
- context.saveLayer(mComponent.getX(), mComponent.getY(),
- mComponent.getWidth(), mComponent.getHeight());
+ context.translate(0f, -(1f - mVp) * mComponent.getParent().getHeight());
+ context.saveLayer(
+ mComponent.getX(),
+ mComponent.getY(),
+ mComponent.getWidth(),
+ mComponent.getHeight());
mComponent.paintingComponent(context);
context.restore();
context.restore();
@@ -300,11 +343,22 @@ public class AnimateMeasure {
mOriginal.setY(getY());
mOriginal.setW(getWidth());
mOriginal.setH(getHeight());
- mTarget.setX(measure.getX());
- mTarget.setY(measure.getY());
- mTarget.setW(measure.getW());
- mTarget.setH(measure.getH());
- mTarget.setVisibility(measure.getVisibility());
- mStartTime = currentTime;
+ float targetX = mTarget.getX();
+ float targetY = mTarget.getY();
+ float targetW = mTarget.getW();
+ float targetH = mTarget.getH();
+ Component.Visibility targetVisibility = mTarget.getVisibility();
+ if (targetX != measure.getX()
+ || targetY != measure.getY()
+ || targetW != measure.getW()
+ || targetH != measure.getH()
+ || targetVisibility != measure.getVisibility()) {
+ mTarget.setX(measure.getX());
+ mTarget.setY(measure.getY());
+ mTarget.setW(measure.getW());
+ mTarget.setH(measure.getH());
+ mTarget.setVisibility(measure.getVisibility());
+ mStartTime = currentTime;
+ }
}
}
diff --git a/core/java/com/android/internal/widget/remotecompose/core/operations/layout/animation/AnimationSpec.java b/core/java/com/android/internal/widget/remotecompose/core/operations/layout/animation/AnimationSpec.java
index 0f7db36e8e01..35533cb95190 100644
--- a/core/java/com/android/internal/widget/remotecompose/core/operations/layout/animation/AnimationSpec.java
+++ b/core/java/com/android/internal/widget/remotecompose/core/operations/layout/animation/AnimationSpec.java
@@ -15,7 +15,7 @@
*/
package com.android.internal.widget.remotecompose.core.operations.layout.animation;
-import static com.android.internal.widget.remotecompose.core.documentation.Operation.INT;
+import static com.android.internal.widget.remotecompose.core.documentation.DocumentedOperation.INT;
import com.android.internal.widget.remotecompose.core.Operation;
import com.android.internal.widget.remotecompose.core.Operations;
@@ -26,9 +26,7 @@ import com.android.internal.widget.remotecompose.core.operations.utilities.easin
import java.util.List;
-/**
- * Basic component animation spec
- */
+/** Basic component animation spec */
public class AnimationSpec implements Operation {
int mAnimationId = -1;
int mMotionDuration = 300;
@@ -38,9 +36,14 @@ public class AnimationSpec implements Operation {
ANIMATION mEnterAnimation = ANIMATION.FADE_IN;
ANIMATION mExitAnimation = ANIMATION.FADE_OUT;
- public AnimationSpec(int animationId, int motionDuration, int motionEasingType,
- int visibilityDuration, int visibilityEasingType,
- ANIMATION enterAnimation, ANIMATION exitAnimation) {
+ public AnimationSpec(
+ int animationId,
+ int motionDuration,
+ int motionEasingType,
+ int visibilityDuration,
+ int visibilityEasingType,
+ ANIMATION enterAnimation,
+ ANIMATION exitAnimation) {
this.mAnimationId = animationId;
this.mMotionDuration = motionDuration;
this.mMotionEasingType = motionEasingType;
@@ -51,9 +54,14 @@ public class AnimationSpec implements Operation {
}
public AnimationSpec() {
- this(-1, 300, GeneralEasing.CUBIC_STANDARD,
- 300, GeneralEasing.CUBIC_STANDARD,
- ANIMATION.FADE_IN, ANIMATION.FADE_OUT);
+ this(
+ -1,
+ 600,
+ GeneralEasing.CUBIC_STANDARD,
+ 500,
+ GeneralEasing.CUBIC_STANDARD,
+ ANIMATION.FADE_IN,
+ ANIMATION.FADE_OUT);
}
public int getAnimationId() {
@@ -102,8 +110,15 @@ public class AnimationSpec implements Operation {
@Override
public void write(WireBuffer buffer) {
- apply(buffer, mAnimationId, mMotionDuration, mMotionEasingType,
- mVisibilityDuration, mVisibilityEasingType, mEnterAnimation, mExitAnimation);
+ apply(
+ buffer,
+ mAnimationId,
+ mMotionDuration,
+ mMotionEasingType,
+ mVisibilityDuration,
+ mVisibilityEasingType,
+ mEnterAnimation,
+ mExitAnimation);
}
@Override
@@ -151,10 +166,15 @@ public class AnimationSpec implements Operation {
}
}
- public static void apply(WireBuffer buffer, int animationId, int motionDuration,
- int motionEasingType, int visibilityDuration,
- int visibilityEasingType, ANIMATION enterAnimation,
- ANIMATION exitAnimation) {
+ public static void apply(
+ WireBuffer buffer,
+ int animationId,
+ int motionDuration,
+ int motionEasingType,
+ int visibilityDuration,
+ int visibilityEasingType,
+ ANIMATION enterAnimation,
+ ANIMATION exitAnimation) {
buffer.start(Operations.ANIMATION_SPEC);
buffer.writeInt(animationId);
buffer.writeInt(motionDuration);
@@ -173,20 +193,25 @@ public class AnimationSpec implements Operation {
int visibilityEasingType = buffer.readInt();
ANIMATION enterAnimation = intToAnimation(buffer.readInt());
ANIMATION exitAnimation = intToAnimation(buffer.readInt());
- AnimationSpec op = new AnimationSpec(animationId, motionDuration, motionEasingType,
- visibilityDuration, visibilityEasingType, enterAnimation, exitAnimation);
+ AnimationSpec op =
+ new AnimationSpec(
+ animationId,
+ motionDuration,
+ motionEasingType,
+ visibilityDuration,
+ visibilityEasingType,
+ enterAnimation,
+ exitAnimation);
operations.add(op);
}
+
public static void documentation(DocumentationBuilder doc) {
- doc.operation("Layout Operations",
- id(),
- name())
+ doc.operation("Layout Operations", id(), name())
.description("define the animation")
.field(INT, "animationId", "")
.field(INT, "motionDuration", "")
.field(INT, "motionEasingType", "")
.field(INT, "visibilityDuration", "")
.field(INT, "visibilityEasingType", "");
-
}
}
diff --git a/core/java/com/android/internal/widget/remotecompose/core/operations/layout/animation/ParticleAnimation.java b/core/java/com/android/internal/widget/remotecompose/core/operations/layout/animation/ParticleAnimation.java
index 5c5d05658f65..686643fbe1bc 100644
--- a/core/java/com/android/internal/widget/remotecompose/core/operations/layout/animation/ParticleAnimation.java
+++ b/core/java/com/android/internal/widget/remotecompose/core/operations/layout/animation/ParticleAnimation.java
@@ -27,9 +27,13 @@ public class ParticleAnimation {
HashMap<Integer, ArrayList<Particle>> mAllParticles = new HashMap<>();
PaintBundle mPaint = new PaintBundle();
- public void animate(PaintContext context, Component component,
- ComponentMeasure start, ComponentMeasure end,
- float progress) {
+
+ public void animate(
+ PaintContext context,
+ Component component,
+ ComponentMeasure start,
+ ComponentMeasure end,
+ float progress) {
ArrayList<Particle> particles = mAllParticles.get(component.getComponentId());
if (particles == null) {
particles = new ArrayList<Particle>();
@@ -37,9 +41,9 @@ public class ParticleAnimation {
float x = (float) Math.random();
float y = (float) Math.random();
float radius = (float) Math.random();
- float r = 250f;
- float g = 250f;
- float b = 250f;
+ float r = 220f;
+ float g = 220f;
+ float b = 220f;
particles.add(new Particle(x, y, radius, r, g, b));
}
mAllParticles.put(component.getComponentId(), particles);
@@ -49,12 +53,17 @@ public class ParticleAnimation {
for (int i = 0; i < particles.size(); i++) {
Particle particle = particles.get(i);
mPaint.reset();
- mPaint.setColor(particle.r, particle.g, particle.b,
- 200 * (1 - progress));
+ mPaint.setColor(
+ particle.r / 255f,
+ particle.g / 255f,
+ particle.b / 255f,
+ (200 * (1 - progress)) / 255f);
context.applyPaint(mPaint);
float dx = start.getX() + component.getWidth() * particle.x;
- float dy = start.getY() + component.getHeight() * particle.y
- + progress * 0.01f * component.getHeight();
+ float dy =
+ start.getY()
+ + component.getHeight() * particle.y
+ + progress * 0.01f * component.getHeight();
float dr = (component.getHeight() + 60) * 0.15f * particle.radius + (30 * progress);
context.drawCircle(dx, dy, dr);
}
diff --git a/core/java/com/android/internal/widget/remotecompose/core/operations/layout/managers/BoxLayout.java b/core/java/com/android/internal/widget/remotecompose/core/operations/layout/managers/BoxLayout.java
index 88a49a666cd7..047a968785c4 100644
--- a/core/java/com/android/internal/widget/remotecompose/core/operations/layout/managers/BoxLayout.java
+++ b/core/java/com/android/internal/widget/remotecompose/core/operations/layout/managers/BoxLayout.java
@@ -15,7 +15,7 @@
*/
package com.android.internal.widget.remotecompose.core.operations.layout.managers;
-import static com.android.internal.widget.remotecompose.core.documentation.Operation.INT;
+import static com.android.internal.widget.remotecompose.core.documentation.DocumentedOperation.INT;
import com.android.internal.widget.remotecompose.core.Operation;
import com.android.internal.widget.remotecompose.core.Operations;
@@ -30,9 +30,7 @@ import com.android.internal.widget.remotecompose.core.operations.layout.measure.
import java.util.List;
-/**
- * Simple Box layout implementation
- */
+/** Simple Box layout implementation */
public class BoxLayout extends LayoutManager implements ComponentStartOperation {
public static final int START = 1;
@@ -41,37 +39,68 @@ public class BoxLayout extends LayoutManager implements ComponentStartOperation
public static final int TOP = 4;
public static final int BOTTOM = 5;
-
int mHorizontalPositioning;
int mVerticalPositioning;
- public BoxLayout(Component parent, int componentId, int animationId,
- float x, float y, float width, float height,
- int horizontalPositioning, int verticalPositioning) {
+ public BoxLayout(
+ Component parent,
+ int componentId,
+ int animationId,
+ float x,
+ float y,
+ float width,
+ float height,
+ int horizontalPositioning,
+ int verticalPositioning) {
super(parent, componentId, animationId, x, y, width, height);
mHorizontalPositioning = horizontalPositioning;
mVerticalPositioning = verticalPositioning;
}
- public BoxLayout(Component parent, int componentId, int animationId,
- int horizontalPositioning, int verticalPositioning) {
- this(parent, componentId, animationId, 0, 0, 0, 0,
- horizontalPositioning, verticalPositioning);
+ public BoxLayout(
+ Component parent,
+ int componentId,
+ int animationId,
+ int horizontalPositioning,
+ int verticalPositioning) {
+ this(
+ parent,
+ componentId,
+ animationId,
+ 0,
+ 0,
+ 0,
+ 0,
+ horizontalPositioning,
+ verticalPositioning);
}
@Override
public String toString() {
- return "BOX [" + mComponentId + ":" + mAnimationId + "] (" + mX + ", "
- + mY + " - " + mWidth + " x " + mHeight + ") " + mVisibility;
+ return "BOX ["
+ + mComponentId
+ + ":"
+ + mAnimationId
+ + "] ("
+ + mX
+ + ", "
+ + mY
+ + " - "
+ + mWidth
+ + " x "
+ + mHeight
+ + ") "
+ + mVisibility;
}
+ @Override
protected String getSerializedName() {
return "BOX";
}
@Override
- public void computeWrapSize(PaintContext context, float maxWidth, float maxHeight,
- MeasurePass measure, Size size) {
+ public void computeWrapSize(
+ PaintContext context, float maxWidth, float maxHeight, MeasurePass measure, Size size) {
for (Component c : mChildrenComponents) {
c.measure(context, 0f, maxWidth, 0f, maxHeight, measure);
ComponentMeasure m = measure.get(c);
@@ -84,16 +113,20 @@ public class BoxLayout extends LayoutManager implements ComponentStartOperation
}
@Override
- public void computeSize(PaintContext context, float minWidth, float maxWidth,
- float minHeight, float maxHeight, MeasurePass measure) {
+ public void computeSize(
+ PaintContext context,
+ float minWidth,
+ float maxWidth,
+ float minHeight,
+ float maxHeight,
+ MeasurePass measure) {
for (Component child : mChildrenComponents) {
child.measure(context, minWidth, maxWidth, minHeight, maxHeight, measure);
}
}
@Override
- public void internalLayoutMeasure(PaintContext context,
- MeasurePass measure) {
+ public void internalLayoutMeasure(PaintContext context, MeasurePass measure) {
ComponentMeasure selfMeasure = measure.get(this);
float selfWidth = selfMeasure.getW() - mPaddingLeft - mPaddingRight;
float selfHeight = selfMeasure.getH() - mPaddingTop - mPaddingBottom;
@@ -136,8 +169,12 @@ public class BoxLayout extends LayoutManager implements ComponentStartOperation
return Operations.LAYOUT_BOX;
}
- public static void apply(WireBuffer buffer, int componentId, int animationId,
- int horizontalPositioning, int verticalPositioning) {
+ public static void apply(
+ WireBuffer buffer,
+ int componentId,
+ int animationId,
+ int horizontalPositioning,
+ int verticalPositioning) {
buffer.start(Operations.LAYOUT_BOX);
buffer.writeInt(componentId);
buffer.writeInt(animationId);
@@ -150,24 +187,32 @@ public class BoxLayout extends LayoutManager implements ComponentStartOperation
int animationId = buffer.readInt();
int horizontalPositioning = buffer.readInt();
int verticalPositioning = buffer.readInt();
- operations.add(new BoxLayout(null, componentId, animationId,
- horizontalPositioning, verticalPositioning));
+ operations.add(
+ new BoxLayout(
+ null,
+ componentId,
+ animationId,
+ horizontalPositioning,
+ verticalPositioning));
}
public static void documentation(DocumentationBuilder doc) {
doc.operation("Layout Operations", id(), name())
- .description("Box layout implementation.\n\n"
- + "Child components are laid out independently from one another,\n"
- + " and painted in their hierarchy order (first children drawn"
- + "before the latter). Horizontal and Vertical positioning"
- + "are supported.")
+ .description(
+ "Box layout implementation.\n\n"
+ + "Child components are laid out independently from one another,\n"
+ + " and painted in their hierarchy order (first children drawn"
+ + "before the latter). Horizontal and Vertical positioning"
+ + "are supported.")
.examplesDimension(150, 100)
.exampleImage("Top", "layout-BoxLayout-start-top.png")
.exampleImage("Center", "layout-BoxLayout-center-center.png")
.exampleImage("Bottom", "layout-BoxLayout-end-bottom.png")
.field(INT, "COMPONENT_ID", "unique id for this component")
- .field(INT, "ANIMATION_ID", "id used to match components,"
- + " for animation purposes")
+ .field(
+ INT,
+ "ANIMATION_ID",
+ "id used to match components," + " for animation purposes")
.field(INT, "HORIZONTAL_POSITIONING", "horizontal positioning value")
.possibleValues("START", BoxLayout.START)
.possibleValues("CENTER", BoxLayout.CENTER)
@@ -178,10 +223,8 @@ public class BoxLayout extends LayoutManager implements ComponentStartOperation
.possibleValues("BOTTOM", BoxLayout.BOTTOM);
}
-
@Override
public void write(WireBuffer buffer) {
- apply(buffer, mComponentId, mAnimationId,
- mHorizontalPositioning, mVerticalPositioning);
+ apply(buffer, mComponentId, mAnimationId, mHorizontalPositioning, mVerticalPositioning);
}
}
diff --git a/core/java/com/android/internal/widget/remotecompose/core/operations/layout/managers/CanvasLayout.java b/core/java/com/android/internal/widget/remotecompose/core/operations/layout/managers/CanvasLayout.java
index bce7a77abb36..f79976715ab3 100644
--- a/core/java/com/android/internal/widget/remotecompose/core/operations/layout/managers/CanvasLayout.java
+++ b/core/java/com/android/internal/widget/remotecompose/core/operations/layout/managers/CanvasLayout.java
@@ -15,7 +15,7 @@
*/
package com.android.internal.widget.remotecompose.core.operations.layout.managers;
-import static com.android.internal.widget.remotecompose.core.documentation.Operation.INT;
+import static com.android.internal.widget.remotecompose.core.documentation.DocumentedOperation.INT;
import com.android.internal.widget.remotecompose.core.Operation;
import com.android.internal.widget.remotecompose.core.Operations;
@@ -29,8 +29,14 @@ import com.android.internal.widget.remotecompose.core.operations.layout.measure.
import java.util.List;
public class CanvasLayout extends BoxLayout {
- public CanvasLayout(Component parent, int componentId, int animationId,
- float x, float y, float width, float height) {
+ public CanvasLayout(
+ Component parent,
+ int componentId,
+ int animationId,
+ float x,
+ float y,
+ float width,
+ float height) {
super(parent, componentId, animationId, x, y, width, height, 0, 0);
}
@@ -40,10 +46,23 @@ public class CanvasLayout extends BoxLayout {
@Override
public String toString() {
- return "CANVAS [" + mComponentId + ":" + mAnimationId + "] (" + mX + ", "
- + mY + " - " + mWidth + " x " + mHeight + ") " + mVisibility;
+ return "CANVAS ["
+ + mComponentId
+ + ":"
+ + mAnimationId
+ + "] ("
+ + mX
+ + ", "
+ + mY
+ + " - "
+ + mWidth
+ + " x "
+ + mHeight
+ + ") "
+ + mVisibility;
}
+ @Override
protected String getSerializedName() {
return "CANVAS";
}
@@ -72,13 +91,14 @@ public class CanvasLayout extends BoxLayout {
doc.operation("Layout Operations", id(), name())
.description("Canvas implementation. Encapsulate draw operations.\n\n")
.field(INT, "COMPONENT_ID", "unique id for this component")
- .field(INT, "ANIMATION_ID", "id used to match components,"
- + " for animation purposes");
+ .field(
+ INT,
+ "ANIMATION_ID",
+ "id used to match components," + " for animation purposes");
}
@Override
- public void internalLayoutMeasure(PaintContext context,
- MeasurePass measure) {
+ public void internalLayoutMeasure(PaintContext context, MeasurePass measure) {
ComponentMeasure selfMeasure = measure.get(this);
float selfWidth = selfMeasure.getW() - mPaddingLeft - mPaddingRight;
float selfHeight = selfMeasure.getH() - mPaddingTop - mPaddingBottom;
diff --git a/core/java/com/android/internal/widget/remotecompose/core/operations/layout/managers/ColumnLayout.java b/core/java/com/android/internal/widget/remotecompose/core/operations/layout/managers/ColumnLayout.java
index 48d966ebe9a7..402b784343ad 100644
--- a/core/java/com/android/internal/widget/remotecompose/core/operations/layout/managers/ColumnLayout.java
+++ b/core/java/com/android/internal/widget/remotecompose/core/operations/layout/managers/ColumnLayout.java
@@ -15,8 +15,8 @@
*/
package com.android.internal.widget.remotecompose.core.operations.layout.managers;
-import static com.android.internal.widget.remotecompose.core.documentation.Operation.FLOAT;
-import static com.android.internal.widget.remotecompose.core.documentation.Operation.INT;
+import static com.android.internal.widget.remotecompose.core.documentation.DocumentedOperation.FLOAT;
+import static com.android.internal.widget.remotecompose.core.documentation.DocumentedOperation.INT;
import com.android.internal.widget.remotecompose.core.Operation;
import com.android.internal.widget.remotecompose.core.Operations;
@@ -34,8 +34,7 @@ import com.android.internal.widget.remotecompose.core.operations.layout.utils.De
import java.util.List;
/**
- * Simple Column layout implementation
- * - also supports weight and horizontal/vertical positioning
+ * Simple Column layout implementation - also supports weight and horizontal/vertical positioning
*/
public class ColumnLayout extends LayoutManager implements ComponentStartOperation {
public static final int START = 1;
@@ -51,69 +50,122 @@ public class ColumnLayout extends LayoutManager implements ComponentStartOperati
int mVerticalPositioning;
float mSpacedBy = 0f;
- public ColumnLayout(Component parent, int componentId, int animationId,
- float x, float y, float width, float height,
- int horizontalPositioning, int verticalPositioning, float spacedBy) {
+ public ColumnLayout(
+ Component parent,
+ int componentId,
+ int animationId,
+ float x,
+ float y,
+ float width,
+ float height,
+ int horizontalPositioning,
+ int verticalPositioning,
+ float spacedBy) {
super(parent, componentId, animationId, x, y, width, height);
mHorizontalPositioning = horizontalPositioning;
mVerticalPositioning = verticalPositioning;
mSpacedBy = spacedBy;
}
- public ColumnLayout(Component parent, int componentId, int animationId,
- int horizontalPositioning, int verticalPositioning, float spacedBy) {
- this(parent, componentId, animationId, 0, 0, 0, 0,
- horizontalPositioning, verticalPositioning, spacedBy);
+ public ColumnLayout(
+ Component parent,
+ int componentId,
+ int animationId,
+ int horizontalPositioning,
+ int verticalPositioning,
+ float spacedBy) {
+ this(
+ parent,
+ componentId,
+ animationId,
+ 0,
+ 0,
+ 0,
+ 0,
+ horizontalPositioning,
+ verticalPositioning,
+ spacedBy);
}
@Override
public String toString() {
- return "COLUMN [" + mComponentId + ":" + mAnimationId + "] (" + mX + ", "
- + mY + " - " + mWidth + " x " + mHeight + ") " + mVisibility;
+ return "COLUMN ["
+ + mComponentId
+ + ":"
+ + mAnimationId
+ + "] ("
+ + mX
+ + ", "
+ + mY
+ + " - "
+ + mWidth
+ + " x "
+ + mHeight
+ + ") "
+ + mVisibility;
}
+ @Override
protected String getSerializedName() {
return "COLUMN";
}
@Override
- public void computeWrapSize(PaintContext context, float maxWidth, float maxHeight,
- MeasurePass measure, Size size) {
+ public void computeWrapSize(
+ PaintContext context, float maxWidth, float maxHeight, MeasurePass measure, Size size) {
DebugLog.s(() -> "COMPUTE WRAP SIZE in " + this + " (" + mComponentId + ")");
+ int visibleChildrens = 0;
for (Component c : mChildrenComponents) {
- c.measure(context, 0f, maxWidth,
- 0f, maxHeight, measure);
+ c.measure(context, 0f, maxWidth, 0f, maxHeight, measure);
ComponentMeasure m = measure.get(c);
- size.setWidth(Math.max(size.getWidth(), m.getW()));
- size.setHeight(size.getHeight() + m.getH());
+ if (m.getVisibility() != Visibility.GONE) {
+ size.setWidth(Math.max(size.getWidth(), m.getW()));
+ size.setHeight(size.getHeight() + m.getH());
+ visibleChildrens++;
+ }
}
if (!mChildrenComponents.isEmpty()) {
- size.setHeight(size.getHeight()
- + (mSpacedBy * (mChildrenComponents.size() - 1)));
+ size.setHeight(size.getHeight() + (mSpacedBy * (visibleChildrens - 1)));
}
DebugLog.e();
}
@Override
- public void computeSize(PaintContext context, float minWidth, float maxWidth,
- float minHeight, float maxHeight, MeasurePass measure) {
+ public void computeSize(
+ PaintContext context,
+ float minWidth,
+ float maxWidth,
+ float minHeight,
+ float maxHeight,
+ MeasurePass measure) {
DebugLog.s(() -> "COMPUTE SIZE in " + this + " (" + mComponentId + ")");
float mh = maxHeight;
for (Component child : mChildrenComponents) {
child.measure(context, minWidth, maxWidth, minHeight, mh, measure);
ComponentMeasure m = measure.get(child);
- mh -= m.getH();
+ if (m.getVisibility() != Visibility.GONE) {
+ mh -= m.getH();
+ }
}
DebugLog.e();
}
@Override
- public void internalLayoutMeasure(PaintContext context,
- MeasurePass measure) {
+ public void internalLayoutMeasure(PaintContext context, MeasurePass measure) {
ComponentMeasure selfMeasure = measure.get(this);
- DebugLog.s(() -> "INTERNAL LAYOUT " + this + " (" + mComponentId + ") children: "
- + mChildrenComponents.size() + " size (" + selfMeasure.getW()
- + " x " + selfMeasure.getH() + ")");
+ DebugLog.s(
+ () ->
+ "INTERNAL LAYOUT "
+ + this
+ + " ("
+ + mComponentId
+ + ") children: "
+ + mChildrenComponents.size()
+ + " size ("
+ + selfMeasure.getW()
+ + " x "
+ + selfMeasure.getH()
+ + ")");
if (mChildrenComponents.isEmpty()) {
DebugLog.e();
return;
@@ -127,6 +179,9 @@ public class ColumnLayout extends LayoutManager implements ComponentStartOperati
float totalWeights = 0f;
for (Component child : mChildrenComponents) {
ComponentMeasure childMeasure = measure.get(child);
+ if (childMeasure.getVisibility() == Visibility.GONE) {
+ continue;
+ }
if (child instanceof LayoutComponent
&& ((LayoutComponent) child).getHeightModifier().hasWeight()) {
hasWeights = true;
@@ -141,21 +196,34 @@ public class ColumnLayout extends LayoutManager implements ComponentStartOperati
if (child instanceof LayoutComponent
&& ((LayoutComponent) child).getHeightModifier().hasWeight()) {
ComponentMeasure childMeasure = measure.get(child);
+ if (childMeasure.getVisibility() == Visibility.GONE) {
+ continue;
+ }
float weight = ((LayoutComponent) child).getHeightModifier().getValue();
childMeasure.setH((weight * availableSpace) / totalWeights);
- child.measure(context, childMeasure.getW(),
- childMeasure.getW(), childMeasure.getH(), childMeasure.getH(), measure);
+ child.measure(
+ context,
+ childMeasure.getW(),
+ childMeasure.getW(),
+ childMeasure.getH(),
+ childMeasure.getH(),
+ measure);
}
}
}
childrenHeight = 0f;
+ int visibleChildrens = 0;
for (Component child : mChildrenComponents) {
ComponentMeasure childMeasure = measure.get(child);
+ if (childMeasure.getVisibility() == Visibility.GONE) {
+ continue;
+ }
childrenWidth = Math.max(childrenWidth, childMeasure.getW());
childrenHeight += childMeasure.getH();
+ visibleChildrens++;
}
- childrenHeight += mSpacedBy * (mChildrenComponents.size() - 1);
+ childrenHeight += mSpacedBy * (visibleChildrens - 1);
float tx = 0f;
float ty = 0f;
@@ -175,24 +243,33 @@ public class ColumnLayout extends LayoutManager implements ComponentStartOperati
case SPACE_BETWEEN:
for (Component child : mChildrenComponents) {
ComponentMeasure childMeasure = measure.get(child);
+ if (childMeasure.getVisibility() == Visibility.GONE) {
+ continue;
+ }
total += childMeasure.getH();
}
- verticalGap = (selfHeight - total) / (mChildrenComponents.size() - 1);
+ verticalGap = (selfHeight - total) / (visibleChildrens - 1);
break;
case SPACE_EVENLY:
for (Component child : mChildrenComponents) {
ComponentMeasure childMeasure = measure.get(child);
+ if (childMeasure.getVisibility() == Visibility.GONE) {
+ continue;
+ }
total += childMeasure.getH();
}
- verticalGap = (selfHeight - total) / (mChildrenComponents.size() + 1);
+ verticalGap = (selfHeight - total) / (visibleChildrens + 1);
ty = verticalGap;
break;
case SPACE_AROUND:
for (Component child : mChildrenComponents) {
ComponentMeasure childMeasure = measure.get(child);
+ if (childMeasure.getVisibility() == Visibility.GONE) {
+ continue;
+ }
total += childMeasure.getH();
}
- verticalGap = (selfHeight - total) / (mChildrenComponents.size());
+ verticalGap = (selfHeight - total) / visibleChildrens;
ty = verticalGap / 2f;
break;
}
@@ -211,6 +288,9 @@ public class ColumnLayout extends LayoutManager implements ComponentStartOperati
}
childMeasure.setX(tx);
childMeasure.setY(ty);
+ if (childMeasure.getVisibility() == Visibility.GONE) {
+ continue;
+ }
ty += childMeasure.getH();
if (mVerticalPositioning == SPACE_BETWEEN
|| mVerticalPositioning == SPACE_AROUND
@@ -230,8 +310,13 @@ public class ColumnLayout extends LayoutManager implements ComponentStartOperati
return Operations.LAYOUT_COLUMN;
}
- public static void apply(WireBuffer buffer, int componentId, int animationId,
- int horizontalPositioning, int verticalPositioning, float spacedBy) {
+ public static void apply(
+ WireBuffer buffer,
+ int componentId,
+ int animationId,
+ int horizontalPositioning,
+ int verticalPositioning,
+ float spacedBy) {
buffer.start(Operations.LAYOUT_COLUMN);
buffer.writeInt(componentId);
buffer.writeInt(animationId);
@@ -246,15 +331,22 @@ public class ColumnLayout extends LayoutManager implements ComponentStartOperati
int horizontalPositioning = buffer.readInt();
int verticalPositioning = buffer.readInt();
float spacedBy = buffer.readFloat();
- operations.add(new ColumnLayout(null, componentId, animationId,
- horizontalPositioning, verticalPositioning, spacedBy));
+ operations.add(
+ new ColumnLayout(
+ null,
+ componentId,
+ animationId,
+ horizontalPositioning,
+ verticalPositioning,
+ spacedBy));
}
public static void documentation(DocumentationBuilder doc) {
doc.operation("Layout Operations", id(), name())
- .description("Column layout implementation, positioning components one"
- + " after the other vertically.\n\n"
- + "It supports weight and horizontal/vertical positioning.")
+ .description(
+ "Column layout implementation, positioning components one"
+ + " after the other vertically.\n\n"
+ + "It supports weight and horizontal/vertical positioning.")
.examplesDimension(100, 400)
.exampleImage("Top", "layout-ColumnLayout-start-top.png")
.exampleImage("Center", "layout-ColumnLayout-start-center.png")
@@ -263,8 +355,10 @@ public class ColumnLayout extends LayoutManager implements ComponentStartOperati
.exampleImage("SpaceAround", "layout-ColumnLayout-start-space-around.png")
.exampleImage("SpaceBetween", "layout-ColumnLayout-start-space-between.png")
.field(INT, "COMPONENT_ID", "unique id for this component")
- .field(INT, "ANIMATION_ID", "id used to match components,"
- + " for animation purposes")
+ .field(
+ INT,
+ "ANIMATION_ID",
+ "id used to match components," + " for animation purposes")
.field(INT, "HORIZONTAL_POSITIONING", "horizontal positioning value")
.possibleValues("START", ColumnLayout.START)
.possibleValues("CENTER", ColumnLayout.CENTER)
@@ -281,7 +375,12 @@ public class ColumnLayout extends LayoutManager implements ComponentStartOperati
@Override
public void write(WireBuffer buffer) {
- apply(buffer, mComponentId, mAnimationId,
- mHorizontalPositioning, mVerticalPositioning, mSpacedBy);
+ apply(
+ buffer,
+ mComponentId,
+ mAnimationId,
+ mHorizontalPositioning,
+ mVerticalPositioning,
+ mSpacedBy);
}
}
diff --git a/core/java/com/android/internal/widget/remotecompose/core/operations/layout/managers/LayoutManager.java b/core/java/com/android/internal/widget/remotecompose/core/operations/layout/managers/LayoutManager.java
index 3a366172a51f..308ed64ee8ea 100644
--- a/core/java/com/android/internal/widget/remotecompose/core/operations/layout/managers/LayoutManager.java
+++ b/core/java/com/android/internal/widget/remotecompose/core/operations/layout/managers/LayoutManager.java
@@ -24,53 +24,58 @@ import com.android.internal.widget.remotecompose.core.operations.layout.measure.
import com.android.internal.widget.remotecompose.core.operations.layout.measure.MeasurePass;
import com.android.internal.widget.remotecompose.core.operations.layout.measure.Size;
-/**
- * Base class for layout managers -- resizable components.
- */
+/** Base class for layout managers -- resizable components. */
public abstract class LayoutManager extends LayoutComponent implements Measurable {
Size mCachedWrapSize = new Size(0f, 0f);
- public LayoutManager(Component parent, int componentId, int animationId,
- float x, float y, float width, float height) {
+ public LayoutManager(
+ Component parent,
+ int componentId,
+ int animationId,
+ float x,
+ float y,
+ float width,
+ float height) {
super(parent, componentId, animationId, x, y, width, height);
}
- /**
- * Implemented by subclasses to provide a layout/measure pass
- */
- public void internalLayoutMeasure(PaintContext context,
- MeasurePass measure) {
+ /** Implemented by subclasses to provide a layout/measure pass */
+ public void internalLayoutMeasure(PaintContext context, MeasurePass measure) {
// nothing here
}
- /**
- * Subclasses can implement this to provide wrap sizing
- */
- public void computeWrapSize(PaintContext context, float maxWidth, float maxHeight,
- MeasurePass measure, Size size) {
+ /** Subclasses can implement this to provide wrap sizing */
+ public void computeWrapSize(
+ PaintContext context, float maxWidth, float maxHeight, MeasurePass measure, Size size) {
// nothing here
}
- /**
- * Subclasses can implement this when not in wrap sizing
- */
- public void computeSize(PaintContext context, float minWidth, float maxWidth,
- float minHeight, float maxHeight, MeasurePass measure) {
+ /** Subclasses can implement this when not in wrap sizing */
+ public void computeSize(
+ PaintContext context,
+ float minWidth,
+ float maxWidth,
+ float minHeight,
+ float maxHeight,
+ MeasurePass measure) {
// nothing here
}
- /**
- * Base implementation of the measure resolution
- */
+ /** Base implementation of the measure resolution */
@Override
- public void measure(PaintContext context, float minWidth, float maxWidth,
- float minHeight, float maxHeight, MeasurePass measure) {
+ public void measure(
+ PaintContext context,
+ float minWidth,
+ float maxWidth,
+ float minHeight,
+ float maxHeight,
+ MeasurePass measure) {
boolean hasWrap = true;
- float measuredWidth = Math.min(maxWidth,
- computeModifierDefinedWidth() - mMarginLeft - mMarginRight);
- float measuredHeight = Math.min(maxHeight,
- computeModifierDefinedHeight() - mMarginTop - mMarginBottom);
+ float measuredWidth =
+ Math.min(maxWidth, computeModifierDefinedWidth() - mMarginLeft - mMarginRight);
+ float measuredHeight =
+ Math.min(maxHeight, computeModifierDefinedHeight() - mMarginTop - mMarginBottom);
float insetMaxWidth = maxWidth - mMarginLeft - mMarginRight;
float insetMaxHeight = maxHeight - mMarginTop - mMarginBottom;
if (mWidthModifier.isWrap() || mHeightModifier.isWrap()) {
@@ -129,9 +134,7 @@ public abstract class LayoutManager extends LayoutComponent implements Measurabl
internalLayoutMeasure(context, measure);
}
- /**
- * basic layout of internal components
- */
+ /** basic layout of internal components */
@Override
public void layout(RemoteContext context, MeasurePass measure) {
super.layout(context, measure);
@@ -143,4 +146,18 @@ public abstract class LayoutManager extends LayoutComponent implements Measurabl
}
this.mNeedsMeasure = false;
}
+
+ /**
+ * Only layout self, not children
+ *
+ * @param context
+ * @param measure
+ */
+ public void selfLayout(RemoteContext context, MeasurePass measure) {
+ super.layout(context, measure);
+ ComponentMeasure self = measure.get(this);
+
+ mComponentModifiers.layout(context, self.getW(), self.getH());
+ this.mNeedsMeasure = false;
+ }
}
diff --git a/core/java/com/android/internal/widget/remotecompose/core/operations/layout/managers/RowLayout.java b/core/java/com/android/internal/widget/remotecompose/core/operations/layout/managers/RowLayout.java
index 5e452f363c06..b29a05c27e5f 100644
--- a/core/java/com/android/internal/widget/remotecompose/core/operations/layout/managers/RowLayout.java
+++ b/core/java/com/android/internal/widget/remotecompose/core/operations/layout/managers/RowLayout.java
@@ -15,8 +15,8 @@
*/
package com.android.internal.widget.remotecompose.core.operations.layout.managers;
-import static com.android.internal.widget.remotecompose.core.documentation.Operation.FLOAT;
-import static com.android.internal.widget.remotecompose.core.documentation.Operation.INT;
+import static com.android.internal.widget.remotecompose.core.documentation.DocumentedOperation.FLOAT;
+import static com.android.internal.widget.remotecompose.core.documentation.DocumentedOperation.INT;
import com.android.internal.widget.remotecompose.core.Operation;
import com.android.internal.widget.remotecompose.core.Operations;
@@ -33,10 +33,7 @@ import com.android.internal.widget.remotecompose.core.operations.layout.utils.De
import java.util.List;
-/**
- * Simple Row layout implementation
- * - also supports weight and horizontal/vertical positioning
- */
+/** Simple Row layout implementation - also supports weight and horizontal/vertical positioning */
public class RowLayout extends LayoutManager implements ComponentStartOperation {
public static final int START = 1;
public static final int CENTER = 2;
@@ -51,68 +48,122 @@ public class RowLayout extends LayoutManager implements ComponentStartOperation
int mVerticalPositioning;
float mSpacedBy = 0f;
- public RowLayout(Component parent, int componentId, int animationId,
- float x, float y, float width, float height,
- int horizontalPositioning, int verticalPositioning, float spacedBy) {
+ public RowLayout(
+ Component parent,
+ int componentId,
+ int animationId,
+ float x,
+ float y,
+ float width,
+ float height,
+ int horizontalPositioning,
+ int verticalPositioning,
+ float spacedBy) {
super(parent, componentId, animationId, x, y, width, height);
mHorizontalPositioning = horizontalPositioning;
mVerticalPositioning = verticalPositioning;
mSpacedBy = spacedBy;
}
- public RowLayout(Component parent, int componentId, int animationId,
- int horizontalPositioning, int verticalPositioning, float spacedBy) {
- this(parent, componentId, animationId, 0, 0, 0, 0,
- horizontalPositioning, verticalPositioning, spacedBy);
+ public RowLayout(
+ Component parent,
+ int componentId,
+ int animationId,
+ int horizontalPositioning,
+ int verticalPositioning,
+ float spacedBy) {
+ this(
+ parent,
+ componentId,
+ animationId,
+ 0,
+ 0,
+ 0,
+ 0,
+ horizontalPositioning,
+ verticalPositioning,
+ spacedBy);
}
@Override
public String toString() {
- return "ROW [" + mComponentId + ":" + mAnimationId + "] (" + mX + ", "
- + mY + " - " + mWidth + " x " + mHeight + ") " + mVisibility;
+ return "ROW ["
+ + mComponentId
+ + ":"
+ + mAnimationId
+ + "] ("
+ + mX
+ + ", "
+ + mY
+ + " - "
+ + mWidth
+ + " x "
+ + mHeight
+ + ") "
+ + mVisibility;
}
+ @Override
protected String getSerializedName() {
return "ROW";
}
@Override
- public void computeWrapSize(PaintContext context, float maxWidth, float maxHeight,
- MeasurePass measure, Size size) {
+ public void computeWrapSize(
+ PaintContext context, float maxWidth, float maxHeight, MeasurePass measure, Size size) {
DebugLog.s(() -> "COMPUTE WRAP SIZE in " + this + " (" + mComponentId + ")");
+ // int visibleChildrens = 0;
for (Component c : mChildrenComponents) {
c.measure(context, 0f, maxWidth, 0f, maxHeight, measure);
ComponentMeasure m = measure.get(c);
- size.setWidth(size.getWidth() + m.getW());
- size.setHeight(Math.max(size.getHeight(), m.getH()));
+ if (m.getVisibility() != Visibility.GONE) {
+ size.setWidth(size.getWidth() + m.getW());
+ size.setHeight(Math.max(size.getHeight(), m.getH()));
+ // visibleChildrens++;
+ }
}
if (!mChildrenComponents.isEmpty()) {
- size.setWidth(size.getWidth()
- + (mSpacedBy * (mChildrenComponents.size() - 1)));
+ size.setWidth(size.getWidth() + (mSpacedBy * (mChildrenComponents.size() - 1)));
}
DebugLog.e();
}
@Override
- public void computeSize(PaintContext context, float minWidth, float maxWidth,
- float minHeight, float maxHeight, MeasurePass measure) {
+ public void computeSize(
+ PaintContext context,
+ float minWidth,
+ float maxWidth,
+ float minHeight,
+ float maxHeight,
+ MeasurePass measure) {
DebugLog.s(() -> "COMPUTE SIZE in " + this + " (" + mComponentId + ")");
float mw = maxWidth;
for (Component child : mChildrenComponents) {
child.measure(context, minWidth, mw, minHeight, maxHeight, measure);
ComponentMeasure m = measure.get(child);
- mw -= m.getW();
+ if (m.getVisibility() != Visibility.GONE) {
+ mw -= m.getW();
+ }
}
DebugLog.e();
}
@Override
- public void internalLayoutMeasure(PaintContext context,
- MeasurePass measure) {
+ public void internalLayoutMeasure(PaintContext context, MeasurePass measure) {
ComponentMeasure selfMeasure = measure.get(this);
- DebugLog.s(() -> "INTERNAL LAYOUT " + this + " (" + mComponentId + ") children: "
- + mChildrenComponents.size() + " size (" + selfMeasure.getW()
- + " x " + selfMeasure.getH() + ")");
+ DebugLog.s(
+ () ->
+ "INTERNAL LAYOUT "
+ + this
+ + " ("
+ + mComponentId
+ + ") children: "
+ + mChildrenComponents.size()
+ + " size ("
+ + selfMeasure.getW()
+ + " x "
+ + selfMeasure.getH()
+ + ")");
if (mChildrenComponents.isEmpty()) {
DebugLog.e();
return;
@@ -126,6 +177,9 @@ public class RowLayout extends LayoutManager implements ComponentStartOperation
float totalWeights = 0f;
for (Component child : mChildrenComponents) {
ComponentMeasure childMeasure = measure.get(child);
+ if (childMeasure.getVisibility() == Visibility.GONE) {
+ continue;
+ }
if (child instanceof LayoutComponent
&& ((LayoutComponent) child).getWidthModifier().hasWeight()) {
hasWeights = true;
@@ -143,21 +197,34 @@ public class RowLayout extends LayoutManager implements ComponentStartOperation
if (child instanceof LayoutComponent
&& ((LayoutComponent) child).getWidthModifier().hasWeight()) {
ComponentMeasure childMeasure = measure.get(child);
+ if (childMeasure.getVisibility() == Visibility.GONE) {
+ continue;
+ }
float weight = ((LayoutComponent) child).getWidthModifier().getValue();
childMeasure.setW((weight * availableSpace) / totalWeights);
- child.measure(context, childMeasure.getW(),
- childMeasure.getW(), childMeasure.getH(), childMeasure.getH(), measure);
+ child.measure(
+ context,
+ childMeasure.getW(),
+ childMeasure.getW(),
+ childMeasure.getH(),
+ childMeasure.getH(),
+ measure);
}
}
}
childrenWidth = 0f;
+ int visibleChildrens = 0;
for (Component child : mChildrenComponents) {
ComponentMeasure childMeasure = measure.get(child);
+ if (childMeasure.getVisibility() == Visibility.GONE) {
+ continue;
+ }
childrenWidth += childMeasure.getW();
childrenHeight = Math.max(childrenHeight, childMeasure.getH());
+ visibleChildrens++;
}
- childrenWidth += mSpacedBy * (mChildrenComponents.size() - 1);
+ childrenWidth += mSpacedBy * (visibleChildrens - 1);
float tx = 0f;
float ty = 0f;
@@ -178,24 +245,33 @@ public class RowLayout extends LayoutManager implements ComponentStartOperation
case SPACE_BETWEEN:
for (Component child : mChildrenComponents) {
ComponentMeasure childMeasure = measure.get(child);
+ if (childMeasure.getVisibility() == Visibility.GONE) {
+ continue;
+ }
total += childMeasure.getW();
}
- horizontalGap = (selfWidth - total) / (mChildrenComponents.size() - 1);
+ horizontalGap = (selfWidth - total) / (visibleChildrens - 1);
break;
case SPACE_EVENLY:
for (Component child : mChildrenComponents) {
ComponentMeasure childMeasure = measure.get(child);
+ if (childMeasure.getVisibility() == Visibility.GONE) {
+ continue;
+ }
total += childMeasure.getW();
}
- horizontalGap = (selfWidth - total) / (mChildrenComponents.size() + 1);
+ horizontalGap = (selfWidth - total) / (visibleChildrens + 1);
tx = horizontalGap;
break;
case SPACE_AROUND:
for (Component child : mChildrenComponents) {
ComponentMeasure childMeasure = measure.get(child);
+ if (childMeasure.getVisibility() == Visibility.GONE) {
+ continue;
+ }
total += childMeasure.getW();
}
- horizontalGap = (selfWidth - total) / (mChildrenComponents.size());
+ horizontalGap = (selfWidth - total) / visibleChildrens;
tx = horizontalGap / 2f;
break;
}
@@ -215,6 +291,9 @@ public class RowLayout extends LayoutManager implements ComponentStartOperation
}
childMeasure.setX(tx);
childMeasure.setY(ty);
+ if (childMeasure.getVisibility() == Visibility.GONE) {
+ continue;
+ }
tx += childMeasure.getW();
if (mHorizontalPositioning == SPACE_BETWEEN
|| mHorizontalPositioning == SPACE_AROUND
@@ -234,8 +313,13 @@ public class RowLayout extends LayoutManager implements ComponentStartOperation
return Operations.LAYOUT_ROW;
}
- public static void apply(WireBuffer buffer, int componentId, int animationId,
- int horizontalPositioning, int verticalPositioning, float spacedBy) {
+ public static void apply(
+ WireBuffer buffer,
+ int componentId,
+ int animationId,
+ int horizontalPositioning,
+ int verticalPositioning,
+ float spacedBy) {
buffer.start(Operations.LAYOUT_ROW);
buffer.writeInt(componentId);
buffer.writeInt(animationId);
@@ -250,15 +334,22 @@ public class RowLayout extends LayoutManager implements ComponentStartOperation
int horizontalPositioning = buffer.readInt();
int verticalPositioning = buffer.readInt();
float spacedBy = buffer.readFloat();
- operations.add(new RowLayout(null, componentId, animationId,
- horizontalPositioning, verticalPositioning, spacedBy));
+ operations.add(
+ new RowLayout(
+ null,
+ componentId,
+ animationId,
+ horizontalPositioning,
+ verticalPositioning,
+ spacedBy));
}
public static void documentation(DocumentationBuilder doc) {
doc.operation("Layout Operations", id(), name())
- .description("Row layout implementation, positioning components one"
- + " after the other horizontally.\n\n"
- + "It supports weight and horizontal/vertical positioning.")
+ .description(
+ "Row layout implementation, positioning components one"
+ + " after the other horizontally.\n\n"
+ + "It supports weight and horizontal/vertical positioning.")
.examplesDimension(400, 100)
.exampleImage("Start", "layout-RowLayout-start-top.png")
.exampleImage("Center", "layout-RowLayout-center-top.png")
@@ -267,8 +358,10 @@ public class RowLayout extends LayoutManager implements ComponentStartOperation
.exampleImage("SpaceAround", "layout-RowLayout-space-around-top.png")
.exampleImage("SpaceBetween", "layout-RowLayout-space-between-top.png")
.field(INT, "COMPONENT_ID", "unique id for this component")
- .field(INT, "ANIMATION_ID", "id used to match components,"
- + " for animation purposes")
+ .field(
+ INT,
+ "ANIMATION_ID",
+ "id used to match components," + " for animation purposes")
.field(INT, "HORIZONTAL_POSITIONING", "horizontal positioning value")
.possibleValues("START", RowLayout.START)
.possibleValues("CENTER", RowLayout.CENTER)
@@ -285,7 +378,12 @@ public class RowLayout extends LayoutManager implements ComponentStartOperation
@Override
public void write(WireBuffer buffer) {
- apply(buffer, mComponentId, mAnimationId,
- mHorizontalPositioning, mVerticalPositioning, mSpacedBy);
+ apply(
+ buffer,
+ mComponentId,
+ mAnimationId,
+ mHorizontalPositioning,
+ mVerticalPositioning,
+ mSpacedBy);
}
}
diff --git a/core/java/com/android/internal/widget/remotecompose/core/operations/layout/managers/StateLayout.java b/core/java/com/android/internal/widget/remotecompose/core/operations/layout/managers/StateLayout.java
new file mode 100644
index 000000000000..b5c728135f76
--- /dev/null
+++ b/core/java/com/android/internal/widget/remotecompose/core/operations/layout/managers/StateLayout.java
@@ -0,0 +1,565 @@
+/*
+ * 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.
+ */
+package com.android.internal.widget.remotecompose.core.operations.layout.managers;
+
+import com.android.internal.widget.remotecompose.core.CoreDocument;
+import com.android.internal.widget.remotecompose.core.Operation;
+import com.android.internal.widget.remotecompose.core.Operations;
+import com.android.internal.widget.remotecompose.core.PaintContext;
+import com.android.internal.widget.remotecompose.core.PaintOperation;
+import com.android.internal.widget.remotecompose.core.RemoteContext;
+import com.android.internal.widget.remotecompose.core.WireBuffer;
+import com.android.internal.widget.remotecompose.core.operations.layout.Component;
+import com.android.internal.widget.remotecompose.core.operations.layout.ComponentStartOperation;
+import com.android.internal.widget.remotecompose.core.operations.layout.LayoutComponent;
+import com.android.internal.widget.remotecompose.core.operations.layout.measure.ComponentMeasure;
+import com.android.internal.widget.remotecompose.core.operations.layout.measure.MeasurePass;
+import com.android.internal.widget.remotecompose.core.operations.layout.measure.Size;
+
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+/**
+ * State-based animated layout
+ *
+ * <p>States are defined as child layouts. This layout handles interpolating between the different
+ * state in order to provide an automatic transition.
+ */
+public class StateLayout extends LayoutManager implements ComponentStartOperation {
+
+ public int measuredLayoutIndex = 0;
+ public int currentLayoutIndex = 0;
+ public int previousLayoutIndex = 0;
+ private int mIndexId = 0;
+
+ // This keep track of all the components associated with a given Id,
+ // (the key being the id), and the set of components corresponds to the set of states
+ // TODO: we should be able to optimize this
+ public Map<Integer, Component[]> statePaintedComponents = new HashMap<>();
+
+ public int MAX_CACHE_ELEMENTS = 16;
+ public int[] cacheListElementsId = new int[MAX_CACHE_ELEMENTS];
+
+ public boolean inTransition = false;
+
+ public StateLayout(
+ Component parent,
+ int componentId,
+ int animationId,
+ float x,
+ float y,
+ float width,
+ float height,
+ int indexId) {
+ super(parent, componentId, animationId, x, y, width, height);
+ // if (layoutInfo.visibleLayoutIndex != null) {
+ // layoutInfo.visibleLayoutIndex!!.addChangeListener(this)
+ // }
+ mIndexId = indexId;
+ }
+
+ @Override
+ public void inflate() {
+ super.inflate();
+ hideLayoutsOtherThan(currentLayoutIndex);
+ }
+
+ public void findAnimatedComponents() {
+ for (int i = 0; i < mChildrenComponents.size(); i++) {
+ Component cs = mChildrenComponents.get(i);
+ if (cs instanceof LayoutComponent) {
+ LayoutComponent state = (LayoutComponent) cs;
+ state.setX(0f);
+ state.setY(0f);
+ ArrayList<Component> childrenComponents = state.getChildrenComponents();
+ for (int j = 0; j < childrenComponents.size(); j++) {
+ Component child = childrenComponents.get(j);
+ if (child.getAnimationId() != -1) {
+ if (!statePaintedComponents.containsKey(child.getAnimationId())) {
+ statePaintedComponents.put(
+ child.getAnimationId(),
+ new Component[mChildrenComponents.size()]);
+ }
+ statePaintedComponents.get(child.getAnimationId())[i] = child;
+ }
+ }
+ }
+ }
+ collapsePaintedComponents();
+ }
+
+ public void collapsePaintedComponents() {
+ int numStates = mChildrenComponents.size();
+ for (Integer id : statePaintedComponents.keySet()) {
+ Component[] list = statePaintedComponents.get(id);
+ int numComponents = list.length;
+ if (numComponents > 1 && list[0] != null) {
+ Component c1 = list[0];
+ boolean same = true;
+ for (int i = 1; i < list.length; i++) {
+ Component c2 = list[i];
+ if (c2 == null || !c1.suitableForTransition(c2)) {
+ same = false;
+ break;
+ }
+ }
+ if (same) {
+ // TODO: Fix, shouldn't want to recopy all components
+ for (int i = 0; i < numStates; i++) {
+ list[i] = c1;
+ }
+ }
+ }
+ }
+ }
+
+ @Override
+ public void computeSize(
+ PaintContext context,
+ float minWidth,
+ float maxWidth,
+ float minHeight,
+ float maxHeight,
+ MeasurePass measure) {
+ LayoutManager layout = getLayout(currentLayoutIndex);
+ layout.computeSize(context, minWidth, maxWidth, minHeight, maxHeight, measure);
+ }
+
+ @Override
+ public void internalLayoutMeasure(
+ PaintContext context,
+ // layoutInfo: LayoutInfo,
+ MeasurePass measure) {
+ LayoutManager layout = getLayout(currentLayoutIndex);
+ // layout.internalLayoutMeasure(context, layoutInfo, measure)
+ layout.internalLayoutMeasure(context, measure);
+ }
+
+ /** Subclasses can implement this to provide wrap sizing */
+ @Override
+ public void computeWrapSize(
+ PaintContext context, float maxWidth, float maxHeight, MeasurePass measure, Size size) {
+ LayoutManager layout = getLayout(currentLayoutIndex);
+ layout.computeWrapSize(context, maxWidth, maxHeight, measure, size);
+ }
+
+ @Override
+ public void onClick(RemoteContext context, CoreDocument document, float x, float y) {
+ if (!contains(x, y)) {
+ return;
+ }
+ LayoutManager layout = getLayout(currentLayoutIndex);
+ layout.onClick(context, document, x, y);
+ }
+
+ @Override
+ public void layout(RemoteContext context, MeasurePass measure) {
+ ComponentMeasure self = measure.get(this);
+ super.selfLayout(context, measure);
+
+ // We can simply layout the current layout...
+ LayoutManager layout = getLayout(currentLayoutIndex);
+
+ // Pass through the measure information from the state layout to the currently
+ // selected component that this being laid out.
+ ComponentMeasure layoutMeasure = measure.get(layout.getComponentId());
+ layoutMeasure.copyFrom(self);
+
+ layout.layout(context, measure);
+
+ // but if we are in a transition, we might have to layout previous widgets
+ if (inTransition && previousLayoutIndex != currentLayoutIndex) {
+ LayoutManager previous = getLayout(previousLayoutIndex);
+ for (Component c : previous.getChildrenComponents()) {
+ int id = c.getComponentId();
+ if (c.getAnimationId() != -1) {
+ id = c.getAnimationId();
+ Component[] rc = statePaintedComponents.get(id);
+ for (Component ac : rc) {
+ if (ac != null) {
+ ac.layout(context, measure);
+ }
+ }
+ }
+ if (measure.contains(id)) {
+ c.layout(context, measure);
+ }
+ }
+ }
+
+ mFirstLayout = false;
+ }
+
+ @Override
+ public void measure(
+ PaintContext context,
+ float minWidth,
+ float maxWidth,
+ float minHeight,
+ float maxHeight,
+ MeasurePass measure) {
+ // The general approach for this widget is to do most of the work/setup in measure.
+ // layout and paint then simply use what's been setup in the measure phase.
+
+ // First, let's initialize the statePaintedComponents array;
+ // it contains for each animation id a set of components associated, one for each state.
+ // For now to keep things simple, all components sets have the same size (== number of
+ // states)
+ if (statePaintedComponents.isEmpty()) {
+ findAnimatedComponents();
+ }
+
+ // TODO : FIRST LAYOUT ANIMATE THE GONE ELEMENT, RESIZING IT. We should be able to fix it
+ // if we resize things before animting.
+
+ LayoutManager layout = getLayout(currentLayoutIndex);
+
+ // ok so *before* we do the layout, we should make sure to set the *new* widgets (that
+ // share the same id) to be at the same bounds / position as the current displayed ones
+ if (inTransition && currentLayoutIndex != previousLayoutIndex) {
+ LayoutManager previousLayout = getLayout(previousLayoutIndex);
+ for (Component c : layout.getChildrenComponents()) {
+ int id = c.getAnimationId();
+ if (id == -1) {
+ continue;
+ }
+ for (Component pc : previousLayout.getChildrenComponents()) {
+ if (pc.getAnimationId() == id) {
+ Component prev =
+ statePaintedComponents.get(c.getAnimationId())[previousLayoutIndex];
+ if (c != prev) {
+ c.measure(
+ context,
+ prev.getWidth(),
+ prev.getWidth(),
+ prev.getHeight(),
+ prev.getHeight(),
+ measure);
+ c.layout(context.getContext(), measure);
+ c.setX(prev.getX());
+ c.setY(prev.getY());
+ c.mVisibility = Visibility.GONE;
+ }
+ break;
+ }
+ }
+ }
+ }
+
+ // Alright, now that things are set in place, let's go ahead and measure the new world...
+ layout.measure(context, minWidth, maxWidth, minHeight, maxHeight, measure);
+
+ // recopy to animationIds the values
+ for (Component c : layout.getChildrenComponents()) {
+ ComponentMeasure cm = measure.get(c);
+ if (c.getAnimationId() != -1) {
+ // First, we grab the current component for an animation id, and get its measure,
+ // then set this measure to the measure for the animation id
+ ComponentMeasure m = measure.get(c.getAnimationId());
+ m.copyFrom(cm);
+
+ m.setVisibility(Visibility.VISIBLE);
+
+ // Then for each components sharing the id in all the states...
+ Component[] components = statePaintedComponents.get(c.getAnimationId());
+ for (int idx = 0; idx < components.length; idx++) {
+ Component ac = components[idx];
+ if (ac != null) {
+ ComponentMeasure m2 = measure.get(ac.getComponentId());
+
+ // ... we set their measures to be the measure of the current component
+ if (c != ac) {
+ m2.copyFrom(cm);
+ }
+
+ // Finally let's make sure that for all components we set their visibility
+ if (idx == currentLayoutIndex) {
+ m2.setVisibility(Visibility.VISIBLE);
+ } else {
+ if (c != ac) {
+ m2.setVisibility(Visibility.GONE);
+ }
+ }
+
+ // if the component isn't the current one, we should measure it
+ if (c != ac) {
+ ac.measure(context, m.getW(), m.getW(), m.getH(), m.getH(), measure);
+ }
+ }
+ }
+ } else {
+ // TODO: Ideally unify the visibility handing so that we also work in terms of
+ // component and not panel visibility. Ideally do not change the .visibility
+ // attribute at all and actually use the "current index" to decide whether to
+ // draw or not.
+ cm.setVisibility(Visibility.VISIBLE);
+ }
+ }
+
+ // Make sure to mark the components that are not in the new layout as being GONE
+ if (previousLayoutIndex != currentLayoutIndex) {
+ LayoutManager previousLayout = getLayout(previousLayoutIndex);
+ for (Component c : previousLayout.getChildrenComponents()) {
+ int id = c.getComponentId();
+ if (c.getAnimationId() != -1) {
+ id = c.getAnimationId();
+ }
+ if (!measure.contains(id)) {
+ ComponentMeasure m = measure.get(c.getComponentId());
+ m.setX(c.getX());
+ m.setY(c.getY());
+ m.setW(c.getWidth());
+ m.setH(c.getHeight());
+ m.setVisibility(Visibility.GONE);
+ }
+ }
+ }
+
+ ComponentMeasure m = measure.get(layout);
+ ComponentMeasure own = measure.get(this);
+ own.copyFrom(m);
+ measuredLayoutIndex = currentLayoutIndex;
+ }
+
+ public void hideLayoutsOtherThan(int idx) {
+ int index = 0;
+ for (Component pane : mChildrenComponents) {
+ if (pane instanceof LayoutComponent) {
+ if (index != idx) {
+ pane.mVisibility = Visibility.GONE;
+ } else {
+ pane.mVisibility = Visibility.VISIBLE;
+ }
+ index++;
+ }
+ }
+ }
+
+ public LayoutManager getLayout(int idx) {
+ int index = 0;
+ for (Component pane : mChildrenComponents) {
+ if (pane instanceof LayoutComponent) {
+ if (index == idx) {
+ return (LayoutManager) pane;
+ }
+ index++;
+ }
+ }
+ return (LayoutManager) mChildrenComponents.get(0);
+ }
+
+ @Override
+ public void paint(PaintContext context) {
+ if (mIndexId != 0) {
+ int newValue = context.getContext().mRemoteComposeState.getInteger(mIndexId);
+ if (newValue != currentLayoutIndex) {
+ previousLayoutIndex = currentLayoutIndex;
+ currentLayoutIndex = newValue;
+ inTransition = true;
+ System.out.println("currentLayout index is $currentLayoutIndex");
+ // executeValueSetActions(getLayout(currentLayoutIndex));
+ invalidateMeasure();
+ }
+ }
+ System.out.println("PAINTING LAYOUT STATELAYOUT, CURRENT INDEX " + currentLayoutIndex);
+ // Make sure to mark any components that are not in either the current or previous layout
+ // as being GONE.
+ int index = 0;
+ for (Component pane : mChildrenComponents) {
+ if (pane instanceof LayoutComponent) {
+ if (index != currentLayoutIndex && index != previousLayoutIndex) {
+ pane.mVisibility = Visibility.GONE;
+ }
+ if (index == currentLayoutIndex && pane.mVisibility != Visibility.VISIBLE) {
+ pane.mVisibility = Visibility.VISIBLE;
+ }
+ index++;
+ }
+ }
+
+ LayoutManager currentLayout = getLayout(measuredLayoutIndex);
+ boolean needsToPaintTransition = inTransition && previousLayoutIndex != measuredLayoutIndex;
+ if (needsToPaintTransition) {
+ // in case we have switched to a new state, during the transition
+ // we might still need to display the previous components that are not part of
+ // the new state (to enable them to run their exit animation)
+
+ LayoutManager previousLayout = getLayout(previousLayoutIndex);
+ int numPreviousComponents = previousLayout.getChildrenComponents().size();
+ if (numPreviousComponents > MAX_CACHE_ELEMENTS) {
+ MAX_CACHE_ELEMENTS *= 2;
+ cacheListElementsId = new int[MAX_CACHE_ELEMENTS];
+ }
+ // Make sure to apply the animation if there...
+ previousLayout.applyAnimationAsNeeded(context);
+
+ // Let's grab all the ids for the components of the previous layout...
+ int idIndex = 0;
+ for (Component c : previousLayout.getChildrenComponents()) {
+ cacheListElementsId[idIndex] = c.getPaintId();
+ idIndex++;
+ }
+ // ...then remove them if they are in the new layout
+ int count = idIndex;
+ for (Component c : currentLayout.getChildrenComponents()) {
+ int id = c.getPaintId();
+ for (int i = 0; i < idIndex; i++) {
+ if (cacheListElementsId[i] == id) {
+ cacheListElementsId[i] = -1;
+ count--;
+ }
+ }
+ }
+ // If we have components not present in the new state, paint them
+ if (count > 0) {
+ context.save();
+ context.translate(previousLayout.getX(), previousLayout.getY());
+ for (Component c : previousLayout.getChildrenComponents()) {
+ int id = c.getPaintId();
+ for (int i = 0; i < idIndex; i++) {
+ if (cacheListElementsId[i] == id) {
+ context.translate(
+ previousLayout.getMarginLeft(), previousLayout.getMarginTop());
+ c.paint(context);
+ context.translate(
+ -currentLayout.getMarginLeft(), -currentLayout.getMarginTop());
+ break;
+ }
+ }
+ }
+ context.restore();
+ }
+
+ // Make sure to apply the animation if there...
+ currentLayout.applyAnimationAsNeeded(context);
+ }
+
+ // We paint all the components and operations of the current layout
+ context.save();
+ context.translate(currentLayout.getX(), currentLayout.getY());
+ for (Operation op : currentLayout.getList()) {
+ if (op instanceof Component && ((Component) op).getAnimationId() != -1) {
+ Component[] stateComponents =
+ statePaintedComponents.get(((Component) op).getAnimationId());
+ Component component = stateComponents[measuredLayoutIndex];
+ if (needsToPaintTransition) {
+ // We might have two components to paint, as in case two different
+ // components share the same id, we'll fade the previous components out
+ // and fade in the new one
+ Component previousComponent = stateComponents[previousLayoutIndex];
+ if (previousComponent != null && component != previousComponent) {
+ context.translate(
+ currentLayout.getMarginLeft(), currentLayout.getMarginTop());
+ previousComponent.paint(context);
+ context.translate(
+ -currentLayout.getMarginLeft(), -currentLayout.getMarginTop());
+ }
+ }
+ context.translate(currentLayout.getMarginLeft(), currentLayout.getMarginTop());
+ component.paint(context);
+ context.translate(-currentLayout.getMarginLeft(), -currentLayout.getMarginTop());
+ } else if (op instanceof PaintOperation) {
+ ((PaintOperation) op).paint(context);
+ }
+ }
+ context.restore();
+
+ if (needsToPaintTransition) {
+ checkEndOfTransition();
+ }
+ }
+
+ public void checkEndOfTransition() {
+ LayoutManager currentLayout = getLayout(measuredLayoutIndex);
+ LayoutManager previousLayout = getLayout(previousLayoutIndex);
+ if (inTransition
+ && currentLayout.mAnimateMeasure == null
+ && previousLayout.mAnimateMeasure == null) {
+ inTransition = false;
+ LayoutManager previous = getLayout(previousLayoutIndex);
+ if (previous != currentLayout && previous.mVisibility != Visibility.GONE) {
+ previous.mVisibility = Visibility.GONE;
+ previous.needsRepaint();
+ }
+ }
+ }
+
+ // override fun onValueChanged(origamiValue: OrigamiValue<out Int>, oldValue: Int?, newValue:
+ // Int) {
+ // if (newValue != currentLayoutIndex) {
+ // previousLayoutIndex = currentLayoutIndex
+ // currentLayoutIndex = newValue
+ // inTransition = true
+ // println("currentLayout index is $currentLayoutIndex")
+ // executeValueSetActions(getLayout(currentLayoutIndex))
+ // invalidateMeasure()
+ // }
+ // }
+
+ // fun executeValueSetActions(layout: LayoutManager) {
+ // // FIXME : quick hack to support ValueSetClickActions, need to make that a little more
+ // // robust!
+ // for (op in layout.list) {
+ // if (op is LayoutComponent) {
+ // for (op2 in op.list) {
+ // if (op2 is OperationsList) {
+ // for (op3 in op2.list) {
+ // if (op3 is ValueSetClickAction<*, *>) {
+ // op3.onClick()
+ // }
+ // }
+ // }
+ // }
+ // }
+ // }
+ // }
+
+ @Override
+ public String toString() {
+ return "STATE_LAYOUT";
+ }
+
+ // companion object {
+ // fun documentation(doc: OrigamiDocumentation) {}
+ // }
+
+ public static void apply(
+ WireBuffer buffer,
+ int componentId,
+ int animationId,
+ int horizontalPositioning,
+ int verticalPositioning,
+ int indexId) {
+ buffer.start(Operations.LAYOUT_STATE);
+ buffer.writeInt(componentId);
+ buffer.writeInt(animationId);
+ buffer.writeInt(horizontalPositioning);
+ buffer.writeInt(verticalPositioning);
+ buffer.writeInt(indexId);
+ }
+
+ public static void read(WireBuffer buffer, List<Operation> operations) {
+ int componentId = buffer.readInt();
+ int animationId = buffer.readInt();
+ buffer.readInt(); // horizontalPositioning
+ buffer.readInt(); // verticalPositioning
+ int indexId = buffer.readInt();
+ operations.add(
+ new StateLayout(null, componentId, animationId, 0f, 0f, 100f, 100f, indexId));
+ }
+}
diff --git a/core/java/com/android/internal/widget/remotecompose/core/operations/layout/managers/TextLayout.java b/core/java/com/android/internal/widget/remotecompose/core/operations/layout/managers/TextLayout.java
index 23705003179c..c1cabcd09c39 100644
--- a/core/java/com/android/internal/widget/remotecompose/core/operations/layout/managers/TextLayout.java
+++ b/core/java/com/android/internal/widget/remotecompose/core/operations/layout/managers/TextLayout.java
@@ -15,8 +15,8 @@
*/
package com.android.internal.widget.remotecompose.core.operations.layout.managers;
-import static com.android.internal.widget.remotecompose.core.documentation.Operation.FLOAT;
-import static com.android.internal.widget.remotecompose.core.documentation.Operation.INT;
+import static com.android.internal.widget.remotecompose.core.documentation.DocumentedOperation.FLOAT;
+import static com.android.internal.widget.remotecompose.core.documentation.DocumentedOperation.INT;
import com.android.internal.widget.remotecompose.core.Operation;
import com.android.internal.widget.remotecompose.core.Operations;
@@ -34,9 +34,7 @@ import com.android.internal.widget.remotecompose.core.operations.utilities.Strin
import java.util.List;
-/**
- * Text component, referencing a text id
- */
+/** Text component, referencing a text id */
public class TextLayout extends LayoutManager implements ComponentStartOperation, VariableSupport {
private static final boolean DEBUG = false;
@@ -86,10 +84,20 @@ public class TextLayout extends LayoutManager implements ComponentStartOperation
needsRepaint();
}
- public TextLayout(Component parent, int componentId, int animationId,
- float x, float y, float width, float height,
- int textId, int color, float fontSize,
- int fontStyle, float fontWeight, int fontFamilyId) {
+ public TextLayout(
+ Component parent,
+ int componentId,
+ int animationId,
+ float x,
+ float y,
+ float width,
+ float height,
+ int textId,
+ int color,
+ float fontSize,
+ int fontStyle,
+ float fontWeight,
+ int fontFamilyId) {
super(parent, componentId, animationId, x, y, width, height);
mTextId = textId;
mColor = color;
@@ -99,11 +107,30 @@ public class TextLayout extends LayoutManager implements ComponentStartOperation
mFontFamilyId = fontFamilyId;
}
- public TextLayout(Component parent, int componentId, int animationId,
- int textId, int color, float fontSize,
- int fontStyle, float fontWeight, int fontFamilyId) {
- this(parent, componentId, animationId, 0, 0, 0, 0,
- textId, color, fontSize, fontStyle, fontWeight, fontFamilyId);
+ public TextLayout(
+ Component parent,
+ int componentId,
+ int animationId,
+ int textId,
+ int color,
+ float fontSize,
+ int fontStyle,
+ float fontWeight,
+ int fontFamilyId) {
+ this(
+ parent,
+ componentId,
+ animationId,
+ 0,
+ 0,
+ 0,
+ 0,
+ textId,
+ color,
+ fontSize,
+ fontStyle,
+ fontWeight,
+ fontFamilyId);
}
public PaintBundle mPaint = new PaintBundle();
@@ -151,26 +178,57 @@ public class TextLayout extends LayoutManager implements ComponentStartOperation
@Override
public String toString() {
- return "TEXT_LAYOUT [" + mComponentId + ":" + mAnimationId + "] (" + mX + ", "
- + mY + " - " + mWidth + " x " + mHeight + ") " + mVisibility;
+ return "TEXT_LAYOUT ["
+ + mComponentId
+ + ":"
+ + mAnimationId
+ + "] ("
+ + mX
+ + ", "
+ + mY
+ + " - "
+ + mWidth
+ + " x "
+ + mHeight
+ + ") "
+ + mVisibility;
}
+ @Override
protected String getSerializedName() {
return "TEXT_LAYOUT";
}
@Override
public void serializeToString(int indent, StringSerializer serializer) {
- serializer.append(indent, getSerializedName() + " [" + mComponentId
- + ":" + mAnimationId + "] = "
- + "[" + mX + ", " + mY + ", " + mWidth + ", " + mHeight + "] "
- + mVisibility + " (" + mTextId + ":\"" + mCachedString + "\")"
- );
+ serializer.append(
+ indent,
+ getSerializedName()
+ + " ["
+ + mComponentId
+ + ":"
+ + mAnimationId
+ + "] = "
+ + "["
+ + mX
+ + ", "
+ + mY
+ + ", "
+ + mWidth
+ + ", "
+ + mHeight
+ + "] "
+ + mVisibility
+ + " ("
+ + mTextId
+ + ":\""
+ + mCachedString
+ + "\")");
}
@Override
- public void computeWrapSize(PaintContext context, float maxWidth, float maxHeight,
- MeasurePass measure, Size size) {
+ public void computeWrapSize(
+ PaintContext context, float maxWidth, float maxHeight, MeasurePass measure, Size size) {
context.savePaint();
mPaint.reset();
mPaint.setTextSize(mFontSize);
@@ -196,9 +254,16 @@ public class TextLayout extends LayoutManager implements ComponentStartOperation
return Operations.LAYOUT_TEXT;
}
- public static void apply(WireBuffer buffer, int componentId, int animationId,
- int textId, int color, float fontSize, int fontStyle,
- float fontWeight, int fontFamilyId) {
+ public static void apply(
+ WireBuffer buffer,
+ int componentId,
+ int animationId,
+ int textId,
+ int color,
+ float fontSize,
+ int fontStyle,
+ float fontWeight,
+ int fontFamilyId) {
buffer.start(id());
buffer.writeInt(componentId);
buffer.writeInt(animationId);
@@ -219,16 +284,27 @@ public class TextLayout extends LayoutManager implements ComponentStartOperation
int fontStyle = buffer.readInt();
float fontWeight = buffer.readFloat();
int fontFamilyId = buffer.readInt();
- operations.add(new TextLayout(null, componentId, animationId, textId, color, fontSize,
- fontStyle, fontWeight, fontFamilyId));
+ operations.add(
+ new TextLayout(
+ null,
+ componentId,
+ animationId,
+ textId,
+ color,
+ fontSize,
+ fontStyle,
+ fontWeight,
+ fontFamilyId));
}
public static void documentation(DocumentationBuilder doc) {
doc.operation("Layout Operations", id(), name())
.description("Text layout implementation.\n\n")
.field(INT, "COMPONENT_ID", "unique id for this component")
- .field(INT, "ANIMATION_ID", "id used to match components,"
- + " for animation purposes")
+ .field(
+ INT,
+ "ANIMATION_ID",
+ "id used to match components," + " for animation purposes")
.field(INT, "COLOR", "text color")
.field(FLOAT, "FONT_SIZE", "font size")
.field(INT, "FONT_STYLE", "font style (0 = normal, 1 = italic)")
@@ -238,8 +314,15 @@ public class TextLayout extends LayoutManager implements ComponentStartOperation
@Override
public void write(WireBuffer buffer) {
- apply(buffer, mComponentId, mAnimationId,
- mTextId, mColor, mFontSize, mFontStyle,
- mFontWeight, mFontFamilyId);
+ apply(
+ buffer,
+ mComponentId,
+ mAnimationId,
+ mTextId,
+ mColor,
+ mFontSize,
+ mFontStyle,
+ mFontWeight,
+ mFontFamilyId);
}
}
diff --git a/core/java/com/android/internal/widget/remotecompose/core/operations/layout/measure/ComponentMeasure.java b/core/java/com/android/internal/widget/remotecompose/core/operations/layout/measure/ComponentMeasure.java
index 8dc10d5b0159..285425f99765 100644
--- a/core/java/com/android/internal/widget/remotecompose/core/operations/layout/measure/ComponentMeasure.java
+++ b/core/java/com/android/internal/widget/remotecompose/core/operations/layout/measure/ComponentMeasure.java
@@ -13,13 +13,11 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
-
package com.android.internal.widget.remotecompose.core.operations.layout.measure;
+
import com.android.internal.widget.remotecompose.core.operations.layout.Component;
-/**
- * Encapsulate the result of a measure pass for a component
- */
+/** Encapsulate the result of a measure pass for a component */
public class ComponentMeasure {
int mId = -1;
float mX;
@@ -31,24 +29,31 @@ public class ComponentMeasure {
public void setX(float value) {
mX = value;
}
+
public void setY(float value) {
mY = value;
}
+
public void setW(float value) {
mW = value;
}
+
public void setH(float value) {
mH = value;
}
+
public float getX() {
return mX;
}
+
public float getY() {
return mY;
}
+
public float getW() {
return mW;
}
+
public float getH() {
return mH;
}
@@ -61,8 +66,8 @@ public class ComponentMeasure {
mVisibility = visibility;
}
- public ComponentMeasure(int id, float x, float y, float w, float h,
- Component.Visibility visibility) {
+ public ComponentMeasure(
+ int id, float x, float y, float w, float h, Component.Visibility visibility) {
this.mId = id;
this.mX = x;
this.mY = y;
@@ -76,8 +81,12 @@ public class ComponentMeasure {
}
public ComponentMeasure(Component component) {
- this(component.getComponentId(), component.getX(), component.getY(),
- component.getWidth(), component.getHeight(),
+ this(
+ component.getComponentId(),
+ component.getX(),
+ component.getY(),
+ component.getWidth(),
+ component.getHeight(),
component.mVisibility);
}
@@ -88,4 +97,8 @@ public class ComponentMeasure {
mH = m.mH;
mVisibility = m.mVisibility;
}
+
+ public boolean same(ComponentMeasure m) {
+ return mX == m.mX && mY == m.mY && mW == m.mW && mH == m.mH && mVisibility == m.mVisibility;
+ }
}
diff --git a/core/java/com/android/internal/widget/remotecompose/core/operations/layout/measure/Measurable.java b/core/java/com/android/internal/widget/remotecompose/core/operations/layout/measure/Measurable.java
index d167d9bf45cb..b48c2d5ae88b 100644
--- a/core/java/com/android/internal/widget/remotecompose/core/operations/layout/measure/Measurable.java
+++ b/core/java/com/android/internal/widget/remotecompose/core/operations/layout/measure/Measurable.java
@@ -13,33 +13,33 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
-
package com.android.internal.widget.remotecompose.core.operations.layout.measure;
import com.android.internal.widget.remotecompose.core.PaintContext;
import com.android.internal.widget.remotecompose.core.RemoteContext;
-/**
- * Interface describing the measure/layout contract for components
- */
+/** Interface describing the measure/layout contract for components */
public interface Measurable {
/**
- * Measure a component and store the result of the measure in the provided MeasurePass.
- * This does not apply the measure to the component.
+ * Measure a component and store the result of the measure in the provided MeasurePass. This
+ * does not apply the measure to the component.
*/
- void measure(PaintContext context, float minWidth, float maxWidth,
- float minHeight, float maxHeight, MeasurePass measure);
+ void measure(
+ PaintContext context,
+ float minWidth,
+ float maxWidth,
+ float minHeight,
+ float maxHeight,
+ MeasurePass measure);
- /**
- * Apply a given measure to the component
- */
+ /** Apply a given measure to the component */
void layout(RemoteContext context, MeasurePass measure);
/**
* Return true if the component needs to be remeasured
+ *
* @return true if need to remeasured, false otherwise
*/
boolean needsMeasure();
-
}
diff --git a/core/java/com/android/internal/widget/remotecompose/core/operations/layout/measure/MeasurePass.java b/core/java/com/android/internal/widget/remotecompose/core/operations/layout/measure/MeasurePass.java
index 6801debb9c28..8d01fea03690 100644
--- a/core/java/com/android/internal/widget/remotecompose/core/operations/layout/measure/MeasurePass.java
+++ b/core/java/com/android/internal/widget/remotecompose/core/operations/layout/measure/MeasurePass.java
@@ -20,8 +20,8 @@ import com.android.internal.widget.remotecompose.core.operations.layout.Componen
import java.util.HashMap;
/**
- * Represents the result of a measure pass on the entire hierarchy
- * TODO: optimize to use a flat array vs the current hashmap
+ * Represents the result of a measure pass on the entire hierarchy TODO: optimize to use a flat
+ * array vs the current hashmap
*/
public class MeasurePass {
HashMap<Integer, ComponentMeasure> mList = new HashMap<>();
@@ -43,8 +43,9 @@ public class MeasurePass {
public ComponentMeasure get(Component c) {
if (!mList.containsKey(c.getComponentId())) {
- ComponentMeasure measure = new ComponentMeasure(c.getComponentId(),
- c.getX(), c.getY(), c.getWidth(), c.getHeight());
+ ComponentMeasure measure =
+ new ComponentMeasure(
+ c.getComponentId(), c.getX(), c.getY(), c.getWidth(), c.getHeight());
mList.put(c.getComponentId(), measure);
return measure;
}
@@ -53,8 +54,8 @@ public class MeasurePass {
public ComponentMeasure get(int id) {
if (!mList.containsKey(id)) {
- ComponentMeasure measure = new ComponentMeasure(id,
- 0f, 0f, 0f, 0f, Component.Visibility.GONE);
+ ComponentMeasure measure =
+ new ComponentMeasure(id, 0f, 0f, 0f, 0f, Component.Visibility.GONE);
mList.put(id, measure);
return measure;
}
diff --git a/core/java/com/android/internal/widget/remotecompose/core/operations/layout/measure/Size.java b/core/java/com/android/internal/widget/remotecompose/core/operations/layout/measure/Size.java
index b11d8e82e7d1..53f4a71ccd6e 100644
--- a/core/java/com/android/internal/widget/remotecompose/core/operations/layout/measure/Size.java
+++ b/core/java/com/android/internal/widget/remotecompose/core/operations/layout/measure/Size.java
@@ -15,12 +15,11 @@
*/
package com.android.internal.widget.remotecompose.core.operations.layout.measure;
-/**
- * Basic data class representing a component size, used during layout computations.
- */
+/** Basic data class representing a component size, used during layout computations. */
public class Size {
float mWidth;
float mHeight;
+
public Size(float width, float height) {
this.mWidth = width;
this.mHeight = height;
diff --git a/core/java/com/android/internal/widget/remotecompose/core/operations/layout/modifiers/BackgroundModifierOperation.java b/core/java/com/android/internal/widget/remotecompose/core/operations/layout/modifiers/BackgroundModifierOperation.java
index f3e6a8e0aa22..64e40f7c591c 100644
--- a/core/java/com/android/internal/widget/remotecompose/core/operations/layout/modifiers/BackgroundModifierOperation.java
+++ b/core/java/com/android/internal/widget/remotecompose/core/operations/layout/modifiers/BackgroundModifierOperation.java
@@ -15,7 +15,7 @@
*/
package com.android.internal.widget.remotecompose.core.operations.layout.modifiers;
-import static com.android.internal.widget.remotecompose.core.documentation.Operation.FLOAT;
+import static com.android.internal.widget.remotecompose.core.documentation.DocumentedOperation.FLOAT;
import com.android.internal.widget.remotecompose.core.Operation;
import com.android.internal.widget.remotecompose.core.Operations;
@@ -28,9 +28,7 @@ import com.android.internal.widget.remotecompose.core.operations.utilities.Strin
import java.util.List;
-/**
- * Component size-aware background draw
- */
+/** Component size-aware background draw */
public class BackgroundModifierOperation extends DecoratorModifierOperation {
private static final int OP_CODE = Operations.MODIFIER_BACKGROUND;
private static final String CLASS_NAME = "BackgroundModifierOperation";
@@ -46,9 +44,16 @@ public class BackgroundModifierOperation extends DecoratorModifierOperation {
public PaintBundle mPaint = new PaintBundle();
- public BackgroundModifierOperation(float x, float y, float width, float height,
- float r, float g, float b, float a,
- int shapeType) {
+ public BackgroundModifierOperation(
+ float x,
+ float y,
+ float width,
+ float height,
+ float r,
+ float g,
+ float b,
+ float a,
+ int shapeType) {
this.mX = x;
this.mY = y;
this.mWidth = width;
@@ -67,10 +72,27 @@ public class BackgroundModifierOperation extends DecoratorModifierOperation {
@Override
public void serializeToString(int indent, StringSerializer serializer) {
- serializer.append(indent, "BACKGROUND = [" + mX + ", "
- + mY + ", " + mWidth + ", " + mHeight
- + "] color [" + mR + ", " + mG + ", " + mB + ", " + mA
- + "] shape [" + mShapeType + "]");
+ serializer.append(
+ indent,
+ "BACKGROUND = ["
+ + mX
+ + ", "
+ + mY
+ + ", "
+ + mWidth
+ + ", "
+ + mHeight
+ + "] color ["
+ + mR
+ + ", "
+ + mG
+ + ", "
+ + mB
+ + ", "
+ + mA
+ + "] shape ["
+ + mShapeType
+ + "]");
}
@Override
@@ -92,8 +114,17 @@ public class BackgroundModifierOperation extends DecoratorModifierOperation {
return OP_CODE;
}
- public static void apply(WireBuffer buffer, float x, float y, float width, float height,
- float r, float g, float b, float a, int shapeType) {
+ public static void apply(
+ WireBuffer buffer,
+ float x,
+ float y,
+ float width,
+ float height,
+ float r,
+ float g,
+ float b,
+ float a,
+ int shapeType) {
buffer.start(OP_CODE);
buffer.writeFloat(x);
buffer.writeFloat(y);
@@ -118,11 +149,9 @@ public class BackgroundModifierOperation extends DecoratorModifierOperation {
float a = buffer.readFloat();
// shape type
int shapeType = buffer.readInt();
- operations.add(new BackgroundModifierOperation(x, y, width, height,
- r, g, b, a, shapeType));
+ operations.add(new BackgroundModifierOperation(x, y, width, height, r, g, b, a, shapeType));
}
-
@Override
public void paint(PaintContext context) {
context.savePaint();
@@ -133,16 +162,13 @@ public class BackgroundModifierOperation extends DecoratorModifierOperation {
if (mShapeType == ShapeType.RECTANGLE) {
context.drawRect(0f, 0f, mWidth, mHeight);
} else if (mShapeType == ShapeType.CIRCLE) {
- context.drawCircle(mWidth / 2f, mHeight / 2f,
- Math.min(mWidth, mHeight) / 2f);
+ context.drawCircle(mWidth / 2f, mHeight / 2f, Math.min(mWidth, mHeight) / 2f);
}
context.restorePaint();
}
public static void documentation(DocumentationBuilder doc) {
- doc.operation("Modifier Operations",
- OP_CODE,
- CLASS_NAME)
+ doc.operation("Modifier Operations", OP_CODE, CLASS_NAME)
.description("define the Background Modifier")
.field(FLOAT, "x", "")
.field(FLOAT, "y", "")
diff --git a/core/java/com/android/internal/widget/remotecompose/core/operations/layout/modifiers/BorderModifierOperation.java b/core/java/com/android/internal/widget/remotecompose/core/operations/layout/modifiers/BorderModifierOperation.java
index 4c83ec4e627e..92c0a733d8a1 100644
--- a/core/java/com/android/internal/widget/remotecompose/core/operations/layout/modifiers/BorderModifierOperation.java
+++ b/core/java/com/android/internal/widget/remotecompose/core/operations/layout/modifiers/BorderModifierOperation.java
@@ -15,7 +15,7 @@
*/
package com.android.internal.widget.remotecompose.core.operations.layout.modifiers;
-import static com.android.internal.widget.remotecompose.core.documentation.Operation.FLOAT;
+import static com.android.internal.widget.remotecompose.core.documentation.DocumentedOperation.FLOAT;
import com.android.internal.widget.remotecompose.core.Operation;
import com.android.internal.widget.remotecompose.core.Operations;
@@ -28,9 +28,7 @@ import com.android.internal.widget.remotecompose.core.operations.utilities.Strin
import java.util.List;
-/**
- * Component size-aware border draw
- */
+/** Component size-aware border draw */
public class BorderModifierOperation extends DecoratorModifierOperation {
private static final int OP_CODE = Operations.MODIFIER_BORDER;
public static final String CLASS_NAME = "BorderModifierOperation";
@@ -49,9 +47,18 @@ public class BorderModifierOperation extends DecoratorModifierOperation {
public PaintBundle paint = new PaintBundle();
- public BorderModifierOperation(float x, float y, float width, float height,
- float borderWidth, float roundedCorner,
- float r, float g, float b, float a, int shapeType) {
+ public BorderModifierOperation(
+ float x,
+ float y,
+ float width,
+ float height,
+ float borderWidth,
+ float roundedCorner,
+ float r,
+ float g,
+ float b,
+ float a,
+ int shapeType) {
this.mX = x;
this.mY = y;
this.mWidth = width;
@@ -67,17 +74,51 @@ public class BorderModifierOperation extends DecoratorModifierOperation {
@Override
public void serializeToString(int indent, StringSerializer serializer) {
- serializer.append(indent, "BORDER = [" + mX + ", " + mY + ", "
- + mWidth + ", " + mHeight + "] "
- + "color [" + mR + ", " + mG + ", " + mB + ", " + mA + "] "
- + "border [" + mBorderWidth + ", " + mRoundedCorner + "] "
- + "shape [" + mShapeType + "]");
+ serializer.append(
+ indent,
+ "BORDER = ["
+ + mX
+ + ", "
+ + mY
+ + ", "
+ + mWidth
+ + ", "
+ + mHeight
+ + "] "
+ + "color ["
+ + mR
+ + ", "
+ + mG
+ + ", "
+ + mB
+ + ", "
+ + mA
+ + "] "
+ + "border ["
+ + mBorderWidth
+ + ", "
+ + mRoundedCorner
+ + "] "
+ + "shape ["
+ + mShapeType
+ + "]");
}
@Override
public void write(WireBuffer buffer) {
- apply(buffer, mX, mY, mWidth, mHeight, mBorderWidth, mRoundedCorner,
- mR, mG, mB, mA, mShapeType);
+ apply(
+ buffer,
+ mX,
+ mY,
+ mWidth,
+ mHeight,
+ mBorderWidth,
+ mRoundedCorner,
+ mR,
+ mG,
+ mB,
+ mA,
+ mShapeType);
}
@Override
@@ -88,12 +129,29 @@ public class BorderModifierOperation extends DecoratorModifierOperation {
@Override
public String toString() {
- return "BorderModifierOperation(" + mX + "," + mY + " - " + mWidth + " x " + mHeight + ") "
- + "borderWidth(" + mBorderWidth + ") "
- + "color(" + mR + "," + mG + "," + mB + "," + mA + ")";
+ return "BorderModifierOperation("
+ + mX
+ + ","
+ + mY
+ + " - "
+ + mWidth
+ + " x "
+ + mHeight
+ + ") "
+ + "borderWidth("
+ + mBorderWidth
+ + ") "
+ + "color("
+ + mR
+ + ","
+ + mG
+ + ","
+ + mB
+ + ","
+ + mA
+ + ")";
}
-
public static String name() {
return CLASS_NAME;
}
@@ -102,10 +160,19 @@ public class BorderModifierOperation extends DecoratorModifierOperation {
return OP_CODE;
}
- public static void apply(WireBuffer buffer, float x, float y, float width, float height,
- float borderWidth, float roundedCorner,
- float r, float g, float b, float a,
- int shapeType) {
+ public static void apply(
+ WireBuffer buffer,
+ float x,
+ float y,
+ float width,
+ float height,
+ float borderWidth,
+ float roundedCorner,
+ float r,
+ float g,
+ float b,
+ float a,
+ int shapeType) {
buffer.start(OP_CODE);
buffer.writeFloat(x);
buffer.writeFloat(y);
@@ -134,11 +201,10 @@ public class BorderModifierOperation extends DecoratorModifierOperation {
float a = buffer.readFloat();
// shape type
int shapeType = buffer.readInt();
- operations.add(new BorderModifierOperation(x, y, width, height, bw,
- rc, r, g, b, a, shapeType));
+ operations.add(
+ new BorderModifierOperation(x, y, width, height, bw, rc, r, g, b, a, shapeType));
}
-
@Override
public void paint(PaintContext context) {
context.savePaint();
@@ -160,9 +226,7 @@ public class BorderModifierOperation extends DecoratorModifierOperation {
}
public static void documentation(DocumentationBuilder doc) {
- doc.operation("Modifier Operations",
- OP_CODE,
- CLASS_NAME)
+ doc.operation("Modifier Operations", OP_CODE, CLASS_NAME)
.description("define the Border Modifier")
.field(FLOAT, "x", "")
.field(FLOAT, "y", "")
diff --git a/core/java/com/android/internal/widget/remotecompose/core/operations/layout/modifiers/ClipRectModifierOperation.java b/core/java/com/android/internal/widget/remotecompose/core/operations/layout/modifiers/ClipRectModifierOperation.java
index 7cb7925692ab..0d8aeaa2f06a 100644
--- a/core/java/com/android/internal/widget/remotecompose/core/operations/layout/modifiers/ClipRectModifierOperation.java
+++ b/core/java/com/android/internal/widget/remotecompose/core/operations/layout/modifiers/ClipRectModifierOperation.java
@@ -27,9 +27,7 @@ import com.android.internal.widget.remotecompose.core.operations.utilities.Strin
import java.util.List;
-/**
- * Support modifier clip with a rectangle
- */
+/** Support modifier clip with a rectangle */
public class ClipRectModifierOperation extends DecoratorModifierOperation {
public static final String CLASS_NAME = "ClipRectModifierOperation";
private static final int OP_CODE = Operations.MODIFIER_CLIP_RECT;
@@ -48,15 +46,14 @@ public class ClipRectModifierOperation extends DecoratorModifierOperation {
}
@Override
- public void onClick(RemoteContext context, CoreDocument document,
- Component component, float x, float y) {
+ public void onClick(
+ RemoteContext context, CoreDocument document, Component component, float x, float y) {
// nothing
}
@Override
public void serializeToString(int indent, StringSerializer serializer) {
- serializer.append(
- indent, "CLIP_RECT = [" + mWidth + ", " + mHeight + "]");
+ serializer.append(indent, "CLIP_RECT = [" + mWidth + ", " + mHeight + "]");
}
@Override
@@ -68,7 +65,6 @@ public class ClipRectModifierOperation extends DecoratorModifierOperation {
return CLASS_NAME;
}
-
public static int id() {
return OP_CODE;
}
@@ -77,16 +73,12 @@ public class ClipRectModifierOperation extends DecoratorModifierOperation {
buffer.start(OP_CODE);
}
-
public static void read(WireBuffer buffer, List<Operation> operations) {
operations.add(new ClipRectModifierOperation());
}
-
public static void documentation(DocumentationBuilder doc) {
- doc.operation("Canvas Operations",
- OP_CODE,
- CLASS_NAME)
+ doc.operation("Canvas Operations", OP_CODE, CLASS_NAME)
.description("Draw the specified round-rect");
}
}
diff --git a/core/java/com/android/internal/widget/remotecompose/core/operations/layout/modifiers/ComponentModifiers.java b/core/java/com/android/internal/widget/remotecompose/core/operations/layout/modifiers/ComponentModifiers.java
index f55c94126001..95786a8b62b0 100644
--- a/core/java/com/android/internal/widget/remotecompose/core/operations/layout/modifiers/ComponentModifiers.java
+++ b/core/java/com/android/internal/widget/remotecompose/core/operations/layout/modifiers/ComponentModifiers.java
@@ -29,9 +29,7 @@ import com.android.internal.widget.remotecompose.core.operations.utilities.Strin
import java.util.ArrayList;
-/**
- * Maintain a list of modifiers
- */
+/** Maintain a list of modifiers */
public class ComponentModifiers extends PaintOperation implements DecoratorComponent {
ArrayList<ModifierOperation> mList = new ArrayList<>();
@@ -49,7 +47,7 @@ public class ComponentModifiers extends PaintOperation implements DecoratorCompo
@Override
public String toString() {
- String str = "ComponentModifiers \n";
+ String str = "ComponentModifiers \n";
for (ModifierOperation modifierOperation : mList) {
str += " " + modifierOperation.toString() + "\n";
}
@@ -125,8 +123,9 @@ public class ComponentModifiers extends PaintOperation implements DecoratorCompo
mList.addAll(operations);
}
- public void onClick(RemoteContext context, CoreDocument document,
- Component component, float x, float y) {
+ @Override
+ public void onClick(
+ RemoteContext context, CoreDocument document, Component component, float x, float y) {
for (ModifierOperation op : mList) {
if (op instanceof DecoratorComponent) {
((DecoratorComponent) op).onClick(context, document, component, x, y);
diff --git a/core/java/com/android/internal/widget/remotecompose/core/operations/layout/modifiers/ComponentVisibilityOperation.java b/core/java/com/android/internal/widget/remotecompose/core/operations/layout/modifiers/ComponentVisibilityOperation.java
index 9c190730706a..312d016029fb 100644
--- a/core/java/com/android/internal/widget/remotecompose/core/operations/layout/modifiers/ComponentVisibilityOperation.java
+++ b/core/java/com/android/internal/widget/remotecompose/core/operations/layout/modifiers/ComponentVisibilityOperation.java
@@ -15,7 +15,7 @@
*/
package com.android.internal.widget.remotecompose.core.operations.layout.modifiers;
-import static com.android.internal.widget.remotecompose.core.documentation.Operation.INT;
+import static com.android.internal.widget.remotecompose.core.documentation.DocumentedOperation.INT;
import com.android.internal.widget.remotecompose.core.CoreDocument;
import com.android.internal.widget.remotecompose.core.Operation;
@@ -31,11 +31,9 @@ import com.android.internal.widget.remotecompose.core.operations.utilities.Strin
import java.util.List;
-/**
- * Allows setting visibility on a component
- */
-public class ComponentVisibilityOperation implements ModifierOperation,
- VariableSupport, DecoratorComponent {
+/** Allows setting visibility on a component */
+public class ComponentVisibilityOperation
+ implements ModifierOperation, VariableSupport, DecoratorComponent {
private static final int OP_CODE = Operations.MODIFIER_VISIBILITY;
int mVisibilityId;
@@ -61,19 +59,15 @@ public class ComponentVisibilityOperation implements ModifierOperation,
}
@Override
- public void apply(RemoteContext context) {
- }
+ public void apply(RemoteContext context) {}
@Override
public String deepToString(String indent) {
return (indent != null ? indent : "") + toString();
}
-
@Override
- public void write(WireBuffer buffer) {
-
- }
+ public void write(WireBuffer buffer) {}
public static void apply(WireBuffer buffer, int valueId) {
buffer.start(OP_CODE);
@@ -87,8 +81,9 @@ public class ComponentVisibilityOperation implements ModifierOperation,
public static void documentation(DocumentationBuilder doc) {
doc.operation("Layout Operations", OP_CODE, "ComponentVisibility")
- .description("This operation allows setting a component"
- + "visibility from a provided value")
+ .description(
+ "This operation allows setting a component"
+ + "visibility from a provided value")
.field(INT, "VALUE_ID", "Value ID representing the visibility");
}
@@ -119,13 +114,9 @@ public class ComponentVisibilityOperation implements ModifierOperation,
}
@Override
- public void layout(RemoteContext context, float width, float height) {
-
- }
+ public void layout(RemoteContext context, float width, float height) {}
@Override
- public void onClick(RemoteContext context, CoreDocument document,
- Component component, float x, float y) {
-
- }
+ public void onClick(
+ RemoteContext context, CoreDocument document, Component component, float x, float y) {}
}
diff --git a/core/java/com/android/internal/widget/remotecompose/core/operations/layout/modifiers/DecoratorModifierOperation.java b/core/java/com/android/internal/widget/remotecompose/core/operations/layout/modifiers/DecoratorModifierOperation.java
index 70a572845c23..41e18cbafbe6 100644
--- a/core/java/com/android/internal/widget/remotecompose/core/operations/layout/modifiers/DecoratorModifierOperation.java
+++ b/core/java/com/android/internal/widget/remotecompose/core/operations/layout/modifiers/DecoratorModifierOperation.java
@@ -22,15 +22,15 @@ import com.android.internal.widget.remotecompose.core.operations.layout.Componen
import com.android.internal.widget.remotecompose.core.operations.layout.DecoratorComponent;
/**
- * Represents a decorator modifier (lightweight component), ie a modifier
- * that impacts the visual output (background, border...)
+ * Represents a decorator modifier (lightweight component), ie a modifier that impacts the visual
+ * output (background, border...)
*/
public abstract class DecoratorModifierOperation extends PaintOperation
implements ModifierOperation, DecoratorComponent {
@Override
- public void onClick(RemoteContext context, CoreDocument document,
- Component component, float x, float y) {
+ public void onClick(
+ RemoteContext context, CoreDocument document, Component component, float x, float y) {
// nothing
}
}
diff --git a/core/java/com/android/internal/widget/remotecompose/core/operations/layout/modifiers/DimensionModifierOperation.java b/core/java/com/android/internal/widget/remotecompose/core/operations/layout/modifiers/DimensionModifierOperation.java
index f085ffb6c73b..408bebcfb7d9 100644
--- a/core/java/com/android/internal/widget/remotecompose/core/operations/layout/modifiers/DimensionModifierOperation.java
+++ b/core/java/com/android/internal/widget/remotecompose/core/operations/layout/modifiers/DimensionModifierOperation.java
@@ -20,13 +20,16 @@ import com.android.internal.widget.remotecompose.core.VariableSupport;
import com.android.internal.widget.remotecompose.core.operations.Utils;
import com.android.internal.widget.remotecompose.core.operations.utilities.StringSerializer;
-/**
- * Base class for dimension modifiers
- */
+/** Base class for dimension modifiers */
public abstract class DimensionModifierOperation implements ModifierOperation, VariableSupport {
public enum Type {
- EXACT, FILL, WRAP, WEIGHT, INTRINSIC_MIN, INTRINSIC_MAX;
+ EXACT,
+ FILL,
+ WRAP,
+ WEIGHT,
+ INTRINSIC_MIN,
+ INTRINSIC_MAX;
static Type fromInt(int value) {
switch (value) {
@@ -67,10 +70,8 @@ public abstract class DimensionModifierOperation implements ModifierOperation, V
@Override
public void updateVariables(RemoteContext context) {
if (mType == Type.EXACT) {
- mOutValue = (Float.isNaN(mValue))
- ? context.getFloat(Utils.idFromNan(mValue)) : mValue;
+ mOutValue = Float.isNaN(mValue) ? context.getFloat(Utils.idFromNan(mValue)) : mValue;
}
-
}
@Override
@@ -80,10 +81,8 @@ public abstract class DimensionModifierOperation implements ModifierOperation, V
context.listensTo(Utils.idFromNan(mValue), this);
}
}
-
}
-
public boolean hasWeight() {
return mType == Type.WEIGHT;
}
@@ -108,7 +107,6 @@ public abstract class DimensionModifierOperation implements ModifierOperation, V
mOutValue = mValue = value;
}
-
public String serializedName() {
return "DIMENSION";
}
@@ -121,8 +119,7 @@ public abstract class DimensionModifierOperation implements ModifierOperation, V
}
@Override
- public void apply(RemoteContext context) {
- }
+ public void apply(RemoteContext context) {}
@Override
public String deepToString(String indent) {
diff --git a/core/java/com/android/internal/widget/remotecompose/core/operations/layout/modifiers/HeightModifierOperation.java b/core/java/com/android/internal/widget/remotecompose/core/operations/layout/modifiers/HeightModifierOperation.java
index a0f576a412f8..d3613f844981 100644
--- a/core/java/com/android/internal/widget/remotecompose/core/operations/layout/modifiers/HeightModifierOperation.java
+++ b/core/java/com/android/internal/widget/remotecompose/core/operations/layout/modifiers/HeightModifierOperation.java
@@ -15,8 +15,8 @@
*/
package com.android.internal.widget.remotecompose.core.operations.layout.modifiers;
-import static com.android.internal.widget.remotecompose.core.documentation.Operation.FLOAT;
-import static com.android.internal.widget.remotecompose.core.documentation.Operation.INT;
+import static com.android.internal.widget.remotecompose.core.documentation.DocumentedOperation.FLOAT;
+import static com.android.internal.widget.remotecompose.core.documentation.DocumentedOperation.INT;
import com.android.internal.widget.remotecompose.core.Operation;
import com.android.internal.widget.remotecompose.core.Operations;
@@ -25,9 +25,7 @@ import com.android.internal.widget.remotecompose.core.documentation.Documentatio
import java.util.List;
-/**
- * Set the height dimension on a component
- */
+/** Set the height dimension on a component */
public class HeightModifierOperation extends DimensionModifierOperation {
private static final int OP_CODE = Operations.MODIFIER_HEIGHT;
public static final String CLASS_NAME = "HeightModifierOperation";
@@ -81,9 +79,7 @@ public class HeightModifierOperation extends DimensionModifierOperation {
}
public static void documentation(DocumentationBuilder doc) {
- doc.operation("Modifier Operations",
- OP_CODE,
- CLASS_NAME)
+ doc.operation("Modifier Operations", OP_CODE, CLASS_NAME)
.description("define the animation")
.field(INT, "type", "")
.field(FLOAT, "value", "");
diff --git a/core/java/com/android/internal/widget/remotecompose/core/operations/layout/modifiers/HostActionOperation.java b/core/java/com/android/internal/widget/remotecompose/core/operations/layout/modifiers/HostActionOperation.java
index d405b2b2a6a7..ac42470a6f8f 100644
--- a/core/java/com/android/internal/widget/remotecompose/core/operations/layout/modifiers/HostActionOperation.java
+++ b/core/java/com/android/internal/widget/remotecompose/core/operations/layout/modifiers/HostActionOperation.java
@@ -15,7 +15,7 @@
*/
package com.android.internal.widget.remotecompose.core.operations.layout.modifiers;
-import static com.android.internal.widget.remotecompose.core.documentation.Operation.INT;
+import static com.android.internal.widget.remotecompose.core.documentation.DocumentedOperation.INT;
import com.android.internal.widget.remotecompose.core.CoreDocument;
import com.android.internal.widget.remotecompose.core.Operation;
@@ -29,9 +29,7 @@ import com.android.internal.widget.remotecompose.core.operations.utilities.Strin
import java.util.List;
-/**
- * Capture a host action information. This can be triggered on eg. a click.
- */
+/** Capture a host action information. This can be triggered on eg. a click. */
public class HostActionOperation implements ActionOperation {
private static final int OP_CODE = Operations.HOST_ACTION;
@@ -60,23 +58,19 @@ public class HostActionOperation implements ActionOperation {
}
@Override
- public void apply(RemoteContext context) {
- }
+ public void apply(RemoteContext context) {}
@Override
public String deepToString(String indent) {
return (indent != null ? indent : "") + toString();
}
-
@Override
- public void write(WireBuffer buffer) {
-
- }
+ public void write(WireBuffer buffer) {}
@Override
- public void runAction(RemoteContext context, CoreDocument document,
- Component component, float x, float y) {
+ public void runAction(
+ RemoteContext context, CoreDocument document, Component component, float x, float y) {
context.runAction(mActionId, "");
}
@@ -95,5 +89,4 @@ public class HostActionOperation implements ActionOperation {
.description("Host action. This operation represents a host action")
.field(INT, "ACTION_ID", "Host Action ID");
}
-
}
diff --git a/core/java/com/android/internal/widget/remotecompose/core/operations/layout/modifiers/HostNamedActionOperation.java b/core/java/com/android/internal/widget/remotecompose/core/operations/layout/modifiers/HostNamedActionOperation.java
index 35f202bd6ed3..b674a582fc14 100644
--- a/core/java/com/android/internal/widget/remotecompose/core/operations/layout/modifiers/HostNamedActionOperation.java
+++ b/core/java/com/android/internal/widget/remotecompose/core/operations/layout/modifiers/HostNamedActionOperation.java
@@ -15,7 +15,7 @@
*/
package com.android.internal.widget.remotecompose.core.operations.layout.modifiers;
-import static com.android.internal.widget.remotecompose.core.documentation.Operation.INT;
+import static com.android.internal.widget.remotecompose.core.documentation.DocumentedOperation.INT;
import com.android.internal.widget.remotecompose.core.CoreDocument;
import com.android.internal.widget.remotecompose.core.Operation;
@@ -29,9 +29,7 @@ import com.android.internal.widget.remotecompose.core.operations.utilities.Strin
import java.util.List;
-/**
- * Capture a host action information. This can be triggered on eg. a click.
- */
+/** Capture a host action information. This can be triggered on eg. a click. */
public class HostNamedActionOperation implements ActionOperation {
private static final int OP_CODE = Operations.HOST_NAMED_ACTION;
@@ -56,23 +54,19 @@ public class HostNamedActionOperation implements ActionOperation {
}
@Override
- public void apply(RemoteContext context) {
- }
+ public void apply(RemoteContext context) {}
@Override
public String deepToString(String indent) {
return (indent != null ? indent : "") + toString();
}
-
@Override
- public void write(WireBuffer buffer) {
-
- }
+ public void write(WireBuffer buffer) {}
@Override
- public void runAction(RemoteContext context, CoreDocument document,
- Component component, float x, float y) {
+ public void runAction(
+ RemoteContext context, CoreDocument document, Component component, float x, float y) {
context.runNamedAction(mTextId);
}
@@ -91,5 +85,4 @@ public class HostNamedActionOperation implements ActionOperation {
.description("Host Named action. This operation represents a host action")
.field(INT, "TEXT_ID", "Named Host Action Text ID");
}
-
}
diff --git a/core/java/com/android/internal/widget/remotecompose/core/operations/layout/modifiers/ModifierOperation.java b/core/java/com/android/internal/widget/remotecompose/core/operations/layout/modifiers/ModifierOperation.java
index 5299719f674f..50f098e25f6a 100644
--- a/core/java/com/android/internal/widget/remotecompose/core/operations/layout/modifiers/ModifierOperation.java
+++ b/core/java/com/android/internal/widget/remotecompose/core/operations/layout/modifiers/ModifierOperation.java
@@ -18,9 +18,7 @@ package com.android.internal.widget.remotecompose.core.operations.layout.modifie
import com.android.internal.widget.remotecompose.core.Operation;
import com.android.internal.widget.remotecompose.core.operations.utilities.StringSerializer;
-/**
- * Represents a modifier
- */
+/** Represents a modifier */
public interface ModifierOperation extends Operation {
void serializeToString(int indent, StringSerializer serializer);
}
diff --git a/core/java/com/android/internal/widget/remotecompose/core/operations/layout/modifiers/PaddingModifierOperation.java b/core/java/com/android/internal/widget/remotecompose/core/operations/layout/modifiers/PaddingModifierOperation.java
index 668db3b71027..e0ec1a60ef7e 100644
--- a/core/java/com/android/internal/widget/remotecompose/core/operations/layout/modifiers/PaddingModifierOperation.java
+++ b/core/java/com/android/internal/widget/remotecompose/core/operations/layout/modifiers/PaddingModifierOperation.java
@@ -15,7 +15,7 @@
*/
package com.android.internal.widget.remotecompose.core.operations.layout.modifiers;
-import static com.android.internal.widget.remotecompose.core.documentation.Operation.FLOAT;
+import static com.android.internal.widget.remotecompose.core.documentation.DocumentedOperation.FLOAT;
import com.android.internal.widget.remotecompose.core.Operation;
import com.android.internal.widget.remotecompose.core.Operations;
@@ -27,8 +27,8 @@ import com.android.internal.widget.remotecompose.core.operations.utilities.Strin
import java.util.List;
/**
- * Represents a padding modifier.
- * Padding modifiers can be chained and will impact following modifiers.
+ * Represents a padding modifier. Padding modifiers can be chained and will impact following
+ * modifiers.
*/
public class PaddingModifierOperation implements ModifierOperation {
private static final int OP_CODE = Operations.MODIFIER_PADDING;
@@ -84,13 +84,12 @@ public class PaddingModifierOperation implements ModifierOperation {
@Override
public void serializeToString(int indent, StringSerializer serializer) {
- serializer.append(indent, "PADDING = [" + mLeft + ", " + mTop + ", "
- + mRight + ", " + mBottom + "]");
+ serializer.append(
+ indent, "PADDING = [" + mLeft + ", " + mTop + ", " + mRight + ", " + mBottom + "]");
}
@Override
- public void apply(RemoteContext context) {
- }
+ public void apply(RemoteContext context) {}
@Override
public String deepToString(String indent) {
@@ -99,8 +98,15 @@ public class PaddingModifierOperation implements ModifierOperation {
@Override
public String toString() {
- return "PaddingModifierOperation(" + mLeft + ", " + mTop
- + ", " + mRight + ", " + mBottom + ")";
+ return "PaddingModifierOperation("
+ + mLeft
+ + ", "
+ + mTop
+ + ", "
+ + mRight
+ + ", "
+ + mBottom
+ + ")";
}
public static String name() {
@@ -111,8 +117,7 @@ public class PaddingModifierOperation implements ModifierOperation {
return Operations.MODIFIER_PADDING;
}
- public static void apply(WireBuffer buffer,
- float left, float top, float right, float bottom) {
+ public static void apply(WireBuffer buffer, float left, float top, float right, float bottom) {
buffer.start(Operations.MODIFIER_PADDING);
buffer.writeFloat(left);
buffer.writeFloat(top);
@@ -129,9 +134,7 @@ public class PaddingModifierOperation implements ModifierOperation {
}
public static void documentation(DocumentationBuilder doc) {
- doc.operation("Modifier Operations",
- OP_CODE,
- CLASS_NAME)
+ doc.operation("Modifier Operations", OP_CODE, CLASS_NAME)
.description("define the Padding Modifier")
.field(FLOAT, "left", "")
.field(FLOAT, "top", "")
diff --git a/core/java/com/android/internal/widget/remotecompose/core/operations/layout/modifiers/RoundedClipRectModifierOperation.java b/core/java/com/android/internal/widget/remotecompose/core/operations/layout/modifiers/RoundedClipRectModifierOperation.java
index 9b662bf5eca7..dc95fe7774aa 100644
--- a/core/java/com/android/internal/widget/remotecompose/core/operations/layout/modifiers/RoundedClipRectModifierOperation.java
+++ b/core/java/com/android/internal/widget/remotecompose/core/operations/layout/modifiers/RoundedClipRectModifierOperation.java
@@ -15,7 +15,7 @@
*/
package com.android.internal.widget.remotecompose.core.operations.layout.modifiers;
-import static com.android.internal.widget.remotecompose.core.documentation.Operation.FLOAT;
+import static com.android.internal.widget.remotecompose.core.documentation.DocumentedOperation.FLOAT;
import com.android.internal.widget.remotecompose.core.CoreDocument;
import com.android.internal.widget.remotecompose.core.Operation;
@@ -31,9 +31,7 @@ import com.android.internal.widget.remotecompose.core.operations.utilities.Strin
import java.util.List;
-/**
- * Support clip with a rounded rectangle
- */
+/** Support clip with a rounded rectangle */
public class RoundedClipRectModifierOperation extends DrawBase4
implements ModifierOperation, DecoratorComponent {
public static final int OP_CODE = Operations.MODIFIER_ROUNDED_CLIP_RECT;
@@ -52,44 +50,41 @@ public class RoundedClipRectModifierOperation extends DrawBase4
return CLASS_NAME;
}
-
- protected void write(WireBuffer buffer,
- float v1,
- float v2,
- float v3,
- float v4) {
+ @Override
+ protected void write(WireBuffer buffer, float v1, float v2, float v3, float v4) {
apply(buffer, v1, v2, v3, v4);
}
public static void documentation(DocumentationBuilder doc) {
- doc.operation("Modifier Operations",
- id(),
- "RoundedClipRectModifierOperation")
+ doc.operation("Modifier Operations", id(), "RoundedClipRectModifierOperation")
.description("clip with rectangle")
- .field(FLOAT, "topStart",
+ .field(
+ FLOAT,
+ "topStart",
"The topStart radius of the rectangle to "
+ "intersect with the current clip")
- .field(FLOAT, "topEnd",
+ .field(
+ FLOAT,
+ "topEnd",
"The topEnd radius of the rectangle to "
+ "intersect with the current clip")
- .field(FLOAT, "bottomStart",
+ .field(
+ FLOAT,
+ "bottomStart",
"The bottomStart radius of the rectangle to "
+ "intersect with the current clip")
- .field(FLOAT, "bottomEnd",
+ .field(
+ FLOAT,
+ "bottomEnd",
"The bottomEnd radius of the rectangle to "
+ "intersect with the current clip");
}
-
float mWidth;
float mHeight;
-
public RoundedClipRectModifierOperation(
- float topStart,
- float topEnd,
- float bottomStart,
- float bottomEnd) {
+ float topStart, float topEnd, float bottomStart, float bottomEnd) {
super(topStart, topEnd, bottomStart, bottomEnd);
mName = CLASS_NAME;
}
@@ -106,33 +101,41 @@ public class RoundedClipRectModifierOperation extends DrawBase4
}
@Override
- public void onClick(RemoteContext context, CoreDocument document,
- Component component, float x, float y) {
+ public void onClick(
+ RemoteContext context, CoreDocument document, Component component, float x, float y) {
// nothing
}
@Override
public void serializeToString(int indent, StringSerializer serializer) {
serializer.append(
- indent, "ROUNDED_CLIP_RECT = [" + mWidth + ", " + mHeight
- + ", " + mX1 + ", " + mY1
- + ", " + mX2 + ", " + mY2 + "]");
+ indent,
+ "ROUNDED_CLIP_RECT = ["
+ + mWidth
+ + ", "
+ + mHeight
+ + ", "
+ + mX1
+ + ", "
+ + mY1
+ + ", "
+ + mX2
+ + ", "
+ + mY2
+ + "]");
}
/**
* Writes out the rounded rect clip to the buffer
*
- * @param buffer buffer to write to
- * @param topStart topStart radius
- * @param topEnd topEnd radius
- * @param bottomStart bottomStart radius
- * @param bottomEnd bottomEnd radius
+ * @param buffer buffer to write to
+ * @param topStart topStart radius
+ * @param topEnd topEnd radius
+ * @param bottomStart bottomStart radius
+ * @param bottomEnd bottomEnd radius
*/
- public static void apply(WireBuffer buffer,
- float topStart,
- float topEnd,
- float bottomStart,
- float bottomEnd) {
+ public static void apply(
+ WireBuffer buffer, float topStart, float topEnd, float bottomStart, float bottomEnd) {
write(buffer, OP_CODE, topStart, topEnd, bottomStart, bottomEnd);
}
}
diff --git a/core/java/com/android/internal/widget/remotecompose/core/operations/layout/modifiers/ShapeType.java b/core/java/com/android/internal/widget/remotecompose/core/operations/layout/modifiers/ShapeType.java
index e425b4e0cc0a..0cd062eb25c3 100644
--- a/core/java/com/android/internal/widget/remotecompose/core/operations/layout/modifiers/ShapeType.java
+++ b/core/java/com/android/internal/widget/remotecompose/core/operations/layout/modifiers/ShapeType.java
@@ -15,9 +15,7 @@
*/
package com.android.internal.widget.remotecompose.core.operations.layout.modifiers;
-/**
- * Known shapes, used for modifiers (clip/background/border)
- */
+/** Known shapes, used for modifiers (clip/background/border) */
public class ShapeType {
public static int RECTANGLE = 0;
public static int CIRCLE = 1;
diff --git a/core/java/com/android/internal/widget/remotecompose/core/operations/layout/modifiers/ValueIntegerChangeActionOperation.java b/core/java/com/android/internal/widget/remotecompose/core/operations/layout/modifiers/ValueIntegerChangeActionOperation.java
index 3f19c9b1bc4b..8876720c9990 100644
--- a/core/java/com/android/internal/widget/remotecompose/core/operations/layout/modifiers/ValueIntegerChangeActionOperation.java
+++ b/core/java/com/android/internal/widget/remotecompose/core/operations/layout/modifiers/ValueIntegerChangeActionOperation.java
@@ -15,7 +15,7 @@
*/
package com.android.internal.widget.remotecompose.core.operations.layout.modifiers;
-import static com.android.internal.widget.remotecompose.core.documentation.Operation.INT;
+import static com.android.internal.widget.remotecompose.core.documentation.DocumentedOperation.INT;
import com.android.internal.widget.remotecompose.core.CoreDocument;
import com.android.internal.widget.remotecompose.core.Operation;
@@ -29,9 +29,7 @@ import com.android.internal.widget.remotecompose.core.operations.utilities.Strin
import java.util.List;
-/**
- * Apply a value change on an integer variable.
- */
+/** Apply a value change on an integer variable. */
public class ValueIntegerChangeActionOperation implements ActionOperation {
private static final int OP_CODE = Operations.VALUE_INTEGER_CHANGE_ACTION;
@@ -54,28 +52,23 @@ public class ValueIntegerChangeActionOperation implements ActionOperation {
@Override
public void serializeToString(int indent, StringSerializer serializer) {
- serializer.append(indent, serializedName()
- + " = " + mTargetValueId + " -> " + mValue);
+ serializer.append(indent, serializedName() + " = " + mTargetValueId + " -> " + mValue);
}
@Override
- public void apply(RemoteContext context) {
- }
+ public void apply(RemoteContext context) {}
@Override
public String deepToString(String indent) {
return (indent != null ? indent : "") + toString();
}
-
@Override
- public void write(WireBuffer buffer) {
-
- }
+ public void write(WireBuffer buffer) {}
@Override
- public void runAction(RemoteContext context, CoreDocument document,
- Component component, float x, float y) {
+ public void runAction(
+ RemoteContext context, CoreDocument document, Component component, float x, float y) {
context.overrideInteger(mTargetValueId, mValue);
}
@@ -93,11 +86,10 @@ public class ValueIntegerChangeActionOperation implements ActionOperation {
public static void documentation(DocumentationBuilder doc) {
doc.operation("Layout Operations", OP_CODE, "ValueIntegerChangeActionOperation")
- .description("ValueIntegerChange action. "
- + " This operation represents a value change for the given id")
+ .description(
+ "ValueIntegerChange action. "
+ + " This operation represents a value change for the given id")
.field(INT, "TARGET_VALUE_ID", "Value ID")
- .field(INT, "VALUE", "integer value to be assigned to the target")
- ;
+ .field(INT, "VALUE", "integer value to be assigned to the target");
}
-
}
diff --git a/core/java/com/android/internal/widget/remotecompose/core/operations/layout/modifiers/ValueIntegerExpressionChangeActionOperation.java b/core/java/com/android/internal/widget/remotecompose/core/operations/layout/modifiers/ValueIntegerExpressionChangeActionOperation.java
new file mode 100644
index 000000000000..fb5e911e8bc6
--- /dev/null
+++ b/core/java/com/android/internal/widget/remotecompose/core/operations/layout/modifiers/ValueIntegerExpressionChangeActionOperation.java
@@ -0,0 +1,96 @@
+/*
+ * 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.
+ */
+package com.android.internal.widget.remotecompose.core.operations.layout.modifiers;
+
+import static com.android.internal.widget.remotecompose.core.documentation.DocumentedOperation.INT;
+
+import com.android.internal.widget.remotecompose.core.CoreDocument;
+import com.android.internal.widget.remotecompose.core.Operation;
+import com.android.internal.widget.remotecompose.core.Operations;
+import com.android.internal.widget.remotecompose.core.RemoteContext;
+import com.android.internal.widget.remotecompose.core.WireBuffer;
+import com.android.internal.widget.remotecompose.core.documentation.DocumentationBuilder;
+import com.android.internal.widget.remotecompose.core.operations.layout.ActionOperation;
+import com.android.internal.widget.remotecompose.core.operations.layout.Component;
+import com.android.internal.widget.remotecompose.core.operations.utilities.StringSerializer;
+
+import java.util.List;
+
+/** Apply a value change on an integer variable. */
+public class ValueIntegerExpressionChangeActionOperation implements ActionOperation {
+ private static final int OP_CODE = Operations.VALUE_INTEGER_EXPRESSION_CHANGE_ACTION;
+
+ long mTargetValueId = -1;
+ long mValueExpressionId = -1;
+
+ public ValueIntegerExpressionChangeActionOperation(long id, long value) {
+ mTargetValueId = id;
+ mValueExpressionId = value;
+ }
+
+ @Override
+ public String toString() {
+ return "ValueIntegerExpressionChangeActionOperation(" + mTargetValueId + ")";
+ }
+
+ public String serializedName() {
+ return "VALUE_INTEGER_EXPRESSION_CHANGE";
+ }
+
+ @Override
+ public void serializeToString(int indent, StringSerializer serializer) {
+ serializer.append(
+ indent, serializedName() + " = " + mTargetValueId + " -> " + mValueExpressionId);
+ }
+
+ @Override
+ public void apply(RemoteContext context) {}
+
+ @Override
+ public String deepToString(String indent) {
+ return (indent != null ? indent : "") + toString();
+ }
+
+ @Override
+ public void write(WireBuffer buffer) {}
+
+ @Override
+ public void runAction(
+ RemoteContext context, CoreDocument document, Component component, float x, float y) {
+ document.evaluateIntExpression(mValueExpressionId, (int) mTargetValueId, context);
+ }
+
+ public static void apply(WireBuffer buffer, long valueId, long value) {
+ buffer.start(OP_CODE);
+ buffer.writeLong(valueId);
+ buffer.writeLong(value);
+ }
+
+ public static void read(WireBuffer buffer, List<Operation> operations) {
+ long valueId = buffer.readLong();
+ long value = buffer.readLong();
+ operations.add(new ValueIntegerExpressionChangeActionOperation(valueId, value));
+ }
+
+ public static void documentation(DocumentationBuilder doc) {
+ doc.operation("Layout Operations", OP_CODE, "ValueIntegerExpressionChangeActionOperation")
+ .description(
+ "ValueIntegerExpressionChange action. "
+ + " This operation represents a value change for the given id")
+ .field(INT, "TARGET_VALUE_ID", "Value ID")
+ .field(INT, "VALUE_ID", "id of the value to be assigned to the target");
+ }
+}
diff --git a/core/java/com/android/internal/widget/remotecompose/core/operations/layout/modifiers/ValueStringChangeActionOperation.java b/core/java/com/android/internal/widget/remotecompose/core/operations/layout/modifiers/ValueStringChangeActionOperation.java
index e2542e529383..a64a492a8cc1 100644
--- a/core/java/com/android/internal/widget/remotecompose/core/operations/layout/modifiers/ValueStringChangeActionOperation.java
+++ b/core/java/com/android/internal/widget/remotecompose/core/operations/layout/modifiers/ValueStringChangeActionOperation.java
@@ -15,7 +15,7 @@
*/
package com.android.internal.widget.remotecompose.core.operations.layout.modifiers;
-import static com.android.internal.widget.remotecompose.core.documentation.Operation.INT;
+import static com.android.internal.widget.remotecompose.core.documentation.DocumentedOperation.INT;
import com.android.internal.widget.remotecompose.core.CoreDocument;
import com.android.internal.widget.remotecompose.core.Operation;
@@ -29,9 +29,7 @@ import com.android.internal.widget.remotecompose.core.operations.utilities.Strin
import java.util.List;
-/**
- * Apply a value change on a string variable.
- */
+/** Apply a value change on a string variable. */
public class ValueStringChangeActionOperation implements ActionOperation {
private static final int OP_CODE = Operations.VALUE_STRING_CHANGE_ACTION;
@@ -62,8 +60,7 @@ public class ValueStringChangeActionOperation implements ActionOperation {
}
@Override
- public void apply(RemoteContext context) {
- }
+ public void apply(RemoteContext context) {}
@Override
public String deepToString(String indent) {
@@ -71,13 +68,11 @@ public class ValueStringChangeActionOperation implements ActionOperation {
}
@Override
- public void write(WireBuffer buffer) {
-
- }
+ public void write(WireBuffer buffer) {}
@Override
- public void runAction(RemoteContext context, CoreDocument document,
- Component component, float x, float y) {
+ public void runAction(
+ RemoteContext context, CoreDocument document, Component component, float x, float y) {
context.overrideText(mTargetValueId, mValueId);
}
@@ -95,13 +90,14 @@ public class ValueStringChangeActionOperation implements ActionOperation {
public static void documentation(DocumentationBuilder doc) {
doc.operation("Layout Operations", OP_CODE, "ValueStringChangeActionOperation")
- .description("ValueStrin gChange action. "
- + " This operation represents a String change (referenced by id) "
- + "for the given string id")
+ .description(
+ "ValueStrin gChange action. "
+ + " This operation represents a String change (referenced by id) "
+ + "for the given string id")
.field(INT, "TARGET_ID", "Target Value ID")
- .field(INT, "VALUE_ID", "Value ID to be assigned to the target "
- + "value as a string")
- ;
+ .field(
+ INT,
+ "VALUE_ID",
+ "Value ID to be assigned to the target " + "value as a string");
}
-
}
diff --git a/core/java/com/android/internal/widget/remotecompose/core/operations/layout/modifiers/WidthModifierOperation.java b/core/java/com/android/internal/widget/remotecompose/core/operations/layout/modifiers/WidthModifierOperation.java
index 7fd5e51add18..62403b3e5e7e 100644
--- a/core/java/com/android/internal/widget/remotecompose/core/operations/layout/modifiers/WidthModifierOperation.java
+++ b/core/java/com/android/internal/widget/remotecompose/core/operations/layout/modifiers/WidthModifierOperation.java
@@ -15,8 +15,8 @@
*/
package com.android.internal.widget.remotecompose.core.operations.layout.modifiers;
-import static com.android.internal.widget.remotecompose.core.documentation.Operation.FLOAT;
-import static com.android.internal.widget.remotecompose.core.documentation.Operation.INT;
+import static com.android.internal.widget.remotecompose.core.documentation.DocumentedOperation.FLOAT;
+import static com.android.internal.widget.remotecompose.core.documentation.DocumentedOperation.INT;
import com.android.internal.widget.remotecompose.core.Operation;
import com.android.internal.widget.remotecompose.core.Operations;
@@ -25,14 +25,11 @@ import com.android.internal.widget.remotecompose.core.documentation.Documentatio
import java.util.List;
-/**
- * Set the width dimension on a component
- */
+/** Set the width dimension on a component */
public class WidthModifierOperation extends DimensionModifierOperation {
private static final int OP_CODE = Operations.MODIFIER_WIDTH;
public static final String CLASS_NAME = "WidthModifierOperation";
-
public static String name() {
return CLASS_NAME;
}
@@ -82,9 +79,7 @@ public class WidthModifierOperation extends DimensionModifierOperation {
}
public static void documentation(DocumentationBuilder doc) {
- doc.operation("Modifier Operations",
- OP_CODE,
- CLASS_NAME)
+ doc.operation("Modifier Operations", OP_CODE, CLASS_NAME)
.description("define the animation")
.field(INT, "type", "")
.field(FLOAT, "value", "");
diff --git a/core/java/com/android/internal/widget/remotecompose/core/operations/layout/utils/DebugLog.java b/core/java/com/android/internal/widget/remotecompose/core/operations/layout/utils/DebugLog.java
index 7ccf7f48af24..4849b12c65b1 100644
--- a/core/java/com/android/internal/widget/remotecompose/core/operations/layout/utils/DebugLog.java
+++ b/core/java/com/android/internal/widget/remotecompose/core/operations/layout/utils/DebugLog.java
@@ -17,9 +17,7 @@ package com.android.internal.widget.remotecompose.core.operations.layout.utils;
import java.util.ArrayList;
-/**
- * Internal utility debug class
- */
+/** Internal utility debug class */
public class DebugLog {
public static final boolean DEBUG_LAYOUT_ON = false;
@@ -109,8 +107,12 @@ public class DebugLog {
if (node instanceof LogNode) {
builder.append(indentation).append(" ").append(node.name).append("\n");
} else {
- builder.append(indentation).append("-- ").append(node.name)
- .append(" : ").append(node.endString).append("\n");
+ builder.append(indentation)
+ .append("-- ")
+ .append(node.name)
+ .append(" : ")
+ .append(node.endString)
+ .append("\n");
}
}
}
@@ -124,4 +126,3 @@ public class DebugLog {
}
}
}
-
diff --git a/core/java/com/android/internal/widget/remotecompose/core/operations/layout/utils/StringValueSupplier.java b/core/java/com/android/internal/widget/remotecompose/core/operations/layout/utils/StringValueSupplier.java
index 79ef16bf25da..701167abb8f0 100644
--- a/core/java/com/android/internal/widget/remotecompose/core/operations/layout/utils/StringValueSupplier.java
+++ b/core/java/com/android/internal/widget/remotecompose/core/operations/layout/utils/StringValueSupplier.java
@@ -15,12 +15,11 @@
*/
package com.android.internal.widget.remotecompose.core.operations.layout.utils;
-/**
- * Basic interface for a lambda (used for logging)
- */
+/** Basic interface for a lambda (used for logging) */
public interface StringValueSupplier {
/**
* returns a string value
+ *
* @return a string
*/
String getString();
diff --git a/core/java/com/android/internal/widget/remotecompose/core/operations/paint/PaintBundle.java b/core/java/com/android/internal/widget/remotecompose/core/operations/paint/PaintBundle.java
index 6c8049a97f1e..e714947c6a5c 100644
--- a/core/java/com/android/internal/widget/remotecompose/core/operations/paint/PaintBundle.java
+++ b/core/java/com/android/internal/widget/remotecompose/core/operations/paint/PaintBundle.java
@@ -23,9 +23,7 @@ import com.android.internal.widget.remotecompose.core.operations.Utils;
import java.util.Arrays;
-/**
- * Paint Bundle represents a delta of changes to a paint object
- */
+/** Paint Bundle represents a delta of changes to a paint object */
public class PaintBundle {
int[] mArray = new int[200];
int[] mOutArray = null;
@@ -33,6 +31,7 @@ public class PaintBundle {
/**
* Apply changes to a PaintChanges interface
+ *
* @param paintContext
* @param p
*/
@@ -46,10 +45,9 @@ public class PaintBundle {
int cmd = mOutArray[i++];
mask = mask | (1 << (cmd - 1));
switch (cmd & 0xFFFF) {
- case TEXT_SIZE: {
+ case TEXT_SIZE:
p.setTextSize(Float.intBitsToFloat(mOutArray[i++]));
break;
- }
case TYPEFACE:
int style = (cmd >> 16);
int weight = style & 0x3ff;
@@ -59,99 +57,86 @@ public class PaintBundle {
p.setTypeFace(font_type, weight, italic);
break;
case COLOR_ID: // mOutArray should have already decoded it
- case COLOR: {
+ case COLOR:
p.setColor(mOutArray[i++]);
break;
- }
- case STROKE_WIDTH: {
+ case STROKE_WIDTH:
p.setStrokeWidth(Float.intBitsToFloat(mOutArray[i++]));
break;
- }
- case STROKE_MITER: {
+ case STROKE_MITER:
p.setStrokeMiter(Float.intBitsToFloat(mOutArray[i++]));
break;
- }
- case STROKE_CAP: {
+ case STROKE_CAP:
p.setStrokeCap(cmd >> 16);
break;
- }
- case STYLE: {
+ case STYLE:
p.setStyle(cmd >> 16);
break;
- }
- case SHADER: {
+ case SHADER:
p.setShader(mOutArray[i++]);
break;
- }
- case STROKE_JOIN: {
+ case STROKE_JOIN:
p.setStrokeJoin(cmd >> 16);
break;
- }
- case IMAGE_FILTER_QUALITY: {
+ case IMAGE_FILTER_QUALITY:
p.setImageFilterQuality(cmd >> 16);
break;
- }
- case BLEND_MODE: {
+ case BLEND_MODE:
p.setBlendMode(cmd >> 16);
break;
- }
- case FILTER_BITMAP: {
+ case FILTER_BITMAP:
p.setFilterBitmap(!((cmd >> 16) == 0));
break;
- }
- case GRADIENT: {
+ case GRADIENT:
i = callSetGradient(cmd, mOutArray, i, p);
break;
- }
- case COLOR_FILTER: {
+ case COLOR_FILTER_ID:
+ case COLOR_FILTER:
p.setColorFilter(mOutArray[i++], cmd >> 16);
break;
- }
- case ALPHA: {
+ case ALPHA:
p.setAlpha(Float.intBitsToFloat(mOutArray[i++]));
break;
- }
+ case CLEAR_COLOR_FILTER:
+ p.clear(0x1L << PaintBundle.COLOR_FILTER);
+ break;
}
}
-
- mask = (~mask) & PaintChanges.VALID_BITS;
-
- p.clear(mask);
}
- private String toName(int id) {
- switch (id) {
- case TEXT_SIZE:
- return "TEXT_SIZE";
- case COLOR:
- return "COLOR";
- case STROKE_WIDTH:
- return "STROKE_WIDTH";
- case STROKE_MITER:
- return "STROKE_MITER";
- case TYPEFACE:
- return "TYPEFACE";
- case STROKE_CAP:
- return "CAP";
- case STYLE:
- return "STYLE";
- case SHADER:
- return "SHADER";
- case IMAGE_FILTER_QUALITY:
- return "IMAGE_FILTER_QUALITY";
- case BLEND_MODE:
- return "BLEND_MODE";
- case FILTER_BITMAP:
- return "FILTER_BITMAP";
- case GRADIENT:
- return "GRADIENT_LINEAR";
- case ALPHA:
- return "ALPHA";
- case COLOR_FILTER:
- return "COLOR_FILTER";
- }
- return "????" + id + "????";
- }
+ // private String toName(int id) {
+ // switch (id) {
+ // case TEXT_SIZE:
+ // return "TEXT_SIZE";
+ // case COLOR:
+ // return "COLOR";
+ // case STROKE_WIDTH:
+ // return "STROKE_WIDTH";
+ // case STROKE_MITER:
+ // return "STROKE_MITER";
+ // case TYPEFACE:
+ // return "TYPEFACE";
+ // case STROKE_CAP:
+ // return "CAP";
+ // case STYLE:
+ // return "STYLE";
+ // case SHADER:
+ // return "SHADER";
+ // case IMAGE_FILTER_QUALITY:
+ // return "IMAGE_FILTER_QUALITY";
+ // case BLEND_MODE:
+ // return "BLEND_MODE";
+ // case FILTER_BITMAP:
+ // return "FILTER_BITMAP";
+ // case GRADIENT:
+ // return "GRADIENT_LINEAR";
+ // case ALPHA:
+ // return "ALPHA";
+ // case COLOR_FILTER:
+ // return "COLOR_FILTER";
+ // }
+ // return "????" + id + "????";
+ // }
private static String colorInt(int color) {
String str = "000000000000" + Integer.toHexString(color);
@@ -185,103 +170,189 @@ public class PaintBundle {
int cmd = mArray[i++];
int type = cmd & 0xFFFF;
switch (type) {
-
- case TEXT_SIZE: {
- ret.append(" TextSize("
- + asFloatStr(mArray[i++]));
- }
-
- break;
- case TYPEFACE: {
+ case TEXT_SIZE:
+ ret.append(" TextSize(" + asFloatStr(mArray[i++]));
+ break;
+ case TYPEFACE:
int style = (cmd >> 16);
int weight = style & 0x3ff;
boolean italic = (style >> 10) > 0;
int font_type = mArray[i++];
- ret.append(" TypeFace(" + (font_type + ", "
- + weight + ", " + italic));
- }
- break;
- case COLOR: {
+ ret.append(" TypeFace(" + (font_type + ", " + weight + ", " + italic));
+ break;
+ case COLOR:
ret.append(" Color(" + colorInt(mArray[i++]));
- }
- break;
- case COLOR_ID: {
+ break;
+ case COLOR_ID:
ret.append(" ColorId([" + mArray[i++] + "]");
- }
- break;
- case STROKE_WIDTH: {
- ret.append(" StrokeWidth("
- + (asFloatStr(mArray[i++])));
- }
- break;
- case STROKE_MITER: {
- ret.append(" StrokeMiter("
- + (asFloatStr(mArray[i++])));
- }
- break;
- case STROKE_CAP: {
- ret.append(" StrokeCap("
- + (cmd >> 16));
- }
- break;
- case STYLE: {
+ break;
+ case STROKE_WIDTH:
+ ret.append(" StrokeWidth(" + asFloatStr(mArray[i++]));
+ break;
+ case STROKE_MITER:
+ ret.append(" StrokeMiter(" + asFloatStr(mArray[i++]));
+ break;
+ case STROKE_CAP:
+ ret.append(" StrokeCap(" + (cmd >> 16));
+ break;
+ case STYLE:
ret.append(" Style(" + (cmd >> 16));
- }
- break;
- case COLOR_FILTER: {
- ret.append(" ColorFilter(color="
- + colorInt(mArray[i++])
- + ", mode=" + blendModeString(cmd >> 16));
- }
- break;
- case SHADER: {
+ break;
+ case COLOR_FILTER:
+ ret.append(
+ " ColorFilter(color="
+ + colorInt(mArray[i++])
+ + ", mode="
+ + blendModeString(cmd >> 16));
+ break;
+ case COLOR_FILTER_ID:
+ ret.append(
+ " ColorFilterID(color=["
+ + mArray[i++]
+ + "], mode="
+ + blendModeString(cmd >> 16));
+ break;
+ case CLEAR_COLOR_FILTER:
+ ret.append(" clearColorFilter");
+ break;
+ case SHADER:
ret.append(" Shader(" + mArray[i++]);
- }
- break;
- case ALPHA: {
- ret.append(" Alpha("
- + (asFloatStr(mArray[i++])));
- }
- break;
- case IMAGE_FILTER_QUALITY: {
+ break;
+ case ALPHA:
+ ret.append(" Alpha(" + asFloatStr(mArray[i++]));
+ break;
+ case IMAGE_FILTER_QUALITY:
ret.append(" ImageFilterQuality(" + (cmd >> 16));
- }
- break;
- case BLEND_MODE: {
+ break;
+ case BLEND_MODE:
ret.append(" BlendMode(" + blendModeString(cmd >> 16));
+ break;
+ case FILTER_BITMAP:
+ ret.append(" FilterBitmap(" + !(cmd >> 16 == 0));
+ break;
+ case STROKE_JOIN:
+ ret.append(" StrokeJoin(" + (cmd >> 16));
+ break;
+ case ANTI_ALIAS:
+ ret.append(" AntiAlias(" + (cmd >> 16));
+ break;
+ case GRADIENT:
+ i = callPrintGradient(cmd, mArray, i, ret);
+ }
+ ret.append("),\n");
+ }
+ return ret.toString();
+ }
+
+ private void registerFloat(int iv, RemoteContext context, VariableSupport support) {
+ float v = Float.intBitsToFloat(iv);
+ if (Float.isNaN(v)) {
+ context.listensTo(Utils.idFromNan(v), support);
+ }
+ }
+
+ int callRegisterGradient(
+ int cmd, int[] array, int i, RemoteContext context, VariableSupport support) {
+ int ret = i;
+ int type = (cmd >> 16);
+ int control = array[ret++];
+ int len = 0xFF & control; // maximum 256 colors
+ int register = 0xFFFF & (control >> 16);
+ int tileMode = 0;
+ switch (type) {
+ /* see {@link #setLinearGradient} */
+ case LINEAR_GRADIENT:
+ if (len > 0) {
+
+ for (int j = 0; j < len; j++) {
+ int color = array[ret++];
+ if ((register & (1 << j)) != 0) {
+ context.listensTo(color, support);
+ }
+ }
}
- break;
- case FILTER_BITMAP: {
- ret.append(" FilterBitmap("
- + (!((cmd >> 16) == 0)));
+ len = array[ret++];
+
+ if (len > 0) {
+
+ for (int j = 0; j < len; j++) {
+ registerFloat(array[ret++], context, support);
+ }
}
+
+ // start x
+ registerFloat(array[ret++], context, support);
+ // start y
+ registerFloat(array[ret++], context, support);
+ // end x
+ registerFloat(array[ret++], context, support);
+ // end y
+ registerFloat(array[ret++], context, support);
+ tileMode = array[ret++];
break;
- case STROKE_JOIN: {
- ret.append(" StrokeJoin(" + (cmd >> 16));
+ /* see {@link #setRadialGradient} */
+ case RADIAL_GRADIENT:
+ if (len > 0) {
+
+ for (int j = 0; j < len; j++) {
+ int color = array[ret++];
+ if ((register & (1 << j)) != 0) {
+ context.listensTo(color, support);
+ }
+ }
}
- break;
- case ANTI_ALIAS: {
- ret.append(" AntiAlias(" + (cmd >> 16));
+ len = array[ret++]; // stops
+ for (int j = 0; j < len; j++) {
+ registerFloat(array[ret++], context, support);
}
+
+ // center x
+ registerFloat(array[ret++], context, support);
+ // center y
+ registerFloat(array[ret++], context, support);
+ // radius
+ registerFloat(array[ret++], context, support);
+
+ tileMode = array[ret++]; // tile Mode
break;
- case GRADIENT: {
- i = callPrintGradient(cmd, mArray, i, ret);
+ /* see {@link #setSweepGradient} */
+ case SWEEP_GRADIENT:
+ if (len > 0) {
+
+ for (int j = 0; j < len; j++) {
+ int color = array[ret++];
+ if ((register & (1 << j)) != 0) {
+ context.listensTo(color, support);
+ }
+ }
}
- }
- ret.append("),\n");
+
+ len = array[ret++]; // stops
+ for (int j = 0; j < len; j++) {
+ registerFloat(array[ret++], context, support);
+ }
+ // center x
+ registerFloat(array[ret++], context, support);
+ // center y
+ registerFloat(array[ret++], context, support);
+ break;
+ default:
+ System.out.println("error ");
}
- return ret.toString();
+
+ return ret;
}
int callPrintGradient(int cmd, int[] array, int i, StringBuilder p) {
int ret = i;
int type = (cmd >> 16);
+ int tileMode = 0;
+ int len = array[ret++];
+ int[] colors = null;
+ String[] stops = null;
switch (type) {
-
- case 0: {
+ case 0:
p.append(" LinearGradient(\n");
- int len = array[ret++];
- int[] colors = null;
if (len > 0) {
colors = new int[len];
for (int j = 0; j < colors.length; j++) {
@@ -289,7 +360,6 @@ public class PaintBundle {
}
}
len = array[ret++];
- String[] stops = null;
if (len > 0) {
stops = new String[len];
for (int j = 0; j < stops.length; j++) {
@@ -305,24 +375,18 @@ public class PaintBundle {
p.append(" end = ");
p.append("[" + asFloatStr(array[ret++]));
p.append(", " + asFloatStr(array[ret++]) + "],\n");
- int tileMode = array[ret++];
+ tileMode = array[ret++];
p.append(" tileMode = " + tileMode + "\n ");
- }
-
- break;
- case 1: {
+ break;
+ case 1:
p.append(" RadialGradient(\n");
- int len = array[ret++];
- int[] colors = null;
if (len > 0) {
colors = new int[len];
for (int j = 0; j < colors.length; j++) {
colors[j] = array[ret++];
-
}
}
len = array[ret++];
- String[] stops = null;
if (len > 0) {
stops = new String[len];
for (int j = 0; j < stops.length; j++) {
@@ -337,24 +401,18 @@ public class PaintBundle {
p.append(", " + asFloatStr(array[ret++]) + "],\n");
p.append(" radius =");
p.append(" " + asFloatStr(array[ret++]) + ",\n");
- int tileMode = array[ret++];
+ tileMode = array[ret++];
p.append(" tileMode = " + tileMode + "\n ");
- }
-
- break;
- case 2: {
+ break;
+ case 2:
p.append(" SweepGradient(\n");
- int len = array[ret++];
- int[] colors = null;
if (len > 0) {
colors = new int[len];
for (int j = 0; j < colors.length; j++) {
colors[j] = array[ret++];
-
}
}
len = array[ret++];
- String[] stops = null;
if (len > 0) {
stops = new String[len];
for (int j = 0; j < stops.length; j++) {
@@ -365,13 +423,10 @@ public class PaintBundle {
p.append(" stops = " + Arrays.toString(stops) + ",\n");
p.append(" center = ");
p.append("[" + asFloatStr(array[ret++]));
- p.append(", "
- + asFloatStr(array[ret++]) + "],\n ");
- }
- break;
- default: {
+ p.append(", " + asFloatStr(array[ret++]) + "],\n ");
+ break;
+ default:
p.append("GRADIENT_??????!!!!");
- }
}
return ret;
@@ -381,7 +436,8 @@ public class PaintBundle {
int ret = i;
int gradientType = (cmd >> 16);
- int len = array[ret++];
+ int len = 0xFF & array[ret++]; // maximum 256 colors
+
int[] colors = null;
if (len > 0) {
colors = new int[len];
@@ -402,33 +458,30 @@ public class PaintBundle {
return ret;
}
- switch (gradientType) {
+ int tileMode = 0;
+ float centerX = 0f;
+ float centerY = 0f;
- case LINEAR_GRADIENT: {
+ switch (gradientType) {
+ case LINEAR_GRADIENT:
float startX = Float.intBitsToFloat(array[ret++]);
float startY = Float.intBitsToFloat(array[ret++]);
float endX = Float.intBitsToFloat(array[ret++]);
float endY = Float.intBitsToFloat(array[ret++]);
- int tileMode = array[ret++];
- p.setLinearGradient(colors, stops, startX,
- startY, endX, endY, tileMode);
- }
-
- break;
- case RADIAL_GRADIENT: {
- float centerX = Float.intBitsToFloat(array[ret++]);
- float centerY = Float.intBitsToFloat(array[ret++]);
+ tileMode = array[ret++];
+ p.setLinearGradient(colors, stops, startX, startY, endX, endY, tileMode);
+ break;
+ case RADIAL_GRADIENT:
+ centerX = Float.intBitsToFloat(array[ret++]);
+ centerY = Float.intBitsToFloat(array[ret++]);
float radius = Float.intBitsToFloat(array[ret++]);
- int tileMode = array[ret++];
- p.setRadialGradient(colors, stops, centerX, centerY,
- radius, tileMode);
- }
- break;
- case SWEEP_GRADIENT: {
- float centerX = Float.intBitsToFloat(array[ret++]);
- float centerY = Float.intBitsToFloat(array[ret++]);
+ tileMode = array[ret++];
+ p.setRadialGradient(colors, stops, centerX, centerY, radius, tileMode);
+ break;
+ case SWEEP_GRADIENT:
+ centerX = Float.intBitsToFloat(array[ret++]);
+ centerY = Float.intBitsToFloat(array[ret++]);
p.setSweepGradient(colors, stops, centerX, centerY);
- }
}
return ret;
@@ -453,9 +506,9 @@ public class PaintBundle {
mPos = len;
}
- public static final int TEXT_SIZE = 1; // float
+ public static final int TEXT_SIZE = 1; // float
- public static final int COLOR = 4; // int
+ public static final int COLOR = 4; // int
public static final int STROKE_WIDTH = 5; // float
public static final int STROKE_MITER = 6;
public static final int STROKE_CAP = 7; // int
@@ -470,7 +523,9 @@ public class PaintBundle {
public static final int TYPEFACE = 16;
public static final int FILTER_BITMAP = 17;
public static final int BLEND_MODE = 18;
- public static final int COLOR_ID = 19; // int
+ public static final int COLOR_ID = 19;
+ public static final int COLOR_FILTER_ID = 20;
+ public static final int CLEAR_COLOR_FILTER = 21;
public static final int BLEND_MODE_CLEAR = 0;
public static final int BLEND_MODE_SRC = 1;
@@ -524,27 +579,28 @@ public class PaintBundle {
/**
* sets a shader that draws a linear gradient along a line.
*
- * @param startX The x-coordinate for the start of the gradient line
- * @param startY The y-coordinate for the start of the gradient line
- * @param endX The x-coordinate for the end of the gradient line
- * @param endY The y-coordinate for the end of the gradient line
- * @param colors The sRGB colors to be distributed along the gradient line
- * @param stops May be null. The relative positions [0..1] of
- * each corresponding color in the colors array. If this is null,
- * the colors are distributed evenly along the gradient line.
+ * @param startX The x-coordinate for the start of the gradient line
+ * @param startY The y-coordinate for the start of the gradient line
+ * @param endX The x-coordinate for the end of the gradient line
+ * @param endY The y-coordinate for the end of the gradient line
+ * @param colors The sRGB colors to be distributed along the gradient line
+ * @param stops May be null. The relative positions [0..1] of each corresponding color in the
+ * colors array. If this is null, the colors are distributed evenly along the gradient line.
* @param tileMode The Shader tiling mode
*/
- public void setLinearGradient(int[] colors,
- float[] stops,
- float startX,
- float startY,
- float endX,
- float endY,
- int tileMode) {
- int startPos = mPos;
+ public void setLinearGradient(
+ int[] colors,
+ int idMask,
+ float[] stops,
+ float startX,
+ float startY,
+ float endX,
+ float endY,
+ int tileMode) {
+ // int startPos = mPos;
int len;
mArray[mPos++] = GRADIENT | (LINEAR_GRADIENT << 16);
- mArray[mPos++] = len = (colors == null) ? 0 : colors.length;
+ mArray[mPos++] = (idMask << 16) | (len = (colors == null) ? 0 : colors.length);
for (int i = 0; i < len; i++) {
mArray[mPos++] = colors[i];
}
@@ -565,20 +621,18 @@ public class PaintBundle {
*
* @param centerX The x-coordinate of the center
* @param centerY The y-coordinate of the center
- * @param colors The sRGB colors to be distributed around the center.
- * There must be at least 2 colors in the array.
- * @param stops May be NULL. The relative position of
- * each corresponding color in the colors array, beginning
- * with 0 and ending with 1.0. If the values are not
- * monotonic, the drawing may produce unexpected results.
- * If positions is NULL, then the colors are automatically
- * spaced evenly.
+ * @param colors The sRGB colors to be distributed around the center. There must be at least 2
+ * colors in the array.
+ * @param stops May be NULL. The relative position of each corresponding color in the colors
+ * array, beginning with 0 and ending with 1.0. If the values are not monotonic, the drawing
+ * may produce unexpected results. If positions is NULL, then the colors are automatically
+ * spaced evenly.
*/
- public void setSweepGradient(int[] colors, float[] stops, float centerX, float centerY) {
- int startPos = mPos;
+ public void setSweepGradient(
+ int[] colors, int idMask, float[] stops, float centerX, float centerY) {
int len;
mArray[mPos++] = GRADIENT | (SWEEP_GRADIENT << 16);
- mArray[mPos++] = len = (colors == null) ? 0 : colors.length;
+ mArray[mPos++] = (idMask << 16) | (len = (colors == null) ? 0 : colors.length);
for (int i = 0; i < len; i++) {
mArray[mPos++] = colors[i];
}
@@ -594,29 +648,28 @@ public class PaintBundle {
/**
* Sets a shader that draws a radial gradient given the center and radius.
*
- * @param centerX The x-coordinate of the center of the radius
- * @param centerY The y-coordinate of the center of the radius
- * @param radius Must be positive. The radius of the gradient.
- * @param colors The sRGB colors distributed between the center and edge
- * @param stops May be <code>null</code>.
- * Valid values are between <code>0.0f</code> and
- * <code>1.0f</code>. The relative position of each
- * corresponding color in
- * the colors array. If <code>null</code>, colors are
- * distributed evenly
- * between the center and edge of the circle.
+ * @param centerX The x-coordinate of the center of the radius
+ * @param centerY The y-coordinate of the center of the radius
+ * @param radius Must be positive. The radius of the gradient.
+ * @param colors The sRGB colors distributed between the center and edge
+ * @param stops May be <code>null</code>. Valid values are between <code>0.0f</code> and <code>
+ * 1.0f</code>. The relative position of each corresponding color in the colors array. If
+ * <code>null</code>, colors are distributed evenly between the center and edge of the
+ * circle.
* @param tileMode The Shader tiling mode
*/
- public void setRadialGradient(int[] colors,
- float[] stops,
- float centerX,
- float centerY,
- float radius,
- int tileMode) {
- int startPos = mPos;
+ public void setRadialGradient(
+ int[] colors,
+ int idMask,
+ float[] stops,
+ float centerX,
+ float centerY,
+ float radius,
+ int tileMode) {
+ // int startPos = mPos;
int len;
mArray[mPos++] = GRADIENT | (RADIAL_GRADIENT << 16);
- mArray[mPos++] = len = (colors == null) ? 0 : colors.length;
+ mArray[mPos++] = (idMask << 16) | (len = (colors == null) ? 0 : colors.length);
for (int i = 0; i < len; i++) {
mArray[mPos++] = colors[i];
}
@@ -629,14 +682,13 @@ public class PaintBundle {
mArray[mPos++] = Float.floatToRawIntBits(centerY);
mArray[mPos++] = Float.floatToRawIntBits(radius);
mArray[mPos++] = tileMode;
-
}
/**
* Create a color filter that uses the specified color and Porter-Duff mode.
*
* @param color The ARGB source color used with the Porter-Duff mode
- * @param mode The porter-duff mode that is applied
+ * @param mode The porter-duff mode that is applied
*/
public void setColorFilter(int color, int mode) {
mArray[mPos] = COLOR_FILTER | (mode << 16);
@@ -645,12 +697,29 @@ public class PaintBundle {
}
/**
+ * Create a color filter that uses the specified color and Porter-Duff mode.
+ *
+ * @param color The id source color used with the Porter-Duff mode
+ * @param mode The porter-duff mode that is applied
+ */
+ public void setColorFilterId(int color, int mode) {
+ mArray[mPos] = COLOR_FILTER_ID | (mode << 16);
+ mPos++;
+ mArray[mPos++] = color;
+ }
+
+ /** This sets the color filter to null */
+ public void clearColorFilter() {
+ mArray[mPos] = CLEAR_COLOR_FILTER;
+ mPos++;
+ }
+
+ /**
* Set the paint's text size. This value must be > 0
*
* @param size set the paint's text size in pixel units.
*/
public void setTextSize(float size) {
- int p = mPos;
mArray[mPos] = TEXT_SIZE;
mPos++;
mArray[mPos] = Float.floatToRawIntBits(size);
@@ -659,22 +728,21 @@ public class PaintBundle {
/**
* @param fontType 0 = default 1 = sans serif 2 = serif 3 = monospace
- * @param weight 100-1000
- * @param italic tur
+ * @param weight 100-1000
+ * @param italic tur
*/
public void setTextStyle(int fontType, int weight, boolean italic) {
- int style = (weight & 0x3FF) | (italic ? 2048 : 0); // pack the weight and italic
+ int style = (weight & 0x3FF) | (italic ? 2048 : 0); // pack the weight and italic
mArray[mPos++] = TYPEFACE | (style << 16);
mArray[mPos++] = fontType;
}
/**
- * Set the width for stroking.
- * Pass 0 to stroke in hairline mode.
- * Hairlines always draws a single pixel independent of the canvas's matrix.
+ * Set the width for stroking. Pass 0 to stroke in hairline mode. Hairlines always draws a
+ * single pixel independent of the canvas's matrix.
*
- * @param width set the paint's stroke width, used whenever the paint's
- * style is Stroke or StrokeAndFill.
+ * @param width set the paint's stroke width, used whenever the paint's style is Stroke or
+ * StrokeAndFill.
*/
public void setStrokeWidth(float width) {
mArray[mPos] = STROKE_WIDTH;
@@ -685,6 +753,7 @@ public class PaintBundle {
/**
* Set the Color based on Color
+ *
* @param color
*/
public void setColor(int color) {
@@ -696,6 +765,7 @@ public class PaintBundle {
/**
* Set the color based the R,G,B,A values
+ *
* @param r red (0 to 255)
* @param g green (0 to 255)
* @param b blue (0 to 255)
@@ -708,6 +778,7 @@ public class PaintBundle {
/**
* Set the color based the R,G,B,A values
+ *
* @param r red (0.0 to 1.0)
* @param g green (0.0 to 1.0)
* @param b blue (0.0 to 1.0)
@@ -719,6 +790,7 @@ public class PaintBundle {
/**
* Set the Color based on ID
+ *
* @param color
*/
public void setColorId(int color) {
@@ -728,12 +800,11 @@ public class PaintBundle {
mPos++;
}
-
/**
* Set the paint's Cap.
*
- * @param cap set the paint's line cap style, used whenever the paint's
- * style is Stroke or StrokeAndFill.
+ * @param cap set the paint's line cap style, used whenever the paint's style is Stroke or
+ * StrokeAndFill.
*/
public void setStrokeCap(int cap) {
mArray[mPos] = STROKE_CAP | (cap << 16);
@@ -742,6 +813,7 @@ public class PaintBundle {
/**
* Set the style STROKE and/or FILL
+ *
* @param style
*/
public void setStyle(int style) {
@@ -751,6 +823,7 @@ public class PaintBundle {
/**
* Set the shader id to use
+ *
* @param shaderId
*/
public void setShader(int shaderId) {
@@ -760,9 +833,7 @@ public class PaintBundle {
mPos++;
}
- /**
- * Set the Alpha value
- */
+ /** Set the Alpha value */
public void setAlpha(float alpha) {
mArray[mPos] = ALPHA;
mPos++;
@@ -771,11 +842,11 @@ public class PaintBundle {
}
/**
- * Set the paint's stroke miter value. This is used to control the behavior
- * of miter joins when the joins angle is sharp. This value must be >= 0.
+ * Set the paint's stroke miter value. This is used to control the behavior of miter joins when
+ * the joins angle is sharp. This value must be >= 0.
*
- * @param miter set the miter limit on the paint, used whenever the paint's
- * style is Stroke or StrokeAndFill.
+ * @param miter set the miter limit on the paint, used whenever the paint's style is Stroke or
+ * StrokeAndFill.
*/
public void setStrokeMiter(float miter) {
mArray[mPos] = STROKE_MITER;
@@ -787,8 +858,7 @@ public class PaintBundle {
/**
* Set the paint's Join.
*
- * @param join set the paint's Join, used whenever the paint's style is
- * Stroke or StrokeAndFill.
+ * @param join set the paint's Join, used whenever the paint's style is Stroke or StrokeAndFill.
*/
public void setStrokeJoin(int join) {
mArray[mPos] = STROKE_JOIN | (join << 16);
@@ -801,10 +871,8 @@ public class PaintBundle {
}
/**
- * Set or clear the blend mode. A blend mode defines how source pixels
- * (generated by a drawing command) are composited with the
- * destination pixels
- * (content of the render target).
+ * Set or clear the blend mode. A blend mode defines how source pixels (generated by a drawing
+ * command) are composited with the destination pixels (content of the render target).
*
* @param blendmode The blend mode to be installed in the paint
*/
@@ -814,15 +882,14 @@ public class PaintBundle {
}
/**
- * Helper for setFlags(), setting or clearing the ANTI_ALIAS_FLAG bit
- * AntiAliasing smooths out the edges of what is being drawn, but is has
- * no impact on the interior of the shape. See setDither() and
- * setFilterBitmap() to affect how colors are treated.
+ * Helper for setFlags(), setting or clearing the ANTI_ALIAS_FLAG bit AntiAliasing smooths out
+ * the edges of what is being drawn, but is has no impact on the interior of the shape. See
+ * setDither() and setFilterBitmap() to affect how colors are treated.
*
* @param aa true to set the antialias bit in the flags, false to clear it
*/
public void setAntiAlias(boolean aa) {
- mArray[mPos] = ANTI_ALIAS | (((aa) ? 1 : 0) << 16);
+ mArray[mPos] = ANTI_ALIAS | (aa ? 1 : 0) << 16;
mPos++;
}
@@ -903,6 +970,7 @@ public class PaintBundle {
/**
* Check all the floats for Nan(id) floats and call listenTo
+ *
* @param context
* @param support
*/
@@ -921,6 +989,7 @@ public class PaintBundle {
context.listensTo(Utils.idFromNan(v), support);
}
break;
+ case COLOR_FILTER_ID:
case COLOR_ID:
context.listensTo(mArray[i++], support);
break;
@@ -940,16 +1009,15 @@ public class PaintBundle {
case ANTI_ALIAS:
break;
- case GRADIENT: {
- // TODO gradients should be handled correctly
- i = callPrintGradient(cmd, mArray, i, new StringBuilder());
- }
+ case GRADIENT:
+ i = callRegisterGradient(cmd, mArray, i, context, support);
}
}
}
/**
* Update variables if any are float ids
+ *
* @param context
*/
public void updateVariables(RemoteContext context) {
@@ -970,6 +1038,7 @@ public class PaintBundle {
mOutArray[i] = fixFloatVar(mArray[i], context);
i++;
break;
+ case COLOR_FILTER_ID:
case COLOR_ID:
mOutArray[i] = fixColor(mArray[i], context);
i++;
@@ -987,12 +1056,12 @@ public class PaintBundle {
case IMAGE_FILTER_QUALITY:
case BLEND_MODE:
case ANTI_ALIAS:
+ case CLEAR_COLOR_FILTER:
break;
- case GRADIENT: {
+ case GRADIENT:
// TODO gradients should be handled correctly
i = updateFloatsInGradient(cmd, mOutArray, mArray, i, context);
- }
}
}
}
@@ -1011,21 +1080,25 @@ public class PaintBundle {
return n;
}
- int updateFloatsInGradient(int cmd, int[] out, int[] array,
- int i,
- RemoteContext context) {
+ int updateFloatsInGradient(int cmd, int[] out, int[] array, int i, RemoteContext context) {
int ret = i;
int type = (cmd >> 16);
+ int control = array[ret++];
+ int len = 0xFF & control; // maximum 256 colors
+ int register = 0xFFFF & (control >> 16);
switch (type) {
- case 0: {
- int len = array[ret++];
+ case LINEAR_GRADIENT:
if (len > 0) {
+
for (int j = 0; j < len; j++) {
+ int color = array[ret];
+ if ((register & (1 << j)) != 0) {
+ out[ret] = fixColor(color, context);
+ }
ret++;
}
}
len = array[ret++];
-
if (len > 0) {
for (int j = 0; j < len; j++) {
out[ret] = fixFloatVar(array[ret], context);
@@ -1044,14 +1117,16 @@ public class PaintBundle {
out[ret] = fixFloatVar(array[ret], context);
ret++;
ret++; // tileMode
- }
-
- break;
- case 1: {
+ break;
+ case RADIAL_GRADIENT:
// RadialGradient
- int len = array[ret++];
if (len > 0) {
+
for (int j = 0; j < len; j++) {
+ int color = array[ret];
+ if ((register & (1 << j)) != 0) {
+ out[ret] = fixColor(color, context);
+ }
ret++;
}
}
@@ -1063,7 +1138,6 @@ public class PaintBundle {
}
}
-
// center
out[ret] = fixFloatVar(array[ret], context);
ret++;
@@ -1073,19 +1147,17 @@ public class PaintBundle {
out[ret] = fixFloatVar(array[ret], context);
ret++;
ret++; // tileMode
-
- }
-
- break;
- case 2: {
+ break;
+ case SWEEP_GRADIENT:
// SweepGradient
- int len = array[ret++];
- int[] colors = null;
if (len > 0) {
- colors = new int[len];
- for (int j = 0; j < colors.length; j++) {
- colors[j] = array[ret++];
+ for (int j = 0; j < len; j++) {
+ int color = array[ret];
+ if ((register & (1 << j)) != 0) {
+ out[ret] = fixColor(color, context);
+ }
+ ret++;
}
}
len = array[ret++];
@@ -1103,14 +1175,12 @@ public class PaintBundle {
ret++;
out[ret] = fixFloatVar(array[ret], context);
ret++;
- }
- break;
- default: {
+
+ break;
+ default:
System.err.println("gradient type unknown");
- }
}
return ret;
}
-
-} \ No newline at end of file
+}
diff --git a/core/java/com/android/internal/widget/remotecompose/core/operations/paint/PaintChangeAdapter.java b/core/java/com/android/internal/widget/remotecompose/core/operations/paint/PaintChangeAdapter.java
index 28fe63a03c67..e2402be4e4d4 100644
--- a/core/java/com/android/internal/widget/remotecompose/core/operations/paint/PaintChangeAdapter.java
+++ b/core/java/com/android/internal/widget/remotecompose/core/operations/paint/PaintChangeAdapter.java
@@ -18,112 +18,73 @@ package com.android.internal.widget.remotecompose.core.operations.paint;
public class PaintChangeAdapter implements PaintChanges {
@Override
- public void setTextSize(float size) {
-
- }
+ public void setTextSize(float size) {}
@Override
- public void setTypeFace(int fontType, int weight, boolean italic) {
-
- }
+ public void setTypeFace(int fontType, int weight, boolean italic) {}
@Override
- public void setStrokeWidth(float width) {
-
- }
+ public void setStrokeWidth(float width) {}
@Override
- public void setColor(int color) {
-
- }
+ public void setColor(int color) {}
@Override
- public void setStrokeCap(int cap) {
-
- }
+ public void setStrokeCap(int cap) {}
@Override
- public void setStyle(int style) {
-
- }
+ public void setStyle(int style) {}
@Override
- public void setShader(int shader) {
-
- }
+ public void setShader(int shader) {}
@Override
- public void setImageFilterQuality(int quality) {
-
- }
+ public void setImageFilterQuality(int quality) {}
@Override
- public void setAlpha(float a) {
-
- }
+ public void setAlpha(float a) {}
@Override
- public void setStrokeMiter(float miter) {
-
- }
+ public void setStrokeMiter(float miter) {}
@Override
- public void setStrokeJoin(int join) {
-
- }
+ public void setStrokeJoin(int join) {}
@Override
- public void setFilterBitmap(boolean filter) {
-
- }
+ public void setFilterBitmap(boolean filter) {}
@Override
- public void setBlendMode(int blendmode) {
-
- }
+ public void setBlendMode(int blendmode) {}
@Override
- public void setAntiAlias(boolean aa) {
-
- }
+ public void setAntiAlias(boolean aa) {}
@Override
- public void clear(long mask) {
-
- }
+ public void clear(long mask) {}
@Override
- public void setLinearGradient(int[] colorsArray,
- float[] stopsArray,
- float startX,
- float startY,
- float endX,
- float endY,
- int tileMode) {
-
- }
+ public void setLinearGradient(
+ int[] colorsArray,
+ float[] stopsArray,
+ float startX,
+ float startY,
+ float endX,
+ float endY,
+ int tileMode) {}
@Override
- public void setRadialGradient(int[] colorsArray,
- float[] stopsArray,
- float centerX,
- float centerY,
- float radius,
- int tileMode) {
-
- }
+ public void setRadialGradient(
+ int[] colorsArray,
+ float[] stopsArray,
+ float centerX,
+ float centerY,
+ float radius,
+ int tileMode) {}
@Override
- public void setSweepGradient(int[] colorsArray,
- float[] stopsArray,
- float centerX,
- float centerY) {
-
- }
+ public void setSweepGradient(
+ int[] colorsArray, float[] stopsArray, float centerX, float centerY) {}
@Override
- public void setColorFilter(int color, int mode) {
-
- }
-
+ public void setColorFilter(int color, int mode) {}
}
diff --git a/core/java/com/android/internal/widget/remotecompose/core/operations/paint/PaintChanges.java b/core/java/com/android/internal/widget/remotecompose/core/operations/paint/PaintChanges.java
index d5dc3889add3..486d763cb6d5 100644
--- a/core/java/com/android/internal/widget/remotecompose/core/operations/paint/PaintChanges.java
+++ b/core/java/com/android/internal/widget/remotecompose/core/operations/paint/PaintChanges.java
@@ -15,10 +15,7 @@
*/
package com.android.internal.widget.remotecompose.core.operations.paint;
-/**
- * Interface to a paint object
- * For more details see Android Paint
- */
+/** Interface to a paint object For more details see Android Paint */
public interface PaintChanges {
// MASK to be set/cleared
@@ -30,8 +27,7 @@ public interface PaintChanges {
int CLEAR_CAP = 1 << (PaintBundle.STROKE_CAP - 1);
int CLEAR_STYLE = 1 << (PaintBundle.STYLE - 1);
int CLEAR_SHADER = 1 << (PaintBundle.SHADER - 1);
- int CLEAR_IMAGE_FILTER_QUALITY =
- 1 << (PaintBundle.IMAGE_FILTER_QUALITY - 1);
+ int CLEAR_IMAGE_FILTER_QUALITY = 1 << (PaintBundle.IMAGE_FILTER_QUALITY - 1);
int CLEAR_RADIENT = 1 << (PaintBundle.GRADIENT - 1);
int CLEAR_ALPHA = 1 << (PaintBundle.ALPHA - 1);
int CLEAR_COLOR_FILTER = 1 << (PaintBundle.COLOR_FILTER - 1);
@@ -39,91 +35,105 @@ public interface PaintChanges {
/**
* Set the size of text
+ *
* @param size
*/
void setTextSize(float size);
/**
* Set the width of lines
+ *
* @param width
*/
void setStrokeWidth(float width);
/**
* Set the color to use
+ *
* @param color
*/
void setColor(int color);
/**
* Set the Stroke Cap
+ *
* @param cap
*/
void setStrokeCap(int cap);
/**
* Set the Stroke style FILL and/or STROKE
+ *
* @param style
*/
void setStyle(int style);
/**
* Set the id of the shader to use
+ *
* @param shader
*/
void setShader(int shader);
/**
* Set the way image is interpolated
+ *
* @param quality
*/
void setImageFilterQuality(int quality);
/**
* Set the alpha to draw under
+ *
* @param a
*/
void setAlpha(float a);
/**
* Set the Stroke Miter
+ *
* @param miter
*/
void setStrokeMiter(float miter);
/**
* Set the Stroke Join
+ *
* @param join
*/
void setStrokeJoin(int join);
/**
* Should bitmaps be interpolated
+ *
* @param filter
*/
void setFilterBitmap(boolean filter);
/**
* Set the blend mode can be porterduff + others
+ *
* @param mode
*/
void setBlendMode(int mode);
/**
- * Set the AntiAlias. Typically true
- * Set to off when you need pixilated look (e.g. QR codes)
+ * Set the AntiAlias. Typically true Set to off when you need pixilated look (e.g. QR codes)
+ *
* @param aa
*/
void setAntiAlias(boolean aa);
/**
* Clear some sub set of the settings
+ *
* @param mask
*/
void clear(long mask);
/**
* Set a linear gradient fill
+ *
* @param colorsArray
* @param stopsArray
* @param startX
@@ -139,11 +149,11 @@ public interface PaintChanges {
float startY,
float endX,
float endY,
- int tileMode
- );
+ int tileMode);
/**
* Set a radial gradient fill
+ *
* @param colorsArray
* @param stopsArray
* @param centerX
@@ -157,36 +167,32 @@ public interface PaintChanges {
float centerX,
float centerY,
float radius,
- int tileMode
- );
+ int tileMode);
/**
* Set a sweep gradient fill
+ *
* @param colorsArray
* @param stopsArray
* @param centerX
* @param centerY
*/
- void setSweepGradient(
- int[] colorsArray,
- float[] stopsArray,
- float centerX,
- float centerY
- );
+ void setSweepGradient(int[] colorsArray, float[] stopsArray, float centerX, float centerY);
/**
* Set Color filter mod
+ *
* @param color
* @param mode
*/
void setColorFilter(int color, int mode);
/**
- * Set TypeFace 0,1,2
- * TODO above should point to a string to be decoded
+ * Set TypeFace 0,1,2 TODO above should point to a string to be decoded
+ *
* @param fontType
* @param weight
* @param italic
*/
void setTypeFace(int fontType, int weight, boolean italic);
-} \ No newline at end of file
+}
diff --git a/core/java/com/android/internal/widget/remotecompose/core/operations/paint/Painter.java b/core/java/com/android/internal/widget/remotecompose/core/operations/paint/Painter.java
index ada3757bd4ca..9a3cd54c3f85 100644
--- a/core/java/com/android/internal/widget/remotecompose/core/operations/paint/Painter.java
+++ b/core/java/com/android/internal/widget/remotecompose/core/operations/paint/Painter.java
@@ -15,16 +15,11 @@
*/
package com.android.internal.widget.remotecompose.core.operations.paint;
-
-/**
- * Provides a Builder pattern for a PaintBundle
- */
+/** Provides a Builder pattern for a PaintBundle */
class Painter {
PaintBundle mPaint;
- /**
- * Write the paint to the buffer
- */
+ /** Write the paint to the buffer */
public PaintBundle commit() {
return mPaint;
}
@@ -47,8 +42,7 @@ class Painter {
/**
* Set the paint's Join.
*
- * @param join set the paint's Join, used whenever the paint's style is
- * Stroke or StrokeAndFill.
+ * @param join set the paint's Join, used whenever the paint's style is Stroke or StrokeAndFill.
*/
public Painter setStrokeJoin(int join) {
mPaint.setStrokeJoin(join);
@@ -56,12 +50,11 @@ class Painter {
}
/**
- * Set the width for stroking. Pass 0 to stroke in hairline mode.
- * Hairlines always draws a single
- * pixel independent of the canvas's matrix.
+ * Set the width for stroking. Pass 0 to stroke in hairline mode. Hairlines always draws a
+ * single pixel independent of the canvas's matrix.
*
- * @param width set the paint's stroke width, used whenever the paint's
- * style is Stroke or StrokeAndFill.
+ * @param width set the paint's stroke width, used whenever the paint's style is Stroke or
+ * StrokeAndFill.
*/
public Painter setStrokeWidth(float width) {
mPaint.setStrokeWidth(width);
@@ -69,8 +62,8 @@ class Painter {
}
/**
- * Set the paint's style, used for controlling how primitives' geometries
- * are interpreted (except for drawBitmap, which always assumes Fill).
+ * Set the paint's style, used for controlling how primitives' geometries are interpreted
+ * (except for drawBitmap, which always assumes Fill).
*
* @param style The new style to set in the paint
*/
@@ -82,8 +75,8 @@ class Painter {
/**
* Set the paint's Cap.
*
- * @param cap set the paint's line cap style, used whenever the paint's
- * style is Stroke or StrokeAndFill.
+ * @param cap set the paint's line cap style, used whenever the paint's style is Stroke or
+ * StrokeAndFill.
*/
public Painter setStrokeCap(int cap) {
mPaint.setStrokeCap(cap);
@@ -91,11 +84,11 @@ class Painter {
}
/**
- * Set the paint's stroke miter value. This is used to control the behavior
- * of miter joins when the joins angle is sharp. This value must be >= 0.
+ * Set the paint's stroke miter value. This is used to control the behavior of miter joins when
+ * the joins angle is sharp. This value must be >= 0.
*
- * @param miter set the miter limit on the paint, used whenever the paint's
- * style is Stroke or StrokeAndFill.
+ * @param miter set the miter limit on the paint, used whenever the paint's style is Stroke or
+ * StrokeAndFill.
*/
public Painter setStrokeMiter(float miter) {
mPaint.setStrokeMiter(miter);
@@ -103,9 +96,8 @@ class Painter {
}
/**
- * Helper to setColor(), that only assigns the color's alpha value,
- * leaving its r,g,b values unchanged. Results are undefined if the alpha
- * value is outside of the range [0..1.0]
+ * Helper to setColor(), that only assigns the color's alpha value, leaving its r,g,b values
+ * unchanged. Results are undefined if the alpha value is outside of the range [0..1.0]
*
* @param alpha set the alpha component [0..1.0] of the paint's color.
*/
@@ -117,9 +109,8 @@ class Painter {
/**
* Create a color filter that uses the specified color and Porter-Duff mode.
*
- * @param color The ARGB source color used with the specified Porter-Duff
- * mode
- * @param mode The porter-duff mode that is applied
+ * @param color The ARGB source color used with the specified Porter-Duff mode
+ * @param mode The porter-duff mode that is applied
*/
public Painter setPorterDuffColorFilter(int color, int mode) {
mPaint.setColorFilter(color, mode);
@@ -129,17 +120,15 @@ class Painter {
/**
* sets a shader that draws a linear gradient along a line.
*
- * @param startX The x-coordinate for the start of the gradient line
- * @param startY The y-coordinate for the start of the gradient line
- * @param endX The x-coordinate for the end of the gradient line
- * @param endY The y-coordinate for the end of the gradient line
- * @param colors The sRGB colors to be distributed along the gradient
- * line
- * @param positions May be null. The relative positions [0..1] of each
- * corresponding color in the colors array. If this is null,
- * the colors are distributed evenly along the gradient
- * line.
- * @param tileMode The Shader tiling mode
+ * @param startX The x-coordinate for the start of the gradient line
+ * @param startY The y-coordinate for the start of the gradient line
+ * @param endX The x-coordinate for the end of the gradient line
+ * @param endY The y-coordinate for the end of the gradient line
+ * @param colors The sRGB colors to be distributed along the gradient line
+ * @param positions May be null. The relative positions [0..1] of each corresponding color in
+ * the colors array. If this is null, the colors are distributed evenly along the gradient
+ * line.
+ * @param tileMode The Shader tiling mode
*/
public Painter setLinearGradient(
float startX,
@@ -148,29 +137,23 @@ class Painter {
float endY,
int[] colors,
float[] positions,
- int tileMode
- ) {
- mPaint.setLinearGradient(colors, positions, startX,
- startY, endX, endY, tileMode);
+ int tileMode) {
+ mPaint.setLinearGradient(colors, 0, positions, startX, startY, endX, endY, tileMode);
return this;
}
/**
* Sets a shader that draws a radial gradient given the center and radius.
*
- * @param centerX The x-coordinate of the center of the radius
- * @param centerY The y-coordinate of the center of the radius
- * @param radius Must be positive. The radius of the circle for this
- * gradient.
- * @param colors The sRGB colors to be distributed between the center
- * and edge of the circle
- * @param positions May be <code>null</code>. Valid values are between
- * <code>0.0f</code> and
- * <code>1.0f</code>. The relative position of each
- * corresponding color in the colors array. If
- * <code>null</code>, colors are distributed evenly
- * between the center and edge of the circle.
- * @param tileMode The Shader tiling mode
+ * @param centerX The x-coordinate of the center of the radius
+ * @param centerY The y-coordinate of the center of the radius
+ * @param radius Must be positive. The radius of the circle for this gradient.
+ * @param colors The sRGB colors to be distributed between the center and edge of the circle
+ * @param positions May be <code>null</code>. Valid values are between <code>0.0f</code> and
+ * <code>1.0f</code>. The relative position of each corresponding color in the colors array.
+ * If <code>null</code>, colors are distributed evenly between the center and edge of the
+ * circle.
+ * @param tileMode The Shader tiling mode
*/
public Painter setRadialGradient(
float centerX,
@@ -178,33 +161,25 @@ class Painter {
float radius,
int[] colors,
float[] positions,
- int tileMode
- ) {
- mPaint.setRadialGradient(colors, positions, centerX,
- centerY, radius, tileMode);
+ int tileMode) {
+ mPaint.setRadialGradient(colors, 0, positions, centerX, centerY, radius, tileMode);
return this;
}
/**
* Set a shader that draws a sweep gradient around a center point.
*
- * @param centerX The x-coordinate of the center
- * @param centerY The y-coordinate of the center
- * @param colors The sRGB colors to be distributed between around the
- * center. There must be at least 2 colors in the array.
- * @param positions May be NULL. The relative position of each corresponding
- * color in the colors array, beginning with 0 and ending
- * with 1.0. If the values are not monotonic, the drawing
- * may produce unexpected results. If positions is NULL,
- * then the colors are automatically spaced evenly.
+ * @param centerX The x-coordinate of the center
+ * @param centerY The y-coordinate of the center
+ * @param colors The sRGB colors to be distributed between around the center. There must be at
+ * least 2 colors in the array.
+ * @param positions May be NULL. The relative position of each corresponding color in the colors
+ * array, beginning with 0 and ending with 1.0. If the values are not monotonic, the drawing
+ * may produce unexpected results. If positions is NULL, then the colors are automatically
+ * spaced evenly.
*/
- public Painter setSweepGradient(
- float centerX,
- float centerY,
- int[] colors,
- float[] positions
- ) {
- mPaint.setSweepGradient(colors, positions, centerX, centerY);
+ public Painter setSweepGradient(float centerX, float centerY, int[] colors, float[] positions) {
+ mPaint.setSweepGradient(colors, 0, positions, centerX, centerY);
return this;
}
@@ -219,10 +194,11 @@ class Painter {
}
/**
- * sets a typeface object that best matches the specified existing
- * typeface and the specified weight and italic style
+ * sets a typeface object that best matches the specified existing typeface and the specified
+ * weight and italic style
+ *
+ * <p>Below are numerical values and corresponding common weight names.
*
- * <p>Below are numerical values and corresponding common weight names.</p>
* <table> <thead>
* <tr><th>Value</th><th>Common weight name</th></tr> </thead> <tbody>
* <tr><td>100</td><td>Thin</td></tr>
@@ -236,25 +212,21 @@ class Painter {
* <tr><td>900</td><td>Black</td></tr> </tbody> </table>
*
* @param fontType 0 = default 1 = sans serif 2 = serif 3 = monospace
- * @param weight The desired weight to be drawn.
- * @param italic {@code true} if italic style is desired to be drawn.
- * Otherwise, {@code false}
+ * @param weight The desired weight to be drawn.
+ * @param italic {@code true} if italic style is desired to be drawn. Otherwise, {@code false}
*/
public Painter setTypeface(int fontType, int weight, boolean italic) {
mPaint.setTextStyle(fontType, weight, italic);
return this;
}
-
public Painter setFilterBitmap(boolean filter) {
mPaint.setFilterBitmap(filter);
return this;
}
-
public Painter setShader(int id) {
mPaint.setShader(id);
return this;
}
-
}
diff --git a/core/java/com/android/internal/widget/remotecompose/core/operations/utilities/AnimatedFloatExpression.java b/core/java/com/android/internal/widget/remotecompose/core/operations/utilities/AnimatedFloatExpression.java
index 5b295eb700ec..1d673c4c5ab6 100644
--- a/core/java/com/android/internal/widget/remotecompose/core/operations/utilities/AnimatedFloatExpression.java
+++ b/core/java/com/android/internal/widget/remotecompose/core/operations/utilities/AnimatedFloatExpression.java
@@ -15,9 +15,6 @@
*/
package com.android.internal.widget.remotecompose.core.operations.utilities;
-import static com.android.internal.widget.remotecompose.core.operations.utilities.NanMap.ID_REGION_ARRAY;
-import static com.android.internal.widget.remotecompose.core.operations.utilities.NanMap.ID_REGION_MASK;
-
/**
* high performance floating point expression evaluator used in animation
*/
@@ -59,7 +56,6 @@ public class AnimatedFloatExpression {
public static final float RAD = asNan(OFFSET + 30);
public static final float CEIL = asNan(OFFSET + 31);
-
// Array ops
public static final float A_DEREF = asNan(OFFSET + 32);
public static final float A_MAX = asNan(OFFSET + 33);
@@ -69,14 +65,13 @@ public class AnimatedFloatExpression {
public static final float A_LEN = asNan(OFFSET + 37);
public static final int LAST_OP = OFFSET + 37;
-
public static final float VAR1 = asNan(OFFSET + 38);
public static final float VAR2 = asNan(OFFSET + 39);
// TODO CLAMP, CBRT, DEG, RAD, EXPM1, CEIL, FLOOR
- private static final float FP_PI = (float) Math.PI;
- private static final float FP_TO_RAD = 57.29577951f; // 180/PI
- private static final float FP_TO_DEG = 0.01745329252f; // 180/PI
+ // private static final float FP_PI = (float) Math.PI;
+ private static final float FP_TO_RAD = 57.29578f; // 180/PI
+ private static final float FP_TO_DEG = 0.017453292f; // 180/PI
float[] mStack;
float[] mLocalStack = new float[128];
@@ -106,20 +101,13 @@ public class AnimatedFloatExpression {
int eval(int sp);
}
-
/**
- * Evaluate a float expression
- * This system works by processing an Array of float (float[])
- * in reverse polish notation (rpn)
- * Within that array some floats are commands
- * they are encoded within an NaN.
- * After processing the array the last item on the array is returned.
- * The system supports variables allowing expressions like.
- * sin(sqrt(x*x+y*y))/sqrt(x*x+y*y)
- * Where x & y are passe as parameters
- * Examples:
- * (1+2) (1, 2, ADD) adds two numbers returns 3
- * eval(new float[]{ Var1, Var * }
+ * Evaluate a float expression This system works by processing an Array of float (float[]) in
+ * reverse polish notation (rpn) Within that array some floats are commands they are encoded
+ * within an NaN. After processing the array the last item on the array is returned. The system
+ * supports variables allowing expressions like. sin(sqrt(x*x+y*y))/sqrt(x*x+y*y) Where x & y
+ * are passe as parameters Examples: (1+2) (1, 2, ADD) adds two numbers returns 3 eval(new
+ * float[]{ Var1, Var * }
*
* @param exp
* @param var
@@ -140,7 +128,6 @@ public class AnimatedFloatExpression {
return mStack[sp];
}
-
/**
* Evaluate a float expression
*
@@ -149,8 +136,9 @@ public class AnimatedFloatExpression {
* @param var
* @return
*/
- public float eval(CollectionsAccess ca, float[] exp, float... var) {
- mStack = exp;
+ public float eval(CollectionsAccess ca, float[] exp, int len, float... var) {
+ System.arraycopy(exp, 0, mLocalStack, 0, len);
+ mStack = mLocalStack;
mVar = var;
mCollectionsAccess = ca;
int sp = -1;
@@ -159,7 +147,7 @@ public class AnimatedFloatExpression {
float v = mStack[i];
if (Float.isNaN(v)) {
int id = fromNaN(v);
- if ((id & ID_REGION_MASK) != ID_REGION_ARRAY) {
+ if ((id & NanMap.ID_REGION_MASK) != NanMap.ID_REGION_ARRAY) {
sp = mOps[id - OFFSET].eval(sp);
} else {
mStack[++sp] = v;
@@ -171,6 +159,34 @@ public class AnimatedFloatExpression {
return mStack[sp];
}
+ /**
+ * Evaluate a float expression
+ *
+ * @param ca
+ * @param exp
+ * @return
+ */
+ public float eval(CollectionsAccess ca, float[] exp, int len) {
+ System.arraycopy(exp, 0, mLocalStack, 0, len);
+ mStack = mLocalStack;
+ mCollectionsAccess = ca;
+ int sp = -1;
+
+ for (int i = 0; i < len; i++) {
+ float v = mStack[i];
+ if (Float.isNaN(v)) {
+ int id = fromNaN(v);
+ if ((id & NanMap.ID_REGION_MASK) != NanMap.ID_REGION_ARRAY) {
+ sp = mOps[id - OFFSET].eval(sp);
+ } else {
+ mStack[++sp] = v;
+ }
+ } else {
+ mStack[++sp] = v;
+ }
+ }
+ return mStack[sp];
+ }
private int dereference(CollectionsAccess ca, int id, int sp) {
mStack[sp] = ca.getFloatValue(id, (int) (mStack[sp]));
@@ -238,11 +254,11 @@ public class AnimatedFloatExpression {
mStack[sp - 1] = mStack[sp - 1] * mStack[sp];
return sp - 1;
},
- (sp) -> { // DIV
+ (sp) -> { // DIV
mStack[sp - 1] = mStack[sp - 1] / mStack[sp];
return sp - 1;
},
- (sp) -> { // MOD
+ (sp) -> { // MOD
mStack[sp - 1] = mStack[sp - 1] % mStack[sp];
return sp - 1;
},
@@ -327,13 +343,11 @@ public class AnimatedFloatExpression {
return sp - 2;
},
(sp) -> { // Ternary conditional
- mStack[sp - 2] = (mStack[sp] > 0)
- ? mStack[sp - 1] : mStack[sp - 2];
+ mStack[sp - 2] = (mStack[sp] > 0) ? mStack[sp - 1] : mStack[sp - 2];
return sp - 2;
},
(sp) -> { // CLAMP(min,max, val)
- mStack[sp - 2] = Math.min(Math.max(mStack[sp - 2], mStack[sp]),
- mStack[sp - 1]);
+ mStack[sp - 2] = Math.min(Math.max(mStack[sp - 2], mStack[sp]), mStack[sp - 1]);
return sp - 2;
},
(sp) -> { // CBRT cuberoot
@@ -354,7 +368,7 @@ public class AnimatedFloatExpression {
},
(sp) -> { // A_DEREF
int id = fromNaN(mStack[sp]);
- mStack[sp] = mCollectionsAccess.getFloatValue(id, (int) (mStack[sp - 1]));
+ mStack[sp] = mCollectionsAccess.getFloatValue(id, (int) mStack[sp - 1]);
return sp - 1;
},
(sp) -> { // A_MAX
@@ -399,11 +413,9 @@ public class AnimatedFloatExpression {
},
(sp) -> { // A_LEN
int id = fromNaN(mStack[sp]);
- mStack[sp] = mCollectionsAccess.getFloatsLength(id);
+ mStack[sp] = mCollectionsAccess.getListLength(id);
return sp;
},
-
-
(sp) -> { // first var =
mStack[sp] = mVar[0];
return sp;
@@ -492,7 +504,8 @@ public class AnimatedFloatExpression {
s.append(toMathName(v));
} else {
int id = fromNaN(v);
- String idString = (id > ID_REGION_ARRAY) ? ("A_" + (id & 0xFFFFF)) : "" + id;
+ String idString =
+ (id > NanMap.ID_REGION_ARRAY) ? ("A_" + (id & 0xFFFFF)) : "" + id;
s.append("[");
s.append(idString);
s.append("]");
@@ -513,7 +526,7 @@ public class AnimatedFloatExpression {
}
static String toString(float[] exp, int sp) {
- String[] str = new String[exp.length];
+ // String[] str = new String[exp.length];
if (Float.isNaN(exp[sp])) {
int id = fromNaN(exp[sp]) - OFFSET;
switch (NO_OF_OPS[id]) {
@@ -523,24 +536,38 @@ public class AnimatedFloatExpression {
return sNames.get(id) + "(" + toString(exp, sp + 1) + ") ";
case 2:
if (infix(id)) {
- return "(" + toString(exp, sp + 1)
- + sNames.get(id) + " "
- + toString(exp, sp + 2) + ") ";
+ return "("
+ + toString(exp, sp + 1)
+ + sNames.get(id)
+ + " "
+ + toString(exp, sp + 2)
+ + ") ";
} else {
- return sNames.get(id) + "("
- + toString(exp, sp + 1) + ", "
- + toString(exp, sp + 2) + ")";
+ return sNames.get(id)
+ + "("
+ + toString(exp, sp + 1)
+ + ", "
+ + toString(exp, sp + 2)
+ + ")";
}
case 3:
if (infix(id)) {
- return "((" + toString(exp, sp + 1) + ") ? "
- + toString(exp, sp + 2) + ":"
- + toString(exp, sp + 3) + ")";
+ return "(("
+ + toString(exp, sp + 1)
+ + ") ? "
+ + toString(exp, sp + 2)
+ + ":"
+ + toString(exp, sp + 3)
+ + ")";
} else {
return sNames.get(id)
- + "(" + toString(exp, sp + 1)
- + ", " + toString(exp, sp + 2)
- + ", " + toString(exp, sp + 3) + ")";
+ + "("
+ + toString(exp, sp + 1)
+ + ", "
+ + toString(exp, sp + 2)
+ + ", "
+ + toString(exp, sp + 3)
+ + ")";
}
}
}
@@ -549,12 +576,40 @@ public class AnimatedFloatExpression {
static final int[] NO_OF_OPS = {
-1, // no op
- 2, 2, 2, 2, 2, // + - * / %
- 2, 2, 2, // min max, power
- 1, 1, 1, 1, 1, 1, 1, 1, //sqrt,abs,CopySign,exp,floor,log,ln
- 1, 1, 1, 1, 1, 1, 1, 2, // round,sin,cos,tan,asin,acos,atan,atan2
- 3, 3, 3, 1, 1, 1, 1,
- 0, 0, 0 // mad, ?:,
+ 2,
+ 2,
+ 2,
+ 2,
+ 2, // + - * / %
+ 2,
+ 2,
+ 2, // min max, power
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1, // sqrt,abs,CopySign,exp,floor,log,ln
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 2, // round,sin,cos,tan,asin,acos,atan,atan2
+ 3,
+ 3,
+ 3,
+ 1,
+ 1,
+ 1,
+ 1,
+ 0,
+ 0,
+ 0 // mad, ?:,
// a[0],a[1],a[2]
};
diff --git a/core/java/com/android/internal/widget/remotecompose/core/operations/utilities/ArrayAccess.java b/core/java/com/android/internal/widget/remotecompose/core/operations/utilities/ArrayAccess.java
index 4c7cc3889bcb..eb5e4828f2a0 100644
--- a/core/java/com/android/internal/widget/remotecompose/core/operations/utilities/ArrayAccess.java
+++ b/core/java/com/android/internal/widget/remotecompose/core/operations/utilities/ArrayAccess.java
@@ -15,10 +15,21 @@
*/
package com.android.internal.widget.remotecompose.core.operations.utilities;
+/**
+ * Support a standardized interface to commands that contain arrays All commands that implement
+ * array access will be collected in a map in the state TODO refactor to DataAccess,
+ * FloatArrayAccess, ListAccess, MapAccess
+ */
public interface ArrayAccess {
float getFloatValue(int index);
+
+ default int getId(int index) {
+ return 0;
+ }
+
float[] getFloats();
- int getFloatsLength();
+
+ int getLength();
default int getIntValue(int index) {
return (int) getFloatValue(index);
diff --git a/core/java/com/android/internal/widget/remotecompose/core/operations/utilities/CollectionsAccess.java b/core/java/com/android/internal/widget/remotecompose/core/operations/utilities/CollectionsAccess.java
index 9e5312676aa4..0128253e1f90 100644
--- a/core/java/com/android/internal/widget/remotecompose/core/operations/utilities/CollectionsAccess.java
+++ b/core/java/com/android/internal/widget/remotecompose/core/operations/utilities/CollectionsAccess.java
@@ -15,12 +15,20 @@
*/
package com.android.internal.widget.remotecompose.core.operations.utilities;
+/**
+ * interface to allow expressions to access collections Todo define a convention for when access is
+ * unavailable
+ */
public interface CollectionsAccess {
float getFloatValue(int id, int index);
+
float[] getFloats(int id);
- int getFloatsLength(int id);
- default int getIntValue(int id, int index) {
- return (int) getFloatValue(id, index);
+ int getListLength(int id);
+
+ int getId(int listId, int index);
+
+ default int getIntValue(int listId, int index) {
+ return (int) getFloatValue(listId, index);
}
}
diff --git a/core/java/com/android/internal/widget/remotecompose/core/operations/utilities/ColorUtils.java b/core/java/com/android/internal/widget/remotecompose/core/operations/utilities/ColorUtils.java
index fb96ceffa58a..937a25d2cda6 100644
--- a/core/java/com/android/internal/widget/remotecompose/core/operations/utilities/ColorUtils.java
+++ b/core/java/com/android/internal/widget/remotecompose/core/operations/utilities/ColorUtils.java
@@ -16,21 +16,14 @@
package com.android.internal.widget.remotecompose.core.operations.utilities;
/**
- * These are tools to use long Color as variables
- * long colors are stored a 0xXXXXXXXX XXXXXX??
- * in SRGB the colors are stored 0xAARRGGBB,00000000
- * SRGB color sapce is color space 0
- * Our Color will use color float with a
- * Current android supports
- * SRGB, LINEAR_SRGB, EXTENDED_SRGB, LINEAR_EXTENDED_SRGB, BT709, BT2020,
- * DCI_P3, DISPLAY_P3, NTSC_1953, SMPTE_C, ADOBE_RGB, PRO_PHOTO_RGB, ACES,
- * ACESCG, CIE_XYZ, CIE_LAB, BT2020_HLG, BT2020_PQ 0..17 respectively
- *
- * Our color space will be 62 (MAX_ID-1). (0x3E)
- * Storing the default value in SRGB format and having the
- * id of the color between the ARGB values and the 62 i.e.
- * 0xAARRGGBB 00 00 00 3E
+ * These are tools to use long Color as variables long colors are stored a 0xXXXXXXXX XXXXXX?? in
+ * SRGB the colors are stored 0xAARRGGBB,00000000 SRGB color sapce is color space 0 Our Color will
+ * use color float with a Current android supports SRGB, LINEAR_SRGB, EXTENDED_SRGB,
+ * LINEAR_EXTENDED_SRGB, BT709, BT2020, DCI_P3, DISPLAY_P3, NTSC_1953, SMPTE_C, ADOBE_RGB,
+ * PRO_PHOTO_RGB, ACES, ACESCG, CIE_XYZ, CIE_LAB, BT2020_HLG, BT2020_PQ 0..17 respectively
*
+ * <p>Our color space will be 62 (MAX_ID-1). (0x3E) Storing the default value in SRGB format and
+ * having the id of the color between the ARGB values and the 62 i.e. 0xAARRGGBB 00 00 00 3E
*/
public class ColorUtils {
public static int RC_COLOR = 62;
@@ -53,6 +46,7 @@ public class ColorUtils {
/**
* get default color from long color
+ *
* @param color
* @return
*/
diff --git a/core/java/com/android/internal/widget/remotecompose/core/operations/utilities/DataMap.java b/core/java/com/android/internal/widget/remotecompose/core/operations/utilities/DataMap.java
new file mode 100644
index 000000000000..24f17d7b00c7
--- /dev/null
+++ b/core/java/com/android/internal/widget/remotecompose/core/operations/utilities/DataMap.java
@@ -0,0 +1,46 @@
+/*
+ * 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.
+ */
+package com.android.internal.widget.remotecompose.core.operations.utilities;
+
+public class DataMap {
+ public String[] mNames;
+ public int[] mIds;
+ public byte[] mTypes;
+
+ public DataMap(String[] names, byte[] types, int[] ids) {
+ mNames = names;
+ mTypes = types;
+ mIds = ids;
+ }
+
+ public int getPos(String str) {
+ for (int i = 0; i < mNames.length; i++) {
+ String name = mNames[i];
+ if (str.equals(name)) {
+ return i;
+ }
+ }
+ return -1;
+ }
+
+ public byte getType(int pos) {
+ return mTypes[pos];
+ }
+
+ public int getId(int pos) {
+ return mIds[pos];
+ }
+}
diff --git a/core/java/com/android/internal/widget/remotecompose/core/operations/utilities/ImageScaling.java b/core/java/com/android/internal/widget/remotecompose/core/operations/utilities/ImageScaling.java
new file mode 100644
index 000000000000..00c87c1f9c80
--- /dev/null
+++ b/core/java/com/android/internal/widget/remotecompose/core/operations/utilities/ImageScaling.java
@@ -0,0 +1,226 @@
+/*
+ * 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.
+ */
+package com.android.internal.widget.remotecompose.core.operations.utilities;
+
+/** Implement the scaling logic for Compose Image or ImageView */
+public class ImageScaling {
+
+ private static final boolean DEBUG = false;
+
+ public static final int SCALE_NONE = 0;
+ public static final int SCALE_INSIDE = 1;
+ public static final int SCALE_FILL_WIDTH = 2;
+ public static final int SCALE_FILL_HEIGHT = 3;
+ public static final int SCALE_FIT = 4;
+ public static final int SCALE_CROP = 5;
+ public static final int SCALE_FILL_BOUNDS = 6;
+ public static final int SCALE_FIXED_SCALE = 7;
+
+ private float mSrcLeft;
+ private float mSrcTop;
+ private float mSrcRight;
+ private float mSrcBottom;
+ private float mDstLeft;
+ private float mDstTop;
+ private float mDstRight;
+ private float mDstBottom;
+ private float mScaleFactor;
+ private int mScaleType;
+
+ public float mFinalDstLeft;
+ public float mFinalDstTop;
+ public float mFinalDstRight;
+ public float mFinalDstBottom;
+
+ public ImageScaling() {}
+
+ public ImageScaling(
+ float srcLeft,
+ float srcTop,
+ float srcRight,
+ float srcBottom,
+ float dstLeft,
+ float dstTop,
+ float dstRight,
+ float dstBottom,
+ int type,
+ float scale) {
+
+ mSrcLeft = srcLeft;
+ mSrcTop = srcTop;
+ mSrcRight = srcRight;
+ mSrcBottom = srcBottom;
+ mDstLeft = dstLeft;
+ mDstTop = dstTop;
+ mDstRight = dstRight;
+ mDstBottom = dstBottom;
+ mScaleType = type;
+ mScaleFactor = scale;
+ adjustDrawToType();
+ }
+
+ public void setup(
+ float srcLeft,
+ float srcTop,
+ float srcRight,
+ float srcBottom,
+ float dstLeft,
+ float dstTop,
+ float dstRight,
+ float dstBottom,
+ int type,
+ float scale) {
+
+ mSrcLeft = srcLeft;
+ mSrcTop = srcTop;
+ mSrcRight = srcRight;
+ mSrcBottom = srcBottom;
+ mDstLeft = dstLeft;
+ mDstTop = dstTop;
+ mDstRight = dstRight;
+ mDstBottom = dstBottom;
+ mScaleType = type;
+ mScaleFactor = scale;
+ adjustDrawToType();
+ }
+
+ static String str(float v) {
+ String s = " " + (int) v;
+ return s.substring(s.length() - 3);
+ }
+
+ void print(String str, float left, float top, float right, float bottom) {
+ String s = str;
+ s += str(left) + ", " + str(top) + ", " + str(right) + ", " + str(bottom) + ", ";
+ s += " [" + str(right - left) + " x " + str(bottom - top) + "]";
+ System.out.println(s);
+ }
+
+ /** This adjust destnation on the DrawBitMapInt to support all contentScale types */
+ private void adjustDrawToType() {
+ int sw = (int) (mSrcRight - mSrcLeft);
+ int sh = (int) (mSrcBottom - mSrcTop);
+ float width = mDstRight - mDstLeft;
+ float height = mDstBottom - mDstTop;
+ int dw = (int) width;
+ int dh = (int) height;
+ int dLeft = 0;
+ int dRight = dw;
+ int dTop = 0;
+ int dBottom = dh;
+ if (DEBUG) {
+ print("test rc ", mSrcLeft, mSrcTop, mSrcRight, mSrcBottom);
+ print("test dst ", mDstLeft, mDstTop, mDstRight, mDstBottom);
+ }
+
+ switch (mScaleType) {
+ case SCALE_NONE:
+ dh = sh;
+ dw = sw;
+ dTop = ((int) height - dh) / 2;
+ dBottom = dh + dTop;
+ dLeft = ((int) width - dw) / 2;
+ dRight = dw + dLeft;
+ break;
+ case SCALE_INSIDE:
+ if (dh > sh && dw > sw) {
+ dh = sh;
+ dw = sw;
+ } else if (sw * height > width * sh) { // width dominated
+ dh = (dw * sh) / sw;
+ } else {
+ dw = (dh * sw) / sh;
+ }
+ dTop = ((int) height - dh) / 2;
+ dBottom = dh + dTop;
+ dLeft = ((int) width - dw) / 2;
+ dRight = dw + dLeft;
+ break;
+ case SCALE_FILL_WIDTH:
+ dh = (dw * sh) / sw;
+
+ dTop = ((int) height - dh) / 2;
+ dBottom = dh + dTop;
+ dLeft = ((int) width - dw) / 2;
+ dRight = dw + dLeft;
+ break;
+ case SCALE_FILL_HEIGHT:
+ dw = (dh * sw) / sh;
+
+ dTop = ((int) height - dh) / 2;
+ dBottom = dh + dTop;
+ dLeft = ((int) width - dw) / 2;
+ dRight = dw + dLeft;
+ break;
+ case SCALE_FIT:
+ if (sw * height > width * sh) { // width dominated
+ dh = (dw * sh) / sw;
+ dTop = ((int) height - dh) / 2;
+ dBottom = dh + dTop;
+ } else {
+ dw = (dh * sw) / sh;
+ dLeft = ((int) width - dw) / 2;
+ dRight = dw + dLeft;
+ }
+ break;
+ case SCALE_CROP:
+ if (sw * height < width * sh) { // width dominated
+ dh = (dw * sh) / sw;
+ dTop = ((int) height - dh) / 2;
+ dBottom = dh + dTop;
+ } else {
+ dw = (dh * sw) / sh;
+ dLeft = ((int) width - dw) / 2;
+ dRight = dw + dLeft;
+ }
+ break;
+ case SCALE_FILL_BOUNDS:
+ // do nothing
+ break;
+ case SCALE_FIXED_SCALE:
+ dh = (int) (sh * mScaleFactor);
+ dw = (int) (sw * mScaleFactor);
+ dTop = ((int) height - dh) / 2;
+ dBottom = dh + dTop;
+ dLeft = ((int) width - dw) / 2;
+ dRight = dw + dLeft;
+ break;
+ }
+
+ mFinalDstRight = dRight + mDstLeft;
+ mFinalDstLeft = dLeft + mDstLeft;
+ mFinalDstBottom = dBottom + mDstTop;
+ mFinalDstTop = dTop + mDstTop;
+
+ if (DEBUG) {
+ print("test out ", mFinalDstLeft, mFinalDstTop, mFinalDstRight, mFinalDstBottom);
+ }
+ }
+
+ public static String typeToString(int type) {
+ String[] typeString = {
+ "none",
+ "inside",
+ "fill_width",
+ "fill_height",
+ "fit",
+ "crop",
+ "fill_bounds",
+ "fixed_scale"
+ };
+ return typeString[type];
+ }
+}
diff --git a/core/java/com/android/internal/widget/remotecompose/core/operations/utilities/IntFloatMap.java b/core/java/com/android/internal/widget/remotecompose/core/operations/utilities/IntFloatMap.java
index b2d714e08a0e..3b4ece937e58 100644
--- a/core/java/com/android/internal/widget/remotecompose/core/operations/utilities/IntFloatMap.java
+++ b/core/java/com/android/internal/widget/remotecompose/core/operations/utilities/IntFloatMap.java
@@ -32,9 +32,7 @@ public class IntFloatMap {
mValues = new float[DEFAULT_CAPACITY];
}
- /**
- * clear the map
- */
+ /** clear the map */
public void clear() {
Arrays.fill(mKeys, NOT_PRESENT);
Arrays.fill(mValues, Float.NaN); // not strictly necessary but defensive
@@ -78,8 +76,7 @@ public class IntFloatMap {
int index = findKey(key);
if (index == -1) {
return 0;
- } else
- return mValues[index];
+ } else return mValues[index];
}
/**
diff --git a/core/java/com/android/internal/widget/remotecompose/core/operations/utilities/IntIntMap.java b/core/java/com/android/internal/widget/remotecompose/core/operations/utilities/IntIntMap.java
index 606dc785eb20..68cd0e655028 100644
--- a/core/java/com/android/internal/widget/remotecompose/core/operations/utilities/IntIntMap.java
+++ b/core/java/com/android/internal/widget/remotecompose/core/operations/utilities/IntIntMap.java
@@ -31,9 +31,7 @@ public class IntIntMap {
mValues = new int[DEFAULT_CAPACITY];
}
- /**
- * clear the map
- */
+ /** clear the map */
public void clear() {
Arrays.fill(mKeys, NOT_PRESENT);
Arrays.fill(mValues, 0);
@@ -77,8 +75,7 @@ public class IntIntMap {
int index = findKey(key);
if (index == -1) {
return 0;
- } else
- return mValues[index];
+ } else return mValues[index];
}
/**
diff --git a/core/java/com/android/internal/widget/remotecompose/core/operations/utilities/IntMap.java b/core/java/com/android/internal/widget/remotecompose/core/operations/utilities/IntMap.java
index 0512fa6be710..84e78431790a 100644
--- a/core/java/com/android/internal/widget/remotecompose/core/operations/utilities/IntMap.java
+++ b/core/java/com/android/internal/widget/remotecompose/core/operations/utilities/IntMap.java
@@ -42,7 +42,7 @@ public class IntMap<T> {
mSize = 0;
}
- public T put(int key, T value) {
+ public T put(int key, T value) {
if (key == NOT_PRESENT) throw new IllegalArgumentException("Key cannot be NOT_PRESENT");
if (mSize > mKeys.length * LOAD_FACTOR) {
resize();
@@ -50,24 +50,23 @@ public class IntMap<T> {
return insert(key, value);
}
- public T get(int key) {
+ public T get(int key) {
int index = findKey(key);
if (index == -1) {
- return null;
- } else
- return mValues.get(index);
+ return null;
+ } else return mValues.get(index);
}
public int size() {
return mSize;
}
- private T insert(int key, T value) {
+ private T insert(int key, T value) {
int index = hash(key) % mKeys.length;
while (mKeys[index] != NOT_PRESENT && mKeys[index] != key) {
index = (index + 1) % mKeys.length;
}
- T oldValue = null;
+ T oldValue = null;
if (mKeys[index] == NOT_PRESENT) {
mSize++;
} else {
@@ -78,7 +77,7 @@ public class IntMap<T> {
return oldValue;
}
- private int findKey(int key) {
+ private int findKey(int key) {
int index = hash(key) % mKeys.length;
while (mKeys[index] != NOT_PRESENT) {
if (mKeys[index] == key) {
@@ -89,11 +88,11 @@ public class IntMap<T> {
return -1;
}
- private int hash(int key) {
+ private int hash(int key) {
return key;
}
- private void resize() {
+ private void resize() {
int[] oldKeys = mKeys;
ArrayList<T> oldValues = mValues;
mKeys = new int[(oldKeys.length * 2)];
@@ -111,4 +110,46 @@ public class IntMap<T> {
}
}
}
+
+ public T remove(int key) {
+ int index = hash(key) % mKeys.length;
+ int initialIndex = index;
+
+ while (mKeys[index] != NOT_PRESENT) {
+ if (mKeys[index] == key) {
+ T oldValue = mValues.get(index);
+ mKeys[index] = NOT_PRESENT;
+ mValues.set(index, null);
+ mSize--;
+
+ // Rehash the cluster of keys following the removed key
+ rehashFrom((index + 1) % mKeys.length);
+ return oldValue;
+ }
+ index = (index + 1) % mKeys.length;
+ if (index == initialIndex) {
+ break; // Avoid infinite loop
+ }
+ }
+ return null; // Key not found
+ }
+
+ private void rehashFrom(int startIndex) {
+ int index = startIndex;
+
+ while (mKeys[index] != NOT_PRESENT) {
+ int keyToRehash = mKeys[index];
+ T valueToRehash = mValues.get(index);
+
+ // Remove the key-value pair from the current position
+ mKeys[index] = NOT_PRESENT;
+ mValues.set(index, null);
+ mSize--;
+
+ // Re-insert the key-value pair
+ insert(keyToRehash, valueToRehash);
+
+ index = (index + 1) % mKeys.length;
+ }
+ }
}
diff --git a/core/java/com/android/internal/widget/remotecompose/core/operations/utilities/IntegerExpressionEvaluator.java b/core/java/com/android/internal/widget/remotecompose/core/operations/utilities/IntegerExpressionEvaluator.java
index ae61ec1e5eec..baa144d6b28d 100644
--- a/core/java/com/android/internal/widget/remotecompose/core/operations/utilities/IntegerExpressionEvaluator.java
+++ b/core/java/com/android/internal/widget/remotecompose/core/operations/utilities/IntegerExpressionEvaluator.java
@@ -18,8 +18,8 @@ package com.android.internal.widget.remotecompose.core.operations.utilities;
/**
* High performance Integer expression evaluator
*
- * The evaluation is based on int opMask, int[]exp
- * exp[i] is an operator if (opMask*(1 << i) != 0)
+ * <p>The evaluation is based on int opMask, int[]exp exp[i] is an operator if (opMask*(1 << i) !=
+ * 0)
*/
public class IntegerExpressionEvaluator {
static IntMap<String> sNames = new IntMap<>();
@@ -51,26 +51,25 @@ public class IntegerExpressionEvaluator {
public static final int I_IFELSE = OFFSET + 22;
public static final int I_MAD = OFFSET + 23;
- public static final float LAST_OP = 24;
+ public static final float LAST_OP = 25;
public static final int I_VAR1 = OFFSET + 24;
- public static final int I_VAR2 = OFFSET + 24;
-
+ public static final int I_VAR2 = OFFSET + 25;
int[] mStack;
int[] mLocalStack = new int[128];
int[] mVar;
-
interface Op {
int eval(int sp);
}
/**
* Evaluate an integer expression
+ *
* @param mask bits that are operators
- * @param exp rpn sequence of values and operators
- * @param var variables if the expression is a function
+ * @param exp rpn sequence of values and operators
+ * @param var variables if the expression is a function
* @return return the results of evaluating the expression
*/
public int eval(int mask, int[] exp, int... var) {
@@ -90,10 +89,11 @@ public class IntegerExpressionEvaluator {
/**
* Evaluate a integer expression
+ *
* @param mask bits that are operators
- * @param exp rpn sequence of values and operators
- * @param len the number of values in the expression
- * @param var variables if the expression is a function
+ * @param exp rpn sequence of values and operators
+ * @param len the number of values in the expression
+ * @param var variables if the expression is a function
* @return return the results of evaluating the expression
*/
public int eval(int mask, int[] exp, int len, int... var) {
@@ -114,9 +114,10 @@ public class IntegerExpressionEvaluator {
/**
* Evaluate a int expression
+ *
* @param opMask bits that are operators
- * @param exp rpn sequence of values and operators
- * @param var variables if the expression is a function
+ * @param exp rpn sequence of values and operators
+ * @param var variables if the expression is a function
* @return return the results of evaluating the expression
*/
public int evalDB(int opMask, int[] exp, int... var) {
@@ -150,11 +151,11 @@ public class IntegerExpressionEvaluator {
mStack[sp - 1] = mStack[sp - 1] * mStack[sp];
return sp - 1;
},
- (sp) -> { // DIV
+ (sp) -> { // DIV
mStack[sp - 1] = mStack[sp - 1] / mStack[sp];
return sp - 1;
},
- (sp) -> { // MOD
+ (sp) -> { // MOD
mStack[sp - 1] = mStack[sp - 1] % mStack[sp];
return sp - 1;
},
@@ -183,8 +184,7 @@ public class IntegerExpressionEvaluator {
return sp - 1;
},
(sp) -> { // COPY_SIGN copy the sing of (using bit magic)
- mStack[sp - 1] = (mStack[sp - 1] ^ (mStack[sp] >> 31))
- - (mStack[sp] >> 31);
+ mStack[sp - 1] = (mStack[sp - 1] ^ (mStack[sp] >> 31)) - (mStack[sp] >> 31);
return sp - 1;
},
(sp) -> { // MIN
@@ -219,22 +219,18 @@ public class IntegerExpressionEvaluator {
mStack[sp] = (mStack[sp] >> 31) | (-mStack[sp] >>> 31);
return sp;
},
-
(sp) -> { // CLAMP(min,max, val)
- mStack[sp - 2] = Math.min(Math.max(mStack[sp - 2], mStack[sp]),
- mStack[sp - 1]);
+ mStack[sp - 2] = Math.min(Math.max(mStack[sp - 2], mStack[sp]), mStack[sp - 1]);
return sp - 2;
},
(sp) -> { // Ternary conditional
- mStack[sp - 2] = (mStack[sp] > 0)
- ? mStack[sp - 1] : mStack[sp - 2];
+ mStack[sp - 2] = (mStack[sp] > 0) ? mStack[sp - 1] : mStack[sp - 2];
return sp - 2;
},
(sp) -> { // MAD
mStack[sp - 2] = mStack[sp] + mStack[sp - 1] * mStack[sp - 2];
return sp - 2;
},
-
(sp) -> { // first var =
mStack[sp] = mVar[0];
return sp;
@@ -296,7 +292,7 @@ public class IntegerExpressionEvaluator {
* Convert an expression encoded as an array of ints int to a string
*
* @param opMask bits that are operators
- * @param exp rpn sequence of values and operators
+ * @param exp rpn sequence of values and operators
* @param labels String that represent the variable names
* @return
*/
@@ -328,7 +324,7 @@ public class IntegerExpressionEvaluator {
* Convert an expression encoded as an array of ints int ot a string
*
* @param opMask bit mask of operators vs commands
- * @param exp rpn sequence of values and operators
+ * @param exp rpn sequence of values and operators
* @return string representation of the expression
*/
public static String toString(int opMask, int[] exp) {
@@ -357,8 +353,9 @@ public class IntegerExpressionEvaluator {
/**
* This creates an infix string expression
+ *
* @param opMask The bits that are operators
- * @param exp the array of expressions
+ * @param exp the array of expressions
* @return infix string
*/
public static String toStringInfix(int opMask, int[] exp) {
@@ -375,24 +372,39 @@ public class IntegerExpressionEvaluator {
return sNames.get(id) + "(" + toString(mask, exp, sp - 1) + ") ";
case 2:
if (infix(id)) {
- return "(" + toString(mask, exp, sp - 2)
- + " " + sNames.get(id) + " "
- + toString(mask, exp, sp - 1) + ") ";
+ return "("
+ + toString(mask, exp, sp - 2)
+ + " "
+ + sNames.get(id)
+ + " "
+ + toString(mask, exp, sp - 1)
+ + ") ";
} else {
- return sNames.get(id) + "("
- + toString(mask, exp, sp - 2) + ", "
- + toString(mask, exp, sp - 1) + ")";
+ return sNames.get(id)
+ + "("
+ + toString(mask, exp, sp - 2)
+ + ", "
+ + toString(mask, exp, sp - 1)
+ + ")";
}
case 3:
if (infix(id)) {
- return "((" + toString(mask, exp, sp + 3) + ") ? "
- + toString(mask, exp, sp - 2) + ":"
- + toString(mask, exp, sp - 1) + ")";
+ return "(("
+ + toString(mask, exp, sp + 3)
+ + ") ? "
+ + toString(mask, exp, sp - 2)
+ + ":"
+ + toString(mask, exp, sp - 1)
+ + ")";
} else {
return sNames.get(id)
- + "(" + toString(mask, exp, sp - 3)
- + ", " + toString(mask, exp, sp - 2)
- + ", " + toString(mask, exp, sp - 1) + ")";
+ + "("
+ + toString(mask, exp, sp - 3)
+ + ", "
+ + toString(mask, exp, sp - 2)
+ + ", "
+ + toString(mask, exp, sp - 1)
+ + ")";
}
}
}
@@ -401,11 +413,32 @@ public class IntegerExpressionEvaluator {
static final int[] NO_OF_OPS = {
-1, // no op
- 2, 2, 2, 2, 2, // + - * / %
- 2, 2, 2, 2, 2, 2, 2, 2, 2, //<<, >> , >>> , | , &, ^, min max
- 1, 1, 1, 1, 1, 1, // neg, abs, ++, -- , not , sign
- 3, 3, 3, // clamp, ifElse, mad,
- 0, 0, 0 // mad, ?:,
+ 2,
+ 2,
+ 2,
+ 2,
+ 2, // + - * / %
+ 2,
+ 2,
+ 2,
+ 2,
+ 2,
+ 2,
+ 2,
+ 2,
+ 2, // <<, >> , >>> , | , &, ^, min max
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1, // neg, abs, ++, -- , not , sign
+ 3,
+ 3,
+ 3, // clamp, ifElse, mad,
+ 0,
+ 0,
+ 0 // mad, ?:,
// a[0],a[1],a[2]
};
@@ -416,13 +449,14 @@ public class IntegerExpressionEvaluator {
* @return true if the operator is infix
*/
static boolean infix(int n) {
- return ((n < 12));
+ return n < 12;
}
/**
* is it an id or operation
+ *
* @param opMask the bits that mark elements as an operation
- * @param i the bit to check
+ * @param i the bit to check
* @return true if the bit is 1
*/
public static boolean isOperation(int opMask, int i) {
diff --git a/core/java/com/android/internal/widget/remotecompose/core/operations/utilities/NanMap.java b/core/java/com/android/internal/widget/remotecompose/core/operations/utilities/NanMap.java
index 1e669c6b2e35..0616cc7306f5 100644
--- a/core/java/com/android/internal/widget/remotecompose/core/operations/utilities/NanMap.java
+++ b/core/java/com/android/internal/widget/remotecompose/core/operations/utilities/NanMap.java
@@ -18,17 +18,12 @@ package com.android.internal.widget.remotecompose.core.operations.utilities;
import com.android.internal.widget.remotecompose.core.operations.Utils;
/**
- * This defines the major id maps and ranges used by remote compose
- * Generally ids ranging from 1 ... 7FFFFF (4095) are for ids
- * The data range is divided int to bits
- * 0xxxxx are allocated for Predefined Global System Variables
- * 1xxxxx are allocated to normal variables
- * 2xxxxx are allocated to List&MAPS (Arrays of stuff)
- * 3xxxxx are allocated to path & float operations
- * 4xxxxx,5xxxxx,7xxxxx are reserved for future use
- * 0x1000-0x1100 are used for path operations in PathData
- * 0x1100-0x1200 are used for math operations in Animated float
- * 0x
+ * This defines the major id maps and ranges used by remote compose Generally ids ranging from 1 ...
+ * 7FFFFF (4095) are for ids The data range is divided int to bits 0xxxxx are allocated for
+ * Predefined Global System Variables 1xxxxx are allocated to normal variables 2xxxxx are allocated
+ * to List&MAPS (Arrays of stuff) 3xxxxx are allocated to path & float operations
+ * 4xxxxx,5xxxxx,7xxxxx are reserved for future use 0x1000-0x1100 are used for path operations in
+ * PathData 0x1100-0x1200 are used for math operations in Animated float 0x
*/
public class NanMap {
public static final int MOVE = 0x300_000;
@@ -71,7 +66,6 @@ public class NanMap {
public static final int ID_REGION_MASK = 0x700000;
public static final int ID_REGION_ARRAY = 0x200000;
-
/**
* Get ID from Nan float
*
diff --git a/core/java/com/android/internal/widget/remotecompose/core/operations/utilities/StringSerializer.java b/core/java/com/android/internal/widget/remotecompose/core/operations/utilities/StringSerializer.java
index fb9078104089..ab7576e12aa6 100644
--- a/core/java/com/android/internal/widget/remotecompose/core/operations/utilities/StringSerializer.java
+++ b/core/java/com/android/internal/widget/remotecompose/core/operations/utilities/StringSerializer.java
@@ -15,15 +15,14 @@
*/
package com.android.internal.widget.remotecompose.core.operations.utilities;
-/**
- * Utility serializer maintaining an indent buffer
- */
+/** Utility serializer maintaining an indent buffer */
public class StringSerializer {
StringBuffer mBuffer = new StringBuffer();
String mIndentBuffer = " ";
/**
* Append some content to the current buffer
+ *
* @param indent the indentation level to use
* @param content content to append
*/
@@ -35,17 +34,17 @@ public class StringSerializer {
mBuffer.append("\n");
}
- /**
- * Reset the buffer
- */
+ /** Reset the buffer */
public void reset() {
mBuffer = new StringBuffer();
}
/**
* Return a string representation of the buffer
+ *
* @return string representation
*/
+ @Override
public String toString() {
return mBuffer.toString();
}
diff --git a/core/java/com/android/internal/widget/remotecompose/core/operations/utilities/StringUtils.java b/core/java/com/android/internal/widget/remotecompose/core/operations/utilities/StringUtils.java
index 8dd5405ddf12..f2ccb401ea8f 100644
--- a/core/java/com/android/internal/widget/remotecompose/core/operations/utilities/StringUtils.java
+++ b/core/java/com/android/internal/widget/remotecompose/core/operations/utilities/StringUtils.java
@@ -17,26 +17,21 @@ package com.android.internal.widget.remotecompose.core.operations.utilities;
import java.util.Arrays;
-/**
- * Utilities for string manipulation
- */
+/** Utilities for string manipulation */
public class StringUtils {
/**
- * Converts a float into a string.
- * Providing a defined number of characters before and after the
+ * Converts a float into a string. Providing a defined number of characters before and after the
* decimal point.
*
- * @param value The value to convert to string
+ * @param value The value to convert to string
* @param beforeDecimalPoint digits before the decimal point
- * @param afterDecimalPoint digits after the decimal point
- * @param pre character to pad width 0 = no pad typically ' ' or '0'
- * @param post character to pad width 0 = no pad typically ' ' or '0'
+ * @param afterDecimalPoint digits after the decimal point
+ * @param pre character to pad width 0 = no pad typically ' ' or '0'
+ * @param post character to pad width 0 = no pad typically ' ' or '0'
* @return
*/
- public static String floatToString(float value,
- int beforeDecimalPoint,
- int afterDecimalPoint,
- char pre, char post) {
+ public static String floatToString(
+ float value, int beforeDecimalPoint, int afterDecimalPoint, char pre, char post) {
int integerPart = (int) value;
float fractionalPart = value % 1;
@@ -52,7 +47,6 @@ public class StringUtils {
integerPartString = new String(pad) + integerPartString;
}
-
} else if (iLen > beforeDecimalPoint) {
integerPartString = integerPartString.substring(iLen - beforeDecimalPoint);
}
@@ -68,7 +62,7 @@ public class StringUtils {
fractionalPart = Math.round(fractionalPart);
for (int i = 0; i < afterDecimalPoint; i++) {
- fractionalPart *= .1;
+ fractionalPart *= .1F;
}
String fact = Float.toString(fractionalPart);
@@ -92,5 +86,4 @@ public class StringUtils {
return integerPartString + "." + fact;
}
-
}
diff --git a/core/java/com/android/internal/widget/remotecompose/core/operations/utilities/easing/BounceCurve.java b/core/java/com/android/internal/widget/remotecompose/core/operations/utilities/easing/BounceCurve.java
index c3cd5ae9c79d..3161aa190895 100644
--- a/core/java/com/android/internal/widget/remotecompose/core/operations/utilities/easing/BounceCurve.java
+++ b/core/java/com/android/internal/widget/remotecompose/core/operations/utilities/easing/BounceCurve.java
@@ -15,9 +15,7 @@
*/
package com.android.internal.widget.remotecompose.core.operations.utilities.easing;
-/**
- * Provide a specific bouncing easing function
- */
+/** Provide a specific bouncing easing function */
public class BounceCurve extends Easing {
private static final float N1 = 7.5625f;
private static final float D1 = 2.75f;
@@ -63,5 +61,4 @@ public class BounceCurve extends Easing {
}
return 0f;
}
-
}
diff --git a/core/java/com/android/internal/widget/remotecompose/core/operations/utilities/easing/CubicEasing.java b/core/java/com/android/internal/widget/remotecompose/core/operations/utilities/easing/CubicEasing.java
index fd1ee036e475..60a59cf464cd 100644
--- a/core/java/com/android/internal/widget/remotecompose/core/operations/utilities/easing/CubicEasing.java
+++ b/core/java/com/android/internal/widget/remotecompose/core/operations/utilities/easing/CubicEasing.java
@@ -16,7 +16,6 @@
package com.android.internal.widget.remotecompose.core.operations.utilities.easing;
class CubicEasing extends Easing {
- float mType = 0;
float mX1 = 0f;
float mY1 = 0f;
float mX2 = 0f;
@@ -92,25 +91,24 @@ class CubicEasing extends Easing {
return mY1 * f1 + mY2 * f2 + f3;
}
- private float getDiffX(float t) {
- float t1 = 1 - t;
- return 3 * t1 * t1 * mX1 + 6 * t1 * t * (mX2 - mX1) + 3 * t * t * (1 - mX2);
- }
+ // private float getDiffX(float t) {
+ // float t1 = 1 - t;
+ // return 3 * t1 * t1 * mX1 + 6 * t1 * t * (mX2 - mX1) + 3 * t * t * (1 - mX2);
+ // }
- private float getDiffY(float t) {
- float t1 = 1 - t;
- return 3 * t1 * t1 * mY1 + 6 * t1 * t * (mY2 - mY1) + 3 * t * t * (1 - mY2);
- }
+ // private float getDiffY(float t) {
+ // float t1 = 1 - t;
+ // return 3 * t1 * t1 * mY1 + 6 * t1 * t * (mY2 - mY1) + 3 * t * t * (1 - mY2);
+ // }
- /**
- * binary search for the region and linear interpolate the answer
- */
+ /** binary search for the region and linear interpolate the answer */
+ @Override
public float getDiff(float x) {
float t = 0.5f;
float range = 0.5f;
while (range > D_ERROR) {
float tx = getX(t);
- range *= 0.5;
+ range *= 0.5f;
if (tx < x) {
t += range;
} else {
@@ -124,9 +122,8 @@ class CubicEasing extends Easing {
return (y2 - y1) / (x2 - x1);
}
- /**
- * binary search for the region and linear interpolate the answer
- */
+ /** binary search for the region and linear interpolate the answer */
+ @Override
public float get(float x) {
if (x <= 0.0f) {
return 0f;
diff --git a/core/java/com/android/internal/widget/remotecompose/core/operations/utilities/easing/Easing.java b/core/java/com/android/internal/widget/remotecompose/core/operations/utilities/easing/Easing.java
index 4ed955069d78..d8bc83eb8a2e 100644
--- a/core/java/com/android/internal/widget/remotecompose/core/operations/utilities/easing/Easing.java
+++ b/core/java/com/android/internal/widget/remotecompose/core/operations/utilities/easing/Easing.java
@@ -15,19 +15,14 @@
*/
package com.android.internal.widget.remotecompose.core.operations.utilities.easing;
-/**
- * The standard interface to Easing functions
- */
+/** The standard interface to Easing functions */
public abstract class Easing {
int mType;
- /**
- * get the value at point x
- */
+
+ /** get the value at point x */
public abstract float get(float x);
- /**
- * get the slope of the easing function at at x
- */
+ /** get the slope of the easing function at at x */
public abstract float getDiff(float x);
public int getType() {
@@ -44,5 +39,4 @@ public abstract class Easing {
public static final int SPLINE_CUSTOM = 12;
public static final int EASE_OUT_BOUNCE = 13;
public static final int EASE_OUT_ELASTIC = 14;
-
}
diff --git a/core/java/com/android/internal/widget/remotecompose/core/operations/utilities/easing/ElasticOutCurve.java b/core/java/com/android/internal/widget/remotecompose/core/operations/utilities/easing/ElasticOutCurve.java
index e26958302e3c..d53cff597f3a 100644
--- a/core/java/com/android/internal/widget/remotecompose/core/operations/utilities/easing/ElasticOutCurve.java
+++ b/core/java/com/android/internal/widget/remotecompose/core/operations/utilities/easing/ElasticOutCurve.java
@@ -15,9 +15,7 @@
*/
package com.android.internal.widget.remotecompose.core.operations.utilities.easing;
-/**
- * Provide a bouncing Easing function
- */
+/** Provide a bouncing Easing function */
public class ElasticOutCurve extends Easing {
private static final float F_PI = (float) Math.PI;
private static final float C4 = 2 * F_PI / 3;
@@ -31,9 +29,7 @@ public class ElasticOutCurve extends Easing {
}
if (x >= 1) {
return 1.0f;
- } else
- return (float) (Math.pow(2.0f, -10 * x)
- * Math.sin((x * 10 - 0.75f) * C4) + 1);
+ } else return (float) (Math.pow(2.0f, -10 * x) * Math.sin((x * 10 - 0.75f) * C4) + 1);
}
@Override
@@ -41,9 +37,11 @@ public class ElasticOutCurve extends Easing {
if (x < 0 || x > 1) {
return 0.0f;
} else
- return (float) ((5 * Math.pow(2.0f, (1 - 10 * x))
- * (LOG_8 * Math.cos(TWENTY_PI * x / 3) + 2
- * F_PI * Math.sin(TWENTY_PI * x / 3))
- / 3));
+ return (float)
+ (5
+ * Math.pow(2.0f, 1 - 10 * x)
+ * (LOG_8 * Math.cos(TWENTY_PI * x / 3)
+ + 2 * F_PI * Math.sin(TWENTY_PI * x / 3))
+ / 3);
}
}
diff --git a/core/java/com/android/internal/widget/remotecompose/core/operations/utilities/easing/FloatAnimation.java b/core/java/com/android/internal/widget/remotecompose/core/operations/utilities/easing/FloatAnimation.java
index 98dbf0048abd..a29b8af5fbd1 100644
--- a/core/java/com/android/internal/widget/remotecompose/core/operations/utilities/easing/FloatAnimation.java
+++ b/core/java/com/android/internal/widget/remotecompose/core/operations/utilities/easing/FloatAnimation.java
@@ -15,9 +15,7 @@
*/
package com.android.internal.widget.remotecompose.core.operations.utilities.easing;
-/**
- * Support Animation of the FloatExpression
- */
+/** Support Animation of the FloatExpression */
public class FloatAnimation extends Easing {
float[] mSpec;
// mSpec[0] = duration
@@ -25,12 +23,12 @@ public class FloatAnimation extends Easing {
// mSpec[2..1+num_of_param] params
// mSpec[2+num_of_param] starting Value
Easing mEasingCurve;
- private int mType = CUBIC_STANDARD;
+
private float mDuration = 1;
private float mWrap = Float.NaN;
private float mInitialValue = Float.NaN;
private float mTargetValue = Float.NaN;
- private float mScale = 1;
+ // private float mScale = 1;
float mOffset = 0;
@Override
@@ -51,20 +49,19 @@ public class FloatAnimation extends Easing {
}
public FloatAnimation() {
+ mType = CUBIC_STANDARD;
mEasingCurve = new CubicEasing(mType);
}
public FloatAnimation(float... description) {
+ mType = CUBIC_STANDARD;
setAnimationDescription(description);
}
- public FloatAnimation(int type,
- float duration,
- float[] description,
- float initialValue,
- float wrap) {
- setAnimationDescription(packToFloatArray(duration,
- type, description, initialValue, wrap));
+ public FloatAnimation(
+ int type, float duration, float[] description, float initialValue, float wrap) {
+ mType = CUBIC_STANDARD;
+ setAnimationDescription(packToFloatArray(duration, type, description, initialValue, wrap));
}
/**
@@ -76,11 +73,8 @@ public class FloatAnimation extends Easing {
* @param initialValue
* @return
*/
- public static float[] packToFloatArray(float duration,
- int type,
- float[] spec,
- float initialValue,
- float wrap) {
+ public static float[] packToFloatArray(
+ float duration, int type, float[] spec, float initialValue, float wrap) {
int count = 0;
if (!Float.isNaN(initialValue)) {
@@ -104,7 +98,7 @@ public class FloatAnimation extends Easing {
if (duration != 1 || count > 0) {
count++;
}
- if (!Float.isNaN(wrap) || !Float.isNaN(initialValue)) {
+ if (!Float.isNaN(wrap) || !Float.isNaN(initialValue)) {
count++;
}
float[] ret = new float[count];
@@ -113,11 +107,10 @@ public class FloatAnimation extends Easing {
if (ret.length > 0) {
ret[pos++] = duration;
-
}
if (ret.length > 1) {
- int wrapBit = (Float.isNaN(wrap)) ? 0 : 1;
- int initBit = (Float.isNaN(initialValue)) ? 0 : 2;
+ int wrapBit = Float.isNaN(wrap) ? 0 : 1;
+ int initBit = Float.isNaN(initialValue) ? 0 : 2;
int bits = type | ((wrapBit | initBit) << 8);
ret[pos++] = Float.intBitsToFloat(specLen << 16 | bits);
}
@@ -137,6 +130,7 @@ public class FloatAnimation extends Easing {
/**
* Create an animation based on a float encoding of the animation
+ *
* @param description
*/
public void setAnimationDescription(float[] description) {
@@ -171,11 +165,12 @@ public class FloatAnimation extends Easing {
mEasingCurve = new CubicEasing(type);
break;
case CUBIC_CUSTOM:
- mEasingCurve = new CubicEasing(params[offset + 0],
- params[offset + 1],
- params[offset + 2],
- params[offset + 3]
- );
+ mEasingCurve =
+ new CubicEasing(
+ params[offset + 0],
+ params[offset + 1],
+ params[offset + 2],
+ params[offset + 3]);
break;
case EASE_OUT_BOUNCE:
mEasingCurve = new BounceCurve(type);
@@ -191,6 +186,7 @@ public class FloatAnimation extends Easing {
/**
* Get the duration the interpolate is to take
+ *
* @return duration in seconds
*/
public float getDuration() {
@@ -199,6 +195,7 @@ public class FloatAnimation extends Easing {
/**
* Set the initial Value
+ *
* @param value
*/
public void setInitialValue(float value) {
@@ -213,6 +210,7 @@ public class FloatAnimation extends Easing {
/**
* Set the target value to interpolate to
+ *
* @param value
*/
public void setTargetValue(float value) {
@@ -236,25 +234,23 @@ public class FloatAnimation extends Easing {
private void setScaleOffset() {
if (!Float.isNaN(mInitialValue) && !Float.isNaN(mTargetValue)) {
- mScale = (mTargetValue - mInitialValue);
+ // mScale = (mTargetValue - mInitialValue); // TODO: commented out because
+ // unused.
mOffset = mInitialValue;
} else {
- mScale = 1;
+ // mScale = 1; // TODO: commented out because its unused
mOffset = 0;
}
}
- /**
- * get the value at time t in seconds since start
- */
+ /** get the value at time t in seconds since start */
+ @Override
public float get(float t) {
- return mEasingCurve.get(t / mDuration)
- * (mTargetValue - mInitialValue) + mInitialValue;
+ return mEasingCurve.get(t / mDuration) * (mTargetValue - mInitialValue) + mInitialValue;
}
- /**
- * get the slope of the easing function at at x
- */
+ /** get the slope of the easing function at at x */
+ @Override
public float getDiff(float t) {
return mEasingCurve.getDiff(t / mDuration) * (mTargetValue - mInitialValue);
}
diff --git a/core/java/com/android/internal/widget/remotecompose/core/operations/utilities/easing/GeneralEasing.java b/core/java/com/android/internal/widget/remotecompose/core/operations/utilities/easing/GeneralEasing.java
index 50a7d59ed8e0..75a60324aa3c 100644
--- a/core/java/com/android/internal/widget/remotecompose/core/operations/utilities/easing/GeneralEasing.java
+++ b/core/java/com/android/internal/widget/remotecompose/core/operations/utilities/easing/GeneralEasing.java
@@ -15,15 +15,14 @@
*/
package com.android.internal.widget.remotecompose.core.operations.utilities.easing;
-/**
- * Provides and interface to create easing functions
- */
-public class GeneralEasing extends Easing{
+/** Provides and interface to create easing functions */
+public class GeneralEasing extends Easing {
float[] mEasingData = new float[0];
Easing mEasingCurve = new CubicEasing(CUBIC_STANDARD);
/**
* Set the curve based on the float encoding of it
+ *
* @param data
*/
public void setCurveSpecification(float[] data) {
@@ -47,11 +46,9 @@ public class GeneralEasing extends Easing{
mEasingCurve = new CubicEasing(type);
break;
case CUBIC_CUSTOM:
- mEasingCurve = new CubicEasing(mEasingData[1],
- mEasingData[2],
- mEasingData[3],
- mEasingData[5]
- );
+ mEasingCurve =
+ new CubicEasing(
+ mEasingData[1], mEasingData[2], mEasingData[3], mEasingData[5]);
break;
case EASE_OUT_BOUNCE:
mEasingCurve = new BounceCurve(type);
@@ -59,23 +56,20 @@ public class GeneralEasing extends Easing{
}
}
- /**
- * get the value at point x
- */
+ /** get the value at point x */
+ @Override
public float get(float x) {
return mEasingCurve.get(x);
}
- /**
- * get the slope of the easing function at at x
- */
+ /** get the slope of the easing function at at x */
+ @Override
public float getDiff(float x) {
return mEasingCurve.getDiff(x);
}
+ @Override
public int getType() {
return mEasingCurve.getType();
}
-
-
}
diff --git a/core/java/com/android/internal/widget/remotecompose/core/operations/utilities/easing/MonotonicCurveFit.java b/core/java/com/android/internal/widget/remotecompose/core/operations/utilities/easing/MonotonicCurveFit.java
index 23930b92a282..9355cacde4ad 100644
--- a/core/java/com/android/internal/widget/remotecompose/core/operations/utilities/easing/MonotonicCurveFit.java
+++ b/core/java/com/android/internal/widget/remotecompose/core/operations/utilities/easing/MonotonicCurveFit.java
@@ -17,12 +17,8 @@ package com.android.internal.widget.remotecompose.core.operations.utilities.easi
import java.util.Arrays;
-/**
- * This performs a spline interpolation in multiple dimensions
- *
- *
- */
-public class MonotonicCurveFit {
+/** This performs a spline interpolation in multiple dimensions */
+public class MonotonicCurveFit {
private static final String TAG = "MonotonicCurveFit";
private double[] mT;
private double[][] mY;
@@ -32,6 +28,7 @@ public class MonotonicCurveFit {
/**
* create a collection of curves
+ *
* @param time the point along the curve
* @param y the parameter at those points
*/
@@ -78,6 +75,7 @@ public class MonotonicCurveFit {
/**
* Get the position of all curves at time t
+ *
* @param t
* @param v
*/
@@ -137,6 +135,7 @@ public class MonotonicCurveFit {
/**
* Get the position of all curves at time t
+ *
* @param t
* @param v
*/
@@ -196,6 +195,7 @@ public class MonotonicCurveFit {
/**
* Get the position of the jth curve at time t
+ *
* @param t
* @param j
* @return
@@ -230,7 +230,6 @@ public class MonotonicCurveFit {
double t1 = mTangent[i][j];
double t2 = mTangent[i + 1][j];
return interpolate(h, x, y1, y2, t1, t2);
-
}
}
return 0; // should never reach here
@@ -238,6 +237,7 @@ public class MonotonicCurveFit {
/**
* Get the slope of all the curves at position t
+ *
* @param t
* @param v
*/
@@ -264,11 +264,11 @@ public class MonotonicCurveFit {
break;
}
}
- return;
}
/**
* Get the slope of the j curve at position t
+ *
* @param t
* @param j
* @return
@@ -299,34 +299,38 @@ public class MonotonicCurveFit {
return mT;
}
- /**
- * Cubic Hermite spline
- */
- private static double interpolate(double h,
- double x,
- double y1,
- double y2,
- double t1,
- double t2) {
+ /** Cubic Hermite spline */
+ private static double interpolate(
+ double h, double x, double y1, double y2, double t1, double t2) {
double x2 = x * x;
double x3 = x2 * x;
- return -2 * x3 * y2 + 3 * x2 * y2 + 2 * x3 * y1 - 3 * x2 * y1 + y1
- + h * t2 * x3 + h * t1 * x3 - h * t2 * x2 - 2 * h * t1 * x2
+ return -2 * x3 * y2
+ + 3 * x2 * y2
+ + 2 * x3 * y1
+ - 3 * x2 * y1
+ + y1
+ + h * t2 * x3
+ + h * t1 * x3
+ - h * t2 * x2
+ - 2 * h * t1 * x2
+ h * t1 * x;
}
- /**
- * Cubic Hermite spline slope differentiated
- */
+ /** Cubic Hermite spline slope differentiated */
private static double diff(double h, double x, double y1, double y2, double t1, double t2) {
double x2 = x * x;
- return -6 * x2 * y2 + 6 * x * y2 + 6 * x2 * y1 - 6 * x * y1 + 3 * h * t2 * x2
- + 3 * h * t1 * x2 - 2 * h * t2 * x - 4 * h * t1 * x + h * t1;
+ return -6 * x2 * y2
+ + 6 * x * y2
+ + 6 * x2 * y1
+ - 6 * x * y1
+ + 3 * h * t2 * x2
+ + 3 * h * t1 * x2
+ - 2 * h * t2 * x
+ - 4 * h * t1 * x
+ + h * t1;
}
- /**
- * This builds a monotonic spline to be used as a wave function
- */
+ /** This builds a monotonic spline to be used as a wave function */
public static MonotonicCurveFit buildWave(String configString) {
// done this way for efficiency
String str = configString;
diff --git a/core/java/com/android/internal/widget/remotecompose/core/operations/utilities/easing/StepCurve.java b/core/java/com/android/internal/widget/remotecompose/core/operations/utilities/easing/StepCurve.java
index 6ed6548405d2..b4596897a44f 100644
--- a/core/java/com/android/internal/widget/remotecompose/core/operations/utilities/easing/StepCurve.java
+++ b/core/java/com/android/internal/widget/remotecompose/core/operations/utilities/easing/StepCurve.java
@@ -15,14 +15,13 @@
*/
package com.android.internal.widget.remotecompose.core.operations.utilities.easing;
-
/**
- * This class translates a series of floating point values into a continuous
- * curve for use in an easing function including quantize functions
- * it is used with the "spline(0,0.3,0.3,0.5,...0.9,1)" it should start at 0 and end at one 1
+ * This class translates a series of floating point values into a continuous curve for use in an
+ * easing function including quantize functions it is used with the "spline(0,0.3,0.3,0.5,...0.9,1)"
+ * it should start at 0 and end at one 1
*/
public class StepCurve extends Easing {
- private static final boolean DEBUG = false;
+ // private static final boolean DEBUG = false;
MonotonicCurveFit mCurveFit;
public StepCurve(float[] params, int offset, int len) {
@@ -55,12 +54,16 @@ public class StepCurve extends Easing {
@Override
public float getDiff(float x) {
+ if (x < 0f) return 0;
+ if (x > 1f) return 0;
return (float) mCurveFit.getSlope(x, 0);
}
-
@Override
public float get(float x) {
+ if (x < 0f) return 0;
+ if (x > 1f) return 1;
+
return (float) mCurveFit.getPos(x, 0);
}
}
diff --git a/core/java/com/android/internal/widget/remotecompose/core/types/BooleanConstant.java b/core/java/com/android/internal/widget/remotecompose/core/types/BooleanConstant.java
index 9045bcbd2e06..57a804284f0d 100644
--- a/core/java/com/android/internal/widget/remotecompose/core/types/BooleanConstant.java
+++ b/core/java/com/android/internal/widget/remotecompose/core/types/BooleanConstant.java
@@ -15,23 +15,21 @@
*/
package com.android.internal.widget.remotecompose.core.types;
-import static com.android.internal.widget.remotecompose.core.documentation.Operation.BYTE;
-import static com.android.internal.widget.remotecompose.core.documentation.Operation.INT;
+import static com.android.internal.widget.remotecompose.core.documentation.DocumentedOperation.BYTE;
import com.android.internal.widget.remotecompose.core.Operation;
import com.android.internal.widget.remotecompose.core.Operations;
import com.android.internal.widget.remotecompose.core.RemoteContext;
import com.android.internal.widget.remotecompose.core.WireBuffer;
import com.android.internal.widget.remotecompose.core.documentation.DocumentationBuilder;
+import com.android.internal.widget.remotecompose.core.documentation.DocumentedOperation;
import java.util.List;
-/**
- * Used to represent a boolean
- */
+/** Used to represent a boolean */
public class BooleanConstant implements Operation {
private static final int OP_CODE = Operations.DATA_BOOLEAN;
- boolean mValue = false;
+ private boolean mValue = false;
private int mId;
public BooleanConstant(int id, boolean value) {
@@ -39,15 +37,22 @@ public class BooleanConstant implements Operation {
mValue = value;
}
+ /**
+ * Get the value of the boolean constant
+ *
+ * @return the value of the boolean
+ */
+ public boolean getValue() {
+ return mValue;
+ }
+
@Override
public void write(WireBuffer buffer) {
apply(buffer, mId, mValue);
}
@Override
- public void apply(RemoteContext context) {
-
- }
+ public void apply(RemoteContext context) {}
@Override
public String deepToString(String indent) {
@@ -88,14 +93,9 @@ public class BooleanConstant implements Operation {
}
public static void documentation(DocumentationBuilder doc) {
- doc.operation("Expressions Operations",
- OP_CODE,
- "BooleanConstant")
+ doc.operation("Expressions Operations", OP_CODE, "BooleanConstant")
.description("A boolean and its associated id")
- .field(INT, "id", "id of Int")
- .field(BYTE, "value",
- "8-bit 0 or 1");
-
+ .field(DocumentedOperation.INT, "id", "id of Int")
+ .field(BYTE, "value", "8-bit 0 or 1");
}
-
}
diff --git a/core/java/com/android/internal/widget/remotecompose/core/types/IntegerConstant.java b/core/java/com/android/internal/widget/remotecompose/core/types/IntegerConstant.java
index 90faf52f2362..3ef9db9de915 100644
--- a/core/java/com/android/internal/widget/remotecompose/core/types/IntegerConstant.java
+++ b/core/java/com/android/internal/widget/remotecompose/core/types/IntegerConstant.java
@@ -15,20 +15,18 @@
*/
package com.android.internal.widget.remotecompose.core.types;
-import static com.android.internal.widget.remotecompose.core.documentation.Operation.INT;
+import static com.android.internal.widget.remotecompose.core.documentation.DocumentedOperation.INT;
import com.android.internal.widget.remotecompose.core.Operation;
import com.android.internal.widget.remotecompose.core.Operations;
import com.android.internal.widget.remotecompose.core.RemoteContext;
import com.android.internal.widget.remotecompose.core.WireBuffer;
import com.android.internal.widget.remotecompose.core.documentation.DocumentationBuilder;
+import com.android.internal.widget.remotecompose.core.documentation.DocumentedOperation;
import java.util.List;
-/**
- * Represents a single integer typically used for states
- * or named for input into the system
- */
+/** Represents a single integer typically used for states or named for input into the system */
public class IntegerConstant implements Operation {
private int mValue = 0;
private int mId;
@@ -87,13 +85,9 @@ public class IntegerConstant implements Operation {
}
public static void documentation(DocumentationBuilder doc) {
- doc.operation("Expressions Operations",
- id(),
- "IntegerConstant")
+ doc.operation("Expressions Operations", id(), "IntegerConstant")
.description("A integer and its associated id")
- .field(INT, "id", "id of Int")
- .field(INT, "value",
- "32-bit int value");
-
+ .field(DocumentedOperation.INT, "id", "id of Int")
+ .field(INT, "value", "32-bit int value");
}
}
diff --git a/core/java/com/android/internal/widget/remotecompose/core/types/LongConstant.java b/core/java/com/android/internal/widget/remotecompose/core/types/LongConstant.java
index 70402cf45eae..6d51d194708f 100644
--- a/core/java/com/android/internal/widget/remotecompose/core/types/LongConstant.java
+++ b/core/java/com/android/internal/widget/remotecompose/core/types/LongConstant.java
@@ -15,23 +15,21 @@
*/
package com.android.internal.widget.remotecompose.core.types;
-import static com.android.internal.widget.remotecompose.core.documentation.Operation.INT;
-import static com.android.internal.widget.remotecompose.core.documentation.Operation.LONG;
+import static com.android.internal.widget.remotecompose.core.documentation.DocumentedOperation.LONG;
import com.android.internal.widget.remotecompose.core.Operation;
import com.android.internal.widget.remotecompose.core.Operations;
import com.android.internal.widget.remotecompose.core.RemoteContext;
import com.android.internal.widget.remotecompose.core.WireBuffer;
import com.android.internal.widget.remotecompose.core.documentation.DocumentationBuilder;
+import com.android.internal.widget.remotecompose.core.documentation.DocumentedOperation;
import java.util.List;
-/**
- * Used to represent a long
- */
+/** Used to represent a long */
public class LongConstant implements Operation {
private static final int OP_CODE = Operations.DATA_LONG;
- long mValue;
+ private long mValue;
private int mId;
public LongConstant(int id, long value) {
@@ -39,6 +37,15 @@ public class LongConstant implements Operation {
mValue = value;
}
+ /**
+ * Get the value of the long constant
+ *
+ * @return the value of the long
+ */
+ public long getValue() {
+ return mValue;
+ }
+
@Override
public void write(WireBuffer buffer) {
apply(buffer, mId, mValue);
@@ -46,6 +53,7 @@ public class LongConstant implements Operation {
@Override
public void apply(RemoteContext context) {
+ context.putObject(mId, this);
}
@Override
@@ -79,14 +87,9 @@ public class LongConstant implements Operation {
}
public static void documentation(DocumentationBuilder doc) {
- doc.operation("Expressions Operations",
- OP_CODE,
- "LongConstant")
+ doc.operation("Expressions Operations", OP_CODE, "LongConstant")
.description("A boolean and its associated id")
- .field(INT, "id", "id of Int")
- .field(LONG, "value",
- "The long Value");
-
+ .field(DocumentedOperation.INT, "id", "id of Int")
+ .field(LONG, "value", "The long Value");
}
-
}
diff --git a/core/java/com/android/internal/widget/remotecompose/player/RemoteComposeDocument.java b/core/java/com/android/internal/widget/remotecompose/player/RemoteComposeDocument.java
index e32f8239d7ef..906282c1dde3 100644
--- a/core/java/com/android/internal/widget/remotecompose/player/RemoteComposeDocument.java
+++ b/core/java/com/android/internal/widget/remotecompose/player/RemoteComposeDocument.java
@@ -22,9 +22,7 @@ import com.android.internal.widget.remotecompose.core.operations.layout.Componen
import java.io.InputStream;
-/**
- * Public API to create a new RemoteComposeDocument coming from an input stream
- */
+/** Public API to create a new RemoteComposeDocument coming from an input stream */
public class RemoteComposeDocument {
CoreDocument mDocument = new CoreDocument();
@@ -48,23 +46,19 @@ public class RemoteComposeDocument {
}
/**
- * Called when an initialization is needed, allowing the document to eg load
- * resources / cache them.
+ * Called when an initialization is needed, allowing the document to eg load resources / cache
+ * them.
*/
public void initializeContext(RemoteContext context) {
mDocument.initializeContext(context);
}
- /**
- * Returns the width of the document in pixels
- */
+ /** Returns the width of the document in pixels */
public int getWidth() {
return mDocument.getWidth();
}
- /**
- * Returns the height of the document in pixels
- */
+ /** Returns the height of the document in pixels */
public int getHeight() {
return mDocument.getHeight();
}
@@ -77,7 +71,7 @@ public class RemoteComposeDocument {
* Paint the document
*
* @param context the provided PaintContext
- * @param theme the theme we want to use for this document.
+ * @param theme the theme we want to use for this document.
*/
public void paint(RemoteContext context, int theme) {
mDocument.paint(context, theme);
@@ -105,8 +99,7 @@ public class RemoteComposeDocument {
@Override
public String toString() {
- return "Document{\n"
- + mDocument + '}';
+ return "Document{\n" + mDocument + '}';
}
/**
@@ -120,6 +113,7 @@ public class RemoteComposeDocument {
/**
* Return a component associated with id
+ *
* @param id the component id
* @return the corresponding component or null if not found
*/
@@ -138,4 +132,3 @@ public class RemoteComposeDocument {
return mDocument.getStats();
}
}
-
diff --git a/core/java/com/android/internal/widget/remotecompose/player/RemoteComposePlayer.java b/core/java/com/android/internal/widget/remotecompose/player/RemoteComposePlayer.java
index 3d78680bff0a..06bf4cdb0a0d 100644
--- a/core/java/com/android/internal/widget/remotecompose/player/RemoteComposePlayer.java
+++ b/core/java/com/android/internal/widget/remotecompose/player/RemoteComposePlayer.java
@@ -29,9 +29,7 @@ import android.widget.ScrollView;
import com.android.internal.widget.remotecompose.core.operations.RootContentBehavior;
import com.android.internal.widget.remotecompose.player.platform.RemoteComposeCanvas;
-/**
- * A view to to display and play RemoteCompose documents
- */
+/** A view to to display and play RemoteCompose documents */
public class RemoteComposePlayer extends FrameLayout {
private RemoteComposeCanvas mInner;
@@ -73,10 +71,7 @@ public class RemoteComposePlayer extends FrameLayout {
public void setDocument(RemoteComposeDocument value) {
if (value != null) {
if (value.canBeDisplayed(
- MAX_SUPPORTED_MAJOR_VERSION,
- MAX_SUPPORTED_MINOR_VERSION, 0L
- )
- ) {
+ MAX_SUPPORTED_MAJOR_VERSION, MAX_SUPPORTED_MINOR_VERSION, 0L)) {
mInner.setDocument(value);
int contentBehavior = value.getDocument().getContentScroll();
applyContentBehavior(contentBehavior);
@@ -90,65 +85,58 @@ public class RemoteComposePlayer extends FrameLayout {
}
/**
- * Apply the content behavior (NONE|SCROLL_HORIZONTAL|SCROLL_VERTICAL) to the player,
- * adding or removing scrollviews as needed.
+ * Apply the content behavior (NONE|SCROLL_HORIZONTAL|SCROLL_VERTICAL) to the player, adding or
+ * removing scrollviews as needed.
*
* @param contentBehavior document content behavior (NONE|SCROLL_HORIZONTAL|SCROLL_VERTICAL)
*/
private void applyContentBehavior(int contentBehavior) {
switch (contentBehavior) {
- case RootContentBehavior.SCROLL_HORIZONTAL: {
+ case RootContentBehavior.SCROLL_HORIZONTAL:
if (!(mInner.getParent() instanceof HorizontalScrollView)) {
((ViewGroup) mInner.getParent()).removeView(mInner);
removeAllViews();
- LayoutParams layoutParamsInner = new LayoutParams(
- LayoutParams.WRAP_CONTENT,
- LayoutParams.MATCH_PARENT);
+ LayoutParams layoutParamsInner =
+ new LayoutParams(LayoutParams.WRAP_CONTENT, LayoutParams.MATCH_PARENT);
HorizontalScrollView horizontalScrollView =
new HorizontalScrollView(getContext());
horizontalScrollView.setBackgroundColor(Color.TRANSPARENT);
horizontalScrollView.setFillViewport(true);
horizontalScrollView.addView(mInner, layoutParamsInner);
- LayoutParams layoutParams = new LayoutParams(
- LayoutParams.MATCH_PARENT,
- LayoutParams.MATCH_PARENT);
+ LayoutParams layoutParams =
+ new LayoutParams(LayoutParams.MATCH_PARENT, LayoutParams.MATCH_PARENT);
addView(horizontalScrollView, layoutParams);
}
- }
- break;
- case RootContentBehavior.SCROLL_VERTICAL: {
+ break;
+ case RootContentBehavior.SCROLL_VERTICAL:
if (!(mInner.getParent() instanceof ScrollView)) {
((ViewGroup) mInner.getParent()).removeView(mInner);
removeAllViews();
- LayoutParams layoutParamsInner = new LayoutParams(
- LayoutParams.MATCH_PARENT,
- LayoutParams.WRAP_CONTENT);
+ LayoutParams layoutParamsInner =
+ new LayoutParams(LayoutParams.MATCH_PARENT, LayoutParams.WRAP_CONTENT);
ScrollView scrollView = new ScrollView(getContext());
scrollView.setBackgroundColor(Color.TRANSPARENT);
scrollView.setFillViewport(true);
scrollView.addView(mInner, layoutParamsInner);
- LayoutParams layoutParams = new LayoutParams(
- LayoutParams.MATCH_PARENT,
- LayoutParams.MATCH_PARENT);
+ LayoutParams layoutParams =
+ new LayoutParams(LayoutParams.MATCH_PARENT, LayoutParams.MATCH_PARENT);
addView(scrollView, layoutParams);
}
- }
- break;
+ break;
default:
if (mInner.getParent() != this) {
((ViewGroup) mInner.getParent()).removeView(mInner);
removeAllViews();
- LayoutParams layoutParams = new LayoutParams(
- LayoutParams.MATCH_PARENT,
- LayoutParams.MATCH_PARENT);
+ LayoutParams layoutParams =
+ new LayoutParams(LayoutParams.MATCH_PARENT, LayoutParams.MATCH_PARENT);
addView(mInner, layoutParams);
}
}
}
private void init(Context context, AttributeSet attrs, int defStyleAttr) {
- LayoutParams layoutParams = new LayoutParams(LayoutParams.MATCH_PARENT,
- LayoutParams.MATCH_PARENT);
+ LayoutParams layoutParams =
+ new LayoutParams(LayoutParams.MATCH_PARENT, LayoutParams.MATCH_PARENT);
setBackgroundColor(Color.TRANSPARENT);
mInner = new RemoteComposeCanvas(context, attrs, defStyleAttr);
mInner.setBackgroundColor(Color.TRANSPARENT);
@@ -241,24 +229,25 @@ public class RemoteComposePlayer extends FrameLayout {
* Add a callback for handling click events on the document
*
* @param callback the callback lambda that will be used when a click is detected
- * <p>
- * The parameter of the callback are:
- * id : the id of the clicked area
- * metadata: a client provided unstructured string associated with that area
+ * <p>The parameter of the callback are:
+ * <ul>
+ * <li>id : the id of the clicked area
+ * <li>metadata: a client provided unstructured string associated with that area
+ * </ul>
*/
public void addClickListener(ClickCallbacks callback) {
mInner.addClickListener((id, metadata) -> callback.click(id, metadata));
}
/**
- * Set the playback theme for the document. This allows to filter operations in order
- * to have the document adapt to the given theme. This method is intended to be used
- * to support night/light themes (system or app level), not custom themes.
+ * Set the playback theme for the document. This allows to filter operations in order to have
+ * the document adapt to the given theme. This method is intended to be used to support
+ * night/light themes (system or app level), not custom themes.
*
- * @param theme the theme used for playing the document. Possible values for theme are:
- * - Theme.UNSPECIFIED -- all instructions in the document will be executed
- * - Theme.DARK -- only executed NON Light theme instructions
- * - Theme.LIGHT -- only executed NON Dark theme instructions
+ * @param theme the theme used for playing the document. Possible values for theme are: -
+ * Theme.UNSPECIFIED -- all instructions in the document will be executed - Theme.DARK --
+ * only executed NON Light theme instructions - Theme.LIGHT -- only executed NON Dark theme
+ * instructions
*/
public void setTheme(int theme) {
if (mInner.getTheme() != theme) {
@@ -277,8 +266,7 @@ public class RemoteComposePlayer extends FrameLayout {
}
/**
- * This sets a color based on its name. Overriding the color set in
- * the document.
+ * This sets a color based on its name. Overriding the color set in the document.
*
* @param colorName Name of the color
* @param colorValue The new color value
@@ -364,7 +352,7 @@ public class RemoteComposePlayer extends FrameLayout {
case "colorFocusedHighlight":
setRColor(s, android.R.attr.colorFocusedHighlight);
break;
- case "colorForeground": // General foreground color for views.
+ case "colorForeground": // General foreground color for views.
setRColor(s, android.R.attr.colorForeground);
break;
// Foreground color for inverse backgrounds.
@@ -483,15 +471,14 @@ public class RemoteComposePlayer extends FrameLayout {
}
private int getColorFromResource(int id) {
+
TypedValue typedValue = new TypedValue();
- try (TypedArray arr = getContext()
- .getApplicationContext()
- .obtainStyledAttributes(typedValue.data, new int[]{id})) {
+ try (TypedArray arr =
+ getContext()
+ .getApplicationContext()
+ .obtainStyledAttributes(typedValue.data, new int[] {id})) {
int color = arr.getColor(0, -1);
return color;
}
}
-
-
}
-
diff --git a/core/java/com/android/internal/widget/remotecompose/player/platform/AndroidPaintContext.java b/core/java/com/android/internal/widget/remotecompose/player/platform/AndroidPaintContext.java
index 4416cf76295c..f59a0d3fa015 100644
--- a/core/java/com/android/internal/widget/remotecompose/player/platform/AndroidPaintContext.java
+++ b/core/java/com/android/internal/widget/remotecompose/player/platform/AndroidPaintContext.java
@@ -43,8 +43,8 @@ import java.util.ArrayList;
import java.util.List;
/**
- * An implementation of PaintContext for the Android Canvas.
- * This is used to play the RemoteCompose operations on Android.
+ * An implementation of PaintContext for the Android Canvas. This is used to play the RemoteCompose
+ * operations on Android.
*/
public class AndroidPaintContext extends PaintContext {
Paint mPaint = new Paint();
@@ -83,37 +83,36 @@ public class AndroidPaintContext extends PaintContext {
/**
* Draw an image onto the canvas
*
- * @param imageId the id of the image
- * @param srcLeft left coordinate of the source area
- * @param srcTop top coordinate of the source area
- * @param srcRight right coordinate of the source area
+ * @param imageId the id of the image
+ * @param srcLeft left coordinate of the source area
+ * @param srcTop top coordinate of the source area
+ * @param srcRight right coordinate of the source area
* @param srcBottom bottom coordinate of the source area
- * @param dstLeft left coordinate of the destination area
- * @param dstTop top coordinate of the destination area
- * @param dstRight right coordinate of the destination area
+ * @param dstLeft left coordinate of the destination area
+ * @param dstTop top coordinate of the destination area
+ * @param dstRight right coordinate of the destination area
* @param dstBottom bottom coordinate of the destination area
*/
-
@Override
- public void drawBitmap(int imageId,
- int srcLeft,
- int srcTop,
- int srcRight,
- int srcBottom,
- int dstLeft,
- int dstTop,
- int dstRight,
- int dstBottom,
- int cdId) {
+ public void drawBitmap(
+ int imageId,
+ int srcLeft,
+ int srcTop,
+ int srcRight,
+ int srcBottom,
+ int dstLeft,
+ int dstTop,
+ int dstRight,
+ int dstBottom,
+ int cdId) {
AndroidRemoteContext androidContext = (AndroidRemoteContext) mContext;
if (androidContext.mRemoteComposeState.containsId(imageId)) {
- Bitmap bitmap = (Bitmap) androidContext.mRemoteComposeState
- .getFromId(imageId);
+ Bitmap bitmap = (Bitmap) androidContext.mRemoteComposeState.getFromId(imageId);
mCanvas.drawBitmap(
bitmap,
new Rect(srcLeft, srcTop, srcRight, srcBottom),
- new Rect(dstLeft, dstTop, dstRight, dstBottom), mPaint
- );
+ new Rect(dstLeft, dstTop, dstRight, dstBottom),
+ mPaint);
}
}
@@ -128,28 +127,23 @@ public class AndroidPaintContext extends PaintContext {
}
@Override
- public void drawArc(float left,
- float top,
- float right,
- float bottom,
- float startAngle,
- float sweepAngle) {
- mCanvas.drawArc(left, top, right, bottom, startAngle,
- sweepAngle, true, mPaint);
+ public void drawArc(
+ float left, float top, float right, float bottom, float startAngle, float sweepAngle) {
+ mCanvas.drawArc(left, top, right, bottom, startAngle, sweepAngle, false, mPaint);
+ }
+
+ @Override
+ public void drawSector(
+ float left, float top, float right, float bottom, float startAngle, float sweepAngle) {
+ mCanvas.drawArc(left, top, right, bottom, startAngle, sweepAngle, true, mPaint);
}
@Override
- public void drawBitmap(int id,
- float left,
- float top,
- float right,
- float bottom) {
+ public void drawBitmap(int id, float left, float top, float right, float bottom) {
AndroidRemoteContext androidContext = (AndroidRemoteContext) mContext;
if (androidContext.mRemoteComposeState.containsId(id)) {
- Bitmap bitmap =
- (Bitmap) androidContext.mRemoteComposeState.getFromId(id);
- Rect src = new Rect(0, 0,
- bitmap.getWidth(), bitmap.getHeight());
+ Bitmap bitmap = (Bitmap) androidContext.mRemoteComposeState.getFromId(id);
+ Rect src = new Rect(0, 0, bitmap.getWidth(), bitmap.getHeight());
RectF dst = new RectF(left, top, right, bottom);
mCanvas.drawBitmap(bitmap, src, dst, mPaint);
}
@@ -191,21 +185,13 @@ public class AndroidPaintContext extends PaintContext {
}
@Override
- public void drawRoundRect(float left,
- float top,
- float right,
- float bottom,
- float radiusX,
- float radiusY) {
- mCanvas.drawRoundRect(left, top, right, bottom,
- radiusX, radiusY, mPaint);
+ public void drawRoundRect(
+ float left, float top, float right, float bottom, float radiusX, float radiusY) {
+ mCanvas.drawRoundRect(left, top, right, bottom, radiusX, radiusY, mPaint);
}
@Override
- public void drawTextOnPath(int textId,
- int pathId,
- float hOffset,
- float vOffset) {
+ public void drawTextOnPath(int textId, int pathId, float hOffset, float vOffset) {
mCanvas.drawTextOnPath(getText(textId), getPath(pathId, 0, 1), hOffset, vOffset, mPaint);
}
@@ -237,14 +223,15 @@ public class AndroidPaintContext extends PaintContext {
}
@Override
- public void drawTextRun(int textID,
- int start,
- int end,
- int contextStart,
- int contextEnd,
- float x,
- float y,
- boolean rtl) {
+ public void drawTextRun(
+ int textID,
+ int start,
+ int end,
+ int contextStart,
+ int contextEnd,
+ float x,
+ float y,
+ boolean rtl) {
String textToPaint = getText(textID);
if (textToPaint == null) {
@@ -262,11 +249,7 @@ public class AndroidPaintContext extends PaintContext {
}
@Override
- public void drawTweenPath(int path1Id,
- int path2Id,
- float tween,
- float start,
- float end) {
+ public void drawTweenPath(int path1Id, int path2Id, float tween, float start, float end) {
mCanvas.drawPath(getPath(path1Id, path2Id, tween, start, end), mPaint);
}
@@ -381,224 +364,219 @@ public class AndroidPaintContext extends PaintContext {
/**
* This applies paint changes to the current paint
*
- * @param mPaintData the list change to the paint
+ * @param paintData the list change to the paint
*/
@Override
- public void applyPaint(PaintBundle mPaintData) {
- mPaintData.applyPaintChange((PaintContext) this, new PaintChanges() {
- @Override
- public void setTextSize(float size) {
- mPaint.setTextSize(size);
- }
-
- @Override
- public void setTypeFace(int fontType, int weight, boolean italic) {
- int[] type = new int[]{Typeface.NORMAL, Typeface.BOLD,
- Typeface.ITALIC, Typeface.BOLD_ITALIC};
-
- switch (fontType) {
- case PaintBundle.FONT_TYPE_DEFAULT: {
- if (weight == 400 && !italic) { // for normal case
- mPaint.setTypeface(Typeface.DEFAULT);
- } else {
- mPaint.setTypeface(Typeface.create(Typeface.DEFAULT,
- weight, italic));
- }
- break;
- }
- case PaintBundle.FONT_TYPE_SERIF: {
- if (weight == 400 && !italic) { // for normal case
- mPaint.setTypeface(Typeface.SERIF);
- } else {
- mPaint.setTypeface(Typeface.create(Typeface.SERIF,
- weight, italic));
- }
- break;
+ public void applyPaint(PaintBundle paintData) {
+ paintData.applyPaintChange(
+ (PaintContext) this,
+ new PaintChanges() {
+ @Override
+ public void setTextSize(float size) {
+ mPaint.setTextSize(size);
}
- case PaintBundle.FONT_TYPE_SANS_SERIF: {
- if (weight == 400 && !italic) { // for normal case
- mPaint.setTypeface(Typeface.SANS_SERIF);
- } else {
- mPaint.setTypeface(
- Typeface.create(Typeface.SANS_SERIF,
- weight, italic));
+
+ @Override
+ public void setTypeFace(int fontType, int weight, boolean italic) {
+ int[] type =
+ new int[] {
+ Typeface.NORMAL,
+ Typeface.BOLD,
+ Typeface.ITALIC,
+ Typeface.BOLD_ITALIC
+ };
+
+ switch (fontType) {
+ case PaintBundle.FONT_TYPE_DEFAULT:
+ if (weight == 400 && !italic) { // for normal case
+ mPaint.setTypeface(Typeface.DEFAULT);
+ } else {
+ mPaint.setTypeface(
+ Typeface.create(Typeface.DEFAULT, weight, italic));
+ }
+ break;
+ case PaintBundle.FONT_TYPE_SERIF:
+ if (weight == 400 && !italic) { // for normal case
+ mPaint.setTypeface(Typeface.SERIF);
+ } else {
+ mPaint.setTypeface(
+ Typeface.create(Typeface.SERIF, weight, italic));
+ }
+ break;
+ case PaintBundle.FONT_TYPE_SANS_SERIF:
+ if (weight == 400 && !italic) { // for normal case
+ mPaint.setTypeface(Typeface.SANS_SERIF);
+ } else {
+ mPaint.setTypeface(
+ Typeface.create(Typeface.SANS_SERIF, weight, italic));
+ }
+ break;
+ case PaintBundle.FONT_TYPE_MONOSPACE:
+ if (weight == 400 && !italic) { // for normal case
+ mPaint.setTypeface(Typeface.MONOSPACE);
+ } else {
+ mPaint.setTypeface(
+ Typeface.create(Typeface.MONOSPACE, weight, italic));
+ }
+
+ break;
}
- break;
}
- case PaintBundle.FONT_TYPE_MONOSPACE: {
- if (weight == 400 && !italic) { // for normal case
- mPaint.setTypeface(Typeface.MONOSPACE);
- } else {
- mPaint.setTypeface(
- Typeface.create(Typeface.MONOSPACE,
- weight, italic));
- }
- break;
+ @Override
+ public void setStrokeWidth(float width) {
+ mPaint.setStrokeWidth(width);
}
- }
- }
-
- @Override
- public void setStrokeWidth(float width) {
- mPaint.setStrokeWidth(width);
- }
-
- @Override
- public void setColor(int color) {
- mPaint.setColor(color);
- }
+ @Override
+ public void setColor(int color) {
+ mPaint.setColor(color);
+ }
- @Override
- public void setStrokeCap(int cap) {
- mPaint.setStrokeCap(Paint.Cap.values()[cap]);
- }
+ @Override
+ public void setStrokeCap(int cap) {
+ mPaint.setStrokeCap(Paint.Cap.values()[cap]);
+ }
- @Override
- public void setStyle(int style) {
- mPaint.setStyle(Paint.Style.values()[style]);
- }
+ @Override
+ public void setStyle(int style) {
+ mPaint.setStyle(Paint.Style.values()[style]);
+ }
- @Override
- public void setShader(int shaderId) {
- // TODO this stuff should check the shader creation
- if (shaderId == 0) {
- mPaint.setShader(null);
- return;
- }
- ShaderData data = getShaderData(shaderId);
- RuntimeShader shader = new RuntimeShader(getText(data.getShaderTextId()));
- String[] names = data.getUniformFloatNames();
- for (int i = 0; i < names.length; i++) {
- String name = names[i];
- float[] val = data.getUniformFloats(name);
- shader.setFloatUniform(name, val);
- }
- names = data.getUniformIntegerNames();
- for (int i = 0; i < names.length; i++) {
- String name = names[i];
- int[] val = data.getUniformInts(name);
- shader.setIntUniform(name, val);
- }
- names = data.getUniformBitmapNames();
- for (int i = 0; i < names.length; i++) {
- String name = names[i];
- int val = data.getUniformBitmapId(name);
- }
- mPaint.setShader(shader);
- }
+ @Override
+ public void setShader(int shaderId) {
+ // TODO this stuff should check the shader creation
+ if (shaderId == 0) {
+ mPaint.setShader(null);
+ return;
+ }
+ ShaderData data = getShaderData(shaderId);
+ RuntimeShader shader = new RuntimeShader(getText(data.getShaderTextId()));
+ String[] names = data.getUniformFloatNames();
+ for (int i = 0; i < names.length; i++) {
+ String name = names[i];
+ float[] val = data.getUniformFloats(name);
+ shader.setFloatUniform(name, val);
+ }
+ names = data.getUniformIntegerNames();
+ for (int i = 0; i < names.length; i++) {
+ String name = names[i];
+ int[] val = data.getUniformInts(name);
+ shader.setIntUniform(name, val);
+ }
+ names = data.getUniformBitmapNames();
+ for (int i = 0; i < names.length; i++) {
+ String name = names[i];
+ int val = data.getUniformBitmapId(name);
+ }
+ mPaint.setShader(shader);
+ }
- @Override
- public void setImageFilterQuality(int quality) {
- Utils.log(" quality =" + quality);
- }
+ @Override
+ public void setImageFilterQuality(int quality) {
+ Utils.log(" quality =" + quality);
+ }
- @Override
- public void setBlendMode(int mode) {
- mPaint.setBlendMode(origamiToBlendMode(mode));
- }
+ @Override
+ public void setBlendMode(int mode) {
+ mPaint.setBlendMode(origamiToBlendMode(mode));
+ }
- @Override
- public void setAlpha(float a) {
- mPaint.setAlpha((int) (255 * a));
- }
+ @Override
+ public void setAlpha(float a) {
+ mPaint.setAlpha((int) (255 * a));
+ }
- @Override
- public void setStrokeMiter(float miter) {
- mPaint.setStrokeMiter(miter);
- }
+ @Override
+ public void setStrokeMiter(float miter) {
+ mPaint.setStrokeMiter(miter);
+ }
- @Override
- public void setStrokeJoin(int join) {
- mPaint.setStrokeJoin(Paint.Join.values()[join]);
- }
+ @Override
+ public void setStrokeJoin(int join) {
+ mPaint.setStrokeJoin(Paint.Join.values()[join]);
+ }
- @Override
- public void setFilterBitmap(boolean filter) {
- mPaint.setFilterBitmap(filter);
- }
+ @Override
+ public void setFilterBitmap(boolean filter) {
+ mPaint.setFilterBitmap(filter);
+ }
- @Override
- public void setAntiAlias(boolean aa) {
- mPaint.setAntiAlias(aa);
- }
+ @Override
+ public void setAntiAlias(boolean aa) {
+ mPaint.setAntiAlias(aa);
+ }
- @Override
- public void clear(long mask) {
- if (true) return;
- long m = mask;
- int k = 1;
- while (m > 0) {
- if ((m & 1) == 1L) {
- switch (k) {
-
- case PaintBundle.COLOR_FILTER:
- mPaint.setColorFilter(null);
- break;
+ @Override
+ public void clear(long mask) {
+ if ((mask & (1L << PaintBundle.COLOR_FILTER)) != 0) {
+ mPaint.setColorFilter(null);
}
}
- k++;
- m = m >> 1;
- }
- }
- Shader.TileMode[] mTileModes = new Shader.TileMode[]{
- Shader.TileMode.CLAMP,
- Shader.TileMode.REPEAT,
- Shader.TileMode.MIRROR};
-
- @Override
- public void setLinearGradient(int[] colors,
- float[] stops,
- float startX,
- float startY,
- float endX,
- float endY,
- int tileMode) {
- mPaint.setShader(new LinearGradient(startX,
- startY,
- endX,
- endY, colors, stops, mTileModes[tileMode]));
-
- }
-
- @Override
- public void setRadialGradient(int[] colors,
- float[] stops,
- float centerX,
- float centerY,
- float radius,
- int tileMode) {
- mPaint.setShader(new RadialGradient(centerX, centerY, radius,
- colors, stops, mTileModes[tileMode]));
- }
+ Shader.TileMode[] mTileModes =
+ new Shader.TileMode[] {
+ Shader.TileMode.CLAMP,
+ Shader.TileMode.REPEAT,
+ Shader.TileMode.MIRROR
+ };
+
+ @Override
+ public void setLinearGradient(
+ int[] colors,
+ float[] stops,
+ float startX,
+ float startY,
+ float endX,
+ float endY,
+ int tileMode) {
+ mPaint.setShader(
+ new LinearGradient(
+ startX,
+ startY,
+ endX,
+ endY,
+ colors,
+ stops,
+ mTileModes[tileMode]));
+ }
- @Override
- public void setSweepGradient(int[] colors,
- float[] stops,
- float centerX,
- float centerY) {
- mPaint.setShader(new SweepGradient(centerX, centerY, colors, stops));
+ @Override
+ public void setRadialGradient(
+ int[] colors,
+ float[] stops,
+ float centerX,
+ float centerY,
+ float radius,
+ int tileMode) {
+ mPaint.setShader(
+ new RadialGradient(
+ centerX,
+ centerY,
+ radius,
+ colors,
+ stops,
+ mTileModes[tileMode]));
+ }
- }
+ @Override
+ public void setSweepGradient(
+ int[] colors, float[] stops, float centerX, float centerY) {
+ mPaint.setShader(new SweepGradient(centerX, centerY, colors, stops));
+ }
- @Override
- public void setColorFilter(int color, int mode) {
- PorterDuff.Mode pmode = origamiToPorterDuffMode(mode);
- if (pmode != null) {
- mPaint.setColorFilter(
- new PorterDuffColorFilter(color, pmode));
- }
- }
- });
+ @Override
+ public void setColorFilter(int color, int mode) {
+ PorterDuff.Mode pmode = origamiToPorterDuffMode(mode);
+ if (pmode != null) {
+ mPaint.setColorFilter(new PorterDuffColorFilter(color, pmode));
+ }
+ }
+ });
}
@Override
- public void matrixScale(float scaleX,
- float scaleY,
- float centerX,
- float centerY) {
+ public void matrixScale(float scaleX, float scaleY, float centerX, float centerY) {
if (Float.isNaN(centerX)) {
mCanvas.scale(scaleX, scaleY);
} else {
@@ -622,7 +600,6 @@ public class AndroidPaintContext extends PaintContext {
mCanvas.rotate(rotate);
} else {
mCanvas.rotate(rotate, pivotX, pivotY);
-
}
}
@@ -642,15 +619,27 @@ public class AndroidPaintContext extends PaintContext {
}
@Override
- public void roundedClipRect(float width, float height,
- float topStart, float topEnd,
- float bottomStart, float bottomEnd) {
+ public void roundedClipRect(
+ float width,
+ float height,
+ float topStart,
+ float topEnd,
+ float bottomStart,
+ float bottomEnd) {
Path roundedPath = new Path();
- float[] radii = new float[] { topStart, topStart,
- topEnd, topEnd, bottomEnd, bottomEnd, bottomStart, bottomStart};
-
- roundedPath.addRoundRect(0f, 0f, width, height,
- radii, android.graphics.Path.Direction.CW);
+ float[] radii =
+ new float[] {
+ topStart,
+ topStart,
+ topEnd,
+ topEnd,
+ bottomEnd,
+ bottomEnd,
+ bottomStart,
+ bottomStart
+ };
+
+ roundedPath.addRoundRect(0f, 0f, width, height, radii, android.graphics.Path.Direction.CW);
mCanvas.clipPath(roundedPath);
}
@@ -660,7 +649,7 @@ public class AndroidPaintContext extends PaintContext {
if (regionOp == ClipPath.DIFFERENCE) {
mCanvas.clipOutPath(path); // DIFFERENCE
} else {
- mCanvas.clipPath(path); // INTERSECT
+ mCanvas.clipPath(path); // INTERSECT
}
}
@@ -669,11 +658,7 @@ public class AndroidPaintContext extends PaintContext {
mPaint.reset();
}
- private Path getPath(int path1Id,
- int path2Id,
- float tween,
- float start,
- float end) {
+ private Path getPath(int path1Id, int path2Id, float tween, float start, float end) {
if (tween == 0.0f) {
return getPath(path1Id, start, end);
}
@@ -681,10 +666,8 @@ public class AndroidPaintContext extends PaintContext {
return getPath(path2Id, start, end);
}
AndroidRemoteContext androidContext = (AndroidRemoteContext) mContext;
- float[] data1 =
- (float[]) androidContext.mRemoteComposeState.getFromId(path1Id);
- float[] data2 =
- (float[]) androidContext.mRemoteComposeState.getFromId(path2Id);
+ float[] data1 = (float[]) androidContext.mRemoteComposeState.getFromId(path1Id);
+ float[] data2 = (float[]) androidContext.mRemoteComposeState.getFromId(path2Id);
float[] tmp = new float[data2.length];
for (int i = 0; i < tmp.length; i++) {
if (Float.isNaN(data1[i]) || Float.isNaN(data2[i])) {
@@ -702,8 +685,7 @@ public class AndroidPaintContext extends PaintContext {
AndroidRemoteContext androidContext = (AndroidRemoteContext) mContext;
Path path = new Path();
if (androidContext.mRemoteComposeState.containsId(id)) {
- float[] data =
- (float[]) androidContext.mRemoteComposeState.getFromId(id);
+ float[] data = (float[]) androidContext.mRemoteComposeState.getFromId(id);
FloatsToPath.genPath(path, data, start, end);
}
return path;
@@ -717,4 +699,3 @@ public class AndroidPaintContext extends PaintContext {
return (ShaderData) mContext.mRemoteComposeState.getFromId(id);
}
}
-
diff --git a/core/java/com/android/internal/widget/remotecompose/player/platform/AndroidPlatformServices.java b/core/java/com/android/internal/widget/remotecompose/player/platform/AndroidPlatformServices.java
new file mode 100644
index 000000000000..f9b22a25ceab
--- /dev/null
+++ b/core/java/com/android/internal/widget/remotecompose/player/platform/AndroidPlatformServices.java
@@ -0,0 +1,119 @@
+/*
+ * Copyright (C) 2023 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.internal.widget.remotecompose.player.platform;
+
+import android.graphics.Bitmap;
+import android.graphics.Path;
+import android.graphics.PathIterator;
+
+import com.android.internal.widget.remotecompose.core.Platform;
+import com.android.internal.widget.remotecompose.core.operations.PathData;
+
+import java.io.ByteArrayOutputStream;
+import java.util.Arrays;
+
+/** Services that are needed to be provided by the platform during encoding. */
+public class AndroidPlatformServices implements Platform {
+ @Override
+ public byte[] imageToByteArray(Object image) {
+ if (image instanceof Bitmap) {
+ // let's create a bitmap
+ ByteArrayOutputStream byteArrayBitmapStream = new ByteArrayOutputStream();
+ ((Bitmap) image).compress(Bitmap.CompressFormat.PNG, 90, byteArrayBitmapStream);
+ return byteArrayBitmapStream.toByteArray();
+ }
+ return null;
+ }
+
+ @Override
+ public int getImageWidth(Object image) {
+ if (image instanceof Bitmap) {
+ return ((Bitmap) image).getWidth();
+ }
+ return 0;
+ }
+
+ @Override
+ public int getImageHeight(Object image) {
+ if (image instanceof Bitmap) {
+ return ((Bitmap) image).getHeight();
+ }
+ return 0;
+ }
+
+ @Override
+ public float[] pathToFloatArray(Object path) {
+ // if (path is RemotePath) {
+ // return path.createFloatArray()
+ // }
+
+ if (path instanceof Path) {
+ return androidPathToFloatArray((Path) path);
+ }
+
+ return null;
+ }
+
+ private float[] androidPathToFloatArray(Path path) {
+ PathIterator i = path.getPathIterator();
+ int estimatedSize = 0;
+
+ while (i.hasNext()) {
+ i.next();
+ estimatedSize++;
+ }
+
+ PathIterator iter = path.getPathIterator();
+ float[] pathFloat = new float[estimatedSize * 10];
+
+ int count = 0;
+ while (i.hasNext()) {
+ PathIterator.Segment seg = i.next();
+
+ switch (seg.getVerb()) {
+ case PathIterator.VERB_MOVE:
+ pathFloat[count++] = PathData.MOVE_NAN;
+ break;
+ case PathIterator.VERB_LINE:
+ pathFloat[count++] = PathData.LINE_NAN;
+ break;
+ case PathIterator.VERB_QUAD:
+ pathFloat[count++] = PathData.QUADRATIC_NAN;
+ break;
+ case PathIterator.VERB_CONIC:
+ pathFloat[count++] = PathData.CONIC_NAN;
+ break;
+ case PathIterator.VERB_CUBIC:
+ pathFloat[count++] = PathData.CUBIC_NAN;
+ break;
+ case PathIterator.VERB_CLOSE:
+ pathFloat[count++] = PathData.CLOSE_NAN;
+ break;
+ case PathIterator.VERB_DONE:
+ pathFloat[count++] = PathData.DONE_NAN;
+ break;
+ }
+ for (float p : seg.getPoints()) {
+ pathFloat[count++] = p;
+ }
+ if (seg.getVerb() == PathIterator.VERB_CONIC) {
+ pathFloat[count++] = seg.getConicWeight();
+ }
+ }
+
+ return Arrays.copyOf(pathFloat, count);
+ }
+}
diff --git a/core/java/com/android/internal/widget/remotecompose/player/platform/AndroidRemoteContext.java b/core/java/com/android/internal/widget/remotecompose/player/platform/AndroidRemoteContext.java
index c98937530897..e7c0cc8a915d 100644
--- a/core/java/com/android/internal/widget/remotecompose/player/platform/AndroidRemoteContext.java
+++ b/core/java/com/android/internal/widget/remotecompose/player/platform/AndroidRemoteContext.java
@@ -24,13 +24,14 @@ import com.android.internal.widget.remotecompose.core.VariableSupport;
import com.android.internal.widget.remotecompose.core.operations.FloatExpression;
import com.android.internal.widget.remotecompose.core.operations.ShaderData;
import com.android.internal.widget.remotecompose.core.operations.utilities.ArrayAccess;
+import com.android.internal.widget.remotecompose.core.operations.utilities.DataMap;
import java.util.HashMap;
/**
* An implementation of Context for Android.
- * <p>
- * This is used to play the RemoteCompose operations on Android.
+ *
+ * <p>This is used to play the RemoteCompose operations on Android.
*/
class AndroidRemoteContext extends RemoteContext {
@@ -127,6 +128,16 @@ class AndroidRemoteContext extends RemoteContext {
}
@Override
+ public void putDataMap(int id, DataMap map) {
+ mRemoteComposeState.putDataMap(id, map);
+ }
+
+ @Override
+ public DataMap getDataMap(int id) {
+ return mRemoteComposeState.getDataMap(id);
+ }
+
+ @Override
public void runAction(int id, String metadata) {
mDocument.performClick(id);
}
@@ -140,7 +151,7 @@ class AndroidRemoteContext extends RemoteContext {
/**
* Decode a byte array into an image and cache it using the given imageId
*
- * @param width with of image to be loaded
+ * @param width with of image to be loaded
* @param height height of image to be loaded
* @param bitmap a byte array containing the image information
* @oaram imageId the id of the image
@@ -223,8 +234,18 @@ class AndroidRemoteContext extends RemoteContext {
}
@Override
+ public void putObject(int id, Object value) {
+ mRemoteComposeState.updateObject(id, value);
+ }
+
+ @Override
+ public Object getObject(int id) {
+ return mRemoteComposeState.getObject(id);
+ }
+
+ @Override
public int getInteger(int id) {
- return mRemoteComposeState.getInteger(id);
+ return mRemoteComposeState.getInteger(id);
}
@Override
@@ -252,17 +273,16 @@ class AndroidRemoteContext extends RemoteContext {
///////////////////////////////////////////////////////////////////////////////////////////////
@Override
- public void addClickArea(int id,
- int contentDescriptionId,
- float left,
- float top,
- float right,
- float bottom,
- int metadataId) {
+ public void addClickArea(
+ int id,
+ int contentDescriptionId,
+ float left,
+ float top,
+ float right,
+ float bottom,
+ int metadataId) {
String contentDescription = (String) mRemoteComposeState.getFromId(contentDescriptionId);
String metadata = (String) mRemoteComposeState.getFromId(metadataId);
mDocument.addClickArea(id, contentDescription, left, top, right, bottom, metadata);
}
-
}
-
diff --git a/core/java/com/android/internal/widget/remotecompose/player/platform/ClickAreaView.java b/core/java/com/android/internal/widget/remotecompose/player/platform/ClickAreaView.java
index 329178abe8b5..fdd9aad68d47 100644
--- a/core/java/com/android/internal/widget/remotecompose/player/platform/ClickAreaView.java
+++ b/core/java/com/android/internal/widget/remotecompose/player/platform/ClickAreaView.java
@@ -20,9 +20,7 @@ import android.graphics.Canvas;
import android.graphics.Paint;
import android.view.View;
-/**
- * Implementation for the click handling
- */
+/** Implementation for the click handling */
class ClickAreaView extends View {
private int mId;
private String mMetadata;
@@ -30,8 +28,8 @@ class ClickAreaView extends View {
private boolean mDebug;
- ClickAreaView(Context context, boolean debug, int id,
- String contentDescription, String metadata) {
+ ClickAreaView(
+ Context context, boolean debug, int id, String contentDescription, String metadata) {
super(context);
this.mId = id;
this.mMetadata = metadata;
diff --git a/core/java/com/android/internal/widget/remotecompose/player/platform/FloatsToPath.java b/core/java/com/android/internal/widget/remotecompose/player/platform/FloatsToPath.java
index d75232a6557e..8d3440c33efb 100644
--- a/core/java/com/android/internal/widget/remotecompose/player/platform/FloatsToPath.java
+++ b/core/java/com/android/internal/widget/remotecompose/player/platform/FloatsToPath.java
@@ -23,72 +23,56 @@ import android.graphics.PathMeasure;
import com.android.internal.widget.remotecompose.core.operations.PathData;
public class FloatsToPath {
- public static void genPath(Path retPath,
- float[] floatPath,
- float start,
- float stop) {
+ public static void genPath(Path retPath, float[] floatPath, float start, float stop) {
int i = 0;
Path path = new Path(); // todo this should be cached for performance
while (i < floatPath.length) {
switch (idFromNan(floatPath[i])) {
- case PathData.MOVE: {
+ case PathData.MOVE:
i++;
path.moveTo(floatPath[i + 0], floatPath[i + 1]);
i += 2;
- }
- break;
- case PathData.LINE: {
+ break;
+ case PathData.LINE:
i += 3;
path.lineTo(floatPath[i + 0], floatPath[i + 1]);
i += 2;
- }
- break;
- case PathData.QUADRATIC: {
+ break;
+ case PathData.QUADRATIC:
i += 3;
path.quadTo(
+ floatPath[i + 0], floatPath[i + 1], floatPath[i + 2], floatPath[i + 3]);
+ i += 4;
+ break;
+ case PathData.CONIC:
+ i += 3;
+
+ path.conicTo(
floatPath[i + 0],
floatPath[i + 1],
floatPath[i + 2],
- floatPath[i + 3]
- );
- i += 4;
+ floatPath[i + 3],
+ floatPath[i + 4]);
- }
- break;
- case PathData.CONIC: {
- i += 3;
- path.conicTo(
- floatPath[i + 0], floatPath[i + 1],
- floatPath[i + 2], floatPath[i + 3],
- floatPath[i + 4]
- );
i += 5;
- }
- break;
- case PathData.CUBIC: {
+ break;
+ case PathData.CUBIC:
i += 3;
path.cubicTo(
floatPath[i + 0], floatPath[i + 1],
floatPath[i + 2], floatPath[i + 3],
- floatPath[i + 4], floatPath[i + 5]
- );
+ floatPath[i + 4], floatPath[i + 5]);
i += 6;
- }
- break;
- case PathData.CLOSE: {
-
+ break;
+ case PathData.CLOSE:
path.close();
i++;
- }
- break;
- case PathData.DONE: {
+ break;
+ case PathData.DONE:
i++;
- }
- break;
- default: {
- System.err.println(" Odd command "
- + idFromNan(floatPath[i]));
- }
+ break;
+ default:
+ System.err.println(" Odd command " + idFromNan(floatPath[i]));
}
}
@@ -101,8 +85,7 @@ public class FloatsToPath {
float len = measure.getLength();
float scaleStart = Math.max(start, 0f) * len;
float scaleStop = Math.min(stop, 1f) * len;
- measure.getSegment(scaleStart, scaleStop, retPath,
- true);
+ measure.getSegment(scaleStart, scaleStop, retPath, true);
}
} else {
diff --git a/core/java/com/android/internal/widget/remotecompose/player/platform/RemoteComposeCanvas.java b/core/java/com/android/internal/widget/remotecompose/player/platform/RemoteComposeCanvas.java
index f91e15818471..7de6988157b7 100644
--- a/core/java/com/android/internal/widget/remotecompose/player/platform/RemoteComposeCanvas.java
+++ b/core/java/com/android/internal/widget/remotecompose/player/platform/RemoteComposeCanvas.java
@@ -31,9 +31,7 @@ import com.android.internal.widget.remotecompose.player.RemoteComposeDocument;
import java.util.Set;
-/**
- * Internal view handling the actual painting / interactions
- */
+/** Internal view handling the actual painting / interactions */
public class RemoteComposeCanvas extends FrameLayout implements View.OnAttachStateChangeListener {
static final boolean USE_VIEW_AREA_CLICK = true; // Use views to represent click areas
@@ -102,13 +100,17 @@ public class RemoteComposeCanvas extends FrameLayout implements View.OnAttachSta
private void updateClickAreas() {
if (USE_VIEW_AREA_CLICK && mDocument != null) {
mHasClickAreas = false;
- Set<CoreDocument.ClickAreaRepresentation> clickAreas = mDocument
- .getDocument().getClickAreas();
+ Set<CoreDocument.ClickAreaRepresentation> clickAreas =
+ mDocument.getDocument().getClickAreas();
removeAllViews();
for (CoreDocument.ClickAreaRepresentation area : clickAreas) {
- ClickAreaView viewArea = new ClickAreaView(getContext(), mDebug,
- area.getId(), area.getContentDescription(),
- area.getMetadata());
+ ClickAreaView viewArea =
+ new ClickAreaView(
+ getContext(),
+ mDebug,
+ area.getId(),
+ area.getContentDescription(),
+ area.getMetadata());
int w = (int) area.width();
int h = (int) area.height();
FrameLayout.LayoutParams param = new FrameLayout.LayoutParams(w, h);
@@ -116,8 +118,8 @@ public class RemoteComposeCanvas extends FrameLayout implements View.OnAttachSta
param.height = h;
param.leftMargin = (int) area.getLeft();
param.topMargin = (int) area.getTop();
- viewArea.setOnClickListener(view1
- -> mDocument.getDocument().performClick(area.getId()));
+ viewArea.setOnClickListener(
+ view1 -> mDocument.getDocument().performClick(area.getId()));
addView(viewArea, param);
}
if (!clickAreas.isEmpty()) {
@@ -201,23 +203,19 @@ public class RemoteComposeCanvas extends FrameLayout implements View.OnAttachSta
return super.onTouchEvent(event);
}
switch (event.getActionMasked()) {
- case MotionEvent.ACTION_DOWN: {
+ case MotionEvent.ACTION_DOWN:
mActionDownPoint.x = (int) event.getX();
mActionDownPoint.y = (int) event.getY();
mInActionDown = true;
return true;
- }
- case MotionEvent.ACTION_CANCEL: {
+ case MotionEvent.ACTION_CANCEL:
mInActionDown = false;
return true;
- }
- case MotionEvent.ACTION_UP: {
+ case MotionEvent.ACTION_UP:
mInActionDown = false;
performClick();
return true;
- }
- case MotionEvent.ACTION_MOVE: {
- }
+ case MotionEvent.ACTION_MOVE:
}
return false;
}
@@ -227,8 +225,9 @@ public class RemoteComposeCanvas extends FrameLayout implements View.OnAttachSta
if (USE_VIEW_AREA_CLICK && mHasClickAreas) {
return super.performClick();
}
- mDocument.getDocument().onClick(mARContext,
- (float) mActionDownPoint.x, (float) mActionDownPoint.y);
+ mDocument
+ .getDocument()
+ .onClick(mARContext, (float) mActionDownPoint.x, (float) mActionDownPoint.y);
super.performClick();
invalidate();
return true;
@@ -305,6 +304,4 @@ public class RemoteComposeCanvas extends FrameLayout implements View.OnAttachSta
invalidate();
}
}
-
}
-
diff --git a/core/jni/Android.bp b/core/jni/Android.bp
index 816ace2310a5..eb07f7c125d0 100644
--- a/core/jni/Android.bp
+++ b/core/jni/Android.bp
@@ -249,6 +249,7 @@ cc_library_shared_for_libandroid_runtime {
"android_backup_BackupDataOutput.cpp",
"android_backup_FileBackupHelperBase.cpp",
"android_backup_BackupHelperDispatcher.cpp",
+ "android_app_PropertyInvalidatedCache.cpp",
"android_app_backup_FullBackup.cpp",
"android_content_res_ApkAssets.cpp",
"android_content_res_ObbScanner.cpp",
diff --git a/core/jni/AndroidRuntime.cpp b/core/jni/AndroidRuntime.cpp
index 76f66cd4ebc9..821861efd59b 100644
--- a/core/jni/AndroidRuntime.cpp
+++ b/core/jni/AndroidRuntime.cpp
@@ -177,6 +177,7 @@ extern int register_android_app_backup_FullBackup(JNIEnv *env);
extern int register_android_app_Activity(JNIEnv *env);
extern int register_android_app_ActivityThread(JNIEnv *env);
extern int register_android_app_NativeActivity(JNIEnv *env);
+extern int register_android_app_PropertyInvalidatedCache(JNIEnv* env);
extern int register_android_media_RemoteDisplay(JNIEnv *env);
extern int register_android_util_jar_StrictJarFile(JNIEnv* env);
extern int register_android_view_InputChannel(JNIEnv* env);
@@ -1659,6 +1660,7 @@ static const RegJNIRec gRegJNI[] = {
REG_JNI(register_android_app_Activity),
REG_JNI(register_android_app_ActivityThread),
REG_JNI(register_android_app_NativeActivity),
+ REG_JNI(register_android_app_PropertyInvalidatedCache),
REG_JNI(register_android_util_jar_StrictJarFile),
REG_JNI(register_android_view_InputChannel),
REG_JNI(register_android_view_InputEventReceiver),
diff --git a/core/jni/android_app_PropertyInvalidatedCache.cpp b/core/jni/android_app_PropertyInvalidatedCache.cpp
new file mode 100644
index 000000000000..ead66660a0a4
--- /dev/null
+++ b/core/jni/android_app_PropertyInvalidatedCache.cpp
@@ -0,0 +1,119 @@
+/*
+ * 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.
+ */
+
+#define LOG_TAG "CacheNonce"
+
+#include <string.h>
+#include <memory.h>
+
+#include <atomic>
+
+#include <nativehelper/JNIHelp.h>
+#include <nativehelper/scoped_primitive_array.h>
+#include <android-base/logging.h>
+
+#include "core_jni_helpers.h"
+#include "android_app_PropertyInvalidatedCache.h"
+
+namespace {
+
+using namespace android::app::PropertyInvalidatedCache;
+
+// Convert a jlong to a nonce block. This is a convenience function that should be inlined by
+// the compiler.
+inline SystemCacheNonce* sysCache(jlong ptr) {
+ return reinterpret_cast<SystemCacheNonce*>(ptr);
+}
+
+// Return the number of nonces in the nonce block.
+jint getMaxNonce(JNIEnv*, jclass, jlong ptr) {
+ return sysCache(ptr)->getMaxNonce();
+}
+
+// Return the number of string bytes in the nonce block.
+jint getMaxByte(JNIEnv*, jclass, jlong ptr) {
+ return sysCache(ptr)->getMaxByte();
+}
+
+// Set the byte block. The first int is the hash to set and the second is the array to copy.
+// This should be synchronized in the Java layer.
+void setByteBlock(JNIEnv* env, jclass, jlong ptr, jint hash, jbyteArray val) {
+ ScopedByteArrayRO value(env, val);
+ if (value.get() == nullptr) {
+ jniThrowExceptionFmt(env, "java/lang/IllegalArgumentException", "null byte block");
+ return;
+ }
+ sysCache(ptr)->setByteBlock(hash, value.get(), value.size());
+}
+
+// Fetch the byte block. If the incoming hash is the same as the local hash, the Java layer is
+// presumed to have an up-to-date copy of the byte block; do not copy byte array. The local
+// hash is returned.
+jint getByteBlock(JNIEnv* env, jclass, jlong ptr, jint hash, jbyteArray val) {
+ if (sysCache(ptr)->getHash() == hash) {
+ return hash;
+ }
+ ScopedByteArrayRW value(env, val);
+ return sysCache(ptr)->getByteBlock(value.get(), value.size());
+}
+
+// Fetch the byte block hash.
+//
+// This is a CriticalNative method and therefore does not get the JNIEnv or jclass parameters.
+jint getByteBlockHash(jlong ptr) {
+ return sysCache(ptr)->getHash();
+}
+
+// Get a nonce value. So that this method can be CriticalNative, it returns 0 if the value is
+// out of range, rather than throwing an exception. This is a CriticalNative method and
+// therefore does not get the JNIEnv or jclass parameters.
+//
+// This method is @CriticalNative and does not take a JNIEnv* or jclass argument.
+jlong getNonce(jlong ptr, jint index) {
+ return sysCache(ptr)->getNonce(index);
+}
+
+// Set a nonce value. So that this method can be CriticalNative, it returns a boolean: false if
+// the index is out of range and true otherwise. Callers may test the returned boolean and
+// generate an exception.
+//
+// This method is @CriticalNative and does not take a JNIEnv* or jclass argument.
+jboolean setNonce(jlong ptr, jint index, jlong value) {
+ return sysCache(ptr)->setNonce(index, value);
+}
+
+static const JNINativeMethod gMethods[] = {
+ {"nativeGetMaxNonce", "(J)I", (void*) getMaxNonce },
+ {"nativeGetMaxByte", "(J)I", (void*) getMaxByte },
+ {"nativeSetByteBlock", "(JI[B)V", (void*) setByteBlock },
+ {"nativeGetByteBlock", "(JI[B)I", (void*) getByteBlock },
+ {"nativeGetByteBlockHash", "(J)I", (void*) getByteBlockHash },
+ {"nativeGetNonce", "(JI)J", (void*) getNonce },
+ {"nativeSetNonce", "(JIJ)Z", (void*) setNonce },
+};
+
+static const char* kClassName = "android/app/PropertyInvalidatedCache";
+
+} // anonymous namespace
+
+namespace android {
+
+int register_android_app_PropertyInvalidatedCache(JNIEnv* env) {
+ RegisterMethodsOrDie(env, kClassName, gMethods, NELEM(gMethods));
+ return JNI_OK;
+}
+
+} // namespace android
diff --git a/core/jni/android_app_PropertyInvalidatedCache.h b/core/jni/android_app_PropertyInvalidatedCache.h
new file mode 100644
index 000000000000..eefa8fa88624
--- /dev/null
+++ b/core/jni/android_app_PropertyInvalidatedCache.h
@@ -0,0 +1,146 @@
+/*
+ * 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.
+ */
+
+#include <string.h>
+#include <memory.h>
+
+#include <atomic>
+
+namespace android {
+namespace app {
+namespace PropertyInvalidatedCache {
+
+/**
+ * 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
+ * of the block but it does not interpret the fields.
+ *
+ * On initialization, all fields are set to zero.
+ *
+ * In general, methods do not report errors. This allows the methods to be used in
+ * CriticalNative JNI APIs.
+ *
+ * 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 {
+
+ // The value of an unset field.
+ static const int UNSET = 0;
+
+ // A convenient typedef. The jbyteArray element type is jbyte, which the compiler treats as
+ // signed char.
+ typedef signed char block_t;
+
+ // The array of nonces
+ volatile std::atomic<int64_t> mNonce[maxNonce];
+
+ // The byte array. This is not atomic but it is guarded by the mByteHash.
+ volatile block_t mByteBlock[maxByte];
+
+ // The hash that validates the byte block
+ volatile std::atomic<int32_t> mByteHash;
+
+ // Pad the class to a multiple of 8 bytes.
+ int32_t _pad;
+
+ public:
+
+ // The expected size of this instance. This is a compile-time constant and can be used in a
+ // static assertion.
+ static const int expectedSize =
+ maxNonce * sizeof(std::atomic<int64_t>)
+ + sizeof(std::atomic<int32_t>)
+ + maxByte * sizeof(block_t)
+ + sizeof(int32_t);
+
+ // These provide run-time access to the sizing parameters.
+ int getMaxNonce() const {
+ return maxNonce;
+ }
+
+ size_t getMaxByte() const {
+ return maxByte;
+ }
+
+ // Construct and initialize the memory.
+ CacheNonce() {
+ for (int i = 0; i < maxNonce; i++) {
+ mNonce[i] = UNSET;
+ }
+ mByteHash = UNSET;
+ memset((void*) mByteBlock, UNSET, sizeof(mByteBlock));
+ }
+
+ // 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
+ // to be called in a CriticalNative JNI API.
+ int64_t getNonce(int index) const {
+ if (index < 0 || index >= maxNonce) {
+ return UNSET;
+ } else {
+ return mNonce[index];
+ }
+ }
+
+ // Set a nonce and return true. Return false 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 to be called in a CriticalNative JNI API.
+ bool setNonce(int index, int64_t value) {
+ if (index < 0 || index >= maxNonce) {
+ return false;
+ } else {
+ mNonce[index] = value;
+ return true;
+ }
+ }
+
+ // Fetch just the byte-block hash
+ int32_t getHash() const {
+ return mByteHash;
+ }
+
+ // Copy the byte block to the target and return the current hash.
+ int32_t getByteBlock(block_t* block, size_t len) const {
+ memcpy(block, (void*) mByteBlock, std::min(maxByte, len));
+ return mByteHash;
+ }
+
+ // Set the byte block and the hash.
+ void setByteBlock(int hash, const block_t* block, size_t len) {
+ memcpy((void*) mByteBlock, block, len = std::min(maxByte, len));
+ mByteHash = hash;
+ }
+};
+
+/**
+ * Sizing parameters for the system_server PropertyInvalidatedCache support. A client can
+ * retrieve the values through the accessors in CacheNonce instances.
+ */
+static const int MAX_NONCE = 64;
+static const int BYTE_BLOCK_SIZE = 8192;
+
+// The CacheNonce for system server holds 64 nonces with a string block of 8192 bytes.
+typedef CacheNonce<MAX_NONCE, BYTE_BLOCK_SIZE> SystemCacheNonce;
+
+// The goal of this assertion is to ensure that the data structure is the same size across 32-bit
+// and 64-bit systems.
+static_assert(sizeof(SystemCacheNonce) == SystemCacheNonce::expectedSize,
+ "Unexpected SystemCacheNonce size");
+
+} // namespace PropertyInvalidatedCache
+} // namespace app
+} // namespace android
diff --git a/core/jni/android_hardware_Camera.cpp b/core/jni/android_hardware_Camera.cpp
index 10e49efee92e..50252c11ffb1 100644
--- a/core/jni/android_hardware_Camera.cpp
+++ b/core/jni/android_hardware_Camera.cpp
@@ -27,6 +27,7 @@
#include <camera/StringUtils.h>
#include <com_android_internal_camera_flags.h>
#include <cutils/properties.h>
+#include <gui/Flags.h>
#include <gui/GLConsumer.h>
#include <gui/Surface.h>
#include <nativehelper/JNIHelp.h>
@@ -715,16 +716,20 @@ static void android_hardware_Camera_setPreviewSurface(JNIEnv *env, jobject thiz,
sp<Camera> camera = get_native_camera(env, thiz, NULL);
if (camera == 0) return;
- sp<IGraphicBufferProducer> gbp;
sp<Surface> surface;
if (jSurface) {
surface = android_view_Surface_getSurface(env, jSurface);
- if (surface != NULL) {
- gbp = surface->getIGraphicBufferProducer();
- }
}
+#if WB_LIBCAMERASERVICE_WITH_DEPENDENCIES
+ if (camera->setPreviewTarget(surface) != NO_ERROR) {
+#else
+ sp<IGraphicBufferProducer> gbp;
+ if (surface != NULL) {
+ gbp = surface->getIGraphicBufferProducer();
+ }
if (camera->setPreviewTarget(gbp) != NO_ERROR) {
+#endif
jniThrowException(env, "java/io/IOException", "setPreviewTexture failed");
}
}
@@ -736,6 +741,9 @@ static void android_hardware_Camera_setPreviewTexture(JNIEnv *env,
sp<Camera> camera = get_native_camera(env, thiz, NULL);
if (camera == 0) return;
+#if WB_LIBCAMERASERVICE_WITH_DEPENDENCIES
+ sp<Surface> surface;
+#endif
sp<IGraphicBufferProducer> producer = NULL;
if (jSurfaceTexture != NULL) {
producer = SurfaceTexture_getProducer(env, jSurfaceTexture);
@@ -744,10 +752,16 @@ static void android_hardware_Camera_setPreviewTexture(JNIEnv *env,
"SurfaceTexture already released in setPreviewTexture");
return;
}
-
+#if WB_LIBCAMERASERVICE_WITH_DEPENDENCIES
+ surface = new Surface(producer);
+#endif
}
+#if WB_LIBCAMERASERVICE_WITH_DEPENDENCIES
+ if (camera->setPreviewTarget(surface) != NO_ERROR) {
+#else
if (camera->setPreviewTarget(producer) != NO_ERROR) {
+#endif
jniThrowException(env, "java/io/IOException",
"setPreviewTexture failed");
}
@@ -761,18 +775,32 @@ static void android_hardware_Camera_setPreviewCallbackSurface(JNIEnv *env,
sp<Camera> camera = get_native_camera(env, thiz, &context);
if (camera == 0) return;
+#if !WB_LIBCAMERASERVICE_WITH_DEPENDENCIES
sp<IGraphicBufferProducer> gbp;
+#endif
sp<Surface> surface;
if (jSurface) {
surface = android_view_Surface_getSurface(env, jSurface);
+ if (surface == NULL) {
+ jniThrowException(env, "java/lang/IllegalArgumentException",
+ "android_view_Surface_getSurface failed");
+ return;
+ }
+
+#if !WB_LIBCAMERASERVICE_WITH_DEPENDENCIES
if (surface != NULL) {
gbp = surface->getIGraphicBufferProducer();
}
+#endif
}
// Clear out normal preview callbacks
context->setCallbackMode(env, false, false);
// Then set up callback surface
+#if WB_LIBCAMERASERVICE_WITH_DEPENDENCIES
+ if (camera->setPreviewCallbackTarget(surface) != NO_ERROR) {
+#else
if (camera->setPreviewCallbackTarget(gbp) != NO_ERROR) {
+#endif
jniThrowException(env, "java/io/IOException", "setPreviewCallbackTarget failed");
}
}
diff --git a/core/jni/android_view_SurfaceControl.cpp b/core/jni/android_view_SurfaceControl.cpp
index 755704a5ad91..f162b7410b10 100644
--- a/core/jni/android_view_SurfaceControl.cpp
+++ b/core/jni/android_view_SurfaceControl.cpp
@@ -35,6 +35,7 @@
#include <android_runtime/android_view_SurfaceSession.h>
#include <cutils/ashmem.h>
#include <gui/ISurfaceComposer.h>
+#include <gui/JankInfo.h>
#include <gui/Surface.h>
#include <gui/SurfaceComposerClient.h>
#include <jni.h>
@@ -2161,9 +2162,30 @@ public:
jobjectArray jJankDataArray = env->NewObjectArray(jankData.size(),
gJankDataClassInfo.clazz, nullptr);
for (size_t i = 0; i < jankData.size(); i++) {
+ // The exposed constants in SurfaceControl are simplified, so we need to translate the
+ // jank type we get from SF to what is exposed in Java.
+ int sfJankType = jankData[i].jankType;
+ int javaJankType = 0x0; // SurfaceControl.JankData.JANK_NONE
+ if (sfJankType &
+ (JankType::DisplayHAL | JankType::SurfaceFlingerCpuDeadlineMissed |
+ JankType::SurfaceFlingerGpuDeadlineMissed | JankType::PredictionError |
+ JankType::SurfaceFlingerScheduling)) {
+ javaJankType |= 0x1; // SurfaceControl.JankData.JANK_COMPOSER
+ }
+ if (sfJankType & JankType::AppDeadlineMissed) {
+ javaJankType |= 0x2; // SurfaceControl.JankData.JANK_APPLICATION
+ }
+ if (sfJankType &
+ ~(JankType::DisplayHAL | JankType::SurfaceFlingerCpuDeadlineMissed |
+ JankType::SurfaceFlingerGpuDeadlineMissed | JankType::AppDeadlineMissed |
+ JankType::PredictionError | JankType::SurfaceFlingerScheduling |
+ JankType::BufferStuffing | JankType::SurfaceFlingerStuffing)) {
+ javaJankType |= 0x4; // SurfaceControl.JankData.JANK_OTHER
+ }
+
jobject jJankData =
env->NewObject(gJankDataClassInfo.clazz, gJankDataClassInfo.ctor,
- jankData[i].frameVsyncId, jankData[i].jankType,
+ jankData[i].frameVsyncId, javaJankType,
jankData[i].frameIntervalNs, jankData[i].scheduledAppFrameTimeNs,
jankData[i].actualAppFrameTimeNs);
env->SetObjectArrayElement(jJankDataArray, i, jJankData);
diff --git a/core/jni/com_android_internal_os_ApplicationSharedMemory.cpp b/core/jni/com_android_internal_os_ApplicationSharedMemory.cpp
index 453e53974e0d..cc1687cd9ffb 100644
--- a/core/jni/com_android_internal_os_ApplicationSharedMemory.cpp
+++ b/core/jni/com_android_internal_os_ApplicationSharedMemory.cpp
@@ -29,8 +29,12 @@
#include "core_jni_helpers.h"
+#include "android_app_PropertyInvalidatedCache.h"
+
namespace {
+using namespace android::app::PropertyInvalidatedCache;
+
// Atomics should be safe to use across processes if they are lock free.
static_assert(std::atomic<int64_t>::is_always_lock_free == true,
"atomic<int64_t> is not always lock free");
@@ -64,12 +68,15 @@ public:
void setLatestNetworkTimeUnixEpochMillisAtZeroElapsedRealtimeMillis(int64_t offset) {
latestNetworkTimeUnixEpochMillisAtZeroElapsedRealtimeMillis = offset;
}
+
+ // The nonce storage for pic. The sizing is suitable for the system server module.
+ SystemCacheNonce systemPic;
};
// Update the expected value when modifying the members of SharedMemory.
// The goal of this assertion is to ensure that the data structure is the same size across 32-bit
// and 64-bit systems.
-static_assert(sizeof(SharedMemory) == 8, "Unexpected SharedMemory size");
+static_assert(sizeof(SharedMemory) == 8 + sizeof(SystemCacheNonce), "Unexpected SharedMemory size");
static jint nativeCreate(JNIEnv* env, jclass) {
// Create anonymous shared memory region
@@ -133,6 +140,12 @@ static jlong nativeGetLatestNetworkTimeUnixEpochMillisAtZeroElapsedRealtimeMilli
return sharedMemory->getLatestNetworkTimeUnixEpochMillisAtZeroElapsedRealtimeMillis();
}
+// This is a FastNative method. It takes the usual JNIEnv* and jclass* arguments.
+static jlong nativeGetSystemNonceBlock(JNIEnv*, jclass*, jlong ptr) {
+ SharedMemory* sharedMemory = reinterpret_cast<SharedMemory*>(ptr);
+ return reinterpret_cast<jlong>(&sharedMemory->systemPic);
+}
+
static const JNINativeMethod gMethods[] = {
{"nativeCreate", "()I", (void*)nativeCreate},
{"nativeMap", "(IZ)J", (void*)nativeMap},
@@ -143,16 +156,17 @@ static const JNINativeMethod gMethods[] = {
(void*)nativeSetLatestNetworkTimeUnixEpochMillisAtZeroElapsedRealtimeMillis},
{"nativeGetLatestNetworkTimeUnixEpochMillisAtZeroElapsedRealtimeMillis", "(J)J",
(void*)nativeGetLatestNetworkTimeUnixEpochMillisAtZeroElapsedRealtimeMillis},
+ {"nativeGetSystemNonceBlock", "(J)J", (void*) nativeGetSystemNonceBlock},
};
-} // anonymous namespace
-
-namespace android {
-
static const char kApplicationSharedMemoryClassName[] =
"com/android/internal/os/ApplicationSharedMemory";
static jclass gApplicationSharedMemoryClass;
+} // anonymous namespace
+
+namespace android {
+
int register_com_android_internal_os_ApplicationSharedMemory(JNIEnv* env) {
gApplicationSharedMemoryClass =
MakeGlobalRefOrDie(env, FindClassOrDie(env, kApplicationSharedMemoryClassName));
diff --git a/core/res/AndroidManifest.xml b/core/res/AndroidManifest.xml
index fb06e9630070..4c38246b35b0 100644
--- a/core/res/AndroidManifest.xml
+++ b/core/res/AndroidManifest.xml
@@ -8483,6 +8483,27 @@
<permission android:name="android.permission.RESERVED_FOR_TESTING_SIGNATURE"
android:protectionLevel="signature"/>
+ <!--
+ @SystemApi
+ @FlaggedApi("android.media.tv.flags.media_quality_fw")
+ Allows an application to access its picture profile from the media quality database.
+ <p> Protection level: signature|privileged|vendor privileged
+ @hide
+ -->
+ <permission android:name="android.permission.MANAGE_GLOBAL_PICTURE_QUALITY_SERVICE"
+ android:protectionLevel="signature|privileged|vendorPrivileged"
+ android:featureFlag="android.media.tv.flags.media_quality_fw"/>
+
+ <!--
+ @SystemApi
+ @FlaggedApi("android.media.tv.flags.media_quality_fw")
+ Allows an application to access its sound profile from the media quality database.
+ <p> Protection level: signature|privileged|vendor privileged
+ @hide
+ -->
+ <permission android:name="android.permission.MANAGE_GLOBAL_SOUND_QUALITY_SERVICE"
+ android:protectionLevel="signature|privileged|vendorPrivileged"
+ android:featureFlag="android.media.tv.flags.media_quality_fw"/>
<!-- @SystemApi
@FlaggedApi("android.content.pm.verification_service")
Allows app to be the verification agent to verify packages.
diff --git a/core/res/res/color-watch-v36/btn_material_filled_background_color.xml b/core/res/res/color-watch-v36/btn_material_filled_background_color.xml
index 8b2afa86986c..70aace4e7d76 100644
--- a/core/res/res/color-watch-v36/btn_material_filled_background_color.xml
+++ b/core/res/res/color-watch-v36/btn_material_filled_background_color.xml
@@ -18,6 +18,5 @@
<item android:state_enabled="false"
android:alpha="?attr/disabledAlpha"
android:color="?attr/materialColorOnSurface" />
- <item android:state_enabled="true"
- android:color="?attr/materialColorPrimary" />
+ <item android:color="?attr/materialColorPrimary" />
</selector> \ No newline at end of file
diff --git a/core/res/res/color-watch-v36/btn_material_filled_text_color.xml b/core/res/res/color-watch-v36/btn_material_filled_content_color.xml
index cefc9121b7a4..4cc8fe5ecb91 100644
--- a/core/res/res/color-watch-v36/btn_material_filled_text_color.xml
+++ b/core/res/res/color-watch-v36/btn_material_filled_content_color.xml
@@ -18,6 +18,5 @@
<item android:state_enabled="false"
android:alpha="?attr/primaryContentAlpha"
android:color="?attr/materialColorOnSurface" />
- <item android:state_enabled="true"
- android:color="?attr/materialColorOnPrimary" />
+ <item android:color="?attr/materialColorOnPrimary" />
</selector> \ No newline at end of file
diff --git a/core/res/res/color-watch-v36/btn_material_filled_tonal_background_color.xml b/core/res/res/color-watch-v36/btn_material_filled_tonal_background_color.xml
index eaf9e7d50bbd..b2a25af0d670 100644
--- a/core/res/res/color-watch-v36/btn_material_filled_tonal_background_color.xml
+++ b/core/res/res/color-watch-v36/btn_material_filled_tonal_background_color.xml
@@ -18,6 +18,5 @@
<item android:state_enabled="false"
android:alpha="?attr/disabledAlpha"
android:color="?attr/materialColorOnSurface" />
- <item android:state_enabled="true"
- android:color="?attr/materialColorSurfaceContainer" />
+ <item android:color="?attr/materialColorSurfaceContainer" />
</selector> \ No newline at end of file
diff --git a/core/res/res/color-watch-v36/btn_material_filled_tonal_text_color.xml b/core/res/res/color-watch-v36/btn_material_filled_tonal_content_color.xml
index 94e50fbe2533..59810356c3b4 100644
--- a/core/res/res/color-watch-v36/btn_material_filled_tonal_text_color.xml
+++ b/core/res/res/color-watch-v36/btn_material_filled_tonal_content_color.xml
@@ -18,6 +18,5 @@
<item android:state_enabled="false"
android:alpha="?attr/primaryContentAlpha"
android:color="?attr/materialColorOnSurface" />
- <item android:state_enabled="true"
- android:color="?attr/materialColorOnSurface" />
+ <item android:color="?attr/materialColorOnSurface" />
</selector> \ No newline at end of file
diff --git a/core/res/res/drawable-watch-v36/dialog_alert_button_background_negative.xml b/core/res/res/drawable-watch-v36/dialog_alert_button_background_negative.xml
new file mode 100644
index 000000000000..b6b8eac3a547
--- /dev/null
+++ b/core/res/res/drawable-watch-v36/dialog_alert_button_background_negative.xml
@@ -0,0 +1,25 @@
+<?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.
+ -->
+
+<shape xmlns:android="http://schemas.android.com/apk/res/android"
+ android:shape="rectangle">
+ <solid android:color="@color/btn_material_filled_tonal_background_color"/>
+ <corners android:radius="@dimen/config_bottomDialogCornerRadius" />
+ <size
+ android:width="@dimen/dialog_btn_negative_width"
+ android:height="@dimen/dialog_btn_negative_height" />
+</shape>
diff --git a/core/res/res/drawable-watch-v36/dialog_alert_button_background_positive.xml b/core/res/res/drawable-watch-v36/dialog_alert_button_background_positive.xml
new file mode 100644
index 000000000000..92262fb6d89d
--- /dev/null
+++ b/core/res/res/drawable-watch-v36/dialog_alert_button_background_positive.xml
@@ -0,0 +1,30 @@
+<?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.
+ -->
+
+<rotate xmlns:android="http://schemas.android.com/apk/res/android"
+ android:fromDegrees="-45"
+ android:toDegrees="-45"
+ android:pivotX="50%"
+ android:pivotY="50%">
+ <shape android:shape="rectangle">
+ <solid android:color="@color/btn_material_filled_background_color"/>
+ <corners android:radius="200dp" />
+ <size
+ android:width="63dp"
+ android:height="54dp" />
+ </shape>
+</rotate> \ No newline at end of file
diff --git a/core/res/res/drawable-watch-v36/dialog_alert_button_negative.xml b/core/res/res/drawable-watch-v36/dialog_alert_button_negative.xml
new file mode 100644
index 000000000000..c155ba1ba077
--- /dev/null
+++ b/core/res/res/drawable-watch-v36/dialog_alert_button_negative.xml
@@ -0,0 +1,22 @@
+<!--
+ ~ 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.
+ -->
+
+<layer-list xmlns:android="http://schemas.android.com/apk/res/android">
+ <item android:drawable="@drawable/dialog_alert_button_background_negative"/>
+ <item
+ android:drawable="@drawable/ic_close"
+ android:gravity="center" />
+</layer-list> \ No newline at end of file
diff --git a/core/res/res/drawable-watch-v36/dialog_alert_button_positive.xml b/core/res/res/drawable-watch-v36/dialog_alert_button_positive.xml
new file mode 100644
index 000000000000..01ab0734d8e8
--- /dev/null
+++ b/core/res/res/drawable-watch-v36/dialog_alert_button_positive.xml
@@ -0,0 +1,22 @@
+<!--
+ ~ 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.
+ -->
+
+<layer-list xmlns:android="http://schemas.android.com/apk/res/android">
+ <item android:drawable="@drawable/dialog_alert_button_background_positive"/>
+ <item
+ android:drawable="@drawable/ic_check"
+ android:gravity="center" />
+</layer-list> \ No newline at end of file
diff --git a/core/res/res/drawable-watch-v36/ic_check.xml b/core/res/res/drawable-watch-v36/ic_check.xml
new file mode 100644
index 000000000000..7b01e64ffdd3
--- /dev/null
+++ b/core/res/res/drawable-watch-v36/ic_check.xml
@@ -0,0 +1,25 @@
+<!--
+ ~ 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.
+ -->
+
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="28dp"
+ android:height="28dp"
+ android:viewportWidth="960"
+ android:viewportHeight="960"
+ android:tint="@color/btn_material_filled_content_color">
+ <path android:fillColor="@color/btn_material_filled_content_color"
+ android:pathData="M382,597.87L716.7,263.17Q730.37,249.5 748.76,249.5Q767.15,249.5 780.83,263.17Q794.5,276.85 794.5,295.62Q794.5,314.39 780.83,328.07L414.07,695.59Q400.39,709.26 382,709.26Q363.61,709.26 349.93,695.59L178.41,524.07Q164.74,510.39 165.12,491.62Q165.5,472.85 179.17,459.17Q192.85,445.5 211.62,445.5Q230.39,445.5 244.07,459.17L382,597.87Z"/>
+</vector>
diff --git a/core/res/res/drawable-watch-v36/ic_close.xml b/core/res/res/drawable-watch-v36/ic_close.xml
new file mode 100644
index 000000000000..1f3da367ac3f
--- /dev/null
+++ b/core/res/res/drawable-watch-v36/ic_close.xml
@@ -0,0 +1,25 @@
+<!--
+ ~ 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.
+ -->
+
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="28dp"
+ android:height="28dp"
+ android:viewportWidth="960"
+ android:viewportHeight="960"
+ android:tint="@color/btn_material_filled_tonal_content_color">
+ <path android:fillColor="@color/btn_material_filled_tonal_content_color"
+ android:pathData="M480,543.65L287.83,735.83Q275.15,748.5 256,748.5Q236.85,748.5 224.17,735.83Q211.5,723.15 211.5,704Q211.5,684.85 224.17,672.17L416.35,480L224.17,287.83Q211.5,275.15 211.5,256Q211.5,236.85 224.17,224.17Q236.85,211.5 256,211.5Q275.15,211.5 287.83,224.17L480,416.35L672.17,224.17Q684.85,211.5 704,211.5Q723.15,211.5 735.83,224.17Q748.5,236.85 748.5,256Q748.5,275.15 735.83,287.83L543.65,480L735.83,672.17Q748.5,684.85 748.5,704Q748.5,723.15 735.83,735.83Q723.15,748.5 704,748.5Q684.85,748.5 672.17,735.83L480,543.65Z"/>
+</vector>
diff --git a/core/res/res/layout-watch-v36/alert_dialog_material.xml b/core/res/res/layout-watch-v36/alert_dialog_material.xml
new file mode 100644
index 000000000000..900102f379d9
--- /dev/null
+++ b/core/res/res/layout-watch-v36/alert_dialog_material.xml
@@ -0,0 +1,123 @@
+<!--
+ ~ 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.
+ -->
+
+<!-- This layout is the AlertDialog template. It overrides the system layout with the same name.
+ Make sure to include all the existing id of the overridden alert_dialog_material.-->
+<com.android.internal.widget.WatchListDecorLayout
+ xmlns:android="http://schemas.android.com/apk/res/android"
+ android:id="@+id/parentPanel"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent">
+ <ScrollView
+ android:id="@+id/scrollView"
+ android:fillViewport="true"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent">
+ <LinearLayout
+ android:orientation="vertical"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content">
+ <!-- Top Panel -->
+ <FrameLayout
+ android:paddingLeft="?dialogPreferredPadding"
+ android:paddingRight="?dialogPreferredPadding"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:id="@+id/topPanel"
+ android:minHeight="@dimen/dialog_list_padding_top_no_title">
+ <include android:id="@+id/title_template"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ layout="@layout/alert_dialog_title_material"/>
+ </FrameLayout>
+
+ <!-- Content Panel -->
+ <FrameLayout android:id="@+id/contentPanel"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:clipToPadding="false">
+ <TextView android:id="@+id/message"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:gravity="center_horizontal|top"
+ android:textAppearance="@style/TextAppearance.DeviceDefault.Body1"
+ android:paddingStart="?dialogPreferredPadding"
+ android:paddingEnd="?dialogPreferredPadding"
+ android:paddingTop="8dip"
+ android:paddingBottom="8dip"/>
+ </FrameLayout>
+
+ <!-- Custom Panel, to replace content panel if needed -->
+ <FrameLayout android:id="@+id/customPanel"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ android:minHeight="64dp">
+ <FrameLayout android:id="@+android:id/custom"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content" />
+ </FrameLayout>
+
+ <!-- Button Panel -->
+ <FrameLayout
+ android:id="@+id/buttonPanel"
+ android:minHeight="@dimen/dialog_list_padding_bottom_no_buttons"
+ android:layout_weight="1"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_gravity="center">
+ <LinearLayout
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_gravity="bottom"
+ android:orientation="horizontal"
+ android:paddingBottom="?dialogPreferredPadding"
+ style="?android:attr/buttonBarStyle"
+ android:measureWithLargestChild="true">
+ <Button android:id="@+id/button2"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_gravity="center"
+ android:gravity="center"
+ android:layout_weight="1"
+ style="@style/Widget.DeviceDefault.Button.ButtonBar.AlertDialog.Negative" />
+ <Button android:id="@+id/button3"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_gravity="center"
+ android:gravity="center"
+ android:layout_weight="1"
+ style="?android:attr/buttonBarButtonStyle"/>
+ <FrameLayout
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content">
+ <Button android:id="@+id/button1"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ android:layout_gravity="center"
+ android:gravity="center"
+ android:layout_weight="1"
+ style="@style/Widget.DeviceDefault.Button.ButtonBar.AlertDialog.Confirm"/>
+ <!-- This works as background. -->
+ <ImageView
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ android:src="@drawable/dialog_alert_button_positive"/>
+ </FrameLayout>
+ </LinearLayout>
+ </FrameLayout>
+ </LinearLayout>
+ </ScrollView>
+</com.android.internal.widget.WatchListDecorLayout>
diff --git a/core/res/res/values-watch-v36/colors.xml b/core/res/res/values-watch-v36/colors.xml
index 4bc2a66fa206..6cb9b853300d 100644
--- a/core/res/res/values-watch-v36/colors.xml
+++ b/core/res/res/values-watch-v36/colors.xml
@@ -15,4 +15,9 @@
-->
<!-- TODO(b/372524566): update color token's value to match material3 design. -->
<resources>
-</resources> \ No newline at end of file
+ <color name="system_primary_dark">#E9DDFF</color>
+ <color name="system_primary_fixed_dim">#D0BCFF</color>
+ <color name="system_on_primary_dark">#210F48</color>
+ <color name="system_primary_container_dark">#4D3D76</color>
+ <color name="system_on_primary_container_dark">#F6EDFF</color>
+</resources>
diff --git a/core/res/res/values-watch-v36/config.xml b/core/res/res/values-watch-v36/config.xml
index c8f347afb318..bb9da177e4fd 100644
--- a/core/res/res/values-watch-v36/config.xml
+++ b/core/res/res/values-watch-v36/config.xml
@@ -17,4 +17,5 @@
<resources>
<!-- Overrides system value -->
<dimen name="config_buttonCornerRadius">26dp</dimen>
+ <dimen name="config_bottomDialogCornerRadius">18dp</dimen>
</resources>
diff --git a/core/res/res/values-watch-v36/dimens_material.xml b/core/res/res/values-watch-v36/dimens_material.xml
index ad3c1a3ef3a1..ffa3b9c614db 100644
--- a/core/res/res/values-watch-v36/dimens_material.xml
+++ b/core/res/res/values-watch-v36/dimens_material.xml
@@ -22,6 +22,12 @@
<dimen name="btn_lineHeight">18sp</dimen>
<dimen name="btn_textSize">15sp</dimen>
+ <!-- values for material3 AlertDialog -->
+ <dimen name="dialog_btn_negative_width">60dp</dimen>
+ <dimen name="dialog_btn_negative_height">60dp</dimen>
+ <dimen name="dialog_btn_confirm_width">62dp</dimen>
+ <dimen name="dialog_btn_confirm_height">60dp</dimen>
+
<!-- Opacity factor for disabled material3 widget -->
<dimen name="disabled_alpha_device_default">0.12</dimen>
<dimen name="primary_content_alpha_device_default">0.38</dimen>
diff --git a/core/res/res/values-watch-v36/styles_material.xml b/core/res/res/values-watch-v36/styles_material.xml
index 32a22bb755cb..b2760e7c10a7 100644
--- a/core/res/res/values-watch-v36/styles_material.xml
+++ b/core/res/res/values-watch-v36/styles_material.xml
@@ -44,7 +44,7 @@
<!-- Text Styles -->
<!-- TextAppearance for Material Button - Filled -->
<style name="TextAppearance.Widget.Button.Material.Filled" parent="TextAppearance.Widget.Button.Material">
- <item name="textColor">@color/btn_material_filled_text_color</item>
+ <item name="textColor">@color/btn_material_filled_content_color</item>
</style>
<!-- TextAppearance for Material Button - Filled Tonal -->
@@ -52,7 +52,31 @@
<item name="android:fontFamily">font-family-flex-device-default</item>
<item name="android:fontVariationSettings">"'wdth' 90, 'wght' 500, 'ROND' 100, 'opsz' 15, 'GRAD' 0"</item>
<item name="textSize">@dimen/btn_textSize</item>
- <item name="textColor">@color/btn_material_filled_tonal_text_color</item>
+ <item name="textColor">@color/btn_material_filled_tonal_content_color</item>
<item name="lineHeight">@dimen/btn_lineHeight</item>
</style>
+
+ <!-- AlertDialog Styles -->
+ <style name="Widget.DeviceDefault.Button.ButtonBar.AlertDialog" parent="Widget.DeviceDefault.Button">
+ <item name="android:textSize">0sp</item>
+ <item name="android:gravity">center</item>
+ <item name="android:paddingStart">0dp</item>
+ <item name="android:paddingEnd">0dp</item>
+ <item name="android:drawablePadding">0dp</item>
+ </style>
+
+ <style name="Widget.DeviceDefault.Button.ButtonBar.AlertDialog.Confirm" parent="Widget.DeviceDefault.Button.ButtonBar.AlertDialog">
+ <!-- Use a ImageView as background -->
+ <item name="background">@android:color/transparent</item>
+ <item name="minWidth">@dimen/dialog_btn_confirm_width</item>
+ <item name="minHeight">@dimen/dialog_btn_confirm_height</item>
+ </style>
+
+ <style name="Widget.DeviceDefault.Button.ButtonBar.AlertDialog.Negative" parent="Widget.DeviceDefault.Button.ButtonBar.AlertDialog">
+ <item name="background">@drawable/dialog_alert_button_negative</item>
+ <item name="minWidth">@dimen/dialog_btn_negative_width</item>
+ <item name="minHeight">@dimen/dialog_btn_negative_height</item>
+ <item name="maxWidth">@dimen/dialog_btn_negative_width</item>
+ <item name="maxHeight">@dimen/dialog_btn_negative_height</item>
+ </style>
</resources> \ No newline at end of file
diff --git a/core/res/res/values/attrs.xml b/core/res/res/values/attrs.xml
index 02f9f3c5f0db..d750ff6623f2 100644
--- a/core/res/res/values/attrs.xml
+++ b/core/res/res/values/attrs.xml
@@ -5731,6 +5731,14 @@
</attr>
</declare-styleable>
+ <!-- @hide internal use only -->
+ <declare-styleable name="NotificationProgressBar">
+ <!-- Draws the tracker on a NotificationProgressBar. -->
+ <attr name="tracker" format="reference" />
+ <!-- Height of the tracker. -->
+ <attr name="trackerHeight" format="dimension" />
+ </declare-styleable>
+
<declare-styleable name="StackView">
<!-- Color of the res-out outline. -->
<attr name="resOutColor" format="color" />
diff --git a/core/res/res/values/styles_material.xml b/core/res/res/values/styles_material.xml
index cb8e4aa9b2a9..73681d26f297 100644
--- a/core/res/res/values/styles_material.xml
+++ b/core/res/res/values/styles_material.xml
@@ -504,6 +504,7 @@ please see styles_device_defaults.xml.
<style name="Widget.Material.Notification.NotificationProgressBar" parent="Widget.Material.Light.ProgressBar.Horizontal">
<item name="progressDrawable">@drawable/notification_progress</item>
+ <item name="trackerHeight">@dimen/notification_progress_tracker_height</item>
</style>
<style name="Widget.Material.Notification.Text" parent="Widget.Material.Light.TextView">
diff --git a/core/tests/BroadcastRadioTests/src/android/hardware/radio/RadioAlertUnitTest.java b/core/tests/BroadcastRadioTests/src/android/hardware/radio/RadioAlertUnitTest.java
new file mode 100644
index 000000000000..7afdde244073
--- /dev/null
+++ b/core/tests/BroadcastRadioTests/src/android/hardware/radio/RadioAlertUnitTest.java
@@ -0,0 +1,377 @@
+/*
+ * 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.
+ */
+
+package android.hardware.radio;
+
+import static org.junit.Assert.assertThrows;
+
+import android.os.Parcel;
+import android.platform.test.annotations.EnableFlags;
+
+import com.google.common.truth.Expect;
+
+import org.junit.Rule;
+import org.junit.Test;
+
+import java.util.ArrayList;
+import java.util.List;
+
+@EnableFlags(Flags.FLAG_HD_RADIO_EMERGENCY_ALERT_SYSTEM)
+public final class RadioAlertUnitTest {
+
+ private static final int TEST_FLAGS = 0;
+ private static final int CREATOR_ARRAY_SIZE = 3;
+ private static final String TEST_GEOCODE_VALUE_NAME = "SAME";
+ private static final String TEST_GEOCODE_VALUE_1 = "006109";
+ private static final String TEST_GEOCODE_VALUE_2 = "006009";
+ private static final double TEST_POLYGON_LATITUDE_START = -38.47;
+ private static final double TEST_POLYGON_LONGITUDE_START = -120.14;
+ private static final RadioAlert.Coordinate TEST_POLYGON_COORDINATE_START =
+ new RadioAlert.Coordinate(TEST_POLYGON_LATITUDE_START, TEST_POLYGON_LONGITUDE_START);
+ private static final List<RadioAlert.Coordinate> TEST_COORDINATES = List.of(
+ TEST_POLYGON_COORDINATE_START, new RadioAlert.Coordinate(38.34, -119.95),
+ new RadioAlert.Coordinate(38.52, -119.74), new RadioAlert.Coordinate(38.62, -119.89),
+ TEST_POLYGON_COORDINATE_START);
+ private static final RadioAlert.Polygon TEST_POLYGON = new RadioAlert.Polygon(TEST_COORDINATES);
+ private static final RadioAlert.Geocode TEST_GEOCODE_1 = new RadioAlert.Geocode(
+ TEST_GEOCODE_VALUE_NAME, TEST_GEOCODE_VALUE_1);
+ private static final RadioAlert.Geocode TEST_GEOCODE_2 = new RadioAlert.Geocode(
+ TEST_GEOCODE_VALUE_NAME, TEST_GEOCODE_VALUE_2);
+ private static final RadioAlert.AlertArea TEST_AREA_1 = new RadioAlert.AlertArea(
+ List.of(TEST_POLYGON), List.of(TEST_GEOCODE_1));
+ private static final RadioAlert.AlertArea TEST_AREA_2 = new RadioAlert.AlertArea(
+ new ArrayList<>(), List.of(TEST_GEOCODE_1, TEST_GEOCODE_2));
+
+ @Rule
+ public final Expect mExpect = Expect.create();
+
+ @Test
+ public void constructor_withNullValueName_forGeocode_fails() {
+ NullPointerException thrown = assertThrows(NullPointerException.class, () ->
+ new RadioAlert.Geocode(/* valueName= */ null, TEST_GEOCODE_VALUE_1));
+
+ mExpect.withMessage("Exception for geocode constructor with null value name")
+ .that(thrown).hasMessageThat()
+ .contains("Geocode value name can not be null");
+ }
+
+ @Test
+ public void constructor_withNullValue_forGeocode_fails() {
+ NullPointerException thrown = assertThrows(NullPointerException.class, () ->
+ new RadioAlert.Geocode(TEST_GEOCODE_VALUE_NAME, /* value= */ null));
+
+ mExpect.withMessage("Exception for geocode constructor with null value")
+ .that(thrown).hasMessageThat()
+ .contains("Geocode value can not be null");
+ }
+
+ @Test
+ public void getValueName_forGeocode() {
+ mExpect.withMessage("Value name of geocode").that(TEST_GEOCODE_1.getValueName())
+ .isEqualTo(TEST_GEOCODE_VALUE_NAME);
+ }
+
+ @Test
+ public void getValue_forGeocode() {
+ mExpect.withMessage("Value of geocode").that(TEST_GEOCODE_1.getValue())
+ .isEqualTo(TEST_GEOCODE_VALUE_1);
+ }
+
+ @Test
+ public void describeContents_forGeocode() {
+ mExpect.withMessage("Contents of geocode")
+ .that(TEST_GEOCODE_1.describeContents()).isEqualTo(0);
+ }
+
+ @Test
+ public void writeToParcel_forGeocode() {
+ Parcel parcel = Parcel.obtain();
+
+ TEST_GEOCODE_1.writeToParcel(parcel, TEST_FLAGS);
+
+ parcel.setDataPosition(0);
+ RadioAlert.Geocode geocodeFromParcel = RadioAlert.Geocode.CREATOR.createFromParcel(parcel);
+ mExpect.withMessage("Geocode from parcel").that(geocodeFromParcel)
+ .isEqualTo(TEST_GEOCODE_1);
+ }
+
+ @Test
+ public void newArray_forGeocodeCreator() {
+ RadioAlert.Geocode[] geocodes = RadioAlert.Geocode.CREATOR.newArray(CREATOR_ARRAY_SIZE);
+
+ mExpect.withMessage("Geocodes").that(geocodes).hasLength(CREATOR_ARRAY_SIZE);
+ }
+
+ @Test
+ public void hashCode_withSameGeocodes() {
+ RadioAlert.Geocode geocodeCompared = new RadioAlert.Geocode(TEST_GEOCODE_VALUE_NAME,
+ TEST_GEOCODE_VALUE_1);
+
+ mExpect.withMessage("Hash code of the same gecode")
+ .that(geocodeCompared.hashCode()).isEqualTo(TEST_GEOCODE_1.hashCode());
+ }
+
+ @Test
+ public void equals_withDifferentGeocodes() {
+ mExpect.withMessage("Different geocode").that(TEST_GEOCODE_1)
+ .isNotEqualTo(TEST_GEOCODE_2);
+ }
+
+ @Test
+ @SuppressWarnings("TruthIncompatibleType")
+ public void equals_withDifferentTypeObject_forGeocode() {
+ mExpect.withMessage("Non-geocode object").that(TEST_GEOCODE_1)
+ .isNotEqualTo(TEST_POLYGON_COORDINATE_START);
+ }
+
+ @Test
+ public void constructor_withInvalidLatitude_forCoordinate_fails() {
+ IllegalArgumentException thrown = assertThrows(IllegalArgumentException.class, () ->
+ new RadioAlert.Coordinate(/* latitude= */ -92.0, TEST_POLYGON_LONGITUDE_START));
+
+ mExpect.withMessage("Exception for coordinate constructor with invalid latitude")
+ .that(thrown).hasMessageThat().contains("Latitude");
+ }
+
+ @Test
+ public void constructor_withInvalidLongitude_forCoordinate_fails() {
+ IllegalArgumentException thrown = assertThrows(IllegalArgumentException.class, () ->
+ new RadioAlert.Coordinate(TEST_POLYGON_LATITUDE_START, /* longitude= */ 200.0));
+
+ mExpect.withMessage("Exception for coordinate constructor with invalid longitude")
+ .that(thrown).hasMessageThat().contains("Longitude");
+ }
+
+ @Test
+ public void getLatitude_forCoordinate() {
+ mExpect.withMessage("Latitude of coordinate")
+ .that(TEST_POLYGON_COORDINATE_START.getLatitude())
+ .isEqualTo(TEST_POLYGON_LATITUDE_START);
+ }
+
+ @Test
+ public void getLongitude_forCoordinate() {
+ mExpect.withMessage("Longitude of coordinate")
+ .that(TEST_POLYGON_COORDINATE_START.getLongitude())
+ .isEqualTo(TEST_POLYGON_LONGITUDE_START);
+ }
+
+ @Test
+ public void describeContents_forCoordinate() {
+ mExpect.withMessage("Contents of coordinate")
+ .that(TEST_POLYGON_COORDINATE_START.describeContents()).isEqualTo(0);
+ }
+
+ @Test
+ public void writeToParcel_forCoordinate() {
+ Parcel parcel = Parcel.obtain();
+
+ TEST_POLYGON_COORDINATE_START.writeToParcel(parcel, TEST_FLAGS);
+
+ parcel.setDataPosition(0);
+ RadioAlert.Coordinate coordinateFromParcel = RadioAlert.Coordinate.CREATOR
+ .createFromParcel(parcel);
+ mExpect.withMessage("Coordinate from parcel").that(coordinateFromParcel)
+ .isEqualTo(TEST_POLYGON_COORDINATE_START);
+ }
+
+ @Test
+ public void newArray_forCoordinateCreator() {
+ RadioAlert.Coordinate[] coordinates = RadioAlert.Coordinate.CREATOR
+ .newArray(CREATOR_ARRAY_SIZE);
+
+ mExpect.withMessage("Coordinates").that(coordinates).hasLength(CREATOR_ARRAY_SIZE);
+ }
+
+ @Test
+ public void hashCode_withSameCoordinates() {
+ RadioAlert.Coordinate coordinateCompared = new RadioAlert.Coordinate(
+ TEST_POLYGON_LATITUDE_START, TEST_POLYGON_LONGITUDE_START);
+
+ mExpect.withMessage("Hash code of the same coordinate")
+ .that(coordinateCompared.hashCode())
+ .isEqualTo(TEST_POLYGON_COORDINATE_START.hashCode());
+ }
+
+ @Test
+ public void equals_withDifferentCoordinates() {
+ mExpect.withMessage("Different coordinate").that(TEST_POLYGON_COORDINATE_START)
+ .isNotEqualTo(TEST_COORDINATES.get(1));
+ }
+
+ @Test
+ @SuppressWarnings("TruthIncompatibleType")
+ public void equals_withDifferentTypeObject_forCoordinate() {
+ mExpect.withMessage("Non-coordinate object").that(TEST_POLYGON_COORDINATE_START)
+ .isNotEqualTo(TEST_GEOCODE_1);
+ }
+
+ @Test
+ public void constructor_withNullCoordinates_forPolygon_fails() {
+ NullPointerException thrown = assertThrows(NullPointerException.class, () ->
+ new RadioAlert.Polygon(/* coordinates= */ null));
+
+ mExpect.withMessage("Exception for polygon constructor with null coordinates")
+ .that(thrown).hasMessageThat().contains("Coordinates can not be null");
+ }
+
+ @Test
+ public void constructor_withLessThanFourCoordinates_forPolygon_fails() {
+ IllegalArgumentException thrown = assertThrows(IllegalArgumentException.class, () ->
+ new RadioAlert.Polygon(List.of(TEST_POLYGON_COORDINATE_START,
+ TEST_POLYGON_COORDINATE_START)));
+
+ mExpect.withMessage("Exception for polygon constructor with less than four coordinates")
+ .that(thrown).hasMessageThat().contains("Number of coordinates must be at least 4");
+ }
+
+ @Test
+ public void constructor_withDifferentFirstAndLastCoordinates_forPolygon_fails() {
+ IllegalArgumentException thrown = assertThrows(IllegalArgumentException.class, () ->
+ new RadioAlert.Polygon(List.of(TEST_POLYGON_COORDINATE_START,
+ new RadioAlert.Coordinate(38.34, -119.95),
+ new RadioAlert.Coordinate(38.52, -119.74),
+ new RadioAlert.Coordinate(38.62, -119.89))));
+
+ mExpect.withMessage(
+ "Exception for polygon constructor with different first and last coordinates")
+ .that(thrown).hasMessageThat().contains(
+ "last and first coordinates must be the same");
+ }
+
+ @Test
+ public void getCoordinates_forPolygon() {
+ mExpect.withMessage("Coordinates in polygon").that(TEST_POLYGON.getCoordinates())
+ .containsExactlyElementsIn(TEST_COORDINATES);
+ }
+
+ @Test
+ public void describeContents_forPolygon() {
+ mExpect.withMessage("Contents of polygon")
+ .that(TEST_POLYGON.describeContents()).isEqualTo(0);
+ }
+
+ @Test
+ public void writeToParcel_forPolygon() {
+ Parcel parcel = Parcel.obtain();
+
+ TEST_POLYGON.writeToParcel(parcel, TEST_FLAGS);
+
+ parcel.setDataPosition(0);
+ RadioAlert.Polygon polygonFromParcel = RadioAlert.Polygon.CREATOR.createFromParcel(parcel);
+ mExpect.withMessage("Polygon from parcel").that(polygonFromParcel)
+ .isEqualTo(TEST_POLYGON);
+ }
+
+ @Test
+ public void newArray_forPolygonCreator() {
+ RadioAlert.Polygon[] polygons = RadioAlert.Polygon.CREATOR.newArray(CREATOR_ARRAY_SIZE);
+
+ mExpect.withMessage("Polygons").that(polygons).hasLength(CREATOR_ARRAY_SIZE);
+ }
+
+ @Test
+ public void hashCode_withSamePolygons() {
+ RadioAlert.Polygon polygonCompared = new RadioAlert.Polygon(TEST_COORDINATES);
+
+ mExpect.withMessage("Hash code of the same polygon")
+ .that(polygonCompared.hashCode()).isEqualTo(TEST_POLYGON.hashCode());
+ }
+
+ @Test
+ @SuppressWarnings("TruthIncompatibleType")
+ public void equals_withDifferentTypeObject_forPolygon() {
+ mExpect.withMessage("Non-polygon object").that(TEST_POLYGON)
+ .isNotEqualTo(TEST_GEOCODE_1);
+ }
+
+ @Test
+ public void constructor_withNullPolygons_forAlertArea_fails() {
+ NullPointerException thrown = assertThrows(NullPointerException.class, () ->
+ new RadioAlert.AlertArea(/* polygons= */ null, List.of(TEST_GEOCODE_1)));
+
+ mExpect.withMessage("Exception for alert area constructor with null polygon list")
+ .that(thrown).hasMessageThat().contains("Polygons can not be null");
+ }
+
+ @Test
+ public void constructor_withNullGeocodes_forAlertArea_fails() {
+ NullPointerException thrown = assertThrows(NullPointerException.class, () ->
+ new RadioAlert.AlertArea(List.of(TEST_POLYGON), /* geocodes= */ null));
+
+ mExpect.withMessage("Exception for alert area constructor with null geocode list")
+ .that(thrown).hasMessageThat().contains("Geocodes can not be null");
+ }
+
+ @Test
+ public void getPolygons_forAlertArea() {
+ mExpect.withMessage("Polygons in alert area").that(TEST_AREA_1.getPolygons())
+ .containsExactly(TEST_POLYGON);
+ }
+
+ @Test
+ public void getGeocodes_forAlertArea() {
+ mExpect.withMessage("Polygons in alert area").that(TEST_AREA_2.getGeocodes())
+ .containsExactly(TEST_GEOCODE_1, TEST_GEOCODE_2);
+ }
+
+ @Test
+ public void describeContents_forAlertArea() {
+ mExpect.withMessage("Contents of alert area")
+ .that(TEST_AREA_1.describeContents()).isEqualTo(0);
+ }
+
+ @Test
+ public void writeToParcel_forAlertArea() {
+ Parcel parcel = Parcel.obtain();
+
+ TEST_AREA_1.writeToParcel(parcel, TEST_FLAGS);
+
+ parcel.setDataPosition(0);
+ RadioAlert.AlertArea areaFromParcel = RadioAlert.AlertArea.CREATOR.createFromParcel(parcel);
+ mExpect.withMessage("Alert area from parcel").that(areaFromParcel)
+ .isEqualTo(TEST_AREA_1);
+ }
+
+ @Test
+ public void newArray_forAlertAreaCreator() {
+ RadioAlert.AlertArea[] alertAreas = RadioAlert.AlertArea.CREATOR
+ .newArray(CREATOR_ARRAY_SIZE);
+
+ mExpect.withMessage("Alert areas").that(alertAreas).hasLength(CREATOR_ARRAY_SIZE);
+ }
+
+ @Test
+ public void hashCode_withSameAlertAreas() {
+ RadioAlert.AlertArea alertAreaCompared = new RadioAlert.AlertArea(List.of(TEST_POLYGON),
+ List.of(TEST_GEOCODE_1));
+
+ mExpect.withMessage("Hash code of the same alert area")
+ .that(alertAreaCompared.hashCode()).isEqualTo(TEST_AREA_1.hashCode());
+ }
+
+ @Test
+ public void equals_withDifferentAlertAreas() {
+ mExpect.withMessage("Different alert area").that(TEST_AREA_1).isNotEqualTo(TEST_AREA_2);
+ }
+
+ @Test
+ @SuppressWarnings("TruthIncompatibleType")
+ public void equals_withDifferentTypeObject_forAlertArea() {
+ mExpect.withMessage("Non-alert-area object").that(TEST_AREA_1)
+ .isNotEqualTo(TEST_GEOCODE_1);
+ }
+}
diff --git a/core/tests/coretests/Android.bp b/core/tests/coretests/Android.bp
index 56e18e6c443f..aee1c3b2f28c 100644
--- a/core/tests/coretests/Android.bp
+++ b/core/tests/coretests/Android.bp
@@ -146,6 +146,10 @@ android_test {
":BinderProxyCountingTestService",
":AppThatUsesAppOps",
":AppThatCallsBinderMethods",
+ ":HelloWorldSdk1",
+ ":HelloWorldUsingSdk1AndSdk1",
+ ":HelloWorldUsingSdk1And2",
+ ":HelloWorldUsingSdkMalformedNegativeVersion",
],
}
diff --git a/core/tests/coretests/AndroidTest.xml b/core/tests/coretests/AndroidTest.xml
index 05ab783c01bb..3bc81724bc0a 100644
--- a/core/tests/coretests/AndroidTest.xml
+++ b/core/tests/coretests/AndroidTest.xml
@@ -29,6 +29,18 @@
<option name="test-file-name" value="AppThatCallsBinderMethods.apk" />
</target_preparer>
+ <target_preparer class="com.android.tradefed.targetprep.PushFilePreparer">
+ <option name="cleanup" value="true"/>
+ <option name="push-file" key="HelloWorldUsingSdk1And2.apk"
+ value="/data/local/tmp/tests/coretests/pm/HelloWorldUsingSdk1And2.apk"/>
+ <option name="push-file" key="HelloWorldUsingSdk1AndSdk1.apk"
+ value="/data/local/tmp/tests/coretests/pm/HelloWorldUsingSdk1AndSdk1.apk"/>
+ <option name="push-file" key="HelloWorldUsingSdkMalformedNegativeVersion.apk"
+ value="/data/local/tmp/tests/coretests/pm/HelloWorldUsingSdkMalformedNegativeVersion.apk"/>
+ <option name="push-file" key="HelloWorldSdk1.apk"
+ value="/data/local/tmp/tests/coretests/pm/HelloWorldSdk1.apk"/>
+ </target_preparer>
+
<target_preparer class="com.android.tradefed.targetprep.RunCommandTargetPreparer">
<!-- TODO(b/254155965): Design a mechanism to finally remove this command. -->
<option name="run-command" value="settings put global device_config_sync_disabled 0" />
diff --git a/core/tests/coretests/src/android/app/PropertyInvalidatedCacheTests.java b/core/tests/coretests/src/android/app/PropertyInvalidatedCacheTests.java
index dcea5b299829..65153f55295a 100644
--- a/core/tests/coretests/src/android/app/PropertyInvalidatedCacheTests.java
+++ b/core/tests/coretests/src/android/app/PropertyInvalidatedCacheTests.java
@@ -16,13 +16,23 @@
package android.app;
+import static android.app.PropertyInvalidatedCache.NONCE_UNSET;
+import static android.app.PropertyInvalidatedCache.NonceStore.INVALID_NONCE_INDEX;
+import static com.android.internal.os.Flags.FLAG_APPLICATION_SHARED_MEMORY_ENABLED;
+
import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNotEquals;
import static org.junit.Assert.assertNotSame;
import static org.junit.Assert.assertSame;
import static org.junit.Assert.assertTrue;
import static org.junit.Assert.fail;
+import com.android.internal.os.ApplicationSharedMemory;
+
import android.platform.test.annotations.IgnoreUnderRavenwood;
+import android.platform.test.annotations.RequiresFlagsEnabled;
+import android.platform.test.flag.junit.CheckFlagsRule;
+import android.platform.test.flag.junit.DeviceFlagsValueProvider;
import android.platform.test.ravenwood.RavenwoodRule;
import androidx.test.filters.SmallTest;
@@ -47,6 +57,9 @@ public class PropertyInvalidatedCacheTests {
@Rule
public final RavenwoodRule mRavenwood = new RavenwoodRule();
+ public final CheckFlagsRule mCheckFlagsRule =
+ DeviceFlagsValueProvider.createCheckFlagsRule();
+
// Configuration for creating caches
private static final String MODULE = PropertyInvalidatedCache.MODULE_TEST;
private static final String API = "testApi";
@@ -423,4 +436,54 @@ public class PropertyInvalidatedCacheTests {
// Re-enable test mode (so that the cleanup for the test does not throw).
PropertyInvalidatedCache.setTestMode(true);
}
+
+ // Verify the behavior of shared memory nonce storage. This does not directly test the cache
+ // storing nonces in shared memory.
+ @RequiresFlagsEnabled(FLAG_APPLICATION_SHARED_MEMORY_ENABLED)
+ @Test
+ public void testSharedMemoryStorage() {
+ // Fetch a shared memory instance for testing.
+ ApplicationSharedMemory shmem = ApplicationSharedMemory.create();
+
+ // Create a server-side store and a client-side store. The server's store is mutable and
+ // the client's store is not mutable.
+ PropertyInvalidatedCache.NonceStore server =
+ new PropertyInvalidatedCache.NonceStore(shmem.getSystemNonceBlock(), true);
+ PropertyInvalidatedCache.NonceStore client =
+ new PropertyInvalidatedCache.NonceStore(shmem.getSystemNonceBlock(), false);
+
+ final String name1 = "name1";
+ assertEquals(server.getHandleForName(name1), INVALID_NONCE_INDEX);
+ assertEquals(client.getHandleForName(name1), INVALID_NONCE_INDEX);
+ final int index1 = server.storeName(name1);
+ assertNotEquals(index1, INVALID_NONCE_INDEX);
+ assertEquals(server.getHandleForName(name1), index1);
+ assertEquals(client.getHandleForName(name1), index1);
+ assertEquals(server.storeName(name1), index1);
+
+ assertEquals(server.getNonce(index1), NONCE_UNSET);
+ assertEquals(client.getNonce(index1), NONCE_UNSET);
+ final int value1 = 4;
+ server.setNonce(index1, value1);
+ assertEquals(server.getNonce(index1), value1);
+ assertEquals(client.getNonce(index1), value1);
+ final int value2 = 8;
+ server.setNonce(index1, value2);
+ assertEquals(server.getNonce(index1), value2);
+ assertEquals(client.getNonce(index1), value2);
+
+ final String name2 = "name2";
+ assertEquals(server.getHandleForName(name2), INVALID_NONCE_INDEX);
+ assertEquals(client.getHandleForName(name2), INVALID_NONCE_INDEX);
+ final int index2 = server.storeName(name2);
+ assertNotEquals(index2, INVALID_NONCE_INDEX);
+ assertEquals(server.getHandleForName(name2), index2);
+ assertEquals(client.getHandleForName(name2), index2);
+ assertEquals(server.storeName(name2), index2);
+
+ // The names are different, so the indices must be different.
+ assertNotEquals(index1, index2);
+
+ shmem.close();
+ }
}
diff --git a/core/tests/coretests/src/android/content/pm/parsing/ApkLiteParseUtilsTest.java b/core/tests/coretests/src/android/content/pm/parsing/ApkLiteParseUtilsTest.java
new file mode 100644
index 000000000000..f9b481ffc1ef
--- /dev/null
+++ b/core/tests/coretests/src/android/content/pm/parsing/ApkLiteParseUtilsTest.java
@@ -0,0 +1,301 @@
+/*
+ * 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.
+ */
+
+package android.content.pm.parsing;
+
+import static android.content.pm.PackageManager.GET_SIGNING_CERTIFICATES;
+import static android.content.pm.PackageManager.MATCH_STATIC_SHARED_AND_SDK_LIBRARIES;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import android.annotation.SuppressLint;
+import android.app.UiAutomation;
+import android.content.pm.ApplicationInfo;
+import android.content.pm.PackageInfo;
+import android.content.pm.PackageManager;
+import android.content.pm.Signature;
+import android.content.pm.SigningInfo;
+import android.content.pm.parsing.result.ParseResult;
+import android.content.pm.parsing.result.ParseTypeImpl;
+import android.os.FileUtils;
+import android.os.ParcelFileDescriptor;
+import android.platform.test.annotations.Presubmit;
+import android.util.ArraySet;
+import android.util.PackageUtils;
+
+import androidx.annotation.NonNull;
+import androidx.test.InstrumentationRegistry;
+
+import com.android.internal.pm.parsing.PackageParser2;
+import com.android.server.pm.pkg.AndroidPackage;
+
+import libcore.util.HexEncoding;
+
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.rules.TemporaryFolder;
+
+import java.io.ByteArrayOutputStream;
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
+import java.util.Arrays;
+import java.util.List;
+import java.util.Set;
+
+@Presubmit
+public class ApkLiteParseUtilsTest {
+
+ @Rule
+ public TemporaryFolder mTemporaryFolder = new TemporaryFolder();
+
+ private static final String PUSH_FILE_DIR = "/data/local/tmp/tests/coretests/pm/";
+ private static final String TEST_APP_USING_SDK1_AND_SDK2 = "HelloWorldUsingSdk1And2.apk";
+ private static final String TEST_APP_USING_SDK1_AND_SDK1 = "HelloWorldUsingSdk1AndSdk1.apk";
+ private static final String TEST_APP_USING_SDK_MALFORMED_VERSION =
+ "HelloWorldUsingSdkMalformedNegativeVersion.apk";
+ private static final String TEST_SDK1 = "HelloWorldSdk1.apk";
+ private static final String TEST_SDK1_PACKAGE = "com.test.sdk1_1";
+ private static final String TEST_SDK1_NAME = "com.test.sdk1";
+ private static final long TEST_SDK1_VERSION = 1;
+ private static final String TEST_SDK2_NAME = "com.test.sdk2";
+ private static final long TEST_SDK2_VERSION = 2;
+
+ private final PackageParser2 mPackageParser2 = new PackageParser2(
+ null, null, null, new FakePackageParser2Callback());
+
+ private File mTmpDir = null;
+
+ @Before
+ public void setUp() throws IOException {
+ mTmpDir = mTemporaryFolder.newFolder("DexMetadataHelperTest");
+ }
+
+ @After
+ public void tearDown() throws Exception {
+ setSystemProperty("debug.pm.uses_sdk_library_default_cert_digest", "invalid");
+ uninstallPackageSilently(TEST_SDK1_NAME);
+ }
+
+ @SuppressLint("CheckResult")
+ @Test
+ public void testParseApkLite_getUsesSdkLibrary() throws Exception {
+ File apkFile = copyApkToTmpDir(TEST_APP_USING_SDK1_AND_SDK2);
+ ParseResult<ApkLite> result = ApkLiteParseUtils
+ .parseApkLite(ParseTypeImpl.forDefaultParsing().reset(), apkFile, 0);
+ assertThat(result.isError()).isFalse();
+
+ ApkLite baseApk = result.getResult();
+ assertThat(baseApk.getUsesSdkLibraries()).containsExactly(TEST_SDK1_NAME, TEST_SDK2_NAME);
+ assertThat(baseApk.getUsesSdkLibrariesVersionsMajor()).asList().containsExactly(
+ TEST_SDK1_VERSION, TEST_SDK2_VERSION
+ );
+ for (String[] certDigests: baseApk.getUsesSdkLibrariesCertDigests()) {
+ assertThat(certDigests).asList().containsExactly("");
+ }
+ }
+
+ @SuppressLint("CheckResult")
+ @Test
+ public void testParseApkLite_getUsesSdkLibrary_overrideCertDigest() throws Exception {
+ installPackage(TEST_SDK1);
+ String certDigest = getPackageCertDigest(TEST_SDK1_PACKAGE);
+ overrideUsesSdkLibraryCertificateDigest(certDigest);
+
+ File apkFile = copyApkToTmpDir(TEST_APP_USING_SDK1_AND_SDK2);
+ ParseResult<ApkLite> result = ApkLiteParseUtils
+ .parseApkLite(ParseTypeImpl.forDefaultParsing().reset(), apkFile, 0);
+ ApkLite baseApk = result.getResult();
+
+ String[][] liteCerts = baseApk.getUsesSdkLibrariesCertDigests();
+ assertThat(liteCerts).isNotNull();
+ for (String[] certDigests: liteCerts) {
+ assertThat(certDigests).asList().containsExactly(certDigest);
+ }
+
+ // Same for package parser
+ AndroidPackage pkg = mPackageParser2.parsePackage(apkFile, 0, true).hideAsFinal();
+ String[][] pkgCerts = pkg.getUsesSdkLibrariesCertDigests();
+ assertThat(pkgCerts).isNotNull();
+ for (int i = 0; i < liteCerts.length; i++) {
+ assertThat(liteCerts[i]).isEqualTo(pkgCerts[i]);
+ }
+ }
+
+
+ @SuppressLint("CheckResult")
+ @Test
+ public void testParseApkLite_getUsesSdkLibrary_sameAsPackageParser() throws Exception {
+ File apkFile = copyApkToTmpDir(TEST_APP_USING_SDK1_AND_SDK2);
+ ParseResult<ApkLite> result = ApkLiteParseUtils
+ .parseApkLite(ParseTypeImpl.forDefaultParsing().reset(), apkFile, 0);
+ assertThat(result.isError()).isFalse();
+ ApkLite baseApk = result.getResult();
+
+ AndroidPackage pkg = mPackageParser2.parsePackage(apkFile, 0, true).hideAsFinal();
+ assertThat(baseApk.getUsesSdkLibraries())
+ .containsExactlyElementsIn(pkg.getUsesSdkLibraries());
+ List<Long> versionsBoxed = Arrays.stream(pkg.getUsesSdkLibrariesVersionsMajor()).boxed()
+ .toList();
+ assertThat(baseApk.getUsesSdkLibrariesVersionsMajor()).asList()
+ .containsExactlyElementsIn(versionsBoxed);
+
+ String[][] liteCerts = baseApk.getUsesSdkLibrariesCertDigests();
+ String[][] pkgCerts = pkg.getUsesSdkLibrariesCertDigests();
+ for (int i = 0; i < liteCerts.length; i++) {
+ assertThat(liteCerts[i]).isEqualTo(pkgCerts[i]);
+ }
+ }
+
+ @SuppressLint("CheckResult")
+ @Test
+ public void testParseApkLite_malformedUsesSdkLibrary_duplicate() throws Exception {
+ File apkFile = copyApkToTmpDir(TEST_APP_USING_SDK1_AND_SDK1);
+ ParseResult<ApkLite> result = ApkLiteParseUtils
+ .parseApkLite(ParseTypeImpl.forDefaultParsing().reset(), apkFile, 0);
+ assertThat(result.isError()).isTrue();
+ assertThat(result.getErrorMessage()).contains("Bad uses-sdk-library declaration");
+ assertThat(result.getErrorMessage()).contains(
+ "Depending on multiple versions of SDK library");
+ }
+
+ @SuppressLint("CheckResult")
+ @Test
+ public void testParseApkLite_malformedUsesSdkLibrary_missingVersion() throws Exception {
+ File apkFile = copyApkToTmpDir(TEST_APP_USING_SDK_MALFORMED_VERSION);
+ ParseResult<ApkLite> result = ApkLiteParseUtils
+ .parseApkLite(ParseTypeImpl.forDefaultParsing().reset(), apkFile, 0);
+ assertThat(result.isError()).isTrue();
+ assertThat(result.getErrorMessage()).contains("Bad uses-sdk-library declaration");
+ }
+
+ private String getPackageCertDigest(String packageName) throws Exception {
+ getUiAutomation().adoptShellPermissionIdentity();
+ try {
+ PackageInfo sdkPackageInfo = getPackageManager().getPackageInfo(packageName,
+ PackageManager.PackageInfoFlags.of(
+ GET_SIGNING_CERTIFICATES | MATCH_STATIC_SHARED_AND_SDK_LIBRARIES));
+ SigningInfo signingInfo = sdkPackageInfo.signingInfo;
+ Signature[] signatures =
+ signingInfo != null ? signingInfo.getSigningCertificateHistory() : null;
+ byte[] digest = PackageUtils.computeSha256DigestBytes(signatures[0].toByteArray());
+ return new String(HexEncoding.encode(digest));
+ } finally {
+ getUiAutomation().dropShellPermissionIdentity();
+ }
+ }
+
+ private static PackageManager getPackageManager() {
+ return InstrumentationRegistry.getContext().getPackageManager();
+ }
+
+ /**
+ * SDK package is signed by build system. In theory we could try to extract the signature,
+ * and patch the app manifest. This property allows us to override in runtime, which is much
+ * easier.
+ */
+ private void overrideUsesSdkLibraryCertificateDigest(String sdkCertDigest) throws Exception {
+ setSystemProperty("debug.pm.uses_sdk_library_default_cert_digest", sdkCertDigest);
+ }
+
+ private void setSystemProperty(String name, String value) throws Exception {
+ assertThat(executeShellCommand("setprop " + name + " " + value)).isEmpty();
+ }
+
+ private static String executeShellCommand(String command) throws IOException {
+ final ParcelFileDescriptor stdout = getUiAutomation().executeShellCommand(command);
+ try (InputStream inputStream = new ParcelFileDescriptor.AutoCloseInputStream(stdout)) {
+ return readFullStream(inputStream);
+ }
+ }
+
+ private static String readFullStream(InputStream inputStream) throws IOException {
+ ByteArrayOutputStream result = new ByteArrayOutputStream();
+ writeFullStream(inputStream, result, -1);
+ return result.toString("UTF-8");
+ }
+
+ private static void writeFullStream(InputStream inputStream, OutputStream outputStream,
+ long expected) throws IOException {
+ byte[] buffer = new byte[1024];
+ long total = 0;
+ int length;
+ while ((length = inputStream.read(buffer)) != -1) {
+ outputStream.write(buffer, 0, length);
+ total += length;
+ }
+ if (expected > 0) {
+ assertThat(expected).isEqualTo(total);
+ }
+ }
+
+ private static UiAutomation getUiAutomation() {
+ return InstrumentationRegistry.getInstrumentation().getUiAutomation();
+ }
+
+ private File copyApkToTmpDir(String apkFileName) throws Exception {
+ File outFile = new File(mTmpDir, apkFileName);
+ String apkFilePath = PUSH_FILE_DIR + apkFileName;
+ File apkFile = new File(apkFilePath);
+ assertThat(apkFile.exists()).isTrue();
+ try (InputStream is = new FileInputStream(apkFile)) {
+ FileUtils.copyToFileOrThrow(is, outFile);
+ }
+ return outFile;
+ }
+
+ static String createApkPath(String baseName) {
+ return PUSH_FILE_DIR + baseName;
+ }
+
+ /* Install for all the users */
+ private void installPackage(String baseName) throws IOException {
+ File file = new File(createApkPath(baseName));
+ assertThat(executeShellCommand("pm install -t -g " + file.getPath()))
+ .isEqualTo("Success\n");
+ }
+
+ private static String uninstallPackageSilently(String packageName) throws IOException {
+ return executeShellCommand("pm uninstall " + packageName);
+ }
+
+ static class FakePackageParser2Callback extends PackageParser2.Callback {
+
+ @Override
+ public boolean isChangeEnabled(long changeId, @NonNull ApplicationInfo appInfo) {
+ return true;
+ }
+
+ @Override
+ public boolean hasFeature(String feature) {
+ return true;
+ }
+
+ @Override
+ public @NonNull Set<String> getHiddenApiWhitelistedApps() {
+ return new ArraySet<>();
+ }
+
+ @Override
+ public @NonNull Set<String> getInstallConstraintsAllowlist() {
+ return new ArraySet<>();
+ }
+ }
+}
diff --git a/core/tests/coretests/src/android/content/pm/verify/VerificationSessionTest.java b/core/tests/coretests/src/android/content/pm/verify/VerificationSessionTest.java
index 90ae306952fe..f1e1df5ae3fb 100644
--- a/core/tests/coretests/src/android/content/pm/verify/VerificationSessionTest.java
+++ b/core/tests/coretests/src/android/content/pm/verify/VerificationSessionTest.java
@@ -142,10 +142,10 @@ public class VerificationSessionTest {
new VerificationStatus.Builder().setVerified(true).build();
mTestSession.reportVerificationComplete(status);
verify(mTestSessionInterface, times(1)).reportVerificationComplete(
- eq(TEST_ID), eq(status));
+ eq(TEST_ID), eq(status), eq(null));
mTestSession.reportVerificationComplete(status, response);
verify(mTestSessionInterface, times(1))
- .reportVerificationCompleteWithExtensionResponse(
+ .reportVerificationComplete(
eq(TEST_ID), eq(status), eq(response));
final int reason = VerificationSession.VERIFICATION_INCOMPLETE_UNKNOWN;
diff --git a/core/tests/coretests/src/android/view/accessibility/AccessibilityNodeInfoTest.java b/core/tests/coretests/src/android/view/accessibility/AccessibilityNodeInfoTest.java
index da202b63d0f1..930e03dfd6db 100644
--- a/core/tests/coretests/src/android/view/accessibility/AccessibilityNodeInfoTest.java
+++ b/core/tests/coretests/src/android/view/accessibility/AccessibilityNodeInfoTest.java
@@ -46,7 +46,7 @@ public class AccessibilityNodeInfoTest {
// The number of fields tested in the corresponding CTS AccessibilityNodeInfoTest:
// See fullyPopulateAccessibilityNodeInfo, assertEqualsAccessibilityNodeInfo,
// and assertAccessibilityNodeInfoCleared in that class.
- private static final int NUM_MARSHALLED_PROPERTIES = 45;
+ private static final int NUM_MARSHALLED_PROPERTIES = 46;
/**
* The number of properties that are purposely not marshalled
diff --git a/core/tests/coretests/src/com/android/internal/jank/FrameTrackerTest.java b/core/tests/coretests/src/com/android/internal/jank/FrameTrackerTest.java
index 1cbc7d6d3f98..60b5a422ea80 100644
--- a/core/tests/coretests/src/com/android/internal/jank/FrameTrackerTest.java
+++ b/core/tests/coretests/src/com/android/internal/jank/FrameTrackerTest.java
@@ -16,9 +16,9 @@
package com.android.internal.jank;
-import static android.view.SurfaceControl.JankData.JANK_APP_DEADLINE_MISSED;
+import static android.view.SurfaceControl.JankData.JANK_APPLICATION;
+import static android.view.SurfaceControl.JankData.JANK_COMPOSER;
import static android.view.SurfaceControl.JankData.JANK_NONE;
-import static android.view.SurfaceControl.JankData.JANK_SURFACEFLINGER_DEADLINE_MISSED;
import static com.android.internal.jank.FrameTracker.SurfaceControlWrapper;
import static com.android.internal.jank.FrameTracker.ViewRootWrapper;
@@ -164,7 +164,7 @@ public class FrameTrackerTest {
verify(mRenderer, only()).addObserver(any());
// send first frame with a long duration - should not be taken into account
- sendFirstWindowFrame(tracker, 100, JANK_APP_DEADLINE_MISSED, 100L);
+ sendFirstWindowFrame(tracker, 100, JANK_APPLICATION, 100L);
// send another frame with a short duration - should not be considered janky
sendFrame(tracker, 5, JANK_NONE, 101L);
@@ -173,7 +173,7 @@ public class FrameTrackerTest {
when(mChoreographer.getVsyncId()).thenReturn(102L);
tracker.end(FrameTracker.REASON_END_NORMAL);
sendFrame(tracker, 5, JANK_NONE, 102L);
- sendFrame(tracker, 500, JANK_APP_DEADLINE_MISSED, 103L);
+ sendFrame(tracker, 500, JANK_APPLICATION, 103L);
verify(tracker).removeObservers();
verify(mTrackerListener, never()).triggerPerfetto(any());
@@ -202,7 +202,7 @@ public class FrameTrackerTest {
sendFrame(tracker, 4, JANK_NONE, 100L);
// send another frame - should be considered janky
- sendFrame(tracker, 40, JANK_SURFACEFLINGER_DEADLINE_MISSED, 101L);
+ sendFrame(tracker, 40, JANK_COMPOSER, 101L);
// end the trace session
when(mChoreographer.getVsyncId()).thenReturn(102L);
@@ -236,7 +236,7 @@ public class FrameTrackerTest {
verify(mRenderer, only()).addObserver(any());
// send first frame - janky
- sendFrame(tracker, 40, JANK_APP_DEADLINE_MISSED, 100L);
+ sendFrame(tracker, 40, JANK_APPLICATION, 100L);
// send another frame - not jank
sendFrame(tracker, 4, JANK_NONE, 101L);
@@ -275,7 +275,7 @@ public class FrameTrackerTest {
sendFrame(tracker, 4, JANK_NONE, 100L);
// send another frame - should be considered janky
- sendFrame(tracker, 40, JANK_APP_DEADLINE_MISSED, 101L);
+ sendFrame(tracker, 40, JANK_APPLICATION, 101L);
// end the trace session
when(mChoreographer.getVsyncId()).thenReturn(102L);
@@ -317,7 +317,7 @@ public class FrameTrackerTest {
// end the trace session, simulate one more valid callback came after the end call.
when(mChoreographer.getVsyncId()).thenReturn(102L);
tracker.end(FrameTracker.REASON_END_NORMAL);
- sendFrame(tracker, 50, JANK_APP_DEADLINE_MISSED, 102L);
+ sendFrame(tracker, 50, JANK_APPLICATION, 102L);
// One more callback with VSYNC after the end() vsync id.
sendFrame(tracker, 4, JANK_NONE, 103L);
@@ -365,7 +365,7 @@ public class FrameTrackerTest {
sendSfFrame(tracker, 4, 102L, JANK_NONE);
// Send janky but complete callbck fo 103L
- sendFrame(tracker, 50, JANK_APP_DEADLINE_MISSED, 103L);
+ sendFrame(tracker, 50, JANK_APPLICATION, 103L);
verify(tracker).removeObservers();
verify(mTrackerListener, never()).triggerPerfetto(any());
@@ -397,7 +397,7 @@ public class FrameTrackerTest {
sendFrame(tracker, 4, JANK_NONE, 101L);
// a janky frame
- sendFrame(tracker, 50, JANK_APP_DEADLINE_MISSED, 102L);
+ sendFrame(tracker, 50, JANK_APPLICATION, 102L);
tracker.cancel(FrameTracker.REASON_CANCEL_NORMAL);
verify(tracker).removeObservers();
@@ -481,7 +481,7 @@ public class FrameTrackerTest {
// normal frame - not janky
sendFrame(tracker, JANK_NONE, 101L);
// a janky frame
- sendFrame(tracker, JANK_APP_DEADLINE_MISSED, 102L);
+ sendFrame(tracker, JANK_APPLICATION, 102L);
when(mChoreographer.getVsyncId()).thenReturn(102L);
tracker.end(FrameTracker.REASON_CANCEL_NORMAL);
@@ -514,7 +514,7 @@ public class FrameTrackerTest {
verify(mSurfaceControlWrapper).addJankStatsListener(any(), any());
// First frame - janky
- sendFrame(tracker, JANK_APP_DEADLINE_MISSED, 100L);
+ sendFrame(tracker, JANK_APPLICATION, 100L);
// normal frame - not janky
sendFrame(tracker, JANK_NONE, 101L);
// normal frame - not janky
@@ -561,7 +561,7 @@ public class FrameTrackerTest {
tracker.end(FrameTracker.REASON_CANCEL_NORMAL);
// janky frame, should be ignored, trigger finish
- sendFrame(tracker, JANK_APP_DEADLINE_MISSED, 103L);
+ sendFrame(tracker, JANK_APPLICATION, 103L);
verify(mJankStatsRegistration).removeAfter(anyLong());
verify(mTrackerListener, never()).triggerPerfetto(any());
@@ -623,16 +623,16 @@ public class FrameTrackerTest {
tracker.begin();
mRunnableArgumentCaptor.getValue().run();
verify(mSurfaceControlWrapper).addJankStatsListener(any(), any());
- sendFrame(tracker, JANK_SURFACEFLINGER_DEADLINE_MISSED, 100L);
- sendFrame(tracker, JANK_SURFACEFLINGER_DEADLINE_MISSED, 101L);
- sendFrame(tracker, JANK_APP_DEADLINE_MISSED, 102L);
+ sendFrame(tracker, JANK_COMPOSER, 100L);
+ sendFrame(tracker, JANK_COMPOSER, 101L);
+ sendFrame(tracker, JANK_APPLICATION, 102L);
sendFrame(tracker, JANK_NONE, 103L);
- sendFrame(tracker, JANK_APP_DEADLINE_MISSED, 104L);
- sendFrame(tracker, JANK_APP_DEADLINE_MISSED, 105L);
+ sendFrame(tracker, JANK_APPLICATION, 104L);
+ sendFrame(tracker, JANK_APPLICATION, 105L);
when(mChoreographer.getVsyncId()).thenReturn(106L);
tracker.end(FrameTracker.REASON_END_NORMAL);
- sendFrame(tracker, JANK_SURFACEFLINGER_DEADLINE_MISSED, 106L);
- sendFrame(tracker, JANK_SURFACEFLINGER_DEADLINE_MISSED, 107L);
+ sendFrame(tracker, JANK_COMPOSER, 106L);
+ sendFrame(tracker, JANK_COMPOSER, 107L);
verify(mJankStatsRegistration).removeAfter(anyLong());
verify(mTrackerListener).triggerPerfetto(any());
verify(mStatsLog).write(eq(UI_INTERACTION_FRAME_INFO_REPORTED),
diff --git a/core/tests/coretests/src/com/android/internal/widget/NotificationProgressBarTest.java b/core/tests/coretests/src/com/android/internal/widget/NotificationProgressBarTest.java
index 79a478a7676b..5613caf4427e 100644
--- a/core/tests/coretests/src/com/android/internal/widget/NotificationProgressBarTest.java
+++ b/core/tests/coretests/src/com/android/internal/widget/NotificationProgressBarTest.java
@@ -41,9 +41,26 @@ public class NotificationProgressBarTest {
List<ProgressStyle.Segment> segments = new ArrayList<>();
List<ProgressStyle.Point> points = new ArrayList<>();
int progress = 50;
+ int progressMax = 100;
boolean isStyledByProgress = true;
NotificationProgressBar.processAndConvertToDrawableParts(segments, points, progress,
+ progressMax,
+ isStyledByProgress);
+ }
+
+ @Test(expected = IllegalArgumentException.class)
+ public void processAndConvertToDrawableParts_segmentsLengthNotMatchingProgressMax() {
+ List<ProgressStyle.Segment> segments = new ArrayList<>();
+ segments.add(new ProgressStyle.Segment(50));
+ segments.add(new ProgressStyle.Segment(100));
+ List<ProgressStyle.Point> points = new ArrayList<>();
+ int progress = 50;
+ int progressMax = 100;
+ boolean isStyledByProgress = true;
+
+ NotificationProgressBar.processAndConvertToDrawableParts(segments, points, progress,
+ progressMax,
isStyledByProgress);
}
@@ -51,11 +68,14 @@ public class NotificationProgressBarTest {
public void processAndConvertToDrawableParts_segmentLengthIsNegative() {
List<ProgressStyle.Segment> segments = new ArrayList<>();
segments.add(new ProgressStyle.Segment(-50));
+ segments.add(new ProgressStyle.Segment(150));
List<ProgressStyle.Point> points = new ArrayList<>();
int progress = 50;
+ int progressMax = 100;
boolean isStyledByProgress = true;
NotificationProgressBar.processAndConvertToDrawableParts(segments, points, progress,
+ progressMax,
isStyledByProgress);
}
@@ -63,11 +83,14 @@ public class NotificationProgressBarTest {
public void processAndConvertToDrawableParts_segmentLengthIsZero() {
List<ProgressStyle.Segment> segments = new ArrayList<>();
segments.add(new ProgressStyle.Segment(0));
+ segments.add(new ProgressStyle.Segment(100));
List<ProgressStyle.Point> points = new ArrayList<>();
int progress = 50;
+ int progressMax = 100;
boolean isStyledByProgress = true;
NotificationProgressBar.processAndConvertToDrawableParts(segments, points, progress,
+ progressMax,
isStyledByProgress);
}
@@ -77,9 +100,11 @@ public class NotificationProgressBarTest {
segments.add(new ProgressStyle.Segment(100));
List<ProgressStyle.Point> points = new ArrayList<>();
int progress = -50;
+ int progressMax = 100;
boolean isStyledByProgress = true;
NotificationProgressBar.processAndConvertToDrawableParts(segments, points, progress,
+ progressMax,
isStyledByProgress);
}
@@ -89,10 +114,11 @@ public class NotificationProgressBarTest {
segments.add(new ProgressStyle.Segment(100).setColor(Color.RED));
List<ProgressStyle.Point> points = new ArrayList<>();
int progress = 0;
+ int progressMax = 100;
boolean isStyledByProgress = true;
List<Part> parts = NotificationProgressBar.processAndConvertToDrawableParts(
- segments, points, progress, isStyledByProgress);
+ segments, points, progress, progressMax, isStyledByProgress);
int fadedRed = 0x7FFF0000;
List<Part> expected = new ArrayList<>(List.of(new Segment(1f, fadedRed, true)));
@@ -106,10 +132,11 @@ public class NotificationProgressBarTest {
segments.add(new ProgressStyle.Segment(100).setColor(Color.RED));
List<ProgressStyle.Point> points = new ArrayList<>();
int progress = 100;
+ int progressMax = 100;
boolean isStyledByProgress = true;
List<Part> parts = NotificationProgressBar.processAndConvertToDrawableParts(
- segments, points, progress, isStyledByProgress);
+ segments, points, progress, progressMax, isStyledByProgress);
List<Part> expected = new ArrayList<>(List.of(new Segment(1f, Color.RED)));
@@ -122,10 +149,11 @@ public class NotificationProgressBarTest {
segments.add(new ProgressStyle.Segment(100));
List<ProgressStyle.Point> points = new ArrayList<>();
int progress = 150;
+ int progressMax = 100;
boolean isStyledByProgress = true;
NotificationProgressBar.processAndConvertToDrawableParts(segments, points, progress,
- isStyledByProgress);
+ progressMax, isStyledByProgress);
}
@Test(expected = IllegalArgumentException.class)
@@ -135,35 +163,11 @@ public class NotificationProgressBarTest {
List<ProgressStyle.Point> points = new ArrayList<>();
points.add(new ProgressStyle.Point(-50).setColor(Color.RED));
int progress = 50;
+ int progressMax = 100;
boolean isStyledByProgress = true;
NotificationProgressBar.processAndConvertToDrawableParts(segments, points, progress,
- isStyledByProgress);
- }
-
- @Test(expected = IllegalArgumentException.class)
- public void processAndConvertToDrawableParts_pointPositionIsZero() {
- List<ProgressStyle.Segment> segments = new ArrayList<>();
- segments.add(new ProgressStyle.Segment(100));
- List<ProgressStyle.Point> points = new ArrayList<>();
- points.add(new ProgressStyle.Point(0).setColor(Color.RED));
- int progress = 50;
- boolean isStyledByProgress = true;
-
- NotificationProgressBar.processAndConvertToDrawableParts(segments, points, progress,
- isStyledByProgress);
- }
-
- @Test(expected = IllegalArgumentException.class)
- public void processAndConvertToDrawableParts_pointPositionAtMax() {
- List<ProgressStyle.Segment> segments = new ArrayList<>();
- segments.add(new ProgressStyle.Segment(100));
- List<ProgressStyle.Point> points = new ArrayList<>();
- points.add(new ProgressStyle.Point(100).setColor(Color.RED));
- int progress = 50;
- boolean isStyledByProgress = true;
-
- NotificationProgressBar.processAndConvertToDrawableParts(segments, points, progress,
+ progressMax,
isStyledByProgress);
}
@@ -174,9 +178,11 @@ public class NotificationProgressBarTest {
List<ProgressStyle.Point> points = new ArrayList<>();
points.add(new ProgressStyle.Point(150).setColor(Color.RED));
int progress = 50;
+ int progressMax = 100;
boolean isStyledByProgress = true;
NotificationProgressBar.processAndConvertToDrawableParts(segments, points, progress,
+ progressMax,
isStyledByProgress);
}
@@ -187,10 +193,11 @@ public class NotificationProgressBarTest {
segments.add(new ProgressStyle.Segment(50).setColor(Color.GREEN));
List<ProgressStyle.Point> points = new ArrayList<>();
int progress = 60;
+ int progressMax = 100;
boolean isStyledByProgress = true;
List<Part> parts = NotificationProgressBar.processAndConvertToDrawableParts(
- segments, points, progress, isStyledByProgress);
+ segments, points, progress, progressMax, isStyledByProgress);
// Colors with 50% opacity
int fadedGreen = 0x7F00FF00;
@@ -213,6 +220,7 @@ public class NotificationProgressBarTest {
points.add(new ProgressStyle.Point(60).setColor(Color.BLUE));
points.add(new ProgressStyle.Point(75).setColor(Color.YELLOW));
int progress = 60;
+ int progressMax = 100;
boolean isStyledByProgress = true;
// Colors with 50% opacity
@@ -231,7 +239,7 @@ public class NotificationProgressBarTest {
new Segment(0.25f, fadedBlue, true)));
List<Part> parts = NotificationProgressBar.processAndConvertToDrawableParts(
- segments, points, progress, isStyledByProgress);
+ segments, points, progress, progressMax, isStyledByProgress);
assertThat(parts).isEqualTo(expected);
}
@@ -247,10 +255,11 @@ public class NotificationProgressBarTest {
points.add(new ProgressStyle.Point(60).setColor(Color.BLUE));
points.add(new ProgressStyle.Point(75).setColor(Color.YELLOW));
int progress = 60;
+ int progressMax = 100;
boolean isStyledByProgress = true;
List<Part> parts = NotificationProgressBar.processAndConvertToDrawableParts(
- segments, points, progress, isStyledByProgress);
+ segments, points, progress, progressMax, isStyledByProgress);
// Colors with 50% opacity
int fadedGreen = 0x7F00FF00;
@@ -281,10 +290,11 @@ public class NotificationProgressBarTest {
points.add(new ProgressStyle.Point(25).setColor(Color.BLUE));
points.add(new ProgressStyle.Point(75).setColor(Color.YELLOW));
int progress = 60;
+ int progressMax = 100;
boolean isStyledByProgress = false;
List<Part> parts = NotificationProgressBar.processAndConvertToDrawableParts(
- segments, points, progress, isStyledByProgress);
+ segments, points, progress, progressMax, isStyledByProgress);
List<Part> expected = new ArrayList<>(List.of(
new Segment(0.15f, Color.RED),
diff --git a/libs/WindowManager/Shell/Android.bp b/libs/WindowManager/Shell/Android.bp
index f36dff06db98..42188dec4236 100644
--- a/libs/WindowManager/Shell/Android.bp
+++ b/libs/WindowManager/Shell/Android.bp
@@ -157,6 +157,13 @@ java_library {
}
filegroup {
+ name: "wm_shell-shared-utils",
+ srcs: [
+ "shared/src/com/android/wm/shell/shared/TransitionUtil.java",
+ ],
+}
+
+filegroup {
name: "wm_shell-shared-aidls",
srcs: [
diff --git a/libs/WindowManager/Shell/multivalentTests/Android.bp b/libs/WindowManager/Shell/multivalentTests/Android.bp
index ee0d5bbed324..41d1b5c15369 100644
--- a/libs/WindowManager/Shell/multivalentTests/Android.bp
+++ b/libs/WindowManager/Shell/multivalentTests/Android.bp
@@ -53,6 +53,8 @@ android_robolectric_test {
"mockito-robolectric-prebuilt",
"mockito-kotlin2",
"truth",
+ "flag-junit-base",
+ "flag-junit",
],
auto_gen_config: true,
}
diff --git a/libs/WindowManager/Shell/multivalentTests/src/com/android/wm/shell/bubbles/BubbleTaskViewTest.kt b/libs/WindowManager/Shell/multivalentTests/src/com/android/wm/shell/bubbles/BubbleTaskViewTest.kt
index 398fd554f030..4214e6ff9eaf 100644
--- a/libs/WindowManager/Shell/multivalentTests/src/com/android/wm/shell/bubbles/BubbleTaskViewTest.kt
+++ b/libs/WindowManager/Shell/multivalentTests/src/com/android/wm/shell/bubbles/BubbleTaskViewTest.kt
@@ -18,14 +18,19 @@ package com.android.wm.shell.bubbles
import android.content.ComponentName
import android.content.Context
+import android.platform.test.annotations.DisableFlags
+import android.platform.test.annotations.EnableFlags
+import android.platform.test.flag.junit.SetFlagsRule
import androidx.test.core.app.ApplicationProvider
import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.SmallTest
+import com.android.wm.shell.Flags
import com.android.wm.shell.taskview.TaskView
import com.google.common.truth.Truth.assertThat
import com.google.common.util.concurrent.MoreExecutors.directExecutor
import org.junit.Before
+import org.junit.Rule
import org.junit.Test
import org.junit.runner.RunWith
import org.mockito.kotlin.mock
@@ -36,6 +41,9 @@ import org.mockito.kotlin.verify
@RunWith(AndroidJUnit4::class)
class BubbleTaskViewTest {
+ @get:Rule
+ val setFlagsRule = SetFlagsRule()
+
private lateinit var bubbleTaskView: BubbleTaskView
private val context = ApplicationProvider.getApplicationContext<Context>()
private lateinit var taskView: TaskView
@@ -75,14 +83,33 @@ class BubbleTaskViewTest {
assertThat(actualComponentName).isEqualTo(componentName)
}
+ @DisableFlags(Flags.FLAG_ENABLE_TASK_VIEW_CONTROLLER_CLEANUP)
@Test
- fun cleanup_invalidTaskId_doesNotRemoveTask() {
+ fun cleanup_flagOff_invalidTaskId_doesNotRemoveTask() {
bubbleTaskView.cleanup()
verify(taskView, never()).removeTask()
}
+ @EnableFlags(Flags.FLAG_ENABLE_TASK_VIEW_CONTROLLER_CLEANUP)
+ @Test
+ fun cleanup_flagOn_invalidTaskId_removesTask() {
+ bubbleTaskView.cleanup()
+ verify(taskView).removeTask()
+ }
+
+ @DisableFlags(Flags.FLAG_ENABLE_TASK_VIEW_CONTROLLER_CLEANUP)
+ @Test
+ fun cleanup_flagOff_validTaskId_removesTask() {
+ val componentName = ComponentName(context, "TestClass")
+ bubbleTaskView.listener.onTaskCreated(123, componentName)
+
+ bubbleTaskView.cleanup()
+ verify(taskView).removeTask()
+ }
+
+ @EnableFlags(Flags.FLAG_ENABLE_TASK_VIEW_CONTROLLER_CLEANUP)
@Test
- fun cleanup_validTaskId_removesTask() {
+ fun cleanup_flagOn_validTaskId_removesTask() {
val componentName = ComponentName(context, "TestClass")
bubbleTaskView.listener.onTaskCreated(123, componentName)
diff --git a/libs/WindowManager/Shell/multivalentTests/src/com/android/wm/shell/bubbles/BubbleViewInfoTaskTest.kt b/libs/WindowManager/Shell/multivalentTests/src/com/android/wm/shell/bubbles/BubbleViewInfoTaskTest.kt
index 712cc7c475bc..776ea9226b06 100644
--- a/libs/WindowManager/Shell/multivalentTests/src/com/android/wm/shell/bubbles/BubbleViewInfoTaskTest.kt
+++ b/libs/WindowManager/Shell/multivalentTests/src/com/android/wm/shell/bubbles/BubbleViewInfoTaskTest.kt
@@ -25,6 +25,7 @@ import android.os.Handler
import android.os.UserManager
import android.view.IWindowManager
import android.view.WindowManager
+import android.widget.FrameLayout
import androidx.test.core.app.ApplicationProvider
import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.SmallTest
@@ -56,7 +57,9 @@ import org.junit.Assert
import org.junit.Before
import org.junit.Test
import org.junit.runner.RunWith
+import org.mockito.kotlin.any
import org.mockito.kotlin.mock
+import org.mockito.kotlin.whenever
/** Test inflating bubbles with [BubbleViewInfoTask]. */
@SmallTest
@@ -158,18 +161,23 @@ class BubbleViewInfoTaskTest {
mock<BubbleProperties>()
)
- val bubbleStackViewManager = BubbleStackViewManager.fromBubbleController(bubbleController)
- bubbleStackView =
- BubbleStackView(
- context,
- bubbleStackViewManager,
- bubblePositioner,
- bubbleData,
- surfaceSynchronizer,
- FloatingContentCoordinator(),
- bubbleController,
- mainExecutor
- )
+ // TODO: (b/371829099) - when optional overflow is no longer flagged we can enable this
+ // again, something about the overflow being added uncovers an issue with Robolectric and
+ // bitmaps; this is switched to a mock to work around that (b/375513387).
+// val bubbleStackViewManager = BubbleStackViewManager.fromBubbleController(bubbleController)
+// bubbleStackView = BubbleStackView(
+// context,
+// bubbleStackViewManager,
+// bubblePositioner,
+// bubbleData,
+// surfaceSynchronizer,
+// FloatingContentCoordinator(),
+// bubbleController,
+// mainExecutor
+// )
+ bubbleStackView = mock<BubbleStackView>()
+ whenever(bubbleStackView.generateLayoutParams(any()))
+ .thenReturn(FrameLayout.LayoutParams(1000, 1000))
expandedViewManager = BubbleExpandedViewManager.fromBubbleController(bubbleController)
}
diff --git a/libs/WindowManager/Shell/res/layout/desktop_mode_window_decor_maximize_menu.xml b/libs/WindowManager/Shell/res/layout/desktop_mode_window_decor_maximize_menu.xml
index 37596182f05b..5e41865cd31e 100644
--- a/libs/WindowManager/Shell/res/layout/desktop_mode_window_decor_maximize_menu.xml
+++ b/libs/WindowManager/Shell/res/layout/desktop_mode_window_decor_maximize_menu.xml
@@ -17,20 +17,53 @@
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:androidprv="http://schemas.android.com/apk/prv/res/android"
android:id="@+id/maximize_menu"
- android:layout_width="@dimen/desktop_mode_maximize_menu_width"
+ android:layout_width="wrap_content"
android:layout_height="@dimen/desktop_mode_maximize_menu_height"
android:background="@drawable/desktop_mode_maximize_menu_background"
android:elevation="1dp">
<LinearLayout
android:id="@+id/container"
- android:layout_width="@dimen/desktop_mode_maximize_menu_width"
+ android:layout_width="wrap_content"
android:layout_height="@dimen/desktop_mode_maximize_menu_height"
android:orientation="horizontal"
android:padding="16dp"
android:gravity="center">
<LinearLayout
+ android:id="@+id/maximize_menu_immersive_toggle_container"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:orientation="vertical">
+
+ <Button
+ android:layout_width="94dp"
+ android:layout_height="60dp"
+ android:id="@+id/maximize_menu_immersive_toggle_button"
+ style="?android:attr/buttonBarButtonStyle"
+ android:stateListAnimator="@null"
+ android:importantForAccessibility="yes"
+ android:contentDescription="@string/desktop_mode_maximize_menu_immersive_button_text"
+ android:layout_marginEnd="8dp"
+ android:layout_marginBottom="4dp"
+ android:alpha="0"/>
+
+ <TextView
+ android:id="@+id/maximize_menu_immersive_toggle_button_text"
+ android:layout_width="94dp"
+ android:layout_height="18dp"
+ android:textSize="11sp"
+ android:layout_marginBottom="76dp"
+ android:gravity="center"
+ android:fontFamily="google-sans-text"
+ android:importantForAccessibility="no"
+ android:text="@string/desktop_mode_maximize_menu_immersive_button_text"
+ android:textColor="?androidprv:attr/materialColorOnSurface"
+ android:alpha="0"/>
+ </LinearLayout>
+
+ <LinearLayout
+ android:id="@+id/maximize_menu_size_toggle_container"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:orientation="vertical">
@@ -43,7 +76,6 @@
android:stateListAnimator="@null"
android:importantForAccessibility="yes"
android:contentDescription="@string/desktop_mode_maximize_menu_maximize_button_text"
- android:layout_marginRight="8dp"
android:layout_marginBottom="4dp"
android:alpha="0"/>
@@ -62,6 +94,7 @@
</LinearLayout>
<LinearLayout
+ android:id="@+id/maximize_menu_snap_container"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:orientation="vertical">
@@ -73,6 +106,7 @@
android:padding="4dp"
android:background="@drawable/desktop_mode_maximize_menu_layout_background"
android:layout_marginBottom="4dp"
+ android:layout_marginStart="8dp"
android:alpha="0">
<Button
android:id="@+id/maximize_menu_snap_left_button"
@@ -115,7 +149,7 @@
used to monitor input events over the entire menu. -->
<View
android:id="@+id/maximize_menu_overlay"
- android:layout_width="@dimen/desktop_mode_maximize_menu_width"
+ android:layout_width="match_parent"
android:layout_height="@dimen/desktop_mode_maximize_menu_height"/>
</FrameLayout>
diff --git a/libs/WindowManager/Shell/res/values/dimen.xml b/libs/WindowManager/Shell/res/values/dimen.xml
index d02c77e831aa..6cd380d1eed9 100644
--- a/libs/WindowManager/Shell/res/values/dimen.xml
+++ b/libs/WindowManager/Shell/res/values/dimen.xml
@@ -479,8 +479,10 @@
<!-- The default minimum allowed window height when resizing a window in desktop mode. -->
<dimen name="desktop_mode_minimum_window_height">352dp</dimen>
- <!-- The width of the maximize menu in desktop mode. -->
- <dimen name="desktop_mode_maximize_menu_width">228dp</dimen>
+ <!-- The width of the maximize menu in desktop mode, depending on the number of options -->
+ <dimen name="desktop_mode_maximize_menu_width_one_options">126dp</dimen>
+ <dimen name="desktop_mode_maximize_menu_width_two_options">228dp</dimen>
+ <dimen name="desktop_mode_maximize_menu_width_three_options">330dp</dimen>
<!-- The height of the maximize menu in desktop mode. -->
<dimen name="desktop_mode_maximize_menu_height">114dp</dimen>
@@ -502,10 +504,14 @@
<dimen name="desktop_mode_maximize_menu_buttons_fill_radius">4dp</dimen>
<!-- The padding between the outline and fill of the maximize menu snap and maximize buttons. -->
<dimen name="desktop_mode_maximize_menu_snap_and_maximize_buttons_fill_padding">4dp</dimen>
+ <!-- The padding between the outline and fill of the maximize menu snap and maximize buttons. -->
+ <dimen name="desktop_mode_maximize_menu_snap_and_maximize_buttons_fill_padding_bottom">8dp</dimen>
<!-- The vertical padding between the outline and fill of the maximize menu restore button. -->
<dimen name="desktop_mode_maximize_menu_restore_button_fill_vertical_padding">13dp</dimen>
<!-- The horizontal padding between the outline and fill of the maximize menu restore button. -->
<dimen name="desktop_mode_maximize_menu_restore_button_fill_horizontal_padding">21dp</dimen>
+ <!-- The padding between the outline and fill of the maximize menu immersive button. -->
+ <dimen name="desktop_mode_maximize_menu_immersive_button_fill_padding">4dp</dimen>
<!-- The corner radius of the maximize menu. -->
<dimen name="desktop_mode_maximize_menu_corner_radius">8dp</dimen>
diff --git a/libs/WindowManager/Shell/res/values/strings.xml b/libs/WindowManager/Shell/res/values/strings.xml
index afac9f6433a3..ef0386a1b4dd 100644
--- a/libs/WindowManager/Shell/res/values/strings.xml
+++ b/libs/WindowManager/Shell/res/values/strings.xml
@@ -317,7 +317,10 @@
<string name="desktop_mode_maximize_menu_snap_text">Snap Screen</string>
<!-- Snap resizing non-resizable string. -->
<string name="desktop_mode_non_resizable_snap_text">App can\'t be moved here</string>
- <!-- Accessibility text for the Maximize Menu's maximize button [CHAR LIMIT=NONE] -->
+ <!-- Accessibility text for the Maximize Menu's immersive button [CHAR LIMIT=NONE] -->
+ <string name="desktop_mode_maximize_menu_immersive_button_text">Immersive</string>
+ <!-- Accessibility text for the Maximize Menu's immersive restore button [CHAR LIMIT=NONE] -->
+ <string name="desktop_mode_maximize_menu_immersive_restore_button_text">Restore</string>
<string name="desktop_mode_maximize_menu_maximize_button_text">Maximize</string>
<!-- Accessibility text for the Maximize Menu's restore button [CHAR LIMIT=NONE] -->
<string name="desktop_mode_maximize_menu_restore_button_text">Restore</string>
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/ShellTaskOrganizer.java b/libs/WindowManager/Shell/src/com/android/wm/shell/ShellTaskOrganizer.java
index 4607a8ec1210..f59fed906e2d 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/ShellTaskOrganizer.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/ShellTaskOrganizer.java
@@ -583,9 +583,8 @@ public class ShellTaskOrganizer extends TaskOrganizer {
}
final boolean windowModeChanged =
data.getTaskInfo().getWindowingMode() != taskInfo.getWindowingMode();
- final boolean visibilityChanged = data.getTaskInfo().isVisible != taskInfo.isVisible;
- if (windowModeChanged || (taskInfo.getWindowingMode() == WINDOWING_MODE_FREEFORM
- && visibilityChanged)) {
+ if (windowModeChanged
+ || hasFreeformConfigurationChanged(data.getTaskInfo(), taskInfo)) {
mRecentTasks.ifPresent(recentTasks ->
recentTasks.onTaskRunningInfoChanged(taskInfo));
}
@@ -606,6 +605,17 @@ public class ShellTaskOrganizer extends TaskOrganizer {
}
}
+ private boolean hasFreeformConfigurationChanged(RunningTaskInfo oldTaskInfo,
+ RunningTaskInfo newTaskInfo) {
+ if (newTaskInfo.getWindowingMode() != WINDOWING_MODE_FREEFORM) {
+ return false;
+ }
+ return oldTaskInfo.isVisible != newTaskInfo.isVisible
+ || !oldTaskInfo.positionInParent.equals(newTaskInfo.positionInParent)
+ || !Objects.equals(oldTaskInfo.configuration.windowConfiguration.getAppBounds(),
+ newTaskInfo.configuration.windowConfiguration.getAppBounds());
+ }
+
@Override
public void onBackPressedOnTaskRoot(RunningTaskInfo taskInfo) {
synchronized (mLock) {
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/back/BackAnimationController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/back/BackAnimationController.java
index 73bc42687bb9..5eb5d8962b55 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/back/BackAnimationController.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/back/BackAnimationController.java
@@ -95,6 +95,7 @@ import com.android.wm.shell.sysui.ShellInit;
import com.android.wm.shell.transition.Transitions;
import java.io.PrintWriter;
+import java.util.ArrayList;
import java.util.concurrent.atomic.AtomicBoolean;
import java.util.function.Predicate;
@@ -1277,42 +1278,42 @@ public class BackAnimationController implements RemoteCallable<BackAnimationCont
}
// Copy initial changes to final transition
final TransitionInfo init = mOpenTransitionInfo;
- // find prepare open target
+ // Find prepare open target
boolean openShowWallpaper = false;
- ComponentName openComponent = null;
+ final ArrayList<OpenChangeInfo> targets = new ArrayList<>();
int tmpSize;
- int openTaskId = INVALID_TASK_ID;
- WindowContainerToken openToken = null;
for (int j = init.getChanges().size() - 1; j >= 0; --j) {
final TransitionInfo.Change change = init.getChanges().get(j);
- if (change.hasFlags(FLAG_BACK_GESTURE_ANIMATED)) {
- openComponent = findComponentName(change);
- openTaskId = findTaskId(change);
- openToken = findToken(change);
+ if (change.hasFlags(FLAG_BACK_GESTURE_ANIMATED)
+ && TransitionUtil.isOpeningMode(change.getMode())) {
+ final ComponentName openComponent = findComponentName(change);
+ final int openTaskId = findTaskId(change);
+ final WindowContainerToken openToken = findToken(change);
+ if (openComponent == null && openTaskId == INVALID_TASK_ID
+ && openToken == null) {
+ continue;
+ }
+ targets.add(new OpenChangeInfo(openComponent, openTaskId, openToken));
if (change.hasFlags(FLAG_SHOW_WALLPAPER)) {
openShowWallpaper = true;
}
- break;
}
}
- if (openComponent == null && openTaskId == INVALID_TASK_ID && openToken == null) {
+ if (targets.isEmpty()) {
// This shouldn't happen, but if that happen, consume the initial transition anyway.
Log.e(TAG, "Unable to merge following transition, cannot find the gesture "
+ "animated target from the open transition=" + mOpenTransitionInfo);
mOpenTransitionInfo = null;
return;
}
- // find first non-prepare open target
+ // Find first non-prepare open target
boolean isOpen = false;
tmpSize = info.getChanges().size();
for (int j = 0; j < tmpSize; ++j) {
final TransitionInfo.Change change = info.getChanges().get(j);
- final ComponentName firstNonOpen = findComponentName(change);
- final int firstTaskId = findTaskId(change);
- if ((firstNonOpen != null && firstNonOpen != openComponent)
- || (firstTaskId != INVALID_TASK_ID && firstTaskId != openTaskId)) {
- // this is original close target, potential be close, but cannot determine from
- // it
+ if (isOpenChangeMatched(targets, change)) {
+ // This is original close target, potential be close, but cannot determine
+ // from it.
if (change.hasFlags(FLAG_BACK_GESTURE_ANIMATED)) {
isOpen = !TransitionUtil.isClosingMode(change.getMode());
} else {
@@ -1321,33 +1322,44 @@ public class BackAnimationController implements RemoteCallable<BackAnimationCont
}
}
}
-
if (!isOpen) {
// Close transition, the transition info should be:
// init info(open A & wallpaper)
// current info(close B target)
// remove init info(open/change A target & wallpaper)
boolean moveToTop = false;
+ boolean excludeOpenTarget = false;
+ boolean mergePredictive = false;
for (int j = info.getChanges().size() - 1; j >= 0; --j) {
final TransitionInfo.Change change = info.getChanges().get(j);
- if (isSameChangeTarget(openComponent, openTaskId, openToken, change)) {
+ if (isOpenChangeMatched(targets, change)) {
+ if (TransitionUtil.isClosingMode(change.getMode())) {
+ excludeOpenTarget = true;
+ }
moveToTop = change.hasFlags(FLAG_MOVED_TO_TOP);
info.getChanges().remove(j);
} else if ((openShowWallpaper && change.hasFlags(FLAG_IS_WALLPAPER))
|| !change.hasFlags(FLAG_BACK_GESTURE_ANIMATED)) {
info.getChanges().remove(j);
+ } else if (!mergePredictive && TransitionUtil.isClosingMode(change.getMode())) {
+ mergePredictive = true;
}
}
// Ignore merge if there is no close target
- if (!info.getChanges().isEmpty()) {
+ if (!info.getChanges().isEmpty() && mergePredictive) {
tmpSize = init.getChanges().size();
for (int i = 0; i < tmpSize; ++i) {
final TransitionInfo.Change change = init.getChanges().get(i);
if (change.hasFlags(FLAG_IS_WALLPAPER)) {
continue;
}
- if (moveToTop) {
- if (isSameChangeTarget(openComponent, openTaskId, openToken, change)) {
+ if (isOpenChangeMatched(targets, change)) {
+ if (excludeOpenTarget) {
+ // App has triggered another change during predictive back
+ // transition, filter out predictive back target.
+ continue;
+ }
+ if (moveToTop) {
change.setFlags(change.getFlags() | FLAG_MOVED_TO_TOP);
}
}
@@ -1376,7 +1388,7 @@ public class BackAnimationController implements RemoteCallable<BackAnimationCont
if (nonBackClose && nonBackOpen) {
for (int j = info.getChanges().size() - 1; j >= 0; --j) {
final TransitionInfo.Change change = info.getChanges().get(j);
- if (isSameChangeTarget(openComponent, openTaskId, openToken, change)) {
+ if (isOpenChangeMatched(targets, change)) {
info.getChanges().remove(j);
} else if ((openShowWallpaper && change.hasFlags(FLAG_IS_WALLPAPER))) {
info.getChanges().remove(j);
@@ -1659,9 +1671,21 @@ public class BackAnimationController implements RemoteCallable<BackAnimationCont
final ComponentName openChange = findComponentName(change);
final int firstTaskId = findTaskId(change);
final WindowContainerToken openToken = findToken(change);
- return (openChange != null && openChange == topActivity)
+ return (openChange != null && openChange.equals(topActivity))
|| (firstTaskId != INVALID_TASK_ID && firstTaskId == taskId)
- || (openToken != null && token == openToken);
+ || (openToken != null && openToken.equals(token));
+ }
+
+ static boolean isOpenChangeMatched(@NonNull ArrayList<OpenChangeInfo> targets,
+ TransitionInfo.Change change) {
+ for (int i = targets.size() - 1; i >= 0; --i) {
+ final OpenChangeInfo next = targets.get(i);
+ if (isSameChangeTarget(next.mOpenComponent, next.mOpenTaskId, next.mOpenToken,
+ change)) {
+ return true;
+ }
+ }
+ return false;
}
private static boolean canBeTransitionTarget(TransitionInfo.Change change) {
@@ -1721,4 +1745,16 @@ public class BackAnimationController implements RemoteCallable<BackAnimationCont
}
}
}
+
+ static class OpenChangeInfo {
+ final ComponentName mOpenComponent;
+ final int mOpenTaskId;
+ final WindowContainerToken mOpenToken;
+ OpenChangeInfo(ComponentName openComponent, int openTaskId,
+ WindowContainerToken openToken) {
+ mOpenComponent = openComponent;
+ mOpenTaskId = openTaskId;
+ mOpenToken = openToken;
+ }
+ }
}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleTaskView.kt b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleTaskView.kt
index 65f8e48eb822..68fc0c99abee 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleTaskView.kt
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleTaskView.kt
@@ -16,14 +16,11 @@
package com.android.wm.shell.bubbles
-import android.app.ActivityTaskManager
import android.app.ActivityTaskManager.INVALID_TASK_ID
import android.content.ComponentName
-import android.os.RemoteException
-import android.util.Log
import androidx.annotation.VisibleForTesting
+import com.android.wm.shell.Flags
import com.android.wm.shell.taskview.TaskView
-import com.android.wm.shell.transition.Transitions.ENABLE_SHELL_TRANSITIONS
import java.util.concurrent.Executor
/**
@@ -89,21 +86,8 @@ class BubbleTaskView(val taskView: TaskView, executor: Executor) {
* This should be called after all other cleanup animations have finished.
*/
fun cleanup() {
- if (taskId != INVALID_TASK_ID) {
- // Ensure the task is removed from WM
- if (ENABLE_SHELL_TRANSITIONS) {
- taskView.removeTask()
- } else {
- try {
- ActivityTaskManager.getService().removeTask(taskId)
- } catch (e: RemoteException) {
- Log.w(TAG, e.message ?: "")
- }
- }
+ if (Flags.enableTaskViewControllerCleanup() || taskId != INVALID_TASK_ID) {
+ taskView.removeTask()
}
}
-
- private companion object {
- const val TAG = "BubbleTaskView"
- }
}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/bar/BubbleEducationViewController.kt b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/bar/BubbleEducationViewController.kt
index 9fd255ded0ad..7adec39c9d66 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/bar/BubbleEducationViewController.kt
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/bar/BubbleEducationViewController.kt
@@ -20,6 +20,7 @@ import android.content.Context
import android.graphics.Point
import android.graphics.Rect
import android.util.Log
+import android.view.Gravity
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
@@ -102,14 +103,17 @@ class BubbleEducationViewController(private val context: Context, private val li
hideEducation(animated = false)
log { "showStackEducation at: $position" }
+ val rootBounds = Rect()
+ // Get root bounds on screen as position is in screen coordinates
+ root.getBoundsOnScreen(rootBounds)
educationView =
createEducationView(R.layout.bubble_bar_stack_education, root).apply {
setArrowDirection(BubblePopupDrawable.ArrowDirection.DOWN)
- setArrowPosition(BubblePopupDrawable.ArrowPosition.End)
- updateEducationPosition(view = this, position, root)
+ updateEducationPosition(view = this, position, rootBounds)
val arrowToEdgeOffset = popupDrawable?.config?.cornerRadius ?: 0f
doOnLayout {
- it.pivotX = it.width - arrowToEdgeOffset
+ it.pivotX = if (position.x < rootBounds.centerX())
+ arrowToEdgeOffset else it.width - arrowToEdgeOffset
it.pivotY = it.height.toFloat()
}
setOnClickListener { educationClickHandler() }
@@ -218,12 +222,9 @@ class BubbleEducationViewController(private val context: Context, private val li
*
* @param view the user education view to layout
* @param position the reference position in Screen coordinates
- * @param root the root view to use for the layout
+ * @param rootBounds bounds of the parent the education view is placed in
*/
- private fun updateEducationPosition(view: BubblePopupView, position: Point, root: ViewGroup) {
- val rootBounds = Rect()
- // Get root bounds on screen as position is in screen coordinates
- root.getBoundsOnScreen(rootBounds)
+ private fun updateEducationPosition(view: BubblePopupView, position: Point, rootBounds: Rect) {
// Get the offset to the arrow from the edge of the education view
val arrowToEdgeOffset =
view.popupDrawable?.config?.let { it.cornerRadius + it.arrowWidth / 2f }?.roundToInt()
@@ -231,7 +232,15 @@ class BubbleEducationViewController(private val context: Context, private val li
// Calculate education view margins
val params = view.layoutParams as FrameLayout.LayoutParams
params.bottomMargin = rootBounds.bottom - position.y
- params.rightMargin = rootBounds.right - position.x - arrowToEdgeOffset
+ if (position.x < rootBounds.centerX()) {
+ params.leftMargin = position.x - arrowToEdgeOffset
+ params.gravity = Gravity.LEFT or Gravity.BOTTOM
+ view.setArrowPosition(BubblePopupDrawable.ArrowPosition.Start)
+ } else {
+ params.rightMargin = rootBounds.right - position.x - arrowToEdgeOffset
+ params.gravity = Gravity.RIGHT or Gravity.BOTTOM
+ view.setArrowPosition(BubblePopupDrawable.ArrowPosition.End)
+ }
view.layoutParams = params
}
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 5998dc848e2b..6f7b7162d2ec 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
@@ -129,19 +129,24 @@ class DesktopRepository (
DesktopModeStatus.getMaxTaskLimit(context).takeIf { it > 0 }
?: desktop.zOrderedTasksCount
+ var visibleTasksCount = 0
desktop.zOrderedTasksList
// Reverse it so we initialize the repo from bottom to top.
.reversed()
- .mapNotNull { taskId ->
- desktop.tasksByTaskIdMap[taskId]?.takeIf {
- it.desktopTaskState == DesktopTaskState.VISIBLE
- }
- }
- .take(maxTasks)
+ .mapNotNull { taskId -> desktop.tasksByTaskIdMap[taskId] }
.forEach { task ->
- addOrMoveFreeformTaskToTop(desktop.displayId, task.taskId)
- addActiveTask(desktop.displayId, task.taskId)
- updateTaskVisibility(desktop.displayId, task.taskId, visible = false)
+ if (task.desktopTaskState == DesktopTaskState.VISIBLE
+ && visibleTasksCount < maxTasks
+ ) {
+ visibleTasksCount++
+ addOrMoveFreeformTaskToTop(desktop.displayId, task.taskId)
+ addActiveTask(desktop.displayId, task.taskId)
+ updateTaskVisibility(desktop.displayId, task.taskId, visible = false)
+ } else {
+ addActiveTask(desktop.displayId, task.taskId)
+ updateTaskVisibility(desktop.displayId, task.taskId, visible = false)
+ minimizeTask(desktop.displayId, task.taskId)
+ }
}
}
}
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 77fb4b4815f1..bc78e43a15ad 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
@@ -373,7 +373,7 @@ class DesktopTasksController(
}
logV("moveBackgroundTaskToDesktop with taskId=%d", taskId)
// TODO(342378842): Instead of using default display, support multiple displays
- val taskToMinimize = bringDesktopAppsToFrontBeforeShowingNewTask(
+ val taskIdToMinimize = bringDesktopAppsToFrontBeforeShowingNewTask(
DEFAULT_DISPLAY, wct, taskId)
val runOnTransit = desktopImmersiveController.exitImmersiveIfApplicable(
wct = wct,
@@ -388,7 +388,7 @@ class DesktopTasksController(
)
// TODO(343149901): Add DPI changes for task launch
val transition = enterDesktopTaskTransitionHandler.moveToDesktop(wct, transitionSource)
- addPendingMinimizeTransition(transition, taskToMinimize)
+ taskIdToMinimize?.let { addPendingMinimizeTransition(transition, it) }
runOnTransit?.invoke(transition)
return true
}
@@ -412,12 +412,12 @@ class DesktopTasksController(
excludeTaskId = task.taskId,
)
// Bring other apps to front first
- val taskToMinimize =
+ val taskIdToMinimize =
bringDesktopAppsToFrontBeforeShowingNewTask(task.displayId, wct, task.taskId)
addMoveToDesktopChanges(wct, task)
val transition = enterDesktopTaskTransitionHandler.moveToDesktop(wct, transitionSource)
- addPendingMinimizeTransition(transition, taskToMinimize)
+ taskIdToMinimize?.let { addPendingMinimizeTransition(transition, it) }
runOnTransit?.invoke(transition)
}
@@ -452,14 +452,14 @@ class DesktopTasksController(
val wct = WindowContainerTransaction()
exitSplitIfApplicable(wct, taskInfo)
moveHomeTask(wct, toTop = true)
- val taskToMinimize =
+ val taskIdToMinimize =
bringDesktopAppsToFrontBeforeShowingNewTask(taskInfo.displayId, wct, taskInfo.taskId)
addMoveToDesktopChanges(wct, taskInfo)
val runOnTransit = desktopImmersiveController.exitImmersiveIfApplicable(
wct, taskInfo.displayId)
val transition = dragToDesktopTransitionHandler.finishDragToDesktopTransition(wct)
transition?.let {
- addPendingMinimizeTransition(it, taskToMinimize)
+ taskIdToMinimize?.let { taskId -> addPendingMinimizeTransition(it, taskId) }
runOnTransit?.invoke(transition)
}
}
@@ -651,7 +651,13 @@ class DesktopTasksController(
excludeTaskId = taskInfo.taskId,
)
val transition =
- startLaunchTransition(TRANSIT_TO_FRONT, wct, taskInfo.taskId, remoteTransition)
+ startLaunchTransition(
+ TRANSIT_TO_FRONT,
+ wct,
+ taskInfo.taskId,
+ remoteTransition,
+ taskInfo.displayId
+ )
runOnTransit?.invoke(transition)
}
@@ -660,15 +666,15 @@ class DesktopTasksController(
wct: WindowContainerTransaction,
taskId: Int,
remoteTransition: RemoteTransition?,
+ displayId: Int = DEFAULT_DISPLAY,
): IBinder {
- val taskToMinimize: RunningTaskInfo? =
- addAndGetMinimizeChanges(DEFAULT_DISPLAY, wct, taskId)
+ val taskIdToMinimize = addAndGetMinimizeChanges(displayId, wct, taskId)
if (remoteTransition == null) {
val t = transitions.startTransition(transitionType, wct, null /* handler */)
- addPendingMinimizeTransition(t, taskToMinimize)
+ taskIdToMinimize?.let { addPendingMinimizeTransition(t, it) }
return t
}
- if (taskToMinimize == null) {
+ if (taskIdToMinimize == null) {
val remoteTransitionHandler = OneShotRemoteHandler(mainExecutor, remoteTransition)
val t = transitions.startTransition(transitionType, wct, remoteTransitionHandler)
remoteTransitionHandler.setTransition(t)
@@ -676,10 +682,10 @@ class DesktopTasksController(
}
val remoteTransitionHandler =
DesktopWindowLimitRemoteHandler(
- mainExecutor, rootTaskDisplayAreaOrganizer, remoteTransition, taskToMinimize.taskId)
+ mainExecutor, rootTaskDisplayAreaOrganizer, remoteTransition, taskIdToMinimize)
val t = transitions.startTransition(transitionType, wct, remoteTransitionHandler)
remoteTransitionHandler.setTransition(t)
- addPendingMinimizeTransition(t, taskToMinimize)
+ taskIdToMinimize?.let { addPendingMinimizeTransition(t, it) }
return t
}
@@ -1032,13 +1038,13 @@ class DesktopTasksController(
displayId: Int,
wct: WindowContainerTransaction,
newTaskIdInFront: Int
- ): RunningTaskInfo? = bringDesktopAppsToFront(displayId, wct, newTaskIdInFront)
+ ): Int? = bringDesktopAppsToFront(displayId, wct, newTaskIdInFront)
private fun bringDesktopAppsToFront(
displayId: Int,
wct: WindowContainerTransaction,
newTaskIdInFront: Int? = null
- ): RunningTaskInfo? {
+ ): Int? {
logV("bringDesktopAppsToFront, newTaskId=%d", newTaskIdInFront)
// Move home to front, ensures that we go back home when all desktop windows are closed
moveHomeTask(wct, toTop = true)
@@ -1054,11 +1060,10 @@ class DesktopTasksController(
taskRepository.getExpandedTasksOrdered(displayId)
// If we're adding a new Task we might need to minimize an old one
// TODO(b/365725441): Handle non running task minimization
- val taskToMinimize: RunningTaskInfo? =
+ val taskIdToMinimize: Int? =
if (newTaskIdInFront != null && desktopTasksLimiter.isPresent) {
- desktopTasksLimiter
- .get()
- .getTaskToMinimize(
+ desktopTasksLimiter.get()
+ .getTaskIdToMinimize(
expandedTasksOrderedFrontToBack,
newTaskIdInFront
)
@@ -1068,7 +1073,7 @@ class DesktopTasksController(
expandedTasksOrderedFrontToBack
// If there is a Task to minimize, let it stay behind the Home Task
- .filter { taskId -> taskId != taskToMinimize?.taskId }
+ .filter { taskId -> taskId != taskIdToMinimize }
.reversed() // Start from the back so the front task is brought forward last
.forEach { taskId ->
val runningTaskInfo = shellTaskOrganizer.getRunningTaskInfo(taskId)
@@ -1089,7 +1094,7 @@ class DesktopTasksController(
taskbarDesktopTaskListener?.
onTaskbarCornerRoundingUpdate(doesAnyTaskRequireTaskbarRounding(displayId))
- return taskToMinimize
+ return taskIdToMinimize
}
private fun moveHomeTask(wct: WindowContainerTransaction, toTop: Boolean) {
@@ -1341,7 +1346,7 @@ class DesktopTasksController(
val options = createNewWindowOptions(callingTask)
if (options.launchWindowingMode == WINDOWING_MODE_FREEFORM) {
wct.startTask(requestedTaskId, options.toBundle())
- val taskToMinimize = bringDesktopAppsToFrontBeforeShowingNewTask(
+ val taskIdToMinimize = bringDesktopAppsToFrontBeforeShowingNewTask(
callingTask.displayId, wct, requestedTaskId)
val runOnTransit = desktopImmersiveController.exitImmersiveIfApplicable(
wct = wct,
@@ -1349,7 +1354,7 @@ class DesktopTasksController(
excludeTaskId = requestedTaskId,
)
val transition = transitions.startTransition(TRANSIT_OPEN, wct, null)
- addPendingMinimizeTransition(transition, taskToMinimize)
+ taskIdToMinimize?.let { addPendingMinimizeTransition(transition, it) }
runOnTransit?.invoke(transition)
} else {
val splitPosition = splitScreenController.determineNewInstancePosition(callingTask)
@@ -1489,9 +1494,9 @@ class DesktopTasksController(
// 1) Exit immersive if needed.
desktopImmersiveController.exitImmersiveIfApplicable(transition, wct, task.displayId)
// 2) minimize a Task if needed.
- val taskToMinimize = addAndGetMinimizeChanges(task.displayId, wct, task.taskId)
- if (taskToMinimize != null) {
- addPendingMinimizeTransition(transition, taskToMinimize)
+ val taskIdToMinimize = addAndGetMinimizeChanges(task.displayId, wct, task.taskId)
+ if (taskIdToMinimize != null) {
+ addPendingMinimizeTransition(transition, taskIdToMinimize)
return wct
}
return if (wct.isEmpty) null else wct
@@ -1515,9 +1520,8 @@ class DesktopTasksController(
// Desktop Mode is already showing and we're launching a new Task - we might need to
// minimize another Task.
- val taskToMinimize =
- addAndGetMinimizeChanges(task.displayId, wct, task.taskId)
- addPendingMinimizeTransition(transition, taskToMinimize)
+ val taskIdToMinimize = addAndGetMinimizeChanges(task.displayId, wct, task.taskId)
+ taskIdToMinimize?.let { addPendingMinimizeTransition(transition, it) }
desktopImmersiveController.exitImmersiveIfApplicable(
transition, wct, task.displayId
)
@@ -1683,7 +1687,7 @@ class DesktopTasksController(
displayId: Int,
wct: WindowContainerTransaction,
newTaskId: Int
- ): RunningTaskInfo? {
+ ): Int? {
if (!desktopTasksLimiter.isPresent) return null
return desktopTasksLimiter
.get()
@@ -1692,9 +1696,9 @@ class DesktopTasksController(
private fun addPendingMinimizeTransition(
transition: IBinder,
- taskToMinimize: RunningTaskInfo?
+ taskIdToMinimize: Int,
) {
- if (taskToMinimize == null) return
+ val taskToMinimize = shellTaskOrganizer.getRunningTaskInfo(taskIdToMinimize) ?: return
desktopTasksLimiter.ifPresent {
it.addPendingMinimizeChange(transition, taskToMinimize.displayId, taskToMinimize.taskId)
}
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 cd28a4fa67ec..7bcc5d1691aa 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
@@ -16,7 +16,6 @@
package com.android.wm.shell.desktopmode
-import android.app.ActivityManager.RunningTaskInfo
import android.content.Context
import android.os.Handler
import android.os.IBinder
@@ -190,17 +189,19 @@ class DesktopTasksLimiter (
displayId: Int,
wct: WindowContainerTransaction,
newFrontTaskId: Int,
- ): RunningTaskInfo? {
+ ): Int? {
logV("addAndGetMinimizeTaskChanges, newFrontTask=%d", newFrontTaskId)
- // This list is ordered from front to back.
- val newTaskOrderedList = createOrderedTaskListWithNewTask(
- taskRepository.getExpandedTasksOrdered(displayId), newFrontTaskId)
- val taskToMinimize = getTaskToMinimize(newTaskOrderedList)
- if (taskToMinimize != null) {
- wct.reorder(taskToMinimize.token, false /* onTop */)
- return taskToMinimize
+
+ val taskIdToMinimize =
+ getTaskIdToMinimize(
+ taskRepository.getExpandedTasksOrdered(displayId),
+ newFrontTaskId
+ )
+ // If it's a running task, reorder it to back.
+ taskIdToMinimize?.let { shellTaskOrganizer.getRunningTaskInfo(it) }?.let {
+ wct.reorder(it.token, false /* onTop */)
}
- return null
+ return taskIdToMinimize
}
/**
@@ -216,31 +217,33 @@ class DesktopTasksLimiter (
* Returns the minimized task from the list of visible tasks ordered from front to back with
* the new task placed in front of other tasks.
*/
- fun getTaskToMinimize(
+ fun getTaskIdToMinimize(
visibleOrderedTasks: List<Int>,
- newTaskIdInFront: Int
- ): RunningTaskInfo? =
- getTaskToMinimize(createOrderedTaskListWithNewTask(visibleOrderedTasks, newTaskIdInFront))
+ newTaskIdInFront: Int? = null
+ ): Int? {
+ return getTaskIdToMinimize(
+ createOrderedTaskListWithGivenTaskInFront(
+ visibleOrderedTasks, newTaskIdInFront))
+ }
/** Returns the Task to minimize given a list of visible tasks ordered from front to back. */
- fun getTaskToMinimize(visibleOrderedTasks: List<Int>): RunningTaskInfo? {
+ private fun getTaskIdToMinimize(visibleOrderedTasks: List<Int>): Int? {
if (visibleOrderedTasks.size <= maxTasksLimit) {
logV("No need to minimize; tasks below limit")
+ // No need to minimize anything
return null
}
- val taskIdToMinimize = visibleOrderedTasks.last()
- val taskToMinimize =
- shellTaskOrganizer.getRunningTaskInfo(taskIdToMinimize)
- if (taskToMinimize == null) {
- logE("taskToMinimize(taskId = %d) == null", taskIdToMinimize)
- return null
- }
- return taskToMinimize
+ return visibleOrderedTasks.last()
}
- private fun createOrderedTaskListWithNewTask(
- orderedTaskIds: List<Int>, newTaskId: Int): List<Int> =
- listOf(newTaskId) + orderedTaskIds.filter { taskId -> taskId != newTaskId }
+ private fun createOrderedTaskListWithGivenTaskInFront(
+ existingTaskIdsOrderedFrontToBack: List<Int>,
+ newTaskId: Int?
+ ): List<Int> {
+ return if (newTaskId == null) existingTaskIdsOrderedFrontToBack
+ else listOf(newTaskId) +
+ existingTaskIdsOrderedFrontToBack.filter { taskId -> taskId != newTaskId }
+ }
@VisibleForTesting
fun getTransitionObserver(): TransitionObserver = minimizeTransitionObserver
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/draganddrop/DragUtils.java b/libs/WindowManager/Shell/src/com/android/wm/shell/draganddrop/DragUtils.java
index 22cfa328bfda..248a1124cd86 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/draganddrop/DragUtils.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/draganddrop/DragUtils.java
@@ -125,25 +125,35 @@ public class DragUtils {
StringJoiner str = new StringJoiner("|");
if ((dragFlags & DRAG_FLAG_GLOBAL) != 0) {
str.add("GLOBAL");
- } else if ((dragFlags & DRAG_FLAG_GLOBAL_URI_READ) != 0) {
+ }
+ if ((dragFlags & DRAG_FLAG_GLOBAL_URI_READ) != 0) {
str.add("GLOBAL_URI_READ");
- } else if ((dragFlags & DRAG_FLAG_GLOBAL_URI_WRITE) != 0) {
+ }
+ if ((dragFlags & DRAG_FLAG_GLOBAL_URI_WRITE) != 0) {
str.add("GLOBAL_URI_WRITE");
- } else if ((dragFlags & DRAG_FLAG_GLOBAL_PERSISTABLE_URI_PERMISSION) != 0) {
+ }
+ if ((dragFlags & DRAG_FLAG_GLOBAL_PERSISTABLE_URI_PERMISSION) != 0) {
str.add("GLOBAL_PERSISTABLE_URI_PERMISSION");
- } else if ((dragFlags & DRAG_FLAG_GLOBAL_PREFIX_URI_PERMISSION) != 0) {
+ }
+ if ((dragFlags & DRAG_FLAG_GLOBAL_PREFIX_URI_PERMISSION) != 0) {
str.add("GLOBAL_PREFIX_URI_PERMISSION");
- } else if ((dragFlags & DRAG_FLAG_OPAQUE) != 0) {
+ }
+ if ((dragFlags & DRAG_FLAG_OPAQUE) != 0) {
str.add("OPAQUE");
- } else if ((dragFlags & DRAG_FLAG_ACCESSIBILITY_ACTION) != 0) {
+ }
+ if ((dragFlags & DRAG_FLAG_ACCESSIBILITY_ACTION) != 0) {
str.add("ACCESSIBILITY_ACTION");
- } else if ((dragFlags & DRAG_FLAG_REQUEST_SURFACE_FOR_RETURN_ANIMATION) != 0) {
+ }
+ if ((dragFlags & DRAG_FLAG_REQUEST_SURFACE_FOR_RETURN_ANIMATION) != 0) {
str.add("REQUEST_SURFACE_FOR_RETURN_ANIMATION");
- } else if ((dragFlags & DRAG_FLAG_GLOBAL_SAME_APPLICATION) != 0) {
+ }
+ if ((dragFlags & DRAG_FLAG_GLOBAL_SAME_APPLICATION) != 0) {
str.add("GLOBAL_SAME_APPLICATION");
- } else if ((dragFlags & DRAG_FLAG_START_INTENT_SENDER_ON_UNHANDLED_DRAG) != 0) {
+ }
+ if ((dragFlags & DRAG_FLAG_START_INTENT_SENDER_ON_UNHANDLED_DRAG) != 0) {
str.add("START_INTENT_SENDER_ON_UNHANDLED_DRAG");
- } else if ((dragFlags & DRAG_FLAG_HIDE_CALLING_TASK_ON_DRAG_START) != 0) {
+ }
+ if ((dragFlags & DRAG_FLAG_HIDE_CALLING_TASK_ON_DRAG_START) != 0) {
str.add("HIDE_CALLING_TASK_ON_DRAG_START");
}
return str.toString();
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipResizeGestureHandler.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipResizeGestureHandler.java
index 89d3dd63a08e..242f7fa5f1b1 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipResizeGestureHandler.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipResizeGestureHandler.java
@@ -15,7 +15,12 @@
*/
package com.android.wm.shell.pip.phone;
+import static com.android.internal.policy.TaskResizingAlgorithm.CTRL_BOTTOM;
+import static com.android.internal.policy.TaskResizingAlgorithm.CTRL_LEFT;
import static com.android.internal.policy.TaskResizingAlgorithm.CTRL_NONE;
+import static com.android.internal.policy.TaskResizingAlgorithm.CTRL_RIGHT;
+import static com.android.internal.policy.TaskResizingAlgorithm.CTRL_TOP;
+import static com.android.wm.shell.pip.phone.PipMenuView.ANIM_TYPE_NONE;
import android.annotation.Nullable;
import android.content.Context;
@@ -23,6 +28,7 @@ import android.content.res.Resources;
import android.graphics.Point;
import android.graphics.PointF;
import android.graphics.Rect;
+import android.graphics.Region;
import android.hardware.input.InputManager;
import android.os.Looper;
import android.view.BatchedInputEventReceiver;
@@ -36,6 +42,8 @@ import android.view.ViewConfiguration;
import androidx.annotation.VisibleForTesting;
+import com.android.internal.policy.TaskResizingAlgorithm;
+import com.android.window.flags.Flags;
import com.android.wm.shell.R;
import com.android.wm.shell.common.ShellExecutor;
import com.android.wm.shell.common.pip.PipBoundsAlgorithm;
@@ -48,6 +56,7 @@ import com.android.wm.shell.pip.PipTaskOrganizer;
import java.io.PrintWriter;
import java.util.function.Consumer;
+import java.util.function.Function;
/**
* Helper on top of PipTouchHandler that handles inputs OUTSIDE of the PIP window, which is used to
@@ -71,6 +80,7 @@ public class PipResizeGestureHandler {
private final PipPinchResizingAlgorithm mPinchResizingAlgorithm;
private final int mDisplayId;
private final ShellExecutor mMainExecutor;
+ private final Region mTmpRegion = new Region();
private final PointF mDownPoint = new PointF();
private final PointF mDownSecondPoint = new PointF();
@@ -81,15 +91,24 @@ public class PipResizeGestureHandler {
private final Rect mLastResizeBounds = new Rect();
private final Rect mUserResizeBounds = new Rect();
private final Rect mDownBounds = new Rect();
+ private final Rect mDragCornerSize = new Rect();
+ private final Rect mTmpTopLeftCorner = new Rect();
+ private final Rect mTmpTopRightCorner = new Rect();
+ private final Rect mTmpBottomLeftCorner = new Rect();
+ private final Rect mTmpBottomRightCorner = new Rect();
+ private final Rect mDisplayBounds = new Rect();
+ private final Function<Rect, Rect> mMovementBoundsSupplier;
private final Runnable mUpdateMovementBoundsRunnable;
private final Consumer<Rect> mUpdateResizeBoundsCallback;
+ private int mDelta;
private float mTouchSlop;
private boolean mAllowGesture;
private boolean mIsAttached;
private boolean mIsEnabled;
private boolean mEnablePinchResize;
+ private boolean mEnableDragCornerResize;
private boolean mIsSysUiStateValid;
private boolean mThresholdCrossed;
private boolean mOngoingPinchToResize = false;
@@ -113,7 +132,7 @@ public class PipResizeGestureHandler {
PipBoundsState pipBoundsState, PipMotionHelper motionHelper,
PipTouchState pipTouchState, PipTaskOrganizer pipTaskOrganizer,
PipDismissTargetHandler pipDismissTargetHandler,
- Runnable updateMovementBoundsRunnable,
+ Function<Rect, Rect> movementBoundsSupplier, Runnable updateMovementBoundsRunnable,
PipUiEventLogger pipUiEventLogger, PhonePipMenuController menuActivityController,
ShellExecutor mainExecutor, @Nullable PipPerfHintController pipPerfHintController) {
mContext = context;
@@ -126,6 +145,7 @@ public class PipResizeGestureHandler {
mPipTouchState = pipTouchState;
mPipTaskOrganizer = pipTaskOrganizer;
mPipDismissTargetHandler = pipDismissTargetHandler;
+ mMovementBoundsSupplier = movementBoundsSupplier;
mUpdateMovementBoundsRunnable = updateMovementBoundsRunnable;
mPhonePipMenuController = menuActivityController;
mPipUiEventLogger = pipUiEventLogger;
@@ -161,9 +181,20 @@ public class PipResizeGestureHandler {
}
private void reloadResources() {
+ final Resources res = mContext.getResources();
+ mDelta = res.getDimensionPixelSize(R.dimen.pip_resize_edge_size);
+ mEnableDragCornerResize = Flags.enableDesktopWindowingPip();
mTouchSlop = ViewConfiguration.get(mContext).getScaledTouchSlop();
}
+ private void resetDragCorners() {
+ mDragCornerSize.set(0, 0, mDelta, mDelta);
+ mTmpTopLeftCorner.set(mDragCornerSize);
+ mTmpTopRightCorner.set(mDragCornerSize);
+ mTmpBottomLeftCorner.set(mDragCornerSize);
+ mTmpBottomRightCorner.set(mDragCornerSize);
+ }
+
private void disposeInputChannel() {
if (mInputEventReceiver != null) {
mInputEventReceiver.dispose();
@@ -211,7 +242,7 @@ public class PipResizeGestureHandler {
@VisibleForTesting
void onInputEvent(InputEvent ev) {
- if (!mEnablePinchResize) {
+ if (!mEnableDragCornerResize && !mEnablePinchResize) {
// No need to handle anything if neither form of resizing is enabled.
return;
}
@@ -239,6 +270,8 @@ public class PipResizeGestureHandler {
if (mEnablePinchResize && mOngoingPinchToResize) {
onPinchResize(mv);
+ } else if (mEnableDragCornerResize) {
+ onDragCornerResize(mv);
}
}
}
@@ -250,6 +283,48 @@ public class PipResizeGestureHandler {
return mCtrlType != CTRL_NONE || mOngoingPinchToResize;
}
+ /**
+ * Check whether the current x,y coordinate is within the region in which drag-resize should
+ * start.
+ * This consists of 4 small squares on the 4 corners of the PIP window, a quarter of which
+ * overlaps with the PIP window while the rest goes outside of the PIP window.
+ * _ _ _ _
+ * |_|_|_________|_|_|
+ * |_|_| |_|_|
+ * | PIP |
+ * | WINDOW |
+ * _|_ _|_
+ * |_|_|_________|_|_|
+ * |_|_| |_|_|
+ */
+ public boolean isWithinDragResizeRegion(int x, int y) {
+ if (!mEnableDragCornerResize) {
+ return false;
+ }
+
+ final Rect currentPipBounds = mPipBoundsState.getBounds();
+ if (currentPipBounds == null) {
+ return false;
+ }
+ resetDragCorners();
+ mTmpTopLeftCorner.offset(currentPipBounds.left - mDelta / 2,
+ currentPipBounds.top - mDelta / 2);
+ mTmpTopRightCorner.offset(currentPipBounds.right - mDelta / 2,
+ currentPipBounds.top - mDelta / 2);
+ mTmpBottomLeftCorner.offset(currentPipBounds.left - mDelta / 2,
+ currentPipBounds.bottom - mDelta / 2);
+ mTmpBottomRightCorner.offset(currentPipBounds.right - mDelta / 2,
+ currentPipBounds.bottom - mDelta / 2);
+
+ mTmpRegion.setEmpty();
+ mTmpRegion.op(mTmpTopLeftCorner, Region.Op.UNION);
+ mTmpRegion.op(mTmpTopRightCorner, Region.Op.UNION);
+ mTmpRegion.op(mTmpBottomLeftCorner, Region.Op.UNION);
+ mTmpRegion.op(mTmpBottomRightCorner, Region.Op.UNION);
+
+ return mTmpRegion.contains(x, y);
+ }
+
public boolean isUsingPinchToZoom() {
return mEnablePinchResize;
}
@@ -260,17 +335,62 @@ public class PipResizeGestureHandler {
public boolean willStartResizeGesture(MotionEvent ev) {
if (isInValidSysUiState()) {
- if (ev.getActionMasked() == MotionEvent.ACTION_POINTER_DOWN) {
- if (mEnablePinchResize && ev.getPointerCount() == 2) {
- onPinchResize(ev);
- mOngoingPinchToResize = mAllowGesture;
- return mAllowGesture;
- }
+ switch (ev.getActionMasked()) {
+ case MotionEvent.ACTION_DOWN:
+ if (isWithinDragResizeRegion((int) ev.getRawX(), (int) ev.getRawY())) {
+ return true;
+ }
+ break;
+
+ case MotionEvent.ACTION_POINTER_DOWN:
+ if (mEnablePinchResize && ev.getPointerCount() == 2) {
+ onPinchResize(ev);
+ mOngoingPinchToResize = mAllowGesture;
+ return mAllowGesture;
+ }
+ break;
+
+ default:
+ break;
}
}
return false;
}
+ private void setCtrlType(int x, int y) {
+ final Rect currentPipBounds = mPipBoundsState.getBounds();
+
+ Rect movementBounds = mMovementBoundsSupplier.apply(currentPipBounds);
+
+ mDisplayBounds.set(movementBounds.left,
+ movementBounds.top,
+ movementBounds.right + currentPipBounds.width(),
+ movementBounds.bottom + currentPipBounds.height());
+
+ if (mTmpTopLeftCorner.contains(x, y) && currentPipBounds.top != mDisplayBounds.top
+ && currentPipBounds.left != mDisplayBounds.left) {
+ mCtrlType |= CTRL_LEFT;
+ mCtrlType |= CTRL_TOP;
+ }
+ if (mTmpTopRightCorner.contains(x, y) && currentPipBounds.top != mDisplayBounds.top
+ && currentPipBounds.right != mDisplayBounds.right) {
+ mCtrlType |= CTRL_RIGHT;
+ mCtrlType |= CTRL_TOP;
+ }
+ if (mTmpBottomRightCorner.contains(x, y)
+ && currentPipBounds.bottom != mDisplayBounds.bottom
+ && currentPipBounds.right != mDisplayBounds.right) {
+ mCtrlType |= CTRL_RIGHT;
+ mCtrlType |= CTRL_BOTTOM;
+ }
+ if (mTmpBottomLeftCorner.contains(x, y)
+ && currentPipBounds.bottom != mDisplayBounds.bottom
+ && currentPipBounds.left != mDisplayBounds.left) {
+ mCtrlType |= CTRL_LEFT;
+ mCtrlType |= CTRL_BOTTOM;
+ }
+ }
+
private boolean isInValidSysUiState() {
return mIsSysUiStateValid;
}
@@ -364,6 +484,59 @@ public class PipResizeGestureHandler {
}
}
+ private void onDragCornerResize(MotionEvent ev) {
+ int action = ev.getActionMasked();
+ float x = ev.getX();
+ float y = ev.getY() - mOhmOffset;
+ if (action == MotionEvent.ACTION_DOWN) {
+ mLastResizeBounds.setEmpty();
+ mAllowGesture = isInValidSysUiState() && isWithinDragResizeRegion((int) x, (int) y);
+ if (mAllowGesture) {
+ setCtrlType((int) x, (int) y);
+ mDownPoint.set(x, y);
+ mDownBounds.set(mPipBoundsState.getBounds());
+ }
+ } else if (mAllowGesture) {
+ switch (action) {
+ case MotionEvent.ACTION_POINTER_DOWN:
+ // We do not support multi touch for resizing via drag
+ mAllowGesture = false;
+ break;
+ case MotionEvent.ACTION_MOVE:
+ // Capture inputs
+ if (!mThresholdCrossed
+ && Math.hypot(x - mDownPoint.x, y - mDownPoint.y) > mTouchSlop) {
+ mThresholdCrossed = true;
+ // Reset the down to begin resizing from this point
+ mDownPoint.set(x, y);
+ mInputMonitor.pilferPointers();
+ }
+ if (mThresholdCrossed) {
+ if (mPhonePipMenuController.isMenuVisible()) {
+ mPhonePipMenuController.hideMenu(ANIM_TYPE_NONE,
+ false /* resize */);
+ }
+ final Rect currentPipBounds = mPipBoundsState.getBounds();
+ mLastResizeBounds.set(TaskResizingAlgorithm.resizeDrag(x, y,
+ mDownPoint.x, mDownPoint.y, currentPipBounds, mCtrlType, mMinSize.x,
+ mMinSize.y, mMaxSize, true,
+ mDownBounds.width() > mDownBounds.height()));
+ mPipBoundsAlgorithm.transformBoundsToAspectRatio(mLastResizeBounds,
+ mPipBoundsState.getAspectRatio(), false /* useCurrentMinEdgeSize */,
+ true /* useCurrentSize */);
+ mPipTaskOrganizer.scheduleUserResizePip(mDownBounds, mLastResizeBounds,
+ null);
+ mPipBoundsState.setHasUserResizedPip(true);
+ }
+ break;
+ case MotionEvent.ACTION_UP:
+ case MotionEvent.ACTION_CANCEL:
+ finishResize();
+ break;
+ }
+ }
+ }
+
private void snapToMovementBoundsEdge(Rect bounds, Rect movementBounds) {
final int leftEdge = bounds.left;
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipTouchHandler.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipTouchHandler.java
index 9c4e723efc23..f4c2a33079ba 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipTouchHandler.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipTouchHandler.java
@@ -213,7 +213,7 @@ public class PipTouchHandler {
mPipResizeGestureHandler =
new PipResizeGestureHandler(context, pipBoundsAlgorithm, pipBoundsState,
mMotionHelper, mTouchState, pipTaskOrganizer, mPipDismissTargetHandler,
- this::updateMovementBounds, pipUiEventLogger,
+ this::getMovementBounds, this::updateMovementBounds, pipUiEventLogger,
menuController, mainExecutor, mPipPerfHintController);
mConnection = new PipAccessibilityInteractionConnection(mContext, pipBoundsState,
mMotionHelper, pipTaskOrganizer, mPipBoundsAlgorithm.getSnapAlgorithm(),
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/tv/TvPipController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/tv/TvPipController.java
index 0ed5079b7fba..8ac7f89d8f8a 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/tv/TvPipController.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/tv/TvPipController.java
@@ -476,6 +476,7 @@ public class TvPipController implements PipTransitionController.PipTransitionCal
}
mPipTaskOrganizer.removePip();
mTvPipMenuController.closeMenu();
+ mPipNotificationController.dismiss();
}
@Override
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/taskview/OWNERS b/libs/WindowManager/Shell/src/com/android/wm/shell/taskview/OWNERS
new file mode 100644
index 000000000000..892d15079de6
--- /dev/null
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/taskview/OWNERS
@@ -0,0 +1,5 @@
+# Bubbles team
+madym@google.com
+atsjenk@google.com
+liranb@google.com
+mpodolian@google.com
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/taskview/TaskViewTaskController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/taskview/TaskViewTaskController.java
index e74342e1910c..eaeedba8ef9c 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/taskview/TaskViewTaskController.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/taskview/TaskViewTaskController.java
@@ -40,6 +40,7 @@ import android.view.WindowInsets;
import android.window.WindowContainerToken;
import android.window.WindowContainerTransaction;
+import com.android.wm.shell.Flags;
import com.android.wm.shell.ShellTaskOrganizer;
import com.android.wm.shell.common.SyncTransactionQueue;
@@ -525,8 +526,13 @@ public class TaskViewTaskController implements ShellTaskOrganizer.TaskListener {
*/
void removeTask() {
if (mTaskToken == null) {
- // Call to remove task before we have one, do nothing
- Slog.w(TAG, "Trying to remove a task that was never added? (no taskToken)");
+ if (Flags.enableTaskViewControllerCleanup()) {
+ // We don't have a task yet. Only clean up the controller
+ mTaskViewTransitions.removeTaskView(this);
+ } else {
+ // Call to remove task before we have one, do nothing
+ Slog.w(TAG, "Trying to remove a task that was never added? (no taskToken)");
+ }
return;
}
mShellExecutor.execute(() -> {
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/taskview/TaskViewTransitions.java b/libs/WindowManager/Shell/src/com/android/wm/shell/taskview/TaskViewTransitions.java
index 39648f65b4f3..ae75cb589de0 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/taskview/TaskViewTransitions.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/taskview/TaskViewTransitions.java
@@ -37,11 +37,14 @@ import android.window.WindowContainerTransaction;
import androidx.annotation.VisibleForTesting;
+import com.android.wm.shell.Flags;
import com.android.wm.shell.shared.TransitionUtil;
import com.android.wm.shell.transition.Transitions;
import java.util.ArrayList;
+import java.util.Map;
import java.util.Objects;
+import java.util.WeakHashMap;
/**
* Handles Shell Transitions that involve TaskView tasks.
@@ -49,8 +52,15 @@ import java.util.Objects;
public class TaskViewTransitions implements Transitions.TransitionHandler {
static final String TAG = "TaskViewTransitions";
- private final ArrayMap<TaskViewTaskController, TaskViewRequestedState> mTaskViews =
- new ArrayMap<>();
+ /**
+ * Map of {@link TaskViewTaskController} to {@link TaskViewRequestedState}.
+ * <p>
+ * {@link TaskView} keeps a reference to the {@link TaskViewTaskController} instance and
+ * manages its lifecycle.
+ * Only keep a weak reference to the controller instance here to allow for it to be cleaned
+ * up when its TaskView is no longer used.
+ */
+ private final Map<TaskViewTaskController, TaskViewRequestedState> mTaskViews;
private final ArrayList<PendingTransition> mPending = new ArrayList<>();
private final Transitions mTransitions;
private final boolean[] mRegistered = new boolean[]{false};
@@ -95,6 +105,11 @@ public class TaskViewTransitions implements Transitions.TransitionHandler {
public TaskViewTransitions(Transitions transitions) {
mTransitions = transitions;
+ if (Flags.enableTaskViewControllerCleanup()) {
+ mTaskViews = new WeakHashMap<>();
+ } else {
+ mTaskViews = new ArrayMap<>();
+ }
// Defer registration until the first TaskView because we want this to be the "first" in
// priority when handling requests.
// TODO(210041388): register here once we have an explicit ordering mechanism.
@@ -208,10 +223,21 @@ public class TaskViewTransitions implements Transitions.TransitionHandler {
}
private TaskViewTaskController findTaskView(ActivityManager.RunningTaskInfo taskInfo) {
- for (int i = 0; i < mTaskViews.size(); ++i) {
- if (mTaskViews.keyAt(i).getTaskInfo() == null) continue;
- if (taskInfo.token.equals(mTaskViews.keyAt(i).getTaskInfo().token)) {
- return mTaskViews.keyAt(i);
+ if (Flags.enableTaskViewControllerCleanup()) {
+ for (TaskViewTaskController controller : mTaskViews.keySet()) {
+ if (controller.getTaskInfo() == null) continue;
+ if (taskInfo.token.equals(controller.getTaskInfo().token)) {
+ return controller;
+ }
+ }
+ } else {
+ ArrayMap<TaskViewTaskController, TaskViewRequestedState> taskViews =
+ (ArrayMap<TaskViewTaskController, TaskViewRequestedState>) mTaskViews;
+ for (int i = 0; i < taskViews.size(); ++i) {
+ if (taskViews.keyAt(i).getTaskInfo() == null) continue;
+ if (taskInfo.token.equals(taskViews.keyAt(i).getTaskInfo().token)) {
+ return taskViews.keyAt(i);
+ }
}
}
return null;
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 a775cbc6c9f3..f404326dda83 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
@@ -130,6 +130,7 @@ import com.android.wm.shell.sysui.ShellInit;
import com.android.wm.shell.transition.FocusTransitionObserver;
import com.android.wm.shell.transition.Transitions;
import com.android.wm.shell.windowdecor.DesktopModeWindowDecoration.ExclusionRegionListener;
+import com.android.wm.shell.windowdecor.additionalviewcontainer.AdditionalSystemViewContainer;
import com.android.wm.shell.windowdecor.extension.InsetsStateKt;
import com.android.wm.shell.windowdecor.extension.TaskInfoKt;
import com.android.wm.shell.windowdecor.viewholder.AppHeaderViewHolder;
@@ -180,6 +181,7 @@ public class DesktopModeWindowDecorViewModel implements WindowDecorViewModel,
private boolean mTransitionDragActive;
private SparseArray<EventReceiver> mEventReceiversByDisplay = new SparseArray<>();
+ private DesktopStatusBarInputLayerSupplier mStatusBarInputLayerSupplier;
private final ExclusionRegionListener mExclusionRegionListener =
new ExclusionRegionListenerImpl();
@@ -418,7 +420,11 @@ public class DesktopModeWindowDecorViewModel implements WindowDecorViewModel,
return Unit.INSTANCE;
});
}
- mFocusTransitionObserver.setLocalFocusTransitionListener(this, mMainExecutor);
+ if (Flags.enableHandleInputFix()) {
+ mStatusBarInputLayerSupplier =
+ new DesktopStatusBarInputLayerSupplier(mContext, mMainHandler);
+ mFocusTransitionObserver.setLocalFocusTransitionListener(this, mMainExecutor);
+ }
}
@Override
@@ -474,6 +480,7 @@ public class DesktopModeWindowDecorViewModel implements WindowDecorViewModel,
removeTaskFromEventReceiver(oldTaskInfo.displayId);
incrementEventReceiverTasks(taskInfo.displayId);
}
+ decoration.setStatusBarInputLayer(getStatusBarInputLayer(taskInfo));
decoration.relayout(taskInfo, decoration.mHasGlobalFocus);
mActivityOrientationChangeHandler.ifPresent(handler ->
handler.handleActivityOrientationChange(oldTaskInfo, taskInfo));
@@ -512,6 +519,7 @@ public class DesktopModeWindowDecorViewModel implements WindowDecorViewModel,
if (decoration == null) {
createWindowDecoration(taskInfo, taskSurface, startT, finishT);
} else {
+ decoration.setStatusBarInputLayer(getStatusBarInputLayer(taskInfo));
decoration.relayout(taskInfo, startT, finishT, false /* applyStartTransactionOnDraw */,
false /* shouldSetTaskPositionAndCrop */,
mFocusTransitionObserver.hasGlobalFocus(taskInfo));
@@ -578,6 +586,7 @@ public class DesktopModeWindowDecorViewModel implements WindowDecorViewModel,
return;
}
mDesktopTasksController.toggleDesktopTaskFullImmersiveState(decoration.mTaskInfo);
+ decoration.closeMaximizeMenu();
}
private void onSnapResize(int taskId, boolean left, MotionEvent motionEvent) {
@@ -663,7 +672,7 @@ public class DesktopModeWindowDecorViewModel implements WindowDecorViewModel,
decoration.closeHandleMenu();
// When the app enters split-select, the handle will no longer be visible, meaning
// we shouldn't receive input for it any longer.
- decoration.disposeStatusBarInputLayer();
+ decoration.detachStatusBarInputLayer();
mDesktopTasksController.requestSplit(decoration.mTaskInfo, false /* leftOrTop */);
}
@@ -1303,8 +1312,8 @@ public class DesktopModeWindowDecorViewModel implements WindowDecorViewModel,
// should not be receiving any input.
if (resultType == TO_SPLIT_LEFT_INDICATOR
|| resultType == TO_SPLIT_RIGHT_INDICATOR) {
- relevantDecor.disposeStatusBarInputLayer();
- // We should also dispose the other split task's input layer if
+ relevantDecor.detachStatusBarInputLayer();
+ // We should also detach the other split task's input layer if
// applicable.
final int splitPosition = mSplitScreenController
.getSplitPosition(relevantDecor.mTaskInfo.taskId);
@@ -1317,7 +1326,7 @@ public class DesktopModeWindowDecorViewModel implements WindowDecorViewModel,
mSplitScreenController.getTaskInfo(oppositePosition);
if (oppositeTaskInfo != null) {
mWindowDecorByTaskId.get(oppositeTaskInfo.taskId)
- .disposeStatusBarInputLayer();
+ .detachStatusBarInputLayer();
}
}
}
@@ -1529,6 +1538,10 @@ public class DesktopModeWindowDecorViewModel implements WindowDecorViewModel,
touchEventListener.mMotionEvent);
return Unit.INSTANCE;
});
+ windowDecoration.setOnImmersiveOrRestoreClickListener(() -> {
+ onEnterOrExitImmersive(taskInfo.taskId);
+ return Unit.INSTANCE;
+ });
windowDecoration.setOnLeftSnapClickListener(() -> {
onSnapResize(taskInfo.taskId, /* isLeft= */ true, touchEventListener.mMotionEvent);
return Unit.INSTANCE;
@@ -1563,6 +1576,7 @@ public class DesktopModeWindowDecorViewModel implements WindowDecorViewModel,
touchEventListener, touchEventListener, touchEventListener, touchEventListener);
windowDecoration.setExclusionRegionListener(mExclusionRegionListener);
windowDecoration.setDragPositioningCallback(taskPositioner);
+ windowDecoration.setStatusBarInputLayer(getStatusBarInputLayer(taskInfo));
windowDecoration.relayout(taskInfo, startT, finishT,
false /* applyStartTransactionOnDraw */, false /* shouldSetTaskPositionAndCrop */,
mFocusTransitionObserver.hasGlobalFocus(taskInfo));
@@ -1571,6 +1585,18 @@ public class DesktopModeWindowDecorViewModel implements WindowDecorViewModel,
}
}
+ /** Decide which cached status bar input layer should be used for a decoration. */
+ private AdditionalSystemViewContainer getStatusBarInputLayer(
+ RunningTaskInfo taskInfo
+ ) {
+ if (mStatusBarInputLayerSupplier == null) return null;
+ return mStatusBarInputLayerSupplier.getStatusBarInputLayer(
+ taskInfo,
+ mSplitScreenController.getSplitPosition(taskInfo.taskId),
+ mSplitScreenController.isLeftRightSplit()
+ );
+ }
+
private RunningTaskInfo getOtherSplitTask(int taskId) {
@SplitPosition int remainingTaskPosition = mSplitScreenController
.getSplitPosition(taskId) == SPLIT_POSITION_BOTTOM_OR_RIGHT
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 d94f3a9a70c6..88651121d73a 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
@@ -102,6 +102,7 @@ import com.android.wm.shell.shared.desktopmode.DesktopModeStatus;
import com.android.wm.shell.shared.desktopmode.DesktopModeTransitionSource;
import com.android.wm.shell.shared.desktopmode.ManageWindowsViewContainer;
import com.android.wm.shell.splitscreen.SplitScreenController;
+import com.android.wm.shell.windowdecor.additionalviewcontainer.AdditionalSystemViewContainer;
import com.android.wm.shell.windowdecor.extension.TaskInfoKt;
import com.android.wm.shell.windowdecor.viewholder.AppHandleViewHolder;
import com.android.wm.shell.windowdecor.viewholder.AppHeaderViewHolder;
@@ -142,6 +143,7 @@ public class DesktopModeWindowDecoration extends WindowDecoration<WindowDecorLin
private View.OnLongClickListener mOnCaptionLongClickListener;
private View.OnGenericMotionListener mOnCaptionGenericMotionListener;
private Function0<Unit> mOnMaximizeOrRestoreClickListener;
+ private Function0<Unit> mOnImmersiveOrRestoreClickListener;
private Function0<Unit> mOnLeftSnapClickListener;
private Function0<Unit> mOnRightSnapClickListener;
private Consumer<DesktopModeTransitionSource> mOnToDesktopClickListener;
@@ -198,6 +200,7 @@ public class DesktopModeWindowDecoration extends WindowDecoration<WindowDecorLin
private final MultiInstanceHelper mMultiInstanceHelper;
private final WindowDecorCaptionHandleRepository mWindowDecorCaptionHandleRepository;
private final DesktopRepository mDesktopRepository;
+ private AdditionalSystemViewContainer mStatusBarInputLayer;
DesktopModeWindowDecoration(
Context context,
@@ -291,6 +294,14 @@ public class DesktopModeWindowDecoration extends WindowDecoration<WindowDecorLin
mOnMaximizeOrRestoreClickListener = listener;
}
+ /**
+ * Registers a listener to be called back when one of the tasks' immersive/restore action is
+ * triggered.
+ */
+ void setOnImmersiveOrRestoreClickListener(Function0<Unit> listener) {
+ mOnImmersiveOrRestoreClickListener = listener;
+ }
+
/** Registers a listener to be called when the decoration's snap-left action is triggered.*/
void setOnLeftSnapClickListener(Function0<Unit> listener) {
mOnLeftSnapClickListener = listener;
@@ -497,13 +508,13 @@ public class DesktopModeWindowDecoration extends WindowDecoration<WindowDecorLin
notifyNoCaptionHandle();
}
mExclusionRegionListener.onExclusionRegionDismissed(mTaskInfo.taskId);
- disposeStatusBarInputLayer();
+ detachStatusBarInputLayer();
Trace.endSection(); // DesktopModeWindowDecoration#updateRelayoutParamsAndSurfaces
return;
}
if (oldRootView != mResult.mRootView) {
- disposeStatusBarInputLayer();
+ detachStatusBarInputLayer();
mWindowDecorViewHolder = createViewHolder();
}
Trace.beginSection("DesktopModeWindowDecoration#relayout-binding");
@@ -521,6 +532,9 @@ public class DesktopModeWindowDecoration extends WindowDecoration<WindowDecorLin
mTaskInfo, position, mResult.mCaptionWidth, mResult.mCaptionHeight,
isCaptionVisible()
));
+ if (mStatusBarInputLayer != null) {
+ asAppHandle(mWindowDecorViewHolder).bindStatusBarInputLayer(mStatusBarInputLayer);
+ }
} else {
mWindowDecorViewHolder.bindData(new AppHeaderViewHolder.HeaderData(
mTaskInfo,
@@ -713,7 +727,8 @@ public class DesktopModeWindowDecoration extends WindowDecoration<WindowDecorLin
if (!mTaskInfo.isVisible()) {
closeMaximizeMenu();
} else {
- mMaximizeMenu.positionMenu(calculateMaximizeMenuPosition(), startT);
+ final int menuWidth = calculateMaximizeMenuWidth();
+ mMaximizeMenu.positionMenu(calculateMaximizeMenuPosition(menuWidth), startT);
}
}
@@ -732,15 +747,15 @@ public class DesktopModeWindowDecoration extends WindowDecoration<WindowDecorLin
}
/**
- * Dispose of the view used to forward inputs in status bar region. Intended to be
+ * Detach the status bar input layer from this decoration. Intended to be
* used any time handle is no longer visible.
*/
- void disposeStatusBarInputLayer() {
+ void detachStatusBarInputLayer() {
if (!isAppHandle(mWindowDecorViewHolder)
|| !Flags.enableHandleInputFix()) {
return;
}
- asAppHandle(mWindowDecorViewHolder).disposeStatusBarInputLayer();
+ asAppHandle(mWindowDecorViewHolder).detachStatusBarInputLayer();
}
private WindowDecorationViewHolder createViewHolder() {
@@ -934,8 +949,27 @@ public class DesktopModeWindowDecoration extends WindowDecoration<WindowDecorLin
return Resources.ID_NULL;
}
-
- private PointF calculateMaximizeMenuPosition() {
+ private int calculateMaximizeMenuWidth() {
+ final boolean showImmersive = Flags.enableFullyImmersiveInDesktop()
+ && TaskInfoKt.getRequestingImmersive(mTaskInfo);
+ final boolean showMaximize = true;
+ final boolean showSnaps = mTaskInfo.isResizeable;
+ int showCount = 0;
+ if (showImmersive) showCount++;
+ if (showMaximize) showCount++;
+ if (showSnaps) showCount++;
+ return switch (showCount) {
+ case 1 -> loadDimensionPixelSize(mContext.getResources(),
+ R.dimen.desktop_mode_maximize_menu_width_one_options);
+ case 2 -> loadDimensionPixelSize(mContext.getResources(),
+ R.dimen.desktop_mode_maximize_menu_width_two_options);
+ case 3 -> loadDimensionPixelSize(mContext.getResources(),
+ R.dimen.desktop_mode_maximize_menu_width_three_options);
+ default -> throw new IllegalArgumentException("");
+ };
+ }
+
+ private PointF calculateMaximizeMenuPosition(int menuWidth) {
final PointF position = new PointF();
final Resources resources = mContext.getResources();
final DisplayLayout displayLayout =
@@ -951,8 +985,6 @@ public class DesktopModeWindowDecoration extends WindowDecoration<WindowDecorLin
final int[] maximizeButtonLocation = new int[2];
maximizeWindowButton.getLocationInWindow(maximizeButtonLocation);
- final int menuWidth = loadDimensionPixelSize(
- resources, R.dimen.desktop_mode_maximize_menu_width);
final int menuHeight = loadDimensionPixelSize(
resources, R.dimen.desktop_mode_maximize_menu_height);
@@ -1183,11 +1215,19 @@ public class DesktopModeWindowDecoration extends WindowDecoration<WindowDecorLin
* Create and display maximize menu window
*/
void createMaximizeMenu() {
+ final int menuWidth = calculateMaximizeMenuWidth();
mMaximizeMenu = mMaximizeMenuFactory.create(mSyncQueue, mRootTaskDisplayAreaOrganizer,
mDisplayController, mTaskInfo, mContext,
- calculateMaximizeMenuPosition(), mSurfaceControlTransactionSupplier);
+ calculateMaximizeMenuPosition(menuWidth), mSurfaceControlTransactionSupplier);
mMaximizeMenu.show(
+ /* isTaskInImmersiveMode= */ Flags.enableFullyImmersiveInDesktop()
+ && mDesktopRepository.isTaskInFullImmersiveState(mTaskInfo.taskId),
+ /* menuWidth= */ menuWidth,
+ /* showImmersiveOption= */ Flags.enableFullyImmersiveInDesktop()
+ && TaskInfoKt.getRequestingImmersive(mTaskInfo),
+ /* showSnapOptions= */ mTaskInfo.isResizeable,
mOnMaximizeOrRestoreClickListener,
+ mOnImmersiveOrRestoreClickListener,
mOnLeftSnapClickListener,
mOnRightSnapClickListener,
hovered -> {
@@ -1428,19 +1468,6 @@ public class DesktopModeWindowDecoration extends WindowDecoration<WindowDecorLin
}
}
- /**
- * Close an open maximize menu if input is outside of menu coordinates
- *
- * @param ev the tapped point to compare against
- */
- void closeMaximizeMenuIfNeeded(MotionEvent ev) {
- if (!isMaximizeMenuActive()) return;
-
- if (!mMaximizeMenu.isValidMenuInput(ev)) {
- closeMaximizeMenu();
- }
- }
-
boolean isFocused() {
return mHasGlobalFocus;
}
@@ -1568,7 +1595,7 @@ public class DesktopModeWindowDecoration extends WindowDecoration<WindowDecorLin
closeManageWindowsMenu();
mExclusionRegionListener.onExclusionRegionDismissed(mTaskInfo.taskId);
disposeResizeVeil();
- disposeStatusBarInputLayer();
+ detachStatusBarInputLayer();
clearCurrentViewHostRunnable();
if (canEnterDesktopMode(mContext) && Flags.enableDesktopWindowingAppHandleEducation()) {
notifyNoCaptionHandle();
@@ -1685,6 +1712,16 @@ public class DesktopModeWindowDecoration extends WindowDecoration<WindowDecorLin
+ "}";
}
+ /**
+ * Set the view container to be used to forward input through status bar. Null in cases
+ * where input forwarding isn't needed.
+ */
+ public void setStatusBarInputLayer(
+ @Nullable AdditionalSystemViewContainer additionalSystemViewContainer
+ ) {
+ mStatusBarInputLayer = additionalSystemViewContainer;
+ }
+
static class Factory {
DesktopModeWindowDecoration create(
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/DesktopStatusBarInputLayerSupplier.kt b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/DesktopStatusBarInputLayerSupplier.kt
new file mode 100644
index 000000000000..025bb403ec8b
--- /dev/null
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/DesktopStatusBarInputLayerSupplier.kt
@@ -0,0 +1,117 @@
+/*
+ * 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.
+ */
+
+package com.android.wm.shell.windowdecor
+
+import android.app.ActivityManager.RunningTaskInfo
+import android.app.WindowConfiguration
+import android.content.Context
+import android.graphics.PixelFormat
+import android.os.Handler
+import android.view.Gravity
+import android.view.View
+import android.view.WindowManager
+import com.android.wm.shell.shared.annotations.ShellMainThread
+import com.android.wm.shell.shared.split.SplitScreenConstants
+import com.android.wm.shell.windowdecor.additionalviewcontainer.AdditionalSystemViewContainer
+
+/**
+ * Supplier for [AdditionalSystemViewContainer] objects to be used for forwarding input
+ * events through status bar to an app handle. Currently supports two simultaneous input layers.
+ *
+ * The supplier will pick one of two input layer view containers to use: one for tasks in
+ * fullscreen or top/left split stage, and one for tasks in right split stage.
+ */
+class DesktopStatusBarInputLayerSupplier(
+ private val context: Context,
+ @ShellMainThread handler: Handler
+) {
+ private val inputLayers: MutableList<AdditionalSystemViewContainer> = mutableListOf()
+
+ init {
+ // Post this as creation of the input layer views is a relatively expensive operation.
+ handler.post {
+ repeat(TOTAL_INPUT_LAYERS) {
+ inputLayers.add(createInputLayer())
+ }
+ }
+ }
+
+ private fun createInputLayer(): AdditionalSystemViewContainer {
+ val lp = WindowManager.LayoutParams(
+ WindowManager.LayoutParams.TYPE_STATUS_BAR_ADDITIONAL,
+ WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE,
+ PixelFormat.TRANSPARENT
+ )
+ lp.title = "Desktop status bar input layer"
+ lp.gravity = Gravity.LEFT or Gravity.TOP
+ lp.setTrustedOverlay()
+
+ // Make this window a spy window to enable it to pilfer pointers from the system-wide
+ // gesture listener that receives events before window. This is to prevent notification
+ // shade gesture when we swipe down to enter desktop.
+ lp.inputFeatures = WindowManager.LayoutParams.INPUT_FEATURE_SPY
+ lp.flags = WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE
+ val view = View(context)
+ view.visibility = View.INVISIBLE
+ return AdditionalSystemViewContainer(
+ WindowManagerWrapper(
+ context.getSystemService<WindowManager>(WindowManager::class.java)
+ ),
+ view,
+ lp
+ )
+ }
+
+ /**
+ * Decide which cached status bar input layer should be used for a decoration, if any.
+ *
+ * [splitPosition] and [isLeftRightSplit] are used to determine which input layer we use.
+ * The first one is reserved for fullscreen tasks or tasks in top/left split,
+ * while the second one is exclusively used for tasks in right split stage. Note we care about
+ * left-right vs top-bottom split as the bottom stage should not use an input layer.
+ */
+ fun getStatusBarInputLayer(
+ taskInfo: RunningTaskInfo,
+ @SplitScreenConstants.SplitPosition splitPosition: Int,
+ isLeftRightSplit: Boolean
+ ): AdditionalSystemViewContainer? {
+ if (!taskInfo.isVisibleRequested) return null
+ // Fullscreen and top/left split tasks will use the first input layer.
+ if (taskInfo.windowingMode == WindowConfiguration.WINDOWING_MODE_FULLSCREEN
+ || splitPosition == SplitScreenConstants.SPLIT_POSITION_TOP_OR_LEFT
+ ) {
+ return inputLayers[LEFT_TOP_INPUT_LAYER]
+ }
+ // Right split tasks will use the second one.
+ if (isLeftRightSplit && splitPosition == SplitScreenConstants.SPLIT_POSITION_BOTTOM_OR_RIGHT
+ ) {
+ return inputLayers[RIGHT_SPLIT_INPUT_LAYER]
+ }
+ // Which leaves bottom split and freeform tasks, which do not need an input layer
+ // as the status bar is not blocking them.
+ return null
+ }
+
+ companion object {
+ private const val TOTAL_INPUT_LAYERS = 2
+ // Input layer index for fullscreen tasks and tasks in top-left split
+ private const val LEFT_TOP_INPUT_LAYER = 0
+ // Input layer index for tasks in right split stage. Does not include bottom split as that
+ // stage is not blocked by status bar.
+ private const val RIGHT_SPLIT_INPUT_LAYER = 1
+ }
+}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/MaximizeMenu.kt b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/MaximizeMenu.kt
index 3ae5a1afc7e2..4bb1e7b6cc05 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/MaximizeMenu.kt
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/MaximizeMenu.kt
@@ -36,7 +36,6 @@ import android.graphics.drawable.StateListDrawable
import android.graphics.drawable.shapes.RoundRectShape
import android.util.StateSet
import android.view.LayoutInflater
-import android.view.MotionEvent
import android.view.MotionEvent.ACTION_HOVER_ENTER
import android.view.MotionEvent.ACTION_HOVER_EXIT
import android.view.MotionEvent.ACTION_HOVER_MOVE
@@ -58,6 +57,8 @@ import android.window.TaskConstants
import androidx.compose.material3.ColorScheme
import androidx.compose.ui.graphics.toArgb
import androidx.core.animation.addListener
+import androidx.core.view.isGone
+import androidx.core.view.isVisible
import com.android.wm.shell.R
import com.android.wm.shell.RootTaskDisplayAreaOrganizer
import com.android.wm.shell.common.DisplayController
@@ -93,7 +94,6 @@ class MaximizeMenu(
private val cornerRadius = loadDimensionPixelSize(
R.dimen.desktop_mode_maximize_menu_corner_radius
).toFloat()
- private val menuWidth = loadDimensionPixelSize(R.dimen.desktop_mode_maximize_menu_width)
private val menuHeight = loadDimensionPixelSize(R.dimen.desktop_mode_maximize_menu_height)
private val menuPadding = loadDimensionPixelSize(R.dimen.desktop_mode_menu_padding)
@@ -105,7 +105,12 @@ class MaximizeMenu(
/** Creates and shows the maximize window. */
fun show(
+ isTaskInImmersiveMode: Boolean,
+ menuWidth: Int,
+ showImmersiveOption: Boolean,
+ showSnapOptions: Boolean,
onMaximizeOrRestoreClickListener: () -> Unit,
+ onImmersiveOrRestoreClickListener: () -> Unit,
onLeftSnapClickListener: () -> Unit,
onRightSnapClickListener: () -> Unit,
onHoverListener: (Boolean) -> Unit,
@@ -113,7 +118,12 @@ class MaximizeMenu(
) {
if (maximizeMenu != null) return
createMaximizeMenu(
+ isTaskInImmersiveMode = isTaskInImmersiveMode,
+ menuWidth = menuWidth,
+ showImmersiveOption = showImmersiveOption,
+ showSnapOptions = showSnapOptions,
onMaximizeClickListener = onMaximizeOrRestoreClickListener,
+ onImmersiveOrRestoreClickListener = onImmersiveOrRestoreClickListener,
onLeftSnapClickListener = onLeftSnapClickListener,
onRightSnapClickListener = onRightSnapClickListener,
onHoverListener = onHoverListener,
@@ -144,7 +154,12 @@ class MaximizeMenu(
/** Create a maximize menu that is attached to the display area. */
private fun createMaximizeMenu(
+ isTaskInImmersiveMode: Boolean,
+ menuWidth: Int,
+ showImmersiveOption: Boolean,
+ showSnapOptions: Boolean,
onMaximizeClickListener: () -> Unit,
+ onImmersiveOrRestoreClickListener: () -> Unit,
onLeftSnapClickListener: () -> Unit,
onRightSnapClickListener: () -> Unit,
onHoverListener: (Boolean) -> Unit,
@@ -179,11 +194,20 @@ class MaximizeMenu(
maximizeMenuView = MaximizeMenuView(
context = decorWindowContext,
sizeToggleDirection = getSizeToggleDirection(),
+ immersiveConfig = if (showImmersiveOption) {
+ MaximizeMenuView.ImmersiveConfig.Visible(
+ getImmersiveToggleDirection(isTaskInImmersiveMode)
+ )
+ } else {
+ MaximizeMenuView.ImmersiveConfig.Hidden
+ },
+ showSnapOptions = showSnapOptions,
menuHeight = menuHeight,
menuPadding = menuPadding,
).also { menuView ->
menuView.bind(taskInfo)
menuView.onMaximizeClickListener = onMaximizeClickListener
+ menuView.onImmersiveOrRestoreClickListener = onImmersiveOrRestoreClickListener
menuView.onLeftSnapClickListener = onLeftSnapClickListener
menuView.onRightSnapClickListener = onRightSnapClickListener
menuView.onMenuHoverListener = onHoverListener
@@ -217,6 +241,15 @@ class MaximizeMenu(
MaximizeMenuView.SizeToggleDirection.MAXIMIZE
}
+ private fun getImmersiveToggleDirection(
+ isTaskImmersive: Boolean
+ ): MaximizeMenuView.ImmersiveToggleDirection =
+ if (isTaskImmersive) {
+ MaximizeMenuView.ImmersiveToggleDirection.EXIT
+ } else {
+ MaximizeMenuView.ImmersiveToggleDirection.ENTER
+ }
+
private fun loadDimensionPixelSize(resourceId: Int): Int {
return if (resourceId == Resources.ID_NULL) {
0
@@ -226,33 +259,14 @@ class MaximizeMenu(
}
/**
- * A valid menu input is one of the following:
- * An input that happens in the menu views.
- * Any input before the views have been laid out.
- *
- * @param inputPoint the input to compare against.
- */
- fun isValidMenuInput(ev: MotionEvent): Boolean {
- val x = ev.rawX
- val y = ev.rawY
- return !viewsLaidOut() || (menuPosition.x <= x && menuPosition.x + menuWidth >= x &&
- menuPosition.y <= y && menuPosition.y + menuHeight >= y)
- }
-
- /**
- * Check if the views for maximize menu can be seen.
- */
- private fun viewsLaidOut(): Boolean {
- return maximizeMenu?.view?.isLaidOut ?: false
- }
-
- /**
* The view within the Maximize Menu, presents maximize, restore and snap-to-side options for
* resizing a Task.
*/
class MaximizeMenuView(
- private val context: Context,
+ context: Context,
private val sizeToggleDirection: SizeToggleDirection,
+ immersiveConfig: ImmersiveConfig,
+ showSnapOptions: Boolean,
private val menuHeight: Int,
private val menuPadding: Int
) {
@@ -260,10 +274,20 @@ class MaximizeMenu(
.inflate(R.layout.desktop_mode_window_decor_maximize_menu, null /* root */) as ViewGroup
private val container = requireViewById(R.id.container)
private val overlay = requireViewById(R.id.maximize_menu_overlay)
+ private val immersiveToggleContainer =
+ requireViewById(R.id.maximize_menu_immersive_toggle_container) as View
+ private val immersiveToggleButtonText =
+ requireViewById(R.id.maximize_menu_immersive_toggle_button_text) as TextView
+ private val immersiveToggleButton =
+ requireViewById(R.id.maximize_menu_immersive_toggle_button) as Button
+ private val sizeToggleContainer =
+ requireViewById(R.id.maximize_menu_size_toggle_container) as View
private val sizeToggleButtonText =
requireViewById(R.id.maximize_menu_size_toggle_button_text) as TextView
private val sizeToggleButton =
requireViewById(R.id.maximize_menu_size_toggle_button) as Button
+ private val snapContainer =
+ requireViewById(R.id.maximize_menu_snap_container) as View
private val snapWindowText =
requireViewById(R.id.maximize_menu_snap_window_text) as TextView
private val snapRightButton =
@@ -282,6 +306,35 @@ class MaximizeMenu(
private val fillRadius = context.resources
.getDimensionPixelSize(R.dimen.desktop_mode_maximize_menu_buttons_fill_radius)
+ private val immersiveFillPadding = context.resources.getDimensionPixelSize(R.dimen
+ .desktop_mode_maximize_menu_immersive_button_fill_padding)
+ private val maximizeFillPaddingDefault = context.resources.getDimensionPixelSize(R.dimen
+ .desktop_mode_maximize_menu_snap_and_maximize_buttons_fill_padding)
+ private val maximizeFillPaddingBottom = context.resources.getDimensionPixelSize(R.dimen
+ .desktop_mode_maximize_menu_snap_and_maximize_buttons_fill_padding_bottom)
+ private val maximizeRestoreFillPaddingVertical = context.resources.getDimensionPixelSize(
+ R.dimen.desktop_mode_maximize_menu_restore_button_fill_vertical_padding)
+ private val maximizeRestoreFillPaddingHorizontal = context.resources.getDimensionPixelSize(
+ R.dimen.desktop_mode_maximize_menu_restore_button_fill_horizontal_padding)
+ private val maximizeFillPaddingRect = Rect(
+ maximizeFillPaddingDefault,
+ maximizeFillPaddingDefault,
+ maximizeFillPaddingDefault,
+ maximizeFillPaddingBottom
+ )
+ private val maximizeRestoreFillPaddingRect = Rect(
+ maximizeRestoreFillPaddingHorizontal,
+ maximizeRestoreFillPaddingVertical,
+ maximizeRestoreFillPaddingHorizontal,
+ maximizeRestoreFillPaddingVertical,
+ )
+ private val immersiveFillPaddingRect = Rect(
+ immersiveFillPadding,
+ immersiveFillPadding,
+ immersiveFillPadding,
+ immersiveFillPadding
+ )
+
private val hoverTempRect = Rect()
private var menuAnimatorSet: AnimatorSet? = null
private lateinit var taskInfo: RunningTaskInfo
@@ -289,6 +342,8 @@ class MaximizeMenu(
/** Invoked when the maximize or restore option is clicked. */
var onMaximizeClickListener: (() -> Unit)? = null
+ /** Invoked when the immersive or restore option is clicked. */
+ var onImmersiveOrRestoreClickListener: (() -> Unit)? = null
/** Invoked when the left snap option is clicked. */
var onLeftSnapClickListener: (() -> Unit)? = null
/** Invoked when the right snap option is clicked. */
@@ -338,6 +393,11 @@ class MaximizeMenu(
return@setOnHoverListener false
}
+ immersiveToggleContainer.isGone = immersiveConfig is ImmersiveConfig.Hidden
+ sizeToggleContainer.isVisible = true
+ snapContainer.isGone = !showSnapOptions
+
+ immersiveToggleButton.setOnClickListener { onImmersiveOrRestoreClickListener?.invoke() }
sizeToggleButton.setOnClickListener { onMaximizeClickListener?.invoke() }
snapRightButton.setOnClickListener { onRightSnapClickListener?.invoke() }
snapLeftButton.setOnClickListener { onLeftSnapClickListener?.invoke() }
@@ -349,17 +409,36 @@ class MaximizeMenu(
true
}
- val btnTextId = if (sizeToggleDirection == SizeToggleDirection.RESTORE)
+ // Maximize/restore button.
+ val sizeToggleBtnTextId = if (sizeToggleDirection == SizeToggleDirection.RESTORE)
R.string.desktop_mode_maximize_menu_restore_button_text
else
R.string.desktop_mode_maximize_menu_maximize_button_text
- val btnText = context.resources.getText(btnTextId)
- sizeToggleButton.contentDescription = btnText
- sizeToggleButtonText.text = btnText
+ val sizeToggleBtnText = context.resources.getText(sizeToggleBtnTextId)
+ sizeToggleButton.contentDescription = sizeToggleBtnText
+ sizeToggleButtonText.text = sizeToggleBtnText
+
+ // Immersive enter/exit button.
+ if (immersiveConfig is ImmersiveConfig.Visible) {
+ val immersiveToggleBtnTextId = when (immersiveConfig.direction) {
+ ImmersiveToggleDirection.ENTER -> {
+ R.string.desktop_mode_maximize_menu_immersive_button_text
+ }
+
+ ImmersiveToggleDirection.EXIT -> {
+ R.string.desktop_mode_maximize_menu_immersive_restore_button_text
+ }
+ }
+ val immersiveToggleBtnText = context.resources.getText(immersiveToggleBtnTextId)
+ immersiveToggleButton.contentDescription = immersiveToggleBtnText
+ immersiveToggleButtonText.text = immersiveToggleBtnText
+ }
// To prevent aliasing.
sizeToggleButton.setLayerType(View.LAYER_TYPE_SOFTWARE, null)
sizeToggleButtonText.setLayerType(View.LAYER_TYPE_SOFTWARE, null)
+ immersiveToggleButton.setLayerType(View.LAYER_TYPE_SOFTWARE, null)
+ immersiveToggleButtonText.setLayerType(View.LAYER_TYPE_SOFTWARE, null)
}
/** Bind the menu views to the new [RunningTaskInfo] data. */
@@ -373,6 +452,10 @@ class MaximizeMenu(
sizeToggleButton.background = style.maximizeOption.drawable
sizeToggleButtonText.setTextColor(style.textColor)
+ // Immersive option.
+ immersiveToggleButton.background = style.immersiveOption.drawable
+ immersiveToggleButtonText.setTextColor(style.textColor)
+
// Snap options.
snapWindowText.setTextColor(style.textColor)
updateSplitSnapSelection(SnapToHalfSelection.NONE)
@@ -382,6 +465,8 @@ class MaximizeMenu(
fun animateOpenMenu(onEnd: () -> Unit) {
sizeToggleButton.setLayerType(View.LAYER_TYPE_HARDWARE, null)
sizeToggleButtonText.setLayerType(View.LAYER_TYPE_HARDWARE, null)
+ immersiveToggleButton.setLayerType(View.LAYER_TYPE_HARDWARE, null)
+ immersiveToggleButtonText.setLayerType(View.LAYER_TYPE_HARDWARE, null)
menuAnimatorSet = AnimatorSet()
menuAnimatorSet?.playTogether(
ObjectAnimator.ofFloat(rootView, SCALE_Y, STARTING_MENU_HEIGHT_SCALE, 1f)
@@ -411,8 +496,10 @@ class MaximizeMenu(
// scale is cancelled out and only the background is scaled.
val value = animatedValue as Float
sizeToggleButton.scaleY = value
+ immersiveToggleButton.scaleY = value
snapButtonsLayout.scaleY = value
sizeToggleButtonText.scaleY = value
+ immersiveToggleButtonText.scaleY = value
snapWindowText.scaleY = value
}
},
@@ -432,8 +519,10 @@ class MaximizeMenu(
addUpdateListener {
val value = animatedValue as Float
sizeToggleButton.alpha = value
+ immersiveToggleButton.alpha = value
snapButtonsLayout.alpha = value
sizeToggleButtonText.alpha = value
+ immersiveToggleButtonText.alpha = value
snapWindowText.alpha = value
}
},
@@ -447,6 +536,8 @@ class MaximizeMenu(
onEnd = {
sizeToggleButton.setLayerType(View.LAYER_TYPE_SOFTWARE, null)
sizeToggleButtonText.setLayerType(View.LAYER_TYPE_SOFTWARE, null)
+ immersiveToggleButton.setLayerType(View.LAYER_TYPE_SOFTWARE, null)
+ immersiveToggleButtonText.setLayerType(View.LAYER_TYPE_SOFTWARE, null)
onEnd.invoke()
}
)
@@ -457,6 +548,8 @@ class MaximizeMenu(
fun animateCloseMenu(onEnd: (() -> Unit)) {
sizeToggleButton.setLayerType(View.LAYER_TYPE_HARDWARE, null)
sizeToggleButtonText.setLayerType(View.LAYER_TYPE_HARDWARE, null)
+ immersiveToggleButton.setLayerType(View.LAYER_TYPE_HARDWARE, null)
+ immersiveToggleButtonText.setLayerType(View.LAYER_TYPE_HARDWARE, null)
cancelAnimation()
menuAnimatorSet = AnimatorSet()
menuAnimatorSet?.playTogether(
@@ -487,8 +580,10 @@ class MaximizeMenu(
// scale is cancelled out and only the background is scaled.
val value = animatedValue as Float
sizeToggleButton.scaleY = value
+ immersiveToggleButton.scaleY = value
snapButtonsLayout.scaleY = value
sizeToggleButtonText.scaleY = value
+ immersiveToggleButtonText.scaleY = value
snapWindowText.scaleY = value
}
},
@@ -508,8 +603,10 @@ class MaximizeMenu(
addUpdateListener {
val value = animatedValue as Float
sizeToggleButton.alpha = value
+ immersiveToggleButton.alpha = value
snapButtonsLayout.alpha = value
sizeToggleButtonText.alpha = value
+ immersiveToggleButtonText.alpha = value
snapWindowText.alpha = value
}
},
@@ -522,6 +619,8 @@ class MaximizeMenu(
onEnd = {
sizeToggleButton.setLayerType(View.LAYER_TYPE_SOFTWARE, null)
sizeToggleButtonText.setLayerType(View.LAYER_TYPE_SOFTWARE, null)
+ immersiveToggleButton.setLayerType(View.LAYER_TYPE_SOFTWARE, null)
+ immersiveToggleButtonText.setLayerType(View.LAYER_TYPE_SOFTWARE, null)
onEnd?.invoke()
}
)
@@ -531,6 +630,14 @@ class MaximizeMenu(
/** Request that the accessibility service focus on the menu. */
fun requestAccessibilityFocus() {
// Focus the first button in the menu by default.
+ if (immersiveToggleButton.isVisible) {
+ immersiveToggleButton.post {
+ immersiveToggleButton.sendAccessibilityEvent(
+ AccessibilityEvent.TYPE_VIEW_FOCUSED
+ )
+ }
+ return
+ }
sizeToggleButton.post {
sizeToggleButton.sendAccessibilityEvent(AccessibilityEvent.TYPE_VIEW_FOCUSED)
}
@@ -557,7 +664,21 @@ class MaximizeMenu(
backgroundColor = menuBackgroundColor,
textColor = colorScheme.onSurface.toArgb(),
maximizeOption = MenuStyle.MaximizeOption(
- drawable = createMaximizeDrawable(menuBackgroundColor, colorScheme)
+ drawable = createMaximizeOrImmersiveDrawable(
+ menuBackgroundColor,
+ colorScheme,
+ fillPadding = when (sizeToggleDirection) {
+ SizeToggleDirection.MAXIMIZE -> maximizeFillPaddingRect
+ SizeToggleDirection.RESTORE -> maximizeRestoreFillPaddingRect
+ }
+ )
+ ),
+ immersiveOption = MenuStyle.ImmersiveOption(
+ drawable = createMaximizeOrImmersiveDrawable(
+ menuBackgroundColor,
+ colorScheme,
+ fillPadding = immersiveFillPaddingRect,
+ ),
),
snapOptions = MenuStyle.SnapOptions(
inactiveSnapSideColor = colorScheme.outlineVariant.toArgb(),
@@ -624,19 +745,21 @@ class MaximizeMenu(
}
}
- private fun createMaximizeDrawable(
+ private fun createMaximizeOrImmersiveDrawable(
@ColorInt menuBackgroundColor: Int,
- colorScheme: ColorScheme
+ colorScheme: ColorScheme,
+ fillPadding: Rect,
): StateListDrawable {
val activeStrokeAndFill = colorScheme.primary.toArgb()
val activeBackground = colorScheme.primary.toArgb().withAlpha(OPACITY_12)
- val activeDrawable = createMaximizeButtonDrawable(
+ val activeDrawable = createMaximizeOrImmersiveButtonDrawable(
strokeAndFillColor = activeStrokeAndFill,
backgroundColor = activeBackground,
// Add a mask with the menu background's color because the active background color is
// semi transparent, otherwise the transparency will reveal the stroke/fill color
// behind it.
- backgroundMask = menuBackgroundColor
+ backgroundMask = menuBackgroundColor,
+ fillPadding = fillPadding,
)
return StateListDrawable().apply {
addState(intArrayOf(android.R.attr.state_pressed), activeDrawable)
@@ -646,19 +769,21 @@ class MaximizeMenu(
// Inactive drawable.
addState(
StateSet.WILD_CARD,
- createMaximizeButtonDrawable(
+ createMaximizeOrImmersiveButtonDrawable(
strokeAndFillColor = colorScheme.outlineVariant.toArgb(),
backgroundColor = colorScheme.surfaceContainerLow.toArgb(),
- backgroundMask = null // not needed because the bg color is fully opaque
+ backgroundMask = null, // not needed because the bg color is fully opaque
+ fillPadding = fillPadding,
)
)
}
}
- private fun createMaximizeButtonDrawable(
+ private fun createMaximizeOrImmersiveButtonDrawable(
@ColorInt strokeAndFillColor: Int,
@ColorInt backgroundColor: Int,
- @ColorInt backgroundMask: Int?
+ @ColorInt backgroundMask: Int?,
+ fillPadding: Rect,
): LayerDrawable {
val layers = mutableListOf<Drawable>()
// First (bottom) layer, effectively the button's border ring once its inner shape is
@@ -708,30 +833,17 @@ class MaximizeMenu(
paint.style = Paint.Style.FILL
})
- val (horizontalFillPadding, verticalFillPadding) =
- if (sizeToggleDirection == SizeToggleDirection.MAXIMIZE) {
- context.resources.getDimensionPixelSize(R.dimen
- .desktop_mode_maximize_menu_snap_and_maximize_buttons_fill_padding) to
- context.resources.getDimensionPixelSize(R.dimen
- .desktop_mode_maximize_menu_snap_and_maximize_buttons_fill_padding)
- } else {
- context.resources.getDimensionPixelSize(R.dimen
- .desktop_mode_maximize_menu_restore_button_fill_horizontal_padding) to
- context.resources.getDimensionPixelSize(R.dimen
- .desktop_mode_maximize_menu_restore_button_fill_vertical_padding)
- }
-
return LayerDrawable(layers.toTypedArray()).apply {
when (numberOfLayers) {
3 -> {
setLayerInset(1, outlineStroke)
- setLayerInset(2, horizontalFillPadding, verticalFillPadding,
- horizontalFillPadding, verticalFillPadding)
+ setLayerInset(2, fillPadding.left, fillPadding.top,
+ fillPadding.right, fillPadding.bottom)
}
4 -> {
setLayerInset(intArrayOf(1, 2), outlineStroke)
- setLayerInset(3, horizontalFillPadding, verticalFillPadding,
- horizontalFillPadding, verticalFillPadding)
+ setLayerInset(3, fillPadding.left, fillPadding.top,
+ fillPadding.right, fillPadding.bottom)
}
else -> error("Unexpected number of layers: $numberOfLayers")
}
@@ -755,11 +867,15 @@ class MaximizeMenu(
@ColorInt val backgroundColor: Int,
@ColorInt val textColor: Int,
val maximizeOption: MaximizeOption,
+ val immersiveOption: ImmersiveOption,
val snapOptions: SnapOptions,
) {
data class MaximizeOption(
val drawable: StateListDrawable,
)
+ data class ImmersiveOption(
+ val drawable: StateListDrawable,
+ )
data class SnapOptions(
@ColorInt val inactiveSnapSideColor: Int,
@ColorInt val semiActiveSnapSideColor: Int,
@@ -776,10 +892,23 @@ class MaximizeMenu(
NONE, LEFT, RIGHT
}
+ /** The possible immersive configs for this menu instance. */
+ sealed class ImmersiveConfig {
+ data class Visible(
+ val direction: ImmersiveToggleDirection,
+ ) : ImmersiveConfig()
+ data object Hidden : ImmersiveConfig()
+ }
+
/** The possible selection states of the size toggle button in the maximize menu. */
enum class SizeToggleDirection {
MAXIMIZE, RESTORE
}
+
+ /** The possible selection states of the immersive toggle button in the maximize menu. */
+ enum class ImmersiveToggleDirection {
+ ENTER, EXIT
+ }
}
companion object {
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/additionalviewcontainer/AdditionalSystemViewContainer.kt b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/additionalviewcontainer/AdditionalSystemViewContainer.kt
index 8b6aaaf619e0..1451f363ec73 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/additionalviewcontainer/AdditionalSystemViewContainer.kt
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/additionalviewcontainer/AdditionalSystemViewContainer.kt
@@ -23,8 +23,8 @@ import android.view.Gravity
import android.view.LayoutInflater
import android.view.SurfaceControl
import android.view.View
-import android.view.WindowInsets
import android.view.WindowManager
+import android.view.WindowManager.LayoutParams
import com.android.wm.shell.windowdecor.WindowManagerWrapper
/**
@@ -33,27 +33,11 @@ import com.android.wm.shell.windowdecor.WindowManagerWrapper
*/
class AdditionalSystemViewContainer(
private val windowManagerWrapper: WindowManagerWrapper,
- taskId: Int,
- x: Int,
- y: Int,
- width: Int,
- height: Int,
- flags: Int,
- @WindowInsets.Type.InsetsType forciblyShownTypes: Int = 0,
- override val view: View
+ override val view: View,
+ val lp: LayoutParams
) : AdditionalViewContainer() {
- val lp: WindowManager.LayoutParams = WindowManager.LayoutParams(
- width, height, x, y,
- WindowManager.LayoutParams.TYPE_STATUS_BAR_ADDITIONAL,
- flags,
- PixelFormat.TRANSPARENT
- ).apply {
- title = "Additional view container of Task=$taskId"
- gravity = Gravity.LEFT or Gravity.TOP
- setTrustedOverlay()
- this.forciblyShownTypes = forciblyShownTypes
- }
+ /** Provide a layout id of a view to inflate for this view container. */
constructor(
context: Context,
windowManagerWrapper: WindowManagerWrapper,
@@ -66,15 +50,30 @@ class AdditionalSystemViewContainer(
@LayoutRes layoutId: Int
) : this(
windowManagerWrapper = windowManagerWrapper,
- taskId = taskId,
- x = x,
- y = y,
- width = width,
- height = height,
- flags = flags,
- view = LayoutInflater.from(context).inflate(layoutId, null /* parent */)
+ view = LayoutInflater.from(context).inflate(layoutId, null /* parent */),
+ lp = createLayoutParams(x, y, width, height, flags, taskId)
)
+ /** Provide a view directly for this view container */
+ constructor(
+ windowManagerWrapper: WindowManagerWrapper,
+ taskId: Int,
+ x: Int,
+ y: Int,
+ width: Int,
+ height: Int,
+ flags: Int,
+ view: View,
+ forciblyShownTypes: Int = 0
+ ) : this(
+ windowManagerWrapper = windowManagerWrapper,
+ view = view,
+ lp = createLayoutParams(x, y, width, height, flags, taskId).apply {
+ this.forciblyShownTypes = forciblyShownTypes
+ }
+ )
+
+ /** Do not supply a view at all, instead creating the view container with a basic view. */
constructor(
context: Context,
windowManagerWrapper: WindowManagerWrapper,
@@ -86,12 +85,7 @@ class AdditionalSystemViewContainer(
flags: Int
) : this(
windowManagerWrapper = windowManagerWrapper,
- taskId = taskId,
- x = x,
- y = y,
- width = width,
- height = height,
- flags = flags,
+ lp = createLayoutParams(x, y, width, height, flags, taskId),
view = View(context)
)
@@ -104,7 +98,7 @@ class AdditionalSystemViewContainer(
}
override fun setPosition(t: SurfaceControl.Transaction, x: Float, y: Float) {
- val lp = (view.layoutParams as WindowManager.LayoutParams).apply {
+ lp.apply {
this.x = x.toInt()
this.y = y.toInt()
}
@@ -124,13 +118,29 @@ class AdditionalSystemViewContainer(
): AdditionalSystemViewContainer =
AdditionalSystemViewContainer(
windowManagerWrapper = windowManagerWrapper,
- taskId = taskId,
- x = x,
- y = y,
- width = width,
- height = height,
- flags = flags,
- view = view
+ view = view,
+ lp = createLayoutParams(x, y, width, height, flags, taskId)
)
}
+ companion object {
+ fun createLayoutParams(
+ x: Int,
+ y: Int,
+ width: Int,
+ height: Int,
+ flags: Int,
+ taskId: Int
+ ): LayoutParams {
+ return LayoutParams(
+ width, height, x, y,
+ LayoutParams.TYPE_STATUS_BAR_ADDITIONAL,
+ flags,
+ PixelFormat.TRANSPARENT
+ ).apply {
+ title = "Additional view container of Task=$taskId"
+ gravity = Gravity.LEFT or Gravity.TOP
+ setTrustedOverlay()
+ }
+ }
+ }
}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/viewholder/AppHandleViewHolder.kt b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/viewholder/AppHandleViewHolder.kt
index b5700ffb046b..b43a9839f042 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/viewholder/AppHandleViewHolder.kt
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/viewholder/AppHandleViewHolder.kt
@@ -36,13 +36,10 @@ import android.view.accessibility.AccessibilityNodeInfo.AccessibilityAction
import android.widget.ImageButton
import androidx.core.view.ViewCompat
import androidx.core.view.accessibility.AccessibilityNodeInfoCompat.AccessibilityActionCompat
-import com.android.internal.policy.SystemBarUtils
-import com.android.window.flags.Flags
import com.android.wm.shell.R
import com.android.wm.shell.shared.animation.Interpolators
import com.android.wm.shell.windowdecor.WindowManagerWrapper
import com.android.wm.shell.windowdecor.additionalviewcontainer.AdditionalSystemViewContainer
-import com.android.wm.shell.windowdecor.viewholder.WindowDecorationViewHolder.Data
/**
* A desktop mode window decoration used when the window is in full "focus" (i.e. fullscreen/split).
@@ -69,10 +66,12 @@ internal class AppHandleViewHolder(
) : Data()
private lateinit var taskInfo: RunningTaskInfo
+ private val position: Point = Point()
+ private var width: Int = 0
+ private var height: Int = 0
private val captionView: View = rootView.requireViewById(R.id.desktop_mode_caption)
private val captionHandle: ImageButton = rootView.requireViewById(R.id.caption_handle)
private val inputManager = context.getSystemService(InputManager::class.java)
- private var statusBarInputLayerExists = false
// An invisible View that takes up the same coordinates as captionHandle but is layered
// above the status bar. The purpose of this View is to receive input intended for
@@ -112,21 +111,54 @@ internal class AppHandleViewHolder(
) {
captionHandle.imageTintList = ColorStateList.valueOf(getCaptionHandleBarColor(taskInfo))
this.taskInfo = taskInfo
- // If handle is not in status bar region(i.e., bottom stage in vertical split),
- // do not create an input layer
- if (position.y >= SystemBarUtils.getStatusBarHeight(context)) return
- if (!isCaptionVisible && statusBarInputLayerExists) {
- disposeStatusBarInputLayer()
+ this.position.set(position)
+ this.width = width
+ this.height = height
+ if (!isCaptionVisible && statusBarInputLayer != null) {
+ detachStatusBarInputLayer()
return
}
- // Input layer view creation / modification takes a significant amount of time;
+ }
+
+ fun bindStatusBarInputLayer(
+ statusBarLayer: AdditionalSystemViewContainer
+ ) {
+ // Input layer view modification takes a significant amount of time;
// post them so we don't hold up DesktopModeWindowDecoration#relayout.
- if (statusBarInputLayerExists) {
+ if (statusBarLayer == statusBarInputLayer) {
handler.post { updateStatusBarInputLayer(position) }
- } else {
- // Input layer is created on a delay; prevent multiple from being created.
- statusBarInputLayerExists = true
- handler.post { createStatusBarInputLayer(position, width, height) }
+ return
+ }
+ // Remove the old input layer when changing to a new one.
+ if (statusBarInputLayer != null) detachStatusBarInputLayer()
+ if (statusBarLayer.view.visibility == View.INVISIBLE) {
+ statusBarLayer.view.visibility = View.VISIBLE
+ }
+ statusBarInputLayer = statusBarLayer
+ statusBarInputLayer?.let {
+ inputLayer -> setupAppHandleA11y(inputLayer.view)
+ }
+ handler.post {
+ val view = statusBarInputLayer?.view
+ ?: error("Unable to find statusBarInputLayer View")
+ // Caption handle is located within the status bar region, meaning the
+ // DisplayPolicy will attempt to transfer this input to status bar if it's
+ // a swipe down. Pilfer here to keep the gesture in handle alone.
+ view.setOnTouchListener { v, event ->
+ if (event.actionMasked == ACTION_DOWN) {
+ inputManager.pilferPointers(v.viewRootImpl.inputToken)
+ }
+ captionHandle.dispatchTouchEvent(event)
+ return@setOnTouchListener true
+ }
+ view.setOnHoverListener { _, event ->
+ captionHandle.onHoverEvent(event)
+ }
+ val lp = statusBarInputLayer?.view?.layoutParams as WindowManager.LayoutParams
+ lp.x = position.x
+ lp.y = position.y
+ lp.width = width
+ lp.height = height
}
}
@@ -138,40 +170,6 @@ internal class AppHandleViewHolder(
animateCaptionHandleAlpha(startValue = 0f, endValue = 1f)
}
- private fun createStatusBarInputLayer(handlePosition: Point,
- handleWidth: Int,
- handleHeight: Int) {
- if (!Flags.enableHandleInputFix()) return
- statusBarInputLayer = AdditionalSystemViewContainer(context, windowManagerWrapper,
- taskInfo.taskId, handlePosition.x, handlePosition.y, handleWidth, handleHeight,
- WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE
- )
- val view = statusBarInputLayer?.view ?: error("Unable to find statusBarInputLayer View")
- val lp = statusBarInputLayer?.lp ?: error("Unable to find statusBarInputLayer " +
- "LayoutParams")
- lp.title = "Handle Input Layer of task " + taskInfo.taskId
- lp.setTrustedOverlay()
- // Make this window a spy window to enable it to pilfer pointers from the system-wide
- // gesture listener that receives events before window. This is to prevent notification
- // shade gesture when we swipe down to enter desktop.
- lp.inputFeatures = WindowManager.LayoutParams.INPUT_FEATURE_SPY
- view.setOnHoverListener { _, event ->
- captionHandle.onHoverEvent(event)
- }
- // Caption handle is located within the status bar region, meaning the
- // DisplayPolicy will attempt to transfer this input to status bar if it's
- // a swipe down. Pilfer here to keep the gesture in handle alone.
- view.setOnTouchListener { v, event ->
- if (event.actionMasked == ACTION_DOWN) {
- inputManager.pilferPointers(v.viewRootImpl.inputToken)
- }
- captionHandle.dispatchTouchEvent(event)
- return@setOnTouchListener true
- }
- setupAppHandleA11y(view)
- windowManagerWrapper.updateViewLayout(view, lp)
- }
-
private fun setupAppHandleA11y(view: View) {
view.accessibilityDelegate = object : View.AccessibilityDelegate() {
override fun onInitializeAccessibilityNodeInfo(
@@ -224,15 +222,12 @@ internal class AppHandleViewHolder(
}
/**
- * Remove the input layer from [WindowManager]. Should be used when caption handle
- * is not visible.
+ * Remove the input listeners from the input layer and remove it from this view holder.
*/
- fun disposeStatusBarInputLayer() {
- statusBarInputLayerExists = false
- handler.post {
- statusBarInputLayer?.releaseView()
- statusBarInputLayer = null
- }
+ fun detachStatusBarInputLayer() {
+ statusBarInputLayer?.view?.setOnTouchListener(null)
+ statusBarInputLayer?.view?.setOnHoverListener(null)
+ statusBarInputLayer = null
}
private fun getCaptionHandleBarColor(taskInfo: RunningTaskInfo): Int {
diff --git a/libs/WindowManager/Shell/tests/unittest/Android.bp b/libs/WindowManager/Shell/tests/unittest/Android.bp
index 049a5a0615e0..91be5f58b1f7 100644
--- a/libs/WindowManager/Shell/tests/unittest/Android.bp
+++ b/libs/WindowManager/Shell/tests/unittest/Android.bp
@@ -63,6 +63,7 @@ android_test {
"com.android.window.flags.window-aconfig-java",
"platform-test-annotations",
"flag-junit",
+ "platform-parametric-runner-lib",
],
libs: [
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/ShellTaskOrganizerTests.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/ShellTaskOrganizerTests.java
index f01ed84adc74..841b6cefcafa 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/ShellTaskOrganizerTests.java
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/ShellTaskOrganizerTests.java
@@ -45,6 +45,8 @@ import android.app.ActivityManager.RunningTaskInfo;
import android.app.TaskInfo;
import android.content.LocusId;
import android.content.pm.ParceledListSlice;
+import android.graphics.Point;
+import android.graphics.Rect;
import android.os.Binder;
import android.os.IBinder;
import android.os.RemoteException;
@@ -589,9 +591,9 @@ public class ShellTaskOrganizerTests extends ShellTestCase {
@Test
public void testRecentTasks_visibilityChanges_shouldNotifyTaskController() {
- RunningTaskInfo task1 = createTaskInfo(/* taskId= */ 1, WINDOWING_MODE_FREEFORM);
+ RunningTaskInfo task1 = createFreeformTaskInfo(/* taskId= */ 1);
mOrganizer.onTaskAppeared(task1, /* leash= */ null);
- RunningTaskInfo task2 = createTaskInfo(/* taskId= */ 1, WINDOWING_MODE_FREEFORM);
+ RunningTaskInfo task2 = createFreeformTaskInfo(/* taskId= */ 1);
task2.isVisible = false;
mOrganizer.onTaskInfoChanged(task2);
@@ -600,6 +602,30 @@ public class ShellTaskOrganizerTests extends ShellTestCase {
}
@Test
+ public void testRecentTasks_sizeChanges_shouldNotifyTaskController() {
+ RunningTaskInfo task1 = createFreeformTaskInfo(/* taskId= */ 1);
+ mOrganizer.onTaskAppeared(task1, /* leash= */ null);
+ RunningTaskInfo task2 = createFreeformTaskInfo(/* taskId= */ 1);
+ task2.configuration.windowConfiguration.setAppBounds(new Rect(0, 0, 300, 400));
+
+ mOrganizer.onTaskInfoChanged(task2);
+
+ verify(mRecentTasksController).onTaskRunningInfoChanged(task2);
+ }
+
+ @Test
+ public void testRecentTasks_positionChanges_shouldNotifyTaskController() {
+ RunningTaskInfo task1 = createFreeformTaskInfo(/* taskId= */ 1);
+ mOrganizer.onTaskAppeared(task1, /* leash= */ null);
+ RunningTaskInfo task2 = createFreeformTaskInfo(/* taskId= */ 1);
+ task2.positionInParent = new Point(200, 200);
+
+ mOrganizer.onTaskInfoChanged(task2);
+
+ verify(mRecentTasksController).onTaskRunningInfoChanged(task2);
+ }
+
+ @Test
public void testRecentTasks_visibilityChanges_notFreeForm_shouldNotNotifyTaskController() {
RunningTaskInfo task1_visible = createTaskInfo(/* taskId= */ 1, WINDOWING_MODE_FULLSCREEN);
mOrganizer.onTaskAppeared(task1_visible, /* leash= */ null);
@@ -649,6 +675,13 @@ public class ShellTaskOrganizerTests extends ShellTestCase {
return taskInfo;
}
+ private static RunningTaskInfo createFreeformTaskInfo(int taskId) {
+ RunningTaskInfo taskInfo = createTaskInfo(taskId, WINDOWING_MODE_FREEFORM);
+ taskInfo.positionInParent = new Point(100, 100);
+ taskInfo.configuration.windowConfiguration.setAppBounds(new Rect(0, 0, 200, 200));
+ return taskInfo;
+ }
+
private void verifyOnCompatInfoChangedInvokedWith(TaskInfo taskInfo,
ShellTaskOrganizer.TaskListener listener) {
final ArgumentCaptor<CompatUIInfo> capture = ArgumentCaptor.forClass(CompatUIInfo.class);
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/back/BackAnimationControllerTest.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/back/BackAnimationControllerTest.java
index 6b62adb34033..72d4dc6ffac9 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/back/BackAnimationControllerTest.java
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/back/BackAnimationControllerTest.java
@@ -51,6 +51,7 @@ import android.content.pm.ApplicationInfo;
import android.graphics.Point;
import android.graphics.Rect;
import android.hardware.input.InputManager;
+import android.os.Binder;
import android.os.Bundle;
import android.os.Handler;
import android.os.IBinder;
@@ -812,7 +813,10 @@ public class BackAnimationControllerTest extends ShellTestCase {
if (taskId != INVALID_TASK_ID) {
final ActivityManager.RunningTaskInfo taskInfo = new ActivityManager.RunningTaskInfo();
taskInfo.taskId = taskId;
- taskInfo.token = new WindowContainerToken(mock(IWindowContainerToken.class));
+ final IWindowContainerToken mockT = mock(IWindowContainerToken.class);
+ Binder binder = new Binder();
+ doReturn(binder).when(mockT).asBinder();
+ taskInfo.token = new WindowContainerToken(mockT);
change = new TransitionInfo.Change(
taskInfo.token, b.build());
change.setTaskInfo(taskInfo);
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 fa878d0bb077..4d7e47fa51bd 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
@@ -329,7 +329,7 @@ class DesktopTasksLimiterTest : ShellTestCase() {
wct = wct,
newFrontTaskId = setUpFreeformTask().taskId)
- assertThat(minimizedTaskId).isEqualTo(tasks.first())
+ assertThat(minimizedTaskId).isEqualTo(tasks.first().taskId)
assertThat(wct.hierarchyOps.size).isEqualTo(1)
assertThat(wct.hierarchyOps[0].type).isEqualTo(HIERARCHY_OP_TYPE_REORDER)
assertThat(wct.hierarchyOps[0].toTop).isFalse() // Reorder to bottom
@@ -355,7 +355,7 @@ class DesktopTasksLimiterTest : ShellTestCase() {
fun getTaskToMinimize_tasksWithinLimit_returnsNull() {
val tasks = (1..MAX_TASK_LIMIT).map { setUpFreeformTask() }
- val minimizedTask = desktopTasksLimiter.getTaskToMinimize(
+ val minimizedTask = desktopTasksLimiter.getTaskIdToMinimize(
visibleOrderedTasks = tasks.map { it.taskId })
assertThat(minimizedTask).isNull()
@@ -365,11 +365,11 @@ class DesktopTasksLimiterTest : ShellTestCase() {
fun getTaskToMinimize_tasksAboveLimit_returnsBackTask() {
val tasks = (1..MAX_TASK_LIMIT + 1).map { setUpFreeformTask() }
- val minimizedTask = desktopTasksLimiter.getTaskToMinimize(
+ val minimizedTask = desktopTasksLimiter.getTaskIdToMinimize(
visibleOrderedTasks = tasks.map { it.taskId })
// first == front, last == back
- assertThat(minimizedTask).isEqualTo(tasks.last())
+ assertThat(minimizedTask).isEqualTo(tasks.last().taskId)
}
@Test
@@ -379,23 +379,23 @@ class DesktopTasksLimiterTest : ShellTestCase() {
interactionJankMonitor, mContext, handler)
val tasks = (1..MAX_TASK_LIMIT2 + 1).map { setUpFreeformTask() }
- val minimizedTask = desktopTasksLimiter.getTaskToMinimize(
+ val minimizedTask = desktopTasksLimiter.getTaskIdToMinimize(
visibleOrderedTasks = tasks.map { it.taskId })
// first == front, last == back
- assertThat(minimizedTask).isEqualTo(tasks.last())
+ assertThat(minimizedTask).isEqualTo(tasks.last().taskId)
}
@Test
fun getTaskToMinimize_withNewTask_tasksAboveLimit_returnsBackTask() {
val tasks = (1..MAX_TASK_LIMIT).map { setUpFreeformTask() }
- val minimizedTask = desktopTasksLimiter.getTaskToMinimize(
+ val minimizedTask = desktopTasksLimiter.getTaskIdToMinimize(
visibleOrderedTasks = tasks.map { it.taskId },
newTaskIdInFront = setUpFreeformTask().taskId)
// first == front, last == back
- assertThat(minimizedTask).isEqualTo(tasks.last())
+ assertThat(minimizedTask).isEqualTo(tasks.last().taskId)
}
@Test
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip/phone/PipResizeGestureHandlerTest.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip/phone/PipResizeGestureHandlerTest.java
index 66f8c0b9558d..880ca2ce0cf9 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip/phone/PipResizeGestureHandlerTest.java
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip/phone/PipResizeGestureHandlerTest.java
@@ -124,7 +124,7 @@ public class PipResizeGestureHandlerTest extends ShellTestCase {
mPipResizeGestureHandler = new PipResizeGestureHandler(mContext, pipBoundsAlgorithm,
mPipBoundsState, motionHelper, mPipTouchState, mPipTaskOrganizer,
mPipDismissTargetHandler,
- () -> {}, mPipUiEventLogger, mPhonePipMenuController,
+ (Rect bounds) -> new Rect(), () -> {}, mPipUiEventLogger, mPhonePipMenuController,
mMainExecutor, null /* pipPerfHintController */) {
@Override
public void pilferPointers() {
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/taskview/TaskViewTransitionsTest.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/taskview/TaskViewTransitionsTest.java
index d3e40f21db23..60e203009e8c 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/taskview/TaskViewTransitionsTest.java
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/taskview/TaskViewTransitionsTest.java
@@ -33,7 +33,8 @@ import static org.mockito.Mockito.when;
import android.app.ActivityManager;
import android.graphics.Rect;
import android.os.IBinder;
-import android.testing.AndroidTestingRunner;
+import android.platform.test.flag.junit.FlagsParameterization;
+import android.platform.test.flag.junit.SetFlagsRule;
import android.testing.TestableLooper;
import android.view.SurfaceControl;
import android.window.TransitionInfo;
@@ -42,10 +43,12 @@ import android.window.WindowContainerTransaction;
import androidx.test.filters.SmallTest;
+import com.android.wm.shell.Flags;
import com.android.wm.shell.ShellTestCase;
import com.android.wm.shell.transition.Transitions;
import org.junit.Before;
+import org.junit.Rule;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.Mock;
@@ -54,11 +57,23 @@ import org.mockito.MockitoAnnotations;
import java.util.ArrayList;
import java.util.List;
+import platform.test.runner.parameterized.ParameterizedAndroidJunit4;
+import platform.test.runner.parameterized.Parameters;
+
@SmallTest
-@RunWith(AndroidTestingRunner.class)
+@RunWith(ParameterizedAndroidJunit4.class)
@TestableLooper.RunWithLooper(setAsMainLooper = true)
public class TaskViewTransitionsTest extends ShellTestCase {
+ @Parameters(name = "{0}")
+ public static List<FlagsParameterization> getParams() {
+ return FlagsParameterization.allCombinationsOf(
+ Flags.FLAG_ENABLE_TASK_VIEW_CONTROLLER_CLEANUP);
+ }
+
+ @Rule
+ public final SetFlagsRule mSetFlagsRule;
+
@Mock
Transitions mTransitions;
@Mock
@@ -70,6 +85,10 @@ public class TaskViewTransitionsTest extends ShellTestCase {
TaskViewTransitions mTaskViewTransitions;
+ public TaskViewTransitionsTest(FlagsParameterization flags) {
+ mSetFlagsRule = new SetFlagsRule(flags);
+ }
+
@Before
public void setUp() {
MockitoAnnotations.initMocks(this);
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/DesktopModeWindowDecorViewModelTests.kt b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/DesktopModeWindowDecorViewModelTests.kt
index 1839b8a367fe..c5526fc07280 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/DesktopModeWindowDecorViewModelTests.kt
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/DesktopModeWindowDecorViewModelTests.kt
@@ -127,6 +127,7 @@ import org.mockito.Mockito.anyInt
import org.mockito.Mockito.mock
import org.mockito.Mockito.never
import org.mockito.Mockito.times
+import org.mockito.kotlin.KArgumentCaptor
import org.mockito.kotlin.verify
import org.mockito.kotlin.any
import org.mockito.kotlin.argThat
@@ -937,7 +938,7 @@ class DesktopModeWindowDecorViewModelTests : ShellTestCase() {
}
@Test
- fun testDecor_onClickToSplitScreen_disposesStatusBarInputLayer() {
+ fun testDecor_onClickToSplitScreen_detachesStatusBarInputLayer() {
val toSplitScreenListenerCaptor = forClass(Function0::class.java)
as ArgumentCaptor<Function0<Unit>>
val decor = createOpenTaskDecoration(
@@ -947,7 +948,7 @@ class DesktopModeWindowDecorViewModelTests : ShellTestCase() {
toSplitScreenListenerCaptor.value.invoke()
- verify(decor).disposeStatusBarInputLayer()
+ verify(decor).detachStatusBarInputLayer()
}
@Test
@@ -1278,12 +1279,45 @@ class DesktopModeWindowDecorViewModelTests : ShellTestCase() {
.toggleDesktopTaskSize(decor.mTaskInfo, ResizeTrigger.MAXIMIZE_BUTTON, null)
}
+ @Test
+ @EnableFlags(Flags.FLAG_ENABLE_FULLY_IMMERSIVE_IN_DESKTOP)
+ fun testImmersiveClick_togglesImmersiveState() {
+ val onImmersiveClickCaptor = argumentCaptor<() -> Unit>()
+ val decor = createOpenTaskDecoration(
+ windowingMode = WINDOWING_MODE_FREEFORM,
+ onImmersiveOrRestoreListenerCaptor = onImmersiveClickCaptor,
+ requestingImmersive = true,
+ )
+
+ onImmersiveClickCaptor.firstValue()
+
+ verify(mockDesktopTasksController)
+ .toggleDesktopTaskFullImmersiveState(decor.mTaskInfo)
+ }
+
+ @Test
+ @EnableFlags(Flags.FLAG_ENABLE_FULLY_IMMERSIVE_IN_DESKTOP)
+ fun testImmersiveClick_closesMaximizeMenu() {
+ val onImmersiveClickCaptor = argumentCaptor<() -> Unit>()
+ val decor = createOpenTaskDecoration(
+ windowingMode = WINDOWING_MODE_FREEFORM,
+ onImmersiveOrRestoreListenerCaptor = onImmersiveClickCaptor,
+ requestingImmersive = true,
+ )
+
+ onImmersiveClickCaptor.firstValue()
+
+ verify(decor).closeMaximizeMenu()
+ }
+
private fun createOpenTaskDecoration(
@WindowingMode windowingMode: Int,
taskSurface: SurfaceControl = SurfaceControl(),
requestingImmersive: Boolean = false,
onMaxOrRestoreListenerCaptor: ArgumentCaptor<Function0<Unit>> =
forClass(Function0::class.java) as ArgumentCaptor<Function0<Unit>>,
+ onImmersiveOrRestoreListenerCaptor: KArgumentCaptor<() -> Unit> =
+ argumentCaptor<() -> Unit>(),
onLeftSnapClickListenerCaptor: ArgumentCaptor<Function0<Unit>> =
forClass(Function0::class.java) as ArgumentCaptor<Function0<Unit>>,
onRightSnapClickListenerCaptor: ArgumentCaptor<Function0<Unit>> =
@@ -1307,6 +1341,8 @@ class DesktopModeWindowDecorViewModelTests : ShellTestCase() {
))
onTaskOpening(decor.mTaskInfo, taskSurface)
verify(decor).setOnMaximizeOrRestoreClickListener(onMaxOrRestoreListenerCaptor.capture())
+ verify(decor)
+ .setOnImmersiveOrRestoreClickListener(onImmersiveOrRestoreListenerCaptor.capture())
verify(decor).setOnLeftSnapClickListener(onLeftSnapClickListenerCaptor.capture())
verify(decor).setOnRightSnapClickListener(onRightSnapClickListenerCaptor.capture())
verify(decor).setOnToDesktopClickListener(onToDesktopClickListenerCaptor.capture())
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/DesktopModeWindowDecorationTests.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/DesktopModeWindowDecorationTests.java
index 0afb6c10b549..8a2c7782906d 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/DesktopModeWindowDecorationTests.java
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/DesktopModeWindowDecorationTests.java
@@ -49,7 +49,6 @@ import static org.mockito.Mockito.never;
import static org.mockito.Mockito.spy;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
-import static org.mockito.kotlin.VerificationKt.times;
import android.app.ActivityManager;
import android.app.assist.AssistContent;
@@ -850,8 +849,7 @@ public class DesktopModeWindowDecorationTests extends ShellTestCase {
ArgumentCaptor<Runnable> runnableArgument = ArgumentCaptor.forClass(Runnable.class);
spyWindowDecor.relayout(taskInfo, true /* hasGlobalFocus */);
- // Once for view host, the other for the AppHandle input layer.
- verify(mMockHandler, times(2)).post(runnableArgument.capture());
+ verify(mMockHandler).post(runnableArgument.capture());
runnableArgument.getValue().run();
verify(mMockSurfaceControlViewHostFactory).create(any(), any(), any());
}
@@ -878,8 +876,7 @@ public class DesktopModeWindowDecorationTests extends ShellTestCase {
taskInfo.configuration.windowConfiguration.setWindowingMode(WINDOWING_MODE_FULLSCREEN);
ArgumentCaptor<Runnable> runnableArgument = ArgumentCaptor.forClass(Runnable.class);
spyWindowDecor.relayout(taskInfo, true /* hasGlobalFocus */);
- // Once for view host, the other for the AppHandle input layer.
- verify(mMockHandler, times(2)).post(runnableArgument.capture());
+ verify(mMockHandler).post(runnableArgument.capture());
spyWindowDecor.relayout(taskInfo, true /* hasGlobalFocus */);
@@ -893,8 +890,7 @@ public class DesktopModeWindowDecorationTests extends ShellTestCase {
taskInfo.configuration.windowConfiguration.setWindowingMode(WINDOWING_MODE_FULLSCREEN);
ArgumentCaptor<Runnable> runnableArgument = ArgumentCaptor.forClass(Runnable.class);
spyWindowDecor.relayout(taskInfo, true /* hasGlobalFocus */);
- // Once for view host, the other for the AppHandle input layer.
- verify(mMockHandler, times(2)).post(runnableArgument.capture());
+ verify(mMockHandler).post(runnableArgument.capture());
spyWindowDecor.close();
@@ -909,8 +905,10 @@ public class DesktopModeWindowDecorationTests extends ShellTestCase {
new FakeMaximizeMenuFactory(menu));
assertFalse(decoration.isMaximizeMenuActive());
- createMaximizeMenu(decoration, menu);
+ createMaximizeMenu(decoration);
+ verify(menu).show(anyBoolean(), anyInt(), anyBoolean(), anyBoolean(), any(), any(), any(),
+ any(), mOnMaxMenuHoverChangeListener.capture(), any());
assertTrue(decoration.isMaximizeMenuActive());
}
@@ -921,7 +919,9 @@ public class DesktopModeWindowDecorationTests extends ShellTestCase {
final DesktopModeWindowDecoration decoration = createWindowDecoration(taskInfo,
new FakeMaximizeMenuFactory(menu));
decoration.setAppHeaderMaximizeButtonHovered(false);
- createMaximizeMenu(decoration, menu);
+ createMaximizeMenu(decoration);
+ verify(menu).show(anyBoolean(), anyInt(), anyBoolean(), anyBoolean(), any(), any(), any(),
+ any(), mOnMaxMenuHoverChangeListener.capture(), any());
mOnMaxMenuHoverChangeListener.getValue().invoke(false);
@@ -940,7 +940,7 @@ public class DesktopModeWindowDecorationTests extends ShellTestCase {
final DesktopModeWindowDecoration decoration = createWindowDecoration(taskInfo,
new FakeMaximizeMenuFactory(menu));
decoration.setAppHeaderMaximizeButtonHovered(true);
- createMaximizeMenu(decoration, menu);
+ createMaximizeMenu(decoration);
decoration.setAppHeaderMaximizeButtonHovered(false);
@@ -958,7 +958,9 @@ public class DesktopModeWindowDecorationTests extends ShellTestCase {
final MaximizeMenu menu = mock(MaximizeMenu.class);
final DesktopModeWindowDecoration decoration = createWindowDecoration(taskInfo,
new FakeMaximizeMenuFactory(menu));
- createMaximizeMenu(decoration, menu);
+ createMaximizeMenu(decoration);
+ verify(menu).show(anyBoolean(), anyInt(), anyBoolean(), anyBoolean(), any(), any(), any(),
+ any(), mOnMaxMenuHoverChangeListener.capture(), any());
mOnMaxMenuHoverChangeListener.getValue().invoke(true);
@@ -971,13 +973,114 @@ public class DesktopModeWindowDecorationTests extends ShellTestCase {
final MaximizeMenu menu = mock(MaximizeMenu.class);
final DesktopModeWindowDecoration decoration = createWindowDecoration(taskInfo,
new FakeMaximizeMenuFactory(menu));
- createMaximizeMenu(decoration, menu);
+ createMaximizeMenu(decoration);
+ verify(menu).show(anyBoolean(), anyInt(), anyBoolean(), anyBoolean(), any(), any(), any(),
+ any(), mOnMaxMenuHoverChangeListener.capture(), any());
decoration.setAppHeaderMaximizeButtonHovered(true);
verify(mMockHandler).removeCallbacks(any());
}
+ @Test
+ @EnableFlags(Flags.FLAG_ENABLE_FULLY_IMMERSIVE_IN_DESKTOP)
+ public void createMaximizeMenu_taskRequestsImmersive_showsImmersiveOption() {
+ final ActivityManager.RunningTaskInfo taskInfo = createTaskInfo(true /* visible */);
+ taskInfo.requestedVisibleTypes = ~statusBars();
+ final MaximizeMenu menu = mock(MaximizeMenu.class);
+ final DesktopModeWindowDecoration decoration = createWindowDecoration(taskInfo,
+ new FakeMaximizeMenuFactory(menu));
+
+ createMaximizeMenu(decoration);
+
+ verify(menu).show(
+ anyBoolean(),
+ anyInt(),
+ /* showImmersiveOption= */ eq(true),
+ anyBoolean(),
+ any(),
+ any(),
+ any(),
+ any(),
+ any(),
+ any()
+ );
+ }
+
+ @Test
+ @EnableFlags(Flags.FLAG_ENABLE_FULLY_IMMERSIVE_IN_DESKTOP)
+ public void createMaximizeMenu_taskDoesNotRequestImmersive_hiddenImmersiveOption() {
+ final ActivityManager.RunningTaskInfo taskInfo = createTaskInfo(true /* visible */);
+ taskInfo.requestedVisibleTypes = statusBars();
+ final MaximizeMenu menu = mock(MaximizeMenu.class);
+ final DesktopModeWindowDecoration decoration = createWindowDecoration(taskInfo,
+ new FakeMaximizeMenuFactory(menu));
+
+ createMaximizeMenu(decoration);
+
+ verify(menu).show(
+ anyBoolean(),
+ anyInt(),
+ /* showImmersiveOption= */ eq(false),
+ anyBoolean(),
+ any(),
+ any(),
+ any(),
+ any(),
+ any(),
+ any()
+ );
+ }
+
+ @Test
+ @EnableFlags(Flags.FLAG_ENABLE_FULLY_IMMERSIVE_IN_DESKTOP)
+ public void createMaximizeMenu_taskResizable_showsSnapOptions() {
+ final ActivityManager.RunningTaskInfo taskInfo = createTaskInfo(true /* visible */);
+ taskInfo.isResizeable = true;
+ final MaximizeMenu menu = mock(MaximizeMenu.class);
+ final DesktopModeWindowDecoration decoration = createWindowDecoration(taskInfo,
+ new FakeMaximizeMenuFactory(menu));
+
+ createMaximizeMenu(decoration);
+
+ verify(menu).show(
+ anyBoolean(),
+ anyInt(),
+ anyBoolean(),
+ /* showSnapOptions= */ eq(true),
+ any(),
+ any(),
+ any(),
+ any(),
+ any(),
+ any()
+ );
+ }
+
+ @Test
+ @EnableFlags(Flags.FLAG_ENABLE_FULLY_IMMERSIVE_IN_DESKTOP)
+ public void createMaximizeMenu_taskUnresizable_hiddenSnapOptions() {
+ final ActivityManager.RunningTaskInfo taskInfo = createTaskInfo(true /* visible */);
+ taskInfo.isResizeable = false;
+ final MaximizeMenu menu = mock(MaximizeMenu.class);
+ final DesktopModeWindowDecoration decoration = createWindowDecoration(taskInfo,
+ new FakeMaximizeMenuFactory(menu));
+
+ createMaximizeMenu(decoration);
+
+ verify(menu).show(
+ anyBoolean(),
+ anyInt(),
+ anyBoolean(),
+ /* showSnapOptions= */ eq(false),
+ any(),
+ any(),
+ any(),
+ any(),
+ any(),
+ any()
+ );
+ }
@Test
@EnableFlags(Flags.FLAG_ENABLE_DESKTOP_WINDOWING_APP_TO_WEB)
@@ -1335,13 +1438,13 @@ public class DesktopModeWindowDecorationTests extends ShellTestCase {
anyInt(), anyInt(), anyInt(), anyInt());
}
- private void createMaximizeMenu(DesktopModeWindowDecoration decoration, MaximizeMenu menu) {
+ private void createMaximizeMenu(DesktopModeWindowDecoration decoration) {
final Function0<Unit> l = () -> Unit.INSTANCE;
decoration.setOnMaximizeOrRestoreClickListener(l);
+ decoration.setOnImmersiveOrRestoreClickListener(l);
decoration.setOnLeftSnapClickListener(l);
decoration.setOnRightSnapClickListener(l);
decoration.createMaximizeMenu();
- verify(menu).show(any(), any(), any(), mOnMaxMenuHoverChangeListener.capture(), any());
}
private void fillRoundedCornersResources(int fillValue) {
diff --git a/lint-baseline.xml b/lint-baseline.xml
index 0320aabd7199..8253c1f8f0b3 100644
--- a/lint-baseline.xml
+++ b/lint-baseline.xml
@@ -11265,4 +11265,15 @@
column="24"/>
</issue>
-</issues> \ No newline at end of file
+ <issue
+ id="SimpleManualPermissionEnforcement"
+ message="INetworkScoreCache permission check should be converted to @EnforcePermission annotation"
+ errorLine1=" mContext.enforceCallingOrSelfPermission(permission.DUMP, TAG);"
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="frameworks/base/wifi/java/src/android/net/wifi/WifiNetworkScoreCache.java"
+ line="250"
+ column="9"/>
+ </issue>
+
+</issues>
diff --git a/media/java/android/media/AudioDeviceInfo.java b/media/java/android/media/AudioDeviceInfo.java
index 0efefb9d1bff..39b29d0156d2 100644
--- a/media/java/android/media/AudioDeviceInfo.java
+++ b/media/java/android/media/AudioDeviceInfo.java
@@ -279,6 +279,7 @@ public final class AudioDeviceInfo {
TYPE_AUX_LINE,
TYPE_IP,
TYPE_BUS,
+ TYPE_REMOTE_SUBMIX,
TYPE_HEARING_AID,
TYPE_BUILTIN_SPEAKER_SAFE,
TYPE_BLE_HEADSET,
@@ -312,6 +313,7 @@ public final class AudioDeviceInfo {
case TYPE_AUX_LINE:
case TYPE_IP:
case TYPE_BUS:
+ case TYPE_REMOTE_SUBMIX:
case TYPE_HEARING_AID:
case TYPE_BUILTIN_SPEAKER_SAFE:
case TYPE_BLE_HEADSET:
diff --git a/media/java/android/media/AudioManager.java b/media/java/android/media/AudioManager.java
index 536afd4f712e..4627be3f5810 100644
--- a/media/java/android/media/AudioManager.java
+++ b/media/java/android/media/AudioManager.java
@@ -896,6 +896,30 @@ public class AudioManager {
*/
public static final int USE_DEFAULT_STREAM_TYPE = Integer.MIN_VALUE;
+ /** @hide */
+ @IntDef(flag = false, prefix = "DEVICE_STATE", value = {
+ DEVICE_CONNECTION_STATE_DISCONNECTED,
+ DEVICE_CONNECTION_STATE_CONNECTED}
+ )
+ @Retention(RetentionPolicy.SOURCE)
+ public @interface DeviceConnectionState {}
+
+ /**
+ * @hide The device connection state for disconnected devices.
+ */
+ @SystemApi
+ @SuppressLint("UnflaggedApi") // b/373465238
+ @RequiresPermission(Manifest.permission.MODIFY_AUDIO_ROUTING)
+ public static final int DEVICE_CONNECTION_STATE_DISCONNECTED = 0;
+
+ /**
+ * @hide The device connection state for connected devices.
+ */
+ @SystemApi
+ @SuppressLint("UnflaggedApi") // b/373465238
+ @RequiresPermission(Manifest.permission.MODIFY_AUDIO_ROUTING)
+ public static final int DEVICE_CONNECTION_STATE_CONNECTED = 1;
+
private static IAudioService sService;
/**
@@ -6726,14 +6750,16 @@ public class AudioManager {
}
/**
+ * @hide
* Indicate wired accessory connection state change and attributes.
- * @param state new connection state: 1 connected, 0 disconnected
* @param attributes attributes of the connected device
- * {@hide}
+ * @param state new connection state
*/
- @UnsupportedAppUsage
+ @SystemApi
+ @SuppressLint("UnflaggedApi") // b/373465238
@RequiresPermission(Manifest.permission.MODIFY_AUDIO_ROUTING)
- public void setWiredDeviceConnectionState(AudioDeviceAttributes attributes, int state) {
+ public void setWiredDeviceConnectionState(@NonNull AudioDeviceAttributes attributes,
+ @DeviceConnectionState int state) {
final IAudioService service = getService();
try {
service.setWiredDeviceConnectionState(attributes, state,
diff --git a/media/java/android/media/audio/common/AidlConversion.java b/media/java/android/media/audio/common/AidlConversion.java
index 65fd51b9ece9..c1d73f9033cf 100644
--- a/media/java/android/media/audio/common/AidlConversion.java
+++ b/media/java/android/media/audio/common/AidlConversion.java
@@ -753,9 +753,11 @@ public class AidlConversion {
break;
case AudioSystem.DEVICE_IN_REMOTE_SUBMIX:
aidl.type = AudioDeviceType.IN_SUBMIX;
+ aidl.connection = AudioDeviceDescription.CONNECTION_VIRTUAL;
break;
case AudioSystem.DEVICE_OUT_REMOTE_SUBMIX:
aidl.type = AudioDeviceType.OUT_SUBMIX;
+ aidl.connection = AudioDeviceDescription.CONNECTION_VIRTUAL;
break;
case AudioSystem.DEVICE_IN_ANLG_DOCK_HEADSET:
aidl.type = AudioDeviceType.IN_DOCK;
diff --git a/media/jni/android_media_ImageReader.cpp b/media/jni/android_media_ImageReader.cpp
index 019b1e0de4d6..effa92c5f2fa 100644
--- a/media/jni/android_media_ImageReader.cpp
+++ b/media/jni/android_media_ImageReader.cpp
@@ -942,6 +942,11 @@ static jint Image_getFormat(JNIEnv* env, jobject thiz, jint readerFormat)
return static_cast<jint>(PublicFormat::PRIVATE);
} else {
BufferItem* buffer = Image_getBufferItem(env, thiz);
+ if (buffer == nullptr) {
+ jniThrowException(env, "java/lang/IllegalStateException",
+ "Image is not initialized");
+ return -1;
+ }
int readerHalFormat = mapPublicFormatToHalFormat(static_cast<PublicFormat>(readerFormat));
int32_t fmt = applyFormatOverrides(
buffer->mGraphicBuffer->getPixelFormat(), readerHalFormat);
@@ -960,6 +965,11 @@ static jint Image_getFormat(JNIEnv* env, jobject thiz, jint readerFormat)
static jobject Image_getHardwareBuffer(JNIEnv* env, jobject thiz) {
BufferItem* buffer = Image_getBufferItem(env, thiz);
+ if (buffer == nullptr) {
+ jniThrowException(env, "java/lang/IllegalStateException",
+ "Image is not initialized");
+ return NULL;
+ }
AHardwareBuffer* b = AHardwareBuffer_from_GraphicBuffer(buffer->mGraphicBuffer.get());
// don't user the public AHardwareBuffer_toHardwareBuffer() because this would force us
// to link against libandroid.so
diff --git a/packages/SettingsLib/src/com/android/settingslib/bluetooth/devicesettings/data/repository/DeviceSettingServiceConnection.kt b/packages/SettingsLib/src/com/android/settingslib/bluetooth/devicesettings/data/repository/DeviceSettingServiceConnection.kt
index c16366e14560..edd49c5a8fb7 100644
--- a/packages/SettingsLib/src/com/android/settingslib/bluetooth/devicesettings/data/repository/DeviceSettingServiceConnection.kt
+++ b/packages/SettingsLib/src/com/android/settingslib/bluetooth/devicesettings/data/repository/DeviceSettingServiceConnection.kt
@@ -296,6 +296,7 @@ class DeviceSettingServiceConnection(
val listener =
object : IDeviceSettingsListener.Stub() {
override fun onDeviceSettingsChanged(settings: List<DeviceSetting>) {
+ Log.i(TAG, "Receive setting ids ${settings.map { it.settingId }}")
launch { send(settings) }
}
}
diff --git a/packages/SettingsLib/src/com/android/settingslib/license/LicenseHtmlLoaderCompat.java b/packages/SettingsLib/src/com/android/settingslib/license/LicenseHtmlLoaderCompat.java
index 121f5492a5ab..36960002e220 100644
--- a/packages/SettingsLib/src/com/android/settingslib/license/LicenseHtmlLoaderCompat.java
+++ b/packages/SettingsLib/src/com/android/settingslib/license/LicenseHtmlLoaderCompat.java
@@ -41,6 +41,7 @@ public class LicenseHtmlLoaderCompat extends AsyncLoaderCompat<File> {
"/system_ext/etc/NOTICE.xml.gz",
"/vendor_dlkm/etc/NOTICE.xml.gz",
"/odm_dlkm/etc/NOTICE.xml.gz",
+ "/system_dlkm/etc/NOTICE.xml.gz",
};
static final String NOTICE_HTML_FILE_NAME = "NOTICE.html";
diff --git a/packages/SettingsProvider/src/android/provider/settings/backup/SecureSettings.java b/packages/SettingsProvider/src/android/provider/settings/backup/SecureSettings.java
index d3ee40083c91..064198fc5e46 100644
--- a/packages/SettingsProvider/src/android/provider/settings/backup/SecureSettings.java
+++ b/packages/SettingsProvider/src/android/provider/settings/backup/SecureSettings.java
@@ -133,6 +133,7 @@ public class SecureSettings {
Settings.Secure.FINGERPRINT_SIDE_FPS_ENROLL_TAP_WINDOW,
Settings.Secure.FINGERPRINT_SIDE_FPS_AUTH_DOWNTIME,
Settings.Secure.SFPS_PERFORMANT_AUTH_ENABLED,
+ Settings.Secure.SCREEN_OFF_UNLOCK_UDFPS_ENABLED,
Settings.Secure.ACTIVE_UNLOCK_ON_WAKE,
Settings.Secure.ACTIVE_UNLOCK_ON_UNLOCK_INTENT,
Settings.Secure.ACTIVE_UNLOCK_ON_UNLOCK_INTENT_LEGACY,
diff --git a/packages/SettingsProvider/src/android/provider/settings/validators/SecureSettingsValidators.java b/packages/SettingsProvider/src/android/provider/settings/validators/SecureSettingsValidators.java
index d34ccc5a2173..c002a04d5b11 100644
--- a/packages/SettingsProvider/src/android/provider/settings/validators/SecureSettingsValidators.java
+++ b/packages/SettingsProvider/src/android/provider/settings/validators/SecureSettingsValidators.java
@@ -190,6 +190,7 @@ public class SecureSettingsValidators {
NON_NEGATIVE_INTEGER_VALIDATOR);
VALIDATORS.put(Secure.FINGERPRINT_SIDE_FPS_AUTH_DOWNTIME, NON_NEGATIVE_INTEGER_VALIDATOR);
VALIDATORS.put(Secure.SFPS_PERFORMANT_AUTH_ENABLED, BOOLEAN_VALIDATOR);
+ VALIDATORS.put(Secure.SCREEN_OFF_UNLOCK_UDFPS_ENABLED, BOOLEAN_VALIDATOR);
VALIDATORS.put(Secure.SHOW_MEDIA_WHEN_BYPASSING, BOOLEAN_VALIDATOR);
VALIDATORS.put(Secure.FACE_UNLOCK_APP_ENABLED, BOOLEAN_VALIDATOR);
VALIDATORS.put(Secure.FACE_UNLOCK_ALWAYS_REQUIRE_CONFIRMATION, BOOLEAN_VALIDATOR);
diff --git a/packages/SystemUI/Android.bp b/packages/SystemUI/Android.bp
index 9265ceb1c052..8ddd922b99f8 100644
--- a/packages/SystemUI/Android.bp
+++ b/packages/SystemUI/Android.bp
@@ -493,6 +493,7 @@ android_library {
},
},
use_resource_processor: true,
+ resource_dirs: [],
static_libs: [
"//frameworks/libs/systemui:compilelib",
"SystemUI-res",
diff --git a/packages/SystemUI/aconfig/systemui.aconfig b/packages/SystemUI/aconfig/systemui.aconfig
index 227878975114..3650f683f6a9 100644
--- a/packages/SystemUI/aconfig/systemui.aconfig
+++ b/packages/SystemUI/aconfig/systemui.aconfig
@@ -33,6 +33,13 @@ flag {
}
flag {
+ name: "notifications_redesign_footer_view"
+ namespace: "systemui"
+ description: "Notifications Redesign: Update the look of the notifications footer."
+ bug: "375010573"
+}
+
+flag {
name: "notification_row_content_binder_refactor"
namespace: "systemui"
description: "Convert the NotificationContentInflater to Kotlin and restructure it to support modern views"
@@ -139,6 +146,13 @@ flag {
}
flag {
+ name: "notifications_dismiss_pruned_summaries"
+ namespace: "systemui"
+ description: "NotifCollection.dismissNotifications will now dismiss summaries that are pruned from the shade."
+ bug: "355967751"
+}
+
+flag {
name: "notification_transparent_header_fix"
namespace: "systemui"
description: "fix the transparent group header issue for async header inflation."
@@ -1104,6 +1118,13 @@ flag {
}
flag {
+ name: "communal_standalone_support"
+ namespace: "systemui"
+ description: "Support communal features without a dock"
+ bug: "352301247"
+}
+
+flag {
name: "dream_overlay_updated_font"
namespace: "systemui"
description: "Flag to enable updated font settings for dream overlay"
@@ -1546,6 +1567,16 @@ flag {
}
flag {
+ name: "transition_race_condition"
+ namespace: "systemui"
+ description: "Thread-safe keyguard transitions"
+ bug: "358533338"
+ metadata {
+ purpose: PURPOSE_BUGFIX
+ }
+}
+
+flag {
name: "media_projection_request_attribution_fix"
namespace: "systemui"
description: "Ensure MediaProjection consent requests are properly attributed"
diff --git a/packages/SystemUI/animation/lib/Android.bp b/packages/SystemUI/animation/lib/Android.bp
index d9230ec67461..7d7302309af2 100644
--- a/packages/SystemUI/animation/lib/Android.bp
+++ b/packages/SystemUI/animation/lib/Android.bp
@@ -48,6 +48,19 @@ java_library {
}
filegroup {
+ name: "PlatformAnimationLib-client-srcs",
+ srcs: [
+ "src/com/android/systemui/animation/OriginRemoteTransition.java",
+ "src/com/android/systemui/animation/OriginTransitionSession.java",
+ "src/com/android/systemui/animation/SurfaceUIComponent.java",
+ "src/com/android/systemui/animation/Transactions.java",
+ "src/com/android/systemui/animation/UIComponent.java",
+ "src/com/android/systemui/animation/ViewUIComponent.java",
+ ":PlatformAnimationLib-aidl",
+ ],
+}
+
+filegroup {
name: "PlatformAnimationLib-aidl",
srcs: [
"src/**/*.aidl",
diff --git a/packages/SystemUI/animation/lib/OWNERS b/packages/SystemUI/animation/lib/OWNERS
new file mode 100644
index 000000000000..7569419bf4a4
--- /dev/null
+++ b/packages/SystemUI/animation/lib/OWNERS
@@ -0,0 +1,3 @@
+#inherits OWNERS from SystemUI in addition to WEAR framework owners below
+file:platform/frameworks/base:/WEAR_OWNERS
+
diff --git a/packages/SystemUI/animation/lib/src/com/android/systemui/animation/OriginRemoteTransition.java b/packages/SystemUI/animation/lib/src/com/android/systemui/animation/OriginRemoteTransition.java
index 08db95e5a795..2b5ff7c4b598 100644
--- a/packages/SystemUI/animation/lib/src/com/android/systemui/animation/OriginRemoteTransition.java
+++ b/packages/SystemUI/animation/lib/src/com/android/systemui/animation/OriginRemoteTransition.java
@@ -44,6 +44,7 @@ import java.util.List;
/**
* An implementation of {@link IRemoteTransition} that accepts a {@link UIComponent} as the origin
* and automatically attaches it to the transition leash before the transition starts.
+ * @hide
*/
public class OriginRemoteTransition extends IRemoteTransition.Stub {
private static final String TAG = "OriginRemoteTransition";
@@ -328,7 +329,9 @@ public class OriginRemoteTransition extends IRemoteTransition.Stub {
/* baseBounds= */ maxBounds);
}
- /** An interface that represents an origin transitions. */
+ /** An interface that represents an origin transitions.
+ * @hide
+ */
public interface TransitionPlayer {
/**
diff --git a/packages/SystemUI/animation/lib/src/com/android/systemui/animation/OriginTransitionSession.java b/packages/SystemUI/animation/lib/src/com/android/systemui/animation/OriginTransitionSession.java
index 23693b68a920..6d6aa8895ed0 100644
--- a/packages/SystemUI/animation/lib/src/com/android/systemui/animation/OriginTransitionSession.java
+++ b/packages/SystemUI/animation/lib/src/com/android/systemui/animation/OriginTransitionSession.java
@@ -43,6 +43,7 @@ import java.util.function.Supplier;
/**
* A session object that holds origin transition states for starting an activity from an on-screen
* UI component and smoothly transitioning back from the activity to the same UI component.
+ * @hide
*/
public class OriginTransitionSession {
private static final String TAG = "OriginTransitionSession";
@@ -179,7 +180,10 @@ public class OriginTransitionSession {
}
}
- /** A builder to build a {@link OriginTransitionSession}. */
+ /**
+ * A builder to build a {@link OriginTransitionSession}.
+ * @hide
+ */
public static class Builder {
private final Context mContext;
@Nullable private final IOriginTransitions mOriginTransitions;
diff --git a/packages/SystemUI/animation/lib/src/com/android/systemui/animation/SurfaceUIComponent.java b/packages/SystemUI/animation/lib/src/com/android/systemui/animation/SurfaceUIComponent.java
index 24387360936b..e1ff2a4c8208 100644
--- a/packages/SystemUI/animation/lib/src/com/android/systemui/animation/SurfaceUIComponent.java
+++ b/packages/SystemUI/animation/lib/src/com/android/systemui/animation/SurfaceUIComponent.java
@@ -26,7 +26,10 @@ import java.util.Arrays;
import java.util.Collection;
import java.util.concurrent.Executor;
-/** A {@link UIComponent} representing a {@link SurfaceControl}. */
+/**
+ * A {@link UIComponent} representing a {@link SurfaceControl}.
+ * @hide
+ */
public class SurfaceUIComponent implements UIComponent {
private final Collection<SurfaceControl> mSurfaces;
private final Rect mBaseBounds;
@@ -89,7 +92,10 @@ public class SurfaceUIComponent implements UIComponent {
+ "}";
}
- /** A {@link Transaction} wrapping a {@link SurfaceControl.Transaction}. */
+ /**
+ * A {@link Transaction} wrapping a {@link SurfaceControl.Transaction}.
+ * @hide
+ */
public static class Transaction implements UIComponent.Transaction<SurfaceUIComponent> {
private final SurfaceControl.Transaction mTransaction;
private final ArrayList<Runnable> mChanges = new ArrayList<>();
diff --git a/packages/SystemUI/animation/lib/src/com/android/systemui/animation/Transactions.java b/packages/SystemUI/animation/lib/src/com/android/systemui/animation/Transactions.java
index 5240d99a9217..64d21724ba32 100644
--- a/packages/SystemUI/animation/lib/src/com/android/systemui/animation/Transactions.java
+++ b/packages/SystemUI/animation/lib/src/com/android/systemui/animation/Transactions.java
@@ -27,6 +27,7 @@ import java.util.concurrent.Executor;
/**
* A composite {@link UIComponent.Transaction} that combines multiple other transactions for each ui
* type.
+ * @hide
*/
public class Transactions implements UIComponent.Transaction<UIComponent> {
private final Map<Class, UIComponent.Transaction> mTransactions = new ArrayMap<>();
diff --git a/packages/SystemUI/animation/lib/src/com/android/systemui/animation/UIComponent.java b/packages/SystemUI/animation/lib/src/com/android/systemui/animation/UIComponent.java
index 747e4d1eb278..8aec2f42a5f1 100644
--- a/packages/SystemUI/animation/lib/src/com/android/systemui/animation/UIComponent.java
+++ b/packages/SystemUI/animation/lib/src/com/android/systemui/animation/UIComponent.java
@@ -17,12 +17,17 @@
package com.android.systemui.animation;
import android.annotation.FloatRange;
+import android.annotation.NonNull;
+import android.annotation.Nullable;
import android.graphics.Rect;
import android.view.SurfaceControl;
import java.util.concurrent.Executor;
-/** An interface representing an UI component on the display. */
+/**
+ * An interface representing an UI component on the display.
+ * @hide
+ */
public interface UIComponent {
/** Get the current alpha of this UI. */
@@ -32,39 +37,48 @@ public interface UIComponent {
boolean isVisible();
/** Get the bounds of this UI in its display. */
+ @NonNull
Rect getBounds();
/** Create a new {@link Transaction} that can update this UI. */
+ @NonNull
Transaction newTransaction();
/**
* A transaction class for updating {@link UIComponent}.
*
* @param <T> the subtype of {@link UIComponent} that this {@link Transaction} can handle.
+ * @hide
*/
interface Transaction<T extends UIComponent> {
/** Update alpha of an UI. Execution will be delayed until {@link #commit()} is called. */
- Transaction setAlpha(T ui, @FloatRange(from = 0.0, to = 1.0) float alpha);
+ Transaction setAlpha(@NonNull T ui, @FloatRange(from = 0.0, to = 1.0) float alpha);
/**
* Update visibility of an UI. Execution will be delayed until {@link #commit()} is called.
*/
- Transaction setVisible(T ui, boolean visible);
+ @NonNull
+ Transaction setVisible(@NonNull T ui, boolean visible);
/** Update bounds of an UI. Execution will be delayed until {@link #commit()} is called. */
- Transaction setBounds(T ui, Rect bounds);
+ @NonNull
+ Transaction setBounds(@NonNull T ui, @NonNull Rect bounds);
/**
* Attach a ui to the transition leash. Execution will be delayed until {@link #commit()} is
* called.
*/
- Transaction attachToTransitionLeash(T ui, SurfaceControl transitionLeash, int w, int h);
+ @NonNull
+ Transaction attachToTransitionLeash(
+ @NonNull T ui, @NonNull SurfaceControl transitionLeash, int w, int h);
/**
* Detach a ui from the transition leash. Execution will be delayed until {@link #commit} is
* called.
*/
- Transaction detachFromTransitionLeash(T ui, Executor executor, Runnable onDone);
+ @NonNull
+ Transaction detachFromTransitionLeash(
+ @NonNull T ui, @NonNull Executor executor, @Nullable Runnable onDone);
/** Commit any pending changes added to this transaction. */
void commit();
diff --git a/packages/SystemUI/animation/lib/src/com/android/systemui/animation/ViewUIComponent.java b/packages/SystemUI/animation/lib/src/com/android/systemui/animation/ViewUIComponent.java
index 313789c4ca7e..4c047d589a66 100644
--- a/packages/SystemUI/animation/lib/src/com/android/systemui/animation/ViewUIComponent.java
+++ b/packages/SystemUI/animation/lib/src/com/android/systemui/animation/ViewUIComponent.java
@@ -38,6 +38,7 @@ import java.util.concurrent.Executor;
* be changed to INVISIBLE in its view tree. This allows the {@link View} to transform in the
* full-screen size leash without being constrained by the view tree's boundary or inheriting its
* parent's alpha and transformation.
+ * @hide
*/
public class ViewUIComponent implements UIComponent {
private static final String TAG = "ViewUIComponent";
@@ -234,6 +235,9 @@ public class ViewUIComponent implements UIComponent {
mView.post(this::draw);
}
+ /**
+ * @hide
+ */
public static class Transaction implements UIComponent.Transaction<ViewUIComponent> {
private final List<Runnable> mChanges = new ArrayList<>();
diff --git a/packages/SystemUI/animation/src/com/android/systemui/animation/back/FlingOnBackAnimationCallback.kt b/packages/SystemUI/animation/src/com/android/systemui/animation/back/FlingOnBackAnimationCallback.kt
index cd2dd04568a7..47ad0b3e0cd4 100644
--- a/packages/SystemUI/animation/src/com/android/systemui/animation/back/FlingOnBackAnimationCallback.kt
+++ b/packages/SystemUI/animation/src/com/android/systemui/animation/back/FlingOnBackAnimationCallback.kt
@@ -95,16 +95,18 @@ abstract class FlingOnBackAnimationCallback(
final override fun onBackProgressed(backEvent: BackEvent) {
val interpolatedProgress = progressInterpolator.getInterpolation(backEvent.progress)
if (predictiveBackTimestampApi()) {
- velocityTracker.addMovement(
- MotionEvent.obtain(
- /* downTime */ downTime!!,
- /* eventTime */ backEvent.frameTimeMillis,
- /* action */ ACTION_MOVE,
- /* x */ interpolatedProgress * SCALE_FACTOR,
- /* y */ 0f,
- /* metaState */ 0,
+ downTime?.let { downTime ->
+ velocityTracker.addMovement(
+ MotionEvent.obtain(
+ /* downTime */ downTime,
+ /* eventTime */ backEvent.frameTimeMillis,
+ /* action */ ACTION_MOVE,
+ /* x */ interpolatedProgress * SCALE_FACTOR,
+ /* y */ 0f,
+ /* metaState */ 0,
+ )
)
- )
+ }
lastBackEvent =
BackEvent(
backEvent.touchX,
diff --git a/packages/SystemUI/common/Android.bp b/packages/SystemUI/common/Android.bp
index 6fc13d7e0ddc..91dc3e338c59 100644
--- a/packages/SystemUI/common/Android.bp
+++ b/packages/SystemUI/common/Android.bp
@@ -22,20 +22,13 @@ package {
default_applicable_licenses: ["frameworks_base_packages_SystemUI_license"],
}
-android_library {
-
+java_library {
name: "SystemUICommon",
- use_resource_processor: true,
srcs: [
"src/**/*.java",
"src/**/*.kt",
],
- static_libs: [
- "androidx.core_core-ktx",
- ],
-
- manifest: "AndroidManifest.xml",
kotlincflags: ["-Xjvm-default=all"],
}
diff --git a/packages/SystemUI/common/AndroidManifest.xml b/packages/SystemUI/common/AndroidManifest.xml
deleted file mode 100644
index 6f757eb67d2e..000000000000
--- a/packages/SystemUI/common/AndroidManifest.xml
+++ /dev/null
@@ -1,21 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
- Copyright (C) 2023 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 xmlns:android="http://schemas.android.com/apk/res/android"
- package="com.android.systemui.common">
-
-</manifest>
diff --git a/packages/SystemUI/common/README.md b/packages/SystemUI/common/README.md
index 1cc5277aa83e..17e541266965 100644
--- a/packages/SystemUI/common/README.md
+++ b/packages/SystemUI/common/README.md
@@ -1,5 +1,9 @@
# SystemUICommon
-`SystemUICommon` is a module within SystemUI that hosts standalone helper libraries. It is intended to be used by other modules, and therefore should not have other SystemUI dependencies to avoid circular dependencies.
+`SystemUICommon` is a module within SystemUI that hosts standalone helper libraries. It is intended
+to be used by other modules, and therefore should not have other SystemUI dependencies to avoid
+circular dependencies.
-To maintain the structure of this module, please refrain from adding components at the top level. Instead, add them to specific sub-packages, such as `systemui/common/buffer/`. This will help to keep the module organized and easy to navigate.
+To maintain the structure of this module, please refrain from adding components at the top level.
+Instead, add them to specific sub-packages, such as `systemui/common/buffer/`. This will help to
+keep the module organized and easy to navigate.
diff --git a/packages/SystemUI/compose/features/src/com/android/systemui/notifications/ui/composable/NotificationScrimNestedScrollConnection.kt b/packages/SystemUI/compose/features/src/com/android/systemui/notifications/ui/composable/NotificationScrimNestedScrollConnection.kt
index e4c60e166fd5..5cb45e5bd914 100644
--- a/packages/SystemUI/compose/features/src/com/android/systemui/notifications/ui/composable/NotificationScrimNestedScrollConnection.kt
+++ b/packages/SystemUI/compose/features/src/com/android/systemui/notifications/ui/composable/NotificationScrimNestedScrollConnection.kt
@@ -18,9 +18,11 @@ package com.android.systemui.notifications.ui.composable
import androidx.compose.foundation.gestures.Orientation
import androidx.compose.ui.input.nestedscroll.NestedScrollConnection
+import androidx.compose.ui.input.nestedscroll.NestedScrollSource
import androidx.compose.ui.util.fastCoerceAtLeast
import androidx.compose.ui.util.fastCoerceAtMost
import com.android.compose.nestedscroll.PriorityNestedScrollConnection
+import com.android.compose.nestedscroll.ScrollController
/**
* A [NestedScrollConnection] that listens for all vertical scroll events and responds in the
@@ -58,33 +60,40 @@ fun NotificationScrimNestedScrollConnection(
offsetAvailable > 0 && (scrimOffset() < maxScrimOffset || isCurrentGestureOverscroll())
},
canStartPostFling = { false },
- canStopOnPreFling = { false },
- onStart = { offsetAvailable -> onStart(offsetAvailable) },
- onScroll = { offsetAvailable, _ ->
- val currentHeight = scrimOffset()
- val amountConsumed =
- if (offsetAvailable > 0) {
- val amountLeft = maxScrimOffset - currentHeight
- offsetAvailable.fastCoerceAtMost(amountLeft)
- } else {
- val amountLeft = minScrimOffset() - currentHeight
- offsetAvailable.fastCoerceAtLeast(amountLeft)
+ onStart = { firstScroll ->
+ onStart(firstScroll)
+ object : ScrollController {
+ override fun onScroll(deltaScroll: Float, source: NestedScrollSource): Float {
+ val currentHeight = scrimOffset()
+ val amountConsumed =
+ if (deltaScroll > 0) {
+ val amountLeft = maxScrimOffset - currentHeight
+ deltaScroll.fastCoerceAtMost(amountLeft)
+ } else {
+ val amountLeft = minScrimOffset() - currentHeight
+ deltaScroll.fastCoerceAtLeast(amountLeft)
+ }
+ snapScrimOffset(currentHeight + amountConsumed)
+ return amountConsumed
}
- snapScrimOffset(currentHeight + amountConsumed)
- amountConsumed
- },
- onStop = { velocityAvailable ->
- onStop(velocityAvailable)
- if (scrimOffset() < minScrimOffset()) {
- animateScrimOffset(minScrimOffset())
- }
- // Don't consume the velocity on pre/post fling
- 0f
- },
- onCancel = {
- onStop(0f)
- if (scrimOffset() < minScrimOffset()) {
- animateScrimOffset(minScrimOffset())
+
+ override suspend fun onStop(initialVelocity: Float): Float {
+ onStop(initialVelocity)
+ if (scrimOffset() < minScrimOffset()) {
+ animateScrimOffset(minScrimOffset())
+ }
+ // Don't consume the velocity on pre/post fling
+ return 0f
+ }
+
+ override fun onCancel() {
+ onStop(0f)
+ if (scrimOffset() < minScrimOffset()) {
+ animateScrimOffset(minScrimOffset())
+ }
+ }
+
+ override fun canStopOnPreFling() = false
}
},
)
diff --git a/packages/SystemUI/compose/features/src/com/android/systemui/notifications/ui/composable/NotificationStackNestedScrollConnection.kt b/packages/SystemUI/compose/features/src/com/android/systemui/notifications/ui/composable/NotificationStackNestedScrollConnection.kt
index edb05ebd77d1..e1b74a968caa 100644
--- a/packages/SystemUI/compose/features/src/com/android/systemui/notifications/ui/composable/NotificationStackNestedScrollConnection.kt
+++ b/packages/SystemUI/compose/features/src/com/android/systemui/notifications/ui/composable/NotificationStackNestedScrollConnection.kt
@@ -23,6 +23,7 @@ import androidx.compose.foundation.layout.offset
import androidx.compose.runtime.Composable
import androidx.compose.runtime.remember
import androidx.compose.ui.Modifier
+import androidx.compose.ui.input.nestedscroll.NestedScrollSource
import androidx.compose.ui.input.nestedscroll.nestedScroll
import androidx.compose.ui.platform.LocalConfiguration
import androidx.compose.ui.platform.LocalDensity
@@ -30,6 +31,7 @@ import androidx.compose.ui.unit.IntOffset
import androidx.compose.ui.unit.dp
import androidx.compose.ui.util.fastCoerceAtLeast
import com.android.compose.nestedscroll.PriorityNestedScrollConnection
+import com.android.compose.nestedscroll.ScrollController
import kotlin.math.max
import kotlin.math.roundToInt
import kotlin.math.tanh
@@ -92,20 +94,29 @@ fun NotificationStackNestedScrollConnection(
offsetAvailable < 0f && offsetBeforeStart < 0f && !canScrollForward()
},
canStartPostFling = { velocityAvailable -> velocityAvailable < 0f && !canScrollForward() },
- canStopOnPreFling = { false },
- onStart = { offsetAvailable -> onStart(offsetAvailable) },
- onScroll = { offsetAvailable, _ ->
- val minOffset = 0f
- val consumed = offsetAvailable.fastCoerceAtLeast(minOffset - stackOffset())
- if (consumed != 0f) {
- onScroll(consumed)
+ onStart = { firstScroll ->
+ onStart(firstScroll)
+ object : ScrollController {
+ override fun onScroll(deltaScroll: Float, source: NestedScrollSource): Float {
+ val minOffset = 0f
+ val consumed = deltaScroll.fastCoerceAtLeast(minOffset - stackOffset())
+ if (consumed != 0f) {
+ onScroll(consumed)
+ }
+ return consumed
+ }
+
+ override suspend fun onStop(initialVelocity: Float): Float {
+ onStop(initialVelocity)
+ return initialVelocity
+ }
+
+ override fun onCancel() {
+ onStop(0f)
+ }
+
+ override fun canStopOnPreFling() = false
}
- consumed
- },
- onStop = { velocityAvailable ->
- onStop(velocityAvailable)
- velocityAvailable
},
- onCancel = { onStop(0f) },
)
}
diff --git a/packages/SystemUI/compose/features/src/com/android/systemui/notifications/ui/composable/Notifications.kt b/packages/SystemUI/compose/features/src/com/android/systemui/notifications/ui/composable/Notifications.kt
index 834a7f5220ab..4fa1984661da 100644
--- a/packages/SystemUI/compose/features/src/com/android/systemui/notifications/ui/composable/Notifications.kt
+++ b/packages/SystemUI/compose/features/src/com/android/systemui/notifications/ui/composable/Notifications.kt
@@ -375,9 +375,13 @@ fun SceneScope.NotificationScrollingStack(
LaunchedEffect(shadeScrollState) { viewModel.setScrollState(shadeScrollState) }
// if contentHeight drops below minimum visible scrim height while scrim is
- // expanded, reset scrim offset.
- LaunchedEffect(stackHeight, scrimOffset) {
- snapshotFlow { stackHeight.intValue < minVisibleScrimHeight() && scrimOffset.value < 0f }
+ // expanded and IME is not showing, reset scrim offset.
+ LaunchedEffect(stackHeight, scrimOffset, imeTop) {
+ snapshotFlow {
+ stackHeight.intValue < minVisibleScrimHeight() &&
+ scrimOffset.value < 0f &&
+ imeTop.floatValue <= 0f
+ }
.collect { shouldCollapse -> if (shouldCollapse) scrimOffset.animateTo(0f, tween()) }
}
diff --git a/packages/SystemUI/compose/features/src/com/android/systemui/qs/ui/composable/QuickSettings.kt b/packages/SystemUI/compose/features/src/com/android/systemui/qs/ui/composable/QuickSettings.kt
index 58801e01d9d6..e725ce589f3e 100644
--- a/packages/SystemUI/compose/features/src/com/android/systemui/qs/ui/composable/QuickSettings.kt
+++ b/packages/SystemUI/compose/features/src/com/android/systemui/qs/ui/composable/QuickSettings.kt
@@ -128,10 +128,10 @@ private fun SceneScope.stateForQuickSettingsContent(
QSSceneAdapter.State.QS
}
else ->
- error(
- "Bad transition for QuickSettings: fromContent=$fromContent," +
- " toScene=$toContent"
- )
+ // We are not in a transition between states that have QS, so just make
+ // sure it's closed. This could be an issue if going from SplitShade to
+ // a folded device.
+ QSSceneAdapter.State.CLOSED
}
}
is TransitionState.Transition.OverlayTransition ->
diff --git a/packages/SystemUI/compose/features/src/com/android/systemui/shade/ui/composable/ShadeScene.kt b/packages/SystemUI/compose/features/src/com/android/systemui/shade/ui/composable/ShadeScene.kt
index 491221f39191..bba3d69ea77f 100644
--- a/packages/SystemUI/compose/features/src/com/android/systemui/shade/ui/composable/ShadeScene.kt
+++ b/packages/SystemUI/compose/features/src/com/android/systemui/shade/ui/composable/ShadeScene.kt
@@ -516,13 +516,14 @@ private fun SceneScope.SplitShade(
.weight(1f)
.graphicsLayer { translationX = unfoldTranslationXForStartSide }
) {
- BrightnessMirror(
- viewModel = brightnessMirrorViewModel,
- qsSceneAdapter = viewModel.qsSceneAdapter,
- // Need to use the offset measured from the container as the header
- // has to be accounted for
- measureFromContainer = true,
- )
+ Box(modifier = Modifier.fillMaxSize()) {
+ BrightnessMirror(
+ viewModel = brightnessMirrorViewModel,
+ qsSceneAdapter = viewModel.qsSceneAdapter,
+ modifier = Modifier.align(Alignment.TopCenter),
+ measureFromContainer = true,
+ )
+ }
Column(
verticalArrangement = Arrangement.Top,
modifier = Modifier.fillMaxSize().padding(bottom = bottomPadding),
diff --git a/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/DraggableHandler.kt b/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/DraggableHandler.kt
index 8469007eddb6..7c7202a5c7f2 100644
--- a/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/DraggableHandler.kt
+++ b/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/DraggableHandler.kt
@@ -20,6 +20,7 @@ package com.android.compose.animation.scene
import androidx.compose.foundation.gestures.Orientation
import androidx.compose.ui.geometry.Offset
+import androidx.compose.ui.input.nestedscroll.NestedScrollSource
import androidx.compose.ui.unit.dp
import androidx.compose.ui.unit.round
import androidx.compose.ui.util.fastCoerceIn
@@ -27,6 +28,7 @@ import com.android.compose.animation.scene.content.Content
import com.android.compose.animation.scene.content.state.TransitionState
import com.android.compose.animation.scene.content.state.TransitionState.HasOverscrollProperties.Companion.DistanceUnspecified
import com.android.compose.nestedscroll.PriorityNestedScrollConnection
+import com.android.compose.nestedscroll.ScrollController
import kotlin.math.absoluteValue
internal typealias SuspendedValue<T> = suspend () -> T
@@ -66,6 +68,7 @@ internal class DraggableHandlerImpl(
internal val orientation: Orientation,
) : DraggableHandler {
internal val nestedScrollKey = Any()
+
/** The [DraggableHandler] can only have one active [DragController] at a time. */
private var dragController: DragControllerImpl? = null
@@ -345,6 +348,7 @@ private class DragControllerImpl(
distance == DistanceUnspecified ||
swipeAnimation.contentTransition.isWithinProgressRange(desiredProgress) ->
desiredOffset
+
distance > 0f -> desiredOffset.fastCoerceIn(0f, distance)
else -> desiredOffset.fastCoerceIn(distance, 0f)
}
@@ -545,6 +549,7 @@ internal class Swipes(
upOrLeftResult == null && downOrRightResult == null -> null
(directionOffset < 0f && upOrLeftResult != null) || downOrRightResult == null ->
upOrLeftResult
+
else -> downOrRightResult
}
}
@@ -608,7 +613,6 @@ internal class NestedScrollHandlerImpl(
return overscrollSpec != null
}
- var dragController: DragController? = null
var isIntercepting = false
return PriorityNestedScrollConnection(
@@ -669,10 +673,12 @@ internal class NestedScrollHandlerImpl(
canChangeScene = isZeroOffset
isZeroOffset && hasNextScene(offsetAvailable)
}
+
NestedScrollBehavior.EdgeWithPreview -> {
canChangeScene = isZeroOffset
hasNextScene(offsetAvailable)
}
+
NestedScrollBehavior.EdgeAlways -> {
canChangeScene = true
hasNextScene(offsetAvailable)
@@ -710,53 +716,56 @@ internal class NestedScrollHandlerImpl(
canStart
},
- // We need to maintain scroll priority even if the scene transition can no longer
- // consume the scroll gesture to allow us to return to the previous scene.
- canStopOnScroll = { _, _ -> false },
- canStopOnPreFling = { true },
- onStart = { offsetAvailable ->
+ onStart = { firstScroll ->
val pointersInfo = pointersInfo()
- dragController =
- draggableHandler.onDragStarted(
- pointersDown = pointersInfo.pointersDown,
- startedPosition = pointersInfo.startedPosition,
- overSlop = if (isIntercepting) 0f else offsetAvailable,
- )
+ scrollController(
+ dragController =
+ draggableHandler.onDragStarted(
+ pointersDown = pointersInfo.pointersDown,
+ startedPosition = pointersInfo.startedPosition,
+ overSlop = if (isIntercepting) 0f else firstScroll,
+ ),
+ canChangeScene = canChangeScene,
+ pointersInfoOwner = pointersInfoOwner,
+ )
},
- onScroll = { offsetAvailable, _ ->
- val controller = dragController ?: error("Should be called after onStart")
+ )
+ }
+}
- val pointersInfo = pointersInfoOwner.pointersInfo()
- if (pointersInfo.isMouseWheel) {
- // Do not support mouse wheel interactions
- return@PriorityNestedScrollConnection 0f
- }
+private fun scrollController(
+ dragController: DragController,
+ canChangeScene: Boolean,
+ pointersInfoOwner: PointersInfoOwner,
+): ScrollController {
+ return object : ScrollController {
+ override fun onScroll(deltaScroll: Float, source: NestedScrollSource): Float {
+ val pointersInfo = pointersInfoOwner.pointersInfo()
+ if (pointersInfo.isMouseWheel) {
+ // Do not support mouse wheel interactions
+ return 0f
+ }
- // TODO(b/297842071) We should handle the overscroll or slow drag if the gesture is
- // initiated in a nested child.
- controller.onDrag(delta = offsetAvailable)
- },
- onStop = { velocityAvailable ->
- val controller = dragController ?: error("Should be called after onStart")
- try {
- controller
- .onStop(velocity = velocityAvailable, canChangeContent = canChangeScene)
- .invoke()
- } finally {
- // onStop might still be running when a new gesture begins.
- // To prevent conflicts, we should only remove the drag controller if it's the
- // same one that was active initially.
- if (dragController == controller) {
- dragController = null
- }
- }
- },
- onCancel = {
- val controller = dragController ?: error("Should be called after onStart")
- controller.onStop(velocity = 0f, canChangeContent = canChangeScene)
- dragController = null
- },
- )
+ return dragController.onDrag(delta = deltaScroll)
+ }
+
+ override suspend fun onStop(initialVelocity: Float): Float {
+ return dragController
+ .onStop(velocity = initialVelocity, canChangeContent = canChangeScene)
+ .invoke()
+ }
+
+ override fun onCancel() {
+ dragController.onStop(velocity = 0f, canChangeContent = canChangeScene)
+ }
+
+ /**
+ * We need to maintain scroll priority even if the scene transition can no longer consume
+ * the scroll gesture to allow us to return to the previous scene.
+ */
+ override fun canCancelScroll(available: Float, consumed: Float) = false
+
+ override fun canStopOnPreFling() = true
}
}
diff --git a/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/NestedScrollToScene.kt b/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/NestedScrollToScene.kt
index 8a0e4627d10c..fbd1cd542c05 100644
--- a/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/NestedScrollToScene.kt
+++ b/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/NestedScrollToScene.kt
@@ -128,10 +128,10 @@ private class NestedScrollToSceneNode(
) : DelegatingNode() {
private var scrollBehaviorOwner: ScrollBehaviorOwner? = null
- private fun requireScrollBehaviorOwner(): ScrollBehaviorOwner {
+ private fun findScrollBehaviorOwner(): ScrollBehaviorOwner? {
var behaviorOwner = scrollBehaviorOwner
if (behaviorOwner == null) {
- behaviorOwner = requireScrollBehaviorOwner(layoutImpl.draggableHandler(orientation))
+ behaviorOwner = findScrollBehaviorOwner(layoutImpl.draggableHandler(orientation))
scrollBehaviorOwner = behaviorOwner
}
return behaviorOwner
@@ -156,8 +156,8 @@ private class NestedScrollToSceneNode(
// transition between scenes. We can assume that the behavior is only needed if
// there is some remaining amount.
if (available != Offset.Zero) {
- requireScrollBehaviorOwner()
- .updateScrollBehaviors(
+ findScrollBehaviorOwner()
+ ?.updateScrollBehaviors(
topOrLeftBehavior = topOrLeftBehavior,
bottomOrRightBehavior = bottomOrRightBehavior,
isExternalOverscrollGesture = isExternalOverscrollGesture,
diff --git a/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/SwipeToScene.kt b/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/SwipeToScene.kt
index a3f2a434cff7..fdf01cce396b 100644
--- a/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/SwipeToScene.kt
+++ b/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/SwipeToScene.kt
@@ -172,15 +172,12 @@ private class SwipeToSceneNode(
}
/** Find the [ScrollBehaviorOwner] for the current orientation. */
-internal fun DelegatableNode.requireScrollBehaviorOwner(
+internal fun DelegatableNode.findScrollBehaviorOwner(
draggableHandler: DraggableHandlerImpl
-): ScrollBehaviorOwner {
- val ancestorNode =
- checkNotNull(findNearestAncestor(draggableHandler.nestedScrollKey)) {
- "This should never happen! Couldn't find a ScrollBehaviorOwner. " +
- "Are we inside an SceneTransitionLayout?"
- }
- return ancestorNode as ScrollBehaviorOwner
+): ScrollBehaviorOwner? {
+ // If there are no scenes in a particular orientation, the corresponding ScrollBehaviorOwnerNode
+ // is removed from the composition.
+ return findNearestAncestor(draggableHandler.nestedScrollKey) as? ScrollBehaviorOwner
}
internal fun interface ScrollBehaviorOwner {
diff --git a/packages/SystemUI/compose/scene/src/com/android/compose/nestedscroll/LargeTopAppBarNestedScrollConnection.kt b/packages/SystemUI/compose/scene/src/com/android/compose/nestedscroll/LargeTopAppBarNestedScrollConnection.kt
index ecf64b771d1f..255da31719f3 100644
--- a/packages/SystemUI/compose/scene/src/com/android/compose/nestedscroll/LargeTopAppBarNestedScrollConnection.kt
+++ b/packages/SystemUI/compose/scene/src/com/android/compose/nestedscroll/LargeTopAppBarNestedScrollConnection.kt
@@ -18,6 +18,7 @@ package com.android.compose.nestedscroll
import androidx.compose.foundation.gestures.Orientation
import androidx.compose.ui.input.nestedscroll.NestedScrollConnection
+import androidx.compose.ui.input.nestedscroll.NestedScrollSource
import androidx.compose.ui.util.fastCoerceAtLeast
import androidx.compose.ui.util.fastCoerceAtMost
@@ -54,23 +55,38 @@ fun LargeTopAppBarNestedScrollConnection(
offsetAvailable > 0 && height() < maxHeight()
},
canStartPostFling = { false },
- canStopOnPreFling = { false },
- onStart = { /* do nothing */ },
- onScroll = { offsetAvailable, _ ->
- val currentHeight = height()
- val amountConsumed =
- if (offsetAvailable > 0) {
- val amountLeft = maxHeight() - currentHeight
- offsetAvailable.fastCoerceAtMost(amountLeft)
- } else {
- val amountLeft = minHeight() - currentHeight
- offsetAvailable.fastCoerceAtLeast(amountLeft)
- }
- onHeightChanged(currentHeight + amountConsumed)
- amountConsumed
- },
- // Don't consume the velocity on pre/post fling
- onStop = { 0f },
- onCancel = { /* do nothing */ },
+ onStart = { LargeTopAppBarScrollController(height, maxHeight, minHeight, onHeightChanged) },
)
}
+
+private class LargeTopAppBarScrollController(
+ val height: () -> Float,
+ val maxHeight: () -> Float,
+ val minHeight: () -> Float,
+ val onHeightChanged: (Float) -> Unit,
+) : ScrollController {
+ override fun onScroll(deltaScroll: Float, source: NestedScrollSource): Float {
+ val currentHeight = height()
+ val amountConsumed =
+ if (deltaScroll > 0) {
+ val amountLeft = maxHeight() - currentHeight
+ deltaScroll.fastCoerceAtMost(amountLeft)
+ } else {
+ val amountLeft = minHeight() - currentHeight
+ deltaScroll.fastCoerceAtLeast(amountLeft)
+ }
+ onHeightChanged(currentHeight + amountConsumed)
+ return amountConsumed
+ }
+
+ override suspend fun onStop(initialVelocity: Float): Float {
+ // Don't consume the velocity on pre/post fling
+ return 0f
+ }
+
+ override fun onCancel() {
+ // do nothing
+ }
+
+ override fun canStopOnPreFling() = false
+}
diff --git a/packages/SystemUI/compose/scene/src/com/android/compose/nestedscroll/PriorityNestedScrollConnection.kt b/packages/SystemUI/compose/scene/src/com/android/compose/nestedscroll/PriorityNestedScrollConnection.kt
index 57d236be40ce..ca44a5c21cab 100644
--- a/packages/SystemUI/compose/scene/src/com/android/compose/nestedscroll/PriorityNestedScrollConnection.kt
+++ b/packages/SystemUI/compose/scene/src/com/android/compose/nestedscroll/PriorityNestedScrollConnection.kt
@@ -16,46 +16,106 @@
package com.android.compose.nestedscroll
-import androidx.compose.animation.core.AnimationState
-import androidx.compose.animation.core.DecayAnimationSpec
-import androidx.compose.animation.core.animateDecay
import androidx.compose.foundation.gestures.Orientation
import androidx.compose.ui.geometry.Offset
import androidx.compose.ui.input.nestedscroll.NestedScrollConnection
import androidx.compose.ui.input.nestedscroll.NestedScrollSource
import androidx.compose.ui.unit.Velocity
import com.android.compose.ui.util.SpaceVectorConverter
-import kotlin.math.abs
import kotlin.math.sign
-import kotlinx.coroutines.CompletableDeferred
import kotlinx.coroutines.Deferred
import kotlinx.coroutines.async
import kotlinx.coroutines.coroutineScope
/**
- * A [NestedScrollConnection] that intercepts scroll events in priority mode.
+ * The [ScrollController] provides control over the scroll gesture. It allows you to:
+ * - Scroll the content by a given pixel amount.
+ * - Cancel the current scroll operation.
+ * - Stop the scrolling with a given initial velocity.
*
- * Priority mode allows this connection to take control over scroll events within a nested scroll
- * hierarchy. When in priority mode, this connection consumes scroll events before its children,
- * enabling custom scrolling behaviors like sticky headers.
+ * **Important Notes:**
+ * - [onCancel] is called only when [PriorityNestedScrollConnection.reset] is invoked or when
+ * [canCancelScroll] returns `true` after a call to [onScroll]. It is never called after [onStop].
+ * - [onStop] can be interrupted by a new gesture. In such cases, you need to handle a potential
+ * cancellation within your implementation of [onStop], although [onCancel] will not be called.
+ */
+interface ScrollController {
+ /**
+ * Scrolls the current content by [deltaScroll] pixels.
+ *
+ * @param deltaScroll The amount of pixels to scroll by.
+ * @param source The source of the scroll event.
+ * @return The amount of [deltaScroll] that was consumed.
+ */
+ fun onScroll(deltaScroll: Float, source: NestedScrollSource): Float
+
+ /**
+ * Checks if the current scroll operation can be canceled. This is typically called after
+ * [onScroll] to determine if the [ScrollController] has lost priority and should cancel the
+ * ongoing scroll operation.
+ *
+ * @param available The total amount of scroll available.
+ * @param consumed The amount of scroll consumed by [onScroll].
+ * @return `true` if the scroll can be canceled.
+ */
+ fun canCancelScroll(available: Float, consumed: Float): Boolean {
+ return consumed == 0f
+ }
+
+ /**
+ * Cancels the current scroll operation. This method is called when
+ * [PriorityNestedScrollConnection.reset] is invoked or when [canCancelScroll] returns `true`.
+ */
+ fun onCancel()
+
+ /**
+ * Checks if the scroll can be stopped during the [NestedScrollConnection.onPreFling] phase.
+ *
+ * @return `true` if the scroll can be stopped.
+ */
+ fun canStopOnPreFling(): Boolean
+
+ /**
+ * Stops the controller with the given [initialVelocity]. This typically starts a decay
+ * animation to smoothly bring the scrolling to a stop. This method can be interrupted by a new
+ * gesture, requiring you to handle potential cancellation within your implementation.
+ *
+ * @param initialVelocity The initial velocity of the scroll when stopping.
+ * @return The consumed [initialVelocity] when the animation completes.
+ */
+ suspend fun onStop(initialVelocity: Float): Float
+}
+
+/**
+ * A [NestedScrollConnection] that lets you implement custom scroll behaviors that take priority
+ * over the default nested scrolling logic.
+ *
+ * When started, this connection intercepts scroll events *before* they reach child composables.
+ * This "priority mode" is activated activated when either [canStartPreScroll], [canStartPostScroll]
+ * or [canStartPostFling] returns `true`.
+ *
+ * Once started, the [onStart] lambda provides a [ScrollController] to manage the scrolling. This
+ * controller allows you to directly manipulate the scroll state and define how scroll events are
+ * consumed.
+ *
+ * **Important Considerations:**
+ * - When started, scroll events are typically consumed in `onPreScroll`.
+ * - The provided [ScrollController] should handle potential cancellation of `onStop` due to new
+ * gestures.
+ * - Use [reset] to release the current [ScrollController] and reset the connection to its initial
+ * state.
*
* @param orientation The orientation of the scroll.
- * @param canStartPreScroll lambda that returns true if the connection can start consuming scroll
- * events in pre-scroll mode.
- * @param canStartPostScroll lambda that returns true if the connection can start consuming scroll
- * events in post-scroll mode.
- * @param canStartPostFling lambda that returns true if the connection can start consuming scroll
- * events in post-fling mode.
- * @param canStopOnScroll lambda that returns true if the connection can stop consuming scroll
- * events in scroll mode.
- * @param canStopOnPreFling lambda that returns true if the connection can stop consuming scroll
- * events in pre-fling (i.e. as soon as the user lifts their fingers).
- * @param onStart lambda that is called when the connection starts consuming scroll events.
- * @param onScroll lambda that is called when the connection consumes a scroll event and returns the
- * consumed amount.
- * @param onStop lambda that is called when the connection stops consuming scroll events and returns
- * the consumed velocity.
- * @param onCancel lambda that is called when the connection is cancelled.
+ * @param canStartPreScroll A lambda that returns `true` if the connection should enter priority
+ * mode during the pre-scroll phase. This is called before child connections have a chance to
+ * consume the scroll.
+ * @param canStartPostScroll A lambda that returns `true` if the connection should enter priority
+ * mode during the post-scroll phase. This is called after child connections have consumed the
+ * scroll.
+ * @param canStartPostFling A lambda that returns `true` if the connection should enter priority
+ * mode during the post-fling phase. This is called after a fling gesture has been initiated.
+ * @param onStart A lambda that is called when the connection enters priority mode. It should return
+ * a [ScrollController] that will be used to control the scroll.
* @sample LargeTopAppBarNestedScrollConnection
* @sample com.android.compose.animation.scene.NestedScrollHandlerImpl.nestedScrollConnection
*/
@@ -66,169 +126,213 @@ class PriorityNestedScrollConnection(
private val canStartPostScroll:
(offsetAvailable: Float, offsetBeforeStart: Float, source: NestedScrollSource) -> Boolean,
private val canStartPostFling: (velocityAvailable: Float) -> Boolean,
- private val canStopOnScroll: (available: Float, consumed: Float) -> Boolean = { _, consumed ->
- consumed == 0f
- },
- private val canStopOnPreFling: () -> Boolean,
- private val onStart: (offsetAvailable: Float) -> Unit,
- private val onScroll: (offsetAvailable: Float, source: NestedScrollSource) -> Float,
- private val onStop: suspend (velocityAvailable: Float) -> Float,
- private val onCancel: () -> Unit,
+ private val onStart: (firstScroll: Float) -> ScrollController,
) : NestedScrollConnection, SpaceVectorConverter by SpaceVectorConverter(orientation) {
- /** In priority mode [onPreScroll] events are first consumed by the parent, via [onScroll]. */
- private var isPriorityMode = false
+ /** The currently active [ScrollController], or `null` if not in priority mode. */
+ private var currentController: ScrollController? = null
+
+ /**
+ * A [Deferred] representing the ongoing `onStop` animation. Used to interrupt the animation if
+ * a new gesture occurs.
+ */
+ private var stoppingJob: Deferred<Float>? = null
+ /**
+ * Indicates whether the connection is currently in the process of stopping the scroll with the
+ * [ScrollController.onStop] animation.
+ */
+ private val isStopping
+ get() = stoppingJob?.isActive ?: false
+
+ /**
+ * Tracks the cumulative scroll offset that has been consumed by other composables before this
+ * connection enters priority mode. This is used to determine when the connection should take
+ * over scrolling based on the [canStartPreScroll] and [canStartPostScroll] conditions.
+ */
private var offsetScrolledBeforePriorityMode = 0f
- /** This job allows us to interrupt the onStop animation */
- private var onStopJob: Deferred<Float> = CompletableDeferred(0f)
+ override fun onPreScroll(available: Offset, source: NestedScrollSource): Offset {
+ // If stopping, interrupt the animation and clear the controller.
+ if (isStopping) {
+ interruptStopping()
+ }
+
+ // If in priority mode, consume the scroll using the current controller.
+ if (currentController != null) {
+ return scroll(available.toFloat(), source)
+ }
+
+ // Check if pre-scroll condition is met, and start priority mode if necessary.
+ val availableFloat = available.toFloat()
+ if (canStartPreScroll(availableFloat, offsetScrolledBeforePriorityMode, source)) {
+ start(availableFloat)
+ return scroll(availableFloat, source)
+ }
+
+ // Track offset consumed before entering priority mode.
+ offsetScrolledBeforePriorityMode += availableFloat
+ return Offset.Zero
+ }
override fun onPostScroll(
consumed: Offset,
available: Offset,
source: NestedScrollSource,
): Offset {
+ // If in priority mode, scroll events are consumed only in pre-scroll phase.
+ if (currentController != null) return Offset.Zero
+
+ // Check if post-scroll condition is met, and start priority mode if necessary.
val availableFloat = available.toFloat()
- // The offset before the start takes into account the up and down movements, starting from
- // the beginning or from the last fling gesture.
val offsetBeforeStart = offsetScrolledBeforePriorityMode - availableFloat
-
- if (isPriorityMode || !canStartPostScroll(availableFloat, offsetBeforeStart, source)) {
- // The priority mode cannot start so we won't consume the available offset.
- return Offset.Zero
+ if (canStartPostScroll(availableFloat, offsetBeforeStart, source)) {
+ start(availableFloat)
+ return scroll(availableFloat, source)
}
- return start(availableFloat, source).toOffset()
- }
-
- override fun onPreScroll(available: Offset, source: NestedScrollSource): Offset {
- if (!isPriorityMode) {
- val availableFloat = available.toFloat()
- if (canStartPreScroll(availableFloat, offsetScrolledBeforePriorityMode, source)) {
- return start(availableFloat, source).toOffset()
- }
- // We want to track the amount of offset consumed before entering priority mode
- offsetScrolledBeforePriorityMode += availableFloat
- return Offset.Zero
- }
-
- return scroll(available.toFloat(), source).toOffset()
+ // Do not consume the offset if priority mode is not activated.
+ return Offset.Zero
}
override suspend fun onPreFling(available: Velocity): Velocity {
- if (!isPriorityMode) {
- resetOffsetTracker()
- return Velocity.Zero
- }
+ val controller = currentController ?: return Velocity.Zero
- if (canStopOnPreFling()) {
- // Step 3b: The finger is lifted, we can stop intercepting scroll events and use the
- // velocity of the fling gesture.
- return stop(velocityAvailable = available.toFloat()).toVelocity()
+ // If in priority mode and can stop on pre-fling phase, stop the scroll.
+ if (controller.canStopOnPreFling()) {
+ return stop(velocity = available.toFloat())
}
- // We don't want to consume the velocity, we prefer to continue receiving scroll events.
+ // Do not consume the velocity if not stopping on pre-fling phase.
return Velocity.Zero
}
override suspend fun onPostFling(consumed: Velocity, available: Velocity): Velocity {
val availableFloat = available.toFloat()
- if (isPriorityMode) {
- return stop(velocityAvailable = availableFloat).toVelocity()
- }
+ val controller = currentController
- if (!canStartPostFling(availableFloat)) {
- return Velocity.Zero
+ // If in priority mode, stop the scroll.
+ if (controller != null) {
+ return stop(velocity = availableFloat)
}
- // The offset passed to onPriorityStart() must be != 0f, so we create a small offset of 1px
- // given the available velocity.
+ // Check if post-fling condition is met, and start priority mode if necessary.
// TODO(b/291053278): Remove canStartPostFling() and instead make it possible to define the
// overscroll behavior on the Scene level.
- val smallOffset = availableFloat.sign
- start(
- availableOffset = smallOffset,
- source = NestedScrollSource.SideEffect,
- skipScroll = true,
- )
-
- // This is the last event of a scroll gesture.
- return stop(availableFloat).toVelocity()
+ if (canStartPostFling(availableFloat)) {
+ // The offset passed to onPriorityStart() must be != 0f, so we create a small offset of
+ // 1px given the available velocity.
+ val smallOffset = availableFloat.sign
+ start(availableOffset = smallOffset)
+ return stop(availableFloat)
+ }
+
+ // Reset offset tracking after the fling gesture is finished.
+ resetOffsetTracker()
+ return Velocity.Zero
}
/**
- * Method to call before destroying the object or to reset the initial state.
- *
- * TODO(b/303224944) This method should be removed.
+ * Resets the connection to its initial state. This cancels any ongoing scroll operation and
+ * clears the current [ScrollController].
*/
fun reset() {
- if (isPriorityMode) {
- // Step 3c: To ensure that an onStop (or onCancel) is always called for every onStart.
+ if (currentController != null && !isStopping) {
cancel()
} else {
resetOffsetTracker()
}
}
- private fun start(
- availableOffset: Float,
- source: NestedScrollSource,
- skipScroll: Boolean = false,
- ): Float {
- check(!isPriorityMode) {
- "This should never happen, start() was called when isPriorityMode"
- }
-
- // Step 1: It's our turn! We start capturing scroll events when one of our children has an
- // available offset following a scroll event.
- isPriorityMode = true
+ /**
+ * Starts priority mode by creating a new [ScrollController] using the [onStart] lambda.
+ *
+ * @param availableOffset The initial scroll offset available.
+ */
+ private fun start(availableOffset: Float) {
+ check(currentController == null) { "Another controller is active: $currentController" }
- onStopJob.cancel()
+ resetOffsetTracker()
- // Note: onStop will be called if we cannot continue to scroll (step 3a), or the finger is
- // lifted (step 3b), or this object has been destroyed (step 3c).
- onStart(availableOffset)
+ currentController = onStart(availableOffset)
+ }
- return if (skipScroll) 0f else scroll(availableOffset, source)
+ /**
+ * Retrieves the current [ScrollController], ensuring that it is not null and that the
+ * [isStopping] state matches the expected value.
+ */
+ private fun requireController(isStopping: Boolean): ScrollController {
+ check(this.isStopping == isStopping) {
+ "isStopping is ${this.isStopping}, instead of $isStopping"
+ }
+ check(offsetScrolledBeforePriorityMode == 0f) {
+ "offset scrolled should be zero, but it was $offsetScrolledBeforePriorityMode"
+ }
+ return checkNotNull(currentController) { "The controller is $currentController" }
}
- private fun scroll(offsetAvailable: Float, source: NestedScrollSource): Float {
- // Step 2: We have the priority and can consume the scroll events.
- val consumedByScroll = onScroll(offsetAvailable, source)
+ /**
+ * Scrolls the content using the current [ScrollController].
+ *
+ * @param delta The amount of scroll to apply.
+ * @param source The source of the scroll event.
+ * @return The amount of scroll consumed.
+ */
+ private fun scroll(delta: Float, source: NestedScrollSource): Offset {
+ val controller = requireController(isStopping = false)
+ val consumedByScroll = controller.onScroll(delta, source)
- if (canStopOnScroll(offsetAvailable, consumedByScroll)) {
- // Step 3a: We have lost priority and we no longer need to intercept scroll events.
+ if (controller.canCancelScroll(delta, consumedByScroll)) {
+ // We have lost priority and we no longer need to intercept scroll events.
cancel()
-
- // We've just reset offsetScrolledBeforePriorityMode to 0f
- // We want to track the amount of offset consumed before entering priority mode
- offsetScrolledBeforePriorityMode += offsetAvailable - consumedByScroll
+ offsetScrolledBeforePriorityMode = delta - consumedByScroll
}
- return consumedByScroll
+ return consumedByScroll.toOffset()
}
- /** Reset the tracking of consumed offsets before entering in priority mode. */
- private fun resetOffsetTracker() {
- offsetScrolledBeforePriorityMode = 0f
+ /** Cancels the current scroll operation and clears the current [ScrollController]. */
+ private fun cancel() {
+ requireController(isStopping = false).onCancel()
+ currentController = null
}
- private suspend fun stop(velocityAvailable: Float): Float {
- check(isPriorityMode) { "This should never happen, stop() was called before start()" }
- isPriorityMode = false
- resetOffsetTracker()
-
+ /**
+ * Stops the scroll with the given velocity using the current [ScrollController].
+ *
+ * @param velocity The velocity to stop with.
+ * @return The consumed velocity.
+ */
+ suspend fun stop(velocity: Float): Velocity {
+ val controller = requireController(isStopping = false)
return coroutineScope {
- onStopJob = async { onStop(velocityAvailable) }
- onStopJob.await()
+ try {
+ async { controller.onStop(velocity) }
+ // Allows others to interrupt the job.
+ .also { stoppingJob = it }
+ // Note: this can be cancelled by [interruptStopping]
+ .await()
+ .toVelocity()
+ } finally {
+ // If the job is interrupted, it might take a while to cancel. We need to make sure
+ // the current controller is still the initial one.
+ if (currentController == controller) {
+ currentController = null
+ }
+ }
}
}
- private fun cancel() {
- check(isPriorityMode) { "This should never happen, cancel() was called before start()" }
- isPriorityMode = false
- resetOffsetTracker()
- onCancel()
+ /** Interrupts the ongoing stop animation and clears the current [ScrollController]. */
+ private fun interruptStopping() {
+ requireController(isStopping = true)
+ // We are throwing a CancellationException in the [ScrollController.onStop] method.
+ stoppingJob?.cancel()
+ currentController = null
+ }
+
+ /** Resets the tracking of consumed offsets before entering priority mode. */
+ private fun resetOffsetTracker() {
+ offsetScrolledBeforePriorityMode = 0f
}
}
diff --git a/packages/SystemUI/compose/scene/tests/src/com/android/compose/animation/scene/NestedScrollToSceneTest.kt b/packages/SystemUI/compose/scene/tests/src/com/android/compose/animation/scene/NestedScrollToSceneTest.kt
index 5edb99ea0795..37dae39f935d 100644
--- a/packages/SystemUI/compose/scene/tests/src/com/android/compose/animation/scene/NestedScrollToSceneTest.kt
+++ b/packages/SystemUI/compose/scene/tests/src/com/android/compose/animation/scene/NestedScrollToSceneTest.kt
@@ -225,6 +225,40 @@ class NestedScrollToSceneTest {
}
@Test
+ fun stlNotConsumeUnobservedGesture() {
+ val state =
+ rule.runOnUiThread {
+ MutableSceneTransitionLayoutState(SceneA, transitions = EmptyTestTransitions)
+ }
+
+ rule.setContent {
+ touchSlop = LocalViewConfiguration.current.touchSlop
+ SceneTransitionLayout(
+ state = state,
+ modifier = Modifier.size(layoutWidth, layoutHeight),
+ ) {
+ scene(SceneA) {
+ Spacer(
+ Modifier.verticalNestedScrollToScene()
+ // This scrollable will not consume the gesture.
+ .scrollable(rememberScrollableState { 0f }, Vertical)
+ .fillMaxSize()
+ )
+ }
+ }
+ }
+
+ rule.onRoot().performTouchInput {
+ down(Offset.Zero)
+ // There is no vertical scene.
+ moveBy(Offset(0f, layoutWidth.toPx()), delayMillis = 1_000)
+ }
+
+ rule.waitForIdle()
+ assertThat(state.transitionState).isIdle()
+ }
+
+ @Test
fun customizeStlNestedScrollBehavior_multipleRequests() {
var canScroll = true
val state = setup2ScenesAndScrollTouchSlop {
diff --git a/packages/SystemUI/compose/scene/tests/src/com/android/compose/nestedscroll/PriorityNestedScrollConnectionTest.kt b/packages/SystemUI/compose/scene/tests/src/com/android/compose/nestedscroll/PriorityNestedScrollConnectionTest.kt
index 1a3b86b936df..0364cdc4166e 100644
--- a/packages/SystemUI/compose/scene/tests/src/com/android/compose/nestedscroll/PriorityNestedScrollConnectionTest.kt
+++ b/packages/SystemUI/compose/scene/tests/src/com/android/compose/nestedscroll/PriorityNestedScrollConnectionTest.kt
@@ -20,6 +20,7 @@ package com.android.compose.nestedscroll
import androidx.compose.foundation.gestures.Orientation
import androidx.compose.ui.geometry.Offset
+import androidx.compose.ui.input.nestedscroll.NestedScrollSource
import androidx.compose.ui.input.nestedscroll.NestedScrollSource.Companion.UserInput
import androidx.compose.ui.unit.Velocity
import androidx.test.ext.junit.runners.AndroidJUnit4
@@ -48,17 +49,26 @@ class PriorityNestedScrollConnectionTest {
canStartPreScroll = { _, _, _ -> canStartPreScroll },
canStartPostScroll = { _, _, _ -> canStartPostScroll },
canStartPostFling = { canStartPostFling },
- canStopOnPreFling = { canStopOnPreFling },
- onStart = { isStarted = true },
- onScroll = { offsetAvailable, _ ->
- lastScroll = offsetAvailable
- if (consumeScroll) offsetAvailable else 0f
+ onStart = { _ ->
+ isStarted = true
+ object : ScrollController {
+ override fun onScroll(deltaScroll: Float, source: NestedScrollSource): Float {
+ lastScroll = deltaScroll
+ return if (consumeScroll) deltaScroll else 0f
+ }
+
+ override suspend fun onStop(initialVelocity: Float): Float {
+ lastStop = initialVelocity
+ return if (consumeStop) initialVelocity else 0f
+ }
+
+ override fun onCancel() {
+ isCancelled = true
+ }
+
+ override fun canStopOnPreFling() = canStopOnPreFling
+ }
},
- onStop = {
- lastStop = it
- if (consumeStop) it else 0f
- },
- onCancel = { isCancelled = true },
)
@Test
diff --git a/packages/SystemUI/customization/src/com/android/systemui/shared/clocks/DefaultClockProvider.kt b/packages/SystemUI/customization/src/com/android/systemui/shared/clocks/DefaultClockProvider.kt
index 8e838f396b64..a89e6fb35fa8 100644
--- a/packages/SystemUI/customization/src/com/android/systemui/shared/clocks/DefaultClockProvider.kt
+++ b/packages/SystemUI/customization/src/com/android/systemui/shared/clocks/DefaultClockProvider.kt
@@ -19,12 +19,14 @@ import android.view.LayoutInflater
import com.android.systemui.customization.R
import com.android.systemui.log.core.LogLevel
import com.android.systemui.log.core.LogcatOnlyMessageBuffer
+import com.android.systemui.plugins.clocks.AxisType
import com.android.systemui.plugins.clocks.ClockController
import com.android.systemui.plugins.clocks.ClockId
import com.android.systemui.plugins.clocks.ClockMessageBuffers
import com.android.systemui.plugins.clocks.ClockMetadata
import com.android.systemui.plugins.clocks.ClockPickerConfig
import com.android.systemui.plugins.clocks.ClockProvider
+import com.android.systemui.plugins.clocks.ClockReactiveAxis
import com.android.systemui.plugins.clocks.ClockSettings
import com.android.systemui.shared.clocks.view.HorizontalAlignment
import com.android.systemui.shared.clocks.view.VerticalAlignment
@@ -85,8 +87,21 @@ class DefaultClockProvider(
// TODO(b/352049256): Update placeholder to actual resource
resources.getDrawable(R.drawable.clock_default_thumbnail, null),
isReactiveToTone = true,
- isReactiveToTouch = isClockReactiveVariantsEnabled,
- axes = listOf(), // TODO: Ater some picker definition
+ // TODO(b/364673969): Populate the rest of this
+ axes =
+ if (isClockReactiveVariantsEnabled)
+ listOf(
+ ClockReactiveAxis(
+ key = "wdth",
+ type = AxisType.Slider,
+ maxValue = 1000f,
+ minValue = 100f,
+ currentValue = 400f,
+ name = "Width",
+ description = "Glyph Width",
+ )
+ )
+ else listOf(),
)
}
diff --git a/packages/SystemUI/log/Android.bp b/packages/SystemUI/log/Android.bp
index 2f1d354c3b3e..afdcae481df5 100644
--- a/packages/SystemUI/log/Android.bp
+++ b/packages/SystemUI/log/Android.bp
@@ -22,19 +22,15 @@ package {
default_applicable_licenses: ["frameworks_base_packages_SystemUI_license"],
}
-android_library {
+java_library {
name: "SystemUILogLib",
- use_resource_processor: true,
srcs: [
"src/**/*.java",
"src/**/*.kt",
],
static_libs: [
- "androidx.core_core-ktx",
- "androidx.annotation_annotation",
"error_prone_annotations",
"SystemUICommon",
],
- manifest: "AndroidManifest.xml",
kotlincflags: ["-Xjvm-default=all"],
}
diff --git a/packages/SystemUI/log/AndroidManifest.xml b/packages/SystemUI/log/AndroidManifest.xml
deleted file mode 100644
index 4021e1a5f751..000000000000
--- a/packages/SystemUI/log/AndroidManifest.xml
+++ /dev/null
@@ -1,22 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
- Copyright (C) 2023 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 xmlns:android="http://schemas.android.com/apk/res/android"
- package="com.android.systemui.log">
-
-
-</manifest>
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/ambient/touch/BouncerFullscreenSwipeTouchHandlerTest.java b/packages/SystemUI/multivalentTests/src/com/android/systemui/ambient/touch/BouncerFullscreenSwipeTouchHandlerTest.java
index bd33e52689c2..58c3fec5b45e 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/ambient/touch/BouncerFullscreenSwipeTouchHandlerTest.java
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/ambient/touch/BouncerFullscreenSwipeTouchHandlerTest.java
@@ -20,9 +20,7 @@ import static com.google.common.truth.Truth.assertThat;
import static kotlinx.coroutines.flow.StateFlowKt.MutableStateFlow;
-import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.anyFloat;
-import static org.mockito.Mockito.spy;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
@@ -31,12 +29,11 @@ import android.content.pm.UserInfo;
import android.graphics.Rect;
import android.platform.test.annotations.DisableFlags;
import android.platform.test.annotations.EnableFlags;
-import android.platform.test.flag.junit.FlagsParameterization;
-import android.view.GestureDetector;
import android.view.GestureDetector.OnGestureListener;
import android.view.MotionEvent;
import android.view.VelocityTracker;
+import androidx.test.ext.junit.runners.AndroidJUnit4;
import androidx.test.filters.SmallTest;
import com.android.internal.logging.UiEventLogger;
@@ -45,12 +42,9 @@ import com.android.systemui.SysuiTestCase;
import com.android.systemui.ambient.touch.scrim.ScrimController;
import com.android.systemui.ambient.touch.scrim.ScrimManager;
import com.android.systemui.communal.ui.viewmodel.CommunalViewModel;
-import com.android.systemui.flags.SceneContainerFlagParameterizationKt;
import com.android.systemui.keyguard.domain.interactor.KeyguardInteractor;
import com.android.systemui.kosmos.KosmosJavaAdapter;
import com.android.systemui.plugins.ActivityStarter;
-import com.android.systemui.scene.domain.interactor.SceneInteractor;
-import com.android.systemui.scene.ui.view.WindowRootView;
import com.android.systemui.shared.system.InputChannelCompat;
import com.android.systemui.statusbar.NotificationShadeWindowController;
import com.android.systemui.statusbar.phone.CentralSurfaces;
@@ -64,14 +58,10 @@ import org.mockito.Mock;
import org.mockito.Mockito;
import org.mockito.MockitoAnnotations;
-import java.util.List;
import java.util.Optional;
-import platform.test.runner.parameterized.ParameterizedAndroidJunit4;
-import platform.test.runner.parameterized.Parameters;
-
@SmallTest
-@RunWith(ParameterizedAndroidJunit4.class)
+@RunWith(AndroidJUnit4.class)
@EnableFlags(Flags.FLAG_HUBMODE_FULLSCREEN_VERTICAL_SWIPE_FIX)
@DisableFlags(Flags.FLAG_COMMUNAL_BOUNCER_DO_NOT_MODIFY_PLUGIN_OPEN)
public class BouncerFullscreenSwipeTouchHandlerTest extends SysuiTestCase {
@@ -124,11 +114,6 @@ public class BouncerFullscreenSwipeTouchHandlerTest extends SysuiTestCase {
@Mock
KeyguardInteractor mKeyguardInteractor;
- @Mock
- WindowRootView mWindowRootView;
-
- private SceneInteractor mSceneInteractor;
-
private static final float TOUCH_REGION = .3f;
private static final float MIN_BOUNCER_HEIGHT = .05f;
@@ -139,21 +124,9 @@ public class BouncerFullscreenSwipeTouchHandlerTest extends SysuiTestCase {
/* flags= */ 0
);
- @Parameters(name = "{0}")
- public static List<FlagsParameterization> getParams() {
- return SceneContainerFlagParameterizationKt.parameterizeSceneContainerFlag();
- }
-
- public BouncerFullscreenSwipeTouchHandlerTest(FlagsParameterization flags) {
- super();
- mSetFlagsRule.setFlagsParameterization(flags);
- }
-
@Before
public void setup() {
mKosmos = new KosmosJavaAdapter(this);
- mSceneInteractor = spy(mKosmos.getSceneInteractor());
-
MockitoAnnotations.initMocks(this);
mTouchHandler = new BouncerSwipeTouchHandler(
mKosmos.getTestScope(),
@@ -169,9 +142,7 @@ public class BouncerFullscreenSwipeTouchHandlerTest extends SysuiTestCase {
MIN_BOUNCER_HEIGHT,
mUiEventLogger,
mActivityStarter,
- mKeyguardInteractor,
- mSceneInteractor,
- Optional.of(() -> mWindowRootView));
+ mKeyguardInteractor);
when(mScrimManager.getCurrentController()).thenReturn(mScrimController);
when(mValueAnimatorCreator.create(anyFloat(), anyFloat())).thenReturn(mValueAnimator);
@@ -182,38 +153,6 @@ public class BouncerFullscreenSwipeTouchHandlerTest extends SysuiTestCase {
}
/**
- * Makes sure that touches go to the scene container when the flag is on.
- */
- @Test
- @EnableFlags(Flags.FLAG_SCENE_CONTAINER)
- public void testSwipeUp_sendsTouchesToWindowRootView() {
- mTouchHandler.onGlanceableTouchAvailable(true);
- mTouchHandler.onSessionStart(mTouchSession);
- ArgumentCaptor<GestureDetector.OnGestureListener> gestureListenerCaptor =
- ArgumentCaptor.forClass(GestureDetector.OnGestureListener.class);
- verify(mTouchSession).registerGestureListener(gestureListenerCaptor.capture());
-
- final OnGestureListener gestureListener = gestureListenerCaptor.getValue();
-
- final int screenHeight = 100;
- final float distanceY = screenHeight * 0.42f;
-
- final MotionEvent event1 = MotionEvent.obtain(0, 0, MotionEvent.ACTION_MOVE,
- 0, screenHeight, 0);
- final MotionEvent event2 = MotionEvent.obtain(0, 0, MotionEvent.ACTION_MOVE,
- 0, screenHeight - distanceY, 0);
-
- assertThat(gestureListener.onScroll(event1, event2, 0,
- distanceY))
- .isTrue();
-
- // Ensure only called once
- verify(mSceneInteractor).onRemoteUserInputStarted(any());
- verify(mWindowRootView).dispatchTouchEvent(event1);
- verify(mWindowRootView).dispatchTouchEvent(event2);
- }
-
- /**
* Ensures expansion does not happen for full vertical swipes when touch is not available.
*/
@Test
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/ambient/touch/BouncerSwipeTouchHandlerTest.java b/packages/SystemUI/multivalentTests/src/com/android/systemui/ambient/touch/BouncerSwipeTouchHandlerTest.java
index 494e0b4deef4..95681671b545 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/ambient/touch/BouncerSwipeTouchHandlerTest.java
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/ambient/touch/BouncerSwipeTouchHandlerTest.java
@@ -26,7 +26,6 @@ import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.ArgumentMatchers.isNull;
import static org.mockito.Mockito.never;
import static org.mockito.Mockito.reset;
-import static org.mockito.Mockito.spy;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.verifyNoMoreInteractions;
import static org.mockito.Mockito.when;
@@ -38,12 +37,12 @@ import android.graphics.Rect;
import android.graphics.Region;
import android.platform.test.annotations.DisableFlags;
import android.platform.test.annotations.EnableFlags;
-import android.platform.test.flag.junit.FlagsParameterization;
import android.view.GestureDetector;
import android.view.GestureDetector.OnGestureListener;
import android.view.MotionEvent;
import android.view.VelocityTracker;
+import androidx.test.ext.junit.runners.AndroidJUnit4;
import androidx.test.filters.SmallTest;
import com.android.internal.logging.UiEventLogger;
@@ -53,12 +52,9 @@ import com.android.systemui.ambient.touch.scrim.ScrimController;
import com.android.systemui.ambient.touch.scrim.ScrimManager;
import com.android.systemui.bouncer.shared.constants.KeyguardBouncerConstants;
import com.android.systemui.communal.ui.viewmodel.CommunalViewModel;
-import com.android.systemui.flags.SceneContainerFlagParameterizationKt;
import com.android.systemui.keyguard.domain.interactor.KeyguardInteractor;
import com.android.systemui.kosmos.KosmosJavaAdapter;
import com.android.systemui.plugins.ActivityStarter;
-import com.android.systemui.scene.domain.interactor.SceneInteractor;
-import com.android.systemui.scene.ui.view.WindowRootView;
import com.android.systemui.shade.ShadeExpansionChangeEvent;
import com.android.systemui.shared.system.InputChannelCompat;
import com.android.systemui.statusbar.NotificationShadeWindowController;
@@ -74,14 +70,10 @@ import org.mockito.Mock;
import org.mockito.Mockito;
import org.mockito.MockitoAnnotations;
-import java.util.List;
import java.util.Optional;
-import platform.test.runner.parameterized.ParameterizedAndroidJunit4;
-import platform.test.runner.parameterized.Parameters;
-
@SmallTest
-@RunWith(ParameterizedAndroidJunit4.class)
+@RunWith(AndroidJUnit4.class)
@DisableFlags(Flags.FLAG_HUBMODE_FULLSCREEN_VERTICAL_SWIPE_FIX)
public class BouncerSwipeTouchHandlerTest extends SysuiTestCase {
private KosmosJavaAdapter mKosmos;
@@ -130,9 +122,6 @@ public class BouncerSwipeTouchHandlerTest extends SysuiTestCase {
Region mRegion;
@Mock
- WindowRootView mWindowRootView;
-
- @Mock
CommunalViewModel mCommunalViewModel;
@Mock
@@ -141,8 +130,6 @@ public class BouncerSwipeTouchHandlerTest extends SysuiTestCase {
@Captor
ArgumentCaptor<Rect> mRectCaptor;
- private SceneInteractor mSceneInteractor;
-
private static final float TOUCH_REGION = .3f;
private static final int SCREEN_WIDTH_PX = 1024;
private static final int SCREEN_HEIGHT_PX = 100;
@@ -155,21 +142,9 @@ public class BouncerSwipeTouchHandlerTest extends SysuiTestCase {
/* flags= */ 0
);
- @Parameters(name = "{0}")
- public static List<FlagsParameterization> getParams() {
- return SceneContainerFlagParameterizationKt.parameterizeSceneContainerFlag();
- }
-
- public BouncerSwipeTouchHandlerTest(FlagsParameterization flags) {
- super();
- mSetFlagsRule.setFlagsParameterization(flags);
- }
-
@Before
public void setup() {
mKosmos = new KosmosJavaAdapter(this);
- mSceneInteractor = spy(mKosmos.getSceneInteractor());
-
MockitoAnnotations.initMocks(this);
mTouchHandler = new BouncerSwipeTouchHandler(
mKosmos.getTestScope(),
@@ -185,10 +160,7 @@ public class BouncerSwipeTouchHandlerTest extends SysuiTestCase {
MIN_BOUNCER_HEIGHT,
mUiEventLogger,
mActivityStarter,
- mKeyguardInteractor,
- mSceneInteractor,
- Optional.of(() -> mWindowRootView)
- );
+ mKeyguardInteractor);
when(mScrimManager.getCurrentController()).thenReturn(mScrimController);
when(mValueAnimatorCreator.create(anyFloat(), anyFloat())).thenReturn(mValueAnimator);
@@ -395,7 +367,6 @@ public class BouncerSwipeTouchHandlerTest extends SysuiTestCase {
* Makes sure the expansion amount is proportional to (1 - scroll).
*/
@Test
- @DisableFlags(Flags.FLAG_SCENE_CONTAINER)
public void testSwipeUp_setsCorrectExpansionAmount() {
mTouchHandler.onSessionStart(mTouchSession);
ArgumentCaptor<GestureDetector.OnGestureListener> gestureListenerCaptor =
@@ -409,36 +380,6 @@ public class BouncerSwipeTouchHandlerTest extends SysuiTestCase {
}
/**
- * Makes sure that touches go to the scene container when the flag is on.
- */
- @Test
- @EnableFlags(Flags.FLAG_SCENE_CONTAINER)
- public void testSwipeUp_sendsTouchesToWindowRootView() {
- mTouchHandler.onSessionStart(mTouchSession);
- ArgumentCaptor<GestureDetector.OnGestureListener> gestureListenerCaptor =
- ArgumentCaptor.forClass(GestureDetector.OnGestureListener.class);
- verify(mTouchSession).registerGestureListener(gestureListenerCaptor.capture());
-
- final OnGestureListener gestureListener = gestureListenerCaptor.getValue();
-
- final float distanceY = SCREEN_HEIGHT_PX * 0.42f;
-
- final MotionEvent event1 = MotionEvent.obtain(0, 0, MotionEvent.ACTION_MOVE,
- 0, SCREEN_HEIGHT_PX, 0);
- final MotionEvent event2 = MotionEvent.obtain(0, 0, MotionEvent.ACTION_MOVE,
- 0, SCREEN_HEIGHT_PX - distanceY, 0);
-
- assertThat(gestureListener.onScroll(event1, event2, 0,
- distanceY))
- .isTrue();
-
- // Ensure only called once
- verify(mSceneInteractor).onRemoteUserInputStarted(any());
- verify(mWindowRootView).dispatchTouchEvent(event1);
- verify(mWindowRootView).dispatchTouchEvent(event2);
- }
-
- /**
* Verifies that swiping up when the lock pattern is not secure dismissed dream and consumes
* the gesture.
*/
@@ -535,7 +476,6 @@ public class BouncerSwipeTouchHandlerTest extends SysuiTestCase {
* Tests that ending an upward swipe before the set threshold leads to bouncer collapsing down.
*/
@Test
- @DisableFlags(Flags.FLAG_SCENE_CONTAINER)
public void testSwipeUpPositionBelowThreshold_collapsesBouncer() {
final float swipeUpPercentage = .3f;
final float expansion = 1 - swipeUpPercentage;
@@ -559,7 +499,6 @@ public class BouncerSwipeTouchHandlerTest extends SysuiTestCase {
* Tests that ending an upward swipe above the set threshold will continue the expansion.
*/
@Test
- @DisableFlags(Flags.FLAG_SCENE_CONTAINER)
public void testSwipeUpPositionAboveThreshold_expandsBouncer() {
final float swipeUpPercentage = .7f;
final float expansion = 1 - swipeUpPercentage;
@@ -589,7 +528,6 @@ public class BouncerSwipeTouchHandlerTest extends SysuiTestCase {
* Tests that swiping up with a speed above the set threshold will continue the expansion.
*/
@Test
- @DisableFlags(Flags.FLAG_SCENE_CONTAINER)
public void testSwipeUpVelocityAboveMin_expandsBouncer() {
when(mFlingAnimationUtils.getMinVelocityPxPerSecond()).thenReturn((float) 0);
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/ambient/touch/ShadeTouchHandlerTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/ambient/touch/ShadeTouchHandlerTest.kt
index ad636cf613ad..38ea44976175 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/ambient/touch/ShadeTouchHandlerTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/ambient/touch/ShadeTouchHandlerTest.kt
@@ -18,9 +18,9 @@ package com.android.systemui.ambient.touch
import android.app.DreamManager
import android.platform.test.annotations.DisableFlags
import android.platform.test.annotations.EnableFlags
-import android.platform.test.flag.junit.FlagsParameterization
import android.view.GestureDetector
import android.view.MotionEvent
+import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.SmallTest
import com.android.systemui.Flags
import com.android.systemui.SysuiTestCase
@@ -28,20 +28,14 @@ import com.android.systemui.ambient.touch.TouchHandler.TouchSession
import com.android.systemui.communal.domain.interactor.communalSettingsInteractor
import com.android.systemui.communal.ui.viewmodel.CommunalViewModel
import com.android.systemui.flags.Flags.COMMUNAL_SERVICE_ENABLED
-import com.android.systemui.flags.andSceneContainer
import com.android.systemui.flags.fakeFeatureFlagsClassic
import com.android.systemui.kosmos.testScope
-import com.android.systemui.scene.data.repository.sceneContainerRepository
-import com.android.systemui.scene.domain.interactor.sceneInteractor
-import com.android.systemui.scene.ui.view.WindowRootView
import com.android.systemui.shade.ShadeViewController
import com.android.systemui.shared.system.InputChannelCompat
import com.android.systemui.statusbar.phone.CentralSurfaces
import com.android.systemui.testKosmos
import com.google.common.truth.Truth
-import com.google.common.truth.Truth.assertThat
import java.util.Optional
-import javax.inject.Provider
import org.junit.Before
import org.junit.Test
import org.junit.runner.RunWith
@@ -53,29 +47,22 @@ import org.mockito.kotlin.never
import org.mockito.kotlin.times
import org.mockito.kotlin.verify
import org.mockito.kotlin.whenever
-import platform.test.runner.parameterized.ParameterizedAndroidJunit4
-import platform.test.runner.parameterized.Parameters
@SmallTest
-@RunWith(ParameterizedAndroidJunit4::class)
-class ShadeTouchHandlerTest(flags: FlagsParameterization) : SysuiTestCase() {
+@RunWith(AndroidJUnit4::class)
+class ShadeTouchHandlerTest : SysuiTestCase() {
private var kosmos = testKosmos()
private var mCentralSurfaces = mock<CentralSurfaces>()
private var mShadeViewController = mock<ShadeViewController>()
private var mDreamManager = mock<DreamManager>()
private var mTouchSession = mock<TouchSession>()
private var communalViewModel = mock<CommunalViewModel>()
- private var windowRootView = mock<WindowRootView>()
private lateinit var mTouchHandler: ShadeTouchHandler
private var mGestureListenerCaptor = argumentCaptor<GestureDetector.OnGestureListener>()
private var mInputListenerCaptor = argumentCaptor<InputChannelCompat.InputEventListener>()
- init {
- mSetFlagsRule.setFlagsParameterization(flags)
- }
-
@Before
fun setup() {
mTouchHandler =
@@ -86,9 +73,7 @@ class ShadeTouchHandlerTest(flags: FlagsParameterization) : SysuiTestCase() {
mDreamManager,
communalViewModel,
kosmos.communalSettingsInteractor,
- kosmos.sceneInteractor,
- Optional.of(Provider<WindowRootView> { windowRootView }),
- TOUCH_HEIGHT,
+ TOUCH_HEIGHT
)
}
@@ -112,7 +97,7 @@ class ShadeTouchHandlerTest(flags: FlagsParameterization) : SysuiTestCase() {
// Verifies that a swipe down forwards captured touches to central surfaces for handling.
@Test
- @DisableFlags(Flags.FLAG_HUBMODE_FULLSCREEN_VERTICAL_SWIPE_FIX, Flags.FLAG_SCENE_CONTAINER)
+ @DisableFlags(Flags.FLAG_HUBMODE_FULLSCREEN_VERTICAL_SWIPE_FIX)
@EnableFlags(Flags.FLAG_COMMUNAL_HUB)
fun testSwipeDown_communalEnabled_sentToCentralSurfaces() {
kosmos.fakeFeatureFlagsClassic.set(COMMUNAL_SERVICE_ENABLED, true)
@@ -125,11 +110,7 @@ class ShadeTouchHandlerTest(flags: FlagsParameterization) : SysuiTestCase() {
// Verifies that a swipe down forwards captured touches to the shade view for handling.
@Test
- @DisableFlags(
- Flags.FLAG_COMMUNAL_HUB,
- Flags.FLAG_HUBMODE_FULLSCREEN_VERTICAL_SWIPE_FIX,
- Flags.FLAG_SCENE_CONTAINER,
- )
+ @DisableFlags(Flags.FLAG_COMMUNAL_HUB, Flags.FLAG_HUBMODE_FULLSCREEN_VERTICAL_SWIPE_FIX)
fun testSwipeDown_communalDisabled_sentToShadeView() {
swipe(Direction.DOWN)
@@ -140,7 +121,7 @@ class ShadeTouchHandlerTest(flags: FlagsParameterization) : SysuiTestCase() {
// Verifies that a swipe down while dreaming forwards captured touches to the shade view for
// handling.
@Test
- @DisableFlags(Flags.FLAG_HUBMODE_FULLSCREEN_VERTICAL_SWIPE_FIX, Flags.FLAG_SCENE_CONTAINER)
+ @DisableFlags(Flags.FLAG_HUBMODE_FULLSCREEN_VERTICAL_SWIPE_FIX)
fun testSwipeDown_dreaming_sentToShadeView() {
whenever(mDreamManager.isDreaming).thenReturn(true)
swipe(Direction.DOWN)
@@ -149,34 +130,9 @@ class ShadeTouchHandlerTest(flags: FlagsParameterization) : SysuiTestCase() {
verify(mShadeViewController, times(2)).handleExternalTouch(any())
}
- // Verifies that a swipe down forwards captured touches to the window root view for handling.
- @Test
- @EnableFlags(Flags.FLAG_COMMUNAL_HUB, Flags.FLAG_SCENE_CONTAINER)
- fun testSwipeDown_sceneContainerEnabled_sentToWindowRootView() {
- swipe(Direction.DOWN)
-
- // Both motion events are sent for central surfaces to process.
- assertThat(kosmos.sceneContainerRepository.isRemoteUserInputOngoing.value).isTrue()
- verify(windowRootView, times(2)).dispatchTouchEvent(any())
- }
-
- // Verifies that a swipe down while dreaming forwards captured touches to the window root view
- // for handling.
- @Test
- @EnableFlags(Flags.FLAG_SCENE_CONTAINER)
- @DisableFlags(Flags.FLAG_HUBMODE_FULLSCREEN_VERTICAL_SWIPE_FIX)
- fun testSwipeDown_dreaming_sentToWindowRootView() {
- whenever(mDreamManager.isDreaming).thenReturn(true)
- swipe(Direction.DOWN)
-
- // Both motion events are sent for the shade view to process.
- assertThat(kosmos.sceneContainerRepository.isRemoteUserInputOngoing.value).isTrue()
- verify(windowRootView, times(2)).dispatchTouchEvent(any())
- }
-
// Verifies that a swipe up is not forwarded to central surfaces.
@Test
- @DisableFlags(Flags.FLAG_HUBMODE_FULLSCREEN_VERTICAL_SWIPE_FIX, Flags.FLAG_SCENE_CONTAINER)
+ @DisableFlags(Flags.FLAG_HUBMODE_FULLSCREEN_VERTICAL_SWIPE_FIX)
@EnableFlags(Flags.FLAG_COMMUNAL_HUB)
fun testSwipeUp_communalEnabled_touchesNotSent() {
kosmos.fakeFeatureFlagsClassic.set(COMMUNAL_SERVICE_ENABLED, true)
@@ -190,11 +146,7 @@ class ShadeTouchHandlerTest(flags: FlagsParameterization) : SysuiTestCase() {
// Verifies that a swipe up is not forwarded to the shade view.
@Test
- @DisableFlags(
- Flags.FLAG_COMMUNAL_HUB,
- Flags.FLAG_HUBMODE_FULLSCREEN_VERTICAL_SWIPE_FIX,
- Flags.FLAG_SCENE_CONTAINER,
- )
+ @DisableFlags(Flags.FLAG_COMMUNAL_HUB, Flags.FLAG_HUBMODE_FULLSCREEN_VERTICAL_SWIPE_FIX)
fun testSwipeUp_communalDisabled_touchesNotSent() {
swipe(Direction.UP)
@@ -203,17 +155,6 @@ class ShadeTouchHandlerTest(flags: FlagsParameterization) : SysuiTestCase() {
verify(mShadeViewController, never()).handleExternalTouch(any())
}
- // Verifies that a swipe up is not forwarded to the window root view.
- @Test
- @EnableFlags(Flags.FLAG_COMMUNAL_HUB, Flags.FLAG_SCENE_CONTAINER)
- fun testSwipeUp_sceneContainerEnabled_touchesNotSent() {
- swipe(Direction.UP)
-
- // Motion events are not sent for window root view to process as the swipe is going in the
- // wrong direction.
- verify(windowRootView, never()).dispatchTouchEvent(any())
- }
-
@Test
@DisableFlags(Flags.FLAG_HUBMODE_FULLSCREEN_VERTICAL_SWIPE_FIX)
fun testCancelMotionEvent_popsTouchSession() {
@@ -302,16 +243,10 @@ class ShadeTouchHandlerTest(flags: FlagsParameterization) : SysuiTestCase() {
private enum class Direction {
DOWN,
- UP,
+ UP
}
companion object {
private const val TOUCH_HEIGHT = 20
-
- @JvmStatic
- @Parameters(name = "{0}")
- fun getParams(): List<FlagsParameterization> {
- return FlagsParameterization.allCombinationsOf().andSceneContainer()
- }
}
}
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/domain/interactor/KeyguardLockWhileAwakeInteractorTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/domain/interactor/KeyguardLockWhileAwakeInteractorTest.kt
new file mode 100644
index 000000000000..bd26e4205242
--- /dev/null
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/domain/interactor/KeyguardLockWhileAwakeInteractorTest.kt
@@ -0,0 +1,101 @@
+/*
+ * 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.
+ */
+
+package com.android.systemui.keyguard.domain.interactor
+
+import androidx.test.ext.junit.runners.AndroidJUnit4
+import androidx.test.filters.SmallTest
+import com.android.systemui.SysuiTestCase
+import com.android.systemui.coroutines.collectValues
+import com.android.systemui.keyguard.data.repository.biometricSettingsRepository
+import com.android.systemui.keyguard.data.repository.fakeKeyguardTransitionRepository
+import com.android.systemui.keyguard.shared.model.KeyguardState
+import com.android.systemui.kosmos.testScope
+import com.android.systemui.testKosmos
+import com.google.common.truth.Truth.assertThat
+import kotlin.test.Test
+import kotlin.test.assertEquals
+import kotlinx.coroutines.test.advanceTimeBy
+import kotlinx.coroutines.test.runCurrent
+import kotlinx.coroutines.test.runTest
+import org.junit.Before
+import org.junit.runner.RunWith
+
+@SmallTest
+@RunWith(AndroidJUnit4::class)
+@kotlinx.coroutines.ExperimentalCoroutinesApi
+class KeyguardLockWhileAwakeInteractorTest : SysuiTestCase() {
+ private val kosmos = testKosmos()
+ private val testScope = kosmos.testScope
+ private lateinit var underTest: KeyguardLockWhileAwakeInteractor
+
+ @Before
+ fun setup() {
+ underTest = kosmos.keyguardLockWhileAwakeInteractor
+ }
+
+ @Test
+ fun emitsMultipleTimeoutEvents() =
+ testScope.runTest {
+ val values by collectValues(underTest.lockWhileAwakeEvents)
+
+ underTest.onKeyguardServiceDoKeyguardTimeout(options = null)
+ runCurrent()
+
+ assertThat(values)
+ .containsExactly(LockWhileAwakeReason.KEYGUARD_TIMEOUT_WHILE_SCREEN_ON)
+
+ advanceTimeBy(1000)
+ underTest.onKeyguardServiceDoKeyguardTimeout(options = null)
+ runCurrent()
+
+ assertThat(values)
+ .containsExactly(
+ LockWhileAwakeReason.KEYGUARD_TIMEOUT_WHILE_SCREEN_ON,
+ LockWhileAwakeReason.KEYGUARD_TIMEOUT_WHILE_SCREEN_ON,
+ )
+ }
+
+ @Test
+ fun emitsWhenKeyguardEnabled_onlyIfShowingWhenDisabled() =
+ testScope.runTest {
+ val values by collectValues(underTest.lockWhileAwakeEvents)
+
+ kosmos.biometricSettingsRepository.setIsUserInLockdown(false)
+ runCurrent()
+
+ kosmos.keyguardEnabledInteractor.notifyKeyguardEnabled(false)
+ runCurrent()
+
+ assertEquals(0, values.size)
+
+ kosmos.keyguardEnabledInteractor.notifyKeyguardEnabled(true)
+ runCurrent()
+
+ assertThat(values).containsExactly(LockWhileAwakeReason.KEYGUARD_REENABLED)
+
+ kosmos.fakeKeyguardTransitionRepository.sendTransitionSteps(
+ from = KeyguardState.LOCKSCREEN,
+ to = KeyguardState.GONE,
+ testScope = testScope,
+ )
+ kosmos.keyguardEnabledInteractor.notifyKeyguardEnabled(false)
+ kosmos.keyguardEnabledInteractor.notifyKeyguardEnabled(true)
+ runCurrent()
+
+ assertThat(values).containsExactly(LockWhileAwakeReason.KEYGUARD_REENABLED)
+ }
+}
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/domain/interactor/KeyguardQuickAffordanceInteractorParameterizedTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/domain/interactor/KeyguardQuickAffordanceInteractorParameterizedTest.kt
index eef4c3de4b19..83d2617fd343 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/domain/interactor/KeyguardQuickAffordanceInteractorParameterizedTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/domain/interactor/KeyguardQuickAffordanceInteractorParameterizedTest.kt
@@ -20,7 +20,6 @@ package com.android.systemui.keyguard.domain.interactor
import android.app.admin.DevicePolicyManager
import android.content.Intent
import android.os.UserHandle
-import androidx.test.filters.FlakyTest
import androidx.test.filters.SmallTest
import com.android.internal.widget.LockPatternUtils
import com.android.keyguard.logging.KeyguardQuickAffordancesLogger
@@ -79,10 +78,6 @@ import platform.test.runner.parameterized.ParameterizedAndroidJunit4
import platform.test.runner.parameterized.Parameters
@OptIn(ExperimentalCoroutinesApi::class)
-@FlakyTest(
- bugId = 292574995,
- detail = "on certain architectures all permutations with startActivity=true is causing failures"
-)
@SmallTest
@RunWith(ParameterizedAndroidJunit4::class)
@DisableSceneContainer
@@ -93,11 +88,7 @@ class KeyguardQuickAffordanceInteractorParameterizedTest : SysuiTestCase() {
private val DRAWABLE =
mock<Icon> {
whenever(this.contentDescription)
- .thenReturn(
- ContentDescription.Resource(
- res = CONTENT_DESCRIPTION_RESOURCE_ID,
- )
- )
+ .thenReturn(ContentDescription.Resource(res = CONTENT_DESCRIPTION_RESOURCE_ID))
}
private const val CONTENT_DESCRIPTION_RESOURCE_ID = 1337
@@ -273,13 +264,7 @@ class KeyguardQuickAffordanceInteractorParameterizedTest : SysuiTestCase() {
context = context,
userFileManager =
mock<UserFileManager>().apply {
- whenever(
- getSharedPreferences(
- anyString(),
- anyInt(),
- anyInt(),
- )
- )
+ whenever(getSharedPreferences(anyString(), anyInt(), anyInt()))
.thenReturn(FakeSharedPreferences())
},
userTracker = userTracker,
@@ -316,9 +301,7 @@ class KeyguardQuickAffordanceInteractorParameterizedTest : SysuiTestCase() {
underTest =
KeyguardQuickAffordanceInteractor(
keyguardInteractor =
- KeyguardInteractorFactory.create(
- featureFlags = featureFlags,
- )
+ KeyguardInteractorFactory.create(featureFlags = featureFlags)
.keyguardInteractor,
shadeInteractor = kosmos.shadeInteractor,
lockPatternUtils = lockPatternUtils,
@@ -350,9 +333,7 @@ class KeyguardQuickAffordanceInteractorParameterizedTest : SysuiTestCase() {
homeControls.setState(
lockScreenState =
- KeyguardQuickAffordanceConfig.LockScreenState.Visible(
- icon = DRAWABLE,
- )
+ KeyguardQuickAffordanceConfig.LockScreenState.Visible(icon = DRAWABLE)
)
homeControls.onTriggeredResult =
if (startActivity) {
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/domain/interactor/KeyguardTransitionScenariosTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/domain/interactor/KeyguardTransitionScenariosTest.kt
index f4d2ea018a44..c8a16483a00c 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/domain/interactor/KeyguardTransitionScenariosTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/domain/interactor/KeyguardTransitionScenariosTest.kt
@@ -560,7 +560,9 @@ class KeyguardTransitionScenariosTest(flags: FlagsParameterization?) : SysuiTest
.startedTransition(
to = KeyguardState.LOCKSCREEN,
from = KeyguardState.GONE,
- ownerName = "FromGoneTransitionInteractor",
+ ownerName =
+ "FromGoneTransitionInteractor" +
+ "(keyguard interactor says keyguard is showing)",
animatorAssertion = { it.isNotNull() },
)
@@ -640,7 +642,9 @@ class KeyguardTransitionScenariosTest(flags: FlagsParameterization?) : SysuiTest
.startedTransition(
to = KeyguardState.GLANCEABLE_HUB,
from = KeyguardState.GONE,
- ownerName = FromGoneTransitionInteractor::class.simpleName,
+ ownerName =
+ FromGoneTransitionInteractor::class.simpleName +
+ "(keyguard interactor says keyguard is showing)",
animatorAssertion = { it.isNotNull() },
)
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/QSPanelControllerBaseTest.java b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/QSPanelControllerBaseTest.java
index 527aeaac426d..83f95eaf4cda 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/QSPanelControllerBaseTest.java
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/QSPanelControllerBaseTest.java
@@ -28,6 +28,8 @@ import static org.junit.Assert.assertEquals;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.anyBoolean;
import static org.mockito.ArgumentMatchers.anyFloat;
+import static org.mockito.ArgumentMatchers.anyInt;
+import static org.mockito.ArgumentMatchers.anyString;
import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.Mockito.clearInvocations;
import static org.mockito.Mockito.doAnswer;
@@ -604,6 +606,46 @@ public class QSPanelControllerBaseTest extends SysuiTestCase {
assertThat(mPagedTileLayoutListening).isFalse();
}
+ @Test
+ public void reAttach_configurationChangePending_triggersConfigurationListener() {
+ mController.onViewDetached();
+
+ when(mQSPanel.hadConfigurationChangeWhileDetached()).thenReturn(true);
+ clearInvocations(mQSLogger);
+
+ mController.onViewAttached();
+
+ verify(mQSLogger).logOnConfigurationChanged(
+ anyInt(),
+ anyInt(),
+ anyBoolean(),
+ anyBoolean(),
+ anyInt(),
+ anyInt(),
+ anyString()
+ );
+ }
+
+ @Test
+ public void reAttach_noConfigurationChangePending_doesntTriggerConfigurationListener() {
+ mController.onViewDetached();
+
+ when(mQSPanel.hadConfigurationChangeWhileDetached()).thenReturn(false);
+ clearInvocations(mQSLogger);
+
+ mController.onViewAttached();
+
+ verify(mQSLogger, never()).logOnConfigurationChanged(
+ anyInt(),
+ anyInt(),
+ anyBoolean(),
+ anyBoolean(),
+ anyInt(),
+ anyInt(),
+ anyString()
+ );
+ }
+
private boolean usingMediaPlayer() {
return !SceneContainerFlag.isEnabled();
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/irecording/IssueRecordingDataInteractorTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/irecording/IssueRecordingDataInteractorTest.kt
index 2194c75dba5b..f3fc0ab92c84 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/irecording/IssueRecordingDataInteractorTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/irecording/IssueRecordingDataInteractorTest.kt
@@ -16,6 +16,7 @@
package com.android.systemui.qs.tiles.impl.irecording
+import android.os.Handler
import android.os.UserHandle
import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.SmallTest
@@ -28,6 +29,7 @@ import com.android.systemui.qs.tiles.base.interactor.DataUpdateTrigger
import com.android.systemui.recordissue.IssueRecordingState
import com.android.systemui.settings.fakeUserFileManager
import com.android.systemui.settings.userTracker
+import com.android.systemui.util.settings.fakeGlobalSettings
import com.google.common.truth.Truth
import kotlinx.coroutines.ExperimentalCoroutinesApi
import kotlinx.coroutines.flow.flowOf
@@ -51,7 +53,14 @@ class IssueRecordingDataInteractorTest : SysuiTestCase() {
@Before
fun setup() {
- state = IssueRecordingState(userTracker, userFileManager)
+ state =
+ IssueRecordingState(
+ userTracker,
+ userFileManager,
+ Handler.getMain(),
+ mContext.contentResolver,
+ kosmos.fakeGlobalSettings,
+ )
underTest = IssueRecordingDataInteractor(state, kosmos.testScope.testScheduler)
}
@@ -67,10 +76,12 @@ class IssueRecordingDataInteractorTest : SysuiTestCase() {
Truth.assertThat(data?.isRecording).isFalse()
state.isRecording = true
+ state.onRecordingChangeListener.onChange(true)
runCurrent()
Truth.assertThat(data?.isRecording).isTrue()
state.isRecording = false
+ state.onRecordingChangeListener.onChange(true)
runCurrent()
Truth.assertThat(data?.isRecording).isFalse()
}
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/irecording/IssueRecordingUserActionInteractorTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/irecording/IssueRecordingUserActionInteractorTest.kt
index 99d2da670172..9c2be899b8ca 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/irecording/IssueRecordingUserActionInteractorTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/irecording/IssueRecordingUserActionInteractorTest.kt
@@ -16,6 +16,7 @@
package com.android.systemui.qs.tiles.impl.irecording
+import android.os.Handler
import android.os.UserHandle
import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.SmallTest
@@ -38,6 +39,7 @@ import com.android.systemui.settings.userFileManager
import com.android.systemui.settings.userTracker
import com.android.systemui.statusbar.phone.KeyguardDismissUtil
import com.android.systemui.statusbar.policy.keyguardStateController
+import com.android.systemui.util.settings.fakeGlobalSettings
import com.google.common.truth.Truth
import kotlinx.coroutines.test.runTest
import org.junit.Before
@@ -83,7 +85,13 @@ class IssueRecordingUserActionInteractorTest : SysuiTestCase() {
underTest =
IssueRecordingUserActionInteractor(
testDispatcher,
- IssueRecordingState(userTracker, userFileManager),
+ IssueRecordingState(
+ userTracker,
+ userFileManager,
+ Handler.getMain(),
+ mContext.contentResolver,
+ kosmos.fakeGlobalSettings,
+ ),
KeyguardDismissUtil(
keyguardStateController,
statusBarStateController,
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/recordissue/IssueRecordingServiceSessionTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/recordissue/IssueRecordingServiceSessionTest.kt
index aceaab8206a2..f2e658dc3759 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/recordissue/IssueRecordingServiceSessionTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/recordissue/IssueRecordingServiceSessionTest.kt
@@ -19,6 +19,7 @@ package com.android.systemui.recordissue
import android.app.IActivityManager
import android.app.NotificationManager
import android.net.Uri
+import android.os.Handler
import android.os.UserHandle
import android.testing.TestableLooper
import androidx.test.ext.junit.runners.AndroidJUnit4
@@ -33,6 +34,7 @@ import com.android.systemui.qs.pipeline.domain.interactor.PanelInteractor
import com.android.systemui.settings.UserContextProvider
import com.android.systemui.settings.userFileManager
import com.android.systemui.settings.userTracker
+import com.android.systemui.util.settings.fakeGlobalSettings
import com.android.traceur.TraceConfig
import com.google.common.truth.Truth
import org.junit.Before
@@ -55,7 +57,13 @@ class IssueRecordingServiceSessionTest : SysuiTestCase() {
private val dialogTransitionAnimator: DialogTransitionAnimator = kosmos.dialogTransitionAnimator
private lateinit var traceurConnection: TraceurConnection
private val issueRecordingState =
- IssueRecordingState(kosmos.userTracker, kosmos.userFileManager)
+ IssueRecordingState(
+ kosmos.userTracker,
+ kosmos.userFileManager,
+ Handler.getMain(),
+ mContext.contentResolver,
+ kosmos.fakeGlobalSettings,
+ )
private val iActivityManager = mock<IActivityManager>()
private val notificationManager = mock<NotificationManager>()
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/recordissue/IssueRecordingStateTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/recordissue/IssueRecordingStateTest.kt
index 4ab3c7b203c4..83bdcd2161ee 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/recordissue/IssueRecordingStateTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/recordissue/IssueRecordingStateTest.kt
@@ -16,6 +16,8 @@
package com.android.systemui.recordissue
+import android.content.ContentResolver
+import android.os.Handler
import android.testing.TestableLooper
import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.SmallTest
@@ -23,11 +25,15 @@ import com.android.systemui.SysuiTestCase
import com.android.systemui.kosmos.Kosmos
import com.android.systemui.settings.userFileManager
import com.android.systemui.settings.userTracker
+import com.android.systemui.util.settings.fakeGlobalSettings
import com.google.common.truth.Truth
-import java.util.concurrent.CountDownLatch
import org.junit.Before
import org.junit.Test
import org.junit.runner.RunWith
+import org.mockito.Mock
+import org.mockito.MockitoAnnotations
+import org.mockito.kotlin.any
+import org.mockito.kotlin.verify
@SmallTest
@RunWith(AndroidJUnit4::class)
@@ -36,10 +42,19 @@ class IssueRecordingStateTest : SysuiTestCase() {
private val kosmos = Kosmos()
private lateinit var underTest: IssueRecordingState
+ @Mock private lateinit var resolver: ContentResolver
@Before
fun setup() {
- underTest = IssueRecordingState(kosmos.userTracker, kosmos.userFileManager)
+ MockitoAnnotations.initMocks(this)
+ underTest =
+ IssueRecordingState(
+ kosmos.userTracker,
+ kosmos.userFileManager,
+ Handler.getMain(),
+ resolver,
+ kosmos.fakeGlobalSettings,
+ )
}
@Test
@@ -47,7 +62,14 @@ class IssueRecordingStateTest : SysuiTestCase() {
val expected = true
underTest.takeBugreport = expected
- underTest = IssueRecordingState(kosmos.userTracker, kosmos.userFileManager)
+ underTest =
+ IssueRecordingState(
+ kosmos.userTracker,
+ kosmos.userFileManager,
+ Handler.getMain(),
+ resolver,
+ kosmos.fakeGlobalSettings,
+ )
Truth.assertThat(underTest.takeBugreport).isEqualTo(expected)
}
@@ -57,7 +79,14 @@ class IssueRecordingStateTest : SysuiTestCase() {
val expected = true
underTest.recordScreen = expected
- underTest = IssueRecordingState(kosmos.userTracker, kosmos.userFileManager)
+ underTest =
+ IssueRecordingState(
+ kosmos.userTracker,
+ kosmos.userFileManager,
+ Handler.getMain(),
+ resolver,
+ kosmos.fakeGlobalSettings,
+ )
Truth.assertThat(underTest.recordScreen).isEqualTo(expected)
}
@@ -65,7 +94,14 @@ class IssueRecordingStateTest : SysuiTestCase() {
@Test
fun hasUserApprovedScreenRecording_isTrue_afterBeingMarkedAsCompleted() {
underTest.markUserApprovalForScreenRecording()
- underTest = IssueRecordingState(kosmos.userTracker, kosmos.userFileManager)
+ underTest =
+ IssueRecordingState(
+ kosmos.userTracker,
+ kosmos.userFileManager,
+ Handler.getMain(),
+ resolver,
+ kosmos.fakeGlobalSettings,
+ )
Truth.assertThat(underTest.hasUserApprovedScreenRecording).isEqualTo(true)
}
@@ -75,35 +111,35 @@ class IssueRecordingStateTest : SysuiTestCase() {
val expected = setOf("a", "b", "c")
underTest.tagTitles = expected
- underTest = IssueRecordingState(kosmos.userTracker, kosmos.userFileManager)
+ underTest =
+ IssueRecordingState(
+ kosmos.userTracker,
+ kosmos.userFileManager,
+ Handler.getMain(),
+ resolver,
+ kosmos.fakeGlobalSettings,
+ )
Truth.assertThat(underTest.tagTitles).isEqualTo(expected)
}
@Test
- fun isRecording_callsListeners_onTheValueChanging() {
- val count = CountDownLatch(1)
- val listener = Runnable { count.countDown() }
+ fun addListener_registersContentObserver_ifListOfListenersIsNotEmpty() {
+ val listener = Runnable { /* No-op */ }
underTest.addListener(listener)
- underTest.isRecording = true
- Truth.assertThat(count.count).isEqualTo(0)
+ verify(resolver).registerContentObserver(any(), any(), any())
}
@Test
- fun isRecording_callsOnlyListeners_whoHaveNotBeenRemoved() {
- val count1 = CountDownLatch(1)
- val count2 = CountDownLatch(1)
- val listener1 = Runnable { count1.countDown() }
- val listener2 = Runnable { count2.countDown() }
-
- underTest.addListener(listener1)
- underTest.removeListener(listener1)
- underTest.addListener(listener2)
- underTest.isRecording = true
-
- Truth.assertThat(count1.count).isEqualTo(1)
- Truth.assertThat(count2.count).isEqualTo(0)
+ fun removeListener_unRegistersContentObserver_ifListOfListenersIsEmpty() {
+ val listener = Runnable { /* No-op */ }
+
+ underTest.addListener(listener)
+ underTest.removeListener(listener)
+
+ verify(resolver).registerContentObserver(any(), any(), any())
+ verify(resolver).unregisterContentObserver(any())
}
}
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/shade/data/repository/ShadeRepositoryImplTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/shade/data/repository/ShadeRepositoryImplTest.kt
index 7d4918a30d9c..b5043ce700f1 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/shade/data/repository/ShadeRepositoryImplTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/shade/data/repository/ShadeRepositoryImplTest.kt
@@ -39,7 +39,7 @@ class ShadeRepositoryImplTest : SysuiTestCase() {
@Before
fun setUp() {
- underTest = ShadeRepositoryImpl(getContext())
+ underTest = ShadeRepositoryImpl()
}
@Test
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 25670cb0bb0c..d5a7c89e6c71 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
@@ -64,7 +64,6 @@ 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.model.Scenes
-import com.android.systemui.shade.data.repository.fakeShadeRepository
import com.android.systemui.shade.mockLargeScreenHeaderHelper
import com.android.systemui.shade.shadeTestUtil
import com.android.systemui.statusbar.notification.stack.domain.interactor.sharedNotificationContainerInteractor
@@ -116,36 +115,18 @@ class SharedNotificationContainerViewModelTest(flags: FlagsParameterization) : S
kosmos.aodBurnInViewModel = aodBurnInViewModel
}
- val testScope = kosmos.testScope
- val configurationRepository
- get() = kosmos.fakeConfigurationRepository
-
- val keyguardRepository
- get() = kosmos.fakeKeyguardRepository
-
- val keyguardInteractor
- get() = kosmos.keyguardInteractor
-
- val keyguardRootViewModel
- get() = kosmos.keyguardRootViewModel
-
- val keyguardTransitionRepository
- get() = kosmos.fakeKeyguardTransitionRepository
-
- val shadeTestUtil
- get() = kosmos.shadeTestUtil
-
- val sharedNotificationContainerInteractor
- get() = kosmos.sharedNotificationContainerInteractor
-
- val largeScreenHeaderHelper
- get() = kosmos.mockLargeScreenHeaderHelper
-
- val communalSceneRepository
- get() = kosmos.communalSceneRepository
-
- val shadeRepository
- get() = kosmos.fakeShadeRepository
+ private val testScope = kosmos.testScope
+ private val configurationRepository by lazy { kosmos.fakeConfigurationRepository }
+ private val keyguardRepository by lazy { kosmos.fakeKeyguardRepository }
+ private val keyguardInteractor by lazy { kosmos.keyguardInteractor }
+ private val keyguardRootViewModel by lazy { kosmos.keyguardRootViewModel }
+ private val keyguardTransitionRepository by lazy { kosmos.fakeKeyguardTransitionRepository }
+ private val shadeTestUtil by lazy { kosmos.shadeTestUtil }
+ private val sharedNotificationContainerInteractor by lazy {
+ kosmos.sharedNotificationContainerInteractor
+ }
+ private val largeScreenHeaderHelper by lazy { kosmos.mockLargeScreenHeaderHelper }
+ private val communalSceneRepository by lazy { kosmos.communalSceneRepository }
lateinit var underTest: SharedNotificationContainerViewModel
@@ -637,6 +618,45 @@ class SharedNotificationContainerViewModelTest(flags: FlagsParameterization) : S
@Test
@DisableSceneContainer
+ fun boundsStableWhenGoingToAlternateBouncer() =
+ testScope.runTest {
+ val bounds by collectLastValue(underTest.bounds)
+
+ // Start on lockscreen
+ showLockscreen()
+
+ keyguardInteractor.setNotificationContainerBounds(
+ NotificationContainerBounds(top = 1f, bottom = 2f)
+ )
+
+ assertThat(bounds).isEqualTo(NotificationContainerBounds(top = 1f, bottom = 2f))
+
+ // Begin transition to AOD
+ keyguardTransitionRepository.sendTransitionStep(
+ TransitionStep(LOCKSCREEN, ALTERNATE_BOUNCER, 0f, TransitionState.STARTED)
+ )
+ runCurrent()
+ keyguardTransitionRepository.sendTransitionStep(
+ TransitionStep(LOCKSCREEN, ALTERNATE_BOUNCER, 0f, TransitionState.RUNNING)
+ )
+ runCurrent()
+
+ // This is the last step before FINISHED is sent, which could trigger a change in bounds
+ keyguardTransitionRepository.sendTransitionStep(
+ TransitionStep(LOCKSCREEN, ALTERNATE_BOUNCER, 1f, TransitionState.RUNNING)
+ )
+ runCurrent()
+ assertThat(bounds).isEqualTo(NotificationContainerBounds(top = 1f, bottom = 2f))
+
+ keyguardTransitionRepository.sendTransitionStep(
+ TransitionStep(LOCKSCREEN, ALTERNATE_BOUNCER, 1f, TransitionState.FINISHED)
+ )
+ runCurrent()
+ assertThat(bounds).isEqualTo(NotificationContainerBounds(top = 1f, bottom = 2f))
+ }
+
+ @Test
+ @DisableSceneContainer
fun boundsDoNotChangeWhileLockscreenToAodTransitionIsActive() =
testScope.runTest {
val bounds by collectLastValue(underTest.bounds)
@@ -1229,6 +1249,75 @@ class SharedNotificationContainerViewModelTest(flags: FlagsParameterization) : S
assertThat(alpha).isEqualTo(1f)
}
+ @Test
+ @DisableSceneContainer
+ fun notificationAbsoluteBottom() =
+ testScope.runTest {
+ var notificationCount = 2
+ val calculateSpace = { _: Float, _: Boolean -> notificationCount }
+ val shelfHeight = 10F
+ val heightForNotification = 20F
+ val calculateHeight = { count: Int -> count * heightForNotification + shelfHeight }
+ val stackAbsoluteBottom by
+ collectLastValue(
+ underTest.getNotificationStackAbsoluteBottom(
+ calculateSpace,
+ calculateHeight,
+ shelfHeight,
+ )
+ )
+ advanceTimeBy(50L)
+ showLockscreen()
+
+ shadeTestUtil.setSplitShade(false)
+ keyguardInteractor.setNotificationContainerBounds(
+ NotificationContainerBounds(top = 100F, bottom = 300F)
+ )
+ configurationRepository.onAnyConfigurationChange()
+
+ assertThat(stackAbsoluteBottom).isEqualTo(150F)
+
+ // Also updates when directly requested (as it would from NotificationStackScrollLayout)
+ notificationCount = 3
+ sharedNotificationContainerInteractor.notificationStackChanged()
+ advanceTimeBy(50L)
+ assertThat(stackAbsoluteBottom).isEqualTo(170F)
+ }
+
+ @Test
+ @DisableSceneContainer
+ fun notificationAbsoluteBottom_maxNotificationIsZero_noShelfHeight() =
+ testScope.runTest {
+ var notificationCount = 2
+ val calculateSpace = { _: Float, _: Boolean -> notificationCount }
+ val shelfHeight = 10F
+ val heightForNotification = 20F
+ val calculateHeight = { count: Int -> count * heightForNotification + shelfHeight }
+ val stackAbsoluteBottom by
+ collectLastValue(
+ underTest.getNotificationStackAbsoluteBottom(
+ calculateSpace,
+ calculateHeight,
+ shelfHeight,
+ )
+ )
+ advanceTimeBy(50L)
+ showLockscreen()
+
+ shadeTestUtil.setSplitShade(false)
+ keyguardInteractor.setNotificationContainerBounds(
+ NotificationContainerBounds(top = 100F, bottom = 300F)
+ )
+ configurationRepository.onAnyConfigurationChange()
+
+ assertThat(stackAbsoluteBottom).isEqualTo(150F)
+
+ notificationCount = 0
+ sharedNotificationContainerInteractor.notificationStackChanged()
+ advanceTimeBy(50L)
+ assertThat(stackAbsoluteBottom).isEqualTo(100F)
+ }
+
private suspend fun TestScope.showLockscreen() {
shadeTestUtil.setQsExpansion(0f)
shadeTestUtil.setLockscreenShadeExpansion(0f)
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/volume/dialog/sliders/domain/interactor/VolumeDialogSliderInteractorTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/volume/dialog/sliders/domain/interactor/VolumeDialogSliderInteractorTest.kt
new file mode 100644
index 000000000000..bfafdab003aa
--- /dev/null
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/volume/dialog/sliders/domain/interactor/VolumeDialogSliderInteractorTest.kt
@@ -0,0 +1,90 @@
+/*
+ * 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.
+ */
+
+package com.android.systemui.volume.dialog.sliders.domain.interactor
+
+import android.testing.TestableLooper
+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.testScope
+import com.android.systemui.plugins.VolumeDialogController
+import com.android.systemui.plugins.fakeVolumeDialogController
+import com.android.systemui.testKosmos
+import com.android.systemui.volume.dialog.sliders.domain.model.volumeDialogSliderType
+import com.google.common.truth.Truth.assertThat
+import kotlinx.coroutines.ExperimentalCoroutinesApi
+import kotlinx.coroutines.test.runCurrent
+import kotlinx.coroutines.test.runTest
+import org.junit.Before
+import org.junit.Test
+import org.junit.runner.RunWith
+
+@OptIn(ExperimentalCoroutinesApi::class)
+@SmallTest
+@RunWith(AndroidJUnit4::class)
+@TestableLooper.RunWithLooper
+class VolumeDialogSliderInteractorTest : SysuiTestCase() {
+
+ private val kosmos = testKosmos()
+
+ private lateinit var underTest: VolumeDialogSliderInteractor
+
+ @Before
+ fun setUp() {
+ underTest = kosmos.volumeDialogSliderInteractor
+ }
+
+ @Test
+ fun settingStreamVolume_setsActiveStream() =
+ with(kosmos) {
+ testScope.runTest {
+ runCurrent()
+ // initialize the stream model
+ fakeVolumeDialogController.setStreamVolume(volumeDialogSliderType.audioStream, 0)
+
+ val sliderModel by collectLastValue(underTest.slider)
+ underTest.setStreamVolume(1)
+ runCurrent()
+
+ assertThat(sliderModel!!.isActive).isTrue()
+ }
+ }
+
+ @Test
+ fun streamVolumeIs_minMaxAreEnforced() =
+ with(kosmos) {
+ testScope.runTest {
+ runCurrent()
+ fakeVolumeDialogController.updateState {
+ states.put(
+ volumeDialogSliderType.audioStream,
+ VolumeDialogController.StreamState().apply {
+ levelMin = 0
+ level = 2
+ levelMax = 1
+ },
+ )
+ }
+
+ val sliderModel by collectLastValue(underTest.slider)
+ runCurrent()
+
+ assertThat(sliderModel!!.level).isEqualTo(1)
+ }
+ }
+}
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/wallpapers/data/repository/FakeWallpaperRepository.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/wallpapers/data/repository/FakeWallpaperRepository.kt
index 59676ce126da..111c232280c3 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/wallpapers/data/repository/FakeWallpaperRepository.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/wallpapers/data/repository/FakeWallpaperRepository.kt
@@ -25,9 +25,4 @@ class FakeWallpaperRepository : WallpaperRepository {
override val wallpaperInfo = MutableStateFlow<WallpaperInfo?>(null)
override val wallpaperSupportsAmbientMode = MutableStateFlow(false)
override var rootView: View? = null
- private val _notificationStackAbsoluteBottom = MutableStateFlow(0F)
-
- override fun setNotificationStackAbsoluteBottom(bottom: Float) {
- _notificationStackAbsoluteBottom.value = bottom
- }
}
diff --git a/packages/SystemUI/plugin/src/com/android/systemui/plugins/clocks/ClockProviderPlugin.kt b/packages/SystemUI/plugin/src/com/android/systemui/plugins/clocks/ClockProviderPlugin.kt
index f975c4f13ff4..e264264d8c6c 100644
--- a/packages/SystemUI/plugin/src/com/android/systemui/plugins/clocks/ClockProviderPlugin.kt
+++ b/packages/SystemUI/plugin/src/com/android/systemui/plugins/clocks/ClockProviderPlugin.kt
@@ -182,7 +182,7 @@ class DefaultClockFaceLayout(val view: View) : ClockFaceLayout {
interface ClockEvents {
@get:ProtectedReturn("return false;")
/** Set to enable or disable swipe interaction */
- var isReactiveTouchInteractionEnabled: Boolean
+ var isReactiveTouchInteractionEnabled: Boolean // TODO(b/364664388): Remove/Rename
/** Call whenever timezone changes */
fun onTimeZoneChanged(timeZone: TimeZone)
@@ -322,9 +322,6 @@ constructor(
/** True if the clock will react to tone changes in the seed color */
val isReactiveToTone: Boolean = true,
- /** True if the clock is capable of changing style in reaction to touches */
- val isReactiveToTouch: Boolean = false,
-
/** Font axes that can be modified on this clock */
val axes: List<ClockReactiveAxis> = listOf(),
)
diff --git a/packages/SystemUI/plugin_core/AndroidManifest.xml b/packages/SystemUI/plugin_core/AndroidManifest.xml
deleted file mode 100644
index df835fd8e32d..000000000000
--- a/packages/SystemUI/plugin_core/AndroidManifest.xml
+++ /dev/null
@@ -1,24 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
- Copyright (C) 2018 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 xmlns:android="http://schemas.android.com/apk/res/android"
- package="com.android.systemui.plugin_core">
-
- <uses-sdk
- android:minSdkVersion="28" />
-
-</manifest>
diff --git a/packages/SystemUI/res/drawable/notif_footer_btn_history.xml b/packages/SystemUI/res/drawable/notif_footer_btn_history.xml
new file mode 100644
index 000000000000..0460a7268fcc
--- /dev/null
+++ b/packages/SystemUI/res/drawable/notif_footer_btn_history.xml
@@ -0,0 +1,10 @@
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="20dp"
+ android:height="20dp"
+ android:viewportWidth="960"
+ android:viewportHeight="960"
+ android:tint="?attr/colorControlNormal">
+ <path
+ android:fillColor="@android:color/white"
+ android:pathData="M480,840Q342,840 239.5,748.5Q137,657 122,520L204,520Q218,624 296.5,692Q375,760 480,760Q597,760 678.5,678.5Q760,597 760,480Q760,363 678.5,281.5Q597,200 480,200Q411,200 351,232Q291,264 250,320L360,320L360,400L120,400L120,160L200,160L200,254Q251,190 324.5,155Q398,120 480,120Q555,120 620.5,148.5Q686,177 734.5,225.5Q783,274 811.5,339.5Q840,405 840,480Q840,555 811.5,620.5Q783,686 734.5,734.5Q686,783 620.5,811.5Q555,840 480,840ZM592,648L440,496L440,280L520,280L520,464L648,592L592,648Z"/>
+</vector>
diff --git a/packages/SystemUI/res/drawable/notif_footer_btn_settings.xml b/packages/SystemUI/res/drawable/notif_footer_btn_settings.xml
new file mode 100644
index 000000000000..800060db7757
--- /dev/null
+++ b/packages/SystemUI/res/drawable/notif_footer_btn_settings.xml
@@ -0,0 +1,10 @@
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="20dp"
+ android:height="20dp"
+ android:viewportWidth="960"
+ android:viewportHeight="960"
+ android:tint="?attr/colorControlNormal">
+ <path
+ android:fillColor="@android:color/white"
+ android:pathData="M370,880L354,752Q341,747 329.5,740Q318,733 307,725L188,775L78,585L181,507Q180,500 180,493.5Q180,487 180,480Q180,473 180,466.5Q180,460 181,453L78,375L188,185L307,235Q318,227 330,220Q342,213 354,208L370,80L590,80L606,208Q619,213 630.5,220Q642,227 653,235L772,185L882,375L779,453Q780,460 780,466.5Q780,473 780,480Q780,487 780,493.5Q780,500 778,507L881,585L771,775L653,725Q642,733 630,740Q618,747 606,752L590,880L370,880ZM440,800L519,800L533,694Q564,686 590.5,670.5Q617,655 639,633L738,674L777,606L691,541Q696,527 698,511.5Q700,496 700,480Q700,464 698,448.5Q696,433 691,419L777,354L738,286L639,328Q617,305 590.5,289.5Q564,274 533,266L520,160L441,160L427,266Q396,274 369.5,289.5Q343,305 321,327L222,286L183,354L269,418Q264,433 262,448Q260,463 260,480Q260,496 262,511Q264,526 269,541L183,606L222,674L321,632Q343,655 369.5,670.5Q396,686 427,694L440,800ZM482,620Q540,620 581,579Q622,538 622,480Q622,422 581,381Q540,340 482,340Q423,340 382.5,381Q342,422 342,480Q342,538 382.5,579Q423,620 482,620ZM480,480L480,480Q480,480 480,480Q480,480 480,480L480,480L480,480L480,480Q480,480 480,480Q480,480 480,480Q480,480 480,480Q480,480 480,480L480,480L480,480L480,480Q480,480 480,480Q480,480 480,480L480,480L480,480L480,480Q480,480 480,480Q480,480 480,480L480,480L480,480L480,480Q480,480 480,480Q480,480 480,480Q480,480 480,480Q480,480 480,480L480,480L480,480L480,480Q480,480 480,480Q480,480 480,480L480,480Z"/>
+</vector>
diff --git a/packages/SystemUI/res/drawable/volume_background_top.xml b/packages/SystemUI/res/drawable/volume_background_top.xml
index 3cd87fc32061..75849beeb016 100644
--- a/packages/SystemUI/res/drawable/volume_background_top.xml
+++ b/packages/SystemUI/res/drawable/volume_background_top.xml
@@ -1,27 +1,26 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
- ~ Copyright (C) 2021 The Android Open Source Project
- ~
- ~ Licensed under the Apache License, Version 2.0 (the "License");
- ~ you may not use this file except in compliance with the License.
- ~ You may obtain a copy of the License at
- ~
- ~ http://www.apache.org/licenses/LICENSE-2.0
- ~
- ~ Unless required by applicable law or agreed to in writing, software
- ~ distributed under the License is distributed on an "AS IS" BASIS,
- ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- ~ See the License for the specific language governing permissions and
- ~ limitations under the License
- -->
+<?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.
+-->
<layer-list xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:androidprv="http://schemas.android.com/apk/prv/res/android">
<item>
<shape>
- <size android:width="@dimen/volume_dialog_panel_width" />
- <solid android:color="?androidprv:attr/colorSurface" />
- <corners android:topLeftRadius="@dimen/volume_dialog_panel_width_half"
- android:topRightRadius="@dimen/volume_dialog_panel_width_half"/>
+ <size android:width="@dimen/volume_dialog_width" />
+ <solid android:color="?androidprv:attr/materialColorSurface" />
+ <corners android:topLeftRadius="@dimen/volume_dialog_background_corner_radius"
+ android:topRightRadius="@dimen/volume_dialog_background_corner_radius"/>
</shape>
</item>
-</layer-list> \ No newline at end of file
+</layer-list>
diff --git a/packages/SystemUI/res/drawable/volume_background_top_legacy.xml b/packages/SystemUI/res/drawable/volume_background_top_legacy.xml
new file mode 100644
index 000000000000..3cd87fc32061
--- /dev/null
+++ b/packages/SystemUI/res/drawable/volume_background_top_legacy.xml
@@ -0,0 +1,27 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ ~ Copyright (C) 2021 The Android Open Source Project
+ ~
+ ~ Licensed under the Apache License, Version 2.0 (the "License");
+ ~ you may not use this file except in compliance with the License.
+ ~ You may obtain a copy of the License at
+ ~
+ ~ http://www.apache.org/licenses/LICENSE-2.0
+ ~
+ ~ Unless required by applicable law or agreed to in writing, software
+ ~ distributed under the License is distributed on an "AS IS" BASIS,
+ ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ ~ See the License for the specific language governing permissions and
+ ~ limitations under the License
+ -->
+<layer-list xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:androidprv="http://schemas.android.com/apk/prv/res/android">
+ <item>
+ <shape>
+ <size android:width="@dimen/volume_dialog_panel_width" />
+ <solid android:color="?androidprv:attr/colorSurface" />
+ <corners android:topLeftRadius="@dimen/volume_dialog_panel_width_half"
+ android:topRightRadius="@dimen/volume_dialog_panel_width_half"/>
+ </shape>
+ </item>
+</layer-list> \ No newline at end of file
diff --git a/packages/SystemUI/res/drawable/volume_drawer_selection_bg.xml b/packages/SystemUI/res/drawable/volume_drawer_selection_bg.xml
index 5e7cb12d1c5f..df8521a15f6d 100644
--- a/packages/SystemUI/res/drawable/volume_drawer_selection_bg.xml
+++ b/packages/SystemUI/res/drawable/volume_drawer_selection_bg.xml
@@ -1,6 +1,5 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
- ~ Copyright (C) 2021 The Android Open Source Project
+<?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.
@@ -15,10 +14,11 @@
~ limitations under the License.
-->
<shape xmlns:android="http://schemas.android.com/apk/res/android"
- android:paddingMode="stack" >
+ xmlns:androidprv="http://schemas.android.com/apk/prv/res/android"
+ android:paddingMode="stack" >
<size
- android:height="@dimen/volume_ringer_drawer_item_size"
- android:width="@dimen/volume_ringer_drawer_item_size" />
- <solid android:color="?android:attr/colorAccent" />
- <corners android:radius="@dimen/volume_ringer_drawer_item_size_half" />
+ android:height="@dimen/volume_ringer_item_size"
+ android:width="@dimen/volume_ringer_item_size" />
+ <solid android:color="?androidprv:attr/materialColorPrimary" />
+ <corners android:radius="360dp" />
</shape> \ No newline at end of file
diff --git a/packages/SystemUI/res/drawable/volume_drawer_selection_bg_legacy.xml b/packages/SystemUI/res/drawable/volume_drawer_selection_bg_legacy.xml
new file mode 100644
index 000000000000..5e7cb12d1c5f
--- /dev/null
+++ b/packages/SystemUI/res/drawable/volume_drawer_selection_bg_legacy.xml
@@ -0,0 +1,24 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ ~ Copyright (C) 2021 The Android Open Source Project
+ ~
+ ~ Licensed under the Apache License, Version 2.0 (the "License");
+ ~ you may not use this file except in compliance with the License.
+ ~ You may obtain a copy of the License at
+ ~
+ ~ http://www.apache.org/licenses/LICENSE-2.0
+ ~
+ ~ Unless required by applicable law or agreed to in writing, software
+ ~ distributed under the License is distributed on an "AS IS" BASIS,
+ ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ ~ See the License for the specific language governing permissions and
+ ~ limitations under the License.
+ -->
+<shape xmlns:android="http://schemas.android.com/apk/res/android"
+ android:paddingMode="stack" >
+ <size
+ android:height="@dimen/volume_ringer_drawer_item_size"
+ android:width="@dimen/volume_ringer_drawer_item_size" />
+ <solid android:color="?android:attr/colorAccent" />
+ <corners android:radius="@dimen/volume_ringer_drawer_item_size_half" />
+</shape> \ No newline at end of file
diff --git a/packages/SystemUI/res/drawable/volume_ringer_item_bg.xml b/packages/SystemUI/res/drawable/volume_ringer_item_bg.xml
new file mode 100644
index 000000000000..7ddd57b7c34e
--- /dev/null
+++ b/packages/SystemUI/res/drawable/volume_ringer_item_bg.xml
@@ -0,0 +1,22 @@
+<?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.
+ -->
+<shape xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:androidprv="http://schemas.android.com/apk/prv/res/android"
+ android:shape="rectangle" >
+ <size android:width="@dimen/volume_ringer_item_size" android:height="@dimen/volume_ringer_item_size"/>
+ <solid android:color="?androidprv:attr/materialColorSurfaceContainerHighest" />
+ <corners android:radius="@dimen/volume_ringer_item_radius" />
+</shape> \ No newline at end of file
diff --git a/packages/SystemUI/res/layout-land-television/volume_dialog.xml b/packages/SystemUI/res/layout-land-television/volume_dialog.xml
deleted file mode 100644
index f77db956a493..000000000000
--- a/packages/SystemUI/res/layout-land-television/volume_dialog.xml
+++ /dev/null
@@ -1,67 +0,0 @@
-<!--
- 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.
--->
-<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:androidprv="http://schemas.android.com/apk/prv/res/android"
- android:id="@+id/volume_dialog_container"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:layout_gravity="right"
- android:divider="@drawable/volume_dialog_floating_sliders_spacer"
- android:orientation="horizontal"
- android:showDividers="middle|end|beginning"
- android:theme="@style/volume_dialog_theme">
-
- <LinearLayout
- android:id="@+id/volume_dialog_floating_sliders_container"
- android:layout_width="wrap_content"
- android:layout_height="match_parent"
- android:background="@drawable/volume_dialog_background"
- android:divider="@drawable/volume_dialog_floating_sliders_spacer"
- android:gravity="bottom"
- android:orientation="horizontal"
- android:paddingBottom="@dimen/volume_dialog_floating_sliders_bottom_padding"
- android:showDividers="middle" />
-
- <LinearLayout
- android:layout_width="@dimen/volume_dialog_width"
- android:layout_height="wrap_content"
- android:background="@drawable/volume_dialog_background"
- android:divider="@drawable/volume_dialog_spacer"
- android:gravity="center_horizontal"
- android:orientation="vertical"
- android:paddingVertical="@dimen/volume_dialog_vertical_padding"
- android:showDividers="middle">
-
- <FrameLayout
- android:id="@+id/volume_dialog_ringer_button"
- android:layout_width="@dimen/volume_dialog_button_size"
- android:layout_height="@dimen/volume_dialog_button_size" />
-
- <include
- android:id="@+id/volume_dialog_slider"
- layout="@layout/volume_dialog_slider" />
-
- <Button
- android:id="@+id/volume_dialog_settings"
- android:layout_width="@dimen/volume_dialog_button_size"
- android:layout_height="@dimen/volume_dialog_button_size"
- android:background="@drawable/ripple_drawable_20dp"
- android:contentDescription="@string/accessibility_volume_settings"
- android:soundEffectsEnabled="false"
- android:src="@drawable/horizontal_ellipsis"
- android:tint="?androidprv:attr/materialColorPrimary" />
- </LinearLayout>
-</LinearLayout> \ No newline at end of file
diff --git a/packages/SystemUI/res/layout-land/volume_dialog.xml b/packages/SystemUI/res/layout-land/volume_dialog.xml
deleted file mode 100644
index f77db956a493..000000000000
--- a/packages/SystemUI/res/layout-land/volume_dialog.xml
+++ /dev/null
@@ -1,67 +0,0 @@
-<!--
- 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.
--->
-<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:androidprv="http://schemas.android.com/apk/prv/res/android"
- android:id="@+id/volume_dialog_container"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:layout_gravity="right"
- android:divider="@drawable/volume_dialog_floating_sliders_spacer"
- android:orientation="horizontal"
- android:showDividers="middle|end|beginning"
- android:theme="@style/volume_dialog_theme">
-
- <LinearLayout
- android:id="@+id/volume_dialog_floating_sliders_container"
- android:layout_width="wrap_content"
- android:layout_height="match_parent"
- android:background="@drawable/volume_dialog_background"
- android:divider="@drawable/volume_dialog_floating_sliders_spacer"
- android:gravity="bottom"
- android:orientation="horizontal"
- android:paddingBottom="@dimen/volume_dialog_floating_sliders_bottom_padding"
- android:showDividers="middle" />
-
- <LinearLayout
- android:layout_width="@dimen/volume_dialog_width"
- android:layout_height="wrap_content"
- android:background="@drawable/volume_dialog_background"
- android:divider="@drawable/volume_dialog_spacer"
- android:gravity="center_horizontal"
- android:orientation="vertical"
- android:paddingVertical="@dimen/volume_dialog_vertical_padding"
- android:showDividers="middle">
-
- <FrameLayout
- android:id="@+id/volume_dialog_ringer_button"
- android:layout_width="@dimen/volume_dialog_button_size"
- android:layout_height="@dimen/volume_dialog_button_size" />
-
- <include
- android:id="@+id/volume_dialog_slider"
- layout="@layout/volume_dialog_slider" />
-
- <Button
- android:id="@+id/volume_dialog_settings"
- android:layout_width="@dimen/volume_dialog_button_size"
- android:layout_height="@dimen/volume_dialog_button_size"
- android:background="@drawable/ripple_drawable_20dp"
- android:contentDescription="@string/accessibility_volume_settings"
- android:soundEffectsEnabled="false"
- android:src="@drawable/horizontal_ellipsis"
- android:tint="?androidprv:attr/materialColorPrimary" />
- </LinearLayout>
-</LinearLayout> \ No newline at end of file
diff --git a/packages/SystemUI/res/layout-land/volume_dialog_legacy.xml b/packages/SystemUI/res/layout-land/volume_dialog_legacy.xml
index 08edf59000b8..de346db41252 100644
--- a/packages/SystemUI/res/layout-land/volume_dialog_legacy.xml
+++ b/packages/SystemUI/res/layout-land/volume_dialog_legacy.xml
@@ -44,7 +44,7 @@
android:clipChildren="false"
android:gravity="right">
- <include layout="@layout/volume_ringer_drawer" />
+ <include layout="@layout/volume_ringer_drawer_legacy" />
<FrameLayout
android:visibility="gone"
diff --git a/packages/SystemUI/res/layout/status_bar_notification_footer_redesign.xml b/packages/SystemUI/res/layout/status_bar_notification_footer_redesign.xml
new file mode 100644
index 000000000000..7c59aad10c91
--- /dev/null
+++ b/packages/SystemUI/res/layout/status_bar_notification_footer_redesign.xml
@@ -0,0 +1,82 @@
+<!--
+ ~ 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.systemui.statusbar.notification.footer.ui.view.FooterView
+ xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:app="http://schemas.android.com/apk/res-auto"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:visibility="gone">
+
+ <com.android.systemui.statusbar.AlphaOptimizedFrameLayout
+ android:id="@+id/content"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:paddingTop="12dp">
+
+ <TextView
+ android:id="@+id/unlock_prompt_footer"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_gravity="center_horizontal"
+ android:drawablePadding="8dp"
+ android:gravity="center"
+ android:text="@string/unlock_to_see_notif_text"
+ android:textAppearance="?android:attr/textAppearanceButton"
+ android:visibility="gone" />
+
+ <androidx.constraintlayout.widget.ConstraintLayout
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content">
+
+ <com.android.systemui.statusbar.notification.row.FooterViewButton
+ android:id="@+id/settings_button"
+ style="@style/TextAppearance.NotificationFooterButtonRedesign"
+ android:layout_width="wrap_content"
+ android:layout_height="48dp"
+ android:background="@drawable/notif_footer_btn_background"
+ android:contentDescription="@string/notification_settings_button_description"
+ android:drawableStart="@drawable/notif_footer_btn_settings"
+ android:focusable="true"
+ app:layout_constraintStart_toStartOf="parent" />
+
+ <com.android.systemui.statusbar.notification.row.FooterViewButton
+ android:id="@+id/dismiss_text"
+ style="@style/TextAppearance.NotificationFooterButtonRedesign"
+ android:layout_width="0dp"
+ android:layout_height="48dp"
+ android:layout_marginHorizontal="8dp"
+ android:background="@drawable/notif_footer_btn_background"
+ android:contentDescription="@string/accessibility_clear_all"
+ android:focusable="true"
+ android:text="@string/clear_all_notifications_text"
+ app:layout_constraintEnd_toStartOf="@id/history_button"
+ app:layout_constraintStart_toEndOf="@id/settings_button" />
+
+ <com.android.systemui.statusbar.notification.row.FooterViewButton
+ android:id="@+id/history_button"
+ style="@style/TextAppearance.NotificationFooterButtonRedesign"
+ android:layout_width="wrap_content"
+ android:layout_height="48dp"
+ android:background="@drawable/notif_footer_btn_background"
+ android:contentDescription="@string/notification_history_button_description"
+ android:drawableStart="@drawable/notif_footer_btn_history"
+ android:focusable="true"
+ app:layout_constraintEnd_toEndOf="parent" />
+ </androidx.constraintlayout.widget.ConstraintLayout>
+ </com.android.systemui.statusbar.AlphaOptimizedFrameLayout>
+</com.android.systemui.statusbar.notification.footer.ui.view.FooterView>
diff --git a/packages/SystemUI/res/layout/volume_dialog.xml b/packages/SystemUI/res/layout/volume_dialog.xml
index f77db956a493..8b39e5eb341f 100644
--- a/packages/SystemUI/res/layout/volume_dialog.xml
+++ b/packages/SystemUI/res/layout/volume_dialog.xml
@@ -28,7 +28,6 @@
android:id="@+id/volume_dialog_floating_sliders_container"
android:layout_width="wrap_content"
android:layout_height="match_parent"
- android:background="@drawable/volume_dialog_background"
android:divider="@drawable/volume_dialog_floating_sliders_spacer"
android:gravity="bottom"
android:orientation="horizontal"
@@ -36,6 +35,7 @@
android:showDividers="middle" />
<LinearLayout
+ android:id="@+id/volume_dialog"
android:layout_width="@dimen/volume_dialog_width"
android:layout_height="wrap_content"
android:background="@drawable/volume_dialog_background"
@@ -45,16 +45,11 @@
android:paddingVertical="@dimen/volume_dialog_vertical_padding"
android:showDividers="middle">
- <FrameLayout
- android:id="@+id/volume_dialog_ringer_button"
- android:layout_width="@dimen/volume_dialog_button_size"
- android:layout_height="@dimen/volume_dialog_button_size" />
+ <include layout="@layout/volume_ringer_drawer" />
- <include
- android:id="@+id/volume_dialog_slider"
- layout="@layout/volume_dialog_slider" />
+ <include layout="@layout/volume_dialog_slider" />
- <Button
+ <ImageButton
android:id="@+id/volume_dialog_settings"
android:layout_width="@dimen/volume_dialog_button_size"
android:layout_height="@dimen/volume_dialog_button_size"
diff --git a/packages/SystemUI/res/layout/volume_dialog_legacy.xml b/packages/SystemUI/res/layout/volume_dialog_legacy.xml
index 39a1f1f9b85d..9010ab764da0 100644
--- a/packages/SystemUI/res/layout/volume_dialog_legacy.xml
+++ b/packages/SystemUI/res/layout/volume_dialog_legacy.xml
@@ -45,7 +45,7 @@
android:orientation="vertical"
android:gravity="right">
- <include layout="@layout/volume_ringer_drawer" />
+ <include layout="@layout/volume_ringer_drawer_legacy" />
<FrameLayout
android:visibility="gone"
diff --git a/packages/SystemUI/res/layout/volume_ringer_drawer.xml b/packages/SystemUI/res/layout/volume_ringer_drawer.xml
index 8f1e0610853f..94b55b18c64f 100644
--- a/packages/SystemUI/res/layout/volume_ringer_drawer.xml
+++ b/packages/SystemUI/res/layout/volume_ringer_drawer.xml
@@ -1,35 +1,32 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
- Copyright (C) 2021 The Android Open Source Project
-
- Licensed under the Apache License, Version 2.0 (the "License");
- you may not use this file except in compliance with the License.
- You may obtain a copy of the License at
-
- http://www.apache.org/licenses/LICENSE-2.0
-
- Unless required by applicable law or agreed to in writing, software
- distributed under the License is distributed on an "AS IS" BASIS,
- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- See the License for the specific language governing permissions and
- limitations under the License.
--->
-
-<!-- Contains the active ringer icon and a hidden drawer containing all three ringer options. -->
-<FrameLayout
- xmlns:android="http://schemas.android.com/apk/res/android"
+<?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.
+ -->
+
+<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:androidprv="http://schemas.android.com/apk/prv/res/android"
android:id="@+id/volume_ringer_and_drawer_container"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:gravity="center"
- android:paddingLeft="@dimen/volume_dialog_ringer_rows_padding"
- android:paddingTop="@dimen/volume_dialog_ringer_rows_padding"
- android:paddingRight="@dimen/volume_dialog_ringer_rows_padding"
- android:paddingBottom="@dimen/volume_dialog_ringer_rows_padding"
- android:background="@drawable/volume_background_top"
+ android:paddingLeft="@dimen/volume_dialog_ringer_container_padding"
+ android:paddingTop="@dimen/volume_dialog_ringer_container_padding"
+ android:paddingRight="@dimen/volume_dialog_ringer_container_padding"
android:layoutDirection="ltr"
android:clipToPadding="false"
- android:clipChildren="false">
+ android:clipChildren="false"
+ android:background="@drawable/volume_background_top">
<!-- Drawer view, invisible by default. -->
<FrameLayout
@@ -37,15 +34,15 @@
android:alpha="0.0"
android:layout_width="match_parent"
android:layout_height="wrap_content"
- android:background="@drawable/volume_drawer_bg"
android:orientation="vertical">
<!-- View that is animated to a tapped ringer selection, so it appears selected. -->
<FrameLayout
android:id="@+id/volume_drawer_selection_background"
android:alpha="0.0"
- android:layout_width="@dimen/volume_ringer_drawer_item_size"
- android:layout_height="@dimen/volume_ringer_drawer_item_size"
+ android:layout_width="@dimen/volume_ringer_item_size"
+ android:layout_marginBottom="8dp"
+ android:layout_height="@dimen/volume_ringer_item_size"
android:layout_gravity="bottom|right"
android:background="@drawable/volume_drawer_selection_bg" />
@@ -57,54 +54,60 @@
<FrameLayout
android:id="@+id/volume_drawer_vibrate"
- android:layout_width="@dimen/volume_ringer_drawer_item_size"
- android:layout_height="@dimen/volume_ringer_drawer_item_size"
+ android:layout_width="@dimen/volume_ringer_item_size"
+ android:layout_height="@dimen/volume_ringer_item_size"
+ android:layout_marginBottom="8dp"
android:contentDescription="@string/volume_ringer_hint_vibrate"
- android:gravity="center">
+ android:gravity="center"
+ android:background="@drawable/volume_ringer_item_bg">
<ImageView
android:id="@+id/volume_drawer_vibrate_icon"
- android:layout_width="@dimen/volume_ringer_drawer_icon_size"
- android:layout_height="@dimen/volume_ringer_drawer_icon_size"
+ android:layout_width="@dimen/volume_ringer_icon_size"
+ android:layout_height="@dimen/volume_ringer_icon_size"
android:layout_gravity="center"
android:src="@drawable/ic_volume_ringer_vibrate"
- android:tint="?android:attr/textColorPrimary" />
+ android:tint="?androidprv:attr/materialColorOnSurface" />
</FrameLayout>
<FrameLayout
android:id="@+id/volume_drawer_mute"
- android:layout_width="@dimen/volume_ringer_drawer_item_size"
- android:layout_height="@dimen/volume_ringer_drawer_item_size"
+ android:layout_width="@dimen/volume_ringer_item_size"
+ android:layout_height="@dimen/volume_ringer_item_size"
+ android:layout_marginBottom="8dp"
android:accessibilityTraversalAfter="@id/volume_drawer_vibrate"
android:contentDescription="@string/volume_ringer_hint_mute"
- android:gravity="center">
+ android:gravity="center"
+ android:background="@drawable/volume_ringer_item_bg">
<ImageView
android:id="@+id/volume_drawer_mute_icon"
- android:layout_width="@dimen/volume_ringer_drawer_icon_size"
- android:layout_height="@dimen/volume_ringer_drawer_icon_size"
+ android:layout_width="@dimen/volume_ringer_icon_size"
+ android:layout_height="@dimen/volume_ringer_icon_size"
android:layout_gravity="center"
android:src="@drawable/ic_speaker_mute"
- android:tint="?android:attr/textColorPrimary" />
+ android:tint="?androidprv:attr/materialColorOnSurface" />
</FrameLayout>
<FrameLayout
android:id="@+id/volume_drawer_normal"
- android:layout_width="@dimen/volume_ringer_drawer_item_size"
- android:layout_height="@dimen/volume_ringer_drawer_item_size"
+ android:layout_width="@dimen/volume_ringer_item_size"
+ android:layout_height="@dimen/volume_ringer_item_size"
+ android:layout_marginBottom="8dp"
android:accessibilityTraversalAfter="@id/volume_drawer_mute"
android:contentDescription="@string/volume_ringer_hint_unmute"
- android:gravity="center">
+ android:gravity="center"
+ android:background="@drawable/volume_ringer_item_bg">
<ImageView
android:id="@+id/volume_drawer_normal_icon"
- android:layout_width="@dimen/volume_ringer_drawer_icon_size"
- android:layout_height="@dimen/volume_ringer_drawer_icon_size"
+ android:layout_width="@dimen/volume_ringer_icon_size"
+ android:layout_height="@dimen/volume_ringer_icon_size"
android:layout_gravity="center"
android:src="@drawable/ic_speaker_on"
- android:tint="?android:attr/textColorPrimary" />
+ android:tint="?androidprv:attr/materialColorOnSurface" />
</FrameLayout>
@@ -116,18 +119,19 @@
position in the drawer. When the drawer is closed, it animates back. -->
<FrameLayout
android:id="@+id/volume_new_ringer_active_icon_container"
- android:layout_width="@dimen/volume_ringer_drawer_item_size"
- android:layout_height="@dimen/volume_ringer_drawer_item_size"
+ android:layout_width="@dimen/volume_ringer_item_size"
+ android:layout_height="@dimen/volume_ringer_item_size"
+ android:layout_marginBottom="8dp"
android:layout_gravity="bottom|right"
android:contentDescription="@string/volume_ringer_change"
android:background="@drawable/volume_drawer_selection_bg">
<ImageView
android:id="@+id/volume_new_ringer_active_icon"
- android:layout_width="@dimen/volume_ringer_drawer_icon_size"
- android:layout_height="@dimen/volume_ringer_drawer_icon_size"
+ android:layout_width="@dimen/volume_ringer_icon_size"
+ android:layout_height="@dimen/volume_ringer_icon_size"
android:layout_gravity="center"
- android:tint="?android:attr/textColorPrimaryInverse"
+ android:tint="?androidprv:attr/materialColorOnPrimary"
android:src="@drawable/ic_volume_media" />
</FrameLayout>
diff --git a/packages/SystemUI/res/layout/volume_ringer_drawer_legacy.xml b/packages/SystemUI/res/layout/volume_ringer_drawer_legacy.xml
new file mode 100644
index 000000000000..0efbc6d651dc
--- /dev/null
+++ b/packages/SystemUI/res/layout/volume_ringer_drawer_legacy.xml
@@ -0,0 +1,135 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ Copyright (C) 2021 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+
+<!-- Contains the active ringer icon and a hidden drawer containing all three ringer options. -->
+<FrameLayout
+ xmlns:android="http://schemas.android.com/apk/res/android"
+ android:id="@+id/volume_ringer_and_drawer_container"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:gravity="center"
+ android:paddingLeft="@dimen/volume_dialog_ringer_rows_padding"
+ android:paddingTop="@dimen/volume_dialog_ringer_rows_padding"
+ android:paddingRight="@dimen/volume_dialog_ringer_rows_padding"
+ android:paddingBottom="@dimen/volume_dialog_ringer_rows_padding"
+ android:background="@drawable/volume_background_top_legacy"
+ android:layoutDirection="ltr"
+ android:clipToPadding="false"
+ android:clipChildren="false">
+
+ <!-- Drawer view, invisible by default. -->
+ <FrameLayout
+ android:id="@+id/volume_drawer_container"
+ android:alpha="0.0"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:background="@drawable/volume_drawer_bg"
+ android:orientation="vertical">
+
+ <!-- View that is animated to a tapped ringer selection, so it appears selected. -->
+ <FrameLayout
+ android:id="@+id/volume_drawer_selection_background"
+ android:alpha="0.0"
+ android:layout_width="@dimen/volume_ringer_drawer_item_size"
+ android:layout_height="@dimen/volume_ringer_drawer_item_size"
+ android:layout_gravity="bottom|right"
+ android:background="@drawable/volume_drawer_selection_bg_legacy" />
+
+ <LinearLayout
+ android:id="@+id/volume_drawer_options"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:orientation="vertical">
+
+ <FrameLayout
+ android:id="@+id/volume_drawer_vibrate"
+ android:layout_width="@dimen/volume_ringer_drawer_item_size"
+ android:layout_height="@dimen/volume_ringer_drawer_item_size"
+ android:contentDescription="@string/volume_ringer_hint_vibrate"
+ android:gravity="center">
+
+ <ImageView
+ android:id="@+id/volume_drawer_vibrate_icon"
+ android:layout_width="@dimen/volume_ringer_drawer_icon_size"
+ android:layout_height="@dimen/volume_ringer_drawer_icon_size"
+ android:layout_gravity="center"
+ android:src="@drawable/ic_volume_ringer_vibrate"
+ android:tint="?android:attr/textColorPrimary" />
+
+ </FrameLayout>
+
+ <FrameLayout
+ android:id="@+id/volume_drawer_mute"
+ android:layout_width="@dimen/volume_ringer_drawer_item_size"
+ android:layout_height="@dimen/volume_ringer_drawer_item_size"
+ android:accessibilityTraversalAfter="@id/volume_drawer_vibrate"
+ android:contentDescription="@string/volume_ringer_hint_mute"
+ android:gravity="center">
+
+ <ImageView
+ android:id="@+id/volume_drawer_mute_icon"
+ android:layout_width="@dimen/volume_ringer_drawer_icon_size"
+ android:layout_height="@dimen/volume_ringer_drawer_icon_size"
+ android:layout_gravity="center"
+ android:src="@drawable/ic_speaker_mute"
+ android:tint="?android:attr/textColorPrimary" />
+
+ </FrameLayout>
+
+ <FrameLayout
+ android:id="@+id/volume_drawer_normal"
+ android:layout_width="@dimen/volume_ringer_drawer_item_size"
+ android:layout_height="@dimen/volume_ringer_drawer_item_size"
+ android:accessibilityTraversalAfter="@id/volume_drawer_mute"
+ android:contentDescription="@string/volume_ringer_hint_unmute"
+ android:gravity="center">
+
+ <ImageView
+ android:id="@+id/volume_drawer_normal_icon"
+ android:layout_width="@dimen/volume_ringer_drawer_icon_size"
+ android:layout_height="@dimen/volume_ringer_drawer_icon_size"
+ android:layout_gravity="center"
+ android:src="@drawable/ic_speaker_on"
+ android:tint="?android:attr/textColorPrimary" />
+
+ </FrameLayout>
+
+ </LinearLayout>
+
+ </FrameLayout>
+
+ <!-- The current ringer selection. When the drawer is opened, this animates to the corresponding
+ position in the drawer. When the drawer is closed, it animates back. -->
+ <FrameLayout
+ android:id="@+id/volume_new_ringer_active_icon_container"
+ android:layout_width="@dimen/volume_ringer_drawer_item_size"
+ android:layout_height="@dimen/volume_ringer_drawer_item_size"
+ android:layout_gravity="bottom|right"
+ android:contentDescription="@string/volume_ringer_change"
+ android:background="@drawable/volume_drawer_selection_bg_legacy">
+
+ <ImageView
+ android:id="@+id/volume_new_ringer_active_icon"
+ android:layout_width="@dimen/volume_ringer_drawer_icon_size"
+ android:layout_height="@dimen/volume_ringer_drawer_icon_size"
+ android:layout_gravity="center"
+ android:tint="?android:attr/textColorPrimaryInverse"
+ android:src="@drawable/ic_volume_media" />
+
+ </FrameLayout>
+
+</FrameLayout> \ No newline at end of file
diff --git a/packages/SystemUI/res/values/dimens.xml b/packages/SystemUI/res/values/dimens.xml
index 87e6aa061a50..c2d942ff5851 100644
--- a/packages/SystemUI/res/values/dimens.xml
+++ b/packages/SystemUI/res/values/dimens.xml
@@ -2064,5 +2064,11 @@
<dimen name="volume_panel_slice_vertical_padding">8dp</dimen>
<dimen name="volume_panel_slice_horizontal_padding">24dp</dimen>
+
+ <dimen name="volume_dialog_ringer_container_padding">10dp</dimen>
+ <dimen name="volume_ringer_item_size">40dp</dimen>
+ <dimen name="volume_ringer_icon_size">20dp</dimen>
+ <dimen name="volume_ringer_item_radius">12dp</dimen>
+ <dimen name="volume_dialog_ringer_item_margin_bottom">8dp</dimen>
<!-- Volume end -->
</resources>
diff --git a/packages/SystemUI/res/values/strings.xml b/packages/SystemUI/res/values/strings.xml
index c494e8525e0f..0aa5ccf7a2b4 100644
--- a/packages/SystemUI/res/values/strings.xml
+++ b/packages/SystemUI/res/values/strings.xml
@@ -1463,6 +1463,12 @@
<!-- The text for the notification history link. [CHAR LIMIT=40] -->
<string name="manage_notifications_history_text">History</string>
+ <!-- The accessibility description for the notification settings button in the notification shade. -->
+ <string name="notification_settings_button_description">Notification settings</string>
+
+ <!-- The accessibility description for the notification history button in the notification shade. -->
+ <string name="notification_history_button_description">Notification history</string>
+
<!-- Section title for notifications that have recently appeared. [CHAR LIMIT=40] -->
<string name="notification_section_header_incoming">New</string>
diff --git a/packages/SystemUI/res/values/styles.xml b/packages/SystemUI/res/values/styles.xml
index 1ab9242438b9..ab45b9f1b5bc 100644
--- a/packages/SystemUI/res/values/styles.xml
+++ b/packages/SystemUI/res/values/styles.xml
@@ -785,6 +785,16 @@
<item name="android:minWidth">0dp</item>
</style>
+ <style
+ name="TextAppearance.NotificationFooterButtonRedesign"
+ parent="@android:style/Widget.DeviceDefault.Button.Borderless">
+ <item name="android:textColor">?androidprv:attr/materialColorOnSurface</item>
+ <item name="android:drawableTint">?androidprv:attr/materialColorOnSurface</item>
+ <item name="android:textAllCaps">false</item>
+ <item name="android:textSize">16sp</item>
+ <item name="android:minWidth">0dp</item>
+ </style>
+
<style name="TextAppearance.HeadsUpStatusBarText"
parent="@*android:style/TextAppearance.DeviceDefault.Notification.Info">
</style>
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitor.java b/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitor.java
index b3ea75d00917..8b2cf7a735de 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitor.java
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitor.java
@@ -80,6 +80,7 @@ import android.hardware.fingerprint.FingerprintSensorPropertiesInternal;
import android.hardware.fingerprint.IFingerprintAuthenticatorsRegisteredCallback;
import android.hardware.usb.UsbManager;
import android.nfc.NfcAdapter;
+import android.os.BatteryManager;
import android.os.CancellationSignal;
import android.os.Handler;
import android.os.Looper;
@@ -345,9 +346,9 @@ public class KeyguardUpdateMonitor implements TrustManager.TrustListener, Dumpab
// Device provisioning state
private boolean mDeviceProvisioned;
- // Battery status
+ // Battery status (null until first update is received)
@VisibleForTesting
- BatteryStatus mBatteryStatus;
+ BatteryStatus mBatteryStatus = null;
@VisibleForTesting
boolean mIncompatibleCharger;
@@ -2367,9 +2368,27 @@ public class KeyguardUpdateMonitor implements TrustManager.TrustListener, Dumpab
watchForDeviceProvisioning();
}
- // Take a guess at initial SIM state, battery status and PLMN until we get an update
- mBatteryStatus = new BatteryStatus(BATTERY_STATUS_UNKNOWN, /* level= */ -1, /* plugged= */
- 0, CHARGING_POLICY_DEFAULT, /* maxChargingWattage= */0, /* present= */true);
+ // Request the initial battery level
+ mBackgroundExecutor.execute(() -> {
+ BatteryManager batteryManager = mContext.getSystemService(BatteryManager.class);
+ int level = -1;
+ if (batteryManager != null) {
+ int capacity = batteryManager.getIntProperty(
+ BatteryManager.BATTERY_PROPERTY_CAPACITY);
+ if (capacity >= 0 && capacity <= 100) {
+ level = capacity;
+ }
+ }
+ // Don't override if a valid battery status update has come in
+ final BatteryStatus status = new BatteryStatus(BATTERY_STATUS_UNKNOWN,
+ /* level= */ level, /* plugged= */ 0, CHARGING_POLICY_DEFAULT,
+ /* maxChargingWattage= */0, /* present= */true);
+ mMainExecutor.execute(() -> {
+ if (mBatteryStatus == null) {
+ handleBatteryUpdate(status);
+ }
+ });
+ });
// Watch for interesting updates
final IntentFilter filter = new IntentFilter();
@@ -3591,6 +3610,9 @@ public class KeyguardUpdateMonitor implements TrustManager.TrustListener, Dumpab
}
private boolean isBatteryUpdateInteresting(BatteryStatus old, BatteryStatus current) {
+ if (old == null) {
+ return true;
+ }
final boolean nowPluggedIn = current.isPluggedIn();
final boolean wasPluggedIn = old.isPluggedIn();
final boolean stateChangedWhilePluggedIn = wasPluggedIn && nowPluggedIn
@@ -3687,7 +3709,9 @@ public class KeyguardUpdateMonitor implements TrustManager.TrustListener, Dumpab
private void sendUpdates(KeyguardUpdateMonitorCallback callback) {
// Notify listener of the current state
- callback.onRefreshBatteryInfo(mBatteryStatus);
+ if (mBatteryStatus != null) {
+ callback.onRefreshBatteryInfo(mBatteryStatus);
+ }
callback.onTimeChanged();
callback.onPhoneStateChanged(mPhoneState);
callback.onRefreshCarrierInfo();
diff --git a/packages/SystemUI/src/com/android/systemui/ambient/touch/BouncerSwipeTouchHandler.kt b/packages/SystemUI/src/com/android/systemui/ambient/touch/BouncerSwipeTouchHandler.kt
index 7b3d337306b4..223a21db2016 100644
--- a/packages/SystemUI/src/com/android/systemui/ambient/touch/BouncerSwipeTouchHandler.kt
+++ b/packages/SystemUI/src/com/android/systemui/ambient/touch/BouncerSwipeTouchHandler.kt
@@ -38,9 +38,6 @@ import com.android.systemui.bouncer.shared.constants.KeyguardBouncerConstants
import com.android.systemui.communal.ui.viewmodel.CommunalViewModel
import com.android.systemui.keyguard.domain.interactor.KeyguardInteractor
import com.android.systemui.plugins.ActivityStarter
-import com.android.systemui.scene.domain.interactor.SceneInteractor
-import com.android.systemui.scene.shared.flag.SceneContainerFlag
-import com.android.systemui.scene.ui.view.WindowRootView
import com.android.systemui.shade.ShadeExpansionChangeEvent
import com.android.systemui.statusbar.NotificationShadeWindowController
import com.android.systemui.statusbar.phone.CentralSurfaces
@@ -48,7 +45,6 @@ import com.android.wm.shell.animation.FlingAnimationUtils
import java.util.Optional
import javax.inject.Inject
import javax.inject.Named
-import javax.inject.Provider
import kotlin.math.abs
import kotlin.math.hypot
import kotlin.math.max
@@ -78,8 +74,6 @@ constructor(
private val uiEventLogger: UiEventLogger,
private val activityStarter: ActivityStarter,
private val keyguardInteractor: KeyguardInteractor,
- private val sceneInteractor: SceneInteractor,
- private val windowRootViewProvider: Optional<Provider<WindowRootView>>,
) : TouchHandler {
/** An interface for creating ValueAnimators. */
interface ValueAnimatorCreator {
@@ -106,8 +100,6 @@ constructor(
currentScrimController = controller
}
- private val windowRootView by lazy { windowRootViewProvider.get().get() }
-
/** Determines whether the touch handler should process touches in fullscreen swiping mode */
private var touchAvailable = false
@@ -117,7 +109,7 @@ constructor(
e1: MotionEvent?,
e2: MotionEvent,
distanceX: Float,
- distanceY: Float,
+ distanceY: Float
): Boolean {
if (capture == null) {
capture =
@@ -136,11 +128,6 @@ constructor(
expanded = false
// Since the user is dragging the bouncer up, set scrimmed to false.
currentScrimController?.show()
-
- if (SceneContainerFlag.isEnabled) {
- sceneInteractor.onRemoteUserInputStarted("bouncer touch handler")
- e1?.apply { windowRootView.dispatchTouchEvent(e1) }
- }
}
}
if (capture != true) {
@@ -165,27 +152,20 @@ constructor(
/* cancelAction= */ null,
/* dismissShade= */ true,
/* afterKeyguardGone= */ true,
- /* deferred= */ false,
+ /* deferred= */ false
)
return true
}
- if (SceneContainerFlag.isEnabled) {
- windowRootView.dispatchTouchEvent(e2)
- } else {
- // For consistency, we adopt the expansion definition found in the
- // PanelViewController. In this case, expansion refers to the view above the
- // bouncer. As that view's expansion shrinks, the bouncer appears. The
- // bouncer
- // is fully hidden at full expansion (1) and fully visible when fully
- // collapsed
- // (0).
- touchSession?.apply {
- val screenTravelPercentage =
- (abs((this@outer.y - e2.y).toDouble()) / getBounds().height())
- .toFloat()
- setPanelExpansion(1 - screenTravelPercentage)
- }
+ // For consistency, we adopt the expansion definition found in the
+ // PanelViewController. In this case, expansion refers to the view above the
+ // bouncer. As that view's expansion shrinks, the bouncer appears. The bouncer
+ // is fully hidden at full expansion (1) and fully visible when fully collapsed
+ // (0).
+ touchSession?.apply {
+ val screenTravelPercentage =
+ (abs((this@outer.y - e2.y).toDouble()) / getBounds().height()).toFloat()
+ setPanelExpansion(1 - screenTravelPercentage)
}
}
@@ -214,7 +194,7 @@ constructor(
ShadeExpansionChangeEvent(
/* fraction= */ currentExpansion,
/* expanded= */ expanded,
- /* tracking= */ true,
+ /* tracking= */ true
)
currentScrimController?.expand(event)
}
@@ -367,7 +347,7 @@ constructor(
currentHeight,
targetHeight,
velocity,
- viewHeight,
+ viewHeight
)
} else {
// Shows the bouncer, i.e., fully collapses the space above the bouncer.
@@ -376,7 +356,7 @@ constructor(
currentHeight,
targetHeight,
velocity,
- viewHeight,
+ viewHeight
)
}
animator.start()
diff --git a/packages/SystemUI/src/com/android/systemui/ambient/touch/ShadeTouchHandler.kt b/packages/SystemUI/src/com/android/systemui/ambient/touch/ShadeTouchHandler.kt
index 05a10092695b..1951a23ebda7 100644
--- a/packages/SystemUI/src/com/android/systemui/ambient/touch/ShadeTouchHandler.kt
+++ b/packages/SystemUI/src/com/android/systemui/ambient/touch/ShadeTouchHandler.kt
@@ -27,15 +27,11 @@ import com.android.systemui.ambient.touch.TouchHandler.TouchSession
import com.android.systemui.ambient.touch.dagger.ShadeModule
import com.android.systemui.communal.domain.interactor.CommunalSettingsInteractor
import com.android.systemui.communal.ui.viewmodel.CommunalViewModel
-import com.android.systemui.scene.domain.interactor.SceneInteractor
-import com.android.systemui.scene.shared.flag.SceneContainerFlag
-import com.android.systemui.scene.ui.view.WindowRootView
import com.android.systemui.shade.ShadeViewController
import com.android.systemui.statusbar.phone.CentralSurfaces
import java.util.Optional
import javax.inject.Inject
import javax.inject.Named
-import javax.inject.Provider
import kotlin.math.abs
import kotlinx.coroutines.CoroutineScope
import com.android.app.tracing.coroutines.launchTraced as launch
@@ -53,10 +49,8 @@ constructor(
private val dreamManager: DreamManager,
private val communalViewModel: CommunalViewModel,
private val communalSettingsInteractor: CommunalSettingsInteractor,
- private val sceneInteractor: SceneInteractor,
- private val windowRootViewProvider: Optional<Provider<WindowRootView>>,
@param:Named(ShadeModule.NOTIFICATION_SHADE_GESTURE_INITIATION_HEIGHT)
- private val initiationHeight: Int,
+ private val initiationHeight: Int
) : TouchHandler {
/**
* Tracks whether or not we are capturing a given touch. Will be null before and after a touch.
@@ -66,8 +60,6 @@ constructor(
/** Determines whether the touch handler should process touches in fullscreen swiping mode */
private var touchAvailable = false
- private val windowRootView by lazy { windowRootViewProvider.get().get() }
-
init {
if (Flags.hubmodeFullscreenVerticalSwipeFix()) {
scope.launch {
@@ -108,7 +100,7 @@ constructor(
e1: MotionEvent?,
e2: MotionEvent,
distanceX: Float,
- distanceY: Float,
+ distanceY: Float
): Boolean {
if (capture == null) {
// Only capture swipes that are going downwards.
@@ -118,10 +110,6 @@ constructor(
if (Flags.hubmodeFullscreenVerticalSwipeFix()) touchAvailable
else true
if (capture == true) {
- if (SceneContainerFlag.isEnabled) {
- sceneInteractor.onRemoteUserInputStarted("shade touch handler")
- }
-
// Send the initial touches over, as the input listener has already
// processed these touches.
e1?.apply { sendTouchEvent(this) }
@@ -135,7 +123,7 @@ constructor(
e1: MotionEvent?,
e2: MotionEvent,
velocityX: Float,
- velocityY: Float,
+ velocityY: Float
): Boolean {
return capture == true
}
@@ -144,11 +132,6 @@ constructor(
}
private fun sendTouchEvent(event: MotionEvent) {
- if (SceneContainerFlag.isEnabled) {
- windowRootView.dispatchTouchEvent(event)
- return
- }
-
if (communalSettingsInteractor.isCommunalFlagEnabled() && !dreamManager.isDreaming) {
// Send touches to central surfaces only when on the glanceable hub while not dreaming.
// While sending touches where while dreaming will open the shade, the shade
diff --git a/packages/SystemUI/src/com/android/systemui/ambient/touch/dagger/ShadeModule.java b/packages/SystemUI/src/com/android/systemui/ambient/touch/dagger/ShadeModule.java
index 1c781d608b04..bc2f35467312 100644
--- a/packages/SystemUI/src/com/android/systemui/ambient/touch/dagger/ShadeModule.java
+++ b/packages/SystemUI/src/com/android/systemui/ambient/touch/dagger/ShadeModule.java
@@ -22,10 +22,8 @@ import com.android.systemui.ambient.touch.ShadeTouchHandler;
import com.android.systemui.ambient.touch.TouchHandler;
import com.android.systemui.dagger.qualifiers.Main;
import com.android.systemui.res.R;
-import com.android.systemui.scene.ui.view.WindowRootView;
import dagger.Binds;
-import dagger.BindsOptionalOf;
import dagger.Module;
import dagger.Provides;
import dagger.multibindings.IntoSet;
@@ -53,13 +51,6 @@ public abstract class ShadeModule {
ShadeTouchHandler touchHandler);
/**
- * Window root view is used to send touches to the scene container. Declaring as optional as it
- * may not be present on all SysUI variants.
- */
- @BindsOptionalOf
- abstract WindowRootView bindWindowRootView();
-
- /**
* Provides the height of the gesture area for notification swipe down.
*/
@Provides
diff --git a/packages/SystemUI/src/com/android/systemui/communal/domain/interactor/CommunalSceneTransitionInteractor.kt b/packages/SystemUI/src/com/android/systemui/communal/domain/interactor/CommunalSceneTransitionInteractor.kt
index 7b8c19c67d9b..ec5540172be5 100644
--- a/packages/SystemUI/src/com/android/systemui/communal/domain/interactor/CommunalSceneTransitionInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/communal/domain/interactor/CommunalSceneTransitionInteractor.kt
@@ -73,7 +73,7 @@ constructor(
private var progressJob: Job? = null
private val currentToState: KeyguardState
- get() = internalTransitionInteractor.currentTransitionInfoInternal.value.to
+ get() = internalTransitionInteractor.currentTransitionInfoInternal().to
/**
* The next keyguard state to trigger when exiting [CommunalScenes.Communal]. This is only used
@@ -197,7 +197,7 @@ constructor(
val newTransition =
TransitionInfo(
ownerName = this::class.java.simpleName,
- from = internalTransitionInteractor.currentTransitionInfoInternal.value.to,
+ from = internalTransitionInteractor.currentTransitionInfoInternal().to,
to = state,
animator = null,
modeOnCanceled = TransitionModeOnCanceled.REVERSE,
@@ -273,7 +273,7 @@ constructor(
}
private suspend fun startTransitionToGlanceableHub() {
- val currentState = internalTransitionInteractor.currentTransitionInfoInternal.value.to
+ val currentState = internalTransitionInteractor.currentTransitionInfoInternal().to
val newTransition =
TransitionInfo(
ownerName = this::class.java.simpleName,
diff --git a/packages/SystemUI/src/com/android/systemui/dreams/DreamOverlayService.java b/packages/SystemUI/src/com/android/systemui/dreams/DreamOverlayService.java
index 1ffbbd2a9f32..7a6ca0859a09 100644
--- a/packages/SystemUI/src/com/android/systemui/dreams/DreamOverlayService.java
+++ b/packages/SystemUI/src/com/android/systemui/dreams/DreamOverlayService.java
@@ -65,6 +65,7 @@ import com.android.systemui.dreams.dagger.DreamOverlayComponent;
import com.android.systemui.keyguard.domain.interactor.KeyguardInteractor;
import com.android.systemui.navigationbar.gestural.domain.GestureInteractor;
import com.android.systemui.navigationbar.gestural.domain.TaskMatcher;
+import com.android.systemui.scene.shared.flag.SceneContainerFlag;
import com.android.systemui.shade.ShadeExpansionChangeEvent;
import com.android.systemui.touch.TouchInsetManager;
import com.android.systemui.util.concurrency.DelayableExecutor;
@@ -502,10 +503,10 @@ public class DreamOverlayService extends android.service.dreams.DreamOverlayServ
mDreamOverlayContainerViewController =
dreamOverlayComponent.getDreamOverlayContainerViewController();
- // Touch monitor are also used with SceneContainer. See individual touch handlers for
- // handling of SceneContainer.
- mTouchMonitor = ambientTouchComponent.getTouchMonitor();
- mTouchMonitor.init();
+ if (!SceneContainerFlag.isEnabled()) {
+ mTouchMonitor = ambientTouchComponent.getTouchMonitor();
+ mTouchMonitor.init();
+ }
mStateController.setShouldShowComplications(shouldShowComplications());
diff --git a/packages/SystemUI/src/com/android/systemui/dreams/dagger/DreamOverlayModule.java b/packages/SystemUI/src/com/android/systemui/dreams/dagger/DreamOverlayModule.java
index 85fb90d32d98..12984efb52c2 100644
--- a/packages/SystemUI/src/com/android/systemui/dreams/dagger/DreamOverlayModule.java
+++ b/packages/SystemUI/src/com/android/systemui/dreams/dagger/DreamOverlayModule.java
@@ -30,10 +30,8 @@ import com.android.systemui.ambient.statusbar.ui.AmbientStatusBarViewController;
import com.android.systemui.dagger.qualifiers.Main;
import com.android.systemui.dreams.DreamOverlayContainerView;
import com.android.systemui.res.R;
-import com.android.systemui.scene.ui.view.WindowRootView;
import com.android.systemui.touch.TouchInsetManager;
-import dagger.BindsOptionalOf;
import dagger.Module;
import dagger.Provides;
@@ -56,13 +54,6 @@ public abstract class DreamOverlayModule {
public static final String DREAM_IN_TRANSLATION_Y_DURATION =
"dream_in_complications_translation_y_duration";
- /**
- * Window root view is used to send touches to the scene container. Declaring as optional as it
- * may not be present on all SysUI variants.
- */
- @BindsOptionalOf
- abstract WindowRootView bindWindowRootView();
-
/** */
@Provides
@DreamOverlayComponent.DreamOverlayScope
diff --git a/packages/SystemUI/src/com/android/systemui/dreams/touch/CommunalTouchHandler.java b/packages/SystemUI/src/com/android/systemui/dreams/touch/CommunalTouchHandler.java
index 42a68771cdcf..5ba780f9c99d 100644
--- a/packages/SystemUI/src/com/android/systemui/dreams/touch/CommunalTouchHandler.java
+++ b/packages/SystemUI/src/com/android/systemui/dreams/touch/CommunalTouchHandler.java
@@ -31,9 +31,6 @@ import com.android.systemui.ambient.touch.TouchHandler;
import com.android.systemui.common.ui.domain.interactor.ConfigurationInteractor;
import com.android.systemui.communal.domain.interactor.CommunalInteractor;
import com.android.systemui.dreams.touch.dagger.CommunalTouchModule;
-import com.android.systemui.scene.domain.interactor.SceneInteractor;
-import com.android.systemui.scene.shared.flag.SceneContainerFlag;
-import com.android.systemui.scene.ui.view.WindowRootView;
import com.android.systemui.statusbar.phone.CentralSurfaces;
import kotlinx.coroutines.Job;
@@ -45,7 +42,6 @@ import java.util.function.Consumer;
import javax.inject.Inject;
import javax.inject.Named;
-import javax.inject.Provider;
/** {@link TouchHandler} responsible for handling touches to open communal hub. **/
public class CommunalTouchHandler implements TouchHandler {
@@ -55,8 +51,6 @@ public class CommunalTouchHandler implements TouchHandler {
private final CommunalInteractor mCommunalInteractor;
private final ConfigurationInteractor mConfigurationInteractor;
- private final SceneInteractor mSceneInteractor;
- private final WindowRootView mWindowRootView;
private Boolean mIsEnabled = false;
private ArrayList<Job> mFlows = new ArrayList<>();
@@ -75,16 +69,12 @@ public class CommunalTouchHandler implements TouchHandler {
@Named(CommunalTouchModule.COMMUNAL_GESTURE_INITIATION_WIDTH) int initiationWidth,
CommunalInteractor communalInteractor,
ConfigurationInteractor configurationInteractor,
- SceneInteractor sceneInteractor,
- Optional<Provider<WindowRootView>> windowRootViewProvider,
Lifecycle lifecycle) {
mInitiationWidth = initiationWidth;
mCentralSurfaces = centralSurfaces;
mLifecycle = lifecycle;
mCommunalInteractor = communalInteractor;
mConfigurationInteractor = configurationInteractor;
- mSceneInteractor = sceneInteractor;
- mWindowRootView = windowRootViewProvider.get().get();
mFlows.add(collectFlow(
mLifecycle,
@@ -135,15 +125,8 @@ public class CommunalTouchHandler implements TouchHandler {
private void handleSessionStart(CentralSurfaces surfaces, TouchSession session) {
// Notification shade window has its own logic to be visible if the hub is open, no need to
// do anything here other than send touch events over.
- if (SceneContainerFlag.isEnabled()) {
- mSceneInteractor.onRemoteUserInputStarted("communal touch handler");
- }
session.registerInputListener(ev -> {
- if (SceneContainerFlag.isEnabled()) {
- mWindowRootView.dispatchTouchEvent((MotionEvent) ev);
- } else {
- surfaces.handleCommunalHubTouch((MotionEvent) ev);
- }
+ surfaces.handleCommunalHubTouch((MotionEvent) ev);
if (ev != null && ((MotionEvent) ev).getAction() == MotionEvent.ACTION_UP) {
var unused = session.pop();
}
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardService.java b/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardService.java
index 67625d04b467..32c2bc7c8620 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardService.java
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardService.java
@@ -81,6 +81,7 @@ import com.android.systemui.flags.FeatureFlags;
import com.android.systemui.keyguard.domain.interactor.KeyguardDismissInteractor;
import com.android.systemui.keyguard.domain.interactor.KeyguardEnabledInteractor;
import com.android.systemui.keyguard.domain.interactor.KeyguardInteractor;
+import com.android.systemui.keyguard.domain.interactor.KeyguardLockWhileAwakeInteractor;
import com.android.systemui.keyguard.domain.interactor.KeyguardStateCallbackInteractor;
import com.android.systemui.keyguard.domain.interactor.KeyguardWakeDirectlyToGoneInteractor;
import com.android.systemui.keyguard.ui.binder.KeyguardSurfaceBehindParamsApplier;
@@ -329,6 +330,8 @@ public class KeyguardService extends Service {
return new FoldGracePeriodProvider();
}
};
+ private final KeyguardLockWhileAwakeInteractor
+ mKeyguardLockWhileAwakeInteractor;
@Inject
public KeyguardService(
@@ -353,7 +356,8 @@ public class KeyguardService extends Service {
KeyguardWakeDirectlyToGoneInteractor keyguardWakeDirectlyToGoneInteractor,
KeyguardDismissInteractor keyguardDismissInteractor,
Lazy<DeviceEntryInteractor> deviceEntryInteractorLazy,
- KeyguardStateCallbackInteractor keyguardStateCallbackInteractor) {
+ KeyguardStateCallbackInteractor keyguardStateCallbackInteractor,
+ KeyguardLockWhileAwakeInteractor keyguardLockWhileAwakeInteractor) {
super();
mKeyguardViewMediator = keyguardViewMediator;
mKeyguardLifecyclesDispatcher = keyguardLifecyclesDispatcher;
@@ -385,6 +389,7 @@ public class KeyguardService extends Service {
mKeyguardEnabledInteractor = keyguardEnabledInteractor;
mKeyguardWakeDirectlyToGoneInteractor = keyguardWakeDirectlyToGoneInteractor;
mKeyguardDismissInteractor = keyguardDismissInteractor;
+ mKeyguardLockWhileAwakeInteractor = keyguardLockWhileAwakeInteractor;
}
@Override
@@ -656,6 +661,11 @@ public class KeyguardService extends Service {
public void doKeyguardTimeout(Bundle options) {
trace("doKeyguardTimeout");
checkPermission();
+
+ if (KeyguardWmStateRefactor.isEnabled()) {
+ mKeyguardLockWhileAwakeInteractor.onKeyguardServiceDoKeyguardTimeout(options);
+ }
+
mKeyguardViewMediator.doKeyguardTimeout(options);
}
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/data/repository/KeyguardRepository.kt b/packages/SystemUI/src/com/android/systemui/keyguard/data/repository/KeyguardRepository.kt
index bbaa9eb85d4a..d3c17ccd2d18 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/data/repository/KeyguardRepository.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/data/repository/KeyguardRepository.kt
@@ -269,6 +269,8 @@ interface KeyguardRepository {
/** The top of shortcut in screen, used by wallpaper to find remaining space in lockscreen */
val shortcutAbsoluteTop: StateFlow<Float>
+ val notificationStackAbsoluteBottom: StateFlow<Float>
+
/**
* Returns `true` if the keyguard is showing; `false` otherwise.
*
@@ -339,6 +341,12 @@ interface KeyguardRepository {
fun isShowKeyguardWhenReenabled(): Boolean
fun setShortcutAbsoluteTop(top: Float)
+
+ /**
+ * Set bottom of notifications from notification stack, and Magic Portrait will layout base on
+ * this value
+ */
+ fun setNotificationStackAbsoluteBottom(bottom: Float)
}
/** Encapsulates application state for the keyguard. */
@@ -635,6 +643,9 @@ constructor(
private val _shortcutAbsoluteTop = MutableStateFlow(0F)
override val shortcutAbsoluteTop = _shortcutAbsoluteTop.asStateFlow()
+ private val _notificationStackAbsoluteBottom = MutableStateFlow(0F)
+ override val notificationStackAbsoluteBottom = _notificationStackAbsoluteBottom.asStateFlow()
+
init {
val callback =
object : KeyguardStateController.Callback {
@@ -717,6 +728,10 @@ constructor(
_shortcutAbsoluteTop.value = top
}
+ override fun setNotificationStackAbsoluteBottom(bottom: Float) {
+ _notificationStackAbsoluteBottom.value = bottom
+ }
+
private fun dozeMachineStateToModel(state: DozeMachine.State): DozeStateModel {
return when (state) {
DozeMachine.State.UNINITIALIZED -> DozeStateModel.UNINITIALIZED
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/data/repository/KeyguardTransitionRepository.kt b/packages/SystemUI/src/com/android/systemui/keyguard/data/repository/KeyguardTransitionRepository.kt
index b7d0d453779f..3a5614fbc430 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/data/repository/KeyguardTransitionRepository.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/data/repository/KeyguardTransitionRepository.kt
@@ -25,6 +25,7 @@ import android.os.Trace
import android.util.Log
import com.android.app.animation.Interpolators
import com.android.app.tracing.coroutines.withContextTraced as withContext
+import com.android.systemui.Flags.transitionRaceCondition
import com.android.systemui.dagger.SysUISingleton
import com.android.systemui.dagger.qualifiers.Main
import com.android.systemui.keyguard.shared.model.KeyguardState
@@ -77,6 +78,8 @@ interface KeyguardTransitionRepository {
/** The [TransitionInfo] of the most recent call to [startTransition]. */
val currentTransitionInfoInternal: StateFlow<TransitionInfo>
+ /** The [TransitionInfo] of the most recent call to [startTransition]. */
+ val currentTransitionInfo: TransitionInfo
/**
* Interactors that require information about changes between [KeyguardState]s will call this to
@@ -132,7 +135,7 @@ constructor(@Main val mainDispatcher: CoroutineDispatcher) : KeyguardTransitionR
private var lastStep: TransitionStep = TransitionStep()
private var lastAnimator: ValueAnimator? = null
- private val _currentTransitionMutex = Mutex()
+ private val withContextMutex = Mutex()
private val _currentTransitionInfo: MutableStateFlow<TransitionInfo> =
MutableStateFlow(
TransitionInfo(
@@ -144,6 +147,16 @@ constructor(@Main val mainDispatcher: CoroutineDispatcher) : KeyguardTransitionR
)
override var currentTransitionInfoInternal = _currentTransitionInfo.asStateFlow()
+ @Volatile
+ override var currentTransitionInfo: TransitionInfo =
+ TransitionInfo(
+ ownerName = "",
+ from = KeyguardState.OFF,
+ to = KeyguardState.OFF,
+ animator = null,
+ )
+ private set
+
/*
* When manual control of the transition is requested, a unique [UUID] is used as the handle
* to permit calls to [updateTransition]
@@ -163,13 +176,17 @@ constructor(@Main val mainDispatcher: CoroutineDispatcher) : KeyguardTransitionR
}
override suspend fun startTransition(info: TransitionInfo): UUID? {
- _currentTransitionInfo.value = info
+ if (transitionRaceCondition()) {
+ currentTransitionInfo = info
+ } else {
+ _currentTransitionInfo.value = info
+ }
Log.d(TAG, "(Internal) Setting current transition info: $info")
// There is no fairness guarantee with 'withContext', which means that transitions could
// be processed out of order. Use a Mutex to guarantee ordering. [updateTransition]
// requires the same lock
- _currentTransitionMutex.lock()
+ withContextMutex.lock()
// Only used in a test environment
if (forceDelayForRaceConditionTest) {
delay(50L)
@@ -177,7 +194,7 @@ constructor(@Main val mainDispatcher: CoroutineDispatcher) : KeyguardTransitionR
// Animators must be started on the main thread.
return withContext("$TAG#startTransition", mainDispatcher) {
- _currentTransitionMutex.unlock()
+ withContextMutex.unlock()
if (lastStep.from == info.from && lastStep.to == info.to) {
Log.i(TAG, "Duplicate call to start the transition, rejecting: $info")
return@withContext null
@@ -265,9 +282,9 @@ constructor(@Main val mainDispatcher: CoroutineDispatcher) : KeyguardTransitionR
// There is no fairness guarantee with 'withContext', which means that transitions could
// be processed out of order. Use a Mutex to guarantee ordering. [startTransition]
// requires the same lock
- _currentTransitionMutex.lock()
+ withContextMutex.lock()
withContext("$TAG#updateTransition", mainDispatcher) {
- _currentTransitionMutex.unlock()
+ withContextMutex.unlock()
updateTransitionInternal(transitionId, value, state)
}
@@ -302,13 +319,23 @@ constructor(@Main val mainDispatcher: CoroutineDispatcher) : KeyguardTransitionR
// Tests runs on testDispatcher, which is not the main thread, causing the animator thread
// check to fail
if (testSetup) {
- _currentTransitionInfo.value =
- TransitionInfo(
- ownerName = ownerName,
- from = KeyguardState.OFF,
- to = to,
- animator = null,
- )
+ if (transitionRaceCondition()) {
+ currentTransitionInfo =
+ TransitionInfo(
+ ownerName = ownerName,
+ from = KeyguardState.OFF,
+ to = to,
+ animator = null,
+ )
+ } else {
+ _currentTransitionInfo.value =
+ TransitionInfo(
+ ownerName = ownerName,
+ from = KeyguardState.OFF,
+ to = to,
+ animator = null,
+ )
+ }
emitTransition(
TransitionStep(
KeyguardState.OFF,
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/FromGoneTransitionInteractor.kt b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/FromGoneTransitionInteractor.kt
index bc2ed71f156c..bd9836f3489d 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/FromGoneTransitionInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/FromGoneTransitionInteractor.kt
@@ -24,8 +24,6 @@ import com.android.systemui.dagger.SysUISingleton
import com.android.systemui.dagger.qualifiers.Background
import com.android.systemui.dagger.qualifiers.Main
import com.android.systemui.keyguard.KeyguardWmStateRefactor
-import com.android.systemui.keyguard.data.repository.BiometricSettingsRepository
-import com.android.systemui.keyguard.data.repository.KeyguardRepository
import com.android.systemui.keyguard.data.repository.KeyguardTransitionRepository
import com.android.systemui.keyguard.shared.model.KeyguardState
import com.android.systemui.keyguard.shared.model.TransitionModeOnCanceled
@@ -54,9 +52,7 @@ constructor(
powerInteractor: PowerInteractor,
private val communalSceneInteractor: CommunalSceneInteractor,
keyguardOcclusionInteractor: KeyguardOcclusionInteractor,
- private val biometricSettingsRepository: BiometricSettingsRepository,
- private val keyguardRepository: KeyguardRepository,
- private val keyguardEnabledInteractor: KeyguardEnabledInteractor,
+ private val keyguardLockWhileAwakeInteractor: KeyguardLockWhileAwakeInteractor,
) :
TransitionInteractor(
fromState = KeyguardState.GONE,
@@ -78,7 +74,9 @@ constructor(
}
fun showKeyguard() {
- scope.launch("$TAG#showKeyguard") { startTransitionTo(KeyguardState.LOCKSCREEN) }
+ scope.launch("$TAG#showKeyguard") {
+ startTransitionTo(KeyguardState.LOCKSCREEN, ownerReason = "showKeyguard()")
+ }
}
/**
@@ -102,34 +100,18 @@ constructor(
// Primarily for when the user chooses to lock down the device
private fun listenForGoneToLockscreenOrHubOrOccluded() {
if (KeyguardWmStateRefactor.isEnabled) {
- scope.launch("$TAG#listenForGoneToLockscreenOrHub") {
- biometricSettingsRepository.isCurrentUserInLockdown
- .distinctUntilChanged()
- .filterRelevantKeyguardStateAnd { inLockdown -> inLockdown }
+ scope.launch {
+ keyguardLockWhileAwakeInteractor.lockWhileAwakeEvents
+ .filterRelevantKeyguardState()
.sample(communalSceneInteractor.isIdleOnCommunalNotEditMode, ::Pair)
- .collect { (_, isIdleOnCommunal) ->
+ .collect { (lockReason, idleOnCommunal) ->
val to =
- if (isIdleOnCommunal) {
+ if (idleOnCommunal) {
KeyguardState.GLANCEABLE_HUB
} else {
KeyguardState.LOCKSCREEN
}
- startTransitionTo(to, ownerReason = "User initiated lockdown")
- }
- }
-
- scope.launch {
- keyguardRepository.isKeyguardEnabled
- .filterRelevantKeyguardStateAnd { enabled -> enabled }
- .sample(keyguardEnabledInteractor.showKeyguardWhenReenabled)
- .filter { reshow -> reshow }
- .collect {
- startTransitionTo(
- KeyguardState.LOCKSCREEN,
- ownerReason =
- "Keyguard was re-enabled, and we weren't GONE when it " +
- "was originally disabled",
- )
+ startTransitionTo(to, ownerReason = "lockWhileAwake: $lockReason")
}
}
} else {
@@ -146,7 +128,10 @@ constructor(
} else {
KeyguardState.LOCKSCREEN
}
- startTransitionTo(to)
+ startTransitionTo(
+ to,
+ ownerReason = "keyguard interactor says keyguard is showing",
+ )
}
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/FromLockscreenTransitionInteractor.kt b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/FromLockscreenTransitionInteractor.kt
index a7dde34e3026..b60e98a68162 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/FromLockscreenTransitionInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/FromLockscreenTransitionInteractor.kt
@@ -40,6 +40,7 @@ import com.android.systemui.scene.shared.flag.SceneContainerFlag
import com.android.systemui.scene.shared.model.Scenes
import com.android.systemui.shade.data.repository.ShadeRepository
import com.android.systemui.util.kotlin.Utils.Companion.sample as sampleCombine
+import com.android.systemui.util.kotlin.sample
import java.util.UUID
import javax.inject.Inject
import kotlin.time.Duration.Companion.milliseconds
@@ -132,11 +133,10 @@ constructor(
scope.launch("$TAG#listenForLockscreenToDreaming") {
keyguardInteractor.isAbleToDream
.filterRelevantKeyguardState()
- .sampleCombine(
- internalTransitionInteractor.currentTransitionInfoInternal,
- transitionInteractor.isFinishedIn(KeyguardState.LOCKSCREEN),
- )
- .collect { (isAbleToDream, transitionInfo, isOnLockscreen) ->
+ .sample(transitionInteractor.isFinishedIn(KeyguardState.LOCKSCREEN), ::Pair)
+ .collect { (isAbleToDream, isOnLockscreen) ->
+ val transitionInfo =
+ internalTransitionInteractor.currentTransitionInfoInternal()
val isTransitionInterruptible =
transitionInfo.to == KeyguardState.LOCKSCREEN &&
!invalidFromStates.contains(transitionInfo.from)
@@ -179,7 +179,6 @@ constructor(
shadeRepository.legacyShadeExpansion
.sampleCombine(
transitionInteractor.startedKeyguardTransitionStep,
- internalTransitionInteractor.currentTransitionInfoInternal,
keyguardInteractor.statusBarState,
keyguardInteractor.isKeyguardDismissible,
keyguardInteractor.isKeyguardOccluded,
@@ -188,11 +187,12 @@ constructor(
(
shadeExpansion,
startedStep,
- currentTransitionInfo,
statusBarState,
isKeyguardUnlocked,
isKeyguardOccluded) ->
val id = transitionId
+ val currentTransitionInfo =
+ internalTransitionInteractor.currentTransitionInfoInternal()
if (id != null) {
if (startedStep.to == KeyguardState.PRIMARY_BOUNCER) {
// An existing `id` means a transition is started, and calls to
@@ -288,6 +288,7 @@ constructor(
startTransitionTo(
KeyguardState.GONE,
modeOnCanceled = TransitionModeOnCanceled.RESET,
+ ownerReason = "keyguard interactor says keyguard is going away",
)
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/InternalKeyguardTransitionInteractor.kt b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/InternalKeyguardTransitionInteractor.kt
index 2cc6afa2f407..05078346399a 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/InternalKeyguardTransitionInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/InternalKeyguardTransitionInteractor.kt
@@ -17,13 +17,13 @@
package com.android.systemui.keyguard.domain.interactor
import android.annotation.FloatRange
+import com.android.systemui.Flags.transitionRaceCondition
import com.android.systemui.dagger.SysUISingleton
import com.android.systemui.keyguard.data.repository.KeyguardTransitionRepository
import com.android.systemui.keyguard.shared.model.TransitionInfo
import com.android.systemui.keyguard.shared.model.TransitionState
import java.util.UUID
import javax.inject.Inject
-import kotlinx.coroutines.flow.StateFlow
/**
* This interactor provides direct access to [KeyguardTransitionRepository] internals and exposes
@@ -32,9 +32,7 @@ import kotlinx.coroutines.flow.StateFlow
@SysUISingleton
class InternalKeyguardTransitionInteractor
@Inject
-constructor(
- private val repository: KeyguardTransitionRepository,
-) {
+constructor(private val repository: KeyguardTransitionRepository) {
/**
* The [TransitionInfo] of the most recent call to
@@ -58,14 +56,19 @@ constructor(
* *will* be emitted, and therefore that it can safely request an AOD -> LOCKSCREEN transition
* which will subsequently cancel GONE -> AOD.
*/
- internal val currentTransitionInfoInternal: StateFlow<TransitionInfo> =
- repository.currentTransitionInfoInternal
+ internal fun currentTransitionInfoInternal(): TransitionInfo {
+ return if (transitionRaceCondition()) {
+ repository.currentTransitionInfo
+ } else {
+ repository.currentTransitionInfoInternal.value
+ }
+ }
suspend fun startTransition(info: TransitionInfo) = repository.startTransition(info)
suspend fun updateTransition(
transitionId: UUID,
@FloatRange(from = 0.0, to = 1.0) value: Float,
- state: TransitionState
+ state: TransitionState,
) = repository.updateTransition(transitionId, value, state)
}
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardDismissTransitionInteractor.kt b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardDismissTransitionInteractor.kt
index c19bbbce3b4b..4793d95b121c 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardDismissTransitionInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardDismissTransitionInteractor.kt
@@ -17,6 +17,7 @@
package com.android.systemui.keyguard.domain.interactor
import android.util.Log
+import com.android.systemui.Flags.transitionRaceCondition
import com.android.systemui.dagger.SysUISingleton
import com.android.systemui.keyguard.data.repository.KeyguardTransitionRepository
import com.android.systemui.keyguard.shared.model.KeyguardState
@@ -51,7 +52,13 @@ constructor(
fun startDismissKeyguardTransition(reason: String = "") {
if (SceneContainerFlag.isEnabled) return
Log.d(TAG, "#startDismissKeyguardTransition(reason=$reason)")
- when (val startedState = repository.currentTransitionInfoInternal.value.to) {
+ val startedState =
+ if (transitionRaceCondition()) {
+ repository.currentTransitionInfo.to
+ } else {
+ repository.currentTransitionInfoInternal.value.to
+ }
+ when (startedState) {
LOCKSCREEN -> fromLockscreenTransitionInteractor.dismissKeyguard()
PRIMARY_BOUNCER -> fromPrimaryBouncerTransitionInteractor.dismissPrimaryBouncer()
ALTERNATE_BOUNCER -> fromAlternateBouncerTransitionInteractor.dismissAlternateBouncer()
@@ -61,7 +68,7 @@ constructor(
KeyguardState.GONE ->
Log.i(
TAG,
- "Already transitioning to GONE; ignoring startDismissKeyguardTransition."
+ "Already transitioning to GONE; ignoring startDismissKeyguardTransition.",
)
else -> Log.e(TAG, "We don't know how to dismiss keyguard from state $startedState.")
}
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardEnabledInteractor.kt b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardEnabledInteractor.kt
index 5f08aa320c95..631e44aca26d 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardEnabledInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardEnabledInteractor.kt
@@ -22,7 +22,7 @@ import com.android.systemui.keyguard.data.repository.BiometricSettingsRepository
import com.android.systemui.keyguard.data.repository.KeyguardRepository
import com.android.systemui.keyguard.shared.model.KeyguardState
import com.android.systemui.scene.shared.flag.SceneContainerFlag
-import com.android.systemui.util.kotlin.Utils.Companion.sample as sampleCombine
+import com.android.systemui.util.kotlin.sample
import javax.inject.Inject
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.flow.Flow
@@ -74,11 +74,9 @@ constructor(
.onEach { SceneContainerFlag.assertInLegacyMode() }
// Whenever the keyguard is disabled...
.filter { enabled -> !enabled }
- .sampleCombine(
- internalTransitionInteractor.currentTransitionInfoInternal,
- biometricSettingsRepository.isCurrentUserInLockdown,
- )
- .map { (_, transitionInfo, inLockdown) ->
+ .sample(biometricSettingsRepository.isCurrentUserInLockdown, ::Pair)
+ .map { (_, inLockdown) ->
+ val transitionInfo = internalTransitionInteractor.currentTransitionInfoInternal()
// ...we hide the keyguard, if it's showing and we're not in lockdown. In that case,
// we want to remember that and re-show it when keyguard is enabled again.
transitionInfo.to != KeyguardState.GONE && !inLockdown
@@ -93,11 +91,10 @@ constructor(
if (!SceneContainerFlag.isEnabled) {
repository.isKeyguardEnabled
.filter { enabled -> !enabled }
- .sampleCombine(
- biometricSettingsRepository.isCurrentUserInLockdown,
- internalTransitionInteractor.currentTransitionInfoInternal,
- )
- .collect { (_, inLockdown, currentTransitionInfo) ->
+ .sample(biometricSettingsRepository.isCurrentUserInLockdown, ::Pair)
+ .collect { (_, inLockdown) ->
+ val currentTransitionInfo =
+ internalTransitionInteractor.currentTransitionInfoInternal()
if (currentTransitionInfo.to != KeyguardState.GONE && !inLockdown) {
keyguardDismissTransitionInteractor.startDismissKeyguardTransition(
"keyguard disabled"
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardInteractor.kt b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardInteractor.kt
index 29c6d5aa5ea2..b24ca1a8d345 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardInteractor.kt
@@ -545,6 +545,10 @@ constructor(
repository.isKeyguardGoingAway.value = isGoingAway
}
+ fun setNotificationStackAbsoluteBottom(bottom: Float) {
+ repository.setNotificationStackAbsoluteBottom(bottom)
+ }
+
companion object {
private const val TAG = "KeyguardInteractor"
}
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardLockWhileAwakeInteractor.kt b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardLockWhileAwakeInteractor.kt
new file mode 100644
index 000000000000..0ab3e5c0927f
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardLockWhileAwakeInteractor.kt
@@ -0,0 +1,121 @@
+/*
+ * 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.
+ */
+
+package com.android.systemui.keyguard.domain.interactor
+
+import android.os.Bundle
+import com.android.systemui.dagger.SysUISingleton
+import com.android.systemui.keyguard.data.repository.BiometricSettingsRepository
+import com.android.systemui.util.kotlin.sample
+import javax.inject.Inject
+import kotlinx.coroutines.flow.Flow
+import kotlinx.coroutines.flow.MutableStateFlow
+import kotlinx.coroutines.flow.distinctUntilChanged
+import kotlinx.coroutines.flow.filter
+import kotlinx.coroutines.flow.filterNotNull
+import kotlinx.coroutines.flow.map
+import kotlinx.coroutines.flow.merge
+
+/**
+ * Emitted when we receive a [KeyguardLockWhileAwakeInteractor.onKeyguardServiceDoKeyguardTimeout]
+ * call.
+ *
+ * Includes a timestamp so it's not conflated by the StateFlow.
+ */
+data class KeyguardTimeoutWhileAwakeEvent(val timestamp: Long, val options: Bundle?)
+
+/** The reason we're locking while awake, used for logging. */
+enum class LockWhileAwakeReason(private val logReason: String) {
+ LOCKDOWN("Lockdown initiated."),
+ KEYGUARD_REENABLED(
+ "Keyguard was re-enabled. We weren't unlocked when it was disabled, " +
+ "so we're returning to the lockscreen."
+ ),
+ KEYGUARD_TIMEOUT_WHILE_SCREEN_ON(
+ "Timed out while the screen was kept on, or WM#lockNow() was called."
+ );
+
+ override fun toString(): String {
+ return logReason
+ }
+}
+
+/**
+ * Logic around cases where the device locks while still awake (transitioning from GONE ->
+ * LOCKSCREEN), vs. the more common cases of a power button press or screen timeout, which result in
+ * the device going to sleep.
+ *
+ * This is possible in the following situations:
+ * - The user initiates lockdown from the power menu.
+ * - Theft detection, etc. has requested lockdown.
+ * - The keyguard was disabled while visible, and has now been re-enabled, so it's re-showing.
+ * - Someone called WM#lockNow().
+ * - The screen timed out, but an activity with FLAG_ALLOW_LOCK_WHILE_SCREEN_ON is on top.
+ */
+@SysUISingleton
+class KeyguardLockWhileAwakeInteractor
+@Inject
+constructor(
+ biometricSettingsRepository: BiometricSettingsRepository,
+ keyguardEnabledInteractor: KeyguardEnabledInteractor,
+) {
+ /** Emits whenever a timeout event is received by [KeyguardService]. */
+ private val timeoutEvents: MutableStateFlow<KeyguardTimeoutWhileAwakeEvent?> =
+ MutableStateFlow(null)
+
+ /** Emits whenever the current user is in lockdown mode. */
+ private val inLockdown: Flow<LockWhileAwakeReason> =
+ biometricSettingsRepository.isCurrentUserInLockdown
+ .distinctUntilChanged()
+ .filter { inLockdown -> inLockdown }
+ .map { LockWhileAwakeReason.LOCKDOWN }
+
+ /**
+ * Emits whenever the keyguard is re-enabled, and we need to return to lockscreen due to the
+ * device being locked when the keyguard was originally disabled.
+ */
+ private val keyguardReenabled: Flow<LockWhileAwakeReason> =
+ keyguardEnabledInteractor.isKeyguardEnabled
+ .filter { enabled -> enabled }
+ .sample(keyguardEnabledInteractor.showKeyguardWhenReenabled)
+ .filter { reshow -> reshow }
+ .map { LockWhileAwakeReason.KEYGUARD_REENABLED }
+
+ /** Emits whenever we should lock while the screen is on, for any reason. */
+ val lockWhileAwakeEvents: Flow<LockWhileAwakeReason> =
+ merge(
+ inLockdown,
+ keyguardReenabled,
+ timeoutEvents.filterNotNull().map {
+ LockWhileAwakeReason.KEYGUARD_TIMEOUT_WHILE_SCREEN_ON
+ },
+ )
+
+ /**
+ * Called by [KeyguardService] when it receives a doKeyguardTimeout() call. This indicates that
+ * the device locked while the screen was on.
+ *
+ * [options] appears to be no longer used, but we'll keep it in this interactor in case that
+ * turns out not to be true.
+ */
+ fun onKeyguardServiceDoKeyguardTimeout(options: Bundle?) {
+ timeoutEvents.value =
+ KeyguardTimeoutWhileAwakeEvent(
+ timestamp = System.currentTimeMillis(),
+ options = options,
+ )
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardOcclusionInteractor.kt b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardOcclusionInteractor.kt
index 7f1e881c0863..278a98f8b157 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardOcclusionInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardOcclusionInteractor.kt
@@ -80,7 +80,7 @@ constructor(
// *_BOUNCER -> LOCKSCREEN.
return powerInteractor.detailedWakefulness.value.powerButtonLaunchGestureTriggered &&
KeyguardState.deviceIsAsleepInState(
- internalTransitionInteractor.currentTransitionInfoInternal.value.to
+ internalTransitionInteractor.currentTransitionInfoInternal().to
)
}
@@ -100,13 +100,13 @@ constructor(
scene = Scenes.Gone,
stateWithoutSceneContainer = KeyguardState.GONE,
),
- ::Pair
+ ::Pair,
)
.map { (wakefulness, isOnGone) ->
wakefulness.powerButtonLaunchGestureTriggered && !isOnGone
},
// Emit false once that activity goes away.
- isShowWhenLockedActivityOnTop.filter { !it }.map { false }
+ isShowWhenLockedActivityOnTop.filter { !it }.map { false },
)
.stateIn(applicationScope, SharingStarted.Eagerly, false)
@@ -134,7 +134,7 @@ constructor(
*/
fun setWmNotifiedShowWhenLockedActivityOnTop(
showWhenLockedActivityOnTop: Boolean,
- taskInfo: RunningTaskInfo? = null
+ taskInfo: RunningTaskInfo? = null,
) {
repository.setShowWhenLockedActivityInfo(showWhenLockedActivityOnTop, taskInfo)
}
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardTransitionBootInteractor.kt b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardTransitionBootInteractor.kt
index cddeaaf27fb9..b986d56e9a82 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardTransitionBootInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardTransitionBootInteractor.kt
@@ -61,7 +61,7 @@ constructor(
fun start() {
scope.launch {
- if (internalTransitionInteractor.currentTransitionInfoInternal.value.from != OFF) {
+ if (internalTransitionInteractor.currentTransitionInfoInternal().from != OFF) {
Log.e(
"KeyguardTransitionInteractor",
"showLockscreenOnBoot emitted, but we've already " +
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/TransitionInteractor.kt b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/TransitionInteractor.kt
index 249982d710a7..abd7f90bbf22 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/TransitionInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/TransitionInteractor.kt
@@ -71,14 +71,14 @@ sealed class TransitionInteractor(
ownerReason: String = "",
): UUID? {
toState.checkValidState()
- if (fromState != internalTransitionInteractor.currentTransitionInfoInternal.value.to) {
+ if (fromState != internalTransitionInteractor.currentTransitionInfoInternal().to) {
Log.e(
name,
"Ignoring startTransition: This interactor asked to transition from " +
"$fromState -> $toState, but we last transitioned to " +
- "${internalTransitionInteractor.currentTransitionInfoInternal.value.to}, not" +
+ "${internalTransitionInteractor.currentTransitionInfoInternal().to}, not" +
" $fromState. This should never happen - check currentTransitionInfoInternal" +
- " or use filterRelevantKeyguardState before starting transitions."
+ " or use filterRelevantKeyguardState before starting transitions.",
)
return null
}
@@ -149,7 +149,7 @@ sealed class TransitionInteractor(
if (keyguardInteractor.isKeyguardDismissible.value) {
startTransitionTo(
KeyguardState.GONE,
- ownerReason = "Power button gesture while keyguard is dismissible"
+ ownerReason = "Power button gesture while keyguard is dismissible",
)
return true
@@ -159,7 +159,7 @@ sealed class TransitionInteractor(
// should transition to GONE.
startTransitionTo(
KeyguardState.GONE,
- ownerReason = "Power button gesture on dismissable keyguard"
+ ownerReason = "Power button gesture on dismissable keyguard",
)
return true
@@ -190,16 +190,13 @@ sealed class TransitionInteractor(
startTransitionTo(
toState = keyguardInteractor.asleepKeyguardState.value,
modeOnCanceled = modeOnCanceled,
- ownerReason = "Sleep transition triggered"
+ ownerReason = "Sleep transition triggered",
)
}
}
/** This signal may come in before the occlusion signal, and can provide a custom transition */
- fun listenForTransitionToCamera(
- scope: CoroutineScope,
- keyguardInteractor: KeyguardInteractor,
- ) {
+ fun listenForTransitionToCamera(scope: CoroutineScope, keyguardInteractor: KeyguardInteractor) {
if (!KeyguardWmStateRefactor.isEnabled) {
scope.launch {
keyguardInteractor.onCameraLaunchDetected.filterRelevantKeyguardState().collect {
@@ -223,7 +220,7 @@ sealed class TransitionInteractor(
* [startedKeyguardState] as it does not wait for the emission of the first STARTED step.
*/
fun inOrTransitioningToRelevantKeyguardState(): Boolean {
- return internalTransitionInteractor.currentTransitionInfoInternal.value.to == fromState
+ return internalTransitionInteractor.currentTransitionInfoInternal().to == fromState
}
/**
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/WindowManagerLockscreenVisibilityInteractor.kt b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/WindowManagerLockscreenVisibilityInteractor.kt
index a09cd7c12d42..a1f606740cd9 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/WindowManagerLockscreenVisibilityInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/WindowManagerLockscreenVisibilityInteractor.kt
@@ -19,6 +19,7 @@
package com.android.systemui.keyguard.domain.interactor
import com.android.compose.animation.scene.ObservableTransitionState
+import com.android.systemui.Flags.transitionRaceCondition
import com.android.systemui.dagger.SysUISingleton
import com.android.systemui.deviceentry.domain.interactor.DeviceEntryInteractor
import com.android.systemui.keyguard.data.repository.KeyguardTransitionRepository
@@ -65,7 +66,7 @@ constructor(
combine(
transitionInteractor.isFinishedIn(
scene = Scenes.Gone,
- stateWithoutSceneContainer = KeyguardState.GONE
+ stateWithoutSceneContainer = KeyguardState.GONE,
),
wakeToGoneInteractor.canWakeDirectlyToGone,
) { isOnGone, canWakeDirectlyToGone ->
@@ -197,11 +198,11 @@ constructor(
combine(
transitionInteractor.isInTransition(
edge = Edge.create(to = Scenes.Gone),
- edgeWithoutSceneContainer = Edge.create(to = KeyguardState.GONE)
+ edgeWithoutSceneContainer = Edge.create(to = KeyguardState.GONE),
),
transitionInteractor.isFinishedIn(
scene = Scenes.Gone,
- stateWithoutSceneContainer = KeyguardState.GONE
+ stateWithoutSceneContainer = KeyguardState.GONE,
),
surfaceBehindInteractor.isAnimatingSurface,
notificationLaunchAnimationInteractor.isLaunchAnimationRunning,
@@ -231,7 +232,7 @@ constructor(
combine(
transitionInteractor.currentKeyguardState,
wakeToGoneInteractor.canWakeDirectlyToGone,
- ::Pair
+ ::Pair,
)
.sample(transitionInteractor.startedStepWithPrecedingStep, ::toTriple)
.map { (currentState, canWakeDirectlyToGone, startedWithPrev) ->
@@ -242,7 +243,12 @@ constructor(
startedFromStep.transitionState == TransitionState.CANCELED &&
startedFromStep.from == KeyguardState.GONE
- val transitionInfo = transitionRepository.currentTransitionInfoInternal.value
+ val transitionInfo =
+ if (transitionRaceCondition()) {
+ transitionRepository.currentTransitionInfo
+ } else {
+ transitionRepository.currentTransitionInfoInternal.value
+ }
val wakingDirectlyToGone =
deviceIsAsleepInState(transitionInfo.from) &&
transitionInfo.to == KeyguardState.GONE
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/scenetransition/LockscreenSceneTransitionInteractor.kt b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/scenetransition/LockscreenSceneTransitionInteractor.kt
index 5524b20aa8f8..aa44b6d46289 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/scenetransition/LockscreenSceneTransitionInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/scenetransition/LockscreenSceneTransitionInteractor.kt
@@ -106,7 +106,7 @@ constructor(
private suspend fun handleIdle(
prevTransition: ObservableTransitionState,
- idle: ObservableTransitionState.Idle
+ idle: ObservableTransitionState.Idle,
) {
if (currentTransitionId == null) return
if (prevTransition !is ObservableTransitionState.Transition) return
@@ -133,10 +133,10 @@ constructor(
val newTransition =
TransitionInfo(
ownerName = this::class.java.simpleName,
- from = internalTransitionInteractor.currentTransitionInfoInternal.value.to,
+ from = internalTransitionInteractor.currentTransitionInfoInternal().to,
to = state,
animator = null,
- modeOnCanceled = TransitionModeOnCanceled.REVERSE
+ modeOnCanceled = TransitionModeOnCanceled.REVERSE,
)
currentTransitionId = internalTransitionInteractor.startTransition(newTransition)
internalTransitionInteractor.updateTransition(currentTransitionId!!, 1f, FINISHED)
@@ -152,8 +152,7 @@ constructor(
private suspend fun handleTransition(transition: ObservableTransitionState.Transition) {
if (transition.fromContent == Scenes.Lockscreen) {
if (currentTransitionId != null) {
- val currentToState =
- internalTransitionInteractor.currentTransitionInfoInternal.value.to
+ val currentToState = internalTransitionInteractor.currentTransitionInfoInternal().to
if (currentToState == UNDEFINED) {
transitionKtfTo(transitionInteractor.startedKeyguardTransitionStep.value.from)
}
@@ -197,21 +196,21 @@ constructor(
from = UNDEFINED,
to = repository.nextLockscreenTargetState.value,
animator = null,
- modeOnCanceled = TransitionModeOnCanceled.RESET
+ modeOnCanceled = TransitionModeOnCanceled.RESET,
)
repository.nextLockscreenTargetState.value = DEFAULT_STATE
startTransition(newTransition)
}
private suspend fun startTransitionFromLockscreen() {
- val currentState = internalTransitionInteractor.currentTransitionInfoInternal.value.to
+ val currentState = internalTransitionInteractor.currentTransitionInfoInternal().to
val newTransition =
TransitionInfo(
ownerName = this::class.java.simpleName,
from = currentState,
to = UNDEFINED,
animator = null,
- modeOnCanceled = TransitionModeOnCanceled.RESET
+ modeOnCanceled = TransitionModeOnCanceled.RESET,
)
startTransition(newTransition)
}
@@ -228,7 +227,7 @@ constructor(
internalTransitionInteractor.updateTransition(
currentTransitionId!!,
progress.coerceIn(0f, 1f),
- RUNNING
+ RUNNING,
)
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/DozingToOccludedTransitionViewModel.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/DozingToOccludedTransitionViewModel.kt
index 12bcc7ecbab8..b15cacf077a4 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/DozingToOccludedTransitionViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/DozingToOccludedTransitionViewModel.kt
@@ -35,9 +35,7 @@ import kotlinx.coroutines.flow.Flow
@SysUISingleton
class DozingToOccludedTransitionViewModel
@Inject
-constructor(
- animationFlow: KeyguardTransitionAnimationFlow,
-) : DeviceEntryIconTransition {
+constructor(animationFlow: KeyguardTransitionAnimationFlow) : DeviceEntryIconTransition {
private val transitionAnimation =
animationFlow.setup(
duration = FromAodTransitionInteractor.TO_OCCLUDED_DURATION,
@@ -56,11 +54,7 @@ constructor(
var currentAlpha = 0f
return transitionAnimation.sharedFlow(
duration = 250.milliseconds,
- startTime = if (lightRevealMigration()) {
- 100.milliseconds // Wait for the light reveal to "hit" the LS elements.
- } else {
- 0.milliseconds
- },
+ startTime = 0.milliseconds,
onStart = {
if (lightRevealMigration()) {
currentAlpha = viewState.alpha()
@@ -69,7 +63,6 @@ constructor(
}
},
onStep = { MathUtils.lerp(currentAlpha, 0f, it) },
- onCancel = { 0f },
)
}
diff --git a/packages/SystemUI/src/com/android/systemui/qs/QSPanel.java b/packages/SystemUI/src/com/android/systemui/qs/QSPanel.java
index d3bed27ab2ab..602f593731c5 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/QSPanel.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/QSPanel.java
@@ -116,6 +116,8 @@ public class QSPanel extends LinearLayout implements Tunable {
@Nullable
private View mMediaViewPlaceHolderForScene;
+ private boolean mHadConfigurationChangeWhileDetached;
+
public QSPanel(Context context, AttributeSet attrs) {
super(context, attrs);
mUsingMediaPlayer = useQsMediaPlayer(context);
@@ -425,10 +427,23 @@ public class QSPanel extends LinearLayout implements Tunable {
@Override
protected void onConfigurationChanged(Configuration newConfig) {
super.onConfigurationChanged(newConfig);
+ if (!isAttachedToWindow()) {
+ mHadConfigurationChangeWhileDetached = true;
+ }
mOnConfigurationChangedListeners.forEach(
listener -> listener.onConfigurationChange(newConfig));
}
+ final boolean hadConfigurationChangeWhileDetached() {
+ return mHadConfigurationChangeWhileDetached;
+ }
+
+ @Override
+ protected void onDetachedFromWindow() {
+ super.onDetachedFromWindow();
+ mHadConfigurationChangeWhileDetached = false;
+ }
+
@Override
protected void onFinishInflate() {
super.onFinishInflate();
diff --git a/packages/SystemUI/src/com/android/systemui/qs/QSPanelControllerBase.java b/packages/SystemUI/src/com/android/systemui/qs/QSPanelControllerBase.java
index 6cf5b32e469b..85bcc25e140c 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/QSPanelControllerBase.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/QSPanelControllerBase.java
@@ -234,6 +234,12 @@ public abstract class QSPanelControllerBase<T extends QSPanel> extends ViewContr
mMediaHost.addVisibilityChangeListener(mMediaHostVisibilityListener);
}
mView.addOnConfigurationChangedListener(mOnConfigurationChangedListener);
+ // We were not attached and the configuration may have changed, trigger the listener.
+ if (mView.hadConfigurationChangeWhileDetached()) {
+ mOnConfigurationChangedListener.onConfigurationChange(
+ getContext().getResources().getConfiguration()
+ );
+ }
setTiles();
mLastOrientation = getResources().getConfiguration().orientation;
mLastScreenLayout = getResources().getConfiguration().screenLayout;
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/RecordIssueTile.kt b/packages/SystemUI/src/com/android/systemui/qs/tiles/RecordIssueTile.kt
index 1792ebd5245d..028ac6f4ac18 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/RecordIssueTile.kt
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/RecordIssueTile.kt
@@ -111,10 +111,12 @@ constructor(
override fun handleSetListening(listening: Boolean) {
super.handleSetListening(listening)
- if (listening) {
- issueRecordingState.addListener(onRecordingChangeListener)
- } else {
- issueRecordingState.removeListener(onRecordingChangeListener)
+ bgExecutor.execute {
+ if (listening) {
+ issueRecordingState.addListener(onRecordingChangeListener)
+ } else {
+ issueRecordingState.removeListener(onRecordingChangeListener)
+ }
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/irecording/IssueRecordingDataInteractor.kt b/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/irecording/IssueRecordingDataInteractor.kt
index 1af328e1e0a8..09a6ce8e5ec0 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/irecording/IssueRecordingDataInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/irecording/IssueRecordingDataInteractor.kt
@@ -41,7 +41,7 @@ constructor(
override fun tileData(
user: UserHandle,
- triggers: Flow<DataUpdateTrigger>
+ triggers: Flow<DataUpdateTrigger>,
): Flow<IssueRecordingModel> =
conflatedCallbackFlow {
val listener = Runnable { trySend(IssueRecordingModel(state.isRecording)) }
diff --git a/packages/SystemUI/src/com/android/systemui/recordissue/IssueRecordingServiceSession.kt b/packages/SystemUI/src/com/android/systemui/recordissue/IssueRecordingServiceSession.kt
index 43539335d8e4..16ca81a970e0 100644
--- a/packages/SystemUI/src/com/android/systemui/recordissue/IssueRecordingServiceSession.kt
+++ b/packages/SystemUI/src/com/android/systemui/recordissue/IssueRecordingServiceSession.kt
@@ -55,8 +55,10 @@ class IssueRecordingServiceSession(
var screenRecord = false
fun start() {
- bgExecutor.execute { traceurConnection.startTracing(traceConfig) }
- issueRecordingState.isRecording = true
+ bgExecutor.execute {
+ traceurConnection.startTracing(traceConfig)
+ issueRecordingState.isRecording = true
+ }
}
fun stop() {
@@ -69,8 +71,8 @@ class IssueRecordingServiceSession(
)
}
traceurConnection.stopTracing()
+ issueRecordingState.isRecording = false
}
- issueRecordingState.isRecording = false
}
fun share(notificationId: Int, screenRecording: Uri?) {
diff --git a/packages/SystemUI/src/com/android/systemui/recordissue/IssueRecordingState.kt b/packages/SystemUI/src/com/android/systemui/recordissue/IssueRecordingState.kt
index 51c437dbef24..ffdcf1b8811e 100644
--- a/packages/SystemUI/src/com/android/systemui/recordissue/IssueRecordingState.kt
+++ b/packages/SystemUI/src/com/android/systemui/recordissue/IssueRecordingState.kt
@@ -16,12 +16,20 @@
package com.android.systemui.recordissue
+import android.annotation.SuppressLint
+import android.content.ContentResolver
import android.content.Context
+import android.database.ContentObserver
+import android.os.Handler
+import androidx.annotation.VisibleForTesting
+import androidx.annotation.WorkerThread
import com.android.systemui.dagger.SysUISingleton
+import com.android.systemui.dagger.qualifiers.Background
import com.android.systemui.recordissue.RecordIssueModule.Companion.TILE_SPEC
import com.android.systemui.res.R
import com.android.systemui.settings.UserFileManager
import com.android.systemui.settings.UserTracker
+import com.android.systemui.util.settings.GlobalSettings
import com.android.traceur.PresetTraceConfigs
import com.android.traceur.TraceConfig
import java.util.concurrent.CopyOnWriteArrayList
@@ -31,12 +39,20 @@ import javax.inject.Inject
class IssueRecordingState
@Inject
constructor(
- userTracker: UserTracker,
- userFileManager: UserFileManager,
+ private val userTracker: UserTracker,
+ private val userFileManager: UserFileManager,
+ @Background bgHandler: Handler,
+ private val resolver: ContentResolver,
+ private val globalSettings: GlobalSettings,
) {
- private val prefs =
- userFileManager.getSharedPreferences(TILE_SPEC, Context.MODE_PRIVATE, userTracker.userId)
+ private val prefs
+ get() =
+ userFileManager.getSharedPreferences(
+ TILE_SPEC,
+ Context.MODE_PRIVATE,
+ userTracker.userId,
+ )
val customTraceState = CustomTraceState(prefs)
@@ -77,22 +93,52 @@ constructor(
private val listeners = CopyOnWriteArrayList<Runnable>()
+ @VisibleForTesting
+ val onRecordingChangeListener =
+ object : ContentObserver(bgHandler) {
+ override fun onChange(selfChange: Boolean) {
+ isRecording = globalSettings.getInt(KEY_ONGOING_ISSUE_RECORDING, 0) == 1
+ listeners.forEach(Runnable::run)
+ }
+ }
+
+ /**
+ * isRecording is purposely always set to false at the initialization of the record issue qs
+ * tile. We want to avoid a situation where the System UI crashed / the device was restarted in
+ * the middle of a trace session and the QS tile is in an active state even though no tracing is
+ * ongoing.
+ */
var isRecording = false
+ @WorkerThread
set(value) {
+ globalSettings.putInt(KEY_ONGOING_ISSUE_RECORDING, if (value) 1 else 0)
field = value
- listeners.forEach(Runnable::run)
}
fun markUserApprovalForScreenRecording() {
hasUserApprovedScreenRecording = true
}
+ @WorkerThread
+ @SuppressLint("RegisterContentObserverViaContentResolver")
fun addListener(listener: Runnable) {
+ if (listeners.isEmpty()) {
+ resolver.registerContentObserver(
+ globalSettings.getUriFor(KEY_ONGOING_ISSUE_RECORDING),
+ false,
+ onRecordingChangeListener,
+ )
+ }
listeners.add(listener)
}
+ @WorkerThread
+ @SuppressLint("RegisterContentObserverViaContentResolver")
fun removeListener(listener: Runnable) {
listeners.remove(listener)
+ if (listeners.isEmpty()) {
+ resolver.unregisterContentObserver(onRecordingChangeListener)
+ }
}
companion object {
@@ -100,6 +146,7 @@ constructor(
private const val HAS_APPROVED_SCREEN_RECORDING = "HasApprovedScreenRecord"
private const val KEY_RECORD_SCREEN = "key_recordScreen"
private const val KEY_TAG_TITLES = "key_tagTitles"
+ private const val KEY_ONGOING_ISSUE_RECORDING = "issueRecordingOngoing"
const val KEY_ISSUE_TYPE_INDEX = "key_issueTypeIndex"
const val ISSUE_TYPE_NOT_SET = -1
const val TAG_TITLE_DELIMITER = ": "
diff --git a/packages/SystemUI/src/com/android/systemui/scene/domain/startable/ScrimStartable.kt b/packages/SystemUI/src/com/android/systemui/scene/domain/startable/ScrimStartable.kt
index 55033f09e263..1d970349b955 100644
--- a/packages/SystemUI/src/com/android/systemui/scene/domain/startable/ScrimStartable.kt
+++ b/packages/SystemUI/src/com/android/systemui/scene/domain/startable/ScrimStartable.kt
@@ -185,6 +185,7 @@ constructor(
} else if (isOnKeyguard && !unlocking && isDreaming) {
Model(scrimState = ScrimState.DREAMING, unlocking = false)
} else {
+ onKeyguardFadedAway(transitionState.isIdle(Scenes.Gone))
Model(scrimState = ScrimState.UNLOCKED, unlocking = unlocking)
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/shade/LargeScreenHeaderHelper.kt b/packages/SystemUI/src/com/android/systemui/shade/LargeScreenHeaderHelper.kt
index c74f038ebea4..38de17eb7fd8 100644
--- a/packages/SystemUI/src/com/android/systemui/shade/LargeScreenHeaderHelper.kt
+++ b/packages/SystemUI/src/com/android/systemui/shade/LargeScreenHeaderHelper.kt
@@ -22,7 +22,7 @@ import com.android.systemui.res.R
import javax.inject.Inject
import kotlin.math.max
-class LargeScreenHeaderHelper @Inject constructor(private val context: Context) {
+class LargeScreenHeaderHelper @Inject constructor(@ShadeDisplayAware private val context: Context) {
fun getLargeScreenHeaderHeight(): Int = getLargeScreenHeaderHeight(context)
diff --git a/packages/SystemUI/src/com/android/systemui/shade/NotificationPanelUnfoldAnimationController.kt b/packages/SystemUI/src/com/android/systemui/shade/NotificationPanelUnfoldAnimationController.kt
index f463cb5bcbd6..6e63446d88d8 100644
--- a/packages/SystemUI/src/com/android/systemui/shade/NotificationPanelUnfoldAnimationController.kt
+++ b/packages/SystemUI/src/com/android/systemui/shade/NotificationPanelUnfoldAnimationController.kt
@@ -34,7 +34,7 @@ import javax.inject.Inject
class NotificationPanelUnfoldAnimationController
@Inject
constructor(
- private val context: Context,
+ @ShadeDisplayAware private val context: Context,
statusBarStateController: StatusBarStateController,
progressProvider: NaturalRotationUnfoldProgressProvider,
) {
diff --git a/packages/SystemUI/src/com/android/systemui/shade/NotificationPanelViewController.java b/packages/SystemUI/src/com/android/systemui/shade/NotificationPanelViewController.java
index 67d162b4aa1b..083cf1fc8b17 100644
--- a/packages/SystemUI/src/com/android/systemui/shade/NotificationPanelViewController.java
+++ b/packages/SystemUI/src/com/android/systemui/shade/NotificationPanelViewController.java
@@ -668,7 +668,7 @@ public final class NotificationPanelViewController implements ShadeSurface, Dump
@Inject
public NotificationPanelViewController(NotificationPanelView view,
@Main Handler handler,
- LayoutInflater layoutInflater,
+ @ShadeDisplayAware LayoutInflater layoutInflater,
FeatureFlags featureFlags,
NotificationWakeUpCoordinator coordinator,
PulseExpansionHandler pulseExpansionHandler,
diff --git a/packages/SystemUI/src/com/android/systemui/shade/NotificationShadeWindowControllerImpl.java b/packages/SystemUI/src/com/android/systemui/shade/NotificationShadeWindowControllerImpl.java
index 5b6696bb60df..24dba59a1d54 100644
--- a/packages/SystemUI/src/com/android/systemui/shade/NotificationShadeWindowControllerImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/shade/NotificationShadeWindowControllerImpl.java
@@ -148,7 +148,7 @@ public class NotificationShadeWindowControllerImpl implements NotificationShadeW
@Inject
public NotificationShadeWindowControllerImpl(
- Context context,
+ @ShadeDisplayAware Context context,
WindowRootViewComponent.Factory windowRootViewComponentFactory,
ViewCaptureAwareWindowManager viewCaptureAwareWindowManager,
IActivityManager activityManager,
diff --git a/packages/SystemUI/src/com/android/systemui/shade/QsBatteryModeController.kt b/packages/SystemUI/src/com/android/systemui/shade/QsBatteryModeController.kt
index 91627d63980e..7a70966c2b12 100644
--- a/packages/SystemUI/src/com/android/systemui/shade/QsBatteryModeController.kt
+++ b/packages/SystemUI/src/com/android/systemui/shade/QsBatteryModeController.kt
@@ -14,7 +14,7 @@ import javax.inject.Inject
class QsBatteryModeController
@Inject
constructor(
- private val context: Context,
+ @ShadeDisplayAware private val context: Context,
insetsProviderStore: StatusBarContentInsetsProviderStore,
) {
diff --git a/packages/SystemUI/src/com/android/systemui/shade/ShadeViewProviderModule.kt b/packages/SystemUI/src/com/android/systemui/shade/ShadeViewProviderModule.kt
index 1918094cceee..15b22700072f 100644
--- a/packages/SystemUI/src/com/android/systemui/shade/ShadeViewProviderModule.kt
+++ b/packages/SystemUI/src/com/android/systemui/shade/ShadeViewProviderModule.kt
@@ -83,7 +83,7 @@ abstract class ShadeViewProviderModule {
@Provides
@SysUISingleton
fun providesWindowRootView(
- layoutInflater: LayoutInflater,
+ @ShadeDisplayAware layoutInflater: LayoutInflater,
viewModelFactory: SceneContainerViewModel.Factory,
containerConfigProvider: Provider<SceneContainerConfig>,
scenesProvider: Provider<Set<@JvmSuppressWildcards Scene>>,
@@ -156,7 +156,7 @@ abstract class ShadeViewProviderModule {
@Provides
fun providesKeyguardBottomAreaView(
npv: NotificationPanelView,
- layoutInflater: LayoutInflater,
+ @ShadeDisplayAware layoutInflater: LayoutInflater,
): KeyguardBottomAreaView {
return layoutInflater.inflate(R.layout.keyguard_bottom_area, npv, false)
as KeyguardBottomAreaView
diff --git a/packages/SystemUI/src/com/android/systemui/shade/carrier/ShadeCarrierGroupController.java b/packages/SystemUI/src/com/android/systemui/shade/carrier/ShadeCarrierGroupController.java
index a171d33ddb47..4b8cc00e1c28 100644
--- a/packages/SystemUI/src/com/android/systemui/shade/carrier/ShadeCarrierGroupController.java
+++ b/packages/SystemUI/src/com/android/systemui/shade/carrier/ShadeCarrierGroupController.java
@@ -44,6 +44,7 @@ import com.android.systemui.dagger.qualifiers.Background;
import com.android.systemui.dagger.qualifiers.Main;
import com.android.systemui.plugins.ActivityStarter;
import com.android.systemui.res.R;
+import com.android.systemui.shade.ShadeDisplayAware;
import com.android.systemui.statusbar.connectivity.MobileDataIndicators;
import com.android.systemui.statusbar.connectivity.NetworkController;
import com.android.systemui.statusbar.connectivity.SignalCallback;
@@ -153,7 +154,7 @@ public class ShadeCarrierGroupController {
ShadeCarrierGroupControllerLogger logger,
NetworkController networkController,
CarrierTextManager.Builder carrierTextManagerBuilder,
- Context context,
+ @ShadeDisplayAware Context context,
CarrierConfigTracker carrierConfigTracker,
SlotIndexResolver slotIndexResolver,
MobileUiAdapter mobileUiAdapter,
@@ -497,7 +498,7 @@ public class ShadeCarrierGroupController {
ShadeCarrierGroupControllerLogger logger,
NetworkController networkController,
CarrierTextManager.Builder carrierTextControllerBuilder,
- Context context,
+ @ShadeDisplayAware Context context,
CarrierConfigTracker carrierConfigTracker,
SlotIndexResolver slotIndexResolver,
MobileUiAdapter mobileUiAdapter,
diff --git a/packages/SystemUI/src/com/android/systemui/shade/data/repository/ShadeRepository.kt b/packages/SystemUI/src/com/android/systemui/shade/data/repository/ShadeRepository.kt
index 193056c19d4e..5629938deeb0 100644
--- a/packages/SystemUI/src/com/android/systemui/shade/data/repository/ShadeRepository.kt
+++ b/packages/SystemUI/src/com/android/systemui/shade/data/repository/ShadeRepository.kt
@@ -182,7 +182,7 @@ interface ShadeRepository {
/** Business logic for shade interactions */
@SysUISingleton
-class ShadeRepositoryImpl @Inject constructor(@Application applicationContext: Context) :
+class ShadeRepositoryImpl @Inject constructor() :
ShadeRepository {
private val _qsExpansion = MutableStateFlow(0f)
@Deprecated("Use ShadeInteractor.qsExpansion instead")
diff --git a/packages/SystemUI/src/com/android/systemui/shade/domain/startable/ShadeStartable.kt b/packages/SystemUI/src/com/android/systemui/shade/domain/startable/ShadeStartable.kt
index 6c44c7315fb9..e5d08a0ac977 100644
--- a/packages/SystemUI/src/com/android/systemui/shade/domain/startable/ShadeStartable.kt
+++ b/packages/SystemUI/src/com/android/systemui/shade/domain/startable/ShadeStartable.kt
@@ -26,6 +26,7 @@ import com.android.systemui.log.LogBuffer
import com.android.systemui.log.dagger.ShadeTouchLog
import com.android.systemui.scene.domain.interactor.SceneInteractor
import com.android.systemui.scene.shared.flag.SceneContainerFlag
+import com.android.systemui.shade.ShadeDisplayAware
import com.android.systemui.shade.ShadeExpansionStateManager
import com.android.systemui.shade.TouchLogger.Companion.logTouchesTo
import com.android.systemui.shade.data.repository.ShadeRepository
@@ -48,7 +49,7 @@ class ShadeStartable
@Inject
constructor(
@Application private val applicationScope: CoroutineScope,
- @Application private val applicationContext: Context,
+ @ShadeDisplayAware private val context: Context,
@ShadeTouchLog private val touchLog: LogBuffer,
private val configurationRepository: ConfigurationRepository,
private val shadeRepository: ShadeRepository,
@@ -94,7 +95,7 @@ constructor(
// Force initial collection.
.onStart { emit(Unit) }
.collect {
- val resources = applicationContext.resources
+ val resources = context.resources
// The configuration for 'shouldUseSplitNotificationShade' dictates the width of
// the shade in both split-shade and dual-shade modes.
shadeRepository.setShadeLayoutWide(
diff --git a/packages/SystemUI/src/com/android/systemui/shade/transition/LargeScreenShadeInterpolatorImpl.kt b/packages/SystemUI/src/com/android/systemui/shade/transition/LargeScreenShadeInterpolatorImpl.kt
index 823742af49d2..a5caa0963bc0 100644
--- a/packages/SystemUI/src/com/android/systemui/shade/transition/LargeScreenShadeInterpolatorImpl.kt
+++ b/packages/SystemUI/src/com/android/systemui/shade/transition/LargeScreenShadeInterpolatorImpl.kt
@@ -30,7 +30,7 @@ internal class LargeScreenShadeInterpolatorImpl
@Inject
internal constructor(
@ShadeDisplayAware configurationController: ConfigurationController,
- private val context: Context,
+ @ShadeDisplayAware private val context: Context,
private val splitShadeInterpolator: SplitShadeInterpolator,
private val portraitShadeInterpolator: LargeScreenPortraitShadeInterpolator,
private val splitShadeStateController: SplitShadeStateController,
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 5be5ccce7c98..45516aa69cd7 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
@@ -30,6 +30,7 @@ import com.android.systemui.privacy.OngoingPrivacyChip
import com.android.systemui.privacy.PrivacyItem
import com.android.systemui.res.R
import com.android.systemui.scene.shared.model.TransitionKeys.SlightlyFasterShadeCollapse
+import com.android.systemui.shade.ShadeDisplayAware
import com.android.systemui.shade.domain.interactor.PrivacyChipInteractor
import com.android.systemui.shade.domain.interactor.ShadeHeaderClockInteractor
import com.android.systemui.shade.domain.interactor.ShadeInteractor
@@ -53,7 +54,7 @@ import com.android.app.tracing.coroutines.launchTraced as launch
class ShadeHeaderViewModel
@AssistedInject
constructor(
- context: Context,
+ @ShadeDisplayAware context: Context,
private val activityStarter: ActivityStarter,
private val shadeInteractor: ShadeInteractor,
private val mobileIconsInteractor: MobileIconsInteractor,
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/ConversationNotifications.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/ConversationNotifications.kt
index 682a9fff2994..77ec65bfad6f 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/ConversationNotifications.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/ConversationNotifications.kt
@@ -29,6 +29,7 @@ import com.android.internal.widget.MessagingLayout
import com.android.systemui.dagger.SysUISingleton
import com.android.systemui.dagger.qualifiers.Main
import com.android.systemui.plugins.statusbar.StatusBarStateController
+import com.android.systemui.shade.ShadeDisplayAware
import com.android.systemui.statusbar.notification.collection.NotificationEntry
import com.android.systemui.statusbar.notification.collection.inflation.BindEventManager
import com.android.systemui.statusbar.notification.collection.notifcollection.CommonNotifCollection
@@ -141,7 +142,7 @@ class ConversationNotificationManager
@Inject
constructor(
bindEventManager: BindEventManager,
- private val context: Context,
+ @ShadeDisplayAware private val context: Context,
private val notifCollection: CommonNotifCollection,
@Main private val mainHandler: Handler
) {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/HeadsUpManagerPhone.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/HeadsUpManagerPhone.java
index fb42bad7033d..0299ebcdf043 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/HeadsUpManagerPhone.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/HeadsUpManagerPhone.java
@@ -426,7 +426,7 @@ public class HeadsUpManagerPhone extends BaseHeadsUpManager implements
}
mAnimationStateHandler.setHeadsUpGoingAwayAnimationsAllowed(false);
for (NotificationEntry entry : mEntriesToRemoveWhenReorderingAllowed) {
- if (isHeadsUpEntry(entry.getKey())) {
+ if (entry != null && isHeadsUpEntry(entry.getKey())) {
// Maybe the heads-up was removed already
removeEntry(entry.getKey(), "mOnReorderingAllowedListener");
}
@@ -488,6 +488,13 @@ public class HeadsUpManagerPhone extends BaseHeadsUpManager implements
}
updateTopHeadsUpFlow();
updateHeadsUpFlow();
+ if (NotificationThrottleHun.isEnabled()) {
+ if (headsUpEntry.mEntry != null) {
+ if (mEntriesToRemoveWhenReorderingAllowed.contains(headsUpEntry.mEntry)) {
+ mEntriesToRemoveWhenReorderingAllowed.remove(headsUpEntry.mEntry);
+ }
+ }
+ }
}
private void updateTopHeadsUpFlow() {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/EntryWithDismissStats.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/EntryWithDismissStats.kt
new file mode 100644
index 000000000000..48a8c01e7c47
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/EntryWithDismissStats.kt
@@ -0,0 +1,46 @@
+/*
+ * 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.
+ */
+
+package com.android.systemui.statusbar.notification.collection
+
+import com.android.internal.statusbar.NotificationVisibility
+import com.android.systemui.statusbar.notification.collection.notifcollection.DismissedByUserStats
+
+/**
+ * A holder class for a [NotificationEntry] and an associated [DismissedByUserStats], used by
+ * [NotifCollection] for handling dismissal.
+ */
+data class EntryWithDismissStats(val entry: NotificationEntry, val stats: DismissedByUserStats) {
+ /**
+ * Creates deep a copy of this object, but with the entry, key and rank updated to correspond to
+ * the given entry.
+ */
+ fun copyForEntry(newEntry: NotificationEntry) =
+ EntryWithDismissStats(
+ entry = newEntry,
+ stats =
+ DismissedByUserStats(
+ stats.dismissalSurface,
+ stats.dismissalSentiment,
+ NotificationVisibility.obtain(
+ newEntry.key,
+ newEntry.ranking.rank,
+ stats.notificationVisibility.count,
+ /* visible= */ false,
+ ),
+ ),
+ )
+}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/NotifCollection.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/NotifCollection.java
index 7b3a93a4a094..cf9ee619b734 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/NotifCollection.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/NotifCollection.java
@@ -39,6 +39,7 @@ import static android.service.notification.NotificationListenerService.REASON_TI
import static android.service.notification.NotificationListenerService.REASON_UNAUTOBUNDLED;
import static android.service.notification.NotificationListenerService.REASON_USER_STOPPED;
+import static com.android.systemui.Flags.notificationsDismissPrunedSummaries;
import static com.android.systemui.statusbar.notification.NotificationUtils.logKey;
import static com.android.systemui.statusbar.notification.collection.NotificationEntry.DismissState.DISMISSED;
import static com.android.systemui.statusbar.notification.collection.NotificationEntry.DismissState.NOT_DISMISSED;
@@ -62,7 +63,6 @@ import android.service.notification.NotificationListenerService.RankingMap;
import android.service.notification.StatusBarNotification;
import android.util.ArrayMap;
import android.util.Log;
-import android.util.Pair;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
@@ -111,6 +111,7 @@ import java.util.Collection;
import java.util.Collections;
import java.util.Comparator;
import java.util.HashMap;
+import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Objects;
@@ -273,15 +274,19 @@ public class NotifCollection implements Dumpable, PipelineDumpable {
* Dismisses multiple notifications on behalf of the user.
*/
public void dismissNotifications(
- List<Pair<NotificationEntry, DismissedByUserStats>> entriesToDismiss) {
+ List<EntryWithDismissStats> entriesToDismiss) {
Assert.isMainThread();
checkForReentrantCall();
+ if (notificationsDismissPrunedSummaries()) {
+ entriesToDismiss = includeSummariesToDismiss(entriesToDismiss);
+ }
+
final int entryCount = entriesToDismiss.size();
final List<NotificationEntry> entriesToLocallyDismiss = new ArrayList<>();
for (int i = 0; i < entriesToDismiss.size(); i++) {
- NotificationEntry entry = entriesToDismiss.get(i).first;
- DismissedByUserStats stats = entriesToDismiss.get(i).second;
+ NotificationEntry entry = entriesToDismiss.get(i).getEntry();
+ DismissedByUserStats stats = entriesToDismiss.get(i).getStats();
requireNonNull(stats);
NotificationEntry storedEntry = mNotificationSet.get(entry.getKey());
@@ -336,13 +341,32 @@ public class NotifCollection implements Dumpable, PipelineDumpable {
dispatchEventsAndRebuildList("dismissNotifications");
}
+ private List<EntryWithDismissStats> includeSummariesToDismiss(
+ List<EntryWithDismissStats> entriesToDismiss) {
+ final HashSet<NotificationEntry> entriesSet = new HashSet<>(entriesToDismiss.size());
+ for (EntryWithDismissStats entryToStats : entriesToDismiss) {
+ entriesSet.add(entryToStats.getEntry());
+ }
+
+ final List<EntryWithDismissStats> entriesPlusSummaries =
+ new ArrayList<>(entriesToDismiss.size() + 1);
+ for (EntryWithDismissStats entryToStats : entriesToDismiss) {
+ entriesPlusSummaries.add(entryToStats);
+ NotificationEntry summary = fetchSummaryToDismiss(entryToStats.getEntry());
+ if (summary != null && !entriesSet.contains(summary)) {
+ entriesPlusSummaries.add(entryToStats.copyForEntry(summary));
+ }
+ }
+ return entriesPlusSummaries;
+ }
+
/**
* Dismisses a single notification on behalf of the user.
*/
public void dismissNotification(
NotificationEntry entry,
@NonNull DismissedByUserStats stats) {
- dismissNotifications(List.of(new Pair<>(entry, stats)));
+ dismissNotifications(List.of(new EntryWithDismissStats(entry, stats)));
}
/**
@@ -1062,6 +1086,16 @@ public class NotifCollection implements Dumpable, PipelineDumpable {
}
}
+ @Nullable
+ private NotificationEntry fetchSummaryToDismiss(NotificationEntry entry) {
+ if (isOnlyChildInGroup(entry)) {
+ String group = entry.getSbn().getGroupKey();
+ NotificationEntry summary = getGroupSummary(group);
+ if (summary != null && isDismissable(summary)) return summary;
+ }
+ return null;
+ }
+
/** A single method interface that callers can pass in when registering future dismissals */
public interface DismissedByUserStatsCreator {
DismissedByUserStats createDismissedByUserStats(NotificationEntry entry);
@@ -1092,16 +1126,6 @@ public class NotifCollection implements Dumpable, PipelineDumpable {
+ ">";
}
- @Nullable
- private NotificationEntry fetchSummaryToDismiss(NotificationEntry entry) {
- if (isOnlyChildInGroup(entry)) {
- String group = entry.getSbn().getGroupKey();
- NotificationEntry summary = getGroupSummary(group);
- if (summary != null && isDismissable(summary)) return summary;
- }
- return null;
- }
-
/** called when the entry has been removed from the collection */
public void onSystemServerCancel(@CancellationReason int cancellationReason) {
Assert.isMainThread();
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 df694bb67684..de868d45e64f 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
@@ -18,6 +18,7 @@ package com.android.systemui.statusbar.notification.collection.coordinator
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.NotifPipeline
@@ -33,7 +34,7 @@ import javax.inject.Inject
*/
@CoordinatorScope
class RowAppearanceCoordinator @Inject internal constructor(
- context: Context,
+ @ShadeDisplayAware context: Context,
private var mAssistantFeedbackController: AssistantFeedbackController,
private var mSectionStyleProvider: SectionStyleProvider
) : Coordinator {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/ViewConfigCoordinator.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/ViewConfigCoordinator.kt
index b8a959440312..db778b801dbc 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/ViewConfigCoordinator.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/ViewConfigCoordinator.kt
@@ -30,6 +30,7 @@ import com.android.systemui.statusbar.notification.row.NotificationGutsManager
import com.android.systemui.statusbar.policy.ConfigurationController
import com.android.systemui.util.Compile
import com.android.app.tracing.traceSection
+import com.android.systemui.shade.ShadeDisplayAware
import javax.inject.Inject
/**
@@ -39,7 +40,7 @@ import javax.inject.Inject
*/
@CoordinatorScope
class ViewConfigCoordinator @Inject internal constructor(
- private val mConfigurationController: ConfigurationController,
+ @ShadeDisplayAware private val mConfigurationController: ConfigurationController,
private val mLockscreenUserManager: NotificationLockscreenUserManager,
private val mGutsManager: NotificationGutsManager,
private val mKeyguardUpdateMonitor: KeyguardUpdateMonitor,
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/inflation/NotificationRowBinderImpl.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/inflation/NotificationRowBinderImpl.java
index 9d5d7a19916a..e6d22b07f3ab 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/inflation/NotificationRowBinderImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/inflation/NotificationRowBinderImpl.java
@@ -37,6 +37,7 @@ import com.android.internal.util.NotificationMessagingUtil;
import com.android.systemui.dagger.SysUISingleton;
import com.android.systemui.flags.FeatureFlags;
import com.android.systemui.flags.Flags;
+import com.android.systemui.shade.ShadeDisplayAware;
import com.android.systemui.statusbar.NotificationLockscreenUserManager;
import com.android.systemui.statusbar.NotificationPresenter;
import com.android.systemui.statusbar.NotificationRemoteInputManager;
@@ -86,7 +87,7 @@ public class NotificationRowBinderImpl implements NotificationRowBinder {
@Inject
public NotificationRowBinderImpl(
- Context context,
+ @ShadeDisplayAware Context context,
NotificationMessagingUtil notificationMessagingUtil,
NotificationRemoteInputManager notificationRemoteInputManager,
NotificationLockscreenUserManager notificationLockscreenUserManager,
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 193586679d23..cab4c1c88b2c 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
@@ -27,6 +27,7 @@ import com.android.systemui.statusbar.notification.collection.PipelineDumper
import com.android.systemui.statusbar.notification.collection.provider.SectionHeaderVisibilityProvider
import com.android.systemui.statusbar.notification.stack.NotificationListContainer
import com.android.app.tracing.traceSection
+import com.android.systemui.shade.ShadeDisplayAware
import dagger.assisted.Assisted
import dagger.assisted.AssistedFactory
import dagger.assisted.AssistedInject
@@ -36,7 +37,7 @@ import dagger.assisted.AssistedInject
* currently populate the notification shade.
*/
class ShadeViewManager @AssistedInject constructor(
- context: Context,
+ @ShadeDisplayAware context: Context,
@Assisted listContainer: NotificationListContainer,
@Assisted private val stackController: NotifStackController,
mediaContainerController: MediaContainerController,
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/footer/ui/view/FooterView.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/footer/ui/view/FooterView.java
index 5a616dfd1f63..e5ce25d5144e 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/footer/ui/view/FooterView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/footer/ui/view/FooterView.java
@@ -40,6 +40,7 @@ import android.widget.TextView;
import androidx.annotation.NonNull;
import com.android.settingslib.Utils;
+import com.android.systemui.Flags;
import com.android.systemui.res.R;
import com.android.systemui.statusbar.notification.ColorUpdateLogger;
import com.android.systemui.statusbar.notification.footer.shared.FooterViewRefactor;
@@ -59,6 +60,9 @@ public class FooterView extends StackScrollerDecorView {
private FooterViewButton mClearAllButton;
private FooterViewButton mManageOrHistoryButton;
+ // The settings & history buttons replace the single manage/history button in the redesign
+ private FooterViewButton mSettingsButton;
+ private FooterViewButton mHistoryButton;
private boolean mShouldBeHidden;
private boolean mShowHistory;
// String cache, for performance reasons.
@@ -269,7 +273,12 @@ public class FooterView extends StackScrollerDecorView {
}
super.onFinishInflate();
mClearAllButton = (FooterViewButton) findSecondaryView();
- mManageOrHistoryButton = findViewById(R.id.manage_text);
+ if (Flags.notificationsRedesignFooterView()) {
+ mSettingsButton = findViewById(R.id.settings_button);
+ mHistoryButton = findViewById(R.id.history_button);
+ } else {
+ mManageOrHistoryButton = findViewById(R.id.manage_text);
+ }
mSeenNotifsFooterTextView = findViewById(R.id.unlock_prompt_footer);
if (!FooterViewRefactor.isEnabled()) {
updateResources();
@@ -342,8 +351,10 @@ public class FooterView extends StackScrollerDecorView {
updateClearAllButtonText();
updateClearAllButtonDescription();
- updateManageOrHistoryButtonText();
- updateManageOrHistoryButtonDescription();
+ if (!Flags.notificationsRedesignFooterView()) {
+ updateManageOrHistoryButtonText();
+ updateManageOrHistoryButtonDescription();
+ }
updateMessageString();
updateMessageIcon();
@@ -420,8 +431,16 @@ public class FooterView extends StackScrollerDecorView {
}
mClearAllButton.setBackground(clearAllBg);
mClearAllButton.setTextColor(onSurface);
- mManageOrHistoryButton.setBackground(manageBg);
- mManageOrHistoryButton.setTextColor(onSurface);
+ if (Flags.notificationsRedesignFooterView()) {
+ mSettingsButton.setBackground(manageBg);
+ mSettingsButton.setCompoundDrawableTintList(ColorStateList.valueOf(onSurface));
+
+ mHistoryButton.setBackground(manageBg);
+ mHistoryButton.setCompoundDrawableTintList(ColorStateList.valueOf(onSurface));
+ } else {
+ mManageOrHistoryButton.setBackground(manageBg);
+ mManageOrHistoryButton.setTextColor(onSurface);
+ }
mSeenNotifsFooterTextView.setTextColor(onSurface);
mSeenNotifsFooterTextView.setCompoundDrawableTintList(ColorStateList.valueOf(onSurface));
ColorUpdateLogger colorUpdateLogger = ColorUpdateLogger.getInstance();
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/footer/ui/viewbinder/FooterViewBinder.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/footer/ui/viewbinder/FooterViewBinder.kt
index 2ec7f532d8c3..ddd9cdd5e306 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/footer/ui/viewbinder/FooterViewBinder.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/footer/ui/viewbinder/FooterViewBinder.kt
@@ -18,6 +18,7 @@ package com.android.systemui.statusbar.notification.footer.ui.viewbinder
import android.view.View
import androidx.lifecycle.lifecycleScope
+import com.android.systemui.Flags
import com.android.systemui.lifecycle.repeatWhenAttached
import com.android.systemui.statusbar.notification.NotificationActivityStarter
import com.android.systemui.statusbar.notification.emptyshade.shared.ModesEmptyShadeFix
@@ -63,14 +64,16 @@ object FooterViewBinder {
notificationActivityStarter: NotificationActivityStarter,
) = coroutineScope {
launch { bindClearAllButton(footer, viewModel, clearAllNotifications) }
- launch {
- bindManageOrHistoryButton(
- footer,
- viewModel,
- launchNotificationSettings,
- launchNotificationHistory,
- notificationActivityStarter,
- )
+ if (!Flags.notificationsRedesignFooterView()) {
+ launch {
+ bindManageOrHistoryButton(
+ footer,
+ viewModel,
+ launchNotificationSettings,
+ launchNotificationHistory,
+ notificationActivityStarter,
+ )
+ }
}
launch { bindMessage(footer, viewModel) }
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/interruption/VisualInterruptionDecisionProviderImpl.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/interruption/VisualInterruptionDecisionProviderImpl.kt
index d4466f8771a6..71cddc99b564 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/interruption/VisualInterruptionDecisionProviderImpl.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/interruption/VisualInterruptionDecisionProviderImpl.kt
@@ -29,6 +29,7 @@ import com.android.internal.logging.UiEventLogger.UiEventEnum
import com.android.systemui.dagger.qualifiers.Main
import com.android.systemui.plugins.statusbar.StatusBarStateController
import com.android.systemui.settings.UserTracker
+import com.android.systemui.shade.ShadeDisplayAware
import com.android.systemui.shared.notifications.domain.interactor.NotificationSettingsInteractor
import com.android.systemui.statusbar.notification.collection.NotificationEntry
import com.android.systemui.statusbar.notification.interruption.VisualInterruptionDecisionProvider.Decision
@@ -72,7 +73,7 @@ constructor(
private val systemSettings: SystemSettings,
private val packageManager: PackageManager,
private val bubbles: Optional<Bubbles>,
- private val context: Context,
+ @ShadeDisplayAware private val context: Context,
private val notificationManager: NotificationManager,
private val settingsInteractor: NotificationSettingsInteractor
) : VisualInterruptionDecisionProvider {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/BigPictureIconManager.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/BigPictureIconManager.kt
index 068f23d4284c..e233deffe42f 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/BigPictureIconManager.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/BigPictureIconManager.kt
@@ -32,6 +32,7 @@ import com.android.systemui.dagger.qualifiers.Application
import com.android.systemui.dagger.qualifiers.Background
import com.android.systemui.dagger.qualifiers.Main
import com.android.systemui.graphics.ImageLoader
+import com.android.systemui.shade.ShadeDisplayAware
import com.android.systemui.statusbar.notification.row.BigPictureIconManager.DrawableState.Empty
import com.android.systemui.statusbar.notification.row.BigPictureIconManager.DrawableState.FullImage
import com.android.systemui.statusbar.notification.row.BigPictureIconManager.DrawableState.Initial
@@ -61,7 +62,7 @@ private const val FREE_IMAGE_DELAY_MS = 3000L
class BigPictureIconManager
@Inject
constructor(
- private val context: Context,
+ @ShadeDisplayAware private val context: Context,
private val imageLoader: ImageLoader,
private val statsManager: BigPictureStatsManager,
@Application private val scope: CoroutineScope,
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/icon/AppIconProvider.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/icon/AppIconProvider.kt
index 172b76cbcca9..98d704c75d33 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/icon/AppIconProvider.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/icon/AppIconProvider.kt
@@ -33,6 +33,7 @@ import com.android.launcher3.util.UserIconInfo
import com.android.systemui.Dumpable
import com.android.systemui.dagger.SysUISingleton
import com.android.systemui.dump.DumpManager
+import com.android.systemui.shade.ShadeDisplayAware
import com.android.systemui.statusbar.notification.collection.NotifCollectionCache
import com.android.systemui.util.asIndenting
import com.android.systemui.util.withIncreasedIndent
@@ -67,7 +68,7 @@ interface AppIconProvider {
@SysUISingleton
class AppIconProviderImpl
@Inject
-constructor(private val sysuiContext: Context, dumpManager: DumpManager) :
+constructor(@ShadeDisplayAware private val sysuiContext: Context, dumpManager: DumpManager) :
AppIconProvider, Dumpable {
init {
dumpManager.registerNormalDumpable(TAG, this)
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/AmbientState.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/AmbientState.java
index 129d4cee9cdb..777142194221 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/AmbientState.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/AmbientState.java
@@ -596,10 +596,12 @@ public class AmbientState implements Dumpable {
}
public void setContentHeight(int contentHeight) {
+ SceneContainerFlag.assertInLegacyMode();
mContentHeight = contentHeight;
}
public float getContentHeight() {
+ SceneContainerFlag.assertInLegacyMode();
return mContentHeight;
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationSectionsManager.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationSectionsManager.kt
index 7441c7058d7b..c9a0010c0de7 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationSectionsManager.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationSectionsManager.kt
@@ -20,6 +20,7 @@ import android.util.Log
import android.view.View
import com.android.internal.annotations.VisibleForTesting
import com.android.systemui.media.controls.ui.controller.KeyguardMediaController
+import com.android.systemui.shade.ShadeDisplayAware
import com.android.systemui.statusbar.notification.NotificationSectionsFeatureManager
import com.android.systemui.statusbar.notification.SourceType
import com.android.systemui.statusbar.notification.collection.NotificationClassificationFlag
@@ -41,7 +42,7 @@ import javax.inject.Inject
class NotificationSectionsManager
@Inject
internal constructor(
- private val configurationController: ConfigurationController,
+ @ShadeDisplayAware private val configurationController: ConfigurationController,
private val keyguardMediaController: KeyguardMediaController,
private val sectionsFeatureManager: NotificationSectionsFeatureManager,
private val mediaContainerController: MediaContainerController,
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 87b16efc64fc..dde83b9bd7a7 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
@@ -615,13 +615,13 @@ public class NotificationStackScrollLayout
if (SceneContainerFlag.isEnabled()) {
return mScrollViewFields.getScrollState().isScrolledToTop();
} else {
- return mOwnScrollY == 0;
+ return getOwnScrollY() == 0;
}
}
@Override
public boolean isScrolledToBottom() {
- return mOwnScrollY >= getScrollRange();
+ return getOwnScrollY() >= getScrollRange();
}
@Override
@@ -900,11 +900,11 @@ public class NotificationStackScrollLayout
drawDebugInfo(canvas, y, Color.LTGRAY,
/* label= */ "mAmbientState.getStackY() + mAmbientState.getStackHeight() = " + y);
- y = (int) (mAmbientState.getStackY() + mIntrinsicContentHeight);
+ y = (int) (mAmbientState.getStackY() + getIntrinsicContentHeight());
drawDebugInfo(canvas, y, Color.YELLOW,
/* label= */ "mAmbientState.getStackY() + mIntrinsicContentHeight = " + y);
- y = mContentHeight;
+ y = getContentHeight();
drawDebugInfo(canvas, y, Color.MAGENTA,
/* label= */ "mContentHeight = " + y);
@@ -1200,7 +1200,6 @@ public class NotificationStackScrollLayout
if (!SceneContainerFlag.isEnabled()) {
setMaxLayoutHeight(getHeight());
updateContentHeight();
- mWallpaperInteractor.setNotificationStackAbsoluteBottom(mContentHeight);
}
clampScrollPosition();
requestChildrenUpdate();
@@ -1278,7 +1277,6 @@ public class NotificationStackScrollLayout
if (mAmbientState.getStackTop() != stackTop) {
mAmbientState.setStackTop(stackTop);
onTopPaddingChanged(/* animate = */ isAddOrRemoveAnimationPending());
- mWallpaperInteractor.setNotificationStackAbsoluteBottom((int) stackTop);
}
}
@@ -1376,7 +1374,7 @@ public class NotificationStackScrollLayout
/**
* Updates the children views according to the stack scroll algorithm. Call this whenever
- * modifications to {@link #mOwnScrollY} are performed to reflect it in the view layout.
+ * modifications to {@link #getOwnScrollY()} are performed to reflect it in the view layout.
*/
private void updateChildren() {
Trace.beginSection("NSSL#updateChildren");
@@ -1406,11 +1404,11 @@ public class NotificationStackScrollLayout
if (mChildrenToAddAnimated.contains(child)) {
final int startingPosition = getPositionInLinearLayout(child);
final int childHeight = getIntrinsicHeight(child) + mPaddingBetweenElements;
- if (startingPosition < mOwnScrollY) {
+ if (startingPosition < getOwnScrollY()) {
// This child starts off screen, so let's keep it offscreen to keep the
// others visible
- setOwnScrollY(mOwnScrollY + childHeight);
+ setOwnScrollY(getOwnScrollY() + childHeight);
}
}
}
@@ -1430,7 +1428,7 @@ public class NotificationStackScrollLayout
targetScroll = Math.max(0, Math.min(targetScroll, getScrollRange()));
// Only apply the scroll if we're scrolling the view upwards, or the view is so
// far up that it is not visible anymore.
- if (mOwnScrollY < targetScroll || outOfViewScroll < mOwnScrollY) {
+ if (getOwnScrollY() < targetScroll || outOfViewScroll < getOwnScrollY()) {
setOwnScrollY(targetScroll);
}
}
@@ -1454,7 +1452,7 @@ public class NotificationStackScrollLayout
return;
}
int scrollRange = getScrollRange();
- if (scrollRange < mOwnScrollY && !mAmbientState.isClearAllInProgress()) {
+ if (scrollRange < getOwnScrollY() && !mAmbientState.isClearAllInProgress()) {
// if the scroll boundary updates the position of the stack,
boolean animateStackY = scrollRange < getScrollAmountToScrollBoundary()
&& mAnimateStackYForContentHeightChange;
@@ -1588,7 +1586,7 @@ public class NotificationStackScrollLayout
if (mMaxDisplayedNotifications != -1) {
// The stack intrinsic height already contains the correct value when there is a limit
// in the max number of notifications (e.g. as in keyguard).
- stackEndHeight = mIntrinsicContentHeight;
+ stackEndHeight = getIntrinsicContentHeight();
} else {
stackEndHeight = Math.max(0f, height - bottomMargin - topPadding);
}
@@ -1694,7 +1692,8 @@ public class NotificationStackScrollLayout
if (mShouldShowShelfOnly) {
stackHeight = getTopPadding() + mShelf.getIntrinsicHeight();
} else if (mQsFullScreen) {
- int stackStartPosition = mContentHeight - getTopPadding() + getIntrinsicPadding();
+ int stackStartPosition =
+ getContentHeight() - getTopPadding() + getIntrinsicPadding();
int stackEndPosition = mMaxTopPadding + mShelf.getIntrinsicHeight();
if (stackStartPosition <= stackEndPosition) {
stackHeight = stackEndPosition;
@@ -1763,14 +1762,6 @@ public class NotificationStackScrollLayout
updateClipping();
}
- /**
- * Return the height of the content ignoring the footer.
- */
- public int getIntrinsicContentHeight() {
- SceneContainerFlag.assertInLegacyMode();
- return (int) mIntrinsicContentHeight;
- }
-
public void updateClipping() {
boolean clipped = mRequestedClipBounds != null && !mInHeadsUpPinnedMode
&& !mHeadsUpAnimatingAway;
@@ -2074,8 +2065,8 @@ public class NotificationStackScrollLayout
// Only apply the scroll if we're scrolling the view upwards, or the view is so far up
// that it is not visible anymore.
- if (mOwnScrollY < targetScroll || outOfViewScroll < mOwnScrollY) {
- mScroller.startScroll(mScrollX, mOwnScrollY, 0, targetScroll - mOwnScrollY);
+ if (getOwnScrollY() < targetScroll || outOfViewScroll < getOwnScrollY()) {
+ mScroller.startScroll(mScrollX, getOwnScrollY(), 0, targetScroll - getOwnScrollY());
mDontReportNextOverScroll = true;
animateScroll();
return true;
@@ -2109,7 +2100,7 @@ public class NotificationStackScrollLayout
}
int range = getScrollRange();
- if (mOwnScrollY > range) {
+ if (getOwnScrollY() > range) {
setOwnScrollY(range);
}
}
@@ -2202,7 +2193,7 @@ public class NotificationStackScrollLayout
// Top overScroll might not grab all scrolling motion,
// we have to scroll as well.
float scrollAmount = newTopAmount < 0 ? -newTopAmount : 0.0f;
- float newScrollY = mOwnScrollY + scrollAmount;
+ float newScrollY = getOwnScrollY() + scrollAmount;
if (newScrollY > range) {
if (!mExpandedInThisMotion) {
float currentBottomPixels = getCurrentOverScrolledPixels(false);
@@ -2235,7 +2226,7 @@ public class NotificationStackScrollLayout
// Bottom overScroll might not grab all scrolling motion,
// we have to scroll as well.
float scrollAmount = newBottomAmount < 0 ? newBottomAmount : 0.0f;
- float newScrollY = mOwnScrollY + scrollAmount;
+ float newScrollY = getOwnScrollY() + scrollAmount;
if (newScrollY < 0) {
float currentTopPixels = getCurrentOverScrolledPixels(true);
// We overScroll on the top
@@ -2275,7 +2266,7 @@ public class NotificationStackScrollLayout
private void animateScroll() {
if (mScroller.computeScrollOffset()) {
- int oldY = mOwnScrollY;
+ int oldY = getOwnScrollY();
int y = mScroller.getCurrY();
if (oldY != y) {
@@ -2462,8 +2453,8 @@ public class NotificationStackScrollLayout
springBack();
} else {
float overScrollTop = getCurrentOverScrollAmount(true);
- if (mOwnScrollY < 0) {
- notifyOverscrollTopListener(-mOwnScrollY, isRubberbanded(true));
+ if (getOwnScrollY() < 0) {
+ notifyOverscrollTopListener(-getOwnScrollY(), isRubberbanded(true));
} else {
notifyOverscrollTopListener(overScrollTop, isRubberbanded(true));
}
@@ -2479,19 +2470,19 @@ public class NotificationStackScrollLayout
*/
private void springBack() {
int scrollRange = getScrollRange();
- boolean overScrolledTop = mOwnScrollY <= 0;
- boolean overScrolledBottom = mOwnScrollY >= scrollRange;
+ boolean overScrolledTop = getOwnScrollY() <= 0;
+ boolean overScrolledBottom = getOwnScrollY() >= scrollRange;
if (overScrolledTop || overScrolledBottom) {
boolean onTop;
float newAmount;
if (overScrolledTop) {
onTop = true;
- newAmount = -mOwnScrollY;
+ newAmount = -getOwnScrollY();
setOwnScrollY(0);
mDontReportNextOverScroll = true;
} else {
onTop = false;
- newAmount = mOwnScrollY - scrollRange;
+ newAmount = getOwnScrollY() - scrollRange;
setOwnScrollY(scrollRange);
}
setOverScrollAmount(newAmount, onTop, false);
@@ -2508,7 +2499,7 @@ public class NotificationStackScrollLayout
}
// In current design, it only use the top HUN to treat all of HUNs
// although there are more than one HUNs
- int contentHeight = mContentHeight;
+ int contentHeight = getContentHeight();
if (!isExpanded() && mInHeadsUpPinnedMode) {
contentHeight = mHeadsUpInset + getTopHeadsUpPinnedHeight();
}
@@ -2644,16 +2635,16 @@ public class NotificationStackScrollLayout
(int) scrimTopPadding + (int) mNotificationStackSizeCalculator.computeHeight(
/* notificationStackScrollLayout= */ this, mMaxDisplayedNotifications,
shelfIntrinsicHeight);
- mIntrinsicContentHeight = height;
+ setIntrinsicContentHeight(height);
// The topPadding can be bigger than the regular padding when qs is expanded, in that
// state the maxPanelHeight and the contentHeight should be bigger
- mContentHeight =
- (int) (height + Math.max(getIntrinsicPadding(), getTopPadding()) + mBottomPadding);
+ setContentHeight(
+ (int) (height + Math.max(getIntrinsicPadding(), getTopPadding()) + mBottomPadding));
updateScrollability();
clampScrollPosition();
updateStackPosition();
- mAmbientState.setContentHeight(mContentHeight);
+ mAmbientState.setContentHeight(getContentHeight());
}
@Override
@@ -2795,7 +2786,7 @@ public class NotificationStackScrollLayout
float topAmount = getCurrentOverScrollAmount(true);
float bottomAmount = getCurrentOverScrollAmount(false);
if (velocityY < 0 && topAmount > 0) {
- setOwnScrollY(mOwnScrollY - (int) topAmount);
+ setOwnScrollY(getOwnScrollY() - (int) topAmount);
if (!mShouldUseSplitNotificationShade) {
mDontReportNextOverScroll = true;
setOverScrollAmount(0, true, false);
@@ -2803,7 +2794,7 @@ public class NotificationStackScrollLayout
mMaxOverScroll = Math.abs(velocityY) / 1000f * getRubberBandFactor(true /* onTop */)
* mOverflingDistance + topAmount;
} else if (velocityY > 0 && bottomAmount > 0) {
- setOwnScrollY((int) (mOwnScrollY + bottomAmount));
+ setOwnScrollY((int) (getOwnScrollY() + bottomAmount));
setOverScrollAmount(0, false, false);
mMaxOverScroll = Math.abs(velocityY) / 1000f
* getRubberBandFactor(false /* onTop */) * mOverflingDistance
@@ -2817,8 +2808,8 @@ public class NotificationStackScrollLayout
if (mExpandedInThisMotion) {
minScrollY = Math.min(minScrollY, mMaxScrollAfterExpand);
}
- mScroller.fling(mScrollX, mOwnScrollY, 1, velocityY, 0, 0, 0, minScrollY, 0,
- mExpandedInThisMotion && mOwnScrollY >= 0 ? 0 : Integer.MAX_VALUE / 2);
+ mScroller.fling(mScrollX, getOwnScrollY(), 1, velocityY, 0, 0, 0, minScrollY, 0,
+ mExpandedInThisMotion && getOwnScrollY() >= 0 ? 0 : Integer.MAX_VALUE / 2);
animateScroll();
}
@@ -3139,11 +3130,11 @@ public class NotificationStackScrollLayout
final int scrollBoundaryStart = getScrollAmountToScrollBoundary();
mAnimateStackYForContentHeightChange = true;
// This is reset onLayout
- if (endPosition <= mOwnScrollY - scrollBoundaryStart) {
+ if (endPosition <= getOwnScrollY() - scrollBoundaryStart) {
// This child is fully scrolled of the top, so we have to deduct its height from the
// scrollPosition
- setOwnScrollY(mOwnScrollY - childHeight);
- } else if (startingPosition < mOwnScrollY - scrollBoundaryStart) {
+ setOwnScrollY(getOwnScrollY() - childHeight);
+ } else if (startingPosition < getOwnScrollY() - scrollBoundaryStart) {
// This child is currently being scrolled into, set the scroll position to the
// start of this child
setOwnScrollY(startingPosition + scrollBoundaryStart);
@@ -3765,7 +3756,7 @@ public class NotificationStackScrollLayout
if (vscroll != 0) {
final int delta = (int) (vscroll * getVerticalScrollFactor());
final int range = getScrollRange();
- int oldScrollY = mOwnScrollY;
+ int oldScrollY = getOwnScrollY();
int newScrollY = oldScrollY - delta;
if (newScrollY < 0) {
newScrollY = 0;
@@ -3882,7 +3873,7 @@ public class NotificationStackScrollLayout
if (scrollAmount != 0.0f) {
// The scrolling motion could not be compensated with the
// existing overScroll, we have to scroll the view
- customOverScrollBy((int) scrollAmount, mOwnScrollY,
+ customOverScrollBy((int) scrollAmount, getOwnScrollY(),
range, getHeight() / 2);
// If we're scrolling, leavebehinds should be dismissed
mController.checkSnoozeLeavebehind();
@@ -3914,7 +3905,7 @@ public class NotificationStackScrollLayout
onOverScrollFling(false, initialVelocity);
}
} else {
- if (mScroller.springBack(mScrollX, mOwnScrollY, 0, 0, 0,
+ if (mScroller.springBack(mScrollX, getOwnScrollY(), 0, 0, 0,
getScrollRange())) {
animateScroll();
}
@@ -3928,7 +3919,7 @@ public class NotificationStackScrollLayout
break;
case ACTION_CANCEL:
if (mIsBeingDragged && getChildCount() > 0) {
- if (mScroller.springBack(mScrollX, mOwnScrollY, 0, 0, 0,
+ if (mScroller.springBack(mScrollX, getOwnScrollY(), 0, 0, 0,
getScrollRange())) {
animateScroll();
}
@@ -4214,7 +4205,7 @@ public class NotificationStackScrollLayout
setIsBeingDragged(false);
mActivePointerId = INVALID_POINTER;
recycleVelocityTracker();
- if (mScroller.springBack(mScrollX, mOwnScrollY, 0, 0, 0, getScrollRange())) {
+ if (mScroller.springBack(mScrollX, getOwnScrollY(), 0, 0, 0, getScrollRange())) {
animateScroll();
}
break;
@@ -4312,10 +4303,10 @@ public class NotificationStackScrollLayout
getHeight() - mPaddingBottom - getTopPadding() - mPaddingTop
- mShelf.getIntrinsicHeight();
final int targetScrollY = Math.max(0,
- Math.min(mOwnScrollY + direction * viewportHeight, getScrollRange()));
- if (targetScrollY != mOwnScrollY) {
- mScroller.startScroll(mScrollX, mOwnScrollY, 0,
- targetScrollY - mOwnScrollY);
+ Math.min(getOwnScrollY() + direction * viewportHeight, getScrollRange()));
+ if (targetScrollY != getOwnScrollY()) {
+ mScroller.startScroll(mScrollX, getOwnScrollY(), 0,
+ targetScrollY - getOwnScrollY());
animateScroll();
return true;
}
@@ -4357,9 +4348,9 @@ public class NotificationStackScrollLayout
// it is based on notifications bottom, which is lower on split shade.
// Here we prefer to use at least a minimum height defined for split shade.
// Otherwise the expansion motion is too fast.
- contentHeight = Math.max(mSplitShadeMinContentHeight, mContentHeight);
+ contentHeight = Math.max(mSplitShadeMinContentHeight, getContentHeight());
} else {
- contentHeight = mContentHeight;
+ contentHeight = getContentHeight();
}
return Math.max(mMaxLayoutHeight - contentHeight, 0);
}
@@ -4569,7 +4560,7 @@ public class NotificationStackScrollLayout
float diff = endPosition - layoutEnd;
mScrollViewFields.sendSyntheticScroll(diff);
}
- setOwnScrollY((int) (mOwnScrollY + endPosition - layoutEnd));
+ setOwnScrollY((int) (getOwnScrollY() + endPosition - layoutEnd));
mDisallowScrollingInThisMotion = true;
}
}
@@ -5080,7 +5071,7 @@ public class NotificationStackScrollLayout
event.setScrollY(mScrollViewFields.getScrollState().getScrollPosition());
event.setMaxScrollY(mScrollViewFields.getScrollState().getMaxScrollPosition());
} else {
- event.setScrollY(mOwnScrollY);
+ event.setScrollY(getOwnScrollY());
event.setMaxScrollY(getScrollRange());
}
}
@@ -5286,8 +5277,8 @@ public class NotificationStackScrollLayout
// If notifications are scrolled,
// clear out scrollY by the time we push notifications offscreen
- if (mOwnScrollY > 0) {
- setOwnScrollY((int) MathUtils.lerp(mOwnScrollY, 0, mQsExpansionFraction));
+ if (getOwnScrollY() > 0) {
+ setOwnScrollY((int) MathUtils.lerp(getOwnScrollY(), 0, mQsExpansionFraction));
}
if (!FooterViewRefactor.isEnabled() && footerAffected) {
updateFooter();
@@ -5295,6 +5286,15 @@ public class NotificationStackScrollLayout
}
@VisibleForTesting
+ int getOwnScrollY() {
+ if (SceneContainerFlag.isEnabled()) {
+ return 0;
+ } else {
+ return mOwnScrollY;
+ }
+ }
+
+ @VisibleForTesting
void setOwnScrollY(int ownScrollY) {
setOwnScrollY(ownScrollY, false /* animateScrollChangeListener */);
}
@@ -5311,11 +5311,11 @@ public class NotificationStackScrollLayout
return;
}
- if (ownScrollY != mOwnScrollY) {
+ if (ownScrollY != getOwnScrollY()) {
// We still want to call the normal scrolled changed for accessibility reasons
- onScrollChanged(mScrollX, ownScrollY, mScrollX, mOwnScrollY);
+ onScrollChanged(mScrollX, ownScrollY, mScrollX, getOwnScrollY());
mOwnScrollY = ownScrollY;
- mAmbientState.setScrollY(mOwnScrollY);
+ mAmbientState.setScrollY(getOwnScrollY());
updateOnScrollChange();
updateStackPosition(animateStackYChangeListener);
}
@@ -5324,7 +5324,7 @@ public class NotificationStackScrollLayout
private void updateOnScrollChange() {
SceneContainerFlag.assertInLegacyMode();
if (mScrollListener != null) {
- mScrollListener.accept(mOwnScrollY);
+ mScrollListener.accept(getOwnScrollY());
}
updateForwardAndBackwardScrollability();
requestChildrenUpdate();
@@ -5517,12 +5517,7 @@ public class NotificationStackScrollLayout
println(pw, "hideAmount", mAmbientState.getHideAmount());
println(pw, "ambientStateSwipingUp", mAmbientState.isSwipingUp());
println(pw, "maxDisplayedNotifications", mMaxDisplayedNotifications);
- println(pw, "intrinsicContentHeight", mIntrinsicContentHeight);
- println(pw, "contentHeight", mContentHeight);
println(pw, "intrinsicPadding", mIntrinsicPadding);
- if (!SceneContainerFlag.isEnabled()) {
- println(pw, "topPadding", getTopPadding());
- }
println(pw, "bottomPadding", mBottomPadding);
dumpRoundedRectClipping(pw);
println(pw, "requestedClipBounds", mRequestedClipBounds);
@@ -5547,6 +5542,12 @@ public class NotificationStackScrollLayout
println(pw, "isSmallLandscapeLockscreenEnabled", mIsSmallLandscapeLockscreenEnabled);
mNotificationStackSizeCalculator.dump(pw, args);
mScrollViewFields.dump(pw);
+ if (!SceneContainerFlag.isEnabled()) {
+ // fields which will be removed with SceneContainer
+ println(pw, "intrinsicContentHeight", getIntrinsicContentHeight());
+ println(pw, "contentHeight", getContentHeight());
+ println(pw, "topPadding", getTopPadding());
+ }
});
pw.println();
pw.println("Contents:");
@@ -6891,7 +6892,7 @@ public class NotificationStackScrollLayout
public void expansionStateChanged(boolean isExpanding) {
mExpandingNotification = isExpanding;
if (!mExpandedInThisMotion) {
- mMaxScrollAfterExpand = mOwnScrollY;
+ mMaxScrollAfterExpand = getOwnScrollY();
mExpandedInThisMotion = true;
}
}
@@ -6947,4 +6948,31 @@ public class NotificationStackScrollLayout
void onAnimationEnd(
List<ExpandableNotificationRow> viewsToRemove, @SelectedRows int selectedRows);
}
+
+ // -------------------- Getters / Setters for the SceneContainer refactor ----------------------
+
+ /** Use {@link ScrollViewFields#intrinsicStackHeight}, when SceneContainerFlag is enabled. */
+ private int getContentHeight() {
+ SceneContainerFlag.assertInLegacyMode();
+ return mContentHeight;
+ }
+
+ private void setContentHeight(int contentHeight) {
+ SceneContainerFlag.assertInLegacyMode();
+ mContentHeight = contentHeight;
+ }
+
+ /**
+ * Use {@link ScrollViewFields#intrinsicStackHeight}, when SceneContainerFlag is enabled.
+ * @return the height of the content ignoring the footer.
+ */
+ public float getIntrinsicContentHeight() {
+ SceneContainerFlag.assertInLegacyMode();
+ return mIntrinsicContentHeight;
+ }
+
+ private void setIntrinsicContentHeight(float intrinsicContentHeight) {
+ SceneContainerFlag.assertInLegacyMode();
+ mIntrinsicContentHeight = intrinsicContentHeight;
+ }
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutController.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutController.java
index 04974b4353f4..dc1a191ce233 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutController.java
@@ -43,7 +43,6 @@ import android.os.UserHandle;
import android.provider.Settings;
import android.service.notification.StatusBarNotification;
import android.util.Log;
-import android.util.Pair;
import android.util.Property;
import android.view.Display;
import android.view.MotionEvent;
@@ -105,6 +104,7 @@ import com.android.systemui.statusbar.notification.HeadsUpTouchHelper.HeadsUpNot
import com.android.systemui.statusbar.notification.LaunchAnimationParameters;
import com.android.systemui.statusbar.notification.NotificationActivityStarter;
import com.android.systemui.statusbar.notification.NotificationWakeUpCoordinator;
+import com.android.systemui.statusbar.notification.collection.EntryWithDismissStats;
import com.android.systemui.statusbar.notification.collection.NotifCollection;
import com.android.systemui.statusbar.notification.collection.NotifPipeline;
import com.android.systemui.statusbar.notification.collection.NotificationEntry;
@@ -1188,7 +1188,7 @@ public class NotificationStackScrollLayoutController implements Dumpable {
public int getIntrinsicContentHeight() {
SceneContainerFlag.assertInLegacyMode();
- return mView.getIntrinsicContentHeight();
+ return (int) mView.getIntrinsicContentHeight();
}
/**
@@ -1777,12 +1777,12 @@ public class NotificationStackScrollLayoutController implements Dumpable {
mNotifCollection.dismissAllNotifications(
mLockscreenUserManager.getCurrentUserId());
} else {
- final List<Pair<NotificationEntry, DismissedByUserStats>>
+ final List<EntryWithDismissStats>
entriesWithRowsDismissedFromShade = new ArrayList<>();
for (ExpandableNotificationRow row : viewsToRemove) {
final NotificationEntry entry = row.getEntry();
entriesWithRowsDismissedFromShade.add(
- new Pair<>(entry, getDismissedByUserStats(entry)));
+ new EntryWithDismissStats(entry, getDismissedByUserStats(entry)));
}
mNotifCollection.dismissNotifications(entriesWithRowsDismissedFromShade);
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/domain/interactor/SharedNotificationContainerInteractor.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/domain/interactor/SharedNotificationContainerInteractor.kt
index 6042964281c2..e644815960aa 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/domain/interactor/SharedNotificationContainerInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/domain/interactor/SharedNotificationContainerInteractor.kt
@@ -25,7 +25,7 @@ import com.android.systemui.keyguard.domain.interactor.KeyguardInteractor
import com.android.systemui.res.R
import com.android.systemui.scene.shared.flag.SceneContainerFlag
import com.android.systemui.shade.LargeScreenHeaderHelper
-import com.android.systemui.shade.domain.interactor.ShadeInteractor
+import com.android.systemui.shade.ShadeDisplayAware
import com.android.systemui.statusbar.policy.SplitShadeStateController
import dagger.Lazy
import javax.inject.Inject
@@ -45,9 +45,8 @@ import kotlinx.coroutines.flow.map
class SharedNotificationContainerInteractor
@Inject
constructor(
- private val context: Context,
+ @ShadeDisplayAware private val context: Context,
private val splitShadeStateController: Lazy<SplitShadeStateController>,
- private val shadeInteractor: Lazy<ShadeInteractor>,
configurationInteractor: ConfigurationInteractor,
keyguardInteractor: KeyguardInteractor,
deviceEntryUdfpsInteractor: DeviceEntryUdfpsInteractor,
@@ -66,23 +65,14 @@ constructor(
* distinctUntilChanged() to this would cause configurationBasedDimensions to miss configuration
* updates that affect other resources, like margins or the large screen header flag.
*/
- private val dimensionsUpdateEventsWithShouldUseSplitShade: Flow<Boolean> =
- if (SceneContainerFlag.isEnabled) {
- combine(
- configurationInteractor.onAnyConfigurationChange,
- shadeInteractor.get().isShadeLayoutWide,
- ) { _, isShadeLayoutWide ->
- isShadeLayoutWide
- }
- } else {
- configurationInteractor.onAnyConfigurationChange.map {
- splitShadeStateController.get().shouldUseSplitNotificationShade(context.resources)
- }
- }
-
+ @Deprecated("Use SharedNotificationContainerViewModel.ConfigurationBasedDimensions instead")
val configurationBasedDimensions: Flow<ConfigurationBasedDimensions> =
- dimensionsUpdateEventsWithShouldUseSplitShade
- .map { shouldUseSplitShade ->
+ configurationInteractor.onAnyConfigurationChange
+ .map {
+ val shouldUseSplitShade =
+ splitShadeStateController
+ .get()
+ .shouldUseSplitNotificationShade(context.resources)
with(context.resources) {
ConfigurationBasedDimensions(
useSplitShade = shouldUseSplitShade,
@@ -101,6 +91,10 @@ constructor(
}
}
.distinctUntilChanged()
+ get() {
+ SceneContainerFlag.assertInLegacyMode()
+ return field
+ }
/**
* The notification shelf can extend over the lock icon area if:
@@ -125,6 +119,7 @@ constructor(
_notificationStackChanged.value = _notificationStackChanged.value + 1
}
+ @Deprecated("Use SharedNotificationContainerViewModel.ConfigurationBasedDimensions instead")
data class ConfigurationBasedDimensions(
val useSplitShade: Boolean,
val useLargeScreenHeader: Boolean,
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/ui/viewbinder/NotificationListViewBinder.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/ui/viewbinder/NotificationListViewBinder.kt
index 787ff024a4d6..fd19f1ff634e 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/ui/viewbinder/NotificationListViewBinder.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/ui/viewbinder/NotificationListViewBinder.kt
@@ -22,6 +22,7 @@ import androidx.lifecycle.lifecycleScope
import com.android.app.tracing.TraceUtils.traceAsync
import com.android.internal.logging.MetricsLogger
import com.android.internal.logging.nano.MetricsProto
+import com.android.systemui.Flags
import com.android.systemui.common.ui.ConfigurationState
import com.android.systemui.common.ui.view.setImportantForAccessibilityYesNo
import com.android.systemui.dagger.qualifiers.Background
@@ -145,7 +146,9 @@ constructor(
// The footer needs to be re-inflated every time the theme or the font size changes.
configuration
.inflateLayout<FooterView>(
- R.layout.status_bar_notification_footer,
+ if (Flags.notificationsRedesignFooterView())
+ R.layout.status_bar_notification_footer_redesign
+ else R.layout.status_bar_notification_footer,
parentView,
attachToRoot = false,
)
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/ui/viewbinder/SharedNotificationContainerBinder.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/ui/viewbinder/SharedNotificationContainerBinder.kt
index 54b5ca37c43e..ce89d786c350 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/ui/viewbinder/SharedNotificationContainerBinder.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/ui/viewbinder/SharedNotificationContainerBinder.kt
@@ -18,10 +18,12 @@ package com.android.systemui.statusbar.notification.stack.ui.viewbinder
import androidx.lifecycle.Lifecycle
import androidx.lifecycle.repeatOnLifecycle
+import com.android.systemui.Flags
import com.android.systemui.common.ui.view.onLayoutChanged
import com.android.systemui.communal.domain.interactor.CommunalSettingsInteractor
import com.android.systemui.dagger.SysUISingleton
import com.android.systemui.dagger.qualifiers.Main
+import com.android.systemui.keyguard.domain.interactor.KeyguardInteractor
import com.android.systemui.keyguard.ui.viewmodel.ViewStateAccessor
import com.android.systemui.lifecycle.repeatWhenAttached
import com.android.systemui.scene.shared.flag.SceneContainerFlag
@@ -48,8 +50,19 @@ constructor(
private val notificationScrollViewBinder: NotificationScrollViewBinder,
private val communalSettingsInteractor: CommunalSettingsInteractor,
@Main private val mainImmediateDispatcher: CoroutineDispatcher,
+ val keyguardInteractor: KeyguardInteractor,
) {
+ private val calculateMaxNotifications: (Float, Boolean) -> Int = { space, extraShelfSpace ->
+ val shelfHeight = controller.getShelfHeight().toFloat()
+ notificationStackSizeCalculator.computeMaxKeyguardNotifications(
+ controller.view,
+ space,
+ if (extraShelfSpace) shelfHeight else 0f,
+ shelfHeight,
+ )
+ }
+
fun bind(
view: SharedNotificationContainer,
viewModel: SharedNotificationContainerViewModel,
@@ -107,17 +120,9 @@ constructor(
}
launch {
- viewModel
- .getMaxNotifications { space, extraShelfSpace ->
- val shelfHeight = controller.getShelfHeight().toFloat()
- notificationStackSizeCalculator.computeMaxKeyguardNotifications(
- controller.getView(),
- space,
- if (extraShelfSpace) shelfHeight else 0f,
- shelfHeight,
- )
- }
- .collect { controller.setMaxDisplayedNotifications(it) }
+ viewModel.getMaxNotifications(calculateMaxNotifications).collect {
+ controller.setMaxDisplayedNotifications(it)
+ }
}
if (!SceneContainerFlag.isEnabled) {
@@ -136,6 +141,30 @@ constructor(
}
}
+ if (!SceneContainerFlag.isEnabled) {
+ if (Flags.magicPortraitWallpapers()) {
+ launch {
+ viewModel
+ .getNotificationStackAbsoluteBottom(
+ calculateMaxNotifications = calculateMaxNotifications,
+ calculateHeight = { maxNotifications ->
+ notificationStackSizeCalculator.computeHeight(
+ maxNotifs = maxNotifications,
+ shelfHeight = controller.getShelfHeight().toFloat(),
+ stack = controller.view,
+ )
+ },
+ controller.getShelfHeight().toFloat(),
+ )
+ .collect { bottom ->
+ keyguardInteractor.setNotificationStackAbsoluteBottom(
+ bottom
+ )
+ }
+ }
+ }
+ }
+
launch { viewModel.translationX.collect { x -> controller.translationX = x } }
launch {
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 9515029962ea..e6663d51aed5 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
@@ -19,9 +19,11 @@
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.common.shared.model.NotificationContainerBounds
+import com.android.systemui.common.ui.domain.interactor.ConfigurationInteractor
import com.android.systemui.communal.domain.interactor.CommunalSceneInteractor
import com.android.systemui.dagger.SysUISingleton
import com.android.systemui.dagger.qualifiers.Application
@@ -66,10 +68,14 @@ import com.android.systemui.keyguard.ui.viewmodel.OffToLockscreenTransitionViewM
import com.android.systemui.keyguard.ui.viewmodel.PrimaryBouncerToGoneTransitionViewModel
import com.android.systemui.keyguard.ui.viewmodel.PrimaryBouncerToLockscreenTransitionViewModel
import com.android.systemui.keyguard.ui.viewmodel.ViewStateAccessor
+import com.android.systemui.res.R
import com.android.systemui.scene.shared.flag.SceneContainerFlag
import com.android.systemui.scene.shared.model.Scenes
+import com.android.systemui.shade.LargeScreenHeaderHelper
import com.android.systemui.shade.domain.interactor.ShadeInteractor
-import com.android.systemui.shade.shared.flag.DualShade
+import com.android.systemui.shade.shared.model.ShadeMode.Dual
+import com.android.systemui.shade.shared.model.ShadeMode.Single
+import com.android.systemui.shade.shared.model.ShadeMode.Split
import com.android.systemui.statusbar.notification.domain.interactor.HeadsUpNotificationInteractor
import com.android.systemui.statusbar.notification.stack.domain.interactor.NotificationStackAppearanceInteractor
import com.android.systemui.statusbar.notification.stack.domain.interactor.SharedNotificationContainerInteractor
@@ -90,6 +96,7 @@ import kotlinx.coroutines.flow.combine
import kotlinx.coroutines.flow.combineTransform
import kotlinx.coroutines.flow.distinctUntilChanged
import kotlinx.coroutines.flow.emptyFlow
+import kotlinx.coroutines.flow.filter
import kotlinx.coroutines.flow.first
import kotlinx.coroutines.flow.flatMapLatest
import kotlinx.coroutines.flow.flow
@@ -108,6 +115,8 @@ constructor(
private val interactor: SharedNotificationContainerInteractor,
dumpManager: DumpManager,
@Application applicationScope: CoroutineScope,
+ private val context: Context,
+ configurationInteractor: ConfigurationInteractor,
private val keyguardInteractor: KeyguardInteractor,
private val keyguardTransitionInteractor: KeyguardTransitionInteractor,
private val shadeInteractor: ShadeInteractor,
@@ -145,6 +154,7 @@ constructor(
private val communalSceneInteractor: CommunalSceneInteractor,
// Lazy because it's only used in the SceneContainer + Dual Shade configuration.
headsUpNotificationInteractor: Lazy<HeadsUpNotificationInteractor>,
+ private val largeScreenHeaderHelperLazy: Lazy<LargeScreenHeaderHelper>,
unfoldTransitionInteractor: UnfoldTransitionInteractor,
) : FlowDumperImpl(dumpManager) {
@@ -187,33 +197,62 @@ constructor(
@VisibleForTesting
val paddingTopDimen: Flow<Int> =
- interactor.configurationBasedDimensions
- .map {
- when {
- it.useLargeScreenHeader -> it.marginTopLargeScreen
- else -> it.marginTop
+ if (SceneContainerFlag.isEnabled) {
+ configurationInteractor.onAnyConfigurationChange.map {
+ with(context.resources) {
+ val useLargeScreenHeader =
+ getBoolean(R.bool.config_use_large_screen_shade_header)
+ if (useLargeScreenHeader) {
+ largeScreenHeaderHelperLazy.get().getLargeScreenHeaderHeight()
+ } else {
+ getDimensionPixelSize(R.dimen.notification_panel_margin_top)
+ }
+ }
+ }
+ } else {
+ interactor.configurationBasedDimensions.map {
+ when {
+ it.useLargeScreenHeader -> it.marginTopLargeScreen
+ else -> it.marginTop
+ }
}
}
.distinctUntilChanged()
.dumpWhileCollecting("paddingTopDimen")
val configurationBasedDimensions: Flow<ConfigurationBasedDimensions> =
- interactor.configurationBasedDimensions
- .map {
- val marginTop =
- when {
- // y position of the NSSL in the window needs to be 0 under scene container
- SceneContainerFlag.isEnabled -> 0
- it.useLargeScreenHeader -> it.marginTopLargeScreen
- else -> it.marginTop
+ if (SceneContainerFlag.isEnabled) {
+ combine(
+ shadeInteractor.isShadeLayoutWide,
+ configurationInteractor.onAnyConfigurationChange,
+ ) { isShadeLayoutWide, _ ->
+ with(context.resources) {
+ // TODO(b/338033836): Define separate horizontal margins for dual shade.
+ val marginHorizontal =
+ getDimensionPixelSize(R.dimen.notification_panel_margin_horizontal)
+ ConfigurationBasedDimensions(
+ marginStart = if (isShadeLayoutWide) 0 else marginHorizontal,
+ marginEnd = marginHorizontal,
+ marginBottom =
+ getDimensionPixelSize(R.dimen.notification_panel_margin_bottom),
+ // y position of the NSSL in the window needs to be 0 under scene
+ // container
+ marginTop = 0,
+ useSplitShade = isShadeLayoutWide,
+ )
}
- ConfigurationBasedDimensions(
- marginStart = if (it.useSplitShade) 0 else it.marginHorizontal,
- marginEnd = it.marginHorizontal,
- marginBottom = it.marginBottom,
- marginTop = marginTop,
- useSplitShade = it.useSplitShade,
- )
+ }
+ } else {
+ interactor.configurationBasedDimensions.map {
+ ConfigurationBasedDimensions(
+ marginStart = if (it.useSplitShade) 0 else it.marginHorizontal,
+ marginEnd = it.marginHorizontal,
+ marginBottom = it.marginBottom,
+ marginTop =
+ if (it.useLargeScreenHeader) it.marginTopLargeScreen else it.marginTop,
+ useSplitShade = it.useSplitShade,
+ )
+ }
}
.distinctUntilChanged()
.dumpWhileCollecting("configurationBasedDimensions")
@@ -221,13 +260,15 @@ constructor(
/** If the user is visually on one of the unoccluded lockscreen states. */
val isOnLockscreen: Flow<Boolean> =
anyOf(
- keyguardTransitionInteractor.isFinishedIn(AOD),
- keyguardTransitionInteractor.isFinishedIn(DOZING),
- keyguardTransitionInteractor.isFinishedIn(ALTERNATE_BOUNCER),
- keyguardTransitionInteractor.isFinishedIn(
- scene = Scenes.Bouncer,
- stateWithoutSceneContainer = PRIMARY_BOUNCER,
- ),
+ 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 },
)
.flowName("isOnLockscreen")
@@ -404,42 +445,60 @@ constructor(
* notifications unless in splitshade.
*/
private val alphaForShadeAndQsExpansion: Flow<Float> =
- if (DualShade.isEnabled) {
- combineTransform(
- headsUpNotificationInteractor.get().isHeadsUpOrAnimatingAway,
- shadeInteractor.shadeExpansion,
- shadeInteractor.qsExpansion,
- ) { isHeadsUpOrAnimatingAway, shadeExpansion, qsExpansion ->
- if (isHeadsUpOrAnimatingAway) {
- // Ensure HUNs will be visible in QS shade (at least while unlocked)
- emit(1f)
- } else if (shadeExpansion > 0f || qsExpansion > 0f) {
- // Fade out as QS shade expands
- emit(1f - qsExpansion)
- }
- }
- } else {
- interactor.configurationBasedDimensions.flatMapLatest { configurationBasedDimensions
- ->
- combineTransform(shadeInteractor.shadeExpansion, shadeInteractor.qsExpansion) {
- shadeExpansion,
- qsExpansion ->
- if (shadeExpansion > 0f || qsExpansion > 0f) {
- if (configurationBasedDimensions.useSplitShade) {
- emit(1f)
- } else if (qsExpansion == 1f) {
+ if (SceneContainerFlag.isEnabled) {
+ shadeInteractor.shadeMode.flatMapLatest { shadeMode ->
+ when (shadeMode) {
+ Single ->
+ combineTransform(
+ shadeInteractor.shadeExpansion,
+ shadeInteractor.qsExpansion,
+ ) { shadeExpansion, qsExpansion ->
+ if (qsExpansion == 1f) {
// Ensure HUNs will be visible in QS shade (at least while unlocked)
emit(1f)
- } else {
+ } else if (shadeExpansion > 0f || qsExpansion > 0f) {
// Fade as QS shade expands
emit(1f - qsExpansion)
}
}
+ Split -> isAnyExpanded.filter { it }.map { 1f }
+ Dual ->
+ combineTransform(
+ headsUpNotificationInteractor.get().isHeadsUpOrAnimatingAway,
+ shadeInteractor.shadeExpansion,
+ shadeInteractor.qsExpansion,
+ ) { isHeadsUpOrAnimatingAway, shadeExpansion, qsExpansion ->
+ if (isHeadsUpOrAnimatingAway) {
+ // Ensure HUNs will be visible in QS shade (at least while unlocked)
+ emit(1f)
+ } else if (shadeExpansion > 0f || qsExpansion > 0f) {
+ // Fade out as QS shade expands
+ emit(1f - qsExpansion)
+ }
+ }
+ }
+ }
+ } else {
+ interactor.configurationBasedDimensions.flatMapLatest { configurationBasedDimensions ->
+ combineTransform(shadeInteractor.shadeExpansion, shadeInteractor.qsExpansion) {
+ shadeExpansion,
+ qsExpansion ->
+ if (shadeExpansion > 0f || qsExpansion > 0f) {
+ if (configurationBasedDimensions.useSplitShade) {
+ emit(1f)
+ } else if (qsExpansion == 1f) {
+ // Ensure HUNs will be visible in QS shade (at least while unlocked)
+ emit(1f)
+ } else {
+ // Fade as QS shade expands
+ emit(1f - qsExpansion)
+ }
}
}
}
- .onStart { emit(1f) }
- .dumpWhileCollecting("alphaForShadeAndQsExpansion")
+ }
+ .onStart { emit(1f) }
+ .dumpWhileCollecting("alphaForShadeAndQsExpansion")
val panelAlpha = keyguardInteractor.panelAlpha
@@ -672,6 +731,36 @@ constructor(
.dumpWhileCollecting("maxNotifications")
}
+ /**
+ * Wallpaper needs the absolute bottom of notification stack to avoid occlusion
+ *
+ * @param calculateMaxNotifications is required by getMaxNotifications as calculateSpace by
+ * calling computeMaxKeyguardNotifications in NotificationStackSizeCalculator
+ * @param calculateHeight is calling computeHeight in NotificationStackSizeCalculator The edge
+ * case is that when maxNotifications is 0, we won't take shelfHeight into account
+ */
+ fun getNotificationStackAbsoluteBottom(
+ calculateMaxNotifications: (Float, Boolean) -> Int,
+ calculateHeight: (Int) -> Float,
+ shelfHeight: Float,
+ ): Flow<Float> {
+ SceneContainerFlag.assertInLegacyMode()
+
+ return combine(
+ getMaxNotifications(calculateMaxNotifications).map {
+ val height = calculateHeight(it)
+ if (it == 0) {
+ height - shelfHeight
+ } else {
+ height
+ }
+ },
+ bounds.map { it.top },
+ ) { height, top ->
+ top + height
+ }
+ }
+
fun notificationStackChanged() {
interactor.notificationStackChanged()
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/CentralSurfacesImpl.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/CentralSurfacesImpl.java
index b469e3db41e5..65663fd3f19c 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/CentralSurfacesImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/CentralSurfacesImpl.java
@@ -646,6 +646,8 @@ public class CentralSurfacesImpl implements CoreStartable, CentralSurfaces {
NavigationBarController navigationBarController,
AccessibilityFloatingMenuController accessibilityFloatingMenuController,
Lazy<AssistManager> assistManagerLazy,
+ // TODO: b/374267505 - Decouple the config change needed for shade window classes from
+ // the one for other windows.
ConfigurationController configurationController,
NotificationShadeWindowController notificationShadeWindowController,
Lazy<NotificationShadeWindowViewController> notificationShadeWindowViewControllerLazy,
@@ -1975,6 +1977,10 @@ public class CentralSurfacesImpl implements CoreStartable, CentralSurfaces {
* meantime, just update the things that we know change.
*/
void updateResources() {
+ // TODO: b/374267505 - we shouldn't propagate this from here. Each class should be
+ // listening at the correct configuration change. For example, shade window classes should
+ // be listening at @ShadeDisplayAware configurations (as it can be on a different display.
+
// Update the quick setting tiles
if (mQSPanelController != null) {
mQSPanelController.updateResources();
diff --git a/packages/SystemUI/src/com/android/systemui/volume/dialog/domain/interactor/VolumeDialogCallbacksInteractor.kt b/packages/SystemUI/src/com/android/systemui/volume/dialog/domain/interactor/VolumeDialogCallbacksInteractor.kt
index 3d125b81cbd2..fa1088426351 100644
--- a/packages/SystemUI/src/com/android/systemui/volume/dialog/domain/interactor/VolumeDialogCallbacksInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/volume/dialog/domain/interactor/VolumeDialogCallbacksInteractor.kt
@@ -105,8 +105,8 @@ constructor(
scope.trySend(VolumeDialogEventModel.ShowSafetyWarning(flags))
}
- override fun onAccessibilityModeChanged(showA11yStream: Boolean) {
- scope.trySend(VolumeDialogEventModel.AccessibilityModeChanged(showA11yStream))
+ override fun onAccessibilityModeChanged(showA11yStream: Boolean?) {
+ scope.trySend(VolumeDialogEventModel.AccessibilityModeChanged(showA11yStream == true))
}
// Captions button is remove from the Volume Dialog
diff --git a/packages/SystemUI/src/com/android/systemui/volume/dialog/domain/interactor/VolumeDialogVisibilityInteractor.kt b/packages/SystemUI/src/com/android/systemui/volume/dialog/domain/interactor/VolumeDialogVisibilityInteractor.kt
index 2668589be1b3..fb108c5b7b15 100644
--- a/packages/SystemUI/src/com/android/systemui/volume/dialog/domain/interactor/VolumeDialogVisibilityInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/volume/dialog/domain/interactor/VolumeDialogVisibilityInteractor.kt
@@ -60,7 +60,7 @@ constructor(
) {
@SuppressLint("SharedFlowCreation")
- private val mutableDismissDialogEvents = MutableSharedFlow<Unit>()
+ private val mutableDismissDialogEvents = MutableSharedFlow<Unit>(extraBufferCapacity = 1)
val dialogVisibility: Flow<VolumeDialogVisibilityModel> = repository.dialogVisibility
init {
@@ -74,7 +74,7 @@ constructor(
.mapNotNull { it.toVisibilityModel() }
.onEach { model ->
updateVisibility { model }
- if (model is VolumeDialogVisibilityModel.Visible) {
+ if (model is Visible) {
resetDismissTimeout()
}
}
@@ -87,17 +87,17 @@ constructor(
*/
fun dismissDialog(reason: Int) {
updateVisibility { visibilityModel ->
- if (visibilityModel is VolumeDialogVisibilityModel.Dismissed) {
+ if (visibilityModel is Dismissed) {
visibilityModel
} else {
- VolumeDialogVisibilityModel.Dismissed(reason)
+ Dismissed(reason)
}
}
}
/** Resets current dialog timeout. */
- suspend fun resetDismissTimeout() {
- mutableDismissDialogEvents.emit(Unit)
+ fun resetDismissTimeout() {
+ mutableDismissDialogEvents.tryEmit(Unit)
}
private fun updateVisibility(
diff --git a/packages/SystemUI/src/com/android/systemui/volume/dialog/ringer/ui/binder/VolumeDialogRingerViewBinder.kt b/packages/SystemUI/src/com/android/systemui/volume/dialog/ringer/ui/binder/VolumeDialogRingerViewBinder.kt
new file mode 100644
index 000000000000..c05a0b265ee9
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/volume/dialog/ringer/ui/binder/VolumeDialogRingerViewBinder.kt
@@ -0,0 +1,48 @@
+/*
+ * 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.
+ */
+
+package com.android.systemui.volume.dialog.ringer.ui.binder
+
+import android.view.View
+import com.android.systemui.lifecycle.WindowLifecycleState
+import com.android.systemui.lifecycle.repeatWhenAttached
+import com.android.systemui.lifecycle.setSnapshotBinding
+import com.android.systemui.lifecycle.viewModel
+import com.android.systemui.volume.dialog.dagger.scope.VolumeDialogScope
+import com.android.systemui.volume.dialog.ringer.ui.viewmodel.VolumeDialogRingerDrawerViewModel
+import javax.inject.Inject
+import kotlinx.coroutines.awaitCancellation
+
+@VolumeDialogScope
+class VolumeDialogRingerViewBinder
+@Inject
+constructor(private val viewModelFactory: VolumeDialogRingerDrawerViewModel.Factory) {
+
+ fun bind(view: View) {
+ with(view) {
+ repeatWhenAttached {
+ viewModel(
+ traceName = "VolumeDialogRingerViewBinder",
+ minWindowLifecycleState = WindowLifecycleState.ATTACHED,
+ factory = { viewModelFactory.create() },
+ ) { viewModel ->
+ setSnapshotBinding {}
+ awaitCancellation()
+ }
+ }
+ }
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/volume/dialog/sliders/domain/interactor/VolumeDialogSliderInteractor.kt b/packages/SystemUI/src/com/android/systemui/volume/dialog/sliders/domain/interactor/VolumeDialogSliderInteractor.kt
index f78a8dcabc1c..876bf2c4a154 100644
--- a/packages/SystemUI/src/com/android/systemui/volume/dialog/sliders/domain/interactor/VolumeDialogSliderInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/volume/dialog/sliders/domain/interactor/VolumeDialogSliderInteractor.kt
@@ -25,6 +25,7 @@ import dagger.assisted.Assisted
import dagger.assisted.AssistedFactory
import dagger.assisted.AssistedInject
import kotlinx.coroutines.flow.Flow
+import kotlinx.coroutines.flow.distinctUntilChanged
import kotlinx.coroutines.flow.mapNotNull
/** Operates a state of particular slider of the Volume Dialog. */
@@ -37,12 +38,23 @@ constructor(
) {
val slider: Flow<VolumeDialogStreamModel> =
- volumeDialogStateInteractor.volumeDialogState.mapNotNull {
- it.streamModels[sliderType.audioStream]
- }
+ volumeDialogStateInteractor.volumeDialogState
+ .mapNotNull {
+ it.streamModels[sliderType.audioStream]?.run {
+ if (level < levelMin || level > levelMax) {
+ copy(level = level.coerceIn(levelMin, levelMax))
+ } else {
+ this
+ }
+ }
+ }
+ .distinctUntilChanged()
fun setStreamVolume(userLevel: Int) {
- volumeDialogController.setStreamVolume(sliderType.audioStream, userLevel)
+ with(volumeDialogController) {
+ setStreamVolume(sliderType.audioStream, userLevel)
+ setActiveStream(sliderType.audioStream)
+ }
}
@VolumeDialogScope
diff --git a/packages/SystemUI/src/com/android/systemui/volume/dialog/sliders/ui/VolumeDialogSliderViewBinder.kt b/packages/SystemUI/src/com/android/systemui/volume/dialog/sliders/ui/VolumeDialogSliderViewBinder.kt
index 25a5f287c21f..5c4d53aaf2c5 100644
--- a/packages/SystemUI/src/com/android/systemui/volume/dialog/sliders/ui/VolumeDialogSliderViewBinder.kt
+++ b/packages/SystemUI/src/com/android/systemui/volume/dialog/sliders/ui/VolumeDialogSliderViewBinder.kt
@@ -16,32 +16,55 @@
package com.android.systemui.volume.dialog.sliders.ui
+import android.animation.Animator
+import android.animation.ObjectAnimator
import android.view.View
-import androidx.lifecycle.viewmodel.compose.viewModel
+import android.view.animation.DecelerateInterpolator
import com.android.systemui.lifecycle.WindowLifecycleState
import com.android.systemui.lifecycle.repeatWhenAttached
-import com.android.systemui.lifecycle.setSnapshotBinding
import com.android.systemui.lifecycle.viewModel
+import com.android.systemui.res.R
import com.android.systemui.volume.dialog.dagger.scope.VolumeDialogScope
+import com.android.systemui.volume.dialog.shared.model.VolumeDialogStreamModel
import com.android.systemui.volume.dialog.sliders.ui.viewmodel.VolumeDialogSliderViewModel
+import com.android.systemui.volume.dialog.ui.utils.JankListenerFactory
+import com.android.systemui.volume.dialog.ui.utils.awaitAnimation
+import com.google.android.material.slider.LabelFormatter
+import com.google.android.material.slider.Slider
import dagger.assisted.Assisted
import dagger.assisted.AssistedFactory
import dagger.assisted.AssistedInject
+import kotlin.math.roundToInt
import kotlinx.coroutines.awaitCancellation
+import kotlinx.coroutines.flow.launchIn
+import kotlinx.coroutines.flow.onEach
+
+private const val PROGRESS_CHANGE_ANIMATION_DURATION_MS = 80L
class VolumeDialogSliderViewBinder
@AssistedInject
-constructor(@Assisted private val viewModelProvider: () -> VolumeDialogSliderViewModel) {
+constructor(
+ @Assisted private val viewModelProvider: () -> VolumeDialogSliderViewModel,
+ private val jankListenerFactory: JankListenerFactory,
+) {
fun bind(view: View) {
with(view) {
+ val sliderView: Slider =
+ requireViewById<Slider>(R.id.volume_dialog_slider).apply {
+ labelBehavior = LabelFormatter.LABEL_GONE
+ }
repeatWhenAttached {
viewModel(
traceName = "VolumeDialogSliderViewBinder",
minWindowLifecycleState = WindowLifecycleState.ATTACHED,
factory = { viewModelProvider() },
) { viewModel ->
- setSnapshotBinding {}
+ sliderView.addOnChangeListener { _, value, fromUser ->
+ viewModel.setStreamVolume(value.roundToInt(), fromUser)
+ }
+
+ viewModel.model.onEach { it.bindToSlider(sliderView) }.launchIn(this)
awaitCancellation()
}
@@ -49,6 +72,19 @@ constructor(@Assisted private val viewModelProvider: () -> VolumeDialogSliderVie
}
}
+ private suspend fun VolumeDialogStreamModel.bindToSlider(slider: Slider) {
+ with(slider) {
+ valueFrom = levelMin.toFloat()
+ valueTo = levelMax.toFloat()
+ // coerce the current value to the new value range before animating it
+ value = value.coerceIn(valueFrom, valueTo)
+ setValueAnimated(
+ level.toFloat(),
+ jankListenerFactory.update(this, PROGRESS_CHANGE_ANIMATION_DURATION_MS),
+ )
+ }
+ }
+
@AssistedFactory
@VolumeDialogScope
interface Factory {
@@ -58,3 +94,16 @@ constructor(@Assisted private val viewModelProvider: () -> VolumeDialogSliderVie
): VolumeDialogSliderViewBinder
}
}
+
+private suspend fun Slider.setValueAnimated(
+ newValue: Float,
+ jankListener: Animator.AnimatorListener,
+) {
+ ObjectAnimator.ofFloat(value, newValue)
+ .apply {
+ duration = PROGRESS_CHANGE_ANIMATION_DURATION_MS
+ interpolator = DecelerateInterpolator()
+ addListener(jankListener)
+ }
+ .awaitAnimation<Float> { value = it }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/volume/dialog/sliders/ui/VolumeDialogSlidersViewBinder.kt b/packages/SystemUI/src/com/android/systemui/volume/dialog/sliders/ui/VolumeDialogSlidersViewBinder.kt
index 0a00f70b54f1..f486fe11bd68 100644
--- a/packages/SystemUI/src/com/android/systemui/volume/dialog/sliders/ui/VolumeDialogSlidersViewBinder.kt
+++ b/packages/SystemUI/src/com/android/systemui/volume/dialog/sliders/ui/VolumeDialogSlidersViewBinder.kt
@@ -16,14 +16,20 @@
package com.android.systemui.volume.dialog.sliders.ui
+import android.view.LayoutInflater
import android.view.View
+import android.view.ViewGroup
+import androidx.annotation.LayoutRes
+import androidx.compose.ui.util.fastForEachIndexed
import com.android.systemui.lifecycle.WindowLifecycleState
import com.android.systemui.lifecycle.repeatWhenAttached
import com.android.systemui.lifecycle.setSnapshotBinding
import com.android.systemui.lifecycle.viewModel
+import com.android.systemui.res.R
import com.android.systemui.volume.dialog.dagger.scope.VolumeDialogScope
import com.android.systemui.volume.dialog.sliders.ui.viewmodel.VolumeDialogSlidersViewModel
import javax.inject.Inject
+import kotlin.math.abs
import kotlinx.coroutines.awaitCancellation
@VolumeDialogScope
@@ -33,17 +39,44 @@ constructor(private val viewModelFactory: VolumeDialogSlidersViewModel.Factory)
fun bind(view: View) {
with(view) {
+ val volumeDialog: View = requireViewById(R.id.volume_dialog)
+ val floatingSlidersContainer: ViewGroup =
+ requireViewById(R.id.volume_dialog_floating_sliders_container)
repeatWhenAttached {
viewModel(
traceName = "VolumeDialogSlidersViewBinder",
minWindowLifecycleState = WindowLifecycleState.ATTACHED,
factory = { viewModelFactory.create() },
) { viewModel ->
- setSnapshotBinding {}
+ setSnapshotBinding {
+ viewModel.uiModel?.sliderViewBinder?.bind(volumeDialog)
+ val floatingSliderViewBinders =
+ viewModel.uiModel?.floatingSliderViewBinders ?: emptyList()
+ floatingSlidersContainer.ensureChildCount(
+ viewLayoutId = R.layout.volume_dialog_slider_floating,
+ count = floatingSliderViewBinders.size,
+ )
+ floatingSliderViewBinders.fastForEachIndexed { index, viewBinder ->
+ viewBinder.bind(floatingSlidersContainer.getChildAt(index))
+ }
+ }
awaitCancellation()
}
}
}
}
}
+
+private fun ViewGroup.ensureChildCount(@LayoutRes viewLayoutId: Int, count: Int) {
+ val childCountDelta = childCount - count
+ when {
+ childCountDelta > 0 -> {
+ removeViews(0, childCountDelta)
+ }
+ childCountDelta < 0 -> {
+ val inflater = LayoutInflater.from(context)
+ repeat(abs(childCountDelta)) { inflater.inflate(viewLayoutId, this, true) }
+ }
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/volume/dialog/sliders/ui/viewmodel/VolumeDialogSliderViewModel.kt b/packages/SystemUI/src/com/android/systemui/volume/dialog/sliders/ui/viewmodel/VolumeDialogSliderViewModel.kt
index 7ee722d97bbc..ea0b49d5294e 100644
--- a/packages/SystemUI/src/com/android/systemui/volume/dialog/sliders/ui/viewmodel/VolumeDialogSliderViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/volume/dialog/sliders/ui/viewmodel/VolumeDialogSliderViewModel.kt
@@ -16,26 +16,85 @@
package com.android.systemui.volume.dialog.sliders.ui.viewmodel
+import com.android.systemui.util.time.SystemClock
+import com.android.systemui.volume.Events
+import com.android.systemui.volume.dialog.dagger.scope.VolumeDialog
+import com.android.systemui.volume.dialog.domain.interactor.VolumeDialogVisibilityInteractor
import com.android.systemui.volume.dialog.shared.model.VolumeDialogStreamModel
import com.android.systemui.volume.dialog.sliders.domain.interactor.VolumeDialogSliderInteractor
import dagger.assisted.Assisted
import dagger.assisted.AssistedFactory
import dagger.assisted.AssistedInject
+import kotlinx.coroutines.CoroutineScope
+import kotlinx.coroutines.ExperimentalCoroutinesApi
import kotlinx.coroutines.flow.Flow
+import kotlinx.coroutines.flow.MutableStateFlow
+import kotlinx.coroutines.flow.SharingStarted
+import kotlinx.coroutines.flow.filter
+import kotlinx.coroutines.flow.filterNotNull
+import kotlinx.coroutines.flow.first
+import kotlinx.coroutines.flow.launchIn
+import kotlinx.coroutines.flow.mapLatest
+import kotlinx.coroutines.flow.stateIn
+/*
+ This prevents volume slider updates while user interacts with it. This is needed due to the
+ flawed VolumeDialogControllerImpl. It has a single threaded message queue that handles all state
+ updates and doesn't skip sequential updates of the same stream. This leads to a bottleneck when
+ user rigorously adjusts the slider.
+
+ Remove this when getting rid of the VolumeDialogControllerImpl as this doesn't happen in the
+ Volume Panel that uses the new coroutine-backed AudioRepository.
+*/
+// TODO(b/375355785) remove this
+private const val VOLUME_UPDATE_GRACE_PERIOD = 1000
+
+@OptIn(ExperimentalCoroutinesApi::class)
class VolumeDialogSliderViewModel
@AssistedInject
-constructor(@Assisted private val interactor: VolumeDialogSliderInteractor) {
+constructor(
+ @Assisted private val interactor: VolumeDialogSliderInteractor,
+ private val visibilityInteractor: VolumeDialogVisibilityInteractor,
+ @VolumeDialog private val coroutineScope: CoroutineScope,
+ private val systemClock: SystemClock,
+) {
+
+ private val userVolumeUpdates = MutableStateFlow<VolumeUpdate?>(null)
- val model: Flow<VolumeDialogStreamModel> = interactor.slider
+ val model: Flow<VolumeDialogStreamModel> =
+ interactor.slider
+ .filter {
+ val lastVolumeUpdateTime = userVolumeUpdates.value?.timestampMillis ?: 0
+ getTimestampMillis() - lastVolumeUpdateTime > VOLUME_UPDATE_GRACE_PERIOD
+ }
+ .stateIn(coroutineScope, SharingStarted.Eagerly, null)
+ .filterNotNull()
- fun setStreamVolume(volume: Int) {
- interactor.setStreamVolume(volume)
+ init {
+ userVolumeUpdates
+ .filterNotNull()
+ .mapLatest { volume ->
+ interactor.setStreamVolume(volume.newVolumeLevel)
+ Events.writeEvent(Events.EVENT_TOUCH_LEVEL_CHANGED, model.first().stream, volume)
+ }
+ .launchIn(coroutineScope)
}
+ fun setStreamVolume(volume: Int, fromUser: Boolean) {
+ if (fromUser) {
+ visibilityInteractor.resetDismissTimeout()
+ userVolumeUpdates.value =
+ VolumeUpdate(newVolumeLevel = volume, timestampMillis = getTimestampMillis())
+ }
+ }
+
+ private fun getTimestampMillis(): Long = systemClock.uptimeMillis()
+
@AssistedFactory
interface Factory {
fun create(interactor: VolumeDialogSliderInteractor): VolumeDialogSliderViewModel
}
+
+ private data class VolumeUpdate(val newVolumeLevel: Int, val timestampMillis: Long)
}
diff --git a/packages/SystemUI/src/com/android/systemui/volume/dialog/sliders/ui/viewmodel/VolumeDialogSlidersViewModel.kt b/packages/SystemUI/src/com/android/systemui/volume/dialog/sliders/ui/viewmodel/VolumeDialogSlidersViewModel.kt
index b5b292fa4a66..22cf89fa6bde 100644
--- a/packages/SystemUI/src/com/android/systemui/volume/dialog/sliders/ui/viewmodel/VolumeDialogSlidersViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/volume/dialog/sliders/ui/viewmodel/VolumeDialogSlidersViewModel.kt
@@ -16,6 +16,9 @@
package com.android.systemui.volume.dialog.sliders.ui.viewmodel
+import androidx.compose.runtime.getValue
+import com.android.systemui.lifecycle.ExclusiveActivatable
+import com.android.systemui.lifecycle.Hydrator
import com.android.systemui.volume.dialog.dagger.scope.VolumeDialog
import com.android.systemui.volume.dialog.sliders.domain.interactor.VolumeDialogSliderInteractor
import com.android.systemui.volume.dialog.sliders.domain.interactor.VolumeDialogSlidersInteractor
@@ -24,10 +27,9 @@ import com.android.systemui.volume.dialog.sliders.ui.VolumeDialogSliderViewBinde
import dagger.assisted.AssistedFactory
import dagger.assisted.AssistedInject
import kotlinx.coroutines.CoroutineScope
-import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.SharingStarted
+import kotlinx.coroutines.flow.StateFlow
import kotlinx.coroutines.flow.distinctUntilChanged
-import kotlinx.coroutines.flow.filterNotNull
import kotlinx.coroutines.flow.map
import kotlinx.coroutines.flow.stateIn
@@ -39,9 +41,10 @@ constructor(
private val sliderInteractorFactory: VolumeDialogSliderInteractor.Factory,
private val sliderViewModelFactory: VolumeDialogSliderViewModel.Factory,
private val sliderViewBinderFactory: VolumeDialogSliderViewBinder.Factory,
-) {
+) : ExclusiveActivatable() {
- val sliders: Flow<VolumeDialogSliderUiModel> =
+ private val hydrator = Hydrator("VolumeDialogSlidersViewModel")
+ private val slidersStateFlow: StateFlow<VolumeDialogSliderUiModel?> =
slidersInteractor.sliders
.distinctUntilChanged()
.map { slidersModel ->
@@ -52,7 +55,13 @@ constructor(
)
}
.stateIn(coroutineScope, SharingStarted.Eagerly, null)
- .filterNotNull()
+
+ val uiModel: VolumeDialogSliderUiModel? by
+ hydrator.hydratedStateOf("VolumeDialogSlidersViewModel#uiModel", slidersStateFlow)
+
+ override suspend fun onActivated(): Nothing {
+ hydrator.activate()
+ }
private fun createSliderViewBinder(type: VolumeDialogSliderType): VolumeDialogSliderViewBinder =
sliderViewBinderFactory.create {
diff --git a/packages/SystemUI/src/com/android/systemui/volume/dialog/ui/binder/VolumeDialogBinder.kt b/packages/SystemUI/src/com/android/systemui/volume/dialog/ui/binder/VolumeDialogBinder.kt
index 77733fe33275..cd535e420e18 100644
--- a/packages/SystemUI/src/com/android/systemui/volume/dialog/ui/binder/VolumeDialogBinder.kt
+++ b/packages/SystemUI/src/com/android/systemui/volume/dialog/ui/binder/VolumeDialogBinder.kt
@@ -20,13 +20,16 @@ import android.app.Dialog
import android.graphics.Color
import android.graphics.PixelFormat
import android.graphics.drawable.ColorDrawable
+import android.view.View
import android.view.ViewGroup
import android.view.Window
import android.view.WindowManager
import com.android.systemui.res.R
import com.android.systemui.volume.dialog.dagger.scope.VolumeDialog
import com.android.systemui.volume.dialog.dagger.scope.VolumeDialogScope
+import com.android.systemui.volume.dialog.ringer.ui.binder.VolumeDialogRingerViewBinder
import com.android.systemui.volume.dialog.settings.ui.binder.VolumeDialogSettingsButtonViewBinder
+import com.android.systemui.volume.dialog.sliders.ui.VolumeDialogSlidersViewBinder
import com.android.systemui.volume.dialog.ui.viewmodel.VolumeDialogGravityViewModel
import javax.inject.Inject
import kotlinx.coroutines.CoroutineScope
@@ -40,6 +43,8 @@ class VolumeDialogBinder
constructor(
@VolumeDialog private val coroutineScope: CoroutineScope,
private val volumeDialogViewBinder: VolumeDialogViewBinder,
+ private val slidersViewBinder: VolumeDialogSlidersViewBinder,
+ private val volumeDialogRingerViewBinder: VolumeDialogRingerViewBinder,
private val settingsButtonViewBinder: VolumeDialogSettingsButtonViewBinder,
private val gravityViewModel: VolumeDialogGravityViewModel,
) {
@@ -50,11 +55,12 @@ constructor(
dialog.setContentView(R.layout.volume_dialog)
dialog.setCanceledOnTouchOutside(true)
- settingsButtonViewBinder.bind(dialog.requireViewById(R.id.volume_dialog_settings))
- volumeDialogViewBinder.bind(
- dialog,
- dialog.requireViewById(R.id.volume_dialog_container),
- )
+ with(dialog.requireViewById<View>(R.id.volume_dialog_container)) {
+ volumeDialogRingerViewBinder.bind(this)
+ slidersViewBinder.bind(this)
+ settingsButtonViewBinder.bind(this)
+ volumeDialogViewBinder.bind(dialog, this)
+ }
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/wallpapers/data/repository/NoopWallpaperRepository.kt b/packages/SystemUI/src/com/android/systemui/wallpapers/data/repository/NoopWallpaperRepository.kt
index 54953c9c2574..9055d18a9f55 100644
--- a/packages/SystemUI/src/com/android/systemui/wallpapers/data/repository/NoopWallpaperRepository.kt
+++ b/packages/SystemUI/src/com/android/systemui/wallpapers/data/repository/NoopWallpaperRepository.kt
@@ -35,6 +35,4 @@ class NoopWallpaperRepository @Inject constructor() : WallpaperRepository {
override val wallpaperInfo: StateFlow<WallpaperInfo?> = MutableStateFlow(null).asStateFlow()
override val wallpaperSupportsAmbientMode = MutableStateFlow(false).asStateFlow()
override var rootView: View? = null
-
- override fun setNotificationStackAbsoluteBottom(bottom: Float) {}
}
diff --git a/packages/SystemUI/src/com/android/systemui/wallpapers/data/repository/WallpaperRepository.kt b/packages/SystemUI/src/com/android/systemui/wallpapers/data/repository/WallpaperRepository.kt
index 0f6e96066cd3..015b480eddc8 100644
--- a/packages/SystemUI/src/com/android/systemui/wallpapers/data/repository/WallpaperRepository.kt
+++ b/packages/SystemUI/src/com/android/systemui/wallpapers/data/repository/WallpaperRepository.kt
@@ -45,7 +45,6 @@ import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.SharingStarted
import kotlinx.coroutines.flow.StateFlow
import kotlinx.coroutines.flow.asStateFlow
-import kotlinx.coroutines.flow.collect
import kotlinx.coroutines.flow.combine
import kotlinx.coroutines.flow.filter
import kotlinx.coroutines.flow.map
@@ -64,12 +63,6 @@ interface WallpaperRepository {
/** Set rootView to get its windowToken afterwards */
var rootView: View?
-
- /**
- * Set bottom of notifications from notification stack, and Magic Portrait will layout base on
- * this value
- */
- fun setNotificationStackAbsoluteBottom(bottom: Float)
}
@SysUISingleton
@@ -106,7 +99,8 @@ constructor(
.filter { it.selectionStatus == SelectionStatus.SELECTION_COMPLETE }
/** The bottom of notification stack respect to the top of screen. */
- private val notificationStackAbsoluteBottom: MutableStateFlow<Float> = MutableStateFlow(0F)
+ private val notificationStackAbsoluteBottom: StateFlow<Float> =
+ keyguardRepository.notificationStackAbsoluteBottom
/** The top of shortcut respect to the top of screen. */
private val shortcutAbsoluteTop: StateFlow<Float> = keyguardRepository.shortcutAbsoluteTop
@@ -206,10 +200,6 @@ constructor(
initialValue = false,
)
- override fun setNotificationStackAbsoluteBottom(bottom: Float) {
- notificationStackAbsoluteBottom.value = bottom
- }
-
private suspend fun getWallpaper(selectedUser: SelectedUserModel): WallpaperInfo? {
return withContext(bgDispatcher) {
wallpaperManager.getWallpaperInfoForUser(selectedUser.userInfo.id)
diff --git a/packages/SystemUI/src/com/android/systemui/wallpapers/domain/interactor/WallpaperInteractor.kt b/packages/SystemUI/src/com/android/systemui/wallpapers/domain/interactor/WallpaperInteractor.kt
index fe6977c367b5..88795cada716 100644
--- a/packages/SystemUI/src/com/android/systemui/wallpapers/domain/interactor/WallpaperInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/wallpapers/domain/interactor/WallpaperInteractor.kt
@@ -21,10 +21,6 @@ import javax.inject.Inject
import kotlinx.coroutines.flow.StateFlow
class WallpaperInteractor @Inject constructor(val wallpaperRepository: WallpaperRepository) {
- fun setNotificationStackAbsoluteBottom(bottom: Float) {
- wallpaperRepository.setNotificationStackAbsoluteBottom(bottom)
- }
-
val wallpaperSupportsAmbientMode: StateFlow<Boolean> =
wallpaperRepository.wallpaperSupportsAmbientMode
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/QSPanelTest.kt b/packages/SystemUI/tests/src/com/android/systemui/qs/QSPanelTest.kt
index 8aaa121640a3..3c922dcb4fd2 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/qs/QSPanelTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/qs/QSPanelTest.kt
@@ -13,6 +13,7 @@
*/
package com.android.systemui.qs
+import android.content.res.Configuration
import android.graphics.Rect
import android.platform.test.flag.junit.FlagsParameterization
import android.testing.TestableContext
@@ -91,7 +92,9 @@ class QSPanelTest(flags: FlagsParameterization) : SysuiTestCase() {
@After
fun tearDown() {
- ViewUtils.detachView(qsPanel)
+ if (qsPanel.isAttachedToWindow) {
+ ViewUtils.detachView(qsPanel)
+ }
}
@Test
@@ -119,7 +122,7 @@ class QSPanelTest(flags: FlagsParameterization) : SysuiTestCase() {
qsPanel.tileLayout?.addTile(
QSPanelControllerBase.TileRecord(
mock(QSTile::class.java),
- QSTileViewImpl(themedContext)
+ QSTileViewImpl(themedContext),
)
)
@@ -129,7 +132,7 @@ class QSPanelTest(flags: FlagsParameterization) : SysuiTestCase() {
qsPanel.setUsingHorizontalLayout(/* horizontal */ true, mediaView, /* force */ true)
qsPanel.measure(
/* width */ View.MeasureSpec.makeMeasureSpec(3000, View.MeasureSpec.EXACTLY),
- /* height */ View.MeasureSpec.makeMeasureSpec(1000, View.MeasureSpec.EXACTLY)
+ /* height */ View.MeasureSpec.makeMeasureSpec(1000, View.MeasureSpec.EXACTLY),
)
qsPanel.layout(0, 0, qsPanel.measuredWidth, qsPanel.measuredHeight)
@@ -147,7 +150,7 @@ class QSPanelTest(flags: FlagsParameterization) : SysuiTestCase() {
val padding = 10
themedContext.orCreateTestableResources.addOverride(
R.dimen.qs_panel_padding_bottom,
- padding
+ padding,
)
qsPanel.updatePadding()
assertThat(qsPanel.paddingBottom).isEqualTo(padding)
@@ -160,7 +163,7 @@ class QSPanelTest(flags: FlagsParameterization) : SysuiTestCase() {
themedContext.orCreateTestableResources.addOverride(R.dimen.qs_panel_padding_top, padding)
themedContext.orCreateTestableResources.addOverride(
R.dimen.qs_panel_padding_top,
- paddingCombined
+ paddingCombined,
)
qsPanel.updatePadding()
@@ -267,6 +270,62 @@ class QSPanelTest(flags: FlagsParameterization) : SysuiTestCase() {
assertThat(qsPanel.tileLayout!!.maxColumns).isEqualTo(2)
}
+ @Test
+ fun noPendingConfigChangesAtBeginning() {
+ assertThat(qsPanel.hadConfigurationChangeWhileDetached()).isFalse()
+ }
+
+ @Test
+ fun configChangesWhileDetached_pendingConfigChanges() {
+ ViewUtils.detachView(qsPanel)
+
+ qsPanel.onConfigurationChanged(Configuration())
+
+ assertThat(qsPanel.hadConfigurationChangeWhileDetached()).isTrue()
+ }
+
+ @Test
+ fun configChangesWhileDetached_reattach_pendingConfigChanges() {
+ ViewUtils.detachView(qsPanel)
+
+ qsPanel.onConfigurationChanged(Configuration())
+ testableLooper.runWithLooper { ViewUtils.attachView(qsPanel) }
+
+ assertThat(qsPanel.hadConfigurationChangeWhileDetached()).isTrue()
+ }
+
+ @Test
+ fun configChangesWhileDetached_reattach_detach_pendingConfigChanges_reset() {
+ ViewUtils.detachView(qsPanel)
+
+ qsPanel.onConfigurationChanged(Configuration())
+
+ testableLooper.runWithLooper { ViewUtils.attachView(qsPanel) }
+ ViewUtils.detachView(qsPanel)
+
+ assertThat(qsPanel.hadConfigurationChangeWhileDetached()).isFalse()
+ }
+
+ @Test
+ fun configChangeWhileAttached_noPendingConfigChanges() {
+ qsPanel.onConfigurationChanged(Configuration())
+
+ assertThat(qsPanel.hadConfigurationChangeWhileDetached()).isFalse()
+ }
+
+ @Test
+ fun configChangeWhileAttachedWithPending_doesntResetPending() {
+ ViewUtils.detachView(qsPanel)
+
+ qsPanel.onConfigurationChanged(Configuration())
+
+ testableLooper.runWithLooper { ViewUtils.attachView(qsPanel) }
+
+ qsPanel.onConfigurationChanged(Configuration())
+
+ assertThat(qsPanel.hadConfigurationChangeWhileDetached()).isTrue()
+ }
+
companion object {
@Parameters(name = "{0}") @JvmStatic fun getParams() = parameterizeSceneContainerFlag()
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/NotifCollectionTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/NotifCollectionTest.java
index 2cf599a99c63..e21a005b8ada 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/NotifCollectionTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/NotifCollectionTest.java
@@ -45,6 +45,7 @@ import static org.mockito.ArgumentMatchers.anyBoolean;
import static org.mockito.ArgumentMatchers.anyInt;
import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.Mockito.clearInvocations;
+import static org.mockito.Mockito.doReturn;
import static org.mockito.Mockito.inOrder;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.never;
@@ -63,6 +64,7 @@ import android.app.NotificationChannel;
import android.app.NotificationManager;
import android.os.Handler;
import android.os.RemoteException;
+import android.platform.test.annotations.EnableFlags;
import android.service.notification.NotificationListenerService.Ranking;
import android.service.notification.NotificationListenerService.RankingMap;
import android.service.notification.StatusBarNotification;
@@ -70,13 +72,13 @@ import android.testing.AndroidTestingRunner;
import android.testing.TestableLooper;
import android.util.ArrayMap;
import android.util.ArraySet;
-import android.util.Pair;
import androidx.annotation.NonNull;
import androidx.test.filters.SmallTest;
import com.android.internal.statusbar.IStatusBarService;
import com.android.internal.statusbar.NotificationVisibility;
+import com.android.systemui.Flags;
import com.android.systemui.SysuiTestCase;
import com.android.systemui.dump.DumpManager;
import com.android.systemui.dump.LogBufferEulogizer;
@@ -129,6 +131,7 @@ public class NotifCollectionTest extends SysuiTestCase {
@Mock private GroupCoalescer mGroupCoalescer;
@Spy private RecordingCollectionListener mCollectionListener;
@Mock private CollectionReadyForBuildListener mBuildListener;
+ @Mock private NotificationDismissibilityProvider mDismissibilityProvider;
@Spy private RecordingLifetimeExtender mExtender1 = new RecordingLifetimeExtender("Extender1");
@Spy private RecordingLifetimeExtender mExtender2 = new RecordingLifetimeExtender("Extender2");
@@ -160,6 +163,7 @@ public class NotifCollectionTest extends SysuiTestCase {
allowTestableLooperAsMainThread();
when(mEulogizer.record(any(Exception.class))).thenAnswer(i -> i.getArguments()[0]);
+ doReturn(Boolean.TRUE).when(mDismissibilityProvider).isDismissable(any());
mListenerInOrder = inOrder(mCollectionListener);
@@ -172,7 +176,7 @@ public class NotifCollectionTest extends SysuiTestCase {
mBgExecutor,
mEulogizer,
mock(DumpManager.class),
- mock(NotificationDismissibilityProvider.class));
+ mDismissibilityProvider);
mCollection.attach(mGroupCoalescer);
mCollection.addCollectionListener(mCollectionListener);
mCollection.setBuildListener(mBuildListener);
@@ -1287,8 +1291,8 @@ public class NotifCollectionTest extends SysuiTestCase {
// WHEN both notifications are manually dismissed together
mCollection.dismissNotifications(
- List.of(new Pair<>(entry1, defaultStats(entry1)),
- new Pair<>(entry2, defaultStats(entry2))));
+ List.of(entryWithDefaultStats(entry1),
+ entryWithDefaultStats(entry2)));
// THEN build list is only called one time
verifyBuiltList(List.of(entry1, entry2));
@@ -1306,8 +1310,8 @@ public class NotifCollectionTest extends SysuiTestCase {
DismissedByUserStats stats1 = defaultStats(entry1);
DismissedByUserStats stats2 = defaultStats(entry2);
mCollection.dismissNotifications(
- List.of(new Pair<>(entry1, defaultStats(entry1)),
- new Pair<>(entry2, defaultStats(entry2))));
+ List.of(entryWithDefaultStats(entry1),
+ entryWithDefaultStats(entry2)));
// THEN we send the dismissals to system server
FakeExecutor.exhaustExecutors(mBgExecutor);
@@ -1338,8 +1342,8 @@ public class NotifCollectionTest extends SysuiTestCase {
// WHEN both notifications are manually dismissed together
mCollection.dismissNotifications(
- List.of(new Pair<>(entry1, defaultStats(entry1)),
- new Pair<>(entry2, defaultStats(entry2))));
+ List.of(entryWithDefaultStats(entry1),
+ entryWithDefaultStats(entry2)));
// THEN the entries are marked as dismissed
assertEquals(DISMISSED, entry1.getDismissState());
@@ -1363,8 +1367,8 @@ public class NotifCollectionTest extends SysuiTestCase {
// WHEN both notifications are manually dismissed together
mCollection.dismissNotifications(
- List.of(new Pair<>(entry1, defaultStats(entry1)),
- new Pair<>(entry2, defaultStats(entry2))));
+ List.of(entryWithDefaultStats(entry1),
+ entryWithDefaultStats(entry2)));
// THEN all interceptors get checked
verify(mInterceptor1).shouldInterceptDismissal(entry1);
@@ -1379,6 +1383,43 @@ public class NotifCollectionTest extends SysuiTestCase {
}
@Test
+ @EnableFlags(Flags.FLAG_NOTIFICATIONS_DISMISS_PRUNED_SUMMARIES)
+ public void testDismissNotificationsIncludesPrunedParents() {
+ // GIVEN a collection with 2 groups; one has a single child, one has two.
+ mCollection.addNotificationDismissInterceptor(mInterceptor1);
+
+ NotifEvent notif1summary = mNoMan.postNotif(
+ buildNotif(TEST_PACKAGE, 1, "notif1summary").setGroup(mContext, "group1")
+ .setGroupSummary(mContext, true));
+ NotifEvent notif1child = mNoMan.postNotif(
+ buildNotif(TEST_PACKAGE, 1, "notif1child").setGroup(mContext, "group1"));
+ NotifEvent notif2summary = mNoMan.postNotif(
+ buildNotif(TEST_PACKAGE2, 2, "notif2summary").setGroup(mContext, "group2")
+ .setGroupSummary(mContext, true));
+ NotifEvent notif2child1 = mNoMan.postNotif(
+ buildNotif(TEST_PACKAGE2, 2, "notif2child1").setGroup(mContext, "group2"));
+ NotifEvent notif2child2 = mNoMan.postNotif(
+ buildNotif(TEST_PACKAGE2, 2, "notif2child2").setGroup(mContext, "group2"));
+ NotificationEntry entry1summary = mCollectionListener.getEntry(notif1summary.key);
+ NotificationEntry entry1child = mCollectionListener.getEntry(notif1child.key);
+ NotificationEntry entry2summary = mCollectionListener.getEntry(notif2summary.key);
+ NotificationEntry entry2child1 = mCollectionListener.getEntry(notif2child1.key);
+ NotificationEntry entry2child2 = mCollectionListener.getEntry(notif2child2.key);
+
+ // WHEN one child from each group are manually dismissed together
+ mCollection.dismissNotifications(
+ List.of(entryWithDefaultStats(entry1child),
+ entryWithDefaultStats(entry2child1)));
+
+ // THEN the summary for the singleton child is dismissed, but not the other summary
+ verify(mInterceptor1).shouldInterceptDismissal(entry1summary);
+ verify(mInterceptor1).shouldInterceptDismissal(entry1child);
+ verify(mInterceptor1, never()).shouldInterceptDismissal(entry2summary);
+ verify(mInterceptor1).shouldInterceptDismissal(entry2child1);
+ verify(mInterceptor1, never()).shouldInterceptDismissal(entry2child2);
+ }
+
+ @Test
public void testDismissAllNotificationsCallsRebuildOnce() {
// GIVEN a collection with a couple notifications
NotifEvent notif1 = mNoMan.postNotif(buildNotif(TEST_PACKAGE, 47, "myTag"));
@@ -1764,6 +1805,10 @@ public class NotifCollectionTest extends SysuiTestCase {
NotificationVisibility.obtain(entry.getKey(), 7, 2, true));
}
+ private static EntryWithDismissStats entryWithDefaultStats(NotificationEntry entry) {
+ return new EntryWithDismissStats(entry, defaultStats(entry));
+ }
+
private CollectionEvent postNotif(NotificationEntryBuilder builder) {
clearInvocations(mCollectionListener);
NotifEvent rawEvent = mNoMan.postNotif(builder);
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/data/repository/FakeKeyguardRepository.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/data/repository/FakeKeyguardRepository.kt
index 08786495eca4..693ec7954d70 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/data/repository/FakeKeyguardRepository.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/data/repository/FakeKeyguardRepository.kt
@@ -131,6 +131,10 @@ class FakeKeyguardRepository @Inject constructor() : KeyguardRepository {
override val shortcutAbsoluteTop: StateFlow<Float>
get() = _shortcutAbsoluteTop.asStateFlow()
+ private val _notificationStackAbsoluteBottom = MutableStateFlow(0F)
+ override val notificationStackAbsoluteBottom: StateFlow<Float>
+ get() = _notificationStackAbsoluteBottom.asStateFlow()
+
private val _isKeyguardEnabled = MutableStateFlow(true)
override val isKeyguardEnabled: StateFlow<Boolean> = _isKeyguardEnabled.asStateFlow()
@@ -294,6 +298,10 @@ class FakeKeyguardRepository @Inject constructor() : KeyguardRepository {
_shortcutAbsoluteTop.value = top
}
+ override fun setNotificationStackAbsoluteBottom(bottom: Float) {
+ _notificationStackAbsoluteBottom.value = bottom
+ }
+
override fun setCanIgnoreAuthAndReturnToGone(canWake: Boolean) {
_canIgnoreAuthAndReturnToGone.value = canWake
}
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/data/repository/FakeKeyguardTransitionRepository.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/data/repository/FakeKeyguardTransitionRepository.kt
index 70b4f79131a7..4976cc26068a 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/data/repository/FakeKeyguardTransitionRepository.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/data/repository/FakeKeyguardTransitionRepository.kt
@@ -18,6 +18,7 @@
package com.android.systemui.keyguard.data.repository
import android.annotation.FloatRange
+import com.android.systemui.Flags.transitionRaceCondition
import com.android.systemui.dagger.SysUISingleton
import com.android.systemui.keyguard.shared.model.KeyguardState
import com.android.systemui.keyguard.shared.model.TransitionInfo
@@ -88,6 +89,13 @@ class FakeKeyguardTransitionRepository(
)
)
override var currentTransitionInfoInternal = _currentTransitionInfo.asStateFlow()
+ override var currentTransitionInfo =
+ TransitionInfo(
+ ownerName = "",
+ from = KeyguardState.OFF,
+ to = KeyguardState.LOCKSCREEN,
+ animator = null,
+ )
init {
// Seed with a FINISHED transition in OFF, same as the real repository.
@@ -261,8 +269,13 @@ class FakeKeyguardTransitionRepository(
validateStep: Boolean = true,
) {
if (step.transitionState == TransitionState.STARTED) {
- _currentTransitionInfo.value =
- TransitionInfo(from = step.from, to = step.to, animator = null, ownerName = "")
+ if (transitionRaceCondition()) {
+ currentTransitionInfo =
+ TransitionInfo(from = step.from, to = step.to, animator = null, ownerName = "")
+ } else {
+ _currentTransitionInfo.value =
+ TransitionInfo(from = step.from, to = step.to, animator = null, ownerName = "")
+ }
}
_transitions.replayCache.last().let { lastStep ->
@@ -308,7 +321,11 @@ class FakeKeyguardTransitionRepository(
}
override suspend fun startTransition(info: TransitionInfo): UUID? {
- _currentTransitionInfo.value = info
+ if (transitionRaceCondition()) {
+ currentTransitionInfo = info
+ } else {
+ _currentTransitionInfo.value = info
+ }
if (sendTransitionStepsOnStartTransition) {
sendTransitionSteps(from = info.from, to = info.to, testScope = testScope)
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/domain/interactor/FromGoneTransitionInteractorKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/domain/interactor/FromGoneTransitionInteractorKosmos.kt
index 700d7e9c5608..ff0f13e7111f 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/domain/interactor/FromGoneTransitionInteractorKosmos.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/domain/interactor/FromGoneTransitionInteractorKosmos.kt
@@ -17,8 +17,6 @@
package com.android.systemui.keyguard.domain.interactor
import com.android.systemui.communal.domain.interactor.communalSceneInteractor
-import com.android.systemui.keyguard.data.repository.biometricSettingsRepository
-import com.android.systemui.keyguard.data.repository.keyguardRepository
import com.android.systemui.keyguard.data.repository.keyguardTransitionRepository
import com.android.systemui.kosmos.Kosmos
import com.android.systemui.kosmos.applicationCoroutineScope
@@ -39,8 +37,6 @@ val Kosmos.fromGoneTransitionInteractor by
powerInteractor = powerInteractor,
communalSceneInteractor = communalSceneInteractor,
keyguardOcclusionInteractor = keyguardOcclusionInteractor,
- biometricSettingsRepository = biometricSettingsRepository,
- keyguardRepository = keyguardRepository,
- keyguardEnabledInteractor = keyguardEnabledInteractor,
+ keyguardLockWhileAwakeInteractor = keyguardLockWhileAwakeInteractor,
)
}
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/domain/interactor/KeyguardLockWhileAwakeInteractorKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/domain/interactor/KeyguardLockWhileAwakeInteractorKosmos.kt
new file mode 100644
index 000000000000..39236c7c9fae
--- /dev/null
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/domain/interactor/KeyguardLockWhileAwakeInteractorKosmos.kt
@@ -0,0 +1,28 @@
+/*
+ * 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.
+ */
+
+package com.android.systemui.keyguard.domain.interactor
+
+import com.android.systemui.keyguard.data.repository.biometricSettingsRepository
+import com.android.systemui.kosmos.Kosmos
+
+val Kosmos.keyguardLockWhileAwakeInteractor by
+ Kosmos.Fixture {
+ KeyguardLockWhileAwakeInteractor(
+ biometricSettingsRepository = biometricSettingsRepository,
+ keyguardEnabledInteractor = keyguardEnabledInteractor,
+ )
+ }
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/plugins/FakeVolumeDialogController.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/plugins/FakeVolumeDialogController.kt
index b45120ee0aba..43eb93e4dd53 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/plugins/FakeVolumeDialogController.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/plugins/FakeVolumeDialogController.kt
@@ -43,30 +43,36 @@ class FakeVolumeDialogController(private val audioManager: AudioManager) : Volum
private var state = VolumeDialogController.State()
override fun setActiveStream(stream: Int) {
- // ensure streamState existence for the active stream
- state.states.getOrElse(stream) {
- VolumeDialogController.StreamState().also { streamState ->
- state.states.put(stream, streamState)
+ updateState {
+ // ensure streamState existence for the active stream`
+ states.getOrElse(stream) {
+ VolumeDialogController.StreamState().also { streamState ->
+ state.states.put(stream, streamState)
+ }
}
+ activeStream = stream
}
- state.activeStream = stream
}
override fun setStreamVolume(stream: Int, userLevel: Int) {
- val streamState =
- state.states.getOrElse(stream) {
- VolumeDialogController.StreamState().also { streamState ->
- state.states.put(stream, streamState)
+ updateState {
+ val streamState =
+ states.getOrElse(stream) {
+ VolumeDialogController.StreamState().also { streamState ->
+ states.put(stream, streamState)
+ }
}
- }
- streamState.level = userLevel.coerceIn(streamState.levelMin, streamState.levelMax)
+ streamState.level = userLevel.coerceIn(streamState.levelMin, streamState.levelMax)
+ }
}
override fun setRingerMode(ringerModeNormal: Int, external: Boolean) {
- if (external) {
- state.ringerModeExternal = ringerModeNormal
- } else {
- state.ringerModeInternal = ringerModeNormal
+ updateState {
+ if (external) {
+ ringerModeExternal = ringerModeNormal
+ } else {
+ ringerModeInternal = ringerModeNormal
+ }
}
}
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/shade/domain/startable/ShadeStartableKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/shade/domain/startable/ShadeStartableKosmos.kt
index a1f157f13210..10534a02bfe7 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/shade/domain/startable/ShadeStartableKosmos.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/shade/domain/startable/ShadeStartableKosmos.kt
@@ -40,7 +40,7 @@ val Kosmos.shadeExpansionStateManager by Fixture { ShadeExpansionStateManager()
val Kosmos.shadeStartable by Fixture {
ShadeStartable(
applicationScope = applicationCoroutineScope,
- applicationContext = applicationContext,
+ context = applicationContext,
touchLog = mock<LogBuffer>(),
configurationRepository = configurationRepository,
shadeRepository = shadeRepository,
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/notification/stack/domain/interactor/SharedNotificationContainerInteractorKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/notification/stack/domain/interactor/SharedNotificationContainerInteractorKosmos.kt
index 83fc3e9f0c58..b1e9d89dfd42 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/notification/stack/domain/interactor/SharedNotificationContainerInteractorKosmos.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/notification/stack/domain/interactor/SharedNotificationContainerInteractorKosmos.kt
@@ -23,7 +23,6 @@ import com.android.systemui.common.ui.domain.interactor.configurationInteractor
import com.android.systemui.deviceentry.domain.interactor.deviceEntryUdfpsInteractor
import com.android.systemui.keyguard.domain.interactor.keyguardInteractor
import com.android.systemui.kosmos.Kosmos
-import com.android.systemui.shade.domain.interactor.shadeInteractor
import com.android.systemui.shade.largeScreenHeaderHelper
import com.android.systemui.statusbar.policy.splitShadeStateController
import kotlinx.coroutines.ExperimentalCoroutinesApi
@@ -33,7 +32,6 @@ val Kosmos.sharedNotificationContainerInteractor by
SharedNotificationContainerInteractor(
context = applicationContext,
splitShadeStateController = { splitShadeStateController },
- shadeInteractor = { shadeInteractor },
configurationInteractor = configurationInteractor,
keyguardInteractor = keyguardInteractor,
deviceEntryUdfpsInteractor = deviceEntryUdfpsInteractor,
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/notification/stack/ui/viewmodel/SharedNotificationContainerViewModelKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/notification/stack/ui/viewmodel/SharedNotificationContainerViewModelKosmos.kt
index a25a3c0c2044..7fbf4e495feb 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/notification/stack/ui/viewmodel/SharedNotificationContainerViewModelKosmos.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/notification/stack/ui/viewmodel/SharedNotificationContainerViewModelKosmos.kt
@@ -16,6 +16,8 @@
package com.android.systemui.statusbar.notification.stack.ui.viewmodel
+import android.content.applicationContext
+import com.android.systemui.common.ui.domain.interactor.configurationInteractor
import com.android.systemui.communal.domain.interactor.communalSceneInteractor
import com.android.systemui.dump.dumpManager
import com.android.systemui.keyguard.domain.interactor.keyguardInteractor
@@ -49,6 +51,7 @@ import com.android.systemui.kosmos.Kosmos
import com.android.systemui.kosmos.Kosmos.Fixture
import com.android.systemui.kosmos.applicationCoroutineScope
import com.android.systemui.shade.domain.interactor.shadeInteractor
+import com.android.systemui.shade.largeScreenHeaderHelper
import com.android.systemui.statusbar.notification.stack.domain.interactor.headsUpNotificationInteractor
import com.android.systemui.statusbar.notification.stack.domain.interactor.notificationStackAppearanceInteractor
import com.android.systemui.statusbar.notification.stack.domain.interactor.sharedNotificationContainerInteractor
@@ -61,6 +64,8 @@ val Kosmos.sharedNotificationContainerViewModel by Fixture {
interactor = sharedNotificationContainerInteractor,
dumpManager = dumpManager,
applicationScope = applicationCoroutineScope,
+ context = applicationContext,
+ configurationInteractor = configurationInteractor,
keyguardInteractor = keyguardInteractor,
keyguardTransitionInteractor = keyguardTransitionInteractor,
shadeInteractor = shadeInteractor,
@@ -94,6 +99,7 @@ val Kosmos.sharedNotificationContainerViewModel by Fixture {
aodBurnInViewModel = aodBurnInViewModel,
communalSceneInteractor = communalSceneInteractor,
headsUpNotificationInteractor = { headsUpNotificationInteractor },
+ largeScreenHeaderHelperLazy = { largeScreenHeaderHelper },
unfoldTransitionInteractor = unfoldTransitionInteractor,
)
}
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/volume/dialog/sliders/domain/interactor/VolumeDialogSliderInteractorKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/volume/dialog/sliders/domain/interactor/VolumeDialogSliderInteractorKosmos.kt
new file mode 100644
index 000000000000..423100a1addf
--- /dev/null
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/volume/dialog/sliders/domain/interactor/VolumeDialogSliderInteractorKosmos.kt
@@ -0,0 +1,31 @@
+/*
+ * 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.
+ */
+
+package com.android.systemui.volume.dialog.sliders.domain.interactor
+
+import com.android.systemui.kosmos.Kosmos
+import com.android.systemui.plugins.volumeDialogController
+import com.android.systemui.volume.dialog.domain.interactor.volumeDialogStateInteractor
+import com.android.systemui.volume.dialog.sliders.domain.model.volumeDialogSliderType
+
+val Kosmos.volumeDialogSliderInteractor: VolumeDialogSliderInteractor by
+ Kosmos.Fixture {
+ VolumeDialogSliderInteractor(
+ volumeDialogSliderType,
+ volumeDialogStateInteractor,
+ volumeDialogController,
+ )
+ }
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/volume/dialog/sliders/domain/model/VolumeDialogSliderTypeKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/volume/dialog/sliders/domain/model/VolumeDialogSliderTypeKosmos.kt
new file mode 100644
index 000000000000..cc8c1ea3fc75
--- /dev/null
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/volume/dialog/sliders/domain/model/VolumeDialogSliderTypeKosmos.kt
@@ -0,0 +1,23 @@
+/*
+ * 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.
+ */
+
+package com.android.systemui.volume.dialog.sliders.domain.model
+
+import android.media.AudioManager
+import com.android.systemui.kosmos.Kosmos
+
+var Kosmos.volumeDialogSliderType: VolumeDialogSliderType by
+ Kosmos.Fixture { VolumeDialogSliderType.Stream(AudioManager.STREAM_SYSTEM) }
diff --git a/ravenwood/junit-impl-src/android/platform/test/ravenwood/RavenwoodAwareTestRunnerHook.java b/ravenwood/junit-impl-src/android/platform/test/ravenwood/RavenwoodAwareTestRunnerHook.java
index 478bead1354f..e0f9ec94a819 100644
--- a/ravenwood/junit-impl-src/android/platform/test/ravenwood/RavenwoodAwareTestRunnerHook.java
+++ b/ravenwood/junit-impl-src/android/platform/test/ravenwood/RavenwoodAwareTestRunnerHook.java
@@ -25,6 +25,7 @@ import android.util.Log;
import androidx.test.platform.app.InstrumentationRegistry;
+import org.junit.AssumptionViolatedException;
import org.junit.runner.Description;
import org.junit.runners.model.TestClass;
@@ -134,8 +135,17 @@ public class RavenwoodAwareTestRunnerHook {
if (scope == Scope.Instance && order == Order.Outer) {
// End of a test method.
runner.mState.exitTestMethod();
- RavenwoodTestStats.getInstance().onTestFinished(classDescription, description,
- th == null ? Result.Passed : Result.Failed);
+
+ final Result result;
+ if (th == null) {
+ result = Result.Passed;
+ } else if (th instanceof AssumptionViolatedException) {
+ result = Result.Skipped;
+ } else {
+ result = Result.Failed;
+ }
+
+ RavenwoodTestStats.getInstance().onTestFinished(classDescription, description, result);
}
// If RUN_DISABLED_TESTS is set, and the method did _not_ throw, make it an error.
diff --git a/ravenwood/junit-impl-src/android/platform/test/ravenwood/RavenwoodTestStats.java b/ravenwood/junit-impl-src/android/platform/test/ravenwood/RavenwoodTestStats.java
index b4b871524291..016de8e45291 100644
--- a/ravenwood/junit-impl-src/android/platform/test/ravenwood/RavenwoodTestStats.java
+++ b/ravenwood/junit-impl-src/android/platform/test/ravenwood/RavenwoodTestStats.java
@@ -90,9 +90,7 @@ public class RavenwoodTestStats {
// Create the "latest" symlink.
Path symlink = Paths.get(tmpdir, basename + "latest.csv");
try {
- if (Files.exists(symlink)) {
- Files.delete(symlink);
- }
+ Files.deleteIfExists(symlink);
Files.createSymbolicLink(symlink, Paths.get(mOutputFile.getName()));
} catch (IOException e) {
diff --git a/ravenwood/scripts/ravenwood-test-summary b/ravenwood/scripts/ravenwood-test-summary
new file mode 100755
index 000000000000..602fbff31ea3
--- /dev/null
+++ b/ravenwood/scripts/ravenwood-test-summary
@@ -0,0 +1,75 @@
+#!/usr/bin/env python3
+#
+# 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.
+
+'''
+Print the latest Ravenwood test execution summary
+
+Usage: /ravenwood-test-summary
+
+Example output:
+Module Passed Failed Skipped
+android.test.mock.ravenwood.tests 2 0 0
+CarLibHostUnitTest 565 0 7
+CarServiceHostUnitTest 364 0 0
+CtsAccountManagerTestCasesRavenwood 4 0 0
+CtsAppTestCasesRavenwood 21 0 0
+
+Description:
+This script finds all the test execution result from /tmp/Ravenwood-stats*,
+and shows per-module summary.
+'''
+
+import csv
+import glob
+import sys
+
+# Find the latest stats files.
+stats_files = glob.glob('/tmp/Ravenwood-stats_*_latest.csv')
+
+if len(stats_files) == 0:
+ print("No log files found.", file=sys.stderr)
+ exit(1)
+
+
+def parse_stats(file, result):
+ module = '(unknwon)'
+ passed = 0
+ failed = 0
+ skipped = 0
+ with open(file) as csvfile:
+ reader = csv.reader(csvfile, delimiter=',')
+
+
+ for i, row in enumerate(reader):
+ if i == 0: continue # Skip header line
+ module = row[0]
+ passed += int(row[3])
+ failed += int(row[4])
+ skipped += int(row[5])
+
+ result[module] = (passed, failed, skipped)
+
+
+result = {}
+
+for file in stats_files:
+ parse_stats(file, result)
+
+print('%-60s %8s %8s %8s' % ("Module", "Passed", "Failed", "Skipped"))
+
+for module in sorted(result.keys(), key=str.casefold):
+ r = result[module]
+ print('%-60s %8d %8d %8d' % (module, r[0], r[1], r[2]))
diff --git a/services/core/java/com/android/server/SystemConfig.java b/services/core/java/com/android/server/SystemConfig.java
index 6a1e319b4039..1082e6214935 100644
--- a/services/core/java/com/android/server/SystemConfig.java
+++ b/services/core/java/com/android/server/SystemConfig.java
@@ -670,6 +670,17 @@ public class SystemConfig {
}
private void readAllPermissions() {
+ readAllPermissionsFromXml();
+ readAllPermissionsFromEnvironment();
+
+ // Apply global feature removal last, after all features have been read.
+ // This only needs to happen once.
+ for (String featureName : mUnavailableFeatures) {
+ removeFeature(featureName);
+ }
+ }
+
+ private void readAllPermissionsFromXml() {
final XmlPullParser parser = Xml.newPullParser();
// Read configuration from system
@@ -1730,7 +1741,13 @@ public class SystemConfig {
} finally {
IoUtils.closeQuietly(permReader);
}
+ }
+ // Add features or permission dependent on global system properties (as
+ // opposed to XML permission files).
+ // This only needs to be called once after all features have been parsed
+ // from various partition/apex sources.
+ private void readAllPermissionsFromEnvironment() {
// Some devices can be field-converted to FBE, so offer to splice in
// those features if not already defined by the static config
if (StorageManager.isFileEncrypted()) {
@@ -1771,10 +1788,6 @@ public class SystemConfig {
addFeature(PackageManager.FEATURE_EROFS_LEGACY, 0);
}
}
-
- for (String featureName : mUnavailableFeatures) {
- removeFeature(featureName);
- }
}
private @Nullable SignedPackage parseEnhancedConfirmationTrustedPackage(XmlPullParser parser,
diff --git a/services/core/java/com/android/server/accounts/AccountManagerService.java b/services/core/java/com/android/server/accounts/AccountManagerService.java
index 0ca3b56486e3..679c7ac3ceac 100644
--- a/services/core/java/com/android/server/accounts/AccountManagerService.java
+++ b/services/core/java/com/android/server/accounts/AccountManagerService.java
@@ -112,6 +112,7 @@ import com.android.internal.messages.nano.SystemMessageProto.SystemMessage;
import com.android.internal.notification.SystemNotificationChannels;
import com.android.internal.util.ArrayUtils;
import com.android.internal.util.DumpUtils;
+import com.android.internal.util.FrameworkStatsLog;
import com.android.internal.util.IndentingPrintWriter;
import com.android.internal.util.Preconditions;
import com.android.modules.expresslog.Histogram;
@@ -1226,7 +1227,17 @@ public class AccountManagerService
// been re-enabled (after being updated for example), then we just overwrite the old
// values.
for (Entry<String, Integer> entry : knownAuth.entrySet()) {
- accountsDb.insertOrReplaceMetaAuthTypeAndUid(entry.getKey(), entry.getValue());
+ String type = entry.getKey();
+ Integer newUid = entry.getValue();
+ if (!Objects.equals(metaAuthUid.get(type), newUid)) {
+ FrameworkStatsLog.write(
+ FrameworkStatsLog.ACCOUNT_MANAGER_EVENT,
+ type,
+ newUid,
+ FrameworkStatsLog
+ .ACCOUNT_MANAGER_EVENT__EVENT_TYPE__AUTHENTICATOR_ADDED);
+ }
+ accountsDb.insertOrReplaceMetaAuthTypeAndUid(type, newUid);
}
final Map<Long, Account> accountsMap = accountsDb.findAllDeAccounts();
@@ -1945,6 +1956,11 @@ public class AccountManagerService
}
accounts.accountsDb.setTransactionSuccessful();
+ FrameworkStatsLog.write(
+ FrameworkStatsLog.ACCOUNT_MANAGER_EVENT,
+ account.type,
+ callingUid,
+ FrameworkStatsLog.ACCOUNT_MANAGER_EVENT__EVENT_TYPE__ACCOUNT_ADDED);
logRecord(AccountsDb.DEBUG_ACTION_ACCOUNT_ADD, AccountsDb.TABLE_ACCOUNTS,
accountId,
accounts, callingUid);
@@ -2544,6 +2560,11 @@ public class AccountManagerService
}
String action = userUnlocked ? AccountsDb.DEBUG_ACTION_ACCOUNT_REMOVE
: AccountsDb.DEBUG_ACTION_ACCOUNT_REMOVE_DE;
+ FrameworkStatsLog.write(
+ FrameworkStatsLog.ACCOUNT_MANAGER_EVENT,
+ account.type,
+ callingUid,
+ FrameworkStatsLog.ACCOUNT_MANAGER_EVENT__EVENT_TYPE__ACCOUNT_REMOVED);
logRecord(action, AccountsDb.TABLE_ACCOUNTS, accountId, accounts);
}
}
diff --git a/services/core/java/com/android/server/am/ActivityManagerService.java b/services/core/java/com/android/server/am/ActivityManagerService.java
index 3e03045c06ce..87ce649474a5 100644
--- a/services/core/java/com/android/server/am/ActivityManagerService.java
+++ b/services/core/java/com/android/server/am/ActivityManagerService.java
@@ -2772,8 +2772,12 @@ public class ActivityManagerService extends IActivityManager.Stub
// Add common services.
// IMPORTANT: Before adding services here, make sure ephemeral apps can access them too.
// Enable the check in ApplicationThread.bindApplication() to make sure.
- if (!android.server.Flags.removeJavaServiceManagerCache()) {
- addServiceToMap(mAppBindArgs, "permissionmgr");
+
+ // Removing User Service and App Ops Service from cache breaks boot for auto.
+ // Removing permissionmgr breaks tests for Android Auto due to SELinux restrictions.
+ // TODO: fix SELinux restrictions and remove caching for Android Auto.
+ if (mContext.getPackageManager().hasSystemFeature(PackageManager.FEATURE_AUTOMOTIVE)
+ || !android.server.Flags.removeJavaServiceManagerCache()) {
addServiceToMap(mAppBindArgs, Context.ALARM_SERVICE);
addServiceToMap(mAppBindArgs, Context.DISPLAY_SERVICE);
addServiceToMap(mAppBindArgs, Context.NETWORKMANAGEMENT_SERVICE);
@@ -2790,16 +2794,16 @@ public class ActivityManagerService extends IActivityManager.Stub
addServiceToMap(mAppBindArgs, Context.POWER_SERVICE);
addServiceToMap(mAppBindArgs, "mount");
addServiceToMap(mAppBindArgs, Context.PLATFORM_COMPAT_SERVICE);
+ addServiceToMap(mAppBindArgs, "permissionmgr");
+ addServiceToMap(mAppBindArgs, Context.APP_OPS_SERVICE);
+ addServiceToMap(mAppBindArgs, Context.USER_SERVICE);
}
// See b/79378449
// Getting the window service and package service binder from servicemanager
// is blocked for Apps. However they are necessary for apps.
- // Removing User Service and App Ops Service from cache breaks boot for auto.
// TODO: remove exception
- addServiceToMap(mAppBindArgs, Context.APP_OPS_SERVICE);
addServiceToMap(mAppBindArgs, "package");
addServiceToMap(mAppBindArgs, Context.WINDOW_SERVICE);
- addServiceToMap(mAppBindArgs, Context.USER_SERVICE);
}
return mAppBindArgs;
}
@@ -5551,6 +5555,8 @@ public class ActivityManagerService extends IActivityManager.Stub
if (target instanceof PendingIntentRecord) {
final PendingIntentRecord originalRecord = (PendingIntentRecord) target;
+ addCreatorToken(intent, originalRecord.getPackageName());
+
// In multi-display scenarios, there can be background users who execute the
// PendingIntent. In these scenarios, we don't want to use the foreground user as the
// current user.
diff --git a/services/core/java/com/android/server/am/OomAdjuster.java b/services/core/java/com/android/server/am/OomAdjuster.java
index 8d16eb5fc95a..42966a347aec 100644
--- a/services/core/java/com/android/server/am/OomAdjuster.java
+++ b/services/core/java/com/android/server/am/OomAdjuster.java
@@ -78,7 +78,6 @@ import static android.os.Process.THREAD_GROUP_RESTRICTED;
import static android.os.Process.THREAD_GROUP_TOP_APP;
import static android.os.Process.THREAD_PRIORITY_DISPLAY;
import static android.os.Process.THREAD_PRIORITY_TOP_APP_BOOST;
-import static android.os.Process.setProcessGroup;
import static com.android.server.am.ActivityManagerDebugConfig.DEBUG_ALL;
import static com.android.server.am.ActivityManagerDebugConfig.DEBUG_BACKUP;
@@ -526,7 +525,7 @@ public class OomAdjuster {
+ msg.obj + " to " + group);
}
try {
- setProcessGroup(pid, group);
+ android.os.Process.setProcessGroup(pid, group);
} catch (Exception e) {
if (DEBUG_ALL) {
Slog.w(TAG, "Failed setting process group of " + pid + " to " + group, e);
@@ -544,6 +543,11 @@ public class OomAdjuster {
/ CACHED_APP_IMPORTANCE_LEVELS;
}
+ void setProcessGroup(int pid, int group, String processName) {
+ mProcessGroupHandler.sendMessage(mProcessGroupHandler.obtainMessage(
+ 0 /* unused */, pid, group, processName));
+ }
+
void initSettings() {
mCachedAppOptimizer.init();
mCacheOomRanker.init(ActivityThread.currentApplication().getMainExecutor());
@@ -3491,8 +3495,8 @@ public class OomAdjuster {
processGroup = THREAD_GROUP_DEFAULT;
break;
}
- mProcessGroupHandler.sendMessage(mProcessGroupHandler.obtainMessage(
- 0 /* unused */, app.getPid(), processGroup, app.processName));
+ setProcessGroup(app.getPid(), processGroup, app.processName);
+ mService.mPhantomProcessList.setProcessGroupForPhantomProcessOfApp(app, processGroup);
try {
final int renderThreadTid = app.getRenderThreadTid();
if (curSchedGroup == SCHED_GROUP_TOP_APP) {
diff --git a/services/core/java/com/android/server/am/PendingIntentRecord.java b/services/core/java/com/android/server/am/PendingIntentRecord.java
index 3fb06a75d79f..4331e0530d4c 100644
--- a/services/core/java/com/android/server/am/PendingIntentRecord.java
+++ b/services/core/java/com/android/server/am/PendingIntentRecord.java
@@ -629,7 +629,8 @@ public final class PendingIntentRecord extends IIntentSender.Stub {
allIntents, allResolvedTypes, resultTo, mergedOptions, userId,
false /* validateIncomingUser */,
this /* originatingPendingIntent */,
- getBackgroundStartPrivilegesForActivitySender(allowlistToken));
+ getBackgroundStartPrivilegesForActivitySender(allowlistToken)
+ .allowsBackgroundActivityStarts());
} else {
res = controller.mAtmInternal.startActivityInPackage(uid, callingPid,
callingUid, key.packageName, key.featureId, finalIntent,
@@ -637,7 +638,8 @@ public final class PendingIntentRecord extends IIntentSender.Stub {
mergedOptions, userId, null, "PendingIntentRecord",
false /* validateIncomingUser */,
this /* originatingPendingIntent */,
- getBackgroundStartPrivilegesForActivitySender(allowlistToken));
+ getBackgroundStartPrivilegesForActivitySender(allowlistToken)
+ .allowsBackgroundActivityStarts());
}
} catch (RuntimeException e) {
Slog.w(TAG, "Unable to send startActivity intent", e);
diff --git a/services/core/java/com/android/server/am/PhantomProcessList.java b/services/core/java/com/android/server/am/PhantomProcessList.java
index 2ec1aedd18f9..bfdced77a1b3 100644
--- a/services/core/java/com/android/server/am/PhantomProcessList.java
+++ b/services/core/java/com/android/server/am/PhantomProcessList.java
@@ -514,6 +514,15 @@ public final class PhantomProcessList {
app.killLocked("Caused by child process: " + msg, reasonCode, subReason, true);
}
+ @GuardedBy("mLock")
+ private SparseArray<PhantomProcessRecord> getPhantomProcessOfAppLocked(ProcessRecord app) {
+ int index = mAppPhantomProcessMap.indexOfKey(app.getPid());
+ if (index >= 0) {
+ return mAppPhantomProcessMap.valueAt(index);
+ }
+ return null;
+ }
+
/**
* Iterate all phantom process belonging to the given app, and invokve callback
* for each of them.
@@ -521,20 +530,35 @@ public final class PhantomProcessList {
void forEachPhantomProcessOfApp(final ProcessRecord app,
final Function<PhantomProcessRecord, Boolean> callback) {
synchronized (mLock) {
- int index = mAppPhantomProcessMap.indexOfKey(app.getPid());
- if (index >= 0) {
- final SparseArray<PhantomProcessRecord> array =
- mAppPhantomProcessMap.valueAt(index);
- for (int i = array.size() - 1; i >= 0; i--) {
- final PhantomProcessRecord r = array.valueAt(i);
- if (!callback.apply(r)) {
- break;
- }
+ final SparseArray<PhantomProcessRecord> array = getPhantomProcessOfAppLocked(app);
+ if (array == null) {
+ return;
+ }
+ for (int i = array.size() - 1; i >= 0; i--) {
+ final PhantomProcessRecord r = array.valueAt(i);
+ if (!callback.apply(r)) {
+ break;
}
}
}
}
+ /**
+ * Set process group of phantom process belonging to the given app.
+ */
+ void setProcessGroupForPhantomProcessOfApp(final ProcessRecord app, final int group) {
+ synchronized (mLock) {
+ final SparseArray<PhantomProcessRecord> array = getPhantomProcessOfAppLocked(app);
+ if (array == null) {
+ return;
+ }
+ for (int i = array.size() - 1; i >= 0; i--) {
+ final PhantomProcessRecord r = array.valueAt(i);
+ mService.mOomAdjuster.setProcessGroup(r.mPid, group, r.mProcessName);
+ }
+ }
+ }
+
@GuardedBy("tracker")
void updateProcessCpuStatesLocked(ProcessCpuTracker tracker) {
synchronized (mLock) {
diff --git a/services/core/java/com/android/server/audio/AudioService.java b/services/core/java/com/android/server/audio/AudioService.java
index bfef6855fe7e..353cfbe773dc 100644
--- a/services/core/java/com/android/server/audio/AudioService.java
+++ b/services/core/java/com/android/server/audio/AudioService.java
@@ -1608,7 +1608,14 @@ public class AudioService extends IAudioService.Stub
if (dev == AudioSystem.DEVICE_OUT_BLUETOOTH_A2DP) {
enabled = mAvrcpAbsVolSupported;
}
- mAudioSystem.setDeviceAbsoluteVolumeEnabled(dev, /*address=*/"", enabled, stream);
+ final int result = mAudioSystem.setDeviceAbsoluteVolumeEnabled(dev, /*address=*/"",
+ enabled, stream);
+ if (result != AudioSystem.AUDIO_STATUS_OK) {
+ sVolumeLogger.enqueueAndSlog(
+ new VolumeEvent(VolumeEvent.VOL_ABS_DEVICE_ENABLED_ERROR,
+ result, dev, enabled, stream).eventToString(), ALOGE, TAG);
+
+ }
});
}
}
@@ -2069,7 +2076,13 @@ public class AudioService extends IAudioService.Stub
if (dev == AudioSystem.DEVICE_OUT_BLUETOOTH_A2DP) {
enabled = mAvrcpAbsVolSupported;
}
- mAudioSystem.setDeviceAbsoluteVolumeEnabled(dev, /*address=*/"", enabled, stream);
+ final int result = mAudioSystem.setDeviceAbsoluteVolumeEnabled(dev, /*address=*/"",
+ enabled, stream);
+ if (result != AudioSystem.AUDIO_STATUS_OK) {
+ sVolumeLogger.enqueueAndSlog(
+ new VolumeEvent(VolumeEvent.VOL_ABS_DEVICE_ENABLED_ERROR,
+ result, dev, enabled, stream).eventToString(), ALOGE, TAG);
+ }
});
}
@@ -4917,15 +4930,27 @@ public class AudioService extends IAudioService.Stub
private void onUpdateContextualVolumes() {
final int streamType = getBluetoothContextualVolumeStream();
+ Log.i(TAG,
+ "onUpdateContextualVolumes: absolute volume driving streams " + streamType
+ + " avrcp supported: " + mAvrcpAbsVolSupported);
synchronized (mCachedAbsVolDrivingStreamsLock) {
mCachedAbsVolDrivingStreams.replaceAll((absDev, stream) -> {
boolean enabled = true;
if (absDev == AudioSystem.DEVICE_OUT_BLUETOOTH_A2DP) {
enabled = mAvrcpAbsVolSupported;
+ if (!enabled) {
+ Log.w(TAG, "Updating avrcp not supported in onUpdateContextualVolumes");
+ }
}
- if (stream != streamType || !enabled) {
- mAudioSystem.setDeviceAbsoluteVolumeEnabled(absDev, /*address=*/"",
- enabled, streamType);
+ if (stream != streamType) {
+ final int result = mAudioSystem.setDeviceAbsoluteVolumeEnabled(absDev,
+ /*address=*/"", enabled, streamType);
+ if (result != AudioSystem.AUDIO_STATUS_OK) {
+ sVolumeLogger.enqueueAndSlog(
+ new VolumeEvent(VolumeEvent.VOL_ABS_DEVICE_ENABLED_ERROR,
+ result, absDev, enabled, streamType).eventToString(), ALOGE,
+ TAG);
+ }
}
return streamType;
});
@@ -10563,14 +10588,23 @@ public class AudioService extends IAudioService.Stub
}
/*package*/ void setAvrcpAbsoluteVolumeSupported(boolean support) {
+ Log.i(TAG, "setAvrcpAbsoluteVolumeSupported support " + support);
synchronized (mCachedAbsVolDrivingStreamsLock) {
mAvrcpAbsVolSupported = support;
if (absVolumeIndexFix()) {
int a2dpDev = AudioSystem.DEVICE_OUT_BLUETOOTH_A2DP;
mCachedAbsVolDrivingStreams.compute(a2dpDev, (dev, stream) -> {
if (!mAvrcpAbsVolSupported) {
- mAudioSystem.setDeviceAbsoluteVolumeEnabled(a2dpDev, /*address=*/
- "", /*enabled*/false, AudioSystem.STREAM_DEFAULT);
+ final int result = mAudioSystem.setDeviceAbsoluteVolumeEnabled(
+ a2dpDev, /*address=*/"", /*enabled*/false,
+ AudioSystem.STREAM_DEFAULT);
+ if (result != AudioSystem.AUDIO_STATUS_OK) {
+ sVolumeLogger.enqueueAndSlog(
+ new VolumeEvent(VolumeEvent.VOL_ABS_DEVICE_ENABLED_ERROR,
+ result, a2dpDev, /*enabled=*/false,
+ AudioSystem.STREAM_DEFAULT).eventToString(), ALOGE,
+ TAG);
+ }
return null;
}
// For A2DP and AVRCP we need to set the driving stream based on the
@@ -10578,8 +10612,14 @@ public class AudioService extends IAudioService.Stub
// and setStreamVolume that the driving abs volume stream is consistent.
int streamToDriveAbs = getBluetoothContextualVolumeStream();
if (stream == null || stream != streamToDriveAbs) {
- mAudioSystem.setDeviceAbsoluteVolumeEnabled(a2dpDev, /*address=*/
- "", /*enabled*/true, streamToDriveAbs);
+ final int result = mAudioSystem.setDeviceAbsoluteVolumeEnabled(a2dpDev,
+ /*address=*/"", /*enabled*/true, streamToDriveAbs);
+ if (result != AudioSystem.AUDIO_STATUS_OK) {
+ sVolumeLogger.enqueueAndSlog(
+ new VolumeEvent(VolumeEvent.VOL_ABS_DEVICE_ENABLED_ERROR,
+ result, a2dpDev, /*enabled=*/true,
+ streamToDriveAbs).eventToString(), ALOGE, TAG);
+ }
}
return streamToDriveAbs;
});
@@ -12679,7 +12719,7 @@ public class AudioService extends IAudioService.Stub
pw.println("\nLoudness alignment:");
mLoudnessCodecHelper.dump(pw);
- pw.println("\nAbsolute voume devices:");
+ pw.println("\nAbsolute volume devices with their volume driving streams:");
synchronized (mCachedAbsVolDrivingStreamsLock) {
mCachedAbsVolDrivingStreams.forEach((dev, stream) -> pw.println(
"Device type: 0x" + Integer.toHexString(dev) + ", driving stream " + stream));
diff --git a/services/core/java/com/android/server/audio/AudioServiceEvents.java b/services/core/java/com/android/server/audio/AudioServiceEvents.java
index 631d5d80acd7..995e16bd8f17 100644
--- a/services/core/java/com/android/server/audio/AudioServiceEvents.java
+++ b/services/core/java/com/android/server/audio/AudioServiceEvents.java
@@ -234,6 +234,7 @@ public class AudioServiceEvents {
static final int VOL_SET_LE_AUDIO_VOL = 10;
static final int VOL_ADJUST_GROUP_VOL = 11;
static final int VOL_MASTER_MUTE = 12;
+ static final int VOL_ABS_DEVICE_ENABLED_ERROR = 13;
final int mOp;
final int mStream;
@@ -365,6 +366,19 @@ public class AudioServiceEvents {
logMetricEvent();
}
+ /** used for VOL_ABS_DEVICE_ENABLED_ERROR */
+ VolumeEvent(int op, int result, int device, boolean enabled, int streamType) {
+ mOp = op;
+ mStream = streamType;
+ mVal1 = device;
+ mVal2 = enabled ? 1 : 0;
+ mVal3 = result;
+ // unused
+ mCaller = null;
+ mGroupName = null;
+ logMetricEvent();
+ }
+
/**
* Audio Analytics unique Id.
@@ -481,6 +495,9 @@ public class AudioServiceEvents {
case VOL_MASTER_MUTE:
// No value in logging metrics for this internal event
return;
+ case VOL_ABS_DEVICE_ENABLED_ERROR:
+ // No value in logging metrics for this internal event
+ return;
default:
return;
}
@@ -569,6 +586,13 @@ public class AudioServiceEvents {
return new StringBuilder("Master mute:")
.append(mVal1 == 1 ? " muted)" : " unmuted)")
.toString();
+ case VOL_ABS_DEVICE_ENABLED_ERROR:
+ return new StringBuilder("setDeviceAbsoluteVolumeEnabled failed with ")
+ .append(mVal3)
+ .append(" for dev: 0x").append(Integer.toHexString(mVal1))
+ .append(" enabled: ").append(mVal2)
+ .append(" streamType: ").append(mStream)
+ .toString();
default: return new StringBuilder("FIXME invalid op:").append(mOp).toString();
}
}
diff --git a/services/core/java/com/android/server/display/DisplayTopology.java b/services/core/java/com/android/server/display/DisplayTopology.java
index b01d61721c9c..fdadafeb98c9 100644
--- a/services/core/java/com/android/server/display/DisplayTopology.java
+++ b/services/core/java/com/android/server/display/DisplayTopology.java
@@ -16,7 +16,13 @@
package com.android.server.display;
+import static com.android.server.display.DisplayTopology.TreeNode.Position.POSITION_BOTTOM;
+import static com.android.server.display.DisplayTopology.TreeNode.Position.POSITION_LEFT;
+import static com.android.server.display.DisplayTopology.TreeNode.Position.POSITION_TOP;
+import static com.android.server.display.DisplayTopology.TreeNode.Position.POSITION_RIGHT;
+
import android.annotation.Nullable;
+import android.graphics.RectF;
import android.util.IndentingPrintWriter;
import android.util.Pair;
import android.util.Slog;
@@ -25,16 +31,21 @@ import android.view.Display;
import com.android.internal.annotations.VisibleForTesting;
import java.io.PrintWriter;
+import java.util.ArrayDeque;
import java.util.ArrayList;
-import java.util.LinkedList;
+import java.util.Comparator;
+import java.util.HashMap;
import java.util.List;
+import java.util.Map;
import java.util.Queue;
/**
* Represents the relative placement of extended displays.
+ * Does not support concurrent calls, so a lock should be held when calling into this class.
*/
class DisplayTopology {
private static final String TAG = "DisplayTopology";
+ private static final float EPSILON = 0.0001f;
/**
* The topology tree
@@ -58,7 +69,7 @@ class DisplayTopology {
* @param width The width of the display
* @param height The height of the display
*/
- void addDisplay(int displayId, double width, double height) {
+ void addDisplay(int displayId, float width, float height) {
addDisplay(displayId, width, height, /* shouldLog= */ true);
}
@@ -69,10 +80,10 @@ class DisplayTopology {
* @param displayId The logical display ID
*/
void removeDisplay(int displayId) {
- if (!isDisplayPresent(displayId, mRoot)) {
+ if (findDisplay(displayId, mRoot) == null) {
return;
}
- Queue<TreeNode> queue = new LinkedList<>();
+ Queue<TreeNode> queue = new ArrayDeque<>();
queue.add(mRoot);
mRoot = null;
while (!queue.isEmpty()) {
@@ -115,7 +126,11 @@ class DisplayTopology {
}
}
- private void addDisplay(int displayId, double width, double height, boolean shouldLog) {
+ private void addDisplay(int displayId, float width, float height, boolean shouldLog) {
+ if (findDisplay(displayId, mRoot) != null) {
+ throw new IllegalArgumentException(
+ "DisplayTopology: attempting to add a display that already exists");
+ }
if (mRoot == null) {
mRoot = new TreeNode(displayId, width, height, /* position= */ null, /* offset= */ 0);
mPrimaryDisplayId = displayId;
@@ -124,9 +139,8 @@ class DisplayTopology {
}
} else if (mRoot.mChildren.isEmpty()) {
// This is the 2nd display. Align the middles of the top and bottom edges.
- double offset = mRoot.mWidth / 2 - width / 2;
- TreeNode display = new TreeNode(displayId, width, height,
- TreeNode.Position.POSITION_TOP, offset);
+ float offset = mRoot.mWidth / 2 - width / 2;
+ TreeNode display = new TreeNode(displayId, width, height, POSITION_TOP, offset);
mRoot.mChildren.add(display);
if (shouldLog) {
Slog.i(TAG, "Second display added: " + display + ", parent ID: "
@@ -134,8 +148,8 @@ class DisplayTopology {
}
} else {
TreeNode rightMostDisplay = findRightMostDisplay(mRoot, mRoot.mWidth).first;
- TreeNode newDisplay = new TreeNode(displayId, width, height,
- TreeNode.Position.POSITION_RIGHT, /* offset= */ 0);
+ TreeNode newDisplay = new TreeNode(displayId, width, height, POSITION_RIGHT,
+ /* offset= */ 0);
rightMostDisplay.mChildren.add(newDisplay);
if (shouldLog) {
Slog.i(TAG, "Display added: " + newDisplay + ", parent ID: "
@@ -150,11 +164,11 @@ class DisplayTopology {
* @return The display that is the furthest to the right and the x position of the right edge
* of that display
*/
- private Pair<TreeNode, Double> findRightMostDisplay(TreeNode display, double xPos) {
- Pair<TreeNode, Double> result = new Pair<>(display, xPos);
+ private static Pair<TreeNode, Float> findRightMostDisplay(TreeNode display, float xPos) {
+ Pair<TreeNode, Float> result = new Pair<>(display, xPos);
for (TreeNode child : display.mChildren) {
// The x position of the right edge of the child
- double childXPos;
+ float childXPos;
switch (child.mPosition) {
case POSITION_LEFT -> childXPos = xPos - display.mWidth;
case POSITION_TOP, POSITION_BOTTOM ->
@@ -164,7 +178,7 @@ class DisplayTopology {
}
// Recursive call - find the rightmost display starting from the child
- Pair<TreeNode, Double> childResult = findRightMostDisplay(child, childXPos);
+ Pair<TreeNode, Float> childResult = findRightMostDisplay(child, childXPos);
// Check if the one found is further right
if (childResult.second > result.second) {
result = new Pair<>(childResult.first, childResult.second);
@@ -173,19 +187,200 @@ class DisplayTopology {
return result;
}
- private boolean isDisplayPresent(int displayId, TreeNode node) {
- if (node == null) {
- return false;
+ @Nullable
+ private static TreeNode findDisplay(int displayId, TreeNode startingNode) {
+ if (startingNode == null) {
+ return null;
}
- if (node.mDisplayId == displayId) {
- return true;
+ if (startingNode.mDisplayId == displayId) {
+ return startingNode;
}
- for (TreeNode child : node.mChildren) {
- if (isDisplayPresent(displayId, child)) {
- return true;
+ for (TreeNode child : startingNode.mChildren) {
+ TreeNode display = findDisplay(displayId, child);
+ if (display != null) {
+ return display;
+ }
+ }
+ return null;
+ }
+
+ /**
+ * Get information about the topology that will be used for the normalization algorithm.
+ * Assigns origins to each display to compute the bounds.
+ * @param bounds The map where the bounds of each display will be put
+ * @param depths The map where the depths of each display in the tree will be put
+ * @param parents The map where the parent of each display will be put
+ * @param display The starting node
+ * @param x The starting x position
+ * @param y The starting y position
+ * @param depth The starting depth
+ */
+ private static void getInfo(Map<TreeNode, RectF> bounds, Map<TreeNode, Integer> depths,
+ Map<TreeNode, TreeNode> parents, TreeNode display, float x, float y, int depth) {
+ bounds.put(display, new RectF(x, y, x + display.mWidth, y + display.mHeight));
+ depths.put(display, depth);
+ for (TreeNode child : display.mChildren) {
+ parents.put(child, display);
+ if (child.mPosition == POSITION_LEFT) {
+ getInfo(bounds, depths, parents, child, x - child.mWidth, y + child.mOffset,
+ depth + 1);
+ } else if (child.mPosition == POSITION_RIGHT) {
+ getInfo(bounds, depths, parents, child, x + display.mWidth, y + child.mOffset,
+ depth + 1);
+ } else if (child.mPosition == POSITION_TOP) {
+ getInfo(bounds, depths, parents, child, x + child.mOffset, y - child.mHeight,
+ depth + 1);
+ } else if (child.mPosition == POSITION_BOTTOM) {
+ getInfo(bounds, depths, parents, child, x + child.mOffset, y + display.mHeight,
+ depth + 1);
+ }
+ }
+ }
+
+ /**
+ * Update the topology to remove any overlaps between displays.
+ */
+ @VisibleForTesting
+ void normalize() {
+ if (mRoot == null) {
+ return;
+ }
+ Map<TreeNode, RectF> bounds = new HashMap<>();
+ Map<TreeNode, Integer> depths = new HashMap<>();
+ Map<TreeNode, TreeNode> parents = new HashMap<>();
+ getInfo(bounds, depths, parents, mRoot, /* x= */ 0, /* y= */ 0, /* depth= */ 0);
+
+ // Sort the displays first by their depth in the tree, then by the distance of their top
+ // left point from the root display's origin (0, 0). This way we process the displays
+ // starting at the root and we push out a display if necessary.
+ Comparator<TreeNode> comparator = (d1, d2) -> {
+ if (d1 == d2) {
+ return 0;
+ }
+
+ int compareDepths = Integer.compare(depths.get(d1), depths.get(d2));
+ if (compareDepths != 0) {
+ return compareDepths;
+ }
+
+ RectF bounds1 = bounds.get(d1);
+ RectF bounds2 = bounds.get(d2);
+ return Double.compare(Math.hypot(bounds1.left, bounds1.top),
+ Math.hypot(bounds2.left, bounds2.top));
+ };
+ List<TreeNode> displays = new ArrayList<>(bounds.keySet());
+ displays.sort(comparator);
+
+ for (int i = 1; i < displays.size(); i++) {
+ TreeNode targetDisplay = displays.get(i);
+ TreeNode lastIntersectingSourceDisplay = null;
+ float lastOffsetX = 0;
+ float lastOffsetY = 0;
+
+ for (int j = 0; j < i; j++) {
+ TreeNode sourceDisplay = displays.get(j);
+ RectF sourceBounds = bounds.get(sourceDisplay);
+ RectF targetBounds = bounds.get(targetDisplay);
+
+ if (!RectF.intersects(sourceBounds, targetBounds)) {
+ continue;
+ }
+
+ // Find the offset by which to move the display. Pick the smaller one among the x
+ // and y axes.
+ float offsetX = targetBounds.left >= 0
+ ? sourceBounds.right - targetBounds.left
+ : sourceBounds.left - targetBounds.right;
+ float offsetY = targetBounds.top >= 0
+ ? sourceBounds.bottom - targetBounds.top
+ : sourceBounds.top - targetBounds.bottom;
+ if (Math.abs(offsetX) <= Math.abs(offsetY)) {
+ targetBounds.left += offsetX;
+ targetBounds.right += offsetX;
+ // We need to also update the offset in the tree
+ if (targetDisplay.mPosition == POSITION_TOP
+ || targetDisplay.mPosition == POSITION_BOTTOM) {
+ targetDisplay.mOffset += offsetX;
+ }
+ offsetY = 0;
+ } else {
+ targetBounds.top += offsetY;
+ targetBounds.bottom += offsetY;
+ // We need to also update the offset in the tree
+ if (targetDisplay.mPosition == POSITION_LEFT
+ || targetDisplay.mPosition == POSITION_RIGHT) {
+ targetDisplay.mOffset += offsetY;
+ }
+ offsetX = 0;
+ }
+
+ lastIntersectingSourceDisplay = sourceDisplay;
+ lastOffsetX = offsetX;
+ lastOffsetY = offsetY;
+ }
+
+ // Now re-parent the target display to the last intersecting source display if it no
+ // longer touches its parent.
+ if (lastIntersectingSourceDisplay == null) {
+ // There was no overlap.
+ continue;
+ }
+ TreeNode parent = parents.get(targetDisplay);
+ if (parent == lastIntersectingSourceDisplay) {
+ // The displays are moved in such a way that they're adjacent to the intersecting
+ // display. If the last intersecting display happens to be the parent then we
+ // already know that the display is adjacent to its parent.
+ continue;
+ }
+
+ RectF childBounds = bounds.get(targetDisplay);
+ RectF parentBounds = bounds.get(parent);
+ // Check that the edges are on the same line
+ boolean areTouching = switch (targetDisplay.mPosition) {
+ case POSITION_LEFT -> floatEquals(parentBounds.left, childBounds.right);
+ case POSITION_RIGHT -> floatEquals(parentBounds.right, childBounds.left);
+ case POSITION_TOP -> floatEquals(parentBounds.top, childBounds.bottom);
+ case POSITION_BOTTOM -> floatEquals(parentBounds.bottom, childBounds.top);
+ };
+ // Check that the offset is within bounds
+ areTouching &= switch (targetDisplay.mPosition) {
+ case POSITION_LEFT, POSITION_RIGHT ->
+ childBounds.bottom + EPSILON >= parentBounds.top
+ && childBounds.top <= parentBounds.bottom + EPSILON;
+ case POSITION_TOP, POSITION_BOTTOM ->
+ childBounds.right + EPSILON >= parentBounds.left
+ && childBounds.left <= parentBounds.right + EPSILON;
+ };
+
+ if (!areTouching) {
+ // Re-parent the display.
+ parent.mChildren.remove(targetDisplay);
+ RectF lastIntersectingSourceDisplayBounds =
+ bounds.get(lastIntersectingSourceDisplay);
+ lastIntersectingSourceDisplay.mChildren.add(targetDisplay);
+
+ if (lastOffsetX != 0) {
+ targetDisplay.mPosition = lastOffsetX > 0 ? POSITION_RIGHT : POSITION_LEFT;
+ targetDisplay.mOffset =
+ childBounds.top - lastIntersectingSourceDisplayBounds.top;
+ } else if (lastOffsetY != 0) {
+ targetDisplay.mPosition = lastOffsetY > 0 ? POSITION_BOTTOM : POSITION_TOP;
+ targetDisplay.mOffset =
+ childBounds.left - lastIntersectingSourceDisplayBounds.left;
+ }
}
}
- return false;
+ }
+
+ /**
+ * Tests whether two brightness float values are within a small enough tolerance
+ * of each other.
+ * @param a first float to compare
+ * @param b second float to compare
+ * @return whether the two values are within a small enough tolerance value
+ */
+ public static boolean floatEquals(float a, float b) {
+ return a == b || Float.isNaN(a) && Float.isNaN(b) || Math.abs(a - b) < EPSILON;
}
@VisibleForTesting
@@ -201,13 +396,13 @@ class DisplayTopology {
* The width of the display in density-independent pixels (dp).
*/
@VisibleForTesting
- double mWidth;
+ float mWidth;
/**
* The height of the display in density-independent pixels (dp).
*/
@VisibleForTesting
- double mHeight;
+ float mHeight;
/**
* The position of this display relative to its parent.
@@ -222,13 +417,12 @@ class DisplayTopology {
* used is density-independent pixels (dp).
*/
@VisibleForTesting
- double mOffset;
+ float mOffset;
@VisibleForTesting
final List<TreeNode> mChildren = new ArrayList<>();
- TreeNode(int displayId, double width, double height, Position position,
- double offset) {
+ TreeNode(int displayId, float width, float height, Position position, float offset) {
mDisplayId = displayId;
mWidth = width;
mHeight = height;
diff --git a/services/core/java/com/android/server/display/DisplayTopologyCoordinator.java b/services/core/java/com/android/server/display/DisplayTopologyCoordinator.java
index 46358dfd90ec..b101e5893b97 100644
--- a/services/core/java/com/android/server/display/DisplayTopologyCoordinator.java
+++ b/services/core/java/com/android/server/display/DisplayTopologyCoordinator.java
@@ -89,8 +89,8 @@ class DisplayTopologyCoordinator {
* @param info The display info
* @return The width of the display in dp
*/
- private double getWidth(DisplayInfo info) {
- return info.logicalWidth * (double) DisplayMetrics.DENSITY_DEFAULT
+ private float getWidth(DisplayInfo info) {
+ return info.logicalWidth * (float) DisplayMetrics.DENSITY_DEFAULT
/ info.logicalDensityDpi;
}
@@ -98,8 +98,8 @@ class DisplayTopologyCoordinator {
* @param info The display info
* @return The height of the display in dp
*/
- private double getHeight(DisplayInfo info) {
- return info.logicalHeight * (double) DisplayMetrics.DENSITY_DEFAULT
+ private float getHeight(DisplayInfo info) {
+ return info.logicalHeight * (float) DisplayMetrics.DENSITY_DEFAULT
/ info.logicalDensityDpi;
}
diff --git a/services/core/java/com/android/server/hdmi/HdmiCecLocalDevicePlayback.java b/services/core/java/com/android/server/hdmi/HdmiCecLocalDevicePlayback.java
index d0ad6fc0854f..b696c5481205 100644
--- a/services/core/java/com/android/server/hdmi/HdmiCecLocalDevicePlayback.java
+++ b/services/core/java/com/android/server/hdmi/HdmiCecLocalDevicePlayback.java
@@ -444,14 +444,62 @@ public class HdmiCecLocalDevicePlayback extends HdmiCecLocalDeviceSource {
new Runnable() {
@Override
public void run() {
- if (!isActiveSource()) {
+ if (isActiveSource()) {
+ return;
+ }
+
+ if (getActiveSource().logicalAddress != Constants.ADDR_TV) {
startHdmiCecActiveSourceLostActivity();
mDelayedStandbyOnActiveSourceLostHandler
.removeCallbacksAndMessages(null);
mDelayedStandbyOnActiveSourceLostHandler.postDelayed(
new DelayedStandbyOnActiveSourceLostRunnable(),
STANDBY_AFTER_ACTIVE_SOURCE_LOST_DELAY_MS);
+ return;
}
+
+ // We observed specific TV panels (old models) that send faulty CEC
+ // source changing messages, especially during wake-up.
+ // This request helps to check if the TV correctly asserted active
+ // source or not. If the request times out, active source is
+ // asserted by the local device.
+ addAndStartAction(new RequestActiveSourceAction(mService.playback(),
+ new IHdmiControlCallback.Stub() {
+ @Override
+ public void onComplete(int result) {
+ // If a device answers to <Request Active Source>, the
+ // pop-up should be triggered.
+ // During this action, the TV can switch to an HDMI input
+ // with a non-CEC capable device that won't be able to
+ // answer this request.
+ // In this case, the known active source would be
+ // represented by a valid physical address, but invalid
+ // logical address. The pop-up will be shown and the local
+ // device will not assert active source.
+ if (result == HdmiControlManager.RESULT_SUCCESS
+ || getActiveSource().logicalAddress
+ != Constants.ADDR_TV) {
+ startHdmiCecActiveSourceLostActivity();
+ mDelayedStandbyOnActiveSourceLostHandler
+ .removeCallbacksAndMessages(null);
+ mDelayedStandbyOnActiveSourceLostHandler
+ .postDelayed(
+ new DelayedStandbyOnActiveSourceLostRunnable(),
+ STANDBY_AFTER_ACTIVE_SOURCE_LOST_DELAY_MS);
+ } else {
+ // The request times out and the local device is not
+ // active source, but the TV previously asserted active
+ // source.
+ if (getActiveSource().logicalAddress
+ == Constants.ADDR_TV) {
+ mService.setAndBroadcastActiveSource(
+ mService.getPhysicalAddress(),
+ getDeviceInfo().getDeviceType(),
+ Constants.ADDR_BROADCAST,
+ "RequestActiveSourceAction#RESULT_TIMEOUT");
+ }
+ }
+ }}));
}
}, POPUP_AFTER_ACTIVE_SOURCE_LOST_DELAY_MS);
return;
@@ -698,6 +746,7 @@ public class HdmiCecLocalDevicePlayback extends HdmiCecLocalDeviceSource {
removeAction(HotplugDetectionAction.class);
removeAction(NewDeviceAction.class);
removeAction(PowerStatusMonitorActionFromPlayback.class);
+ removeAction(RequestActiveSourceAction.class);
super.disableDevice(initiatedByCec, callback);
clearDeviceInfoList();
checkIfPendingActionsCleared();
diff --git a/services/core/java/com/android/server/hdmi/HdmiCecLocalDeviceTv.java b/services/core/java/com/android/server/hdmi/HdmiCecLocalDeviceTv.java
index aae7b59b1a1a..5682c330e4b0 100644
--- a/services/core/java/com/android/server/hdmi/HdmiCecLocalDeviceTv.java
+++ b/services/core/java/com/android/server/hdmi/HdmiCecLocalDeviceTv.java
@@ -471,6 +471,10 @@ public class HdmiCecLocalDeviceTv extends HdmiCecLocalDevice {
void startRoutingControl(int oldPath, int newPath, IHdmiControlCallback callback) {
assertRunOnServiceThread();
if (oldPath == newPath) {
+ HdmiCecMessage setStreamPath =
+ HdmiCecMessageBuilder.buildSetStreamPath(getDeviceInfo().getLogicalAddress(),
+ oldPath);
+ mService.sendCecCommand(setStreamPath);
return;
}
HdmiCecMessage routingChange =
@@ -642,7 +646,8 @@ public class HdmiCecLocalDeviceTv extends HdmiCecLocalDevice {
int address = message.getSource();
int type = message.getParams()[2];
- if (!mService.getHdmiCecNetwork().isInDeviceList(address, path)) {
+ if (!ActiveSource.of(address, path).equals(getActiveSource())) {
+ HdmiLogger.debug("Check if a new device is connected to the active path");
handleNewDeviceAtTheTailOfActivePath(path);
}
startNewDeviceAction(ActiveSource.of(address, path), type);
diff --git a/services/core/java/com/android/server/hdmi/HdmiControlService.java b/services/core/java/com/android/server/hdmi/HdmiControlService.java
index 81be0baefd7a..0766c3ae0f2b 100644
--- a/services/core/java/com/android/server/hdmi/HdmiControlService.java
+++ b/services/core/java/com/android/server/hdmi/HdmiControlService.java
@@ -1988,7 +1988,6 @@ public class HdmiControlService extends SystemService {
void setAudioStatus(boolean mute, int volume) {
if (!isTvDeviceEnabled()
|| !tv().isSystemAudioActivated()
- || !tv().isArcEstablished() // Don't update TV volume when SAM is on and ARC is off
|| getHdmiCecVolumeControl()
== HdmiControlManager.VOLUME_CONTROL_DISABLED) {
return;
@@ -4344,6 +4343,7 @@ public class HdmiControlService extends SystemService {
if (deviceType == HdmiDeviceInfo.DEVICE_PLAYBACK) {
HdmiCecLocalDevicePlayback playback = playback();
playback.dismissUiOnActiveSourceStatusRecovered();
+ playback.removeAction(RequestActiveSourceAction.class);
playback.setActiveSource(playback.getDeviceInfo().getLogicalAddress(), physicalAddress,
caller);
playback.wakeUpIfActiveSource();
diff --git a/services/core/java/com/android/server/hdmi/RequestActiveSourceAction.java b/services/core/java/com/android/server/hdmi/RequestActiveSourceAction.java
index a33d70a9b876..b0e93989d463 100644
--- a/services/core/java/com/android/server/hdmi/RequestActiveSourceAction.java
+++ b/services/core/java/com/android/server/hdmi/RequestActiveSourceAction.java
@@ -22,11 +22,11 @@ import android.util.Slog;
import com.android.internal.annotations.VisibleForTesting;
/**
- * Feature action that sends <Request Active Source> message and waits for <Active Source> on TV
- * panels.
- * This action has a delay before sending <Request Active Source>. This is because it should wait
- * for a possible request from LauncherX and can be cancelled if an <Active Source> message was
- * received or the TV switched to another input.
+ * Feature action that sends <Request Active Source> message and waits for <Active Source>.
+ *
+ * For TV panels, this action has a delay before sending <Request Active Source>. This is because it
+ * should wait for a possible request from LauncherX and can be cancelled if an <Active Source>
+ * message was received or the TV switched to another input.
*/
public class RequestActiveSourceAction extends HdmiCecFeatureAction {
private static final String TAG = "RequestActiveSourceAction";
@@ -55,6 +55,13 @@ public class RequestActiveSourceAction extends HdmiCecFeatureAction {
boolean start() {
Slog.v(TAG, "RequestActiveSourceAction started.");
+ if (localDevice().mService.isPlaybackDevice()) {
+ mState = STATE_WAIT_FOR_ACTIVE_SOURCE;
+ sendCommand(HdmiCecMessageBuilder.buildRequestActiveSource(getSourceAddress()));
+ addTimer(mState, HdmiConfig.TIMEOUT_MS);
+ return true;
+ }
+
mState = STATE_WAIT_FOR_LAUNCHERX_API_CALL;
// We wait for default timeout to allow the message triggered by the LauncherX API call to
diff --git a/services/core/java/com/android/server/input/InputManagerInternal.java b/services/core/java/com/android/server/input/InputManagerInternal.java
index e40d855293cd..1c5bd59fa386 100644
--- a/services/core/java/com/android/server/input/InputManagerInternal.java
+++ b/services/core/java/com/android/server/input/InputManagerInternal.java
@@ -127,6 +127,13 @@ public abstract class InputManagerInternal {
*/
public abstract void notifyInputMethodConnectionActive(boolean connectionIsActive);
+ /**
+ * Notify user id changes to input.
+ *
+ * TODO(b/362473586): Cleanup after input shifts to Lifecycle with user change callbacks
+ */
+ public abstract void setCurrentUser(@UserIdInt int newUserId);
+
/** Callback interface for notifications relating to the lid switch. */
public interface LidSwitchCallback {
/**
diff --git a/services/core/java/com/android/server/input/InputManagerService.java b/services/core/java/com/android/server/input/InputManagerService.java
index bea520f9429e..a421d044507a 100644
--- a/services/core/java/com/android/server/input/InputManagerService.java
+++ b/services/core/java/com/android/server/input/InputManagerService.java
@@ -175,6 +175,7 @@ public class InputManagerService extends IInputManager.Stub
private static final int MSG_DELIVER_INPUT_DEVICES_CHANGED = 1;
private static final int MSG_RELOAD_DEVICE_ALIASES = 2;
private static final int MSG_DELIVER_TABLET_MODE_CHANGED = 3;
+ private static final int MSG_CURRENT_USER_CHANGED = 4;
private static final int DEFAULT_VIBRATION_MAGNITUDE = 192;
private static final AdditionalDisplayInputProperties
@@ -184,6 +185,8 @@ public class InputManagerService extends IInputManager.Stub
private final Context mContext;
private final InputManagerHandler mHandler;
+ @UserIdInt
+ private int mCurrentUserId = UserHandle.USER_SYSTEM;
private DisplayManagerInternal mDisplayManagerInternal;
private WindowManagerInternal mWindowManagerInternal;
@@ -2982,6 +2985,10 @@ public class InputManagerService extends IInputManager.Stub
mKeyGestureController.unregisterKeyGestureHandler(handler, Binder.getCallingPid());
}
+ private void handleCurrentUserChanged(@UserIdInt int userId) {
+ mCurrentUserId = userId;
+ }
+
/**
* Callback interface implemented by the Window Manager.
*/
@@ -3150,6 +3157,9 @@ public class InputManagerService extends IInputManager.Stub
boolean inTabletMode = (boolean) args.arg1;
deliverTabletModeChanged(whenNanos, inTabletMode);
break;
+ case MSG_CURRENT_USER_CHANGED:
+ handleCurrentUserChanged((int) msg.obj);
+ break;
}
}
}
@@ -3513,6 +3523,11 @@ public class InputManagerService extends IInputManager.Stub
}
@Override
+ public void setCurrentUser(@UserIdInt int newUserId) {
+ mHandler.obtainMessage(MSG_CURRENT_USER_CHANGED, newUserId).sendToTarget();
+ }
+
+ @Override
public boolean setKernelWakeEnabled(int deviceId, boolean enabled) {
return mNative.setKernelWakeEnabled(deviceId, enabled);
}
diff --git a/services/core/java/com/android/server/input/KeyGestureController.java b/services/core/java/com/android/server/input/KeyGestureController.java
index bd1625ea6d3e..4d93e6570a79 100644
--- a/services/core/java/com/android/server/input/KeyGestureController.java
+++ b/services/core/java/com/android/server/input/KeyGestureController.java
@@ -1012,16 +1012,16 @@ final class KeyGestureController {
if (device == null) {
return;
}
+ KeyGestureEvent keyGestureEvent = new KeyGestureEvent(event);
if (event.action == KeyGestureEvent.ACTION_GESTURE_COMPLETE) {
KeyboardMetricsCollector.logKeyboardSystemsEventReportedAtom(device, event.keycodes,
- event.modifierState,
- KeyGestureEvent.keyGestureTypeToLogEvent(event.gestureType));
+ event.modifierState, keyGestureEvent.getLogEvent());
}
notifyAllListeners(event);
while (mLastHandledEvents.size() >= MAX_TRACKED_EVENTS) {
mLastHandledEvents.removeFirst();
}
- mLastHandledEvents.addLast(new KeyGestureEvent(event));
+ mLastHandledEvents.addLast(keyGestureEvent);
}
@MainThread
diff --git a/services/core/java/com/android/server/integrity/parser/RuleIndexRange.java b/services/core/java/com/android/server/integrity/parser/RuleIndexRange.java
index 595a035baf1d..408df5a784ff 100644
--- a/services/core/java/com/android/server/integrity/parser/RuleIndexRange.java
+++ b/services/core/java/com/android/server/integrity/parser/RuleIndexRange.java
@@ -19,8 +19,7 @@ package com.android.server.integrity.parser;
import android.annotation.Nullable;
/**
- * A wrapper class to represent an indexing range that is identified by the {@link
- * RuleIndexingController}.
+ * A wrapper class to represent an indexing range.
*/
public class RuleIndexRange {
private int mStartIndex;
diff --git a/services/core/java/com/android/server/integrity/parser/RuleIndexingController.java b/services/core/java/com/android/server/integrity/parser/RuleIndexingController.java
deleted file mode 100644
index 348a03be7317..000000000000
--- a/services/core/java/com/android/server/integrity/parser/RuleIndexingController.java
+++ /dev/null
@@ -1,128 +0,0 @@
-/*
- * Copyright (C) 2020 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.server.integrity.parser;
-
-import static com.android.server.integrity.model.IndexingFileConstants.END_INDEXING_KEY;
-import static com.android.server.integrity.model.IndexingFileConstants.START_INDEXING_KEY;
-import static com.android.server.integrity.parser.BinaryFileOperations.getIntValue;
-import static com.android.server.integrity.parser.BinaryFileOperations.getStringValue;
-
-import android.content.integrity.AppInstallMetadata;
-
-import com.android.server.integrity.model.BitInputStream;
-
-import java.io.IOException;
-import java.io.InputStream;
-import java.util.ArrayList;
-import java.util.Arrays;
-import java.util.LinkedHashMap;
-import java.util.List;
-import java.util.stream.Collectors;
-
-/** Helper class to identify the necessary indexes that needs to be read. */
-public class RuleIndexingController {
-
- private static LinkedHashMap<String, Integer> sPackageNameBasedIndexes;
- private static LinkedHashMap<String, Integer> sAppCertificateBasedIndexes;
- private static LinkedHashMap<String, Integer> sUnindexedRuleIndexes;
-
- /**
- * Provide the indexing file to read and the object will be constructed by reading and
- * identifying the indexes.
- */
- public RuleIndexingController(InputStream inputStream) throws IOException {
- BitInputStream bitInputStream = new BitInputStream(inputStream);
- sPackageNameBasedIndexes = getNextIndexGroup(bitInputStream);
- sAppCertificateBasedIndexes = getNextIndexGroup(bitInputStream);
- sUnindexedRuleIndexes = getNextIndexGroup(bitInputStream);
- }
-
- /**
- * Returns a list of integers with the starting and ending bytes of the rules that needs to be
- * read and evaluated.
- */
- public List<RuleIndexRange> identifyRulesToEvaluate(AppInstallMetadata appInstallMetadata) {
- List<RuleIndexRange> indexRanges = new ArrayList<>();
-
- // Add the range for package name indexes rules.
- indexRanges.add(
- searchIndexingKeysRangeContainingKey(
- sPackageNameBasedIndexes, appInstallMetadata.getPackageName()));
-
- // Add the range for app certificate indexes rules of all certificates.
- for (String appCertificate : appInstallMetadata.getAppCertificates()) {
- indexRanges.add(
- searchIndexingKeysRangeContainingKey(
- sAppCertificateBasedIndexes, appCertificate));
- }
-
- // Add the range for unindexed rules.
- indexRanges.add(
- new RuleIndexRange(
- sUnindexedRuleIndexes.get(START_INDEXING_KEY),
- sUnindexedRuleIndexes.get(END_INDEXING_KEY)));
-
- return indexRanges;
- }
-
- private LinkedHashMap<String, Integer> getNextIndexGroup(BitInputStream bitInputStream)
- throws IOException {
- LinkedHashMap<String, Integer> keyToIndexMap = new LinkedHashMap<>();
- while (bitInputStream.hasNext()) {
- String key = getStringValue(bitInputStream);
- int value = getIntValue(bitInputStream);
-
- keyToIndexMap.put(key, value);
-
- if (key.matches(END_INDEXING_KEY)) {
- break;
- }
- }
- if (keyToIndexMap.size() < 2) {
- throw new IllegalStateException("Indexing file is corrupt.");
- }
- return keyToIndexMap;
- }
-
- private static RuleIndexRange searchIndexingKeysRangeContainingKey(
- LinkedHashMap<String, Integer> indexMap, String searchedKey) {
- List<String> keys = indexMap.keySet().stream().collect(Collectors.toList());
- List<String> identifiedKeyRange =
- searchKeysRangeContainingKey(keys, searchedKey, 0, keys.size() - 1);
- return new RuleIndexRange(
- indexMap.get(identifiedKeyRange.get(0)), indexMap.get(identifiedKeyRange.get(1)));
- }
-
- private static List<String> searchKeysRangeContainingKey(
- List<String> sortedKeyList, String key, int startIndex, int endIndex) {
- if (endIndex <= startIndex) {
- throw new IllegalStateException("Indexing file is corrupt.");
- }
- if (endIndex - startIndex == 1) {
- return Arrays.asList(sortedKeyList.get(startIndex), sortedKeyList.get(endIndex));
- }
-
- int midKeyIndex = startIndex + ((endIndex - startIndex) / 2);
- String midKey = sortedKeyList.get(midKeyIndex);
-
- if (key.compareTo(midKey) >= 0) {
- return searchKeysRangeContainingKey(sortedKeyList, key, midKeyIndex, endIndex);
- } else {
- return searchKeysRangeContainingKey(sortedKeyList, key, startIndex, midKeyIndex);
- }
- }
-}
diff --git a/services/core/java/com/android/server/notification/NotificationManagerService.java b/services/core/java/com/android/server/notification/NotificationManagerService.java
index 48cc03221124..abf3da45d0cb 100644
--- a/services/core/java/com/android/server/notification/NotificationManagerService.java
+++ b/services/core/java/com/android/server/notification/NotificationManagerService.java
@@ -49,10 +49,6 @@ import static android.app.Notification.FLAG_ONLY_ALERT_ONCE;
import static android.app.Notification.FLAG_PROMOTED_ONGOING;
import static android.app.Notification.FLAG_USER_INITIATED_JOB;
import static android.app.NotificationChannel.CONVERSATION_CHANNEL_ID_FORMAT;
-import static android.app.NotificationChannel.NEWS_ID;
-import static android.app.NotificationChannel.PROMOTIONS_ID;
-import static android.app.NotificationChannel.RECS_ID;
-import static android.app.NotificationChannel.SOCIAL_MEDIA_ID;
import static android.app.NotificationChannel.SYSTEM_RESERVED_IDS;
import static android.app.NotificationManager.ACTION_APP_BLOCK_STATE_CHANGED;
import static android.app.NotificationManager.ACTION_AUTOMATIC_ZEN_RULE_STATUS_CHANGED;
@@ -108,9 +104,7 @@ import static android.os.UserHandle.USER_NULL;
import static android.os.UserHandle.USER_SYSTEM;
import static android.service.notification.Adjustment.KEY_TYPE;
import static android.service.notification.Adjustment.TYPE_CONTENT_RECOMMENDATION;
-import static android.service.notification.Adjustment.TYPE_NEWS;
import static android.service.notification.Adjustment.TYPE_PROMOTION;
-import static android.service.notification.Adjustment.TYPE_SOCIAL_MEDIA;
import static android.service.notification.Flags.callstyleCallbackApi;
import static android.service.notification.Flags.notificationClassification;
import static android.service.notification.Flags.notificationForceGrouping;
@@ -6924,21 +6918,19 @@ public class NotificationManagerService extends SystemService {
@GuardedBy("mNotificationLock")
@Nullable
+ @FlaggedApi(android.service.notification.Flags.FLAG_NOTIFICATION_CLASSIFICATION)
private NotificationChannel getClassificationChannelLocked(NotificationRecord r,
Bundle adjustments) {
int type = adjustments.getInt(KEY_TYPE);
- if (TYPE_NEWS == type) {
- return mPreferencesHelper.getNotificationChannel(
- r.getSbn().getPackageName(), r.getUid(), NEWS_ID, false);
- } else if (TYPE_PROMOTION == type) {
- return mPreferencesHelper.getNotificationChannel(
- r.getSbn().getPackageName(), r.getUid(), PROMOTIONS_ID, false);
- } else if (TYPE_SOCIAL_MEDIA == type) {
- return mPreferencesHelper.getNotificationChannel(
- r.getSbn().getPackageName(), r.getUid(), SOCIAL_MEDIA_ID, false);
- } else if (TYPE_CONTENT_RECOMMENDATION == type) {
- return mPreferencesHelper.getNotificationChannel(
- r.getSbn().getPackageName(), r.getUid(), RECS_ID, false);
+ if (type >= TYPE_PROMOTION && type <= TYPE_CONTENT_RECOMMENDATION) {
+ NotificationChannel channel = mPreferencesHelper.getReservedChannel(
+ r.getSbn().getPackageName(), r.getUid(), type);
+ if (channel == null) {
+ channel = mPreferencesHelper.createReservedChannel(
+ r.getSbn().getPackageName(), r.getUid(), type);
+ handleSavePolicyFile();
+ }
+ return channel;
}
return null;
}
diff --git a/services/core/java/com/android/server/notification/PreferencesHelper.java b/services/core/java/com/android/server/notification/PreferencesHelper.java
index 964a5d002bf2..d26a5aa5491f 100644
--- a/services/core/java/com/android/server/notification/PreferencesHelper.java
+++ b/services/core/java/com/android/server/notification/PreferencesHelper.java
@@ -17,6 +17,7 @@
package com.android.server.notification;
import static android.app.AppOpsManager.OP_SYSTEM_ALERT_WINDOW;
+import static android.app.NotificationChannel.DEFAULT_CHANNEL_ID;
import static android.app.NotificationChannel.NEWS_ID;
import static android.app.NotificationChannel.PLACEHOLDER_CONVERSATION_ID;
import static android.app.NotificationChannel.PROMOTIONS_ID;
@@ -32,6 +33,10 @@ import static android.app.NotificationManager.IMPORTANCE_MAX;
import static android.app.NotificationManager.IMPORTANCE_NONE;
import static android.app.NotificationManager.IMPORTANCE_UNSPECIFIED;
import static android.os.UserHandle.USER_SYSTEM;
+import static android.service.notification.Adjustment.TYPE_CONTENT_RECOMMENDATION;
+import static android.service.notification.Adjustment.TYPE_NEWS;
+import static android.service.notification.Adjustment.TYPE_PROMOTION;
+import static android.service.notification.Adjustment.TYPE_SOCIAL_MEDIA;
import static android.service.notification.Flags.notificationClassification;
import static com.android.internal.util.FrameworkStatsLog.PACKAGE_NOTIFICATION_CHANNEL_GROUP_PREFERENCES;
@@ -66,6 +71,7 @@ import android.os.Process;
import android.os.UserHandle;
import android.permission.PermissionManager;
import android.provider.Settings;
+import android.service.notification.Adjustment;
import android.service.notification.ConversationChannelWrapper;
import android.service.notification.NotificationListenerService;
import android.service.notification.RankingHelperProto;
@@ -549,10 +555,6 @@ public class PreferencesHelper implements RankingConfig {
Slog.e(TAG, "createDefaultChannelIfNeededLocked - Exception: " + e);
}
- if (notificationClassification()) {
- addReservedChannelsLocked(r);
- }
-
if (r.uid == UNKNOWN_UID) {
if (Flags.persistIncompleteRestoreData()) {
r.userId = userId;
@@ -587,7 +589,7 @@ public class PreferencesHelper implements RankingConfig {
private boolean deleteDefaultChannelIfNeededLocked(PackagePreferences r) throws
PackageManager.NameNotFoundException {
- if (!r.channels.containsKey(NotificationChannel.DEFAULT_CHANNEL_ID)) {
+ if (!r.channels.containsKey(DEFAULT_CHANNEL_ID)) {
// Not present
return false;
}
@@ -598,7 +600,7 @@ public class PreferencesHelper implements RankingConfig {
}
// Remove Default Channel.
- r.channels.remove(NotificationChannel.DEFAULT_CHANNEL_ID);
+ r.channels.remove(DEFAULT_CHANNEL_ID);
return true;
}
@@ -609,8 +611,8 @@ public class PreferencesHelper implements RankingConfig {
return false;
}
- if (r.channels.containsKey(NotificationChannel.DEFAULT_CHANNEL_ID)) {
- r.channels.get(NotificationChannel.DEFAULT_CHANNEL_ID).setName(mContext.getString(
+ if (r.channels.containsKey(DEFAULT_CHANNEL_ID)) {
+ r.channels.get(DEFAULT_CHANNEL_ID).setName(mContext.getString(
com.android.internal.R.string.default_notification_channel_label));
return false;
}
@@ -623,7 +625,7 @@ public class PreferencesHelper implements RankingConfig {
// Create Default Channel
NotificationChannel channel;
channel = new NotificationChannel(
- NotificationChannel.DEFAULT_CHANNEL_ID,
+ DEFAULT_CHANNEL_ID,
mContext.getString(R.string.default_notification_channel_label),
r.importance);
channel.setBypassDnd(r.priority == Notification.PRIORITY_MAX);
@@ -642,38 +644,25 @@ public class PreferencesHelper implements RankingConfig {
return true;
}
- private void addReservedChannelsLocked(PackagePreferences p) {
- if (!p.channels.containsKey(NotificationChannel.PROMOTIONS_ID)) {
- NotificationChannel channel = new NotificationChannel(
- NotificationChannel.PROMOTIONS_ID,
- mContext.getString(R.string.promotional_notification_channel_label),
- IMPORTANCE_LOW);
- p.channels.put(channel.getId(), channel);
- }
-
- if (!p.channels.containsKey(NotificationChannel.RECS_ID)) {
- NotificationChannel channel = new NotificationChannel(
- NotificationChannel.RECS_ID,
- mContext.getString(R.string.recs_notification_channel_label),
- IMPORTANCE_LOW);
- p.channels.put(channel.getId(), channel);
- }
-
- if (!p.channels.containsKey(NotificationChannel.NEWS_ID)) {
- NotificationChannel channel = new NotificationChannel(
- NotificationChannel.NEWS_ID,
- mContext.getString(R.string.news_notification_channel_label),
- IMPORTANCE_LOW);
- p.channels.put(channel.getId(), channel);
- }
-
- if (!p.channels.containsKey(NotificationChannel.SOCIAL_MEDIA_ID)) {
- NotificationChannel channel = new NotificationChannel(
- NotificationChannel.SOCIAL_MEDIA_ID,
- mContext.getString(R.string.social_notification_channel_label),
- IMPORTANCE_LOW);
- p.channels.put(channel.getId(), channel);
+ private NotificationChannel addReservedChannelLocked(PackagePreferences p, String channelId) {
+ String label = "";
+ switch (channelId) {
+ case PROMOTIONS_ID:
+ label = mContext.getString(R.string.promotional_notification_channel_label);
+ break;
+ case RECS_ID:
+ label = mContext.getString(R.string.recs_notification_channel_label);
+ break;
+ case NEWS_ID:
+ label = mContext.getString(R.string.news_notification_channel_label);
+ break;
+ case SOCIAL_MEDIA_ID:
+ label = mContext.getString(R.string.social_notification_channel_label);
+ break;
}
+ NotificationChannel channel = new NotificationChannel(channelId, label, IMPORTANCE_LOW);
+ p.channels.put(channelId, channel);
+ return channel;
}
public void writeXml(TypedXmlSerializer out, boolean forBackup, int userId) throws IOException {
@@ -1078,7 +1067,7 @@ public class PreferencesHelper implements RankingConfig {
if (channel.getGroup() != null && !r.groups.containsKey(channel.getGroup())) {
throw new IllegalArgumentException("NotificationChannelGroup doesn't exist");
}
- if (NotificationChannel.DEFAULT_CHANNEL_ID.equals(channel.getId())) {
+ if (DEFAULT_CHANNEL_ID.equals(channel.getId())) {
throw new IllegalArgumentException("Reserved id");
}
// Only the user can update bundle channel settings
@@ -1411,6 +1400,54 @@ public class PreferencesHelper implements RankingConfig {
}
}
+ private @Nullable String getChannelIdForBundleType(@Adjustment.Types int type) {
+ switch (type) {
+ case TYPE_CONTENT_RECOMMENDATION:
+ return RECS_ID;
+ case TYPE_NEWS:
+ return NEWS_ID;
+ case TYPE_PROMOTION:
+ return PROMOTIONS_ID;
+ case TYPE_SOCIAL_MEDIA:
+ return SOCIAL_MEDIA_ID;
+ }
+ return null;
+ }
+
+ @FlaggedApi(android.service.notification.Flags.FLAG_NOTIFICATION_CLASSIFICATION)
+ public NotificationChannel getReservedChannel(String pkg, int uid,
+ @Adjustment.Types int type) {
+ if (!notificationClassification()) {
+ return null;
+ }
+ Objects.requireNonNull(pkg);
+ String channelId = getChannelIdForBundleType(type);
+ if (channelId == null) {
+ return null;
+ }
+ NotificationChannel channel =
+ getConversationNotificationChannel(pkg, uid, channelId, null, true, false);
+ return channel;
+ }
+
+ @FlaggedApi(android.service.notification.Flags.FLAG_NOTIFICATION_CLASSIFICATION)
+ public NotificationChannel createReservedChannel(String pkg, int uid,
+ @Adjustment.Types int type) {
+ if (!notificationClassification()) {
+ return null;
+ }
+ Objects.requireNonNull(pkg);
+ PackagePreferences r = getOrCreatePackagePreferencesLocked(pkg, uid);
+ if (r == null) {
+ return null;
+ }
+ String channelId = getChannelIdForBundleType(type);
+ if (channelId == null) {
+ return null;
+ }
+ return addReservedChannelLocked(r, channelId);
+ }
+
@Override
public NotificationChannel getNotificationChannel(String pkg, int uid, String channelId,
boolean includeDeleted) {
@@ -1429,7 +1466,7 @@ public class PreferencesHelper implements RankingConfig {
return null;
}
if (channelId == null) {
- channelId = NotificationChannel.DEFAULT_CHANNEL_ID;
+ channelId = DEFAULT_CHANNEL_ID;
}
NotificationChannel channel = null;
if (conversationId != null) {
@@ -1540,7 +1577,7 @@ public class PreferencesHelper implements RankingConfig {
int N = r.channels.size() - 1;
for (int i = N; i >= 0; i--) {
String key = r.channels.keyAt(i);
- if (!NotificationChannel.DEFAULT_CHANNEL_ID.equals(key)) {
+ if (!DEFAULT_CHANNEL_ID.equals(key)) {
r.channels.remove(key);
}
}
@@ -1658,10 +1695,7 @@ public class PreferencesHelper implements RankingConfig {
&& (activeChannelFilter == null
|| (includeBlocked && nc.getImportance() == IMPORTANCE_NONE)
|| activeChannelFilter.contains(nc.getId()))
- && !PROMOTIONS_ID.equals(nc.getId())
- && !NEWS_ID.equals(nc.getId())
- && !SOCIAL_MEDIA_ID.equals(nc.getId())
- && !RECS_ID.equals(nc.getId());
+ && !SYSTEM_RESERVED_IDS.contains(nc.getId());
if (includeChannel) {
if (nc.getGroup() != null) {
if (r.groups.get(nc.getGroup()) != null) {
@@ -1924,9 +1958,23 @@ public class PreferencesHelper implements RankingConfig {
public boolean onlyHasDefaultChannel(String pkg, int uid) {
synchronized (mLock) {
PackagePreferences r = getOrCreatePackagePreferencesLocked(pkg, uid);
- if (r.channels.size() == (notificationClassification() ? 5 : 1)
- && r.channels.containsKey(NotificationChannel.DEFAULT_CHANNEL_ID)) {
- return true;
+ if (r.channels.containsKey(DEFAULT_CHANNEL_ID)) {
+ if (r.channels.size() == 1) {
+ return true;
+ }
+ if (notificationClassification()) {
+ if (r.channels.size() <= 5) {
+ for (NotificationChannel c : r.channels.values()) {
+ if (!SYSTEM_RESERVED_IDS.contains(c.getId()) &&
+ !DEFAULT_CHANNEL_ID.equals(c.getId())) {
+ return false;
+ }
+ return true;
+ }
+ } else {
+ return false;
+ }
+ }
}
return false;
}
@@ -2744,9 +2792,9 @@ public class PreferencesHelper implements RankingConfig {
PackagePreferences PackagePreferences = mPackagePreferences.valueAt(i);
if (UserHandle.getUserId(PackagePreferences.uid) == userId) {
if (PackagePreferences.channels.containsKey(
- NotificationChannel.DEFAULT_CHANNEL_ID)) {
+ DEFAULT_CHANNEL_ID)) {
PackagePreferences.channels.get(
- NotificationChannel.DEFAULT_CHANNEL_ID).setName(
+ DEFAULT_CHANNEL_ID).setName(
context.getResources().getString(
R.string.default_notification_channel_label));
}
diff --git a/services/core/java/com/android/server/pm/DefaultCrossProfileIntentFiltersUtils.java b/services/core/java/com/android/server/pm/DefaultCrossProfileIntentFiltersUtils.java
index 5ac883c58f03..9c24abcef123 100644
--- a/services/core/java/com/android/server/pm/DefaultCrossProfileIntentFiltersUtils.java
+++ b/services/core/java/com/android/server/pm/DefaultCrossProfileIntentFiltersUtils.java
@@ -316,6 +316,8 @@ public class DefaultCrossProfileIntentFiltersUtils {
/* letsPersonalDataIntoProfile= */ true)
.addAction(MediaStore.ACTION_IMAGE_CAPTURE)
.addAction(MediaStore.ACTION_IMAGE_CAPTURE_SECURE)
+ .addAction(MediaStore.ACTION_MOTION_PHOTO_CAPTURE)
+ .addAction(MediaStore.ACTION_MOTION_PHOTO_CAPTURE_SECURE)
.addAction(MediaStore.ACTION_VIDEO_CAPTURE)
.addAction(MediaStore.Audio.Media.RECORD_SOUND_ACTION)
.addAction(MediaStore.INTENT_ACTION_STILL_IMAGE_CAMERA)
@@ -438,6 +440,8 @@ public class DefaultCrossProfileIntentFiltersUtils {
/* letsPersonalDataIntoProfile= */ false)
.addAction(MediaStore.ACTION_IMAGE_CAPTURE)
.addAction(MediaStore.ACTION_IMAGE_CAPTURE_SECURE)
+ .addAction(MediaStore.ACTION_MOTION_PHOTO_CAPTURE)
+ .addAction(MediaStore.ACTION_MOTION_PHOTO_CAPTURE_SECURE)
.addAction(MediaStore.ACTION_VIDEO_CAPTURE)
.addAction(MediaStore.Audio.Media.RECORD_SOUND_ACTION)
.addAction(MediaStore.INTENT_ACTION_STILL_IMAGE_CAMERA)
diff --git a/services/core/java/com/android/server/pm/PackageInstallerService.java b/services/core/java/com/android/server/pm/PackageInstallerService.java
index f6a808b6c33e..a59f4bd8abe2 100644
--- a/services/core/java/com/android/server/pm/PackageInstallerService.java
+++ b/services/core/java/com/android/server/pm/PackageInstallerService.java
@@ -39,6 +39,7 @@ import static org.xmlpull.v1.XmlPullParser.END_DOCUMENT;
import static org.xmlpull.v1.XmlPullParser.START_TAG;
import android.Manifest;
+import android.annotation.EnforcePermission;
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.app.ActivityManager;
@@ -87,6 +88,7 @@ import android.os.HandlerThread;
import android.os.Looper;
import android.os.Message;
import android.os.ParcelableException;
+import android.os.PermissionEnforcer;
import android.os.Process;
import android.os.RemoteCallback;
import android.os.RemoteCallbackList;
@@ -314,6 +316,8 @@ public class PackageInstallerService extends IPackageInstaller.Stub implements
public PackageInstallerService(Context context, PackageManagerService pm,
Supplier<PackageParser2> apexParserSupplier) {
+ super(PermissionEnforcer.fromContext(context));
+
mContext = context;
mPm = pm;
@@ -1877,23 +1881,20 @@ public class PackageInstallerService extends IPackageInstaller.Stub implements
}
@Override
+ @EnforcePermission(android.Manifest.permission.VERIFICATION_AGENT)
public @PackageInstaller.VerificationPolicy int getVerificationPolicy() {
- if (mContext.checkCallingOrSelfPermission(Manifest.permission.VERIFICATION_AGENT)
- != PackageManager.PERMISSION_GRANTED) {
- throw new SecurityException("You need the "
- + "com.android.permission.VERIFICATION_AGENT permission "
- + "to get the verification policy");
- }
+ getVerificationPolicy_enforcePermission();
return mVerificationPolicy.get();
}
@Override
+ @EnforcePermission(android.Manifest.permission.VERIFICATION_AGENT)
public boolean setVerificationPolicy(@PackageInstaller.VerificationPolicy int policy) {
- if (mContext.checkCallingOrSelfPermission(Manifest.permission.VERIFICATION_AGENT)
- != PackageManager.PERMISSION_GRANTED) {
- throw new SecurityException("You need the "
- + "com.android.permission.VERIFICATION_AGENT permission "
- + "to set the verification policy");
+ setVerificationPolicy_enforcePermission();
+ final int callingUid = getCallingUid();
+ // Only the verifier currently bound by the system can change the policy, except for Shell
+ if (!PackageManagerServiceUtils.isRootOrShell(callingUid)) {
+ mVerifierController.assertCallerIsCurrentVerifier(callingUid);
}
if (!isValidVerificationPolicy(policy)) {
return false;
diff --git a/services/core/java/com/android/server/pm/PackageInstallerSession.java b/services/core/java/com/android/server/pm/PackageInstallerSession.java
index 9a9e434c32d3..6ea5369040fe 100644
--- a/services/core/java/com/android/server/pm/PackageInstallerSession.java
+++ b/services/core/java/com/android/server/pm/PackageInstallerSession.java
@@ -2884,14 +2884,13 @@ public class PackageInstallerSession extends IPackageInstallerSession.Stub {
}
// Send the request to the verifier and wait for its response before the rest of
// the installation can proceed.
+ final VerifierCallback verifierCallback = new VerifierCallback();
if (!mVerifierController.startVerificationSession(mPm::snapshotComputer, userId,
sessionId, getPackageName(), Uri.fromFile(stageDir), signingInfo,
declaredLibraries, mVerificationPolicy.get(), /* extensionParams= */ null,
- new VerifierCallback(), /* retry= */ false)) {
- // A verifier is installed but cannot be connected. Installation disallowed.
- onSessionVerificationFailure(INSTALL_FAILED_INTERNAL_ERROR,
- "A verifier agent is available on device but cannot be connected.",
- /* extras= */ null);
+ verifierCallback, /* retry= */ false)) {
+ // A verifier is installed but cannot be connected.
+ verifierCallback.onConnectionFailed();
}
} else {
// No need to check with verifier. Proceed with the rest of the verification.
@@ -2995,7 +2994,6 @@ public class PackageInstallerSession extends IPackageInstallerSession.Stub {
onSessionVerificationFailure(INSTALL_FAILED_VERIFICATION_FAILURE,
"A verifier agent is available on device but cannot be connected.",
bundle);
-
});
}
/**
@@ -4447,9 +4445,7 @@ public class PackageInstallerSession extends IPackageInstallerSession.Stub {
* @return the uid of the owner this session
*/
public int getInstallerUid() {
- synchronized (mLock) {
- return mInstallerUid;
- }
+ return mInstallerUid;
}
/**
diff --git a/services/core/java/com/android/server/pm/PackageManagerService.java b/services/core/java/com/android/server/pm/PackageManagerService.java
index d78f12217271..807445ef062d 100644
--- a/services/core/java/com/android/server/pm/PackageManagerService.java
+++ b/services/core/java/com/android/server/pm/PackageManagerService.java
@@ -4310,6 +4310,10 @@ public class PackageManagerService implements PackageSender, TestUtilityService
if (intent == null) {
return;
}
+ final String action = intent.getAction();
+ if (action == null) {
+ return;
+ }
Uri data = intent.getData();
if (data == null) {
return;
diff --git a/services/core/java/com/android/server/pm/PackageManagerShellCommand.java b/services/core/java/com/android/server/pm/PackageManagerShellCommand.java
index 4652c3af1185..f8e56e11735f 100644
--- a/services/core/java/com/android/server/pm/PackageManagerShellCommand.java
+++ b/services/core/java/com/android/server/pm/PackageManagerShellCommand.java
@@ -399,6 +399,10 @@ class PackageManagerShellCommand extends ShellCommand {
return runUnarchive();
case "get-domain-verification-agent":
return runGetDomainVerificationAgent();
+ case "get-verification-policy":
+ return runGetVerificationPolicy();
+ case "set-verification-policy":
+ return runSetVerificationPolicy();
default: {
if (ART_SERVICE_COMMANDS.contains(cmd)) {
return runArtServiceCommand();
@@ -4645,6 +4649,86 @@ class PackageManagerShellCommand extends ShellCommand {
return 0;
}
+ private int runGetVerificationPolicy() throws RemoteException {
+ final PrintWriter pw = getOutPrintWriter();
+ int userId = UserHandle.USER_ALL;
+
+ String opt;
+ while ((opt = getNextOption()) != null) {
+ if (opt.equals("--user")) {
+ userId = UserHandle.parseUserArg(getNextArgRequired());
+ if (userId != UserHandle.USER_ALL && userId != UserHandle.USER_CURRENT) {
+ UserManagerInternal umi =
+ LocalServices.getService(UserManagerInternal.class);
+ UserInfo userInfo = umi.getUserInfo(userId);
+ if (userInfo == null) {
+ pw.println("Failure [user " + userId + " doesn't exist]");
+ return 1;
+ }
+ }
+ } else {
+ pw.println("Error: Unknown option: " + opt);
+ return 1;
+ }
+ }
+ final int translatedUserId =
+ translateUserId(userId, UserHandle.USER_SYSTEM, "runGetVerificationPolicy");
+ try {
+ final IPackageInstaller installer = mInterface.getPackageInstaller();
+ // TODO(b/360129657): global verification policy should be per user
+ final int policy = installer.getVerificationPolicy();
+ pw.println(policy);
+ } catch (Exception e) {
+ pw.println("Failure [" + e.getMessage() + "]");
+ return 1;
+ }
+ return 0;
+ }
+
+ private int runSetVerificationPolicy() throws RemoteException {
+ final PrintWriter pw = getOutPrintWriter();
+ int userId = UserHandle.USER_ALL;
+
+ String opt;
+ while ((opt = getNextOption()) != null) {
+ if (opt.equals("--user")) {
+ userId = UserHandle.parseUserArg(getNextArgRequired());
+ if (userId != UserHandle.USER_ALL && userId != UserHandle.USER_CURRENT) {
+ UserManagerInternal umi =
+ LocalServices.getService(UserManagerInternal.class);
+ UserInfo userInfo = umi.getUserInfo(userId);
+ if (userInfo == null) {
+ pw.println("Failure [user " + userId + " doesn't exist]");
+ return 1;
+ }
+ }
+ } else {
+ pw.println("Error: Unknown option: " + opt);
+ return 1;
+ }
+ }
+ final String policyStr = getNextArg();
+ if (policyStr == null) {
+ pw.println("Error: policy not specified");
+ return 1;
+ }
+ final int translatedUserId =
+ translateUserId(userId, UserHandle.USER_SYSTEM, "runSetVerificationPolicy");
+ try {
+ final IPackageInstaller installer = mInterface.getPackageInstaller();
+ // TODO(b/360129657): global verification policy should be per user
+ final boolean success = installer.setVerificationPolicy(Integer.parseInt(policyStr));
+ if (!success) {
+ pw.println("Failure setting verification policy.");
+ return 1;
+ }
+ } catch (Exception e) {
+ pw.println("Failure [" + e.getMessage() + "]");
+ return 1;
+ }
+ return 0;
+ }
+
@Override
public void onHelp() {
final PrintWriter pw = getOutPrintWriter();
@@ -5073,6 +5157,14 @@ class PackageManagerShellCommand extends ShellCommand {
pw.println(" --user: return the agent of the given user (SYSTEM_USER if unspecified)");
pw.println(" get-package-storage-stats [--user <USER_ID>] <PACKAGE>");
pw.println(" Return the storage stats for the given app, if present");
+ pw.println(" get-verification-policy [--user USER_ID]");
+ pw.println(" Display current verification enforcement policy which will be applied to");
+ pw.println(" all the future installation sessions");
+ pw.println(" --user: show the policy of the given user (SYSTEM_USER if unspecified)");
+ pw.println(" set-verification-policy POLICY [--user USER_ID]");
+ pw.println(" Sets the verification policy of all the future installation sessions.");
+ pw.println(" --user: set the policy of the given user (SYSTEM_USER if unspecified)");
+ pw.println("");
pw.println("");
printArtServiceHelp();
pw.println("");
diff --git a/services/core/java/com/android/server/pm/verify/pkg/VerifierController.java b/services/core/java/com/android/server/pm/verify/pkg/VerifierController.java
index a35618b309bb..78849d286ebe 100644
--- a/services/core/java/com/android/server/pm/verify/pkg/VerifierController.java
+++ b/services/core/java/com/android/server/pm/verify/pkg/VerifierController.java
@@ -18,13 +18,12 @@ package com.android.server.pm.verify.pkg;
import static android.content.pm.PackageManager.MATCH_DIRECT_BOOT_AWARE;
import static android.content.pm.PackageManager.MATCH_DIRECT_BOOT_UNAWARE;
+import static android.os.Process.INVALID_UID;
import static android.os.Process.SYSTEM_UID;
import static android.provider.DeviceConfig.NAMESPACE_PACKAGE_MANAGER_SERVICE;
-import android.Manifest;
import android.annotation.NonNull;
import android.annotation.Nullable;
-import android.annotation.RequiresPermission;
import android.annotation.SuppressLint;
import android.content.ComponentName;
import android.content.Context;
@@ -114,10 +113,17 @@ public class VerifierController {
private final Context mContext;
private final Handler mHandler;
+ // Guards the remote service object, as well as the verifier name and UID, which should all be
+ // changed at the same time.
+ private final Object mLock = new Object();
@Nullable
+ @GuardedBy("mLock")
private ServiceConnector<IVerifierService> mRemoteService;
@Nullable
+ @GuardedBy("mLock")
private ComponentName mRemoteServiceComponentName;
+ @GuardedBy("mLock")
+ private int mRemoteServiceUid = INVALID_UID;
@NonNull
private Injector mInjector;
@@ -143,9 +149,11 @@ public class VerifierController {
*/
@Nullable
public String getVerifierPackageName(Supplier<Computer> snapshotSupplier, int userId) {
- if (isVerifierConnected()) {
- // Verifier is connected or is being connected, so it must be installed.
- return mRemoteServiceComponentName.getPackageName();
+ synchronized (mLock) {
+ if (isVerifierConnectedLocked()) {
+ // Verifier is connected or is being connected, so it must be installed.
+ return mRemoteServiceComponentName.getPackageName();
+ }
}
// Verifier has been disconnected, or it hasn't been connected. Check if it's installed.
return mInjector.getVerifierPackageName(snapshotSupplier.get(), userId);
@@ -178,16 +186,29 @@ public class VerifierController {
}
return true;
}
+ Computer snapshot = snapshotSupplier.get();
Pair<ServiceConnector<IVerifierService>, ComponentName> result =
- mInjector.getRemoteService(snapshotSupplier.get(), mContext, userId, mHandler);
+ mInjector.getRemoteService(snapshot, mContext, userId, mHandler);
if (result == null || result.first == null) {
if (DEBUG) {
Slog.i(TAG, "Unable to find a qualified verifier.");
}
return false;
}
- mRemoteService = result.first;
- mRemoteServiceComponentName = result.second;
+ final int verifierUid = snapshot.getPackageUidInternal(
+ result.second.getPackageName(), 0, userId, /* callingUid= */ SYSTEM_UID);
+ if (verifierUid == INVALID_UID) {
+ if (DEBUG) {
+ Slog.i(TAG, "Unable to find the UID of the qualified verifier.");
+ }
+ return false;
+ }
+ synchronized (mLock) {
+ mRemoteService = result.first;
+ mRemoteServiceComponentName = result.second;
+ mRemoteServiceUid = verifierUid;
+ }
+
if (DEBUG) {
Slog.i(TAG, "Connecting to a qualified verifier: " + mRemoteServiceComponentName);
}
@@ -212,10 +233,13 @@ public class VerifierController {
}
private void destroy() {
- if (isVerifierConnected()) {
- mRemoteService.unbind();
- mRemoteService = null;
- mRemoteServiceComponentName = null;
+ synchronized (mLock) {
+ if (isVerifierConnectedLocked()) {
+ mRemoteService.unbind();
+ mRemoteService = null;
+ mRemoteServiceComponentName = null;
+ mRemoteServiceUid = INVALID_UID;
+ }
}
}
});
@@ -223,7 +247,8 @@ public class VerifierController {
return true;
}
- private boolean isVerifierConnected() {
+ @GuardedBy("mLock")
+ private boolean isVerifierConnectedLocked() {
return mRemoteService != null && mRemoteServiceComponentName != null;
}
@@ -232,19 +257,21 @@ public class VerifierController {
* requested for verification.
*/
public void notifyPackageNameAvailable(@NonNull String packageName) {
- if (!isVerifierConnected()) {
- if (DEBUG) {
- Slog.i(TAG, "Verifier is not connected. Not notifying package name available");
+ synchronized (mLock) {
+ if (!isVerifierConnectedLocked()) {
+ if (DEBUG) {
+ Slog.i(TAG, "Verifier is not connected. Not notifying package name available");
+ }
+ return;
}
- return;
+ // Best effort. We don't check for the result.
+ mRemoteService.run(service -> {
+ if (DEBUG) {
+ Slog.i(TAG, "Notifying package name available for " + packageName);
+ }
+ service.onPackageNameAvailable(packageName);
+ });
}
- // Best effort. We don't check for the result.
- mRemoteService.run(service -> {
- if (DEBUG) {
- Slog.i(TAG, "Notifying package name available for " + packageName);
- }
- service.onPackageNameAvailable(packageName);
- });
}
/**
@@ -253,27 +280,29 @@ public class VerifierController {
* will no longer be requested for verification, possibly because the installation is canceled.
*/
public void notifyVerificationCancelled(@NonNull String packageName) {
- if (!isVerifierConnected()) {
- if (DEBUG) {
- Slog.i(TAG, "Verifier is not connected. Not notifying verification cancelled");
+ synchronized (mLock) {
+ if (!isVerifierConnectedLocked()) {
+ if (DEBUG) {
+ Slog.i(TAG, "Verifier is not connected. Not notifying verification cancelled");
+ }
+ return;
}
- return;
+ // Best effort. We don't check for the result.
+ mRemoteService.run(service -> {
+ if (DEBUG) {
+ Slog.i(TAG, "Notifying verification cancelled for " + packageName);
+ }
+ service.onVerificationCancelled(packageName);
+ });
}
- // Best effort. We don't check for the result.
- mRemoteService.run(service -> {
- if (DEBUG) {
- Slog.i(TAG, "Notifying verification cancelled for " + packageName);
- }
- service.onVerificationCancelled(packageName);
- });
}
/**
* Called to notify the bound verifier agent that a package that's pending installation needs
* to be verified right now.
* <p>The verification request must be sent to the verifier as soon as the verifier is
- * connected. If the connection cannot be made within {@link #CONNECTION_TIMEOUT_SECONDS}</p>
- * of when the request is sent out, we consider the verification to be failed and notify the
+ * connected. If the connection cannot be made within the specified time limit from
+ * when the request is sent out, we consider the verification to be failed and notify the
* installation session.</p>
* <p>If a response is not returned from the verifier agent within a timeout duration from the
* time the request is sent to the verifier, the verification will be considered a failure.</p>
@@ -291,43 +320,48 @@ public class VerifierController {
if (!bindToVerifierServiceIfNeeded(snapshotSupplier, userId)) {
return false;
}
- if (!isVerifierConnected()) {
- if (DEBUG) {
- Slog.i(TAG, "Verifier is not connected. Not notifying verification required");
- }
- // Normally this should not happen because we just tried to bind. But if the verifier
- // just crashed or just became unavailable, we should notify the installation session so
- // it can finish with a verification failure.
- return false;
- }
// For now, the verification id is the same as the installation session id.
final int verificationId = installationSessionId;
- final VerificationSession session = new VerificationSession(
- /* id= */ verificationId,
- /* installSessionId= */ installationSessionId,
- packageName, stagedPackageUri, signingInfo, declaredLibraries, extensionParams,
- verificationPolicy, new VerificationSessionInterface(callback));
- AndroidFuture<Void> unusedFuture = mRemoteService.post(service -> {
- if (!retry) {
- if (DEBUG) {
- Slog.i(TAG, "Notifying verification required for session " + verificationId);
- }
- service.onVerificationRequired(session);
- } else {
+ synchronized (mLock) {
+ if (!isVerifierConnectedLocked()) {
if (DEBUG) {
- Slog.i(TAG, "Notifying verification retry for session " + verificationId);
+ Slog.i(TAG, "Verifier is not connected. Not notifying verification required");
}
- service.onVerificationRetry(session);
- }
- }).orTimeout(mInjector.getVerifierConnectionTimeoutMillis(), TimeUnit.MILLISECONDS)
- .whenComplete((res, err) -> {
- if (err != null) {
- Slog.e(TAG, "Error notifying verification request for session " + verificationId,
- err);
- // Notify the installation session so it can finish with verification failure.
- callback.onConnectionFailed();
+ // Normally this should not happen because we just tried to bind. But if the
+ // verifier just crashed or just became unavailable, we should notify the
+ // installation session so it can finish with a verification failure.
+ return false;
}
- });
+ final VerificationSession session = new VerificationSession(
+ /* id= */ verificationId,
+ /* installSessionId= */ installationSessionId,
+ packageName, stagedPackageUri, signingInfo, declaredLibraries, extensionParams,
+ verificationPolicy, new VerificationSessionInterface(callback));
+ AndroidFuture<Void> unusedFuture = mRemoteService.post(service -> {
+ if (!retry) {
+ if (DEBUG) {
+ Slog.i(TAG, "Notifying verification required for session "
+ + verificationId);
+ }
+ service.onVerificationRequired(session);
+ } else {
+ if (DEBUG) {
+ Slog.i(TAG, "Notifying verification retry for session "
+ + verificationId);
+ }
+ service.onVerificationRetry(session);
+ }
+ }).orTimeout(mInjector.getVerifierConnectionTimeoutMillis(), TimeUnit.MILLISECONDS)
+ .whenComplete((res, err) -> {
+ if (err != null) {
+ Slog.e(TAG, "Error notifying verification request for session "
+ + verificationId, err);
+ // Notify the installation session so it can finish with verification
+ // failure.
+ callback.onConnectionFailed();
+ }
+ });
+ }
// Keep track of the session status with the ID. Start counting down the session timeout.
final long defaultTimeoutMillis = mInjector.getVerificationRequestTimeoutMillis();
final long maxExtendedTimeoutMillis = mInjector.getMaxVerificationExtendedTimeoutMillis();
@@ -369,24 +403,27 @@ public class VerifierController {
* Called to notify the bound verifier agent that a verification request has timed out.
*/
public void notifyVerificationTimeout(int verificationId) {
- if (!isVerifierConnected()) {
- if (DEBUG) {
- Slog.i(TAG,
- "Verifier is not connected. Not notifying timeout for " + verificationId);
+ synchronized (mLock) {
+ if (!isVerifierConnectedLocked()) {
+ if (DEBUG) {
+ Slog.i(TAG,
+ "Verifier is not connected. Not notifying timeout for "
+ + verificationId);
+ }
+ return;
}
- return;
+ AndroidFuture<Void> unusedFuture = mRemoteService.post(service -> {
+ if (DEBUG) {
+ Slog.i(TAG, "Notifying timeout for " + verificationId);
+ }
+ service.onVerificationTimeout(verificationId);
+ }).whenComplete((res, err) -> {
+ if (err != null) {
+ Slog.e(TAG, "Error notifying VerificationTimeout for session "
+ + verificationId, err);
+ }
+ });
}
- AndroidFuture<Void> unusedFuture = mRemoteService.post(service -> {
- if (DEBUG) {
- Slog.i(TAG, "Notifying timeout for " + verificationId);
- }
- service.onVerificationTimeout(verificationId);
- }).whenComplete((res, err) -> {
- if (err != null) {
- Slog.e(TAG, "Error notifying VerificationTimeout for session "
- + verificationId, (Throwable) err);
- }
- });
}
/**
@@ -405,17 +442,19 @@ public class VerifierController {
}
}
- @RequiresPermission(Manifest.permission.VERIFICATION_AGENT)
- private void checkCallerPermission() {
- // TODO: think of a better way to test it on non-eng builds
- if (Build.IS_ENG) {
- return;
- }
- if (mContext.checkCallingOrSelfPermission(Manifest.permission.VERIFICATION_AGENT)
- != PackageManager.PERMISSION_GRANTED) {
- throw new SecurityException("You need the"
- + " com.android.permission.VERIFICATION_AGENT permission"
- + " to use VerificationSession APIs.");
+ /**
+ * Assert that the calling UID is the same as the UID of the currently connected verifier.
+ */
+ public void assertCallerIsCurrentVerifier(int callingUid) {
+ synchronized (mLock) {
+ if (!isVerifierConnectedLocked()) {
+ throw new IllegalStateException(
+ "Unable to proceed because the verifier has been disconnected.");
+ }
+ if (callingUid != mRemoteServiceUid) {
+ throw new IllegalStateException(
+ "Calling uid " + callingUid + " is not the current verifier.");
+ }
}
}
@@ -429,7 +468,7 @@ public class VerifierController {
@Override
public long getTimeoutTime(int verificationId) {
- checkCallerPermission();
+ assertCallerIsCurrentVerifier(getCallingUid());
synchronized (mVerificationStatus) {
final VerificationStatusTracker tracker = mVerificationStatus.get(verificationId);
if (tracker == null) {
@@ -442,7 +481,7 @@ public class VerifierController {
@Override
public long extendTimeRemaining(int verificationId, long additionalMs) {
- checkCallerPermission();
+ assertCallerIsCurrentVerifier(getCallingUid());
synchronized (mVerificationStatus) {
final VerificationStatusTracker tracker = mVerificationStatus.get(verificationId);
if (tracker == null) {
@@ -456,7 +495,7 @@ public class VerifierController {
@Override
public boolean setVerificationPolicy(int verificationId,
@PackageInstaller.VerificationPolicy int policy) {
- checkCallerPermission();
+ assertCallerIsCurrentVerifier(getCallingUid());
synchronized (mVerificationStatus) {
final VerificationStatusTracker tracker = mVerificationStatus.get(verificationId);
if (tracker == null) {
@@ -469,7 +508,7 @@ public class VerifierController {
@Override
public void reportVerificationIncomplete(int id, int reason) {
- checkCallerPermission();
+ assertCallerIsCurrentVerifier(getCallingUid());
final VerificationStatusTracker tracker;
synchronized (mVerificationStatus) {
tracker = mVerificationStatus.get(id);
@@ -484,15 +523,9 @@ public class VerifierController {
}
@Override
- public void reportVerificationComplete(int id, VerificationStatus verificationStatus) {
- reportVerificationCompleteWithExtensionResponse(id, verificationStatus,
- /* extensionResponse= */ null);
- }
-
- @Override
- public void reportVerificationCompleteWithExtensionResponse(int id,
- VerificationStatus verificationStatus, PersistableBundle extensionResponse) {
- checkCallerPermission();
+ public void reportVerificationComplete(int id, VerificationStatus verificationStatus,
+ @Nullable PersistableBundle extensionResponse) {
+ assertCallerIsCurrentVerifier(getCallingUid());
final VerificationStatusTracker tracker;
synchronized (mVerificationStatus) {
tracker = mVerificationStatus.get(id);
diff --git a/services/core/java/com/android/server/policy/ModifierShortcutManager.java b/services/core/java/com/android/server/policy/ModifierShortcutManager.java
index 66ec53e6500e..a1236e533beb 100644
--- a/services/core/java/com/android/server/policy/ModifierShortcutManager.java
+++ b/services/core/java/com/android/server/policy/ModifierShortcutManager.java
@@ -31,6 +31,7 @@ import android.content.pm.ActivityInfo;
import android.content.pm.PackageManager;
import android.content.res.XmlResourceParser;
import android.graphics.drawable.Icon;
+import android.hardware.input.AppLaunchData;
import android.hardware.input.KeyGestureEvent;
import android.os.Handler;
import android.os.RemoteException;
@@ -48,6 +49,7 @@ import android.view.KeyboardShortcutGroup;
import android.view.KeyboardShortcutInfo;
import com.android.internal.R;
+import com.android.internal.annotations.GuardedBy;
import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.policy.IShortcutService;
import com.android.internal.util.XmlUtils;
@@ -63,6 +65,7 @@ import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
+import java.util.Objects;
import java.util.Set;
/**
@@ -131,7 +134,10 @@ public class ModifierShortcutManager {
private boolean mConsumeSearchKeyUp = true;
private UserHandle mCurrentUser;
private final Map<Pair<Character, Boolean>, Bookmark> mBookmarks = new HashMap<>();
+ @GuardedBy("mAppIntentCache")
+ private final Map<AppLaunchData, Intent> mAppIntentCache = new HashMap<>();
+ @SuppressLint("MissingPermission")
ModifierShortcutManager(Context context, Handler handler, UserHandle currentUser) {
mContext = context;
mHandler = handler;
@@ -146,6 +152,17 @@ public class ModifierShortcutManager {
} else {
mRoleIntents.remove(roleName);
}
+ synchronized (mAppIntentCache) {
+ mAppIntentCache.entrySet().removeIf(
+ entry -> {
+ if (entry.getKey() instanceof AppLaunchData.RoleData) {
+ return Objects.equals(
+ ((AppLaunchData.RoleData) entry.getKey()).getRole(),
+ roleName);
+ }
+ return false;
+ });
+ }
}, UserHandle.ALL);
mCurrentUser = currentUser;
mInputManagerInternal = LocalServices.getService(InputManagerInternal.class);
@@ -159,6 +176,10 @@ public class ModifierShortcutManager {
// so clear the cache.
clearRoleIntents();
clearComponentIntents();
+
+ synchronized (mAppIntentCache) {
+ mAppIntentCache.clear();
+ }
}
void clearRoleIntents() {
@@ -748,6 +769,46 @@ public class ModifierShortcutManager {
shortcuts);
}
+ private Intent getIntentFromAppLaunchData(@NonNull AppLaunchData data) {
+ Context context = mContext.createContextAsUser(mCurrentUser, 0);
+ synchronized (mAppIntentCache) {
+ Intent intent = mAppIntentCache.get(data);
+ if (intent != null) {
+ return intent;
+ }
+ if (data instanceof AppLaunchData.CategoryData) {
+ intent = Intent.makeMainSelectorActivity(Intent.ACTION_MAIN,
+ ((AppLaunchData.CategoryData) data).getCategory());
+ } else if (data instanceof AppLaunchData.RoleData) {
+ intent = getRoleLaunchIntent(context, ((AppLaunchData.RoleData) data).getRole());
+ } else if (data instanceof AppLaunchData.ComponentData) {
+ AppLaunchData.ComponentData componentData = (AppLaunchData.ComponentData) data;
+ intent = resolveComponentNameIntent(context, componentData.getPackageName(),
+ componentData.getClassName());
+ }
+ if (intent != null) {
+ mAppIntentCache.put(data, intent);
+ }
+ return intent;
+ }
+ }
+
+ boolean launchApplication(@NonNull AppLaunchData data) {
+ Intent intent = getIntentFromAppLaunchData(data);
+ if (intent == null) {
+ return false;
+ }
+ intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
+ try {
+ mContext.startActivityAsUser(intent, mCurrentUser);
+ return true;
+ } catch (ActivityNotFoundException ex) {
+ Slog.w(TAG, "Not launching app because "
+ + "the activity to which it refers to was not found: " + data);
+ }
+ return false;
+ }
+
/**
* Given an intent to launch an application and the character and shift state that should
* trigger it, return a suitable {@link KeyboardShortcutInfo} that contains the label and
@@ -869,7 +930,7 @@ public class ModifierShortcutManager {
// TODO(b/280423320): Add new field package name associated in the
// KeyboardShortcutEvent atom and log it accordingly.
- return KeyGestureEvent.KEY_GESTURE_TYPE_LAUNCH_APPLICATION_BY_PACKAGE_NAME;
+ return KeyGestureEvent.KEY_GESTURE_TYPE_LAUNCH_APPLICATION;
}
@KeyGestureEvent.KeyGestureType
diff --git a/services/core/java/com/android/server/policy/PhoneWindowManager.java b/services/core/java/com/android/server/policy/PhoneWindowManager.java
index 6a7f22e7b06c..3ab1009b22e1 100644
--- a/services/core/java/com/android/server/policy/PhoneWindowManager.java
+++ b/services/core/java/com/android/server/policy/PhoneWindowManager.java
@@ -149,6 +149,7 @@ import android.hardware.hdmi.HdmiAudioSystemClient;
import android.hardware.hdmi.HdmiControlManager;
import android.hardware.hdmi.HdmiPlaybackClient;
import android.hardware.hdmi.HdmiPlaybackClient.OneTouchPlayCallback;
+import android.hardware.input.AppLaunchData;
import android.hardware.input.InputManager;
import android.hardware.input.KeyGestureEvent;
import android.media.AudioManager;
@@ -4037,6 +4038,7 @@ public class PhoneWindowManager implements WindowManagerPolicy {
case KeyGestureEvent.KEY_GESTURE_TYPE_LANGUAGE_SWITCH:
case KeyGestureEvent.KEY_GESTURE_TYPE_ACCESSIBILITY_SHORTCUT:
case KeyGestureEvent.KEY_GESTURE_TYPE_CLOSE_ALL_DIALOGS:
+ case KeyGestureEvent.KEY_GESTURE_TYPE_LAUNCH_APPLICATION:
return true;
case KeyGestureEvent.KEY_GESTURE_TYPE_SCREENSHOT_CHORD:
case KeyGestureEvent.KEY_GESTURE_TYPE_RINGER_TOGGLE_CHORD:
@@ -4279,6 +4281,14 @@ public class PhoneWindowManager implements WindowManagerPolicy {
return true;
}
break;
+ case KeyGestureEvent.KEY_GESTURE_TYPE_LAUNCH_APPLICATION:
+ AppLaunchData data = event.getAppLaunchData();
+ if (complete && isUserSetupComplete() && !keyguardOn
+ && data != null && mModifierShortcutManager.launchApplication(data)) {
+ dismissKeyboardShortcutsMenu();
+ return true;
+ }
+ break;
}
return false;
}
@@ -6948,6 +6958,7 @@ public class PhoneWindowManager implements WindowManagerPolicy {
if (modifierShortcutManagerMultiuser()) {
mModifierShortcutManager.setCurrentUser(UserHandle.of(newUserId));
}
+ mInputManagerInternal.setCurrentUser(newUserId);
}
@Override
diff --git a/services/core/java/com/android/server/security/forensic/DataAggregator.java b/services/core/java/com/android/server/security/forensic/DataAggregator.java
new file mode 100644
index 000000000000..0079818e733b
--- /dev/null
+++ b/services/core/java/com/android/server/security/forensic/DataAggregator.java
@@ -0,0 +1,142 @@
+/*
+ * 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.
+ */
+
+package com.android.server.security.forensic;
+
+import android.os.Handler;
+import android.os.Looper;
+import android.os.Message;
+import android.security.forensic.ForensicEvent;
+import android.util.Slog;
+
+import com.android.internal.annotations.VisibleForTesting;
+import com.android.server.ServiceThread;
+
+import java.util.ArrayList;
+import java.util.List;
+
+public class DataAggregator {
+ private static final String TAG = "Forensic DataAggregator";
+ private static final int MSG_SINGLE_DATA = 0;
+ private static final int MSG_BATCH_DATA = 1;
+ private static final int MSG_DISABLE = 2;
+
+ private static final int STORED_EVENTS_SIZE_LIMIT = 1024;
+ private final ForensicService mForensicService;
+ private final ArrayList<DataSource> mDataSources;
+
+ private List<ForensicEvent> mStoredEvents = new ArrayList<>();
+ private ServiceThread mHandlerThread;
+ private Handler mHandler;
+ public DataAggregator(ForensicService forensicService) {
+ mForensicService = forensicService;
+ mDataSources = new ArrayList<DataSource>();
+ }
+
+ @VisibleForTesting
+ void setHandler(Looper looper, ServiceThread serviceThread) {
+ mHandlerThread = serviceThread;
+ mHandler = new EventHandler(looper, this);
+ }
+
+ /**
+ * Initialize DataSources
+ * @return Whether the initialization succeeds.
+ */
+ // TODO: Add the corresponding data sources
+ public boolean initialize() {
+ return true;
+ }
+
+ /**
+ * Enable the data collection of all DataSources.
+ */
+ public void enable() {
+ mHandlerThread = new ServiceThread(TAG, android.os.Process.THREAD_PRIORITY_BACKGROUND,
+ /* allowIo */ false);
+ mHandlerThread.start();
+ mHandler = new EventHandler(mHandlerThread.getLooper(), this);
+ for (DataSource ds : mDataSources) {
+ ds.enable();
+ }
+ }
+
+ /**
+ * DataSource calls it to transmit a single event.
+ */
+ public void addSingleData(ForensicEvent event) {
+ mHandler.obtainMessage(MSG_SINGLE_DATA, event).sendToTarget();
+ }
+
+ /**
+ * DataSource calls it to transmit list of events.
+ */
+ public void addBatchData(List<ForensicEvent> events) {
+ mHandler.obtainMessage(MSG_BATCH_DATA, events).sendToTarget();
+ }
+
+ /**
+ * Disable the data collection of all DataSources.
+ */
+ public void disable() {
+ mHandler.obtainMessage(MSG_DISABLE).sendToTarget();
+ }
+
+ private void onNewSingleData(ForensicEvent event) {
+ if (mStoredEvents.size() < STORED_EVENTS_SIZE_LIMIT) {
+ mStoredEvents.add(event);
+ } else {
+ mForensicService.addNewData(mStoredEvents);
+ mStoredEvents = new ArrayList<>();
+ }
+ }
+
+ private void onNewBatchData(List<ForensicEvent> events) {
+ mForensicService.addNewData(events);
+ }
+
+ private void onDisable() {
+ for (DataSource ds : mDataSources) {
+ ds.disable();
+ }
+ mHandlerThread.quitSafely();
+ mHandlerThread = null;
+ }
+
+ private static class EventHandler extends Handler {
+ private final DataAggregator mDataAggregator;
+ EventHandler(Looper looper, DataAggregator dataAggregator) {
+ super(looper);
+ mDataAggregator = dataAggregator;
+ }
+ @Override
+ public void handleMessage(Message msg) {
+ switch (msg.what) {
+ case MSG_SINGLE_DATA:
+ mDataAggregator.onNewSingleData((ForensicEvent) msg.obj);
+ break;
+ case MSG_BATCH_DATA:
+ mDataAggregator.onNewBatchData((List<ForensicEvent>) msg.obj);
+ break;
+ case MSG_DISABLE:
+ mDataAggregator.onDisable();
+ break;
+ default:
+ Slog.w(TAG, "Unknown message: " + msg.what);
+ }
+ }
+ }
+}
diff --git a/services/core/java/com/android/server/security/forensic/DataSource.java b/services/core/java/com/android/server/security/forensic/DataSource.java
new file mode 100644
index 000000000000..da7ee210281b
--- /dev/null
+++ b/services/core/java/com/android/server/security/forensic/DataSource.java
@@ -0,0 +1,29 @@
+/*
+ * 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.
+ */
+
+package com.android.server.security.forensic;
+
+public interface DataSource {
+ /**
+ * Enable the data collection.
+ */
+ void enable();
+
+ /**
+ * Disable the data collection.
+ */
+ void disable();
+}
diff --git a/services/core/java/com/android/server/security/forensic/ForensicService.java b/services/core/java/com/android/server/security/forensic/ForensicService.java
index 20c648eb61c2..53b07c04139c 100644
--- a/services/core/java/com/android/server/security/forensic/ForensicService.java
+++ b/services/core/java/com/android/server/security/forensic/ForensicService.java
@@ -22,6 +22,7 @@ import android.os.Handler;
import android.os.Looper;
import android.os.Message;
import android.os.RemoteException;
+import android.security.forensic.ForensicEvent;
import android.security.forensic.IForensicService;
import android.security.forensic.IForensicServiceCommandCallback;
import android.security.forensic.IForensicServiceStateCallback;
@@ -32,6 +33,7 @@ import com.android.server.ServiceThread;
import com.android.server.SystemService;
import java.util.ArrayList;
+import java.util.List;
/**
* @hide
@@ -64,6 +66,7 @@ public class ForensicService extends SystemService {
private final Context mContext;
private final Handler mHandler;
private final BackupTransportConnection mBackupTransportConnection;
+ private final DataAggregator mDataAggregator;
private final BinderService mBinderService;
private final ArrayList<IForensicServiceStateCallback> mStateMonitors = new ArrayList<>();
@@ -79,6 +82,7 @@ public class ForensicService extends SystemService {
mContext = injector.getContext();
mHandler = new EventHandler(injector.getLooper(), this);
mBackupTransportConnection = injector.getBackupTransportConnection();
+ mDataAggregator = injector.getDataAggregator(this);
mBinderService = new BinderService(this);
}
@@ -167,6 +171,9 @@ public class ForensicService extends SystemService {
Slog.e(TAG, "RemoteException", e);
}
break;
+ case MSG_BACKUP:
+ mService.backup((List<ForensicEvent>) msg.obj);
+ break;
default:
Slog.w(TAG, "Unknown message: " + msg.what);
}
@@ -192,6 +199,10 @@ public class ForensicService extends SystemService {
private void makeVisible(IForensicServiceCommandCallback callback) throws RemoteException {
switch (mState) {
case STATE_INVISIBLE:
+ if (!mDataAggregator.initialize()) {
+ callback.onFailure(ERROR_DATA_SOURCE_UNAVAILABLE);
+ break;
+ }
mState = STATE_VISIBLE;
notifyStateMonitors();
callback.onSuccess();
@@ -227,6 +238,7 @@ public class ForensicService extends SystemService {
callback.onFailure(ERROR_BACKUP_TRANSPORT_UNAVAILABLE);
break;
}
+ mDataAggregator.enable();
mState = STATE_ENABLED;
notifyStateMonitors();
callback.onSuccess();
@@ -243,6 +255,7 @@ public class ForensicService extends SystemService {
switch (mState) {
case STATE_ENABLED:
mBackupTransportConnection.release();
+ mDataAggregator.disable();
mState = STATE_VISIBLE;
notifyStateMonitors();
callback.onSuccess();
@@ -255,6 +268,17 @@ public class ForensicService extends SystemService {
}
}
+ /**
+ * Add a list of ForensicEvent.
+ */
+ public void addNewData(List<ForensicEvent> events) {
+ mHandler.obtainMessage(MSG_BACKUP, events).sendToTarget();
+ }
+
+ private void backup(List<ForensicEvent> events) {
+ mBackupTransportConnection.addData(events);
+ }
+
@Override
public void onStart() {
try {
@@ -275,6 +299,8 @@ public class ForensicService extends SystemService {
Looper getLooper();
BackupTransportConnection getBackupTransportConnection();
+
+ DataAggregator getDataAggregator(ForensicService forensicService);
}
private static final class InjectorImpl implements Injector {
@@ -303,6 +329,11 @@ public class ForensicService extends SystemService {
public BackupTransportConnection getBackupTransportConnection() {
return new BackupTransportConnection(mContext);
}
+
+ @Override
+ public DataAggregator getDataAggregator(ForensicService forensicService) {
+ return new DataAggregator(forensicService);
+ }
}
}
diff --git a/services/core/java/com/android/server/wm/ActivityRecord.java b/services/core/java/com/android/server/wm/ActivityRecord.java
index 25dfbd77642d..af7b8d645a65 100644
--- a/services/core/java/com/android/server/wm/ActivityRecord.java
+++ b/services/core/java/com/android/server/wm/ActivityRecord.java
@@ -1792,8 +1792,6 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A
}
prevDc.onRunningActivityChanged();
- // TODO(b/169035022): move to a more-appropriate place.
- mTransitionController.collect(this);
if (prevDc.mOpeningApps.remove(this)) {
// Transfer opening transition to new display.
mDisplayContent.mOpeningApps.add(this);
diff --git a/services/core/java/com/android/server/wm/ActivityStartController.java b/services/core/java/com/android/server/wm/ActivityStartController.java
index a2c2dfc0a5be..a82aae7b710e 100644
--- a/services/core/java/com/android/server/wm/ActivityStartController.java
+++ b/services/core/java/com/android/server/wm/ActivityStartController.java
@@ -34,7 +34,6 @@ import android.annotation.NonNull;
import android.annotation.Nullable;
import android.app.ActivityManager;
import android.app.ActivityOptions;
-import android.app.BackgroundStartPrivileges;
import android.app.IApplicationThread;
import android.content.ComponentName;
import android.content.ContentResolver;
@@ -280,8 +279,8 @@ public class ActivityStartController {
* @param validateIncomingUser Set true to skip checking {@code userId} with the calling UID.
* @param originatingPendingIntent PendingIntentRecord that originated this activity start or
* null if not originated by PendingIntent
- * @param forcedBalByPiSender If set to allow, the
- * PendingIntent's sender will try to force allow background activity starts.
+ * @param allowBalExemptionForSystemProcess If set to {@code true}, the
+ * PendingIntent's sender will allow additional exemptions.
* This is only possible if the sender of the PendingIntent is a system process.
*/
final int startActivityInPackage(int uid, int realCallingPid, int realCallingUid,
@@ -289,7 +288,7 @@ public class ActivityStartController {
String resolvedType, IBinder resultTo, String resultWho, int requestCode,
int startFlags, SafeActivityOptions options, int userId, Task inTask, String reason,
boolean validateIncomingUser, PendingIntentRecord originatingPendingIntent,
- BackgroundStartPrivileges forcedBalByPiSender) {
+ boolean allowBalExemptionForSystemProcess) {
userId = checkTargetUser(userId, validateIncomingUser, realCallingPid, realCallingUid,
reason);
@@ -310,7 +309,7 @@ public class ActivityStartController {
.setUserId(userId)
.setInTask(inTask)
.setOriginatingPendingIntent(originatingPendingIntent)
- .setBackgroundStartPrivileges(forcedBalByPiSender)
+ .setAllowBalExemptionForSystemProcess(allowBalExemptionForSystemProcess)
.execute();
}
@@ -325,18 +324,18 @@ public class ActivityStartController {
* @param validateIncomingUser Set true to skip checking {@code userId} with the calling UID.
* @param originatingPendingIntent PendingIntentRecord that originated this activity start or
* null if not originated by PendingIntent
- * @param forcedBalByPiSender If set to allow, the
- * PendingIntent's sender will try to force allow background activity starts.
+ * @param allowBalExemptionForSystemProcess If set to {@code true}, the
+ * PendingIntent's sender will allow additional exemptions.
* This is only possible if the sender of the PendingIntent is a system process.
*/
final int startActivitiesInPackage(int uid, String callingPackage,
@Nullable String callingFeatureId, Intent[] intents, String[] resolvedTypes,
IBinder resultTo, SafeActivityOptions options, int userId, boolean validateIncomingUser,
PendingIntentRecord originatingPendingIntent,
- BackgroundStartPrivileges forcedBalByPiSender) {
+ boolean allowBalExemptionForSystemProcess) {
return startActivitiesInPackage(uid, 0 /* realCallingPid */, -1 /* realCallingUid */,
callingPackage, callingFeatureId, intents, resolvedTypes, resultTo, options, userId,
- validateIncomingUser, originatingPendingIntent, forcedBalByPiSender);
+ validateIncomingUser, originatingPendingIntent, allowBalExemptionForSystemProcess);
}
/**
@@ -351,15 +350,15 @@ public class ActivityStartController {
* @param validateIncomingUser Set true to skip checking {@code userId} with the calling UID.
* @param originatingPendingIntent PendingIntentRecord that originated this activity start or
* null if not originated by PendingIntent
- * @param forcedBalByPiSender If set to allow, the
- * PendingIntent's sender will try to force allow background activity starts.
+ * @param allowBalExemptionForSystemProcess If set to {@code true}, the
+ * PendingIntent's sender will allow additional exemptions.
* This is only possible if the sender of the PendingIntent is a system process.
*/
final int startActivitiesInPackage(int uid, int realCallingPid, int realCallingUid,
String callingPackage, @Nullable String callingFeatureId, Intent[] intents,
String[] resolvedTypes, IBinder resultTo, SafeActivityOptions options, int userId,
boolean validateIncomingUser, PendingIntentRecord originatingPendingIntent,
- BackgroundStartPrivileges forcedBalByPiSender) {
+ boolean allowBalExemptionForSystemProcess) {
final String reason = "startActivityInPackage";
@@ -369,14 +368,14 @@ public class ActivityStartController {
// TODO: Switch to user app stacks here.
return startActivities(null, uid, realCallingPid, realCallingUid, callingPackage,
callingFeatureId, intents, resolvedTypes, resultTo, options, userId, reason,
- originatingPendingIntent, forcedBalByPiSender);
+ originatingPendingIntent, allowBalExemptionForSystemProcess);
}
int startActivities(IApplicationThread caller, int callingUid, int incomingRealCallingPid,
int incomingRealCallingUid, String callingPackage, @Nullable String callingFeatureId,
Intent[] intents, String[] resolvedTypes, IBinder resultTo, SafeActivityOptions options,
int userId, String reason, PendingIntentRecord originatingPendingIntent,
- BackgroundStartPrivileges forcedBalByPiSender) {
+ boolean allowBalExemptionForSystemProcess) {
if (intents == null) {
throw new NullPointerException("intents is null");
}
@@ -518,7 +517,7 @@ public class ActivityStartController {
// top one as otherwise an activity below might consume it.
.setAllowPendingRemoteAnimationRegistryLookup(top /* allowLookup*/)
.setOriginatingPendingIntent(originatingPendingIntent)
- .setBackgroundStartPrivileges(forcedBalByPiSender);
+ .setAllowBalExemptionForSystemProcess(allowBalExemptionForSystemProcess);
}
// Log if the activities to be started have different uids.
if (startingUidPkgs.size() > 1) {
diff --git a/services/core/java/com/android/server/wm/ActivityStarter.java b/services/core/java/com/android/server/wm/ActivityStarter.java
index 8dab71701d18..6b482f9e2a32 100644
--- a/services/core/java/com/android/server/wm/ActivityStarter.java
+++ b/services/core/java/com/android/server/wm/ActivityStarter.java
@@ -95,7 +95,6 @@ import android.annotation.NonNull;
import android.annotation.Nullable;
import android.app.ActivityManager;
import android.app.ActivityOptions;
-import android.app.BackgroundStartPrivileges;
import android.app.IApplicationThread;
import android.app.PendingIntent;
import android.app.ProfilerInfo;
@@ -430,7 +429,7 @@ class ActivityStarter {
WaitResult waitResult;
int filterCallingUid;
PendingIntentRecord originatingPendingIntent;
- BackgroundStartPrivileges forcedBalByPiSender;
+ boolean allowBalExemptionForSystemProcess;
boolean freezeScreen;
final StringBuilder logMessage = new StringBuilder();
@@ -496,7 +495,7 @@ class ActivityStarter {
allowPendingRemoteAnimationRegistryLookup = true;
filterCallingUid = UserHandle.USER_NULL;
originatingPendingIntent = null;
- forcedBalByPiSender = BackgroundStartPrivileges.NONE;
+ allowBalExemptionForSystemProcess = false;
freezeScreen = false;
errorCallbackToken = null;
}
@@ -540,7 +539,7 @@ class ActivityStarter {
= request.allowPendingRemoteAnimationRegistryLookup;
filterCallingUid = request.filterCallingUid;
originatingPendingIntent = request.originatingPendingIntent;
- forcedBalByPiSender = request.forcedBalByPiSender;
+ allowBalExemptionForSystemProcess = request.allowBalExemptionForSystemProcess;
freezeScreen = request.freezeScreen;
errorCallbackToken = request.errorCallbackToken;
}
@@ -1275,8 +1274,8 @@ class ActivityStarter {
"Creator PermissionPolicyService.checkStartActivity Caused abortion.",
intent, intentCreatorUid, intentCreatorPackage, callingUid, callingPackage);
}
- intent.removeCreatorTokenInfo();
}
+ intent.removeCreatorToken();
// Merge the two options bundles, while realCallerOptions takes precedence.
ActivityOptions checkedOptions = options != null
@@ -1298,7 +1297,7 @@ class ActivityStarter {
realCallingPid,
callerApp,
request.originatingPendingIntent,
- request.forcedBalByPiSender,
+ request.allowBalExemptionForSystemProcess,
resultRecord,
intent,
checkedOptions);
@@ -3533,8 +3532,9 @@ class ActivityStarter {
return this;
}
- ActivityStarter setBackgroundStartPrivileges(BackgroundStartPrivileges forcedBalByPiSender) {
- mRequest.forcedBalByPiSender = forcedBalByPiSender;
+ ActivityStarter setAllowBalExemptionForSystemProcess(
+ boolean allowBalExemptionForSystemProcess) {
+ mRequest.allowBalExemptionForSystemProcess = allowBalExemptionForSystemProcess;
return this;
}
diff --git a/services/core/java/com/android/server/wm/ActivityTaskManagerInternal.java b/services/core/java/com/android/server/wm/ActivityTaskManagerInternal.java
index 3560565ce9cd..0a57cb50d681 100644
--- a/services/core/java/com/android/server/wm/ActivityTaskManagerInternal.java
+++ b/services/core/java/com/android/server/wm/ActivityTaskManagerInternal.java
@@ -21,7 +21,6 @@ import android.annotation.Nullable;
import android.annotation.UserIdInt;
import android.app.ActivityManager;
import android.app.AppProtoEnums;
-import android.app.BackgroundStartPrivileges;
import android.app.IActivityManager;
import android.app.IAppTask;
import android.app.IApplicationThread;
@@ -179,15 +178,15 @@ public abstract class ActivityTaskManagerInternal {
* @param validateIncomingUser Set true to skip checking {@code userId} with the calling UID.
* @param originatingPendingIntent PendingIntentRecord that originated this activity start or
* null if not originated by PendingIntent
- * @param forcedBalByPiSender If set to allow, the
- * PendingIntent's sender will try to force allow background activity starts.
+ * @param allowBalExemptionForSystemProcess If set to {@code true}, the
+ * PendingIntent's sender will allow additional exemptions.
* This is only possible if the sender of the PendingIntent is a system process.
*/
public abstract int startActivitiesInPackage(int uid, int realCallingPid, int realCallingUid,
String callingPackage, @Nullable String callingFeatureId, Intent[] intents,
String[] resolvedTypes, IBinder resultTo, SafeActivityOptions options, int userId,
boolean validateIncomingUser, PendingIntentRecord originatingPendingIntent,
- BackgroundStartPrivileges forcedBalByPiSender);
+ boolean allowBalExemptionForSystemProcess);
/**
* Start intent as a package.
@@ -202,8 +201,8 @@ public abstract class ActivityTaskManagerInternal {
* @param validateIncomingUser Set true to skip checking {@code userId} with the calling UID.
* @param originatingPendingIntent PendingIntentRecord that originated this activity start or
* null if not originated by PendingIntent
- * @param forcedBalByPiSender If set to allow, the
- * PendingIntent's sender will try to force allow background activity starts.
+ * @param allowBalExemptionForSystemProcess If set to {@code true}, the
+ * PendingIntent's sender will allow additional exemptions.
* This is only possible if the sender of the PendingIntent is a system process.
*/
public abstract int startActivityInPackage(int uid, int realCallingPid, int realCallingUid,
@@ -211,7 +210,7 @@ public abstract class ActivityTaskManagerInternal {
String resolvedType, IBinder resultTo, String resultWho, int requestCode,
int startFlags, SafeActivityOptions options, int userId, Task inTask, String reason,
boolean validateIncomingUser, PendingIntentRecord originatingPendingIntent,
- BackgroundStartPrivileges forcedBalByPiSender);
+ boolean allowBalExemptionForSystemProcess);
/**
* Callback to be called on certain activity start scenarios.
diff --git a/services/core/java/com/android/server/wm/ActivityTaskManagerService.java b/services/core/java/com/android/server/wm/ActivityTaskManagerService.java
index 111e74e9b590..96cb2f23b995 100644
--- a/services/core/java/com/android/server/wm/ActivityTaskManagerService.java
+++ b/services/core/java/com/android/server/wm/ActivityTaskManagerService.java
@@ -145,7 +145,6 @@ import android.app.AlertDialog;
import android.app.AnrController;
import android.app.AppGlobals;
import android.app.AppOpsManager;
-import android.app.BackgroundStartPrivileges;
import android.app.Dialog;
import android.app.IActivityClientController;
import android.app.IActivityController;
@@ -1228,7 +1227,6 @@ public class ActivityTaskManagerService extends IActivityTaskManager.Stub {
String callingFeatureId, Intent intent, String resolvedType, IBinder resultTo,
String resultWho, int requestCode, int startFlags, ProfilerInfo profilerInfo,
Bundle bOptions) {
- mAmInternal.addCreatorToken(intent, callingPackage);
return startActivityAsUser(caller, callingPackage, callingFeatureId, intent, resolvedType,
resultTo, resultWho, requestCode, startFlags, profilerInfo, bOptions,
UserHandle.getCallingUserId());
@@ -1251,7 +1249,7 @@ public class ActivityTaskManagerService extends IActivityTaskManager.Stub {
return getActivityStartController().startActivities(caller, -1, 0, -1, callingPackage,
callingFeatureId, intents, resolvedTypes, resultTo,
SafeActivityOptions.fromBundle(bOptions), userId, reason,
- null /* originatingPendingIntent */, BackgroundStartPrivileges.NONE);
+ null /* originatingPendingIntent */, false);
}
@Override
@@ -1560,7 +1558,7 @@ public class ActivityTaskManagerService extends IActivityTaskManager.Stub {
// To start the dream from background, we need to start it from a persistent
// system process. Here we set the real calling uid to the system server uid
.setRealCallingUid(Binder.getCallingUid())
- .setBackgroundStartPrivileges(BackgroundStartPrivileges.ALLOW_BAL)
+ .setAllowBalExemptionForSystemProcess(true)
.execute();
final ActivityRecord started = outActivity[0];
@@ -1711,7 +1709,7 @@ public class ActivityTaskManagerService extends IActivityTaskManager.Stub {
.setFilterCallingUid(isResolver ? 0 /* system */ : targetUid)
// The target may well be in the background, which would normally prevent it
// from starting an activity. Here we definitely want the start to succeed.
- .setBackgroundStartPrivileges(BackgroundStartPrivileges.ALLOW_BAL)
+ .setAllowBalExemptionForSystemProcess(true)
.execute();
} catch (SecurityException e) {
// XXX need to figure out how to propagate to original app.
@@ -1757,7 +1755,7 @@ public class ActivityTaskManagerService extends IActivityTaskManager.Stub {
.setProfilerInfo(profilerInfo)
.setActivityOptions(createSafeActivityOptionsWithBalAllowed(bOptions))
.setUserId(userId)
- .setBackgroundStartPrivileges(BackgroundStartPrivileges.ALLOW_BAL)
+ .setAllowBalExemptionForSystemProcess(true)
.execute();
}
@@ -1784,7 +1782,7 @@ public class ActivityTaskManagerService extends IActivityTaskManager.Stub {
.setResolvedType(resolvedType)
.setActivityOptions(createSafeActivityOptionsWithBalAllowed(bOptions))
.setUserId(userId)
- .setBackgroundStartPrivileges(BackgroundStartPrivileges.ALLOW_BAL)
+ .setAllowBalExemptionForSystemProcess(true)
.execute();
} finally {
Binder.restoreCallingIdentity(origId);
@@ -2256,7 +2254,7 @@ public class ActivityTaskManagerService extends IActivityTaskManager.Stub {
-1,
callerApp,
null,
- BackgroundStartPrivileges.NONE,
+ false,
null,
null,
null);
@@ -6066,7 +6064,7 @@ public class ActivityTaskManagerService extends IActivityTaskManager.Stub {
intents, resolvedTypes, null /* resultTo */,
SafeActivityOptions.fromBundle(bOptions), userId,
false /* validateIncomingUser */, null /* originatingPendingIntent */,
- BackgroundStartPrivileges.NONE);
+ false);
}
@Override
@@ -6074,12 +6072,12 @@ public class ActivityTaskManagerService extends IActivityTaskManager.Stub {
String callingPackage, @Nullable String callingFeatureId, Intent[] intents,
String[] resolvedTypes, IBinder resultTo, SafeActivityOptions options, int userId,
boolean validateIncomingUser, PendingIntentRecord originatingPendingIntent,
- BackgroundStartPrivileges forcedBalByPiSender) {
+ boolean allowBalExemptionForSystemProcess) {
assertPackageMatchesCallingUid(callingPackage);
return getActivityStartController().startActivitiesInPackage(uid, realCallingPid,
realCallingUid, callingPackage, callingFeatureId, intents, resolvedTypes,
resultTo, options, userId, validateIncomingUser, originatingPendingIntent,
- forcedBalByPiSender);
+ allowBalExemptionForSystemProcess);
}
@Override
@@ -6088,13 +6086,13 @@ public class ActivityTaskManagerService extends IActivityTaskManager.Stub {
String resolvedType, IBinder resultTo, String resultWho, int requestCode,
int startFlags, SafeActivityOptions options, int userId, Task inTask, String reason,
boolean validateIncomingUser, PendingIntentRecord originatingPendingIntent,
- BackgroundStartPrivileges forcedBalByPiSender) {
+ boolean allowBalExemptionForSystemProcess) {
assertPackageMatchesCallingUid(callingPackage);
return getActivityStartController().startActivityInPackage(uid, realCallingPid,
realCallingUid, callingPackage, callingFeatureId, intent, resolvedType,
resultTo, resultWho, requestCode, startFlags, options, userId, inTask,
reason, validateIncomingUser, originatingPendingIntent,
- forcedBalByPiSender);
+ allowBalExemptionForSystemProcess);
}
@Override
@@ -6125,7 +6123,7 @@ public class ActivityTaskManagerService extends IActivityTaskManager.Stub {
.setActivityOptions(createSafeActivityOptionsWithBalAllowed(options))
.setRealCallingUid(Binder.getCallingUid())
.setUserId(userId)
- .setBackgroundStartPrivileges(BackgroundStartPrivileges.ALLOW_BAL)
+ .setAllowBalExemptionForSystemProcess(true)
.setFreezeScreen(true)
.execute();
}
diff --git a/services/core/java/com/android/server/wm/ActivityTaskSupervisor.java b/services/core/java/com/android/server/wm/ActivityTaskSupervisor.java
index 6a1f28de72d3..18b23ad9824e 100644
--- a/services/core/java/com/android/server/wm/ActivityTaskSupervisor.java
+++ b/services/core/java/com/android/server/wm/ActivityTaskSupervisor.java
@@ -96,7 +96,6 @@ import android.app.ActivityManagerInternal;
import android.app.ActivityOptions;
import android.app.AppOpsManager;
import android.app.AppOpsManagerInternal;
-import android.app.BackgroundStartPrivileges;
import android.app.IActivityClientController;
import android.app.ProfilerInfo;
import android.app.ResultInfo;
@@ -242,6 +241,8 @@ public class ActivityTaskSupervisor implements RecentTasks.Callbacks {
static {
ACTION_TO_RUNTIME_PERMISSION.put(MediaStore.ACTION_IMAGE_CAPTURE,
Manifest.permission.CAMERA);
+ ACTION_TO_RUNTIME_PERMISSION.put(MediaStore.ACTION_MOTION_PHOTO_CAPTURE,
+ Manifest.permission.CAMERA);
ACTION_TO_RUNTIME_PERMISSION.put(MediaStore.ACTION_VIDEO_CAPTURE,
Manifest.permission.CAMERA);
ACTION_TO_RUNTIME_PERMISSION.put(Intent.ACTION_CALL,
@@ -2873,7 +2874,7 @@ public class ActivityTaskSupervisor implements RecentTasks.Callbacks {
callingPid, callingUid, callingPackage, callingFeatureId, intent, null, null,
null, 0, 0, options, userId, task, "startActivityFromRecents",
false /* validateIncomingUser */, null /* originatingPendingIntent */,
- BackgroundStartPrivileges.NONE);
+ /* allowBalExemptionForSystemProcess */ false);
} finally {
SaferIntentUtils.DISABLE_ENFORCE_INTENTS_TO_MATCH_INTENT_FILTERS.set(false);
synchronized (mService.mGlobalLock) {
diff --git a/services/core/java/com/android/server/wm/AppTaskImpl.java b/services/core/java/com/android/server/wm/AppTaskImpl.java
index eee4c86bc483..ef34dab34968 100644
--- a/services/core/java/com/android/server/wm/AppTaskImpl.java
+++ b/services/core/java/com/android/server/wm/AppTaskImpl.java
@@ -17,12 +17,11 @@
package com.android.server.wm;
import static com.android.server.wm.ActivityTaskManagerDebugConfig.DEBUG_ACTIVITY_STARTS;
-import static com.android.server.wm.BackgroundActivityStartController.BalVerdict;
import static com.android.server.wm.ActivityTaskSupervisor.REMOVE_FROM_RECENTS;
+import static com.android.server.wm.BackgroundActivityStartController.BalVerdict;
import static com.android.server.wm.RootWindowContainer.MATCH_ATTACHED_TASK_OR_RECENT_TASKS;
import android.app.ActivityManager;
-import android.app.BackgroundStartPrivileges;
import android.app.IAppTask;
import android.app.IApplicationThread;
import android.content.Intent;
@@ -136,7 +135,7 @@ class AppTaskImpl extends IAppTask.Stub {
-1,
callerApp,
null,
- BackgroundStartPrivileges.NONE,
+ false,
null,
null,
null);
diff --git a/services/core/java/com/android/server/wm/BackgroundActivityStartController.java b/services/core/java/com/android/server/wm/BackgroundActivityStartController.java
index f9902cf0db9c..3e553adc44f9 100644
--- a/services/core/java/com/android/server/wm/BackgroundActivityStartController.java
+++ b/services/core/java/com/android/server/wm/BackgroundActivityStartController.java
@@ -304,7 +304,7 @@ public class BackgroundActivityStartController {
private final @ActivityManager.ProcessState int mRealCallingUidProcState;
private final boolean mIsRealCallingUidPersistentSystemProcess;
private final PendingIntentRecord mOriginatingPendingIntent;
- private final BackgroundStartPrivileges mForcedBalByPiSender;
+ private final boolean mAllowBalExemptionForSystemProcess;
private final Intent mIntent;
private final WindowProcessController mCallerApp;
private final WindowProcessController mRealCallerApp;
@@ -319,7 +319,7 @@ public class BackgroundActivityStartController {
int realCallingUid, int realCallingPid,
WindowProcessController callerApp,
PendingIntentRecord originatingPendingIntent,
- BackgroundStartPrivileges forcedBalByPiSender,
+ boolean allowBalExemptionForSystemProcess,
ActivityRecord resultRecord,
Intent intent,
ActivityOptions checkedOptions) {
@@ -329,7 +329,7 @@ public class BackgroundActivityStartController {
mRealCallingUid = realCallingUid;
mRealCallingPid = realCallingPid;
mCallerApp = callerApp;
- mForcedBalByPiSender = forcedBalByPiSender;
+ mAllowBalExemptionForSystemProcess = allowBalExemptionForSystemProcess;
mOriginatingPendingIntent = originatingPendingIntent;
mIntent = intent;
mRealCallingPackage = mService.getPackageNameIfUnique(realCallingUid, realCallingPid);
@@ -549,7 +549,8 @@ public class BackgroundActivityStartController {
ActivityManager.class, "PROCESS_STATE_", mCallingUidProcState));
sb.append("; isCallingUidPersistentSystemProcess: ")
.append(mIsCallingUidPersistentSystemProcess);
- sb.append("; forcedBalByPiSender: ").append(mForcedBalByPiSender);
+ sb.append("; allowBalExemptionForSystemProcess: ")
+ .append(mAllowBalExemptionForSystemProcess);
sb.append("; intent: ").append(mIntent);
sb.append("; callerApp: ").append(mCallerApp);
if (mCallerApp != null) {
@@ -704,8 +705,8 @@ public class BackgroundActivityStartController {
* @param callerApp The process that calls this method (only if not a PendingIntent)
* @param originatingPendingIntent PendingIntentRecord that originated this activity start or
* null if not originated by PendingIntent
- * @param forcedBalByPiSender If set to allow, the
- * PendingIntent's sender will try to force allow background activity starts.
+ * @param allowBalExemptionForSystemProcess If set to true, the
+ * PendingIntent's sender will allow additional exemptions.
* This is only possible if the sender of the PendingIntent is a system process.
* @param resultRecord If not null, this indicates that the caller expects a result.
* @param intent Intent that should be started.
@@ -722,7 +723,7 @@ public class BackgroundActivityStartController {
int realCallingPid,
WindowProcessController callerApp,
PendingIntentRecord originatingPendingIntent,
- BackgroundStartPrivileges forcedBalByPiSender,
+ boolean allowBalExemptionForSystemProcess,
ActivityRecord resultRecord,
Intent intent,
ActivityOptions checkedOptions) {
@@ -734,7 +735,7 @@ public class BackgroundActivityStartController {
BalState state = new BalState(callingUid, callingPid, callingPackage,
realCallingUid, realCallingPid, callerApp, originatingPendingIntent,
- forcedBalByPiSender, resultRecord, intent, checkedOptions);
+ allowBalExemptionForSystemProcess, resultRecord, intent, checkedOptions);
// In the case of an SDK sandbox calling uid, check if the corresponding app uid has a
// visible window.
@@ -1069,7 +1070,7 @@ public class BackgroundActivityStartController {
// if the realCallingUid is a persistent system process, abort if the IntentSender
// wasn't allowed to start an activity
- if (state.mForcedBalByPiSender.allowsBackgroundActivityStarts()
+ if (state.mAllowBalExemptionForSystemProcess
&& state.mIsRealCallingUidPersistentSystemProcess) {
return new BalVerdict(BAL_ALLOW_ALLOWLISTED_UID,
/*background*/ false,
@@ -1385,7 +1386,7 @@ public class BackgroundActivityStartController {
String packageName = mService.mContext.getPackageManager().getNameForUid(callingUid);
BalState state = new BalState(callingUid, callingPid, packageName, INVALID_UID,
- INVALID_PID, null, null, null, null, null, ActivityOptions.makeBasic());
+ INVALID_PID, null, null, false, null, null, ActivityOptions.makeBasic());
@BalCode int balCode = checkBackgroundActivityStartAllowedByCaller(state).mCode;
if (balCode == BAL_ALLOW_ALLOWLISTED_UID
|| balCode == BAL_ALLOW_ALLOWLISTED_COMPONENT
diff --git a/services/core/java/com/android/server/wm/DragDropController.java b/services/core/java/com/android/server/wm/DragDropController.java
index c849a37ede53..258a87eae196 100644
--- a/services/core/java/com/android/server/wm/DragDropController.java
+++ b/services/core/java/com/android/server/wm/DragDropController.java
@@ -156,7 +156,7 @@ class DragDropController {
SurfaceControl surface, int touchSource, int touchDeviceId, int touchPointerId,
float touchX, float touchY, float thumbCenterX, float thumbCenterY, ClipData data) {
if (DEBUG_DRAG) {
- Slog.d(TAG_WM, "perform drag: win=" + window + " surface=" + surface + " flags=" +
+ Slog.d(TAG_WM, "perform drag: win=" + window + " surface=" + surface + " flags=0x" +
Integer.toHexString(flags) + " data=" + data + " touch(" + touchX + ","
+ touchY + ") thumb center(" + thumbCenterX + "," + thumbCenterY + ")");
}
diff --git a/services/core/java/com/android/server/wm/WindowContainer.java b/services/core/java/com/android/server/wm/WindowContainer.java
index 95cf6bc3b771..6c92ae6bb3e7 100644
--- a/services/core/java/com/android/server/wm/WindowContainer.java
+++ b/services/core/java/com/android/server/wm/WindowContainer.java
@@ -1117,6 +1117,7 @@ class WindowContainer<E extends WindowContainer> extends ConfigurationContainer<
*/
void onDisplayChanged(DisplayContent dc) {
if (mDisplayContent != null && mDisplayContent != dc) {
+ mTransitionController.collect(this);
// Cancel any change transition queued-up for this container on the old display when
// this container is moved from the old display.
mDisplayContent.mClosingChangingContainers.remove(this);
diff --git a/services/core/lint-baseline.xml b/services/core/lint-baseline.xml
index 3b81f0a6191e..4c1ac39a5da0 100644
--- a/services/core/lint-baseline.xml
+++ b/services/core/lint-baseline.xml
@@ -178,4 +178,675 @@
column="51"/>
</issue>
-</issues> \ No newline at end of file
+ <issue
+ id="MissingPermissionAnnotation"
+ message="onShellCommand should be annotated with either @EnforcePermission, @RequiresNoPermission or @PermissionManuallyEnforced."
+ errorLine1=" @Override"
+ errorLine2=" ^">
+ <location
+ file="frameworks/base/services/core/java/com/android/server/security/advancedprotection/AdvancedProtectionService.java"
+ line="128"
+ column="5"/>
+ </issue>
+
+ <issue
+ id="MissingPermissionAnnotation"
+ message="monitorState should be annotated with either @EnforcePermission, @RequiresNoPermission or @PermissionManuallyEnforced."
+ errorLine1=" @Override"
+ errorLine2=" ^">
+ <location
+ file="frameworks/base/services/core/java/com/android/server/security/forensic/ForensicService.java"
+ line="95"
+ column="9"/>
+ </issue>
+
+ <issue
+ id="MissingPermissionAnnotation"
+ message="makeVisible should be annotated with either @EnforcePermission, @RequiresNoPermission or @PermissionManuallyEnforced."
+ errorLine1=" @Override"
+ errorLine2=" ^">
+ <location
+ file="frameworks/base/services/core/java/com/android/server/security/forensic/ForensicService.java"
+ line="100"
+ column="9"/>
+ </issue>
+
+ <issue
+ id="MissingPermissionAnnotation"
+ message="makeInvisible should be annotated with either @EnforcePermission, @RequiresNoPermission or @PermissionManuallyEnforced."
+ errorLine1=" @Override"
+ errorLine2=" ^">
+ <location
+ file="frameworks/base/services/core/java/com/android/server/security/forensic/ForensicService.java"
+ line="105"
+ column="9"/>
+ </issue>
+
+ <issue
+ id="MissingPermissionAnnotation"
+ message="enable should be annotated with either @EnforcePermission, @RequiresNoPermission or @PermissionManuallyEnforced."
+ errorLine1=" @Override"
+ errorLine2=" ^">
+ <location
+ file="frameworks/base/services/core/java/com/android/server/security/forensic/ForensicService.java"
+ line="110"
+ column="9"/>
+ </issue>
+
+ <issue
+ id="MissingPermissionAnnotation"
+ message="disable should be annotated with either @EnforcePermission, @RequiresNoPermission or @PermissionManuallyEnforced."
+ errorLine1=" @Override"
+ errorLine2=" ^">
+ <location
+ file="frameworks/base/services/core/java/com/android/server/security/forensic/ForensicService.java"
+ line="115"
+ column="9"/>
+ </issue>
+
+ <issue
+ id="MissingPermissionAnnotation"
+ message="getTimeoutTime should be annotated with either @EnforcePermission, @RequiresNoPermission or @PermissionManuallyEnforced."
+ errorLine1=" @Override"
+ errorLine2=" ^">
+ <location
+ file="frameworks/base/services/core/java/com/android/server/pm/verify/pkg/VerifierController.java"
+ line="430"
+ column="9"/>
+ </issue>
+
+ <issue
+ id="MissingPermissionAnnotation"
+ message="extendTimeRemaining should be annotated with either @EnforcePermission, @RequiresNoPermission or @PermissionManuallyEnforced."
+ errorLine1=" @Override"
+ errorLine2=" ^">
+ <location
+ file="frameworks/base/services/core/java/com/android/server/pm/verify/pkg/VerifierController.java"
+ line="443"
+ column="9"/>
+ </issue>
+
+ <issue
+ id="MissingPermissionAnnotation"
+ message="setVerificationPolicy should be annotated with either @EnforcePermission, @RequiresNoPermission or @PermissionManuallyEnforced."
+ errorLine1=" @Override"
+ errorLine2=" ^">
+ <location
+ file="frameworks/base/services/core/java/com/android/server/pm/verify/pkg/VerifierController.java"
+ line="456"
+ column="9"/>
+ </issue>
+
+ <issue
+ id="MissingPermissionAnnotation"
+ message="reportVerificationIncomplete should be annotated with either @EnforcePermission, @RequiresNoPermission or @PermissionManuallyEnforced."
+ errorLine1=" @Override"
+ errorLine2=" ^">
+ <location
+ file="frameworks/base/services/core/java/com/android/server/pm/verify/pkg/VerifierController.java"
+ line="470"
+ column="9"/>
+ </issue>
+
+ <issue
+ id="MissingPermissionAnnotation"
+ message="reportVerificationComplete should be annotated with either @EnforcePermission, @RequiresNoPermission or @PermissionManuallyEnforced."
+ errorLine1=" @Override"
+ errorLine2=" ^">
+ <location
+ file="frameworks/base/services/core/java/com/android/server/pm/verify/pkg/VerifierController.java"
+ line="486"
+ column="9"/>
+ </issue>
+
+ <issue
+ id="MissingPermissionAnnotation"
+ message="reportVerificationCompleteWithExtensionResponse should be annotated with either @EnforcePermission, @RequiresNoPermission or @PermissionManuallyEnforced."
+ errorLine1=" @Override"
+ errorLine2=" ^">
+ <location
+ file="frameworks/base/services/core/java/com/android/server/pm/verify/pkg/VerifierController.java"
+ line="492"
+ column="9"/>
+ </issue>
+
+ <issue
+ id="SimpleManualPermissionEnforcement"
+ message="IActivityClientController permission check can be converted to @EnforcePermission annotation"
+ errorLine1=" mService.mAmInternal.enforceCallingPermission("
+ errorLine2=" ^">
+ <location
+ file="out/soong/.intermediates/frameworks/base/services/core/services.core.protologsrc/gen/services.core.protolog.srcjar!/frameworks/base/services/core/java/com/android/server/wm/ActivityClientController.java"
+ line="592"
+ column="9"/>
+ </issue>
+
+ <issue
+ id="SimpleManualPermissionEnforcement"
+ message="IActivityClientController permission check can be converted to @EnforcePermission annotation"
+ errorLine1=" mService.mAmInternal.enforceCallingPermission(CONTROL_REMOTE_APP_TRANSITION_ANIMATIONS,"
+ errorLine2=" ^">
+ <location
+ file="out/soong/.intermediates/frameworks/base/services/core/services.core.protologsrc/gen/services.core.protolog.srcjar!/frameworks/base/services/core/java/com/android/server/wm/ActivityClientController.java"
+ line="1636"
+ column="9"/>
+ </issue>
+
+ <issue
+ id="SimpleManualPermissionEnforcement"
+ message="IActivityClientController permission check can be converted to @EnforcePermission annotation"
+ errorLine1=" mService.mAmInternal.enforceCallingPermission(CONTROL_REMOTE_APP_TRANSITION_ANIMATIONS,"
+ errorLine2=" ^">
+ <location
+ file="out/soong/.intermediates/frameworks/base/services/core/services.core.protologsrc/gen/services.core.protolog.srcjar!/frameworks/base/services/core/java/com/android/server/wm/ActivityClientController.java"
+ line="1654"
+ column="9"/>
+ </issue>
+
+ <issue
+ id="SimpleManualPermissionEnforcement"
+ message="IActivityTaskManager permission check can be converted to @EnforcePermission annotation"
+ errorLine1=" mAmInternal.enforceCallingPermission(START_TASKS_FROM_RECENTS,"
+ errorLine2=" ^">
+ <location
+ file="out/soong/.intermediates/frameworks/base/services/core/services.core.protologsrc/gen/services.core.protolog.srcjar!/frameworks/base/services/core/java/com/android/server/wm/ActivityTaskManagerService.java"
+ line="1820"
+ column="9"/>
+ </issue>
+
+ <issue
+ id="SimpleManualPermissionEnforcement"
+ message="IActivityTaskManager permission check can be converted to @EnforcePermission annotation"
+ errorLine1=" mAmInternal.enforceCallingPermission(START_TASKS_FROM_RECENTS,"
+ errorLine2=" ^">
+ <location
+ file="out/soong/.intermediates/frameworks/base/services/core/services.core.protologsrc/gen/services.core.protolog.srcjar!/frameworks/base/services/core/java/com/android/server/wm/ActivityTaskManagerService.java"
+ line="1875"
+ column="9"/>
+ </issue>
+
+ <issue
+ id="SimpleManualPermissionEnforcement"
+ message="IActivityTaskManager permission check can be converted to @EnforcePermission annotation"
+ errorLine1=" mAmInternal.enforceCallingPermission(android.Manifest.permission.SET_SCREEN_COMPATIBILITY,"
+ errorLine2=" ^">
+ <location
+ file="out/soong/.intermediates/frameworks/base/services/core/services.core.protologsrc/gen/services.core.protolog.srcjar!/frameworks/base/services/core/java/com/android/server/wm/ActivityTaskManagerService.java"
+ line="1980"
+ column="9"/>
+ </issue>
+
+ <issue
+ id="SimpleManualPermissionEnforcement"
+ message="IActivityTaskManager permission check can be converted to @EnforcePermission annotation"
+ errorLine1=" mAmInternal.enforceCallingPermission(REMOVE_TASKS, &quot;removeTask()&quot;);"
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="out/soong/.intermediates/frameworks/base/services/core/services.core.protologsrc/gen/services.core.protolog.srcjar!/frameworks/base/services/core/java/com/android/server/wm/ActivityTaskManagerService.java"
+ line="2116"
+ column="9"/>
+ </issue>
+
+ <issue
+ id="SimpleManualPermissionEnforcement"
+ message="IActivityTaskManager permission check can be converted to @EnforcePermission annotation"
+ errorLine1=" mAmInternal.enforceCallingPermission(REMOVE_TASKS, &quot;removeAllVisibleRecentTasks()&quot;);"
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="out/soong/.intermediates/frameworks/base/services/core/services.core.protologsrc/gen/services.core.protolog.srcjar!/frameworks/base/services/core/java/com/android/server/wm/ActivityTaskManagerService.java"
+ line="2144"
+ column="9"/>
+ </issue>
+
+ <issue
+ id="SimpleManualPermissionEnforcement"
+ message="IActivityTaskManager permission check can be converted to @EnforcePermission annotation"
+ errorLine1=" mAmInternal.enforceCallingPermission(android.Manifest.permission.FORCE_BACK,"
+ errorLine2=" ^">
+ <location
+ file="out/soong/.intermediates/frameworks/base/services/core/services.core.protologsrc/gen/services.core.protolog.srcjar!/frameworks/base/services/core/java/com/android/server/wm/ActivityTaskManagerService.java"
+ line="2206"
+ column="9"/>
+ </issue>
+
+ <issue
+ id="SimpleManualPermissionEnforcement"
+ message="IActivityTaskManager permission check can be converted to @EnforcePermission annotation"
+ errorLine1=" mAmInternal.enforceCallingPermission(android.Manifest.permission.REORDER_TASKS,"
+ errorLine2=" ^">
+ <location
+ file="out/soong/.intermediates/frameworks/base/services/core/services.core.protologsrc/gen/services.core.protolog.srcjar!/frameworks/base/services/core/java/com/android/server/wm/ActivityTaskManagerService.java"
+ line="2228"
+ column="9"/>
+ </issue>
+
+ <issue
+ id="SimpleManualPermissionEnforcement"
+ message="IActivityTaskManager permission check can be converted to @EnforcePermission annotation"
+ errorLine1=" mAmInternal.enforceCallingPermission(android.Manifest.permission.SET_ACTIVITY_WATCHER,"
+ errorLine2=" ^">
+ <location
+ file="out/soong/.intermediates/frameworks/base/services/core/services.core.protologsrc/gen/services.core.protolog.srcjar!/frameworks/base/services/core/java/com/android/server/wm/ActivityTaskManagerService.java"
+ line="2371"
+ column="9"/>
+ </issue>
+
+ <issue
+ id="SimpleManualPermissionEnforcement"
+ message="IActivityTaskManager permission check can be converted to @EnforcePermission annotation"
+ errorLine1=" mAmInternal.enforceCallingPermission(INTERNAL_SYSTEM_WINDOW, &quot;moveRootTaskToDisplay()&quot;);"
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="out/soong/.intermediates/frameworks/base/services/core/services.core.protologsrc/gen/services.core.protolog.srcjar!/frameworks/base/services/core/java/com/android/server/wm/ActivityTaskManagerService.java"
+ line="3103"
+ column="9"/>
+ </issue>
+
+ <issue
+ id="SimpleManualPermissionEnforcement"
+ message="IActivityTaskManager permission check can be converted to @EnforcePermission annotation"
+ errorLine1=" mAmInternal.enforceCallingPermission(android.Manifest.permission.GET_TOP_ACTIVITY_INFO,"
+ errorLine2=" ^">
+ <location
+ file="out/soong/.intermediates/frameworks/base/services/core/services.core.protologsrc/gen/services.core.protolog.srcjar!/frameworks/base/services/core/java/com/android/server/wm/ActivityTaskManagerService.java"
+ line="3157"
+ column="9"/>
+ </issue>
+
+ <issue
+ id="SimpleManualPermissionEnforcement"
+ message="IActivityTaskManager permission check can be converted to @EnforcePermission annotation"
+ errorLine1=" mAmInternal.enforceCallingPermission(CONTROL_KEYGUARD, &quot;unlock keyguard&quot;);"
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="out/soong/.intermediates/frameworks/base/services/core/services.core.protologsrc/gen/services.core.protolog.srcjar!/frameworks/base/services/core/java/com/android/server/wm/ActivityTaskManagerService.java"
+ line="3640"
+ column="9"/>
+ </issue>
+
+ <issue
+ id="SimpleManualPermissionEnforcement"
+ message="IActivityTaskManager permission check can be converted to @EnforcePermission annotation"
+ errorLine1=" mAmInternal.enforceCallingPermission(MANAGE_ACTIVITY_TASKS,"
+ errorLine2=" ^">
+ <location
+ file="out/soong/.intermediates/frameworks/base/services/core/services.core.protologsrc/gen/services.core.protolog.srcjar!/frameworks/base/services/core/java/com/android/server/wm/ActivityTaskManagerService.java"
+ line="3683"
+ column="9"/>
+ </issue>
+
+ <issue
+ id="SimpleManualPermissionEnforcement"
+ message="IActivityTaskManager permission check can be converted to @EnforcePermission annotation"
+ errorLine1=" mAmInternal.enforceCallingPermission(MANAGE_ACTIVITY_TASKS,"
+ errorLine2=" ^">
+ <location
+ file="out/soong/.intermediates/frameworks/base/services/core/services.core.protologsrc/gen/services.core.protolog.srcjar!/frameworks/base/services/core/java/com/android/server/wm/ActivityTaskManagerService.java"
+ line="3701"
+ column="9"/>
+ </issue>
+
+ <issue
+ id="SimpleManualPermissionEnforcement"
+ message="IActivityTaskManager permission check can be converted to @EnforcePermission annotation"
+ errorLine1=" mAmInternal.enforceCallingPermission(CHANGE_CONFIGURATION, &quot;updateConfiguration()&quot;);"
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="out/soong/.intermediates/frameworks/base/services/core/services.core.protologsrc/gen/services.core.protolog.srcjar!/frameworks/base/services/core/java/com/android/server/wm/ActivityTaskManagerService.java"
+ line="3904"
+ column="9"/>
+ </issue>
+
+ <issue
+ id="SimpleManualPermissionEnforcement"
+ message="IActivityTaskManager permission check can be converted to @EnforcePermission annotation"
+ errorLine1=" mAmInternal.enforceCallingPermission(READ_FRAME_BUFFER, &quot;getTaskSnapshot()&quot;);"
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="out/soong/.intermediates/frameworks/base/services/core/services.core.protologsrc/gen/services.core.protolog.srcjar!/frameworks/base/services/core/java/com/android/server/wm/ActivityTaskManagerService.java"
+ line="3978"
+ column="9"/>
+ </issue>
+
+ <issue
+ id="SimpleManualPermissionEnforcement"
+ message="IActivityTaskManager permission check can be converted to @EnforcePermission annotation"
+ errorLine1=" mAmInternal.enforceCallingPermission(READ_FRAME_BUFFER, &quot;takeTaskSnapshot()&quot;);"
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="out/soong/.intermediates/frameworks/base/services/core/services.core.protologsrc/gen/services.core.protolog.srcjar!/frameworks/base/services/core/java/com/android/server/wm/ActivityTaskManagerService.java"
+ line="4000"
+ column="9"/>
+ </issue>
+
+ <issue
+ id="SimpleManualPermissionEnforcement"
+ message="IActivityTaskManager permission check can be converted to @EnforcePermission annotation"
+ errorLine1=" mAmInternal.enforceCallingPermission("
+ errorLine2=" ^">
+ <location
+ file="out/soong/.intermediates/frameworks/base/services/core/services.core.protologsrc/gen/services.core.protolog.srcjar!/frameworks/base/services/core/java/com/android/server/wm/ActivityTaskManagerService.java"
+ line="4029"
+ column="9"/>
+ </issue>
+
+ <issue
+ id="SimpleManualPermissionEnforcement"
+ message="IActivityTaskManager permission check can be converted to @EnforcePermission annotation"
+ errorLine1=" mAmInternal.enforceCallingPermission(CONTROL_REMOTE_APP_TRANSITION_ANIMATIONS,"
+ errorLine2=" ^">
+ <location
+ file="out/soong/.intermediates/frameworks/base/services/core/services.core.protologsrc/gen/services.core.protolog.srcjar!/frameworks/base/services/core/java/com/android/server/wm/ActivityTaskManagerService.java"
+ line="4057"
+ column="9"/>
+ </issue>
+
+ <issue
+ id="SimpleManualPermissionEnforcement"
+ message="IActivityTaskManager permission check can be converted to @EnforcePermission annotation"
+ errorLine1=" mAmInternal.enforceCallingPermission(CONTROL_REMOTE_APP_TRANSITION_ANIMATIONS,"
+ errorLine2=" ^">
+ <location
+ file="out/soong/.intermediates/frameworks/base/services/core/services.core.protologsrc/gen/services.core.protolog.srcjar!/frameworks/base/services/core/java/com/android/server/wm/ActivityTaskManagerService.java"
+ line="4074"
+ column="9"/>
+ </issue>
+
+ <issue
+ id="SimpleManualPermissionEnforcement"
+ message="IActivityTaskManager permission check can be converted to @EnforcePermission annotation"
+ errorLine1=" mAmInternal.enforceCallingPermission(STOP_APP_SWITCHES, &quot;stopAppSwitches&quot;);"
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="out/soong/.intermediates/frameworks/base/services/core/services.core.protologsrc/gen/services.core.protolog.srcjar!/frameworks/base/services/core/java/com/android/server/wm/ActivityTaskManagerService.java"
+ line="4136"
+ column="9"/>
+ </issue>
+
+ <issue
+ id="SimpleManualPermissionEnforcement"
+ message="IActivityTaskManager permission check can be converted to @EnforcePermission annotation"
+ errorLine1=" mAmInternal.enforceCallingPermission(STOP_APP_SWITCHES, &quot;resumeAppSwitches&quot;);"
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="out/soong/.intermediates/frameworks/base/services/core/services.core.protologsrc/gen/services.core.protolog.srcjar!/frameworks/base/services/core/java/com/android/server/wm/ActivityTaskManagerService.java"
+ line="4147"
+ column="9"/>
+ </issue>
+
+ <issue
+ id="SimpleManualPermissionEnforcement"
+ message="IActivityTaskManager permission check can be converted to @EnforcePermission annotation"
+ errorLine1=" mAmInternal.enforceCallingPermission(android.Manifest.permission.SET_SCREEN_COMPATIBILITY,"
+ errorLine2=" ^">
+ <location
+ file="out/soong/.intermediates/frameworks/base/services/core/services.core.protologsrc/gen/services.core.protolog.srcjar!/frameworks/base/services/core/java/com/android/server/wm/ActivityTaskManagerService.java"
+ line="4198"
+ column="9"/>
+ </issue>
+
+ <issue
+ id="SimpleManualPermissionEnforcement"
+ message="IActivityTaskManager permission check can be converted to @EnforcePermission annotation"
+ errorLine1=" mAmInternal.enforceCallingPermission(android.Manifest.permission.SET_SCREEN_COMPATIBILITY,"
+ errorLine2=" ^">
+ <location
+ file="out/soong/.intermediates/frameworks/base/services/core/services.core.protologsrc/gen/services.core.protolog.srcjar!/frameworks/base/services/core/java/com/android/server/wm/ActivityTaskManagerService.java"
+ line="4215"
+ column="9"/>
+ </issue>
+
+ <issue
+ id="SimpleManualPermissionEnforcement"
+ message="IActivityTaskManager permission check can be converted to @EnforcePermission annotation"
+ errorLine1=" mAmInternal.enforceCallingPermission(CONTROL_REMOTE_APP_TRANSITION_ANIMATIONS,"
+ errorLine2=" ^">
+ <location
+ file="out/soong/.intermediates/frameworks/base/services/core/services.core.protologsrc/gen/services.core.protolog.srcjar!/frameworks/base/services/core/java/com/android/server/wm/ActivityTaskManagerService.java"
+ line="4280"
+ column="9"/>
+ </issue>
+
+ <issue
+ id="SimpleManualPermissionEnforcement"
+ message="IActivityTaskManager permission check can be converted to @EnforcePermission annotation"
+ errorLine1=" mAmInternal.enforceCallingPermission(DETECT_SCREEN_CAPTURE,"
+ errorLine2=" ^">
+ <location
+ file="out/soong/.intermediates/frameworks/base/services/core/services.core.protologsrc/gen/services.core.protolog.srcjar!/frameworks/base/services/core/java/com/android/server/wm/ActivityTaskManagerService.java"
+ line="5836"
+ column="9"/>
+ </issue>
+
+ <issue
+ id="SimpleManualPermissionEnforcement"
+ message="IActivityTaskManager permission check can be converted to @EnforcePermission annotation"
+ errorLine1=" mAmInternal.enforceCallingPermission(DETECT_SCREEN_CAPTURE,"
+ errorLine2=" ^">
+ <location
+ file="out/soong/.intermediates/frameworks/base/services/core/services.core.protologsrc/gen/services.core.protolog.srcjar!/frameworks/base/services/core/java/com/android/server/wm/ActivityTaskManagerService.java"
+ line="5849"
+ column="9"/>
+ </issue>
+
+ <issue
+ id="SimpleManualPermissionEnforcement"
+ message="IColorDisplayManager permission check should be converted to @EnforcePermission annotation"
+ errorLine1=" getContext().enforceCallingOrSelfPermission("
+ errorLine2=" ^">
+ <location
+ file="frameworks/base/services/core/java/com/android/server/display/color/ColorDisplayService.java"
+ line="2152"
+ column="13"/>
+ </issue>
+
+ <issue
+ id="SimpleManualPermissionEnforcement"
+ message="IMediaProjectionManager permission check can be converted to @EnforcePermission annotation"
+ errorLine1=" if (mContext.checkCallingPermission(MANAGE_MEDIA_PROJECTION)"
+ errorLine2=" ^">
+ <location
+ file="frameworks/base/services/core/java/com/android/server/media/projection/MediaProjectionManagerService.java"
+ line="779"
+ column="13"/>
+ </issue>
+
+ <issue
+ id="SimpleManualPermissionEnforcement"
+ message="IMediaProjectionManager permission check can be converted to @EnforcePermission annotation"
+ errorLine1=" if (mContext.checkCallingPermission(MANAGE_MEDIA_PROJECTION)"
+ errorLine2=" ^">
+ <location
+ file="frameworks/base/services/core/java/com/android/server/media/projection/MediaProjectionManagerService.java"
+ line="820"
+ column="13"/>
+ </issue>
+
+ <issue
+ id="SimpleManualPermissionEnforcement"
+ message="IMediaProjectionManager permission check can be converted to @EnforcePermission annotation"
+ errorLine1=" if (mContext.checkCallingPermission(MANAGE_MEDIA_PROJECTION)"
+ errorLine2=" ^">
+ <location
+ file="frameworks/base/services/core/java/com/android/server/media/projection/MediaProjectionManagerService.java"
+ line="906"
+ column="13"/>
+ </issue>
+
+ <issue
+ id="SimpleManualPermissionEnforcement"
+ message="INotificationManager permission check can be converted to @EnforcePermission annotation"
+ errorLine1=" if (PERMISSION_GRANTED"
+ errorLine2=" ^">
+ <location
+ file="frameworks/base/services/core/java/com/android/server/notification/NotificationManagerService.java"
+ line="6691"
+ column="13"/>
+ </issue>
+
+ <issue
+ id="SimpleManualPermissionEnforcement"
+ message="INotificationManager permission check can be converted to @EnforcePermission annotation"
+ errorLine1=" if (PERMISSION_GRANTED"
+ errorLine2=" ^">
+ <location
+ file="frameworks/base/services/core/java/com/android/server/notification/NotificationManagerService.java"
+ line="6712"
+ column="13"/>
+ </issue>
+
+ <issue
+ id="SimpleManualPermissionEnforcement"
+ message="IOnDeviceIntelligenceManager permission check can be converted to @EnforcePermission annotation"
+ errorLine1=" mContext.enforceCallingPermission("
+ errorLine2=" ^">
+ <location
+ file="frameworks/base/services/core/java/com/android/server/ondeviceintelligence/OnDeviceIntelligenceManagerService.java"
+ line="237"
+ column="17"/>
+ </issue>
+
+ <issue
+ id="SimpleManualPermissionEnforcement"
+ message="IPackageInstaller permission check should be converted to @EnforcePermission annotation"
+ errorLine1=" if (mContext.checkCallingOrSelfPermission(Manifest.permission.VERIFICATION_AGENT)"
+ errorLine2=" ^">
+ <location
+ file="frameworks/base/services/core/java/com/android/server/pm/PackageInstallerService.java"
+ line="1881"
+ column="9"/>
+ </issue>
+
+ <issue
+ id="SimpleManualPermissionEnforcement"
+ message="IPackageInstaller permission check should be converted to @EnforcePermission annotation"
+ errorLine1=" if (mContext.checkCallingOrSelfPermission(Manifest.permission.VERIFICATION_AGENT)"
+ errorLine2=" ^">
+ <location
+ file="frameworks/base/services/core/java/com/android/server/pm/PackageInstallerService.java"
+ line="1892"
+ column="9"/>
+ </issue>
+
+ <issue
+ id="SimpleManualPermissionEnforcement"
+ message="IPackageManager permission check can be converted to @EnforcePermission annotation"
+ errorLine1=" mContext.enforceCallingPermission(Manifest.permission.SEND_DEVICE_CUSTOMIZATION_READY,"
+ errorLine2=" ^">
+ <location
+ file="frameworks/base/services/core/java/com/android/server/pm/PackageManagerService.java"
+ line="5798"
+ column="13"/>
+ </issue>
+
+ <issue
+ id="SimpleManualPermissionEnforcement"
+ message="IPackageManager permission check can be converted to @EnforcePermission annotation"
+ errorLine1=" mContext.enforceCallingPermission("
+ errorLine2=" ^">
+ <location
+ file="frameworks/base/services/core/java/com/android/server/pm/PackageManagerService.java"
+ line="6266"
+ column="13"/>
+ </issue>
+
+ <issue
+ id="SimpleManualPermissionEnforcement"
+ message="ITvInputManager permission check can be converted to @EnforcePermission annotation"
+ errorLine1=" if (mContext.checkCallingPermission("
+ errorLine2=" ^">
+ <location
+ file="frameworks/base/services/core/java/com/android/server/tv/TvInputManagerService.java"
+ line="1406"
+ column="13"/>
+ </issue>
+
+ <issue
+ id="SimpleManualPermissionEnforcement"
+ message="ITvInputManager permission check can be converted to @EnforcePermission annotation"
+ errorLine1=" if (mContext.checkCallingPermission(android.Manifest.permission.NOTIFY_TV_INPUTS)"
+ errorLine2=" ^">
+ <location
+ file="frameworks/base/services/core/java/com/android/server/tv/TvInputManagerService.java"
+ line="1427"
+ column="13"/>
+ </issue>
+
+ <issue
+ id="SimpleManualPermissionEnforcement"
+ message="ITvInputManager permission check can be converted to @EnforcePermission annotation"
+ errorLine1=" if (mContext.checkCallingPermission("
+ errorLine2=" ^">
+ <location
+ file="frameworks/base/services/core/java/com/android/server/tv/TvInputManagerService.java"
+ line="1734"
+ column="13"/>
+ </issue>
+
+ <issue
+ id="SimpleManualPermissionEnforcement"
+ message="ITvInputManager permission check can be converted to @EnforcePermission annotation"
+ errorLine1=" if (mContext.checkCallingPermission(android.Manifest.permission.DVB_DEVICE)"
+ errorLine2=" ^">
+ <location
+ file="frameworks/base/services/core/java/com/android/server/tv/TvInputManagerService.java"
+ line="2509"
+ column="13"/>
+ </issue>
+
+ <issue
+ id="SimpleManualPermissionEnforcement"
+ message="ITvInputManager permission check can be converted to @EnforcePermission annotation"
+ errorLine1=" if (mContext.checkCallingPermission(android.Manifest.permission.DVB_DEVICE)"
+ errorLine2=" ^">
+ <location
+ file="frameworks/base/services/core/java/com/android/server/tv/TvInputManagerService.java"
+ line="2564"
+ column="13"/>
+ </issue>
+
+ <issue
+ id="SimpleManualPermissionEnforcement"
+ message="ITvInputManager permission check can be converted to @EnforcePermission annotation"
+ errorLine1=" if (mContext.checkCallingPermission(android.Manifest.permission.ACCESS_TUNED_INFO)"
+ errorLine2=" ^">
+ <location
+ file="frameworks/base/services/core/java/com/android/server/tv/TvInputManagerService.java"
+ line="2932"
+ column="13"/>
+ </issue>
+
+ <issue
+ id="SimpleManualPermissionEnforcement"
+ message="IUriGrantsManager permission check can be converted to @EnforcePermission annotation"
+ errorLine1=" mAmInternal.enforceCallingPermission("
+ errorLine2=" ^">
+ <location
+ file="frameworks/base/services/core/java/com/android/server/uri/UriGrantsManagerService.java"
+ line="366"
+ column="9"/>
+ </issue>
+
+ <issue
+ id="SimpleManualPermissionEnforcement"
+ message="IUriGrantsManager permission check can be converted to @EnforcePermission annotation"
+ errorLine1=" mAmInternal.enforceCallingPermission("
+ errorLine2=" ^">
+ <location
+ file="frameworks/base/services/core/java/com/android/server/uri/UriGrantsManagerService.java"
+ line="444"
+ column="9"/>
+ </issue>
+
+ <issue
+ id="SimpleManualPermissionEnforcement"
+ message="IVcnManagementService permission check should be converted to @EnforcePermission annotation"
+ errorLine1=" mContext.enforceCallingOrSelfPermission(DUMP, TAG);"
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="frameworks/base/services/core/java/com/android/server/VcnManagementService.java"
+ line="1329"
+ column="9"/>
+ </issue>
+
+</issues>
diff --git a/services/profcollect/src/com/android/server/profcollect/ProfcollectForwardingService.java b/services/profcollect/src/com/android/server/profcollect/ProfcollectForwardingService.java
index 105147fe212d..42c171bb4351 100644
--- a/services/profcollect/src/com/android/server/profcollect/ProfcollectForwardingService.java
+++ b/services/profcollect/src/com/android/server/profcollect/ProfcollectForwardingService.java
@@ -26,6 +26,7 @@ import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
import android.hardware.camera2.CameraManager;
+import android.hardware.usb.UsbManager;
import android.os.Handler;
import android.os.IBinder.DeathRecipient;
import android.os.Looper;
@@ -67,6 +68,8 @@ public final class ProfcollectForwardingService extends SystemService {
private int mUsageSetting;
private boolean mUploadEnabled;
+ private boolean mAdbActive;
+
private IProfCollectd mIProfcollect;
private static ProfcollectForwardingService sSelfService;
private final Handler mHandler = new ProfcollectdHandler(IoThread.getHandler().getLooper());
@@ -84,6 +87,14 @@ public final class ProfcollectForwardingService extends SystemService {
Log.d(LOG_TAG, "Received broadcast to pack and upload reports");
createAndUploadReport(sSelfService);
}
+ if (UsbManager.ACTION_USB_STATE.equals(intent.getAction())) {
+ boolean connected = intent.getBooleanExtra(UsbManager.USB_CONNECTED, false);
+ boolean isADB = intent.getBooleanExtra(UsbManager.USB_FUNCTION_ADB, false);
+ if (isADB) {
+ Log.d(LOG_TAG, "Received broadcast that ADB became " + connected);
+ mAdbActive = connected;
+ }
+ }
}
};
@@ -106,8 +117,12 @@ public final class ProfcollectForwardingService extends SystemService {
mUploadEnabled =
context.getResources().getBoolean(R.bool.config_profcollectReportUploaderEnabled);
+ // TODO: ADB might already be active when our service started.
+ mAdbActive = false;
+
final IntentFilter filter = new IntentFilter();
filter.addAction(INTENT_UPLOAD_PROFILES);
+ filter.addAction(UsbManager.ACTION_USB_STATE);
context.registerReceiver(mBroadcastReceiver, filter, Context.RECEIVER_NOT_EXPORTED);
}
@@ -281,6 +296,9 @@ public final class ProfcollectForwardingService extends SystemService {
if (mIProfcollect == null) {
return;
}
+ if (mAdbActive) {
+ return;
+ }
if (Utils.withFrequency("applaunch_trace_freq", 5)) {
Utils.traceSystem(mIProfcollect, "applaunch");
}
@@ -303,6 +321,9 @@ public final class ProfcollectForwardingService extends SystemService {
if (mIProfcollect == null) {
return;
}
+ if (mAdbActive) {
+ return;
+ }
if (Utils.withFrequency("dex2oat_trace_freq", 25)) {
// Dex2oat could take a while before it starts. Add a short delay before start tracing.
Utils.traceSystem(mIProfcollect, "dex2oat", /* delayMs */ 1000);
diff --git a/services/tests/PackageManagerServiceTests/server/src/com/android/server/pm/verify/pkg/VerifierControllerTest.java b/services/tests/PackageManagerServiceTests/server/src/com/android/server/pm/verify/pkg/VerifierControllerTest.java
index 24617984eaf7..3046d4beb7a3 100644
--- a/services/tests/PackageManagerServiceTests/server/src/com/android/server/pm/verify/pkg/VerifierControllerTest.java
+++ b/services/tests/PackageManagerServiceTests/server/src/com/android/server/pm/verify/pkg/VerifierControllerTest.java
@@ -24,6 +24,7 @@ import static com.google.common.truth.Truth.assertThat;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.anyInt;
import static org.mockito.ArgumentMatchers.anyLong;
+import static org.mockito.ArgumentMatchers.anyString;
import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verify;
@@ -49,6 +50,7 @@ import android.util.Pair;
import androidx.test.ext.junit.runners.AndroidJUnit4;
import androidx.test.filters.SmallTest;
+import androidx.test.platform.app.InstrumentationRegistry;
import com.android.internal.infra.AndroidFuture;
import com.android.internal.infra.ServiceConnector;
@@ -122,6 +124,10 @@ public class VerifierControllerTest {
@Before
public void setUp() {
MockitoAnnotations.initMocks(this);
+ // Mock that the UID of this test becomes the UID of the verifier
+ when(mSnapshot.getPackageUidInternal(anyString(), anyLong(), anyInt(), anyInt()))
+ .thenReturn(InstrumentationRegistry.getInstrumentation().getContext()
+ .getApplicationInfo().uid);
when(mInjector.getVerifierPackageName(any(Computer.class), anyInt())).thenReturn(
TEST_VERIFIER_COMPONENT_NAME.getPackageName());
when(mInjector.getRemoteService(
diff --git a/services/tests/displayservicetests/src/com/android/server/display/DisplayTopologyCoordinatorTest.kt b/services/tests/displayservicetests/src/com/android/server/display/DisplayTopologyCoordinatorTest.kt
index 17af6335e25b..85e73561cf59 100644
--- a/services/tests/displayservicetests/src/com/android/server/display/DisplayTopologyCoordinatorTest.kt
+++ b/services/tests/displayservicetests/src/com/android/server/display/DisplayTopologyCoordinatorTest.kt
@@ -21,7 +21,7 @@ import android.view.Display
import android.view.DisplayInfo
import org.junit.Before
import org.junit.Test
-import org.mockito.ArgumentMatchers.anyDouble
+import org.mockito.ArgumentMatchers.anyFloat
import org.mockito.ArgumentMatchers.anyInt
import org.mockito.kotlin.mock
import org.mockito.kotlin.never
@@ -55,9 +55,9 @@ class DisplayTopologyCoordinatorTest {
coordinator.onDisplayAdded(displayInfo)
- val widthDp = displayInfo.logicalWidth * (DisplayMetrics.DENSITY_DEFAULT.toDouble()
+ val widthDp = displayInfo.logicalWidth * (DisplayMetrics.DENSITY_DEFAULT.toFloat()
/ displayInfo.logicalDensityDpi)
- val heightDp = displayInfo.logicalHeight * (DisplayMetrics.DENSITY_DEFAULT.toDouble()
+ val heightDp = displayInfo.logicalHeight * (DisplayMetrics.DENSITY_DEFAULT.toFloat()
/ displayInfo.logicalDensityDpi)
verify(mockTopology).addDisplay(displayInfo.displayId, widthDp, heightDp)
}
@@ -68,7 +68,7 @@ class DisplayTopologyCoordinatorTest {
coordinator.onDisplayAdded(displayInfo)
- verify(mockTopology, never()).addDisplay(anyInt(), anyDouble(), anyDouble())
+ verify(mockTopology, never()).addDisplay(anyInt(), anyFloat(), anyFloat())
}
@Test
@@ -78,6 +78,6 @@ class DisplayTopologyCoordinatorTest {
coordinator.onDisplayAdded(displayInfo)
- verify(mockTopology, never()).addDisplay(anyInt(), anyDouble(), anyDouble())
+ verify(mockTopology, never()).addDisplay(anyInt(), anyFloat(), anyFloat())
}
} \ No newline at end of file
diff --git a/services/tests/displayservicetests/src/com/android/server/display/DisplayTopologyTest.kt b/services/tests/displayservicetests/src/com/android/server/display/DisplayTopologyTest.kt
index f3a8d8415c19..cd8c26d0d337 100644
--- a/services/tests/displayservicetests/src/com/android/server/display/DisplayTopologyTest.kt
+++ b/services/tests/displayservicetests/src/com/android/server/display/DisplayTopologyTest.kt
@@ -17,6 +17,9 @@
package com.android.server.display
import android.view.Display
+import com.android.server.display.DisplayTopology.TreeNode.Position.POSITION_BOTTOM
+import com.android.server.display.DisplayTopology.TreeNode.Position.POSITION_TOP
+import com.android.server.display.DisplayTopology.TreeNode.Position.POSITION_RIGHT
import com.google.common.truth.Truth.assertThat
import org.junit.Test
@@ -26,8 +29,8 @@ class DisplayTopologyTest {
@Test
fun addOneDisplay() {
val displayId = 1
- val width = 800.0
- val height = 600.0
+ val width = 800f
+ val height = 600f
topology.addDisplay(displayId, width, height)
@@ -43,12 +46,12 @@ class DisplayTopologyTest {
@Test
fun addTwoDisplays() {
val displayId1 = 1
- val width1 = 800.0
- val height1 = 600.0
+ val width1 = 800f
+ val height1 = 600f
val displayId2 = 2
- val width2 = 1000.0
- val height2 = 1500.0
+ val width2 = 1000f
+ val height2 = 1500f
topology.addDisplay(displayId1, width1, height1)
topology.addDisplay(displayId2, width2, height2)
@@ -66,20 +69,19 @@ class DisplayTopologyTest {
assertThat(display2.mWidth).isEqualTo(width2)
assertThat(display2.mHeight).isEqualTo(height2)
assertThat(display2.mChildren).isEmpty()
- assertThat(display2.mPosition).isEqualTo(
- DisplayTopology.TreeNode.Position.POSITION_TOP)
+ assertThat(display2.mPosition).isEqualTo(POSITION_TOP)
assertThat(display2.mOffset).isEqualTo(width1 / 2 - width2 / 2)
}
@Test
fun addManyDisplays() {
val displayId1 = 1
- val width1 = 800.0
- val height1 = 600.0
+ val width1 = 800f
+ val height1 = 600f
val displayId2 = 2
- val width2 = 1000.0
- val height2 = 1500.0
+ val width2 = 1000f
+ val height2 = 1500f
topology.addDisplay(displayId1, width1, height1)
topology.addDisplay(displayId2, width2, height2)
@@ -102,8 +104,7 @@ class DisplayTopologyTest {
assertThat(display2.mWidth).isEqualTo(width2)
assertThat(display2.mHeight).isEqualTo(height2)
assertThat(display2.mChildren).hasSize(1)
- assertThat(display2.mPosition).isEqualTo(
- DisplayTopology.TreeNode.Position.POSITION_TOP)
+ assertThat(display2.mPosition).isEqualTo(POSITION_TOP)
assertThat(display2.mOffset).isEqualTo(width1 / 2 - width2 / 2)
var display = display2
@@ -114,8 +115,7 @@ class DisplayTopologyTest {
assertThat(display.mHeight).isEqualTo(height1)
// The last display should have no children
assertThat(display.mChildren).hasSize(if (i < noOfDisplays) 1 else 0)
- assertThat(display.mPosition).isEqualTo(
- DisplayTopology.TreeNode.Position.POSITION_RIGHT)
+ assertThat(display.mPosition).isEqualTo(POSITION_RIGHT)
assertThat(display.mOffset).isEqualTo(0)
}
}
@@ -123,12 +123,12 @@ class DisplayTopologyTest {
@Test
fun removeDisplays() {
val displayId1 = 1
- val width1 = 800.0
- val height1 = 600.0
+ val width1 = 800f
+ val height1 = 600f
val displayId2 = 2
- val width2 = 1000.0
- val height2 = 1500.0
+ val width2 = 1000f
+ val height2 = 1500f
topology.addDisplay(displayId1, width1, height1)
topology.addDisplay(displayId2, width2, height2)
@@ -154,8 +154,7 @@ class DisplayTopologyTest {
assertThat(display2.mWidth).isEqualTo(width2)
assertThat(display2.mHeight).isEqualTo(height2)
assertThat(display2.mChildren).hasSize(1)
- assertThat(display2.mPosition).isEqualTo(
- DisplayTopology.TreeNode.Position.POSITION_TOP)
+ assertThat(display2.mPosition).isEqualTo(POSITION_TOP)
assertThat(display2.mOffset).isEqualTo(width1 / 2 - width2 / 2)
var display = display2
@@ -169,8 +168,7 @@ class DisplayTopologyTest {
assertThat(display.mHeight).isEqualTo(height1)
// The last display should have no children
assertThat(display.mChildren).hasSize(if (i < noOfDisplays) 1 else 0)
- assertThat(display.mPosition).isEqualTo(
- DisplayTopology.TreeNode.Position.POSITION_RIGHT)
+ assertThat(display.mPosition).isEqualTo(POSITION_RIGHT)
assertThat(display.mOffset).isEqualTo(0)
}
@@ -194,8 +192,7 @@ class DisplayTopologyTest {
assertThat(display2.mWidth).isEqualTo(width2)
assertThat(display2.mHeight).isEqualTo(height2)
assertThat(display2.mChildren).hasSize(1)
- assertThat(display2.mPosition).isEqualTo(
- DisplayTopology.TreeNode.Position.POSITION_TOP)
+ assertThat(display2.mPosition).isEqualTo(POSITION_TOP)
assertThat(display2.mOffset).isEqualTo(width1 / 2 - width2 / 2)
display = display2
@@ -209,8 +206,7 @@ class DisplayTopologyTest {
assertThat(display.mHeight).isEqualTo(height1)
// The last display should have no children
assertThat(display.mChildren).hasSize(if (i < noOfDisplays) 1 else 0)
- assertThat(display.mPosition).isEqualTo(
- DisplayTopology.TreeNode.Position.POSITION_RIGHT)
+ assertThat(display.mPosition).isEqualTo(POSITION_RIGHT)
assertThat(display.mOffset).isEqualTo(0)
}
}
@@ -218,8 +214,8 @@ class DisplayTopologyTest {
@Test
fun removeAllDisplays() {
val displayId = 1
- val width = 800.0
- val height = 600.0
+ val width = 800f
+ val height = 600f
topology.addDisplay(displayId, width, height)
topology.removeDisplay(displayId)
@@ -231,8 +227,8 @@ class DisplayTopologyTest {
@Test
fun removeDisplayThatDoesNotExist() {
val displayId = 1
- val width = 800.0
- val height = 600.0
+ val width = 800f
+ val height = 600f
topology.addDisplay(displayId, width, height)
topology.removeDisplay(3)
@@ -245,4 +241,236 @@ class DisplayTopologyTest {
assertThat(display.mHeight).isEqualTo(height)
assertThat(display.mChildren).isEmpty()
}
+
+ @Test
+ fun removePrimaryDisplay() {
+ val displayId1 = 1
+ val displayId2 = 2
+ val width = 800f
+ val height = 600f
+
+ topology.addDisplay(displayId1, width, height)
+ topology.addDisplay(displayId2, width, height)
+ topology.mPrimaryDisplayId = displayId2
+ topology.removeDisplay(displayId2)
+
+ assertThat(topology.mPrimaryDisplayId).isEqualTo(displayId1)
+ val display = topology.mRoot!!
+ assertThat(display.mDisplayId).isEqualTo(displayId1)
+ assertThat(display.mWidth).isEqualTo(width)
+ assertThat(display.mHeight).isEqualTo(height)
+ assertThat(display.mChildren).isEmpty()
+ }
+
+ @Test
+ fun normalization_noOverlaps_leavesTopologyUnchanged() {
+ val display1 = DisplayTopology.TreeNode(/* displayId= */ 1, /* width= */ 200f,
+ /* height= */ 600f, /* position= */ null, /* offset= */ 0f)
+ topology.mRoot = display1
+
+ val display2 = DisplayTopology.TreeNode(/* displayId= */ 2, /* width= */ 600f,
+ /* height= */ 200f, POSITION_RIGHT, /* offset= */ 0f)
+ display1.mChildren.add(display2)
+
+ val primaryDisplayId = 3
+ val display3 = DisplayTopology.TreeNode(primaryDisplayId, /* width= */ 600f,
+ /* height= */ 200f, POSITION_RIGHT, /* offset= */ 400f)
+ display1.mChildren.add(display3)
+ topology.mPrimaryDisplayId = primaryDisplayId
+
+ val display4 = DisplayTopology.TreeNode(/* displayId= */ 4, /* width= */ 200f,
+ /* height= */ 600f, POSITION_RIGHT, /* offset= */ 0f)
+ display2.mChildren.add(display4)
+
+ topology.normalize()
+
+ assertThat(topology.mPrimaryDisplayId).isEqualTo(primaryDisplayId)
+
+ val actualDisplay1 = topology.mRoot!!
+ assertThat(actualDisplay1.mDisplayId).isEqualTo(1)
+ assertThat(actualDisplay1.mWidth).isEqualTo(200f)
+ assertThat(actualDisplay1.mHeight).isEqualTo(600f)
+ assertThat(actualDisplay1.mChildren).hasSize(2)
+
+ val actualDisplay2 = actualDisplay1.mChildren[0]
+ assertThat(actualDisplay2.mDisplayId).isEqualTo(2)
+ assertThat(actualDisplay2.mWidth).isEqualTo(600f)
+ assertThat(actualDisplay2.mHeight).isEqualTo(200f)
+ assertThat(actualDisplay2.mPosition).isEqualTo(POSITION_RIGHT)
+ assertThat(actualDisplay2.mOffset).isEqualTo(0f)
+ assertThat(actualDisplay2.mChildren).hasSize(1)
+
+ val actualDisplay3 = actualDisplay1.mChildren[1]
+ assertThat(actualDisplay3.mDisplayId).isEqualTo(3)
+ assertThat(actualDisplay3.mWidth).isEqualTo(600f)
+ assertThat(actualDisplay3.mHeight).isEqualTo(200f)
+ assertThat(actualDisplay3.mPosition).isEqualTo(POSITION_RIGHT)
+ assertThat(actualDisplay3.mOffset).isEqualTo(400f)
+ assertThat(actualDisplay3.mChildren).isEmpty()
+
+ val actualDisplay4 = actualDisplay2.mChildren[0]
+ assertThat(actualDisplay4.mDisplayId).isEqualTo(4)
+ assertThat(actualDisplay4.mWidth).isEqualTo(200f)
+ assertThat(actualDisplay4.mHeight).isEqualTo(600f)
+ assertThat(actualDisplay4.mPosition).isEqualTo(POSITION_RIGHT)
+ assertThat(actualDisplay4.mOffset).isEqualTo(0f)
+ assertThat(actualDisplay4.mChildren).isEmpty()
+ }
+
+ @Test
+ fun normalization_moveDisplayWithoutReparenting() {
+ val display1 = DisplayTopology.TreeNode(/* displayId= */ 1, /* width= */ 200f,
+ /* height= */ 600f, /* position= */ null, /* offset= */ 0f)
+ topology.mRoot = display1
+
+ val display2 = DisplayTopology.TreeNode(/* displayId= */ 2, /* width= */ 200f,
+ /* height= */ 600f, POSITION_RIGHT, /* offset= */ 0f)
+ display1.mChildren.add(display2)
+
+ val primaryDisplayId = 3
+ val display3 = DisplayTopology.TreeNode(primaryDisplayId, /* width= */ 600f,
+ /* height= */ 200f, POSITION_RIGHT, /* offset= */ 10f)
+ display1.mChildren.add(display3)
+ topology.mPrimaryDisplayId = primaryDisplayId
+
+ val display4 = DisplayTopology.TreeNode(/* displayId= */ 4, /* width= */ 200f,
+ /* height= */ 600f, POSITION_RIGHT, /* offset= */ 0f)
+ display2.mChildren.add(display4)
+
+ // Display 3 becomes a child of display 2. Display 4 gets moved without changing its parent.
+ topology.normalize()
+
+ assertThat(topology.mPrimaryDisplayId).isEqualTo(primaryDisplayId)
+
+ val actualDisplay1 = topology.mRoot!!
+ assertThat(actualDisplay1.mDisplayId).isEqualTo(1)
+ assertThat(actualDisplay1.mWidth).isEqualTo(200f)
+ assertThat(actualDisplay1.mHeight).isEqualTo(600f)
+ assertThat(actualDisplay1.mChildren).hasSize(1)
+
+ val actualDisplay2 = actualDisplay1.mChildren[0]
+ assertThat(actualDisplay2.mDisplayId).isEqualTo(2)
+ assertThat(actualDisplay2.mWidth).isEqualTo(200f)
+ assertThat(actualDisplay2.mHeight).isEqualTo(600f)
+ assertThat(actualDisplay2.mPosition).isEqualTo(POSITION_RIGHT)
+ assertThat(actualDisplay2.mOffset).isEqualTo(0f)
+ assertThat(actualDisplay2.mChildren).hasSize(2)
+
+ val actualDisplay3 = actualDisplay2.mChildren[1]
+ assertThat(actualDisplay3.mDisplayId).isEqualTo(3)
+ assertThat(actualDisplay3.mWidth).isEqualTo(600f)
+ assertThat(actualDisplay3.mHeight).isEqualTo(200f)
+ assertThat(actualDisplay3.mPosition).isEqualTo(POSITION_RIGHT)
+ assertThat(actualDisplay3.mOffset).isEqualTo(10f)
+ assertThat(actualDisplay3.mChildren).isEmpty()
+
+ val actualDisplay4 = actualDisplay2.mChildren[0]
+ assertThat(actualDisplay4.mDisplayId).isEqualTo(4)
+ assertThat(actualDisplay4.mWidth).isEqualTo(200f)
+ assertThat(actualDisplay4.mHeight).isEqualTo(600f)
+ assertThat(actualDisplay4.mPosition).isEqualTo(POSITION_RIGHT)
+ assertThat(actualDisplay4.mOffset).isEqualTo(210f)
+ assertThat(actualDisplay4.mChildren).isEmpty()
+ }
+
+ @Test
+ fun normalization_moveDisplayWithoutReparenting_offsetOutOfBounds() {
+ val display1 = DisplayTopology.TreeNode(/* displayId= */ 1, /* width= */ 200f,
+ /* height= */ 50f, /* position= */ null, /* offset= */ 0f)
+ topology.mRoot = display1
+
+ val display2 = DisplayTopology.TreeNode(/* displayId= */ 2, /* width= */ 600f,
+ /* height= */ 200f, POSITION_RIGHT, /* offset= */ 0f)
+ display1.mChildren.add(display2)
+
+ val primaryDisplayId = 3
+ val display3 = DisplayTopology.TreeNode(primaryDisplayId, /* width= */ 600f,
+ /* height= */ 200f, POSITION_RIGHT, /* offset= */ 10f)
+ display1.mChildren.add(display3)
+ topology.mPrimaryDisplayId = primaryDisplayId
+
+ // Display 3 gets moved and its left side is still on the same line as the right side
+ // of Display 1, but it no longer touches it (the offset is out of bounds), so Display 2
+ // becomes its new parent.
+ topology.normalize()
+
+ assertThat(topology.mPrimaryDisplayId).isEqualTo(primaryDisplayId)
+
+ val actualDisplay1 = topology.mRoot!!
+ assertThat(actualDisplay1.mDisplayId).isEqualTo(1)
+ assertThat(actualDisplay1.mWidth).isEqualTo(200f)
+ assertThat(actualDisplay1.mHeight).isEqualTo(50f)
+ assertThat(actualDisplay1.mChildren).hasSize(1)
+
+ val actualDisplay2 = actualDisplay1.mChildren[0]
+ assertThat(actualDisplay2.mDisplayId).isEqualTo(2)
+ assertThat(actualDisplay2.mWidth).isEqualTo(600f)
+ assertThat(actualDisplay2.mHeight).isEqualTo(200f)
+ assertThat(actualDisplay2.mPosition).isEqualTo(POSITION_RIGHT)
+ assertThat(actualDisplay2.mOffset).isEqualTo(0f)
+ assertThat(actualDisplay2.mChildren).hasSize(1)
+
+ val actualDisplay3 = actualDisplay2.mChildren[0]
+ assertThat(actualDisplay3.mDisplayId).isEqualTo(3)
+ assertThat(actualDisplay3.mWidth).isEqualTo(600f)
+ assertThat(actualDisplay3.mHeight).isEqualTo(200f)
+ assertThat(actualDisplay3.mPosition).isEqualTo(POSITION_BOTTOM)
+ assertThat(actualDisplay3.mOffset).isEqualTo(0f)
+ assertThat(actualDisplay3.mChildren).isEmpty()
+ }
+
+ @Test
+ fun normalization_moveAndReparentDisplay() {
+ val display1 = DisplayTopology.TreeNode(/* displayId= */ 1, /* width= */ 200f,
+ /* height= */ 600f, /* position= */ null, /* offset= */ 0f)
+ topology.mRoot = display1
+
+ val display2 = DisplayTopology.TreeNode(/* displayId= */ 2, /* width= */ 200f,
+ /* height= */ 600f, POSITION_RIGHT, /* offset= */ 0f)
+ display1.mChildren.add(display2)
+
+ val primaryDisplayId = 3
+ val display3 = DisplayTopology.TreeNode(primaryDisplayId, /* width= */ 600f,
+ /* height= */ 200f, POSITION_RIGHT, /* offset= */ 400f)
+ display1.mChildren.add(display3)
+ topology.mPrimaryDisplayId = primaryDisplayId
+
+ val display4 = DisplayTopology.TreeNode(/* displayId= */ 4, /* width= */ 200f,
+ /* height= */ 600f, POSITION_RIGHT, /* offset= */ 0f)
+ display2.mChildren.add(display4)
+
+ topology.normalize()
+
+ assertThat(topology.mPrimaryDisplayId).isEqualTo(primaryDisplayId)
+
+ val actualDisplay1 = topology.mRoot!!
+ assertThat(actualDisplay1.mDisplayId).isEqualTo(1)
+ assertThat(actualDisplay1.mWidth).isEqualTo(200f)
+ assertThat(actualDisplay1.mHeight).isEqualTo(600f)
+ assertThat(actualDisplay1.mChildren).hasSize(1)
+
+ val actualDisplay2 = actualDisplay1.mChildren[0]
+ assertThat(actualDisplay2.mDisplayId).isEqualTo(2)
+ assertThat(actualDisplay2.mWidth).isEqualTo(200f)
+ assertThat(actualDisplay2.mHeight).isEqualTo(600f)
+ assertThat(actualDisplay2.mPosition).isEqualTo(POSITION_RIGHT)
+ assertThat(actualDisplay2.mOffset).isEqualTo(0f)
+ assertThat(actualDisplay2.mChildren).hasSize(1)
+
+ val actualDisplay3 = actualDisplay2.mChildren[0]
+ assertThat(actualDisplay3.mDisplayId).isEqualTo(3)
+ assertThat(actualDisplay3.mWidth).isEqualTo(600f)
+ assertThat(actualDisplay3.mHeight).isEqualTo(200f)
+ assertThat(actualDisplay3.mPosition).isEqualTo(POSITION_RIGHT)
+ assertThat(actualDisplay3.mOffset).isEqualTo(400f)
+ assertThat(actualDisplay3.mChildren).hasSize(1)
+
+ val actualDisplay4 = actualDisplay3.mChildren[0]
+ assertThat(actualDisplay4.mDisplayId).isEqualTo(4)
+ assertThat(actualDisplay4.mWidth).isEqualTo(200f)
+ assertThat(actualDisplay4.mHeight).isEqualTo(600f)
+ assertThat(actualDisplay4.mPosition).isEqualTo(POSITION_RIGHT)
+ assertThat(actualDisplay4.mOffset).isEqualTo(-400f)
+ assertThat(actualDisplay4.mChildren).isEmpty()
+ }
} \ No newline at end of file
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 6ab72cd33042..b0053581963a 100644
--- a/services/tests/mockingservicestests/src/com/android/server/am/MockingOomAdjusterTests.java
+++ b/services/tests/mockingservicestests/src/com/android/server/am/MockingOomAdjusterTests.java
@@ -228,6 +228,8 @@ public class MockingOomAdjusterTests {
mock(BatteryStatsService.class));
setFieldValue(ActivityManagerService.class, mService, "mInjector",
new ActivityManagerService.Injector(mContext));
+ setFieldValue(ActivityManagerService.class, mService, "mPhantomProcessList",
+ new PhantomProcessList(mService));
doReturn(mock(AppOpsManager.class)).when(mService).getAppOpsManager();
doCallRealMethod().when(mService).enqueueOomAdjTargetLocked(any(ProcessRecord.class));
doCallRealMethod().when(mService).updateOomAdjPendingTargetsLocked(OOM_ADJ_REASON_ACTIVITY);
diff --git a/services/tests/security/forensic/src/com/android/server/security/forensic/ForensicServiceTest.java b/services/tests/security/forensic/src/com/android/server/security/forensic/ForensicServiceTest.java
index 2b55303bd89f..feb00e760ce6 100644
--- a/services/tests/security/forensic/src/com/android/server/security/forensic/ForensicServiceTest.java
+++ b/services/tests/security/forensic/src/com/android/server/security/forensic/ForensicServiceTest.java
@@ -19,23 +19,35 @@ package com.android.server.security.forensic;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertNull;
+import static org.mockito.ArgumentMatchers.any;
import static org.mockito.Mockito.doNothing;
import static org.mockito.Mockito.doReturn;
import static org.mockito.Mockito.spy;
+import static org.mockito.Mockito.times;
+import static org.mockito.Mockito.verify;
import android.annotation.SuppressLint;
import android.content.Context;
import android.os.Looper;
import android.os.RemoteException;
import android.os.test.TestLooper;
+import android.security.forensic.ForensicEvent;
import android.security.forensic.IForensicServiceCommandCallback;
import android.security.forensic.IForensicServiceStateCallback;
+import android.util.ArrayMap;
+
+import com.android.server.ServiceThread;
import org.junit.Before;
import org.junit.Test;
+import org.mockito.ArgumentCaptor;
import org.mockito.Mock;
import org.mockito.MockitoAnnotations;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Map;
+
public class ForensicServiceTest {
private static final int STATE_UNKNOWN = IForensicServiceStateCallback.State.UNKNOWN;
private static final int STATE_INVISIBLE = IForensicServiceStateCallback.State.INVISIBLE;
@@ -55,10 +67,12 @@ public class ForensicServiceTest {
@Mock
private Context mContext;
private BackupTransportConnection mBackupTransportConnection;
-
+ private DataAggregator mDataAggregator;
private ForensicService mForensicService;
private TestLooper mTestLooper;
private Looper mLooper;
+ private TestLooper mTestLooperOfDataAggregator;
+ private Looper mLooperOfDataAggregator;
@SuppressLint("VisibleForTests")
@Before
@@ -67,6 +81,8 @@ public class ForensicServiceTest {
mTestLooper = new TestLooper();
mLooper = mTestLooper.getLooper();
+ mTestLooperOfDataAggregator = new TestLooper();
+ mLooperOfDataAggregator = mTestLooperOfDataAggregator.getLooper();
mForensicService = new ForensicService(new MockInjector(mContext));
mForensicService.onStart();
}
@@ -121,6 +137,8 @@ public class ForensicServiceTest {
assertEquals(STATE_INVISIBLE, scb1.mState);
assertEquals(STATE_INVISIBLE, scb2.mState);
+ doReturn(true).when(mDataAggregator).initialize();
+
CommandCallback ccb = new CommandCallback();
mForensicService.getBinderService().makeVisible(ccb);
mTestLooper.dispatchAll();
@@ -130,6 +148,29 @@ public class ForensicServiceTest {
}
@Test
+ public void testMakeVisible_FromInvisible_TwoMonitors_DataSourceUnavailable()
+ throws RemoteException {
+ mForensicService.setState(STATE_INVISIBLE);
+ StateCallback scb1 = new StateCallback();
+ StateCallback scb2 = new StateCallback();
+ mForensicService.getBinderService().monitorState(scb1);
+ mForensicService.getBinderService().monitorState(scb2);
+ mTestLooper.dispatchAll();
+ assertEquals(STATE_INVISIBLE, scb1.mState);
+ assertEquals(STATE_INVISIBLE, scb2.mState);
+
+ doReturn(false).when(mDataAggregator).initialize();
+
+ CommandCallback ccb = new CommandCallback();
+ mForensicService.getBinderService().makeVisible(ccb);
+ mTestLooper.dispatchAll();
+ assertEquals(STATE_INVISIBLE, scb1.mState);
+ assertEquals(STATE_INVISIBLE, scb2.mState);
+ assertNotNull(ccb.mErrorCode);
+ assertEquals(ERROR_DATA_SOURCE_UNAVAILABLE, ccb.mErrorCode.intValue());
+ }
+
+ @Test
public void testMakeVisible_FromVisible_TwoMonitors() throws RemoteException {
mForensicService.setState(STATE_VISIBLE);
StateCallback scb1 = new StateCallback();
@@ -262,6 +303,8 @@ public class ForensicServiceTest {
CommandCallback ccb = new CommandCallback();
mForensicService.getBinderService().enable(ccb);
mTestLooper.dispatchAll();
+
+ verify(mDataAggregator, times(1)).enable();
assertEquals(STATE_ENABLED, scb1.mState);
assertEquals(STATE_ENABLED, scb2.mState);
assertNull(ccb.mErrorCode);
@@ -361,14 +404,67 @@ public class ForensicServiceTest {
doNothing().when(mBackupTransportConnection).release();
+ ServiceThread mockThread = spy(ServiceThread.class);
+ mDataAggregator.setHandler(mLooperOfDataAggregator, mockThread);
+
CommandCallback ccb = new CommandCallback();
mForensicService.getBinderService().disable(ccb);
mTestLooper.dispatchAll();
+ mTestLooperOfDataAggregator.dispatchAll();
+ // TODO: We can verify the data sources once we implement them.
+ verify(mockThread, times(1)).quitSafely();
assertEquals(STATE_VISIBLE, scb1.mState);
assertEquals(STATE_VISIBLE, scb2.mState);
assertNull(ccb.mErrorCode);
}
+ @Test
+ public void testDataAggregator_AddBatchData() {
+ mForensicService.setState(STATE_ENABLED);
+ ServiceThread mockThread = spy(ServiceThread.class);
+ mDataAggregator.setHandler(mLooperOfDataAggregator, mockThread);
+
+ String eventOneType = "event_one_type";
+ String eventOneMapKey = "event_one_map_key";
+ String eventOneMapVal = "event_one_map_val";
+ Map<String, String> eventOneMap = new ArrayMap<String, String>();
+ eventOneMap.put(eventOneMapKey, eventOneMapVal);
+ ForensicEvent eventOne = new ForensicEvent(eventOneType, eventOneMap);
+
+ String eventTwoType = "event_two_type";
+ String eventTwoMapKey = "event_two_map_key";
+ String eventTwoMapVal = "event_two_map_val";
+ Map<String, String> eventTwoMap = new ArrayMap<String, String>();
+ eventTwoMap.put(eventTwoMapKey, eventTwoMapVal);
+ ForensicEvent eventTwo = new ForensicEvent(eventTwoType, eventTwoMap);
+
+ List<ForensicEvent> events = new ArrayList<>();
+ events.add(eventOne);
+ events.add(eventTwo);
+
+ doReturn(true).when(mBackupTransportConnection).addData(any());
+
+ mDataAggregator.addBatchData(events);
+ mTestLooperOfDataAggregator.dispatchAll();
+ mTestLooper.dispatchAll();
+
+ ArgumentCaptor<List<ForensicEvent>> captor = ArgumentCaptor.forClass(List.class);
+ verify(mBackupTransportConnection).addData(captor.capture());
+ List<ForensicEvent> receivedEvents = captor.getValue();
+ assertEquals(receivedEvents.size(), 2);
+
+ assertEquals(receivedEvents.getFirst().getType(), eventOneType);
+ assertEquals(receivedEvents.getFirst().getKeyValuePairs().size(), 1);
+ assertEquals(receivedEvents.getFirst().getKeyValuePairs().get(eventOneMapKey),
+ eventOneMapVal);
+
+ assertEquals(receivedEvents.getLast().getType(), eventTwoType);
+ assertEquals(receivedEvents.getLast().getKeyValuePairs().size(), 1);
+ assertEquals(receivedEvents.getLast().getKeyValuePairs().get(eventTwoMapKey),
+ eventTwoMapVal);
+
+ }
+
private class MockInjector implements ForensicService.Injector {
private final Context mContext;
@@ -393,6 +489,11 @@ public class ForensicServiceTest {
return mBackupTransportConnection;
}
+ @Override
+ public DataAggregator getDataAggregator(ForensicService forensicService) {
+ mDataAggregator = spy(new DataAggregator(forensicService));
+ return mDataAggregator;
+ }
}
private static class StateCallback extends IForensicServiceStateCallback.Stub {
diff --git a/services/tests/servicestests/src/com/android/server/hdmi/HdmiCecLocalDevicePlaybackTest.java b/services/tests/servicestests/src/com/android/server/hdmi/HdmiCecLocalDevicePlaybackTest.java
index a0005d968e31..3360e1d08bda 100644
--- a/services/tests/servicestests/src/com/android/server/hdmi/HdmiCecLocalDevicePlaybackTest.java
+++ b/services/tests/servicestests/src/com/android/server/hdmi/HdmiCecLocalDevicePlaybackTest.java
@@ -413,7 +413,8 @@ public class HdmiCecLocalDevicePlaybackTest {
.isEqualTo(Constants.HANDLED);
assertThat(mHdmiCecLocalDevicePlayback.isActiveSource()).isFalse();
// After 30s of device inactivity, device would go to sleep.
- skipActiveSourceLostUi(STANDBY_AFTER_ACTIVE_SOURCE_LOST_DELAY_MS);
+ skipActiveSourceLostUi(STANDBY_AFTER_ACTIVE_SOURCE_LOST_DELAY_MS, false,
+ false);
assertThat(mPowerManager.isInteractive()).isFalse();
}
@@ -422,9 +423,8 @@ public class HdmiCecLocalDevicePlaybackTest {
int newPlaybackPhysicalAddress = 0x2100;
int switchPhysicalAddress = 0x2000;
mNativeWrapper.setPhysicalAddress(newPlaybackPhysicalAddress);
- mHdmiControlService.onHotplug(newPlaybackPhysicalAddress, true);
mHdmiControlService.allocateLogicalAddress(mLocalDevices, INITIATED_BY_ENABLE_CEC);
-
+ mHdmiControlService.onHotplug(newPlaybackPhysicalAddress, true);
mHdmiCecLocalDevicePlayback.mService.getHdmiCecConfig().setStringValue(
HdmiControlManager.CEC_SETTING_NAME_POWER_STATE_CHANGE_ON_ACTIVE_SOURCE_LOST,
HdmiControlManager.POWER_STATE_CHANGE_ON_ACTIVE_SOURCE_LOST_STANDBY_NOW);
@@ -457,7 +457,8 @@ public class HdmiCecLocalDevicePlaybackTest {
.isEqualTo(Constants.HANDLED);
assertThat(mHdmiCecLocalDevicePlayback.isActiveSource()).isFalse();
// After 30s of device inactivity, device would go to sleep.
- skipActiveSourceLostUi(STANDBY_AFTER_ACTIVE_SOURCE_LOST_DELAY_MS);
+ skipActiveSourceLostUi(STANDBY_AFTER_ACTIVE_SOURCE_LOST_DELAY_MS, false,
+ false);
assertThat(mPowerManager.isInteractive()).isFalse();
}
@@ -617,7 +618,8 @@ public class HdmiCecLocalDevicePlaybackTest {
.isEqualTo(Constants.HANDLED);
assertThat(mHdmiCecLocalDevicePlayback.isActiveSource()).isFalse();
// After 30s of device inactivity, device would go to sleep.
- skipActiveSourceLostUi(STANDBY_AFTER_ACTIVE_SOURCE_LOST_DELAY_MS);
+ skipActiveSourceLostUi(STANDBY_AFTER_ACTIVE_SOURCE_LOST_DELAY_MS, false,
+ false);
assertThat(mPowerManager.isInteractive()).isFalse();
}
@@ -722,7 +724,8 @@ public class HdmiCecLocalDevicePlaybackTest {
assertThat(mHdmiCecLocalDevicePlayback.getActiveSource().logicalAddress).isEqualTo(
ADDR_INVALID);
// After 30s of device inactivity, device would go to sleep.
- skipActiveSourceLostUi(STANDBY_AFTER_ACTIVE_SOURCE_LOST_DELAY_MS);
+ skipActiveSourceLostUi(STANDBY_AFTER_ACTIVE_SOURCE_LOST_DELAY_MS,true,
+ true);
assertThat(mPowerManager.isInteractive()).isFalse();
}
@@ -1265,14 +1268,35 @@ public class HdmiCecLocalDevicePlaybackTest {
assertThat(mHdmiCecLocalDevicePlayback.handleActiveSource(message))
.isEqualTo(Constants.HANDLED);
mTestLooper.dispatchAll();
-
- // After 30s of device inactivity, device would go to sleep.
- skipActiveSourceLostUi(STANDBY_AFTER_ACTIVE_SOURCE_LOST_DELAY_MS);
+ // After 30s of device inactivity, device would assert active source.
+ skipActiveSourceLostUi(STANDBY_AFTER_ACTIVE_SOURCE_LOST_DELAY_MS, true,
+ true);
assertThat(mPowerManager.isInteractive()).isFalse();
assertThat(mHdmiCecLocalDevicePlayback.isActiveSource()).isFalse();
}
@Test
+ public void handleActiveSourceFromTv_tvNotAnswerRequest_assertActiveSource() {
+ mHdmiCecLocalDevicePlayback.mService.getHdmiCecConfig().setStringValue(
+ HdmiControlManager.CEC_SETTING_NAME_POWER_STATE_CHANGE_ON_ACTIVE_SOURCE_LOST,
+ HdmiControlManager.POWER_STATE_CHANGE_ON_ACTIVE_SOURCE_LOST_STANDBY_NOW);
+ mPowerManager.setInteractive(true);
+ HdmiCecMessage message = HdmiCecMessageBuilder.buildActiveSource(mPlaybackLogicalAddress,
+ mPlaybackPhysicalAddress);
+ assertThat(mHdmiCecLocalDevicePlayback.handleActiveSource(message))
+ .isEqualTo(Constants.HANDLED);
+ message = HdmiCecMessageBuilder.buildActiveSource(ADDR_TV, 0x0000);
+ assertThat(mHdmiCecLocalDevicePlayback.handleActiveSource(message))
+ .isEqualTo(Constants.HANDLED);
+ mTestLooper.dispatchAll();
+ // After 30s of device inactivity, device would assert active source.
+ skipActiveSourceLostUi(STANDBY_AFTER_ACTIVE_SOURCE_LOST_DELAY_MS, true,
+ false);
+ assertThat(mPowerManager.isInteractive()).isTrue();
+ assertThat(mHdmiCecLocalDevicePlayback.isActiveSource()).isTrue();
+ }
+
+ @Test
public void handleActiveSource_otherDevice_ActiveSource_mediaSessionsPaused() {
mHdmiCecLocalDevicePlayback.setActiveSource(mPlaybackLogicalAddress,
mPlaybackPhysicalAddress, "HdmiCecLocalDevicePlaybackTest");
@@ -1343,7 +1367,8 @@ public class HdmiCecLocalDevicePlaybackTest {
.isEqualTo(Constants.HANDLED);
assertThat(mHdmiCecLocalDevicePlayback.isActiveSource()).isFalse();
// After 30s of device inactivity, device would go to sleep.
- skipActiveSourceLostUi(STANDBY_AFTER_ACTIVE_SOURCE_LOST_DELAY_MS);
+ skipActiveSourceLostUi(STANDBY_AFTER_ACTIVE_SOURCE_LOST_DELAY_MS, true,
+ true);
assertThat(mPowerManager.isInteractive()).isFalse();
mHdmiControlService.onStandby(HdmiControlService.STANDBY_SCREEN_OFF);
// 3. DUT becomes <AS> again.
@@ -1704,9 +1729,9 @@ public class HdmiCecLocalDevicePlaybackTest {
assertThat(mHdmiCecLocalDevicePlayback.handleSetStreamPath(message))
.isEqualTo(Constants.HANDLED);
assertThat(mHdmiCecLocalDevicePlayback.isActiveSource()).isFalse();
-
// After 30s of device inactivity, device would go to sleep.
- skipActiveSourceLostUi(STANDBY_AFTER_ACTIVE_SOURCE_LOST_DELAY_MS);
+ skipActiveSourceLostUi(STANDBY_AFTER_ACTIVE_SOURCE_LOST_DELAY_MS, false,
+ false);
assertThat(mPowerManager.isInteractive()).isFalse();
}
@@ -2323,11 +2348,197 @@ public class HdmiCecLocalDevicePlaybackTest {
mTestLooper.dispatchAll();
// After 30s of device inactivity, device would go to sleep.
- skipActiveSourceLostUi(STANDBY_AFTER_ACTIVE_SOURCE_LOST_DELAY_MS);
+ skipActiveSourceLostUi(STANDBY_AFTER_ACTIVE_SOURCE_LOST_DELAY_MS, true,
+ true);
assertThat(mPowerManager.isInteractive()).isFalse();
}
@Test
+ public void onActiveSourceLostToTv_requestActiveSourceAnsweredFromTv_showPopup() {
+ mHdmiCecLocalDevicePlayback.mService.getHdmiCecConfig().setStringValue(
+ HdmiControlManager.CEC_SETTING_NAME_POWER_STATE_CHANGE_ON_ACTIVE_SOURCE_LOST,
+ HdmiControlManager.POWER_STATE_CHANGE_ON_ACTIVE_SOURCE_LOST_STANDBY_NOW);
+
+ mHdmiCecLocalDevicePlayback.setActiveSource(mPlaybackLogicalAddress,
+ mPlaybackPhysicalAddress, "HdmiCecLocalDevicePlaybackTest");
+ mPowerManager.setInteractive(true);
+ mNativeWrapper.clearResultMessages();
+ mTestLooper.dispatchAll();
+
+ HdmiCecMessage activeSourceFromTv =
+ HdmiCecMessageBuilder.buildActiveSource(ADDR_TV, 0x0000);
+ HdmiCecMessage requestActiveSource =
+ HdmiCecMessageBuilder.buildRequestActiveSource(mPlaybackLogicalAddress);
+
+ assertThat(mHdmiCecLocalDevicePlayback.handleActiveSource(activeSourceFromTv))
+ .isEqualTo(Constants.HANDLED);
+ mTestLooper.dispatchAll();
+
+ mTestLooper.moveTimeForward(POPUP_AFTER_ACTIVE_SOURCE_LOST_DELAY_MS);
+ mTestLooper.dispatchAll();
+
+ // RequestActiveSourceAction is triggered.
+ assertThat(mNativeWrapper.getResultMessages()).contains(requestActiveSource);
+ mTestLooper.moveTimeForward(TIMEOUT_MS);
+ mTestLooper.dispatchAll();
+
+ assertThat(mNativeWrapper.getResultMessages()).contains(requestActiveSource);
+ mNativeWrapper.onCecMessage(activeSourceFromTv);
+ mTestLooper.moveTimeForward(TIMEOUT_MS);
+ mTestLooper.dispatchAll();
+
+ assertThat(mHdmiCecLocalDevicePlayback.getActiveSource().logicalAddress).isEqualTo(ADDR_TV);
+ assertThat(mIsOnActiveSourceLostPopupActive).isTrue();
+ }
+
+ @Test
+ public void onActiveSourceLostToTv_requestActiveSourceNotAnswered_assertActiveSource() {
+ mHdmiCecLocalDevicePlayback.mService.getHdmiCecConfig().setStringValue(
+ HdmiControlManager.CEC_SETTING_NAME_POWER_STATE_CHANGE_ON_ACTIVE_SOURCE_LOST,
+ HdmiControlManager.POWER_STATE_CHANGE_ON_ACTIVE_SOURCE_LOST_STANDBY_NOW);
+
+ mHdmiCecLocalDevicePlayback.setActiveSource(mPlaybackLogicalAddress,
+ mPlaybackPhysicalAddress, "HdmiCecLocalDevicePlaybackTest");
+ mPowerManager.setInteractive(true);
+ mNativeWrapper.clearResultMessages();
+ mTestLooper.dispatchAll();
+
+ HdmiCecMessage activeSourceFromTv =
+ HdmiCecMessageBuilder.buildActiveSource(ADDR_TV, 0x0000);
+ HdmiCecMessage requestActiveSource =
+ HdmiCecMessageBuilder.buildRequestActiveSource(mPlaybackLogicalAddress);
+ HdmiCecMessage activeSourceFromPlayback =
+ HdmiCecMessageBuilder.buildActiveSource(mPlaybackLogicalAddress,
+ mPlaybackPhysicalAddress);
+ assertThat(mHdmiCecLocalDevicePlayback.handleActiveSource(activeSourceFromTv))
+ .isEqualTo(Constants.HANDLED);
+ assertThat(mHdmiCecLocalDevicePlayback.getActiveSource().logicalAddress).isEqualTo(ADDR_TV);
+ mTestLooper.dispatchAll();
+
+ mTestLooper.moveTimeForward(POPUP_AFTER_ACTIVE_SOURCE_LOST_DELAY_MS);
+ mTestLooper.dispatchAll();
+
+ // RequestActiveSourceAction is triggered.
+ assertThat(mNativeWrapper.getResultMessages()).contains(requestActiveSource);
+ mTestLooper.moveTimeForward(TIMEOUT_MS);
+ mTestLooper.dispatchAll();
+
+ assertThat(mNativeWrapper.getResultMessages()).contains(requestActiveSource);
+ mTestLooper.moveTimeForward(TIMEOUT_MS);
+ mTestLooper.dispatchAll();
+
+ // Pop-up is not shown, playback device asserts active source since TV doesn't answer the
+ // request.
+ assertThat(mIsOnActiveSourceLostPopupActive).isFalse();
+ assertThat(mNativeWrapper.getResultMessages().contains(activeSourceFromPlayback)).isTrue();
+ assertThat(mHdmiCecLocalDevicePlayback.getActiveSource().logicalAddress)
+ .isEqualTo(mPlaybackLogicalAddress);
+ assertThat(mHdmiCecLocalDevicePlayback.getActiveSource().physicalAddress)
+ .isEqualTo(mPlaybackPhysicalAddress);
+ }
+
+ @Test
+ public void onActiveSourceLost_requestActiveSourceNotAnswered_playbackIsAS_dontShowPopup() {
+ mHdmiCecLocalDevicePlayback.mService.getHdmiCecConfig().setStringValue(
+ HdmiControlManager.CEC_SETTING_NAME_POWER_STATE_CHANGE_ON_ACTIVE_SOURCE_LOST,
+ HdmiControlManager.POWER_STATE_CHANGE_ON_ACTIVE_SOURCE_LOST_STANDBY_NOW);
+
+ mHdmiCecLocalDevicePlayback.setActiveSource(mPlaybackLogicalAddress,
+ mPlaybackPhysicalAddress, "HdmiCecLocalDevicePlaybackTest");
+ mPowerManager.setInteractive(true);
+ mNativeWrapper.clearResultMessages();
+ mTestLooper.dispatchAll();
+
+ HdmiCecMessage activeSourceFromTv =
+ HdmiCecMessageBuilder.buildActiveSource(ADDR_TV, 0x0000);
+ HdmiCecMessage requestActiveSource =
+ HdmiCecMessageBuilder.buildRequestActiveSource(mPlaybackLogicalAddress);
+ HdmiCecMessage setStreamPathToPlayback = HdmiCecMessageBuilder.buildSetStreamPath(ADDR_TV,
+ mPlaybackPhysicalAddress);
+ HdmiCecMessage activeSourceFromPlayback =
+ HdmiCecMessageBuilder.buildActiveSource(mPlaybackLogicalAddress,
+ mPlaybackPhysicalAddress);
+ assertThat(mHdmiCecLocalDevicePlayback.handleActiveSource(activeSourceFromTv))
+ .isEqualTo(Constants.HANDLED);
+ assertThat(mHdmiCecLocalDevicePlayback.getActiveSource().logicalAddress).isEqualTo(ADDR_TV);
+ mTestLooper.dispatchAll();
+
+ mTestLooper.moveTimeForward(POPUP_AFTER_ACTIVE_SOURCE_LOST_DELAY_MS);
+ mTestLooper.dispatchAll();
+
+ // RequestActiveSourceAction is triggered.
+ assertThat(mNativeWrapper.getResultMessages()).contains(requestActiveSource);
+ mTestLooper.moveTimeForward(TIMEOUT_MS);
+ mTestLooper.dispatchAll();
+
+ assertThat(mHdmiCecLocalDevicePlayback.handleSetStreamPath(setStreamPathToPlayback))
+ .isEqualTo(Constants.HANDLED);
+ mTestLooper.dispatchAll();
+
+ mTestLooper.moveTimeForward(TIMEOUT_MS);
+ mTestLooper.dispatchAll();
+
+ // Pop-up is not shown since playback device is active source.
+ assertThat(mIsOnActiveSourceLostPopupActive).isFalse();
+ assertThat(mNativeWrapper.getResultMessages().contains(activeSourceFromPlayback)).isTrue();
+ assertThat(mHdmiCecLocalDevicePlayback.getActiveSource().logicalAddress)
+ .isEqualTo(mPlaybackLogicalAddress);
+ assertThat(mHdmiCecLocalDevicePlayback.getActiveSource().physicalAddress)
+ .isEqualTo(mPlaybackPhysicalAddress);
+ }
+
+ @Test
+ public void onActiveSourceLost_requestASNotAnswered_setStreamPathToNonCecInput_dontShowPopup() {
+ int otherPhysicalAddress = mPlaybackPhysicalAddress + 0x0100;
+ mHdmiCecLocalDevicePlayback.mService.getHdmiCecConfig().setStringValue(
+ HdmiControlManager.CEC_SETTING_NAME_POWER_STATE_CHANGE_ON_ACTIVE_SOURCE_LOST,
+ HdmiControlManager.POWER_STATE_CHANGE_ON_ACTIVE_SOURCE_LOST_STANDBY_NOW);
+
+ mHdmiCecLocalDevicePlayback.setActiveSource(mPlaybackLogicalAddress,
+ mPlaybackPhysicalAddress, "HdmiCecLocalDevicePlaybackTest");
+ mPowerManager.setInteractive(true);
+ mNativeWrapper.clearResultMessages();
+ mTestLooper.dispatchAll();
+
+ HdmiCecMessage activeSourceFromTv =
+ HdmiCecMessageBuilder.buildActiveSource(ADDR_TV, 0x0000);
+ HdmiCecMessage requestActiveSource =
+ HdmiCecMessageBuilder.buildRequestActiveSource(mPlaybackLogicalAddress);
+ HdmiCecMessage setStreamPathToOtherInput = HdmiCecMessageBuilder.buildSetStreamPath(ADDR_TV,
+ otherPhysicalAddress);
+ HdmiCecMessage activeSourceFromPlayback =
+ HdmiCecMessageBuilder.buildActiveSource(mPlaybackLogicalAddress,
+ mPlaybackPhysicalAddress);
+ assertThat(mHdmiCecLocalDevicePlayback.handleActiveSource(activeSourceFromTv))
+ .isEqualTo(Constants.HANDLED);
+ assertThat(mHdmiCecLocalDevicePlayback.getActiveSource().logicalAddress).isEqualTo(ADDR_TV);
+ mTestLooper.dispatchAll();
+
+ mTestLooper.moveTimeForward(POPUP_AFTER_ACTIVE_SOURCE_LOST_DELAY_MS);
+ mTestLooper.dispatchAll();
+
+ // RequestActiveSourceAction is triggered.
+ assertThat(mNativeWrapper.getResultMessages()).contains(requestActiveSource);
+ mTestLooper.moveTimeForward(TIMEOUT_MS);
+ mTestLooper.dispatchAll();
+
+ assertThat(mHdmiCecLocalDevicePlayback.handleSetStreamPath(setStreamPathToOtherInput))
+ .isEqualTo(Constants.HANDLED);
+ mTestLooper.dispatchAll();
+
+ mTestLooper.moveTimeForward(TIMEOUT_MS);
+ mTestLooper.dispatchAll();
+
+ // Pop-up is shown, playback device doesn't assert active source since active path is
+ // switched to a non-CEC device.
+ assertThat(mIsOnActiveSourceLostPopupActive).isTrue();
+ assertThat(mHdmiCecLocalDevicePlayback.getActiveSource().logicalAddress)
+ .isEqualTo(ADDR_INVALID);
+ assertThat(mHdmiCecLocalDevicePlayback.getActiveSource().physicalAddress)
+ .isEqualTo(otherPhysicalAddress);
+ }
+
+ @Test
public void onActiveSourceLost_interactionWithDut_noStandbyAfterTimeout() {
mHdmiCecLocalDevicePlayback.mService.getHdmiCecConfig().setStringValue(
HdmiControlManager.CEC_SETTING_NAME_POWER_STATE_CHANGE_ON_ACTIVE_SOURCE_LOST,
@@ -2350,7 +2561,7 @@ public class HdmiCecLocalDevicePlaybackTest {
mTestLooper.dispatchAll();
// User interacted with the DUT, so the device will not go to standby.
- skipActiveSourceLostUi(0);
+ skipActiveSourceLostUi(0, true, true);
assertThat(mIsOnActiveSourceLostPopupActive).isFalse();
assertThat(mPowerManager.isInteractive()).isTrue();
assertThat(mNativeWrapper.getResultMessages().contains(activeSourceFromPlayback)).isTrue();
@@ -2387,6 +2598,10 @@ public class HdmiCecLocalDevicePlaybackTest {
// Pop-up is triggered.
mTestLooper.moveTimeForward(POPUP_AFTER_ACTIVE_SOURCE_LOST_DELAY_MS);
mTestLooper.dispatchAll();
+ // RequestActiveSourceAction is triggered and TV confirms active source.
+ mNativeWrapper.onCecMessage(activeSourceFromTv);
+ mTestLooper.dispatchAll();
+
assertThat(mIsOnActiveSourceLostPopupActive).isTrue();
assertThat(mHdmiCecLocalDevicePlayback.handleSetStreamPath(setStreamPathToPlayback))
@@ -2407,6 +2622,8 @@ public class HdmiCecLocalDevicePlaybackTest {
@Test
public void onActiveSourceLost_incomingRoutingChangeToDut_noStandbyAfterTimeout() {
+ int otherPlaybackLogicalAddress = mPlaybackLogicalAddress == Constants.ADDR_PLAYBACK_2
+ ? Constants.ADDR_PLAYBACK_1 : Constants.ADDR_PLAYBACK_2;
mHdmiCecLocalDevicePlayback.mService.getHdmiCecConfig().setStringValue(
HdmiControlManager.CEC_SETTING_NAME_POWER_STATE_CHANGE_ON_ACTIVE_SOURCE_LOST,
HdmiControlManager.POWER_STATE_CHANGE_ON_ACTIVE_SOURCE_LOST_STANDBY_NOW);
@@ -2420,18 +2637,21 @@ public class HdmiCecLocalDevicePlaybackTest {
mNativeWrapper.clearResultMessages();
mTestLooper.dispatchAll();
- HdmiCecMessage activeSourceFromTv =
- HdmiCecMessageBuilder.buildActiveSource(ADDR_TV, 0x0000);
+ HdmiCecMessage activeSourceFromOtherPlayback =
+ HdmiCecMessageBuilder.buildActiveSource(otherPlaybackLogicalAddress,
+ mPlaybackPhysicalAddress + 0x0100);
HdmiCecMessage activeSourceFromPlayback =
HdmiCecMessageBuilder.buildActiveSource(mPlaybackLogicalAddress,
mPlaybackPhysicalAddress);
HdmiCecMessage routingChangeToPlayback =
- HdmiCecMessageBuilder.buildRoutingChange(ADDR_TV, 0x0000,
+ HdmiCecMessageBuilder.buildRoutingChange(ADDR_TV,
+ mPlaybackPhysicalAddress + 0x0100,
mPlaybackPhysicalAddress);
- assertThat(mHdmiCecLocalDevicePlayback.handleActiveSource(activeSourceFromTv))
+ assertThat(mHdmiCecLocalDevicePlayback.handleActiveSource(activeSourceFromOtherPlayback))
.isEqualTo(Constants.HANDLED);
- assertThat(mHdmiCecLocalDevicePlayback.getActiveSource().logicalAddress).isEqualTo(ADDR_TV);
+ assertThat(mHdmiCecLocalDevicePlayback.getActiveSource().logicalAddress).isEqualTo(
+ otherPlaybackLogicalAddress);
mTestLooper.dispatchAll();
// Pop-up is triggered.
@@ -2600,13 +2820,30 @@ public class HdmiCecLocalDevicePlaybackTest {
assertThat(mPowerManager.isInteractive()).isFalse();
}
- private void skipActiveSourceLostUi(long idleDuration) {
+ private void skipActiveSourceLostUi(long idleDuration, boolean activeSourceLostToTv,
+ boolean tvAnswersRequest) {
mTestLooper.moveTimeForward(POPUP_AFTER_ACTIVE_SOURCE_LOST_DELAY_MS);
mTestLooper.dispatchAll();
+ if (activeSourceLostToTv) {
+ // RequestActiveSourceAction is triggered.
+ mTestLooper.moveTimeForward(TIMEOUT_MS);
+ mTestLooper.dispatchAll();
+ if (tvAnswersRequest) {
+ HdmiCecMessage activeSource = HdmiCecMessageBuilder.buildActiveSource(ADDR_TV,
+ 0x0000);
+ mNativeWrapper.onCecMessage(activeSource);
+ mTestLooper.dispatchAll();
+ } else {
+ mTestLooper.moveTimeForward(TIMEOUT_MS);
+ mTestLooper.dispatchAll();
+ assertThat(mIsOnActiveSourceLostPopupActive).isFalse();
+ return;
+ }
+ }
assertThat(mIsOnActiveSourceLostPopupActive).isTrue();
mPowerManagerInternal.setIdleDuration(idleDuration);
mTestLooper.moveTimeForward(STANDBY_AFTER_ACTIVE_SOURCE_LOST_DELAY_MS);
mTestLooper.dispatchAll();
}
-}
+} \ No newline at end of file
diff --git a/services/tests/servicestests/src/com/android/server/hdmi/HdmiCecLocalDeviceTvTest.java b/services/tests/servicestests/src/com/android/server/hdmi/HdmiCecLocalDeviceTvTest.java
index 935c8b8720fb..51276a4db883 100644
--- a/services/tests/servicestests/src/com/android/server/hdmi/HdmiCecLocalDeviceTvTest.java
+++ b/services/tests/servicestests/src/com/android/server/hdmi/HdmiCecLocalDeviceTvTest.java
@@ -2234,6 +2234,25 @@ public class HdmiCecLocalDeviceTvTest {
}
@Test
+ public void handleReportPhysicalAddress_samePathAsActiveSource_differentLA_newActiveSource() {
+ // This scenario can be reproduced if active source is hotplugged out and replaced with
+ // another device that might have another LA.
+ int physicalAddress = 0x1000;
+ mHdmiControlService.setActiveSource(Constants.ADDR_PLAYBACK_1, physicalAddress,
+ "HdmiControlServiceTest");
+ mHdmiCecLocalDeviceTv.setActivePath(physicalAddress);
+ HdmiCecMessage reportPhysicalAddressFromPlayback2 =
+ HdmiCecMessageBuilder.buildReportPhysicalAddressCommand(ADDR_PLAYBACK_2,
+ physicalAddress, HdmiDeviceInfo.DEVICE_PLAYBACK);
+ HdmiCecMessage setStreamPath = HdmiCecMessageBuilder.buildSetStreamPath(ADDR_TV,
+ physicalAddress);
+ mNativeWrapper.onCecMessage(reportPhysicalAddressFromPlayback2);
+ mTestLooper.dispatchAll();
+
+ assertThat(mNativeWrapper.getResultMessages()).contains(setStreamPath);
+ }
+
+ @Test
public void onOneTouchPlay_wakeUp_addCecDevice() {
assertThat(mHdmiControlService.getHdmiCecNetwork().getDeviceInfoList(false))
.isEmpty();
diff --git a/services/tests/servicestests/src/com/android/server/integrity/parser/RuleIndexingControllerTest.java b/services/tests/servicestests/src/com/android/server/integrity/parser/RuleIndexingControllerTest.java
deleted file mode 100644
index 370bd80003bd..000000000000
--- a/services/tests/servicestests/src/com/android/server/integrity/parser/RuleIndexingControllerTest.java
+++ /dev/null
@@ -1,216 +0,0 @@
-/*
- * Copyright (C) 2020 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.server.integrity.parser;
-
-import static com.android.server.integrity.model.ComponentBitSize.VALUE_SIZE_BITS;
-import static com.android.server.integrity.model.IndexingFileConstants.END_INDEXING_KEY;
-import static com.android.server.integrity.model.IndexingFileConstants.START_INDEXING_KEY;
-import static com.android.server.integrity.utils.TestUtils.getBits;
-import static com.android.server.integrity.utils.TestUtils.getBytes;
-import static com.android.server.integrity.utils.TestUtils.getValueBits;
-
-import static com.google.common.truth.Truth.assertThat;
-
-import static org.testng.Assert.assertThrows;
-
-import android.content.integrity.AppInstallMetadata;
-
-import org.junit.Test;
-import org.junit.runner.RunWith;
-import org.junit.runners.JUnit4;
-
-import java.io.ByteArrayInputStream;
-import java.io.IOException;
-import java.io.InputStream;
-import java.nio.ByteBuffer;
-import java.util.Arrays;
-import java.util.Collections;
-import java.util.List;
-
-@RunWith(JUnit4.class)
-public class RuleIndexingControllerTest {
-
- @Test
- public void verifyIndexRangeSearchIsCorrect() throws IOException {
- InputStream inputStream = obtainDefaultIndexingMapForTest();
-
- RuleIndexingController indexingController = new RuleIndexingController(inputStream);
-
- AppInstallMetadata appInstallMetadata =
- new AppInstallMetadata.Builder()
- .setPackageName("ddd")
- .setAppCertificates(Collections.singletonList("777"))
- .setAppCertificateLineage(Collections.singletonList("777"))
- .build();
-
- List<RuleIndexRange> resultingIndexes =
- indexingController.identifyRulesToEvaluate(appInstallMetadata);
-
- assertThat(resultingIndexes)
- .containsExactly(
- new RuleIndexRange(200, 300),
- new RuleIndexRange(700, 800),
- new RuleIndexRange(900, 945));
- }
-
- @Test
- public void verifyIndexRangeSearchIsCorrect_multipleAppCertificates() throws IOException {
- InputStream inputStream = obtainDefaultIndexingMapForTest();
-
- RuleIndexingController indexingController = new RuleIndexingController(inputStream);
-
- AppInstallMetadata appInstallMetadata =
- new AppInstallMetadata.Builder()
- .setPackageName("ddd")
- .setAppCertificates(Arrays.asList("777", "999"))
- .setAppCertificateLineage(Arrays.asList("777", "999"))
- .build();
-
- List<RuleIndexRange> resultingIndexes =
- indexingController.identifyRulesToEvaluate(appInstallMetadata);
-
- assertThat(resultingIndexes)
- .containsExactly(
- new RuleIndexRange(200, 300),
- new RuleIndexRange(700, 800),
- new RuleIndexRange(800, 900),
- new RuleIndexRange(900, 945));
- }
-
- @Test
- public void verifyIndexRangeSearchIsCorrect_keysInFirstAndLastBlock() throws IOException {
- InputStream inputStream = obtainDefaultIndexingMapForTest();
-
- RuleIndexingController indexingController = new RuleIndexingController(inputStream);
-
- AppInstallMetadata appInstallMetadata =
- new AppInstallMetadata.Builder()
- .setPackageName("bbb")
- .setAppCertificates(Collections.singletonList("999"))
- .setAppCertificateLineage(Collections.singletonList("999"))
- .build();
-
- List<RuleIndexRange> resultingIndexes =
- indexingController.identifyRulesToEvaluate(appInstallMetadata);
-
- assertThat(resultingIndexes)
- .containsExactly(
- new RuleIndexRange(100, 200),
- new RuleIndexRange(800, 900),
- new RuleIndexRange(900, 945));
- }
-
- @Test
- public void verifyIndexRangeSearchIsCorrect_keysMatchWithValues() throws IOException {
- InputStream inputStream = obtainDefaultIndexingMapForTest();
-
- RuleIndexingController indexingController = new RuleIndexingController(inputStream);
-
- AppInstallMetadata appInstallMetadata =
- new AppInstallMetadata.Builder()
- .setPackageName("ccc")
- .setAppCertificates(Collections.singletonList("444"))
- .setAppCertificateLineage(Collections.singletonList("444"))
- .build();
-
- List<RuleIndexRange> resultingIndexes =
- indexingController.identifyRulesToEvaluate(appInstallMetadata);
-
- assertThat(resultingIndexes)
- .containsExactly(
- new RuleIndexRange(200, 300),
- new RuleIndexRange(700, 800),
- new RuleIndexRange(900, 945));
- }
-
- @Test
- public void verifyIndexRangeSearchIsCorrect_noIndexesAvailable() throws IOException {
- byte[] stringBytes =
- getBytes(
- getKeyValueString(START_INDEXING_KEY, 100)
- + getKeyValueString(END_INDEXING_KEY, 500)
- + getKeyValueString(START_INDEXING_KEY, 500)
- + getKeyValueString(END_INDEXING_KEY, 900)
- + getKeyValueString(START_INDEXING_KEY, 900)
- + getKeyValueString(END_INDEXING_KEY, 945));
- ByteBuffer rule = ByteBuffer.allocate(stringBytes.length);
- rule.put(stringBytes);
- InputStream inputStream = new ByteArrayInputStream(rule.array());
-
- RuleIndexingController indexingController = new RuleIndexingController(inputStream);
-
- AppInstallMetadata appInstallMetadata =
- new AppInstallMetadata.Builder()
- .setPackageName("ccc")
- .setAppCertificates(Collections.singletonList("444"))
- .setAppCertificateLineage(Collections.singletonList("444"))
- .build();
-
- List<RuleIndexRange> resultingIndexes =
- indexingController.identifyRulesToEvaluate(appInstallMetadata);
-
- assertThat(resultingIndexes)
- .containsExactly(
- new RuleIndexRange(100, 500),
- new RuleIndexRange(500, 900),
- new RuleIndexRange(900, 945));
- }
-
- @Test
- public void verifyIndexingFileIsCorrupt() throws IOException {
- byte[] stringBytes =
- getBytes(
- getKeyValueString(START_INDEXING_KEY, 100)
- + getKeyValueString("ccc", 200)
- + getKeyValueString(END_INDEXING_KEY, 300)
- + getKeyValueString(END_INDEXING_KEY, 900));
- ByteBuffer rule = ByteBuffer.allocate(stringBytes.length);
- rule.put(stringBytes);
- InputStream inputStream = new ByteArrayInputStream(rule.array());
-
- assertThrows(IllegalStateException.class,
- () -> new RuleIndexingController(inputStream));
- }
-
- private static InputStream obtainDefaultIndexingMapForTest() {
- byte[] stringBytes =
- getBytes(
- getKeyValueString(START_INDEXING_KEY, 100)
- + getKeyValueString("ccc", 200)
- + getKeyValueString("eee", 300)
- + getKeyValueString("hhh", 400)
- + getKeyValueString(END_INDEXING_KEY, 500)
- + getKeyValueString(START_INDEXING_KEY, 500)
- + getKeyValueString("111", 600)
- + getKeyValueString("444", 700)
- + getKeyValueString("888", 800)
- + getKeyValueString(END_INDEXING_KEY, 900)
- + getKeyValueString(START_INDEXING_KEY, 900)
- + getKeyValueString(END_INDEXING_KEY, 945));
- ByteBuffer rule = ByteBuffer.allocate(stringBytes.length);
- rule.put(stringBytes);
- return new ByteArrayInputStream(rule.array());
- }
-
- private static String getKeyValueString(String key, int value) {
- String isNotHashed = "0";
- return isNotHashed
- + getBits(key.length(), VALUE_SIZE_BITS)
- + getValueBits(key)
- + getBits(value, /* numOfBits= */ 32);
- }
-}
diff --git a/services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java b/services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java
index e845d80b412a..dec7f09f2da4 100644
--- a/services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java
+++ b/services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java
@@ -45,6 +45,9 @@ import static android.app.Notification.GROUP_ALERT_CHILDREN;
import static android.app.Notification.VISIBILITY_PRIVATE;
import static android.app.NotificationChannel.NEWS_ID;
import static android.app.NotificationChannel.DEFAULT_CHANNEL_ID;
+import static android.app.NotificationChannel.PROMOTIONS_ID;
+import static android.app.NotificationChannel.RECS_ID;
+import static android.app.NotificationChannel.SOCIAL_MEDIA_ID;
import static android.app.NotificationChannel.USER_LOCKED_ALLOW_BUBBLE;
import static android.app.NotificationManager.ACTION_INTERRUPTION_FILTER_CHANGED;
import static android.app.NotificationManager.BUBBLE_PREFERENCE_ALL;
@@ -98,7 +101,10 @@ import static android.service.notification.Adjustment.KEY_IMPORTANCE;
import static android.service.notification.Adjustment.KEY_TEXT_REPLIES;
import static android.service.notification.Adjustment.KEY_TYPE;
import static android.service.notification.Adjustment.KEY_USER_SENTIMENT;
+import static android.service.notification.Adjustment.TYPE_CONTENT_RECOMMENDATION;
import static android.service.notification.Adjustment.TYPE_NEWS;
+import static android.service.notification.Adjustment.TYPE_PROMOTION;
+import static android.service.notification.Adjustment.TYPE_SOCIAL_MEDIA;
import static android.service.notification.Condition.SOURCE_CONTEXT;
import static android.service.notification.Condition.SOURCE_USER_ACTION;
import static android.service.notification.Condition.STATE_TRUE;
@@ -16767,6 +16773,24 @@ public class NotificationManagerServiceTest extends UiServiceTestCase {
r.applyAdjustments();
assertThat(r.getChannel().getId()).isEqualTo(NEWS_ID);
+
+ signals.putInt(KEY_TYPE, TYPE_PROMOTION);
+ mBinderService.applyAdjustmentFromAssistant(null, adjustment);
+ waitForIdle();
+ r.applyAdjustments();
+ assertThat(r.getChannel().getId()).isEqualTo(PROMOTIONS_ID);
+
+ signals.putInt(KEY_TYPE, TYPE_SOCIAL_MEDIA);
+ mBinderService.applyAdjustmentFromAssistant(null, adjustment);
+ waitForIdle();
+ r.applyAdjustments();
+ assertThat(r.getChannel().getId()).isEqualTo(SOCIAL_MEDIA_ID);
+
+ signals.putInt(KEY_TYPE, TYPE_CONTENT_RECOMMENDATION);
+ mBinderService.applyAdjustmentFromAssistant(null, adjustment);
+ waitForIdle();
+ r.applyAdjustments();
+ assertThat(r.getChannel().getId()).isEqualTo(RECS_ID);
}
@Test
@@ -17066,7 +17090,7 @@ public class NotificationManagerServiceTest extends UiServiceTestCase {
@Test
@EnableFlags(FLAG_NOTIFICATION_CLASSIFICATION)
public void testAppCannotUseReservedBundleChannels() throws Exception {
- mBinderService.getBubblePreferenceForPackage(mPkg, mUid);
+ mService.mPreferencesHelper.createReservedChannel(mPkg, mUid, TYPE_NEWS);
NotificationChannel news = mBinderService.getNotificationChannel(
mPkg, mContext.getUserId(), mPkg, NEWS_ID);
assertThat(news).isNotNull();
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 36fa1b82fe69..1a1da0f6d408 100644
--- a/services/tests/uiservicestests/src/com/android/server/notification/PreferencesHelperTest.java
+++ b/services/tests/uiservicestests/src/com/android/server/notification/PreferencesHelperTest.java
@@ -54,6 +54,11 @@ import static android.media.AudioAttributes.USAGE_NOTIFICATION;
import static android.os.UserHandle.USER_ALL;
import static android.os.UserHandle.USER_SYSTEM;
import static android.platform.test.flag.junit.SetFlagsRule.DefaultInitValueType.DEVICE_DEFAULT;
+import static android.service.notification.Adjustment.TYPE_CONTENT_RECOMMENDATION;
+import static android.service.notification.Adjustment.TYPE_NEWS;
+import static android.service.notification.Adjustment.TYPE_OTHER;
+import static android.service.notification.Adjustment.TYPE_PROMOTION;
+import static android.service.notification.Adjustment.TYPE_SOCIAL_MEDIA;
import static android.service.notification.Flags.FLAG_NOTIFICATION_CLASSIFICATION;
import static android.service.notification.Flags.notificationClassification;
@@ -640,6 +645,7 @@ public class PreferencesHelperTest extends UiServiceTestCase {
NotificationChannel updateNews = null;
if (notificationClassification()) {
+ mHelper.createReservedChannel(PKG_N_MR1, UID_N_MR1, TYPE_NEWS);
// change one of the reserved bundle channels to ensure changes are persisted across
// boot
updateNews = mHelper.getNotificationChannel(
@@ -1210,22 +1216,9 @@ public class PreferencesHelperTest extends UiServiceTestCase {
+ "app_user_locked_fields=\"0\" sent_invalid_msg=\"false\" "
+ "sent_valid_msg=\"false\" user_demote_msg_app=\"false\" sent_valid_bubble"
+ "=\"false\" uid=\"10002\">\n"
- + (notificationClassification() ? "<channel id=\"android.app.social\" "
- + "name=\"Social\" importance=\"2\" "
- + "sound=\"content://settings/system/notification_sound\" "
- + "usage=\"5\" content_type=\"4\" flags=\"0\" show_badge=\"true\" />\n" : "")
+ "<channel id=\"id\" name=\"name\" importance=\"2\" "
+ "sound=\"content://settings/system/notification_sound\" usage=\"5\" "
+ "content_type=\"4\" flags=\"0\" show_badge=\"true\" orig_imp=\"2\" />\n"
- + (notificationClassification() ? "<channel id=\"android.app.news\" name=\"News\" "
- + "importance=\"2\" sound=\"content://settings/system/notification_sound\" "
- + "usage=\"5\" content_type=\"4\" flags=\"0\" show_badge=\"true\" />\n"
- + "<channel id=\"android.app.recs\" name=\"Recommendations\" importance=\"2\" "
- + "sound=\"content://settings/system/notification_sound\" usage=\"5\" "
- + "content_type=\"4\" flags=\"0\" show_badge=\"true\" />\n"
- + "<channel id=\"android.app.promotions\" name=\"Promotions\" importance=\"2\" "
- + "sound=\"content://settings/system/notification_sound\" usage=\"5\" "
- + "content_type=\"4\" flags=\"0\" show_badge=\"true\" />\n" : "")
+ "</package>\n"
+ "<package name=\"com.example.n_mr1\" show_badge=\"true\" "
+ "app_user_locked_fields=\"0\" sent_invalid_msg=\"false\" "
@@ -1233,10 +1226,6 @@ public class PreferencesHelperTest extends UiServiceTestCase {
+ "=\"false\" uid=\"10001\">\n"
+ "<channelGroup id=\"1\" name=\"bye\" blocked=\"false\" locked=\"0\" />\n"
+ "<channelGroup id=\"2\" name=\"hello\" blocked=\"false\" locked=\"0\" />\n"
- + (notificationClassification() ? "<channel id=\"android.app.social\" "
- + "name=\"Social\" importance=\"2\" "
- + "sound=\"content://settings/system/notification_sound\" "
- + "usage=\"5\" content_type=\"4\" flags=\"0\" show_badge=\"true\" />\n" : "")
+ "<channel id=\"id1\" name=\"name1\" importance=\"4\" show_badge=\"true\" "
+ "orig_imp=\"4\" />\n"
+ "<channel id=\"id2\" name=\"name2\" desc=\"descriptions for all\" "
@@ -1246,15 +1235,6 @@ public class PreferencesHelperTest extends UiServiceTestCase {
+ "sound=\"content://settings/system/notification_sound\" usage=\"5\" "
+ "content_type=\"4\" flags=\"0\" vibration_enabled=\"true\" show_badge=\"true\" "
+ "orig_imp=\"4\" />\n"
- + (notificationClassification() ? "<channel id=\"android.app.news\" name=\"News\" "
- + "importance=\"2\" sound=\"content://settings/system/notification_sound\" "
- + "usage=\"5\" content_type=\"4\" flags=\"0\" show_badge=\"true\" />\n"
- + "<channel id=\"android.app.recs\" name=\"Recommendations\" importance=\"2\" "
- + "sound=\"content://settings/system/notification_sound\" usage=\"5\" "
- + "content_type=\"4\" flags=\"0\" show_badge=\"true\" />\n"
- + "<channel id=\"android.app.promotions\" name=\"Promotions\" importance=\"2\" "
- + "sound=\"content://settings/system/notification_sound\" usage=\"5\" "
- + "content_type=\"4\" flags=\"0\" show_badge=\"true\" />\n" : "")
+ "<channel id=\"miscellaneous\" name=\"Uncategorized\" "
+ "sound=\"content://settings/system/notification_sound\" usage=\"5\" "
+ "content_type=\"4\" flags=\"0\" show_badge=\"true\" />\n"
@@ -1321,22 +1301,9 @@ public class PreferencesHelperTest extends UiServiceTestCase {
+ "app_user_locked_fields=\"0\" sent_invalid_msg=\"false\" "
+ "sent_valid_msg=\"false\" user_demote_msg_app=\"false\" sent_valid_bubble"
+ "=\"false\">\n"
- + (notificationClassification() ? "<channel id=\"android.app.social\" "
- + "name=\"Social\" importance=\"2\" "
- + "sound=\"content://settings/system/notification_sound\" "
- + "usage=\"5\" content_type=\"4\" flags=\"0\" show_badge=\"true\" />\n" : "")
+ "<channel id=\"id\" name=\"name\" importance=\"2\" "
+ "sound=\"content://settings/system/notification_sound\" usage=\"5\" "
+ "content_type=\"4\" flags=\"0\" show_badge=\"true\" orig_imp=\"2\" />\n"
- + (notificationClassification() ? "<channel id=\"android.app.news\" name=\"News\" "
- + "importance=\"2\" sound=\"content://settings/system/notification_sound\" "
- + "usage=\"5\" content_type=\"4\" flags=\"0\" show_badge=\"true\" />\n"
- + "<channel id=\"android.app.recs\" name=\"Recommendations\" importance=\"2\" "
- + "sound=\"content://settings/system/notification_sound\" usage=\"5\" "
- + "content_type=\"4\" flags=\"0\" show_badge=\"true\" />\n"
- + "<channel id=\"android.app.promotions\" name=\"Promotions\" importance=\"2\" "
- + "sound=\"content://settings/system/notification_sound\" usage=\"5\" "
- + "content_type=\"4\" flags=\"0\" show_badge=\"true\" />\n" : "")
+ "</package>\n"
// Importance default because on in permission helper
+ "<package name=\"com.example.n_mr1\" importance=\"3\" show_badge=\"true\" "
@@ -1345,10 +1312,6 @@ public class PreferencesHelperTest extends UiServiceTestCase {
+ "=\"false\">\n"
+ "<channelGroup id=\"1\" name=\"bye\" blocked=\"false\" locked=\"0\" />\n"
+ "<channelGroup id=\"2\" name=\"hello\" blocked=\"false\" locked=\"0\" />\n"
- + (notificationClassification() ? "<channel id=\"android.app.social\" "
- + "name=\"Social\" importance=\"2\" "
- + "sound=\"content://settings/system/notification_sound\" "
- + "usage=\"5\" content_type=\"4\" flags=\"0\" show_badge=\"true\" />\n" : "")
+ "<channel id=\"id1\" name=\"name1\" importance=\"4\" show_badge=\"true\" "
+ "orig_imp=\"4\" />\n"
+ "<channel id=\"id2\" name=\"name2\" desc=\"descriptions for all\" "
@@ -1358,15 +1321,6 @@ public class PreferencesHelperTest extends UiServiceTestCase {
+ "sound=\"content://settings/system/notification_sound\" usage=\"5\" "
+ "content_type=\"4\" flags=\"0\" vibration_enabled=\"true\" show_badge=\"true\" "
+ "orig_imp=\"4\" />\n"
- + (notificationClassification() ? "<channel id=\"android.app.news\" name=\"News\" "
- + "importance=\"2\" sound=\"content://settings/system/notification_sound\" "
- + "usage=\"5\" content_type=\"4\" flags=\"0\" show_badge=\"true\" />\n"
- + "<channel id=\"android.app.recs\" name=\"Recommendations\" importance=\"2\" "
- + "sound=\"content://settings/system/notification_sound\" usage=\"5\" "
- + "content_type=\"4\" flags=\"0\" show_badge=\"true\" />\n"
- + "<channel id=\"android.app.promotions\" name=\"Promotions\" importance=\"2\" "
- + "sound=\"content://settings/system/notification_sound\" usage=\"5\" "
- + "content_type=\"4\" flags=\"0\" show_badge=\"true\" />\n" : "")
+ "<channel id=\"miscellaneous\" name=\"Uncategorized\" "
+ "sound=\"content://settings/system/notification_sound\" usage=\"5\" "
+ "content_type=\"4\" flags=\"0\" show_badge=\"true\" />\n"
@@ -1433,22 +1387,9 @@ public class PreferencesHelperTest extends UiServiceTestCase {
+ "app_user_locked_fields=\"0\" sent_invalid_msg=\"false\" "
+ "sent_valid_msg=\"false\" user_demote_msg_app=\"false\" sent_valid_bubble"
+ "=\"false\">\n"
- + (notificationClassification() ? "<channel id=\"android.app.social\" "
- + "name=\"Social\" importance=\"2\" "
- + "sound=\"content://settings/system/notification_sound\" usage=\"5\" "
- + "content_type=\"4\" flags=\"0\" show_badge=\"true\" />\n" : "")
+ "<channel id=\"id\" name=\"name\" importance=\"2\" "
+ "sound=\"content://settings/system/notification_sound\" usage=\"5\" "
+ "content_type=\"4\" flags=\"0\" show_badge=\"true\" orig_imp=\"2\" />\n"
- + (notificationClassification() ? "<channel id=\"android.app.news\" name=\"News\" "
- + "importance=\"2\" sound=\"content://settings/system/notification_sound\" "
- + "usage=\"5\" content_type=\"4\" flags=\"0\" show_badge=\"true\" />\n"
- + "<channel id=\"android.app.recs\" name=\"Recommendations\" importance=\"2\" "
- + "sound=\"content://settings/system/notification_sound\" usage=\"5\" "
- + "content_type=\"4\" flags=\"0\" show_badge=\"true\" />\n"
- + "<channel id=\"android.app.promotions\" name=\"Promotions\" importance=\"2\" "
- + "sound=\"content://settings/system/notification_sound\" usage=\"5\" "
- + "content_type=\"4\" flags=\"0\" show_badge=\"true\" />\n" : "")
+ "</package>\n"
// Importance 0 because missing from permission helper
+ "<package name=\"com.example.n_mr1\" importance=\"0\" show_badge=\"true\" "
@@ -1457,10 +1398,6 @@ public class PreferencesHelperTest extends UiServiceTestCase {
+ "=\"false\">\n"
+ "<channelGroup id=\"1\" name=\"bye\" blocked=\"false\" locked=\"0\" />\n"
+ "<channelGroup id=\"2\" name=\"hello\" blocked=\"false\" locked=\"0\" />\n"
- + (notificationClassification() ? "<channel id=\"android.app.social\" "
- + "name=\"Social\" importance=\"2\" "
- + "sound=\"content://settings/system/notification_sound\" usage=\"5\" "
- + "content_type=\"4\" flags=\"0\" show_badge=\"true\" />\n" : "")
+ "<channel id=\"id1\" name=\"name1\" importance=\"4\" show_badge=\"true\" "
+ "orig_imp=\"4\" />\n"
+ "<channel id=\"id2\" name=\"name2\" desc=\"descriptions for all\" "
@@ -1470,15 +1407,6 @@ public class PreferencesHelperTest extends UiServiceTestCase {
+ "sound=\"content://settings/system/notification_sound\" usage=\"5\" "
+ "content_type=\"4\" flags=\"0\" vibration_enabled=\"true\" show_badge=\"true\" "
+ "orig_imp=\"4\" />\n"
- + (notificationClassification() ? "<channel id=\"android.app.news\" name=\"News\" "
- + "importance=\"2\" sound=\"content://settings/system/notification_sound\" "
- + "usage=\"5\" content_type=\"4\" flags=\"0\" show_badge=\"true\" />\n"
- + "<channel id=\"android.app.recs\" name=\"Recommendations\" importance=\"2\" "
- + "sound=\"content://settings/system/notification_sound\" usage=\"5\" "
- + "content_type=\"4\" flags=\"0\" show_badge=\"true\" />\n"
- + "<channel id=\"android.app.promotions\" name=\"Promotions\" importance=\"2\" "
- + "sound=\"content://settings/system/notification_sound\" usage=\"5\" "
- + "content_type=\"4\" flags=\"0\" show_badge=\"true\" />\n" : "")
+ "<channel id=\"miscellaneous\" name=\"Uncategorized\" "
+ "sound=\"content://settings/system/notification_sound\" usage=\"5\" "
+ "content_type=\"4\" flags=\"0\" show_badge=\"true\" />\n"
@@ -2183,10 +2111,10 @@ public class PreferencesHelperTest extends UiServiceTestCase {
}
@Test
- @DisableFlags(FLAG_NOTIFICATION_CLASSIFICATION)
public void testUpdate_preUpgrade_updatesAppFields() throws Exception {
assertTrue(mHelper.canShowBadge(PKG_N_MR1, UID_N_MR1));
- assertEquals(Notification.PRIORITY_DEFAULT, mHelper.getPackagePriority(PKG_N_MR1, UID_N_MR1));
+ assertEquals(Notification.PRIORITY_DEFAULT,
+ mHelper.getPackagePriority(PKG_N_MR1, UID_N_MR1));
assertEquals(VISIBILITY_NO_OVERRIDE,
mHelper.getPackageVisibility(PKG_N_MR1, UID_N_MR1));
@@ -2549,7 +2477,7 @@ public class PreferencesHelperTest extends UiServiceTestCase {
List<NotificationChannel> channels =
mHelper.getNotificationChannels(PKG_N_MR1, UID_N_MR1, false, true).getList();
// Default channel + non-deleted channel + system defaults
- assertEquals(notificationClassification() ? 6 : 2, channels.size());
+ assertEquals(2, channels.size());
for (NotificationChannel nc : channels) {
if (channel2.getId().equals(nc.getId())) {
compareChannels(channel2, nc);
@@ -2559,7 +2487,7 @@ public class PreferencesHelperTest extends UiServiceTestCase {
// Returns deleted channels too
channels = mHelper.getNotificationChannels(PKG_N_MR1, UID_N_MR1, true, true).getList();
// Includes system channel(s)
- assertEquals(notificationClassification() ? 7 : 3, channels.size());
+ assertEquals(3, channels.size());
for (NotificationChannel nc : channels) {
if (channel2.getId().equals(nc.getId())) {
compareChannels(channelMap.get(nc.getId()), nc);
@@ -3036,7 +2964,6 @@ public class PreferencesHelperTest extends UiServiceTestCase {
}
@Test
- @DisableFlags(FLAG_NOTIFICATION_CLASSIFICATION)
public void testOnlyHasDefaultChannel() throws Exception {
assertTrue(mHelper.onlyHasDefaultChannel(PKG_N_MR1, UID_N_MR1));
assertFalse(mHelper.onlyHasDefaultChannel(PKG_O, UID_O));
@@ -3047,6 +2974,18 @@ public class PreferencesHelperTest extends UiServiceTestCase {
}
@Test
+ @EnableFlags(FLAG_NOTIFICATION_CLASSIFICATION)
+ public void testOnlyHasDefaultChannel_bundleExists() throws Exception {
+ mHelper.createReservedChannel(PKG_N_MR1, UID_N_MR1, TYPE_NEWS);
+ assertTrue(mHelper.onlyHasDefaultChannel(PKG_N_MR1, UID_N_MR1));
+ assertFalse(mHelper.onlyHasDefaultChannel(PKG_O, UID_O));
+
+ mHelper.createNotificationChannel(PKG_N_MR1, UID_N_MR1, getChannel(), true, false,
+ UID_N_MR1, false);
+ assertFalse(mHelper.onlyHasDefaultChannel(PKG_N_MR1, UID_N_MR1));
+ }
+
+ @Test
public void testCreateDeletedChannel() throws Exception {
long[] vibration = new long[]{100, 67, 145, 156};
NotificationChannel channel =
@@ -3315,7 +3254,7 @@ public class PreferencesHelperTest extends UiServiceTestCase {
// user 0 records remain
for (int i = 0; i < user0Uids.length; i++) {
- assertEquals(notificationClassification() ? 5 : 1,
+ assertEquals(1,
mHelper.getNotificationChannels(PKG_N_MR1, user0Uids[i], false, true)
.getList().size());
}
@@ -3346,7 +3285,7 @@ public class PreferencesHelperTest extends UiServiceTestCase {
assertFalse(mHelper.onPackagesChanged(false, USER_SYSTEM,
new String[]{PKG_N_MR1}, new int[]{UID_N_MR1}));
- assertEquals(notificationClassification() ? 6 : 2,
+ assertEquals(2,
mHelper.getNotificationChannels(PKG_N_MR1, UID_N_MR1, false, true)
.getList().size());
}
@@ -3420,7 +3359,7 @@ public class PreferencesHelperTest extends UiServiceTestCase {
@Test
public void testRecordDefaults() throws Exception {
assertEquals(true, mHelper.canShowBadge(PKG_N_MR1, UID_N_MR1));
- assertEquals(notificationClassification() ? 5 : 1,
+ assertEquals(1,
mHelper.getNotificationChannels(PKG_N_MR1, UID_N_MR1, false, true)
.getList().size());
}
@@ -3659,9 +3598,6 @@ public class PreferencesHelperTest extends UiServiceTestCase {
new NotificationChannel("" + j, "a", IMPORTANCE_HIGH), true, false,
UID_N_MR1, false);
}
- if (notificationClassification()) {
- numChannels += 4;
- }
expectedChannels.put(pkgName, numChannels);
}
@@ -4883,10 +4819,6 @@ public class PreferencesHelperTest extends UiServiceTestCase {
@Test
public void testTooManyChannels() {
int numToCreate = NOTIFICATION_CHANNEL_COUNT_LIMIT;
- if (notificationClassification()) {
- // reserved channels lower limit
- numToCreate -= 4;
- }
for (int i = 0; i < numToCreate; i++) {
NotificationChannel channel = new NotificationChannel(String.valueOf(i),
String.valueOf(i), NotificationManager.IMPORTANCE_HIGH);
@@ -4907,10 +4839,6 @@ public class PreferencesHelperTest extends UiServiceTestCase {
@Test
public void testTooManyChannels_xml() throws Exception {
int numToCreate = NOTIFICATION_CHANNEL_COUNT_LIMIT;
- if (notificationClassification()) {
- // reserved channels lower limit
- numToCreate -= 4;
- }
String extraChannel = "EXTRA";
String extraChannel1 = "EXTRA1";
@@ -5928,9 +5856,7 @@ public class PreferencesHelperTest extends UiServiceTestCase {
ArrayList<StatsEvent> events = new ArrayList<>();
mHelper.pullPackageChannelPreferencesStats(events);
- assertEquals("expected number of events",
- notificationClassification() ? 7 : 3,
- events.size());
+ assertEquals("expected number of events", 3, events.size());
for (StatsEvent ev : events) {
// all of these events should be of PackageNotificationChannelPreferences type,
// and therefore we expect the atom to have this field.
@@ -5971,17 +5897,11 @@ public class PreferencesHelperTest extends UiServiceTestCase {
mHelper.createNotificationChannel(PKG_O, UID_O, channelC, true, false, UID_O, false);
List<String> channels = new LinkedList<>(Arrays.asList("a", "b", "c"));
- if (notificationClassification()) {
- channels.add(NEWS_ID);
- channels.add(PROMOTIONS_ID);
- channels.add(SOCIAL_MEDIA_ID);
- channels.add(RECS_ID);
- }
ArrayList<StatsEvent> events = new ArrayList<>();
mHelper.pullPackageChannelPreferencesStats(events);
- assertEquals("total events", notificationClassification() ? 7 : 3, events.size());
+ assertEquals("total events", 3, events.size());
for (StatsEvent ev : events) {
AtomsProto.Atom atom = StatsEventTestUtils.convertToAtom(ev);
assertTrue(atom.hasPackageNotificationChannelPreferences());
@@ -6015,7 +5935,7 @@ public class PreferencesHelperTest extends UiServiceTestCase {
mHelper.pullPackageChannelPreferencesStats(events);
// In this case, we want to check the properties of the conversation channel (not parent)
- assertEquals("total events", notificationClassification() ? 6 : 2, events.size());
+ assertEquals("total events", 2, events.size());
for (StatsEvent ev : events) {
AtomsProto.Atom atom = StatsEventTestUtils.convertToAtom(ev);
assertTrue(atom.hasPackageNotificationChannelPreferences());
@@ -6047,9 +5967,7 @@ public class PreferencesHelperTest extends UiServiceTestCase {
ArrayList<StatsEvent> events = new ArrayList<>();
mHelper.pullPackageChannelPreferencesStats(events);
- assertEquals("total events",
- notificationClassification() ? 6 : 2,
- events.size());
+ assertEquals("total events", 2, events.size());
for (StatsEvent ev : events) {
AtomsProto.Atom atom = StatsEventTestUtils.convertToAtom(ev);
assertTrue(atom.hasPackageNotificationChannelPreferences());
@@ -6080,9 +5998,7 @@ public class PreferencesHelperTest extends UiServiceTestCase {
ArrayList<StatsEvent> events = new ArrayList<>();
mHelper.pullPackageChannelPreferencesStats(events);
- assertEquals("total events",
- notificationClassification() ? 6 : 2,
- events.size());
+ assertEquals("total events", 2, events.size());
for (StatsEvent ev : events) {
AtomsProto.Atom atom = StatsEventTestUtils.convertToAtom(ev);
assertTrue(atom.hasPackageNotificationChannelPreferences());
@@ -6367,8 +6283,7 @@ public class PreferencesHelperTest extends UiServiceTestCase {
@Test
@EnableFlags(FLAG_NOTIFICATION_CLASSIFICATION)
public void testGetNotificationChannels_omitBundleChannels() {
- // do something that triggers settings creation for an app
- mHelper.setShowBadge(PKG_O, UID_O, true);
+ mHelper.createReservedChannel(PKG_O, UID_O, TYPE_NEWS);
assertThat(mHelper.getNotificationChannels(PKG_O, UID_O, true, false).getList()).isEmpty();
}
@@ -6376,18 +6291,34 @@ public class PreferencesHelperTest extends UiServiceTestCase {
@Test
@EnableFlags(FLAG_NOTIFICATION_CLASSIFICATION)
public void testNotificationBundles() {
- // do something that triggers settings creation for an app
- mHelper.setShowBadge(PKG_O, UID_O, true);
-
- // verify 4 reserved channels are created
+ mHelper.createReservedChannel(PKG_O, UID_O, TYPE_NEWS);
assertThat(mHelper.getNotificationChannel(PKG_O, UID_O, NEWS_ID, false).getImportance())
.isEqualTo(IMPORTANCE_LOW);
- assertThat(mHelper.getNotificationChannel(PKG_O, UID_O, PROMOTIONS_ID, false)
- .getImportance()).isEqualTo(IMPORTANCE_LOW);
- assertThat(mHelper.getNotificationChannel(PKG_O, UID_O, SOCIAL_MEDIA_ID, false)
- .getImportance()).isEqualTo(IMPORTANCE_LOW);
+ assertThat(mHelper.getNotificationChannels(PKG_O, UID_O, true, true).getList().size())
+ .isEqualTo(1);
+
+ mHelper.createReservedChannel(PKG_O, UID_O, TYPE_SOCIAL_MEDIA);
+ assertThat(mHelper.getNotificationChannel(PKG_O, UID_O, SOCIAL_MEDIA_ID, false).
+ getImportance()).isEqualTo(IMPORTANCE_LOW);
+ assertThat(mHelper.getNotificationChannels(PKG_O, UID_O, true, true).getList().size())
+ .isEqualTo(2);
+
+ mHelper.createReservedChannel(PKG_O, UID_O, TYPE_CONTENT_RECOMMENDATION);
assertThat(mHelper.getNotificationChannel(PKG_O, UID_O, RECS_ID, false).getImportance())
.isEqualTo(IMPORTANCE_LOW);
+ assertThat(mHelper.getNotificationChannels(PKG_O, UID_O, true, true).getList().size())
+ .isEqualTo(3);
+
+ mHelper.createReservedChannel(PKG_O, UID_O, TYPE_PROMOTION);
+ assertThat(mHelper.getNotificationChannel(PKG_O, UID_O, PROMOTIONS_ID, false)
+ .getImportance()).isEqualTo(IMPORTANCE_LOW);
+ assertThat(mHelper.getNotificationChannels(PKG_O, UID_O, true, true).getList().size())
+ .isEqualTo(4);
+
+ // only the first 4 types are created; no others
+ mHelper.createReservedChannel(PKG_O, UID_O, TYPE_OTHER);
+ assertThat(mHelper.getNotificationChannels(PKG_O, UID_O, true, true).getList().size())
+ .isEqualTo(4);
}
@Test
@@ -6417,8 +6348,7 @@ public class PreferencesHelperTest extends UiServiceTestCase {
@Test
@EnableFlags(FLAG_NOTIFICATION_CLASSIFICATION)
public void testNotificationBundles_appsCannotUpdate() {
- // do something that triggers settings creation for an app
- mHelper.setShowBadge(PKG_O, UID_O, true);
+ mHelper.createReservedChannel(PKG_O, UID_O, TYPE_NEWS);
NotificationChannel fromApp =
new NotificationChannel(NEWS_ID, "The best channel", IMPORTANCE_HIGH);
@@ -6431,8 +6361,7 @@ public class PreferencesHelperTest extends UiServiceTestCase {
@Test
@EnableFlags(FLAG_NOTIFICATION_CLASSIFICATION)
public void testNotificationBundles_osCanAllowToBypassDnd() {
- // do something that triggers settings creation for an app
- mHelper.setShowBadge(PKG_O, UID_O, true);
+ mHelper.createReservedChannel(PKG_O, UID_O, TYPE_NEWS);
NotificationChannel fromApp =
new NotificationChannel(NEWS_ID, "The best channel", IMPORTANCE_HIGH);
@@ -6442,18 +6371,17 @@ public class PreferencesHelperTest extends UiServiceTestCase {
@Test
@EnableFlags(FLAG_NOTIFICATION_CLASSIFICATION)
public void testUnDeleteBundleChannelsOnLoadIfNotUserChange() throws Exception {
- mHelper.setShowBadge(PKG_P, UID_P, true);
// the public create/update methods should prevent this, so take advantage of the fact that
// the object is in the same process
- mHelper.getNotificationChannel(PKG_P, UID_P, SOCIAL_MEDIA_ID, true).setDeleted(true);
+ mHelper.createReservedChannel(PKG_N_MR1, UID_N_MR1, TYPE_SOCIAL_MEDIA).setDeleted(true);
ByteArrayOutputStream baos = writeXmlAndPurge(PKG_N_MR1, UID_N_MR1, false,
UserHandle.USER_ALL, SOCIAL_MEDIA_ID);
loadStreamXml(baos, false, UserHandle.USER_ALL);
- assertThat(mXmlHelper.getNotificationChannel(PKG_P, UID_P, SOCIAL_MEDIA_ID, true).
- isDeleted()).isFalse();
+ assertThat(mXmlHelper.getNotificationChannel(PKG_N_MR1, UID_N_MR1, SOCIAL_MEDIA_ID, true)
+ .isDeleted()).isFalse();
}
@Test
diff --git a/services/tests/wmtests/src/com/android/server/policy/ModifierShortcutTests.java b/services/tests/wmtests/src/com/android/server/policy/ModifierShortcutTests.java
index c186a0355588..28ae271e20fc 100644
--- a/services/tests/wmtests/src/com/android/server/policy/ModifierShortcutTests.java
+++ b/services/tests/wmtests/src/com/android/server/policy/ModifierShortcutTests.java
@@ -43,6 +43,8 @@ import static android.view.KeyEvent.KEYCODE_Z;
import android.app.role.RoleManager;
import android.content.ComponentName;
import android.content.Intent;
+import android.hardware.input.AppLaunchData;
+import android.hardware.input.KeyGestureEvent;
import android.os.RemoteException;
import android.platform.test.annotations.DisableFlags;
import android.platform.test.annotations.EnableFlags;
@@ -85,7 +87,8 @@ public class ModifierShortcutTests extends ShortcutKeyTestBase {
* Test meta+ shortcuts defined in bookmarks.xml.
*/
@Test
- public void testMetaShortcuts() {
+ @DisableFlags(com.android.hardware.input.Flags.FLAG_USE_KEY_GESTURE_EVENT_HANDLER)
+ public void testMetaShortcuts_withoutKeyGestureEventHandling() {
for (int i = 0; i < INTENT_SHORTCUTS.size(); i++) {
final int keyCode = INTENT_SHORTCUTS.keyAt(i);
final String category = INTENT_SHORTCUTS.valueAt(i);
@@ -115,6 +118,49 @@ public class ModifierShortcutTests extends ShortcutKeyTestBase {
}
+ @Test
+ @EnableFlags(com.android.hardware.input.Flags.FLAG_USE_KEY_GESTURE_EVENT_HANDLER)
+ public void testMetaShortcuts_withKeyGestureEventHandling() {
+ for (int i = 0; i < INTENT_SHORTCUTS.size(); i++) {
+ final String category = INTENT_SHORTCUTS.valueAt(i);
+ mPhoneWindowManager.sendKeyGestureEvent(
+ new KeyGestureEvent.Builder()
+ .setKeyGestureType(KeyGestureEvent.KEY_GESTURE_TYPE_LAUNCH_APPLICATION)
+ .setAction(KeyGestureEvent.ACTION_GESTURE_COMPLETE)
+ .setAppLaunchData(AppLaunchData.createLaunchDataForCategory(category))
+ .build()
+ );
+ mPhoneWindowManager.assertLaunchCategory(category);
+ }
+
+ mPhoneWindowManager.overrideRoleManager();
+ for (int i = 0; i < ROLE_SHORTCUTS.size(); i++) {
+ final String role = ROLE_SHORTCUTS.valueAt(i);
+
+ mPhoneWindowManager.sendKeyGestureEvent(
+ new KeyGestureEvent.Builder()
+ .setKeyGestureType(KeyGestureEvent.KEY_GESTURE_TYPE_LAUNCH_APPLICATION)
+ .setAction(KeyGestureEvent.ACTION_GESTURE_COMPLETE)
+ .setAppLaunchData(AppLaunchData.createLaunchDataForRole(role))
+ .build()
+ );
+ mPhoneWindowManager.assertLaunchRole(role);
+ }
+
+ mPhoneWindowManager.sendKeyGestureEvent(
+ new KeyGestureEvent.Builder()
+ .setKeyGestureType(KeyGestureEvent.KEY_GESTURE_TYPE_LAUNCH_APPLICATION)
+ .setAction(KeyGestureEvent.ACTION_GESTURE_COMPLETE)
+ .setAppLaunchData(
+ new AppLaunchData.ComponentData("com.test",
+ "com.test.BookmarkTest"))
+ .build()
+ );
+ mPhoneWindowManager.assertActivityTargetLaunched(
+ new ComponentName("com.test", "com.test.BookmarkTest"));
+
+ }
+
/**
* ALT + TAB to show recent apps.
*/
diff --git a/services/tests/wmtests/src/com/android/server/wm/BackgroundActivityStartControllerExemptionTests.java b/services/tests/wmtests/src/com/android/server/wm/BackgroundActivityStartControllerExemptionTests.java
index 750968100e2b..965b65c8f1d1 100644
--- a/services/tests/wmtests/src/com/android/server/wm/BackgroundActivityStartControllerExemptionTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/BackgroundActivityStartControllerExemptionTests.java
@@ -39,7 +39,6 @@ import static org.mockito.Mockito.when;
import android.app.ActivityOptions;
import android.app.AppOpsManager;
-import android.app.BackgroundStartPrivileges;
import android.content.ComponentName;
import android.content.Context;
import android.content.Intent;
@@ -223,12 +222,12 @@ public class BackgroundActivityStartControllerExemptionTests {
// prepare call
PendingIntentRecord originatingPendingIntent = mPendingIntentRecord;
- BackgroundStartPrivileges forcedBalByPiSender = BackgroundStartPrivileges.NONE;
+ boolean allowBalExemptionForSystemProcess = false;
Intent intent = TEST_INTENT;
ActivityOptions checkedOptions = mCheckedOptions;
BackgroundActivityStartController.BalState balState = mController.new BalState(callingUid,
callingPid, callingPackage, realCallingUid, realCallingPid, mCallerApp,
- originatingPendingIntent, forcedBalByPiSender, mResultRecord, intent,
+ originatingPendingIntent, allowBalExemptionForSystemProcess, mResultRecord, intent,
checkedOptions);
assertWithMessage(balState.toString()).that(balState.isPendingIntent()).isTrue();
@@ -263,12 +262,12 @@ public class BackgroundActivityStartControllerExemptionTests {
// prepare call
PendingIntentRecord originatingPendingIntent = mPendingIntentRecord;
- BackgroundStartPrivileges forcedBalByPiSender = BackgroundStartPrivileges.NONE;
+ boolean allowBalExemptionForSystemProcess = false;
Intent intent = TEST_INTENT;
ActivityOptions checkedOptions = mCheckedOptions;
BackgroundActivityStartController.BalState balState = mController.new BalState(callingUid,
callingPid, callingPackage, realCallingUid, realCallingPid, mCallerApp,
- originatingPendingIntent, forcedBalByPiSender, mResultRecord, intent,
+ originatingPendingIntent, allowBalExemptionForSystemProcess, mResultRecord, intent,
checkedOptions);
// call
@@ -295,12 +294,12 @@ public class BackgroundActivityStartControllerExemptionTests {
// prepare call
PendingIntentRecord originatingPendingIntent = mPendingIntentRecord;
- BackgroundStartPrivileges forcedBalByPiSender = BackgroundStartPrivileges.NONE;
+ boolean allowBalExemptionForSystemProcess = false;
Intent intent = TEST_INTENT;
ActivityOptions checkedOptions = mCheckedOptions;
BackgroundActivityStartController.BalState balState = mController.new BalState(callingUid,
callingPid, callingPackage, realCallingUid, realCallingPid, mCallerApp,
- originatingPendingIntent, forcedBalByPiSender, mResultRecord, intent,
+ originatingPendingIntent, allowBalExemptionForSystemProcess, mResultRecord, intent,
checkedOptions);
// call
@@ -328,14 +327,14 @@ public class BackgroundActivityStartControllerExemptionTests {
// prepare call
PendingIntentRecord originatingPendingIntent = mPendingIntentRecord;
- BackgroundStartPrivileges forcedBalByPiSender = BackgroundStartPrivileges.NONE;
+ boolean allowBalExemptionForSystemProcess = false;
Intent intent = TEST_INTENT;
ActivityOptions checkedOptions = mCheckedOptions
.setPendingIntentCreatorBackgroundActivityStartMode(
MODE_BACKGROUND_ACTIVITY_START_ALLOW_IF_VISIBLE);
BackgroundActivityStartController.BalState balState = mController.new BalState(callingUid,
callingPid, callingPackage, realCallingUid, realCallingPid, mCallerApp,
- originatingPendingIntent, forcedBalByPiSender, mResultRecord, intent,
+ originatingPendingIntent, allowBalExemptionForSystemProcess, mResultRecord, intent,
checkedOptions);
// call
@@ -363,14 +362,14 @@ public class BackgroundActivityStartControllerExemptionTests {
// prepare call
PendingIntentRecord originatingPendingIntent = mPendingIntentRecord;
- BackgroundStartPrivileges forcedBalByPiSender = BackgroundStartPrivileges.NONE;
+ boolean allowBalExemptionForSystemProcess = false;
Intent intent = TEST_INTENT;
ActivityOptions checkedOptions = mCheckedOptions
.setPendingIntentCreatorBackgroundActivityStartMode(
MODE_BACKGROUND_ACTIVITY_START_ALLOW_IF_VISIBLE);
BackgroundActivityStartController.BalState balState = mController.new BalState(callingUid,
callingPid, callingPackage, realCallingUid, realCallingPid, mCallerApp,
- originatingPendingIntent, forcedBalByPiSender, mResultRecord, intent,
+ originatingPendingIntent, allowBalExemptionForSystemProcess, mResultRecord, intent,
checkedOptions);
// call
@@ -405,12 +404,12 @@ public class BackgroundActivityStartControllerExemptionTests {
// prepare call
PendingIntentRecord originatingPendingIntent = mPendingIntentRecord;
- BackgroundStartPrivileges forcedBalByPiSender = BackgroundStartPrivileges.NONE;
+ boolean allowBalExemptionForSystemProcess = false;
Intent intent = TEST_INTENT;
ActivityOptions checkedOptions = mCheckedOptions;
BackgroundActivityStartController.BalState balState = mController.new BalState(callingUid,
callingPid, callingPackage, realCallingUid, realCallingPid, mCallerApp,
- originatingPendingIntent, forcedBalByPiSender, mResultRecord, intent,
+ originatingPendingIntent, allowBalExemptionForSystemProcess, mResultRecord, intent,
checkedOptions);
// call
@@ -441,12 +440,12 @@ public class BackgroundActivityStartControllerExemptionTests {
// prepare call
PendingIntentRecord originatingPendingIntent = mPendingIntentRecord;
- BackgroundStartPrivileges forcedBalByPiSender = BackgroundStartPrivileges.NONE;
+ boolean allowBalExemptionForSystemProcess = false;
Intent intent = TEST_INTENT;
ActivityOptions checkedOptions = mCheckedOptions;
BackgroundActivityStartController.BalState balState = mController.new BalState(callingUid,
callingPid, callingPackage, realCallingUid, realCallingPid, null,
- originatingPendingIntent, forcedBalByPiSender, mResultRecord, intent,
+ originatingPendingIntent, allowBalExemptionForSystemProcess, mResultRecord, intent,
checkedOptions);
// call
@@ -490,12 +489,12 @@ public class BackgroundActivityStartControllerExemptionTests {
// prepare call
PendingIntentRecord originatingPendingIntent = mPendingIntentRecord;
- BackgroundStartPrivileges forcedBalByPiSender = BackgroundStartPrivileges.NONE;
+ boolean allowBalExemptionForSystemProcess = false;
Intent intent = TEST_INTENT;
ActivityOptions checkedOptions = mCheckedOptions;
BackgroundActivityStartController.BalState balState = mController.new BalState(callingUid,
callingPid, callingPackage, realCallingUid, realCallingPid, null,
- originatingPendingIntent, forcedBalByPiSender, mResultRecord, intent,
+ originatingPendingIntent, allowBalExemptionForSystemProcess, mResultRecord, intent,
checkedOptions);
// call
@@ -526,12 +525,12 @@ public class BackgroundActivityStartControllerExemptionTests {
// prepare call
PendingIntentRecord originatingPendingIntent = mPendingIntentRecord;
- BackgroundStartPrivileges forcedBalByPiSender = BackgroundStartPrivileges.NONE;
+ boolean allowBalExemptionForSystemProcess = false;
Intent intent = TEST_INTENT;
ActivityOptions checkedOptions = mCheckedOptions;
BackgroundActivityStartController.BalState balState = mController.new BalState(callingUid,
callingPid, callingPackage, realCallingUid, realCallingPid, null,
- originatingPendingIntent, forcedBalByPiSender, mResultRecord, intent,
+ originatingPendingIntent, allowBalExemptionForSystemProcess, mResultRecord, intent,
checkedOptions);
// call
@@ -563,14 +562,14 @@ public class BackgroundActivityStartControllerExemptionTests {
// prepare call
PendingIntentRecord originatingPendingIntent = mPendingIntentRecord;
- BackgroundStartPrivileges forcedBalByPiSender = BackgroundStartPrivileges.NONE;
+ boolean allowBalExemptionForSystemProcess = false;
Intent intent = TEST_INTENT;
ActivityOptions checkedOptions = mCheckedOptions
.setPendingIntentBackgroundActivityStartMode(
MODE_BACKGROUND_ACTIVITY_START_ALLOW_IF_VISIBLE);
BackgroundActivityStartController.BalState balState = mController.new BalState(callingUid,
callingPid, callingPackage, realCallingUid, realCallingPid, null,
- originatingPendingIntent, forcedBalByPiSender, mResultRecord, intent,
+ originatingPendingIntent, allowBalExemptionForSystemProcess, mResultRecord, intent,
checkedOptions);
// call
@@ -597,12 +596,12 @@ public class BackgroundActivityStartControllerExemptionTests {
// prepare call
PendingIntentRecord originatingPendingIntent = mPendingIntentRecord;
- BackgroundStartPrivileges forcedBalByPiSender = BackgroundStartPrivileges.NONE;
+ boolean allowBalExemptionForSystemProcess = false;
Intent intent = TEST_INTENT;
ActivityOptions checkedOptions = mCheckedOptions;
BackgroundActivityStartController.BalState balState = mController.new BalState(callingUid,
callingPid, callingPackage, realCallingUid, realCallingPid, null,
- originatingPendingIntent, forcedBalByPiSender, mResultRecord, intent,
+ originatingPendingIntent, allowBalExemptionForSystemProcess, mResultRecord, intent,
checkedOptions);
// call
@@ -630,14 +629,14 @@ public class BackgroundActivityStartControllerExemptionTests {
// prepare call
PendingIntentRecord originatingPendingIntent = mPendingIntentRecord;
- BackgroundStartPrivileges forcedBalByPiSender = BackgroundStartPrivileges.NONE;
+ boolean allowBalExemptionForSystemProcess = false;
Intent intent = TEST_INTENT;
ActivityOptions checkedOptions = mCheckedOptions;
checkedOptions.setPendingIntentBackgroundActivityStartMode(
MODE_BACKGROUND_ACTIVITY_START_ALLOW_ALWAYS);
BackgroundActivityStartController.BalState balState = mController.new BalState(callingUid,
callingPid, callingPackage, realCallingUid, realCallingPid, null,
- originatingPendingIntent, forcedBalByPiSender, mResultRecord, intent,
+ originatingPendingIntent, allowBalExemptionForSystemProcess, mResultRecord, intent,
checkedOptions);
// call
@@ -664,12 +663,12 @@ public class BackgroundActivityStartControllerExemptionTests {
// prepare call
PendingIntentRecord originatingPendingIntent = mPendingIntentRecord;
- BackgroundStartPrivileges forcedBalByPiSender = BackgroundStartPrivileges.NONE;
+ boolean allowBalExemptionForSystemProcess = false;
Intent intent = TEST_INTENT;
ActivityOptions checkedOptions = mCheckedOptions;
BackgroundActivityStartController.BalState balState = mController.new BalState(callingUid,
callingPid, callingPackage, realCallingUid, realCallingPid, null,
- originatingPendingIntent, forcedBalByPiSender, mResultRecord, intent,
+ originatingPendingIntent, allowBalExemptionForSystemProcess, mResultRecord, intent,
checkedOptions);
// call
@@ -697,14 +696,14 @@ public class BackgroundActivityStartControllerExemptionTests {
// prepare call
PendingIntentRecord originatingPendingIntent = mPendingIntentRecord;
- BackgroundStartPrivileges forcedBalByPiSender = BackgroundStartPrivileges.NONE;
+ boolean allowBalExemptionForSystemProcess = false;
Intent intent = TEST_INTENT;
ActivityOptions checkedOptions =
mCheckedOptions.setPendingIntentBackgroundActivityStartMode(
MODE_BACKGROUND_ACTIVITY_START_ALLOW_ALWAYS);
BackgroundActivityStartController.BalState balState = mController.new BalState(callingUid,
callingPid, callingPackage, realCallingUid, realCallingPid, null,
- originatingPendingIntent, forcedBalByPiSender, mResultRecord, intent,
+ originatingPendingIntent, allowBalExemptionForSystemProcess, mResultRecord, intent,
checkedOptions);
// call
@@ -733,12 +732,12 @@ public class BackgroundActivityStartControllerExemptionTests {
// prepare call
PendingIntentRecord originatingPendingIntent = mPendingIntentRecord;
- BackgroundStartPrivileges forcedBalByPiSender = BackgroundStartPrivileges.NONE;
+ boolean allowBalExemptionForSystemProcess = false;
Intent intent = TEST_INTENT;
ActivityOptions checkedOptions = mCheckedOptions;
BackgroundActivityStartController.BalState balState = mController.new BalState(callingUid,
callingPid, callingPackage, realCallingUid, realCallingPid, null,
- originatingPendingIntent, forcedBalByPiSender, mResultRecord, intent,
+ originatingPendingIntent, allowBalExemptionForSystemProcess, mResultRecord, intent,
checkedOptions);
// call
@@ -764,12 +763,12 @@ public class BackgroundActivityStartControllerExemptionTests {
// prepare call
PendingIntentRecord originatingPendingIntent = mPendingIntentRecord;
- BackgroundStartPrivileges forcedBalByPiSender = BackgroundStartPrivileges.NONE;
+ boolean allowBalExemptionForSystemProcess = false;
Intent intent = TEST_INTENT;
ActivityOptions checkedOptions = mCheckedOptions;
BackgroundActivityStartController.BalState balState = mController.new BalState(callingUid,
callingPid, callingPackage, realCallingUid, realCallingPid, null,
- originatingPendingIntent, forcedBalByPiSender, mResultRecord, intent,
+ originatingPendingIntent, allowBalExemptionForSystemProcess, mResultRecord, intent,
checkedOptions);
// call
@@ -795,12 +794,12 @@ public class BackgroundActivityStartControllerExemptionTests {
// prepare call
PendingIntentRecord originatingPendingIntent = mPendingIntentRecord;
- BackgroundStartPrivileges forcedBalByPiSender = BackgroundStartPrivileges.NONE;
+ boolean allowBalExemptionForSystemProcess = false;
Intent intent = TEST_INTENT;
ActivityOptions checkedOptions = mCheckedOptions;
BackgroundActivityStartController.BalState balState = mController.new BalState(callingUid,
callingPid, callingPackage, realCallingUid, realCallingPid, null,
- originatingPendingIntent, forcedBalByPiSender, mResultRecord, intent,
+ originatingPendingIntent, allowBalExemptionForSystemProcess, mResultRecord, intent,
checkedOptions);
// call
@@ -831,12 +830,12 @@ public class BackgroundActivityStartControllerExemptionTests {
// prepare call
PendingIntentRecord originatingPendingIntent = mPendingIntentRecord;
- BackgroundStartPrivileges forcedBalByPiSender = BackgroundStartPrivileges.NONE;
+ boolean allowBalExemptionForSystemProcess = false;
Intent intent = TEST_INTENT;
ActivityOptions checkedOptions = mCheckedOptions;
BackgroundActivityStartController.BalState balState = mController.new BalState(callingUid,
callingPid, callingPackage, realCallingUid, realCallingPid, null,
- originatingPendingIntent, forcedBalByPiSender, mResultRecord, intent,
+ originatingPendingIntent, allowBalExemptionForSystemProcess, mResultRecord, intent,
checkedOptions);
// call
diff --git a/services/tests/wmtests/src/com/android/server/wm/BackgroundActivityStartControllerLogTests.java b/services/tests/wmtests/src/com/android/server/wm/BackgroundActivityStartControllerLogTests.java
index 23b1c4b9e150..7f7462debe8f 100644
--- a/services/tests/wmtests/src/com/android/server/wm/BackgroundActivityStartControllerLogTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/BackgroundActivityStartControllerLogTests.java
@@ -23,7 +23,6 @@ import static com.android.server.wm.BackgroundActivityStartControllerTests.setVi
import static com.google.common.truth.Truth.assertThat;
import android.app.ActivityOptions;
-import android.app.BackgroundStartPrivileges;
import android.content.Intent;
import android.platform.test.annotations.Presubmit;
@@ -189,7 +188,7 @@ public class BackgroundActivityStartControllerLogTests {
private void useIntent(int uid) {
mState = mController.new BalState(uid, APP1_PID,
"calling.package", uid, APP1_PID, null,
- null, BackgroundStartPrivileges.NONE, null, new Intent(),
+ null, false, null, new Intent(),
ActivityOptions.makeBasic());
}
@@ -200,7 +199,7 @@ public class BackgroundActivityStartControllerLogTests {
private void usePendingIntent(int callerUid, int realCallerUid) {
mState = mController.new BalState(callerUid, APP1_PID,
"calling.package", realCallerUid, APP2_PID, null,
- mPendingIntentRecord, BackgroundStartPrivileges.NONE, null, new Intent(),
+ mPendingIntentRecord, false, null, new Intent(),
ActivityOptions.makeBasic());
}
}
diff --git a/services/tests/wmtests/src/com/android/server/wm/BackgroundActivityStartControllerTests.java b/services/tests/wmtests/src/com/android/server/wm/BackgroundActivityStartControllerTests.java
index 6ec789599482..7bc9f301bcd6 100644
--- a/services/tests/wmtests/src/com/android/server/wm/BackgroundActivityStartControllerTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/BackgroundActivityStartControllerTests.java
@@ -241,14 +241,14 @@ public class BackgroundActivityStartControllerTests {
int realCallingUid = NO_UID;
int realCallingPid = NO_PID;
PendingIntentRecord originatingPendingIntent = null;
- BackgroundStartPrivileges forcedBalByPiSender = BackgroundStartPrivileges.NONE;
+ boolean allowBalExemptionForSystemProcess = false;
Intent intent = TEST_INTENT;
ActivityOptions checkedOptions = ActivityOptions.makeBasic();
// call
BalVerdict verdict = mController.checkBackgroundActivityStart(callingUid, callingPid,
callingPackage, realCallingUid, realCallingPid, mCallerApp,
- originatingPendingIntent, forcedBalByPiSender, mResultRecord, intent,
+ originatingPendingIntent, allowBalExemptionForSystemProcess, mResultRecord, intent,
checkedOptions);
// assertions
@@ -276,14 +276,14 @@ public class BackgroundActivityStartControllerTests {
int realCallingUid = NO_UID;
int realCallingPid = NO_PID;
PendingIntentRecord originatingPendingIntent = null;
- BackgroundStartPrivileges forcedBalByPiSender = BackgroundStartPrivileges.NONE;
+ boolean allowBalExemptionForSystemProcess = false;
Intent intent = TEST_INTENT;
ActivityOptions checkedOptions = ActivityOptions.makeBasic();
// call
BalVerdict verdict = mController.checkBackgroundActivityStart(callingUid, callingPid,
callingPackage, realCallingUid, realCallingPid, mCallerApp,
- originatingPendingIntent, forcedBalByPiSender, mResultRecord, intent,
+ originatingPendingIntent, allowBalExemptionForSystemProcess, mResultRecord, intent,
checkedOptions);
// assertions
@@ -311,14 +311,14 @@ public class BackgroundActivityStartControllerTests {
int realCallingUid = NO_UID;
int realCallingPid = NO_PID;
PendingIntentRecord originatingPendingIntent = null;
- BackgroundStartPrivileges forcedBalByPiSender = BackgroundStartPrivileges.NONE;
+ boolean allowBalExemptionForSystemProcess = false;
Intent intent = TEST_INTENT;
ActivityOptions checkedOptions = ActivityOptions.makeBasic();
// call
BalVerdict verdict = mController.checkBackgroundActivityStart(callingUid, callingPid,
callingPackage, realCallingUid, realCallingPid, mCallerApp,
- originatingPendingIntent, forcedBalByPiSender, mResultRecord, intent,
+ originatingPendingIntent, allowBalExemptionForSystemProcess, mResultRecord, intent,
checkedOptions);
// assertions
@@ -346,14 +346,14 @@ public class BackgroundActivityStartControllerTests {
int realCallingUid = NO_UID;
int realCallingPid = NO_PID;
PendingIntentRecord originatingPendingIntent = null;
- BackgroundStartPrivileges forcedBalByPiSender = BackgroundStartPrivileges.NONE;
+ boolean allowBalExemptionForSystemProcess = false;
Intent intent = TEST_INTENT;
ActivityOptions checkedOptions = ActivityOptions.makeBasic();
// call
BalVerdict verdict = mController.checkBackgroundActivityStart(callingUid, callingPid,
callingPackage, realCallingUid, realCallingPid, mCallerApp,
- originatingPendingIntent, forcedBalByPiSender, mResultRecord, intent,
+ originatingPendingIntent, allowBalExemptionForSystemProcess, mResultRecord, intent,
checkedOptions);
// assertions
@@ -380,14 +380,14 @@ public class BackgroundActivityStartControllerTests {
int realCallingUid = NO_UID;
int realCallingPid = NO_PID;
PendingIntentRecord originatingPendingIntent = null;
- BackgroundStartPrivileges forcedBalByPiSender = BackgroundStartPrivileges.NONE;
+ boolean allowBalExemptionForSystemProcess = false;
Intent intent = TEST_INTENT;
ActivityOptions checkedOptions = ActivityOptions.makeBasic();
// call
BalVerdict verdict = mController.checkBackgroundActivityStart(callingUid, callingPid,
callingPackage, realCallingUid, realCallingPid, mCallerApp,
- originatingPendingIntent, forcedBalByPiSender, mResultRecord, intent,
+ originatingPendingIntent, allowBalExemptionForSystemProcess, mResultRecord, intent,
checkedOptions);
// assertions
@@ -418,7 +418,7 @@ public class BackgroundActivityStartControllerTests {
int realCallingUid = NO_UID;
int realCallingPid = NO_PID;
PendingIntentRecord originatingPendingIntent = mPendingIntentRecord;
- BackgroundStartPrivileges forcedBalByPiSender = BackgroundStartPrivileges.NONE;
+ boolean allowBalExemptionForSystemProcess = false;
Intent intent = TEST_INTENT;
ActivityOptions checkedOptions = ActivityOptions.makeBasic()
.setPendingIntentBackgroundActivityStartMode(
@@ -429,7 +429,7 @@ public class BackgroundActivityStartControllerTests {
// call
BalVerdict verdict = mController.checkBackgroundActivityStart(callingUid, callingPid,
callingPackage, realCallingUid, realCallingPid, mCallerApp,
- originatingPendingIntent, forcedBalByPiSender, mResultRecord, intent,
+ originatingPendingIntent, allowBalExemptionForSystemProcess, mResultRecord, intent,
checkedOptions);
// assertions
@@ -457,7 +457,7 @@ public class BackgroundActivityStartControllerTests {
int realCallingUid = NO_UID;
int realCallingPid = NO_PID;
PendingIntentRecord originatingPendingIntent = mPendingIntentRecord;
- BackgroundStartPrivileges forcedBalByPiSender = BackgroundStartPrivileges.NONE;
+ boolean allowBalExemptionForSystemProcess = false;
Intent intent = TEST_INTENT;
ActivityOptions checkedOptions = ActivityOptions.makeBasic()
.setPendingIntentCreatorBackgroundActivityStartMode(
@@ -466,7 +466,7 @@ public class BackgroundActivityStartControllerTests {
// call
BalVerdict verdict = mController.checkBackgroundActivityStart(callingUid, callingPid,
callingPackage, realCallingUid, realCallingPid, mCallerApp,
- originatingPendingIntent, forcedBalByPiSender, mResultRecord, intent,
+ originatingPendingIntent, allowBalExemptionForSystemProcess, mResultRecord, intent,
checkedOptions);
// assertions
@@ -494,7 +494,7 @@ public class BackgroundActivityStartControllerTests {
int realCallingUid = NO_UID;
int realCallingPid = NO_PID;
PendingIntentRecord originatingPendingIntent = mPendingIntentRecord;
- BackgroundStartPrivileges forcedBalByPiSender = BackgroundStartPrivileges.NONE;
+ boolean allowBalExemptionForSystemProcess = false;
Intent intent = TEST_INTENT;
ActivityOptions checkedOptions = ActivityOptions.makeBasic()
.setPendingIntentBackgroundActivityStartMode(
@@ -503,7 +503,7 @@ public class BackgroundActivityStartControllerTests {
// call
BalVerdict verdict = mController.checkBackgroundActivityStart(callingUid, callingPid,
callingPackage, realCallingUid, realCallingPid, mCallerApp,
- originatingPendingIntent, forcedBalByPiSender, mResultRecord, intent,
+ originatingPendingIntent, allowBalExemptionForSystemProcess, mResultRecord, intent,
checkedOptions);
// assertions
@@ -530,7 +530,7 @@ public class BackgroundActivityStartControllerTests {
int realCallingUid = NO_UID;
int realCallingPid = NO_PID;
PendingIntentRecord originatingPendingIntent = null;
- BackgroundStartPrivileges forcedBalByPiSender = BackgroundStartPrivileges.NONE;
+ boolean allowBalExemptionForSystemProcess = false;
Intent intent = TEST_INTENT;
ActivityOptions checkedOptions = ActivityOptions.makeBasic();
WindowProcessController callerApp = mCallerApp;
@@ -539,8 +539,8 @@ public class BackgroundActivityStartControllerTests {
// call
BackgroundActivityStartController.BalState balState = mController
.new BalState(callingUid, callingPid, callingPackage, realCallingUid,
- realCallingPid, callerApp, originatingPendingIntent, forcedBalByPiSender,
- resultRecord, intent, checkedOptions);
+ realCallingPid, callerApp, originatingPendingIntent,
+ allowBalExemptionForSystemProcess, resultRecord, intent, checkedOptions);
// assertions
assertThat(balState.mAutoOptInReason).isEqualTo("notPendingIntent");
@@ -559,7 +559,7 @@ public class BackgroundActivityStartControllerTests {
+ "callingUidHasAnyVisibleWindow: false; "
+ "callingUidProcState: NONEXISTENT; "
+ "isCallingUidPersistentSystemProcess: false; "
- + "forcedBalByPiSender: BSP.NONE; "
+ + "allowBalExemptionForSystemProcess: false; "
+ "intent: Intent { cmp=package.app3/someClass }; "
+ "callerApp: mCallerApp; "
+ "inVisibleTask: false; "
@@ -596,7 +596,7 @@ public class BackgroundActivityStartControllerTests {
int realCallingUid = NO_UID;
int realCallingPid = NO_PID;
PendingIntentRecord originatingPendingIntent = mPendingIntentRecord;
- BackgroundStartPrivileges forcedBalByPiSender = BackgroundStartPrivileges.NONE;
+ boolean allowBalExemptionForSystemProcess = false;
Intent intent = TEST_INTENT;
ActivityOptions checkedOptions = ActivityOptions.makeBasic();
WindowProcessController callerApp = mCallerApp;
@@ -605,8 +605,8 @@ public class BackgroundActivityStartControllerTests {
// call
BackgroundActivityStartController.BalState balState = mController
.new BalState(callingUid, callingPid, callingPackage, realCallingUid,
- realCallingPid, callerApp, originatingPendingIntent, forcedBalByPiSender,
- resultRecord, intent, checkedOptions);
+ realCallingPid, callerApp, originatingPendingIntent,
+ allowBalExemptionForSystemProcess, resultRecord, intent, checkedOptions);
// assertions
assertThat(balState.mAutoOptInReason).isEqualTo("callForResult");
@@ -629,7 +629,7 @@ public class BackgroundActivityStartControllerTests {
int realCallingUid = NO_UID;
int realCallingPid = NO_PID;
PendingIntentRecord originatingPendingIntent = mPendingIntentRecord;
- BackgroundStartPrivileges forcedBalByPiSender = BackgroundStartPrivileges.NONE;
+ boolean allowBalExemptionForSystemProcess = false;
Intent intent = TEST_INTENT;
ActivityOptions checkedOptions = ActivityOptions.makeBasic();
WindowProcessController callerApp = mCallerApp;
@@ -638,8 +638,8 @@ public class BackgroundActivityStartControllerTests {
// call
BackgroundActivityStartController.BalState balState = mController
.new BalState(callingUid, callingPid, callingPackage, realCallingUid,
- realCallingPid, callerApp, originatingPendingIntent, forcedBalByPiSender,
- resultRecord, intent, checkedOptions);
+ realCallingPid, callerApp, originatingPendingIntent,
+ allowBalExemptionForSystemProcess, resultRecord, intent, checkedOptions);
// assertions
assertThat(balState.mAutoOptInReason).isNull();
@@ -659,7 +659,7 @@ public class BackgroundActivityStartControllerTests {
+ "callingUidHasAnyVisibleWindow: false; "
+ "callingUidProcState: NONEXISTENT; "
+ "isCallingUidPersistentSystemProcess: false; "
- + "forcedBalByPiSender: BSP.NONE; "
+ + "allowBalExemptionForSystemProcess: false; "
+ "intent: Intent { cmp=package.app3/someClass }; "
+ "callerApp: mCallerApp; "
+ "inVisibleTask: false; "
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 7196acc8ec2e..e8779c2b9ead 100644
--- a/services/tests/wmtests/src/com/android/server/wm/TransitionTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/TransitionTests.java
@@ -2354,6 +2354,11 @@ public class TransitionTests extends WindowTestsBase {
// ChangeInfo#mCommonAncestor should be set after reparent.
final Transition.ChangeInfo change = transition.mChanges.get(activity);
assertEquals(newParent.getDisplayArea(), change.mCommonAncestor);
+
+ // WindowContainer#onDisplayChanged should collect the moved task.
+ final DisplayContent newDisplay = createNewDisplay();
+ newParent.reparent(newDisplay.getDefaultTaskDisplayArea(), true /* onTop */);
+ assertTrue(transition.mParticipants.contains(newParent));
}
@Test
diff --git a/services/voiceinteraction/java/com/android/server/soundtrigger_middleware/SoundTriggerHw3Compat.java b/services/voiceinteraction/java/com/android/server/soundtrigger_middleware/SoundTriggerHw3Compat.java
index b1165bb57628..dfd80a00544d 100644
--- a/services/voiceinteraction/java/com/android/server/soundtrigger_middleware/SoundTriggerHw3Compat.java
+++ b/services/voiceinteraction/java/com/android/server/soundtrigger_middleware/SoundTriggerHw3Compat.java
@@ -36,6 +36,8 @@ import android.os.RemoteException;
import android.os.ServiceSpecificException;
import android.os.SystemClock;
+import com.android.server.FgThread;
+
public class SoundTriggerHw3Compat implements ISoundTriggerHal {
private final @NonNull ISoundTriggerHw mDriver;
private final @NonNull Runnable mRebootRunnable;
@@ -217,7 +219,12 @@ public class SoundTriggerHw3Compat implements ISoundTriggerHal {
@Override
public void onResourcesAvailable() {
- mDelegate.onResourcesAvailable();
+ // This call does not need to be sequenced relative to sessions on the upper levels.
+ // That is, if a new session gets this callback or if a already detached session gets
+ // this callback, because it is delayed, it doesn't matter, since this callback is
+ // purely informative and does not mutate any state -- it merely causes an already legal
+ // operation to be possibly re-attempted.
+ FgThread.getExecutor().execute(mDelegate::onResourcesAvailable);
}
@Override
diff --git a/telecomm/java/android/telecom/Conference.java b/telecomm/java/android/telecom/Conference.java
index f8037175fb05..7adcd4675297 100644
--- a/telecomm/java/android/telecom/Conference.java
+++ b/telecomm/java/android/telecom/Conference.java
@@ -101,7 +101,7 @@ public abstract class Conference extends Conferenceable {
private Set<String> mPreviousExtraKeys;
private final Object mExtrasLock = new Object();
private Uri mAddress;
- private int mAddressPresentation;
+ private int mAddressPresentation = TelecomManager.PRESENTATION_UNKNOWN;
private String mCallerDisplayName;
private int mCallerDisplayNamePresentation;
private int mCallDirection;
diff --git a/telecomm/java/android/telecom/Connection.java b/telecomm/java/android/telecom/Connection.java
index ad7d9870ca98..29d394201f39 100644
--- a/telecomm/java/android/telecom/Connection.java
+++ b/telecomm/java/android/telecom/Connection.java
@@ -2164,7 +2164,7 @@ public abstract class Connection extends Conferenceable {
private CallAudioState mCallAudioState;
private CallEndpoint mCallEndpoint;
private Uri mAddress;
- private int mAddressPresentation;
+ private int mAddressPresentation = TelecomManager.PRESENTATION_UNKNOWN;
private String mCallerDisplayName;
private int mCallerDisplayNamePresentation;
private boolean mRingbackRequested = false;
diff --git a/telephony/java/android/telephony/TelephonyManager.java b/telephony/java/android/telephony/TelephonyManager.java
index a7fe0cb0940c..fad59f8bb37b 100644
--- a/telephony/java/android/telephony/TelephonyManager.java
+++ b/telephony/java/android/telephony/TelephonyManager.java
@@ -17278,6 +17278,18 @@ public class TelephonyManager {
}
/**
+ * Setup sISms for testing.
+ *
+ * @hide
+ */
+ @VisibleForTesting
+ public static void setupISmsForTest(ISms iSms) {
+ synchronized (sCacheLock) {
+ sISms = iSms;
+ }
+ }
+
+ /**
* Whether device can connect to 5G network when two SIMs are active.
*
* @hide TODO b/153669716: remove or make system API.