aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorAndroid Build Coastguard Worker <android-build-coastguard-worker@google.com>2022-05-10 07:23:56 +0000
committerAndroid Build Coastguard Worker <android-build-coastguard-worker@google.com>2022-05-10 07:23:56 +0000
commitd4093da9e56086798dd7b7dc26ab3dd0105f453f (patch)
tree04a073792c5343b320d1a8a3898e231b113f84a0
parentbffe93db853aa434327ac5fa58eddd6c0a362dd9 (diff)
parentf08abef7ba949e37b2c3e378e7f442497358c2b5 (diff)
downloadlayoutlib-aml_sta_331610000.tar.gz
Change-Id: Iacfc81d6f3d787038eddbb021b523881cd845b9e
-rw-r--r--.idea/inspectionProfiles/profiles_settings.xml7
-rw-r--r--.idea/libraries/framework_jar.xml1
-rw-r--r--.idea/misc.xml14
-rw-r--r--.idea/modules.xml1
-rw-r--r--.idea/runConfigurations/All_in_bridge.xml1
-rw-r--r--.idea/runConfigurations/All_in_create.xml17
-rw-r--r--.idea/runConfigurations/Bridge_quick.xml7
-rw-r--r--.idea/runConfigurations/Create.xml3
-rw-r--r--.idea/vcs.xml37
-rw-r--r--Android.bp52
-rw-r--r--METADATA16
-rw-r--r--bridge/Android.bp13
-rw-r--r--bridge/bridge.iml25
-rw-r--r--bridge/resources/icons/shadow-b.pngbin215 -> 0 bytes
-rw-r--r--bridge/resources/icons/shadow-bl.pngbin397 -> 0 bytes
-rw-r--r--bridge/resources/icons/shadow-br.pngbin406 -> 0 bytes
-rw-r--r--bridge/resources/icons/shadow-l.pngbin120 -> 0 bytes
-rw-r--r--bridge/resources/icons/shadow-r.pngbin207 -> 0 bytes
-rw-r--r--bridge/resources/icons/shadow-tl.pngbin277 -> 0 bytes
-rw-r--r--bridge/resources/icons/shadow-tr.pngbin397 -> 0 bytes
-rw-r--r--bridge/resources/icons/shadow2-b.pngbin195 -> 0 bytes
-rw-r--r--bridge/resources/icons/shadow2-bl.pngbin277 -> 0 bytes
-rw-r--r--bridge/resources/icons/shadow2-br.pngbin282 -> 0 bytes
-rw-r--r--bridge/resources/icons/shadow2-l.pngbin108 -> 0 bytes
-rw-r--r--bridge/resources/icons/shadow2-r.pngbin192 -> 0 bytes
-rw-r--r--bridge/resources/icons/shadow2-tl.pngbin2855 -> 0 bytes
-rw-r--r--bridge/resources/icons/shadow2-tr.pngbin286 -> 0 bytes
-rw-r--r--bridge/src/android/animation/AnimationThread.java176
-rw-r--r--bridge/src/android/animation/PropertyValuesHolder_Delegate.java204
-rw-r--r--bridge/src/android/app/ActivityThread_Delegate.java27
-rw-r--r--bridge/src/android/content/res/AssetManager_Delegate.java19
-rw-r--r--bridge/src/android/content/res/BridgeTypedArray.java24
-rw-r--r--bridge/src/android/content/res/Resources_Delegate.java104
-rw-r--r--bridge/src/android/graphics/BaseCanvas_Delegate.java815
-rw-r--r--bridge/src/android/graphics/BidiRenderer.java379
-rw-r--r--bridge/src/android/graphics/BitmapFactory_Delegate.java161
-rw-r--r--bridge/src/android/graphics/BitmapShader_Delegate.java259
-rw-r--r--bridge/src/android/graphics/Bitmap_Delegate.java760
-rw-r--r--bridge/src/android/graphics/BlendComposite.java287
-rw-r--r--bridge/src/android/graphics/BlendModeColorFilter_Delegate.java52
-rw-r--r--bridge/src/android/graphics/BlurMaskFilter_Delegate.java64
-rw-r--r--bridge/src/android/graphics/Canvas_Delegate.java473
-rw-r--r--bridge/src/android/graphics/ColorFilter_Delegate.java83
-rw-r--r--bridge/src/android/graphics/ColorMatrixColorFilter_Delegate.java59
-rw-r--r--bridge/src/android/graphics/ColorSpace_Rgb_Delegate.java54
-rw-r--r--bridge/src/android/graphics/Color_Delegate.java44
-rw-r--r--bridge/src/android/graphics/ComposePathEffect_Delegate.java71
-rw-r--r--bridge/src/android/graphics/ComposeShader_Delegate.java79
-rw-r--r--bridge/src/android/graphics/CornerPathEffect_Delegate.java71
-rw-r--r--bridge/src/android/graphics/DashPathEffect_Delegate.java89
-rw-r--r--bridge/src/android/graphics/DiscretePathEffect_Delegate.java71
-rw-r--r--bridge/src/android/graphics/DrawFilter_Delegate.java64
-rw-r--r--bridge/src/android/graphics/EmbossMaskFilter_Delegate.java65
-rw-r--r--bridge/src/android/graphics/FontFamily_Delegate.java467
-rw-r--r--bridge/src/android/graphics/Gradient_Delegate.java228
-rw-r--r--bridge/src/android/graphics/HardwareRenderer_ProcessInitializer_Delegate.java26
-rw-r--r--bridge/src/android/graphics/ImageDecoder.java768
-rw-r--r--bridge/src/android/graphics/ImageDecoder_Delegate.java62
-rw-r--r--bridge/src/android/graphics/LayoutlibRenderer.java48
-rw-r--r--bridge/src/android/graphics/LightingColorFilter_Delegate.java59
-rw-r--r--bridge/src/android/graphics/LinearGradient_Delegate.java218
-rw-r--r--bridge/src/android/graphics/MaskFilter_Delegate.java64
-rw-r--r--bridge/src/android/graphics/Matrix_Delegate.java1084
-rw-r--r--bridge/src/android/graphics/NinePatch_Delegate.java181
-rw-r--r--bridge/src/android/graphics/PaintFlagsDrawFilter_Delegate.java64
-rw-r--r--bridge/src/android/graphics/Paint_Delegate.java1373
-rw-r--r--bridge/src/android/graphics/PathDashPathEffect_Delegate.java72
-rw-r--r--bridge/src/android/graphics/PathEffect_Delegate.java69
-rw-r--r--bridge/src/android/graphics/PathMeasure_Delegate.java222
-rw-r--r--bridge/src/android/graphics/Path_Delegate.java896
-rw-r--r--bridge/src/android/graphics/PorterDuffColorFilter_Delegate.java124
-rw-r--r--bridge/src/android/graphics/RadialGradient_Delegate.java198
-rw-r--r--bridge/src/android/graphics/Region_Delegate.java483
-rw-r--r--bridge/src/android/graphics/RenderNode_Delegate.java333
-rw-r--r--bridge/src/android/graphics/RoundRectangle.java368
-rw-r--r--bridge/src/android/graphics/Shader_Delegate.java118
-rw-r--r--bridge/src/android/graphics/SumPathEffect_Delegate.java71
-rw-r--r--bridge/src/android/graphics/SweepGradient_Delegate.java208
-rw-r--r--bridge/src/android/graphics/Typeface_Delegate.java322
-rw-r--r--bridge/src/android/graphics/animation/NativeInterpolatorFactory_Delegate.java140
-rw-r--r--bridge/src/android/graphics/drawable/AnimatedVectorDrawable_Delegate.java289
-rw-r--r--bridge/src/android/graphics/drawable/AnimatedVectorDrawable_VectorDrawableAnimatorUI_Delegate.java46
-rw-r--r--bridge/src/android/graphics/drawable/GradientDrawable_Delegate.java73
-rw-r--r--bridge/src/android/graphics/drawable/VectorDrawable_Delegate.java1299
-rw-r--r--bridge/src/android/graphics/fonts/FontFamily_Builder_Delegate.java210
-rw-r--r--bridge/src/android/graphics/fonts/Font_Builder_Delegate.java49
-rw-r--r--bridge/src/android/graphics/fonts/SystemFonts_Delegate.java58
-rw-r--r--bridge/src/android/graphics/text/BaseLineBreaker.java53
-rw-r--r--bridge/src/android/graphics/text/GreedyLineBreaker.java166
-rw-r--r--bridge/src/android/graphics/text/LineBreaker_Delegate.java263
-rw-r--r--bridge/src/android/graphics/text/LineWidth.java35
-rw-r--r--bridge/src/android/graphics/text/MeasuredText_Builder_Delegate.java146
-rw-r--r--bridge/src/android/graphics/text/MeasuredText_Delegate.java93
-rw-r--r--bridge/src/android/graphics/text/OptimizingLineBreaker.java250
-rw-r--r--bridge/src/android/graphics/text/Primitive.java92
-rw-r--r--bridge/src/android/graphics/text/TabStops.java44
-rw-r--r--bridge/src/android/hardware/display/DisplayManagerGlobal.java204
-rw-r--r--bridge/src/android/media/AudioManager.java616
-rw-r--r--bridge/src/android/os/HandlerThread_Delegate.java2
-rw-r--r--bridge/src/android/os/Handler_Delegate.java75
-rw-r--r--bridge/src/android/os/SystemClock_Delegate.java4
-rw-r--r--bridge/src/android/os/SystemProperties_Delegate.java141
-rw-r--r--bridge/src/android/preference/BridgePreferenceInflater.java8
-rw-r--r--bridge/src/android/util/Log_Delegate.java51
-rw-r--r--bridge/src/android/util/PathParser_Delegate.java846
-rw-r--r--bridge/src/android/util/imagepool/Bucket.java76
-rw-r--r--bridge/src/android/util/imagepool/ImageImpl.java90
-rw-r--r--bridge/src/android/util/imagepool/ImagePool.java104
-rw-r--r--bridge/src/android/util/imagepool/ImagePoolHelper.java144
-rw-r--r--bridge/src/android/util/imagepool/ImagePoolImpl.java158
-rw-r--r--bridge/src/android/util/imagepool/ImagePoolProvider.java40
-rw-r--r--bridge/src/android/util/imagepool/ImagePoolStats.java44
-rw-r--r--bridge/src/android/util/imagepool/ImagePoolStatsDebugImpl.java192
-rw-r--r--bridge/src/android/util/imagepool/ImagePoolStatsProdImpl.java69
-rw-r--r--bridge/src/android/view/AttachInfo_Accessor.java38
-rw-r--r--bridge/src/android/view/BridgeInflater.java6
-rw-r--r--bridge/src/android/view/Choreographer_Delegate.java122
-rw-r--r--bridge/src/android/view/DisplayEventReceiver_VsyncEventData_Accessor.java33
-rw-r--r--bridge/src/android/view/RectShadowPainter.java205
-rw-r--r--bridge/src/android/view/ShadowPainter.java424
-rw-r--r--bridge/src/android/view/TextureView_Delegate.java31
-rw-r--r--bridge/src/android/view/ViewGroup_Delegate.java169
-rw-r--r--bridge/src/android/view/WindowManagerImpl.java119
-rw-r--r--bridge/src/android/view/math/Math3DHelper.java75
-rw-r--r--bridge/src/android/view/shadow/AmbientShadowConfig.java112
-rw-r--r--bridge/src/android/view/shadow/AmbientShadowTriangulator.java64
-rw-r--r--bridge/src/android/view/shadow/AmbientShadowVertexCalculator.java111
-rw-r--r--bridge/src/android/view/shadow/HighQualityShadowPainter.java334
-rw-r--r--bridge/src/android/view/shadow/ShadowConstants.java46
-rw-r--r--bridge/src/android/view/shadow/SpotShadowConfig.java137
-rw-r--r--bridge/src/android/view/shadow/SpotShadowTriangulator.java75
-rw-r--r--bridge/src/android/view/shadow/SpotShadowVertexCalculator.java195
-rw-r--r--bridge/src/android/view/shadow/TriangleBuffer.java265
-rw-r--r--bridge/src/android/widget/RemoteViews_Delegate.java (renamed from bridge/src/android/graphics/drawable/AnimatedVectorDrawable_VectorDrawableAnimatorRT_Delegate.java)18
-rw-r--r--bridge/src/com/android/internal/lang/System_Delegate.java (renamed from create/src/com/android/tools/layoutlib/java/System_Delegate.java)45
-rw-r--r--bridge/src/com/android/internal/util/ArrayUtils_Delegate.java27
-rw-r--r--bridge/src/com/android/internal/util/VirtualRefBasePtr_Delegate.java53
-rw-r--r--bridge/src/com/android/layoutlib/bridge/Bridge.java211
-rw-r--r--bridge/src/com/android/layoutlib/bridge/BridgeRenderSession.java85
-rw-r--r--bridge/src/com/android/layoutlib/bridge/MockView.java6
-rw-r--r--bridge/src/com/android/layoutlib/bridge/SessionInteractiveData.java45
-rw-r--r--bridge/src/com/android/layoutlib/bridge/android/AndroidLocale.java17
-rw-r--r--bridge/src/com/android/layoutlib/bridge/android/BridgeContext.java79
-rw-r--r--bridge/src/com/android/layoutlib/bridge/android/BridgePackageManager.java2
-rw-r--r--bridge/src/com/android/layoutlib/bridge/android/BridgePowerManager.java35
-rw-r--r--bridge/src/com/android/layoutlib/bridge/android/BridgeXmlBlockParser.java7
-rw-r--r--bridge/src/com/android/layoutlib/bridge/android/RenderParamsFlags.java29
-rw-r--r--bridge/src/com/android/layoutlib/bridge/android/graphics/NopCanvas.java7
-rw-r--r--bridge/src/com/android/layoutlib/bridge/android/support/DesignLibUtil.java6
-rw-r--r--bridge/src/com/android/layoutlib/bridge/android/support/DrawerLayoutUtil.java8
-rw-r--r--bridge/src/com/android/layoutlib/bridge/android/support/FragmentTabHostUtil.java8
-rw-r--r--bridge/src/com/android/layoutlib/bridge/android/support/RecyclerViewUtil.java31
-rw-r--r--bridge/src/com/android/layoutlib/bridge/android/support/SupportPreferencesUtil.java31
-rw-r--r--bridge/src/com/android/layoutlib/bridge/bars/Config.java7
-rw-r--r--bridge/src/com/android/layoutlib/bridge/bars/CustomBar.java14
-rw-r--r--bridge/src/com/android/layoutlib/bridge/impl/DelegateManager.java104
-rw-r--r--bridge/src/com/android/layoutlib/bridge/impl/GcSnapshot.java925
-rw-r--r--bridge/src/com/android/layoutlib/bridge/impl/Layout.java5
-rw-r--r--bridge/src/com/android/layoutlib/bridge/impl/LayoutParserWrapper.java12
-rw-r--r--bridge/src/com/android/layoutlib/bridge/impl/PlayAnimationThread.java49
-rw-r--r--bridge/src/com/android/layoutlib/bridge/impl/PorterDuffUtility.java107
-rw-r--r--bridge/src/com/android/layoutlib/bridge/impl/RenderAction.java73
-rw-r--r--bridge/src/com/android/layoutlib/bridge/impl/RenderDrawable.java15
-rw-r--r--bridge/src/com/android/layoutlib/bridge/impl/RenderSessionImpl.java409
-rw-r--r--bridge/src/com/android/layoutlib/bridge/impl/ResourceHelper.java325
-rw-r--r--bridge/src/com/android/layoutlib/bridge/impl/binding/AdapterHelper.java8
-rw-r--r--bridge/src/com/android/layoutlib/bridge/impl/binding/FakeAdapter.java6
-rw-r--r--bridge/src/com/android/layoutlib/bridge/impl/binding/FakeExpandableAdapter.java10
-rw-r--r--bridge/src/com/android/layoutlib/bridge/libcore/util/Cleaner.java9
-rw-r--r--bridge/src/com/android/layoutlib/bridge/resources/SysUiResources.java13
-rw-r--r--bridge/src/com/android/layoutlib/bridge/util/CachedPathIteratorFactory.java485
-rw-r--r--bridge/src/com/android/layoutlib/bridge/util/ChoreographerCallbacks.java112
-rw-r--r--bridge/src/com/android/layoutlib/bridge/util/DynamicIdMap.java4
-rw-r--r--bridge/src/com/android/layoutlib/bridge/util/HandlerMessageQueue.java110
-rw-r--r--bridge/src/com/android/layoutlib/bridge/util/NinePatchInputStream.java10
-rw-r--r--bridge/src/dalvik/system/VMRuntime_Delegate.java51
-rw-r--r--bridge/src/libcore/util/NativeAllocationRegistry_Delegate.java20
-rw-r--r--bridge/tests/Android.bp1
-rw-r--r--bridge/tests/res/com/android/layoutlib/testdata/compiled.9.pngbin0 -> 1082 bytes
-rw-r--r--bridge/tests/res/com/android/layoutlib/testdata/non_compiled.9.pngbin0 -> 3750 bytes
-rw-r--r--bridge/tests/res/testApp/MyApplication/build/intermediates/javac/debug/compileDebugJavaWithJavac/classes/com/android/layoutlib/test/myapplication/R$drawable.classbin969 -> 1171 bytes
-rw-r--r--bridge/tests/res/testApp/MyApplication/build/intermediates/javac/debug/compileDebugJavaWithJavac/classes/com/android/layoutlib/test/myapplication/R$font.classbin525 -> 525 bytes
-rw-r--r--bridge/tests/res/testApp/MyApplication/build/intermediates/javac/debug/compileDebugJavaWithJavac/classes/com/android/layoutlib/test/myapplication/R$id.classbin2072 -> 2360 bytes
-rw-r--r--bridge/tests/res/testApp/MyApplication/build/intermediates/javac/debug/compileDebugJavaWithJavac/classes/com/android/layoutlib/test/myapplication/R$integer.classbin492 -> 492 bytes
-rw-r--r--bridge/tests/res/testApp/MyApplication/build/intermediates/javac/debug/compileDebugJavaWithJavac/classes/com/android/layoutlib/test/myapplication/R$layout.classbin932 -> 1193 bytes
-rw-r--r--bridge/tests/res/testApp/MyApplication/build/intermediates/javac/debug/compileDebugJavaWithJavac/classes/com/android/layoutlib/test/myapplication/R$menu.classbin452 -> 452 bytes
-rw-r--r--bridge/tests/res/testApp/MyApplication/build/intermediates/javac/debug/compileDebugJavaWithJavac/classes/com/android/layoutlib/test/myapplication/R$string.classbin538 -> 572 bytes
-rw-r--r--bridge/tests/res/testApp/MyApplication/build/intermediates/javac/debug/compileDebugJavaWithJavac/classes/com/android/layoutlib/test/myapplication/R$style.classbin504 -> 504 bytes
-rw-r--r--bridge/tests/res/testApp/MyApplication/build/intermediates/javac/debug/compileDebugJavaWithJavac/classes/com/android/layoutlib/test/myapplication/ThemableWidget.classbin1999 -> 1999 bytes
-rw-r--r--bridge/tests/res/testApp/MyApplication/golden-mac/a11y_test1.pngbin0 -> 12184 bytes
-rw-r--r--bridge/tests/res/testApp/MyApplication/golden-mac/activity.pngbin0 -> 89971 bytes
-rw-r--r--bridge/tests/res/testApp/MyApplication/golden-mac/adaptive_icon.pngbin0 -> 23469 bytes
-rw-r--r--bridge/tests/res/testApp/MyApplication/golden-mac/adaptive_icon_circle.pngbin0 -> 26151 bytes
-rw-r--r--bridge/tests/res/testApp/MyApplication/golden-mac/adaptive_icon_rounded_corners.pngbin0 -> 24229 bytes
-rw-r--r--bridge/tests/res/testApp/MyApplication/golden-mac/adaptive_icon_squircle.pngbin0 -> 26816 bytes
-rw-r--r--bridge/tests/res/testApp/MyApplication/golden-mac/allwidgets.pngbin0 -> 161678 bytes
-rw-r--r--bridge/tests/res/testApp/MyApplication/golden-mac/allwidgets_tab.pngbin0 -> 62949 bytes
-rw-r--r--bridge/tests/res/testApp/MyApplication/golden-mac/animated_vector.pngbin0 -> 42362 bytes
-rw-r--r--bridge/tests/res/testApp/MyApplication/golden-mac/animated_vector_1.pngbin0 -> 37224 bytes
-rw-r--r--bridge/tests/res/testApp/MyApplication/golden-mac/array_check.pngbin0 -> 57970 bytes
-rw-r--r--bridge/tests/res/testApp/MyApplication/golden-mac/asset.pngbin0 -> 415049 bytes
-rw-r--r--bridge/tests/res/testApp/MyApplication/golden-mac/auto-scale-image.pngbin0 -> 2020 bytes
-rw-r--r--bridge/tests/res/testApp/MyApplication/golden-mac/button_resize.pngbin0 -> 2506 bytes
-rw-r--r--bridge/tests/res/testApp/MyApplication/golden-mac/button_resize2.pngbin0 -> 3055 bytes
-rw-r--r--bridge/tests/res/testApp/MyApplication/golden-mac/canvas.pngbin0 -> 23045 bytes
-rw-r--r--bridge/tests/res/testApp/MyApplication/golden-mac/color_interpolation.pngbin0 -> 39748 bytes
-rw-r--r--bridge/tests/res/testApp/MyApplication/golden-mac/context_theme_wrapper.pngbin0 -> 19874 bytes
-rw-r--r--bridge/tests/res/testApp/MyApplication/golden-mac/dialog.pngbin0 -> 33533 bytes
-rw-r--r--bridge/tests/res/testApp/MyApplication/golden-mac/expand_horz_layout.pngbin0 -> 9636 bytes
-rw-r--r--bridge/tests/res/testApp/MyApplication/golden-mac/expand_vert_layout.pngbin0 -> 16866 bytes
-rw-r--r--bridge/tests/res/testApp/MyApplication/golden-mac/font_test.pngbin0 -> 76885 bytes
-rw-r--r--bridge/tests/res/testApp/MyApplication/golden-mac/four_corners.pngbin0 -> 50376 bytes
-rw-r--r--bridge/tests/res/testApp/MyApplication/golden-mac/four_corners_translucent.pngbin0 -> 49252 bytes
-rw-r--r--bridge/tests/res/testApp/MyApplication/golden-mac/four_corners_translucent_land.pngbin0 -> 55566 bytes
-rw-r--r--bridge/tests/res/testApp/MyApplication/golden-mac/gradient_alpha_drawable.pngbin0 -> 32405 bytes
-rw-r--r--bridge/tests/res/testApp/MyApplication/golden-mac/gradient_colors.pngbin0 -> 35356 bytes
-rw-r--r--bridge/tests/res/testApp/MyApplication/golden-mac/justified_inter_word.pngbin0 -> 258544 bytes
-rw-r--r--bridge/tests/res/testApp/MyApplication/golden-mac/justified_none.pngbin0 -> 254039 bytes
-rw-r--r--bridge/tests/res/testApp/MyApplication/golden-mac/large_shadows_test.pngbin0 -> 65364 bytes
-rw-r--r--bridge/tests/res/testApp/MyApplication/golden-mac/many_line_breaks.pngbin0 -> 85776 bytes
-rw-r--r--bridge/tests/res/testApp/MyApplication/golden-mac/ninepatch_background.pngbin0 -> 32383 bytes
-rw-r--r--bridge/tests/res/testApp/MyApplication/golden-mac/ninepatch_drawable.pngbin0 -> 43986 bytes
-rw-r--r--bridge/tests/res/testApp/MyApplication/golden-mac/non-styled_resources.pngbin0 -> 58207 bytes
-rw-r--r--bridge/tests/res/testApp/MyApplication/golden-mac/normal_layout.pngbin0 -> 26120 bytes
-rw-r--r--bridge/tests/res/testApp/MyApplication/golden-mac/ondraw_crash.pngbin0 -> 22251 bytes
-rw-r--r--bridge/tests/res/testApp/MyApplication/golden-mac/onmeasure_crash.pngbin0 -> 20098 bytes
-rw-r--r--bridge/tests/res/testApp/MyApplication/golden-mac/render_effect.pngbin0 -> 98036 bytes
-rw-r--r--bridge/tests/res/testApp/MyApplication/golden-mac/rtl_ltr.pngbin0 -> 51066 bytes
-rw-r--r--bridge/tests/res/testApp/MyApplication/golden-mac/scrolled.pngbin0 -> 15309 bytes
-rw-r--r--bridge/tests/res/testApp/MyApplication/golden-mac/shadow_scrollview_test.pngbin0 -> 94883 bytes
-rw-r--r--bridge/tests/res/testApp/MyApplication/golden-mac/shadow_sizes_test.pngbin0 -> 143877 bytes
-rw-r--r--bridge/tests/res/testApp/MyApplication/golden-mac/shadows_test.pngbin0 -> 124440 bytes
-rw-r--r--bridge/tests/res/testApp/MyApplication/golden-mac/shadows_test_rounded_edge.pngbin0 -> 113318 bytes
-rw-r--r--bridge/tests/res/testApp/MyApplication/golden-mac/shrunk_layout.pngbin0 -> 14436 bytes
-rw-r--r--bridge/tests/res/testApp/MyApplication/golden-mac/simple_activity-old-theme.pngbin0 -> 28761 bytes
-rw-r--r--bridge/tests/res/testApp/MyApplication/golden-mac/simple_activity.pngbin0 -> 26935 bytes
-rw-r--r--bridge/tests/res/testApp/MyApplication/golden-mac/simple_activity_noactionbar.pngbin0 -> 26769 bytes
-rw-r--r--bridge/tests/res/testApp/MyApplication/golden-mac/textclock.pngbin0 -> 15383 bytes
-rw-r--r--bridge/tests/res/testApp/MyApplication/golden-mac/translate_test.pngbin0 -> 13779 bytes
-rw-r--r--bridge/tests/res/testApp/MyApplication/golden-mac/transparent_drawable.pngbin0 -> 11683 bytes
-rw-r--r--bridge/tests/res/testApp/MyApplication/golden-mac/typed_arrays.pngbin0 -> 54684 bytes
-rw-r--r--bridge/tests/res/testApp/MyApplication/golden-mac/vector_drawable.pngbin0 -> 38997 bytes
-rw-r--r--bridge/tests/res/testApp/MyApplication/golden-mac/vector_drawable_91383.pngbin0 -> 34923 bytes
-rw-r--r--bridge/tests/res/testApp/MyApplication/golden-mac/vector_drawable_gradient.pngbin0 -> 37498 bytes
-rw-r--r--bridge/tests/res/testApp/MyApplication/golden-mac/vector_drawable_radial_gradient.pngbin0 -> 61757 bytes
-rw-r--r--bridge/tests/res/testApp/MyApplication/golden-mac/vector_drawable_with_tint_in_image_view.pngbin0 -> 82791 bytes
-rw-r--r--bridge/tests/res/testApp/MyApplication/golden-mac/vector_drawable_with_tint_itself.pngbin0 -> 43991 bytes
-rw-r--r--bridge/tests/res/testApp/MyApplication/golden-mac/view_boundaries.pngbin0 -> 23079 bytes
-rw-r--r--bridge/tests/res/testApp/MyApplication/golden-mac/view_stub.pngbin0 -> 41024 bytes
-rw-r--r--bridge/tests/res/testApp/MyApplication/golden-mac/window_background.pngbin0 -> 15591 bytes
-rw-r--r--bridge/tests/res/testApp/MyApplication/golden/activity.pngbin41147 -> 89971 bytes
-rw-r--r--bridge/tests/res/testApp/MyApplication/golden/adaptive_icon.pngbin8900 -> 23469 bytes
-rw-r--r--bridge/tests/res/testApp/MyApplication/golden/adaptive_icon_circle.pngbin10231 -> 26151 bytes
-rw-r--r--bridge/tests/res/testApp/MyApplication/golden/adaptive_icon_rounded_corners.pngbin9241 -> 24229 bytes
-rw-r--r--bridge/tests/res/testApp/MyApplication/golden/adaptive_icon_squircle.pngbin10555 -> 26816 bytes
-rw-r--r--bridge/tests/res/testApp/MyApplication/golden/allwidgets.pngbin72502 -> 161678 bytes
-rw-r--r--bridge/tests/res/testApp/MyApplication/golden/allwidgets_tab.pngbin52561 -> 62949 bytes
-rw-r--r--bridge/tests/res/testApp/MyApplication/golden/animated_vector.pngbin18856 -> 42362 bytes
-rw-r--r--bridge/tests/res/testApp/MyApplication/golden/animated_vector_1.pngbin16404 -> 37224 bytes
-rw-r--r--bridge/tests/res/testApp/MyApplication/golden/array_check.pngbin26407 -> 57970 bytes
-rw-r--r--bridge/tests/res/testApp/MyApplication/golden/asset.pngbin103248 -> 415049 bytes
-rw-r--r--bridge/tests/res/testApp/MyApplication/golden/auto-scale-image.pngbin29860 -> 2020 bytes
-rw-r--r--bridge/tests/res/testApp/MyApplication/golden/button_resize.pngbin0 -> 2506 bytes
-rw-r--r--bridge/tests/res/testApp/MyApplication/golden/button_resize2.pngbin0 -> 3055 bytes
-rw-r--r--bridge/tests/res/testApp/MyApplication/golden/canvas.pngbin8609 -> 23021 bytes
-rw-r--r--bridge/tests/res/testApp/MyApplication/golden/color_interpolation.pngbin12667 -> 39748 bytes
-rw-r--r--bridge/tests/res/testApp/MyApplication/golden/context_theme_wrapper.pngbin7700 -> 19874 bytes
-rw-r--r--bridge/tests/res/testApp/MyApplication/golden/dialog.pngbin0 -> 33533 bytes
-rw-r--r--bridge/tests/res/testApp/MyApplication/golden/expand_horz_layout.pngbin3306 -> 9636 bytes
-rw-r--r--bridge/tests/res/testApp/MyApplication/golden/expand_vert_layout.pngbin4454 -> 16866 bytes
-rw-r--r--bridge/tests/res/testApp/MyApplication/golden/font_test.pngbin54379 -> 76885 bytes
-rw-r--r--bridge/tests/res/testApp/MyApplication/golden/four_corners.pngbin23616 -> 50376 bytes
-rw-r--r--bridge/tests/res/testApp/MyApplication/golden/four_corners_translucent.pngbin22865 -> 49252 bytes
-rw-r--r--bridge/tests/res/testApp/MyApplication/golden/four_corners_translucent_land.pngbin24859 -> 55566 bytes
-rw-r--r--bridge/tests/res/testApp/MyApplication/golden/gradient_alpha_drawable.pngbin14860 -> 32405 bytes
-rw-r--r--bridge/tests/res/testApp/MyApplication/golden/gradient_colors.pngbin14777 -> 35356 bytes
-rw-r--r--bridge/tests/res/testApp/MyApplication/golden/justified_inter_word.pngbin0 -> 258544 bytes
-rw-r--r--bridge/tests/res/testApp/MyApplication/golden/justified_none.pngbin0 -> 254039 bytes
-rw-r--r--bridge/tests/res/testApp/MyApplication/golden/large_shadows_test.pngbin0 -> 65364 bytes
-rw-r--r--bridge/tests/res/testApp/MyApplication/golden/large_shadows_test_high_quality.pngbin12100 -> 0 bytes
-rw-r--r--bridge/tests/res/testApp/MyApplication/golden/many_line_breaks.pngbin131980 -> 85776 bytes
-rw-r--r--bridge/tests/res/testApp/MyApplication/golden/ninepatch_background.pngbin12706 -> 32383 bytes
-rw-r--r--bridge/tests/res/testApp/MyApplication/golden/ninepatch_drawable.pngbin0 -> 43986 bytes
-rw-r--r--bridge/tests/res/testApp/MyApplication/golden/non-styled_resources.pngbin0 -> 58207 bytes
-rw-r--r--bridge/tests/res/testApp/MyApplication/golden/normal_layout.pngbin4804 -> 26120 bytes
-rw-r--r--bridge/tests/res/testApp/MyApplication/golden/ondraw_crash.pngbin9101 -> 22251 bytes
-rw-r--r--bridge/tests/res/testApp/MyApplication/golden/onmeasure_crash.pngbin7931 -> 20098 bytes
-rw-r--r--bridge/tests/res/testApp/MyApplication/golden/render_effect.pngbin0 -> 98036 bytes
-rw-r--r--bridge/tests/res/testApp/MyApplication/golden/rtl_ltr.pngbin27790 -> 51076 bytes
-rw-r--r--bridge/tests/res/testApp/MyApplication/golden/scrolled.pngbin5954 -> 15309 bytes
-rw-r--r--bridge/tests/res/testApp/MyApplication/golden/shadow_scrollview_test.pngbin0 -> 94874 bytes
-rw-r--r--bridge/tests/res/testApp/MyApplication/golden/shadow_scrollview_test_high_quality.pngbin25934 -> 0 bytes
-rw-r--r--bridge/tests/res/testApp/MyApplication/golden/shadow_sizes_test.pngbin0 -> 151331 bytes
-rw-r--r--bridge/tests/res/testApp/MyApplication/golden/shadow_sizes_test_high_quality.pngbin25309 -> 0 bytes
-rw-r--r--bridge/tests/res/testApp/MyApplication/golden/shadows_test.pngbin29571 -> 124440 bytes
-rw-r--r--bridge/tests/res/testApp/MyApplication/golden/shadows_test_high_quality.pngbin18991 -> 0 bytes
-rw-r--r--bridge/tests/res/testApp/MyApplication/golden/shadows_test_high_quality_rounded_edge.pngbin34437 -> 0 bytes
-rw-r--r--bridge/tests/res/testApp/MyApplication/golden/shadows_test_no_shadow.pngbin12614 -> 0 bytes
-rw-r--r--bridge/tests/res/testApp/MyApplication/golden/shadows_test_rounded_edge.pngbin0 -> 177804 bytes
-rw-r--r--bridge/tests/res/testApp/MyApplication/golden/shrunk_layout.pngbin3128 -> 14436 bytes
-rw-r--r--bridge/tests/res/testApp/MyApplication/golden/simple_activity-old-theme.pngbin12134 -> 28761 bytes
-rw-r--r--bridge/tests/res/testApp/MyApplication/golden/simple_activity.pngbin10904 -> 26935 bytes
-rw-r--r--bridge/tests/res/testApp/MyApplication/golden/simple_activity_noactionbar.pngbin10832 -> 26769 bytes
-rw-r--r--bridge/tests/res/testApp/MyApplication/golden/textclock.pngbin4970 -> 15383 bytes
-rw-r--r--bridge/tests/res/testApp/MyApplication/golden/translate_test.pngbin5077 -> 13779 bytes
-rw-r--r--bridge/tests/res/testApp/MyApplication/golden/transparent_drawable.pngbin0 -> 11683 bytes
-rw-r--r--bridge/tests/res/testApp/MyApplication/golden/typed_arrays.pngbin23738 -> 54684 bytes
-rw-r--r--bridge/tests/res/testApp/MyApplication/golden/vector_drawable.pngbin23394 -> 38997 bytes
-rw-r--r--bridge/tests/res/testApp/MyApplication/golden/vector_drawable_91383.pngbin16592 -> 34923 bytes
-rw-r--r--bridge/tests/res/testApp/MyApplication/golden/vector_drawable_gradient.pngbin24873 -> 37498 bytes
-rw-r--r--bridge/tests/res/testApp/MyApplication/golden/vector_drawable_multi_line_of_path_data.pngbin14076 -> 0 bytes
-rw-r--r--bridge/tests/res/testApp/MyApplication/golden/vector_drawable_radial_gradient.pngbin26548 -> 61757 bytes
-rw-r--r--bridge/tests/res/testApp/MyApplication/golden/vector_drawable_with_tint_in_image_view.pngbin39435 -> 82791 bytes
-rw-r--r--bridge/tests/res/testApp/MyApplication/golden/vector_drawable_with_tint_itself.pngbin19411 -> 43991 bytes
-rw-r--r--bridge/tests/res/testApp/MyApplication/golden/view_boundaries.pngbin8549 -> 23079 bytes
-rw-r--r--bridge/tests/res/testApp/MyApplication/golden/view_stub.pngbin20120 -> 41024 bytes
-rw-r--r--bridge/tests/res/testApp/MyApplication/golden/window_background.pngbin0 -> 15591 bytes
-rw-r--r--bridge/tests/res/testApp/MyApplication/src/main/myapplication.widgets/DialogView.java59
-rw-r--r--bridge/tests/res/testApp/MyApplication/src/main/res/drawable-nodpi/ninepatch.9.pngbin882 -> 794 bytes
-rw-r--r--bridge/tests/res/testApp/MyApplication/src/main/res/drawable-nodpi/uncompiled_ninepatch.9.pngbin0 -> 882 bytes
-rw-r--r--bridge/tests/res/testApp/MyApplication/src/main/res/drawable/ninepatch_drawable.xml19
-rw-r--r--bridge/tests/res/testApp/MyApplication/src/main/res/drawable/theme_attribute_drawable.xml20
-rw-r--r--bridge/tests/res/testApp/MyApplication/src/main/res/drawable/transparent_drawable.xml24
-rw-r--r--bridge/tests/res/testApp/MyApplication/src/main/res/layout/a11y_scrolling_view_test.xml44
-rw-r--r--bridge/tests/res/testApp/MyApplication/src/main/res/layout/a11y_test_switch_text_contrast.xml34
-rw-r--r--bridge/tests/res/testApp/MyApplication/src/main/res/layout/fonts_test.xml3
-rw-r--r--bridge/tests/res/testApp/MyApplication/src/main/res/layout/justified_inter_word.xml12
-rw-r--r--bridge/tests/res/testApp/MyApplication/src/main/res/layout/justified_none.xml12
-rw-r--r--bridge/tests/res/testApp/MyApplication/src/main/res/layout/layout.xml2
-rw-r--r--bridge/tests/res/testApp/MyApplication/src/main/res/values/strings.xml89
-rw-r--r--bridge/tests/res/testApp/MyApplication/src/main/res/values/styles.xml5
-rwxr-xr-xbridge/tests/run_tests.sh35
-rwxr-xr-xbridge/tests/run_tests_mac.sh82
-rw-r--r--bridge/tests/src/android/content/res/BridgeTypedArrayTest.java2
-rw-r--r--bridge/tests/src/android/graphics/Color_DelegateTest.java37
-rw-r--r--bridge/tests/src/android/graphics/Matrix_DelegateTest.java66
-rw-r--r--bridge/tests/src/android/util/imagepool/ImagePoolHelperTest.java108
-rw-r--r--bridge/tests/src/android/util/imagepool/ImagePoolImplTest.java306
-rw-r--r--bridge/tests/src/com/android/layoutlib/bridge/TestDelegates.java9
-rw-r--r--bridge/tests/src/com/android/layoutlib/bridge/android/BitmapTest.java67
-rw-r--r--bridge/tests/src/com/android/layoutlib/bridge/android/BridgeContextTest.java36
-rw-r--r--bridge/tests/src/com/android/layoutlib/bridge/android/BridgeXmlBlockParserTest.java9
-rw-r--r--bridge/tests/src/com/android/layoutlib/bridge/impl/ResourceHelperTest.java51
-rw-r--r--bridge/tests/src/com/android/layoutlib/bridge/intensive/Main.java17
-rw-r--r--bridge/tests/src/com/android/layoutlib/bridge/intensive/RenderTestBase.java106
-rw-r--r--bridge/tests/src/com/android/layoutlib/bridge/intensive/RenderTests.java371
-rw-r--r--bridge/tests/src/com/android/layoutlib/bridge/intensive/ShadowsRenderTests.java (renamed from bridge/tests/src/com/android/layoutlib/bridge/intensive/HighQualityShadowsRenderTests.java)27
-rw-r--r--bridge/tests/src/com/android/layoutlib/bridge/intensive/setup/LayoutLibTestCallback.java8
-rw-r--r--bridge/tests/src/com/android/layoutlib/bridge/intensive/util/ImageUtils.java138
-rw-r--r--bridge/tests/src/com/android/layoutlib/bridge/intensive/util/ModuleClassLoader.java7
-rw-r--r--bridge/tests/src/com/android/layoutlib/bridge/intensive/util/SessionParamsBuilder.java27
-rw-r--r--bridge/tests/src/com/android/layoutlib/bridge/test/widgets/BlurryImageView.java53
-rw-r--r--bridge/tests/src/com/android/layoutlib/bridge/test/widgets/CustomImageView.java50
-rw-r--r--bridge/tests/src/com/android/layoutlib/bridge/util/ChoreographerCallbacksTest.java107
-rw-r--r--bridge/tests/src/com/android/layoutlib/bridge/util/HandlerMessageQueueTest.java124
-rw-r--r--bridge/tests/src/com/android/tools/idea/validator/AccessibilityValidatorTests.java (renamed from bridge/tests/src/com/android/tools/idea/validator/accessibility/AccessibilityValidatorTests.java)146
-rw-r--r--bridge/tests/src/com/android/tools/idea/validator/LayoutValidatorTests.java75
-rw-r--r--bridge/tests/src/com/android/tools/idea/validator/ValidatorResultTests.java104
-rw-r--r--common/src/com/android/layoutlib/common/util/ReflectionUtils.java (renamed from bridge/src/com/android/layoutlib/bridge/util/ReflectionUtils.java)59
-rw-r--r--common/src/com/android/tools/layoutlib/annotations/NonNull.java34
-rw-r--r--common/src/com/android/tools/layoutlib/create/NativeConfig.java217
-rw-r--r--create/Android.bp14
-rw-r--r--create/create.iml70
-rw-r--r--create/src/com/android/tools/layoutlib/create/AsmAnalyzer.java38
-rw-r--r--create/src/com/android/tools/layoutlib/create/AsmGenerator.java70
-rw-r--r--create/src/com/android/tools/layoutlib/create/CreateInfo.java572
-rw-r--r--create/src/com/android/tools/layoutlib/create/DeferStaticInitializerClassAdapter.java54
-rw-r--r--create/src/com/android/tools/layoutlib/create/DelegateClassAdapter.java19
-rw-r--r--create/src/com/android/tools/layoutlib/create/DelegateMethodAdapter.java19
-rw-r--r--create/src/com/android/tools/layoutlib/create/DelegateToNativeAdapter.java104
-rw-r--r--create/src/com/android/tools/layoutlib/create/ICreateInfo.java57
-rw-r--r--create/src/com/android/tools/layoutlib/create/Main.java22
-rw-r--r--create/src/com/android/tools/layoutlib/create/PromoteMethodClassAdapter.java32
-rw-r--r--create/src/com/android/tools/layoutlib/create/ReplaceMethodCallsAdapter.java208
-rw-r--r--create/src/com/android/tools/layoutlib/create/StaticInitMethodAdapter.java290
-rw-r--r--create/src/com/android/tools/layoutlib/create/StubClassAdapter.java2
-rw-r--r--create/src/com/android/tools/layoutlib/java/NioUtils_Delegate.java15
-rw-r--r--create/src/com/android/tools/layoutlib/java/Reference_Delegate.java29
-rw-r--r--create/tests/Android.bp18
-rw-r--r--create/tests/LICENSE261
-rw-r--r--create/tests/res/data/mock_android.jarbin15405 -> 16389 bytes
-rw-r--r--create/tests/res/mock_data/README.md25
-rw-r--r--create/tests/res/mock_data/mock_android/view/LibLoader.java7
-rwxr-xr-xcreate/tests/run_tests.sh13
-rw-r--r--create/tests/src/com/android/tools/layoutlib/create/AllTests.java34
-rw-r--r--create/tests/src/com/android/tools/layoutlib/create/AsmAnalyzerTest.java8
-rw-r--r--create/tests/src/com/android/tools/layoutlib/create/AsmGeneratorTest.java73
-rw-r--r--create/tests/src/com/android/tools/layoutlib/create/CreateInfoAdapter.java30
-rw-r--r--create/tests/src/com/android/tools/layoutlib/create/DelegateClassAdapterTest.java76
-rw-r--r--create/tests/src/com/android/tools/layoutlib/create/PromoteClassClassAdapterTest.java2
-rw-r--r--create/tests/src/com/android/tools/layoutlib/create/dataclass/ClassWithStaticInit.java28
-rw-r--r--create/tests/src/com/android/tools/layoutlib/create/dataclass/ClassWithStaticInit_Delegate.java35
-rw-r--r--create/tests/src/com/android/tools/layoutlib/create/dataclass/ClassWithStaticInit_InnerClass_Delegate.java36
-rw-r--r--delegates/Android.bp31
-rw-r--r--delegates/README1
-rw-r--r--delegates/delegates.iml14
-rw-r--r--delegates/src/android/graphics/fonts/SystemFonts_Delegate.java94
-rw-r--r--delegates/src/android/media/ImageReader_Delegate.java24
-rw-r--r--delegates/src/com/android/tools/layoutlib/java/nio/Buffer_Delegate.java52
-rw-r--r--delegates/src/com/android/tools/layoutlib/java/nio/NIOAccess_Delegate.java73
-rw-r--r--delegates/src/com/android/tools/layoutlib/java/text/DateFormat_Delegate.java31
-rw-r--r--delegates/src/com/android/tools/layoutlib/java/util/LocaleAdjustLanguageCodeReplacement.java43
-rw-r--r--delegates/src/com/android/tools/layoutlib/java/util/zip/ZipEntry_Delegate.java49
-rw-r--r--delegates/src/dalvik/system/VMRuntimeCommonHelper.java78
-rw-r--r--remote/client/src/com/android/layoutlib/bridge/remote/client/adapters/RemoteLayoutlibCallbackAdapter.java5
-rw-r--r--remote/common/src/com/android/layout/remote/api/RemoteLayoutLog.java6
-rw-r--r--remote/common/src/com/android/layout/remote/api/RemoteLayoutlibCallback.java2
-rw-r--r--remote/server/src/com/android/layoutlib/bridge/remote/server/adapters/RemoteLayoutlibCallbackAdapter.java9
-rw-r--r--rename_font/README9
-rw-r--r--rename_font/Roboto-Regular.ttfbin114976 -> 0 bytes
-rwxr-xr-xrename_font/build_font.py227
-rwxr-xr-xrename_font/build_font_single.py221
-rwxr-xr-xrename_font/test.py45
-rwxr-xr-xsplit_universal_binary.sh37
-rw-r--r--validator/Android.bp1
-rw-r--r--validator/resources/strings.properties220
-rw-r--r--validator/resources/values.xml155
-rw-r--r--validator/src/ResourceConverter.java52
-rw-r--r--validator/src/com/android/tools/idea/validator/AtfBufferedImage.java180
-rw-r--r--validator/src/com/android/tools/idea/validator/LayoutValidator.java104
-rw-r--r--validator/src/com/android/tools/idea/validator/ValidatorData.java126
-rw-r--r--validator/src/com/android/tools/idea/validator/ValidatorHierarchy.java43
-rw-r--r--validator/src/com/android/tools/idea/validator/ValidatorResult.java54
-rw-r--r--validator/src/com/android/tools/idea/validator/ValidatorUtil.java377
-rw-r--r--validator/src/com/android/tools/idea/validator/accessibility/AccessibilityValidator.java203
-rw-r--r--validator/src/com/android/tools/idea/validator/accessibility/AtfBufferedImage.java98
-rw-r--r--validator/src/com/android/tools/idea/validator/hierarchy/CustomHierarchyHelper.java65
-rw-r--r--validator/src/com/google/android/apps/common/testing/accessibility/framework/uielement/AccessibilityHierarchyAndroid_ViewElementClassNamesAndroid_Delegate.java39
428 files changed, 8378 insertions, 24691 deletions
diff --git a/.idea/inspectionProfiles/profiles_settings.xml b/.idea/inspectionProfiles/profiles_settings.xml
deleted file mode 100644
index 3b312839bf..0000000000
--- a/.idea/inspectionProfiles/profiles_settings.xml
+++ /dev/null
@@ -1,7 +0,0 @@
-<component name="InspectionProjectProfileManager">
- <settings>
- <option name="PROJECT_PROFILE" value="Project Default" />
- <option name="USE_PROJECT_PROFILE" value="true" />
- <version value="1.0" />
- </settings>
-</component> \ No newline at end of file
diff --git a/.idea/libraries/framework_jar.xml b/.idea/libraries/framework_jar.xml
index 2ad7916efe..82af4c51f7 100644
--- a/.idea/libraries/framework_jar.xml
+++ b/.idea/libraries/framework_jar.xml
@@ -8,6 +8,7 @@
<root url="file://$PROJECT_DIR$/../base/core/java" />
<root url="file://$PROJECT_DIR$/../base/graphics/java" />
<root url="file://$PROJECT_DIR$/../../libcore/luni/src/main/java" />
+ <root url="file://$PROJECT_DIR$/../../libcore/dalvik/src/main/java" />
</SOURCES>
</library>
</component> \ No newline at end of file
diff --git a/.idea/misc.xml b/.idea/misc.xml
index 22a20936ac..943a663b39 100644
--- a/.idea/misc.xml
+++ b/.idea/misc.xml
@@ -13,7 +13,7 @@
<option name="myDefaultNotNull" value="android.annotation.NonNull" />
<option name="myNullables">
<value>
- <list size="13">
+ <list size="15">
<item index="0" class="java.lang.String" itemvalue="org.jetbrains.annotations.Nullable" />
<item index="1" class="java.lang.String" itemvalue="javax.annotation.Nullable" />
<item index="2" class="java.lang.String" itemvalue="edu.umd.cs.findbugs.annotations.Nullable" />
@@ -26,13 +26,15 @@
<item index="9" class="java.lang.String" itemvalue="org.checkerframework.checker.nullness.qual.Nullable" />
<item index="10" class="java.lang.String" itemvalue="org.checkerframework.checker.nullness.compatqual.NullableDecl" />
<item index="11" class="java.lang.String" itemvalue="org.checkerframework.checker.nullness.compatqual.NullableType" />
- <item index="12" class="java.lang.String" itemvalue="org.eclipse.jdt.annotation.Nullable" />
+ <item index="12" class="java.lang.String" itemvalue="org.checkerframework.checker.nullness.compatqual.NullableDecl" />
+ <item index="13" class="java.lang.String" itemvalue="org.checkerframework.checker.nullness.compatqual.NullableType" />
+ <item index="14" class="java.lang.String" itemvalue="org.checkerframework.checker.nullness.qual.Nullable" />
</list>
</value>
</option>
<option name="myNotNulls">
<value>
- <list size="12">
+ <list size="14">
<item index="0" class="java.lang.String" itemvalue="org.jetbrains.annotations.NotNull" />
<item index="1" class="java.lang.String" itemvalue="javax.annotation.Nonnull" />
<item index="2" class="java.lang.String" itemvalue="edu.umd.cs.findbugs.annotations.NonNull" />
@@ -44,12 +46,14 @@
<item index="8" class="java.lang.String" itemvalue="org.checkerframework.checker.nullness.qual.NonNull" />
<item index="9" class="java.lang.String" itemvalue="org.checkerframework.checker.nullness.compatqual.NonNullDecl" />
<item index="10" class="java.lang.String" itemvalue="org.checkerframework.checker.nullness.compatqual.NonNullType" />
- <item index="11" class="java.lang.String" itemvalue="org.eclipse.jdt.annotation.NonNull" />
+ <item index="11" class="java.lang.String" itemvalue="org.checkerframework.checker.nullness.compatqual.NonNullDecl" />
+ <item index="12" class="java.lang.String" itemvalue="org.checkerframework.checker.nullness.compatqual.NonNullType" />
+ <item index="13" class="java.lang.String" itemvalue="org.checkerframework.checker.nullness.qual.NonNull" />
</list>
</value>
</option>
</component>
- <component name="ProjectRootManager" version="2" languageLevel="JDK_1_9" default="false" project-jdk-name="11" project-jdk-type="JavaSDK">
+ <component name="ProjectRootManager" version="2" languageLevel="JDK_11" project-jdk-name="11" project-jdk-type="JavaSDK">
<output url="file://$PROJECT_DIR$/out" />
</component>
</project> \ No newline at end of file
diff --git a/.idea/modules.xml b/.idea/modules.xml
index 52102152a7..5b018bb9ec 100644
--- a/.idea/modules.xml
+++ b/.idea/modules.xml
@@ -5,6 +5,7 @@
<module fileurl="file://$PROJECT_DIR$/bridge/bridge.iml" filepath="$PROJECT_DIR$/bridge/bridge.iml" />
<module fileurl="file://$PROJECT_DIR$/common/common.iml" filepath="$PROJECT_DIR$/common/common.iml" />
<module fileurl="file://$PROJECT_DIR$/create/create.iml" filepath="$PROJECT_DIR$/create/create.iml" />
+ <module fileurl="file://$PROJECT_DIR$/delegates/delegates.iml" filepath="$PROJECT_DIR$/delegates/delegates.iml" />
<module fileurl="file://$PROJECT_DIR$/remote/client/remote client.iml" filepath="$PROJECT_DIR$/remote/client/remote client.iml" group="remote" />
<module fileurl="file://$PROJECT_DIR$/remote/common/remote common.iml" filepath="$PROJECT_DIR$/remote/common/remote common.iml" group="remote" />
<module fileurl="file://$PROJECT_DIR$/remote/server/remote server.iml" filepath="$PROJECT_DIR$/remote/server/remote server.iml" group="remote" />
diff --git a/.idea/runConfigurations/All_in_bridge.xml b/.idea/runConfigurations/All_in_bridge.xml
index 1e82efcfe3..e89deb32e0 100644
--- a/.idea/runConfigurations/All_in_bridge.xml
+++ b/.idea/runConfigurations/All_in_bridge.xml
@@ -1,7 +1,6 @@
<component name="ProjectRunConfigurationManager">
<configuration default="false" name="All in bridge" type="JUnit" factoryName="JUnit" singleton="true">
<module name="bridge" />
- <option name="ALTERNATIVE_JRE_PATH_ENABLED" value="true" />
<option name="ALTERNATIVE_JRE_PATH" value="11" />
<option name="PACKAGE_NAME" value="" />
<option name="MAIN_CLASS_NAME" value="" />
diff --git a/.idea/runConfigurations/All_in_create.xml b/.idea/runConfigurations/All_in_create.xml
index b9cd419d24..08ae5b5df0 100644
--- a/.idea/runConfigurations/All_in_create.xml
+++ b/.idea/runConfigurations/All_in_create.xml
@@ -1,23 +1,12 @@
<component name="ProjectRunConfigurationManager">
<configuration default="false" name="All in create" type="JUnit" factoryName="JUnit" singleton="false" nameIsGenerated="true">
- <extension name="coverage" enabled="false" merge="false" sample_coverage="true" runner="idea" />
<module name="create" />
- <option name="ALTERNATIVE_JRE_PATH_ENABLED" value="false" />
- <option name="ALTERNATIVE_JRE_PATH" value="" />
<option name="PACKAGE_NAME" value="" />
<option name="MAIN_CLASS_NAME" value="" />
<option name="METHOD_NAME" value="" />
<option name="TEST_OBJECT" value="package" />
- <option name="VM_PARAMETERS" value="-ea" />
<option name="PARAMETERS" value="" />
- <option name="WORKING_DIRECTORY" value="file://$PROJECT_DIR$" />
- <option name="ENV_VARIABLES" />
- <option name="PASS_PARENT_ENVS" value="true" />
- <option name="TEST_SEARCH_SCOPE">
- <value defaultName="singleModule" />
- </option>
- <envs />
- <patterns />
+ <option name="WORKING_DIRECTORY" value="$PROJECT_DIR$" />
<RunnerSettings RunnerId="Debug">
<option name="DEBUG_PORT" value="" />
<option name="TRANSPORT" value="0" />
@@ -26,6 +15,8 @@
<RunnerSettings RunnerId="Run" />
<ConfigurationWrapper RunnerId="Debug" />
<ConfigurationWrapper RunnerId="Run" />
- <method />
+ <method v="2">
+ <option name="Make" enabled="true" />
+ </method>
</configuration>
</component> \ No newline at end of file
diff --git a/.idea/runConfigurations/Bridge_quick.xml b/.idea/runConfigurations/Bridge_quick.xml
index e9797b0ebf..9ed99d1039 100644
--- a/.idea/runConfigurations/Bridge_quick.xml
+++ b/.idea/runConfigurations/Bridge_quick.xml
@@ -1,8 +1,6 @@
<component name="ProjectRunConfigurationManager">
<configuration default="false" name="Bridge quick" type="JUnit" factoryName="JUnit">
<module name="bridge" />
- <useClassPathOnly />
- <option name="ALTERNATIVE_JRE_PATH_ENABLED" value="true" />
<option name="ALTERNATIVE_JRE_PATH" value="11" />
<option name="MAIN_CLASS_NAME" value="" />
<option name="METHOD_NAME" value="" />
@@ -11,13 +9,10 @@
<option name="WORKING_DIRECTORY" value="$PROJECT_DIR$" />
<patterns>
<pattern testClass="com.android.layoutlib.bridge.TestDelegates" />
- <pattern testClass="android.graphics.Matrix_DelegateTest" />
<pattern testClass="com.android.layoutlib.bridge.android.BridgeXmlBlockParserTest" />
</patterns>
<RunnerSettings RunnerId="Run" />
<ConfigurationWrapper RunnerId="Run" />
- <method v="2">
- <option name="Make" enabled="true" />
- </method>
+ <method v="2" />
</configuration>
</component> \ No newline at end of file
diff --git a/.idea/runConfigurations/Create.xml b/.idea/runConfigurations/Create.xml
index a3fd3a1b56..11a2ae4296 100644
--- a/.idea/runConfigurations/Create.xml
+++ b/.idea/runConfigurations/Create.xml
@@ -1,10 +1,9 @@
<component name="ProjectRunConfigurationManager">
<configuration default="false" name="Create" type="Application" factoryName="Application" singleton="true">
<option name="ALTERNATIVE_JRE_PATH" value="$PROJECT_DIR$/../../prebuilts/jdk/jdk9/linux-x86" />
- <option name="ALTERNATIVE_JRE_PATH_ENABLED" value="true" />
<option name="MAIN_CLASS_NAME" value="com.android.tools.layoutlib.create.Main" />
<module name="create" />
- <option name="PROGRAM_PARAMETERS" value="--create-stub out/host/common/obj/JAVA_LIBRARIES/temp_layoutlib_intermediates/classes.jar out/target/common/obj/JAVA_LIBRARIES/core-libart_intermediates/classes.jar out/target/common/obj/JAVA_LIBRARIES/framework_intermediates/classes.jar out/target/common/obj/JAVA_LIBRARIES/ext_intermediates/classes.jar out/host/common/obj/JAVA_LIBRARIES/icu4j-icudata-jarjar_intermediates/classes.jar out/host/common/obj/JAVA_LIBRARIES/icu4j-icutzdata-jarjar_intermediates/classes.jar" />
+ <option name="PROGRAM_PARAMETERS" value="--create-stub out/soong/.temp/temp_layoutlib.jar out/soong/.intermediates/prebuilts/misc/common/atf/atf-prebuilt-jars-371374941/linux_glibc_common/combined/atf-prebuilt-jars-371374941.jar out/soong/.intermediates/external/icu/android_icu4j/core-icu4j-for-host/android_common/withres/core-icu4j-for-host.jar out/soong/.intermediates/libcore/core-libart/android_common/javac/core-libart.jar out/soong/.intermediates/frameworks/base/framework-all/android_common/combined/framework-all.jar out/soong/.intermediates/frameworks/base/ext/android_common/withres/ext.jar out/soong/.intermediates/external/icu/icu4j/icu4j-icudata-jarjar/linux_glibc_common/jarjar/icu4j-icudata-jarjar.jar out/soong/.intermediates/external/icu/icu4j/icu4j-icutzdata-jarjar/linux_glibc_common/jarjar/icu4j-icutzdata-jarjar.jar" />
<option name="VM_PARAMETERS" value="-ea" />
<option name="WORKING_DIRECTORY" value="$PROJECT_DIR$/../.." />
<RunnerSettings RunnerId="Debug">
diff --git a/.idea/vcs.xml b/.idea/vcs.xml
index c80f2198b5..df496fd13c 100644
--- a/.idea/vcs.xml
+++ b/.idea/vcs.xml
@@ -1,7 +1,40 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
+ <component name="IssueNavigationConfiguration">
+ <option name="links">
+ <list>
+ <IssueNavigationLink>
+ <option name="issueRegexp" value="\bb/(\d+)(#\w+)?\b" />
+ <option name="linkRegexp" value="https://buganizer.corp.google.com/issues/$1$2" />
+ </IssueNavigationLink>
+ <IssueNavigationLink>
+ <option name="issueRegexp" value="\b(?:BUG=|FIXED=)(\d+)\b" />
+ <option name="linkRegexp" value="https://buganizer.corp.google.com/issues/$1" />
+ </IssueNavigationLink>
+ <IssueNavigationLink>
+ <option name="issueRegexp" value="\b(?:cl/|cr/|OCL=|DIFFBASE=|ROLLBACK_OF=)(\d+)\b" />
+ <option name="linkRegexp" value="https://critique.corp.google.com/$1" />
+ </IssueNavigationLink>
+ <IssueNavigationLink>
+ <option name="issueRegexp" value="\bomg/(\d+)\b" />
+ <option name="linkRegexp" value="https://omg.corp.google.com/$1" />
+ </IssueNavigationLink>
+ <IssueNavigationLink>
+ <option name="issueRegexp" value="\b(?:go/|goto/)([^,.&lt;&gt;()&quot;\s]+(?:[.,][^,.&lt;&gt;()&quot;\s]+)*)" />
+ <option name="linkRegexp" value="https://goto.google.com/$1" />
+ </IssueNavigationLink>
+ <IssueNavigationLink>
+ <option name="issueRegexp" value="\bcs/([^\s]+[\w$])" />
+ <option name="linkRegexp" value="https://cs.corp.google.com/search/?q=$1" />
+ </IssueNavigationLink>
+ <IssueNavigationLink>
+ <option name="issueRegexp" value="(LINT\.IfChange)|(LINT\.ThenChange)" />
+ <option name="linkRegexp" value="https://goto.google.com/ifthisthenthatlint" />
+ </IssueNavigationLink>
+ </list>
+ </option>
+ </component>
<component name="VcsDirectoryMappings">
<mapping directory="$PROJECT_DIR$" vcs="Git" />
</component>
-</project>
-
+</project> \ No newline at end of file
diff --git a/Android.bp b/Android.bp
index 048b78e942..0be4cccad8 100644
--- a/Android.bp
+++ b/Android.bp
@@ -21,31 +21,8 @@
//
package {
- default_applicable_licenses: ["frameworks_layoutlib_license"],
-}
-
-// Added automatically by a large-scale-change that took the approach of
-// 'apply every license found to every target'. While this makes sure we respect
-// every license restriction, it may not be entirely correct.
-//
-// e.g. GPL in an MIT project might only apply to the contrib/ directory.
-//
-// Please consider splitting the single license below into multiple licenses,
-// taking care not to lose any license_kind information, and overriding the
-// default license using the 'licenses: [...]' property on targets as needed.
-//
-// For unused files, consider creating a 'fileGroup' with "//visibility:private"
-// to attach the license to, and including a comment whether the files may be
-// used in the current project.
-// See: http://go/android-license-faq
-license {
- name: "frameworks_layoutlib_license",
- visibility: [":__subpackages__"],
- license_kinds: [
- "SPDX-license-identifier-Apache-2.0",
- "SPDX-license-identifier-EPL",
- ],
- // large-scale-change unable to identify any license_text files
+ // See: http://go/android-license-faq
+ default_applicable_licenses: ["Android-Apache-2.0"],
}
java_genrule_host {
@@ -53,7 +30,7 @@ java_genrule_host {
tools: ["layoutlib_create"],
out: ["temp_layoutlib.jar"],
srcs: [
- ":atf-prebuilt{.jar}",
+ ":atf-prebuilt-371374941{.jar}",
":core-icu4j-for-host{.jar}",
":core-libart-for-host{.jar}",
":framework-all{.jar}",
@@ -63,3 +40,26 @@ java_genrule_host {
],
cmd: "rm -f $(out) && $(location layoutlib_create) --create-stub $(out) $(in)",
}
+
+java_genrule_host {
+ name: "layoutlib-native-delegates",
+ tools: ["layoutlib_create"],
+ out: ["layoutlib-native-delegates.jar"],
+ srcs: [
+ ":framework{.jar}",
+ ],
+ cmd: "rm -f $(out) && $(location layoutlib_create) --create-native-only-delegates $(out) $(in)",
+}
+
+java_device_for_host {
+ name: "layoutlib_create-classpath",
+ libs: [
+ "conscrypt-for-host",
+ "core-icu4j-for-host",
+ "core-libart-for-host",
+ "ext",
+ "framework-all",
+ "icu4j-icudata-jarjar",
+ "icu4j-icutzdata-jarjar",
+ ],
+}
diff --git a/METADATA b/METADATA
index 487c3e38ce..d65b0fb952 100644
--- a/METADATA
+++ b/METADATA
@@ -1,11 +1,11 @@
third_party {
- # would be NOTICE save for EPL in:
- # create/tests/res/mock_data/mock_android/fake/InnerTest.java
- # create/tests/res/mock_data/mock_android/util/EmptyArray.java
- # create/tests/res/mock_data/mock_android/view/View.java
- # create/tests/res/mock_data/mock_android/view/ViewGroup.java
- # create/tests/res/mock_data/mock_android/widget/LinearLayout.java
- # create/tests/res/mock_data/mock_android/widget/TableLayout.java
- # create/tests/res/mock_data/notjava/lang/JavaClass.java
+ license_note: "would be NOTICE save for EPL in:\n"
+ " create/tests/res/mock_data/mock_android/fake/InnerTest.java\n"
+ " create/tests/res/mock_data/mock_android/util/EmptyArray.java\n"
+ " create/tests/res/mock_data/mock_android/view/View.java\n"
+ " create/tests/res/mock_data/mock_android/view/ViewGroup.java\n"
+ " create/tests/res/mock_data/mock_android/widget/LinearLayout.java\n"
+ " create/tests/res/mock_data/mock_android/widget/TableLayout.java\n"
+ " create/tests/res/mock_data/notjava/lang/JavaClass.java"
license_type: RECIPROCAL
}
diff --git a/bridge/Android.bp b/bridge/Android.bp
index 6e86073052..5eefce25e0 100644
--- a/bridge/Android.bp
+++ b/bridge/Android.bp
@@ -27,16 +27,16 @@ java_library_host {
libs: [
"kxml2-2.3.0",
"layoutlib_api-prebuilt",
- "tools-common-prebuilt",
"guava",
+ "ninepatch-prebuilt",
+ "sdk-common",
],
static_libs: [
"temp_layoutlib",
- "ninepatch-prebuilt",
"layoutlib-common",
+ "layoutlib-common-delegates",
"layoutlib-validator",
- "sdk-common",
],
jarjar_rules: "jarjar-rules.txt",
@@ -56,16 +56,17 @@ java_library_host {
"kxml2-2.3.0",
"temp_layoutlib",
"guava",
+ "ninepatch-prebuilt",
+ "sdk-common",
],
static_libs: [
"layoutlib_create",
"layoutlib_api-prebuilt",
- "tools-common-prebuilt",
- "ninepatch-prebuilt",
"layoutlib-common",
+ "layoutlib-common-delegates",
+ "layoutlib-native-delegates",
"layoutlib-validator",
- "sdk-common",
],
jarjar_rules: "jarjar-rules.txt",
diff --git a/bridge/bridge.iml b/bridge/bridge.iml
index c9ce012ce1..870615d38c 100644
--- a/bridge/bridge.iml
+++ b/bridge/bridge.iml
@@ -27,7 +27,7 @@
<orderEntry type="inheritedJdk" />
<orderEntry type="sourceFolder" forTests="false" />
<orderEntry type="library" name="layoutlib_api-prebuilt" level="project" />
- <orderEntry type="module-library">
+ <orderEntry type="module-library" scope="TEST">
<library name="tools-common-prebuilt">
<ANNOTATIONS>
<root url="file://$MODULE_DIR$/.." />
@@ -43,7 +43,7 @@
</orderEntry>
<orderEntry type="library" name="framework.jar" level="project" />
<orderEntry type="library" name="guava" level="project" />
- <orderEntry type="module-library" scope="TEST">
+ <orderEntry type="module-library">
<library name="sdk-common">
<CLASSES>
<root url="jar://$MODULE_DIR$/../../../prebuilts/misc/common/sdk-common/sdk-common.jar!/" />
@@ -70,6 +70,7 @@
</library>
</orderEntry>
<orderEntry type="library" scope="TEST" name="hamcrest" level="project" />
+ <orderEntry type="module" module-name="delegates" />
<orderEntry type="module-library">
<library name="ninepatch-prebuilt">
<CLASSES>
@@ -93,25 +94,5 @@
</library>
</orderEntry>
<orderEntry type="module" module-name="validator" />
- <orderEntry type="module-library" scope="TEST">
- <library>
- <CLASSES>
- <root url="jar://$MODULE_DIR$/../../../prebuilts/misc/common/atf/atf_classes.jar!/" />
- </CLASSES>
- <JAVADOC />
- <SOURCES />
- </library>
- </orderEntry>
- <orderEntry type="module-library">
- <library>
- <CLASSES>
- <root url="jar://$MODULE_DIR$/../../../prebuilts/misc/common/sdk-common/sdk-common.jar!/" />
- </CLASSES>
- <JAVADOC />
- <SOURCES>
- <root url="jar://$MODULE_DIR$/../../../prebuilts/misc/common/sdk-common/sdk-common-sources.jar!/" />
- </SOURCES>
- </library>
- </orderEntry>
</component>
</module> \ No newline at end of file
diff --git a/bridge/resources/icons/shadow-b.png b/bridge/resources/icons/shadow-b.png
deleted file mode 100644
index 68f4f4ba5b..0000000000
--- a/bridge/resources/icons/shadow-b.png
+++ /dev/null
Binary files differ
diff --git a/bridge/resources/icons/shadow-bl.png b/bridge/resources/icons/shadow-bl.png
deleted file mode 100644
index ee7dbe8460..0000000000
--- a/bridge/resources/icons/shadow-bl.png
+++ /dev/null
Binary files differ
diff --git a/bridge/resources/icons/shadow-br.png b/bridge/resources/icons/shadow-br.png
deleted file mode 100644
index c45ad77f8d..0000000000
--- a/bridge/resources/icons/shadow-br.png
+++ /dev/null
Binary files differ
diff --git a/bridge/resources/icons/shadow-l.png b/bridge/resources/icons/shadow-l.png
deleted file mode 100644
index 77d0bd0893..0000000000
--- a/bridge/resources/icons/shadow-l.png
+++ /dev/null
Binary files differ
diff --git a/bridge/resources/icons/shadow-r.png b/bridge/resources/icons/shadow-r.png
deleted file mode 100644
index 4af7a33305..0000000000
--- a/bridge/resources/icons/shadow-r.png
+++ /dev/null
Binary files differ
diff --git a/bridge/resources/icons/shadow-tl.png b/bridge/resources/icons/shadow-tl.png
deleted file mode 100644
index 424fb3676f..0000000000
--- a/bridge/resources/icons/shadow-tl.png
+++ /dev/null
Binary files differ
diff --git a/bridge/resources/icons/shadow-tr.png b/bridge/resources/icons/shadow-tr.png
deleted file mode 100644
index 1fd0c77269..0000000000
--- a/bridge/resources/icons/shadow-tr.png
+++ /dev/null
Binary files differ
diff --git a/bridge/resources/icons/shadow2-b.png b/bridge/resources/icons/shadow2-b.png
deleted file mode 100644
index 963973ea7d..0000000000
--- a/bridge/resources/icons/shadow2-b.png
+++ /dev/null
Binary files differ
diff --git a/bridge/resources/icons/shadow2-bl.png b/bridge/resources/icons/shadow2-bl.png
deleted file mode 100644
index 7612487775..0000000000
--- a/bridge/resources/icons/shadow2-bl.png
+++ /dev/null
Binary files differ
diff --git a/bridge/resources/icons/shadow2-br.png b/bridge/resources/icons/shadow2-br.png
deleted file mode 100644
index 8e20252cbf..0000000000
--- a/bridge/resources/icons/shadow2-br.png
+++ /dev/null
Binary files differ
diff --git a/bridge/resources/icons/shadow2-l.png b/bridge/resources/icons/shadow2-l.png
deleted file mode 100644
index 2db18a0591..0000000000
--- a/bridge/resources/icons/shadow2-l.png
+++ /dev/null
Binary files differ
diff --git a/bridge/resources/icons/shadow2-r.png b/bridge/resources/icons/shadow2-r.png
deleted file mode 100644
index 8e026f147c..0000000000
--- a/bridge/resources/icons/shadow2-r.png
+++ /dev/null
Binary files differ
diff --git a/bridge/resources/icons/shadow2-tl.png b/bridge/resources/icons/shadow2-tl.png
deleted file mode 100644
index a8045ed379..0000000000
--- a/bridge/resources/icons/shadow2-tl.png
+++ /dev/null
Binary files differ
diff --git a/bridge/resources/icons/shadow2-tr.png b/bridge/resources/icons/shadow2-tr.png
deleted file mode 100644
index 590373c955..0000000000
--- a/bridge/resources/icons/shadow2-tr.png
+++ /dev/null
Binary files differ
diff --git a/bridge/src/android/animation/AnimationThread.java b/bridge/src/android/animation/AnimationThread.java
deleted file mode 100644
index ce2aec7901..0000000000
--- a/bridge/src/android/animation/AnimationThread.java
+++ /dev/null
@@ -1,176 +0,0 @@
-/*
- * Copyright (C) 2010 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package android.animation;
-
-import com.android.ide.common.rendering.api.IAnimationListener;
-import com.android.ide.common.rendering.api.RenderSession;
-import com.android.ide.common.rendering.api.Result;
-import com.android.ide.common.rendering.api.Result.Status;
-import com.android.layoutlib.bridge.Bridge;
-import com.android.layoutlib.bridge.impl.RenderSessionImpl;
-
-import android.os.Handler;
-import android.os.Handler_Delegate;
-import android.os.Message;
-
-import java.util.PriorityQueue;
-import java.util.Queue;
-
-/**
- * Abstract animation thread.
- * <p/>
- * This does not actually start an animation, instead it fakes a looper that will play whatever
- * animation is sending messages to its own {@link Handler}.
- * <p/>
- * Classes should implement {@link #preAnimation()} and {@link #postAnimation()}.
- * <p/>
- * If {@link #preAnimation()} does not start an animation somehow then the thread doesn't do
- * anything.
- *
- */
-public abstract class AnimationThread extends Thread {
-
- private static class MessageBundle implements Comparable<MessageBundle> {
- final Handler mTarget;
- final Message mMessage;
- final long mUptimeMillis;
-
- MessageBundle(Handler target, Message message, long uptimeMillis) {
- mTarget = target;
- mMessage = message;
- mUptimeMillis = uptimeMillis;
- }
-
- @Override
- public int compareTo(MessageBundle bundle) {
- if (mUptimeMillis < bundle.mUptimeMillis) {
- return -1;
- }
- return 1;
- }
- }
-
- private final RenderSessionImpl mSession;
-
- private Queue<MessageBundle> mQueue = new PriorityQueue<MessageBundle>();
- private final IAnimationListener mListener;
-
- public AnimationThread(RenderSessionImpl scene, String threadName,
- IAnimationListener listener) {
- super(threadName);
- mSession = scene;
- mListener = listener;
- }
-
- public abstract Result preAnimation();
- public abstract void postAnimation();
-
- @Override
- public void run() {
- Bridge.prepareThread();
- try {
- /* FIXME: The ANIMATION_FRAME message no longer exists. Instead, the
- * animation timing loop is completely based on a Choreographer objects
- * that schedules animation and drawing frames. The animation handler is
- * no longer even a handler; it is just a Runnable enqueued on the Choreographer.
- Handler_Delegate.setCallback(new IHandlerCallback() {
- @Override
- public void sendMessageAtTime(Handler handler, Message msg, long uptimeMillis) {
- if (msg.what == ValueAnimator.ANIMATION_START ||
- msg.what == ValueAnimator.ANIMATION_FRAME) {
- mQueue.add(new MessageBundle(handler, msg, uptimeMillis));
- } else {
- // just ignore.
- }
- }
- });
- */
-
- // call out to the pre-animation work, which should start an animation or more.
- Result result = preAnimation();
- if (result.isSuccess() == false) {
- mListener.done(result);
- }
-
- // loop the animation
- RenderSession session = mSession.getSession();
- do {
- // check early.
- if (mListener.isCanceled()) {
- break;
- }
-
- // get the next message.
- MessageBundle bundle = mQueue.poll();
- if (bundle == null) {
- break;
- }
-
- // sleep enough for this bundle to be on time
- long currentTime = System.currentTimeMillis();
- if (currentTime < bundle.mUptimeMillis) {
- try {
- sleep(bundle.mUptimeMillis - currentTime);
- } catch (InterruptedException e) {
- // FIXME log/do something/sleep again?
- e.printStackTrace();
- }
- }
-
- // check after sleeping.
- if (mListener.isCanceled()) {
- break;
- }
-
- // ready to do the work, acquire the scene.
- result = mSession.acquire(250);
- if (result.isSuccess() == false) {
- mListener.done(result);
- return;
- }
-
- // process the bundle. If the animation is not finished, this will enqueue
- // the next message, so mQueue will have another one.
- try {
- // check after acquiring in case it took a while.
- if (mListener.isCanceled()) {
- break;
- }
-
- bundle.mTarget.handleMessage(bundle.mMessage);
- if (mSession.render(false /*freshRender*/).isSuccess()) {
- mListener.onNewFrame(session);
- }
- } finally {
- mSession.release();
- }
- } while (mListener.isCanceled() == false && mQueue.size() > 0);
-
- mListener.done(Status.SUCCESS.createResult());
-
- } catch (Throwable throwable) {
- // can't use Bridge.getLog() as the exception might be thrown outside
- // of an acquire/release block.
- mListener.done(Status.ERROR_UNKNOWN.createResult("Error playing animation", throwable));
-
- } finally {
- postAnimation();
- Handler_Delegate.setCallback(null);
- Bridge.cleanupThread();
- }
- }
-}
diff --git a/bridge/src/android/animation/PropertyValuesHolder_Delegate.java b/bridge/src/android/animation/PropertyValuesHolder_Delegate.java
deleted file mode 100644
index 556a0632c9..0000000000
--- a/bridge/src/android/animation/PropertyValuesHolder_Delegate.java
+++ /dev/null
@@ -1,204 +0,0 @@
-/*
- * Copyright (C) 2010 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package android.animation;
-
-import com.android.layoutlib.bridge.Bridge;
-import com.android.layoutlib.bridge.impl.DelegateManager;
-import com.android.tools.layoutlib.annotations.LayoutlibDelegate;
-
-import java.lang.reflect.InvocationTargetException;
-import java.lang.reflect.Method;
-import java.util.Arrays;
-import java.util.HashMap;
-import java.util.Map;
-
-/**
- * Delegate implementing the native methods of android.animation.PropertyValuesHolder
- *
- * Through the layoutlib_create tool, the original native methods of PropertyValuesHolder have been
- * replaced by calls to methods of the same name in this delegate class.
- *
- * Because it's a stateless class to start with, there's no need to keep a {@link DelegateManager}
- * around to map int to instance of the delegate.
- *
- * The main goal of this class' methods are to provide a native way to access setters and getters
- * on some object. We override these methods to use reflection since the original reflection
- * implementation of the PropertyValuesHolder won't be able to access protected methods.
- *
- */
-public class PropertyValuesHolder_Delegate {
- // This code is copied from android.animation.PropertyValuesHolder and must be kept in sync
- // We try several different types when searching for appropriate setter/getter functions.
- // The caller may have supplied values in a type that does not match the setter/getter
- // functions (such as the integers 0 and 1 to represent floating point values for alpha).
- // Also, the use of generics in constructors means that we end up with the Object versions
- // of primitive types (Float vs. float). But most likely, the setter/getter functions
- // will take primitive types instead.
- // So we supply an ordered array of other types to try before giving up.
- private static Class[] FLOAT_VARIANTS = {float.class, Float.class, double.class, int.class,
- Double.class, Integer.class};
- private static Class[] INTEGER_VARIANTS = {int.class, Integer.class, float.class, double.class,
- Float.class, Double.class};
-
- private static final Object sMethodIndexLock = new Object();
- private static final Map<Long, Method> ID_TO_METHOD = new HashMap<Long, Method>();
- private static final Map<String, Long> METHOD_NAME_TO_ID = new HashMap<String, Long>();
- private static long sNextId = 1;
-
- private static long registerMethod(Class<?> targetClass, String methodName, Class[] types,
- int nArgs) {
- // Encode the number of arguments in the method name
- String methodIndexName = String.format("%1$s.%2$s#%3$d", targetClass.getSimpleName(),
- methodName, nArgs);
- synchronized (sMethodIndexLock) {
- Long methodId = METHOD_NAME_TO_ID.get(methodIndexName);
-
- if (methodId != null) {
- // The method was already registered, check whether the class loader has changed
- Method method = ID_TO_METHOD.get(methodId);
- if (targetClass.equals(method.getDeclaringClass())) {
- return methodId;
- }
- }
-
- Class[] args = new Class[nArgs];
- Method method = null;
- for (Class typeVariant : types) {
- for (int i = 0; i < nArgs; i++) {
- args[i] = typeVariant;
- }
- try {
- method = targetClass.getDeclaredMethod(methodName, args);
- } catch (NoSuchMethodException ignore) {
- }
- }
-
- if (method != null) {
- if (methodId == null) {
- methodId = sNextId++;
- }
- ID_TO_METHOD.put(methodId, method);
- METHOD_NAME_TO_ID.put(methodIndexName, methodId);
-
- return methodId;
- }
- }
-
- // Method not found
- return 0;
- }
-
- private static void callMethod(Object target, long methodID, Object... args) {
- Method method = ID_TO_METHOD.get(methodID);
- assert method != null;
-
- try {
- method.setAccessible(true);
- method.invoke(target, args);
- } catch (IllegalAccessException | InvocationTargetException e) {
- Bridge.getLog().error(null, "Unable to update property during animation", e, null,
- null);
- }
- }
-
- @LayoutlibDelegate
- /*package*/ static long nGetIntMethod(Class<?> targetClass, String methodName) {
- return nGetMultipleIntMethod(targetClass, methodName, 1);
- }
-
- @LayoutlibDelegate
- /*package*/ static long nGetFloatMethod(Class<?> targetClass, String methodName) {
- return nGetMultipleFloatMethod(targetClass, methodName, 1);
- }
-
- @LayoutlibDelegate
- /*package*/ static long nGetMultipleIntMethod(Class<?> targetClass, String methodName,
- int numParams) {
- return registerMethod(targetClass, methodName, INTEGER_VARIANTS, numParams);
- }
-
- @LayoutlibDelegate
- /*package*/ static long nGetMultipleFloatMethod(Class<?> targetClass, String methodName,
- int numParams) {
- return registerMethod(targetClass, methodName, FLOAT_VARIANTS, numParams);
- }
-
- @LayoutlibDelegate
- /*package*/ static void nCallIntMethod(Object target, long methodID, int arg) {
- callMethod(target, methodID, arg);
- }
-
- @LayoutlibDelegate
- /*package*/ static void nCallFloatMethod(Object target, long methodID, float arg) {
- callMethod(target, methodID, arg);
- }
-
- @LayoutlibDelegate
- /*package*/ static void nCallTwoIntMethod(Object target, long methodID, int arg1,
- int arg2) {
- callMethod(target, methodID, arg1, arg2);
- }
-
- @LayoutlibDelegate
- /*package*/ static void nCallFourIntMethod(Object target, long methodID, int arg1,
- int arg2, int arg3, int arg4) {
- callMethod(target, methodID, arg1, arg2, arg3, arg4);
- }
-
- @LayoutlibDelegate
- /*package*/ static void nCallMultipleIntMethod(Object target, long methodID,
- int[] args) {
- assert args != null;
-
- // Box parameters
- Object[] params = new Object[args.length];
- for (int i = 0; i < args.length; i++) {
- params[i] = args;
- }
- callMethod(target, methodID, params);
- }
-
- @LayoutlibDelegate
- /*package*/ static void nCallTwoFloatMethod(Object target, long methodID, float arg1,
- float arg2) {
- callMethod(target, methodID, arg1, arg2);
- }
-
- @LayoutlibDelegate
- /*package*/ static void nCallFourFloatMethod(Object target, long methodID, float arg1,
- float arg2, float arg3, float arg4) {
- callMethod(target, methodID, arg1, arg2, arg3, arg4);
- }
-
- @LayoutlibDelegate
- /*package*/ static void nCallMultipleFloatMethod(Object target, long methodID,
- float[] args) {
- assert args != null;
-
- // Box parameters
- Object[] params = new Object[args.length];
- for (int i = 0; i < args.length; i++) {
- params[i] = args;
- }
- callMethod(target, methodID, params);
- }
-
- public static void clearCaches() {
- ID_TO_METHOD.clear();
- METHOD_NAME_TO_ID.clear();
- }
-}
diff --git a/bridge/src/android/app/ActivityThread_Delegate.java b/bridge/src/android/app/ActivityThread_Delegate.java
new file mode 100644
index 0000000000..da81134497
--- /dev/null
+++ b/bridge/src/android/app/ActivityThread_Delegate.java
@@ -0,0 +1,27 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.app;
+
+import com.android.layoutlib.bridge.impl.RenderAction;
+
+import android.content.Context;
+
+public class ActivityThread_Delegate {
+ public static Context getSystemUiContext() {
+ return RenderAction.getCurrentContext();
+ }
+}
diff --git a/bridge/src/android/content/res/AssetManager_Delegate.java b/bridge/src/android/content/res/AssetManager_Delegate.java
index c27df098af..1b268342c5 100644
--- a/bridge/src/android/content/res/AssetManager_Delegate.java
+++ b/bridge/src/android/content/res/AssetManager_Delegate.java
@@ -21,6 +21,8 @@ import com.android.tools.layoutlib.annotations.LayoutlibDelegate;
import android.util.SparseArray;
+import libcore.util.NativeAllocationRegistry_Delegate;
+
/**
* Delegate used to provide implementation of a select few native methods of {@link AssetManager}
* <p/>
@@ -34,6 +36,7 @@ public class AssetManager_Delegate {
private static final DelegateManager<AssetManager_Delegate> sManager =
new DelegateManager<>(AssetManager_Delegate.class);
+ private static long sFinalizer = -1;
// ---- delegate methods. ----
@@ -55,8 +58,14 @@ public class AssetManager_Delegate {
}
@LayoutlibDelegate
- /*package*/ static void nativeThemeDestroy(long theme) {
- Resources_Theme_Delegate.getDelegateManager().removeJavaReferenceFor(theme);
+ /*package*/ static long nativeGetThemeFreeFunction() {
+ synchronized (AssetManager_Delegate.class) {
+ if (sFinalizer == -1) {
+ sFinalizer = NativeAllocationRegistry_Delegate.createFinalizer(
+ Resources_Theme_Delegate.getDelegateManager()::removeJavaReferenceFor);
+ }
+ }
+ return sFinalizer;
}
@LayoutlibDelegate
@@ -71,8 +80,6 @@ public class AssetManager_Delegate {
}
@LayoutlibDelegate
- /*package*/ static String[] nativeCreateIdmapsForStaticOverlaysTargetingAndroid() {
- // AssetManager requires this not to be null
- return new String[0];
- }
+ /*package*/ static void createSystemAssetsInZygoteLocked(boolean reinitialize,
+ String frameworkPath) { }
}
diff --git a/bridge/src/android/content/res/BridgeTypedArray.java b/bridge/src/android/content/res/BridgeTypedArray.java
index eb603f0ebe..2935c15db1 100644
--- a/bridge/src/android/content/res/BridgeTypedArray.java
+++ b/bridge/src/android/content/res/BridgeTypedArray.java
@@ -40,7 +40,6 @@ import android.content.res.Resources.Theme;
import android.graphics.Typeface;
import android.graphics.Typeface_Accessor;
import android.graphics.drawable.Drawable;
-import android.text.Html;
import android.util.DisplayMetrics;
import android.util.TypedValue;
import android.view.LayoutInflater_Delegate;
@@ -50,7 +49,6 @@ import java.util.ArrayList;
import java.util.Arrays;
import java.util.Map;
-import static android.text.Html.FROM_HTML_MODE_COMPACT;
import static android.util.TypedValue.TYPE_ATTRIBUTE;
import static android.util.TypedValue.TYPE_DIMENSION;
import static android.util.TypedValue.TYPE_FLOAT;
@@ -64,8 +62,8 @@ import static android.util.TypedValue.TYPE_INT_HEX;
import static android.util.TypedValue.TYPE_NULL;
import static android.util.TypedValue.TYPE_REFERENCE;
import static android.util.TypedValue.TYPE_STRING;
-import static com.android.SdkConstants.PREFIX_RESOURCE_REF;
-import static com.android.SdkConstants.PREFIX_THEME_REF;
+import static com.android.ide.common.rendering.api.AndroidConstants.PREFIX_RESOURCE_REF;
+import static com.android.ide.common.rendering.api.AndroidConstants.PREFIX_THEME_REF;
import static com.android.ide.common.rendering.api.RenderResources.REFERENCE_EMPTY;
import static com.android.ide.common.rendering.api.RenderResources.REFERENCE_NULL;
import static com.android.ide.common.rendering.api.RenderResources.REFERENCE_UNDEFINED;
@@ -215,7 +213,7 @@ public final class BridgeTypedArray extends TypedArray {
ValueXmlHelper.unescapeResourceString(resourceValue.getRawXmlValue(),
true, false);
if (rawValue != null && !rawValue.equals(value)) {
- return Html.fromHtml(rawValue, FROM_HTML_MODE_COMPACT);
+ return ResourceHelper.parseHtml(rawValue);
}
}
return value;
@@ -270,10 +268,15 @@ public final class BridgeTypedArray extends TypedArray {
try {
return convertValueToInt(s, defValue);
} catch (NumberFormatException e) {
- Bridge.getLog().warning(ILayoutLog.TAG_RESOURCES_FORMAT,
- String.format("\"%1$s\" in attribute \"%2$s\" is not a valid integer",
- s, mNames[index]),
- null, null);
+ // If s starts with ?, it means that it is a theme attribute that wasn't defined.
+ // That is an allowed behaviour, and the expected result is to return the default
+ // value.
+ // If we are in this case, we do not want to log a warning.
+ if (s == null || !s.startsWith(PREFIX_THEME_REF)) {
+ Bridge.getLog().warning(ILayoutLog.TAG_RESOURCES_FORMAT,
+ String.format("\"%1$s\" in attribute \"%2$s\" is not a valid integer", s,
+ mNames[index]), null, null);
+ }
}
return defValue;
}
@@ -985,6 +988,9 @@ public final class BridgeTypedArray extends TypedArray {
if (value == null) {
return TYPE_NULL;
}
+ if (value.isEmpty()) {
+ return TYPE_STRING;
+ }
if (value.startsWith(PREFIX_RESOURCE_REF)) {
return TYPE_REFERENCE;
}
diff --git a/bridge/src/android/content/res/Resources_Delegate.java b/bridge/src/android/content/res/Resources_Delegate.java
index 2689acda75..f453aecce2 100644
--- a/bridge/src/android/content/res/Resources_Delegate.java
+++ b/bridge/src/android/content/res/Resources_Delegate.java
@@ -16,7 +16,6 @@
package android.content.res;
-import com.android.SdkConstants;
import com.android.ide.common.rendering.api.ArrayResourceValue;
import com.android.ide.common.rendering.api.AssetRepository;
import com.android.ide.common.rendering.api.DensityBasedResourceValue;
@@ -25,7 +24,6 @@ import com.android.ide.common.rendering.api.LayoutlibCallback;
import com.android.ide.common.rendering.api.PluralsResourceValue;
import com.android.ide.common.rendering.api.RenderResources;
import com.android.ide.common.rendering.api.ResourceNamespace;
-import com.android.ide.common.rendering.api.ResourceNamespace.Resolver;
import com.android.ide.common.rendering.api.ResourceReference;
import com.android.ide.common.rendering.api.ResourceValue;
import com.android.ide.common.rendering.api.ResourceValueImpl;
@@ -37,12 +35,10 @@ import com.android.layoutlib.bridge.android.UnresolvedResourceValue;
import com.android.layoutlib.bridge.impl.ParserFactory;
import com.android.layoutlib.bridge.impl.ResourceHelper;
import com.android.layoutlib.bridge.util.NinePatchInputStream;
-import com.android.ninepatch.NinePatch;
import com.android.resources.ResourceType;
import com.android.resources.ResourceUrl;
import com.android.tools.layoutlib.annotations.LayoutlibDelegate;
import com.android.tools.layoutlib.annotations.VisibleForTesting;
-import com.android.utils.Pair;
import org.xmlpull.v1.XmlPullParser;
import org.xmlpull.v1.XmlPullParserException;
@@ -58,6 +54,7 @@ import android.icu.text.PluralRules;
import android.util.AttributeSet;
import android.util.DisplayMetrics;
import android.util.LruCache;
+import android.util.Pair;
import android.util.TypedValue;
import android.view.DisplayAdjustments;
import android.view.ViewGroup.LayoutParams;
@@ -68,8 +65,9 @@ import java.util.Objects;
import java.util.WeakHashMap;
import static android.content.res.AssetManager.ACCESS_STREAMING;
-import static com.android.SdkConstants.ANDROID_PKG;
-import static com.android.SdkConstants.PREFIX_RESOURCE_REF;
+import static com.android.ide.common.rendering.api.AndroidConstants.ANDROID_PKG;
+import static com.android.ide.common.rendering.api.AndroidConstants.APP_PREFIX;
+import static com.android.ide.common.rendering.api.AndroidConstants.PREFIX_RESOURCE_REF;
public class Resources_Delegate {
private static WeakHashMap<Resources, LayoutlibCallback> sLayoutlibCallbacks =
@@ -160,7 +158,7 @@ public class Resources_Delegate {
value = new ResourceValueImpl(resourceInfo.getNamespace(),
resourceInfo.getResourceType(), attributeName, attributeName);
}
- return Pair.of(attributeName, value);
+ return Pair.create(attributeName, value);
}
return null;
@@ -175,7 +173,7 @@ public class Resources_Delegate {
static Drawable getDrawable(Resources resources, int id, Theme theme) {
Pair<String, ResourceValue> value = getResourceValue(resources, id);
if (value != null) {
- String key = value.getSecond().getValue();
+ String key = value.second.getValue();
Drawable.ConstantState constantState = key != null ? sDrawableCache.get(key) : null;
Drawable drawable;
@@ -183,10 +181,18 @@ public class Resources_Delegate {
drawable = constantState.newDrawable(resources, theme);
} else {
drawable =
- ResourceHelper.getDrawable(value.getSecond(), getContext(resources), theme);
+ ResourceHelper.getDrawable(value.second, getContext(resources), theme);
+
+ if (drawable == null) {
+ throwException(resources, id);
+ return null;
+ }
if (key != null) {
- sDrawableCache.put(key, drawable.getConstantState());
+ Drawable.ConstantState state = drawable.getConstantState();
+ if (state != null) {
+ sDrawableCache.put(key, state);
+ }
}
}
@@ -210,7 +216,7 @@ public class Resources_Delegate {
Pair<String, ResourceValue> value = getResourceValue(resources, id);
if (value != null) {
- ResourceValue resourceValue = value.getSecond();
+ ResourceValue resourceValue = value.second;
try {
return ResourceHelper.getColor(resourceValue.getValue());
} catch (NumberFormatException e) {
@@ -247,7 +253,7 @@ public class Resources_Delegate {
Pair<String, ResourceValue> resValue = getResourceValue(resources, id);
if (resValue != null) {
- ColorStateList stateList = ResourceHelper.getColorStateList(resValue.getSecond(),
+ ColorStateList stateList = ResourceHelper.getColorStateList(resValue.second,
getContext(resources), theme);
if (stateList != null) {
return stateList;
@@ -266,7 +272,7 @@ public class Resources_Delegate {
Pair<String, ResourceValue> value = getResourceValue(resources, id);
if (value != null) {
- ResourceValue resValue = value.getSecond();
+ ResourceValue resValue = value.second;
assert resValue != null;
if (resValue != null) {
@@ -285,7 +291,7 @@ public class Resources_Delegate {
Pair<String, ResourceValue> value = getResourceValue(resources, id);
if (value != null) {
- ResourceValue resValue = value.getSecond();
+ ResourceValue resValue = value.second;
assert resValue != null;
if (resValue != null) {
@@ -425,7 +431,7 @@ public class Resources_Delegate {
Pair<String, ResourceValue> v = getResourceValue(resources, id);
if (v != null) {
- ResourceValue resValue = v.getSecond();
+ ResourceValue resValue = v.second;
assert resValue != null;
if (resValue != null) {
@@ -478,7 +484,7 @@ public class Resources_Delegate {
Pair<String, ResourceValue> v = getResourceValue(resources, id);
if (v != null) {
- ResourceValue value = v.getSecond();
+ ResourceValue value = v.second;
try {
BridgeXmlBlockParser parser =
@@ -505,7 +511,7 @@ public class Resources_Delegate {
Pair<String, ResourceValue> v = getResourceValue(resources, id);
if (v != null) {
- ResourceValue value = v.getSecond();
+ ResourceValue value = v.second;
try {
return ResourceHelper.getXmlBlockParser(getContext(resources), value);
@@ -525,7 +531,14 @@ public class Resources_Delegate {
@LayoutlibDelegate
static TypedArray obtainAttributes(Resources resources, AttributeSet set, int[] attrs) {
- return getContext(resources).obtainStyledAttributes(set, attrs);
+ BridgeContext context = getContext(resources);
+ RenderResources renderResources = context.getRenderResources();
+ // Remove all themes, including default, to ensure theme attributes are not resolved
+ renderResources.getAllThemes().clear();
+ BridgeTypedArray ta = context.internalObtainStyledAttributes(set, attrs, 0, 0);
+ // Reset styles to only the default if present
+ renderResources.clearStyles();
+ return ta;
}
@LayoutlibDelegate
@@ -573,7 +586,7 @@ public class Resources_Delegate {
Pair<String, ResourceValue> value = getResourceValue(resources, id);
if (value != null) {
- ResourceValue resValue = value.getSecond();
+ ResourceValue resValue = value.second;
assert resValue != null;
if (resValue != null) {
@@ -587,7 +600,7 @@ public class Resources_Delegate {
}
TypedValue tmpValue = new TypedValue();
if (ResourceHelper.parseFloatAttribute(
- value.getFirst(), v, tmpValue, true /*requireUnit*/) &&
+ value.first, v, tmpValue, true /*requireUnit*/) &&
tmpValue.type == TypedValue.TYPE_DIMENSION) {
return tmpValue.getDimension(resources.getDisplayMetrics());
}
@@ -607,7 +620,7 @@ public class Resources_Delegate {
Pair<String, ResourceValue> value = getResourceValue(resources, id);
if (value != null) {
- ResourceValue resValue = value.getSecond();
+ ResourceValue resValue = value.second;
assert resValue != null;
if (resValue != null) {
@@ -615,7 +628,7 @@ public class Resources_Delegate {
if (v != null) {
TypedValue tmpValue = new TypedValue();
if (ResourceHelper.parseFloatAttribute(
- value.getFirst(), v, tmpValue, true /*requireUnit*/) &&
+ value.first, v, tmpValue, true /*requireUnit*/) &&
tmpValue.type == TypedValue.TYPE_DIMENSION) {
return TypedValue.complexToDimensionPixelOffset(tmpValue.data,
resources.getDisplayMetrics());
@@ -636,7 +649,7 @@ public class Resources_Delegate {
Pair<String, ResourceValue> value = getResourceValue(resources, id);
if (value != null) {
- ResourceValue resValue = value.getSecond();
+ ResourceValue resValue = value.second;
assert resValue != null;
if (resValue != null) {
@@ -644,7 +657,7 @@ public class Resources_Delegate {
if (v != null) {
TypedValue tmpValue = new TypedValue();
if (ResourceHelper.parseFloatAttribute(
- value.getFirst(), v, tmpValue, true /*requireUnit*/) &&
+ value.first, v, tmpValue, true /*requireUnit*/) &&
tmpValue.type == TypedValue.TYPE_DIMENSION) {
return TypedValue.complexToDimensionPixelSize(tmpValue.data,
resources.getDisplayMetrics());
@@ -665,7 +678,7 @@ public class Resources_Delegate {
Pair<String, ResourceValue> value = getResourceValue(resources, id);
if (value != null) {
- ResourceValue resValue = value.getSecond();
+ ResourceValue resValue = value.second;
assert resValue != null;
if (resValue != null) {
@@ -692,7 +705,7 @@ public class Resources_Delegate {
Pair<String, ResourceValue> value = getResourceValue(resources, id);
if (value != null) {
- ResourceValue resValue = value.getSecond();
+ ResourceValue resValue = value.second;
if (resValue != null) {
String v = resValue.getValue();
@@ -712,7 +725,7 @@ public class Resources_Delegate {
Pair<String, ResourceValue> value = getResourceValue(resources, id);
if (value != null) {
- ResourceValue resValue = value.getSecond();
+ ResourceValue resValue = value.second;
if (resValue != null) {
String v = resValue.getValue();
@@ -774,9 +787,9 @@ public class Resources_Delegate {
private static String getPackageName(ResourceReference resourceInfo, Resources resources) {
String packageName = resourceInfo.getNamespace().getPackageName();
if (packageName == null) {
- packageName = getContext(resources).getPackageName();
+ packageName = getLayoutlibCallback(resources).getResourcePackage();
if (packageName == null) {
- packageName = SdkConstants.APP_PREFIX;
+ packageName = APP_PREFIX;
}
}
return packageName;
@@ -802,8 +815,8 @@ public class Resources_Delegate {
static String getString(Resources resources, int id) throws NotFoundException {
Pair<String, ResourceValue> value = getResourceValue(resources, id);
- if (value != null && value.getSecond().getValue() != null) {
- return value.getSecond().getValue();
+ if (value != null && value.second.getValue() != null) {
+ return value.second.getValue();
}
// id was not found or not resolved. Throw a NotFoundException.
@@ -819,8 +832,8 @@ public class Resources_Delegate {
Pair<String, ResourceValue> value = getResourceValue(resources, id);
if (value != null) {
- if (value.getSecond() instanceof PluralsResourceValue) {
- PluralsResourceValue pluralsResourceValue = (PluralsResourceValue) value.getSecond();
+ if (value.second instanceof PluralsResourceValue) {
+ PluralsResourceValue pluralsResourceValue = (PluralsResourceValue) value.second;
PluralRules pluralRules = PluralRules.forLocale(resources.getConfiguration().getLocales()
.get(0));
String strValue = pluralsResourceValue.getValue(pluralRules.select(quantity));
@@ -831,7 +844,7 @@ public class Resources_Delegate {
return strValue;
}
else {
- return value.getSecond().getValue();
+ return value.second.getValue();
}
}
@@ -860,7 +873,7 @@ public class Resources_Delegate {
NotFoundException {
Pair<String, ResourceValue> value = getResourceValue(resources, id);
if (value != null) {
- return ResourceHelper.getFont(value.getSecond(), getContext(resources), null);
+ return ResourceHelper.getFont(value.second, getContext(resources), null);
}
throwException(resources, id);
@@ -892,11 +905,11 @@ public class Resources_Delegate {
Pair<String, ResourceValue> value = getResourceValue(resources, id);
if (value != null) {
- ResourceValue resVal = value.getSecond();
+ ResourceValue resVal = value.second;
String v = resVal != null ? resVal.getValue() : null;
if (v != null) {
- if (ResourceHelper.parseFloatAttribute(value.getFirst(), v, outValue,
+ if (ResourceHelper.parseFloatAttribute(value.first, v, outValue,
false /*requireUnit*/)) {
return resVal;
}
@@ -940,7 +953,7 @@ public class Resources_Delegate {
Pair<String, ResourceValue> v = getResourceValue(resources, id);
if (v != null) {
- ResourceValue value = v.getSecond();
+ ResourceValue value = v.second;
try {
return ResourceHelper.getXmlBlockParser(getContext(resources), value);
@@ -973,8 +986,8 @@ public class Resources_Delegate {
Pair<String, ResourceValue> result = getResourceValue(resources, id);
ResourceNamespace layoutNamespace;
- if (result != null && result.getSecond() != null) {
- layoutNamespace = result.getSecond().getNamespace();
+ if (result != null && result.second != null) {
+ layoutNamespace = result.second.getNamespace();
} else {
// We need to pick something, even though the resource system never heard about a layout
// with this numeric id.
@@ -996,7 +1009,7 @@ public class Resources_Delegate {
Pair<String, ResourceValue> value = getResourceValue(resources, id);
if (value != null) {
- String path = value.getSecond().getValue();
+ String path = value.second.getValue();
if (path != null) {
return openRawResource(resources, path);
}
@@ -1026,11 +1039,8 @@ public class Resources_Delegate {
if (stream == null) {
throw new NotFoundException(path);
}
- // If it's a nine-patch return a custom input stream so that
- // other methods (mainly bitmap factory) can detect it's a 9-patch
- // and actually load it as a 9-patch instead of a normal bitmap.
- if (path.toLowerCase().endsWith(NinePatch.EXTENSION_9PATCH)) {
- return new NinePatchInputStream(stream);
+ if (path.endsWith(".9.png")) {
+ stream = new NinePatchInputStream(stream, path);
}
return stream;
} catch (IOException e) {
@@ -1104,7 +1114,7 @@ public class Resources_Delegate {
return Bridge.getResourceId(url.type, url.name);
}
- if (getContext(resources).getPackageName().equals(url.namespace)) {
+ if (getLayoutlibCallback(resources).getResourcePackage().equals(url.namespace)) {
return getLayoutlibCallback(resources).getOrGenerateResourceId(
new ResourceReference(ResourceNamespace.RES_AUTO, url.type, url.name));
}
diff --git a/bridge/src/android/graphics/BaseCanvas_Delegate.java b/bridge/src/android/graphics/BaseCanvas_Delegate.java
deleted file mode 100644
index 5a9236ca2e..0000000000
--- a/bridge/src/android/graphics/BaseCanvas_Delegate.java
+++ /dev/null
@@ -1,815 +0,0 @@
-/*
- * Copyright (C) 2016 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package android.graphics;
-
-import com.android.ide.common.rendering.api.ILayoutLog;
-import com.android.layoutlib.bridge.Bridge;
-import com.android.layoutlib.bridge.impl.DelegateManager;
-import com.android.layoutlib.bridge.impl.GcSnapshot;
-import com.android.layoutlib.bridge.impl.PorterDuffUtility;
-import com.android.ninepatch.NinePatchChunk;
-import com.android.tools.layoutlib.annotations.LayoutlibDelegate;
-
-import android.annotation.Nullable;
-import android.text.TextUtils;
-import android.util.imagepool.ImagePool;
-import android.util.imagepool.ImagePoolProvider;
-
-import java.awt.Composite;
-import java.awt.Graphics2D;
-import java.awt.PaintContext;
-import java.awt.RenderingHints;
-import java.awt.Shape;
-import java.awt.geom.AffineTransform;
-import java.awt.geom.Arc2D;
-import java.awt.geom.Area;
-import java.awt.geom.Rectangle2D;
-import java.awt.image.BufferedImage;
-import java.awt.image.ColorModel;
-import java.awt.image.DataBuffer;
-
-public class BaseCanvas_Delegate {
- // ---- delegate manager ----
- protected static DelegateManager<BaseCanvas_Delegate> sManager =
- new DelegateManager<>(BaseCanvas_Delegate.class);
-
- // ---- delegate helper data ----
- private final static boolean[] sBoolOut = new boolean[1];
-
-
- // ---- delegate data ----
- protected Bitmap_Delegate mBitmap;
- protected GcSnapshot mSnapshot;
-
- // ---- Public Helper methods ----
-
- protected BaseCanvas_Delegate(Bitmap_Delegate bitmap) {
- mSnapshot = GcSnapshot.createDefaultSnapshot(mBitmap = bitmap);
- }
-
- protected BaseCanvas_Delegate() {
- mSnapshot = GcSnapshot.createDefaultSnapshot(null /*image*/);
- }
-
- /**
- * Disposes of the {@link Graphics2D} stack.
- */
- protected void dispose() {
- mSnapshot.dispose();
- }
-
- /**
- * Returns the current {@link Graphics2D} used to draw.
- */
- public GcSnapshot getSnapshot() {
- return mSnapshot;
- }
-
- // ---- native methods ----
-
- @LayoutlibDelegate
- /*package*/ static void nDrawBitmap(long nativeCanvas, long bitmapHandle, float left, float top,
- long nativePaintOrZero, int canvasDensity, int screenDensity, int bitmapDensity) {
- // get the delegate from the native int.
- Bitmap_Delegate bitmapDelegate = Bitmap_Delegate.getDelegate(bitmapHandle);
- if (bitmapDelegate == null) {
- return;
- }
-
- BufferedImage image = bitmapDelegate.getImage();
- float right = left + image.getWidth();
- float bottom = top + image.getHeight();
-
- drawBitmap(nativeCanvas, bitmapDelegate, nativePaintOrZero,
- 0, 0, image.getWidth(), image.getHeight(),
- (int)left, (int)top, (int)right, (int)bottom);
- }
-
- @LayoutlibDelegate
- /*package*/ static void nDrawBitmap(long nativeCanvas, long bitmapHandle, float srcLeft,
- float srcTop, float srcRight, float srcBottom, float dstLeft, float dstTop,
- float dstRight, float dstBottom, long nativePaintOrZero, int screenDensity,
- int bitmapDensity) {
- // get the delegate from the native int.
- Bitmap_Delegate bitmapDelegate = Bitmap_Delegate.getDelegate(bitmapHandle);
- if (bitmapDelegate == null) {
- return;
- }
-
- drawBitmap(nativeCanvas, bitmapDelegate, nativePaintOrZero, (int) srcLeft, (int) srcTop,
- (int) srcRight, (int) srcBottom, (int) dstLeft, (int) dstTop, (int) dstRight,
- (int) dstBottom);
- }
-
- @LayoutlibDelegate
- /*package*/ static void nDrawBitmap(long nativeCanvas, int[] colors, int offset, int stride,
- final float x, final float y, int width, int height, boolean hasAlpha,
- long nativePaintOrZero) {
- // create a temp BufferedImage containing the content.
- final ImagePool.Image image = ImagePoolProvider.get().acquire(width, height,
- hasAlpha ? BufferedImage.TYPE_INT_ARGB : BufferedImage.TYPE_INT_RGB);
- image.setRGB(0, 0, width, height, colors, offset, stride);
-
- draw(nativeCanvas, nativePaintOrZero, true /*compositeOnly*/, false /*forceSrcMode*/,
- (graphics, paint) -> {
- if (paint != null && paint.isFilterBitmap()) {
- graphics.setRenderingHint(RenderingHints.KEY_INTERPOLATION,
- RenderingHints.VALUE_INTERPOLATION_BILINEAR);
- }
-
- image.drawImage(graphics, (int) x, (int) y, null);
- });
- }
-
- @LayoutlibDelegate
- /*package*/ static void nDrawColor(long nativeCanvas, final int color, final int mode) {
- // get the delegate from the native int.
- BaseCanvas_Delegate canvasDelegate = sManager.getDelegate(nativeCanvas);
- if (canvasDelegate == null) {
- return;
- }
-
- final int w = canvasDelegate.mBitmap.getImage().getWidth();
- final int h = canvasDelegate.mBitmap.getImage().getHeight();
- draw(nativeCanvas, (graphics, paint) -> {
- // reset its transform just in case
- graphics.setTransform(new AffineTransform());
-
- // set the color
- graphics.setColor(new java.awt.Color(color, true /*alpha*/));
-
- Composite composite = PorterDuffUtility.getComposite(
- PorterDuffUtility.getPorterDuffMode(mode), 0xFF);
- if (composite != null) {
- graphics.setComposite(composite);
- }
-
- graphics.fillRect(0, 0, w, h);
- });
- }
-
- @LayoutlibDelegate
- /*package*/ static void nDrawColor(long nativeCanvas, long nativeColorSpace, long color,
- int mode) {
- nDrawColor(nativeCanvas, Color.toArgb(color), mode);
- }
-
- @LayoutlibDelegate
- /*package*/ static void nDrawPaint(long nativeCanvas, long paint) {
- // FIXME
- Bridge.getLog().fidelityWarning(ILayoutLog.TAG_UNSUPPORTED,
- "Canvas.drawPaint is not supported.", null,null, null /*data*/);
- }
-
- @LayoutlibDelegate
- /*package*/ static void nDrawPoint(long nativeCanvas, float x, float y,
- long nativePaint) {
- // TODO: need to support the attribute (e.g. stroke width) of paint
- draw(nativeCanvas, nativePaint, false /*compositeOnly*/, false /*forceSrcMode*/,
- (graphics, paintDelegate) -> graphics.fillRect((int)x, (int)y, 1, 1));
- }
-
- @LayoutlibDelegate
- /*package*/ static void nDrawPoints(long nativeCanvas, float[] pts, int offset, int count,
- long nativePaint) {
- if (offset < 0 || count < 0 || offset + count > pts.length) {
- throw new IllegalArgumentException("Invalid argument set");
- }
- // ignore the last point if the count is odd (It means it is not paired).
- count = (count >> 1) << 1;
- for (int i = offset; i < offset + count; i += 2) {
- nDrawPoint(nativeCanvas, pts[i], pts[i + 1], nativePaint);
- }
- }
-
- @LayoutlibDelegate
- /*package*/ static void nDrawLine(long nativeCanvas,
- final float startX, final float startY, final float stopX, final float stopY,
- long paint) {
- draw(nativeCanvas, paint, false /*compositeOnly*/, false /*forceSrcMode*/,
- (graphics, paintDelegate) -> graphics.drawLine((int)startX, (int)startY, (int)stopX, (int)stopY));
- }
-
- @LayoutlibDelegate
- /*package*/ static void nDrawLines(long nativeCanvas,
- final float[] pts, final int offset, final int count,
- long nativePaint) {
- draw(nativeCanvas, nativePaint, false /*compositeOnly*/,
- false /*forceSrcMode*/, (graphics, paintDelegate) -> {
- for (int i = 0; i < count; i += 4) {
- graphics.drawLine((int) pts[i + offset], (int) pts[i + offset + 1],
- (int) pts[i + offset + 2], (int) pts[i + offset + 3]);
- }
- });
- }
-
- @LayoutlibDelegate
- /*package*/ static void nDrawRect(long nativeCanvas,
- final float left, final float top, final float right, final float bottom, long paint) {
-
- draw(nativeCanvas, paint, false /*compositeOnly*/, false /*forceSrcMode*/,
- (graphics, paintDelegate) -> {
- int style = paintDelegate.getStyle();
-
- // draw
- if (style == Paint.Style.FILL.nativeInt ||
- style == Paint.Style.FILL_AND_STROKE.nativeInt) {
- graphics.fillRect((int)left, (int)top,
- (int)(right-left), (int)(bottom-top));
- }
-
- if (style == Paint.Style.STROKE.nativeInt ||
- style == Paint.Style.FILL_AND_STROKE.nativeInt) {
- graphics.drawRect((int)left, (int)top,
- (int)(right-left), (int)(bottom-top));
- }
- });
- }
-
- @LayoutlibDelegate
- /*package*/ static void nDrawOval(long nativeCanvas, final float left,
- final float top, final float right, final float bottom, long paint) {
- if (right > left && bottom > top) {
- draw(nativeCanvas, paint, false /*compositeOnly*/, false /*forceSrcMode*/,
- (graphics, paintDelegate) -> {
- int style = paintDelegate.getStyle();
-
- // draw
- if (style == Paint.Style.FILL.nativeInt ||
- style == Paint.Style.FILL_AND_STROKE.nativeInt) {
- graphics.fillOval((int)left, (int)top,
- (int)(right - left), (int)(bottom - top));
- }
-
- if (style == Paint.Style.STROKE.nativeInt ||
- style == Paint.Style.FILL_AND_STROKE.nativeInt) {
- graphics.drawOval((int)left, (int)top,
- (int)(right - left), (int)(bottom - top));
- }
- });
- }
- }
-
- @LayoutlibDelegate
- /*package*/ static void nDrawCircle(long nativeCanvas,
- float cx, float cy, float radius, long paint) {
- nDrawOval(nativeCanvas,
- cx - radius, cy - radius, cx + radius, cy + radius,
- paint);
- }
-
- @LayoutlibDelegate
- /*package*/ static void nDrawArc(long nativeCanvas,
- final float left, final float top, final float right, final float bottom,
- final float startAngle, final float sweep,
- final boolean useCenter, long paint) {
- if (right > left && bottom > top) {
- draw(nativeCanvas, paint, false /*compositeOnly*/, false /*forceSrcMode*/,
- (graphics, paintDelegate) -> {
- int style = paintDelegate.getStyle();
-
- Arc2D.Float arc = new Arc2D.Float(
- left, top, right - left, bottom - top,
- -startAngle, -sweep,
- useCenter ? Arc2D.PIE : Arc2D.OPEN);
-
- // draw
- if (style == Paint.Style.FILL.nativeInt ||
- style == Paint.Style.FILL_AND_STROKE.nativeInt) {
- graphics.fill(arc);
- }
-
- if (style == Paint.Style.STROKE.nativeInt ||
- style == Paint.Style.FILL_AND_STROKE.nativeInt) {
- graphics.draw(arc);
- }
- });
- }
- }
-
- @LayoutlibDelegate
- /*package*/ static void nDrawRoundRect(long nativeCanvas,
- final float left, final float top, final float right, final float bottom,
- final float rx, final float ry, long paint) {
- draw(nativeCanvas, paint, false /*compositeOnly*/, false /*forceSrcMode*/,
- (graphics, paintDelegate) -> {
- int style = paintDelegate.getStyle();
-
- // draw
- if (style == Paint.Style.FILL.nativeInt ||
- style == Paint.Style.FILL_AND_STROKE.nativeInt) {
- graphics.fillRoundRect(
- (int)left, (int)top,
- (int)(right - left), (int)(bottom - top),
- 2 * (int)rx, 2 * (int)ry);
- }
-
- if (style == Paint.Style.STROKE.nativeInt ||
- style == Paint.Style.FILL_AND_STROKE.nativeInt) {
- graphics.drawRoundRect(
- (int)left, (int)top,
- (int)(right - left), (int)(bottom - top),
- 2 * (int)rx, 2 * (int)ry);
- }
- });
- }
-
- @LayoutlibDelegate
- /*package*/ static void nDrawDoubleRoundRect(long nativeCanvas, float outerLeft,
- float outerTop, float outerRight, float outerBottom, float outerRx, float outerRy,
- float innerLeft, float innerTop, float innerRight, float innerBottom, float innerRx,
- float innerRy, long nativePaint) {
- nDrawDoubleRoundRect(nativeCanvas, outerLeft, outerTop, outerRight, outerBottom,
- new float[]{outerRx, outerRy, outerRx, outerRy, outerRx, outerRy, outerRx, outerRy},
- innerLeft, innerTop, innerRight, innerBottom,
- new float[]{innerRx, innerRy, innerRx, innerRy, innerRx, innerRy, innerRx, innerRy},
- nativePaint);
- }
-
- @LayoutlibDelegate
- /*package*/ static void nDrawDoubleRoundRect(long nativeCanvas, float outerLeft,
- float outerTop, float outerRight, float outerBottom, float[] outerRadii,
- float innerLeft, float innerTop, float innerRight, float innerBottom,
- float[] innerRadii, long nativePaint) {
- draw(nativeCanvas, nativePaint, false /*compositeOnly*/, false /*forceSrcMode*/,
- (graphics, paintDelegate) -> {
- RoundRectangle innerRect = new RoundRectangle(innerLeft, innerTop,
- innerRight - innerLeft, innerBottom - innerTop, innerRadii);
- RoundRectangle outerRect = new RoundRectangle(outerLeft, outerTop,
- outerRight - outerLeft, outerBottom - outerTop, outerRadii);
-
- int style = paintDelegate.getStyle();
-
- // draw
- if (style == Paint.Style.STROKE.nativeInt ||
- style == Paint.Style.FILL_AND_STROKE.nativeInt) {
- graphics.draw(innerRect);
- graphics.draw(outerRect);
- }
-
- if (style == Paint.Style.FILL.nativeInt ||
- style == Paint.Style.FILL_AND_STROKE.nativeInt) {
- Area outerArea = new Area(outerRect);
- Area innerArea = new Area(innerRect);
- outerArea.subtract(innerArea);
- graphics.fill(outerArea);
- }
- });
- }
-
- @LayoutlibDelegate
- public static void nDrawPath(long nativeCanvas, long path, long paint) {
- final Path_Delegate pathDelegate = Path_Delegate.getDelegate(path);
- if (pathDelegate == null) {
- return;
- }
-
- draw(nativeCanvas, paint, false /*compositeOnly*/, false /*forceSrcMode*/,
- (graphics, paintDelegate) -> {
- Shape shape = pathDelegate.getJavaShape();
- Rectangle2D bounds = shape.getBounds2D();
- if (bounds.isEmpty()) {
- // Apple JRE 1.6 doesn't like drawing empty shapes.
- // http://b.android.com/178278
-
- if (pathDelegate.isEmpty()) {
- // This means that the path doesn't have any lines or curves so
- // nothing to draw.
- return;
- }
-
- // The stroke width is not consider for the size of the bounds so,
- // for example, a horizontal line, would be considered as an empty
- // rectangle.
- // If the strokeWidth is not 0, we use it to consider the size of the
- // path as well.
- float strokeWidth = paintDelegate.getStrokeWidth();
- if (strokeWidth <= 0.0f) {
- return;
- }
- bounds.setRect(bounds.getX(), bounds.getY(),
- Math.max(strokeWidth, bounds.getWidth()),
- Math.max(strokeWidth, bounds.getHeight()));
- }
-
- int style = paintDelegate.getStyle();
-
- if (style == Paint.Style.FILL.nativeInt ||
- style == Paint.Style.FILL_AND_STROKE.nativeInt) {
- graphics.fill(shape);
- }
-
- if (style == Paint.Style.STROKE.nativeInt ||
- style == Paint.Style.FILL_AND_STROKE.nativeInt) {
- graphics.draw(shape);
- }
- });
- }
-
- @LayoutlibDelegate
- /*package*/ static void nDrawRegion(long nativeCanvas, long nativeRegion,
- long nativePaint) {
- // FIXME
- Bridge.getLog().fidelityWarning(ILayoutLog.TAG_UNSUPPORTED,
- "Some canvas paths may not be drawn", null, null, null);
- }
-
- @LayoutlibDelegate
- /*package*/ static void nDrawNinePatch(long nativeCanvas, long nativeBitmap, long ninePatch,
- final float dstLeft, final float dstTop, final float dstRight, final float dstBottom,
- long nativePaintOrZero, final int screenDensity, final int bitmapDensity) {
-
- // get the delegate from the native int.
- final Bitmap_Delegate bitmapDelegate = Bitmap_Delegate.getDelegate(nativeBitmap);
- if (bitmapDelegate == null) {
- return;
- }
-
- byte[] c = NinePatch_Delegate.getChunk(ninePatch);
- if (c == null) {
- // not a 9-patch?
- BufferedImage image = bitmapDelegate.getImage();
- drawBitmap(nativeCanvas, bitmapDelegate, nativePaintOrZero, 0, 0, image.getWidth(),
- image.getHeight(), (int) dstLeft, (int) dstTop, (int) dstRight,
- (int) dstBottom);
- return;
- }
-
- final NinePatchChunk chunkObject = NinePatch_Delegate.getChunk(c);
- if (chunkObject == null) {
- return;
- }
-
- Canvas_Delegate canvasDelegate = Canvas_Delegate.getDelegate(nativeCanvas);
- if (canvasDelegate == null) {
- return;
- }
-
- // this one can be null
- Paint_Delegate paintDelegate = Paint_Delegate.getDelegate(nativePaintOrZero);
-
- canvasDelegate.getSnapshot().draw(new GcSnapshot.Drawable() {
- @Override
- public void draw(Graphics2D graphics, Paint_Delegate paint) {
- chunkObject.draw(bitmapDelegate.getImage(), graphics, (int) dstLeft, (int) dstTop,
- (int) (dstRight - dstLeft), (int) (dstBottom - dstTop), screenDensity,
- bitmapDensity);
- }
- }, paintDelegate, true, false);
-
- }
-
- @LayoutlibDelegate
- /*package*/ static void nDrawBitmapMatrix(long nCanvas, long bitmapHandle,
- long nMatrix, long nPaint) {
- // get the delegate from the native int.
- BaseCanvas_Delegate canvasDelegate = sManager.getDelegate(nCanvas);
- if (canvasDelegate == null) {
- return;
- }
-
- // get the delegate from the native int, which can be null
- Paint_Delegate paintDelegate = Paint_Delegate.getDelegate(nPaint);
-
- // get the delegate from the native int.
- Bitmap_Delegate bitmapDelegate = Bitmap_Delegate.getDelegate(bitmapHandle);
- if (bitmapDelegate == null) {
- return;
- }
-
- final BufferedImage image = getImageToDraw(bitmapDelegate, paintDelegate, sBoolOut);
-
- Matrix_Delegate matrixDelegate = Matrix_Delegate.getDelegate(nMatrix);
- if (matrixDelegate == null) {
- return;
- }
-
- final AffineTransform mtx = matrixDelegate.getAffineTransform();
-
- canvasDelegate.getSnapshot().draw((graphics, paint) -> {
- if (paint != null && paint.isFilterBitmap()) {
- graphics.setRenderingHint(RenderingHints.KEY_INTERPOLATION,
- RenderingHints.VALUE_INTERPOLATION_BILINEAR);
- }
-
- //FIXME add support for canvas, screen and bitmap densities.
- graphics.drawImage(image, mtx, null);
- }, paintDelegate, true /*compositeOnly*/, false /*forceSrcMode*/);
- }
-
- @LayoutlibDelegate
- /*package*/ static void nDrawBitmapMesh(long nCanvas, long bitmapHandle,
- int meshWidth, int meshHeight, float[] verts, int vertOffset, int[] colors,
- int colorOffset, long nPaint) {
- // FIXME
- Bridge.getLog().fidelityWarning(ILayoutLog.TAG_UNSUPPORTED,
- "Canvas.drawBitmapMesh is not supported.", null, null, null /*data*/);
- }
-
- @LayoutlibDelegate
- /*package*/ static void nDrawVertices(long nCanvas, int mode, int n,
- float[] verts, int vertOffset,
- float[] texs, int texOffset,
- int[] colors, int colorOffset,
- short[] indices, int indexOffset,
- int indexCount, long nPaint) {
- // FIXME
- Bridge.getLog().fidelityWarning(ILayoutLog.TAG_UNSUPPORTED,
- "Canvas.drawVertices is not supported.", null, null, null /*data*/);
- }
-
- @LayoutlibDelegate
- /*package*/ static void nDrawText(long nativeCanvas, char[] text, int index, int count,
- float startX, float startY, int flags, long paint) {
- drawText(nativeCanvas, text, index, count, startX, startY, flags,
- paint);
- }
-
- @LayoutlibDelegate
- /*package*/ static void nDrawText(long nativeCanvas, String text,
- int start, int end, float x, float y, final int flags, long paint) {
- int count = end - start;
- char[] buffer = TemporaryBuffer.obtain(count);
- TextUtils.getChars(text, start, end, buffer, 0);
-
- nDrawText(nativeCanvas, buffer, 0, count, x, y, flags, paint);
- }
-
- @LayoutlibDelegate
- /*package*/ static void nDrawTextRun(long nativeCanvas, String text,
- int start, int end, int contextStart, int contextEnd,
- float x, float y, boolean isRtl, long paint) {
- int count = end - start;
- char[] buffer = TemporaryBuffer.obtain(count);
- TextUtils.getChars(text, start, end, buffer, 0);
-
- drawText(nativeCanvas, buffer, 0, count, x, y, isRtl ? Paint.BIDI_RTL : Paint.BIDI_LTR,
- paint);
- }
-
- @LayoutlibDelegate
- /*package*/ static void nDrawTextRun(long nativeCanvas, char[] text,
- int start, int count, int contextStart, int contextCount,
- float x, float y, boolean isRtl, long paint,
- long nativeMeasuredText) {
- drawText(nativeCanvas, text, start, count, x, y, isRtl ? Paint.BIDI_RTL : Paint.BIDI_LTR, paint);
- }
-
- @LayoutlibDelegate
- /*package*/ static void nDrawTextOnPath(long nativeCanvas,
- char[] text, int index,
- int count, long path,
- float hOffset,
- float vOffset, int bidiFlags,
- long paint) {
- // FIXME
- Bridge.getLog().fidelityWarning(ILayoutLog.TAG_UNSUPPORTED,
- "Canvas.drawTextOnPath is not supported.", null, null, null /*data*/);
- }
-
- @LayoutlibDelegate
- /*package*/ static void nDrawTextOnPath(long nativeCanvas,
- String text, long path,
- float hOffset,
- float vOffset,
- int bidiFlags, long paint) {
- // FIXME
- Bridge.getLog().fidelityWarning(ILayoutLog.TAG_UNSUPPORTED,
- "Canvas.drawTextOnPath is not supported.", null, null, null /*data*/);
- }
-
- // ---- Private delegate/helper methods ----
-
- /**
- * Executes a {@link GcSnapshot.Drawable} with a given canvas and paint.
- * <p>Note that the drawable may actually be executed several times if there are
- * layers involved (see {@link #saveLayer(RectF, Paint_Delegate, int)}.
- */
- private static void draw(long nCanvas, long nPaint, boolean compositeOnly, boolean forceSrcMode,
- GcSnapshot.Drawable drawable) {
- // get the delegate from the native int.
- BaseCanvas_Delegate canvasDelegate = sManager.getDelegate(nCanvas);
- if (canvasDelegate == null) {
- return;
- }
-
- // get the paint which can be null if nPaint is 0;
- Paint_Delegate paintDelegate = Paint_Delegate.getDelegate(nPaint);
-
- canvasDelegate.getSnapshot().draw(drawable, paintDelegate, compositeOnly, forceSrcMode);
- }
-
- /**
- * Executes a {@link GcSnapshot.Drawable} with a given canvas. No paint object will be provided
- * to {@link GcSnapshot.Drawable#draw(Graphics2D, Paint_Delegate)}.
- * <p>Note that the drawable may actually be executed several times if there are
- * layers involved (see {@link #saveLayer(RectF, Paint_Delegate, int)}.
- */
- private static void draw(long nCanvas, GcSnapshot.Drawable drawable) {
- // get the delegate from the native int.
- BaseCanvas_Delegate canvasDelegate = sManager.getDelegate(nCanvas);
- if (canvasDelegate == null) {
- return;
- }
-
- canvasDelegate.mSnapshot.draw(drawable);
- }
-
- private static void drawText(long nativeCanvas, final char[] text, final int index,
- final int count, final float startX, final float startY, final int bidiFlags,
- long paint) {
-
- draw(nativeCanvas, paint, false /*compositeOnly*/, false /*forceSrcMode*/,
- (graphics, paintDelegate) -> {
- // WARNING: the logic in this method is similar to Paint_Delegate.measureText.
- // Any change to this method should be reflected in Paint.measureText
-
- // Paint.TextAlign indicates how the text is positioned relative to X.
- // LEFT is the default and there's nothing to do.
- float x = startX;
- int limit = index + count;
- if (paintDelegate.getTextAlign() != Paint.Align.LEFT.nativeInt) {
- RectF bounds =
- paintDelegate.measureText(text, index, count, null, 0, bidiFlags);
- float m = bounds.right - bounds.left;
- if (paintDelegate.getTextAlign() == Paint.Align.CENTER.nativeInt) {
- x -= m / 2;
- } else if (paintDelegate.getTextAlign() == Paint.Align.RIGHT.nativeInt) {
- x -= m;
- }
- }
-
- new BidiRenderer(graphics, paintDelegate, text).setRenderLocation(x,
- startY).renderText(index, limit, bidiFlags, null, 0, true);
- });
- }
-
- private static void drawBitmap(long nativeCanvas, Bitmap_Delegate bitmap,
- long nativePaintOrZero, final int sleft, final int stop, final int sright,
- final int sbottom, final int dleft, final int dtop, final int dright,
- final int dbottom) {
- // get the delegate from the native int.
- BaseCanvas_Delegate canvasDelegate = sManager.getDelegate(nativeCanvas);
- if (canvasDelegate == null) {
- return;
- }
-
- // get the paint, which could be null if the int is 0
- Paint_Delegate paintDelegate = Paint_Delegate.getDelegate(nativePaintOrZero);
-
- final BufferedImage image = getImageToDraw(bitmap, paintDelegate, sBoolOut);
-
- draw(nativeCanvas, nativePaintOrZero, true /*compositeOnly*/, sBoolOut[0],
- (graphics, paint) -> {
- if (paint != null && paint.isFilterBitmap()) {
- graphics.setRenderingHint(RenderingHints.KEY_INTERPOLATION,
- RenderingHints.VALUE_INTERPOLATION_BILINEAR);
- }
-
- //FIXME add support for canvas, screen and bitmap densities.
- graphics.drawImage(image, dleft, dtop, dright, dbottom, sleft, stop, sright,
- sbottom, null);
- });
- }
-
- /**
- * Returns a BufferedImage ready for drawing, based on the bitmap and paint delegate.
- * The image returns, through a 1-size boolean array, whether the drawing code should
- * use a SRC composite no matter what the paint says.
- *
- * @param bitmap the bitmap
- * @param paint the paint that will be used to draw
- * @param forceSrcMode whether the composite will have to be SRC
- * @return the image to draw
- */
- private static BufferedImage getImageToDraw(Bitmap_Delegate bitmap, Paint_Delegate paint,
- boolean[] forceSrcMode) {
- BufferedImage image = bitmap.getImage();
- forceSrcMode[0] = false;
-
- // if the bitmap config is alpha_8, then we erase all color value from it
- // before drawing it or apply the texture from the shader if present.
- if (bitmap.getConfig() == Bitmap.Config.ALPHA_8) {
- Shader_Delegate shader = paint.getShader();
- java.awt.Paint javaPaint = null;
- if (shader instanceof BitmapShader_Delegate) {
- javaPaint = shader.getJavaPaint();
- }
-
- fixAlpha8Bitmap(image, javaPaint);
- } else if (!bitmap.hasAlpha()) {
- // hasAlpha is merely a rendering hint. There can in fact be alpha values
- // in the bitmap but it should be ignored at drawing time.
- // There is two ways to do this:
- // - override the composite to be SRC. This can only be used if the composite
- // was going to be SRC or SRC_OVER in the first place
- // - Create a different bitmap to draw in which all the alpha channel values is set
- // to 0xFF.
- if (paint != null) {
- PorterDuff.Mode mode = PorterDuff.intToMode(paint.getPorterDuffMode());
-
- forceSrcMode[0] = mode == PorterDuff.Mode.SRC_OVER || mode == PorterDuff.Mode.SRC;
- }
-
- // if we can't force SRC mode, then create a temp bitmap of TYPE_RGB
- if (!forceSrcMode[0]) {
- image = Bitmap_Delegate.createCopy(image, BufferedImage.TYPE_INT_RGB, 0xFF);
- }
- }
-
- return image;
- }
-
- /**
- * This method will apply the correct color to the passed "only alpha" image. Colors on the
- * passed image will be destroyed.
- * If the passed javaPaint is null, the color will be set to 0. If a paint is passed, it will
- * be used to obtain the color that will be applied.
- * <p/>
- * This will destroy the passed image color channel.
- */
- private static void fixAlpha8Bitmap(final BufferedImage image,
- @Nullable java.awt.Paint javaPaint) {
- int w = image.getWidth();
- int h = image.getHeight();
-
- DataBuffer texture = null;
- if (javaPaint != null) {
- PaintContext context = javaPaint.createContext(ColorModel.getRGBdefault(), null, null,
- new AffineTransform(), null);
- texture = context.getRaster(0, 0, w, h).getDataBuffer();
- }
-
- int[] argb = new int[w * h];
- image.getRGB(0, 0, image.getWidth(), image.getHeight(), argb, 0, image.getWidth());
-
- final int length = argb.length;
- for (int i = 0; i < length; i++) {
- argb[i] &= 0xFF000000;
- if (texture != null) {
- argb[i] |= texture.getElem(i) & 0x00FFFFFF;
- }
- }
-
- image.setRGB(0, 0, w, h, argb, 0, w);
- }
-
- protected int save(int saveFlags) {
- // get the current save count
- int count = mSnapshot.size();
-
- mSnapshot = mSnapshot.save(saveFlags);
-
- // return the old save count
- return count;
- }
-
- protected int saveLayerAlpha(RectF rect, int alpha, int saveFlags) {
- Paint_Delegate paint = new Paint_Delegate();
- paint.setAlpha(alpha);
- return saveLayer(rect, paint, saveFlags);
- }
-
- protected int saveLayer(RectF rect, Paint_Delegate paint, int saveFlags) {
- // get the current save count
- int count = mSnapshot.size();
-
- mSnapshot = mSnapshot.saveLayer(rect, paint, saveFlags);
-
- // return the old save count
- return count;
- }
-
- /**
- * Restores the {@link GcSnapshot} to <var>saveCount</var>
- * @param saveCount the saveCount
- */
- protected void restoreTo(int saveCount) {
- mSnapshot = mSnapshot.restoreTo(saveCount);
- }
-
- /**
- * Restores the top {@link GcSnapshot}
- */
- protected void restore() {
- mSnapshot = mSnapshot.restore();
- }
-
- protected boolean clipRect(float left, float top, float right, float bottom, int regionOp) {
- return mSnapshot.clipRect(left, top, right, bottom, regionOp);
- }
-}
diff --git a/bridge/src/android/graphics/BidiRenderer.java b/bridge/src/android/graphics/BidiRenderer.java
deleted file mode 100644
index 40d350b158..0000000000
--- a/bridge/src/android/graphics/BidiRenderer.java
+++ /dev/null
@@ -1,379 +0,0 @@
-/*
- * Copyright (C) 2013 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.graphics;
-
-import com.android.ide.common.rendering.api.ILayoutLog;
-import com.android.layoutlib.bridge.Bridge;
-
-import android.annotation.NonNull;
-import android.annotation.Nullable;
-import android.graphics.Paint_Delegate.FontInfo;
-import android.icu.lang.UScriptRun;
-import android.icu.text.Bidi;
-import android.icu.text.BidiRun;
-
-import java.awt.Font;
-import java.awt.Graphics2D;
-import java.awt.Toolkit;
-import java.awt.font.FontRenderContext;
-import java.awt.font.GlyphVector;
-import java.awt.geom.AffineTransform;
-import java.awt.geom.Rectangle2D;
-import java.util.Arrays;
-import java.util.LinkedList;
-import java.util.List;
-
-/**
- * Render the text by breaking it into various scripts and using the right font for each script.
- * Can be used to measure the text without actually drawing it.
- */
-@SuppressWarnings("deprecation")
-public class BidiRenderer {
- private static final String JETBRAINS_VENDOR_ID = "JetBrains s.r.o";
- private static final String JAVA_VENDOR = System.getProperty("java.vendor");
- /** When scaleX is bigger than this, we need to apply the workaround for http://b.android.com/211659 */
- private static final double SCALEX_WORKAROUND_LIMIT = 9;
-
- private static class ScriptRun {
- private final int start;
- private final int limit;
- private final Font font;
-
- private ScriptRun(int start, int limit, @NonNull Font font) {
- this.start = start;
- this.limit = limit;
- this.font = font;
- }
- }
-
- private final Graphics2D mGraphics;
- private final Paint_Delegate mPaint;
- private char[] mText;
- // Bounds of the text drawn so far.
- private RectF mBounds;
- private float mBaseline;
- private final Bidi mBidi = new Bidi();
-
-
- /**
- * @param graphics May be null.
- * @param paint The Paint to use to get the fonts. Should not be null.
- * @param text Unidirectional text. Should not be null.
- */
- public BidiRenderer(Graphics2D graphics, Paint_Delegate paint, char[] text) {
- assert (paint != null);
- mGraphics = graphics;
- mPaint = paint;
- mText = text;
- mBounds = new RectF();
- }
-
- /**
- *
- * @param x The x-coordinate of the left edge of where the text should be drawn on the given
- * graphics.
- * @param y The y-coordinate at which to draw the text on the given mGraphics.
- *
- */
- public BidiRenderer setRenderLocation(float x, float y) {
- mBounds.set(x, y, x, y);
- mBaseline = y;
- return this;
- }
-
- /**
- * Perform Bidi Analysis on the text and then render it.
- * <p/>
- * To skip the analysis and render unidirectional text, see {@link
- * #renderText(int, int, boolean, float[], int, boolean)}
- */
- public RectF renderText(int start, int limit, int bidiFlags, float[] advances,
- int advancesIndex, boolean draw) {
- mBidi.setPara(Arrays.copyOfRange(mText, start, limit), (byte)getIcuFlags(bidiFlags), null);
- mText = mBidi.getText();
- for (int i = 0; i < mBidi.countRuns(); i++) {
- BidiRun visualRun = mBidi.getVisualRun(i);
- boolean isRtl = visualRun.getDirection() == Bidi.RTL;
- renderText(visualRun.getStart(), visualRun.getLimit(), isRtl, advances,
- advancesIndex, draw);
- }
- return mBounds;
- }
-
- /**
- * Render unidirectional text.
- * <p/>
- * This method can also be used to measure the width of the text without actually drawing it.
- * <p/>
- * @param start index of the first character
- * @param limit index of the first character that should not be rendered.
- * @param isRtl is the text right-to-left
- * @param advances If not null, then advances for each character to be rendered are returned
- * here.
- * @param advancesIndex index into advances from where the advances need to be filled.
- * @param draw If true and {@code graphics} is not null, draw the rendered text on the graphics
- * at the given co-ordinates
- * @return A rectangle specifying the bounds of the text drawn.
- */
- public RectF renderText(int start, int limit, boolean isRtl, float[] advances,
- int advancesIndex, boolean draw) {
- // We break the text into scripts and then select font based on it and then render each of
- // the script runs.
- for (ScriptRun run : getScriptRuns(mText, start, limit, mPaint.getFonts())) {
- int flag = Font.LAYOUT_NO_LIMIT_CONTEXT | Font.LAYOUT_NO_START_CONTEXT;
- flag |= isRtl ? Font.LAYOUT_RIGHT_TO_LEFT : Font.LAYOUT_LEFT_TO_RIGHT;
- renderScript(run.start, run.limit, run.font, flag, advances, advancesIndex, draw);
- advancesIndex += run.limit - run.start;
- }
- return mBounds;
- }
-
- /**
- * Render a script run to the right of the bounds passed. Use the preferred font to render as
- * much as possible. This also implements a fallback mechanism to render characters that cannot
- * be drawn using the preferred font.
- */
- private void renderScript(int start, int limit, Font preferredFont, int flag,
- float[] advances, int advancesIndex, boolean draw) {
- if (mPaint.getFonts().size() == 0 || preferredFont == null) {
- return;
- }
-
- while (start < limit) {
- int canDisplayUpTo = preferredFont.canDisplayUpTo(mText, start, limit);
- if (canDisplayUpTo == -1) {
- // We can draw all characters in the text.
- render(start, limit, preferredFont, flag, advances, advancesIndex, draw);
- return;
- }
- if (canDisplayUpTo > start) {
- // We can draw something.
- render(start, canDisplayUpTo, preferredFont, flag, advances, advancesIndex, draw);
- advancesIndex += canDisplayUpTo - start;
- start = canDisplayUpTo;
- } else {
- // We can display everything with the preferred font. Search for the font that
- // allows us to display the maximum number of chars
- List<FontInfo> fontInfos = mPaint.getFonts();
- Font bestFont = null;
- int highestUpTo = canDisplayUpTo;
- //noinspection ForLoopReplaceableByForEach
- for (int i = 0; i < fontInfos.size(); i++) {
- Font font = fontInfos.get(i).mFont;
-
- if (preferredFont == font) {
- // We know this font won't work since we've already tested it at the
- // beginning of the loop
- continue;
- }
-
- if (font == null) {
- logFontWarning();
- continue;
- }
-
- canDisplayUpTo = font.canDisplayUpTo(mText, start, limit);
- if (canDisplayUpTo == -1) {
- // This font can dis
- highestUpTo = limit;
- bestFont = font;
- break;
- } else if (canDisplayUpTo > highestUpTo) {
- highestUpTo = canDisplayUpTo;
- bestFont = font;
- // Keep searching in case there is a font that allows to display even
- // more text
- }
- }
-
- if (bestFont != null) {
- render(start, highestUpTo, bestFont, flag, advances, advancesIndex, draw);
- advancesIndex += highestUpTo - start;
- start = highestUpTo;
- } else {
- int charCount = Character.isHighSurrogate(mText[start]) ? 2 : 1;
-
- // No font can display this char. Use the preferred font and skip this char.
- // The char will most probably appear as a box or a blank space. We could,
- // probably, use some heuristics and break the character into the base
- // character and diacritics and then draw it, but it's probably not worth the
- // effort.
- render(start, start + charCount, preferredFont, flag, advances, advancesIndex,
- draw);
- start += charCount;
- advancesIndex += charCount;
- }
- }
- }
- }
-
- private static void logFontWarning() {
- Bridge.getLog().fidelityWarning(ILayoutLog.TAG_BROKEN,
- "Some fonts could not be loaded. The rendering may not be perfect.", null, null,
- null);
- }
-
- /**
- * Renders the text to the right of the bounds with the given font.
- * @param font The font to render the text with.
- */
- private void render(int start, int limit, Font font, int flag, float[] advances,
- int advancesIndex, boolean draw) {
- FontRenderContext frc = mGraphics != null ? mGraphics.getFontRenderContext() :
- Toolkit.getDefaultToolkit().getFontMetrics(font).getFontRenderContext();
-
- boolean frcIsAntialiased = frc.isAntiAliased();
- boolean useAntialiasing = mPaint.isAntiAliased();
-
- if (frcIsAntialiased) {
- if (!useAntialiasing) {
- // The context has antialiasing enabled but the paint does not. We need to
- // disable it
- frc = new FontRenderContext(font.getTransform(), false,
- frc.usesFractionalMetrics());
- } else {
- // In this case both the paint and the context antialising match but we need
- // to check for a bug in the JDK
- // Workaround for http://b.android.com/211659 (disable antialiasing)
- if (font.isTransformed()) {
- AffineTransform transform = font.getTransform();
- if (transform.getScaleX() >= SCALEX_WORKAROUND_LIMIT &&
- JETBRAINS_VENDOR_ID.equals(JAVA_VENDOR)) {
- frc = new FontRenderContext(transform, false, frc.usesFractionalMetrics());
- }
- }
- }
- } else if (useAntialiasing) {
- // The context does not have antialiasing enabled but the paint does. We need to
- // enable it unless we need to avoid the JDK bug
-
- AffineTransform transform = font.getTransform();
- // Workaround for http://b.android.com/211659 (disable antialiasing)
- if (transform.getScaleX() < SCALEX_WORKAROUND_LIMIT ||
- !JETBRAINS_VENDOR_ID.equals(JAVA_VENDOR)) {
- frc = new FontRenderContext(font.getTransform(), true, frc.usesFractionalMetrics());
- }
- }
-
- GlyphVector gv = font.layoutGlyphVector(frc, mText, start, limit, flag);
- int ng = gv.getNumGlyphs();
- int[] ci = gv.getGlyphCharIndices(0, ng, null);
- if (advances != null) {
- for (int i = 0; i < ng; i++) {
- if (mText[ci[i]] == '\uFEFF') {
- // Workaround for bug in JetBrains JDK
- // where the character \uFEFF is associated a glyph with non-zero width
- continue;
- }
- int adv_idx = advancesIndex + ci[i];
- advances[adv_idx] += gv.getGlyphMetrics(i).getAdvanceX();
- }
- }
- if (draw && mGraphics != null) {
- mGraphics.drawGlyphVector(gv, mBounds.right, mBaseline);
- }
-
- // Update the bounds.
- Rectangle2D awtBounds = gv.getLogicalBounds();
- // If the width of the bounds is zero, no text had been drawn earlier. Hence, use the
- // coordinates from the bounds as an offset.
- if (Math.abs(mBounds.right - mBounds.left) == 0) {
- mBounds = awtRectToAndroidRect(awtBounds, mBounds.right, mBaseline, mBounds);
- } else {
- mBounds.union(awtRectToAndroidRect(awtBounds, mBounds.right, mBaseline, null));
- }
- }
-
- // --- Static helper methods ---
-
- private static RectF awtRectToAndroidRect(Rectangle2D awtRec, float offsetX, float offsetY,
- @Nullable RectF destination) {
- float left = (float) awtRec.getX();
- float top = (float) awtRec.getY();
- float right = (float) (left + awtRec.getWidth());
- float bottom = (float) (top + awtRec.getHeight());
- if (destination != null) {
- destination.set(left, top, right, bottom);
- } else {
- destination = new RectF(left, top, right, bottom);
- }
- destination.offset(offsetX, offsetY);
- return destination;
- }
-
- private static List<ScriptRun> getScriptRuns(char[] text, int start, int limit, List<FontInfo> fonts) {
- LinkedList<ScriptRun> scriptRuns = new LinkedList<>();
-
- int count = limit - start;
- UScriptRun uScriptRun = new UScriptRun(text, start, count);
- while (uScriptRun.next()) {
- int scriptStart = uScriptRun.getScriptStart();
- int scriptLimit = uScriptRun.getScriptLimit();
- ScriptRun run = new ScriptRun(
- scriptStart, scriptLimit,
- getScriptFont(text, scriptStart, scriptLimit, fonts));
- scriptRuns.add(run);
- }
- return scriptRuns;
- }
-
- // TODO: Replace this method with one which returns the font based on the scriptCode.
- @NonNull
- private static Font getScriptFont(char[] text, int start, int limit, List<FontInfo> fonts) {
- if (fonts.isEmpty()) {
- logFontWarning();
- // Fallback font in case no font can be loaded
- return Font.getFont(Font.SERIF);
- }
-
- // From all the fonts, select the one that can display the highest number of characters
- Font bestFont = fonts.get(0).mFont;
- int bestFontCount = 0;
- for (FontInfo fontInfo : fonts) {
- int count = fontInfo.mFont.canDisplayUpTo(text, start, limit);
- if (count == -1) {
- // This font can display everything, return this one
- return fontInfo.mFont;
- }
-
- if (count > bestFontCount) {
- bestFontCount = count;
- bestFont = fontInfo.mFont;
- }
- }
-
- return bestFont;
- }
-
- private static int getIcuFlags(int bidiFlag) {
- switch (bidiFlag) {
- case Paint.BIDI_LTR:
- case Paint.BIDI_FORCE_LTR:
- return Bidi.DIRECTION_LEFT_TO_RIGHT;
- case Paint.BIDI_RTL:
- case Paint.BIDI_FORCE_RTL:
- return Bidi.DIRECTION_RIGHT_TO_LEFT;
- case Paint.BIDI_DEFAULT_LTR:
- return Bidi.DIRECTION_DEFAULT_LEFT_TO_RIGHT;
- case Paint.BIDI_DEFAULT_RTL:
- return Bidi.DIRECTION_DEFAULT_RIGHT_TO_LEFT;
- default:
- assert false;
- return Bidi.DIRECTION_DEFAULT_LEFT_TO_RIGHT;
- }
- }
-}
diff --git a/bridge/src/android/graphics/BitmapFactory_Delegate.java b/bridge/src/android/graphics/BitmapFactory_Delegate.java
deleted file mode 100644
index 7a12d382f5..0000000000
--- a/bridge/src/android/graphics/BitmapFactory_Delegate.java
+++ /dev/null
@@ -1,161 +0,0 @@
-/*
- * Copyright (C) 2011 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.graphics;
-
-import com.android.layoutlib.bridge.Bridge;
-import com.android.layoutlib.bridge.impl.DelegateManager;
-import com.android.ninepatch.NinePatchChunk;
-import com.android.resources.Density;
-import com.android.tools.layoutlib.annotations.LayoutlibDelegate;
-
-import android.annotation.Nullable;
-import com.android.layoutlib.bridge.util.NinePatchInputStream;
-import android.graphics.BitmapFactory.Options;
-import android.graphics.Bitmap_Delegate.BitmapCreateFlags;
-
-import java.io.FileDescriptor;
-import java.io.IOException;
-import java.io.InputStream;
-import java.util.EnumSet;
-import java.util.Set;
-
-/**
- * Delegate implementing the native methods of android.graphics.BitmapFactory
- *
- * Through the layoutlib_create tool, the original native methods of BitmapFactory have been
- * replaced by calls to methods of the same name in this delegate class.
- *
- * Because it's a stateless class to start with, there's no need to keep a {@link DelegateManager}
- * around to map int to instance of the delegate.
- *
- */
-/*package*/ class BitmapFactory_Delegate {
-
- // ------ Native Delegates ------
-
- @LayoutlibDelegate
- /*package*/ static Bitmap nativeDecodeStream(InputStream is, byte[] storage,
- @Nullable Rect padding, @Nullable Options opts, long inBitmapHandle,
- long colorSpaceHandle) {
- Bitmap bm = null;
-
- Density density = Density.MEDIUM;
- Set<BitmapCreateFlags> bitmapCreateFlags = EnumSet.of(BitmapCreateFlags.MUTABLE);
- if (opts != null) {
- density = Density.getEnum(opts.inDensity);
- if (opts.inPremultiplied) {
- bitmapCreateFlags.add(BitmapCreateFlags.PREMULTIPLIED);
- }
- opts.inScaled = false;
- }
-
- try {
- if (is instanceof NinePatchInputStream) {
- NinePatchInputStream npis = (NinePatchInputStream) is;
- npis.disableFakeMarkSupport();
-
- // load the bitmap as a nine patch
- com.android.ninepatch.NinePatch ninePatch = com.android.ninepatch.NinePatch.load(
- npis, true /*is9Patch*/, false /*convert*/);
-
- // get the bitmap and chunk objects.
- NinePatchChunk chunk = ninePatch.getChunk();
- bm = Bitmap_Delegate.createBitmap(ninePatch.getImage(),
- NinePatch_Delegate.serialize(chunk), bitmapCreateFlags, density);
-
- if (padding != null) {
- // read the padding
- int[] paddingArray = chunk.getPadding();
- padding.left = paddingArray[0];
- padding.top = paddingArray[1];
- padding.right = paddingArray[2];
- padding.bottom = paddingArray[3];
- }
- } else {
- // load the bitmap directly.
- bm = Bitmap_Delegate.createBitmap(is, bitmapCreateFlags, density);
- }
- } catch (IOException e) {
- Bridge.getLog().error(null, "Failed to load image", e, null, null);
- }
-
- return bm;
- }
-
- @LayoutlibDelegate
- /*package*/ static Bitmap nativeDecodeFileDescriptor(FileDescriptor fd,
- Rect padding, Options opts, long inBitmapHandle, long colorSpaceHandle) {
- if (opts != null) {
- opts.inBitmap = null;
- }
- return null;
- }
-
- @LayoutlibDelegate
- /*package*/ static Bitmap nativeDecodeAsset(long asset, Rect padding, Options opts,
- long inBitmapHandle, long colorSpaceHandle) {
- if (opts != null) {
- opts.inBitmap = null;
- }
- return null;
- }
-
- @LayoutlibDelegate
- /*package*/ static Bitmap nativeDecodeByteArray(byte[] data, int offset,
- int length, Options opts, long inBitmapHandle, long colorSpaceHandle) {
- if (opts != null) {
- opts.inBitmap = null;
- }
- return null;
- }
-
- @LayoutlibDelegate
- /*package*/ static boolean nativeIsSeekable(FileDescriptor fd) {
- return true;
- }
-
- /**
- * Set the newly decoded bitmap's density based on the Options.
- *
- * Copied from {@link BitmapFactory#setDensityFromOptions(Bitmap, Options)}.
- */
- @LayoutlibDelegate
- /*package*/ static void setDensityFromOptions(Bitmap outputBitmap, Options opts) {
- if (outputBitmap == null || opts == null) return;
-
- final int density = opts.inDensity;
- if (density != 0) {
- outputBitmap.setDensity(density);
- final int targetDensity = opts.inTargetDensity;
- if (targetDensity == 0 || density == targetDensity || density == opts.inScreenDensity) {
- return;
- }
-
- // --- Change from original implementation begins ---
- // LayoutLib doesn't scale the nine patch when decoding it. Hence, don't change the
- // density of the source bitmap in case of ninepatch.
-
- if (opts.inScaled) {
- // --- Change from original implementation ends. ---
- outputBitmap.setDensity(targetDensity);
- }
- } else if (opts.inBitmap != null) {
- // bitmap was reused, ensure density is reset
- outputBitmap.setDensity(Bitmap.getDefaultDensity());
- }
- }
-}
diff --git a/bridge/src/android/graphics/BitmapShader_Delegate.java b/bridge/src/android/graphics/BitmapShader_Delegate.java
deleted file mode 100644
index e763824e18..0000000000
--- a/bridge/src/android/graphics/BitmapShader_Delegate.java
+++ /dev/null
@@ -1,259 +0,0 @@
-/*
- * Copyright (C) 2010 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package android.graphics;
-
-import com.android.ide.common.rendering.api.ILayoutLog;
-import com.android.layoutlib.bridge.Bridge;
-import com.android.layoutlib.bridge.impl.DelegateManager;
-import com.android.tools.layoutlib.annotations.LayoutlibDelegate;
-
-import android.graphics.Shader.TileMode;
-
-import java.awt.PaintContext;
-import java.awt.Rectangle;
-import java.awt.RenderingHints;
-import java.awt.geom.AffineTransform;
-import java.awt.geom.NoninvertibleTransformException;
-import java.awt.geom.Rectangle2D;
-import java.awt.image.BufferedImage;
-import java.awt.image.ColorModel;
-import java.awt.image.DataBufferInt;
-import java.awt.image.Raster;
-import java.awt.image.SampleModel;
-
-/**
- * Delegate implementing the native methods of android.graphics.BitmapShader
- *
- * Through the layoutlib_create tool, the original native methods of BitmapShader have been
- * replaced by calls to methods of the same name in this delegate class.
- *
- * This class behaves like the original native implementation, but in Java, keeping previously
- * native data into its own objects and mapping them to int that are sent back and forth between
- * it and the original BitmapShader class.
- *
- * Because this extends {@link Shader_Delegate}, there's no need to use a {@link DelegateManager},
- * as all the Shader classes will be added to the manager owned by {@link Shader_Delegate}.
- *
- * @see Shader_Delegate
- *
- */
-public class BitmapShader_Delegate extends Shader_Delegate {
-
- // ---- delegate data ----
- private java.awt.Paint mJavaPaint;
-
- // ---- Public Helper methods ----
-
- @Override
- public java.awt.Paint getJavaPaint() {
- return mJavaPaint;
- }
-
- @Override
- public boolean isSupported() {
- return true;
- }
-
- @Override
- public String getSupportMessage() {
- // no message since isSupported returns true;
- return null;
- }
-
- // ---- native methods ----
-
- @LayoutlibDelegate
- /*package*/ static long nativeCreate(long nativeMatrix, long bitmapHandle,
- int shaderTileModeX, int shaderTileModeY) {
- Bitmap_Delegate bitmap = Bitmap_Delegate.getDelegate(bitmapHandle);
- if (bitmap == null) {
- return 0;
- }
-
- BitmapShader_Delegate newDelegate = new BitmapShader_Delegate(nativeMatrix,
- bitmap.getImage(),
- Shader_Delegate.getTileMode(shaderTileModeX),
- Shader_Delegate.getTileMode(shaderTileModeY));
- return sManager.addNewDelegate(newDelegate);
- }
-
- // ---- Private delegate/helper methods ----
-
- private BitmapShader_Delegate(long matrix, BufferedImage image,
- TileMode tileModeX, TileMode tileModeY) {
- super(matrix);
- mJavaPaint = new BitmapShaderPaint(image, tileModeX, tileModeY);
- }
-
- private class BitmapShaderPaint implements java.awt.Paint {
- private final BufferedImage mImage;
- private final TileMode mTileModeX;
- private final TileMode mTileModeY;
-
- BitmapShaderPaint(BufferedImage image,
- TileMode tileModeX, TileMode tileModeY) {
- mImage = image;
- mTileModeX = tileModeX;
- mTileModeY = tileModeY;
- }
-
- @Override
- public PaintContext createContext(ColorModel colorModel, Rectangle deviceBounds,
- Rectangle2D userBounds, AffineTransform xform, RenderingHints hints) {
- AffineTransform canvasMatrix;
- try {
- canvasMatrix = xform.createInverse();
- } catch (NoninvertibleTransformException e) {
- Bridge.getLog().fidelityWarning(ILayoutLog.TAG_MATRIX_INVERSE,
- "Unable to inverse matrix in BitmapShader", e, null, null /*data*/);
- canvasMatrix = new AffineTransform();
- }
-
- AffineTransform localMatrix = getLocalMatrix();
- try {
- localMatrix = localMatrix.createInverse();
- } catch (NoninvertibleTransformException e) {
- Bridge.getLog().fidelityWarning(ILayoutLog.TAG_MATRIX_INVERSE,
- "Unable to inverse matrix in BitmapShader", e, null, null /*data*/);
- localMatrix = new AffineTransform();
- }
-
- if (!colorModel.isCompatibleRaster(mImage.getRaster())) {
- // Fallback to the default ARGB color model
- colorModel = ColorModel.getRGBdefault();
- }
-
- return new BitmapShaderContext(canvasMatrix, localMatrix, colorModel);
- }
-
- private class BitmapShaderContext implements PaintContext {
-
- private final AffineTransform mCanvasMatrix;
- private final AffineTransform mLocalMatrix;
- private final ColorModel mColorModel;
-
- public BitmapShaderContext(
- AffineTransform canvasMatrix,
- AffineTransform localMatrix,
- ColorModel colorModel) {
- mCanvasMatrix = canvasMatrix;
- mLocalMatrix = localMatrix;
- mColorModel = colorModel;
- }
-
- @Override
- public void dispose() {
- }
-
- @Override
- public ColorModel getColorModel() {
- return mColorModel;
- }
-
- @Override
- public Raster getRaster(int x, int y, int w, int h) {
- BufferedImage image = new BufferedImage(
- mColorModel, mColorModel.createCompatibleWritableRaster(w, h),
- mColorModel.isAlphaPremultiplied(), null);
-
- int[] data = new int[w*h];
-
- int index = 0;
- float[] pt1 = new float[2];
- float[] pt2 = new float[2];
- for (int iy = 0 ; iy < h ; iy++) {
- for (int ix = 0 ; ix < w ; ix++) {
- // handle the canvas transform
- pt1[0] = x + ix;
- pt1[1] = y + iy;
- mCanvasMatrix.transform(pt1, 0, pt2, 0, 1);
-
- // handle the local matrix.
- pt1[0] = pt2[0];
- pt1[1] = pt2[1];
- mLocalMatrix.transform(pt1, 0, pt2, 0, 1);
-
- data[index++] = getColor(pt2[0], pt2[1]);
- }
- }
-
- DataBufferInt dataBuffer = new DataBufferInt(data, data.length);
- SampleModel colorModel = mColorModel.createCompatibleSampleModel(w, h);
- return Raster.createWritableRaster(colorModel, dataBuffer, null);
- }
- }
-
- /**
- * Returns a color for an arbitrary point.
- */
- private int getColor(float fx, float fy) {
- int x = getCoordinate(Math.round(fx), mImage.getWidth(), mTileModeX);
- int y = getCoordinate(Math.round(fy), mImage.getHeight(), mTileModeY);
-
- return mImage.getRGB(x, y);
- }
-
- private int getCoordinate(int i, int size, TileMode mode) {
- if (i < 0) {
- switch (mode) {
- case CLAMP:
- i = 0;
- break;
- case REPEAT:
- i = size - 1 - (-i % size);
- break;
- case MIRROR:
- // this is the same as the positive side, just make the value positive
- // first.
- i = -i;
- int count = i / size;
- i = i % size;
-
- if ((count % 2) == 1) {
- i = size - 1 - i;
- }
- break;
- }
- } else if (i >= size) {
- switch (mode) {
- case CLAMP:
- i = size - 1;
- break;
- case REPEAT:
- i = i % size;
- break;
- case MIRROR:
- int count = i / size;
- i = i % size;
-
- if ((count % 2) == 1) {
- i = size - 1 - i;
- }
- break;
- }
- }
-
- return i;
- }
-
-
- @Override
- public int getTransparency() {
- return java.awt.Paint.TRANSLUCENT;
- }
- }
-}
diff --git a/bridge/src/android/graphics/Bitmap_Delegate.java b/bridge/src/android/graphics/Bitmap_Delegate.java
deleted file mode 100644
index bfd00c5e8f..0000000000
--- a/bridge/src/android/graphics/Bitmap_Delegate.java
+++ /dev/null
@@ -1,760 +0,0 @@
-/*
- * Copyright (C) 2010 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package android.graphics;
-
-import com.android.ide.common.rendering.api.AssetRepository;
-import com.android.ide.common.rendering.api.ILayoutLog;
-import com.android.ide.common.rendering.api.RenderResources;
-import com.android.ide.common.rendering.api.ResourceValue;
-import com.android.layoutlib.bridge.Bridge;
-import com.android.layoutlib.bridge.android.BridgeContext;
-import com.android.layoutlib.bridge.impl.DelegateManager;
-import com.android.layoutlib.bridge.impl.RenderAction;
-import com.android.resources.Density;
-import com.android.resources.ResourceType;
-import com.android.tools.layoutlib.annotations.LayoutlibDelegate;
-
-import android.annotation.Nullable;
-import android.graphics.Bitmap.Config;
-import android.hardware.HardwareBuffer;
-import android.os.Parcel;
-
-import java.awt.Graphics2D;
-import java.awt.image.BufferedImage;
-import java.io.IOException;
-import java.io.InputStream;
-import java.io.OutputStream;
-import java.nio.Buffer;
-import java.util.Arrays;
-import java.util.EnumSet;
-import java.util.Set;
-
-import javax.imageio.ImageIO;
-import libcore.util.NativeAllocationRegistry_Delegate;
-
-import static android.content.res.AssetManager.ACCESS_STREAMING;
-
-/**
- * Delegate implementing the native methods of android.graphics.Bitmap
- *
- * Through the layoutlib_create tool, the original native methods of Bitmap have been replaced
- * by calls to methods of the same name in this delegate class.
- *
- * This class behaves like the original native implementation, but in Java, keeping previously
- * native data into its own objects and mapping them to int that are sent back and forth between
- * it and the original Bitmap class.
- *
- * @see DelegateManager
- *
- */
-public final class Bitmap_Delegate {
-
- public enum BitmapCreateFlags {
- NONE, PREMULTIPLIED, MUTABLE
- }
-
- // ---- delegate manager ----
- private static final DelegateManager<Bitmap_Delegate> sManager =
- new DelegateManager<>(Bitmap_Delegate.class);
- private static long sFinalizer = -1;
-
- // ---- delegate helper data ----
-
- // ---- delegate data ----
- private final Config mConfig;
- private final BufferedImage mImage;
- private boolean mHasAlpha = true;
- private boolean mHasMipMap = false; // TODO: check the default.
- private boolean mIsPremultiplied = true;
- private int mGenerationId = 0;
- private boolean mIsMutable;
-
-
- // ---- Public Helper methods ----
-
- /**
- * Returns the native delegate associated to a given an int referencing a {@link Bitmap} object.
- */
- public static Bitmap_Delegate getDelegate(long native_bitmap) {
- return sManager.getDelegate(native_bitmap);
- }
-
- /**
- * Creates and returns a {@link Bitmap} initialized with the given stream content.
- *
- * @param input the stream from which to read the bitmap content
- * @param isMutable whether the bitmap is mutable
- * @param density the density associated with the bitmap
- *
- * @see Bitmap#isMutable()
- * @see Bitmap#getDensity()
- */
- public static Bitmap createBitmap(@Nullable InputStream input, boolean isMutable,
- Density density) throws IOException {
- return createBitmap(input, getPremultipliedBitmapCreateFlags(isMutable), density);
- }
-
- /**
- * Creates and returns a {@link Bitmap} initialized with the given file content.
- *
- * @param input the file from which to read the bitmap content
- * @param density the density associated with the bitmap
- *
- * @see Bitmap#isPremultiplied()
- * @see Bitmap#isMutable()
- * @see Bitmap#getDensity()
- */
- static Bitmap createBitmap(@Nullable InputStream input, Set<BitmapCreateFlags> createFlags,
- Density density) throws IOException {
- // create a delegate with the content of the file.
- BufferedImage image = input == null ? null : ImageIO.read(input);
- if (image == null) {
- // There was a problem decoding the image, or the decoder isn't registered. Webp maybe.
- // Replace with a broken image icon.
- BridgeContext currentContext = RenderAction.getCurrentContext();
- if (currentContext != null) {
- RenderResources resources = currentContext.getRenderResources();
- ResourceValue broken = resources.getResolvedResource(
- BridgeContext.createFrameworkResourceReference(
- ResourceType.DRAWABLE, "ic_menu_report_image"));
- AssetRepository assetRepository = currentContext.getAssets().getAssetRepository();
- try (InputStream stream =
- assetRepository.openNonAsset(0, broken.getValue(), ACCESS_STREAMING)) {
- if (stream != null) {
- image = ImageIO.read(stream);
- }
- }
- }
- }
- Bitmap_Delegate delegate = new Bitmap_Delegate(image, Config.ARGB_8888);
- delegate.mIsMutable = createFlags.contains(BitmapCreateFlags.MUTABLE);
-
- return createBitmap(delegate, createFlags, density.getDpiValue(), null);
- }
-
- /**
- * Creates and returns a {@link Bitmap} initialized with the given {@link BufferedImage}
- *
- * @param image the bitmap content
- * @param isMutable whether the bitmap is mutable
- * @param density the density associated with the bitmap
- *
- * @see Bitmap#isMutable()
- * @see Bitmap#getDensity()
- */
- public static Bitmap createBitmap(BufferedImage image, boolean isMutable, Density density) {
- return createBitmap(image, getPremultipliedBitmapCreateFlags(isMutable), density);
- }
-
- /**
- * Creates and returns a {@link Bitmap} initialized with the given {@link BufferedImage}
- *
- * @param image the bitmap content
- * @param density the density associated with the bitmap
- *
- * @see Bitmap#isPremultiplied()
- * @see Bitmap#isMutable()
- * @see Bitmap#getDensity()
- */
- public static Bitmap createBitmap(BufferedImage image, Set<BitmapCreateFlags> createFlags,
- Density density) {
- return createBitmap(image, null, createFlags, density);
- }
-
- /**
- * Creates and returns a {@link Bitmap} initialized with the given {@link BufferedImage}
- *
- * @param image the bitmap content
- * @param ninePatchChunk serialized ninepatch data
- * @param density the density associated with the bitmap
- *
- * @see Bitmap#isPremultiplied()
- * @see Bitmap#isMutable()
- * @see Bitmap#getDensity()
- */
- public static Bitmap createBitmap(BufferedImage image, byte[] ninePatchChunk,
- Set<BitmapCreateFlags> createFlags, Density density) {
- // create a delegate with the given image.
- Bitmap_Delegate delegate = new Bitmap_Delegate(image, Config.ARGB_8888);
- delegate.mIsMutable = createFlags.contains(BitmapCreateFlags.MUTABLE);
-
- return createBitmap(delegate, createFlags, density.getDpiValue(), ninePatchChunk);
- }
-
- private static int getBufferedImageType() {
- return BufferedImage.TYPE_INT_ARGB;
- }
-
- /**
- * Returns the {@link BufferedImage} used by the delegate of the given {@link Bitmap}.
- */
- public BufferedImage getImage() {
- return mImage;
- }
-
- /**
- * Returns the Android bitmap config. Note that this not the config of the underlying
- * Java2D bitmap.
- */
- public Config getConfig() {
- return mConfig;
- }
-
- /**
- * Returns the hasAlpha rendering hint
- * @return true if the bitmap alpha should be used at render time
- */
- public boolean hasAlpha() {
- return mHasAlpha && mConfig != Config.RGB_565;
- }
-
- /**
- * Update the generationId.
- *
- * @see Bitmap#getGenerationId()
- */
- public void change() {
- mGenerationId++;
- }
-
- // ---- native methods ----
-
- @LayoutlibDelegate
- /*package*/ static Bitmap nativeCreate(int[] colors, int offset, int stride, int width,
- int height, int nativeConfig, boolean isMutable, long nativeColorSpace) {
- int imageType = getBufferedImageType();
-
- // create the image
- BufferedImage image = new BufferedImage(width, height, imageType);
-
- if (colors != null) {
- image.setRGB(0, 0, width, height, colors, offset, stride);
- }
-
- // create a delegate with the content of the stream.
- Bitmap_Delegate delegate = new Bitmap_Delegate(image, Config.nativeToConfig(nativeConfig));
- delegate.mIsMutable = isMutable;
-
- return createBitmap(delegate, getPremultipliedBitmapCreateFlags(isMutable),
- Bitmap.getDefaultDensity(), null);
- }
-
- @LayoutlibDelegate
- /*package*/ static Bitmap nativeCopy(long srcBitmap, int nativeConfig, boolean isMutable) {
- Bitmap_Delegate srcBmpDelegate = sManager.getDelegate(srcBitmap);
- if (srcBmpDelegate == null) {
- return null;
- }
-
- BufferedImage srcImage = srcBmpDelegate.getImage();
-
- int width = srcImage.getWidth();
- int height = srcImage.getHeight();
-
- int imageType = getBufferedImageType();
-
- // create the image
- BufferedImage image = new BufferedImage(width, height, imageType);
-
- // copy the source image into the image.
- int[] argb = new int[width * height];
- srcImage.getRGB(0, 0, width, height, argb, 0, width);
- image.setRGB(0, 0, width, height, argb, 0, width);
-
- // create a delegate with the content of the stream.
- Bitmap_Delegate delegate = new Bitmap_Delegate(image, Config.nativeToConfig(nativeConfig));
- delegate.mIsMutable = isMutable;
-
- return createBitmap(delegate, getPremultipliedBitmapCreateFlags(isMutable),
- Bitmap.getDefaultDensity(), null);
- }
-
- @LayoutlibDelegate
- /*package*/ static Bitmap nativeCopyAshmem(long nativeSrcBitmap) {
- // Unused method; no implementation provided.
- assert false;
- return null;
- }
-
- @LayoutlibDelegate
- /*package*/ static Bitmap nativeCopyAshmemConfig(long nativeSrcBitmap, int nativeConfig) {
- // Unused method; no implementation provided.
- assert false;
- return null;
- }
-
- @LayoutlibDelegate
- /*package*/ static long nativeGetNativeFinalizer() {
- synchronized (Bitmap_Delegate.class) {
- if (sFinalizer == -1) {
- sFinalizer = NativeAllocationRegistry_Delegate.createFinalizer(sManager::removeJavaReferenceFor);
- }
- return sFinalizer;
- }
- }
-
- @LayoutlibDelegate
- /*package*/ static void nativeRecycle(long nativeBitmap) {
- // In our case recycle() is a no-op. We will let the finalizer to dispose the bitmap.
- }
-
- @LayoutlibDelegate
- /*package*/ static void nativeReconfigure(long nativeBitmap, int width, int height,
- int config, boolean isPremultiplied) {
- Bridge.getLog().error(ILayoutLog.TAG_UNSUPPORTED,
- "Bitmap.reconfigure() is not supported", null, null /*data*/);
- }
-
- @LayoutlibDelegate
- /*package*/ static boolean nativeCompress(long nativeBitmap, int format, int quality,
- OutputStream stream, byte[] tempStorage) {
- Bridge.getLog().error(ILayoutLog.TAG_UNSUPPORTED,
- "Bitmap.compress() is not supported", null, null /*data*/);
- return true;
- }
-
- @LayoutlibDelegate
- /*package*/ static void nativeErase(long nativeBitmap, int color) {
- // get the delegate from the native int.
- Bitmap_Delegate delegate = sManager.getDelegate(nativeBitmap);
- if (delegate == null) {
- return;
- }
-
- BufferedImage image = delegate.mImage;
-
- Graphics2D g = image.createGraphics();
- try {
- g.setColor(new java.awt.Color(color, true));
-
- g.fillRect(0, 0, image.getWidth(), image.getHeight());
- } finally {
- g.dispose();
- }
- }
-
- @LayoutlibDelegate
- /*package*/ static void nativeErase(long nativeBitmap, long colorSpacePtr, long color) {
- nativeErase(nativeBitmap, Color.toArgb(color));
- }
-
- @LayoutlibDelegate
- /*package*/ static int nativeRowBytes(long nativeBitmap) {
- // get the delegate from the native int.
- Bitmap_Delegate delegate = sManager.getDelegate(nativeBitmap);
- if (delegate == null) {
- return 0;
- }
-
- return delegate.mImage.getWidth();
- }
-
- @LayoutlibDelegate
- /*package*/ static int nativeConfig(long nativeBitmap) {
- // get the delegate from the native int.
- Bitmap_Delegate delegate = sManager.getDelegate(nativeBitmap);
- if (delegate == null) {
- return 0;
- }
-
- return delegate.mConfig.nativeInt;
- }
-
- @LayoutlibDelegate
- /*package*/ static boolean nativeHasAlpha(long nativeBitmap) {
- // get the delegate from the native int.
- Bitmap_Delegate delegate = sManager.getDelegate(nativeBitmap);
- return delegate == null || delegate.mHasAlpha;
-
- }
-
- @LayoutlibDelegate
- /*package*/ static boolean nativeHasMipMap(long nativeBitmap) {
- // get the delegate from the native int.
- Bitmap_Delegate delegate = sManager.getDelegate(nativeBitmap);
- return delegate == null || delegate.mHasMipMap;
-
- }
-
- @LayoutlibDelegate
- /*package*/ static int nativeGetPixel(long nativeBitmap, int x, int y) {
- // get the delegate from the native int.
- Bitmap_Delegate delegate = sManager.getDelegate(nativeBitmap);
- if (delegate == null) {
- return 0;
- }
-
- return delegate.mImage.getRGB(x, y);
- }
-
- @LayoutlibDelegate
- /*package*/ static long nativeGetColor(long nativeBitmap, int x, int y) {
- return nativeGetPixel(nativeBitmap, x, y);
- }
-
- @LayoutlibDelegate
- /*package*/ static void nativeGetPixels(long nativeBitmap, int[] pixels, int offset,
- int stride, int x, int y, int width, int height) {
- Bitmap_Delegate delegate = sManager.getDelegate(nativeBitmap);
- if (delegate == null) {
- return;
- }
-
- delegate.getImage().getRGB(x, y, width, height, pixels, offset, stride);
- }
-
-
- @LayoutlibDelegate
- /*package*/ static void nativeSetPixel(long nativeBitmap, int x, int y, int color) {
- Bitmap_Delegate delegate = sManager.getDelegate(nativeBitmap);
- if (delegate == null) {
- return;
- }
-
- delegate.getImage().setRGB(x, y, color);
- }
-
- @LayoutlibDelegate
- /*package*/ static void nativeSetPixels(long nativeBitmap, int[] colors, int offset,
- int stride, int x, int y, int width, int height) {
- Bitmap_Delegate delegate = sManager.getDelegate(nativeBitmap);
- if (delegate == null) {
- return;
- }
-
- delegate.getImage().setRGB(x, y, width, height, colors, offset, stride);
- }
-
- @LayoutlibDelegate
- /*package*/ static void nativeCopyPixelsToBuffer(long nativeBitmap, Buffer dst) {
- // FIXME implement native delegate
- Bridge.getLog().fidelityWarning(ILayoutLog.TAG_UNSUPPORTED,
- "Bitmap.copyPixelsToBuffer is not supported.", null, null, null /*data*/);
- }
-
- @LayoutlibDelegate
- /*package*/ static void nativeCopyPixelsFromBuffer(long nb, Buffer src) {
- // FIXME implement native delegate
- Bridge.getLog().fidelityWarning(ILayoutLog.TAG_UNSUPPORTED,
- "Bitmap.copyPixelsFromBuffer is not supported.", null, null, null /*data*/);
- }
-
- @LayoutlibDelegate
- /*package*/ static int nativeGenerationId(long nativeBitmap) {
- Bitmap_Delegate delegate = sManager.getDelegate(nativeBitmap);
- if (delegate == null) {
- return 0;
- }
-
- return delegate.mGenerationId;
- }
-
- @LayoutlibDelegate
- /*package*/ static Bitmap nativeCreateFromParcel(Parcel p) {
- // This is only called by Bitmap.CREATOR (Parcelable.Creator<Bitmap>), which is only
- // used during aidl call so really this should not be called.
- Bridge.getLog().error(ILayoutLog.TAG_UNSUPPORTED,
- "AIDL is not suppored, and therefore Bitmaps cannot be created from parcels.",
- null, null /*data*/);
- return null;
- }
-
- @LayoutlibDelegate
- /*package*/ static boolean nativeWriteToParcel(long nativeBitmap, int density, Parcel p) {
- // This is only called when sending a bitmap through aidl, so really this should not
- // be called.
- Bridge.getLog().error(ILayoutLog.TAG_UNSUPPORTED,
- "AIDL is not suppored, and therefore Bitmaps cannot be written to parcels.",
- null, null /*data*/);
- return false;
- }
-
- @LayoutlibDelegate
- /*package*/ static Bitmap nativeExtractAlpha(long nativeBitmap, long nativePaint,
- int[] offsetXY) {
- Bitmap_Delegate bitmap = sManager.getDelegate(nativeBitmap);
- if (bitmap == null) {
- return null;
- }
-
- // get the paint which can be null if nativePaint is 0.
- Paint_Delegate paint = Paint_Delegate.getDelegate(nativePaint);
-
- if (paint != null && paint.getMaskFilter() != null) {
- Bridge.getLog().fidelityWarning(ILayoutLog.TAG_MASKFILTER,
- "MaskFilter not supported in Bitmap.extractAlpha",
- null, null, null /*data*/);
- }
-
- int alpha = paint != null ? paint.getAlpha() : 0xFF;
- BufferedImage image = createCopy(bitmap.getImage(), BufferedImage.TYPE_INT_ARGB, alpha);
-
- // create the delegate. The actual Bitmap config is only an alpha channel
- Bitmap_Delegate delegate = new Bitmap_Delegate(image, Config.ALPHA_8);
- delegate.mIsMutable = true;
-
- // the density doesn't matter, it's set by the Java method.
- return createBitmap(delegate, EnumSet.of(BitmapCreateFlags.MUTABLE),
- Density.DEFAULT_DENSITY /*density*/, null);
- }
-
- @LayoutlibDelegate
- /*package*/ static boolean nativeIsPremultiplied(long nativeBitmap) {
- // get the delegate from the native int.
- Bitmap_Delegate delegate = sManager.getDelegate(nativeBitmap);
- return delegate != null && delegate.mIsPremultiplied;
-
- }
-
- @LayoutlibDelegate
- /*package*/ static void nativeSetPremultiplied(long nativeBitmap, boolean isPremul) {
- // get the delegate from the native int.
- Bitmap_Delegate delegate = sManager.getDelegate(nativeBitmap);
- if (delegate == null) {
- return;
- }
-
- delegate.mIsPremultiplied = isPremul;
- }
-
- @LayoutlibDelegate
- /*package*/ static void nativeSetHasAlpha(long nativeBitmap, boolean hasAlpha,
- boolean isPremul) {
- // get the delegate from the native int.
- Bitmap_Delegate delegate = sManager.getDelegate(nativeBitmap);
- if (delegate == null) {
- return;
- }
-
- delegate.mHasAlpha = hasAlpha;
- }
-
- @LayoutlibDelegate
- /*package*/ static void nativeSetHasMipMap(long nativeBitmap, boolean hasMipMap) {
- // get the delegate from the native int.
- Bitmap_Delegate delegate = sManager.getDelegate(nativeBitmap);
- if (delegate == null) {
- return;
- }
-
- delegate.mHasMipMap = hasMipMap;
- }
-
- @LayoutlibDelegate
- /*package*/ static boolean nativeSameAs(long nb0, long nb1) {
- Bitmap_Delegate delegate1 = sManager.getDelegate(nb0);
- if (delegate1 == null) {
- return false;
- }
-
- Bitmap_Delegate delegate2 = sManager.getDelegate(nb1);
- if (delegate2 == null) {
- return false;
- }
-
- BufferedImage image1 = delegate1.getImage();
- BufferedImage image2 = delegate2.getImage();
- if (delegate1.mConfig != delegate2.mConfig ||
- image1.getWidth() != image2.getWidth() ||
- image1.getHeight() != image2.getHeight()) {
- return false;
- }
-
- // get the internal data
- int w = image1.getWidth();
- int h = image2.getHeight();
- int[] argb1 = new int[w*h];
- int[] argb2 = new int[w*h];
-
- image1.getRGB(0, 0, w, h, argb1, 0, w);
- image2.getRGB(0, 0, w, h, argb2, 0, w);
-
- // compares
- if (delegate1.mConfig == Config.ALPHA_8) {
- // in this case we have to manually compare the alpha channel as the rest is garbage.
- final int length = w*h;
- for (int i = 0 ; i < length ; i++) {
- if ((argb1[i] & 0xFF000000) != (argb2[i] & 0xFF000000)) {
- return false;
- }
- }
- return true;
- }
-
- return Arrays.equals(argb1, argb2);
- }
-
- @LayoutlibDelegate
- /*package*/ static int nativeGetAllocationByteCount(long nativeBitmap) {
- // get the delegate from the native int.
- Bitmap_Delegate delegate = sManager.getDelegate(nativeBitmap);
- if (delegate == null) {
- return 0;
- }
- int size = nativeRowBytes(nativeBitmap) * delegate.mImage.getHeight();
- return size < 0 ? Integer.MAX_VALUE : size;
-
- }
-
- @LayoutlibDelegate
- /*package*/ static void nativePrepareToDraw(long nativeBitmap) {
- // do nothing as Bitmap_Delegate does not have caches
- }
-
- @LayoutlibDelegate
- /*package*/ static Bitmap nativeCopyPreserveInternalConfig(long nativeBitmap) {
- Bitmap_Delegate srcBmpDelegate = sManager.getDelegate(nativeBitmap);
- if (srcBmpDelegate == null) {
- return null;
- }
-
- BufferedImage srcImage = srcBmpDelegate.getImage();
-
- // create the image
- BufferedImage image = new BufferedImage(srcImage.getColorModel(), srcImage.copyData(null),
- srcImage.isAlphaPremultiplied(), null);
-
- // create a delegate with the content of the stream.
- Bitmap_Delegate delegate = new Bitmap_Delegate(image, srcBmpDelegate.getConfig());
- delegate.mIsMutable = srcBmpDelegate.mIsMutable;
-
- return createBitmap(delegate, EnumSet.of(BitmapCreateFlags.NONE),
- Bitmap.getDefaultDensity(), null);
- }
-
- @LayoutlibDelegate
- /*package*/ static Bitmap nativeWrapHardwareBufferBitmap(HardwareBuffer buffer,
- long nativeColorSpace) {
- Bridge.getLog().error(ILayoutLog.TAG_UNSUPPORTED,
- "Bitmap.nativeWrapHardwareBufferBitmap() is not supported", null, null, null);
- return null;
- }
-
- @LayoutlibDelegate
- /*package*/ static boolean nativeIsSRGB(long nativeBitmap) {
- Bridge.getLog().error(ILayoutLog.TAG_UNSUPPORTED,
- "Color spaces are not supported", null, null /*data*/);
- return false;
- }
-
- @LayoutlibDelegate
- /*package*/ static ColorSpace nativeComputeColorSpace(long nativePtr) {
- Bridge.getLog().error(ILayoutLog.TAG_UNSUPPORTED,
- "Color spaces are not supported", null, null /*data*/);
- return null;
- }
-
- @LayoutlibDelegate
- /*package*/ static void nativeSetColorSpace(long nativePtr, long nativeColorSpace) {
- Bridge.getLog().error(ILayoutLog.TAG_UNSUPPORTED,
- "Color spaces are not supported", null, null /*data*/);
- }
-
- @LayoutlibDelegate
- /*package*/ static boolean nativeIsSRGBLinear(long nativePtr) {
- Bridge.getLog().error(ILayoutLog.TAG_UNSUPPORTED,
- "Color spaces are not supported", null, null /*data*/);
- return false;
- }
-
- @LayoutlibDelegate
- /*package*/ static void nativeSetImmutable(long nativePtr) {
- Bitmap_Delegate bmpDelegate = sManager.getDelegate(nativePtr);
- if (bmpDelegate == null) {
- return;
- }
- bmpDelegate.mIsMutable = false;
- }
-
- @LayoutlibDelegate
- /*package*/ static boolean nativeIsImmutable(long nativePtr) {
- Bitmap_Delegate bmpDelegate = sManager.getDelegate(nativePtr);
- if (bmpDelegate == null) {
- return false;
- }
- return !bmpDelegate.mIsMutable;
- }
-
- @LayoutlibDelegate
- /*package*/ static HardwareBuffer nativeGetHardwareBuffer(long nativeBitmap) {
- Bridge.getLog().error(ILayoutLog.TAG_UNSUPPORTED,
- "HardwareBuffer is not supported", null, null /*data*/);
- return null;
- }
-
- // ---- Private delegate/helper methods ----
-
- private Bitmap_Delegate(BufferedImage image, Config config) {
- mImage = image;
- mConfig = config;
- }
-
- private static Bitmap createBitmap(Bitmap_Delegate delegate,
- Set<BitmapCreateFlags> createFlags, int density, byte[] ninePatchChunk) {
- // get its native_int
- long nativeInt = sManager.addNewDelegate(delegate);
-
- int width = delegate.mImage.getWidth();
- int height = delegate.mImage.getHeight();
- boolean isPremultiplied = createFlags.contains(BitmapCreateFlags.PREMULTIPLIED);
-
- // and create/return a new Bitmap with it
- return new Bitmap(nativeInt, width, height, density, isPremultiplied,
- ninePatchChunk, null /* layoutBounds */, true /* fromMalloc */);
- }
-
- private static Set<BitmapCreateFlags> getPremultipliedBitmapCreateFlags(boolean isMutable) {
- Set<BitmapCreateFlags> createFlags = EnumSet.of(BitmapCreateFlags.PREMULTIPLIED);
- if (isMutable) {
- createFlags.add(BitmapCreateFlags.MUTABLE);
- }
- return createFlags;
- }
-
- /**
- * Creates and returns a copy of a given BufferedImage.
- * <p/>
- * if alpha is different than 255, then it is applied to the alpha channel of each pixel.
- *
- * @param image the image to copy
- * @param imageType the type of the new image
- * @param alpha an optional alpha modifier
- * @return a new BufferedImage
- */
- /*package*/ static BufferedImage createCopy(BufferedImage image, int imageType, int alpha) {
- int w = image.getWidth();
- int h = image.getHeight();
-
- BufferedImage result = new BufferedImage(w, h, imageType);
-
- int[] argb = new int[w * h];
- image.getRGB(0, 0, image.getWidth(), image.getHeight(), argb, 0, image.getWidth());
-
- if (alpha != 255) {
- final int length = argb.length;
- for (int i = 0 ; i < length; i++) {
- int a = (argb[i] >>> 24 * alpha) / 255;
- argb[i] = (a << 24) | (argb[i] & 0x00FFFFFF);
- }
- }
-
- result.setRGB(0, 0, w, h, argb, 0, w);
-
- return result;
- }
-
-}
diff --git a/bridge/src/android/graphics/BlendComposite.java b/bridge/src/android/graphics/BlendComposite.java
deleted file mode 100644
index 5cc964aee7..0000000000
--- a/bridge/src/android/graphics/BlendComposite.java
+++ /dev/null
@@ -1,287 +0,0 @@
-/*
- * Copyright (C) 2014 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.graphics;
-
-import java.awt.Composite;
-import java.awt.CompositeContext;
-import java.awt.RenderingHints;
-import java.awt.image.ColorModel;
-import java.awt.image.DataBuffer;
-import java.awt.image.Raster;
-import java.awt.image.WritableRaster;
-
-/*
- * (non-Javadoc)
- * The class is adapted from a demo tool for Blending Modes written by
- * Romain Guy (romainguy@android.com). The tool is available at
- * http://www.curious-creature.org/2006/09/20/new-blendings-modes-for-java2d/
- *
- * This class has been adapted for applying color filters. When applying color filters, the src
- * image should not extend beyond the dest image, but in our implementation of the filters, it does.
- * To compensate for the effect, we recompute the alpha value of the src image before applying
- * the color filter as it should have been applied.
- */
-public final class BlendComposite implements Composite {
- public enum BlendingMode {
- MULTIPLY(),
- SCREEN(),
- DARKEN(),
- LIGHTEN(),
- OVERLAY(),
- ADD();
-
- private final BlendComposite mComposite;
-
- BlendingMode() {
- mComposite = new BlendComposite(this);
- }
-
- BlendComposite getBlendComposite() {
- return mComposite;
- }
- }
-
- private float alpha;
- private BlendingMode mode;
-
- private BlendComposite(BlendingMode mode) {
- this(mode, 1.0f);
- }
-
- private BlendComposite(BlendingMode mode, float alpha) {
- this.mode = mode;
- setAlpha(alpha);
- }
-
- public static BlendComposite getInstance(BlendingMode mode) {
- return mode.getBlendComposite();
- }
-
- public static BlendComposite getInstance(BlendingMode mode, float alpha) {
- if (alpha > 0.9999f) {
- return getInstance(mode);
- }
- return new BlendComposite(mode, alpha);
- }
-
- public float getAlpha() {
- return alpha;
- }
-
- public BlendingMode getMode() {
- return mode;
- }
-
- private void setAlpha(float alpha) {
- if (alpha < 0.0f || alpha > 1.0f) {
- assert false : "alpha must be comprised between 0.0f and 1.0f";
- alpha = Math.min(alpha, 1.0f);
- alpha = Math.max(alpha, 0.0f);
- }
-
- this.alpha = alpha;
- }
-
- @Override
- public int hashCode() {
- return Float.floatToIntBits(alpha) * 31 + mode.ordinal();
- }
-
- @Override
- public boolean equals(Object obj) {
- if (!(obj instanceof BlendComposite)) {
- return false;
- }
-
- BlendComposite bc = (BlendComposite) obj;
-
- return mode == bc.mode && alpha == bc.alpha;
- }
-
- public CompositeContext createContext(ColorModel srcColorModel,
- ColorModel dstColorModel,
- RenderingHints hints) {
- return new BlendingContext(this);
- }
-
- private static final class BlendingContext implements CompositeContext {
- private final Blender blender;
- private final BlendComposite composite;
-
- private BlendingContext(BlendComposite composite) {
- this.composite = composite;
- this.blender = Blender.getBlenderFor(composite);
- }
-
- public void dispose() {
- }
-
- public void compose(Raster src, Raster dstIn, WritableRaster dstOut) {
- if (src.getSampleModel().getDataType() != DataBuffer.TYPE_INT ||
- dstIn.getSampleModel().getDataType() != DataBuffer.TYPE_INT ||
- dstOut.getSampleModel().getDataType() != DataBuffer.TYPE_INT) {
- throw new IllegalStateException(
- "Source and destination must store pixels as INT.");
- }
-
- int width = Math.min(src.getWidth(), dstIn.getWidth());
- int height = Math.min(src.getHeight(), dstIn.getHeight());
-
- float alpha = composite.getAlpha();
-
- int[] srcPixel = new int[4];
- int[] dstPixel = new int[4];
- int[] result = new int[4];
- int[] srcPixels = new int[width];
- int[] dstPixels = new int[width];
-
- for (int y = 0; y < height; y++) {
- dstIn.getDataElements(0, y, width, 1, dstPixels);
- if (alpha != 0) {
- src.getDataElements(0, y, width, 1, srcPixels);
- for (int x = 0; x < width; x++) {
- // pixels are stored as INT_ARGB
- // our arrays are [R, G, B, A]
- int pixel = srcPixels[x];
- srcPixel[0] = (pixel >> 16) & 0xFF;
- srcPixel[1] = (pixel >> 8) & 0xFF;
- srcPixel[2] = (pixel ) & 0xFF;
- srcPixel[3] = (pixel >> 24) & 0xFF;
-
- pixel = dstPixels[x];
- dstPixel[0] = (pixel >> 16) & 0xFF;
- dstPixel[1] = (pixel >> 8) & 0xFF;
- dstPixel[2] = (pixel ) & 0xFF;
- dstPixel[3] = (pixel >> 24) & 0xFF;
-
- // ---- Modified from original ----
- // recompute src pixel for transparency.
- srcPixel[3] *= dstPixel[3] / 0xFF;
- // ---- Modification ends ----
-
- result = blender.blend(srcPixel, dstPixel, result);
-
- // mixes the result with the opacity
- if (alpha == 1) {
- dstPixels[x] = (result[3] & 0xFF) << 24 |
- (result[0] & 0xFF) << 16 |
- (result[1] & 0xFF) << 8 |
- result[2] & 0xFF;
- } else {
- dstPixels[x] =
- ((int) (dstPixel[3] + (result[3] - dstPixel[3]) * alpha) & 0xFF) << 24 |
- ((int) (dstPixel[0] + (result[0] - dstPixel[0]) * alpha) & 0xFF) << 16 |
- ((int) (dstPixel[1] + (result[1] - dstPixel[1]) * alpha) & 0xFF) << 8 |
- (int) (dstPixel[2] + (result[2] - dstPixel[2]) * alpha) & 0xFF;
- }
-
- }
- }
- dstOut.setDataElements(0, y, width, 1, dstPixels);
- }
- }
- }
-
- private static abstract class Blender {
- public abstract int[] blend(int[] src, int[] dst, int[] result);
-
- public static Blender getBlenderFor(BlendComposite composite) {
- switch (composite.getMode()) {
- case ADD:
- return new Blender() {
- @Override
- public int[] blend(int[] src, int[] dst, int[] result) {
- for (int i = 0; i < 4; i++) {
- result[i] = Math.min(255, src[i] + dst[i]);
- }
- return result;
- }
- };
- case DARKEN:
- return new Blender() {
- @Override
- public int[] blend(int[] src, int[] dst, int[] result) {
- for (int i = 0; i < 3; i++) {
- result[i] = Math.min(src[i], dst[i]);
- }
- result[3] = Math.min(255, src[3] + dst[3]);
- return result;
- }
- };
- case LIGHTEN:
- return new Blender() {
- @Override
- public int[] blend(int[] src, int[] dst, int[] result) {
- for (int i = 0; i < 3; i++) {
- result[i] = Math.max(src[i], dst[i]);
- }
- result[3] = Math.min(255, src[3] + dst[3]);
- return result;
- }
- };
- case MULTIPLY:
- return new Blender() {
- @Override
- public int[] blend(int[] src, int[] dst, int[] result) {
- for (int i = 0; i < 3; i++) {
- result[i] = (src[i] * dst[i]) >> 8;
- }
- result[3] = Math.min(255, src[3] + dst[3] - (src[3] * dst[3]) / 255);
- return result;
- }
- };
- case OVERLAY:
- return new Blender() {
- @Override
- public int[] blend(int[] src, int[] dst, int[] result) {
- for (int i = 0; i < 3; i++) {
- result[i] = dst[i] < 128 ? dst[i] * src[i] >> 7 :
- 255 - ((255 - dst[i]) * (255 - src[i]) >> 7);
- }
- result[3] = Math.min(255, src[3] + dst[3]);
- return result;
- }
- };
- case SCREEN:
- return new Blender() {
- @Override
- public int[] blend(int[] src, int[] dst, int[] result) {
- result[0] = 255 - ((255 - src[0]) * (255 - dst[0]) >> 8);
- result[1] = 255 - ((255 - src[1]) * (255 - dst[1]) >> 8);
- result[2] = 255 - ((255 - src[2]) * (255 - dst[2]) >> 8);
- result[3] = Math.min(255, src[3] + dst[3]);
- return result;
- }
- };
- default:
- assert false : "Blender not implement for " + composite.getMode().name();
-
- // Ignore the blend
- return new Blender() {
- @Override
- public int[] blend(int[] src, int[] dst, int[] result) {
- result[0] = dst[0];
- result[1] = dst[1];
- result[2] = dst[2];
- result[3] = dst[3];
- return result;
- }
- };
- }
- }
- }
-}
diff --git a/bridge/src/android/graphics/BlendModeColorFilter_Delegate.java b/bridge/src/android/graphics/BlendModeColorFilter_Delegate.java
deleted file mode 100644
index fc259035a9..0000000000
--- a/bridge/src/android/graphics/BlendModeColorFilter_Delegate.java
+++ /dev/null
@@ -1,52 +0,0 @@
-/*
- * 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.
- */
-
-package android.graphics;
-
-import com.android.layoutlib.bridge.impl.DelegateManager;
-import com.android.tools.layoutlib.annotations.LayoutlibDelegate;
-
-/**
- * Delegate implementing the native methods of android.graphics.BlendModeColorFilter
- *
- * Through the layoutlib_create tool, the original native methods of BlendModeColorFilter have
- * been replaced by calls to methods of the same name in this delegate class.
- *
- * This class behaves like the original native implementation, but in Java, keeping previously
- * native data into its own objects and mapping them to int that are sent back and forth between
- * it and the original PorterDuffColorFilter class.
- *
- * Because this extends {@link ColorFilter_Delegate}, there's no need to use a
- * {@link DelegateManager}, as all the Shader classes will be added to the manager
- * owned by {@link ColorFilter_Delegate}.
- *
- * @see ColorFilter_Delegate
- *
- */
-public class BlendModeColorFilter_Delegate extends ColorFilter_Delegate {
-
- @Override
- public String getSupportMessage() {
- return "BlendMode Color Filters are not supported.";
- }
-
- // ---- native methods ----
-
- @LayoutlibDelegate
- /*package*/ static long native_CreateBlendModeFilter(int srcColor, int blendmode) {
- return PorterDuffColorFilter_Delegate.native_CreateBlendModeFilter(srcColor, blendmode);
- }
-}
diff --git a/bridge/src/android/graphics/BlurMaskFilter_Delegate.java b/bridge/src/android/graphics/BlurMaskFilter_Delegate.java
deleted file mode 100644
index d2569c7775..0000000000
--- a/bridge/src/android/graphics/BlurMaskFilter_Delegate.java
+++ /dev/null
@@ -1,64 +0,0 @@
-/*
- * Copyright (C) 2010 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package android.graphics;
-
-import com.android.layoutlib.bridge.impl.DelegateManager;
-import com.android.tools.layoutlib.annotations.LayoutlibDelegate;
-
-/**
- * Delegate implementing the native methods of android.graphics.BlurMaskFilter
- *
- * Through the layoutlib_create tool, the original native methods of BlurMaskFilter have
- * been replaced by calls to methods of the same name in this delegate class.
- *
- * This class behaves like the original native implementation, but in Java, keeping previously
- * native data into its own objects and mapping them to int that are sent back and forth between
- * it and the original BlurMaskFilter class.
- *
- * Because this extends {@link MaskFilter_Delegate}, there's no need to use a
- * {@link DelegateManager}, as all the Shader classes will be added to the manager
- * owned by {@link MaskFilter_Delegate}.
- *
- * @see MaskFilter_Delegate
- *
- */
-public class BlurMaskFilter_Delegate extends MaskFilter_Delegate {
-
- // ---- delegate data ----
-
- // ---- Public Helper methods ----
-
- @Override
- public boolean isSupported() {
- return false;
- }
-
- @Override
- public String getSupportMessage() {
- return "Blur Mask Filters are not supported.";
- }
-
- // ---- native methods ----
-
- @LayoutlibDelegate
- /*package*/ static long nativeConstructor(float radius, int style) {
- BlurMaskFilter_Delegate newDelegate = new BlurMaskFilter_Delegate();
- return sManager.addNewDelegate(newDelegate);
- }
-
- // ---- Private delegate/helper methods ----
-}
diff --git a/bridge/src/android/graphics/Canvas_Delegate.java b/bridge/src/android/graphics/Canvas_Delegate.java
index 19e88b6d3b..8ce28c90cb 100644
--- a/bridge/src/android/graphics/Canvas_Delegate.java
+++ b/bridge/src/android/graphics/Canvas_Delegate.java
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2010 The Android Open Source Project
+ * 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.
@@ -16,474 +16,21 @@
package android.graphics;
-import com.android.ide.common.rendering.api.ILayoutLog;
-import com.android.layoutlib.bridge.Bridge;
-import com.android.layoutlib.bridge.impl.DelegateManager;
-import com.android.layoutlib.bridge.impl.GcSnapshot;
import com.android.tools.layoutlib.annotations.LayoutlibDelegate;
-import android.graphics.Bitmap.Config;
-
-import java.awt.Graphics2D;
-import java.awt.Rectangle;
-import java.awt.geom.AffineTransform;
-
-import libcore.util.NativeAllocationRegistry_Delegate;
-
-
-/**
- * Delegate implementing the native methods of android.graphics.Canvas
- *
- * Through the layoutlib_create tool, the original native methods of Canvas have been replaced
- * by calls to methods of the same name in this delegate class.
- *
- * This class behaves like the original native implementation, but in Java, keeping previously
- * native data into its own objects and mapping them to int that are sent back and forth between
- * it and the original Canvas class.
- *
- * @see DelegateManager
- *
- */
-public final class Canvas_Delegate extends BaseCanvas_Delegate {
-
- // ---- delegate manager ----
- private static long sFinalizer = -1;
-
- private DrawFilter_Delegate mDrawFilter = null;
-
- // ---- Public Helper methods ----
-
- /**
- * Returns the native delegate associated to a given {@link Canvas} object.
- */
- public static Canvas_Delegate getDelegate(Canvas canvas) {
- return (Canvas_Delegate) sManager.getDelegate(canvas.getNativeCanvasWrapper());
- }
-
- /**
- * Returns the native delegate associated to a given an int referencing a {@link Canvas} object.
- */
- public static Canvas_Delegate getDelegate(long native_canvas) {
- return (Canvas_Delegate) sManager.getDelegate(native_canvas);
- }
-
- /**
- * Returns the current {@link Graphics2D} used to draw.
- */
- public GcSnapshot getSnapshot() {
- return mSnapshot;
- }
-
- /**
- * Returns the {@link DrawFilter} delegate or null if none have been set.
- *
- * @return the delegate or null.
- */
- public DrawFilter_Delegate getDrawFilter() {
- return mDrawFilter;
- }
-
- // ---- native methods ----
-
- @LayoutlibDelegate
- /*package*/ static void nFreeCaches() {
- // nothing to be done here.
- }
-
- @LayoutlibDelegate
- /*package*/ static void nFreeTextLayoutCaches() {
- // nothing to be done here yet.
- }
-
- @LayoutlibDelegate
- /*package*/ static long nInitRaster(long bitmapHandle) {
- if (bitmapHandle > 0) {
- // get the Bitmap from the int
- Bitmap_Delegate bitmapDelegate = Bitmap_Delegate.getDelegate(bitmapHandle);
-
- // create a new Canvas_Delegate with the given bitmap and return its new native int.
- Canvas_Delegate newDelegate = new Canvas_Delegate(bitmapDelegate);
-
- return sManager.addNewDelegate(newDelegate);
- }
-
- // create a new Canvas_Delegate and return its new native int.
- Canvas_Delegate newDelegate = new Canvas_Delegate();
-
- return sManager.addNewDelegate(newDelegate);
- }
-
- @LayoutlibDelegate
- public static void nSetBitmap(long canvas, long bitmapHandle) {
- Canvas_Delegate canvasDelegate = Canvas_Delegate.getDelegate(canvas);
- Bitmap_Delegate bitmapDelegate = Bitmap_Delegate.getDelegate(bitmapHandle);
- if (canvasDelegate == null || bitmapDelegate == null) {
- return;
- }
- canvasDelegate.mBitmap = bitmapDelegate;
- canvasDelegate.mSnapshot = GcSnapshot.createDefaultSnapshot(bitmapDelegate);
- }
-
- @LayoutlibDelegate
- public static boolean nIsOpaque(long nativeCanvas) {
- // get the delegate from the native int.
- Canvas_Delegate canvasDelegate = Canvas_Delegate.getDelegate(nativeCanvas);
- if (canvasDelegate == null) {
- return false;
- }
-
- return canvasDelegate.mBitmap.getConfig() == Config.RGB_565;
- }
-
+public class Canvas_Delegate {
@LayoutlibDelegate
- public static int nGetWidth(long nativeCanvas) {
- // get the delegate from the native int.
- Canvas_Delegate canvasDelegate = Canvas_Delegate.getDelegate(nativeCanvas);
- if (canvasDelegate == null) {
- return 0;
+ static boolean getClipBounds(Canvas thisCanvas, Rect bounds) {
+ // Despite allowing a null argument, Canvas.getClipBounds(Rect) causes a native crash
+ // in Android. In this case, throw an exception to avoid a layoutlib native crash.
+ if (bounds == null) {
+ throw new RuntimeException("Null bounds causes a crash in Android");
}
-
- return canvasDelegate.mBitmap.getImage().getWidth();
- }
-
- @LayoutlibDelegate
- public static int nGetHeight(long nativeCanvas) {
- // get the delegate from the native int.
- Canvas_Delegate canvasDelegate = Canvas_Delegate.getDelegate(nativeCanvas);
- if (canvasDelegate == null) {
- return 0;
- }
-
- return canvasDelegate.mBitmap.getImage().getHeight();
+ return thisCanvas.getClipBounds_Original(bounds);
}
@LayoutlibDelegate
- public static int nSave(long nativeCanvas, int saveFlags) {
- // get the delegate from the native int.
- Canvas_Delegate canvasDelegate = Canvas_Delegate.getDelegate(nativeCanvas);
- if (canvasDelegate == null) {
- return 0;
- }
-
- return canvasDelegate.save(saveFlags);
- }
-
- @LayoutlibDelegate
- public static int nSaveLayer(long nativeCanvas, float l,
- float t, float r, float b,
- long paint, int layerFlags) {
- // get the delegate from the native int.
- Canvas_Delegate canvasDelegate = Canvas_Delegate.getDelegate(nativeCanvas);
- if (canvasDelegate == null) {
- return 0;
- }
-
- Paint_Delegate paintDelegate = Paint_Delegate.getDelegate(paint);
-
- return canvasDelegate.saveLayer(new RectF(l, t, r, b),
- paintDelegate, layerFlags);
- }
-
- @LayoutlibDelegate
- public static int nSaveUnclippedLayer(long nativeCanvas, int l, int t, int r, int b) {
- return nSaveLayer(nativeCanvas, l, t, r, b, 0, 0);
- }
-
- @LayoutlibDelegate
- public static int nSaveLayerAlpha(long nativeCanvas, float l,
- float t, float r, float b,
- int alpha, int layerFlags) {
- // get the delegate from the native int.
- Canvas_Delegate canvasDelegate = Canvas_Delegate.getDelegate(nativeCanvas);
- if (canvasDelegate == null) {
- return 0;
- }
-
- return canvasDelegate.saveLayerAlpha(new RectF(l, t, r, b), alpha, layerFlags);
- }
-
- @LayoutlibDelegate
- public static void nRestoreUnclippedLayer(long nativeCanvas, int saveCount,
- long nativePaint) {
- nRestoreToCount(nativeCanvas, saveCount);
- }
-
- @LayoutlibDelegate
- public static boolean nRestore(long nativeCanvas) {
- // FIXME: implement throwOnUnderflow.
- // get the delegate from the native int.
- Canvas_Delegate canvasDelegate = Canvas_Delegate.getDelegate(nativeCanvas);
- if (canvasDelegate == null) {
- return false;
- }
-
- canvasDelegate.restore();
- return true;
- }
-
- @LayoutlibDelegate
- public static void nRestoreToCount(long nativeCanvas, int saveCount) {
- // FIXME: implement throwOnUnderflow.
- // get the delegate from the native int.
- Canvas_Delegate canvasDelegate = Canvas_Delegate.getDelegate(nativeCanvas);
- if (canvasDelegate == null) {
- return;
- }
-
- canvasDelegate.restoreTo(saveCount);
- }
-
- @LayoutlibDelegate
- public static int nGetSaveCount(long nativeCanvas) {
- // get the delegate from the native int.
- Canvas_Delegate canvasDelegate = Canvas_Delegate.getDelegate(nativeCanvas);
- if (canvasDelegate == null) {
- return 0;
- }
-
- return canvasDelegate.getSnapshot().size();
- }
-
- @LayoutlibDelegate
- public static void nTranslate(long nativeCanvas, float dx, float dy) {
- // get the delegate from the native int.
- Canvas_Delegate canvasDelegate = Canvas_Delegate.getDelegate(nativeCanvas);
- if (canvasDelegate == null) {
- return;
- }
-
- canvasDelegate.getSnapshot().translate(dx, dy);
- }
-
- @LayoutlibDelegate
- public static void nScale(long nativeCanvas, float sx, float sy) {
- // get the delegate from the native int.
- Canvas_Delegate canvasDelegate = Canvas_Delegate.getDelegate(nativeCanvas);
- if (canvasDelegate == null) {
- return;
- }
-
- canvasDelegate.getSnapshot().scale(sx, sy);
- }
-
- @LayoutlibDelegate
- public static void nRotate(long nativeCanvas, float degrees) {
- // get the delegate from the native int.
- Canvas_Delegate canvasDelegate = Canvas_Delegate.getDelegate(nativeCanvas);
- if (canvasDelegate == null) {
- return;
- }
-
- canvasDelegate.getSnapshot().rotate(Math.toRadians(degrees));
- }
-
- @LayoutlibDelegate
- public static void nSkew(long nativeCanvas, float kx, float ky) {
- // get the delegate from the native int.
- Canvas_Delegate canvasDelegate = Canvas_Delegate.getDelegate(nativeCanvas);
- if (canvasDelegate == null) {
- return;
- }
-
- // get the current top graphics2D object.
- GcSnapshot g = canvasDelegate.getSnapshot();
-
- // get its current matrix
- AffineTransform currentTx = g.getTransform();
- // get the AffineTransform for the given skew.
- float[] mtx = Matrix_Delegate.getSkew(kx, ky);
- AffineTransform matrixTx = Matrix_Delegate.getAffineTransform(mtx);
-
- // combine them so that the given matrix is applied after.
- currentTx.preConcatenate(matrixTx);
-
- // give it to the graphics2D as a new matrix replacing all previous transform
- g.setTransform(currentTx);
- }
-
- @LayoutlibDelegate
- public static void nConcat(long nCanvas, long nMatrix) {
- // get the delegate from the native int.
- Canvas_Delegate canvasDelegate = Canvas_Delegate.getDelegate(nCanvas);
- if (canvasDelegate == null) {
- return;
- }
-
- Matrix_Delegate matrixDelegate = Matrix_Delegate.getDelegate(nMatrix);
- if (matrixDelegate == null) {
- return;
- }
-
- // get the current top graphics2D object.
- GcSnapshot snapshot = canvasDelegate.getSnapshot();
-
- // get its current matrix
- AffineTransform currentTx = snapshot.getTransform();
- // get the AffineTransform of the given matrix
- AffineTransform matrixTx = matrixDelegate.getAffineTransform();
-
- // combine them so that the given matrix is applied after.
- currentTx.concatenate(matrixTx);
-
- // give it to the graphics2D as a new matrix replacing all previous transform
- snapshot.setTransform(currentTx);
- }
-
- @LayoutlibDelegate
- public static void nSetMatrix(long nCanvas, long nMatrix) {
- // get the delegate from the native int.
- Canvas_Delegate canvasDelegate = Canvas_Delegate.getDelegate(nCanvas);
- if (canvasDelegate == null) {
- return;
- }
-
- Matrix_Delegate matrixDelegate = Matrix_Delegate.getDelegate(nMatrix);
- if (matrixDelegate == null) {
- return;
- }
-
- // get the current top graphics2D object.
- GcSnapshot snapshot = canvasDelegate.getSnapshot();
-
- // get the AffineTransform of the given matrix
- AffineTransform matrixTx = matrixDelegate.getAffineTransform();
-
- // give it to the graphics2D as a new matrix replacing all previous transform
- snapshot.setTransform(matrixTx);
-
- if (matrixDelegate.hasPerspective()) {
- assert false;
- Bridge.getLog().fidelityWarning(ILayoutLog.TAG_MATRIX_AFFINE,
- "android.graphics.Canvas#setMatrix(android.graphics.Matrix) only " +
- "supports affine transformations.", null, null, null /*data*/);
- }
- }
-
- @LayoutlibDelegate
- public static boolean nClipRect(long nCanvas,
- float left, float top,
- float right, float bottom,
- int regionOp) {
- // get the delegate from the native int.
- Canvas_Delegate canvasDelegate = Canvas_Delegate.getDelegate(nCanvas);
- if (canvasDelegate == null) {
- return false;
- }
-
- return canvasDelegate.clipRect(left, top, right, bottom, regionOp);
- }
-
- @LayoutlibDelegate
- public static boolean nClipPath(long nativeCanvas,
- long nativePath,
- int regionOp) {
- Canvas_Delegate canvasDelegate = Canvas_Delegate.getDelegate(nativeCanvas);
- if (canvasDelegate == null) {
- return true;
- }
-
- Path_Delegate pathDelegate = Path_Delegate.getDelegate(nativePath);
- if (pathDelegate == null) {
- return true;
- }
-
- return canvasDelegate.mSnapshot.clip(pathDelegate.getJavaShape(), regionOp);
- }
-
- @LayoutlibDelegate
- public static void nSetDrawFilter(long nativeCanvas, long nativeFilter) {
- Canvas_Delegate canvasDelegate = Canvas_Delegate.getDelegate(nativeCanvas);
- if (canvasDelegate == null) {
- return;
- }
-
- canvasDelegate.mDrawFilter = DrawFilter_Delegate.getDelegate(nativeFilter);
-
- if (canvasDelegate.mDrawFilter != null && !canvasDelegate.mDrawFilter.isSupported()) {
- Bridge.getLog().fidelityWarning(ILayoutLog.TAG_DRAWFILTER,
- canvasDelegate.mDrawFilter.getSupportMessage(), null, null, null /*data*/);
- }
- }
-
- @LayoutlibDelegate
- public static boolean nGetClipBounds(long nativeCanvas,
- Rect bounds) {
- // get the delegate from the native int.
- Canvas_Delegate canvasDelegate = Canvas_Delegate.getDelegate(nativeCanvas);
- if (canvasDelegate == null) {
- return false;
- }
-
- Rectangle rect = canvasDelegate.getSnapshot().getClip().getBounds();
- if (rect != null && !rect.isEmpty()) {
- bounds.left = rect.x;
- bounds.top = rect.y;
- bounds.right = rect.x + rect.width;
- bounds.bottom = rect.y + rect.height;
- return true;
- }
-
- return false;
- }
-
- @LayoutlibDelegate
- public static void nGetMatrix(long canvas, long matrix) {
- // get the delegate from the native int.
- Canvas_Delegate canvasDelegate = Canvas_Delegate.getDelegate(canvas);
- if (canvasDelegate == null) {
- return;
- }
-
- Matrix_Delegate matrixDelegate = Matrix_Delegate.getDelegate(matrix);
- if (matrixDelegate == null) {
- return;
- }
-
- AffineTransform transform = canvasDelegate.getSnapshot().getTransform();
- matrixDelegate.set(Matrix_Delegate.makeValues(transform));
- }
-
- @LayoutlibDelegate
- public static boolean nQuickReject(long nativeCanvas, long path) {
- // FIXME properly implement quickReject
- return false;
- }
-
- @LayoutlibDelegate
- public static boolean nQuickReject(long nativeCanvas,
- float left, float top,
- float right, float bottom) {
- // FIXME properly implement quickReject
- return false;
- }
-
- @LayoutlibDelegate
- /*package*/ static long nGetNativeFinalizer() {
- synchronized (Canvas_Delegate.class) {
- if (sFinalizer == -1) {
- sFinalizer = NativeAllocationRegistry_Delegate.createFinalizer(nativePtr -> {
- Canvas_Delegate delegate = Canvas_Delegate.getDelegate(nativePtr);
- if (delegate != null) {
- delegate.dispose();
- }
- sManager.removeJavaReferenceFor(nativePtr);
- });
- }
- }
- return sFinalizer;
- }
-
- @LayoutlibDelegate
- /*package*/ static void nSetCompatibilityVersion(int apiLevel) {
- // Unsupported by layoutlib, do nothing
- }
-
- private Canvas_Delegate(Bitmap_Delegate bitmap) {
- super(bitmap);
- }
-
- private Canvas_Delegate() {
- super();
+ static Rect getClipBounds(Canvas thisCanvas) {
+ return thisCanvas.getClipBounds_Original();
}
}
-
diff --git a/bridge/src/android/graphics/ColorFilter_Delegate.java b/bridge/src/android/graphics/ColorFilter_Delegate.java
deleted file mode 100644
index 84424bc179..0000000000
--- a/bridge/src/android/graphics/ColorFilter_Delegate.java
+++ /dev/null
@@ -1,83 +0,0 @@
-/*
- * Copyright (C) 2010 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package android.graphics;
-
-import com.android.layoutlib.bridge.impl.DelegateManager;
-import com.android.tools.layoutlib.annotations.LayoutlibDelegate;
-
-import java.awt.Graphics2D;
-
-import libcore.util.NativeAllocationRegistry_Delegate;
-
-/**
- * Delegate implementing the native methods of android.graphics.ColorFilter
- *
- * Through the layoutlib_create tool, the original native methods of ColorFilter have been replaced
- * by calls to methods of the same name in this delegate class.
- *
- * This class behaves like the original native implementation, but in Java, keeping previously
- * native data into its own objects and mapping them to int that are sent back and forth between
- * it and the original ColorFilter class.
- *
- * This also serve as a base class for all ColorFilter delegate classes.
- *
- * @see DelegateManager
- *
- */
-public abstract class ColorFilter_Delegate {
-
- // ---- delegate manager ----
- protected static final DelegateManager<ColorFilter_Delegate> sManager =
- new DelegateManager<ColorFilter_Delegate>(ColorFilter_Delegate.class);
- private static long sFinalizer = -1;
-
- // ---- delegate helper data ----
-
- // ---- delegate data ----
-
- // ---- Public Helper methods ----
-
- public static ColorFilter_Delegate getDelegate(long nativeShader) {
- return sManager.getDelegate(nativeShader);
- }
-
- public abstract String getSupportMessage();
-
- public boolean isSupported() {
- return false;
- }
-
- public void applyFilter(Graphics2D g, int width, int height) {
- // This should never be called directly. If supported, the sub class should override this.
- assert false;
- }
-
- // ---- native methods ----
-
- @LayoutlibDelegate
- /*package*/ static long nativeGetFinalizer() {
- synchronized (ColorFilter_Delegate.class) {
- if (sFinalizer == -1) {
- sFinalizer = NativeAllocationRegistry_Delegate.createFinalizer(
- sManager::removeJavaReferenceFor);
- }
- }
- return sFinalizer;
- }
-
- // ---- Private delegate/helper methods ----
-}
diff --git a/bridge/src/android/graphics/ColorMatrixColorFilter_Delegate.java b/bridge/src/android/graphics/ColorMatrixColorFilter_Delegate.java
deleted file mode 100644
index 67394847fa..0000000000
--- a/bridge/src/android/graphics/ColorMatrixColorFilter_Delegate.java
+++ /dev/null
@@ -1,59 +0,0 @@
-/*
- * Copyright (C) 2010 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package android.graphics;
-
-import com.android.layoutlib.bridge.impl.DelegateManager;
-import com.android.tools.layoutlib.annotations.LayoutlibDelegate;
-
-/**
- * Delegate implementing the native methods of android.graphics.ColorMatrixColorFilter
- *
- * Through the layoutlib_create tool, the original native methods of ColorMatrixColorFilter have
- * been replaced by calls to methods of the same name in this delegate class.
- *
- * This class behaves like the original native implementation, but in Java, keeping previously
- * native data into its own objects and mapping them to int that are sent back and forth between
- * it and the original ColorMatrixColorFilter class.
- *
- * Because this extends {@link ColorFilter_Delegate}, there's no need to use a
- * {@link DelegateManager}, as all the Shader classes will be added to the manager
- * owned by {@link ColorFilter_Delegate}.
- *
- * @see ColorFilter_Delegate
- *
- */
-public class ColorMatrixColorFilter_Delegate extends ColorFilter_Delegate {
-
- // ---- delegate data ----
-
- // ---- Public Helper methods ----
-
- @Override
- public String getSupportMessage() {
- return "ColorMatrix Color Filters are not supported.";
- }
-
- // ---- native methods ----
-
- @LayoutlibDelegate
- /*package*/ static long nativeColorMatrixFilter(float[] array) {
- ColorMatrixColorFilter_Delegate newDelegate = new ColorMatrixColorFilter_Delegate();
- return sManager.addNewDelegate(newDelegate);
- }
-
- // ---- Private delegate/helper methods ----
-}
diff --git a/bridge/src/android/graphics/ColorSpace_Rgb_Delegate.java b/bridge/src/android/graphics/ColorSpace_Rgb_Delegate.java
deleted file mode 100644
index 3637055780..0000000000
--- a/bridge/src/android/graphics/ColorSpace_Rgb_Delegate.java
+++ /dev/null
@@ -1,54 +0,0 @@
-/*
- * Copyright (C) 2019 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package android.graphics;
-
-import com.android.layoutlib.bridge.impl.DelegateManager;
-import com.android.tools.layoutlib.annotations.LayoutlibDelegate;
-
-import libcore.util.NativeAllocationRegistry_Delegate;
-
-/**
- * Delegate implementing the native methods of android.graphics.ColorSpace
- *
- * Through the layoutlib_create tool, the original native methods of ColorSpace have been replaced
- * by calls to methods of the same name in this delegate class.
- */
-public class ColorSpace_Rgb_Delegate {
-
- // ---- delegate manager ----
- private static final DelegateManager<ColorSpace_Rgb_Delegate> sManager =
- new DelegateManager<>(ColorSpace_Rgb_Delegate.class);
- private static long sFinalizer = -1;
-
- @LayoutlibDelegate
- /*package*/ static long nativeGetNativeFinalizer() {
- synchronized (ColorSpace_Rgb_Delegate.class) {
- if (sFinalizer == -1) {
- sFinalizer = NativeAllocationRegistry_Delegate.createFinalizer(sManager::removeJavaReferenceFor);
- }
- }
- return sFinalizer;
- }
-
- @LayoutlibDelegate
- /*package*/ static long nativeCreate(float a, float b, float c, float d,
- float e, float f, float g, float[] xyz) {
- // Layoutlib does not support color spaces, but a native object is required
- // for ColorSpace$Rgb. This creates an empty delegate for it.
- return sManager.addNewDelegate(new ColorSpace_Rgb_Delegate());
- }
-}
diff --git a/bridge/src/android/graphics/Color_Delegate.java b/bridge/src/android/graphics/Color_Delegate.java
deleted file mode 100644
index afd24f0513..0000000000
--- a/bridge/src/android/graphics/Color_Delegate.java
+++ /dev/null
@@ -1,44 +0,0 @@
-/*
- * 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.
- */
-
-package android.graphics;
-
-import com.android.tools.layoutlib.annotations.LayoutlibDelegate;
-
-/**
- * Delegate implementing the native methods of android.graphics.Color
- *
- * Through the layoutlib_create tool, the original native methods of Color have been replaced
- * by calls to methods of the same name in this delegate class.
- */
-public class Color_Delegate {
-
- @LayoutlibDelegate
- /*package*/ static void nativeRGBToHSV(int red, int greed, int blue, float hsv[]) {
- java.awt.Color.RGBtoHSB(red, greed, blue, hsv);
- hsv[0] = hsv[0] * 360;
- }
-
- @LayoutlibDelegate
- /*package*/ static int nativeHSVToColor(int alpha, float hsv[]) {
- java.awt.Color rgb = new java.awt.Color(java.awt.Color.HSBtoRGB(hsv[0] / 360, pin(hsv[1]), pin(hsv[2])));
- return Color.argb(alpha, rgb.getRed(), rgb.getGreen(), rgb.getBlue());
- }
-
- private static float pin(float value) {
- return Math.max(0, Math.min(1, value));
- }
-}
diff --git a/bridge/src/android/graphics/ComposePathEffect_Delegate.java b/bridge/src/android/graphics/ComposePathEffect_Delegate.java
deleted file mode 100644
index bc3df7d848..0000000000
--- a/bridge/src/android/graphics/ComposePathEffect_Delegate.java
+++ /dev/null
@@ -1,71 +0,0 @@
-/*
- * Copyright (C) 2010 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package android.graphics;
-
-import com.android.layoutlib.bridge.impl.DelegateManager;
-import com.android.tools.layoutlib.annotations.LayoutlibDelegate;
-
-import java.awt.Stroke;
-
-/**
- * Delegate implementing the native methods of android.graphics.ComposePathEffect
- *
- * Through the layoutlib_create tool, the original native methods of ComposePathEffect have been
- * replaced by calls to methods of the same name in this delegate class.
- *
- * This class behaves like the original native implementation, but in Java, keeping previously
- * native data into its own objects and mapping them to int that are sent back and forth between
- * it and the original ComposePathEffect class.
- *
- * Because this extends {@link PathEffect_Delegate}, there's no need to use a {@link DelegateManager},
- * as all the Shader classes will be added to the manager owned by {@link PathEffect_Delegate}.
- *
- * @see PathEffect_Delegate
- *
- */
-public class ComposePathEffect_Delegate extends PathEffect_Delegate {
-
- // ---- delegate data ----
-
- // ---- Public Helper methods ----
-
- @Override
- public Stroke getStroke(Paint_Delegate paint) {
- // FIXME
- return null;
- }
-
- @Override
- public boolean isSupported() {
- return false;
- }
-
- @Override
- public String getSupportMessage() {
- return "Compose Path Effects are not supported in Layout Preview mode.";
- }
-
- // ---- native methods ----
-
- @LayoutlibDelegate
- /*package*/ static long nativeCreate(long outerpe, long innerpe) {
- ComposePathEffect_Delegate newDelegate = new ComposePathEffect_Delegate();
- return sManager.addNewDelegate(newDelegate);
- }
-
- // ---- Private delegate/helper methods ----
-}
diff --git a/bridge/src/android/graphics/ComposeShader_Delegate.java b/bridge/src/android/graphics/ComposeShader_Delegate.java
deleted file mode 100644
index ab37968a1d..0000000000
--- a/bridge/src/android/graphics/ComposeShader_Delegate.java
+++ /dev/null
@@ -1,79 +0,0 @@
-/*
- * Copyright (C) 2010 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package android.graphics;
-
-import com.android.layoutlib.bridge.impl.DelegateManager;
-import com.android.tools.layoutlib.annotations.LayoutlibDelegate;
-
-import java.awt.Paint;
-
-/**
- * Delegate implementing the native methods of android.graphics.ComposeShader
- *
- * Through the layoutlib_create tool, the original native methods of ComposeShader have been
- * replaced by calls to methods of the same name in this delegate class.
- *
- * This class behaves like the original native implementation, but in Java, keeping previously
- * native data into its own objects and mapping them to int that are sent back and forth between
- * it and the original ComposeShader class.
- *
- * Because this extends {@link Shader_Delegate}, there's no need to use a {@link DelegateManager},
- * as all the Shader classes will be added to the manager owned by {@link Shader_Delegate}.
- *
- * @see Shader_Delegate
- *
- */
-public class ComposeShader_Delegate extends Shader_Delegate {
-
- // ---- delegate data ----
-
- // ---- Public Helper methods ----
-
- @Override
- public Paint getJavaPaint() {
- // FIXME
- return null;
- }
-
- @Override
- public boolean isSupported() {
- return false;
- }
-
- @Override
- public String getSupportMessage() {
- return "Compose Shaders are not supported in Layout Preview mode.";
- }
-
-
- // ---- native methods ----
-
- @LayoutlibDelegate
- /*package*/ static long nativeCreate(long nativeMatrix, long native_shaderA,
- long native_shaderB, int native_mode) {
- // FIXME not supported yet.
- ComposeShader_Delegate newDelegate = new ComposeShader_Delegate(nativeMatrix);
- return sManager.addNewDelegate(newDelegate);
- }
-
-
- // ---- Private delegate/helper methods ----
-
- private ComposeShader_Delegate(long nativeMatrix) {
- super(nativeMatrix);
- }
-}
diff --git a/bridge/src/android/graphics/CornerPathEffect_Delegate.java b/bridge/src/android/graphics/CornerPathEffect_Delegate.java
deleted file mode 100644
index 73745c3b0c..0000000000
--- a/bridge/src/android/graphics/CornerPathEffect_Delegate.java
+++ /dev/null
@@ -1,71 +0,0 @@
-/*
- * Copyright (C) 2010 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package android.graphics;
-
-import com.android.layoutlib.bridge.impl.DelegateManager;
-import com.android.tools.layoutlib.annotations.LayoutlibDelegate;
-
-import java.awt.Stroke;
-
-/**
- * Delegate implementing the native methods of android.graphics.CornerPathEffect
- *
- * Through the layoutlib_create tool, the original native methods of CornerPathEffect have been
- * replaced by calls to methods of the same name in this delegate class.
- *
- * This class behaves like the original native implementation, but in Java, keeping previously
- * native data into its own objects and mapping them to int that are sent back and forth between
- * it and the original CornerPathEffect class.
- *
- * Because this extends {@link PathEffect_Delegate}, there's no need to use a {@link DelegateManager},
- * as all the Shader classes will be added to the manager owned by {@link PathEffect_Delegate}.
- *
- * @see PathEffect_Delegate
- *
- */
-public class CornerPathEffect_Delegate extends PathEffect_Delegate {
-
- // ---- delegate data ----
-
- // ---- Public Helper methods ----
-
- @Override
- public Stroke getStroke(Paint_Delegate paint) {
- // FIXME
- return null;
- }
-
- @Override
- public boolean isSupported() {
- return false;
- }
-
- @Override
- public String getSupportMessage() {
- return "Corner Path Effects are not supported in Layout Preview mode.";
- }
-
- // ---- native methods ----
-
- @LayoutlibDelegate
- /*package*/ static long nativeCreate(float radius) {
- CornerPathEffect_Delegate newDelegate = new CornerPathEffect_Delegate();
- return sManager.addNewDelegate(newDelegate);
- }
-
- // ---- Private delegate/helper methods ----
-}
diff --git a/bridge/src/android/graphics/DashPathEffect_Delegate.java b/bridge/src/android/graphics/DashPathEffect_Delegate.java
deleted file mode 100644
index 881afded11..0000000000
--- a/bridge/src/android/graphics/DashPathEffect_Delegate.java
+++ /dev/null
@@ -1,89 +0,0 @@
-/*
- * Copyright (C) 2010 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package android.graphics;
-
-import com.android.layoutlib.bridge.impl.DelegateManager;
-import com.android.tools.layoutlib.annotations.LayoutlibDelegate;
-
-import java.awt.BasicStroke;
-import java.awt.Stroke;
-
-/**
- * Delegate implementing the native methods of android.graphics.DashPathEffect
- *
- * Through the layoutlib_create tool, the original native methods of DashPathEffect have been
- * replaced by calls to methods of the same name in this delegate class.
- *
- * This class behaves like the original native implementation, but in Java, keeping previously
- * native data into its own objects and mapping them to int that are sent back and forth between
- * it and the original DashPathEffect class.
- *
- * Because this extends {@link PathEffect_Delegate}, there's no need to use a
- * {@link DelegateManager}, as all the PathEffect classes will be added to the manager owned by
- * {@link PathEffect_Delegate}.
- *
- * @see PathEffect_Delegate
- *
- */
-public final class DashPathEffect_Delegate extends PathEffect_Delegate {
-
- // ---- delegate data ----
-
- private final float[] mIntervals;
- private final float mPhase;
-
- // ---- Public Helper methods ----
-
- @Override
- public Stroke getStroke(Paint_Delegate paint) {
- return new BasicStroke(
- paint.getStrokeWidth(),
- paint.getJavaCap(),
- paint.getJavaJoin(),
- paint.getJavaStrokeMiter(),
- mIntervals,
- mPhase);
- }
-
- @Override
- public boolean isSupported() {
- return true;
- }
-
- @Override
- public String getSupportMessage() {
- // no message since isSupported returns true;
- return null;
- }
-
- // ---- native methods ----
-
- @LayoutlibDelegate
- /*package*/ static long nativeCreate(float intervals[], float phase) {
- DashPathEffect_Delegate newDelegate = new DashPathEffect_Delegate(intervals, phase);
- return sManager.addNewDelegate(newDelegate);
- }
-
- // ---- Private delegate/helper methods ----
-
- private DashPathEffect_Delegate(float intervals[], float phase) {
- mIntervals = new float[intervals.length];
- System.arraycopy(intervals, 0, mIntervals, 0, intervals.length);
- mPhase = phase;
- }
-}
-
diff --git a/bridge/src/android/graphics/DiscretePathEffect_Delegate.java b/bridge/src/android/graphics/DiscretePathEffect_Delegate.java
deleted file mode 100644
index 46109f3018..0000000000
--- a/bridge/src/android/graphics/DiscretePathEffect_Delegate.java
+++ /dev/null
@@ -1,71 +0,0 @@
-/*
- * Copyright (C) 2010 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package android.graphics;
-
-import com.android.layoutlib.bridge.impl.DelegateManager;
-import com.android.tools.layoutlib.annotations.LayoutlibDelegate;
-
-import java.awt.Stroke;
-
-/**
- * Delegate implementing the native methods of android.graphics.DiscretePathEffect
- *
- * Through the layoutlib_create tool, the original native methods of DiscretePathEffect have been
- * replaced by calls to methods of the same name in this delegate class.
- *
- * This class behaves like the original native implementation, but in Java, keeping previously
- * native data into its own objects and mapping them to int that are sent back and forth between
- * it and the original DiscretePathEffect class.
- *
- * Because this extends {@link PathEffect_Delegate}, there's no need to use a {@link DelegateManager},
- * as all the Shader classes will be added to the manager owned by {@link PathEffect_Delegate}.
- *
- * @see PathEffect_Delegate
- *
- */
-public class DiscretePathEffect_Delegate extends PathEffect_Delegate {
-
- // ---- delegate data ----
-
- // ---- Public Helper methods ----
-
- @Override
- public Stroke getStroke(Paint_Delegate paint) {
- // FIXME
- return null;
- }
-
- @Override
- public boolean isSupported() {
- return false;
- }
-
- @Override
- public String getSupportMessage() {
- return "Discrete Path Effects are not supported in Layout Preview mode.";
- }
-
- // ---- native methods ----
-
- @LayoutlibDelegate
- /*package*/ static long nativeCreate(float length, float deviation) {
- DiscretePathEffect_Delegate newDelegate = new DiscretePathEffect_Delegate();
- return sManager.addNewDelegate(newDelegate);
- }
-
- // ---- Private delegate/helper methods ----
-}
diff --git a/bridge/src/android/graphics/DrawFilter_Delegate.java b/bridge/src/android/graphics/DrawFilter_Delegate.java
deleted file mode 100644
index 2e1074080b..0000000000
--- a/bridge/src/android/graphics/DrawFilter_Delegate.java
+++ /dev/null
@@ -1,64 +0,0 @@
-/*
- * Copyright (C) 2010 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package android.graphics;
-
-import com.android.layoutlib.bridge.impl.DelegateManager;
-import com.android.tools.layoutlib.annotations.LayoutlibDelegate;
-
-/**
- * Delegate implementing the native methods of android.graphics.DrawFilter
- *
- * Through the layoutlib_create tool, the original native methods of DrawFilter have been replaced
- * by calls to methods of the same name in this delegate class.
- *
- * This class behaves like the original native implementation, but in Java, keeping previously
- * native data into its own objects and mapping them to int that are sent back and forth between
- * it and the original DrawFilter class.
- *
- * This also serve as a base class for all DrawFilter delegate classes.
- *
- * @see DelegateManager
- *
- */
-public abstract class DrawFilter_Delegate {
-
- // ---- delegate manager ----
- protected static final DelegateManager<DrawFilter_Delegate> sManager =
- new DelegateManager<DrawFilter_Delegate>(DrawFilter_Delegate.class);
-
- // ---- delegate helper data ----
-
- // ---- delegate data ----
-
- // ---- Public Helper methods ----
-
- public static DrawFilter_Delegate getDelegate(long nativeDrawFilter) {
- return sManager.getDelegate(nativeDrawFilter);
- }
-
- public abstract boolean isSupported();
- public abstract String getSupportMessage();
-
- // ---- native methods ----
-
- @LayoutlibDelegate
- /*package*/ static void nativeDestructor(long nativeDrawFilter) {
- sManager.removeJavaReferenceFor(nativeDrawFilter);
- }
-
- // ---- Private delegate/helper methods ----
-}
diff --git a/bridge/src/android/graphics/EmbossMaskFilter_Delegate.java b/bridge/src/android/graphics/EmbossMaskFilter_Delegate.java
deleted file mode 100644
index e5040ccc4b..0000000000
--- a/bridge/src/android/graphics/EmbossMaskFilter_Delegate.java
+++ /dev/null
@@ -1,65 +0,0 @@
-/*
- * Copyright (C) 2010 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package android.graphics;
-
-import com.android.layoutlib.bridge.impl.DelegateManager;
-import com.android.tools.layoutlib.annotations.LayoutlibDelegate;
-
-/**
- * Delegate implementing the native methods of android.graphics.EmbossMaskFilter
- *
- * Through the layoutlib_create tool, the original native methods of EmbossMaskFilter have
- * been replaced by calls to methods of the same name in this delegate class.
- *
- * This class behaves like the original native implementation, but in Java, keeping previously
- * native data into its own objects and mapping them to int that are sent back and forth between
- * it and the original EmbossMaskFilter class.
- *
- * Because this extends {@link MaskFilter_Delegate}, there's no need to use a
- * {@link DelegateManager}, as all the Shader classes will be added to the manager
- * owned by {@link MaskFilter_Delegate}.
- *
- * @see MaskFilter_Delegate
- *
- */
-public class EmbossMaskFilter_Delegate extends MaskFilter_Delegate {
-
- // ---- delegate data ----
-
- // ---- Public Helper methods ----
-
- @Override
- public boolean isSupported() {
- return false;
- }
-
- @Override
- public String getSupportMessage() {
- return "Emboss Mask Filters are not supported.";
- }
-
- // ---- native methods ----
-
- @LayoutlibDelegate
- /*package*/ static long nativeConstructor(float[] direction, float ambient,
- float specular, float blurRadius) {
- EmbossMaskFilter_Delegate newDelegate = new EmbossMaskFilter_Delegate();
- return sManager.addNewDelegate(newDelegate);
- }
-
- // ---- Private delegate/helper methods ----
-}
diff --git a/bridge/src/android/graphics/FontFamily_Delegate.java b/bridge/src/android/graphics/FontFamily_Delegate.java
deleted file mode 100644
index a452aae16b..0000000000
--- a/bridge/src/android/graphics/FontFamily_Delegate.java
+++ /dev/null
@@ -1,467 +0,0 @@
-/*
- * Copyright (C) 2014 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.graphics;
-
-import com.android.ide.common.rendering.api.ILayoutLog;
-import com.android.layoutlib.bridge.Bridge;
-import com.android.layoutlib.bridge.impl.DelegateManager;
-import com.android.tools.layoutlib.annotations.LayoutlibDelegate;
-
-import android.annotation.NonNull;
-import android.annotation.Nullable;
-import android.graphics.fonts.FontVariationAxis;
-
-import java.awt.Font;
-import java.awt.FontFormatException;
-import java.io.File;
-import java.io.FileNotFoundException;
-import java.nio.ByteBuffer;
-import java.util.ArrayList;
-import java.util.Collections;
-import java.util.HashSet;
-import java.util.LinkedHashMap;
-import java.util.List;
-import java.util.Map;
-import java.util.Objects;
-import java.util.Scanner;
-import java.util.Set;
-import java.util.logging.Logger;
-
-import libcore.util.NativeAllocationRegistry_Delegate;
-
-import static android.graphics.Typeface.RESOLVE_BY_FONT_TABLE;
-import static android.graphics.Typeface_Delegate.SYSTEM_FONTS;
-
-/**
- * Delegate implementing the native methods of android.graphics.FontFamily
- *
- * Through the layoutlib_create tool, the original native methods of FontFamily have been replaced
- * by calls to methods of the same name in this delegate class.
- *
- * This class behaves like the original native implementation, but in Java, keeping previously
- * native data into its own objects and mapping them to int that are sent back and forth between
- * it and the original FontFamily class.
- *
- * @see DelegateManager
- */
-public class FontFamily_Delegate {
-
- public static final int DEFAULT_FONT_WEIGHT = 400;
- public static final int BOLD_FONT_WEIGHT_DELTA = 300;
- public static final int BOLD_FONT_WEIGHT = 700;
-
- private static final String FONT_SUFFIX_ITALIC = "Italic.ttf";
- private static final String FN_ALL_FONTS_LIST = "fontsInSdk.txt";
- private static final String EXTENSION_OTF = ".otf";
-
- private static final int CACHE_SIZE = 10;
- // The cache has a drawback that if the font file changed after the font object was created,
- // we will not update it.
- private static final Map<String, FontInfo> sCache =
- new LinkedHashMap<String, FontInfo>(CACHE_SIZE) {
- @Override
- protected boolean removeEldestEntry(Map.Entry<String, FontInfo> eldest) {
- return size() > CACHE_SIZE;
- }
-
- @Override
- public FontInfo put(String key, FontInfo value) {
- // renew this entry.
- FontInfo removed = remove(key);
- super.put(key, value);
- return removed;
- }
- };
-
- /**
- * A class associating {@link Font} with its metadata.
- */
- public static final class FontInfo {
- @Nullable
- public Font mFont;
- public int mWeight;
- public boolean mIsItalic;
-
- @Override
- public boolean equals(Object o) {
- if (this == o) {
- return true;
- }
- if (o == null || getClass() != o.getClass()) {
- return false;
- }
- FontInfo fontInfo = (FontInfo) o;
- return mWeight == fontInfo.mWeight && mIsItalic == fontInfo.mIsItalic;
- }
-
- @Override
- public int hashCode() {
- return Objects.hash(mWeight, mIsItalic);
- }
-
- @Override
- public String toString() {
- return "FontInfo{" + "mWeight=" + mWeight + ", mIsItalic=" + mIsItalic + '}';
- }
- }
-
- // ---- delegate manager ----
- private static final DelegateManager<FontFamily_Delegate> sManager =
- new DelegateManager<FontFamily_Delegate>(FontFamily_Delegate.class);
- private static long sFamilyFinalizer = -1;
-
- // ---- delegate helper data ----
- private static String sFontLocation;
- private static final List<FontFamily_Delegate> sPostInitDelegate = new
- ArrayList<FontFamily_Delegate>();
- private static Set<String> SDK_FONTS;
-
-
- // ---- delegate data ----
-
- // Order does not really matter but we use a LinkedHashMap to get reproducible results across
- // render calls
- private Map<FontInfo, Font> mFonts = new LinkedHashMap<>();
-
- /**
- * The variant of the Font Family - compact or elegant.
- * <p/>
- * 0 is unspecified, 1 is compact and 2 is elegant. This needs to be kept in sync with values in
- * android.graphics.FontFamily
- *
- * @see Paint#setElegantTextHeight(boolean)
- */
- private FontVariant mVariant;
- // List of runnables to process fonts after sFontLoader is initialized.
- private List<Runnable> mPostInitRunnables = new ArrayList<Runnable>();
- /** @see #isValid() */
- private boolean mValid = false;
-
-
- // ---- Public helper class ----
-
- public enum FontVariant {
- // The order needs to be kept in sync with android.graphics.FontFamily.
- NONE, COMPACT, ELEGANT
- }
-
- // ---- Public Helper methods ----
-
- public static FontFamily_Delegate getDelegate(long nativeFontFamily) {
- return sManager.getDelegate(nativeFontFamily);
- }
-
- public static synchronized void setFontLocation(String fontLocation) {
- sFontLocation = fontLocation;
- // init list of bundled fonts.
- File allFonts = new File(fontLocation, FN_ALL_FONTS_LIST);
- // Current number of fonts is 103. Use the next round number to leave scope for more fonts
- // in the future.
- Set<String> allFontsList = new HashSet<>(128);
- Scanner scanner = null;
- try {
- scanner = new Scanner(allFonts);
- while (scanner.hasNext()) {
- String name = scanner.next();
- // Skip font configuration files.
- if (!name.endsWith(".xml")) {
- allFontsList.add(name);
- }
- }
- } catch (FileNotFoundException e) {
- Bridge.getLog().error(ILayoutLog.TAG_BROKEN,
- "Unable to load the list of fonts. Try re-installing the SDK Platform from the SDK Manager.",
- e, null, null);
- } finally {
- if (scanner != null) {
- scanner.close();
- }
- }
- SDK_FONTS = Collections.unmodifiableSet(allFontsList);
- for (FontFamily_Delegate fontFamily : sPostInitDelegate) {
- fontFamily.init();
- }
- sPostInitDelegate.clear();
- }
-
- @Nullable
- public Font getFont(int desiredWeight, boolean isItalic) {
- FontInfo desiredStyle = new FontInfo();
- desiredStyle.mWeight = desiredWeight;
- desiredStyle.mIsItalic = isItalic;
-
- Font cachedFont = mFonts.get(desiredStyle);
- if (cachedFont != null) {
- return cachedFont;
- }
-
- FontInfo bestFont = null;
-
- if (mFonts.size() == 1) {
- // No need to compute the match since we only have one candidate
- bestFont = mFonts.keySet().iterator().next();
- } else {
- int bestMatch = Integer.MAX_VALUE;
-
- for (FontInfo font : mFonts.keySet()) {
- int match = computeMatch(font, desiredStyle);
- if (match < bestMatch) {
- bestMatch = match;
- bestFont = font;
- if (bestMatch == 0) {
- break;
- }
- }
- }
- }
-
- if (bestFont == null) {
- return null;
- }
-
-
- // Derive the font as required and add it to the list of Fonts.
- deriveFont(bestFont, desiredStyle);
- addFont(desiredStyle);
- return desiredStyle.mFont;
- }
-
- public FontVariant getVariant() {
- return mVariant;
- }
-
- /**
- * Returns if the FontFamily should contain any fonts. If this returns true and
- * {@link #getFont(int, boolean)} returns an empty list, it means that an error occurred while
- * loading the fonts. However, some fonts are deliberately skipped, for example they are not
- * bundled with the SDK. In such a case, this method returns false.
- */
- public boolean isValid() {
- return mValid;
- }
-
- private static Font loadFont(String path) {
- if (path.startsWith(SYSTEM_FONTS) ) {
- String relativePath = path.substring(SYSTEM_FONTS.length());
- File f = new File(sFontLocation, relativePath);
-
- try {
- return Font.createFont(Font.TRUETYPE_FONT, f);
- } catch (Exception e) {
- if (path.endsWith(EXTENSION_OTF) && e instanceof FontFormatException) {
- // If we aren't able to load an Open Type font, don't log a warning just yet.
- // We wait for a case where font is being used. Only then we try to log the
- // warning.
- return null;
- }
- Bridge.getLog().fidelityWarning(ILayoutLog.TAG_BROKEN,
- String.format("Unable to load font %1$s", relativePath),
- e, null, null);
- }
- } else {
- Bridge.getLog().fidelityWarning(ILayoutLog.TAG_UNSUPPORTED,
- "Only platform fonts located in " + SYSTEM_FONTS + "can be loaded.",
- null, null, null);
- }
-
- return null;
- }
-
- @Nullable
- public static String getFontLocation() {
- return sFontLocation;
- }
-
- // ---- delegate methods ----
- @LayoutlibDelegate
- /*package*/ static boolean addFont(FontFamily thisFontFamily, String path, int ttcIndex,
- FontVariationAxis[] axes, int weight, int italic) {
- if (thisFontFamily.mBuilderPtr == 0) {
- assert false : "Unable to call addFont after freezing.";
- return false;
- }
- final FontFamily_Delegate delegate = getDelegate(thisFontFamily.mBuilderPtr);
- return delegate != null && delegate.addFont(path, ttcIndex, weight, italic);
- }
-
- // ---- native methods ----
-
- @LayoutlibDelegate
- /*package*/ static long nInitBuilder(String lang, int variant) {
- // TODO: support lang. This is required for japanese locale.
- FontFamily_Delegate delegate = new FontFamily_Delegate();
- // variant can be 0, 1 or 2.
- assert variant < 3;
- delegate.mVariant = FontVariant.values()[variant];
- if (sFontLocation != null) {
- delegate.init();
- } else {
- sPostInitDelegate.add(delegate);
- }
- return sManager.addNewDelegate(delegate);
- }
-
- @LayoutlibDelegate
- /*package*/ static long nCreateFamily(long builderPtr) {
- return builderPtr;
- }
-
- @LayoutlibDelegate
- /*package*/ static long nGetFamilyReleaseFunc() {
- synchronized (FontFamily_Delegate.class) {
- if (sFamilyFinalizer == -1) {
- sFamilyFinalizer = NativeAllocationRegistry_Delegate.createFinalizer(
- sManager::removeJavaReferenceFor);
- }
- }
- return sFamilyFinalizer;
- }
-
- @LayoutlibDelegate
- /*package*/ static boolean nAddFont(long builderPtr, ByteBuffer font, int ttcIndex,
- int weight, int isItalic) {
- assert false : "The only client of this method has been overridden.";
- return false;
- }
-
- @LayoutlibDelegate
- /*package*/ static boolean nAddFontWeightStyle(long builderPtr, ByteBuffer font,
- int ttcIndex, int weight, int isItalic) {
- assert false : "The only client of this method has been overridden.";
- return false;
- }
-
- @LayoutlibDelegate
- /*package*/ static void nAddAxisValue(long builderPtr, int tag, float value) {
- assert false : "The only client of this method has been overridden.";
- }
-
- static boolean addFont(long builderPtr, final String path, final int weight,
- final boolean isItalic) {
- final FontFamily_Delegate delegate = getDelegate(builderPtr);
- int italic = isItalic ? 1 : 0;
- if (delegate != null) {
- if (sFontLocation == null) {
- delegate.mPostInitRunnables.add(() -> delegate.addFont(path, weight, italic));
- return true;
- }
- return delegate.addFont(path, weight, italic);
- }
- return false;
- }
-
- @LayoutlibDelegate
- /*package*/ static long nGetBuilderReleaseFunc() {
- // Layoutlib uses the same reference for the builder and the font family,
- // so it should not release that reference at the builder stage.
- return -1;
- }
-
- // ---- private helper methods ----
-
- private void init() {
- for (Runnable postInitRunnable : mPostInitRunnables) {
- postInitRunnable.run();
- }
- mPostInitRunnables = null;
- }
-
- private boolean addFont(final String path, int ttcIndex, int weight, int italic) {
- // FIXME: support ttc fonts. Hack JRE??
- if (sFontLocation == null) {
- mPostInitRunnables.add(() -> addFont(path, weight, italic));
- return true;
- }
- return addFont(path, weight, italic);
- }
-
- private boolean addFont(@NonNull String path) {
- return addFont(path, DEFAULT_FONT_WEIGHT, path.endsWith(FONT_SUFFIX_ITALIC) ? 1 : RESOLVE_BY_FONT_TABLE);
- }
-
- private boolean addFont(@NonNull String path, int weight, int italic) {
- if (path.startsWith(SYSTEM_FONTS) &&
- !SDK_FONTS.contains(path.substring(SYSTEM_FONTS.length()))) {
- Logger.getLogger(FontFamily_Delegate.class.getSimpleName()).warning("Unable to load font " + path);
- return mValid = false;
- }
- // Set valid to true, even if the font fails to load.
- mValid = true;
- Font font = loadFont(path);
- if (font == null) {
- return false;
- }
- FontInfo fontInfo = new FontInfo();
- fontInfo.mFont = font;
- fontInfo.mWeight = weight;
- fontInfo.mIsItalic = italic == RESOLVE_BY_FONT_TABLE ? font.isItalic() : italic == 1;
- addFont(fontInfo);
- return true;
- }
-
- private boolean addFont(@NonNull FontInfo fontInfo) {
- return mFonts.putIfAbsent(fontInfo, fontInfo.mFont) == null;
- }
-
- /**
- * Compute matching metric between two styles - 0 is an exact match.
- */
- public static int computeMatch(@NonNull FontInfo font1, @NonNull FontInfo font2) {
- int score = Math.abs(font1.mWeight / 100 - font2.mWeight / 100);
- if (font1.mIsItalic != font2.mIsItalic) {
- score += 2;
- }
- return score;
- }
-
- /**
- * Try to derive a font from {@code srcFont} for the style in {@code outFont}.
- * <p/>
- * {@code outFont} is updated to reflect the style of the derived font.
- * @param srcFont the source font
- * @param outFont contains the desired font style. Updated to contain the derived font and
- * its style
- */
- public static void deriveFont(@NonNull FontInfo srcFont, @NonNull FontInfo outFont) {
- int desiredWeight = outFont.mWeight;
- int srcWeight = srcFont.mWeight;
- assert srcFont.mFont != null;
- Font derivedFont = srcFont.mFont;
- int derivedStyle = 0;
- // Embolden the font if required.
- if (desiredWeight >= BOLD_FONT_WEIGHT && desiredWeight - srcWeight > BOLD_FONT_WEIGHT_DELTA / 2) {
- derivedStyle |= Font.BOLD;
- srcWeight += BOLD_FONT_WEIGHT_DELTA;
- }
- // Italicize the font if required.
- if (outFont.mIsItalic && !srcFont.mIsItalic) {
- derivedStyle |= Font.ITALIC;
- } else if (outFont.mIsItalic != srcFont.mIsItalic) {
- // The desired font is plain, but the src font is italics. We can't convert it back. So
- // we update the value to reflect the true style of the font we're deriving.
- outFont.mIsItalic = srcFont.mIsItalic;
- }
-
- if (derivedStyle != 0) {
- derivedFont = derivedFont.deriveFont(derivedStyle);
- }
-
- outFont.mFont = derivedFont;
- outFont.mWeight = srcWeight;
- // No need to update mIsItalics, as it's already been handled above.
- }
-}
diff --git a/bridge/src/android/graphics/Gradient_Delegate.java b/bridge/src/android/graphics/Gradient_Delegate.java
deleted file mode 100644
index 8aed4b41fb..0000000000
--- a/bridge/src/android/graphics/Gradient_Delegate.java
+++ /dev/null
@@ -1,228 +0,0 @@
-/*
- * Copyright (C) 2010 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package android.graphics;
-
-import android.graphics.Shader.TileMode;
-
-import java.util.Arrays;
-
-/**
- * Base class for true Gradient shader delegate.
- */
-public abstract class Gradient_Delegate extends Shader_Delegate {
-
- protected final int[] mColors;
- protected final float[] mPositions;
-
- @Override
- public boolean isSupported() {
- // all gradient shaders are supported.
- return true;
- }
-
- @Override
- public String getSupportMessage() {
- // all gradient shaders are supported, no need for a gradient support
- return null;
- }
-
- /**
- * Creates the base shader and do some basic test on the parameters.
- *
- * @param nativeMatrix reference to the shader's native transformation matrix
- * @param colors The 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
- * the colors are distributed evenly along the gradient line.
- */
- protected Gradient_Delegate(long nativeMatrix, long[] colors, float[] positions) {
- super(nativeMatrix);
- assert colors.length >= 2 : "needs >= 2 number of colors";
-
- if (positions == null) {
- float spacing = 1.f / (colors.length - 1);
- positions = new float[colors.length];
- positions[0] = 0.f;
- positions[colors.length - 1] = 1.f;
- for (int i = 1; i < colors.length - 1; i++) {
- positions[i] = spacing * i;
- }
- } else {
- assert colors.length == positions.length :
- "color and position " + "arrays must be of equal length";
- positions[0] = Math.min(Math.max(0, positions[0]), 1);
- for (int i = 1; i < positions.length; i++) {
- positions[i] = Math.min(Math.max(positions[i-1], positions[i]), 1);
- }
- }
-
- mColors = Arrays.stream(colors).mapToInt(Color::toArgb).toArray();
- mPositions = positions;
- }
-
- /**
- * Base class for (Java) Gradient Paints. This handles computing the gradient colors based
- * on the color and position lists, as well as the {@link TileMode}
- *
- */
- protected abstract static class GradientPaint implements java.awt.Paint {
- private final static int GRADIENT_SIZE = 100;
-
- private final int[] mColors;
- private final float[] mPositions;
- private final TileMode mTileMode;
- private int[] mGradient;
-
- protected GradientPaint(int[] colors, float[] positions, TileMode tileMode) {
- mColors = colors;
- mPositions = positions;
- mTileMode = tileMode;
- }
-
- @Override
- public int getTransparency() {
- return java.awt.Paint.TRANSLUCENT;
- }
-
- /**
- * Pre-computes the colors for the gradient. This must be called once before any call
- * to {@link #getGradientColor(float)}
- */
- protected void precomputeGradientColors() {
- if (mGradient == null) {
- // actually create an array with an extra size, so that we can really go
- // from 0 to SIZE (100%), or currentPos in the loop below will never equal 1.0
- mGradient = new int[GRADIENT_SIZE+1];
-
- int prevPos = 0;
- int nextPos = 1;
- for (int i = 0 ; i <= GRADIENT_SIZE ; i++) {
- // compute current position
- float currentPos = (float)i/GRADIENT_SIZE;
-
- if (currentPos < mPositions[0]) {
- mGradient[i] = mColors[0];
- continue;
- }
-
- while (nextPos < mPositions.length && currentPos >= mPositions[nextPos]) {
- prevPos = nextPos++;
- }
-
- if (nextPos == mPositions.length || currentPos == prevPos) {
- mGradient[i] = mColors[prevPos];
- } else {
- float percent = (currentPos - mPositions[prevPos]) /
- (mPositions[nextPos] - mPositions[prevPos]);
-
- mGradient[i] = computeColor(mColors[prevPos], mColors[nextPos], percent);
- }
- }
- }
- }
-
- /**
- * Returns the color based on the position in the gradient.
- * <var>pos</var> can be anything, even &lt; 0 or &gt; > 1, as the gradient
- * will use {@link TileMode} value to convert it into a [0,1] value.
- */
- protected int getGradientColor(float pos) {
- if (pos < 0.f) {
- if (mTileMode != null) {
- switch (mTileMode) {
- case CLAMP:
- pos = 0.f;
- break;
- case REPEAT:
- // remove the integer part to stay in the [0,1] range.
- // we also need to invert the value from [-1,0] to [0, 1]
- pos = pos - (float)Math.floor(pos);
- break;
- case MIRROR:
- // this is the same as the positive side, just make the value positive
- // first.
- pos = Math.abs(pos);
-
- // get the integer and the decimal part
- int intPart = (int)Math.floor(pos);
- pos = pos - intPart;
- // 0 -> 1 : normal order
- // 1 -> 2: mirrored
- // etc..
- // this means if the intpart is odd we invert
- if ((intPart % 2) == 1) {
- pos = 1.f - pos;
- }
- break;
- }
- } else {
- pos = 0.0f;
- }
- } else if (pos > 1f) {
- if (mTileMode != null) {
- switch (mTileMode) {
- case CLAMP:
- pos = 1.f;
- break;
- case REPEAT:
- // remove the integer part to stay in the [0,1] range
- pos = pos - (float)Math.floor(pos);
- break;
- case MIRROR:
- // get the integer and the decimal part
- int intPart = (int)Math.floor(pos);
- pos = pos - intPart;
- // 0 -> 1 : normal order
- // 1 -> 2: mirrored
- // etc..
- // this means if the intpart is odd we invert
- if ((intPart % 2) == 1) {
- pos = 1.f - pos;
- }
- break;
- }
- } else {
- pos = 1.0f;
- }
- }
-
- int index = (int)((pos * GRADIENT_SIZE) + .5);
-
- return mGradient[index];
- }
-
- /**
- * Returns the color between c1, and c2, based on the percent of the distance
- * between c1 and c2.
- */
- private int computeColor(int c1, int c2, float percent) {
- int a = computeChannel((c1 >> 24) & 0xFF, (c2 >> 24) & 0xFF, percent);
- int r = computeChannel((c1 >> 16) & 0xFF, (c2 >> 16) & 0xFF, percent);
- int g = computeChannel((c1 >> 8) & 0xFF, (c2 >> 8) & 0xFF, percent);
- int b = computeChannel((c1 ) & 0xFF, (c2 ) & 0xFF, percent);
- return a << 24 | r << 16 | g << 8 | b;
- }
-
- /**
- * Returns the channel value between 2 values based on the percent of the distance between
- * the 2 values..
- */
- private int computeChannel(int c1, int c2, float percent) {
- return c1 + (int)((percent * (c2-c1)) + .5);
- }
- }
-}
diff --git a/bridge/src/android/graphics/HardwareRenderer_ProcessInitializer_Delegate.java b/bridge/src/android/graphics/HardwareRenderer_ProcessInitializer_Delegate.java
new file mode 100644
index 0000000000..88c034ba2a
--- /dev/null
+++ b/bridge/src/android/graphics/HardwareRenderer_ProcessInitializer_Delegate.java
@@ -0,0 +1,26 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.graphics;
+
+public class HardwareRenderer_ProcessInitializer_Delegate {
+ public static void initSched(long renderProxy) {
+ /*
+ * This is done in order to prevent NullPointerException when creating HardwareRenderer in
+ * layoutlib
+ */
+ }
+}
diff --git a/bridge/src/android/graphics/ImageDecoder.java b/bridge/src/android/graphics/ImageDecoder.java
deleted file mode 100644
index eefdb2ec32..0000000000
--- a/bridge/src/android/graphics/ImageDecoder.java
+++ /dev/null
@@ -1,768 +0,0 @@
-/*
- * Copyright (C) 2017 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package android.graphics;
-
-import android.annotation.NonNull;
-import android.annotation.Nullable;
-import android.content.ContentResolver;
-import android.content.res.AssetManager.AssetInputStream;
-import android.content.res.Resources;
-import android.graphics.drawable.AnimatedImageDrawable;
-import android.graphics.drawable.Drawable;
-import android.graphics.drawable.BitmapDrawable;
-import android.net.Uri;
-import android.util.DisplayMetrics;
-import android.util.Size;
-import android.util.TypedValue;
-
-import java.nio.ByteBuffer;
-import java.io.File;
-import java.io.IOException;
-import java.io.InputStream;
-import java.lang.ArrayIndexOutOfBoundsException;
-import java.lang.AutoCloseable;
-import java.lang.NullPointerException;
-import java.lang.annotation.Retention;
-import static java.lang.annotation.RetentionPolicy.SOURCE;
-
-/**
- * Class for decoding images as {@link Bitmap}s or {@link Drawable}s.
- */
-public final class ImageDecoder implements AutoCloseable {
-
- /**
- * Source of the encoded image data.
- */
- public static abstract class Source {
- private Source() {}
-
- /* @hide */
- @Nullable
- Resources getResources() { return null; }
-
- /* @hide */
- int getDensity() { return Bitmap.DENSITY_NONE; }
-
- /* @hide */
- int computeDstDensity() {
- Resources res = getResources();
- if (res == null) {
- return Bitmap.getDefaultDensity();
- }
-
- return res.getDisplayMetrics().densityDpi;
- }
-
- /* @hide */
- @NonNull
- abstract ImageDecoder createImageDecoder() throws IOException;
- };
-
- private static class ByteArraySource extends Source {
- ByteArraySource(@NonNull byte[] data, int offset, int length) {
- mData = data;
- mOffset = offset;
- mLength = length;
- };
- private final byte[] mData;
- private final int mOffset;
- private final int mLength;
-
- @Override
- public ImageDecoder createImageDecoder() throws IOException {
- return new ImageDecoder();
- }
- }
-
- private static class ByteBufferSource extends Source {
- ByteBufferSource(@NonNull ByteBuffer buffer) {
- mBuffer = buffer;
- }
- private final ByteBuffer mBuffer;
-
- @Override
- public ImageDecoder createImageDecoder() throws IOException {
- return new ImageDecoder();
- }
- }
-
- private static class ContentResolverSource extends Source {
- ContentResolverSource(@NonNull ContentResolver resolver, @NonNull Uri uri) {
- mResolver = resolver;
- mUri = uri;
- }
-
- private final ContentResolver mResolver;
- private final Uri mUri;
-
- @Override
- public ImageDecoder createImageDecoder() throws IOException {
- return new ImageDecoder();
- }
- }
-
- /**
- * For backwards compatibility, this does *not* close the InputStream.
- */
- private static class InputStreamSource extends Source {
- InputStreamSource(Resources res, InputStream is, int inputDensity) {
- if (is == null) {
- throw new IllegalArgumentException("The InputStream cannot be null");
- }
- mResources = res;
- mInputStream = is;
- mInputDensity = res != null ? inputDensity : Bitmap.DENSITY_NONE;
- }
-
- final Resources mResources;
- InputStream mInputStream;
- final int mInputDensity;
-
- @Override
- public Resources getResources() { return mResources; }
-
- @Override
- public int getDensity() { return mInputDensity; }
-
- @Override
- public ImageDecoder createImageDecoder() throws IOException {
- return new ImageDecoder();
- }
- }
-
- /**
- * Takes ownership of the AssetInputStream.
- *
- * @hide
- */
- public static class AssetInputStreamSource extends Source {
- public AssetInputStreamSource(@NonNull AssetInputStream ais,
- @NonNull Resources res, @NonNull TypedValue value) {
- mAssetInputStream = ais;
- mResources = res;
-
- if (value.density == TypedValue.DENSITY_DEFAULT) {
- mDensity = DisplayMetrics.DENSITY_DEFAULT;
- } else if (value.density != TypedValue.DENSITY_NONE) {
- mDensity = value.density;
- } else {
- mDensity = Bitmap.DENSITY_NONE;
- }
- }
-
- private AssetInputStream mAssetInputStream;
- private final Resources mResources;
- private final int mDensity;
-
- @Override
- public Resources getResources() { return mResources; }
-
- @Override
- public int getDensity() {
- return mDensity;
- }
-
- @Override
- public ImageDecoder createImageDecoder() throws IOException {
- return new ImageDecoder();
- }
- }
-
- private static class ResourceSource extends Source {
- ResourceSource(@NonNull Resources res, int resId) {
- mResources = res;
- mResId = resId;
- mResDensity = Bitmap.DENSITY_NONE;
- }
-
- final Resources mResources;
- final int mResId;
- int mResDensity;
-
- @Override
- public Resources getResources() { return mResources; }
-
- @Override
- public int getDensity() { return mResDensity; }
-
- @Override
- public ImageDecoder createImageDecoder() throws IOException {
- return new ImageDecoder();
- }
- }
-
- private static class FileSource extends Source {
- FileSource(@NonNull File file) {
- mFile = file;
- }
-
- private final File mFile;
-
- @Override
- public ImageDecoder createImageDecoder() throws IOException {
- return new ImageDecoder();
- }
- }
-
- /**
- * Contains information about the encoded image.
- */
- public static class ImageInfo {
- private ImageDecoder mDecoder;
-
- private ImageInfo(@NonNull ImageDecoder decoder) {
- mDecoder = decoder;
- }
-
- /**
- * Size of the image, without scaling or cropping.
- */
- @NonNull
- public Size getSize() {
- return new Size(0, 0);
- }
-
- /**
- * The mimeType of the image.
- */
- @NonNull
- public String getMimeType() {
- return "";
- }
-
- /**
- * Whether the image is animated.
- *
- * <p>Calling {@link #decodeDrawable} will return an
- * {@link AnimatedImageDrawable}.</p>
- */
- public boolean isAnimated() {
- return mDecoder.mAnimated;
- }
- };
-
- /**
- * Thrown if the provided data is incomplete.
- */
- public static class IncompleteException extends IOException {};
-
- /**
- * Optional listener supplied to {@link #decodeDrawable} or
- * {@link #decodeBitmap}.
- */
- public interface OnHeaderDecodedListener {
- /**
- * Called when the header is decoded and the size is known.
- *
- * @param decoder allows changing the default settings of the decode.
- * @param info Information about the encoded image.
- * @param source that created the decoder.
- */
- void onHeaderDecoded(@NonNull ImageDecoder decoder,
- @NonNull ImageInfo info, @NonNull Source source);
-
- };
-
- /**
- * An Exception was thrown reading the {@link Source}.
- */
- public static final int ERROR_SOURCE_EXCEPTION = 1;
-
- /**
- * The encoded data was incomplete.
- */
- public static final int ERROR_SOURCE_INCOMPLETE = 2;
-
- /**
- * The encoded data contained an error.
- */
- public static final int ERROR_SOURCE_ERROR = 3;
-
- @Retention(SOURCE)
- public @interface Error {}
-
- /**
- * Optional listener supplied to the ImageDecoder.
- *
- * Without this listener, errors will throw {@link java.io.IOException}.
- */
- public interface OnPartialImageListener {
- /**
- * Called when there is only a partial image to display.
- *
- * If decoding is interrupted after having decoded a partial image,
- * this listener lets the client know that and allows them to
- * optionally finish the rest of the decode/creation process to create
- * a partial {@link Drawable}/{@link Bitmap}.
- *
- * @param error indicating what interrupted the decode.
- * @param source that had the error.
- * @return True to create and return a {@link Drawable}/{@link Bitmap}
- * with partial data. False (which is the default) to abort the
- * decode and throw {@link java.io.IOException}.
- */
- boolean onPartialImage(@Error int error, @NonNull Source source);
- }
-
- private boolean mAnimated;
- private Rect mOutPaddingRect;
-
- public ImageDecoder() {
- mAnimated = true; // This is too avoid throwing an exception in AnimatedImageDrawable
- }
-
- /**
- * Create a new {@link Source} from an asset.
- * @hide
- *
- * @param res the {@link Resources} object containing the image data.
- * @param resId resource ID of the image data.
- * // FIXME: Can be an @DrawableRes?
- * @return a new Source object, which can be passed to
- * {@link #decodeDrawable} or {@link #decodeBitmap}.
- */
- @NonNull
- public static Source createSource(@NonNull Resources res, int resId)
- {
- return new ResourceSource(res, resId);
- }
-
- /**
- * Create a new {@link Source} from a {@link android.net.Uri}.
- *
- * @param cr to retrieve from.
- * @param uri of the image file.
- * @return a new Source object, which can be passed to
- * {@link #decodeDrawable} or {@link #decodeBitmap}.
- */
- @NonNull
- public static Source createSource(@NonNull ContentResolver cr,
- @NonNull Uri uri) {
- return new ContentResolverSource(cr, uri);
- }
-
- /**
- * Create a new {@link Source} from a byte array.
- *
- * @param data byte array of compressed image data.
- * @param offset offset into data for where the decoder should begin
- * parsing.
- * @param length number of bytes, beginning at offset, to parse.
- * @throws NullPointerException if data is null.
- * @throws ArrayIndexOutOfBoundsException if offset and length are
- * not within data.
- * @hide
- */
- @NonNull
- public static Source createSource(@NonNull byte[] data, int offset,
- int length) throws ArrayIndexOutOfBoundsException {
- if (offset < 0 || length < 0 || offset >= data.length ||
- offset + length > data.length) {
- throw new ArrayIndexOutOfBoundsException(
- "invalid offset/length!");
- }
- return new ByteArraySource(data, offset, length);
- }
-
- /**
- * See {@link #createSource(byte[], int, int).
- * @hide
- */
- @NonNull
- public static Source createSource(@NonNull byte[] data) {
- return createSource(data, 0, data.length);
- }
-
- /**
- * Create a new {@link Source} from a {@link java.nio.ByteBuffer}.
- *
- * <p>The returned {@link Source} effectively takes ownership of the
- * {@link java.nio.ByteBuffer}; i.e. no other code should modify it after
- * this call.</p>
- *
- * Decoding will start from {@link java.nio.ByteBuffer#position()}. The
- * position after decoding is undefined.
- */
- @NonNull
- public static Source createSource(@NonNull ByteBuffer buffer) {
- return new ByteBufferSource(buffer);
- }
-
- /**
- * Internal API used to generate bitmaps for use by Drawables (i.e. BitmapDrawable)
- * @hide
- */
- public static Source createSource(Resources res, InputStream is) {
- return new InputStreamSource(res, is, Bitmap.getDefaultDensity());
- }
-
- /**
- * Internal API used to generate bitmaps for use by Drawables (i.e. BitmapDrawable)
- * @hide
- */
- public static Source createSource(Resources res, InputStream is, int density) {
- return new InputStreamSource(res, is, density);
- }
-
- /**
- * Create a new {@link Source} from a {@link java.io.File}.
- */
- @NonNull
- public static Source createSource(@NonNull File file) {
- return new FileSource(file);
- }
-
- /**
- * Return the width and height of a given sample size.
- *
- * <p>This takes an input that functions like
- * {@link BitmapFactory.Options#inSampleSize}. It returns a width and
- * height that can be acheived by sampling the encoded image. Other widths
- * and heights may be supported, but will require an additional (internal)
- * scaling step. Such internal scaling is *not* supported with
- * {@link #setRequireUnpremultiplied} set to {@code true}.</p>
- *
- * @param sampleSize Sampling rate of the encoded image.
- * @return {@link android.util.Size} of the width and height after
- * sampling.
- */
- @NonNull
- public Size getSampledSize(int sampleSize) {
- return new Size(0, 0);
- }
-
- // Modifiers
- /**
- * Resize the output to have the following size.
- *
- * @param width must be greater than 0.
- * @param height must be greater than 0.
- */
- public void setResize(int width, int height) {
- }
-
- /**
- * Resize based on a sample size.
- *
- * <p>This has the same effect as passing the result of
- * {@link #getSampledSize} to {@link #setResize(int, int)}.</p>
- *
- * @param sampleSize Sampling rate of the encoded image.
- */
- public void setResize(int sampleSize) {
- }
-
- // These need to stay in sync with ImageDecoder.cpp's Allocator enum.
- /**
- * Use the default allocation for the pixel memory.
- *
- * Will typically result in a {@link Bitmap.Config#HARDWARE}
- * allocation, but may be software for small images. In addition, this will
- * switch to software when HARDWARE is incompatible, e.g.
- * {@link #setMutable}, {@link #setAsAlphaMask}.
- */
- public static final int ALLOCATOR_DEFAULT = 0;
-
- /**
- * Use a software allocation for the pixel memory.
- *
- * Useful for drawing to a software {@link Canvas} or for
- * accessing the pixels on the final output.
- */
- public static final int ALLOCATOR_SOFTWARE = 1;
-
- /**
- * Use shared memory for the pixel memory.
- *
- * Useful for sharing across processes.
- */
- public static final int ALLOCATOR_SHARED_MEMORY = 2;
-
- /**
- * Require a {@link Bitmap.Config#HARDWARE} {@link Bitmap}.
- *
- * When this is combined with incompatible options, like
- * {@link #setMutable} or {@link #setAsAlphaMask}, {@link #decodeDrawable}
- * / {@link #decodeBitmap} will throw an
- * {@link java.lang.IllegalStateException}.
- */
- public static final int ALLOCATOR_HARDWARE = 3;
-
- /** @hide **/
- @Retention(SOURCE)
- public @interface Allocator {};
-
- /**
- * Choose the backing for the pixel memory.
- *
- * This is ignored for animated drawables.
- *
- * @param allocator Type of allocator to use.
- */
- public void setAllocator(@Allocator int allocator) { }
-
- /**
- * Specify whether the {@link Bitmap} should have unpremultiplied pixels.
- *
- * By default, ImageDecoder will create a {@link Bitmap} with
- * premultiplied pixels, which is required for drawing with the
- * {@link android.view.View} system (i.e. to a {@link Canvas}). Calling
- * this method with a value of {@code true} will result in
- * {@link #decodeBitmap} returning a {@link Bitmap} with unpremultiplied
- * pixels. See {@link Bitmap#isPremultiplied}. This is incompatible with
- * {@link #decodeDrawable}; attempting to decode an unpremultiplied
- * {@link Drawable} will throw an {@link java.lang.IllegalStateException}.
- */
- public ImageDecoder setRequireUnpremultiplied(boolean requireUnpremultiplied) {
- return this;
- }
-
- /**
- * Modify the image after decoding and scaling.
- *
- * <p>This allows adding effects prior to returning a {@link Drawable} or
- * {@link Bitmap}. For a {@code Drawable} or an immutable {@code Bitmap},
- * this is the only way to process the image after decoding.</p>
- *
- * <p>If set on a nine-patch image, the nine-patch data is ignored.</p>
- *
- * <p>For an animated image, the drawing commands drawn on the
- * {@link Canvas} will be recorded immediately and then applied to each
- * frame.</p>
- */
- public void setPostProcessor(@Nullable PostProcessor p) { }
-
- /**
- * Set (replace) the {@link OnPartialImageListener} on this object.
- *
- * Will be called if there is an error in the input. Without one, a
- * partial {@link Bitmap} will be created.
- */
- public void setOnPartialImageListener(@Nullable OnPartialImageListener l) { }
-
- /**
- * Crop the output to {@code subset} of the (possibly) scaled image.
- *
- * <p>{@code subset} must be contained within the size set by
- * {@link #setResize} or the bounds of the image if setResize was not
- * called. Otherwise an {@link IllegalStateException} will be thrown by
- * {@link #decodeDrawable}/{@link #decodeBitmap}.</p>
- *
- * <p>NOT intended as a replacement for
- * {@link BitmapRegionDecoder#decodeRegion}. This supports all formats,
- * but merely crops the output.</p>
- */
- public void setCrop(@Nullable Rect subset) { }
-
- /**
- * Set a Rect for retrieving nine patch padding.
- *
- * If the image is a nine patch, this Rect will be set to the padding
- * rectangle during decode. Otherwise it will not be modified.
- *
- * @hide
- */
- public void setOutPaddingRect(@NonNull Rect outPadding) {
- mOutPaddingRect = outPadding;
- }
-
- /**
- * Specify whether the {@link Bitmap} should be mutable.
- *
- * <p>By default, a {@link Bitmap} created will be immutable, but that can
- * be changed with this call.</p>
- *
- * <p>Mutable Bitmaps are incompatible with {@link #ALLOCATOR_HARDWARE},
- * because {@link Bitmap.Config#HARDWARE} Bitmaps cannot be mutable.
- * Attempting to combine them will throw an
- * {@link java.lang.IllegalStateException}.</p>
- *
- * <p>Mutable Bitmaps are also incompatible with {@link #decodeDrawable},
- * which would require retrieving the Bitmap from the returned Drawable in
- * order to modify. Attempting to decode a mutable {@link Drawable} will
- * throw an {@link java.lang.IllegalStateException}.</p>
- */
- public ImageDecoder setMutable(boolean mutable) {
- return this;
- }
-
- /**
- * Specify whether to potentially save RAM at the expense of quality.
- *
- * Setting this to {@code true} may result in a {@link Bitmap} with a
- * denser {@link Bitmap.Config}, depending on the image. For example, for
- * an opaque {@link Bitmap}, this may result in a {@link Bitmap.Config}
- * with no alpha information.
- */
- public ImageDecoder setPreferRamOverQuality(boolean preferRamOverQuality) {
- return this;
- }
-
- /**
- * Specify whether to potentially treat the output as an alpha mask.
- *
- * <p>If this is set to {@code true} and the image is encoded in a format
- * with only one channel, treat that channel as alpha. Otherwise this call has
- * no effect.</p>
- *
- * <p>setAsAlphaMask is incompatible with {@link #ALLOCATOR_HARDWARE}. Trying to
- * combine them will result in {@link #decodeDrawable}/
- * {@link #decodeBitmap} throwing an
- * {@link java.lang.IllegalStateException}.</p>
- */
- public ImageDecoder setAsAlphaMask(boolean asAlphaMask) {
- return this;
- }
-
- @Override
- public void close() {
- }
-
- /**
- * Create a {@link Drawable} from a {@code Source}.
- *
- * @param src representing the encoded image.
- * @param listener for learning the {@link ImageInfo} and changing any
- * default settings on the {@code ImageDecoder}. If not {@code null},
- * this will be called on the same thread as {@code decodeDrawable}
- * before that method returns.
- * @return Drawable for displaying the image.
- * @throws IOException if {@code src} is not found, is an unsupported
- * format, or cannot be decoded for any reason.
- */
- @NonNull
- public static Drawable decodeDrawable(@NonNull Source src,
- @Nullable OnHeaderDecodedListener listener) throws IOException {
- Bitmap bitmap = decodeBitmap(src, listener);
- return new BitmapDrawable(src.getResources(), bitmap);
- }
-
- /**
- * See {@link #decodeDrawable(Source, OnHeaderDecodedListener)}.
- */
- @NonNull
- public static Drawable decodeDrawable(@NonNull Source src)
- throws IOException {
- return decodeDrawable(src, null);
- }
-
- /**
- * Create a {@link Bitmap} from a {@code Source}.
- *
- * @param src representing the encoded image.
- * @param listener for learning the {@link ImageInfo} and changing any
- * default settings on the {@code ImageDecoder}. If not {@code null},
- * this will be called on the same thread as {@code decodeBitmap}
- * before that method returns.
- * @return Bitmap containing the image.
- * @throws IOException if {@code src} is not found, is an unsupported
- * format, or cannot be decoded for any reason.
- */
- @NonNull
- public static Bitmap decodeBitmap(@NonNull Source src,
- @Nullable OnHeaderDecodedListener listener) throws IOException {
- TypedValue value = new TypedValue();
- value.density = src.getDensity();
- ImageDecoder decoder = src.createImageDecoder();
- if (listener != null) {
- listener.onHeaderDecoded(decoder, new ImageInfo(decoder), src);
- }
- return BitmapFactory.decodeResourceStream(src.getResources(), value,
- ((InputStreamSource) src).mInputStream, decoder.mOutPaddingRect, null);
- }
-
- /**
- * See {@link #decodeBitmap(Source, OnHeaderDecodedListener)}.
- */
- @NonNull
- public static Bitmap decodeBitmap(@NonNull Source src) throws IOException {
- return decodeBitmap(src, null);
- }
-
- public static final class DecodeException extends IOException {
- /**
- * An Exception was thrown reading the {@link Source}.
- */
- public static final int SOURCE_EXCEPTION = 1;
-
- /**
- * The encoded data was incomplete.
- */
- public static final int SOURCE_INCOMPLETE = 2;
-
- /**
- * The encoded data contained an error.
- */
- public static final int SOURCE_MALFORMED_DATA = 3;
-
- @Error final int mError;
- @NonNull final Source mSource;
-
- DecodeException(@Error int error, @Nullable Throwable cause, @NonNull Source source) {
- super(errorMessage(error, cause), cause);
- mError = error;
- mSource = source;
- }
-
- /**
- * Private method called by JNI.
- */
- @SuppressWarnings("unused")
- DecodeException(@Error int error, @Nullable String msg, @Nullable Throwable cause,
- @NonNull Source source) {
- super(msg + errorMessage(error, cause), cause);
- mError = error;
- mSource = source;
- }
-
- /**
- * Retrieve the reason that decoding was interrupted.
- *
- * <p>If the error is {@link #SOURCE_EXCEPTION}, the underlying
- * {@link java.lang.Throwable} can be retrieved with
- * {@link java.lang.Throwable#getCause}.</p>
- */
- @Error
- public int getError() {
- return mError;
- }
-
- /**
- * Retrieve the {@link Source Source} that was interrupted.
- *
- * <p>This can be used for equality checking to find the Source which
- * failed to completely decode.</p>
- */
- @NonNull
- public Source getSource() {
- return mSource;
- }
-
- private static String errorMessage(@Error int error, @Nullable Throwable cause) {
- switch (error) {
- case SOURCE_EXCEPTION:
- return "Exception in input: " + cause;
- case SOURCE_INCOMPLETE:
- return "Input was incomplete.";
- case SOURCE_MALFORMED_DATA:
- return "Input contained an error.";
- default:
- return "";
- }
- }
- }
-}
diff --git a/bridge/src/android/graphics/ImageDecoder_Delegate.java b/bridge/src/android/graphics/ImageDecoder_Delegate.java
new file mode 100644
index 0000000000..4eebc9d41d
--- /dev/null
+++ b/bridge/src/android/graphics/ImageDecoder_Delegate.java
@@ -0,0 +1,62 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.graphics;
+
+import com.android.layoutlib.bridge.util.NinePatchInputStream;
+import com.android.ninepatch.GraphicsUtilities;
+import com.android.ninepatch.NinePatch;
+import com.android.tools.layoutlib.annotations.LayoutlibDelegate;
+
+import android.annotation.NonNull;
+import android.graphics.Bitmap.Config;
+import android.graphics.ImageDecoder.InputStreamSource;
+import android.graphics.ImageDecoder.OnHeaderDecodedListener;
+import android.graphics.ImageDecoder.Source;
+
+import java.awt.image.BufferedImage;
+import java.io.FileInputStream;
+import java.io.IOException;
+import java.io.InputStream;
+
+public class ImageDecoder_Delegate {
+
+ @LayoutlibDelegate
+ /*package*/ static Bitmap decodeBitmapImpl(@NonNull Source src,
+ @NonNull OnHeaderDecodedListener listener) throws IOException {
+ InputStream stream = src instanceof InputStreamSource ?
+ ((InputStreamSource) src).mInputStream : null;
+ Bitmap bm = ImageDecoder.decodeBitmapImpl_Original(src, listener);
+ if (stream instanceof NinePatchInputStream && bm.getNinePatchChunk() == null) {
+ stream = new FileInputStream(((NinePatchInputStream) stream).getPath());
+ NinePatch ninePatch = NinePatch.load(stream, true /*is9Patch*/, false /* convert */);
+ BufferedImage image = ninePatch.getImage();
+
+ // width and height of the nine patch without the special border.
+ int width = image.getWidth();
+ int height = image.getHeight();
+
+ // Get pixel data from image independently of its type.
+ int[] imageData = GraphicsUtilities.getPixels(image, 0, 0, width, height, null);
+
+ bm = Bitmap.createBitmap(imageData, width, height, Config.ARGB_8888);
+
+ bm.setDensity(src.getDensity());
+ bm.setNinePatchChunk(ninePatch.getChunk().getSerializedChunk());
+ }
+ return bm;
+ }
+}
diff --git a/bridge/src/android/graphics/LayoutlibRenderer.java b/bridge/src/android/graphics/LayoutlibRenderer.java
new file mode 100644
index 0000000000..e96c790261
--- /dev/null
+++ b/bridge/src/android/graphics/LayoutlibRenderer.java
@@ -0,0 +1,48 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.graphics;
+
+import android.annotation.Nullable;
+
+public class LayoutlibRenderer extends HardwareRenderer {
+
+ private float scaleX = 1.0f;
+ private float scaleY = 1.0f;
+
+ /**
+ * We are overriding this method in order to call {@link Canvas#enableZ} (for shadows) and set
+ * the scale
+ */
+ @Override
+ public void setContentRoot(@Nullable RenderNode content) {
+ RecordingCanvas canvas = mRootNode.beginRecording();
+ canvas.scale(scaleX, scaleY);
+ canvas.enableZ();
+ // This way we clear the native image buffer before drawing
+ canvas.drawColor(0, BlendMode.CLEAR);
+ if (content != null) {
+ canvas.drawRenderNode(content);
+ }
+ canvas.disableZ();
+ mRootNode.endRecording();
+ }
+
+ public void setScale(float scaleX, float scaleY) {
+ this.scaleX = scaleX;
+ this.scaleY = scaleY;
+ }
+}
diff --git a/bridge/src/android/graphics/LightingColorFilter_Delegate.java b/bridge/src/android/graphics/LightingColorFilter_Delegate.java
deleted file mode 100644
index 0dd9703503..0000000000
--- a/bridge/src/android/graphics/LightingColorFilter_Delegate.java
+++ /dev/null
@@ -1,59 +0,0 @@
-/*
- * Copyright (C) 2010 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package android.graphics;
-
-import com.android.layoutlib.bridge.impl.DelegateManager;
-import com.android.tools.layoutlib.annotations.LayoutlibDelegate;
-
-/**
- * Delegate implementing the native methods of android.graphics.LightingColorFilter
- *
- * Through the layoutlib_create tool, the original native methods of LightingColorFilter have
- * been replaced by calls to methods of the same name in this delegate class.
- *
- * This class behaves like the original native implementation, but in Java, keeping previously
- * native data into its own objects and mapping them to int that are sent back and forth between
- * it and the original LightingColorFilter class.
- *
- * Because this extends {@link ColorFilter_Delegate}, there's no need to use a
- * {@link DelegateManager}, as all the Shader classes will be added to the manager
- * owned by {@link ColorFilter_Delegate}.
- *
- * @see ColorFilter_Delegate
- *
- */
-public class LightingColorFilter_Delegate extends ColorFilter_Delegate {
-
- // ---- delegate data ----
-
- // ---- Public Helper methods ----
-
- @Override
- public String getSupportMessage() {
- return "Lighting Color Filters are not supported.";
- }
-
- // ---- native methods ----
-
- @LayoutlibDelegate
- /*package*/ static long native_CreateLightingFilter(int mul, int add) {
- LightingColorFilter_Delegate newDelegate = new LightingColorFilter_Delegate();
- return sManager.addNewDelegate(newDelegate);
- }
-
- // ---- Private delegate/helper methods ----
-}
diff --git a/bridge/src/android/graphics/LinearGradient_Delegate.java b/bridge/src/android/graphics/LinearGradient_Delegate.java
deleted file mode 100644
index 4574dd7d80..0000000000
--- a/bridge/src/android/graphics/LinearGradient_Delegate.java
+++ /dev/null
@@ -1,218 +0,0 @@
-/*
- * Copyright (C) 2010 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package android.graphics;
-
-import com.android.ide.common.rendering.api.ILayoutLog;
-import com.android.layoutlib.bridge.Bridge;
-import com.android.layoutlib.bridge.impl.DelegateManager;
-import com.android.tools.layoutlib.annotations.LayoutlibDelegate;
-
-import android.graphics.Shader.TileMode;
-
-import java.awt.image.ColorModel;
-import java.awt.image.DataBufferInt;
-import java.awt.image.Raster;
-import java.awt.image.SampleModel;
-
-/**
- * Delegate implementing the native methods of android.graphics.LinearGradient
- *
- * Through the layoutlib_create tool, the original native methods of LinearGradient have been
- * replaced by calls to methods of the same name in this delegate class.
- *
- * This class behaves like the original native implementation, but in Java, keeping previously
- * native data into its own objects and mapping them to int that are sent back and forth between
- * it and the original LinearGradient class.
- *
- * Because this extends {@link Shader_Delegate}, there's no need to use a {@link DelegateManager},
- * as all the Shader classes will be added to the manager owned by {@link Shader_Delegate}.
- *
- * @see Shader_Delegate
- *
- */
-public final class LinearGradient_Delegate extends Gradient_Delegate {
-
- // ---- delegate data ----
- private java.awt.Paint mJavaPaint;
-
- // ---- Public Helper methods ----
-
- @Override
- public java.awt.Paint getJavaPaint() {
- return mJavaPaint;
- }
-
- // ---- native methods ----
-
- @LayoutlibDelegate
- /*package*/ static long nativeCreate(LinearGradient thisGradient, long matrix,
- float x0, float y0, float x1, float y1, long[] colors, float[] positions,
- int tileMode, long colorSpaceHandle) {
- LinearGradient_Delegate newDelegate = new LinearGradient_Delegate(matrix, x0, y0,
- x1, y1, colors, positions, Shader_Delegate.getTileMode(tileMode));
- return sManager.addNewDelegate(newDelegate);
- }
-
- // ---- Private delegate/helper methods ----
-
- /**
- * Create a shader that draws a linear gradient along a line.
- *
- * @param nativeMatrix reference to the shader's native transformation matrix
- * @param x0 The x-coordinate for the start of the gradient line
- * @param y0 The y-coordinate for the start of the gradient line
- * @param x1 The x-coordinate for the end of the gradient line
- * @param y1 The y-coordinate for the end of the gradient line
- * @param colors The 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
- * the colors are distributed evenly along the gradient line.
- * @param tile The Shader tiling mode
- */
- private LinearGradient_Delegate(long nativeMatrix, float x0, float y0, float x1,
- float y1, long[] colors, float[] positions, TileMode tile) {
- super(nativeMatrix, colors, positions);
- mJavaPaint = new LinearGradientPaint(x0, y0, x1, y1, mColors, mPositions, tile);
- }
-
- // ---- Custom Java Paint ----
- /**
- * Linear Gradient (Java) Paint able to handle more than 2 points, as
- * {@link java.awt.GradientPaint} only supports 2 points and does not support Android's tile
- * modes.
- */
- private class LinearGradientPaint extends GradientPaint {
-
- private final float mX0;
- private final float mY0;
- private final float mDx;
- private final float mDy;
- private final float mDSize2;
-
- public LinearGradientPaint(float x0, float y0, float x1, float y1, int[] colors,
- float[] positions, TileMode tile) {
- super(colors, positions, tile);
- mX0 = x0;
- mY0 = y0;
- mDx = x1 - x0;
- mDy = y1 - y0;
- mDSize2 = mDx * mDx + mDy * mDy;
- }
-
- @Override
- public java.awt.PaintContext createContext(
- java.awt.image.ColorModel colorModel,
- java.awt.Rectangle deviceBounds,
- java.awt.geom.Rectangle2D userBounds,
- java.awt.geom.AffineTransform xform,
- java.awt.RenderingHints hints) {
- precomputeGradientColors();
-
- java.awt.geom.AffineTransform canvasMatrix;
- try {
- canvasMatrix = xform.createInverse();
- } catch (java.awt.geom.NoninvertibleTransformException e) {
- Bridge.getLog().fidelityWarning(ILayoutLog.TAG_MATRIX_INVERSE,
- "Unable to inverse matrix in LinearGradient", e, null, null /*data*/);
- canvasMatrix = new java.awt.geom.AffineTransform();
- }
-
- java.awt.geom.AffineTransform localMatrix = getLocalMatrix();
- try {
- localMatrix = localMatrix.createInverse();
- } catch (java.awt.geom.NoninvertibleTransformException e) {
- Bridge.getLog().fidelityWarning(ILayoutLog.TAG_MATRIX_INVERSE,
- "Unable to inverse matrix in LinearGradient", e, null, null /*data*/);
- localMatrix = new java.awt.geom.AffineTransform();
- }
-
- return new LinearGradientPaintContext(canvasMatrix, localMatrix, colorModel);
- }
-
- private class LinearGradientPaintContext implements java.awt.PaintContext {
-
- private final java.awt.geom.AffineTransform mCanvasMatrix;
- private final java.awt.geom.AffineTransform mLocalMatrix;
- private final java.awt.image.ColorModel mColorModel;
-
- private LinearGradientPaintContext(
- java.awt.geom.AffineTransform canvasMatrix,
- java.awt.geom.AffineTransform localMatrix,
- java.awt.image.ColorModel colorModel) {
- mCanvasMatrix = canvasMatrix;
- mLocalMatrix = localMatrix;
- mColorModel = colorModel.hasAlpha() ? colorModel : ColorModel.getRGBdefault();
- }
-
- @Override
- public void dispose() {
- }
-
- @Override
- public java.awt.image.ColorModel getColorModel() {
- return mColorModel;
- }
-
- @Override
- public java.awt.image.Raster getRaster(int x, int y, int w, int h) {
- int[] data = new int[w*h];
-
- int index = 0;
- float[] pt1 = new float[2];
- float[] pt2 = new float[2];
- for (int iy = 0 ; iy < h ; iy++) {
- for (int ix = 0 ; ix < w ; ix++) {
- // handle the canvas transform
- pt1[0] = x + ix;
- pt1[1] = y + iy;
- mCanvasMatrix.transform(pt1, 0, pt2, 0, 1);
-
- // handle the local matrix.
- pt1[0] = pt2[0];
- pt1[1] = pt2[1];
- mLocalMatrix.transform(pt1, 0, pt2, 0, 1);
-
- data[index++] = getColor(pt2[0], pt2[1]);
- }
- }
-
- DataBufferInt dataBuffer = new DataBufferInt(data, data.length);
- SampleModel colorModel = mColorModel.createCompatibleSampleModel(w, h);
- return Raster.createWritableRaster(colorModel, dataBuffer, null);
- }
- }
-
- /**
- * Returns a color for an arbitrary point.
- */
- private int getColor(float x, float y) {
- float pos;
- if (mDx == 0) {
- pos = (y - mY0) / mDy;
- } else if (mDy == 0) {
- pos = (x - mX0) / mDx;
- } else {
- // find the x position on the gradient vector.
- float _x = (mDx*mDy*(y-mY0) + mDy*mDy*mX0 + mDx*mDx*x) / mDSize2;
- // from it get the position relative to the vector
- pos = (_x - mX0) / mDx;
- }
-
- return getGradientColor(pos);
- }
- }
-}
diff --git a/bridge/src/android/graphics/MaskFilter_Delegate.java b/bridge/src/android/graphics/MaskFilter_Delegate.java
deleted file mode 100644
index e726c5982b..0000000000
--- a/bridge/src/android/graphics/MaskFilter_Delegate.java
+++ /dev/null
@@ -1,64 +0,0 @@
-/*
- * Copyright (C) 2010 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package android.graphics;
-
-import com.android.layoutlib.bridge.impl.DelegateManager;
-import com.android.tools.layoutlib.annotations.LayoutlibDelegate;
-
-/**
- * Delegate implementing the native methods of android.graphics.MaskFilter
- *
- * Through the layoutlib_create tool, the original native methods of MaskFilter have been replaced
- * by calls to methods of the same name in this delegate class.
- *
- * This class behaves like the original native implementation, but in Java, keeping previously
- * native data into its own objects and mapping them to int that are sent back and forth between
- * it and the original MaskFilter class.
- *
- * This also serve as a base class for all MaskFilter delegate classes.
- *
- * @see DelegateManager
- *
- */
-public abstract class MaskFilter_Delegate {
-
- // ---- delegate manager ----
- protected static final DelegateManager<MaskFilter_Delegate> sManager =
- new DelegateManager<MaskFilter_Delegate>(MaskFilter_Delegate.class);
-
- // ---- delegate helper data ----
-
- // ---- delegate data ----
-
- // ---- Public Helper methods ----
-
- public static MaskFilter_Delegate getDelegate(long nativeShader) {
- return sManager.getDelegate(nativeShader);
- }
-
- public abstract boolean isSupported();
- public abstract String getSupportMessage();
-
- // ---- native methods ----
-
- @LayoutlibDelegate
- /*package*/ static void nativeDestructor(long native_filter) {
- sManager.removeJavaReferenceFor(native_filter);
- }
-
- // ---- Private delegate/helper methods ----
-}
diff --git a/bridge/src/android/graphics/Matrix_Delegate.java b/bridge/src/android/graphics/Matrix_Delegate.java
deleted file mode 100644
index d0a0adc5e2..0000000000
--- a/bridge/src/android/graphics/Matrix_Delegate.java
+++ /dev/null
@@ -1,1084 +0,0 @@
-/*
- * Copyright (C) 2010 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package android.graphics;
-
-
-import com.android.ide.common.rendering.api.ILayoutLog;
-import com.android.layoutlib.bridge.Bridge;
-import com.android.layoutlib.bridge.impl.DelegateManager;
-import com.android.tools.layoutlib.annotations.LayoutlibDelegate;
-
-import android.graphics.Matrix.ScaleToFit;
-
-import java.awt.geom.AffineTransform;
-
-import libcore.util.NativeAllocationRegistry_Delegate;
-
-/**
- * Delegate implementing the native methods of android.graphics.Matrix
- *
- * Through the layoutlib_create tool, the original native methods of Matrix have been replaced
- * by calls to methods of the same name in this delegate class.
- *
- * This class behaves like the original native implementation, but in Java, keeping previously
- * native data into its own objects and mapping them to int that are sent back and forth between
- * it and the original Matrix class.
- *
- * @see DelegateManager
- *
- */
-public final class Matrix_Delegate {
-
- private final static int MATRIX_SIZE = 9;
-
- // ---- delegate manager ----
- private static final DelegateManager<Matrix_Delegate> sManager =
- new DelegateManager<Matrix_Delegate>(Matrix_Delegate.class);
- private static long sFinalizer = -1;
-
- // ---- delegate data ----
- private float mValues[] = new float[MATRIX_SIZE];
-
- // ---- Public Helper methods ----
-
- public static Matrix_Delegate getDelegate(long native_instance) {
- return sManager.getDelegate(native_instance);
- }
-
- /**
- * Returns an {@link AffineTransform} matching the given Matrix.
- */
- public static AffineTransform getAffineTransform(Matrix m) {
- Matrix_Delegate delegate = sManager.getDelegate(m.ni());
- if (delegate == null) {
- return null;
- }
-
- return delegate.getAffineTransform();
- }
-
- public static boolean hasPerspective(Matrix m) {
- Matrix_Delegate delegate = sManager.getDelegate(m.ni());
- if (delegate == null) {
- return false;
- }
-
- return delegate.hasPerspective();
- }
-
- /**
- * Sets the content of the matrix with the content of another matrix.
- */
- public void set(Matrix_Delegate matrix) {
- System.arraycopy(matrix.mValues, 0, mValues, 0, MATRIX_SIZE);
- }
-
- /**
- * Sets the content of the matrix with the content of another matrix represented as an array
- * of values.
- */
- public void set(float[] values) {
- System.arraycopy(values, 0, mValues, 0, MATRIX_SIZE);
- }
-
- /**
- * Resets the matrix to be the identity matrix.
- */
- public void reset() {
- reset(mValues);
- }
-
- /**
- * Returns whether or not the matrix is identity.
- */
- public boolean isIdentity() {
- for (int i = 0, k = 0; i < 3; i++) {
- for (int j = 0; j < 3; j++, k++) {
- if (mValues[k] != ((i==j) ? 1 : 0)) {
- return false;
- }
- }
- }
-
- return true;
- }
-
- private static float[] setValues(AffineTransform matrix, float[] values) {
- values[0] = (float) matrix.getScaleX();
- values[1] = (float) matrix.getShearX();
- values[2] = (float) matrix.getTranslateX();
- values[3] = (float) matrix.getShearY();
- values[4] = (float) matrix.getScaleY();
- values[5] = (float) matrix.getTranslateY();
- values[6] = 0.f;
- values[7] = 0.f;
- values[8] = 1.f;
-
- return values;
- }
-
- public static float[] makeValues(AffineTransform matrix) {
- return setValues(matrix, new float[MATRIX_SIZE]);
- }
-
- public static Matrix_Delegate make(AffineTransform matrix) {
- return new Matrix_Delegate(makeValues(matrix));
- }
-
- public boolean mapRect(RectF dst, RectF src) {
- // array with 4 corners
- float[] corners = new float[] {
- src.left, src.top,
- src.right, src.top,
- src.right, src.bottom,
- src.left, src.bottom,
- };
-
- // apply the transform to them.
- mapPoints(corners);
-
- // now put the result in the rect. We take the min/max of Xs and min/max of Ys
- dst.left = Math.min(Math.min(corners[0], corners[2]), Math.min(corners[4], corners[6]));
- dst.right = Math.max(Math.max(corners[0], corners[2]), Math.max(corners[4], corners[6]));
-
- dst.top = Math.min(Math.min(corners[1], corners[3]), Math.min(corners[5], corners[7]));
- dst.bottom = Math.max(Math.max(corners[1], corners[3]), Math.max(corners[5], corners[7]));
-
-
- return (computeTypeMask() & kRectStaysRect_Mask) != 0;
- }
-
-
- /**
- * Returns an {@link AffineTransform} matching the matrix.
- */
- public AffineTransform getAffineTransform() {
- return getAffineTransform(mValues);
- }
-
- public boolean hasPerspective() {
- return (mValues[6] != 0 || mValues[7] != 0 || mValues[8] != 1);
- }
-
-
-
- // ---- native methods ----
-
- @LayoutlibDelegate
- /*package*/ static long nCreate(long native_src_or_zero) {
- // create the delegate
- Matrix_Delegate newDelegate = new Matrix_Delegate();
-
- // copy from values if needed.
- if (native_src_or_zero > 0) {
- Matrix_Delegate oldDelegate = sManager.getDelegate(native_src_or_zero);
- if (oldDelegate != null) {
- System.arraycopy(
- oldDelegate.mValues, 0,
- newDelegate.mValues, 0,
- MATRIX_SIZE);
- }
- }
-
- return sManager.addNewDelegate(newDelegate);
- }
-
- @LayoutlibDelegate
- /*package*/ static boolean nIsIdentity(long native_object) {
- Matrix_Delegate d = sManager.getDelegate(native_object);
- if (d == null) {
- return false;
- }
-
- return d.isIdentity();
- }
-
- @LayoutlibDelegate
- /*package*/ static boolean nIsAffine(long native_object) {
- Matrix_Delegate d = sManager.getDelegate(native_object);
- if (d == null) {
- return true;
- }
-
- return (d.computeTypeMask() & kPerspective_Mask) == 0;
- }
-
- @LayoutlibDelegate
- /*package*/ static boolean nRectStaysRect(long native_object) {
- Matrix_Delegate d = sManager.getDelegate(native_object);
- if (d == null) {
- return true;
- }
-
- return (d.computeTypeMask() & kRectStaysRect_Mask) != 0;
- }
-
- @LayoutlibDelegate
- /*package*/ static void nReset(long native_object) {
- Matrix_Delegate d = sManager.getDelegate(native_object);
- if (d == null) {
- return;
- }
-
- reset(d.mValues);
- }
-
- @LayoutlibDelegate
- /*package*/ static void nSet(long native_object, long other) {
- Matrix_Delegate d = sManager.getDelegate(native_object);
- if (d == null) {
- return;
- }
-
- Matrix_Delegate src = sManager.getDelegate(other);
- if (src == null) {
- return;
- }
-
- System.arraycopy(src.mValues, 0, d.mValues, 0, MATRIX_SIZE);
- }
-
- @LayoutlibDelegate
- /*package*/ static void nSetTranslate(long native_object, float dx, float dy) {
- Matrix_Delegate d = sManager.getDelegate(native_object);
- if (d == null) {
- return;
- }
-
- setTranslate(d.mValues, dx, dy);
- }
-
- @LayoutlibDelegate
- /*package*/ static void nSetScale(long native_object, float sx, float sy,
- float px, float py) {
- Matrix_Delegate d = sManager.getDelegate(native_object);
- if (d == null) {
- return;
- }
-
- d.mValues = getScale(sx, sy, px, py);
- }
-
- @LayoutlibDelegate
- /*package*/ static void nSetScale(long native_object, float sx, float sy) {
- Matrix_Delegate d = sManager.getDelegate(native_object);
- if (d == null) {
- return;
- }
-
- d.mValues[0] = sx;
- d.mValues[1] = 0;
- d.mValues[2] = 0;
- d.mValues[3] = 0;
- d.mValues[4] = sy;
- d.mValues[5] = 0;
- d.mValues[6] = 0;
- d.mValues[7] = 0;
- d.mValues[8] = 1;
- }
-
- @LayoutlibDelegate
- /*package*/ static void nSetRotate(long native_object, float degrees, float px, float py) {
- Matrix_Delegate d = sManager.getDelegate(native_object);
- if (d == null) {
- return;
- }
-
- d.mValues = getRotate(degrees, px, py);
- }
-
- @LayoutlibDelegate
- /*package*/ static void nSetRotate(long native_object, float degrees) {
- Matrix_Delegate d = sManager.getDelegate(native_object);
- if (d == null) {
- return;
- }
-
- setRotate(d.mValues, degrees);
- }
-
- @LayoutlibDelegate
- /*package*/ static void nSetSinCos(long native_object, float sinValue, float cosValue,
- float px, float py) {
- Matrix_Delegate d = sManager.getDelegate(native_object);
- if (d == null) {
- return;
- }
-
- // TODO: do it in one pass
-
- // translate so that the pivot is in 0,0
- setTranslate(d.mValues, -px, -py);
-
- // scale
- d.postTransform(getRotate(sinValue, cosValue));
- // translate back the pivot
- d.postTransform(getTranslate(px, py));
- }
-
- @LayoutlibDelegate
- /*package*/ static void nSetSinCos(long native_object, float sinValue, float cosValue) {
- Matrix_Delegate d = sManager.getDelegate(native_object);
- if (d == null) {
- return;
- }
-
- setRotate(d.mValues, sinValue, cosValue);
- }
-
- @LayoutlibDelegate
- /*package*/ static void nSetSkew(long native_object, float kx, float ky,
- float px, float py) {
- Matrix_Delegate d = sManager.getDelegate(native_object);
- if (d == null) {
- return;
- }
-
- d.mValues = getSkew(kx, ky, px, py);
- }
-
- @LayoutlibDelegate
- /*package*/ static void nSetSkew(long native_object, float kx, float ky) {
- Matrix_Delegate d = sManager.getDelegate(native_object);
- if (d == null) {
- return;
- }
-
- d.mValues[0] = 1;
- d.mValues[1] = kx;
- d.mValues[2] = -0;
- d.mValues[3] = ky;
- d.mValues[4] = 1;
- d.mValues[5] = 0;
- d.mValues[6] = 0;
- d.mValues[7] = 0;
- d.mValues[8] = 1;
- }
-
- @LayoutlibDelegate
- /*package*/ static void nSetConcat(long native_object, long a, long b) {
- if (a == native_object) {
- nPreConcat(native_object, b);
- return;
- } else if (b == native_object) {
- nPostConcat(native_object, a);
- return;
- }
-
- Matrix_Delegate d = sManager.getDelegate(native_object);
- Matrix_Delegate a_mtx = sManager.getDelegate(a);
- Matrix_Delegate b_mtx = sManager.getDelegate(b);
- if (d != null && a_mtx != null && b_mtx != null) {
- multiply(d.mValues, a_mtx.mValues, b_mtx.mValues);
- }
- }
-
- @LayoutlibDelegate
- /*package*/ static void nPreTranslate(long native_object, float dx, float dy) {
- Matrix_Delegate d = sManager.getDelegate(native_object);
- if (d != null) {
- d.preTransform(getTranslate(dx, dy));
- }
- }
-
- @LayoutlibDelegate
- /*package*/ static void nPreScale(long native_object, float sx, float sy,
- float px, float py) {
- Matrix_Delegate d = sManager.getDelegate(native_object);
- if (d != null) {
- d.preTransform(getScale(sx, sy, px, py));
- }
- }
-
- @LayoutlibDelegate
- /*package*/ static void nPreScale(long native_object, float sx, float sy) {
- Matrix_Delegate d = sManager.getDelegate(native_object);
- if (d != null) {
- d.preTransform(getScale(sx, sy));
- }
- }
-
- @LayoutlibDelegate
- /*package*/ static void nPreRotate(long native_object, float degrees,
- float px, float py) {
- Matrix_Delegate d = sManager.getDelegate(native_object);
- if (d != null) {
- d.preTransform(getRotate(degrees, px, py));
- }
- }
-
- @LayoutlibDelegate
- /*package*/ static void nPreRotate(long native_object, float degrees) {
- Matrix_Delegate d = sManager.getDelegate(native_object);
- if (d != null) {
-
- double rad = Math.toRadians(degrees);
- float sin = (float) Math.sin(rad);
- float cos = (float) Math.cos(rad);
-
- d.preTransform(getRotate(sin, cos));
- }
- }
-
- @LayoutlibDelegate
- /*package*/ static void nPreSkew(long native_object, float kx, float ky,
- float px, float py) {
- Matrix_Delegate d = sManager.getDelegate(native_object);
- if (d != null) {
- d.preTransform(getSkew(kx, ky, px, py));
- }
- }
-
- @LayoutlibDelegate
- /*package*/ static void nPreSkew(long native_object, float kx, float ky) {
- Matrix_Delegate d = sManager.getDelegate(native_object);
- if (d != null) {
- d.preTransform(getSkew(kx, ky));
- }
- }
-
- @LayoutlibDelegate
- /*package*/ static void nPreConcat(long native_object, long other_matrix) {
- Matrix_Delegate d = sManager.getDelegate(native_object);
- Matrix_Delegate other = sManager.getDelegate(other_matrix);
- if (d != null && other != null) {
- d.preTransform(other.mValues);
- }
- }
-
- @LayoutlibDelegate
- /*package*/ static void nPostTranslate(long native_object, float dx, float dy) {
- Matrix_Delegate d = sManager.getDelegate(native_object);
- if (d != null) {
- d.postTransform(getTranslate(dx, dy));
- }
- }
-
- @LayoutlibDelegate
- /*package*/ static void nPostScale(long native_object, float sx, float sy,
- float px, float py) {
- Matrix_Delegate d = sManager.getDelegate(native_object);
- if (d != null) {
- d.postTransform(getScale(sx, sy, px, py));
- }
- }
-
- @LayoutlibDelegate
- /*package*/ static void nPostScale(long native_object, float sx, float sy) {
- Matrix_Delegate d = sManager.getDelegate(native_object);
- if (d != null) {
- d.postTransform(getScale(sx, sy));
- }
- }
-
- @LayoutlibDelegate
- /*package*/ static void nPostRotate(long native_object, float degrees,
- float px, float py) {
- Matrix_Delegate d = sManager.getDelegate(native_object);
- if (d != null) {
- d.postTransform(getRotate(degrees, px, py));
- }
- }
-
- @LayoutlibDelegate
- /*package*/ static void nPostRotate(long native_object, float degrees) {
- Matrix_Delegate d = sManager.getDelegate(native_object);
- if (d != null) {
- d.postTransform(getRotate(degrees));
- }
- }
-
- @LayoutlibDelegate
- /*package*/ static void nPostSkew(long native_object, float kx, float ky,
- float px, float py) {
- Matrix_Delegate d = sManager.getDelegate(native_object);
- if (d != null) {
- d.postTransform(getSkew(kx, ky, px, py));
- }
- }
-
- @LayoutlibDelegate
- /*package*/ static void nPostSkew(long native_object, float kx, float ky) {
- Matrix_Delegate d = sManager.getDelegate(native_object);
- if (d != null) {
- d.postTransform(getSkew(kx, ky));
- }
- }
-
- @LayoutlibDelegate
- /*package*/ static void nPostConcat(long native_object, long other_matrix) {
- Matrix_Delegate d = sManager.getDelegate(native_object);
- Matrix_Delegate other = sManager.getDelegate(other_matrix);
- if (d != null && other != null) {
- d.postTransform(other.mValues);
- }
- }
-
- @LayoutlibDelegate
- /*package*/ static boolean nSetRectToRect(long native_object, RectF src,
- RectF dst, int stf) {
- Matrix_Delegate d = sManager.getDelegate(native_object);
- if (d == null) {
- return false;
- }
-
- if (src.isEmpty()) {
- reset(d.mValues);
- return false;
- }
-
- if (dst.isEmpty()) {
- d.mValues[0] = d.mValues[1] = d.mValues[2] = d.mValues[3] = d.mValues[4] = d.mValues[5]
- = d.mValues[6] = d.mValues[7] = 0;
- d.mValues[8] = 1;
- } else {
- float tx, sx = dst.width() / src.width();
- float ty, sy = dst.height() / src.height();
- boolean xLarger = false;
-
- if (stf != ScaleToFit.FILL.nativeInt) {
- if (sx > sy) {
- xLarger = true;
- sx = sy;
- } else {
- sy = sx;
- }
- }
-
- tx = dst.left - src.left * sx;
- ty = dst.top - src.top * sy;
- if (stf == ScaleToFit.CENTER.nativeInt || stf == ScaleToFit.END.nativeInt) {
- float diff;
-
- if (xLarger) {
- diff = dst.width() - src.width() * sy;
- } else {
- diff = dst.height() - src.height() * sy;
- }
-
- if (stf == ScaleToFit.CENTER.nativeInt) {
- diff = diff / 2;
- }
-
- if (xLarger) {
- tx += diff;
- } else {
- ty += diff;
- }
- }
-
- d.mValues[0] = sx;
- d.mValues[4] = sy;
- d.mValues[2] = tx;
- d.mValues[5] = ty;
- d.mValues[1] = d.mValues[3] = d.mValues[6] = d.mValues[7] = 0;
-
- }
- // shared cleanup
- d.mValues[8] = 1;
- return true;
- }
-
- @LayoutlibDelegate
- /*package*/ static boolean nSetPolyToPoly(long native_object, float[] src, int srcIndex,
- float[] dst, int dstIndex, int pointCount) {
- // FIXME
- Bridge.getLog().fidelityWarning(ILayoutLog.TAG_UNSUPPORTED,
- "Matrix.setPolyToPoly is not supported.",
- null, null, null /*data*/);
- return false;
- }
-
- @LayoutlibDelegate
- /*package*/ static boolean nInvert(long native_object, long inverse) {
- Matrix_Delegate d = sManager.getDelegate(native_object);
- if (d == null) {
- return false;
- }
-
- Matrix_Delegate inv_mtx = sManager.getDelegate(inverse);
- if (inv_mtx == null) {
- return false;
- }
-
- float det = d.mValues[0] * (d.mValues[4] * d.mValues[8] - d.mValues[5] * d.mValues[7])
- + d.mValues[1] * (d.mValues[5] * d.mValues[6] - d.mValues[3] * d.mValues[8])
- + d.mValues[2] * (d.mValues[3] * d.mValues[7] - d.mValues[4] * d.mValues[6]);
-
- if (det == 0.0) {
- return false;
- }
-
- inv_mtx.mValues[0] = (d.mValues[4] * d.mValues[8] - d.mValues[5] * d.mValues[7]) / det;
- inv_mtx.mValues[1] = (d.mValues[2] * d.mValues[7] - d.mValues[1] * d.mValues[8]) / det;
- inv_mtx.mValues[2] = (d.mValues[1] * d.mValues[5] - d.mValues[2] * d.mValues[4]) / det;
- inv_mtx.mValues[3] = (d.mValues[5] * d.mValues[6] - d.mValues[3] * d.mValues[8]) / det;
- inv_mtx.mValues[4] = (d.mValues[0] * d.mValues[8] - d.mValues[2] * d.mValues[6]) / det;
- inv_mtx.mValues[5] = (d.mValues[2] * d.mValues[3] - d.mValues[0] * d.mValues[5]) / det;
- inv_mtx.mValues[6] = (d.mValues[3] * d.mValues[7] - d.mValues[4] * d.mValues[6]) / det;
- inv_mtx.mValues[7] = (d.mValues[1] * d.mValues[6] - d.mValues[0] * d.mValues[7]) / det;
- inv_mtx.mValues[8] = (d.mValues[0] * d.mValues[4] - d.mValues[1] * d.mValues[3]) / det;
-
- return true;
- }
-
- @LayoutlibDelegate
- /*package*/ static void nMapPoints(long native_object, float[] dst, int dstIndex,
- float[] src, int srcIndex, int ptCount, boolean isPts) {
- Matrix_Delegate d = sManager.getDelegate(native_object);
- if (d == null) {
- return;
- }
-
- if (isPts) {
- d.mapPoints(dst, dstIndex, src, srcIndex, ptCount);
- } else {
- d.mapVectors(dst, dstIndex, src, srcIndex, ptCount);
- }
- }
-
- @LayoutlibDelegate
- /*package*/ static boolean nMapRect(long native_object, RectF dst, RectF src) {
- Matrix_Delegate d = sManager.getDelegate(native_object);
- if (d == null) {
- return false;
- }
-
- return d.mapRect(dst, src);
- }
-
- @LayoutlibDelegate
- /*package*/ static float nMapRadius(long native_object, float radius) {
- Matrix_Delegate d = sManager.getDelegate(native_object);
- if (d == null) {
- return 0.f;
- }
-
- float[] src = new float[] { radius, 0.f, 0.f, radius };
- d.mapVectors(src, 0, src, 0, 2);
-
- float l1 = (float) Math.hypot(src[0], src[1]);
- float l2 = (float) Math.hypot(src[2], src[3]);
- return (float) Math.sqrt(l1 * l2);
- }
-
- @LayoutlibDelegate
- /*package*/ static void nGetValues(long native_object, float[] values) {
- Matrix_Delegate d = sManager.getDelegate(native_object);
- if (d == null) {
- return;
- }
-
- System.arraycopy(d.mValues, 0, values, 0, MATRIX_SIZE);
- }
-
- @LayoutlibDelegate
- /*package*/ static void nSetValues(long native_object, float[] values) {
- Matrix_Delegate d = sManager.getDelegate(native_object);
- if (d == null) {
- return;
- }
-
- System.arraycopy(values, 0, d.mValues, 0, MATRIX_SIZE);
- }
-
- @LayoutlibDelegate
- /*package*/ static boolean nEquals(long native_a, long native_b) {
- Matrix_Delegate a = sManager.getDelegate(native_a);
- if (a == null) {
- return false;
- }
-
- Matrix_Delegate b = sManager.getDelegate(native_b);
- if (b == null) {
- return false;
- }
-
- for (int i = 0 ; i < MATRIX_SIZE ; i++) {
- if (a.mValues[i] != b.mValues[i]) {
- return false;
- }
- }
-
- return true;
- }
-
- @LayoutlibDelegate
- /*package*/ static long nGetNativeFinalizer() {
- synchronized (Matrix_Delegate.class) {
- if (sFinalizer == -1) {
- sFinalizer = NativeAllocationRegistry_Delegate.createFinalizer(sManager::removeJavaReferenceFor);
- }
- }
- return sFinalizer;
- }
-
- // ---- Private helper methods ----
-
- /*package*/ static AffineTransform getAffineTransform(float[] matrix) {
- // the AffineTransform constructor takes the value in a different order
- // for a matrix [ 0 1 2 ]
- // [ 3 4 5 ]
- // the order is 0, 3, 1, 4, 2, 5...
- return new AffineTransform(
- matrix[0], matrix[3], matrix[1],
- matrix[4], matrix[2], matrix[5]);
- }
-
- /**
- * Reset a matrix to the identity
- */
- private static void reset(float[] mtx) {
- for (int i = 0, k = 0; i < 3; i++) {
- for (int j = 0; j < 3; j++, k++) {
- mtx[k] = ((i==j) ? 1 : 0);
- }
- }
- }
-
- @SuppressWarnings("unused")
- private final static int kIdentity_Mask = 0;
- private final static int kTranslate_Mask = 0x01; //!< set if the matrix has translation
- private final static int kScale_Mask = 0x02; //!< set if the matrix has X or Y scale
- private final static int kAffine_Mask = 0x04; //!< set if the matrix skews or rotates
- private final static int kPerspective_Mask = 0x08; //!< set if the matrix is in perspective
- private final static int kRectStaysRect_Mask = 0x10;
- @SuppressWarnings("unused")
- private final static int kUnknown_Mask = 0x80;
-
- @SuppressWarnings("unused")
- private final static int kAllMasks = kTranslate_Mask |
- kScale_Mask |
- kAffine_Mask |
- kPerspective_Mask |
- kRectStaysRect_Mask;
-
- // these guys align with the masks, so we can compute a mask from a variable 0/1
- @SuppressWarnings("unused")
- private final static int kTranslate_Shift = 0;
- @SuppressWarnings("unused")
- private final static int kScale_Shift = 1;
- @SuppressWarnings("unused")
- private final static int kAffine_Shift = 2;
- @SuppressWarnings("unused")
- private final static int kPerspective_Shift = 3;
- private final static int kRectStaysRect_Shift = 4;
-
- private int computeTypeMask() {
- int mask = 0;
-
- if (mValues[6] != 0. || mValues[7] != 0. || mValues[8] != 1.) {
- mask |= kPerspective_Mask;
- }
-
- if (mValues[2] != 0. || mValues[5] != 0.) {
- mask |= kTranslate_Mask;
- }
-
- float m00 = mValues[0];
- float m01 = mValues[1];
- float m10 = mValues[3];
- float m11 = mValues[4];
-
- if (m01 != 0. || m10 != 0.) {
- mask |= kAffine_Mask;
- }
-
- if (m00 != 1. || m11 != 1.) {
- mask |= kScale_Mask;
- }
-
- if ((mask & kPerspective_Mask) == 0) {
- // map non-zero to 1
- int im00 = m00 != 0 ? 1 : 0;
- int im01 = m01 != 0 ? 1 : 0;
- int im10 = m10 != 0 ? 1 : 0;
- int im11 = m11 != 0 ? 1 : 0;
-
- // record if the (p)rimary and (s)econdary diagonals are all 0 or
- // all non-zero (answer is 0 or 1)
- int dp0 = (im00 | im11) ^ 1; // true if both are 0
- int dp1 = im00 & im11; // true if both are 1
- int ds0 = (im01 | im10) ^ 1; // true if both are 0
- int ds1 = im01 & im10; // true if both are 1
-
- // return 1 if primary is 1 and secondary is 0 or
- // primary is 0 and secondary is 1
- mask |= ((dp0 & ds1) | (dp1 & ds0)) << kRectStaysRect_Shift;
- }
-
- return mask;
- }
-
- private Matrix_Delegate() {
- reset();
- }
-
- private Matrix_Delegate(float[] values) {
- System.arraycopy(values, 0, mValues, 0, MATRIX_SIZE);
- }
-
- /**
- * Adds the given transformation to the current Matrix
- * <p/>This in effect does this = this*matrix
- * @param matrix
- */
- private void postTransform(float[] matrix) {
- float[] tmp = new float[9];
- multiply(tmp, mValues, matrix);
- mValues = tmp;
- }
-
- /**
- * Adds the given transformation to the current Matrix
- * <p/>This in effect does this = matrix*this
- * @param matrix
- */
- private void preTransform(float[] matrix) {
- float[] tmp = new float[9];
- multiply(tmp, matrix, mValues);
- mValues = tmp;
- }
-
- /**
- * Apply this matrix to the array of 2D points specified by src, and write
- * the transformed points into the array of points specified by dst. The
- * two arrays represent their "points" as pairs of floats [x, y].
- *
- * @param dst The array of dst points (x,y pairs)
- * @param dstIndex The index of the first [x,y] pair of dst floats
- * @param src The array of src points (x,y pairs)
- * @param srcIndex The index of the first [x,y] pair of src floats
- * @param pointCount The number of points (x,y pairs) to transform
- */
-
- private void mapPoints(float[] dst, int dstIndex, float[] src, int srcIndex,
- int pointCount) {
- final int count = pointCount * 2;
-
- float[] tmpDest = dst;
- boolean inPlace = dst == src;
- if (inPlace) {
- tmpDest = new float[dstIndex + count];
- }
-
- for (int i = 0 ; i < count ; i += 2) {
- // just in case we are doing in place, we better put this in temp vars
- float x = mValues[0] * src[i + srcIndex] +
- mValues[1] * src[i + srcIndex + 1] +
- mValues[2];
- float y = mValues[3] * src[i + srcIndex] +
- mValues[4] * src[i + srcIndex + 1] +
- mValues[5];
-
- tmpDest[i + dstIndex] = x;
- tmpDest[i + dstIndex + 1] = y;
- }
-
- if (inPlace) {
- System.arraycopy(tmpDest, dstIndex, dst, dstIndex, count);
- }
- }
-
- /**
- * Apply this matrix to the array of 2D points, and write the transformed
- * points back into the array
- *
- * @param pts The array [x0, y0, x1, y1, ...] of points to transform.
- */
-
- private void mapPoints(float[] pts) {
- mapPoints(pts, 0, pts, 0, pts.length >> 1);
- }
-
- private void mapVectors(float[] dst, int dstIndex, float[] src, int srcIndex, int ptCount) {
- if (hasPerspective()) {
- // transform the (0,0) point
- float[] origin = new float[] { 0.f, 0.f};
- mapPoints(origin);
-
- // translate the vector data as points
- mapPoints(dst, dstIndex, src, srcIndex, ptCount);
-
- // then substract the transformed origin.
- final int count = ptCount * 2;
- for (int i = 0 ; i < count ; i += 2) {
- dst[dstIndex + i] = dst[dstIndex + i] - origin[0];
- dst[dstIndex + i + 1] = dst[dstIndex + i + 1] - origin[1];
- }
- } else {
- // make a copy of the matrix
- Matrix_Delegate copy = new Matrix_Delegate(mValues);
-
- // remove the translation
- setTranslate(copy.mValues, 0, 0);
-
- // map the content as points.
- copy.mapPoints(dst, dstIndex, src, srcIndex, ptCount);
- }
- }
-
- /**
- * multiply two matrices and store them in a 3rd.
- * <p/>This in effect does dest = a*b
- * dest cannot be the same as a or b.
- */
- /*package*/ static void multiply(float dest[], float[] a, float[] b) {
- // first row
- dest[0] = b[0] * a[0] + b[1] * a[3] + b[2] * a[6];
- dest[1] = b[0] * a[1] + b[1] * a[4] + b[2] * a[7];
- dest[2] = b[0] * a[2] + b[1] * a[5] + b[2] * a[8];
-
- // 2nd row
- dest[3] = b[3] * a[0] + b[4] * a[3] + b[5] * a[6];
- dest[4] = b[3] * a[1] + b[4] * a[4] + b[5] * a[7];
- dest[5] = b[3] * a[2] + b[4] * a[5] + b[5] * a[8];
-
- // 3rd row
- dest[6] = b[6] * a[0] + b[7] * a[3] + b[8] * a[6];
- dest[7] = b[6] * a[1] + b[7] * a[4] + b[8] * a[7];
- dest[8] = b[6] * a[2] + b[7] * a[5] + b[8] * a[8];
- }
-
- /**
- * Returns a matrix that represents a given translate
- * @param dx
- * @param dy
- * @return
- */
- /*package*/ static float[] getTranslate(float dx, float dy) {
- return setTranslate(new float[9], dx, dy);
- }
-
- /*package*/ static float[] setTranslate(float[] dest, float dx, float dy) {
- dest[0] = 1;
- dest[1] = 0;
- dest[2] = dx;
- dest[3] = 0;
- dest[4] = 1;
- dest[5] = dy;
- dest[6] = 0;
- dest[7] = 0;
- dest[8] = 1;
- return dest;
- }
-
- /*package*/ static float[] getScale(float sx, float sy) {
- return new float[] { sx, 0, 0, 0, sy, 0, 0, 0, 1 };
- }
-
- /**
- * Returns a matrix that represents the given scale info.
- * @param sx
- * @param sy
- * @param px
- * @param py
- */
- /*package*/ static float[] getScale(float sx, float sy, float px, float py) {
- float[] tmp = new float[9];
- float[] tmp2 = new float[9];
-
- // TODO: do it in one pass
-
- // translate tmp so that the pivot is in 0,0
- setTranslate(tmp, -px, -py);
-
- // scale into tmp2
- multiply(tmp2, tmp, getScale(sx, sy));
-
- // translate back the pivot back into tmp
- multiply(tmp, tmp2, getTranslate(px, py));
-
- return tmp;
- }
-
-
- /*package*/ static float[] getRotate(float degrees) {
- double rad = Math.toRadians(degrees);
- float sin = (float)Math.sin(rad);
- float cos = (float)Math.cos(rad);
-
- return getRotate(sin, cos);
- }
-
- /*package*/ static float[] getRotate(float sin, float cos) {
- return setRotate(new float[9], sin, cos);
- }
-
- /*package*/ static float[] setRotate(float[] dest, float degrees) {
- double rad = Math.toRadians(degrees);
- float sin = (float)Math.sin(rad);
- float cos = (float)Math.cos(rad);
-
- return setRotate(dest, sin, cos);
- }
-
- /*package*/ static float[] setRotate(float[] dest, float sin, float cos) {
- dest[0] = cos;
- dest[1] = -sin;
- dest[2] = 0;
- dest[3] = sin;
- dest[4] = cos;
- dest[5] = 0;
- dest[6] = 0;
- dest[7] = 0;
- dest[8] = 1;
- return dest;
- }
-
- /*package*/ static float[] getRotate(float degrees, float px, float py) {
- float[] tmp = new float[9];
- float[] tmp2 = new float[9];
-
- // TODO: do it in one pass
-
- // translate so that the pivot is in 0,0
- setTranslate(tmp, -px, -py);
-
- // rotate into tmp2
- double rad = Math.toRadians(degrees);
- float cos = (float)Math.cos(rad);
- float sin = (float)Math.sin(rad);
- multiply(tmp2, tmp, getRotate(sin, cos));
-
- // translate back the pivot back into tmp
- multiply(tmp, tmp2, getTranslate(px, py));
-
- return tmp;
- }
-
- /*package*/ static float[] getSkew(float kx, float ky) {
- return new float[] { 1, kx, 0, ky, 1, 0, 0, 0, 1 };
- }
-
- /*package*/ static float[] getSkew(float kx, float ky, float px, float py) {
- float[] tmp = new float[9];
- float[] tmp2 = new float[9];
-
- // TODO: do it in one pass
-
- // translate so that the pivot is in 0,0
- setTranslate(tmp, -px, -py);
-
- // skew into tmp2
- multiply(tmp2, tmp, new float[] { 1, kx, 0, ky, 1, 0, 0, 0, 1 });
- // translate back the pivot back into tmp
- multiply(tmp, tmp2, getTranslate(px, py));
-
- return tmp;
- }
-}
diff --git a/bridge/src/android/graphics/NinePatch_Delegate.java b/bridge/src/android/graphics/NinePatch_Delegate.java
deleted file mode 100644
index 28e682b6d3..0000000000
--- a/bridge/src/android/graphics/NinePatch_Delegate.java
+++ /dev/null
@@ -1,181 +0,0 @@
-/*
- * Copyright (C) 2010 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package android.graphics;
-
-import com.android.ide.common.rendering.api.ILayoutLog;
-import com.android.layoutlib.bridge.Bridge;
-import com.android.layoutlib.bridge.impl.DelegateManager;
-import com.android.ninepatch.NinePatchChunk;
-import com.android.tools.layoutlib.annotations.LayoutlibDelegate;
-
-import android.graphics.drawable.NinePatchDrawable;
-
-import java.io.ByteArrayInputStream;
-import java.io.ByteArrayOutputStream;
-import java.io.IOException;
-import java.io.ObjectInputStream;
-import java.io.ObjectOutputStream;
-import java.lang.ref.SoftReference;
-import java.util.HashMap;
-import java.util.Map;
-
-/**
- * Delegate implementing the native methods of android.graphics.NinePatch
- *
- * Through the layoutlib_create tool, the original native methods of NinePatch have been replaced
- * by calls to methods of the same name in this delegate class.
- *
- * Because it's a stateless class to start with, there's no need to keep a {@link DelegateManager}
- * around to map int to instance of the delegate.
- *
- */
-public final class NinePatch_Delegate {
-
- // ---- delegate manager ----
- private static final DelegateManager<NinePatch_Delegate> sManager =
- new DelegateManager<>(NinePatch_Delegate.class);
-
- // ---- delegate helper data ----
- /**
- * Cache map for {@link NinePatchChunk}.
- * When the chunks are created they are serialized into a byte[], and both are put
- * in the cache, using a {@link SoftReference} for the chunk. The default Java classes
- * for {@link NinePatch} and {@link NinePatchDrawable} only reference to the byte[] data, and
- * provide this for drawing.
- * Using the cache map allows us to not have to deserialize the byte[] back into a
- * {@link NinePatchChunk} every time a rendering is done.
- */
- private final static Map<byte[], SoftReference<NinePatchChunk>> sChunkCache = new HashMap<>();
-
- // ---- delegate data ----
- private byte[] chunk;
-
-
- // ---- Public Helper methods ----
-
- /**
- * Serializes the given chunk.
- *
- * @return the serialized data for the chunk.
- */
- public static byte[] serialize(NinePatchChunk chunk) {
- // serialize the chunk to get a byte[]
- ByteArrayOutputStream baos = new ByteArrayOutputStream();
- ObjectOutputStream oos = null;
- try {
- oos = new ObjectOutputStream(baos);
- oos.writeObject(chunk);
- } catch (IOException e) {
- Bridge.getLog().error(null, "Failed to serialize NinePatchChunk.", e, null,
- null /*data*/);
- return null;
- } finally {
- if (oos != null) {
- try {
- oos.close();
- } catch (IOException ignored) {
- }
- }
- }
-
- // get the array and add it to the cache
- byte[] array = baos.toByteArray();
- sChunkCache.put(array, new SoftReference<>(chunk));
- return array;
- }
-
- /**
- * Returns a {@link NinePatchChunk} object for the given serialized representation.
- *
- * If the chunk is present in the cache then the object from the cache is returned, otherwise
- * the array is deserialized into a {@link NinePatchChunk} object.
- *
- * @param array the serialized representation of the chunk.
- * @return the NinePatchChunk or null if deserialization failed.
- */
- public static NinePatchChunk getChunk(byte[] array) {
- SoftReference<NinePatchChunk> chunkRef = sChunkCache.get(array);
- NinePatchChunk chunk = chunkRef == null ? null : chunkRef.get();
- if (chunk == null) {
- ByteArrayInputStream bais = new ByteArrayInputStream(array);
- try (ObjectInputStream ois = new ObjectInputStream(bais)) {
- chunk = (NinePatchChunk) ois.readObject();
-
- // put back the chunk in the cache
- if (chunk != null) {
- sChunkCache.put(array, new SoftReference<>(chunk));
- }
- } catch (IOException e) {
- Bridge.getLog().error(ILayoutLog.TAG_BROKEN,
- "Failed to deserialize NinePatchChunk content.", e, null, null /*data*/);
- return null;
- } catch (ClassNotFoundException e) {
- Bridge.getLog().error(ILayoutLog.TAG_BROKEN,
- "Failed to deserialize NinePatchChunk class.", e, null, null /*data*/);
- return null;
- }
- }
-
- return chunk;
- }
-
- // ---- native methods ----
-
- @LayoutlibDelegate
- /*package*/ static boolean isNinePatchChunk(byte[] chunk) {
- NinePatchChunk chunkObject = getChunk(chunk);
- return chunkObject != null;
- }
-
- @LayoutlibDelegate
- /*package*/ static long validateNinePatchChunk(byte[] chunk) {
- // the default JNI implementation only checks that the byte[] has the same
- // size as the C struct it represent. Since we cannot do the same check (serialization
- // will return different size depending on content), we do nothing.
- NinePatch_Delegate newDelegate = new NinePatch_Delegate();
- newDelegate.chunk = chunk;
- return sManager.addNewDelegate(newDelegate);
- }
-
- @LayoutlibDelegate
- /*package*/ static void nativeFinalize(long nativeNinePatch) {
- NinePatch_Delegate delegate = sManager.getDelegate(nativeNinePatch);
- if (delegate != null && delegate.chunk != null) {
- sChunkCache.remove(delegate.chunk);
- }
- sManager.removeJavaReferenceFor(nativeNinePatch);
- }
-
-
- @LayoutlibDelegate
- /*package*/ static long nativeGetTransparentRegion(long bitmapHandle, long chunk,
- Rect location) {
- return 0;
- }
-
- static byte[] getChunk(long nativeNinePatch) {
- NinePatch_Delegate delegate = sManager.getDelegate(nativeNinePatch);
- if (delegate != null) {
- return delegate.chunk;
- }
- return null;
- }
-
- public static void clearCache() {
- sChunkCache.clear();
- }
-}
diff --git a/bridge/src/android/graphics/PaintFlagsDrawFilter_Delegate.java b/bridge/src/android/graphics/PaintFlagsDrawFilter_Delegate.java
deleted file mode 100644
index fa20746e44..0000000000
--- a/bridge/src/android/graphics/PaintFlagsDrawFilter_Delegate.java
+++ /dev/null
@@ -1,64 +0,0 @@
-/*
- * Copyright (C) 2010 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package android.graphics;
-
-import com.android.layoutlib.bridge.impl.DelegateManager;
-import com.android.tools.layoutlib.annotations.LayoutlibDelegate;
-
-/**
- * Delegate implementing the native methods of android.graphics.PaintFlagsDrawFilter
- *
- * Through the layoutlib_create tool, the original native methods of PaintFlagsDrawFilter have been
- * replaced by calls to methods of the same name in this delegate class.
- *
- * This class behaves like the original native implementation, but in Java, keeping previously
- * native data into its own objects and mapping them to int that are sent back and forth between
- * it and the original PaintFlagsDrawFilter class.
- *
- * Because this extends {@link DrawFilter_Delegate}, there's no need to use a
- * {@link DelegateManager}, as all the DrawFilter classes will be added to the manager owned by
- * {@link DrawFilter_Delegate}.
- *
- * @see DrawFilter_Delegate
- *
- */
-public class PaintFlagsDrawFilter_Delegate extends DrawFilter_Delegate {
-
- // ---- delegate data ----
-
- // ---- Public Helper methods ----
-
- @Override
- public boolean isSupported() {
- return false;
- }
-
- @Override
- public String getSupportMessage() {
- return "Paint Flags Draw Filters are not supported.";
- }
-
- // ---- native methods ----
-
- @LayoutlibDelegate
- /*package*/ static long nativeConstructor(int clearBits, int setBits) {
- PaintFlagsDrawFilter_Delegate newDelegate = new PaintFlagsDrawFilter_Delegate();
- return sManager.addNewDelegate(newDelegate);
- }
-
- // ---- Private delegate/helper methods ----
-}
diff --git a/bridge/src/android/graphics/Paint_Delegate.java b/bridge/src/android/graphics/Paint_Delegate.java
deleted file mode 100644
index f6c6d71a92..0000000000
--- a/bridge/src/android/graphics/Paint_Delegate.java
+++ /dev/null
@@ -1,1373 +0,0 @@
-/*
- * Copyright (C) 2010 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package android.graphics;
-
-import com.android.ide.common.rendering.api.ILayoutLog;
-import com.android.layoutlib.bridge.Bridge;
-import com.android.layoutlib.bridge.impl.DelegateManager;
-import com.android.tools.layoutlib.annotations.LayoutlibDelegate;
-
-import android.annotation.NonNull;
-import android.annotation.Nullable;
-import android.graphics.FontFamily_Delegate.FontVariant;
-import android.graphics.Paint.FontMetrics;
-import android.graphics.Paint.FontMetricsInt;
-import android.text.TextUtils;
-
-import java.awt.BasicStroke;
-import java.awt.Font;
-import java.awt.Shape;
-import java.awt.Stroke;
-import java.awt.Toolkit;
-import java.awt.geom.AffineTransform;
-import java.util.Collections;
-import java.util.List;
-import java.util.Locale;
-import java.util.Objects;
-import java.util.stream.Collectors;
-import java.util.stream.StreamSupport;
-
-import libcore.util.NativeAllocationRegistry_Delegate;
-
-/**
- * Delegate implementing the native methods of android.graphics.Paint
- *
- * Through the layoutlib_create tool, the original native methods of Paint have been replaced
- * by calls to methods of the same name in this delegate class.
- *
- * This class behaves like the original native implementation, but in Java, keeping previously
- * native data into its own objects and mapping them to int that are sent back and forth between
- * it and the original Paint class.
- *
- * @see DelegateManager
- *
- */
-public class Paint_Delegate {
- private static final float DEFAULT_TEXT_SIZE = 20.f;
- private static final float DEFAULT_TEXT_SCALE_X = 1.f;
- private static final float DEFAULT_TEXT_SKEW_X = 0.f;
-
- /**
- * Class associating a {@link Font} and its {@link java.awt.FontMetrics}.
- */
- /*package*/ static final class FontInfo {
- final Font mFont;
- final java.awt.FontMetrics mMetrics;
-
- FontInfo(@NonNull Font font, @NonNull java.awt.FontMetrics fontMetrics) {
- this.mFont = font;
- this.mMetrics = fontMetrics;
- }
- }
-
- // ---- delegate manager ----
- private static final DelegateManager<Paint_Delegate> sManager =
- new DelegateManager<>(Paint_Delegate.class);
- private static long sFinalizer = -1;
-
- // ---- delegate helper data ----
-
- // This list can contain null elements.
- @Nullable
- private List<FontInfo> mFonts;
-
- // ---- delegate data ----
- private int mFlags;
- private int mColor;
- private int mStyle;
- private int mCap;
- private int mJoin;
- private int mTextAlign;
- private Typeface_Delegate mTypeface;
- private float mStrokeWidth;
- private float mStrokeMiter;
- private float mTextSize;
- private float mTextScaleX;
- private float mTextSkewX;
- private int mHintingMode = Paint.HINTING_ON;
- private int mStartHyphenEdit;
- private int mEndHyphenEdit;
- private float mLetterSpacing; // not used in actual text rendering.
- private float mWordSpacing; // not used in actual text rendering.
- // Variant of the font. A paint's variant can only be compact or elegant.
- private FontVariant mFontVariant = FontVariant.COMPACT;
-
- private int mPorterDuffMode = Xfermode.DEFAULT;
- private ColorFilter_Delegate mColorFilter;
- private Shader_Delegate mShader;
- private PathEffect_Delegate mPathEffect;
- private MaskFilter_Delegate mMaskFilter;
-
- @SuppressWarnings("FieldCanBeLocal") // Used to store the locale for future use
- private Locale mLocale = Locale.getDefault();
-
- // ---- Public Helper methods ----
-
- @Nullable
- public static Paint_Delegate getDelegate(long native_paint) {
- return sManager.getDelegate(native_paint);
- }
-
- /**
- * Returns the list of {@link Font} objects.
- */
- @NonNull
- public List<FontInfo> getFonts() {
- Typeface_Delegate typeface = mTypeface;
- if (typeface == null) {
- if (Typeface.sDefaultTypeface == null) {
- return Collections.emptyList();
- }
-
- typeface = Typeface_Delegate.getDelegate(Typeface.sDefaultTypeface.native_instance);
- }
-
- if (mFonts != null) {
- return mFonts;
- }
-
- // Apply an optional transformation for skew and scale
- AffineTransform affineTransform = mTextScaleX != 1.0 || mTextSkewX != 0 ?
- new AffineTransform(mTextScaleX, mTextSkewX, 0, 1, 0, 0) :
- null;
-
- List<FontInfo> infoList = StreamSupport.stream(typeface.getFonts(mFontVariant).spliterator
- (), false)
- .filter(Objects::nonNull)
- .map(font -> getFontInfo(font, mTextSize, affineTransform))
- .collect(Collectors.toList());
- mFonts = Collections.unmodifiableList(infoList);
-
- return mFonts;
- }
-
- public boolean isAntiAliased() {
- return (mFlags & Paint.ANTI_ALIAS_FLAG) != 0;
- }
-
- public boolean isFilterBitmap() {
- return (mFlags & Paint.FILTER_BITMAP_FLAG) != 0;
- }
-
- public int getStyle() {
- return mStyle;
- }
-
- public int getColor() {
- return mColor;
- }
-
- public int getAlpha() {
- return mColor >>> 24;
- }
-
- public void setAlpha(int alpha) {
- mColor = (alpha << 24) | (mColor & 0x00FFFFFF);
- }
-
- public int getTextAlign() {
- return mTextAlign;
- }
-
- public float getStrokeWidth() {
- return mStrokeWidth;
- }
-
- /**
- * returns the value of stroke miter needed by the java api.
- */
- public float getJavaStrokeMiter() {
- return mStrokeMiter;
- }
-
- public int getJavaCap() {
- switch (Paint.sCapArray[mCap]) {
- case BUTT:
- return BasicStroke.CAP_BUTT;
- case ROUND:
- return BasicStroke.CAP_ROUND;
- default:
- case SQUARE:
- return BasicStroke.CAP_SQUARE;
- }
- }
-
- public int getJavaJoin() {
- switch (Paint.sJoinArray[mJoin]) {
- default:
- case MITER:
- return BasicStroke.JOIN_MITER;
- case ROUND:
- return BasicStroke.JOIN_ROUND;
- case BEVEL:
- return BasicStroke.JOIN_BEVEL;
- }
- }
-
- public Stroke getJavaStroke() {
- if (mPathEffect != null) {
- if (mPathEffect.isSupported()) {
- Stroke stroke = mPathEffect.getStroke(this);
- assert stroke != null;
- //noinspection ConstantConditions
- if (stroke != null) {
- return stroke;
- }
- } else {
- Bridge.getLog().fidelityWarning(ILayoutLog.TAG_PATHEFFECT,
- mPathEffect.getSupportMessage(),
- null, null, null /*data*/);
- }
- }
-
- // if no custom stroke as been set, set the default one.
- return new BasicStroke(
- getStrokeWidth(),
- getJavaCap(),
- getJavaJoin(),
- getJavaStrokeMiter());
- }
-
- /**
- * Returns the {@link PorterDuff.Mode} as an int
- */
- public int getPorterDuffMode() {
- return mPorterDuffMode;
- }
-
- /**
- * Returns the {@link ColorFilter} delegate or null if none have been set
- *
- * @return the delegate or null.
- */
- public ColorFilter_Delegate getColorFilter() {
- return mColorFilter;
- }
-
- public void setColorFilter(long colorFilterPtr) {
- mColorFilter = ColorFilter_Delegate.getDelegate(colorFilterPtr);
- }
-
- public void setShader(long shaderPtr) {
- mShader = Shader_Delegate.getDelegate(shaderPtr);
- }
-
- /**
- * Returns the {@link Shader} delegate or null if none have been set
- *
- * @return the delegate or null.
- */
- public Shader_Delegate getShader() {
- return mShader;
- }
-
- /**
- * Returns the {@link MaskFilter} delegate or null if none have been set
- *
- * @return the delegate or null.
- */
- public MaskFilter_Delegate getMaskFilter() {
- return mMaskFilter;
- }
-
- // ---- native methods ----
-
- @LayoutlibDelegate
- /*package*/ static int nGetFlags(long nativePaint) {
- // get the delegate from the native int.
- Paint_Delegate delegate = sManager.getDelegate(nativePaint);
- if (delegate == null) {
- return 0;
- }
-
- return delegate.mFlags;
- }
-
-
-
- @LayoutlibDelegate
- /*package*/ static void nSetFlags(long nativePaint, int flags) {
- // get the delegate from the native int.
- Paint_Delegate delegate = sManager.getDelegate(nativePaint);
- if (delegate == null) {
- return;
- }
-
- delegate.mFlags = flags;
- }
-
- @LayoutlibDelegate
- /*package*/ static void nSetFilterBitmap(long nativePaint, boolean filter) {
- setFlag(nativePaint, Paint.FILTER_BITMAP_FLAG, filter);
- }
-
- @LayoutlibDelegate
- /*package*/ static int nGetHinting(long nativePaint) {
- // get the delegate from the native int.
- Paint_Delegate delegate = sManager.getDelegate(nativePaint);
- if (delegate == null) {
- return Paint.HINTING_ON;
- }
-
- return delegate.mHintingMode;
- }
-
- @LayoutlibDelegate
- /*package*/ static void nSetHinting(long nativePaint, int mode) {
- // get the delegate from the native int.
- Paint_Delegate delegate = sManager.getDelegate(nativePaint);
- if (delegate == null) {
- return;
- }
-
- delegate.mHintingMode = mode;
- }
-
- @LayoutlibDelegate
- /*package*/ static void nSetAntiAlias(long nativePaint, boolean aa) {
- setFlag(nativePaint, Paint.ANTI_ALIAS_FLAG, aa);
- }
-
- @LayoutlibDelegate
- /*package*/ static void nSetSubpixelText(long nativePaint,
- boolean subpixelText) {
- setFlag(nativePaint, Paint.SUBPIXEL_TEXT_FLAG, subpixelText);
- }
-
- @LayoutlibDelegate
- /*package*/ static void nSetUnderlineText(long nativePaint,
- boolean underlineText) {
- setFlag(nativePaint, Paint.UNDERLINE_TEXT_FLAG, underlineText);
- }
-
- @LayoutlibDelegate
- /*package*/ static void nSetStrikeThruText(long nativePaint,
- boolean strikeThruText) {
- setFlag(nativePaint, Paint.STRIKE_THRU_TEXT_FLAG, strikeThruText);
- }
-
- @LayoutlibDelegate
- /*package*/ static void nSetFakeBoldText(long nativePaint,
- boolean fakeBoldText) {
- setFlag(nativePaint, Paint.FAKE_BOLD_TEXT_FLAG, fakeBoldText);
- }
-
- @LayoutlibDelegate
- /*package*/ static void nSetDither(long nativePaint, boolean dither) {
- setFlag(nativePaint, Paint.DITHER_FLAG, dither);
- }
-
- @LayoutlibDelegate
- /*package*/ static void nSetLinearText(long nativePaint, boolean linearText) {
- setFlag(nativePaint, Paint.LINEAR_TEXT_FLAG, linearText);
- }
-
- @LayoutlibDelegate
- /*package*/ static void nSetColor(long paintPtr, long colorSpaceHandle, long color) {
- Paint_Delegate delegate = sManager.getDelegate(paintPtr);
- if (delegate == null) {
- return;
- }
-
- delegate.mColor = Color.toArgb(color);
- }
-
- @LayoutlibDelegate
- /*package*/ static void nSetColor(long paintPtr, int color) {
- Paint_Delegate delegate = sManager.getDelegate(paintPtr);
- if (delegate == null) {
- return;
- }
-
- delegate.mColor = color;
- }
-
- @LayoutlibDelegate
- /*package*/ static void nSetAlpha(long nativePaint, int a) {
- // get the delegate from the native int.
- Paint_Delegate delegate = sManager.getDelegate(nativePaint);
- if (delegate == null) {
- return;
- }
-
- delegate.setAlpha(a);
- }
-
- @LayoutlibDelegate
- /*package*/ static float nGetStrokeWidth(long nativePaint) {
- // get the delegate from the native int.
- Paint_Delegate delegate = sManager.getDelegate(nativePaint);
- if (delegate == null) {
- return 1.f;
- }
-
- return delegate.mStrokeWidth;
- }
-
- @LayoutlibDelegate
- /*package*/ static void nSetStrokeWidth(long nativePaint, float width) {
- // get the delegate from the native int.
- Paint_Delegate delegate = sManager.getDelegate(nativePaint);
- if (delegate == null) {
- return;
- }
-
- delegate.mStrokeWidth = width;
- }
-
- @LayoutlibDelegate
- /*package*/ static float nGetStrokeMiter(long nativePaint) {
- // get the delegate from the native int.
- Paint_Delegate delegate = sManager.getDelegate(nativePaint);
- if (delegate == null) {
- return 1.f;
- }
-
- return delegate.mStrokeMiter;
- }
-
- @LayoutlibDelegate
- /*package*/ static void nSetStrokeMiter(long nativePaint, float miter) {
- // get the delegate from the native int.
- Paint_Delegate delegate = sManager.getDelegate(nativePaint);
- if (delegate == null) {
- return;
- }
-
- delegate.mStrokeMiter = miter;
- }
-
- @LayoutlibDelegate
- /*package*/ static void nSetShadowLayer(long paintPtr,
- float radius, float dx, float dy, long colorSpaceHandle,
- long shadowColor) {
- Bridge.getLog().fidelityWarning(ILayoutLog.TAG_UNSUPPORTED,
- "Paint.setShadowLayer is not supported.", null, null, null /*data*/);
- }
-
- @LayoutlibDelegate
- /*package*/ static boolean nHasShadowLayer(long paint) {
- // FIXME
- Bridge.getLog().fidelityWarning(ILayoutLog.TAG_UNSUPPORTED,
- "Paint.hasShadowLayer is not supported.", null, null, null /*data*/);
- return false;
- }
-
- @LayoutlibDelegate
- /*package*/ static boolean nIsElegantTextHeight(long nativePaint) {
- // get the delegate from the native int.
- Paint_Delegate delegate = sManager.getDelegate(nativePaint);
- return delegate != null && delegate.mFontVariant == FontVariant.ELEGANT;
- }
-
- @LayoutlibDelegate
- /*package*/ static void nSetElegantTextHeight(long nativePaint,
- boolean elegant) {
- // get the delegate from the native int.
- Paint_Delegate delegate = sManager.getDelegate(nativePaint);
- if (delegate == null) {
- return;
- }
-
- delegate.mFontVariant = elegant ? FontVariant.ELEGANT : FontVariant.COMPACT;
- }
-
- @LayoutlibDelegate
- /*package*/ static float nGetTextSize(long nativePaint) {
- // get the delegate from the native int.
- Paint_Delegate delegate = sManager.getDelegate(nativePaint);
- if (delegate == null) {
- return 1.f;
- }
-
- return delegate.mTextSize;
- }
-
- @LayoutlibDelegate
- /*package*/ static void nSetTextSize(long nativePaint, float textSize) {
- // get the delegate from the native int.
- Paint_Delegate delegate = sManager.getDelegate(nativePaint);
- if (delegate == null) {
- return;
- }
-
- if (delegate.mTextSize != textSize) {
- delegate.mTextSize = textSize;
- delegate.invalidateFonts();
- }
- }
-
- @LayoutlibDelegate
- /*package*/ static float nGetTextScaleX(long nativePaint) {
- // get the delegate from the native int.
- Paint_Delegate delegate = sManager.getDelegate(nativePaint);
- if (delegate == null) {
- return 1.f;
- }
-
- return delegate.mTextScaleX;
- }
-
- @LayoutlibDelegate
- /*package*/ static void nSetTextScaleX(long nativePaint, float scaleX) {
- // get the delegate from the native int.
- Paint_Delegate delegate = sManager.getDelegate(nativePaint);
- if (delegate == null) {
- return;
- }
-
- if (delegate.mTextScaleX != scaleX) {
- delegate.mTextScaleX = scaleX;
- delegate.invalidateFonts();
- }
- }
-
- @LayoutlibDelegate
- /*package*/ static float nGetTextSkewX(long nativePaint) {
- // get the delegate from the native int.
- Paint_Delegate delegate = sManager.getDelegate(nativePaint);
- if (delegate == null) {
- return 1.f;
- }
-
- return delegate.mTextSkewX;
- }
-
- @LayoutlibDelegate
- /*package*/ static void nSetTextSkewX(long nativePaint, float skewX) {
- // get the delegate from the native int.
- Paint_Delegate delegate = sManager.getDelegate(nativePaint);
- if (delegate == null) {
- return;
- }
-
- if (delegate.mTextSkewX != skewX) {
- delegate.mTextSkewX = skewX;
- delegate.invalidateFonts();
- }
- }
-
- @LayoutlibDelegate
- /*package*/ static float nAscent(long nativePaint) {
- // get the delegate
- Paint_Delegate delegate = sManager.getDelegate(nativePaint);
- if (delegate == null) {
- return 0;
- }
-
- List<FontInfo> fonts = delegate.getFonts();
- if (fonts.size() > 0) {
- java.awt.FontMetrics javaMetrics = fonts.get(0).mMetrics;
- // Android expects negative ascent so we invert the value from Java.
- return - javaMetrics.getAscent();
- }
-
- return 0;
- }
-
- @LayoutlibDelegate
- /*package*/ static float nDescent(long nativePaint) {
- // get the delegate
- Paint_Delegate delegate = sManager.getDelegate(nativePaint);
- if (delegate == null) {
- return 0;
- }
-
- List<FontInfo> fonts = delegate.getFonts();
- if (fonts.size() > 0) {
- java.awt.FontMetrics javaMetrics = fonts.get(0).mMetrics;
- return javaMetrics.getDescent();
- }
-
- return 0;
-
- }
-
- @LayoutlibDelegate
- /*package*/ static float nGetFontMetrics(long nativePaint,
- FontMetrics metrics) {
- // get the delegate
- Paint_Delegate delegate = sManager.getDelegate(nativePaint);
- if (delegate == null) {
- return 0;
- }
-
- return delegate.getFontMetrics(metrics);
- }
-
- @LayoutlibDelegate
- /*package*/ static int nGetFontMetricsInt(long nativePaint, FontMetricsInt fmi) {
- // get the delegate
- Paint_Delegate delegate = sManager.getDelegate(nativePaint);
- if (delegate == null) {
- return 0;
- }
-
- List<FontInfo> fonts = delegate.getFonts();
- if (fonts.size() > 0) {
- java.awt.FontMetrics javaMetrics = fonts.get(0).mMetrics;
- if (fmi != null) {
- // Android expects negative ascent so we invert the value from Java.
- fmi.top = (int)(- javaMetrics.getMaxAscent() * 1.15);
- fmi.ascent = - javaMetrics.getAscent();
- fmi.descent = javaMetrics.getDescent();
- fmi.bottom = (int)(javaMetrics.getMaxDescent() * 1.15);
- fmi.leading = javaMetrics.getLeading();
- }
-
- return javaMetrics.getHeight();
- }
-
- return 0;
- }
-
- @LayoutlibDelegate
- /*package*/ static int nBreakText(long nativePaint, char[] text,
- int index, int count, float maxWidth, int bidiFlags, float[] measuredWidth) {
-
- // get the delegate
- Paint_Delegate delegate = sManager.getDelegate(nativePaint);
- if (delegate == null) {
- return 0;
- }
-
- int inc = count > 0 ? 1 : -1;
-
- int measureIndex = 0;
- for (int i = index; i != index + count; i += inc, measureIndex++) {
- int start, end;
- if (i < index) {
- start = i;
- end = index;
- } else {
- start = index;
- end = i;
- }
-
- // measure from start to end
- RectF bounds = delegate.measureText(text, start, end - start + 1, null, 0, bidiFlags);
- float res = bounds.right - bounds.left;
-
- if (measuredWidth != null) {
- measuredWidth[measureIndex] = res;
- }
-
- if (res > maxWidth) {
- // we should not return this char index, but since it's 0-based
- // and we need to return a count, we simply return measureIndex;
- return measureIndex;
- }
-
- }
-
- return measureIndex;
- }
-
- @LayoutlibDelegate
- /*package*/ static int nBreakText(long nativePaint, String text, boolean measureForwards,
- float maxWidth, int bidiFlags, float[] measuredWidth) {
- return nBreakText(nativePaint, text.toCharArray(), 0, text.length(),
- maxWidth, bidiFlags, measuredWidth);
- }
-
- @LayoutlibDelegate
- /*package*/ static long nInit() {
- Paint_Delegate newDelegate = new Paint_Delegate();
- return sManager.addNewDelegate(newDelegate);
- }
-
- @LayoutlibDelegate
- /*package*/ static long nInitWithPaint(long paint) {
- // get the delegate from the native int.
- Paint_Delegate delegate = sManager.getDelegate(paint);
- if (delegate == null) {
- return 0;
- }
-
- Paint_Delegate newDelegate = new Paint_Delegate(delegate);
- return sManager.addNewDelegate(newDelegate);
- }
-
- @LayoutlibDelegate
- /*package*/ static void nReset(long native_object) {
- // get the delegate from the native int.
- Paint_Delegate delegate = sManager.getDelegate(native_object);
- if (delegate == null) {
- return;
- }
-
- delegate.reset();
- }
-
- @LayoutlibDelegate
- /*package*/ static void nSet(long native_dst, long native_src) {
- // get the delegate from the native int.
- Paint_Delegate delegate_dst = sManager.getDelegate(native_dst);
- if (delegate_dst == null) {
- return;
- }
-
- // get the delegate from the native int.
- Paint_Delegate delegate_src = sManager.getDelegate(native_src);
- if (delegate_src == null) {
- return;
- }
-
- delegate_dst.set(delegate_src);
- }
-
- @LayoutlibDelegate
- /*package*/ static int nGetStyle(long native_object) {
- // get the delegate from the native int.
- Paint_Delegate delegate = sManager.getDelegate(native_object);
- if (delegate == null) {
- return 0;
- }
-
- return delegate.mStyle;
- }
-
- @LayoutlibDelegate
- /*package*/ static void nSetStyle(long native_object, int style) {
- // get the delegate from the native int.
- Paint_Delegate delegate = sManager.getDelegate(native_object);
- if (delegate == null) {
- return;
- }
-
- delegate.mStyle = style;
- }
-
- @LayoutlibDelegate
- /*package*/ static int nGetStrokeCap(long native_object) {
- // get the delegate from the native int.
- Paint_Delegate delegate = sManager.getDelegate(native_object);
- if (delegate == null) {
- return 0;
- }
-
- return delegate.mCap;
- }
-
- @LayoutlibDelegate
- /*package*/ static void nSetStrokeCap(long native_object, int cap) {
- // get the delegate from the native int.
- Paint_Delegate delegate = sManager.getDelegate(native_object);
- if (delegate == null) {
- return;
- }
-
- delegate.mCap = cap;
- }
-
- @LayoutlibDelegate
- /*package*/ static int nGetStrokeJoin(long native_object) {
- // get the delegate from the native int.
- Paint_Delegate delegate = sManager.getDelegate(native_object);
- if (delegate == null) {
- return 0;
- }
-
- return delegate.mJoin;
- }
-
- @LayoutlibDelegate
- /*package*/ static void nSetStrokeJoin(long native_object, int join) {
- // get the delegate from the native int.
- Paint_Delegate delegate = sManager.getDelegate(native_object);
- if (delegate == null) {
- return;
- }
-
- delegate.mJoin = join;
- }
-
- @LayoutlibDelegate
- /*package*/ static boolean nGetFillPath(long native_object, long src, long dst) {
- Paint_Delegate paint = sManager.getDelegate(native_object);
- if (paint == null) {
- return false;
- }
-
- Path_Delegate srcPath = Path_Delegate.getDelegate(src);
- if (srcPath == null) {
- return true;
- }
-
- Path_Delegate dstPath = Path_Delegate.getDelegate(dst);
- if (dstPath == null) {
- return true;
- }
-
- Stroke stroke = paint.getJavaStroke();
- Shape strokeShape = stroke.createStrokedShape(srcPath.getJavaShape());
-
- dstPath.setJavaShape(strokeShape);
-
- // FIXME figure out the return value?
- return true;
- }
-
- @LayoutlibDelegate
- /*package*/ static long nSetShader(long native_object, long shader) {
- // get the delegate from the native int.
- Paint_Delegate delegate = sManager.getDelegate(native_object);
- if (delegate == null) {
- return shader;
- }
-
- delegate.mShader = Shader_Delegate.getDelegate(shader);
-
- return shader;
- }
-
- @LayoutlibDelegate
- /*package*/ static long nSetColorFilter(long native_object, long filter) {
- // get the delegate from the native int.
- Paint_Delegate delegate = sManager.getDelegate(native_object);
- if (delegate == null) {
- return filter;
- }
-
- delegate.mColorFilter = ColorFilter_Delegate.getDelegate(filter);
-
- // Log warning if it's not supported.
- if (delegate.mColorFilter != null && !delegate.mColorFilter.isSupported()) {
- Bridge.getLog().fidelityWarning(ILayoutLog.TAG_COLORFILTER,
- delegate.mColorFilter.getSupportMessage(), null, null, null /*data*/);
- }
-
- return filter;
- }
-
- @LayoutlibDelegate
- /*package*/ static void nSetXfermode(long native_object, int xfermode) {
- Paint_Delegate delegate = sManager.getDelegate(native_object);
- if (delegate == null) {
- return;
- }
- delegate.mPorterDuffMode = xfermode;
- }
-
- @LayoutlibDelegate
- /*package*/ static long nSetPathEffect(long native_object, long effect) {
- // get the delegate from the native int.
- Paint_Delegate delegate = sManager.getDelegate(native_object);
- if (delegate == null) {
- return effect;
- }
-
- delegate.mPathEffect = PathEffect_Delegate.getDelegate(effect);
-
- return effect;
- }
-
- @LayoutlibDelegate
- /*package*/ static long nSetMaskFilter(long native_object, long maskfilter) {
- // get the delegate from the native int.
- Paint_Delegate delegate = sManager.getDelegate(native_object);
- if (delegate == null) {
- return maskfilter;
- }
-
- delegate.mMaskFilter = MaskFilter_Delegate.getDelegate(maskfilter);
-
- // since none of those are supported, display a fidelity warning right away
- if (delegate.mMaskFilter != null && !delegate.mMaskFilter.isSupported()) {
- Bridge.getLog().fidelityWarning(ILayoutLog.TAG_MASKFILTER,
- delegate.mMaskFilter.getSupportMessage(), null, null, null /*data*/);
- }
-
- return maskfilter;
- }
-
- @LayoutlibDelegate
- /*package*/ static void nSetTypeface(long native_object, long typeface) {
- // get the delegate from the native int.
- Paint_Delegate delegate = sManager.getDelegate(native_object);
- if (delegate == null) {
- return;
- }
-
- Typeface_Delegate typefaceDelegate = Typeface_Delegate.getDelegate(typeface);
- if (delegate.mTypeface != typefaceDelegate) {
- delegate.mTypeface = typefaceDelegate;
- delegate.invalidateFonts();
- }
- }
-
- @LayoutlibDelegate
- /*package*/ static int nGetTextAlign(long native_object) {
- // get the delegate from the native int.
- Paint_Delegate delegate = sManager.getDelegate(native_object);
- if (delegate == null) {
- return 0;
- }
-
- return delegate.mTextAlign;
- }
-
- @LayoutlibDelegate
- /*package*/ static void nSetTextAlign(long native_object, int align) {
- // get the delegate from the native int.
- Paint_Delegate delegate = sManager.getDelegate(native_object);
- if (delegate == null) {
- return;
- }
-
- delegate.mTextAlign = align;
- }
-
- @LayoutlibDelegate
- /*package*/ static int nSetTextLocales(long native_object, String locale) {
- // get the delegate from the native int.
- Paint_Delegate delegate = sManager.getDelegate(native_object);
- if (delegate == null) {
- return 0;
- }
-
- delegate.setTextLocale(locale);
- return 0;
- }
-
- @LayoutlibDelegate
- /*package*/ static void nSetTextLocalesByMinikinLocaleListId(long paintPtr,
- int mMinikinLangListId) {
- // FIXME
- }
-
- @LayoutlibDelegate
- /*package*/ static float nGetTextAdvances(long native_object, char[] text, int index,
- int count, int contextIndex, int contextCount,
- int bidiFlags, float[] advances, int advancesIndex) {
-
- if (advances != null)
- for (int i = advancesIndex; i< advancesIndex+count; i++)
- advances[i]=0;
- // get the delegate from the native int.
- Paint_Delegate delegate = sManager.getDelegate(native_object);
- if (delegate == null) {
- return 0.f;
- }
-
- RectF bounds = delegate.measureText(text, index, count, advances, advancesIndex, bidiFlags);
- return bounds.right - bounds.left;
- }
-
- @LayoutlibDelegate
- /*package*/ static float nGetTextAdvances(long native_object, String text, int start, int end,
- int contextStart, int contextEnd, int bidiFlags, float[] advances, int advancesIndex) {
- // FIXME: support contextStart and contextEnd
- int count = end - start;
- char[] buffer = TemporaryBuffer.obtain(count);
- TextUtils.getChars(text, start, end, buffer, 0);
-
- return nGetTextAdvances(native_object, buffer, 0, count,
- contextStart, contextEnd - contextStart, bidiFlags, advances, advancesIndex);
- }
-
- @LayoutlibDelegate
- /*package*/ static int nGetTextRunCursor(Paint paint, long native_object, char[] text,
- int contextStart, int contextLength, int flags, int offset, int cursorOpt) {
- // FIXME
- Bridge.getLog().fidelityWarning(ILayoutLog.TAG_UNSUPPORTED,
- "Paint.getTextRunCursor is not supported.", null, null, null /*data*/);
- return 0;
- }
-
- @LayoutlibDelegate
- /*package*/ static int nGetTextRunCursor(Paint paint, long native_object, String text,
- int contextStart, int contextEnd, int flags, int offset, int cursorOpt) {
- // FIXME
- Bridge.getLog().fidelityWarning(ILayoutLog.TAG_UNSUPPORTED,
- "Paint.getTextRunCursor is not supported.", null, null, null /*data*/);
- return 0;
- }
-
- @LayoutlibDelegate
- /*package*/ static void nGetTextPath(long native_object, int bidiFlags, char[] text,
- int index, int count, float x, float y, long path) {
- // FIXME
- Bridge.getLog().fidelityWarning(ILayoutLog.TAG_UNSUPPORTED,
- "Paint.getTextPath is not supported.", null, null, null /*data*/);
- }
-
- @LayoutlibDelegate
- /*package*/ static void nGetTextPath(long native_object, int bidiFlags, String text, int start,
- int end, float x, float y, long path) {
- // FIXME
- Bridge.getLog().fidelityWarning(ILayoutLog.TAG_UNSUPPORTED,
- "Paint.getTextPath is not supported.", null, null, null /*data*/);
- }
-
- @LayoutlibDelegate
- /*package*/ static void nGetStringBounds(long nativePaint, String text, int start, int end,
- int bidiFlags, Rect bounds) {
- nGetCharArrayBounds(nativePaint, text.toCharArray(), start,
- end - start, bidiFlags, bounds);
- }
-
- @LayoutlibDelegate
- public static void nGetCharArrayBounds(long nativePaint, char[] text, int index,
- int count, int bidiFlags, Rect bounds) {
-
- // get the delegate from the native int.
- Paint_Delegate delegate = sManager.getDelegate(nativePaint);
- if (delegate == null) {
- return;
- }
-
- delegate.measureText(text, index, count, null, 0, bidiFlags).roundOut(bounds);
- }
-
- @LayoutlibDelegate
- /*package*/ static long nGetNativeFinalizer() {
- synchronized (Paint_Delegate.class) {
- if (sFinalizer == -1) {
- sFinalizer = NativeAllocationRegistry_Delegate.createFinalizer(
- sManager::removeJavaReferenceFor);
- }
- }
- return sFinalizer;
- }
-
- @LayoutlibDelegate
- /*package*/ static float nGetLetterSpacing(long nativePaint) {
- Paint_Delegate delegate = sManager.getDelegate(nativePaint);
- if (delegate == null) {
- return 0;
- }
- return delegate.mLetterSpacing;
- }
-
- @LayoutlibDelegate
- /*package*/ static void nSetLetterSpacing(long nativePaint, float letterSpacing) {
- Bridge.getLog().fidelityWarning(ILayoutLog.TAG_TEXT_RENDERING,
- "Paint.setLetterSpacing() not supported.", null, null, null);
- Paint_Delegate delegate = sManager.getDelegate(nativePaint);
- if (delegate == null) {
- return;
- }
- delegate.mLetterSpacing = letterSpacing;
- }
-
- @LayoutlibDelegate
- /*package*/ static float nGetWordSpacing(long nativePaint) {
- Paint_Delegate delegate = sManager.getDelegate(nativePaint);
- if (delegate == null) {
- return 0;
- }
- return delegate.mWordSpacing;
- }
-
- @LayoutlibDelegate
- /*package*/ static void nSetWordSpacing(long nativePaint, float wordSpacing) {
- Paint_Delegate delegate = sManager.getDelegate(nativePaint);
- if (delegate == null) {
- return;
- }
- delegate.mWordSpacing = wordSpacing;
- }
-
- @LayoutlibDelegate
- /*package*/ static void nSetFontFeatureSettings(long nativePaint, String settings) {
- Bridge.getLog().fidelityWarning(ILayoutLog.TAG_TEXT_RENDERING,
- "Paint.setFontFeatureSettings() not supported.", null, null, null);
- }
-
- @LayoutlibDelegate
- /*package*/ static int nGetStartHyphenEdit(long nativePaint) {
- Paint_Delegate delegate = sManager.getDelegate(nativePaint);
- if (delegate == null) {
- return 0;
- }
- return delegate.mStartHyphenEdit;
- }
-
- @LayoutlibDelegate
- /*package*/ static void nSetStartHyphenEdit(long nativePaint, int hyphen) {
- Paint_Delegate delegate = sManager.getDelegate(nativePaint);
- if (delegate == null) {
- return;
- }
- delegate.mStartHyphenEdit = hyphen;
- }
-
- @LayoutlibDelegate
- /*package*/ static int nGetEndHyphenEdit(long nativePaint) {
- Paint_Delegate delegate = sManager.getDelegate(nativePaint);
- if (delegate == null) {
- return 0;
- }
- return delegate.mEndHyphenEdit;
- }
-
- @LayoutlibDelegate
- /*package*/ static void nSetEndHyphenEdit(long nativePaint, int hyphen) {
- Paint_Delegate delegate = sManager.getDelegate(nativePaint);
- if (delegate == null) {
- return;
- }
- delegate.mEndHyphenEdit = hyphen;
- }
-
- @LayoutlibDelegate
- /*package*/ static boolean nHasGlyph(long nativePaint, int bidiFlags, String string) {
- Paint_Delegate delegate = sManager.getDelegate(nativePaint);
- if (delegate == null) {
- return false;
- }
- if (string.length() == 0) {
- return false;
- }
- if (string.length() > 1) {
- Bridge.getLog().fidelityWarning(ILayoutLog.TAG_TEXT_RENDERING,
- "Paint.hasGlyph() is not supported for ligatures.", null, null, null);
- return false;
- }
-
- char c = string.charAt(0);
- for (Font font : delegate.mTypeface.getFonts(delegate.mFontVariant)) {
- if (font.canDisplay(c)) {
- return true;
- }
- }
- return false;
- }
-
-
- @LayoutlibDelegate
- /*package*/ static float nGetRunAdvance(long nativePaint, @NonNull char[] text, int start,
- int end, int contextStart, int contextEnd,
- boolean isRtl, int offset) {
- int count = end - start;
- float[] advances = new float[count];
- int bidiFlags = isRtl ? Paint.BIDI_FORCE_RTL : Paint.BIDI_FORCE_LTR;
- nGetTextAdvances(nativePaint, text, start, count, contextStart,
- contextEnd - contextStart, bidiFlags, advances, 0);
- int startOffset = offset - start; // offset from start.
- float sum = 0;
- for (int i = 0; i < startOffset; i++) {
- sum += advances[i];
- }
- return sum;
- }
-
- @LayoutlibDelegate
- /*package*/ static int nGetOffsetForAdvance(long nativePaint, char[] text, int start,
- int end, int contextStart, int contextEnd, boolean isRtl, float advance) {
- int count = end - start;
- float[] advances = new float[count];
- int bidiFlags = isRtl ? Paint.BIDI_FORCE_RTL : Paint.BIDI_FORCE_LTR;
- nGetTextAdvances(nativePaint, text, start, count, contextStart,
- contextEnd - contextStart, bidiFlags, advances, 0);
- float sum = 0;
- int i;
- for (i = 0; i < count && sum < advance; i++) {
- sum += advances[i];
- }
- float distanceToI = sum - advance;
- float distanceToIMinus1 = advance - (sum - advances[i]);
- return distanceToI > distanceToIMinus1 ? i : i - 1;
- }
-
- @LayoutlibDelegate
- /*package*/ static float nGetUnderlinePosition(long paintPtr) {
- return (1.0f / 9.0f) * nGetTextSize(paintPtr);
- }
-
- @LayoutlibDelegate
- /*package*/ static float nGetUnderlineThickness(long paintPtr) {
- return (1.0f / 18.0f) * nGetTextSize(paintPtr);
- }
-
- @LayoutlibDelegate
- /*package*/ static float nGetStrikeThruPosition(long paintPtr) {
- return (-79.0f / 252.0f) * nGetTextSize(paintPtr);
- }
-
- @LayoutlibDelegate
- /*package*/ static float nGetStrikeThruThickness(long paintPtr) {
- return (1.0f / 18.0f) * nGetTextSize(paintPtr);
- }
-
- @LayoutlibDelegate
- /*package*/ static boolean nEqualsForTextMeasurement(long leftPaintPtr, long rightPaintPtr) {
- return leftPaintPtr == rightPaintPtr;
- }
-
- // ---- Private delegate/helper methods ----
-
- /*package*/ Paint_Delegate() {
- reset();
- }
-
- private Paint_Delegate(Paint_Delegate paint) {
- set(paint);
- }
-
- private void set(Paint_Delegate paint) {
- mFlags = paint.mFlags;
- mColor = paint.mColor;
- mStyle = paint.mStyle;
- mCap = paint.mCap;
- mJoin = paint.mJoin;
- mTextAlign = paint.mTextAlign;
-
- if (mTypeface != paint.mTypeface) {
- mTypeface = paint.mTypeface;
- invalidateFonts();
- }
-
- if (mTextSize != paint.mTextSize) {
- mTextSize = paint.mTextSize;
- invalidateFonts();
- }
-
- if (mTextScaleX != paint.mTextScaleX) {
- mTextScaleX = paint.mTextScaleX;
- invalidateFonts();
- }
-
- if (mTextSkewX != paint.mTextSkewX) {
- mTextSkewX = paint.mTextSkewX;
- invalidateFonts();
- }
-
- mStrokeWidth = paint.mStrokeWidth;
- mStrokeMiter = paint.mStrokeMiter;
- mPorterDuffMode = paint.mPorterDuffMode;
- mColorFilter = paint.mColorFilter;
- mShader = paint.mShader;
- mPathEffect = paint.mPathEffect;
- mMaskFilter = paint.mMaskFilter;
- mHintingMode = paint.mHintingMode;
- }
-
- private void reset() {
- Typeface_Delegate defaultTypeface =
- Typeface_Delegate.getDelegate(Typeface.sDefaults[0].native_instance);
-
- mFlags = Paint.HIDDEN_DEFAULT_PAINT_FLAGS;
- mColor = 0xFF000000;
- mStyle = Paint.Style.FILL.nativeInt;
- mCap = Paint.Cap.BUTT.nativeInt;
- mJoin = Paint.Join.MITER.nativeInt;
- mTextAlign = 0;
-
- if (mTypeface != defaultTypeface) {
- mTypeface = defaultTypeface;
- invalidateFonts();
- }
-
- mStrokeWidth = 1.f;
- mStrokeMiter = 4.f;
-
- if (mTextSize != DEFAULT_TEXT_SIZE) {
- mTextSize = DEFAULT_TEXT_SIZE;
- invalidateFonts();
- }
-
- if (mTextScaleX != DEFAULT_TEXT_SCALE_X) {
- mTextScaleX = DEFAULT_TEXT_SCALE_X;
- invalidateFonts();
- }
-
- if (mTextSkewX != DEFAULT_TEXT_SKEW_X) {
- mTextSkewX = DEFAULT_TEXT_SKEW_X;
- invalidateFonts();
- }
-
- mPorterDuffMode = Xfermode.DEFAULT;
- mColorFilter = null;
- mShader = null;
- mPathEffect = null;
- mMaskFilter = null;
- mHintingMode = Paint.HINTING_ON;
- }
-
- private void invalidateFonts() {
- mFonts = null;
- }
-
- @Nullable
- private static FontInfo getFontInfo(@Nullable Font font, float textSize,
- @Nullable AffineTransform transform) {
- if (font == null) {
- return null;
- }
-
- Font transformedFont = font.deriveFont(textSize);
- if (transform != null) {
- // TODO: support skew
- transformedFont = transformedFont.deriveFont(transform);
- }
-
- // The metrics here don't have anti-aliasing set.
- return new FontInfo(transformedFont,
- Toolkit.getDefaultToolkit().getFontMetrics(transformedFont));
- }
-
- /*package*/ RectF measureText(char[] text, int index, int count, float[] advances,
- int advancesIndex, int bidiFlags) {
- return new BidiRenderer(null, this, text)
- .renderText(index, index + count, bidiFlags, advances, advancesIndex, false);
- }
-
- /*package*/ RectF measureText(char[] text, int index, int count, float[] advances,
- int advancesIndex, boolean isRtl) {
- return new BidiRenderer(null, this, text)
- .renderText(index, index + count, isRtl, advances, advancesIndex, false);
- }
-
- private float getFontMetrics(FontMetrics metrics) {
- List<FontInfo> fonts = getFonts();
- if (fonts.size() > 0) {
- java.awt.FontMetrics javaMetrics = fonts.get(0).mMetrics;
- if (metrics != null) {
- // Android expects negative ascent so we invert the value from Java.
- metrics.top = - javaMetrics.getMaxAscent();
- metrics.ascent = - javaMetrics.getAscent();
- metrics.descent = javaMetrics.getDescent();
- metrics.bottom = javaMetrics.getMaxDescent();
- metrics.leading = javaMetrics.getLeading();
- }
-
- return javaMetrics.getHeight();
- }
-
- return 0;
- }
-
- private void setTextLocale(String locale) {
- mLocale = new Locale(locale);
- }
-
- private static void setFlag(long nativePaint, int flagMask, boolean flagValue) {
- // get the delegate from the native int.
- Paint_Delegate delegate = sManager.getDelegate(nativePaint);
- if (delegate == null) {
- return;
- }
-
- if (flagValue) {
- delegate.mFlags |= flagMask;
- } else {
- delegate.mFlags &= ~flagMask;
- }
- }
-}
diff --git a/bridge/src/android/graphics/PathDashPathEffect_Delegate.java b/bridge/src/android/graphics/PathDashPathEffect_Delegate.java
deleted file mode 100644
index fd9ba62e26..0000000000
--- a/bridge/src/android/graphics/PathDashPathEffect_Delegate.java
+++ /dev/null
@@ -1,72 +0,0 @@
-/*
- * Copyright (C) 2010 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package android.graphics;
-
-import com.android.layoutlib.bridge.impl.DelegateManager;
-import com.android.tools.layoutlib.annotations.LayoutlibDelegate;
-
-import java.awt.Stroke;
-
-/**
- * Delegate implementing the native methods of android.graphics.PathDashPathEffect
- *
- * Through the layoutlib_create tool, the original native methods of PathDashPathEffect have been
- * replaced by calls to methods of the same name in this delegate class.
- *
- * This class behaves like the original native implementation, but in Java, keeping previously
- * native data into its own objects and mapping them to int that are sent back and forth between
- * it and the original PathDashPathEffect class.
- *
- * Because this extends {@link PathEffect_Delegate}, there's no need to use a {@link DelegateManager},
- * as all the Shader classes will be added to the manager owned by {@link PathEffect_Delegate}.
- *
- * @see PathEffect_Delegate
- *
- */
-public class PathDashPathEffect_Delegate extends PathEffect_Delegate {
-
- // ---- delegate data ----
-
- // ---- Public Helper methods ----
-
- @Override
- public Stroke getStroke(Paint_Delegate paint) {
- // FIXME
- return null;
- }
-
- @Override
- public boolean isSupported() {
- return false;
- }
-
- @Override
- public String getSupportMessage() {
- return "Path Dash Path Effects are not supported in Layout Preview mode.";
- }
-
- // ---- native methods ----
-
- @LayoutlibDelegate
- /*package*/ static long nativeCreate(long native_path, float advance, float phase,
- int native_style) {
- PathDashPathEffect_Delegate newDelegate = new PathDashPathEffect_Delegate();
- return sManager.addNewDelegate(newDelegate);
- }
-
- // ---- Private delegate/helper methods ----
-}
diff --git a/bridge/src/android/graphics/PathEffect_Delegate.java b/bridge/src/android/graphics/PathEffect_Delegate.java
deleted file mode 100644
index 000481ec7c..0000000000
--- a/bridge/src/android/graphics/PathEffect_Delegate.java
+++ /dev/null
@@ -1,69 +0,0 @@
-/*
- * Copyright (C) 2010 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package android.graphics;
-
-import com.android.layoutlib.bridge.impl.DelegateManager;
-import com.android.tools.layoutlib.annotations.LayoutlibDelegate;
-
-import java.awt.Stroke;
-
-/**
- * Delegate implementing the native methods of android.graphics.PathEffect
- *
- * Through the layoutlib_create tool, the original native methods of PathEffect have been replaced
- * by calls to methods of the same name in this delegate class.
- *
- * This class behaves like the original native implementation, but in Java, keeping previously
- * native data into its own objects and mapping them to int that are sent back and forth between
- * it and the original PathEffect class.
- *
- * This also serve as a base class for all PathEffect delegate classes.
- *
- * @see DelegateManager
- *
- */
-public abstract class PathEffect_Delegate {
-
- // ---- delegate manager ----
- protected static final DelegateManager<PathEffect_Delegate> sManager =
- new DelegateManager<PathEffect_Delegate>(PathEffect_Delegate.class);
-
- // ---- delegate helper data ----
-
- // ---- delegate data ----
-
- // ---- Public Helper methods ----
-
- public static PathEffect_Delegate getDelegate(long nativeShader) {
- return sManager.getDelegate(nativeShader);
- }
-
- public abstract Stroke getStroke(Paint_Delegate paint);
- public abstract boolean isSupported();
- public abstract String getSupportMessage();
-
-
- // ---- native methods ----
-
- @LayoutlibDelegate
- /*package*/ static void nativeDestructor(long native_patheffect) {
- sManager.removeJavaReferenceFor(native_patheffect);
- }
-
- // ---- Private delegate/helper methods ----
-
-}
diff --git a/bridge/src/android/graphics/PathMeasure_Delegate.java b/bridge/src/android/graphics/PathMeasure_Delegate.java
deleted file mode 100644
index 83a4ff1843..0000000000
--- a/bridge/src/android/graphics/PathMeasure_Delegate.java
+++ /dev/null
@@ -1,222 +0,0 @@
-/*
- * Copyright (C) 2015 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.graphics;
-
-import com.android.ide.common.rendering.api.ILayoutLog;
-import com.android.layoutlib.bridge.Bridge;
-import com.android.layoutlib.bridge.impl.DelegateManager;
-import com.android.layoutlib.bridge.util.CachedPathIteratorFactory;
-import com.android.layoutlib.bridge.util.CachedPathIteratorFactory.CachedPathIterator;
-import com.android.tools.layoutlib.annotations.LayoutlibDelegate;
-
-import java.awt.geom.PathIterator;
-
-/**
- * Delegate implementing the native methods of {@link android.graphics.PathMeasure}
- * <p/>
- * Through the layoutlib_create tool, the original native methods of PathMeasure have been
- * replaced by
- * calls to methods of the same name in this delegate class.
- * <p/>
- * This class behaves like the original native implementation, but in Java, keeping previously
- * native data into its own objects and mapping them to int that are sent back and forth between it
- * and the original PathMeasure class.
- *
- * @see DelegateManager
- */
-public final class PathMeasure_Delegate {
-
- // ---- delegate manager ----
- private static final DelegateManager<PathMeasure_Delegate> sManager =
- new DelegateManager<PathMeasure_Delegate>(PathMeasure_Delegate.class);
-
- // ---- delegate data ----
- private CachedPathIteratorFactory mOriginalPathIterator;
-
- private long mNativePath;
-
-
- private PathMeasure_Delegate(long native_path, boolean forceClosed) {
- mNativePath = native_path;
- if (native_path != 0) {
- if (forceClosed) {
- // Copy the path and call close
- native_path = Path_Delegate.nInit(native_path);
- Path_Delegate.nClose(native_path);
- }
-
- Path_Delegate pathDelegate = Path_Delegate.getDelegate(native_path);
- mOriginalPathIterator = new CachedPathIteratorFactory(pathDelegate.getJavaShape()
- .getPathIterator(null));
- }
- }
-
- @LayoutlibDelegate
- /*package*/ static long native_create(long native_path, boolean forceClosed) {
- return sManager.addNewDelegate(new PathMeasure_Delegate(native_path, forceClosed));
- }
-
- @LayoutlibDelegate
- /*package*/ static void native_destroy(long native_instance) {
- sManager.removeJavaReferenceFor(native_instance);
- }
-
- @LayoutlibDelegate
- /*package*/ static boolean native_getPosTan(long native_instance, float distance, float pos[],
- float tan[]) {
- Bridge.getLog().fidelityWarning(ILayoutLog.TAG_UNSUPPORTED,
- "PathMeasure.getPostTan is not supported.", null, null, null);
- return false;
- }
-
- @LayoutlibDelegate
- /*package*/ static boolean native_getMatrix(long native_instance, float distance, long
- native_matrix, int flags) {
- Bridge.getLog().fidelityWarning(ILayoutLog.TAG_UNSUPPORTED,
- "PathMeasure.getMatrix is not supported.", null, null, null);
- return false;
- }
-
- @LayoutlibDelegate
- /*package*/ static boolean native_nextContour(long native_instance) {
- Bridge.getLog().fidelityWarning(ILayoutLog.TAG_UNSUPPORTED,
- "PathMeasure.nextContour is not supported.", null, null, null);
- return false;
- }
-
- @LayoutlibDelegate
- /*package*/ static void native_setPath(long native_instance, long native_path, boolean
- forceClosed) {
- PathMeasure_Delegate pathMeasure = sManager.getDelegate(native_instance);
- assert pathMeasure != null;
-
- if (native_path != 0) {
- if (forceClosed) {
- // Copy the path and call close
- native_path = Path_Delegate.nInit(native_path);
- Path_Delegate.nClose(native_path);
- }
-
- Path_Delegate pathDelegate = Path_Delegate.getDelegate(native_path);
- pathMeasure.mOriginalPathIterator = new CachedPathIteratorFactory(pathDelegate.getJavaShape()
- .getPathIterator(null));
- }
-
- pathMeasure.mNativePath = native_path;
- }
-
- @LayoutlibDelegate
- /*package*/ static float native_getLength(long native_instance) {
- PathMeasure_Delegate pathMeasure = sManager.getDelegate(native_instance);
- assert pathMeasure != null;
-
- if (pathMeasure.mOriginalPathIterator == null) {
- return 0;
- }
-
- return pathMeasure.mOriginalPathIterator.iterator().getTotalLength();
- }
-
- @LayoutlibDelegate
- /*package*/ static boolean native_isClosed(long native_instance) {
- PathMeasure_Delegate pathMeasure = sManager.getDelegate(native_instance);
- assert pathMeasure != null;
-
- Path_Delegate path = Path_Delegate.getDelegate(pathMeasure.mNativePath);
- if (path == null) {
- return false;
- }
-
- int type = 0;
- float segment[] = new float[6];
- for (PathIterator pi = path.getJavaShape().getPathIterator(null); !pi.isDone(); pi.next()) {
- type = pi.currentSegment(segment);
- }
-
- // A path is a closed path if the last element is SEG_CLOSE
- return type == PathIterator.SEG_CLOSE;
- }
-
- @LayoutlibDelegate
- /*package*/ static boolean native_getSegment(long native_instance, float startD, float stopD,
- long native_dst_path, boolean startWithMoveTo) {
- if (startD < 0) {
- startD = 0;
- }
-
- if (startD >= stopD) {
- return false;
- }
-
- PathMeasure_Delegate pathMeasure = sManager.getDelegate(native_instance);
- assert pathMeasure != null;
-
- CachedPathIterator iterator = pathMeasure.mOriginalPathIterator.iterator();
- float accLength = startD;
- boolean isZeroLength = true; // Whether the output has zero length or not
- float[] points = new float[6];
-
- iterator.jumpToSegment(accLength);
- while (!iterator.isDone() && (stopD - accLength > 0.1f)) {
- int type = iterator.currentSegment(points, stopD - accLength);
-
- if (accLength - iterator.getCurrentSegmentLength() <= stopD) {
- if (startWithMoveTo) {
- startWithMoveTo = false;
-
- // If this segment is a MOVETO, then we just use that one. If not, then we issue
- // a first moveto
- if (type != PathIterator.SEG_MOVETO) {
- float[] lastPoint = new float[2];
- iterator.getCurrentSegmentEnd(lastPoint);
- Path_Delegate.nMoveTo(native_dst_path, lastPoint[0], lastPoint[1]);
- }
- }
-
- isZeroLength = isZeroLength && iterator.getCurrentSegmentLength() > 0;
- switch (type) {
- case PathIterator.SEG_MOVETO:
- Path_Delegate.nMoveTo(native_dst_path, points[0], points[1]);
- break;
- case PathIterator.SEG_LINETO:
- Path_Delegate.nLineTo(native_dst_path, points[0], points[1]);
- break;
- case PathIterator.SEG_CLOSE:
- Path_Delegate.nClose(native_dst_path);
- break;
- case PathIterator.SEG_CUBICTO:
- Path_Delegate.nCubicTo(native_dst_path, points[0], points[1],
- points[2], points[3],
- points[4], points[5]);
- break;
- case PathIterator.SEG_QUADTO:
- Path_Delegate.nQuadTo(native_dst_path, points[0], points[1],
- points[2],
- points[3]);
- break;
- default:
- assert false;
- }
- }
-
- accLength += iterator.getCurrentSegmentLength();
- iterator.next();
- }
-
- return !isZeroLength;
- }
-}
diff --git a/bridge/src/android/graphics/Path_Delegate.java b/bridge/src/android/graphics/Path_Delegate.java
index 88f2815855..6e84eea7a2 100644
--- a/bridge/src/android/graphics/Path_Delegate.java
+++ b/bridge/src/android/graphics/Path_Delegate.java
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2010 The Android Open Source Project
+ * 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.
@@ -16,894 +16,12 @@
package android.graphics;
-import com.android.ide.common.rendering.api.ILayoutLog;
-import com.android.layoutlib.bridge.Bridge;
-import com.android.layoutlib.bridge.impl.DelegateManager;
-import com.android.tools.layoutlib.annotations.LayoutlibDelegate;
-
-import android.annotation.NonNull;
-import android.graphics.Path.Direction;
-import android.graphics.Path.FillType;
-
-import java.awt.Shape;
-import java.awt.geom.AffineTransform;
-import java.awt.geom.Arc2D;
-import java.awt.geom.Area;
-import java.awt.geom.Ellipse2D;
-import java.awt.geom.GeneralPath;
-import java.awt.geom.Path2D;
-import java.awt.geom.PathIterator;
-import java.awt.geom.Point2D;
-import java.awt.geom.Rectangle2D;
-import java.awt.geom.RoundRectangle2D;
-import java.util.ArrayList;
-
-import libcore.util.NativeAllocationRegistry_Delegate;
-
-/**
- * Delegate implementing the native methods of android.graphics.Path
- *
- * Through the layoutlib_create tool, the original native methods of Path have been replaced
- * by calls to methods of the same name in this delegate class.
- *
- * This class behaves like the original native implementation, but in Java, keeping previously
- * native data into its own objects and mapping them to int that are sent back and forth between
- * it and the original Path class.
- *
- * @see DelegateManager
- *
- */
-public final class Path_Delegate {
-
- // ---- delegate manager ----
- private static final DelegateManager<Path_Delegate> sManager =
- new DelegateManager<Path_Delegate>(Path_Delegate.class);
-
- private static final float EPSILON = 1e-4f;
-
- private static long sFinalizer = -1;
-
- // ---- delegate data ----
- private FillType mFillType = FillType.WINDING;
- private Path2D mPath = new Path2D.Double();
-
- private float mLastX = 0;
- private float mLastY = 0;
-
- // true if the path contains does not contain a curve or line.
- private boolean mCachedIsEmpty = true;
-
- // ---- Public Helper methods ----
-
- public static Path_Delegate getDelegate(long nPath) {
- return sManager.getDelegate(nPath);
- }
-
- public Path2D getJavaShape() {
- return mPath;
- }
-
- public void setJavaShape(Shape shape) {
- reset();
- mPath.append(shape, false /*connect*/);
- }
-
- public void reset() {
- mPath.reset();
- mLastX = 0;
- mLastY = 0;
- }
-
- public void setPathIterator(PathIterator iterator) {
- reset();
- mPath.append(iterator, false /*connect*/);
- }
-
- // ---- native methods ----
-
- @LayoutlibDelegate
- /*package*/ static long nInit() {
- // create the delegate
- Path_Delegate newDelegate = new Path_Delegate();
-
- return sManager.addNewDelegate(newDelegate);
- }
-
- @LayoutlibDelegate
- /*package*/ static long nInit(long nPath) {
- // create the delegate
- Path_Delegate newDelegate = new Path_Delegate();
-
- // get the delegate to copy, which could be null if nPath is 0
- Path_Delegate pathDelegate = sManager.getDelegate(nPath);
- if (pathDelegate != null) {
- newDelegate.set(pathDelegate);
- }
-
- return sManager.addNewDelegate(newDelegate);
- }
-
- @LayoutlibDelegate
- /*package*/ static void nReset(long nPath) {
- Path_Delegate pathDelegate = sManager.getDelegate(nPath);
- if (pathDelegate == null) {
- return;
- }
-
- pathDelegate.reset();
- }
-
- @LayoutlibDelegate
- /*package*/ static void nRewind(long nPath) {
- // call out to reset since there's nothing to optimize in
- // terms of data structs.
- nReset(nPath);
- }
-
- @LayoutlibDelegate
- /*package*/ static void nSet(long native_dst, long nSrc) {
- Path_Delegate pathDstDelegate = sManager.getDelegate(native_dst);
- if (pathDstDelegate == null) {
- return;
- }
-
- Path_Delegate pathSrcDelegate = sManager.getDelegate(nSrc);
- if (pathSrcDelegate == null) {
- return;
- }
-
- pathDstDelegate.set(pathSrcDelegate);
- }
-
- @LayoutlibDelegate
- /*package*/ static boolean nIsConvex(long nPath) {
- Bridge.getLog().fidelityWarning(ILayoutLog.TAG_UNSUPPORTED,
- "Path.isConvex is not supported.", null, null, null);
- return true;
- }
-
- @LayoutlibDelegate
- /*package*/ static int nGetFillType(long nPath) {
- Path_Delegate pathDelegate = sManager.getDelegate(nPath);
- if (pathDelegate == null) {
- return 0;
- }
-
- return pathDelegate.mFillType.nativeInt;
- }
-
- @LayoutlibDelegate
- public static void nSetFillType(long nPath, int ft) {
- Path_Delegate pathDelegate = sManager.getDelegate(nPath);
- if (pathDelegate == null) {
- return;
- }
-
- pathDelegate.setFillType(Path.sFillTypeArray[ft]);
- }
-
- @LayoutlibDelegate
- /*package*/ static boolean nIsEmpty(long nPath) {
- Path_Delegate pathDelegate = sManager.getDelegate(nPath);
- return pathDelegate == null || pathDelegate.isEmpty();
-
- }
-
- @LayoutlibDelegate
- /*package*/ static boolean nIsRect(long nPath, RectF rect) {
- Path_Delegate pathDelegate = sManager.getDelegate(nPath);
- if (pathDelegate == null) {
- return false;
- }
-
- // create an Area that can test if the path is a rect
- Area area = new Area(pathDelegate.mPath);
- if (area.isRectangular()) {
- if (rect != null) {
- pathDelegate.fillBounds(rect);
- }
-
- return true;
- }
-
- return false;
- }
-
- @LayoutlibDelegate
- /*package*/ static void nComputeBounds(long nPath, RectF bounds) {
- Path_Delegate pathDelegate = sManager.getDelegate(nPath);
- if (pathDelegate == null) {
- return;
- }
-
- pathDelegate.fillBounds(bounds);
- }
-
- @LayoutlibDelegate
- /*package*/ static void nIncReserve(long nPath, int extraPtCount) {
- // since we use a java2D path, there's no way to pre-allocate new points,
- // so we do nothing.
- }
-
- @LayoutlibDelegate
- /*package*/ static void nMoveTo(long nPath, float x, float y) {
- Path_Delegate pathDelegate = sManager.getDelegate(nPath);
- if (pathDelegate == null) {
- return;
- }
-
- pathDelegate.moveTo(x, y);
- }
-
- @LayoutlibDelegate
- /*package*/ static void nRMoveTo(long nPath, float dx, float dy) {
- Path_Delegate pathDelegate = sManager.getDelegate(nPath);
- if (pathDelegate == null) {
- return;
- }
-
- pathDelegate.rMoveTo(dx, dy);
- }
-
- @LayoutlibDelegate
- /*package*/ static void nLineTo(long nPath, float x, float y) {
- Path_Delegate pathDelegate = sManager.getDelegate(nPath);
- if (pathDelegate == null) {
- return;
- }
-
- pathDelegate.lineTo(x, y);
- }
-
- @LayoutlibDelegate
- /*package*/ static void nRLineTo(long nPath, float dx, float dy) {
- Path_Delegate pathDelegate = sManager.getDelegate(nPath);
- if (pathDelegate == null) {
- return;
- }
-
- pathDelegate.rLineTo(dx, dy);
- }
-
- @LayoutlibDelegate
- /*package*/ static void nQuadTo(long nPath, float x1, float y1, float x2, float y2) {
- Path_Delegate pathDelegate = sManager.getDelegate(nPath);
- if (pathDelegate == null) {
- return;
- }
-
- pathDelegate.quadTo(x1, y1, x2, y2);
- }
-
- @LayoutlibDelegate
- /*package*/ static void nRQuadTo(long nPath, float dx1, float dy1, float dx2, float dy2) {
- Path_Delegate pathDelegate = sManager.getDelegate(nPath);
- if (pathDelegate == null) {
- return;
- }
-
- pathDelegate.rQuadTo(dx1, dy1, dx2, dy2);
- }
-
- @LayoutlibDelegate
- /*package*/ static void nCubicTo(long nPath, float x1, float y1,
- float x2, float y2, float x3, float y3) {
- Path_Delegate pathDelegate = sManager.getDelegate(nPath);
- if (pathDelegate == null) {
- return;
- }
-
- pathDelegate.cubicTo(x1, y1, x2, y2, x3, y3);
- }
-
- @LayoutlibDelegate
- /*package*/ static void nRCubicTo(long nPath, float x1, float y1,
- float x2, float y2, float x3, float y3) {
- Path_Delegate pathDelegate = sManager.getDelegate(nPath);
- if (pathDelegate == null) {
- return;
- }
-
- pathDelegate.rCubicTo(x1, y1, x2, y2, x3, y3);
- }
-
- @LayoutlibDelegate
- /*package*/ static void nArcTo(long nPath, float left, float top, float right,
- float bottom,
- float startAngle, float sweepAngle, boolean forceMoveTo) {
- Path_Delegate pathDelegate = sManager.getDelegate(nPath);
- if (pathDelegate == null) {
- return;
- }
-
- pathDelegate.arcTo(left, top, right, bottom, startAngle, sweepAngle, forceMoveTo);
- }
-
- @LayoutlibDelegate
- /*package*/ static void nClose(long nPath) {
- Path_Delegate pathDelegate = sManager.getDelegate(nPath);
- if (pathDelegate == null) {
- return;
- }
-
- pathDelegate.close();
- }
-
- @LayoutlibDelegate
- /*package*/ static void nAddRect(long nPath,
- float left, float top, float right, float bottom, int dir) {
- Path_Delegate pathDelegate = sManager.getDelegate(nPath);
- if (pathDelegate == null) {
- return;
- }
-
- pathDelegate.addRect(left, top, right, bottom, dir);
- }
-
- @LayoutlibDelegate
- /*package*/ static void nAddOval(long nPath, float left, float top, float right,
- float bottom, int dir) {
- Path_Delegate pathDelegate = sManager.getDelegate(nPath);
- if (pathDelegate == null) {
- return;
- }
-
- pathDelegate.mPath.append(new Ellipse2D.Float(
- left, top, right - left, bottom - top), false);
- }
-
- @LayoutlibDelegate
- /*package*/ static void nAddCircle(long nPath, float x, float y, float radius, int dir) {
- Path_Delegate pathDelegate = sManager.getDelegate(nPath);
- if (pathDelegate == null) {
- return;
- }
-
- // because x/y is the center of the circle, need to offset this by the radius
- pathDelegate.mPath.append(new Ellipse2D.Float(
- x - radius, y - radius, radius * 2, radius * 2), false);
- }
-
- @LayoutlibDelegate
- /*package*/ static void nAddArc(long nPath, float left, float top, float right,
- float bottom, float startAngle, float sweepAngle) {
- Path_Delegate pathDelegate = sManager.getDelegate(nPath);
- if (pathDelegate == null) {
- return;
- }
-
- // because x/y is the center of the circle, need to offset this by the radius
- pathDelegate.mPath.append(new Arc2D.Float(
- left, top, right - left, bottom - top,
- -startAngle, -sweepAngle, Arc2D.OPEN), false);
- }
-
- @LayoutlibDelegate
- /*package*/ static void nAddRoundRect(long nPath, float left, float top, float right,
- float bottom, float rx, float ry, int dir) {
-
- Path_Delegate pathDelegate = sManager.getDelegate(nPath);
- if (pathDelegate == null) {
- return;
- }
-
- pathDelegate.mPath.append(new RoundRectangle2D.Float(
- left, top, right - left, bottom - top, rx * 2, ry * 2), false);
- }
-
- @LayoutlibDelegate
- /*package*/ static void nAddRoundRect(long nPath, float left, float top, float right,
- float bottom, float[] radii, int dir) {
-
- Path_Delegate pathDelegate = sManager.getDelegate(nPath);
- if (pathDelegate == null) {
- return;
- }
-
- float[] cornerDimensions = new float[radii.length];
- for (int i = 0; i < radii.length; i++) {
- cornerDimensions[i] = 2 * radii[i];
- }
- pathDelegate.mPath.append(new RoundRectangle(left, top, right - left, bottom - top,
- cornerDimensions), false);
- }
-
- @LayoutlibDelegate
- /*package*/ static void nAddPath(long nPath, long src, float dx, float dy) {
- addPath(nPath, src, AffineTransform.getTranslateInstance(dx, dy));
- }
-
- @LayoutlibDelegate
- /*package*/ static void nAddPath(long nPath, long src) {
- addPath(nPath, src, null /*transform*/);
- }
-
- @LayoutlibDelegate
- /*package*/ static void nAddPath(long nPath, long src, long matrix) {
- Matrix_Delegate matrixDelegate = Matrix_Delegate.getDelegate(matrix);
- if (matrixDelegate == null) {
- return;
- }
-
- addPath(nPath, src, matrixDelegate.getAffineTransform());
- }
-
- @LayoutlibDelegate
- /*package*/ static void nOffset(long nPath, float dx, float dy) {
- Path_Delegate pathDelegate = sManager.getDelegate(nPath);
- if (pathDelegate == null) {
- return;
- }
-
- pathDelegate.offset(dx, dy);
- }
-
- @LayoutlibDelegate
- /*package*/ static void nSetLastPoint(long nPath, float dx, float dy) {
- Path_Delegate pathDelegate = sManager.getDelegate(nPath);
- if (pathDelegate == null) {
- return;
- }
-
- pathDelegate.mLastX = dx;
- pathDelegate.mLastY = dy;
- }
-
- @LayoutlibDelegate
- /*package*/ static void nTransform(long nPath, long matrix,
- long dst_path) {
- Path_Delegate pathDelegate = sManager.getDelegate(nPath);
- if (pathDelegate == null) {
- return;
- }
-
- Matrix_Delegate matrixDelegate = Matrix_Delegate.getDelegate(matrix);
- if (matrixDelegate == null) {
- return;
- }
-
- // this can be null if dst_path is 0
- Path_Delegate dstDelegate = sManager.getDelegate(dst_path);
-
- pathDelegate.transform(matrixDelegate, dstDelegate);
- }
-
- @LayoutlibDelegate
- /*package*/ static void nTransform(long nPath, long matrix) {
- nTransform(nPath, matrix, 0);
- }
-
- @LayoutlibDelegate
- /*package*/ static boolean nOp(long nPath1, long nPath2, int op, long result) {
- Bridge.getLog().error(ILayoutLog.TAG_UNSUPPORTED, "Path.op() not supported", null, null);
- return false;
- }
-
- @LayoutlibDelegate
- /*package*/ static long nGetFinalizer() {
- synchronized (Path_Delegate.class) {
- if (sFinalizer == -1) {
- sFinalizer = NativeAllocationRegistry_Delegate.createFinalizer(
- sManager::removeJavaReferenceFor);
- }
- }
- return sFinalizer;
- }
-
- @LayoutlibDelegate
- /*package*/ static float[] nApproximate(long nPath, float error) {
- Path_Delegate pathDelegate = sManager.getDelegate(nPath);
- if (pathDelegate == null) {
- return null;
- }
- // Get a FlatteningIterator
- PathIterator iterator = pathDelegate.getJavaShape().getPathIterator(null, error);
-
- float segment[] = new float[6];
- float totalLength = 0;
- ArrayList<Point2D.Float> points = new ArrayList<Point2D.Float>();
- Point2D.Float previousPoint = null;
- while (!iterator.isDone()) {
- int type = iterator.currentSegment(segment);
- Point2D.Float currentPoint = new Point2D.Float(segment[0], segment[1]);
- // MoveTo shouldn't affect the length
- if (previousPoint != null && type != PathIterator.SEG_MOVETO) {
- totalLength += currentPoint.distance(previousPoint);
- }
- previousPoint = currentPoint;
- points.add(currentPoint);
- iterator.next();
- }
-
- int nPoints = points.size();
- float[] result = new float[nPoints * 3];
- previousPoint = null;
- // Distance that we've covered so far. Used to calculate the fraction of the path that
- // we've covered up to this point.
- float walkedDistance = .0f;
- for (int i = 0; i < nPoints; i++) {
- Point2D.Float point = points.get(i);
- float distance = previousPoint != null ? (float) previousPoint.distance(point) : .0f;
- walkedDistance += distance;
- result[i * 3] = walkedDistance / totalLength;
- result[i * 3 + 1] = point.x;
- result[i * 3 + 2] = point.y;
-
- previousPoint = point;
- }
-
- return result;
- }
-
- // ---- Private helper methods ----
-
- private void set(Path_Delegate delegate) {
- mPath.reset();
- setFillType(delegate.mFillType);
- mPath.append(delegate.mPath, false /*connect*/);
- }
-
- private void setFillType(FillType fillType) {
- mFillType = fillType;
- mPath.setWindingRule(getWindingRule(fillType));
- }
-
- /**
- * Returns the Java2D winding rules matching a given Android {@link FillType}.
- * @param type the android fill type
- * @return the matching java2d winding rule.
- */
- private static int getWindingRule(FillType type) {
- switch (type) {
- case WINDING:
- case INVERSE_WINDING:
- return GeneralPath.WIND_NON_ZERO;
- case EVEN_ODD:
- case INVERSE_EVEN_ODD:
- return GeneralPath.WIND_EVEN_ODD;
-
- default:
- assert false;
- return GeneralPath.WIND_NON_ZERO;
- }
- }
-
- @NonNull
- private static Direction getDirection(int direction) {
- for (Direction d : Direction.values()) {
- if (direction == d.nativeInt) {
- return d;
- }
- }
-
- assert false;
- return null;
- }
-
- public static void addPath(long destPath, long srcPath, AffineTransform transform) {
- Path_Delegate destPathDelegate = sManager.getDelegate(destPath);
- if (destPathDelegate == null) {
- return;
- }
-
- Path_Delegate srcPathDelegate = sManager.getDelegate(srcPath);
- if (srcPathDelegate == null) {
- return;
- }
-
- if (transform != null) {
- destPathDelegate.mPath.append(
- srcPathDelegate.mPath.getPathIterator(transform), false);
- } else {
- destPathDelegate.mPath.append(srcPathDelegate.mPath, false);
- }
- }
-
-
- /**
- * Returns whether the path already contains any points.
- * Note that this is different to
- * {@link #isEmpty} because if all elements are {@link PathIterator#SEG_MOVETO},
- * {@link #isEmpty} will return true while hasPoints will return false.
- */
- public boolean hasPoints() {
- return !mPath.getPathIterator(null).isDone();
- }
-
- /**
- * Returns whether the path is empty (contains no lines or curves).
- * @see Path#isEmpty
- */
- public boolean isEmpty() {
- if (!mCachedIsEmpty) {
- return false;
- }
-
- float[] coords = new float[6];
- mCachedIsEmpty = Boolean.TRUE;
- for (PathIterator it = mPath.getPathIterator(null); !it.isDone(); it.next()) {
- int type = it.currentSegment(coords);
- if (type != PathIterator.SEG_MOVETO) {
- // Once we know that the path is not empty, we do not need to check again unless
- // Path#reset is called.
- mCachedIsEmpty = false;
- return false;
- }
- }
-
- return true;
- }
-
- /**
- * Fills the given {@link RectF} with the path bounds.
- * @param bounds the RectF to be filled.
- */
- public void fillBounds(RectF bounds) {
- Rectangle2D rect = mPath.getBounds2D();
- bounds.left = (float)rect.getMinX();
- bounds.right = (float)rect.getMaxX();
- bounds.top = (float)rect.getMinY();
- bounds.bottom = (float)rect.getMaxY();
- }
-
- /**
- * Set the beginning of the next contour to the point (x,y).
- *
- * @param x The x-coordinate of the start of a new contour
- * @param y The y-coordinate of the start of a new contour
- */
- public void moveTo(float x, float y) {
- mPath.moveTo(mLastX = x, mLastY = y);
- }
-
- /**
- * Set the beginning of the next contour relative to the last point on the
- * previous contour. If there is no previous contour, this is treated the
- * same as moveTo().
- *
- * @param dx The amount to add to the x-coordinate of the end of the
- * previous contour, to specify the start of a new contour
- * @param dy The amount to add to the y-coordinate of the end of the
- * previous contour, to specify the start of a new contour
- */
- public void rMoveTo(float dx, float dy) {
- dx += mLastX;
- dy += mLastY;
- mPath.moveTo(mLastX = dx, mLastY = dy);
- }
-
- /**
- * Add a line from the last point to the specified point (x,y).
- * If no moveTo() call has been made for this contour, the first point is
- * automatically set to (0,0).
- *
- * @param x The x-coordinate of the end of a line
- * @param y The y-coordinate of the end of a line
- */
- public void lineTo(float x, float y) {
- if (!hasPoints()) {
- mPath.moveTo(mLastX = 0, mLastY = 0);
- }
- mPath.lineTo(mLastX = x, mLastY = y);
- }
-
- /**
- * Same as lineTo, but the coordinates are considered relative to the last
- * point on this contour. If there is no previous point, then a moveTo(0,0)
- * is inserted automatically.
- *
- * @param dx The amount to add to the x-coordinate of the previous point on
- * this contour, to specify a line
- * @param dy The amount to add to the y-coordinate of the previous point on
- * this contour, to specify a line
- */
- public void rLineTo(float dx, float dy) {
- if (!hasPoints()) {
- mPath.moveTo(mLastX = 0, mLastY = 0);
- }
-
- if (Math.abs(dx) < EPSILON && Math.abs(dy) < EPSILON) {
- // The delta is so small that this shouldn't generate a line
- return;
- }
-
- dx += mLastX;
- dy += mLastY;
- mPath.lineTo(mLastX = dx, mLastY = dy);
- }
-
- /**
- * Add a quadratic bezier from the last point, approaching control point
- * (x1,y1), and ending at (x2,y2). If no moveTo() call has been made for
- * this contour, the first point is automatically set to (0,0).
- *
- * @param x1 The x-coordinate of the control point on a quadratic curve
- * @param y1 The y-coordinate of the control point on a quadratic curve
- * @param x2 The x-coordinate of the end point on a quadratic curve
- * @param y2 The y-coordinate of the end point on a quadratic curve
- */
- public void quadTo(float x1, float y1, float x2, float y2) {
- mPath.quadTo(x1, y1, mLastX = x2, mLastY = y2);
- }
-
- /**
- * Same as quadTo, but the coordinates are considered relative to the last
- * point on this contour. If there is no previous point, then a moveTo(0,0)
- * is inserted automatically.
- *
- * @param dx1 The amount to add to the x-coordinate of the last point on
- * this contour, for the control point of a quadratic curve
- * @param dy1 The amount to add to the y-coordinate of the last point on
- * this contour, for the control point of a quadratic curve
- * @param dx2 The amount to add to the x-coordinate of the last point on
- * this contour, for the end point of a quadratic curve
- * @param dy2 The amount to add to the y-coordinate of the last point on
- * this contour, for the end point of a quadratic curve
- */
- public void rQuadTo(float dx1, float dy1, float dx2, float dy2) {
- if (!hasPoints()) {
- mPath.moveTo(mLastX = 0, mLastY = 0);
- }
- dx1 += mLastX;
- dy1 += mLastY;
- dx2 += mLastX;
- dy2 += mLastY;
- mPath.quadTo(dx1, dy1, mLastX = dx2, mLastY = dy2);
- }
-
- /**
- * Add a cubic bezier from the last point, approaching control points
- * (x1,y1) and (x2,y2), and ending at (x3,y3). If no moveTo() call has been
- * made for this contour, the first point is automatically set to (0,0).
- *
- * @param x1 The x-coordinate of the 1st control point on a cubic curve
- * @param y1 The y-coordinate of the 1st control point on a cubic curve
- * @param x2 The x-coordinate of the 2nd control point on a cubic curve
- * @param y2 The y-coordinate of the 2nd control point on a cubic curve
- * @param x3 The x-coordinate of the end point on a cubic curve
- * @param y3 The y-coordinate of the end point on a cubic curve
- */
- public void cubicTo(float x1, float y1, float x2, float y2,
- float x3, float y3) {
- if (!hasPoints()) {
- mPath.moveTo(0, 0);
- }
- mPath.curveTo(x1, y1, x2, y2, mLastX = x3, mLastY = y3);
- }
-
- /**
- * Same as cubicTo, but the coordinates are considered relative to the
- * current point on this contour. If there is no previous point, then a
- * moveTo(0,0) is inserted automatically.
- */
- public void rCubicTo(float dx1, float dy1, float dx2, float dy2,
- float dx3, float dy3) {
- if (!hasPoints()) {
- mPath.moveTo(mLastX = 0, mLastY = 0);
- }
- dx1 += mLastX;
- dy1 += mLastY;
- dx2 += mLastX;
- dy2 += mLastY;
- dx3 += mLastX;
- dy3 += mLastY;
- mPath.curveTo(dx1, dy1, dx2, dy2, mLastX = dx3, mLastY = dy3);
- }
-
- /**
- * Append the specified arc to the path as a new contour. If the start of
- * the path is different from the path's current last point, then an
- * automatic lineTo() is added to connect the current contour to the
- * start of the arc. However, if the path is empty, then we call moveTo()
- * with the first point of the arc. The sweep angle is tread mod 360.
- *
- * @param left The left of oval defining shape and size of the arc
- * @param top The top of oval defining shape and size of the arc
- * @param right The right of oval defining shape and size of the arc
- * @param bottom The bottom of oval defining shape and size of the arc
- * @param startAngle Starting angle (in degrees) where the arc begins
- * @param sweepAngle Sweep angle (in degrees) measured clockwise, treated
- * mod 360.
- * @param forceMoveTo If true, always begin a new contour with the arc
- */
- public void arcTo(float left, float top, float right, float bottom, float startAngle,
- float sweepAngle,
- boolean forceMoveTo) {
- Arc2D arc = new Arc2D.Float(left, top, right - left, bottom - top, -startAngle,
- -sweepAngle, Arc2D.OPEN);
- mPath.append(arc, true /*connect*/);
-
- resetLastPointFromPath();
- }
-
- /**
- * Close the current contour. If the current point is not equal to the
- * first point of the contour, a line segment is automatically added.
- */
- public void close() {
- mPath.closePath();
- }
-
- private void resetLastPointFromPath() {
- Point2D last = mPath.getCurrentPoint();
- mLastX = (float) last.getX();
- mLastY = (float) last.getY();
- }
-
- /**
- * Add a closed rectangle contour to the path
- *
- * @param left The left side of a rectangle to add to the path
- * @param top The top of a rectangle to add to the path
- * @param right The right side of a rectangle to add to the path
- * @param bottom The bottom of a rectangle to add to the path
- * @param dir The direction to wind the rectangle's contour
- */
- public void addRect(float left, float top, float right, float bottom,
- int dir) {
- moveTo(left, top);
-
- Direction direction = getDirection(dir);
-
- switch (direction) {
- case CW:
- lineTo(right, top);
- lineTo(right, bottom);
- lineTo(left, bottom);
- break;
- case CCW:
- lineTo(left, bottom);
- lineTo(right, bottom);
- lineTo(right, top);
- break;
- }
-
- close();
-
- resetLastPointFromPath();
- }
-
- /**
- * Offset the path by (dx,dy), returning true on success
- *
- * @param dx The amount in the X direction to offset the entire path
- * @param dy The amount in the Y direction to offset the entire path
- */
- public void offset(float dx, float dy) {
- GeneralPath newPath = new GeneralPath();
-
- PathIterator iterator = mPath.getPathIterator(new AffineTransform(0, 0, dx, 0, 0, dy));
-
- newPath.append(iterator, false /*connect*/);
- mPath = newPath;
- }
-
- /**
- * Transform the points in this path by matrix, and write the answer
- * into dst. If dst is null, then the the original path is modified.
- *
- * @param matrix The matrix to apply to the path
- * @param dst The transformed path is written here. If dst is null,
- * then the the original path is modified
- */
- public void transform(Matrix_Delegate matrix, Path_Delegate dst) {
- if (matrix.hasPerspective()) {
- assert false;
- Bridge.getLog().fidelityWarning(ILayoutLog.TAG_MATRIX_AFFINE,
- "android.graphics.Path#transform() only " +
- "supports affine transformations.", null, null, null /*data*/);
- }
-
- GeneralPath newPath = new GeneralPath();
-
- PathIterator iterator = mPath.getPathIterator(matrix.getAffineTransform());
-
- newPath.append(iterator, false /*connect*/);
-
- if (dst != null) {
- dst.mPath = newPath;
- } else {
- mPath = newPath;
+public class Path_Delegate {
+ static long nInit(long nPath) {
+ if (nPath == 0) {
+ throw new IllegalArgumentException(
+ "A null argument to the Path constructor causes a crash in Android");
}
+ return Path.nInit(nPath);
}
}
diff --git a/bridge/src/android/graphics/PorterDuffColorFilter_Delegate.java b/bridge/src/android/graphics/PorterDuffColorFilter_Delegate.java
deleted file mode 100644
index 0dc6f6134b..0000000000
--- a/bridge/src/android/graphics/PorterDuffColorFilter_Delegate.java
+++ /dev/null
@@ -1,124 +0,0 @@
-/*
- * Copyright (C) 2010 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package android.graphics;
-
-import com.android.layoutlib.bridge.impl.DelegateManager;
-import com.android.tools.layoutlib.annotations.LayoutlibDelegate;
-
-import android.graphics.PorterDuff.Mode;
-
-import java.awt.Graphics2D;
-import java.awt.image.BufferedImage;
-
-import static com.android.layoutlib.bridge.impl.PorterDuffUtility.getComposite;
-import static com.android.layoutlib.bridge.impl.PorterDuffUtility.getPorterDuffMode;
-
-/**
- * Delegate implementing the native methods of android.graphics.PorterDuffColorFilter
- *
- * Through the layoutlib_create tool, the original native methods of PorterDuffColorFilter have
- * been replaced by calls to methods of the same name in this delegate class.
- *
- * This class behaves like the original native implementation, but in Java, keeping previously
- * native data into its own objects and mapping them to int that are sent back and forth between
- * it and the original PorterDuffColorFilter class.
- *
- * Because this extends {@link ColorFilter_Delegate}, there's no need to use a
- * {@link DelegateManager}, as all the Shader classes will be added to the manager
- * owned by {@link ColorFilter_Delegate}.
- *
- * @see ColorFilter_Delegate
- *
- */
-public class PorterDuffColorFilter_Delegate extends ColorFilter_Delegate {
-
- // ---- delegate data ----
-
- private final java.awt.Color mSrcColor;
- private final Mode mMode;
-
-
- // ---- Public Helper methods ----
-
- @Override
- public boolean isSupported() {
- return true;
- }
-
- @Override
- public String getSupportMessage() {
- return "PorterDuff Color Filter is not supported for mode: " + mMode.name() + ".";
- }
-
- @Override
- public void applyFilter(Graphics2D g, int width, int height) {
- g.setComposite(getComposite(mMode, 0xFF));
- g.setColor(mSrcColor);
- g.fillRect(0, 0, width, height);
- }
-
- // ---- native methods ----
-
- @LayoutlibDelegate
- /*package*/ static long native_CreateBlendModeFilter(int srcColor, int porterDuffMode) {
- PorterDuffColorFilter_Delegate newDelegate =
- new PorterDuffColorFilter_Delegate(srcColor, porterDuffMode);
- return sManager.addNewDelegate(newDelegate);
- }
-
-
- // ---- Private delegate/helper methods ----
-
- private PorterDuffColorFilter_Delegate(int srcColor, int mode) {
- mSrcColor = new java.awt.Color(srcColor, true /* hasAlpha */);
- mMode = getCompatibleMode(getPorterDuffMode(mode));
- }
-
- // For filtering the colors, the src image should contain the "color" only for pixel values
- // which are not transparent in the target image. But, we are using a simple rectangular image
- // completely filled with color. Hence some Composite rules do not apply as intended. However,
- // in such cases, they can usually be mapped to some other mode, which produces an approximately
- // equivalent result.
- private Mode getCompatibleMode(Mode mode) {
- Mode m = mode;
- // Modes that are directly supported:
- // CLEAR, DST, SRC_IN, DST_IN, DST_OUT, SRC_ATOP, DARKEN, LIGHTEN, MULTIPLY, SCREEN,
- // ADD, OVERLAY
- switch (mode) {
- // Modes that can be mapped to one of the supported modes.
- case SRC:
- m = Mode.SRC_IN;
- break;
- case SRC_OVER:
- m = Mode.SRC_ATOP;
- break;
- case DST_OVER:
- m = Mode.DST;
- break;
- case SRC_OUT:
- m = Mode.CLEAR;
- break;
- case DST_ATOP:
- m = Mode.DST_IN;
- break;
- case XOR:
- m = Mode.DST_OUT;
- break;
- }
- return m;
- }
-}
diff --git a/bridge/src/android/graphics/RadialGradient_Delegate.java b/bridge/src/android/graphics/RadialGradient_Delegate.java
deleted file mode 100644
index 4a18219f82..0000000000
--- a/bridge/src/android/graphics/RadialGradient_Delegate.java
+++ /dev/null
@@ -1,198 +0,0 @@
-/*
- * Copyright (C) 2010 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package android.graphics;
-
-import com.android.ide.common.rendering.api.ILayoutLog;
-import com.android.layoutlib.bridge.Bridge;
-import com.android.layoutlib.bridge.impl.DelegateManager;
-import com.android.tools.layoutlib.annotations.LayoutlibDelegate;
-
-import android.graphics.Shader.TileMode;
-
-import java.awt.image.ColorModel;
-import java.awt.image.DataBufferInt;
-import java.awt.image.Raster;
-import java.awt.image.SampleModel;
-
-/**
- * Delegate implementing the native methods of android.graphics.RadialGradient
- *
- * Through the layoutlib_create tool, the original native methods of RadialGradient have been
- * replaced by calls to methods of the same name in this delegate class.
- *
- * This class behaves like the original native implementation, but in Java, keeping previously
- * native data into its own objects and mapping them to int that are sent back and forth between
- * it and the original RadialGradient class.
- *
- * Because this extends {@link Shader_Delegate}, there's no need to use a {@link DelegateManager},
- * as all the Shader classes will be added to the manager owned by {@link Shader_Delegate}.
- *
- * @see Shader_Delegate
- *
- */
-public class RadialGradient_Delegate extends Gradient_Delegate {
-
- // ---- delegate data ----
- private java.awt.Paint mJavaPaint;
-
- // ---- Public Helper methods ----
-
- @Override
- public java.awt.Paint getJavaPaint() {
- return mJavaPaint;
- }
-
- // ---- native methods ----
-
- @LayoutlibDelegate
- /*package*/ static long nativeCreate(long matrix, float x, float y, float radius,
- long[] colors, float[] positions, int tileMode, long colorSpaceHandle) {
- RadialGradient_Delegate newDelegate = new RadialGradient_Delegate(matrix, x, y, radius,
- colors, positions, Shader_Delegate.getTileMode(tileMode));
- return sManager.addNewDelegate(newDelegate);
- }
-
- // ---- Private delegate/helper methods ----
-
- /**
- * Create a shader that draws a radial gradient given the center and radius.
- *
- * @param nativeMatrix reference to the shader's native transformation matrix
- * @param x The x-coordinate of the center of the radius
- * @param y 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 colors to be distributed between the center and edge of
- * the circle
- * @param positions May be NULL. The relative position of each corresponding
- * color in the colors array. If this is NULL, the the colors are
- * distributed evenly between the center and edge of the circle.
- * @param tile The Shader tiling mode
- */
- private RadialGradient_Delegate(long nativeMatrix, float x, float y, float radius,
- long[] colors, float[] positions, TileMode tile) {
- super(nativeMatrix, colors, positions);
- mJavaPaint = new RadialGradientPaint(x, y, radius, mColors, mPositions, tile);
- }
-
- private class RadialGradientPaint extends GradientPaint {
-
- private final float mX;
- private final float mY;
- private final float mRadius;
-
- public RadialGradientPaint(float x, float y, float radius,
- int[] colors, float[] positions, TileMode mode) {
- super(colors, positions, mode);
- mX = x;
- mY = y;
- mRadius = radius;
- }
-
- @Override
- public java.awt.PaintContext createContext(
- java.awt.image.ColorModel colorModel,
- java.awt.Rectangle deviceBounds,
- java.awt.geom.Rectangle2D userBounds,
- java.awt.geom.AffineTransform xform,
- java.awt.RenderingHints hints) {
- precomputeGradientColors();
-
- java.awt.geom.AffineTransform canvasMatrix;
- try {
- canvasMatrix = xform.createInverse();
- } catch (java.awt.geom.NoninvertibleTransformException e) {
- Bridge.getLog().fidelityWarning(ILayoutLog.TAG_MATRIX_INVERSE,
- "Unable to inverse matrix in RadialGradient", e, null, null /*data*/);
- canvasMatrix = new java.awt.geom.AffineTransform();
- }
-
- java.awt.geom.AffineTransform localMatrix = getLocalMatrix();
- try {
- localMatrix = localMatrix.createInverse();
- } catch (java.awt.geom.NoninvertibleTransformException e) {
- Bridge.getLog().fidelityWarning(ILayoutLog.TAG_MATRIX_INVERSE,
- "Unable to inverse matrix in RadialGradient", e, null, null /*data*/);
- localMatrix = new java.awt.geom.AffineTransform();
- }
-
- return new RadialGradientPaintContext(canvasMatrix, localMatrix, colorModel);
- }
-
- private class RadialGradientPaintContext implements java.awt.PaintContext {
-
- private final java.awt.geom.AffineTransform mCanvasMatrix;
- private final java.awt.geom.AffineTransform mLocalMatrix;
- private final java.awt.image.ColorModel mColorModel;
-
- public RadialGradientPaintContext(
- java.awt.geom.AffineTransform canvasMatrix,
- java.awt.geom.AffineTransform localMatrix,
- java.awt.image.ColorModel colorModel) {
- mCanvasMatrix = canvasMatrix;
- mLocalMatrix = localMatrix;
- mColorModel = colorModel.hasAlpha() ? colorModel : ColorModel.getRGBdefault();
- }
-
- @Override
- public void dispose() {
- }
-
- @Override
- public java.awt.image.ColorModel getColorModel() {
- return mColorModel;
- }
-
- @Override
- public java.awt.image.Raster getRaster(int x, int y, int w, int h) {
- int[] data = new int[w*h];
-
- // compute distance from each point to the center, and figure out the distance from
- // it.
- int index = 0;
- float[] pt1 = new float[2];
- float[] pt2 = new float[2];
-
- for (int iy = 0 ; iy < h ; iy++) {
- for (int ix = 0 ; ix < w ; ix++) {
- // handle the canvas transform
- pt1[0] = x + ix;
- pt1[1] = y + iy;
- mCanvasMatrix.transform(pt1, 0, pt2, 0, 1);
-
- // handle the local matrix
- pt1[0] = pt2[0];
- pt1[1] = pt2[1];
- mLocalMatrix.transform(pt1, 0, pt2, 0, 1);
-
- float _x = pt2[0] - mX;
- float _y = pt2[1] - mY;
- float distance = (float) Math.hypot(_x, _y);
-
- data[index++] = getGradientColor(distance / mRadius);
- }
- }
-
- DataBufferInt dataBuffer = new DataBufferInt(data, data.length);
- SampleModel colorModel = mColorModel.createCompatibleSampleModel(w, h);
- return Raster.createWritableRaster(colorModel, dataBuffer, null);
- }
-
- }
- }
-
-}
diff --git a/bridge/src/android/graphics/Region_Delegate.java b/bridge/src/android/graphics/Region_Delegate.java
deleted file mode 100644
index 254e825fee..0000000000
--- a/bridge/src/android/graphics/Region_Delegate.java
+++ /dev/null
@@ -1,483 +0,0 @@
-/*
- * Copyright (C) 2010 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package android.graphics;
-
-import com.android.ide.common.rendering.api.ILayoutLog;
-import com.android.layoutlib.bridge.Bridge;
-import com.android.layoutlib.bridge.impl.DelegateManager;
-import com.android.tools.layoutlib.annotations.LayoutlibDelegate;
-
-import android.os.Parcel;
-
-import java.awt.Rectangle;
-import java.awt.Shape;
-import java.awt.geom.AffineTransform;
-import java.awt.geom.Area;
-import java.awt.geom.Rectangle2D;
-
-/**
- * Delegate implementing the native methods of android.graphics.Region
- *
- * Through the layoutlib_create tool, the original native methods of Region have been replaced
- * by calls to methods of the same name in this delegate class.
- *
- * This class behaves like the original native implementation, but in Java, keeping previously
- * native data into its own objects and mapping them to int that are sent back and forth between
- * it and the original Region class.
- *
- * This also serve as a base class for all Region delegate classes.
- *
- * @see DelegateManager
- *
- */
-public class Region_Delegate {
-
- // ---- delegate manager ----
- protected static final DelegateManager<Region_Delegate> sManager =
- new DelegateManager<Region_Delegate>(Region_Delegate.class);
-
- // ---- delegate helper data ----
-
- // ---- delegate data ----
- private Area mArea = new Area();
-
- // ---- Public Helper methods ----
-
- public static Region_Delegate getDelegate(long nativeShader) {
- return sManager.getDelegate(nativeShader);
- }
-
- public Area getJavaArea() {
- return mArea;
- }
-
- /**
- * Combines two {@link Shape} into another one (actually an {@link Area}), according
- * to the given {@link Region.Op}.
- *
- * If the Op is not one that combines two shapes, then this return null
- *
- * @param shape1 the firt shape to combine which can be null if there's no original clip.
- * @param shape2 the 2nd shape to combine
- * @param regionOp the operande for the combine
- * @return a new area or null.
- */
- public static Area combineShapes(Shape shape1, Shape shape2, int regionOp) {
- if (regionOp == Region.Op.DIFFERENCE.nativeInt) {
- // if shape1 is null (empty), then the result is null.
- if (shape1 == null) {
- return null;
- }
-
- // result is always a new area.
- Area result = new Area(shape1);
- result.subtract(shape2 instanceof Area ? (Area) shape2 : new Area(shape2));
- return result;
-
- } else if (regionOp == Region.Op.INTERSECT.nativeInt) {
- // if shape1 is null, then the result is simply shape2.
- if (shape1 == null) {
- return new Area(shape2);
- }
-
- // result is always a new area.
- Area result = new Area(shape1);
- result.intersect(shape2 instanceof Area ? (Area) shape2 : new Area(shape2));
- return result;
-
- } else if (regionOp == Region.Op.UNION.nativeInt) {
- // if shape1 is null, then the result is simply shape2.
- if (shape1 == null) {
- return new Area(shape2);
- }
-
- // result is always a new area.
- Area result = new Area(shape1);
- result.add(shape2 instanceof Area ? (Area) shape2 : new Area(shape2));
- return result;
-
- } else if (regionOp == Region.Op.XOR.nativeInt) {
- // if shape1 is null, then the result is simply shape2
- if (shape1 == null) {
- return new Area(shape2);
- }
-
- // result is always a new area.
- Area result = new Area(shape1);
- result.exclusiveOr(shape2 instanceof Area ? (Area) shape2 : new Area(shape2));
- return result;
-
- } else if (regionOp == Region.Op.REVERSE_DIFFERENCE.nativeInt) {
- // result is always a new area.
- Area result = new Area(shape2);
-
- if (shape1 != null) {
- result.subtract(shape1 instanceof Area ? (Area) shape1 : new Area(shape1));
- }
-
- return result;
- }
-
- return null;
- }
-
- // ---- native methods ----
-
- @LayoutlibDelegate
- /*package*/ static boolean isEmpty(Region thisRegion) {
- Region_Delegate regionDelegate = sManager.getDelegate(thisRegion.mNativeRegion);
- if (regionDelegate == null) {
- return true;
- }
-
- return regionDelegate.mArea.isEmpty();
- }
-
- @LayoutlibDelegate
- /*package*/ static boolean isRect(Region thisRegion) {
- Region_Delegate regionDelegate = sManager.getDelegate(thisRegion.mNativeRegion);
- if (regionDelegate == null) {
- return true;
- }
-
- return regionDelegate.mArea.isRectangular();
- }
-
- @LayoutlibDelegate
- /*package*/ static boolean isComplex(Region thisRegion) {
- Region_Delegate regionDelegate = sManager.getDelegate(thisRegion.mNativeRegion);
- if (regionDelegate == null) {
- return true;
- }
-
- return regionDelegate.mArea.isSingular() == false;
- }
-
- @LayoutlibDelegate
- /*package*/ static boolean contains(Region thisRegion, int x, int y) {
- Region_Delegate regionDelegate = sManager.getDelegate(thisRegion.mNativeRegion);
- if (regionDelegate == null) {
- return false;
- }
-
- return regionDelegate.mArea.contains(x, y);
- }
-
- @LayoutlibDelegate
- /*package*/ static boolean quickContains(Region thisRegion,
- int left, int top, int right, int bottom) {
- Region_Delegate regionDelegate = sManager.getDelegate(thisRegion.mNativeRegion);
- if (regionDelegate == null) {
- return false;
- }
-
- return regionDelegate.mArea.isRectangular() &&
- regionDelegate.mArea.contains(left, top, right - left, bottom - top);
- }
-
- @LayoutlibDelegate
- /*package*/ static boolean quickReject(Region thisRegion,
- int left, int top, int right, int bottom) {
- Region_Delegate regionDelegate = sManager.getDelegate(thisRegion.mNativeRegion);
- if (regionDelegate == null) {
- return false;
- }
-
- return regionDelegate.mArea.isEmpty() ||
- regionDelegate.mArea.intersects(left, top, right - left, bottom - top) == false;
- }
-
- @LayoutlibDelegate
- /*package*/ static boolean quickReject(Region thisRegion, Region rgn) {
- Region_Delegate regionDelegate = sManager.getDelegate(thisRegion.mNativeRegion);
- if (regionDelegate == null) {
- return false;
- }
-
- Region_Delegate targetRegionDelegate = sManager.getDelegate(rgn.mNativeRegion);
- if (targetRegionDelegate == null) {
- return false;
- }
-
- return regionDelegate.mArea.isEmpty() ||
- regionDelegate.mArea.getBounds().intersects(
- targetRegionDelegate.mArea.getBounds()) == false;
-
- }
-
- @LayoutlibDelegate
- /*package*/ static void translate(Region thisRegion, int dx, int dy, Region dst) {
- Region_Delegate regionDelegate = sManager.getDelegate(thisRegion.mNativeRegion);
- if (regionDelegate == null) {
- return;
- }
-
- Region_Delegate targetRegionDelegate = sManager.getDelegate(dst.mNativeRegion);
- if (targetRegionDelegate == null) {
- return;
- }
-
- if (regionDelegate.mArea.isEmpty()) {
- targetRegionDelegate.mArea = new Area();
- } else {
- targetRegionDelegate.mArea = new Area(regionDelegate.mArea);
- AffineTransform mtx = new AffineTransform();
- mtx.translate(dx, dy);
- targetRegionDelegate.mArea.transform(mtx);
- }
- }
-
- @LayoutlibDelegate
- /*package*/ static void scale(Region thisRegion, float scale, Region dst) {
- Region_Delegate regionDelegate = sManager.getDelegate(thisRegion.mNativeRegion);
- if (regionDelegate == null) {
- return;
- }
-
- Region_Delegate targetRegionDelegate = sManager.getDelegate(dst.mNativeRegion);
- if (targetRegionDelegate == null) {
- return;
- }
-
- if (regionDelegate.mArea.isEmpty()) {
- targetRegionDelegate.mArea = new Area();
- } else {
- targetRegionDelegate.mArea = new Area(regionDelegate.mArea);
- AffineTransform mtx = new AffineTransform();
- mtx.scale(scale, scale);
- targetRegionDelegate.mArea.transform(mtx);
- }
- }
-
- @LayoutlibDelegate
- /*package*/ static long nativeConstructor() {
- Region_Delegate newDelegate = new Region_Delegate();
- return sManager.addNewDelegate(newDelegate);
- }
-
- @LayoutlibDelegate
- /*package*/ static void nativeDestructor(long native_region) {
- sManager.removeJavaReferenceFor(native_region);
- }
-
- @LayoutlibDelegate
- /*package*/ static void nativeSetRegion(long native_dst, long native_src) {
- Region_Delegate dstRegion = sManager.getDelegate(native_dst);
- if (dstRegion == null) {
- return;
- }
-
- Region_Delegate srcRegion = sManager.getDelegate(native_src);
- if (srcRegion == null) {
- return;
- }
-
- dstRegion.mArea.reset();
- dstRegion.mArea.add(srcRegion.mArea);
-
- }
-
- @LayoutlibDelegate
- /*package*/ static boolean nativeSetRect(long native_dst,
- int left, int top, int right, int bottom) {
- Region_Delegate dstRegion = sManager.getDelegate(native_dst);
- if (dstRegion == null) {
- return true;
- }
-
- dstRegion.mArea = new Area(new Rectangle2D.Float(left, top, right - left, bottom - top));
- return dstRegion.mArea.getBounds().isEmpty() == false;
- }
-
- @LayoutlibDelegate
- /*package*/ static boolean nativeSetPath(long native_dst, long native_path, long native_clip) {
- Region_Delegate dstRegion = sManager.getDelegate(native_dst);
- if (dstRegion == null) {
- return true;
- }
-
- Path_Delegate path = Path_Delegate.getDelegate(native_path);
- if (path == null) {
- return true;
- }
-
- dstRegion.mArea = new Area(path.getJavaShape());
-
- Region_Delegate clip = sManager.getDelegate(native_clip);
- if (clip != null) {
- dstRegion.mArea.subtract(clip.getJavaArea());
- }
-
- return dstRegion.mArea.getBounds().isEmpty() == false;
- }
-
- @LayoutlibDelegate
- /*package*/ static boolean nativeGetBounds(long native_region, Rect rect) {
- Region_Delegate region = sManager.getDelegate(native_region);
- if (region == null) {
- return true;
- }
-
- Rectangle bounds = region.mArea.getBounds();
- if (bounds.isEmpty()) {
- rect.left = rect.top = rect.right = rect.bottom = 0;
- return false;
- }
-
- rect.left = bounds.x;
- rect.top = bounds.y;
- rect.right = bounds.x + bounds.width;
- rect.bottom = bounds.y + bounds.height;
- return true;
- }
-
- @LayoutlibDelegate
- /*package*/ static boolean nativeGetBoundaryPath(long native_region, long native_path) {
- Region_Delegate region = sManager.getDelegate(native_region);
- if (region == null) {
- return false;
- }
-
- Path_Delegate path = Path_Delegate.getDelegate(native_path);
- if (path == null) {
- return false;
- }
-
- if (region.mArea.isEmpty()) {
- path.reset();
- return false;
- }
-
- path.setPathIterator(region.mArea.getPathIterator(new AffineTransform()));
- return true;
- }
-
- @LayoutlibDelegate
- /*package*/ static boolean nativeOp(long native_dst,
- int left, int top, int right, int bottom, int op) {
- Region_Delegate region = sManager.getDelegate(native_dst);
- if (region == null) {
- return false;
- }
-
- region.mArea = combineShapes(region.mArea,
- new Rectangle2D.Float(left, top, right - left, bottom - top), op);
-
- assert region.mArea != null;
- if (region.mArea != null) {
- region.mArea = new Area();
- }
-
- return region.mArea.getBounds().isEmpty() == false;
- }
-
- @LayoutlibDelegate
- /*package*/ static boolean nativeOp(long native_dst, Rect rect, long native_region, int op) {
- Region_Delegate region = sManager.getDelegate(native_dst);
- if (region == null) {
- return false;
- }
-
- region.mArea = combineShapes(region.mArea,
- new Rectangle2D.Float(rect.left, rect.top, rect.width(), rect.height()), op);
-
- assert region.mArea != null;
- if (region.mArea != null) {
- region.mArea = new Area();
- }
-
- return region.mArea.getBounds().isEmpty() == false;
- }
-
- @LayoutlibDelegate
- /*package*/ static boolean nativeOp(long native_dst,
- long native_region1, long native_region2, int op) {
- Region_Delegate dstRegion = sManager.getDelegate(native_dst);
- if (dstRegion == null) {
- return true;
- }
-
- Region_Delegate region1 = sManager.getDelegate(native_region1);
- if (region1 == null) {
- return false;
- }
-
- Region_Delegate region2 = sManager.getDelegate(native_region2);
- if (region2 == null) {
- return false;
- }
-
- dstRegion.mArea = combineShapes(region1.mArea, region2.mArea, op);
-
- assert dstRegion.mArea != null;
- if (dstRegion.mArea != null) {
- dstRegion.mArea = new Area();
- }
-
- return dstRegion.mArea.getBounds().isEmpty() == false;
-
- }
-
- @LayoutlibDelegate
- /*package*/ static long nativeCreateFromParcel(Parcel p) {
- // This is only called by Region.CREATOR (Parcelable.Creator<Region>), which is only
- // used during aidl call so really this should not be called.
- Bridge.getLog().error(ILayoutLog.TAG_UNSUPPORTED,
- "AIDL is not suppored, and therefore Regions cannot be created from parcels.",
- null, null /*data*/);
- return 0;
- }
-
- @LayoutlibDelegate
- /*package*/ static boolean nativeWriteToParcel(long native_region,
- Parcel p) {
- // This is only called when sending a region through aidl, so really this should not
- // be called.
- Bridge.getLog().error(ILayoutLog.TAG_UNSUPPORTED,
- "AIDL is not suppored, and therefore Regions cannot be written to parcels.",
- null, null /*data*/);
- return false;
- }
-
- @LayoutlibDelegate
- /*package*/ static boolean nativeEquals(long native_r1, long native_r2) {
- Region_Delegate region1 = sManager.getDelegate(native_r1);
- if (region1 == null) {
- return false;
- }
-
- Region_Delegate region2 = sManager.getDelegate(native_r2);
- if (region2 == null) {
- return false;
- }
-
- return region1.mArea.equals(region2.mArea);
- }
-
- @LayoutlibDelegate
- /*package*/ static String nativeToString(long native_region) {
- Region_Delegate region = sManager.getDelegate(native_region);
- if (region == null) {
- return "not found";
- }
-
- return region.mArea.toString();
- }
-
- // ---- Private delegate/helper methods ----
-
-}
diff --git a/bridge/src/android/graphics/RenderNode_Delegate.java b/bridge/src/android/graphics/RenderNode_Delegate.java
deleted file mode 100644
index ae08b00e37..0000000000
--- a/bridge/src/android/graphics/RenderNode_Delegate.java
+++ /dev/null
@@ -1,333 +0,0 @@
-/*
- * Copyright (C) 2014 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.graphics;
-
-import com.android.layoutlib.bridge.impl.DelegateManager;
-import com.android.tools.layoutlib.annotations.LayoutlibDelegate;
-
-import libcore.util.NativeAllocationRegistry_Delegate;
-
-/**
- * Delegate implementing the native methods of {@link RenderNode}
- * <p/>
- * Through the layoutlib_create tool, some native methods of RenderNode have been replaced by calls
- * to methods of the same name in this delegate class.
- *
- * @see DelegateManager
- */
-public class RenderNode_Delegate {
-
-
- // ---- delegate manager ----
- private static final DelegateManager<RenderNode_Delegate> sManager =
- new DelegateManager<RenderNode_Delegate>(RenderNode_Delegate.class);
- private static long sFinalizer = -1;
-
- private float mLift;
- private float mTranslationX;
- private float mTranslationY;
- private float mTranslationZ;
- private float mRotation;
- private float mScaleX = 1;
- private float mScaleY = 1;
- private float mPivotX;
- private float mPivotY;
- private boolean mPivotExplicitlySet;
- private int mLeft;
- private int mRight;
- private int mTop;
- private int mBottom;
- @SuppressWarnings("UnusedDeclaration")
- private String mName;
-
- @LayoutlibDelegate
- /*package*/ static long nCreate(String name) {
- RenderNode_Delegate renderNodeDelegate = new RenderNode_Delegate();
- renderNodeDelegate.mName = name;
- return sManager.addNewDelegate(renderNodeDelegate);
- }
-
- @LayoutlibDelegate
- /*package*/ static long nGetNativeFinalizer() {
- synchronized (RenderNode_Delegate.class) {
- if (sFinalizer == -1) {
- sFinalizer = NativeAllocationRegistry_Delegate.createFinalizer(sManager::removeJavaReferenceFor);
- }
- }
- return sFinalizer;
- }
-
- @LayoutlibDelegate
- /*package*/ static boolean nSetElevation(long renderNode, float lift) {
- RenderNode_Delegate delegate = sManager.getDelegate(renderNode);
- if (delegate != null && delegate.mLift != lift) {
- delegate.mLift = lift;
- return true;
- }
- return false;
- }
-
- @LayoutlibDelegate
- /*package*/ static float nGetElevation(long renderNode) {
- RenderNode_Delegate delegate = sManager.getDelegate(renderNode);
- if (delegate != null) {
- return delegate.mLift;
- }
- return 0f;
- }
-
- @LayoutlibDelegate
- /*package*/ static boolean nSetTranslationX(long renderNode, float translationX) {
- RenderNode_Delegate delegate = sManager.getDelegate(renderNode);
- if (delegate != null && delegate.mTranslationX != translationX) {
- delegate.mTranslationX = translationX;
- return true;
- }
- return false;
- }
-
- @LayoutlibDelegate
- /*package*/ static float nGetTranslationX(long renderNode) {
- RenderNode_Delegate delegate = sManager.getDelegate(renderNode);
- if (delegate != null) {
- return delegate.mTranslationX;
- }
- return 0f;
- }
-
- @LayoutlibDelegate
- /*package*/ static boolean nSetTranslationY(long renderNode, float translationY) {
- RenderNode_Delegate delegate = sManager.getDelegate(renderNode);
- if (delegate != null && delegate.mTranslationY != translationY) {
- delegate.mTranslationY = translationY;
- return true;
- }
- return false;
- }
-
- @LayoutlibDelegate
- /*package*/ static float nGetTranslationY(long renderNode) {
- RenderNode_Delegate delegate = sManager.getDelegate(renderNode);
- if (delegate != null) {
- return delegate.mTranslationY;
- }
- return 0f;
- }
-
- @LayoutlibDelegate
- /*package*/ static boolean nSetTranslationZ(long renderNode, float translationZ) {
- RenderNode_Delegate delegate = sManager.getDelegate(renderNode);
- if (delegate != null && delegate.mTranslationZ != translationZ) {
- delegate.mTranslationZ = translationZ;
- return true;
- }
- return false;
- }
-
- @LayoutlibDelegate
- /*package*/ static float nGetTranslationZ(long renderNode) {
- RenderNode_Delegate delegate = sManager.getDelegate(renderNode);
- if (delegate != null) {
- return delegate.mTranslationZ;
- }
- return 0f;
- }
-
- @LayoutlibDelegate
- /*package*/ static boolean nSetRotation(long renderNode, float rotation) {
- RenderNode_Delegate delegate = sManager.getDelegate(renderNode);
- if (delegate != null && delegate.mRotation != rotation) {
- delegate.mRotation = rotation;
- return true;
- }
- return false;
- }
-
- @LayoutlibDelegate
- /*package*/ static float nGetRotation(long renderNode) {
- RenderNode_Delegate delegate = sManager.getDelegate(renderNode);
- if (delegate != null) {
- return delegate.mRotation;
- }
- return 0f;
- }
-
- @LayoutlibDelegate
- /*package*/ static void getMatrix(RenderNode renderNode, Matrix outMatrix) {
- outMatrix.reset();
- if (renderNode != null) {
- float rotation = renderNode.getRotationZ();
- float translationX = renderNode.getTranslationX();
- float translationY = renderNode.getTranslationY();
- float pivotX = renderNode.getPivotX();
- float pivotY = renderNode.getPivotY();
- float scaleX = renderNode.getScaleX();
- float scaleY = renderNode.getScaleY();
-
- outMatrix.setTranslate(translationX, translationY);
- outMatrix.preRotate(rotation, pivotX, pivotY);
- outMatrix.preScale(scaleX, scaleY, pivotX, pivotY);
- }
- }
-
- @LayoutlibDelegate
- /*package*/ static boolean nSetLeft(long renderNode, int left) {
- RenderNode_Delegate delegate = sManager.getDelegate(renderNode);
- if (delegate != null && delegate.mLeft != left) {
- delegate.mLeft = left;
- return true;
- }
- return false;
- }
-
- @LayoutlibDelegate
- /*package*/ static boolean nSetTop(long renderNode, int top) {
- RenderNode_Delegate delegate = sManager.getDelegate(renderNode);
- if (delegate != null && delegate.mTop != top) {
- delegate.mTop = top;
- return true;
- }
- return false;
- }
-
- @LayoutlibDelegate
- /*package*/ static boolean nSetRight(long renderNode, int right) {
- RenderNode_Delegate delegate = sManager.getDelegate(renderNode);
- if (delegate != null && delegate.mRight != right) {
- delegate.mRight = right;
- return true;
- }
- return false;
- }
-
- @LayoutlibDelegate
- /*package*/ static boolean nSetBottom(long renderNode, int bottom) {
- RenderNode_Delegate delegate = sManager.getDelegate(renderNode);
- if (delegate != null && delegate.mBottom != bottom) {
- delegate.mBottom = bottom;
- return true;
- }
- return false;
- }
-
- @LayoutlibDelegate
- /*package*/ static boolean nSetLeftTopRightBottom(long renderNode, int left, int top, int right,
- int bottom) {
- RenderNode_Delegate delegate = sManager.getDelegate(renderNode);
- if (delegate != null && (delegate.mLeft != left || delegate.mTop != top || delegate
- .mRight != right || delegate.mBottom != bottom)) {
- delegate.mLeft = left;
- delegate.mTop = top;
- delegate.mRight = right;
- delegate.mBottom = bottom;
- return true;
- }
- return false;
- }
-
- @LayoutlibDelegate
- /*package*/ static boolean nIsPivotExplicitlySet(long renderNode) {
- RenderNode_Delegate delegate = sManager.getDelegate(renderNode);
- return delegate != null && delegate.mPivotExplicitlySet;
- }
-
- @LayoutlibDelegate
- /*package*/ static boolean nSetPivotX(long renderNode, float pivotX) {
- RenderNode_Delegate delegate = sManager.getDelegate(renderNode);
- if (delegate != null) {
- delegate.mPivotX = pivotX;
- delegate.mPivotExplicitlySet = true;
- return true;
- }
- return false;
- }
-
- @LayoutlibDelegate
- /*package*/ static float nGetPivotX(long renderNode) {
- RenderNode_Delegate delegate = sManager.getDelegate(renderNode);
- if (delegate != null) {
- if (delegate.mPivotExplicitlySet) {
- return delegate.mPivotX;
- } else {
- return (delegate.mRight - delegate.mLeft) / 2.0f;
- }
- }
- return 0f;
- }
-
- @LayoutlibDelegate
- /*package*/ static boolean nSetPivotY(long renderNode, float pivotY) {
- RenderNode_Delegate delegate = sManager.getDelegate(renderNode);
- if (delegate != null) {
- delegate.mPivotY = pivotY;
- delegate.mPivotExplicitlySet = true;
- return true;
- }
- return false;
- }
-
- @LayoutlibDelegate
- /*package*/ static float nGetPivotY(long renderNode) {
- RenderNode_Delegate delegate = sManager.getDelegate(renderNode);
- if (delegate != null) {
- if (delegate.mPivotExplicitlySet) {
- return delegate.mPivotY;
- } else {
- return (delegate.mBottom - delegate.mTop) / 2.0f;
- }
- }
- return 0f;
- }
-
- @LayoutlibDelegate
- /*package*/ static boolean nSetScaleX(long renderNode, float scaleX) {
- RenderNode_Delegate delegate = sManager.getDelegate(renderNode);
- if (delegate != null && delegate.mScaleX != scaleX) {
- delegate.mScaleX = scaleX;
- return true;
- }
- return false;
- }
-
- @LayoutlibDelegate
- /*package*/ static float nGetScaleX(long renderNode) {
- RenderNode_Delegate delegate = sManager.getDelegate(renderNode);
- if (delegate != null) {
- return delegate.mScaleX;
- }
- return 0f;
- }
-
- @LayoutlibDelegate
- /*package*/ static boolean nSetScaleY(long renderNode, float scaleY) {
- RenderNode_Delegate delegate = sManager.getDelegate(renderNode);
- if (delegate != null && delegate.mScaleY != scaleY) {
- delegate.mScaleY = scaleY;
- return true;
- }
- return false;
- }
-
- @LayoutlibDelegate
- /*package*/ static float nGetScaleY(long renderNode) {
- RenderNode_Delegate delegate = sManager.getDelegate(renderNode);
- if (delegate != null) {
- return delegate.mScaleY;
- }
- return 0f;
- }
-}
diff --git a/bridge/src/android/graphics/RoundRectangle.java b/bridge/src/android/graphics/RoundRectangle.java
deleted file mode 100644
index 736f03ec5a..0000000000
--- a/bridge/src/android/graphics/RoundRectangle.java
+++ /dev/null
@@ -1,368 +0,0 @@
-/*
- * Copyright (C) 2016 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package android.graphics;
-
-import java.awt.geom.AffineTransform;
-import java.awt.geom.PathIterator;
-import java.awt.geom.Rectangle2D;
-import java.awt.geom.RectangularShape;
-import java.awt.geom.RoundRectangle2D;
-import java.util.EnumSet;
-import java.util.NoSuchElementException;
-
-/**
- * Defines a rectangle with rounded corners, where the sizes of the corners
- * are potentially different.
- */
-public class RoundRectangle extends RectangularShape {
- public double x;
- public double y;
- public double width;
- public double height;
- public double ulWidth;
- public double ulHeight;
- public double urWidth;
- public double urHeight;
- public double lrWidth;
- public double lrHeight;
- public double llWidth;
- public double llHeight;
-
- private enum Zone {
- CLOSE_OUTSIDE,
- CLOSE_INSIDE,
- MIDDLE,
- FAR_INSIDE,
- FAR_OUTSIDE
- }
-
- private final EnumSet<Zone> close = EnumSet.of(Zone.CLOSE_OUTSIDE, Zone.CLOSE_INSIDE);
- private final EnumSet<Zone> far = EnumSet.of(Zone.FAR_OUTSIDE, Zone.FAR_INSIDE);
-
- /**
- * @param cornerDimensions array of 8 floating-point number corresponding to the width and
- * the height of each corner in the following order: upper-left, upper-right, lower-right,
- * lower-left. It assumes for the size the same convention as {@link RoundRectangle2D}, that
- * is that the width and height of a corner correspond to the total width and height of the
- * ellipse that corner is a quarter of.
- */
- public RoundRectangle(float x, float y, float width, float height, float[] cornerDimensions) {
- assert cornerDimensions.length == 8 : "The array of corner dimensions must have eight " +
- "elements";
-
- this.x = x;
- this.y = y;
- this.width = width;
- this.height = height;
-
- float[] dimensions = cornerDimensions.clone();
- // If a value is negative, the corresponding corner is squared
- for (int i = 0; i < dimensions.length; i += 2) {
- if (dimensions[i] < 0 || dimensions[i + 1] < 0) {
- dimensions[i] = 0;
- dimensions[i + 1] = 0;
- }
- }
-
- double topCornerWidth = (dimensions[0] + dimensions[2]) / 2d;
- double bottomCornerWidth = (dimensions[4] + dimensions[6]) / 2d;
- double leftCornerHeight = (dimensions[1] + dimensions[7]) / 2d;
- double rightCornerHeight = (dimensions[3] + dimensions[5]) / 2d;
-
- // Rescale the corner dimensions if they are bigger than the rectangle
- double scale = Math.min(1.0, width / topCornerWidth);
- scale = Math.min(scale, width / bottomCornerWidth);
- scale = Math.min(scale, height / leftCornerHeight);
- scale = Math.min(scale, height / rightCornerHeight);
-
- this.ulWidth = dimensions[0] * scale;
- this.ulHeight = dimensions[1] * scale;
- this.urWidth = dimensions[2] * scale;
- this.urHeight = dimensions[3] * scale;
- this.lrWidth = dimensions[4] * scale;
- this.lrHeight = dimensions[5] * scale;
- this.llWidth = dimensions[6] * scale;
- this.llHeight = dimensions[7] * scale;
- }
-
- @Override
- public double getX() {
- return x;
- }
-
- @Override
- public double getY() {
- return y;
- }
-
- @Override
- public double getWidth() {
- return width;
- }
-
- @Override
- public double getHeight() {
- return height;
- }
-
- @Override
- public boolean isEmpty() {
- return (width <= 0d) || (height <= 0d);
- }
-
- @Override
- public void setFrame(double x, double y, double w, double h) {
- this.x = x;
- this.y = y;
- this.width = w;
- this.height = h;
- }
-
- @Override
- public Rectangle2D getBounds2D() {
- return new Rectangle2D.Double(x, y, width, height);
- }
-
- @Override
- public boolean contains(double x, double y) {
- if (isEmpty()) {
- return false;
- }
-
- double x0 = getX();
- double y0 = getY();
- double x1 = x0 + getWidth();
- double y1 = y0 + getHeight();
- // Check for trivial rejection - point is outside bounding rectangle
- if (x < x0 || y < y0 || x >= x1 || y >= y1) {
- return false;
- }
-
- double insideTopX0 = x0 + ulWidth / 2d;
- double insideLeftY0 = y0 + ulHeight / 2d;
- if (x < insideTopX0 && y < insideLeftY0) {
- // In the upper-left corner
- return isInsideCorner(x - insideTopX0, y - insideLeftY0, ulWidth / 2d, ulHeight / 2d);
- }
-
- double insideTopX1 = x1 - urWidth / 2d;
- double insideRightY0 = y0 + urHeight / 2d;
- if (x > insideTopX1 && y < insideRightY0) {
- // In the upper-right corner
- return isInsideCorner(x - insideTopX1, y - insideRightY0, urWidth / 2d, urHeight / 2d);
- }
-
- double insideBottomX1 = x1 - lrWidth / 2d;
- double insideRightY1 = y1 - lrHeight / 2d;
- if (x > insideBottomX1 && y > insideRightY1) {
- // In the lower-right corner
- return isInsideCorner(x - insideBottomX1, y - insideRightY1, lrWidth / 2d,
- lrHeight / 2d);
- }
-
- double insideBottomX0 = x0 + llWidth / 2d;
- double insideLeftY1 = y1 - llHeight / 2d;
- if (x < insideBottomX0 && y > insideLeftY1) {
- // In the lower-left corner
- return isInsideCorner(x - insideBottomX0, y - insideLeftY1, llWidth / 2d,
- llHeight / 2d);
- }
-
- // In the central part of the rectangle
- return true;
- }
-
- private boolean isInsideCorner(double x, double y, double width, double height) {
- double squareDist = height * height * x * x + width * width * y * y;
- return squareDist <= width * width * height * height;
- }
-
- private Zone classify(double coord, double side1, double arcSize1, double side2,
- double arcSize2) {
- if (coord < side1) {
- return Zone.CLOSE_OUTSIDE;
- } else if (coord < side1 + arcSize1) {
- return Zone.CLOSE_INSIDE;
- } else if (coord < side2 - arcSize2) {
- return Zone.MIDDLE;
- } else if (coord < side2) {
- return Zone.FAR_INSIDE;
- } else {
- return Zone.FAR_OUTSIDE;
- }
- }
-
- public boolean intersects(double x, double y, double w, double h) {
- if (isEmpty() || w <= 0 || h <= 0) {
- return false;
- }
- double x0 = getX();
- double y0 = getY();
- double x1 = x0 + getWidth();
- double y1 = y0 + getHeight();
- // Check for trivial rejection - bounding rectangles do not intersect
- if (x + w <= x0 || x >= x1 || y + h <= y0 || y >= y1) {
- return false;
- }
-
- double maxLeftCornerWidth = Math.max(ulWidth, llWidth) / 2d;
- double maxRightCornerWidth = Math.max(urWidth, lrWidth) / 2d;
- double maxUpperCornerHeight = Math.max(ulHeight, urHeight) / 2d;
- double maxLowerCornerHeight = Math.max(llHeight, lrHeight) / 2d;
- Zone x0class = classify(x, x0, maxLeftCornerWidth, x1, maxRightCornerWidth);
- Zone x1class = classify(x + w, x0, maxLeftCornerWidth, x1, maxRightCornerWidth);
- Zone y0class = classify(y, y0, maxUpperCornerHeight, y1, maxLowerCornerHeight);
- Zone y1class = classify(y + h, y0, maxUpperCornerHeight, y1, maxLowerCornerHeight);
-
- // Trivially accept if any point is inside inner rectangle
- if (x0class == Zone.MIDDLE || x1class == Zone.MIDDLE || y0class == Zone.MIDDLE || y1class == Zone.MIDDLE) {
- return true;
- }
- // Trivially accept if either edge spans inner rectangle
- if ((close.contains(x0class) && far.contains(x1class)) || (close.contains(y0class) &&
- far.contains(y1class))) {
- return true;
- }
-
- // Since neither edge spans the center, then one of the corners
- // must be in one of the rounded edges. We detect this case if
- // a [xy]0class is 3 or a [xy]1class is 1. One of those two cases
- // must be true for each direction.
- // We now find a "nearest point" to test for being inside a rounded
- // corner.
- if (x1class == Zone.CLOSE_INSIDE && y1class == Zone.CLOSE_INSIDE) {
- // Potentially in upper-left corner
- x = x + w - x0 - ulWidth / 2d;
- y = y + h - y0 - ulHeight / 2d;
- return x > 0 || y > 0 || isInsideCorner(x, y, ulWidth / 2d, ulHeight / 2d);
- }
- if (x1class == Zone.CLOSE_INSIDE) {
- // Potentially in lower-left corner
- x = x + w - x0 - llWidth / 2d;
- y = y - y1 + llHeight / 2d;
- return x > 0 || y < 0 || isInsideCorner(x, y, llWidth / 2d, llHeight / 2d);
- }
- if (y1class == Zone.CLOSE_INSIDE) {
- //Potentially in the upper-right corner
- x = x - x1 + urWidth / 2d;
- y = y + h - y0 - urHeight / 2d;
- return x < 0 || y > 0 || isInsideCorner(x, y, urWidth / 2d, urHeight / 2d);
- }
- // Potentially in the lower-right corner
- x = x - x1 + lrWidth / 2d;
- y = y - y1 + lrHeight / 2d;
- return x < 0 || y < 0 || isInsideCorner(x, y, lrWidth / 2d, lrHeight / 2d);
- }
-
- @Override
- public boolean contains(double x, double y, double w, double h) {
- if (isEmpty() || w <= 0 || h <= 0) {
- return false;
- }
- return (contains(x, y) &&
- contains(x + w, y) &&
- contains(x, y + h) &&
- contains(x + w, y + h));
- }
-
- @Override
- public PathIterator getPathIterator(final AffineTransform at) {
- return new PathIterator() {
- int index;
-
- // ArcIterator.btan(Math.PI/2)
- public static final double CtrlVal = 0.5522847498307933;
- private final double ncv = 1.0 - CtrlVal;
-
- // Coordinates of control points for Bezier curves approximating the straight lines
- // and corners of the rounded rectangle.
- private final double[][] ctrlpts = {
- {0.0, 0.0, 0.0, ulHeight},
- {0.0, 0.0, 1.0, -llHeight},
- {0.0, 0.0, 1.0, -llHeight * ncv, 0.0, ncv * llWidth, 1.0, 0.0, 0.0, llWidth,
- 1.0, 0.0},
- {1.0, -lrWidth, 1.0, 0.0},
- {1.0, -lrWidth * ncv, 1.0, 0.0, 1.0, 0.0, 1.0, -lrHeight * ncv, 1.0, 0.0, 1.0,
- -lrHeight},
- {1.0, 0.0, 0.0, urHeight},
- {1.0, 0.0, 0.0, ncv * urHeight, 1.0, -urWidth * ncv, 0.0, 0.0, 1.0, -urWidth,
- 0.0, 0.0},
- {0.0, ulWidth, 0.0, 0.0},
- {0.0, ncv * ulWidth, 0.0, 0.0, 0.0, 0.0, 0.0, ncv * ulHeight, 0.0, 0.0, 0.0,
- ulHeight},
- {}
- };
- private final int[] types = {
- SEG_MOVETO,
- SEG_LINETO, SEG_CUBICTO,
- SEG_LINETO, SEG_CUBICTO,
- SEG_LINETO, SEG_CUBICTO,
- SEG_LINETO, SEG_CUBICTO,
- SEG_CLOSE,
- };
-
- @Override
- public int getWindingRule() {
- return WIND_NON_ZERO;
- }
-
- @Override
- public boolean isDone() {
- return index >= ctrlpts.length;
- }
-
- @Override
- public void next() {
- index++;
- }
-
- @Override
- public int currentSegment(float[] coords) {
- if (isDone()) {
- throw new NoSuchElementException("roundrect iterator out of bounds");
- }
- int nc = 0;
- double ctrls[] = ctrlpts[index];
- for (int i = 0; i < ctrls.length; i += 4) {
- coords[nc++] = (float) (x + ctrls[i] * width + ctrls[i + 1] / 2d);
- coords[nc++] = (float) (y + ctrls[i + 2] * height + ctrls[i + 3] / 2d);
- }
- if (at != null) {
- at.transform(coords, 0, coords, 0, nc / 2);
- }
- return types[index];
- }
-
- @Override
- public int currentSegment(double[] coords) {
- if (isDone()) {
- throw new NoSuchElementException("roundrect iterator out of bounds");
- }
- int nc = 0;
- double ctrls[] = ctrlpts[index];
- for (int i = 0; i < ctrls.length; i += 4) {
- coords[nc++] = x + ctrls[i] * width + ctrls[i + 1] / 2d;
- coords[nc++] = y + ctrls[i + 2] * height + ctrls[i + 3] / 2d;
- }
- if (at != null) {
- at.transform(coords, 0, coords, 0, nc / 2);
- }
- return types[index];
- }
- };
- }
-}
diff --git a/bridge/src/android/graphics/Shader_Delegate.java b/bridge/src/android/graphics/Shader_Delegate.java
deleted file mode 100644
index d606e2d30e..0000000000
--- a/bridge/src/android/graphics/Shader_Delegate.java
+++ /dev/null
@@ -1,118 +0,0 @@
-/*
- * Copyright (C) 2010 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package android.graphics;
-
-import com.android.layoutlib.bridge.impl.DelegateManager;
-import com.android.tools.layoutlib.annotations.LayoutlibDelegate;
-
-import android.graphics.Shader.TileMode;
-
-import libcore.util.NativeAllocationRegistry_Delegate;
-
-/**
- * Delegate implementing the native methods of android.graphics.Shader
- *
- * Through the layoutlib_create tool, the original native methods of Shader have been replaced
- * by calls to methods of the same name in this delegate class.
- *
- * This class behaves like the original native implementation, but in Java, keeping previously
- * native data into its own objects and mapping them to int that are sent back and forth between
- * it and the original Shader class.
- *
- * This also serve as a base class for all Shader delegate classes.
- *
- * @see DelegateManager
- *
- */
-public abstract class Shader_Delegate {
-
- // ---- delegate manager ----
- protected static final DelegateManager<Shader_Delegate> sManager =
- new DelegateManager<Shader_Delegate>(Shader_Delegate.class);
- private static long sFinalizer = -1;
-
- // ---- delegate helper data ----
-
- // ---- delegate data ----
- private Matrix_Delegate mLocalMatrix = null;
- private float mAlpha = 1.0f;
-
- // ---- Public Helper methods ----
-
- public static Shader_Delegate getDelegate(long nativeShader) {
- return sManager.getDelegate(nativeShader);
- }
-
- /**
- * Returns the {@link TileMode} matching the given int.
- * @param tileMode the tile mode int value
- * @return the TileMode enum.
- */
- public static TileMode getTileMode(int tileMode) {
- for (TileMode tm : TileMode.values()) {
- if (tm.nativeInt == tileMode) {
- return tm;
- }
- }
-
- assert false;
- return TileMode.CLAMP;
- }
-
- public abstract java.awt.Paint getJavaPaint();
- public abstract boolean isSupported();
- public abstract String getSupportMessage();
-
- // ---- native methods ----
-
- @LayoutlibDelegate
- /*package*/ static long nativeGetFinalizer() {
- synchronized (Shader_Delegate.class) {
- if (sFinalizer == -1) {
- sFinalizer = NativeAllocationRegistry_Delegate.createFinalizer(
- sManager::removeJavaReferenceFor);
- }
- }
- return sFinalizer;
- }
-
- // ---- Private delegate/helper methods ----
-
- protected Shader_Delegate(long nativeMatrix) {
- setLocalMatrix(nativeMatrix);
- }
-
- public void setLocalMatrix(long nativeMatrix) {
- mLocalMatrix = Matrix_Delegate.getDelegate(nativeMatrix);
- }
-
- protected java.awt.geom.AffineTransform getLocalMatrix() {
- if (mLocalMatrix != null) {
- return mLocalMatrix.getAffineTransform();
- }
-
- return new java.awt.geom.AffineTransform();
- }
-
- public void setAlpha(float alpha) {
- mAlpha = alpha;
- }
-
- public float getAlpha() {
- return mAlpha;
- }
-}
diff --git a/bridge/src/android/graphics/SumPathEffect_Delegate.java b/bridge/src/android/graphics/SumPathEffect_Delegate.java
deleted file mode 100644
index 6d2e9b41cc..0000000000
--- a/bridge/src/android/graphics/SumPathEffect_Delegate.java
+++ /dev/null
@@ -1,71 +0,0 @@
-/*
- * Copyright (C) 2010 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package android.graphics;
-
-import com.android.layoutlib.bridge.impl.DelegateManager;
-import com.android.tools.layoutlib.annotations.LayoutlibDelegate;
-
-import java.awt.Stroke;
-
-/**
- * Delegate implementing the native methods of android.graphics.SumPathEffect
- *
- * Through the layoutlib_create tool, the original native methods of SumPathEffect have been
- * replaced by calls to methods of the same name in this delegate class.
- *
- * This class behaves like the original native implementation, but in Java, keeping previously
- * native data into its own objects and mapping them to int that are sent back and forth between
- * it and the original SumPathEffect class.
- *
- * Because this extends {@link PathEffect_Delegate}, there's no need to use a {@link DelegateManager},
- * as all the Shader classes will be added to the manager owned by {@link PathEffect_Delegate}.
- *
- * @see PathEffect_Delegate
- *
- */
-public class SumPathEffect_Delegate extends PathEffect_Delegate {
-
- // ---- delegate data ----
-
- // ---- Public Helper methods ----
-
- @Override
- public Stroke getStroke(Paint_Delegate paint) {
- // FIXME
- return null;
- }
-
- @Override
- public boolean isSupported() {
- return false;
- }
-
- @Override
- public String getSupportMessage() {
- return "Sum Path Effects are not supported in Layout Preview mode.";
- }
-
- // ---- native methods ----
-
- @LayoutlibDelegate
- /*package*/ static long nativeCreate(long first, long second) {
- SumPathEffect_Delegate newDelegate = new SumPathEffect_Delegate();
- return sManager.addNewDelegate(newDelegate);
- }
-
- // ---- Private delegate/helper methods ----
-}
diff --git a/bridge/src/android/graphics/SweepGradient_Delegate.java b/bridge/src/android/graphics/SweepGradient_Delegate.java
deleted file mode 100644
index 01ef14073c..0000000000
--- a/bridge/src/android/graphics/SweepGradient_Delegate.java
+++ /dev/null
@@ -1,208 +0,0 @@
-/*
- * Copyright (C) 2010 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package android.graphics;
-
-import com.android.ide.common.rendering.api.ILayoutLog;
-import com.android.layoutlib.bridge.Bridge;
-import com.android.layoutlib.bridge.impl.DelegateManager;
-import com.android.tools.layoutlib.annotations.LayoutlibDelegate;
-
-import java.awt.image.DataBufferInt;
-import java.awt.image.Raster;
-import java.awt.image.SampleModel;
-
-/**
- * Delegate implementing the native methods of android.graphics.SweepGradient
- *
- * Through the layoutlib_create tool, the original native methods of SweepGradient have been
- * replaced by calls to methods of the same name in this delegate class.
- *
- * This class behaves like the original native implementation, but in Java, keeping previously
- * native data into its own objects and mapping them to int that are sent back and forth between
- * it and the original SweepGradient class.
- *
- * Because this extends {@link Shader_Delegate}, there's no need to use a {@link DelegateManager},
- * as all the Shader classes will be added to the manager owned by {@link Shader_Delegate}.
- *
- * @see Shader_Delegate
- *
- */
-public class SweepGradient_Delegate extends Gradient_Delegate {
-
- // ---- delegate data ----
- private java.awt.Paint mJavaPaint;
-
- // ---- Public Helper methods ----
-
- @Override
- public java.awt.Paint getJavaPaint() {
- return mJavaPaint;
- }
-
- // ---- native methods ----
-
- @LayoutlibDelegate
- /*package*/ static long nativeCreate(long matrix, float x, float y, long[] colors,
- float[] positions, long colorSpaceHandle) {
- SweepGradient_Delegate newDelegate = new SweepGradient_Delegate(matrix, x, y, colors,
- positions);
- return sManager.addNewDelegate(newDelegate);
- }
-
- // ---- Private delegate/helper methods ----
-
- /**
- * A subclass of Shader that draws a sweep gradient around a center point.
- *
- * @param nativeMatrix reference to the shader's native transformation matrix
- * @param cx The x-coordinate of the center
- * @param cy The y-coordinate of the center
- * @param colors The 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.
- */
- private SweepGradient_Delegate(long nativeMatrix, float cx, float cy,
- long[] colors, float[] positions) {
- super(nativeMatrix, colors, positions);
- mJavaPaint = new SweepGradientPaint(cx, cy, mColors, mPositions);
- }
-
- private class SweepGradientPaint extends GradientPaint {
-
- private final float mCx;
- private final float mCy;
-
- public SweepGradientPaint(float cx, float cy, int[] colors,
- float[] positions) {
- super(colors, positions, null /*tileMode*/);
- mCx = cx;
- mCy = cy;
- }
-
- @Override
- public java.awt.PaintContext createContext(
- java.awt.image.ColorModel colorModel,
- java.awt.Rectangle deviceBounds,
- java.awt.geom.Rectangle2D userBounds,
- java.awt.geom.AffineTransform xform,
- java.awt.RenderingHints hints) {
- precomputeGradientColors();
-
- java.awt.geom.AffineTransform canvasMatrix;
- try {
- canvasMatrix = xform.createInverse();
- } catch (java.awt.geom.NoninvertibleTransformException e) {
- Bridge.getLog().fidelityWarning(ILayoutLog.TAG_MATRIX_INVERSE,
- "Unable to inverse matrix in SweepGradient", e, null, null /*data*/);
- canvasMatrix = new java.awt.geom.AffineTransform();
- }
-
- java.awt.geom.AffineTransform localMatrix = getLocalMatrix();
- try {
- localMatrix = localMatrix.createInverse();
- } catch (java.awt.geom.NoninvertibleTransformException e) {
- Bridge.getLog().fidelityWarning(ILayoutLog.TAG_MATRIX_INVERSE,
- "Unable to inverse matrix in SweepGradient", e, null, null /*data*/);
- localMatrix = new java.awt.geom.AffineTransform();
- }
-
- return new SweepGradientPaintContext(canvasMatrix, localMatrix, colorModel);
- }
-
- private class SweepGradientPaintContext implements java.awt.PaintContext {
-
- private final java.awt.geom.AffineTransform mCanvasMatrix;
- private final java.awt.geom.AffineTransform mLocalMatrix;
- private final java.awt.image.ColorModel mColorModel;
-
- public SweepGradientPaintContext(
- java.awt.geom.AffineTransform canvasMatrix,
- java.awt.geom.AffineTransform localMatrix,
- java.awt.image.ColorModel colorModel) {
- mCanvasMatrix = canvasMatrix;
- mLocalMatrix = localMatrix;
- mColorModel = colorModel;
- }
-
- @Override
- public void dispose() {
- }
-
- @Override
- public java.awt.image.ColorModel getColorModel() {
- return mColorModel;
- }
-
- @Override
- public java.awt.image.Raster getRaster(int x, int y, int w, int h) {
-
- int[] data = new int[w*h];
-
- // compute angle from each point to the center, and figure out the distance from
- // it.
- int index = 0;
- float[] pt1 = new float[2];
- float[] pt2 = new float[2];
- for (int iy = 0 ; iy < h ; iy++) {
- for (int ix = 0 ; ix < w ; ix++) {
- // handle the canvas transform
- pt1[0] = x + ix;
- pt1[1] = y + iy;
- mCanvasMatrix.transform(pt1, 0, pt2, 0, 1);
-
- // handle the local matrix
- pt1[0] = pt2[0] - mCx;
- pt1[1] = pt2[1] - mCy;
- mLocalMatrix.transform(pt1, 0, pt2, 0, 1);
-
- float dx = pt2[0];
- float dy = pt2[1];
-
- float angle;
- if (dx == 0) {
- angle = (float) (dy < 0 ? 3 * Math.PI / 2 : Math.PI / 2);
- } else if (dy == 0) {
- angle = (float) (dx < 0 ? Math.PI : 0);
- } else {
- angle = (float) Math.atan(dy / dx);
- if (dx > 0) {
- if (dy < 0) {
- angle += Math.PI * 2;
- }
- } else {
- angle += Math.PI;
- }
- }
-
- // convert to 0-1. value and get color
- data[index++] = getGradientColor((float) (angle / (2 * Math.PI)));
- }
- }
-
- DataBufferInt dataBuffer = new DataBufferInt(data, data.length);
- SampleModel colorModel = mColorModel.createCompatibleSampleModel(w, h);
- return Raster.createWritableRaster(colorModel, dataBuffer, null);
- }
-
- }
- }
-}
diff --git a/bridge/src/android/graphics/Typeface_Delegate.java b/bridge/src/android/graphics/Typeface_Delegate.java
index 6c0ab20ec5..d9d38ad4c3 100644
--- a/bridge/src/android/graphics/Typeface_Delegate.java
+++ b/bridge/src/android/graphics/Typeface_Delegate.java
@@ -16,13 +16,12 @@
package android.graphics;
-import com.android.SdkConstants;
+import com.android.ide.common.rendering.api.AndroidConstants;
import com.android.ide.common.rendering.api.ILayoutLog;
import com.android.ide.common.rendering.api.ResourceNamespace;
import com.android.layoutlib.bridge.Bridge;
import com.android.layoutlib.bridge.android.BridgeContext;
import com.android.layoutlib.bridge.android.BridgeXmlBlockParser;
-import com.android.layoutlib.bridge.android.RenderParamsFlags;
import com.android.layoutlib.bridge.impl.DelegateManager;
import com.android.layoutlib.bridge.impl.RenderAction;
import com.android.tools.layoutlib.annotations.LayoutlibDelegate;
@@ -33,23 +32,10 @@ import org.xmlpull.v1.XmlPullParserException;
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.content.res.FontResourcesParser;
-import android.graphics.FontFamily_Delegate.FontVariant;
-import android.graphics.fonts.FontFamily_Builder_Delegate;
-import android.graphics.fonts.FontVariationAxis;
-import java.awt.Font;
import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Paths;
-import java.util.ArrayList;
-import java.util.HashMap;
-import java.util.Iterator;
-import java.util.List;
-import java.util.Map;
-import java.util.Spliterator;
-import java.util.Spliterators;
-
-import libcore.util.NativeAllocationRegistry_Delegate;
/**
* Delegate implementing the native methods of android.graphics.Typeface
@@ -64,174 +50,6 @@ import libcore.util.NativeAllocationRegistry_Delegate;
* @see DelegateManager
*/
public final class Typeface_Delegate {
-
- public static final String SYSTEM_FONTS = "/system/fonts/";
-
- public static final Map<String, FontFamily_Delegate[]> sGenericNativeFamilies = new HashMap<>();
-
- // ---- delegate manager ----
- private static final DelegateManager<Typeface_Delegate> sManager =
- new DelegateManager<>(Typeface_Delegate.class);
- private static long sFinalizer = -1;
-
- // ---- delegate data ----
- private static long sDefaultTypeface;
- @NonNull
- private final FontFamily_Delegate[] mFontFamilies; // the reference to FontFamily_Delegate.
- @NonNull
- private final FontFamily_Builder_Delegate[] mFontFamilyBuilders; // the reference to
- // FontFamily_Builder_Delegate.
- /** @see Font#getStyle() */
- private final int mStyle;
- private final int mWeight;
-
-
- // ---- Public Helper methods ----
-
- private Typeface_Delegate(@NonNull FontFamily_Delegate[] fontFamilies,
- @NonNull FontFamily_Builder_Delegate[] fontFamilyBuilders, int style,
- int weight) {
- mFontFamilies = fontFamilies;
- mFontFamilyBuilders = fontFamilyBuilders;
- mStyle = style;
- mWeight = weight;
- }
-
- public static Typeface_Delegate getDelegate(long nativeTypeface) {
- return sManager.getDelegate(nativeTypeface);
- }
-
- // ---- native methods ----
-
- @LayoutlibDelegate
- /*package*/ static synchronized long nativeCreateFromTypeface(long native_instance, int style) {
- Typeface_Delegate delegate = sManager.getDelegate(native_instance);
- if (delegate == null) {
- delegate = sManager.getDelegate(sDefaultTypeface);
- }
- if (delegate == null) {
- return 0;
- }
-
- return sManager.addNewDelegate(
- new Typeface_Delegate(delegate.mFontFamilies, delegate.mFontFamilyBuilders, style,
- delegate.mWeight));
- }
-
- @LayoutlibDelegate
- /*package*/ static long nativeCreateFromTypefaceWithExactStyle(long native_instance, int weight,
- boolean italic) {
- Typeface_Delegate delegate = sManager.getDelegate(native_instance);
- if (delegate == null) {
- delegate = sManager.getDelegate(sDefaultTypeface);
- }
- if (delegate == null) {
- return 0;
- }
-
- int style = weight >= 600 ? (italic ? Typeface.BOLD_ITALIC : Typeface.BOLD) :
- (italic ? Typeface.ITALIC : Typeface.NORMAL);
- return sManager.addNewDelegate(
- new Typeface_Delegate(delegate.mFontFamilies, delegate.mFontFamilyBuilders, style,
- weight));
- }
-
- @LayoutlibDelegate
- /*package*/ static synchronized long nativeCreateFromTypefaceWithVariation(long native_instance,
- List<FontVariationAxis> axes) {
- long newInstance = nativeCreateFromTypeface(native_instance, 0);
-
- if (newInstance != 0) {
- Bridge.getLog().fidelityWarning(ILayoutLog.TAG_UNSUPPORTED,
- "nativeCreateFromTypefaceWithVariation is not supported", null, null, null);
- }
- return newInstance;
- }
-
- @LayoutlibDelegate
- /*package*/ static synchronized int[] nativeGetSupportedAxes(long native_instance) {
- // nativeCreateFromTypefaceWithVariation is not supported so we do not keep the axes
- return null;
- }
-
- @LayoutlibDelegate
- /*package*/ static long nativeCreateWeightAlias(long native_instance, int weight) {
- Typeface_Delegate delegate = sManager.getDelegate(native_instance);
- if (delegate == null) {
- delegate = sManager.getDelegate(sDefaultTypeface);
- }
- if (delegate == null) {
- return 0;
- }
- Typeface_Delegate weightAlias =
- new Typeface_Delegate(delegate.mFontFamilies, delegate.mFontFamilyBuilders,
- delegate.mStyle,
- weight);
- return sManager.addNewDelegate(weightAlias);
- }
-
- @LayoutlibDelegate
- /*package*/ static synchronized long nativeCreateFromArray(long[] familyArray, int weight,
- int italic) {
- List<FontFamily_Delegate> fontFamilies = new ArrayList<>();
- List<FontFamily_Builder_Delegate> fontFamilyBuilders = new ArrayList<>();
- for (long aFamilyArray : familyArray) {
- try {
- fontFamilies.add(FontFamily_Delegate.getDelegate(aFamilyArray));
- } catch (ClassCastException e) {
- fontFamilyBuilders.add(FontFamily_Builder_Delegate.getDelegate(aFamilyArray));
- }
- }
- if (weight == Typeface.RESOLVE_BY_FONT_TABLE) {
- weight = 400;
- }
- if (italic == Typeface.RESOLVE_BY_FONT_TABLE) {
- italic = 0;
- }
- int style = weight >= 600 ? (italic == 1 ? Typeface.BOLD_ITALIC : Typeface.BOLD) :
- (italic == 1 ? Typeface.ITALIC : Typeface.NORMAL);
- Typeface_Delegate delegate =
- new Typeface_Delegate(fontFamilies.toArray(new FontFamily_Delegate[0]),
- fontFamilyBuilders.toArray(new FontFamily_Builder_Delegate[0]),
- style, weight);
- return sManager.addNewDelegate(delegate);
- }
-
- @LayoutlibDelegate
- /*package*/ static long nativeGetReleaseFunc() {
- synchronized (Typeface_Delegate.class) {
- if (sFinalizer == -1) {
- sFinalizer = NativeAllocationRegistry_Delegate.createFinalizer(
- sManager::removeJavaReferenceFor);
- }
- }
- return sFinalizer;
- }
-
- @LayoutlibDelegate
- /*package*/ static int nativeGetStyle(long native_instance) {
- Typeface_Delegate delegate = sManager.getDelegate(native_instance);
- if (delegate == null) {
- return 0;
- }
-
- return delegate.mStyle;
- }
-
- @LayoutlibDelegate
- /*package*/ static void nativeSetDefault(long native_instance) {
- sDefaultTypeface = native_instance;
- }
-
- @LayoutlibDelegate
- /*package*/ static int nativeGetWeight(long native_instance) {
- Typeface_Delegate delegate = sManager.getDelegate(native_instance);
- if (delegate == null) {
- return 0;
- }
- return delegate.mWeight;
- }
-
/**
* Loads a single font or font family from disk
*/
@@ -245,16 +63,9 @@ public final class Typeface_Delegate {
}
String lowerCaseValue = path.toLowerCase();
- if (lowerCaseValue.endsWith(SdkConstants.DOT_XML)) {
+ if (lowerCaseValue.endsWith(AndroidConstants.DOT_XML)) {
// create a block parser for the file
- Boolean psiParserSupport = context.getLayoutlibCallback().getFlag(
- RenderParamsFlags.FLAG_KEY_XML_FILE_PARSER_SUPPORT);
- XmlPullParser parser;
- if (psiParserSupport != null && psiParserSupport) {
- parser = context.getLayoutlibCallback().createXmlParserForPsiFile(path);
- } else {
- parser = context.getLayoutlibCallback().createXmlParserForFile(path);
- }
+ XmlPullParser parser = context.getLayoutlibCallback().createXmlParserForPsiFile(path);
if (parser != null) {
// TODO(b/156609434): The aapt namespace should not matter for parsing font files?
@@ -266,8 +77,8 @@ public final class Typeface_Delegate {
FontResourcesParser.parse(blockParser, context.getResources());
typeface = Typeface.createFromResources(entry, context.getAssets(), path);
} catch (XmlPullParserException | IOException e) {
- Bridge.getLog().error(null, "Failed to parse file " + path, e, null, null /*data
- */);
+ Bridge.getLog().error(null, "Failed to parse file " + path, e, null,
+ null /*data*/);
} finally {
blockParser.ensurePopped();
}
@@ -303,125 +114,4 @@ public final class Typeface_Delegate {
/*package*/ static Typeface create(Typeface family, int style, boolean isItalic) {
return Typeface.create_Original(family, style, isItalic);
}
-
- @LayoutlibDelegate
- /*package*/ static void nativeRegisterGenericFamily(String str, long nativePtr) {
- Typeface_Delegate delegate = sManager.getDelegate(nativePtr);
- if (delegate == null) {
- return;
- }
- sGenericNativeFamilies.put(str, delegate.mFontFamilies);
- }
-
- // ---- Private delegate/helper methods ----
-
- /**
- * Return an Iterable of fonts that match the style and variant. The list is ordered
- * according to preference of fonts.
- * <p>
- * The Iterator may contain null when the font failed to load. If null is reached when trying to
- * render with this list of fonts, then a warning should be logged letting the user know that
- * some font failed to load.
- *
- * @param variant The variant preferred. Can only be {@link FontVariant#COMPACT} or {@link
- * FontVariant#ELEGANT}
- */
- @NonNull
- public Iterable<Font> getFonts(final FontVariant variant) {
- assert variant != FontVariant.NONE;
-
- return new FontsIterator(mFontFamilies, mFontFamilyBuilders, variant, mWeight, mStyle);
- }
-
- private static class FontsIterator implements Iterator<Font>, Iterable<Font> {
- private final FontFamily_Delegate[] fontFamilies;
- private final FontFamily_Builder_Delegate[] fontFamilyBuilders;
- private final int weight;
- private final boolean isItalic;
- private final FontVariant variant;
-
- private int index = 0;
-
- private FontsIterator(@NonNull FontFamily_Delegate[] fontFamilies,
- @NonNull FontFamily_Builder_Delegate[] fontFamilyBuilders,
- @NonNull FontVariant variant, int weight, int style) {
- // Calculate the required weight based on style and weight of this typeface.
- int boldExtraWeight =
- ((style & Font.BOLD) == 0 ? 0 : FontFamily_Delegate.BOLD_FONT_WEIGHT_DELTA);
- this.weight = Math.min(Math.max(100, weight + 50 + boldExtraWeight), 1000);
- this.isItalic = (style & Font.ITALIC) != 0;
- this.fontFamilies = fontFamilies;
- this.fontFamilyBuilders = fontFamilyBuilders;
- this.variant = variant;
- }
-
- @Override
- public boolean hasNext() {
- return index < (fontFamilies.length + fontFamilyBuilders.length);
- }
-
- @Override
- @Nullable
- public Font next() {
- Font font;
- FontVariant ffdVariant;
- if (index < fontFamilies.length) {
- FontFamily_Delegate ffd = fontFamilies[index++];
- if (ffd == null || !ffd.isValid()) {
- return null;
- }
- font = ffd.getFont(weight, isItalic);
- ffdVariant = ffd.getVariant();
- } else {
- FontFamily_Builder_Delegate ffd = fontFamilyBuilders[index++ - fontFamilies.length];
- if (ffd == null) {
- return null;
- }
- font = ffd.getFont(weight, isItalic);
- ffdVariant = ffd.getVariant();
- }
-
- if (font == null) {
- // The FontFamily is valid but doesn't contain any matching font. This means
- // that the font failed to load. We add null to the list of fonts. Don't throw
- // the warning just yet. If this is a non-english font, we don't want to warn
- // users who are trying to render only english text.
- return null;
- }
-
- if (ffdVariant == FontVariant.NONE || ffdVariant == variant) {
- return font;
- }
-
- // We cannot open each font and get locales supported, etc to match the fonts.
- // As a workaround, we hardcode certain assumptions like Elegant and Compact
- // always appear in pairs.
- if (index < fontFamilies.length) {
- assert index < fontFamilies.length - 1;
- FontFamily_Delegate ffd2 = fontFamilies[index++];
- assert ffd2 != null;
-
- return ffd2.getFont(weight, isItalic);
- } else {
- assert index < fontFamilies.length + fontFamilyBuilders.length - 1;
- FontFamily_Builder_Delegate ffd2 = fontFamilyBuilders[index++ - fontFamilies.length];
- assert ffd2 != null;
-
- return ffd2.getFont(weight, isItalic);
- }
- }
-
- @NonNull
- @Override
- public Iterator<Font> iterator() {
- return this;
- }
-
- @Override
- public Spliterator<Font> spliterator() {
- return Spliterators.spliterator(iterator(),
- fontFamilies.length + fontFamilyBuilders.length,
- Spliterator.IMMUTABLE | Spliterator.SIZED);
- }
- }
-}
+} \ No newline at end of file
diff --git a/bridge/src/android/graphics/animation/NativeInterpolatorFactory_Delegate.java b/bridge/src/android/graphics/animation/NativeInterpolatorFactory_Delegate.java
deleted file mode 100644
index 38ad602ab2..0000000000
--- a/bridge/src/android/graphics/animation/NativeInterpolatorFactory_Delegate.java
+++ /dev/null
@@ -1,140 +0,0 @@
-/*
- * Copyright (C) 2016 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package android.graphics.animation;
-
-import com.android.layoutlib.bridge.impl.DelegateManager;
-import com.android.tools.layoutlib.annotations.LayoutlibDelegate;
-
-import android.graphics.Path;
-import android.util.MathUtils;
-import android.view.animation.AccelerateDecelerateInterpolator;
-import android.view.animation.AccelerateInterpolator;
-import android.view.animation.AnticipateInterpolator;
-import android.view.animation.AnticipateOvershootInterpolator;
-import android.view.animation.BaseInterpolator;
-import android.view.animation.BounceInterpolator;
-import android.view.animation.CycleInterpolator;
-import android.view.animation.DecelerateInterpolator;
-import android.view.animation.Interpolator;
-import android.view.animation.LinearInterpolator;
-import android.view.animation.OvershootInterpolator;
-import android.view.animation.PathInterpolator;
-
-/**
- * Delegate used to provide new implementation of a select few methods of {@link
- * NativeInterpolatorFactory}
- * <p>
- * Through the layoutlib_create tool, the original methods of NativeInterpolatorFactory have
- * been replaced by calls to methods of the same name in this delegate class.
- */
-@SuppressWarnings("unused")
-public class NativeInterpolatorFactory_Delegate {
- private static final DelegateManager<Interpolator> sManager = new DelegateManager<>
- (Interpolator.class);
-
- public static Interpolator getDelegate(long nativePtr) {
- return sManager.getDelegate(nativePtr);
- }
-
- @LayoutlibDelegate
- /*package*/ static long createAccelerateDecelerateInterpolator() {
- return sManager.addNewDelegate(new AccelerateDecelerateInterpolator());
- }
-
- @LayoutlibDelegate
- /*package*/ static long createAccelerateInterpolator(float factor) {
- return sManager.addNewDelegate(new AccelerateInterpolator(factor));
- }
-
- @LayoutlibDelegate
- /*package*/ static long createAnticipateInterpolator(float tension) {
- return sManager.addNewDelegate(new AnticipateInterpolator(tension));
- }
-
- @LayoutlibDelegate
- /*package*/ static long createAnticipateOvershootInterpolator(float tension) {
- return sManager.addNewDelegate(new AnticipateOvershootInterpolator(tension));
- }
-
- @LayoutlibDelegate
- /*package*/ static long createBounceInterpolator() {
- return sManager.addNewDelegate(new BounceInterpolator());
- }
-
- @LayoutlibDelegate
- /*package*/ static long createCycleInterpolator(float cycles) {
- return sManager.addNewDelegate(new CycleInterpolator(cycles));
- }
-
- @LayoutlibDelegate
- /*package*/ static long createDecelerateInterpolator(float factor) {
- return sManager.addNewDelegate(new DecelerateInterpolator(factor));
- }
-
- @LayoutlibDelegate
- /*package*/ static long createLinearInterpolator() {
- return sManager.addNewDelegate(new LinearInterpolator());
- }
-
- @LayoutlibDelegate
- /*package*/ static long createOvershootInterpolator(float tension) {
- return sManager.addNewDelegate(new OvershootInterpolator(tension));
- }
-
- @LayoutlibDelegate
- /*package*/ static long createPathInterpolator(float[] x, float[] y) {
- Path path = new Path();
- path.moveTo(x[0], y[0]);
- for (int i = 1; i < x.length; i++) {
- path.lineTo(x[i], y[i]);
- }
- return sManager.addNewDelegate(new PathInterpolator(path));
- }
-
- private static class LutInterpolator extends BaseInterpolator {
- private final float[] mValues;
- private final int mSize;
-
- private LutInterpolator(float[] values) {
- mValues = values;
- mSize = mValues.length;
- }
-
- @Override
- public float getInterpolation(float input) {
- float lutpos = input * (mSize - 1);
- if (lutpos >= (mSize - 1)) {
- return mValues[mSize - 1];
- }
-
- int ipart = (int) lutpos;
- float weight = lutpos - ipart;
-
- int i1 = ipart;
- int i2 = Math.min(i1 + 1, mSize - 1);
-
- assert i1 >= 0 && i2 >= 0 : "Negatives in the interpolation";
-
- return MathUtils.lerp(mValues[i1], mValues[i2], weight);
- }
- }
-
- @LayoutlibDelegate
- /*package*/ static long createLutInterpolator(float[] values) {
- return sManager.addNewDelegate(new LutInterpolator(values));
- }
-}
diff --git a/bridge/src/android/graphics/drawable/AnimatedVectorDrawable_Delegate.java b/bridge/src/android/graphics/drawable/AnimatedVectorDrawable_Delegate.java
index 9c272fac48..92f4ab85dc 100644
--- a/bridge/src/android/graphics/drawable/AnimatedVectorDrawable_Delegate.java
+++ b/bridge/src/android/graphics/drawable/AnimatedVectorDrawable_Delegate.java
@@ -1,7 +1,7 @@
/*
- * Copyright (C) 2016 The Android Open Source Project
+ * Copyright (C) 2019 The Android Open Source Project
*
- * Licensed under the Apache License, Version 2.0 (the "License") {}
+ * 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
*
@@ -16,283 +16,30 @@
package android.graphics.drawable;
-import com.android.ide.common.rendering.api.ILayoutLog;
-import com.android.layoutlib.bridge.Bridge;
-import com.android.layoutlib.bridge.impl.DelegateManager;
import com.android.tools.layoutlib.annotations.LayoutlibDelegate;
-import android.animation.Animator;
-import android.animation.AnimatorSet;
-import android.animation.ObjectAnimator;
-import android.animation.PropertyValuesHolder;
-import android.annotation.NonNull;
-import android.annotation.Nullable;
-import android.graphics.animation.NativeInterpolatorFactory_Delegate;
+import android.graphics.Canvas;
import android.graphics.drawable.AnimatedVectorDrawable.VectorDrawableAnimatorRT;
-import android.graphics.drawable.VectorDrawable_Delegate.VFullPath_Delegate;
-import android.graphics.drawable.VectorDrawable_Delegate.VGroup_Delegate;
-import android.graphics.drawable.VectorDrawable_Delegate.VNativeObject;
-import android.graphics.drawable.VectorDrawable_Delegate.VPathRenderer_Delegate;
-import java.util.ArrayList;
-import java.util.function.Consumer;
-
-/**
- * Delegate used to provide new implementation of a select few methods of {@link
- * AnimatedVectorDrawable}
- * <p>
- * Through the layoutlib_create tool, the original methods of AnimatedVectorDrawable have been
- * replaced by calls to methods of the same name in this delegate class.
- */
-@SuppressWarnings("unused")
public class AnimatedVectorDrawable_Delegate {
- private static DelegateManager<AnimatorSetHolder> sAnimatorSets = new
- DelegateManager<>(AnimatorSetHolder.class);
- private static DelegateManager<PropertySetter> sHolders = new
- DelegateManager<>(PropertySetter.class);
-
-
- @LayoutlibDelegate
- /*package*/ static long nCreateAnimatorSet() {
- return sAnimatorSets.addNewDelegate(new AnimatorSetHolder());
- }
-
- @LayoutlibDelegate
- /*package*/ static void nSetVectorDrawableTarget(long animatorPtr, long vectorDrawablePtr) {
- // TODO: implement
- }
- @LayoutlibDelegate
- /*package*/ static void nAddAnimator(long setPtr, long propertyValuesHolder,
- long nativeInterpolator, long startDelay, long duration, int repeatCount,
- int repeatMode) {
- PropertySetter holder = sHolders.getDelegate(propertyValuesHolder);
- if (holder == null || holder.getValues() == null) {
- return;
- }
-
- ObjectAnimator animator = new ObjectAnimator();
- animator.setValues(holder.getValues());
- animator.setInterpolator(
- NativeInterpolatorFactory_Delegate.getDelegate(nativeInterpolator));
- animator.setStartDelay(startDelay);
- animator.setDuration(duration);
- animator.setRepeatCount(repeatCount);
- animator.setRepeatMode(repeatMode);
- animator.setTarget(holder);
- animator.setPropertyName(holder.getValues().getPropertyName());
-
- AnimatorSetHolder set = sAnimatorSets.getDelegate(setPtr);
- assert set != null;
- set.addAnimator(animator);
- }
-
- @LayoutlibDelegate
- /*package*/ static long nCreateGroupPropertyHolder(long nativePtr, int propertyId,
- float startValue, float endValue) {
- VGroup_Delegate group = VNativeObject.getDelegate(nativePtr);
- Consumer<Float> setter = group.getPropertySetter(propertyId);
-
- return sHolders.addNewDelegate(FloatPropertySetter.of(setter, startValue,
- endValue));
- }
-
- @LayoutlibDelegate
- /*package*/ static long nCreatePathDataPropertyHolder(long nativePtr, long startValuePtr,
- long endValuePtr) {
- Bridge.getLog().fidelityWarning(ILayoutLog.TAG_UNSUPPORTED, "AnimatedVectorDrawable path " +
- "animations are not supported.", null, null, null);
- return 0;
- }
-
- @LayoutlibDelegate
- /*package*/ static long nCreatePathColorPropertyHolder(long nativePtr, int propertyId,
- int startValue, int endValue) {
- VFullPath_Delegate path = VNativeObject.getDelegate(nativePtr);
- Consumer<Integer> setter = path.getIntPropertySetter(propertyId);
-
- return sHolders.addNewDelegate(IntPropertySetter.of(setter, startValue,
- endValue));
- }
-
- @LayoutlibDelegate
- /*package*/ static long nCreatePathPropertyHolder(long nativePtr, int propertyId,
- float startValue, float endValue) {
- VFullPath_Delegate path = VNativeObject.getDelegate(nativePtr);
- Consumer<Float> setter = path.getFloatPropertySetter(propertyId);
-
- return sHolders.addNewDelegate(FloatPropertySetter.of(setter, startValue,
- endValue));
- }
-
- @LayoutlibDelegate
- /*package*/ static long nCreateRootAlphaPropertyHolder(long nativePtr, float startValue,
- float endValue) {
- VPathRenderer_Delegate renderer = VNativeObject.getDelegate(nativePtr);
-
- return sHolders.addNewDelegate(FloatPropertySetter.of(renderer::setRootAlpha,
- startValue,
- endValue));
- }
-
- @LayoutlibDelegate
- /*package*/ static void nSetPropertyHolderData(long nativePtr, float[] data, int length) {
- PropertySetter setter = sHolders.getDelegate(nativePtr);
- assert setter != null;
-
- setter.setValues(data);
- }
-
- @LayoutlibDelegate
- /*package*/ static void nSetPropertyHolderData(long nativePtr, int[] data, int length) {
- PropertySetter setter = sHolders.getDelegate(nativePtr);
- assert setter != null;
-
- setter.setValues(data);
- }
-
- @LayoutlibDelegate
- /*package*/ static void nStart(long animatorSetPtr, VectorDrawableAnimatorRT set, int id) {
- AnimatorSetHolder animatorSet = sAnimatorSets.getDelegate(animatorSetPtr);
- assert animatorSet != null;
-
- animatorSet.start();
- }
-
- @LayoutlibDelegate
- /*package*/ static void nReverse(long animatorSetPtr, VectorDrawableAnimatorRT set, int id) {
- AnimatorSetHolder animatorSet = sAnimatorSets.getDelegate(animatorSetPtr);
- assert animatorSet != null;
-
- animatorSet.reverse();
- }
-
- @LayoutlibDelegate
- /*package*/ static void nEnd(long animatorSetPtr) {
- AnimatorSetHolder animatorSet = sAnimatorSets.getDelegate(animatorSetPtr);
- assert animatorSet != null;
-
- animatorSet.end();
- }
-
- @LayoutlibDelegate
- /*package*/ static void nReset(long animatorSetPtr) {
- AnimatorSetHolder animatorSet = sAnimatorSets.getDelegate(animatorSetPtr);
- assert animatorSet != null;
-
- animatorSet.end();
- animatorSet.start();
- }
-
- private static class AnimatorSetHolder {
- private ArrayList<Animator> mAnimators = new ArrayList<>();
- private AnimatorSet mAnimatorSet = null;
-
- private void addAnimator(@NonNull Animator animator) {
- mAnimators.add(animator);
- }
-
- private void ensureAnimatorSet() {
- if (mAnimatorSet == null) {
- mAnimatorSet = new AnimatorSet();
- mAnimatorSet.playTogether(mAnimators);
- }
- }
-
- private void start() {
- ensureAnimatorSet();
-
- mAnimatorSet.start();
- }
-
- private void end() {
- mAnimatorSet.end();
- }
-
- private void reset() {
- end();
- start();
- }
-
- private void reverse() {
- mAnimatorSet.reverse();
- }
- }
/**
- * Class that allows setting a value and holds the range of values for the given property.
- *
- * @param <T> the type of the property
+ * We would like to do the same as in {@link AnimatedVectorDrawable#draw}, but bypass the
+ * {@link Canvas#isHardwareAccelerated} check and call
+ * {@link AnimatedVectorDrawable#forceAnimationOnUI}. We need this for the callbacks to be
+ * properly set up so that {@link AnimatedVectorDrawable} receives property updates.
+ * TODO (b/141682855): Figure out how to properly manage this in the hardware accelerated case
*/
- private static class PropertySetter<T> {
- final Consumer<T> mValueSetter;
- private PropertyValuesHolder mValues;
-
- private PropertySetter(@NonNull Consumer<T> valueSetter) {
- mValueSetter = valueSetter;
- }
-
- /**
- * Method to set an {@link Integer} value for this property. The default implementation of
- * this method doesn't do anything. This method is accessed via reflection by the
- * PropertyValuesHolder.
- */
- public void setIntValue(Integer value) {
- }
-
- /**
- * Method to set an {@link Integer} value for this property. The default implementation of
- * this method doesn't do anything. This method is accessed via reflection by the
- * PropertyValuesHolder.
- */
- public void setFloatValue(Float value) {
- }
-
- void setValues(float... values) {
- mValues = PropertyValuesHolder.ofFloat("floatValue", values);
- }
-
- @Nullable
- PropertyValuesHolder getValues() {
- return mValues;
- }
-
- void setValues(int... values) {
- mValues = PropertyValuesHolder.ofInt("intValue", values);
- }
- }
-
- private static class IntPropertySetter extends PropertySetter<Integer> {
- private IntPropertySetter(Consumer<Integer> valueSetter) {
- super(valueSetter);
- }
-
- private static PropertySetter of(Consumer<Integer> valueSetter, int... values) {
- PropertySetter setter = new IntPropertySetter(valueSetter);
- setter.setValues(values);
-
- return setter;
- }
-
- public void setIntValue(Integer value) {
- mValueSetter.accept(value);
- }
- }
-
- private static class FloatPropertySetter extends PropertySetter<Float> {
- private FloatPropertySetter(Consumer<Float> valueSetter) {
- super(valueSetter);
- }
-
- private static PropertySetter of(Consumer<Float> valueSetter, float... values) {
- PropertySetter setter = new FloatPropertySetter(valueSetter);
- setter.setValues(values);
-
- return setter;
- }
-
- public void setFloatValue(Float value) {
- mValueSetter.accept(value);
+ @LayoutlibDelegate
+ static void draw(AnimatedVectorDrawable thisDrawable, Canvas canvas) {
+ if (thisDrawable.mAnimatorSet instanceof VectorDrawableAnimatorRT) {
+ // If we have SW canvas and the RT animation is waiting to start, We need to fallback
+ // to UI thread animation for AVD.
+ if (!thisDrawable.mAnimatorSet.isRunning() &&
+ ((VectorDrawableAnimatorRT)thisDrawable.mAnimatorSet).mPendingAnimationActions.size() > 0) {
+ thisDrawable.forceAnimationOnUI();
+ }
}
-
+ thisDrawable.draw_Original(canvas);
}
}
diff --git a/bridge/src/android/graphics/drawable/AnimatedVectorDrawable_VectorDrawableAnimatorUI_Delegate.java b/bridge/src/android/graphics/drawable/AnimatedVectorDrawable_VectorDrawableAnimatorUI_Delegate.java
new file mode 100644
index 0000000000..b21b372cd6
--- /dev/null
+++ b/bridge/src/android/graphics/drawable/AnimatedVectorDrawable_VectorDrawableAnimatorUI_Delegate.java
@@ -0,0 +1,46 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License") {}
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.graphics.drawable;
+
+import com.android.tools.layoutlib.annotations.LayoutlibDelegate;
+
+import android.animation.AnimationHandler;
+import android.graphics.Canvas;
+import android.graphics.drawable.AnimatedVectorDrawable.VectorDrawableAnimatorUI;
+
+/**
+ * Delegate used to provide new implementation of a select few methods of {@link
+ * AnimatedVectorDrawable}
+ * <p>
+ * Through the layoutlib_create tool, the original methods of AnimatedVectorDrawable have been
+ * replaced by calls to methods of the same name in this delegate class.
+ */
+@SuppressWarnings("unused")
+public class AnimatedVectorDrawable_VectorDrawableAnimatorUI_Delegate {
+
+ public static long sFrameTime;
+
+ @LayoutlibDelegate
+ /*package*/ static void onDraw(VectorDrawableAnimatorUI thisDrawableAnimator, Canvas canvas) {
+ thisDrawableAnimator.onDraw_Original(canvas);
+ AnimationHandler handler = AnimationHandler.getInstance();
+ if (thisDrawableAnimator.mSet.mLastFrameTime < 0) {
+ handler.doAnimationFrame(0);
+ }
+ handler.doAnimationFrame(sFrameTime);
+ }
+}
diff --git a/bridge/src/android/graphics/drawable/GradientDrawable_Delegate.java b/bridge/src/android/graphics/drawable/GradientDrawable_Delegate.java
deleted file mode 100644
index a3ad2aac7d..0000000000
--- a/bridge/src/android/graphics/drawable/GradientDrawable_Delegate.java
+++ /dev/null
@@ -1,73 +0,0 @@
-/*
- * Copyright (C) 2015 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.graphics.drawable;
-
-import com.android.tools.layoutlib.annotations.LayoutlibDelegate;
-
-import android.graphics.Path;
-import android.graphics.drawable.GradientDrawable.GradientState;
-
-import java.lang.reflect.Field;
-
-/**
- * Delegate implementing the native methods of {@link GradientDrawable}
- *
- * Through the layoutlib_create tool, the original native methods of GradientDrawable have been
- * replaced by calls to methods of the same name in this delegate class.
- */
-public class GradientDrawable_Delegate {
-
- /**
- * The ring can be built either by drawing full circles, or by drawing arcs in case the
- * circle isn't complete. LayoutLib cannot handle drawing full circles (requires path
- * subtraction). So, if we need to draw full circles, we switch to drawing 99% circle.
- */
- @LayoutlibDelegate
- /*package*/ static Path buildRing(GradientDrawable thisDrawable, GradientState st) {
- boolean useLevel = st.mUseLevelForShape;
- int level = thisDrawable.getLevel();
- // 10000 is the max level. See android.graphics.drawable.Drawable#getLevel()
- float sweep = useLevel ? (360.0f * level / 10000.0f) : 360f;
- Field mLevel = null;
- if (sweep >= 360 || sweep <= -360) {
- st.mUseLevelForShape = true;
- // Use reflection to set the value of the field to prevent setting the drawable to
- // dirty again.
- try {
- mLevel = Drawable.class.getDeclaredField("mLevel");
- mLevel.setAccessible(true);
- mLevel.setInt(thisDrawable, 9999); // set to one less than max.
- } catch (NoSuchFieldException e) {
- // The field has been removed in a recent framework change. Fall back to old
- // buggy behaviour.
- } catch (IllegalAccessException e) {
- // We've already set the field to be accessible.
- assert false;
- }
- }
- Path path = thisDrawable.buildRing_Original(st);
- st.mUseLevelForShape = useLevel;
- if (mLevel != null) {
- try {
- mLevel.setInt(thisDrawable, level);
- } catch (IllegalAccessException e) {
- assert false;
- }
- }
- return path;
- }
-}
diff --git a/bridge/src/android/graphics/drawable/VectorDrawable_Delegate.java b/bridge/src/android/graphics/drawable/VectorDrawable_Delegate.java
deleted file mode 100644
index 828df6bfd5..0000000000
--- a/bridge/src/android/graphics/drawable/VectorDrawable_Delegate.java
+++ /dev/null
@@ -1,1299 +0,0 @@
-/*
- * Copyright (C) 2016 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package android.graphics.drawable;
-
-import com.android.layoutlib.bridge.impl.DelegateManager;
-import com.android.tools.layoutlib.annotations.LayoutlibDelegate;
-
-import android.annotation.NonNull;
-import android.content.res.Resources;
-import android.content.res.Resources.Theme;
-import android.content.res.TypedArray;
-import android.graphics.BaseCanvas_Delegate;
-import android.graphics.Canvas_Delegate;
-import android.graphics.Color;
-import android.graphics.Matrix;
-import android.graphics.Paint;
-import android.graphics.Paint.Cap;
-import android.graphics.Paint.Join;
-import android.graphics.Paint_Delegate;
-import android.graphics.Path;
-import android.graphics.Path.FillType;
-import android.graphics.PathMeasure;
-import android.graphics.Path_Delegate;
-import android.graphics.Rect;
-import android.graphics.Region;
-import android.graphics.Region.Op;
-import android.graphics.Shader_Delegate;
-import android.util.ArrayMap;
-import android.util.AttributeSet;
-import android.util.Log;
-import android.util.MathUtils;
-import android.util.PathParser_Delegate;
-
-import java.nio.ByteBuffer;
-import java.nio.ByteOrder;
-import java.nio.FloatBuffer;
-import java.util.ArrayList;
-import java.util.function.Consumer;
-
-import static android.graphics.Canvas.CLIP_SAVE_FLAG;
-import static android.graphics.Canvas.MATRIX_SAVE_FLAG;
-import static android.graphics.Paint.Cap.BUTT;
-import static android.graphics.Paint.Cap.ROUND;
-import static android.graphics.Paint.Cap.SQUARE;
-import static android.graphics.Paint.Join.BEVEL;
-import static android.graphics.Paint.Join.MITER;
-import static android.graphics.Paint.Style;
-
-/**
- * Delegate used to provide new implementation of a select few methods of {@link VectorDrawable}
- * <p>
- * Through the layoutlib_create tool, the original methods of VectorDrawable have been replaced by
- * calls to methods of the same name in this delegate class.
- */
-@SuppressWarnings("unused")
-public class VectorDrawable_Delegate {
- private static final String LOGTAG = VectorDrawable_Delegate.class.getSimpleName();
- private static final boolean DBG_VECTOR_DRAWABLE = false;
-
- private static final DelegateManager<VNativeObject> sPathManager =
- new DelegateManager<>(VNativeObject.class);
-
- private static long addNativeObject(VNativeObject object) {
- long ptr = sPathManager.addNewDelegate(object);
- object.setNativePtr(ptr);
-
- return ptr;
- }
-
- /**
- * Obtains styled attributes from the theme, if available, or unstyled resources if the theme is
- * null.
- */
- private static TypedArray obtainAttributes(
- Resources res, Theme theme, AttributeSet set, int[] attrs) {
- if (theme == null) {
- return res.obtainAttributes(set, attrs);
- }
- return theme.obtainStyledAttributes(set, attrs, 0, 0);
- }
-
- private static int applyAlpha(int color, float alpha) {
- int alphaBytes = Color.alpha(color);
- color &= 0x00FFFFFF;
- color |= ((int) (alphaBytes * alpha)) << 24;
- return color;
- }
-
- @LayoutlibDelegate
- static long nCreateTree(long rootGroupPtr) {
- return addNativeObject(new VPathRenderer_Delegate(rootGroupPtr));
- }
-
- @LayoutlibDelegate
- static long nCreateTreeFromCopy(long rendererToCopyPtr, long rootGroupPtr) {
- VPathRenderer_Delegate rendererToCopy = VNativeObject.getDelegate(rendererToCopyPtr);
- return addNativeObject(new VPathRenderer_Delegate(rendererToCopy,
- rootGroupPtr));
- }
-
- @LayoutlibDelegate
- static void nSetRendererViewportSize(long rendererPtr, float viewportWidth,
- float viewportHeight) {
- VPathRenderer_Delegate nativePathRenderer = VNativeObject.getDelegate(rendererPtr);
- nativePathRenderer.mViewportWidth = viewportWidth;
- nativePathRenderer.mViewportHeight = viewportHeight;
- }
-
- @LayoutlibDelegate
- static boolean nSetRootAlpha(long rendererPtr, float alpha) {
- VPathRenderer_Delegate nativePathRenderer = VNativeObject.getDelegate(rendererPtr);
- nativePathRenderer.setRootAlpha(alpha);
-
- return true;
- }
-
- @LayoutlibDelegate
- static float nGetRootAlpha(long rendererPtr) {
- VPathRenderer_Delegate nativePathRenderer = VNativeObject.getDelegate(rendererPtr);
-
- return nativePathRenderer.getRootAlpha();
- }
-
- @LayoutlibDelegate
- static void nSetAntiAlias(long rendererPtr, boolean aa) {
- VPathRenderer_Delegate nativePathRenderer = VNativeObject.getDelegate(rendererPtr);
- nativePathRenderer.setAntiAlias(aa);
- }
-
- @LayoutlibDelegate
- static void nSetAllowCaching(long rendererPtr, boolean allowCaching) {
- // ignored
- }
-
- @LayoutlibDelegate
- static int nDraw(long rendererPtr, long canvasWrapperPtr,
- long colorFilterPtr, Rect bounds, boolean needsMirroring, boolean canReuseCache) {
- VPathRenderer_Delegate nativePathRenderer = VNativeObject.getDelegate(rendererPtr);
-
- Canvas_Delegate.nSave(canvasWrapperPtr, MATRIX_SAVE_FLAG | CLIP_SAVE_FLAG);
- Canvas_Delegate.nClipRect(canvasWrapperPtr,
- bounds.left, bounds.top, bounds.right, bounds.bottom,
- Region.Op.INTERSECT.nativeInt);
- Canvas_Delegate.nTranslate(canvasWrapperPtr, bounds.left, bounds.top);
-
- if (needsMirroring) {
- Canvas_Delegate.nTranslate(canvasWrapperPtr, bounds.width(), 0);
- Canvas_Delegate.nScale(canvasWrapperPtr, -1.0f, 1.0f);
- }
-
- // At this point, canvas has been translated to the right position.
- // And we use this bound for the destination rect for the drawBitmap, so
- // we offset to (0, 0);
- bounds.offsetTo(0, 0);
- nativePathRenderer.draw(canvasWrapperPtr, colorFilterPtr, bounds.width(), bounds.height());
-
- Canvas_Delegate.nRestore(canvasWrapperPtr);
-
- return bounds.width() * bounds.height();
- }
-
- @LayoutlibDelegate
- static long nCreateFullPath() {
- return addNativeObject(new VFullPath_Delegate());
- }
-
- @LayoutlibDelegate
- static long nCreateFullPath(long nativeFullPathPtr) {
- VFullPath_Delegate original = VNativeObject.getDelegate(nativeFullPathPtr);
- return addNativeObject(new VFullPath_Delegate(original));
- }
-
- @LayoutlibDelegate
- static boolean nGetFullPathProperties(long pathPtr, byte[] propertiesData,
- int length) {
- VFullPath_Delegate path = VNativeObject.getDelegate(pathPtr);
-
- ByteBuffer properties = ByteBuffer.wrap(propertiesData);
- properties.order(ByteOrder.nativeOrder());
-
- properties.putFloat(VFullPath_Delegate.STROKE_WIDTH_INDEX * 4, path.getStrokeWidth());
- properties.putInt(VFullPath_Delegate.STROKE_COLOR_INDEX * 4, path.getStrokeColor());
- properties.putFloat(VFullPath_Delegate.STROKE_ALPHA_INDEX * 4, path.getStrokeAlpha());
- properties.putInt(VFullPath_Delegate.FILL_COLOR_INDEX * 4, path.getFillColor());
- properties.putFloat(VFullPath_Delegate.FILL_ALPHA_INDEX * 4, path.getStrokeAlpha());
- properties.putFloat(VFullPath_Delegate.TRIM_PATH_START_INDEX * 4, path.getTrimPathStart());
- properties.putFloat(VFullPath_Delegate.TRIM_PATH_END_INDEX * 4, path.getTrimPathEnd());
- properties.putFloat(VFullPath_Delegate.TRIM_PATH_OFFSET_INDEX * 4,
- path.getTrimPathOffset());
- properties.putInt(VFullPath_Delegate.STROKE_LINE_CAP_INDEX * 4, path.getStrokeLineCap());
- properties.putInt(VFullPath_Delegate.STROKE_LINE_JOIN_INDEX * 4, path.getStrokeLineJoin());
- properties.putFloat(VFullPath_Delegate.STROKE_MITER_LIMIT_INDEX * 4,
- path.getStrokeMiterlimit());
- properties.putInt(VFullPath_Delegate.FILL_TYPE_INDEX * 4, path.getFillType());
-
- return true;
- }
-
- @LayoutlibDelegate
- static void nUpdateFullPathProperties(long pathPtr, float strokeWidth,
- int strokeColor, float strokeAlpha, int fillColor, float fillAlpha, float trimPathStart,
- float trimPathEnd, float trimPathOffset, float strokeMiterLimit, int strokeLineCap,
- int strokeLineJoin, int fillType) {
- VFullPath_Delegate path = VNativeObject.getDelegate(pathPtr);
-
- path.setStrokeWidth(strokeWidth);
- path.setStrokeColor(strokeColor);
- path.setStrokeAlpha(strokeAlpha);
- path.setFillColor(fillColor);
- path.setFillAlpha(fillAlpha);
- path.setTrimPathStart(trimPathStart);
- path.setTrimPathEnd(trimPathEnd);
- path.setTrimPathOffset(trimPathOffset);
- path.setStrokeMiterlimit(strokeMiterLimit);
- path.setStrokeLineCap(strokeLineCap);
- path.setStrokeLineJoin(strokeLineJoin);
- path.setFillType(fillType);
- }
-
- @LayoutlibDelegate
- static void nUpdateFullPathFillGradient(long pathPtr, long fillGradientPtr) {
- VFullPath_Delegate path = VNativeObject.getDelegate(pathPtr);
-
- path.setFillGradient(fillGradientPtr);
- }
-
- @LayoutlibDelegate
- static void nUpdateFullPathStrokeGradient(long pathPtr, long strokeGradientPtr) {
- VFullPath_Delegate path = VNativeObject.getDelegate(pathPtr);
-
- path.setStrokeGradient(strokeGradientPtr);
- }
-
- @LayoutlibDelegate
- static long nCreateClipPath() {
- return addNativeObject(new VClipPath_Delegate());
- }
-
- @LayoutlibDelegate
- static long nCreateClipPath(long clipPathPtr) {
- VClipPath_Delegate original = VNativeObject.getDelegate(clipPathPtr);
- return addNativeObject(new VClipPath_Delegate(original));
- }
-
- @LayoutlibDelegate
- static long nCreateGroup() {
- return addNativeObject(new VGroup_Delegate());
- }
-
- @LayoutlibDelegate
- static long nCreateGroup(long groupPtr) {
- VGroup_Delegate original = VNativeObject.getDelegate(groupPtr);
- return addNativeObject(new VGroup_Delegate(original, new ArrayMap<>()));
- }
-
- @LayoutlibDelegate
- static void nSetName(long nodePtr, String name) {
- VNativeObject group = VNativeObject.getDelegate(nodePtr);
- group.setName(name);
- }
-
- @LayoutlibDelegate
- static boolean nGetGroupProperties(long groupPtr, float[] propertiesData,
- int length) {
- VGroup_Delegate group = VNativeObject.getDelegate(groupPtr);
-
- FloatBuffer properties = FloatBuffer.wrap(propertiesData);
-
- properties.put(VGroup_Delegate.ROTATE_INDEX, group.getRotation());
- properties.put(VGroup_Delegate.PIVOT_X_INDEX, group.getPivotX());
- properties.put(VGroup_Delegate.PIVOT_Y_INDEX, group.getPivotY());
- properties.put(VGroup_Delegate.SCALE_X_INDEX, group.getScaleX());
- properties.put(VGroup_Delegate.SCALE_Y_INDEX, group.getScaleY());
- properties.put(VGroup_Delegate.TRANSLATE_X_INDEX, group.getTranslateX());
- properties.put(VGroup_Delegate.TRANSLATE_Y_INDEX, group.getTranslateY());
-
- return true;
- }
- @LayoutlibDelegate
- static void nUpdateGroupProperties(long groupPtr, float rotate, float pivotX,
- float pivotY, float scaleX, float scaleY, float translateX, float translateY) {
- VGroup_Delegate group = VNativeObject.getDelegate(groupPtr);
-
- group.setRotation(rotate);
- group.setPivotX(pivotX);
- group.setPivotY(pivotY);
- group.setScaleX(scaleX);
- group.setScaleY(scaleY);
- group.setTranslateX(translateX);
- group.setTranslateY(translateY);
- }
-
- @LayoutlibDelegate
- static void nAddChild(long groupPtr, long nodePtr) {
- VGroup_Delegate group = VNativeObject.getDelegate(groupPtr);
- group.mChildren.add(VNativeObject.getDelegate(nodePtr));
- }
-
- @LayoutlibDelegate
- static void nSetPathString(long pathPtr, String pathString, int length) {
- VPath_Delegate path = VNativeObject.getDelegate(pathPtr);
- path.setPathData(PathParser_Delegate.createNodesFromPathData(pathString));
- }
-
- /**
- * The setters and getters below for paths and groups are here temporarily, and will be removed
- * once the animation in AVD is replaced with RenderNodeAnimator, in which case the animation
- * will modify these properties in native. By then no JNI hopping would be necessary for VD
- * during animation, and these setters and getters will be obsolete.
- */
- // Setters and getters during animation.
- @LayoutlibDelegate
- static float nGetRotation(long groupPtr) {
- VGroup_Delegate group = VNativeObject.getDelegate(groupPtr);
- return group.getRotation();
- }
-
- @LayoutlibDelegate
- static void nSetRotation(long groupPtr, float rotation) {
- VGroup_Delegate group = VNativeObject.getDelegate(groupPtr);
- group.setRotation(rotation);
- }
-
- @LayoutlibDelegate
- static float nGetPivotX(long groupPtr) {
- VGroup_Delegate group = VNativeObject.getDelegate(groupPtr);
- return group.getPivotX();
- }
-
- @LayoutlibDelegate
- static void nSetPivotX(long groupPtr, float pivotX) {
- VGroup_Delegate group = VNativeObject.getDelegate(groupPtr);
- group.setPivotX(pivotX);
- }
-
- @LayoutlibDelegate
- static float nGetPivotY(long groupPtr) {
- VGroup_Delegate group = VNativeObject.getDelegate(groupPtr);
- return group.getPivotY();
- }
-
- @LayoutlibDelegate
- static void nSetPivotY(long groupPtr, float pivotY) {
- VGroup_Delegate group = VNativeObject.getDelegate(groupPtr);
- group.setPivotY(pivotY);
- }
-
- @LayoutlibDelegate
- static float nGetScaleX(long groupPtr) {
- VGroup_Delegate group = VNativeObject.getDelegate(groupPtr);
- return group.getScaleX();
- }
-
- @LayoutlibDelegate
- static void nSetScaleX(long groupPtr, float scaleX) {
- VGroup_Delegate group = VNativeObject.getDelegate(groupPtr);
- group.setScaleX(scaleX);
- }
-
- @LayoutlibDelegate
- static float nGetScaleY(long groupPtr) {
- VGroup_Delegate group = VNativeObject.getDelegate(groupPtr);
- return group.getScaleY();
- }
-
- @LayoutlibDelegate
- static void nSetScaleY(long groupPtr, float scaleY) {
- VGroup_Delegate group = VNativeObject.getDelegate(groupPtr);
- group.setScaleY(scaleY);
- }
-
- @LayoutlibDelegate
- static float nGetTranslateX(long groupPtr) {
- VGroup_Delegate group = VNativeObject.getDelegate(groupPtr);
- return group.getTranslateX();
- }
-
- @LayoutlibDelegate
- static void nSetTranslateX(long groupPtr, float translateX) {
- VGroup_Delegate group = VNativeObject.getDelegate(groupPtr);
- group.setTranslateX(translateX);
- }
-
- @LayoutlibDelegate
- static float nGetTranslateY(long groupPtr) {
- VGroup_Delegate group = VNativeObject.getDelegate(groupPtr);
- return group.getTranslateY();
- }
-
- @LayoutlibDelegate
- static void nSetTranslateY(long groupPtr, float translateY) {
- VGroup_Delegate group = VNativeObject.getDelegate(groupPtr);
- group.setTranslateY(translateY);
- }
-
- @LayoutlibDelegate
- static void nSetPathData(long pathPtr, long pathDataPtr) {
- VPath_Delegate path = VNativeObject.getDelegate(pathPtr);
- path.setPathData(PathParser_Delegate.getDelegate(pathDataPtr).getPathDataNodes());
- }
-
- @LayoutlibDelegate
- static float nGetStrokeWidth(long pathPtr) {
- VFullPath_Delegate path = VNativeObject.getDelegate(pathPtr);
- return path.getStrokeWidth();
- }
-
- @LayoutlibDelegate
- static void nSetStrokeWidth(long pathPtr, float width) {
- VFullPath_Delegate path = VNativeObject.getDelegate(pathPtr);
- path.setStrokeWidth(width);
- }
-
- @LayoutlibDelegate
- static int nGetStrokeColor(long pathPtr) {
- VFullPath_Delegate path = VNativeObject.getDelegate(pathPtr);
- return path.getStrokeColor();
- }
-
- @LayoutlibDelegate
- static void nSetStrokeColor(long pathPtr, int strokeColor) {
- VFullPath_Delegate path = VNativeObject.getDelegate(pathPtr);
- path.setStrokeColor(strokeColor);
- }
-
- @LayoutlibDelegate
- static float nGetStrokeAlpha(long pathPtr) {
- VFullPath_Delegate path = VNativeObject.getDelegate(pathPtr);
- return path.getStrokeAlpha();
- }
-
- @LayoutlibDelegate
- static void nSetStrokeAlpha(long pathPtr, float alpha) {
- VFullPath_Delegate path = VNativeObject.getDelegate(pathPtr);
- path.setStrokeAlpha(alpha);
- }
-
- @LayoutlibDelegate
- static int nGetFillColor(long pathPtr) {
- VFullPath_Delegate path = VNativeObject.getDelegate(pathPtr);
- return path.getFillColor();
- }
-
- @LayoutlibDelegate
- static void nSetFillColor(long pathPtr, int fillColor) {
- VFullPath_Delegate path = VNativeObject.getDelegate(pathPtr);
- path.setFillColor(fillColor);
- }
-
- @LayoutlibDelegate
- static float nGetFillAlpha(long pathPtr) {
- VFullPath_Delegate path = VNativeObject.getDelegate(pathPtr);
- return path.getFillAlpha();
- }
-
- @LayoutlibDelegate
- static void nSetFillAlpha(long pathPtr, float fillAlpha) {
- VFullPath_Delegate path = VNativeObject.getDelegate(pathPtr);
- path.setFillAlpha(fillAlpha);
- }
-
- @LayoutlibDelegate
- static float nGetTrimPathStart(long pathPtr) {
- VFullPath_Delegate path = VNativeObject.getDelegate(pathPtr);
- return path.getTrimPathStart();
- }
-
- @LayoutlibDelegate
- static void nSetTrimPathStart(long pathPtr, float trimPathStart) {
- VFullPath_Delegate path = VNativeObject.getDelegate(pathPtr);
- path.setTrimPathStart(trimPathStart);
- }
-
- @LayoutlibDelegate
- static float nGetTrimPathEnd(long pathPtr) {
- VFullPath_Delegate path = VNativeObject.getDelegate(pathPtr);
- return path.getTrimPathEnd();
- }
-
- @LayoutlibDelegate
- static void nSetTrimPathEnd(long pathPtr, float trimPathEnd) {
- VFullPath_Delegate path = VNativeObject.getDelegate(pathPtr);
- path.setTrimPathEnd(trimPathEnd);
- }
-
- @LayoutlibDelegate
- static float nGetTrimPathOffset(long pathPtr) {
- VFullPath_Delegate path = VNativeObject.getDelegate(pathPtr);
- return path.getTrimPathOffset();
- }
-
- @LayoutlibDelegate
- static void nSetTrimPathOffset(long pathPtr, float trimPathOffset) {
- VFullPath_Delegate path = VNativeObject.getDelegate(pathPtr);
- path.setTrimPathOffset(trimPathOffset);
- }
-
- /**
- * Base class for all the internal Delegates that does two functions:
- * <ol>
- * <li>Serves as base class to store all the delegates in one {@link DelegateManager}
- * <li>Provides setName for all the classes. {@link VPathRenderer_Delegate} does actually
- * not need it
- * </ol>
- */
- abstract static class VNativeObject {
- long mNativePtr = 0;
-
- @NonNull
- static <T> T getDelegate(long nativePtr) {
- //noinspection unchecked
- T vNativeObject = (T) sPathManager.getDelegate(nativePtr);
-
- assert vNativeObject != null;
- return vNativeObject;
- }
-
- abstract void setName(String name);
-
- void setNativePtr(long nativePtr) {
- mNativePtr = nativePtr;
- }
-
- /**
- * Method to explicitly dispose native objects
- */
- void dispose() {
- }
- }
-
- private static class VClipPath_Delegate extends VPath_Delegate {
- private VClipPath_Delegate() {
- // Empty constructor.
- }
-
- private VClipPath_Delegate(VClipPath_Delegate copy) {
- super(copy);
- }
-
- @Override
- public boolean isClipPath() {
- return true;
- }
- }
-
- static class VFullPath_Delegate extends VPath_Delegate {
- // These constants need to be kept in sync with their values in VectorDrawable.VFullPath
- private static final int STROKE_WIDTH_INDEX = 0;
- private static final int STROKE_COLOR_INDEX = 1;
- private static final int STROKE_ALPHA_INDEX = 2;
- private static final int FILL_COLOR_INDEX = 3;
- private static final int FILL_ALPHA_INDEX = 4;
- private static final int TRIM_PATH_START_INDEX = 5;
- private static final int TRIM_PATH_END_INDEX = 6;
- private static final int TRIM_PATH_OFFSET_INDEX = 7;
- private static final int STROKE_LINE_CAP_INDEX = 8;
- private static final int STROKE_LINE_JOIN_INDEX = 9;
- private static final int STROKE_MITER_LIMIT_INDEX = 10;
- private static final int FILL_TYPE_INDEX = 11;
-
- private static final int LINECAP_BUTT = 0;
- private static final int LINECAP_ROUND = 1;
- private static final int LINECAP_SQUARE = 2;
-
- private static final int LINEJOIN_MITER = 0;
- private static final int LINEJOIN_ROUND = 1;
- private static final int LINEJOIN_BEVEL = 2;
-
- @NonNull
- public Consumer<Float> getFloatPropertySetter(int propertyIdx) {
- switch (propertyIdx) {
- case STROKE_WIDTH_INDEX:
- return this::setStrokeWidth;
- case STROKE_ALPHA_INDEX:
- return this::setStrokeAlpha;
- case FILL_ALPHA_INDEX:
- return this::setFillAlpha;
- case TRIM_PATH_START_INDEX:
- return this::setTrimPathStart;
- case TRIM_PATH_END_INDEX:
- return this::setTrimPathEnd;
- case TRIM_PATH_OFFSET_INDEX:
- return this::setTrimPathOffset;
- }
-
- assert false : ("Invalid VFullPath_Delegate property index " + propertyIdx);
- return t -> {};
- }
-
- @NonNull
- public Consumer<Integer> getIntPropertySetter(int propertyIdx) {
- switch (propertyIdx) {
- case STROKE_COLOR_INDEX:
- return this::setStrokeColor;
- case FILL_COLOR_INDEX:
- return this::setFillColor;
- }
-
- assert false : ("Invalid VFullPath_Delegate property index " + propertyIdx);
- return t -> {};
- }
-
- /////////////////////////////////////////////////////
- // Variables below need to be copied (deep copy if applicable) for mutation.
-
- int mStrokeColor = Color.TRANSPARENT;
- float mStrokeWidth = 0;
-
- int mFillColor = Color.TRANSPARENT;
- long mStrokeGradient = 0;
- long mFillGradient = 0;
- float mStrokeAlpha = 1.0f;
- float mFillAlpha = 1.0f;
- float mTrimPathStart = 0;
- float mTrimPathEnd = 1;
- float mTrimPathOffset = 0;
-
- Cap mStrokeLineCap = BUTT;
- Join mStrokeLineJoin = MITER;
- float mStrokeMiterlimit = 4;
-
- int mFillType = 0; // WINDING(0) is the default value. See Path.FillType
-
- private VFullPath_Delegate() {
- // Empty constructor.
- }
-
- private VFullPath_Delegate(VFullPath_Delegate copy) {
- super(copy);
-
- mStrokeColor = copy.mStrokeColor;
- mStrokeWidth = copy.mStrokeWidth;
- mStrokeAlpha = copy.mStrokeAlpha;
- mFillColor = copy.mFillColor;
- mFillAlpha = copy.mFillAlpha;
- mTrimPathStart = copy.mTrimPathStart;
- mTrimPathEnd = copy.mTrimPathEnd;
- mTrimPathOffset = copy.mTrimPathOffset;
-
- mStrokeLineCap = copy.mStrokeLineCap;
- mStrokeLineJoin = copy.mStrokeLineJoin;
- mStrokeMiterlimit = copy.mStrokeMiterlimit;
-
- mStrokeGradient = copy.mStrokeGradient;
- mFillGradient = copy.mFillGradient;
- mFillType = copy.mFillType;
- }
-
- private int getStrokeLineCap() {
- switch (mStrokeLineCap) {
- case BUTT:
- return LINECAP_BUTT;
- case ROUND:
- return LINECAP_ROUND;
- case SQUARE:
- return LINECAP_SQUARE;
- default:
- assert false;
- }
-
- return -1;
- }
-
- private void setStrokeLineCap(int cap) {
- switch (cap) {
- case LINECAP_BUTT:
- mStrokeLineCap = BUTT;
- break;
- case LINECAP_ROUND:
- mStrokeLineCap = ROUND;
- break;
- case LINECAP_SQUARE:
- mStrokeLineCap = SQUARE;
- break;
- default:
- assert false;
- }
- }
-
- private int getStrokeLineJoin() {
- switch (mStrokeLineJoin) {
- case MITER:
- return LINEJOIN_MITER;
- case ROUND:
- return LINEJOIN_ROUND;
- case BEVEL:
- return LINEJOIN_BEVEL;
- default:
- assert false;
- }
-
- return -1;
- }
-
- private void setStrokeLineJoin(int join) {
- switch (join) {
- case LINEJOIN_BEVEL:
- mStrokeLineJoin = BEVEL;
- break;
- case LINEJOIN_MITER:
- mStrokeLineJoin = MITER;
- break;
- case LINEJOIN_ROUND:
- mStrokeLineJoin = Join.ROUND;
- break;
- default:
- assert false;
- }
- }
-
- private int getStrokeColor() {
- return mStrokeColor;
- }
-
- private void setStrokeColor(int strokeColor) {
- mStrokeColor = strokeColor;
- }
-
- private float getStrokeWidth() {
- return mStrokeWidth;
- }
-
- private void setStrokeWidth(float strokeWidth) {
- mStrokeWidth = strokeWidth;
- }
-
- private float getStrokeAlpha() {
- return mStrokeAlpha;
- }
-
- private void setStrokeAlpha(float strokeAlpha) {
- mStrokeAlpha = strokeAlpha;
- }
-
- private int getFillColor() {
- return mFillColor;
- }
-
- private void setFillColor(int fillColor) {
- mFillColor = fillColor;
- }
-
- private float getFillAlpha() {
- return mFillAlpha;
- }
-
- private void setFillAlpha(float fillAlpha) {
- mFillAlpha = fillAlpha;
- }
-
- private float getTrimPathStart() {
- return mTrimPathStart;
- }
-
- private void setTrimPathStart(float trimPathStart) {
- mTrimPathStart = trimPathStart;
- }
-
- private float getTrimPathEnd() {
- return mTrimPathEnd;
- }
-
- private void setTrimPathEnd(float trimPathEnd) {
- mTrimPathEnd = trimPathEnd;
- }
-
- private float getTrimPathOffset() {
- return mTrimPathOffset;
- }
-
- private void setTrimPathOffset(float trimPathOffset) {
- mTrimPathOffset = trimPathOffset;
- }
-
- private void setStrokeMiterlimit(float limit) {
- mStrokeMiterlimit = limit;
- }
-
- private float getStrokeMiterlimit() {
- return mStrokeMiterlimit;
- }
-
- private void setStrokeGradient(long gradientPtr) {
- mStrokeGradient = gradientPtr;
- }
-
- private void setFillGradient(long gradientPtr) {
- mFillGradient = gradientPtr;
- }
-
- private void setFillType(int fillType) {
- mFillType = fillType;
- }
-
- private int getFillType() {
- return mFillType;
- }
- }
-
- static class VGroup_Delegate extends VNativeObject {
- // This constants need to be kept in sync with their definitions in VectorDrawable.Group
- private static final int ROTATE_INDEX = 0;
- private static final int PIVOT_X_INDEX = 1;
- private static final int PIVOT_Y_INDEX = 2;
- private static final int SCALE_X_INDEX = 3;
- private static final int SCALE_Y_INDEX = 4;
- private static final int TRANSLATE_X_INDEX = 5;
- private static final int TRANSLATE_Y_INDEX = 6;
-
- public Consumer<Float> getPropertySetter(int propertyIdx) {
- switch (propertyIdx) {
- case ROTATE_INDEX:
- return this::setRotation;
- case PIVOT_X_INDEX:
- return this::setPivotX;
- case PIVOT_Y_INDEX:
- return this::setPivotY;
- case SCALE_X_INDEX:
- return this::setScaleX;
- case SCALE_Y_INDEX:
- return this::setScaleY;
- case TRANSLATE_X_INDEX:
- return this::setTranslateX;
- case TRANSLATE_Y_INDEX:
- return this::setTranslateY;
- }
-
- assert false : ("Invalid VGroup_Delegate property index " + propertyIdx);
- return t -> {};
- }
-
- /////////////////////////////////////////////////////
- // Variables below need to be copied (deep copy if applicable) for mutation.
- final ArrayList<Object> mChildren = new ArrayList<>();
- // mStackedMatrix is only used temporarily when drawing, it combines all
- // the parents' local matrices with the current one.
- private final Matrix mStackedMatrix = new Matrix();
- // mLocalMatrix is updated based on the update of transformation information,
- // either parsed from the XML or by animation.
- private final Matrix mLocalMatrix = new Matrix();
- private float mRotate = 0;
- private float mPivotX = 0;
- private float mPivotY = 0;
- private float mScaleX = 1;
- private float mScaleY = 1;
- private float mTranslateX = 0;
- private float mTranslateY = 0;
- private int mChangingConfigurations;
- private String mGroupName = null;
-
- private VGroup_Delegate(VGroup_Delegate copy, ArrayMap<String, Object> targetsMap) {
- mRotate = copy.mRotate;
- mPivotX = copy.mPivotX;
- mPivotY = copy.mPivotY;
- mScaleX = copy.mScaleX;
- mScaleY = copy.mScaleY;
- mTranslateX = copy.mTranslateX;
- mTranslateY = copy.mTranslateY;
- mGroupName = copy.mGroupName;
- mChangingConfigurations = copy.mChangingConfigurations;
- if (mGroupName != null) {
- targetsMap.put(mGroupName, this);
- }
-
- mLocalMatrix.set(copy.mLocalMatrix);
- }
-
- private VGroup_Delegate() {
- }
-
- private void updateLocalMatrix() {
- // The order we apply is the same as the
- // RenderNode.cpp::applyViewPropertyTransforms().
- mLocalMatrix.reset();
- mLocalMatrix.postTranslate(-mPivotX, -mPivotY);
- mLocalMatrix.postScale(mScaleX, mScaleY);
- mLocalMatrix.postRotate(mRotate, 0, 0);
- mLocalMatrix.postTranslate(mTranslateX + mPivotX, mTranslateY + mPivotY);
- }
-
- /* Setters and Getters, used by animator from AnimatedVectorDrawable. */
- private float getRotation() {
- return mRotate;
- }
-
- private void setRotation(float rotation) {
- if (rotation != mRotate) {
- mRotate = rotation;
- updateLocalMatrix();
- }
- }
-
- private float getPivotX() {
- return mPivotX;
- }
-
- private void setPivotX(float pivotX) {
- if (pivotX != mPivotX) {
- mPivotX = pivotX;
- updateLocalMatrix();
- }
- }
-
- private float getPivotY() {
- return mPivotY;
- }
-
- private void setPivotY(float pivotY) {
- if (pivotY != mPivotY) {
- mPivotY = pivotY;
- updateLocalMatrix();
- }
- }
-
- private float getScaleX() {
- return mScaleX;
- }
-
- private void setScaleX(float scaleX) {
- if (scaleX != mScaleX) {
- mScaleX = scaleX;
- updateLocalMatrix();
- }
- }
-
- private float getScaleY() {
- return mScaleY;
- }
-
- private void setScaleY(float scaleY) {
- if (scaleY != mScaleY) {
- mScaleY = scaleY;
- updateLocalMatrix();
- }
- }
-
- private float getTranslateX() {
- return mTranslateX;
- }
-
- private void setTranslateX(float translateX) {
- if (translateX != mTranslateX) {
- mTranslateX = translateX;
- updateLocalMatrix();
- }
- }
-
- private float getTranslateY() {
- return mTranslateY;
- }
-
- private void setTranslateY(float translateY) {
- if (translateY != mTranslateY) {
- mTranslateY = translateY;
- updateLocalMatrix();
- }
- }
-
- @Override
- public void setName(String name) {
- mGroupName = name;
- }
-
- @Override
- protected void dispose() {
- mChildren.stream().filter(child -> child instanceof VNativeObject).forEach(child
- -> {
- VNativeObject nativeObject = (VNativeObject) child;
- if (nativeObject.mNativePtr != 0) {
- sPathManager.removeJavaReferenceFor(nativeObject.mNativePtr);
- nativeObject.mNativePtr = 0;
- }
- nativeObject.dispose();
- });
- mChildren.clear();
- }
-
- @Override
- protected void finalize() throws Throwable {
- super.finalize();
- }
- }
-
- public static class VPath_Delegate extends VNativeObject {
- protected PathParser_Delegate.PathDataNode[] mNodes = null;
- String mPathName;
- int mChangingConfigurations;
-
- public VPath_Delegate() {
- // Empty constructor.
- }
-
- public VPath_Delegate(VPath_Delegate copy) {
- mPathName = copy.mPathName;
- mChangingConfigurations = copy.mChangingConfigurations;
- mNodes = copy.mNodes != null ? PathParser_Delegate.deepCopyNodes(copy.mNodes) : null;
- }
-
- public void toPath(Path path) {
- path.reset();
- if (mNodes != null) {
- PathParser_Delegate.PathDataNode.nodesToPath(mNodes,
- Path_Delegate.getDelegate(path.mNativePath));
- }
- }
-
- @Override
- public void setName(String name) {
- mPathName = name;
- }
-
- public boolean isClipPath() {
- return false;
- }
-
- private void setPathData(PathParser_Delegate.PathDataNode[] nodes) {
- if (!PathParser_Delegate.canMorph(mNodes, nodes)) {
- // This should not happen in the middle of animation.
- mNodes = PathParser_Delegate.deepCopyNodes(nodes);
- } else {
- PathParser_Delegate.updateNodes(mNodes, nodes);
- }
- }
-
- @Override
- void dispose() {
- mNodes = null;
- }
- }
-
- static class VPathRenderer_Delegate extends VNativeObject {
- /* Right now the internal data structure is organized as a tree.
- * Each node can be a group node, or a path.
- * A group node can have groups or paths as children, but a path node has
- * no children.
- * One example can be:
- * Root Group
- * / | \
- * Group Path Group
- * / \ |
- * Path Path Path
- *
- */
- // Variables that only used temporarily inside the draw() call, so there
- // is no need for deep copying.
- private final Path mPath;
- private final Path mRenderPath;
- private final Matrix mFinalPathMatrix = new Matrix();
- private final long mRootGroupPtr;
- private float mViewportWidth = 0;
- private float mViewportHeight = 0;
- private float mRootAlpha = 1.0f;
- private Paint mStrokePaint;
- private Paint mFillPaint;
- private PathMeasure mPathMeasure;
- private boolean mAntiAlias = true;
-
- private VPathRenderer_Delegate(long rootGroupPtr) {
- mRootGroupPtr = rootGroupPtr;
- mPath = new Path();
- mRenderPath = new Path();
- }
-
- private VPathRenderer_Delegate(VPathRenderer_Delegate rendererToCopy,
- long rootGroupPtr) {
- this(rootGroupPtr);
- mViewportWidth = rendererToCopy.mViewportWidth;
- mViewportHeight = rendererToCopy.mViewportHeight;
- mRootAlpha = rendererToCopy.mRootAlpha;
- }
-
- private float getRootAlpha() {
- return mRootAlpha;
- }
-
- void setRootAlpha(float alpha) {
- mRootAlpha = alpha;
- }
-
- private void drawGroupTree(VGroup_Delegate currentGroup, Matrix currentMatrix,
- long canvasPtr, int w, int h, long filterPtr) {
- // Calculate current group's matrix by preConcat the parent's and
- // and the current one on the top of the stack.
- // Basically the Mfinal = Mviewport * M0 * M1 * M2;
- // Mi the local matrix at level i of the group tree.
- currentGroup.mStackedMatrix.set(currentMatrix);
- currentGroup.mStackedMatrix.preConcat(currentGroup.mLocalMatrix);
-
- // Save the current clip information, which is local to this group.
- Canvas_Delegate.nSave(canvasPtr, MATRIX_SAVE_FLAG | CLIP_SAVE_FLAG);
- // Draw the group tree in the same order as the XML file.
- for (int i = 0; i < currentGroup.mChildren.size(); i++) {
- Object child = currentGroup.mChildren.get(i);
- if (child instanceof VGroup_Delegate) {
- VGroup_Delegate childGroup = (VGroup_Delegate) child;
- drawGroupTree(childGroup, currentGroup.mStackedMatrix,
- canvasPtr, w, h, filterPtr);
- } else if (child instanceof VPath_Delegate) {
- VPath_Delegate childPath = (VPath_Delegate) child;
- drawPath(currentGroup, childPath, canvasPtr, w, h, filterPtr);
- }
- }
- Canvas_Delegate.nRestore(canvasPtr);
- }
-
- public void draw(long canvasPtr, long filterPtr, int w, int h) {
- // Traverse the tree in pre-order to draw.
- drawGroupTree(VNativeObject.getDelegate(mRootGroupPtr), Matrix.IDENTITY_MATRIX, canvasPtr, w, h, filterPtr);
- }
-
- private void drawPath(VGroup_Delegate VGroup, VPath_Delegate VPath, long canvasPtr,
- int w,
- int h,
- long filterPtr) {
- final float scaleX = w / mViewportWidth;
- final float scaleY = h / mViewportHeight;
- final float minScale = Math.min(scaleX, scaleY);
- final Matrix groupStackedMatrix = VGroup.mStackedMatrix;
-
- mFinalPathMatrix.set(groupStackedMatrix);
- mFinalPathMatrix.postScale(scaleX, scaleY);
-
- final float matrixScale = getMatrixScale(groupStackedMatrix);
- if (matrixScale == 0) {
- // When either x or y is scaled to 0, we don't need to draw anything.
- return;
- }
- VPath.toPath(mPath);
- final Path path = mPath;
-
- mRenderPath.reset();
-
- if (VPath.isClipPath()) {
- mRenderPath.setFillType(FillType.WINDING);
- mRenderPath.addPath(path, mFinalPathMatrix);
- Canvas_Delegate.nClipPath(canvasPtr, mRenderPath.mNativePath, Op
- .INTERSECT.nativeInt);
- } else {
- VFullPath_Delegate fullPath = (VFullPath_Delegate) VPath;
- if (fullPath.mTrimPathStart != 0.0f || fullPath.mTrimPathEnd != 1.0f) {
- float start = (fullPath.mTrimPathStart + fullPath.mTrimPathOffset) % 1.0f;
- float end = (fullPath.mTrimPathEnd + fullPath.mTrimPathOffset) % 1.0f;
-
- if (mPathMeasure == null) {
- mPathMeasure = new PathMeasure();
- }
- mPathMeasure.setPath(mPath, false);
-
- float len = mPathMeasure.getLength();
- start = start * len;
- end = end * len;
- path.reset();
- if (start > end) {
- mPathMeasure.getSegment(start, len, path, true);
- mPathMeasure.getSegment(0f, end, path, true);
- } else {
- mPathMeasure.getSegment(start, end, path, true);
- }
- path.rLineTo(0, 0); // fix bug in measure
- }
- mRenderPath.addPath(path, mFinalPathMatrix);
-
- if (fullPath.mFillColor != Color.TRANSPARENT) {
- if (mFillPaint == null) {
- mFillPaint = new Paint();
- mFillPaint.setStyle(Style.FILL);
- mFillPaint.setAntiAlias(mAntiAlias);
- }
-
- final Paint fillPaint = mFillPaint;
- fillPaint.setColor(applyAlpha(applyAlpha(fullPath.mFillColor, fullPath
- .mFillAlpha), getRootAlpha()));
- Paint_Delegate fillPaintDelegate = Paint_Delegate.getDelegate(fillPaint
- .getNativeInstance());
- // mFillPaint can not be null at this point so we will have a delegate
- assert fillPaintDelegate != null;
- fillPaintDelegate.setColorFilter(filterPtr);
-
- Shader_Delegate shaderDelegate =
- Shader_Delegate.getDelegate(fullPath.mFillGradient);
- if (shaderDelegate != null) {
- // If there is a shader, apply the local transformation to make sure
- // the gradient is transformed to match the viewport
- shaderDelegate.setLocalMatrix(mFinalPathMatrix.ni());
- shaderDelegate.setAlpha(fullPath.mFillAlpha);
- }
-
- fillPaintDelegate.setShader(fullPath.mFillGradient);
- Path_Delegate.nSetFillType(mRenderPath.mNativePath, fullPath.mFillType);
- BaseCanvas_Delegate.nDrawPath(canvasPtr, mRenderPath.mNativePath, fillPaint
- .getNativeInstance());
- if (shaderDelegate != null) {
- // Remove the local matrix
- shaderDelegate.setLocalMatrix(0);
- }
- }
-
- if (fullPath.mStrokeColor != Color.TRANSPARENT) {
- if (mStrokePaint == null) {
- mStrokePaint = new Paint();
- mStrokePaint.setStyle(Style.STROKE);
- mStrokePaint.setAntiAlias(mAntiAlias);
- }
-
- final Paint strokePaint = mStrokePaint;
- if (fullPath.mStrokeLineJoin != null) {
- strokePaint.setStrokeJoin(fullPath.mStrokeLineJoin);
- }
-
- if (fullPath.mStrokeLineCap != null) {
- strokePaint.setStrokeCap(fullPath.mStrokeLineCap);
- }
-
- strokePaint.setStrokeMiter(fullPath.mStrokeMiterlimit);
- strokePaint.setColor(applyAlpha(applyAlpha(fullPath.mStrokeColor, fullPath
- .mStrokeAlpha), getRootAlpha()));
- Paint_Delegate strokePaintDelegate = Paint_Delegate.getDelegate(strokePaint
- .getNativeInstance());
- // mStrokePaint can not be null at this point so we will have a delegate
- assert strokePaintDelegate != null;
- strokePaintDelegate.setColorFilter(filterPtr);
- final float finalStrokeScale = minScale * matrixScale;
- strokePaint.setStrokeWidth(fullPath.mStrokeWidth * finalStrokeScale);
- Shader_Delegate strokeShaderDelegate =
- Shader_Delegate.getDelegate(fullPath.mStrokeGradient);
- if (strokeShaderDelegate != null) {
- strokeShaderDelegate.setAlpha(fullPath.mStrokeAlpha);
- }
- strokePaintDelegate.setShader(fullPath.mStrokeGradient);
- BaseCanvas_Delegate.nDrawPath(canvasPtr, mRenderPath.mNativePath, strokePaint
- .getNativeInstance());
- }
- }
- }
-
- private float getMatrixScale(Matrix groupStackedMatrix) {
- // Given unit vectors A = (0, 1) and B = (1, 0).
- // After matrix mapping, we got A' and B'. Let theta = the angel b/t A' and B'.
- // Therefore, the final scale we want is min(|A'| * sin(theta), |B'| * sin(theta)),
- // which is (|A'| * |B'| * sin(theta)) / max (|A'|, |B'|);
- // If max (|A'|, |B'|) = 0, that means either x or y has a scale of 0.
- //
- // For non-skew case, which is most of the cases, matrix scale is computing exactly the
- // scale on x and y axis, and take the minimal of these two.
- // For skew case, an unit square will mapped to a parallelogram. And this function will
- // return the minimal height of the 2 bases.
- float[] unitVectors = new float[]{0, 1, 1, 0};
- groupStackedMatrix.mapVectors(unitVectors);
- float scaleX = MathUtils.mag(unitVectors[0], unitVectors[1]);
- float scaleY = MathUtils.mag(unitVectors[2], unitVectors[3]);
- float crossProduct = MathUtils.cross(unitVectors[0], unitVectors[1],
- unitVectors[2], unitVectors[3]);
- float maxScale = MathUtils.max(scaleX, scaleY);
-
- float matrixScale = 0;
- if (maxScale > 0) {
- matrixScale = MathUtils.abs(crossProduct) / maxScale;
- }
- if (DBG_VECTOR_DRAWABLE) {
- Log.d(LOGTAG, "Scale x " + scaleX + " y " + scaleY + " final " + matrixScale);
- }
- return matrixScale;
- }
-
- private void setAntiAlias(boolean aa) {
- mAntiAlias = aa;
- }
-
- @Override
- public void setName(String name) {
- }
-
- @Override
- protected void finalize() throws Throwable {
- // The mRootGroupPtr is not explicitly freed by anything in the VectorDrawable so we
- // need to free it here.
- VNativeObject nativeObject = sPathManager.getDelegate(mRootGroupPtr);
- sPathManager.removeJavaReferenceFor(mRootGroupPtr);
- assert nativeObject != null;
- nativeObject.dispose();
-
- super.finalize();
- }
- }
-}
diff --git a/bridge/src/android/graphics/fonts/FontFamily_Builder_Delegate.java b/bridge/src/android/graphics/fonts/FontFamily_Builder_Delegate.java
deleted file mode 100644
index 018329dc2e..0000000000
--- a/bridge/src/android/graphics/fonts/FontFamily_Builder_Delegate.java
+++ /dev/null
@@ -1,210 +0,0 @@
-/*
- * 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.
- */
-
-package android.graphics.fonts;
-
-import com.android.ide.common.rendering.api.ILayoutLog;
-import com.android.layoutlib.bridge.Bridge;
-import com.android.layoutlib.bridge.impl.DelegateManager;
-import com.android.tools.layoutlib.annotations.LayoutlibDelegate;
-
-import android.annotation.NonNull;
-import android.annotation.Nullable;
-import android.graphics.FontFamily_Delegate.FontInfo;
-import android.graphics.FontFamily_Delegate.FontVariant;
-import android.graphics.Paint;
-
-import java.awt.Font;
-import java.io.ByteArrayInputStream;
-import java.io.File;
-import java.nio.ByteBuffer;
-import java.util.LinkedHashMap;
-import java.util.Map;
-
-import libcore.util.NativeAllocationRegistry_Delegate;
-
-import static android.graphics.FontFamily_Delegate.computeMatch;
-import static android.graphics.FontFamily_Delegate.deriveFont;
-
-/**
- * Delegate implementing the native methods of android.graphics.fonts.FontFamily$Builder
- * <p>
- * Through the layoutlib_create tool, the original native methods of FontFamily$Builder have been
- * replaced by calls to methods of the same name in this delegate class.
- * <p>
- * This class behaves like the original native implementation, but in Java, keeping previously
- * native data into its own objects and mapping them to int that are sent back and forth between it
- * and the original FontFamily$Builder class.
- *
- * @see DelegateManager
- */
-public class FontFamily_Builder_Delegate {
- private static final DelegateManager<FontFamily_Builder_Delegate> sBuilderManager =
- new DelegateManager<>(FontFamily_Builder_Delegate.class);
-
- private static long sFontFamilyFinalizer = -1;
-
- // Order does not really matter but we use a LinkedHashMap to get reproducible results across
- // render calls
- private Map<FontInfo, Font> mFonts = new LinkedHashMap<>();
- /**
- * The variant of the Font Family - compact or elegant.
- * <p/>
- * 0 is unspecified, 1 is compact and 2 is elegant. This needs to be kept in sync with values in
- * android.graphics.FontFamily
- *
- * @see Paint#setElegantTextHeight(boolean)
- */
- private FontVariant mVariant;
- private boolean mIsCustomFallback;
-
- @LayoutlibDelegate
- /*package*/ static long nInitBuilder() {
- return sBuilderManager.addNewDelegate(new FontFamily_Builder_Delegate());
- }
-
- @LayoutlibDelegate
- /*package*/ static void nAddFont(long builderPtr, long fontPtr) {
- FontFamily_Builder_Delegate familyBuilder = sBuilderManager.getDelegate(builderPtr);
- Font_Builder_Delegate fontBuilder = Font_Builder_Delegate.sBuilderManager.getDelegate(fontPtr);
- if (familyBuilder == null || fontBuilder == null) {
- return;
- }
- Font font;
- if (fontBuilder.filePath.equals("")) {
- font = loadFontBuffer(fontBuilder.mBuffer);
-
- } else {
- font = loadFontPath(fontBuilder.filePath);
- }
- if (font != null) {
- familyBuilder.addFont(font, fontBuilder.mWeight, fontBuilder.mItalic);
- }
- }
-
- @LayoutlibDelegate
- /*package*/ static long nBuild(long builderPtr, String langTags, int variant,
- boolean isCustomFallback) {
- FontFamily_Builder_Delegate builder = sBuilderManager.getDelegate(builderPtr);
- if (builder != null) {
- assert variant < 3;
- builder.mVariant = FontVariant.values()[variant];
- builder.mIsCustomFallback = isCustomFallback;
- }
- return builderPtr;
- }
-
- @LayoutlibDelegate
- /*package*/ static long nGetReleaseNativeFamily() {
- synchronized (Font_Builder_Delegate.class) {
- if (sFontFamilyFinalizer == -1) {
- sFontFamilyFinalizer = NativeAllocationRegistry_Delegate.createFinalizer(
- sBuilderManager::removeJavaReferenceFor);
- }
- }
- return sFontFamilyFinalizer;
- }
-
- public static FontFamily_Builder_Delegate getDelegate(long nativeFontFamily) {
- return sBuilderManager.getDelegate(nativeFontFamily);
- }
-
- @Nullable
- public Font getFont(int desiredWeight, boolean isItalic) {
- FontInfo desiredStyle = new FontInfo();
- desiredStyle.mWeight = desiredWeight;
- desiredStyle.mIsItalic = isItalic;
-
- Font cachedFont = mFonts.get(desiredStyle);
- if (cachedFont != null) {
- return cachedFont;
- }
-
- FontInfo bestFont = null;
-
- if (mFonts.size() == 1) {
- // No need to compute the match since we only have one candidate
- bestFont = mFonts.keySet().iterator().next();
- } else {
- int bestMatch = Integer.MAX_VALUE;
-
- for (FontInfo font : mFonts.keySet()) {
- int match = computeMatch(font, desiredStyle);
- if (match < bestMatch) {
- bestMatch = match;
- bestFont = font;
- if (bestMatch == 0) {
- break;
- }
- }
- }
- }
-
- if (bestFont == null) {
- return null;
- }
-
-
- // Derive the font as required and add it to the list of Fonts.
- deriveFont(bestFont, desiredStyle);
- addFont(desiredStyle);
- return desiredStyle.mFont;
- }
-
- public FontVariant getVariant() {
- return mVariant;
- }
-
- // ---- private helper methods ----
-
- private void addFont(@NonNull Font font, int weight, boolean italic) {
- FontInfo fontInfo = new FontInfo();
- fontInfo.mFont = font;
- fontInfo.mWeight = weight;
- fontInfo.mIsItalic = italic;
- addFont(fontInfo);
- }
-
- private void addFont(@NonNull FontInfo fontInfo) {
- mFonts.putIfAbsent(fontInfo, fontInfo.mFont);
- }
-
- private static Font loadFontBuffer(@NonNull ByteBuffer buffer) {
- try {
- byte[] byteArray = new byte[buffer.limit()];
- buffer.rewind();
- buffer.get(byteArray);
- return Font.createFont(Font.TRUETYPE_FONT, new ByteArrayInputStream(byteArray));
- } catch (Exception e) {
- Bridge.getLog().fidelityWarning(ILayoutLog.TAG_BROKEN, "Unable to load font",
- e, null, null);
- }
-
- return null;
- }
-
- private static Font loadFontPath(@NonNull String path) {
- try {
- File file = new File(path);
- return Font.createFont(Font.TRUETYPE_FONT, file);
- } catch (Exception e) {
- Bridge.getLog().fidelityWarning(ILayoutLog.TAG_BROKEN, "Unable to load font",
- e, null, null);
- }
-
- return null;
- }
-}
diff --git a/bridge/src/android/graphics/fonts/Font_Builder_Delegate.java b/bridge/src/android/graphics/fonts/Font_Builder_Delegate.java
index 0d3ce3f3ca..490659bb60 100644
--- a/bridge/src/android/graphics/fonts/Font_Builder_Delegate.java
+++ b/bridge/src/android/graphics/fonts/Font_Builder_Delegate.java
@@ -16,8 +16,6 @@
package android.graphics.fonts;
-import com.android.ide.common.rendering.api.ILayoutLog;
-import com.android.layoutlib.bridge.Bridge;
import com.android.layoutlib.bridge.impl.DelegateManager;
import com.android.tools.layoutlib.annotations.LayoutlibDelegate;
@@ -31,8 +29,6 @@ import java.nio.ByteOrder;
import java.nio.channels.Channels;
import java.nio.channels.ReadableByteChannel;
-import libcore.util.NativeAllocationRegistry_Delegate;
-
/**
* Delegate implementing the native methods of android.graphics.fonts.Font$Builder
* <p>
@@ -46,20 +42,6 @@ import libcore.util.NativeAllocationRegistry_Delegate;
* @see DelegateManager
*/
public class Font_Builder_Delegate {
- protected static final DelegateManager<Font_Builder_Delegate> sBuilderManager =
- new DelegateManager<>(Font_Builder_Delegate.class);
- private static long sFontFinalizer = -1;
-
- protected ByteBuffer mBuffer;
- protected int mWeight;
- protected boolean mItalic;
- protected int mTtcIndex;
- protected String filePath;
-
- @LayoutlibDelegate
- /*package*/ static long nInitBuilder() {
- return sBuilderManager.addNewDelegate(new Font_Builder_Delegate());
- }
@LayoutlibDelegate
/*package*/ static ByteBuffer createBuffer(@NonNull AssetManager am, @NonNull String path,
@@ -80,35 +62,4 @@ public class Font_Builder_Delegate {
return buffer;
}
}
-
- @LayoutlibDelegate
- /*package*/ static void nAddAxis(long builderPtr, int tag, float value) {
- Bridge.getLog().fidelityWarning(ILayoutLog.TAG_UNSUPPORTED,
- "Font$Builder.nAddAxis is not supported.", null, null, null);
- }
-
- @LayoutlibDelegate
- /*package*/ static long nBuild(long builderPtr, ByteBuffer buffer, String filePath, int weight,
- boolean italic, int ttcIndex) {
- Font_Builder_Delegate font = sBuilderManager.getDelegate(builderPtr);
- if (font != null) {
- font.mBuffer = buffer;
- font.mWeight = weight;
- font.mItalic = italic;
- font.mTtcIndex = ttcIndex;
- font.filePath = filePath;
- }
- return builderPtr;
- }
-
- @LayoutlibDelegate
- /*package*/ static long nGetReleaseNativeFont() {
- synchronized (Font_Builder_Delegate.class) {
- if (sFontFinalizer == -1) {
- sFontFinalizer = NativeAllocationRegistry_Delegate.createFinalizer(
- sBuilderManager::removeJavaReferenceFor);
- }
- }
- return sFontFinalizer;
- }
}
diff --git a/bridge/src/android/graphics/fonts/SystemFonts_Delegate.java b/bridge/src/android/graphics/fonts/SystemFonts_Delegate.java
deleted file mode 100644
index 3f3e065338..0000000000
--- a/bridge/src/android/graphics/fonts/SystemFonts_Delegate.java
+++ /dev/null
@@ -1,58 +0,0 @@
-/*
- * 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.
- */
-
-package android.graphics.fonts;
-
-import com.android.layoutlib.bridge.Bridge;
-import com.android.layoutlib.bridge.impl.DelegateManager;
-import com.android.tools.layoutlib.annotations.LayoutlibDelegate;
-
-import android.annotation.NonNull;
-import android.annotation.Nullable;
-import android.text.FontConfig;
-
-import java.io.File;
-import java.util.Map;
-
-import static android.graphics.FontFamily_Delegate.getFontLocation;
-
-/**
- * Delegate implementing the native methods of android.graphics.fonts.SystemFonts
- * <p>
- * Through the layoutlib_create tool, the original native methods of SystemFonts have been
- * replaced by calls to methods of the same name in this delegate class.
- * <p>
- * This class behaves like the original native implementation, but in Java, keeping previously
- * native data into its own objects and mapping them to int that are sent back and forth between it
- * and the original SystemFonts class.
- *
- * @see DelegateManager
- */
-public class SystemFonts_Delegate {
-
- @LayoutlibDelegate
- /*package*/ static FontConfig getSystemFontConfigInternal(
- String fontsXml,
- String systemFontDir,
- String oemXml,
- String productFontDir,
- Map<String, File> updatableFontMap) {
- Bridge.sIsTypefaceInitialized = true;
- return SystemFonts.getSystemFontConfigInternal_Original(
- getFontLocation() + "/standard/fonts.xml", getFontLocation() + "/",
- null, null, updatableFontMap, 0, 0);
- }
-}
diff --git a/bridge/src/android/graphics/text/BaseLineBreaker.java b/bridge/src/android/graphics/text/BaseLineBreaker.java
deleted file mode 100644
index 61fa21643f..0000000000
--- a/bridge/src/android/graphics/text/BaseLineBreaker.java
+++ /dev/null
@@ -1,53 +0,0 @@
-/*
- * 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.
- */
-
-package android.graphics.text;
-
-import android.annotation.NonNull;
-
-import java.util.ArrayList;
-import java.util.Collections;
-import java.util.List;
-
-// Based on the native implementation of LineBreaker in
-// frameworks/base/core/jni/android_text_StaticLayout.cpp revision b808260
-public abstract class BaseLineBreaker {
-
- protected static final int TAB_MASK = 0x20000000; // keep in sync with StaticLayout
-
- protected final @NonNull List<Primitive> mPrimitives;
- protected final @NonNull
- LineWidth mLineWidth;
- protected final @NonNull
- TabStops mTabStops;
-
- public BaseLineBreaker(@NonNull List<Primitive> primitives, @NonNull LineWidth lineWidth,
- @NonNull TabStops tabStops) {
- mPrimitives = Collections.unmodifiableList(primitives);
- mLineWidth = lineWidth;
- mTabStops = tabStops;
- }
-
- public abstract Result computeBreaks();
-
- public static class Result {
- List<Integer> mLineBreakOffset = new ArrayList<>();
- List<Float> mLineWidths = new ArrayList<>();
- List<Float> mLineAscents = new ArrayList<>();
- List<Float> mLineDescents = new ArrayList<>();
- List<Integer> mLineFlags = new ArrayList<>();
- }
-}
diff --git a/bridge/src/android/graphics/text/GreedyLineBreaker.java b/bridge/src/android/graphics/text/GreedyLineBreaker.java
deleted file mode 100644
index 940e235359..0000000000
--- a/bridge/src/android/graphics/text/GreedyLineBreaker.java
+++ /dev/null
@@ -1,166 +0,0 @@
-/*
- * 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.
- */
-
-package android.graphics.text;
-
-import android.annotation.NonNull;
-import android.graphics.text.Primitive.PrimitiveType;
-
-import java.util.List;
-
-import static android.graphics.text.Primitive.PrimitiveType.PENALTY_INFINITY;
-
-// Based on the native implementation of GreedyLineBreaker in
-// frameworks/base/core/jni/android_text_StaticLayout.cpp revision b808260
-public class GreedyLineBreaker extends BaseLineBreaker {
-
- public GreedyLineBreaker(@NonNull List<Primitive> primitives, @NonNull LineWidth lineWidth,
- @NonNull TabStops tabStops) {
- super(primitives, lineWidth, tabStops);
- }
-
- @Override
- public Result computeBreaks() {
- int lineNum = 0;
- float width = 0, printedWidth = 0;
- boolean breakFound = false, goodBreakFound = false;
- int breakIndex = 0, goodBreakIndex = 0;
- float breakWidth = 0, goodBreakWidth = 0;
- int firstTabIndex = Integer.MAX_VALUE;
-
- float maxWidth = mLineWidth.getLineWidth(lineNum);
-
- int numPrimitives = mPrimitives.size();
- Result result = new Result();
- // greedily fit as many characters as possible on each line
- // loop over all primitives, and choose the best break point
- // (if possible, a break point without splitting a word)
- // after going over the maximum length
- for (int i = 0; i < numPrimitives; i++) {
- Primitive p = mPrimitives.get(i);
-
- // update the current line width
- if (p.type == PrimitiveType.BOX || p.type == PrimitiveType.GLUE) {
- width += p.width;
- if (p.type == PrimitiveType.BOX) {
- printedWidth = width;
- }
- } else if (p.type == PrimitiveType.VARIABLE) {
- width = mTabStops.width(width);
- // keep track of first tab character in the region we are examining
- // so we can determine whether or not a line contains a tab
- firstTabIndex = Math.min(firstTabIndex, i);
- }
-
- // find the best break point for the characters examined so far
- if (printedWidth > maxWidth) {
- //noinspection StatementWithEmptyBody
- if (breakFound || goodBreakFound) {
- if (goodBreakFound) {
- // a true line break opportunity existed in the characters examined so far,
- // so there is no need to split a word
- i = goodBreakIndex; // no +1 because of i++
- lineNum++;
- maxWidth = mLineWidth.getLineWidth(lineNum);
- result.mLineBreakOffset.add(mPrimitives.get(goodBreakIndex).location);
- result.mLineWidths.add(goodBreakWidth);
- result.mLineAscents.add(0f);
- result.mLineDescents.add(0f);
- result.mLineFlags.add(firstTabIndex < goodBreakIndex ? TAB_MASK : 0);
- firstTabIndex = Integer.MAX_VALUE;
- } else {
- // must split a word because there is no other option
- i = breakIndex; // no +1 because of i++
- lineNum++;
- maxWidth = mLineWidth.getLineWidth(lineNum);
- result.mLineBreakOffset.add(mPrimitives.get(breakIndex).location);
- result.mLineWidths.add(breakWidth);
- result.mLineAscents.add(0f);
- result.mLineDescents.add(0f);
- result.mLineFlags.add(firstTabIndex < breakIndex ? TAB_MASK : 0);
- firstTabIndex = Integer.MAX_VALUE;
- }
- printedWidth = width = 0;
- goodBreakFound = breakFound = false;
- goodBreakWidth = breakWidth = 0;
- continue;
- } else {
- // no choice, keep going... must make progress by putting at least one
- // character on a line, even if part of that character is cut off --
- // there is no other option
- }
- }
-
- // update possible break points
- if (p.type == PrimitiveType.PENALTY &&
- p.penalty < PENALTY_INFINITY) {
- // this does not handle penalties with width
-
- // handle forced line break
- if (p.penalty == -PENALTY_INFINITY) {
- lineNum++;
- maxWidth = mLineWidth.getLineWidth(lineNum);
- result.mLineBreakOffset.add(p.location);
- result.mLineWidths.add(printedWidth);
- result.mLineAscents.add(0f);
- result.mLineDescents.add(0f);
- result.mLineFlags.add(firstTabIndex < i ? TAB_MASK : 0);
- firstTabIndex = Integer.MAX_VALUE;
- printedWidth = width = 0;
- goodBreakFound = breakFound = false;
- goodBreakWidth = breakWidth = 0;
- continue;
- }
- if (i > breakIndex && (printedWidth <= maxWidth || !breakFound)) {
- breakFound = true;
- breakIndex = i;
- breakWidth = printedWidth;
- }
- if (i > goodBreakIndex && printedWidth <= maxWidth) {
- goodBreakFound = true;
- goodBreakIndex = i;
- goodBreakWidth = printedWidth;
- }
- } else if (p.type == PrimitiveType.WORD_BREAK) {
- // only do this if necessary -- we don't want to break words
- // when possible, but sometimes it is unavoidable
- if (i > breakIndex && (printedWidth <= maxWidth || !breakFound)) {
- breakFound = true;
- breakIndex = i;
- breakWidth = printedWidth;
- }
- }
- }
-
- if (breakFound || goodBreakFound) {
- // output last break if there are more characters to output
- if (goodBreakFound) {
- result.mLineBreakOffset.add(mPrimitives.get(goodBreakIndex).location);
- result.mLineWidths.add(goodBreakWidth);
- result.mLineAscents.add(0f);
- result.mLineDescents.add(0f);
- result.mLineFlags.add(firstTabIndex < goodBreakIndex ? TAB_MASK : 0);
- } else {
- result.mLineBreakOffset.add(mPrimitives.get(breakIndex).location);
- result.mLineWidths.add(breakWidth);
- result.mLineAscents.add(0f);
- result.mLineDescents.add(0f);
- result.mLineFlags.add(firstTabIndex < breakIndex ? TAB_MASK : 0);
- }
- }
- return result;
- }
-}
diff --git a/bridge/src/android/graphics/text/LineBreaker_Delegate.java b/bridge/src/android/graphics/text/LineBreaker_Delegate.java
deleted file mode 100644
index 92c99cc062..0000000000
--- a/bridge/src/android/graphics/text/LineBreaker_Delegate.java
+++ /dev/null
@@ -1,263 +0,0 @@
-/*
- * 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.
- */
-
-package android.graphics.text;
-
-import com.android.layoutlib.bridge.impl.DelegateManager;
-import com.android.tools.layoutlib.annotations.LayoutlibDelegate;
-
-import android.annotation.NonNull;
-import android.annotation.Nullable;
-import android.icu.text.BreakIterator;
-import android.text.Layout;
-import android.text.Layout.BreakStrategy;
-import android.text.Layout.HyphenationFrequency;
-import android.graphics.text.Primitive.PrimitiveType;
-
-import java.text.CharacterIterator;
-import java.util.ArrayList;
-import java.util.List;
-
-import javax.swing.text.Segment;
-import libcore.util.NativeAllocationRegistry_Delegate;
-
-/**
- * Delegate that provides implementation for native methods in {@link android.text.StaticLayout}
- * <p/>
- * Through the layoutlib_create tool, selected methods of StaticLayout have been replaced
- * by calls to methods of the same name in this delegate class.
- *
- */
-public class LineBreaker_Delegate {
-
- private static final char CHAR_SPACE = 0x20;
- private static final char CHAR_TAB = 0x09;
- private static final char CHAR_NEWLINE = 0x0A;
- private static final char CHAR_ZWSP = 0x200B; // Zero width space.
-
- // ---- Builder delegate manager ----
- private static final DelegateManager<Builder> sBuilderManager =
- new DelegateManager<>(Builder.class);
- private static long sFinalizer = -1;
-
- // ---- Result delegate manager ----
- private static final DelegateManager<Result> sResultManager =
- new DelegateManager<>(Result.class);
- private static long sResultFinalizer = -1;
-
- @LayoutlibDelegate
- /*package*/ static long nInit(
- @BreakStrategy int breakStrategy,
- @HyphenationFrequency int hyphenationFrequency,
- boolean isJustified,
- @Nullable int[] indents) {
- Builder builder = new Builder();
- builder.mBreakStrategy = breakStrategy;
- return sBuilderManager.addNewDelegate(builder);
- }
-
- @LayoutlibDelegate
- /*package*/ static long nGetReleaseFunc() {
- synchronized (MeasuredText_Delegate.class) {
- if (sFinalizer == -1) {
- sFinalizer = NativeAllocationRegistry_Delegate.createFinalizer(
- sBuilderManager::removeJavaReferenceFor);
- }
- }
- return sFinalizer;
- }
-
- @LayoutlibDelegate
- /*package*/ static long nComputeLineBreaks(
- /* non zero */ long nativePtr,
-
- // Inputs
- @NonNull char[] text,
- long measuredTextPtr,
- int length,
- float firstWidth,
- int firstWidthLineCount,
- float restWidth,
- @Nullable float[] variableTabStops,
- float defaultTabStop,
- int indentsOffset) {
- Builder builder = sBuilderManager.getDelegate(nativePtr);
- if (builder == null) {
- return 0;
- }
-
- builder.mText = text;
- builder.mWidths = new float[length];
- builder.mLineWidth = new LineWidth(firstWidth, firstWidthLineCount, restWidth);
- builder.mTabStopCalculator = new TabStops(variableTabStops, defaultTabStop);
-
- MeasuredText_Delegate.computeRuns(measuredTextPtr, builder);
-
- // compute all possible breakpoints.
- BreakIterator it = BreakIterator.getLineInstance();
- it.setText((CharacterIterator) new Segment(builder.mText, 0, length));
-
- // average word length in english is 5. So, initialize the possible breaks with a guess.
- List<Integer> breaks = new ArrayList<Integer>((int) Math.ceil(length / 5d));
- int loc;
- it.first();
- while ((loc = it.next()) != BreakIterator.DONE) {
- breaks.add(loc);
- }
-
- List<Primitive> primitives =
- computePrimitives(builder.mText, builder.mWidths, length, breaks);
- switch (builder.mBreakStrategy) {
- case Layout.BREAK_STRATEGY_SIMPLE:
- builder.mLineBreaker = new GreedyLineBreaker(primitives, builder.mLineWidth,
- builder.mTabStopCalculator);
- break;
- case Layout.BREAK_STRATEGY_HIGH_QUALITY:
- // TODO
-// break;
- case Layout.BREAK_STRATEGY_BALANCED:
- builder.mLineBreaker = new OptimizingLineBreaker(primitives, builder.mLineWidth,
- builder.mTabStopCalculator);
- break;
- default:
- assert false : "Unknown break strategy: " + builder.mBreakStrategy;
- builder.mLineBreaker = new GreedyLineBreaker(primitives, builder.mLineWidth,
- builder.mTabStopCalculator);
- }
- Result result = new Result(builder.mLineBreaker.computeBreaks());
- return sResultManager.addNewDelegate(result);
- }
-
- @LayoutlibDelegate
- /*package*/ static int nGetLineCount(long ptr) {
- Result result = sResultManager.getDelegate(ptr);
- return result.mResult.mLineBreakOffset.size();
- }
-
- @LayoutlibDelegate
- /*package*/ static int nGetLineBreakOffset(long ptr, int idx) {
- Result result = sResultManager.getDelegate(ptr);
- return result.mResult.mLineBreakOffset.get(idx);
- }
-
- @LayoutlibDelegate
- /*package*/ static float nGetLineWidth(long ptr, int idx) {
- Result result = sResultManager.getDelegate(ptr);
- return result.mResult.mLineWidths.get(idx);
- }
-
- @LayoutlibDelegate
- /*package*/ static float nGetLineAscent(long ptr, int idx) {
- Result result = sResultManager.getDelegate(ptr);
- return result.mResult.mLineAscents.get(idx);
- }
-
- @LayoutlibDelegate
- /*package*/ static float nGetLineDescent(long ptr, int idx) {
- Result result = sResultManager.getDelegate(ptr);
- return result.mResult.mLineDescents.get(idx);
- }
-
- @LayoutlibDelegate
- /*package*/ static int nGetLineFlag(long ptr, int idx) {
- Result result = sResultManager.getDelegate(ptr);
- return result.mResult.mLineFlags.get(idx);
- }
-
- @LayoutlibDelegate
- /*package*/ static long nGetReleaseResultFunc() {
- synchronized (MeasuredText_Delegate.class) {
- if (sResultFinalizer == -1) {
- sResultFinalizer = NativeAllocationRegistry_Delegate.createFinalizer(
- sBuilderManager::removeJavaReferenceFor);
- }
- }
- return sResultFinalizer;
- }
-
- /**
- * Compute metadata each character - things which help in deciding if it's possible to break
- * at a point or not.
- */
- @NonNull
- private static List<Primitive> computePrimitives(@NonNull char[] text, @NonNull float[] widths,
- int length, @NonNull List<Integer> breaks) {
- // Initialize the list with a guess of the number of primitives:
- // 2 Primitives per non-whitespace char and approx 5 chars per word (i.e. 83% chars)
- List<Primitive> primitives = new ArrayList<Primitive>(((int) Math.ceil(length * 1.833)));
- int breaksSize = breaks.size();
- int breakIndex = 0;
- for (int i = 0; i < length; i++) {
- char c = text[i];
- if (c == CHAR_SPACE || c == CHAR_ZWSP) {
- primitives.add(PrimitiveType.GLUE.getNewPrimitive(i, widths[i]));
- } else if (c == CHAR_TAB) {
- primitives.add(PrimitiveType.VARIABLE.getNewPrimitive(i));
- } else if (c != CHAR_NEWLINE) {
- while (breakIndex < breaksSize && breaks.get(breakIndex) < i) {
- breakIndex++;
- }
- Primitive p;
- if (widths[i] != 0) {
- if (breakIndex < breaksSize && breaks.get(breakIndex) == i) {
- p = PrimitiveType.PENALTY.getNewPrimitive(i, 0, 0);
- } else {
- p = PrimitiveType.WORD_BREAK.getNewPrimitive(i, 0);
- }
- primitives.add(p);
- }
-
- primitives.add(PrimitiveType.BOX.getNewPrimitive(i, widths[i]));
- }
- }
- // final break at end of everything
- primitives.add(
- PrimitiveType.PENALTY.getNewPrimitive(length, 0, -PrimitiveType.PENALTY_INFINITY));
- return primitives;
- }
-
- // TODO: Rename to LineBreakerRef and move everything other than LineBreaker to LineBreaker.
- /**
- * Java representation of the native Builder class.
- */
- public static class Builder {
- char[] mText;
- float[] mWidths;
- private BaseLineBreaker mLineBreaker;
- private int mBreakStrategy;
- private LineWidth mLineWidth;
- private TabStops mTabStopCalculator;
- }
-
- public abstract static class Run {
- int mStart;
- int mEnd;
-
- Run(int start, int end) {
- mStart = start;
- mEnd = end;
- }
-
- abstract void addTo(Builder builder);
- }
-
- public static class Result {
- final BaseLineBreaker.Result mResult;
- public Result(BaseLineBreaker.Result result) {
- mResult = result;
- }
- }
-}
diff --git a/bridge/src/android/graphics/text/LineWidth.java b/bridge/src/android/graphics/text/LineWidth.java
deleted file mode 100644
index 809b96778c..0000000000
--- a/bridge/src/android/graphics/text/LineWidth.java
+++ /dev/null
@@ -1,35 +0,0 @@
-/*
- * 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.
- */
-
-package android.graphics.text;
-
-// Based on the native implementation of LineWidth in
-// frameworks/base/core/jni/android_text_StaticLayout.cpp revision b808260
-public class LineWidth {
- private final float mFirstWidth;
- private final int mFirstWidthLineCount;
- private float mRestWidth;
-
- public LineWidth(float firstWidth, int firstWidthLineCount, float restWidth) {
- mFirstWidth = firstWidth;
- mFirstWidthLineCount = firstWidthLineCount;
- mRestWidth = restWidth;
- }
-
- public float getLineWidth(int line) {
- return (line < mFirstWidthLineCount) ? mFirstWidth : mRestWidth;
- }
-}
diff --git a/bridge/src/android/graphics/text/MeasuredText_Builder_Delegate.java b/bridge/src/android/graphics/text/MeasuredText_Builder_Delegate.java
deleted file mode 100644
index f9abbe1272..0000000000
--- a/bridge/src/android/graphics/text/MeasuredText_Builder_Delegate.java
+++ /dev/null
@@ -1,146 +0,0 @@
-/*
- * 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.
- */
-
-package android.graphics.text;
-
-import com.android.layoutlib.bridge.impl.DelegateManager;
-import com.android.tools.layoutlib.annotations.LayoutlibDelegate;
-
-import android.annotation.NonNull;
-import android.graphics.BidiRenderer;
-import android.graphics.Paint;
-import android.graphics.Paint_Delegate;
-import android.graphics.RectF;
-import android.graphics.text.LineBreaker_Delegate.Builder;
-import android.graphics.text.LineBreaker_Delegate.Run;
-
-import java.util.ArrayList;
-import java.util.Arrays;
-
-/**
- * Delegate that provides implementation for native methods in
- * {@link android.graphics.text.MeasuredText}
- * <p/>
- * Through the layoutlib_create tool, selected methods of StaticLayout have been replaced
- * by calls to methods of the same name in this delegate class.
- *
- */
-public class MeasuredText_Builder_Delegate {
- // ---- Builder delegate manager ----
- protected static final DelegateManager<MeasuredText_Builder_Delegate>
- sBuilderManager =
- new DelegateManager<>(MeasuredText_Builder_Delegate.class);
-
- protected final ArrayList<Run> mRuns = new ArrayList<>();
-
- @LayoutlibDelegate
- /*package*/ static long nInitBuilder() {
- return sBuilderManager.addNewDelegate(new MeasuredText_Builder_Delegate());
- }
-
- /**
- * Apply style to make native measured text.
- *
- * @param nativeBuilderPtr The native NativeMeasuredParagraph builder pointer.
- * @param paintPtr The native paint pointer to be applied.
- * @param start The start offset in the copied buffer.
- * @param end The end offset in the copied buffer.
- * @param isRtl True if the text is RTL.
- */
- @LayoutlibDelegate
- /*package*/ static void nAddStyleRun(long nativeBuilderPtr, long paintPtr, int start,
- int end, boolean isRtl) {
- MeasuredText_Builder_Delegate builder = sBuilderManager.getDelegate(nativeBuilderPtr);
- if (builder == null) {
- return;
- }
- builder.mRuns.add(new StyleRun(paintPtr, start, end, isRtl));
- }
-
- /**
- * Apply ReplacementRun to make native measured text.
- *
- * @param nativeBuilderPtr The native NativeMeasuredParagraph builder pointer.
- * @param paintPtr The native paint pointer to be applied.
- * @param start The start offset in the copied buffer.
- * @param end The end offset in the copied buffer.
- * @param width The width of the replacement.
- */
- @LayoutlibDelegate
- /*package*/ static void nAddReplacementRun(long nativeBuilderPtr, long paintPtr, int start,
- int end, float width) {
- MeasuredText_Builder_Delegate builder = sBuilderManager.getDelegate(nativeBuilderPtr);
- if (builder == null) {
- return;
- }
- builder.mRuns.add(new ReplacementRun(start, end, width));
- }
-
- @LayoutlibDelegate
- /*package*/ static long nBuildMeasuredText(long nativeBuilderPtr, long hintMtPtr,
- @NonNull char[] text, boolean computeHyphenation, boolean computeLayout) {
- MeasuredText_Delegate delegate = new MeasuredText_Delegate();
- delegate.mNativeBuilderPtr = nativeBuilderPtr;
- return MeasuredText_Delegate.sManager.addNewDelegate(delegate);
- }
-
- @LayoutlibDelegate
- /*package*/ static void nFreeBuilder(long nativeBuilderPtr) {
- sBuilderManager.removeJavaReferenceFor(nativeBuilderPtr);
- }
-
- private static float measureText(long nativePaint, char[] text, int index, int count,
- float[] widths, int bidiFlags) {
- Paint_Delegate paint = Paint_Delegate.getDelegate(nativePaint);
- RectF bounds =
- new BidiRenderer(null, paint, text).renderText(index, index + count, bidiFlags,
- widths, 0, false);
- return bounds.right - bounds.left;
- }
-
- private static class StyleRun extends Run {
- private final long mNativePaint;
- private final boolean mIsRtl;
-
- private StyleRun(long nativePaint, int start, int end, boolean isRtl) {
- super(start, end);
- mNativePaint = nativePaint;
- mIsRtl = isRtl;
- }
-
- @Override
- void addTo(Builder builder) {
- int bidiFlags = mIsRtl ? Paint.BIDI_FORCE_RTL : Paint.BIDI_FORCE_LTR;
- measureText(mNativePaint, builder.mText, mStart, mEnd - mStart, builder.mWidths,
- bidiFlags);
- }
- }
-
- private static class ReplacementRun extends Run {
- private final float mWidth;
-
- private ReplacementRun(int start, int end, float width) {
- super(start, end);
- mWidth = width;
- }
-
- @Override
- void addTo(Builder builder) {
- builder.mWidths[mStart] = mWidth;
- Arrays.fill(builder.mWidths, mStart + 1, mEnd, 0.0f);
- }
- }
-}
diff --git a/bridge/src/android/graphics/text/MeasuredText_Delegate.java b/bridge/src/android/graphics/text/MeasuredText_Delegate.java
deleted file mode 100644
index 02e28450df..0000000000
--- a/bridge/src/android/graphics/text/MeasuredText_Delegate.java
+++ /dev/null
@@ -1,93 +0,0 @@
-/*
- * 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.
- */
-
-package android.graphics.text;
-
-import com.android.layoutlib.bridge.impl.DelegateManager;
-import com.android.tools.layoutlib.annotations.LayoutlibDelegate;
-
-import android.graphics.Rect;
-import android.graphics.text.LineBreaker_Delegate.Builder;
-import android.graphics.text.LineBreaker_Delegate.Run;
-
-import libcore.util.NativeAllocationRegistry_Delegate;
-
-/**
- * Delegate that provides implementation for native methods in
- * {@link android.graphics.text.MeasuredText}
- * <p/>
- * Through the layoutlib_create tool, selected methods of StaticLayout have been replaced
- * by calls to methods of the same name in this delegate class.
- *
- */
-public class MeasuredText_Delegate {
-
- // ---- Builder delegate manager ----
- protected static final DelegateManager<MeasuredText_Delegate> sManager =
- new DelegateManager<>(MeasuredText_Delegate.class);
- private static long sFinalizer = -1;
-
- protected long mNativeBuilderPtr;
-
- @LayoutlibDelegate
- /*package*/ static float nGetWidth(long nativePtr, int start, int end) {
- // Ignore as it is not used for the layoutlib implementation
- return 0.0f;
- }
-
- @LayoutlibDelegate
- /*package*/ static long nGetReleaseFunc() {
- synchronized (MeasuredText_Delegate.class) {
- if (sFinalizer == -1) {
- sFinalizer = NativeAllocationRegistry_Delegate.createFinalizer(
- sManager::removeJavaReferenceFor);
- }
- }
- return sFinalizer;
- }
-
- @LayoutlibDelegate
- /*package*/ static int nGetMemoryUsage(long nativePtr) {
- // Ignore as it is not used for the layoutlib implementation
- return 0;
- }
-
- @LayoutlibDelegate
- /*package*/ static void nGetBounds(long nativePtr, char[] buf, int start, int end, Rect rect) {
- // Ignore as it is not used for the layoutlib implementation
- }
-
- @LayoutlibDelegate
- /*package*/ static float nGetCharWidthAt(long nativePtr, int offset) {
- // Ignore as it is not used for the layoutlib implementation
- return 0.0f;
- }
-
- public static void computeRuns(long measuredTextPtr, Builder staticLayoutBuilder) {
- MeasuredText_Delegate delegate = sManager.getDelegate(measuredTextPtr);
- if (delegate == null) {
- return;
- }
- MeasuredText_Builder_Delegate builder =
- MeasuredText_Builder_Delegate.sBuilderManager.getDelegate(delegate.mNativeBuilderPtr);
- if (builder == null) {
- return;
- }
- for (Run run: builder.mRuns) {
- run.addTo(staticLayoutBuilder);
- }
- }
-} \ No newline at end of file
diff --git a/bridge/src/android/graphics/text/OptimizingLineBreaker.java b/bridge/src/android/graphics/text/OptimizingLineBreaker.java
deleted file mode 100644
index 95e0920ce4..0000000000
--- a/bridge/src/android/graphics/text/OptimizingLineBreaker.java
+++ /dev/null
@@ -1,250 +0,0 @@
-/*
- * 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.
- */
-
-package android.graphics.text;
-
-import android.annotation.NonNull;
-import android.graphics.text.Primitive.PrimitiveType;
-
-import java.util.ArrayList;
-import java.util.Collections;
-import java.util.List;
-import java.util.ListIterator;
-
-import static android.graphics.text.Primitive.PrimitiveType.PENALTY_INFINITY;
-
-
-// Based on the native implementation of OptimizingLineBreaker in
-// frameworks/base/core/jni/android_text_StaticLayout.cpp revision b808260
-/**
- * A more complex version of line breaking where we try to prevent the right edge from being too
- * jagged.
- */
-public class OptimizingLineBreaker extends BaseLineBreaker {
-
- public OptimizingLineBreaker(@NonNull List<Primitive> primitives, @NonNull LineWidth lineWidth,
- @NonNull TabStops tabStops) {
- super(primitives, lineWidth, tabStops);
- }
-
- @Override
- public Result computeBreaks() {
- Result result = new Result();
- int numBreaks = mPrimitives.size();
- assert numBreaks > 0;
- if (numBreaks == 1) {
- // This can be true only if it's an empty paragraph.
- Primitive p = mPrimitives.get(0);
- assert p.type == PrimitiveType.PENALTY;
- result.mLineBreakOffset.add(0);
- result.mLineWidths.add(p.width);
- result.mLineAscents.add(0f);
- result.mLineDescents.add(0f);
- result.mLineFlags.add(0);
- return result;
- }
- Node[] opt = new Node[numBreaks];
- opt[0] = new Node(-1, 0, 0, 0, false);
- opt[numBreaks - 1] = new Node(-1, 0, 0, 0, false);
-
- ArrayList<Integer> active = new ArrayList<Integer>();
- active.add(0);
- int lastBreak = 0;
- for (int i = 0; i < numBreaks; i++) {
- Primitive p = mPrimitives.get(i);
- if (p.type == PrimitiveType.PENALTY) {
- boolean finalBreak = (i + 1 == numBreaks);
- Node bestBreak = null;
-
- for (ListIterator<Integer> it = active.listIterator(); it.hasNext();
- /* incrementing done in loop */) {
- int pos = it.next();
- int lines = opt[pos].mPrevCount;
- float maxWidth = mLineWidth.getLineWidth(lines);
- // we have to compute metrics every time --
- // we can't really pre-compute this stuff and just deal with breaks
- // because of the way tab characters work, this makes it computationally
- // harder, but this way, we can still optimize while treating tab characters
- // correctly
- LineMetrics lineMetrics = computeMetrics(pos, i);
- if (lineMetrics.mPrintedWidth <= maxWidth) {
- float demerits = computeDemerits(maxWidth, lineMetrics.mPrintedWidth,
- finalBreak, p.penalty) + opt[pos].mDemerits;
- if (bestBreak == null || demerits < bestBreak.mDemerits) {
- if (bestBreak == null) {
- bestBreak = new Node(pos, opt[pos].mPrevCount + 1, demerits,
- lineMetrics.mPrintedWidth, lineMetrics.mHasTabs);
- } else {
- bestBreak.mPrev = pos;
- bestBreak.mPrevCount = opt[pos].mPrevCount + 1;
- bestBreak.mDemerits = demerits;
- bestBreak.mWidth = lineMetrics.mPrintedWidth;
- bestBreak.mHasTabs = lineMetrics.mHasTabs;
- }
- }
- } else {
- it.remove();
- }
- }
- if (p.penalty == -PENALTY_INFINITY) {
- active.clear();
- }
- if (bestBreak != null) {
- opt[i] = bestBreak;
- active.add(i);
- lastBreak = i;
- }
- if (active.isEmpty()) {
- // we can't give up!
- LineMetrics lineMetrics = new LineMetrics();
- int lines = opt[lastBreak].mPrevCount;
- float maxWidth = mLineWidth.getLineWidth(lines);
- int breakIndex = desperateBreak(lastBreak, numBreaks, maxWidth, lineMetrics);
- opt[breakIndex] = new Node(lastBreak, lines + 1, 0 /*doesn't matter*/,
- lineMetrics.mWidth, lineMetrics.mHasTabs);
- active.add(breakIndex);
- lastBreak = breakIndex;
- i = breakIndex; // incremented by i++
- }
- }
- }
-
- int idx = numBreaks - 1;
- while (opt[idx].mPrev != -1) {
- result.mLineBreakOffset.add(mPrimitives.get(idx).location);
- result.mLineWidths.add(opt[idx].mWidth);
- result.mLineAscents.add(0f);
- result.mLineDescents.add(0f);
- result.mLineFlags.add(opt[idx].mHasTabs ? TAB_MASK : 0);
- idx = opt[idx].mPrev;
- }
-
- Collections.reverse(result.mLineBreakOffset);
- Collections.reverse(result.mLineWidths);
- Collections.reverse(result.mLineAscents);
- Collections.reverse(result.mLineDescents);
- Collections.reverse(result.mLineFlags);
- return result;
- }
-
- @NonNull
- private LineMetrics computeMetrics(int start, int end) {
- boolean f = false;
- float w = 0, pw = 0;
- for (int i = start; i < end; i++) {
- Primitive p = mPrimitives.get(i);
- if (p.type == PrimitiveType.BOX || p.type == PrimitiveType.GLUE) {
- w += p.width;
- if (p.type == PrimitiveType.BOX) {
- pw = w;
- }
- } else if (p.type == PrimitiveType.VARIABLE) {
- w = mTabStops.width(w);
- f = true;
- }
- }
- return new LineMetrics(w, pw, f);
- }
-
- private static float computeDemerits(float maxWidth, float width, boolean finalBreak,
- float penalty) {
- float deviation = finalBreak ? 0 : maxWidth - width;
- return (deviation * deviation) + penalty;
- }
-
- /**
- * @return the last break position or -1 if failed.
- */
- @SuppressWarnings("ConstantConditions") // method too complex to be analyzed.
- private int desperateBreak(int start, int limit, float maxWidth,
- @NonNull LineMetrics lineMetrics) {
- float w = 0, pw = 0;
- boolean breakFound = false;
- int breakIndex = 0, firstTabIndex = Integer.MAX_VALUE;
- for (int i = start; i < limit; i++) {
- Primitive p = mPrimitives.get(i);
-
- if (p.type == PrimitiveType.BOX || p.type == PrimitiveType.GLUE) {
- w += p.width;
- if (p.type == PrimitiveType.BOX) {
- pw = w;
- }
- } else if (p.type == PrimitiveType.VARIABLE) {
- w = mTabStops.width(w);
- firstTabIndex = Math.min(firstTabIndex, i);
- }
-
- if (pw > maxWidth && breakFound) {
- break;
- }
-
- // must make progress
- if (i > start &&
- (p.type == PrimitiveType.PENALTY || p.type == PrimitiveType.WORD_BREAK)) {
- breakFound = true;
- breakIndex = i;
- }
- }
-
- if (breakFound) {
- lineMetrics.mWidth = w;
- lineMetrics.mPrintedWidth = pw;
- lineMetrics.mHasTabs = (start <= firstTabIndex && firstTabIndex < breakIndex);
- return breakIndex;
- } else {
- return -1;
- }
- }
-
- private static class LineMetrics {
- /** Actual width of the line. */
- float mWidth;
- /** Width of the line minus trailing whitespace. */
- float mPrintedWidth;
- boolean mHasTabs;
-
- public LineMetrics() {
- }
-
- public LineMetrics(float width, float printedWidth, boolean hasTabs) {
- mWidth = width;
- mPrintedWidth = printedWidth;
- mHasTabs = hasTabs;
- }
- }
-
- /**
- * A struct to store the info about a break.
- */
- @SuppressWarnings("SpellCheckingInspection") // For the word struct.
- private static class Node {
- // -1 for the first node.
- int mPrev;
- // number of breaks so far.
- int mPrevCount;
- float mDemerits;
- float mWidth;
- boolean mHasTabs;
-
- public Node(int prev, int prevCount, float demerits, float width, boolean hasTabs) {
- mPrev = prev;
- mPrevCount = prevCount;
- mDemerits = demerits;
- mWidth = width;
- mHasTabs = hasTabs;
- }
- }
-}
diff --git a/bridge/src/android/graphics/text/Primitive.java b/bridge/src/android/graphics/text/Primitive.java
deleted file mode 100644
index b8157eca52..0000000000
--- a/bridge/src/android/graphics/text/Primitive.java
+++ /dev/null
@@ -1,92 +0,0 @@
-/*
- * 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.
- */
-
-package android.graphics.text;
-
-import android.annotation.NonNull;
-
-// Based on the native implementation of Primitive in
-// frameworks/base/core/jni/android_text_StaticLayout.cpp revision b808260
-public class Primitive {
- public final @NonNull PrimitiveType type;
- public final int location;
- // The following fields don't make sense for all types.
- // Box and Glue have width only.
- // Penalty has both width and penalty.
- // Word_break has penalty only.
- public final float width;
- public final float penalty;
-
- /**
- * Use {@code PrimitiveType#getNewPrimitive()}
- */
- private Primitive(@NonNull PrimitiveType type, int location, float width, float penalty) {
- this.type = type;
- this.location = location;
- this.width = width;
- this.penalty = penalty;
- }
-
- public static enum PrimitiveType {
- /**
- * Something with a constant width that is to be typeset - like a character.
- */
- BOX,
- /**
- * Blank space with fixed width.
- */
- GLUE,
- /**
- * Aesthetic cost indicating how desirable breaking at this point will be. A penalty of
- * {@link #PENALTY_INFINITY} means a forced non-break, whereas a penalty of negative
- * {@code #PENALTY_INFINITY} means a forced break.
- * <p/>
- * Currently, it only stores penalty with values 0 or -infinity.
- */
- PENALTY,
- /**
- * For tabs - variable width space.
- */
- VARIABLE,
- /**
- * Possible breakpoints within a word. Think of this as a high cost {@link #PENALTY}.
- */
- WORD_BREAK;
-
- public Primitive getNewPrimitive(int location) {
- assert this == VARIABLE;
- return new Primitive(this, location, 0f, 0f);
- }
-
- public Primitive getNewPrimitive(int location, float value) {
- assert this == BOX || this == GLUE || this == WORD_BREAK;
- if (this == BOX || this == GLUE) {
- return new Primitive(this, location, value, 0f);
- } else {
- return new Primitive(this, location, 0f, value);
- }
- }
-
- public Primitive getNewPrimitive(int location, float width, float penalty) {
- assert this == PENALTY;
- return new Primitive(this, location, width, penalty);
- }
-
- // forced non-break, negative infinity is forced break.
- public static final float PENALTY_INFINITY = 1e7f;
- }
-}
-
diff --git a/bridge/src/android/graphics/text/TabStops.java b/bridge/src/android/graphics/text/TabStops.java
deleted file mode 100644
index 6ede24c378..0000000000
--- a/bridge/src/android/graphics/text/TabStops.java
+++ /dev/null
@@ -1,44 +0,0 @@
-/*
- * 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.
- */
-
-package android.graphics.text;
-
-import android.annotation.Nullable;
-
-// Based on the native implementation of TabStops in
-// frameworks/base/core/jni/android_text_StaticLayout.cpp revision b808260
-public class TabStops {
- @Nullable
- private float[] mStops;
- private final float mTabWidth;
-
- public TabStops(@Nullable float[] stops, float defaultTabWidth) {
- mTabWidth = defaultTabWidth;
- mStops = stops;
- }
-
- public float width(float widthSoFar) {
- if (mStops != null) {
- for (float f : mStops) {
- if (f > widthSoFar) {
- return f;
- }
- }
- }
- // find the next tabStop after widthSoFar.
- return ((widthSoFar + mTabWidth) / mTabWidth) * mTabWidth;
- }
-}
diff --git a/bridge/src/android/hardware/display/DisplayManagerGlobal.java b/bridge/src/android/hardware/display/DisplayManagerGlobal.java
new file mode 100644
index 0000000000..f58467f51b
--- /dev/null
+++ b/bridge/src/android/hardware/display/DisplayManagerGlobal.java
@@ -0,0 +1,204 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.hardware.display;
+
+
+import com.android.internal.annotations.VisibleForTesting;
+
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.content.Context;
+import android.content.res.Resources;
+import android.graphics.ColorSpace;
+import android.graphics.Point;
+import android.hardware.display.DisplayManager.DisplayListener;
+import android.media.projection.MediaProjection;
+import android.os.Handler;
+import android.util.Pair;
+import android.view.Display;
+import android.view.DisplayAdjustments;
+import android.view.DisplayInfo;
+import android.view.Surface;
+
+import java.util.List;
+
+public final class DisplayManagerGlobal {
+ public static final int EVENT_DISPLAY_ADDED = 1;
+ public static final int EVENT_DISPLAY_CHANGED = 2;
+ public static final int EVENT_DISPLAY_REMOVED = 3;
+ public static final int EVENT_DISPLAY_BRIGHTNESS_CHANGED = 4;
+
+ private static DisplayManagerGlobal sInstance;
+
+ @VisibleForTesting
+ public DisplayManagerGlobal(IDisplayManager dm) {}
+
+ public static DisplayManagerGlobal getInstance() {
+ synchronized (DisplayManagerGlobal.class) {
+ if (sInstance == null) {
+ sInstance = new DisplayManagerGlobal(null);
+ }
+ return sInstance;
+ }
+ }
+
+ public DisplayInfo getDisplayInfo(int displayId) {
+ return null;
+ }
+
+ public int[] getDisplayIds() {
+ return null;
+ }
+
+ public boolean isUidPresentOnDisplay(int uid, int displayId) {
+ return false;
+ }
+
+ public Display getCompatibleDisplay(int displayId, DisplayAdjustments daj) {
+ return null;
+ }
+
+ public Display getCompatibleDisplay(int displayId, Resources resources) {
+ return null;
+ }
+
+ public Display getRealDisplay(int displayId) {
+ return null;
+ }
+
+ public void registerDisplayListener(@NonNull DisplayListener listener,
+ @Nullable Handler handler, long eventsMask) {}
+
+ public void unregisterDisplayListener(DisplayListener listener) {}
+
+ public void startWifiDisplayScan() {}
+
+ public void stopWifiDisplayScan() {}
+
+ public void connectWifiDisplay(String deviceAddress) {}
+
+ public void pauseWifiDisplay() {}
+
+ public void resumeWifiDisplay() {}
+
+ public void disconnectWifiDisplay() {}
+
+ public void renameWifiDisplay(String deviceAddress, String alias) {}
+
+ public void forgetWifiDisplay(String deviceAddress) {}
+
+ public WifiDisplayStatus getWifiDisplayStatus() {
+ return null;
+ }
+
+ public void setUserDisabledHdrTypes(int[] userDisabledHdrTypes) {}
+
+ public void setAreUserDisabledHdrTypesAllowed(boolean areUserDisabledHdrTypesAllowed) {}
+
+ public boolean areUserDisabledHdrTypesAllowed() {
+ return false;
+ }
+
+ public int[] getUserDisabledHdrTypes() {
+ return null;
+ }
+
+ public void requestColorMode(int displayId, int colorMode) {}
+
+ public VirtualDisplay createVirtualDisplay(@NonNull Context context, MediaProjection projection,
+ @NonNull VirtualDisplayConfig virtualDisplayConfig, VirtualDisplay.Callback callback,
+ Handler handler) {
+ return null;
+ }
+
+ public void setVirtualDisplaySurface(IVirtualDisplayCallback token, Surface surface) {}
+
+ public void resizeVirtualDisplay(IVirtualDisplayCallback token,
+ int width, int height, int densityDpi) {}
+
+ public void releaseVirtualDisplay(IVirtualDisplayCallback token) {}
+
+ void setVirtualDisplayState(IVirtualDisplayCallback token, boolean isOn) {}
+
+ public Point getStableDisplaySize() {
+ return null;
+ }
+
+ public List<BrightnessChangeEvent> getBrightnessEvents(String callingPackage) {
+ return null;
+ }
+
+ public BrightnessInfo getBrightnessInfo(int displayId) {
+ return null;
+ }
+
+ public ColorSpace getPreferredWideGamutColorSpace() {
+ return null;
+ }
+
+ public void setBrightnessConfigurationForUser(BrightnessConfiguration c, int userId,
+ String packageName) {}
+
+ public BrightnessConfiguration getBrightnessConfigurationForUser(int userId) {
+ return null;
+ }
+
+ public BrightnessConfiguration getDefaultBrightnessConfiguration() {
+ return null;
+ }
+
+ public boolean isMinimalPostProcessingRequested(int displayId) {
+ return false;
+ }
+
+ public void setTemporaryBrightness(int displayId, float brightness) {}
+
+ public void setBrightness(int displayId, float brightness) {}
+
+ public float getBrightness(int displayId) {
+ return 0.0f;
+ }
+
+ public void setTemporaryAutoBrightnessAdjustment(float adjustment) {}
+
+ public Pair<float[], float[]> getMinimumBrightnessCurve() {
+ return null;
+ }
+
+ public List<AmbientBrightnessDayStats> getAmbientBrightnessStats() {
+ return null;
+ }
+
+ public void setShouldAlwaysRespectAppRequestedMode(boolean enabled) {}
+
+ public boolean shouldAlwaysRespectAppRequestedMode() {
+ return false;
+ }
+
+ public void setRefreshRateSwitchingType(int newValue) {}
+
+ public int getRefreshRateSwitchingType() {
+ return 0;
+ }
+
+ public static final String CACHE_KEY_DISPLAY_INFO_PROPERTY =
+ "cache_key.display_info";
+
+ public static void invalidateLocalDisplayInfoCaches() {}
+
+ public void disableLocalDisplayInfoCaches() {}
+}
diff --git a/bridge/src/android/media/AudioManager.java b/bridge/src/android/media/AudioManager.java
new file mode 100644
index 0000000000..a64547cf3c
--- /dev/null
+++ b/bridge/src/android/media/AudioManager.java
@@ -0,0 +1,616 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.media;
+
+import android.app.PendingIntent;
+import android.bluetooth.BluetoothCodecConfig;
+import android.bluetooth.BluetoothDevice;
+import android.content.ComponentName;
+import android.content.Context;
+import android.media.audiopolicy.AudioPolicy;
+import android.media.audiopolicy.AudioProductStrategy;
+import android.os.Handler;
+import android.view.KeyEvent;
+
+import java.io.IOException;
+import java.util.List;
+import java.util.Map;
+import java.util.concurrent.Executor;
+
+public class AudioManager {
+ public AudioManager() {}
+
+ public AudioManager(Context context) { }
+
+ public void dispatchMediaKeyEvent(KeyEvent keyEvent) { }
+
+ public void preDispatchKeyEvent(KeyEvent event, int stream) { }
+
+ public boolean isVolumeFixed() {
+ return false;
+ }
+
+ public void adjustStreamVolume(int streamType, int direction, int flags) { }
+
+ public void adjustVolume(int direction, int flags) { }
+
+ public void adjustSuggestedStreamVolume(int direction, int suggestedStreamType, int flags) { }
+
+ public void setMasterMute(boolean mute, int flags) { }
+
+ public int getRingerMode() {
+ return 0;
+ }
+
+ public int getStreamMaxVolume(int streamType) {
+ return 0;
+ }
+
+ public int getStreamMinVolume(int streamType) {
+ return 0;
+ }
+
+ public int getStreamMinVolumeInt(int streamType) {
+ return 0;
+ }
+
+ public int getStreamVolume(int streamType) {
+ return 0;
+ }
+
+ public float getStreamVolumeDb(int streamType, int index, int deviceType) {
+ return 0;
+ }
+
+ public int getLastAudibleStreamVolume(int streamType) {
+ return 0;
+ }
+
+ public int getUiSoundsStreamType() {
+ return 0;
+ }
+
+ public void setRingerMode(int ringerMode) { }
+
+ public void setStreamVolume(int streamType, int index, int flags) { }
+
+ public void setVolumeIndexForAttributes(AudioAttributes attr, int index, int flags) { }
+
+ public int getVolumeIndexForAttributes(AudioAttributes attr) {
+ return 0;
+ }
+
+ public int getMaxVolumeIndexForAttributes(AudioAttributes attr) {
+ return 0;
+ }
+
+ public int getMinVolumeIndexForAttributes(AudioAttributes attr) {
+ return 0;
+ }
+
+ public void setSupportedSystemUsages(int[] systemUsages) { }
+
+ public int[] getSupportedSystemUsages() {
+ return null;
+ }
+
+ public void setStreamSolo(int streamType, boolean state) { }
+
+ public void setStreamMute(int streamType, boolean state) { }
+
+ public boolean isStreamMute(int streamType) {
+ return false;
+ }
+
+ public boolean isMasterMute() {
+ return false;
+ }
+
+ public void forceVolumeControlStream(int streamType) { }
+
+ public boolean shouldVibrate(int vibrateType) {
+ return false;
+ }
+
+ public int getVibrateSetting(int vibrateType) {
+ return 0;
+ }
+
+ public void setVibrateSetting(int vibrateType, int vibrateSetting) { }
+
+ public void setSpeakerphoneOn(boolean on) { }
+
+ public boolean isSpeakerphoneOn() {
+ return false;
+ }
+
+ public void setAllowedCapturePolicy(int capturePolicy) { }
+
+ public int getAllowedCapturePolicy() {
+ return 0;
+ }
+
+ public boolean setPreferredDeviceForStrategy(AudioProductStrategy strategy, AudioDeviceAttributes device) {
+ return false;
+ }
+
+ public boolean removePreferredDeviceForStrategy(AudioProductStrategy strategy) {
+ return false;
+ }
+
+ public AudioDeviceAttributes getPreferredDeviceForStrategy(AudioProductStrategy strategy) {
+ return null;
+ }
+
+ public boolean setPreferredDevicesForStrategy(AudioProductStrategy strategy, List<AudioDeviceAttributes> devices) {
+ return false;
+ }
+
+ public List<AudioDeviceAttributes> getPreferredDevicesForStrategy(AudioProductStrategy strategy) {
+ return null;
+ }
+
+ public void addOnPreferredDeviceForStrategyChangedListener(Executor executor, AudioManager.OnPreferredDeviceForStrategyChangedListener listener) throws SecurityException { }
+
+ public void removeOnPreferredDeviceForStrategyChangedListener(AudioManager.OnPreferredDeviceForStrategyChangedListener listener) { }
+
+ public void addOnPreferredDevicesForStrategyChangedListener(Executor executor,
+ AudioManager.OnPreferredDevicesForStrategyChangedListener listener) throws SecurityException { }
+
+ public void removeOnPreferredDevicesForStrategyChangedListener(AudioManager.OnPreferredDevicesForStrategyChangedListener listener) { }
+
+ public boolean setPreferredDeviceForCapturePreset(int capturePreset, AudioDeviceAttributes device) {
+ return false;
+ }
+
+ public boolean clearPreferredDevicesForCapturePreset(int capturePreset) {
+ return false;
+ }
+
+ public List<AudioDeviceAttributes> getPreferredDevicesForCapturePreset(int capturePreset) {
+ return null;
+ }
+
+ public void addOnPreferredDevicesForCapturePresetChangedListener(Executor executor,
+ AudioManager.OnPreferredDevicesForCapturePresetChangedListener listener) throws SecurityException { }
+
+ public void removeOnPreferredDevicesForCapturePresetChangedListener(AudioManager.OnPreferredDevicesForCapturePresetChangedListener listener) { }
+
+ public boolean isBluetoothScoAvailableOffCall() {
+ return false;
+ }
+
+ public void startBluetoothSco() { }
+
+ public void startBluetoothScoVirtualCall() { }
+
+ public void stopBluetoothSco() { }
+
+ public void setBluetoothScoOn(boolean on) { }
+
+ public boolean isBluetoothScoOn() {
+ return false;
+ }
+
+ public void setBluetoothA2dpOn(boolean on) { }
+
+ public boolean isBluetoothA2dpOn() {
+ return false;
+ }
+
+ public void setWiredHeadsetOn(boolean on) { }
+
+ public boolean isWiredHeadsetOn() {
+ return false;
+ }
+
+ public void setMicrophoneMute(boolean on) { }
+
+ public void setMicrophoneMuteFromSwitch(boolean on) { }
+
+ public boolean isMicrophoneMute() {
+ return false;
+ }
+
+ public void setMode(int mode) { }
+
+ public int getMode() {
+ return 0;
+ }
+
+ public boolean isCallScreeningModeSupported() {
+ return false;
+ }
+
+ public void setRouting(int mode, int routes, int mask) { }
+
+ public int getRouting(int mode) {
+ return 0;
+ }
+
+ public boolean isMusicActive() {
+ return false;
+ }
+
+ public boolean isMusicActiveRemotely() {
+ return false;
+ }
+
+ public boolean isAudioFocusExclusive() {
+ return false;
+ }
+
+ public int generateAudioSessionId() {
+ return 0;
+ }
+
+ public void setParameter(String key, String value) { }
+
+ public void setParameters(String keyValuePairs) { }
+
+ public String getParameters(String keys) {
+ return null;
+ }
+
+ public void setNavigationRepeatSoundEffectsEnabled(boolean enabled) { }
+
+ public boolean areNavigationRepeatSoundEffectsEnabled() {
+ return false;
+ }
+
+ public void setHomeSoundEffectEnabled(boolean enabled) { }
+
+ public boolean isHomeSoundEffectEnabled() {
+ return false;
+ }
+
+ public void playSoundEffect(int effectType) { }
+
+ public void playSoundEffect(int effectType, int userId) { }
+
+ public void playSoundEffect(int effectType, float volume) { }
+
+ public void loadSoundEffects() { }
+
+ public void unloadSoundEffects() { }
+
+ public void registerAudioFocusRequest(AudioFocusRequest afr) { }
+
+ public void unregisterAudioFocusRequest(AudioManager.OnAudioFocusChangeListener l) { }
+
+ public int requestAudioFocus(AudioManager.OnAudioFocusChangeListener l, int streamType, int durationHint) {
+ return 0;
+ }
+
+ public int requestAudioFocus(AudioFocusRequest focusRequest) {
+ return 0;
+ }
+
+ public int abandonAudioFocusRequest(AudioFocusRequest focusRequest) {
+ return 0;
+ }
+
+ public int requestAudioFocus(AudioManager.OnAudioFocusChangeListener l, AudioAttributes requestAttributes, int durationHint, int flags) throws IllegalArgumentException {
+ return 0;
+ }
+
+ public int requestAudioFocus(AudioManager.OnAudioFocusChangeListener l, AudioAttributes requestAttributes, int durationHint, int flags, AudioPolicy ap) throws IllegalArgumentException {
+ return 0;
+ }
+
+ public int requestAudioFocus(AudioFocusRequest afr, AudioPolicy ap) {
+ return 0;
+ }
+
+ public void requestAudioFocusForCall(int streamType, int durationHint) { }
+
+ public int getFocusRampTimeMs(int focusGain, AudioAttributes attr) {
+ return 0;
+ }
+
+ public void setFocusRequestResult(AudioFocusInfo afi, int requestResult, AudioPolicy ap) { }
+
+ public int dispatchAudioFocusChange(AudioFocusInfo afi, int focusChange, AudioPolicy ap) {
+ return 0;
+ }
+
+ public void abandonAudioFocusForCall() { }
+
+ public int abandonAudioFocus(AudioManager.OnAudioFocusChangeListener l) {
+ return 0;
+ }
+
+ public int abandonAudioFocus(AudioManager.OnAudioFocusChangeListener l, AudioAttributes aa) {
+ return 0;
+ }
+
+ public void registerMediaButtonEventReceiver(ComponentName eventReceiver) { }
+
+ public void registerMediaButtonEventReceiver(PendingIntent eventReceiver) { }
+
+ public void registerMediaButtonIntent(PendingIntent pi, ComponentName eventReceiver) { }
+
+ public void unregisterMediaButtonEventReceiver(ComponentName eventReceiver) { }
+
+ public void unregisterMediaButtonEventReceiver(PendingIntent eventReceiver) { }
+
+ public void unregisterMediaButtonIntent(PendingIntent pi) { }
+
+ public void registerRemoteControlClient(RemoteControlClient rcClient) { }
+
+ public void unregisterRemoteControlClient(RemoteControlClient rcClient) { }
+
+ public boolean registerRemoteController(RemoteController rctlr) {
+ return false;
+ }
+
+ public void unregisterRemoteController(RemoteController rctlr) { }
+
+ public int registerAudioPolicy(AudioPolicy policy) {
+ return 0;
+ }
+
+ public void unregisterAudioPolicyAsync(AudioPolicy policy) { }
+
+ public void unregisterAudioPolicy(AudioPolicy policy) { }
+
+ public boolean hasRegisteredDynamicPolicy() {
+ return false;
+ }
+
+ public void registerAudioPlaybackCallback(AudioManager.AudioPlaybackCallback cb,
+ Handler handler) { }
+
+ public void unregisterAudioPlaybackCallback(AudioManager.AudioPlaybackCallback cb) { }
+
+ public List<AudioPlaybackConfiguration> getActivePlaybackConfigurations() {
+ return null;
+ }
+
+ public void registerAudioRecordingCallback(AudioManager.AudioRecordingCallback cb,
+ Handler handler) { }
+
+ public void unregisterAudioRecordingCallback(AudioManager.AudioRecordingCallback cb) { }
+
+ public List<AudioRecordingConfiguration> getActiveRecordingConfigurations() {
+ return null;
+ }
+
+ public void reloadAudioSettings() { }
+
+ public void avrcpSupportsAbsoluteVolume(String address, boolean support) { }
+
+ public boolean isSilentMode() {
+ return false;
+ }
+
+ public int getDevicesForStream(int streamType) {
+ return 0;
+ }
+
+ public List<AudioDeviceAttributes> getDevicesForAttributes(AudioAttributes attributes) {
+ return null;
+ }
+
+ public void setDeviceVolumeBehavior(AudioDeviceAttributes device, int deviceVolumeBehavior) { }
+
+ public int getDeviceVolumeBehavior(AudioDeviceAttributes device) {
+ return 0;
+ }
+
+ public void setWiredDeviceConnectionState(int type, int state, String address, String name) { }
+
+ public void setBluetoothHearingAidDeviceConnectionState(BluetoothDevice device, int state,
+ boolean suppressNoisyIntent, int musicDevice) { }
+
+ public void setBluetoothA2dpDeviceConnectionStateSuppressNoisyIntent(BluetoothDevice device,
+ int state, int profile, boolean suppressNoisyIntent, int a2dpVolume) { }
+
+ public void handleBluetoothA2dpDeviceConfigChange(BluetoothDevice device) { }
+
+ public IRingtonePlayer getRingtonePlayer() {
+ return null;
+ }
+
+ public String getProperty(String key) {
+ return null;
+ }
+
+ public boolean setAdditionalOutputDeviceDelay(AudioDeviceInfo device, long delayMillis) {
+ return false;
+ }
+
+ public long getAdditionalOutputDeviceDelay(AudioDeviceInfo device) {
+ return 0;
+ }
+
+ public long getMaxAdditionalOutputDeviceDelay(AudioDeviceInfo device) {
+ return 0;
+ }
+
+ public int getOutputLatency(int streamType) {
+ return 0;
+ }
+
+ public void setVolumeController(IVolumeController controller) { }
+
+ public void notifyVolumeControllerVisible(IVolumeController controller, boolean visible) { }
+
+ public boolean isStreamAffectedByRingerMode(int streamType) {
+ return false;
+ }
+
+ public boolean isStreamAffectedByMute(int streamType) {
+ return false;
+ }
+
+ public void disableSafeMediaVolume() { }
+
+ public void setRingerModeInternal(int ringerMode) { }
+
+ public int getRingerModeInternal() {
+ return 0;
+ }
+
+ public void setVolumePolicy(VolumePolicy policy) { }
+
+ public int setHdmiSystemAudioSupported(boolean on) {
+ return 0;
+ }
+
+ public boolean isHdmiSystemAudioSupported() {
+ return false;
+ }
+
+ public void registerAudioPortUpdateListener(AudioManager.OnAudioPortUpdateListener l) { }
+
+ public void unregisterAudioPortUpdateListener(AudioManager.OnAudioPortUpdateListener l) { }
+
+ public AudioDeviceInfo[] getDevices(int flags) {
+ return null;
+ }
+
+ public void registerAudioDeviceCallback(AudioDeviceCallback callback, Handler handler) { }
+
+ public void unregisterAudioDeviceCallback(AudioDeviceCallback callback) { }
+
+ public List<MicrophoneInfo> getMicrophones() throws IOException {
+ return null;
+ }
+
+ public List<BluetoothCodecConfig> getHwOffloadEncodingFormatsSupportedForA2DP() {
+ return null;
+ }
+
+ public void setAudioServerStateCallback(Executor executor,
+ AudioManager.AudioServerStateCallback stateCallback) { }
+
+ public void clearAudioServerStateCallback() { }
+
+ public boolean isAudioServerRunning() {
+ return false;
+ }
+
+ public Map<Integer, Boolean> getSurroundFormats() {
+ return null;
+ }
+
+ public boolean setSurroundFormatEnabled(int audioFormat, boolean enabled) {
+ return false;
+ }
+
+ public Map<Integer, Boolean> getReportedSurroundFormats() {
+ return null;
+ }
+
+ public void registerVolumeGroupCallback(Executor executor,
+ AudioManager.VolumeGroupCallback callback) { }
+
+ public void unregisterVolumeGroupCallback(AudioManager.VolumeGroupCallback callback) { }
+
+ public void adjustSuggestedStreamVolumeForUid(int suggestedStreamType, int direction,
+ int flags, String packageName, int uid, int pid, int targetSdkVersion) { }
+
+ public void adjustStreamVolumeForUid(int streamType, int direction, int flags,
+ String packageName, int uid, int pid, int targetSdkVersion) { }
+
+ public void setStreamVolumeForUid(int streamType, int index, int flags, String packageName,
+ int uid, int pid, int targetSdkVersion) { }
+
+ public void setMultiAudioFocusEnabled(boolean enabled) { }
+
+ public int getAudioHwSyncForSession(int sessionId) {
+ return 0;
+ }
+
+ public boolean setDeviceForCommunication(AudioDeviceInfo device) {
+ return false;
+ }
+
+ public void clearDeviceForCommunication() { }
+
+ public AudioDeviceInfo getDeviceForCommunication() {
+ return null;
+ }
+
+ public void addOnCommunicationDeviceChangedListener(Executor executor, AudioManager.OnCommunicationDeviceChangedListener listener) { }
+
+ public void removeOnCommunicationDeviceChangedListener(AudioManager.OnCommunicationDeviceChangedListener listener) { }
+
+ public interface OnCommunicationDeviceChangedListener {
+ void onCommunicationDeviceChanged(AudioDeviceInfo var1);
+ }
+
+ public abstract static class VolumeGroupCallback {
+ public VolumeGroupCallback() {
+ }
+
+ public void onAudioVolumeGroupChanged(int group, int flags) {
+ }
+ }
+
+ public abstract static class AudioServerStateCallback {
+ public AudioServerStateCallback() {
+ }
+
+ public void onAudioServerDown() {
+ }
+
+ public void onAudioServerUp() {
+ }
+ }
+
+ public interface OnAudioPortUpdateListener {
+ void onAudioPortListUpdate(AudioPort[] var1);
+
+ void onAudioPatchListUpdate(AudioPatch[] var1);
+
+ void onServiceDied();
+ }
+
+ public abstract static class AudioRecordingCallback {
+ public AudioRecordingCallback() {
+ }
+
+ public void onRecordingConfigChanged(List<AudioRecordingConfiguration> configs) {
+ }
+ }
+
+ public abstract static class AudioPlaybackCallback {
+ public AudioPlaybackCallback() {
+ }
+
+ public void onPlaybackConfigChanged(List<AudioPlaybackConfiguration> configs) {
+ }
+ }
+
+ public interface OnAudioFocusChangeListener {
+ void onAudioFocusChange(int var1);
+ }
+
+ public interface OnPreferredDevicesForCapturePresetChangedListener {
+ void onPreferredDevicesForCapturePresetChanged(int var1, List<AudioDeviceAttributes> var2);
+ }
+
+ public interface OnPreferredDevicesForStrategyChangedListener {
+ void onPreferredDevicesForStrategyChanged(AudioProductStrategy var1, List<AudioDeviceAttributes> var2);
+ }
+
+ public interface OnPreferredDeviceForStrategyChangedListener {
+ void onPreferredDeviceForStrategyChanged(AudioProductStrategy var1, AudioDeviceAttributes var2);
+ }
+}
diff --git a/bridge/src/android/os/HandlerThread_Delegate.java b/bridge/src/android/os/HandlerThread_Delegate.java
index afbe97c06e..18faa5de4f 100644
--- a/bridge/src/android/os/HandlerThread_Delegate.java
+++ b/bridge/src/android/os/HandlerThread_Delegate.java
@@ -35,7 +35,7 @@ import java.util.Map;
*/
public class HandlerThread_Delegate {
- private static Map<BridgeContext, List<HandlerThread>> sThreads =
+ private static final Map<BridgeContext, List<HandlerThread>> sThreads =
new HashMap<BridgeContext, List<HandlerThread>>();
public static void cleanUp(BridgeContext context) {
diff --git a/bridge/src/android/os/Handler_Delegate.java b/bridge/src/android/os/Handler_Delegate.java
index 2152c8ad19..d8688afa9d 100644
--- a/bridge/src/android/os/Handler_Delegate.java
+++ b/bridge/src/android/os/Handler_Delegate.java
@@ -16,8 +16,14 @@
package android.os;
+import com.android.ide.common.rendering.api.ILayoutLog;
+import com.android.layoutlib.bridge.Bridge;
+import com.android.layoutlib.bridge.android.BridgeContext;
+import com.android.layoutlib.bridge.util.HandlerMessageQueue;
import com.android.tools.layoutlib.annotations.LayoutlibDelegate;
+import com.android.tools.layoutlib.annotations.NotNull;
+import static com.android.layoutlib.bridge.impl.RenderAction.getCurrentContext;
/**
* Delegate overriding selected methods of android.os.Handler
@@ -28,20 +34,69 @@ import com.android.tools.layoutlib.annotations.LayoutlibDelegate;
*
*/
public class Handler_Delegate {
-
- // -------- Delegate methods
-
@LayoutlibDelegate
/*package*/ static boolean sendMessageAtTime(Handler handler, Message msg, long uptimeMillis) {
// get the callback
IHandlerCallback callback = sCallbacks.get();
if (callback != null) {
callback.sendMessageAtTime(handler, msg, uptimeMillis);
+ } else {
+ if (msg.callback != null) {
+ BridgeContext context = getCurrentContext();
+ if (context == null) {
+ return true;
+ }
+ context.getSessionInteractiveData()
+ .getHandlerMessageQueue()
+ .add(handler, uptimeMillis, msg.callback);
+ }
}
return true;
}
+ /**
+ * Current implementation of Compose uses {@link Handler#postAtFrontOfQueue} to execute state
+ * updates. We can not intercept postAtFrontOfQueue Compose calls, however we can intecept
+ * internal Handler calls. Since postAtFrontOfQueue is just a wrapper of
+ * sendMessageAtFrontOfQueue we re-define sendMessageAtFrontOfQueue here to catch Compose calls
+ * (we are only interested in them) and execute them.
+ * TODO(b/137794558): Clean/rework this when Compose reworks Handler usage.
+ */
+ @LayoutlibDelegate
+ /*package*/ static boolean sendMessageAtFrontOfQueue(Handler handler, Message msg) {
+ // We will also catch calls from the Choreographer that have no callback.
+ if (msg.callback != null) {
+ BridgeContext context = getCurrentContext();
+ if (context == null) {
+ return true;
+ }
+ context.getSessionInteractiveData()
+ .getHandlerMessageQueue()
+ .add(handler, 0, msg.callback);
+ }
+
+ return true;
+ }
+
// -------- Delegate implementation
+ /**
+ * Executed all the collected callbacks
+ *
+ * @return if there are more callbacks to execute
+ */
+ public static boolean executeCallbacks() {
+ BridgeContext context = getCurrentContext();
+ if (context == null) {
+ return false;
+ }
+ HandlerMessageQueue queue = context.getSessionInteractiveData().getHandlerMessageQueue();
+ long uptimeMillis = SystemClock_Delegate.uptimeMillis();
+ Runnable r;
+ while ((r = queue.extractFirst(uptimeMillis)) != null) {
+ executeSafely(r);
+ }
+ return queue.isNotEmpty();
+ }
public interface IHandlerCallback {
void sendMessageAtTime(Handler handler, Message msg, long uptimeMillis);
@@ -54,4 +109,18 @@ public class Handler_Delegate {
sCallbacks.set(callback);
}
+ /**
+ * The runnables we are executing are mostly library/user code and we have no guarantee that it
+ * is safe to execute them. Thus, we have to wrap each executing in try/catch block to isolate
+ * dangerous executions.
+ * @param r a runnable to be executed
+ */
+ private static void executeSafely(@NotNull Runnable r) {
+ try {
+ r.run();
+ } catch (Throwable t) {
+ Bridge.getLog().error(ILayoutLog.TAG_BROKEN, "Failed executing Handler callback", t,
+ null, null);
+ }
+ }
}
diff --git a/bridge/src/android/os/SystemClock_Delegate.java b/bridge/src/android/os/SystemClock_Delegate.java
index b6a85f893a..21c2fca6c9 100644
--- a/bridge/src/android/os/SystemClock_Delegate.java
+++ b/bridge/src/android/os/SystemClock_Delegate.java
@@ -16,9 +16,9 @@
package android.os;
+import com.android.internal.lang.System_Delegate;
import com.android.layoutlib.bridge.impl.DelegateManager;
import com.android.tools.layoutlib.annotations.LayoutlibDelegate;
-import com.android.tools.layoutlib.java.System_Delegate;
/**
* Delegate implementing the native methods of android.os.SystemClock
@@ -39,7 +39,7 @@ public class SystemClock_Delegate {
* @return milliseconds of non-sleep uptime since boot.
*/
@LayoutlibDelegate
- /*package*/ static long uptimeMillis() {
+ public static long uptimeMillis() {
return System_Delegate.currentTimeMillis() - System_Delegate.bootTimeMillis();
}
diff --git a/bridge/src/android/os/SystemProperties_Delegate.java b/bridge/src/android/os/SystemProperties_Delegate.java
deleted file mode 100644
index b5c829427e..0000000000
--- a/bridge/src/android/os/SystemProperties_Delegate.java
+++ /dev/null
@@ -1,141 +0,0 @@
-/*
- * Copyright (C) 2014 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.os;
-
-import com.android.ide.common.rendering.api.ILayoutLog;
-import com.android.layoutlib.bridge.Bridge;
-import com.android.layoutlib.bridge.impl.DelegateManager;
-import com.android.tools.layoutlib.annotations.LayoutlibDelegate;
-
-import java.util.Map;
-
-/**
- * Delegate implementing the native methods of android.os.SystemProperties
- *
- * Through the layoutlib_create tool, the original native methods of SystemProperties have been
- * replaced by calls to methods of the same name in this delegate class.
- *
- * Because it's a stateless class to start with, there's no need to keep a {@link DelegateManager}
- * around to map int to instance of the delegate.
- */
-public class SystemProperties_Delegate {
-
- @LayoutlibDelegate
- /*package*/ static String native_get(String key, String def) {
- Map<String, String> properties = Bridge.getPlatformProperties();
- String value = properties.get(key);
- if (value != null) {
- return value;
- }
-
- return def;
- }
- @LayoutlibDelegate
- /*package*/ static int native_get_int(String key, int def) {
- Map<String, String> properties = Bridge.getPlatformProperties();
- String value = properties.get(key);
- if (value != null) {
- return Integer.decode(value);
- }
-
- return def;
- }
-
- @LayoutlibDelegate
- /*package*/ static long native_get_long(String key, long def) {
- Map<String, String> properties = Bridge.getPlatformProperties();
- String value = properties.get(key);
- if (value != null) {
- return Long.decode(value);
- }
-
- return def;
- }
-
- /**
- * Values 'n', 'no', '0', 'false' or 'off' are considered false.
- * Values 'y', 'yes', '1', 'true' or 'on' are considered true.
- */
- @LayoutlibDelegate
- /*package*/ static boolean native_get_boolean(String key, boolean def) {
- Map<String, String> properties = Bridge.getPlatformProperties();
- String value = properties.get(key);
-
- if ("n".equals(value) || "no".equals(value) || "0".equals(value) || "false".equals(value)
- || "off".equals(value)) {
- return false;
- }
- //noinspection SimplifiableIfStatement
- if ("y".equals(value) || "yes".equals(value) || "1".equals(value) || "true".equals(value)
- || "on".equals(value)) {
- return true;
- }
-
- return def;
- }
-
- @LayoutlibDelegate
- /*package*/ static void native_set(String key, String def) {
- Map<String, String> properties = Bridge.getPlatformProperties();
- properties.put(key, def);
- }
-
- @LayoutlibDelegate
- /*package*/ static void native_add_change_callback() {
- // pass.
- }
-
- @LayoutlibDelegate
- /*package*/ static void native_report_sysprop_change() {
- // pass.
- }
-
- @LayoutlibDelegate
- /*package*/ static String native_get(long handle) {
- Bridge.getLog().fidelityWarning(ILayoutLog.TAG_UNSUPPORTED,
- "Layoutlib does not support SystemProperties Handle", null, null, null);
- return null;
- }
-
- @LayoutlibDelegate
- /*package*/ static int native_get_int(long handle, int def) {
- Bridge.getLog().fidelityWarning(ILayoutLog.TAG_UNSUPPORTED,
- "Layoutlib does not support SystemProperties Handle", null, null, null);
- return def;
- }
-
- @LayoutlibDelegate
- /*package*/ static long native_get_long(long handle, long def) {
- Bridge.getLog().fidelityWarning(ILayoutLog.TAG_UNSUPPORTED,
- "Layoutlib does not support SystemProperties Handle", null, null, null);
- return def;
- }
-
- @LayoutlibDelegate
- /*package*/ static boolean native_get_boolean(long handle, boolean def) {
- Bridge.getLog().fidelityWarning(ILayoutLog.TAG_UNSUPPORTED,
- "Layoutlib does not support SystemProperties Handle", null, null, null);
- return def;
- }
-
- @LayoutlibDelegate
- /*package*/ static long native_find(String name) {
- Bridge.getLog().fidelityWarning(ILayoutLog.TAG_UNSUPPORTED,
- "Layoutlib does not support SystemProperties Handle", null, null, null);
- return 0;
- }
-}
diff --git a/bridge/src/android/preference/BridgePreferenceInflater.java b/bridge/src/android/preference/BridgePreferenceInflater.java
index 3665d86134..c17313bcdf 100644
--- a/bridge/src/android/preference/BridgePreferenceInflater.java
+++ b/bridge/src/android/preference/BridgePreferenceInflater.java
@@ -16,6 +16,7 @@
package android.preference;
+import com.android.ide.common.rendering.api.ILayoutLog;
import com.android.layoutlib.bridge.android.BridgeContext;
import com.android.layoutlib.bridge.android.BridgeXmlBlockParser;
@@ -23,6 +24,8 @@ import android.content.Context;
import android.util.AttributeSet;
import android.view.InflateException;
+import static com.android.layoutlib.bridge.Bridge.getLog;
+
public class BridgePreferenceInflater extends PreferenceInflater {
public BridgePreferenceInflater(Context context, PreferenceManager preferenceManager) {
@@ -53,6 +56,11 @@ public class BridgePreferenceInflater extends PreferenceInflater {
"androidx.preference".equals(prefix)) &&
"SwitchPreferenceCompat".equals(name)) {
preference = super.createItem("SwitchPreference", prefix, attrs);
+ } else {
+ // Log the error and rethrow the exception as returning null would later result in
+ // a NullPointerException without a good error message for the user.
+ getLog().error(ILayoutLog.TAG_INFLATE, exception.getMessage(), null, null);
+ throw exception;
}
}
diff --git a/bridge/src/android/util/Log_Delegate.java b/bridge/src/android/util/Log_Delegate.java
deleted file mode 100644
index 7f432abdda..0000000000
--- a/bridge/src/android/util/Log_Delegate.java
+++ /dev/null
@@ -1,51 +0,0 @@
-/*
- * Copyright (C) 2011 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.util;
-
-import com.android.tools.layoutlib.annotations.LayoutlibDelegate;
-
-class Log_Delegate {
- // to replicate prefix visible when using 'adb logcat'
- private static char priorityChar(int priority) {
- switch (priority) {
- case Log.VERBOSE:
- return 'V';
- case Log.DEBUG:
- return 'D';
- case Log.INFO:
- return 'I';
- case Log.WARN:
- return 'W';
- case Log.ERROR:
- return 'E';
- case Log.ASSERT:
- return 'A';
- default:
- return '?';
- }
- }
-
- @LayoutlibDelegate
- static int println_native(int bufID, int priority, String tag, String msgs) {
- String prefix = priorityChar(priority) + "/" + tag + ": ";
- for (String msg: msgs.split("\n")) {
- System.out.println(prefix + msg);
- }
- return 0;
- }
-
-}
diff --git a/bridge/src/android/util/PathParser_Delegate.java b/bridge/src/android/util/PathParser_Delegate.java
deleted file mode 100644
index 46e94a7f46..0000000000
--- a/bridge/src/android/util/PathParser_Delegate.java
+++ /dev/null
@@ -1,846 +0,0 @@
-/*
- * Copyright (C) 2015 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.util;
-
-import com.android.ide.common.rendering.api.ILayoutLog;
-import com.android.layoutlib.bridge.Bridge;
-import com.android.layoutlib.bridge.impl.DelegateManager;
-import com.android.tools.layoutlib.annotations.LayoutlibDelegate;
-
-import android.annotation.NonNull;
-import android.graphics.Path_Delegate;
-
-import java.util.ArrayList;
-import java.util.Arrays;
-import java.util.logging.Level;
-import java.util.logging.Logger;
-
-/**
- * Delegate that provides implementation for native methods in {@link android.util.PathParser}
- * <p/>
- * Through the layoutlib_create tool, selected methods of PathParser have been replaced by calls to
- * methods of the same name in this delegate class.
- *
- * Most of the code has been taken from the implementation in
- * {@code tools/base/sdk-common/src/main/java/com/android/ide/common/vectordrawable/PathParser.java}
- * revision be6fe89a3b686db5a75e7e692a148699973957f3
- */
-public class PathParser_Delegate {
-
- private static final Logger LOGGER = Logger.getLogger("PathParser");
-
- // ---- Builder delegate manager ----
- private static final DelegateManager<PathParser_Delegate> sManager =
- new DelegateManager<PathParser_Delegate>(PathParser_Delegate.class);
-
- // ---- delegate data ----
- @NonNull
- private PathDataNode[] mPathDataNodes;
-
- public static PathParser_Delegate getDelegate(long nativePtr) {
- return sManager.getDelegate(nativePtr);
- }
-
- private PathParser_Delegate(@NonNull PathDataNode[] nodes) {
- mPathDataNodes = nodes;
- }
-
- public PathDataNode[] getPathDataNodes() {
- return mPathDataNodes;
- }
-
- @LayoutlibDelegate
- /*package*/ static void nParseStringForPath(long pathPtr, @NonNull String pathString, int
- stringLength) {
- Path_Delegate path_delegate = Path_Delegate.getDelegate(pathPtr);
- if (path_delegate == null) {
- return;
- }
- assert pathString.length() == stringLength;
- PathDataNode.nodesToPath(createNodesFromPathData(pathString), path_delegate);
- }
-
- @LayoutlibDelegate
- /*package*/ static void nCreatePathFromPathData(long outPathPtr, long pathData) {
- Path_Delegate path_delegate = Path_Delegate.getDelegate(outPathPtr);
- PathParser_Delegate source = sManager.getDelegate(outPathPtr);
- if (source == null || path_delegate == null) {
- return;
- }
- PathDataNode.nodesToPath(source.mPathDataNodes, path_delegate);
- }
-
- @LayoutlibDelegate
- /*package*/ static long nCreateEmptyPathData() {
- PathParser_Delegate newDelegate = new PathParser_Delegate(new PathDataNode[0]);
- return sManager.addNewDelegate(newDelegate);
- }
-
- @LayoutlibDelegate
- /*package*/ static long nCreatePathData(long nativePtr) {
- PathParser_Delegate source = sManager.getDelegate(nativePtr);
- if (source == null) {
- return 0;
- }
- PathParser_Delegate dest = new PathParser_Delegate(deepCopyNodes(source.mPathDataNodes));
- return sManager.addNewDelegate(dest);
- }
-
- @LayoutlibDelegate
- /*package*/ static long nCreatePathDataFromString(@NonNull String pathString,
- int stringLength) {
- assert pathString.length() == stringLength : "Inconsistent path string length.";
- PathDataNode[] nodes = createNodesFromPathData(pathString);
- PathParser_Delegate delegate = new PathParser_Delegate(nodes);
- return sManager.addNewDelegate(delegate);
-
- }
-
- @LayoutlibDelegate
- /*package*/ static boolean nInterpolatePathData(long outDataPtr, long fromDataPtr,
- long toDataPtr, float fraction) {
- PathParser_Delegate out = sManager.getDelegate(outDataPtr);
- PathParser_Delegate from = sManager.getDelegate(fromDataPtr);
- PathParser_Delegate to = sManager.getDelegate(toDataPtr);
- if (out == null || from == null || to == null) {
- return false;
- }
- int length = from.mPathDataNodes.length;
- if (length != to.mPathDataNodes.length) {
- Bridge.getLog().error(ILayoutLog.TAG_BROKEN,
- "Cannot interpolate path data with different lengths (from " + length + " to " +
- to.mPathDataNodes.length + ").", null, null);
- return false;
- }
- if (out.mPathDataNodes.length != length) {
- out.mPathDataNodes = new PathDataNode[length];
- }
- for (int i = 0; i < length; i++) {
- if (out.mPathDataNodes[i] == null) {
- out.mPathDataNodes[i] = new PathDataNode(from.mPathDataNodes[i]);
- }
- out.mPathDataNodes[i].interpolatePathDataNode(from.mPathDataNodes[i],
- to.mPathDataNodes[i], fraction);
- }
- return true;
- }
-
- @LayoutlibDelegate
- /*package*/ static void nFinalize(long nativePtr) {
- sManager.removeJavaReferenceFor(nativePtr);
- }
-
- @LayoutlibDelegate
- /*package*/ static boolean nCanMorph(long fromDataPtr, long toDataPtr) {
- PathParser_Delegate fromPath = PathParser_Delegate.getDelegate(fromDataPtr);
- PathParser_Delegate toPath = PathParser_Delegate.getDelegate(toDataPtr);
- if (fromPath == null || toPath == null || fromPath.getPathDataNodes() == null || toPath
- .getPathDataNodes() == null) {
- return true;
- }
- return PathParser_Delegate.canMorph(fromPath.getPathDataNodes(), toPath.getPathDataNodes());
- }
-
- @LayoutlibDelegate
- /*package*/ static void nSetPathData(long outDataPtr, long fromDataPtr) {
- PathParser_Delegate out = sManager.getDelegate(outDataPtr);
- PathParser_Delegate from = sManager.getDelegate(fromDataPtr);
- if (from == null || out == null) {
- return;
- }
- out.mPathDataNodes = deepCopyNodes(from.mPathDataNodes);
- }
-
- /**
- * @param pathData The string representing a path, the same as "d" string in svg file.
- *
- * @return an array of the PathDataNode.
- */
- @NonNull
- public static PathDataNode[] createNodesFromPathData(@NonNull String pathData) {
- int start = 0;
- int end = 1;
-
- ArrayList<PathDataNode> list = new ArrayList<PathDataNode>();
- while (end < pathData.length()) {
- end = nextStart(pathData, end);
- String s = pathData.substring(start, end).trim();
- if (s.length() > 0) {
- float[] val = getFloats(s);
- addNode(list, s.charAt(0), val);
- }
-
- start = end;
- end++;
- }
- if ((end - start) == 1 && start < pathData.length()) {
- addNode(list, pathData.charAt(start), new float[0]);
- }
- return list.toArray(new PathDataNode[list.size()]);
- }
-
- /**
- * @param source The array of PathDataNode to be duplicated.
- *
- * @return a deep copy of the <code>source</code>.
- */
- @NonNull
- public static PathDataNode[] deepCopyNodes(@NonNull PathDataNode[] source) {
- PathDataNode[] copy = new PathDataNode[source.length];
- for (int i = 0; i < source.length; i++) {
- copy[i] = new PathDataNode(source[i]);
- }
- return copy;
- }
-
- /**
- * @param nodesFrom The source path represented in an array of PathDataNode
- * @param nodesTo The target path represented in an array of PathDataNode
- * @return whether the <code>nodesFrom</code> can morph into <code>nodesTo</code>
- */
- public static boolean canMorph(PathDataNode[] nodesFrom, PathDataNode[] nodesTo) {
- if (nodesFrom == null || nodesTo == null) {
- return false;
- }
-
- if (nodesFrom.length != nodesTo.length) {
- return false;
- }
-
- for (int i = 0; i < nodesFrom.length; i ++) {
- if (nodesFrom[i].mType != nodesTo[i].mType
- || nodesFrom[i].mParams.length != nodesTo[i].mParams.length) {
- return false;
- }
- }
- return true;
- }
-
- /**
- * Update the target's data to match the source.
- * Before calling this, make sure canMorph(target, source) is true.
- *
- * @param target The target path represented in an array of PathDataNode
- * @param source The source path represented in an array of PathDataNode
- */
- public static void updateNodes(PathDataNode[] target, PathDataNode[] source) {
- for (int i = 0; i < source.length; i ++) {
- target[i].mType = source[i].mType;
- for (int j = 0; j < source[i].mParams.length; j ++) {
- target[i].mParams[j] = source[i].mParams[j];
- }
- }
- }
-
- private static int nextStart(@NonNull String s, int end) {
- char c;
-
- while (end < s.length()) {
- c = s.charAt(end);
- // Note that 'e' or 'E' are not valid path commands, but could be
- // used for floating point numbers' scientific notation.
- // Therefore, when searching for next command, we should ignore 'e'
- // and 'E'.
- if ((((c - 'A') * (c - 'Z') <= 0) || ((c - 'a') * (c - 'z') <= 0))
- && c != 'e' && c != 'E') {
- return end;
- }
- end++;
- }
- return end;
- }
-
- /**
- * Calculate the position of the next comma or space or negative sign
- *
- * @param s the string to search
- * @param start the position to start searching
- * @param result the result of the extraction, including the position of the the starting
- * position of next number, whether it is ending with a '-'.
- */
- private static void extract(@NonNull String s, int start, @NonNull ExtractFloatResult result) {
- // Now looking for ' ', ',', '.' or '-' from the start.
- int currentIndex = start;
- boolean foundSeparator = false;
- result.mEndWithNegOrDot = false;
- boolean secondDot = false;
- boolean isExponential = false;
- for (; currentIndex < s.length(); currentIndex++) {
- boolean isPrevExponential = isExponential;
- isExponential = false;
- char currentChar = s.charAt(currentIndex);
- switch (currentChar) {
- case ' ':
- case ',':
- case '\t':
- case '\n':
- foundSeparator = true;
- break;
- case '-':
- // The negative sign following a 'e' or 'E' is not a separator.
- if (currentIndex != start && !isPrevExponential) {
- foundSeparator = true;
- result.mEndWithNegOrDot = true;
- }
- break;
- case '.':
- if (!secondDot) {
- secondDot = true;
- } else {
- // This is the second dot, and it is considered as a separator.
- foundSeparator = true;
- result.mEndWithNegOrDot = true;
- }
- break;
- case 'e':
- case 'E':
- isExponential = true;
- break;
- }
- if (foundSeparator) {
- break;
- }
- }
- // When there is nothing found, then we put the end position to the end
- // of the string.
- result.mEndPosition = currentIndex;
- }
-
- /**
- * Parse the floats in the string. This is an optimized version of
- * parseFloat(s.split(",|\\s"));
- *
- * @param s the string containing a command and list of floats
- *
- * @return array of floats
- */
- @NonNull
- private static float[] getFloats(@NonNull String s) {
- if (s.charAt(0) == 'z' || s.charAt(0) == 'Z') {
- return new float[0];
- }
- try {
- float[] results = new float[s.length()];
- int count = 0;
- int startPosition = 1;
- int endPosition;
-
- ExtractFloatResult result = new ExtractFloatResult();
- int totalLength = s.length();
-
- // The startPosition should always be the first character of the
- // current number, and endPosition is the character after the current
- // number.
- while (startPosition < totalLength) {
- extract(s, startPosition, result);
- endPosition = result.mEndPosition;
-
- if (startPosition < endPosition) {
- results[count++] = Float.parseFloat(
- s.substring(startPosition, endPosition));
- }
-
- if (result.mEndWithNegOrDot) {
- // Keep the '-' or '.' sign with next number.
- startPosition = endPosition;
- } else {
- startPosition = endPosition + 1;
- }
- }
- return Arrays.copyOf(results, count);
- } catch (NumberFormatException e) {
- assert false : "error in parsing \"" + s + "\"" + e;
- return new float[0];
- }
- }
-
-
- private static void addNode(@NonNull ArrayList<PathDataNode> list, char cmd,
- @NonNull float[] val) {
- list.add(new PathDataNode(cmd, val));
- }
-
- private static class ExtractFloatResult {
- // We need to return the position of the next separator and whether the
- // next float starts with a '-' or a '.'.
- private int mEndPosition;
- private boolean mEndWithNegOrDot;
- }
-
- /**
- * Each PathDataNode represents one command in the "d" attribute of the svg file. An array of
- * PathDataNode can represent the whole "d" attribute.
- */
- public static class PathDataNode {
- private char mType;
- @NonNull
- private float[] mParams;
-
- private PathDataNode(char type, @NonNull float[] params) {
- mType = type;
- mParams = params;
- }
-
- public char getType() {
- return mType;
- }
-
- @NonNull
- public float[] getParams() {
- return mParams;
- }
-
- private PathDataNode(@NonNull PathDataNode n) {
- mType = n.mType;
- mParams = Arrays.copyOf(n.mParams, n.mParams.length);
- }
-
- /**
- * Convert an array of PathDataNode to Path. Reset the passed path as needed before
- * calling this method.
- *
- * @param node The source array of PathDataNode.
- * @param path The target Path object.
- */
- public static void nodesToPath(@NonNull PathDataNode[] node, @NonNull Path_Delegate path) {
- float[] current = new float[6];
- char previousCommand = 'm';
- //noinspection ForLoopReplaceableByForEach
- for (int i = 0; i < node.length; i++) {
- addCommand(path, current, previousCommand, node[i].mType, node[i].mParams);
- previousCommand = node[i].mType;
- }
- }
-
- /**
- * The current PathDataNode will be interpolated between the <code>nodeFrom</code> and
- * <code>nodeTo</code> according to the <code>fraction</code>.
- *
- * @param nodeFrom The start value as a PathDataNode.
- * @param nodeTo The end value as a PathDataNode
- * @param fraction The fraction to interpolate.
- */
- private void interpolatePathDataNode(@NonNull PathDataNode nodeFrom,
- @NonNull PathDataNode nodeTo, float fraction) {
- for (int i = 0; i < nodeFrom.mParams.length; i++) {
- mParams[i] = nodeFrom.mParams[i] * (1 - fraction)
- + nodeTo.mParams[i] * fraction;
- }
- }
-
- @SuppressWarnings("PointlessArithmeticExpression")
- private static void addCommand(@NonNull Path_Delegate path, float[] current,
- char previousCmd, char cmd, @NonNull float[] val) {
-
- int incr = 2;
- float currentX = current[0];
- float currentY = current[1];
- float ctrlPointX = current[2];
- float ctrlPointY = current[3];
- float currentSegmentStartX = current[4];
- float currentSegmentStartY = current[5];
- float reflectiveCtrlPointX;
- float reflectiveCtrlPointY;
-
- switch (cmd) {
- case 'z':
- case 'Z':
- path.close();
- // Path is closed here, but we need to move the pen to the
- // closed position. So we cache the segment's starting position,
- // and restore it here.
- currentX = currentSegmentStartX;
- currentY = currentSegmentStartY;
- ctrlPointX = currentSegmentStartX;
- ctrlPointY = currentSegmentStartY;
- path.moveTo(currentX, currentY);
- break;
- case 'm':
- case 'M':
- case 'l':
- case 'L':
- case 't':
- case 'T':
- incr = 2;
- break;
- case 'h':
- case 'H':
- case 'v':
- case 'V':
- incr = 1;
- break;
- case 'c':
- case 'C':
- incr = 6;
- break;
- case 's':
- case 'S':
- case 'q':
- case 'Q':
- incr = 4;
- break;
- case 'a':
- case 'A':
- incr = 7;
- break;
- }
-
- for (int k = 0; k < val.length; k += incr) {
- switch (cmd) {
- case 'm': // moveto - Start a new sub-path (relative)
- currentX += val[k + 0];
- currentY += val[k + 1];
-
- if (k > 0) {
- // According to the spec, if a moveto is followed by multiple
- // pairs of coordinates, the subsequent pairs are treated as
- // implicit lineto commands.
- path.rLineTo(val[k + 0], val[k + 1]);
- } else {
- path.rMoveTo(val[k + 0], val[k + 1]);
- currentSegmentStartX = currentX;
- currentSegmentStartY = currentY;
- }
- break;
- case 'M': // moveto - Start a new sub-path
- currentX = val[k + 0];
- currentY = val[k + 1];
-
- if (k > 0) {
- // According to the spec, if a moveto is followed by multiple
- // pairs of coordinates, the subsequent pairs are treated as
- // implicit lineto commands.
- path.lineTo(val[k + 0], val[k + 1]);
- } else {
- path.moveTo(val[k + 0], val[k + 1]);
- currentSegmentStartX = currentX;
- currentSegmentStartY = currentY;
- }
- break;
- case 'l': // lineto - Draw a line from the current point (relative)
- path.rLineTo(val[k + 0], val[k + 1]);
- currentX += val[k + 0];
- currentY += val[k + 1];
- break;
- case 'L': // lineto - Draw a line from the current point
- path.lineTo(val[k + 0], val[k + 1]);
- currentX = val[k + 0];
- currentY = val[k + 1];
- break;
- case 'h': // horizontal lineto - Draws a horizontal line (relative)
- path.rLineTo(val[k + 0], 0);
- currentX += val[k + 0];
- break;
- case 'H': // horizontal lineto - Draws a horizontal line
- path.lineTo(val[k + 0], currentY);
- currentX = val[k + 0];
- break;
- case 'v': // vertical lineto - Draws a vertical line from the current point (r)
- path.rLineTo(0, val[k + 0]);
- currentY += val[k + 0];
- break;
- case 'V': // vertical lineto - Draws a vertical line from the current point
- path.lineTo(currentX, val[k + 0]);
- currentY = val[k + 0];
- break;
- case 'c': // curveto - Draws a cubic Bézier curve (relative)
- path.rCubicTo(val[k + 0], val[k + 1], val[k + 2], val[k + 3],
- val[k + 4], val[k + 5]);
-
- ctrlPointX = currentX + val[k + 2];
- ctrlPointY = currentY + val[k + 3];
- currentX += val[k + 4];
- currentY += val[k + 5];
-
- break;
- case 'C': // curveto - Draws a cubic Bézier curve
- path.cubicTo(val[k + 0], val[k + 1], val[k + 2], val[k + 3],
- val[k + 4], val[k + 5]);
- currentX = val[k + 4];
- currentY = val[k + 5];
- ctrlPointX = val[k + 2];
- ctrlPointY = val[k + 3];
- break;
- case 's': // smooth curveto - Draws a cubic Bézier curve (reflective cp)
- reflectiveCtrlPointX = 0;
- reflectiveCtrlPointY = 0;
- if (previousCmd == 'c' || previousCmd == 's'
- || previousCmd == 'C' || previousCmd == 'S') {
- reflectiveCtrlPointX = currentX - ctrlPointX;
- reflectiveCtrlPointY = currentY - ctrlPointY;
- }
- path.rCubicTo(reflectiveCtrlPointX, reflectiveCtrlPointY,
- val[k + 0], val[k + 1],
- val[k + 2], val[k + 3]);
-
- ctrlPointX = currentX + val[k + 0];
- ctrlPointY = currentY + val[k + 1];
- currentX += val[k + 2];
- currentY += val[k + 3];
- break;
- case 'S': // shorthand/smooth curveto Draws a cubic Bézier curve(reflective cp)
- reflectiveCtrlPointX = currentX;
- reflectiveCtrlPointY = currentY;
- if (previousCmd == 'c' || previousCmd == 's'
- || previousCmd == 'C' || previousCmd == 'S') {
- reflectiveCtrlPointX = 2 * currentX - ctrlPointX;
- reflectiveCtrlPointY = 2 * currentY - ctrlPointY;
- }
- path.cubicTo(reflectiveCtrlPointX, reflectiveCtrlPointY,
- val[k + 0], val[k + 1], val[k + 2], val[k + 3]);
- ctrlPointX = val[k + 0];
- ctrlPointY = val[k + 1];
- currentX = val[k + 2];
- currentY = val[k + 3];
- break;
- case 'q': // Draws a quadratic Bézier (relative)
- path.rQuadTo(val[k + 0], val[k + 1], val[k + 2], val[k + 3]);
- ctrlPointX = currentX + val[k + 0];
- ctrlPointY = currentY + val[k + 1];
- currentX += val[k + 2];
- currentY += val[k + 3];
- break;
- case 'Q': // Draws a quadratic Bézier
- path.quadTo(val[k + 0], val[k + 1], val[k + 2], val[k + 3]);
- ctrlPointX = val[k + 0];
- ctrlPointY = val[k + 1];
- currentX = val[k + 2];
- currentY = val[k + 3];
- break;
- case 't': // Draws a quadratic Bézier curve(reflective control point)(relative)
- reflectiveCtrlPointX = 0;
- reflectiveCtrlPointY = 0;
- if (previousCmd == 'q' || previousCmd == 't'
- || previousCmd == 'Q' || previousCmd == 'T') {
- reflectiveCtrlPointX = currentX - ctrlPointX;
- reflectiveCtrlPointY = currentY - ctrlPointY;
- }
- path.rQuadTo(reflectiveCtrlPointX, reflectiveCtrlPointY,
- val[k + 0], val[k + 1]);
- ctrlPointX = currentX + reflectiveCtrlPointX;
- ctrlPointY = currentY + reflectiveCtrlPointY;
- currentX += val[k + 0];
- currentY += val[k + 1];
- break;
- case 'T': // Draws a quadratic Bézier curve (reflective control point)
- reflectiveCtrlPointX = currentX;
- reflectiveCtrlPointY = currentY;
- if (previousCmd == 'q' || previousCmd == 't'
- || previousCmd == 'Q' || previousCmd == 'T') {
- reflectiveCtrlPointX = 2 * currentX - ctrlPointX;
- reflectiveCtrlPointY = 2 * currentY - ctrlPointY;
- }
- path.quadTo(reflectiveCtrlPointX, reflectiveCtrlPointY,
- val[k + 0], val[k + 1]);
- ctrlPointX = reflectiveCtrlPointX;
- ctrlPointY = reflectiveCtrlPointY;
- currentX = val[k + 0];
- currentY = val[k + 1];
- break;
- case 'a': // Draws an elliptical arc
- // (rx ry x-axis-rotation large-arc-flag sweep-flag x y)
- drawArc(path,
- currentX,
- currentY,
- val[k + 5] + currentX,
- val[k + 6] + currentY,
- val[k + 0],
- val[k + 1],
- val[k + 2],
- val[k + 3] != 0,
- val[k + 4] != 0);
- currentX += val[k + 5];
- currentY += val[k + 6];
- ctrlPointX = currentX;
- ctrlPointY = currentY;
- break;
- case 'A': // Draws an elliptical arc
- drawArc(path,
- currentX,
- currentY,
- val[k + 5],
- val[k + 6],
- val[k + 0],
- val[k + 1],
- val[k + 2],
- val[k + 3] != 0,
- val[k + 4] != 0);
- currentX = val[k + 5];
- currentY = val[k + 6];
- ctrlPointX = currentX;
- ctrlPointY = currentY;
- break;
- }
- previousCmd = cmd;
- }
- current[0] = currentX;
- current[1] = currentY;
- current[2] = ctrlPointX;
- current[3] = ctrlPointY;
- current[4] = currentSegmentStartX;
- current[5] = currentSegmentStartY;
- }
-
- private static void drawArc(@NonNull Path_Delegate p, float x0, float y0, float x1,
- float y1, float a, float b, float theta, boolean isMoreThanHalf,
- boolean isPositiveArc) {
-
- LOGGER.log(Level.FINE, "(" + x0 + "," + y0 + ")-(" + x1 + "," + y1
- + ") {" + a + " " + b + "}");
- /* Convert rotation angle from degrees to radians */
- double thetaD = theta * Math.PI / 180.0f;
- /* Pre-compute rotation matrix entries */
- double cosTheta = Math.cos(thetaD);
- double sinTheta = Math.sin(thetaD);
- /* Transform (x0, y0) and (x1, y1) into unit space */
- /* using (inverse) rotation, followed by (inverse) scale */
- double x0p = (x0 * cosTheta + y0 * sinTheta) / a;
- double y0p = (-x0 * sinTheta + y0 * cosTheta) / b;
- double x1p = (x1 * cosTheta + y1 * sinTheta) / a;
- double y1p = (-x1 * sinTheta + y1 * cosTheta) / b;
- LOGGER.log(Level.FINE, "unit space (" + x0p + "," + y0p + ")-(" + x1p
- + "," + y1p + ")");
- /* Compute differences and averages */
- double dx = x0p - x1p;
- double dy = y0p - y1p;
- double xm = (x0p + x1p) / 2;
- double ym = (y0p + y1p) / 2;
- /* Solve for intersecting unit circles */
- double dsq = dx * dx + dy * dy;
- if (dsq == 0.0) {
- LOGGER.log(Level.FINE, " Points are coincident");
- return; /* Points are coincident */
- }
- double disc = 1.0 / dsq - 1.0 / 4.0;
- if (disc < 0.0) {
- LOGGER.log(Level.FINE, "Points are too far apart " + dsq);
- float adjust = (float) (Math.sqrt(dsq) / 1.99999);
- drawArc(p, x0, y0, x1, y1, a * adjust, b * adjust, theta,
- isMoreThanHalf, isPositiveArc);
- return; /* Points are too far apart */
- }
- double s = Math.sqrt(disc);
- double sdx = s * dx;
- double sdy = s * dy;
- double cx;
- double cy;
- if (isMoreThanHalf == isPositiveArc) {
- cx = xm - sdy;
- cy = ym + sdx;
- } else {
- cx = xm + sdy;
- cy = ym - sdx;
- }
-
- double eta0 = Math.atan2((y0p - cy), (x0p - cx));
- LOGGER.log(Level.FINE, "eta0 = Math.atan2( " + (y0p - cy) + " , "
- + (x0p - cx) + ") = " + Math.toDegrees(eta0));
-
- double eta1 = Math.atan2((y1p - cy), (x1p - cx));
- LOGGER.log(Level.FINE, "eta1 = Math.atan2( " + (y1p - cy) + " , "
- + (x1p - cx) + ") = " + Math.toDegrees(eta1));
- double sweep = (eta1 - eta0);
- if (isPositiveArc != (sweep >= 0)) {
- if (sweep > 0) {
- sweep -= 2 * Math.PI;
- } else {
- sweep += 2 * Math.PI;
- }
- }
-
- cx *= a;
- cy *= b;
- double tcx = cx;
- cx = cx * cosTheta - cy * sinTheta;
- cy = tcx * sinTheta + cy * cosTheta;
- LOGGER.log(
- Level.FINE,
- "cx, cy, a, b, x0, y0, thetaD, eta0, sweep = " + cx + " , "
- + cy + " , " + a + " , " + b + " , " + x0 + " , " + y0
- + " , " + Math.toDegrees(thetaD) + " , "
- + Math.toDegrees(eta0) + " , " + Math.toDegrees(sweep));
-
- arcToBezier(p, cx, cy, a, b, x0, y0, thetaD, eta0, sweep);
- }
-
- /**
- * Converts an arc to cubic Bezier segments and records them in p.
- *
- * @param p The target for the cubic Bezier segments
- * @param cx The x coordinate center of the ellipse
- * @param cy The y coordinate center of the ellipse
- * @param a The radius of the ellipse in the horizontal direction
- * @param b The radius of the ellipse in the vertical direction
- * @param e1x E(eta1) x coordinate of the starting point of the arc
- * @param e1y E(eta2) y coordinate of the starting point of the arc
- * @param theta The angle that the ellipse bounding rectangle makes with the horizontal
- * plane
- * @param start The start angle of the arc on the ellipse
- * @param sweep The angle (positive or negative) of the sweep of the arc on the ellipse
- */
- private static void arcToBezier(@NonNull Path_Delegate p, double cx, double cy, double a,
- double b, double e1x, double e1y, double theta, double start,
- double sweep) {
- // Taken from equations at:
- // http://spaceroots.org/documents/ellipse/node8.html
- // and http://www.spaceroots.org/documents/ellipse/node22.html
- // Maximum of 45 degrees per cubic Bezier segment
- int numSegments = (int) Math.ceil(Math.abs(sweep * 4 / Math.PI));
-
-
- double eta1 = start;
- double cosTheta = Math.cos(theta);
- double sinTheta = Math.sin(theta);
- double cosEta1 = Math.cos(eta1);
- double sinEta1 = Math.sin(eta1);
- double ep1x = (-a * cosTheta * sinEta1) - (b * sinTheta * cosEta1);
- double ep1y = (-a * sinTheta * sinEta1) + (b * cosTheta * cosEta1);
-
- double anglePerSegment = sweep / numSegments;
- for (int i = 0; i < numSegments; i++) {
- double eta2 = eta1 + anglePerSegment;
- double sinEta2 = Math.sin(eta2);
- double cosEta2 = Math.cos(eta2);
- double e2x = cx + (a * cosTheta * cosEta2)
- - (b * sinTheta * sinEta2);
- double e2y = cy + (a * sinTheta * cosEta2)
- + (b * cosTheta * sinEta2);
- double ep2x = -a * cosTheta * sinEta2 - b * sinTheta * cosEta2;
- double ep2y = -a * sinTheta * sinEta2 + b * cosTheta * cosEta2;
- double tanDiff2 = Math.tan((eta2 - eta1) / 2);
- double alpha = Math.sin(eta2 - eta1)
- * (Math.sqrt(4 + (3 * tanDiff2 * tanDiff2)) - 1) / 3;
- double q1x = e1x + alpha * ep1x;
- double q1y = e1y + alpha * ep1y;
- double q2x = e2x - alpha * ep2x;
- double q2y = e2y - alpha * ep2y;
-
- p.cubicTo((float) q1x,
- (float) q1y,
- (float) q2x,
- (float) q2y,
- (float) e2x,
- (float) e2y);
- eta1 = eta2;
- e1x = e2x;
- e1y = e2y;
- ep1x = ep2x;
- ep1y = ep2y;
- }
- }
- }
-}
diff --git a/bridge/src/android/util/imagepool/Bucket.java b/bridge/src/android/util/imagepool/Bucket.java
deleted file mode 100644
index c56224373e..0000000000
--- a/bridge/src/android/util/imagepool/Bucket.java
+++ /dev/null
@@ -1,76 +0,0 @@
-/*
- * 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.
- */
-
-package android.util.imagepool;
-
-import com.android.tools.layoutlib.annotations.Nullable;
-import com.android.tools.layoutlib.annotations.VisibleForTesting;
-
-import android.util.imagepool.ImagePool.Image.Orientation;
-
-import java.awt.image.BufferedImage;
-import java.lang.ref.SoftReference;
-import java.util.LinkedList;
-import java.util.Queue;
-
-/**
- * Data model for image pool. Bucket contains the list of same sized buffered image in soft ref.
- */
-/* private package */ class Bucket {
-
- @VisibleForTesting final Queue<SoftReference<BufferedImage>> mBufferedImageRef = new LinkedList<>();
-
- public boolean isEmpty() {
- return mBufferedImageRef.isEmpty();
- }
-
- @Nullable
- public BufferedImage remove() {
- if (mBufferedImageRef.isEmpty()) {
- return null;
- }
-
- SoftReference<BufferedImage> reference = mBufferedImageRef.remove();
- return reference == null ? null : reference.get();
- }
-
- public void offer(BufferedImage img) {
- mBufferedImageRef.offer(new SoftReference<>(img));
- }
-
- public void clear() {
- mBufferedImageRef.clear();
- }
-
- static class BucketCreationMetaData {
- public final int mWidth;
- public final int mHeight;
- public final int mType;
- public final int mNumberOfCopies;
- public final Orientation mOrientation;
- public final long mMaxCacheSize;
-
- BucketCreationMetaData(int width, int height, int type, int numberOfCopies,
- Orientation orientation, long maxCacheSize) {
- mWidth = width;
- mHeight = height;
- mType = type;
- mNumberOfCopies = numberOfCopies;
- mOrientation = orientation;
- mMaxCacheSize = maxCacheSize;
- }
- }
-} \ No newline at end of file
diff --git a/bridge/src/android/util/imagepool/ImageImpl.java b/bridge/src/android/util/imagepool/ImageImpl.java
deleted file mode 100644
index 42a6e73903..0000000000
--- a/bridge/src/android/util/imagepool/ImageImpl.java
+++ /dev/null
@@ -1,90 +0,0 @@
-/*
- * 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.
- */
-
-package android.util.imagepool;
-
-import com.android.tools.layoutlib.annotations.NotNull;
-import com.android.tools.layoutlib.annotations.Nullable;
-import com.android.tools.layoutlib.annotations.VisibleForTesting;
-import java.awt.Graphics2D;
-import java.awt.image.BufferedImage;
-import java.awt.image.ImageObserver;
-import java.util.concurrent.locks.ReadWriteLock;
-import java.util.concurrent.locks.ReentrantReadWriteLock;
-
-/**
- * Representation of buffered image. When used with ImagePool, it provides 2 features
- * (last one not yet impl'd):
- *
- * <ul>
- * <li>Automatic recycle of BufferedImage through use of reference counter</li>
- * <li>Able to re-use the image for similar size of buffered image</li>
- * <li>TODO: Able to re-use the iamge for different orientation (not yet impl'd)</li>
- * </ul>
- */
-/* private package */ class ImageImpl implements ImagePool.Image {
-
- private final ReadWriteLock mLock = new ReentrantReadWriteLock();
-
- private final int mWidth;
- private final int mHeight;
- private final Orientation mOrientation;
-
- @VisibleForTesting final BufferedImage mImg;
-
- ImageImpl(
- int width,
- int height,
- @NotNull BufferedImage img,
- Orientation orientation) {
- mImg = img;
- mWidth = width;
- mHeight = height;
- mOrientation = orientation;
- }
-
- @Override
- public int getWidth() {
- return mWidth;
- }
-
- @Override
- public int getHeight() {
- return mHeight;
- }
-
- @Override
- public void setRGB(int x, int y, int width, int height, int[] colors, int offset, int stride) {
- mLock.readLock().lock();
- try {
- // TODO: Apply orientation.
- mImg.setRGB(x, y, width, height, colors, offset, stride);
- } finally {
- mLock.readLock().unlock();
- }
- }
-
- @Override
- public void drawImage(Graphics2D graphics, int x, int y, @Nullable ImageObserver o) {
- mLock.readLock().lock();
- try {
- // TODO: Apply orientation.
- graphics.drawImage(mImg, x, y, mWidth, mHeight, o);
- } finally {
- mLock.readLock().unlock();
- }
- }
-} \ No newline at end of file
diff --git a/bridge/src/android/util/imagepool/ImagePool.java b/bridge/src/android/util/imagepool/ImagePool.java
deleted file mode 100644
index baec65de9a..0000000000
--- a/bridge/src/android/util/imagepool/ImagePool.java
+++ /dev/null
@@ -1,104 +0,0 @@
-/*
- * 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.
- */
-
-package android.util.imagepool;
-
-import com.android.tools.layoutlib.annotations.NotNull;
-
-import java.awt.Graphics2D;
-import java.awt.Image;
-import java.awt.image.BufferedImage;
-import java.awt.image.ImageObserver;
-import java.util.function.Consumer;
-
-/**
- * Simplified version of image pool that exists in Studio.
- *
- * Lacks:
- * - PhantomReference and FinalizableReference to recognize the death of references automatically.
- * (Meaning devs need to be more deligent in dispose.)
- * Has:
- * + Debugger that allows us to better trace where image is being leaked in stack.
- */
-public interface ImagePool {
-
- /**
- * Returns a new image of width w and height h.
- */
- @NotNull
- Image acquire(final int w, final int h, final int type);
-
- /**
- * Disposes the image pool, releasing all the references to the buffered images.
- */
- void dispose();
-
- /**
- * Interface that represents a buffered image. Using this wrapper allows us ot better track
- * memory usages around BufferedImage. When all of it's references are removed, it will
- * automatically be pooled back into the image pool for re-use.
- */
- interface Image {
-
- /**
- * Same as {@link BufferedImage#setRGB(int, int, int, int, int[], int, int)}
- */
- void setRGB(int x, int y, int width, int height, int[] colors, int offset, int stride);
-
- /**
- * Same as {@link Graphics2D#drawImage(java.awt.Image, int, int, ImageObserver)}
- */
- void drawImage(Graphics2D graphics, int x, int y, ImageObserver o);
-
- /**
- * Image orientation. It's not used at the moment. To be used later.
- */
- enum Orientation {
- NONE,
- CW_90
- }
-
- int getWidth();
- int getHeight();
- }
-
- /**
- * Policy for how to set up the memory pool.
- */
- class ImagePoolPolicy {
-
- public final int[] mBucketSizes;
- public final int[] mNumberOfCopies;
- public final long mBucketMaxCacheSize;
-
- /**
- * @param bucketPixelSizes - list of pixel sizes to bucket (categorize) images. The list
- * must be sorted (low to high).
- * @param numberOfCopies - Allows users to create multiple copies of the bucket. It is
- * recommended to create more copies for smaller images to avoid fragmentation in memory.
- * It must match the size of bucketPixelSizes.
- * @param bucketMaxCacheByteSize - Maximum cache byte sizes image pool is allowed to hold onto
- * in memory.
- */
- public ImagePoolPolicy(
- int[] bucketPixelSizes, int[] numberOfCopies, long bucketMaxCacheByteSize) {
- assert bucketPixelSizes.length == numberOfCopies.length;
- mBucketSizes = bucketPixelSizes;
- mNumberOfCopies = numberOfCopies;
- mBucketMaxCacheSize = bucketMaxCacheByteSize;
- }
- }
-}
diff --git a/bridge/src/android/util/imagepool/ImagePoolHelper.java b/bridge/src/android/util/imagepool/ImagePoolHelper.java
deleted file mode 100644
index 292fc59c89..0000000000
--- a/bridge/src/android/util/imagepool/ImagePoolHelper.java
+++ /dev/null
@@ -1,144 +0,0 @@
-/*
- * 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.
- */
-
-package android.util.imagepool;
-
-import com.android.tools.layoutlib.annotations.Nullable;
-
-import android.util.imagepool.Bucket.BucketCreationMetaData;
-import android.util.imagepool.ImagePool.Image.Orientation;
-import android.util.imagepool.ImagePool.ImagePoolPolicy;
-
-import java.awt.image.BufferedImage;
-import java.util.Map;
-
-/* private package */ class ImagePoolHelper {
-
- @Nullable
- public static BucketCreationMetaData getBucketCreationMetaData(int w, int h, int type, ImagePoolPolicy poolPolicy, ImagePoolStats stats) {
- // Find the bucket sizes for both dimensions
- int widthBucket = -1;
- int heightBucket = -1;
- int index = 0;
-
- for (int bucketMinSize : poolPolicy.mBucketSizes) {
- if (widthBucket == -1 && w <= bucketMinSize) {
- widthBucket = bucketMinSize;
-
- if (heightBucket != -1) {
- break;
- }
- }
- if (heightBucket == -1 && h <= bucketMinSize) {
- heightBucket = bucketMinSize;
-
- if (widthBucket != -1) {
- break;
- }
- }
- ++index;
- }
-
- stats.recordBucketRequest(w, h);
-
- if (index >= poolPolicy.mNumberOfCopies.length) {
- return null;
- }
-
- // TODO: Apply orientation
-// if (widthBucket < heightBucket) {
-// return new BucketCreationMetaData(heightBucket, widthBucket, type, poolPolicy.mNumberOfCopies[index],
-// Orientation.CW_90, poolPolicy.mBucketMaxCacheSize);
-// }
- return new BucketCreationMetaData(widthBucket, heightBucket, type, poolPolicy.mNumberOfCopies[index],
- Orientation.NONE, poolPolicy.mBucketMaxCacheSize);
- }
-
- @Nullable
- public static BufferedImage getBufferedImage(
- Bucket bucket, BucketCreationMetaData metaData, ImagePoolStats stats) {
-
- // strongref is just for gc.
- BufferedImage strongRef = populateBucket(bucket, metaData, stats);
-
- // pool is too small to create the requested buffer.
- if (bucket.isEmpty()) {
- assert strongRef == null;
- return null;
- }
-
- // Go through the bucket of soft references to find the first buffer that's not null.
- // Even if gc is triggered here, strongref should survive.
- BufferedImage img = bucket.remove();
- while (img == null && !bucket.isEmpty()) {
- img = bucket.remove();
- }
-
- if (img == null && bucket.isEmpty()) {
- // Whole buffer was null. Recurse.
- return getBufferedImage(bucket, metaData, stats);
- }
- return img;
- }
-
- /**
- * Populate the bucket in greedy manner to avoid fragmentation.
- * Behaviour is controlled by {@link ImagePoolPolicy}.
- * Returns one strong referenced buffer to avoid getting results gc'd. Null if pool is not large
- * enough.
- */
- @Nullable
- private static BufferedImage populateBucket(
- Bucket bucket, BucketCreationMetaData metaData, ImagePoolStats stats) {
- if (!bucket.isEmpty()) {
- // If not empty no need to populate.
- return null;
- }
-
- BufferedImage strongRef = null;
- for (int i = 0; i < metaData.mNumberOfCopies; i++) {
- if (!stats.fitsMaxCacheSize(
- metaData.mWidth, metaData.mHeight, metaData.mMaxCacheSize)) {
- break;
- }
- strongRef =new BufferedImage(metaData.mWidth, metaData.mHeight,
- metaData.mType);
- bucket.offer(strongRef);
- stats.recordBucketCreation(metaData.mWidth, metaData.mHeight);
- }
- return strongRef;
- }
-
- private static String toKey(int w, int h, int type) {
- return new StringBuilder()
- .append(w)
- .append('x')
- .append(h)
- .append(':')
- .append(type)
- .toString();
- }
-
- public static Bucket getBucket(Map<String, Bucket> map, BucketCreationMetaData metaData, ImagePoolPolicy mPolicy) {
- String key = toKey(metaData.mWidth, metaData.mHeight, metaData.mType);
- Bucket bucket = map.get(key);
- if (bucket == null) {
- bucket = new Bucket();
- map.put(key, bucket);
- }
- return bucket;
- }
-} \ No newline at end of file
diff --git a/bridge/src/android/util/imagepool/ImagePoolImpl.java b/bridge/src/android/util/imagepool/ImagePoolImpl.java
deleted file mode 100644
index 3da706ec1a..0000000000
--- a/bridge/src/android/util/imagepool/ImagePoolImpl.java
+++ /dev/null
@@ -1,158 +0,0 @@
-/*
- * 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.
- */
-
-package android.util.imagepool;
-
-import com.android.tools.layoutlib.annotations.Nullable;
-import com.android.tools.layoutlib.annotations.VisibleForTesting;
-
-import android.util.imagepool.Bucket.BucketCreationMetaData;
-import android.util.imagepool.ImagePool.Image.Orientation;
-
-import java.awt.image.BufferedImage;
-import java.awt.image.DataBufferInt;
-import java.lang.ref.Reference;
-import java.util.Arrays;
-import java.util.HashMap;
-import java.util.HashSet;
-import java.util.Map;
-import java.util.Set;
-import java.util.concurrent.locks.ReentrantReadWriteLock;
-import java.util.function.Consumer;
-
-import com.google.common.base.FinalizablePhantomReference;
-import com.google.common.base.FinalizableReferenceQueue;
-
-class ImagePoolImpl implements ImagePool {
-
- private final ReentrantReadWriteLock mReentrantLock = new ReentrantReadWriteLock();
- private final ImagePoolPolicy mPolicy;
- @VisibleForTesting final Map<String, Bucket> mPool = new HashMap<>();
- @VisibleForTesting final ImagePoolStats mImagePoolStats = new ImagePoolStatsProdImpl();
- private final FinalizableReferenceQueue mFinalizableReferenceQueue = new FinalizableReferenceQueue();
- private final Set<Reference<?>> mReferences = new HashSet<>();
-
- public ImagePoolImpl(ImagePoolPolicy policy) {
- mPolicy = policy;
- mImagePoolStats.start();
- }
-
- @Override
- public Image acquire(int w, int h, int type) {
- return acquire(w, h, type, null);
- }
-
- /* package private */ Image acquire(int w, int h, int type,
- @Nullable Consumer<BufferedImage> freedCallback) {
- mReentrantLock.writeLock().lock();
- try {
- BucketCreationMetaData metaData =
- ImagePoolHelper.getBucketCreationMetaData(w, h, type, mPolicy, mImagePoolStats);
- if (metaData == null) {
- return defaultImageImpl(w, h, type, freedCallback);
- }
-
- final Bucket existingBucket = ImagePoolHelper.getBucket(mPool, metaData, mPolicy);
- final BufferedImage img =
- ImagePoolHelper.getBufferedImage(existingBucket, metaData, mImagePoolStats);
- if (img == null) {
- return defaultImageImpl(w, h, type, freedCallback);
- }
-
- // Clear the image. - is this necessary?
- if (img.getRaster().getDataBuffer().getDataType() == java.awt.image.DataBuffer.TYPE_INT) {
- Arrays.fill(((DataBufferInt)img.getRaster().getDataBuffer()).getData(), 0);
- }
-
- return prepareImage(
- new ImageImpl(w, h, img, metaData.mOrientation),
- true,
- img,
- existingBucket,
- freedCallback);
- } finally {
- mReentrantLock.writeLock().unlock();
- }
- }
-
- /**
- * Add statistics as well as dispose behaviour before returning image.
- */
- private Image prepareImage(
- Image image,
- boolean offerBackToBucket,
- @Nullable BufferedImage img,
- @Nullable Bucket existingBucket,
- @Nullable Consumer<BufferedImage> freedCallback) {
- final Integer imageHash = image.hashCode();
- mImagePoolStats.acquiredImage(imageHash);
- FinalizablePhantomReference<Image> reference =
- new FinalizablePhantomReference<ImagePool.Image>(image, mFinalizableReferenceQueue) {
- @Override
- public void finalizeReferent() {
- // This method might be called twice if the user has manually called the free() method. The second call will have no effect.
- if (mReferences.remove(this)) {
- mImagePoolStats.disposeImage(imageHash);
- if (offerBackToBucket) {
- if (!mImagePoolStats.fitsMaxCacheSize(img.getWidth(), img.getHeight(),
- mPolicy.mBucketMaxCacheSize)) {
- mImagePoolStats.tooBigForCache();
- // Adding this back would go over the max cache size we set for ourselves. Release it.
- return;
- }
-
- // else stat does not change.
- existingBucket.offer(img);
- }
- if (freedCallback != null) {
- freedCallback.accept(img);
- }
- }
- }
- };
- mReferences.add(reference);
- return image;
- }
-
- /**
- * Default Image Impl to be used when the pool is not big enough.
- */
- private Image defaultImageImpl(int w, int h, int type,
- @Nullable Consumer<BufferedImage> freedCallback) {
- BufferedImage bufferedImage = new BufferedImage(w, h, type);
- mImagePoolStats.tooBigForCache();
- mImagePoolStats.recordAllocOutsidePool(w, h);
- return prepareImage(new ImageImpl(w, h, bufferedImage, Orientation.NONE),
- false, null, null, freedCallback);
- }
-
- @Override
- public void dispose() {
- mReentrantLock.writeLock().lock();
- try {
- for (Bucket bucket : mPool.values()) {
- bucket.clear();
- }
- mImagePoolStats.clear();
- } finally {
- mReentrantLock.writeLock().unlock();
- }
- }
-
- /* package private */ void printStat() {
- System.out.println(mImagePoolStats.getStatistic());
- }
-} \ No newline at end of file
diff --git a/bridge/src/android/util/imagepool/ImagePoolProvider.java b/bridge/src/android/util/imagepool/ImagePoolProvider.java
deleted file mode 100644
index dbdd8492b4..0000000000
--- a/bridge/src/android/util/imagepool/ImagePoolProvider.java
+++ /dev/null
@@ -1,40 +0,0 @@
-/*
- * 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.
- */
-
-package android.util.imagepool;
-
-import com.android.tools.layoutlib.annotations.NotNull;
-
-import android.util.imagepool.ImagePool.ImagePoolPolicy;
-
-public class ImagePoolProvider {
-
- private static ImagePool sInstance;
-
- @NotNull
- public static synchronized ImagePool get() {
- if (sInstance == null) {
- // Idea is to create more
- ImagePoolPolicy policy = new ImagePoolPolicy(
- new int[]{100, 200, 400, 600, 800, 1000, 1600, 3200},
- new int[]{ 3, 3, 2, 2, 2, 1, 1, 1},
- 10_000_000L); // 10 MB
-
- sInstance = new ImagePoolImpl(policy);
- }
- return sInstance;
- }
-} \ No newline at end of file
diff --git a/bridge/src/android/util/imagepool/ImagePoolStats.java b/bridge/src/android/util/imagepool/ImagePoolStats.java
deleted file mode 100644
index 17dab14399..0000000000
--- a/bridge/src/android/util/imagepool/ImagePoolStats.java
+++ /dev/null
@@ -1,44 +0,0 @@
-/*
- * 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.
- */
-
-package android.util.imagepool;
-
-/**
- * Keeps track of statistics. Some are purely for debugging purposes in which case it's wrapped
- * around DEBUG check.
- */
-interface ImagePoolStats {
-
- void recordBucketCreation(int widthBucket, int heightBucket);
-
- boolean fitsMaxCacheSize(int width, int height, long maxCacheSize);
-
- void clear();
-
- void recordBucketRequest(int w, int h);
-
- void recordAllocOutsidePool(int width, int height);
-
- void tooBigForCache();
-
- void acquiredImage(Integer imageHash);
-
- void disposeImage(Integer imageHash);
-
- void start();
-
- String getStatistic();
-}
diff --git a/bridge/src/android/util/imagepool/ImagePoolStatsDebugImpl.java b/bridge/src/android/util/imagepool/ImagePoolStatsDebugImpl.java
deleted file mode 100644
index de0f757a8a..0000000000
--- a/bridge/src/android/util/imagepool/ImagePoolStatsDebugImpl.java
+++ /dev/null
@@ -1,192 +0,0 @@
-/*
- * 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.
- */
-
-package android.util.imagepool;
-
-import java.lang.management.GarbageCollectorMXBean;
-import java.lang.management.ManagementFactory;
-import java.util.HashMap;
-import java.util.Map;
-
-import com.google.common.collect.HashMultiset;
-import com.google.common.collect.Multiset;
-
-/**
- * Useful impl for debugging reproable error.
- */
-public class ImagePoolStatsDebugImpl extends ImagePoolStatsProdImpl {
-
- private static String PACKAGE_NAME = ImagePoolStats.class.getPackage().getName();
-
- // Used for deugging purposes only.
- private final Map<Integer, String> mCallStack = new HashMap<>();
- private long mRequestedTotalBytes = 0;
- private long mAllocatedOutsidePoolBytes = 0;
-
- // Used for gc-related stats.
- private long mPreviousGcCollection = 0;
- private long mPreviousGcTime = 0;
-
- /** Used for policy */
- @Override
- public void recordBucketCreation(int widthBucket, int heightBucket) {
- super.recordBucketCreation(widthBucket, heightBucket);
- }
-
- @Override
- public boolean fitsMaxCacheSize(int width, int height, long maxCacheSize) {
- return super.fitsMaxCacheSize(width, height, maxCacheSize);
- }
-
- @Override
- public void clear() {
- super.clear();
-
- mRequestedTotalBytes = 0;
- mAllocatedOutsidePoolBytes = 0;
- mTooBigForPoolCount = 0;
- mCallStack.clear();
- }
-
- @Override
- public void tooBigForCache() {
- super.tooBigForCache();
- }
-
- /** Used for Debugging only */
- @Override
- public void recordBucketRequest(int w, int h) {
- mRequestedTotalBytes += (w * h * ESTIMATED_PIXEL_BYTES);
- }
-
- @Override
- public void recordAllocOutsidePool(int width, int height) {
- mAllocatedOutsidePoolBytes += (width * height * ESTIMATED_PIXEL_BYTES);
- }
-
- @Override
- public void acquiredImage(Integer imageHash) {
- for (int i = 1; i < Thread.currentThread().getStackTrace().length; i++) {
- StackTraceElement element = Thread.currentThread().getStackTrace()[i];
- String str = element.toString();
-
- if (!str.contains(PACKAGE_NAME)) {
- mCallStack.put(imageHash, str);
- break;
- }
- }
- }
-
- @Override
- public void disposeImage(Integer imageHash) {
- mCallStack.remove(imageHash);
- }
-
- @Override
- public void start() {
- long totalGarbageCollections = 0;
- long garbageCollectionTime = 0;
- for (GarbageCollectorMXBean gc : ManagementFactory.getGarbageCollectorMXBeans()) {
- long count = gc.getCollectionCount();
- if (count >= 0) {
- totalGarbageCollections += count;
- }
- long time = gc.getCollectionTime();
- if (time >= 0) {
- garbageCollectionTime += time;
- }
- }
- mPreviousGcCollection = totalGarbageCollections;
- mPreviousGcTime = garbageCollectionTime;
- }
-
- private String calculateGcStatAndReturn() {
- long totalGarbageCollections = 0;
- long garbageCollectionTime = 0;
- for (GarbageCollectorMXBean gc : ManagementFactory.getGarbageCollectorMXBeans()) {
- long count = gc.getCollectionCount();
- if (count > 0) {
- totalGarbageCollections += count;
- }
- long time = gc.getCollectionTime();
- if(time > 0) {
- garbageCollectionTime += time;
- }
- }
- totalGarbageCollections -= mPreviousGcCollection;
- garbageCollectionTime -= mPreviousGcTime;
-
- StringBuilder builder = new StringBuilder();
- builder.append("Total Garbage Collections: ");
- builder.append(totalGarbageCollections);
- builder.append("\n");
-
- builder.append("Total Garbage Collection Time (ms): ");
- builder.append(garbageCollectionTime);
- builder.append("\n");
-
- return builder.toString();
- }
-
- @Override
- public String getStatistic() {
- StringBuilder builder = new StringBuilder();
-
- builder.append(calculateGcStatAndReturn());
- builder.append("Memory\n");
- builder.append(" requested total : ");
- builder.append(mRequestedTotalBytes / 1_000_000);
- builder.append(" MB\n");
- builder.append(" allocated (in pool) : ");
- builder.append(mAllocateTotalBytes / 1_000_000);
- builder.append(" MB\n");
- builder.append(" allocated (out of pool) : ");
- builder.append(mAllocatedOutsidePoolBytes / 1_000_000);
- builder.append(" MB\n");
-
- double percent = (1.0 - (double) mRequestedTotalBytes / (mAllocateTotalBytes +
- mAllocatedOutsidePoolBytes));
- if (percent < 0.0) {
- builder.append(" saved : ");
- builder.append(-1.0 * percent);
- builder.append("%\n");
- } else {
- builder.append(" wasting : ");
- builder.append(percent);
- builder.append("%\n");
- }
-
- builder.append("Undispose images\n");
- Multiset<String> countSet = HashMultiset.create();
- for (String callsite : mCallStack.values()) {
- countSet.add(callsite);
- }
-
- for (Multiset.Entry<String> entry : countSet.entrySet()) {
- builder.append(" - ");
- builder.append(entry.getElement());
- builder.append(" - missed dispose : ");
- builder.append(entry.getCount());
- builder.append(" times\n");
- }
-
- builder.append("Number of times requested image didn't fit the pool : ");
- builder.append(mTooBigForPoolCount);
- builder.append("\n");
-
- return builder.toString();
- }
-}
diff --git a/bridge/src/android/util/imagepool/ImagePoolStatsProdImpl.java b/bridge/src/android/util/imagepool/ImagePoolStatsProdImpl.java
deleted file mode 100644
index e6c6abec29..0000000000
--- a/bridge/src/android/util/imagepool/ImagePoolStatsProdImpl.java
+++ /dev/null
@@ -1,69 +0,0 @@
-/*
- * 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.
- */
-
-package android.util.imagepool;
-
-import com.android.tools.layoutlib.annotations.NotNull;
-import com.android.tools.layoutlib.annotations.VisibleForTesting;
-
-class ImagePoolStatsProdImpl implements ImagePoolStats {
-
- static int ESTIMATED_PIXEL_BYTES = 4;
-
- // Used for determining how many buckets can be created.
- @VisibleForTesting long mAllocateTotalBytes = 0;
- @VisibleForTesting int mTooBigForPoolCount = 0;
-
- /** Used for policy */
- @Override
- public void recordBucketCreation(int widthBucket, int heightBucket) {
- mAllocateTotalBytes += (widthBucket * heightBucket * ESTIMATED_PIXEL_BYTES);
- }
-
- @Override
- public boolean fitsMaxCacheSize(int width, int height, long maxCacheSize) {
- long newTotal = mAllocateTotalBytes + (width * height * ESTIMATED_PIXEL_BYTES);
- return newTotal <= maxCacheSize;
- }
-
- @Override
- public void tooBigForCache() {
- mTooBigForPoolCount++;
- }
-
- @Override
- public void clear() {
- mAllocateTotalBytes = 0;
- }
-
- @Override
- public void recordBucketRequest(int w, int h) { }
-
- @Override
- public void recordAllocOutsidePool(int width, int height) { }
-
- @Override
- public void acquiredImage(@NotNull Integer imageHash) { }
-
- @Override
- public void disposeImage(@NotNull Integer imageHash) { }
-
- @Override
- public void start() { }
-
- @Override
- public String getStatistic() { return ""; }
-}
diff --git a/bridge/src/android/view/AttachInfo_Accessor.java b/bridge/src/android/view/AttachInfo_Accessor.java
index 60c13c09b3..5f7588ee7d 100644
--- a/bridge/src/android/view/AttachInfo_Accessor.java
+++ b/bridge/src/android/view/AttachInfo_Accessor.java
@@ -17,28 +17,44 @@
package android.view;
import android.content.Context;
+import android.graphics.HardwareRenderer;
+import android.graphics.RenderNode;
import android.os.Handler;
import android.view.View.AttachInfo;
-import com.android.layoutlib.bridge.util.ReflectionUtils;
+import com.android.layoutlib.common.util.ReflectionUtils;
/**
* Class allowing access to package-protected methods/fields.
*/
public class AttachInfo_Accessor {
- public static void setAttachInfo(View view) {
+ public static void setAttachInfo(ViewGroup view, HardwareRenderer renderer) {
Context context = view.getContext();
- WindowManager wm = (WindowManager)context.getSystemService(Context.WINDOW_SERVICE);
+ WindowManagerImpl wm = (WindowManagerImpl)context.getSystemService(Context.WINDOW_SERVICE);
+ wm.setBaseRootView(view);
Display display = wm.getDefaultDisplay();
ViewRootImpl root = new ViewRootImpl(context, display);
+ root.mAttachInfo.mThreadedRenderer = new ThreadedRenderer(context, false,
+ "delegate-renderer") {
+ @Override
+ public void registerAnimatingRenderNode(RenderNode animator) {
+ if (renderer != null) {
+ renderer.registerAnimatingRenderNode(animator);
+ } else {
+ super.registerAnimatingRenderNode(animator);
+ }
+ }
+ };
AttachInfo info = new AttachInfo(ReflectionUtils.createProxy(IWindowSession.class),
ReflectionUtils.createProxy(IWindow.class), display, root, new Handler(), null,
context);
info.mHasWindowFocus = true;
info.mWindowVisibility = View.VISIBLE;
info.mInTouchMode = false; // this is so that we can display selections.
- info.mHardwareAccelerated = false;
+ info.mHardwareAccelerated = true;
+ // We do not use this one at all, it is only needed to satisfy null checks in View
+ info.mThreadedRenderer = new ThreadedRenderer(context, false, "layoutlib-renderer");
view.dispatchAttachedToWindow(info, 0);
}
@@ -46,9 +62,21 @@ public class AttachInfo_Accessor {
view.mAttachInfo.mTreeObserver.dispatchOnPreDraw();
}
- public static void detachFromWindow(View view) {
+ public static void detachFromWindow(final View view) {
if (view != null) {
+ final View.AttachInfo attachInfo = view.mAttachInfo;
view.dispatchDetachedFromWindow();
+ if (attachInfo != null) {
+ final ThreadedRenderer threadedRenderer = attachInfo.mThreadedRenderer;
+ if(threadedRenderer != null) {
+ threadedRenderer.destroy();
+ }
+ ThreadedRenderer rootRenderer =
+ attachInfo.mViewRootImpl.mAttachInfo.mThreadedRenderer;
+ if (rootRenderer != null) {
+ rootRenderer.destroy();
+ }
+ }
}
}
diff --git a/bridge/src/android/view/BridgeInflater.java b/bridge/src/android/view/BridgeInflater.java
index dfb46f2bd4..842badeb6a 100644
--- a/bridge/src/android/view/BridgeInflater.java
+++ b/bridge/src/android/view/BridgeInflater.java
@@ -16,7 +16,7 @@
package android.view;
-import com.android.SdkConstants;
+import com.android.ide.common.rendering.api.AndroidConstants;
import com.android.ide.common.rendering.api.ILayoutLog;
import com.android.ide.common.rendering.api.LayoutlibCallback;
import com.android.ide.common.rendering.api.MergeCookie;
@@ -31,7 +31,7 @@ import com.android.layoutlib.bridge.android.BridgeXmlBlockParser;
import com.android.layoutlib.bridge.android.support.DrawerLayoutUtil;
import com.android.layoutlib.bridge.android.support.RecyclerViewUtil;
import com.android.layoutlib.bridge.impl.ParserFactory;
-import com.android.layoutlib.bridge.util.ReflectionUtils;
+import com.android.layoutlib.common.util.ReflectionUtils;
import com.android.tools.layoutlib.annotations.NotNull;
import com.android.tools.layoutlib.annotations.Nullable;
@@ -495,7 +495,7 @@ public final class BridgeInflater extends LayoutInflater {
// By default, ViewStub will be set to GONE and won't be inflate. If the XML has the
// tools:visibility attribute we'll workaround that behavior.
String visibility = attrs.getAttributeValue(BridgeConstants.NS_TOOLS_URI,
- SdkConstants.ATTR_VISIBILITY);
+ AndroidConstants.ATTR_VISIBILITY);
boolean isVisible = "visible".equals(visibility);
if (isVisible || "invisible".equals(visibility)) {
diff --git a/bridge/src/android/view/Choreographer_Delegate.java b/bridge/src/android/view/Choreographer_Delegate.java
index 3a8839f2ab..0b6a9e1e89 100644
--- a/bridge/src/android/view/Choreographer_Delegate.java
+++ b/bridge/src/android/view/Choreographer_Delegate.java
@@ -15,13 +15,18 @@
*/
package android.view;
+import com.android.ide.common.rendering.api.ILayoutLog;
+import com.android.internal.lang.System_Delegate;
+import com.android.layoutlib.bridge.Bridge;
+import com.android.layoutlib.bridge.android.BridgeContext;
+import com.android.layoutlib.bridge.impl.RenderAction;
import com.android.tools.layoutlib.annotations.LayoutlibDelegate;
+import com.android.tools.layoutlib.annotations.Nullable;
-import android.animation.AnimationHandler;
-import android.util.TimeUtils;
-import android.view.animation.AnimationUtils;
+import java.lang.StackWalker.StackFrame;
+import java.util.Optional;
-import java.util.concurrent.atomic.AtomicReference;
+import static com.android.layoutlib.bridge.impl.RenderAction.getCurrentContext;
/**
* Delegate used to provide new implementation of a select few methods of {@link Choreographer}
@@ -31,65 +36,80 @@ import java.util.concurrent.atomic.AtomicReference;
*
*/
public class Choreographer_Delegate {
- private static final AtomicReference<Choreographer> mInstance = new AtomicReference<Choreographer>();
-
- private static final int MS_16 = 16000000;
-
- @LayoutlibDelegate
- public static Choreographer getInstance() {
- if (mInstance.get() == null) {
- mInstance.compareAndSet(null, Choreographer.getInstance_Original());
- }
-
- return mInstance.get();
- }
-
@LayoutlibDelegate
public static float getRefreshRate() {
return 60.f;
}
@LayoutlibDelegate
- static void scheduleVsyncLocked(Choreographer thisChoreographer) {
- // do nothing
+ public static void postCallbackDelayedInternal(
+ Choreographer thiz, int callbackType, Object action, Object token, long delayMillis) {
+ BridgeContext context = getCurrentContext();
+ if (context == null) {
+ if (!Thread.currentThread().getName().equals("kotlinx.coroutines.DefaultExecutor")) {
+ return;
+ }
+ ClassLoader moduleClassLoader = findCallingClassLoader();
+ if (moduleClassLoader == null) {
+ return;
+ }
+ context = RenderAction.findContextFor(moduleClassLoader);
+ if (context == null) {
+ return;
+ }
+ }
+ if (callbackType != Choreographer.CALLBACK_ANIMATION) {
+ // Ignore non-animation callbacks
+ return;
+ }
+ if (action == null) {
+ Bridge.getLog().error(ILayoutLog.TAG_BROKEN,
+ "Callback with null action", (Object) null, null);
+ }
+ context.getSessionInteractiveData().getChoreographerCallbacks().add(action, delayMillis);
}
- public static void doFrame(long frameTimeNanos) {
- Choreographer thisChoreographer = Choreographer.getInstance();
-
- AnimationUtils.lockAnimationClock(frameTimeNanos / TimeUtils.NANOS_PER_MS);
-
- try {
- thisChoreographer.mLastFrameTimeNanos = frameTimeNanos - thisChoreographer.getFrameIntervalNanos();
- thisChoreographer.mFrameInfo.markInputHandlingStart();
- thisChoreographer.doCallbacks(Choreographer.CALLBACK_INPUT, frameTimeNanos, MS_16);
-
- thisChoreographer.mFrameInfo.markAnimationsStart();
- thisChoreographer.doCallbacks(Choreographer.CALLBACK_ANIMATION, frameTimeNanos, MS_16);
-
- thisChoreographer.mFrameInfo.markPerformTraversalsStart();
- thisChoreographer.doCallbacks(Choreographer.CALLBACK_TRAVERSAL, frameTimeNanos, MS_16);
-
- thisChoreographer.doCallbacks(Choreographer.CALLBACK_COMMIT, frameTimeNanos, MS_16);
- } finally {
- AnimationUtils.unlockAnimationClock();
+ @LayoutlibDelegate
+ public static void removeCallbacksInternal(
+ Choreographer thiz, int callbackType, Object action, Object token) {
+ BridgeContext context = getCurrentContext();
+ if (context == null) {
+ return;
+ }
+ if (callbackType != Choreographer.CALLBACK_ANIMATION) {
+ // Ignore non-animation callbacks
+ return;
}
+ if (action == null) {
+ Bridge.getLog().error(ILayoutLog.TAG_BROKEN,
+ "Callback with null action", (Object) null, null);
+ }
+ context.getSessionInteractiveData().getChoreographerCallbacks().remove(action);
}
- public static void clearFrames() {
- Choreographer thisChoreographer = Choreographer.getInstance();
-
- thisChoreographer.removeCallbacks(Choreographer.CALLBACK_INPUT, null, null);
- thisChoreographer.removeCallbacks(Choreographer.CALLBACK_ANIMATION, null, null);
- thisChoreographer.removeCallbacks(Choreographer.CALLBACK_TRAVERSAL, null, null);
- thisChoreographer.removeCallbacks(Choreographer.CALLBACK_COMMIT, null, null);
-
- // Release animation handler instance since it holds references to the callbacks
- AnimationHandler.sAnimatorHandler.set(null);
+ @LayoutlibDelegate
+ public static long getFrameTimeNanos(Choreographer thiz) {
+ return System.nanoTime();
}
- public static void dispose() {
- clearFrames();
- Choreographer.releaseInstance();
+ /**
+ * With this method we are trying to find a child ClassLoader that calls this method. We assume
+ * that the child ClassLoader is the first ClassLoader in the callstack that is different from
+ * the current one.
+ */
+ @Nullable
+ private static ClassLoader findCallingClassLoader() {
+ final ClassLoader current = Choreographer_Delegate.class.getClassLoader();
+ StackWalker walker = StackWalker.getInstance(StackWalker.Option.RETAIN_CLASS_REFERENCE);
+ try {
+ return walker.walk(stackFrameStream -> {
+ Optional<StackFrame> stackFrame = stackFrameStream
+ .filter(sf -> sf.getDeclaringClass().getClassLoader() != current)
+ .findFirst();
+ return stackFrame.map(f -> f.getDeclaringClass().getClassLoader()).orElse(null);
+ });
+ } catch (Throwable ex) {
+ return null;
+ }
}
}
diff --git a/bridge/src/android/view/DisplayEventReceiver_VsyncEventData_Accessor.java b/bridge/src/android/view/DisplayEventReceiver_VsyncEventData_Accessor.java
new file mode 100644
index 0000000000..a72ea7954f
--- /dev/null
+++ b/bridge/src/android/view/DisplayEventReceiver_VsyncEventData_Accessor.java
@@ -0,0 +1,33 @@
+/*
+ * 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 android.view;
+
+import com.android.tools.layoutlib.annotations.NotNull;
+
+import android.view.DisplayEventReceiver.VsyncEventData;
+
+public class DisplayEventReceiver_VsyncEventData_Accessor {
+ private static VsyncEventData sVsyncEventData;
+
+ @NotNull
+ public static VsyncEventData getVsyncEventDataInstance() {
+ if (sVsyncEventData == null) {
+ sVsyncEventData = new VsyncEventData();
+ }
+ return sVsyncEventData;
+ }
+}
diff --git a/bridge/src/android/view/RectShadowPainter.java b/bridge/src/android/view/RectShadowPainter.java
deleted file mode 100644
index 88771a7653..0000000000
--- a/bridge/src/android/view/RectShadowPainter.java
+++ /dev/null
@@ -1,205 +0,0 @@
-/*
- * Copyright (C) 2015 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.view;
-
-import com.android.layoutlib.bridge.impl.GcSnapshot;
-import com.android.layoutlib.bridge.impl.ResourceHelper;
-
-import android.graphics.BaseCanvas_Delegate;
-import android.graphics.Canvas;
-import android.graphics.Canvas_Delegate;
-import android.graphics.Color;
-import android.graphics.LinearGradient;
-import android.graphics.Outline;
-import android.graphics.Paint;
-import android.graphics.Paint.Style;
-import android.graphics.Path;
-import android.graphics.Path.FillType;
-import android.graphics.RadialGradient;
-import android.graphics.Rect;
-import android.graphics.RectF;
-import android.graphics.Region.Op;
-import android.graphics.Shader.TileMode;
-
-import java.awt.Rectangle;
-
-/**
- * Paints shadow for rounded rectangles. Inspiration from CardView. Couldn't use that directly,
- * since it modifies the size of the content, that we can't do.
- */
-public class RectShadowPainter {
-
-
- private static final int START_COLOR = ResourceHelper.getColor("#37000000");
- private static final int END_COLOR = ResourceHelper.getColor("#03000000");
- private static final float PERPENDICULAR_ANGLE = 90f;
-
- public static void paintShadow(Outline viewOutline, float elevation, Canvas canvas,
- float alpha) {
- Rect outline = new Rect();
- if (!viewOutline.getRect(outline)) {
- assert false : "Outline is not a rect shadow";
- return;
- }
-
- // TODO replacing the algorithm here to create better shadow
-
- float shadowSize = elevationToShadow(elevation);
- int saved = modifyCanvas(canvas, shadowSize);
- if (saved == -1) {
- return;
- }
-
- float radius = viewOutline.getRadius();
- if (radius <= 0) {
- // We can not paint a shadow with radius 0
- return;
- }
-
- try {
- Paint cornerPaint = new Paint(Paint.ANTI_ALIAS_FLAG | Paint.DITHER_FLAG);
- cornerPaint.setStyle(Style.FILL);
- Paint edgePaint = new Paint(cornerPaint);
- edgePaint.setAntiAlias(false);
- float outerArcRadius = radius + shadowSize;
- int[] colors = {START_COLOR, START_COLOR, END_COLOR};
- if (alpha != 1f) {
- // Correct colors using the given component alpha
- for (int i = 0; i < colors.length; i++) {
- colors[i] = Color.argb((int) (Color.alpha(colors[i]) * alpha), Color.red(colors[i]),
- Color.green(colors[i]), Color.blue(colors[i]));
- }
- }
- cornerPaint.setShader(new RadialGradient(0, 0, outerArcRadius, colors,
- new float[]{0f, radius / outerArcRadius, 1f}, TileMode.CLAMP));
- edgePaint.setShader(new LinearGradient(0, 0, -shadowSize, 0, colors[0], colors[2],
- TileMode.CLAMP));
- Path path = new Path();
- path.setFillType(FillType.EVEN_ODD);
- // A rectangle bounding the complete shadow.
- RectF shadowRect = new RectF(outline);
- shadowRect.inset(-shadowSize, -shadowSize);
- // A rectangle with edges corresponding to the straight edges of the outline.
- RectF inset = new RectF(outline);
- inset.inset(radius, radius);
- // A rectangle used to represent the edge shadow.
- RectF edgeShadowRect = new RectF();
-
-
- // left and right sides.
- edgeShadowRect.set(-shadowSize, 0f, 0f, inset.height());
- // Left shadow
- sideShadow(canvas, edgePaint, edgeShadowRect, outline.left, inset.top, 0);
- // Right shadow
- sideShadow(canvas, edgePaint, edgeShadowRect, outline.right, inset.bottom, 2);
- // Top shadow
- edgeShadowRect.set(-shadowSize, 0, 0, inset.width());
- sideShadow(canvas, edgePaint, edgeShadowRect, inset.right, outline.top, 1);
- // bottom shadow. This needs an inset so that blank doesn't appear when the content is
- // moved up.
- edgeShadowRect.set(-shadowSize, 0, shadowSize / 2f, inset.width());
- edgePaint.setShader(new LinearGradient(edgeShadowRect.right, 0, edgeShadowRect.left, 0,
- colors, new float[]{0f, 1 / 3f, 1f}, TileMode.CLAMP));
- sideShadow(canvas, edgePaint, edgeShadowRect, inset.left, outline.bottom, 3);
-
- // Draw corners.
- drawCorner(canvas, cornerPaint, path, inset.right, inset.bottom, outerArcRadius, 0);
- drawCorner(canvas, cornerPaint, path, inset.left, inset.bottom, outerArcRadius, 1);
- drawCorner(canvas, cornerPaint, path, inset.left, inset.top, outerArcRadius, 2);
- drawCorner(canvas, cornerPaint, path, inset.right, inset.top, outerArcRadius, 3);
- } finally {
- canvas.restoreToCount(saved);
- }
- }
-
- private static float elevationToShadow(float elevation) {
- // The factor is chosen by eyeballing the shadow size on device and preview.
- return elevation * 0.5f;
- }
-
- /**
- * Translate canvas by half of shadow size up, so that it appears that light is coming
- * slightly from above. Also, remove clipping, so that shadow is not clipped.
- */
- private static int modifyCanvas(Canvas canvas, float shadowSize) {
- Rect clipBounds = canvas.getClipBounds();
- if (clipBounds.isEmpty()) {
- return -1;
- }
- int saved = canvas.save();
- // Usually canvas has been translated to the top left corner of the view when this is
- // called. So, setting a clip rect at 0,0 will clip the top left part of the shadow.
- // Thus, we just expand in each direction by width and height of the canvas, while staying
- // inside the original drawing region.
- GcSnapshot snapshot = Canvas_Delegate.getDelegate(canvas).getSnapshot();
- Rectangle originalClip = snapshot.getOriginalClip();
- if (originalClip != null) {
- canvas.clipRect(originalClip.x, originalClip.y, originalClip.x + originalClip.width,
- originalClip.y + originalClip.height, Op.REPLACE);
- canvas.clipRect(-canvas.getWidth(), -canvas.getHeight(), canvas.getWidth(),
- canvas.getHeight(), Op.INTERSECT);
- }
- canvas.translate(0, shadowSize / 2f);
- return saved;
- }
-
- private static void sideShadow(Canvas canvas, Paint edgePaint,
- RectF edgeShadowRect, float dx, float dy, int rotations) {
- if (isRectEmpty(edgeShadowRect)) {
- return;
- }
- int saved = canvas.save();
- canvas.translate(dx, dy);
- canvas.rotate(rotations * PERPENDICULAR_ANGLE);
- canvas.drawRect(edgeShadowRect, edgePaint);
- canvas.restoreToCount(saved);
- }
-
- /**
- * @param canvas Canvas to draw the rectangle on.
- * @param paint Paint to use when drawing the corner.
- * @param path A path to reuse. Prevents allocating memory for each path.
- * @param x Center of circle, which this corner is a part of.
- * @param y Center of circle, which this corner is a part of.
- * @param radius radius of the arc
- * @param rotations number of quarter rotations before starting to paint the arc.
- */
- private static void drawCorner(Canvas canvas, Paint paint, Path path, float x, float y,
- float radius, int rotations) {
- int saved = canvas.save();
- canvas.translate(x, y);
- path.reset();
- path.arcTo(-radius, -radius, radius, radius, rotations * PERPENDICULAR_ANGLE,
- PERPENDICULAR_ANGLE, false);
- path.lineTo(0, 0);
- path.close();
- canvas.drawPath(path, paint);
- canvas.restoreToCount(saved);
- }
-
- /**
- * Differs from {@link RectF#isEmpty()} as this first converts the rect to int and then checks.
- * <p/>
- * This is required because {@link BaseCanvas_Delegate#native_drawRect(long, float, float,
- * float,
- * float, long)} casts the co-ordinates to int and we want to ensure that it doesn't end up
- * drawing empty rectangles, which results in IllegalArgumentException.
- */
- private static boolean isRectEmpty(RectF rect) {
- return (int) rect.left >= (int) rect.right || (int) rect.top >= (int) rect.bottom;
- }
-}
diff --git a/bridge/src/android/view/ShadowPainter.java b/bridge/src/android/view/ShadowPainter.java
deleted file mode 100644
index 788c6c3533..0000000000
--- a/bridge/src/android/view/ShadowPainter.java
+++ /dev/null
@@ -1,424 +0,0 @@
-/*
- * Copyright (C) 2014 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.view;
-
-import android.annotation.NonNull;
-
-import java.awt.Graphics2D;
-import java.awt.Image;
-import java.awt.image.BufferedImage;
-import java.awt.image.DataBufferInt;
-import java.io.IOException;
-import java.io.InputStream;
-
-import javax.imageio.ImageIO;
-
-public class ShadowPainter {
-
- /**
- * Adds a drop shadow to a semi-transparent image (of an arbitrary shape) and returns it as a
- * new image. This method attempts to mimic the same visual characteristics as the rectangular
- * shadow painting methods in this class, {@link #createRectangularDropShadow(java.awt.image.BufferedImage)}
- * and {@link #createSmallRectangularDropShadow(java.awt.image.BufferedImage)}.
- * <p/>
- * If shadowSize is less or equals to 1, no shadow will be painted and the source image will be
- * returned instead.
- *
- * @param source the source image
- * @param shadowSize the size of the shadow, normally {@link #SHADOW_SIZE or {@link
- * #SMALL_SHADOW_SIZE}}
- * @param alpha alpha value to apply to the shadow
- *
- * @return an image with the shadow painted in or the source image if shadowSize <= 1
- */
- @NonNull
- public static BufferedImage createDropShadow(BufferedImage source, int shadowSize, float
- alpha) {
- shadowSize /= 2; // make shadow size have the same meaning as in the other shadow paint methods in this class
-
- return createDropShadow(source, shadowSize, 0.7f * alpha, 0);
- }
-
- /**
- * Creates a drop shadow of a given image and returns a new image which shows the input image on
- * top of its drop shadow.
- * <p/>
- * <b>NOTE: If the shape is rectangular and opaque, consider using {@link
- * #drawRectangleShadow(Graphics2D, int, int, int, int)} instead.</b>
- *
- * @param source the source image to be shadowed
- * @param shadowSize the size of the shadow in pixels
- * @param shadowOpacity the opacity of the shadow, with 0=transparent and 1=opaque
- * @param shadowRgb the RGB int to use for the shadow color
- *
- * @return a new image with the source image on top of its shadow when shadowSize > 0 or the
- * source image otherwise
- */
- @SuppressWarnings({"SuspiciousNameCombination", "UnnecessaryLocalVariable"}) // Imported code
- public static BufferedImage createDropShadow(BufferedImage source, int shadowSize,
- float shadowOpacity, int shadowRgb) {
- if (shadowSize <= 0) {
- return source;
- }
-
- // This code is based on
- // http://www.jroller.com/gfx/entry/non_rectangular_shadow
-
- BufferedImage image;
- int width = source.getWidth();
- int height = source.getHeight();
- image = new BufferedImage(width + SHADOW_SIZE, height + SHADOW_SIZE,
- BufferedImage.TYPE_INT_ARGB);
-
- Graphics2D g2 = image.createGraphics();
- g2.drawImage(image, shadowSize, shadowSize, null);
-
- int dstWidth = image.getWidth();
- int dstHeight = image.getHeight();
-
- int left = (shadowSize - 1) >> 1;
- int right = shadowSize - left;
- int xStart = left;
- int xStop = dstWidth - right;
- int yStart = left;
- int yStop = dstHeight - right;
-
- shadowRgb &= 0x00FFFFFF;
-
- int[] aHistory = new int[shadowSize];
- int historyIdx;
-
- int aSum;
-
- int[] dataBuffer = ((DataBufferInt) image.getRaster().getDataBuffer()).getData();
- int lastPixelOffset = right * dstWidth;
- float sumDivider = shadowOpacity / shadowSize;
-
- // horizontal pass
- for (int y = 0, bufferOffset = 0; y < dstHeight; y++, bufferOffset = y * dstWidth) {
- aSum = 0;
- historyIdx = 0;
- for (int x = 0; x < shadowSize; x++, bufferOffset++) {
- int a = dataBuffer[bufferOffset] >>> 24;
- aHistory[x] = a;
- aSum += a;
- }
-
- bufferOffset -= right;
-
- for (int x = xStart; x < xStop; x++, bufferOffset++) {
- int a = (int) (aSum * sumDivider);
- dataBuffer[bufferOffset] = a << 24 | shadowRgb;
-
- // subtract the oldest pixel from the sum
- aSum -= aHistory[historyIdx];
-
- // get the latest pixel
- a = dataBuffer[bufferOffset + right] >>> 24;
- aHistory[historyIdx] = a;
- aSum += a;
-
- if (++historyIdx >= shadowSize) {
- historyIdx -= shadowSize;
- }
- }
- }
- // vertical pass
- for (int x = 0, bufferOffset = 0; x < dstWidth; x++, bufferOffset = x) {
- aSum = 0;
- historyIdx = 0;
- for (int y = 0; y < shadowSize; y++, bufferOffset += dstWidth) {
- int a = dataBuffer[bufferOffset] >>> 24;
- aHistory[y] = a;
- aSum += a;
- }
-
- bufferOffset -= lastPixelOffset;
-
- for (int y = yStart; y < yStop; y++, bufferOffset += dstWidth) {
- int a = (int) (aSum * sumDivider);
- dataBuffer[bufferOffset] = a << 24 | shadowRgb;
-
- // subtract the oldest pixel from the sum
- aSum -= aHistory[historyIdx];
-
- // get the latest pixel
- a = dataBuffer[bufferOffset + lastPixelOffset] >>> 24;
- aHistory[historyIdx] = a;
- aSum += a;
-
- if (++historyIdx >= shadowSize) {
- historyIdx -= shadowSize;
- }
- }
- }
-
- g2.drawImage(source, null, 0, 0);
- g2.dispose();
-
- return image;
- }
-
- /**
- * Draws a rectangular drop shadow (of size {@link #SHADOW_SIZE} by {@link #SHADOW_SIZE} around
- * the given source and returns a new image with both combined
- *
- * @param source the source image
- *
- * @return the source image with a drop shadow on the bottom and right
- */
- @SuppressWarnings("UnusedDeclaration")
- public static BufferedImage createRectangularDropShadow(BufferedImage source) {
- int type = source.getType();
- if (type == BufferedImage.TYPE_CUSTOM) {
- type = BufferedImage.TYPE_INT_ARGB;
- }
-
- int width = source.getWidth();
- int height = source.getHeight();
- BufferedImage image;
- image = new BufferedImage(width + SHADOW_SIZE, height + SHADOW_SIZE, type);
- Graphics2D g = image.createGraphics();
- g.drawImage(source, 0, 0, null);
- drawRectangleShadow(image, 0, 0, width, height);
- g.dispose();
-
- return image;
- }
-
- /**
- * Draws a small rectangular drop shadow (of size {@link #SMALL_SHADOW_SIZE} by {@link
- * #SMALL_SHADOW_SIZE} around the given source and returns a new image with both combined
- *
- * @param source the source image
- *
- * @return the source image with a drop shadow on the bottom and right
- */
- @SuppressWarnings("UnusedDeclaration")
- public static BufferedImage createSmallRectangularDropShadow(BufferedImage source) {
- int type = source.getType();
- if (type == BufferedImage.TYPE_CUSTOM) {
- type = BufferedImage.TYPE_INT_ARGB;
- }
-
- int width = source.getWidth();
- int height = source.getHeight();
-
- BufferedImage image;
- image = new BufferedImage(width + SMALL_SHADOW_SIZE, height + SMALL_SHADOW_SIZE, type);
-
- Graphics2D g = image.createGraphics();
- g.drawImage(source, 0, 0, null);
- drawSmallRectangleShadow(image, 0, 0, width, height);
- g.dispose();
-
- return image;
- }
-
- /**
- * Draws a drop shadow for the given rectangle into the given context. It will not draw anything
- * if the rectangle is smaller than a minimum determined by the assets used to draw the shadow
- * graphics. The size of the shadow is {@link #SHADOW_SIZE}.
- *
- * @param image the image to draw the shadow into
- * @param x the left coordinate of the left hand side of the rectangle
- * @param y the top coordinate of the top of the rectangle
- * @param width the width of the rectangle
- * @param height the height of the rectangle
- */
- public static void drawRectangleShadow(BufferedImage image,
- int x, int y, int width, int height) {
- Graphics2D gc = image.createGraphics();
- try {
- drawRectangleShadow(gc, x, y, width, height);
- } finally {
- gc.dispose();
- }
- }
-
- /**
- * Draws a small drop shadow for the given rectangle into the given context. It will not draw
- * anything if the rectangle is smaller than a minimum determined by the assets used to draw the
- * shadow graphics. The size of the shadow is {@link #SMALL_SHADOW_SIZE}.
- *
- * @param image the image to draw the shadow into
- * @param x the left coordinate of the left hand side of the rectangle
- * @param y the top coordinate of the top of the rectangle
- * @param width the width of the rectangle
- * @param height the height of the rectangle
- */
- public static void drawSmallRectangleShadow(BufferedImage image,
- int x, int y, int width, int height) {
- Graphics2D gc = image.createGraphics();
- try {
- drawSmallRectangleShadow(gc, x, y, width, height);
- } finally {
- gc.dispose();
- }
- }
-
- /**
- * The width and height of the drop shadow painted by
- * {@link #drawRectangleShadow(Graphics2D, int, int, int, int)}
- */
- public static final int SHADOW_SIZE = 20; // DO NOT EDIT. This corresponds to bitmap graphics
-
- /**
- * The width and height of the drop shadow painted by
- * {@link #drawSmallRectangleShadow(Graphics2D, int, int, int, int)}
- */
- public static final int SMALL_SHADOW_SIZE = 10; // DO NOT EDIT. Corresponds to bitmap graphics
-
- /**
- * Draws a drop shadow for the given rectangle into the given context. It will not draw anything
- * if the rectangle is smaller than a minimum determined by the assets used to draw the shadow
- * graphics.
- *
- * @param gc the graphics context to draw into
- * @param x the left coordinate of the left hand side of the rectangle
- * @param y the top coordinate of the top of the rectangle
- * @param width the width of the rectangle
- * @param height the height of the rectangle
- */
- public static void drawRectangleShadow(Graphics2D gc, int x, int y, int width, int height) {
- assert ShadowBottomLeft != null;
- assert ShadowBottomRight.getWidth(null) == SHADOW_SIZE;
- assert ShadowBottomRight.getHeight(null) == SHADOW_SIZE;
-
- int blWidth = ShadowBottomLeft.getWidth(null);
- int trHeight = ShadowTopRight.getHeight(null);
- if (width < blWidth) {
- return;
- }
- if (height < trHeight) {
- return;
- }
-
- gc.drawImage(ShadowBottomLeft, x - ShadowBottomLeft.getWidth(null), y + height, null);
- gc.drawImage(ShadowBottomRight, x + width, y + height, null);
- gc.drawImage(ShadowTopRight, x + width, y, null);
- gc.drawImage(ShadowTopLeft, x - ShadowTopLeft.getWidth(null), y, null);
- gc.drawImage(ShadowBottom,
- x, y + height, x + width, y + height + ShadowBottom.getHeight(null),
- 0, 0, ShadowBottom.getWidth(null), ShadowBottom.getHeight(null), null);
- gc.drawImage(ShadowRight,
- x + width, y + ShadowTopRight.getHeight(null), x + width + ShadowRight.getWidth(null), y + height,
- 0, 0, ShadowRight.getWidth(null), ShadowRight.getHeight(null), null);
- gc.drawImage(ShadowLeft,
- x - ShadowLeft.getWidth(null), y + ShadowTopLeft.getHeight(null), x, y + height,
- 0, 0, ShadowLeft.getWidth(null), ShadowLeft.getHeight(null), null);
- }
-
- /**
- * Draws a small drop shadow for the given rectangle into the given context. It will not draw
- * anything if the rectangle is smaller than a minimum determined by the assets used to draw the
- * shadow graphics.
- * <p/>
- *
- * @param gc the graphics context to draw into
- * @param x the left coordinate of the left hand side of the rectangle
- * @param y the top coordinate of the top of the rectangle
- * @param width the width of the rectangle
- * @param height the height of the rectangle
- */
- public static void drawSmallRectangleShadow(Graphics2D gc, int x, int y, int width,
- int height) {
- assert Shadow2BottomLeft != null;
- assert Shadow2TopRight != null;
- assert Shadow2BottomRight.getWidth(null) == SMALL_SHADOW_SIZE;
- assert Shadow2BottomRight.getHeight(null) == SMALL_SHADOW_SIZE;
-
- int blWidth = Shadow2BottomLeft.getWidth(null);
- int trHeight = Shadow2TopRight.getHeight(null);
- if (width < blWidth) {
- return;
- }
- if (height < trHeight) {
- return;
- }
-
- gc.drawImage(Shadow2BottomLeft, x - Shadow2BottomLeft.getWidth(null), y + height, null);
- gc.drawImage(Shadow2BottomRight, x + width, y + height, null);
- gc.drawImage(Shadow2TopRight, x + width, y, null);
- gc.drawImage(Shadow2TopLeft, x - Shadow2TopLeft.getWidth(null), y, null);
- gc.drawImage(Shadow2Bottom,
- x, y + height, x + width, y + height + Shadow2Bottom.getHeight(null),
- 0, 0, Shadow2Bottom.getWidth(null), Shadow2Bottom.getHeight(null), null);
- gc.drawImage(Shadow2Right,
- x + width, y + Shadow2TopRight.getHeight(null), x + width + Shadow2Right.getWidth(null), y + height,
- 0, 0, Shadow2Right.getWidth(null), Shadow2Right.getHeight(null), null);
- gc.drawImage(Shadow2Left,
- x - Shadow2Left.getWidth(null), y + Shadow2TopLeft.getHeight(null), x, y + height,
- 0, 0, Shadow2Left.getWidth(null), Shadow2Left.getHeight(null), null);
- }
-
- private static Image loadIcon(String name) {
- InputStream inputStream = ShadowPainter.class.getResourceAsStream(name);
- if (inputStream == null) {
- throw new RuntimeException("Unable to load image for shadow: " + name);
- }
- try {
- return ImageIO.read(inputStream);
- } catch (IOException e) {
- throw new RuntimeException("Unable to load image for shadow:" + name, e);
- } finally {
- try {
- inputStream.close();
- } catch (IOException e) {
- // ignore.
- }
- }
- }
-
- // Shadow graphics. This was generated by creating a drop shadow in
- // Gimp, using the parameters x offset=10, y offset=10, blur radius=10,
- // (for the small drop shadows x offset=10, y offset=10, blur radius=10)
- // color=black, and opacity=51. These values attempt to make a shadow
- // that is legible both for dark and light themes, on top of the
- // canvas background (rgb(150,150,150). Darker shadows would tend to
- // blend into the foreground for a dark holo screen, and lighter shadows
- // would be hard to spot on the canvas background. If you make adjustments,
- // make sure to check the shadow with both dark and light themes.
- //
- // After making the graphics, I cut out the top right, bottom left
- // and bottom right corners as 20x20 images, and these are reproduced by
- // painting them in the corresponding places in the target graphics context.
- // I then grabbed a single horizontal gradient line from the middle of the
- // right edge,and a single vertical gradient line from the bottom. These
- // are then painted scaled/stretched in the target to fill the gaps between
- // the three corner images.
- //
- // Filenames: bl=bottom left, b=bottom, br=bottom right, r=right, tr=top right
-
- // Normal Drop Shadow
- private static final Image ShadowBottom = loadIcon("/icons/shadow-b.png");
- private static final Image ShadowBottomLeft = loadIcon("/icons/shadow-bl.png");
- private static final Image ShadowBottomRight = loadIcon("/icons/shadow-br.png");
- private static final Image ShadowRight = loadIcon("/icons/shadow-r.png");
- private static final Image ShadowTopRight = loadIcon("/icons/shadow-tr.png");
- private static final Image ShadowTopLeft = loadIcon("/icons/shadow-tl.png");
- private static final Image ShadowLeft = loadIcon("/icons/shadow-l.png");
-
- // Small Drop Shadow
- private static final Image Shadow2Bottom = loadIcon("/icons/shadow2-b.png");
- private static final Image Shadow2BottomLeft = loadIcon("/icons/shadow2-bl.png");
- private static final Image Shadow2BottomRight = loadIcon("/icons/shadow2-br.png");
- private static final Image Shadow2Right = loadIcon("/icons/shadow2-r.png");
- private static final Image Shadow2TopRight = loadIcon("/icons/shadow2-tr.png");
- private static final Image Shadow2TopLeft = loadIcon("/icons/shadow2-tl.png");
- private static final Image Shadow2Left = loadIcon("/icons/shadow2-l.png");
-}
diff --git a/bridge/src/android/view/TextureView_Delegate.java b/bridge/src/android/view/TextureView_Delegate.java
new file mode 100644
index 0000000000..0a2abe10cb
--- /dev/null
+++ b/bridge/src/android/view/TextureView_Delegate.java
@@ -0,0 +1,31 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.view;
+
+import com.android.tools.layoutlib.annotations.LayoutlibDelegate;
+
+import android.graphics.TextureLayer;
+
+public class TextureView_Delegate {
+ @LayoutlibDelegate
+ static TextureLayer getTextureLayer(TextureView thisTextureView) {
+ /*
+ * Currently layoutlib does not support TextureLayers (no OpenGL)
+ */
+ return null;
+ }
+}
diff --git a/bridge/src/android/view/ViewGroup_Delegate.java b/bridge/src/android/view/ViewGroup_Delegate.java
deleted file mode 100644
index e71af61658..0000000000
--- a/bridge/src/android/view/ViewGroup_Delegate.java
+++ /dev/null
@@ -1,169 +0,0 @@
-/*
- * Copyright (C) 2014 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.view;
-
-import com.android.layoutlib.bridge.android.BridgeContext;
-import com.android.resources.Density;
-import com.android.tools.layoutlib.annotations.LayoutlibDelegate;
-
-import android.content.Context;
-import android.graphics.Bitmap;
-import android.graphics.Bitmap_Delegate;
-import android.graphics.Canvas;
-import android.graphics.Outline;
-import android.graphics.Path_Delegate;
-import android.graphics.Rect;
-import android.view.animation.Transformation;
-import android.view.shadow.HighQualityShadowPainter;
-
-import java.awt.Graphics2D;
-import java.awt.image.BufferedImage;
-
-/**
- * Delegate used to provide new implementation of a select few methods of {@link ViewGroup}
- * <p/>
- * Through the layoutlib_create tool, the original methods of ViewGroup have been replaced by calls
- * to methods of the same name in this delegate class.
- */
-public class ViewGroup_Delegate {
-
- /**
- * Overrides the original drawChild call in ViewGroup to draw the shadow.
- */
- @LayoutlibDelegate
- /*package*/ static boolean drawChild(ViewGroup thisVG, Canvas canvas, View child,
- long drawingTime) {
- if (child.getZ() > thisVG.getZ()) {
- // The background's bounds are set lazily. Make sure they are set correctly so that
- // the outline obtained is correct.
- child.setBackgroundBounds();
- ViewOutlineProvider outlineProvider = child.getOutlineProvider();
- if (outlineProvider != null) {
- Outline outline = child.mAttachInfo.mTmpOutline;
- outlineProvider.getOutline(child, outline);
- if (outline.mPath != null || (outline.mRect != null && !outline.mRect.isEmpty())) {
- int restoreTo = transformCanvas(thisVG, canvas, child);
- drawShadow(thisVG, canvas, child, outline);
- canvas.restoreToCount(restoreTo);
- }
- }
- }
- return thisVG.drawChild_Original(canvas, child, drawingTime);
- }
-
- private static void drawShadow(ViewGroup parent, Canvas canvas, View child,
- Outline outline) {
- boolean highQualityShadow = false;
- boolean enableShadow = true;
- float elevation = getElevation(child, parent);
- Context bridgeContext = parent.getContext();
- if (bridgeContext instanceof BridgeContext) {
- highQualityShadow = ((BridgeContext) bridgeContext).isHighQualityShadows();
- enableShadow = ((BridgeContext) bridgeContext).isShadowsEnabled();
- }
-
- if (!enableShadow) {
- return;
- }
-
- if(outline.mMode == Outline.MODE_ROUND_RECT && outline.mRect != null) {
- if (highQualityShadow) {
- float densityDpi = bridgeContext.getResources().getDisplayMetrics().densityDpi;
- HighQualityShadowPainter.paintRectShadow(
- parent, outline, elevation, canvas, child.getAlpha(), densityDpi);
- } else {
- RectShadowPainter.paintShadow(outline, elevation, canvas, child.getAlpha());
- }
- return;
- }
-
- BufferedImage shadow = null;
- if (outline.mPath != null) {
- shadow = getPathShadow(outline, canvas, elevation, child.getAlpha());
- }
- if (shadow == null) {
- return;
- }
- Bitmap bitmap = Bitmap_Delegate.createBitmap(shadow, false,
- Density.getEnum(canvas.getDensity()));
- canvas.save();
- Rect clipBounds = canvas.getClipBounds();
- Rect newBounds = new Rect(clipBounds);
- newBounds.inset((int)-elevation, (int)-elevation);
- canvas.clipRectUnion(newBounds);
- canvas.drawBitmap(bitmap, 0, 0, null);
- canvas.restore();
- }
-
- private static float getElevation(View child, ViewGroup parent) {
- return child.getZ() - parent.getZ();
- }
-
- private static BufferedImage getPathShadow(Outline outline, Canvas canvas, float elevation,
- float alpha) {
- Rect clipBounds = canvas.getClipBounds();
- if (clipBounds.isEmpty()) {
- return null;
- }
- BufferedImage image = new BufferedImage(clipBounds.width(), clipBounds.height(),
- BufferedImage.TYPE_INT_ARGB);
- Graphics2D graphics = image.createGraphics();
- graphics.draw(Path_Delegate.getDelegate(outline.mPath.mNativePath).getJavaShape());
- graphics.dispose();
- return ShadowPainter.createDropShadow(image, (int) elevation, alpha);
- }
-
- // Copied from android.view.View#draw(Canvas, ViewGroup, long) and removed code paths
- // which were never taken. Ideally, we should hook up the shadow code in the same method so
- // that we don't have to transform the canvas twice.
- private static int transformCanvas(ViewGroup thisVG, Canvas canvas, View child) {
- final int restoreTo = canvas.save();
- final boolean childHasIdentityMatrix = child.hasIdentityMatrix();
- int flags = thisVG.mGroupFlags;
- Transformation transformToApply = null;
- boolean concatMatrix = false;
- if ((flags & ViewGroup.FLAG_SUPPORT_STATIC_TRANSFORMATIONS) != 0) {
- final Transformation t = thisVG.getChildTransformation();
- final boolean hasTransform = thisVG.getChildStaticTransformation(child, t);
- if (hasTransform) {
- final int transformType = t.getTransformationType();
- transformToApply = transformType != Transformation.TYPE_IDENTITY ? t : null;
- concatMatrix = (transformType & Transformation.TYPE_MATRIX) != 0;
- }
- }
- concatMatrix |= childHasIdentityMatrix;
-
- canvas.translate(child.mLeft, child.mTop);
- float alpha = child.getAlpha() * child.getTransitionAlpha();
-
- if (transformToApply != null || alpha < 1 || !childHasIdentityMatrix) {
- if (transformToApply != null || !childHasIdentityMatrix) {
-
- if (transformToApply != null) {
- if (concatMatrix) {
- canvas.concat(transformToApply.getMatrix());
- }
- }
- if (!childHasIdentityMatrix) {
- canvas.concat(child.getMatrix());
- }
-
- }
- }
- return restoreTo;
- }
-}
diff --git a/bridge/src/android/view/WindowManagerImpl.java b/bridge/src/android/view/WindowManagerImpl.java
index 36b7165c1e..dabeda3584 100644
--- a/bridge/src/android/view/WindowManagerImpl.java
+++ b/bridge/src/android/view/WindowManagerImpl.java
@@ -16,12 +16,14 @@
package android.view;
import static android.view.View.SYSTEM_UI_FLAG_VISIBLE;
+import static android.view.ViewGroup.LayoutParams.WRAP_CONTENT;
import static android.view.WindowManager.LayoutParams.SOFT_INPUT_ADJUST_NOTHING;
import static android.view.WindowManager.LayoutParams.TYPE_APPLICATION;
import android.app.ResourcesManager;
import android.content.Context;
import android.content.res.Configuration;
+import android.graphics.Color;
import android.graphics.Point;
import android.graphics.Rect;
import android.graphics.Region;
@@ -29,8 +31,10 @@ import android.os.IBinder;
import android.os.RemoteException;
import android.util.DisplayMetrics;
import android.view.Display.Mode;
+import android.widget.FrameLayout;
import com.android.ide.common.rendering.api.ILayoutLog;
+import com.android.internal.R;
import com.android.layoutlib.bridge.Bridge;
public class WindowManagerImpl implements WindowManager {
@@ -38,6 +42,15 @@ public class WindowManagerImpl implements WindowManager {
private final Context mContext;
private final DisplayMetrics mMetrics;
private final Display mDisplay;
+ /**
+ * Root view of the base window, new windows will be added on top of this.
+ */
+ private ViewGroup mBaseRootView;
+ /**
+ * Root view of the current window at the top of the display,
+ * null if there is only the base window present.
+ */
+ private ViewGroup mCurrentRootView;
public WindowManagerImpl(Context context, DisplayMetrics metrics) {
mContext = context;
@@ -55,15 +68,12 @@ public class WindowManagerImpl implements WindowManager {
}
public WindowManagerImpl createLocalWindowManager(Window parentWindow) {
- Bridge.getLog().fidelityWarning(ILayoutLog.TAG_UNSUPPORTED,
- "The preview does not support multiple windows.",
- null, null, null);
return this;
}
public WindowManagerImpl createPresentationWindowManager(Context displayContext) {
Bridge.getLog().fidelityWarning(ILayoutLog.TAG_UNSUPPORTED,
- "The preview does not support multiple windows.",
+ "The preview does not fully support multiple windows.",
null, null, null);
return this;
}
@@ -86,12 +96,78 @@ public class WindowManagerImpl implements WindowManager {
@Override
public void addView(View arg0, android.view.ViewGroup.LayoutParams arg1) {
- // pass
+ if (mBaseRootView == null) {
+ return;
+ }
+ if (mCurrentRootView == null) {
+ FrameLayout layout = new FrameLayout(mContext) {
+ @Override
+ public boolean dispatchTouchEvent(MotionEvent ev) {
+ View baseRootParent = (View)mBaseRootView.getParent();
+ if (baseRootParent != null) {
+ ev.offsetLocation(-baseRootParent.getX(), -baseRootParent.getY());
+ }
+ return super.dispatchTouchEvent(ev);
+ }
+
+ @Override
+ protected void measureChildWithMargins(View child, int parentWidthMeasureSpec,
+ int widthUsed, int parentHeightMeasureSpec, int heightUsed) {
+ // This reproduces ViewRootImpl#measureHierarchy as this FrameLayout should
+ // be treated as a ViewRoot.
+ ViewGroup.LayoutParams lp = child.getLayoutParams();
+ int parentWidth = MeasureSpec.getSize(parentWidthMeasureSpec);
+ int parentHeight = MeasureSpec.getSize(parentHeightMeasureSpec);
+ int childWidthMeasureSpec = 0;
+ int childHeightMeasureSpec = ViewRootImpl.getRootMeasureSpec(parentHeight,
+ lp.height, 0);
+ if (lp.width == WRAP_CONTENT) {
+ int baseSize =
+ mContext.getResources().getDimensionPixelSize(R.dimen.config_prefDialogWidth);
+ if (baseSize != 0 && baseSize < parentWidth) {
+ childWidthMeasureSpec = ViewRootImpl.getRootMeasureSpec(baseSize,
+ lp.width, 0);
+ }
+ }
+ if (childWidthMeasureSpec == 0) {
+ childWidthMeasureSpec = ViewRootImpl.getRootMeasureSpec(parentWidth,
+ lp.width, 0);
+ }
+ child.measure(childWidthMeasureSpec, childHeightMeasureSpec);
+ }
+ };
+ // The window root view should not handle touch events.
+ // Events need to be dispatched to the base view inside the window,
+ // with coordinates shifted accordingly.
+ layout.setOnTouchListener((v, event) -> {
+ event.offsetLocation(-arg0.getX(), -arg0.getY());
+ return arg0.dispatchTouchEvent(event);
+ });
+ mBaseRootView.addView(layout, new FrameLayout.LayoutParams(LayoutParams.MATCH_PARENT,
+ LayoutParams.MATCH_PARENT));
+ mCurrentRootView = layout;
+ }
+
+ FrameLayout.LayoutParams frameLayoutParams = new FrameLayout.LayoutParams(arg1);
+ if (arg1 instanceof WindowManager.LayoutParams) {
+ LayoutParams params = (LayoutParams) arg1;
+ frameLayoutParams.gravity = params.gravity;
+ if ((params.flags & LayoutParams.FLAG_DIM_BEHIND) != 0) {
+ mCurrentRootView.setBackgroundColor(Color.argb(params.dimAmount, 0, 0, 0));
+ }
+ }
+ mCurrentRootView.addView(arg0, frameLayoutParams);
}
@Override
public void removeView(View arg0) {
- // pass
+ if (mCurrentRootView != null) {
+ mCurrentRootView.removeView(arg0);
+ if (mBaseRootView != null && mCurrentRootView.getChildCount() == 0) {
+ mBaseRootView.removeView(mCurrentRootView);
+ mCurrentRootView = null;
+ }
+ }
}
@Override
@@ -102,7 +178,7 @@ public class WindowManagerImpl implements WindowManager {
@Override
public void removeViewImmediate(View arg0) {
- // pass
+ removeView(arg0);
}
@Override
@@ -171,4 +247,33 @@ public class WindowManagerImpl implements WindowManager {
}
return null;
}
+
+ // ---- Extra methods for layoutlib ----
+
+ public void setBaseRootView(ViewGroup baseRootView) {
+ // If used within Compose Preview, use the ComposeViewAdapter as the root
+ // so that the preview attributes are handled correctly.
+ ViewGroup composableRoot = findComposableRoot(baseRootView);
+ mBaseRootView = composableRoot != null ? composableRoot : baseRootView;
+ }
+
+ private ViewGroup findComposableRoot(ViewGroup baseRootView) {
+ if (baseRootView.getClass().getName().endsWith("ComposeViewAdapter")) {
+ return baseRootView;
+ }
+ for (int i = 0; i < baseRootView.getChildCount(); i++) {
+ View child = baseRootView.getChildAt(i);
+ if (child instanceof ViewGroup) {
+ ViewGroup composableRoot = findComposableRoot((ViewGroup)child);
+ if (composableRoot != null) {
+ return composableRoot;
+ }
+ }
+ }
+ return null;
+ }
+
+ public ViewGroup getCurrentRootView() {
+ return mCurrentRootView;
+ }
}
diff --git a/bridge/src/android/view/math/Math3DHelper.java b/bridge/src/android/view/math/Math3DHelper.java
deleted file mode 100644
index 35a1ee1a31..0000000000
--- a/bridge/src/android/view/math/Math3DHelper.java
+++ /dev/null
@@ -1,75 +0,0 @@
-/*
- * 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.
- */
-
-package android.view.math;
-
-public class Math3DHelper {
-
- private Math3DHelper() { }
-
- public final static int min(int x1, int x2, int x3) {
- return (x1 > x2) ? ((x2 > x3) ? x3 : x2) : ((x1 > x3) ? x3 : x1);
- }
-
- public final static int max(int x1, int x2, int x3) {
- return (x1 < x2) ? ((x2 < x3) ? x3 : x2) : ((x1 < x3) ? x3 : x1);
- }
-
- /**
- * @return Rect bound of flattened (ignoring z). LTRB
- * @param dimension - 2D or 3D
- */
- public static float[] flatBound(float[] poly, int dimension) {
- int polySize = poly.length/dimension;
- float left = poly[0];
- float right = poly[0];
- float top = poly[1];
- float bottom = poly[1];
-
- for (int i = 0; i < polySize; i++) {
- float x = poly[i * dimension + 0];
- float y = poly[i * dimension + 1];
-
- if (left > x) {
- left = x;
- } else if (right < x) {
- right = x;
- }
-
- if (top > y) {
- top = y;
- } else if (bottom < y) {
- bottom = y;
- }
- }
- return new float[]{left, top, right, bottom};
- }
-
- /**
- * Translate the polygon to x and y
- * @param dimension in what dimension is polygon represented (supports 2 or 3D).
- */
- public static void translate(float[] poly, float translateX, float translateY, int dimension) {
- int polySize = poly.length/dimension;
-
- for (int i = 0; i < polySize; i++) {
- poly[i * dimension + 0] += translateX;
- poly[i * dimension + 1] += translateY;
- }
- }
-
-}
-
diff --git a/bridge/src/android/view/shadow/AmbientShadowConfig.java b/bridge/src/android/view/shadow/AmbientShadowConfig.java
deleted file mode 100644
index 97efd86f70..0000000000
--- a/bridge/src/android/view/shadow/AmbientShadowConfig.java
+++ /dev/null
@@ -1,112 +0,0 @@
-/*
- * 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.
- */
-
-package android.view.shadow;
-
-/**
- * Model for ambient shadow rendering. Assumes light sources from centroid of the object.
- */
-class AmbientShadowConfig {
-
- private final float mEdgeScale;
- private final float mShadowBoundRatio;
- private final float mShadowStrength;
-
- private final float[] mPolygon;
- private float[] mLightSourcePosition;
-
- private AmbientShadowConfig(Builder builder) {
- mEdgeScale = builder.mEdgeScale;
- mShadowBoundRatio = builder.mShadowBoundRatio;
- mShadowStrength = builder.mShadowStrength;
- mPolygon = builder.mPolygon;
- mLightSourcePosition = builder.mLightSourcePosition;
- }
-
- /**
- * Returns scales intensity of the edge of the shadow (opacity) [0-100]
- */
- public float getEdgeScale() {
- return mEdgeScale;
- }
-
- /**
- * Returns scales the area (in xy) of the shadow [0-1]
- */
- public float getShadowBoundRatio() {
- return mShadowBoundRatio;
- }
-
- /**
- * Returns scales the intensity of the entire shadow (opacity) [0-1]
- */
- public float getShadowStrength() {
- return mShadowStrength;
- }
-
- /**
- * Returns opaque polygon to cast shadow
- */
- public float[] getPolygon() {
- return mPolygon;
- }
-
- /**
- * Returns 2D position of the light source
- */
- public float[] getLightSourcePosition() {
- return mLightSourcePosition;
- }
-
- public static class Builder {
-
- private float mEdgeScale;
- private float mShadowBoundRatio;
- private float mShadowStrength;
-
- private float[] mPolygon;
- private float[] mLightSourcePosition;
-
- public Builder setEdgeScale(float edgeScale) {
- mEdgeScale = edgeScale;
- return this;
- }
-
- public Builder setShadowBoundRatio(float shadowBoundRatio) {
- mShadowBoundRatio = shadowBoundRatio;
- return this;
- }
-
- public Builder setShadowStrength(float shadowStrength) {
- mShadowStrength = shadowStrength;
- return this;
- }
-
- public Builder setPolygon(float[] polygon) {
- mPolygon = polygon;
- return this;
- }
-
- public Builder setLightSourcePosition(float x, float y) {
- mLightSourcePosition = new float[] { x, y };
- return this;
- }
-
- public AmbientShadowConfig build() {
- return new AmbientShadowConfig(this);
- }
- }
-}
diff --git a/bridge/src/android/view/shadow/AmbientShadowTriangulator.java b/bridge/src/android/view/shadow/AmbientShadowTriangulator.java
deleted file mode 100644
index d768fa3466..0000000000
--- a/bridge/src/android/view/shadow/AmbientShadowTriangulator.java
+++ /dev/null
@@ -1,64 +0,0 @@
-/*
- * 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.
- */
-
-package android.view.shadow;
-
-import com.android.ide.common.rendering.api.ILayoutLog;
-import com.android.layoutlib.bridge.Bridge;
-
-/**
- * Generates ambient shadow bitmap
- */
-class AmbientShadowTriangulator {
-
- private final AmbientShadowConfig mShadowConfig;
- private final AmbientShadowVertexCalculator mCalculator;
-
- private boolean mValid;
-
- public AmbientShadowTriangulator(AmbientShadowConfig shadowConfig) {
- mShadowConfig = shadowConfig;
-
- mCalculator = new AmbientShadowVertexCalculator(mShadowConfig);
- }
-
- /**
- * Populate vertices and fill the triangle buffers.
- */
- public void triangulate() {
- try {
- mCalculator.generateVertex();
- mValid = true;
- } catch (IndexOutOfBoundsException|ArithmeticException mathError) {
- Bridge.getLog().warning(ILayoutLog.TAG_INFO, "Arithmetic error while drawing " +
- "ambient shadow",
- null, mathError);
- } catch (Exception ex) {
- Bridge.getLog().warning(ILayoutLog.TAG_INFO, "Error while drawing shadow",
- null, ex);
- }
- }
-
- public boolean isValid() {
- return mValid;
- }
-
- public float[] getVertices() { return mCalculator.getVertex(); }
-
- public int[] getIndices() { return mCalculator.getIndex(); }
-
- public float[] getColors() { return mCalculator.getColor(); }
-}
diff --git a/bridge/src/android/view/shadow/AmbientShadowVertexCalculator.java b/bridge/src/android/view/shadow/AmbientShadowVertexCalculator.java
deleted file mode 100644
index 5928f3565c..0000000000
--- a/bridge/src/android/view/shadow/AmbientShadowVertexCalculator.java
+++ /dev/null
@@ -1,111 +0,0 @@
-/*
- * 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.
- */
-
-package android.view.shadow;
-
-import android.view.math.Math3DHelper;
-
-/**
- * Generates vertices, colours, and indices required for ambient shadow. Ambient shadows are
- * assumed to be raycasted from the centroid of the polygon, and reaches upto a ratio based on
- * the polygon's z-height.
- */
-class AmbientShadowVertexCalculator {
-
- private final float[] mVertex;
- private final float[] mColor;
- private final int[] mIndex;
- private final AmbientShadowConfig mConfig;
-
- public AmbientShadowVertexCalculator(AmbientShadowConfig config) {
- mConfig = config;
-
- int size = mConfig.getPolygon().length / 3;
-
- mVertex = new float[size * 2 * 2];
- mColor = new float[size * 2 * 4];
- mIndex = new int[(size * 3 - 2) * 3];
- }
-
- /**
- * Generates ambient shadow triangulation using configuration provided
- */
- public void generateVertex() {
- float[] polygon = mConfig.getPolygon();
- float cx = mConfig.getLightSourcePosition()[0];
- float cy = mConfig.getLightSourcePosition()[1];
-
- int polygonLength = polygon.length/3;
-
- float opacity = .8f * (0.5f / (mConfig.getEdgeScale() / 10f));
-
- int trShift = 0;
- for (int i = 0; i < polygonLength; ++i, trShift += 6) {
- int shift = i * 4;
- int colorShift = i * 8;
- int idxShift = i * 2;
-
- float px = polygon[3 * i + 0];
- float py = polygon[3 * i + 1];
- mVertex[shift + 0] = px;
- mVertex[shift + 1] = py;
-
- // TODO: I do not really understand this but this matches the previous behavior.
- // The weird bit is that for outlines with low elevation the ambient shadow is
- // entirely drawn underneath the shadow caster. This is most probably incorrect
- float h = polygon[3 * i + 2] * mConfig.getShadowBoundRatio();
-
- mVertex[shift + 2] = cx + h * (px - cx);
- mVertex[shift + 3] = cy + h * (py - cy);
-
- mColor[colorShift + 3] = opacity;
-
- mIndex[trShift + 0] = idxShift + 0;
- mIndex[trShift + 1] = idxShift + 1;
- mIndex[trShift + 2] = idxShift + 2;
-
- mIndex[trShift + 3] = idxShift + 1;
- mIndex[trShift + 4] = idxShift + 2;
- mIndex[trShift + 5] = idxShift + 3;
- }
- // cycle back to the front
- mIndex[trShift - 1] = 1;
- mIndex[trShift - 2] = 0;
- mIndex[trShift - 4] = 0;
-
- // Filling the shadow right under the outline. Can we skip that?
- for (int i = 1; i < polygonLength - 1; ++i, trShift += 3) {
- mIndex[trShift + 0] = 0;
- mIndex[trShift + 1] = 2 * i;
- mIndex[trShift + 2] = 2 * (i+1);
- }
- }
-
- public int[] getIndex() {
- return mIndex;
- }
-
- /**
- * @return list of vertices in 2d in format : {x1, y1, x2, y2 ...}
- */
- public float[] getVertex() {
- return mVertex;
- }
-
- public float[] getColor() {
- return mColor;
- }
-}
diff --git a/bridge/src/android/view/shadow/HighQualityShadowPainter.java b/bridge/src/android/view/shadow/HighQualityShadowPainter.java
deleted file mode 100644
index 7b8873c1ee..0000000000
--- a/bridge/src/android/view/shadow/HighQualityShadowPainter.java
+++ /dev/null
@@ -1,334 +0,0 @@
-/*
- * 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.
- */
-
-package android.view.shadow;
-
-import android.graphics.Bitmap;
-import android.graphics.Canvas;
-import android.graphics.Outline;
-import android.graphics.Paint;
-import android.graphics.Rect;
-import android.util.DisplayMetrics;
-import android.view.ViewGroup;
-import android.view.math.Math3DHelper;
-
-import static android.view.shadow.ShadowConstants.MIN_ALPHA;
-import static android.view.shadow.ShadowConstants.SCALE_DOWN;
-
-public class HighQualityShadowPainter {
- private static final float sRoundedGap = (float) (1.0 - Math.sqrt(2.0) / 2.0);
-
- private HighQualityShadowPainter() { }
-
- /**
- * Draws simple Rect shadow
- */
- public static void paintRectShadow(ViewGroup parent, Outline outline, float elevation,
- Canvas canvas, float alpha, float densityDpi) {
-
- if (!validate(elevation, densityDpi)) {
- return;
- }
-
- int width = parent.getWidth() / SCALE_DOWN;
- int height = parent.getHeight() / SCALE_DOWN;
-
- Rect rectOriginal = new Rect();
- Rect rectScaled = new Rect();
- if (!outline.getRect(rectScaled) || alpha < MIN_ALPHA) {
- // If alpha below MIN_ALPHA it's invisible (based on manual test). Save some perf.
- return;
- }
-
- outline.getRect(rectOriginal);
-
- rectScaled.left /= SCALE_DOWN;
- rectScaled.right /= SCALE_DOWN;
- rectScaled.top /= SCALE_DOWN;
- rectScaled.bottom /= SCALE_DOWN;
- float radius = outline.getRadius() / SCALE_DOWN;
-
- if (radius > rectScaled.width() || radius > rectScaled.height()) {
- // Rounded edge generation fails if radius is bigger than drawing box.
- return;
- }
-
- // ensure alpha doesn't go over 1
- alpha = (alpha > 1.0f) ? 1.0f : alpha;
- boolean isOpaque = outline.getAlpha() * alpha == 1.0f;
- float[] poly = getPoly(rectScaled, elevation / SCALE_DOWN, radius);
-
- AmbientShadowConfig ambientConfig = new AmbientShadowConfig.Builder()
- .setPolygon(poly)
- .setLightSourcePosition(
- (rectScaled.left + rectScaled.right) / 2.0f,
- (rectScaled.top + rectScaled.bottom) / 2.0f)
- .setEdgeScale(ShadowConstants.AMBIENT_SHADOW_EDGE_SCALE)
- .setShadowBoundRatio(ShadowConstants.AMBIENT_SHADOW_SHADOW_BOUND)
- .setShadowStrength(ShadowConstants.AMBIENT_SHADOW_STRENGTH * alpha)
- .build();
-
- AmbientShadowTriangulator ambientTriangulator = new AmbientShadowTriangulator(ambientConfig);
- ambientTriangulator.triangulate();
-
- SpotShadowTriangulator spotTriangulator = null;
- float lightZHeightPx = ShadowConstants.SPOT_SHADOW_LIGHT_Z_HEIGHT_DP * (densityDpi / DisplayMetrics.DENSITY_DEFAULT);
- if (lightZHeightPx - elevation / SCALE_DOWN >= ShadowConstants.SPOT_SHADOW_LIGHT_Z_EPSILON) {
-
- float lightX = (rectScaled.left + rectScaled.right) / 2;
- float lightY = rectScaled.top;
- // Light shouldn't be bigger than the object by too much.
- int dynamicLightRadius = Math.min(rectScaled.width(), rectScaled.height());
-
- SpotShadowConfig spotConfig = new SpotShadowConfig.Builder()
- .setLightCoord(lightX, lightY, lightZHeightPx)
- .setLightRadius(dynamicLightRadius)
- .setShadowStrength(ShadowConstants.SPOT_SHADOW_STRENGTH * alpha)
- .setPolygon(poly, poly.length / ShadowConstants.COORDINATE_SIZE)
- .build();
-
- spotTriangulator = new SpotShadowTriangulator(spotConfig);
- spotTriangulator.triangulate();
- }
-
- int translateX = 0;
- int translateY = 0;
- int imgW = 0;
- int imgH = 0;
-
- if (ambientTriangulator.isValid()) {
- float[] shadowBounds = Math3DHelper.flatBound(ambientTriangulator.getVertices(), 2);
- // Move the shadow to the left top corner to occupy the least possible bitmap
-
- translateX = -(int) Math.floor(shadowBounds[0]);
- translateY = -(int) Math.floor(shadowBounds[1]);
-
- // create bitmap of the least possible size that covers the entire shadow
- imgW = (int) Math.ceil(shadowBounds[2] + translateX);
- imgH = (int) Math.ceil(shadowBounds[3] + translateY);
- }
-
- if (spotTriangulator != null && spotTriangulator.validate()) {
-
- // Bit of a hack to re-adjust spot shadow to fit correctly within parent canvas.
- // Problem is that outline passed is not a final position, which throws off our
- // whereas our shadow rendering algorithm, which requires pre-set range for
- // optimization purposes.
- float[] shadowBounds = Math3DHelper.flatBound(spotTriangulator.getStrips()[0], 3);
-
- if ((shadowBounds[2] - shadowBounds[0]) > width ||
- (shadowBounds[3] - shadowBounds[1]) > height) {
- // Spot shadow to be casted is larger than the parent canvas,
- // We'll let ambient shadow do the trick and skip spot shadow here.
- spotTriangulator = null;
- }
-
- translateX = Math.max(-(int) Math.floor(shadowBounds[0]), translateX);
- translateY = Math.max(-(int) Math.floor(shadowBounds[1]), translateY);
-
- // create bitmap of the least possible size that covers the entire shadow
- imgW = Math.max((int) Math.ceil(shadowBounds[2] + translateX), imgW);
- imgH = Math.max((int) Math.ceil(shadowBounds[3] + translateY), imgH);
- }
-
- TriangleBuffer renderer = new TriangleBuffer();
- renderer.setSize(imgW, imgH, 0);
-
- if (ambientTriangulator.isValid()) {
-
- Math3DHelper.translate(ambientTriangulator.getVertices(), translateX, translateY, 2);
- renderer.drawTriangles(ambientTriangulator.getIndices(), ambientTriangulator.getVertices(),
- ambientTriangulator.getColors(), ambientConfig.getShadowStrength());
- }
-
- if (spotTriangulator != null && spotTriangulator.validate()) {
- float[][] strips = spotTriangulator.getStrips();
- for (int i = 0; i < strips.length; ++i) {
- Math3DHelper.translate(strips[i], translateX, translateY, 3);
- renderer.drawTriangles(strips[i], ShadowConstants.SPOT_SHADOW_STRENGTH * alpha);
- }
- }
-
- Bitmap img = renderer.createImage();
-
- drawScaled(canvas, img, translateX, translateY, rectOriginal, radius, isOpaque);
- }
-
- /**
- * High quality shadow does not work well with object that is too high in elevation. Check if
- * the object elevation is reasonable and returns true if shadow will work well. False other
- * wise.
- */
- private static boolean validate(float elevation, float densityDpi) {
- float scaledElevationPx = elevation / SCALE_DOWN;
- float scaledSpotLightHeightPx = ShadowConstants.SPOT_SHADOW_LIGHT_Z_HEIGHT_DP *
- (densityDpi / DisplayMetrics.DENSITY_DEFAULT);
- if (scaledElevationPx > scaledSpotLightHeightPx) {
- return false;
- }
-
- return true;
- }
-
- /**
- * Draw the bitmap scaled up.
- * @param translateX - offset in x axis by which the bitmap is shifted.
- * @param translateY - offset in y axis by which the bitmap is shifted.
- * @param shadowCaster - unscaled outline of shadow caster
- * @param radius
- */
- private static void drawScaled(Canvas canvas, Bitmap bitmap, int translateX, int translateY,
- Rect shadowCaster, float radius, boolean isOpaque) {
- int unscaledTranslateX = translateX * SCALE_DOWN;
- int unscaledTranslateY = translateY * SCALE_DOWN;
-
- // To the canvas
- Rect dest = new Rect(
- -unscaledTranslateX,
- -unscaledTranslateY,
- (bitmap.getWidth() * SCALE_DOWN) - unscaledTranslateX,
- (bitmap.getHeight() * SCALE_DOWN) - unscaledTranslateY);
- Rect destSrc = new Rect(0, 0, bitmap.getWidth(), bitmap.getHeight());
- // We can skip drawing the shadows behind the caster if either
- // 1) radius is 0, the shadow caster is rectangle and we can have a perfect cut
- // 2) shadow caster is opaque and even if remove shadow only partially it won't affect
- // the visual quality, otherwise we will observe shadow part through the translucent caster
- // This can be improved by:
- // TODO: do not draw the shadow behind the caster at all during the tesselation phase
- if (radius > 0 && !isOpaque) {
- // Rounded edge.
- int save = canvas.save();
- canvas.drawBitmap(bitmap, destSrc, dest, null);
- canvas.restoreToCount(save);
- return;
- }
-
- /**
- * ----------------------------------
- * | |
- * | top |
- * | |
- * ----------------------------------
- * | | | |
- * | left | shadow caster | right |
- * | | | |
- * ----------------------------------
- * | |
- * | bottom |
- * | |
- * ----------------------------------
- *
- * dest == top + left + shadow caster + right + bottom
- * Visually, canvas.drawBitmap(bitmap, destSrc, dest, paint) would achieve the same result.
- */
- int gap = (int) Math.ceil(radius * SCALE_DOWN * sRoundedGap);
- shadowCaster.bottom -= gap;
- shadowCaster.top += gap;
- shadowCaster.left += gap;
- shadowCaster.right -= gap;
- Rect left = new Rect(dest.left, shadowCaster.top, shadowCaster.left,
- shadowCaster.bottom);
- int leftScaled = left.width() / SCALE_DOWN + destSrc.left;
-
- Rect top = new Rect(dest.left, dest.top, dest.right, shadowCaster.top);
- int topScaled = top.height() / SCALE_DOWN + destSrc.top;
-
- Rect right = new Rect(shadowCaster.right, shadowCaster.top, dest.right,
- shadowCaster.bottom);
- int rightScaled = (shadowCaster.right - dest.left) / SCALE_DOWN + destSrc.left;
-
- Rect bottom = new Rect(dest.left, shadowCaster.bottom, dest.right, dest.bottom);
- int bottomScaled = (shadowCaster.bottom - dest.top) / SCALE_DOWN + destSrc.top;
-
- // calculate parts of the middle ground that can be ignored.
- Rect leftSrc = new Rect(destSrc.left, topScaled, leftScaled, bottomScaled);
- Rect topSrc = new Rect(destSrc.left, destSrc.top, destSrc.right, topScaled);
- Rect rightSrc = new Rect(rightScaled, topScaled, destSrc.right, bottomScaled);
- Rect bottomSrc = new Rect(destSrc.left, bottomScaled, destSrc.right, destSrc.bottom);
-
- int save = canvas.save();
- Paint paint = new Paint();
- canvas.drawBitmap(bitmap, leftSrc, left, paint);
- canvas.drawBitmap(bitmap, topSrc, top, paint);
- canvas.drawBitmap(bitmap, rightSrc, right, paint);
- canvas.drawBitmap(bitmap, bottomSrc, bottom, paint);
- canvas.restoreToCount(save);
- }
-
- private static float[] getPoly(Rect rect, float elevation, float radius) {
- if (radius <= 0) {
- float[] poly = new float[ShadowConstants.RECT_VERTICES_SIZE * ShadowConstants.COORDINATE_SIZE];
-
- poly[0] = poly[9] = rect.left;
- poly[1] = poly[4] = rect.top;
- poly[3] = poly[6] = rect.right;
- poly[7] = poly[10] = rect.bottom;
- poly[2] = poly[5] = poly[8] = poly[11] = elevation;
-
- return poly;
- }
-
- return buildRoundedEdges(rect, elevation, radius);
- }
-
- private static float[] buildRoundedEdges(
- Rect rect, float elevation, float radius) {
-
- float[] roundedEdgeVertices = new float[(ShadowConstants.SPLICE_ROUNDED_EDGE + 1) * 4 * 3];
- int index = 0;
- // 1.0 LT. From theta 0 to pi/2 in K division.
- for (int i = 0; i <= ShadowConstants.SPLICE_ROUNDED_EDGE; i++) {
- double theta = (Math.PI / 2.0d) * ((double) i / ShadowConstants.SPLICE_ROUNDED_EDGE);
- float x = (float) (rect.left + (radius - radius * Math.cos(theta)));
- float y = (float) (rect.top + (radius - radius * Math.sin(theta)));
- roundedEdgeVertices[index++] = x;
- roundedEdgeVertices[index++] = y;
- roundedEdgeVertices[index++] = elevation;
- }
-
- // 2.0 RT
- for (int i = ShadowConstants.SPLICE_ROUNDED_EDGE; i >= 0; i--) {
- double theta = (Math.PI / 2.0d) * ((double) i / ShadowConstants.SPLICE_ROUNDED_EDGE);
- float x = (float) (rect.right - (radius - radius * Math.cos(theta)));
- float y = (float) (rect.top + (radius - radius * Math.sin(theta)));
- roundedEdgeVertices[index++] = x;
- roundedEdgeVertices[index++] = y;
- roundedEdgeVertices[index++] = elevation;
- }
-
- // 3.0 RB
- for (int i = 0; i <= ShadowConstants.SPLICE_ROUNDED_EDGE; i++) {
- double theta = (Math.PI / 2.0d) * ((double) i / ShadowConstants.SPLICE_ROUNDED_EDGE);
- float x = (float) (rect.right - (radius - radius * Math.cos(theta)));
- float y = (float) (rect.bottom - (radius - radius * Math.sin(theta)));
- roundedEdgeVertices[index++] = x;
- roundedEdgeVertices[index++] = y;
- roundedEdgeVertices[index++] = elevation;
- }
-
- // 4.0 LB
- for (int i = ShadowConstants.SPLICE_ROUNDED_EDGE; i >= 0; i--) {
- double theta = (Math.PI / 2.0d) * ((double) i / ShadowConstants.SPLICE_ROUNDED_EDGE);
- float x = (float) (rect.left + (radius - radius * Math.cos(theta)));
- float y = (float) (rect.bottom - (radius - radius * Math.sin(theta)));
- roundedEdgeVertices[index++] = x;
- roundedEdgeVertices[index++] = y;
- roundedEdgeVertices[index++] = elevation;
- }
-
- return roundedEdgeVertices;
- }
-}
diff --git a/bridge/src/android/view/shadow/ShadowConstants.java b/bridge/src/android/view/shadow/ShadowConstants.java
deleted file mode 100644
index 049a549d18..0000000000
--- a/bridge/src/android/view/shadow/ShadowConstants.java
+++ /dev/null
@@ -1,46 +0,0 @@
-/*
- * 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.
- */
-
-package android.view.shadow;
-
-/**
- * Constant values for shadow related configuration
- */
-class ShadowConstants {
-
- /**
- * This is used as a factor by which to scale down the shadow bitmap. If we have world
- * Width x Height, shadow bitmap will be Width/SCALE_DOWN x Height/SCALE_DOWN and during
- * canvas draw the shadow will be scaled up, resulting faster perf (due to smaller bitmap) but
- * blurrier (lower quality) shadow.
- */
- public static final int SCALE_DOWN = 5;
-
- public static final float MIN_ALPHA = 0.2f;
-
- public static final int SPOT_SHADOW_LIGHT_Z_HEIGHT_DP = 50 / SCALE_DOWN;
- public static final int SPOT_SHADOW_LIGHT_Z_EPSILON = 10 / SCALE_DOWN;
- public static final float SPOT_SHADOW_STRENGTH = 0.3f;
-
- public static final float AMBIENT_SHADOW_EDGE_SCALE = 60f;
- public static final float AMBIENT_SHADOW_SHADOW_BOUND = 0.02f * SCALE_DOWN;
- public static final float AMBIENT_SHADOW_STRENGTH = 1.0f;
-
- public static final int COORDINATE_SIZE = 3;
- public static final int RECT_VERTICES_SIZE = 4;
-
- public static final int SPLICE_ROUNDED_EDGE = 5;
-}
diff --git a/bridge/src/android/view/shadow/SpotShadowConfig.java b/bridge/src/android/view/shadow/SpotShadowConfig.java
deleted file mode 100644
index 7b4fa4b5cc..0000000000
--- a/bridge/src/android/view/shadow/SpotShadowConfig.java
+++ /dev/null
@@ -1,137 +0,0 @@
-/*
- * 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.
- */
-
-package android.view.shadow;
-
-
-/**
- * Model for spot shadow rendering. Assumes single light, single object.
- */
-class SpotShadowConfig {
-
- // No need to be final but making it immutable for now.
- private final int mLightRadius;
-
-
- // No need to be final but making it immutable for now.
- private final float[] mPoly;
- private final int mPolyLength;
-
- private float[] mLightCoord;
-
- private final float mShadowStrength;
-
- private SpotShadowConfig(SpotShadowConfig.Builder builder) {
- mLightRadius = builder.mLightRadius;
- mPoly = builder.mPoly;
- mPolyLength = builder.mPolyLength;
-
- mLightCoord = new float[3];
- mLightCoord[0] = builder.mLightX;
- mLightCoord[1] = builder.mLightY;
- mLightCoord[2] = builder.mLightHeight;
- mShadowStrength = builder.mShadowStrength;
- }
-
- /**
- * @return size of the light source radius (light source is always generated as a circular shape)
- */
- public int getLightRadius() {
- return mLightRadius;
- }
-
- /**
- * @return object that casts shadow. xyz coordinates.
- */
- public float[] getPoly() {
- return mPoly;
- }
-
- /**
- * @return # of vertices in the object {@link #getPoly()} that casts shadow.
- */
- public int getPolyLength() {
- return mPolyLength;
- }
-
- /**
- * Update the light source coord.
- * @param x - horizontal coordinate
- * @param y - vertical coordinate
- */
- public void setLightCoord(float x, float y) {
- mLightCoord[0] = x;
- mLightCoord[1] = y;
- }
-
- /**
- * @return shadow intensity from 0 to 1
- */
- public float getShadowStrength() {
- return mShadowStrength;
- }
-
- public float[] getLightCoord() {
- return mLightCoord;
- }
-
- public static class Builder {
-
- // No need to be final but making it immutable for now.
- private int mLightRadius;
-
- // No need to be final but making it immutable for now.
- private float[] mPoly;
- private int mPolyLength;
-
- private float mLightX;
- private float mLightY;
- private float mLightHeight;
-
- private float mShadowStrength;
-
- /**
- * @param shadowStrength from 0 to 1
- */
- public Builder setShadowStrength(float shadowStrength) {
- this.mShadowStrength = shadowStrength;
- return this;
- }
-
- public Builder setLightRadius(int mLightRadius) {
- this.mLightRadius = mLightRadius;
- return this;
- }
-
- public Builder setPolygon(float[] poly, int polyLength) {
- this.mPoly = poly;
- this.mPolyLength = polyLength;
- return this;
- }
-
- public Builder setLightCoord(float lightX, float lightY, float lightHeight) {
- this.mLightX = lightX;
- this.mLightY = lightY;
- this.mLightHeight = lightHeight;
- return this;
- }
-
- public SpotShadowConfig build() {
- return new SpotShadowConfig(this);
- }
- }
-
-} \ No newline at end of file
diff --git a/bridge/src/android/view/shadow/SpotShadowTriangulator.java b/bridge/src/android/view/shadow/SpotShadowTriangulator.java
deleted file mode 100644
index d6661537c7..0000000000
--- a/bridge/src/android/view/shadow/SpotShadowTriangulator.java
+++ /dev/null
@@ -1,75 +0,0 @@
-/*
- * 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.
- */
-
-package android.view.shadow;
-
-import com.android.ide.common.rendering.api.ILayoutLog;
-import com.android.layoutlib.bridge.Bridge;
-
-/**
- * Generate spot shadow bitmap.
- */
-class SpotShadowTriangulator {
-
- private final SpotShadowConfig mShadowConfig;
- private float[][] mStrips;
-
- public SpotShadowTriangulator(SpotShadowConfig config) {
- mShadowConfig = config;
- }
-
- /**
- * Populate the shadow bitmap.
- */
- public void triangulate() {
- try {
- float[] lightSources =
- SpotShadowVertexCalculator.calculateLight(mShadowConfig.getLightRadius(),
- mShadowConfig.getLightCoord()[0],
- mShadowConfig.getLightCoord()[1], mShadowConfig.getLightCoord()[2]);
-
-
- mStrips = new float[2][];
- int[] sizes = SpotShadowVertexCalculator.getStripSizes(mShadowConfig.getPolyLength());
- for (int i = 0; i < sizes.length; ++i) {
- mStrips[i] = new float[3 * sizes[i]];
- }
-
- SpotShadowVertexCalculator.calculateShadow(lightSources,
- mShadowConfig.getPoly(),
- mShadowConfig.getPolyLength(),
- mShadowConfig.getShadowStrength(),
- mStrips);
- } catch (IndexOutOfBoundsException|ArithmeticException mathError) {
- Bridge.getLog().warning(ILayoutLog.TAG_INFO, "Arithmetic error while drawing " +
- "spot shadow",
- null, mathError);
- } catch (Exception ex) {
- Bridge.getLog().warning(ILayoutLog.TAG_INFO, "Error while drawing shadow",
- null, ex);
- }
- }
- /**
- * @return true if generated shadow poly is valid. False otherwise.
- */
- public boolean validate() {
- return mStrips != null && mStrips[0].length >= 9;
- }
-
- public float[][] getStrips() {
- return mStrips;
- }
-}
diff --git a/bridge/src/android/view/shadow/SpotShadowVertexCalculator.java b/bridge/src/android/view/shadow/SpotShadowVertexCalculator.java
deleted file mode 100644
index fc02d18572..0000000000
--- a/bridge/src/android/view/shadow/SpotShadowVertexCalculator.java
+++ /dev/null
@@ -1,195 +0,0 @@
-/*
- * 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.
- */
-
-package android.view.shadow;
-
-import android.graphics.Rect;
-import android.view.math.Math3DHelper;
-
-/**
- * Generates the vertices required for spot shadow and all other shadow-related rendering.
- */
-class SpotShadowVertexCalculator {
-
- private SpotShadowVertexCalculator() { }
-
- /**
- * Create evenly distributed circular light source points from x and y (on flat z plane).
- * This is useful for ray tracing the shadow points later. Format : (x1,y1,z1,x2,y2,z2 ...)
- *
- * @param radius - radius of the light source
- * @param x - center X of the light source
- * @param y - center Y of the light source
- * @param height - how high (z depth) the light should be
- * @return float points (x,y,z) of light source points.
- */
- public static float[] calculateLight(float radius, float x, float y, float height) {
- float[] ret = new float[4 * 3];
- // bottom
- ret[0] = x;
- ret[1] = y + radius;
- ret[2] = height;
- // left
- ret[3] = x - radius;
- ret[4] = y;
- ret[5] = height;
- // top
- ret[6] = x;
- ret[7] = y - radius;
- ret[8] = height;
- // right
- ret[9] = x + radius;
- ret[10] = y;
- ret[11] = height;
-
- return ret;
- }
-
- /**
- * @param polyLength - length of the outline polygon
- * @return size required for shadow vertices mData array based on # of vertices in the
- * outline polygon
- */
- public static int[] getStripSizes(int polyLength){
- return new int[] { ((polyLength + 4) / 8) * 16 + 2, 4};
- }
-
- /**
- * Generate shadow vertices based on params. Format : (x1,y1,z1,x2,y2,z2 ...)
- * Precondition : Light poly must be evenly distributed on a flat surface
- * Precondition : Poly vertices must be a convex
- * Precondition : Light height must be higher than any poly vertices
- *
- * @param lightPoly - Vertices of a light source.
- * @param poly - Vertices of opaque object casting shadow
- * @param polyLength - Size of the vertices
- * @param strength - Strength of the shadow overall [0-1]
- * @param retstrips - Arrays of triplets, each triplet represents a point, thus every array to
- * be filled in format : {x1, y1, z1, x2, y2, z2, ...},
- * every 3 consecutive triplets constitute a triangle to fill, namely [t1, t2, t3], [t2, t3,
- * t4], ... If at some point [t(n-1), tn, t(n+1)] is no longer a desired a triangle and
- * there are more triangles to draw one can start a new array, hence retstrips is a 2D array.
- */
- public static void calculateShadow(
- float[] lightPoly,
- float[] poly,
- int polyLength,
- float strength,
- float[][] retstrips) {
- float[] outerStrip = retstrips[0];
-
- // We would like to unify the cases where we have roundrects and rectangles
- int roundedEdgeSegments = ((polyLength == 4) ? 0 : ShadowConstants.SPLICE_ROUNDED_EDGE);
- int sideLength = (roundedEdgeSegments / 2 + 1) * 2;
- float[] umbra = new float[4 * 2 * sideLength];
- int idx = (roundedEdgeSegments + 1) / 2;
- int uShift = 0;
- // If we have even number of segments in rounded corner (0 included), the central point of
- // rounded corner contributes to the hull twice, from 2 different light sources, thus
- // rollBack in that case, otherwise every point contributes only once
- int rollBack = (((polyLength % 8) == 0) ? 0 : 1);
- // Calculate umbra - a hull of all projections
- for (int s = 0; s < 4; ++s) { // 4 sides
- float lx = lightPoly[s * 3 + 0];
- float ly = lightPoly[s * 3 + 1];
- float lz = lightPoly[s * 3 + 2];
- for (int i = 0; i < sideLength; ++i, uShift += 2, ++idx) {
- int shift = (idx % polyLength) * 3;
-
- float t = lz / (lz - poly[shift + 2]);
-
- umbra[uShift + 0] = lx - t * (lx - poly[shift + 0]);
- umbra[uShift + 1] = ly - t * (ly - poly[shift + 1]);
- }
-
- idx -= rollBack;
- }
-
- idx = roundedEdgeSegments;
- // An array that wil contain top, right, bottom, left coordinate of penumbra
- float[] penumbraRect = new float[4];
- // Calculate penumbra
- for (int s = 0; s < 4; ++s, idx += (roundedEdgeSegments + 1)) { // 4 sides
- int sp = (s + 2) % 4;
-
- float lz = lightPoly[sp * 3 + 2];
-
- int shift = (idx % polyLength) * 3;
-
- float t = lz / (lz - poly[shift + 2]);
-
- // We are interested in just one coordinate: x or y, depending on the light source
- int c = (s + 1) % 2;
- penumbraRect[s] =
- lightPoly[sp * 3 + c] - t * (lightPoly[sp * 3 + c] - poly[shift + c]);
- }
- if (penumbraRect[0] > penumbraRect[2]) {
- float tmp = (penumbraRect[0] + penumbraRect[2]) / 2.0f;
- penumbraRect[0] = penumbraRect[2] = tmp;
- }
- if (penumbraRect[3] > penumbraRect[1]) {
- float tmp = (penumbraRect[1] + penumbraRect[3]) / 2.0f;
- penumbraRect[1] = penumbraRect[3] = tmp;
- }
-
- // Now just connect umbra points (at least 8 of them) with the closest points from
- // penumbra (only 4 of them) to form triangles to fill the entire space between umbra and
- // penumbra
- idx = sideLength * 4 - sideLength / 2;
- int rsShift = 0;
- for (int s = 0; s < 4; ++s) {
- int xidx = (((s + 3) % 4) / 2) * 2 + 1;
- int yidx = (s / 2) * 2;
- float penumbraX = penumbraRect[xidx];
- float penumbraY = penumbraRect[yidx];
- for (int i = 0; i < sideLength; ++i, rsShift += 6, ++idx) {
- int shift = (idx % (sideLength * 4)) * 2;
-
- outerStrip[rsShift + 0] = umbra[shift + 0];
- outerStrip[rsShift + 1] = umbra[shift + 1];
- outerStrip[rsShift + 3] = penumbraX;
- outerStrip[rsShift + 4] = penumbraY;
- outerStrip[rsShift + 5] = strength;
- }
- }
- // Connect with the beginning
- outerStrip[rsShift + 0] = outerStrip[0];
- outerStrip[rsShift + 1] = outerStrip[1];
- // outerStrip[rsShift + 2] = 0;
- outerStrip[rsShift + 3] = outerStrip[3];
- outerStrip[rsShift + 4] = outerStrip[4];
- outerStrip[rsShift + 5] = strength;
-
- float[] innerStrip = retstrips[1];
- // Covering penumbra rectangle
- // left, top
- innerStrip[0] = penumbraRect[3];
- innerStrip[1] = penumbraRect[0];
- innerStrip[2] = strength;
- // right, top
- innerStrip[3] = penumbraRect[1];
- innerStrip[4] = penumbraRect[0];
- innerStrip[5] = strength;
- // left, bottom
- innerStrip[6] = penumbraRect[3];
- innerStrip[7] = penumbraRect[2];
- innerStrip[8] = strength;
- // right, bottom
- innerStrip[9] = penumbraRect[1];
- innerStrip[10] = penumbraRect[2];
- innerStrip[11] = strength;
- }
-} \ No newline at end of file
diff --git a/bridge/src/android/view/shadow/TriangleBuffer.java b/bridge/src/android/view/shadow/TriangleBuffer.java
deleted file mode 100644
index 3c0117114c..0000000000
--- a/bridge/src/android/view/shadow/TriangleBuffer.java
+++ /dev/null
@@ -1,265 +0,0 @@
-/*
- * 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.
- */
-
-package android.view.shadow;
-
-import android.graphics.Bitmap;
-import android.graphics.Bitmap.Config;
-import android.view.math.Math3DHelper;
-
-import java.util.Arrays;
-
-import static java.lang.Math.max;
-import static java.lang.Math.min;
-
-/**
- * 2D Triangle buffer element that colours using z value. (z scale set).
- */
-class TriangleBuffer {
- int mWidth;
- int mHeight;
- int mImgWidth;
- int mImgHeight;
- int mBorder;
- Bitmap mBitmap;
- int mData[];
-
- public void setSize(int width, int height, int border) {
- if (mWidth == width && mHeight == height) {
- return;
- }
- mWidth = width-2*border;
- mHeight = height-2*border;
- mBorder = border;
- mImgWidth = width;
- mImgHeight = height;
-
- mBitmap = Bitmap.createBitmap(width, height, Config.ARGB_8888);
- mData = new int[width * height];
- }
-
- public void drawTriangles(int[] index, float[] vert, float[] color,float scale) {
- int indexSize = index.length / 3;
- for (int i = 0; i < indexSize; i++) {
- int vIndex = index[i * 3 + 0];
- float vx = vert[vIndex * 2 + 0];
- float vy = vert[vIndex * 2 + 1];
- float c = scale*color[vIndex * 4 + 3];
- float fx3 = vx, fy3 = vy, fz3 = c;
-
- vIndex = index[i * 3 + 1];
- vx = vert[vIndex * 2 + 0];
- vy = vert[vIndex * 2 + 1];
- c = scale*color[vIndex * 4 + 3];
- float fx2 = vx, fy2 = vy, fz2 = c;
-
- vIndex = index[i * 3 + 2];
- vx = vert[vIndex * 2 + 0];
- vy = vert[vIndex * 2 + 1];
- c = scale*color[vIndex * 4 + 3];
- float fx1 = vx, fy1 = vy, fz1 = c;
-
- triangleZBuffMin(mData, mImgWidth, mImgHeight, fx1, fy1, fz1, fx2, fy2,
- fz2, fx3, fy3, fz3);
- }
- }
-
- public void drawTriangles(float[] strip,float scale) {
- for (int i = 0; i < strip.length-8; i+=3) {
- float fx3 = strip[i], fy3 = strip[i+1], fz3 = scale* strip[i+2];
- float fx2 = strip[i+3], fy2 = strip[i+4], fz2 = scale* strip[i+5];
- float fx1 = strip[i+6], fy1 = strip[i+7], fz1 = scale* strip[i+8];
-
- if (fx1*(fy2-fy3)+fx2*(fy3-fy1)+fx3*(fy1-fy2) ==0) {
- continue;
- }
- triangleZBuffMin(mData, mImgWidth, mImgHeight, fx3, fy3, fz3, fx2, fy2,
- fz2, fx1, fy1, fz1);
- }
- }
-
- public Bitmap createImage() {
- mBitmap.setPixels(mData, 0, mWidth, 0, 0, mWidth, mHeight);
- return mBitmap;
- }
-
- private static void triangleZBuffMin(int[] buff, int w, int h, float fx3,
- float fy3, float fz3, float fx2, float fy2, float fz2, float fx1,
- float fy1, float fz1) {
- if (((fx1 - fx2) * (fy3 - fy2) - (fy1 - fy2) * (fx3 - fx2)) < 0) {
- float tmpx = fx1;
- float tmpy = fy1;
- float tmpz = fz1;
- fx1 = fx2;
- fy1 = fy2;
- fz1 = fz2;
- fx2 = tmpx;
- fy2 = tmpy;
- fz2 = tmpz;
- }
- // using maxmima
- // solve([x1*dx+y1*dy+zoff=z1,x2*dx+y2*dy+zoff=z2,x3*dx+y3*dy+zoff=z3],[dx,dy,zoff]);
- double d = (fx1 * (fy3 - fy2) - fx2 * fy3 + fx3 * fy2 + (fx2 - fx3) * fy1);
- if (d == 0) {
- return;
- }
- float dx = (float) (-(fy1 * (fz3 - fz2) - fy2 * fz3 + fy3 * fz2 + (fy2 - fy3)
- * fz1) / d);
- float dy = (float) ((fx1 * (fz3 - fz2) - fx2 * fz3 + fx3 * fz2 + (fx2 - fx3)
- * fz1) / d);
- float zoff = (float) ((fx1 * (fy3 * fz2 - fy2 * fz3) + fy1
- * (fx2 * fz3 - fx3 * fz2) + (fx3 * fy2 - fx2 * fy3) * fz1) / d);
-
- // 28.4 fixed-point coordinates
- int y1 = (int) (16.0f * fy1 + .5f);
- int y2 = (int) (16.0f * fy2 + .5f);
- int y3 = (int) (16.0f * fy3 + .5f);
-
- int x1 = (int) (16.0f * fx1 + .5f);
- int x2 = (int) (16.0f * fx2 + .5f);
- int x3 = (int) (16.0f * fx3 + .5f);
-
- int dx12 = x1 - x2;
- int dx23 = x2 - x3;
- int dx31 = x3 - x1;
-
- int dy12 = y1 - y2;
- int dy23 = y2 - y3;
- int dy31 = y3 - y1;
-
- int fdx12 = dx12 << 4;
- int fdx23 = dx23 << 4;
- int fdx31 = dx31 << 4;
-
- int fdy12 = dy12 << 4;
- int fdy23 = dy23 << 4;
- int fdy31 = dy31 << 4;
-
- int minx = (Math3DHelper.min(x1, x2, x3) + 0xF) >> 4;
- int maxx = (Math3DHelper.max(x1, x2, x3) + 0xF) >> 4;
- int miny = (Math3DHelper.min(y1, y2, y3) + 0xF) >> 4;
- int maxy = (Math3DHelper.max(y1, y2, y3) + 0xF) >> 4;
-
- if (miny < 0) {
- miny = 0;
- }
- if (minx < 0) {
- minx = 0;
- }
- if (maxx > w) {
- maxx = w;
- }
- if (maxy > h) {
- maxy = h;
- }
- int off = miny * w;
-
- int c1 = dy12 * x1 - dx12 * y1;
- int c2 = dy23 * x2 - dx23 * y2;
- int c3 = dy31 * x3 - dx31 * y3;
-
- if (dy12 < 0 || (dy12 == 0 && dx12 > 0)) {
- c1++;
- }
- if (dy23 < 0 || (dy23 == 0 && dx23 > 0)) {
- c2++;
- }
- if (dy31 < 0 || (dy31 == 0 && dx31 > 0)) {
- c3++;
- }
- int cy1 = c1 + dx12 * (miny << 4) - dy12 * (minx << 4);
- int cy2 = c2 + dx23 * (miny << 4) - dy23 * (minx << 4);
- int cy3 = c3 + dx31 * (miny << 4) - dy31 * (minx << 4);
-
- for (int y = miny; y < maxy; y++) {
- int cx1 = cy1;
- int cx2 = cy2;
- int cx3 = cy3;
- float p = zoff + dy * y;
-
- int startx = start(cx1, fdy12, minx, minx, maxx);
- startx = start(cx2, fdy23, minx, startx, maxx);
- startx = start(cx3, fdy31, minx, startx, maxx);
-
- cx1 -= (startx - minx) * fdy12;
- cx2 -= (startx - minx) * fdy23;
- cx3 -= (startx - minx) * fdy31;
-
- int endx = end(cx1, fdy12, startx, minx, maxx);
- endx = end(cx2, fdy23, startx, minx, endx);
- endx = end(cx3, fdy31, startx, minx, endx);
-
- for (int x = startx; x < endx; x++) {
- int point = x + off;
- float zval = p + dx * x;
- // Simple alpha-blending
- int prev = (buff[point] >> 24) & 0xFF;
- int res = (int) (zval * (255 - prev )) + prev;
- buff[point] = res << 24;
- }
- cy1 += fdx12;
- cy2 += fdx23;
- cy3 += fdx31;
- off += w;
- }
- }
-
- /**
- * Returns the minimum value of x in the range [minx, maxx]: y0 - dy * (x - x0) > 0
- * If no value satisfies the expression, maxx is returned
- *
- * @param y0 - value in x0
- * @param dy - delta y
- * @param x0 - some position, for which value is known (y0)
- * @param minx - start of the range
- * @param maxx - end of the range
- * @return minimum x
- */
- private static int start(int y0, int dy, int x0, int minx, int maxx) {
- if (y0 > 0) {
- return minx;
- }
- if (dy >= 0) {
- return maxx;
- }
- return max(x0 + y0 / dy + 1, minx);
- }
-
- /**
- * Returns the minimum value of x in range [minx, maxx]: y0 - dy * (x - x0) <= 0
- * If no value satisfies the expression maxx is returned
- *
- * @param y0 - value in x0
- * @param dy - delta y
- * @param x0 - some position, for which value is known (y0)
- * @param minx - start of the range
- * @param maxx - end of the range
- * @return minimum x
- */
- private static int end(int y0, int dy, int x0, int minx, int maxx) {
- if (y0 <= 0) {
- return minx;
- }
- if (dy <= 0) {
- return maxx;
- }
- return min(x0 + (y0 - 1) / dy + 1, maxx);
- }
-
- public void clear() {
- Arrays.fill(mData, 0);
- }
-} \ No newline at end of file
diff --git a/bridge/src/android/graphics/drawable/AnimatedVectorDrawable_VectorDrawableAnimatorRT_Delegate.java b/bridge/src/android/widget/RemoteViews_Delegate.java
index fc848d9af9..35809d579d 100644
--- a/bridge/src/android/graphics/drawable/AnimatedVectorDrawable_VectorDrawableAnimatorRT_Delegate.java
+++ b/bridge/src/android/widget/RemoteViews_Delegate.java
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2016 The Android Open Source Project
+ * 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.
@@ -14,21 +14,17 @@
* limitations under the License.
*/
-package android.graphics.drawable;
+package android.widget;
import com.android.tools.layoutlib.annotations.LayoutlibDelegate;
-import android.graphics.Canvas;
-import android.graphics.drawable.AnimatedVectorDrawable.VectorDrawableAnimatorRT;
+import android.content.pm.ApplicationInfo;
-public class AnimatedVectorDrawable_VectorDrawableAnimatorRT_Delegate {
- @LayoutlibDelegate
- /*package*/ static boolean useLastSeenTarget(VectorDrawableAnimatorRT thisDrawableAnimator) {
- return true;
- }
+import static com.android.layoutlib.bridge.impl.RenderAction.getCurrentContext;
+public class RemoteViews_Delegate {
@LayoutlibDelegate
- /*package*/ static void onDraw(VectorDrawableAnimatorRT thisDrawableAnimator, Canvas canvas) {
- // Do not attempt to record as we are not using a DisplayListCanvas
+ public static ApplicationInfo getApplicationInfo(String packageName, int userId) {
+ return getCurrentContext().getApplicationInfo();
}
}
diff --git a/create/src/com/android/tools/layoutlib/java/System_Delegate.java b/bridge/src/com/android/internal/lang/System_Delegate.java
index 335f566cd4..2558989a6e 100644
--- a/create/src/com/android/tools/layoutlib/java/System_Delegate.java
+++ b/bridge/src/com/android/internal/lang/System_Delegate.java
@@ -13,13 +13,14 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
+package com.android.internal.lang;
-package com.android.tools.layoutlib.java;
-
-import com.android.tools.layoutlib.create.ReplaceMethodCallsAdapter;
+import com.android.layoutlib.bridge.android.BridgeContext;
+import java.util.WeakHashMap;
import java.util.concurrent.TimeUnit;
-import java.util.concurrent.atomic.AtomicLong;
+
+import static com.android.layoutlib.bridge.impl.RenderAction.getCurrentContext;
/**
* Provides alternative implementations of methods that don't exist on the host VM.
@@ -29,11 +30,6 @@ import java.util.concurrent.atomic.AtomicLong;
*/
@SuppressWarnings("unused")
public class System_Delegate {
- // Current system time
- private static AtomicLong mNanosTime = new AtomicLong(System.nanoTime());
- // Time that the system booted up in nanos
- private static AtomicLong mBootNanosTime = new AtomicLong(System.nanoTime());
-
public static void log(String message) {
// ignore.
}
@@ -43,26 +39,45 @@ public class System_Delegate {
}
public static void setNanosTime(long nanos) {
- mNanosTime.set(nanos);
+ BridgeContext context = getCurrentContext();
+ if (context != null) {
+ context.getSessionInteractiveData().setNanosTime(nanos);
+ }
}
public static void setBootTimeNanos(long nanos) {
- mBootNanosTime.set(nanos);
+ BridgeContext context = getCurrentContext();
+ if (context != null) {
+ context.getSessionInteractiveData().setBootNanosTime(nanos);
+ }
}
public static long nanoTime() {
- return mNanosTime.get();
+ BridgeContext context = getCurrentContext();
+ if (context != null) {
+ return context.getSessionInteractiveData().getNanosTime();
+ }
+ return 0;
}
public static long currentTimeMillis() {
- return TimeUnit.NANOSECONDS.toMillis(mNanosTime.get());
+ return TimeUnit.NANOSECONDS.toMillis(nanoTime());
}
public static long bootTime() {
- return mBootNanosTime.get();
+ BridgeContext context = getCurrentContext();
+ if (context != null) {
+ return context.getSessionInteractiveData().getBootNanosTime();
+ }
+ return 0;
}
public static long bootTimeMillis() {
- return TimeUnit.NANOSECONDS.toMillis(mBootNanosTime.get());
+ return TimeUnit.NANOSECONDS.toMillis(bootTime());
+ }
+
+ // This is no-op since layoutlib infrastructure loads all the native libraries.
+ public static void loadLibrary(String libname) {
+ // ignore.
}
}
diff --git a/bridge/src/com/android/internal/util/ArrayUtils_Delegate.java b/bridge/src/com/android/internal/util/ArrayUtils_Delegate.java
new file mode 100644
index 0000000000..f830f0e138
--- /dev/null
+++ b/bridge/src/com/android/internal/util/ArrayUtils_Delegate.java
@@ -0,0 +1,27 @@
+/*
+ * 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.internal.util;
+
+import java.util.Arrays;
+
+public class ArrayUtils_Delegate {
+ public static void clearCache() {
+ // Prevent cache from retaining ModuleClassLoader classes, so that when we change
+ // ModuleClassLoader (when rebuilding or using other project/module) we can free the memory.
+ Arrays.fill(ArrayUtils.sCache, null);
+ }
+}
diff --git a/bridge/src/com/android/internal/util/VirtualRefBasePtr_Delegate.java b/bridge/src/com/android/internal/util/VirtualRefBasePtr_Delegate.java
deleted file mode 100644
index 01fe45d264..0000000000
--- a/bridge/src/com/android/internal/util/VirtualRefBasePtr_Delegate.java
+++ /dev/null
@@ -1,53 +0,0 @@
-/*
- * Copyright (C) 2016 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.internal.util;
-
-import com.android.layoutlib.bridge.impl.DelegateManager;
-import com.android.tools.layoutlib.annotations.LayoutlibDelegate;
-
-import android.util.LongSparseLongArray;
-
-/**
- * Delegate used to provide new implementation the native methods of {@link VirtualRefBasePtr}
- *
- * Through the layoutlib_create tool, the original native methods of VirtualRefBasePtr have been
- * replaced by calls to methods of the same name in this delegate class.
- *
- */
-@SuppressWarnings("unused")
-public class VirtualRefBasePtr_Delegate {
- private static final DelegateManager<Object> sManager = new DelegateManager<>(Object.class);
- private static final LongSparseLongArray sRefCount = new LongSparseLongArray();
-
- @LayoutlibDelegate
- /*package*/ static synchronized void nIncStrong(long ptr) {
- long counter = sRefCount.get(ptr);
- sRefCount.put(ptr, ++counter);
- }
-
- @LayoutlibDelegate
- /*package*/ static synchronized void nDecStrong(long ptr) {
- long counter = sRefCount.get(ptr);
-
- if (counter > 1) {
- sRefCount.put(ptr, --counter);
- } else {
- sRefCount.delete(ptr);
- sManager.removeJavaReferenceFor(ptr);
- }
- }
-}
diff --git a/bridge/src/com/android/layoutlib/bridge/Bridge.java b/bridge/src/com/android/layoutlib/bridge/Bridge.java
index aa76e750d9..be8c6177ce 100644
--- a/bridge/src/com/android/layoutlib/bridge/Bridge.java
+++ b/bridge/src/com/android/layoutlib/bridge/Bridge.java
@@ -24,40 +24,50 @@ import com.android.ide.common.rendering.api.ResourceReference;
import com.android.ide.common.rendering.api.Result;
import com.android.ide.common.rendering.api.Result.Status;
import com.android.ide.common.rendering.api.SessionParams;
+import com.android.ide.common.rendering.api.XmlParserFactory;
import com.android.layoutlib.bridge.android.RenderParamsFlags;
+import com.android.layoutlib.bridge.impl.ParserFactory;
import com.android.layoutlib.bridge.impl.RenderDrawable;
import com.android.layoutlib.bridge.impl.RenderSessionImpl;
import com.android.layoutlib.bridge.util.DynamicIdMap;
-import com.android.ninepatch.NinePatchChunk;
+import com.android.layoutlib.common.util.ReflectionUtils;
import com.android.resources.ResourceType;
+import com.android.tools.layoutlib.annotations.NonNull;
import com.android.tools.layoutlib.annotations.Nullable;
import com.android.tools.layoutlib.create.MethodAdapter;
+import com.android.tools.layoutlib.create.NativeConfig;
import com.android.tools.layoutlib.create.OverrideMethod;
-import com.android.utils.Pair;
-import android.animation.PropertyValuesHolder;
-import android.animation.PropertyValuesHolder_Delegate;
+import org.kxml2.io.KXmlParser;
+import org.xmlpull.v1.XmlPullParser;
+
import android.content.res.BridgeAssetManager;
import android.graphics.Bitmap;
-import android.graphics.FontFamily_Delegate;
import android.graphics.Typeface;
import android.graphics.Typeface_Builder_Delegate;
-import android.graphics.Typeface_Delegate;
+import android.graphics.fonts.SystemFonts_Delegate;
import android.icu.util.ULocale;
import android.os.Looper;
import android.os.Looper_Accessor;
+import android.os.SystemProperties;
+import android.util.Pair;
+import android.view.Gravity;
import android.view.View;
import android.view.ViewGroup;
import android.view.ViewParent;
import java.io.File;
import java.lang.ref.SoftReference;
+import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
+import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Modifier;
import java.util.Arrays;
import java.util.EnumMap;
import java.util.HashMap;
+import java.util.Locale;
import java.util.Map;
+import java.util.Map.Entry;
import java.util.WeakHashMap;
import java.util.concurrent.locks.ReentrantLock;
@@ -109,12 +119,8 @@ public final class Bridge extends com.android.ide.common.rendering.api.Bridge {
private final static Map<Object, Map<String, SoftReference<Bitmap>>> sProjectBitmapCache =
new WeakHashMap<>();
- private final static Map<Object, Map<String, SoftReference<NinePatchChunk>>> sProject9PatchCache =
- new WeakHashMap<>();
private final static Map<String, SoftReference<Bitmap>> sFrameworkBitmapCache = new HashMap<>();
- private final static Map<String, SoftReference<NinePatchChunk>> sFramework9PatchCache =
- new HashMap<>();
private static Map<String, Map<String, Integer>> sEnumValueMap;
private static Map<String, String> sPlatformProperties;
@@ -138,6 +144,11 @@ public final class Bridge extends com.android.ide.common.rendering.api.Bridge {
public void warning(String tag, String message, Object viewCookie, Object data) {
System.out.println(message);
}
+
+ @Override
+ public void logAndroidFramework(int priority, String tag, String message) {
+ System.out.println(message);
+ }
};
/**
@@ -145,7 +156,12 @@ public final class Bridge extends com.android.ide.common.rendering.api.Bridge {
*/
private static ILayoutLog sCurrentLog = sDefaultLog;
- public static boolean sIsTypefaceInitialized;
+ private static String sIcuDataPath;
+
+ private static final String[] LINUX_NATIVE_LIBRARIES = {"libandroid_runtime.so"};
+ private static final String[] MAC_NATIVE_LIBRARIES = {"libandroid_runtime.dylib"};
+ private static final String[] WINDOWS_NATIVE_LIBRARIES =
+ {"libicuuc_stubdata.dll", "libicuuc-host.dll", "libandroid_runtime.dll"};
@Override
public boolean init(Map<String,String> platformProperties,
@@ -156,8 +172,12 @@ public final class Bridge extends com.android.ide.common.rendering.api.Bridge {
ILayoutLog log) {
sPlatformProperties = platformProperties;
sEnumValueMap = enumValueMap;
+ sIcuDataPath = icuDataPath;
+ sCurrentLog = log;
- BridgeAssetManager.initSystem();
+ if (!loadNativeLibrariesIfNeeded(log, nativeLibPath)) {
+ return false;
+ }
// When DEBUG_LAYOUT is set and is not 0 or false, setup a default listener
// on static (native) methods which prints the signature on the console and
@@ -188,9 +208,46 @@ public final class Bridge extends com.android.ide.common.rendering.api.Bridge {
});
}
- // load the fonts.
- FontFamily_Delegate.setFontLocation(fontLocation.getAbsolutePath());
- MemoryMappedFile_Delegate.setDataDir(fontLocation.getAbsoluteFile().getParentFile());
+ try {
+ BridgeAssetManager.initSystem();
+
+ // Do the static initialization of all the classes for which it was deferred.
+ // In order to initialize Typeface, we first need to specify the location of fonts
+ // and set a parser factory that will be used to parse the fonts.xml file.
+ SystemFonts_Delegate.setFontLocation(fontLocation.getAbsolutePath() + File.separator);
+ MemoryMappedFile_Delegate.setDataDir(fontLocation.getAbsoluteFile().getParentFile());
+ ParserFactory.setParserFactory(new XmlParserFactory() {
+ @Override
+ @Nullable
+ public XmlPullParser createXmlParserForPsiFile(@NonNull String fileName) {
+ return null;
+ }
+
+ @Override
+ @Nullable
+ public XmlPullParser createXmlParserForFile(@NonNull String fileName) {
+ return null;
+ }
+
+ @Override
+ @NonNull
+ public XmlPullParser createXmlParser() {
+ return new KXmlParser();
+ }
+ });
+ for (String deferredClass : NativeConfig.DEFERRED_STATIC_INITIALIZER_CLASSES) {
+ ReflectionUtils.invokeStatic(deferredClass, "deferredStaticInitializer");
+ }
+ // Load system fonts now that Typeface has been initialized
+ Typeface.loadPreinstalledSystemFontMap();
+ ParserFactory.setParserFactory(null);
+ } catch (Throwable t) {
+ if (log != null) {
+ log.error(ILayoutLog.TAG_BROKEN, "Layoutlib Bridge initialization failed", t,
+ null, null);
+ }
+ return false;
+ }
// now parse com.android.internal.R (and only this one as android.R is a subset of
// the internal version), and put the content in the maps.
@@ -234,7 +291,7 @@ public final class Bridge extends com.android.ide.common.rendering.api.Bridge {
Class<?> type = f.getType();
if (!type.isArray()) {
Integer value = (Integer) f.get(null);
- sRMap.put(value, Pair.of(resType, f.getName()));
+ sRMap.put(value, Pair.create(resType, f.getName()));
fullMap.put(f.getName(), value);
}
}
@@ -253,6 +310,17 @@ public final class Bridge extends com.android.ide.common.rendering.api.Bridge {
}
/**
+ * Sets System properties using the Android framework code.
+ * This is accessed by the native libraries through JNI.
+ */
+ @SuppressWarnings("unused")
+ private static void setSystemProperties() {
+ for (Entry<String, String> property : sPlatformProperties.entrySet()) {
+ SystemProperties.set(property.getKey(), property.getValue());
+ }
+ }
+
+ /**
* Tests if the field is pubic, static and one of int or int[].
*/
private static boolean isValidRField(Field field) {
@@ -324,10 +392,10 @@ public final class Bridge extends com.android.ide.common.rendering.api.Bridge {
if (arrayValue != null) {
String attrName = name.substring(arrayName.length() + 1);
int attrValue = arrayValue[index];
- sRMap.put(attrValue, Pair.of(ResourceType.ATTR, attrName));
+ sRMap.put(attrValue, Pair.create(ResourceType.ATTR, attrName));
revRAttrMap.put(attrName, attrValue);
}
- sRMap.put(index, Pair.of(ResourceType.STYLEABLE, name));
+ sRMap.put(index, Pair.create(ResourceType.STYLEABLE, name));
revRStyleableMap.put(name, index);
}
}
@@ -337,10 +405,9 @@ public final class Bridge extends com.android.ide.common.rendering.api.Bridge {
BridgeAssetManager.clearSystem();
// dispose of the default typeface.
- if (sIsTypefaceInitialized) {
+ if (SystemFonts_Delegate.sIsTypefaceInitialized) {
Typeface.sDynamicTypefaceCache.evictAll();
}
- sProject9PatchCache.clear();
sProjectBitmapCache.clear();
return true;
@@ -422,14 +489,12 @@ public final class Bridge extends com.android.ide.common.rendering.api.Bridge {
public void clearResourceCaches(Object projectKey) {
if (projectKey != null) {
sProjectBitmapCache.remove(projectKey);
- sProject9PatchCache.remove(projectKey);
}
}
@Override
public void clearAllCaches(Object projectKey) {
clearResourceCaches(projectKey);
- PropertyValuesHolder_Delegate.clearCaches();
}
@Override
@@ -537,7 +602,7 @@ public final class Bridge extends com.android.ide.common.rendering.api.Bridge {
}
if (pair != null) {
- return new ResourceReference(ResourceNamespace.ANDROID, pair.getFirst(), pair.getSecond());
+ return new ResourceReference(ResourceNamespace.ANDROID, pair.first, pair.second);
}
return null;
}
@@ -570,13 +635,6 @@ public final class Bridge extends com.android.ide.common.rendering.api.Bridge {
}
/**
- * Returns the platform build properties.
- */
- public static Map<String, String> getPlatformProperties() {
- return sPlatformProperties;
- }
-
- /**
* Returns the bitmap for a specific path, from a specific project cache, or from the
* framework cache.
* @param value the path of the bitmap
@@ -620,56 +678,83 @@ public final class Bridge extends com.android.ide.common.rendering.api.Bridge {
}
/**
- * Returns the 9 patch chunk for a specific path, from a specific project cache, or from the
- * framework cache.
- * @param value the path of the 9 patch
- * @param projectKey the key of the project, or null to query the framework cache.
- * @return the cached 9 patch or null if not found.
+ * This is called by the native layoutlib loader.
*/
- public static NinePatchChunk getCached9Patch(String value, Object projectKey) {
- if (projectKey != null) {
- Map<String, SoftReference<NinePatchChunk>> map = sProject9PatchCache.get(projectKey);
+ @SuppressWarnings("unused")
+ public static String getIcuDataPath() {
+ return sIcuDataPath;
+ }
- if (map != null) {
- SoftReference<NinePatchChunk> ref = map.get(value);
- if (ref != null) {
- return ref.get();
- }
+ private static boolean sJniLibLoadAttempted;
+ private static boolean sJniLibLoaded;
+
+ private synchronized static boolean loadNativeLibrariesIfNeeded(ILayoutLog log,
+ String nativeLibDir) {
+ if (!sJniLibLoadAttempted) {
+ try {
+ loadNativeLibraries(nativeLibDir);
}
- } else {
- SoftReference<NinePatchChunk> ref = sFramework9PatchCache.get(value);
- if (ref != null) {
- return ref.get();
+ catch (Throwable t) {
+ log.error(ILayoutLog.TAG_BROKEN, "Native layoutlib failed to load", t, null, null);
}
}
-
- return null;
+ return sJniLibLoaded;
}
- /**
- * Sets a 9 patch chunk in a project cache or in the framework cache.
- * @param value the path of the 9 patch
- * @param ninePatch the 9 patch object
- * @param projectKey the key of the project, or null to put the bitmap in the framework cache.
- */
- public static void setCached9Patch(String value, NinePatchChunk ninePatch, Object projectKey) {
- if (projectKey != null) {
- Map<String, SoftReference<NinePatchChunk>> map =
- sProject9PatchCache.computeIfAbsent(projectKey, k -> new HashMap<>());
+ private synchronized static void loadNativeLibraries(String nativeLibDir) {
+ if (sJniLibLoadAttempted) {
+ // Already attempted to load, nothing to do here.
+ return;
+ }
+ try {
+ // set the system property so LayoutLibLoader.cpp can read it
+ System.setProperty("core_native_classes", String.join(",",
+ NativeConfig.CORE_CLASS_NATIVES));
+ System.setProperty("graphics_native_classes", String.join(",",
+ NativeConfig.GRAPHICS_CLASS_NATIVES));
+ System.setProperty("icu.data.path", Bridge.getIcuDataPath());
+ System.setProperty("use_bridge_for_logging", "true");
+ System.setProperty("register_properties_during_load", "true");
+ for (String library : getNativeLibraries()) {
+ String path = new File(nativeLibDir, library).getAbsolutePath();
+ System.load(path);
+ }
+ }
+ finally {
+ sJniLibLoadAttempted = true;
+ }
+ sJniLibLoaded = true;
+ }
- map.put(value, new SoftReference<>(ninePatch));
- } else {
- sFramework9PatchCache.put(value, new SoftReference<>(ninePatch));
+ private static String[] getNativeLibraries() {
+ String osName = System.getProperty("os.name").toLowerCase(Locale.US);
+ if (osName.startsWith("windows")) {
+ return WINDOWS_NATIVE_LIBRARIES;
}
+ if (osName.startsWith("mac")) {
+ return MAC_NATIVE_LIBRARIES;
+ }
+ return LINUX_NATIVE_LIBRARIES;
}
@Override
public void clearFontCache(String path) {
- if (sIsTypefaceInitialized) {
+ if (SystemFonts_Delegate.sIsTypefaceInitialized) {
final String key =
Typeface_Builder_Delegate.createAssetUid(BridgeAssetManager.initSystem(), path,
0, null, RESOLVE_BY_FONT_TABLE, RESOLVE_BY_FONT_TABLE, DEFAULT_FAMILY);
Typeface.sDynamicTypefaceCache.remove(key);
}
}
+
+ @Override
+ public Object createMockView(String label, Class<?>[] signature, Object[] args)
+ throws NoSuchMethodException, InstantiationException, IllegalAccessException,
+ InvocationTargetException {
+ Constructor<MockView> constructor = MockView.class.getConstructor(signature);
+ MockView mockView = constructor.newInstance(args);
+ mockView.setText(label);
+ mockView.setGravity(Gravity.CENTER);
+ return mockView;
+ }
}
diff --git a/bridge/src/com/android/layoutlib/bridge/BridgeRenderSession.java b/bridge/src/com/android/layoutlib/bridge/BridgeRenderSession.java
index 558470845f..3d1b2c7bcc 100644
--- a/bridge/src/com/android/layoutlib/bridge/BridgeRenderSession.java
+++ b/bridge/src/com/android/layoutlib/bridge/BridgeRenderSession.java
@@ -16,22 +16,32 @@
package com.android.layoutlib.bridge;
+import com.android.ide.common.rendering.api.ILayoutLog;
+import com.android.ide.common.rendering.api.RenderParams;
import com.android.ide.common.rendering.api.RenderSession;
import com.android.ide.common.rendering.api.ResourceReference;
import com.android.ide.common.rendering.api.ResourceValue;
import com.android.ide.common.rendering.api.Result;
import com.android.ide.common.rendering.api.ViewInfo;
+import com.android.internal.lang.System_Delegate;
+import com.android.internal.util.ArrayUtils_Delegate;
import com.android.layoutlib.bridge.impl.RenderSessionImpl;
-import com.android.tools.layoutlib.java.System_Delegate;
import android.annotation.NonNull;
import android.annotation.Nullable;
+import android.os.Handler_Delegate;
+import android.os.SystemClock_Delegate;
+import android.view.Choreographer;
+import android.view.DisplayEventReceiver_VsyncEventData_Accessor;
+import android.view.MotionEvent;
import java.awt.image.BufferedImage;
import java.util.Collections;
import java.util.List;
import java.util.Map;
+import static com.android.layoutlib.bridge.impl.RenderAction.getCurrentContext;
+
/**
* An implementation of {@link RenderSession}.
*
@@ -46,6 +56,8 @@ public class BridgeRenderSession extends RenderSession {
@NonNull
private Result mLastResult;
+ private static final Runnable NOOP_RUNNABLE = () -> { };
+
@Override
public Result getResult() {
return mLastResult;
@@ -120,12 +132,12 @@ public class BridgeRenderSession extends RenderSession {
@Override
public void setSystemTimeNanos(long nanos) {
- System_Delegate.setNanosTime(nanos);
+ execute(() -> System_Delegate.setNanosTime(nanos));
}
@Override
public void setSystemBootTimeNanos(long nanos) {
- System_Delegate.setBootTimeNanos(nanos);
+ execute(() -> System_Delegate.setBootTimeNanos(nanos));
}
@Override
@@ -136,12 +148,73 @@ public class BridgeRenderSession extends RenderSession {
}
@Override
- public void dispose() {
+ public boolean executeCallbacks(long nanos) {
+ // Currently, Compose relies on Choreographer frame callback and Handler#postAtFrontOfQueue.
+ // Calls to Handler are handled by Handler_Delegate and can be executed by Handler_Delegate#
+ // executeCallbacks. Choreographer frame callback is handled by Choreographer#doFrame.
+ if (mSession == null) {
+ return false;
+ }
+ try {
+ Bridge.prepareThread();
+ mLastResult = mSession.acquire(RenderParams.DEFAULT_TIMEOUT);
+ boolean hasMoreCallbacks = Handler_Delegate.executeCallbacks();
+ long currentTimeMs = SystemClock_Delegate.uptimeMillis();
+ getCurrentContext()
+ .getSessionInteractiveData()
+ .getChoreographerCallbacks()
+ .execute(currentTimeMs, Bridge.getLog());
+ return hasMoreCallbacks;
+ } catch (Throwable t) {
+ Bridge.getLog().error(ILayoutLog.TAG_BROKEN, "Failed executing Choreographer#doFrame "
+ , t, null, null);
+ return false;
+ } finally {
+ mSession.release();
+ Bridge.cleanupThread();
+ }
+ }
+
+ private static int toMotionEventType(TouchEventType eventType) {
+ switch (eventType) {
+ case PRESS:
+ return MotionEvent.ACTION_DOWN;
+ case RELEASE:
+ return MotionEvent.ACTION_UP;
+ case DRAG:
+ return MotionEvent.ACTION_MOVE;
+ }
+ throw new IllegalStateException("Unexpected touch event type: " + eventType);
+ }
+
+ @Override
+ public void triggerTouchEvent(TouchEventType type, int x, int y) {
+ execute(() -> {
+ int motionEventType = toMotionEventType(type);
+ mSession.dispatchTouchEvent(motionEventType, System_Delegate.nanoTime(), x, y);
+ });
+ }
+
+ @Override
+ public void execute(Runnable r) {
if (mSession != null) {
- mSession.dispose();
+ try {
+ Bridge.prepareThread();
+ mLastResult = mSession.acquire(RenderParams.DEFAULT_TIMEOUT);
+ r.run();
+ } finally {
+ mSession.release();
+ Bridge.cleanupThread();
+ }
}
}
+ @Override
+ public void dispose() {
+ execute(mSession::dispose);
+ ArrayUtils_Delegate.clearCache();
+ }
+
/*package*/ BridgeRenderSession(@Nullable RenderSessionImpl scene, @NonNull Result lastResult) {
mSession = scene;
if (scene != null) {
@@ -153,7 +226,7 @@ public class BridgeRenderSession extends RenderSession {
@Override
public Object getValidationData() {
if (mSession != null) {
- return mSession.getValidatorResult();
+ return mSession.getValidatorHierarchy();
}
return null;
}
diff --git a/bridge/src/com/android/layoutlib/bridge/MockView.java b/bridge/src/com/android/layoutlib/bridge/MockView.java
index a1d1bbc456..fac3cd3128 100644
--- a/bridge/src/com/android/layoutlib/bridge/MockView.java
+++ b/bridge/src/com/android/layoutlib/bridge/MockView.java
@@ -16,7 +16,7 @@
package com.android.layoutlib.bridge;
-import com.android.SdkConstants;
+import com.android.ide.common.rendering.api.AndroidConstants;
import com.android.layoutlib.bridge.android.BridgeLayoutParamsMapAttributes;
import android.content.Context;
@@ -56,8 +56,8 @@ public class MockView extends FrameLayout {
// This inner TextView is only used to show some message on render result and should not
// inherit any attributes from parent (MockView). So it only needs layout width and height.
Map<String, String> attributes = new HashMap<>();
- attributes.put(SdkConstants.ATTR_LAYOUT_WIDTH, SdkConstants.VALUE_MATCH_PARENT);
- attributes.put(SdkConstants.ATTR_LAYOUT_HEIGHT, SdkConstants.VALUE_MATCH_PARENT);
+ attributes.put(AndroidConstants.ATTR_LAYOUT_WIDTH, AndroidConstants.VALUE_MATCH_PARENT);
+ attributes.put(AndroidConstants.ATTR_LAYOUT_HEIGHT, AndroidConstants.VALUE_MATCH_PARENT);
mView = new TextView(context, new BridgeLayoutParamsMapAttributes(attributes));
mView.setTextColor(0xFF000000);
setGravity(Gravity.CENTER);
diff --git a/bridge/src/com/android/layoutlib/bridge/SessionInteractiveData.java b/bridge/src/com/android/layoutlib/bridge/SessionInteractiveData.java
new file mode 100644
index 0000000000..39b8b082f3
--- /dev/null
+++ b/bridge/src/com/android/layoutlib/bridge/SessionInteractiveData.java
@@ -0,0 +1,45 @@
+package com.android.layoutlib.bridge;
+
+import com.android.layoutlib.bridge.util.ChoreographerCallbacks;
+import com.android.layoutlib.bridge.util.HandlerMessageQueue;
+import com.android.tools.layoutlib.annotations.NotNull;
+
+import java.util.concurrent.atomic.AtomicLong;
+
+public class SessionInteractiveData {
+ private final HandlerMessageQueue mHandlerMessageQueue = new HandlerMessageQueue();
+ private final ChoreographerCallbacks mChoreographerCallbacks = new ChoreographerCallbacks();
+ // Current system time
+ private final AtomicLong mNanosTime = new AtomicLong(System.nanoTime());
+ // Time that the system booted up in nanos
+ private final AtomicLong mBootNanosTime = new AtomicLong(System.nanoTime());
+
+ @NotNull
+ public HandlerMessageQueue getHandlerMessageQueue() {
+ return mHandlerMessageQueue;
+ }
+
+ @NotNull
+ public ChoreographerCallbacks getChoreographerCallbacks() { return mChoreographerCallbacks; }
+
+ public void setNanosTime(long nanos) {
+ mNanosTime.set(nanos);
+ }
+
+ public long getNanosTime() {
+ return mNanosTime.get();
+ }
+
+ public void setBootNanosTime(long nanos) {
+ mBootNanosTime.set(nanos);
+ }
+
+ public long getBootNanosTime() {
+ return mBootNanosTime.get();
+ }
+
+ public void dispose() {
+ mHandlerMessageQueue.clear();
+ mChoreographerCallbacks.clear();
+ }
+}
diff --git a/bridge/src/com/android/layoutlib/bridge/android/AndroidLocale.java b/bridge/src/com/android/layoutlib/bridge/android/AndroidLocale.java
index 2dfbd24459..abeab83b06 100644
--- a/bridge/src/com/android/layoutlib/bridge/android/AndroidLocale.java
+++ b/bridge/src/com/android/layoutlib/bridge/android/AndroidLocale.java
@@ -23,7 +23,7 @@ import android.os.LocaleList;
import java.util.Locale;
/**
- * This class provides an alternate implementation for {@code java.util.Locale#adjustLanguageCode}
+ * This class provides an alternate implementation for {@code java.util.Locale#getDefault}
* which is not available in openJDK. It also overrides the getDefault method.
*
* The create tool re-writes references to the above mentioned method to this one. Hence it's
@@ -32,21 +32,6 @@ import java.util.Locale;
@SuppressWarnings("UnusedDeclaration")
public class AndroidLocale {
- public static String adjustLanguageCode(String languageCode) {
- String adjusted = languageCode.toLowerCase(Locale.US);
- // Map new language codes to the obsolete language
- // codes so the correct resource bundles will be used.
- if (languageCode.equals("he")) {
- adjusted = "iw";
- } else if (languageCode.equals("id")) {
- adjusted = "in";
- } else if (languageCode.equals("yi")) {
- adjusted = "ji";
- }
-
- return adjusted;
- }
-
public static Locale getDefault() {
BridgeContext context = RenderAction.getCurrentContext();
if (context != null) {
diff --git a/bridge/src/com/android/layoutlib/bridge/android/BridgeContext.java b/bridge/src/com/android/layoutlib/bridge/android/BridgeContext.java
index 73a73d0381..1d42d0a316 100644
--- a/bridge/src/com/android/layoutlib/bridge/android/BridgeContext.java
+++ b/bridge/src/com/android/layoutlib/bridge/android/BridgeContext.java
@@ -16,7 +16,7 @@
package com.android.layoutlib.bridge.android;
-import com.android.SdkConstants;
+import com.android.ide.common.rendering.api.AndroidConstants;
import com.android.ide.common.rendering.api.AssetRepository;
import com.android.ide.common.rendering.api.ILayoutLog;
import com.android.ide.common.rendering.api.ILayoutPullParser;
@@ -30,11 +30,12 @@ import com.android.ide.common.rendering.api.ResourceValueImpl;
import com.android.ide.common.rendering.api.StyleResourceValue;
import com.android.layoutlib.bridge.Bridge;
import com.android.layoutlib.bridge.BridgeConstants;
+import com.android.layoutlib.bridge.SessionInteractiveData;
import com.android.layoutlib.bridge.impl.ParserFactory;
import com.android.layoutlib.bridge.impl.ResourceHelper;
import com.android.layoutlib.bridge.impl.Stack;
import com.android.resources.ResourceType;
-import com.android.utils.Pair;
+import com.android.tools.layoutlib.annotations.NotNull;
import org.xmlpull.v1.XmlPullParser;
import org.xmlpull.v1.XmlPullParserException;
@@ -71,6 +72,7 @@ import android.database.sqlite.SQLiteDatabase.CursorFactory;
import android.graphics.Bitmap;
import android.graphics.drawable.Drawable;
import android.hardware.display.DisplayManager;
+import android.media.AudioManager;
import android.net.ConnectivityManager;
import android.net.Uri;
import android.os.Bundle;
@@ -86,6 +88,7 @@ import android.os.ShellCallback;
import android.os.UserHandle;
import android.util.AttributeSet;
import android.util.DisplayMetrics;
+import android.util.Pair;
import android.util.TypedValue;
import android.view.BridgeInflater;
import android.view.Display;
@@ -156,8 +159,6 @@ public class BridgeContext extends Context {
*/
private final HashMap<Object, Object> mViewKeyHelpMap = new HashMap<>();
private final BridgeAssetManager mAssets;
- private final boolean mShadowsEnabled;
- private final boolean mHighQualityShadows;
private Resources mSystemResources;
private final Object mProjectKey;
private final DisplayMetrics mMetrics;
@@ -171,6 +172,7 @@ public class BridgeContext extends Context {
private final ClipboardManager mClipboardManager;
private final ActivityManager mActivityManager;
private final ConnectivityManager mConnectivityManager;
+ private final AudioManager mAudioManager;
private final HashMap<View, Integer> mScrollYPos = new HashMap<>();
private final HashMap<View, Integer> mScrollXPos = new HashMap<>();
@@ -195,6 +197,8 @@ public class BridgeContext extends Context {
private final ResourceNamespace mAppCompatNamespace;
private final Map<Key<?>, Object> mUserData = new HashMap<>();
+ private final SessionInteractiveData mSessionInteractiveData;
+
/**
* Some applications that target both pre API 17 and post API 17, set the newer attrs to
* reference the older ones. For example, android:paddingStart will resolve to
@@ -231,9 +235,7 @@ public class BridgeContext extends Context {
@NonNull LayoutlibCallback layoutlibCallback,
@NonNull Configuration config,
int targetSdkVersion,
- boolean hasRtlSupport,
- boolean shadowsEnabled,
- boolean highQualityShadows) {
+ boolean hasRtlSupport) {
mProjectKey = projectKey;
mMetrics = metrics;
mLayoutlibCallback = layoutlibCallback;
@@ -260,6 +262,7 @@ public class BridgeContext extends Context {
mClipboardManager = new ClipboardManager(this, null);
mActivityManager = ActivityManager_Accessor.getActivityManagerInstance(this);
mConnectivityManager = new ConnectivityManager(this, null);
+ mAudioManager = new AudioManager(this);
if (mLayoutlibCallback.isResourceNamespacingRequired()) {
if (mLayoutlibCallback.hasAndroidXAppCompat()) {
@@ -271,8 +274,7 @@ public class BridgeContext extends Context {
mAppCompatNamespace = ResourceNamespace.RES_AUTO;
}
- mShadowsEnabled = shadowsEnabled;
- mHighQualityShadows = highQualityShadows;
+ mSessionInteractiveData = new SessionInteractiveData();
}
/**
@@ -281,9 +283,11 @@ public class BridgeContext extends Context {
*
* @see #disposeResources()
*/
- public void initResources() {
+ public void initResources(@NonNull AssetRepository assetRepository) {
AssetManager assetManager = AssetManager.getSystem();
+ mAssets.setAssetRepository(assetRepository);
+
mSystemResources = Resources_Delegate.initSystem(
this,
assetManager,
@@ -498,7 +502,7 @@ public class BridgeContext extends Context {
new BridgeXmlBlockParser(parser, this, layout.getNamespace());
try {
pushParser(blockParser);
- return Pair.of(
+ return Pair.create(
mBridgeInflater.inflate(blockParser, parent, attachToRoot),
Boolean.TRUE);
} finally {
@@ -523,7 +527,8 @@ public class BridgeContext extends Context {
new BridgeXmlBlockParser(parser, this, layout.getNamespace());
try {
pushParser(blockParser);
- return Pair.of(mBridgeInflater.inflate(blockParser, parent, attachToRoot),
+ return Pair.create(mBridgeInflater.inflate(blockParser, parent,
+ attachToRoot),
Boolean.FALSE);
} finally {
popParser();
@@ -545,7 +550,7 @@ public class BridgeContext extends Context {
layout.getName()), null, null);
}
- return Pair.of(null, Boolean.FALSE);
+ return Pair.create(null, Boolean.FALSE);
}
/**
@@ -662,11 +667,19 @@ public class BridgeContext extends Context {
return mConnectivityManager;
case AUDIO_SERVICE:
+ return mAudioManager;
+
case TEXT_CLASSIFICATION_SERVICE:
case CONTENT_CAPTURE_MANAGER_SERVICE:
+ case ALARM_SERVICE:
return null;
default:
- assert false : "Unsupported Service: " + service;
+ // Only throw exception if the required service is unsupported but recognized as
+ // an existing system service.
+ assert SystemServiceRegistry.getSystemServiceClassName(service) == null :
+ "Unsupported Service: " + service;
+ Bridge.getLog().warning(ILayoutLog.TAG_UNSUPPORTED, "Service " + service +
+ " was not found or is unsupported", null, null);
}
return null;
@@ -718,20 +731,20 @@ public class BridgeContext extends Context {
mTypedArrayCache.put(attrs, currentThemes, resId, typeArrayAndPropertiesPair);
}
// Add value to defaultPropsMap if needed
- if (typeArrayAndPropertiesPair.getSecond() != null) {
+ if (typeArrayAndPropertiesPair.second != null) {
BridgeXmlBlockParser parser = getCurrentParser();
Object key = parser != null ? parser.getViewCookie() : null;
if (key != null) {
Map<ResourceReference, ResourceValue> defaultPropMap = mDefaultPropMaps.get(key);
if (defaultPropMap == null) {
- defaultPropMap = typeArrayAndPropertiesPair.getSecond();
+ defaultPropMap = typeArrayAndPropertiesPair.second;
mDefaultPropMaps.put(key, defaultPropMap);
} else {
- defaultPropMap.putAll(typeArrayAndPropertiesPair.getSecond());
+ defaultPropMap.putAll(typeArrayAndPropertiesPair.second);
}
}
}
- return typeArrayAndPropertiesPair.getFirst();
+ return typeArrayAndPropertiesPair.first;
}
/**
@@ -949,7 +962,7 @@ public class BridgeContext extends Context {
// If the value is a reference to another theme attribute that doesn't
// exist, we should log a warning and omit it.
String val = defaultValue.getValue();
- if (val != null && val.startsWith(SdkConstants.PREFIX_THEME_REF)) {
+ if (val != null && val.startsWith(AndroidConstants.PREFIX_THEME_REF)) {
// Because we always use the latest framework code, some resources might
// fail to resolve when using old themes (they haven't been backported).
// Since this is an artifact caused by us using always the latest
@@ -1068,7 +1081,7 @@ public class BridgeContext extends Context {
ta.sealArray();
- return Pair.of(ta, defaultPropMap);
+ return Pair.create(ta, defaultPropMap);
}
/**
@@ -1694,6 +1707,13 @@ public class BridgeContext extends Context {
}
@Override
+ public Intent registerReceiverAsUser(BroadcastReceiver arg0, UserHandle arg0p5,
+ IntentFilter arg1, String arg2, Handler arg3, int arg4) {
+ // pass
+ return null;
+ }
+
+ @Override
public void removeStickyBroadcast(Intent arg0) {
// pass
@@ -2090,20 +2110,6 @@ public class BridgeContext extends Context {
return true;
}
- /**
- * Returns whether shadows should be rendered or not
- */
- public boolean isShadowsEnabled() {
- return mShadowsEnabled;
- }
-
- /**
- * Returns whether high quality shadows should be used
- */
- public boolean isHighQualityShadows() {
- return mHighQualityShadows;
- }
-
public <T> void putUserData(@NonNull Key<T> key, @Nullable T data) {
mUserData.put(key, data);
}
@@ -2243,4 +2249,9 @@ public class BridgeContext extends Context {
}
}
+
+ @NotNull
+ public SessionInteractiveData getSessionInteractiveData() {
+ return mSessionInteractiveData;
+ }
}
diff --git a/bridge/src/com/android/layoutlib/bridge/android/BridgePackageManager.java b/bridge/src/com/android/layoutlib/bridge/android/BridgePackageManager.java
index b0b09b22af..902f11d34b 100644
--- a/bridge/src/com/android/layoutlib/bridge/android/BridgePackageManager.java
+++ b/bridge/src/com/android/layoutlib/bridge/android/BridgePackageManager.java
@@ -75,7 +75,7 @@ public class BridgePackageManager extends PackageManager {
@Override
public PackageInfo getPackageInfo(VersionedPackage versionedPackage,
- @PackageInfoFlags int flags) throws NameNotFoundException {
+ int packageInfoFlags) throws NameNotFoundException {
return null;
}
diff --git a/bridge/src/com/android/layoutlib/bridge/android/BridgePowerManager.java b/bridge/src/com/android/layoutlib/bridge/android/BridgePowerManager.java
index 41e4e2dfd9..22676e4312 100644
--- a/bridge/src/com/android/layoutlib/bridge/android/BridgePowerManager.java
+++ b/bridge/src/com/android/layoutlib/bridge/android/BridgePowerManager.java
@@ -20,6 +20,7 @@ import android.os.BatterySaverPolicyConfig;
import android.os.ParcelDuration;
import android.os.IBinder;
import android.os.IPowerManager;
+import android.os.IWakeLockCallback;
import android.os.PowerManager;
import android.os.PowerManager.WakeReason;
import android.os.PowerSaveState;
@@ -106,7 +107,7 @@ public class BridgePowerManager implements IPowerManager {
@Override
public void acquireWakeLock(IBinder arg0, int arg1, String arg2, String arg2_5, WorkSource arg3,
- String arg4, int arg5)
+ String arg4, int arg5, IWakeLockCallback callback)
throws RemoteException {
// pass for now.
}
@@ -119,7 +120,7 @@ public class BridgePowerManager implements IPowerManager {
@Override
public void acquireWakeLockWithUid(IBinder arg0, int arg1, String arg2, String arg2_5,
- int arg3, int arg4)
+ int arg3, int arg4, IWakeLockCallback callback)
throws RemoteException {
// pass for now.
}
@@ -210,6 +211,11 @@ public class BridgePowerManager implements IPowerManager {
}
@Override
+ public void updateWakeLockCallback(IBinder arg0, IWakeLockCallback arg1) throws RemoteException {
+ // pass for now.
+ }
+
+ @Override
public boolean isWakeLockLevelSupported(int level) throws RemoteException {
// pass for now.
return true;
@@ -243,6 +249,31 @@ public class BridgePowerManager implements IPowerManager {
}
@Override
+ public boolean isLowPowerStandbySupported() {
+ return false;
+ }
+
+ @Override
+ public boolean isLowPowerStandbyEnabled() {
+ return false;
+ }
+
+ @Override
+ public void setLowPowerStandbyEnabled(boolean enabled) {
+ // pass for now
+ }
+
+ @Override
+ public void setLowPowerStandbyActiveDuringMaintenance(boolean activeDuringMaintenance) {
+ // pass for now
+ }
+
+ @Override
+ public void forceLowPowerStandbyActive(boolean active) {
+ // pass for now
+ }
+
+ @Override
public boolean isScreenBrightnessBoosted() throws RemoteException {
return false;
}
diff --git a/bridge/src/com/android/layoutlib/bridge/android/BridgeXmlBlockParser.java b/bridge/src/com/android/layoutlib/bridge/android/BridgeXmlBlockParser.java
index 0269352512..460dd43f7a 100644
--- a/bridge/src/com/android/layoutlib/bridge/android/BridgeXmlBlockParser.java
+++ b/bridge/src/com/android/layoutlib/bridge/android/BridgeXmlBlockParser.java
@@ -304,6 +304,13 @@ public class BridgeXmlBlockParser implements XmlResourceParser, ResolvingAttribu
int ev = mParser.next();
+ // AAPT treats resources so that XmlBlock$Parser never has TEXT events that are
+ // whitespace only. Ignore those events here as resources from Studio have not gone
+ // through AAPT compilation.
+ while (ev == TEXT && mParser.isWhitespace()) {
+ ev = mParser.next();
+ }
+
if (ParserFactory.LOG_PARSER) {
System.out.println("NEXT " + mParser.toString() + " " +
eventTypeToString(mEventType) + " -> " + eventTypeToString(ev));
diff --git a/bridge/src/com/android/layoutlib/bridge/android/RenderParamsFlags.java b/bridge/src/com/android/layoutlib/bridge/android/RenderParamsFlags.java
index 4eaf352aa3..e370fa0c72 100644
--- a/bridge/src/com/android/layoutlib/bridge/android/RenderParamsFlags.java
+++ b/bridge/src/com/android/layoutlib/bridge/android/RenderParamsFlags.java
@@ -36,24 +36,13 @@ public final class RenderParamsFlags {
new Key<Boolean>("disableBitmapCaching", Boolean.class);
public static final Key<Boolean> FLAG_KEY_RENDER_ALL_DRAWABLE_STATES =
new Key<Boolean>("renderAllDrawableStates", Boolean.class);
- /**
- * To tell LayoutLib that the IDE supports RecyclerView.
- * <p/>
- * Default is false.
- */
- public static final Key<Boolean> FLAG_KEY_RECYCLER_VIEW_SUPPORT =
- new Key<Boolean>("recyclerViewSupport", Boolean.class);
+
/**
* The application package name. Used via {@link LayoutlibCallback#getFlag(Key)}
*/
public static final Key<String> FLAG_KEY_APPLICATION_PACKAGE =
new Key<String>("applicationPackage", String.class);
- /**
- * To tell LayoutLib that IDE supports providing XML Parser for a file (useful for getting in
- * memory contents of the file). Used via {@link LayoutlibCallback#getFlag(Key)}
- */
- public static final Key<Boolean> FLAG_KEY_XML_FILE_PARSER_SUPPORT =
- new Key<Boolean>("xmlFileParser", Boolean.class);
+
/**
* To tell LayoutLib to not render when creating a new session. This allows controlling when the first
* layout rendering will happen.
@@ -76,18 +65,6 @@ public final class RenderParamsFlags {
new Key<Boolean>("enableResultImageAutoScale", Boolean.class);
/**
- * Enables Ray Traced shadows in layoutlib.
- */
- public static final Key<Boolean> FLAG_RENDER_HIGH_QUALITY_SHADOW =
- new Key<>("renderHighQualityShadow", Boolean.class);
-
- /**
- * Flags to enable shadows in layoutlib.
- */
- public static final Key<Boolean> FLAG_ENABLE_SHADOW =
- new Key<>("enableShadow", Boolean.class);
-
- /**
* Enables layout validation calls within rendering.
*/
public static final Key<Boolean> FLAG_ENABLE_LAYOUT_VALIDATOR =
@@ -95,7 +72,7 @@ public final class RenderParamsFlags {
/**
* Enables image-related validation checks within layout validation.
- * {@link FLAG_ENABLE_LAYOUT_VALIDATOR} must be enabled before this can be effective.
+ * {@link #FLAG_ENABLE_LAYOUT_VALIDATOR} must be enabled before this can be effective.
*/
public static final Key<Boolean> FLAG_ENABLE_LAYOUT_VALIDATOR_IMAGE_CHECK =
new Key<>("enableLayoutValidatorImageCheck", Boolean.class);
diff --git a/bridge/src/com/android/layoutlib/bridge/android/graphics/NopCanvas.java b/bridge/src/com/android/layoutlib/bridge/android/graphics/NopCanvas.java
index 131aa1752b..6e424fa102 100644
--- a/bridge/src/com/android/layoutlib/bridge/android/graphics/NopCanvas.java
+++ b/bridge/src/com/android/layoutlib/bridge/android/graphics/NopCanvas.java
@@ -42,7 +42,7 @@ public class NopCanvas extends Canvas {
public boolean isHardwareAccelerated() {
// We return true the first time so there are no allocations for the software renderer in
// the constructor
- return !mIsInitialized;
+ return false;
}
@Override
@@ -304,4 +304,9 @@ public class NopCanvas extends Canvas {
@Override
public void drawPicture(Picture picture, Rect dst) {
}
+
+ @Override
+ public boolean quickReject(float left, float top, float right, float bottom) {
+ return false;
+ }
}
diff --git a/bridge/src/com/android/layoutlib/bridge/android/support/DesignLibUtil.java b/bridge/src/com/android/layoutlib/bridge/android/support/DesignLibUtil.java
index 92693c69be..226be240f4 100644
--- a/bridge/src/com/android/layoutlib/bridge/android/support/DesignLibUtil.java
+++ b/bridge/src/com/android/layoutlib/bridge/android/support/DesignLibUtil.java
@@ -18,14 +18,14 @@ package com.android.layoutlib.bridge.android.support;
import com.android.ide.common.rendering.api.ILayoutLog;
import com.android.layoutlib.bridge.Bridge;
-import com.android.layoutlib.bridge.util.ReflectionUtils.ReflectionException;
+import com.android.layoutlib.common.util.ReflectionUtils.ReflectionException;
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.view.View;
-import static com.android.layoutlib.bridge.util.ReflectionUtils.getMethod;
-import static com.android.layoutlib.bridge.util.ReflectionUtils.invoke;
+import static com.android.layoutlib.common.util.ReflectionUtils.getMethod;
+import static com.android.layoutlib.common.util.ReflectionUtils.invoke;
/**
* Utility class for working with the design support lib.
diff --git a/bridge/src/com/android/layoutlib/bridge/android/support/DrawerLayoutUtil.java b/bridge/src/com/android/layoutlib/bridge/android/support/DrawerLayoutUtil.java
index 08e7419a42..3f99c646c9 100644
--- a/bridge/src/com/android/layoutlib/bridge/android/support/DrawerLayoutUtil.java
+++ b/bridge/src/com/android/layoutlib/bridge/android/support/DrawerLayoutUtil.java
@@ -18,7 +18,7 @@ package com.android.layoutlib.bridge.android.support;
import com.android.ide.common.rendering.api.ILayoutLog;
import com.android.layoutlib.bridge.Bridge;
-import com.android.layoutlib.bridge.util.ReflectionUtils.ReflectionException;
+import com.android.layoutlib.common.util.ReflectionUtils.ReflectionException;
import android.annotation.Nullable;
import android.view.View;
@@ -27,9 +27,9 @@ import static android.view.Gravity.END;
import static android.view.Gravity.LEFT;
import static android.view.Gravity.RIGHT;
import static android.view.Gravity.START;
-import static com.android.layoutlib.bridge.util.ReflectionUtils.getCause;
-import static com.android.layoutlib.bridge.util.ReflectionUtils.getMethod;
-import static com.android.layoutlib.bridge.util.ReflectionUtils.invoke;
+import static com.android.layoutlib.common.util.ReflectionUtils.getCause;
+import static com.android.layoutlib.common.util.ReflectionUtils.getMethod;
+import static com.android.layoutlib.common.util.ReflectionUtils.invoke;
public class DrawerLayoutUtil {
diff --git a/bridge/src/com/android/layoutlib/bridge/android/support/FragmentTabHostUtil.java b/bridge/src/com/android/layoutlib/bridge/android/support/FragmentTabHostUtil.java
index a426b27a12..1224e572dc 100644
--- a/bridge/src/com/android/layoutlib/bridge/android/support/FragmentTabHostUtil.java
+++ b/bridge/src/com/android/layoutlib/bridge/android/support/FragmentTabHostUtil.java
@@ -18,14 +18,14 @@ package com.android.layoutlib.bridge.android.support;
import com.android.ide.common.rendering.api.ILayoutLog;
import com.android.layoutlib.bridge.Bridge;
-import com.android.layoutlib.bridge.util.ReflectionUtils.ReflectionException;
+import com.android.layoutlib.common.util.ReflectionUtils.ReflectionException;
import android.content.Context;
import android.widget.TabHost;
-import static com.android.layoutlib.bridge.util.ReflectionUtils.getCause;
-import static com.android.layoutlib.bridge.util.ReflectionUtils.getMethod;
-import static com.android.layoutlib.bridge.util.ReflectionUtils.invoke;
+import static com.android.layoutlib.common.util.ReflectionUtils.getCause;
+import static com.android.layoutlib.common.util.ReflectionUtils.getMethod;
+import static com.android.layoutlib.common.util.ReflectionUtils.invoke;
/**
* Utility class for working with android.support.v4.app.FragmentTabHost
diff --git a/bridge/src/com/android/layoutlib/bridge/android/support/RecyclerViewUtil.java b/bridge/src/com/android/layoutlib/bridge/android/support/RecyclerViewUtil.java
index ff20fbf6a2..46dd0b4149 100644
--- a/bridge/src/com/android/layoutlib/bridge/android/support/RecyclerViewUtil.java
+++ b/bridge/src/com/android/layoutlib/bridge/android/support/RecyclerViewUtil.java
@@ -20,18 +20,17 @@ import com.android.ide.common.rendering.api.ILayoutLog;
import com.android.ide.common.rendering.api.LayoutlibCallback;
import com.android.layoutlib.bridge.Bridge;
import com.android.layoutlib.bridge.android.BridgeContext;
-import com.android.layoutlib.bridge.android.RenderParamsFlags;
-import com.android.layoutlib.bridge.util.ReflectionUtils;
-import com.android.layoutlib.bridge.util.ReflectionUtils.ReflectionException;
+import com.android.layoutlib.common.util.ReflectionUtils;
+import com.android.layoutlib.common.util.ReflectionUtils.ReflectionException;
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.content.Context;
import android.view.View;
-import static com.android.layoutlib.bridge.util.ReflectionUtils.getCause;
-import static com.android.layoutlib.bridge.util.ReflectionUtils.getMethod;
-import static com.android.layoutlib.bridge.util.ReflectionUtils.invoke;
+import static com.android.layoutlib.common.util.ReflectionUtils.getCause;
+import static com.android.layoutlib.common.util.ReflectionUtils.getMethod;
+import static com.android.layoutlib.common.util.ReflectionUtils.invoke;
/**
* Utility class for working with android.support.v7.widget.RecyclerView and
@@ -54,13 +53,20 @@ public class RecyclerViewUtil {
*/
public static void setAdapter(@NonNull View recyclerView, @NonNull BridgeContext context,
@NonNull LayoutlibCallback layoutlibCallback, int adapterLayout, int itemCount) {
- String recyclerViewClassName =
+ Class<?> recyclerViewClass =
ReflectionUtils.getParentClass(recyclerView, RecyclerViewUtil.CN_RECYCLER_VIEW);
+ if (recyclerViewClass == null) {
+ Bridge.getLog().error(ILayoutLog.TAG_BROKEN,
+ "Unable to setup RecyclerView. No parent found.", null, null, null);
+ return;
+ }
+ String recyclerViewClassName = recyclerViewClass.getName();
+ String recyclerViewPackageName = recyclerViewClass.getPackage().getName();
String adapterClassName = recyclerViewClassName + "$Adapter";
String layoutMgrClassName = recyclerViewClassName + "$LayoutManager";
try {
- setLayoutManager(recyclerView, layoutMgrClassName, context, layoutlibCallback);
+ setLayoutManager(recyclerView, recyclerViewPackageName, layoutMgrClassName, context, layoutlibCallback);
Object adapter = createAdapter(layoutlibCallback, adapterClassName);
if (adapter != null) {
setProperty(recyclerView, adapterClassName, adapter, "setAdapter");
@@ -78,11 +84,11 @@ public class RecyclerViewUtil {
}
private static void setLayoutManager(@NonNull View recyclerView,
+ @NonNull String recyclerViewPackageName,
@NonNull String layoutMgrClassName, @NonNull BridgeContext context,
@NonNull LayoutlibCallback callback) throws ReflectionException {
if (getLayoutManager(recyclerView) == null) {
- String linearLayoutMgrClassManager =
- recyclerView.getClass().getPackage().getName() + ".LinearLayoutManager";
+ String linearLayoutMgrClassManager = recyclerViewPackageName + ".LinearLayoutManager";
// Only set the layout manager if not already set by the recycler view.
Object layoutManager =
createLayoutManager(context, linearLayoutMgrClassManager, callback);
@@ -113,11 +119,6 @@ public class RecyclerViewUtil {
@Nullable
private static Object createAdapter(@NonNull LayoutlibCallback layoutlibCallback,
@NonNull String adapterClassName) throws ReflectionException {
- Boolean ideSupport =
- layoutlibCallback.getFlag(RenderParamsFlags.FLAG_KEY_RECYCLER_VIEW_SUPPORT);
- if (ideSupport != Boolean.TRUE) {
- return null;
- }
try {
return layoutlibCallback.loadClass(adapterClassName, new Class[0], new Object[0]);
} catch (Exception e) {
diff --git a/bridge/src/com/android/layoutlib/bridge/android/support/SupportPreferencesUtil.java b/bridge/src/com/android/layoutlib/bridge/android/support/SupportPreferencesUtil.java
index f195a5e593..6876312f40 100644
--- a/bridge/src/com/android/layoutlib/bridge/android/support/SupportPreferencesUtil.java
+++ b/bridge/src/com/android/layoutlib/bridge/android/support/SupportPreferencesUtil.java
@@ -16,15 +16,14 @@
package com.android.layoutlib.bridge.android.support;
+import com.android.ide.common.rendering.api.ILayoutLog;
import com.android.ide.common.rendering.api.LayoutlibCallback;
import com.android.ide.common.rendering.api.RenderResources;
-import com.android.ide.common.rendering.api.ResourceNamespace;
-import com.android.ide.common.rendering.api.ResourceReference;
import com.android.ide.common.rendering.api.ResourceValue;
import com.android.ide.common.rendering.api.StyleResourceValue;
import com.android.layoutlib.bridge.android.BridgeContext;
import com.android.layoutlib.bridge.android.BridgeXmlBlockParser;
-import com.android.layoutlib.bridge.util.ReflectionUtils.ReflectionException;
+import com.android.layoutlib.common.util.ReflectionUtils.ReflectionException;
import com.android.resources.ResourceType;
import com.android.tools.layoutlib.annotations.NotNull;
@@ -45,10 +44,11 @@ import java.io.IOException;
import java.lang.reflect.Method;
import java.util.ArrayList;
-import static com.android.layoutlib.bridge.util.ReflectionUtils.getAccessibleMethod;
-import static com.android.layoutlib.bridge.util.ReflectionUtils.getClassInstance;
-import static com.android.layoutlib.bridge.util.ReflectionUtils.getMethod;
-import static com.android.layoutlib.bridge.util.ReflectionUtils.invoke;
+import static com.android.layoutlib.bridge.Bridge.getLog;
+import static com.android.layoutlib.common.util.ReflectionUtils.getAccessibleMethod;
+import static com.android.layoutlib.common.util.ReflectionUtils.getClassInstance;
+import static com.android.layoutlib.common.util.ReflectionUtils.getMethod;
+import static com.android.layoutlib.common.util.ReflectionUtils.invoke;
/**
* Class with utility methods to instantiate Preferences provided by the support library.
@@ -215,7 +215,7 @@ public class SupportPreferencesUtil {
*/
@Nullable
public static View inflatePreference(@NonNull BridgeContext bridgeContext,
- @NonNull XmlPullParser parser, @Nullable ViewGroup root) {
+ @NonNull XmlPullParser parser, @Nullable ViewGroup root) throws Throwable {
String preferencePackageName = null;
String preferenceManagerClassName = null;
// Find the correct package for the classes
@@ -308,6 +308,21 @@ public class SupportPreferencesUtil {
return scrollView;
} catch (ReflectionException e) {
+ Throwable t = e;
+ while (t.getCause() != null) {
+ t = t.getCause();
+ }
+ if (t instanceof ClassNotFoundException) {
+ String message = t.getMessage();
+ if (message != null && !message.contains(preferencePackageName)) {
+ // If the class not found is not part of the preference library, then it
+ // must be a custom preference. Log the error and throw the exception, which
+ // will prevent trying to inflate with the Android framework preference
+ // installer
+ getLog().error(ILayoutLog.TAG_INFLATE, t.getMessage(), null, null);
+ throw t;
+ }
+ }
return null;
}
}
diff --git a/bridge/src/com/android/layoutlib/bridge/bars/Config.java b/bridge/src/com/android/layoutlib/bridge/bars/Config.java
index a49a6643a5..1c6d55c568 100644
--- a/bridge/src/com/android/layoutlib/bridge/bars/Config.java
+++ b/bridge/src/com/android/layoutlib/bridge/bars/Config.java
@@ -92,8 +92,8 @@ public class Config {
}
public static String getTime(int platformVersion) {
- if (isGreaterOrEqual(platformVersion, R)) {
- return "11:00";
+ if (isGreaterOrEqual(platformVersion, S)) {
+ return "12:00";
}
if (platformVersion < GINGERBREAD) {
return "2:20";
@@ -137,6 +137,9 @@ public class Config {
if (platformVersion < R) {
return "10:00";
}
+ if (platformVersion < S) {
+ return "11:00";
+ }
// Should never happen.
return "4:04";
}
diff --git a/bridge/src/com/android/layoutlib/bridge/bars/CustomBar.java b/bridge/src/com/android/layoutlib/bridge/bars/CustomBar.java
index 0bb72ed851..63efec36f1 100644
--- a/bridge/src/com/android/layoutlib/bridge/bars/CustomBar.java
+++ b/bridge/src/com/android/layoutlib/bridge/bars/CustomBar.java
@@ -33,7 +33,8 @@ import com.android.resources.ResourceType;
import android.annotation.NonNull;
import android.content.res.ColorStateList;
import android.graphics.Bitmap;
-import android.graphics.Bitmap_Delegate;
+import android.graphics.BitmapFactory;
+import android.graphics.BitmapFactory.Options;
import android.graphics.drawable.BitmapDrawable;
import android.graphics.drawable.Drawable;
import android.util.TypedValue;
@@ -44,7 +45,6 @@ import android.widget.ImageView;
import android.widget.LinearLayout;
import android.widget.TextView;
-import java.io.IOException;
import java.io.InputStream;
import static android.os._Original_Build.VERSION_CODES.LOLLIPOP;
@@ -116,12 +116,10 @@ abstract class CustomBar extends LinearLayout {
// look for a cached bitmap
Bitmap bitmap = Bridge.getCachedBitmap(path, Boolean.TRUE /*isFramework*/);
if (bitmap == null) {
- try {
- bitmap = Bitmap_Delegate.createBitmap(stream, false /*isMutable*/, density);
- Bridge.setCachedBitmap(path, bitmap, Boolean.TRUE /*isFramework*/);
- } catch (IOException e) {
- return imageView;
- }
+ Options options = new Options();
+ options.inDensity = density.getDpiValue();
+ bitmap = BitmapFactory.decodeStream(stream, null, options);
+ Bridge.setCachedBitmap(path, bitmap, Boolean.TRUE /*isFramework*/);
}
if (bitmap != null) {
diff --git a/bridge/src/com/android/layoutlib/bridge/impl/DelegateManager.java b/bridge/src/com/android/layoutlib/bridge/impl/DelegateManager.java
index 5991eb0721..398c260b70 100644
--- a/bridge/src/com/android/layoutlib/bridge/impl/DelegateManager.java
+++ b/bridge/src/com/android/layoutlib/bridge/impl/DelegateManager.java
@@ -16,7 +16,6 @@
package com.android.layoutlib.bridge.impl;
-import com.android.internal.annotations.GuardedBy;
import com.android.layoutlib.bridge.util.Debug;
import com.android.layoutlib.bridge.util.SparseWeakArray;
@@ -26,12 +25,9 @@ import android.util.SparseArray;
import java.io.PrintStream;
import java.lang.ref.WeakReference;
import java.util.HashSet;
-import java.util.LinkedList;
import java.util.Set;
-import java.util.WeakHashMap;
import java.util.concurrent.atomic.AtomicLong;
-import libcore.util.NativeAllocationRegistry_Delegate;
/**
* Manages native delegates.
@@ -86,30 +82,6 @@ public final class DelegateManager<T> {
*/
private static final Set<Object> sJavaReferences = new HashSet<>();
private static final AtomicLong sDelegateCounter = new AtomicLong(1);
- /**
- * Tracks "native" allocations. This means that we know of the object in the Java side and we
- * can attach the delegate lifecycle to the lifecycle of the Java object. If the Java object
- * is disposed, it means we can get rid of the delegate allocation.
- * Ideally, we would use a {@link WeakHashMap} but we do not control the equals() method of the
- * referents so we can not safely rely on them.
- */
- private static final LinkedList<NativeAllocationHolder> sNativeAllocations = new LinkedList<>();
- /**
- * Map that allows to do a quick lookup of delegates that have been marked as native
- * allocations.
- * This allows us to quickly check if, when a manual dispose happens, there is work we have
- * to do.
- */
- @GuardedBy("sNativeAllocations")
- private static final WeakHashMap<Object, WeakReference<NativeAllocationHolder>>
- sNativeAllocationsReferences = new WeakHashMap<>();
- /**
- * Counter of the number of native allocations. We use this counter to trigger the collection
- * of unlinked references in the sNativeAllocations list. We do not need to do this process
- * on every allocation so only run it every 50 allocations.
- */
- @GuardedBy("sNativeAllocations")
- private static long sNativeAllocationsCount = 0;
private final Class<T> mClass;
@@ -150,7 +122,6 @@ public final class DelegateManager<T> {
}
}
- assert delegate != null;
//noinspection unchecked
return (T)delegate;
}
@@ -193,80 +164,7 @@ public final class DelegateManager<T> {
" with int " + native_object);
}
- if (!sJavaReferences.remove(delegate)) {
- // We didn't have any strong references to the delegate so it might be tracked by
- // the native allocations tracker. If so, we want to remove that reference to
- // make it available to collect ASAP.
- synchronized (sNativeAllocations) {
- WeakReference<NativeAllocationHolder> holderRef = sNativeAllocationsReferences.get(delegate);
- NativeAllocationHolder holder = holderRef.get();
- if (holder != null) {
- // We only null the referred delegate. We do not spend the time in finding
- // the holder in the list and removing it since the "garbage collection" in
- // markAsNativeAllocation will do it for us.
- holder.mReferred = null;
- }
- }
- }
- }
- }
-
- /**
- * This method marks the given native_object as a native allocation of the passed referent.
- * This means that the lifecycle of the native_object can now be attached to the referent and
- * if the referent is disposed, we can safely dispose the delegate.
- * This method is called by the {@link NativeAllocationRegistry_Delegate} and allows the
- * DelegateManager to remove the strong reference to the delegate.
- */
- public void markAsNativeAllocation(Object referent, long native_object) {
- NativeAllocationHolder holder;
- synchronized (DelegateManager.class) {
- T delegate = getDelegate(native_object);
- if (Debug.DEBUG) {
- if (delegate == null) {
- System.err.println("Unknown " + mClass.getSimpleName() + " with int " +
- native_object);
- }
- else {
- System.err.println("Marking element as native " + native_object);
- }
- }
-
- assert delegate != null;
- if (sJavaReferences.remove(delegate)) {
- // We had a strong reference, move to the native allocation tracker.
- holder = new NativeAllocationHolder(referent, delegate);
- }
- else {
- holder = null;
- }
- }
-
- if (holder != null) {
- synchronized (sNativeAllocations) {
- sNativeAllocations.add(holder);
- // The value references the key in this case but we use a WeakReference value.
- sNativeAllocationsReferences.put(holder.mReferred, new WeakReference<>(holder));
-
- if (++sNativeAllocationsCount % 50 == 0) {
- // Do garbage collection
- boolean collected = sNativeAllocations.removeIf(e -> e.mReferent.get() == null);
- if (Debug.DEBUG && collected) {
- System.err.println("Elements collected");
- }
- }
- }
- }
- }
-
- private static class NativeAllocationHolder {
- private final WeakReference<Object> mReferent;
- // The referred object is not null so we can null them on demand
- private Object mReferred;
-
- private NativeAllocationHolder(Object referent, Object referred) {
- mReferent = new WeakReference<>(referent);
- mReferred = referred;
+ sJavaReferences.remove(delegate);
}
}
}
diff --git a/bridge/src/com/android/layoutlib/bridge/impl/GcSnapshot.java b/bridge/src/com/android/layoutlib/bridge/impl/GcSnapshot.java
deleted file mode 100644
index f3ebd40706..0000000000
--- a/bridge/src/com/android/layoutlib/bridge/impl/GcSnapshot.java
+++ /dev/null
@@ -1,925 +0,0 @@
-/*
- * Copyright (C) 2010 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.layoutlib.bridge.impl;
-
-import com.android.ide.common.rendering.api.ILayoutLog;
-import com.android.layoutlib.bridge.Bridge;
-
-import android.graphics.Bitmap_Delegate;
-import android.graphics.Canvas;
-import android.graphics.ColorFilter_Delegate;
-import android.graphics.Paint;
-import android.graphics.Paint_Delegate;
-import android.graphics.PorterDuff;
-import android.graphics.PorterDuff.Mode;
-import android.graphics.Rect;
-import android.graphics.RectF;
-import android.graphics.Region;
-import android.graphics.Region_Delegate;
-import android.graphics.Shader_Delegate;
-
-import java.awt.AlphaComposite;
-import java.awt.Color;
-import java.awt.Composite;
-import java.awt.Graphics2D;
-import java.awt.Rectangle;
-import java.awt.RenderingHints;
-import java.awt.Shape;
-import java.awt.geom.AffineTransform;
-import java.awt.geom.Area;
-import java.awt.geom.NoninvertibleTransformException;
-import java.awt.geom.Rectangle2D;
-import java.awt.image.BufferedImage;
-import java.util.ArrayList;
-
-import static java.awt.image.BufferedImage.TYPE_INT_ARGB;
-import static java.awt.image.BufferedImage.TYPE_INT_RGB;
-import static java.lang.Math.max;
-import static java.lang.Math.min;
-
-/**
- * Class representing a graphics context snapshot, as well as a context stack as a linked list.
- * <p>
- * This is based on top of {@link Graphics2D} but can operate independently if none are available
- * yet when setting transforms and clip information.
- * <p>
- * This allows for drawing through {@link #draw(Drawable, Paint_Delegate, boolean, boolean)} and
- * {@link #draw(Drawable)}
- *
- * Handling of layers (created with {@link Canvas#saveLayer(RectF, Paint, int)}) is handled through
- * a list of Graphics2D for each layers. The class actually maintains a list of {@link Layer}
- * for each layer. Doing a save() will duplicate this list so that each graphics2D object
- * ({@link Layer#getGraphics()}) is configured only for the new snapshot.
- */
-public class GcSnapshot {
- private static final AffineTransform IDENTITY_TRANSFORM = new AffineTransform();
-
- private final GcSnapshot mPrevious;
- private final int mFlags;
-
- /** list of layers. The first item in the list is always the */
- private final ArrayList<Layer> mLayers = new ArrayList<Layer>();
-
- /** temp transform in case transformation are set before a Graphics2D exists */
- private AffineTransform mTransform = null;
- /** temp clip in case clipping is set before a Graphics2D exists */
- private Area mClip = null;
-
- // local layer data
- /** a local layer created with {@link Canvas#saveLayer(RectF, Paint, int)}.
- * If this is null, this does not mean there's no layer, just that the snapshot is not the
- * one that created the layer.
- */
- private final Layer mLocalLayer;
- private final Paint_Delegate mLocalLayerPaint;
- private final Rect mLayerBounds;
-
- public interface Drawable {
- void draw(Graphics2D graphics, Paint_Delegate paint);
- }
-
- /**
- * Class containing information about a layer.
- *
- * This contains graphics, bitmap and layer information.
- */
- private static class Layer {
- private final Graphics2D mGraphics;
- private final Bitmap_Delegate mBitmap;
- private final BufferedImage mImage;
- /** the flags that were used to configure the layer. This is never changed, and passed
- * as is when {@link #makeCopy()} is called */
- private final int mFlags;
- /** the original content of the layer when the next object was created. This is not
- * passed in {@link #makeCopy()} and instead is recreated when a new layer is added
- * (depending on its flags) */
- private BufferedImage mOriginalCopy;
-
- /**
- * Creates a layer with a graphics and a bitmap. This is only used to create
- * the base layer.
- *
- * @param graphics the graphics
- * @param bitmap the bitmap
- */
- Layer(Graphics2D graphics, Bitmap_Delegate bitmap) {
- mGraphics = graphics;
- mBitmap = bitmap;
- mImage = mBitmap.getImage();
- mFlags = 0;
- }
-
- /**
- * Creates a layer with a graphics and an image. If the image belongs to a
- * {@link Bitmap_Delegate} (case of the base layer), then
- * {@link Layer#Layer(Graphics2D, Bitmap_Delegate)} should be used.
- *
- * @param graphics the graphics the new graphics for this layer
- * @param image the image the image from which the graphics came
- * @param flags the flags that were used to save this layer
- */
- Layer(Graphics2D graphics, BufferedImage image, int flags) {
- mGraphics = graphics;
- mBitmap = null;
- mImage = image;
- mFlags = flags;
- }
-
- /** The Graphics2D, guaranteed to be non null */
- Graphics2D getGraphics() {
- return mGraphics;
- }
-
- /** The BufferedImage, guaranteed to be non null */
- BufferedImage getImage() {
- return mImage;
- }
-
- /** Returns the layer save flags. This is only valid for additional layers.
- * For the base layer this will always return 0;
- * For a given layer, all further copies of this {@link Layer} object in new snapshots
- * will always return the same value.
- */
- int getFlags() {
- return mFlags;
- }
-
- Layer makeCopy() {
- if (mBitmap != null) {
- return new Layer((Graphics2D) mGraphics.create(), mBitmap);
- }
-
- return new Layer((Graphics2D) mGraphics.create(), mImage, mFlags);
- }
-
- /** sets an optional copy of the original content to be used during restore */
- void setOriginalCopy(BufferedImage image) {
- mOriginalCopy = image;
- }
-
- BufferedImage getOriginalCopy() {
- return mOriginalCopy;
- }
-
- void change() {
- if (mBitmap != null) {
- mBitmap.change();
- }
- }
-
- /**
- * Sets the clip for the graphics2D object associated with the layer.
- * This should be used over the normal Graphics2D setClip method.
- *
- * @param clipShape the shape to use a the clip shape.
- */
- void setClip(Shape clipShape) {
- // because setClip is only guaranteed to work with rectangle shape,
- // first reset the clip to max and then intersect the current (empty)
- // clip with the shap.
- mGraphics.setClip(null);
- mGraphics.clip(clipShape);
- }
-
- /**
- * Clips the layer with the given shape. This performs an intersect between the current
- * clip shape and the given shape.
- * @param shape the new clip shape.
- */
- public void clip(Shape shape) {
- mGraphics.clip(shape);
- }
- }
-
- /**
- * Creates the root snapshot associating it with a given bitmap.
- * <p>
- * If <var>bitmap</var> is null, then {@link GcSnapshot#setBitmap(Bitmap_Delegate)} must be
- * called before the snapshot can be used to draw. Transform and clip operations are permitted
- * before.
- *
- * @param bitmap the image to associate to the snapshot or null.
- * @return the root snapshot
- */
- public static GcSnapshot createDefaultSnapshot(Bitmap_Delegate bitmap) {
- GcSnapshot snapshot = new GcSnapshot();
- if (bitmap != null) {
- snapshot.setBitmap(bitmap);
- }
-
- return snapshot;
- }
-
- /**
- * Saves the current state according to the given flags and returns the new current snapshot.
- * <p/>
- * This is the equivalent of {@link Canvas#save(int)}
- *
- * @param flags the save flags.
- * @return the new snapshot
- *
- * @see Canvas#save(int)
- */
- public GcSnapshot save(int flags) {
- return new GcSnapshot(this, null /*layerbounds*/, null /*paint*/, flags);
- }
-
- /**
- * Saves the current state and creates a new layer, and returns the new current snapshot.
- * <p/>
- * This is the equivalent of {@link Canvas#saveLayer(RectF, Paint, int)}
- *
- * @param layerBounds the layer bounds
- * @param paint the Paint information used to blit the layer back into the layers underneath
- * upon restore
- * @param flags the save flags.
- * @return the new snapshot
- *
- * @see Canvas#saveLayer(RectF, Paint, int)
- */
- public GcSnapshot saveLayer(RectF layerBounds, Paint_Delegate paint, int flags) {
- return new GcSnapshot(this, layerBounds, paint, flags);
- }
-
- /**
- * Creates the root snapshot.
- */
- private GcSnapshot() {
- mPrevious = null;
- mFlags = 0;
- mLocalLayer = null;
- mLocalLayerPaint = null;
- mLayerBounds = null;
- }
-
- /**
- * Creates a new {@link GcSnapshot} on top of another one, with a layer data to be restored
- * into the main graphics when {@link #restore()} is called.
- *
- * @param previous the previous snapshot head.
- * @param layerBounds the region of the layer. Optional, if null, this is a normal save()
- * @param paint the Paint information used to blit the layer back into the layers underneath
- * upon restore
- * @param flags the flags regarding what should be saved.
- */
- private GcSnapshot(GcSnapshot previous, RectF layerBounds, Paint_Delegate paint, int flags) {
- assert previous != null;
- mPrevious = previous;
- mFlags = flags;
-
- // make a copy of the current layers before adding the new one.
- // This keeps the same BufferedImage reference but creates new Graphics2D for this
- // snapshot.
- // It does not copy whatever original copy the layers have, as they will be done
- // only if the new layer doesn't clip drawing to itself.
- for (Layer layer : mPrevious.mLayers) {
- mLayers.add(layer.makeCopy());
- }
-
- if (layerBounds != null) {
- // get the current transform
- AffineTransform matrix = mLayers.get(0).getGraphics().getTransform();
-
- // transform the layerBounds with the current transform and stores it into a int rect
- RectF rect2 = new RectF();
- mapRect(matrix, rect2, layerBounds);
- mLayerBounds = new Rect();
- rect2.round(mLayerBounds);
-
- // get the base layer (always at index 0)
- Layer baseLayer = mLayers.get(0);
-
- // create the image for the layer
- BufferedImage layerImage = new BufferedImage(
- baseLayer.getImage().getWidth(),
- baseLayer.getImage().getHeight(),
- (mFlags & Canvas.HAS_ALPHA_LAYER_SAVE_FLAG) != 0 ?
- TYPE_INT_ARGB :
- TYPE_INT_RGB);
-
- // create a graphics for it so that drawing can be done.
- Graphics2D layerGraphics = layerImage.createGraphics();
-
- // because this layer inherits the current context for transform and clip,
- // set them to one from the base layer.
- AffineTransform currentMtx = baseLayer.getGraphics().getTransform();
- layerGraphics.setTransform(currentMtx);
-
- // create a new layer for this new layer and add it to the list at the end.
- mLayers.add(mLocalLayer = new Layer(layerGraphics, layerImage, flags));
-
- // set the clip on it.
- Shape currentClip = baseLayer.getGraphics().getClip();
- mLocalLayer.setClip(currentClip);
-
- // if the drawing is not clipped to the local layer only, we save the current content
- // of all other layers. We are only interested in the part that will actually
- // be drawn, so we create as small bitmaps as we can.
- // This is so that we can erase the drawing that goes in the layers below that will
- // be coming from the layer itself.
- if ((mFlags & Canvas.CLIP_TO_LAYER_SAVE_FLAG) == 0) {
- int w = mLayerBounds.width();
- int h = mLayerBounds.height();
- for (int i = 0 ; i < mLayers.size() - 1 ; i++) {
- Layer layer = mLayers.get(i);
- BufferedImage image = new BufferedImage(w, h, TYPE_INT_ARGB);
- Graphics2D graphics = image.createGraphics();
- graphics.drawImage(layer.getImage(),
- 0, 0, w, h,
- mLayerBounds.left, mLayerBounds.top,
- mLayerBounds.right, mLayerBounds.bottom,
- null);
- graphics.dispose();
- layer.setOriginalCopy(image);
- }
- }
- } else {
- mLocalLayer = null;
- mLayerBounds = null;
- }
-
- mLocalLayerPaint = paint;
- }
-
- public void dispose() {
- for (Layer layer : mLayers) {
- layer.getGraphics().dispose();
- }
-
- if (mPrevious != null) {
- mPrevious.dispose();
- }
- }
-
- /**
- * Restores the top {@link GcSnapshot}, and returns the next one.
- */
- public GcSnapshot restore() {
- return doRestore();
- }
-
- /**
- * Restores the {@link GcSnapshot} to <var>saveCount</var>.
- * @param saveCount the saveCount or -1 to only restore 1.
- *
- * @return the new head of the Gc snapshot stack.
- */
- public GcSnapshot restoreTo(int saveCount) {
- return doRestoreTo(size(), saveCount);
- }
-
- public int size() {
- if (mPrevious != null) {
- return mPrevious.size() + 1;
- }
-
- return 1;
- }
-
- /**
- * Link the snapshot to a Bitmap_Delegate.
- * <p/>
- * This is only for the case where the snapshot was created with a null image when calling
- * {@link #createDefaultSnapshot(Bitmap_Delegate)}, and is therefore not yet linked to
- * a previous snapshot.
- * <p/>
- * If any transform or clip information was set before, they are put into the Graphics object.
- * @param bitmap the bitmap to link to.
- */
- public void setBitmap(Bitmap_Delegate bitmap) {
- // create a new Layer for the bitmap. This will be the base layer.
- Graphics2D graphics2D = bitmap.getImage().createGraphics();
- Layer baseLayer = new Layer(graphics2D, bitmap);
-
- // Set the current transform and clip which can either come from mTransform/mClip if they
- // were set when there was no bitmap/layers or from the current base layers if there is
- // one already.
-
- graphics2D.setTransform(getTransform());
- // reset mTransform in case there was one.
- mTransform = null;
-
- baseLayer.setClip(getClip());
- // reset mClip in case there was one.
- mClip = null;
-
- // replace whatever current layers we have with this.
- mLayers.clear();
- mLayers.add(baseLayer);
-
- }
-
- public void translate(float dx, float dy) {
- if (mLayers.size() > 0) {
- for (Layer layer : mLayers) {
- layer.getGraphics().translate(dx, dy);
- }
- } else {
- if (mTransform == null) {
- mTransform = new AffineTransform();
- }
- mTransform.translate(dx, dy);
- }
- }
-
- public void rotate(double radians) {
- if (mLayers.size() > 0) {
- for (Layer layer : mLayers) {
- layer.getGraphics().rotate(radians);
- }
- } else {
- if (mTransform == null) {
- mTransform = new AffineTransform();
- }
- mTransform.rotate(radians);
- }
- }
-
- public void scale(float sx, float sy) {
- if (mLayers.size() > 0) {
- for (Layer layer : mLayers) {
- layer.getGraphics().scale(sx, sy);
- }
- } else {
- if (mTransform == null) {
- mTransform = new AffineTransform();
- }
- mTransform.scale(sx, sy);
- }
- }
-
- public AffineTransform getTransform() {
- if (mLayers.size() > 0) {
- // all graphics2D in the list have the same transform
- return mLayers.get(0).getGraphics().getTransform();
- } else {
- if (mTransform == null) {
- mTransform = new AffineTransform();
- }
- return mTransform;
- }
- }
-
- public void setTransform(AffineTransform transform) {
- if (mLayers.size() > 0) {
- for (Layer layer : mLayers) {
- layer.getGraphics().setTransform(transform);
- }
- } else {
- if (mTransform == null) {
- mTransform = new AffineTransform();
- }
- mTransform.setTransform(transform);
- }
- }
-
- public boolean clip(Shape shape, int regionOp) {
- // Simple case of intersect with existing layers.
- // Because Graphics2D#setClip works a bit peculiarly, we optimize
- // the case of clipping by intersection, as it's supported natively.
- if (regionOp == Region.Op.INTERSECT.nativeInt && mLayers.size() > 0) {
- for (Layer layer : mLayers) {
- layer.clip(shape);
- }
-
- Shape currentClip = getClip();
- return currentClip != null && currentClip.getBounds().isEmpty() == false;
- }
-
- Area area = null;
-
- if (regionOp == Region.Op.REPLACE.nativeInt) {
- area = new Area(shape);
- } else {
- area = Region_Delegate.combineShapes(getClip(), shape, regionOp);
- }
-
- if (mLayers.size() > 0) {
- if (area != null) {
- for (Layer layer : mLayers) {
- layer.setClip(area);
- }
- }
-
- Shape currentClip = getClip();
- return currentClip != null && currentClip.getBounds().isEmpty() == false;
- } else {
- if (area != null) {
- mClip = area;
- } else {
- mClip = new Area();
- }
-
- return mClip.getBounds().isEmpty() == false;
- }
- }
-
- public boolean clipRect(float left, float top, float right, float bottom, int regionOp) {
- return clip(new Rectangle2D.Float(left, top, right - left, bottom - top), regionOp);
- }
-
- /**
- * Returns the current clip, or null if none have been setup.
- */
- public Shape getClip() {
- if (mLayers.size() > 0) {
- // they all have the same clip
- return mLayers.get(0).getGraphics().getClip();
- } else {
- return mClip;
- }
- }
-
- private GcSnapshot doRestoreTo(int size, int saveCount) {
- if (size <= saveCount) {
- return this;
- }
-
- // restore the current one first.
- GcSnapshot previous = doRestore();
-
- if (size == saveCount + 1) { // this was the only one that needed restore.
- return previous;
- } else {
- return previous.doRestoreTo(size - 1, saveCount);
- }
- }
-
- /**
- * Executes the Drawable's draw method, with a null paint delegate.
- * <p/>
- * Note that the method can be called several times if there are more than one active layer.
- */
- public void draw(Drawable drawable) {
- draw(drawable, null, false /*compositeOnly*/, false /*forceSrcMode*/);
- }
-
- /**
- * Executes the Drawable's draw method.
- * <p/>
- * Note that the method can be called several times if there are more than one active layer.
- * @param compositeOnly whether the paint is used for composite only. This is typically
- * the case for bitmaps.
- * @param forceSrcMode if true, this overrides the composite to be SRC
- */
- public void draw(Drawable drawable, Paint_Delegate paint, boolean compositeOnly,
- boolean forceSrcMode) {
- int forceMode = forceSrcMode ? AlphaComposite.SRC : 0;
- // the current snapshot may not have a mLocalLayer (ie it was created on save() instead
- // of saveLayer(), but that doesn't mean there's no layer.
- // mLayers however saves all the information we need (flags).
- if (mLayers.size() == 1) {
- // no layer, only base layer. easy case.
- drawInLayer(mLayers.get(0), drawable, paint, compositeOnly, forceMode);
- } else {
- // draw in all the layers until the layer save flags tells us to stop (ie drawing
- // in that layer is limited to the layer itself.
- int flags;
- int i = mLayers.size() - 1;
-
- do {
- Layer layer = mLayers.get(i);
-
- drawInLayer(layer, drawable, paint, compositeOnly, forceMode);
-
- // then go to previous layer, only if there are any left, and its flags
- // doesn't restrict drawing to the layer itself.
- i--;
- flags = layer.getFlags();
- } while (i >= 0 && (flags & Canvas.CLIP_TO_LAYER_SAVE_FLAG) == 0);
- }
- }
-
- /**
- * This function calculates a minimum (in area) integer rectangle that contains the input
- * rectangle after applying to it the affine transform
- *
- * @param rect input rectangle
- * @param transform affine transform applied to the input rectangle
- *
- * Returns an output rectangle
- */
- private static Rectangle transformRect(Rectangle rect, AffineTransform transform) {
- double[] coords = new double[16];
- coords[0] = rect.x;
- coords[1] = rect.y;
- coords[2] = rect.x + rect.width;
- coords[3] = rect.y + rect.height;
- coords[4] = rect.x;
- coords[5] = rect.y + rect.height;
- coords[6] = rect.x + rect.width;
- coords[7] = rect.y;
- transform.transform(coords, 0, coords, 8, 4);
- // From 4 transformed vertices of the input rectangle we search for the minimum and maximum
- // for both coordinates. We round the found extrema to the closest integer, smaller of equal
- // for the minimums and larger or equal for the maximums. These values represent the border
- // or the minimum rectangle with sides parallel to the coordinate axis that contains
- // the transformed rectangle
- int x = (int) Math.floor(min(min(coords[8], coords[10]), min(coords[12], coords[14])));
- int y = (int) Math.floor(min(min(coords[9], coords[11]), min(coords[13], coords[15])));
- int w = (int) Math.ceil(max(max(coords[8], coords[10]), max(coords[12], coords[14]))) - x;
- int h = (int) Math.ceil(max(max(coords[9], coords[11]), max(coords[13], coords[15]))) - y;
- return new Rectangle(x, y, w, h);
- }
-
- private void drawInLayer(Layer layer, Drawable drawable, Paint_Delegate paint,
- boolean compositeOnly, int forceMode) {
- Graphics2D originalGraphics = layer.getGraphics();
- if (paint == null) {
- drawOnGraphics((Graphics2D) originalGraphics.create(), drawable,
- null /*paint*/, layer);
- } else {
- ColorFilter_Delegate filter = paint.getColorFilter();
- if (filter == null || !filter.isSupported()) {
- // get a Graphics2D object configured with the drawing parameters.
- Graphics2D configuredGraphics = createCustomGraphics(originalGraphics, paint,
- compositeOnly, forceMode);
- drawOnGraphics(configuredGraphics, drawable, paint, layer);
- return;
- }
-
- Rectangle clipBounds = originalGraphics.getClip() != null ? originalGraphics
- .getClipBounds() : null;
- AffineTransform transform = originalGraphics.getTransform();
- Rectangle imgRect;
- if (clipBounds != null) {
- if (clipBounds.width == 0 || clipBounds.height == 0) {
- // Clip is 0 so no need to paint anything.
- return;
- }
- // Calculate integer rectangle that contains clipBounds after the transform, that is
- // the minimum image size we can use to render the drawable
- imgRect = transformRect(clipBounds, transform);
- transform = new AffineTransform(
- transform.getScaleX(),
- transform.getShearY(),
- transform.getShearX(),
- transform.getScaleY(),
- transform.getTranslateX() - imgRect.x,
- transform.getTranslateY() - imgRect.y);
- } else {
- imgRect =
- new Rectangle(
- 0, 0, layer.getImage().getWidth(), layer.getImage().getHeight());
- }
-
- // Create a temporary image to which the color filter will be applied.
- BufferedImage image = new BufferedImage(imgRect.width, imgRect.height, TYPE_INT_ARGB);
- Graphics2D imageBaseGraphics = (Graphics2D) image.getGraphics();
- // Configure the Graphics2D object with drawing parameters and shader.
- Graphics2D imageGraphics = createCustomGraphics(
- imageBaseGraphics, paint, compositeOnly,
- AlphaComposite.SRC_OVER);
-
- // get a Graphics2D object configured with the drawing parameters, but no shader.
- Graphics2D configuredGraphics = createCustomGraphics(originalGraphics, paint,
- true /*compositeOnly*/, forceMode);
- configuredGraphics.setTransform(IDENTITY_TRANSFORM);
- try {
- // The main draw operation.
- // We translate the operation to take into account that the rendering does not
- // know about the clipping area.
- imageGraphics.setTransform(transform);
- drawable.draw(imageGraphics, paint);
-
- // Apply the color filter.
- // Restore the original coordinates system and apply the filter only to the
- // clipped area.
- imageGraphics.setTransform(IDENTITY_TRANSFORM);
- filter.applyFilter(imageGraphics, imgRect.width, imgRect.height);
-
- // Draw the tinted image on the main layer using as start point the clipping
- // upper left coordinates.
- configuredGraphics.drawImage(image, imgRect.x, imgRect.y, null);
- layer.change();
- } finally {
- // dispose Graphics2D objects
- imageGraphics.dispose();
- imageBaseGraphics.dispose();
- configuredGraphics.dispose();
- }
- }
- }
-
- private void drawOnGraphics(Graphics2D g, Drawable drawable, Paint_Delegate paint,
- Layer layer) {
- try {
- drawable.draw(g, paint);
- layer.change();
- } finally {
- g.dispose();
- }
- }
-
- private GcSnapshot doRestore() {
- if (mPrevious != null) {
- if (mLocalLayer != null) {
- // prepare to blit the layers in which we have draw, in the layer beneath
- // them, starting with the top one (which is the current local layer).
- int i = mLayers.size() - 1;
- int flags;
- do {
- Layer dstLayer = mLayers.get(i - 1);
-
- restoreLayer(dstLayer);
-
- flags = dstLayer.getFlags();
- i--;
- } while (i > 0 && (flags & Canvas.CLIP_TO_LAYER_SAVE_FLAG) == 0);
- }
-
- // if this snapshot does not save everything, then set the previous snapshot
- // to this snapshot content
-
- // didn't save the matrix? set the current matrix on the previous snapshot
- if ((mFlags & Canvas.MATRIX_SAVE_FLAG) == 0) {
- AffineTransform mtx = getTransform();
- for (Layer layer : mPrevious.mLayers) {
- layer.getGraphics().setTransform(mtx);
- }
- }
-
- // didn't save the clip? set the current clip on the previous snapshot
- if ((mFlags & Canvas.CLIP_SAVE_FLAG) == 0) {
- Shape clip = getClip();
- for (Layer layer : mPrevious.mLayers) {
- layer.setClip(clip);
- }
- }
- }
-
- for (Layer layer : mLayers) {
- layer.getGraphics().dispose();
- }
-
- return mPrevious;
- }
-
- private void restoreLayer(Layer dstLayer) {
-
- Graphics2D baseGfx = dstLayer.getImage().createGraphics();
-
- // if the layer contains an original copy this means the flags
- // didn't restrict drawing to the local layer and we need to make sure the
- // layer bounds in the layer beneath didn't receive any drawing.
- // so we use the originalCopy to erase the new drawings in there.
- BufferedImage originalCopy = dstLayer.getOriginalCopy();
- if (originalCopy != null) {
- Graphics2D g = (Graphics2D) baseGfx.create();
- g.setComposite(AlphaComposite.Src);
-
- g.drawImage(originalCopy,
- mLayerBounds.left, mLayerBounds.top, mLayerBounds.right, mLayerBounds.bottom,
- 0, 0, mLayerBounds.width(), mLayerBounds.height(),
- null);
- g.dispose();
- }
-
- // now draw put the content of the local layer onto the layer,
- // using the paint information
- Graphics2D g = createCustomGraphics(baseGfx, mLocalLayerPaint,
- true /*alphaOnly*/, 0 /*forceMode*/);
-
- g.drawImage(mLocalLayer.getImage(),
- mLayerBounds.left, mLayerBounds.top, mLayerBounds.right, mLayerBounds.bottom,
- mLayerBounds.left, mLayerBounds.top, mLayerBounds.right, mLayerBounds.bottom,
- null);
- g.dispose();
-
- baseGfx.dispose();
- }
-
- /**
- * Creates a new {@link Graphics2D} based on the {@link Paint} parameters.
- * <p/>The object must be disposed ({@link Graphics2D#dispose()}) after being used.
- */
- private Graphics2D createCustomGraphics(Graphics2D original, Paint_Delegate paint,
- boolean compositeOnly, int forceMode) {
- // make new one graphics
- Graphics2D g = (Graphics2D) original.create();
-
- if (paint == null) {
- return g;
- }
-
- // configure it
-
- if (paint.isAntiAliased()) {
- g.setRenderingHint(
- RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
- g.setRenderingHint(
- RenderingHints.KEY_TEXT_ANTIALIASING, RenderingHints.VALUE_TEXT_ANTIALIAS_ON);
- }
-
- // set the shader first, as it'll replace the color if it can be used it.
- if (!compositeOnly) {
- setShader(g, paint);
- // set the stroke
- g.setStroke(paint.getJavaStroke());
- }
- // set the composite.
- setComposite(g, paint, compositeOnly, forceMode);
-
- return g;
- }
-
- private void setShader(Graphics2D g, Paint_Delegate paint) {
- Shader_Delegate shaderDelegate = paint.getShader();
- if (shaderDelegate != null) {
- if (shaderDelegate.isSupported()) {
- java.awt.Paint shaderPaint = shaderDelegate.getJavaPaint();
- assert shaderPaint != null;
- g.setPaint(shaderPaint);
- return;
- } else {
- Bridge.getLog().fidelityWarning(ILayoutLog.TAG_SHADER,
- shaderDelegate.getSupportMessage(), null, null, null);
- }
- }
-
- // if no shader, use the paint color
- g.setColor(new Color(paint.getColor(), true /*hasAlpha*/));
- }
-
- private void setComposite(Graphics2D g, Paint_Delegate paint, boolean usePaintAlpha,
- int forceMode) {
- // the alpha for the composite. Always opaque if the normal paint color is used since
- // it contains the alpha
- int alpha = usePaintAlpha ? paint.getAlpha() : 0xFF;
- Shader_Delegate shader = paint.getShader();
- if (shader != null) {
- alpha = (int)(alpha * shader.getAlpha());
- }
- if (forceMode != 0) {
- g.setComposite(AlphaComposite.getInstance(forceMode, (float) alpha / 255.f));
- return;
- }
- Mode mode = PorterDuff.intToMode(paint.getPorterDuffMode());
- Composite composite = PorterDuffUtility.getComposite(mode, alpha);
- g.setComposite(composite);
- }
-
- private void mapRect(AffineTransform matrix, RectF dst, RectF src) {
- // array with 4 corners
- float[] corners = new float[] {
- src.left, src.top,
- src.right, src.top,
- src.right, src.bottom,
- src.left, src.bottom,
- };
-
- // apply the transform to them.
- matrix.transform(corners, 0, corners, 0, 4);
-
- // now put the result in the rect. We take the min/max of Xs and min/max of Ys
- dst.left = Math.min(Math.min(corners[0], corners[2]), Math.min(corners[4], corners[6]));
- dst.right = Math.max(Math.max(corners[0], corners[2]), Math.max(corners[4], corners[6]));
-
- dst.top = Math.min(Math.min(corners[1], corners[3]), Math.min(corners[5], corners[7]));
- dst.bottom = Math.max(Math.max(corners[1], corners[3]), Math.max(corners[5], corners[7]));
- }
-
- /**
- * Returns the clip of the oldest snapshot of the stack, appropriately translated to be
- * expressed in the coordinate system of the latest snapshot.
- */
- public Rectangle getOriginalClip() {
- GcSnapshot originalSnapshot = this;
- while (originalSnapshot.mPrevious != null) {
- originalSnapshot = originalSnapshot.mPrevious;
- }
- if (originalSnapshot.mLayers.isEmpty()) {
- return null;
- }
- Graphics2D graphics2D = originalSnapshot.mLayers.get(0).getGraphics();
- Rectangle bounds = graphics2D.getClipBounds();
- if (bounds == null) {
- return null;
- }
- try {
- AffineTransform originalTransform =
- ((Graphics2D) graphics2D.create()).getTransform().createInverse();
- AffineTransform latestTransform = getTransform().createInverse();
- bounds.x += latestTransform.getTranslateX() - originalTransform.getTranslateX();
- bounds.y += latestTransform.getTranslateY() - originalTransform.getTranslateY();
- } catch (NoninvertibleTransformException e) {
- Bridge.getLog().warning(null, "Non invertible transformation", null, null);
- }
- return bounds;
- }
-
-}
diff --git a/bridge/src/com/android/layoutlib/bridge/impl/Layout.java b/bridge/src/com/android/layoutlib/bridge/impl/Layout.java
index c2fb3e1ac7..c8e5009888 100644
--- a/bridge/src/com/android/layoutlib/bridge/impl/Layout.java
+++ b/bridge/src/com/android/layoutlib/bridge/impl/Layout.java
@@ -18,7 +18,6 @@ package com.android.layoutlib.bridge.impl;
import com.android.ide.common.rendering.api.HardwareConfig;
import com.android.ide.common.rendering.api.RenderResources;
-import com.android.ide.common.rendering.api.ResourceNamespace;
import com.android.ide.common.rendering.api.ResourceReference;
import com.android.ide.common.rendering.api.ResourceValue;
import com.android.ide.common.rendering.api.SessionParams;
@@ -38,7 +37,6 @@ import com.android.resources.ScreenOrientation;
import android.R.id;
import android.annotation.NonNull;
-import android.annotation.Nullable;
import android.graphics.Color;
import android.graphics.drawable.Drawable;
import android.util.DisplayMetrics;
@@ -148,7 +146,8 @@ class Layout extends FrameLayout {
NavigationBar navBar = null;
if (builder.mWindowBackground != null) {
- Drawable d = ResourceHelper.getDrawable(builder.mWindowBackground, builder.mContext);
+ Drawable d = ResourceHelper.getDrawable(builder.mWindowBackground, builder.mContext,
+ builder.mContext.getTheme());
setBackground(d);
}
diff --git a/bridge/src/com/android/layoutlib/bridge/impl/LayoutParserWrapper.java b/bridge/src/com/android/layoutlib/bridge/impl/LayoutParserWrapper.java
index 71e7fd2c5e..8c3b128c4b 100644
--- a/bridge/src/com/android/layoutlib/bridge/impl/LayoutParserWrapper.java
+++ b/bridge/src/com/android/layoutlib/bridge/impl/LayoutParserWrapper.java
@@ -49,6 +49,7 @@ public class LayoutParserWrapper implements XmlPullParser {
private List<Attribute> mAttributes;
private String mText;
private String mName;
+ private boolean mIsWhitespace;
// Used to end the document before the actual parser ends.
private int mFinalDepth = -1;
@@ -119,6 +120,7 @@ public class LayoutParserWrapper implements XmlPullParser {
mDepth = mDelegate.getDepth();
mText = mDelegate.getText();
mName = mDelegate.getName();
+ mIsWhitespace = mNext == TEXT && mDelegate.isWhitespace();
mPeeked = true;
return mNext;
}
@@ -203,6 +205,11 @@ public class LayoutParserWrapper implements XmlPullParser {
return returnValue;
}
+ @Override
+ public boolean isWhitespace() throws XmlPullParserException {
+ return mPeeked ? mIsWhitespace : mDelegate.isWhitespace();
+ }
+
private static class Attribute {
@Nullable
public final String namespace;
@@ -311,11 +318,6 @@ public class LayoutParserWrapper implements XmlPullParser {
}
@Override
- public boolean isWhitespace() throws XmlPullParserException {
- throw new UnsupportedOperationException("Only few parser methods are supported.");
- }
-
- @Override
public char[] getTextCharacters(int[] ints) {
throw new UnsupportedOperationException("Only few parser methods are supported.");
}
diff --git a/bridge/src/com/android/layoutlib/bridge/impl/PlayAnimationThread.java b/bridge/src/com/android/layoutlib/bridge/impl/PlayAnimationThread.java
deleted file mode 100644
index 7b701802b9..0000000000
--- a/bridge/src/com/android/layoutlib/bridge/impl/PlayAnimationThread.java
+++ /dev/null
@@ -1,49 +0,0 @@
-/*
- * Copyright (C) 2010 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.layoutlib.bridge.impl;
-
-import com.android.ide.common.rendering.api.IAnimationListener;
-import com.android.ide.common.rendering.api.Result;
-import com.android.ide.common.rendering.api.Result.Status;
-
-import android.animation.AnimationThread;
-import android.animation.Animator;
-
-public class PlayAnimationThread extends AnimationThread {
-
- private final Animator mAnimator;
-
- public PlayAnimationThread(Animator animator, RenderSessionImpl scene, String animName,
- IAnimationListener listener) {
- super(scene, animName, listener);
- mAnimator = animator;
- }
-
- @Override
- public Result preAnimation() {
- // start the animation. This will send a message to the handler right away, so
- // the queue is filled when this method returns.
- mAnimator.start();
-
- return Status.SUCCESS.createResult();
- }
-
- @Override
- public void postAnimation() {
- // nothing to be done.
- }
-}
diff --git a/bridge/src/com/android/layoutlib/bridge/impl/PorterDuffUtility.java b/bridge/src/com/android/layoutlib/bridge/impl/PorterDuffUtility.java
deleted file mode 100644
index 86f80fee23..0000000000
--- a/bridge/src/com/android/layoutlib/bridge/impl/PorterDuffUtility.java
+++ /dev/null
@@ -1,107 +0,0 @@
-/*
- * Copyright (C) 2014 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.layoutlib.bridge.impl;
-
-import com.android.ide.common.rendering.api.ILayoutLog;
-import com.android.layoutlib.bridge.Bridge;
-
-import android.graphics.BlendComposite;
-import android.graphics.BlendComposite.BlendingMode;
-import android.graphics.PorterDuff;
-import android.graphics.PorterDuff.Mode;
-import android.graphics.PorterDuffColorFilter_Delegate;
-
-import java.awt.AlphaComposite;
-import java.awt.Composite;
-
-/**
- * Provides various utility methods for {@link PorterDuffColorFilter_Delegate}.
- */
-public final class PorterDuffUtility {
-
- private static final int MODES_COUNT = Mode.values().length;
-
- // Make the class non-instantiable.
- private PorterDuffUtility() {
- }
-
- /**
- * Convert the porterDuffMode from the framework to its corresponding enum. This defaults to
- * {@link Mode#SRC_OVER} for invalid modes.
- */
- public static Mode getPorterDuffMode(int porterDuffMode) {
- if (porterDuffMode >= 0 && porterDuffMode < MODES_COUNT) {
- return PorterDuff.intToMode(porterDuffMode);
- }
- Bridge.getLog().error(ILayoutLog.TAG_BROKEN,
- String.format("Unknown PorterDuff.Mode: %1$d", porterDuffMode), null, null);
- assert false;
- return Mode.SRC_OVER;
- }
-
- /**
- * A utility method to get the {@link Composite} that represents the filter for the given
- * PorterDuff mode and the alpha. Defaults to {@link Mode#SRC_OVER} for invalid modes.
- */
- public static Composite getComposite(Mode mode, int alpha255) {
- float alpha1 = alpha255 != 0xFF ? alpha255 / 255.f : 1.f;
- switch (mode) {
- case CLEAR:
- return AlphaComposite.getInstance(AlphaComposite.CLEAR, alpha1);
- case SRC:
- return AlphaComposite.getInstance(AlphaComposite.SRC, alpha1);
- case DST:
- return AlphaComposite.getInstance(AlphaComposite.DST, alpha1);
- case SRC_OVER:
- return AlphaComposite.getInstance(AlphaComposite.SRC_OVER, alpha1);
- case DST_OVER:
- return AlphaComposite.getInstance(AlphaComposite.DST_OVER, alpha1);
- case SRC_IN:
- return AlphaComposite.getInstance(AlphaComposite.SRC_IN, alpha1);
- case DST_IN:
- return AlphaComposite.getInstance(AlphaComposite.DST_IN, alpha1);
- case SRC_OUT:
- return AlphaComposite.getInstance(AlphaComposite.SRC_OUT, alpha1);
- case DST_OUT:
- return AlphaComposite.getInstance(AlphaComposite.DST_OUT, alpha1);
- case SRC_ATOP:
- return AlphaComposite.getInstance(AlphaComposite.SRC_ATOP, alpha1);
- case DST_ATOP:
- return AlphaComposite.getInstance(AlphaComposite.DST_ATOP, alpha1);
- case XOR:
- return AlphaComposite.getInstance(AlphaComposite.XOR, alpha1);
- case DARKEN:
- return BlendComposite.getInstance(BlendingMode.DARKEN, alpha1);
- case LIGHTEN:
- return BlendComposite.getInstance(BlendingMode.LIGHTEN, alpha1);
- case MULTIPLY:
- return BlendComposite.getInstance(BlendingMode.MULTIPLY, alpha1);
- case SCREEN:
- return BlendComposite.getInstance(BlendingMode.SCREEN, alpha1);
- case ADD:
- return BlendComposite.getInstance(BlendingMode.ADD, alpha1);
- case OVERLAY:
- return BlendComposite.getInstance(BlendingMode.OVERLAY, alpha1);
- default:
- Bridge.getLog().fidelityWarning(ILayoutLog.TAG_BROKEN,
- String.format("Unsupported PorterDuff Mode: %1$s", mode.name()),
- null, null, null /*data*/);
-
- return AlphaComposite.getInstance(AlphaComposite.SRC_OVER, alpha1);
- }
- }
-}
diff --git a/bridge/src/com/android/layoutlib/bridge/impl/RenderAction.java b/bridge/src/com/android/layoutlib/bridge/impl/RenderAction.java
index 2ab0509b6a..d444754145 100644
--- a/bridge/src/com/android/layoutlib/bridge/impl/RenderAction.java
+++ b/bridge/src/com/android/layoutlib/bridge/impl/RenderAction.java
@@ -28,10 +28,11 @@ import com.android.resources.Density;
import com.android.resources.ScreenOrientation;
import com.android.resources.ScreenRound;
import com.android.resources.ScreenSize;
+import com.android.tools.layoutlib.annotations.NotNull;
+import com.android.tools.layoutlib.annotations.Nullable;
import com.android.tools.layoutlib.annotations.VisibleForTesting;
import android.animation.PropertyValuesHolder_Accessor;
-import android.animation.PropertyValuesHolder_Delegate;
import android.content.res.Configuration;
import android.os.HandlerThread_Delegate;
import android.util.DisplayMetrics;
@@ -42,7 +43,10 @@ import android.view.ViewConfiguration_Accessor;
import android.view.WindowManagerGlobal_Delegate;
import android.view.inputmethod.InputMethodManager_Accessor;
+import java.util.Collections;
import java.util.Locale;
+import java.util.Set;
+import java.util.WeakHashMap;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.locks.ReentrantLock;
@@ -64,6 +68,10 @@ import static com.android.ide.common.rendering.api.Result.Status.SUCCESS;
*/
public abstract class RenderAction<T extends RenderParams> {
+ private static final Set<String> COMPOSE_CLASS_FQNS =
+ Set.of("androidx.compose.ui.tooling.ComposeViewAdapter",
+ "androidx.compose.ui.tooling.preview.ComposeViewAdapter");
+
/**
* The current context being rendered. This is set through {@link #acquire(long)} and
* {@link #init(long)}, and unset in {@link #release()}.
@@ -75,6 +83,10 @@ public abstract class RenderAction<T extends RenderParams> {
private BridgeContext mContext;
+ private static final Object sContextLock = new Object();
+ private static final Set<BridgeContext> sContexts =
+ Collections.newSetFromMap(new WeakHashMap<>());
+
/**
* Creates a renderAction.
* <p>
@@ -129,10 +141,11 @@ public abstract class RenderAction<T extends RenderParams> {
// build the context
mContext = new BridgeContext(mParams.getProjectKey(), metrics, resources,
mParams.getAssets(), mParams.getLayoutlibCallback(), getConfiguration(mParams),
- mParams.getTargetSdkVersion(), mParams.isRtlSupported(),
- Boolean.TRUE.equals(mParams.getFlag(RenderParamsFlags.FLAG_ENABLE_SHADOW)),
- Boolean.TRUE.equals(mParams.getFlag(RenderParamsFlags.FLAG_RENDER_HIGH_QUALITY_SHADOW)));
+ mParams.getTargetSdkVersion(), mParams.isRtlSupported());
+ synchronized (sContextLock) {
+ sContexts.add(mContext);
+ }
setUp();
return SUCCESS.createResult();
@@ -241,7 +254,7 @@ public abstract class RenderAction<T extends RenderParams> {
// make sure the Resources object references the context (and other objects) for this
// scene
- mContext.initResources();
+ mContext.initResources(mParams.getAssets());
sCurrentContext = mContext;
// Set-up WindowManager
@@ -269,19 +282,12 @@ public abstract class RenderAction<T extends RenderParams> {
mContext.disposeResources();
}
- if (sCurrentContext != null) {
- // quit HandlerThread created during this session.
- HandlerThread_Delegate.cleanUp(sCurrentContext);
- }
-
// clear the stored ViewConfiguration since the map is per density and not per context.
ViewConfiguration_Accessor.clearConfigurations();
// remove the InputMethodManager
InputMethodManager_Accessor.tearDownEditMode();
- sCurrentContext = null;
-
Bridge.setLog(null);
if (mContext != null) {
mContext.getRenderResources().setLogger(null);
@@ -409,9 +415,52 @@ public abstract class RenderAction<T extends RenderParams> {
if (locale != null && !locale.isEmpty()) config.locale = new Locale(locale);
config.fontScale = params.getFontScale();
+ config.uiMode = params.getUiMode();
// TODO: fill in more config info.
return config;
}
+
+ @Nullable
+ private static ClassLoader findComposeClassLoader(@NotNull BridgeContext context) {
+ for (String composeClassName: COMPOSE_CLASS_FQNS) {
+ try {
+ return context.getLayoutlibCallback().findClass(composeClassName).getClassLoader();
+ } catch (Throwable ignore) {}
+ }
+
+ return null;
+ }
+
+ @Nullable
+ public static BridgeContext findContextFor(@NotNull ClassLoader classLoader) {
+ synchronized (sContextLock) {
+ for (BridgeContext c : RenderAction.sContexts) {
+ if (c == null) {
+ continue;
+ }
+ try {
+ if (findComposeClassLoader(c) == classLoader) {
+ return c;
+ }
+ } catch (Throwable ignore) {
+ }
+ }
+ return null;
+ }
+ }
+
+ protected void dispose() {
+ synchronized (sContextLock) {
+ sContexts.remove(mContext);
+ }
+
+ if (sCurrentContext != null) {
+ // quit HandlerThread created during this session.
+ HandlerThread_Delegate.cleanUp(sCurrentContext);
+ }
+
+ sCurrentContext = null;
+ }
}
diff --git a/bridge/src/com/android/layoutlib/bridge/impl/RenderDrawable.java b/bridge/src/com/android/layoutlib/bridge/impl/RenderDrawable.java
index dc0595150d..60c3864a66 100644
--- a/bridge/src/com/android/layoutlib/bridge/impl/RenderDrawable.java
+++ b/bridge/src/com/android/layoutlib/bridge/impl/RenderDrawable.java
@@ -27,7 +27,7 @@ import com.android.resources.ResourceType;
import android.annotation.NonNull;
import android.graphics.Bitmap;
-import android.graphics.Bitmap_Delegate;
+import android.graphics.Bitmap.Config;
import android.graphics.Canvas;
import android.graphics.drawable.Drawable;
import android.graphics.drawable.StateListDrawable;
@@ -115,7 +115,7 @@ public class RenderDrawable extends RenderAction<DrawableParams> {
content.setBackground(d);
// Set the AttachInfo on the root view.
- AttachInfo_Accessor.setAttachInfo(content);
+ AttachInfo_Accessor.setAttachInfo(content, null);
// Measure.
int w = d.getIntrinsicWidth();
@@ -153,8 +153,11 @@ public class RenderDrawable extends RenderAction<DrawableParams> {
BufferedImage image = getImage(w, h);
// Create an Android bitmap around the BufferedImage.
- Bitmap bitmap = Bitmap_Delegate.createBitmap(image,
- true /*isMutable*/, hardwareConfig.getDensity());
+ Bitmap bitmap = Bitmap.createBitmap(image.getWidth(), image.getHeight(),
+ Config.ARGB_8888);
+ bitmap.setPixels(image.getRGB(0, 0, image.getWidth(), image.getHeight(),
+ null, 0, image.getWidth()), 0, image.getWidth(), 0, 0, image
+ .getWidth(), image.getHeight());
// Create a Canvas around the Android bitmap.
Canvas canvas = new Canvas(bitmap);
@@ -162,6 +165,10 @@ public class RenderDrawable extends RenderAction<DrawableParams> {
// Draw.
content.draw(canvas);
+ int[] pixels = new int[image.getWidth() * image.getHeight()];
+ bitmap.getPixels(pixels, 0, image.getWidth(), 0, 0, image.getWidth(),
+ image.getHeight());
+ image.setRGB(0, 0, image.getWidth(), image.getHeight(), pixels, 0, image.getWidth());
// Detach root from window after draw.
AttachInfo_Accessor.detachFromWindow(content);
diff --git a/bridge/src/com/android/layoutlib/bridge/impl/RenderSessionImpl.java b/bridge/src/com/android/layoutlib/bridge/impl/RenderSessionImpl.java
index 93d3df8640..8c46e33c3b 100644
--- a/bridge/src/com/android/layoutlib/bridge/impl/RenderSessionImpl.java
+++ b/bridge/src/com/android/layoutlib/bridge/impl/RenderSessionImpl.java
@@ -47,28 +47,43 @@ import com.android.layoutlib.bridge.android.support.SupportPreferencesUtil;
import com.android.layoutlib.bridge.impl.binding.FakeAdapter;
import com.android.layoutlib.bridge.impl.binding.FakeExpandableAdapter;
import com.android.tools.idea.validator.LayoutValidator;
+import com.android.tools.idea.validator.ValidatorData.Issue.IssueBuilder;
+import com.android.tools.idea.validator.ValidatorData.Level;
+import com.android.tools.idea.validator.ValidatorData.Type;
+import com.android.tools.idea.validator.ValidatorHierarchy;
import com.android.tools.idea.validator.ValidatorResult;
import com.android.tools.idea.validator.ValidatorResult.Builder;
-import com.android.tools.layoutlib.java.System_Delegate;
-import com.android.utils.Pair;
+import com.android.tools.idea.validator.hierarchy.CustomHierarchyHelper;
+import com.android.tools.layoutlib.annotations.NotNull;
+import android.animation.AnimationHandler;
import android.annotation.NonNull;
import android.annotation.Nullable;
+import android.content.Context;
import android.graphics.Bitmap;
-import android.graphics.Bitmap_Delegate;
import android.graphics.Canvas;
-import android.graphics.NinePatch_Delegate;
-import android.os.Looper;
+import android.graphics.HardwareRenderer;
+import android.graphics.LayoutlibRenderer;
+import android.graphics.PixelFormat;
+import android.graphics.RenderNode;
+import android.graphics.drawable.AnimatedVectorDrawable_VectorDrawableAnimatorUI_Delegate;
+import android.media.Image;
+import android.media.Image.Plane;
+import android.media.ImageReader;
import android.preference.Preference_Delegate;
+import android.util.Pair;
+import android.util.TimeUtils;
import android.view.AttachInfo_Accessor;
import android.view.BridgeInflater;
import android.view.Choreographer_Delegate;
+import android.view.MotionEvent;
import android.view.View;
import android.view.View.MeasureSpec;
import android.view.ViewGroup;
import android.view.ViewGroup.LayoutParams;
import android.view.ViewGroup.MarginLayoutParams;
import android.view.ViewParent;
+import android.view.WindowManagerImpl;
import android.widget.AbsListView;
import android.widget.AbsSpinner;
import android.widget.ActionMenuView;
@@ -82,22 +97,24 @@ import android.widget.TabHost;
import android.widget.TabHost.TabSpec;
import android.widget.TabWidget;
-import java.awt.AlphaComposite;
-import java.awt.Color;
-import java.awt.Graphics2D;
import java.awt.image.BufferedImage;
+import java.awt.image.DataBufferInt;
+import java.io.PrintWriter;
+import java.io.StringWriter;
+import java.nio.IntBuffer;
import java.util.ArrayList;
import java.util.IdentityHashMap;
import java.util.List;
import java.util.Map;
-import com.google.android.apps.common.testing.accessibility.framework.uielement.AccessibilityHierarchyAndroid_ViewElementClassNamesAndroid_Delegate;
+import com.android.internal.R;
+import android.content.res.TypedArray;
import static com.android.ide.common.rendering.api.Result.Status.ERROR_INFLATION;
import static com.android.ide.common.rendering.api.Result.Status.ERROR_NOT_INFLATED;
import static com.android.ide.common.rendering.api.Result.Status.ERROR_UNKNOWN;
import static com.android.ide.common.rendering.api.Result.Status.SUCCESS;
-import static com.android.layoutlib.bridge.util.ReflectionUtils.isInstanceOf;
+import static com.android.layoutlib.common.util.ReflectionUtils.isInstanceOf;
/**
* Class implementing the render session.
@@ -116,7 +133,6 @@ public class RenderSessionImpl extends RenderAction<SessionParams> {
private BridgeInflater mInflater;
private ViewGroup mViewRoot;
private FrameLayout mContentRoot;
- private Canvas mCanvas;
private int mMeasuredScreenWidth = -1;
private int mMeasuredScreenHeight = -1;
/** If >= 0, a frame will be executed */
@@ -130,7 +146,19 @@ public class RenderSessionImpl extends RenderAction<SessionParams> {
private List<ViewInfo> mSystemViewInfoList;
private Layout.Builder mLayoutBuilder;
private boolean mNewRenderSize;
+ private ImageReader mImageReader;
+ private Image mNativeImage;
+ private LayoutlibRenderer mRenderer = new LayoutlibRenderer();
+
+ // Passed in MotionEvent initialization when dispatching a touch event.
+ private final MotionEvent.PointerProperties[] mPointerProperties =
+ MotionEvent.PointerProperties.createArray(1);
+ private final MotionEvent.PointerCoords[] mPointerCoords =
+ MotionEvent.PointerCoords.createArray(1);
+
+ private long mLastActionDownTimeNanos = -1;
@Nullable private ValidatorResult mValidatorResult = null;
+ @Nullable private ValidatorHierarchy mValidatorHierarchy = null;
private static final class PostInflateException extends Exception {
private static final long serialVersionUID = 1L;
@@ -183,6 +211,11 @@ public class RenderSessionImpl extends RenderAction<SessionParams> {
ILayoutPullParser layoutParser = params.getLayoutDescription();
mBlockParser = new BridgeXmlBlockParser(layoutParser, context, layoutParser.getLayoutNamespace());
+ Bitmap.setDefaultDensity(params.getHardwareConfig().getDensity().getDpiValue());
+
+ // Needed in order to initialize static state of ImageReader
+ ImageReader.nativeClassInit();
+
return SUCCESS.createResult();
}
@@ -191,16 +224,15 @@ public class RenderSessionImpl extends RenderAction<SessionParams> {
*/
private void measureLayout(@NonNull SessionParams params) {
// only do the screen measure when needed.
- if (mMeasuredScreenWidth != -1) {
- return;
+ int previousWidth = mMeasuredScreenWidth;
+ int previousHeight = mMeasuredScreenHeight;
+ HardwareConfig hardwareConfig = params.getHardwareConfig();
+ if (mMeasuredScreenWidth == -1) {
+ mMeasuredScreenWidth = hardwareConfig.getScreenWidth();
+ mMeasuredScreenHeight = hardwareConfig.getScreenHeight();
}
RenderingMode renderingMode = params.getRenderingMode();
- HardwareConfig hardwareConfig = params.getHardwareConfig();
-
- mNewRenderSize = true;
- mMeasuredScreenWidth = hardwareConfig.getScreenWidth();
- mMeasuredScreenHeight = hardwareConfig.getScreenHeight();
if (renderingMode != RenderingMode.NORMAL) {
int widthMeasureSpecMode = renderingMode.getHorizAction() == SizeAction.EXPAND ?
@@ -224,55 +256,62 @@ public class RenderSessionImpl extends RenderAction<SessionParams> {
// and apply this to the screen size.
View measuredView = mContentRoot.getChildAt(0);
+ if (measuredView == null) {
+ return;
+ }
+ int maxWidth = hardwareConfig.getScreenWidth();
+ int maxHeight = hardwareConfig.getScreenHeight();
// first measure the full layout, with EXACTLY to get the size of the
// content as it is inside the decor/dialog
Pair<Integer, Integer> exactMeasure = measureView(
mViewRoot, measuredView,
- mMeasuredScreenWidth, MeasureSpec.EXACTLY,
- mMeasuredScreenHeight, MeasureSpec.EXACTLY);
+ maxWidth, MeasureSpec.EXACTLY,
+ maxHeight, MeasureSpec.EXACTLY);
// now measure the content only using UNSPECIFIED (where applicable, based on
// the rendering mode). This will give us the size the content needs.
Pair<Integer, Integer> neededMeasure = measureView(
- mContentRoot, mContentRoot.getChildAt(0),
- mMeasuredScreenWidth, widthMeasureSpecMode,
- mMeasuredScreenHeight, heightMeasureSpecMode);
- int neededWidth = neededMeasure.getFirst();
- int neededHeight = neededMeasure.getSecond();
+ mContentRoot, measuredView,
+ maxWidth, widthMeasureSpecMode,
+ maxHeight, heightMeasureSpecMode);
// If measuredView is not null, exactMeasure nor result will be null.
- assert (exactMeasure != null && neededMeasure != null) || measuredView == null;
+ assert (exactMeasure != null && neededMeasure != null);
// now look at the difference and add what is needed.
- if (renderingMode.getHorizAction() == SizeAction.EXPAND) {
- int measuredWidth = exactMeasure.getFirst();
- if (neededWidth > measuredWidth) {
- mMeasuredScreenWidth += neededWidth - measuredWidth;
- }
- if (mMeasuredScreenWidth < measuredWidth) {
- // If the screen width is less than the exact measured width,
- // expand to match.
- mMeasuredScreenWidth = measuredWidth;
- }
- } else if (renderingMode.getHorizAction() == SizeAction.SHRINK) {
- mMeasuredScreenWidth = neededWidth;
- }
+ mMeasuredScreenWidth = calcSize(mMeasuredScreenWidth, neededMeasure.first,
+ exactMeasure.first, renderingMode.getHorizAction());
+ mMeasuredScreenHeight = calcSize(mMeasuredScreenHeight, neededMeasure.second,
+ exactMeasure.second, renderingMode.getVertAction());
+ }
+ mNewRenderSize =
+ mMeasuredScreenWidth != previousWidth || mMeasuredScreenHeight != previousHeight;
+ }
- if (renderingMode.getVertAction() == SizeAction.EXPAND) {
- int measuredHeight = exactMeasure.getSecond();
- if (neededHeight > measuredHeight) {
- mMeasuredScreenHeight += neededHeight - measuredHeight;
- }
- if (mMeasuredScreenHeight < measuredHeight) {
- // If the screen height is less than the exact measured height,
- // expand to match.
- mMeasuredScreenHeight = measuredHeight;
- }
- } else if (renderingMode.getVertAction() == SizeAction.SHRINK) {
- mMeasuredScreenHeight = neededHeight;
+ /**
+ * Calculate the required vertical (height) or horizontal (width) size of the canvas for the
+ * view, given current size requirements.
+ * @param currentSize current size of the canvas
+ * @param neededSize the size the content actually needs
+ * @param measuredSize the measured size of the content (restricted by the current size)
+ * @param action the {@link SizeAction} of the view
+ * @return the size the canvas should be
+ */
+ private static int calcSize(int currentSize, int neededSize, int measuredSize,
+ SizeAction action) {
+ if (action == SizeAction.EXPAND) {
+ if (neededSize > measuredSize) {
+ currentSize += neededSize - measuredSize;
+ }
+ if (currentSize < measuredSize) {
+ // If the screen size is less than the exact measured size, expand to match.
+ currentSize = measuredSize;
}
+ } else if (action == SizeAction.SHRINK) {
+ currentSize = neededSize;
}
+ return currentSize;
}
/**
@@ -329,7 +368,7 @@ public class RenderSessionImpl extends RenderAction<SessionParams> {
context.popParser();
// set the AttachInfo on the root view.
- AttachInfo_Accessor.setAttachInfo(mViewRoot);
+ AttachInfo_Accessor.setAttachInfo(mViewRoot, mRenderer);
// post-inflate process. For now this supports TabHost/TabWidget
postInflateProcess(view, params.getLayoutlibCallback(), isPreference ? view : null);
@@ -346,8 +385,6 @@ public class RenderSessionImpl extends RenderAction<SessionParams> {
visitAllChildren(mViewRoot, 0, 0, params.getExtendedViewInfoMode(),
false);
- Choreographer_Delegate.clearFrames();
-
return SUCCESS.createResult();
} catch (PostInflateException e) {
return ERROR_INFLATION.createResult(e.getMessage(), e);
@@ -387,18 +424,19 @@ public class RenderSessionImpl extends RenderAction<SessionParams> {
}
/**
- * Renders the given view hierarchy to the passed canvas and returns the result of the render
- * operation.
- * @param canvas an optional canvas to render the views to. If null, only the measure and
- * layout steps will be executed.
+ * Creates a display list for the root view and draws that display list with a "hardware"
+ * renderer. In layoutlib the renderer is not actually hardware (in contrast to the actual
+ * android) but pretends to be so in order to draw all the advanced android features (e.g.
+ * shadows).
*/
- private static Result renderAndBuildResult(@NonNull ViewGroup viewRoot, @Nullable Canvas canvas) {
- if (canvas == null) {
- return SUCCESS.createResult();
- }
+ private static Result renderAndBuildResult(@NonNull ViewGroup viewRoot,
+ @NonNull HardwareRenderer renderer) {
AttachInfo_Accessor.dispatchOnPreDraw(viewRoot);
- viewRoot.draw(canvas);
+
+ RenderNode node = viewRoot.updateDisplayListIfDirty();
+ renderer.setContentRoot(node);
+ renderer.createRenderRequest().syncAndDraw();
return SUCCESS.createResult();
}
@@ -466,16 +504,14 @@ public class RenderSessionImpl extends RenderAction<SessionParams> {
HardwareConfig hardwareConfig = params.getHardwareConfig();
Result renderResult = SUCCESS.createResult();
+ float scaleX = 1.0f;
+ float scaleY = 1.0f;
if (onlyMeasure) {
// delete the canvas and image to reset them on the next full rendering
mImage = null;
- mCanvas = null;
+ disposeImageSurface();
doLayout(getContext(), mViewRoot, mMeasuredScreenWidth, mMeasuredScreenHeight);
} else {
- // draw the views
- // create the BufferedImage into which the layout will be rendered.
- boolean newImage = false;
-
// When disableBitmapCaching is true, we do not reuse mImage and
// we create a new one in every render.
// This is useful when mImage is just a wrapper of Graphics2D so
@@ -483,8 +519,7 @@ public class RenderSessionImpl extends RenderAction<SessionParams> {
boolean disableBitmapCaching = Boolean.TRUE.equals(params.getFlag(
RenderParamsFlags.FLAG_KEY_DISABLE_BITMAP_CACHING));
- if (mNewRenderSize || mCanvas == null || disableBitmapCaching) {
- mNewRenderSize = false;
+ if (mNewRenderSize || mImageReader == null || disableBitmapCaching) {
if (params.getImageFactory() != null) {
mImage = params.getImageFactory().getImage(
mMeasuredScreenWidth,
@@ -494,99 +529,104 @@ public class RenderSessionImpl extends RenderAction<SessionParams> {
mMeasuredScreenWidth,
mMeasuredScreenHeight,
BufferedImage.TYPE_INT_ARGB);
- newImage = true;
- }
-
- if (params.isTransparentBackground()) {
- // since we override the content, it's the same as if it was a new image.
- newImage = true;
- Graphics2D gc = mImage.createGraphics();
- gc.setColor(new Color(0, true));
- gc.setComposite(AlphaComposite.Src);
- gc.fillRect(0, 0, mMeasuredScreenWidth, mMeasuredScreenHeight);
- gc.dispose();
- }
-
- // create an Android bitmap around the BufferedImage
- Bitmap bitmap = Bitmap_Delegate.createBitmap(mImage,
- true /*isMutable*/, hardwareConfig.getDensity());
-
- if (mCanvas == null) {
- // create a Canvas around the Android bitmap
- mCanvas = new Canvas(bitmap);
- } else {
- mCanvas.setBitmap(bitmap);
}
boolean enableImageResizing =
mImage.getWidth() != mMeasuredScreenWidth &&
- mImage.getHeight() != mMeasuredScreenHeight &&
- Boolean.TRUE.equals(params.getFlag(
- RenderParamsFlags.FLAG_KEY_RESULT_IMAGE_AUTO_SCALE));
+ mImage.getHeight() != mMeasuredScreenHeight &&
+ Boolean.TRUE.equals(params.getFlag(
+ RenderParamsFlags.FLAG_KEY_RESULT_IMAGE_AUTO_SCALE));
- if (enableImageResizing) {
- float scaleX = (float)mImage.getWidth() / mMeasuredScreenWidth;
- float scaleY = (float)mImage.getHeight() / mMeasuredScreenHeight;
- mCanvas.scale(scaleX, scaleY);
+ if (enableImageResizing || mNewRenderSize) {
+ disposeImageSurface();
}
- mCanvas.setDensity(hardwareConfig.getDensity().getDpiValue());
- }
-
- if (freshRender && !newImage) {
- Graphics2D gc = mImage.createGraphics();
- gc.setComposite(AlphaComposite.Src);
-
- gc.setColor(new Color(0x00000000, true));
- gc.fillRect(0, 0,
- mMeasuredScreenWidth, mMeasuredScreenHeight);
+ if (enableImageResizing) {
+ scaleX = mImage.getWidth() * 1.0f / mMeasuredScreenWidth;
+ scaleY = mImage.getHeight() * 1.0f / mMeasuredScreenHeight;
+ mRenderer.setScale(scaleX, scaleY);
+ } else {
+ mRenderer.setScale(1.0f, 1.0f);
+ }
- // done
- gc.dispose();
+ if (mImageReader == null) {
+ mImageReader = ImageReader.newInstance(mImage.getWidth(), mImage.getHeight(), PixelFormat.RGBA_8888, 1);
+ mRenderer.setSurface(mImageReader.getSurface());
+ mNativeImage = mImageReader.acquireNextImage();
+ }
+ mNewRenderSize = false;
}
doLayout(getContext(), mViewRoot, mMeasuredScreenWidth, mMeasuredScreenHeight);
+
if (mElapsedFrameTimeNanos >= 0) {
- long initialTime = System_Delegate.nanoTime();
if (!mFirstFrameExecuted) {
// We need to run an initial draw call to initialize the animations
- renderAndBuildResult(mViewRoot, NOP_CANVAS);
+ AttachInfo_Accessor.dispatchOnPreDraw(mViewRoot);
+ mViewRoot.draw(NOP_CANVAS);
// The first frame will initialize the animations
- Choreographer_Delegate.doFrame(initialTime);
mFirstFrameExecuted = true;
}
// Second frame will move the animations
- Choreographer_Delegate.doFrame(initialTime + mElapsedFrameTimeNanos);
+ AnimatedVectorDrawable_VectorDrawableAnimatorUI_Delegate.sFrameTime =
+ mElapsedFrameTimeNanos / 1000000;
}
- renderResult = renderAndBuildResult(mViewRoot, mCanvas);
+
+ final TypedArray a = getContext().obtainStyledAttributes(null, R.styleable.Lighting, 0, 0);
+ float lightY = a.getDimension(R.styleable.Lighting_lightY, 0);
+ float lightZ = a.getDimension(R.styleable.Lighting_lightZ, 0);
+ float lightRadius = a.getDimension(R.styleable.Lighting_lightRadius, 0);
+ float ambientShadowAlpha = a.getFloat(R.styleable.Lighting_ambientShadowAlpha, 0);
+ float spotShadowAlpha = a.getFloat(R.styleable.Lighting_spotShadowAlpha, 0);
+ a.recycle();
+
+ mRenderer.setLightSourceGeometry(mMeasuredScreenWidth / 2, lightY, lightZ, lightRadius);
+ mRenderer.setLightSourceAlpha(ambientShadowAlpha, spotShadowAlpha);
+
+ renderResult = renderAndBuildResult(mViewRoot, mRenderer);
+
+ int[] imageData = ((DataBufferInt) mImage.getRaster().getDataBuffer()).getData();
+
+ Plane[] planes = mNativeImage.getPlanes();
+ IntBuffer buff = planes[0].getBuffer().asIntBuffer();
+ int len = buff.remaining();
+ buff.get(imageData, 0, len);
}
mSystemViewInfoList =
visitAllChildren(mViewRoot, 0, 0, params.getExtendedViewInfoMode(),
false);
- try {
- boolean enableLayoutValidation = Boolean.TRUE.equals(params.getFlag(RenderParamsFlags.FLAG_ENABLE_LAYOUT_VALIDATOR));
- boolean enableLayoutValidationImageCheck = Boolean.TRUE.equals(
- params.getFlag(RenderParamsFlags.FLAG_ENABLE_LAYOUT_VALIDATOR_IMAGE_CHECK));
+ boolean enableLayoutValidation = Boolean.TRUE.equals(params.getFlag(RenderParamsFlags.FLAG_ENABLE_LAYOUT_VALIDATOR));
+ boolean enableLayoutValidationImageCheck = Boolean.TRUE.equals(
+ params.getFlag(RenderParamsFlags.FLAG_ENABLE_LAYOUT_VALIDATOR_IMAGE_CHECK));
+ try {
if (enableLayoutValidation && !getViewInfos().isEmpty()) {
- AccessibilityHierarchyAndroid_ViewElementClassNamesAndroid_Delegate.sLayoutlibCallback =
+ CustomHierarchyHelper.sLayoutlibCallback =
getContext().getLayoutlibCallback();
BufferedImage imageToPass =
enableLayoutValidationImageCheck ? getImage() : null;
- ValidatorResult validatorResult =
- LayoutValidator.validate(((View) getViewInfos().get(0).getViewObject()), imageToPass);
- setValidatorResult(validatorResult);
+
+ ValidatorHierarchy hierarchy = LayoutValidator.buildHierarchy(
+ ((View) getViewInfos().get(0).getViewObject()),
+ imageToPass,
+ scaleX,
+ scaleY);
+ setValidatorHierarchy(hierarchy);
}
} catch (Throwable e) {
- ValidatorResult.Builder builder = new Builder();
- builder.mMetric.mErrorMessage = e.getMessage();
- setValidatorResult(builder.build());
+ StringWriter sw = new StringWriter();
+ PrintWriter pw = new PrintWriter(sw);
+ e.printStackTrace(pw);
+
+ ValidatorHierarchy hierarchy = new ValidatorHierarchy();
+ hierarchy.mErrorMessage = sw.toString();
+ setValidatorHierarchy(hierarchy);
} finally {
- AccessibilityHierarchyAndroid_ViewElementClassNamesAndroid_Delegate.sLayoutlibCallback = null;
+ CustomHierarchyHelper.sLayoutlibCallback = null;
}
// success!
@@ -624,7 +664,7 @@ public class RenderSessionImpl extends RenderAction<SessionParams> {
viewToMeasure.measure(w_spec, h_spec);
if (measuredView != null) {
- return Pair.of(measuredView.getMeasuredWidth(), measuredView.getMeasuredHeight());
+ return Pair.create(measuredView.getMeasuredWidth(), measuredView.getMeasuredHeight());
}
return null;
@@ -683,11 +723,11 @@ public class RenderSessionImpl extends RenderAction<SessionParams> {
Pair<View, Boolean> pair = context.inflateView(
binding.getHeaderAt(i),
list, false, skipCallbackParser);
- if (pair.getFirst() != null) {
- list.addHeaderView(pair.getFirst());
+ if (pair.first != null) {
+ list.addHeaderView(pair.first);
}
- skipCallbackParser |= pair.getSecond();
+ skipCallbackParser |= pair.second;
}
count = binding.getFooterCount();
@@ -695,11 +735,11 @@ public class RenderSessionImpl extends RenderAction<SessionParams> {
Pair<View, Boolean> pair = context.inflateView(
binding.getFooterAt(i),
list, false, skipCallbackParser);
- if (pair.getFirst() != null) {
- list.addFooterView(pair.getFirst());
+ if (pair.first != null) {
+ list.addFooterView(pair.first);
}
- skipCallbackParser |= pair.getSecond();
+ skipCallbackParser |= pair.second;
}
}
@@ -1021,7 +1061,7 @@ public class RenderSessionImpl extends RenderAction<SessionParams> {
int shiftX = -scrollX + Math.round(view.getTranslationX()) + hOffset;
int shiftY = -scrollY + Math.round(view.getTranslationY()) + vOffset;
result = new ViewInfo(view.getClass().getName(),
- getContext().getViewKey(view),
+ getViewKey(view),
shiftX + view.getLeft(),
shiftY + view.getTop(),
shiftX + view.getRight(),
@@ -1088,6 +1128,17 @@ public class RenderSessionImpl extends RenderAction<SessionParams> {
@Nullable
private Object getViewKey(View view) {
BridgeContext context = getContext();
+ if ("com.google.android.material.tabs.TabLayout.TabView".equals(
+ view.getClass().getCanonicalName())) {
+ // TabView from the material library is a LinearLayout, but it is defined in XML
+ // as a TabItem. Because of this, TabView doesn't get the correct cookie, but its
+ // children do. So this reassigns the cookie from the first child to link the XML
+ // TabItem to the actual TabView view.
+ ViewGroup tabView = (ViewGroup)view;
+ if (tabView.getChildCount() > 0) {
+ return context.getViewKey(tabView.getChildAt(0));
+ }
+ }
if (!(view instanceof MenuView.ItemView)) {
return context.getViewKey(view);
}
@@ -1151,6 +1202,15 @@ public class RenderSessionImpl extends RenderAction<SessionParams> {
mValidatorResult = result;
}
+ @Nullable
+ public ValidatorHierarchy getValidatorHierarchy() {
+ return mValidatorHierarchy;
+ }
+
+ public void setValidatorHierarchy(@NotNull ValidatorHierarchy validatorHierarchy) {
+ mValidatorHierarchy = validatorHierarchy;
+ }
+
public void setScene(RenderSession session) {
mScene = session;
}
@@ -1159,38 +1219,79 @@ public class RenderSessionImpl extends RenderAction<SessionParams> {
return mScene;
}
+ public void dispatchTouchEvent(int motionEventType, long currentTimeNanos, float x, float y) {
+ // Events should be dispatched to the top window if there are more than one present.
+ WindowManagerImpl wm =
+ (WindowManagerImpl)getContext().getSystemService(Context.WINDOW_SERVICE);
+ ViewGroup root = wm.getCurrentRootView();
+ if (root == null) {
+ root = mViewRoot;
+ }
+ if (root == null) {
+ return;
+ }
+ if (motionEventType == MotionEvent.ACTION_DOWN) {
+ mLastActionDownTimeNanos = currentTimeNanos;
+ }
+ // Ignore events not started with MotionEvent.ACTION_DOWN
+ if (mLastActionDownTimeNanos == -1) {
+ return;
+ }
+
+ mPointerProperties[0].id = 0;
+ mPointerProperties[0].toolType = MotionEvent.TOOL_TYPE_FINGER;
+
+ mPointerCoords[0].clear();
+ mPointerCoords[0].x = x;
+ mPointerCoords[0].y = y;
+ mPointerCoords[0].pressure = 1.0f;
+ mPointerCoords[0].size = 1.0f;
+
+ MotionEvent event = MotionEvent.obtain(
+ mLastActionDownTimeNanos / TimeUtils.NANOS_PER_MS,
+ currentTimeNanos / TimeUtils.NANOS_PER_MS,
+ motionEventType,
+ 1, mPointerProperties, mPointerCoords,
+ 0, 0, 1.0f, 1.0f, 0, 0, 0, 0);
+
+ root.dispatchTouchEvent(event);
+ }
+
+ private void disposeImageSurface() {
+ if (mImageReader != null) {
+ mImageReader.close();
+ mImageReader = null;
+ }
+ }
+
+ @Override
public void dispose() {
try {
- boolean createdLooper = false;
- if (Looper.myLooper() == null) {
- // Detaching the root view from the window will try to stop any running animations.
- // The stop method checks that it can run in the looper so, if there is no current
- // looper, we create a temporary one to complete the shutdown.
- Bridge.prepareThread();
- createdLooper = true;
- }
+ mRenderer.destroy();
+ disposeImageSurface();
+ mImage = null;
+ // detachFromWindow might create Handler callbacks, thus before Handler_Delegate.dispose
AttachInfo_Accessor.detachFromWindow(mViewRoot);
- if (mCanvas != null) {
- mCanvas.release();
- mCanvas = null;
+ AnimationHandler animationHandler = AnimationHandler.sAnimatorHandler.get();
+ if (animationHandler != null) {
+ animationHandler.mDelayedCallbackStartTime.clear();
+ animationHandler.mAnimationCallbacks.clear();
+ animationHandler.mCommitCallbacks.clear();
}
+ getContext().getSessionInteractiveData().dispose();
if (mViewInfoList != null) {
mViewInfoList.clear();
}
if (mSystemViewInfoList != null) {
mSystemViewInfoList.clear();
}
- mImage = null;
+ mValidatorResult = null;
+ mValidatorHierarchy = null;
mViewRoot = null;
mContentRoot = null;
- NinePatch_Delegate.clearCache();
-
- if (createdLooper) {
- Choreographer_Delegate.dispose();
- Bridge.cleanupThread();
- }
} catch (Throwable t) {
getContext().error("Error while disposing a RenderSession", t);
}
+ super.dispose();
}
}
diff --git a/bridge/src/com/android/layoutlib/bridge/impl/ResourceHelper.java b/bridge/src/com/android/layoutlib/bridge/impl/ResourceHelper.java
index 0a363590a7..b344537b2b 100644
--- a/bridge/src/com/android/layoutlib/bridge/impl/ResourceHelper.java
+++ b/bridge/src/com/android/layoutlib/bridge/impl/ResourceHelper.java
@@ -15,7 +15,7 @@
*/
package com.android.layoutlib.bridge.impl;
-import com.android.SdkConstants;
+import com.android.ide.common.rendering.api.AndroidConstants;
import com.android.ide.common.rendering.api.AssetRepository;
import com.android.ide.common.rendering.api.DensityBasedResourceValue;
import com.android.ide.common.rendering.api.ILayoutLog;
@@ -30,11 +30,17 @@ import com.android.layoutlib.bridge.Bridge;
import com.android.layoutlib.bridge.android.BridgeContext;
import com.android.layoutlib.bridge.android.BridgeContext.Key;
import com.android.layoutlib.bridge.android.BridgeXmlBlockParser;
+import com.android.ninepatch.GraphicsUtilities;
import com.android.ninepatch.NinePatch;
-import com.android.ninepatch.NinePatchChunk;
import com.android.resources.Density;
import com.android.resources.ResourceType;
+import org.ccil.cowan.tagsoup.HTMLSchema;
+import org.ccil.cowan.tagsoup.Parser;
+import org.xml.sax.Attributes;
+import org.xml.sax.InputSource;
+import org.xml.sax.SAXException;
+import org.xml.sax.helpers.DefaultHandler;
import org.xmlpull.v1.XmlPullParser;
import org.xmlpull.v1.XmlPullParserException;
@@ -47,9 +53,12 @@ import android.content.res.ComplexColor_Accessor;
import android.content.res.GradientColor;
import android.content.res.Resources;
import android.content.res.Resources.Theme;
+import android.content.res.StringBlock;
+import android.content.res.StringBlock.Height;
import android.graphics.Bitmap;
-import android.graphics.Bitmap_Delegate;
-import android.graphics.NinePatch_Delegate;
+import android.graphics.Bitmap.Config;
+import android.graphics.BitmapFactory;
+import android.graphics.BitmapFactory.Options;
import android.graphics.Rect;
import android.graphics.Typeface;
import android.graphics.Typeface_Accessor;
@@ -58,17 +67,42 @@ import android.graphics.drawable.BitmapDrawable;
import android.graphics.drawable.ColorDrawable;
import android.graphics.drawable.Drawable;
import android.graphics.drawable.NinePatchDrawable;
+import android.text.Annotation;
+import android.text.Spannable;
+import android.text.SpannableString;
+import android.text.Spanned;
+import android.text.SpannedString;
+import android.text.TextUtils;
+import android.text.style.AbsoluteSizeSpan;
+import android.text.style.BulletSpan;
+import android.text.style.RelativeSizeSpan;
+import android.text.style.StrikethroughSpan;
+import android.text.style.StyleSpan;
+import android.text.style.SubscriptSpan;
+import android.text.style.SuperscriptSpan;
+import android.text.style.TypefaceSpan;
+import android.text.style.URLSpan;
+import android.text.style.UnderlineSpan;
import android.util.TypedValue;
+import java.awt.image.BufferedImage;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.InputStream;
-import java.net.MalformedURLException;
+import java.io.StringReader;
+import java.util.ArrayDeque;
+import java.util.ArrayList;
+import java.util.Deque;
+import java.util.HashMap;
import java.util.HashSet;
+import java.util.List;
+import java.util.Map;
import java.util.Set;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
+import com.google.common.base.Strings;
+
import static android.content.res.AssetManager.ACCESS_STREAMING;
/**
@@ -77,7 +111,7 @@ import static android.content.res.AssetManager.ACCESS_STREAMING;
public final class ResourceHelper {
private static final Key<Set<ResourceValue>> KEY_GET_DRAWABLE =
Key.create("ResourceHelper.getDrawable");
- private static final Pattern sFloatPattern = Pattern.compile("(-?[0-9]+(?:\\.[0-9]+)?)(.*)");
+ private static final Pattern sFloatPattern = Pattern.compile("(-?[0-9]*(?:\\.[0-9]+)?)(.*)");
private static final float[] sFloatOut = new float[1];
private static final TypedValue mValue = new TypedValue();
@@ -106,7 +140,7 @@ public final class ResourceHelper {
}
if (value.charAt(0) != '#') {
- if (value.startsWith(SdkConstants.PREFIX_THEME_REF)) {
+ if (value.startsWith(AndroidConstants.PREFIX_THEME_REF)) {
throw new NumberFormatException(String.format(
"Attribute '%s' not found. Are you using the right theme?", value));
}
@@ -339,18 +373,7 @@ public final class ResourceHelper {
}
String lowerCaseValue = stringValue.toLowerCase();
- if (lowerCaseValue.endsWith(NinePatch.EXTENSION_9PATCH)) {
- try {
- return getNinePatchDrawable(density, value.isFramework(), stringValue, context);
- } catch (IOException e) {
- // failed to read the file, we'll return null below.
- Bridge.getLog().error(ILayoutLog.TAG_RESOURCES_READ,
- "Failed to load " + stringValue, e, null, null /*data*/);
- }
-
- return null;
- } else if (lowerCaseValue.endsWith(".xml") ||
- value.getResourceType() == ResourceType.AAPT) {
+ if (lowerCaseValue.endsWith(".xml") || value.getResourceType() == ResourceType.AAPT) {
// create a block parser for the file
try {
BridgeXmlBlockParser blockParser = getXmlBlockParser(context, value);
@@ -396,13 +419,39 @@ public final class ResourceHelper {
} catch (FileNotFoundException e) {
stream = null;
}
- bitmap =
- Bitmap_Delegate.createBitmap(stream, false /*isMutable*/, density);
+ Options options = new Options();
+ options.inDensity = density.getDpiValue();
+ bitmap = BitmapFactory.decodeStream(stream, null, options);
+ if (bitmap != null && bitmap.getNinePatchChunk() == null &&
+ lowerCaseValue.endsWith(NinePatch.EXTENSION_9PATCH)) {
+ //We are dealing with a non-compiled nine patch.
+ stream = repository.openNonAsset(0, stringValue, ACCESS_STREAMING);
+ NinePatch ninePatch = NinePatch.load(stream, true /*is9Patch*/, false /* convert */);
+ BufferedImage image = ninePatch.getImage();
+
+ // width and height of the nine patch without the special border.
+ int width = image.getWidth();
+ int height = image.getHeight();
+
+ // Get pixel data from image independently of its type.
+ int[] imageData = GraphicsUtilities.getPixels(image, 0, 0, width,
+ height, null);
+
+ bitmap = Bitmap.createBitmap(imageData, width, height, Config.ARGB_8888);
+
+ bitmap.setDensity(options.inDensity);
+ bitmap.setNinePatchChunk(ninePatch.getChunk().getSerializedChunk());
+ }
Bridge.setCachedBitmap(stringValue, bitmap,
value.isFramework() ? null : context.getProjectKey());
}
- return new BitmapDrawable(context.getResources(), bitmap);
+ if (bitmap != null && bitmap.getNinePatchChunk() != null) {
+ return new NinePatchDrawable(context.getResources(), bitmap, bitmap
+ .getNinePatchChunk(), new Rect(), lowerCaseValue);
+ } else {
+ return new BitmapDrawable(context.getResources(), bitmap);
+ }
} catch (IOException e) {
// we'll return null below
Bridge.getLog().error(ILayoutLog.TAG_RESOURCES_READ,
@@ -451,58 +500,6 @@ public final class ResourceHelper {
return getFont(value.getValue(), context, theme, value.isFramework());
}
- private static Drawable getNinePatchDrawable(Density density, boolean isFramework,
- String path, BridgeContext context) throws IOException {
- // see if we still have both the chunk and the bitmap in the caches
- NinePatchChunk chunk = Bridge.getCached9Patch(path,
- isFramework ? null : context.getProjectKey());
- Bitmap bitmap = Bridge.getCachedBitmap(path,
- isFramework ? null : context.getProjectKey());
-
- // if either chunk or bitmap is null, then we reload the 9-patch file.
- if (chunk == null || bitmap == null) {
- try {
- AssetRepository repository = getAssetRepository(context);
- if (!repository.isFileResource(path)) {
- return null;
- }
- InputStream stream = repository.openNonAsset(0, path, ACCESS_STREAMING);
- NinePatch ninePatch = NinePatch.load(stream, true /*is9Patch*/,
- false /* convert */);
- if (ninePatch != null) {
- if (chunk == null) {
- chunk = ninePatch.getChunk();
-
- Bridge.setCached9Patch(path, chunk,
- isFramework ? null : context.getProjectKey());
- }
-
- if (bitmap == null) {
- bitmap = Bitmap_Delegate.createBitmap(ninePatch.getImage(),
- false /*isMutable*/,
- density);
-
- Bridge.setCachedBitmap(path, bitmap,
- isFramework ? null : context.getProjectKey());
- }
- }
- } catch (MalformedURLException e) {
- // URL is wrong, we'll return null below
- }
- }
-
- if (chunk != null && bitmap != null) {
- int[] padding = chunk.getPadding();
- Rect paddingRect = new Rect(padding[0], padding[1], padding[2], padding[3]);
-
- return new NinePatchDrawable(context.getResources(), bitmap,
- NinePatch_Delegate.serialize(chunk),
- paddingRect, null);
- }
-
- return null;
- }
-
/**
* Looks for an attribute in the current theme.
*
@@ -535,6 +532,175 @@ public final class ResourceHelper {
return getBooleanThemeValue(resources, attrRef, defaultValue);
}
+ /**
+ * This takes a resource string containing HTML tags for styling,
+ * and returns it correctly formatted to be displayed.
+ */
+ public static CharSequence parseHtml(String string) {
+ // The parser requires <li> tags to be surrounded by <ul> tags to handle whitespace
+ // correctly, though Android does not support <ul> tags.
+ String str = string.replaceAll("<li>", "<ul><li>")
+ .replaceAll("</li>","</li></ul>");
+ int firstTagIndex = str.indexOf('<');
+ int lastTagIndex = str.lastIndexOf('>');
+ StringBuilder stringBuilder = new StringBuilder(str.substring(0, firstTagIndex));
+ List<Tag> tagList = new ArrayList<>();
+ Map<String, Deque<Tag>> startStacks = new HashMap<>();
+ Parser parser = new Parser();
+ parser.setContentHandler(new DefaultHandler() {
+ @Override
+ public void startElement(String uri, String localName, String qName,
+ Attributes attributes) {
+ if (!Strings.isNullOrEmpty(localName)) {
+ Tag tag = new Tag(localName);
+ tag.mStart = stringBuilder.length();
+ tag.mAttributes = attributes;
+ startStacks.computeIfAbsent(localName, key -> new ArrayDeque<>()).addFirst(tag);
+ }
+ }
+
+ @Override
+ public void endElement(String uri, String localName, String qName) {
+ if (!Strings.isNullOrEmpty(localName)) {
+ Tag tag = startStacks.get(localName).removeFirst();
+ tag.mEnd = stringBuilder.length();
+ tagList.add(tag);
+ }
+ }
+
+ @Override
+ public void characters(char[] ch, int start, int length) {
+ stringBuilder.append(ch, start, length);
+ }
+ });
+ try {
+ parser.setProperty(Parser.schemaProperty, new HTMLSchema());
+ parser.parse(new InputSource(
+ new StringReader(str.substring(firstTagIndex, lastTagIndex + 1))));
+ } catch (SAXException | IOException e) {
+ Bridge.getLog().warning(ILayoutLog.TAG_RESOURCES_FORMAT,
+ "The string " + str + " is not valid HTML", null, null);
+ return str;
+ }
+ stringBuilder.append(str.substring(lastTagIndex + 1));
+ return applyStyles(stringBuilder, tagList);
+ }
+
+ /**
+ * This applies the styles from tagList that are supported by Android
+ * and returns a {@link SpannedString}.
+ * This should mirror {@link StringBlock#applyStyles}
+ */
+ @NonNull
+ private static SpannedString applyStyles(@NonNull StringBuilder stringBuilder,
+ @NonNull List<Tag> tagList) {
+ SpannableString spannableString = new SpannableString(stringBuilder);
+ for (Tag tag : tagList) {
+ int start = tag.mStart;
+ int end = tag.mEnd;
+ Attributes attrs = tag.mAttributes;
+ switch (tag.mLabel) {
+ case "b":
+ spannableString.setSpan(new StyleSpan(Typeface.BOLD), start, end,
+ Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
+ break;
+ case "i":
+ spannableString.setSpan(new StyleSpan(Typeface.ITALIC), start, end,
+ Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
+ break;
+ case "u":
+ spannableString.setSpan(new UnderlineSpan(), start, end,
+ Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
+ break;
+ case "tt":
+ spannableString.setSpan(new TypefaceSpan("monospace"), start, end,
+ Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
+ break;
+ case "big":
+ spannableString.setSpan(new RelativeSizeSpan(1.25f), start, end,
+ Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
+ break;
+ case "small":
+ spannableString.setSpan(new RelativeSizeSpan(0.8f), start, end,
+ Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
+ break;
+ case "sup":
+ spannableString.setSpan(new SuperscriptSpan(), start, end,
+ Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
+ break;
+ case "sub":
+ spannableString.setSpan(new SubscriptSpan(), start, end,
+ Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
+ break;
+ case "strike":
+ spannableString.setSpan(new StrikethroughSpan(), start, end,
+ Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
+ break;
+ case "li":
+ StringBlock.addParagraphSpan(spannableString, new BulletSpan(10), start, end);
+ break;
+ case "marquee":
+ spannableString.setSpan(TextUtils.TruncateAt.MARQUEE, start, end,
+ Spanned.SPAN_INCLUSIVE_INCLUSIVE);
+ break;
+ case "font":
+ String heightAttr = attrs.getValue("height");
+ if (heightAttr != null) {
+ int height = Integer.parseInt(heightAttr);
+ StringBlock.addParagraphSpan(spannableString, new Height(height), start,
+ end);
+ }
+
+ String sizeAttr = attrs.getValue("size");
+ if (sizeAttr != null) {
+ int size = Integer.parseInt(sizeAttr);
+ spannableString.setSpan(new AbsoluteSizeSpan(size, true), start, end,
+ Spannable.SPAN_EXCLUSIVE_EXCLUSIVE);
+ }
+
+ String fgcolorAttr = attrs.getValue("fgcolor");
+ if (fgcolorAttr != null) {
+ spannableString.setSpan(StringBlock.getColor(fgcolorAttr, true), start, end,
+ Spannable.SPAN_EXCLUSIVE_EXCLUSIVE);
+ }
+
+ String colorAttr = attrs.getValue("color");
+ if (colorAttr != null) {
+ spannableString.setSpan(StringBlock.getColor(colorAttr, true), start, end,
+ Spannable.SPAN_EXCLUSIVE_EXCLUSIVE);
+ }
+
+ String bgcolorAttr = attrs.getValue("bgcolor");
+ if (bgcolorAttr != null) {
+ spannableString.setSpan(StringBlock.getColor(bgcolorAttr, false), start,
+ end, Spannable.SPAN_EXCLUSIVE_EXCLUSIVE);
+ }
+
+ String faceAttr = attrs.getValue("face");
+ if (faceAttr != null) {
+ spannableString.setSpan(new TypefaceSpan(faceAttr), start, end,
+ Spannable.SPAN_EXCLUSIVE_EXCLUSIVE);
+ }
+ break;
+ case "a":
+ String href = tag.mAttributes.getValue("href");
+ if (href != null) {
+ spannableString.setSpan(new URLSpan(href), start, end,
+ Spannable.SPAN_EXCLUSIVE_EXCLUSIVE);
+ }
+ break;
+ case "annotation":
+ for (int i = 0; i < attrs.getLength(); i++) {
+ String key = attrs.getLocalName(i);
+ String value = attrs.getValue(i);
+ spannableString.setSpan(new Annotation(key, value), start, end,
+ Spannable.SPAN_EXCLUSIVE_EXCLUSIVE);
+ }
+ }
+ }
+ return new SpannedString(spannableString);
+ }
+
// ------- TypedValue stuff
// This is taken from //device/libs/utils/ResourceTypes.cpp
@@ -720,5 +886,16 @@ public final class ResourceHelper {
outValue.data = unit.unit << TypedValue.COMPLEX_UNIT_SHIFT;
outScale[0] = unit.scale;
}
+
+ private static class Tag {
+ private String mLabel;
+ private int mStart;
+ private int mEnd;
+ private Attributes mAttributes;
+
+ private Tag(String label) {
+ mLabel = label;
+ }
+ }
}
diff --git a/bridge/src/com/android/layoutlib/bridge/impl/binding/AdapterHelper.java b/bridge/src/com/android/layoutlib/bridge/impl/binding/AdapterHelper.java
index b7f59c4994..640b5635cd 100644
--- a/bridge/src/com/android/layoutlib/bridge/impl/binding/AdapterHelper.java
+++ b/bridge/src/com/android/layoutlib/bridge/impl/binding/AdapterHelper.java
@@ -24,8 +24,8 @@ import com.android.ide.common.rendering.api.ResourceReference;
import com.android.layoutlib.bridge.Bridge;
import com.android.layoutlib.bridge.android.BridgeContext;
import com.android.layoutlib.bridge.impl.RenderAction;
-import com.android.utils.Pair;
+import android.util.Pair;
import android.view.View;
import android.view.ViewGroup;
import android.widget.AdapterView;
@@ -48,8 +48,8 @@ public class AdapterHelper {
Pair<View, Boolean> pair = context.inflateView(dataBindingItem.getViewReference(),
parent, false /*attachToRoot*/, skipCallbackParser);
- View view = pair.getFirst();
- skipCallbackParser |= pair.getSecond();
+ View view = pair.first;
+ skipCallbackParser |= pair.second;
if (view != null) {
fillView(context, view, item, parentItem, callback, adapterRef);
@@ -60,7 +60,7 @@ public class AdapterHelper {
view = tv;
}
- return Pair.of(view, skipCallbackParser);
+ return Pair.create(view, skipCallbackParser);
}
private static void fillView(BridgeContext context, View view, AdapterItem item,
diff --git a/bridge/src/com/android/layoutlib/bridge/impl/binding/FakeAdapter.java b/bridge/src/com/android/layoutlib/bridge/impl/binding/FakeAdapter.java
index 83ff28d05f..1c5c6a6226 100644
--- a/bridge/src/com/android/layoutlib/bridge/impl/binding/FakeAdapter.java
+++ b/bridge/src/com/android/layoutlib/bridge/impl/binding/FakeAdapter.java
@@ -20,8 +20,8 @@ import com.android.ide.common.rendering.api.AdapterBinding;
import com.android.ide.common.rendering.api.DataBindingItem;
import com.android.ide.common.rendering.api.LayoutlibCallback;
import com.android.ide.common.rendering.api.ResourceReference;
-import com.android.utils.Pair;
+import android.util.Pair;
import android.view.View;
import android.view.ViewGroup;
import android.widget.AdapterView;
@@ -112,8 +112,8 @@ public class FakeAdapter extends BaseAdapter {
AdapterItem item = mItems.get(position);
Pair<View, Boolean> pair = AdapterHelper.getView(item, null, parent, mCallback,
mAdapterRef, mSkipCallbackParser);
- mSkipCallbackParser = pair.getSecond();
- return pair.getFirst();
+ mSkipCallbackParser = pair.second;
+ return pair.first;
}
@Override
diff --git a/bridge/src/com/android/layoutlib/bridge/impl/binding/FakeExpandableAdapter.java b/bridge/src/com/android/layoutlib/bridge/impl/binding/FakeExpandableAdapter.java
index 21679f726e..1f72978aeb 100644
--- a/bridge/src/com/android/layoutlib/bridge/impl/binding/FakeExpandableAdapter.java
+++ b/bridge/src/com/android/layoutlib/bridge/impl/binding/FakeExpandableAdapter.java
@@ -20,9 +20,9 @@ import com.android.ide.common.rendering.api.AdapterBinding;
import com.android.ide.common.rendering.api.DataBindingItem;
import com.android.ide.common.rendering.api.LayoutlibCallback;
import com.android.ide.common.rendering.api.ResourceReference;
-import com.android.utils.Pair;
import android.database.DataSetObserver;
+import android.util.Pair;
import android.view.View;
import android.view.ViewGroup;
import android.widget.ExpandableListAdapter;
@@ -135,8 +135,8 @@ public class FakeExpandableAdapter implements ExpandableListAdapter, Heterogeneo
AdapterItem item = mItems.get(groupPosition);
Pair<View, Boolean> pair = AdapterHelper.getView(item, null /*parentItem*/, parent,
mCallback, mAdapterRef, mSkipCallbackParser);
- mSkipCallbackParser = pair.getSecond();
- return pair.getFirst();
+ mSkipCallbackParser = pair.second;
+ return pair.first;
}
@Override
@@ -147,8 +147,8 @@ public class FakeExpandableAdapter implements ExpandableListAdapter, Heterogeneo
AdapterItem item = getChildItem(groupPosition, childPosition);
Pair<View, Boolean> pair = AdapterHelper.getView(item, parentItem, parent, mCallback,
mAdapterRef, mSkipCallbackParser);
- mSkipCallbackParser = pair.getSecond();
- return pair.getFirst();
+ mSkipCallbackParser = pair.second;
+ return pair.first;
}
@Override
diff --git a/bridge/src/com/android/layoutlib/bridge/libcore/util/Cleaner.java b/bridge/src/com/android/layoutlib/bridge/libcore/util/Cleaner.java
index 7c95e646d0..22b3d0cea4 100644
--- a/bridge/src/com/android/layoutlib/bridge/libcore/util/Cleaner.java
+++ b/bridge/src/com/android/layoutlib/bridge/libcore/util/Cleaner.java
@@ -19,6 +19,8 @@ package com.android.layoutlib.bridge.libcore.util;
import java.lang.ref.Cleaner.Cleanable;
public class Cleaner {
+ private static final java.lang.ref.Cleaner sCleaner = java.lang.ref.Cleaner.create();
+
private final Cleanable mCleanable;
private Cleaner(Cleanable cleanable) {
@@ -37,10 +39,11 @@ public class Cleaner {
* @return The new cleaner
*/
public static Cleaner create(Object ob, Runnable thunk) {
- if (thunk == null)
+ if (thunk == null) {
return null;
- java.lang.ref.Cleaner cleaner = java.lang.ref.Cleaner.create();
- return new Cleaner(cleaner.register(ob, thunk));
+ }
+ Cleanable cleanable = sCleaner.register(ob, thunk);
+ return new Cleaner(cleanable);
}
/**
diff --git a/bridge/src/com/android/layoutlib/bridge/resources/SysUiResources.java b/bridge/src/com/android/layoutlib/bridge/resources/SysUiResources.java
index d84b411367..84ed6a04c4 100644
--- a/bridge/src/com/android/layoutlib/bridge/resources/SysUiResources.java
+++ b/bridge/src/com/android/layoutlib/bridge/resources/SysUiResources.java
@@ -31,7 +31,8 @@ import org.xmlpull.v1.XmlPullParserException;
import android.content.Context;
import android.graphics.Bitmap;
-import android.graphics.Bitmap_Delegate;
+import android.graphics.BitmapFactory;
+import android.graphics.BitmapFactory.Options;
import android.graphics.drawable.BitmapDrawable;
import android.widget.ImageView;
@@ -81,12 +82,10 @@ public class SysUiResources {
// look for a cached bitmap
Bitmap bitmap = Bridge.getCachedBitmap(path, Boolean.TRUE /*isFramework*/);
if (bitmap == null) {
- try {
- bitmap = Bitmap_Delegate.createBitmap(stream, false /*isMutable*/, density);
- Bridge.setCachedBitmap(path, bitmap, Boolean.TRUE /*isFramework*/);
- } catch (IOException e) {
- return imageView;
- }
+ Options options = new Options();
+ options.inDensity = density.getDpiValue();
+ bitmap = BitmapFactory.decodeStream(stream, null, options);
+ Bridge.setCachedBitmap(path, bitmap, Boolean.TRUE /*isFramework*/);
}
if (bitmap != null) {
diff --git a/bridge/src/com/android/layoutlib/bridge/util/CachedPathIteratorFactory.java b/bridge/src/com/android/layoutlib/bridge/util/CachedPathIteratorFactory.java
deleted file mode 100644
index 0a9b9ecde2..0000000000
--- a/bridge/src/com/android/layoutlib/bridge/util/CachedPathIteratorFactory.java
+++ /dev/null
@@ -1,485 +0,0 @@
-/*
- * Copyright (C) 2015 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.layoutlib.bridge.util;
-
-import android.annotation.NonNull;
-
-import java.awt.geom.CubicCurve2D;
-import java.awt.geom.PathIterator;
-import java.awt.geom.Point2D;
-import java.awt.geom.QuadCurve2D;
-import java.util.ArrayList;
-
-import com.google.android.collect.Lists;
-
-/**
- * Class that returns iterators for a given path. These iterators are lightweight and can be reused
- * multiple times to iterate over the path.
- */
-public class CachedPathIteratorFactory {
- /*
- * A few conventions used in the code:
- * Coordinates or coords arrays store segment coordinates. They use the same format as
- * PathIterator#currentSegment coordinates array.
- * float arrays store always points where the first element is X and the second is Y.
- */
-
- // This governs how accurate the approximation of the Path is.
- private static final float PRECISION = 0.002f;
-
- private final int mWindingRule;
- private final int[] mTypes;
- private final float[][] mCoordinates;
- private final float[] mSegmentsLength;
- private final float mTotalLength;
-
- public CachedPathIteratorFactory(@NonNull PathIterator iterator) {
- mWindingRule = iterator.getWindingRule();
-
- ArrayList<Integer> typesArray = Lists.newArrayList();
- ArrayList<float[]> pointsArray = Lists.newArrayList();
- float[] points = new float[6];
- while (!iterator.isDone()) {
- int type = iterator.currentSegment(points);
- int nPoints = getNumberOfPoints(type) * 2; // 2 coordinates per point
-
- typesArray.add(type);
- float[] itemPoints = new float[nPoints];
- System.arraycopy(points, 0, itemPoints, 0, nPoints);
- pointsArray.add(itemPoints);
- iterator.next();
- }
-
- mTypes = new int[typesArray.size()];
- mCoordinates = new float[mTypes.length][];
- for (int i = 0; i < typesArray.size(); i++) {
- mTypes[i] = typesArray.get(i);
- mCoordinates[i] = pointsArray.get(i);
- }
-
- // Do measurement
- mSegmentsLength = new float[mTypes.length];
-
- // Curves that we can reuse to estimate segments length
- CubicCurve2D.Float cubicCurve = new CubicCurve2D.Float();
- QuadCurve2D.Float quadCurve = new QuadCurve2D.Float();
- float lastX = 0;
- float lastY = 0;
- float totalLength = 0;
- for (int i = 0; i < mTypes.length; i++) {
- switch (mTypes[i]) {
- case PathIterator.SEG_CUBICTO:
- cubicCurve.setCurve(lastX, lastY,
- mCoordinates[i][0], mCoordinates[i][1], mCoordinates[i][2],
- mCoordinates[i][3], lastX = mCoordinates[i][4],
- lastY = mCoordinates[i][5]);
- mSegmentsLength[i] =
- getFlatPathLength(cubicCurve.getPathIterator(null, PRECISION));
- break;
- case PathIterator.SEG_QUADTO:
- quadCurve.setCurve(lastX, lastY, mCoordinates[i][0], mCoordinates[i][1],
- lastX = mCoordinates[i][2], lastY = mCoordinates[i][3]);
- mSegmentsLength[i] =
- getFlatPathLength(quadCurve.getPathIterator(null, PRECISION));
- break;
- case PathIterator.SEG_CLOSE:
- mSegmentsLength[i] = (float) Point2D.distance(lastX, lastY,
- lastX = mCoordinates[0][0],
- lastY = mCoordinates[0][1]);
- mCoordinates[i] = new float[2];
- // We convert a SEG_CLOSE segment to a SEG_LINETO so we do not have to worry
- // about this special case in the rest of the code.
- mTypes[i] = PathIterator.SEG_LINETO;
- mCoordinates[i][0] = mCoordinates[0][0];
- mCoordinates[i][1] = mCoordinates[0][1];
- break;
- case PathIterator.SEG_MOVETO:
- mSegmentsLength[i] = 0;
- lastX = mCoordinates[i][0];
- lastY = mCoordinates[i][1];
- break;
- case PathIterator.SEG_LINETO:
- mSegmentsLength[i] = (float) Point2D.distance(lastX, lastY, mCoordinates[i][0],
- mCoordinates[i][1]);
- lastX = mCoordinates[i][0];
- lastY = mCoordinates[i][1];
- default:
- }
- totalLength += mSegmentsLength[i];
- }
-
- mTotalLength = totalLength;
- }
-
- private static void quadCurveSegment(float[] coords, float t0, float t1) {
- // Calculate X and Y at 0.5 (We'll use this to reconstruct the control point later)
- float mt = t0 + (t1 - t0) / 2;
- float mu = 1 - mt;
- float mx = mu * mu * coords[0] + 2 * mu * mt * coords[2] + mt * mt * coords[4];
- float my = mu * mu * coords[1] + 2 * mu * mt * coords[3] + mt * mt * coords[5];
-
- float u0 = 1 - t0;
- float u1 = 1 - t1;
-
- // coords at t0
- coords[0] = coords[0] * u0 * u0 + coords[2] * 2 * t0 * u0 + coords[4] * t0 * t0;
- coords[1] = coords[1] * u0 * u0 + coords[3] * 2 * t0 * u0 + coords[5] * t0 * t0;
-
- // coords at t1
- coords[4] = coords[0] * u1 * u1 + coords[2] * 2 * t1 * u1 + coords[4] * t1 * t1;
- coords[5] = coords[1] * u1 * u1 + coords[3] * 2 * t1 * u1 + coords[5] * t1 * t1;
-
- // estimated control point at t'=0.5
- coords[2] = 2 * mx - coords[0] / 2 - coords[4] / 2;
- coords[3] = 2 * my - coords[1] / 2 - coords[5] / 2;
- }
-
- private static void cubicCurveSegment(float[] coords, float t0, float t1) {
- // http://stackoverflow.com/questions/11703283/cubic-bezier-curve-segment
- float u0 = 1 - t0;
- float u1 = 1 - t1;
-
- // Calculate the points at t0 and t1 for the quadratic curves formed by (P0, P1, P2) and
- // (P1, P2, P3)
- float qxa = coords[0] * u0 * u0 + coords[2] * 2 * t0 * u0 + coords[4] * t0 * t0;
- float qxb = coords[0] * u1 * u1 + coords[2] * 2 * t1 * u1 + coords[4] * t1 * t1;
- float qxc = coords[2] * u0 * u0 + coords[4] * 2 * t0 * u0 + coords[6] * t0 * t0;
- float qxd = coords[2] * u1 * u1 + coords[4] * 2 * t1 * u1 + coords[6] * t1 * t1;
-
- float qya = coords[1] * u0 * u0 + coords[3] * 2 * t0 * u0 + coords[5] * t0 * t0;
- float qyb = coords[1] * u1 * u1 + coords[3] * 2 * t1 * u1 + coords[5] * t1 * t1;
- float qyc = coords[3] * u0 * u0 + coords[5] * 2 * t0 * u0 + coords[7] * t0 * t0;
- float qyd = coords[3] * u1 * u1 + coords[5] * 2 * t1 * u1 + coords[7] * t1 * t1;
-
- // Linear interpolation
- coords[0] = qxa * u0 + qxc * t0;
- coords[1] = qya * u0 + qyc * t0;
-
- coords[2] = qxa * u1 + qxc * t1;
- coords[3] = qya * u1 + qyc * t1;
-
- coords[4] = qxb * u0 + qxd * t0;
- coords[5] = qyb * u0 + qyd * t0;
-
- coords[6] = qxb * u1 + qxd * t1;
- coords[7] = qyb * u1 + qyd * t1;
- }
-
- /**
- * Returns the end point of a given segment
- *
- * @param type the segment type
- * @param coords the segment coordinates array
- * @param point the return array where the point will be stored
- */
- private static void getShapeEndPoint(int type, @NonNull float[] coords, @NonNull float[]
- point) {
- // start index of the end point for the segment type
- int pointIndex = (getNumberOfPoints(type) - 1) * 2;
- point[0] = coords[pointIndex];
- point[1] = coords[pointIndex + 1];
- }
-
- /**
- * Returns the number of points stored in a coordinates array for the given segment type.
- */
- private static int getNumberOfPoints(int segmentType) {
- switch (segmentType) {
- case PathIterator.SEG_QUADTO:
- return 2;
- case PathIterator.SEG_CUBICTO:
- return 3;
- case PathIterator.SEG_CLOSE:
- return 0;
- default:
- return 1;
- }
- }
-
- /**
- * Returns the estimated length of a flat path. If the passed path is not flat (i.e. contains a
- * segment that is not {@link PathIterator#SEG_CLOSE}, {@link PathIterator#SEG_MOVETO} or {@link
- * PathIterator#SEG_LINETO} this method will fail.
- */
- private static float getFlatPathLength(@NonNull PathIterator iterator) {
- float segment[] = new float[6];
- float totalLength = 0;
- float[] previousPoint = new float[2];
- boolean isFirstPoint = true;
-
- while (!iterator.isDone()) {
- int type = iterator.currentSegment(segment);
- assert type == PathIterator.SEG_LINETO || type == PathIterator.SEG_CLOSE || type ==
- PathIterator.SEG_MOVETO;
-
- // MoveTo shouldn't affect the length
- if (!isFirstPoint && type != PathIterator.SEG_MOVETO) {
- totalLength += Point2D.distance(previousPoint[0], previousPoint[1], segment[0],
- segment[1]);
- } else {
- isFirstPoint = false;
- }
- previousPoint[0] = segment[0];
- previousPoint[1] = segment[1];
- iterator.next();
- }
-
- return totalLength;
- }
-
- /**
- * Returns the estimated position along a path of the given length.
- */
- private void getPointAtLength(int type, @NonNull float[] coords, float lastX, float
- lastY, float t, @NonNull float[] point) {
- if (type == PathIterator.SEG_LINETO) {
- point[0] = lastX + (coords[0] - lastX) * t;
- point[1] = lastY + (coords[1] - lastY) * t;
- // Return here, since we do not need a shape to estimate
- return;
- }
-
- float[] curve = new float[8];
- int lastPointIndex = (getNumberOfPoints(type) - 1) * 2;
-
- System.arraycopy(coords, 0, curve, 2, coords.length);
- curve[0] = lastX;
- curve[1] = lastY;
- if (type == PathIterator.SEG_CUBICTO) {
- cubicCurveSegment(curve, 0f, t);
- } else {
- quadCurveSegment(curve, 0f, t);
- }
-
- point[0] = curve[2 + lastPointIndex];
- point[1] = curve[2 + lastPointIndex + 1];
- }
-
- public CachedPathIterator iterator() {
- return new CachedPathIterator();
- }
-
- /**
- * Class that allows us to iterate over a path multiple times
- */
- public class CachedPathIterator implements PathIterator {
- private int mNextIndex;
-
- /**
- * Current segment type.
- *
- * @see PathIterator
- */
- private int mCurrentType;
-
- /**
- * Stores the coordinates array of the current segment. The number of points stored depends
- * on the segment type.
- *
- * @see PathIterator
- */
- private float[] mCurrentCoords = new float[6];
- private float mCurrentSegmentLength;
-
- /**
- * Current segment length offset. When asking for the length of the current segment, the
- * length will be reduced by this amount. This is useful when we are only using portions of
- * the segment.
- *
- * @see #jumpToSegment(float)
- */
- private float mOffsetLength;
-
- /** Point where the current segment started */
- private float[] mLastPoint = new float[2];
- private boolean isIteratorDone;
-
- private CachedPathIterator() {
- next();
- }
-
- public float getCurrentSegmentLength() {
- return mCurrentSegmentLength;
- }
-
- @Override
- public int getWindingRule() {
- return mWindingRule;
- }
-
- @Override
- public boolean isDone() {
- return isIteratorDone;
- }
-
- @Override
- public void next() {
- if (mNextIndex >= mTypes.length) {
- isIteratorDone = true;
- return;
- }
-
- if (mNextIndex >= 1) {
- // We've already called next() once so there is a previous segment in this path.
- // We want to get the coordinates where the path ends.
- getShapeEndPoint(mCurrentType, mCurrentCoords, mLastPoint);
- } else {
- // This is the first segment, no previous point so initialize to 0, 0
- mLastPoint[0] = mLastPoint[1] = 0f;
- }
- mCurrentType = mTypes[mNextIndex];
- mCurrentSegmentLength = mSegmentsLength[mNextIndex] - mOffsetLength;
-
- if (mOffsetLength > 0f && (mCurrentType == SEG_CUBICTO || mCurrentType == SEG_QUADTO)) {
- // We need to skip part of the start of the current segment (because
- // mOffsetLength > 0)
- float[] points = new float[8];
-
- if (mNextIndex < 1) {
- points[0] = points[1] = 0f;
- } else {
- getShapeEndPoint(mTypes[mNextIndex - 1], mCoordinates[mNextIndex - 1], points);
- }
-
- System.arraycopy(mCoordinates[mNextIndex], 0, points, 2,
- mCoordinates[mNextIndex].length);
- float t0 = (mSegmentsLength[mNextIndex] - mCurrentSegmentLength) /
- mSegmentsLength[mNextIndex];
- if (mCurrentType == SEG_CUBICTO) {
- cubicCurveSegment(points, t0, 1f);
- } else {
- quadCurveSegment(points, t0, 1f);
- }
- System.arraycopy(points, 2, mCurrentCoords, 0, mCoordinates[mNextIndex].length);
- } else {
- System.arraycopy(mCoordinates[mNextIndex], 0, mCurrentCoords, 0,
- mCoordinates[mNextIndex].length);
- }
-
- mOffsetLength = 0f;
- mNextIndex++;
- }
-
- @Override
- public int currentSegment(float[] coords) {
- System.arraycopy(mCurrentCoords, 0, coords, 0, getNumberOfPoints(mCurrentType) * 2);
- return mCurrentType;
- }
-
- @Override
- public int currentSegment(double[] coords) {
- throw new UnsupportedOperationException();
- }
-
- /**
- * Returns the point where the current segment ends
- */
- public void getCurrentSegmentEnd(float[] point) {
- point[0] = mLastPoint[0];
- point[1] = mLastPoint[1];
- }
-
- /**
- * Restarts the iterator and jumps all the segments of this path up to the length value.
- */
- public void jumpToSegment(float length) {
- isIteratorDone = false;
- if (length <= 0f) {
- mNextIndex = 0;
- return;
- }
-
- float accLength = 0;
- float lastPoint[] = new float[2];
- for (mNextIndex = 0; mNextIndex < mTypes.length; mNextIndex++) {
- float segmentLength = mSegmentsLength[mNextIndex];
- if (accLength + segmentLength >= length && mTypes[mNextIndex] != SEG_MOVETO) {
- float[] estimatedPoint = new float[2];
- getPointAtLength(mTypes[mNextIndex],
- mCoordinates[mNextIndex], lastPoint[0], lastPoint[1],
- (length - accLength) / segmentLength,
- estimatedPoint);
-
- // This segment makes us go further than length so we go back one step,
- // set a moveto and offset the length of the next segment by the length
- // of this segment that we've already used.
- mCurrentType = PathIterator.SEG_MOVETO;
- mCurrentCoords[0] = estimatedPoint[0];
- mCurrentCoords[1] = estimatedPoint[1];
- mCurrentSegmentLength = 0;
-
- // We need to offset next path length to account for the segment we've just
- // skipped.
- mOffsetLength = length - accLength;
- return;
- }
- accLength += segmentLength;
- getShapeEndPoint(mTypes[mNextIndex], mCoordinates[mNextIndex], lastPoint);
- }
- }
-
- /**
- * Returns the current segment up to certain length. If the current segment is shorter than
- * length, then the whole segment is returned. The segment coordinates are copied into the
- * coords array.
- *
- * @return the segment type
- */
- public int currentSegment(@NonNull float[] coords, float length) {
- int type = currentSegment(coords);
- // If the length is greater than the current segment length, no need to find
- // the cut point. Same if this is a SEG_MOVETO.
- if (mCurrentSegmentLength <= length || type == SEG_MOVETO) {
- return type;
- }
-
- float t = length / getCurrentSegmentLength();
-
- // We find at which offset the end point is located within the coords array and set
- // a new end point to cut the segment short
- switch (type) {
- case SEG_CUBICTO:
- case SEG_QUADTO:
- float[] curve = new float[8];
- curve[0] = mLastPoint[0];
- curve[1] = mLastPoint[1];
- System.arraycopy(coords, 0, curve, 2, coords.length);
- if (type == SEG_CUBICTO) {
- cubicCurveSegment(curve, 0f, t);
- } else {
- quadCurveSegment(curve, 0f, t);
- }
- System.arraycopy(curve, 2, coords, 0, coords.length);
- break;
- default:
- float[] point = new float[2];
- getPointAtLength(type, coords, mLastPoint[0], mLastPoint[1], t, point);
- coords[0] = point[0];
- coords[1] = point[1];
- }
-
- return type;
- }
-
- /**
- * Returns the total length of the path
- */
- public float getTotalLength() {
- return mTotalLength;
- }
- }
-}
diff --git a/bridge/src/com/android/layoutlib/bridge/util/ChoreographerCallbacks.java b/bridge/src/com/android/layoutlib/bridge/util/ChoreographerCallbacks.java
new file mode 100644
index 0000000000..9d5830b4de
--- /dev/null
+++ b/bridge/src/com/android/layoutlib/bridge/util/ChoreographerCallbacks.java
@@ -0,0 +1,112 @@
+/*
+ * 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.layoutlib.bridge.util;
+
+import com.android.ide.common.rendering.api.ILayoutLog;
+import com.android.tools.layoutlib.annotations.NotNull;
+
+import android.os.SystemClock_Delegate;
+import android.util.Pair;
+import android.util.TimeUtils;
+import android.view.Choreographer.FrameCallback;
+
+import java.util.ArrayList;
+import java.util.List;
+import java.util.concurrent.locks.ReentrantReadWriteLock;
+
+/**
+ * Manages {@link android.view.Choreographer} callbacks. Keeps track of the currently active
+ * callbacks and allows to execute callbacks if their time is due.
+ */
+public class ChoreographerCallbacks {
+ // Simple wrapper around ArrayList to be able to use protected removeRange method
+ private static class RangeList<T> extends ArrayList<T> {
+ private void removeFrontElements(int n) {
+ removeRange(0, n);
+ }
+ }
+
+ private final RangeList<Pair<Object, Long>> mCallbacks = new RangeList<>();
+
+ public void add(Object action, long delayMillis) {
+ synchronized (mCallbacks) {
+ int idx = 0;
+ final long now = SystemClock_Delegate.uptimeMillis();
+ final long dueTime = now + delayMillis;
+ while (idx < mCallbacks.size()) {
+ if (mCallbacks.get(idx).second > dueTime) {
+ break;
+ } else {
+ ++idx;
+ }
+ }
+ mCallbacks.add(idx, Pair.create(action, dueTime));
+ }
+ }
+
+ public void remove(Object action) {
+ synchronized (mCallbacks) {
+ mCallbacks.removeIf(el -> el.first == action);
+ }
+ }
+
+ public void execute(long currentTimeMs, @NotNull ILayoutLog logger) {
+ final long currentTimeNanos = currentTimeMs * TimeUtils.NANOS_PER_MS;
+ List<Pair<Object, Long>> toExecute;
+ synchronized (mCallbacks) {
+ int idx = 0;
+ while (idx < mCallbacks.size()) {
+ if (mCallbacks.get(idx).second > currentTimeMs) {
+ break;
+ } else {
+ ++idx;
+ }
+ }
+ toExecute = new ArrayList<>(mCallbacks.subList(0, idx));
+ mCallbacks.removeFrontElements(idx);
+ }
+
+ // We run the callbacks outside of the synchronized block to avoid deadlocks caused by
+ // callbacks calling back into ChoreographerCallbacks.
+ toExecute.forEach(p -> executeSafely(p.first, currentTimeNanos, logger));
+ }
+
+ public void clear() {
+ synchronized (mCallbacks) {
+ mCallbacks.clear();
+ }
+ }
+
+ private static void executeSafely(@NotNull Object action, long frameTimeNanos,
+ @NotNull ILayoutLog logger) {
+ try {
+ if (action instanceof FrameCallback) {
+ FrameCallback callback = (FrameCallback) action;
+ callback.doFrame(frameTimeNanos);
+ } else if (action instanceof Runnable) {
+ Runnable runnable = (Runnable) action;
+ runnable.run();
+ } else {
+ logger.error(ILayoutLog.TAG_BROKEN,
+ "Unexpected action as Choreographer callback", (Object) null, null);
+ }
+ } catch (Throwable t) {
+ logger.error(ILayoutLog.TAG_BROKEN, "Failed executing Choreographer callback", t,
+ null, null);
+ }
+ }
+}
diff --git a/bridge/src/com/android/layoutlib/bridge/util/DynamicIdMap.java b/bridge/src/com/android/layoutlib/bridge/util/DynamicIdMap.java
index 16aa058b76..4a5c6e6e2b 100644
--- a/bridge/src/com/android/layoutlib/bridge/util/DynamicIdMap.java
+++ b/bridge/src/com/android/layoutlib/bridge/util/DynamicIdMap.java
@@ -17,9 +17,9 @@
package com.android.layoutlib.bridge.util;
import com.android.resources.ResourceType;
-import com.android.utils.Pair;
import android.annotation.NonNull;
+import android.util.Pair;
import android.util.SparseArray;
import java.util.HashMap;
@@ -51,7 +51,7 @@ public class DynamicIdMap {
*/
@NonNull
public Integer getId(ResourceType type, String name) {
- return getId(Pair.of(type, name));
+ return getId(Pair.create(type, name));
}
/**
diff --git a/bridge/src/com/android/layoutlib/bridge/util/HandlerMessageQueue.java b/bridge/src/com/android/layoutlib/bridge/util/HandlerMessageQueue.java
new file mode 100644
index 0000000000..e1737177c7
--- /dev/null
+++ b/bridge/src/com/android/layoutlib/bridge/util/HandlerMessageQueue.java
@@ -0,0 +1,110 @@
+/*
+ * 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.layoutlib.bridge.util;
+
+import com.android.tools.layoutlib.annotations.NotNull;
+import com.android.tools.layoutlib.annotations.Nullable;
+
+import android.os.Handler;
+import android.util.Pair;
+
+import java.util.LinkedList;
+import java.util.WeakHashMap;
+
+/**
+ * A queue that stores {@link Runnable}s associated with the corresponding {@link Handler}s.
+ * {@link Runnable}s get automatically released when the corresponding {@link Handler} gets
+ * collected by the garbage collector. All {@link Runnable}s are queued in a single virtual queue
+ * with respect to their corresponding uptime (the time when they should be executed).
+ */
+public class HandlerMessageQueue {
+ private final WeakHashMap<Handler, LinkedList<Pair<Long, Runnable>>> runnablesMap =
+ new WeakHashMap<>();
+
+ /**
+ * Adds a {@link Runnable} associated with the {@link Handler} to be executed at
+ * particular time
+ * @param h handler associated with the {@link Runnable}
+ * @param uptimeMillis time in milliseconds the {@link Runnable} to be executed
+ * @param r {@link Runnable} to be added
+ */
+ public void add(@NotNull Handler h, long uptimeMillis, @NotNull Runnable r) {
+ LinkedList<Pair<Long, Runnable>> runnables = runnablesMap.computeIfAbsent(h,
+ k -> new LinkedList<>());
+
+ int idx = 0;
+ while (idx < runnables.size()) {
+ if (runnables.get(idx).first <= uptimeMillis) {
+ idx++;
+ } else {
+ break;
+ }
+ }
+ runnables.add(idx, Pair.create(uptimeMillis, r));
+ }
+
+ private static class HandlerWrapper {
+ public Handler handler;
+ }
+
+ /**
+ * Removes from the queue and returns the {@link Runnable} with the smallest uptime if it
+ * is less than the one passed as a parameter or null if such runnable does not exist.
+ * @param uptimeMillis
+ * @return the {@link Runnable} from the queue
+ */
+ @Nullable
+ public Runnable extractFirst(long uptimeMillis) {
+ final HandlerWrapper w = new HandlerWrapper();
+ runnablesMap.forEach((h, l) -> {
+ if (!l.isEmpty()) {
+ long currentUptime = l.getFirst().first;
+ if (currentUptime <= uptimeMillis) {
+ if (w.handler == null || currentUptime <
+ runnablesMap.get(w.handler).getFirst().first) {
+ w.handler = h;
+ }
+ }
+ }
+ });
+ if (w.handler != null) {
+ return runnablesMap.get(w.handler).pollFirst().second;
+ }
+ return null;
+ }
+
+ /**
+ * @return true is queue has no runnables left
+ */
+ public boolean isNotEmpty() {
+ return runnablesMap.values().stream().anyMatch(l -> !l.isEmpty());
+ }
+
+ /**
+ * @return number of runnables in the queue
+ */
+ public int size() {
+ return runnablesMap.values().stream().mapToInt(LinkedList::size).sum();
+ }
+
+ /**
+ * Completely clears the entire queue
+ */
+ public void clear() {
+ runnablesMap.clear();
+ }
+}
diff --git a/bridge/src/com/android/layoutlib/bridge/util/NinePatchInputStream.java b/bridge/src/com/android/layoutlib/bridge/util/NinePatchInputStream.java
index 75e4a2b2bf..a50090bc30 100644
--- a/bridge/src/com/android/layoutlib/bridge/util/NinePatchInputStream.java
+++ b/bridge/src/com/android/layoutlib/bridge/util/NinePatchInputStream.java
@@ -33,13 +33,21 @@ import java.io.InputStream;
public class NinePatchInputStream extends InputStream {
private final InputStream mDelegate;
private boolean mFakeMarkSupport = true;
+ private final String mPath;
public NinePatchInputStream(File file) throws FileNotFoundException {
mDelegate = new FileInputStream(file);
+ mPath = file.getPath();
}
- public NinePatchInputStream(@NotNull InputStream stream) {
+ public NinePatchInputStream(@NotNull InputStream stream, @NotNull String path) {
mDelegate = stream;
+ mPath = path;
+ }
+
+ @NotNull
+ public String getPath() {
+ return mPath;
}
@Override
diff --git a/bridge/src/dalvik/system/VMRuntime_Delegate.java b/bridge/src/dalvik/system/VMRuntime_Delegate.java
index 648d2be78c..2fe10154b8 100644
--- a/bridge/src/dalvik/system/VMRuntime_Delegate.java
+++ b/bridge/src/dalvik/system/VMRuntime_Delegate.java
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2014 The Android Open Source Project
+ * Copyright (C) 2019 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -26,59 +26,14 @@ import com.android.tools.layoutlib.annotations.LayoutlibDelegate;
*/
public class VMRuntime_Delegate {
- // Copied from libcore/libdvm/src/main/java/dalvik/system/VMRuntime
@LayoutlibDelegate
/*package*/ static Object newUnpaddedArray(VMRuntime runtime, Class<?> componentType,
int minLength) {
- // Dalvik has 32bit pointers, the array header is 16bytes plus 4bytes for dlmalloc,
- // allocations are 8byte aligned so having 4bytes of array data avoids padding.
- if (!componentType.isPrimitive()) {
- int size = ((minLength & 1) == 0) ? minLength + 1 : minLength;
- return java.lang.reflect.Array.newInstance(componentType, size);
- } else if (componentType == char.class) {
- int bytes = 20 + (2 * minLength);
- int alignedUpBytes = (bytes + 7) & -8;
- int dataBytes = alignedUpBytes - 20;
- int size = dataBytes / 2;
- return new char[size];
- } else if (componentType == int.class) {
- int size = ((minLength & 1) == 0) ? minLength + 1 : minLength;
- return new int[size];
- } else if (componentType == byte.class) {
- int bytes = 20 + minLength;
- int alignedUpBytes = (bytes + 7) & -8;
- int dataBytes = alignedUpBytes - 20;
- int size = dataBytes;
- return new byte[size];
- } else if (componentType == boolean.class) {
- int bytes = 20 + minLength;
- int alignedUpBytes = (bytes + 7) & -8;
- int dataBytes = alignedUpBytes - 20;
- int size = dataBytes;
- return new boolean[size];
- } else if (componentType == short.class) {
- int bytes = 20 + (2 * minLength);
- int alignedUpBytes = (bytes + 7) & -8;
- int dataBytes = alignedUpBytes - 20;
- int size = dataBytes / 2;
- return new short[size];
- } else if (componentType == float.class) {
- int size = ((minLength & 1) == 0) ? minLength + 1 : minLength;
- return new float[size];
- } else if (componentType == long.class) {
- return new long[minLength];
- } else if (componentType == double.class) {
- return new double[minLength];
- } else {
- assert componentType == void.class;
- throw new IllegalArgumentException("Can't allocate an array of void");
- }
+ return VMRuntimeCommonHelper.newUnpaddedArray(runtime, componentType, minLength);
}
@LayoutlibDelegate
/*package*/ static int getNotifyNativeInterval() {
- // This cannot return 0, otherwise it is responsible for triggering an exception
- // whenever trying to use a NativeAllocationRegistry with size 0
- return 1;
+ return VMRuntimeCommonHelper.getNotifyNativeInterval();
}
}
diff --git a/bridge/src/libcore/util/NativeAllocationRegistry_Delegate.java b/bridge/src/libcore/util/NativeAllocationRegistry_Delegate.java
index 141c72884a..857b4fec6f 100644
--- a/bridge/src/libcore/util/NativeAllocationRegistry_Delegate.java
+++ b/bridge/src/libcore/util/NativeAllocationRegistry_Delegate.java
@@ -51,22 +51,6 @@ public class NativeAllocationRegistry_Delegate {
}
@LayoutlibDelegate
- /*package*/ static void registerNativeAllocation(long size) {
- NativeAllocationRegistry.registerNativeAllocation_Original(size);
- }
-
- @LayoutlibDelegate
- /*package*/ static Runnable registerNativeAllocation(NativeAllocationRegistry registry,
- Object referent,
- long nativePtr) {
- // Mark the object as already "natively" tracked.
- // This allows the DelegateManager to dispose objects without waiting
- // for an explicit call when the referent does not exist anymore.
- sManager.markAsNativeAllocation(referent, nativePtr);
- return registry.registerNativeAllocation_Original(referent, nativePtr);
- }
-
- @LayoutlibDelegate
/*package*/ static void applyFreeFunction(long freeFunction, long nativePtr) {
// This method MIGHT run in the context of the finalizer thread. If the delegate method
// crashes, it could bring down the VM. That's why we catch all the exceptions and ignore
@@ -75,6 +59,8 @@ public class NativeAllocationRegistry_Delegate {
NativeAllocationRegistry_Delegate delegate = sManager.getDelegate(freeFunction);
if (delegate != null) {
delegate.mFinalizer.free(nativePtr);
+ } else if (freeFunction != 0) {
+ nativeApplyFreeFunction(freeFunction, nativePtr);
}
} catch (Throwable ignore) {
}
@@ -83,4 +69,6 @@ public class NativeAllocationRegistry_Delegate {
public interface FreeFunction {
void free(long nativePtr);
}
+
+ private static native void nativeApplyFreeFunction(long freeFunction, long nativePtr);
}
diff --git a/bridge/tests/Android.bp b/bridge/tests/Android.bp
index 7beb94d8a8..4aec3ad213 100644
--- a/bridge/tests/Android.bp
+++ b/bridge/tests/Android.bp
@@ -31,6 +31,7 @@ java_test_host {
"kxml2-2.3.0",
"layoutlib_api-prebuilt",
"tools-common-prebuilt",
+ "ninepatch-prebuilt",
"sdk-common",
"junit",
"guava",
diff --git a/bridge/tests/res/com/android/layoutlib/testdata/compiled.9.png b/bridge/tests/res/com/android/layoutlib/testdata/compiled.9.png
new file mode 100644
index 0000000000..a5f5f3bba5
--- /dev/null
+++ b/bridge/tests/res/com/android/layoutlib/testdata/compiled.9.png
Binary files differ
diff --git a/bridge/tests/res/com/android/layoutlib/testdata/non_compiled.9.png b/bridge/tests/res/com/android/layoutlib/testdata/non_compiled.9.png
new file mode 100644
index 0000000000..9d52f40d00
--- /dev/null
+++ b/bridge/tests/res/com/android/layoutlib/testdata/non_compiled.9.png
Binary files differ
diff --git a/bridge/tests/res/testApp/MyApplication/build/intermediates/javac/debug/compileDebugJavaWithJavac/classes/com/android/layoutlib/test/myapplication/R$drawable.class b/bridge/tests/res/testApp/MyApplication/build/intermediates/javac/debug/compileDebugJavaWithJavac/classes/com/android/layoutlib/test/myapplication/R$drawable.class
index 5ac9e72a18..0292770afa 100644
--- a/bridge/tests/res/testApp/MyApplication/build/intermediates/javac/debug/compileDebugJavaWithJavac/classes/com/android/layoutlib/test/myapplication/R$drawable.class
+++ b/bridge/tests/res/testApp/MyApplication/build/intermediates/javac/debug/compileDebugJavaWithJavac/classes/com/android/layoutlib/test/myapplication/R$drawable.class
Binary files differ
diff --git a/bridge/tests/res/testApp/MyApplication/build/intermediates/javac/debug/compileDebugJavaWithJavac/classes/com/android/layoutlib/test/myapplication/R$font.class b/bridge/tests/res/testApp/MyApplication/build/intermediates/javac/debug/compileDebugJavaWithJavac/classes/com/android/layoutlib/test/myapplication/R$font.class
index 6fe46c5901..5a596a27d8 100644
--- a/bridge/tests/res/testApp/MyApplication/build/intermediates/javac/debug/compileDebugJavaWithJavac/classes/com/android/layoutlib/test/myapplication/R$font.class
+++ b/bridge/tests/res/testApp/MyApplication/build/intermediates/javac/debug/compileDebugJavaWithJavac/classes/com/android/layoutlib/test/myapplication/R$font.class
Binary files differ
diff --git a/bridge/tests/res/testApp/MyApplication/build/intermediates/javac/debug/compileDebugJavaWithJavac/classes/com/android/layoutlib/test/myapplication/R$id.class b/bridge/tests/res/testApp/MyApplication/build/intermediates/javac/debug/compileDebugJavaWithJavac/classes/com/android/layoutlib/test/myapplication/R$id.class
index 15e3e525a7..aafc5b4e34 100644
--- a/bridge/tests/res/testApp/MyApplication/build/intermediates/javac/debug/compileDebugJavaWithJavac/classes/com/android/layoutlib/test/myapplication/R$id.class
+++ b/bridge/tests/res/testApp/MyApplication/build/intermediates/javac/debug/compileDebugJavaWithJavac/classes/com/android/layoutlib/test/myapplication/R$id.class
Binary files differ
diff --git a/bridge/tests/res/testApp/MyApplication/build/intermediates/javac/debug/compileDebugJavaWithJavac/classes/com/android/layoutlib/test/myapplication/R$integer.class b/bridge/tests/res/testApp/MyApplication/build/intermediates/javac/debug/compileDebugJavaWithJavac/classes/com/android/layoutlib/test/myapplication/R$integer.class
index 64df0c3b04..def2f600da 100644
--- a/bridge/tests/res/testApp/MyApplication/build/intermediates/javac/debug/compileDebugJavaWithJavac/classes/com/android/layoutlib/test/myapplication/R$integer.class
+++ b/bridge/tests/res/testApp/MyApplication/build/intermediates/javac/debug/compileDebugJavaWithJavac/classes/com/android/layoutlib/test/myapplication/R$integer.class
Binary files differ
diff --git a/bridge/tests/res/testApp/MyApplication/build/intermediates/javac/debug/compileDebugJavaWithJavac/classes/com/android/layoutlib/test/myapplication/R$layout.class b/bridge/tests/res/testApp/MyApplication/build/intermediates/javac/debug/compileDebugJavaWithJavac/classes/com/android/layoutlib/test/myapplication/R$layout.class
index c0bb96d177..6786cf29cb 100644
--- a/bridge/tests/res/testApp/MyApplication/build/intermediates/javac/debug/compileDebugJavaWithJavac/classes/com/android/layoutlib/test/myapplication/R$layout.class
+++ b/bridge/tests/res/testApp/MyApplication/build/intermediates/javac/debug/compileDebugJavaWithJavac/classes/com/android/layoutlib/test/myapplication/R$layout.class
Binary files differ
diff --git a/bridge/tests/res/testApp/MyApplication/build/intermediates/javac/debug/compileDebugJavaWithJavac/classes/com/android/layoutlib/test/myapplication/R$menu.class b/bridge/tests/res/testApp/MyApplication/build/intermediates/javac/debug/compileDebugJavaWithJavac/classes/com/android/layoutlib/test/myapplication/R$menu.class
index afeb35780b..252030e0ec 100644
--- a/bridge/tests/res/testApp/MyApplication/build/intermediates/javac/debug/compileDebugJavaWithJavac/classes/com/android/layoutlib/test/myapplication/R$menu.class
+++ b/bridge/tests/res/testApp/MyApplication/build/intermediates/javac/debug/compileDebugJavaWithJavac/classes/com/android/layoutlib/test/myapplication/R$menu.class
Binary files differ
diff --git a/bridge/tests/res/testApp/MyApplication/build/intermediates/javac/debug/compileDebugJavaWithJavac/classes/com/android/layoutlib/test/myapplication/R$string.class b/bridge/tests/res/testApp/MyApplication/build/intermediates/javac/debug/compileDebugJavaWithJavac/classes/com/android/layoutlib/test/myapplication/R$string.class
index c8d5e0b5a8..823ba90fd1 100644
--- a/bridge/tests/res/testApp/MyApplication/build/intermediates/javac/debug/compileDebugJavaWithJavac/classes/com/android/layoutlib/test/myapplication/R$string.class
+++ b/bridge/tests/res/testApp/MyApplication/build/intermediates/javac/debug/compileDebugJavaWithJavac/classes/com/android/layoutlib/test/myapplication/R$string.class
Binary files differ
diff --git a/bridge/tests/res/testApp/MyApplication/build/intermediates/javac/debug/compileDebugJavaWithJavac/classes/com/android/layoutlib/test/myapplication/R$style.class b/bridge/tests/res/testApp/MyApplication/build/intermediates/javac/debug/compileDebugJavaWithJavac/classes/com/android/layoutlib/test/myapplication/R$style.class
index ccfecc84e8..4dd0af6439 100644
--- a/bridge/tests/res/testApp/MyApplication/build/intermediates/javac/debug/compileDebugJavaWithJavac/classes/com/android/layoutlib/test/myapplication/R$style.class
+++ b/bridge/tests/res/testApp/MyApplication/build/intermediates/javac/debug/compileDebugJavaWithJavac/classes/com/android/layoutlib/test/myapplication/R$style.class
Binary files differ
diff --git a/bridge/tests/res/testApp/MyApplication/build/intermediates/javac/debug/compileDebugJavaWithJavac/classes/com/android/layoutlib/test/myapplication/ThemableWidget.class b/bridge/tests/res/testApp/MyApplication/build/intermediates/javac/debug/compileDebugJavaWithJavac/classes/com/android/layoutlib/test/myapplication/ThemableWidget.class
index 5171dadef4..5500abe3bd 100644
--- a/bridge/tests/res/testApp/MyApplication/build/intermediates/javac/debug/compileDebugJavaWithJavac/classes/com/android/layoutlib/test/myapplication/ThemableWidget.class
+++ b/bridge/tests/res/testApp/MyApplication/build/intermediates/javac/debug/compileDebugJavaWithJavac/classes/com/android/layoutlib/test/myapplication/ThemableWidget.class
Binary files differ
diff --git a/bridge/tests/res/testApp/MyApplication/golden-mac/a11y_test1.png b/bridge/tests/res/testApp/MyApplication/golden-mac/a11y_test1.png
new file mode 100644
index 0000000000..067a19363e
--- /dev/null
+++ b/bridge/tests/res/testApp/MyApplication/golden-mac/a11y_test1.png
Binary files differ
diff --git a/bridge/tests/res/testApp/MyApplication/golden-mac/activity.png b/bridge/tests/res/testApp/MyApplication/golden-mac/activity.png
new file mode 100644
index 0000000000..0dbe45fa86
--- /dev/null
+++ b/bridge/tests/res/testApp/MyApplication/golden-mac/activity.png
Binary files differ
diff --git a/bridge/tests/res/testApp/MyApplication/golden-mac/adaptive_icon.png b/bridge/tests/res/testApp/MyApplication/golden-mac/adaptive_icon.png
new file mode 100644
index 0000000000..efcd49e750
--- /dev/null
+++ b/bridge/tests/res/testApp/MyApplication/golden-mac/adaptive_icon.png
Binary files differ
diff --git a/bridge/tests/res/testApp/MyApplication/golden-mac/adaptive_icon_circle.png b/bridge/tests/res/testApp/MyApplication/golden-mac/adaptive_icon_circle.png
new file mode 100644
index 0000000000..f85ef1b7fe
--- /dev/null
+++ b/bridge/tests/res/testApp/MyApplication/golden-mac/adaptive_icon_circle.png
Binary files differ
diff --git a/bridge/tests/res/testApp/MyApplication/golden-mac/adaptive_icon_rounded_corners.png b/bridge/tests/res/testApp/MyApplication/golden-mac/adaptive_icon_rounded_corners.png
new file mode 100644
index 0000000000..3bb70551c2
--- /dev/null
+++ b/bridge/tests/res/testApp/MyApplication/golden-mac/adaptive_icon_rounded_corners.png
Binary files differ
diff --git a/bridge/tests/res/testApp/MyApplication/golden-mac/adaptive_icon_squircle.png b/bridge/tests/res/testApp/MyApplication/golden-mac/adaptive_icon_squircle.png
new file mode 100644
index 0000000000..f3b3a08d5d
--- /dev/null
+++ b/bridge/tests/res/testApp/MyApplication/golden-mac/adaptive_icon_squircle.png
Binary files differ
diff --git a/bridge/tests/res/testApp/MyApplication/golden-mac/allwidgets.png b/bridge/tests/res/testApp/MyApplication/golden-mac/allwidgets.png
new file mode 100644
index 0000000000..d99df26898
--- /dev/null
+++ b/bridge/tests/res/testApp/MyApplication/golden-mac/allwidgets.png
Binary files differ
diff --git a/bridge/tests/res/testApp/MyApplication/golden-mac/allwidgets_tab.png b/bridge/tests/res/testApp/MyApplication/golden-mac/allwidgets_tab.png
new file mode 100644
index 0000000000..5ddd9d2e85
--- /dev/null
+++ b/bridge/tests/res/testApp/MyApplication/golden-mac/allwidgets_tab.png
Binary files differ
diff --git a/bridge/tests/res/testApp/MyApplication/golden-mac/animated_vector.png b/bridge/tests/res/testApp/MyApplication/golden-mac/animated_vector.png
new file mode 100644
index 0000000000..b4bed24937
--- /dev/null
+++ b/bridge/tests/res/testApp/MyApplication/golden-mac/animated_vector.png
Binary files differ
diff --git a/bridge/tests/res/testApp/MyApplication/golden-mac/animated_vector_1.png b/bridge/tests/res/testApp/MyApplication/golden-mac/animated_vector_1.png
new file mode 100644
index 0000000000..37863c7e05
--- /dev/null
+++ b/bridge/tests/res/testApp/MyApplication/golden-mac/animated_vector_1.png
Binary files differ
diff --git a/bridge/tests/res/testApp/MyApplication/golden-mac/array_check.png b/bridge/tests/res/testApp/MyApplication/golden-mac/array_check.png
new file mode 100644
index 0000000000..db6e934548
--- /dev/null
+++ b/bridge/tests/res/testApp/MyApplication/golden-mac/array_check.png
Binary files differ
diff --git a/bridge/tests/res/testApp/MyApplication/golden-mac/asset.png b/bridge/tests/res/testApp/MyApplication/golden-mac/asset.png
new file mode 100644
index 0000000000..9e587206eb
--- /dev/null
+++ b/bridge/tests/res/testApp/MyApplication/golden-mac/asset.png
Binary files differ
diff --git a/bridge/tests/res/testApp/MyApplication/golden-mac/auto-scale-image.png b/bridge/tests/res/testApp/MyApplication/golden-mac/auto-scale-image.png
new file mode 100644
index 0000000000..7644183a59
--- /dev/null
+++ b/bridge/tests/res/testApp/MyApplication/golden-mac/auto-scale-image.png
Binary files differ
diff --git a/bridge/tests/res/testApp/MyApplication/golden-mac/button_resize.png b/bridge/tests/res/testApp/MyApplication/golden-mac/button_resize.png
new file mode 100644
index 0000000000..dc81204ad6
--- /dev/null
+++ b/bridge/tests/res/testApp/MyApplication/golden-mac/button_resize.png
Binary files differ
diff --git a/bridge/tests/res/testApp/MyApplication/golden-mac/button_resize2.png b/bridge/tests/res/testApp/MyApplication/golden-mac/button_resize2.png
new file mode 100644
index 0000000000..9b7957628a
--- /dev/null
+++ b/bridge/tests/res/testApp/MyApplication/golden-mac/button_resize2.png
Binary files differ
diff --git a/bridge/tests/res/testApp/MyApplication/golden-mac/canvas.png b/bridge/tests/res/testApp/MyApplication/golden-mac/canvas.png
new file mode 100644
index 0000000000..ec503a4f1a
--- /dev/null
+++ b/bridge/tests/res/testApp/MyApplication/golden-mac/canvas.png
Binary files differ
diff --git a/bridge/tests/res/testApp/MyApplication/golden-mac/color_interpolation.png b/bridge/tests/res/testApp/MyApplication/golden-mac/color_interpolation.png
new file mode 100644
index 0000000000..c816e57273
--- /dev/null
+++ b/bridge/tests/res/testApp/MyApplication/golden-mac/color_interpolation.png
Binary files differ
diff --git a/bridge/tests/res/testApp/MyApplication/golden-mac/context_theme_wrapper.png b/bridge/tests/res/testApp/MyApplication/golden-mac/context_theme_wrapper.png
new file mode 100644
index 0000000000..63578fafa9
--- /dev/null
+++ b/bridge/tests/res/testApp/MyApplication/golden-mac/context_theme_wrapper.png
Binary files differ
diff --git a/bridge/tests/res/testApp/MyApplication/golden-mac/dialog.png b/bridge/tests/res/testApp/MyApplication/golden-mac/dialog.png
new file mode 100644
index 0000000000..2f029e5a52
--- /dev/null
+++ b/bridge/tests/res/testApp/MyApplication/golden-mac/dialog.png
Binary files differ
diff --git a/bridge/tests/res/testApp/MyApplication/golden-mac/expand_horz_layout.png b/bridge/tests/res/testApp/MyApplication/golden-mac/expand_horz_layout.png
new file mode 100644
index 0000000000..f179977a92
--- /dev/null
+++ b/bridge/tests/res/testApp/MyApplication/golden-mac/expand_horz_layout.png
Binary files differ
diff --git a/bridge/tests/res/testApp/MyApplication/golden-mac/expand_vert_layout.png b/bridge/tests/res/testApp/MyApplication/golden-mac/expand_vert_layout.png
new file mode 100644
index 0000000000..269b7ac4ea
--- /dev/null
+++ b/bridge/tests/res/testApp/MyApplication/golden-mac/expand_vert_layout.png
Binary files differ
diff --git a/bridge/tests/res/testApp/MyApplication/golden-mac/font_test.png b/bridge/tests/res/testApp/MyApplication/golden-mac/font_test.png
new file mode 100644
index 0000000000..e036c162ad
--- /dev/null
+++ b/bridge/tests/res/testApp/MyApplication/golden-mac/font_test.png
Binary files differ
diff --git a/bridge/tests/res/testApp/MyApplication/golden-mac/four_corners.png b/bridge/tests/res/testApp/MyApplication/golden-mac/four_corners.png
new file mode 100644
index 0000000000..fc68ac18d1
--- /dev/null
+++ b/bridge/tests/res/testApp/MyApplication/golden-mac/four_corners.png
Binary files differ
diff --git a/bridge/tests/res/testApp/MyApplication/golden-mac/four_corners_translucent.png b/bridge/tests/res/testApp/MyApplication/golden-mac/four_corners_translucent.png
new file mode 100644
index 0000000000..2aae93b01a
--- /dev/null
+++ b/bridge/tests/res/testApp/MyApplication/golden-mac/four_corners_translucent.png
Binary files differ
diff --git a/bridge/tests/res/testApp/MyApplication/golden-mac/four_corners_translucent_land.png b/bridge/tests/res/testApp/MyApplication/golden-mac/four_corners_translucent_land.png
new file mode 100644
index 0000000000..dae944a2ff
--- /dev/null
+++ b/bridge/tests/res/testApp/MyApplication/golden-mac/four_corners_translucent_land.png
Binary files differ
diff --git a/bridge/tests/res/testApp/MyApplication/golden-mac/gradient_alpha_drawable.png b/bridge/tests/res/testApp/MyApplication/golden-mac/gradient_alpha_drawable.png
new file mode 100644
index 0000000000..8892bcf6e1
--- /dev/null
+++ b/bridge/tests/res/testApp/MyApplication/golden-mac/gradient_alpha_drawable.png
Binary files differ
diff --git a/bridge/tests/res/testApp/MyApplication/golden-mac/gradient_colors.png b/bridge/tests/res/testApp/MyApplication/golden-mac/gradient_colors.png
new file mode 100644
index 0000000000..d5b7c101a2
--- /dev/null
+++ b/bridge/tests/res/testApp/MyApplication/golden-mac/gradient_colors.png
Binary files differ
diff --git a/bridge/tests/res/testApp/MyApplication/golden-mac/justified_inter_word.png b/bridge/tests/res/testApp/MyApplication/golden-mac/justified_inter_word.png
new file mode 100644
index 0000000000..48078217cb
--- /dev/null
+++ b/bridge/tests/res/testApp/MyApplication/golden-mac/justified_inter_word.png
Binary files differ
diff --git a/bridge/tests/res/testApp/MyApplication/golden-mac/justified_none.png b/bridge/tests/res/testApp/MyApplication/golden-mac/justified_none.png
new file mode 100644
index 0000000000..e083346b66
--- /dev/null
+++ b/bridge/tests/res/testApp/MyApplication/golden-mac/justified_none.png
Binary files differ
diff --git a/bridge/tests/res/testApp/MyApplication/golden-mac/large_shadows_test.png b/bridge/tests/res/testApp/MyApplication/golden-mac/large_shadows_test.png
new file mode 100644
index 0000000000..a79ad55557
--- /dev/null
+++ b/bridge/tests/res/testApp/MyApplication/golden-mac/large_shadows_test.png
Binary files differ
diff --git a/bridge/tests/res/testApp/MyApplication/golden-mac/many_line_breaks.png b/bridge/tests/res/testApp/MyApplication/golden-mac/many_line_breaks.png
new file mode 100644
index 0000000000..ea5750c79c
--- /dev/null
+++ b/bridge/tests/res/testApp/MyApplication/golden-mac/many_line_breaks.png
Binary files differ
diff --git a/bridge/tests/res/testApp/MyApplication/golden-mac/ninepatch_background.png b/bridge/tests/res/testApp/MyApplication/golden-mac/ninepatch_background.png
new file mode 100644
index 0000000000..014df65928
--- /dev/null
+++ b/bridge/tests/res/testApp/MyApplication/golden-mac/ninepatch_background.png
Binary files differ
diff --git a/bridge/tests/res/testApp/MyApplication/golden-mac/ninepatch_drawable.png b/bridge/tests/res/testApp/MyApplication/golden-mac/ninepatch_drawable.png
new file mode 100644
index 0000000000..211594dfe3
--- /dev/null
+++ b/bridge/tests/res/testApp/MyApplication/golden-mac/ninepatch_drawable.png
Binary files differ
diff --git a/bridge/tests/res/testApp/MyApplication/golden-mac/non-styled_resources.png b/bridge/tests/res/testApp/MyApplication/golden-mac/non-styled_resources.png
new file mode 100644
index 0000000000..6490b263cf
--- /dev/null
+++ b/bridge/tests/res/testApp/MyApplication/golden-mac/non-styled_resources.png
Binary files differ
diff --git a/bridge/tests/res/testApp/MyApplication/golden-mac/normal_layout.png b/bridge/tests/res/testApp/MyApplication/golden-mac/normal_layout.png
new file mode 100644
index 0000000000..f60ecb0b74
--- /dev/null
+++ b/bridge/tests/res/testApp/MyApplication/golden-mac/normal_layout.png
Binary files differ
diff --git a/bridge/tests/res/testApp/MyApplication/golden-mac/ondraw_crash.png b/bridge/tests/res/testApp/MyApplication/golden-mac/ondraw_crash.png
new file mode 100644
index 0000000000..8633a259d8
--- /dev/null
+++ b/bridge/tests/res/testApp/MyApplication/golden-mac/ondraw_crash.png
Binary files differ
diff --git a/bridge/tests/res/testApp/MyApplication/golden-mac/onmeasure_crash.png b/bridge/tests/res/testApp/MyApplication/golden-mac/onmeasure_crash.png
new file mode 100644
index 0000000000..5559a89456
--- /dev/null
+++ b/bridge/tests/res/testApp/MyApplication/golden-mac/onmeasure_crash.png
Binary files differ
diff --git a/bridge/tests/res/testApp/MyApplication/golden-mac/render_effect.png b/bridge/tests/res/testApp/MyApplication/golden-mac/render_effect.png
new file mode 100644
index 0000000000..7a909981e9
--- /dev/null
+++ b/bridge/tests/res/testApp/MyApplication/golden-mac/render_effect.png
Binary files differ
diff --git a/bridge/tests/res/testApp/MyApplication/golden-mac/rtl_ltr.png b/bridge/tests/res/testApp/MyApplication/golden-mac/rtl_ltr.png
new file mode 100644
index 0000000000..6a451a2a65
--- /dev/null
+++ b/bridge/tests/res/testApp/MyApplication/golden-mac/rtl_ltr.png
Binary files differ
diff --git a/bridge/tests/res/testApp/MyApplication/golden-mac/scrolled.png b/bridge/tests/res/testApp/MyApplication/golden-mac/scrolled.png
new file mode 100644
index 0000000000..97bf039562
--- /dev/null
+++ b/bridge/tests/res/testApp/MyApplication/golden-mac/scrolled.png
Binary files differ
diff --git a/bridge/tests/res/testApp/MyApplication/golden-mac/shadow_scrollview_test.png b/bridge/tests/res/testApp/MyApplication/golden-mac/shadow_scrollview_test.png
new file mode 100644
index 0000000000..b1a3b26739
--- /dev/null
+++ b/bridge/tests/res/testApp/MyApplication/golden-mac/shadow_scrollview_test.png
Binary files differ
diff --git a/bridge/tests/res/testApp/MyApplication/golden-mac/shadow_sizes_test.png b/bridge/tests/res/testApp/MyApplication/golden-mac/shadow_sizes_test.png
new file mode 100644
index 0000000000..3bc9b4c27b
--- /dev/null
+++ b/bridge/tests/res/testApp/MyApplication/golden-mac/shadow_sizes_test.png
Binary files differ
diff --git a/bridge/tests/res/testApp/MyApplication/golden-mac/shadows_test.png b/bridge/tests/res/testApp/MyApplication/golden-mac/shadows_test.png
new file mode 100644
index 0000000000..c8dea1bf57
--- /dev/null
+++ b/bridge/tests/res/testApp/MyApplication/golden-mac/shadows_test.png
Binary files differ
diff --git a/bridge/tests/res/testApp/MyApplication/golden-mac/shadows_test_rounded_edge.png b/bridge/tests/res/testApp/MyApplication/golden-mac/shadows_test_rounded_edge.png
new file mode 100644
index 0000000000..8bac13b854
--- /dev/null
+++ b/bridge/tests/res/testApp/MyApplication/golden-mac/shadows_test_rounded_edge.png
Binary files differ
diff --git a/bridge/tests/res/testApp/MyApplication/golden-mac/shrunk_layout.png b/bridge/tests/res/testApp/MyApplication/golden-mac/shrunk_layout.png
new file mode 100644
index 0000000000..e2eb120ef7
--- /dev/null
+++ b/bridge/tests/res/testApp/MyApplication/golden-mac/shrunk_layout.png
Binary files differ
diff --git a/bridge/tests/res/testApp/MyApplication/golden-mac/simple_activity-old-theme.png b/bridge/tests/res/testApp/MyApplication/golden-mac/simple_activity-old-theme.png
new file mode 100644
index 0000000000..d782cd198c
--- /dev/null
+++ b/bridge/tests/res/testApp/MyApplication/golden-mac/simple_activity-old-theme.png
Binary files differ
diff --git a/bridge/tests/res/testApp/MyApplication/golden-mac/simple_activity.png b/bridge/tests/res/testApp/MyApplication/golden-mac/simple_activity.png
new file mode 100644
index 0000000000..efb7ac3ae3
--- /dev/null
+++ b/bridge/tests/res/testApp/MyApplication/golden-mac/simple_activity.png
Binary files differ
diff --git a/bridge/tests/res/testApp/MyApplication/golden-mac/simple_activity_noactionbar.png b/bridge/tests/res/testApp/MyApplication/golden-mac/simple_activity_noactionbar.png
new file mode 100644
index 0000000000..36350a04ad
--- /dev/null
+++ b/bridge/tests/res/testApp/MyApplication/golden-mac/simple_activity_noactionbar.png
Binary files differ
diff --git a/bridge/tests/res/testApp/MyApplication/golden-mac/textclock.png b/bridge/tests/res/testApp/MyApplication/golden-mac/textclock.png
new file mode 100644
index 0000000000..108380c4d3
--- /dev/null
+++ b/bridge/tests/res/testApp/MyApplication/golden-mac/textclock.png
Binary files differ
diff --git a/bridge/tests/res/testApp/MyApplication/golden-mac/translate_test.png b/bridge/tests/res/testApp/MyApplication/golden-mac/translate_test.png
new file mode 100644
index 0000000000..032089f012
--- /dev/null
+++ b/bridge/tests/res/testApp/MyApplication/golden-mac/translate_test.png
Binary files differ
diff --git a/bridge/tests/res/testApp/MyApplication/golden-mac/transparent_drawable.png b/bridge/tests/res/testApp/MyApplication/golden-mac/transparent_drawable.png
new file mode 100644
index 0000000000..68ccf2f91d
--- /dev/null
+++ b/bridge/tests/res/testApp/MyApplication/golden-mac/transparent_drawable.png
Binary files differ
diff --git a/bridge/tests/res/testApp/MyApplication/golden-mac/typed_arrays.png b/bridge/tests/res/testApp/MyApplication/golden-mac/typed_arrays.png
new file mode 100644
index 0000000000..e3f4df7c20
--- /dev/null
+++ b/bridge/tests/res/testApp/MyApplication/golden-mac/typed_arrays.png
Binary files differ
diff --git a/bridge/tests/res/testApp/MyApplication/golden-mac/vector_drawable.png b/bridge/tests/res/testApp/MyApplication/golden-mac/vector_drawable.png
new file mode 100644
index 0000000000..ecc4497a3c
--- /dev/null
+++ b/bridge/tests/res/testApp/MyApplication/golden-mac/vector_drawable.png
Binary files differ
diff --git a/bridge/tests/res/testApp/MyApplication/golden-mac/vector_drawable_91383.png b/bridge/tests/res/testApp/MyApplication/golden-mac/vector_drawable_91383.png
new file mode 100644
index 0000000000..1d4ac397f0
--- /dev/null
+++ b/bridge/tests/res/testApp/MyApplication/golden-mac/vector_drawable_91383.png
Binary files differ
diff --git a/bridge/tests/res/testApp/MyApplication/golden-mac/vector_drawable_gradient.png b/bridge/tests/res/testApp/MyApplication/golden-mac/vector_drawable_gradient.png
new file mode 100644
index 0000000000..0268d9c33f
--- /dev/null
+++ b/bridge/tests/res/testApp/MyApplication/golden-mac/vector_drawable_gradient.png
Binary files differ
diff --git a/bridge/tests/res/testApp/MyApplication/golden-mac/vector_drawable_radial_gradient.png b/bridge/tests/res/testApp/MyApplication/golden-mac/vector_drawable_radial_gradient.png
new file mode 100644
index 0000000000..a27a48eb21
--- /dev/null
+++ b/bridge/tests/res/testApp/MyApplication/golden-mac/vector_drawable_radial_gradient.png
Binary files differ
diff --git a/bridge/tests/res/testApp/MyApplication/golden-mac/vector_drawable_with_tint_in_image_view.png b/bridge/tests/res/testApp/MyApplication/golden-mac/vector_drawable_with_tint_in_image_view.png
new file mode 100644
index 0000000000..48f0f0542a
--- /dev/null
+++ b/bridge/tests/res/testApp/MyApplication/golden-mac/vector_drawable_with_tint_in_image_view.png
Binary files differ
diff --git a/bridge/tests/res/testApp/MyApplication/golden-mac/vector_drawable_with_tint_itself.png b/bridge/tests/res/testApp/MyApplication/golden-mac/vector_drawable_with_tint_itself.png
new file mode 100644
index 0000000000..92855d13db
--- /dev/null
+++ b/bridge/tests/res/testApp/MyApplication/golden-mac/vector_drawable_with_tint_itself.png
Binary files differ
diff --git a/bridge/tests/res/testApp/MyApplication/golden-mac/view_boundaries.png b/bridge/tests/res/testApp/MyApplication/golden-mac/view_boundaries.png
new file mode 100644
index 0000000000..da770e1f94
--- /dev/null
+++ b/bridge/tests/res/testApp/MyApplication/golden-mac/view_boundaries.png
Binary files differ
diff --git a/bridge/tests/res/testApp/MyApplication/golden-mac/view_stub.png b/bridge/tests/res/testApp/MyApplication/golden-mac/view_stub.png
new file mode 100644
index 0000000000..b66ff2d524
--- /dev/null
+++ b/bridge/tests/res/testApp/MyApplication/golden-mac/view_stub.png
Binary files differ
diff --git a/bridge/tests/res/testApp/MyApplication/golden-mac/window_background.png b/bridge/tests/res/testApp/MyApplication/golden-mac/window_background.png
new file mode 100644
index 0000000000..1d25725e4e
--- /dev/null
+++ b/bridge/tests/res/testApp/MyApplication/golden-mac/window_background.png
Binary files differ
diff --git a/bridge/tests/res/testApp/MyApplication/golden/activity.png b/bridge/tests/res/testApp/MyApplication/golden/activity.png
index 2d55e8ff79..0dbe45fa86 100644
--- a/bridge/tests/res/testApp/MyApplication/golden/activity.png
+++ b/bridge/tests/res/testApp/MyApplication/golden/activity.png
Binary files differ
diff --git a/bridge/tests/res/testApp/MyApplication/golden/adaptive_icon.png b/bridge/tests/res/testApp/MyApplication/golden/adaptive_icon.png
index eccd6eab09..efcd49e750 100644
--- a/bridge/tests/res/testApp/MyApplication/golden/adaptive_icon.png
+++ b/bridge/tests/res/testApp/MyApplication/golden/adaptive_icon.png
Binary files differ
diff --git a/bridge/tests/res/testApp/MyApplication/golden/adaptive_icon_circle.png b/bridge/tests/res/testApp/MyApplication/golden/adaptive_icon_circle.png
index b98b38b20a..f85ef1b7fe 100644
--- a/bridge/tests/res/testApp/MyApplication/golden/adaptive_icon_circle.png
+++ b/bridge/tests/res/testApp/MyApplication/golden/adaptive_icon_circle.png
Binary files differ
diff --git a/bridge/tests/res/testApp/MyApplication/golden/adaptive_icon_rounded_corners.png b/bridge/tests/res/testApp/MyApplication/golden/adaptive_icon_rounded_corners.png
index 44d2161231..3bb70551c2 100644
--- a/bridge/tests/res/testApp/MyApplication/golden/adaptive_icon_rounded_corners.png
+++ b/bridge/tests/res/testApp/MyApplication/golden/adaptive_icon_rounded_corners.png
Binary files differ
diff --git a/bridge/tests/res/testApp/MyApplication/golden/adaptive_icon_squircle.png b/bridge/tests/res/testApp/MyApplication/golden/adaptive_icon_squircle.png
index 3361cc991a..f3b3a08d5d 100644
--- a/bridge/tests/res/testApp/MyApplication/golden/adaptive_icon_squircle.png
+++ b/bridge/tests/res/testApp/MyApplication/golden/adaptive_icon_squircle.png
Binary files differ
diff --git a/bridge/tests/res/testApp/MyApplication/golden/allwidgets.png b/bridge/tests/res/testApp/MyApplication/golden/allwidgets.png
index 8d64d47888..d99df26898 100644
--- a/bridge/tests/res/testApp/MyApplication/golden/allwidgets.png
+++ b/bridge/tests/res/testApp/MyApplication/golden/allwidgets.png
Binary files differ
diff --git a/bridge/tests/res/testApp/MyApplication/golden/allwidgets_tab.png b/bridge/tests/res/testApp/MyApplication/golden/allwidgets_tab.png
index 7559d54571..5ddd9d2e85 100644
--- a/bridge/tests/res/testApp/MyApplication/golden/allwidgets_tab.png
+++ b/bridge/tests/res/testApp/MyApplication/golden/allwidgets_tab.png
Binary files differ
diff --git a/bridge/tests/res/testApp/MyApplication/golden/animated_vector.png b/bridge/tests/res/testApp/MyApplication/golden/animated_vector.png
index 072df3070a..b4bed24937 100644
--- a/bridge/tests/res/testApp/MyApplication/golden/animated_vector.png
+++ b/bridge/tests/res/testApp/MyApplication/golden/animated_vector.png
Binary files differ
diff --git a/bridge/tests/res/testApp/MyApplication/golden/animated_vector_1.png b/bridge/tests/res/testApp/MyApplication/golden/animated_vector_1.png
index 348a0f4883..37863c7e05 100644
--- a/bridge/tests/res/testApp/MyApplication/golden/animated_vector_1.png
+++ b/bridge/tests/res/testApp/MyApplication/golden/animated_vector_1.png
Binary files differ
diff --git a/bridge/tests/res/testApp/MyApplication/golden/array_check.png b/bridge/tests/res/testApp/MyApplication/golden/array_check.png
index e4be8427be..db6e934548 100644
--- a/bridge/tests/res/testApp/MyApplication/golden/array_check.png
+++ b/bridge/tests/res/testApp/MyApplication/golden/array_check.png
Binary files differ
diff --git a/bridge/tests/res/testApp/MyApplication/golden/asset.png b/bridge/tests/res/testApp/MyApplication/golden/asset.png
index 3a0036179b..9e587206eb 100644
--- a/bridge/tests/res/testApp/MyApplication/golden/asset.png
+++ b/bridge/tests/res/testApp/MyApplication/golden/asset.png
Binary files differ
diff --git a/bridge/tests/res/testApp/MyApplication/golden/auto-scale-image.png b/bridge/tests/res/testApp/MyApplication/golden/auto-scale-image.png
index f6ff6235c6..7644183a59 100644
--- a/bridge/tests/res/testApp/MyApplication/golden/auto-scale-image.png
+++ b/bridge/tests/res/testApp/MyApplication/golden/auto-scale-image.png
Binary files differ
diff --git a/bridge/tests/res/testApp/MyApplication/golden/button_resize.png b/bridge/tests/res/testApp/MyApplication/golden/button_resize.png
new file mode 100644
index 0000000000..dc81204ad6
--- /dev/null
+++ b/bridge/tests/res/testApp/MyApplication/golden/button_resize.png
Binary files differ
diff --git a/bridge/tests/res/testApp/MyApplication/golden/button_resize2.png b/bridge/tests/res/testApp/MyApplication/golden/button_resize2.png
new file mode 100644
index 0000000000..9b7957628a
--- /dev/null
+++ b/bridge/tests/res/testApp/MyApplication/golden/button_resize2.png
Binary files differ
diff --git a/bridge/tests/res/testApp/MyApplication/golden/canvas.png b/bridge/tests/res/testApp/MyApplication/golden/canvas.png
index a267b07738..55f84c0073 100644
--- a/bridge/tests/res/testApp/MyApplication/golden/canvas.png
+++ b/bridge/tests/res/testApp/MyApplication/golden/canvas.png
Binary files differ
diff --git a/bridge/tests/res/testApp/MyApplication/golden/color_interpolation.png b/bridge/tests/res/testApp/MyApplication/golden/color_interpolation.png
index 2d90bc33f3..c816e57273 100644
--- a/bridge/tests/res/testApp/MyApplication/golden/color_interpolation.png
+++ b/bridge/tests/res/testApp/MyApplication/golden/color_interpolation.png
Binary files differ
diff --git a/bridge/tests/res/testApp/MyApplication/golden/context_theme_wrapper.png b/bridge/tests/res/testApp/MyApplication/golden/context_theme_wrapper.png
index 5627108645..63578fafa9 100644
--- a/bridge/tests/res/testApp/MyApplication/golden/context_theme_wrapper.png
+++ b/bridge/tests/res/testApp/MyApplication/golden/context_theme_wrapper.png
Binary files differ
diff --git a/bridge/tests/res/testApp/MyApplication/golden/dialog.png b/bridge/tests/res/testApp/MyApplication/golden/dialog.png
new file mode 100644
index 0000000000..2f029e5a52
--- /dev/null
+++ b/bridge/tests/res/testApp/MyApplication/golden/dialog.png
Binary files differ
diff --git a/bridge/tests/res/testApp/MyApplication/golden/expand_horz_layout.png b/bridge/tests/res/testApp/MyApplication/golden/expand_horz_layout.png
index 0ae96a5341..f179977a92 100644
--- a/bridge/tests/res/testApp/MyApplication/golden/expand_horz_layout.png
+++ b/bridge/tests/res/testApp/MyApplication/golden/expand_horz_layout.png
Binary files differ
diff --git a/bridge/tests/res/testApp/MyApplication/golden/expand_vert_layout.png b/bridge/tests/res/testApp/MyApplication/golden/expand_vert_layout.png
index 2f0b937c5e..269b7ac4ea 100644
--- a/bridge/tests/res/testApp/MyApplication/golden/expand_vert_layout.png
+++ b/bridge/tests/res/testApp/MyApplication/golden/expand_vert_layout.png
Binary files differ
diff --git a/bridge/tests/res/testApp/MyApplication/golden/font_test.png b/bridge/tests/res/testApp/MyApplication/golden/font_test.png
index b3db6241e3..e036c162ad 100644
--- a/bridge/tests/res/testApp/MyApplication/golden/font_test.png
+++ b/bridge/tests/res/testApp/MyApplication/golden/font_test.png
Binary files differ
diff --git a/bridge/tests/res/testApp/MyApplication/golden/four_corners.png b/bridge/tests/res/testApp/MyApplication/golden/four_corners.png
index e79c860024..fc68ac18d1 100644
--- a/bridge/tests/res/testApp/MyApplication/golden/four_corners.png
+++ b/bridge/tests/res/testApp/MyApplication/golden/four_corners.png
Binary files differ
diff --git a/bridge/tests/res/testApp/MyApplication/golden/four_corners_translucent.png b/bridge/tests/res/testApp/MyApplication/golden/four_corners_translucent.png
index 71299446f3..2aae93b01a 100644
--- a/bridge/tests/res/testApp/MyApplication/golden/four_corners_translucent.png
+++ b/bridge/tests/res/testApp/MyApplication/golden/four_corners_translucent.png
Binary files differ
diff --git a/bridge/tests/res/testApp/MyApplication/golden/four_corners_translucent_land.png b/bridge/tests/res/testApp/MyApplication/golden/four_corners_translucent_land.png
index 0e5336ff85..dae944a2ff 100644
--- a/bridge/tests/res/testApp/MyApplication/golden/four_corners_translucent_land.png
+++ b/bridge/tests/res/testApp/MyApplication/golden/four_corners_translucent_land.png
Binary files differ
diff --git a/bridge/tests/res/testApp/MyApplication/golden/gradient_alpha_drawable.png b/bridge/tests/res/testApp/MyApplication/golden/gradient_alpha_drawable.png
index c5e6e40627..8892bcf6e1 100644
--- a/bridge/tests/res/testApp/MyApplication/golden/gradient_alpha_drawable.png
+++ b/bridge/tests/res/testApp/MyApplication/golden/gradient_alpha_drawable.png
Binary files differ
diff --git a/bridge/tests/res/testApp/MyApplication/golden/gradient_colors.png b/bridge/tests/res/testApp/MyApplication/golden/gradient_colors.png
index 137eda8e7e..d5b7c101a2 100644
--- a/bridge/tests/res/testApp/MyApplication/golden/gradient_colors.png
+++ b/bridge/tests/res/testApp/MyApplication/golden/gradient_colors.png
Binary files differ
diff --git a/bridge/tests/res/testApp/MyApplication/golden/justified_inter_word.png b/bridge/tests/res/testApp/MyApplication/golden/justified_inter_word.png
new file mode 100644
index 0000000000..48078217cb
--- /dev/null
+++ b/bridge/tests/res/testApp/MyApplication/golden/justified_inter_word.png
Binary files differ
diff --git a/bridge/tests/res/testApp/MyApplication/golden/justified_none.png b/bridge/tests/res/testApp/MyApplication/golden/justified_none.png
new file mode 100644
index 0000000000..e083346b66
--- /dev/null
+++ b/bridge/tests/res/testApp/MyApplication/golden/justified_none.png
Binary files differ
diff --git a/bridge/tests/res/testApp/MyApplication/golden/large_shadows_test.png b/bridge/tests/res/testApp/MyApplication/golden/large_shadows_test.png
new file mode 100644
index 0000000000..a79ad55557
--- /dev/null
+++ b/bridge/tests/res/testApp/MyApplication/golden/large_shadows_test.png
Binary files differ
diff --git a/bridge/tests/res/testApp/MyApplication/golden/large_shadows_test_high_quality.png b/bridge/tests/res/testApp/MyApplication/golden/large_shadows_test_high_quality.png
deleted file mode 100644
index 17bb71327f..0000000000
--- a/bridge/tests/res/testApp/MyApplication/golden/large_shadows_test_high_quality.png
+++ /dev/null
Binary files differ
diff --git a/bridge/tests/res/testApp/MyApplication/golden/many_line_breaks.png b/bridge/tests/res/testApp/MyApplication/golden/many_line_breaks.png
index bfe7ce09b1..ea5750c79c 100644
--- a/bridge/tests/res/testApp/MyApplication/golden/many_line_breaks.png
+++ b/bridge/tests/res/testApp/MyApplication/golden/many_line_breaks.png
Binary files differ
diff --git a/bridge/tests/res/testApp/MyApplication/golden/ninepatch_background.png b/bridge/tests/res/testApp/MyApplication/golden/ninepatch_background.png
index b3ac4361d5..014df65928 100644
--- a/bridge/tests/res/testApp/MyApplication/golden/ninepatch_background.png
+++ b/bridge/tests/res/testApp/MyApplication/golden/ninepatch_background.png
Binary files differ
diff --git a/bridge/tests/res/testApp/MyApplication/golden/ninepatch_drawable.png b/bridge/tests/res/testApp/MyApplication/golden/ninepatch_drawable.png
new file mode 100644
index 0000000000..211594dfe3
--- /dev/null
+++ b/bridge/tests/res/testApp/MyApplication/golden/ninepatch_drawable.png
Binary files differ
diff --git a/bridge/tests/res/testApp/MyApplication/golden/non-styled_resources.png b/bridge/tests/res/testApp/MyApplication/golden/non-styled_resources.png
new file mode 100644
index 0000000000..6490b263cf
--- /dev/null
+++ b/bridge/tests/res/testApp/MyApplication/golden/non-styled_resources.png
Binary files differ
diff --git a/bridge/tests/res/testApp/MyApplication/golden/normal_layout.png b/bridge/tests/res/testApp/MyApplication/golden/normal_layout.png
index f1d406e305..f60ecb0b74 100644
--- a/bridge/tests/res/testApp/MyApplication/golden/normal_layout.png
+++ b/bridge/tests/res/testApp/MyApplication/golden/normal_layout.png
Binary files differ
diff --git a/bridge/tests/res/testApp/MyApplication/golden/ondraw_crash.png b/bridge/tests/res/testApp/MyApplication/golden/ondraw_crash.png
index 79aa3a81cf..8633a259d8 100644
--- a/bridge/tests/res/testApp/MyApplication/golden/ondraw_crash.png
+++ b/bridge/tests/res/testApp/MyApplication/golden/ondraw_crash.png
Binary files differ
diff --git a/bridge/tests/res/testApp/MyApplication/golden/onmeasure_crash.png b/bridge/tests/res/testApp/MyApplication/golden/onmeasure_crash.png
index bac8f8e1a8..5559a89456 100644
--- a/bridge/tests/res/testApp/MyApplication/golden/onmeasure_crash.png
+++ b/bridge/tests/res/testApp/MyApplication/golden/onmeasure_crash.png
Binary files differ
diff --git a/bridge/tests/res/testApp/MyApplication/golden/render_effect.png b/bridge/tests/res/testApp/MyApplication/golden/render_effect.png
new file mode 100644
index 0000000000..7a909981e9
--- /dev/null
+++ b/bridge/tests/res/testApp/MyApplication/golden/render_effect.png
Binary files differ
diff --git a/bridge/tests/res/testApp/MyApplication/golden/rtl_ltr.png b/bridge/tests/res/testApp/MyApplication/golden/rtl_ltr.png
index 1690785144..40bb188ab2 100644
--- a/bridge/tests/res/testApp/MyApplication/golden/rtl_ltr.png
+++ b/bridge/tests/res/testApp/MyApplication/golden/rtl_ltr.png
Binary files differ
diff --git a/bridge/tests/res/testApp/MyApplication/golden/scrolled.png b/bridge/tests/res/testApp/MyApplication/golden/scrolled.png
index 27227ea330..97bf039562 100644
--- a/bridge/tests/res/testApp/MyApplication/golden/scrolled.png
+++ b/bridge/tests/res/testApp/MyApplication/golden/scrolled.png
Binary files differ
diff --git a/bridge/tests/res/testApp/MyApplication/golden/shadow_scrollview_test.png b/bridge/tests/res/testApp/MyApplication/golden/shadow_scrollview_test.png
new file mode 100644
index 0000000000..2f1544f79c
--- /dev/null
+++ b/bridge/tests/res/testApp/MyApplication/golden/shadow_scrollview_test.png
Binary files differ
diff --git a/bridge/tests/res/testApp/MyApplication/golden/shadow_scrollview_test_high_quality.png b/bridge/tests/res/testApp/MyApplication/golden/shadow_scrollview_test_high_quality.png
deleted file mode 100644
index 716cd08b5f..0000000000
--- a/bridge/tests/res/testApp/MyApplication/golden/shadow_scrollview_test_high_quality.png
+++ /dev/null
Binary files differ
diff --git a/bridge/tests/res/testApp/MyApplication/golden/shadow_sizes_test.png b/bridge/tests/res/testApp/MyApplication/golden/shadow_sizes_test.png
new file mode 100644
index 0000000000..301f40f650
--- /dev/null
+++ b/bridge/tests/res/testApp/MyApplication/golden/shadow_sizes_test.png
Binary files differ
diff --git a/bridge/tests/res/testApp/MyApplication/golden/shadow_sizes_test_high_quality.png b/bridge/tests/res/testApp/MyApplication/golden/shadow_sizes_test_high_quality.png
deleted file mode 100644
index a4ff637988..0000000000
--- a/bridge/tests/res/testApp/MyApplication/golden/shadow_sizes_test_high_quality.png
+++ /dev/null
Binary files differ
diff --git a/bridge/tests/res/testApp/MyApplication/golden/shadows_test.png b/bridge/tests/res/testApp/MyApplication/golden/shadows_test.png
index 21f5ee1568..c8dea1bf57 100644
--- a/bridge/tests/res/testApp/MyApplication/golden/shadows_test.png
+++ b/bridge/tests/res/testApp/MyApplication/golden/shadows_test.png
Binary files differ
diff --git a/bridge/tests/res/testApp/MyApplication/golden/shadows_test_high_quality.png b/bridge/tests/res/testApp/MyApplication/golden/shadows_test_high_quality.png
deleted file mode 100644
index 1e109635e0..0000000000
--- a/bridge/tests/res/testApp/MyApplication/golden/shadows_test_high_quality.png
+++ /dev/null
Binary files differ
diff --git a/bridge/tests/res/testApp/MyApplication/golden/shadows_test_high_quality_rounded_edge.png b/bridge/tests/res/testApp/MyApplication/golden/shadows_test_high_quality_rounded_edge.png
deleted file mode 100644
index 25ba5359d3..0000000000
--- a/bridge/tests/res/testApp/MyApplication/golden/shadows_test_high_quality_rounded_edge.png
+++ /dev/null
Binary files differ
diff --git a/bridge/tests/res/testApp/MyApplication/golden/shadows_test_no_shadow.png b/bridge/tests/res/testApp/MyApplication/golden/shadows_test_no_shadow.png
deleted file mode 100644
index 049249189b..0000000000
--- a/bridge/tests/res/testApp/MyApplication/golden/shadows_test_no_shadow.png
+++ /dev/null
Binary files differ
diff --git a/bridge/tests/res/testApp/MyApplication/golden/shadows_test_rounded_edge.png b/bridge/tests/res/testApp/MyApplication/golden/shadows_test_rounded_edge.png
new file mode 100644
index 0000000000..d9672f1d4a
--- /dev/null
+++ b/bridge/tests/res/testApp/MyApplication/golden/shadows_test_rounded_edge.png
Binary files differ
diff --git a/bridge/tests/res/testApp/MyApplication/golden/shrunk_layout.png b/bridge/tests/res/testApp/MyApplication/golden/shrunk_layout.png
index fe6fc163ab..e2eb120ef7 100644
--- a/bridge/tests/res/testApp/MyApplication/golden/shrunk_layout.png
+++ b/bridge/tests/res/testApp/MyApplication/golden/shrunk_layout.png
Binary files differ
diff --git a/bridge/tests/res/testApp/MyApplication/golden/simple_activity-old-theme.png b/bridge/tests/res/testApp/MyApplication/golden/simple_activity-old-theme.png
index 01adbd72c7..d782cd198c 100644
--- a/bridge/tests/res/testApp/MyApplication/golden/simple_activity-old-theme.png
+++ b/bridge/tests/res/testApp/MyApplication/golden/simple_activity-old-theme.png
Binary files differ
diff --git a/bridge/tests/res/testApp/MyApplication/golden/simple_activity.png b/bridge/tests/res/testApp/MyApplication/golden/simple_activity.png
index 4f62a5d41b..efb7ac3ae3 100644
--- a/bridge/tests/res/testApp/MyApplication/golden/simple_activity.png
+++ b/bridge/tests/res/testApp/MyApplication/golden/simple_activity.png
Binary files differ
diff --git a/bridge/tests/res/testApp/MyApplication/golden/simple_activity_noactionbar.png b/bridge/tests/res/testApp/MyApplication/golden/simple_activity_noactionbar.png
index aee806f81b..36350a04ad 100644
--- a/bridge/tests/res/testApp/MyApplication/golden/simple_activity_noactionbar.png
+++ b/bridge/tests/res/testApp/MyApplication/golden/simple_activity_noactionbar.png
Binary files differ
diff --git a/bridge/tests/res/testApp/MyApplication/golden/textclock.png b/bridge/tests/res/testApp/MyApplication/golden/textclock.png
index e33047d2cb..108380c4d3 100644
--- a/bridge/tests/res/testApp/MyApplication/golden/textclock.png
+++ b/bridge/tests/res/testApp/MyApplication/golden/textclock.png
Binary files differ
diff --git a/bridge/tests/res/testApp/MyApplication/golden/translate_test.png b/bridge/tests/res/testApp/MyApplication/golden/translate_test.png
index c031e41ba9..032089f012 100644
--- a/bridge/tests/res/testApp/MyApplication/golden/translate_test.png
+++ b/bridge/tests/res/testApp/MyApplication/golden/translate_test.png
Binary files differ
diff --git a/bridge/tests/res/testApp/MyApplication/golden/transparent_drawable.png b/bridge/tests/res/testApp/MyApplication/golden/transparent_drawable.png
new file mode 100644
index 0000000000..68ccf2f91d
--- /dev/null
+++ b/bridge/tests/res/testApp/MyApplication/golden/transparent_drawable.png
Binary files differ
diff --git a/bridge/tests/res/testApp/MyApplication/golden/typed_arrays.png b/bridge/tests/res/testApp/MyApplication/golden/typed_arrays.png
index 5e13aacbab..e3f4df7c20 100644
--- a/bridge/tests/res/testApp/MyApplication/golden/typed_arrays.png
+++ b/bridge/tests/res/testApp/MyApplication/golden/typed_arrays.png
Binary files differ
diff --git a/bridge/tests/res/testApp/MyApplication/golden/vector_drawable.png b/bridge/tests/res/testApp/MyApplication/golden/vector_drawable.png
index 88f72c6983..ecc4497a3c 100644
--- a/bridge/tests/res/testApp/MyApplication/golden/vector_drawable.png
+++ b/bridge/tests/res/testApp/MyApplication/golden/vector_drawable.png
Binary files differ
diff --git a/bridge/tests/res/testApp/MyApplication/golden/vector_drawable_91383.png b/bridge/tests/res/testApp/MyApplication/golden/vector_drawable_91383.png
index 5e27b09449..1d4ac397f0 100644
--- a/bridge/tests/res/testApp/MyApplication/golden/vector_drawable_91383.png
+++ b/bridge/tests/res/testApp/MyApplication/golden/vector_drawable_91383.png
Binary files differ
diff --git a/bridge/tests/res/testApp/MyApplication/golden/vector_drawable_gradient.png b/bridge/tests/res/testApp/MyApplication/golden/vector_drawable_gradient.png
index 89da00516f..0268d9c33f 100644
--- a/bridge/tests/res/testApp/MyApplication/golden/vector_drawable_gradient.png
+++ b/bridge/tests/res/testApp/MyApplication/golden/vector_drawable_gradient.png
Binary files differ
diff --git a/bridge/tests/res/testApp/MyApplication/golden/vector_drawable_multi_line_of_path_data.png b/bridge/tests/res/testApp/MyApplication/golden/vector_drawable_multi_line_of_path_data.png
deleted file mode 100644
index 64220561b8..0000000000
--- a/bridge/tests/res/testApp/MyApplication/golden/vector_drawable_multi_line_of_path_data.png
+++ /dev/null
Binary files differ
diff --git a/bridge/tests/res/testApp/MyApplication/golden/vector_drawable_radial_gradient.png b/bridge/tests/res/testApp/MyApplication/golden/vector_drawable_radial_gradient.png
index a3dd935fe0..a27a48eb21 100644
--- a/bridge/tests/res/testApp/MyApplication/golden/vector_drawable_radial_gradient.png
+++ b/bridge/tests/res/testApp/MyApplication/golden/vector_drawable_radial_gradient.png
Binary files differ
diff --git a/bridge/tests/res/testApp/MyApplication/golden/vector_drawable_with_tint_in_image_view.png b/bridge/tests/res/testApp/MyApplication/golden/vector_drawable_with_tint_in_image_view.png
index dc8bd065e4..48f0f0542a 100644
--- a/bridge/tests/res/testApp/MyApplication/golden/vector_drawable_with_tint_in_image_view.png
+++ b/bridge/tests/res/testApp/MyApplication/golden/vector_drawable_with_tint_in_image_view.png
Binary files differ
diff --git a/bridge/tests/res/testApp/MyApplication/golden/vector_drawable_with_tint_itself.png b/bridge/tests/res/testApp/MyApplication/golden/vector_drawable_with_tint_itself.png
index 9e13c62f8d..92855d13db 100644
--- a/bridge/tests/res/testApp/MyApplication/golden/vector_drawable_with_tint_itself.png
+++ b/bridge/tests/res/testApp/MyApplication/golden/vector_drawable_with_tint_itself.png
Binary files differ
diff --git a/bridge/tests/res/testApp/MyApplication/golden/view_boundaries.png b/bridge/tests/res/testApp/MyApplication/golden/view_boundaries.png
index b9a5001674..da770e1f94 100644
--- a/bridge/tests/res/testApp/MyApplication/golden/view_boundaries.png
+++ b/bridge/tests/res/testApp/MyApplication/golden/view_boundaries.png
Binary files differ
diff --git a/bridge/tests/res/testApp/MyApplication/golden/view_stub.png b/bridge/tests/res/testApp/MyApplication/golden/view_stub.png
index 7e32c13d2d..b66ff2d524 100644
--- a/bridge/tests/res/testApp/MyApplication/golden/view_stub.png
+++ b/bridge/tests/res/testApp/MyApplication/golden/view_stub.png
Binary files differ
diff --git a/bridge/tests/res/testApp/MyApplication/golden/window_background.png b/bridge/tests/res/testApp/MyApplication/golden/window_background.png
new file mode 100644
index 0000000000..1d25725e4e
--- /dev/null
+++ b/bridge/tests/res/testApp/MyApplication/golden/window_background.png
Binary files differ
diff --git a/bridge/tests/res/testApp/MyApplication/src/main/myapplication.widgets/DialogView.java b/bridge/tests/res/testApp/MyApplication/src/main/myapplication.widgets/DialogView.java
new file mode 100644
index 0000000000..983b96c096
--- /dev/null
+++ b/bridge/tests/res/testApp/MyApplication/src/main/myapplication.widgets/DialogView.java
@@ -0,0 +1,59 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.layoutlib.test.myapplication.widgets;
+
+import android.app.AlertDialog.Builder;
+import android.app.Dialog;
+import android.content.Context;
+import android.util.AttributeSet;
+import android.view.View;
+
+public class DialogView extends View {
+ private Dialog dialog;
+
+ public DialogView(Context context) {
+ super(context);
+ init(context);
+ }
+
+ public DialogView(Context context, AttributeSet attrs) {
+ super(context, attrs);
+ init(context);
+ }
+
+ public DialogView(Context context, AttributeSet attrs, int defStyle) {
+ super(context, attrs, defStyle);
+ init(context);
+ }
+
+ private void init(Context context) {
+ Builder builder = new Builder(context);
+ dialog = builder.setMessage("My message")
+ .setTitle("My title")
+ .setPositiveButton("Ok", (dialog, id) -> {})
+ .setNegativeButton("Cancel", (dialog, id) -> {})
+ .create();
+ }
+
+ @Override
+ protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
+ // This creates the dialog view and adds it to the hierarchy before
+ // measuring happens.
+ dialog.show();
+ super.onMeasure(widthMeasureSpec, heightMeasureSpec);
+ }
+}
diff --git a/bridge/tests/res/testApp/MyApplication/src/main/res/drawable-nodpi/ninepatch.9.png b/bridge/tests/res/testApp/MyApplication/src/main/res/drawable-nodpi/ninepatch.9.png
index fb3660eab1..c0bbc81f18 100644
--- a/bridge/tests/res/testApp/MyApplication/src/main/res/drawable-nodpi/ninepatch.9.png
+++ b/bridge/tests/res/testApp/MyApplication/src/main/res/drawable-nodpi/ninepatch.9.png
Binary files differ
diff --git a/bridge/tests/res/testApp/MyApplication/src/main/res/drawable-nodpi/uncompiled_ninepatch.9.png b/bridge/tests/res/testApp/MyApplication/src/main/res/drawable-nodpi/uncompiled_ninepatch.9.png
new file mode 100644
index 0000000000..fb3660eab1
--- /dev/null
+++ b/bridge/tests/res/testApp/MyApplication/src/main/res/drawable-nodpi/uncompiled_ninepatch.9.png
Binary files differ
diff --git a/bridge/tests/res/testApp/MyApplication/src/main/res/drawable/ninepatch_drawable.xml b/bridge/tests/res/testApp/MyApplication/src/main/res/drawable/ninepatch_drawable.xml
new file mode 100644
index 0000000000..c14388d685
--- /dev/null
+++ b/bridge/tests/res/testApp/MyApplication/src/main/res/drawable/ninepatch_drawable.xml
@@ -0,0 +1,19 @@
+<!--
+ ~ Copyright (C) 2019 The Android Open Source Project
+ ~
+ ~ Licensed under the Apache License, Version 2.0 (the "License");
+ ~ you may not use this file except in compliance with the License.
+ ~ You may obtain a copy of the License at
+ ~
+ ~ http://www.apache.org/licenses/LICENSE-2.0
+ ~
+ ~ Unless required by applicable law or agreed to in writing, software
+ ~ distributed under the License is distributed on an "AS IS" BASIS,
+ ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ ~ See the License for the specific language governing permissions and
+ ~ limitations under the License.
+ -->
+
+<nine-patch
+ xmlns:android="http://schemas.android.com/apk/res/android"
+ android:src="@drawable/uncompiled_ninepatch" /> \ No newline at end of file
diff --git a/bridge/tests/res/testApp/MyApplication/src/main/res/drawable/theme_attribute_drawable.xml b/bridge/tests/res/testApp/MyApplication/src/main/res/drawable/theme_attribute_drawable.xml
new file mode 100644
index 0000000000..c3bfe66a07
--- /dev/null
+++ b/bridge/tests/res/testApp/MyApplication/src/main/res/drawable/theme_attribute_drawable.xml
@@ -0,0 +1,20 @@
+<!--
+ ~ 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">
+ <item android:drawable="?android:attr/colorAccent" />
+</layer-list> \ No newline at end of file
diff --git a/bridge/tests/res/testApp/MyApplication/src/main/res/drawable/transparent_drawable.xml b/bridge/tests/res/testApp/MyApplication/src/main/res/drawable/transparent_drawable.xml
new file mode 100644
index 0000000000..e7cbc06a45
--- /dev/null
+++ b/bridge/tests/res/testApp/MyApplication/src/main/res/drawable/transparent_drawable.xml
@@ -0,0 +1,24 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ ~ Copyright (C) 2020 The Android Open Source Project
+ ~
+ ~ Licensed under the Apache License, Version 2.0 (the "License");
+ ~ you may not use this file except in compliance with the License.
+ ~ You may obtain a copy of the License at
+ ~
+ ~ http://www.apache.org/licenses/LICENSE-2.0
+ ~
+ ~ Unless required by applicable law or agreed to in writing, software
+ ~ distributed under the License is distributed on an "AS IS" BASIS,
+ ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ ~ See the License for the specific language governing permissions and
+ ~ limitations under the License.
+ -->
+
+<shape xmlns:android="http://schemas.android.com/apk/res/android"
+ android:shape="rectangle">
+ <solid android:color="#55FF0000"/>
+ <size
+ android:width="48dp"
+ android:height="48dp"/>
+</shape> \ No newline at end of file
diff --git a/bridge/tests/res/testApp/MyApplication/src/main/res/layout/a11y_scrolling_view_test.xml b/bridge/tests/res/testApp/MyApplication/src/main/res/layout/a11y_scrolling_view_test.xml
new file mode 100644
index 0000000000..f8ef1456c5
--- /dev/null
+++ b/bridge/tests/res/testApp/MyApplication/src/main/res/layout/a11y_scrolling_view_test.xml
@@ -0,0 +1,44 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ ~ Copyright (C) 2020 The Android Open Source Project
+ ~
+ ~ Licensed under the Apache License, Version 2.0 (the "License");
+ ~ you may not use this file except in compliance with the License.
+ ~ You may obtain a copy of the License at
+ ~
+ ~ http://www.apache.org/licenses/LICENSE-2.0
+ ~
+ ~ Unless required by applicable law or agreed to in writing, software
+ ~ distributed under the License is distributed on an "AS IS" BASIS,
+ ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ ~ See the License for the specific language governing permissions and
+ ~ limitations under the License.
+ -->
+
+<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:app="http://schemas.android.com/apk/res-auto"
+ xmlns:tools="http://schemas.android.com/tools"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent">
+
+ <TextView
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+
+ android:text="vertical_scrollview_example"
+ android:id="@+id/textView"
+ android:layout_gravity="center_horizontal"
+ android:layout_centerHorizontal="true"
+ android:layout_alignParentTop="true" />
+
+ <ScrollView
+ android:layout_marginTop="30dp"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:id="@+id/scrollView">
+
+
+ </ScrollView>
+
+</RelativeLayout>
+
diff --git a/bridge/tests/res/testApp/MyApplication/src/main/res/layout/a11y_test_switch_text_contrast.xml b/bridge/tests/res/testApp/MyApplication/src/main/res/layout/a11y_test_switch_text_contrast.xml
new file mode 100644
index 0000000000..db408f1b3b
--- /dev/null
+++ b/bridge/tests/res/testApp/MyApplication/src/main/res/layout/a11y_test_switch_text_contrast.xml
@@ -0,0 +1,34 @@
+<?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.
+ -->
+
+<LinearLayout
+ xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:tools="http://schemas.android.com/tools"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ android:background="#FFFFFFFF">
+ <!-- Low contrast, with slightly transparent text -->
+ <Switch
+ android:text="Switch"
+ android:textColor="#3000FFFF"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:minHeight="48dp"
+ android:id="@+id/switch1"
+ tools:layout_editor_absoluteY="191dp"
+ tools:layout_editor_absoluteX="104dp" />
+</LinearLayout> \ No newline at end of file
diff --git a/bridge/tests/res/testApp/MyApplication/src/main/res/layout/fonts_test.xml b/bridge/tests/res/testApp/MyApplication/src/main/res/layout/fonts_test.xml
index c63b211c96..67869c89ec 100644
--- a/bridge/tests/res/testApp/MyApplication/src/main/res/layout/fonts_test.xml
+++ b/bridge/tests/res/testApp/MyApplication/src/main/res/layout/fonts_test.xml
@@ -23,7 +23,8 @@
android:layout_height="wrap_content"
android:text="MONOSPACE"
android:textSize="50sp"
- android:fontFamily="monospace"/>
+ android:fontFamily="monospace"
+ android:rotationX="45"/>
<Space
android:layout_width="wrap_content"
diff --git a/bridge/tests/res/testApp/MyApplication/src/main/res/layout/justified_inter_word.xml b/bridge/tests/res/testApp/MyApplication/src/main/res/layout/justified_inter_word.xml
new file mode 100644
index 0000000000..6e9f183a1e
--- /dev/null
+++ b/bridge/tests/res/testApp/MyApplication/src/main/res/layout/justified_inter_word.xml
@@ -0,0 +1,12 @@
+<?xml version="1.0" encoding="utf-8"?>
+<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
+ android:layout_width="match_parent" android:layout_height="match_parent">
+
+ <TextView
+ android:id="@+id/justified"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:justificationMode="inter_word"
+ android:text="@string/large_text"/>
+
+</FrameLayout> \ No newline at end of file
diff --git a/bridge/tests/res/testApp/MyApplication/src/main/res/layout/justified_none.xml b/bridge/tests/res/testApp/MyApplication/src/main/res/layout/justified_none.xml
new file mode 100644
index 0000000000..ffd757e93d
--- /dev/null
+++ b/bridge/tests/res/testApp/MyApplication/src/main/res/layout/justified_none.xml
@@ -0,0 +1,12 @@
+<?xml version="1.0" encoding="utf-8"?>
+<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
+ android:layout_width="match_parent" android:layout_height="match_parent">
+
+ <TextView
+ android:id="@+id/justified"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:justificationMode="none"
+ android:text="@string/large_text"/>
+
+</FrameLayout> \ No newline at end of file
diff --git a/bridge/tests/res/testApp/MyApplication/src/main/res/layout/layout.xml b/bridge/tests/res/testApp/MyApplication/src/main/res/layout/layout.xml
index ff14ce0a62..568279b13a 100644
--- a/bridge/tests/res/testApp/MyApplication/src/main/res/layout/layout.xml
+++ b/bridge/tests/res/testApp/MyApplication/src/main/res/layout/layout.xml
@@ -16,9 +16,11 @@
android:layout_gravity="center"
/>
<com.android.layoutlib.test.myapplication.widgets.CustomDate
+ android:firstDayOfWeek="1"
android:layout_width="100dp"
android:layout_height="wrap_content"/>
<com.android.layoutlib.test.myapplication.widgets.CustomCalendar
+ android:firstDayOfWeek="1"
android:layout_width="200dp"
android:layout_gravity="center_horizontal"
android:layout_height="200dp"/>
diff --git a/bridge/tests/res/testApp/MyApplication/src/main/res/values/strings.xml b/bridge/tests/res/testApp/MyApplication/src/main/res/values/strings.xml
index 2b7083bfef..f4ff361ae4 100644
--- a/bridge/tests/res/testApp/MyApplication/src/main/res/values/strings.xml
+++ b/bridge/tests/res/testApp/MyApplication/src/main/res/values/strings.xml
@@ -4,5 +4,94 @@
<string name="app_name">My Application</string>
<string name="hello_world">Hello world!</string>
<string name="action_settings">Settings</string>
+ <string name="large_text">
+ "Material is the metaphor.\n\n"
+
+ "A material metaphor is the unifying theory of a rationalized space and a system of motion."
+ "The material is grounded in tactile reality, inspired by the study of paper and ink, yet "
+ "technologically advanced and open to imagination and magic.\n"
+ "Surfaces and edges of the material provide visual cues that are grounded in reality. The "
+ "use of familiar tactile attributes helps users quickly understand affordances. Yet the "
+ "flexibility of the material creates new affordances that supercede those in the physical "
+ "world, without breaking the rules of physics.\n"
+ "The fundamentals of light, surface, and movement are key to conveying how objects move, "
+ "interact, and exist in space and in relation to each other. Realistic lighting shows "
+ "seams, divides space, and indicates moving parts.\n\n"
+
+ "Bold, graphic, intentional.\n\n"
+
+ "The foundational elements of print based design typography, grids, space, scale, color, "
+ "and use of imagery guide visual treatments. These elements do far more than please the "
+ "eye. They create hierarchy, meaning, and focus. Deliberate color choices, edge to edge "
+ "imagery, large scale typography, and intentional white space create a bold and graphic "
+ "interface that immerse the user in the experience.\n"
+ "An emphasis on user actions makes core functionality immediately apparent and provides "
+ "waypoints for the user.\n\n"
+
+ "Motion provides meaning.\n\n"
+
+ "Motion respects and reinforces the user as the prime mover. Primary user actions are "
+ "inflection points that initiate motion, transforming the whole design.\n"
+ "All action takes place in a single environment. Objects are presented to the user without "
+ "breaking the continuity of experience even as they transform and reorganize.\n"
+ "Motion is meaningful and appropriate, serving to focus attention and maintain continuity. "
+ "Feedback is subtle yet clear. Transitions are efficient yet coherent.\n\n"
+
+ "3D world.\n\n"
+
+ "The material environment is a 3D space, which means all objects have x, y, and z "
+ "dimensions. The z-axis is perpendicularly aligned to the plane of the display, with the "
+ "positive z-axis extending towards the viewer. Every sheet of material occupies a single "
+ "position along the z-axis and has a standard 1dp thickness.\n"
+ "On the web, the z-axis is used for layering and not for perspective. The 3D world is "
+ "emulated by manipulating the y-axis.\n\n"
+
+ "Light and shadow.\n\n"
+
+ "Within the material environment, virtual lights illuminate the scene. Key lights create "
+ "directional shadows, while ambient light creates soft shadows from all angles.\n"
+ "Shadows in the material environment are cast by these two light sources. In Android "
+ "development, shadows occur when light sources are blocked by sheets of material at "
+ "various positions along the z-axis. On the web, shadows are depicted by manipulating the "
+ "y-axis only. The following example shows the card with a height of 6dp.\n\n"
+
+ "Resting elevation.\n\n"
+
+ "All material objects, regardless of size, have a resting elevation, or default elevation "
+ "that does not change. If an object changes elevation, it should return to its resting "
+ "elevation as soon as possible.\n\n"
+
+ "Component elevations.\n\n"
+
+ "The resting elevation for a component type is consistent across apps (e.g., FAB elevation "
+ "does not vary from 6dp in one app to 16dp in another app).\n"
+ "Components may have different resting elevations across platforms, depending on the depth "
+ "of the environment (e.g., TV has a greater depth than mobile or desktop).\n\n"
+
+ "Responsive elevation and dynamic elevation offsets.\n\n"
+
+ "Some component types have responsive elevation, meaning they change elevation in response "
+ "to user input (e.g., normal, focused, and pressed) or system events. These elevation "
+ "changes are consistently implemented using dynamic elevation offsets.\n"
+ "Dynamic elevation offsets are the goal elevation that a component moves towards, relative "
+ "to the component’s resting state. They ensure that elevation changes are consistent "
+ "across actions and component types. For example, all components that lift on press have "
+ "the same elevation change relative to their resting elevation.\n"
+ "Once the input event is completed or cancelled, the component will return to its resting "
+ "elevation.\n\n"
+
+ "Avoiding elevation interference.\n\n"
+
+ "Components with responsive elevations may encounter other components as they move between "
+ "their resting elevations and dynamic elevation offsets. Because material cannot pass "
+ "through other material, components avoid interfering with one another any number of ways, "
+ "whether on a per component basis or using the entire app layout.\n"
+ "On a component level, components can move or be removed before they cause interference. "
+ "For example, a floating action button (FAB) can disappear or move off screen before a "
+ "user picks up a card, or it can move if a snackbar appears.\n"
+ "On the layout level, design your app layout to minimize opportunities for interference. "
+ "For example, position the FAB to one side of stream of a cards so the FAB won’t interfere "
+ "when a user tries to pick up one of cards.\n\n"
+ </string>
</resources>
diff --git a/bridge/tests/res/testApp/MyApplication/src/main/res/values/styles.xml b/bridge/tests/res/testApp/MyApplication/src/main/res/values/styles.xml
index 9e84a53bed..7b338d1dc6 100644
--- a/bridge/tests/res/testApp/MyApplication/src/main/res/values/styles.xml
+++ b/bridge/tests/res/testApp/MyApplication/src/main/res/values/styles.xml
@@ -11,4 +11,9 @@
<item name="android:layout_height">150dp</item>
</style>
+ <style name="WindowBackgroundTheme" parent="android:Theme.Material.Light.DarkActionBar">
+ <item name="android:colorAccent">#ffff0000</item>
+ <item name="android:windowBackground">@drawable/theme_attribute_drawable</item>
+ </style>
+
</resources>
diff --git a/bridge/tests/run_tests.sh b/bridge/tests/run_tests.sh
index 3c689213fd..de2ed79283 100755
--- a/bridge/tests/run_tests.sh
+++ b/bridge/tests/run_tests.sh
@@ -12,9 +12,40 @@ readonly FAILURE_ZIP=layoutlib-test-failures.zip
STUDIO_JDK=${SCRIPT_DIR}"/../../../../prebuilts/jdk/jdk11/linux-x86"
MISC_COMMON=${SCRIPT_DIR}"/../../../../prebuilts/misc/common"
OUT_INTERMEDIATES=${SCRIPT_DIR}"/../../../../out/soong/.intermediates"
+NATIVE_LIBRARIES=${SCRIPT_DIR}"/../../../../out/host/linux-x86/lib64/"
+SDK=${SCRIPT_DIR}"/../../../../out/host/linux-x86/sdk/sdk*/android-sdk*"
+SDK_REPO=${SCRIPT_DIR}"/../../../../out/host/linux-x86/sdk-repo"
+FONT_DIR=${SCRIPT_DIR}"/../../../../out/host/common/obj/PACKAGING/fonts_intermediates"
+ICU_DATA_PATH=${SCRIPT_DIR}"/../../../../out/host/linux-x86/com.android.i18n/etc/icu/icudt70l.dat"
+TMP_DIR=$(mktemp -d)
+PLATFORM=${TMP_DIR}/"android"
+
+# Copy resources to a temp directory
+cp -r ${SDK}/platforms/android* ${PLATFORM}
+
+# Unzip build-tools to access aapt2
+mkdir ${TMP_DIR}/build-tools
+unzip -q ${SDK_REPO}/sdk-repo-linux-build-tools.zip -d ${TMP_DIR}/build-tools
+
+# Compile 9-patch files
+mkdir ${TMP_DIR}/compiled
+mkdir ${TMP_DIR}/manifest
+echo \
+'<?xml version="1.0" encoding="utf-8"?>
+<manifest xmlns:android="http://schemas.android.com/apk/res/android" package="com.google.android.layoutlib" />' \
+> ${TMP_DIR}/manifest/AndroidManifest.xml
+find ${SDK}/platforms/android*/data/res -name "*.9.png" -print0 | xargs -0 ${TMP_DIR}/build-tools/android-*/aapt2 compile -o ${TMP_DIR}/compiled/
+find ${TMP_DIR}/compiled -name "*.flat" -print0 | xargs -0 -s 1000000 ${TMP_DIR}/build-tools/android-*/aapt2 link -o ${TMP_DIR}/compiled.apk --manifest ${TMP_DIR}/manifest/AndroidManifest.xml -R
+unzip -q ${TMP_DIR}/compiled.apk -d ${TMP_DIR}
+for f in ${TMP_DIR}/res/*; do mv "$f" "${f/-v4/}";done
+cp -RL ${TMP_DIR}/res ${PLATFORM}/data
# Run layoutlib tests
${STUDIO_JDK}/bin/java -ea \
+ -Dnative.lib.path=${NATIVE_LIBRARIES} \
+ -Dfont.dir=${FONT_DIR} \
+ -Dicu.data.path=${ICU_DATA_PATH} \
+ -Dplatform.dir=${PLATFORM} \
-Dtest_res.dir=${SCRIPT_DIR}/res \
-Dtest_failure.dir=${OUT_DIR}/${FAILURE_DIR} \
-cp ${MISC_COMMON}/tools-common/tools-common-prebuilt.jar:${MISC_COMMON}/ninepatch/ninepatch-prebuilt.jar:${MISC_COMMON}/sdk-common/sdk-common.jar:${MISC_COMMON}/kxml2/kxml2-2.3.0.jar:${MISC_COMMON}/layoutlib_api/layoutlib_api-prebuilt.jar:${OUT_INTERMEDIATES}/prebuilts/tools/common/m2/trove-prebuilt/linux_glibc_common/combined/trove-prebuilt.jar:${OUT_INTERMEDIATES}/external/junit/junit/linux_glibc_common/javac/junit.jar:${OUT_INTERMEDIATES}/external/guava/guava-jre/linux_glibc_common/javac/guava-jre.jar:${OUT_INTERMEDIATES}/external/hamcrest/hamcrest-core/hamcrest/linux_glibc_common/javac/hamcrest.jar:${OUT_INTERMEDIATES}/external/mockito/mockito/linux_glibc_common/combined/mockito.jar:${OUT_INTERMEDIATES}/external/objenesis/objenesis/linux_glibc_common/javac/objenesis.jar:${OUT_INTERMEDIATES}/frameworks/layoutlib/bridge/layoutlib/linux_glibc_common/withres/layoutlib.jar:${OUT_INTERMEDIATES}/frameworks/layoutlib/temp_layoutlib/linux_glibc_common/gen/temp_layoutlib.jar:${OUT_INTERMEDIATES}/frameworks/layoutlib/bridge/tests/layoutlib-tests/linux_glibc_common/withres/layoutlib-tests.jar \
@@ -33,4 +64,8 @@ if [[ -d "${DIST_DIR}" ]] && [[ -e "${OUT_DIR}/${FAILURE_ZIP}" ]]; then
mv ${OUT_DIR}/${FAILURE_ZIP} ${DIST_DIR}
fi
+# Clean
+rm -rf ${TMP_DIR}
+rm -rf ${OUT_DIR}/${FAILURE_DIR}
+
exit ${test_exit_code}
diff --git a/bridge/tests/run_tests_mac.sh b/bridge/tests/run_tests_mac.sh
new file mode 100755
index 0000000000..0649f254f4
--- /dev/null
+++ b/bridge/tests/run_tests_mac.sh
@@ -0,0 +1,82 @@
+#!/bin/bash
+
+# There is no macOS build of the SDK anymore
+# Do not run layoutlib tests
+exit 0
+
+readonly OUT_DIR="$1"
+readonly DIST_DIR="$2"
+readonly BUILD_NUMBER="$3"
+
+readonly SCRIPT_DIR="$(dirname "$0")"
+
+readonly FAILURE_DIR=layoutlib-test-failures
+readonly FAILURE_ZIP=layoutlib-test-failures.zip
+
+STUDIO_JDK=${SCRIPT_DIR}"/../../../../prebuilts/jdk/jdk11/darwin-x86"
+MISC_COMMON=${SCRIPT_DIR}"/../../../../prebuilts/misc/common"
+OUT_INTERMEDIATES=${SCRIPT_DIR}"/../../../../out/soong/.intermediates"
+NATIVE_LIBRARIES=${SCRIPT_DIR}"/../../../../out/host/darwin-x86/lib64/"
+SDK=${SCRIPT_DIR}"/../../../../out/host/darwin-x86/sdk/sdk*/android-sdk*"
+SDK_REPO=${SCRIPT_DIR}"/../../../../out/soong/host/linux-x86/sdk-repo"
+FONT_DIR=${SCRIPT_DIR}"/../../../../out/host/common/obj/PACKAGING/fonts_intermediates"
+ICU_DATA_PATH=${SCRIPT_DIR}"/../../../../out/host/darwin-x86/com.android.i18n/etc/icu/icudt69l.dat"
+TMP_DIR=$(mktemp -d -t tmp)
+PLATFORM=${TMP_DIR}/"android"
+
+# Copy resources to a temp directory
+cp -r ${SDK}/platforms/android* ${PLATFORM}
+
+# Unzip build-tools to access aapt2
+mkdir ${TMP_DIR}/build-tools
+unzip -q ${SDK_REPO}/sdk-repo-linux-build-tools.zip -d ${TMP_DIR}/build-tools
+
+# Compile 9-patch files
+mkdir ${TMP_DIR}/compiled
+mkdir ${TMP_DIR}/manifest
+echo \
+'<?xml version="1.0" encoding="utf-8"?>
+<manifest xmlns:android="http://schemas.android.com/apk/res/android" package="com.google.android.layoutlib" />' \
+> ${TMP_DIR}/manifest/AndroidManifest.xml
+for f in ${SDK}/platforms/android*/data/res/*
+do
+ find $f -name "*.9.png" -print0 | xargs -0 ${TMP_DIR}/build-tools/android-*/aapt2 compile -o ${TMP_DIR}/compiled/
+ find ${TMP_DIR}/compiled -name "*.flat" -print0 | xargs -0 ${TMP_DIR}/build-tools/android-*/aapt2 link -o ${TMP_DIR}/compiled.apk --manifest ${TMP_DIR}/manifest/AndroidManifest.xml -R
+ if [[ -f "${TMP_DIR}/compiled.apk" ]]; then
+ unzip -qo ${TMP_DIR}/compiled.apk -d ${TMP_DIR}
+ rm -r ${TMP_DIR}/compiled/*
+ rm ${TMP_DIR}/compiled.apk
+ fi
+done
+for f in ${TMP_DIR}/res/*; do mv "$f" "${f/-v4/}";done
+cp -RL ${TMP_DIR}/res ${PLATFORM}/data
+
+# Run layoutlib tests
+${STUDIO_JDK}/bin/java -ea \
+ -Dnative.lib.path=${NATIVE_LIBRARIES} \
+ -Dfont.dir=${FONT_DIR} \
+ -Dicu.data.path=${ICU_DATA_PATH} \
+ -Dplatform.dir=${PLATFORM} \
+ -Dtest_res.dir=${SCRIPT_DIR}/res \
+ -Dtest_failure.dir=${OUT_DIR}/${FAILURE_DIR} \
+ -cp ${MISC_COMMON}/tools-common/tools-common-prebuilt.jar:${MISC_COMMON}/ninepatch/ninepatch-prebuilt.jar:${MISC_COMMON}/sdk-common/sdk-common.jar:${MISC_COMMON}/kxml2/kxml2-2.3.0.jar:${MISC_COMMON}/layoutlib_api/layoutlib_api-prebuilt.jar:${OUT_INTERMEDIATES}/prebuilts/tools/common/m2/trove-prebuilt/darwin_common/combined/trove-prebuilt.jar:${OUT_INTERMEDIATES}/external/junit/junit/darwin_common/javac/junit.jar:${OUT_INTERMEDIATES}/external/guava/guava-jre/darwin_common/javac/guava-jre.jar:${OUT_INTERMEDIATES}/external/hamcrest/hamcrest-core/hamcrest/darwin_common/javac/hamcrest.jar:${OUT_INTERMEDIATES}/external/mockito/mockito/darwin_common/combined/mockito.jar:${OUT_INTERMEDIATES}/external/objenesis/objenesis/darwin_common/javac/objenesis.jar:${OUT_INTERMEDIATES}/frameworks/layoutlib/bridge/layoutlib/darwin_common/withres/layoutlib.jar:${OUT_INTERMEDIATES}/frameworks/layoutlib/temp_layoutlib/darwin_common/gen/temp_layoutlib.jar:${OUT_INTERMEDIATES}/frameworks/layoutlib/bridge/tests/layoutlib-tests/darwin_common/withres/layoutlib-tests.jar \
+ org.junit.runner.JUnitCore \
+ com.android.layoutlib.bridge.intensive.Main
+
+test_exit_code=$?
+
+# Create zip of all failure screenshots
+if [[ -d "${OUT_DIR}/${FAILURE_DIR}" ]]; then
+ zip -q -j -r ${OUT_DIR}/${FAILURE_ZIP} ${OUT_DIR}/${FAILURE_DIR}
+fi
+
+# Move failure zip to dist directory if specified
+if [[ -d "${DIST_DIR}" ]] && [[ -e "${OUT_DIR}/${FAILURE_ZIP}" ]]; then
+ mv ${OUT_DIR}/${FAILURE_ZIP} ${DIST_DIR}
+fi
+
+# Clean
+rm -rf ${TMP_DIR}
+rm -rf ${OUT_DIR}/${FAILURE_DIR}
+
+exit ${test_exit_code}
diff --git a/bridge/tests/src/android/content/res/BridgeTypedArrayTest.java b/bridge/tests/src/android/content/res/BridgeTypedArrayTest.java
index 0be3423624..39759279a3 100644
--- a/bridge/tests/src/android/content/res/BridgeTypedArrayTest.java
+++ b/bridge/tests/src/android/content/res/BridgeTypedArrayTest.java
@@ -38,6 +38,7 @@ public class BridgeTypedArrayTest {
@Test
public void getType() {
assertEquals(TYPE_NULL, BridgeTypedArray.getType(null));
+ assertEquals(TYPE_STRING, BridgeTypedArray.getType(""));
assertEquals(TYPE_REFERENCE, BridgeTypedArray.getType("@drawable/my_drawable"));
assertEquals(TYPE_ATTRIBUTE, BridgeTypedArray.getType("?attr/colorPrimary"));
assertEquals(TYPE_INT_BOOLEAN, BridgeTypedArray.getType("true"));
@@ -50,6 +51,7 @@ public class BridgeTypedArrayTest {
assertEquals(TYPE_INT_COLOR_ARGB8, BridgeTypedArray.getType("#1f34dc28"));
assertEquals(TYPE_STRING, BridgeTypedArray.getType("#notacolor"));
assertEquals(TYPE_DIMENSION, BridgeTypedArray.getType("16dp"));
+ assertEquals(TYPE_DIMENSION, BridgeTypedArray.getType(".16dp"));
assertEquals(TYPE_STRING, BridgeTypedArray.getType("16notaunit"));
assertEquals(TYPE_INT_DEC, BridgeTypedArray.getType("98543"));
assertEquals(TYPE_FLOAT, BridgeTypedArray.getType("43.364"));
diff --git a/bridge/tests/src/android/graphics/Color_DelegateTest.java b/bridge/tests/src/android/graphics/Color_DelegateTest.java
deleted file mode 100644
index 1b9c13e177..0000000000
--- a/bridge/tests/src/android/graphics/Color_DelegateTest.java
+++ /dev/null
@@ -1,37 +0,0 @@
-/*
- * 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.
- */
-
-package android.graphics;
-
-import junit.framework.TestCase;
-
-public class Color_DelegateTest extends TestCase {
-
- public void testRGBToHSV() {
- float[] hsv = new float[3];
- Color_Delegate.nativeRGBToHSV(14, 203, 49, hsv);
- assertTrue(131.1111 - hsv[0] < 0.001);
- assertTrue(0.9310 - hsv[1] < 0.001);
- assertTrue(0.7961 - hsv[2] < 0.001);
- }
-
- public void testHSVToColor() {
- assertEquals(2077003642,
- Color_Delegate.nativeHSVToColor(123, new float[]{15.0f, 0.4f, 0.8f}));
- assertEquals(603979776,
- Color_Delegate.nativeHSVToColor(36, new float[]{15.0f, 25.0f, -17.0f}));
- }
-}
diff --git a/bridge/tests/src/android/graphics/Matrix_DelegateTest.java b/bridge/tests/src/android/graphics/Matrix_DelegateTest.java
deleted file mode 100644
index 08861322b5..0000000000
--- a/bridge/tests/src/android/graphics/Matrix_DelegateTest.java
+++ /dev/null
@@ -1,66 +0,0 @@
-/*
- * Copyright (C) 2008 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package android.graphics;
-
-import java.util.Arrays;
-
-import junit.framework.TestCase;
-
-/**
- *
- */
-public class Matrix_DelegateTest extends TestCase {
-
- public void testIdentity() {
- Matrix m1 = new Matrix();
-
- assertTrue(m1.isIdentity());
-
- m1.setValues(new float[]{1, 0, 0, 0, 1, 0, 0, 0, 1});
- assertTrue(m1.isIdentity());
- }
-
- public void testCopyConstructor() {
- Matrix m1 = new Matrix();
- Matrix m2 = new Matrix(m1);
-
- float[] v1 = new float[9];
- float[] v2 = new float[9];
- m1.getValues(v1);
- m2.getValues(v2);
-
- for (int i = 0; i < 9; i++) {
- assertEquals(v1[i], v2[i]);
- }
- }
-
- public void testInvert() {
- Matrix m1 = new Matrix();
- Matrix inverse = new Matrix();
- m1.setValues(new float[]{1, 2, 3, 4, 5, 6, 7, 8, 9});
- assertFalse(m1.invert(inverse));
-
- m1.setValues(new float[]{3, 5, 6, 2, 5, 7, 4, 8, 2});
- m1.invert(inverse);
- float[] values = new float[9];
- inverse.getValues(values);
-
- assertTrue(Arrays.equals(values,
- new float[]{1.0952381f, -0.9047619f, -0.11904762f, -0.5714286f, 0.42857143f,
- 0.21428572f, 0.0952381f, 0.0952381f, -0.11904762f}));
- }
-}
diff --git a/bridge/tests/src/android/util/imagepool/ImagePoolHelperTest.java b/bridge/tests/src/android/util/imagepool/ImagePoolHelperTest.java
deleted file mode 100644
index 25aa466631..0000000000
--- a/bridge/tests/src/android/util/imagepool/ImagePoolHelperTest.java
+++ /dev/null
@@ -1,108 +0,0 @@
-/*
- * 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.
- */
-
-package android.util.imagepool;
-
-import org.junit.Test;
-
-import android.util.imagepool.Bucket.BucketCreationMetaData;
-import android.util.imagepool.ImagePool.Image.Orientation;
-
-import java.awt.image.BufferedImage;
-import java.lang.ref.SoftReference;
-
-import static org.junit.Assert.assertNotNull;
-import static org.junit.Assert.assertNull;
-
-public class ImagePoolHelperTest {
-
- @Test
- public void testGetBufferedImage() {
- int width = 10;
- int height = 10;
- int numberOfCopiesInBucket = 10;
- int maxCacheSize = width * height * 4 * 5; // can fit 5 width | height buffer
- Bucket bucket = new Bucket();
- BucketCreationMetaData metaData = new BucketCreationMetaData(
- width, height, BufferedImage.TYPE_INT_ARGB, numberOfCopiesInBucket, Orientation
- .NONE, maxCacheSize);
- ImagePoolStats stats = new ImagePoolStatsProdImpl();
-
- assertNotNull(ImagePoolHelper.getBufferedImage(bucket, metaData, stats));
- }
-
- @Test
- public void testGetBufferedImageRecurse() {
- int width = 10;
- int height = 10;
- int numberOfCopiesToRequestInBucket = 1;
- int numberOfCopiesInBucket = 10;
- int maxCacheSize = width * height * 4 * numberOfCopiesToRequestInBucket;
-
- Bucket bucket = new Bucket();
- for (int i = 0; i < numberOfCopiesInBucket; i++) {
- bucket.mBufferedImageRef.add(new SoftReference<>(null));
- }
- BucketCreationMetaData metaData = new BucketCreationMetaData(
- width, height, BufferedImage.TYPE_INT_ARGB, numberOfCopiesToRequestInBucket, Orientation
- .NONE, maxCacheSize);
- ImagePoolStats stats = new ImagePoolStatsProdImpl();
-
- assertNotNull(ImagePoolHelper.getBufferedImage(bucket, metaData, stats));
- }
-
- @Test
- public void testRecurseThenHitCacheLimit() {
- int width = 10;
- int height = 10;
- int numberOfCopiesToRequestInBucket = 1;
- int numberOfCopiesInBucket = 10;
- int maxCacheSize = width * height * 4 * numberOfCopiesToRequestInBucket / 2;
-
- Bucket bucket = new Bucket();
- for (int i = 0; i < numberOfCopiesInBucket; i++) {
- bucket.mBufferedImageRef.add(new SoftReference<>(null));
- }
- BucketCreationMetaData metaData = new BucketCreationMetaData(
- width, height, BufferedImage.TYPE_INT_ARGB, numberOfCopiesToRequestInBucket, Orientation
- .NONE, maxCacheSize);
- ImagePoolStats stats = new ImagePoolStatsProdImpl();
-
- assertNull(ImagePoolHelper.getBufferedImage(bucket, metaData, stats));
- }
-
- @Test
- public void testBucketHasImageToReturn() {
- int width = 10;
- int height = 10;
- int numberOfCopiesToRequestInBucket = 1;
- int numberOfCopiesInBucket = 10;
- int maxCacheSize = width * height * 4 * numberOfCopiesToRequestInBucket / 2;
- BufferedImage image = new BufferedImage(width, height, BufferedImage.TYPE_4BYTE_ABGR);
-
- Bucket bucket = new Bucket();
- for (int i = 0; i < numberOfCopiesInBucket; i++) {
- bucket.mBufferedImageRef.add(new SoftReference<>(null));
- }
- bucket.mBufferedImageRef.add(new SoftReference<>(image));
- BucketCreationMetaData metaData = new BucketCreationMetaData(
- width, height, BufferedImage.TYPE_INT_ARGB, numberOfCopiesToRequestInBucket, Orientation
- .NONE, maxCacheSize);
- ImagePoolStats stats = new ImagePoolStatsProdImpl();
-
- assertNotNull(ImagePoolHelper.getBufferedImage(bucket, metaData, stats));
- }
-} \ No newline at end of file
diff --git a/bridge/tests/src/android/util/imagepool/ImagePoolImplTest.java b/bridge/tests/src/android/util/imagepool/ImagePoolImplTest.java
deleted file mode 100644
index 976109775c..0000000000
--- a/bridge/tests/src/android/util/imagepool/ImagePoolImplTest.java
+++ /dev/null
@@ -1,306 +0,0 @@
-/*
- * 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.
- */
-
-package android.util.imagepool;
-
-import org.junit.Ignore;
-import org.junit.Test;
-
-import android.util.imagepool.ImagePool.Image;
-import android.util.imagepool.ImagePool.ImagePoolPolicy;
-
-import java.awt.image.BufferedImage;
-import java.lang.ref.SoftReference;
-import java.util.concurrent.CountDownLatch;
-import java.util.concurrent.TimeUnit;
-
-import static org.junit.Assert.assertEquals;
-import static org.junit.Assert.assertNotEquals;
-import static org.junit.Assert.assertNotNull;
-import static org.junit.Assert.assertTrue;
-
-public class ImagePoolImplTest {
-
- private static final long TIMEOUT_SEC = 3;
-
- @Test
- public void testImagePoolInstance() {
- ImagePool pool1 = ImagePoolProvider.get();
- ImagePool pool2 = ImagePoolProvider.get();
- assertNotNull(pool1);
- assertNotNull(pool2);
- assertEquals(pool1, pool2);
- }
-
-
- @Test
- public void testImageDispose() throws InterruptedException {
- int width = 700;
- int height = 800;
- int type = BufferedImage.TYPE_INT_ARGB;
- CountDownLatch countDownLatch = new CountDownLatch(1);
- ImagePoolImpl pool = getSimpleSingleBucketPool(width, height);
- Image img1 = pool.acquire(width, height, type,
- bufferedImage -> countDownLatch.countDown());
- BufferedImage img = getImg(img1);
- assertNotNull(img);
- img1 = null;
-
- // ensure dispose actually loses buffered image link so it can be gc'd
- gc();
- assertTrue(countDownLatch.await(TIMEOUT_SEC, TimeUnit.SECONDS));
- }
- @Test
- public void testImageDisposeFromFunction() throws InterruptedException {
- int width = 700;
- int height = 800;
- int type = BufferedImage.TYPE_INT_ARGB;
- CountDownLatch cd = new CountDownLatch(1);
- ImagePoolImpl pool = getSimpleSingleBucketPool(width, height);
-
- BufferedImage img = createImageAndReturnBufferedImage(pool, width, height, type, cd);
- assertNotNull(img);
-
- // ensure dispose actually loses buffered image link so it can be gc'd
- gc();
- assertTrue(cd.await(TIMEOUT_SEC, TimeUnit.SECONDS));
- }
-
- @Test
- public void testImageDisposedAndRecycled() throws InterruptedException {
- int width = 700;
- int height = 800;
- int bucketWidth = 800;
- int bucketHeight = 800;
- int variant = 1;
- int type = BufferedImage.TYPE_INT_ARGB;
- ImagePoolImpl pool = new ImagePoolImpl(new ImagePoolPolicy(
- new int[]{bucketWidth, bucketHeight},
- new int[]{1, 1},
- bucketHeight * bucketWidth * 4 * 3));
-
- // acquire first image and draw something.
- BufferedImage bufferedImageForImg1;
- final CountDownLatch countDownLatch1 = new CountDownLatch(1);
- {
- Image img1 = pool.acquire(width, height, type,
- bufferedImage -> countDownLatch1.countDown());
- bufferedImageForImg1 = getImg(img1);
- img1 = null; // this is still needed.
- }
- // dispose
- gc();
- assertTrue(countDownLatch1.await(TIMEOUT_SEC, TimeUnit.SECONDS));
-
- // ensure dispose actually loses buffered image link so it can be gc'd
- assertNotNull(bufferedImageForImg1);
- assertEquals(bufferedImageForImg1.getWidth(), bucketWidth);
- assertEquals(bufferedImageForImg1.getHeight(), bucketHeight);
-
- // get 2nd image with the same spec
- final CountDownLatch countDownLatch2 = new CountDownLatch(1);
- BufferedImage bufferedImageForImg2;
- {
- Image img2 = pool.acquire(width - variant, height - variant, type,
- bufferedImage -> countDownLatch2.countDown());
- bufferedImageForImg2 = getImg(img2);
- assertEquals(bufferedImageForImg1, bufferedImageForImg2);
- img2 = null;
- }
- // dispose
- gc();
- assertTrue(countDownLatch2.await(TIMEOUT_SEC, TimeUnit.SECONDS));
-
- // ensure that we're recycling previously created buffered image.
- assertNotNull(bufferedImageForImg1);
- assertNotNull(bufferedImageForImg2);
- }
-
-
- @Test
- public void testBufferedImageReleased() throws InterruptedException {
- int width = 700;
- int height = 800;
- int bucketWidth = 800;
- int bucketHeight = 800;
- ImagePoolImpl pool = new ImagePoolImpl(new ImagePoolPolicy(
- new int[]{bucketWidth, bucketHeight},
- new int[]{1, 1},
- bucketWidth * bucketWidth * 4 * 2));
- CountDownLatch countDownLatch = new CountDownLatch(1);
- Image image1 = pool.acquire(width, height, BufferedImage.TYPE_INT_ARGB,
- bufferedImage -> countDownLatch.countDown());
- BufferedImage internalPtr = getImg(image1);
- // dispose
- image1 = null;
- gc();
- assertTrue(countDownLatch.await(TIMEOUT_SEC, TimeUnit.SECONDS));
-
- // Simulate BufferedBitmaps being gc'd. Bucket filled with null soft refs.
- for (Bucket bucket : ((ImagePoolImpl) pool).mPool.values()) {
- bucket.mBufferedImageRef.clear();
- bucket.mBufferedImageRef.add(new SoftReference<>(null));
- bucket.mBufferedImageRef.add(new SoftReference<>(null));
- }
-
- assertNotEquals(internalPtr,
- getImg(pool.acquire(width, height, BufferedImage.TYPE_INT_ARGB)));
- }
-
- @Test
- public void testPoolWidthHeightNotBigEnough() {
- int width = 1000;
- int height = 1000;
- int bucketWidth = 999;
- int bucketHeight = 800;
- ImagePool pool = new ImagePoolImpl(
- new ImagePoolPolicy(new int[]{bucketWidth, bucketHeight}, new int[]{1, 1},
- bucketWidth * bucketWidth * 4 * 2));
- ImageImpl image = (ImageImpl) pool.acquire(width, height, BufferedImage.TYPE_INT_ARGB);
-
- assertEquals(getTooBigForPoolCount(pool), 1);
- }
-
- @Test
- public void testSizeNotBigEnough() {
- int width = 500;
- int height = 500;
- int bucketWidth = 800;
- int bucketHeight = 800;
- ImagePoolImpl pool = new ImagePoolImpl(
- new ImagePoolPolicy(new int[]{bucketWidth, bucketHeight}, new int[]{1, 1},
- bucketWidth * bucketWidth)); // cache not big enough.
- ImageImpl image = (ImageImpl) pool.acquire(width, height, BufferedImage.TYPE_INT_ARGB);
-
- assertEquals(getTooBigForPoolCount(pool), 1);
- assertEquals(image.getWidth(), width);
- assertEquals(image.getHeight(), height);
- }
-
- @Test
- public void testImageMultipleCopies() throws InterruptedException {
- int width = 700;
- int height = 800;
- int bucketWidth = 800;
- int bucketHeight = 800;
- int type = BufferedImage.TYPE_INT_ARGB;
- ImagePoolImpl pool = new ImagePoolImpl(new ImagePoolPolicy(
- new int[]{bucketWidth, bucketHeight},
- new int[]{2, 2},
- bucketHeight * bucketWidth * 4 * 4));
-
- // create 1, and 2 different instances.
- final CountDownLatch cd1 = new CountDownLatch(1);
- Image img1 = pool.acquire(width, height, type, bufferedImage -> cd1.countDown());
- BufferedImage bufferedImg1 = getImg(img1);
-
- Image img2 = pool.acquire(width, height, type);
- BufferedImage bufferedImg2 = getImg(img2);
-
- assertNotEquals(bufferedImg1, bufferedImg2);
-
- // disposing img1. Since # of copies == 2, this buffer should be recycled.
- img1 = null;
- gc();
- cd1.await(TIMEOUT_SEC, TimeUnit.SECONDS);
-
- // Ensure bufferedImg1 is recycled in newly acquired img3.
- Image img3 = pool.acquire(width, height, type);
- BufferedImage bufferedImage3 = getImg(img3);
- assertNotEquals(bufferedImg2, bufferedImage3);
- assertEquals(bufferedImg1, bufferedImage3);
- }
-
- @Ignore("b/132614809")
- @Test
- public void testPoolDispose() throws InterruptedException {
- int width = 700;
- int height = 800;
- int bucketWidth = 800;
- int bucketHeight = 800;
- int type = BufferedImage.TYPE_INT_ARGB;
-
- // Pool barely enough for 1 image.
- ImagePoolImpl pool = new ImagePoolImpl(new ImagePoolPolicy(
- new int[]{bucketWidth, bucketHeight},
- new int[]{2, 2},
- bucketHeight * bucketWidth * 4));
-
- // create 1, and 2 different instances.
- final CountDownLatch cd1 = new CountDownLatch(1);
- Image img1 = pool.acquire(width, height, type, bufferedImage -> cd1.countDown());
- BufferedImage bufferedImg1 = getImg(img1);
- assertEquals(getAllocatedTotalBytes(pool), bucketWidth * bucketHeight * 4);
- assertEquals(getTooBigForPoolCount(pool), 0);
-
- // Release the img1.
- img1 = null;
- gc();
- cd1.await(TIMEOUT_SEC, TimeUnit.SECONDS);
-
- // Dispose pool.
- pool.dispose();
- assertEquals(getAllocatedTotalBytes(pool), 0);
-
- // Request the same sized image as previous.
- // If the pool was not disposed, this would return the image with bufferedImg1.
- Image img2 = pool.acquire(width, height, type);
- BufferedImage bufferedImg2 = getImg(img2);
- assertEquals(getAllocatedTotalBytes(pool), bucketWidth * bucketHeight * 4);
- assertEquals(getTooBigForPoolCount(pool), 0);
-
- // Pool disposed before. No buffered image should be recycled.
- assertNotEquals(img1, img2);
- assertNotEquals(bufferedImg1, bufferedImg2);
- }
-
- private static BufferedImage createImageAndReturnBufferedImage(ImagePoolImpl pool, int width,
- int height
- , int type, CountDownLatch cd) {
- Image img1 = pool.acquire(width, height, type, bufferedImage -> cd.countDown());
- return getImg(img1);
- // At this point img1 should have no reference, causing finalizable to trigger
- }
-
- private static ImagePoolImpl getSimpleSingleBucketPool(int width, int height) {
-
- int bucketWidth = Math.max(width, height);
- int bucketHeight = Math.max(width, height);
- return new ImagePoolImpl(new ImagePoolPolicy(
- new int[]{bucketWidth, bucketHeight},
- new int[]{1, 1},
- bucketHeight * bucketWidth * 4 * 3));
- }
-
- // Try to force a gc round
- private static void gc() {
- System.gc();
- System.gc();
- System.gc();
- }
-
- private static int getTooBigForPoolCount(ImagePool pool) {
- return ((ImagePoolStatsProdImpl) ((ImagePoolImpl) pool).mImagePoolStats).mTooBigForPoolCount;
- }
-
- private static long getAllocatedTotalBytes(ImagePool pool) {
- return ((ImagePoolStatsProdImpl) ((ImagePoolImpl) pool).mImagePoolStats).mAllocateTotalBytes;
- }
-
- private static BufferedImage getImg(Image image) {
- return ((ImageImpl) image).mImg;
- }
-}
diff --git a/bridge/tests/src/com/android/layoutlib/bridge/TestDelegates.java b/bridge/tests/src/com/android/layoutlib/bridge/TestDelegates.java
index 8c69e3e47d..60db306f62 100644
--- a/bridge/tests/src/com/android/layoutlib/bridge/TestDelegates.java
+++ b/bridge/tests/src/com/android/layoutlib/bridge/TestDelegates.java
@@ -17,7 +17,8 @@
package com.android.layoutlib.bridge;
import com.android.tools.layoutlib.annotations.LayoutlibDelegate;
-import com.android.tools.layoutlib.create.CreateInfo;
+
+import com.android.tools.layoutlib.create.NativeConfig;
import java.lang.reflect.Method;
import java.lang.reflect.Modifier;
@@ -29,7 +30,7 @@ import junit.framework.TestCase;
/**
* Tests that native delegate classes implement all the required methods.
*
- * This looks at {@link CreateInfo#DELEGATE_CLASS_NATIVES} to get the list of classes that
+ * This looks at {@link NativeConfig#DELEGATE_CLASS_NATIVES} to get the list of classes that
* have their native methods reimplemented through a delegate.
*
* Since the reimplemented methods are not native anymore, we look for the annotation
@@ -45,7 +46,7 @@ public class TestDelegates extends TestCase {
public void testNativeDelegates() {
- final String[] classes = CreateInfo.DELEGATE_CLASS_NATIVES;
+ final String[] classes = NativeConfig.DELEGATE_CLASS_NATIVES;
mErrors.clear();
for (String clazz : classes) {
String targetClassName = clazz.replace('$', '_') + "_Delegate";
@@ -55,7 +56,7 @@ public class TestDelegates extends TestCase {
}
public void testMethodDelegates() {
- final String[] methods = CreateInfo.DELEGATE_METHODS;
+ final String[] methods = NativeConfig.DELEGATE_METHODS;
mErrors.clear();
for (String methodName : methods) {
// extract the class name
diff --git a/bridge/tests/src/com/android/layoutlib/bridge/android/BitmapTest.java b/bridge/tests/src/com/android/layoutlib/bridge/android/BitmapTest.java
new file mode 100644
index 0000000000..67d21e9a1f
--- /dev/null
+++ b/bridge/tests/src/com/android/layoutlib/bridge/android/BitmapTest.java
@@ -0,0 +1,67 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.layoutlib.bridge.android;
+
+import com.android.layoutlib.bridge.Bridge;
+import com.android.layoutlib.bridge.intensive.RenderTestBase;
+import com.android.ninepatch.NinePatch;
+
+import org.junit.Assert;
+import org.junit.BeforeClass;
+import org.junit.Test;
+
+import android.graphics.Bitmap;
+import android.graphics.BitmapFactory;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.nio.Buffer;
+import java.nio.ByteBuffer;
+
+import static org.junit.Assert.assertNotNull;
+
+public class BitmapTest extends RenderTestBase {
+ @BeforeClass
+ public static void setUp() {
+ Bridge.prepareThread();
+ }
+
+ @Test
+ public void testNinePatchChunk() throws IOException {
+ InputStream compiled =
+ getClass().getResourceAsStream("/com/android/layoutlib/testdata/compiled.9.png");
+ Bitmap compiledBitmap = BitmapFactory.decodeStream(compiled, null, null);
+
+ InputStream nonCompiled = getClass().getResourceAsStream(
+ "/com/android/layoutlib/testdata/non_compiled.9.png");
+ NinePatch ninePatch = NinePatch.load(nonCompiled, true, false);
+
+ Assert.assertArrayEquals(compiledBitmap.getNinePatchChunk(), ninePatch.getChunk().getSerializedChunk());
+ }
+
+ @Test
+ public void testNativeBitmap() {
+ InputStream compiled =
+ getClass().getResourceAsStream("/com/android/layoutlib/testdata/compiled.9.png");
+ Bitmap compiledBitmap = BitmapFactory.decodeStream(compiled, null, null);
+ assertNotNull(compiledBitmap);
+ Buffer buffer = ByteBuffer.allocate(compiledBitmap.getByteCount());
+ compiledBitmap.copyPixelsToBuffer(buffer);
+ buffer.rewind();
+ compiledBitmap.copyPixelsFromBuffer(buffer);
+ }
+}
diff --git a/bridge/tests/src/com/android/layoutlib/bridge/android/BridgeContextTest.java b/bridge/tests/src/com/android/layoutlib/bridge/android/BridgeContextTest.java
index d877110335..5266c57909 100644
--- a/bridge/tests/src/com/android/layoutlib/bridge/android/BridgeContextTest.java
+++ b/bridge/tests/src/com/android/layoutlib/bridge/android/BridgeContextTest.java
@@ -17,13 +17,14 @@
package com.android.layoutlib.bridge.android;
import com.android.ide.common.rendering.api.SessionParams;
+import com.android.layoutlib.bridge.Bridge;
import com.android.layoutlib.bridge.impl.RenderAction;
import com.android.layoutlib.bridge.impl.RenderActionTestUtil;
import com.android.layoutlib.bridge.intensive.RenderTestBase;
-import com.android.layoutlib.bridge.intensive.setup.ConfigGenerator;
import com.android.layoutlib.bridge.intensive.setup.LayoutLibTestCallback;
import com.android.layoutlib.bridge.intensive.setup.LayoutPullParser;
+import org.junit.BeforeClass;
import org.junit.Test;
import android.R.attr;
@@ -34,9 +35,15 @@ import android.content.res.TypedArray;
import android.util.DisplayMetrics;
import android.view.ContextThemeWrapper;
+import static org.junit.Assert.assertNull;
import static org.junit.Assert.assertTrue;
public class BridgeContextTest extends RenderTestBase {
+ @BeforeClass
+ public static void setUp() {
+ Bridge.prepareThread();
+ }
+
@Test
public void basic() throws ClassNotFoundException {
// Setup
@@ -56,9 +63,9 @@ public class BridgeContextTest extends RenderTestBase {
Configuration configuration = RenderAction.getConfiguration(params);
BridgeContext context = new BridgeContext(params.getProjectKey(), metrics, params.getResources(),
params.getAssets(), params.getLayoutlibCallback(), configuration,
- params.getTargetSdkVersion(), params.isRtlSupported(), true, true);
+ params.getTargetSdkVersion(), params.isRtlSupported());
- context.initResources();
+ context.initResources(params.getAssets());
BridgeContext oldContext = RenderActionTestUtil.setBridgeContext(context);
try {
Context themeContext = new ContextThemeWrapper(context, style.Theme_Material);
@@ -103,9 +110,9 @@ public class BridgeContextTest extends RenderTestBase {
Configuration configuration = RenderAction.getConfiguration(params);
BridgeContext context = new BridgeContext(params.getProjectKey(), metrics, params.getResources(),
params.getAssets(), params.getLayoutlibCallback(), configuration,
- params.getTargetSdkVersion(), params.isRtlSupported(), true, true);
+ params.getTargetSdkVersion(), params.isRtlSupported());
- context.initResources();
+ context.initResources(params.getAssets());
BridgeContext oldContext = RenderActionTestUtil.setBridgeContext(context);
try {
Context themeContext = new ContextThemeWrapper(context, style.Theme_Material);
@@ -119,7 +126,26 @@ public class BridgeContextTest extends RenderTestBase {
RenderActionTestUtil.setBridgeContext(oldContext);
context.disposeResources();
}
+ }
+ @Test
+ public void noExceptionForCustomService() throws ClassNotFoundException {
+ LayoutPullParser parser = LayoutPullParser.createFromPath("/empty.xml");
+ LayoutLibTestCallback layoutLibCallback =
+ new LayoutLibTestCallback(getLogger(), mDefaultClassLoader);
+ layoutLibCallback.initResources();
+ SessionParams params = getSessionParamsBuilder()
+ .setParser(parser)
+ .setCallback(layoutLibCallback)
+ .setTheme("Theme.Material", false)
+ .build();
+ DisplayMetrics metrics = new DisplayMetrics();
+ Configuration configuration = RenderAction.getConfiguration(params);
+ BridgeContext context = new BridgeContext(params.getProjectKey(), metrics, params.getResources(),
+ params.getAssets(), params.getLayoutlibCallback(), configuration,
+ params.getTargetSdkVersion(), params.isRtlSupported());
+ assertNull(context.getSystemService("my_custom_service"));
+ sRenderMessages.removeIf(message -> message.equals("Service my_custom_service was not found or is unsupported"));
}
}
diff --git a/bridge/tests/src/com/android/layoutlib/bridge/android/BridgeXmlBlockParserTest.java b/bridge/tests/src/com/android/layoutlib/bridge/android/BridgeXmlBlockParserTest.java
index 1a66c94cb2..f8fd565ff2 100644
--- a/bridge/tests/src/com/android/layoutlib/bridge/android/BridgeXmlBlockParserTest.java
+++ b/bridge/tests/src/com/android/layoutlib/bridge/android/BridgeXmlBlockParserTest.java
@@ -58,27 +58,18 @@ public class BridgeXmlBlockParserTest {
assertEquals(XmlPullParser.START_TAG, parser.next());
assertEquals("LinearLayout", parser.getName());
- assertEquals(XmlPullParser.TEXT, parser.next());
-
assertEquals(XmlPullParser.START_TAG, parser.next());
assertEquals("Button", parser.getName());
- assertEquals(XmlPullParser.TEXT, parser.next());
assertEquals(XmlPullParser.END_TAG, parser.next());
- assertEquals(XmlPullParser.TEXT, parser.next());
-
assertEquals(XmlPullParser.START_TAG, parser.next());
assertEquals("View", parser.getName());
assertEquals(XmlPullParser.END_TAG, parser.next());
- assertEquals(XmlPullParser.TEXT, parser.next());
-
assertEquals(XmlPullParser.START_TAG, parser.next());
assertEquals("TextView", parser.getName());
assertEquals(XmlPullParser.END_TAG, parser.next());
- assertEquals(XmlPullParser.TEXT, parser.next());
-
assertEquals(XmlPullParser.END_TAG, parser.next());
assertEquals(XmlPullParser.END_DOCUMENT, parser.next());
}
diff --git a/bridge/tests/src/com/android/layoutlib/bridge/impl/ResourceHelperTest.java b/bridge/tests/src/com/android/layoutlib/bridge/impl/ResourceHelperTest.java
index 4e18f076a0..aedd845ce4 100644
--- a/bridge/tests/src/com/android/layoutlib/bridge/impl/ResourceHelperTest.java
+++ b/bridge/tests/src/com/android/layoutlib/bridge/impl/ResourceHelperTest.java
@@ -18,6 +18,21 @@ package com.android.layoutlib.bridge.impl;
import org.junit.Test;
+import android.content.res.StringBlock;
+import android.text.SpannedString;
+import android.text.TextUtils.TruncateAt;
+import android.text.style.AbsoluteSizeSpan;
+import android.text.style.BulletSpan;
+import android.text.style.ForegroundColorSpan;
+import android.text.style.RelativeSizeSpan;
+import android.text.style.StrikethroughSpan;
+import android.text.style.StyleSpan;
+import android.text.style.SubscriptSpan;
+import android.text.style.SuperscriptSpan;
+import android.text.style.TypefaceSpan;
+import android.text.style.URLSpan;
+import android.text.style.UnderlineSpan;
+
import static org.junit.Assert.*;
public class ResourceHelperTest {
@@ -48,4 +63,40 @@ public class ResourceHelperTest {
assertEquals(0x12aabbcc, ResourceHelper.getColor("#12AABBCC"));
assertEquals(0x12345, ResourceHelper.getColor("#12345"));
}
+
+ @Test
+ public void testParseHtml() {
+ CharSequence parsed = ResourceHelper.parseHtml("Text <b>bold</b> " +
+ "<i>italic</i> <u>underline</u> <tt>monospace</tt> " +
+ "<big>big</big> <small>small</small> " +
+ "<sup>superscript</sup> <sub>subscript</sub> <strike>strike</strike> " +
+ "<li>bullet</li> <marquee>marquee</marquee> " +
+ "<a href=\"http://link.com\">link</a> " +
+ "<font face=\"serif\" color=\"#ff0000\" height=\"20\" size=\"8\">font</font> " +
+ "<em>fake italic</em> <del>fake strike</del> " +
+ "End text");
+ Class<?>[] classes = {StyleSpan.class, StyleSpan.class, UnderlineSpan.class,
+ TypefaceSpan.class, RelativeSizeSpan.class, RelativeSizeSpan.class,
+ SuperscriptSpan.class, SubscriptSpan.class, StrikethroughSpan.class,
+ BulletSpan.class, TruncateAt.class, URLSpan.class, StringBlock.Height.class,
+ AbsoluteSizeSpan.class, ForegroundColorSpan.class, TypefaceSpan.class};
+ int[] starts = {5, 10, 17, 27, 37, 41, 47, 59, 69, 0, 83, 91, 0, 96, 96, 96};
+ int[] ends = {9, 16, 26, 36, 40, 46, 58, 68, 75, 133, 90, 95, 133, 100, 100, 100};
+ SpannedString spanned = (SpannedString)parsed;
+ assertEquals("Text bold " +
+ "italic underline monospace " +
+ "big small " +
+ "superscript subscript strike " +
+ "bullet marquee " +
+ "link " +
+ "font " +
+ "fake italic fake strike " +
+ "End text", spanned.toString());
+ Object[] spans = spanned.getSpans(0, spanned.length(), Object.class);
+ for (int i =0; i < spans.length; i++) {
+ assertEquals(classes[i], spans[i].getClass());
+ assertEquals(starts[i], spanned.getSpanStart(spans[i]));
+ assertEquals(ends[i], spanned.getSpanEnd(spans[i]));
+ }
+ }
} \ No newline at end of file
diff --git a/bridge/tests/src/com/android/layoutlib/bridge/intensive/Main.java b/bridge/tests/src/com/android/layoutlib/bridge/intensive/Main.java
index be57b88ade..2c69f3a2e9 100644
--- a/bridge/tests/src/com/android/layoutlib/bridge/intensive/Main.java
+++ b/bridge/tests/src/com/android/layoutlib/bridge/intensive/Main.java
@@ -18,12 +18,14 @@ package com.android.layoutlib.bridge.intensive;
import com.android.layoutlib.bridge.BridgeRenderSessionTest;
import com.android.layoutlib.bridge.TestDelegates;
+import com.android.layoutlib.bridge.android.BitmapTest;
import com.android.layoutlib.bridge.android.BridgeContextTest;
import com.android.layoutlib.bridge.android.BridgeXmlBlockParserTest;
import com.android.layoutlib.bridge.impl.LayoutParserWrapperTest;
import com.android.layoutlib.bridge.impl.ResourceHelperTest;
import com.android.tools.idea.validator.LayoutValidatorTests;
-import com.android.tools.idea.validator.accessibility.AccessibilityValidatorTests;
+import com.android.tools.idea.validator.ValidatorResultTests;
+import com.android.tools.idea.validator.AccessibilityValidatorTests;
import org.junit.runner.RunWith;
import org.junit.runners.Suite;
@@ -31,11 +33,7 @@ import org.junit.runners.Suite.SuiteClasses;
import android.content.res.BridgeTypedArrayTest;
import android.content.res.Resources_DelegateTest;
-import android.graphics.Color_DelegateTest;
-import android.graphics.Matrix_DelegateTest;
import android.util.BridgeXmlPullAttributesTest;
-import android.util.imagepool.ImagePoolHelperTest;
-import android.util.imagepool.ImagePoolImplTest;
/**
* Suite used by the layoutlib build system
@@ -44,11 +42,10 @@ import android.util.imagepool.ImagePoolImplTest;
@SuiteClasses({
RenderTests.class, LayoutParserWrapperTest.class,
BridgeXmlBlockParserTest.class, BridgeXmlPullAttributesTest.class,
- Matrix_DelegateTest.class, TestDelegates.class,
- BridgeRenderSessionTest.class, ResourceHelperTest.class, BridgeContextTest.class,
- Resources_DelegateTest.class, Color_DelegateTest.class, ImagePoolHelperTest.class,
- ImagePoolImplTest.class, HighQualityShadowsRenderTests.class,
- LayoutValidatorTests.class, AccessibilityValidatorTests.class, BridgeTypedArrayTest.class
+ TestDelegates.class, BridgeRenderSessionTest.class, ResourceHelperTest.class,
+ BridgeContextTest.class, Resources_DelegateTest.class, ShadowsRenderTests.class,
+ LayoutValidatorTests.class, AccessibilityValidatorTests.class, BridgeTypedArrayTest.class,
+ ValidatorResultTests.class, BitmapTest.class
})
public class Main {
}
diff --git a/bridge/tests/src/com/android/layoutlib/bridge/intensive/RenderTestBase.java b/bridge/tests/src/com/android/layoutlib/bridge/intensive/RenderTestBase.java
index 67bb7af0b9..7054f2b49c 100644
--- a/bridge/tests/src/com/android/layoutlib/bridge/intensive/RenderTestBase.java
+++ b/bridge/tests/src/com/android/layoutlib/bridge/intensive/RenderTestBase.java
@@ -25,9 +25,9 @@ import com.android.ide.common.resources.deprecated.FrameworkResources;
import com.android.ide.common.resources.deprecated.ResourceItem;
import com.android.ide.common.resources.deprecated.ResourceRepository;
import com.android.ide.common.resources.deprecated.TestFolderWrapper;
+import com.android.internal.lang.System_Delegate;
import com.android.layoutlib.bridge.Bridge;
import com.android.layoutlib.bridge.android.RenderParamsFlags;
-import com.android.layoutlib.bridge.impl.DelegateManager;
import com.android.layoutlib.bridge.intensive.setup.ConfigGenerator;
import com.android.layoutlib.bridge.intensive.setup.LayoutLibTestCallback;
import com.android.layoutlib.bridge.intensive.setup.LayoutPullParser;
@@ -35,8 +35,6 @@ import com.android.layoutlib.bridge.intensive.util.ImageUtils;
import com.android.layoutlib.bridge.intensive.util.ModuleClassLoader;
import com.android.layoutlib.bridge.intensive.util.SessionParamsBuilder;
import com.android.layoutlib.bridge.intensive.util.TestAssetRepository;
-import com.android.layoutlib.bridge.intensive.util.TestUtils;
-import com.android.tools.layoutlib.java.System_Delegate;
import com.android.utils.ILogger;
import org.junit.AfterClass;
@@ -48,6 +46,7 @@ import org.junit.runner.Description;
import android.annotation.NonNull;
import android.annotation.Nullable;
+import android.view.Choreographer;
import java.awt.image.BufferedImage;
import java.io.File;
@@ -56,10 +55,10 @@ import java.io.IOException;
import java.net.URL;
import java.util.ArrayList;
import java.util.Arrays;
+import java.util.Locale;
import java.util.concurrent.TimeUnit;
import com.google.android.collect.Lists;
-import com.google.common.collect.ImmutableMap;
import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.fail;
@@ -99,9 +98,16 @@ public class RenderTestBase {
*/
void beforeDisposed(RenderSession session);
}
+
+ private static final String NATIVE_LIB_PATH_PROPERTY = "native.lib.path";
+ private static final String FONT_DIR_PROPERTY = "font.dir";
+ private static final String ICU_DATA_PATH_PROPERTY = "icu.data.path";
private static final String PLATFORM_DIR_PROPERTY = "platform.dir";
private static final String RESOURCE_DIR_PROPERTY = "test_res.dir";
+ private static final String NATIVE_LIB_DIR_PATH;
+ private static final String FONT_DIR;
+ private static final String ICU_DATA_PATH;
protected static final String PLATFORM_DIR;
private static final String TEST_RES_DIR;
/** Location of the app to test inside {@link #TEST_RES_DIR} */
@@ -128,6 +134,10 @@ public class RenderTestBase {
PLATFORM_DIR_PROPERTY, System.getProperty(PLATFORM_DIR_PROPERTY)));
}
+ NATIVE_LIB_DIR_PATH = getNativeLibDirPath();
+ FONT_DIR = getFontDir();
+ ICU_DATA_PATH = getIcuDataPath();
+
TEST_RES_DIR = getTestResDir();
if (TEST_RES_DIR == null) {
fail(String.format("System property %1$s.dir not properly set. The value is %2$s",
@@ -147,8 +157,56 @@ public class RenderTestBase {
}
};
+ @Rule
+ public TestWatcher sMemoryLeakChecker = new TestWatcher() {
+ @Override
+ protected void succeeded(Description description) {
+ for (int i = Choreographer.CALLBACK_INPUT; i <= Choreographer.CALLBACK_COMMIT; ++i) {
+ if (Choreographer.getInstance().mCallbackQueues[i].mHead != null) {
+ fail("Memory leak: leftover frame callbacks are detected in Choreographer");
+ }
+ }
+ }
+ };
+
protected ClassLoader mDefaultClassLoader;
+ private static String getNativeLibDirPath() {
+ String nativeLibDirPath = System.getProperty(NATIVE_LIB_PATH_PROPERTY);
+ if (nativeLibDirPath != null) {
+ File nativeLibDir = new File(nativeLibDirPath);
+ if (nativeLibDir.isDirectory()) {
+ nativeLibDirPath = nativeLibDir.getAbsolutePath();
+ } else {
+ nativeLibDirPath = null;
+ }
+ }
+ if (nativeLibDirPath == null) {
+ nativeLibDirPath = PLATFORM_DIR + "/../../../../../lib64/";
+ }
+ return nativeLibDirPath;
+ }
+
+ private static String getFontDir() {
+ String fontDir = System.getProperty(FONT_DIR_PROPERTY);
+ if (fontDir == null) {
+ // The fonts are built into out/host/common/obj/PACKAGING/fonts_intermediates
+ // as specified in build/make/core/layoutlib_fonts.mk, and PLATFORM_DIR is
+ // out/host/[arch]/sdk/sdk*/android-sdk*/platforms/android*
+ fontDir = PLATFORM_DIR +
+ "/../../../../../../common/obj/PACKAGING/fonts_intermediates";
+ }
+ return fontDir;
+ }
+
+ private static String getIcuDataPath() {
+ String icuDataPath = System.getProperty(ICU_DATA_PATH_PROPERTY);
+ if (icuDataPath == null) {
+ icuDataPath = PLATFORM_DIR + "/../../../../../com.android.i18n/etc/icu/icudt70l.dat";
+ }
+ return icuDataPath;
+ }
+
private static String getPlatformDir() {
String platformDir = System.getProperty(PLATFORM_DIR_PROPERTY);
if (platformDir != null && !platformDir.isEmpty() && new File(platformDir).isDirectory()) {
@@ -318,15 +376,12 @@ public class RenderTestBase {
};
sProjectResources.loadResources();
- // The fonts are built into out/host/common/obj/PACKAGING/sdk-fonts_intermediates as specified in
- // build/make/core/sdk_font.mk, and PLATFORM_DIR is out/host/[arch]/sdk/sdk*/android-sdk*/platforms/android*
- File fontLocation = new File(PLATFORM_DIR,
- "../../../../../../common/obj/PACKAGING/sdk-fonts_intermediates");
+ File fontLocation = new File(FONT_DIR);
File buildProp = new File(PLATFORM_DIR, "build.prop");
File attrs = new File(res, "values" + File.separator + "attrs.xml");
sBridge = new Bridge();
- sBridge.init(ConfigGenerator.loadProperties(buildProp), fontLocation, null, null,
- ConfigGenerator.getEnumMap(attrs), getLayoutLog());
+ sBridge.init(ConfigGenerator.loadProperties(buildProp), fontLocation, NATIVE_LIB_DIR_PATH,
+ ICU_DATA_PATH, ConfigGenerator.getEnumMap(attrs), getLayoutLog());
Bridge.getLock().lock();
try {
Bridge.setLog(getLayoutLog());
@@ -342,11 +397,6 @@ public class RenderTestBase {
sProjectResources = null;
sLogger = null;
sBridge = null;
-
- TestUtils.gc();
-
- System.out.println("Objects still linked from the DelegateManager:");
- DelegateManager.dump(System.out);
}
@NonNull
@@ -399,7 +449,14 @@ public class RenderTestBase {
*/
protected static void verify(@NonNull String goldenImageName, @NonNull BufferedImage image) {
try {
- String goldenImagePath = APP_TEST_DIR + "/golden/" + goldenImageName;
+ boolean isMac = System.getProperty("os.name").toLowerCase(Locale.US).contains("mac");
+ String goldenImagePath = APP_TEST_DIR;
+ if (isMac) {
+ goldenImagePath += "/golden-mac/";
+ } else {
+ goldenImagePath += "/golden/";
+ }
+ goldenImagePath += goldenImageName;
ImageUtils.requireSimilar(goldenImagePath, image);
} catch (IOException e) {
getLogger().error(e, e.getMessage());
@@ -430,14 +487,15 @@ public class RenderTestBase {
@Nullable
protected static RenderResult renderAndVerify(SessionParams params, String goldenFileName)
throws ClassNotFoundException {
- return RenderTestBase.renderAndVerify(params, goldenFileName, -1);
+ return RenderTestBase.renderAndVerify(params, goldenFileName, TimeUnit.SECONDS.toNanos(2));
}
protected static ILayoutLog getLayoutLog() {
if (sLayoutLibLog == null) {
sLayoutLibLog = new ILayoutLog() {
@Override
- public void warning(String tag, String message, Object cookie, Object data) {
+ public void warning(@Nullable String tag, @NonNull String message, @Nullable Object viewCookie,
+ @Nullable Object data) {
System.out.println("Warning " + tag + ": " + message);
failWithMsg(message);
}
@@ -454,20 +512,26 @@ public class RenderTestBase {
}
@Override
- public void error(String tag, String message, Object cookie, Object data) {
+ public void error(@Nullable String tag, @NonNull String message, @Nullable Object viewCookie,
+ @Nullable Object data) {
System.out.println("Error " + tag + ": " + message);
failWithMsg(message);
}
@Override
- public void error(String tag, String message, Throwable throwable, Object cookie,
- Object data) {
+ public void error(@Nullable String tag, @NonNull String message, @Nullable Throwable throwable,
+ @Nullable Object viewCookie, @Nullable Object data) {
System.out.println("Error " + tag + ": " + message);
if (throwable != null) {
throwable.printStackTrace();
}
failWithMsg(message);
}
+
+ @Override
+ public void logAndroidFramework(int priority, String tag, String message) {
+ System.out.println("Android framework message " + tag + ": " + message);
+ }
};
}
return sLayoutLibLog;
diff --git a/bridge/tests/src/com/android/layoutlib/bridge/intensive/RenderTests.java b/bridge/tests/src/com/android/layoutlib/bridge/intensive/RenderTests.java
index 303c2474da..91c79c9587 100644
--- a/bridge/tests/src/com/android/layoutlib/bridge/intensive/RenderTests.java
+++ b/bridge/tests/src/com/android/layoutlib/bridge/intensive/RenderTests.java
@@ -20,11 +20,13 @@ import com.android.ide.common.rendering.api.RenderSession;
import com.android.ide.common.rendering.api.ResourceNamespace;
import com.android.ide.common.rendering.api.ResourceReference;
import com.android.ide.common.rendering.api.ResourceValueImpl;
+import com.android.ide.common.rendering.api.Result;
import com.android.ide.common.rendering.api.SessionParams;
import com.android.ide.common.rendering.api.SessionParams.RenderingMode;
import com.android.ide.common.rendering.api.ViewInfo;
import com.android.ide.common.rendering.api.XmlParserFactory;
import com.android.internal.R;
+import com.android.internal.lang.System_Delegate;
import com.android.layoutlib.bridge.android.BridgeContext;
import com.android.layoutlib.bridge.android.RenderParamsFlags;
import com.android.layoutlib.bridge.impl.ParserFactory;
@@ -39,6 +41,7 @@ import com.android.resources.Navigation;
import com.android.resources.ResourceType;
import org.junit.After;
+import org.junit.Ignore;
import org.junit.Test;
import org.kxml2.io.KXmlParser;
import org.xmlpull.v1.XmlPullParser;
@@ -55,6 +58,8 @@ import android.graphics.Color;
import android.util.DisplayMetrics;
import android.util.StateSet;
import android.util.TypedValue;
+import android.widget.Button;
+import android.widget.LinearLayout;
import java.awt.BasicStroke;
import java.awt.Graphics2D;
@@ -161,22 +166,14 @@ public class RenderTests extends RenderTestBase {
.setParser(parser)
.setConfigGenerator(ConfigGenerator.NEXUS_5)
.setCallback(layoutLibCallback)
- .disableShadows()
.build();
renderAndVerify(params, "allwidgets.png");
-
- // We expect fidelity warnings for Path.isConvex. Fail for anything else.
- sRenderMessages.removeIf(message -> message.equals("Path.isConvex is not supported."));
}
@Test
public void testArrayCheck() throws ClassNotFoundException, FileNotFoundException {
renderAndVerify("array_check.xml", "array_check.png", false);
-
- // We expect fidelity warnings for Path.isConvex. Fail for anything else.
- sRenderMessages.removeIf(
- message -> message.equals("Font$Builder.nAddAxis is not supported."));
}
@Test
@@ -189,13 +186,8 @@ public class RenderTests extends RenderTestBase {
.setParser(parser)
.setConfigGenerator(ConfigGenerator.NEXUS_7_2012)
.setCallback(layoutLibCallback)
- .disableShadows()
.build();
renderAndVerify(params, "allwidgets_tab.png");
-
- // We expect fidelity warnings for Path.isConvex. Fail for anything else.
- sRenderMessages.removeIf(message -> message.equals("Path.isConvex is not supported."));
- sRenderMessages.removeIf(message -> message.equals("Font$Builder.nAddAxis is not supported."));
}
@Test
@@ -545,9 +537,39 @@ public class RenderTests extends RenderTestBase {
}
/**
+ * Test a vector drawable which is transparent.
+ */
+ @Test
+ public void testTransparentDrawable() throws ClassNotFoundException {
+ // Create the layout pull parser.
+ LayoutPullParser parser = LayoutPullParser.createFromString(
+ "<ImageView xmlns:android=\"http://schemas.android.com/apk/res/android\"\n" +
+ " android:layout_height=\"fill_parent\"\n" +
+ " android:layout_width=\"fill_parent\"\n" +
+ " android:src=\"@drawable/transparent_drawable\" />");
+ // Create LayoutLibCallback.
+ LayoutLibTestCallback layoutLibCallback =
+ new LayoutLibTestCallback(getLogger(), mDefaultClassLoader);
+ layoutLibCallback.initResources();
+
+ SessionParams params = getSessionParamsBuilder()
+ .setParser(parser)
+ .setCallback(layoutLibCallback)
+ .setTheme("Theme.Material.Light.NoActionBar.Fullscreen", false)
+ .setRenderingMode(RenderingMode.V_SCROLL)
+ .disableDecoration()
+ .setTransparentBackground()
+ .build();
+
+ renderAndVerify(params, "transparent_drawable.png",
+ TimeUnit.SECONDS.toNanos(2));
+ }
+
+ /**
* Test a vector drawable that uses trimStart and trimEnd. It also tests all the primitives
* for vector drawables (lines, moves and cubic and quadratic curves).
*/
+ @Ignore("This test does not make sense in layoutlib anymore, test in Studio")
@Test
public void testVectorDrawableHasMultipleLineInPathData() throws ClassNotFoundException {
// Create the layout pull parser.
@@ -764,6 +786,7 @@ public class RenderTests extends RenderTestBase {
assertEquals(90, rootLayout.getChildren().get(5).getChildren().get(0).getLeft());
assertEquals(-270, rootLayout.getChildren().get(5).getChildren().get(0).getBottom());
assertEquals(690, rootLayout.getChildren().get(5).getChildren().get(0).getRight());
+ session.dispose();
// Do a full render pass
parser = createParserFromPath("scrolled.xml");
@@ -803,7 +826,7 @@ public class RenderTests extends RenderTestBase {
Configuration configuration = RenderAction.getConfiguration(params);
BridgeContext context = new BridgeContext(params.getProjectKey(), metrics, params.getResources(),
params.getAssets(), params.getLayoutlibCallback(), configuration,
- params.getTargetSdkVersion(), params.isRtlSupported(), true, true);
+ params.getTargetSdkVersion(), params.isRtlSupported());
Resources resources = Resources_Delegate.initSystem(context, assetManager, metrics,
configuration, params.getLayoutlibCallback());
// Test
@@ -845,7 +868,7 @@ public class RenderTests extends RenderTestBase {
Configuration configuration = RenderAction.getConfiguration(params);
BridgeContext context = new BridgeContext(params.getProjectKey(), metrics, params.getResources(),
params.getAssets(), params.getLayoutlibCallback(), configuration,
- params.getTargetSdkVersion(), params.isRtlSupported(), true, true);
+ params.getTargetSdkVersion(), params.isRtlSupported());
Resources resources = Resources_Delegate.initSystem(context, assetManager, metrics,
configuration, params.getLayoutlibCallback());
@@ -870,8 +893,6 @@ public class RenderTests extends RenderTestBase {
public void testFonts() throws ClassNotFoundException, FileNotFoundException {
// TODO: styles seem to be broken in TextView
renderAndVerify("fonts_test.xml", "font_test.png", false);
- sRenderMessages.removeIf(
- message -> message.equals("Font$Builder.nAddAxis is not supported."));
}
@Test
@@ -958,7 +979,7 @@ public class RenderTests extends RenderTestBase {
BridgeContext mContext =
new BridgeContext(params.getProjectKey(), metrics, params.getResources(),
params.getAssets(), params.getLayoutlibCallback(), configuration,
- params.getTargetSdkVersion(), params.isRtlSupported(), true, true);
+ params.getTargetSdkVersion(), params.isRtlSupported());
TypedValue outValue = new TypedValue();
mContext.resolveThemeAttribute(android.R.attr.colorPrimary, outValue, true);
@@ -1036,8 +1057,8 @@ public class RenderTests extends RenderTestBase {
BridgeContext mContext =
new BridgeContext(params.getProjectKey(), metrics, params.getResources(),
params.getAssets(), params.getLayoutlibCallback(), configuration,
- params.getTargetSdkVersion(), params.isRtlSupported(), true, true);
- mContext.initResources();
+ params.getTargetSdkVersion(), params.isRtlSupported());
+ mContext.initResources(params.getAssets());
BridgeContext oldContext = RenderActionTestUtil.setBridgeContext(mContext);
try {
@@ -1088,44 +1109,6 @@ public class RenderTests extends RenderTestBase {
}
@Test
- public void testShadowFlagsNoShadows() throws Exception {
- LayoutPullParser parser = createParserFromPath("shadows_test.xml");
- LayoutLibTestCallback layoutLibCallback =
- new LayoutLibTestCallback(getLogger(), mDefaultClassLoader);
- layoutLibCallback.initResources();
- SessionParams params = getSessionParamsBuilder()
- .setParser(parser)
- .setConfigGenerator(ConfigGenerator.NEXUS_5)
- .setCallback(layoutLibCallback)
- .disableDecoration()
- .disableShadows()
- .build();
-
- renderAndVerify(params, "shadows_test_no_shadow.png");
- // We expect fidelity warnings for Path.isConvex. Fail for anything else.
- sRenderMessages.removeIf(message -> message.equals("Path.isConvex is not supported."));
- }
-
- @Test
- public void testRectangleShadow() throws Exception {
- LayoutPullParser parser = createParserFromPath("shadows_test.xml");
- LayoutLibTestCallback layoutLibCallback =
- new LayoutLibTestCallback(getLogger(), mDefaultClassLoader);
- layoutLibCallback.initResources();
- SessionParams params = getSessionParamsBuilder()
- .setParser(parser)
- .setConfigGenerator(ConfigGenerator.NEXUS_5)
- .setCallback(layoutLibCallback)
- .disableDecoration()
- .disableHighQualityShadows()
- .build();
-
- renderAndVerify(params, "shadows_test.png");
- // We expect fidelity warnings for Path.isConvex. Fail for anything else.
- sRenderMessages.removeIf(message -> message.equals("Path.isConvex is not supported."));
- }
-
- @Test
public void testResourcesGetIdentifier() throws Exception {
// Setup
// Create the layout pull parser for our resources (empty.xml can not be part of the test
@@ -1145,7 +1128,7 @@ public class RenderTests extends RenderTestBase {
Configuration configuration = RenderAction.getConfiguration(params);
BridgeContext context = new BridgeContext(params.getProjectKey(), metrics, params.getResources(),
params.getAssets(), params.getLayoutlibCallback(), configuration,
- params.getTargetSdkVersion(), params.isRtlSupported(), true, true);
+ params.getTargetSdkVersion(), params.isRtlSupported());
Resources resources = Resources_Delegate.initSystem(context, assetManager, metrics,
configuration, params.getLayoutlibCallback());
Integer id =
@@ -1186,7 +1169,12 @@ public class RenderTests extends RenderTestBase {
*/
@Test
public void test9PatchNoDPIBackground() throws Exception {
- String layout =
+ // Create LayoutLibCallback.
+ LayoutLibTestCallback layoutLibCallback =
+ new LayoutLibTestCallback(getLogger(), mDefaultClassLoader);
+ layoutLibCallback.initResources();
+
+ String layoutCompiled =
"<LinearLayout xmlns:android=\"http://schemas.android.com/apk/res/android\"\n" +
" android:layout_width=\"match_parent\"\n" +
" android:layout_height=\"match_parent\"\n" +
@@ -1203,12 +1191,7 @@ public class RenderTests extends RenderTestBase {
" android:text=\"Button\" />\n"
+ "</LinearLayout>";
- LayoutPullParser parser = LayoutPullParser.createFromString(layout);
- // Create LayoutLibCallback.
- LayoutLibTestCallback layoutLibCallback =
- new LayoutLibTestCallback(getLogger(), mDefaultClassLoader);
- layoutLibCallback.initResources();
-
+ LayoutPullParser parser = LayoutPullParser.createFromString(layoutCompiled);
SessionParams params = getSessionParamsBuilder()
.setParser(parser)
.setCallback(layoutLibCallback)
@@ -1217,6 +1200,33 @@ public class RenderTests extends RenderTestBase {
.build();
renderAndVerify(params, "ninepatch_background.png");
+
+ String layoutNonCompiled =
+ "<LinearLayout xmlns:android=\"http://schemas.android.com/apk/res/android\"\n" +
+ " android:layout_width=\"match_parent\"\n" +
+ " android:layout_height=\"match_parent\"\n" +
+ " android:background=\"@drawable/uncompiled_ninepatch\"\n" +
+ " android:layout_margin=\"20dp\"\n" +
+ " android:orientation=\"vertical\">\n\n" +
+ " <Button\n" +
+ " android:layout_width=\"wrap_content\"\n" +
+ " android:layout_height=\"wrap_content\"\n" +
+ " android:text=\"Button\" />\n\n" +
+ " <Button\n" +
+ " android:layout_width=\"wrap_content\"\n" +
+ " android:layout_height=\"wrap_content\"\n" +
+ " android:text=\"Button\" />\n"
+ + "</LinearLayout>";
+
+ parser = LayoutPullParser.createFromString(layoutNonCompiled);
+ params = getSessionParamsBuilder()
+ .setParser(parser)
+ .setCallback(layoutLibCallback)
+ .setTheme("Theme.Material.NoActionBar.Fullscreen", false)
+ .setRenderingMode(RenderingMode.V_SCROLL)
+ .build();
+
+ renderAndVerify(params, "ninepatch_background.png");
}
@Test
@@ -1547,7 +1557,6 @@ public class RenderTests extends RenderTestBase {
new BufferedImage(width / 10, height / 10,
BufferedImage.TYPE_INT_ARGB))
.setFlag(RenderParamsFlags.FLAG_KEY_RESULT_IMAGE_AUTO_SCALE, true)
- .disableShadows()
.build();
renderAndVerify(params, "auto-scale-image.png");
@@ -1638,6 +1647,12 @@ public class RenderTests extends RenderTestBase {
}
@Test
+ public void testJustified() throws ClassNotFoundException, FileNotFoundException {
+ renderAndVerify("justified_inter_word.xml", "justified_inter_word.png", false);
+ renderAndVerify("justified_none.xml", "justified_none.png", false);
+ }
+
+ @Test
public void testManyLineBreaks() throws Exception {
String layout =
"<FrameLayout xmlns:android=\"http://schemas.android.com/apk/res/android\"\n" +
@@ -1681,20 +1696,30 @@ public class RenderTests extends RenderTestBase {
}
@Test
- public void testHighQualityShadowWidgetWithScroll() throws Exception {
- LayoutPullParser parser = createParserFromPath("shadows_scrollview.xml");
+ public void testNinePatchDrawable() throws Exception {
+ String layout =
+ "<LinearLayout xmlns:android=\"http://schemas.android.com/apk/res/android\"\n" +
+ " android:padding=\"16dp\"\n" +
+ " android:orientation=\"horizontal\"\n" +
+ " android:layout_width=\"fill_parent\"\n" +
+ " android:layout_height=\"fill_parent\">\n" +
+ " <ImageView\n" +
+ " android:layout_height=\"fill_parent\"\n" +
+ " android:layout_width=\"fill_parent\"\n" +
+ " android:src=\"@drawable/ninepatch_drawable\" />\n" +
+ "</LinearLayout>\n";
+ LayoutPullParser parser = LayoutPullParser.createFromString(layout);
+ // Create LayoutLibCallback.
LayoutLibTestCallback layoutLibCallback =
new LayoutLibTestCallback(getLogger(), mDefaultClassLoader);
layoutLibCallback.initResources();
SessionParams params = getSessionParamsBuilder()
.setParser(parser)
.setCallback(layoutLibCallback)
+ .disableDecoration()
.build();
- renderAndVerify(params, "shadow_scrollview_test_high_quality.png");
- // We expect fidelity warnings for Path.isConvex. Fail for anything else.
- sRenderMessages.removeIf(message -> message.equals("Path.isConvex is not supported."));
- sRenderMessages.removeIf(message -> message.equals("Font$Builder.nAddAxis is not supported."));
+ renderAndVerify(params, "ninepatch_drawable.png");
}
@Test
@@ -1776,4 +1801,204 @@ public class RenderTests extends RenderTestBase {
renderAndVerify(params, "textclock.png");
}
+
+ @Test
+ public void testChangeSize() throws ClassNotFoundException {
+ final String layout =
+ "<LinearLayout xmlns:android=\"http://schemas.android.com/apk/res/android\"\n" +
+ " android:orientation=\"vertical\"\n" +
+ " android:layout_width=\"wrap_content\"\n" +
+ " android:layout_height=\"wrap_content\">\n" +
+ " <Button\n" +
+ " android:layout_height=\"50dp\"\n" +
+ " android:layout_width=\"100dp\"\n" +
+ " android:text=\"Hello\" />\n" +
+ "</LinearLayout>\n";
+
+ // Create the layout pull parser.
+ LayoutPullParser parser = LayoutPullParser.createFromString(layout);
+ // Create LayoutLibCallback.
+ LayoutLibTestCallback layoutLibCallback = new LayoutLibTestCallback(getLogger(), mDefaultClassLoader);
+ layoutLibCallback.initResources();
+
+ SessionParams params = getSessionParamsBuilder()
+ .setParser(parser)
+ .setCallback(layoutLibCallback)
+ .setRenderingMode(RenderingMode.SHRINK)
+ .disableDecoration()
+ .build();
+
+ System_Delegate.setBootTimeNanos(TimeUnit.MILLISECONDS.toNanos(871732800000L));
+ System_Delegate.setNanosTime(TimeUnit.MILLISECONDS.toNanos(871732800000L));
+ RenderSession session = sBridge.createSession(params);
+
+ try {
+ session.setElapsedFrameTimeNanos(TimeUnit.SECONDS.toNanos(2));
+
+ if (!session.getResult().isSuccess()) {
+ getLogger().error(session.getResult().getException(),
+ session.getResult().getErrorMessage());
+ }
+ else {
+ // Render the session with a timeout of 50s.
+ Result renderResult = session.render(50000);
+ if (!renderResult.isSuccess()) {
+ getLogger().error(session.getResult().getException(),
+ session.getResult().getErrorMessage());
+ }
+ }
+
+ BufferedImage resultImage = session.getImage();
+
+ assertNotNull(resultImage);
+ verify("button_resize.png", resultImage);
+
+ Object viewObject = session.getRootViews().get(0)
+ .getChildren().get(0).getViewObject();
+
+ Button btn = (Button) viewObject;
+ btn.setLayoutParams(new LinearLayout.LayoutParams(300, 300));
+
+ Result renderResult = session.render(50000);
+ if (!renderResult.isSuccess()) {
+ getLogger().error(session.getResult().getException(),
+ session.getResult().getErrorMessage());
+ }
+
+ resultImage = session.getImage();
+
+ assertNotNull(resultImage);
+ verify("button_resize2.png", resultImage);
+ } finally {
+ session.dispose();
+ }
+ }
+
+ /**
+ * Tests that theme attributes are not resolved when using Resources_Delegate.obtainAttributes
+ * <p/>
+ * http://b/175943371
+ */
+ @Test
+ public void testNonStyledResources() throws ClassNotFoundException {
+ // Create the layout pull parser.
+ LayoutPullParser parser = LayoutPullParser.createFromString(
+ "<LinearLayout xmlns:android=\"http://schemas.android.com/apk/res/android\"\n" +
+ " android:padding=\"16dp\"\n" +
+ " android:background=\"#999\"" +
+ " android:orientation=\"horizontal\"\n" +
+ " android:layout_width=\"match_parent\"\n" +
+ " android:layout_height=\"match_parent\">\n" +
+ " <com.android.layoutlib.bridge.test.widgets.CustomImageView\n" +
+ " android:layout_width=\"100dp\"\n" +
+ " android:layout_height=\"100dp\"/>\n" +
+ "</LinearLayout>");
+ // Create LayoutLibCallback.
+ LayoutLibTestCallback layoutLibCallback =
+ new LayoutLibTestCallback(getLogger(), mDefaultClassLoader);
+ layoutLibCallback.initResources();
+
+ SessionParams params = getSessionParamsBuilder()
+ .setParser(parser)
+ .setCallback(layoutLibCallback)
+ .setTheme("Theme.Material.Light.NoActionBar.Fullscreen", false)
+ .setRenderingMode(RenderingMode.V_SCROLL)
+ .disableDecoration()
+ .build();
+
+ renderAndVerify(params, "non-styled_resources.png",
+ TimeUnit.SECONDS.toNanos(2));
+ }
+
+ @Test
+ public void testRenderEffect() throws ClassNotFoundException {
+ // Create the layout pull parser.
+ LayoutPullParser parser = LayoutPullParser.createFromString(
+ "<LinearLayout xmlns:android=\"http://schemas.android.com/apk/res/android\"\n" +
+ " android:padding=\"16dp\"\n" +
+ " android:background=\"#999\"" +
+ " android:orientation=\"horizontal\"\n" +
+ " android:layout_width=\"match_parent\"\n" +
+ " android:layout_height=\"match_parent\">\n" +
+ " <com.android.layoutlib.bridge.test.widgets.BlurryImageView\n" +
+ " android:layout_width=\"100dp\"\n" +
+ " android:layout_height=\"100dp\"/>\n" +
+ "</LinearLayout>");
+ // Create LayoutLibCallback.
+ LayoutLibTestCallback layoutLibCallback =
+ new LayoutLibTestCallback(getLogger(), mDefaultClassLoader);
+ layoutLibCallback.initResources();
+
+ SessionParams params = getSessionParamsBuilder()
+ .setParser(parser)
+ .setCallback(layoutLibCallback)
+ .setTheme("Theme.Material.Light.NoActionBar.Fullscreen", false)
+ .setRenderingMode(RenderingMode.V_SCROLL)
+ .disableDecoration()
+ .build();
+
+ renderAndVerify(params, "render_effect.png",
+ TimeUnit.SECONDS.toNanos(2));
+ }
+
+ @Test
+ public void testDialog() throws Exception {
+ String layout =
+ "<LinearLayout xmlns:android=\"http://schemas.android.com/apk/res/android\"\n" +
+ " android:padding=\"16dp\"\n" +
+ " android:orientation=\"horizontal\"\n" +
+ " android:layout_width=\"fill_parent\"\n" +
+ " android:layout_height=\"fill_parent\">\n" +
+ " <com.android.layoutlib.test.myapplication.widgets.DialogView\n" +
+ " android:layout_height=\"wrap_content\"\n" +
+ " android:layout_width=\"wrap_content\" />\n" +
+ "</LinearLayout>\n";
+ LayoutPullParser parser = LayoutPullParser.createFromString(layout);
+ // Create LayoutLibCallback.
+ LayoutLibTestCallback layoutLibCallback =
+ new LayoutLibTestCallback(getLogger(), mDefaultClassLoader);
+ layoutLibCallback.initResources();
+
+ SessionParams params = getSessionParamsBuilder()
+ .setParser(parser)
+ .setCallback(layoutLibCallback)
+ .setTheme("Theme.Material.Light.NoActionBar.Fullscreen", false)
+ .setRenderingMode(RenderingMode.V_SCROLL)
+ .disableDecoration()
+ .build();
+
+ renderAndVerify(params, "dialog.png",
+ TimeUnit.SECONDS.toNanos(2));
+ }
+
+ @Test
+ public void testWindowBackgroundWithThemeAttribute() throws Exception {
+ String layout =
+ "<LinearLayout xmlns:android=\"http://schemas.android.com/apk/res/android\"\n" +
+ " android:padding=\"16dp\"\n" +
+ " android:orientation=\"horizontal\"\n" +
+ " android:layout_width=\"fill_parent\"\n" +
+ " android:layout_height=\"fill_parent\">\n" +
+ " <TextView\n" +
+ " android:layout_height=\"wrap_content\"\n" +
+ " android:layout_width=\"wrap_content\"\n" +
+ " android:text=\"Hello World!\" />\n" +
+ "</LinearLayout>\n";
+ LayoutPullParser parser = LayoutPullParser.createFromString(layout);
+ // Create LayoutLibCallback.
+ LayoutLibTestCallback layoutLibCallback =
+ new LayoutLibTestCallback(getLogger(), mDefaultClassLoader);
+ layoutLibCallback.initResources();
+
+ SessionParams params = getSessionParamsBuilder()
+ .setParser(parser)
+ .setCallback(layoutLibCallback)
+ .setTheme("WindowBackgroundTheme", true)
+ .setRenderingMode(RenderingMode.V_SCROLL)
+ .disableDecoration()
+ .build();
+
+ renderAndVerify(params, "window_background.png",
+ TimeUnit.SECONDS.toNanos(2));
+ }
}
diff --git a/bridge/tests/src/com/android/layoutlib/bridge/intensive/HighQualityShadowsRenderTests.java b/bridge/tests/src/com/android/layoutlib/bridge/intensive/ShadowsRenderTests.java
index 3582860cdc..5c997dcd34 100644
--- a/bridge/tests/src/com/android/layoutlib/bridge/intensive/HighQualityShadowsRenderTests.java
+++ b/bridge/tests/src/com/android/layoutlib/bridge/intensive/ShadowsRenderTests.java
@@ -24,14 +24,14 @@ import com.android.layoutlib.bridge.intensive.setup.LayoutPullParser;
import org.junit.After;
import org.junit.Test;
-public class HighQualityShadowsRenderTests extends RenderTestBase {
+public class ShadowsRenderTests extends RenderTestBase {
@After
public void afterTestCase() {
com.android.layoutlib.bridge.test.widgets.HookWidget.reset();
}
@Test
- public void testHighQualityRectangleShadow() throws Exception {
+ public void testRectangleShadow() throws Exception {
LayoutPullParser parser = createParserFromPath("shadows_test.xml");
LayoutLibTestCallback layoutLibCallback =
new LayoutLibTestCallback(getLogger(), mDefaultClassLoader);
@@ -43,10 +43,7 @@ public class HighQualityShadowsRenderTests extends RenderTestBase {
.disableDecoration()
.build();
- renderAndVerify(params, "shadows_test_high_quality.png");
- // We expect fidelity warnings for Path.isConvex. Fail for anything else.
- sRenderMessages.removeIf(message -> message.equals("Path.isConvex is not supported."));
- sRenderMessages.removeIf(message -> message.equals("Font$Builder.nAddAxis is not supported."));
+ renderAndVerify(params, "shadows_test.png");
}
@Test
@@ -61,10 +58,7 @@ public class HighQualityShadowsRenderTests extends RenderTestBase {
.setCallback(layoutLibCallback)
.build();
- renderAndVerify(params, "shadows_test_high_quality_rounded_edge.png");
- // We expect fidelity warnings for Path.isConvex. Fail for anything else.
- sRenderMessages.removeIf(message -> message.equals("Path.isConvex is not supported."));
- sRenderMessages.removeIf(message -> message.equals("Font$Builder.nAddAxis is not supported."));
+ renderAndVerify(params, "shadows_test_rounded_edge.png");
}
@Test
@@ -80,9 +74,7 @@ public class HighQualityShadowsRenderTests extends RenderTestBase {
.disableDecoration()
.build();
- renderAndVerify(params, "large_shadows_test_high_quality.png");
- // We expect fidelity warnings for Path.isConvex. Fail for anything else.
- sRenderMessages.removeIf(message -> message.equals("Path.isConvex is not supported."));
+ renderAndVerify(params, "large_shadows_test.png");
}
@Test
@@ -97,9 +89,7 @@ public class HighQualityShadowsRenderTests extends RenderTestBase {
.setCallback(layoutLibCallback)
.build();
- renderAndVerify(params, "shadow_sizes_test_high_quality.png");
- // We expect fidelity warnings for Path.isConvex. Fail for anything else.
- sRenderMessages.removeIf(message -> message.equals("Path.isConvex is not supported."));
+ renderAndVerify(params, "shadow_sizes_test.png");
}
@Test
@@ -113,9 +103,6 @@ public class HighQualityShadowsRenderTests extends RenderTestBase {
.setCallback(layoutLibCallback)
.build();
- renderAndVerify(params, "shadow_scrollview_test_high_quality.png");
- // We expect fidelity warnings for Path.isConvex. Fail for anything else.
- sRenderMessages.removeIf(message -> message.equals("Path.isConvex is not supported."));
- sRenderMessages.removeIf(message -> message.equals("Font$Builder.nAddAxis is not supported."));
+ renderAndVerify(params, "shadow_scrollview_test.png");
}
}
diff --git a/bridge/tests/src/com/android/layoutlib/bridge/intensive/setup/LayoutLibTestCallback.java b/bridge/tests/src/com/android/layoutlib/bridge/intensive/setup/LayoutLibTestCallback.java
index 9faa0a64ae..8dd7ec7a61 100644
--- a/bridge/tests/src/com/android/layoutlib/bridge/intensive/setup/LayoutLibTestCallback.java
+++ b/bridge/tests/src/com/android/layoutlib/bridge/intensive/setup/LayoutLibTestCallback.java
@@ -16,7 +16,6 @@
package com.android.layoutlib.bridge.intensive.setup;
-import com.android.SdkConstants;
import com.android.ide.common.rendering.api.ActionBarCallback;
import com.android.ide.common.rendering.api.AdapterBinding;
import com.android.ide.common.rendering.api.ILayoutPullParser;
@@ -60,8 +59,6 @@ public class LayoutLibTestCallback extends LayoutlibCallback {
private final ActionBarCallback mActionBarCallback = new ActionBarCallback();
private final ClassLoader mModuleClassLoader;
private String mAdaptiveIconMaskPath;
- private boolean mSetUseShadow = true;
- private boolean mHighShadowQuality = true;
public LayoutLibTestCallback(ILogger logger, ClassLoader classLoader) {
mLog = logger;
@@ -188,6 +185,11 @@ public class LayoutLibTestCallback extends LayoutlibCallback {
}
@Override
+ public String getResourcePackage() {
+ return PACKAGE_NAME;
+ }
+
+ @Override
public Class<?> findClass(String name) throws ClassNotFoundException {
return mModuleClassLoader.loadClass(name);
}
diff --git a/bridge/tests/src/com/android/layoutlib/bridge/intensive/util/ImageUtils.java b/bridge/tests/src/com/android/layoutlib/bridge/intensive/util/ImageUtils.java
index b1801c6d57..a8adf95d1a 100644
--- a/bridge/tests/src/com/android/layoutlib/bridge/intensive/util/ImageUtils.java
+++ b/bridge/tests/src/com/android/layoutlib/bridge/intensive/util/ImageUtils.java
@@ -18,10 +18,8 @@ package com.android.layoutlib.bridge.intensive.util;
import android.annotation.NonNull;
-import java.awt.AlphaComposite;
import java.awt.Color;
import java.awt.Graphics;
-import java.awt.Graphics2D;
import java.awt.image.BufferedImage;
import java.io.File;
import java.io.IOException;
@@ -29,7 +27,6 @@ import java.io.InputStream;
import javax.imageio.ImageIO;
-import static java.awt.RenderingHints.*;
import static java.awt.image.BufferedImage.TYPE_INT_ARGB;
import static java.io.File.separatorChar;
import static org.junit.Assert.assertEquals;
@@ -47,28 +44,22 @@ import static org.junit.Assert.fail;
*/
public class ImageUtils {
/**
- * Normally, this test will fail when there is a missing thumbnail. However, when
+ * Normally, this test will fail when there is a missing golden image. However, when
* you create creating a new test, it's useful to be able to turn this off such that
- * you can generate all the missing thumbnails in one go, rather than having to run
- * the test repeatedly to get to each new render assertion generating its thumbnail.
+ * you can generate all the missing golden images in one go, rather than having to run
+ * the test repeatedly to get to each new render assertion generating its golden images.
*/
- private static final boolean FAIL_ON_MISSING_THUMBNAIL = true;
-
- private static final int THUMBNAIL_SIZE = 1000;
+ private static final boolean FAIL_ON_MISSING_GOLDEN = true;
private static final double MAX_PERCENT_DIFFERENCE = 0.1;
public static void requireSimilar(@NonNull String relativePath, @NonNull BufferedImage image)
throws IOException {
- int maxDimension = Math.max(image.getWidth(), image.getHeight());
- double scale = THUMBNAIL_SIZE / (double)maxDimension;
- BufferedImage thumbnail = scale(image, scale, scale);
-
InputStream is = ImageUtils.class.getClassLoader().getResourceAsStream(relativePath);
if (is == null) {
- String message = "Unable to load golden thumbnail: " + relativePath + "\n";
- message = saveImageAndAppendMessage(thumbnail, message, relativePath);
- if (FAIL_ON_MISSING_THUMBNAIL) {
+ String message = "Unable to load golden image: " + relativePath + "\n";
+ message = saveImageAndAppendMessage(image, message, relativePath);
+ if (FAIL_ON_MISSING_GOLDEN) {
fail(message);
} else {
System.out.println(message);
@@ -77,7 +68,7 @@ public class ImageUtils {
else {
try {
BufferedImage goldenImage = ImageIO.read(is);
- assertImageSimilar(relativePath, goldenImage, thumbnail, MAX_PERCENT_DIFFERENCE);
+ assertImageSimilar(relativePath, goldenImage, image, MAX_PERCENT_DIFFERENCE);
} finally {
is.close();
}
@@ -192,114 +183,7 @@ public class ImageUtils {
}
/**
- * Resize the given image
- *
- * @param source the image to be scaled
- * @param xScale x scale
- * @param yScale y scale
- * @return the scaled image
- */
- @NonNull
- public static BufferedImage scale(@NonNull BufferedImage source, double xScale, double yScale) {
-
- int sourceWidth = source.getWidth();
- int sourceHeight = source.getHeight();
- int destWidth = Math.max(1, (int) (xScale * sourceWidth));
- int destHeight = Math.max(1, (int) (yScale * sourceHeight));
- int imageType = source.getType();
- if (imageType == BufferedImage.TYPE_CUSTOM) {
- imageType = BufferedImage.TYPE_INT_ARGB;
- }
- if (xScale > 0.5 && yScale > 0.5) {
- BufferedImage scaled =
- new BufferedImage(destWidth, destHeight, imageType);
- Graphics2D g2 = scaled.createGraphics();
- g2.setComposite(AlphaComposite.Src);
- g2.setColor(new Color(0, true));
- g2.fillRect(0, 0, destWidth, destHeight);
- if (xScale == 1 && yScale == 1) {
- g2.drawImage(source, 0, 0, null);
- } else {
- setRenderingHints(g2);
- g2.drawImage(source, 0, 0, destWidth, destHeight, 0, 0, sourceWidth, sourceHeight,
- null);
- }
- g2.dispose();
- return scaled;
- } else {
- // When creating a thumbnail, using the above code doesn't work very well;
- // you get some visible artifacts, especially for text. Instead use the
- // technique of repeatedly scaling the image into half; this will cause
- // proper averaging of neighboring pixels, and will typically (for the kinds
- // of screen sizes used by this utility method in the layout editor) take
- // about 3-4 iterations to get the result since we are logarithmically reducing
- // the size. Besides, each successive pass in operating on much fewer pixels
- // (a reduction of 4 in each pass).
- //
- // However, we may not be resizing to a size that can be reached exactly by
- // successively diving in half. Therefore, once we're within a factor of 2 of
- // the final size, we can do a resize to the exact target size.
- // However, we can get even better results if we perform this final resize
- // up front. Let's say we're going from width 1000 to a destination width of 85.
- // The first approach would cause a resize from 1000 to 500 to 250 to 125, and
- // then a resize from 125 to 85. That last resize can distort/blur a lot.
- // Instead, we can start with the destination width, 85, and double it
- // successfully until we're close to the initial size: 85, then 170,
- // then 340, and finally 680. (The next one, 1360, is larger than 1000).
- // So, now we *start* the thumbnail operation by resizing from width 1000 to
- // width 680, which will preserve a lot of visual details such as text.
- // Then we can successively resize the image in half, 680 to 340 to 170 to 85.
- // We end up with the expected final size, but we've been doing an exact
- // divide-in-half resizing operation at the end so there is less distortion.
-
- int iterations = 0; // Number of halving operations to perform after the initial resize
- int nearestWidth = destWidth; // Width closest to source width that = 2^x, x is integer
- int nearestHeight = destHeight;
- while (nearestWidth < sourceWidth / 2) {
- nearestWidth *= 2;
- nearestHeight *= 2;
- iterations++;
- }
-
- BufferedImage scaled = new BufferedImage(nearestWidth, nearestHeight, imageType);
-
- Graphics2D g2 = scaled.createGraphics();
- setRenderingHints(g2);
- g2.drawImage(source, 0, 0, nearestWidth, nearestHeight, 0, 0, sourceWidth, sourceHeight,
- null);
- g2.dispose();
-
- sourceWidth = nearestWidth;
- sourceHeight = nearestHeight;
- source = scaled;
-
- for (int iteration = iterations - 1; iteration >= 0; iteration--) {
- int halfWidth = sourceWidth / 2;
- int halfHeight = sourceHeight / 2;
- scaled = new BufferedImage(halfWidth, halfHeight, imageType);
- g2 = scaled.createGraphics();
- setRenderingHints(g2);
- g2.drawImage(source, 0, 0, halfWidth, halfHeight, 0, 0, sourceWidth, sourceHeight,
- null);
- g2.dispose();
-
- sourceWidth = halfWidth;
- sourceHeight = halfHeight;
- source = scaled;
- iterations--;
- }
- return scaled;
- }
- }
-
- private static void setRenderingHints(@NonNull Graphics2D g2) {
- g2.setRenderingHint(KEY_INTERPOLATION,VALUE_INTERPOLATION_BILINEAR);
- g2.setRenderingHint(KEY_RENDERING, VALUE_RENDER_QUALITY);
- g2.setRenderingHint(KEY_ANTIALIASING, VALUE_ANTIALIAS_ON);
- }
-
- /**
- * Directory where to write the thumbnails and deltas.
+ * Directory where to write the generated image and deltas.
*/
@NonNull
private static File getFailureDir() {
@@ -318,7 +202,7 @@ public class ImageUtils {
}
/**
- * Saves the generated thumbnail image and appends the info message to an initial message
+ * Saves the generated golden image and appends the info message to an initial message
*/
@NonNull
private static String saveImageAndAppendMessage(@NonNull BufferedImage image,
@@ -329,7 +213,7 @@ public class ImageUtils {
assertTrue(deleted);
}
ImageIO.write(image, "PNG", output);
- initialMessage += "Thumbnail for current rendering stored at " + output.getPath();
+ initialMessage += "Golden image for current rendering stored at " + output.getPath();
// initialMessage += "\nRun the following command to accept the changes:\n";
// initialMessage += String.format("mv %1$s %2$s", output.getPath(),
// ImageUtils.class.getResource(relativePath).getPath());
diff --git a/bridge/tests/src/com/android/layoutlib/bridge/intensive/util/ModuleClassLoader.java b/bridge/tests/src/com/android/layoutlib/bridge/intensive/util/ModuleClassLoader.java
index da360f37df..d52fdcf9d4 100644
--- a/bridge/tests/src/com/android/layoutlib/bridge/intensive/util/ModuleClassLoader.java
+++ b/bridge/tests/src/com/android/layoutlib/bridge/intensive/util/ModuleClassLoader.java
@@ -17,6 +17,7 @@
package com.android.layoutlib.bridge.intensive.util;
import java.io.IOException;
+import java.io.InputStream;
import java.util.HashMap;
import java.util.Map;
@@ -49,7 +50,11 @@ public class ModuleClassLoader extends ClassLoader {
if (clazz == null) {
String path = name.replace('.', '/').concat(".class");
try {
- byte[] b = Streams.readFully(getResourceAsStream(myModuleRoot + path));
+ InputStream classStream = getResourceAsStream(myModuleRoot + path);
+ if (classStream == null) {
+ throw new IOException("Cannot find resource stream for class " + name);
+ }
+ byte[] b = Streams.readFully(classStream);
clazz = defineClass(name, b, 0, b.length);
mClasses.put(name, clazz);
} catch (IOException ignore) {
diff --git a/bridge/tests/src/com/android/layoutlib/bridge/intensive/util/SessionParamsBuilder.java b/bridge/tests/src/com/android/layoutlib/bridge/intensive/util/SessionParamsBuilder.java
index 396880eb00..35ade3454c 100644
--- a/bridge/tests/src/com/android/layoutlib/bridge/intensive/util/SessionParamsBuilder.java
+++ b/bridge/tests/src/com/android/layoutlib/bridge/intensive/util/SessionParamsBuilder.java
@@ -61,10 +61,9 @@ public class SessionParamsBuilder {
private AssetRepository mAssetRepository = null;
private boolean mDecor = true;
private IImageFactory mImageFactory = null;
- private boolean enableShadows = true;
- private boolean highQualityShadows = true;
private boolean enableLayoutValidator = false;
private boolean enableLayoutValidatorImageCheck = false;
+ private boolean transparentBackground = false;
@NonNull
public SessionParamsBuilder setParser(@NonNull LayoutPullParser layoutParser) {
@@ -166,18 +165,6 @@ public class SessionParamsBuilder {
}
@NonNull
- public SessionParamsBuilder disableShadows() {
- this.enableShadows = false;
- return this;
- }
-
- @NonNull
- public SessionParamsBuilder disableHighQualityShadows() {
- this.highQualityShadows = false;
- return this;
- }
-
- @NonNull
public SessionParamsBuilder enableLayoutValidation() {
this.enableLayoutValidator = true;
return this;
@@ -188,6 +175,12 @@ public class SessionParamsBuilder {
this.enableLayoutValidatorImageCheck = true;
return this;
}
+
+ @NonNull
+ public SessionParamsBuilder setTransparentBackground() {
+ this.transparentBackground = true;
+ return this;
+ }
@NonNull
public SessionParams build() {
@@ -210,8 +203,6 @@ public class SessionParamsBuilder {
SessionParams params = new SessionParams(mLayoutParser, mRenderingMode, mProjectKey /* for
caching */, mConfigGenerator.getHardwareConfig(), resourceResolver, mLayoutlibCallback,
mMinSdk, mTargetSdk, mLayoutLog);
- params.setFlag(RenderParamsFlags.FLAG_ENABLE_SHADOW, enableShadows);
- params.setFlag(RenderParamsFlags.FLAG_RENDER_HIGH_QUALITY_SHADOW, highQualityShadows);
params.setFlag(RenderParamsFlags.FLAG_ENABLE_LAYOUT_VALIDATOR, enableLayoutValidator);
params.setFlag(
RenderParamsFlags.FLAG_ENABLE_LAYOUT_VALIDATOR_IMAGE_CHECK,
@@ -227,6 +218,10 @@ public class SessionParamsBuilder {
params.setForceNoDecor();
}
+ if (transparentBackground) {
+ params.setTransparentBackground();
+ }
+
return params;
}
}
diff --git a/bridge/tests/src/com/android/layoutlib/bridge/test/widgets/BlurryImageView.java b/bridge/tests/src/com/android/layoutlib/bridge/test/widgets/BlurryImageView.java
new file mode 100644
index 0000000000..a2268b91be
--- /dev/null
+++ b/bridge/tests/src/com/android/layoutlib/bridge/test/widgets/BlurryImageView.java
@@ -0,0 +1,53 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.layoutlib.bridge.test.widgets;
+
+import android.R;
+import android.content.Context;
+import android.graphics.RenderEffect;
+import android.graphics.Shader;
+import android.graphics.drawable.Drawable;
+import android.util.AttributeSet;
+import android.widget.ImageView;
+
+public class BlurryImageView extends ImageView {
+ public BlurryImageView(Context context) {
+ super(context);
+ init(context);
+ }
+
+ public BlurryImageView(Context context, AttributeSet attrs) {
+ super(context, attrs);
+ init(context);
+ }
+
+ public BlurryImageView(Context context, AttributeSet attrs, int defStyleAttr) {
+ super(context, attrs, defStyleAttr);
+ init(context);
+ }
+
+ public BlurryImageView(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) {
+ super(context, attrs, defStyleAttr, defStyleRes);
+ init(context);
+ }
+
+ private void init(Context context) {
+ Drawable drawable = context.getResources().getDrawable(R.drawable.star_big_on, null);
+ setImageDrawable(drawable);
+ setRenderEffect(RenderEffect.createBlurEffect(20f, 20f, Shader.TileMode.MIRROR));
+ }
+}
diff --git a/bridge/tests/src/com/android/layoutlib/bridge/test/widgets/CustomImageView.java b/bridge/tests/src/com/android/layoutlib/bridge/test/widgets/CustomImageView.java
new file mode 100644
index 0000000000..d1326c8262
--- /dev/null
+++ b/bridge/tests/src/com/android/layoutlib/bridge/test/widgets/CustomImageView.java
@@ -0,0 +1,50 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.layoutlib.bridge.test.widgets;
+
+import android.R;
+import android.content.Context;
+import android.graphics.drawable.Drawable;
+import android.util.AttributeSet;
+import android.widget.ImageView;
+
+public class CustomImageView extends ImageView {
+ public CustomImageView(Context context) {
+ super(context);
+ init(context);
+ }
+
+ public CustomImageView(Context context, AttributeSet attrs) {
+ super(context, attrs);
+ init(context);
+ }
+
+ public CustomImageView(Context context, AttributeSet attrs, int defStyleAttr) {
+ super(context, attrs, defStyleAttr);
+ init(context);
+ }
+
+ public CustomImageView(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) {
+ super(context, attrs, defStyleAttr, defStyleRes);
+ init(context);
+ }
+
+ private void init(Context context) {
+ Drawable drawable = context.getResources().getDrawable(R.drawable.ic_lock_idle_alarm, null);
+ setImageDrawable(drawable);
+ }
+}
diff --git a/bridge/tests/src/com/android/layoutlib/bridge/util/ChoreographerCallbacksTest.java b/bridge/tests/src/com/android/layoutlib/bridge/util/ChoreographerCallbacksTest.java
new file mode 100644
index 0000000000..68e7006b05
--- /dev/null
+++ b/bridge/tests/src/com/android/layoutlib/bridge/util/ChoreographerCallbacksTest.java
@@ -0,0 +1,107 @@
+/*
+ * 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.layoutlib.bridge.util;
+
+import com.android.ide.common.rendering.api.ILayoutLog;
+
+import org.junit.Assert;
+import org.junit.Before;
+import org.junit.Test;
+
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.view.Choreographer.FrameCallback;
+
+import java.util.ArrayList;
+
+public class ChoreographerCallbacksTest {
+
+ private static class ValidatingLogger implements ILayoutLog {
+ @Override
+ public void error(@Nullable String tag, @NonNull String message,
+ @Nullable Object viewCookie, @Nullable Object data) {
+ errorMessages.add(message);
+ }
+
+ @Override
+ public void error(@Nullable String tag, @NonNull String message,
+ @Nullable Throwable throwable, @Nullable Object viewCookie, @Nullable Object data) {
+ errorMessages.add(message);
+ }
+
+ private final ArrayList<String> errorMessages = new ArrayList<>();
+ }
+
+ private final ValidatingLogger logger = new ValidatingLogger();
+
+ @Before
+ public void setUp() {
+ logger.errorMessages.clear();
+ }
+
+ @Test
+ public void testAddAndExecuteInOrder() {
+ ChoreographerCallbacks callbacks = new ChoreographerCallbacks();
+ ArrayList<Integer> order = new ArrayList<>();
+
+ callbacks.add((Runnable) () -> order.add(2), 200);
+ callbacks.add((FrameCallback) frameTimeNanos -> order.add(1), 100);
+ callbacks.execute(200, logger);
+
+ Assert.assertArrayEquals(order.toArray(), new Object[] { 1, 2 });
+ Assert.assertTrue(logger.errorMessages.isEmpty());
+ }
+
+ @Test
+ public void testAddAndExecuteOnlyDue() {
+ ChoreographerCallbacks callbacks = new ChoreographerCallbacks();
+ ArrayList<Integer> order = new ArrayList<>();
+
+ callbacks.add((Runnable) () -> order.add(2), 200);
+ callbacks.add((FrameCallback) frameTimeNanos -> order.add(1), 100);
+ callbacks.execute(100, logger);
+
+ Assert.assertArrayEquals(order.toArray(), new Object[] { 1 });
+ Assert.assertTrue(logger.errorMessages.isEmpty());
+ }
+
+ @Test
+ public void testRemove() {
+ ChoreographerCallbacks callbacks = new ChoreographerCallbacks();
+ ArrayList<Integer> order = new ArrayList<>();
+
+ Runnable runnable = () -> order.add(2);
+ callbacks.add(runnable, 200);
+ callbacks.add((FrameCallback) frameTimeNanos -> order.add(1), 100);
+ callbacks.remove(runnable);
+ callbacks.execute(200, logger);
+
+ Assert.assertArrayEquals(order.toArray(), new Object[] { 1 });
+ Assert.assertTrue(logger.errorMessages.isEmpty());
+ }
+
+ @Test
+ public void testErrorIfUnknownCallbackType() {
+ ChoreographerCallbacks callbacks = new ChoreographerCallbacks();
+
+ callbacks.add(new Object(), 100);
+ callbacks.execute(200, logger);
+
+ Assert.assertFalse(logger.errorMessages.isEmpty());
+ Assert.assertEquals(logger.errorMessages.get(0), "Unexpected action as Choreographer callback");
+ }
+}
diff --git a/bridge/tests/src/com/android/layoutlib/bridge/util/HandlerMessageQueueTest.java b/bridge/tests/src/com/android/layoutlib/bridge/util/HandlerMessageQueueTest.java
new file mode 100644
index 0000000000..f7b091e1a2
--- /dev/null
+++ b/bridge/tests/src/com/android/layoutlib/bridge/util/HandlerMessageQueueTest.java
@@ -0,0 +1,124 @@
+/*
+ * 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.layoutlib.bridge.util;
+
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Test;
+
+import android.os.Handler;
+import android.os.Looper;
+import android.os.Looper_Accessor;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertNull;
+import static org.junit.Assert.assertTrue;
+
+public class HandlerMessageQueueTest {
+
+ @Before
+ public void setUp() {
+ Looper.prepareMainLooper();
+ }
+
+ @Test
+ public void testSingleHandler_SingleRunnable() {
+ HandlerMessageQueue q = new HandlerMessageQueue();
+ Handler h1 = new Handler();
+ Runnable r1 = () -> {};
+
+ assertFalse(q.isNotEmpty());
+
+ q.add(h1, 100, r1);
+
+ assertTrue(q.isNotEmpty());
+ assertNull(q.extractFirst(0));
+ assertEquals(q.extractFirst(100), r1);
+ assertFalse(q.isNotEmpty());
+ assertNull(q.extractFirst(100));
+ }
+
+ @Test
+ public void testSingleHandler_MultipleRunnables() {
+ HandlerMessageQueue q = new HandlerMessageQueue();
+ Handler h1 = new Handler();
+ Runnable r1 = () -> {};
+ Runnable r2 = () -> {};
+ Runnable r3 = () -> {};
+
+ q.add(h1, 100, r1);
+ q.add(h1, 100, r2);
+ q.add(h1, 50, r3);
+
+ assertEquals(q.extractFirst(100), r3);
+ assertEquals(q.extractFirst(100), r1);
+ assertTrue(q.isNotEmpty());
+ assertEquals(q.extractFirst(100), r2);
+ assertFalse(q.isNotEmpty());
+ }
+
+ @Test
+ public void testMultipleHandlers() {
+ HandlerMessageQueue q = new HandlerMessageQueue();
+ Handler h1 = new Handler();
+ Handler h2 = new Handler();
+ Runnable r1 = () -> {};
+ Runnable r2 = () -> {};
+ Runnable r3 = () -> {};
+
+ q.add(h1, 200, r1);
+ q.add(h2, 100, r2);
+ q.add(h1, 50, r3);
+
+ assertEquals(q.extractFirst(70), r3);
+ assertEquals(q.extractFirst(300), r2);
+ assertEquals(q.extractFirst(300), r1);
+ assertFalse(q.isNotEmpty());
+ assertNull(q.extractFirst(500));
+
+ q.add(h1, 400, r1);
+
+ assertTrue(q.isNotEmpty());
+ assertEquals(q.extractFirst(500), r1);
+ assertFalse(q.isNotEmpty());
+ }
+
+ @Test
+ public void testMultipleHandlers_Clear() {
+ HandlerMessageQueue q = new HandlerMessageQueue();
+ Handler h1 = new Handler();
+ Handler h2 = new Handler();
+ Runnable r1 = () -> {};
+ Runnable r2 = () -> {};
+ Runnable r3 = () -> {};
+
+ q.add(h1, 200, r1);
+ q.add(h2, 100, r2);
+ q.add(h1, 50, r3);
+
+ q.clear();
+
+ assertFalse(q.isNotEmpty());
+ assertNull(q.extractFirst(200));
+ }
+
+ @After
+ public void tearDown() {
+ Looper_Accessor.cleanupThread();
+ }
+}
diff --git a/bridge/tests/src/com/android/tools/idea/validator/accessibility/AccessibilityValidatorTests.java b/bridge/tests/src/com/android/tools/idea/validator/AccessibilityValidatorTests.java
index 16ad4a2061..4ab7863d38 100644
--- a/bridge/tests/src/com/android/tools/idea/validator/accessibility/AccessibilityValidatorTests.java
+++ b/bridge/tests/src/com/android/tools/idea/validator/AccessibilityValidatorTests.java
@@ -14,29 +14,29 @@
* limitations under the License.
*/
-package com.android.tools.idea.validator.accessibility;
+package com.android.tools.idea.validator;
import com.android.ide.common.rendering.api.RenderSession;
-import com.android.ide.common.rendering.api.SessionParams;
import com.android.layoutlib.bridge.intensive.RenderTestBase;
import com.android.layoutlib.bridge.intensive.setup.ConfigGenerator;
import com.android.layoutlib.bridge.intensive.setup.LayoutLibTestCallback;
import com.android.layoutlib.bridge.intensive.setup.LayoutPullParser;
import com.android.layoutlib.bridge.intensive.util.SessionParamsBuilder;
-import com.android.tools.idea.validator.LayoutValidator;
-import com.android.tools.idea.validator.ValidatorData;
import com.android.tools.idea.validator.ValidatorData.Issue;
import com.android.tools.idea.validator.ValidatorData.Level;
import com.android.tools.idea.validator.ValidatorData.Policy;
import com.android.tools.idea.validator.ValidatorData.Type;
-import com.android.tools.idea.validator.ValidatorResult;
+import org.junit.Ignore;
import org.junit.Test;
import java.util.EnumSet;
import java.util.List;
-import java.util.stream.Collectors;
+import com.google.android.apps.common.testing.accessibility.framework.uielement.DefaultCustomViewBuilderAndroid;
+import com.google.android.apps.common.testing.accessibility.framework.uielement.ViewHierarchyElementAndroid;
+
+import static com.android.tools.idea.validator.ValidatorUtil.filter;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertTrue;
@@ -57,6 +57,26 @@ import static org.junit.Assert.assertTrue;
public class AccessibilityValidatorTests extends RenderTestBase {
@Test
+ public void testPaused() throws Exception {
+ try {
+ LayoutValidator.setPaused(true);
+ render("a11y_test_dup_clickable_bounds.xml", session -> {
+ ValidatorResult result = getRenderResult(session);
+ List<Issue> dupBounds = filter(result.getIssues(), "DuplicateClickableBoundsCheck");
+
+ /**
+ * Expects no errors since disabled. When enabled it should print
+ * the same result as {@link #testDuplicateClickableBoundsCheck}
+ */
+ ExpectedLevels expectedLevels = new ExpectedLevels();
+ expectedLevels.check(dupBounds);
+ });
+ } finally {
+ LayoutValidator.setPaused(false);
+ }
+ }
+
+ @Test
public void testDuplicateClickableBoundsCheck() throws Exception {
render("a11y_test_dup_clickable_bounds.xml", session -> {
ValidatorResult result = getRenderResult(session);
@@ -91,6 +111,7 @@ public class AccessibilityValidatorTests extends RenderTestBase {
ExpectedLevels expectedLevels = new ExpectedLevels();
expectedLevels.expectedVerboses = 3;
expectedLevels.expectedWarnings = 1;
+ expectedLevels.expectedFixes = 0;
expectedLevels.check(redundant);
});
}
@@ -133,6 +154,7 @@ public class AccessibilityValidatorTests extends RenderTestBase {
ExpectedLevels expectedLevels = new ExpectedLevels();
expectedLevels.expectedVerboses = 1;
expectedLevels.expectedErrors = 1;
+ expectedLevels.expectedFixes = 1;
expectedLevels.check(speakableCheck);
// Make sure no other errors in the system.
@@ -155,6 +177,31 @@ public class AccessibilityValidatorTests extends RenderTestBase {
expectedLevels.expectedErrors = 3;
expectedLevels.expectedWarnings = 1; // This is true only if image is passed.
expectedLevels.expectedVerboses = 2;
+ expectedLevels.expectedFixes = 4;
+ expectedLevels.check(textContrast);
+
+ // Make sure no other errors in the system.
+ textContrast = filter(textContrast, EnumSet.of(Level.ERROR));
+ List<Issue> filtered = filter(result.getIssues(), EnumSet.of(Level.ERROR));
+ checkEquals(filtered, textContrast);
+ });
+ }
+
+ /* TODO: {@link LayoutValidator::obtainCharacterLocations is false by default for now }*/
+ @Test
+ @Ignore
+ public void testSwitchTextContrastCheck() throws Exception {
+ render("a11y_test_switch_text_contrast.xml", session -> {
+ ValidatorResult result = getRenderResult(session);
+ List<Issue> textContrast = filter(result.getIssues(), "TextContrastCheck");
+
+ // ATF doesn't count alpha values in a Switch unless image is passed and the character
+ // locations are available.
+ ExpectedLevels expectedLevels = new ExpectedLevels();
+ expectedLevels.expectedErrors = 0;
+ expectedLevels.expectedWarnings = 1; // True only if character locations are available.
+ expectedLevels.expectedVerboses = 2;
+ expectedLevels.expectedFixes = 1;
expectedLevels.check(textContrast);
// Make sure no other errors in the system.
@@ -174,6 +221,7 @@ public class AccessibilityValidatorTests extends RenderTestBase {
ExpectedLevels expectedLevels = new ExpectedLevels();
expectedLevels.expectedErrors = 3;
expectedLevels.expectedVerboses = 3;
+ expectedLevels.expectedFixes = 3;
expectedLevels.check(textContrast);
// Make sure no other errors in the system.
@@ -202,6 +250,49 @@ public class AccessibilityValidatorTests extends RenderTestBase {
}
@Test
+ public void testClassLoaderOverride() throws Exception {
+ final boolean[] overriddenClassLoaderCalled = {false};
+
+ // testAndroid will fail to find class - so to trigger LayoutlibCallback
+ DefaultCustomViewBuilderAndroid testAndroid = new DefaultCustomViewBuilderAndroid() {
+ @Override
+ public Class<?> getClassByName(
+ ViewHierarchyElementAndroid view, String className) {
+ return null;
+ }
+ };
+ // Callback when CustomViewBuilderAndroid fails.
+ LayoutLibTestCallback testCallback =
+ new LayoutLibTestCallback(getLogger(), mDefaultClassLoader) {
+ @Override
+ public Class<?> findClass(String name) throws ClassNotFoundException {
+ if (name.contains("ImageView")) {
+ // Make sure one of the view (ImageView) passes thru here
+ overriddenClassLoaderCalled[0] = true;
+ }
+ return mDefaultClassLoader.loadClass(name);
+ }
+ };
+ try {
+ ValidatorUtil.sDefaultCustomViewBuilderAndroid = testAndroid;
+ render("a11y_test_image_contrast.xml", session -> {
+ ValidatorResult result = getRenderResult(session);
+ List<Issue> imageContrast = filter(result.getIssues(), "ImageContrastCheck");
+
+ ExpectedLevels expectedLevels = new ExpectedLevels();
+ expectedLevels.expectedWarnings = 1;
+ expectedLevels.expectedVerboses = 1;
+ expectedLevels.check(imageContrast);
+
+ // Ensure that the check went thru the overridden class loader.
+ assertTrue(overriddenClassLoaderCalled[0]);
+ }, true, testCallback);
+ } finally {
+ ValidatorUtil.sDefaultCustomViewBuilderAndroid = new DefaultCustomViewBuilderAndroid();
+ }
+ }
+
+ @Test
public void testImageContrastCheckNoImage() throws Exception {
render("a11y_test_image_contrast.xml", session -> {
ValidatorResult result = getRenderResult(session);
@@ -227,6 +318,7 @@ public class AccessibilityValidatorTests extends RenderTestBase {
ExpectedLevels expectedLevels = new ExpectedLevels();
expectedLevels.expectedErrors = 5;
expectedLevels.expectedVerboses = 1;
+ expectedLevels.expectedFixes = 5;
expectedLevels.check(targetSizes);
// Make sure no other errors in the system.
@@ -243,22 +335,13 @@ public class AccessibilityValidatorTests extends RenderTestBase {
}
}
- private List<Issue> filter(List<ValidatorData.Issue> results, EnumSet<Level> errors) {
- return results.stream().filter(
- issue -> errors.contains(issue.mLevel)).collect(Collectors.toList());
- }
-
- private List<Issue> filter(
- List<ValidatorData.Issue> results, String sourceClass) {
- return results.stream().filter(
- issue -> sourceClass.equals(issue.mSourceClass)).collect(Collectors.toList());
- }
-
private ValidatorResult getRenderResult(RenderSession session) {
Object validationData = session.getValidationData();
- assertNotNull(validationData);
- assertTrue(validationData instanceof ValidatorResult);
- return (ValidatorResult) validationData;
+ assertTrue(validationData instanceof ValidatorHierarchy);
+
+ ValidatorResult result = ValidatorUtil.generateResults(LayoutValidator.DEFAULT_POLICY,
+ (ValidatorHierarchy) validationData);
+ return result;
}
private void render(String fileName, RenderSessionListener verifier) throws Exception {
render(fileName, verifier, true);
@@ -268,13 +351,24 @@ public class AccessibilityValidatorTests extends RenderTestBase {
String fileName,
RenderSessionListener verifier,
boolean enableImageCheck) throws Exception {
+ render(
+ fileName,
+ verifier,
+ enableImageCheck,
+ new LayoutLibTestCallback(getLogger(), mDefaultClassLoader));
+ }
+
+ private void render(
+ String fileName,
+ RenderSessionListener verifier,
+ boolean enableImageCheck,
+ LayoutLibTestCallback layoutLibCallback) throws Exception {
LayoutValidator.updatePolicy(new Policy(
EnumSet.of(Type.ACCESSIBILITY, Type.RENDER),
EnumSet.of(Level.ERROR, Level.WARNING, Level.INFO, Level.VERBOSE)));
+ LayoutValidator.setObtainCharacterLocations(false);
LayoutPullParser parser = createParserFromPath(fileName);
- LayoutLibTestCallback layoutLibCallback =
- new LayoutLibTestCallback(getLogger(), mDefaultClassLoader);
layoutLibCallback.initResources();
SessionParamsBuilder params = getSessionParamsBuilder()
.setParser(parser)
@@ -302,12 +396,15 @@ public class AccessibilityValidatorTests extends RenderTestBase {
public int expectedInfos = 0;
// Number of verboses expected
public int expectedVerboses = 0;
+ // Number of fixes expected
+ public int expectedFixes = 0;
public void check(List<Issue> issues) {
int errors = 0;
int warnings = 0;
int infos = 0;
int verboses = 0;
+ int fixes = 0;
for (Issue issue : issues) {
switch (issue.mLevel) {
@@ -324,12 +421,17 @@ public class AccessibilityValidatorTests extends RenderTestBase {
verboses++;
break;
}
+
+ if (issue.mFix != null) {
+ fixes ++;
+ }
}
assertEquals("Number of expected errors", expectedErrors, errors);
assertEquals("Number of expected warnings",expectedWarnings, warnings);
assertEquals("Number of expected infos", expectedInfos, infos);
assertEquals("Number of expected verboses", expectedVerboses, verboses);
+ assertEquals("Number of expected fixes", expectedFixes, fixes);
int size = expectedErrors + expectedWarnings + expectedInfos + expectedVerboses;
assertEquals("expected size", size, issues.size());
diff --git a/bridge/tests/src/com/android/tools/idea/validator/LayoutValidatorTests.java b/bridge/tests/src/com/android/tools/idea/validator/LayoutValidatorTests.java
index ec91e17560..723a2c046b 100644
--- a/bridge/tests/src/com/android/tools/idea/validator/LayoutValidatorTests.java
+++ b/bridge/tests/src/com/android/tools/idea/validator/LayoutValidatorTests.java
@@ -21,14 +21,18 @@ import com.android.layoutlib.bridge.intensive.RenderTestBase;
import com.android.layoutlib.bridge.intensive.setup.ConfigGenerator;
import com.android.layoutlib.bridge.intensive.setup.LayoutLibTestCallback;
import com.android.layoutlib.bridge.intensive.setup.LayoutPullParser;
+import com.android.tools.idea.validator.ValidatorData.CompoundFix;
import com.android.tools.idea.validator.ValidatorData.Issue;
import com.android.tools.idea.validator.ValidatorData.Level;
+import com.android.tools.idea.validator.ValidatorData.SetViewAttributeFix;
+
import com.android.tools.idea.validator.ValidatorData.Type;
import org.junit.Test;
import android.view.View;
+import java.util.ArrayList;
import java.util.EnumSet;
import java.util.Set;
import java.util.stream.Collectors;
@@ -37,10 +41,24 @@ import com.google.android.apps.common.testing.accessibility.framework.Accessibil
import com.google.android.apps.common.testing.accessibility.framework.AccessibilityHierarchyCheck;
import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertTrue;
public class LayoutValidatorTests extends RenderTestBase {
+ private static final float SCALE_X_FOR_NEXUS_5 = 1.0f;
+ private static final float SCALE_Y_FOR_NEXUS_5 = 1.0f;
+
+ @Test
+ public void testEnsureDebuggingOff() {
+ assertFalse(LayoutValidator.shouldSaveCroppedImages());
+ }
+
+ @Test
+ public void testEnsureObtainCharacterLocation() {
+ assertFalse(LayoutValidator.obtainCharacterLocations());
+ }
+
@Test
public void testRenderAndVerify() throws Exception {
LayoutPullParser parser = createParserFromPath("a11y_test1.xml");
@@ -61,30 +79,48 @@ public class LayoutValidatorTests extends RenderTestBase {
@Test
public void testValidation() throws Exception {
render(sBridge, generateParams(), -1, session -> {
- ValidatorResult result = LayoutValidator
- .validate(((View) session.getRootViews().get(0).getViewObject()), null);
- assertEquals(3, result.getIssues().size());
+ ValidatorResult result = LayoutValidator.validate(
+ ((View) session.getRootViews().get(0).getViewObject()),
+ null,
+ SCALE_X_FOR_NEXUS_5,
+ SCALE_Y_FOR_NEXUS_5);
+ assertEquals(30, result.getIssues().size());
+ ArrayList<Issue> errorIssues = new ArrayList<>();
for (Issue issue : result.getIssues()) {
assertEquals(Type.ACCESSIBILITY, issue.mType);
- assertEquals(Level.ERROR, issue.mLevel);
+ if (issue.mLevel == Level.ERROR) {
+ errorIssues.add(issue);
+ }
}
- Issue first = result.getIssues().get(0);
+ Issue first = errorIssues.get(0);
assertEquals("This item may not have a label readable by screen readers.",
first.mMsg);
assertEquals("https://support.google.com/accessibility/android/answer/7158690",
first.mHelpfulUrl);
assertEquals("SpeakableTextPresentCheck", first.mSourceClass);
+ assertTrue(first.mFix instanceof SetViewAttributeFix);
+ assertEquals("Set this item's android:contentDescription to a meaningful" +
+ " non-empty string or resource reference.",
+ first.mFix.getDescription());
- Issue second = result.getIssues().get(1);
+ Issue second = errorIssues.get(1);
+ CompoundFix compoundFix = (CompoundFix) second.mFix;
assertEquals("This item's size is 10dp x 10dp. Consider making this touch target " +
"48dp wide and 48dp high or larger.",
second.mMsg);
assertEquals("https://support.google.com/accessibility/android/answer/7101858",
second.mHelpfulUrl);
assertEquals("TouchTargetSizeCheck", second.mSourceClass);
-
- Issue third = result.getIssues().get(2);
+ assertTrue(compoundFix.mFixes.size() == 2);
+ assertEquals(
+ "Set this item's android:layout_width to 48dp.",
+ compoundFix.mFixes.get(0).getDescription());
+ assertEquals(
+ "Set this item's android:layout_height to 48dp.",
+ compoundFix.mFixes.get(1).getDescription());
+
+ Issue third = errorIssues.get(2);
assertEquals("The item's text contrast ratio is 1.00. This ratio is based on a text color " +
"of #000000 and background color of #000000. Consider increasing this item's" +
" text contrast ratio to 4.50 or greater.",
@@ -92,6 +128,9 @@ public class LayoutValidatorTests extends RenderTestBase {
assertEquals("https://support.google.com/accessibility/android/answer/7158390",
third.mHelpfulUrl);
assertEquals("TextContrastCheck", third.mSourceClass);
+ assertTrue(third.mFix instanceof SetViewAttributeFix);
+ assertEquals("Set this item's android:textColor to #757575.",
+ third.mFix.getDescription());
});
}
@@ -105,8 +144,14 @@ public class LayoutValidatorTests extends RenderTestBase {
render(sBridge, generateParams(), -1, session -> {
ValidatorResult result = LayoutValidator.validate(
- ((View) session.getRootViews().get(0).getViewObject()), null);
- assertTrue(result.getIssues().isEmpty());
+ ((View) session.getRootViews().get(0).getViewObject()),
+ null,
+ SCALE_X_FOR_NEXUS_5,
+ SCALE_Y_FOR_NEXUS_5);
+
+ assertEquals(1, result.getIssues().size());
+ assertEquals("Hierarchy is not built yet.",
+ result.getIssues().get(0).mMsg);
});
} finally {
LayoutValidator.updatePolicy(LayoutValidator.DEFAULT_POLICY);
@@ -123,7 +168,10 @@ public class LayoutValidatorTests extends RenderTestBase {
render(sBridge, generateParams(), -1, session -> {
ValidatorResult result = LayoutValidator.validate(
- ((View) session.getRootViews().get(0).getViewObject()), null);
+ ((View) session.getRootViews().get(0).getViewObject()),
+ null,
+ SCALE_X_FOR_NEXUS_5,
+ SCALE_Y_FOR_NEXUS_5);
assertEquals(27, result.getIssues().size());
result.getIssues().forEach(issue ->assertEquals(Level.VERBOSE, issue.mLevel));
});
@@ -150,7 +198,10 @@ public class LayoutValidatorTests extends RenderTestBase {
render(sBridge, generateParams(), -1, session -> {
ValidatorResult result = LayoutValidator.validate(
- ((View) session.getRootViews().get(0).getViewObject()), null);
+ ((View) session.getRootViews().get(0).getViewObject()),
+ null,
+ SCALE_X_FOR_NEXUS_5,
+ SCALE_Y_FOR_NEXUS_5);
assertEquals(1, result.getIssues().size());
Issue textCheck = result.getIssues().get(0);
assertEquals("The item's text contrast ratio is 1.00. This ratio is based on a text color " +
diff --git a/bridge/tests/src/com/android/tools/idea/validator/ValidatorResultTests.java b/bridge/tests/src/com/android/tools/idea/validator/ValidatorResultTests.java
new file mode 100644
index 0000000000..618200c921
--- /dev/null
+++ b/bridge/tests/src/com/android/tools/idea/validator/ValidatorResultTests.java
@@ -0,0 +1,104 @@
+/*
+ * 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.tools.idea.validator;
+
+import com.android.tools.idea.validator.ValidatorData.Issue.IssueBuilder;
+import com.android.tools.idea.validator.ValidatorData.Level;
+import com.android.tools.idea.validator.ValidatorData.Type;
+import com.android.tools.idea.validator.ValidatorResult.Builder;
+import com.android.tools.idea.validator.ValidatorResult.Metric;
+
+import org.junit.Ignore;
+import org.junit.Test;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertTrue;
+import static org.junit.Assert.fail;
+
+public class ValidatorResultTests {
+ private static final long EPSILON = 10L;
+
+ @Test
+ public void testBuildingEmptyResult() {
+ ValidatorResult result = new ValidatorResult.Builder().build();
+ assertNotNull(result);
+ assertTrue(result.getIssues().isEmpty());
+ assertTrue(result.getSrcMap().isEmpty());
+ assertNotNull(result.getMetric());
+ assertEquals("Result containing 0 issues:\n", result.toString());
+ }
+
+ @Test
+ public void testBuildingResult() {
+ ValidatorResult.Builder builder = new ValidatorResult.Builder();
+ for (int i = 0; i < 3; i++) {
+ builder.mIssues.add(createIssueBuilder().setMsg("issue " + i).build());
+ }
+ assertEquals(
+ "Result containing 3 issues:\n" +
+ " - [ERROR] issue 0\n" +
+ " - [ERROR] issue 1\n" +
+ " - [ERROR] issue 2\n",
+ builder.build().toString());
+ }
+
+ private static IssueBuilder createIssueBuilder() {
+ return new IssueBuilder()
+ .setCategory("category")
+ .setType(Type.ACCESSIBILITY)
+ .setMsg("msg")
+ .setLevel(Level.ERROR)
+ .setSourceClass("Source class");
+ }
+
+ @Ignore("b/172205439")
+ @Test
+ public void testMetricRecordHierarchyCreationTime() {
+ long expectedElapsed = 100;
+ Metric metric = new Builder().mMetric;
+
+ metric.startHierarchyCreationTimer();
+ try {
+ Thread.sleep(expectedElapsed);
+ } catch (Exception e) {
+ String msg = "Unexpected exception. ";
+ if (e.getMessage() == null) {
+ msg += e.getMessage();
+ }
+ fail(msg);
+ }
+ metric.recordHierarchyCreationTime();
+
+ long diff = Math.abs(expectedElapsed - metric.mHierarchyCreationMs);
+ System.out.println(diff);
+ assertTrue(diff < EPSILON);
+ }
+
+ @Test
+ public void testMetricToString() {
+ Metric metric = new Builder().mMetric;
+
+ metric.mErrorMessage = "TestError";
+ metric.mImageMemoryBytes = Long.MAX_VALUE;
+
+ assertEquals(
+ "Validation result metric: { hierarchy creation=0ms, image memory=9223372036gb }",
+ metric.toString());
+ }
+
+}
diff --git a/bridge/src/com/android/layoutlib/bridge/util/ReflectionUtils.java b/common/src/com/android/layoutlib/common/util/ReflectionUtils.java
index 00348ea336..7c2bff0ca9 100644
--- a/bridge/src/com/android/layoutlib/bridge/util/ReflectionUtils.java
+++ b/common/src/com/android/layoutlib/common/util/ReflectionUtils.java
@@ -14,11 +14,12 @@
* limitations under the License.
*/
-package com.android.layoutlib.bridge.util;
+package com.android.layoutlib.common.util;
-import android.annotation.NonNull;
-import android.annotation.Nullable;
+import com.android.tools.layoutlib.annotations.NonNull;
+import com.android.tools.layoutlib.annotations.Nullable;
+import java.lang.reflect.Field;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
@@ -30,6 +31,16 @@ import java.lang.reflect.Proxy;
public class ReflectionUtils {
@NonNull
+ public static Method getMethod(@NonNull String className, @NonNull String name,
+ @Nullable Class<?>... params) throws ReflectionException {
+ try {
+ return getMethod(Class.forName(className), name, params);
+ } catch (ClassNotFoundException e) {
+ throw new ReflectionException(e);
+ }
+ }
+
+ @NonNull
public static Method getMethod(@NonNull Class<?> clazz, @NonNull String name,
@Nullable Class<?>... params) throws ReflectionException {
try {
@@ -48,6 +59,19 @@ public class ReflectionUtils {
return method;
}
+ @NonNull
+ public static Object getFieldValue(@NonNull Class<?> clazz, Object object, @NonNull String name) throws ReflectionException {
+ try {
+ Field field = clazz.getDeclaredField(name);
+ field.setAccessible(true);
+ return field.get(object);
+ } catch (NoSuchFieldException e) {
+ throw new ReflectionException(e);
+ } catch (IllegalAccessException e) {
+ throw new ReflectionException(e);
+ }
+ }
+
@Nullable
public static Object invoke(@NonNull Method method, @Nullable Object object,
@Nullable Object... args) throws ReflectionException {
@@ -60,6 +84,17 @@ public class ReflectionUtils {
throw new ReflectionException(ex);
}
+ @Nullable
+ public static Object invokeStatic(String className, String methodName, @Nullable Object... args)
+ throws ReflectionException {
+ try {
+ Method m = getMethod(Class.forName(className), methodName);
+ return invoke(m, null, args);
+ } catch (ClassNotFoundException e) {
+ throw new ReflectionException(e);
+ }
+ }
+
/**
* Check if the object is an instance of a class named {@code className}. This doesn't work
* for interfaces.
@@ -86,16 +121,16 @@ public class ReflectionUtils {
/**
* Check if the object is an instance of any of the class named in {@code className} and
- * returns the name of the parent class that matched. This doesn't work for interfaces.
+ * returns the parent class that matched. This doesn't work for interfaces.
*/
@Nullable
- public static String getParentClass(Object object, String[] classNames) {
- Class superClass = object.getClass();
+ public static Class<?> getParentClass(Object object, String[] classNames) {
+ Class<?> superClass = object.getClass();
while (superClass != null) {
String name = superClass.getName();
for (String className : classNames) {
if (name.equals(className)) {
- return className;
+ return superClass;
}
}
superClass = superClass.getSuperclass();
@@ -103,6 +138,16 @@ public class ReflectionUtils {
return null;
}
+ /**
+ * Check if the object is an instance of any of the class named in {@code className} and
+ * returns the name of the parent class that matched. This doesn't work for interfaces.
+ */
+ @Nullable
+ public static String getParentClassName(Object object, String[] classNames) {
+ Class<?> superClass = getParentClass(object, classNames);
+ return superClass != null ? superClass.getName() : null;
+ }
+
@NonNull
public static Throwable getCause(@NonNull Throwable throwable) {
Throwable cause = throwable.getCause();
diff --git a/common/src/com/android/tools/layoutlib/annotations/NonNull.java b/common/src/com/android/tools/layoutlib/annotations/NonNull.java
new file mode 100644
index 0000000000..b48171b38e
--- /dev/null
+++ b/common/src/com/android/tools/layoutlib/annotations/NonNull.java
@@ -0,0 +1,34 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.tools.layoutlib.annotations;
+
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+
+/**
+ * Denotes a parameter or field can not be null.
+ * <p/>
+ * When decorating a method call parameter, this denotes the parameter can
+ * not be null.
+ * <p/>
+ * When decorating a method, this denotes the method can not return null.
+ * <p/>
+ * This is a marker annotation and it has no specific attributes.
+ */
+@Retention(RetentionPolicy.SOURCE)
+public @interface NonNull {
+}
diff --git a/common/src/com/android/tools/layoutlib/create/NativeConfig.java b/common/src/com/android/tools/layoutlib/create/NativeConfig.java
new file mode 100644
index 0000000000..9c05df2ab2
--- /dev/null
+++ b/common/src/com/android/tools/layoutlib/create/NativeConfig.java
@@ -0,0 +1,217 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.tools.layoutlib.create;
+
+/**
+ * Stores data needed for native JNI registration, and possibly the framework bytecode
+ * instrumentation.
+ */
+public class NativeConfig {
+
+ private NativeConfig() {}
+
+ public final static String[] DEFERRED_STATIC_INITIALIZER_CLASSES = new String [] {
+ "android.graphics.ColorSpace",
+ "android.graphics.FontFamily",
+ "android.graphics.Matrix",
+ "android.graphics.Path",
+ // Order is important! Fonts and FontFamily have to be initialized before Typeface
+ "android.graphics.fonts.Font",
+ "android.graphics.fonts.FontFamily$Builder",
+ "android.graphics.Typeface",
+ "android.graphics.text.PositionedGlyphs",
+ "android.graphics.text.LineBreaker",
+ };
+
+ public static final String[] DELEGATE_METHODS = new String[] {
+ "android.app.Fragment#instantiate", //(Landroid/content/Context;Ljava/lang/String;Landroid/os/Bundle;)Landroid/app/Fragment;",
+ "android.content.res.AssetManager#createSystemAssetsInZygoteLocked",
+ "android.content.res.AssetManager#getAssignedPackageIdentifiers",
+ "android.content.res.AssetManager#nativeCreate",
+ "android.content.res.AssetManager#nativeDestroy",
+ "android.content.res.AssetManager#nativeThemeCreate",
+ "android.content.res.AssetManager#nativeGetThemeFreeFunction",
+ "android.content.res.Resources#getAnimation",
+ "android.content.res.Resources#getAttributeSetSourceResId",
+ "android.content.res.Resources#getBoolean",
+ "android.content.res.Resources#getColor",
+ "android.content.res.Resources#getColorStateList",
+ "android.content.res.Resources#getDimension",
+ "android.content.res.Resources#getDimensionPixelOffset",
+ "android.content.res.Resources#getDimensionPixelSize",
+ "android.content.res.Resources#getDrawable",
+ "android.content.res.Resources#getFloat",
+ "android.content.res.Resources#getFont",
+ "android.content.res.Resources#getIdentifier",
+ "android.content.res.Resources#getIntArray",
+ "android.content.res.Resources#getInteger",
+ "android.content.res.Resources#getLayout",
+ "android.content.res.Resources#getQuantityString",
+ "android.content.res.Resources#getQuantityText",
+ "android.content.res.Resources#getResourceEntryName",
+ "android.content.res.Resources#getResourceName",
+ "android.content.res.Resources#getResourcePackageName",
+ "android.content.res.Resources#getResourceTypeName",
+ "android.content.res.Resources#getString",
+ "android.content.res.Resources#getStringArray",
+ "android.content.res.Resources#getText",
+ "android.content.res.Resources#getTextArray",
+ "android.content.res.Resources#getValue",
+ "android.content.res.Resources#getValueForDensity",
+ "android.content.res.Resources#getXml",
+ "android.content.res.Resources#loadXmlResourceParser",
+ "android.content.res.Resources#obtainAttributes",
+ "android.content.res.Resources#openRawResource",
+ "android.content.res.Resources#openRawResourceFd",
+ "android.content.res.Resources#obtainTypedArray",
+ "android.content.res.Resources$Theme#obtainStyledAttributes",
+ "android.content.res.Resources$Theme#resolveAttribute",
+ "android.content.res.Resources$Theme#resolveAttributes",
+ "android.content.res.TypedArray#getValueAt",
+ "android.content.res.TypedArray#obtain",
+ "android.graphics.Canvas#getClipBounds",
+ "android.graphics.ImageDecoder#decodeBitmapImpl",
+ "android.graphics.Typeface#create",
+ "android.graphics.Typeface$Builder#createAssetUid",
+ "android.graphics.drawable.AdaptiveIconDrawable#<init>",
+ "android.graphics.drawable.AnimatedVectorDrawable$VectorDrawableAnimatorUI#onDraw",
+ "android.graphics.drawable.AnimatedVectorDrawable#draw",
+ "android.graphics.drawable.DrawableInflater#inflateFromClass",
+ "android.graphics.drawable.NinePatchDrawable#getOpacity",
+ "android.graphics.fonts.Font$Builder#createBuffer",
+ "android.graphics.fonts.SystemFonts#getSystemFontConfigInternal",
+ "android.graphics.fonts.SystemFonts#mmap",
+ "android.os.Binder#getNativeBBinderHolder",
+ "android.os.Binder#getNativeFinalizer",
+ "android.os.Handler#sendMessageAtFrontOfQueue",
+ "android.os.Handler#sendMessageAtTime",
+ "android.os.HandlerThread#run",
+ "android.preference.Preference#getView",
+ "android.provider.DeviceConfig#getBoolean",
+ "android.provider.DeviceConfig#getFloat",
+ "android.provider.DeviceConfig#getInt",
+ "android.provider.DeviceConfig#getLong",
+ "android.provider.DeviceConfig#getString",
+ "android.text.format.DateFormat#is24HourFormat",
+ "android.util.Xml#newPullParser",
+ "android.view.Choreographer#getFrameTimeNanos",
+ "android.view.Choreographer#getRefreshRate",
+ "android.view.Choreographer#postCallbackDelayedInternal",
+ "android.view.Choreographer#removeCallbacksInternal",
+ "android.view.Display#getWindowManager",
+ "android.view.Display#updateDisplayInfoLocked",
+ "android.view.HandlerActionQueue#postDelayed",
+ "android.view.LayoutInflater#initPrecompiledViews",
+ "android.view.LayoutInflater#parseInclude",
+ "android.view.LayoutInflater#rInflate",
+ "android.view.MenuInflater#registerMenu",
+ "android.view.PointerIcon#loadResource",
+ "android.view.PointerIcon#registerDisplayListener",
+ "android.view.SurfaceControl#nativeCreateTransaction",
+ "android.view.SurfaceControl#nativeGetNativeTransactionFinalizer",
+ "android.view.TextureView#getTextureLayer",
+ "android.view.View#draw",
+ "android.view.View#dispatchDetachedFromWindow",
+ "android.view.View#getWindowToken",
+ "android.view.View#isInEditMode",
+ "android.view.View#layout",
+ "android.view.View#measure",
+ "android.view.ViewRootImpl#isInTouchMode",
+ "android.view.WindowManagerGlobal#getWindowManagerService",
+ "android.view.inputmethod.InputMethodManager#isInEditMode",
+ "android.widget.RemoteViews#getApplicationInfo",
+ "com.android.internal.util.XmlUtils#convertValueToInt",
+ "com.android.internal.view.menu.MenuBuilder#createNewMenuItem",
+ "dalvik.system.VMRuntime#getNotifyNativeInterval",
+ "dalvik.system.VMRuntime#newUnpaddedArray",
+ "libcore.io.MemoryMappedFile#bigEndianIterator",
+ "libcore.io.MemoryMappedFile#close",
+ "libcore.io.MemoryMappedFile#mmapRO",
+ "libcore.util.NativeAllocationRegistry#applyFreeFunction",
+ };
+
+ public final static String[] DELEGATE_CLASS_NATIVES = new String[] {
+ "android.os.SystemClock",
+ "android.view.Display",
+ "libcore.icu.ICU",
+ };
+
+ /**
+ * The list of core classes to register with JNI
+ */
+ public final static String[] CORE_CLASS_NATIVES = new String[] {
+ "android.animation.PropertyValuesHolder",
+ "android.content.res.StringBlock",
+ "android.content.res.XmlBlock",
+ "android.media.ImageReader",
+ "android.media.PublicFormatUtils",
+ "android.os.SystemProperties",
+ "android.os.Trace",
+ "android.text.AndroidCharacter",
+ "android.util.Log",
+ "android.view.MotionEvent",
+ "android.view.Surface",
+ "com.android.internal.util.VirtualRefBasePtr",
+ "libcore.util.NativeAllocationRegistry_Delegate",
+ };
+
+ /**
+ * The list of graphics classes to register with JNI
+ */
+ public final static String[] GRAPHICS_CLASS_NATIVES = new String[] {
+ "android.graphics.Bitmap",
+ "android.graphics.BitmapFactory",
+ "android.graphics.ByteBufferStreamAdaptor",
+ "android.graphics.Camera",
+ "android.graphics.Canvas",
+ "android.graphics.CanvasProperty",
+ "android.graphics.ColorFilter",
+ "android.graphics.ColorSpace",
+ "android.graphics.CreateJavaOutputStreamAdaptor",
+ "android.graphics.DrawFilter",
+ "android.graphics.FontFamily",
+ "android.graphics.Graphics",
+ "android.graphics.HardwareRenderer",
+ "android.graphics.ImageDecoder",
+ "android.graphics.Interpolator",
+ "android.graphics.MaskFilter",
+ "android.graphics.Matrix",
+ "android.graphics.NinePatch",
+ "android.graphics.Paint",
+ "android.graphics.Path",
+ "android.graphics.PathEffect",
+ "android.graphics.PathMeasure",
+ "android.graphics.Picture",
+ "android.graphics.RecordingCanvas",
+ "android.graphics.Region",
+ "android.graphics.RenderEffect",
+ "android.graphics.RenderNode",
+ "android.graphics.Shader",
+ "android.graphics.Typeface",
+ "android.graphics.YuvImage",
+ "android.graphics.animation.NativeInterpolatorFactory",
+ "android.graphics.animation.RenderNodeAnimator",
+ "android.graphics.drawable.AnimatedVectorDrawable",
+ "android.graphics.drawable.VectorDrawable",
+ "android.graphics.fonts.Font",
+ "android.graphics.fonts.FontFamily",
+ "android.graphics.text.LineBreaker",
+ "android.graphics.text.MeasuredText",
+ "android.graphics.text.TextRunShaper",
+ "android.util.PathParser",
+ };
+}
diff --git a/create/Android.bp b/create/Android.bp
index 0fc219d7c0..96622e958e 100644
--- a/create/Android.bp
+++ b/create/Android.bp
@@ -15,13 +15,7 @@
//
package {
- // See: http://go/android-license-faq
- // A large-scale-change added 'default_applicable_licenses' to import
- // all of the 'license_kinds' from "frameworks_layoutlib_license"
- // to get the below license kinds:
- // SPDX-license-identifier-Apache-2.0
- // SPDX-license-identifier-EPL
- default_applicable_licenses: ["frameworks_layoutlib_license"],
+ default_applicable_licenses: ["Android-Apache-2.0"],
}
java_binary_host {
@@ -31,9 +25,11 @@ java_binary_host {
main_class: "com.android.tools.layoutlib.create.Main",
static_libs: [
- "asm-6.0",
- "asm-commons-6.0",
+ "asm-9.2",
+ "asm-commons-9.2",
"guava",
"layoutlib-common",
+ "layoutlib_create-classpath",
+ "atf-prebuilt-371374941",
],
}
diff --git a/create/create.iml b/create/create.iml
index b2a53d2047..00abf5dc6e 100644
--- a/create/create.iml
+++ b/create/create.iml
@@ -1,6 +1,6 @@
<?xml version="1.0" encoding="UTF-8"?>
<module type="JAVA_MODULE" version="4">
- <component name="NewModuleRootManager" LANGUAGE_LEVEL="JDK_1_8" inherit-compiler-output="true">
+ <component name="NewModuleRootManager" inherit-compiler-output="true">
<exclude-output />
<content url="file://$MODULE_DIR$">
<sourceFolder url="file://$MODULE_DIR$/src" isTestSource="false" />
@@ -11,24 +11,24 @@
<orderEntry type="inheritedJdk" />
<orderEntry type="sourceFolder" forTests="false" />
<orderEntry type="module-library">
- <library name="asm-6.0">
+ <library name="asm-7.0">
<CLASSES>
- <root url="jar://$MODULE_DIR$/../../../out/soong/.intermediates/prebuilts/misc/common/asm/asm-6.0/linux_glibc_common/combined/asm-6.0.jar!/" />
+ <root url="jar://$MODULE_DIR$/../../../out/soong/.intermediates/prebuilts/misc/common/asm/asm-7.0/linux_glibc_common/combined/asm-7.0.jar!/" />
</CLASSES>
<JAVADOC />
<SOURCES>
- <root url="jar://$MODULE_DIR$/../../../prebuilts/misc/common/asm/src-6.0.zip!/" />
+ <root url="jar://$MODULE_DIR$/../../../prebuilts/misc/common/asm/src-7.0.zip!/" />
</SOURCES>
</library>
</orderEntry>
<orderEntry type="module-library">
- <library name="asm-commons-6.0">
+ <library name="asm-commons-7.0">
<CLASSES>
- <root url="jar://$MODULE_DIR$/../../../out/soong/.intermediates/prebuilts/misc/common/asm/asm-commons-6.0/linux_glibc_common/combined/asm-commons-6.0.jar!/" />
+ <root url="jar://$MODULE_DIR$/../../../out/soong/.intermediates/prebuilts/misc/common/asm/asm-commons-7.0/linux_glibc_common/combined/asm-commons-7.0.jar!/" />
</CLASSES>
<JAVADOC />
<SOURCES>
- <root url="jar://$MODULE_DIR$/../../../prebuilts/misc/common/asm/src-6.0.zip!/" />
+ <root url="jar://$MODULE_DIR$/../../../prebuilts/misc/common/asm/src-7.0.zip!/" />
</SOURCES>
</library>
</orderEntry>
@@ -36,5 +36,59 @@
<orderEntry type="module" module-name="common" />
<orderEntry type="library" name="guava" level="project" />
<orderEntry type="library" scope="TEST" name="hamcrest" level="project" />
+ <orderEntry type="module-library" scope="RUNTIME">
+ <library>
+ <CLASSES>
+ <root url="jar://$MODULE_DIR$/../../../out/soong/.intermediates/external/conscrypt/conscrypt/android_common/javac/conscrypt.jar!/" />
+ </CLASSES>
+ <JAVADOC />
+ <SOURCES />
+ </library>
+ </orderEntry>
+ <orderEntry type="module-library" scope="RUNTIME">
+ <library>
+ <CLASSES>
+ <root url="jar://$MODULE_DIR$/../../../out/soong/.intermediates/external/icu/android_icu4j/core-icu4j-for-host/android_common/withres/core-icu4j-for-host.jar!/" />
+ </CLASSES>
+ <JAVADOC />
+ <SOURCES />
+ </library>
+ </orderEntry>
+ <orderEntry type="module-library" scope="RUNTIME">
+ <library>
+ <CLASSES>
+ <root url="jar://$MODULE_DIR$/../../../out/soong/.intermediates/libcore/core-libart/android_common/javac/core-libart.jar!/" />
+ </CLASSES>
+ <JAVADOC />
+ <SOURCES />
+ </library>
+ </orderEntry>
+ <orderEntry type="module-library" scope="RUNTIME">
+ <library>
+ <CLASSES>
+ <root url="jar://$MODULE_DIR$/../../../out/soong/.intermediates/frameworks/base/framework-all/android_common/combined/framework-all.jar!/" />
+ </CLASSES>
+ <JAVADOC />
+ <SOURCES />
+ </library>
+ </orderEntry>
+ <orderEntry type="module-library" scope="RUNTIME">
+ <library>
+ <CLASSES>
+ <root url="jar://$MODULE_DIR$/../../../out/soong/.intermediates/frameworks/base/ext/android_common/withres/ext.jar!/" />
+ </CLASSES>
+ <JAVADOC />
+ <SOURCES />
+ </library>
+ </orderEntry>
+ <orderEntry type="module-library" scope="RUNTIME">
+ <library>
+ <CLASSES>
+ <root url="jar://$MODULE_DIR$/../../../out/soong/.intermediates/prebuilts/misc/common/atf/atf-prebuilt-jars-371374941/linux_glibc_common/combined/atf-prebuilt-jars-371374941.jar!/" />
+ </CLASSES>
+ <JAVADOC />
+ <SOURCES />
+ </library>
+ </orderEntry>
</component>
-</module> \ No newline at end of file
+</module>
diff --git a/create/src/com/android/tools/layoutlib/create/AsmAnalyzer.java b/create/src/com/android/tools/layoutlib/create/AsmAnalyzer.java
index b77708bde4..e5d1088b31 100644
--- a/create/src/com/android/tools/layoutlib/create/AsmAnalyzer.java
+++ b/create/src/com/android/tools/layoutlib/create/AsmAnalyzer.java
@@ -18,6 +18,7 @@ package com.android.tools.layoutlib.create;
import com.android.tools.layoutlib.annotations.NotNull;
import com.android.tools.layoutlib.annotations.Nullable;
+import com.android.tools.layoutlib.create.ICreateInfo.MethodReplacer;
import org.objectweb.asm.AnnotationVisitor;
import org.objectweb.asm.Attribute;
@@ -43,10 +44,8 @@ import java.util.Map;
import java.util.Map.Entry;
import java.util.Set;
import java.util.TreeMap;
-import java.util.concurrent.CompletableFuture;
import java.util.concurrent.ForkJoinPool;
import java.util.concurrent.ForkJoinTask;
-import java.util.concurrent.Future;
import java.util.function.Consumer;
import java.util.regex.Pattern;
import java.util.zip.ZipEntry;
@@ -105,27 +104,28 @@ public class AsmAnalyzer {
private final String[] mIncludeFileGlobs;
/** Internal names of classes that contain method calls that need to be rewritten. */
private final Set<String> mReplaceMethodCallClasses = new HashSet<>();
+ /** Internal names of method calls that need to be rewritten. */
+ private final MethodReplacer[] mMethodReplacers;
/**
* Creates a new analyzer.
- *
* @param log The log output.
* @param osJarPath The input source JARs to parse.
* @param deriveFrom Keep all classes that derive from these one (these included).
* @param includeGlobs Glob patterns of classes to keep, e.g. "com.foo.*"
- * ("*" does not matches dots whilst "**" does, "." and "$" are interpreted as-is)
+* ("*" does not matches dots whilst "**" does, "." and "$" are interpreted as-is)
* @param includeFileGlobs Glob patterns of files which are kept as is. This is only for files
- * not ending in .class.
+ * @param methodReplacers names of method calls that need to be rewritten
*/
- public AsmAnalyzer(Log log, List<String> osJarPath,
- String[] deriveFrom, String[] includeGlobs, String[] excludedGlobs,
- String[] includeFileGlobs) {
+ public AsmAnalyzer(Log log, List<String> osJarPath, String[] deriveFrom, String[] includeGlobs,
+ String[] excludedGlobs, String[] includeFileGlobs, MethodReplacer[] methodReplacers) {
mLog = log;
mOsSourceJar = osJarPath != null ? osJarPath : new ArrayList<>();
mDeriveFrom = deriveFrom != null ? deriveFrom : new String[0];
mIncludeGlobs = includeGlobs != null ? includeGlobs : new String[0];
mExcludedGlobs = excludedGlobs != null ? excludedGlobs : new String[0];
mIncludeFileGlobs = includeFileGlobs != null ? includeFileGlobs : new String[0];
+ mMethodReplacers = methodReplacers;
}
/**
@@ -488,7 +488,7 @@ public class AsmAnalyzer {
try {
// exclude classes that are part of the default JRE (the one executing this program)
if (className.startsWith("java.") || className.startsWith("sun.") ||
- getClass().getClassLoader().loadClass(className) != null) {
+ getClass().getClassLoader().getParent().loadClass(className) != null) {
return;
}
} catch (ClassNotFoundException e) {
@@ -773,6 +773,24 @@ public class AsmAnalyzer {
// pass
}
+ /**
+ * If a method some.package.Class.Method(args) is called from some.other.Class,
+ * @param owner some/package/Class
+ * @param name Method
+ * @param desc (args)returnType
+ * @param sourceClass some/other/Class
+ * @return if the method invocation needs to be replaced by some other class.
+ */
+ private boolean isReplacementNeeded(String owner, String name, String desc,
+ String sourceClass) {
+ for (MethodReplacer replacer : mMethodReplacers) {
+ if (replacer.isNeeded(owner, name, desc, sourceClass)) {
+ return true;
+ }
+ }
+ return false;
+ }
+
// instruction that invokes a method
@Override
public void visitMethodInsn(int opcode, String owner, String name, String desc,
@@ -785,7 +803,7 @@ public class AsmAnalyzer {
// Check if method needs to replaced by a call to a different method.
- if (ReplaceMethodCallsAdapter.isReplacementNeeded(owner, name, desc, mOwnerClass)) {
+ if (isReplacementNeeded(owner, name, desc, mOwnerClass)) {
mReplaceMethodCallClasses.add(mOwnerClass);
}
}
diff --git a/create/src/com/android/tools/layoutlib/create/AsmGenerator.java b/create/src/com/android/tools/layoutlib/create/AsmGenerator.java
index f4682887cd..98055e3a90 100644
--- a/create/src/com/android/tools/layoutlib/create/AsmGenerator.java
+++ b/create/src/com/android/tools/layoutlib/create/AsmGenerator.java
@@ -18,6 +18,7 @@ package com.android.tools.layoutlib.create;
import com.android.tools.layoutlib.annotations.NotNull;
import com.android.tools.layoutlib.create.AsmAnalyzer.Result;
+import com.android.tools.layoutlib.create.ICreateInfo.MethodReplacer;
import org.objectweb.asm.ClassReader;
import org.objectweb.asm.ClassVisitor;
@@ -52,6 +53,7 @@ public class AsmGenerator {
private Map<String, ClassReader> mKeep;
/** All dependencies that must be completely stubbed. */
private Map<String, ClassReader> mDeps;
+ private Map<String, ClassWriter> mDelegates = new HashMap<>();
/** All files that are to be copied as-is. */
private Map<String, InputStream> mCopyFiles;
/** All classes where certain method calls need to be rewritten. */
@@ -77,8 +79,21 @@ public class AsmGenerator {
private final Map<String, ICreateInfo.InjectMethodRunnable> mInjectedMethodsMap;
/** A map { FQCN => set { field names } } which should be promoted to public visibility */
private final Map<String, Set<String>> mPromotedFields;
+ /** A map { FQCN => set { method names } } which should be promoted to public visibility */
+ private final Map<String, Set<String>> mPromotedMethods;
/** A list of classes to be promoted to public visibility */
private final Set<String> mPromotedClasses;
+ /** A set of classes for which NOT to delegate any native method */
+ private final Set<String> mKeepNativeClasses;
+
+ private final Set<String> mDelegateAllNative;
+ /** A set of classes for which to rename static initializers */
+ private Set<String> mRenameStaticInitializerClasses;
+
+ /** A Set of methods that should be intercepted and replaced **/
+ private final Set<MethodReplacer> mMethodReplacers;
+ private boolean mKeepAllNativeClasses;
+
/**
* Creates a new generator that can generate the output JAR with the stubbed classes.
@@ -183,10 +198,26 @@ public class AsmGenerator {
mPromotedFields = new HashMap<>();
addToMap(createInfo.getPromotedFields(), mPromotedFields);
+ mPromotedMethods = new HashMap<>();
+ addToMap(createInfo.getPromotedMethods(), mPromotedMethods);
+
mInjectedMethodsMap = createInfo.getInjectedMethodsMap();
mPromotedClasses =
Arrays.stream(createInfo.getPromotedClasses()).collect(Collectors.toSet());
+
+ mKeepAllNativeClasses = createInfo.shouldKeepAllNativeClasses();
+
+ mKeepNativeClasses =
+ Arrays.stream(createInfo.getKeepClassNatives()).collect(Collectors.toSet());
+
+ mDelegateAllNative =
+ Arrays.stream(createInfo.getDelegateClassNativesToNatives()).collect(Collectors.toSet());
+
+ mMethodReplacers = Arrays.stream(createInfo.getMethodReplacers()).collect(Collectors.toSet());
+
+ mRenameStaticInitializerClasses =
+ Arrays.stream(createInfo.getDeferredStaticInitializerClasses()).collect(Collectors.toSet());
}
/**
@@ -259,6 +290,13 @@ public class AsmGenerator {
all.put(name, b);
}
+ for (Entry<String, ClassWriter> entry : mDelegates.entrySet()) {
+ ClassWriter value = entry.getValue();
+ value.visitEnd();
+ String name = classNameToEntryPath(entry.getKey());
+ all.put(name, value.toByteArray());
+ }
+
for (Entry<String, InputStream> entry : mCopyFiles.entrySet()) {
try {
byte[] b = inputStreamToByteArray(entry.getValue());
@@ -329,12 +367,12 @@ public class AsmGenerator {
// Rewrite the new class from scratch, without reusing the constant pool from the
// original class reader.
- ClassWriter cw = new ClassWriter(ClassWriter.COMPUTE_MAXS);
+ ClassWriter cw = new ClassWriter(ClassWriter.COMPUTE_FRAMES);
ClassVisitor cv = cw;
if (mReplaceMethodCallsClasses.contains(className)) {
- cv = new ReplaceMethodCallsAdapter(cv, className);
+ cv = new ReplaceMethodCallsAdapter(mMethodReplacers, cv, className);
}
cv = new RefactorClassAdapter(cv, mRefactorClasses);
@@ -346,11 +384,21 @@ public class AsmGenerator {
if (mInjectedMethodsMap.keySet().contains(binaryNewName)) {
cv = new InjectMethodsAdapter(cv, mInjectedMethodsMap.get(binaryNewName));
}
- cv = StubClassAdapter.builder(mLog, cv)
- .withDeleteReturns(mDeleteReturns.get(className))
- .withNewClassName(newName)
- .useOnlyStubNative(stubNativesOnly)
- .build();
+
+ if (mDelegateAllNative.contains(binaryNewName)) {
+ Set<String> delegateMethods = mDelegateMethods.remove(className);
+ if (delegateMethods != null && !delegateMethods.isEmpty()) {
+ cv = new DelegateClassAdapter(mLog, cv, className, delegateMethods);
+ }
+ cv = new DelegateToNativeAdapter(mLog, cv, className, mDelegates, delegateMethods);
+ }
+ else if (!mKeepAllNativeClasses && !mKeepNativeClasses.contains(binaryNewName)) {
+ cv = StubClassAdapter.builder(mLog, cv)
+ .withDeleteReturns(mDeleteReturns.get(className))
+ .withNewClassName(newName)
+ .useOnlyStubNative(stubNativesOnly)
+ .build();
+ }
Set<String> delegateMethods = mDelegateMethods.get(className);
if (delegateMethods != null && !delegateMethods.isEmpty()) {
@@ -367,10 +415,18 @@ public class AsmGenerator {
if (promoteFields != null && !promoteFields.isEmpty()) {
cv = new PromoteFieldClassAdapter(cv, promoteFields);
}
+ Set<String> promoteMethods = mPromotedMethods.get(className);
+ if (promoteMethods != null && !promoteMethods.isEmpty()) {
+ cv = new PromoteMethodClassAdapter(cv, promoteMethods);
+ }
if (!mPromotedClasses.isEmpty()) {
cv = new PromoteClassClassAdapter(cv, mPromotedClasses);
}
+ if (mRenameStaticInitializerClasses.contains(binaryNewName)) {
+ cv = new DeferStaticInitializerClassAdapter(cv);
+ }
+
// Make sure no class file has a version above 55 (corresponding to Java 11),
// so that layoutlib can be run with JDK 11.
cv = new ChangeFileVersionAdapter(mLog, 55, cv);
diff --git a/create/src/com/android/tools/layoutlib/create/CreateInfo.java b/create/src/com/android/tools/layoutlib/create/CreateInfo.java
index 44046eb9eb..66a22d1fa5 100644
--- a/create/src/com/android/tools/layoutlib/create/CreateInfo.java
+++ b/create/src/com/android/tools/layoutlib/create/CreateInfo.java
@@ -18,13 +18,23 @@ package com.android.tools.layoutlib.create;
import com.android.tools.layoutlib.annotations.LayoutlibDelegate;
import com.android.tools.layoutlib.java.LinkedHashMap_Delegate;
-import com.android.tools.layoutlib.java.System_Delegate;
+import com.android.tools.layoutlib.java.NioUtils_Delegate;
+import com.android.tools.layoutlib.java.Reference_Delegate;
+import org.objectweb.asm.Opcodes;
+import org.objectweb.asm.Type;
+
+import java.lang.ref.Reference;
+import java.lang.ref.WeakReference;
+import java.text.DateFormat;
import java.util.Arrays;
import java.util.HashMap;
import java.util.HashSet;
+import java.util.LinkedHashMap;
+import java.util.Locale;
import java.util.Map;
import java.util.Set;
+import java.util.zip.ZipEntry;
/**
* Describes the work to be done by {@link AsmGenerator}.
@@ -32,6 +42,11 @@ import java.util.Set;
public final class CreateInfo implements ICreateInfo {
@Override
+ public MethodReplacer[] getMethodReplacers() {
+ return METHOD_REPLACERS;
+ }
+
+ @Override
public Class<?>[] getInjectedClasses() {
return INJECTED_CLASSES;
}
@@ -47,6 +62,21 @@ public final class CreateInfo implements ICreateInfo {
}
@Override
+ public String[] getDelegateClassNativesToNatives() {
+ return DELEGATE_CLASS_NATIVES_TO_NATIVES;
+ }
+
+ @Override
+ public boolean shouldKeepAllNativeClasses() {
+ return false;
+ }
+
+ @Override
+ public String[] getKeepClassNatives() {
+ return KEEP_CLASS_NATIVES;
+ }
+
+ @Override
public String[] getRenamedClasses() {
return RENAMED_CLASSES;
}
@@ -84,6 +114,11 @@ public final class CreateInfo implements ICreateInfo {
}
@Override
+ public String[] getPromotedMethods() {
+ return PROMOTED_METHODS;
+ }
+
+ @Override
public String[] getPromotedClasses() {
return PROMOTED_CLASSES;
}
@@ -93,8 +128,31 @@ public final class CreateInfo implements ICreateInfo {
return INJECTED_METHODS;
}
+ @Override
+ public String[] getDeferredStaticInitializerClasses() {
+ return DEFERRED_STATIC_INITIALIZER_CLASSES;
+ }
+
//-----
+ private static final MethodReplacer[] METHOD_REPLACERS = new MethodReplacer[] {
+ new SystemLoadLibraryReplacer(),
+ new SystemArrayCopyReplacer(),
+ new LocaleGetDefaultReplacer(),
+ new LocaleAdjustLanguageCodeReplacer(),
+ new SystemLogReplacer(),
+ new SystemNanoTimeReplacer(),
+ new SystemCurrentTimeMillisReplacer(),
+ new LinkedHashMapEldestReplacer(),
+ new ContextGetClassLoaderReplacer(),
+ new ImageReaderNativeInitReplacer(),
+ new NioUtilsFreeBufferReplacer(),
+ new ProcessInitializerInitSchedReplacer(),
+ new NativeInitPathReplacer(),
+ new ActivityThreadInAnimationReplacer(),
+ new ReferenceRefersToReplacer(),
+ };
+
/**
* The list of class from layoutlib_create to inject in layoutlib.
*/
@@ -108,160 +166,41 @@ public final class CreateInfo implements ICreateInfo {
InjectMethodRunnable.class,
InjectMethodRunnables.class,
/* Java package classes */
- System_Delegate.class,
LinkedHashMap_Delegate.class,
+ NioUtils_Delegate.class,
+ Reference_Delegate.class,
};
/**
* The list of methods to rewrite as delegates.
*/
- public final static String[] DELEGATE_METHODS = new String[] {
- "android.app.Fragment#instantiate", //(Landroid/content/Context;Ljava/lang/String;Landroid/os/Bundle;)Landroid/app/Fragment;",
- "android.content.res.Resources#getAnimation",
- "android.content.res.Resources#getAttributeSetSourceResId",
- "android.content.res.Resources#getBoolean",
- "android.content.res.Resources#getColor",
- "android.content.res.Resources#getColorStateList",
- "android.content.res.Resources#getDimension",
- "android.content.res.Resources#getDimensionPixelOffset",
- "android.content.res.Resources#getDimensionPixelSize",
- "android.content.res.Resources#getDrawable",
- "android.content.res.Resources#getFloat",
- "android.content.res.Resources#getFont",
- "android.content.res.Resources#getIdentifier",
- "android.content.res.Resources#getIntArray",
- "android.content.res.Resources#getInteger",
- "android.content.res.Resources#getLayout",
- "android.content.res.Resources#getQuantityString",
- "android.content.res.Resources#getQuantityText",
- "android.content.res.Resources#getResourceEntryName",
- "android.content.res.Resources#getResourceName",
- "android.content.res.Resources#getResourcePackageName",
- "android.content.res.Resources#getResourceTypeName",
- "android.content.res.Resources#getString",
- "android.content.res.Resources#getStringArray",
- "android.content.res.Resources#getText",
- "android.content.res.Resources#getTextArray",
- "android.content.res.Resources#getValue",
- "android.content.res.Resources#getValueForDensity",
- "android.content.res.Resources#getXml",
- "android.content.res.Resources#loadXmlResourceParser",
- "android.content.res.Resources#obtainAttributes",
- "android.content.res.Resources#obtainTypedArray",
- "android.content.res.Resources#openRawResource",
- "android.content.res.Resources#openRawResourceFd",
- "android.content.res.Resources$Theme#obtainStyledAttributes",
- "android.content.res.Resources$Theme#resolveAttribute",
- "android.content.res.Resources$Theme#resolveAttributes",
- "android.content.res.AssetManager#nativeCreate",
- "android.content.res.AssetManager#nativeDestroy",
- "android.content.res.AssetManager#nativeThemeCreate",
- "android.content.res.AssetManager#nativeThemeDestroy",
- "android.content.res.AssetManager#getAssignedPackageIdentifiers",
- "android.content.res.AssetManager#nativeCreateIdmapsForStaticOverlaysTargetingAndroid",
- "android.content.res.TypedArray#getValueAt",
- "android.content.res.TypedArray#obtain",
- "android.graphics.BitmapFactory#finishDecode",
- "android.graphics.BitmapFactory#setDensityFromOptions",
- "android.graphics.drawable.AnimatedVectorDrawable$VectorDrawableAnimatorRT#useLastSeenTarget",
- "android.graphics.drawable.AnimatedVectorDrawable$VectorDrawableAnimatorRT#onDraw",
- "android.graphics.drawable.GradientDrawable#buildRing",
- "android.graphics.drawable.AdaptiveIconDrawable#<init>",
- "android.graphics.drawable.DrawableInflater#inflateFromClass",
- "android.graphics.drawable.NinePatchDrawable#getOpacity",
- "android.graphics.FontFamily#addFont",
- "com.google.android.apps.common.testing.accessibility.framework.uielement" +
- ".AccessibilityHierarchyAndroid$ViewElementClassNamesAndroid#getClassByName",
- "android.graphics.Typeface#create",
- "android.graphics.Typeface$Builder#createAssetUid",
- "android.graphics.fonts.Font$Builder#createBuffer",
- "android.graphics.fonts.SystemFonts#getSystemFontConfigInternal",
- "android.os.Binder#getNativeBBinderHolder",
- "android.os.Binder#getNativeFinalizer",
- "android.os.Handler#sendMessageAtTime",
- "android.os.HandlerThread#run",
- "android.preference.Preference#getView",
- "android.text.format.DateFormat#is24HourFormat",
- "android.util.Xml#newPullParser",
- "android.view.Choreographer#getInstance",
- "android.view.Choreographer#getRefreshRate",
- "android.view.Choreographer#scheduleVsyncLocked",
- "android.view.Display#updateDisplayInfoLocked",
- "android.view.Display#getWindowManager",
- "android.view.HandlerActionQueue#postDelayed",
- "android.view.LayoutInflater#initPrecompiledViews",
- "android.view.LayoutInflater#rInflate",
- "android.view.LayoutInflater#parseInclude",
- "android.view.View#draw",
- "android.view.View#dispatchDetachedFromWindow",
- "android.view.View#layout",
- "android.view.View#measure",
- "android.view.View#getWindowToken",
- "android.view.View#isInEditMode",
- "android.view.ViewRootImpl#isInTouchMode",
- "android.view.WindowManagerGlobal#getWindowManagerService",
- "android.view.inputmethod.InputMethodManager#isInEditMode",
- "android.view.MenuInflater#registerMenu",
- "android.graphics.RenderNode#getMatrix",
- "android.graphics.RenderNode#nCreate",
- "android.graphics.RenderNode#nGetNativeFinalizer",
- "android.graphics.RenderNode#nSetElevation",
- "android.graphics.RenderNode#nGetElevation",
- "android.graphics.RenderNode#nSetTranslationX",
- "android.graphics.RenderNode#nGetTranslationX",
- "android.graphics.RenderNode#nSetTranslationY",
- "android.graphics.RenderNode#nGetTranslationY",
- "android.graphics.RenderNode#nSetTranslationZ",
- "android.graphics.RenderNode#nGetTranslationZ",
- "android.graphics.RenderNode#nSetRotation",
- "android.graphics.RenderNode#nGetRotation",
- "android.graphics.RenderNode#nSetLeft",
- "android.graphics.RenderNode#nSetTop",
- "android.graphics.RenderNode#nSetRight",
- "android.graphics.RenderNode#nSetBottom",
- "android.graphics.RenderNode#nSetLeftTopRightBottom",
- "android.graphics.RenderNode#nSetPivotX",
- "android.graphics.RenderNode#nGetPivotX",
- "android.graphics.RenderNode#nSetPivotY",
- "android.graphics.RenderNode#nGetPivotY",
- "android.graphics.RenderNode#nSetScaleX",
- "android.graphics.RenderNode#nGetScaleX",
- "android.graphics.RenderNode#nSetScaleY",
- "android.graphics.RenderNode#nGetScaleY",
- "android.graphics.RenderNode#nIsPivotExplicitlySet",
- "android.provider.DeviceConfig#getBoolean",
- "android.provider.DeviceConfig#getFloat",
- "android.provider.DeviceConfig#getInt",
- "android.provider.DeviceConfig#getLong",
- "android.provider.DeviceConfig#getString",
- "android.view.PointerIcon#loadResource",
- "android.view.PointerIcon#registerDisplayListener",
- "android.view.SurfaceControl#nativeCreateTransaction",
- "android.view.SurfaceControl#nativeGetNativeTransactionFinalizer",
- "android.view.ViewGroup#drawChild",
- "com.android.internal.view.menu.MenuBuilder#createNewMenuItem",
- "com.android.internal.util.XmlUtils#convertValueToInt",
- "dalvik.system.VMRuntime#getNotifyNativeInterval",
- "dalvik.system.VMRuntime#newUnpaddedArray",
- "libcore.io.MemoryMappedFile#mmapRO",
- "libcore.io.MemoryMappedFile#close",
- "libcore.io.MemoryMappedFile#bigEndianIterator",
- "libcore.util.NativeAllocationRegistry#applyFreeFunction",
- "libcore.util.NativeAllocationRegistry#registerNativeAllocation",
- };
+ public final static String[] DELEGATE_METHODS = NativeConfig.DELEGATE_METHODS;
/**
* The list of classes on which to delegate all native methods.
*/
- public final static String[] DELEGATE_CLASS_NATIVES = new String[] {
+ public final static String[] DELEGATE_CLASS_NATIVES = NativeConfig.DELEGATE_CLASS_NATIVES;
+
+ public final static String[] DELEGATE_CLASS_NATIVES_TO_NATIVES = new String[] {};
+
+ /**
+ * The list of classes on which NOT to delegate any native method.
+ */
+ public final static String[] KEEP_CLASS_NATIVES = new String[] {
"android.animation.PropertyValuesHolder",
+ "android.content.res.StringBlock",
+ "android.content.res.XmlBlock",
"android.graphics.BaseCanvas",
+ "android.graphics.BaseRecordingCanvas",
"android.graphics.Bitmap",
"android.graphics.BitmapFactory",
"android.graphics.BitmapShader",
"android.graphics.BlendModeColorFilter",
"android.graphics.BlurMaskFilter",
+ "android.graphics.BlurShader",
+ "android.graphics.Camera",
"android.graphics.Canvas",
+ "android.graphics.CanvasProperty",
"android.graphics.Color",
"android.graphics.ColorFilter",
"android.graphics.ColorMatrixColorFilter",
@@ -274,6 +213,9 @@ public final class CreateInfo implements ICreateInfo {
"android.graphics.DrawFilter",
"android.graphics.EmbossMaskFilter",
"android.graphics.FontFamily",
+ "android.graphics.HardwareRenderer",
+ "android.graphics.ImageDecoder",
+ "android.graphics.Interpolator",
"android.graphics.LightingColorFilter",
"android.graphics.LinearGradient",
"android.graphics.MaskFilter",
@@ -285,27 +227,47 @@ public final class CreateInfo implements ICreateInfo {
"android.graphics.PathDashPathEffect",
"android.graphics.PathEffect",
"android.graphics.PathMeasure",
+ "android.graphics.Picture",
"android.graphics.PorterDuffColorFilter",
"android.graphics.RadialGradient",
+ "android.graphics.RecordingCanvas",
"android.graphics.Region",
+ "android.graphics.RegionIterator",
+ "android.graphics.RenderEffect",
+ "android.graphics.RenderNode",
+ "android.graphics.RuntimeShader",
"android.graphics.Shader",
"android.graphics.SumPathEffect",
"android.graphics.SweepGradient",
+ "android.graphics.TableMaskFilter",
"android.graphics.Typeface",
+ "android.graphics.YuvImage",
"android.graphics.animation.NativeInterpolatorFactory",
+ "android.graphics.animation.RenderNodeAnimator",
"android.graphics.drawable.AnimatedVectorDrawable",
"android.graphics.drawable.VectorDrawable",
+ "android.graphics.fonts.Font",
"android.graphics.fonts.Font$Builder",
+ "android.graphics.fonts.FontFamily",
"android.graphics.fonts.FontFamily$Builder",
+ "android.graphics.fonts.FontFileUtil",
+ "android.graphics.fonts.SystemFonts",
+ "android.graphics.text.PositionedGlyphs",
+ "android.graphics.text.LineBreaker",
"android.graphics.text.MeasuredText",
"android.graphics.text.MeasuredText$Builder",
- "android.graphics.text.LineBreaker",
- "android.os.SystemClock",
+ "android.graphics.text.TextRunShaper",
+ "android.media.ImageReader",
+ "android.media.ImageReader$SurfaceImage",
+ "android.media.PublicFormatUtils",
"android.os.SystemProperties",
+ "android.os.Trace",
+ "android.text.AndroidCharacter",
+ "android.util.Log",
"android.util.PathParser",
- "android.view.Display",
+ "android.view.MotionEvent",
+ "android.view.Surface",
"com.android.internal.util.VirtualRefBasePtr",
- "libcore.icu.ICU",
};
/**
@@ -321,7 +283,6 @@ public final class CreateInfo implements ICreateInfo {
"android.view.accessibility.AccessibilityManager", "android.view.accessibility._Original_AccessibilityManager",
"android.view.accessibility.AccessibilityNodeIdManager", "android.view.accessibility._Original_AccessibilityNodeIdManager",
"android.webkit.WebView", "android.webkit._Original_WebView",
- "android.graphics.ImageDecoder", "android.graphics._Original_ImageDecoder",
};
/**
@@ -360,19 +321,43 @@ public final class CreateInfo implements ICreateInfo {
* needed when access from the delegate classes is needed.
*/
private final static String[] PROMOTED_FIELDS = new String[] {
- "android.graphics.drawable.VectorDrawable#mVectorState",
- "android.view.Choreographer#mLastFrameTimeNanos",
- "android.graphics.FontFamily#mBuilderPtr",
- "android.graphics.Typeface#DEFAULT_FAMILY",
- "android.graphics.Typeface#sDynamicTypefaceCache",
- "android.graphics.drawable.AdaptiveIconDrawable#sMask",
- "android.graphics.drawable.DrawableInflater#mRes",
+ "android.animation.AnimationHandler#mDelayedCallbackStartTime",
+ "android.animation.AnimationHandler#mAnimationCallbacks",
+ "android.animation.AnimationHandler#mCommitCallbacks",
+ "android.animation.AnimatorSet#mLastFrameTime",
"android.animation.PropertyValuesHolder#sSetterPropertyMap",
"android.animation.PropertyValuesHolder#sGetterPropertyMap",
"android.animation.PropertyValuesHolder$IntPropertyValuesHolder#sJNISetterPropertyMap",
"android.animation.PropertyValuesHolder$FloatPropertyValuesHolder#sJNISetterPropertyMap",
"android.animation.PropertyValuesHolder$MultiFloatValuesHolder#sJNISetterPropertyMap",
"android.animation.PropertyValuesHolder$MultiIntValuesHolder#sJNISetterPropertyMap",
+ "android.graphics.ImageDecoder$InputStreamSource#mInputStream",
+ "android.graphics.Typeface#DEFAULT_FAMILY",
+ "android.graphics.Typeface#sDynamicTypefaceCache",
+ "android.graphics.drawable.AnimatedVectorDrawable$VectorDrawableAnimatorUI#mSet",
+ "android.graphics.drawable.AnimatedVectorDrawable$VectorDrawableAnimatorRT#mPendingAnimationActions",
+ "android.graphics.drawable.AnimatedVectorDrawable#mAnimatorSet",
+ "android.graphics.drawable.AdaptiveIconDrawable#sMask",
+ "android.graphics.drawable.DrawableInflater#mRes",
+ "android.view.Choreographer#mCallbackQueues", // required for tests only
+ "android.view.Choreographer$CallbackQueue#mHead", // required for tests only
+ "com.android.internal.util.ArrayUtils#sCache",
+ };
+
+ /**
+ * List of methods for which we will update the visibility to be public.
+ */
+ private final static String[] PROMOTED_METHODS = new String[] {
+ "android.animation.AnimationHandler#doAnimationFrame",
+ "android.content.res.StringBlock#addParagraphSpan",
+ "android.content.res.StringBlock#getColor",
+ "android.graphics.Bitmap#setNinePatchChunk",
+ "android.graphics.Path#nInit",
+ "android.media.ImageReader#nativeClassInit",
+ "android.view.Choreographer#doFrame",
+ "android.view.Choreographer#postCallbackDelayedInternal",
+ "android.view.Choreographer#removeCallbacksInternal",
+ "android.view.ViewRootImpl#getRootMeasureSpec",
};
/**
@@ -380,6 +365,11 @@ public final class CreateInfo implements ICreateInfo {
* if possible.
*/
private final static String[] PROMOTED_CLASSES = new String[] {
+ "android.content.res.StringBlock$Height",
+ "android.graphics.ImageDecoder$InputStreamSource",
+ "android.graphics.drawable.AnimatedVectorDrawable$VectorDrawableAnimatorUI",
+ "android.graphics.drawable.AnimatedVectorDrawable$VectorDrawableAnimator",
+ "android.view.Choreographer$CallbackQueue", // required for tests only
};
/**
@@ -392,9 +382,293 @@ public final class CreateInfo implements ICreateInfo {
new String[] {
null }; // separator, for next class/methods list.
+ private final static String[] DEFERRED_STATIC_INITIALIZER_CLASSES =
+ NativeConfig.DEFERRED_STATIC_INITIALIZER_CLASSES;
+
private final static Map<String, InjectMethodRunnable> INJECTED_METHODS =
new HashMap<String, InjectMethodRunnable>(1) {{
put("android.content.Context",
InjectMethodRunnables.CONTEXT_GET_FRAMEWORK_CLASS_LOADER);
}};
+
+ public static class LinkedHashMapEldestReplacer implements MethodReplacer {
+
+ private final String VOID_TO_MAP_ENTRY =
+ Type.getMethodDescriptor(Type.getType(Map.Entry.class));
+ private final String LINKED_HASH_MAP = Type.getInternalName(LinkedHashMap.class);
+
+ @Override
+ public boolean isNeeded(String owner, String name, String desc, String sourceClass) {
+ return LINKED_HASH_MAP.equals(owner) &&
+ "eldest".equals(name) &&
+ VOID_TO_MAP_ENTRY.equals(desc);
+ }
+
+ @Override
+ public void replace(MethodInformation mi) {
+ mi.opcode = Opcodes.INVOKESTATIC;
+ mi.owner = Type.getInternalName(LinkedHashMap_Delegate.class);
+ mi.desc = Type.getMethodDescriptor(
+ Type.getType(Map.Entry.class), Type.getType(LinkedHashMap.class));
+ }
+ }
+
+ private static class ContextGetClassLoaderReplacer implements MethodReplacer {
+ // When LayoutInflater asks for a class loader, we must return the class loader that
+ // cannot return app's custom views/classes. This is so that in case of any failure
+ // or exception when instantiating the views, the IDE can replace it with a mock view
+ // and have proper error handling. However, if a custom view asks for the class
+ // loader, we must return a class loader that can find app's custom views as well.
+ // Thus, we rewrite the call to get class loader in LayoutInflater to
+ // getFrameworkClassLoader and inject a new method in Context. This leaves the normal
+ // method: Context.getClassLoader() free to be used by the apps.
+ private final String VOID_TO_CLASS_LOADER =
+ Type.getMethodDescriptor(Type.getType(ClassLoader.class));
+
+ @Override
+ public boolean isNeeded(String owner, String name, String desc, String sourceClass) {
+ return owner.equals("android/content/Context") &&
+ sourceClass.equals("android/view/LayoutInflater") &&
+ name.equals("getClassLoader") &&
+ desc.equals(VOID_TO_CLASS_LOADER);
+ }
+
+ @Override
+ public void replace(MethodInformation mi) {
+ mi.name = "getFrameworkClassLoader";
+ }
+ }
+
+ private static class SystemCurrentTimeMillisReplacer implements MethodReplacer {
+ @Override
+ public boolean isNeeded(String owner, String name, String desc, String sourceClass) {
+ return Type.getInternalName(System.class).equals(owner) && name.equals("currentTimeMillis");
+ }
+
+ @Override
+ public void replace(MethodInformation mi) {
+ mi.name = "currentTimeMillis";
+ mi.owner = "com/android/internal/lang/System_Delegate";
+ }
+ }
+
+ private static class SystemNanoTimeReplacer implements MethodReplacer {
+ @Override
+ public boolean isNeeded(String owner, String name, String desc, String sourceClass) {
+ return Type.getInternalName(System.class).equals(owner) && name.equals("nanoTime");
+ }
+
+ @Override
+ public void replace(MethodInformation mi) {
+ mi.name = "nanoTime";
+ mi.owner = "com/android/internal/lang/System_Delegate";
+ }
+ }
+
+ public static class SystemLogReplacer implements MethodReplacer {
+ @Override
+ public boolean isNeeded(String owner, String name, String desc, String sourceClass) {
+ return Type.getInternalName(System.class).equals(owner) && name.length() == 4
+ && name.startsWith("log");
+ }
+
+ @Override
+ public void replace(MethodInformation mi) {
+ assert mi.desc.equals("(Ljava/lang/String;Ljava/lang/Throwable;)V")
+ || mi.desc.equals("(Ljava/lang/String;)V");
+ mi.name = "log";
+ mi.owner = "com/android/internal/lang/System_Delegate";
+ }
+ }
+
+ /**
+ * Platform code should not loadLibrary on its own. Layoutlib loading infrastructure takes case
+ * of loading all the necessary native libraries (having the right paths etc.)
+ */
+ public static class SystemLoadLibraryReplacer implements MethodReplacer {
+ @Override
+ public boolean isNeeded(String owner, String name, String desc, String sourceClass) {
+ return Type.getInternalName(System.class).equals(owner) && name.equals("loadLibrary");
+ }
+
+ @Override
+ public void replace(MethodInformation mi) {
+ mi.owner = "com/android/internal/lang/System_Delegate";
+ }
+ }
+
+ /**
+ * This is to replace a static call to a dummy, so that ImageReader can be loaded and accessed
+ * during JNI loading
+ */
+ public static class ImageReaderNativeInitReplacer implements MethodReplacer {
+ @Override
+ public boolean isNeeded(String owner, String name, String desc, String sourceClass) {
+ return "android/media/ImageReader".equals(owner) && name.equals("nativeClassInit");
+ }
+
+ @Override
+ public void replace(MethodInformation mi) {
+ mi.owner = "android/media/ImageReader_Delegate";
+ mi.opcode = Opcodes.INVOKESTATIC;
+ }
+ }
+
+ private static class LocaleGetDefaultReplacer implements MethodReplacer {
+
+ @Override
+ public boolean isNeeded(String owner, String name, String desc, String sourceClass) {
+ return Type.getInternalName(Locale.class).equals(owner)
+ && "getDefault".equals(name)
+ && desc.equals(Type.getMethodDescriptor(Type.getType(Locale.class)));
+ }
+
+ @Override
+ public void replace(MethodInformation mi) {
+ mi.owner = "com/android/layoutlib/bridge/android/AndroidLocale";
+ }
+ }
+
+ public static class LocaleAdjustLanguageCodeReplacer implements MethodReplacer {
+
+ @Override
+ public boolean isNeeded(String owner, String name, String desc, String sourceClass) {
+ return Type.getInternalName(java.util.Locale.class).equals(owner)
+ && ("adjustLanguageCode".equals(name)
+ && desc.equals(Type.getMethodDescriptor(Type.getType(String.class), Type.getType(String.class))));
+ }
+
+ @Override
+ public void replace(MethodInformation mi) {
+ mi.owner = "com/android/tools/layoutlib/java/util/LocaleAdjustLanguageCodeReplacement";
+ }
+ }
+
+ private static class SystemArrayCopyReplacer implements MethodReplacer {
+ /**
+ * Descriptors for specialized versions {@link System#arraycopy} that are not present on the
+ * Desktop VM.
+ */
+ private static Set<String> ARRAYCOPY_DESCRIPTORS = new HashSet<>(Arrays.asList(
+ "([CI[CII)V", "([BI[BII)V", "([SI[SII)V", "([II[III)V",
+ "([JI[JII)V", "([FI[FII)V", "([DI[DII)V", "([ZI[ZII)V"));
+
+ @Override
+ public boolean isNeeded(String owner, String name, String desc, String sourceClass) {
+ return Type.getInternalName(System.class).equals(owner) && "arraycopy".equals(name) &&
+ ARRAYCOPY_DESCRIPTORS.contains(desc);
+ }
+
+ @Override
+ public void replace(MethodInformation mi) {
+ mi.desc = "(Ljava/lang/Object;ILjava/lang/Object;II)V";
+ }
+ }
+
+ public static class DateFormatSet24HourTimePrefReplacer implements MethodReplacer {
+
+ @Override
+ public boolean isNeeded(String owner, String name, String desc, String sourceClass) {
+ return Type.getInternalName(DateFormat.class).equals(owner) &&
+ "set24HourTimePref".equals(name);
+ }
+
+ @Override
+ public void replace(MethodInformation mi) {
+ mi.owner = "com/android/tools/layoutlib/java/text/DateFormat_Delegate";
+ }
+ }
+
+ /**
+ * Replace references to ZipEntry.getDataOffset with a delegate, since it does not exist in the JDK.
+ * @see {@link com.android.tools.layoutlib.java.util.zip.ZipEntry_Delegate#getDataOffset(ZipEntry)}
+ */
+ public static class ZipEntryGetDataOffsetReplacer implements MethodReplacer {
+ @Override
+ public boolean isNeeded(String owner, String name, String desc, String sourceClass) {
+ return Type.getInternalName(ZipEntry.class).equals(owner)
+ && "getDataOffset".equals(name);
+ }
+
+ @Override
+ public void replace(MethodInformation mi) {
+ mi.opcode = Opcodes.INVOKESTATIC;
+ mi.owner = "com/android/tools/layoutlib/java/util/zip/ZipEntry_Delegate";
+ mi.desc = Type.getMethodDescriptor(
+ Type.getType(long.class), Type.getType(ZipEntry.class));
+ }
+ }
+
+ public static class NioUtilsFreeBufferReplacer implements MethodReplacer {
+ @Override
+ public boolean isNeeded(String owner, String name, String desc, String sourceClass) {
+ return "java/nio/NioUtils".equals(owner) && name.equals("freeDirectBuffer");
+ }
+
+ @Override
+ public void replace(MethodInformation mi) {
+ mi.owner = Type.getInternalName(NioUtils_Delegate.class);
+ }
+ }
+
+ public static class ProcessInitializerInitSchedReplacer implements MethodReplacer {
+ @Override
+ public boolean isNeeded(String owner, String name, String desc, String sourceClass) {
+ return "android/graphics/HardwareRenderer$ProcessInitializer".equals(owner) &&
+ name.equals("initSched");
+ }
+
+ @Override
+ public void replace(MethodInformation mi) {
+ mi.owner = "android/graphics/HardwareRenderer_ProcessInitializer_Delegate";
+ mi.opcode = Opcodes.INVOKESTATIC;
+ mi.desc = "(J)V";
+ }
+ }
+
+ public static class NativeInitPathReplacer implements MethodReplacer {
+ @Override
+ public boolean isNeeded(String owner, String name, String desc, String sourceClass) {
+ return "android/graphics/Path".equals(owner) &&
+ "nInit".equals(name) && "(J)J".equals(desc);
+ }
+
+ @Override
+ public void replace(MethodInformation mi) {
+ mi.owner = "android/graphics/Path_Delegate";
+ mi.opcode = Opcodes.INVOKESTATIC;
+ }
+ }
+
+ public static class ActivityThreadInAnimationReplacer implements MethodReplacer {
+ @Override
+ public boolean isNeeded(String owner, String name, String desc, String sourceClass) {
+ return ("android/app/ActivityThread").equals(owner) &&
+ name.equals("getSystemUiContext") &&
+ sourceClass.equals("android/view/animation/Animation");
+ }
+
+ @Override
+ public void replace(MethodInformation mi) {
+ mi.owner = "android/app/ActivityThread_Delegate";
+ mi.opcode = Opcodes.INVOKESTATIC;
+ mi.desc = "()Landroid/content/Context;";
+ }
+ }
+
+ public static class ReferenceRefersToReplacer implements MethodReplacer {
+ @Override
+ public boolean isNeeded(String owner, String name, String desc, String sourceClass) {
+ return Type.getInternalName(WeakReference.class).equals(owner) &&
+ "refersTo".equals(name);
+ }
+
+ @Override
+ public void replace(MethodInformation mi) {
+ mi.opcode = Opcodes.INVOKESTATIC;
+ mi.owner = Type.getInternalName(Reference_Delegate.class);
+ mi.desc = Type.getMethodDescriptor(Type.BOOLEAN_TYPE, Type.getType(Reference.class),
+ Type.getType(Object.class));
+ }
+ }
}
diff --git a/create/src/com/android/tools/layoutlib/create/DeferStaticInitializerClassAdapter.java b/create/src/com/android/tools/layoutlib/create/DeferStaticInitializerClassAdapter.java
new file mode 100644
index 0000000000..b5c331d838
--- /dev/null
+++ b/create/src/com/android/tools/layoutlib/create/DeferStaticInitializerClassAdapter.java
@@ -0,0 +1,54 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.tools.layoutlib.create;
+
+import org.objectweb.asm.ClassVisitor;
+import org.objectweb.asm.FieldVisitor;
+import org.objectweb.asm.MethodVisitor;
+import org.objectweb.asm.Opcodes;
+
+import java.lang.reflect.Modifier;
+
+/**
+ * Renames the static initializer to a public deferredStaticInitializer method.
+ */
+public class DeferStaticInitializerClassAdapter extends ClassVisitor {
+
+ public DeferStaticInitializerClassAdapter(ClassVisitor cv) {
+ super(Main.ASM_VERSION, cv);
+ }
+
+ @Override
+ public MethodVisitor visitMethod(int access, String name, String desc, String signature,
+ String[] exceptions) {
+ if (name.equals("<clinit>")) {
+ name = "deferredStaticInitializer";
+ access |= Modifier.PUBLIC;
+ }
+ return super.visitMethod(access, name, desc, signature, exceptions);
+ }
+
+ @Override
+ public FieldVisitor visitField(int access, String name, String desc, String signature,
+ Object value) {
+ // Java 9 does not allow static final field to be modified outside of <clinit>.
+ // So if a field is static, it has to be non-final.
+ if ((access & Opcodes.ACC_STATIC) != 0 ) {
+ access = access & ~Opcodes.ACC_FINAL;;
+ }
+ return super.visitField(access, name, desc, signature, value);
+ }
+}
diff --git a/create/src/com/android/tools/layoutlib/create/DelegateClassAdapter.java b/create/src/com/android/tools/layoutlib/create/DelegateClassAdapter.java
index f9c7eb3d73..ac24170874 100644
--- a/create/src/com/android/tools/layoutlib/create/DelegateClassAdapter.java
+++ b/create/src/com/android/tools/layoutlib/create/DelegateClassAdapter.java
@@ -99,11 +99,20 @@ public class DelegateClassAdapter extends ClassVisitor {
}
if (CLASS_INIT.equals(name)) {
- // We don't currently support generating delegates for constructors.
- throw new UnsupportedOperationException(
- String.format(
- "Delegate doesn't support overriding static constructor %1$s:%2$s(%3$s)",
- mClassName, name, desc));
+ access = access & ~Opcodes.ACC_PRIVATE; // make the replacement method package protected.
+
+ // writer for moving the original code to a 'SomeClass.staticInit_Original' method
+ MethodVisitor renamedMethodWriter = super.visitMethod(access,
+ "staticInit" + ORIGINAL_SUFFIX,
+ desc, signature, exceptions);
+ // writer for writing the SomeClass.clinit method with a single call to
+ // SomeClass_Delegate.staticInit
+ MethodVisitor originalMethodWriter = super.visitMethod(access, name,
+ desc, signature, exceptions);
+
+ return new StaticInitMethodAdapter(mLog, renamedMethodWriter,
+ originalMethodWriter, mClassName);
+
}
if (isNative) {
diff --git a/create/src/com/android/tools/layoutlib/create/DelegateMethodAdapter.java b/create/src/com/android/tools/layoutlib/create/DelegateMethodAdapter.java
index da8babcbca..630550031d 100644
--- a/create/src/com/android/tools/layoutlib/create/DelegateMethodAdapter.java
+++ b/create/src/com/android/tools/layoutlib/create/DelegateMethodAdapter.java
@@ -89,6 +89,8 @@ class DelegateMethodAdapter extends MethodVisitor {
private final boolean mIsStaticInnerClass;
/** The internal class name (e.g. <code>com/android/SomeClass$InnerClass</code>.) */
private final String mClassName;
+ /** The corresponding delegate class name */
+ private final String mDelegateClassName;
/** The method name. */
private final String mMethodName;
/** Logger object. */
@@ -111,6 +113,7 @@ class DelegateMethodAdapter extends MethodVisitor {
* Must never be null.
* @param className The internal class name of the class to visit,
* e.g. <code>com/android/SomeClass$InnerClass</code>.
+ * @param delegateClassName The internal class name of the delegate class
* @param methodName The simple name of the method.
* @param desc A method descriptor (c.f. {@link Type#getReturnType(String)} +
* {@link Type#getArgumentTypes(String)})
@@ -120,6 +123,7 @@ class DelegateMethodAdapter extends MethodVisitor {
MethodVisitor mvOriginal,
MethodVisitor mvDelegate,
String className,
+ String delegateClassName,
String methodName,
String desc,
boolean isStatic,
@@ -129,12 +133,25 @@ class DelegateMethodAdapter extends MethodVisitor {
mOrgWriter = mvOriginal;
mDelWriter = mvDelegate;
mClassName = className;
+ mDelegateClassName = delegateClassName;
mMethodName = methodName;
mDesc = desc;
mIsStatic = isStatic;
mIsStaticInnerClass = isStaticClass;
}
+ public DelegateMethodAdapter(Log log,
+ MethodVisitor mvOriginal,
+ MethodVisitor mvDelegate,
+ String className,
+ String methodName,
+ String desc,
+ boolean isStatic,
+ boolean isStaticClass) {
+ this(log, mvOriginal, mvDelegate, className, className + DELEGATE_SUFFIX, methodName,
+ desc, isStatic, isStaticClass);
+ }
+
/**
* Generates the new code for the method.
* <p/>
@@ -189,7 +206,7 @@ class DelegateMethodAdapter extends MethodVisitor {
}
ArrayList<Type> paramTypes = new ArrayList<>();
- String delegateClassName = mClassName + DELEGATE_SUFFIX;
+ String delegateClassName = mDelegateClassName;
boolean pushedArg0 = false;
int maxStack = 0;
diff --git a/create/src/com/android/tools/layoutlib/create/DelegateToNativeAdapter.java b/create/src/com/android/tools/layoutlib/create/DelegateToNativeAdapter.java
new file mode 100644
index 0000000000..d063e15f71
--- /dev/null
+++ b/create/src/com/android/tools/layoutlib/create/DelegateToNativeAdapter.java
@@ -0,0 +1,104 @@
+package com.android.tools.layoutlib.create;
+
+import com.android.tools.layoutlib.annotations.LayoutlibDelegate;
+
+import org.objectweb.asm.AnnotationVisitor;
+import org.objectweb.asm.ClassVisitor;
+import org.objectweb.asm.ClassWriter;
+import org.objectweb.asm.MethodVisitor;
+import org.objectweb.asm.Opcodes;
+import org.objectweb.asm.Type;
+
+import java.util.ArrayList;
+import java.util.Map;
+import java.util.Set;
+
+
+public class DelegateToNativeAdapter extends ClassVisitor {
+ private final Log mLog;
+ private final ClassWriter mDelegateWriter;
+ private final String mDelegateName;
+ private final String mClassName;
+ private final Set<String> mDelegateMethods;
+
+ public DelegateToNativeAdapter(Log logger, ClassVisitor cv, String className,
+ Map<String, ClassWriter> delegates, Set<String> delegateMethods) {
+ super(Main.ASM_VERSION, cv);
+ mLog = logger;
+ mDelegateWriter = new ClassWriter(ClassWriter.COMPUTE_MAXS);
+ mClassName = className;
+ mDelegateName = (className + "_NativeDelegate").replace('$', '_');
+ mDelegateMethods = delegateMethods;
+ delegates.put(mDelegateName, mDelegateWriter);
+ }
+
+ @Override
+ public void visit(int version, int access, String name, String signature, String superName,
+ String[] interfaces) {
+ super.visit(version, access, name, signature, superName, interfaces);
+
+ mDelegateWriter.visit(version, Opcodes.ACC_PUBLIC, mDelegateName, null, "java/lang/Object",
+ null);
+ }
+
+ @Override
+ public MethodVisitor visitMethod(int access, String name, String desc, String signature,
+ String[] exceptions) {
+ boolean isStaticMethod = (access & Opcodes.ACC_STATIC) != 0;
+ boolean isNative = (access & Opcodes.ACC_NATIVE) != 0;
+
+ if (isNative) {
+ mDelegateWriter.visitMethod(access, name + "_Original", desc, signature, exceptions);
+ generateDelegateMethod(name, desc, signature, exceptions);
+
+ if (mDelegateMethods == null || !mDelegateMethods.contains(name)) {
+ // Remove native flag
+ access = access & ~Opcodes.ACC_NATIVE;
+ MethodVisitor mwDelegate =
+ super.visitMethod(access, name, desc, signature, exceptions);
+
+ DelegateMethodAdapter a =
+ new DelegateMethodAdapter(mLog, null, mwDelegate, mClassName, mDelegateName,
+ name, desc, isStaticMethod, false);
+
+ // A native has no code to visit, so we need to generate it directly.
+ a.generateDelegateCode();
+
+ return mwDelegate;
+ }
+ }
+ return super.visitMethod(access, name, desc, signature, exceptions);
+ }
+
+ private void generateDelegateMethod(String name, String desc, String signature,
+ String[] exceptions) {
+ MethodVisitor delegateVisitor =
+ mDelegateWriter.visitMethod(Opcodes.ACC_STATIC, name, desc,
+ signature,
+ exceptions);
+ AnnotationVisitor aw = delegateVisitor.visitAnnotation(
+ Type.getObjectType(Type.getInternalName(LayoutlibDelegate.class)).toString(),
+ true); // visible at runtime
+ if (aw != null) {
+ aw.visitEnd();
+ }
+ delegateVisitor.visitCode();
+ int maxStack = 0;
+ int maxLocals = 0;
+ Type[] argTypes = Type.getArgumentTypes(desc);
+ for (Type t : argTypes) {
+ int size = t.getSize();
+ delegateVisitor.visitVarInsn(t.getOpcode(Opcodes.ILOAD), maxLocals);
+ maxLocals += size;
+ maxStack += size;
+ }
+ delegateVisitor.visitMethodInsn(Opcodes.INVOKESTATIC, mDelegateName,
+ name + "_Original", desc, false);
+
+ Type returnType = Type.getReturnType(desc);
+ delegateVisitor.visitInsn(returnType.getOpcode(Opcodes.IRETURN));
+
+ delegateVisitor.visitMaxs(maxStack, maxLocals);
+ delegateVisitor.visitEnd();
+ }
+}
diff --git a/create/src/com/android/tools/layoutlib/create/ICreateInfo.java b/create/src/com/android/tools/layoutlib/create/ICreateInfo.java
index 42d57279c4..83c5b24523 100644
--- a/create/src/com/android/tools/layoutlib/create/ICreateInfo.java
+++ b/create/src/com/android/tools/layoutlib/create/ICreateInfo.java
@@ -19,13 +19,14 @@ package com.android.tools.layoutlib.create;
import org.objectweb.asm.ClassVisitor;
import java.util.Map;
-import java.util.Set;
/**
* Interface describing the work to be done by {@link AsmGenerator}.
*/
public interface ICreateInfo {
+ MethodReplacer[] getMethodReplacers();
+
/**
* Returns the list of class from layoutlib_create to inject in layoutlib.
* The list can be empty but must not be null.
@@ -45,6 +46,27 @@ public interface ICreateInfo {
String[] getDelegateClassNatives();
/**
+ * Returns the list of classes for which to create a delegate class that delegates all native
+ * methods to corresponding native methods. This is useful for classes that call native
+ * methods during static initialization.
+ * The list can be empty but must not be null.
+ */
+ String[] getDelegateClassNativesToNatives();
+
+ /**
+ * Returns true if native methods should not be stubbed by default.
+ */
+ boolean shouldKeepAllNativeClasses();
+
+ /**
+ * Returns the list of classes for which not to delegate any native method.
+ * The list can be empty but must not be null.
+ *
+ * Only used when shouldKeepAllNativeClasses is false.
+ */
+ String[] getKeepClassNatives();
+
+ /**
* Returns the list of classes to rename, must be an even list: the binary FQCN
* of class to replace followed by the new FQCN.
* The list can be empty but must not be null.
@@ -87,6 +109,13 @@ public interface ICreateInfo {
String[] getPromotedFields();
/**
+ * Returns a list of methods which should be promoted to public visibility. The array values
+ * are in the form of the binary FQCN of the class containing the method and the method name
+ * separated by a '#'.
+ */
+ String[] getPromotedMethods();
+
+ /**
* Returns a list of classes to be promoted to public visibility.
*/
String[] getPromotedClasses();
@@ -98,6 +127,18 @@ public interface ICreateInfo {
*/
Map<String, InjectMethodRunnable> getInjectedMethodsMap();
+ String[] getDeferredStaticInitializerClasses();
+
+ interface MethodReplacer {
+ boolean isNeeded(String owner, String name, String desc, String sourceClass);
+
+ /**
+ * Updates the MethodInformation with the new values of the method attributes -
+ * opcode, owner, name and desc.
+ */
+ void replace(MethodInformation mi);
+ }
+
abstract class InjectMethodRunnable {
/**
* @param cv Must be {@link ClassVisitor}. However, the param type is object so that when
@@ -107,4 +148,18 @@ public interface ICreateInfo {
*/
public abstract void generateMethods(Object cv);
}
+
+ class MethodInformation {
+ public int opcode;
+ public String owner;
+ public String name;
+ public String desc;
+
+ public MethodInformation(int opcode, String owner, String name, String desc) {
+ this.opcode = opcode;
+ this.owner = owner;
+ this.name = name;
+ this.desc = desc;
+ }
+ }
}
diff --git a/create/src/com/android/tools/layoutlib/create/Main.java b/create/src/com/android/tools/layoutlib/create/Main.java
index 4acc754d42..4d0de146c6 100644
--- a/create/src/com/android/tools/layoutlib/create/Main.java
+++ b/create/src/com/android/tools/layoutlib/create/Main.java
@@ -16,9 +16,6 @@
package com.android.tools.layoutlib.create;
-import org.objectweb.asm.ClassReader;
-import org.objectweb.asm.ClassVisitor;
-import org.objectweb.asm.ClassWriter;
import org.objectweb.asm.Opcodes;
import java.io.File;
@@ -62,9 +59,10 @@ public class Main {
private boolean listAllDeps = false;
private boolean listOnlyMissingDeps = false;
private boolean createStubLib = false;
+ private boolean createNativeOnlyDelegates = false;
}
- public static final int ASM_VERSION = Opcodes.ASM6;
+ public static final int ASM_VERSION = Opcodes.ASM7;
private static final Options sOptions = new Options();
@@ -76,7 +74,7 @@ public class Main {
String[] osDestJar = { null };
if (!processArgs(log, args, osJarPath, osDestJar)) {
- log.error("Usage: layoutlib_create [-v] [--create-stub] output.jar input.jar ...");
+ log.error("Usage: layoutlib_create [-v] [--create-stub] [--create-native-only-delegates] output.jar input.jar ...");
log.error("Usage: layoutlib_create [-v] [--list-deps|--missing-deps] input.jar ...");
System.exit(1);
}
@@ -99,7 +97,7 @@ public class Main {
}
try {
- CreateInfo info = new CreateInfo();
+ ICreateInfo info = new CreateInfo();
AsmGenerator agen = new AsmGenerator(log, info);
AsmAnalyzer aa = new AsmAnalyzer(log, osJarPath,
@@ -138,12 +136,13 @@ public class Main {
"com.android.internal.graphics.drawable.AnimationScaleListDrawable",
"com.google.android.apps.common.testing.accessibility.**",
"com.google.android.libraries.accessibility.**",
+ "android.service.wallpaper.*", // needed for Wear OS watch faces
},
info.getExcludedClasses(),
new String[] {
"com/android/i18n/phonenumbers/data/*",
"android/icu/impl/data/**"
- });
+ }, info.getMethodReplacers());
agen.setAnalysisResult(aa.analyze());
Map<String, byte[]> outputClasses = agen.generate();
@@ -166,6 +165,13 @@ public class Main {
log.info("Created stub JAR file %s", stubDestJarFile);
}
+ if (sOptions.createNativeOnlyDelegates) {
+ File osDestJarFile = new File(osDestJar);
+ Map<String, byte[]> nativeDelegateClasses =
+ outputClasses.entrySet().stream().filter(entry -> entry.getKey().endsWith("_NativeDelegate.class")).collect(Collectors.toMap(Entry::getKey, Entry::getValue));
+ JarUtil.createJar(new FileOutputStream(osDestJarFile), nativeDelegateClasses);
+ log.info("Created native delegate JAR file %s", osDestJarFile);
+ }
// Throw an error if any class failed to get renamed by the generator
//
@@ -234,6 +240,8 @@ public class Main {
needs_dest = false;
} else if (s.equals("--create-stub")) {
sOptions.createStubLib = true;
+ } else if (s.equals("--create-native-only-delegates")) {
+ sOptions.createNativeOnlyDelegates = true;
} else if (!s.startsWith("-")) {
if (needs_dest && osDestJar[0] == null) {
osDestJar[0] = s;
diff --git a/create/src/com/android/tools/layoutlib/create/PromoteMethodClassAdapter.java b/create/src/com/android/tools/layoutlib/create/PromoteMethodClassAdapter.java
new file mode 100644
index 0000000000..45be08f0e2
--- /dev/null
+++ b/create/src/com/android/tools/layoutlib/create/PromoteMethodClassAdapter.java
@@ -0,0 +1,32 @@
+package com.android.tools.layoutlib.create;
+
+import org.objectweb.asm.ClassVisitor;
+import org.objectweb.asm.MethodVisitor;
+
+import java.util.Set;
+
+import static org.objectweb.asm.Opcodes.ACC_PRIVATE;
+import static org.objectweb.asm.Opcodes.ACC_PROTECTED;
+import static org.objectweb.asm.Opcodes.ACC_PUBLIC;
+
+public class PromoteMethodClassAdapter extends ClassVisitor {
+
+ private final Set<String> mMethodNames;
+ private static final int CLEAR_PRIVATE_MASK = ~(ACC_PRIVATE | ACC_PROTECTED);
+
+ public PromoteMethodClassAdapter(ClassVisitor cv, Set<String> methodNames) {
+ super(Main.ASM_VERSION, cv);
+ mMethodNames = methodNames;
+ }
+
+ @Override
+ public MethodVisitor visitMethod(int access, String name, String desc, String signature,
+ String[] exceptions) {
+ if (mMethodNames.contains(name)) {
+ if ((access & ACC_PUBLIC) == 0) {
+ access = (access & CLEAR_PRIVATE_MASK) | ACC_PUBLIC;
+ }
+ }
+ return super.visitMethod(access, name, desc, signature, exceptions);
+ }
+}
diff --git a/create/src/com/android/tools/layoutlib/create/ReplaceMethodCallsAdapter.java b/create/src/com/android/tools/layoutlib/create/ReplaceMethodCallsAdapter.java
index 2967b05872..a9bcd224e1 100644
--- a/create/src/com/android/tools/layoutlib/create/ReplaceMethodCallsAdapter.java
+++ b/create/src/com/android/tools/layoutlib/create/ReplaceMethodCallsAdapter.java
@@ -16,21 +16,12 @@
package com.android.tools.layoutlib.create;
-import com.android.tools.layoutlib.java.LinkedHashMap_Delegate;
-import com.android.tools.layoutlib.java.System_Delegate;
+import com.android.tools.layoutlib.create.ICreateInfo.MethodInformation;
+import com.android.tools.layoutlib.create.ICreateInfo.MethodReplacer;
import org.objectweb.asm.ClassVisitor;
import org.objectweb.asm.MethodVisitor;
-import org.objectweb.asm.Opcodes;
-import org.objectweb.asm.Type;
-import java.util.ArrayList;
-import java.util.Arrays;
-import java.util.HashSet;
-import java.util.LinkedHashMap;
-import java.util.List;
-import java.util.Locale;
-import java.util.Map;
import java.util.Set;
/**
@@ -39,176 +30,12 @@ import java.util.Set;
*/
public class ReplaceMethodCallsAdapter extends ClassVisitor {
- /**
- * Descriptors for specialized versions {@link System#arraycopy} that are not present on the
- * Desktop VM.
- */
- private static Set<String> ARRAYCOPY_DESCRIPTORS = new HashSet<>(Arrays.asList(
- "([CI[CII)V", "([BI[BII)V", "([SI[SII)V", "([II[III)V",
- "([JI[JII)V", "([FI[FII)V", "([DI[DII)V", "([ZI[ZII)V"));
-
- private static final List<MethodReplacer> METHOD_REPLACERS = new ArrayList<>(5);
-
- private static final String ANDROID_LOCALE_CLASS =
- "com/android/layoutlib/bridge/android/AndroidLocale";
-
- private static final String JAVA_LOCALE_CLASS = Type.getInternalName(java.util.Locale.class);
- private static final Type STRING = Type.getType(String.class);
-
- private static final String JAVA_LANG_SYSTEM = Type.getInternalName(System.class);
-
- // Static initialization block to initialize METHOD_REPLACERS.
- static {
- // Case 1: java.lang.System.arraycopy()
- METHOD_REPLACERS.add(new MethodReplacer() {
- @Override
- public boolean isNeeded(String owner, String name, String desc, String sourceClass) {
- return JAVA_LANG_SYSTEM.equals(owner) && "arraycopy".equals(name) &&
- ARRAYCOPY_DESCRIPTORS.contains(desc);
- }
-
- @Override
- public void replace(MethodInformation mi) {
- mi.desc = "(Ljava/lang/Object;ILjava/lang/Object;II)V";
- }
- });
-
- // Case 2: java.util.Locale.adjustLanguageCode() or java.util.Locale.getDefault()
- METHOD_REPLACERS.add(new MethodReplacer() {
-
- private final String STRING_TO_STRING = Type.getMethodDescriptor(STRING, STRING);
- private final String VOID_TO_LOCALE =
- Type.getMethodDescriptor(Type.getType(Locale.class));
-
- @Override
- public boolean isNeeded(String owner, String name, String desc, String sourceClass) {
- return JAVA_LOCALE_CLASS.equals(owner) &&
- ("adjustLanguageCode".equals(name) && desc.equals(STRING_TO_STRING) ||
- "getDefault".equals(name) && desc.equals(VOID_TO_LOCALE));
- }
-
- @Override
- public void replace(MethodInformation mi) {
- mi.owner = ANDROID_LOCALE_CLASS;
- }
- });
-
- // Case 3: java.lang.System.log?()
- METHOD_REPLACERS.add(new MethodReplacer() {
- @Override
- public boolean isNeeded(String owner, String name, String desc, String sourceClass) {
- return JAVA_LANG_SYSTEM.equals(owner) && name.length() == 4
- && name.startsWith("log");
- }
-
- @Override
- public void replace(MethodInformation mi) {
- assert mi.desc.equals("(Ljava/lang/String;Ljava/lang/Throwable;)V")
- || mi.desc.equals("(Ljava/lang/String;)V");
- mi.name = "log";
- mi.owner = Type.getInternalName(System_Delegate.class);
- }
- });
-
- // Case 4: java.lang.System time calls
- METHOD_REPLACERS.add(new MethodReplacer() {
- @Override
- public boolean isNeeded(String owner, String name, String desc, String sourceClass) {
- return JAVA_LANG_SYSTEM.equals(owner) && name.equals("nanoTime");
- }
-
- @Override
- public void replace(MethodInformation mi) {
- mi.name = "nanoTime";
- mi.owner = Type.getInternalName(System_Delegate.class);
- }
- });
- METHOD_REPLACERS.add(new MethodReplacer() {
- @Override
- public boolean isNeeded(String owner, String name, String desc, String sourceClass) {
- return JAVA_LANG_SYSTEM.equals(owner) && name.equals("currentTimeMillis");
- }
-
- @Override
- public void replace(MethodInformation mi) {
- mi.name = "currentTimeMillis";
- mi.owner = Type.getInternalName(System_Delegate.class);
- }
- });
-
- // Case 5: java.util.LinkedHashMap.eldest()
- METHOD_REPLACERS.add(new MethodReplacer() {
-
- private final String VOID_TO_MAP_ENTRY =
- Type.getMethodDescriptor(Type.getType(Map.Entry.class));
- private final String LINKED_HASH_MAP = Type.getInternalName(LinkedHashMap.class);
-
- @Override
- public boolean isNeeded(String owner, String name, String desc, String sourceClass) {
- return LINKED_HASH_MAP.equals(owner) &&
- "eldest".equals(name) &&
- VOID_TO_MAP_ENTRY.equals(desc);
- }
-
- @Override
- public void replace(MethodInformation mi) {
- mi.opcode = Opcodes.INVOKESTATIC;
- mi.owner = Type.getInternalName(LinkedHashMap_Delegate.class);
- mi.desc = Type.getMethodDescriptor(
- Type.getType(Map.Entry.class), Type.getType(LinkedHashMap.class));
- }
- });
-
- // Case 6: android.content.Context.getClassLoader() in LayoutInflater
- METHOD_REPLACERS.add(new MethodReplacer() {
- // When LayoutInflater asks for a class loader, we must return the class loader that
- // cannot return app's custom views/classes. This is so that in case of any failure
- // or exception when instantiating the views, the IDE can replace it with a mock view
- // and have proper error handling. However, if a custom view asks for the class
- // loader, we must return a class loader that can find app's custom views as well.
- // Thus, we rewrite the call to get class loader in LayoutInflater to
- // getFrameworkClassLoader and inject a new method in Context. This leaves the normal
- // method: Context.getClassLoader() free to be used by the apps.
- private final String VOID_TO_CLASS_LOADER =
- Type.getMethodDescriptor(Type.getType(ClassLoader.class));
-
- @Override
- public boolean isNeeded(String owner, String name, String desc, String sourceClass) {
- return owner.equals("android/content/Context") &&
- sourceClass.equals("android/view/LayoutInflater") &&
- name.equals("getClassLoader") &&
- desc.equals(VOID_TO_CLASS_LOADER);
- }
-
- @Override
- public void replace(MethodInformation mi) {
- mi.name = "getFrameworkClassLoader";
- }
- });
- }
-
- /**
- * If a method some.package.Class.Method(args) is called from some.other.Class,
- * @param owner some/package/Class
- * @param name Method
- * @param desc (args)returnType
- * @param sourceClass some/other/Class
- * @return if the method invocation needs to be replaced by some other class.
- */
- public static boolean isReplacementNeeded(String owner, String name, String desc,
- String sourceClass) {
- for (MethodReplacer replacer : METHOD_REPLACERS) {
- if (replacer.isNeeded(owner, name, desc, sourceClass)) {
- return true;
- }
- }
- return false;
- }
-
+ private Set<MethodReplacer> mMethodReplacers;
private final String mOriginalClassName;
- public ReplaceMethodCallsAdapter(ClassVisitor cv, String originalClassName) {
+ public ReplaceMethodCallsAdapter(Set<MethodReplacer> methodReplacers, ClassVisitor cv, String originalClassName) {
super(Main.ASM_VERSION, cv);
+ mMethodReplacers = methodReplacers;
mOriginalClassName = originalClassName;
}
@@ -227,7 +54,7 @@ public class ReplaceMethodCallsAdapter extends ClassVisitor {
@Override
public void visitMethodInsn(int opcode, String owner, String name, String desc,
boolean itf) {
- for (MethodReplacer replacer : METHOD_REPLACERS) {
+ for (MethodReplacer replacer : mMethodReplacers) {
if (replacer.isNeeded(owner, name, desc, mOriginalClassName)) {
MethodInformation mi = new MethodInformation(opcode, owner, name, desc);
replacer.replace(mi);
@@ -242,27 +69,4 @@ public class ReplaceMethodCallsAdapter extends ClassVisitor {
}
}
- private static class MethodInformation {
- public int opcode;
- public String owner;
- public String name;
- public String desc;
-
- public MethodInformation(int opcode, String owner, String name, String desc) {
- this.opcode = opcode;
- this.owner = owner;
- this.name = name;
- this.desc = desc;
- }
- }
-
- private interface MethodReplacer {
- boolean isNeeded(String owner, String name, String desc, String sourceClass);
-
- /**
- * Updates the MethodInformation with the new values of the method attributes -
- * opcode, owner, name and desc.
- */
- void replace(MethodInformation mi);
- }
}
diff --git a/create/src/com/android/tools/layoutlib/create/StaticInitMethodAdapter.java b/create/src/com/android/tools/layoutlib/create/StaticInitMethodAdapter.java
new file mode 100644
index 0000000000..0e3ffeb271
--- /dev/null
+++ b/create/src/com/android/tools/layoutlib/create/StaticInitMethodAdapter.java
@@ -0,0 +1,290 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.tools.layoutlib.create;
+
+import org.objectweb.asm.AnnotationVisitor;
+import org.objectweb.asm.Attribute;
+import org.objectweb.asm.Label;
+import org.objectweb.asm.MethodVisitor;
+import org.objectweb.asm.Opcodes;
+import org.objectweb.asm.Type;
+
+import static com.android.tools.layoutlib.create.DelegateMethodAdapter.DELEGATE_SUFFIX;
+import com.android.tools.layoutlib.annotations.LayoutlibDelegate;
+
+class StaticInitMethodAdapter extends MethodVisitor {
+ /** static initializer delegate name. */
+ private static final String DELEGATE_STATIC_NAME = "staticInit";
+
+ /** The internal class name (e.g. <code>com/android/SomeClass$InnerClass</code>.) */
+ private final String mClassName;
+
+ /** Logger object. */
+ private final Log mLog;
+
+ /** The method writer to copy of the original method. */
+ private final MethodVisitor mRenamedMethodWriter;
+
+ /** The method writer to generate the original static { SomeClass_Delegate.staticInit} block */
+ private final MethodVisitor mOriginalMethodWriter;
+
+ /** Array used to capture the first line number information from the original method
+ * and duplicate it in the delegate. */
+ private Object[] mDelegateLineNumber;
+
+ public StaticInitMethodAdapter(Log log, MethodVisitor renamedMethodWriter,
+ MethodVisitor originalMethodWriter,
+ String className) {
+ super(Main.ASM_VERSION);
+ mLog = log;
+ mRenamedMethodWriter = renamedMethodWriter;
+ mOriginalMethodWriter = originalMethodWriter;
+ mClassName = className;
+ }
+
+
+
+ /**
+ * Generate the new code for the method.
+ *
+ * This will be a call to className_Delegate#staticInit
+ */
+ private void generateDelegateCode() {
+ AnnotationVisitor aw = mOriginalMethodWriter.visitAnnotation(
+ Type.getObjectType(Type.getInternalName(LayoutlibDelegate.class)).toString(),
+ true); // visible at runtime
+ if (aw != null) {
+ aw.visitEnd();
+ }
+
+ mOriginalMethodWriter.visitCode();
+
+ if (mDelegateLineNumber != null) {
+ Object[] p = mDelegateLineNumber;
+ mOriginalMethodWriter.visitLineNumber((Integer) p[0], (Label) p[1]);
+ }
+
+ String delegateClassName = mClassName + DELEGATE_SUFFIX;
+ delegateClassName = delegateClassName.replace('$', '_');
+
+ // generate the SomeClass_Delegate.staticInit call.
+ mOriginalMethodWriter.visitMethodInsn(Opcodes.INVOKESTATIC, delegateClassName,
+ DELEGATE_STATIC_NAME,
+ Type.getMethodDescriptor(Type.VOID_TYPE),
+ false);
+ mOriginalMethodWriter.visitInsn(Type.VOID_TYPE.getOpcode(Opcodes.IRETURN));
+ mOriginalMethodWriter.visitMaxs(0, 0);
+
+ mOriginalMethodWriter.visitEnd();
+
+ mLog.debug("static initializer call for class %s delegated to %s#%s",
+ mClassName,
+ delegateClassName, DELEGATE_STATIC_NAME);
+ }
+
+ /* Pass down to visitor writer. In this implementation, either do nothing. */
+ @Override
+ public void visitCode() {
+ if (mRenamedMethodWriter != null) {
+ mRenamedMethodWriter.visitCode();
+ }
+ }
+
+ /*
+ * visitMaxs is called just before visitEnd if there was any code to rewrite.
+ */
+ @Override
+ public void visitMaxs(int maxStack, int maxLocals) {
+ if (mRenamedMethodWriter != null) {
+ mRenamedMethodWriter.visitMaxs(maxStack, maxLocals);
+ }
+ }
+
+ /** End of visiting. Generate the delegating code. */
+ @Override
+ public void visitEnd() {
+ if (mRenamedMethodWriter != null) {
+ mRenamedMethodWriter.visitEnd();
+ }
+ generateDelegateCode();
+ }
+
+ /* Writes all annotation from the original method. */
+ @Override
+ public AnnotationVisitor visitAnnotation(String desc, boolean visible) {
+ if (mRenamedMethodWriter != null) {
+ return mRenamedMethodWriter.visitAnnotation(desc, visible);
+ } else {
+ return null;
+ }
+ }
+
+ /* Writes all annotation default values from the original method. */
+ @Override
+ public AnnotationVisitor visitAnnotationDefault() {
+ if (mRenamedMethodWriter != null) {
+ return mRenamedMethodWriter.visitAnnotationDefault();
+ } else {
+ return null;
+ }
+ }
+
+ @Override
+ public AnnotationVisitor visitParameterAnnotation(int parameter, String desc,
+ boolean visible) {
+ if (mRenamedMethodWriter != null) {
+ return mRenamedMethodWriter.visitParameterAnnotation(parameter, desc, visible);
+ } else {
+ return null;
+ }
+ }
+
+ /* Writes all attributes from the original method. */
+ @Override
+ public void visitAttribute(Attribute attr) {
+ if (mRenamedMethodWriter != null) {
+ mRenamedMethodWriter.visitAttribute(attr);
+ }
+ }
+
+ /*
+ * Only writes the first line number present in the original code so that source
+ * viewers can direct to the correct method, even if the content doesn't match.
+ */
+ @Override
+ public void visitLineNumber(int line, Label start) {
+ // Capture the first line values for the new delegate method
+ if (mDelegateLineNumber == null) {
+ mDelegateLineNumber = new Object[] { line, start };
+ }
+ if (mRenamedMethodWriter != null) {
+ mRenamedMethodWriter.visitLineNumber(line, start);
+ }
+ }
+
+ @Override
+ public void visitInsn(int opcode) {
+ if (mRenamedMethodWriter != null) {
+ mRenamedMethodWriter.visitInsn(opcode);
+ }
+ }
+
+ @Override
+ public void visitLabel(Label label) {
+ if (mRenamedMethodWriter != null) {
+ mRenamedMethodWriter.visitLabel(label);
+ }
+ }
+
+ @Override
+ public void visitTryCatchBlock(Label start, Label end, Label handler, String type) {
+ if (mRenamedMethodWriter != null) {
+ mRenamedMethodWriter.visitTryCatchBlock(start, end, handler, type);
+ }
+ }
+
+ @Override
+ public void visitMethodInsn(int opcode, String owner, String name, String desc, boolean itf) {
+ if (mRenamedMethodWriter != null) {
+ mRenamedMethodWriter.visitMethodInsn(opcode, owner, name, desc, itf);
+ }
+ }
+
+ @Override
+ public void visitFieldInsn(int opcode, String owner, String name, String desc) {
+ if (mRenamedMethodWriter != null) {
+ mRenamedMethodWriter.visitFieldInsn(opcode, owner, name, desc);
+ }
+ }
+
+ @Override
+ public void visitFrame(int type, int nLocal, Object[] local, int nStack, Object[] stack) {
+ if (mRenamedMethodWriter != null) {
+ mRenamedMethodWriter.visitFrame(type, nLocal, local, nStack, stack);
+ }
+ }
+
+ @Override
+ public void visitIincInsn(int var, int increment) {
+ if (mRenamedMethodWriter != null) {
+ mRenamedMethodWriter.visitIincInsn(var, increment);
+ }
+ }
+
+ @Override
+ public void visitIntInsn(int opcode, int operand) {
+ if (mRenamedMethodWriter != null) {
+ mRenamedMethodWriter.visitIntInsn(opcode, operand);
+ }
+ }
+
+ @Override
+ public void visitJumpInsn(int opcode, Label label) {
+ if (mRenamedMethodWriter != null) {
+ mRenamedMethodWriter.visitJumpInsn(opcode, label);
+ }
+ }
+
+ @Override
+ public void visitLdcInsn(Object cst) {
+ if (mRenamedMethodWriter != null) {
+ mRenamedMethodWriter.visitLdcInsn(cst);
+ }
+ }
+
+ @Override
+ public void visitLocalVariable(String name, String desc, String signature,
+ Label start, Label end, int index) {
+ if (mRenamedMethodWriter != null) {
+ mRenamedMethodWriter.visitLocalVariable(name, desc, signature, start, end, index);
+ }
+ }
+
+ @Override
+ public void visitLookupSwitchInsn(Label dflt, int[] keys, Label[] labels) {
+ if (mRenamedMethodWriter != null) {
+ mRenamedMethodWriter.visitLookupSwitchInsn(dflt, keys, labels);
+ }
+ }
+
+ @Override
+ public void visitMultiANewArrayInsn(String desc, int dims) {
+ if (mRenamedMethodWriter != null) {
+ mRenamedMethodWriter.visitMultiANewArrayInsn(desc, dims);
+ }
+ }
+
+ @Override
+ public void visitTableSwitchInsn(int min, int max, Label dflt, Label[] labels) {
+ if (mRenamedMethodWriter != null) {
+ mRenamedMethodWriter.visitTableSwitchInsn(min, max, dflt, labels);
+ }
+ }
+
+ @Override
+ public void visitTypeInsn(int opcode, String type) {
+ if (mRenamedMethodWriter != null) {
+ mRenamedMethodWriter.visitTypeInsn(opcode, type);
+ }
+ }
+
+ @Override
+ public void visitVarInsn(int opcode, int var) {
+ if (mRenamedMethodWriter != null) {
+ mRenamedMethodWriter.visitVarInsn(opcode, var);
+ }
+ }
+}
diff --git a/create/src/com/android/tools/layoutlib/create/StubClassAdapter.java b/create/src/com/android/tools/layoutlib/create/StubClassAdapter.java
index e1d2df2a85..5202ce49bd 100644
--- a/create/src/com/android/tools/layoutlib/create/StubClassAdapter.java
+++ b/create/src/com/android/tools/layoutlib/create/StubClassAdapter.java
@@ -34,7 +34,7 @@ import java.util.Set;
/**
* Class adapter that can stub some or all of the methods of the class.
*/
-class StubClassAdapter extends ClassVisitor {
+public class StubClassAdapter extends ClassVisitor {
public interface MethodVisitorFactory {
@NotNull
MethodVisitor create(@NotNull MethodVisitor mv,
diff --git a/create/src/com/android/tools/layoutlib/java/NioUtils_Delegate.java b/create/src/com/android/tools/layoutlib/java/NioUtils_Delegate.java
new file mode 100644
index 0000000000..33039b5fe9
--- /dev/null
+++ b/create/src/com/android/tools/layoutlib/java/NioUtils_Delegate.java
@@ -0,0 +1,15 @@
+
+package com.android.tools.layoutlib.java;
+
+import java.nio.ByteBuffer;
+
+public final class NioUtils_Delegate {
+ public static void freeDirectBuffer(ByteBuffer buffer) {
+ /*
+ * NioUtils is not included in layoutlib classpath. Thus, calling NioUtils.freeDirectBuffer in
+ * {@link android.graphics.ImageReader} produces ClassNotFound exception. Moreover, it does not
+ * seem we have to do anything in here as we are only referencing the existing native buffer
+ * and do not perform any allocation on creation.
+ */
+ }
+} \ No newline at end of file
diff --git a/create/src/com/android/tools/layoutlib/java/Reference_Delegate.java b/create/src/com/android/tools/layoutlib/java/Reference_Delegate.java
new file mode 100644
index 0000000000..a774275051
--- /dev/null
+++ b/create/src/com/android/tools/layoutlib/java/Reference_Delegate.java
@@ -0,0 +1,29 @@
+/*
+ * Copyright (C) 2022 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.tools.layoutlib.java;
+
+import java.lang.ref.Reference;
+
+/**
+ * Provides an alternative implementation for java.lang.ref.Reference#refersTo, as it exists
+ * in ART but not in the host JDK.
+ */
+public class Reference_Delegate {
+ public static <T> boolean refersTo(Reference<T> thisRef, T obj) {
+ return thisRef.get() == obj;
+ }
+}
diff --git a/create/tests/Android.bp b/create/tests/Android.bp
index 31fbe22abd..b5e7033f0d 100644
--- a/create/tests/Android.bp
+++ b/create/tests/Android.bp
@@ -14,12 +14,16 @@
package {
// See: http://go/android-license-faq
- // A large-scale-change added 'default_applicable_licenses' to import
- // all of the 'license_kinds' from "frameworks_layoutlib_license"
- // to get the below license kinds:
- // SPDX-license-identifier-Apache-2.0
- // SPDX-license-identifier-EPL
- default_applicable_licenses: ["frameworks_layoutlib_license"],
+ default_applicable_licenses: [
+ "Android-Apache-2.0",
+ "frameworks_layoutlib_create_tests_license",
+ ],
+}
+
+license {
+ name: "frameworks_layoutlib_create_tests_license",
+ license_kinds: ["SPDX-license-identifier-EPL"],
+ license_text: ["LICENSE"],
}
java_test_host {
@@ -35,7 +39,7 @@ java_test_host {
"junit",
"hamcrest",
],
- static_libs: ["asm-6.0"],
+ static_libs: ["asm-9.2"],
// Copy the jar to DIST_DIR for sdk builds
dist: {
diff --git a/create/tests/LICENSE b/create/tests/LICENSE
new file mode 100644
index 0000000000..3998fcebee
--- /dev/null
+++ b/create/tests/LICENSE
@@ -0,0 +1,261 @@
+<?xml version="1.0" encoding="ISO-8859-1" ?>
+<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
+<html xmlns="http://www.w3.org/1999/xhtml">
+
+<head>
+<meta http-equiv="Content-Type" content="text/html; charset=ISO-8859-1" />
+<title>Eclipse Public License - Version 1.0</title>
+<style type="text/css">
+ body {
+ size: 8.5in 11.0in;
+ margin: 0.25in 0.5in 0.25in 0.5in;
+ tab-interval: 0.5in;
+ }
+ p {
+ margin-left: auto;
+ margin-top: 0.5em;
+ margin-bottom: 0.5em;
+ }
+ p.list {
+ margin-left: 0.5in;
+ margin-top: 0.05em;
+ margin-bottom: 0.05em;
+ }
+ </style>
+
+</head>
+
+<body lang="EN-US">
+
+<h2>Eclipse Public License - v 1.0</h2>
+
+<p>THE ACCOMPANYING PROGRAM IS PROVIDED UNDER THE TERMS OF THIS ECLIPSE
+PUBLIC LICENSE (&quot;AGREEMENT&quot;). ANY USE, REPRODUCTION OR
+DISTRIBUTION OF THE PROGRAM CONSTITUTES RECIPIENT'S ACCEPTANCE OF THIS
+AGREEMENT.</p>
+
+<p><b>1. DEFINITIONS</b></p>
+
+<p>&quot;Contribution&quot; means:</p>
+
+<p class="list">a) in the case of the initial Contributor, the initial
+code and documentation distributed under this Agreement, and</p>
+<p class="list">b) in the case of each subsequent Contributor:</p>
+<p class="list">i) changes to the Program, and</p>
+<p class="list">ii) additions to the Program;</p>
+<p class="list">where such changes and/or additions to the Program
+originate from and are distributed by that particular Contributor. A
+Contribution 'originates' from a Contributor if it was added to the
+Program by such Contributor itself or anyone acting on such
+Contributor's behalf. Contributions do not include additions to the
+Program which: (i) are separate modules of software distributed in
+conjunction with the Program under their own license agreement, and (ii)
+are not derivative works of the Program.</p>
+
+<p>&quot;Contributor&quot; means any person or entity that distributes
+the Program.</p>
+
+<p>&quot;Licensed Patents&quot; mean patent claims licensable by a
+Contributor which are necessarily infringed by the use or sale of its
+Contribution alone or when combined with the Program.</p>
+
+<p>&quot;Program&quot; means the Contributions distributed in accordance
+with this Agreement.</p>
+
+<p>&quot;Recipient&quot; means anyone who receives the Program under
+this Agreement, including all Contributors.</p>
+
+<p><b>2. GRANT OF RIGHTS</b></p>
+
+<p class="list">a) Subject to the terms of this Agreement, each
+Contributor hereby grants Recipient a non-exclusive, worldwide,
+royalty-free copyright license to reproduce, prepare derivative works
+of, publicly display, publicly perform, distribute and sublicense the
+Contribution of such Contributor, if any, and such derivative works, in
+source code and object code form.</p>
+
+<p class="list">b) Subject to the terms of this Agreement, each
+Contributor hereby grants Recipient a non-exclusive, worldwide,
+royalty-free patent license under Licensed Patents to make, use, sell,
+offer to sell, import and otherwise transfer the Contribution of such
+Contributor, if any, in source code and object code form. This patent
+license shall apply to the combination of the Contribution and the
+Program if, at the time the Contribution is added by the Contributor,
+such addition of the Contribution causes such combination to be covered
+by the Licensed Patents. The patent license shall not apply to any other
+combinations which include the Contribution. No hardware per se is
+licensed hereunder.</p>
+
+<p class="list">c) Recipient understands that although each Contributor
+grants the licenses to its Contributions set forth herein, no assurances
+are provided by any Contributor that the Program does not infringe the
+patent or other intellectual property rights of any other entity. Each
+Contributor disclaims any liability to Recipient for claims brought by
+any other entity based on infringement of intellectual property rights
+or otherwise. As a condition to exercising the rights and licenses
+granted hereunder, each Recipient hereby assumes sole responsibility to
+secure any other intellectual property rights needed, if any. For
+example, if a third party patent license is required to allow Recipient
+to distribute the Program, it is Recipient's responsibility to acquire
+that license before distributing the Program.</p>
+
+<p class="list">d) Each Contributor represents that to its knowledge it
+has sufficient copyright rights in its Contribution, if any, to grant
+the copyright license set forth in this Agreement.</p>
+
+<p><b>3. REQUIREMENTS</b></p>
+
+<p>A Contributor may choose to distribute the Program in object code
+form under its own license agreement, provided that:</p>
+
+<p class="list">a) it complies with the terms and conditions of this
+Agreement; and</p>
+
+<p class="list">b) its license agreement:</p>
+
+<p class="list">i) effectively disclaims on behalf of all Contributors
+all warranties and conditions, express and implied, including warranties
+or conditions of title and non-infringement, and implied warranties or
+conditions of merchantability and fitness for a particular purpose;</p>
+
+<p class="list">ii) effectively excludes on behalf of all Contributors
+all liability for damages, including direct, indirect, special,
+incidental and consequential damages, such as lost profits;</p>
+
+<p class="list">iii) states that any provisions which differ from this
+Agreement are offered by that Contributor alone and not by any other
+party; and</p>
+
+<p class="list">iv) states that source code for the Program is available
+from such Contributor, and informs licensees how to obtain it in a
+reasonable manner on or through a medium customarily used for software
+exchange.</p>
+
+<p>When the Program is made available in source code form:</p>
+
+<p class="list">a) it must be made available under this Agreement; and</p>
+
+<p class="list">b) a copy of this Agreement must be included with each
+copy of the Program.</p>
+
+<p>Contributors may not remove or alter any copyright notices contained
+within the Program.</p>
+
+<p>Each Contributor must identify itself as the originator of its
+Contribution, if any, in a manner that reasonably allows subsequent
+Recipients to identify the originator of the Contribution.</p>
+
+<p><b>4. COMMERCIAL DISTRIBUTION</b></p>
+
+<p>Commercial distributors of software may accept certain
+responsibilities with respect to end users, business partners and the
+like. While this license is intended to facilitate the commercial use of
+the Program, the Contributor who includes the Program in a commercial
+product offering should do so in a manner which does not create
+potential liability for other Contributors. Therefore, if a Contributor
+includes the Program in a commercial product offering, such Contributor
+(&quot;Commercial Contributor&quot;) hereby agrees to defend and
+indemnify every other Contributor (&quot;Indemnified Contributor&quot;)
+against any losses, damages and costs (collectively &quot;Losses&quot;)
+arising from claims, lawsuits and other legal actions brought by a third
+party against the Indemnified Contributor to the extent caused by the
+acts or omissions of such Commercial Contributor in connection with its
+distribution of the Program in a commercial product offering. The
+obligations in this section do not apply to any claims or Losses
+relating to any actual or alleged intellectual property infringement. In
+order to qualify, an Indemnified Contributor must: a) promptly notify
+the Commercial Contributor in writing of such claim, and b) allow the
+Commercial Contributor to control, and cooperate with the Commercial
+Contributor in, the defense and any related settlement negotiations. The
+Indemnified Contributor may participate in any such claim at its own
+expense.</p>
+
+<p>For example, a Contributor might include the Program in a commercial
+product offering, Product X. That Contributor is then a Commercial
+Contributor. If that Commercial Contributor then makes performance
+claims, or offers warranties related to Product X, those performance
+claims and warranties are such Commercial Contributor's responsibility
+alone. Under this section, the Commercial Contributor would have to
+defend claims against the other Contributors related to those
+performance claims and warranties, and if a court requires any other
+Contributor to pay any damages as a result, the Commercial Contributor
+must pay those damages.</p>
+
+<p><b>5. NO WARRANTY</b></p>
+
+<p>EXCEPT AS EXPRESSLY SET FORTH IN THIS AGREEMENT, THE PROGRAM IS
+PROVIDED ON AN &quot;AS IS&quot; BASIS, WITHOUT WARRANTIES OR CONDITIONS
+OF ANY KIND, EITHER EXPRESS OR IMPLIED INCLUDING, WITHOUT LIMITATION,
+ANY WARRANTIES OR CONDITIONS OF TITLE, NON-INFRINGEMENT, MERCHANTABILITY
+OR FITNESS FOR A PARTICULAR PURPOSE. Each Recipient is solely
+responsible for determining the appropriateness of using and
+distributing the Program and assumes all risks associated with its
+exercise of rights under this Agreement , including but not limited to
+the risks and costs of program errors, compliance with applicable laws,
+damage to or loss of data, programs or equipment, and unavailability or
+interruption of operations.</p>
+
+<p><b>6. DISCLAIMER OF LIABILITY</b></p>
+
+<p>EXCEPT AS EXPRESSLY SET FORTH IN THIS AGREEMENT, NEITHER RECIPIENT
+NOR ANY CONTRIBUTORS SHALL HAVE ANY LIABILITY FOR ANY DIRECT, INDIRECT,
+INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING
+WITHOUT LIMITATION LOST PROFITS), HOWEVER CAUSED AND ON ANY THEORY OF
+LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OR
+DISTRIBUTION OF THE PROGRAM OR THE EXERCISE OF ANY RIGHTS GRANTED
+HEREUNDER, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGES.</p>
+
+<p><b>7. GENERAL</b></p>
+
+<p>If any provision of this Agreement is invalid or unenforceable under
+applicable law, it shall not affect the validity or enforceability of
+the remainder of the terms of this Agreement, and without further action
+by the parties hereto, such provision shall be reformed to the minimum
+extent necessary to make such provision valid and enforceable.</p>
+
+<p>If Recipient institutes patent litigation against any entity
+(including a cross-claim or counterclaim in a lawsuit) alleging that the
+Program itself (excluding combinations of the Program with other
+software or hardware) infringes such Recipient's patent(s), then such
+Recipient's rights granted under Section 2(b) shall terminate as of the
+date such litigation is filed.</p>
+
+<p>All Recipient's rights under this Agreement shall terminate if it
+fails to comply with any of the material terms or conditions of this
+Agreement and does not cure such failure in a reasonable period of time
+after becoming aware of such noncompliance. If all Recipient's rights
+under this Agreement terminate, Recipient agrees to cease use and
+distribution of the Program as soon as reasonably practicable. However,
+Recipient's obligations under this Agreement and any licenses granted by
+Recipient relating to the Program shall continue and survive.</p>
+
+<p>Everyone is permitted to copy and distribute copies of this
+Agreement, but in order to avoid inconsistency the Agreement is
+copyrighted and may only be modified in the following manner. The
+Agreement Steward reserves the right to publish new versions (including
+revisions) of this Agreement from time to time. No one other than the
+Agreement Steward has the right to modify this Agreement. The Eclipse
+Foundation is the initial Agreement Steward. The Eclipse Foundation may
+assign the responsibility to serve as the Agreement Steward to a
+suitable separate entity. Each new version of the Agreement will be
+given a distinguishing version number. The Program (including
+Contributions) may always be distributed subject to the version of the
+Agreement under which it was received. In addition, after a new version
+of the Agreement is published, Contributor may elect to distribute the
+Program (including its Contributions) under the new version. Except as
+expressly stated in Sections 2(a) and 2(b) above, Recipient receives no
+rights or licenses to the intellectual property of any Contributor under
+this Agreement, whether expressly, by implication, estoppel or
+otherwise. All rights in the Program not expressly granted under this
+Agreement are reserved.</p>
+
+<p>This Agreement is governed by the laws of the State of New York and
+the intellectual property laws of the United States of America. No party
+to this Agreement will bring a legal action under this Agreement more
+than one year after the cause of action arose. Each party waives its
+rights to a jury trial in any resulting litigation.</p>
+
+</body>
+
+</html> \ No newline at end of file
diff --git a/create/tests/res/data/mock_android.jar b/create/tests/res/data/mock_android.jar
index 3fd699920d..2534ece32e 100644
--- a/create/tests/res/data/mock_android.jar
+++ b/create/tests/res/data/mock_android.jar
Binary files differ
diff --git a/create/tests/res/mock_data/README.md b/create/tests/res/mock_data/README.md
new file mode 100644
index 0000000000..6b59184903
--- /dev/null
+++ b/create/tests/res/mock_data/README.md
@@ -0,0 +1,25 @@
+# Update mock_android.jar
+
+## Build mock_android.jar
+
+First, use set up instructions from layoutlib tutorial to make commands like `m` work.
+
+Then, run the following in the repository root:
+
+```
+m mock_android
+mv out/host/linux-x86/framework/mock_android.jar frameworks/layoutlib/create/tests/res/data/mock_android.jar
+```
+
+## Build problems
+
+If you see a build error like:
+
+```
+[ 29% 351/1203] including frameworks/layoutlib/out/test/create/mock_data/Android.mk ...
+FAILED:
+build/make/core/base_rules.mk:325: error: frameworks/layoutlib/out/test/create/mock_data: MODULE.linux.JAVA_LIBRARIES.mock_android already defined by frameworks/layoutlib/create/tests/res/mock_data.
+16:26:52 ckati failed with: exit status 1
+```
+
+a quick solution would be to remove `frameworks/layoutlib/out/test/create/mock_data/Android.mk`.
diff --git a/create/tests/res/mock_data/mock_android/view/LibLoader.java b/create/tests/res/mock_data/mock_android/view/LibLoader.java
new file mode 100644
index 0000000000..c24a288196
--- /dev/null
+++ b/create/tests/res/mock_data/mock_android/view/LibLoader.java
@@ -0,0 +1,7 @@
+package mock_android.view;
+
+class LibLoader {
+ static {
+ System.loadLibrary("dummy");
+ }
+} \ No newline at end of file
diff --git a/create/tests/run_tests.sh b/create/tests/run_tests.sh
new file mode 100755
index 0000000000..3d5383855a
--- /dev/null
+++ b/create/tests/run_tests.sh
@@ -0,0 +1,13 @@
+#!/bin/bash
+
+SCRIPT_DIR="$(dirname $0)"
+DIST_DIR="$1"
+
+STUDIO_JDK=${SCRIPT_DIR}"/../../../../prebuilts/jdk/jdk11/linux-x86"
+OUT_INTERMEDIATES=${SCRIPT_DIR}"/../../../../out/soong/.intermediates"
+
+${STUDIO_JDK}/bin/java -ea \
+ -cp ${OUT_INTERMEDIATES}/external/junit/junit/linux_glibc_common/javac/junit.jar:${OUT_INTERMEDIATES}/external/hamcrest/hamcrest-core/hamcrest/linux_glibc_common/javac/hamcrest.jar:${OUT_INTERMEDIATES}/frameworks/layoutlib/create/layoutlib_create/linux_glibc_common/combined/layoutlib_create.jar:${OUT_INTERMEDIATES}/frameworks/layoutlib/create/tests/layoutlib-create-tests/linux_glibc_common/combined/layoutlib-create-tests.jar:${SCRIPT_DIR}/res \
+ org.junit.runner.JUnitCore \
+ com.android.tools.layoutlib.create.AllTests
+
diff --git a/create/tests/src/com/android/tools/layoutlib/create/AllTests.java b/create/tests/src/com/android/tools/layoutlib/create/AllTests.java
new file mode 100644
index 0000000000..cdc43e0be2
--- /dev/null
+++ b/create/tests/src/com/android/tools/layoutlib/create/AllTests.java
@@ -0,0 +1,34 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.tools.layoutlib.create;
+
+import org.junit.runner.RunWith;
+import org.junit.runners.Suite;
+import org.junit.runners.Suite.SuiteClasses;
+
+/**
+ * Suite containing all the layoutlib-create tests
+ */
+@RunWith(Suite.class)
+@SuiteClasses({
+ AsmAnalyzerTest.class, AsmGeneratorTest.class,
+ ClassHasNativeVisitorTest.class, DelegateClassAdapterTest.class,
+ LogTest.class, PromoteClassClassAdapterTest.class, RenameClassAdapterTest.class,
+ StubClassAdapterTest.class, StubMethodAdapterTest.class,
+})
+public class AllTests {
+}
diff --git a/create/tests/src/com/android/tools/layoutlib/create/AsmAnalyzerTest.java b/create/tests/src/com/android/tools/layoutlib/create/AsmAnalyzerTest.java
index 327bbb7615..d0943d5cad 100644
--- a/create/tests/src/com/android/tools/layoutlib/create/AsmAnalyzerTest.java
+++ b/create/tests/src/com/android/tools/layoutlib/create/AsmAnalyzerTest.java
@@ -19,6 +19,7 @@ package com.android.tools.layoutlib.create;
import com.android.tools.layoutlib.create.AsmAnalyzer.DependencyVisitor;
import com.android.tools.layoutlib.create.AsmAnalyzer.Result;
+import com.android.tools.layoutlib.create.ICreateInfo.MethodReplacer;
import org.junit.Test;
import org.objectweb.asm.ClassReader;
@@ -55,7 +56,8 @@ public class AsmAnalyzerTest {
private static AsmAnalyzer getDefaultAnalyzer() {
MockLog log = new MockLog();
return new AsmAnalyzer(log, MOCK_ANDROID_JAR, null ,
- null /* includeGlobs */, DEFAULT_EXCLUDES, DEFAULT_INCLUDE_FILES);
+ null /* includeGlobs */, DEFAULT_EXCLUDES, DEFAULT_INCLUDE_FILES,
+ new MethodReplacer[] {});
}
@Test
@@ -84,6 +86,7 @@ public class AsmAnalyzerTest {
"mock_android.fake2.keep.DoNotRemove",
"mock_android.util.EmptyArray",
"mock_android.util.NotNeeded",
+ "mock_android.view.LibLoader",
"mock_android.view.View",
"mock_android.view.ViewGroup",
"mock_android.view.ViewGroup$LayoutParams",
@@ -126,7 +129,8 @@ public class AsmAnalyzerTest {
"mock_android.fake2.*", // Exclude subpackages select
},
DEFAULT_EXCLUDES,
- DEFAULT_INCLUDE_FILES);
+ DEFAULT_INCLUDE_FILES,
+ new MethodReplacer[] {});
Result result = analyzer.analyze();
assertArrayEquals(new String[] {
"mock_android.fake.InnerTest$NotStaticInner1",
diff --git a/create/tests/src/com/android/tools/layoutlib/create/AsmGeneratorTest.java b/create/tests/src/com/android/tools/layoutlib/create/AsmGeneratorTest.java
index 3f5e8f6306..aadab22173 100644
--- a/create/tests/src/com/android/tools/layoutlib/create/AsmGeneratorTest.java
+++ b/create/tests/src/com/android/tools/layoutlib/create/AsmGeneratorTest.java
@@ -18,6 +18,10 @@
package com.android.tools.layoutlib.create;
+import com.android.tools.layoutlib.create.CreateInfo.SystemLoadLibraryReplacer;
+import com.android.tools.layoutlib.create.ICreateInfo.MethodInformation;
+import com.android.tools.layoutlib.create.ICreateInfo.MethodReplacer;
+
import org.junit.After;
import org.junit.Before;
import org.junit.Test;
@@ -39,6 +43,7 @@ import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.HashSet;
+import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.zip.ZipEntry;
@@ -106,7 +111,8 @@ public class AsmGeneratorTest {
"**"
},
new String[]{} /* excluded classes */,
- new String[]{} /* include files */);
+ new String[]{}, /* include files */
+ new MethodReplacer[] {});
agen.setAnalysisResult(aa.analyze());
agen.generate();
@@ -150,7 +156,8 @@ public class AsmGeneratorTest {
new String[]{},
new String[] { /* include files */
"mock_android/data/data*"
- });
+ },
+ new MethodReplacer[] {} /* method replacers */);
agen.setAnalysisResult(aa.analyze());
Map<String, byte[]> output = agen.generate();
RecordingClassVisitor cv = new RecordingClassVisitor();
@@ -195,7 +202,8 @@ public class AsmGeneratorTest {
"**"
},
new String[]{},
- new String[] {});
+ new String[] {},
+ new MethodReplacer[] {});
agen.setAnalysisResult(aa.analyze());
Map<String, byte[]> output = agen.generate();
RecordingClassVisitor cv = new RecordingClassVisitor();
@@ -232,7 +240,8 @@ public class AsmGeneratorTest {
ci.getExcludedClasses(),
new String[] { /* include files */
"mock_android/data/data*"
- });
+ },
+ new MethodReplacer[] {});
agen.setAnalysisResult(aa.analyze());
Map<String, byte[]> output = agen.generate();
// Everything in .fake.** should be filtered
@@ -240,6 +249,7 @@ public class AsmGeneratorTest {
assertArrayEquals(new String[] {
"mock_android.fake2.keep.DoNotRemove",
"mock_android.util.EmptyArray",
+ "mock_android.view.LibLoader",
"mock_android.view.View",
"mock_android.view.ViewGroup",
"mock_android.view.ViewGroup$LayoutParams",
@@ -275,7 +285,8 @@ public class AsmGeneratorTest {
ci.getExcludedClasses(),
new String[] { /* include files */
"mock_android/data/data*"
- });
+ },
+ new MethodReplacer[] {});
agen.setAnalysisResult(aa.analyze());
JarUtil.createJar(new FileOutputStream(mOsDestJar), agen.generate());
@@ -304,6 +315,58 @@ public class AsmGeneratorTest {
assertEquals(classLoader, cl);
}
+ @Test
+ public void testMethodVisitor_loadLibraryReplacer() throws IOException {
+ final List<String> isNeeded = new ArrayList<>();
+ final List<String> replaced = new ArrayList<>();
+ MethodReplacer recordingReplacer = new MethodReplacer() {
+ private final MethodReplacer loadLibraryReplacer = new SystemLoadLibraryReplacer();
+ private final List<String> isNeededList = isNeeded;
+ private final List<String> replacedList = replaced;
+
+ @Override
+ public boolean isNeeded(String owner, String name, String desc, String sourceClass) {
+ boolean res = loadLibraryReplacer.isNeeded(owner, name, desc, sourceClass);
+ if (res) {
+ isNeededList.add(sourceClass + "->" + owner + "." + name);
+ }
+ return res;
+ }
+
+ @Override
+ public void replace(MethodInformation mi) {
+ replacedList.add(mi.owner + "." + mi.name);
+ }
+ };
+ MethodReplacer[] replacers = new MethodReplacer[] { recordingReplacer };
+
+ ICreateInfo ci = new CreateInfoAdapter() {
+ @Override
+ public MethodReplacer[] getMethodReplacers() {
+ return replacers;
+ }
+ };
+
+ AsmGenerator agen = new AsmGenerator(mLog, ci);
+ AsmAnalyzer aa = new AsmAnalyzer(mLog, mOsJarPath,
+ null, // derived from
+ new String[] { // include classes
+ "**"
+ },
+ new String[] {},
+ new String[] {},
+ replacers);
+ agen.setAnalysisResult(aa.analyze());
+
+ assertTrue(isNeeded.contains("mock_android/view/LibLoader->java/lang/System.loadLibrary"));
+ assertTrue(replaced.isEmpty());
+
+ agen.generate();
+
+ assertTrue(isNeeded.contains("mock_android/view/LibLoader->java/lang/System.loadLibrary"));
+ assertTrue(replaced.contains("java/lang/System.loadLibrary"));
+ }
+
private static byte[] getByteArray(InputStream stream) throws IOException {
ByteArrayOutputStream bos = new ByteArrayOutputStream();
byte[] buffer = new byte[1024];
diff --git a/create/tests/src/com/android/tools/layoutlib/create/CreateInfoAdapter.java b/create/tests/src/com/android/tools/layoutlib/create/CreateInfoAdapter.java
index 668b5788de..3b1cb06a1f 100644
--- a/create/tests/src/com/android/tools/layoutlib/create/CreateInfoAdapter.java
+++ b/create/tests/src/com/android/tools/layoutlib/create/CreateInfoAdapter.java
@@ -23,6 +23,11 @@ class CreateInfoAdapter implements ICreateInfo {
private static final String[] EMPTY_STRING_ARRAY = new String[0];
@Override
+ public MethodReplacer[] getMethodReplacers() {
+ return new MethodReplacer[0];
+ }
+
+ @Override
public Class<?>[] getInjectedClasses() {
return new Class<?>[0];
}
@@ -38,6 +43,21 @@ class CreateInfoAdapter implements ICreateInfo {
}
@Override
+ public String[] getDelegateClassNativesToNatives() {
+ return EMPTY_STRING_ARRAY;
+ }
+
+ @Override
+ public boolean shouldKeepAllNativeClasses() {
+ return false;
+ }
+
+ @Override
+ public String[] getKeepClassNatives() {
+ return EMPTY_STRING_ARRAY;
+ }
+
+ @Override
public String[] getRenamedClasses() {
return EMPTY_STRING_ARRAY;
}
@@ -68,6 +88,11 @@ class CreateInfoAdapter implements ICreateInfo {
}
@Override
+ public String[] getPromotedMethods() {
+ return EMPTY_STRING_ARRAY;
+ }
+
+ @Override
public String[] getPromotedClasses() {
return EMPTY_STRING_ARRAY;
}
@@ -76,4 +101,9 @@ class CreateInfoAdapter implements ICreateInfo {
public Map<String, InjectMethodRunnable> getInjectedMethodsMap() {
return Collections.emptyMap();
}
+
+ @Override
+ public String[] getDeferredStaticInitializerClasses() {
+ return EMPTY_STRING_ARRAY;
+ }
}
diff --git a/create/tests/src/com/android/tools/layoutlib/create/DelegateClassAdapterTest.java b/create/tests/src/com/android/tools/layoutlib/create/DelegateClassAdapterTest.java
index b7aa4e6f42..c6e4508743 100644
--- a/create/tests/src/com/android/tools/layoutlib/create/DelegateClassAdapterTest.java
+++ b/create/tests/src/com/android/tools/layoutlib/create/DelegateClassAdapterTest.java
@@ -20,6 +20,7 @@ package com.android.tools.layoutlib.create;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertNull;
import static org.junit.Assert.assertSame;
import static org.junit.Assert.assertTrue;
import static org.junit.Assert.fail;
@@ -57,6 +58,13 @@ public class DelegateClassAdapterTest {
private static final String OUTER_CLASS_NAME = OuterClass.class.getName();
private static final String INNER_CLASS_NAME = InnerClass.class.getName();
private static final String STATIC_INNER_CLASS_NAME = StaticInnerClass.class.getName();
+ // use a string to avoid triggering the static init
+ private static final String CLASS_WITH_STATIC_INIT_NAME =
+ "com.android.tools.layoutlib.create.dataclass.ClassWithStaticInit";
+ private static final String INNER_CLASS_WITH_STATIC_INIT_NAME =
+ "com.android.tools.layoutlib.create.dataclass.ClassWithStaticInit$InnerClass";
+ private static final String INNER_CLASS_WITH_STATIC_INIT_DELEGATE_NAME =
+ "com.android.tools.layoutlib.create.dataclass.ClassWithStaticInit_InnerClass_Delegate";
@Before
public void setUp() throws Exception {
@@ -237,6 +245,74 @@ public class DelegateClassAdapterTest {
}
@Test
+ public void testDelegateStaticInitializer() throws Throwable {
+ ClassWriter cw = new ClassWriter(0 /*flags*/);
+
+ String internalClassName = CLASS_WITH_STATIC_INIT_NAME.replace('.', '/');
+
+ HashSet<String> delegateMethods = new HashSet<>();
+ delegateMethods.add("<clinit>");
+ DelegateClassAdapter cv = new DelegateClassAdapter(
+ mLog, cw, internalClassName, delegateMethods);
+
+ ClassReader cr = new ClassReader(CLASS_WITH_STATIC_INIT_NAME);
+ cr.accept(cv, 0 /* flags */);
+
+ ClassLoader2 cl2 = null;
+ try {
+ cl2 = new ClassLoader2() {
+ @Override
+ public void testModifiedInstance() throws Exception {
+ Class<?> clazz2 = loadClass(CLASS_WITH_STATIC_INIT_NAME);
+
+ assertNull(clazz2.getField("sList").get(null));
+
+ Class<?> delegateClass = loadClass(CLASS_WITH_STATIC_INIT_NAME + "_Delegate");
+ assertNotNull( delegateClass.getField("sList").get(null));
+ }
+ };
+ cl2.add(CLASS_WITH_STATIC_INIT_NAME, cw);
+ cl2.testModifiedInstance();
+ } catch (Throwable t) {
+ throw dumpGeneratedClass(t, cl2);
+ }
+ }
+
+ @Test
+ public void testDelegateInnerClassStaticInitializer() throws Throwable {
+ ClassWriter cw = new ClassWriter(0 /*flags*/);
+
+ String internalClassName = INNER_CLASS_WITH_STATIC_INIT_NAME.replace('.', '/');
+
+ HashSet<String> delegateMethods = new HashSet<>();
+ delegateMethods.add("<clinit>");
+ DelegateClassAdapter cv = new DelegateClassAdapter(
+ mLog, cw, internalClassName, delegateMethods);
+
+ ClassReader cr = new ClassReader(INNER_CLASS_WITH_STATIC_INIT_NAME);
+ cr.accept(cv, 0 /* flags */);
+
+ ClassLoader2 cl2 = null;
+ try {
+ cl2 = new ClassLoader2() {
+ @Override
+ public void testModifiedInstance() throws Exception {
+ Class<?> clazz2 = loadClass(INNER_CLASS_WITH_STATIC_INIT_NAME);
+
+ assertNull(clazz2.getField("sInnerList").get(null));
+
+ Class<?> delegateClass = loadClass(INNER_CLASS_WITH_STATIC_INIT_DELEGATE_NAME);
+ assertNotNull( delegateClass.getField("sList").get(null));
+ }
+ };
+ cl2.add(INNER_CLASS_WITH_STATIC_INIT_NAME, cw);
+ cl2.testModifiedInstance();
+ } catch (Throwable t) {
+ throw dumpGeneratedClass(t, cl2);
+ }
+ }
+
+ @Test
public void testDelegateNative() throws Throwable {
ClassWriter cw = new ClassWriter(0 /*flags*/);
String internalClassName = NATIVE_CLASS_NAME.replace('.', '/');
diff --git a/create/tests/src/com/android/tools/layoutlib/create/PromoteClassClassAdapterTest.java b/create/tests/src/com/android/tools/layoutlib/create/PromoteClassClassAdapterTest.java
index 928ac4d7e8..2e6ccd36c7 100644
--- a/create/tests/src/com/android/tools/layoutlib/create/PromoteClassClassAdapterTest.java
+++ b/create/tests/src/com/android/tools/layoutlib/create/PromoteClassClassAdapterTest.java
@@ -162,7 +162,7 @@ public class PromoteClassClassAdapterTest {
});
reader.accept(adapter, 0);
- assertTrue(log.mLog.contains("[visit] - version=52, access=[public], " +
+ assertTrue(log.mLog.contains("[visit] - version=55, access=[public], " +
"name=com/android/tools/layoutlib/create/PackageProtectedClass, signature=null, " +
"superName=java/lang/Object, interfaces=[]"));
diff --git a/create/tests/src/com/android/tools/layoutlib/create/dataclass/ClassWithStaticInit.java b/create/tests/src/com/android/tools/layoutlib/create/dataclass/ClassWithStaticInit.java
new file mode 100644
index 0000000000..f5a78a941a
--- /dev/null
+++ b/create/tests/src/com/android/tools/layoutlib/create/dataclass/ClassWithStaticInit.java
@@ -0,0 +1,28 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.tools.layoutlib.create.dataclass;
+
+import java.util.ArrayList;
+import java.util.List;
+
+public class ClassWithStaticInit {
+
+ public static List<String> sList = new ArrayList<>();
+
+ public static class InnerClass {
+ public static List<String> sInnerList = new ArrayList<>();
+ }
+}
diff --git a/create/tests/src/com/android/tools/layoutlib/create/dataclass/ClassWithStaticInit_Delegate.java b/create/tests/src/com/android/tools/layoutlib/create/dataclass/ClassWithStaticInit_Delegate.java
new file mode 100644
index 0000000000..3bc5491bca
--- /dev/null
+++ b/create/tests/src/com/android/tools/layoutlib/create/dataclass/ClassWithStaticInit_Delegate.java
@@ -0,0 +1,35 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.tools.layoutlib.create.dataclass;
+
+import com.android.tools.layoutlib.create.DelegateClassAdapterTest;
+
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * The delegate that receives the call to {@link ClassWithStaticInit}'s overridden methods.
+ *
+ * Used by {@link DelegateClassAdapterTest}.
+ */
+public class ClassWithStaticInit_Delegate {
+
+ public static List<String> sList = null;
+
+ public static void staticInit() {
+ sList = new ArrayList<>();
+ }
+}
diff --git a/create/tests/src/com/android/tools/layoutlib/create/dataclass/ClassWithStaticInit_InnerClass_Delegate.java b/create/tests/src/com/android/tools/layoutlib/create/dataclass/ClassWithStaticInit_InnerClass_Delegate.java
new file mode 100644
index 0000000000..faa295638f
--- /dev/null
+++ b/create/tests/src/com/android/tools/layoutlib/create/dataclass/ClassWithStaticInit_InnerClass_Delegate.java
@@ -0,0 +1,36 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.tools.layoutlib.create.dataclass;
+
+import com.android.tools.layoutlib.create.DelegateClassAdapterTest;
+
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * The delegate that receives the call to {@link ClassWithStaticInit$InnerClass}'s overridden
+ * methods.
+ *
+ * Used by {@link DelegateClassAdapterTest}.
+ */
+public class ClassWithStaticInit_InnerClass_Delegate {
+
+ public static List<String> sList = null;
+
+ public static void staticInit() {
+ sList = new ArrayList<>();
+ }
+}
diff --git a/delegates/Android.bp b/delegates/Android.bp
new file mode 100644
index 0000000000..93bcd6ddb4
--- /dev/null
+++ b/delegates/Android.bp
@@ -0,0 +1,31 @@
+//
+// Copyright (C) 2008 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+//
+
+package {
+ default_applicable_licenses: ["Android-Apache-2.0"],
+}
+
+java_library_host {
+ name: "layoutlib-common-delegates",
+
+ srcs: ["src/**/*.java"],
+
+ libs: [
+ "temp_layoutlib",
+ "layoutlib-common",
+ ],
+
+}
diff --git a/delegates/README b/delegates/README
new file mode 100644
index 0000000000..0ef28c61fb
--- /dev/null
+++ b/delegates/README
@@ -0,0 +1 @@
+The common set of delegates shared between layoutlib and simulated_device \ No newline at end of file
diff --git a/delegates/delegates.iml b/delegates/delegates.iml
new file mode 100644
index 0000000000..04f8ae4b77
--- /dev/null
+++ b/delegates/delegates.iml
@@ -0,0 +1,14 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<module type="JAVA_MODULE" version="4">
+ <component name="NewModuleRootManager" inherit-compiler-output="true">
+ <exclude-output />
+ <content url="file://$MODULE_DIR$">
+ <sourceFolder url="file://$MODULE_DIR$/src" isTestSource="false" />
+ <sourceFolder url="file://$MODULE_DIR$/tests/src" isTestSource="true" />
+ </content>
+ <orderEntry type="inheritedJdk" />
+ <orderEntry type="sourceFolder" forTests="false" />
+ <orderEntry type="library" name="framework.jar" level="project" />
+ <orderEntry type="module" module-name="common" />
+ </component>
+</module> \ No newline at end of file
diff --git a/delegates/src/android/graphics/fonts/SystemFonts_Delegate.java b/delegates/src/android/graphics/fonts/SystemFonts_Delegate.java
new file mode 100644
index 0000000000..ff2d64c708
--- /dev/null
+++ b/delegates/src/android/graphics/fonts/SystemFonts_Delegate.java
@@ -0,0 +1,94 @@
+/*
+ * 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.
+ */
+
+package android.graphics.fonts;
+
+import com.android.tools.layoutlib.annotations.LayoutlibDelegate;
+
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.text.FontConfig;
+import android.util.Log;
+
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.IOException;
+import java.nio.ByteBuffer;
+import java.nio.channels.FileChannel;
+import java.util.Locale;
+import java.util.Map;
+
+/**
+ * Delegate implementing the native methods of android.graphics.fonts.SystemFonts
+ * <p>
+ * Through the layoutlib_create tool, the original native methods of SystemFonts have been
+ * replaced by calls to methods of the same name in this delegate class.
+ * <p>
+ * This class behaves like the original native implementation, but in Java, keeping previously
+ * native data into its own objects and mapping them to int that are sent back and forth between it
+ * and the original SystemFonts class.
+ *
+ */
+public class SystemFonts_Delegate {
+
+ private static final String TAG = "SystemFonts_Delegate";
+ private static String sFontLocation;
+ public static boolean sIsTypefaceInitialized = false;
+
+ public static void setFontLocation(String fontLocation) {
+ sFontLocation = fontLocation;
+ }
+
+ @LayoutlibDelegate
+ /*package*/ static FontConfig getSystemFontConfigInternal(
+ String fontsXml,
+ String systemFontDir,
+ String oemXml,
+ String productFontDir,
+ Map<String, File> updatableFontMap,
+ long lastModifiedDate,
+ int configVersion) {
+ sIsTypefaceInitialized = true;
+ return SystemFonts.getSystemFontConfigInternal_Original(
+ sFontLocation + "fonts.xml", sFontLocation, null, null, updatableFontMap,
+ lastModifiedDate, configVersion);
+ }
+
+ @LayoutlibDelegate
+ /*package*/ static ByteBuffer mmap(@NonNull String fullPath) {
+ // Android does memory mapping for font files. But Windows keeps files open
+ // until the byte buffer from the memory mapping is garbage collected.
+ // To avoid that, on Windows, read the file into a byte buffer instead.
+ // See JDK-4715154.
+ String osName = System.getProperty("os.name").toLowerCase(Locale.US);
+ if (osName.startsWith("windows")) {
+ try (FileInputStream file = new FileInputStream(fullPath)) {
+ final FileChannel fileChannel = file.getChannel();
+ final int size = (int) fileChannel.size();
+ // Native code requires the ByteBuffer to be direct
+ // (see android/graphics/fonts/Font.cpp)
+ ByteBuffer buffer = ByteBuffer.allocateDirect(size);
+ fileChannel.read(buffer);
+ return buffer;
+ } catch (IOException e) {
+ Log.e(TAG, "Error mapping font file " + fullPath);
+ return null;
+ }
+ } else {
+ return SystemFonts.mmap_Original(fullPath);
+ }
+ }
+} \ No newline at end of file
diff --git a/delegates/src/android/media/ImageReader_Delegate.java b/delegates/src/android/media/ImageReader_Delegate.java
new file mode 100644
index 0000000000..0ff1ce7303
--- /dev/null
+++ b/delegates/src/android/media/ImageReader_Delegate.java
@@ -0,0 +1,24 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.media;
+
+public class ImageReader_Delegate {
+
+ static void nativeClassInit() {
+ // Call ImageReader.nativeClassInit(); in layoutlib implicitly before using ImageReader
+ }
+}
diff --git a/delegates/src/com/android/tools/layoutlib/java/nio/Buffer_Delegate.java b/delegates/src/com/android/tools/layoutlib/java/nio/Buffer_Delegate.java
new file mode 100644
index 0000000000..351d9b4e15
--- /dev/null
+++ b/delegates/src/com/android/tools/layoutlib/java/nio/Buffer_Delegate.java
@@ -0,0 +1,52 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.tools.layoutlib.java.nio;
+
+import java.nio.Buffer;
+import java.nio.ByteBuffer;
+import java.nio.CharBuffer;
+import java.nio.DoubleBuffer;
+import java.nio.FloatBuffer;
+import java.nio.IntBuffer;
+import java.nio.LongBuffer;
+import java.nio.ShortBuffer;
+
+/**
+ * Delegate to fix differences between the Android and the JVM versions of java.nio.Buffer
+ */
+public class Buffer_Delegate {
+ /**
+ * The Android version of java.nio.Buffer has an extra final field called _elementSizeShift
+ * that only depend on the implementation of the buffer. This method can be called instead
+ * when wanting to access the value of that field on the JVM.
+ */
+ public static int elementSizeShift(Buffer buffer) {
+ if (buffer instanceof ByteBuffer) {
+ return 0;
+ }
+ if (buffer instanceof ShortBuffer || buffer instanceof CharBuffer) {
+ return 1;
+ }
+ if (buffer instanceof IntBuffer || buffer instanceof FloatBuffer) {
+ return 2;
+ }
+ if (buffer instanceof LongBuffer || buffer instanceof DoubleBuffer) {
+ return 3;
+ }
+ return 0;
+ }
+}
diff --git a/delegates/src/com/android/tools/layoutlib/java/nio/NIOAccess_Delegate.java b/delegates/src/com/android/tools/layoutlib/java/nio/NIOAccess_Delegate.java
new file mode 100644
index 0000000000..addcc0bd35
--- /dev/null
+++ b/delegates/src/com/android/tools/layoutlib/java/nio/NIOAccess_Delegate.java
@@ -0,0 +1,73 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.tools.layoutlib.java.nio;
+
+import com.android.layoutlib.common.util.ReflectionUtils;
+import com.android.layoutlib.common.util.ReflectionUtils.ReflectionException;
+
+import java.nio.Buffer;
+
+/**
+ * A fork of libcore's java.nio.NIOAccess which does not exist in the JVM
+ *
+ * This class is used via JNI by code in frameworks/base/.
+ * @hide
+ */
+// @VisibleForTesting : was default
+public final class NIOAccess_Delegate {
+
+ /**
+ * Returns the underlying native pointer to the data of the given
+ * Buffer starting at the Buffer's current position, or 0 if the
+ * Buffer is not backed by native heap storage.
+ * @hide
+ */
+ // @VisibleForTesting : was default
+ public static long getBasePointer(Buffer b) {
+ try {
+ long address = (long)ReflectionUtils.getFieldValue(Buffer.class, b, "address");
+ if (address == 0L || !b.isDirect()) {
+ return 0L;
+ }
+ return address + ((long)b.position() << Buffer_Delegate.elementSizeShift(b));
+ } catch (ReflectionException e) {
+ return 0L;
+ }
+ }
+
+ /**
+ * Returns the underlying Java array containing the data of the
+ * given Buffer, or null if the Buffer is not backed by a Java array.
+ */
+ static Object getBaseArray(Buffer b) {
+ return b.hasArray() ? b.array() : null;
+ }
+
+ /**
+ * Returns the offset in bytes from the start of the underlying
+ * Java array object containing the data of the given Buffer to
+ * the actual start of the data. The start of the data takes into
+ * account the Buffer's current position. This method is only
+ * meaningful if getBaseArray() returns non-null.
+ */
+ static int getBaseArrayOffset(Buffer b) {
+ return b.hasArray() ?
+ ((b.arrayOffset() + b.position()) << Buffer_Delegate.elementSizeShift(b)) : 0;
+ }
+
+
+}
diff --git a/delegates/src/com/android/tools/layoutlib/java/text/DateFormat_Delegate.java b/delegates/src/com/android/tools/layoutlib/java/text/DateFormat_Delegate.java
new file mode 100644
index 0000000000..cb72c2d060
--- /dev/null
+++ b/delegates/src/com/android/tools/layoutlib/java/text/DateFormat_Delegate.java
@@ -0,0 +1,31 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.tools.layoutlib.java.text;
+
+import java.text.DateFormat;
+
+/**
+ * Provides alternate implementation to java.text.DateFormat.set24HourTimePref, which is present
+ * as a
+ * non-public method in the Android VM, but not present on the host VM. This is injected in the
+ * layoutlib using {@link ReplaceMethodCallsAdapter}.
+ */
+public class DateFormat_Delegate {
+
+ public static final void set24HourTimePref(Boolean is24Hour) {
+ // ignore
+ }
+}
diff --git a/delegates/src/com/android/tools/layoutlib/java/util/LocaleAdjustLanguageCodeReplacement.java b/delegates/src/com/android/tools/layoutlib/java/util/LocaleAdjustLanguageCodeReplacement.java
new file mode 100644
index 0000000000..b335e86ef4
--- /dev/null
+++ b/delegates/src/com/android/tools/layoutlib/java/util/LocaleAdjustLanguageCodeReplacement.java
@@ -0,0 +1,43 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.tools.layoutlib.java.util;
+
+import java.util.Locale;
+
+/**
+ * This class provides an alternate implementation for {@code java.util.Locale#adjustLanguageCode}
+ * which is not available in openJDK.
+ *
+ * The create tool re-writes references to the above mentioned method to this one. Hence it's
+ * imperative that this class is not deleted unless the create tool is modified.
+ */
+public class LocaleAdjustLanguageCodeReplacement {
+
+ public static String adjustLanguageCode(String languageCode) {
+ String adjusted = languageCode.toLowerCase(Locale.US);
+ // Map new language codes to the obsolete language
+ // codes so the correct resource bundles will be used.
+ if (languageCode.equals("he")) {
+ adjusted = "iw";
+ } else if (languageCode.equals("id")) {
+ adjusted = "in";
+ } else if (languageCode.equals("yi")) {
+ adjusted = "ji";
+ }
+
+ return adjusted;
+ }
+}
diff --git a/delegates/src/com/android/tools/layoutlib/java/util/zip/ZipEntry_Delegate.java b/delegates/src/com/android/tools/layoutlib/java/util/zip/ZipEntry_Delegate.java
new file mode 100644
index 0000000000..4475fffe8f
--- /dev/null
+++ b/delegates/src/com/android/tools/layoutlib/java/util/zip/ZipEntry_Delegate.java
@@ -0,0 +1,49 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.tools.layoutlib.java.util.zip;
+
+import java.util.zip.ZipEntry;
+
+/**
+ * Wrapper for calls to Android-added API to ZipEntry
+ */
+public class ZipEntry_Delegate extends ZipEntry {
+
+ private final long mDataOffset;
+
+ // Called from StrictJarFile native code.
+ public ZipEntry_Delegate(String name, String comment, long crc, long compressedSize, long size,
+ int compressionMethod, int xdostime, byte[] extra, long dataOffset) {
+ super(name);
+ setComment(comment);
+ setCrc(crc);
+ setCompressedSize(compressedSize);
+ setSize(size);
+ setMethod(compressionMethod);
+ setTime(xdostime);
+ setExtra(extra);
+ mDataOffset = dataOffset;
+ }
+
+ /**
+ * Handle calls to the Android-added ZipEntry#getDataOffset.
+ *
+ * Called from StrictJarFile java code.
+ */
+ public static long getDataOffset(ZipEntry original) {
+ return ((ZipEntry_Delegate) original).mDataOffset;
+ }
+}
diff --git a/delegates/src/dalvik/system/VMRuntimeCommonHelper.java b/delegates/src/dalvik/system/VMRuntimeCommonHelper.java
new file mode 100644
index 0000000000..ae63625e16
--- /dev/null
+++ b/delegates/src/dalvik/system/VMRuntimeCommonHelper.java
@@ -0,0 +1,78 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package dalvik.system;
+
+/**
+ * Common VMRuntime Delegate code used by both layoutlib and simulated_device.
+ */
+class VMRuntimeCommonHelper {
+
+ // Copied from libcore/libdvm/src/main/java/dalvik/system/VMRuntime
+ /*package*/ static Object newUnpaddedArray(VMRuntime runtime, Class<?> componentType,
+ int minLength) {
+ // Dalvik has 32bit pointers, the array header is 16bytes plus 4bytes for dlmalloc,
+ // allocations are 8byte aligned so having 4bytes of array data avoids padding.
+ if (!componentType.isPrimitive()) {
+ int size = ((minLength & 1) == 0) ? minLength + 1 : minLength;
+ return java.lang.reflect.Array.newInstance(componentType, size);
+ } else if (componentType == char.class) {
+ int bytes = 20 + (2 * minLength);
+ int alignedUpBytes = (bytes + 7) & -8;
+ int dataBytes = alignedUpBytes - 20;
+ int size = dataBytes / 2;
+ return new char[size];
+ } else if (componentType == int.class) {
+ int size = ((minLength & 1) == 0) ? minLength + 1 : minLength;
+ return new int[size];
+ } else if (componentType == byte.class) {
+ int bytes = 20 + minLength;
+ int alignedUpBytes = (bytes + 7) & -8;
+ int dataBytes = alignedUpBytes - 20;
+ int size = dataBytes;
+ return new byte[size];
+ } else if (componentType == boolean.class) {
+ int bytes = 20 + minLength;
+ int alignedUpBytes = (bytes + 7) & -8;
+ int dataBytes = alignedUpBytes - 20;
+ int size = dataBytes;
+ return new boolean[size];
+ } else if (componentType == short.class) {
+ int bytes = 20 + (2 * minLength);
+ int alignedUpBytes = (bytes + 7) & -8;
+ int dataBytes = alignedUpBytes - 20;
+ int size = dataBytes / 2;
+ return new short[size];
+ } else if (componentType == float.class) {
+ int size = ((minLength & 1) == 0) ? minLength + 1 : minLength;
+ return new float[size];
+ } else if (componentType == long.class) {
+ return new long[minLength];
+ } else if (componentType == double.class) {
+ return new double[minLength];
+ } else {
+ assert componentType == void.class;
+ throw new IllegalArgumentException("Can't allocate an array of void");
+ }
+ }
+
+
+ /*package*/ static int getNotifyNativeInterval() {
+ // This cannot return 0, otherwise it is responsible for triggering an exception
+ // whenever trying to use a NativeAllocationRegistry with size 0
+ return 128; // see art/runtime/gc/heap.h -> kNotifyNativeInterval
+ }
+}
diff --git a/remote/client/src/com/android/layoutlib/bridge/remote/client/adapters/RemoteLayoutlibCallbackAdapter.java b/remote/client/src/com/android/layoutlib/bridge/remote/client/adapters/RemoteLayoutlibCallbackAdapter.java
index 01efd1224d..06fa6b26bf 100644
--- a/remote/client/src/com/android/layoutlib/bridge/remote/client/adapters/RemoteLayoutlibCallbackAdapter.java
+++ b/remote/client/src/com/android/layoutlib/bridge/remote/client/adapters/RemoteLayoutlibCallbackAdapter.java
@@ -102,6 +102,11 @@ public class RemoteLayoutlibCallbackAdapter implements RemoteLayoutlibCallback {
return mDelegate.getFlag(key);
}
+ @Override
+ public String getResourcePackage() throws RemoteException {
+ return mDelegate.getResourcePackage();
+ }
+
@Nullable
@Override
public Path findClassPath(String name) {
diff --git a/remote/common/src/com/android/layout/remote/api/RemoteLayoutLog.java b/remote/common/src/com/android/layout/remote/api/RemoteLayoutLog.java
index 335cf9dbe6..8e09ece5c4 100644
--- a/remote/common/src/com/android/layout/remote/api/RemoteLayoutLog.java
+++ b/remote/common/src/com/android/layout/remote/api/RemoteLayoutLog.java
@@ -73,10 +73,10 @@ public interface RemoteLayoutLog extends Remote {
void error(String tag, String message, Throwable throwable, Object viewCookie, Serializable data)
throws RemoteException;
- /**
- * Logs messages coming from the Android Framework.
+ /**
+ * Logs messages coming from the Android Framework.
*
- * @param priority the priority level of the message
+ * @param priority the priority level of the message
* @param tag a tag describing the type of the error
* @param message the message of the error
*/
diff --git a/remote/common/src/com/android/layout/remote/api/RemoteLayoutlibCallback.java b/remote/common/src/com/android/layout/remote/api/RemoteLayoutlibCallback.java
index d68934ae65..ccf1feeb4e 100644
--- a/remote/common/src/com/android/layout/remote/api/RemoteLayoutlibCallback.java
+++ b/remote/common/src/com/android/layout/remote/api/RemoteLayoutlibCallback.java
@@ -52,6 +52,8 @@ public interface RemoteLayoutlibCallback extends Remote {
<T> T getFlag(Key<T> key) throws RemoteException;
+ String getResourcePackage() throws RemoteException;
+
Path findClassPath(String name) throws RemoteException;
RemoteXmlPullParser createXmlParserForPsiFile(String fileName) throws RemoteException;
diff --git a/remote/server/src/com/android/layoutlib/bridge/remote/server/adapters/RemoteLayoutlibCallbackAdapter.java b/remote/server/src/com/android/layoutlib/bridge/remote/server/adapters/RemoteLayoutlibCallbackAdapter.java
index 719de4dcd3..cba8a472ef 100644
--- a/remote/server/src/com/android/layoutlib/bridge/remote/server/adapters/RemoteLayoutlibCallbackAdapter.java
+++ b/remote/server/src/com/android/layoutlib/bridge/remote/server/adapters/RemoteLayoutlibCallbackAdapter.java
@@ -218,6 +218,15 @@ public class RemoteLayoutlibCallbackAdapter extends LayoutlibCallback {
}
@Override
+ public String getResourcePackage() {
+ try {
+ return mDelegate.getResourcePackage();
+ } catch (RemoteException e) {
+ throw new RuntimeException(e);
+ }
+ }
+
+ @Override
public Class<?> findClass(String name) throws ClassNotFoundException {
return mPathClassLoader.loadClass(name);
}
diff --git a/rename_font/README b/rename_font/README
deleted file mode 100644
index 600b756161..0000000000
--- a/rename_font/README
+++ /dev/null
@@ -1,9 +0,0 @@
-This tool is used to rename the PS name encoded inside the ttf font that we ship
-with the SDK. There is bug in Java that returns incorrect results for
-java.awt.Font#layoutGlyphVector() if two fonts with same name but differnt
-versions are loaded. As a workaround, we rename all the fonts that we ship with
-the SDK by appending the font version to its name.
-
-
-The build_font.py copies all files from input_dir to output_dir while renaming
-the font files (*.ttf) in the process.
diff --git a/rename_font/Roboto-Regular.ttf b/rename_font/Roboto-Regular.ttf
deleted file mode 100644
index 746906381b..0000000000
--- a/rename_font/Roboto-Regular.ttf
+++ /dev/null
Binary files differ
diff --git a/rename_font/build_font.py b/rename_font/build_font.py
deleted file mode 100755
index db0c98a3fe..0000000000
--- a/rename_font/build_font.py
+++ /dev/null
@@ -1,227 +0,0 @@
-#!/usr/bin/env python3
-
-# Copyright (C) 2014 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.
-
-"""
-Rename the PS name of all fonts in the input directories and copy them to the
-output directory.
-
-Usage: build_font.py /path/to/input_fonts1/ /path/to/input_fonts2/ /path/to/output_fonts/
-
-"""
-
-import glob
-from multiprocessing import Pool
-import os
-import re
-import shutil
-import sys
-import xml.etree.ElementTree as etree
-
-# Prevent .pyc files from being created.
-sys.dont_write_bytecode = True
-
-# fontTools is available at platform/external/fonttools
-from fontTools import ttx
-
-# global variable
-dest_dir = '/tmp'
-
-
-class FontInfo(object):
- family = None
- style = None
- version = None
- ends_in_regular = False
- fullname = None
-
-
-class InvalidFontException(Exception):
- pass
-
-
-# These constants represent the value of nameID parameter in the namerecord for
-# different information.
-# see http://scripts.sil.org/cms/scripts/page.php?item_id=IWS-Chapter08#3054f18b
-NAMEID_FAMILY = 1
-NAMEID_STYLE = 2
-NAMEID_FULLNAME = 4
-NAMEID_VERSION = 5
-
-
-def main(argv):
- if len(argv) < 2:
- sys.exit('Usage: build_font.py /path/to/input_fonts/ /path/to/out/dir/')
- for directory in argv:
- if not os.path.isdir(directory):
- sys.exit(directory + ' is not a valid directory')
- global dest_dir
- dest_dir = argv[-1]
- src_dirs = argv[:-1]
- cwd = os.getcwd()
- os.chdir(dest_dir)
- files = glob.glob('*')
- for filename in files:
- os.remove(filename)
- os.chdir(cwd)
- input_fonts = list()
- for src_dir in src_dirs:
- for dirname, dirnames, filenames in os.walk(src_dir):
- for filename in filenames:
- input_path = os.path.join(dirname, filename)
- extension = os.path.splitext(filename)[1].lower()
- if extension == '.ttf':
- input_fonts.append(input_path)
- elif extension == '.xml':
- shutil.copy(input_path, dest_dir)
- if '.git' in dirnames:
- # don't go into any .git directories.
- dirnames.remove('.git')
- # Create as many threads as the number of CPUs
- pool = Pool(processes=None)
- pool.map(convert_font, input_fonts)
-
-
-def convert_font(input_path):
- filename = os.path.basename(input_path)
- print('Converting font: ' + filename)
- # the path to the output file. The file name is the fontfilename.ttx
- ttx_path = os.path.join(dest_dir, filename)
- ttx_path = ttx_path[:-1] + 'x'
- try:
- # run ttx to generate an xml file in the output folder which represents all
- # its info
- ttx_args = ['--no-recalc-timestamp', '-q', '-d', dest_dir, input_path]
- ttx.main(ttx_args)
- # now parse the xml file to change its PS name.
- tree = etree.parse(ttx_path)
- root = tree.getroot()
- for name in root.iter('name'):
- update_tag(name, get_font_info(name))
- tree.write(ttx_path, xml_declaration=True, encoding='utf-8')
- # generate the udpated font now.
- ttx_args = ['-q', '-d', dest_dir, ttx_path]
- ttx.main(ttx_args)
- except InvalidFontException:
- # In case of invalid fonts, we exit.
- print(filename + ' is not a valid font')
- raise
- except Exception as e:
- print('Error converting font: ' + filename)
- print(e)
- # Some fonts are too big to be handled by the ttx library.
- # Just copy paste them.
- shutil.copy(input_path, dest_dir)
- try:
- # delete the temp ttx file is it exists.
- os.remove(ttx_path)
- except OSError:
- pass
-
-
-def get_font_info(tag):
- """ Returns a list of FontInfo representing the various sets of namerecords
- found in the name table of the font. """
- fonts = []
- font = None
- last_name_id = sys.maxsize
- for namerecord in tag.iter('namerecord'):
- if 'nameID' in namerecord.attrib:
- name_id = int(namerecord.attrib['nameID'])
- # A new font should be created for each platform, encoding and language
- # id. But, since the nameIDs are sorted, we use the easy approach of
- # creating a new one when the nameIDs reset.
- if name_id <= last_name_id and font is not None:
- fonts.append(font)
- font = None
- last_name_id = name_id
- if font is None:
- font = FontInfo()
- if name_id == NAMEID_FAMILY:
- font.family = namerecord.text.strip()
- if name_id == NAMEID_STYLE:
- font.style = namerecord.text.strip()
- if name_id == NAMEID_FULLNAME:
- font.ends_in_regular = ends_in_regular(namerecord.text)
- font.fullname = namerecord.text.strip()
- if name_id == NAMEID_VERSION:
- font.version = get_version(namerecord.text)
- if font is not None:
- fonts.append(font)
- return fonts
-
-
-def update_tag(tag, fonts):
- last_name_id = sys.maxsize
- fonts_iterator = fonts.__iter__()
- font = None
- for namerecord in tag.iter('namerecord'):
- if 'nameID' in namerecord.attrib:
- name_id = int(namerecord.attrib['nameID'])
- if name_id <= last_name_id:
- font = next(fonts_iterator)
- font = update_font_name(font)
- last_name_id = name_id
- if name_id == NAMEID_FAMILY:
- namerecord.text = font.family
- if name_id == NAMEID_FULLNAME:
- namerecord.text = font.fullname
-
-
-def update_font_name(font):
- """ Compute the new font family name and font fullname. If the font has a
- valid version, it's sanitized and appended to the font family name. The
- font fullname is then created by joining the new family name and the
- style. If the style is 'Regular', it is appended only if the original font
- had it. """
- if font.family is None or font.style is None:
- raise InvalidFontException('Font doesn\'t have proper family name or style')
- if font.version is not None:
- new_family = font.family + font.version
- else:
- new_family = font.family
- if font.style == 'Regular' and not font.ends_in_regular:
- font.fullname = new_family
- else:
- font.fullname = new_family + ' ' + font.style
- font.family = new_family
- return font
-
-
-def ends_in_regular(string):
- """ According to the specification, the font fullname should not end in
- 'Regular' for plain fonts. However, some fonts don't obey this rule. We
- keep the style info, to minimize the diff. """
- string = string.strip().split()[-1]
- return string == 'Regular'
-
-
-def get_version(string):
- string = string.strip()
- # The spec says that the version string should start with "Version ". But not
- # all fonts do. So, we return the complete string if it doesn't start with
- # the prefix, else we return the rest of the string after sanitizing it.
- prefix = 'Version '
- if string.startswith(prefix):
- string = string[len(prefix):]
- return sanitize(string)
-
-
-def sanitize(string):
- """ Remove non-standard chars. """
- return re.sub(r'[^\w-]+', '', string)
-
-if __name__ == '__main__':
- main(sys.argv[1:])
diff --git a/rename_font/build_font_single.py b/rename_font/build_font_single.py
deleted file mode 100755
index b25407241f..0000000000
--- a/rename_font/build_font_single.py
+++ /dev/null
@@ -1,221 +0,0 @@
-#!/usr/bin/env python3
-
-# Copyright (C) 2014 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.
-
-"""
-Rename the PS name of the input font.
-
-OpenType fonts (*.otf) and TrueType Collections (*.ttc) are not currently supported. They are copied to the destination
-without renaming. XML files are also copied in case they are passed there by mistake.
-
-Usage: build_font_single.py /path/to/input_font.ttf /path/to/output_font.ttf
-
-"""
-
-import glob
-import os
-import re
-import shutil
-import sys
-import xml.etree.ElementTree as etree
-
-# Prevent .pyc files from being created.
-sys.dont_write_bytecode = True
-
-# fontTools is available at platform/external/fonttools
-from fontTools import ttx
-
-
-class FontInfo(object):
- family = None
- style = None
- version = None
- ends_in_regular = False
- fullname = None
-
-
-class InvalidFontException(Exception):
- pass
-
-
-# A constant to copy the font without modifying. This is useful when running
-# locally and speed up the time to build the SDK.
-COPY_ONLY = False
-
-# These constants represent the value of nameID parameter in the namerecord for
-# different information.
-# see http://scripts.sil.org/cms/scripts/page.php?item_id=IWS-Chapter08#3054f18b
-NAMEID_FAMILY = 1
-NAMEID_STYLE = 2
-NAMEID_FULLNAME = 4
-NAMEID_VERSION = 5
-
-NAMEID_LIST = [NAMEID_FAMILY, NAMEID_STYLE, NAMEID_FULLNAME, NAMEID_VERSION]
-NAMEID_LIST_MIN = min(NAMEID_LIST)
-NAMEID_LIST_MAX = max(NAMEID_LIST)
-
-# A list of extensions to process.
-EXTENSIONS = ['.ttf', '.ttc', '.otf', '.xml']
-
-def main(argv):
- if len(argv) < 2:
- print('Incorrect usage: ' + str(argv))
- sys.exit('Usage: build_font_single.py /path/to/input/font.ttf /path/to/out/font.ttf')
- dest_path = argv[-1]
- input_path = argv[0]
- extension = os.path.splitext(input_path)[1].lower()
- if extension in EXTENSIONS:
- if not COPY_ONLY and extension == '.ttf':
- convert_font(input_path, dest_path)
- return
- shutil.copy(input_path, dest_path)
-
-
-def convert_font(input_path, dest_path):
- filename = os.path.basename(input_path)
- print('Converting font: ' + filename)
- # the path to the output file. The file name is the fontfilename.ttx
- ttx_path = dest_path[:-1] + 'x'
- try:
- # run ttx to generate an xml file in the output folder which represents all
- # its info
- ttx_args = ['--no-recalc-timestamp', '-q', '-o', ttx_path, input_path]
- ttx.main(ttx_args)
- # now parse the xml file to change its PS name.
- tree = etree.parse(ttx_path)
- root = tree.getroot()
- for name in root.iter('name'):
- update_tag(name, get_font_info(name))
- tree.write(ttx_path, xml_declaration=True, encoding='utf-8')
- # generate the udpated font now.
- ttx_args = ['-q', '-o', dest_path, ttx_path]
- ttx.main(ttx_args)
- except InvalidFontException:
- # assume, like for .ttc and .otf that font might be valid, but warn.
- print('Family and/or Style nameIDs not found in '+ filename)
- shutil.copy(input_path, dest_path)
- except Exception as e:
- print('Error converting font: ' + filename)
- print(e)
- # Some fonts are too big to be handled by the ttx library.
- # Just copy paste them.
- shutil.copy(input_path, dest_path)
- try:
- # delete the temp ttx file is it exists.
- os.remove(ttx_path)
- except OSError:
- pass
-
-
-def get_font_info(tag):
- """ Returns a list of FontInfo representing the various sets of namerecords
- found in the name table of the font. """
- fonts = []
- font = None
- last_name_id = sys.maxsize
- for namerecord in tag.iter('namerecord'):
- if 'nameID' in namerecord.attrib:
- name_id = int(namerecord.attrib['nameID'])
- # skip irrelevant records
- if name_id < NAMEID_LIST_MIN or name_id > NAMEID_LIST_MAX:
- continue
- # A new font should be created for each platform, encoding and language
- # id. But, since the nameIDs are sorted, we use the easy approach of
- # creating a new one when the nameIDs reset.
- if name_id <= last_name_id and font is not None:
- fonts.append(font)
- font = None
- last_name_id = name_id
- if font is None:
- font = FontInfo()
- if name_id == NAMEID_FAMILY:
- font.family = namerecord.text.strip()
- if name_id == NAMEID_STYLE:
- font.style = namerecord.text.strip()
- if name_id == NAMEID_FULLNAME:
- font.ends_in_regular = ends_in_regular(namerecord.text)
- font.fullname = namerecord.text.strip()
- if name_id == NAMEID_VERSION:
- font.version = get_version(namerecord.text)
- if font is not None:
- fonts.append(font)
- return fonts
-
-
-def update_tag(tag, fonts):
- last_name_id = sys.maxsize
- fonts_iterator = fonts.__iter__()
- font = None
- for namerecord in tag.iter('namerecord'):
- if 'nameID' in namerecord.attrib:
- name_id = int(namerecord.attrib['nameID'])
- # skip irrelevant records
- if name_id < NAMEID_LIST_MIN or name_id > NAMEID_LIST_MAX:
- continue
- if name_id <= last_name_id:
- font = next(fonts_iterator)
- font = update_font_name(font)
- last_name_id = name_id
- if name_id == NAMEID_FAMILY:
- namerecord.text = font.family
- if name_id == NAMEID_FULLNAME:
- namerecord.text = font.fullname
-
-
-def update_font_name(font):
- """ Compute the new font family name and font fullname. If the font has a
- valid version, it's sanitized and appended to the font family name. The
- font fullname is then created by joining the new family name and the
- style. If the style is 'Regular', it is appended only if the original font
- had it. """
- if font.family is None or font.style is None:
- raise InvalidFontException('Font doesn\'t have proper family name or style')
- if font.version is not None:
- new_family = font.family + font.version
- else:
- new_family = font.family
- if font.style == 'Regular' and not font.ends_in_regular:
- font.fullname = new_family
- else:
- font.fullname = new_family + ' ' + font.style
- font.family = new_family
- return font
-
-
-def ends_in_regular(string):
- """ According to the specification, the font fullname should not end in
- 'Regular' for plain fonts. However, some fonts don't obey this rule. We
- keep the style info, to minimize the diff. """
- string = string.strip().split()[-1]
- return string == 'Regular'
-
-
-def get_version(string):
- string = string.strip()
- # The spec says that the version string should start with "Version ". But not
- # all fonts do. So, we return the complete string if it doesn't start with
- # the prefix, else we return the rest of the string after sanitizing it.
- prefix = 'Version '
- if string.startswith(prefix):
- string = string[len(prefix):]
- return sanitize(string)
-
-
-def sanitize(string):
- """ Remove non-standard chars. """
- return re.sub(r'[^\w-]+', '', string)
-
-if __name__ == '__main__':
- main(sys.argv[1:])
diff --git a/rename_font/test.py b/rename_font/test.py
deleted file mode 100755
index cf26ee9780..0000000000
--- a/rename_font/test.py
+++ /dev/null
@@ -1,45 +0,0 @@
-#!/usr/bin/env python3
-
-"""Tests build_font.py by renaming a font.
-
-The test copies Roboto-Regular.ttf to a tmp directory and ask build_font.py to rename it and put in another dir.
-We then use ttx to dump the new font to its xml and check if rename was successful
-
-To test locally, use:
-PYTHONPATH="$PYTHONPATH:/path/to/android/checkout/external/fonttools/Lib" ./test.py
-"""
-
-import unittest
-import build_font
-
-from fontTools import ttx
-import os
-import xml.etree.ElementTree as etree
-import shutil
-import tempfile
-
-class MyTest(unittest.TestCase):
- def test(self):
- font_name = "Roboto-Regular.ttf"
- srcdir = tempfile.mkdtemp()
- print("srcdir: " + srcdir)
- shutil.copy(font_name, srcdir)
- destdir = tempfile.mkdtemp()
- print("destdir: " + destdir)
- self.assertTrue(build_font.main([srcdir, destdir]) is None)
- out_path = os.path.join(destdir, font_name)
- ttx.main([out_path])
- ttx_path = out_path[:-1] + "x"
- tree = etree.parse(ttx_path)
- root = tree.getroot()
- name_tag = root.find('name')
- fonts = build_font.get_font_info(name_tag)
- shutil.rmtree(srcdir)
- shutil.rmtree(destdir)
- self.assertEqual(fonts[0].family, "Roboto1200310")
- self.assertEqual(fonts[0].fullname, "Roboto1200310 Regular")
-
-
-
-if __name__ == '__main__':
- unittest.main()
diff --git a/split_universal_binary.sh b/split_universal_binary.sh
new file mode 100755
index 0000000000..bd57245a7a
--- /dev/null
+++ b/split_universal_binary.sh
@@ -0,0 +1,37 @@
+#!/bin/bash
+
+readonly OUT_DIR="$1"
+readonly DIST_DIR="$2"
+readonly BUILD_NUMBER="$3"
+
+readonly SCRIPT_DIR="$(dirname "$0")"
+
+readonly ARM=arm64
+readonly X86=x86_64
+
+NATIVE_LIBRARIES=${SCRIPT_DIR}"/../../out/host/darwin-x86/lib64"
+
+# Find lipo command used to create and manipulate universal binaries
+LIPO=$(/usr/bin/xcrun --find lipo)
+
+mkdir ${OUT_DIR}/${ARM}
+mkdir ${OUT_DIR}/${X86}
+
+# Split all universal binaries built for layoutlib into an ARM64 version and a X86_64 version
+for f in ${NATIVE_LIBRARIES}/*
+do
+ ${LIPO} $f -output ${OUT_DIR}/${ARM}/$(basename $f) -thin ${ARM}
+ ${LIPO} $f -output ${OUT_DIR}/${X86}/$(basename $f) -thin ${X86}
+done
+
+# Put the single architecture binaries inside the DIST folder to be accessible on ab/
+if [[ -d "${DIST_DIR}" ]]; then
+ cp -r ${OUT_DIR}/${ARM} ${DIST_DIR}/layoutlib_native/darwin
+ cp -r ${OUT_DIR}/${X86} ${DIST_DIR}/layoutlib_native/darwin
+fi
+
+# Clean
+rm -rf ${OUT_DIR}/${ARM}
+rm -rf ${OUT_DIR}/${X86}
+
+exit 0
diff --git a/validator/Android.bp b/validator/Android.bp
index ca68179d92..2ca1f21aba 100644
--- a/validator/Android.bp
+++ b/validator/Android.bp
@@ -25,7 +25,6 @@ java_library_host {
java_resource_dirs: ["resources"],
libs: [
- "tools-common-prebuilt",
"temp_layoutlib",
"layoutlib_api-prebuilt",
"layoutlib-common",
diff --git a/validator/resources/strings.properties b/validator/resources/strings.properties
index 7855142697..7b324b94ca 100644
--- a/validator/resources/strings.properties
+++ b/validator/resources/strings.properties
@@ -14,132 +14,140 @@
# See the License for the specific language governing permissions and
# limitations under the License.
#
+result_message_not_important_for_accessibility = This item was not found to be important for accessibility.
+result_message_no_content_desc = This item has no <tt>android:contentDescription</tt>.
+result_message_not_visible = This item is not visible.
+result_message_not_enabled = This item isn\'t enabled.
+result_message_not_text_view = This item is not a <tt>TextView</tt>.
+result_message_no_screencapture = Screen capture data could not be obtained.
+result_message_view_not_within_screencapture = This item\'s on-screen location (<tt>%1$s</tt>) were not within the screen capture on-screen location (<tt>%2$s</tt>).
+result_message_screencapture_data_hidden = Screen capture information for this item was hidden.
+result_message_screencapture_uniform_color = Screen capture has a uniform color.
+clickable = clickable
+non_clickable = non-clickable
+long_clickable = long clickable
+clickable_and_long_clickable = clickable and long clickable
actionable = actionable
-button_item_type = button
-check_title_accessibility_traversal = Traversal order
-check_title_class_name_not_supported = Unsupported item type
+result_message_sdk_version_not_applicable = This check is not applicable on devices running Android %1$s and above.
+result_message_addendum_view_potentially_obscured = This item may be obscured by other on-screen content. Consider manually testing this item\'s contrast.
check_title_clickablespan = Link
+result_message_clickablespan_no_determined_type = This item\'s type is undetermined.
+result_message_urlspan_invalid_url = Verify that the URL within this item\'s <tt>URLSpan</tt> is valid.
+result_message_urlspan_not_clickablespan = This item should use a <tt>URLSpan</tt> in place of a <tt>ClickableSpan</tt>.
check_title_duplicate_clickable_bounds = Clickable items
+result_message_brief_same_view_bounds = Multiple %1$s items share this location on the screen.
+result_message_same_view_bounds = This %1$s item has the same on-screen location (<tt>%2$s</tt>) as %3$d other item(s) with those properties.
+result_message_view_bounds = This %1$s item also has an on-screen location of <tt>%2$s</tt>.
check_title_duplicate_speakable_text = Item descriptions
+result_message_brief_same_speakable_text = Multiple items have the same description.
+result_message_same_speakable_text = This %1$s item\'s speakable text: \"<tt>%2$s</tt>\" is identical to that of %3$d other item(s).
+result_message_speakable_text = This %1$s item also has speakable text: \"<tt>%2$s</tt>\".
check_title_editable_content_desc = Editable item label
+result_message_editable_textview_content_desc = This editable <tt>TextView</tt> has an <tt>android:contentDescription</tt>. A screen reader may read this attribute instead of the editable content when the user is navigating.
+result_message_not_editable_textview = This item is not an editable <tt>TextView</tt>.
check_title_image_contrast = Image contrast
-check_title_item_exposed = Exposed items
-check_title_link_test = Link text
-check_title_reading_score = Readability
+result_message_not_imageview = This item is not an <tt>ImageView</tt>.
+result_message_brief_image_contrast_not_sufficient = Consider increasing the contrast ratio between this image\'s foreground and background.
+result_message_image_contrast_not_sufficient = The image\'s contrast ratio is %1$.2f. This ratio is based on an estimated foreground color of <tt>#%3$06X</tt> and an estimated background color of <tt>#%4$06X</tt>. Consider increasing this ratio to %2$.2f or greater.
+result_message_image_contrast_not_sufficient_confirmed = The image\'s contrast ratio is %1$.2f. This ratio is based on the provided foreground color of <tt>#%3$06X</tt> and provided background color of <tt>#%4$06X</tt>. Consider increasing this ratio to %2$.2f or greater.
+result_message_image_customized_contrast_not_sufficient = The image\'s contrast ratio is %1$.2f. This ratio is based on an estimated foreground color of <tt>#%3$06X</tt> and an estimated background color of <tt>#%4$06X</tt>. Consider increasing this ratio to the modified ratio of %2$.2f or greater.
+result_message_image_customized_contrast_not_sufficient_confirmed = The image\'s contrast ratio is %1$.2f. This ratio is based on the provided foreground color of <tt>#%3$06X</tt> and provided background color of <tt>#%4$06X</tt>. Consider increasing this ratio to the modified ratio of %2$.2f or greater.
check_title_redundant_description = Item type label
-check_title_speakable_text_present = Item label
-check_title_text_contrast = Text contrast
-check_title_text_style = Text Style
-check_title_touch_target_size = Touch target
-check_view_banned_word = Banned word
+result_message_english_locale_only = This check only runs on devices with locales set to English.
+result_message_brief_content_desc_contains_redundant_word = This item\'s <tt>android:contentDescription</tt> might contain unnecessary text.
+result_message_content_desc_ends_with_view_type = This item\'s <tt>android:contentDescription</tt>, \"<tt>%1$s</tt>\" ends with the item\'s type.
+result_message_content_desc_contains_redundant_word = This item\'s <tt>android:contentDescription</tt>, \"<tt>%1$s</tt>\" contains the item type \"<tt>%2$s</tt>\".
+result_message_content_desc_contains_action = This item\'s <tt>android:contentDescription</tt>, \"<tt>%1$s</tt>\", contains the action \"<tt>%2$s</tt>\".
+result_message_content_desc_contains_state = This item\'s <tt>android:contentDescription</tt>, \"<tt>%1$s</tt>\", contains the state \"<tt>%2$s</tt>\".
+button_item_type = button
checkbox_item_type = checkbox
checkbox_item_type_separate_words = check box
checked_state = checked
+unchecked_state = unchecked
+selected_state = selected
+unselected_state = unselected
click_action = click
-clickable = clickable
-clickable_and_long_clickable = clickable and long clickable
-description_remove_view_attribute = Remove the view attribute <tt>%1$s</tt>.
-description_set_view_attribute = Set the view attribute <tt>%1$s</tt> to <tt>%2$s</tt>.
-description_set_view_attribute_with_an_empty_string = Set the view attribute <tt>%1$s</tt> to a meaningful non-empty string or resource reference.
-italic_text = italic
-italic_underline_text = italic and underline
-long_clickable = long clickable
-non_clickable = non-clickable
+swipe_action = swipe
+tap_action = tap
+check_title_speakable_text_present = Item label
+result_message_should_not_focus = This item would not be focused by a screen reader.
+result_message_web_content = Web content is not evaluated.
+result_message_missing_speakable_text = This item may not have a label readable by screen readers.
+check_title_text_contrast = Text contrast
+result_message_textview_empty = This <tt>TextView</tt> is empty.
+result_message_could_not_get_text_color = This item\'s text color could not be determined.
+result_message_could_not_get_background_color = This item\'s background color could not be determined.
+result_message_text_must_be_opaque = This item\'s text color is not opaque.
+result_message_background_must_be_opaque = This items\'s background color is not opaque.
+result_message_addendum_opacity_description = Its actual opacity is %1$.2f%%.
+result_message_brief_text_contrast_not_sufficient = Consider increasing this item\'s text foreground to background contrast ratio.
+result_message_textview_contrast_not_sufficient = The item\'s text contrast ratio is %1$.2f. This ratio is based on a text color of <tt>#%2$06X</tt> and background color of <tt>#%3$06X</tt>. Consider increasing this item\'s text contrast ratio to %4$.2f or greater.
+result_message_customized_textview_contrast_not_sufficient = The item\'s text contrast ratio is %1$.2f. This ratio is based on a text color of <tt>#%2$06X</tt> and background color of <tt>#%3$06X</tt>. Consider using colors that result in a contrast ratio greater than the modified ratio of %4$.2f.
+result_message_textview_heuristic_contrast_not_sufficient = The item\'s text contrast ratio is %1$.2f. This ratio is based on an estimated foreground color of <tt>#%2$06X</tt> and an estimated background color of <tt>#%3$06X</tt>. Consider using colors that result in a contrast ratio greater than %4$.2f for small text, or %5$.2f for large text.
+result_message_textview_heuristic_contrast_not_sufficient_when_text_size_available = The item\'s text contrast ratio is %1$.2f. This ratio is based on an estimated foreground color of <tt>#%2$06X</tt> and an estimated background color of <tt>#%3$06X</tt>. Consider increasing this item\'s text contrast ratio to %4$.2f or greater.
+result_message_textview_heuristic_contrast_not_sufficient_confirmed = The item\'s text contrast ratio is %1$.2f. This ratio is based on the provided foreground color of <tt>#%2$06X</tt> and provided background color of <tt>#%3$06X</tt>. Consider using colors that result in a contrast ratio greater than %4$.2f for small text, or %5$.2f for large text.
+result_message_textview_heuristic_contrast_not_sufficient_when_text_size_available_confirmed = The item\'s text contrast ratio is %1$.2f. This ratio is based on the provided foreground color of <tt>#%2$06X</tt> and provided background color of <tt>#%3$06X</tt>. Consider increasing this item\'s text contrast ratio to %4$.2f or greater.
+result_message_textview_heuristic_customized_contrast_not_sufficient = The item\'s text contrast ratio is %1$.2f. This ratio is based on an estimated foreground color of <tt>#%2$06X</tt> and an estimated background color of <tt>#%3$06X</tt>. Consider increasing this item\'s text contrast ratio to the modified ratio of %4$.2f or greater.
+result_message_textview_heuristic_customized_contrast_not_sufficient_confirmed = The item\'s text contrast ratio is %1$.2f. This ratio is based on the provided foreground color of <tt>#%2$06X</tt> and provided background color of <tt>#%3$06X</tt>. Consider increasing this item\'s text contrast ratio to the modified ratio of %4$.2f or greater.
+result_message_contrast_sufficient_confirmed = This item\'s foreground and background colors meet the suggested contrast ratio.
+result_message_customized_contrast_sufficient_confirmed = This item\'s foreground and background colors meet the modified contrast ratio.
question_id_message_confirm_foreground_background_colors = Are the detected foreground and background colors correct?
-question_id_message_provide_background_color = What is the correct background color?
-question_id_message_provide_foreground_color = What is the correct foreground color?
-question_message_identify_unexposed_items = Select regions of the screen with unidentified items.
-question_message_screen_has_unexposed_items = Does this screen contain important items that are not outlined?
-question_option_message_background_incorrect = Background incorrect
question_option_message_both_correct = Both correct
-question_option_message_both_incorrect = Foreground and background incorrect
question_option_message_foreground_incorrect = Foreground incorrect
+question_option_message_background_incorrect = Background incorrect
+question_option_message_both_incorrect = Foreground and background incorrect
question_option_message_unknown = Unknown
-result_message_addendum_against_scrollable_edge = This item may be only partially visible within a scrollable container.
-result_message_addendum_clickable_ancestor = A parent container may be handling touch events for this item. If selecting the larger container performs the same action as selecting this item, consider defining this item as not clickable. If a different action is performed, consider increasing the size of this item.
-result_message_addendum_clipped_by_ancestor = A parent container may be clipping the size of this item, which has a drawing area of <tt>%1$ddp</tt> x <tt>%2$ddp</tt>. Consider increasing the size of this item\'s clipping ancestor, or allowing a larger parent container to handle actions on behalf of this item.
-result_message_addendum_opacity_description = Its actual opacity is %1$.2f%%.
+question_id_message_provide_foreground_color = What is the correct foreground color?
+question_id_message_provide_background_color = What is the correct background color?
+check_title_touch_target_size = Touch target
+result_message_not_clickable = This view is not clickable.
+result_message_brief_small_touch_target = Consider making this clickable item larger.
+result_message_small_touch_target_width_and_height = This item\'s size is <tt>%1$ddp</tt> x <tt>%2$ddp</tt>. Consider making this touch target <tt>%3$ddp</tt> wide and <tt>%4$ddp</tt> high or larger.
+result_message_small_touch_target_height = This item\'s height is <tt>%1$ddp</tt>. Consider making the height of this touch target <tt>%2$ddp</tt> or larger.
+result_message_small_touch_target_width = This item\'s width is <tt>%1$ddp</tt>. Consider making the width of this touch target <tt>%2$ddp</tt> or larger.
+result_message_customized_small_touch_target_width_and_height = This item\'s size is <tt>%1$ddp</tt> x <tt>%2$ddp</tt>. Consider increasing this touch target to at least the modified minimum size of <tt>%3$ddp</tt> x <tt>%4$ddp</tt>.
+result_message_customized_small_touch_target_height = This item\'s height is <tt>%1$ddp</tt>. Consider increasing the height of this touch target to at least the modified minimum height of <tt>%2$ddp</tt>.
+result_message_customized_small_touch_target_width = This item\'s width is <tt>%1$ddp</tt>. Consider increasing the width of this touch target to at least the modified minimum width of <tt>%2$ddp</tt>.
result_message_addendum_touch_delegate = A <tt>TouchDelegate</tt> has been detected on one of this item\'s ancestors. This message can be ignored if the delegate is of sufficient size and handles touches for this item.
result_message_addendum_touch_delegate_with_hit_rect = A <tt>TouchDelegate</tt> with size <tt>%1$ddp</tt> x <tt>%2$ddp</tt> has been detected for this item. Consider increasing the size of its hit <tt>Rect</tt>.
-result_message_addendum_view_potentially_obscured = This item may be obscured by other on-screen content. Consider manually testing this item\'s contrast.
-result_message_background_must_be_opaque = This items\'s background color is not opaque.
-result_message_banned_word = This item\'s text may contain an inappropriate word, "<tt>%1$s</tt>"
-result_message_brief_banned_word = Consider removing inappropriate words from this item\'s text
-result_message_brief_content_desc_contains_redundant_word = This item\'s <tt>android:contentDescription</tt> might contain unnecessary text.
-result_message_brief_image_contrast_not_sufficient = Consider increasing the contrast ratio between this image\'s foreground and background.
-result_message_brief_is_unexposed_item_screen_region = Consider exposing items in this region to accessibility services.
-result_message_brief_link_text_not_descriptive = Consider using more descriptive text in the link.
-result_message_brief_low_reading_score = This text may have a low readability score.
-result_message_brief_same_speakable_text = Multiple items have the same description.
-result_message_brief_same_view_bounds = Multiple %1$s items share this location on the screen.
-result_message_brief_small_touch_target = Consider making this clickable item larger.
-result_message_brief_styled_text = Consider removing %1$s styling on longer passages of text.
-result_message_brief_text_contrast_not_sufficient = Consider increasing this item\'s text foreground to background contrast ratio.
-result_message_brief_unpredictable_traversal = Traversal behavior with screen readers may be unpredictable.
-result_message_class_name_is_empty = This item\'s type may not be reported to accessibility services. Consider using a type defined by the Android SDK.
-result_message_class_name_is_unknown = This item\'s type could not be determined.
+result_message_addendum_clickable_ancestor = A parent container may be handling touch events for this item. If selecting the larger container performs the same action as selecting this item, consider defining this item as not clickable. If a different action is performed, consider increasing the size of this item.
+result_message_addendum_clipped_by_ancestor = A parent container may be clipping the size of this item, which has a drawing area of <tt>%1$ddp</tt> x <tt>%2$ddp</tt>. Consider increasing the size of this item\'s clipping ancestor, or allowing a larger parent container to handle actions on behalf of this item.
+result_message_addendum_against_scrollable_edge = This item may be only partially visible within a scrollable container.
+check_title_class_name_not_supported = Unsupported item type
result_message_class_name_not_supported_brief = This item\'s type may not be supported.
result_message_class_name_not_supported_detail = This item\'s type <tt>%1$s</tt> may not be resolvable by accessibility services. Consider using a type defined by the Android SDK.
-result_message_clickablespan_no_determined_type = This item\'s type is undetermined.
-result_message_content_desc_contains_action = This item\'s <tt>android:contentDescription</tt>, \"<tt>%1$s</tt>\", contains the action \"<tt>%2$s</tt>\".
-result_message_content_desc_contains_redundant_word = This item\'s <tt>android:contentDescription</tt>, \"<tt>%1$s</tt>\" contains the item type \"<tt>%2$s</tt>\".
-result_message_content_desc_contains_state = This item\'s <tt>android:contentDescription</tt>, \"<tt>%1$s</tt>\", contains the state \"<tt>%2$s</tt>\".
-result_message_content_desc_ends_with_view_type = This item\'s <tt>android:contentDescription</tt>, \"<tt>%1$s</tt>\" ends with the item\'s type.
-result_message_could_not_get_background_color = This item\'s background color could not be determined.
-result_message_could_not_get_text_color = This item\'s text color could not be determined.
-result_message_customized_small_touch_target_height = This item\'s height is <tt>%1$ddp</tt>. Consider increasing the height of this touch target to at least the configured minimum height of <tt>%2$ddp</tt>.
-result_message_customized_small_touch_target_width = This item\'s width is <tt>%1$ddp</tt>. Consider increasing the width of this touch target to at least the configured minimum width of <tt>%2$ddp</tt>.
-result_message_customized_small_touch_target_width_and_height = This item\'s size is <tt>%1$ddp</tt> x <tt>%2$ddp</tt>. Consider increasing this touch target to at least the configured minimum size of <tt>%3$ddp</tt> x <tt>%4$ddp</tt>.
+result_message_class_name_is_empty = This item\'s type may not be reported to accessibility services. Consider using a type defined by the Android SDK.
+result_message_class_name_is_unknown = This item\'s type could not be determined.
+check_title_accessibility_traversal = Traversal order
+result_message_traversal_cycle = This item may be part of a traversal ordering cycle due to its <tt>%1$s</tt> attribute. Traversal behavior with screen readers may be unpredictable.
+result_message_traversal_over_constrained = Traversal ordering for this item may be over constrained based on its <tt>android:accessibilityTraversalBefore</tt> and <tt>android:accessibilityTraversalAfter</tt> attributes. Traversal behavior with screen readers may be unpredictable.
+result_message_brief_unpredictable_traversal = Traversal behavior with screen readers may be unpredictable.
result_message_disruptive_announcement = A disruptive accessibility announcement has been used.
-result_message_editable_textview_content_desc = This editable <tt>TextView</tt> has an <tt>android:contentDescription</tt>. A screen reader may read this attribute instead of the editable content when the user is navigating.
-result_message_english_locale_only = This check only runs on devices with locales set to English.
-result_message_has_unexposed_items = This screen may have items that are not exposed to accessibility services.
-result_message_image_contrast_not_sufficient = The image\'s contrast ratio is %1$.2f. This ratio is based on an estimated foreground color of <tt>#%3$06X</tt> and an estimated background color of <tt>#%4$06X</tt>. Consider increasing this ratio to %2$.2f or greater.
-result_message_image_customized_contrast_not_sufficient = The image\'s contrast ratio is %1$.2f. This ratio is based on an estimated foreground color of <tt>#%3$06X</tt> and an estimated background color of <tt>#%4$06X</tt>. Consider increasing this ratio to the configured ratio of %2$.2f or greater.
-result_message_is_unexposed_item_screen_region = The region with on-screen location <tt>%1$s</tt> contains at least one item that is not exposed to accessibility services.
-result_message_item_exposed_needs_manual_assessment = This screen needs manual inspection to ensure all items are exposed to accessibility services.
+check_view_banned_word = Banned word
+result_message_banned_word = This item\'s text may contain an inappropriate word, "<tt>%1$s</tt>"
+result_message_brief_banned_word = Consider removing inappropriate words from this item\'s text
+check_title_text_style = Text Style
+result_message_styled_text = This item may use %1$s font for a long passage of text. Consider removing the style from the font to improve readability.
+result_message_brief_styled_text = Consider removing %1$s styling on longer passages of text.
+italic_text = italic
+underline_text = underline
+italic_underline_text = italic and underline
+result_message_no_typeface_info = This item\'s typeface could not be determined.
+check_title_link_test = Link text
+result_message_brief_link_text_not_descriptive = Consider using more descriptive text in the link.
result_message_link_text_not_descriptive = The link text \"<tt>%1$s</tt>\" may not independently convey the link\'s purpose.
+check_title_reading_score = Readability
result_message_low_reading_score = This item\'s text has an approximate readability score of %1$.0f, which is lower than the recommended score of %2$.0f. Consider using simpler words or sentences to make the text easier to read.
-result_message_missing_speakable_text = This item may not have a label readable by screen readers.
-result_message_no_content_desc = This item has no <tt>android:contentDescription</tt>.
-result_message_no_screencapture = Screen capture data could not be obtained.
-result_message_no_typeface_info = This item\'s typeface could not be determined.
-result_message_not_clickable = This view is not clickable.
-result_message_not_editable_textview = This item is not an editable <tt>TextView</tt>.
-result_message_not_enabled = This item isn\'t enabled.
-result_message_not_imageview = This item is not an <tt>ImageView</tt>.
-result_message_not_important_for_accessibility = This item was not found to be important for accessibility.
-result_message_not_text_view = This item is not a <tt>TextView</tt>.
-result_message_not_visible = This item is not visible.
-result_message_same_speakable_text = This %1$s item\'s speakable text: \"<tt>%2$s</tt>\" is identical to that of %3$d other item(s).
-result_message_same_view_bounds = This %1$s item has the same on-screen location (<tt>%2$s</tt>) as %3$d other item(s) with those properties.
-result_message_screencapture_data_hidden = Screen capture information for this item was hidden.
-result_message_screencapture_uniform_color = Screen capture has a uniform color.
-result_message_sdk_version_not_applicable = This check is not applicable on devices running Android %1$s and above.
+result_message_brief_low_reading_score = This text may have a low readability score.
result_message_short_text = This item\'s text is too short to be evaluated.
-result_message_should_not_focus = This item would not be focused by a screen reader.
-result_message_small_touch_target_height = This item\'s height is <tt>%1$ddp</tt>. Consider making the height of this touch target <tt>%2$ddp</tt> or larger.
-result_message_small_touch_target_width = This item\'s width is <tt>%1$ddp</tt>. Consider making the width of this touch target <tt>%2$ddp</tt> or larger.
-result_message_small_touch_target_width_and_height = This item\'s size is <tt>%1$ddp</tt> x <tt>%2$ddp</tt>. Consider making this touch target <tt>%3$ddp</tt> wide and <tt>%4$ddp</tt> high or larger.
-result_message_speakable_text = This %1$s item also has speakable text: \"<tt>%2$s</tt>\".
-result_message_styled_text = This item may use %1$s font for a long passage of text. Consider removing the style from the font to improve readability.
-result_message_text_must_be_opaque = This item\'s text color is not opaque.
-result_message_textview_contrast_not_sufficient = The item\'s text contrast ratio is %1$.2f. This ratio is based on a text color of <tt>#%2$06X</tt> and background color of <tt>#%3$06X</tt>. Consider increasing this item\'s text contrast ratio to %4$.2f or greater.
-result_message_textview_empty = This <tt>TextView</tt> is empty.
-result_message_textview_heuristic_contrast_not_sufficient = The item\'s text contrast ratio is %1$.2f. This ratio is based on an estimated foreground color of <tt>#%2$06X</tt> and an estimated background color of <tt>#%3$06X</tt>. Consider using colors that result in a contrast ratio greater than %4$.2f for small text, or %5$.2f for large text.
-result_message_textview_heuristic_contrast_not_sufficient_confirmed = The item\'s text contrast ratio is %1$.2f. This ratio is based on the provided foreground color of <tt>#%2$06X</tt> and provided background color of <tt>#%3$06X</tt>. Consider using colors that result in a contrast ratio greater than %4$.2f for small text, or %5$.2f for large text.
-result_message_textview_heuristic_customized_contrast_not_sufficient = The item\'s text contrast ratio is %1$.2f. This ratio is based on an estimated foreground color of <tt>#%2$06X</tt> and an estimated background color of <tt>#%3$06X</tt>. Consider increasing this item\'s text contrast ratio to the configured ratio of %4$.2f or greater.
-result_message_textview_heuristic_customized_contrast_not_sufficient_confirmed = The item\'s text contrast ratio is %1$.2f. This ratio is based on the provided foreground color of <tt>#%2$06X</tt> and provided background color of <tt>#%3$06X</tt>. Consider increasing this item\'s text contrast ratio to the configured ratio of %4$.2f or greater.
-result_message_traversal_cycle = This item may be part of a traversal ordering cycle due to its <tt>%1$s</tt> attribute. Traversal behavior with screen readers may be unpredictable.
-result_message_traversal_over_constrained = Traversal ordering for this item may be over constrained based on its <tt>android:accessibilityTraversalBefore</tt> and <tt>android:accessibilityTraversalAfter</tt> attributes. Traversal behavior with screen readers may be unpredictable.
-result_message_urlspan_invalid_url = Verify that the URL within this item\'s <tt>URLSpan</tt> is valid.
-result_message_urlspan_not_clickablespan = This item should use a <tt>URLSpan</tt> in place of a <tt>ClickableSpan</tt>.
-result_message_view_bounds = This %1$s item also has an on-screen location of <tt>%2$s</tt>.
-result_message_view_not_within_screencapture = This item\'s on-screen location (<tt>%1$s</tt>) were not within the screen capture on-screen location (<tt>%2$s</tt>).
-result_message_web_content = Web content is not evaluated.
-selected_state = selected
-swipe_action = swipe
-tap_action = tap
-unchecked_state = unchecked
-underline_text = underline
-unselected_state = unselected
+result_message_item_exposed_needs_manual_assessment = This screen needs manual inspection to ensure all items are exposed to accessibility services.
+result_message_has_unexposed_items = This screen may have items that are not exposed to accessibility services.
+result_message_is_unexposed_item_screen_region = The region with on-screen location <tt>%1$s</tt> contains at least one item that is not exposed to accessibility services.
+check_title_item_exposed = Exposed items
+result_message_brief_is_unexposed_item_screen_region = Consider exposing items in this region to accessibility services.
+check_title_text_size = Text Size
+question_message_screen_has_unexposed_items = Does this screen contain important items that are not outlined?
+question_message_identify_unexposed_items = Select regions of the screen with unidentified items.
+suggestion_remove_view_attribute = Remove this item\'s <tt>%1$s</tt>.
+suggestion_set_view_attribute = Set this item\'s <tt>%1$s</tt> to <tt>%2$s</tt>.
+suggestion_set_view_attribute_with_an_non_empty_string = Set this item\'s <tt>%1$s</tt> to a meaningful non-empty string or resource reference.
diff --git a/validator/resources/values.xml b/validator/resources/values.xml
deleted file mode 100644
index 55dcf57ffa..0000000000
--- a/validator/resources/values.xml
+++ /dev/null
@@ -1,155 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
- ~ Copyright (C) 2020 The Android Open Source Project
- ~
- ~ Licensed under the Apache License, Version 2.0 (the "License");
- ~ you may not use this file except in compliance with the License.
- ~ You may obtain a copy of the License at
- ~
- ~ http://www.apache.org/licenses/LICENSE-2.0
- ~
- ~ Unless required by applicable law or agreed to in writing, software
- ~ distributed under the License is distributed on an "AS IS" BASIS,
- ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- ~ See the License for the specific language governing permissions and
- ~ limitations under the License.
- -->
-<resources xmlns:ns1="urn:oasis:names:tc:xliff:document:1.2">
- <string description="Describes a UI element on which an action may be performed by the user [CHAR LIMIT=NONE]" name="actionable">actionable</string>
- <string description="The term for a UI element that functions as a button. [CHAR LIMIT=NONE]" name="button_item_type">button</string>
- <string description="The title of a check on the accessiblity traversal constraints on the elements in a view. [CHAR LIMIT=50]" name="check_title_accessibility_traversal">Traversal order</string>
- <string description="The title of a check describing that the class name is not supported by the accessibility service. [CHAR LIMIT=50]" name="check_title_class_name_not_supported">Unsupported item type</string>
- <string description="The title of a check describing that views should use the android concept of a &apos;URLSpan&apos; instead of a &apos;ClickableSpan&apos;, for improved accessibility. [CHAR LIMIT=50]" name="check_title_clickablespan">Link</string>
- <string description="The title of a check describing that multiple clickable views (UI elements) share the exact same space on the screen. [CHAR LIMIT=50]" name="check_title_duplicate_clickable_bounds">Clickable items</string>
- <string description="The title of a check describing that this view (UI element) has the same text to be spoken by a screen reader as another view on the screen. [CHAR LIMIT=50]" name="check_title_duplicate_speakable_text">Item descriptions</string>
- <string description="The title of a check describing that this editable view (UI element) should not populate its &apos;android:contentDescription&apos; attribute. [CHAR LIMIT=50]" name="check_title_editable_content_desc">Editable item label</string>
- <string description="The title of a check describing that the contrast ratio (the ratio of the luminance of the foreground and background colors) of the image in this view (UI element) is lower than the required ratio. [CHAR LIMIT=50]" name="check_title_image_contrast">Image contrast</string>
- <string description="The title of a check describing that UI elements may be unexposed to accessibility services [CHAR LIMIT=NONE]" name="check_title_item_exposed">Exposed items</string>
- <string description="The title of a check on link text. [CHAR LIMIT=50]" name="check_title_link_test">Link text</string>
- <string description="The title of a check used to determine readability of text based on reading score. [CHAR LIMIT=50]" name="check_title_reading_score">Readability</string>
- <string description="The title of a check describing that this view (UI element) has redundant or unnecessary text within its &apos;android:contentDescription&apos; attribute. [CHAR LIMIT=50]" name="check_title_redundant_description">Item type label</string>
- <string description="The title of a check describing that this View (UI element) has no description that would be spoken to the user by a screen reader if this view were to be focused. [CHAR LIMIT=50]" name="check_title_speakable_text_present">Item label</string>
- <string description="The title of a check describing that the contrast ratio (the ratio of the luminance of the foreground and background colors) of this TextView (UI element) is lower than the required ratio. [CHAR LIMIT=50]" name="check_title_text_contrast">Text contrast</string>
- <string description="The title of a check on the styling of text. [CHAR LIMIT=50]" name="check_title_text_style">Text Style</string>
- <string description="The title of a check describing that the size of this clickable view (UI element) is below minimum requirements. [CHAR LIMIT=50]" name="check_title_touch_target_size">Touch target</string>
- <string description="The title of a check used to detect banned words within the text of a view. [CHAR LIMIT=50]" name="check_view_banned_word">Banned word</string>
- <string description="The term for a UI element that functions as a checkbox, as a single word (ex. &apos;checkbox&apos; instead of &apos;check box&apos; in English). If there isn&apos;t a single word representation in the target language, use multiple words. [CHAR LIMIT=NONE]" name="checkbox_item_type">checkbox</string>
- <string description="The term for a UI element that functions as a checkbox, as separate words (ex. &apos;check box&apos; instead of &apos;checkbox&apos; in English). If there isn&apos;t a multiple word representation in the target language, use a single word. [CHAR LIMIT=NONE]" name="checkbox_item_type_separate_words">check box</string>
- <string description="Adjective describing a checkbox that is selected. [CHAR LIMIT=NONE]" name="checked_state">checked</string>
- <string description="An action in a UI, as in the imperative &apos;click to send&apos;. [CHAR LIMIT=NONE]" name="click_action">click</string>
- <string description="Describes a UI element that is clickable [CHAR LIMIT=NONE]" name="clickable">clickable</string>
- <string description="Describes a UI element that is both clickable and long clickable [CHAR LIMIT=NONE]" name="clickable_and_long_clickable">clickable and long clickable</string>
- <string description="The description for a fix suggestion which recommends removing a view attribute [CHAR LIMIT=NONE]" name="description_remove_view_attribute" translatable="false">Remove the view attribute &lt;tt><ns1:g example="android:contentDescription" id="view_attribute_fully_qualified_name">%1$s</ns1:g>&lt;/tt>.</string>
- <string description="The description for a fix suggestion which recommends setting a value to a view attribute [CHAR LIMIT=NONE]" name="description_set_view_attribute" translatable="false">Set the view attribute &lt;tt><ns1:g example="android:contentDescription" id="view_attribute_fully_qualified_name">%1$s</ns1:g>&lt;/tt> to &lt;tt><ns1:g example="#FFFFFF" id="suggested_value">%2$s</ns1:g>&lt;/tt>.</string>
- <string description="The description for a fix suggestion which recommends asking the developer to set the view attribute to a meaningful non-empty String literal or relevant resource [CHAR LIMIT=NONE]" name="description_set_view_attribute_with_an_empty_string" translatable="false">Set the view attribute &lt;tt><ns1:g example="android:contentDescription" id="view_attribute_fully_qualified_name">%1$s</ns1:g>&lt;/tt> to a meaningful non-empty string or resource reference.</string>
- <string description="Describes italic text styling. [CHAR LIMIT=NONE]" name="italic_text">italic</string>
- <string description="Describes text with both italic and underlined styling. [CHAR LIMIT=NONE]" name="italic_underline_text">italic and underline</string>
- <string description="Describes a UI element that is long clickable [CHAR LIMIT=NONE]" name="long_clickable">long clickable</string>
- <string description="Describes a UI element that is not clickable [CHAR LIMIT=NONE]" name="non_clickable">non-clickable</string>
- <string description="The question message for question of if the foreground and background colors are correct [CHAR LIMIT=NONE]" name="question_id_message_confirm_foreground_background_colors">Are the detected foreground and background colors correct?</string>
- <string description="The question prompting a user to provide a background color [CHAR LIMIT=NONE]" name="question_id_message_provide_background_color">What is the correct background color?</string>
- <string description="The question prompting a user to provide a foreground color [CHAR LIMIT=NONE]" name="question_id_message_provide_foreground_color">What is the correct foreground color?</string>
- <string description="The question message for question prompting the identification of screen regions with unexposed elements [CHAR LIMIT=NONE]" name="question_message_identify_unexposed_items">Select regions of the screen with unidentified items.</string>
- <string description="The question message for question of if there are unexposed elements in a view [CHAR LIMIT=NONE]" name="question_message_screen_has_unexposed_items">Does this screen contain important items that are not outlined?</string>
- <string description="The user selection option for if only the background color is incorrect. [CHAR LIMIT=NONE]" name="question_option_message_background_incorrect">Background incorrect</string>
- <string description="The user selection option for if the foreground and background colors are both correct [CHAR LIMIT=NONE]" name="question_option_message_both_correct">Both correct</string>
- <string description="The user selection option for if the foreground and background colors are incorrect.[CHAR LIMIT=NONE]" name="question_option_message_both_incorrect">Foreground and background incorrect</string>
- <string description="The user selection option for if only the foreground color is incorrect. [CHAR LIMIT=NONE]" name="question_option_message_foreground_incorrect">Foreground incorrect</string>
- <string description="The user selection option for if it is unknown if the foreground and background colors are both incorrect.[CHAR LIMIT=NONE]" name="question_option_message_unknown">Unknown</string>
- <string description="A message that is appended to a result message of a check indicating the view (UI element) may only be partially visible and is against the scrollable edge of a parent (a larger containing UI element). [CHAR LIMIT=NONE]" name="result_message_addendum_against_scrollable_edge">This item may be only partially visible within a scrollable container.</string>
- <string description="A message that is appended to a result message of a check describing that the result may be ignored in the case where this view&apos;s (UI element) parent (a larger containing UI element) performs the same action of this view when clicked. [CHAR LIMIT=NONE]" name="result_message_addendum_clickable_ancestor">A parent container may be handling touch events for this item. If selecting the larger container performs the same action as selecting this item, consider defining this item as not clickable. If a different action is performed, consider increasing the size of this item.</string>
- <string description="A message that is appended to a result message of a check describing that this view&apos;s (UI element) parent (a containing UI element) is clipping (constraining in size) the size of this item. [CHAR LIMIT=NONE]" name="result_message_addendum_clipped_by_ancestor">A parent container may be clipping the size of this item, which has a drawing area of <ns1:g example="30dp" id="nonclipped_width">&lt;tt>%1$ddp&lt;/tt></ns1:g> x <ns1:g example="30dp" id="nonclipped_height">&lt;tt>%2$ddp&lt;/tt></ns1:g>. Consider increasing the size of this item\'s clipping ancestor, or allowing a larger parent container to handle actions on behalf of this item.</string>
- <string description="A description of a view&apos;s (UI element&apos;s) opacity including level of translucency. [CHAR LIMIT=NONE]" name="result_message_addendum_opacity_description">Its actual opacity is <ns1:g example="50" id="opacity">%1$.2f</ns1:g>%%.</string>
- <string description="A message that is appended to a result message of a check describing that the result may be inaccurate because this view (UI element) uses a &apos;TouchDelegate&apos;, which allows a developer to define their own touchable region for the view. [CHAR LIMIT=NONE]" name="result_message_addendum_touch_delegate">A <ns1:g example="TouchDelegate" id="touch_delegate_class">&lt;tt>TouchDelegate&lt;/tt></ns1:g> has been detected on one of this item\'s ancestors. This message can be ignored if the delegate is of sufficient size and handles touches for this item.</string>
- <string description="A message that is appended to a result message of a check describing that the result may be inaccurate because this view (UI element) uses a &apos;TouchDelegate&apos;, which allows a developer to define their own touchable region for the view. [CHAR LIMIT=NONE]" name="result_message_addendum_touch_delegate_with_hit_rect">A <ns1:g example="TouchDelegate" id="touch_delegate_class">&lt;tt>TouchDelegate&lt;/tt></ns1:g> with size <ns1:g example="30dp" id="hit_rect_width">&lt;tt>%1$ddp&lt;/tt></ns1:g> x <ns1:g example="30dp" id="hit_rect_height">&lt;tt>%2$ddp&lt;/tt></ns1:g> has been detected for this item. Consider increasing the size of its hit <ns1:g example="Rect" id="rect_class">&lt;tt>Rect&lt;/tt></ns1:g>.</string>
- <string description="A message that is appended to a result message of a check describing that the view (UI element) may be obscured by other on-screen content. [CHAR LIMIT=NONE]" name="result_message_addendum_view_potentially_obscured">This item may be obscured by other on-screen content. Consider manually testing this item\'s contrast.</string>
- <string description="The result message of a check describing that this view&apos;s background is non-opaque/translucent. [CHAR LIMIT=NONE]" name="result_message_background_must_be_opaque">This items\'s background color is not opaque.</string>
- <string description="The result message of a check describing that the word used in this string is inappropriate. [CHAR LIMIT=NONE]" name="result_message_banned_word">This item\'s text may contain an inappropriate word, "<ns1:g example="heck" id="banned_word">&lt;tt>%1$s&lt;/tt></ns1:g>"</string>
- <string description="The brief result message describing an inappropriate word being used. [CHAR LIMIT=NONE]" name="result_message_brief_banned_word">Consider removing inappropriate words from this item\'s text</string>
- <string description="The brief result message of a check describing that this view (UI element) has redundant or unnecessary text within its &apos;android:contentDescription&apos; attribute. [CHAR LIMIT=NONE]" name="result_message_brief_content_desc_contains_redundant_word">This item\'s <ns1:g example="android:contentDescription" id="content_description_attr">&lt;tt>android:contentDescription&lt;/tt></ns1:g> might contain unnecessary text.</string>
- <string description="The brief result message of a check describing that the contrast ratio (the ratio of the luminance of the foreground and background colors) of the image in this view (UI element) is lower than the required ratio. [CHAR LIMIT=NONE]" name="result_message_brief_image_contrast_not_sufficient">Consider increasing the contrast ratio between this image\'s foreground and background.</string>
- <string description="The brief result message of a check describing that an element is not exposed to accessibility services [CHAR LIMIT=NONE]" name="result_message_brief_is_unexposed_item_screen_region">Consider exposing items in this region to accessibility services.</string>
- <string description="The brief result message of a check describing that link text is not descriptive. [CHAR LIMIT=NONE]" name="result_message_brief_link_text_not_descriptive">Consider using more descriptive text in the link.</string>
- <string description="The brief result message if text has a low readability score. [CHAR LIMIT=NONE]" name="result_message_brief_low_reading_score">This text may have a low readability score.</string>
- <string description="The brief result message of a check describing that multiple items (UI elements) share the same description that would be spoken by a screen reader. [CHAR LIMIT=NONE]" name="result_message_brief_same_speakable_text">Multiple items have the same description.</string>
- <string description="The brief result message of a check describing that multiple actionable items (UI elements) share the same space on the screen. [CHAR LIMIT=NONE]" name="result_message_brief_same_view_bounds">Multiple <ns1:g example="clickable and long clickable" id="clickability">%1$s</ns1:g> items share this location on the screen.</string>
- <string description="The brief result message of a check describing that the size of this view (UI element) may be too small to be touched or interacted with reliably. [CHAR LIMIT=NONE]" name="result_message_brief_small_touch_target">Consider making this clickable item larger.</string>
- <string description="The brief message describing that using bold typeface is ideal. [CHAR LIMIT=NONE]" name="result_message_brief_styled_text">Consider removing <ns1:g example="italic" id="style_info">%1$s</ns1:g> styling on longer passages of text.</string>
- <string description="The brief result message of a check describing that the contrast ratio (the ratio of the luminance of the foreground and background colors) of the text in this TextView (UI element) is lower than the required ratio. [CHAR LIMIT=NONE]" name="result_message_brief_text_contrast_not_sufficient">Consider increasing this item\'s text foreground to background contrast ratio.</string>
- <string description="The brief result message of a check describing that this view may be presented incorrectly to the user when traversed with a screen reader. [CHAR LIMIT=NONE]" name="result_message_brief_unpredictable_traversal">Traversal behavior with screen readers may be unpredictable.</string>
- <string description="The result message of a check describing that the class name is empty and not supported by the accessibility service. [CHAR LIMIT=NONE]" name="result_message_class_name_is_empty">This item\'s type may not be reported to accessibility services. Consider using a type defined by the Android SDK.</string>
- <string description="The result message of a check describing that the class name is unknown. [CHAR LIMIT=NONE]" name="result_message_class_name_is_unknown">This item\'s type could not be determined.</string>
- <string description="The result message of a check describing that the class name is not supported by the accessibility service. [CHAR LIMIT=NONE]" name="result_message_class_name_not_supported_brief">This item\'s type may not be supported.</string>
- <string description="The result message of a check describing that the class name is not supported by the accessibility service. [CHAR LIMIT=NONE]" name="result_message_class_name_not_supported_detail">This item\'s type <ns1:g example="com.example.MyButton" id="class_name">&lt;tt>%1$s&lt;/tt></ns1:g> may not be resolvable by accessibility services. Consider using a type defined by the Android SDK.</string>
- <string description="The result message of a check describing the specific android class of the view (UI element) could not be determined. [CHAR LIMIT=NONE]" name="result_message_clickablespan_no_determined_type">This item\'s type is undetermined.</string>
- <string description="The result message of a check stating that a description of a view (UI element) might specify an available action within its &apos;android:contentDescription&apos; attribute. [CHAR LIMIT=NONE]" name="result_message_content_desc_contains_action">
- This item\'s <ns1:g example="android:contentDescription" id="content_description_attr">&lt;tt>android:contentDescription&lt;/tt></ns1:g>,
- \"<ns1:g example="Tap to send" id="content_desc">&lt;tt>%1$s&lt;/tt></ns1:g>\",
- contains the action \"<ns1:g example="tap" id="item_action">&lt;tt>%2$s&lt;/tt></ns1:g>\".
- </string>
- <string description="The result message of a check stating that a description of a view (UI element) has redundant or unnecessary text (ex: a view of type Button whose &apos;android:contentDescription&apos; is &apos;Save button&apos;). [CHAR LIMIT=NONE]" name="result_message_content_desc_contains_redundant_word">This item\'s <ns1:g example="android:contentDescription" id="content_description_attr">&lt;tt>android:contentDescription&lt;/tt></ns1:g>, \"<ns1:g example="Save button" id="content_desc">&lt;tt>%1$s&lt;/tt></ns1:g>\" contains the item type \"<ns1:g example="button" id="item_type">&lt;tt>%2$s&lt;/tt></ns1:g>\".</string>
- <string description="The result message of a check stating that a description of a view (UI element) might specify an element state within its &apos;android:contentDescription&apos; attribute. [CHAR LIMIT=NONE]" name="result_message_content_desc_contains_state">
- This item\'s <ns1:g example="android:contentDescription" id="content_description_attr">&lt;tt>android:contentDescription&lt;/tt></ns1:g>,
- \"<ns1:g example="Rush delivery selected" id="content_desc">&lt;tt>%1$s&lt;/tt></ns1:g>\",
- contains the state \"<ns1:g example="selected" id="item_state">&lt;tt>%2$s&lt;/tt></ns1:g>\".
- </string>
- <string description="The result message of a check stating that a description of a view (UI element) ends with that view&apos;s type (ex: a view of type Button whose &apos;android:contentDescription&apos; is &apos;Save button&apos;). [CHAR LIMIT=NONE]" name="result_message_content_desc_ends_with_view_type">This item\'s <ns1:g example="android:contentDescription" id="content_description_attr">&lt;tt>android:contentDescription&lt;/tt></ns1:g>, \"<ns1:g example="Save button" id="content_desc">&lt;tt>%1$s&lt;/tt></ns1:g>\" ends with the item\'s type.</string>
- <string description="The result message of a check describing that the background color of a view (UI element) could not be determined. [CHAR LIMIT=NONE]" name="result_message_could_not_get_background_color">This item\'s background color could not be determined.</string>
- <string description="The result message of a check describing that the color of the text within a view (UI element) could not be determined. [CHAR LIMIT=NONE]" name="result_message_could_not_get_text_color">This item\'s text color could not be determined.</string>
- <string description="The result message of a check describing that the height of this view (UI element) is below the user-defined minimum requirement. [CHAR LIMIT=NONE]" name="result_message_customized_small_touch_target_height">This item\'s height is <ns1:g example="30dp" id="view_height">&lt;tt>%1$ddp&lt;/tt></ns1:g>. Consider increasing the height of this touch target to at least the configured minimum height of <ns1:g example="48dp" id="recommended_minimum_height">&lt;tt>%2$ddp&lt;/tt></ns1:g>.</string>
- <string description="The result message of a check describing that the width of this view (UI element) is below the user-defined minimum requirement. [CHAR LIMIT=NONE]" name="result_message_customized_small_touch_target_width">This item\'s width is <ns1:g example="30dp" id="view_width">&lt;tt>%1$ddp&lt;/tt></ns1:g>. Consider increasing the width of this touch target to at least the configured minimum width of <ns1:g example="48dp" id="recommended_minimum_width">&lt;tt>%2$ddp&lt;/tt></ns1:g>.</string>
- <string description="The result message of a check describing that the width and height of this view (UI element) are both below user-defined minimum requirements. [CHAR LIMIT=NONE]" name="result_message_customized_small_touch_target_width_and_height">This item\'s size is <ns1:g example="30dp" id="view_width">&lt;tt>%1$ddp&lt;/tt></ns1:g> x <ns1:g example="30dp" id="view_height">&lt;tt>%2$ddp&lt;/tt></ns1:g>. Consider increasing this touch target to at least the configured minimum size of <ns1:g example="32dp" id="recommended_view_width">&lt;tt>%3$ddp&lt;/tt></ns1:g> x <ns1:g example="32dp" id="recommended_view_height">&lt;tt>%4$ddp&lt;/tt></ns1:g>.</string>
- <string description="The result message of a check describing that an event can be disruptive to the user. [CHAR LIMIT=NONE]" name="result_message_disruptive_announcement">A disruptive accessibility announcement has been used.</string>
- <string description="The result message of a check describing that this editable view (UI element) should not populate its &apos;android:contentDescription&apos; attribute. [CHAR LIMIT=NONE]" name="result_message_editable_textview_content_desc">This editable <ns1:g example="TextView" id="text_view_class">&lt;tt>TextView&lt;/tt></ns1:g> has an <ns1:g example="android:contentDescription" id="content_description_tag">&lt;tt>android:contentDescription&lt;/tt></ns1:g>. A screen reader may read this attribute instead of the editable content when the user is navigating.</string>
- <string description="The result message of a check describing that this check can only be run in English locales. [CHAR LIMIT=NONE]" name="result_message_english_locale_only">This check only runs on devices with locales set to English.</string>
- <string description="The result message if user was uncertain if a screen has any UI elements that were not exposed properly to accessibility services. [CHAR LIMIT=NONE]" name="result_message_has_unexposed_items">This screen may have items that are not exposed to accessibility services.</string>
- <string description="The result message of a check describing that the contrast ratio (the ratio of the luminance of the foreground and background colors) of the image in this view (UI element) is lower than the required ratio. [CHAR LIMIT=NONE]" name="result_message_image_contrast_not_sufficient">The image\'s contrast ratio is <ns1:g example="2.9" id="contrast_ratio">%1$.2f</ns1:g>. This ratio is based on an estimated foreground color of <ns1:g example="#000000" id="foreground_color">&lt;tt>#%3$06X&lt;/tt></ns1:g> and an estimated background color of <ns1:g example="#FFFFFF" id="background_color">&lt;tt>#%4$06X&lt;/tt></ns1:g>. Consider increasing this ratio to <ns1:g example="3.0" id="recommended_contrast_ratio">%2$.2f</ns1:g> or greater.</string>
- <string description="The result message of a check describing that the contrast ratio (the ratio of the luminance of the foreground and background colors) of the image in this view (UI element) is lower than a user-defined ratio. [CHAR LIMIT=NONE]" name="result_message_image_customized_contrast_not_sufficient">The image\'s contrast ratio is <ns1:g example="2.9" id="contrast_ratio">%1$.2f</ns1:g>. This ratio is based on an estimated foreground color of <ns1:g example="#000000" id="foreground_color">&lt;tt>#%3$06X&lt;/tt></ns1:g> and an estimated background color of <ns1:g example="#FFFFFF" id="background_color">&lt;tt>#%4$06X&lt;/tt></ns1:g>. Consider increasing this ratio to the configured ratio of <ns1:g example="3.0" id="customized_heuristic_contrast_ratio">%2$.2f</ns1:g> or greater.</string>
- <string description="The result message if a screen region contains an unexposed element [CHAR LIMIT=NONE]" name="result_message_is_unexposed_item_screen_region">The region with on-screen location <ns1:g example="[0,0][1920,1080]" id="region_bounds">&lt;tt>%1$s&lt;/tt></ns1:g> contains at least one item that is not exposed to accessibility services.</string>
- <string description="The result message indicating that the user must provide more information to determine if all UI elements within a view hierarchy are exposed properly to accessibility services. [CHAR LIMIT=NONE]" name="result_message_item_exposed_needs_manual_assessment">This screen needs manual inspection to ensure all items are exposed to accessibility services.</string>
- <string description="The result message of a check describing that link text is not descriptive. [CHAR LIMIT=NONE]" name="result_message_link_text_not_descriptive">The link text \"<ns1:g example="This is an arbitrary string." id="link_text">&lt;tt>%1$s&lt;/tt></ns1:g>\" may not independently convey the link\'s purpose.</string>
- <string description="The full result message if text has a low readability score. [CHAR LIMIT=NONE]" name="result_message_low_reading_score">This item\'s text has an approximate readability score of <ns1:g example="32" id="text_reading_score">%1$.0f</ns1:g>, which is lower than the recommended score of <ns1:g example="70" id="target_reading_score">%2$.0f</ns1:g>. Consider using simpler words or sentences to make the text easier to read.</string>
- <string description="The result message of a check describing that this View (UI element) has no description that would be spoken to the user by a screen reader if this view were to be focused. [CHAR LIMIT=NONE]" name="result_message_missing_speakable_text">This item may not have a label readable by screen readers.</string>
- <string description="The result message of a check describing that this view (UI element) does not have any text set for the android contentDescription attribute. [CHAR LIMIT=NONE]" name="result_message_no_content_desc">This item has no <ns1:g example="android:contentDescription" id="content_description_attr">&lt;tt>android:contentDescription&lt;/tt></ns1:g>.</string>
- <string description="The result message of a check describing that we were unable to obtain a screenshot. [CHAR LIMIT=NONE]" name="result_message_no_screencapture">Screen capture data could not be obtained.</string>
- <string description="The message if this check was unable to get typeface info for a TextView. [CHAR LIMIT=NONE]" name="result_message_no_typeface_info">This item\'s typeface could not be determined.</string>
- <string description="The result message of a check describing that this view (UI element) is not clickable. [CHAR LIMIT=NONE]" name="result_message_not_clickable">This view is not clickable.</string>
- <string description="The result message of a check describing that this view (UI element) does not contain editable text content. [CHAR LIMIT=NONE]" name="result_message_not_editable_textview">This item is not an editable <ns1:g example="TextView" id="text_view_class">&lt;tt>TextView&lt;/tt></ns1:g>.</string>
- <string description="The result message of a check describing that this view (UI element) is in a &apos;not enabled&apos; or &apos;disabled&apos; state. [CHAR LIMIT=NONE]" name="result_message_not_enabled">This item isn\'t enabled.</string>
- <string description="The result message of a check describing that this view (UI element) is not an instance of the android class ImageView. [CHAR LIMIT=NONE]" name="result_message_not_imageview">This item is not an <ns1:g example="ImageView" id="image_view_class">&lt;tt>ImageView&lt;/tt></ns1:g>.</string>
- <string description="The result message of a check describing that this view (UI element) is not exposed to a screen reader or other accessibility service. [CHAR LIMIT=NONE]" name="result_message_not_important_for_accessibility">This item was not found to be important for accessibility.</string>
- <string description="The result message of a check describing that this view (UI element) is not an instance of the android TextView class [CHAR LIMIT=NONE]" name="result_message_not_text_view">This item is not a <ns1:g example="TextView" id="text_view_class">&lt;tt>TextView&lt;/tt></ns1:g>.</string>
- <string description="The result message of a check describing that this view (UI element) is not currently visible to the user. [CHAR LIMIT=NONE]" name="result_message_not_visible">This item is not visible.</string>
- <string description="The result message of a check describing that this view (UI element) has the same text to be spoken by a screen reader as another view which has the same clickability. [CHAR LIMIT=NONE]" name="result_message_same_speakable_text">This <ns1:g example="clickable" id="clickability">%1$s</ns1:g> item\'s speakable text: \"<ns1:g example="This is an arbitrary string." id="speakable_text">&lt;tt>%2$s&lt;/tt></ns1:g>\" is identical to that of <ns1:g example="2" id="num_views">%3$d</ns1:g> other item(s).</string>
- <string description="The result message of a check describing that this view (UI element) shares exact same space on the screen as another view which has the same clickability and long clickability. [CHAR LIMIT=NONE]" name="result_message_same_view_bounds">This <ns1:g example="clickable and long clickable" id="clickability">%1$s</ns1:g> item has the same on-screen location (<ns1:g example="[0,0][100,100]" id="view_bounds">&lt;tt>%2$s&lt;/tt></ns1:g>) as <ns1:g example="2" id="num_views">%3$d</ns1:g> other item(s) with those properties.</string>
- <string description="The result message of a check describing that the screenshot of a view (UI element) was hidden. [CHAR LIMIT=NONE]" name="result_message_screencapture_data_hidden">Screen capture information for this item was hidden.</string>
- <string description="The result message of a check describing that the screenshot of a view (UI element) has a uniform color. [CHAR LIMIT=NONE]" name="result_message_screencapture_uniform_color">Screen capture has a uniform color.</string>
- <string description="The result message of a check describing that the check is not applicable since specific Android SDK version. [CHAR LIMIT=NONE]" name="result_message_sdk_version_not_applicable">This check is not applicable on devices running Android <ns1:g example="8.0" id="android_version">%1$s</ns1:g> and above.</string>
- <string description="The result message if text is too short to run a check on. [CHAR LIMIT=NONE]" name="result_message_short_text">This item\'s text is too short to be evaluated.</string>
- <string description="The result message of a check describing that a screen reader would not focus this view (UI element). [CHAR LIMIT=NONE]" name="result_message_should_not_focus">This item would not be focused by a screen reader.</string>
- <string description="The result message of a check describing that the height of this view (UI element) is below the minimum requirement. [CHAR LIMIT=NONE]" name="result_message_small_touch_target_height">This item\'s height is <ns1:g example="30dp" id="view_height">&lt;tt>%1$ddp&lt;/tt></ns1:g>. Consider making the height of this touch target <ns1:g example="48dp" id="recommended_minimum_height">&lt;tt>%2$ddp&lt;/tt></ns1:g> or larger.</string>
- <string description="The result message of a check describing that the width of this view (UI element) is below the minimum requirement. [CHAR LIMIT=NONE]" name="result_message_small_touch_target_width">This item\'s width is <ns1:g example="30dp" id="view_width">&lt;tt>%1$ddp&lt;/tt></ns1:g>. Consider making the width of this touch target <ns1:g example="48dp" id="recommended_minimum_width">&lt;tt>%2$ddp&lt;/tt></ns1:g> or larger.</string>
- <string description="The result message of a check describing that the width and height of this view (UI element) are both below minimum requirements. [CHAR LIMIT=NONE]" name="result_message_small_touch_target_width_and_height">This item\'s size is <ns1:g example="30dp" id="view_width">&lt;tt>%1$ddp&lt;/tt></ns1:g> x <ns1:g example="30dp" id="view_height">&lt;tt>%2$ddp&lt;/tt></ns1:g>. Consider making this touch target <ns1:g example="32dp" id="recommended_view_width">&lt;tt>%3$ddp&lt;/tt></ns1:g> wide and <ns1:g example="32dp" id="recommended_view_height">&lt;tt>%4$ddp&lt;/tt></ns1:g> high or larger.</string>
- <string description="The result message of a check describing the text that would be spoken to the user by a screen reader if this view (UI element) were to be focused. [CHAR LIMIT=NONE]" name="result_message_speakable_text">This <ns1:g example="clickable and long clickable" id="clickability">%1$s</ns1:g> item also has speakable text: \"<ns1:g example="This is an arbitrary string." id="speakable_text">&lt;tt>%2$s&lt;/tt></ns1:g>\".</string>
- <string description="The full result message if text uses italic style. [CHAR LIMIT=NONE]" name="result_message_styled_text">This item may use <ns1:g example="italic" id="style_info">%1$s</ns1:g> font for a long passage of text. Consider removing the style from the font to improve readability.</string>
- <string description="The result message of a check describing that this view&apos;s text is non-opaque/translucent. [CHAR LIMIT=NONE]" name="result_message_text_must_be_opaque">This item\'s text color is not opaque.</string>
- <string description="The result message of a check describing that the contrast ratio (the ratio of the luminance of the foreground and background colors) of this TextView (UI element) is lower than the required ratio. [CHAR LIMIT=NONE]" name="result_message_textview_contrast_not_sufficient">The item\'s text contrast ratio is <ns1:g example="2.9" id="contrast_ratio">%1$.2f</ns1:g>. This ratio is based on a text color of <ns1:g example="#000000" id="text_color">&lt;tt>#%2$06X&lt;/tt></ns1:g> and background color of <ns1:g example="#FFFFFF" id="background_color">&lt;tt>#%3$06X&lt;/tt></ns1:g>. Consider increasing this item\'s text contrast ratio to <ns1:g example="3.0" id="recommended_contrast_ratio">%4$.2f</ns1:g> or greater.</string>
- <string description="The result message of a check describing that this TextView (UI element) has no text. [CHAR LIMIT=NONE]" name="result_message_textview_empty">This <ns1:g example="TextView" id="text_view_class">&lt;tt>TextView&lt;/tt></ns1:g> is empty.</string>
- <string description="The result message of a check describing that the contrast ratio (the ratio of the luminance of the foreground and background colors) of this TextView (UI element) is lower than the required ratio unless its text is considered to be large. [CHAR LIMIT=NONE]" name="result_message_textview_heuristic_contrast_not_sufficient">The item\'s text contrast ratio is <ns1:g example="2.9" id="contrast_ratio">%1$.2f</ns1:g>. This ratio is based on an estimated foreground color of <ns1:g example="#FFFFFF" id="foreground_color">&lt;tt>#%2$06X&lt;/tt></ns1:g> and an estimated background color of <ns1:g example="#000000" id="background_color">&lt;tt>#%3$06X&lt;/tt></ns1:g>. Consider using colors that result in a contrast ratio greater than <ns1:g example="4.5" id="recommended_contrast_ratio">%4$.2f</ns1:g> for small text, or <ns1:g example="3.0" id="tolerant_contrast_ratio">%5$.2f</ns1:g> for large text.</string>
- <string description="The result message of a check describing that the contrast ratio (the ratio of the luminance of the foreground and background colors) of this TextView (UI element) is lower than the required ratio unless its text is considered to be large. Result based on colors obtained or confirmed through questions. [CHAR LIMIT=NONE]" name="result_message_textview_heuristic_contrast_not_sufficient_confirmed">The item\'s text contrast ratio is <ns1:g example="2.9" id="contrast_ratio">%1$.2f</ns1:g>. This ratio is based on the provided foreground color of <ns1:g example="#FFFFFF" id="foreground_color">&lt;tt>#%2$06X&lt;/tt></ns1:g> and provided background color of <ns1:g example="#000000" id="background_color">&lt;tt>#%3$06X&lt;/tt></ns1:g>. Consider using colors that result in a contrast ratio greater than <ns1:g example="4.5" id="recommended_contrast_ratio">%4$.2f</ns1:g> for small text, or <ns1:g example="3.0" id="tolerant_contrast_ratio">%5$.2f</ns1:g> for large text.</string>
- <string description="The result message of a check describing that the contrast ratio (the ratio of the luminance of the foreground and background colors) of this TextView (UI element) is lower than a user-defined ratio. [CHAR LIMIT=NONE]" name="result_message_textview_heuristic_customized_contrast_not_sufficient">The item\'s text contrast ratio is <ns1:g example="2.9" id="contrast_ratio">%1$.2f</ns1:g>. This ratio is based on an estimated foreground color of <ns1:g example="#FFFFFF" id="foreground_color">&lt;tt>#%2$06X&lt;/tt></ns1:g> and an estimated background color of <ns1:g example="#000000" id="background_color">&lt;tt>#%3$06X&lt;/tt></ns1:g>. Consider increasing this item\'s text contrast ratio to the configured ratio of <ns1:g example="3.0" id="customized_heuristic_contrast_ratio">%4$.2f</ns1:g> or greater.</string>
- <string description="The result message of a check describing that the contrast ratio (the ratio of the luminance of the foreground and background colors) of this TextView (UI element) is lower than a user-defined ratio based on user provided colors. [CHAR LIMIT=NONE]" name="result_message_textview_heuristic_customized_contrast_not_sufficient_confirmed">The item\'s text contrast ratio is <ns1:g example="2.9" id="contrast_ratio">%1$.2f</ns1:g>. This ratio is based on the provided foreground color of <ns1:g example="#FFFFFF" id="foreground_color">&lt;tt>#%2$06X&lt;/tt></ns1:g> and provided background color of <ns1:g example="#000000" id="background_color">&lt;tt>#%3$06X&lt;/tt></ns1:g>. Consider increasing this item\'s text contrast ratio to the configured ratio of <ns1:g example="3.0" id="customized_heuristic_contrast_ratio">%4$.2f</ns1:g> or greater.</string>
- <string description="The result message of a check describing that this view (UI element) may be presented incorrectly to the user when traversed with a screen reader. [CHAR LIMIT=NONE]" name="result_message_traversal_cycle">This item may be part of a traversal ordering cycle due to its <ns1:g example="android:accessibilityTraversalAfter" id="traversal_after_attr">&lt;tt>%1$s&lt;/tt></ns1:g> attribute. Traversal behavior with screen readers may be unpredictable.</string>
- <string description="The result message of a check describing that this view (UI element) may be presented incorrectly to the user when traversed with a screen reader. [CHAR LIMIT=NONE]" name="result_message_traversal_over_constrained">Traversal ordering for this item may be over constrained based on its <ns1:g example="android:accessibilityTraversalBefore" id="traversal_before_attr">&lt;tt>android:accessibilityTraversalBefore&lt;/tt></ns1:g> and <ns1:g example="android:accessibilityTraversalAfter" id="traversal_after_attr">&lt;tt>android:accessibilityTraversalAfter&lt;/tt></ns1:g> attributes. Traversal behavior with screen readers may be unpredictable.</string>
- <string description="The result message of a check describing that the view has text marked up with an android URLSpan (a hyperlink), which has an invalid URL. [CHAR LIMIT=NONE]" name="result_message_urlspan_invalid_url">Verify that the URL within this item\'s <ns1:g example="URLSpan" id="url_span_class">&lt;tt>URLSpan&lt;/tt></ns1:g> is valid.</string>
- <string description="The result message of a check describing that views should use the android concept of a &apos;URLSpan&apos; instead of a &apos;ClickableSpan&apos;, for improved accessibility. [CHAR LIMIT=NONE]" name="result_message_urlspan_not_clickablespan">This item should use a <ns1:g example="URLSpan" id="url_span_class">&lt;tt>URLSpan&lt;/tt></ns1:g> in place of a <ns1:g example="ClickableSpan" id="clickable_span_class">&lt;tt>ClickableSpan&lt;/tt></ns1:g>.</string>
- <string description="The result message of a check describing the position of this view (UI element) on the screen. [CHAR LIMIT=NONE]" name="result_message_view_bounds">This <ns1:g example="clickable and long clickable" id="clickability">%1$s</ns1:g> item also has an on-screen location of <ns1:g example="[0,0][100,100]" id="view_bounds">&lt;tt>%2$s&lt;/tt></ns1:g>.</string>
- <string description="The result message of a check describing that all or part of the view (UI element) was off the screen when the screenshot was taken. [CHAR LIMIT=NONE]" name="result_message_view_not_within_screencapture">This item\'s on-screen location (<ns1:g example="[0,0][100,100]" id="view_bounds">&lt;tt>%1$s&lt;/tt></ns1:g>) were not within the screen capture on-screen location (<ns1:g example="[0,0][1920,1080]" id="capture_bounds">&lt;tt>%2$s&lt;/tt></ns1:g>).</string>
- <string description="The result message of a check describing that web content (UI element shown within a browser) was not evaluated. [CHAR LIMIT=NONE]" name="result_message_web_content">Web content is not evaluated.</string>
- <string description="Adjective describing a view that is selected. [CHAR LIMIT=NONE]" name="selected_state">selected</string>
- <string description="An action in a UI, as in the imperative &apos;swipe to unlock&apos;. [CHAR LIMIT=NONE]" name="swipe_action">swipe</string>
- <string description="An action in a UI, as in the imperative &apos;tap to select&apos;. [CHAR LIMIT=NONE]" name="tap_action">tap</string>
- <string description="Adjective describing a checkbox that is not selected. [CHAR LIMIT=NONE]" name="unchecked_state">unchecked</string>
- <string description="Describes underlined text styling. [CHAR LIMIT=NONE]" name="underline_text">underline</string>
- <string description="Adjective describing a view that is not selected. [CHAR LIMIT=NONE]" name="unselected_state">unselected</string>
-</resources> \ No newline at end of file
diff --git a/validator/src/ResourceConverter.java b/validator/src/ResourceConverter.java
index 6b9d73e255..839f1919d8 100644
--- a/validator/src/ResourceConverter.java
+++ b/validator/src/ResourceConverter.java
@@ -37,7 +37,7 @@ public class ResourceConverter {
*/
public static void main(String[] args) throws Exception {
System.out.println("Parsing input...");
- Map<String, String> map = loadStrings("./validator/resources/values.xml");
+ Map<String, String> map = loadStrings("./validator/resources/strings.xml");
System.out.println("Writing to output...");
writeStrings(map, "./validator/resources/strings.properties");
System.out.println("Finished converting.");
@@ -91,30 +91,36 @@ public class ResourceConverter {
String name = node.getAttributes().getNamedItem("name").getNodeValue();
StringBuilder valueBuilder = new StringBuilder();
- /**
- * This is a very hacky way to bypass "ns1:g" tag in android's .xml.
- * Ideally we'll read the tag from the parent and apply it here, but it being the
- * deep node list I'm not currently sure how to parse it safely. Might need to look
- * into IntelliJ PSI tree we have in Studio. But I didn't want to add unnecessary deps
- * to LayoutLib.
- *
- * It also means resource namespaces are rendered useless after conversion.
- */
- for (int j = 0; j < node.getChildNodes().getLength(); j++) {
- Node child = node.getChildNodes().item(j);
- String toAdd = null;
- if ("ns1:g".equals(child.getNodeName())) {
- toAdd = child.getFirstChild().getNodeValue();
- } else {
- toAdd = child.getNodeValue();
+ try {
+ /**
+ * This is a very hacky way to bypass "ns1:g" tag in android's .xml.
+ * Ideally we'll read the tag from the parent and apply it here, but it being the
+ * deep node list I'm not currently sure how to parse it safely. Might need to look
+ * into IntelliJ PSI tree we have in Studio. But I didn't want to add unnecessary
+ * deps to LayoutLib.
+ *
+ * It also means resource namespaces are rendered useless after conversion.
+ */
+ for (int j = 0; j < node.getChildNodes().getLength(); j++) {
+ Node child = node.getChildNodes().item(j);
+ String toAdd = null;
+ if ("ns1:g".equals(child.getNodeName())) {
+ toAdd = child.getFirstChild().getNodeValue();
+ } else if ("xliff:g".equals(child.getNodeName())) {
+ toAdd = child.getFirstChild().getNodeValue();
+ } else {
+ toAdd = child.getNodeValue();
+ }
+ // Replace all tab, newline and multi indentations.
+ toAdd = toAdd.replaceAll("[\n\t]", "");
+ toAdd = toAdd.replaceAll("[ ]+", " ");
+ valueBuilder.append(toAdd);
}
- // Replace all tab, newline and multi indentations.
- toAdd = toAdd.replaceAll("[\n\t]", "");
- toAdd = toAdd.replaceAll("[ ]+", " ");
- valueBuilder.append(toAdd);
+ String finalString = valueBuilder.toString().trim();
+ toReturn.put(name, finalString);
+ } catch (Exception e) {
+ e.printStackTrace();
}
- String finalString = valueBuilder.toString().trim();
- toReturn.put(name, finalString);
}
return toReturn;
}
diff --git a/validator/src/com/android/tools/idea/validator/AtfBufferedImage.java b/validator/src/com/android/tools/idea/validator/AtfBufferedImage.java
new file mode 100644
index 0000000000..c5fc13fce0
--- /dev/null
+++ b/validator/src/com/android/tools/idea/validator/AtfBufferedImage.java
@@ -0,0 +1,180 @@
+/*
+ * 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.tools.idea.validator;
+
+import com.android.tools.idea.validator.ValidatorResult.ImageSize;
+import com.android.tools.idea.validator.ValidatorResult.Metric;
+import com.android.tools.layoutlib.annotations.NotNull;
+
+import android.annotation.NonNull;
+
+import java.awt.image.BufferedImage;
+import java.awt.image.DataBufferInt;
+import java.awt.image.WritableRaster;
+import java.io.File;
+import java.io.IOException;
+
+import com.google.android.apps.common.testing.accessibility.framework.utils.contrast.Image;
+import javax.imageio.ImageIO;
+
+import static java.awt.image.BufferedImage.TYPE_INT_ARGB;
+
+/**
+ * Image implementation to be used in Accessibility Test Framework.
+ */
+public class AtfBufferedImage implements Image {
+
+ // The source buffered image, expected to contain the full screen rendered image of the layout.
+ @NotNull private final BufferedImage mImage;
+ // Metrics to be returned
+ @NotNull private final Metric mMetric;
+
+ // Points in before scaled coord
+ private final int mLeft;
+ private final int mTop;
+ private final int mWidth;
+ private final int mHeight;
+
+ // Scale factors in case layoutlib scaled the screen.
+ private final float mScaleX;
+ private final float mScaleY;
+
+ AtfBufferedImage(
+ @NotNull BufferedImage image,
+ @NotNull Metric metric,
+ float scaleX,
+ float scaleY) {
+ // Without unscaling, atf does not recognize bounds that goes over the scaled image.
+ // E.g. if pxl4 is scaled to 1k x 2k (originally 2k x 4k), then atf could request for
+ // bounds at x:1.5k y:0, then without unscaling the call would fail.
+ this(image,
+ metric,
+ 0,
+ 0,
+ (int) (image.getWidth() * 1.0f / scaleX),
+ (int) (image.getHeight() * 1.0f / scaleY),
+ scaleX,
+ scaleY);
+ assert(image.getType() == TYPE_INT_ARGB);
+
+ // FOR DEBUGGING ONLY
+ if (LayoutValidator.shouldSaveCroppedImages()) {
+ saveImage(image);
+ }
+ }
+
+ private AtfBufferedImage(
+ @NotNull BufferedImage image,
+ @NotNull Metric metric,
+ int left,
+ int top,
+ int width,
+ int height,
+ float scaleX,
+ float scaleY) {
+ mImage = image;
+ mMetric = metric;
+ mLeft = left;
+ mTop = top;
+ mWidth = width;
+ mHeight = height;
+ mScaleX = scaleX;
+ mScaleY = scaleY;
+ }
+
+ @Override
+ public int getHeight() {
+ return mHeight;
+ }
+
+ @Override
+ public int getWidth() {
+ return mWidth;
+ }
+
+ @Override
+ @NotNull
+ public Image crop(int left, int top, int width, int height) {
+ return new AtfBufferedImage(mImage, mMetric, left, top, width, height, mScaleX, mScaleY);
+ }
+
+ /**
+ * @return the region that matches the scaled buffered image. The returned image
+ * will not have the width same as {@link #getWidth()} but rather width * mScaleX.
+ */
+ @Override
+ @NotNull
+ public int[] getPixels() {
+ int scaledLeft = (int)(mLeft * mScaleX);
+ int scaledTop = (int)(mTop * mScaleY);
+ int scaledWidth = (int)(mWidth * mScaleX);
+ int scaledHeight = (int)(mHeight * mScaleY);
+
+ if (scaledWidth <= 0 || scaledHeight <= 0) {
+ return new int[0];
+ }
+
+ BufferedImage cropped = mImage.getSubimage(
+ scaledLeft, scaledTop, scaledWidth, scaledHeight);
+ WritableRaster raster =
+ cropped.copyData(cropped.getRaster().createCompatibleWritableRaster());
+ int[] toReturn = ((DataBufferInt) raster.getDataBuffer()).getData();
+ mMetric.mImageMemoryBytes += toReturn.length * 4;
+
+ if (LayoutValidator.shouldSaveCroppedImages()) {
+ saveImage(cropped);
+ }
+
+ return toReturn;
+ }
+
+ // FOR DEBUGGING ONLY
+ private static int SAVE_IMAGE_COUNTER = 0;
+ private void saveImage(BufferedImage image) {
+ try {
+ String name = SAVE_IMAGE_COUNTER + "_img_for_atf_LxT:WxH_" +
+ mLeft + "x" + mTop + ":" +
+ mWidth + "x" + mHeight;
+
+ mMetric.mImageSizes.add(new ImageSize(mLeft, mTop, mWidth, mHeight));
+
+ File output = new File(getDebugDir(), name);
+ if (output.exists()) {
+ output.delete();
+ }
+ ImageIO.write(image, "PNG", output);
+ SAVE_IMAGE_COUNTER++;
+ } catch (IOException ioe) {
+ mMetric.mErrorMessage = ioe.getMessage();
+ }
+ }
+
+ @NonNull
+ private File getDebugDir() {
+ File failureDir;
+ String failureDirString = System.getProperty("debug.dir");
+ if (failureDirString != null) {
+ failureDir = new File(failureDirString);
+ } else {
+ String workingDirString = System.getProperty("user.dir");
+ failureDir = new File(workingDirString, "out/debugs");
+ }
+
+ failureDir.mkdirs();
+ return failureDir;
+ }
+}
diff --git a/validator/src/com/android/tools/idea/validator/LayoutValidator.java b/validator/src/com/android/tools/idea/validator/LayoutValidator.java
index dc34e908ff..03f080ca40 100644
--- a/validator/src/com/android/tools/idea/validator/LayoutValidator.java
+++ b/validator/src/com/android/tools/idea/validator/LayoutValidator.java
@@ -19,9 +19,9 @@ package com.android.tools.idea.validator;
import com.android.tools.idea.validator.ValidatorData.Level;
import com.android.tools.idea.validator.ValidatorData.Policy;
import com.android.tools.idea.validator.ValidatorData.Type;
-import com.android.tools.idea.validator.accessibility.AccessibilityValidator;
import com.android.tools.layoutlib.annotations.NotNull;
import com.android.tools.layoutlib.annotations.Nullable;
+import com.android.tools.layoutlib.annotations.VisibleForTesting;
import android.view.View;
@@ -35,26 +35,120 @@ public class LayoutValidator {
public static final ValidatorData.Policy DEFAULT_POLICY = new Policy(
EnumSet.of(Type.ACCESSIBILITY, Type.RENDER),
- EnumSet.of(Level.ERROR, Level.WARNING));
+ EnumSet.of(Level.ERROR, Level.WARNING, Level.INFO, Level.VERBOSE));
private static ValidatorData.Policy sPolicy = DEFAULT_POLICY;
+ private static boolean sPaused = false;
+
+ private static boolean sSaveCroppedImages = false;
+
+ private static boolean sObtainCharacterLocations = false;
+
+ /**
+ * @return true if validator is paused. False otherwise.
+ */
+ public static boolean isPaused() {
+ return sPaused;
+ }
+
+ /**
+ * Pause or resume validator. {@link RenderParamsFlags#FLAG_ENABLE_LAYOUT_VALIDATOR} must be
+ * enabled.
+ * @param paused true if validator should be paused. False to resume.
+ */
+ public static void setPaused(boolean paused) {
+ sPaused = paused;
+ }
+
+ public static boolean shouldSaveCroppedImages() {
+ return sSaveCroppedImages;
+ }
+
+ /**
+ * For Debugging purpose. Save all cropped images used by atf if enabled.
+ * @param save
+ */
+ public static void setSaveCroppedImages(boolean save) {
+ sSaveCroppedImages = save;
+ }
+
+ /**
+ * Indicates whether text character locations should be requested.
+ *
+ * @param obtainCharacterLocations true if text character locations should be requested.
+ */
+ public static void setObtainCharacterLocations(boolean obtainCharacterLocations) {
+ sObtainCharacterLocations = obtainCharacterLocations;
+ }
+
+ /**
+ * @return true if text character locations should be requested.
+ */
+ public static boolean obtainCharacterLocations() {
+ return sObtainCharacterLocations;
+ }
+
/**
* Validate the layout using the default policy.
* Precondition: View must be attached to the window.
*
+ * Used for testing.
+ *
* @return The validation results. If no issue is found it'll return empty result.
*/
@NotNull
- public static ValidatorResult validate(@NotNull View view, @Nullable BufferedImage image) {
- if (view.isAttachedToWindow()) {
- return AccessibilityValidator.validateAccessibility(view, image, sPolicy);
+ @VisibleForTesting
+ public static ValidatorResult validate(
+ @NotNull View view,
+ @Nullable BufferedImage image,
+ float scaleX,
+ float scaleY) {
+ if (!sPaused && view.isAttachedToWindow()) {
+ ValidatorHierarchy hierarchy = ValidatorUtil.buildHierarchy(
+ sPolicy,
+ view,
+ image,
+ scaleX,
+ scaleY);
+ return ValidatorUtil.generateResults(sPolicy, hierarchy);
}
// TODO: Add non-a11y layout validation later.
return new ValidatorResult.Builder().build();
}
/**
+ * Build the hierarchy necessary for validating the layout.
+ * The operation is quick thus can be used frequently.
+ *
+ * @return The hierarchy to be used for validation.
+ */
+ @NotNull
+ public static ValidatorHierarchy buildHierarchy(
+ @NotNull View view,
+ @Nullable BufferedImage image,
+ float scaleX,
+ float scaleY) {
+ if (!sPaused && view.isAttachedToWindow()) {
+ return ValidatorUtil.buildHierarchy(
+ sPolicy,
+ view,
+ image,
+ scaleX,
+ scaleY);
+ }
+ return new ValidatorHierarchy();
+ }
+
+ /**
+ * @return The validator result that matches the hierarchy
+ */
+ @NotNull
+ public static ValidatorResult validate(@NotNull ValidatorHierarchy hierarchy) {
+ return ValidatorUtil.generateResults(sPolicy, hierarchy);
+ }
+
+ /**
* Update the policy with which to run the validation call.
* @param policy new policy.
*/
diff --git a/validator/src/com/android/tools/idea/validator/ValidatorData.java b/validator/src/com/android/tools/idea/validator/ValidatorData.java
index 49f0e307f5..167cab1789 100644
--- a/validator/src/com/android/tools/idea/validator/ValidatorData.java
+++ b/validator/src/com/android/tools/idea/validator/ValidatorData.java
@@ -21,6 +21,7 @@ import com.android.tools.layoutlib.annotations.Nullable;
import java.util.EnumSet;
import java.util.HashSet;
+import java.util.List;
import com.google.android.apps.common.testing.accessibility.framework.AccessibilityHierarchyCheck;
@@ -53,8 +54,14 @@ public class ValidatorData {
* Determine what types and levels of validation to run.
*/
public static class Policy {
+ /** Sets of types to filter by. */
@NotNull public final EnumSet<Type> mTypes;
+ /** Sets of levels to filter by. */
@NotNull public final EnumSet<Level> mLevels;
+ /**
+ * List of checks to use for the scan. If empty we use the default set
+ * defined by {@link AccessibilityCheckPreset.LATEST}
+ */
@NotNull public final HashSet<AccessibilityHierarchyCheck> mChecks = new HashSet();
public Policy(@NotNull EnumSet<Type> types, @NotNull EnumSet<Level> levels) {
@@ -64,17 +71,6 @@ public class ValidatorData {
}
/**
- * Suggested fix to the user or to the studio.
- */
- public static class Fix {
- @NotNull public final String mFix;
-
- public Fix(String fix) {
- mFix = fix;
- }
- }
-
- /**
* Issue describing the layout problem.
*/
public static class Issue {
@@ -181,4 +177,112 @@ public class ValidatorData {
}
}
}
+
+ /**
+ * Represents a view attribute which contains a namespace and an attribute name.
+ */
+ public static class ViewAttribute {
+ /** The namespace used in XML files for this view attribute. */
+ @NotNull public final String mNamespaceUri;
+ /** The namespace of this view attribute. */
+ @NotNull public final String mNamespace;
+ /** The attribute name of this view attribute. */
+ @NotNull public final String mAttributeName;
+
+ public ViewAttribute(
+ @NotNull String namespaceUri,
+ @NotNull String namespace,
+ @NotNull String attributeName) {
+ mNamespaceUri = namespaceUri;
+ mNamespace = namespace;
+ mAttributeName = attributeName;
+ }
+ }
+
+ /**
+ * Suggested fix to the user or to the studio.
+ */
+ public static interface Fix {
+ /**
+ * @return a human-readable description for this fix.
+ */
+ @NotNull String getDescription();
+ }
+
+ /**
+ * Suggest setting a value to a {@link ViewAttribute} to fix a specific {@link Issue}.
+ *
+ * <ul>
+ * <li>If the view attribute has not been set before, add the view attribute and set its value
+ * to the suggested value.
+ * <li>If the view attribute has been set before, replace its value with the suggested value.
+ * <li>If the suggested value is an empty string, ask the developer to set the view attribute
+ * to a meaningful non-empty string or resource reference. DO NOT set the view attribute
+ * to an empty string.
+ * </ul>
+ */
+ public static class SetViewAttributeFix implements Fix {
+ /** The {@link ViewAttribute} suggested to be changed. */
+ @NotNull public final ViewAttribute mViewAttribute;
+
+ /** The suggested value of the {@link ViewAttribute} suggested to be changed. */
+ @NotNull public final String mSuggestedValue;
+
+ @NotNull private final String mDescription;
+
+ public SetViewAttributeFix(@NotNull ViewAttribute viewAttribute,
+ @NotNull String suggestedValue, @NotNull String description) {
+ mViewAttribute = viewAttribute;
+ mSuggestedValue = suggestedValue;
+ mDescription = description;
+ }
+
+ @Override
+ @NotNull public String getDescription() {
+ return mDescription;
+ }
+ }
+
+ /**
+ * Suggest removing a {@link ViewAttribute} to fix a specific {@link Issue}.
+ */
+ public static class RemoveViewAttributeFix implements Fix {
+ /** The {@link ViewAttribute} suggested to be removed. */
+ @NotNull public final ViewAttribute mViewAttribute;
+
+ @NotNull private final String mDescription;
+
+ public RemoveViewAttributeFix(@NotNull ViewAttribute viewAttribute,
+ @NotNull String description) {
+ mViewAttribute = viewAttribute;
+ mDescription = description;
+ }
+
+ @Override
+ @NotNull public String getDescription() {
+ return mDescription;
+ }
+ }
+
+ /**
+ * Suggest applying multiple {@link Fix} together to fix a specific {@link Issue}.
+ *
+ * <p>A {@link CompoundFix} must contain at least 2 {@link Fix}.
+ */
+ public static class CompoundFix implements Fix {
+ /** Lists of {@link Fix} suggested to be applied together. */
+ @NotNull public final List<Fix> mFixes;
+
+ @NotNull private final String mDescription;
+
+ public CompoundFix(@NotNull List<Fix> fixes, String description) {
+ mFixes = fixes;
+ mDescription = description;
+ }
+
+ @Override
+ @NotNull public String getDescription() {
+ return mDescription;
+ }
+ }
}
diff --git a/validator/src/com/android/tools/idea/validator/ValidatorHierarchy.java b/validator/src/com/android/tools/idea/validator/ValidatorHierarchy.java
new file mode 100644
index 0000000000..f855af6251
--- /dev/null
+++ b/validator/src/com/android/tools/idea/validator/ValidatorHierarchy.java
@@ -0,0 +1,43 @@
+/*
+ * 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.tools.idea.validator;
+
+import com.android.tools.layoutlib.annotations.Nullable;
+
+import com.google.android.apps.common.testing.accessibility.framework.Parameters;
+import com.google.android.apps.common.testing.accessibility.framework.uielement.AccessibilityHierarchyAndroid;
+
+/**
+ * Hierarchical data required for running the ATF scanner checks.
+ * Creation of the hierarchical data is pretty quick.
+ */
+public class ValidatorHierarchy {
+ /** Contains meta data (such as src map) that is required for building result */
+ public @Nullable ValidatorResult.Builder mBuilder = null;
+ /** Contains view hierarchy data related to a11y */
+ public @Nullable AccessibilityHierarchyAndroid mView = null;
+ /** Contains screen capture of the view */
+ public @Nullable Parameters mParameters = null;
+
+ public @Nullable String mErrorMessage = null;
+
+ /** Returns true if hierarchical data is available to build results. */
+ public boolean isHierarchyBuilt() {
+ return mBuilder != null && mView != null;
+ }
+
+}
diff --git a/validator/src/com/android/tools/idea/validator/ValidatorResult.java b/validator/src/com/android/tools/idea/validator/ValidatorResult.java
index 8cc5c3d1df..7f25d46489 100644
--- a/validator/src/com/android/tools/idea/validator/ValidatorResult.java
+++ b/validator/src/com/android/tools/idea/validator/ValidatorResult.java
@@ -104,28 +104,44 @@ public class ValidatorResult {
/** Error message. If null no error was thrown. */
public String mErrorMessage = null;
- /** Records how long validation took */
- public long mElapsedMs = 0;
+ /** Record how long hierarchy creation took */
+ public long mHierarchyCreationMs = 0;
+
+ /** Record how long generating results took */
+ public long mGenerateResultsMs = 0;
/** How many new memories (bytes) validator creates for images. */
public long mImageMemoryBytes = 0;
- private long mStart;
+ /** Debugging purpose only. Use it with {@link LayoutValidator#shouldSaveCroppedImages()} */
+ public List<ImageSize> mImageSizes = new ArrayList<>();
+
+ private long mHierarchyCreationTimeStart;
+
+ private long mGenerateRulesTimeStart;
private Metric() { }
- public void startTimer() {
- mStart = System.currentTimeMillis();
+ public void startHierarchyCreationTimer() {
+ mHierarchyCreationTimeStart = System.currentTimeMillis();
+ }
+
+ public void recordHierarchyCreationTime() {
+ mHierarchyCreationMs = System.currentTimeMillis() - mHierarchyCreationTimeStart;
+ }
+
+ public void startGenerateResultsTimer() {
+ mGenerateRulesTimeStart = System.currentTimeMillis();
}
- public void endTimer() {
- mElapsedMs = System.currentTimeMillis() - mStart;
+ public void recordGenerateResultsTime() {
+ mGenerateResultsMs = System.currentTimeMillis() - mGenerateRulesTimeStart;
}
@Override
public String toString() {
- return "Validation result metric: { elapsed=" + mElapsedMs +
- "ms, image memory=" + readableBytes() + " }";
+ return "Validation result metric: { hierarchy creation=" + mHierarchyCreationMs
+ +"ms, image memory=" + readableBytes() + " }";
}
private String readableBytes() {
@@ -141,4 +157,24 @@ public class ValidatorResult {
return mImageMemoryBytes + "bytes";
}
}
+
+ public static class ImageSize {
+ private final int mLeft;
+ private final int mTop;
+ private final int mWidth;
+ private final int mHeight;
+
+ public ImageSize(int left, int top, int width, int height) {
+ mLeft = left;
+ mTop = top;
+ mWidth = width;
+ mHeight = height;
+ }
+
+ @Override
+ public String toString() {
+ return "ImageSize{" + "mLeft=" + mLeft + ", mTop=" + mTop + ", mWidth=" + mWidth +
+ ", mHeight=" + mHeight + '}';
+ }
+ }
}
diff --git a/validator/src/com/android/tools/idea/validator/ValidatorUtil.java b/validator/src/com/android/tools/idea/validator/ValidatorUtil.java
new file mode 100644
index 0000000000..0e944f14f5
--- /dev/null
+++ b/validator/src/com/android/tools/idea/validator/ValidatorUtil.java
@@ -0,0 +1,377 @@
+/*
+ * 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.tools.idea.validator;
+
+import com.android.tools.idea.validator.ValidatorData.CompoundFix;
+import com.android.tools.idea.validator.ValidatorData.Issue;
+import com.android.tools.idea.validator.ValidatorData.Issue.IssueBuilder;
+import com.android.tools.idea.validator.ValidatorData.Level;
+import com.android.tools.idea.validator.ValidatorData.RemoveViewAttributeFix;
+import com.android.tools.idea.validator.ValidatorData.SetViewAttributeFix;
+import com.android.tools.idea.validator.ValidatorData.Type;
+import com.android.tools.idea.validator.ValidatorResult.Builder;
+import com.android.tools.idea.validator.hierarchy.CustomHierarchyHelper;
+import com.android.tools.layoutlib.annotations.NotNull;
+import com.android.tools.layoutlib.annotations.Nullable;
+
+import android.view.View;
+
+import java.awt.image.BufferedImage;
+import java.io.PrintWriter;
+import java.io.StringWriter;
+import java.util.ArrayList;
+import java.util.EnumSet;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Locale;
+import java.util.ResourceBundle;
+import java.util.Set;
+import java.util.stream.Collectors;
+
+import com.google.android.apps.common.testing.accessibility.framework.AccessibilityCheck;
+import com.google.android.apps.common.testing.accessibility.framework.AccessibilityCheckPreset;
+import com.google.android.apps.common.testing.accessibility.framework.AccessibilityCheckResult.AccessibilityCheckResultType;
+import com.google.android.apps.common.testing.accessibility.framework.AccessibilityHierarchyCheck;
+import com.google.android.apps.common.testing.accessibility.framework.AccessibilityHierarchyCheckResult;
+import com.google.android.apps.common.testing.accessibility.framework.Parameters;
+import com.google.android.apps.common.testing.accessibility.framework.checks.EditableContentDescCheck;
+import com.google.android.apps.common.testing.accessibility.framework.checks.SpeakableTextPresentCheck;
+import com.google.android.apps.common.testing.accessibility.framework.checks.TextContrastCheck;
+import com.google.android.apps.common.testing.accessibility.framework.checks.TouchTargetSizeCheck;
+import com.google.android.apps.common.testing.accessibility.framework.strings.StringManager;
+import com.google.android.apps.common.testing.accessibility.framework.suggestions.CompoundFixSuggestions;
+import com.google.android.apps.common.testing.accessibility.framework.suggestions.FixSuggestion;
+import com.google.android.apps.common.testing.accessibility.framework.suggestions.FixSuggestionPreset;
+import com.google.android.apps.common.testing.accessibility.framework.suggestions.RemoveViewAttributeFixSuggestion;
+import com.google.android.apps.common.testing.accessibility.framework.suggestions.SetViewAttributeFixSuggestion;
+import com.google.android.apps.common.testing.accessibility.framework.suggestions.ViewAttribute;
+import com.google.android.apps.common.testing.accessibility.framework.uielement.AccessibilityHierarchy;
+import com.google.android.apps.common.testing.accessibility.framework.uielement.AccessibilityHierarchyAndroid;
+import com.google.android.apps.common.testing.accessibility.framework.uielement.CustomViewBuilderAndroid;
+import com.google.android.apps.common.testing.accessibility.framework.uielement.DefaultCustomViewBuilderAndroid;
+import com.google.android.apps.common.testing.accessibility.framework.uielement.ViewHierarchyElementAndroid;
+import com.google.common.collect.ImmutableList;
+import com.google.common.collect.ImmutableSet;
+
+public class ValidatorUtil {
+
+ static {
+ /**
+ * Overriding default ResourceBundle ATF uses. ATF would use generic Java resources
+ * instead of Android's .xml.
+ *
+ * By default ATF generates ResourceBundle to support Android specific env/ classloader,
+ * which is quite different from Layoutlib, which supports multiple classloader depending
+ * on env (testing vs in studio).
+ *
+ * To support ATF in Layoutlib, easiest way is to convert resources from Android xml to
+ * generic Java resources (strings.properties), and have the default ResourceBundle ATF
+ * uses be redirected.
+ */
+ StringManager.setResourceBundleProvider(locale -> ResourceBundle.getBundle("strings"));
+ }
+
+ // Visible for testing.
+ protected static DefaultCustomViewBuilderAndroid sDefaultCustomViewBuilderAndroid =
+ new DefaultCustomViewBuilderAndroid();
+
+ /**
+ * Fixes could be only provided for a {@link AccessibilityHierarchyCheckResult}s generated by
+ * a predefined set of {@link AccessibilityHierarchyCheck}s.
+ */
+ private final static ImmutableSet<Class<? extends AccessibilityHierarchyCheck>>
+ sAllowedCheckResultClassSet4Fix = ImmutableSet.of(SpeakableTextPresentCheck.class,
+ TextContrastCheck.class, TouchTargetSizeCheck.class, EditableContentDescCheck.class);
+
+ /**
+ * @param policy policy to apply for the hierarchy
+ * @param view root view to build hierarchy from
+ * @param image screenshot image that matches the view
+ * @param scaleX scaling done via layoutlib in x coord
+ * @param scaleY scaling done via layoutlib in y coord
+ * @return The hierarchical data required for running the ATF checks.
+ */
+ public static ValidatorHierarchy buildHierarchy(
+ @NotNull ValidatorData.Policy policy,
+ @NotNull View view,
+ @Nullable BufferedImage image,
+ float scaleX,
+ float scaleY) {
+ ValidatorHierarchy hierarchy = new ValidatorHierarchy();
+ if (!policy.mTypes.contains(Type.ACCESSIBILITY)) {
+ return hierarchy;
+ }
+
+ ValidatorResult.Builder builder = new ValidatorResult.Builder();
+ @Nullable Parameters parameters = null;
+ builder.mMetric.startHierarchyCreationTimer();
+ try {
+ hierarchy.mView = AccessibilityHierarchyAndroid
+ .newBuilder(view)
+ .setViewOriginMap(builder.mSrcMap)
+ .setObtainCharacterLocations(LayoutValidator.obtainCharacterLocations())
+ .setCustomViewBuilder(new CustomViewBuilderAndroid() {
+ @Override
+ public Class<?> getClassByName(
+ ViewHierarchyElementAndroid viewHierarchyElementAndroid,
+ String className) {
+ Class<?> toReturn = sDefaultCustomViewBuilderAndroid.getClassByName(
+ viewHierarchyElementAndroid, className);
+ if (toReturn == null) {
+ toReturn = CustomHierarchyHelper.getClassByName(className);
+ }
+ return toReturn;
+ }
+
+ @Override
+ public boolean isCheckable(View view) {
+ return CustomHierarchyHelper.isCheckable(view);
+ }
+ }).build();
+ if (image != null) {
+ parameters = new Parameters();
+ parameters.putScreenCapture(
+ new AtfBufferedImage(image, builder.mMetric, scaleX, scaleY));
+ }
+ } finally {
+ builder.mMetric.recordHierarchyCreationTime();
+ }
+
+ hierarchy.mBuilder = builder;
+ hierarchy.mParameters = parameters;
+ return hierarchy;
+ }
+
+ /**
+ * @param hierarchy to build result from. If {@link ValidatorHierarchy#isHierarchyBuilt()}
+ * is false, returns a result with an internal error.
+ * @return Returns ValidatorResult with given hierarchical data.
+ */
+ public static ValidatorResult generateResults(
+ @NotNull ValidatorData.Policy policy,
+ @NotNull ValidatorHierarchy hierarchy) {
+ ValidatorResult.Builder builder = hierarchy.mBuilder;
+ try {
+ if (!hierarchy.isHierarchyBuilt()) {
+ // Unable to build.
+ builder = new Builder();
+ String errorMsg = hierarchy.mErrorMessage != null ? hierarchy.mErrorMessage :
+ "Hierarchy is not built yet.";
+ builder.mIssues.add(new IssueBuilder()
+ .setCategory("Accessibility")
+ .setType(Type.INTERNAL_ERROR)
+ .setMsg(errorMsg)
+ .setLevel(Level.ERROR)
+ .setSourceClass("ValidatorHierarchy")
+ .build());
+ return builder.build();
+ }
+ builder.mMetric.startGenerateResultsTimer();
+
+ AccessibilityHierarchyAndroid view = hierarchy.mView;
+ Parameters parameters = hierarchy.mParameters;
+
+ EnumSet<Level> filter = policy.mLevels;
+ ArrayList<AccessibilityHierarchyCheckResult> a11yResults = new ArrayList<>();
+
+ HashSet<AccessibilityHierarchyCheck> policyChecks = policy.mChecks;
+ @NotNull Set<AccessibilityHierarchyCheck> checks = policyChecks.isEmpty() ?
+ AccessibilityCheckPreset.getAccessibilityHierarchyChecksForPreset(
+ AccessibilityCheckPreset.LATEST) : policyChecks;
+
+ for (AccessibilityHierarchyCheck check : checks) {
+ a11yResults.addAll(check.runCheckOnHierarchy(view, null, parameters));
+ }
+
+ for (AccessibilityHierarchyCheckResult result : a11yResults) {
+ // TODO: b/183726816 replace this with
+ // AccessibilityCheckPreset.getHierarchyCheckForClassName(checkClassName)
+ // .getTitleMessage(Locale.ENGLISH)
+ String category = ValidatorUtil.getCheckClassCategory(result.getSourceCheckClass());
+
+ ValidatorData.Level level = ValidatorUtil.convertLevel(result.getType());
+ if (!filter.contains(level)) {
+ continue;
+ }
+
+ try {
+ IssueBuilder issueBuilder = new IssueBuilder().setCategory(category).setMsg(
+ result.getMessage(Locale.ENGLISH).toString()).setLevel(level).setFix(
+ ValidatorUtil.generateFix(result, view, parameters)).setSourceClass(
+ result.getSourceCheckClass().getSimpleName());
+ if (result.getElement() != null) {
+ issueBuilder.setSrcId(result.getElement().getCondensedUniqueId());
+ }
+ AccessibilityHierarchyCheck subclass =
+ AccessibilityCheckPreset.getHierarchyCheckForClass(
+ result.getSourceCheckClass().asSubclass(
+ AccessibilityHierarchyCheck.class));
+ if (subclass != null) {
+ issueBuilder.setHelpfulUrl(subclass.getHelpUrl());
+ }
+ builder.mIssues.add(issueBuilder.build());
+ } catch (Exception e) {
+ StringWriter sw = new StringWriter();
+ PrintWriter pw = new PrintWriter(sw);
+ e.printStackTrace(pw);
+ builder.mIssues.add(new IssueBuilder()
+ .setCategory(category)
+ .setType(Type.INTERNAL_ERROR)
+ .setMsg(sw.toString())
+ .setLevel(Level.ERROR)
+ .setSourceClass("ValidatorHierarchy").build());
+ }
+ }
+ } finally {
+ builder.mMetric.recordGenerateResultsTime();
+ }
+ return builder.build();
+ }
+
+ /**
+ * @return the list of internal errors in results. Useful for testing and debugging.
+ */
+ public static List<Issue> filterInternalErrors(List<ValidatorData.Issue> results) {
+ return filterByTypes(results, EnumSet.of(Type.INTERNAL_ERROR));
+ }
+
+ /**
+ * @return the list filtered by the level. Useful for testing and debugging.
+ */
+ public static List<Issue> filter(List<ValidatorData.Issue> results, EnumSet<Level> errors) {
+ return results.stream().filter(
+ issue -> errors.contains(issue.mLevel)).collect(Collectors.toList());
+ }
+
+ /**
+ * @return the list filtered by the source class name. Useful for testing and debugging.
+ */
+ public static List<Issue> filter(
+ List<ValidatorData.Issue> results, String sourceClass) {
+ return results.stream().filter(
+ issue -> sourceClass.equals(issue.mSourceClass)).collect(Collectors.toList());
+ }
+
+ /**
+ * @return the list filtered by the source class name. Useful for testing and debugging.
+ */
+ public static List<Issue> filterByTypes(
+ List<ValidatorData.Issue> results, EnumSet<Type> types) {
+ return results.stream().filter(
+ issue -> types.contains(issue.mType)).collect(Collectors.toList());
+ }
+
+ /**
+ * @param checkClass classes expected to extend AccessibilityHierarchyCheck
+ * @return {@link AccessibilityCheck.Category} of the class.
+ */
+ @NotNull
+ private static String getCheckClassCategory(@NotNull Class<?> checkClass) {
+ try {
+ Class<? extends AccessibilityHierarchyCheck> subClass =
+ checkClass.asSubclass(AccessibilityHierarchyCheck.class);
+ AccessibilityHierarchyCheck check =
+ AccessibilityCheckPreset.getHierarchyCheckForClass(subClass);
+ return (check == null) ? "Accessibility" : check.getCategory().name();
+ } catch (ClassCastException e) {
+ return "Accessibility";
+ }
+ }
+
+ /** Convert {@link AccessibilityCheckResultType} to {@link ValidatorData.Level} */
+ @NotNull
+ private static ValidatorData.Level convertLevel(@NotNull AccessibilityCheckResultType type) {
+ switch (type) {
+ case ERROR:
+ return Level.ERROR;
+ case WARNING:
+ return Level.WARNING;
+ case INFO:
+ return Level.INFO;
+ // TODO: Maybe useful later?
+ case SUPPRESSED:
+ case NOT_RUN:
+ default:
+ return Level.VERBOSE;
+ }
+ }
+
+ /**
+ * Create a {@link ValidatorData.Fix} for the given result, or {@code null} if there is no
+ * fixes available.
+ *
+ * <p>If there are multiple fixes available, return the first fix which is considered to be the
+ * best fix available.
+ *
+ * @param result to generate a fix from.
+ * @param hierarchy The hierarchy from which the result is generated from.
+ * @param parameters Optional input data or preferences.
+ */
+ @Nullable
+ private static ValidatorData.Fix generateFix(
+ @NotNull AccessibilityHierarchyCheckResult result,
+ @NotNull AccessibilityHierarchy hierarchy,
+ @Nullable Parameters parameters) {
+ if (sAllowedCheckResultClassSet4Fix.contains(result.getSourceCheckClass())) {
+ ImmutableList<FixSuggestion> fixSuggestions =
+ FixSuggestionPreset.provideFixSuggestions(result, hierarchy, parameters);
+ return fixSuggestions.isEmpty() ? null : convertFix(fixSuggestions.get(0));
+ }
+ return null;
+ }
+
+ /** Convert {@link FixSuggestion} to {@link ValidatorData.Fix} */
+ @Nullable
+ private static ValidatorData.Fix convertFix(@NotNull FixSuggestion fixSuggestion) {
+ if (fixSuggestion instanceof CompoundFixSuggestions) {
+ CompoundFixSuggestions compoundFixSuggestions = (CompoundFixSuggestions)fixSuggestion;
+ List<ValidatorData.Fix> fixes =
+ compoundFixSuggestions
+ .getFixSuggestions()
+ .stream()
+ .map(ValidatorUtil::convertFix)
+ .collect(Collectors.toList());
+ return new CompoundFix(
+ fixes,
+ compoundFixSuggestions.getDescription(Locale.ENGLISH));
+ } else if (fixSuggestion instanceof RemoveViewAttributeFixSuggestion) {
+ RemoveViewAttributeFixSuggestion removeViewAttributeFix =
+ (RemoveViewAttributeFixSuggestion)fixSuggestion;
+ return new RemoveViewAttributeFix(
+ convertViewAttribute(removeViewAttributeFix.getViewAttribute()),
+ removeViewAttributeFix.getDescription(Locale.ENGLISH));
+ } else if (fixSuggestion instanceof SetViewAttributeFixSuggestion) {
+ SetViewAttributeFixSuggestion setViewAttributeFixSuggestion =
+ (SetViewAttributeFixSuggestion)fixSuggestion;
+ return new SetViewAttributeFix(
+ convertViewAttribute(setViewAttributeFixSuggestion.getViewAttribute()),
+ setViewAttributeFixSuggestion.getSuggestedValue(),
+ setViewAttributeFixSuggestion.getDescription(Locale.ENGLISH));
+ }
+ return null;
+ }
+
+ /** Convert {@link ViewAttribute} to {@link ValidatorData.ViewAttribute} */
+ @NotNull
+ private static ValidatorData.ViewAttribute convertViewAttribute(
+ @NotNull ViewAttribute viewAttribute) {
+ return new ValidatorData.ViewAttribute(
+ viewAttribute.getNamespaceUri(),
+ viewAttribute.getNamespace(),
+ viewAttribute.getAttributeName());
+ }
+}
diff --git a/validator/src/com/android/tools/idea/validator/accessibility/AccessibilityValidator.java b/validator/src/com/android/tools/idea/validator/accessibility/AccessibilityValidator.java
deleted file mode 100644
index 859e5bc1f7..0000000000
--- a/validator/src/com/android/tools/idea/validator/accessibility/AccessibilityValidator.java
+++ /dev/null
@@ -1,203 +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.tools.idea.validator.accessibility;
-
-import com.android.tools.idea.validator.ValidatorData;
-import com.android.tools.idea.validator.ValidatorData.Fix;
-import com.android.tools.idea.validator.ValidatorData.Issue.IssueBuilder;
-import com.android.tools.idea.validator.ValidatorData.Level;
-import com.android.tools.idea.validator.ValidatorData.Type;
-import com.android.tools.idea.validator.ValidatorResult;
-import com.android.tools.idea.validator.ValidatorResult.Metric;
-import com.android.tools.layoutlib.annotations.NotNull;
-import com.android.tools.layoutlib.annotations.Nullable;
-
-import android.view.View;
-
-import java.awt.image.BufferedImage;
-import java.util.ArrayList;
-import java.util.EnumSet;
-import java.util.HashSet;
-import java.util.List;
-import java.util.Locale;
-import java.util.ResourceBundle;
-import java.util.Set;
-
-import com.google.android.apps.common.testing.accessibility.framework.AccessibilityCheckPreset;
-import com.google.android.apps.common.testing.accessibility.framework.AccessibilityCheckResult.AccessibilityCheckResultType;
-import com.google.android.apps.common.testing.accessibility.framework.AccessibilityHierarchyCheck;
-import com.google.android.apps.common.testing.accessibility.framework.AccessibilityHierarchyCheckResult;
-import com.google.android.apps.common.testing.accessibility.framework.Parameters;
-import com.google.android.apps.common.testing.accessibility.framework.strings.StringManager;
-import com.google.android.apps.common.testing.accessibility.framework.uielement.AccessibilityHierarchyAndroid;
-import com.google.common.collect.BiMap;
-
-/**
- * Validator specific for running Accessibility specific issues.
- */
-public class AccessibilityValidator {
-
- static {
- /**
- * Overriding default ResourceBundle ATF uses. ATF would use generic Java resources
- * instead of Android's .xml.
- *
- * By default ATF generates ResourceBundle to support Android specific env/ classloader,
- * which is quite different from Layoutlib, which supports multiple classloader depending
- * on env (testing vs in studio).
- *
- * To support ATF in Layoutlib, easiest way is to convert resources from Android xml to
- * generic Java resources (strings.properties), and have the default ResourceBundle ATF
- * uses be redirected.
- */
- StringManager.setResourceBundleProvider(locale -> ResourceBundle.getBundle("strings"));
- }
-
- /**
- * Run Accessibility specific validation test and receive results.
- * @param view the root view
- * @param image the output image of the view. Null if not available.
- * @param policy e.g: list of levels to allow
- * @return results with all the accessibility issues and warnings.
- */
- @NotNull
- public static ValidatorResult validateAccessibility(
- @NotNull View view,
- @Nullable BufferedImage image,
- @NotNull ValidatorData.Policy policy) {
-
- EnumSet<Level> filter = policy.mLevels;
- ValidatorResult.Builder builder = new ValidatorResult.Builder();
- builder.mMetric.startTimer();
- if (!policy.mTypes.contains(Type.ACCESSIBILITY)) {
- return builder.build();
- }
-
- List<AccessibilityHierarchyCheckResult> results = getHierarchyCheckResults(
- builder.mMetric,
- view,
- builder.mSrcMap,
- image,
- policy.mChecks);
-
- for (AccessibilityHierarchyCheckResult result : results) {
- String category = getCheckClassCategory(result.getSourceCheckClass());
-
- ValidatorData.Level level = convertLevel(result.getType());
- if (!filter.contains(level)) {
- continue;
- }
-
- try {
- IssueBuilder issueBuilder = new IssueBuilder()
- .setCategory(category)
- .setMsg(result.getMessage(Locale.ENGLISH).toString())
- .setLevel(level)
- .setFix(generateFix(result))
- .setSourceClass(result.getSourceCheckClass().getSimpleName());
- if (result.getElement() != null) {
- issueBuilder.setSrcId(result.getElement().getCondensedUniqueId());
- }
- AccessibilityHierarchyCheck subclass = AccessibilityCheckPreset
- .getHierarchyCheckForClass(result
- .getSourceCheckClass()
- .asSubclass(AccessibilityHierarchyCheck.class));
- if (subclass != null) {
- issueBuilder.setHelpfulUrl(subclass.getHelpUrl());
- }
- builder.mIssues.add(issueBuilder.build());
- } catch (Exception e) {
- builder.mIssues.add(new IssueBuilder()
- .setCategory(category)
- .setType(Type.INTERNAL_ERROR)
- .setMsg(e.getMessage())
- .setLevel(Level.ERROR)
- .setSourceClass("AccessibilityValidator").build());
- }
- }
- builder.mMetric.endTimer();
- return builder.build();
- }
-
- @NotNull
- private static String getCheckClassCategory(@NotNull Class<?> checkClass) {
- try {
- Class<? extends AccessibilityHierarchyCheck> subClass =
- checkClass.asSubclass(AccessibilityHierarchyCheck.class);
- AccessibilityHierarchyCheck check =
- AccessibilityCheckPreset.getHierarchyCheckForClass(subClass);
- return (check == null) ? "Accessibility" : check.getCategory().name();
- } catch (ClassCastException e) {
- return "Accessibility";
- }
- }
-
- @NotNull
- private static ValidatorData.Level convertLevel(@NotNull AccessibilityCheckResultType type) {
- switch (type) {
- case ERROR:
- return Level.ERROR;
- case WARNING:
- return Level.WARNING;
- case INFO:
- return Level.INFO;
- // TODO: Maybe useful later?
- case SUPPRESSED:
- case NOT_RUN:
- default:
- return Level.VERBOSE;
- }
- }
-
- @Nullable
- private static ValidatorData.Fix generateFix(@NotNull AccessibilityHierarchyCheckResult result) {
- // TODO: Once ATF is ready to return us with appropriate fix, build proper fix here.
- return new Fix("");
- }
-
- @NotNull
- private static List<AccessibilityHierarchyCheckResult> getHierarchyCheckResults(
- @NotNull Metric metric,
- @NotNull View view,
- @NotNull BiMap<Long, View> originMap,
- @Nullable BufferedImage image,
- HashSet<AccessibilityHierarchyCheck> policyChecks) {
-
- @NotNull Set<AccessibilityHierarchyCheck> checks = policyChecks.isEmpty()
- ? AccessibilityCheckPreset
- .getAccessibilityHierarchyChecksForPreset(AccessibilityCheckPreset.LATEST)
- : policyChecks;
-
- @NotNull AccessibilityHierarchyAndroid hierarchy = AccessibilityHierarchyAndroid
- .newBuilder(view)
- .setViewOriginMap(originMap)
- .build();
- ArrayList<AccessibilityHierarchyCheckResult> a11yResults = new ArrayList();
-
- Parameters parameters = null;
- if (image != null) {
- parameters = new Parameters();
- parameters.putScreenCapture(new AtfBufferedImage(image, metric));
- }
-
- for (AccessibilityHierarchyCheck check : checks) {
- a11yResults.addAll(check.runCheckOnHierarchy(hierarchy, null, parameters));
- }
-
- return a11yResults;
- }
-}
diff --git a/validator/src/com/android/tools/idea/validator/accessibility/AtfBufferedImage.java b/validator/src/com/android/tools/idea/validator/accessibility/AtfBufferedImage.java
deleted file mode 100644
index 59d20a8f92..0000000000
--- a/validator/src/com/android/tools/idea/validator/accessibility/AtfBufferedImage.java
+++ /dev/null
@@ -1,98 +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.tools.idea.validator.accessibility;
-
-import com.android.tools.idea.validator.ValidatorResult.Metric;
-import com.android.tools.layoutlib.annotations.NotNull;
-
-import java.awt.image.BufferedImage;
-import java.awt.image.DataBufferInt;
-import java.awt.image.WritableRaster;
-
-import com.google.android.apps.common.testing.accessibility.framework.utils.contrast.Image;
-
-import static java.awt.image.BufferedImage.TYPE_INT_ARGB;
-
-/**
- * Image implementation to be used in Accessibility Test Framework.
- */
-public class AtfBufferedImage implements Image {
-
- // The source buffered image, expected to contain the full screen rendered image of the layout.
- @NotNull private final BufferedImage mBufferedImage;
- // Metrics to be returned
- @NotNull private final Metric mMetric;
-
- private final int mLeft;
- private final int mTop;
- private final int mWidth;
- private final int mHeight;
-
- AtfBufferedImage(@NotNull BufferedImage image, @NotNull Metric metric) {
- assert(image.getType() == TYPE_INT_ARGB);
- mBufferedImage = image;
- mMetric = metric;
- mWidth = mBufferedImage.getWidth();
- mHeight = mBufferedImage.getHeight();
- mLeft = 0;
- mTop = 0;
- }
-
- private AtfBufferedImage(
- @NotNull BufferedImage image,
- @NotNull Metric metric,
- int left,
- int top,
- int width,
- int height) {
- mBufferedImage = image;
- mMetric = metric;
- mLeft = left;
- mTop = top;
- mWidth = width;
- mHeight = height;
- }
-
- @Override
- public int getHeight() {
- return mHeight;
- }
-
- @Override
- public int getWidth() {
- return mWidth;
- }
-
- @Override
- @NotNull
- public Image crop(int left, int top, int width, int height) {
- return new AtfBufferedImage(mBufferedImage, mMetric, left, top, width, height);
- }
-
- @Override
- @NotNull
- public int[] getPixels() {
- // ATF unfortunately writes in-place on returned int[] for color analysis.
- // It must return copied list otherwise it won't work.
- BufferedImage cropped = mBufferedImage.getSubimage(mLeft, mTop, mWidth, mHeight);
- WritableRaster raster = cropped.copyData(
- cropped.getRaster().createCompatibleWritableRaster());
- int[] toReturn = ((DataBufferInt) raster.getDataBuffer()).getData();
- mMetric.mImageMemoryBytes += toReturn.length * 4;
- return toReturn;
- }
-}
diff --git a/validator/src/com/android/tools/idea/validator/hierarchy/CustomHierarchyHelper.java b/validator/src/com/android/tools/idea/validator/hierarchy/CustomHierarchyHelper.java
new file mode 100644
index 0000000000..eee2f32c36
--- /dev/null
+++ b/validator/src/com/android/tools/idea/validator/hierarchy/CustomHierarchyHelper.java
@@ -0,0 +1,65 @@
+/*
+ * 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.tools.idea.validator.hierarchy;
+
+import com.android.ide.common.rendering.api.LayoutlibCallback;
+
+import android.view.View;
+import android.widget.Checkable;
+
+import java.lang.reflect.InvocationTargetException;
+import java.lang.reflect.Method;
+
+/** Helper for support lib dependencies. */
+public class CustomHierarchyHelper {
+ public static LayoutlibCallback sLayoutlibCallback;
+
+ /** Get the class instance from the studio based on the string class name. */
+ public static Class<?> getClassByName(String className) {
+ try {
+ return sLayoutlibCallback.findClass(className);
+ } catch (ClassNotFoundException ignore) {
+ }
+ return null;
+ }
+
+ /** Returns true if the view is of {@link Checkable} instance. False otherwise. */
+ public static boolean isCheckable(View fromView) {
+ LayoutlibCallback callback = sLayoutlibCallback;
+ if (callback == null) {
+ return false;
+ }
+
+ try {
+ // This is required as layoutlib does not know the support library such as
+ // MaterialButton. LayoutlibCallback calls for studio which understands all the maven
+ // pulled library.
+ Class button = callback.findClass(
+ "com.google.android.material.button.MaterialButton");
+ if (button.isInstance(fromView)) {
+ Method isCheckable = button.getMethod("isCheckable");
+ Object toReturn = isCheckable.invoke(fromView);
+ return (toReturn instanceof Boolean) && ((Boolean) toReturn);
+ }
+ } catch (ClassNotFoundException |
+ NoSuchMethodException |
+ IllegalAccessException |
+ InvocationTargetException ignore) {
+ }
+ return fromView instanceof Checkable;
+ }
+}
diff --git a/validator/src/com/google/android/apps/common/testing/accessibility/framework/uielement/AccessibilityHierarchyAndroid_ViewElementClassNamesAndroid_Delegate.java b/validator/src/com/google/android/apps/common/testing/accessibility/framework/uielement/AccessibilityHierarchyAndroid_ViewElementClassNamesAndroid_Delegate.java
deleted file mode 100644
index 025bd77a77..0000000000
--- a/validator/src/com/google/android/apps/common/testing/accessibility/framework/uielement/AccessibilityHierarchyAndroid_ViewElementClassNamesAndroid_Delegate.java
+++ /dev/null
@@ -1,39 +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.google.android.apps.common.testing.accessibility.framework.uielement;
-
-import com.android.ide.common.rendering.api.LayoutlibCallback;
-import com.android.tools.layoutlib.annotations.LayoutlibDelegate;
-
-public class AccessibilityHierarchyAndroid_ViewElementClassNamesAndroid_Delegate {
-
- public static LayoutlibCallback sLayoutlibCallback;
-
- @LayoutlibDelegate
- public static Class<?> getClassByName(ViewHierarchyElementAndroid view, String className) {
- Class toReturn = AccessibilityHierarchyAndroid
- .ViewElementClassNamesAndroid.getClassByName_Original(view, className);
- if (toReturn == null && sLayoutlibCallback != null) {
- try {
- return sLayoutlibCallback.findClass(className);
- } catch (ClassNotFoundException ignore) {
- }
- }
-
- return toReturn;
- }
-}